mirror of
https://github.com/283375/arcaea-offline-pyside-ui.git
synced 2025-07-01 04:16:26 +00:00
Compare commits
16 Commits
ui-refacto
...
v0.3.8
Author | SHA1 | Date | |
---|---|---|---|
d5895fe230
|
|||
cd2e3f51ca
|
|||
4a09dc210a | |||
cc8ab11b78
|
|||
48c5682e55 | |||
ee03770764
|
|||
b45c7f7de5
|
|||
15bc56e6f9
|
|||
39ee379010
|
|||
5a71a5822b
|
|||
c888b312b3
|
|||
8e4fdc30b5
|
|||
1ca868cfc6
|
|||
d63d2f0d8b
|
|||
3cd187fde3
|
|||
cce918a121
|
40
.github/workflows/build-from-latest-dependency.yml
vendored
Normal file
40
.github/workflows/build-from-latest-dependency.yml
vendored
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
name: Build UI from latest `arcaea-offline-*` dependencies
|
||||||
|
run-name: ${{ github.actor }} started a build request.
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-windows:
|
||||||
|
runs-on: windows-2022
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: "3.11"
|
||||||
|
|
||||||
|
# install dependencies
|
||||||
|
- run: "pip install -r requirements.txt"
|
||||||
|
- run: "pip uninstall arcaea-offline arcaea-offline-ocr -y"
|
||||||
|
- run: "pip install git+https://github.com/283375/arcaea-offline"
|
||||||
|
- run: "pip install git+https://github.com/283375/arcaea-offline-ocr"
|
||||||
|
- run: "pip install nuitka imageio"
|
||||||
|
- name: Install UPX
|
||||||
|
uses: crazy-max/ghaction-upx@v3
|
||||||
|
with:
|
||||||
|
install-only: true
|
||||||
|
|
||||||
|
# release builtin files
|
||||||
|
- run: 'pyside6-lrelease.exe .\ui\resources\lang\en_US.ts .\ui\resources\lang\zh_CN.ts'
|
||||||
|
- run: "python prebuild.py"
|
||||||
|
- run: 'pyside6-rcc.exe .\ui\resources\resources.qrc -o .\ui\resources\resources_rc.py'
|
||||||
|
|
||||||
|
# build
|
||||||
|
- run: "python -m nuitka --plugin-enable=upx --enable-plugin=pyside6 --assume-yes-for-downloads --windows-icon-from-ico=./ui/resources/images/icon.png --standalone --onefile index.py"
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: build-windows
|
||||||
|
path: index.exe
|
49
.github/workflows/build.yml
vendored
Normal file
49
.github/workflows/build.yml
vendored
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
name: Build UI
|
||||||
|
run-name: ${{ github.actor }} started a build request.
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- "v[0-9]+.[0-9]+.[0-9]+"
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
discussions: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-windows:
|
||||||
|
runs-on: windows-2022
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: "3.11"
|
||||||
|
cache: "pip"
|
||||||
|
|
||||||
|
# install dependencies
|
||||||
|
- run: "pip install -r requirements.txt"
|
||||||
|
- run: "pip install nuitka imageio"
|
||||||
|
- name: Install UPX
|
||||||
|
uses: crazy-max/ghaction-upx@v3
|
||||||
|
with:
|
||||||
|
install-only: true
|
||||||
|
|
||||||
|
# release builtin files
|
||||||
|
- run: 'pyside6-lrelease.exe .\ui\resources\lang\en_US.ts .\ui\resources\lang\zh_CN.ts'
|
||||||
|
- run: "python prebuild.py"
|
||||||
|
- run: 'pyside6-rcc.exe .\ui\resources\resources.qrc -o .\ui\resources\resources_rc.py'
|
||||||
|
|
||||||
|
# build
|
||||||
|
- run: "python -m nuitka --plugin-enable=upx --enable-plugin=pyside6 --assume-yes-for-downloads --windows-icon-from-ico=./ui/resources/images/icon.png --standalone --onefile index.py"
|
||||||
|
|
||||||
|
- name: Draft a release
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
with:
|
||||||
|
discussion_category_name: New releases
|
||||||
|
draft: true
|
||||||
|
generate_release_notes: true
|
||||||
|
files: |
|
||||||
|
index.exe
|
22
README.md
22
README.md
@ -1,9 +1,27 @@
|
|||||||
# Arcaea Offline PySide UI
|
# Arcaea Offline PySide UI
|
||||||
|
|
||||||
GUI for both [283375/arcaea-offline](https://github.com/283375/arcaea-offline) and [283375/arcaea-offline-ocr](https://github.com/283375/arcaea-offline-ocr)
|
GUI for both [283375/arcaea-offline](https://github.com/283375/arcaea-offline) and [ArcaeaOffline/core-ocr](https://github.com/ArcaeaOffline/core-ocr).
|
||||||
|
|
||||||
## Before you run `python index.py`...
|
## Prerequisites
|
||||||
|
|
||||||
* Install requirements
|
* Install requirements
|
||||||
* Release translation files from `ui/resources/lang/*.ts`
|
* Release translation files from `ui/resources/lang/*.ts`
|
||||||
|
* Run `prebuild.py`
|
||||||
* Compile `ui/resources/resources.qrc` to `ui/resources/resources_rc.py`
|
* Compile `ui/resources/resources.qrc` to `ui/resources/resources_rc.py`
|
||||||
|
|
||||||
|
You can refer to the [GitHub Actions file](./.github/workflows/build.yml) for a rough reference.
|
||||||
|
|
||||||
|
```
|
||||||
|
pip install -r ./requirements.txt
|
||||||
|
pyside6-lrelease ./ui/resources/lang/en_US.ts ./ui/resources/lang/zh_CN.ts
|
||||||
|
python prebuild.py
|
||||||
|
pyside6-rcc ./ui/resources/resources.qrc -o ./ui/resources/resources_rc.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Sometimes you have to install the latest, unpublished version of `arcaea-offline` and `arcaea-offline-ocr`.
|
||||||
|
|
||||||
|
```
|
||||||
|
pip uninstall -y arcaea-offline arcaea-offline-ocr
|
||||||
|
pip install git+https://github.com/283375/arcaea-offline
|
||||||
|
pip install git+https://github.com/ArcaeaOffline/core-ocr
|
||||||
|
```
|
||||||
|
@ -40,7 +40,6 @@ def getBuildToolsVer():
|
|||||||
|
|
||||||
def writeVersionFile():
|
def writeVersionFile():
|
||||||
versionFile = Path("ui/resources/VERSION")
|
versionFile = Path("ui/resources/VERSION")
|
||||||
assert versionFile.exists()
|
|
||||||
|
|
||||||
versionText = (
|
versionText = (
|
||||||
"arcaea-offline-pyside-ui\n{gitDesc}\n{buildToolsVer}\n\n"
|
"arcaea-offline-pyside-ui\n{gitDesc}\n{buildToolsVer}\n\n"
|
||||||
|
@ -4,14 +4,14 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "arcaea-offline-pyside-ui"
|
name = "arcaea-offline-pyside-ui"
|
||||||
version = "0.1.0"
|
version = "0.3.8"
|
||||||
authors = [{ name = "283375", email = "log_283375@163.com" }]
|
authors = [{ name = "283375", email = "log_283375@163.com" }]
|
||||||
description = "No description."
|
description = "No description."
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.9"
|
requires-python = ">=3.9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arcaea-offline==0.1.0",
|
"arcaea-offline==0.2.2",
|
||||||
"arcaea-offline-ocr==0.1.0",
|
"arcaea-offline-ocr==0.0.98",
|
||||||
"exif==1.6.0",
|
"exif==1.6.0",
|
||||||
"PySide6==6.5.2",
|
"PySide6==6.5.2",
|
||||||
]
|
]
|
||||||
@ -21,8 +21,8 @@ classifiers = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[project.urls]
|
[project.urls]
|
||||||
"Homepage" = "https://github.com/283375/arcaea-offline-pyside-ui"
|
"Homepage" = "https://github.com/ArcaeaOffline/client-pyside6"
|
||||||
"Bug Tracker" = "https://github.com/283375/arcaea-offline-pyside-ui/issues"
|
"Bug Tracker" = "https://github.com/ArcaeaOffline/client-pyside6/issues"
|
||||||
|
|
||||||
[tool.black]
|
[tool.black]
|
||||||
force-exclude = '''
|
force-exclude = '''
|
||||||
|
@ -2,4 +2,3 @@ black == 23.7.0
|
|||||||
isort == 5.12.0
|
isort == 5.12.0
|
||||||
imageio==2.31.4
|
imageio==2.31.4
|
||||||
Nuitka==1.8.4
|
Nuitka==1.8.4
|
||||||
pytest==7.4.3
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
arcaea-offline==0.1.0
|
arcaea-offline==0.2.2
|
||||||
arcaea-offline-ocr==0.1.0
|
arcaea-offline-ocr==0.0.98
|
||||||
exif==1.6.0
|
exif==1.6.0
|
||||||
|
Pillow==10.1.0
|
||||||
PySide6==6.5.2
|
PySide6==6.5.2
|
||||||
typing-extensions==4.8.0
|
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
from ui.navigation.navhost import NavHost
|
|
||||||
from ui.navigation.navitem import NavItem
|
|
||||||
|
|
||||||
|
|
||||||
class TestNavHost:
|
|
||||||
def test_auto_append_parent(self):
|
|
||||||
navHost = NavHost()
|
|
||||||
|
|
||||||
navHost.registerNavItem(NavItem(id="aaa.bbb.ccc.ddd"))
|
|
||||||
|
|
||||||
navItems = navHost.navItems
|
|
||||||
|
|
||||||
assert NavItem(id="aaa.bbb.ccc.ddd") in navItems
|
|
||||||
assert NavItem(id="aaa.bbb.ccc") in navItems
|
|
||||||
assert NavItem(id="aaa.bbb") in navItems
|
|
||||||
assert NavItem(id="aaa") in navItems
|
|
||||||
|
|
||||||
def test_auto_select_child(self):
|
|
||||||
navHost = NavHost()
|
|
||||||
|
|
||||||
navHost.registerNavItem(NavItem(id="aaa"))
|
|
||||||
navHost.registerNavItem(NavItem(id="bbb"))
|
|
||||||
|
|
||||||
assert navHost.currentNavItem.id == "aaa"
|
|
||||||
|
|
||||||
navHost.registerNavItem(NavItem(id="aaa.bbb"))
|
|
||||||
navHost.registerNavItem(NavItem(id="aaa.ccc"))
|
|
||||||
|
|
||||||
navHost.navigate("aaa")
|
|
||||||
|
|
||||||
assert navHost.currentNavItem.id == "aaa.bbb"
|
|
@ -1,47 +0,0 @@
|
|||||||
from PySide6.QtWidgets import QWidget
|
|
||||||
|
|
||||||
from ui.navigation.navhost import NavHost, navHost
|
|
||||||
from ui.navigation.navitem import NavItem
|
|
||||||
from ui.navigation.navitemwidgets import NavItemWidgets
|
|
||||||
from ui.widgets.slidingstackedwidget import SlidingStackedWidget
|
|
||||||
|
|
||||||
|
|
||||||
class AnimatedStackedNavItemsWidgets(SlidingStackedWidget):
|
|
||||||
def __init__(
|
|
||||||
self, navItemWidgets: NavItemWidgets, navHost: NavHost = navHost, parent=None
|
|
||||||
):
|
|
||||||
super().__init__(parent)
|
|
||||||
|
|
||||||
self.navItemWidgets = navItemWidgets
|
|
||||||
self.navHost = navHost
|
|
||||||
|
|
||||||
self.navHost.activated.connect(self.__switchTo)
|
|
||||||
self.animationFinished.connect(self.endChangingWidget)
|
|
||||||
|
|
||||||
def __switchTo(self, oldNavItem: NavItem, newNavItem: NavItem):
|
|
||||||
oldNavItemDepth = self.navHost.getNavItemDepth(oldNavItem.id)
|
|
||||||
newNavItemDepth = self.navHost.getNavItemDepth(newNavItem.id)
|
|
||||||
|
|
||||||
if oldNavItemDepth != newNavItemDepth:
|
|
||||||
slidingDirection = (
|
|
||||||
self.slidingDirection.RightToLeft
|
|
||||||
if newNavItemDepth > oldNavItemDepth
|
|
||||||
else self.slidingDirection.LeftToRight
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
slidingDirection = self.slidingDirection.TopToBottom
|
|
||||||
|
|
||||||
newWidget = self.navItemWidgets.get(newNavItem.id) or QWidget()
|
|
||||||
self.startChangingWidget(newWidget, slidingDirection)
|
|
||||||
|
|
||||||
def startChangingWidget(self, newWidget: QWidget, slidingDirection):
|
|
||||||
newIndex = self.addWidget(newWidget)
|
|
||||||
[self.widget(i).setEnabled(False) for i in range(self.count())]
|
|
||||||
self.slideInIdx(newIndex, slidingDirection)
|
|
||||||
|
|
||||||
def endChangingWidget(self):
|
|
||||||
oldWidget = self.widget(0)
|
|
||||||
self.removeWidget(oldWidget)
|
|
||||||
|
|
||||||
newWidget = self.widget(0)
|
|
||||||
newWidget.setEnabled(True)
|
|
@ -6,8 +6,8 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>630</width>
|
<width>580</width>
|
||||||
<height>528</height>
|
<height>551</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
@ -17,70 +17,21 @@
|
|||||||
<property name="labelAlignment">
|
<property name="labelAlignment">
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
</property>
|
</property>
|
||||||
<item row="0" column="0">
|
<item row="1" column="0">
|
||||||
<widget class="QPushButton" name="syncArcSongDbButton">
|
|
||||||
<property name="text">
|
|
||||||
<string>syncArcSongDbButton</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="1">
|
|
||||||
<widget class="QLabel" name="label">
|
|
||||||
<property name="text">
|
|
||||||
<string>syncArcSongDb.description</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="5" column="0">
|
|
||||||
<widget class="QPushButton" name="importSt3Button">
|
|
||||||
<property name="text">
|
|
||||||
<string>importSt3Button</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="5" column="1">
|
|
||||||
<widget class="QLabel" name="label_2">
|
|
||||||
<property name="text">
|
|
||||||
<string>importSt3.description</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="7" column="0" colspan="2">
|
|
||||||
<widget class="Line" name="line">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="8" column="0">
|
|
||||||
<widget class="QPushButton" name="exportScoresButton">
|
|
||||||
<property name="text">
|
|
||||||
<string>exportScoresButton</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="8" column="1">
|
|
||||||
<widget class="QLabel" name="label_3">
|
|
||||||
<property name="text">
|
|
||||||
<string>exportScores.description</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="0" colspan="2">
|
|
||||||
<widget class="Line" name="line_2">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="0">
|
|
||||||
<widget class="QPushButton" name="importPacklistButton">
|
<widget class="QPushButton" name="importPacklistButton">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>importPacklistButton</string>
|
<string>importPacklistButton</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0">
|
<item row="1" column="1">
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="text">
|
||||||
|
<string>importPacklist.description</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
<widget class="QPushButton" name="importSonglistButton">
|
<widget class="QPushButton" name="importSonglistButton">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>importSonglistButton</string>
|
<string>importSonglistButton</string>
|
||||||
@ -88,69 +39,129 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="1">
|
<item row="2" column="1">
|
||||||
<widget class="QLabel" name="label_4">
|
|
||||||
<property name="text">
|
|
||||||
<string>importPacklist.description</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="1">
|
|
||||||
<widget class="QLabel" name="label_5">
|
<widget class="QLabel" name="label_5">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>importSonglist.description</string>
|
<string>importSonglist.description</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="9" column="0">
|
<item row="3" column="0">
|
||||||
<widget class="QPushButton" name="exportArcsongJsonButton">
|
|
||||||
<property name="text">
|
|
||||||
<string>exportArcsongJsonButton</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="9" column="1">
|
|
||||||
<widget class="QLabel" name="label_6">
|
|
||||||
<property name="text">
|
|
||||||
<string>exportArcsongJson.description</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="4" column="0">
|
|
||||||
<widget class="QPushButton" name="importApkButton">
|
<widget class="QPushButton" name="importApkButton">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>importApkButton</string>
|
<string>importApkButton</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="1">
|
<item row="3" column="1">
|
||||||
<widget class="QLabel" name="label_7">
|
<widget class="QLabel" name="label_7">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>importApk.description</string>
|
<string>importApk.description</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="5" column="0" colspan="2">
|
||||||
|
<widget class="QLabel" name="label_11">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>12</pointsize>
|
||||||
|
<bold>false</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>chartInfoGroup</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item row="6" column="0">
|
<item row="6" column="0">
|
||||||
|
<widget class="QPushButton" name="syncArcSongDbButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>syncArcSongDbButton</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="1">
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>syncArcSongDb.description</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="9" column="0" colspan="2">
|
||||||
|
<widget class="QLabel" name="label_12">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>12</pointsize>
|
||||||
|
<bold>false</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>importScoreGroup</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="10" column="0">
|
||||||
|
<widget class="QPushButton" name="importSt3Button">
|
||||||
|
<property name="text">
|
||||||
|
<string>importSt3Button</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="10" column="1">
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>importSt3.description</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="11" column="0">
|
||||||
<widget class="QPushButton" name="importOnlineButton">
|
<widget class="QPushButton" name="importOnlineButton">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>importOnlineButton</string>
|
<string>importOnlineButton</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="6" column="1">
|
<item row="11" column="1">
|
||||||
<widget class="QLabel" name="label_8">
|
<widget class="QLabel" name="label_8">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>importOnline.description</string>
|
<string>importOnline.description</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="10" column="0">
|
<item row="13" column="0" colspan="2">
|
||||||
|
<widget class="QLabel" name="label_13">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>12</pointsize>
|
||||||
|
<bold>false</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>exportScoreGroup</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="14" column="0">
|
||||||
|
<widget class="QPushButton" name="exportScoresButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>exportScoresButton</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="14" column="1">
|
||||||
|
<widget class="QLabel" name="label_3">
|
||||||
|
<property name="text">
|
||||||
|
<string>exportScores.description</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="15" column="0">
|
||||||
<widget class="QPushButton" name="exportSmartRteB30Button">
|
<widget class="QPushButton" name="exportSmartRteB30Button">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>exportSmartRteB30Button</string>
|
<string>exportSmartRteB30Button</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="10" column="1">
|
<item row="15" column="1">
|
||||||
<widget class="QLabel" name="label_9">
|
<widget class="QLabel" name="label_9">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>exportSmartRteB30.description</string>
|
<string>exportSmartRteB30.description</string>
|
||||||
@ -163,6 +174,124 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="17" column="0" colspan="2">
|
||||||
|
<widget class="QLabel" name="label_14">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>12</pointsize>
|
||||||
|
<bold>false</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>miscGroup</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="18" column="0">
|
||||||
|
<widget class="QPushButton" name="exportArcsongJsonButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>exportArcsongJsonButton</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="18" column="1">
|
||||||
|
<widget class="QLabel" name="label_6">
|
||||||
|
<property name="text">
|
||||||
|
<string>exportArcsongJson.description</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0" colspan="2">
|
||||||
|
<widget class="QLabel" name="label_10">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>12</pointsize>
|
||||||
|
<bold>false</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>packSongInfoGroup</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0">
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeType">
|
||||||
|
<enum>QSizePolicy::Minimum</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item row="8" column="0">
|
||||||
|
<spacer name="verticalSpacer_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeType">
|
||||||
|
<enum>QSizePolicy::Minimum</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item row="12" column="0">
|
||||||
|
<spacer name="verticalSpacer_3">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeType">
|
||||||
|
<enum>QSizePolicy::Minimum</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item row="16" column="0">
|
||||||
|
<spacer name="verticalSpacer_4">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeType">
|
||||||
|
<enum>QSizePolicy::Minimum</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item row="7" column="0">
|
||||||
|
<widget class="QPushButton" name="syncChartInfoDbButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>syncChartInfoDbButton</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="7" column="1">
|
||||||
|
<widget class="QLabel" name="label_15">
|
||||||
|
<property name="text">
|
||||||
|
<string>syncChartInfoDb.description</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<resources/>
|
<resources/>
|
||||||
|
@ -15,123 +15,168 @@ from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
|
|||||||
QFont, QFontDatabase, QGradient, QIcon,
|
QFont, QFontDatabase, QGradient, QIcon,
|
||||||
QImage, QKeySequence, QLinearGradient, QPainter,
|
QImage, QKeySequence, QLinearGradient, QPainter,
|
||||||
QPalette, QPixmap, QRadialGradient, QTransform)
|
QPalette, QPixmap, QRadialGradient, QTransform)
|
||||||
from PySide6.QtWidgets import (QApplication, QFormLayout, QFrame, QLabel,
|
from PySide6.QtWidgets import (QApplication, QFormLayout, QLabel, QPushButton,
|
||||||
QPushButton, QSizePolicy, QWidget)
|
QSizePolicy, QSpacerItem, QWidget)
|
||||||
|
|
||||||
class Ui_TabDb_Manage(object):
|
class Ui_TabDb_Manage(object):
|
||||||
def setupUi(self, TabDb_Manage):
|
def setupUi(self, TabDb_Manage):
|
||||||
if not TabDb_Manage.objectName():
|
if not TabDb_Manage.objectName():
|
||||||
TabDb_Manage.setObjectName(u"TabDb_Manage")
|
TabDb_Manage.setObjectName(u"TabDb_Manage")
|
||||||
TabDb_Manage.resize(630, 528)
|
TabDb_Manage.resize(580, 551)
|
||||||
TabDb_Manage.setWindowTitle(u"TabDb_Manage")
|
TabDb_Manage.setWindowTitle(u"TabDb_Manage")
|
||||||
self.formLayout = QFormLayout(TabDb_Manage)
|
self.formLayout = QFormLayout(TabDb_Manage)
|
||||||
self.formLayout.setObjectName(u"formLayout")
|
self.formLayout.setObjectName(u"formLayout")
|
||||||
self.formLayout.setLabelAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
|
self.formLayout.setLabelAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
|
||||||
self.syncArcSongDbButton = QPushButton(TabDb_Manage)
|
|
||||||
self.syncArcSongDbButton.setObjectName(u"syncArcSongDbButton")
|
|
||||||
|
|
||||||
self.formLayout.setWidget(0, QFormLayout.LabelRole, self.syncArcSongDbButton)
|
|
||||||
|
|
||||||
self.label = QLabel(TabDb_Manage)
|
|
||||||
self.label.setObjectName(u"label")
|
|
||||||
|
|
||||||
self.formLayout.setWidget(0, QFormLayout.FieldRole, self.label)
|
|
||||||
|
|
||||||
self.importSt3Button = QPushButton(TabDb_Manage)
|
|
||||||
self.importSt3Button.setObjectName(u"importSt3Button")
|
|
||||||
|
|
||||||
self.formLayout.setWidget(5, QFormLayout.LabelRole, self.importSt3Button)
|
|
||||||
|
|
||||||
self.label_2 = QLabel(TabDb_Manage)
|
|
||||||
self.label_2.setObjectName(u"label_2")
|
|
||||||
|
|
||||||
self.formLayout.setWidget(5, QFormLayout.FieldRole, self.label_2)
|
|
||||||
|
|
||||||
self.line = QFrame(TabDb_Manage)
|
|
||||||
self.line.setObjectName(u"line")
|
|
||||||
self.line.setFrameShape(QFrame.HLine)
|
|
||||||
self.line.setFrameShadow(QFrame.Sunken)
|
|
||||||
|
|
||||||
self.formLayout.setWidget(7, QFormLayout.SpanningRole, self.line)
|
|
||||||
|
|
||||||
self.exportScoresButton = QPushButton(TabDb_Manage)
|
|
||||||
self.exportScoresButton.setObjectName(u"exportScoresButton")
|
|
||||||
|
|
||||||
self.formLayout.setWidget(8, QFormLayout.LabelRole, self.exportScoresButton)
|
|
||||||
|
|
||||||
self.label_3 = QLabel(TabDb_Manage)
|
|
||||||
self.label_3.setObjectName(u"label_3")
|
|
||||||
|
|
||||||
self.formLayout.setWidget(8, QFormLayout.FieldRole, self.label_3)
|
|
||||||
|
|
||||||
self.line_2 = QFrame(TabDb_Manage)
|
|
||||||
self.line_2.setObjectName(u"line_2")
|
|
||||||
self.line_2.setFrameShape(QFrame.HLine)
|
|
||||||
self.line_2.setFrameShadow(QFrame.Sunken)
|
|
||||||
|
|
||||||
self.formLayout.setWidget(1, QFormLayout.SpanningRole, self.line_2)
|
|
||||||
|
|
||||||
self.importPacklistButton = QPushButton(TabDb_Manage)
|
self.importPacklistButton = QPushButton(TabDb_Manage)
|
||||||
self.importPacklistButton.setObjectName(u"importPacklistButton")
|
self.importPacklistButton.setObjectName(u"importPacklistButton")
|
||||||
|
|
||||||
self.formLayout.setWidget(2, QFormLayout.LabelRole, self.importPacklistButton)
|
self.formLayout.setWidget(1, QFormLayout.LabelRole, self.importPacklistButton)
|
||||||
|
|
||||||
self.importSonglistButton = QPushButton(TabDb_Manage)
|
|
||||||
self.importSonglistButton.setObjectName(u"importSonglistButton")
|
|
||||||
|
|
||||||
self.formLayout.setWidget(3, QFormLayout.LabelRole, self.importSonglistButton)
|
|
||||||
|
|
||||||
self.label_4 = QLabel(TabDb_Manage)
|
self.label_4 = QLabel(TabDb_Manage)
|
||||||
self.label_4.setObjectName(u"label_4")
|
self.label_4.setObjectName(u"label_4")
|
||||||
|
|
||||||
self.formLayout.setWidget(2, QFormLayout.FieldRole, self.label_4)
|
self.formLayout.setWidget(1, QFormLayout.FieldRole, self.label_4)
|
||||||
|
|
||||||
|
self.importSonglistButton = QPushButton(TabDb_Manage)
|
||||||
|
self.importSonglistButton.setObjectName(u"importSonglistButton")
|
||||||
|
|
||||||
|
self.formLayout.setWidget(2, QFormLayout.LabelRole, self.importSonglistButton)
|
||||||
|
|
||||||
self.label_5 = QLabel(TabDb_Manage)
|
self.label_5 = QLabel(TabDb_Manage)
|
||||||
self.label_5.setObjectName(u"label_5")
|
self.label_5.setObjectName(u"label_5")
|
||||||
|
|
||||||
self.formLayout.setWidget(3, QFormLayout.FieldRole, self.label_5)
|
self.formLayout.setWidget(2, QFormLayout.FieldRole, self.label_5)
|
||||||
|
|
||||||
self.exportArcsongJsonButton = QPushButton(TabDb_Manage)
|
|
||||||
self.exportArcsongJsonButton.setObjectName(u"exportArcsongJsonButton")
|
|
||||||
|
|
||||||
self.formLayout.setWidget(9, QFormLayout.LabelRole, self.exportArcsongJsonButton)
|
|
||||||
|
|
||||||
self.label_6 = QLabel(TabDb_Manage)
|
|
||||||
self.label_6.setObjectName(u"label_6")
|
|
||||||
|
|
||||||
self.formLayout.setWidget(9, QFormLayout.FieldRole, self.label_6)
|
|
||||||
|
|
||||||
self.importApkButton = QPushButton(TabDb_Manage)
|
self.importApkButton = QPushButton(TabDb_Manage)
|
||||||
self.importApkButton.setObjectName(u"importApkButton")
|
self.importApkButton.setObjectName(u"importApkButton")
|
||||||
|
|
||||||
self.formLayout.setWidget(4, QFormLayout.LabelRole, self.importApkButton)
|
self.formLayout.setWidget(3, QFormLayout.LabelRole, self.importApkButton)
|
||||||
|
|
||||||
self.label_7 = QLabel(TabDb_Manage)
|
self.label_7 = QLabel(TabDb_Manage)
|
||||||
self.label_7.setObjectName(u"label_7")
|
self.label_7.setObjectName(u"label_7")
|
||||||
|
|
||||||
self.formLayout.setWidget(4, QFormLayout.FieldRole, self.label_7)
|
self.formLayout.setWidget(3, QFormLayout.FieldRole, self.label_7)
|
||||||
|
|
||||||
|
self.label_11 = QLabel(TabDb_Manage)
|
||||||
|
self.label_11.setObjectName(u"label_11")
|
||||||
|
font = QFont()
|
||||||
|
font.setPointSize(12)
|
||||||
|
font.setBold(False)
|
||||||
|
self.label_11.setFont(font)
|
||||||
|
|
||||||
|
self.formLayout.setWidget(5, QFormLayout.SpanningRole, self.label_11)
|
||||||
|
|
||||||
|
self.syncArcSongDbButton = QPushButton(TabDb_Manage)
|
||||||
|
self.syncArcSongDbButton.setObjectName(u"syncArcSongDbButton")
|
||||||
|
|
||||||
|
self.formLayout.setWidget(6, QFormLayout.LabelRole, self.syncArcSongDbButton)
|
||||||
|
|
||||||
|
self.label = QLabel(TabDb_Manage)
|
||||||
|
self.label.setObjectName(u"label")
|
||||||
|
|
||||||
|
self.formLayout.setWidget(6, QFormLayout.FieldRole, self.label)
|
||||||
|
|
||||||
|
self.label_12 = QLabel(TabDb_Manage)
|
||||||
|
self.label_12.setObjectName(u"label_12")
|
||||||
|
self.label_12.setFont(font)
|
||||||
|
|
||||||
|
self.formLayout.setWidget(9, QFormLayout.SpanningRole, self.label_12)
|
||||||
|
|
||||||
|
self.importSt3Button = QPushButton(TabDb_Manage)
|
||||||
|
self.importSt3Button.setObjectName(u"importSt3Button")
|
||||||
|
|
||||||
|
self.formLayout.setWidget(10, QFormLayout.LabelRole, self.importSt3Button)
|
||||||
|
|
||||||
|
self.label_2 = QLabel(TabDb_Manage)
|
||||||
|
self.label_2.setObjectName(u"label_2")
|
||||||
|
|
||||||
|
self.formLayout.setWidget(10, QFormLayout.FieldRole, self.label_2)
|
||||||
|
|
||||||
self.importOnlineButton = QPushButton(TabDb_Manage)
|
self.importOnlineButton = QPushButton(TabDb_Manage)
|
||||||
self.importOnlineButton.setObjectName(u"importOnlineButton")
|
self.importOnlineButton.setObjectName(u"importOnlineButton")
|
||||||
|
|
||||||
self.formLayout.setWidget(6, QFormLayout.LabelRole, self.importOnlineButton)
|
self.formLayout.setWidget(11, QFormLayout.LabelRole, self.importOnlineButton)
|
||||||
|
|
||||||
self.label_8 = QLabel(TabDb_Manage)
|
self.label_8 = QLabel(TabDb_Manage)
|
||||||
self.label_8.setObjectName(u"label_8")
|
self.label_8.setObjectName(u"label_8")
|
||||||
|
|
||||||
self.formLayout.setWidget(6, QFormLayout.FieldRole, self.label_8)
|
self.formLayout.setWidget(11, QFormLayout.FieldRole, self.label_8)
|
||||||
|
|
||||||
|
self.label_13 = QLabel(TabDb_Manage)
|
||||||
|
self.label_13.setObjectName(u"label_13")
|
||||||
|
self.label_13.setFont(font)
|
||||||
|
|
||||||
|
self.formLayout.setWidget(13, QFormLayout.SpanningRole, self.label_13)
|
||||||
|
|
||||||
|
self.exportScoresButton = QPushButton(TabDb_Manage)
|
||||||
|
self.exportScoresButton.setObjectName(u"exportScoresButton")
|
||||||
|
|
||||||
|
self.formLayout.setWidget(14, QFormLayout.LabelRole, self.exportScoresButton)
|
||||||
|
|
||||||
|
self.label_3 = QLabel(TabDb_Manage)
|
||||||
|
self.label_3.setObjectName(u"label_3")
|
||||||
|
|
||||||
|
self.formLayout.setWidget(14, QFormLayout.FieldRole, self.label_3)
|
||||||
|
|
||||||
self.exportSmartRteB30Button = QPushButton(TabDb_Manage)
|
self.exportSmartRteB30Button = QPushButton(TabDb_Manage)
|
||||||
self.exportSmartRteB30Button.setObjectName(u"exportSmartRteB30Button")
|
self.exportSmartRteB30Button.setObjectName(u"exportSmartRteB30Button")
|
||||||
|
|
||||||
self.formLayout.setWidget(10, QFormLayout.LabelRole, self.exportSmartRteB30Button)
|
self.formLayout.setWidget(15, QFormLayout.LabelRole, self.exportSmartRteB30Button)
|
||||||
|
|
||||||
self.label_9 = QLabel(TabDb_Manage)
|
self.label_9 = QLabel(TabDb_Manage)
|
||||||
self.label_9.setObjectName(u"label_9")
|
self.label_9.setObjectName(u"label_9")
|
||||||
self.label_9.setOpenExternalLinks(True)
|
self.label_9.setOpenExternalLinks(True)
|
||||||
self.label_9.setTextInteractionFlags(Qt.LinksAccessibleByKeyboard|Qt.LinksAccessibleByMouse)
|
self.label_9.setTextInteractionFlags(Qt.LinksAccessibleByKeyboard|Qt.LinksAccessibleByMouse)
|
||||||
|
|
||||||
self.formLayout.setWidget(10, QFormLayout.FieldRole, self.label_9)
|
self.formLayout.setWidget(15, QFormLayout.FieldRole, self.label_9)
|
||||||
|
|
||||||
|
self.label_14 = QLabel(TabDb_Manage)
|
||||||
|
self.label_14.setObjectName(u"label_14")
|
||||||
|
self.label_14.setFont(font)
|
||||||
|
|
||||||
|
self.formLayout.setWidget(17, QFormLayout.SpanningRole, self.label_14)
|
||||||
|
|
||||||
|
self.exportArcsongJsonButton = QPushButton(TabDb_Manage)
|
||||||
|
self.exportArcsongJsonButton.setObjectName(u"exportArcsongJsonButton")
|
||||||
|
|
||||||
|
self.formLayout.setWidget(18, QFormLayout.LabelRole, self.exportArcsongJsonButton)
|
||||||
|
|
||||||
|
self.label_6 = QLabel(TabDb_Manage)
|
||||||
|
self.label_6.setObjectName(u"label_6")
|
||||||
|
|
||||||
|
self.formLayout.setWidget(18, QFormLayout.FieldRole, self.label_6)
|
||||||
|
|
||||||
|
self.label_10 = QLabel(TabDb_Manage)
|
||||||
|
self.label_10.setObjectName(u"label_10")
|
||||||
|
self.label_10.setFont(font)
|
||||||
|
|
||||||
|
self.formLayout.setWidget(0, QFormLayout.SpanningRole, self.label_10)
|
||||||
|
|
||||||
|
self.verticalSpacer = QSpacerItem(20, 20, QSizePolicy.Minimum, QSizePolicy.Minimum)
|
||||||
|
|
||||||
|
self.formLayout.setItem(4, QFormLayout.LabelRole, self.verticalSpacer)
|
||||||
|
|
||||||
|
self.verticalSpacer_2 = QSpacerItem(20, 20, QSizePolicy.Minimum, QSizePolicy.Minimum)
|
||||||
|
|
||||||
|
self.formLayout.setItem(8, QFormLayout.LabelRole, self.verticalSpacer_2)
|
||||||
|
|
||||||
|
self.verticalSpacer_3 = QSpacerItem(20, 20, QSizePolicy.Minimum, QSizePolicy.Minimum)
|
||||||
|
|
||||||
|
self.formLayout.setItem(12, QFormLayout.LabelRole, self.verticalSpacer_3)
|
||||||
|
|
||||||
|
self.verticalSpacer_4 = QSpacerItem(20, 20, QSizePolicy.Minimum, QSizePolicy.Minimum)
|
||||||
|
|
||||||
|
self.formLayout.setItem(16, QFormLayout.LabelRole, self.verticalSpacer_4)
|
||||||
|
|
||||||
|
self.syncChartInfoDbButton = QPushButton(TabDb_Manage)
|
||||||
|
self.syncChartInfoDbButton.setObjectName(u"syncChartInfoDbButton")
|
||||||
|
|
||||||
|
self.formLayout.setWidget(7, QFormLayout.LabelRole, self.syncChartInfoDbButton)
|
||||||
|
|
||||||
|
self.label_15 = QLabel(TabDb_Manage)
|
||||||
|
self.label_15.setObjectName(u"label_15")
|
||||||
|
|
||||||
|
self.formLayout.setWidget(7, QFormLayout.FieldRole, self.label_15)
|
||||||
|
|
||||||
|
|
||||||
self.retranslateUi(TabDb_Manage)
|
self.retranslateUi(TabDb_Manage)
|
||||||
@ -140,24 +185,30 @@ class Ui_TabDb_Manage(object):
|
|||||||
# setupUi
|
# setupUi
|
||||||
|
|
||||||
def retranslateUi(self, TabDb_Manage):
|
def retranslateUi(self, TabDb_Manage):
|
||||||
self.syncArcSongDbButton.setText(QCoreApplication.translate("TabDb_Manage", u"syncArcSongDbButton", None))
|
|
||||||
self.label.setText(QCoreApplication.translate("TabDb_Manage", u"syncArcSongDb.description", None))
|
|
||||||
self.importSt3Button.setText(QCoreApplication.translate("TabDb_Manage", u"importSt3Button", None))
|
|
||||||
self.label_2.setText(QCoreApplication.translate("TabDb_Manage", u"importSt3.description", None))
|
|
||||||
self.exportScoresButton.setText(QCoreApplication.translate("TabDb_Manage", u"exportScoresButton", None))
|
|
||||||
self.label_3.setText(QCoreApplication.translate("TabDb_Manage", u"exportScores.description", None))
|
|
||||||
self.importPacklistButton.setText(QCoreApplication.translate("TabDb_Manage", u"importPacklistButton", None))
|
self.importPacklistButton.setText(QCoreApplication.translate("TabDb_Manage", u"importPacklistButton", None))
|
||||||
self.importSonglistButton.setText(QCoreApplication.translate("TabDb_Manage", u"importSonglistButton", None))
|
|
||||||
self.label_4.setText(QCoreApplication.translate("TabDb_Manage", u"importPacklist.description", None))
|
self.label_4.setText(QCoreApplication.translate("TabDb_Manage", u"importPacklist.description", None))
|
||||||
|
self.importSonglistButton.setText(QCoreApplication.translate("TabDb_Manage", u"importSonglistButton", None))
|
||||||
self.label_5.setText(QCoreApplication.translate("TabDb_Manage", u"importSonglist.description", None))
|
self.label_5.setText(QCoreApplication.translate("TabDb_Manage", u"importSonglist.description", None))
|
||||||
self.exportArcsongJsonButton.setText(QCoreApplication.translate("TabDb_Manage", u"exportArcsongJsonButton", None))
|
|
||||||
self.label_6.setText(QCoreApplication.translate("TabDb_Manage", u"exportArcsongJson.description", None))
|
|
||||||
self.importApkButton.setText(QCoreApplication.translate("TabDb_Manage", u"importApkButton", None))
|
self.importApkButton.setText(QCoreApplication.translate("TabDb_Manage", u"importApkButton", None))
|
||||||
self.label_7.setText(QCoreApplication.translate("TabDb_Manage", u"importApk.description", None))
|
self.label_7.setText(QCoreApplication.translate("TabDb_Manage", u"importApk.description", None))
|
||||||
|
self.label_11.setText(QCoreApplication.translate("TabDb_Manage", u"chartInfoGroup", None))
|
||||||
|
self.syncArcSongDbButton.setText(QCoreApplication.translate("TabDb_Manage", u"syncArcSongDbButton", None))
|
||||||
|
self.label.setText(QCoreApplication.translate("TabDb_Manage", u"syncArcSongDb.description", None))
|
||||||
|
self.label_12.setText(QCoreApplication.translate("TabDb_Manage", u"importScoreGroup", None))
|
||||||
|
self.importSt3Button.setText(QCoreApplication.translate("TabDb_Manage", u"importSt3Button", None))
|
||||||
|
self.label_2.setText(QCoreApplication.translate("TabDb_Manage", u"importSt3.description", None))
|
||||||
self.importOnlineButton.setText(QCoreApplication.translate("TabDb_Manage", u"importOnlineButton", None))
|
self.importOnlineButton.setText(QCoreApplication.translate("TabDb_Manage", u"importOnlineButton", None))
|
||||||
self.label_8.setText(QCoreApplication.translate("TabDb_Manage", u"importOnline.description", None))
|
self.label_8.setText(QCoreApplication.translate("TabDb_Manage", u"importOnline.description", None))
|
||||||
|
self.label_13.setText(QCoreApplication.translate("TabDb_Manage", u"exportScoreGroup", None))
|
||||||
|
self.exportScoresButton.setText(QCoreApplication.translate("TabDb_Manage", u"exportScoresButton", None))
|
||||||
|
self.label_3.setText(QCoreApplication.translate("TabDb_Manage", u"exportScores.description", None))
|
||||||
self.exportSmartRteB30Button.setText(QCoreApplication.translate("TabDb_Manage", u"exportSmartRteB30Button", None))
|
self.exportSmartRteB30Button.setText(QCoreApplication.translate("TabDb_Manage", u"exportSmartRteB30Button", None))
|
||||||
self.label_9.setText(QCoreApplication.translate("TabDb_Manage", u"exportSmartRteB30.description", None))
|
self.label_9.setText(QCoreApplication.translate("TabDb_Manage", u"exportSmartRteB30.description", None))
|
||||||
|
self.label_14.setText(QCoreApplication.translate("TabDb_Manage", u"miscGroup", None))
|
||||||
|
self.exportArcsongJsonButton.setText(QCoreApplication.translate("TabDb_Manage", u"exportArcsongJsonButton", None))
|
||||||
|
self.label_6.setText(QCoreApplication.translate("TabDb_Manage", u"exportArcsongJson.description", None))
|
||||||
|
self.label_10.setText(QCoreApplication.translate("TabDb_Manage", u"packSongInfoGroup", None))
|
||||||
|
self.syncChartInfoDbButton.setText(QCoreApplication.translate("TabDb_Manage", u"syncChartInfoDbButton", None))
|
||||||
|
self.label_15.setText(QCoreApplication.translate("TabDb_Manage", u"syncChartInfoDb.description", None))
|
||||||
pass
|
pass
|
||||||
# retranslateUi
|
# retranslateUi
|
||||||
|
|
||||||
|
@ -82,6 +82,7 @@ class ChartDelegate(TextSegmentDelegate):
|
|||||||
QColor("#809955"),
|
QColor("#809955"),
|
||||||
QColor("#702d60"),
|
QColor("#702d60"),
|
||||||
QColor("#710f25"),
|
QColor("#710f25"),
|
||||||
|
QColor("#8b77a4"),
|
||||||
]
|
]
|
||||||
ChartInvalidBackgroundColor = QColor("#e6a23c")
|
ChartInvalidBackgroundColor = QColor("#e6a23c")
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import logging
|
||||||
from typing import Type
|
from typing import Type
|
||||||
|
|
||||||
from PySide6.QtCore import Signal
|
from PySide6.QtCore import Signal
|
||||||
@ -6,6 +7,8 @@ from PySide6.QtWidgets import QHBoxLayout, QSizePolicy, QVBoxLayout, QWidget
|
|||||||
|
|
||||||
from ui.implements.components.ratingClassRadioButton import RatingClassRadioButton
|
from ui.implements.components.ratingClassRadioButton import RatingClassRadioButton
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class RatingClassSelector(QWidget):
|
class RatingClassSelector(QWidget):
|
||||||
valueChanged = Signal()
|
valueChanged = Signal()
|
||||||
@ -41,16 +44,30 @@ class RatingClassSelector(QWidget):
|
|||||||
self.bydButton.setAutoExclusive(False)
|
self.bydButton.setAutoExclusive(False)
|
||||||
self.preferredLayout.addWidget(self.bydButton)
|
self.preferredLayout.addWidget(self.bydButton)
|
||||||
|
|
||||||
self.buttons = [self.pstButton, self.prsButton, self.ftrButton, self.bydButton]
|
self.etrButton = RatingClassRadioButton(self)
|
||||||
|
self.etrButton.setObjectName("etrButton")
|
||||||
|
self.etrButton.setText("ETERNAL")
|
||||||
|
self.etrButton.setAutoExclusive(False)
|
||||||
|
self.preferredLayout.addWidget(self.etrButton)
|
||||||
|
|
||||||
|
self.buttons = [
|
||||||
|
self.pstButton,
|
||||||
|
self.prsButton,
|
||||||
|
self.ftrButton,
|
||||||
|
self.bydButton,
|
||||||
|
self.etrButton,
|
||||||
|
]
|
||||||
self.pstButton.setColors(QColor("#399bb2"), QColor("#f0f8fa"))
|
self.pstButton.setColors(QColor("#399bb2"), QColor("#f0f8fa"))
|
||||||
self.prsButton.setColors(QColor("#809955"), QColor("#f7f9f4"))
|
self.prsButton.setColors(QColor("#809955"), QColor("#f7f9f4"))
|
||||||
self.ftrButton.setColors(QColor("#702d60"), QColor("#f7ebf4"))
|
self.ftrButton.setColors(QColor("#702d60"), QColor("#f7ebf4"))
|
||||||
self.bydButton.setColors(QColor("#710f25"), QColor("#f9ced8"))
|
self.bydButton.setColors(QColor("#710f25"), QColor("#f9ced8"))
|
||||||
|
self.etrButton.setColors(QColor("#4f2c7a"), QColor("#e4daf1"))
|
||||||
|
|
||||||
self.pstButton.clicked.connect(self.select)
|
self.pstButton.clicked.connect(self.select)
|
||||||
self.prsButton.clicked.connect(self.select)
|
self.prsButton.clicked.connect(self.select)
|
||||||
self.ftrButton.clicked.connect(self.select)
|
self.ftrButton.clicked.connect(self.select)
|
||||||
self.bydButton.clicked.connect(self.select)
|
self.bydButton.clicked.connect(self.select)
|
||||||
|
self.etrButton.clicked.connect(self.select)
|
||||||
self.reset()
|
self.reset()
|
||||||
self.setButtonsEnabled([])
|
self.setButtonsEnabled([])
|
||||||
|
|
||||||
@ -106,9 +123,10 @@ class RatingClassSelector(QWidget):
|
|||||||
|
|
||||||
if ratingClass is None or isinstance(ratingClass, bool):
|
if ratingClass is None or isinstance(ratingClass, bool):
|
||||||
button = self.sender()
|
button = self.sender()
|
||||||
elif ratingClass in range(4):
|
elif ratingClass in range(len(self.buttons)):
|
||||||
button = self.buttons[ratingClass]
|
button = self.buttons[ratingClass]
|
||||||
else:
|
else:
|
||||||
|
logger.debug(f"Cannot select {ratingClass=}, condition check failed")
|
||||||
return
|
return
|
||||||
|
|
||||||
if not button.isEnabled():
|
if not button.isEnabled():
|
||||||
|
@ -14,9 +14,10 @@ from arcaea_offline.external.arcaea import (
|
|||||||
)
|
)
|
||||||
from arcaea_offline.external.arcaea.common import ArcaeaParser
|
from arcaea_offline.external.arcaea.common import ArcaeaParser
|
||||||
from arcaea_offline.external.arcsong import ArcsongDbParser
|
from arcaea_offline.external.arcsong import ArcsongDbParser
|
||||||
|
from arcaea_offline.external.chart_info_db import ChartInfoDbParser
|
||||||
from arcaea_offline.external.smartrte import SmartRteB30CsvConverter
|
from arcaea_offline.external.smartrte import SmartRteB30CsvConverter
|
||||||
from arcaea_offline.models import Difficulty, Pack, Song
|
from arcaea_offline.models import Difficulty, Pack, Song
|
||||||
from PySide6.QtCore import QDir, Slot
|
from PySide6.QtCore import QDateTime, QDir, Slot
|
||||||
from PySide6.QtWidgets import QFileDialog, QMessageBox, QWidget
|
from PySide6.QtWidgets import QFileDialog, QMessageBox, QWidget
|
||||||
|
|
||||||
from ui.designer.tabs.tabDb.tabDb_Manage_ui import Ui_TabDb_Manage
|
from ui.designer.tabs.tabDb.tabDb_Manage_ui import Ui_TabDb_Manage
|
||||||
@ -57,6 +58,29 @@ class TabDb_Manage(Ui_TabDb_Manage, QWidget):
|
|||||||
self, "Sync Error", "\n".join(traceback.format_exception(e))
|
self, "Sync Error", "\n".join(traceback.format_exception(e))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@Slot()
|
||||||
|
def on_syncChartInfoDbButton_clicked(self):
|
||||||
|
dbFile, filter = QFileDialog.getOpenFileName(
|
||||||
|
self, None, "", "DB File (*.db);;*"
|
||||||
|
)
|
||||||
|
|
||||||
|
if not dbFile:
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
db = Database()
|
||||||
|
parser = ChartInfoDbParser(dbFile)
|
||||||
|
with db.sessionmaker() as session:
|
||||||
|
parser.write_database(session)
|
||||||
|
session.commit()
|
||||||
|
databaseUpdateSignals.chartInfoUpdated.emit()
|
||||||
|
QMessageBox.information(self, None, "OK")
|
||||||
|
except Exception as e:
|
||||||
|
logging.exception("Sync chart info database error")
|
||||||
|
QMessageBox.critical(
|
||||||
|
self, "Sync Error", "\n".join(traceback.format_exception(e))
|
||||||
|
)
|
||||||
|
|
||||||
def importFromArcaeaParser(
|
def importFromArcaeaParser(
|
||||||
self, parser: ArcaeaParser, instance, logName, path
|
self, parser: ArcaeaParser, instance, logName, path
|
||||||
) -> int:
|
) -> int:
|
||||||
@ -208,14 +232,14 @@ class TabDb_Manage(Ui_TabDb_Manage, QWidget):
|
|||||||
|
|
||||||
@Slot()
|
@Slot()
|
||||||
def on_exportScoresButton_clicked(self):
|
def on_exportScoresButton_clicked(self):
|
||||||
scores = Database().export_scores()
|
scores = Database().export_scores_def_v2()
|
||||||
version = Database().version()
|
timestamp = QDateTime.currentMSecsSinceEpoch()
|
||||||
content = json.dumps(scores, ensure_ascii=False)
|
content = json.dumps(scores, ensure_ascii=False)
|
||||||
|
|
||||||
exportLocation, _filter = QFileDialog.getSaveFileName(
|
exportLocation, _filter = QFileDialog.getSaveFileName(
|
||||||
self,
|
self,
|
||||||
"Save your scores to...",
|
"Save your scores to...",
|
||||||
QDir.current().filePath(f"arcaea-offline-scores-v{version}.json"),
|
QDir.current().filePath(f"arcaea-offline-def-v2-scores-{timestamp}.json"),
|
||||||
"JSON (*.json);;*",
|
"JSON (*.json);;*",
|
||||||
)
|
)
|
||||||
with open(exportLocation, "w", encoding="utf-8") as f:
|
with open(exportLocation, "w", encoding="utf-8") as f:
|
||||||
|
@ -1,151 +0,0 @@
|
|||||||
import logging
|
|
||||||
from dataclasses import dataclass
|
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
from PySide6.QtCore import QObject, Signal
|
|
||||||
|
|
||||||
from .navitem import NavItem
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class NavItemRelatives:
|
|
||||||
parent: Optional[NavItem]
|
|
||||||
children: list[NavItem]
|
|
||||||
|
|
||||||
|
|
||||||
class NavHost(QObject):
|
|
||||||
activated = Signal(NavItem, NavItem)
|
|
||||||
|
|
||||||
navItemsChanged = Signal()
|
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
|
||||||
super().__init__(parent)
|
|
||||||
|
|
||||||
self.__navItems: list[NavItem] = []
|
|
||||||
self.__cachedNavItems: list[NavItem] = []
|
|
||||||
self.__currentNavItem: NavItem = None
|
|
||||||
|
|
||||||
def __flushCachedNavItems(self):
|
|
||||||
navItems = set(self.__navItems)
|
|
||||||
|
|
||||||
for item in self.__navItems:
|
|
||||||
parts = item.id.split(".")
|
|
||||||
for i in range(1, len(parts)):
|
|
||||||
parentItemId = ".".join(parts[:i])
|
|
||||||
navItems.add(NavItem(id=parentItemId))
|
|
||||||
|
|
||||||
self.__cachedNavItems = list(navItems)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def navItems(self) -> list[NavItem]:
|
|
||||||
return self.__cachedNavItems
|
|
||||||
|
|
||||||
@property
|
|
||||||
def currentNavItem(self) -> NavItem:
|
|
||||||
if self.__currentNavItem:
|
|
||||||
return self.__currentNavItem
|
|
||||||
|
|
||||||
if self.__navItems:
|
|
||||||
self.__currentNavItem = self.__navItems[0]
|
|
||||||
return self.__currentNavItem
|
|
||||||
|
|
||||||
def findNavItem(self, navItemId: str) -> Optional[NavItem]:
|
|
||||||
navItemIds = [item.id for item in self.__navItems]
|
|
||||||
try:
|
|
||||||
index = navItemIds.index(navItemId)
|
|
||||||
return self.__navItems[index]
|
|
||||||
except IndexError:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def isNavigatingBack(self, oldNavItemId: str, newNavItemId: str) -> bool:
|
|
||||||
# sourcery skip: class-extract-method
|
|
||||||
# | oldNavItemId | newNavItemId | back? |
|
|
||||||
# |-----------------------|--------------------|-------|
|
|
||||||
# | database.manage.packs | database | True |
|
|
||||||
# | ocr.device | ocr | True |
|
|
||||||
# | database.manage.songs | ocr.b30 | False |
|
|
||||||
# | database | database | False |
|
|
||||||
|
|
||||||
oldNavItemIdSplitted = self.getNavItemIdSplitted(oldNavItemId)
|
|
||||||
newNavItemIdSplitted = self.getNavItemIdSplitted(newNavItemId)
|
|
||||||
|
|
||||||
return self.getNavItemDepth(newNavItemId) < self.getNavItemDepth(
|
|
||||||
oldNavItemId
|
|
||||||
) and all((idFrag in oldNavItemIdSplitted) for idFrag in newNavItemIdSplitted)
|
|
||||||
|
|
||||||
def isChild(self, childNavItemId: str, parentNavItemId: str) -> bool:
|
|
||||||
childNavItemIdSplitted = self.getNavItemIdSplitted(childNavItemId)
|
|
||||||
parentNavItemIdSplitted = self.getNavItemIdSplitted(parentNavItemId)
|
|
||||||
|
|
||||||
return all(
|
|
||||||
(idFrag in childNavItemIdSplitted) for idFrag in parentNavItemIdSplitted
|
|
||||||
)
|
|
||||||
|
|
||||||
def getNavItemIdSplitted(self, navItemId: str) -> list[str]:
|
|
||||||
return navItemId.split(".")
|
|
||||||
|
|
||||||
def getNavItemDepth(self, navItemId: str) -> int:
|
|
||||||
return len(self.getNavItemIdSplitted(navItemId))
|
|
||||||
|
|
||||||
def getNavItemRelatives(self, navItemId: str) -> NavItemRelatives:
|
|
||||||
parent = None
|
|
||||||
if "." in navItemId:
|
|
||||||
navItemIdSplitted = navItemId.split(".")
|
|
||||||
parentId = navItemIdSplitted[:-1]
|
|
||||||
parent = self.findNavItem(".".join(parentId))
|
|
||||||
|
|
||||||
if not navItemId:
|
|
||||||
# return root navItems
|
|
||||||
children = [navItem for navItem in self.__navItems if "." not in navItem.id]
|
|
||||||
else:
|
|
||||||
children = [
|
|
||||||
navItem
|
|
||||||
for navItem in self.__navItems
|
|
||||||
if navItem.id.startswith(navItemId)
|
|
||||||
and navItem.id != navItemId
|
|
||||||
and self.getNavItemDepth(navItem.id)
|
|
||||||
== self.getNavItemDepth(navItemId) + 1
|
|
||||||
]
|
|
||||||
|
|
||||||
return NavItemRelatives(parent=parent, children=children)
|
|
||||||
|
|
||||||
def registerNavItem(self, item: NavItem):
|
|
||||||
self.__navItems.append(item)
|
|
||||||
self.__flushCachedNavItems()
|
|
||||||
self.navItemsChanged.emit()
|
|
||||||
|
|
||||||
def navigate(self, navItemId: str):
|
|
||||||
oldNavItem = self.__currentNavItem or NavItem("")
|
|
||||||
newNavItem = self.findNavItem(navItemId)
|
|
||||||
|
|
||||||
if newNavItem is None:
|
|
||||||
raise IndexError(
|
|
||||||
f"Cannot find '{navItemId}' in {repr(self)}. "
|
|
||||||
"Maybe try registering it?"
|
|
||||||
)
|
|
||||||
|
|
||||||
# if the navItem have children, navigate to first child
|
|
||||||
# but if the navItem is going back, e.g. 'database.manage' -> 'database'
|
|
||||||
# then don't navigate to it's child.
|
|
||||||
if self.isNavigatingBack(oldNavItem.id, newNavItem.id):
|
|
||||||
# navItem is going back
|
|
||||||
currentNavItem = newNavItem
|
|
||||||
else:
|
|
||||||
newNavItemRelatives = self.getNavItemRelatives(newNavItem.id)
|
|
||||||
if newNavItemRelatives.children:
|
|
||||||
currentNavItem = newNavItemRelatives.children[0]
|
|
||||||
else:
|
|
||||||
currentNavItem = newNavItem
|
|
||||||
|
|
||||||
self.__currentNavItem = currentNavItem
|
|
||||||
self.activated.emit(oldNavItem, self.currentNavItem)
|
|
||||||
|
|
||||||
def navigateUp(self):
|
|
||||||
navItemRelatives = self.getNavItemRelatives(self.currentNavItem.id)
|
|
||||||
if navItemRelatives.parent:
|
|
||||||
self.navigate(navItemRelatives.parent.id)
|
|
||||||
|
|
||||||
|
|
||||||
navHost = NavHost()
|
|
@ -1,17 +0,0 @@
|
|||||||
from dataclasses import dataclass
|
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
from PySide6.QtCore import QCoreApplication
|
|
||||||
from PySide6.QtGui import QIcon, QPixmap
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class NavItem:
|
|
||||||
id: str
|
|
||||||
icon: Optional[QIcon | QPixmap | str] = None
|
|
||||||
|
|
||||||
def text(self):
|
|
||||||
return QCoreApplication.translate("NavItem", f"{self.id}.title")
|
|
||||||
|
|
||||||
def __hash__(self):
|
|
||||||
return hash(self.id)
|
|
@ -1,84 +0,0 @@
|
|||||||
from PySide6.QtCore import QObject
|
|
||||||
from PySide6.QtGui import QFont
|
|
||||||
from PySide6.QtWidgets import QLabel, QSizePolicy, QSpacerItem, QVBoxLayout, QWidget
|
|
||||||
|
|
||||||
from ui.navigation.navhost import NavHost, navHost
|
|
||||||
from ui.navigation.navitem import NavItem
|
|
||||||
from ui.navigation.navsidebar import NavigationSideBar
|
|
||||||
|
|
||||||
|
|
||||||
class DefaultParentNavItemWidget(QWidget):
|
|
||||||
def __init__(self, navItem: NavItem, navItemChildren: list[NavItem], parent=None):
|
|
||||||
super().__init__(parent)
|
|
||||||
|
|
||||||
self.navItem = navItem
|
|
||||||
|
|
||||||
self.partialNavHost = NavHost(self)
|
|
||||||
self.partialNavHost.registerNavItem(navItem)
|
|
||||||
for _navItem in navItemChildren:
|
|
||||||
self.partialNavHost.registerNavItem(_navItem)
|
|
||||||
self.partialNavHost.navigate(navItem.id)
|
|
||||||
|
|
||||||
self.verticalLayout = QVBoxLayout(self)
|
|
||||||
|
|
||||||
self.navItemLabelFont = QFont(self.font())
|
|
||||||
self.navItemLabelFont.setPointSize(14)
|
|
||||||
self.navItemLabel = QLabel(self)
|
|
||||||
self.navItemLabel.setFont(self.navItemLabelFont)
|
|
||||||
|
|
||||||
spacer = QSpacerItem(
|
|
||||||
20, 20, QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Expanding
|
|
||||||
)
|
|
||||||
|
|
||||||
self.verticalLayout.addSpacerItem(spacer)
|
|
||||||
|
|
||||||
self.navSideBar = NavigationSideBar(self, self.partialNavHost)
|
|
||||||
self.navSideBar.navigateUpButton.setEnabled(False)
|
|
||||||
self.verticalLayout.addWidget(self.navSideBar)
|
|
||||||
|
|
||||||
self.verticalLayout.addSpacerItem(spacer)
|
|
||||||
|
|
||||||
self.retranslateUi()
|
|
||||||
|
|
||||||
def retranslateUi(self):
|
|
||||||
self.navItemLabel.setText(self.navItem.text())
|
|
||||||
|
|
||||||
|
|
||||||
class NavItemWidgets(QObject):
|
|
||||||
def __init__(self, parent=None):
|
|
||||||
super().__init__(parent)
|
|
||||||
|
|
||||||
self.__map: dict[str, QWidget] = {}
|
|
||||||
# this reference holds all the `DefaultParentNavItemWidget`s
|
|
||||||
# since these widgets are created with no parents, not keeping a reference to
|
|
||||||
# them may result in errors, even a silent app crash that is hard to debug
|
|
||||||
self.__defaultParentWidgetRefs: dict[str, QWidget] = {}
|
|
||||||
|
|
||||||
def register(self, navItemId: str, widget: QWidget):
|
|
||||||
self.__map[navItemId] = widget
|
|
||||||
|
|
||||||
def unregister(self, navItemId: str) -> bool:
|
|
||||||
try:
|
|
||||||
widget = self.__map.pop(navItemId)
|
|
||||||
widget.deleteLater()
|
|
||||||
return True
|
|
||||||
except KeyError:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def get(self, navItemId: str) -> QWidget | None:
|
|
||||||
widget = self.__map.get(navItemId)
|
|
||||||
if widget is not None:
|
|
||||||
return widget
|
|
||||||
elif navItemChildren := navHost.getNavItemRelatives(navItemId).children:
|
|
||||||
if self.__defaultParentWidgetRefs.get(navItemId) is None:
|
|
||||||
defaultParentNavItemWidget = DefaultParentNavItemWidget(
|
|
||||||
navHost.findNavItem(navItemId), navItemChildren
|
|
||||||
)
|
|
||||||
self.__defaultParentWidgetRefs[navItemId] = defaultParentNavItemWidget
|
|
||||||
defaultParentNavItemWidget.partialNavHost.activated.connect(
|
|
||||||
lambda o, n: navHost.navigate(n.id)
|
|
||||||
)
|
|
||||||
|
|
||||||
return self.__defaultParentWidgetRefs.get(navItemId)
|
|
||||||
else:
|
|
||||||
return None
|
|
@ -1,190 +0,0 @@
|
|||||||
from PySide6.QtCore import QModelIndex, Qt, Signal, Slot
|
|
||||||
from PySide6.QtGui import QFont, QIcon, QKeySequence, QShortcut
|
|
||||||
from PySide6.QtWidgets import (
|
|
||||||
QListWidget,
|
|
||||||
QListWidgetItem,
|
|
||||||
QPushButton,
|
|
||||||
QVBoxLayout,
|
|
||||||
QWidget,
|
|
||||||
)
|
|
||||||
|
|
||||||
from ui.navigation.navhost import NavHost, NavItem, navHost
|
|
||||||
from ui.widgets.slidingstackedwidget import SlidingStackedWidget
|
|
||||||
|
|
||||||
|
|
||||||
class NavItemListWidget(QListWidget):
|
|
||||||
NavItemRole = Qt.ItemDataRole.UserRole
|
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
|
||||||
super().__init__(parent)
|
|
||||||
|
|
||||||
font = QFont(self.font())
|
|
||||||
font.setPointSize(14)
|
|
||||||
self.setFont(font)
|
|
||||||
|
|
||||||
self.clicked.connect(self.activated)
|
|
||||||
|
|
||||||
def setNavItems(self, items: list[NavItem]):
|
|
||||||
self.clear()
|
|
||||||
|
|
||||||
for navItem in items:
|
|
||||||
if navItem.icon:
|
|
||||||
listWidgetItem = QListWidgetItem(QIcon(navItem.icon), navItem.text())
|
|
||||||
else:
|
|
||||||
listWidgetItem = QListWidgetItem(navItem.text())
|
|
||||||
listWidgetItem.setData(self.NavItemRole, navItem)
|
|
||||||
listWidgetItem.setTextAlignment(
|
|
||||||
Qt.AlignmentFlag.AlignLeading | Qt.AlignmentFlag.AlignVCenter
|
|
||||||
)
|
|
||||||
|
|
||||||
self.addItem(listWidgetItem)
|
|
||||||
|
|
||||||
def selectNavItem(self, navItemId: str):
|
|
||||||
navItemIds = [
|
|
||||||
self.item(r).data(self.NavItemRole).id for r in range(self.count())
|
|
||||||
]
|
|
||||||
index = navItemIds.index(navItemId)
|
|
||||||
self.setCurrentIndex(self.model().index(index, 0))
|
|
||||||
|
|
||||||
|
|
||||||
class NavigationWidget(QWidget):
|
|
||||||
def __init__(self, parent=None):
|
|
||||||
super().__init__(parent)
|
|
||||||
|
|
||||||
self.navHost = navHost
|
|
||||||
|
|
||||||
self.verticalLayout = QVBoxLayout(self)
|
|
||||||
self.verticalLayout.setContentsMargins(0, 0, 0, 0)
|
|
||||||
|
|
||||||
self.backButton = QPushButton(QIcon(":/icons/back.svg"), "")
|
|
||||||
self.backButton.setFlat(True)
|
|
||||||
self.backButton.setFixedHeight(20)
|
|
||||||
self.verticalLayout.addWidget(self.backButton)
|
|
||||||
|
|
||||||
self.navListWidget = NavItemListWidget(self)
|
|
||||||
self.verticalLayout.addWidget(self.navListWidget)
|
|
||||||
|
|
||||||
def setNavigationItems(self, items: list[NavItem]):
|
|
||||||
self.navListWidget.setNavItems(items)
|
|
||||||
|
|
||||||
|
|
||||||
class NavigationSideBar(QWidget):
|
|
||||||
navItemActivated = Signal(NavItem)
|
|
||||||
|
|
||||||
def __init__(self, parent=None, navHost=navHost):
|
|
||||||
super().__init__(parent)
|
|
||||||
|
|
||||||
self.navHost = None
|
|
||||||
self.navigateUpKeyboardShortcut = QShortcut(
|
|
||||||
QKeySequence(Qt.Modifier.ALT | Qt.Key.Key_Left), self, lambda: True
|
|
||||||
)
|
|
||||||
|
|
||||||
self.verticalLayout = QVBoxLayout(self)
|
|
||||||
|
|
||||||
self.navigateUpButton = QPushButton(QIcon(":/icons/back.svg"), "")
|
|
||||||
self.navigateUpButton.setFlat(True)
|
|
||||||
self.navigateUpButton.setFixedHeight(20)
|
|
||||||
self.verticalLayout.addWidget(self.navigateUpButton)
|
|
||||||
|
|
||||||
self.slidingStackedWidget = SlidingStackedWidget(self)
|
|
||||||
self.slidingStackedWidget.animationFinished.connect(
|
|
||||||
self.endChangingNavItemListWidget
|
|
||||||
)
|
|
||||||
self.verticalLayout.addWidget(self.slidingStackedWidget)
|
|
||||||
|
|
||||||
navItemListWidget = NavItemListWidget(self)
|
|
||||||
navItemListWidget.activated.connect(self.navItemListWidgetActivatedProxy)
|
|
||||||
self.slidingStackedWidget.addWidget(navItemListWidget)
|
|
||||||
|
|
||||||
self.setNavHost(navHost)
|
|
||||||
self.reloadNavWidget()
|
|
||||||
|
|
||||||
def setNavHost(self, navHost: NavHost):
|
|
||||||
if self.navHost is not None:
|
|
||||||
self.navHost.navItemsChanged.disconnect(self.reloadNavWidget)
|
|
||||||
self.navHost.activated.disconnect(self.navItemChanged)
|
|
||||||
self.navigateUpKeyboardShortcut.activated.disconnect(
|
|
||||||
self.navHost.navigateUp
|
|
||||||
)
|
|
||||||
self.navigateUpButton.clicked.disconnect(self.navHost.navigateUp)
|
|
||||||
|
|
||||||
self.navHost = navHost
|
|
||||||
|
|
||||||
self.navHost.navItemsChanged.connect(self.reloadNavWidget)
|
|
||||||
self.navHost.activated.connect(self.navItemChanged)
|
|
||||||
self.navigateUpKeyboardShortcut.activated.connect(self.navHost.navigateUp)
|
|
||||||
self.navigateUpButton.clicked.connect(self.navHost.navigateUp)
|
|
||||||
|
|
||||||
@Slot(QModelIndex)
|
|
||||||
def navItemListWidgetActivatedProxy(self, index: QModelIndex):
|
|
||||||
self.navHost.navigate(index.data(NavItemListWidget.NavItemRole).id)
|
|
||||||
self.navItemActivated.emit(index.data(NavItemListWidget.NavItemRole))
|
|
||||||
|
|
||||||
def fillNavItemListWidget(
|
|
||||||
self, currentNavItem: NavItem, listWidget: NavItemListWidget
|
|
||||||
):
|
|
||||||
currentNavItemParent = self.navHost.getNavItemRelatives(
|
|
||||||
currentNavItem.id
|
|
||||||
).parent
|
|
||||||
currentNavItems = self.navHost.getNavItemRelatives(
|
|
||||||
currentNavItemParent.id if currentNavItemParent else ""
|
|
||||||
)
|
|
||||||
listWidget.setNavItems(currentNavItems.children)
|
|
||||||
listWidget.selectNavItem(currentNavItem.id)
|
|
||||||
|
|
||||||
def reloadNavWidget(self):
|
|
||||||
self.fillNavItemListWidget(
|
|
||||||
self.navHost.currentNavItem, self.slidingStackedWidget.widget(0)
|
|
||||||
)
|
|
||||||
self.navItemChanged(self.navHost.currentNavItem, self.navHost.currentNavItem)
|
|
||||||
|
|
||||||
@Slot(NavItem, NavItem)
|
|
||||||
def navItemChanged(self, oldNavItem: NavItem, newNavItem: NavItem):
|
|
||||||
# update navigateUpButton text
|
|
||||||
if newNavItemParent := self.navHost.getNavItemRelatives(newNavItem.id).parent:
|
|
||||||
self.navigateUpButton.setText(newNavItemParent.text())
|
|
||||||
else:
|
|
||||||
self.navigateUpButton.setText("Arcaea Offline")
|
|
||||||
|
|
||||||
# update navItemListWidget
|
|
||||||
oldNavItemIdSplitted = self.navHost.getNavItemIdSplitted(oldNavItem.id)
|
|
||||||
newNavItemIdSplitted = self.navHost.getNavItemIdSplitted(newNavItem.id)
|
|
||||||
|
|
||||||
oldNavItemDepth = len(oldNavItemIdSplitted)
|
|
||||||
newNavItemDepth = len(newNavItemIdSplitted)
|
|
||||||
|
|
||||||
if oldNavItemDepth != newNavItemDepth:
|
|
||||||
# navItem depth changed, replace current NavItemListWidget
|
|
||||||
newNavItemListWidget = NavItemListWidget(self)
|
|
||||||
slidingDirection = (
|
|
||||||
self.slidingStackedWidget.slidingDirection.RightToLeft
|
|
||||||
if newNavItemDepth > oldNavItemDepth
|
|
||||||
else self.slidingStackedWidget.slidingDirection.LeftToRight
|
|
||||||
)
|
|
||||||
|
|
||||||
self.fillNavItemListWidget(newNavItem, newNavItemListWidget)
|
|
||||||
newNavItemListWidget.activated.connect(self.navItemListWidgetActivatedProxy)
|
|
||||||
|
|
||||||
self.startChangingNavItemListWidget(newNavItemListWidget, slidingDirection)
|
|
||||||
|
|
||||||
def startChangingNavItemListWidget(
|
|
||||||
self, newNavItemListWidget: NavItemListWidget, slidingDirection
|
|
||||||
):
|
|
||||||
newIndex = self.slidingStackedWidget.addWidget(newNavItemListWidget)
|
|
||||||
[
|
|
||||||
self.slidingStackedWidget.widget(i).setEnabled(False)
|
|
||||||
for i in range(self.slidingStackedWidget.count())
|
|
||||||
]
|
|
||||||
self.navigateUpButton.setEnabled(False)
|
|
||||||
self.navigateUpKeyboardShortcut.setEnabled(False)
|
|
||||||
self.slidingStackedWidget.slideInIdx(newIndex, slidingDirection)
|
|
||||||
|
|
||||||
def endChangingNavItemListWidget(self):
|
|
||||||
oldWidget = self.slidingStackedWidget.widget(0)
|
|
||||||
self.slidingStackedWidget.removeWidget(oldWidget)
|
|
||||||
oldWidget.deleteLater()
|
|
||||||
|
|
||||||
newWidget = self.slidingStackedWidget.widget(0)
|
|
||||||
newWidget.setEnabled(True)
|
|
||||||
self.navigateUpButton.setEnabled(True)
|
|
||||||
self.navigateUpKeyboardShortcut.setEnabled(True)
|
|
@ -1,59 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
|
|
||||||
<svg
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
version="1.1"
|
|
||||||
id="svg1"
|
|
||||||
inkscape:version="1.3 (0e150ed6c4, 2023-07-21)"
|
|
||||||
sodipodi:docname="back.svg"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg">
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="namedview1"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#000000"
|
|
||||||
borderopacity="0.25"
|
|
||||||
inkscape:showpageshadow="2"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pagecheckerboard="true"
|
|
||||||
inkscape:deskcolor="#d1d1d1"
|
|
||||||
inkscape:document-units="px"
|
|
||||||
showgrid="true"
|
|
||||||
inkscape:zoom="24.40625"
|
|
||||||
inkscape:cx="15.323944"
|
|
||||||
inkscape:cy="10.632522"
|
|
||||||
inkscape:current-layer="layer1">
|
|
||||||
<inkscape:grid
|
|
||||||
id="grid4"
|
|
||||||
units="px"
|
|
||||||
originx="12"
|
|
||||||
originy="12"
|
|
||||||
spacingx="1"
|
|
||||||
spacingy="1"
|
|
||||||
empcolor="#0099e5"
|
|
||||||
empopacity="0.30196078"
|
|
||||||
color="#0099e5"
|
|
||||||
opacity="0.14901961"
|
|
||||||
empspacing="5"
|
|
||||||
dotted="false"
|
|
||||||
gridanglex="30"
|
|
||||||
gridanglez="30"
|
|
||||||
visible="true" />
|
|
||||||
</sodipodi:namedview>
|
|
||||||
<defs
|
|
||||||
id="defs1" />
|
|
||||||
<g
|
|
||||||
inkscape:label="图层 1"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer1">
|
|
||||||
<path
|
|
||||||
style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
d="M 17,2 7,12 17,22"
|
|
||||||
id="path2" />
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 1.6 KiB |
@ -672,92 +672,127 @@ validation</translation>
|
|||||||
<context>
|
<context>
|
||||||
<name>TabDb_Manage</name>
|
<name>TabDb_Manage</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="23"/>
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="78"/>
|
||||||
<source>syncArcSongDbButton</source>
|
<source>syncArcSongDbButton</source>
|
||||||
<translation>Sync arcsong.db</translation>
|
<translation>Sync arcsong.db</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="30"/>
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="85"/>
|
||||||
<source>syncArcSongDb.description</source>
|
<source>syncArcSongDb.description</source>
|
||||||
<translation>Update chart info</translation>
|
<translation>Update chart info</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="37"/>
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="98"/>
|
||||||
|
<source>importScoreGroup</source>
|
||||||
|
<translation>Import Score</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="105"/>
|
||||||
<source>importSt3Button</source>
|
<source>importSt3Button</source>
|
||||||
<translation>Import from Game Save</translation>
|
<translation>Game Save Database</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="44"/>
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="112"/>
|
||||||
<source>importSt3.description</source>
|
<source>importSt3.description</source>
|
||||||
<translation>Import scores from your game save</translation>
|
<translation>Import scores from your game save database</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="58"/>
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="139"/>
|
||||||
<source>exportScoresButton</source>
|
<source>exportScoreGroup</source>
|
||||||
<translation>Export Scores</translation>
|
<translation>Export Scores</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="65"/>
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="146"/>
|
||||||
<source>exportScores.description</source>
|
<source>exportScoresButton</source>
|
||||||
<translation>Export all your scores to a JSON file</translation>
|
<translation>D.E.F. V2</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="79"/>
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="153"/>
|
||||||
|
<source>exportScores.description</source>
|
||||||
|
<translation>Export all your scores in <i>Arcaea Offline Data Exchange Format V2</i> formed JSON file</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="186"/>
|
||||||
|
<source>miscGroup</source>
|
||||||
|
<translation>Miscellaneous</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="284"/>
|
||||||
|
<source>syncChartInfoDbButton</source>
|
||||||
|
<translation>Sync Chart Info Database</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="291"/>
|
||||||
|
<source>syncChartInfoDb.description</source>
|
||||||
|
<translation>Update chart info</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="23"/>
|
||||||
<source>importPacklistButton</source>
|
<source>importPacklistButton</source>
|
||||||
<translation>Import packlist</translation>
|
<translation>Import packlist</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="86"/>
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="37"/>
|
||||||
<source>importSonglistButton</source>
|
<source>importSonglistButton</source>
|
||||||
<translation>Import songlist</translation>
|
<translation>Import songlist</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="93"/>
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="30"/>
|
||||||
<source>importPacklist.description</source>
|
<source>importPacklist.description</source>
|
||||||
<translation>Import packlist file</translation>
|
<translation>Import packlist file</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="100"/>
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="44"/>
|
||||||
<source>importSonglist.description</source>
|
<source>importSonglist.description</source>
|
||||||
<translation>Import songlist file</translation>
|
<translation>Import songlist file</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="107"/>
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="193"/>
|
||||||
<source>exportArcsongJsonButton</source>
|
<source>exportArcsongJsonButton</source>
|
||||||
<translation>Export arcsong.json</translation>
|
<translation>Export arcsong.json</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="114"/>
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="200"/>
|
||||||
<source>exportArcsongJson.description</source>
|
<source>exportArcsongJson.description</source>
|
||||||
<translation>Export arcsong.json file</translation>
|
<translation>Export arcsong.json file based on the information in database</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="121"/>
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="51"/>
|
||||||
<source>importApkButton</source>
|
<source>importApkButton</source>
|
||||||
<translation>Import from APK</translation>
|
<translation>Import from APK</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="128"/>
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="213"/>
|
||||||
|
<source>packSongInfoGroup</source>
|
||||||
|
<translation>Pack/Song Info</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="58"/>
|
||||||
<source>importApk.description</source>
|
<source>importApk.description</source>
|
||||||
<translation>Import packlist and songlist from .apk file</translation>
|
<translation>Import packlist and songlist from .apk file</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="135"/>
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="71"/>
|
||||||
|
<source>chartInfoGroup</source>
|
||||||
|
<translation>Chart Info</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="119"/>
|
||||||
<source>importOnlineButton</source>
|
<source>importOnlineButton</source>
|
||||||
<translation>Import from Arcaea Online</translation>
|
<translation>Arcaea Online</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="142"/>
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="126"/>
|
||||||
<source>importOnline.description</source>
|
<source>importOnline.description</source>
|
||||||
<translation>Import scores from the result of Arcaea Online API</translation>
|
<translation>Import scores from the Arcaea Online API result</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="149"/>
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="160"/>
|
||||||
<source>exportSmartRteB30Button</source>
|
<source>exportSmartRteB30Button</source>
|
||||||
<translation>Export Scores (CSV, SmartRTE B30)</translation>
|
<translation>SmartRTE B30</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="156"/>
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="167"/>
|
||||||
<source>exportSmartRteB30.description</source>
|
<source>exportSmartRteB30.description</source>
|
||||||
<translation>Export all your scores to <a href="https://smartrte.github.io/b30gen.html">smartrte.github.io</a> compatible CSV file</translation>
|
<translation>Export all your scores to <a href="https://smartrte.github.io/b30gen.html">smartrte.github.io</a> compatible CSV file</translation>
|
||||||
</message>
|
</message>
|
||||||
@ -840,27 +875,27 @@ validation</translation>
|
|||||||
<translation>Delete Selected Scores</translation>
|
<translation>Delete Selected Scores</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../implements/tabs/tabDb/tabDb_RemoveDuplicateScores.py" line="142"/>
|
<location filename="../../implements/tabs/tabDb/tabDb_RemoveDuplicateScores.py" line="158"/>
|
||||||
<source>quickSelectComboBox.idEarlier</source>
|
<source>quickSelectComboBox.idEarlier</source>
|
||||||
<translation>Earlier ID</translation>
|
<translation>Earlier ID</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../implements/tabs/tabDb/tabDb_RemoveDuplicateScores.py" line="148"/>
|
<location filename="../../implements/tabs/tabDb/tabDb_RemoveDuplicateScores.py" line="164"/>
|
||||||
<source>quickSelectComboBox.dateEarlier</source>
|
<source>quickSelectComboBox.dateEarlier</source>
|
||||||
<translation>Earlier date</translation>
|
<translation>Earlier date</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../implements/tabs/tabDb/tabDb_RemoveDuplicateScores.py" line="154"/>
|
<location filename="../../implements/tabs/tabDb/tabDb_RemoveDuplicateScores.py" line="170"/>
|
||||||
<source>quickSelectComboBox.columnsIntegral</source>
|
<source>quickSelectComboBox.columnsIntegral</source>
|
||||||
<translation>More complete data</translation>
|
<translation>More complete data</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../implements/tabs/tabDb/tabDb_RemoveDuplicateScores.py" line="279"/>
|
<location filename="../../implements/tabs/tabDb/tabDb_RemoveDuplicateScores.py" line="295"/>
|
||||||
<source>deleteSelectionDialog.content {}</source>
|
<source>deleteSelectionDialog.content {}</source>
|
||||||
<translation>Deleting {} scores from database, this cannot be undone!<br>Confirm?</translation>
|
<translation>Deleting {} scores from database, this cannot be undone!<br>Confirm?</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../implements/tabs/tabDb/tabDb_RemoveDuplicateScores.py" line="301"/>
|
<location filename="../../implements/tabs/tabDb/tabDb_RemoveDuplicateScores.py" line="317"/>
|
||||||
<source>scan_noColumnsDialog.content</source>
|
<source>scan_noColumnsDialog.content</source>
|
||||||
<translation>You haven't selected any column! Are you sure to continue?</translation>
|
<translation>You haven't selected any column! Are you sure to continue?</translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -671,92 +671,127 @@
|
|||||||
<context>
|
<context>
|
||||||
<name>TabDb_Manage</name>
|
<name>TabDb_Manage</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="23"/>
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="78"/>
|
||||||
<source>syncArcSongDbButton</source>
|
<source>syncArcSongDbButton</source>
|
||||||
<translation>同步 arcsong.db</translation>
|
<translation>同步 arcsong.db</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="30"/>
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="85"/>
|
||||||
<source>syncArcSongDb.description</source>
|
<source>syncArcSongDb.description</source>
|
||||||
<translation>更新谱面信息</translation>
|
<translation>更新谱面信息</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="37"/>
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="98"/>
|
||||||
<source>importSt3Button</source>
|
<source>importScoreGroup</source>
|
||||||
<translation>导入本地存档</translation>
|
<translation>导入分数</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="44"/>
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="105"/>
|
||||||
|
<source>importSt3Button</source>
|
||||||
|
<translation>存档文件</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="112"/>
|
||||||
<source>importSt3.description</source>
|
<source>importSt3.description</source>
|
||||||
<translation>遏卡抑五年,天降飞龙,姿容极度美艳。白衣仙女以妙法擒之,与之相恋。<br>来年,恶人携联结万邦之力来袭,仙女龙妃以根源之术,呼唤神通叁式之威,退敌千里,永护宝库安宁。</translation>
|
<translation>遏卡抑五年,天降飞龙,姿容极度美艳。白衣仙女以妙法擒之,与之相恋。<br>来年,恶人携联结万邦之力来袭,仙女龙妃以根源之术,呼唤神通叁式之威,退敌千里,永护宝库安宁。</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="58"/>
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="139"/>
|
||||||
<source>exportScoresButton</source>
|
<source>exportScoreGroup</source>
|
||||||
<translation>导出分数</translation>
|
<translation>导出分数</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="65"/>
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="146"/>
|
||||||
<source>exportScores.description</source>
|
<source>exportScoresButton</source>
|
||||||
<translation>将所有分数导出为 JSON 文件</translation>
|
<translation>数据交换格式 V2</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="79"/>
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="153"/>
|
||||||
|
<source>exportScores.description</source>
|
||||||
|
<translation>将所有分数导出为 <i>Arcaea Offline 数据交换格式 V2</i> JSON 文件</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="186"/>
|
||||||
|
<source>miscGroup</source>
|
||||||
|
<translation>杂项</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="284"/>
|
||||||
|
<source>syncChartInfoDbButton</source>
|
||||||
|
<translation>同步谱面信息数据库</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="291"/>
|
||||||
|
<source>syncChartInfoDb.description</source>
|
||||||
|
<translation>更新谱面信息</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="23"/>
|
||||||
<source>importPacklistButton</source>
|
<source>importPacklistButton</source>
|
||||||
<translation>导入 packlist</translation>
|
<translation>导入 packlist</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="86"/>
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="37"/>
|
||||||
<source>importSonglistButton</source>
|
<source>importSonglistButton</source>
|
||||||
<translation>导入 songlist</translation>
|
<translation>导入 songlist</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="93"/>
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="30"/>
|
||||||
<source>importPacklist.description</source>
|
<source>importPacklist.description</source>
|
||||||
<translation>导入 packlist 文件</translation>
|
<translation>导入 packlist 文件</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="100"/>
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="44"/>
|
||||||
<source>importSonglist.description</source>
|
<source>importSonglist.description</source>
|
||||||
<translation>导入 songlist 文件</translation>
|
<translation>导入 songlist 文件</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="107"/>
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="193"/>
|
||||||
<source>exportArcsongJsonButton</source>
|
<source>exportArcsongJsonButton</source>
|
||||||
<translation>导出 arcsong.json</translation>
|
<translation>导出 arcsong.json</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="114"/>
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="200"/>
|
||||||
<source>exportArcsongJson.description</source>
|
<source>exportArcsongJson.description</source>
|
||||||
<translation>导出 arcsong.json 文件</translation>
|
<translation>基于数据库信息导出 arcsong.json 文件</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="121"/>
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="51"/>
|
||||||
<source>importApkButton</source>
|
<source>importApkButton</source>
|
||||||
<translation>从 APK 导入</translation>
|
<translation>从 APK 导入</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="128"/>
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="213"/>
|
||||||
|
<source>packSongInfoGroup</source>
|
||||||
|
<translation>曲包、歌曲信息</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="58"/>
|
||||||
<source>importApk.description</source>
|
<source>importApk.description</source>
|
||||||
<translation>从 .apk 文件导入 packlist 和 songlist</translation>
|
<translation>从 .apk 文件导入 packlist 和 songlist</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="135"/>
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="71"/>
|
||||||
<source>importOnlineButton</source>
|
<source>chartInfoGroup</source>
|
||||||
<translation>从 Arcaea Online 导入</translation>
|
<translation>谱面信息</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="142"/>
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="119"/>
|
||||||
|
<source>importOnlineButton</source>
|
||||||
|
<translation>Arcaea Online</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="126"/>
|
||||||
<source>importOnline.description</source>
|
<source>importOnline.description</source>
|
||||||
<translation>从 Arcaea Online 的 API 结果导入分数</translation>
|
<translation>从 Arcaea Online 的 API 结果导入分数</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="149"/>
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="160"/>
|
||||||
<source>exportSmartRteB30Button</source>
|
<source>exportSmartRteB30Button</source>
|
||||||
<translation>导出分数(CSV,SmartRTE B30)</translation>
|
<translation>SmartRTE B30</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="156"/>
|
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="167"/>
|
||||||
<source>exportSmartRteB30.description</source>
|
<source>exportSmartRteB30.description</source>
|
||||||
<translation>将所有分数导出为兼容 <a href="https://smartrte.github.io/b30gen.html">smartrte.github.io</a> 的 CSV 文件</translation>
|
<translation>将所有分数导出为兼容 <a href="https://smartrte.github.io/b30gen.html">smartrte.github.io</a> 的 CSV 文件</translation>
|
||||||
</message>
|
</message>
|
||||||
@ -839,27 +874,27 @@
|
|||||||
<translation>删除已选分数</translation>
|
<translation>删除已选分数</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../implements/tabs/tabDb/tabDb_RemoveDuplicateScores.py" line="142"/>
|
<location filename="../../implements/tabs/tabDb/tabDb_RemoveDuplicateScores.py" line="158"/>
|
||||||
<source>quickSelectComboBox.idEarlier</source>
|
<source>quickSelectComboBox.idEarlier</source>
|
||||||
<translation>ID 更早</translation>
|
<translation>ID 更早</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../implements/tabs/tabDb/tabDb_RemoveDuplicateScores.py" line="148"/>
|
<location filename="../../implements/tabs/tabDb/tabDb_RemoveDuplicateScores.py" line="164"/>
|
||||||
<source>quickSelectComboBox.dateEarlier</source>
|
<source>quickSelectComboBox.dateEarlier</source>
|
||||||
<translation>时间更早</translation>
|
<translation>时间更早</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../implements/tabs/tabDb/tabDb_RemoveDuplicateScores.py" line="154"/>
|
<location filename="../../implements/tabs/tabDb/tabDb_RemoveDuplicateScores.py" line="170"/>
|
||||||
<source>quickSelectComboBox.columnsIntegral</source>
|
<source>quickSelectComboBox.columnsIntegral</source>
|
||||||
<translation>数据更完整</translation>
|
<translation>数据更完整</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../implements/tabs/tabDb/tabDb_RemoveDuplicateScores.py" line="279"/>
|
<location filename="../../implements/tabs/tabDb/tabDb_RemoveDuplicateScores.py" line="295"/>
|
||||||
<source>deleteSelectionDialog.content {}</source>
|
<source>deleteSelectionDialog.content {}</source>
|
||||||
<translation>将从数据库中删除 {} 个分数。此操作无法撤销!<br>确认吗?</translation>
|
<translation>将从数据库中删除 {} 个分数。此操作无法撤销!<br>确认吗?</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../implements/tabs/tabDb/tabDb_RemoveDuplicateScores.py" line="301"/>
|
<location filename="../../implements/tabs/tabDb/tabDb_RemoveDuplicateScores.py" line="317"/>
|
||||||
<source>scan_noColumnsDialog.content</source>
|
<source>scan_noColumnsDialog.content</source>
|
||||||
<translation>还未选择任何字段!确定继续吗?</translation>
|
<translation>还未选择任何字段!确定继续吗?</translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -8,8 +8,6 @@
|
|||||||
|
|
||||||
<file>fonts/GeosansLight.ttf</file>
|
<file>fonts/GeosansLight.ttf</file>
|
||||||
|
|
||||||
<file>icons/back.svg</file>
|
|
||||||
|
|
||||||
<file>images/icon.png</file>
|
<file>images/icon.png</file>
|
||||||
<file>images/logo.png</file>
|
<file>images/logo.png</file>
|
||||||
<file>images/jacket-placeholder.png</file>
|
<file>images/jacket-placeholder.png</file>
|
||||||
|
@ -3,7 +3,7 @@ import traceback
|
|||||||
from enum import IntEnum
|
from enum import IntEnum
|
||||||
|
|
||||||
from arcaea_offline.database import Database
|
from arcaea_offline.database import Database
|
||||||
from PySide6.QtCore import QCoreApplication, QDir, QFileInfo, Qt, QUrl, Slot
|
from PySide6.QtCore import QCoreApplication, QDir, QFileInfo, QSysInfo, Qt, QUrl, Slot
|
||||||
from PySide6.QtWidgets import QDialog, QMessageBox
|
from PySide6.QtWidgets import QDialog, QMessageBox
|
||||||
|
|
||||||
from ui.extends.shared.database import create_engine
|
from ui.extends.shared.database import create_engine
|
||||||
@ -59,8 +59,13 @@ class DatabaseChecker(Ui_DatabaseChecker, QDialog):
|
|||||||
return QUrl.fromLocalFile(self.dbFileInfo().filePath())
|
return QUrl.fromLocalFile(self.dbFileInfo().filePath())
|
||||||
|
|
||||||
def dbSqliteUrl(self):
|
def dbSqliteUrl(self):
|
||||||
# dbSqliteUrl.setScheme("sqlite")
|
kernelType = QSysInfo.kernelType()
|
||||||
return QUrl(self.dbFileUrl().toString().replace("file://", "sqlite://"))
|
# the slash count varies depending on the kernel
|
||||||
|
# https://docs.sqlalchemy.org/en/20/core/engines.html#sqlite
|
||||||
|
if kernelType == "winnt":
|
||||||
|
return QUrl(self.dbFileUrl().toString().replace("file://", "sqlite://"))
|
||||||
|
else:
|
||||||
|
return QUrl(self.dbFileUrl().toString().replace("file://", "sqlite:///"))
|
||||||
|
|
||||||
def confirmDb(self) -> DatabaseCheckerResult:
|
def confirmDb(self) -> DatabaseCheckerResult:
|
||||||
flags = 0x000
|
flags = 0x000
|
||||||
|
@ -1,231 +0,0 @@
|
|||||||
"""
|
|
||||||
Adapted from https://github.com/Qt-Widgets/SlidingStackedWidget-1
|
|
||||||
|
|
||||||
MIT License
|
|
||||||
Copyright (c) 2020 Tim Schneeberger (ThePBone) <tim.schneeberger(at)outlook.de>
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
from enum import IntEnum
|
|
||||||
|
|
||||||
from PySide6.QtCore import (
|
|
||||||
QAbstractAnimation,
|
|
||||||
QEasingCurve,
|
|
||||||
QParallelAnimationGroup,
|
|
||||||
QPoint,
|
|
||||||
QPropertyAnimation,
|
|
||||||
Signal,
|
|
||||||
)
|
|
||||||
from PySide6.QtWidgets import (
|
|
||||||
QGraphicsEffect,
|
|
||||||
QGraphicsOpacityEffect,
|
|
||||||
QStackedWidget,
|
|
||||||
QWidget,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class SlidingDirection(IntEnum):
|
|
||||||
Auto = 0
|
|
||||||
LeftToRight = 1
|
|
||||||
RightToLeft = 2
|
|
||||||
TopToBottom = 3
|
|
||||||
BottomToTop = 4
|
|
||||||
|
|
||||||
|
|
||||||
class SlidingStackedWidget(QStackedWidget):
|
|
||||||
slidingDirection = SlidingDirection
|
|
||||||
|
|
||||||
animationFinished = Signal()
|
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
|
||||||
super().__init__(parent)
|
|
||||||
|
|
||||||
self.vertical = False
|
|
||||||
self.speedMs = 300
|
|
||||||
self.animationEasingCurve = QEasingCurve.Type.OutQuart
|
|
||||||
self.animationCurrentIndex = 0
|
|
||||||
self.animationNextIndex = 0
|
|
||||||
self.animationCurrentPoint = QPoint(0, 0)
|
|
||||||
self.animationRunning = False
|
|
||||||
|
|
||||||
self.wrap = False
|
|
||||||
self.opacityAnimation = False
|
|
||||||
|
|
||||||
def setVertical(self, vertical: bool):
|
|
||||||
self.vertical = vertical
|
|
||||||
|
|
||||||
def setSpeedMs(self, speedMs: int):
|
|
||||||
self.speedMs = speedMs
|
|
||||||
|
|
||||||
def setAnimationEasingCurve(self, easingCurve: QEasingCurve.Type):
|
|
||||||
self.animationEasingCurve = easingCurve
|
|
||||||
|
|
||||||
def setWrap(self, wrap: bool):
|
|
||||||
self.wrap = wrap
|
|
||||||
|
|
||||||
def setOpacityAnimation(self, value: bool):
|
|
||||||
self.opacityAnimation = value
|
|
||||||
|
|
||||||
def slideInNext(self) -> bool:
|
|
||||||
currentIndex = self.currentIndex()
|
|
||||||
if self.wrap or (currentIndex < self.count() - 1):
|
|
||||||
self.slideInIdx(currentIndex + 1)
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
def slideInPrev(self) -> bool:
|
|
||||||
currentIndex = self.currentIndex()
|
|
||||||
if self.wrap or (currentIndex > 0):
|
|
||||||
self.slideInIdx(currentIndex - 1)
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
def slideInIdx(self, idx: int, direction: SlidingDirection = SlidingDirection.Auto):
|
|
||||||
if idx > self.count() - 1:
|
|
||||||
direction = (
|
|
||||||
SlidingDirection.TopToBottom
|
|
||||||
if self.vertical
|
|
||||||
else SlidingDirection.RightToLeft
|
|
||||||
)
|
|
||||||
idx %= self.count()
|
|
||||||
elif idx < 0:
|
|
||||||
direction = (
|
|
||||||
SlidingDirection.BottomToTop
|
|
||||||
if self.vertical
|
|
||||||
else SlidingDirection.LeftToRight
|
|
||||||
)
|
|
||||||
idx = (idx + self.count()) % self.count()
|
|
||||||
|
|
||||||
self.slideInWgt(self.widget(idx), direction)
|
|
||||||
|
|
||||||
def slideInWgt(self, newwidget: QWidget, direction: SlidingDirection):
|
|
||||||
if self.animationRunning:
|
|
||||||
return
|
|
||||||
|
|
||||||
self.animationRunning = True
|
|
||||||
|
|
||||||
autoDirection = SlidingDirection.LeftToRight
|
|
||||||
|
|
||||||
currentIndex = self.currentIndex()
|
|
||||||
nextIndex = self.indexOf(newwidget)
|
|
||||||
if currentIndex == nextIndex:
|
|
||||||
self.animationRunning = False
|
|
||||||
return
|
|
||||||
elif currentIndex < nextIndex:
|
|
||||||
autoDirection = (
|
|
||||||
SlidingDirection.TopToBottom
|
|
||||||
if self.vertical
|
|
||||||
else SlidingDirection.RightToLeft
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
autoDirection = (
|
|
||||||
SlidingDirection.BottomToTop
|
|
||||||
if self.vertical
|
|
||||||
else SlidingDirection.LeftToRight
|
|
||||||
)
|
|
||||||
|
|
||||||
if direction == SlidingDirection.Auto:
|
|
||||||
direction = autoDirection
|
|
||||||
|
|
||||||
offsetX = self.frameRect().width()
|
|
||||||
offsetY = self.frameRect().height()
|
|
||||||
|
|
||||||
self.widget(nextIndex).setGeometry(0, 0, offsetX, offsetY)
|
|
||||||
if direction == SlidingDirection.BottomToTop:
|
|
||||||
offsetX = 0
|
|
||||||
offsetY = -offsetY
|
|
||||||
elif direction == SlidingDirection.TopToBottom:
|
|
||||||
offsetX = 0
|
|
||||||
elif direction == SlidingDirection.RightToLeft:
|
|
||||||
offsetX = -offsetX
|
|
||||||
offsetY = 0
|
|
||||||
elif direction == SlidingDirection.LeftToRight:
|
|
||||||
offsetY = 0
|
|
||||||
|
|
||||||
nextPoint = self.widget(nextIndex).pos()
|
|
||||||
currentPoint = self.widget(currentIndex).pos()
|
|
||||||
self.animationCurrentPoint = currentPoint
|
|
||||||
self.widget(nextIndex).move(nextPoint.x() - offsetX, nextPoint.y() - offsetY)
|
|
||||||
|
|
||||||
self.widget(nextIndex).show()
|
|
||||||
self.widget(nextIndex).raise_()
|
|
||||||
|
|
||||||
currentWidgetAnimation = self.widgetPosAnimation(currentIndex)
|
|
||||||
currentWidgetAnimation.setStartValue(QPoint(currentPoint.x(), currentPoint.y()))
|
|
||||||
currentWidgetAnimation.setEndValue(
|
|
||||||
QPoint(offsetX + currentPoint.x(), offsetY + currentPoint.y())
|
|
||||||
)
|
|
||||||
|
|
||||||
nextWidgetAnimation = self.widgetPosAnimation(nextIndex)
|
|
||||||
nextWidgetAnimation.setStartValue(
|
|
||||||
QPoint(-offsetX + nextPoint.x(), offsetY + nextPoint.y())
|
|
||||||
)
|
|
||||||
nextWidgetAnimation.setEndValue(QPoint(nextPoint.x(), nextPoint.y()))
|
|
||||||
|
|
||||||
animationGroup = QParallelAnimationGroup(self)
|
|
||||||
animationGroup.addAnimation(currentWidgetAnimation)
|
|
||||||
animationGroup.addAnimation(nextWidgetAnimation)
|
|
||||||
|
|
||||||
if self.opacityAnimation:
|
|
||||||
currentWidgetOpacityEffect = QGraphicsOpacityEffect()
|
|
||||||
currentWidgetOpacityEffectAnimation = self.widgetOpacityAnimation(
|
|
||||||
currentIndex, currentWidgetOpacityEffect, 1, 0
|
|
||||||
)
|
|
||||||
|
|
||||||
nextWidgetOpacityEffect = QGraphicsOpacityEffect()
|
|
||||||
nextWidgetOpacityEffect.setOpacity(0)
|
|
||||||
nextWidgetOpacityEffectAnimation = self.widgetOpacityAnimation(
|
|
||||||
nextIndex, nextWidgetOpacityEffect, 0, 1
|
|
||||||
)
|
|
||||||
|
|
||||||
animationGroup.addAnimation(currentWidgetOpacityEffectAnimation)
|
|
||||||
animationGroup.addAnimation(nextWidgetOpacityEffectAnimation)
|
|
||||||
|
|
||||||
animationGroup.finished.connect(self.animationDoneSlot)
|
|
||||||
self.animationNextIndex = nextIndex
|
|
||||||
self.animationCurrentIndex = currentIndex
|
|
||||||
self.animationRunning = True
|
|
||||||
animationGroup.start(QAbstractAnimation.DeletionPolicy.DeleteWhenStopped)
|
|
||||||
|
|
||||||
def widgetPosAnimation(self, widgetIndex: int):
|
|
||||||
result = QPropertyAnimation(self.widget(widgetIndex), b"pos")
|
|
||||||
result.setDuration(self.speedMs)
|
|
||||||
result.setEasingCurve(self.animationEasingCurve)
|
|
||||||
return result
|
|
||||||
|
|
||||||
def widgetOpacityAnimation(
|
|
||||||
self, widgetIndex: int, graphicEffect: QGraphicsEffect, startValue, endValue
|
|
||||||
):
|
|
||||||
self.widget(widgetIndex).setGraphicsEffect(graphicEffect)
|
|
||||||
result = QPropertyAnimation(graphicEffect, b"opacity")
|
|
||||||
result.setDuration(round(self.speedMs / 2))
|
|
||||||
result.setStartValue(startValue)
|
|
||||||
result.setEndValue(endValue)
|
|
||||||
result.finished.connect(
|
|
||||||
lambda: graphicEffect.deleteLater() if graphicEffect is not None else ...
|
|
||||||
)
|
|
||||||
return result
|
|
||||||
|
|
||||||
def animationDoneSlot(self):
|
|
||||||
self.setCurrentIndex(self.animationNextIndex)
|
|
||||||
self.widget(self.animationCurrentIndex).hide()
|
|
||||||
self.widget(self.animationCurrentIndex).move(self.animationCurrentPoint)
|
|
||||||
self.animationRunning = False
|
|
||||||
self.animationFinished.emit()
|
|
Reference in New Issue
Block a user