45 Commits

Author SHA1 Message Date
29eb135752 log tag support 2024-06-28 22:24:57 +08:00
b2a10d02ce change # fmt: labels to ruff compatible 2024-06-28 20:20:26 +08:00
0c88302053 change logging format to % 2024-06-28 20:08:08 +08:00
fe3f610878 core.color 2024-06-28 20:05:10 +08:00
09063cc987 disable strict ruff rules 2024-06-20 22:30:39 +08:00
a9d7681ee7 refactor: moving ui.extends to core
* Settings and Singletons moved
2024-06-20 21:30:21 +08:00
da3ac7acb3 using ruff as formatter & linter 2024-06-20 21:26:39 +08:00
0c6f4f4961 chore: v0.3.9 2024-06-20 00:04:46 +08:00
10fb98d530 chore: upgrade dependencies 2024-06-20 00:03:19 +08:00
bf034d1397 ci: update build actions
* Switch to official Nuitka GitHub Actions

* Adding Linux build support

* Upgrade deprecated actions
2024-06-20 00:00:28 +08:00
4f864611ee fix: B30 table order (#11) 2024-06-19 22:20:31 +08:00
d9c163431c feat: OCR score date source (#9)
* New settings entries

* Choose `birthTime`/`lastModified` for OCR score date source if the image EXIF fails
2024-06-19 22:18:25 +08:00
d5895fe230 chore: v0.3.8 2024-04-01 01:00:46 +08:00
cd2e3f51ca ci: update build actions 2024-04-01 01:00:27 +08:00
4a09dc210a Merge pull request #7 from ArcaeaOffline/fix-issue-6
fix: rating class selection logic
2024-03-24 16:40:24 +08:00
cc8ab11b78 fix: rating class selection logic 2024-03-24 16:17:48 +08:00
48c5682e55 Merge pull request #5 from ArcaeaOffline/fix-issue-4
fix: linux dbUrl issue
2024-03-23 19:08:23 +08:00
ee03770764 chore: update README 2024-03-23 18:21:48 +08:00
b45c7f7de5 chore: dependencies 2024-03-23 18:18:53 +08:00
15bc56e6f9 fix: linux dbUrl issue 2024-03-23 17:41:36 +08:00
39ee379010 feat: ETERNAL rating class support 2024-03-20 15:52:26 +08:00
5a71a5822b feat: sync chart info database 2024-03-16 02:14:47 +08:00
c888b312b3 feat: DEF v2 scores export support 2024-02-27 17:24:49 +08:00
8e4fdc30b5 refactor(ui): TabDb_Manage 2024-02-15 17:59:14 +08:00
1ca868cfc6 ci: get full repo history for VERSION generating 2023-11-09 21:53:55 +08:00
d63d2f0d8b ci: build actions improve 2023-11-01 21:30:31 +08:00
3cd187fde3 ci: github actions 2023-11-01 20:00:16 +08:00
cce918a121 chore: update dependencies 2023-11-01 15:57:11 +08:00
1ec302d98c Merge branch 'master' of github.com:283375/arcaea-offline-pyside-ui 2023-10-29 17:13:52 +08:00
3d6e5f997e pre-commit 2023-10-29 00:12:01 +08:00
495f6dc424 impr: TabDb_RemoveDuplicateScores chart selecting 2023-10-25 20:04:23 +08:00
0b599e3d9c fix: ensure database reset works 2023-10-25 18:58:13 +08:00
a51a67fae3 impr: minor improvements 2023-10-25 17:53:21 +08:00
b48e177ae8 feat: TabDb_RemoveDuplicateScores 2023-10-25 17:41:40 +08:00
865fc8b7c8 style: isort & black ignore files 2023-10-23 23:51:29 +08:00
1eeec6f745 wip: TabDb_RemoveDuplicateScores ui 2023-10-23 23:51:11 +08:00
8558f5e403 impr: handle exceptions in TabOverview 2023-10-23 16:18:35 +08:00
1a37310091 impr: TabTools_Andreal source code link 2023-10-23 16:15:26 +08:00
d460e935b4 fix: DbB30TableModel 2023-10-23 16:08:51 +08:00
38d2e4ad5a fix: translation file extraction script 2023-10-23 16:08:11 +08:00
28599cfb04 feat: DatabaseChecker re-init database button 2023-10-23 15:31:56 +08:00
1d01356327 impr: popup PlayRatingCalculator when double clicking an item in TabTools_ChartRecommend 2023-10-23 15:19:56 +08:00
738975a83d chore: translations 2023-10-23 14:39:46 +08:00
5adea908f9 impr: PlayRatingCalculator ui 2023-10-23 14:39:36 +08:00
21ca1018db impr: translation file extraction script 2023-10-23 14:39:11 +08:00
66 changed files with 2953 additions and 930 deletions

View 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
View 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/**/*

13
.pre-commit-config.yaml Normal file
View File

@ -0,0 +1,13 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.4.9
hooks:
- id: ruff
args: ["--fix"]
- id: ruff-format

View File

@ -1,9 +1,27 @@
# Arcaea Offline PySide UI # Arcaea Offline PySide UI
GUI for both [283375/arcaea-offline](https://github.com/283375/arcaea-offline) and [283375/arcaea-offline-ocr](https://github.com/283375/arcaea-offline-ocr) GUI for both [283375/arcaea-offline](https://github.com/283375/arcaea-offline) and [ArcaeaOffline/core-ocr](https://github.com/ArcaeaOffline/core-ocr).
## Before you run `python index.py`... ## Prerequisites
* Install requirements * Install requirements
* Release translation files from `ui/resources/lang/*.ts` * Release translation files from `ui/resources/lang/*.ts`
* Run `prebuild.py`
* Compile `ui/resources/resources.qrc` to `ui/resources/resources_rc.py` * Compile `ui/resources/resources.qrc` to `ui/resources/resources_rc.py`
You can refer to the [GitHub Actions file](./.github/workflows/build.yml) for a rough reference.
```
pip install -r ./requirements.txt
pyside6-lrelease ./ui/resources/lang/en_US.ts ./ui/resources/lang/zh_CN.ts
python prebuild.py
pyside6-rcc ./ui/resources/resources.qrc -o ./ui/resources/resources_rc.py
```
Sometimes you have to install the latest, unpublished version of `arcaea-offline` and `arcaea-offline-ocr`.
```
pip uninstall -y arcaea-offline arcaea-offline-ocr
pip install git+https://github.com/283375/arcaea-offline
pip install git+https://github.com/ArcaeaOffline/core-ocr
```

0
core/__init__.py Normal file
View File

10
core/color.py Normal file
View 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)

View 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
View 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
View 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
View 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()

View File

@ -14,5 +14,5 @@ class Singleton(type, Generic[T]):
return cls._instance return cls._instance
class QObjectSingleton(type(QObject), Singleton): class QSingleton(type(QObject), Singleton):
pass pass

View File

@ -9,25 +9,21 @@ from PySide6.QtCore import QCoreApplication, QLocale
from PySide6.QtGui import QFontDatabase, QIcon from PySide6.QtGui import QFontDatabase, QIcon
from PySide6.QtWidgets import QApplication, QDialog, QMessageBox 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.language import changeAppLanguage
from ui.extends.shared.settings import Settings
from ui.implements.mainwindow import MainWindow from ui.implements.mainwindow import MainWindow
from ui.startup.databaseChecker import DatabaseChecker, DatabaseCheckerResult from ui.startup.databaseChecker import DatabaseChecker, DatabaseCheckerResult
rootLogger = logging.getLogger("root") rootLogger = logging.getLogger("root")
rootLogger.setLevel(logging.DEBUG) 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): def handle_exception(exc_type, exc_value, exc_traceback):
if issubclass(exc_type, KeyboardInterrupt): sys.__excepthook__(exc_type, exc_value, exc_traceback)
sys.__excepthook__(exc_type, exc_value, exc_traceback)
return
if issubclass(exc_type, KeyboardInterrupt):
return
rootLogger.critical( rootLogger.critical(
"Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback) "Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback)
) )
@ -46,6 +42,11 @@ if __name__ == "__main__":
ymd = now.strftime("%Y%m%d") ymd = now.strftime("%Y%m%d")
hms = now.strftime("%H%M%S") 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( rootLoggerFileHandler = logging.FileHandler(
str(logFolder / f"arcaea-offline-pyside-ui-{ymd}-{hms}_debug.log"), str(logFolder / f"arcaea-offline-pyside-ui-{ymd}-{hms}_debug.log"),
encoding="utf-8", encoding="utf-8",
@ -59,16 +60,19 @@ if __name__ == "__main__":
rootLogger.addHandler(rootLoggerStdOutHandler) rootLogger.addHandler(rootLoggerStdOutHandler)
app = QApplication(sys.argv) app = QApplication(sys.argv)
locale = ( settingsLanguage = settings.stringValue(SettingsKeys.General.Language)
QLocale(Settings().language()) if Settings().language() else QLocale.system() locale = QLocale(settingsLanguage) if settingsLanguage else QLocale.system()
)
changeAppLanguage(locale) changeAppLanguage(locale)
QFontDatabase.addApplicationFont(":/fonts/GeosansLight.ttf") QFontDatabase.addApplicationFont(":/fonts/GeosansLight.ttf")
databaseChecker = DatabaseChecker() databaseChecker = DatabaseChecker()
databaseChecker.setWindowIcon(QIcon(":/images/icon.png")) databaseChecker.setWindowIcon(QIcon(":/images/icon.png"))
databaseCheckResult = databaseChecker.confirmDb() databaseCheckResult = (
databaseChecker.confirmDb()
if settings.stringValue(SettingsKeys.General.DatabaseUrl)
else 0
)
if not databaseCheckResult & DatabaseCheckerResult.Initted: if not databaseCheckResult & DatabaseCheckerResult.Initted:
result = databaseChecker.exec() result = databaseChecker.exec()

View File

@ -40,7 +40,6 @@ def getBuildToolsVer():
def writeVersionFile(): def writeVersionFile():
versionFile = Path("ui/resources/VERSION") versionFile = Path("ui/resources/VERSION")
assert versionFile.exists()
versionText = ( versionText = (
"arcaea-offline-pyside-ui\n{gitDesc}\n{buildToolsVer}\n\n" "arcaea-offline-pyside-ui\n{gitDesc}\n{buildToolsVer}\n\n"

View File

@ -4,14 +4,14 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "arcaea-offline-pyside-ui" name = "arcaea-offline-pyside-ui"
version = "0.1.0" version = "0.3.9"
authors = [{ name = "283375", email = "log_283375@163.com" }] authors = [{ name = "283375", email = "log_283375@163.com" }]
description = "No description." description = "No description."
readme = "README.md" readme = "README.md"
requires-python = ">=3.9" requires-python = ">=3.9"
dependencies = [ dependencies = [
"arcaea-offline==0.1.0", "arcaea-offline==0.2.2",
"arcaea-offline-ocr==0.1.0", "arcaea-offline-ocr==0.0.99",
"exif==1.6.0", "exif==1.6.0",
"PySide6==6.5.2", "PySide6==6.5.2",
] ]
@ -21,21 +21,37 @@ classifiers = [
] ]
[project.urls] [project.urls]
"Homepage" = "https://github.com/283375/arcaea-offline-pyside-ui" "Homepage" = "https://github.com/ArcaeaOffline/client-pyside6"
"Bug Tracker" = "https://github.com/283375/arcaea-offline-pyside-ui/issues" "Bug Tracker" = "https://github.com/ArcaeaOffline/client-pyside6/issues"
[tool.black] [tool.ruff]
force-exclude = ''' exclude = ["*_ui.py", "*_rc.py"]
(
ui/designer
| .*_rc.py
)
'''
[tool.isort] [tool.ruff.lint]
profile = "black" # Full list: https://docs.astral.sh/ruff/rules
extend_skip = ["ui/designer"] select = [
extend_skip_glob = ["*_rc.py"] "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] [tool.pyright]
ignore = ["**/__debug*.*"] ignore = ["**/__debug*.*"]

View File

@ -1,4 +1,5 @@
arcaea-offline==0.1.0 arcaea-offline==0.2.2
arcaea-offline-ocr==0.1.0 arcaea-offline-ocr==0.0.99
exif==1.6.0 exif==1.6.0
Pillow==10.1.0
PySide6==6.5.2 PySide6==6.5.2

View File

@ -22,39 +22,6 @@
<string>queue.title</string> <string>queue.title</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_2"> <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> <item>
<widget class="QPushButton" name="ocr_addImageButton"> <widget class="QPushButton" name="ocr_addImageButton">
<property name="text"> <property name="text">
@ -95,6 +62,13 @@
</property> </property>
</spacer> </spacer>
</item> </item>
<item>
<widget class="QPushButton" name="optionsDialogButton">
<property name="text">
<string>queue.optionsButton</string>
</property>
</widget>
</item>
<item> <item>
<widget class="QPushButton" name="ocr_startButton"> <widget class="QPushButton" name="ocr_startButton">
<property name="text"> <property name="text">

View 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>

View 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

View File

@ -17,8 +17,8 @@ from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
QPalette, QPixmap, QRadialGradient, QTransform) QPalette, QPixmap, QRadialGradient, QTransform)
from PySide6.QtWidgets import (QAbstractItemView, QApplication, QCheckBox, QGroupBox, from PySide6.QtWidgets import (QAbstractItemView, QApplication, QCheckBox, QGroupBox,
QHBoxLayout, QHeaderView, QLabel, QProgressBar, QHBoxLayout, QHeaderView, QLabel, QProgressBar,
QPushButton, QRadioButton, QSizePolicy, QSpacerItem, QPushButton, QSizePolicy, QSpacerItem, QTableView,
QTableView, QVBoxLayout, QWidget) QVBoxLayout, QWidget)
class Ui_OcrQueue(object): class Ui_OcrQueue(object):
def setupUi(self, OcrQueue): def setupUi(self, OcrQueue):
@ -34,29 +34,6 @@ class Ui_OcrQueue(object):
self.groupBox_3.setObjectName(u"groupBox_3") self.groupBox_3.setObjectName(u"groupBox_3")
self.verticalLayout_2 = QVBoxLayout(self.groupBox_3) self.verticalLayout_2 = QVBoxLayout(self.groupBox_3)
self.verticalLayout_2.setObjectName(u"verticalLayout_2") 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 = QPushButton(self.groupBox_3)
self.ocr_addImageButton.setObjectName(u"ocr_addImageButton") self.ocr_addImageButton.setObjectName(u"ocr_addImageButton")
@ -78,6 +55,11 @@ class Ui_OcrQueue(object):
self.verticalLayout_2.addItem(self.verticalSpacer) 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 = QPushButton(self.groupBox_3)
self.ocr_startButton.setObjectName(u"ocr_startButton") self.ocr_startButton.setObjectName(u"ocr_startButton")
@ -154,13 +136,10 @@ class Ui_OcrQueue(object):
def retranslateUi(self, OcrQueue): def retranslateUi(self, OcrQueue):
self.groupBox_3.setTitle(QCoreApplication.translate("OcrQueue", u"queue.title", None)) 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_addImageButton.setText(QCoreApplication.translate("OcrQueue", u"queue.addImageButton", None))
self.ocr_removeSelectedButton.setText(QCoreApplication.translate("OcrQueue", u"queue.removeSelected", None)) self.ocr_removeSelectedButton.setText(QCoreApplication.translate("OcrQueue", u"queue.removeSelected", None))
self.ocr_removeAllButton.setText(QCoreApplication.translate("OcrQueue", u"queue.removeAll", 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.ocr_startButton.setText(QCoreApplication.translate("OcrQueue", u"queue.startOcrButton", None))
self.groupBox_5.setTitle(QCoreApplication.translate("OcrQueue", u"results", None)) self.groupBox_5.setTitle(QCoreApplication.translate("OcrQueue", u"results", None))
self.ocr_acceptSelectedButton.setText(QCoreApplication.translate("OcrQueue", u"results.acceptSelectedButton", None)) self.ocr_acceptSelectedButton.setText(QCoreApplication.translate("OcrQueue", u"results.acceptSelectedButton", None))
@ -169,4 +148,3 @@ class Ui_OcrQueue(object):
self.statusLabel.setText("") self.statusLabel.setText("")
pass pass
# retranslateUi # retranslateUi

View File

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>630</width> <width>580</width>
<height>528</height> <height>551</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -17,70 +17,21 @@
<property name="labelAlignment"> <property name="labelAlignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property> </property>
<item row="0" column="0"> <item row="1" column="0">
<widget class="QPushButton" name="syncArcSongDbButton">
<property name="text">
<string>syncArcSongDbButton</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label">
<property name="text">
<string>syncArcSongDb.description</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QPushButton" name="importSt3Button">
<property name="text">
<string>importSt3Button</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLabel" name="label_2">
<property name="text">
<string>importSt3.description</string>
</property>
</widget>
</item>
<item row="7" column="0" colspan="2">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="8" column="0">
<widget class="QPushButton" name="exportScoresButton">
<property name="text">
<string>exportScoresButton</string>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QLabel" name="label_3">
<property name="text">
<string>exportScores.description</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QPushButton" name="importPacklistButton"> <widget class="QPushButton" name="importPacklistButton">
<property name="text"> <property name="text">
<string>importPacklistButton</string> <string>importPacklistButton</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="0"> <item row="1" column="1">
<widget class="QLabel" name="label_4">
<property name="text">
<string>importPacklist.description</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QPushButton" name="importSonglistButton"> <widget class="QPushButton" name="importSonglistButton">
<property name="text"> <property name="text">
<string>importSonglistButton</string> <string>importSonglistButton</string>
@ -88,69 +39,129 @@
</widget> </widget>
</item> </item>
<item row="2" column="1"> <item row="2" column="1">
<widget class="QLabel" name="label_4">
<property name="text">
<string>importPacklist.description</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="label_5"> <widget class="QLabel" name="label_5">
<property name="text"> <property name="text">
<string>importSonglist.description</string> <string>importSonglist.description</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="9" column="0"> <item row="3" column="0">
<widget class="QPushButton" name="exportArcsongJsonButton">
<property name="text">
<string>exportArcsongJsonButton</string>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QLabel" name="label_6">
<property name="text">
<string>exportArcsongJson.description</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QPushButton" name="importApkButton"> <widget class="QPushButton" name="importApkButton">
<property name="text"> <property name="text">
<string>importApkButton</string> <string>importApkButton</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="1"> <item row="3" column="1">
<widget class="QLabel" name="label_7"> <widget class="QLabel" name="label_7">
<property name="text"> <property name="text">
<string>importApk.description</string> <string>importApk.description</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="0" colspan="2">
<widget class="QLabel" name="label_11">
<property name="font">
<font>
<pointsize>12</pointsize>
<bold>false</bold>
</font>
</property>
<property name="text">
<string>chartInfoGroup</string>
</property>
</widget>
</item>
<item row="6" column="0"> <item row="6" column="0">
<widget class="QPushButton" name="syncArcSongDbButton">
<property name="text">
<string>syncArcSongDbButton</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QLabel" name="label">
<property name="text">
<string>syncArcSongDb.description</string>
</property>
</widget>
</item>
<item row="9" column="0" colspan="2">
<widget class="QLabel" name="label_12">
<property name="font">
<font>
<pointsize>12</pointsize>
<bold>false</bold>
</font>
</property>
<property name="text">
<string>importScoreGroup</string>
</property>
</widget>
</item>
<item row="10" column="0">
<widget class="QPushButton" name="importSt3Button">
<property name="text">
<string>importSt3Button</string>
</property>
</widget>
</item>
<item row="10" column="1">
<widget class="QLabel" name="label_2">
<property name="text">
<string>importSt3.description</string>
</property>
</widget>
</item>
<item row="11" column="0">
<widget class="QPushButton" name="importOnlineButton"> <widget class="QPushButton" name="importOnlineButton">
<property name="text"> <property name="text">
<string>importOnlineButton</string> <string>importOnlineButton</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="6" column="1"> <item row="11" column="1">
<widget class="QLabel" name="label_8"> <widget class="QLabel" name="label_8">
<property name="text"> <property name="text">
<string>importOnline.description</string> <string>importOnline.description</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="10" column="0"> <item row="13" column="0" colspan="2">
<widget class="QLabel" name="label_13">
<property name="font">
<font>
<pointsize>12</pointsize>
<bold>false</bold>
</font>
</property>
<property name="text">
<string>exportScoreGroup</string>
</property>
</widget>
</item>
<item row="14" column="0">
<widget class="QPushButton" name="exportScoresButton">
<property name="text">
<string>exportScoresButton</string>
</property>
</widget>
</item>
<item row="14" column="1">
<widget class="QLabel" name="label_3">
<property name="text">
<string>exportScores.description</string>
</property>
</widget>
</item>
<item row="15" column="0">
<widget class="QPushButton" name="exportSmartRteB30Button"> <widget class="QPushButton" name="exportSmartRteB30Button">
<property name="text"> <property name="text">
<string>exportSmartRteB30Button</string> <string>exportSmartRteB30Button</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="10" column="1"> <item row="15" column="1">
<widget class="QLabel" name="label_9"> <widget class="QLabel" name="label_9">
<property name="text"> <property name="text">
<string>exportSmartRteB30.description</string> <string>exportSmartRteB30.description</string>
@ -163,6 +174,124 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="17" column="0" colspan="2">
<widget class="QLabel" name="label_14">
<property name="font">
<font>
<pointsize>12</pointsize>
<bold>false</bold>
</font>
</property>
<property name="text">
<string>miscGroup</string>
</property>
</widget>
</item>
<item row="18" column="0">
<widget class="QPushButton" name="exportArcsongJsonButton">
<property name="text">
<string>exportArcsongJsonButton</string>
</property>
</widget>
</item>
<item row="18" column="1">
<widget class="QLabel" name="label_6">
<property name="text">
<string>exportArcsongJson.description</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="label_10">
<property name="font">
<font>
<pointsize>12</pointsize>
<bold>false</bold>
</font>
</property>
<property name="text">
<string>packSongInfoGroup</string>
</property>
</widget>
</item>
<item row="4" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Minimum</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="8" column="0">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Minimum</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="12" column="0">
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Minimum</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="16" column="0">
<spacer name="verticalSpacer_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Minimum</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="7" column="0">
<widget class="QPushButton" name="syncChartInfoDbButton">
<property name="text">
<string>syncChartInfoDbButton</string>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QLabel" name="label_15">
<property name="text">
<string>syncChartInfoDb.description</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<resources/> <resources/>

View File

@ -15,123 +15,168 @@ from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
QFont, QFontDatabase, QGradient, QIcon, QFont, QFontDatabase, QGradient, QIcon,
QImage, QKeySequence, QLinearGradient, QPainter, QImage, QKeySequence, QLinearGradient, QPainter,
QPalette, QPixmap, QRadialGradient, QTransform) QPalette, QPixmap, QRadialGradient, QTransform)
from PySide6.QtWidgets import (QApplication, QFormLayout, QFrame, QLabel, from PySide6.QtWidgets import (QApplication, QFormLayout, QLabel, QPushButton,
QPushButton, QSizePolicy, QWidget) QSizePolicy, QSpacerItem, QWidget)
class Ui_TabDb_Manage(object): class Ui_TabDb_Manage(object):
def setupUi(self, TabDb_Manage): def setupUi(self, TabDb_Manage):
if not TabDb_Manage.objectName(): if not TabDb_Manage.objectName():
TabDb_Manage.setObjectName(u"TabDb_Manage") TabDb_Manage.setObjectName(u"TabDb_Manage")
TabDb_Manage.resize(630, 528) TabDb_Manage.resize(580, 551)
TabDb_Manage.setWindowTitle(u"TabDb_Manage") TabDb_Manage.setWindowTitle(u"TabDb_Manage")
self.formLayout = QFormLayout(TabDb_Manage) self.formLayout = QFormLayout(TabDb_Manage)
self.formLayout.setObjectName(u"formLayout") self.formLayout.setObjectName(u"formLayout")
self.formLayout.setLabelAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter) self.formLayout.setLabelAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
self.syncArcSongDbButton = QPushButton(TabDb_Manage)
self.syncArcSongDbButton.setObjectName(u"syncArcSongDbButton")
self.formLayout.setWidget(0, QFormLayout.LabelRole, self.syncArcSongDbButton)
self.label = QLabel(TabDb_Manage)
self.label.setObjectName(u"label")
self.formLayout.setWidget(0, QFormLayout.FieldRole, self.label)
self.importSt3Button = QPushButton(TabDb_Manage)
self.importSt3Button.setObjectName(u"importSt3Button")
self.formLayout.setWidget(5, QFormLayout.LabelRole, self.importSt3Button)
self.label_2 = QLabel(TabDb_Manage)
self.label_2.setObjectName(u"label_2")
self.formLayout.setWidget(5, QFormLayout.FieldRole, self.label_2)
self.line = QFrame(TabDb_Manage)
self.line.setObjectName(u"line")
self.line.setFrameShape(QFrame.HLine)
self.line.setFrameShadow(QFrame.Sunken)
self.formLayout.setWidget(7, QFormLayout.SpanningRole, self.line)
self.exportScoresButton = QPushButton(TabDb_Manage)
self.exportScoresButton.setObjectName(u"exportScoresButton")
self.formLayout.setWidget(8, QFormLayout.LabelRole, self.exportScoresButton)
self.label_3 = QLabel(TabDb_Manage)
self.label_3.setObjectName(u"label_3")
self.formLayout.setWidget(8, QFormLayout.FieldRole, self.label_3)
self.line_2 = QFrame(TabDb_Manage)
self.line_2.setObjectName(u"line_2")
self.line_2.setFrameShape(QFrame.HLine)
self.line_2.setFrameShadow(QFrame.Sunken)
self.formLayout.setWidget(1, QFormLayout.SpanningRole, self.line_2)
self.importPacklistButton = QPushButton(TabDb_Manage) self.importPacklistButton = QPushButton(TabDb_Manage)
self.importPacklistButton.setObjectName(u"importPacklistButton") self.importPacklistButton.setObjectName(u"importPacklistButton")
self.formLayout.setWidget(2, QFormLayout.LabelRole, self.importPacklistButton) self.formLayout.setWidget(1, QFormLayout.LabelRole, self.importPacklistButton)
self.importSonglistButton = QPushButton(TabDb_Manage)
self.importSonglistButton.setObjectName(u"importSonglistButton")
self.formLayout.setWidget(3, QFormLayout.LabelRole, self.importSonglistButton)
self.label_4 = QLabel(TabDb_Manage) self.label_4 = QLabel(TabDb_Manage)
self.label_4.setObjectName(u"label_4") self.label_4.setObjectName(u"label_4")
self.formLayout.setWidget(2, QFormLayout.FieldRole, self.label_4) self.formLayout.setWidget(1, QFormLayout.FieldRole, self.label_4)
self.importSonglistButton = QPushButton(TabDb_Manage)
self.importSonglistButton.setObjectName(u"importSonglistButton")
self.formLayout.setWidget(2, QFormLayout.LabelRole, self.importSonglistButton)
self.label_5 = QLabel(TabDb_Manage) self.label_5 = QLabel(TabDb_Manage)
self.label_5.setObjectName(u"label_5") self.label_5.setObjectName(u"label_5")
self.formLayout.setWidget(3, QFormLayout.FieldRole, self.label_5) self.formLayout.setWidget(2, QFormLayout.FieldRole, self.label_5)
self.exportArcsongJsonButton = QPushButton(TabDb_Manage)
self.exportArcsongJsonButton.setObjectName(u"exportArcsongJsonButton")
self.formLayout.setWidget(9, QFormLayout.LabelRole, self.exportArcsongJsonButton)
self.label_6 = QLabel(TabDb_Manage)
self.label_6.setObjectName(u"label_6")
self.formLayout.setWidget(9, QFormLayout.FieldRole, self.label_6)
self.importApkButton = QPushButton(TabDb_Manage) self.importApkButton = QPushButton(TabDb_Manage)
self.importApkButton.setObjectName(u"importApkButton") self.importApkButton.setObjectName(u"importApkButton")
self.formLayout.setWidget(4, QFormLayout.LabelRole, self.importApkButton) self.formLayout.setWidget(3, QFormLayout.LabelRole, self.importApkButton)
self.label_7 = QLabel(TabDb_Manage) self.label_7 = QLabel(TabDb_Manage)
self.label_7.setObjectName(u"label_7") self.label_7.setObjectName(u"label_7")
self.formLayout.setWidget(4, QFormLayout.FieldRole, self.label_7) self.formLayout.setWidget(3, QFormLayout.FieldRole, self.label_7)
self.label_11 = QLabel(TabDb_Manage)
self.label_11.setObjectName(u"label_11")
font = QFont()
font.setPointSize(12)
font.setBold(False)
self.label_11.setFont(font)
self.formLayout.setWidget(5, QFormLayout.SpanningRole, self.label_11)
self.syncArcSongDbButton = QPushButton(TabDb_Manage)
self.syncArcSongDbButton.setObjectName(u"syncArcSongDbButton")
self.formLayout.setWidget(6, QFormLayout.LabelRole, self.syncArcSongDbButton)
self.label = QLabel(TabDb_Manage)
self.label.setObjectName(u"label")
self.formLayout.setWidget(6, QFormLayout.FieldRole, self.label)
self.label_12 = QLabel(TabDb_Manage)
self.label_12.setObjectName(u"label_12")
self.label_12.setFont(font)
self.formLayout.setWidget(9, QFormLayout.SpanningRole, self.label_12)
self.importSt3Button = QPushButton(TabDb_Manage)
self.importSt3Button.setObjectName(u"importSt3Button")
self.formLayout.setWidget(10, QFormLayout.LabelRole, self.importSt3Button)
self.label_2 = QLabel(TabDb_Manage)
self.label_2.setObjectName(u"label_2")
self.formLayout.setWidget(10, QFormLayout.FieldRole, self.label_2)
self.importOnlineButton = QPushButton(TabDb_Manage) self.importOnlineButton = QPushButton(TabDb_Manage)
self.importOnlineButton.setObjectName(u"importOnlineButton") self.importOnlineButton.setObjectName(u"importOnlineButton")
self.formLayout.setWidget(6, QFormLayout.LabelRole, self.importOnlineButton) self.formLayout.setWidget(11, QFormLayout.LabelRole, self.importOnlineButton)
self.label_8 = QLabel(TabDb_Manage) self.label_8 = QLabel(TabDb_Manage)
self.label_8.setObjectName(u"label_8") self.label_8.setObjectName(u"label_8")
self.formLayout.setWidget(6, QFormLayout.FieldRole, self.label_8) self.formLayout.setWidget(11, QFormLayout.FieldRole, self.label_8)
self.label_13 = QLabel(TabDb_Manage)
self.label_13.setObjectName(u"label_13")
self.label_13.setFont(font)
self.formLayout.setWidget(13, QFormLayout.SpanningRole, self.label_13)
self.exportScoresButton = QPushButton(TabDb_Manage)
self.exportScoresButton.setObjectName(u"exportScoresButton")
self.formLayout.setWidget(14, QFormLayout.LabelRole, self.exportScoresButton)
self.label_3 = QLabel(TabDb_Manage)
self.label_3.setObjectName(u"label_3")
self.formLayout.setWidget(14, QFormLayout.FieldRole, self.label_3)
self.exportSmartRteB30Button = QPushButton(TabDb_Manage) self.exportSmartRteB30Button = QPushButton(TabDb_Manage)
self.exportSmartRteB30Button.setObjectName(u"exportSmartRteB30Button") self.exportSmartRteB30Button.setObjectName(u"exportSmartRteB30Button")
self.formLayout.setWidget(10, QFormLayout.LabelRole, self.exportSmartRteB30Button) self.formLayout.setWidget(15, QFormLayout.LabelRole, self.exportSmartRteB30Button)
self.label_9 = QLabel(TabDb_Manage) self.label_9 = QLabel(TabDb_Manage)
self.label_9.setObjectName(u"label_9") self.label_9.setObjectName(u"label_9")
self.label_9.setOpenExternalLinks(True) self.label_9.setOpenExternalLinks(True)
self.label_9.setTextInteractionFlags(Qt.LinksAccessibleByKeyboard|Qt.LinksAccessibleByMouse) self.label_9.setTextInteractionFlags(Qt.LinksAccessibleByKeyboard|Qt.LinksAccessibleByMouse)
self.formLayout.setWidget(10, QFormLayout.FieldRole, self.label_9) self.formLayout.setWidget(15, QFormLayout.FieldRole, self.label_9)
self.label_14 = QLabel(TabDb_Manage)
self.label_14.setObjectName(u"label_14")
self.label_14.setFont(font)
self.formLayout.setWidget(17, QFormLayout.SpanningRole, self.label_14)
self.exportArcsongJsonButton = QPushButton(TabDb_Manage)
self.exportArcsongJsonButton.setObjectName(u"exportArcsongJsonButton")
self.formLayout.setWidget(18, QFormLayout.LabelRole, self.exportArcsongJsonButton)
self.label_6 = QLabel(TabDb_Manage)
self.label_6.setObjectName(u"label_6")
self.formLayout.setWidget(18, QFormLayout.FieldRole, self.label_6)
self.label_10 = QLabel(TabDb_Manage)
self.label_10.setObjectName(u"label_10")
self.label_10.setFont(font)
self.formLayout.setWidget(0, QFormLayout.SpanningRole, self.label_10)
self.verticalSpacer = QSpacerItem(20, 20, QSizePolicy.Minimum, QSizePolicy.Minimum)
self.formLayout.setItem(4, QFormLayout.LabelRole, self.verticalSpacer)
self.verticalSpacer_2 = QSpacerItem(20, 20, QSizePolicy.Minimum, QSizePolicy.Minimum)
self.formLayout.setItem(8, QFormLayout.LabelRole, self.verticalSpacer_2)
self.verticalSpacer_3 = QSpacerItem(20, 20, QSizePolicy.Minimum, QSizePolicy.Minimum)
self.formLayout.setItem(12, QFormLayout.LabelRole, self.verticalSpacer_3)
self.verticalSpacer_4 = QSpacerItem(20, 20, QSizePolicy.Minimum, QSizePolicy.Minimum)
self.formLayout.setItem(16, QFormLayout.LabelRole, self.verticalSpacer_4)
self.syncChartInfoDbButton = QPushButton(TabDb_Manage)
self.syncChartInfoDbButton.setObjectName(u"syncChartInfoDbButton")
self.formLayout.setWidget(7, QFormLayout.LabelRole, self.syncChartInfoDbButton)
self.label_15 = QLabel(TabDb_Manage)
self.label_15.setObjectName(u"label_15")
self.formLayout.setWidget(7, QFormLayout.FieldRole, self.label_15)
self.retranslateUi(TabDb_Manage) self.retranslateUi(TabDb_Manage)
@ -140,24 +185,30 @@ class Ui_TabDb_Manage(object):
# setupUi # setupUi
def retranslateUi(self, TabDb_Manage): def retranslateUi(self, TabDb_Manage):
self.syncArcSongDbButton.setText(QCoreApplication.translate("TabDb_Manage", u"syncArcSongDbButton", None))
self.label.setText(QCoreApplication.translate("TabDb_Manage", u"syncArcSongDb.description", None))
self.importSt3Button.setText(QCoreApplication.translate("TabDb_Manage", u"importSt3Button", None))
self.label_2.setText(QCoreApplication.translate("TabDb_Manage", u"importSt3.description", None))
self.exportScoresButton.setText(QCoreApplication.translate("TabDb_Manage", u"exportScoresButton", None))
self.label_3.setText(QCoreApplication.translate("TabDb_Manage", u"exportScores.description", None))
self.importPacklistButton.setText(QCoreApplication.translate("TabDb_Manage", u"importPacklistButton", None)) self.importPacklistButton.setText(QCoreApplication.translate("TabDb_Manage", u"importPacklistButton", None))
self.importSonglistButton.setText(QCoreApplication.translate("TabDb_Manage", u"importSonglistButton", None))
self.label_4.setText(QCoreApplication.translate("TabDb_Manage", u"importPacklist.description", None)) self.label_4.setText(QCoreApplication.translate("TabDb_Manage", u"importPacklist.description", None))
self.importSonglistButton.setText(QCoreApplication.translate("TabDb_Manage", u"importSonglistButton", None))
self.label_5.setText(QCoreApplication.translate("TabDb_Manage", u"importSonglist.description", None)) self.label_5.setText(QCoreApplication.translate("TabDb_Manage", u"importSonglist.description", None))
self.exportArcsongJsonButton.setText(QCoreApplication.translate("TabDb_Manage", u"exportArcsongJsonButton", None))
self.label_6.setText(QCoreApplication.translate("TabDb_Manage", u"exportArcsongJson.description", None))
self.importApkButton.setText(QCoreApplication.translate("TabDb_Manage", u"importApkButton", None)) self.importApkButton.setText(QCoreApplication.translate("TabDb_Manage", u"importApkButton", None))
self.label_7.setText(QCoreApplication.translate("TabDb_Manage", u"importApk.description", None)) self.label_7.setText(QCoreApplication.translate("TabDb_Manage", u"importApk.description", None))
self.label_11.setText(QCoreApplication.translate("TabDb_Manage", u"chartInfoGroup", None))
self.syncArcSongDbButton.setText(QCoreApplication.translate("TabDb_Manage", u"syncArcSongDbButton", None))
self.label.setText(QCoreApplication.translate("TabDb_Manage", u"syncArcSongDb.description", None))
self.label_12.setText(QCoreApplication.translate("TabDb_Manage", u"importScoreGroup", None))
self.importSt3Button.setText(QCoreApplication.translate("TabDb_Manage", u"importSt3Button", None))
self.label_2.setText(QCoreApplication.translate("TabDb_Manage", u"importSt3.description", None))
self.importOnlineButton.setText(QCoreApplication.translate("TabDb_Manage", u"importOnlineButton", None)) self.importOnlineButton.setText(QCoreApplication.translate("TabDb_Manage", u"importOnlineButton", None))
self.label_8.setText(QCoreApplication.translate("TabDb_Manage", u"importOnline.description", None)) self.label_8.setText(QCoreApplication.translate("TabDb_Manage", u"importOnline.description", None))
self.label_13.setText(QCoreApplication.translate("TabDb_Manage", u"exportScoreGroup", None))
self.exportScoresButton.setText(QCoreApplication.translate("TabDb_Manage", u"exportScoresButton", None))
self.label_3.setText(QCoreApplication.translate("TabDb_Manage", u"exportScores.description", None))
self.exportSmartRteB30Button.setText(QCoreApplication.translate("TabDb_Manage", u"exportSmartRteB30Button", None)) self.exportSmartRteB30Button.setText(QCoreApplication.translate("TabDb_Manage", u"exportSmartRteB30Button", None))
self.label_9.setText(QCoreApplication.translate("TabDb_Manage", u"exportSmartRteB30.description", None)) self.label_9.setText(QCoreApplication.translate("TabDb_Manage", u"exportSmartRteB30.description", None))
self.label_14.setText(QCoreApplication.translate("TabDb_Manage", u"miscGroup", None))
self.exportArcsongJsonButton.setText(QCoreApplication.translate("TabDb_Manage", u"exportArcsongJsonButton", None))
self.label_6.setText(QCoreApplication.translate("TabDb_Manage", u"exportArcsongJson.description", None))
self.label_10.setText(QCoreApplication.translate("TabDb_Manage", u"packSongInfoGroup", None))
self.syncChartInfoDbButton.setText(QCoreApplication.translate("TabDb_Manage", u"syncChartInfoDbButton", None))
self.label_15.setText(QCoreApplication.translate("TabDb_Manage", u"syncChartInfoDb.description", None))
pass pass
# retranslateUi # retranslateUi

View File

@ -0,0 +1,286 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TabDb_RemoveDuplicateScores</class>
<widget class="QWidget" name="TabDb_RemoveDuplicateScores">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>600</width>
<height>500</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">TabDb_RemoveDuplicateScores</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>scan.title</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QCheckBox" name="scan_option_scoreCheckBox">
<property name="text">
<string>scan.option.score</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="scan_option_pureCheckBox">
<property name="text">
<string notr="true">PURE</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="scan_option_farCheckBox">
<property name="text">
<string notr="true">FAR</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="scan_option_lostCheckBox">
<property name="text">
<string notr="true">LOST</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="scan_option_maxRecallCheckBox">
<property name="text">
<string notr="true">MAX RECALL</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QCheckBox" name="scan_option_dateCheckBox">
<property name="text">
<string>scan.option.date</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="scan_option_modifierCheckBox">
<property name="text">
<string>scan.option.modifier</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="scan_option_clearTypeCheckBox">
<property name="text">
<string>scan.option.clearType</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="scan_scanButton">
<property name="text">
<string>scan.scanButton</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QTreeView" name="treeView">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::NoSelection</enum>
</property>
<property name="verticalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
<property name="headerHidden">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>quickSelect.title</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>quickSelect.description</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="quickSelect_comboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="quickSelect_selectButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>quickSelect.selectButton</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string/>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QPushButton" name="deselectAllButton">
<property name="text">
<string>deselectAllButton</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="reverseSelectionButton">
<property name="text">
<string>reverseSelectionButton</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="collapseAllButton">
<property name="text">
<string>collapseAllButton</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="expandAllButton">
<property name="text">
<string>expandAllButton</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="resetModelButton">
<property name="text">
<string>resetModelButton</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="deleteSelectionButton">
<property name="font">
<font>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">QPushButton { color: red };</string>
</property>
<property name="text">
<string>deleteSelectionButton</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<tabstops>
<tabstop>scan_option_scoreCheckBox</tabstop>
<tabstop>scan_option_pureCheckBox</tabstop>
<tabstop>scan_option_farCheckBox</tabstop>
<tabstop>scan_option_lostCheckBox</tabstop>
<tabstop>scan_option_maxRecallCheckBox</tabstop>
<tabstop>scan_option_dateCheckBox</tabstop>
<tabstop>scan_option_modifierCheckBox</tabstop>
<tabstop>scan_option_clearTypeCheckBox</tabstop>
<tabstop>scan_scanButton</tabstop>
<tabstop>treeView</tabstop>
<tabstop>quickSelect_comboBox</tabstop>
<tabstop>quickSelect_selectButton</tabstop>
<tabstop>deselectAllButton</tabstop>
<tabstop>reverseSelectionButton</tabstop>
<tabstop>collapseAllButton</tabstop>
<tabstop>expandAllButton</tabstop>
<tabstop>resetModelButton</tabstop>
<tabstop>deleteSelectionButton</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,245 @@
# -*- coding: utf-8 -*-
################################################################################
## Form generated from reading UI file 'tabDb_RemoveDuplicateScores.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 (QAbstractItemView, QApplication, QCheckBox, QComboBox,
QGroupBox, QHBoxLayout, QHeaderView, QLabel,
QPushButton, QSizePolicy, QSpacerItem, QTreeView,
QVBoxLayout, QWidget)
class Ui_TabDb_RemoveDuplicateScores(object):
def setupUi(self, TabDb_RemoveDuplicateScores):
if not TabDb_RemoveDuplicateScores.objectName():
TabDb_RemoveDuplicateScores.setObjectName(u"TabDb_RemoveDuplicateScores")
TabDb_RemoveDuplicateScores.resize(600, 500)
TabDb_RemoveDuplicateScores.setWindowTitle(u"TabDb_RemoveDuplicateScores")
self.verticalLayout_2 = QVBoxLayout(TabDb_RemoveDuplicateScores)
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
self.groupBox_2 = QGroupBox(TabDb_RemoveDuplicateScores)
self.groupBox_2.setObjectName(u"groupBox_2")
self.verticalLayout = QVBoxLayout(self.groupBox_2)
self.verticalLayout.setObjectName(u"verticalLayout")
self.verticalLayout_4 = QVBoxLayout()
self.verticalLayout_4.setObjectName(u"verticalLayout_4")
self.horizontalLayout_2 = QHBoxLayout()
self.horizontalLayout_2.setObjectName(u"horizontalLayout_2")
self.scan_option_scoreCheckBox = QCheckBox(self.groupBox_2)
self.scan_option_scoreCheckBox.setObjectName(u"scan_option_scoreCheckBox")
self.horizontalLayout_2.addWidget(self.scan_option_scoreCheckBox)
self.scan_option_pureCheckBox = QCheckBox(self.groupBox_2)
self.scan_option_pureCheckBox.setObjectName(u"scan_option_pureCheckBox")
self.scan_option_pureCheckBox.setText(u"PURE")
self.horizontalLayout_2.addWidget(self.scan_option_pureCheckBox)
self.scan_option_farCheckBox = QCheckBox(self.groupBox_2)
self.scan_option_farCheckBox.setObjectName(u"scan_option_farCheckBox")
self.scan_option_farCheckBox.setText(u"FAR")
self.horizontalLayout_2.addWidget(self.scan_option_farCheckBox)
self.scan_option_lostCheckBox = QCheckBox(self.groupBox_2)
self.scan_option_lostCheckBox.setObjectName(u"scan_option_lostCheckBox")
self.scan_option_lostCheckBox.setText(u"LOST")
self.horizontalLayout_2.addWidget(self.scan_option_lostCheckBox)
self.scan_option_maxRecallCheckBox = QCheckBox(self.groupBox_2)
self.scan_option_maxRecallCheckBox.setObjectName(u"scan_option_maxRecallCheckBox")
self.scan_option_maxRecallCheckBox.setText(u"MAX RECALL")
self.horizontalLayout_2.addWidget(self.scan_option_maxRecallCheckBox)
self.verticalLayout_4.addLayout(self.horizontalLayout_2)
self.verticalLayout.addLayout(self.verticalLayout_4)
self.horizontalLayout_3 = QHBoxLayout()
self.horizontalLayout_3.setObjectName(u"horizontalLayout_3")
self.scan_option_dateCheckBox = QCheckBox(self.groupBox_2)
self.scan_option_dateCheckBox.setObjectName(u"scan_option_dateCheckBox")
self.horizontalLayout_3.addWidget(self.scan_option_dateCheckBox)
self.scan_option_modifierCheckBox = QCheckBox(self.groupBox_2)
self.scan_option_modifierCheckBox.setObjectName(u"scan_option_modifierCheckBox")
self.horizontalLayout_3.addWidget(self.scan_option_modifierCheckBox)
self.scan_option_clearTypeCheckBox = QCheckBox(self.groupBox_2)
self.scan_option_clearTypeCheckBox.setObjectName(u"scan_option_clearTypeCheckBox")
self.horizontalLayout_3.addWidget(self.scan_option_clearTypeCheckBox)
self.verticalLayout.addLayout(self.horizontalLayout_3)
self.scan_scanButton = QPushButton(self.groupBox_2)
self.scan_scanButton.setObjectName(u"scan_scanButton")
self.verticalLayout.addWidget(self.scan_scanButton)
self.verticalLayout_2.addWidget(self.groupBox_2)
self.horizontalLayout = QHBoxLayout()
self.horizontalLayout.setObjectName(u"horizontalLayout")
self.treeView = QTreeView(TabDb_RemoveDuplicateScores)
self.treeView.setObjectName(u"treeView")
self.treeView.setEditTriggers(QAbstractItemView.NoEditTriggers)
self.treeView.setSelectionMode(QAbstractItemView.NoSelection)
self.treeView.setVerticalScrollMode(QAbstractItemView.ScrollPerPixel)
self.treeView.setHeaderHidden(True)
self.horizontalLayout.addWidget(self.treeView)
self.verticalLayout_6 = QVBoxLayout()
self.verticalLayout_6.setObjectName(u"verticalLayout_6")
self.groupBox = QGroupBox(TabDb_RemoveDuplicateScores)
self.groupBox.setObjectName(u"groupBox")
self.verticalLayout_3 = QVBoxLayout(self.groupBox)
self.verticalLayout_3.setObjectName(u"verticalLayout_3")
self.label = QLabel(self.groupBox)
self.label.setObjectName(u"label")
self.verticalLayout_3.addWidget(self.label)
self.quickSelect_comboBox = QComboBox(self.groupBox)
self.quickSelect_comboBox.setObjectName(u"quickSelect_comboBox")
sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.quickSelect_comboBox.sizePolicy().hasHeightForWidth())
self.quickSelect_comboBox.setSizePolicy(sizePolicy)
self.verticalLayout_3.addWidget(self.quickSelect_comboBox)
self.verticalSpacer_2 = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
self.verticalLayout_3.addItem(self.verticalSpacer_2)
self.quickSelect_selectButton = QPushButton(self.groupBox)
self.quickSelect_selectButton.setObjectName(u"quickSelect_selectButton")
sizePolicy.setHeightForWidth(self.quickSelect_selectButton.sizePolicy().hasHeightForWidth())
self.quickSelect_selectButton.setSizePolicy(sizePolicy)
self.verticalLayout_3.addWidget(self.quickSelect_selectButton)
self.verticalLayout_6.addWidget(self.groupBox)
self.groupBox_3 = QGroupBox(TabDb_RemoveDuplicateScores)
self.groupBox_3.setObjectName(u"groupBox_3")
self.verticalLayout_5 = QVBoxLayout(self.groupBox_3)
self.verticalLayout_5.setObjectName(u"verticalLayout_5")
self.deselectAllButton = QPushButton(self.groupBox_3)
self.deselectAllButton.setObjectName(u"deselectAllButton")
self.verticalLayout_5.addWidget(self.deselectAllButton)
self.reverseSelectionButton = QPushButton(self.groupBox_3)
self.reverseSelectionButton.setObjectName(u"reverseSelectionButton")
self.verticalLayout_5.addWidget(self.reverseSelectionButton)
self.verticalSpacer_3 = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
self.verticalLayout_5.addItem(self.verticalSpacer_3)
self.collapseAllButton = QPushButton(self.groupBox_3)
self.collapseAllButton.setObjectName(u"collapseAllButton")
self.verticalLayout_5.addWidget(self.collapseAllButton)
self.expandAllButton = QPushButton(self.groupBox_3)
self.expandAllButton.setObjectName(u"expandAllButton")
self.verticalLayout_5.addWidget(self.expandAllButton)
self.resetModelButton = QPushButton(self.groupBox_3)
self.resetModelButton.setObjectName(u"resetModelButton")
self.verticalLayout_5.addWidget(self.resetModelButton)
self.verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
self.verticalLayout_5.addItem(self.verticalSpacer)
self.deleteSelectionButton = QPushButton(self.groupBox_3)
self.deleteSelectionButton.setObjectName(u"deleteSelectionButton")
font = QFont()
font.setBold(True)
self.deleteSelectionButton.setFont(font)
self.deleteSelectionButton.setStyleSheet(u"QPushButton { color: red };")
self.verticalLayout_5.addWidget(self.deleteSelectionButton)
self.verticalLayout_6.addWidget(self.groupBox_3)
self.horizontalLayout.addLayout(self.verticalLayout_6)
self.verticalLayout_2.addLayout(self.horizontalLayout)
QWidget.setTabOrder(self.scan_option_scoreCheckBox, self.scan_option_pureCheckBox)
QWidget.setTabOrder(self.scan_option_pureCheckBox, self.scan_option_farCheckBox)
QWidget.setTabOrder(self.scan_option_farCheckBox, self.scan_option_lostCheckBox)
QWidget.setTabOrder(self.scan_option_lostCheckBox, self.scan_option_maxRecallCheckBox)
QWidget.setTabOrder(self.scan_option_maxRecallCheckBox, self.scan_option_dateCheckBox)
QWidget.setTabOrder(self.scan_option_dateCheckBox, self.scan_option_modifierCheckBox)
QWidget.setTabOrder(self.scan_option_modifierCheckBox, self.scan_option_clearTypeCheckBox)
QWidget.setTabOrder(self.scan_option_clearTypeCheckBox, self.scan_scanButton)
QWidget.setTabOrder(self.scan_scanButton, self.treeView)
QWidget.setTabOrder(self.treeView, self.quickSelect_comboBox)
QWidget.setTabOrder(self.quickSelect_comboBox, self.quickSelect_selectButton)
QWidget.setTabOrder(self.quickSelect_selectButton, self.deselectAllButton)
QWidget.setTabOrder(self.deselectAllButton, self.reverseSelectionButton)
QWidget.setTabOrder(self.reverseSelectionButton, self.collapseAllButton)
QWidget.setTabOrder(self.collapseAllButton, self.expandAllButton)
QWidget.setTabOrder(self.expandAllButton, self.resetModelButton)
QWidget.setTabOrder(self.resetModelButton, self.deleteSelectionButton)
self.retranslateUi(TabDb_RemoveDuplicateScores)
QMetaObject.connectSlotsByName(TabDb_RemoveDuplicateScores)
# setupUi
def retranslateUi(self, TabDb_RemoveDuplicateScores):
self.groupBox_2.setTitle(QCoreApplication.translate("TabDb_RemoveDuplicateScores", u"scan.title", None))
self.scan_option_scoreCheckBox.setText(QCoreApplication.translate("TabDb_RemoveDuplicateScores", u"scan.option.score", None))
self.scan_option_dateCheckBox.setText(QCoreApplication.translate("TabDb_RemoveDuplicateScores", u"scan.option.date", None))
self.scan_option_modifierCheckBox.setText(QCoreApplication.translate("TabDb_RemoveDuplicateScores", u"scan.option.modifier", None))
self.scan_option_clearTypeCheckBox.setText(QCoreApplication.translate("TabDb_RemoveDuplicateScores", u"scan.option.clearType", None))
self.scan_scanButton.setText(QCoreApplication.translate("TabDb_RemoveDuplicateScores", u"scan.scanButton", None))
self.groupBox.setTitle(QCoreApplication.translate("TabDb_RemoveDuplicateScores", u"quickSelect.title", None))
self.label.setText(QCoreApplication.translate("TabDb_RemoveDuplicateScores", u"quickSelect.description", None))
self.quickSelect_selectButton.setText(QCoreApplication.translate("TabDb_RemoveDuplicateScores", u"quickSelect.selectButton", None))
self.groupBox_3.setTitle("")
self.deselectAllButton.setText(QCoreApplication.translate("TabDb_RemoveDuplicateScores", u"deselectAllButton", None))
self.reverseSelectionButton.setText(QCoreApplication.translate("TabDb_RemoveDuplicateScores", u"reverseSelectionButton", None))
self.collapseAllButton.setText(QCoreApplication.translate("TabDb_RemoveDuplicateScores", u"collapseAllButton", None))
self.expandAllButton.setText(QCoreApplication.translate("TabDb_RemoveDuplicateScores", u"expandAllButton", None))
self.resetModelButton.setText(QCoreApplication.translate("TabDb_RemoveDuplicateScores", u"resetModelButton", None))
self.deleteSelectionButton.setText(QCoreApplication.translate("TabDb_RemoveDuplicateScores", u"deleteSelectionButton", None))
pass
# retranslateUi

View File

@ -29,6 +29,11 @@
<string>tab.chartInfoEditor</string> <string>tab.chartInfoEditor</string>
</attribute> </attribute>
</widget> </widget>
<widget class="TabDb_RemoveDuplicateScores" name="tab_removeDuplicateScores">
<attribute name="title">
<string>tab.removeDuplicateScores</string>
</attribute>
</widget>
</widget> </widget>
</item> </item>
</layout> </layout>
@ -46,6 +51,12 @@
<header>ui.implements.tabs.tabDb.tabDb_ChartInfoEditor</header> <header>ui.implements.tabs.tabDb.tabDb_ChartInfoEditor</header>
<container>1</container> <container>1</container>
</customwidget> </customwidget>
<customwidget>
<class>TabDb_RemoveDuplicateScores</class>
<extends>QWidget</extends>
<header>ui.implements.tabs.tabDb.tabDb_RemoveDuplicateScores</header>
<container>1</container>
</customwidget>
</customwidgets> </customwidgets>
<resources/> <resources/>
<connections/> <connections/>

View File

@ -20,6 +20,7 @@ from PySide6.QtWidgets import (QApplication, QSizePolicy, QTabWidget, QVBoxLayou
from ui.implements.tabs.tabDb.tabDb_ChartInfoEditor import TabDb_ChartInfoEditor from ui.implements.tabs.tabDb.tabDb_ChartInfoEditor import TabDb_ChartInfoEditor
from ui.implements.tabs.tabDb.tabDb_Manage import TabDb_Manage from ui.implements.tabs.tabDb.tabDb_Manage import TabDb_Manage
from ui.implements.tabs.tabDb.tabDb_RemoveDuplicateScores import TabDb_RemoveDuplicateScores
class Ui_TabDbEntry(object): class Ui_TabDbEntry(object):
def setupUi(self, TabDbEntry): def setupUi(self, TabDbEntry):
@ -37,6 +38,9 @@ class Ui_TabDbEntry(object):
self.tab_chartInfoEditor = TabDb_ChartInfoEditor() self.tab_chartInfoEditor = TabDb_ChartInfoEditor()
self.tab_chartInfoEditor.setObjectName(u"tab_chartInfoEditor") self.tab_chartInfoEditor.setObjectName(u"tab_chartInfoEditor")
self.tabWidget.addTab(self.tab_chartInfoEditor, "") self.tabWidget.addTab(self.tab_chartInfoEditor, "")
self.tab_removeDuplicateScores = TabDb_RemoveDuplicateScores()
self.tab_removeDuplicateScores.setObjectName(u"tab_removeDuplicateScores")
self.tabWidget.addTab(self.tab_removeDuplicateScores, "")
self.verticalLayout.addWidget(self.tabWidget) self.verticalLayout.addWidget(self.tabWidget)
@ -52,6 +56,7 @@ class Ui_TabDbEntry(object):
def retranslateUi(self, TabDbEntry): def retranslateUi(self, TabDbEntry):
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_manage), QCoreApplication.translate("TabDbEntry", u"tab.manage", None)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_manage), QCoreApplication.translate("TabDbEntry", u"tab.manage", None))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_chartInfoEditor), QCoreApplication.translate("TabDbEntry", u"tab.chartInfoEditor", None)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_chartInfoEditor), QCoreApplication.translate("TabDbEntry", u"tab.chartInfoEditor", None))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_removeDuplicateScores), QCoreApplication.translate("TabDbEntry", u"tab.removeDuplicateScores", None))
pass pass
# retranslateUi # retranslateUi

View File

@ -329,6 +329,36 @@
</item> </item>
</layout> </layout>
</item> </item>
<item row="9" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>sourceCode</string>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QLabel" name="label_7">
<property name="text">
<string notr="true">&lt;a href=&quot;https://github.com/283375/AndrealImageGenerator&quot;&gt;283375/AndrealImageGenerator&lt;/a&gt;&lt;br&gt;(forked from &lt;a href=&quot;https://github.com/Awbugl/AndrealImageGenerator&quot;&gt;Awbugl/AndrealImageGenerator&lt;/a&gt;)</string>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
<item row="8" column="0" colspan="2">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout> </layout>
</widget> </widget>
<customwidgets> <customwidgets>

View File

@ -220,6 +220,22 @@ class Ui_TabTools_Andreal(object):
self.formLayout.setLayout(7, QFormLayout.SpanningRole, self.horizontalLayout_5) self.formLayout.setLayout(7, QFormLayout.SpanningRole, self.horizontalLayout_5)
self.label_4 = QLabel(TabTools_Andreal)
self.label_4.setObjectName(u"label_4")
self.formLayout.setWidget(9, QFormLayout.LabelRole, self.label_4)
self.label_7 = QLabel(TabTools_Andreal)
self.label_7.setObjectName(u"label_7")
self.label_7.setText(u"<a href=\"https://github.com/283375/AndrealImageGenerator\">283375/AndrealImageGenerator</a><br>(forked from <a href=\"https://github.com/Awbugl/AndrealImageGenerator\">Awbugl/AndrealImageGenerator</a>)")
self.label_7.setOpenExternalLinks(True)
self.formLayout.setWidget(9, QFormLayout.FieldRole, self.label_7)
self.verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
self.formLayout.setItem(8, QFormLayout.SpanningRole, self.verticalSpacer)
self.retranslateUi(TabTools_Andreal) self.retranslateUi(TabTools_Andreal)
self.imageFormat_jpgRadioButton.toggled.connect(self.jpgQualityHolderWidget.setEnabled) self.imageFormat_jpgRadioButton.toggled.connect(self.jpgQualityHolderWidget.setEnabled)
@ -241,6 +257,7 @@ class Ui_TabTools_Andreal(object):
self.exportJsonButton.setText(QCoreApplication.translate("TabTools_Andreal", u"exportJsonButton", None)) self.exportJsonButton.setText(QCoreApplication.translate("TabTools_Andreal", u"exportJsonButton", None))
self.generatePreviewButton.setText(QCoreApplication.translate("TabTools_Andreal", u"generatePreviewButton", None)) self.generatePreviewButton.setText(QCoreApplication.translate("TabTools_Andreal", u"generatePreviewButton", None))
self.generateImageButton.setText(QCoreApplication.translate("TabTools_Andreal", u"generateImageButton", None)) self.generateImageButton.setText(QCoreApplication.translate("TabTools_Andreal", u"generateImageButton", None))
self.label_4.setText(QCoreApplication.translate("TabTools_Andreal", u"sourceCode", None))
pass pass
# retranslateUi # retranslateUi

View File

@ -82,6 +82,9 @@
<property name="selectionMode"> <property name="selectionMode">
<enum>QAbstractItemView::NoSelection</enum> <enum>QAbstractItemView::NoSelection</enum>
</property> </property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="verticalScrollMode"> <property name="verticalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum> <enum>QAbstractItemView::ScrollPerPixel</enum>
</property> </property>
@ -325,7 +328,7 @@
<set>QAbstractItemView::NoEditTriggers</set> <set>QAbstractItemView::NoEditTriggers</set>
</property> </property>
<property name="selectionMode"> <property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum> <enum>QAbstractItemView::NoSelection</enum>
</property> </property>
<property name="selectionBehavior"> <property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum> <enum>QAbstractItemView::SelectRows</enum>

View File

@ -68,6 +68,7 @@ class Ui_TabTools_ChartRecommend(object):
self.chartsByConstant_modelView.setMinimumSize(QSize(150, 0)) self.chartsByConstant_modelView.setMinimumSize(QSize(150, 0))
self.chartsByConstant_modelView.setEditTriggers(QAbstractItemView.NoEditTriggers) self.chartsByConstant_modelView.setEditTriggers(QAbstractItemView.NoEditTriggers)
self.chartsByConstant_modelView.setSelectionMode(QAbstractItemView.NoSelection) self.chartsByConstant_modelView.setSelectionMode(QAbstractItemView.NoSelection)
self.chartsByConstant_modelView.setSelectionBehavior(QAbstractItemView.SelectRows)
self.chartsByConstant_modelView.setVerticalScrollMode(QAbstractItemView.ScrollPerPixel) self.chartsByConstant_modelView.setVerticalScrollMode(QAbstractItemView.ScrollPerPixel)
self.chartsByConstant_modelView.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel) self.chartsByConstant_modelView.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel)
@ -228,7 +229,7 @@ class Ui_TabTools_ChartRecommend(object):
self.chartsRecommendFromPlayRating_modelView.setSizePolicy(sizePolicy2) self.chartsRecommendFromPlayRating_modelView.setSizePolicy(sizePolicy2)
self.chartsRecommendFromPlayRating_modelView.setMinimumSize(QSize(200, 0)) self.chartsRecommendFromPlayRating_modelView.setMinimumSize(QSize(200, 0))
self.chartsRecommendFromPlayRating_modelView.setEditTriggers(QAbstractItemView.NoEditTriggers) self.chartsRecommendFromPlayRating_modelView.setEditTriggers(QAbstractItemView.NoEditTriggers)
self.chartsRecommendFromPlayRating_modelView.setSelectionMode(QAbstractItemView.SingleSelection) self.chartsRecommendFromPlayRating_modelView.setSelectionMode(QAbstractItemView.NoSelection)
self.chartsRecommendFromPlayRating_modelView.setSelectionBehavior(QAbstractItemView.SelectRows) self.chartsRecommendFromPlayRating_modelView.setSelectionBehavior(QAbstractItemView.SelectRows)
self.chartsRecommendFromPlayRating_modelView.setVerticalScrollMode(QAbstractItemView.ScrollPerPixel) self.chartsRecommendFromPlayRating_modelView.setVerticalScrollMode(QAbstractItemView.ScrollPerPixel)
self.chartsRecommendFromPlayRating_modelView.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel) self.chartsRecommendFromPlayRating_modelView.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel)

View File

@ -5,7 +5,6 @@ from typing import Any, Callable, Optional, overload
from arcaea_offline.calculate import calculate_score_range from arcaea_offline.calculate import calculate_score_range
from arcaea_offline.database import Database from arcaea_offline.database import Database
from arcaea_offline.models import Chart, Score from arcaea_offline.models import Chart, Score
from arcaea_offline_ocr.b30.shared import B30OcrResultItem
from arcaea_offline_ocr.device.common import DeviceOcrResult from arcaea_offline_ocr.device.common import DeviceOcrResult
from PIL import Image from PIL import Image
from PIL.ImageQt import ImageQt from PIL.ImageQt import ImageQt
@ -140,7 +139,11 @@ class OcrQueueModel(QAbstractListModel):
return True return True
else: else:
logger.warning( 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 return False
@ -150,6 +153,7 @@ class OcrQueueModel(QAbstractListModel):
@iccOption.setter @iccOption.setter
def iccOption(self, opt: IccOption): def iccOption(self, opt: IccOption):
logger.debug("ICC option changed to %s", opt)
self.__iccOption = opt self.__iccOption = opt
@overload @overload
@ -158,8 +162,7 @@ class OcrQueueModel(QAbstractListModel):
image: str, image: str,
runnable: OcrRunnable = None, runnable: OcrRunnable = None,
process_func: Callable[[Optional[str], QImage, Any], Score] = None, process_func: Callable[[Optional[str], QImage, Any], Score] = None,
): ): ...
...
@overload @overload
def addItem( def addItem(
@ -167,8 +170,7 @@ class OcrQueueModel(QAbstractListModel):
image: QImage, image: QImage,
runnable: OcrRunnable = None, runnable: OcrRunnable = None,
process_func: Callable[[Optional[str], QImage, Any], Score] = None, process_func: Callable[[Optional[str], QImage, Any], Score] = None,
): ): ...
...
def addItem( def addItem(
self, self,
@ -178,7 +180,7 @@ class OcrQueueModel(QAbstractListModel):
): ):
if isinstance(image, str): if isinstance(image, str):
if image in self.imagePaths or not QFileInfo(image).exists(): 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 return
imagePath = image imagePath = image
if self.iccOption == IccOption.TryFix: if self.iccOption == IccOption.TryFix:
@ -222,7 +224,7 @@ class OcrQueueModel(QAbstractListModel):
index = self.index(row, 0) index = self.index(row, 0)
imagePath: str = index.data(self.ImagePathRole) imagePath: str = index.data(self.ImagePathRole)
qImage: QImage = index.data(self.ImageQImageRole) 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) processOcrResultFunc = index.data(self.ProcessOcrResultFuncRole)
chart, scoreInsert = processOcrResultFunc(imagePath, qImage, result) chart, scoreInsert = processOcrResultFunc(imagePath, qImage, result)
@ -293,8 +295,8 @@ class OcrQueueModel(QAbstractListModel):
self.__items.pop(row) self.__items.pop(row)
self.endRemoveRows() self.endRemoveRows()
return return
except Exception as e: except Exception:
logger.exception(f"Error accepting {repr(item)}") logger.exception("Error accepting %r", item)
return return
def acceptItems(self, __rows: list[int], ignoreValidate: bool = False): def acceptItems(self, __rows: list[int], ignoreValidate: bool = False):
@ -343,13 +345,11 @@ class OcrQueueTableProxyModel(QAbstractTableModel):
def retranslateHeaders(self): def retranslateHeaders(self):
self.__horizontalHeaders = [ self.__horizontalHeaders = [
# fmt: off
QCoreApplication.translate("OcrTableModel", "horizontalHeader.title.select"), QCoreApplication.translate("OcrTableModel", "horizontalHeader.title.select"),
QCoreApplication.translate("OcrTableModel", "horizontalHeader.title.imagePreview"), QCoreApplication.translate("OcrTableModel", "horizontalHeader.title.imagePreview"),
QCoreApplication.translate("OcrTableModel", "horizontalHeader.title.chart"), QCoreApplication.translate("OcrTableModel", "horizontalHeader.title.chart"),
QCoreApplication.translate("OcrTableModel", "horizontalHeader.title.score"), QCoreApplication.translate("OcrTableModel", "horizontalHeader.title.score"),
# fmt: on ] # fmt: skip
]
def sourceModel(self) -> OcrQueueModel: def sourceModel(self) -> OcrQueueModel:
return self.__sourceModel return self.__sourceModel

View File

@ -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)

View File

@ -7,7 +7,7 @@ from typing import Literal, Optional, overload
from arcaea_offline.models import Chart, Difficulty, Song from arcaea_offline.models import Chart, Difficulty, Song
from PySide6.QtCore import QFile from PySide6.QtCore import QFile
from .singleton import Singleton from core.singleton import Singleton
TPartnerModifier = dict[str, Literal[0, 1, 2]] TPartnerModifier = dict[str, Literal[0, 1, 2]]
@ -48,14 +48,12 @@ class Data(metaclass=Singleton):
return self.dataPath / "Arcaea" return self.dataPath / "Arcaea"
@overload @overload
def getJacketPath(self, chart: Chart, /) -> Path | None: def getJacketPath(self, chart: Chart, /) -> Path | None: ...
...
@overload @overload
def getJacketPath( def getJacketPath(
self, song: Song, difficulty: Optional[Difficulty] = None, / self, song: Song, difficulty: Optional[Difficulty] = None, /
) -> Path | None: ) -> Path | None: ...
...
def getJacketPath(self, *args) -> Path | None: def getJacketPath(self, *args) -> Path | None:
if isinstance(args[0], Chart): if isinstance(args[0], Chart):

View File

@ -82,6 +82,7 @@ class ChartDelegate(TextSegmentDelegate):
QColor("#809955"), QColor("#809955"),
QColor("#702d60"), QColor("#702d60"),
QColor("#710f25"), QColor("#710f25"),
QColor("#8b77a4"),
] ]
ChartInvalidBackgroundColor = QColor("#e6a23c") ChartInvalidBackgroundColor = QColor("#e6a23c")

View File

@ -1,6 +1,5 @@
from arcaea_offline.models import Chart, Score, ScoreBest from arcaea_offline.models import Chart, Score, ScoreBest
from PySide6.QtCore import QCoreApplication, QModelIndex, QSortFilterProxyModel, Qt from PySide6.QtCore import QCoreApplication, QModelIndex, QSortFilterProxyModel, Qt
from sqlalchemy import select
from .base import DbTableModel from .base import DbTableModel
@ -17,51 +16,44 @@ class DbB30TableModel(DbTableModel):
def retranslateHeaders(self): def retranslateHeaders(self):
self._horizontalHeaders = [ self._horizontalHeaders = [
# fmt: off QCoreApplication.translate("DbB30TableModel", "horizontalHeader.id"),
QCoreApplication.translate("DB30TableModel", "horizontalHeader.id"), QCoreApplication.translate("DbB30TableModel", "horizontalHeader.chart"),
QCoreApplication.translate("DB30TableModel", "horizontalHeader.chart"), QCoreApplication.translate("DbB30TableModel", "horizontalHeader.score"),
QCoreApplication.translate("DB30TableModel", "horizontalHeader.score"), QCoreApplication.translate("DbB30TableModel", "horizontalHeader.potential"),
QCoreApplication.translate("DB30TableModel", "horizontalHeader.potential"), ] # fmt: skip
# fmt: on
]
def syncDb(self): def syncDb(self):
self.beginResetModel()
self.beginRemoveRows(QModelIndex(), 0, self.rowCount())
self.__items.clear() self.__items.clear()
self.endRemoveRows()
self.endResetModel()
with self._db.sessionmaker() as session: with self._db.sessionmaker() as session:
results = list( results = (
session.scalars( session.query(ScoreBest, Chart)
select(ScoreBest).order_by(ScoreBest.potential.desc()).limit(40) .join(
Chart,
(ScoreBest.song_id == Chart.song_id)
& (ScoreBest.rating_class == Chart.rating_class),
) )
.order_by(ScoreBest.potential.desc())
.limit(50)
.all()
) )
songIds = [r.id for r in results] self.beginInsertRows(QModelIndex(), 0, len(results) - 1)
ptts = [r.potential for r in results] for scoreBest, chart in results:
self.__items.append(
for scoreId, ptt in zip(songIds, ptts): {
score = self._db.get_score(scoreId) self.IdRole: scoreBest.id,
chart = self._db.get_chart(score.song_id, score.rating_class) self.ChartRole: chart,
self.ScoreRole: scoreBest,
self.beginInsertRows(QModelIndex(), self.rowCount(), self.rowCount()) self.PttRole: scoreBest.potential,
self.__items.append( }
{ )
self.IdRole: score.id,
self.ChartRole: chart,
self.ScoreRole: score,
self.PttRole: ptt,
}
)
self.endInsertRows() self.endInsertRows()
# trigger view update
topLeft = self.index(0, 0)
bottomRight = self.index(self.rowCount() - 1, self.columnCount() - 1)
self.dataChanged.emit(
topLeft,
bottomRight,
[Qt.ItemDataRole.DisplayRole, self.IdRole, self.ChartRole, self.ScoreRole],
)
def rowCount(self, *args): def rowCount(self, *args):
return len(self.__items) return len(self.__items)
@ -117,30 +109,35 @@ class DbB30TableSortFilterProxyModel(QSortFilterProxyModel):
return super().headerData(section, orientation, role) return super().headerData(section, orientation, role)
return section + 1 return section + 1
def lessThan(self, source_left, source_right) -> bool: def lessThan(self, sourceLeft: QModelIndex, sourceRight: QModelIndex) -> bool:
if source_left.column() != source_right.column(): if sourceLeft.column() != sourceRight.column():
return return
column = source_left.column() column = sourceLeft.column()
if column == 0: if column == 0:
return source_left.data(DbB30TableModel.IdRole) < source_right.data( return sourceLeft.data(DbB30TableModel.IdRole) < sourceRight.data(
DbB30TableModel.IdRole DbB30TableModel.IdRole
) )
elif column == 2: elif column == 2:
score_left = source_left.data(DbB30TableModel.ScoreRole) scoreLeft = sourceLeft.data(DbB30TableModel.ScoreRole)
score_right = source_right.data(DbB30TableModel.ScoreRole) scoreRight = sourceRight.data(DbB30TableModel.ScoreRole)
if isinstance(score_left, Score) and isinstance(score_right, Score): if isinstance(scoreLeft, Score) and isinstance(scoreRight, Score):
if self.sortRole() == self.Sort_C2_ScoreRole: if self.sortRole() == self.Sort_C2_ScoreRole:
return score_left.score < score_right.score return scoreLeft.score < scoreRight.score
elif self.sortRole() == self.Sort_C2_TimeRole: elif self.sortRole() == self.Sort_C2_TimeRole:
if score_left.date and score_right.date: if scoreLeft.date and scoreRight.date:
return score_left.date < score_right.date return scoreLeft.date < scoreRight.date
elif score_left.date: elif scoreLeft.date:
return False return False
else: else:
return True return True
elif column == 3: elif column == 3:
return source_left.data(DbB30TableModel.PttRole) < source_right.data( pttLeft = sourceLeft.data(DbB30TableModel.PttRole)
DbB30TableModel.PttRole pttRight = sourceRight.data(DbB30TableModel.PttRole)
) if pttLeft and pttRight:
return super().lessThan(source_left, source_right) return pttLeft < pttRight
elif pttLeft:
return False
else:
return True
return super().lessThan(sourceLeft, sourceRight)

View File

@ -24,13 +24,11 @@ class DbScoreTableModel(DbTableModel):
def retranslateHeaders(self): def retranslateHeaders(self):
self._horizontalHeaders = [ self._horizontalHeaders = [
# fmt: off
QCoreApplication.translate("DbScoreTableModel", "horizontalHeader.id"), QCoreApplication.translate("DbScoreTableModel", "horizontalHeader.id"),
QCoreApplication.translate("DbScoreTableModel", "horizontalHeader.chart"), QCoreApplication.translate("DbScoreTableModel", "horizontalHeader.chart"),
QCoreApplication.translate("DbScoreTableModel", "horizontalHeader.score"), QCoreApplication.translate("DbScoreTableModel", "horizontalHeader.score"),
QCoreApplication.translate("DbScoreTableModel", "horizontalHeader.potential"), QCoreApplication.translate("DbScoreTableModel", "horizontalHeader.potential"),
# fmt: on ] # fmt: skip
]
def syncDb(self): def syncDb(self):
self.beginResetModel() self.beginResetModel()
@ -154,7 +152,7 @@ class DbScoreTableModel(DbTableModel):
self.syncDb() self.syncDb()
return True return True
except Exception: except Exception:
logger.exception(f"Table[Score]: Cannot remove row {row}") logger.exception("Table[Score]: Cannot remove row %s", row)
return False return False
def removeRow(self, row: int, parent=...): def removeRow(self, row: int, parent=...):

View File

@ -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)

View File

@ -6,10 +6,10 @@ from arcaea_offline_ocr.b30.chieri.v4.ocr import ChieriBotV4Ocr
from arcaea_offline_ocr.b30.shared import B30OcrResultItem from arcaea_offline_ocr.b30.shared import B30OcrResultItem
from PySide6.QtGui import QImage from PySide6.QtGui import QImage
logger = logging.getLogger(__name__)
from ui.extends.components.ocrQueue import OcrRunnable from ui.extends.components.ocrQueue import OcrRunnable
logger = logging.getLogger(__name__)
class ChieriV4OcrRunnable(OcrRunnable): class ChieriV4OcrRunnable(OcrRunnable):
def __init__(self, ocr: ChieriBotV4Ocr, component): def __init__(self, ocr: ChieriBotV4Ocr, component):

View File

@ -19,6 +19,7 @@ from arcaea_offline_ocr.phash_db import ImagePhashDatabase
from arcaea_offline_ocr.utils import imread_unicode from arcaea_offline_ocr.utils import imread_unicode
from PySide6.QtCore import QDateTime, QFileInfo from PySide6.QtCore import QDateTime, QFileInfo
from core.settings import SettingsKeys, SettingsValues, settings
from ui.extends.components.ocrQueue import OcrRunnable from ui.extends.components.ocrQueue import OcrRunnable
from ui.extends.shared.data import Data 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"): if exifImage.has_exif and exifImage.get("datetime_original"):
datetimeStr = exifImage.get("datetime_original") datetimeStr = exifImage.get("datetime_original")
datetime = QDateTime.fromString(datetimeStr, "yyyy:MM:dd hh:mm:ss") datetime = QDateTime.fromString(datetimeStr, "yyyy:MM:dd hh:mm:ss")
if not isinstance(datetime, QDateTime): 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 return datetime

View File

@ -41,7 +41,7 @@ class AndrealExecuteRunnable(QRunnable):
self.signals.completed.emit(self.jsonPath, imageBytes) self.signals.completed.emit(self.jsonPath, imageBytes)
except Exception as e: except Exception as e:
imageBytes = None imageBytes = None
logger.exception(f"{self.__class__.__name__} error") logger.exception("%s error", self.__class__.__name__)
self.signals.error.emit(self.jsonPath, str(e)) self.signals.error.emit(self.jsonPath, str(e))
finally: finally:
os.unlink(self.jsonPath) os.unlink(self.jsonPath)
@ -84,7 +84,10 @@ class AndrealHelper(QObject):
def request(self, jsonPath: str, arguments: list[str]): def request(self, jsonPath: str, arguments: list[str]):
logger.debug( 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 = AndrealExecuteRunnable(self.andrealExecutable, jsonPath, arguments)
runnable.signals.error.connect(self.error) runnable.signals.error.connect(self.error)

View File

@ -2,9 +2,9 @@ from PySide6.QtCore import QDir, QFileInfo, Qt, Signal, Slot
from PySide6.QtGui import QDragEnterEvent, QDragLeaveEvent, QDropEvent from PySide6.QtGui import QDragEnterEvent, QDragLeaveEvent, QDropEvent
from PySide6.QtWidgets import QFileDialog, QWidget from PySide6.QtWidgets import QFileDialog, QWidget
from core.settings import settings
from ui.designer.components.fileSelector_ui import Ui_FileSelector from ui.designer.components.fileSelector_ui import Ui_FileSelector
from ui.extends.shared.language import LanguageChangeEventFilter from ui.extends.shared.language import LanguageChangeEventFilter
from ui.extends.shared.settings import Settings
class FileSelector(Ui_FileSelector, QWidget): class FileSelector(Ui_FileSelector, QWidget):
@ -122,13 +122,13 @@ class FileSelector(Ui_FileSelector, QWidget):
if self.__selectedFiles: if self.__selectedFiles:
return return
if value := Settings().value(self.settingsKey): if value := settings.value(self.settingsKey):
self.selectFile(value) self.selectFile(value)
Settings().updated.connect(self.settingsUpdated) settings.updated.connect(self.settingsUpdated)
def disconnectSettings(self): def disconnectSettings(self):
Settings().updated.disconnect(self.settingsUpdated) settings.updated.disconnect(self.settingsUpdated)
self.settingsKey = None self.settingsKey = None
def settingsUpdated(self, key: str): def settingsUpdated(self, key: str):
@ -139,4 +139,4 @@ class FileSelector(Ui_FileSelector, QWidget):
if self.__selectedFiles: if self.__selectedFiles:
return return
self.selectFile(Settings().value(self.settingsKey)) self.selectFile(settings.value(self.settingsKey))

View File

@ -2,7 +2,7 @@ from typing import Optional
from PySide6.QtCore import Qt, QTimer, Slot from PySide6.QtCore import Qt, QTimer, Slot
from PySide6.QtGui import QColor, QPalette 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.designer.components.ocrQueue_ui import Ui_OcrQueue
from ui.extends.components.ocrQueue import ( from ui.extends.components.ocrQueue import (
@ -13,6 +13,7 @@ from ui.extends.components.ocrQueue import (
OcrScoreDelegate, OcrScoreDelegate,
) )
from ui.extends.shared.language import LanguageChangeEventFilter from ui.extends.shared.language import LanguageChangeEventFilter
from ui.implements.components.ocrQueueOptionsDialog import OcrQueueOptionsDialog
class OcrQueue(Ui_OcrQueue, QWidget): class OcrQueue(Ui_OcrQueue, QWidget):
@ -26,6 +27,9 @@ class OcrQueue(Ui_OcrQueue, QWidget):
self.__model: Optional[OcrQueueModel] = None self.__model: Optional[OcrQueueModel] = None
self.__tableProxyModel: Optional[OcrQueueTableProxyModel] = None self.__tableProxyModel: Optional[OcrQueueTableProxyModel] = None
self.optionsDialog = OcrQueueOptionsDialog(self)
self.optionsDialog.iccOptionsChanged.connect(self.setIccOption)
self.__firstResizeDone = False self.__firstResizeDone = False
self.resizeTimer = QTimer(self) self.resizeTimer = QTimer(self)
self.resizeTimer.timeout.connect(self.tableView.resizeRowsToContents) self.resizeTimer.timeout.connect(self.tableView.resizeRowsToContents)
@ -41,13 +45,6 @@ class OcrQueue(Ui_OcrQueue, QWidget):
tableViewPalette.setColor(QPalette.ColorRole.Highlight, highlightColor) tableViewPalette.setColor(QPalette.ColorRole.Highlight, highlightColor)
self.tableView.setPalette(tableViewPalette) 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 = QTimer(self)
self.statusLabelClearTimer.setSingleShot(True) self.statusLabelClearTimer.setSingleShot(True)
self.statusLabelClearTimer.timeout.connect(self.clearStatusMessage) self.statusLabelClearTimer.timeout.connect(self.clearStatusMessage)
@ -93,9 +90,10 @@ class OcrQueue(Ui_OcrQueue, QWidget):
self.ocr_acceptAllButton.setEnabled(__bool) self.ocr_acceptAllButton.setEnabled(__bool)
self.ocr_ignoreValidateCheckBox.setEnabled(__bool) self.ocr_ignoreValidateCheckBox.setEnabled(__bool)
def updateIccOption(self): @Slot(int)
def setIccOption(self, option):
if self.model(): if self.model():
self.model().iccOption = self.iccOptionButtonGroup.checkedId() self.model().iccOption = option
def showStatusMessage(self, message: str): def showStatusMessage(self, message: str):
self.statusLabel.setText(message) self.statusLabel.setText(message)
@ -131,6 +129,10 @@ class OcrQueue(Ui_OcrQueue, QWidget):
def modelReseted(self): def modelReseted(self):
self.progressBar.setMaximum(0) self.progressBar.setMaximum(0)
@Slot()
def on_optionsDialogButton_clicked(self):
self.optionsDialog.exec()
@Slot() @Slot()
def on_ocr_removeSelectedButton_clicked(self): def on_ocr_removeSelectedButton_clicked(self):
if self.model(): if self.model():

View 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)

View File

@ -39,14 +39,12 @@ class PlayRatingCalculator(QWidget):
return None return None
score = self.arcaeaScoreLineEdit.score() score = self.arcaeaScoreLineEdit.score()
if score is None: return None if score is None else calculate_play_rating(self.constant, score)
return None
return calculate_play_rating(self.constant, score)
def updateResultLabel(self): def updateResultLabel(self):
result = self.result result = self.result
self.resultLabel.setText(str(result) if result is not None else "...") self.resultLabel.setText(str(round(result, 3)) if result is not None else "...")
self.resultLabel.setToolTip(str(result))
def on_copyButton_clicked(self): def on_copyButton_clicked(self):
result = self.result result = self.result
@ -68,6 +66,7 @@ class PlayRatingCalculator(QWidget):
self.resultLabel.setSizePolicy( self.resultLabel.setSizePolicy(
QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred
) )
self.resultLabel.setMinimumWidth(100)
self.horizontalLayout.addWidget(self.resultLabel) self.horizontalLayout.addWidget(self.resultLabel)
self.horizontalSpacer = QSpacerItem( self.horizontalSpacer = QSpacerItem(

View File

@ -2,7 +2,7 @@ from PySide6.QtCore import Slot
from PySide6.QtGui import QColor from PySide6.QtGui import QColor
from PySide6.QtWidgets import QGraphicsColorizeEffect, QRadioButton from PySide6.QtWidgets import QGraphicsColorizeEffect, QRadioButton
from ui.extends.shared.color import mix_color from core.color import mixColor
STYLESHEET = """ STYLESHEET = """
QRadioButton {{ QRadioButton {{
@ -40,7 +40,7 @@ class RatingClassRadioButton(QRadioButton):
def setColors(self, dark_color: QColor, text_color: QColor): def setColors(self, dark_color: QColor, text_color: QColor):
self._dark_color = dark_color self._dark_color = dark_color
self._text_color = text_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() self.updateEffects()
def isColorsSet(self) -> bool: def isColorsSet(self) -> bool:

View File

@ -1,3 +1,4 @@
import logging
from typing import Type from typing import Type
from PySide6.QtCore import Signal from PySide6.QtCore import Signal
@ -6,6 +7,8 @@ from PySide6.QtWidgets import QHBoxLayout, QSizePolicy, QVBoxLayout, QWidget
from ui.implements.components.ratingClassRadioButton import RatingClassRadioButton from ui.implements.components.ratingClassRadioButton import RatingClassRadioButton
logger = logging.getLogger(__name__)
class RatingClassSelector(QWidget): class RatingClassSelector(QWidget):
valueChanged = Signal() valueChanged = Signal()
@ -41,16 +44,30 @@ class RatingClassSelector(QWidget):
self.bydButton.setAutoExclusive(False) self.bydButton.setAutoExclusive(False)
self.preferredLayout.addWidget(self.bydButton) self.preferredLayout.addWidget(self.bydButton)
self.buttons = [self.pstButton, self.prsButton, self.ftrButton, self.bydButton] self.etrButton = RatingClassRadioButton(self)
self.etrButton.setObjectName("etrButton")
self.etrButton.setText("ETERNAL")
self.etrButton.setAutoExclusive(False)
self.preferredLayout.addWidget(self.etrButton)
self.buttons = [
self.pstButton,
self.prsButton,
self.ftrButton,
self.bydButton,
self.etrButton,
]
self.pstButton.setColors(QColor("#399bb2"), QColor("#f0f8fa")) self.pstButton.setColors(QColor("#399bb2"), QColor("#f0f8fa"))
self.prsButton.setColors(QColor("#809955"), QColor("#f7f9f4")) self.prsButton.setColors(QColor("#809955"), QColor("#f7f9f4"))
self.ftrButton.setColors(QColor("#702d60"), QColor("#f7ebf4")) self.ftrButton.setColors(QColor("#702d60"), QColor("#f7ebf4"))
self.bydButton.setColors(QColor("#710f25"), QColor("#f9ced8")) self.bydButton.setColors(QColor("#710f25"), QColor("#f9ced8"))
self.etrButton.setColors(QColor("#4f2c7a"), QColor("#e4daf1"))
self.pstButton.clicked.connect(self.select) self.pstButton.clicked.connect(self.select)
self.prsButton.clicked.connect(self.select) self.prsButton.clicked.connect(self.select)
self.ftrButton.clicked.connect(self.select) self.ftrButton.clicked.connect(self.select)
self.bydButton.clicked.connect(self.select) self.bydButton.clicked.connect(self.select)
self.etrButton.clicked.connect(self.select)
self.reset() self.reset()
self.setButtonsEnabled([]) self.setButtonsEnabled([])
@ -106,9 +123,10 @@ class RatingClassSelector(QWidget):
if ratingClass is None or isinstance(ratingClass, bool): if ratingClass is None or isinstance(ratingClass, bool):
button = self.sender() button = self.sender()
elif ratingClass in range(4): elif ratingClass in range(len(self.buttons)):
button = self.buttons[ratingClass] button = self.buttons[ratingClass]
else: else:
logger.debug("Cannot select ratingClass=%s, condition check failed", ratingClass)
return return
if not button.isEnabled(): if not button.isEnabled():

View File

@ -61,30 +61,22 @@ class ScoreEditor(Ui_ScoreEditor, QWidget):
VALIDATION_ITEMS_TEXT = [ VALIDATION_ITEMS_TEXT = [
[ [
# fmt: off
lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.chartIncomplete.title"), lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.chartIncomplete.title"),
lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.chartIncomplete.text"), lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.chartIncomplete.text"),
# fmt: on
], ],
[ [
# fmt: off
lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.scoreMismatch.title"), lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.scoreMismatch.title"),
lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.scoreMismatch.text"), lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.scoreMismatch.text"),
# fmt: on
], ],
[ [
# fmt: off
lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.emptyScore.title"), lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.emptyScore.title"),
lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.emptyScore.text"), lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.emptyScore.text"),
# fmt: on
], ],
[ [
# fmt: off
lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.scoreIncompleteForValidate.title"), lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.scoreIncompleteForValidate.title"),
lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.scoreIncompleteForValidate.text"), lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.scoreIncompleteForValidate.text"),
# fmt: on,
], ],
] ] # fmt: skip
def __init__(self, parent=None): def __init__(self, parent=None):
super().__init__(parent) super().__init__(parent)
@ -208,20 +200,16 @@ class ScoreEditor(Ui_ScoreEditor, QWidget):
if validate & ScoreValidateResult.ChartNotSet: if validate & ScoreValidateResult.ChartNotSet:
self.__triggerMessageBox( self.__triggerMessageBox(
"critical", "critical",
# fmt: off
QCoreApplication.translate("ScoreEditor", "confirmDialog.chartNotSet.title"), QCoreApplication.translate("ScoreEditor", "confirmDialog.chartNotSet.title"),
QCoreApplication.translate("ScoreEditor", "confirmDialog.chartNotSet.text"), QCoreApplication.translate("ScoreEditor", "confirmDialog.chartNotSet.text"),
# fmt: on ) # fmt: skip
)
return False return False
if validate & ScoreValidateResult.ScoreIncomplete: if validate & ScoreValidateResult.ScoreIncomplete:
self.__triggerMessageBox( self.__triggerMessageBox(
"critical", "critical",
# fmt: off
QCoreApplication.translate("ScoreEditor", "confirmDialog.scoreIncomplete.title"), QCoreApplication.translate("ScoreEditor", "confirmDialog.scoreIncomplete.title"),
QCoreApplication.translate("ScoreEditor", "confirmDialog.scoreIncomplete.text"), QCoreApplication.translate("ScoreEditor", "confirmDialog.scoreIncomplete.text"),
# fmt: on ) # fmt: skip
)
return False return False
# since validate may have multiple results # since validate may have multiple results
@ -347,10 +335,8 @@ class ScoreEditor(Ui_ScoreEditor, QWidget):
) )
if validate & ScoreValidateResult.ScoreIncompleteForValidate: if validate & ScoreValidateResult.ScoreIncompleteForValidate:
texts.append( texts.append(
# fmt: off
QCoreApplication.translate("ScoreEditor", "validate.scoreIncompleteForValidate") QCoreApplication.translate("ScoreEditor", "validate.scoreIncompleteForValidate")
# fmt: on ) # fmt: skip
)
if not texts: if not texts:
texts.append( texts.append(

View File

@ -192,7 +192,7 @@ class SongIdSelector(Ui_SongIdSelector, QWidget):
self.fillSongIdComboBox() self.fillSongIdComboBox()
return True return True
else: else:
logger.warning(f'Attempting to select an unknown pack "{packId}"') logger.warning("Attempting to select an unknown pack [%s]", packId)
return False return False
def selectSongId(self, songId: str) -> bool: def selectSongId(self, songId: str) -> bool:
@ -202,7 +202,8 @@ class SongIdSelector(Ui_SongIdSelector, QWidget):
return True return True
else: else:
logger.warning( 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 return False

View File

@ -1,6 +1,7 @@
from PySide6.QtCore import QCoreApplication from PySide6.QtCore import QCoreApplication
from PySide6.QtWidgets import QLabel, QPushButton from PySide6.QtWidgets import QLabel, QPushButton
from core.settings import SettingsKeys, settings
from ui.implements.components.fileSelector import FileSelector from ui.implements.components.fileSelector import FileSelector
from ui.implements.settings.settingsBaseWidget import SettingsBaseWidget from ui.implements.settings.settingsBaseWidget import SettingsBaseWidget
@ -14,8 +15,8 @@ class SettingsAndreal(SettingsBaseWidget):
self.andrealFolderValueWidget.setMode( self.andrealFolderValueWidget.setMode(
self.andrealFolderValueWidget.getExistingDirectory self.andrealFolderValueWidget.getExistingDirectory
) )
if self.settings.andrealFolder(): if andrealFolder := settings.stringValue(SettingsKeys.Andreal.Folder):
self.andrealFolderValueWidget.selectFile(self.settings.andrealFolder()) self.andrealFolderValueWidget.selectFile(andrealFolder)
self.andrealFolderValueWidget.filesSelected.connect(self.setAndrealFolder) self.andrealFolderValueWidget.filesSelected.connect(self.setAndrealFolder)
self.andrealFolderResetButton.clicked.connect(self.resetAndrealFolder) self.andrealFolderResetButton.clicked.connect(self.resetAndrealFolder)
self.insertItem( self.insertItem(
@ -25,10 +26,8 @@ class SettingsAndreal(SettingsBaseWidget):
self.andrealFolderResetButton, self.andrealFolderResetButton,
) )
if self.settings.andrealExecutable(): if andrealExecutable := settings.stringValue(SettingsKeys.Andreal.Executable):
self.andrealExecutableValueWidget.selectFile( self.andrealExecutableValueWidget.selectFile(andrealExecutable)
self.settings.andrealExecutable()
)
self.andrealExecutableValueWidget.filesSelected.connect( self.andrealExecutableValueWidget.filesSelected.connect(
self.setAndrealExecutable self.setAndrealExecutable
) )
@ -44,21 +43,21 @@ class SettingsAndreal(SettingsBaseWidget):
selectedFile = self.andrealFolderValueWidget.selectedFiles() selectedFile = self.andrealFolderValueWidget.selectedFiles()
if selectedFile and selectedFile[0]: if selectedFile and selectedFile[0]:
file = selectedFile[0] file = selectedFile[0]
self.settings.setAndrealFolder(file) settings.setValue(SettingsKeys.Andreal.Folder, file)
def resetAndrealFolder(self): def resetAndrealFolder(self):
self.andrealFolderValueWidget.reset() self.andrealFolderValueWidget.reset()
self.settings.resetAndrealFolder() settings.setValue(SettingsKeys.Andreal.Folder, None)
def setAndrealExecutable(self): def setAndrealExecutable(self):
selectedFile = self.andrealExecutableValueWidget.selectedFiles() selectedFile = self.andrealExecutableValueWidget.selectedFiles()
if selectedFile and selectedFile[0]: if selectedFile and selectedFile[0]:
file = selectedFile[0] file = selectedFile[0]
self.settings.setAndrealExecutable(file) settings.setValue(SettingsKeys.Andreal.Executable, file)
def resetAndrealExecutable(self): def resetAndrealExecutable(self):
self.andrealExecutableValueWidget.reset() self.andrealExecutableValueWidget.reset()
self.settings.resetAndrealExecutable() settings.setValue(SettingsKeys.Andreal.Executable, None)
def setupUi(self, *args): def setupUi(self, *args):
self.andrealFolderLabel = QLabel(self) self.andrealFolderLabel = QLabel(self)

View File

@ -1,15 +1,15 @@
from PySide6.QtCore import Qt from PySide6.QtCore import Qt
from PySide6.QtWidgets import QLabel, QPushButton, QWidget from PySide6.QtWidgets import QLabel, QPushButton, QWidget
from core.settings import settings
from ui.designer.settings.settingsBaseWidget_ui import Ui_SettingsBaseWidget from ui.designer.settings.settingsBaseWidget_ui import Ui_SettingsBaseWidget
from ui.extends.shared.language import LanguageChangeEventFilter from ui.extends.shared.language import LanguageChangeEventFilter
from ui.extends.shared.settings import Settings
class SettingsBaseWidget(Ui_SettingsBaseWidget, QWidget): class SettingsBaseWidget(Ui_SettingsBaseWidget, QWidget):
def __init__(self, parent=None): def __init__(self, parent=None):
super().__init__(parent) super().__init__(parent)
self.settings = Settings() self.settings = settings
self.languageChangeEventFilter = LanguageChangeEventFilter(self) self.languageChangeEventFilter = LanguageChangeEventFilter(self)
self.installEventFilter(self.languageChangeEventFilter) self.installEventFilter(self.languageChangeEventFilter)

View File

@ -1,6 +1,4 @@
import sys from PySide6.QtCore import QCoreApplication, QDir, QLocale
from PySide6.QtCore import QCoreApplication, QDir, QLocale, QProcess
from PySide6.QtWidgets import ( from PySide6.QtWidgets import (
QApplication, QApplication,
QCheckBox, QCheckBox,
@ -10,8 +8,8 @@ from PySide6.QtWidgets import (
QPushButton, QPushButton,
) )
from core.settings import SettingsKeys, settings
from ui.extends.shared.language import changeAppLanguage, localeToCode, localeToFullName 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 from ui.implements.settings.settingsBaseWidget import SettingsBaseWidget
@ -33,8 +31,8 @@ class SettingsGeneral(SettingsBaseWidget):
self.languageFollowSystemCheckBox.toggled.connect( self.languageFollowSystemCheckBox.toggled.connect(
self.changeLanguageFollowSystem self.changeLanguageFollowSystem
) )
if self.settings.language(): if language := settings.stringValue(SettingsKeys.General.Language):
locale = QLocale(self.settings.language()) locale = QLocale(language)
index = self.languageValueWidget.findData(locale) index = self.languageValueWidget.findData(locale)
if index > -1: if index > -1:
self.languageValueWidget.setCurrentIndex(index) self.languageValueWidget.setCurrentIndex(index)
@ -51,7 +49,7 @@ class SettingsGeneral(SettingsBaseWidget):
self.insertItem( self.insertItem(
"dbUrl", "dbUrl",
self.dbUrlLabel, self.dbUrlLabel,
QLabel(self.settings.databaseUrl()), QLabel(settings.stringValue(SettingsKeys.General.DatabaseUrl)),
self.dbUrlResetButton, self.dbUrlResetButton,
) )
@ -59,13 +57,13 @@ class SettingsGeneral(SettingsBaseWidget):
locale = self.languageValueWidget.currentData() locale = self.languageValueWidget.currentData()
if locale: if locale:
changeAppLanguage(locale) changeAppLanguage(locale)
self.settings.setLanguage(localeToCode(locale)) settings.setValue(SettingsKeys.General.Language, localeToCode(locale))
def changeLanguageFollowSystem(self): def changeLanguageFollowSystem(self):
followSystem = self.languageFollowSystemCheckBox.isChecked() followSystem = self.languageFollowSystemCheckBox.isChecked()
self.languageValueWidget.setCurrentIndex(-1) self.languageValueWidget.setCurrentIndex(-1)
if followSystem: if followSystem:
self.settings.remove(LANGUAGE) settings.remove(SettingsKeys.General.Language)
changeAppLanguage(QLocale.system()) changeAppLanguage(QLocale.system())
self.languageValueWidget.setEnabled(False) self.languageValueWidget.setEnabled(False)
else: else:
@ -80,7 +78,7 @@ class SettingsGeneral(SettingsBaseWidget):
QMessageBox.StandardButton.No, QMessageBox.StandardButton.No,
) )
if userConfirm == QMessageBox.StandardButton.Yes: if userConfirm == QMessageBox.StandardButton.Yes:
self.settings.remove(DATABASE_URL) settings.remove(SettingsKeys.General.DatabaseUrl)
QApplication.instance().quit() QApplication.instance().quit()
def setupUi(self, *args): def setupUi(self, *args):
@ -99,10 +97,10 @@ class SettingsGeneral(SettingsBaseWidget):
# fmt: off # fmt: off
self.setTitle(QCoreApplication.translate("Settings", "general.title")) self.setTitle(QCoreApplication.translate("Settings", "general.title"))
self.languageLabel.setText(QCoreApplication.translate("Settings", "general.language.label")) self.languageLabel.setText(QCoreApplication.translate("Settings", "general.language.label"))
self.languageFollowSystemCheckBox.setText(QCoreApplication.translate("Settings", "general.language.followSystem")) self.languageFollowSystemCheckBox.setText(QCoreApplication.translate("Settings", "general.language.followSystem"))
self.dbUrlLabel.setText(QCoreApplication.translate("Settings", "general.dbUrl.label")) self.dbUrlLabel.setText(QCoreApplication.translate("Settings", "general.dbUrl.label"))
self.dbUrlResetButton.setText(QCoreApplication.translate("Settings", "resetButton")) self.dbUrlResetButton.setText(QCoreApplication.translate("Settings", "resetButton"))
# fmt: on # fmt: on

View File

@ -1,6 +1,7 @@
from PySide6.QtCore import QCoreApplication from PySide6.QtCore import QCoreApplication
from PySide6.QtWidgets import QLabel, QPushButton from PySide6.QtWidgets import QLabel, QPushButton
from core.settings import SettingsKeys, settings
from ui.implements.components.fileSelector import FileSelector from ui.implements.components.fileSelector import FileSelector
from ui.implements.settings.settingsBaseWidget import SettingsBaseWidget from ui.implements.settings.settingsBaseWidget import SettingsBaseWidget
@ -11,8 +12,8 @@ class SettingsOcr(SettingsBaseWidget):
self.setupUi(self) self.setupUi(self)
if self.settings.knnModelFile(): if knnModelFile := settings.stringValue(SettingsKeys.Ocr.KnnModelFile):
self.knnModelFileValueWidget.selectFile(self.settings.knnModelFile()) self.knnModelFileValueWidget.selectFile(knnModelFile)
self.knnModelFileValueWidget.filesSelected.connect(self.setKnnModelFile) self.knnModelFileValueWidget.filesSelected.connect(self.setKnnModelFile)
self.knnModelFileResetButton.clicked.connect(self.resetKnnModelFile) self.knnModelFileResetButton.clicked.connect(self.resetKnnModelFile)
self.insertItem( self.insertItem(
@ -22,8 +23,8 @@ class SettingsOcr(SettingsBaseWidget):
self.knnModelFileResetButton, self.knnModelFileResetButton,
) )
if self.settings.b30KnnModelFile(): if b30KnnModelFile := settings.stringValue(SettingsKeys.Ocr.B30KnnModelFile):
self.b30KnnModelFileValueWidget.selectFile(self.settings.b30KnnModelFile()) self.b30KnnModelFileValueWidget.selectFile(b30KnnModelFile)
self.b30KnnModelFileValueWidget.filesSelected.connect(self.setB30KnnModelFile) self.b30KnnModelFileValueWidget.filesSelected.connect(self.setB30KnnModelFile)
self.b30KnnModelFileResetButton.clicked.connect(self.resetB30KnnModelFile) self.b30KnnModelFileResetButton.clicked.connect(self.resetB30KnnModelFile)
self.insertItem( self.insertItem(
@ -33,10 +34,10 @@ class SettingsOcr(SettingsBaseWidget):
self.b30KnnModelFileResetButton, self.b30KnnModelFileResetButton,
) )
if self.settings.phashDatabaseFile(): if phashDatabaseFile := settings.stringValue(
self.phashDatabaseFileValueWidget.selectFile( SettingsKeys.Ocr.PhashDatabaseFile
self.settings.phashDatabaseFile() ):
) self.phashDatabaseFileValueWidget.selectFile(phashDatabaseFile)
self.phashDatabaseFileValueWidget.filesSelected.connect( self.phashDatabaseFileValueWidget.filesSelected.connect(
self.setPHashDatabaseFile self.setPHashDatabaseFile
) )
@ -52,31 +53,31 @@ class SettingsOcr(SettingsBaseWidget):
selectedFile = self.knnModelFileValueWidget.selectedFiles() selectedFile = self.knnModelFileValueWidget.selectedFiles()
if selectedFile and selectedFile[0]: if selectedFile and selectedFile[0]:
file = selectedFile[0] file = selectedFile[0]
self.settings.setKnnModelFile(file) settings.setValue(SettingsKeys.Ocr.KnnModelFile, file)
def resetKnnModelFile(self): def resetKnnModelFile(self):
self.knnModelFileValueWidget.reset() self.knnModelFileValueWidget.reset()
self.settings.resetKnnModelFile() settings.setValue(SettingsKeys.Ocr.KnnModelFile, None)
def setB30KnnModelFile(self): def setB30KnnModelFile(self):
selectedFile = self.b30KnnModelFileValueWidget.selectedFiles() selectedFile = self.b30KnnModelFileValueWidget.selectedFiles()
if selectedFile and selectedFile[0]: if selectedFile and selectedFile[0]:
file = selectedFile[0] file = selectedFile[0]
self.settings.setB30KnnModelFile(file) settings.setValue(SettingsKeys.Ocr.B30KnnModelFile, file)
def resetB30KnnModelFile(self): def resetB30KnnModelFile(self):
self.b30KnnModelFileValueWidget.reset() self.b30KnnModelFileValueWidget.reset()
self.settings.resetB30KnnModelFile() settings.setValue(SettingsKeys.Ocr.B30KnnModelFile, None)
def setPHashDatabaseFile(self): def setPHashDatabaseFile(self):
selectedFile = self.phashDatabaseFileValueWidget.selectedFiles() selectedFile = self.phashDatabaseFileValueWidget.selectedFiles()
if selectedFile and selectedFile[0]: if selectedFile and selectedFile[0]:
file = selectedFile[0] file = selectedFile[0]
self.settings.setPHashDatabaseFile(file) settings.setValue(SettingsKeys.Ocr.PhashDatabaseFile, file)
def resetPHashDatabaseFile(self): def resetPHashDatabaseFile(self):
self.phashDatabaseFileValueWidget.reset() self.phashDatabaseFileValueWidget.reset()
self.settings.resetPHashDatabaseFile() settings.setValue(SettingsKeys.Ocr.PhashDatabaseFile, None)
def setupUi(self, *args): def setupUi(self, *args):
self.knnModelFileLabel = QLabel(self) self.knnModelFileLabel = QLabel(self)

View File

@ -163,19 +163,15 @@ class TabDb_ChartInfoEditor(Ui_TabDb_ChartInfoEditor, QWidget):
QMessageBox.critical( QMessageBox.critical(
self, self,
None, None,
# fmt: off
QCoreApplication.translate("TabDb_ChartInfoEditor", "commit.chartNotSelected"), QCoreApplication.translate("TabDb_ChartInfoEditor", "commit.chartNotSelected"),
# fmt: on ) # fmt: skip
)
return return
if not self.constantLineEdit.hasAcceptableInput(): if not self.constantLineEdit.hasAcceptableInput():
QMessageBox.critical( QMessageBox.critical(
self, self,
None, None,
# fmt: off
QCoreApplication.translate("TabDb_ChartInfoEditor", "commit.constantRequired"), QCoreApplication.translate("TabDb_ChartInfoEditor", "commit.constantRequired"),
# fmt: on ) # fmt: skip
)
return return
constant = int(self.constantLineEdit.text()) constant = int(self.constantLineEdit.text())
@ -202,10 +198,8 @@ class TabDb_ChartInfoEditor(Ui_TabDb_ChartInfoEditor, QWidget):
QMessageBox.critical( QMessageBox.critical(
self, self,
None, None,
# fmt: off
QCoreApplication.translate("TabDb_ChartInfoEditor", "commit.chartNotSelected"), QCoreApplication.translate("TabDb_ChartInfoEditor", "commit.chartNotSelected"),
# fmt: on ) # fmt: skip
)
return return
chartInfo = self.db.get_chart_info(chart.song_id, chart.rating_class) 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( result = QMessageBox.warning(
self, self,
None, None,
# fmt: off
QCoreApplication.translate("TabDb_ChartInfoEditor", "deleteConfirm"), QCoreApplication.translate("TabDb_ChartInfoEditor", "deleteConfirm"),
# fmt: on
QMessageBox.StandardButton.Yes, QMessageBox.StandardButton.Yes,
QMessageBox.StandardButton.No, QMessageBox.StandardButton.No,
) ) # fmt: skip
if result == QMessageBox.StandardButton.Yes: if result == QMessageBox.StandardButton.Yes:
with self.db.sessionmaker() as session: with self.db.sessionmaker() as session:
session.delete(chartInfo) session.delete(chartInfo)

View File

@ -14,9 +14,10 @@ from arcaea_offline.external.arcaea import (
) )
from arcaea_offline.external.arcaea.common import ArcaeaParser from arcaea_offline.external.arcaea.common import ArcaeaParser
from arcaea_offline.external.arcsong import ArcsongDbParser from arcaea_offline.external.arcsong import ArcsongDbParser
from arcaea_offline.external.chart_info_db import ChartInfoDbParser
from arcaea_offline.external.smartrte import SmartRteB30CsvConverter from arcaea_offline.external.smartrte import SmartRteB30CsvConverter
from arcaea_offline.models import Difficulty, Pack, Song from arcaea_offline.models import Difficulty, Pack, Song
from PySide6.QtCore import QDir, Slot from PySide6.QtCore import QDateTime, QDir, Slot
from PySide6.QtWidgets import QFileDialog, QMessageBox, QWidget from PySide6.QtWidgets import QFileDialog, QMessageBox, QWidget
from ui.designer.tabs.tabDb.tabDb_Manage_ui import Ui_TabDb_Manage from ui.designer.tabs.tabDb.tabDb_Manage_ui import Ui_TabDb_Manage
@ -57,6 +58,29 @@ class TabDb_Manage(Ui_TabDb_Manage, QWidget):
self, "Sync Error", "\n".join(traceback.format_exception(e)) self, "Sync Error", "\n".join(traceback.format_exception(e))
) )
@Slot()
def on_syncChartInfoDbButton_clicked(self):
dbFile, filter = QFileDialog.getOpenFileName(
self, None, "", "DB File (*.db);;*"
)
if not dbFile:
return
try:
db = Database()
parser = ChartInfoDbParser(dbFile)
with db.sessionmaker() as session:
parser.write_database(session)
session.commit()
databaseUpdateSignals.chartInfoUpdated.emit()
QMessageBox.information(self, None, "OK")
except Exception as e:
logging.exception("Sync chart info database error")
QMessageBox.critical(
self, "Sync Error", "\n".join(traceback.format_exception(e))
)
def importFromArcaeaParser( def importFromArcaeaParser(
self, parser: ArcaeaParser, instance, logName, path self, parser: ArcaeaParser, instance, logName, path
) -> int: ) -> int:
@ -67,7 +91,7 @@ class TabDb_Manage(Ui_TabDb_Manage, QWidget):
session.commit() session.commit()
databaseUpdateSignals.songAddOrDelete.emit() databaseUpdateSignals.songAddOrDelete.emit()
itemNum = len([item for item in parser.parse() if isinstance(item, instance)]) 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 return itemNum
def importPacklist(self, packlistPath): def importPacklist(self, packlistPath):
@ -137,7 +161,7 @@ class TabDb_Manage(Ui_TabDb_Manage, QWidget):
return return
try: try:
logger.info(f"Importing {apkFile}") logger.info("Importing %s", apkFile)
with zipfile.ZipFile(apkFile) as zf: with zipfile.ZipFile(apkFile) as zf:
packlistPath = zipfile.Path(zf) / "assets" / "songs" / "packlist" packlistPath = zipfile.Path(zf) / "assets" / "songs" / "packlist"
@ -169,7 +193,9 @@ class TabDb_Manage(Ui_TabDb_Manage, QWidget):
db = Database() db = Database()
parser = St3ScoreParser(dbFile) parser = St3ScoreParser(dbFile)
logger.info( 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: with db.sessionmaker() as session:
parser.write_database(session) parser.write_database(session)
@ -194,7 +220,9 @@ class TabDb_Manage(Ui_TabDb_Manage, QWidget):
db = Database() db = Database()
parser = ArcaeaOnlineParser(apiResultFile) parser = ArcaeaOnlineParser(apiResultFile)
logger.info( 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: with db.sessionmaker() as session:
parser.write_database(session) parser.write_database(session)
@ -208,14 +236,14 @@ class TabDb_Manage(Ui_TabDb_Manage, QWidget):
@Slot() @Slot()
def on_exportScoresButton_clicked(self): def on_exportScoresButton_clicked(self):
scores = Database().export_scores() scores = Database().export_scores_def_v2()
version = Database().version() timestamp = QDateTime.currentMSecsSinceEpoch()
content = json.dumps(scores, ensure_ascii=False) content = json.dumps(scores, ensure_ascii=False)
exportLocation, _filter = QFileDialog.getSaveFileName( exportLocation, _filter = QFileDialog.getSaveFileName(
self, self,
"Save your scores to...", "Save your scores to...",
QDir.current().filePath(f"arcaea-offline-scores-v{version}.json"), QDir.current().filePath(f"arcaea-offline-def-v2-scores-{timestamp}.json"),
"JSON (*.json);;*", "JSON (*.json);;*",
) )
with open(exportLocation, "w", encoding="utf-8") as f: with open(exportLocation, "w", encoding="utf-8") as f:

View File

@ -0,0 +1,346 @@
from enum import IntEnum
from arcaea_offline.database import Database
from arcaea_offline.models import Chart, Difficulty, Score, Song
from PySide6.QtCore import QCoreApplication, QModelIndex, Qt, Slot
from PySide6.QtGui import QStandardItem, QStandardItemModel
from PySide6.QtWidgets import QMessageBox, QStyledItemDelegate, QWidget
from sqlalchemy import delete, func, select
from sqlalchemy.orm import InstrumentedAttribute, Session
from ui.designer.tabs.tabDb.tabDb_RemoveDuplicateScores_ui import (
Ui_TabDb_RemoveDuplicateScores,
)
from ui.extends.shared.delegates.chartDelegate import ChartDelegate
from ui.extends.shared.delegates.scoreDelegate import ScoreDelegate
from ui.extends.shared.language import LanguageChangeEventFilter
class RemoveDuplicateScoresModel(QStandardItemModel):
ScoreRole = Qt.ItemDataRole.UserRole
ChartRole = Qt.ItemDataRole.UserRole + 10
SongRole = Qt.ItemDataRole.UserRole + 11
DifficultyRole = Qt.ItemDataRole.UserRole + 12
def setChartDelegateDatas(
self, item: QStandardItem, songId: str, ratingClass: int, session: Session
):
chart = (
session.query(Chart)
.where((Chart.song_id == songId) & (Chart.rating_class == ratingClass))
.first()
)
song = session.query(Song).where(Song.id == songId).first()
difficulty = (
session.query(Difficulty)
.where(
(Difficulty.song_id == songId)
& (Difficulty.rating_class == ratingClass)
)
.first()
)
if chart is None and song is None and difficulty is None:
chart = Chart(song_id=songId, rating_class=ratingClass, set="unknown")
item.setData(chart, self.ChartRole)
item.setData(song, self.SongRole)
item.setData(difficulty, self.DifficultyRole)
def getGroupKey(self, score: Score, columns: list[InstrumentedAttribute]) -> str:
baseKeys = [score.song_id, str(score.rating_class)]
for column in columns:
key = f"{column.key}{getattr(score,column.key)}"
baseKeys.append(key)
return "||".join(baseKeys)
def setScores(self, scores: list[Score], columns: list[InstrumentedAttribute]):
self.clear()
scoreKeyMap: dict[str, list[Score]] = {}
for score in scores:
key = self.getGroupKey(score, columns)
if scoreKeyMap.get(key) is None:
scoreKeyMap[key] = [score]
else:
scoreKeyMap[key].append(score)
db = Database()
with db.sessionmaker() as session:
for key, scores in scoreKeyMap.items():
songId, ratingClass = key.split("||")[:2]
ratingClass = int(ratingClass)
parentCheckBoxItem = QStandardItem(f"{len(scores)} items")
parentChartItem = QStandardItem()
self.setChartDelegateDatas(
parentChartItem, songId, ratingClass, session
)
for i, score in enumerate(scores):
scoreCheckBoxItem = QStandardItem()
scoreCheckBoxItem.setEditable(False)
scoreCheckBoxItem.setCheckable(True)
scoreCheckBoxItem.setEnabled(True)
scoreItem = QStandardItem()
scoreItem.setData(score, self.ScoreRole)
scoreItem.setEditable(False)
scoreItem.setEnabled(True)
parentCheckBoxItem.setChild(i, 0, scoreCheckBoxItem)
parentCheckBoxItem.setChild(i, 1, scoreItem)
self.appendRow([parentCheckBoxItem, parentChartItem])
class TreeViewChartDelegate(ChartDelegate):
def getChart(self, index: QModelIndex):
return index.data(RemoveDuplicateScoresModel.ChartRole)
def getSong(self, index: QModelIndex):
return index.data(RemoveDuplicateScoresModel.SongRole)
def getDifficulty(self, index: QModelIndex):
return index.data(RemoveDuplicateScoresModel.DifficultyRole)
class TreeViewScoreDelegate(ScoreDelegate):
def getScore(self, index: QModelIndex):
return index.data(RemoveDuplicateScoresModel.ScoreRole)
class TreeViewProxyDelegate(QStyledItemDelegate):
def __init__(
self, chartDelegate: ChartDelegate, scoreDelegate: ScoreDelegate, parent=None
):
super().__init__(parent)
self.chartDelegate = chartDelegate
self.scoreDelegate = scoreDelegate
def delegateForIndex(self, index: QModelIndex) -> QStyledItemDelegate:
return self.scoreDelegate if index.parent().isValid() else self.chartDelegate
def sizeHint(self, option, index: QModelIndex):
return self.delegateForIndex(index).sizeHint(option, index)
def paint(self, painter, option, index: QModelIndex):
self.delegateForIndex(index).paint(painter, option, index)
QStyledItemDelegate.paint(self, painter, option, index)
class QuickSelectComboBoxValues(IntEnum):
ID_EARLIER = 0
DATE_EARLIER = 1
COLUMNS_INTEGRAL = 2
class TabDb_RemoveDuplicateScores(Ui_TabDb_RemoveDuplicateScores, QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setupUi(self)
self.languageChangeEventFilter = LanguageChangeEventFilter(self)
self.installEventFilter(self.languageChangeEventFilter)
self.db = Database()
self.removeDuplicateScoresModel = RemoveDuplicateScoresModel(self)
self.treeView.setModel(self.removeDuplicateScoresModel)
self.treeViewChartDelegate = TreeViewChartDelegate(self.treeView)
self.treeViewScoreDelegate = TreeViewScoreDelegate(self.treeView)
self.treeViewProxyDelegate = TreeViewProxyDelegate(
self.treeViewChartDelegate, self.treeViewScoreDelegate, self.treeView
)
self.treeView.setItemDelegateForColumn(1, self.treeViewProxyDelegate)
self.quickSelect_comboBox.addItem(
QCoreApplication.translate("TabDb_RemoveDuplicateScores", "quickSelectComboBox.idEarlier"),
QuickSelectComboBoxValues.ID_EARLIER
) # fmt: skip
self.quickSelect_comboBox.addItem(
QCoreApplication.translate("TabDb_RemoveDuplicateScores", "quickSelectComboBox.dateEarlier"),
QuickSelectComboBoxValues.DATE_EARLIER
) # fmt: skip
self.quickSelect_comboBox.addItem(
QCoreApplication.translate("TabDb_RemoveDuplicateScores", "quickSelectComboBox.columnsIntegral"),
QuickSelectComboBoxValues.COLUMNS_INTEGRAL
) # fmt: skip
def getQueryColumns(self):
columns: list[InstrumentedAttribute] = [Score.song_id, Score.rating_class]
if self.scan_option_scoreCheckBox.isChecked():
columns.append(Score.score)
if self.scan_option_pureCheckBox.isChecked():
columns.append(Score.pure)
if self.scan_option_farCheckBox.isChecked():
columns.append(Score.far)
if self.scan_option_lostCheckBox.isChecked():
columns.append(Score.lost)
if self.scan_option_maxRecallCheckBox.isChecked():
columns.append(Score.max_recall)
if self.scan_option_dateCheckBox.isChecked():
columns.append(Score.date)
if self.scan_option_modifierCheckBox.isChecked():
columns.append(Score.modifier)
if self.scan_option_clearTypeCheckBox.isChecked():
columns.append(Score.clear_type)
return columns
def getQueryScores(self):
columns = self.getQueryColumns()
with self.db.sessionmaker() as session:
groupBySubquery = (
select(*columns).group_by(*columns).having(func.count() > 1).subquery()
)
selectInClause = [
col == getattr(groupBySubquery.c, col.key) for col in columns
]
return session.query(Score).where(*selectInClause).all()
def scan(self):
scores = self.getQueryScores()
self.removeDuplicateScoresModel.setScores(scores, self.getQueryColumns())
self.treeView.expandAll()
def deselectAll(self):
for row in range(self.removeDuplicateScoresModel.rowCount()):
parentItem = self.removeDuplicateScoresModel.item(row, 0)
for childRow in range(parentItem.rowCount()):
childCheckBoxItem = parentItem.child(childRow, 0)
childCheckBoxItem.setCheckState(Qt.CheckState.Unchecked)
def quickSelect(self):
mode = self.quickSelect_comboBox.currentData()
if mode is None:
return
for row in range(self.removeDuplicateScoresModel.rowCount()):
parentItem = self.removeDuplicateScoresModel.item(row, 0)
scores: list[Score] = []
for childRow in range(parentItem.rowCount()):
childScoreItem = parentItem.child(childRow, 1)
scores.append(childScoreItem.data(RemoveDuplicateScoresModel.ScoreRole))
if mode == QuickSelectComboBoxValues.ID_EARLIER:
chosenRow = min(enumerate(scores), key=lambda i: i[1].id)[0]
elif mode == QuickSelectComboBoxValues.DATE_EARLIER:
chosenRow = min(
enumerate(scores),
key=lambda i: float("inf") if i[1].date is None else i[1].date,
)[0]
elif mode == QuickSelectComboBoxValues.COLUMNS_INTEGRAL:
chosenRow = max(
enumerate(scores),
key=lambda i: sum(
getattr(i[1], col.key) is not None
for col in i[1].__table__.columns
),
)[0]
for childRow in range(parentItem.rowCount()):
childCheckBoxItem = parentItem.child(childRow, 0)
if childRow != chosenRow:
childCheckBoxItem.setCheckState(Qt.CheckState.Checked)
else:
childCheckBoxItem.setCheckState(Qt.CheckState.Unchecked)
def reverseSelection(self):
for row in range(self.removeDuplicateScoresModel.rowCount()):
parentItem = self.removeDuplicateScoresModel.item(row, 0)
# only when there's a checked item in this group, we perform a reversed selection
# otherwise we ignore this group
performReverse = any(
parentItem.child(childRow, 0).checkState() == Qt.CheckState.Checked
for childRow in range(parentItem.rowCount())
)
if not performReverse:
continue
for childRow in range(parentItem.rowCount()):
childCheckBoxItem = parentItem.child(childRow, 0)
newCheckState = (
Qt.CheckState.Unchecked
if childCheckBoxItem.checkState() != Qt.CheckState.Unchecked
else Qt.CheckState.Checked
)
childCheckBoxItem.setCheckState(newCheckState)
def deleteSelection(self):
selectedScores: list[Score] = []
for row in range(self.removeDuplicateScoresModel.rowCount()):
parentItem = self.removeDuplicateScoresModel.item(row, 0)
for childRow in range(parentItem.rowCount()):
childCheckBoxItem = parentItem.child(childRow, 0)
if childCheckBoxItem.checkState() == Qt.CheckState.Checked:
childScoreItem = parentItem.child(childRow, 1)
selectedScores.append(
childScoreItem.data(RemoveDuplicateScoresModel.ScoreRole)
)
confirm = QMessageBox.warning(
self,
None,
QCoreApplication.translate(
"TabDb_RemoveDuplicateScores", "deleteSelectionDialog.content {}"
).format(len(selectedScores)),
QMessageBox.StandardButton.Yes,
QMessageBox.StandardButton.No,
) # fmt: skip
if confirm != QMessageBox.StandardButton.Yes:
return
with self.db.sessionmaker() as session:
ids = [s.id for s in selectedScores]
session.execute(delete(Score).where(Score.id.in_(ids)))
session.commit()
self.scan()
@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,
message,
QMessageBox.StandardButton.Yes,
QMessageBox.StandardButton.No,
)
if result != QMessageBox.StandardButton.Yes:
return
self.scan()
@Slot()
def on_quickSelect_selectButton_clicked(self):
self.quickSelect()
@Slot()
def on_deselectAllButton_clicked(self):
self.deselectAll()
@Slot()
def on_reverseSelectionButton_clicked(self):
self.reverseSelection()
@Slot()
def on_expandAllButton_clicked(self):
self.treeView.expandAll()
@Slot()
def on_collapseAllButton_clicked(self):
self.treeView.collapseAll()
@Slot()
def on_resetModelButton_clicked(self):
self.removeDuplicateScoresModel.clear()
@Slot()
def on_deleteSelectionButton_clicked(self):
self.deleteSelection()

View File

@ -9,6 +9,7 @@ from PIL import Image
from PySide6.QtCore import Signal, Slot from PySide6.QtCore import Signal, Slot
from PySide6.QtWidgets import QFileDialog, QMessageBox, QWidget 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.designer.tabs.tabOcr.tabOcr_B30_ui import Ui_TabOcr_B30
from ui.extends.components.ocrQueue import OcrQueueModel from ui.extends.components.ocrQueue import OcrQueueModel
from ui.extends.ocr.dependencies import ( from ui.extends.ocr.dependencies import (
@ -16,11 +17,6 @@ from ui.extends.ocr.dependencies import (
getPhashDatabaseStatusText, getPhashDatabaseStatusText,
) )
from ui.extends.shared.language import LanguageChangeEventFilter 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 from ui.extends.tabs.tabOcr.tabOcr_B30 import ChieriV4OcrRunnable, b30ResultToScore
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -55,9 +51,15 @@ class TabOcr_B30(Ui_TabOcr_B30, QWidget):
self.ocr = None self.ocr = None
logger.info("Applying settings...") logger.info("Applying settings...")
self.dependencies_knnModelSelector.connectSettings(KNN_MODEL_FILE) self.dependencies_knnModelSelector.connectSettings(
self.dependencies_b30KnnModelSelector.connectSettings(B30_KNN_MODEL_FILE) SettingsKeys.Ocr.KnnModelFile
self.dependencies_phashDatabaseSelector.connectSettings(PHASH_DATABASE_FILE) )
self.dependencies_b30KnnModelSelector.connectSettings(
SettingsKeys.Ocr.B30KnnModelFile
)
self.dependencies_phashDatabaseSelector.connectSettings(
SettingsKeys.Ocr.PhashDatabaseFile
)
self.ocrQueueModel = OcrQueueModel(self) self.ocrQueueModel = OcrQueueModel(self)
self.ocrQueue.setModel(self.ocrQueueModel) self.ocrQueue.setModel(self.ocrQueueModel)

View File

@ -11,10 +11,10 @@ from arcaea_offline_ocr.phash_db import ImagePhashDatabase
from PySide6.QtCore import Slot from PySide6.QtCore import Slot
from PySide6.QtWidgets import QApplication, QFileDialog, QMessageBox, QWidget 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.designer.tabs.tabOcr.tabOcr_Device_ui import Ui_TabOcr_Device
from ui.extends.components.ocrQueue import OcrQueueModel from ui.extends.components.ocrQueue import OcrQueueModel
from ui.extends.shared.language import LanguageChangeEventFilter 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 from ui.extends.tabs.tabOcr.tabOcr_Device import ScoreConverter, TabDeviceOcrRunnable
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -54,8 +54,12 @@ class TabOcr_Device(Ui_TabOcr_Device, QWidget):
) )
logger.info("Applying settings...") logger.info("Applying settings...")
self.dependencies_knnModelSelector.connectSettings(KNN_MODEL_FILE) self.dependencies_knnModelSelector.connectSettings(
self.dependencies_phashDatabaseSelector.connectSettings(PHASH_DATABASE_FILE) SettingsKeys.Ocr.KnnModelFile
)
self.dependencies_phashDatabaseSelector.connectSettings(
SettingsKeys.Ocr.PhashDatabaseFile
)
self.options_usePresetCheckBox.setChecked(True) self.options_usePresetCheckBox.setChecked(True)
self.options_usePresetCheckBox.setEnabled(False) self.options_usePresetCheckBox.setEnabled(False)

View File

@ -1,3 +1,5 @@
import logging
from arcaea_offline.database import Database from arcaea_offline.database import Database
from PySide6.QtCore import QCoreApplication from PySide6.QtCore import QCoreApplication
from PySide6.QtGui import QShowEvent from PySide6.QtGui import QShowEvent
@ -6,6 +8,8 @@ from PySide6.QtWidgets import QWidget
from ui.designer.tabs.tabOverview_ui import Ui_TabOverview from ui.designer.tabs.tabOverview_ui import Ui_TabOverview
from ui.extends.shared.language import LanguageChangeEventFilter from ui.extends.shared.language import LanguageChangeEventFilter
logger = logging.getLogger(__name__)
class TabOverview(Ui_TabOverview, QWidget): class TabOverview(Ui_TabOverview, QWidget):
def __init__(self, parent=None): def __init__(self, parent=None):
@ -22,21 +26,28 @@ class TabOverview(Ui_TabOverview, QWidget):
return super().showEvent(event) return super().showEvent(event)
def updateOverview(self): def updateOverview(self):
b30 = self.db.get_b30() or 0.00 try:
self.b30Label.setText(str(f"{b30:.3f}")) b30 = self.db.get_b30() or 0.00
self.databaseDescribeLabel.setText( self.b30Label.setText(str(f"{b30:.3f}"))
self.describeFormatString.format( except Exception:
self.db.count_packs(), logger.exception("Cannot get b30:")
self.db.count_songs(), self.b30Label.setText("ERR")
self.db.count_difficulties(),
self.db.count_chart_infos(), try:
self.db.count_complete_chart_infos(), self.databaseDescribeLabel.setText(
self.db.count_scores(), self.describeFormatString.format(
self.db.count_packs(),
self.db.count_songs(),
self.db.count_difficulties(),
self.db.count_chart_infos(),
self.db.count_complete_chart_infos(),
self.db.count_scores(),
)
) )
) except Exception:
logger.exception("Cannot update overview:")
self.databaseDescribeLabel.setText("ERR")
def retranslateUi(self, *args): def retranslateUi(self, *args):
super().retranslateUi(self) super().retranslateUi(self)
# fmt: off self.describeFormatString = QCoreApplication.translate("TabOverview", "databaseDescribeLabel {} {} {} {} {} {}") # fmt: skip
self.describeFormatString = QCoreApplication.translate("TabOverview", "databaseDescribeLabel {} {} {} {} {} {}")
# fmt: on

View File

@ -11,9 +11,9 @@ from PySide6.QtCore import QCoreApplication, QDir, QFileInfo, Qt, Slot
from PySide6.QtGui import QGuiApplication, QImage, QPainter, QPaintEvent, QPixmap from PySide6.QtGui import QGuiApplication, QImage, QPainter, QPaintEvent, QPixmap
from PySide6.QtWidgets import QButtonGroup, QFileDialog, QLabel, QMessageBox, QWidget 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.designer.tabs.tabTools.tabTools_Andreal_ui import Ui_TabTools_Andreal
from ui.extends.shared.language import LanguageChangeEventFilter 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.extends.tabs.tabTools.tabTools_Andreal import AndrealHelper
from ui.implements.components.chartSelector import ChartSelector from ui.implements.components.chartSelector import ChartSelector
from ui.implements.components.songIdSelector import SongIdSelectorMode 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.andrealFolderSelector.filesSelected.connect(self.setHelperPaths)
self.andrealExecutableSelector.filesSelected.connect(self.setHelperPaths) self.andrealExecutableSelector.filesSelected.connect(self.setHelperPaths)
self.andrealFolderSelector.connectSettings(ANDREAL_FOLDER) self.andrealFolderSelector.connectSettings(SettingsKeys.Andreal.Folder)
self.andrealExecutableSelector.connectSettings(ANDREAL_EXECUTABLE) self.andrealExecutableSelector.connectSettings(SettingsKeys.Andreal.Executable)
self.generatePreviewButton.clicked.connect(self.requestPreview) self.generatePreviewButton.clicked.connect(self.requestPreview)
self.generateImageButton.clicked.connect(self.requestGenerate) self.generateImageButton.clicked.connect(self.requestGenerate)
@ -131,13 +131,8 @@ class TabTools_Andreal(Ui_TabTools_Andreal, QWidget):
@Slot() @Slot()
def on_imageTypeWhatIsThisButton_clicked(self): def on_imageTypeWhatIsThisButton_clicked(self):
QMessageBox.information( message = QCoreApplication.translate("TabTools_Andreal", "imageWhatIsThisDialog.description") # fmt: skip
self, QMessageBox.information(self, None, message)
None,
# fmt: off
QCoreApplication.translate("TabTools_Andreal", "imageWhatIsThisDialog.description"),
# fmt: on
)
def imageFormat(self): def imageFormat(self):
buttonId = self.imageFormatButtonGroup.checkedId() buttonId = self.imageFormatButtonGroup.checkedId()

View File

@ -2,31 +2,52 @@ import logging
from arcaea_offline.calculate import calculate_constants_from_play_rating from arcaea_offline.calculate import calculate_constants_from_play_rating
from arcaea_offline.database import Database from arcaea_offline.database import Database
from arcaea_offline.models import Chart, ScoreBest from arcaea_offline.models import Chart, Score
from arcaea_offline.utils.rating import rating_class_to_text from arcaea_offline.utils.rating import rating_class_to_text
from arcaea_offline.utils.score import score_to_grade_text from PySide6.QtCore import QModelIndex, Qt, Slot
from PySide6.QtCore import Slot from PySide6.QtWidgets import QDialog, QLabel, QVBoxLayout, QWidget
from PySide6.QtWidgets import QWidget
from ui.designer.tabs.tabTools.tabTools_ChartRecommend_ui import ( from ui.designer.tabs.tabTools.tabTools_ChartRecommend_ui import (
Ui_TabTools_ChartRecommend, Ui_TabTools_ChartRecommend,
) )
from ui.extends.shared.language import LanguageChangeEventFilter
from ui.extends.tabs.tabTools.tabTools_ChartRecommend import ( from ui.extends.tabs.tabTools.tabTools_ChartRecommend import (
ChartsModel, ChartsModel,
ChartsWithScoreBestModel, ChartsWithScoreBestModel,
CustomChartDelegate, CustomChartDelegate,
CustomScoreBestDelegate, CustomScoreBestDelegate,
) )
from ui.implements.components.playRatingCalculator import PlayRatingCalculator
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def chartToText(chart: Chart): class QuickPlayRatingCalculatorDialog(QDialog):
return f"{chart.artist} - {chart.title}<br>({chart.song_id}) {rating_class_to_text(chart.rating_class)}" def __init__(self, parent=None):
super().__init__(parent)
self.verticalLayout = QVBoxLayout(self)
def scoreBestToText(score: ScoreBest): self.chartLabel = QLabel(self)
return f"{score_to_grade_text(score.score)} {score.score} > {score.potential:.4f}" self.verticalLayout.addWidget(self.chartLabel)
self.playRatingCalculator = PlayRatingCalculator(self)
self.verticalLayout.addWidget(self.playRatingCalculator)
self.setMinimumWidth(400)
self.playRatingCalculator.arcaeaScoreLineEdit.setFocus(
Qt.FocusReason.PopupFocusReason
)
def setChart(self, chart: Chart):
self.chartLabel.setText(
f"{chart.title} {rating_class_to_text(chart.rating_class)} {chart.constant / 10}"
)
self.playRatingCalculator.setConstant(chart.constant)
def setScore(self, score: Score):
self.playRatingCalculator.arcaeaScoreLineEdit.setText(str(score))
class TabTools_ChartRecommend(Ui_TabTools_ChartRecommend, QWidget): class TabTools_ChartRecommend(Ui_TabTools_ChartRecommend, QWidget):
@ -34,6 +55,9 @@ class TabTools_ChartRecommend(Ui_TabTools_ChartRecommend, QWidget):
super().__init__(parent) super().__init__(parent)
self.setupUi(self) self.setupUi(self)
self.languageChangeEventFilter = LanguageChangeEventFilter(self)
self.installEventFilter(self.languageChangeEventFilter)
self.db = Database() self.db = Database()
self.chartsByConstantModel = ChartsModel(self) self.chartsByConstantModel = ChartsModel(self)
@ -62,6 +86,13 @@ class TabTools_ChartRecommend(Ui_TabTools_ChartRecommend, QWidget):
self.updateChartsRecommendFromPlayRating self.updateChartsRecommendFromPlayRating
) )
self.chartsByConstant_modelView.doubleClicked.connect(
self.openQuickPlayRatingCalculator_chartsByConstant
)
self.chartsRecommendFromPlayRating_modelView.doubleClicked.connect(
self.openQuickPlayRatingCalculator_chartsRecommendFromPlayRating
)
@Slot(float) @Slot(float)
def on_rangeFromPlayRating_playRatingSpinBox_valueChanged(self, value: float): def on_rangeFromPlayRating_playRatingSpinBox_valueChanged(self, value: float):
try: try:
@ -120,3 +151,26 @@ class TabTools_ChartRecommend(Ui_TabTools_ChartRecommend, QWidget):
self.chartsRecommendFromPlayRatingModel.setChartAndScore(charts, scores) self.chartsRecommendFromPlayRatingModel.setChartAndScore(charts, scores)
self.chartsRecommendFromPlayRating_modelView.resizeRowsToContents() self.chartsRecommendFromPlayRating_modelView.resizeRowsToContents()
self.chartsRecommendFromPlayRating_modelView.resizeColumnsToContents() self.chartsRecommendFromPlayRating_modelView.resizeColumnsToContents()
@Slot(QModelIndex)
def openQuickPlayRatingCalculator_chartsByConstant(self, index: QModelIndex):
dialog = QuickPlayRatingCalculatorDialog(self)
chart = index.data(ChartsModel.ChartRole)
dialog.setChart(chart)
dialog.show()
@Slot(QModelIndex)
def openQuickPlayRatingCalculator_chartsRecommendFromPlayRating(
self, index: QModelIndex
):
dialog = QuickPlayRatingCalculatorDialog(self)
row = index.row()
chartIndex = self.chartsRecommendFromPlayRatingModel.item(row, 0)
scoreIndex = self.chartsRecommendFromPlayRatingModel.item(row, 1)
chart = chartIndex.data(ChartsWithScoreBestModel.ChartRole)
score: Score = scoreIndex.data(ChartsWithScoreBestModel.ScoreBestRole)
dialog.setChart(chart)
dialog.setScore(score.score)
dialog.show()

View File

@ -90,10 +90,8 @@ class PlayRatingCalculatorDialog(QDialog):
self.acceptButton = QPushButton(self) self.acceptButton = QPushButton(self)
self.acceptButton.setText( self.acceptButton.setText(
# fmt: off
QCoreApplication.translate("StepCalculator", "playRatingCalculatorDialog.acceptButton") QCoreApplication.translate("StepCalculator", "playRatingCalculatorDialog.acceptButton")
# fmt: on ) # fmt: skip
)
self.acceptButton.setEnabled(False) self.acceptButton.setEnabled(False)
self.verticalLayout.addWidget(self.acceptButton) self.verticalLayout.addWidget(self.acceptButton)

View File

@ -27,78 +27,82 @@
<translation>Reset</translation> <translation>Reset</translation>
</message> </message>
</context> </context>
<context>
<name>DB30TableModel</name>
<message>
<location filename="../../extends/shared/models/tables/b30.py" line="21"/>
<source>horizontalHeader.id</source>
<translation>ID</translation>
</message>
<message>
<location filename="../../extends/shared/models/tables/b30.py" line="22"/>
<source>horizontalHeader.chart</source>
<translation>Chart</translation>
</message>
<message>
<location filename="../../extends/shared/models/tables/b30.py" line="23"/>
<source>horizontalHeader.score</source>
<translation>Score</translation>
</message>
<message>
<location filename="../../extends/shared/models/tables/b30.py" line="24"/>
<source>horizontalHeader.potential</source>
<translation>Potential</translation>
</message>
</context>
<context> <context>
<name>DatabaseChecker</name> <name>DatabaseChecker</name>
<message> <message>
<location filename="../../startup/databaseChecker.ui" line="23"/> <location filename="../../startup/databaseChecker.ui" line="23"/>
<location filename="../../startup/databaseChecker_ui.py" line="162"/>
<source>dbPathLabel</source> <source>dbPathLabel</source>
<translation>Database path</translation> <translation>Database path</translation>
</message> </message>
<message> <message>
<location filename="../../startup/databaseChecker.ui" line="33"/> <location filename="../../startup/databaseChecker.ui" line="33"/>
<location filename="../../startup/databaseChecker_ui.py" line="165"/>
<source>dbFilenameLabel</source> <source>dbFilenameLabel</source>
<translation>Database filename</translation> <translation>Database filename</translation>
</message> </message>
<message> <message>
<location filename="../../startup/databaseChecker.ui" line="64"/> <location filename="../../startup/databaseChecker.ui" line="64"/>
<location filename="../../startup/databaseChecker_ui.py" line="168"/>
<source>confirmDbPathButton</source> <source>confirmDbPathButton</source>
<translation>Confirm</translation> <translation>Confirm</translation>
</message> </message>
<message> <message>
<location filename="../../startup/databaseChecker.ui" line="117"/> <location filename="../../startup/databaseChecker.ui" line="117"/>
<location filename="../../startup/databaseChecker_ui.py" line="177"/>
<source>dbVersionLabel</source> <source>dbVersionLabel</source>
<translation>Database version</translation> <translation>Database version</translation>
</message> </message>
<message>
<location filename="../../startup/databaseChecker.ui" line="131"/>
<source>dbReInitLabel</source>
<translation>Re-initialize database</translation>
</message>
<message>
<location filename="../../startup/databaseChecker.ui" line="138"/>
<source>dbReInitButton</source>
<translation>Re-initialize</translation>
</message>
<message> <message>
<location filename="../../startup/databaseChecker.ui" line="93"/> <location filename="../../startup/databaseChecker.ui" line="93"/>
<location filename="../../startup/databaseChecker_ui.py" line="171"/>
<source>dbCheckConnLabel</source> <source>dbCheckConnLabel</source>
<translation>Database connection</translation> <translation>Database connection</translation>
</message> </message>
<message> <message>
<location filename="../../startup/databaseChecker.ui" line="110"/> <location filename="../../startup/databaseChecker.ui" line="110"/>
<location filename="../../startup/databaseChecker_ui.py" line="174"/>
<source>continueButton</source> <source>continueButton</source>
<translation>Continue</translation> <translation>Continue</translation>
</message> </message>
<message> <message>
<location filename="../../startup/databaseChecker.py" line="117"/> <location filename="../../startup/databaseChecker.py" line="122"/>
<source>dialog.tryInitExistingDatabase</source> <source>dialog.tryInitExistingDatabase</source>
<translation>The existing database doesn&apos;t seem to be initialized properly, try initialize again?</translation> <translation>The existing database doesn&apos;t seem to be initialized properly, try initialize again?</translation>
</message> </message>
<message> <message>
<location filename="../../startup/databaseChecker.py" line="133"/> <location filename="../../startup/databaseChecker.py" line="138"/>
<source>dialog.confirmNewDatabase</source> <source>dialog.confirmNewDatabase</source>
<translation>Database file does not exist. Create now?</translation> <translation>Database file does not exist. Create now?</translation>
</message> </message>
</context> </context>
<context>
<name>DbB30TableModel</name>
<message>
<location filename="../../extends/shared/models/tables/b30.py" line="20"/>
<source>horizontalHeader.id</source>
<translation>ID</translation>
</message>
<message>
<location filename="../../extends/shared/models/tables/b30.py" line="21"/>
<source>horizontalHeader.chart</source>
<translation>Chart</translation>
</message>
<message>
<location filename="../../extends/shared/models/tables/b30.py" line="22"/>
<source>horizontalHeader.score</source>
<translation>Score</translation>
</message>
<message>
<location filename="../../extends/shared/models/tables/b30.py" line="23"/>
<source>horizontalHeader.potential</source>
<translation>Potential</translation>
</message>
</context>
<context> <context>
<name>DbScoreTableModel</name> <name>DbScoreTableModel</name>
<message> <message>
@ -220,89 +224,130 @@
</message> </message>
<message> <message>
<location filename="../../designer/components/ocrQueue.ui" line="28"/> <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> <source>queue.addImageButton</source>
<translation>Add Image</translation> <translation>Add Image</translation>
</message> </message>
<message> <message>
<location filename="../../designer/components/ocrQueue.ui" line="71"/> <location filename="../../designer/components/ocrQueue.ui" line="38"/>
<source>queue.removeSelected</source> <source>queue.removeSelected</source>
<translation>Remove Selected</translation> <translation>Remove Selected</translation>
</message> </message>
<message> <message>
<location filename="../../designer/components/ocrQueue.ui" line="81"/> <location filename="../../designer/components/ocrQueue.ui" line="48"/>
<source>queue.removeAll</source> <source>queue.removeAll</source>
<translation>Remove All</translation> <translation>Remove All</translation>
</message> </message>
<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> <source>queue.startOcrButton</source>
<translation>Start OCR</translation> <translation>Start OCR</translation>
</message> </message>
<message> <message>
<location filename="../../designer/components/ocrQueue.ui" line="153"/> <location filename="../../designer/components/ocrQueue.ui" line="127"/>
<source>results</source> <source>results</source>
<translation>Results</translation> <translation>Results</translation>
</message> </message>
<message> <message>
<location filename="../../designer/components/ocrQueue.ui" line="162"/> <location filename="../../designer/components/ocrQueue.ui" line="136"/>
<source>results.acceptSelectedButton</source> <source>results.acceptSelectedButton</source>
<translation>Accept Selected</translation> <translation>Accept Selected</translation>
</message> </message>
<message> <message>
<location filename="../../designer/components/ocrQueue.ui" line="169"/> <location filename="../../designer/components/ocrQueue.ui" line="143"/>
<source>results.acceptAllButton</source> <source>results.acceptAllButton</source>
<translation>Accept All</translation> <translation>Accept All</translation>
</message> </message>
<message> <message>
<location filename="../../designer/components/ocrQueue.ui" line="189"/> <location filename="../../designer/components/ocrQueue.ui" line="163"/>
<source>results.ignoreValidate</source> <source>results.ignoreValidate</source>
<translation>Ignore <translation>Ignore
validation</translation> validation</translation>
</message> </message>
</context> </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> <context>
<name>OcrTableModel</name> <name>OcrTableModel</name>
<message> <message>
<location filename="../../extends/components/ocrQueue.py" line="347"/> <location filename="../../extends/components/ocrQueue.py" line="348"/>
<source>horizontalHeader.title.select</source> <source>horizontalHeader.title.select</source>
<translation>Select</translation> <translation>Select</translation>
</message> </message>
<message> <message>
<location filename="../../extends/components/ocrQueue.py" line="348"/> <location filename="../../extends/components/ocrQueue.py" line="351"/>
<source>horizontalHeader.title.imagePreview</source> <source>horizontalHeader.title.imagePreview</source>
<translation>Image Preview</translation> <translation>Image Preview</translation>
</message> </message>
<message> <message>
<location filename="../../extends/components/ocrQueue.py" line="349"/> <location filename="../../extends/components/ocrQueue.py" line="352"/>
<source>horizontalHeader.title.chart</source> <source>horizontalHeader.title.chart</source>
<translation>Chart</translation> <translation>Chart</translation>
</message> </message>
<message> <message>
<location filename="../../extends/components/ocrQueue.py" line="350"/> <location filename="../../extends/components/ocrQueue.py" line="353"/>
<source>horizontalHeader.title.score</source> <source>horizontalHeader.title.score</source>
<translation>Score</translation> <translation>Score</translation>
</message> </message>
</context> </context>
<context>
<name>PotentialCalculator</name>
<message>
<location filename="../../implements/components/playRatingCalculator.py" line="85"/>
<source>copyButton</source>
<translation>Copy</translation>
</message>
</context>
<context> <context>
<name>ResettableItem</name> <name>ResettableItem</name>
<message> <message>
@ -554,6 +599,14 @@ validation</translation>
<translation>Next</translation> <translation>Next</translation>
</message> </message>
</context> </context>
<context>
<name>StepCalculator</name>
<message>
<location filename="../../implements/tabs/tabTools/tabTools_StepCalculator.py" line="96"/>
<source>playRatingCalculatorDialog.acceptButton</source>
<translation>Accept</translation>
</message>
</context>
<context> <context>
<name>TabAbout</name> <name>TabAbout</name>
<message> <message>
@ -579,6 +632,11 @@ validation</translation>
<source>tab.chartInfoEditor</source> <source>tab.chartInfoEditor</source>
<translation>Chart Info Editor</translation> <translation>Chart Info Editor</translation>
</message> </message>
<message>
<location filename="../../designer/tabs/tabDbEntry.ui" line="34"/>
<source>tab.removeDuplicateScores</source>
<translation>Remove Duplicate Scores</translation>
</message>
<message> <message>
<location filename="../../implements/tabs/tabDbEntry.py" line="20"/> <location filename="../../implements/tabs/tabDbEntry.py" line="20"/>
<source>tab.scoreTableViewer</source> <source>tab.scoreTableViewer</source>
@ -647,96 +705,234 @@ validation</translation>
<context> <context>
<name>TabDb_Manage</name> <name>TabDb_Manage</name>
<message> <message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="23"/> <location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="78"/>
<source>syncArcSongDbButton</source> <source>syncArcSongDbButton</source>
<translation>Sync arcsong.db</translation> <translation>Sync arcsong.db</translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="30"/> <location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="85"/>
<source>syncArcSongDb.description</source> <source>syncArcSongDb.description</source>
<translation>Update chart info</translation> <translation>Update chart info</translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="37"/> <location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="98"/>
<source>importScoreGroup</source>
<translation>Import Score</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="105"/>
<source>importSt3Button</source> <source>importSt3Button</source>
<translation>Import from Game Save</translation> <translation>Game Save Database</translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="44"/> <location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="112"/>
<source>importSt3.description</source> <source>importSt3.description</source>
<translation>Import scores from your game save</translation> <translation>Import scores from your game save database</translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="58"/> <location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="139"/>
<source>exportScoresButton</source> <source>exportScoreGroup</source>
<translation>Export Scores</translation> <translation>Export Scores</translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="65"/> <location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="146"/>
<source>exportScores.description</source> <source>exportScoresButton</source>
<translation>Export all your scores to a JSON file</translation> <translation>D.E.F. V2</translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="79"/> <location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="153"/>
<source>exportScores.description</source>
<translation>Export all your scores in &lt;i&gt;Arcaea Offline Data Exchange Format V2&lt;/i&gt; formed JSON file</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="186"/>
<source>miscGroup</source>
<translation>Miscellaneous</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="284"/>
<source>syncChartInfoDbButton</source>
<translation>Sync Chart Info Database</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="291"/>
<source>syncChartInfoDb.description</source>
<translation>Update chart info</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="23"/>
<source>importPacklistButton</source> <source>importPacklistButton</source>
<translation>Import packlist</translation> <translation>Import packlist</translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="86"/> <location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="37"/>
<source>importSonglistButton</source> <source>importSonglistButton</source>
<translation>Import songlist</translation> <translation>Import songlist</translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="93"/> <location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="30"/>
<source>importPacklist.description</source> <source>importPacklist.description</source>
<translation>Import packlist file</translation> <translation>Import packlist file</translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="100"/> <location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="44"/>
<source>importSonglist.description</source> <source>importSonglist.description</source>
<translation>Import songlist file</translation> <translation>Import songlist file</translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="107"/> <location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="193"/>
<source>exportArcsongJsonButton</source> <source>exportArcsongJsonButton</source>
<translation>Export arcsong.json</translation> <translation>Export arcsong.json</translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="114"/> <location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="200"/>
<source>exportArcsongJson.description</source> <source>exportArcsongJson.description</source>
<translation>Export arcsong.json file</translation> <translation>Export arcsong.json file based on the information in database</translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="121"/> <location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="51"/>
<source>importApkButton</source> <source>importApkButton</source>
<translation>Import from APK</translation> <translation>Import from APK</translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="128"/> <location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="213"/>
<source>packSongInfoGroup</source>
<translation>Pack/Song Info</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="58"/>
<source>importApk.description</source> <source>importApk.description</source>
<translation>Import packlist and songlist from .apk file</translation> <translation>Import packlist and songlist from .apk file</translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="135"/> <location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="71"/>
<source>chartInfoGroup</source>
<translation>Chart Info</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="119"/>
<source>importOnlineButton</source> <source>importOnlineButton</source>
<translation>Import from Arcaea Online</translation> <translation>Arcaea Online</translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="142"/> <location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="126"/>
<source>importOnline.description</source> <source>importOnline.description</source>
<translation>Import scores from the result of Arcaea Online API</translation> <translation>Import scores from the Arcaea Online API result</translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="149"/> <location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="160"/>
<source>exportSmartRteB30Button</source> <source>exportSmartRteB30Button</source>
<translation>Export Scores (CSV, SmartRTE B30)</translation> <translation>SmartRTE B30</translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="156"/> <location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="167"/>
<source>exportSmartRteB30.description</source> <source>exportSmartRteB30.description</source>
<translation>Export all your scores to &lt;a href=&quot;https://smartrte.github.io/b30gen.html&quot;&gt;smartrte.github.io&lt;/a&gt; compatible CSV file</translation> <translation>Export all your scores to &lt;a href=&quot;https://smartrte.github.io/b30gen.html&quot;&gt;smartrte.github.io&lt;/a&gt; compatible CSV file</translation>
</message> </message>
</context> </context>
<context>
<name>TabDb_RemoveDuplicateScores</name>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="20"/>
<source>scan.title</source>
<translation>Scan Options</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="30"/>
<source>scan.option.score</source>
<translation>Score</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="71"/>
<source>scan.option.date</source>
<translation>Date</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="78"/>
<source>scan.option.modifier</source>
<translation>Modifier</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="85"/>
<source>scan.option.clearType</source>
<translation>Clear Type</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="94"/>
<source>scan.scanButton</source>
<translation>Scan</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="124"/>
<source>quickSelect.title</source>
<translation>Quick Select</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="130"/>
<source>quickSelect.description</source>
<translation>Keep the first score item&lt;br&gt;that matches:</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="166"/>
<source>quickSelect.selectButton</source>
<translation>Select</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="182"/>
<source>deselectAllButton</source>
<translation>Clear Selection</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="189"/>
<source>reverseSelectionButton</source>
<translation>Reverse Selection</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="209"/>
<source>collapseAllButton</source>
<translation>Collapse All Groups</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="216"/>
<source>expandAllButton</source>
<translation>Expand All Groups</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="223"/>
<source>resetModelButton</source>
<translation>Reset Model</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="251"/>
<source>deleteSelectionButton</source>
<translation>Delete Selected Scores</translation>
</message>
<message>
<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="164"/>
<source>quickSelectComboBox.dateEarlier</source>
<translation>Earlier date</translation>
</message>
<message>
<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="295"/>
<source>deleteSelectionDialog.content {}</source>
<translation>Deleting {} scores from database, this cannot be undone!&lt;br&gt;Confirm?</translation>
</message>
<message>
<location filename="../../implements/tabs/tabDb/tabDb_RemoveDuplicateScores.py" line="317"/>
<source>scan_noColumnsDialog.content</source>
<translation>You haven&apos;t selected any column! Are you sure to continue?</translation>
</message>
</context>
<context> <context>
<name>TabOcrDisabled</name> <name>TabOcrDisabled</name>
<message> <message>
@ -891,7 +1087,7 @@ validation</translation>
<context> <context>
<name>TabOverview</name> <name>TabOverview</name>
<message> <message>
<location filename="../../implements/tabs/tabOverview.py" line="43"/> <location filename="../../implements/tabs/tabOverview.py" line="56"/>
<source>databaseDescribeLabel {} {} {} {} {} {}</source> <source>databaseDescribeLabel {} {} {} {} {} {}</source>
<translation>There are {} packs, {} songs, {} difficulties, {} chart info ({} complete) and {} scores in database.</translation> <translation>There are {} packs, {} songs, {} difficulties, {} chart info ({} complete) and {} scores in database.</translation>
</message> </message>
@ -981,6 +1177,11 @@ validation</translation>
<source>generateImageButton</source> <source>generateImageButton</source>
<translation>Generate</translation> <translation>Generate</translation>
</message> </message>
<message>
<location filename="../../designer/tabs/tabTools/tabTools_Andreal.ui" line="335"/>
<source>sourceCode</source>
<translation>Source code</translation>
</message>
<message> <message>
<location filename="../../implements/tabs/tabTools/tabTools_Andreal.py" line="138"/> <location filename="../../implements/tabs/tabTools/tabTools_Andreal.py" line="138"/>
<source>imageWhatIsThisDialog.description</source> <source>imageWhatIsThisDialog.description</source>
@ -990,7 +1191,7 @@ validation</translation>
<context> <context>
<name>TabTools_ChartRecommend</name> <name>TabTools_ChartRecommend</name>
<message> <message>
<location filename="../../designer/tabs/tabTools/tabTools_ChartRecommend.ui" line="99"/> <location filename="../../designer/tabs/tabTools/tabTools_ChartRecommend.ui" line="102"/>
<source>constantRangeFromPlayRating</source> <source>constantRangeFromPlayRating</source>
<translation>Chart Constant Range from Play Rating</translation> <translation>Chart Constant Range from Play Rating</translation>
</message> </message>
@ -1000,7 +1201,7 @@ validation</translation>
<translation>Charts by Constant</translation> <translation>Charts by Constant</translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabTools/tabTools_ChartRecommend.ui" line="245"/> <location filename="../../designer/tabs/tabTools/tabTools_ChartRecommend.ui" line="248"/>
<source>chartsRecommendFromPlayRating</source> <source>chartsRecommendFromPlayRating</source>
<translation>Chart from Play Rating Based on Best Score</translation> <translation>Chart from Play Rating Based on Best Score</translation>
</message> </message>

View File

@ -1,5 +1,5 @@
import argparse import argparse
import os import subprocess
import sys import sys
from pathlib import Path from pathlib import Path
@ -32,23 +32,31 @@ assert startup.exists()
no_obsolete = args.no_obsolete no_obsolete = args.no_obsolete
commands = [ commands = [
( [
"pyside6-lupdate" "pyside6-lupdate",
" -extensions py,ui" "-extensions",
f" {designer.absolute()} {extends.absolute()} {implements.absolute()} {startup.absolute()}" "py,ui",
f" -ts {str((output_dir_path / 'zh_CN.ts').absolute())}" str(designer.absolute()),
), # zh_CN str(extends.absolute()),
( str(implements.absolute()),
"pyside6-lupdate" str(startup.absolute()),
" -extensions py,ui" "-ts",
f" {designer.absolute()} {extends.absolute()} {implements.absolute()} {startup.absolute()}" str((output_dir_path / "zh_CN.ts").absolute()),
f" -ts {str((output_dir_path / 'en_US.ts').absolute())}" ], # zh_CN
), # en_US [
"pyside6-lupdate",
"-extensions",
"py,ui",
str(designer.absolute()),
str(extends.absolute()),
str(implements.absolute()),
str(startup.absolute()),
"-ts",
str((output_dir_path / "en_US.ts").absolute()),
], # en_US
] ]
if no_obsolete: if no_obsolete:
commands = [f"{command} -no-obsolete" for command in commands] commands = [command + ["-no-obsolete"] for command in commands]
for command in commands: for command in commands:
print(f"Executing '{command}'") subprocess.run(command)
output = os.popen(command).read()
print(output)

View File

@ -27,78 +27,82 @@
<translation></translation> <translation></translation>
</message> </message>
</context> </context>
<context>
<name>DB30TableModel</name>
<message>
<location filename="../../extends/shared/models/tables/b30.py" line="21"/>
<source>horizontalHeader.id</source>
<translation>ID</translation>
</message>
<message>
<location filename="../../extends/shared/models/tables/b30.py" line="22"/>
<source>horizontalHeader.chart</source>
<translation></translation>
</message>
<message>
<location filename="../../extends/shared/models/tables/b30.py" line="23"/>
<source>horizontalHeader.score</source>
<translation></translation>
</message>
<message>
<location filename="../../extends/shared/models/tables/b30.py" line="24"/>
<source>horizontalHeader.potential</source>
<translation> PTT</translation>
</message>
</context>
<context> <context>
<name>DatabaseChecker</name> <name>DatabaseChecker</name>
<message> <message>
<location filename="../../startup/databaseChecker.ui" line="23"/> <location filename="../../startup/databaseChecker.ui" line="23"/>
<location filename="../../startup/databaseChecker_ui.py" line="162"/>
<source>dbPathLabel</source> <source>dbPathLabel</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../../startup/databaseChecker.ui" line="33"/> <location filename="../../startup/databaseChecker.ui" line="33"/>
<location filename="../../startup/databaseChecker_ui.py" line="165"/>
<source>dbFilenameLabel</source> <source>dbFilenameLabel</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../../startup/databaseChecker.ui" line="64"/> <location filename="../../startup/databaseChecker.ui" line="64"/>
<location filename="../../startup/databaseChecker_ui.py" line="168"/>
<source>confirmDbPathButton</source> <source>confirmDbPathButton</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../../startup/databaseChecker.ui" line="117"/> <location filename="../../startup/databaseChecker.ui" line="117"/>
<location filename="../../startup/databaseChecker_ui.py" line="177"/>
<source>dbVersionLabel</source> <source>dbVersionLabel</source>
<translation></translation> <translation></translation>
</message> </message>
<message>
<location filename="../../startup/databaseChecker.ui" line="131"/>
<source>dbReInitLabel</source>
<translation></translation>
</message>
<message>
<location filename="../../startup/databaseChecker.ui" line="138"/>
<source>dbReInitButton</source>
<translation></translation>
</message>
<message> <message>
<location filename="../../startup/databaseChecker.ui" line="93"/> <location filename="../../startup/databaseChecker.ui" line="93"/>
<location filename="../../startup/databaseChecker_ui.py" line="171"/>
<source>dbCheckConnLabel</source> <source>dbCheckConnLabel</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../../startup/databaseChecker.ui" line="110"/> <location filename="../../startup/databaseChecker.ui" line="110"/>
<location filename="../../startup/databaseChecker_ui.py" line="174"/>
<source>continueButton</source> <source>continueButton</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../../startup/databaseChecker.py" line="117"/> <location filename="../../startup/databaseChecker.py" line="122"/>
<source>dialog.tryInitExistingDatabase</source> <source>dialog.tryInitExistingDatabase</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../../startup/databaseChecker.py" line="133"/> <location filename="../../startup/databaseChecker.py" line="138"/>
<source>dialog.confirmNewDatabase</source> <source>dialog.confirmNewDatabase</source>
<translation></translation> <translation></translation>
</message> </message>
</context> </context>
<context>
<name>DbB30TableModel</name>
<message>
<location filename="../../extends/shared/models/tables/b30.py" line="20"/>
<source>horizontalHeader.id</source>
<translation>ID</translation>
</message>
<message>
<location filename="../../extends/shared/models/tables/b30.py" line="21"/>
<source>horizontalHeader.chart</source>
<translation></translation>
</message>
<message>
<location filename="../../extends/shared/models/tables/b30.py" line="22"/>
<source>horizontalHeader.score</source>
<translation></translation>
</message>
<message>
<location filename="../../extends/shared/models/tables/b30.py" line="23"/>
<source>horizontalHeader.potential</source>
<translation> PTT</translation>
</message>
</context>
<context> <context>
<name>DbScoreTableModel</name> <name>DbScoreTableModel</name>
<message> <message>
@ -220,88 +224,129 @@
</message> </message>
<message> <message>
<location filename="../../designer/components/ocrQueue.ui" line="28"/> <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> <source>queue.addImageButton</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../../designer/components/ocrQueue.ui" line="71"/> <location filename="../../designer/components/ocrQueue.ui" line="38"/>
<source>queue.removeSelected</source> <source>queue.removeSelected</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../../designer/components/ocrQueue.ui" line="81"/> <location filename="../../designer/components/ocrQueue.ui" line="48"/>
<source>queue.removeAll</source> <source>queue.removeAll</source>
<translation></translation> <translation></translation>
</message> </message>
<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> <source>queue.startOcrButton</source>
<translation> OCR</translation> <translation> OCR</translation>
</message> </message>
<message> <message>
<location filename="../../designer/components/ocrQueue.ui" line="153"/> <location filename="../../designer/components/ocrQueue.ui" line="127"/>
<source>results</source> <source>results</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../../designer/components/ocrQueue.ui" line="162"/> <location filename="../../designer/components/ocrQueue.ui" line="136"/>
<source>results.acceptSelectedButton</source> <source>results.acceptSelectedButton</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../../designer/components/ocrQueue.ui" line="169"/> <location filename="../../designer/components/ocrQueue.ui" line="143"/>
<source>results.acceptAllButton</source> <source>results.acceptAllButton</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../../designer/components/ocrQueue.ui" line="189"/> <location filename="../../designer/components/ocrQueue.ui" line="163"/>
<source>results.ignoreValidate</source> <source>results.ignoreValidate</source>
<translation></translation> <translation></translation>
</message> </message>
</context> </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> <context>
<name>OcrTableModel</name> <name>OcrTableModel</name>
<message> <message>
<location filename="../../extends/components/ocrQueue.py" line="347"/> <location filename="../../extends/components/ocrQueue.py" line="348"/>
<source>horizontalHeader.title.select</source> <source>horizontalHeader.title.select</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../../extends/components/ocrQueue.py" line="348"/> <location filename="../../extends/components/ocrQueue.py" line="351"/>
<source>horizontalHeader.title.imagePreview</source> <source>horizontalHeader.title.imagePreview</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../../extends/components/ocrQueue.py" line="349"/> <location filename="../../extends/components/ocrQueue.py" line="352"/>
<source>horizontalHeader.title.chart</source> <source>horizontalHeader.title.chart</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../../extends/components/ocrQueue.py" line="350"/> <location filename="../../extends/components/ocrQueue.py" line="353"/>
<source>horizontalHeader.title.score</source> <source>horizontalHeader.title.score</source>
<translation></translation> <translation></translation>
</message> </message>
</context> </context>
<context>
<name>PotentialCalculator</name>
<message>
<location filename="../../implements/components/playRatingCalculator.py" line="85"/>
<source>copyButton</source>
<translation></translation>
</message>
</context>
<context> <context>
<name>ResettableItem</name> <name>ResettableItem</name>
<message> <message>
@ -553,6 +598,14 @@
<translation></translation> <translation></translation>
</message> </message>
</context> </context>
<context>
<name>StepCalculator</name>
<message>
<location filename="../../implements/tabs/tabTools/tabTools_StepCalculator.py" line="96"/>
<source>playRatingCalculatorDialog.acceptButton</source>
<translation></translation>
</message>
</context>
<context> <context>
<name>TabAbout</name> <name>TabAbout</name>
<message> <message>
@ -578,6 +631,11 @@
<source>tab.chartInfoEditor</source> <source>tab.chartInfoEditor</source>
<translation></translation> <translation></translation>
</message> </message>
<message>
<location filename="../../designer/tabs/tabDbEntry.ui" line="34"/>
<source>tab.removeDuplicateScores</source>
<translation></translation>
</message>
<message> <message>
<location filename="../../implements/tabs/tabDbEntry.py" line="20"/> <location filename="../../implements/tabs/tabDbEntry.py" line="20"/>
<source>tab.scoreTableViewer</source> <source>tab.scoreTableViewer</source>
@ -646,96 +704,234 @@
<context> <context>
<name>TabDb_Manage</name> <name>TabDb_Manage</name>
<message> <message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="23"/> <location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="78"/>
<source>syncArcSongDbButton</source> <source>syncArcSongDbButton</source>
<translation> arcsong.db</translation> <translation> arcsong.db</translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="30"/> <location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="85"/>
<source>syncArcSongDb.description</source> <source>syncArcSongDb.description</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="37"/> <location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="98"/>
<source>importSt3Button</source> <source>importScoreGroup</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="44"/> <location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="105"/>
<source>importSt3Button</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="112"/>
<source>importSt3.description</source> <source>importSt3.description</source>
<translation>姿&lt;br&gt;退</translation> <translation>姿&lt;br&gt;退</translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="58"/> <location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="139"/>
<source>exportScoresButton</source> <source>exportScoreGroup</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="65"/> <location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="146"/>
<source>exportScores.description</source> <source>exportScoresButton</source>
<translation> JSON </translation> <translation> V2</translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="79"/> <location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="153"/>
<source>exportScores.description</source>
<translation> &lt;i&gt;Arcaea Offline V2&lt;/i&gt; JSON </translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="186"/>
<source>miscGroup</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="284"/>
<source>syncChartInfoDbButton</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="291"/>
<source>syncChartInfoDb.description</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="23"/>
<source>importPacklistButton</source> <source>importPacklistButton</source>
<translation> packlist</translation> <translation> packlist</translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="86"/> <location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="37"/>
<source>importSonglistButton</source> <source>importSonglistButton</source>
<translation> songlist</translation> <translation> songlist</translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="93"/> <location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="30"/>
<source>importPacklist.description</source> <source>importPacklist.description</source>
<translation> packlist </translation> <translation> packlist </translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="100"/> <location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="44"/>
<source>importSonglist.description</source> <source>importSonglist.description</source>
<translation> songlist </translation> <translation> songlist </translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="107"/> <location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="193"/>
<source>exportArcsongJsonButton</source> <source>exportArcsongJsonButton</source>
<translation> arcsong.json</translation> <translation> arcsong.json</translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="114"/> <location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="200"/>
<source>exportArcsongJson.description</source> <source>exportArcsongJson.description</source>
<translation> arcsong.json </translation> <translation> arcsong.json </translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="121"/> <location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="51"/>
<source>importApkButton</source> <source>importApkButton</source>
<translation> APK </translation> <translation> APK </translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="128"/> <location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="213"/>
<source>packSongInfoGroup</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="58"/>
<source>importApk.description</source> <source>importApk.description</source>
<translation> .apk packlist songlist</translation> <translation> .apk packlist songlist</translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="135"/> <location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="71"/>
<source>importOnlineButton</source> <source>chartInfoGroup</source>
<translation> Arcaea Online </translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="142"/> <location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="119"/>
<source>importOnlineButton</source>
<translation>Arcaea Online</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="126"/>
<source>importOnline.description</source> <source>importOnline.description</source>
<translation> Arcaea Online API </translation> <translation> Arcaea Online API </translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="149"/> <location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="160"/>
<source>exportSmartRteB30Button</source> <source>exportSmartRteB30Button</source>
<translation>CSVSmartRTE B30</translation> <translation>SmartRTE B30</translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="156"/> <location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="167"/>
<source>exportSmartRteB30.description</source> <source>exportSmartRteB30.description</source>
<translation> &lt;a href=&quot;https://smartrte.github.io/b30gen.html&quot;&gt;smartrte.github.io&lt;/a&gt; 的 CSV 文件</translation> <translation> &lt;a href=&quot;https://smartrte.github.io/b30gen.html&quot;&gt;smartrte.github.io&lt;/a&gt; 的 CSV 文件</translation>
</message> </message>
</context> </context>
<context>
<name>TabDb_RemoveDuplicateScores</name>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="20"/>
<source>scan.title</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="30"/>
<source>scan.option.score</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="71"/>
<source>scan.option.date</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="78"/>
<source>scan.option.modifier</source>
<translation>Modifier</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="85"/>
<source>scan.option.clearType</source>
<translation>Clear Type</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="94"/>
<source>scan.scanButton</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="124"/>
<source>quickSelect.title</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="130"/>
<source>quickSelect.description</source>
<translation>&lt;br&gt;</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="166"/>
<source>quickSelect.selectButton</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="182"/>
<source>deselectAllButton</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="189"/>
<source>reverseSelectionButton</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="209"/>
<source>collapseAllButton</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="216"/>
<source>expandAllButton</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="223"/>
<source>resetModelButton</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="251"/>
<source>deleteSelectionButton</source>
<translation></translation>
</message>
<message>
<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="164"/>
<source>quickSelectComboBox.dateEarlier</source>
<translation></translation>
</message>
<message>
<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="295"/>
<source>deleteSelectionDialog.content {}</source>
<translation> {} &lt;br&gt;</translation>
</message>
<message>
<location filename="../../implements/tabs/tabDb/tabDb_RemoveDuplicateScores.py" line="317"/>
<source>scan_noColumnsDialog.content</source>
<translation></translation>
</message>
</context>
<context> <context>
<name>TabOcrDisabled</name> <name>TabOcrDisabled</name>
<message> <message>
@ -890,7 +1086,7 @@
<context> <context>
<name>TabOverview</name> <name>TabOverview</name>
<message> <message>
<location filename="../../implements/tabs/tabOverview.py" line="43"/> <location filename="../../implements/tabs/tabOverview.py" line="56"/>
<source>databaseDescribeLabel {} {} {} {} {} {}</source> <source>databaseDescribeLabel {} {} {} {} {} {}</source>
<translation> {} {} {} {} {} {} </translation> <translation> {} {} {} {} {} {} </translation>
</message> </message>
@ -980,6 +1176,11 @@
<source>generateImageButton</source> <source>generateImageButton</source>
<translation></translation> <translation></translation>
</message> </message>
<message>
<location filename="../../designer/tabs/tabTools/tabTools_Andreal.ui" line="335"/>
<source>sourceCode</source>
<translation></translation>
</message>
<message> <message>
<location filename="../../implements/tabs/tabTools/tabTools_Andreal.py" line="138"/> <location filename="../../implements/tabs/tabTools/tabTools_Andreal.py" line="138"/>
<source>imageWhatIsThisDialog.description</source> <source>imageWhatIsThisDialog.description</source>
@ -989,7 +1190,7 @@
<context> <context>
<name>TabTools_ChartRecommend</name> <name>TabTools_ChartRecommend</name>
<message> <message>
<location filename="../../designer/tabs/tabTools/tabTools_ChartRecommend.ui" line="99"/> <location filename="../../designer/tabs/tabTools/tabTools_ChartRecommend.ui" line="102"/>
<source>constantRangeFromPlayRating</source> <source>constantRangeFromPlayRating</source>
<translation> PTT </translation> <translation> PTT </translation>
</message> </message>
@ -999,7 +1200,7 @@
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabTools/tabTools_ChartRecommend.ui" line="245"/> <location filename="../../designer/tabs/tabTools/tabTools_ChartRecommend.ui" line="248"/>
<source>chartsRecommendFromPlayRating</source> <source>chartsRecommendFromPlayRating</source>
<translation> PTT </translation> <translation> PTT </translation>
</message> </message>

View File

@ -3,11 +3,11 @@ import traceback
from enum import IntEnum from enum import IntEnum
from arcaea_offline.database import Database from arcaea_offline.database import Database
from PySide6.QtCore import QCoreApplication, QDir, QFileInfo, Qt, QUrl, Slot from PySide6.QtCore import QCoreApplication, QDir, QFileInfo, QSysInfo, Qt, QUrl, Slot
from PySide6.QtWidgets import QDialog, QMessageBox from PySide6.QtWidgets import QDialog, QMessageBox
from core.settings import SettingsKeys, settings
from ui.extends.shared.database import create_engine from ui.extends.shared.database import create_engine
from ui.extends.shared.settings import Settings
from .databaseChecker_ui import Ui_DatabaseChecker from .databaseChecker_ui import Ui_DatabaseChecker
@ -29,8 +29,7 @@ class DatabaseChecker(Ui_DatabaseChecker, QDialog):
self.dbDirSelector.setMode(self.dbDirSelector.getExistingDirectory) self.dbDirSelector.setMode(self.dbDirSelector.getExistingDirectory)
self.confirmDbByExistingSettings = False self.confirmDbByExistingSettings = False
self.settings = Settings(self) if dbUrlString := settings.stringValue(SettingsKeys.General.DatabaseUrl):
if dbUrlString := self.settings.databaseUrl():
dbFileUrl = QUrl(dbUrlString.replace("sqlite://", "file://")) dbFileUrl = QUrl(dbUrlString.replace("sqlite://", "file://"))
dbFileInfo = QFileInfo(dbFileUrl.toLocalFile()) dbFileInfo = QFileInfo(dbFileUrl.toLocalFile())
if dbFileInfo.exists(): if dbFileInfo.exists():
@ -45,6 +44,9 @@ class DatabaseChecker(Ui_DatabaseChecker, QDialog):
self.dbDirSelector.selectFile(QDir.currentPath()) self.dbDirSelector.selectFile(QDir.currentPath())
self.dbFilenameLineEdit.setText("arcaea_offline.db") self.dbFilenameLineEdit.setText("arcaea_offline.db")
def writeDatabaseUrlToSettings(self, databaseUrl: str):
settings.setValue(SettingsKeys.General.DatabaseUrl, databaseUrl)
def dbPath(self): def dbPath(self):
return QDir(self.dbDirSelector.selectedFiles()[0]) return QDir(self.dbDirSelector.selectedFiles()[0])
@ -59,8 +61,13 @@ class DatabaseChecker(Ui_DatabaseChecker, QDialog):
return QUrl.fromLocalFile(self.dbFileInfo().filePath()) return QUrl.fromLocalFile(self.dbFileInfo().filePath())
def dbSqliteUrl(self): def dbSqliteUrl(self):
# dbSqliteUrl.setScheme("sqlite") kernelType = QSysInfo.kernelType()
return QUrl(self.dbFileUrl().toString().replace("file://", "sqlite://")) # the slash count varies depending on the kernel
# https://docs.sqlalchemy.org/en/20/core/engines.html#sqlite
if kernelType == "winnt":
return QUrl(self.dbFileUrl().toString().replace("file://", "sqlite://"))
else:
return QUrl(self.dbFileUrl().toString().replace("file://", "sqlite:///"))
def confirmDb(self) -> DatabaseCheckerResult: def confirmDb(self) -> DatabaseCheckerResult:
flags = 0x000 flags = 0x000
@ -74,7 +81,7 @@ class DatabaseChecker(Ui_DatabaseChecker, QDialog):
db = Database(create_engine(dbSqliteUrl)) db = Database(create_engine(dbSqliteUrl))
if db.check_init(): if db.check_init():
flags |= DatabaseCheckerResult.Initted flags |= DatabaseCheckerResult.Initted
self.settings.setDatabaseUrl(self.dbSqliteUrl().toString()) self.writeDatabaseUrlToSettings(self.dbSqliteUrl().toString())
return flags return flags
@ -103,20 +110,18 @@ class DatabaseChecker(Ui_DatabaseChecker, QDialog):
@Slot() @Slot()
def on_confirmDbPathButton_clicked(self): def on_confirmDbPathButton_clicked(self):
dbSqliteUrl = self.dbSqliteUrl() dbSqliteUrl = self.dbSqliteUrl()
self.settings.setDatabaseUrl(dbSqliteUrl.toString()) self.writeDatabaseUrlToSettings(dbSqliteUrl.toString())
result = self.confirmDb() result = self.confirmDb()
if result & DatabaseCheckerResult.Initted: if result & DatabaseCheckerResult.Initted:
if not self.confirmDbByExistingSettings: if not self.confirmDbByExistingSettings:
self.settings.setDatabaseUrl(dbSqliteUrl.toString()) self.writeDatabaseUrlToSettings(dbSqliteUrl.toString())
elif result & DatabaseCheckerResult.FileExist: elif result & DatabaseCheckerResult.FileExist:
confirm_try_init = QMessageBox.question( confirm_try_init = QMessageBox.question(
self, self,
None, None,
# fmt: off
QCoreApplication.translate("DatabaseChecker", "dialog.tryInitExistingDatabase"), QCoreApplication.translate("DatabaseChecker", "dialog.tryInitExistingDatabase"),
# fmt: on ) # fmt: skip
)
if confirm_try_init == QMessageBox.StandardButton.Yes: if confirm_try_init == QMessageBox.StandardButton.Yes:
try: try:
Database().init(checkfirst=True) Database().init(checkfirst=True)
@ -129,15 +134,18 @@ class DatabaseChecker(Ui_DatabaseChecker, QDialog):
confirm_new_database = QMessageBox.question( confirm_new_database = QMessageBox.question(
self, self,
None, None,
# fmt: off
QCoreApplication.translate("DatabaseChecker", "dialog.confirmNewDatabase"), QCoreApplication.translate("DatabaseChecker", "dialog.confirmNewDatabase"),
# fmt: on ) # fmt: skip
)
if confirm_new_database == QMessageBox.StandardButton.Yes: if confirm_new_database == QMessageBox.StandardButton.Yes:
db = Database(create_engine(dbSqliteUrl)) db = Database(create_engine(dbSqliteUrl))
db.init() db.init()
self.updateLabels() self.updateLabels()
@Slot()
def on_dbReInitButton_clicked(self):
Database().init(checkfirst=True)
QMessageBox.information(self, None, "OK")
@Slot() @Slot()
def on_continueButton_clicked(self): def on_continueButton_clicked(self):
self.accept() self.accept()

View File

@ -125,6 +125,20 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>dbReInitLabel</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QPushButton" name="dbReInitButton">
<property name="text">
<string>dbReInitButton</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<customwidgets> <customwidgets>

View File

@ -8,173 +8,131 @@
## WARNING! All changes made in this file will be lost when recompiling UI file! ## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################ ################################################################################
from PySide6.QtCore import ( from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
QCoreApplication, QMetaObject, QObject, QPoint, QRect,
QDate, QSize, QTime, QUrl, Qt)
QDateTime, from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
QLocale, QFont, QFontDatabase, QGradient, QIcon,
QMetaObject, QImage, QKeySequence, QLinearGradient, QPainter,
QObject, QPalette, QPixmap, QRadialGradient, QTransform)
QPoint, from PySide6.QtWidgets import (QApplication, QFormLayout, QFrame, QHBoxLayout,
QRect, QLabel, QLineEdit, QPushButton, QSizePolicy,
QSize, QSpacerItem, QWidget)
Qt,
QTime,
QUrl,
)
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,
QHBoxLayout,
QLabel,
QLineEdit,
QPushButton,
QSizePolicy,
QSpacerItem,
QWidget,
)
from ui.implements.components.fileSelector import FileSelector from ui.implements.components.fileSelector import FileSelector
class Ui_DatabaseChecker(object): class Ui_DatabaseChecker(object):
def setupUi(self, DatabaseChecker): def setupUi(self, DatabaseChecker):
if not DatabaseChecker.objectName(): if not DatabaseChecker.objectName():
DatabaseChecker.setObjectName("DatabaseChecker") DatabaseChecker.setObjectName(u"DatabaseChecker")
DatabaseChecker.resize(350, 250) DatabaseChecker.resize(350, 250)
DatabaseChecker.setWindowTitle("DatabaseChecker") DatabaseChecker.setWindowTitle(u"DatabaseChecker")
self.formLayout = QFormLayout(DatabaseChecker) self.formLayout = QFormLayout(DatabaseChecker)
self.formLayout.setObjectName("formLayout") self.formLayout.setObjectName(u"formLayout")
self.formLayout.setLabelAlignment( self.formLayout.setLabelAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
Qt.AlignRight | Qt.AlignTrailing | Qt.AlignVCenter
)
self.label = QLabel(DatabaseChecker) self.label = QLabel(DatabaseChecker)
self.label.setObjectName("label") self.label.setObjectName(u"label")
self.formLayout.setWidget(0, QFormLayout.LabelRole, self.label) self.formLayout.setWidget(0, QFormLayout.LabelRole, self.label)
self.dbDirSelector = FileSelector(DatabaseChecker) self.dbDirSelector = FileSelector(DatabaseChecker)
self.dbDirSelector.setObjectName("dbDirSelector") self.dbDirSelector.setObjectName(u"dbDirSelector")
self.formLayout.setWidget(0, QFormLayout.FieldRole, self.dbDirSelector) self.formLayout.setWidget(0, QFormLayout.FieldRole, self.dbDirSelector)
self.label_3 = QLabel(DatabaseChecker) self.label_3 = QLabel(DatabaseChecker)
self.label_3.setObjectName("label_3") self.label_3.setObjectName(u"label_3")
self.formLayout.setWidget(1, QFormLayout.LabelRole, self.label_3) self.formLayout.setWidget(1, QFormLayout.LabelRole, self.label_3)
self.dbFilenameLineEdit = QLineEdit(DatabaseChecker) self.dbFilenameLineEdit = QLineEdit(DatabaseChecker)
self.dbFilenameLineEdit.setObjectName("dbFilenameLineEdit") self.dbFilenameLineEdit.setObjectName(u"dbFilenameLineEdit")
self.formLayout.setWidget(1, QFormLayout.FieldRole, self.dbFilenameLineEdit) self.formLayout.setWidget(1, QFormLayout.FieldRole, self.dbFilenameLineEdit)
self.horizontalLayout = QHBoxLayout() self.horizontalLayout = QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout") self.horizontalLayout.setObjectName(u"horizontalLayout")
self.horizontalSpacer = QSpacerItem( self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum
)
self.horizontalLayout.addItem(self.horizontalSpacer) self.horizontalLayout.addItem(self.horizontalSpacer)
self.confirmDbPathButton = QPushButton(DatabaseChecker) self.confirmDbPathButton = QPushButton(DatabaseChecker)
self.confirmDbPathButton.setObjectName("confirmDbPathButton") self.confirmDbPathButton.setObjectName(u"confirmDbPathButton")
sizePolicy = QSizePolicy(QSizePolicy.Maximum, QSizePolicy.Fixed) sizePolicy = QSizePolicy(QSizePolicy.Maximum, QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth( sizePolicy.setHeightForWidth(self.confirmDbPathButton.sizePolicy().hasHeightForWidth())
self.confirmDbPathButton.sizePolicy().hasHeightForWidth()
)
self.confirmDbPathButton.setSizePolicy(sizePolicy) self.confirmDbPathButton.setSizePolicy(sizePolicy)
self.horizontalLayout.addWidget(self.confirmDbPathButton) self.horizontalLayout.addWidget(self.confirmDbPathButton)
self.formLayout.setLayout(2, QFormLayout.FieldRole, self.horizontalLayout) self.formLayout.setLayout(2, QFormLayout.FieldRole, self.horizontalLayout)
self.dbVersionLabel = QLabel(DatabaseChecker) self.dbVersionLabel = QLabel(DatabaseChecker)
self.dbVersionLabel.setObjectName("dbVersionLabel") self.dbVersionLabel.setObjectName(u"dbVersionLabel")
self.dbVersionLabel.setText("-") self.dbVersionLabel.setText(u"-")
self.formLayout.setWidget(4, QFormLayout.FieldRole, self.dbVersionLabel) self.formLayout.setWidget(4, QFormLayout.FieldRole, self.dbVersionLabel)
self.verticalSpacer = QSpacerItem( self.verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding
)
self.formLayout.setItem(6, QFormLayout.FieldRole, self.verticalSpacer) self.formLayout.setItem(6, QFormLayout.FieldRole, self.verticalSpacer)
self.label_5 = QLabel(DatabaseChecker) self.label_5 = QLabel(DatabaseChecker)
self.label_5.setObjectName("label_5") self.label_5.setObjectName(u"label_5")
self.formLayout.setWidget(7, QFormLayout.LabelRole, self.label_5) self.formLayout.setWidget(7, QFormLayout.LabelRole, self.label_5)
self.dbCheckConnLabel = QLabel(DatabaseChecker) self.dbCheckConnLabel = QLabel(DatabaseChecker)
self.dbCheckConnLabel.setObjectName("dbCheckConnLabel") self.dbCheckConnLabel.setObjectName(u"dbCheckConnLabel")
self.dbCheckConnLabel.setText("...") self.dbCheckConnLabel.setText(u"...")
self.formLayout.setWidget(7, QFormLayout.FieldRole, self.dbCheckConnLabel) self.formLayout.setWidget(7, QFormLayout.FieldRole, self.dbCheckConnLabel)
self.continueButton = QPushButton(DatabaseChecker) self.continueButton = QPushButton(DatabaseChecker)
self.continueButton.setObjectName("continueButton") self.continueButton.setObjectName(u"continueButton")
self.continueButton.setEnabled(False) self.continueButton.setEnabled(False)
self.formLayout.setWidget(8, QFormLayout.SpanningRole, self.continueButton) self.formLayout.setWidget(8, QFormLayout.SpanningRole, self.continueButton)
self.label_2 = QLabel(DatabaseChecker) self.label_2 = QLabel(DatabaseChecker)
self.label_2.setObjectName("label_2") self.label_2.setObjectName(u"label_2")
self.formLayout.setWidget(4, QFormLayout.LabelRole, self.label_2) self.formLayout.setWidget(4, QFormLayout.LabelRole, self.label_2)
self.line = QFrame(DatabaseChecker) self.line = QFrame(DatabaseChecker)
self.line.setObjectName("line") self.line.setObjectName(u"line")
self.line.setFrameShape(QFrame.HLine) self.line.setFrameShape(QFrame.HLine)
self.line.setFrameShadow(QFrame.Sunken) self.line.setFrameShadow(QFrame.Sunken)
self.formLayout.setWidget(3, QFormLayout.SpanningRole, self.line) self.formLayout.setWidget(3, QFormLayout.SpanningRole, self.line)
self.label_4 = QLabel(DatabaseChecker)
self.label_4.setObjectName(u"label_4")
self.formLayout.setWidget(5, QFormLayout.LabelRole, self.label_4)
self.dbReInitButton = QPushButton(DatabaseChecker)
self.dbReInitButton.setObjectName(u"dbReInitButton")
self.formLayout.setWidget(5, QFormLayout.FieldRole, self.dbReInitButton)
self.retranslateUi(DatabaseChecker) self.retranslateUi(DatabaseChecker)
QMetaObject.connectSlotsByName(DatabaseChecker) QMetaObject.connectSlotsByName(DatabaseChecker)
# setupUi # setupUi
def retranslateUi(self, DatabaseChecker): def retranslateUi(self, DatabaseChecker):
self.label.setText( self.label.setText(QCoreApplication.translate("DatabaseChecker", u"dbPathLabel", None))
QCoreApplication.translate("DatabaseChecker", "dbPathLabel", None) self.label_3.setText(QCoreApplication.translate("DatabaseChecker", u"dbFilenameLabel", None))
) self.confirmDbPathButton.setText(QCoreApplication.translate("DatabaseChecker", u"confirmDbPathButton", None))
self.label_3.setText( self.label_5.setText(QCoreApplication.translate("DatabaseChecker", u"dbCheckConnLabel", None))
QCoreApplication.translate("DatabaseChecker", "dbFilenameLabel", None) self.continueButton.setText(QCoreApplication.translate("DatabaseChecker", u"continueButton", None))
) self.label_2.setText(QCoreApplication.translate("DatabaseChecker", u"dbVersionLabel", None))
self.confirmDbPathButton.setText( self.label_4.setText(QCoreApplication.translate("DatabaseChecker", u"dbReInitLabel", None))
QCoreApplication.translate("DatabaseChecker", "confirmDbPathButton", None) self.dbReInitButton.setText(QCoreApplication.translate("DatabaseChecker", u"dbReInitButton", None))
)
self.label_5.setText(
QCoreApplication.translate("DatabaseChecker", "dbCheckConnLabel", None)
)
self.continueButton.setText(
QCoreApplication.translate("DatabaseChecker", "continueButton", None)
)
self.label_2.setText(
QCoreApplication.translate("DatabaseChecker", "dbVersionLabel", None)
)
pass pass
# retranslateUi # retranslateUi