mirror of
https://github.com/283375/arcaea-offline-pyside-ui.git
synced 2025-07-01 12:26:26 +00:00
Compare commits
29 Commits
ui-refacto
...
ceb6e2932e
Author | SHA1 | Date | |
---|---|---|---|
ceb6e2932e
|
|||
035e2157a8 | |||
e964a38e3d | |||
0e2026ff1c | |||
3ce4c7bed9 | |||
ad09c95b03 | |||
c664ed7e8d | |||
4e7d54fbef | |||
0c6f4f4961
|
|||
10fb98d530
|
|||
bf034d1397
|
|||
4f864611ee
|
|||
d9c163431c
|
|||
d5895fe230
|
|||
cd2e3f51ca
|
|||
4a09dc210a | |||
cc8ab11b78
|
|||
48c5682e55 | |||
ee03770764
|
|||
b45c7f7de5
|
|||
15bc56e6f9
|
|||
39ee379010
|
|||
5a71a5822b
|
|||
c888b312b3
|
|||
8e4fdc30b5
|
|||
1ca868cfc6
|
|||
d63d2f0d8b
|
|||
3cd187fde3
|
|||
cce918a121
|
65
.github/workflows/build-from-latest-dependency.yml
vendored
Normal file
65
.github/workflows/build-from-latest-dependency.yml
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
name: Build Executable from latest `arcaea-offline-*` dependencies
|
||||
run-name: ${{ github.actor }} started a build request.
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
discussions: write
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
fail-fast: false
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
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 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 imageio"
|
||||
- name: Install UPX
|
||||
uses: crazy-max/ghaction-upx@v3
|
||||
with:
|
||||
install-only: true
|
||||
|
||||
- name: Release builtin files
|
||||
run: |
|
||||
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
|
||||
|
||||
- name: Build Executable
|
||||
uses: Nuitka/Nuitka-Action@main
|
||||
with:
|
||||
nuitka-version: main
|
||||
script-name: index.py
|
||||
standalone: true
|
||||
onefile: true
|
||||
enable-plugins: pyside6,upx
|
||||
windows-icon-from-ico: ui/resources/images/icon.png
|
||||
linux-icon: ui/resources/images/icon.png
|
||||
|
||||
- name: Upload Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ runner.os }} Build
|
||||
path: |
|
||||
build/*.exe
|
||||
build/*.bin
|
||||
build/*.app/**/*
|
78
.github/workflows/build.yml
vendored
Normal file
78
.github/workflows/build.yml
vendored
Normal file
@ -0,0 +1,78 @@
|
||||
name: Build Executable
|
||||
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:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
fail-fast: false
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.11"
|
||||
cache: "pip"
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
pip install -r requirements.txt
|
||||
pip install imageio
|
||||
|
||||
- name: Install UPX
|
||||
uses: crazy-max/ghaction-upx@v3
|
||||
with:
|
||||
install-only: true
|
||||
|
||||
- name: Release builtin files
|
||||
run: |
|
||||
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
|
||||
|
||||
- name: Build Executable
|
||||
uses: Nuitka/Nuitka-Action@main
|
||||
with:
|
||||
nuitka-version: main
|
||||
script-name: index.py
|
||||
standalone: true
|
||||
onefile: true
|
||||
enable-plugins: pyside6,upx
|
||||
windows-icon-from-ico: ui/resources/images/icon.png
|
||||
linux-icon: ui/resources/images/icon.png
|
||||
|
||||
- name: Upload Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ runner.os }} Build
|
||||
path: |
|
||||
build/*.exe
|
||||
build/*.bin
|
||||
build/*.app/**/*
|
||||
|
||||
- name: Draft a release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
discussion_category_name: New releases
|
||||
draft: true
|
||||
generate_release_notes: true
|
||||
files: |
|
||||
build/*.exe
|
||||
build/*.bin
|
||||
build/*.app/**/*
|
@ -4,11 +4,10 @@ repos:
|
||||
hooks:
|
||||
- id: end-of-file-fixer
|
||||
- id: trailing-whitespace
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 23.1.0
|
||||
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.11.12
|
||||
hooks:
|
||||
- id: black
|
||||
- repo: https://github.com/PyCQA/isort
|
||||
rev: 5.12.0
|
||||
hooks:
|
||||
- id: isort
|
||||
- id: ruff
|
||||
args: ["--fix"]
|
||||
- id: ruff-format
|
||||
|
22
README.md
22
README.md
@ -1,9 +1,27 @@
|
||||
# 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
|
||||
* Release translation files from `ui/resources/lang/*.ts`
|
||||
* Run `prebuild.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
|
||||
```
|
||||
|
10
core/color.py
Normal file
10
core/color.py
Normal file
@ -0,0 +1,10 @@
|
||||
from PySide6.QtGui import QColor
|
||||
|
||||
|
||||
def mixColor(source: QColor, mix: QColor, ratio: float = 0.5):
|
||||
r = round((mix.red() - source.red()) * ratio + source.red())
|
||||
g = round((mix.green() - source.green()) * ratio + source.green())
|
||||
b = round((mix.blue() - source.blue()) * ratio + source.blue())
|
||||
a = round((mix.alpha() - source.alpha()) * ratio + source.alpha())
|
||||
|
||||
return QColor(r, g, b, a)
|
5
core/settings/__init__.py
Normal file
5
core/settings/__init__.py
Normal file
@ -0,0 +1,5 @@
|
||||
from .base import Settings, settings
|
||||
from .keys import SettingsKeys
|
||||
from .values import SettingsValues
|
||||
|
||||
__all__ = ["settings", "Settings", "SettingsKeys", "SettingsValues"]
|
44
core/settings/base.py
Normal file
44
core/settings/base.py
Normal file
@ -0,0 +1,44 @@
|
||||
import sys
|
||||
from enum import Enum
|
||||
from typing import Any
|
||||
|
||||
from PySide6.QtCore import QFileInfo, QSettings, Signal
|
||||
|
||||
from core.singleton import QSingleton
|
||||
|
||||
__all__ = ["Settings"]
|
||||
|
||||
TSettingsKey = str | Enum
|
||||
|
||||
|
||||
class Settings(QSettings, metaclass=QSingleton):
|
||||
updated = Signal(str)
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(
|
||||
QFileInfo(sys.argv[0]).dir().absoluteFilePath("arcaea_offline.ini"),
|
||||
QSettings.Format.IniFormat,
|
||||
parent,
|
||||
)
|
||||
|
||||
def __settingsKey(self, key: TSettingsKey) -> str:
|
||||
if isinstance(key, Enum):
|
||||
return self.__settingsKey(key.value)
|
||||
|
||||
if isinstance(key, str):
|
||||
return key
|
||||
|
||||
raise TypeError(f"{key!r} is not a valid key")
|
||||
|
||||
def setValue(self, key: TSettingsKey, value: Any) -> None:
|
||||
_key = self.__settingsKey(key)
|
||||
|
||||
super().setValue(_key, value)
|
||||
self.updated.emit(_key)
|
||||
|
||||
def stringValue(self, key: TSettingsKey) -> str | None:
|
||||
_key = self.__settingsKey(key)
|
||||
return self.value(_key, None, type=str)
|
||||
|
||||
|
||||
settings = Settings()
|
26
core/settings/keys.py
Normal file
26
core/settings/keys.py
Normal file
@ -0,0 +1,26 @@
|
||||
from dataclasses import dataclass
|
||||
from enum import StrEnum
|
||||
|
||||
|
||||
class _General(StrEnum):
|
||||
Language = "Language"
|
||||
DatabaseUrl = "DatabaseUrl"
|
||||
|
||||
|
||||
class _Ocr(StrEnum):
|
||||
KnnModelFile = "Ocr/KnnModelFile"
|
||||
B30KnnModelFile = "Ocr/B30KnnModelFile"
|
||||
PhashDatabaseFile = "Ocr/PHashDatabaseFile"
|
||||
DateSource = "Ocr/DateSource"
|
||||
|
||||
|
||||
class _Andreal(StrEnum):
|
||||
Folder = "Andreal/AndrealFolder"
|
||||
Executable = "Andreal/AndrealExecutable"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SettingsKeys:
|
||||
General = _General
|
||||
Ocr = _Ocr
|
||||
Andreal = _Andreal
|
17
core/settings/values.py
Normal file
17
core/settings/values.py
Normal file
@ -0,0 +1,17 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class _Ocr_ScoreDateSource:
|
||||
FileCreated: str = "FileCreated"
|
||||
FileLastModified: str = "FileLastModified"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class _Ocr:
|
||||
DateSource = _Ocr_ScoreDateSource()
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SettingsValues:
|
||||
Ocr = _Ocr()
|
@ -14,5 +14,5 @@ class Singleton(type, Generic[T]):
|
||||
return cls._instance
|
||||
|
||||
|
||||
class QObjectSingleton(type(QObject), Singleton):
|
||||
class QSingleton(type(QObject), Singleton):
|
||||
pass
|
30
index.py
30
index.py
@ -9,25 +9,21 @@ from PySide6.QtCore import QCoreApplication, QLocale
|
||||
from PySide6.QtGui import QFontDatabase, QIcon
|
||||
from PySide6.QtWidgets import QApplication, QDialog, QMessageBox
|
||||
|
||||
import ui.resources.resources_rc
|
||||
import ui.resources.resources_rc # noqa: F401
|
||||
from core.settings import SettingsKeys, settings
|
||||
from ui.extends.shared.language import changeAppLanguage
|
||||
from ui.extends.shared.settings import Settings
|
||||
from ui.implements.mainwindow import MainWindow
|
||||
from ui.startup.databaseChecker import DatabaseChecker, DatabaseCheckerResult
|
||||
|
||||
rootLogger = logging.getLogger("root")
|
||||
rootLogger.setLevel(logging.DEBUG)
|
||||
|
||||
rootLoggerFormatter = logging.Formatter(
|
||||
"[{levelname}]{asctime}|{name}: {msg}", "%m-%d %H:%M:%S", "{"
|
||||
)
|
||||
|
||||
|
||||
def handle_exception(exc_type, exc_value, exc_traceback):
|
||||
if issubclass(exc_type, KeyboardInterrupt):
|
||||
sys.__excepthook__(exc_type, exc_value, exc_traceback)
|
||||
return
|
||||
sys.__excepthook__(exc_type, exc_value, exc_traceback)
|
||||
|
||||
if issubclass(exc_type, KeyboardInterrupt):
|
||||
return
|
||||
rootLogger.critical(
|
||||
"Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback)
|
||||
)
|
||||
@ -46,6 +42,11 @@ if __name__ == "__main__":
|
||||
ymd = now.strftime("%Y%m%d")
|
||||
hms = now.strftime("%H%M%S")
|
||||
|
||||
rootLoggerFormatter = logging.Formatter(
|
||||
"[%(asctime)s/%(levelname)s] %(name)s (%(tag)s): %(message)s",
|
||||
"%m-%d %H:%M:%S",
|
||||
defaults={"tag": "/"},
|
||||
)
|
||||
rootLoggerFileHandler = logging.FileHandler(
|
||||
str(logFolder / f"arcaea-offline-pyside-ui-{ymd}-{hms}_debug.log"),
|
||||
encoding="utf-8",
|
||||
@ -59,16 +60,19 @@ if __name__ == "__main__":
|
||||
rootLogger.addHandler(rootLoggerStdOutHandler)
|
||||
|
||||
app = QApplication(sys.argv)
|
||||
locale = (
|
||||
QLocale(Settings().language()) if Settings().language() else QLocale.system()
|
||||
)
|
||||
settingsLanguage = settings.stringValue(SettingsKeys.General.Language)
|
||||
locale = QLocale(settingsLanguage) if settingsLanguage else QLocale.system()
|
||||
changeAppLanguage(locale)
|
||||
|
||||
QFontDatabase.addApplicationFont(":/fonts/GeosansLight.ttf")
|
||||
|
||||
databaseChecker = DatabaseChecker()
|
||||
databaseChecker.setWindowIcon(QIcon(":/images/icon.png"))
|
||||
databaseCheckResult = databaseChecker.confirmDb() if Settings().databaseUrl() else 0
|
||||
databaseCheckResult = (
|
||||
databaseChecker.confirmDb()
|
||||
if settings.stringValue(SettingsKeys.General.DatabaseUrl)
|
||||
else 0
|
||||
)
|
||||
|
||||
if not databaseCheckResult & DatabaseCheckerResult.Initted:
|
||||
result = databaseChecker.exec()
|
||||
|
@ -40,7 +40,6 @@ def getBuildToolsVer():
|
||||
|
||||
def writeVersionFile():
|
||||
versionFile = Path("ui/resources/VERSION")
|
||||
assert versionFile.exists()
|
||||
|
||||
versionText = (
|
||||
"arcaea-offline-pyside-ui\n{gitDesc}\n{buildToolsVer}\n\n"
|
||||
|
@ -4,14 +4,14 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "arcaea-offline-pyside-ui"
|
||||
version = "0.1.0"
|
||||
version = "0.3.9"
|
||||
authors = [{ name = "283375", email = "log_283375@163.com" }]
|
||||
description = "No description."
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.9"
|
||||
dependencies = [
|
||||
"arcaea-offline==0.1.0",
|
||||
"arcaea-offline-ocr==0.1.0",
|
||||
"arcaea-offline==0.2.2",
|
||||
"arcaea-offline-ocr==0.0.99",
|
||||
"exif==1.6.0",
|
||||
"PySide6==6.5.2",
|
||||
]
|
||||
@ -21,22 +21,37 @@ classifiers = [
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
"Homepage" = "https://github.com/283375/arcaea-offline-pyside-ui"
|
||||
"Bug Tracker" = "https://github.com/283375/arcaea-offline-pyside-ui/issues"
|
||||
"Homepage" = "https://github.com/ArcaeaOffline/client-pyside6"
|
||||
"Bug Tracker" = "https://github.com/ArcaeaOffline/client-pyside6/issues"
|
||||
|
||||
[tool.black]
|
||||
force-exclude = '''
|
||||
(
|
||||
ui/designer
|
||||
| .*_ui.py
|
||||
| .*_rc.py
|
||||
)
|
||||
'''
|
||||
[tool.ruff]
|
||||
exclude = ["*_ui.py", "*_rc.py"]
|
||||
|
||||
[tool.isort]
|
||||
profile = "black"
|
||||
extend_skip = ["ui/designer"]
|
||||
extend_skip_glob = ["*_ui.py", "*_rc.py"]
|
||||
[tool.ruff.lint]
|
||||
# Full list: https://docs.astral.sh/ruff/rules
|
||||
select = [
|
||||
"E", # pycodestyle (Error)
|
||||
"W", # pycodestyle (Warning)
|
||||
"F", # pyflakes
|
||||
"I", # isort
|
||||
"PL", # pylint
|
||||
"N", # pep8-naming
|
||||
"A", # flake8-builtins
|
||||
"DTZ", # flake8-datetimez
|
||||
"LOG", # flake8-logging
|
||||
"Q", # flake8-quotes
|
||||
"G", # flake8-logging-format
|
||||
"PIE", # flake8-pie
|
||||
"PT", # flake8-pytest-style
|
||||
]
|
||||
ignore = [
|
||||
"E501", # line-too-long
|
||||
"N802", # invalid-function-name
|
||||
"N803", # invalid-argument-name
|
||||
"N806", # non-lowercase-variable-in-function
|
||||
"N815", # mixed-case-variable-in-class-scope
|
||||
"N816", # mixed-case-variable-in-global-scope
|
||||
]
|
||||
|
||||
[tool.pyright]
|
||||
ignore = ["**/__debug*.*"]
|
||||
|
@ -1,5 +1,3 @@
|
||||
black == 23.7.0
|
||||
isort == 5.12.0
|
||||
imageio==2.31.4
|
||||
Nuitka==1.8.4
|
||||
pytest==7.4.3
|
||||
ruff
|
||||
imageio
|
||||
Nuitka~=2.7.6
|
||||
|
@ -1,5 +1,5 @@
|
||||
arcaea-offline==0.1.0
|
||||
arcaea-offline-ocr==0.1.0
|
||||
exif==1.6.0
|
||||
PySide6==6.5.2
|
||||
typing-extensions==4.8.0
|
||||
arcaea-offline==0.2.2
|
||||
arcaea-offline-ocr==0.0.99
|
||||
exif~=1.6.0
|
||||
Pillow~=10.1.0
|
||||
PySide6==6.9.1
|
||||
|
@ -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)
|
@ -22,39 +22,6 @@
|
||||
<string>queue.title</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>iccOptionsGroupBox</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="iccUseQtRadioButton">
|
||||
<property name="text">
|
||||
<string>icc.useQt</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="iccUsePILRadioButton">
|
||||
<property name="text">
|
||||
<string>icc.usePIL</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="iccTryFixRadioButton">
|
||||
<property name="text">
|
||||
<string>icc.tryFix</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="ocr_addImageButton">
|
||||
<property name="text">
|
||||
@ -95,6 +62,13 @@
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="optionsDialogButton">
|
||||
<property name="text">
|
||||
<string>queue.optionsButton</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="ocr_startButton">
|
||||
<property name="text">
|
||||
|
140
ui/designer/components/ocrQueueOptionsDialog.ui
Normal file
140
ui/designer/components/ocrQueueOptionsDialog.ui
Normal file
@ -0,0 +1,140 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>OcrQueueOptionsDialog</class>
|
||||
<widget class="QDialog" name="OcrQueueOptionsDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>331</width>
|
||||
<height>157</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>OCR Options</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>iccOptionsGroupBox</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="iccUseQtRadioButton">
|
||||
<property name="text">
|
||||
<string>icc.useQt</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="iccUsePILRadioButton">
|
||||
<property name="text">
|
||||
<string>icc.usePIL</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="iccTryFixRadioButton">
|
||||
<property name="text">
|
||||
<string>icc.tryFix</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_2">
|
||||
<property name="title">
|
||||
<string>dateOptionsGroupBox</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="dateReadFromExifCheckbox">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>date.readFromExif</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="dateUseCreationDateRadioButton">
|
||||
<property name="text">
|
||||
<string>date.useCreationDate</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="dateUseModifyDateRadioButton">
|
||||
<property name="text">
|
||||
<string>date.useModifyDate</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>OcrQueueOptionsDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>OcrQueueOptionsDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
107
ui/designer/components/ocrQueueOptionsDialog_ui.py
Normal file
107
ui/designer/components/ocrQueueOptionsDialog_ui.py
Normal file
@ -0,0 +1,107 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
################################################################################
|
||||
## Form generated from reading UI file 'ocrQueueOptionsDialog.ui'
|
||||
##
|
||||
## Created by: Qt User Interface Compiler version 6.5.2
|
||||
##
|
||||
## WARNING! All changes made in this file will be lost when recompiling UI file!
|
||||
################################################################################
|
||||
|
||||
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
|
||||
QMetaObject, QObject, QPoint, QRect,
|
||||
QSize, QTime, QUrl, Qt)
|
||||
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
|
||||
QFont, QFontDatabase, QGradient, QIcon,
|
||||
QImage, QKeySequence, QLinearGradient, QPainter,
|
||||
QPalette, QPixmap, QRadialGradient, QTransform)
|
||||
from PySide6.QtWidgets import (QAbstractButton, QApplication, QCheckBox, QDialog,
|
||||
QDialogButtonBox, QGroupBox, QHBoxLayout, QRadioButton,
|
||||
QSizePolicy, QVBoxLayout, QWidget)
|
||||
|
||||
class Ui_OcrQueueOptionsDialog(object):
|
||||
def setupUi(self, OcrQueueOptionsDialog):
|
||||
if not OcrQueueOptionsDialog.objectName():
|
||||
OcrQueueOptionsDialog.setObjectName(u"OcrQueueOptionsDialog")
|
||||
OcrQueueOptionsDialog.resize(331, 157)
|
||||
self.verticalLayout = QVBoxLayout(OcrQueueOptionsDialog)
|
||||
self.verticalLayout.setObjectName(u"verticalLayout")
|
||||
self.horizontalLayout = QHBoxLayout()
|
||||
self.horizontalLayout.setObjectName(u"horizontalLayout")
|
||||
self.groupBox = QGroupBox(OcrQueueOptionsDialog)
|
||||
self.groupBox.setObjectName(u"groupBox")
|
||||
self.verticalLayout_2 = QVBoxLayout(self.groupBox)
|
||||
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
|
||||
self.iccUseQtRadioButton = QRadioButton(self.groupBox)
|
||||
self.iccUseQtRadioButton.setObjectName(u"iccUseQtRadioButton")
|
||||
|
||||
self.verticalLayout_2.addWidget(self.iccUseQtRadioButton)
|
||||
|
||||
self.iccUsePILRadioButton = QRadioButton(self.groupBox)
|
||||
self.iccUsePILRadioButton.setObjectName(u"iccUsePILRadioButton")
|
||||
self.iccUsePILRadioButton.setChecked(True)
|
||||
|
||||
self.verticalLayout_2.addWidget(self.iccUsePILRadioButton)
|
||||
|
||||
self.iccTryFixRadioButton = QRadioButton(self.groupBox)
|
||||
self.iccTryFixRadioButton.setObjectName(u"iccTryFixRadioButton")
|
||||
|
||||
self.verticalLayout_2.addWidget(self.iccTryFixRadioButton)
|
||||
|
||||
|
||||
self.horizontalLayout.addWidget(self.groupBox)
|
||||
|
||||
self.groupBox_2 = QGroupBox(OcrQueueOptionsDialog)
|
||||
self.groupBox_2.setObjectName(u"groupBox_2")
|
||||
self.verticalLayout_3 = QVBoxLayout(self.groupBox_2)
|
||||
self.verticalLayout_3.setObjectName(u"verticalLayout_3")
|
||||
self.dateReadFromExifCheckbox = QCheckBox(self.groupBox_2)
|
||||
self.dateReadFromExifCheckbox.setObjectName(u"dateReadFromExifCheckbox")
|
||||
self.dateReadFromExifCheckbox.setEnabled(False)
|
||||
self.dateReadFromExifCheckbox.setChecked(True)
|
||||
|
||||
self.verticalLayout_3.addWidget(self.dateReadFromExifCheckbox)
|
||||
|
||||
self.dateUseCreationDateRadioButton = QRadioButton(self.groupBox_2)
|
||||
self.dateUseCreationDateRadioButton.setObjectName(u"dateUseCreationDateRadioButton")
|
||||
self.dateUseCreationDateRadioButton.setChecked(True)
|
||||
|
||||
self.verticalLayout_3.addWidget(self.dateUseCreationDateRadioButton)
|
||||
|
||||
self.dateUseModifyDateRadioButton = QRadioButton(self.groupBox_2)
|
||||
self.dateUseModifyDateRadioButton.setObjectName(u"dateUseModifyDateRadioButton")
|
||||
|
||||
self.verticalLayout_3.addWidget(self.dateUseModifyDateRadioButton)
|
||||
|
||||
|
||||
self.horizontalLayout.addWidget(self.groupBox_2)
|
||||
|
||||
|
||||
self.verticalLayout.addLayout(self.horizontalLayout)
|
||||
|
||||
self.buttonBox = QDialogButtonBox(OcrQueueOptionsDialog)
|
||||
self.buttonBox.setObjectName(u"buttonBox")
|
||||
self.buttonBox.setOrientation(Qt.Horizontal)
|
||||
self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel|QDialogButtonBox.Ok)
|
||||
|
||||
self.verticalLayout.addWidget(self.buttonBox)
|
||||
|
||||
|
||||
self.retranslateUi(OcrQueueOptionsDialog)
|
||||
self.buttonBox.accepted.connect(OcrQueueOptionsDialog.accept)
|
||||
self.buttonBox.rejected.connect(OcrQueueOptionsDialog.reject)
|
||||
|
||||
QMetaObject.connectSlotsByName(OcrQueueOptionsDialog)
|
||||
# setupUi
|
||||
|
||||
def retranslateUi(self, OcrQueueOptionsDialog):
|
||||
OcrQueueOptionsDialog.setWindowTitle(QCoreApplication.translate("OcrQueueOptionsDialog", u"OCR Options", None))
|
||||
self.groupBox.setTitle(QCoreApplication.translate("OcrQueueOptionsDialog", u"iccOptionsGroupBox", None))
|
||||
self.iccUseQtRadioButton.setText(QCoreApplication.translate("OcrQueueOptionsDialog", u"icc.useQt", None))
|
||||
self.iccUsePILRadioButton.setText(QCoreApplication.translate("OcrQueueOptionsDialog", u"icc.usePIL", None))
|
||||
self.iccTryFixRadioButton.setText(QCoreApplication.translate("OcrQueueOptionsDialog", u"icc.tryFix", None))
|
||||
self.groupBox_2.setTitle(QCoreApplication.translate("OcrQueueOptionsDialog", u"dateOptionsGroupBox", None))
|
||||
self.dateReadFromExifCheckbox.setText(QCoreApplication.translate("OcrQueueOptionsDialog", u"date.readFromExif", None))
|
||||
self.dateUseCreationDateRadioButton.setText(QCoreApplication.translate("OcrQueueOptionsDialog", u"date.useCreationDate", None))
|
||||
self.dateUseModifyDateRadioButton.setText(QCoreApplication.translate("OcrQueueOptionsDialog", u"date.useModifyDate", None))
|
||||
# retranslateUi
|
@ -17,8 +17,8 @@ from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
|
||||
QPalette, QPixmap, QRadialGradient, QTransform)
|
||||
from PySide6.QtWidgets import (QAbstractItemView, QApplication, QCheckBox, QGroupBox,
|
||||
QHBoxLayout, QHeaderView, QLabel, QProgressBar,
|
||||
QPushButton, QRadioButton, QSizePolicy, QSpacerItem,
|
||||
QTableView, QVBoxLayout, QWidget)
|
||||
QPushButton, QSizePolicy, QSpacerItem, QTableView,
|
||||
QVBoxLayout, QWidget)
|
||||
|
||||
class Ui_OcrQueue(object):
|
||||
def setupUi(self, OcrQueue):
|
||||
@ -34,29 +34,6 @@ class Ui_OcrQueue(object):
|
||||
self.groupBox_3.setObjectName(u"groupBox_3")
|
||||
self.verticalLayout_2 = QVBoxLayout(self.groupBox_3)
|
||||
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
|
||||
self.groupBox = QGroupBox(self.groupBox_3)
|
||||
self.groupBox.setObjectName(u"groupBox")
|
||||
self.verticalLayout = QVBoxLayout(self.groupBox)
|
||||
self.verticalLayout.setObjectName(u"verticalLayout")
|
||||
self.iccUseQtRadioButton = QRadioButton(self.groupBox)
|
||||
self.iccUseQtRadioButton.setObjectName(u"iccUseQtRadioButton")
|
||||
|
||||
self.verticalLayout.addWidget(self.iccUseQtRadioButton)
|
||||
|
||||
self.iccUsePILRadioButton = QRadioButton(self.groupBox)
|
||||
self.iccUsePILRadioButton.setObjectName(u"iccUsePILRadioButton")
|
||||
self.iccUsePILRadioButton.setChecked(True)
|
||||
|
||||
self.verticalLayout.addWidget(self.iccUsePILRadioButton)
|
||||
|
||||
self.iccTryFixRadioButton = QRadioButton(self.groupBox)
|
||||
self.iccTryFixRadioButton.setObjectName(u"iccTryFixRadioButton")
|
||||
|
||||
self.verticalLayout.addWidget(self.iccTryFixRadioButton)
|
||||
|
||||
|
||||
self.verticalLayout_2.addWidget(self.groupBox)
|
||||
|
||||
self.ocr_addImageButton = QPushButton(self.groupBox_3)
|
||||
self.ocr_addImageButton.setObjectName(u"ocr_addImageButton")
|
||||
|
||||
@ -78,6 +55,11 @@ class Ui_OcrQueue(object):
|
||||
|
||||
self.verticalLayout_2.addItem(self.verticalSpacer)
|
||||
|
||||
self.optionsDialogButton = QPushButton(self.groupBox_3)
|
||||
self.optionsDialogButton.setObjectName(u"optionsDialogButton")
|
||||
|
||||
self.verticalLayout_2.addWidget(self.optionsDialogButton)
|
||||
|
||||
self.ocr_startButton = QPushButton(self.groupBox_3)
|
||||
self.ocr_startButton.setObjectName(u"ocr_startButton")
|
||||
|
||||
@ -154,13 +136,10 @@ class Ui_OcrQueue(object):
|
||||
|
||||
def retranslateUi(self, OcrQueue):
|
||||
self.groupBox_3.setTitle(QCoreApplication.translate("OcrQueue", u"queue.title", None))
|
||||
self.groupBox.setTitle(QCoreApplication.translate("OcrQueue", u"iccOptionsGroupBox", None))
|
||||
self.iccUseQtRadioButton.setText(QCoreApplication.translate("OcrQueue", u"icc.useQt", None))
|
||||
self.iccUsePILRadioButton.setText(QCoreApplication.translate("OcrQueue", u"icc.usePIL", None))
|
||||
self.iccTryFixRadioButton.setText(QCoreApplication.translate("OcrQueue", u"icc.tryFix", None))
|
||||
self.ocr_addImageButton.setText(QCoreApplication.translate("OcrQueue", u"queue.addImageButton", None))
|
||||
self.ocr_removeSelectedButton.setText(QCoreApplication.translate("OcrQueue", u"queue.removeSelected", None))
|
||||
self.ocr_removeAllButton.setText(QCoreApplication.translate("OcrQueue", u"queue.removeAll", None))
|
||||
self.optionsDialogButton.setText(QCoreApplication.translate("OcrQueue", u"queue.optionsButton", None))
|
||||
self.ocr_startButton.setText(QCoreApplication.translate("OcrQueue", u"queue.startOcrButton", None))
|
||||
self.groupBox_5.setTitle(QCoreApplication.translate("OcrQueue", u"results", None))
|
||||
self.ocr_acceptSelectedButton.setText(QCoreApplication.translate("OcrQueue", u"results.acceptSelectedButton", None))
|
||||
@ -169,4 +148,3 @@ class Ui_OcrQueue(object):
|
||||
self.statusLabel.setText("")
|
||||
pass
|
||||
# retranslateUi
|
||||
|
||||
|
@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>630</width>
|
||||
<height>528</height>
|
||||
<width>580</width>
|
||||
<height>551</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@ -17,70 +17,21 @@
|
||||
<property name="labelAlignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<item row="0" 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">
|
||||
<item row="1" column="0">
|
||||
<widget class="QPushButton" name="importPacklistButton">
|
||||
<property name="text">
|
||||
<string>importPacklistButton</string>
|
||||
</property>
|
||||
</widget>
|
||||
</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">
|
||||
<property name="text">
|
||||
<string>importSonglistButton</string>
|
||||
@ -88,69 +39,129 @@
|
||||
</widget>
|
||||
</item>
|
||||
<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">
|
||||
<property name="text">
|
||||
<string>importSonglist.description</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" 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">
|
||||
<item row="3" column="0">
|
||||
<widget class="QPushButton" name="importApkButton">
|
||||
<property name="text">
|
||||
<string>importApkButton</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<item row="3" column="1">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string>importApk.description</string>
|
||||
</property>
|
||||
</widget>
|
||||
</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">
|
||||
<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">
|
||||
<property name="text">
|
||||
<string>importOnlineButton</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<item row="11" column="1">
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="text">
|
||||
<string>importOnline.description</string>
|
||||
</property>
|
||||
</widget>
|
||||
</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">
|
||||
<property name="text">
|
||||
<string>exportSmartRteB30Button</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="1">
|
||||
<item row="15" column="1">
|
||||
<widget class="QLabel" name="label_9">
|
||||
<property name="text">
|
||||
<string>exportSmartRteB30.description</string>
|
||||
@ -163,6 +174,124 @@
|
||||
</property>
|
||||
</widget>
|
||||
</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>
|
||||
</widget>
|
||||
<resources/>
|
||||
|
@ -15,123 +15,168 @@ from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
|
||||
QFont, QFontDatabase, QGradient, QIcon,
|
||||
QImage, QKeySequence, QLinearGradient, QPainter,
|
||||
QPalette, QPixmap, QRadialGradient, QTransform)
|
||||
from PySide6.QtWidgets import (QApplication, QFormLayout, QFrame, QLabel,
|
||||
QPushButton, QSizePolicy, QWidget)
|
||||
from PySide6.QtWidgets import (QApplication, QFormLayout, QLabel, QPushButton,
|
||||
QSizePolicy, QSpacerItem, QWidget)
|
||||
|
||||
class Ui_TabDb_Manage(object):
|
||||
def setupUi(self, TabDb_Manage):
|
||||
if not TabDb_Manage.objectName():
|
||||
TabDb_Manage.setObjectName(u"TabDb_Manage")
|
||||
TabDb_Manage.resize(630, 528)
|
||||
TabDb_Manage.resize(580, 551)
|
||||
TabDb_Manage.setWindowTitle(u"TabDb_Manage")
|
||||
self.formLayout = QFormLayout(TabDb_Manage)
|
||||
self.formLayout.setObjectName(u"formLayout")
|
||||
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.setObjectName(u"importPacklistButton")
|
||||
|
||||
self.formLayout.setWidget(2, QFormLayout.LabelRole, self.importPacklistButton)
|
||||
|
||||
self.importSonglistButton = QPushButton(TabDb_Manage)
|
||||
self.importSonglistButton.setObjectName(u"importSonglistButton")
|
||||
|
||||
self.formLayout.setWidget(3, QFormLayout.LabelRole, self.importSonglistButton)
|
||||
self.formLayout.setWidget(1, QFormLayout.LabelRole, self.importPacklistButton)
|
||||
|
||||
self.label_4 = QLabel(TabDb_Manage)
|
||||
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.setObjectName(u"label_5")
|
||||
|
||||
self.formLayout.setWidget(3, 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.formLayout.setWidget(2, QFormLayout.FieldRole, self.label_5)
|
||||
|
||||
self.importApkButton = QPushButton(TabDb_Manage)
|
||||
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.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.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.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.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.setObjectName(u"label_9")
|
||||
self.label_9.setOpenExternalLinks(True)
|
||||
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)
|
||||
@ -140,24 +185,30 @@ class Ui_TabDb_Manage(object):
|
||||
# setupUi
|
||||
|
||||
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.importSonglistButton.setText(QCoreApplication.translate("TabDb_Manage", u"importSonglistButton", 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.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.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.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.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
|
||||
# retranslateUi
|
||||
|
||||
|
@ -5,7 +5,6 @@ from typing import Any, Callable, Optional, overload
|
||||
from arcaea_offline.calculate import calculate_score_range
|
||||
from arcaea_offline.database import Database
|
||||
from arcaea_offline.models import Chart, Score
|
||||
from arcaea_offline_ocr.b30.shared import B30OcrResultItem
|
||||
from arcaea_offline_ocr.device.common import DeviceOcrResult
|
||||
from PIL import Image
|
||||
from PIL.ImageQt import ImageQt
|
||||
@ -140,7 +139,11 @@ class OcrQueueModel(QAbstractListModel):
|
||||
return True
|
||||
else:
|
||||
logger.warning(
|
||||
f"{repr(self)} setData at row {index.row()} with role {role} and value {value} rejected."
|
||||
"%r setData at row %d with role %d and value %s rejected!",
|
||||
self,
|
||||
index.row(),
|
||||
role,
|
||||
value,
|
||||
)
|
||||
return False
|
||||
|
||||
@ -150,6 +153,7 @@ class OcrQueueModel(QAbstractListModel):
|
||||
|
||||
@iccOption.setter
|
||||
def iccOption(self, opt: IccOption):
|
||||
logger.debug("ICC option changed to %s", opt)
|
||||
self.__iccOption = opt
|
||||
|
||||
@overload
|
||||
@ -158,8 +162,7 @@ class OcrQueueModel(QAbstractListModel):
|
||||
image: str,
|
||||
runnable: OcrRunnable = None,
|
||||
process_func: Callable[[Optional[str], QImage, Any], Score] = None,
|
||||
):
|
||||
...
|
||||
): ...
|
||||
|
||||
@overload
|
||||
def addItem(
|
||||
@ -167,8 +170,7 @@ class OcrQueueModel(QAbstractListModel):
|
||||
image: QImage,
|
||||
runnable: OcrRunnable = None,
|
||||
process_func: Callable[[Optional[str], QImage, Any], Score] = None,
|
||||
):
|
||||
...
|
||||
): ...
|
||||
|
||||
def addItem(
|
||||
self,
|
||||
@ -178,7 +180,7 @@ class OcrQueueModel(QAbstractListModel):
|
||||
):
|
||||
if isinstance(image, str):
|
||||
if image in self.imagePaths or not QFileInfo(image).exists():
|
||||
logger.warning(f"Attempting to add an invalid file {image}")
|
||||
logger.warning("Attempting to add an invalid file %s", image)
|
||||
return
|
||||
imagePath = image
|
||||
if self.iccOption == IccOption.TryFix:
|
||||
@ -222,7 +224,7 @@ class OcrQueueModel(QAbstractListModel):
|
||||
index = self.index(row, 0)
|
||||
imagePath: str = index.data(self.ImagePathRole)
|
||||
qImage: QImage = index.data(self.ImageQImageRole)
|
||||
logger.debug(f"update request: {result}@row{row}")
|
||||
logger.debug("update request: %r@row%d", result, row)
|
||||
processOcrResultFunc = index.data(self.ProcessOcrResultFuncRole)
|
||||
|
||||
chart, scoreInsert = processOcrResultFunc(imagePath, qImage, result)
|
||||
@ -293,8 +295,8 @@ class OcrQueueModel(QAbstractListModel):
|
||||
self.__items.pop(row)
|
||||
self.endRemoveRows()
|
||||
return
|
||||
except Exception as e:
|
||||
logger.exception(f"Error accepting {repr(item)}")
|
||||
except Exception:
|
||||
logger.exception("Error accepting %r", item)
|
||||
return
|
||||
|
||||
def acceptItems(self, __rows: list[int], ignoreValidate: bool = False):
|
||||
@ -343,13 +345,11 @@ class OcrQueueTableProxyModel(QAbstractTableModel):
|
||||
|
||||
def retranslateHeaders(self):
|
||||
self.__horizontalHeaders = [
|
||||
# fmt: off
|
||||
QCoreApplication.translate("OcrTableModel", "horizontalHeader.title.select"),
|
||||
QCoreApplication.translate("OcrTableModel", "horizontalHeader.title.imagePreview"),
|
||||
QCoreApplication.translate("OcrTableModel", "horizontalHeader.title.chart"),
|
||||
QCoreApplication.translate("OcrTableModel", "horizontalHeader.title.score"),
|
||||
# fmt: on
|
||||
]
|
||||
] # fmt: skip
|
||||
|
||||
def sourceModel(self) -> OcrQueueModel:
|
||||
return self.__sourceModel
|
||||
|
@ -1,16 +0,0 @@
|
||||
from PySide6.QtGui import QColor
|
||||
|
||||
|
||||
def mix_color(source_color: QColor, mix_color: QColor, mix_ratio: float = 0.5):
|
||||
r = round((mix_color.red() - source_color.red()) * mix_ratio + source_color.red())
|
||||
g = round(
|
||||
(mix_color.green() - source_color.green()) * mix_ratio + source_color.green()
|
||||
)
|
||||
b = round(
|
||||
(mix_color.blue() - source_color.blue()) * mix_ratio + source_color.blue()
|
||||
)
|
||||
a = round(
|
||||
(mix_color.alpha() - source_color.alpha()) * mix_ratio + source_color.alpha()
|
||||
)
|
||||
|
||||
return QColor(r, g, b, a)
|
@ -7,7 +7,7 @@ from typing import Literal, Optional, overload
|
||||
from arcaea_offline.models import Chart, Difficulty, Song
|
||||
from PySide6.QtCore import QFile
|
||||
|
||||
from .singleton import Singleton
|
||||
from core.singleton import Singleton
|
||||
|
||||
TPartnerModifier = dict[str, Literal[0, 1, 2]]
|
||||
|
||||
@ -48,14 +48,12 @@ class Data(metaclass=Singleton):
|
||||
return self.dataPath / "Arcaea"
|
||||
|
||||
@overload
|
||||
def getJacketPath(self, chart: Chart, /) -> Path | None:
|
||||
...
|
||||
def getJacketPath(self, chart: Chart, /) -> Path | None: ...
|
||||
|
||||
@overload
|
||||
def getJacketPath(
|
||||
self, song: Song, difficulty: Optional[Difficulty] = None, /
|
||||
) -> Path | None:
|
||||
...
|
||||
) -> Path | None: ...
|
||||
|
||||
def getJacketPath(self, *args) -> Path | None:
|
||||
if isinstance(args[0], Chart):
|
||||
|
@ -82,6 +82,7 @@ class ChartDelegate(TextSegmentDelegate):
|
||||
QColor("#809955"),
|
||||
QColor("#702d60"),
|
||||
QColor("#710f25"),
|
||||
QColor("#8b77a4"),
|
||||
]
|
||||
ChartInvalidBackgroundColor = QColor("#e6a23c")
|
||||
|
||||
|
@ -16,13 +16,11 @@ class DbB30TableModel(DbTableModel):
|
||||
|
||||
def retranslateHeaders(self):
|
||||
self._horizontalHeaders = [
|
||||
# fmt: off
|
||||
QCoreApplication.translate("DbB30TableModel", "horizontalHeader.id"),
|
||||
QCoreApplication.translate("DbB30TableModel", "horizontalHeader.chart"),
|
||||
QCoreApplication.translate("DbB30TableModel", "horizontalHeader.score"),
|
||||
QCoreApplication.translate("DbB30TableModel", "horizontalHeader.potential"),
|
||||
# fmt: on
|
||||
]
|
||||
] # fmt: skip
|
||||
|
||||
def syncDb(self):
|
||||
self.beginResetModel()
|
||||
@ -39,7 +37,7 @@ class DbB30TableModel(DbTableModel):
|
||||
(ScoreBest.song_id == Chart.song_id)
|
||||
& (ScoreBest.rating_class == Chart.rating_class),
|
||||
)
|
||||
.order_by(ScoreBest.potential)
|
||||
.order_by(ScoreBest.potential.desc())
|
||||
.limit(50)
|
||||
.all()
|
||||
)
|
||||
|
@ -24,13 +24,11 @@ class DbScoreTableModel(DbTableModel):
|
||||
|
||||
def retranslateHeaders(self):
|
||||
self._horizontalHeaders = [
|
||||
# fmt: off
|
||||
QCoreApplication.translate("DbScoreTableModel", "horizontalHeader.id"),
|
||||
QCoreApplication.translate("DbScoreTableModel", "horizontalHeader.chart"),
|
||||
QCoreApplication.translate("DbScoreTableModel", "horizontalHeader.score"),
|
||||
QCoreApplication.translate("DbScoreTableModel", "horizontalHeader.potential"),
|
||||
# fmt: on
|
||||
]
|
||||
] # fmt: skip
|
||||
|
||||
def syncDb(self):
|
||||
self.beginResetModel()
|
||||
@ -154,7 +152,7 @@ class DbScoreTableModel(DbTableModel):
|
||||
self.syncDb()
|
||||
return True
|
||||
except Exception:
|
||||
logger.exception(f"Table[Score]: Cannot remove row {row}")
|
||||
logger.exception("Table[Score]: Cannot remove row %s", row)
|
||||
return False
|
||||
|
||||
def removeRow(self, row: int, parent=...):
|
||||
|
@ -1,111 +0,0 @@
|
||||
import sys
|
||||
|
||||
from PySide6.QtCore import QFileInfo, QSettings, Signal
|
||||
|
||||
from .singleton import QObjectSingleton
|
||||
|
||||
__all__ = [
|
||||
"LANGUAGE",
|
||||
"DATABASE_URL",
|
||||
"KNN_MODEL_FILE",
|
||||
"B30_KNN_MODEL_FILE",
|
||||
"PHASH_DATABASE_FILE",
|
||||
"ANDREAL_FOLDER",
|
||||
"ANDREAL_EXECUTABLE",
|
||||
"Settings",
|
||||
]
|
||||
|
||||
# a key without slashes will appear in the "General" section
|
||||
# see https://doc.qt.io/qt-6/qsettings.html#Format-enum for details
|
||||
LANGUAGE = "Language"
|
||||
DATABASE_URL = "DatabaseUrl"
|
||||
|
||||
KNN_MODEL_FILE = "Ocr/KnnModelFile"
|
||||
B30_KNN_MODEL_FILE = "Ocr/B30KnnModelFile"
|
||||
PHASH_DATABASE_FILE = "Ocr/PHashDatabaseFile"
|
||||
|
||||
ANDREAL_FOLDER = "Andreal/AndrealFolder"
|
||||
ANDREAL_EXECUTABLE = "Andreal/AndrealExecutable"
|
||||
|
||||
|
||||
class Settings(QSettings, metaclass=QObjectSingleton):
|
||||
updated = Signal(str)
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(
|
||||
QFileInfo(sys.argv[0]).dir().absoluteFilePath("arcaea_offline.ini"),
|
||||
QSettings.Format.IniFormat,
|
||||
parent,
|
||||
)
|
||||
|
||||
def setValue(self, key: str, value) -> None:
|
||||
super().setValue(key, value)
|
||||
self.updated.emit(key)
|
||||
|
||||
def _strItem(self, key: str) -> str | None:
|
||||
return self.value(key, None, str)
|
||||
|
||||
def _setStrItem(self, key: str, value: str):
|
||||
self.setValue(key, value)
|
||||
self.sync()
|
||||
|
||||
def _resetStrItem(self, key: str):
|
||||
self.setValue(key, None)
|
||||
self.sync()
|
||||
|
||||
def language(self):
|
||||
return self._strItem(LANGUAGE)
|
||||
|
||||
def setLanguage(self, value: str):
|
||||
self._setStrItem(LANGUAGE, value)
|
||||
|
||||
def databaseUrl(self):
|
||||
return self._strItem(DATABASE_URL)
|
||||
|
||||
def setDatabaseUrl(self, value: str):
|
||||
self._setStrItem(DATABASE_URL, value)
|
||||
|
||||
def knnModelFile(self):
|
||||
return self._strItem(KNN_MODEL_FILE)
|
||||
|
||||
def setKnnModelFile(self, value: str):
|
||||
self._setStrItem(KNN_MODEL_FILE, value)
|
||||
|
||||
def resetKnnModelFile(self):
|
||||
self._resetStrItem(KNN_MODEL_FILE)
|
||||
|
||||
def b30KnnModelFile(self):
|
||||
return self._strItem(B30_KNN_MODEL_FILE)
|
||||
|
||||
def setB30KnnModelFile(self, value: str):
|
||||
self._setStrItem(B30_KNN_MODEL_FILE, value)
|
||||
|
||||
def resetB30KnnModelFile(self):
|
||||
self._resetStrItem(B30_KNN_MODEL_FILE)
|
||||
|
||||
def phashDatabaseFile(self):
|
||||
return self._strItem(PHASH_DATABASE_FILE)
|
||||
|
||||
def setPHashDatabaseFile(self, value: str):
|
||||
self._setStrItem(PHASH_DATABASE_FILE, value)
|
||||
|
||||
def resetPHashDatabaseFile(self):
|
||||
self._resetStrItem(PHASH_DATABASE_FILE)
|
||||
|
||||
def andrealFolder(self):
|
||||
return self._strItem(ANDREAL_FOLDER)
|
||||
|
||||
def setAndrealFolder(self, value: str):
|
||||
self._setStrItem(ANDREAL_FOLDER, value)
|
||||
|
||||
def resetAndrealFolder(self):
|
||||
self._resetStrItem(ANDREAL_FOLDER)
|
||||
|
||||
def andrealExecutable(self):
|
||||
return self._strItem(ANDREAL_EXECUTABLE)
|
||||
|
||||
def setAndrealExecutable(self, value: str):
|
||||
self._setStrItem(ANDREAL_EXECUTABLE, value)
|
||||
|
||||
def resetAndrealExecutable(self):
|
||||
self._resetStrItem(ANDREAL_EXECUTABLE)
|
@ -6,10 +6,10 @@ from arcaea_offline_ocr.b30.chieri.v4.ocr import ChieriBotV4Ocr
|
||||
from arcaea_offline_ocr.b30.shared import B30OcrResultItem
|
||||
from PySide6.QtGui import QImage
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
from ui.extends.components.ocrQueue import OcrRunnable
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ChieriV4OcrRunnable(OcrRunnable):
|
||||
def __init__(self, ocr: ChieriBotV4Ocr, component):
|
||||
|
@ -19,6 +19,7 @@ from arcaea_offline_ocr.phash_db import ImagePhashDatabase
|
||||
from arcaea_offline_ocr.utils import imread_unicode
|
||||
from PySide6.QtCore import QDateTime, QFileInfo
|
||||
|
||||
from core.settings import SettingsKeys, SettingsValues, settings
|
||||
from ui.extends.components.ocrQueue import OcrRunnable
|
||||
from ui.extends.shared.data import Data
|
||||
|
||||
@ -67,8 +68,14 @@ def getImageDate(imagePath: str) -> QDateTime:
|
||||
if exifImage.has_exif and exifImage.get("datetime_original"):
|
||||
datetimeStr = exifImage.get("datetime_original")
|
||||
datetime = QDateTime.fromString(datetimeStr, "yyyy:MM:dd hh:mm:ss")
|
||||
|
||||
if not isinstance(datetime, QDateTime):
|
||||
datetime = QFileInfo(imagePath).birthTime()
|
||||
dateSource = settings.stringValue(SettingsKeys.Ocr.DateSource)
|
||||
if dateSource == SettingsValues.Ocr.DateSource.FileLastModified:
|
||||
datetime = QFileInfo(imagePath).lastModified()
|
||||
else:
|
||||
datetime = QFileInfo(imagePath).birthTime()
|
||||
|
||||
return datetime
|
||||
|
||||
|
||||
|
@ -41,7 +41,7 @@ class AndrealExecuteRunnable(QRunnable):
|
||||
self.signals.completed.emit(self.jsonPath, imageBytes)
|
||||
except Exception as e:
|
||||
imageBytes = None
|
||||
logger.exception(f"{self.__class__.__name__} error")
|
||||
logger.exception("%s error", self.__class__.__name__)
|
||||
self.signals.error.emit(self.jsonPath, str(e))
|
||||
finally:
|
||||
os.unlink(self.jsonPath)
|
||||
@ -84,7 +84,10 @@ class AndrealHelper(QObject):
|
||||
|
||||
def request(self, jsonPath: str, arguments: list[str]):
|
||||
logger.debug(
|
||||
f"{self.__class__.__name__} received request {jsonPath=} {arguments=}"
|
||||
"%s received request jsonPath=%r arguments=%r",
|
||||
self.__class__.__name__,
|
||||
jsonPath,
|
||||
arguments,
|
||||
)
|
||||
runnable = AndrealExecuteRunnable(self.andrealExecutable, jsonPath, arguments)
|
||||
runnable.signals.error.connect(self.error)
|
||||
|
@ -2,9 +2,9 @@ from PySide6.QtCore import QDir, QFileInfo, Qt, Signal, Slot
|
||||
from PySide6.QtGui import QDragEnterEvent, QDragLeaveEvent, QDropEvent
|
||||
from PySide6.QtWidgets import QFileDialog, QWidget
|
||||
|
||||
from core.settings import settings
|
||||
from ui.designer.components.fileSelector_ui import Ui_FileSelector
|
||||
from ui.extends.shared.language import LanguageChangeEventFilter
|
||||
from ui.extends.shared.settings import Settings
|
||||
|
||||
|
||||
class FileSelector(Ui_FileSelector, QWidget):
|
||||
@ -122,13 +122,13 @@ class FileSelector(Ui_FileSelector, QWidget):
|
||||
if self.__selectedFiles:
|
||||
return
|
||||
|
||||
if value := Settings().value(self.settingsKey):
|
||||
if value := settings.value(self.settingsKey):
|
||||
self.selectFile(value)
|
||||
|
||||
Settings().updated.connect(self.settingsUpdated)
|
||||
settings.updated.connect(self.settingsUpdated)
|
||||
|
||||
def disconnectSettings(self):
|
||||
Settings().updated.disconnect(self.settingsUpdated)
|
||||
settings.updated.disconnect(self.settingsUpdated)
|
||||
self.settingsKey = None
|
||||
|
||||
def settingsUpdated(self, key: str):
|
||||
@ -139,4 +139,4 @@ class FileSelector(Ui_FileSelector, QWidget):
|
||||
if self.__selectedFiles:
|
||||
return
|
||||
|
||||
self.selectFile(Settings().value(self.settingsKey))
|
||||
self.selectFile(settings.value(self.settingsKey))
|
||||
|
@ -2,7 +2,7 @@ from typing import Optional
|
||||
|
||||
from PySide6.QtCore import Qt, QTimer, Slot
|
||||
from PySide6.QtGui import QColor, QPalette
|
||||
from PySide6.QtWidgets import QButtonGroup, QWidget
|
||||
from PySide6.QtWidgets import QWidget
|
||||
|
||||
from ui.designer.components.ocrQueue_ui import Ui_OcrQueue
|
||||
from ui.extends.components.ocrQueue import (
|
||||
@ -13,6 +13,7 @@ from ui.extends.components.ocrQueue import (
|
||||
OcrScoreDelegate,
|
||||
)
|
||||
from ui.extends.shared.language import LanguageChangeEventFilter
|
||||
from ui.implements.components.ocrQueueOptionsDialog import OcrQueueOptionsDialog
|
||||
|
||||
|
||||
class OcrQueue(Ui_OcrQueue, QWidget):
|
||||
@ -26,6 +27,9 @@ class OcrQueue(Ui_OcrQueue, QWidget):
|
||||
self.__model: Optional[OcrQueueModel] = None
|
||||
self.__tableProxyModel: Optional[OcrQueueTableProxyModel] = None
|
||||
|
||||
self.optionsDialog = OcrQueueOptionsDialog(self)
|
||||
self.optionsDialog.iccOptionsChanged.connect(self.setIccOption)
|
||||
|
||||
self.__firstResizeDone = False
|
||||
self.resizeTimer = QTimer(self)
|
||||
self.resizeTimer.timeout.connect(self.tableView.resizeRowsToContents)
|
||||
@ -41,13 +45,6 @@ class OcrQueue(Ui_OcrQueue, QWidget):
|
||||
tableViewPalette.setColor(QPalette.ColorRole.Highlight, highlightColor)
|
||||
self.tableView.setPalette(tableViewPalette)
|
||||
|
||||
self.iccOptionButtonGroup = QButtonGroup(self)
|
||||
self.iccOptionButtonGroup.buttonToggled.connect(self.updateIccOption)
|
||||
self.iccOptionButtonGroup.addButton(self.iccUseQtRadioButton, 0)
|
||||
self.iccOptionButtonGroup.addButton(self.iccUsePILRadioButton, 1)
|
||||
self.iccOptionButtonGroup.addButton(self.iccTryFixRadioButton, 2)
|
||||
self.updateIccOption()
|
||||
|
||||
self.statusLabelClearTimer = QTimer(self)
|
||||
self.statusLabelClearTimer.setSingleShot(True)
|
||||
self.statusLabelClearTimer.timeout.connect(self.clearStatusMessage)
|
||||
@ -93,9 +90,10 @@ class OcrQueue(Ui_OcrQueue, QWidget):
|
||||
self.ocr_acceptAllButton.setEnabled(__bool)
|
||||
self.ocr_ignoreValidateCheckBox.setEnabled(__bool)
|
||||
|
||||
def updateIccOption(self):
|
||||
@Slot(int)
|
||||
def setIccOption(self, option):
|
||||
if self.model():
|
||||
self.model().iccOption = self.iccOptionButtonGroup.checkedId()
|
||||
self.model().iccOption = option
|
||||
|
||||
def showStatusMessage(self, message: str):
|
||||
self.statusLabel.setText(message)
|
||||
@ -131,6 +129,10 @@ class OcrQueue(Ui_OcrQueue, QWidget):
|
||||
def modelReseted(self):
|
||||
self.progressBar.setMaximum(0)
|
||||
|
||||
@Slot()
|
||||
def on_optionsDialogButton_clicked(self):
|
||||
self.optionsDialog.exec()
|
||||
|
||||
@Slot()
|
||||
def on_ocr_removeSelectedButton_clicked(self):
|
||||
if self.model():
|
||||
|
49
ui/implements/components/ocrQueueOptionsDialog.py
Normal file
49
ui/implements/components/ocrQueueOptionsDialog.py
Normal file
@ -0,0 +1,49 @@
|
||||
from PySide6.QtCore import Signal
|
||||
from PySide6.QtWidgets import QButtonGroup, QDialog
|
||||
|
||||
from core.settings import SettingsKeys, SettingsValues, settings
|
||||
from ui.designer.components.ocrQueueOptionsDialog_ui import Ui_OcrQueueOptionsDialog
|
||||
|
||||
|
||||
class OcrQueueOptionsDialog(QDialog, Ui_OcrQueueOptionsDialog):
|
||||
iccOptionsChanged = Signal(int)
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super(OcrQueueOptionsDialog, self).__init__(parent)
|
||||
self.setupUi(self)
|
||||
|
||||
self.iccOptionButtonGroup = QButtonGroup(self)
|
||||
self.iccOptionButtonGroup.buttonToggled.connect(
|
||||
lambda: self.iccOptionsChanged.emit(self.iccOptionButtonGroup.checkedId())
|
||||
)
|
||||
self.iccOptionButtonGroup.addButton(self.iccUseQtRadioButton, 0)
|
||||
self.iccOptionButtonGroup.addButton(self.iccUsePILRadioButton, 1)
|
||||
self.iccOptionButtonGroup.addButton(self.iccTryFixRadioButton, 2)
|
||||
|
||||
self.scoreDateSourceButtonGroup = QButtonGroup(self)
|
||||
self.scoreDateSourceButtonGroup.addButton(
|
||||
self.dateUseCreationDateRadioButton, 0
|
||||
)
|
||||
self.scoreDateSourceButtonGroup.addButton(self.dateUseModifyDateRadioButton, 1)
|
||||
self.scoreDateSourceButtonGroup.buttonClicked.connect(
|
||||
self.on_scoreDateSourceButtonGroup_buttonClicked
|
||||
)
|
||||
|
||||
settings.updated.connect(self.syncCheckboxesFromSettings)
|
||||
self.syncCheckboxesFromSettings()
|
||||
|
||||
def syncCheckboxesFromSettings(self):
|
||||
scoreDateSource = settings.stringValue(SettingsKeys.Ocr.DateSource)
|
||||
if scoreDateSource == SettingsValues.Ocr.DateSource.FileLastModified:
|
||||
self.dateUseModifyDateRadioButton.setChecked(True)
|
||||
else:
|
||||
self.dateUseCreationDateRadioButton.setChecked(True)
|
||||
|
||||
def on_scoreDateSourceButtonGroup_buttonClicked(self, button):
|
||||
buttonId = self.scoreDateSourceButtonGroup.id(button)
|
||||
if buttonId == 1:
|
||||
value = SettingsValues.Ocr.DateSource.FileLastModified
|
||||
else:
|
||||
value = SettingsValues.Ocr.DateSource.FileCreated
|
||||
|
||||
settings.setValue(SettingsKeys.Ocr.DateSource, value)
|
@ -2,7 +2,7 @@ from PySide6.QtCore import Slot
|
||||
from PySide6.QtGui import QColor
|
||||
from PySide6.QtWidgets import QGraphicsColorizeEffect, QRadioButton
|
||||
|
||||
from ui.extends.shared.color import mix_color
|
||||
from core.color import mixColor
|
||||
|
||||
STYLESHEET = """
|
||||
QRadioButton {{
|
||||
@ -40,7 +40,7 @@ class RatingClassRadioButton(QRadioButton):
|
||||
def setColors(self, dark_color: QColor, text_color: QColor):
|
||||
self._dark_color = dark_color
|
||||
self._text_color = text_color
|
||||
self._mid_color = mix_color(dark_color, text_color, 0.616)
|
||||
self._mid_color = mixColor(dark_color, text_color, 0.616)
|
||||
self.updateEffects()
|
||||
|
||||
def isColorsSet(self) -> bool:
|
||||
|
@ -1,3 +1,4 @@
|
||||
import logging
|
||||
from typing import Type
|
||||
|
||||
from PySide6.QtCore import Signal
|
||||
@ -6,6 +7,8 @@ from PySide6.QtWidgets import QHBoxLayout, QSizePolicy, QVBoxLayout, QWidget
|
||||
|
||||
from ui.implements.components.ratingClassRadioButton import RatingClassRadioButton
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class RatingClassSelector(QWidget):
|
||||
valueChanged = Signal()
|
||||
@ -41,16 +44,30 @@ class RatingClassSelector(QWidget):
|
||||
self.bydButton.setAutoExclusive(False)
|
||||
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.prsButton.setColors(QColor("#809955"), QColor("#f7f9f4"))
|
||||
self.ftrButton.setColors(QColor("#702d60"), QColor("#f7ebf4"))
|
||||
self.bydButton.setColors(QColor("#710f25"), QColor("#f9ced8"))
|
||||
self.etrButton.setColors(QColor("#4f2c7a"), QColor("#e4daf1"))
|
||||
|
||||
self.pstButton.clicked.connect(self.select)
|
||||
self.prsButton.clicked.connect(self.select)
|
||||
self.ftrButton.clicked.connect(self.select)
|
||||
self.bydButton.clicked.connect(self.select)
|
||||
self.etrButton.clicked.connect(self.select)
|
||||
self.reset()
|
||||
self.setButtonsEnabled([])
|
||||
|
||||
@ -106,9 +123,10 @@ class RatingClassSelector(QWidget):
|
||||
|
||||
if ratingClass is None or isinstance(ratingClass, bool):
|
||||
button = self.sender()
|
||||
elif ratingClass in range(4):
|
||||
elif ratingClass in range(len(self.buttons)):
|
||||
button = self.buttons[ratingClass]
|
||||
else:
|
||||
logger.debug("Cannot select ratingClass=%s, condition check failed", ratingClass)
|
||||
return
|
||||
|
||||
if not button.isEnabled():
|
||||
|
@ -61,30 +61,22 @@ class ScoreEditor(Ui_ScoreEditor, QWidget):
|
||||
|
||||
VALIDATION_ITEMS_TEXT = [
|
||||
[
|
||||
# fmt: off
|
||||
lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.chartIncomplete.title"),
|
||||
lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.chartIncomplete.text"),
|
||||
# fmt: on
|
||||
],
|
||||
[
|
||||
# fmt: off
|
||||
lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.scoreMismatch.title"),
|
||||
lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.scoreMismatch.text"),
|
||||
# fmt: on
|
||||
],
|
||||
[
|
||||
# fmt: off
|
||||
lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.emptyScore.title"),
|
||||
lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.emptyScore.text"),
|
||||
# fmt: on
|
||||
],
|
||||
[
|
||||
# fmt: off
|
||||
lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.scoreIncompleteForValidate.title"),
|
||||
lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.scoreIncompleteForValidate.text"),
|
||||
# fmt: on,
|
||||
],
|
||||
]
|
||||
] # fmt: skip
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
@ -208,20 +200,16 @@ class ScoreEditor(Ui_ScoreEditor, QWidget):
|
||||
if validate & ScoreValidateResult.ChartNotSet:
|
||||
self.__triggerMessageBox(
|
||||
"critical",
|
||||
# fmt: off
|
||||
QCoreApplication.translate("ScoreEditor", "confirmDialog.chartNotSet.title"),
|
||||
QCoreApplication.translate("ScoreEditor", "confirmDialog.chartNotSet.text"),
|
||||
# fmt: on
|
||||
)
|
||||
) # fmt: skip
|
||||
return False
|
||||
if validate & ScoreValidateResult.ScoreIncomplete:
|
||||
self.__triggerMessageBox(
|
||||
"critical",
|
||||
# fmt: off
|
||||
QCoreApplication.translate("ScoreEditor", "confirmDialog.scoreIncomplete.title"),
|
||||
QCoreApplication.translate("ScoreEditor", "confirmDialog.scoreIncomplete.text"),
|
||||
# fmt: on
|
||||
)
|
||||
) # fmt: skip
|
||||
return False
|
||||
|
||||
# since validate may have multiple results
|
||||
@ -347,10 +335,8 @@ class ScoreEditor(Ui_ScoreEditor, QWidget):
|
||||
)
|
||||
if validate & ScoreValidateResult.ScoreIncompleteForValidate:
|
||||
texts.append(
|
||||
# fmt: off
|
||||
QCoreApplication.translate("ScoreEditor", "validate.scoreIncompleteForValidate")
|
||||
# fmt: on
|
||||
)
|
||||
) # fmt: skip
|
||||
|
||||
if not texts:
|
||||
texts.append(
|
||||
|
@ -192,7 +192,7 @@ class SongIdSelector(Ui_SongIdSelector, QWidget):
|
||||
self.fillSongIdComboBox()
|
||||
return True
|
||||
else:
|
||||
logger.warning(f'Attempting to select an unknown pack "{packId}"')
|
||||
logger.warning("Attempting to select an unknown pack [%s]", packId)
|
||||
return False
|
||||
|
||||
def selectSongId(self, songId: str) -> bool:
|
||||
@ -202,7 +202,8 @@ class SongIdSelector(Ui_SongIdSelector, QWidget):
|
||||
return True
|
||||
else:
|
||||
logger.warning(
|
||||
f'Attempting to select an unknown song "{songId}", maybe try selecting a pack first?'
|
||||
"Attempting to select an unknown song [%s], maybe try selecting a pack first?",
|
||||
songId,
|
||||
)
|
||||
return False
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
from PySide6.QtCore import QCoreApplication
|
||||
from PySide6.QtWidgets import QLabel, QPushButton
|
||||
|
||||
from core.settings import SettingsKeys, settings
|
||||
from ui.implements.components.fileSelector import FileSelector
|
||||
from ui.implements.settings.settingsBaseWidget import SettingsBaseWidget
|
||||
|
||||
@ -14,8 +15,8 @@ class SettingsAndreal(SettingsBaseWidget):
|
||||
self.andrealFolderValueWidget.setMode(
|
||||
self.andrealFolderValueWidget.getExistingDirectory
|
||||
)
|
||||
if self.settings.andrealFolder():
|
||||
self.andrealFolderValueWidget.selectFile(self.settings.andrealFolder())
|
||||
if andrealFolder := settings.stringValue(SettingsKeys.Andreal.Folder):
|
||||
self.andrealFolderValueWidget.selectFile(andrealFolder)
|
||||
self.andrealFolderValueWidget.filesSelected.connect(self.setAndrealFolder)
|
||||
self.andrealFolderResetButton.clicked.connect(self.resetAndrealFolder)
|
||||
self.insertItem(
|
||||
@ -25,10 +26,8 @@ class SettingsAndreal(SettingsBaseWidget):
|
||||
self.andrealFolderResetButton,
|
||||
)
|
||||
|
||||
if self.settings.andrealExecutable():
|
||||
self.andrealExecutableValueWidget.selectFile(
|
||||
self.settings.andrealExecutable()
|
||||
)
|
||||
if andrealExecutable := settings.stringValue(SettingsKeys.Andreal.Executable):
|
||||
self.andrealExecutableValueWidget.selectFile(andrealExecutable)
|
||||
self.andrealExecutableValueWidget.filesSelected.connect(
|
||||
self.setAndrealExecutable
|
||||
)
|
||||
@ -44,21 +43,21 @@ class SettingsAndreal(SettingsBaseWidget):
|
||||
selectedFile = self.andrealFolderValueWidget.selectedFiles()
|
||||
if selectedFile and selectedFile[0]:
|
||||
file = selectedFile[0]
|
||||
self.settings.setAndrealFolder(file)
|
||||
settings.setValue(SettingsKeys.Andreal.Folder, file)
|
||||
|
||||
def resetAndrealFolder(self):
|
||||
self.andrealFolderValueWidget.reset()
|
||||
self.settings.resetAndrealFolder()
|
||||
settings.setValue(SettingsKeys.Andreal.Folder, None)
|
||||
|
||||
def setAndrealExecutable(self):
|
||||
selectedFile = self.andrealExecutableValueWidget.selectedFiles()
|
||||
if selectedFile and selectedFile[0]:
|
||||
file = selectedFile[0]
|
||||
self.settings.setAndrealExecutable(file)
|
||||
settings.setValue(SettingsKeys.Andreal.Executable, file)
|
||||
|
||||
def resetAndrealExecutable(self):
|
||||
self.andrealExecutableValueWidget.reset()
|
||||
self.settings.resetAndrealExecutable()
|
||||
settings.setValue(SettingsKeys.Andreal.Executable, None)
|
||||
|
||||
def setupUi(self, *args):
|
||||
self.andrealFolderLabel = QLabel(self)
|
||||
|
@ -1,15 +1,15 @@
|
||||
from PySide6.QtCore import Qt
|
||||
from PySide6.QtWidgets import QLabel, QPushButton, QWidget
|
||||
|
||||
from core.settings import settings
|
||||
from ui.designer.settings.settingsBaseWidget_ui import Ui_SettingsBaseWidget
|
||||
from ui.extends.shared.language import LanguageChangeEventFilter
|
||||
from ui.extends.shared.settings import Settings
|
||||
|
||||
|
||||
class SettingsBaseWidget(Ui_SettingsBaseWidget, QWidget):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.settings = Settings()
|
||||
self.settings = settings
|
||||
|
||||
self.languageChangeEventFilter = LanguageChangeEventFilter(self)
|
||||
self.installEventFilter(self.languageChangeEventFilter)
|
||||
|
@ -1,6 +1,4 @@
|
||||
import sys
|
||||
|
||||
from PySide6.QtCore import QCoreApplication, QDir, QLocale, QProcess
|
||||
from PySide6.QtCore import QCoreApplication, QDir, QLocale
|
||||
from PySide6.QtWidgets import (
|
||||
QApplication,
|
||||
QCheckBox,
|
||||
@ -10,8 +8,8 @@ from PySide6.QtWidgets import (
|
||||
QPushButton,
|
||||
)
|
||||
|
||||
from core.settings import SettingsKeys, settings
|
||||
from ui.extends.shared.language import changeAppLanguage, localeToCode, localeToFullName
|
||||
from ui.extends.shared.settings import DATABASE_URL, LANGUAGE
|
||||
from ui.implements.settings.settingsBaseWidget import SettingsBaseWidget
|
||||
|
||||
|
||||
@ -33,8 +31,8 @@ class SettingsGeneral(SettingsBaseWidget):
|
||||
self.languageFollowSystemCheckBox.toggled.connect(
|
||||
self.changeLanguageFollowSystem
|
||||
)
|
||||
if self.settings.language():
|
||||
locale = QLocale(self.settings.language())
|
||||
if language := settings.stringValue(SettingsKeys.General.Language):
|
||||
locale = QLocale(language)
|
||||
index = self.languageValueWidget.findData(locale)
|
||||
if index > -1:
|
||||
self.languageValueWidget.setCurrentIndex(index)
|
||||
@ -51,7 +49,7 @@ class SettingsGeneral(SettingsBaseWidget):
|
||||
self.insertItem(
|
||||
"dbUrl",
|
||||
self.dbUrlLabel,
|
||||
QLabel(self.settings.databaseUrl()),
|
||||
QLabel(settings.stringValue(SettingsKeys.General.DatabaseUrl)),
|
||||
self.dbUrlResetButton,
|
||||
)
|
||||
|
||||
@ -59,13 +57,13 @@ class SettingsGeneral(SettingsBaseWidget):
|
||||
locale = self.languageValueWidget.currentData()
|
||||
if locale:
|
||||
changeAppLanguage(locale)
|
||||
self.settings.setLanguage(localeToCode(locale))
|
||||
settings.setValue(SettingsKeys.General.Language, localeToCode(locale))
|
||||
|
||||
def changeLanguageFollowSystem(self):
|
||||
followSystem = self.languageFollowSystemCheckBox.isChecked()
|
||||
self.languageValueWidget.setCurrentIndex(-1)
|
||||
if followSystem:
|
||||
self.settings.remove(LANGUAGE)
|
||||
settings.remove(SettingsKeys.General.Language)
|
||||
changeAppLanguage(QLocale.system())
|
||||
self.languageValueWidget.setEnabled(False)
|
||||
else:
|
||||
@ -80,7 +78,7 @@ class SettingsGeneral(SettingsBaseWidget):
|
||||
QMessageBox.StandardButton.No,
|
||||
)
|
||||
if userConfirm == QMessageBox.StandardButton.Yes:
|
||||
self.settings.remove(DATABASE_URL)
|
||||
settings.remove(SettingsKeys.General.DatabaseUrl)
|
||||
QApplication.instance().quit()
|
||||
|
||||
def setupUi(self, *args):
|
||||
@ -99,10 +97,10 @@ class SettingsGeneral(SettingsBaseWidget):
|
||||
|
||||
# fmt: off
|
||||
self.setTitle(QCoreApplication.translate("Settings", "general.title"))
|
||||
|
||||
|
||||
self.languageLabel.setText(QCoreApplication.translate("Settings", "general.language.label"))
|
||||
self.languageFollowSystemCheckBox.setText(QCoreApplication.translate("Settings", "general.language.followSystem"))
|
||||
|
||||
|
||||
self.dbUrlLabel.setText(QCoreApplication.translate("Settings", "general.dbUrl.label"))
|
||||
self.dbUrlResetButton.setText(QCoreApplication.translate("Settings", "resetButton"))
|
||||
# fmt: on
|
||||
|
@ -1,6 +1,7 @@
|
||||
from PySide6.QtCore import QCoreApplication
|
||||
from PySide6.QtWidgets import QLabel, QPushButton
|
||||
|
||||
from core.settings import SettingsKeys, settings
|
||||
from ui.implements.components.fileSelector import FileSelector
|
||||
from ui.implements.settings.settingsBaseWidget import SettingsBaseWidget
|
||||
|
||||
@ -11,8 +12,8 @@ class SettingsOcr(SettingsBaseWidget):
|
||||
|
||||
self.setupUi(self)
|
||||
|
||||
if self.settings.knnModelFile():
|
||||
self.knnModelFileValueWidget.selectFile(self.settings.knnModelFile())
|
||||
if knnModelFile := settings.stringValue(SettingsKeys.Ocr.KnnModelFile):
|
||||
self.knnModelFileValueWidget.selectFile(knnModelFile)
|
||||
self.knnModelFileValueWidget.filesSelected.connect(self.setKnnModelFile)
|
||||
self.knnModelFileResetButton.clicked.connect(self.resetKnnModelFile)
|
||||
self.insertItem(
|
||||
@ -22,8 +23,8 @@ class SettingsOcr(SettingsBaseWidget):
|
||||
self.knnModelFileResetButton,
|
||||
)
|
||||
|
||||
if self.settings.b30KnnModelFile():
|
||||
self.b30KnnModelFileValueWidget.selectFile(self.settings.b30KnnModelFile())
|
||||
if b30KnnModelFile := settings.stringValue(SettingsKeys.Ocr.B30KnnModelFile):
|
||||
self.b30KnnModelFileValueWidget.selectFile(b30KnnModelFile)
|
||||
self.b30KnnModelFileValueWidget.filesSelected.connect(self.setB30KnnModelFile)
|
||||
self.b30KnnModelFileResetButton.clicked.connect(self.resetB30KnnModelFile)
|
||||
self.insertItem(
|
||||
@ -33,10 +34,10 @@ class SettingsOcr(SettingsBaseWidget):
|
||||
self.b30KnnModelFileResetButton,
|
||||
)
|
||||
|
||||
if self.settings.phashDatabaseFile():
|
||||
self.phashDatabaseFileValueWidget.selectFile(
|
||||
self.settings.phashDatabaseFile()
|
||||
)
|
||||
if phashDatabaseFile := settings.stringValue(
|
||||
SettingsKeys.Ocr.PhashDatabaseFile
|
||||
):
|
||||
self.phashDatabaseFileValueWidget.selectFile(phashDatabaseFile)
|
||||
self.phashDatabaseFileValueWidget.filesSelected.connect(
|
||||
self.setPHashDatabaseFile
|
||||
)
|
||||
@ -52,31 +53,31 @@ class SettingsOcr(SettingsBaseWidget):
|
||||
selectedFile = self.knnModelFileValueWidget.selectedFiles()
|
||||
if selectedFile and selectedFile[0]:
|
||||
file = selectedFile[0]
|
||||
self.settings.setKnnModelFile(file)
|
||||
settings.setValue(SettingsKeys.Ocr.KnnModelFile, file)
|
||||
|
||||
def resetKnnModelFile(self):
|
||||
self.knnModelFileValueWidget.reset()
|
||||
self.settings.resetKnnModelFile()
|
||||
settings.setValue(SettingsKeys.Ocr.KnnModelFile, None)
|
||||
|
||||
def setB30KnnModelFile(self):
|
||||
selectedFile = self.b30KnnModelFileValueWidget.selectedFiles()
|
||||
if selectedFile and selectedFile[0]:
|
||||
file = selectedFile[0]
|
||||
self.settings.setB30KnnModelFile(file)
|
||||
settings.setValue(SettingsKeys.Ocr.B30KnnModelFile, file)
|
||||
|
||||
def resetB30KnnModelFile(self):
|
||||
self.b30KnnModelFileValueWidget.reset()
|
||||
self.settings.resetB30KnnModelFile()
|
||||
settings.setValue(SettingsKeys.Ocr.B30KnnModelFile, None)
|
||||
|
||||
def setPHashDatabaseFile(self):
|
||||
selectedFile = self.phashDatabaseFileValueWidget.selectedFiles()
|
||||
if selectedFile and selectedFile[0]:
|
||||
file = selectedFile[0]
|
||||
self.settings.setPHashDatabaseFile(file)
|
||||
settings.setValue(SettingsKeys.Ocr.PhashDatabaseFile, file)
|
||||
|
||||
def resetPHashDatabaseFile(self):
|
||||
self.phashDatabaseFileValueWidget.reset()
|
||||
self.settings.resetPHashDatabaseFile()
|
||||
settings.setValue(SettingsKeys.Ocr.PhashDatabaseFile, None)
|
||||
|
||||
def setupUi(self, *args):
|
||||
self.knnModelFileLabel = QLabel(self)
|
||||
|
@ -163,19 +163,15 @@ class TabDb_ChartInfoEditor(Ui_TabDb_ChartInfoEditor, QWidget):
|
||||
QMessageBox.critical(
|
||||
self,
|
||||
None,
|
||||
# fmt: off
|
||||
QCoreApplication.translate("TabDb_ChartInfoEditor", "commit.chartNotSelected"),
|
||||
# fmt: on
|
||||
)
|
||||
) # fmt: skip
|
||||
return
|
||||
if not self.constantLineEdit.hasAcceptableInput():
|
||||
QMessageBox.critical(
|
||||
self,
|
||||
None,
|
||||
# fmt: off
|
||||
QCoreApplication.translate("TabDb_ChartInfoEditor", "commit.constantRequired"),
|
||||
# fmt: on
|
||||
)
|
||||
) # fmt: skip
|
||||
return
|
||||
|
||||
constant = int(self.constantLineEdit.text())
|
||||
@ -202,10 +198,8 @@ class TabDb_ChartInfoEditor(Ui_TabDb_ChartInfoEditor, QWidget):
|
||||
QMessageBox.critical(
|
||||
self,
|
||||
None,
|
||||
# fmt: off
|
||||
QCoreApplication.translate("TabDb_ChartInfoEditor", "commit.chartNotSelected"),
|
||||
# fmt: on
|
||||
)
|
||||
) # fmt: skip
|
||||
return
|
||||
|
||||
chartInfo = self.db.get_chart_info(chart.song_id, chart.rating_class)
|
||||
@ -213,12 +207,10 @@ class TabDb_ChartInfoEditor(Ui_TabDb_ChartInfoEditor, QWidget):
|
||||
result = QMessageBox.warning(
|
||||
self,
|
||||
None,
|
||||
# fmt: off
|
||||
QCoreApplication.translate("TabDb_ChartInfoEditor", "deleteConfirm"),
|
||||
# fmt: on
|
||||
QMessageBox.StandardButton.Yes,
|
||||
QMessageBox.StandardButton.No,
|
||||
)
|
||||
) # fmt: skip
|
||||
if result == QMessageBox.StandardButton.Yes:
|
||||
with self.db.sessionmaker() as session:
|
||||
session.delete(chartInfo)
|
||||
|
@ -14,9 +14,10 @@ from arcaea_offline.external.arcaea import (
|
||||
)
|
||||
from arcaea_offline.external.arcaea.common import ArcaeaParser
|
||||
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.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 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))
|
||||
)
|
||||
|
||||
@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(
|
||||
self, parser: ArcaeaParser, instance, logName, path
|
||||
) -> int:
|
||||
@ -67,7 +91,7 @@ class TabDb_Manage(Ui_TabDb_Manage, QWidget):
|
||||
session.commit()
|
||||
databaseUpdateSignals.songAddOrDelete.emit()
|
||||
itemNum = len([item for item in parser.parse() if isinstance(item, instance)])
|
||||
logger.info(f"updated {itemNum} {logName} from {path}")
|
||||
logger.info("updated %d %s from %s", itemNum, logName, path)
|
||||
return itemNum
|
||||
|
||||
def importPacklist(self, packlistPath):
|
||||
@ -137,7 +161,7 @@ class TabDb_Manage(Ui_TabDb_Manage, QWidget):
|
||||
return
|
||||
|
||||
try:
|
||||
logger.info(f"Importing {apkFile}")
|
||||
logger.info("Importing %s", apkFile)
|
||||
|
||||
with zipfile.ZipFile(apkFile) as zf:
|
||||
packlistPath = zipfile.Path(zf) / "assets" / "songs" / "packlist"
|
||||
@ -169,7 +193,9 @@ class TabDb_Manage(Ui_TabDb_Manage, QWidget):
|
||||
db = Database()
|
||||
parser = St3ScoreParser(dbFile)
|
||||
logger.info(
|
||||
f"Got {len(parser.parse())} items from {dbFile}, writing into database..."
|
||||
"Got %d items from %s, writing into database...",
|
||||
len(parser.parse()),
|
||||
dbFile,
|
||||
)
|
||||
with db.sessionmaker() as session:
|
||||
parser.write_database(session)
|
||||
@ -194,7 +220,9 @@ class TabDb_Manage(Ui_TabDb_Manage, QWidget):
|
||||
db = Database()
|
||||
parser = ArcaeaOnlineParser(apiResultFile)
|
||||
logger.info(
|
||||
f"Got {len(parser.parse())} items from {apiResultFile}, writing into database..."
|
||||
"Got %d items from %s, writing into database...",
|
||||
len(parser.parse()),
|
||||
apiResultFile,
|
||||
)
|
||||
with db.sessionmaker() as session:
|
||||
parser.write_database(session)
|
||||
@ -208,14 +236,14 @@ class TabDb_Manage(Ui_TabDb_Manage, QWidget):
|
||||
|
||||
@Slot()
|
||||
def on_exportScoresButton_clicked(self):
|
||||
scores = Database().export_scores()
|
||||
version = Database().version()
|
||||
scores = Database().export_scores_def_v2()
|
||||
timestamp = QDateTime.currentMSecsSinceEpoch()
|
||||
content = json.dumps(scores, ensure_ascii=False)
|
||||
|
||||
exportLocation, _filter = QFileDialog.getSaveFileName(
|
||||
self,
|
||||
"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);;*",
|
||||
)
|
||||
with open(exportLocation, "w", encoding="utf-8") as f:
|
||||
|
@ -154,23 +154,17 @@ class TabDb_RemoveDuplicateScores(Ui_TabDb_RemoveDuplicateScores, QWidget):
|
||||
self.treeView.setItemDelegateForColumn(1, self.treeViewProxyDelegate)
|
||||
|
||||
self.quickSelect_comboBox.addItem(
|
||||
# fmt: off
|
||||
QCoreApplication.translate("TabDb_RemoveDuplicateScores", "quickSelectComboBox.idEarlier"),
|
||||
# fmt: on
|
||||
QuickSelectComboBoxValues.ID_EARLIER
|
||||
)
|
||||
) # fmt: skip
|
||||
self.quickSelect_comboBox.addItem(
|
||||
# fmt: off
|
||||
QCoreApplication.translate("TabDb_RemoveDuplicateScores", "quickSelectComboBox.dateEarlier"),
|
||||
# fmt: on
|
||||
QuickSelectComboBoxValues.DATE_EARLIER
|
||||
)
|
||||
) # fmt: skip
|
||||
self.quickSelect_comboBox.addItem(
|
||||
# fmt: off
|
||||
QCoreApplication.translate("TabDb_RemoveDuplicateScores", "quickSelectComboBox.columnsIntegral"),
|
||||
# fmt: on
|
||||
QuickSelectComboBoxValues.COLUMNS_INTEGRAL
|
||||
)
|
||||
) # fmt: skip
|
||||
|
||||
def getQueryColumns(self):
|
||||
columns: list[InstrumentedAttribute] = [Score.song_id, Score.rating_class]
|
||||
@ -291,12 +285,12 @@ class TabDb_RemoveDuplicateScores(Ui_TabDb_RemoveDuplicateScores, QWidget):
|
||||
confirm = QMessageBox.warning(
|
||||
self,
|
||||
None,
|
||||
# fmt: off
|
||||
QCoreApplication.translate("TabDb_RemoveDuplicateScores", "deleteSelectionDialog.content {}").format(len(selectedScores)),
|
||||
# fmt: on
|
||||
QCoreApplication.translate(
|
||||
"TabDb_RemoveDuplicateScores", "deleteSelectionDialog.content {}"
|
||||
).format(len(selectedScores)),
|
||||
QMessageBox.StandardButton.Yes,
|
||||
QMessageBox.StandardButton.No,
|
||||
)
|
||||
) # fmt: skip
|
||||
if confirm != QMessageBox.StandardButton.Yes:
|
||||
return
|
||||
|
||||
@ -310,12 +304,11 @@ class TabDb_RemoveDuplicateScores(Ui_TabDb_RemoveDuplicateScores, QWidget):
|
||||
@Slot()
|
||||
def on_scan_scanButton_clicked(self):
|
||||
if len(self.getQueryColumns()) <= 2:
|
||||
message = QCoreApplication.translate("TabDb_RemoveDuplicateScores", "scan_noColumnsDialog.content") # fmt: skip
|
||||
result = QMessageBox.warning(
|
||||
self,
|
||||
None,
|
||||
# fmt: off
|
||||
QCoreApplication.translate("TabDb_RemoveDuplicateScores", "scan_noColumnsDialog.content"),
|
||||
# fmt: on
|
||||
message,
|
||||
QMessageBox.StandardButton.Yes,
|
||||
QMessageBox.StandardButton.No,
|
||||
)
|
||||
|
@ -9,6 +9,7 @@ from PIL import Image
|
||||
from PySide6.QtCore import Signal, Slot
|
||||
from PySide6.QtWidgets import QFileDialog, QMessageBox, QWidget
|
||||
|
||||
from core.settings import SettingsKeys
|
||||
from ui.designer.tabs.tabOcr.tabOcr_B30_ui import Ui_TabOcr_B30
|
||||
from ui.extends.components.ocrQueue import OcrQueueModel
|
||||
from ui.extends.ocr.dependencies import (
|
||||
@ -16,11 +17,6 @@ from ui.extends.ocr.dependencies import (
|
||||
getPhashDatabaseStatusText,
|
||||
)
|
||||
from ui.extends.shared.language import LanguageChangeEventFilter
|
||||
from ui.extends.shared.settings import (
|
||||
B30_KNN_MODEL_FILE,
|
||||
KNN_MODEL_FILE,
|
||||
PHASH_DATABASE_FILE,
|
||||
)
|
||||
from ui.extends.tabs.tabOcr.tabOcr_B30 import ChieriV4OcrRunnable, b30ResultToScore
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@ -55,9 +51,15 @@ class TabOcr_B30(Ui_TabOcr_B30, QWidget):
|
||||
self.ocr = None
|
||||
|
||||
logger.info("Applying settings...")
|
||||
self.dependencies_knnModelSelector.connectSettings(KNN_MODEL_FILE)
|
||||
self.dependencies_b30KnnModelSelector.connectSettings(B30_KNN_MODEL_FILE)
|
||||
self.dependencies_phashDatabaseSelector.connectSettings(PHASH_DATABASE_FILE)
|
||||
self.dependencies_knnModelSelector.connectSettings(
|
||||
SettingsKeys.Ocr.KnnModelFile
|
||||
)
|
||||
self.dependencies_b30KnnModelSelector.connectSettings(
|
||||
SettingsKeys.Ocr.B30KnnModelFile
|
||||
)
|
||||
self.dependencies_phashDatabaseSelector.connectSettings(
|
||||
SettingsKeys.Ocr.PhashDatabaseFile
|
||||
)
|
||||
|
||||
self.ocrQueueModel = OcrQueueModel(self)
|
||||
self.ocrQueue.setModel(self.ocrQueueModel)
|
||||
|
@ -11,10 +11,10 @@ from arcaea_offline_ocr.phash_db import ImagePhashDatabase
|
||||
from PySide6.QtCore import Slot
|
||||
from PySide6.QtWidgets import QApplication, QFileDialog, QMessageBox, QWidget
|
||||
|
||||
from core.settings import SettingsKeys
|
||||
from ui.designer.tabs.tabOcr.tabOcr_Device_ui import Ui_TabOcr_Device
|
||||
from ui.extends.components.ocrQueue import OcrQueueModel
|
||||
from ui.extends.shared.language import LanguageChangeEventFilter
|
||||
from ui.extends.shared.settings import KNN_MODEL_FILE, PHASH_DATABASE_FILE
|
||||
from ui.extends.tabs.tabOcr.tabOcr_Device import ScoreConverter, TabDeviceOcrRunnable
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@ -54,8 +54,12 @@ class TabOcr_Device(Ui_TabOcr_Device, QWidget):
|
||||
)
|
||||
|
||||
logger.info("Applying settings...")
|
||||
self.dependencies_knnModelSelector.connectSettings(KNN_MODEL_FILE)
|
||||
self.dependencies_phashDatabaseSelector.connectSettings(PHASH_DATABASE_FILE)
|
||||
self.dependencies_knnModelSelector.connectSettings(
|
||||
SettingsKeys.Ocr.KnnModelFile
|
||||
)
|
||||
self.dependencies_phashDatabaseSelector.connectSettings(
|
||||
SettingsKeys.Ocr.PhashDatabaseFile
|
||||
)
|
||||
|
||||
self.options_usePresetCheckBox.setChecked(True)
|
||||
self.options_usePresetCheckBox.setEnabled(False)
|
||||
|
@ -50,6 +50,4 @@ class TabOverview(Ui_TabOverview, QWidget):
|
||||
|
||||
def retranslateUi(self, *args):
|
||||
super().retranslateUi(self)
|
||||
# fmt: off
|
||||
self.describeFormatString = QCoreApplication.translate("TabOverview", "databaseDescribeLabel {} {} {} {} {} {}")
|
||||
# fmt: on
|
||||
self.describeFormatString = QCoreApplication.translate("TabOverview", "databaseDescribeLabel {} {} {} {} {} {}") # fmt: skip
|
||||
|
@ -11,9 +11,9 @@ from PySide6.QtCore import QCoreApplication, QDir, QFileInfo, Qt, Slot
|
||||
from PySide6.QtGui import QGuiApplication, QImage, QPainter, QPaintEvent, QPixmap
|
||||
from PySide6.QtWidgets import QButtonGroup, QFileDialog, QLabel, QMessageBox, QWidget
|
||||
|
||||
from core.settings import SettingsKeys
|
||||
from ui.designer.tabs.tabTools.tabTools_Andreal_ui import Ui_TabTools_Andreal
|
||||
from ui.extends.shared.language import LanguageChangeEventFilter
|
||||
from ui.extends.shared.settings import ANDREAL_EXECUTABLE, ANDREAL_FOLDER
|
||||
from ui.extends.tabs.tabTools.tabTools_Andreal import AndrealHelper
|
||||
from ui.implements.components.chartSelector import ChartSelector
|
||||
from ui.implements.components.songIdSelector import SongIdSelectorMode
|
||||
@ -80,8 +80,8 @@ class TabTools_Andreal(Ui_TabTools_Andreal, QWidget):
|
||||
self.andrealFolderSelector.filesSelected.connect(self.setHelperPaths)
|
||||
self.andrealExecutableSelector.filesSelected.connect(self.setHelperPaths)
|
||||
|
||||
self.andrealFolderSelector.connectSettings(ANDREAL_FOLDER)
|
||||
self.andrealExecutableSelector.connectSettings(ANDREAL_EXECUTABLE)
|
||||
self.andrealFolderSelector.connectSettings(SettingsKeys.Andreal.Folder)
|
||||
self.andrealExecutableSelector.connectSettings(SettingsKeys.Andreal.Executable)
|
||||
|
||||
self.generatePreviewButton.clicked.connect(self.requestPreview)
|
||||
self.generateImageButton.clicked.connect(self.requestGenerate)
|
||||
@ -131,13 +131,8 @@ class TabTools_Andreal(Ui_TabTools_Andreal, QWidget):
|
||||
|
||||
@Slot()
|
||||
def on_imageTypeWhatIsThisButton_clicked(self):
|
||||
QMessageBox.information(
|
||||
self,
|
||||
None,
|
||||
# fmt: off
|
||||
QCoreApplication.translate("TabTools_Andreal", "imageWhatIsThisDialog.description"),
|
||||
# fmt: on
|
||||
)
|
||||
message = QCoreApplication.translate("TabTools_Andreal", "imageWhatIsThisDialog.description") # fmt: skip
|
||||
QMessageBox.information(self, None, message)
|
||||
|
||||
def imageFormat(self):
|
||||
buttonId = self.imageFormatButtonGroup.checkedId()
|
||||
|
@ -90,10 +90,8 @@ class PlayRatingCalculatorDialog(QDialog):
|
||||
|
||||
self.acceptButton = QPushButton(self)
|
||||
self.acceptButton.setText(
|
||||
# fmt: off
|
||||
QCoreApplication.translate("StepCalculator", "playRatingCalculatorDialog.acceptButton")
|
||||
# fmt: on
|
||||
)
|
||||
) # fmt: skip
|
||||
self.acceptButton.setEnabled(False)
|
||||
self.verticalLayout.addWidget(self.acceptButton)
|
||||
|
||||
|
@ -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 |
@ -70,12 +70,12 @@
|
||||
<translation>Continue</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../startup/databaseChecker.py" line="117"/>
|
||||
<location filename="../../startup/databaseChecker.py" line="122"/>
|
||||
<source>dialog.tryInitExistingDatabase</source>
|
||||
<translation>The existing database doesn't seem to be initialized properly, try initialize again?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../startup/databaseChecker.py" line="133"/>
|
||||
<location filename="../../startup/databaseChecker.py" line="138"/>
|
||||
<source>dialog.confirmNewDatabase</source>
|
||||
<translation>Database file does not exist. Create now?</translation>
|
||||
</message>
|
||||
@ -224,85 +224,118 @@
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../designer/components/ocrQueue.ui" line="28"/>
|
||||
<source>iccOptionsGroupBox</source>
|
||||
<translation>ICC Profile Options</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../designer/components/ocrQueue.ui" line="34"/>
|
||||
<source>icc.useQt</source>
|
||||
<translation>Use Qt</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../designer/components/ocrQueue.ui" line="41"/>
|
||||
<source>icc.usePIL</source>
|
||||
<translation>Use PIL</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../designer/components/ocrQueue.ui" line="51"/>
|
||||
<source>icc.tryFix</source>
|
||||
<translation>Try fix</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../designer/components/ocrQueue.ui" line="61"/>
|
||||
<source>queue.addImageButton</source>
|
||||
<translation>Add Image</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../designer/components/ocrQueue.ui" line="71"/>
|
||||
<location filename="../../designer/components/ocrQueue.ui" line="38"/>
|
||||
<source>queue.removeSelected</source>
|
||||
<translation>Remove Selected</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../designer/components/ocrQueue.ui" line="81"/>
|
||||
<location filename="../../designer/components/ocrQueue.ui" line="48"/>
|
||||
<source>queue.removeAll</source>
|
||||
<translation>Remove All</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../designer/components/ocrQueue.ui" line="101"/>
|
||||
<location filename="../../designer/components/ocrQueue.ui" line="68"/>
|
||||
<source>queue.optionsButton</source>
|
||||
<translation>Options</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../designer/components/ocrQueue.ui" line="75"/>
|
||||
<source>queue.startOcrButton</source>
|
||||
<translation>Start OCR</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../designer/components/ocrQueue.ui" line="153"/>
|
||||
<location filename="../../designer/components/ocrQueue.ui" line="127"/>
|
||||
<source>results</source>
|
||||
<translation>Results</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../designer/components/ocrQueue.ui" line="162"/>
|
||||
<location filename="../../designer/components/ocrQueue.ui" line="136"/>
|
||||
<source>results.acceptSelectedButton</source>
|
||||
<translation>Accept Selected</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../designer/components/ocrQueue.ui" line="169"/>
|
||||
<location filename="../../designer/components/ocrQueue.ui" line="143"/>
|
||||
<source>results.acceptAllButton</source>
|
||||
<translation>Accept All</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../designer/components/ocrQueue.ui" line="189"/>
|
||||
<location filename="../../designer/components/ocrQueue.ui" line="163"/>
|
||||
<source>results.ignoreValidate</source>
|
||||
<translation>Ignore
|
||||
validation</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>OcrQueueOptionsDialog</name>
|
||||
<message>
|
||||
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="14"/>
|
||||
<source>OCR Options</source>
|
||||
<translation>OCR Options</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="22"/>
|
||||
<source>iccOptionsGroupBox</source>
|
||||
<translation>ICC Profile Options</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="28"/>
|
||||
<source>icc.useQt</source>
|
||||
<translation>Use Qt</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="35"/>
|
||||
<source>icc.usePIL</source>
|
||||
<translation>Use PIL</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="45"/>
|
||||
<source>icc.tryFix</source>
|
||||
<translation>Try fix</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="55"/>
|
||||
<source>dateOptionsGroupBox</source>
|
||||
<translation>Date Source</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="64"/>
|
||||
<source>date.readFromExif</source>
|
||||
<translation>Read from image EXIF</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="74"/>
|
||||
<source>date.useCreationDate</source>
|
||||
<translation>File creation time</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="84"/>
|
||||
<source>date.useModifyDate</source>
|
||||
<translation>File last modification time</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>OcrTableModel</name>
|
||||
<message>
|
||||
<location filename="../../extends/components/ocrQueue.py" line="347"/>
|
||||
<location filename="../../extends/components/ocrQueue.py" line="348"/>
|
||||
<source>horizontalHeader.title.select</source>
|
||||
<translation>Select</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../extends/components/ocrQueue.py" line="348"/>
|
||||
<location filename="../../extends/components/ocrQueue.py" line="351"/>
|
||||
<source>horizontalHeader.title.imagePreview</source>
|
||||
<translation>Image Preview</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../extends/components/ocrQueue.py" line="349"/>
|
||||
<location filename="../../extends/components/ocrQueue.py" line="352"/>
|
||||
<source>horizontalHeader.title.chart</source>
|
||||
<translation>Chart</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../extends/components/ocrQueue.py" line="350"/>
|
||||
<location filename="../../extends/components/ocrQueue.py" line="353"/>
|
||||
<source>horizontalHeader.title.score</source>
|
||||
<translation>Score</translation>
|
||||
</message>
|
||||
@ -672,92 +705,127 @@ validation</translation>
|
||||
<context>
|
||||
<name>TabDb_Manage</name>
|
||||
<message>
|
||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="23"/>
|
||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="78"/>
|
||||
<source>syncArcSongDbButton</source>
|
||||
<translation>Sync arcsong.db</translation>
|
||||
</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>
|
||||
<translation>Update chart info</translation>
|
||||
</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>
|
||||
<translation>Import from Game Save</translation>
|
||||
<translation>Game Save Database</translation>
|
||||
</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>
|
||||
<translation>Import scores from your game save</translation>
|
||||
<translation>Import scores from your game save database</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="58"/>
|
||||
<source>exportScoresButton</source>
|
||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="139"/>
|
||||
<source>exportScoreGroup</source>
|
||||
<translation>Export Scores</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="65"/>
|
||||
<source>exportScores.description</source>
|
||||
<translation>Export all your scores to a JSON file</translation>
|
||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="146"/>
|
||||
<source>exportScoresButton</source>
|
||||
<translation>D.E.F. V2</translation>
|
||||
</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>
|
||||
<translation>Import packlist</translation>
|
||||
</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>
|
||||
<translation>Import songlist</translation>
|
||||
</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>
|
||||
<translation>Import packlist file</translation>
|
||||
</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>
|
||||
<translation>Import songlist file</translation>
|
||||
</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>
|
||||
<translation>Export arcsong.json</translation>
|
||||
</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>
|
||||
<translation>Export arcsong.json file</translation>
|
||||
<translation>Export arcsong.json file based on the information in database</translation>
|
||||
</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>
|
||||
<translation>Import from APK</translation>
|
||||
</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>
|
||||
<translation>Import packlist and songlist from .apk file</translation>
|
||||
</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>
|
||||
<translation>Import from Arcaea Online</translation>
|
||||
<translation>Arcaea Online</translation>
|
||||
</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>
|
||||
<translation>Import scores from the result of Arcaea Online API</translation>
|
||||
<translation>Import scores from the Arcaea Online API result</translation>
|
||||
</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>
|
||||
<translation>Export Scores (CSV, SmartRTE B30)</translation>
|
||||
<translation>SmartRTE B30</translation>
|
||||
</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>
|
||||
<translation>Export all your scores to <a href="https://smartrte.github.io/b30gen.html">smartrte.github.io</a> compatible CSV file</translation>
|
||||
</message>
|
||||
@ -840,27 +908,27 @@ validation</translation>
|
||||
<translation>Delete Selected Scores</translation>
|
||||
</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>
|
||||
<translation>Earlier ID</translation>
|
||||
</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>
|
||||
<translation>Earlier date</translation>
|
||||
</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>
|
||||
<translation>More complete data</translation>
|
||||
</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>
|
||||
<translation>Deleting {} scores from database, this cannot be undone!<br>Confirm?</translation>
|
||||
</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>
|
||||
<translation>You haven't selected any column! Are you sure to continue?</translation>
|
||||
</message>
|
||||
|
@ -70,12 +70,12 @@
|
||||
<translation>继续</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../startup/databaseChecker.py" line="117"/>
|
||||
<location filename="../../startup/databaseChecker.py" line="122"/>
|
||||
<source>dialog.tryInitExistingDatabase</source>
|
||||
<translation>现有的数据库似乎没有正确初始化,是否尝试再次初始化?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../startup/databaseChecker.py" line="133"/>
|
||||
<location filename="../../startup/databaseChecker.py" line="138"/>
|
||||
<source>dialog.confirmNewDatabase</source>
|
||||
<translation>数据库文件不存在,是否创建?</translation>
|
||||
</message>
|
||||
@ -224,84 +224,117 @@
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../designer/components/ocrQueue.ui" line="28"/>
|
||||
<source>iccOptionsGroupBox</source>
|
||||
<translation>ICC 文件选项</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../designer/components/ocrQueue.ui" line="34"/>
|
||||
<source>icc.useQt</source>
|
||||
<translation>使用 Qt</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../designer/components/ocrQueue.ui" line="41"/>
|
||||
<source>icc.usePIL</source>
|
||||
<translation>使用 PIL</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../designer/components/ocrQueue.ui" line="51"/>
|
||||
<source>icc.tryFix</source>
|
||||
<translation>尝试修复</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../designer/components/ocrQueue.ui" line="61"/>
|
||||
<source>queue.addImageButton</source>
|
||||
<translation>添加图像文件</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../designer/components/ocrQueue.ui" line="71"/>
|
||||
<location filename="../../designer/components/ocrQueue.ui" line="38"/>
|
||||
<source>queue.removeSelected</source>
|
||||
<translation>移除选中</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../designer/components/ocrQueue.ui" line="81"/>
|
||||
<location filename="../../designer/components/ocrQueue.ui" line="48"/>
|
||||
<source>queue.removeAll</source>
|
||||
<translation>移除所有</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../designer/components/ocrQueue.ui" line="101"/>
|
||||
<location filename="../../designer/components/ocrQueue.ui" line="68"/>
|
||||
<source>queue.optionsButton</source>
|
||||
<translation>选项</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../designer/components/ocrQueue.ui" line="75"/>
|
||||
<source>queue.startOcrButton</source>
|
||||
<translation>开始 OCR</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../designer/components/ocrQueue.ui" line="153"/>
|
||||
<location filename="../../designer/components/ocrQueue.ui" line="127"/>
|
||||
<source>results</source>
|
||||
<translation>结果</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../designer/components/ocrQueue.ui" line="162"/>
|
||||
<location filename="../../designer/components/ocrQueue.ui" line="136"/>
|
||||
<source>results.acceptSelectedButton</source>
|
||||
<translation>提交选中</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../designer/components/ocrQueue.ui" line="169"/>
|
||||
<location filename="../../designer/components/ocrQueue.ui" line="143"/>
|
||||
<source>results.acceptAllButton</source>
|
||||
<translation>提交所有</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../designer/components/ocrQueue.ui" line="189"/>
|
||||
<location filename="../../designer/components/ocrQueue.ui" line="163"/>
|
||||
<source>results.ignoreValidate</source>
|
||||
<translation>忽略验证</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>OcrQueueOptionsDialog</name>
|
||||
<message>
|
||||
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="14"/>
|
||||
<source>OCR Options</source>
|
||||
<translation>OCR 选项</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="22"/>
|
||||
<source>iccOptionsGroupBox</source>
|
||||
<translation>ICC 文件选项</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="28"/>
|
||||
<source>icc.useQt</source>
|
||||
<translation>使用 Qt</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="35"/>
|
||||
<source>icc.usePIL</source>
|
||||
<translation>使用 PIL</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="45"/>
|
||||
<source>icc.tryFix</source>
|
||||
<translation>尝试修复</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="55"/>
|
||||
<source>dateOptionsGroupBox</source>
|
||||
<translation>日期来源</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="64"/>
|
||||
<source>date.readFromExif</source>
|
||||
<translation>从 EXIF 读取</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="74"/>
|
||||
<source>date.useCreationDate</source>
|
||||
<translation>文件创建时间</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="84"/>
|
||||
<source>date.useModifyDate</source>
|
||||
<translation>文件最后修改时间</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>OcrTableModel</name>
|
||||
<message>
|
||||
<location filename="../../extends/components/ocrQueue.py" line="347"/>
|
||||
<location filename="../../extends/components/ocrQueue.py" line="348"/>
|
||||
<source>horizontalHeader.title.select</source>
|
||||
<translation>选择</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../extends/components/ocrQueue.py" line="348"/>
|
||||
<location filename="../../extends/components/ocrQueue.py" line="351"/>
|
||||
<source>horizontalHeader.title.imagePreview</source>
|
||||
<translation>图像预览</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../extends/components/ocrQueue.py" line="349"/>
|
||||
<location filename="../../extends/components/ocrQueue.py" line="352"/>
|
||||
<source>horizontalHeader.title.chart</source>
|
||||
<translation>谱面</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../extends/components/ocrQueue.py" line="350"/>
|
||||
<location filename="../../extends/components/ocrQueue.py" line="353"/>
|
||||
<source>horizontalHeader.title.score</source>
|
||||
<translation>分数</translation>
|
||||
</message>
|
||||
@ -671,92 +704,127 @@
|
||||
<context>
|
||||
<name>TabDb_Manage</name>
|
||||
<message>
|
||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="23"/>
|
||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="78"/>
|
||||
<source>syncArcSongDbButton</source>
|
||||
<translation>同步 arcsong.db</translation>
|
||||
</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>
|
||||
<translation>更新谱面信息</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="37"/>
|
||||
<source>importSt3Button</source>
|
||||
<translation>导入本地存档</translation>
|
||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="98"/>
|
||||
<source>importScoreGroup</source>
|
||||
<translation>导入分数</translation>
|
||||
</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>
|
||||
<translation>遏卡抑五年,天降飞龙,姿容极度美艳。白衣仙女以妙法擒之,与之相恋。<br>来年,恶人携联结万邦之力来袭,仙女龙妃以根源之术,呼唤神通叁式之威,退敌千里,永护宝库安宁。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="58"/>
|
||||
<source>exportScoresButton</source>
|
||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="139"/>
|
||||
<source>exportScoreGroup</source>
|
||||
<translation>导出分数</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="65"/>
|
||||
<source>exportScores.description</source>
|
||||
<translation>将所有分数导出为 JSON 文件</translation>
|
||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="146"/>
|
||||
<source>exportScoresButton</source>
|
||||
<translation>数据交换格式 V2</translation>
|
||||
</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>
|
||||
<translation>导入 packlist</translation>
|
||||
</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>
|
||||
<translation>导入 songlist</translation>
|
||||
</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>
|
||||
<translation>导入 packlist 文件</translation>
|
||||
</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>
|
||||
<translation>导入 songlist 文件</translation>
|
||||
</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>
|
||||
<translation>导出 arcsong.json</translation>
|
||||
</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>
|
||||
<translation>导出 arcsong.json 文件</translation>
|
||||
<translation>基于数据库信息导出 arcsong.json 文件</translation>
|
||||
</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>
|
||||
<translation>从 APK 导入</translation>
|
||||
</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>
|
||||
<translation>从 .apk 文件导入 packlist 和 songlist</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="135"/>
|
||||
<source>importOnlineButton</source>
|
||||
<translation>从 Arcaea Online 导入</translation>
|
||||
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="71"/>
|
||||
<source>chartInfoGroup</source>
|
||||
<translation>谱面信息</translation>
|
||||
</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>
|
||||
<translation>从 Arcaea Online 的 API 结果导入分数</translation>
|
||||
</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>
|
||||
<translation>导出分数(CSV,SmartRTE B30)</translation>
|
||||
<translation>SmartRTE B30</translation>
|
||||
</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>
|
||||
<translation>将所有分数导出为兼容 <a href="https://smartrte.github.io/b30gen.html">smartrte.github.io</a> 的 CSV 文件</translation>
|
||||
</message>
|
||||
@ -839,27 +907,27 @@
|
||||
<translation>删除已选分数</translation>
|
||||
</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>
|
||||
<translation>ID 更早</translation>
|
||||
</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>
|
||||
<translation>时间更早</translation>
|
||||
</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>
|
||||
<translation>数据更完整</translation>
|
||||
</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>
|
||||
<translation>将从数据库中删除 {} 个分数。此操作无法撤销!<br>确认吗?</translation>
|
||||
</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>
|
||||
<translation>还未选择任何字段!确定继续吗?</translation>
|
||||
</message>
|
||||
|
@ -8,8 +8,6 @@
|
||||
|
||||
<file>fonts/GeosansLight.ttf</file>
|
||||
|
||||
<file>icons/back.svg</file>
|
||||
|
||||
<file>images/icon.png</file>
|
||||
<file>images/logo.png</file>
|
||||
<file>images/jacket-placeholder.png</file>
|
||||
|
@ -3,11 +3,11 @@ import traceback
|
||||
from enum import IntEnum
|
||||
|
||||
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 core.settings import SettingsKeys, settings
|
||||
from ui.extends.shared.database import create_engine
|
||||
from ui.extends.shared.settings import Settings
|
||||
|
||||
from .databaseChecker_ui import Ui_DatabaseChecker
|
||||
|
||||
@ -29,8 +29,7 @@ class DatabaseChecker(Ui_DatabaseChecker, QDialog):
|
||||
self.dbDirSelector.setMode(self.dbDirSelector.getExistingDirectory)
|
||||
|
||||
self.confirmDbByExistingSettings = False
|
||||
self.settings = Settings(self)
|
||||
if dbUrlString := self.settings.databaseUrl():
|
||||
if dbUrlString := settings.stringValue(SettingsKeys.General.DatabaseUrl):
|
||||
dbFileUrl = QUrl(dbUrlString.replace("sqlite://", "file://"))
|
||||
dbFileInfo = QFileInfo(dbFileUrl.toLocalFile())
|
||||
if dbFileInfo.exists():
|
||||
@ -45,6 +44,9 @@ class DatabaseChecker(Ui_DatabaseChecker, QDialog):
|
||||
self.dbDirSelector.selectFile(QDir.currentPath())
|
||||
self.dbFilenameLineEdit.setText("arcaea_offline.db")
|
||||
|
||||
def writeDatabaseUrlToSettings(self, databaseUrl: str):
|
||||
settings.setValue(SettingsKeys.General.DatabaseUrl, databaseUrl)
|
||||
|
||||
def dbPath(self):
|
||||
return QDir(self.dbDirSelector.selectedFiles()[0])
|
||||
|
||||
@ -59,8 +61,13 @@ class DatabaseChecker(Ui_DatabaseChecker, QDialog):
|
||||
return QUrl.fromLocalFile(self.dbFileInfo().filePath())
|
||||
|
||||
def dbSqliteUrl(self):
|
||||
# dbSqliteUrl.setScheme("sqlite")
|
||||
return QUrl(self.dbFileUrl().toString().replace("file://", "sqlite://"))
|
||||
kernelType = QSysInfo.kernelType()
|
||||
# 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:
|
||||
flags = 0x000
|
||||
@ -74,7 +81,7 @@ class DatabaseChecker(Ui_DatabaseChecker, QDialog):
|
||||
db = Database(create_engine(dbSqliteUrl))
|
||||
if db.check_init():
|
||||
flags |= DatabaseCheckerResult.Initted
|
||||
self.settings.setDatabaseUrl(self.dbSqliteUrl().toString())
|
||||
self.writeDatabaseUrlToSettings(self.dbSqliteUrl().toString())
|
||||
|
||||
return flags
|
||||
|
||||
@ -103,20 +110,18 @@ class DatabaseChecker(Ui_DatabaseChecker, QDialog):
|
||||
@Slot()
|
||||
def on_confirmDbPathButton_clicked(self):
|
||||
dbSqliteUrl = self.dbSqliteUrl()
|
||||
self.settings.setDatabaseUrl(dbSqliteUrl.toString())
|
||||
self.writeDatabaseUrlToSettings(dbSqliteUrl.toString())
|
||||
|
||||
result = self.confirmDb()
|
||||
if result & DatabaseCheckerResult.Initted:
|
||||
if not self.confirmDbByExistingSettings:
|
||||
self.settings.setDatabaseUrl(dbSqliteUrl.toString())
|
||||
self.writeDatabaseUrlToSettings(dbSqliteUrl.toString())
|
||||
elif result & DatabaseCheckerResult.FileExist:
|
||||
confirm_try_init = QMessageBox.question(
|
||||
self,
|
||||
None,
|
||||
# fmt: off
|
||||
QCoreApplication.translate("DatabaseChecker", "dialog.tryInitExistingDatabase"),
|
||||
# fmt: on
|
||||
)
|
||||
) # fmt: skip
|
||||
if confirm_try_init == QMessageBox.StandardButton.Yes:
|
||||
try:
|
||||
Database().init(checkfirst=True)
|
||||
@ -129,10 +134,8 @@ class DatabaseChecker(Ui_DatabaseChecker, QDialog):
|
||||
confirm_new_database = QMessageBox.question(
|
||||
self,
|
||||
None,
|
||||
# fmt: off
|
||||
QCoreApplication.translate("DatabaseChecker", "dialog.confirmNewDatabase"),
|
||||
# fmt: on
|
||||
)
|
||||
) # fmt: skip
|
||||
if confirm_new_database == QMessageBox.StandardButton.Yes:
|
||||
db = Database(create_engine(dbSqliteUrl))
|
||||
db.init()
|
||||
|
@ -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