From da3ac7acb32c377a7bfbae539f50049405e6b5f7 Mon Sep 17 00:00:00 2001 From: 283375 Date: Thu, 20 Jun 2024 21:26:39 +0800 Subject: [PATCH 1/7] using ruff as formatter & linter --- .pre-commit-config.yaml | 13 ++++++------- pyproject.toml | 40 ++++++++++++++++++++++++++++------------ 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 273404c..b932d10 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,11 +4,10 @@ repos: hooks: - id: end-of-file-fixer - id: trailing-whitespace - - repo: https://github.com/psf/black - rev: 23.1.0 + + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.4.9 hooks: - - id: black - - repo: https://github.com/PyCQA/isort - rev: 5.12.0 - hooks: - - id: isort + - id: ruff + args: ["--fix"] + - id: ruff-format diff --git a/pyproject.toml b/pyproject.toml index 131fa37..8ad189a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,19 +24,35 @@ classifiers = [ "Homepage" = "https://github.com/ArcaeaOffline/client-pyside6" "Bug Tracker" = "https://github.com/ArcaeaOffline/client-pyside6/issues" -[tool.black] -force-exclude = ''' -( - ui/designer - | .*_ui.py - | .*_rc.py -) -''' +[tool.ruff] +exclude = ["*_ui.py", "*_rc.py"] -[tool.isort] -profile = "black" -extend_skip = ["ui/designer"] -extend_skip_glob = ["*_ui.py", "*_rc.py"] +[tool.ruff.lint] +# Full list: https://docs.astral.sh/ruff/rules +select = [ + "E", # pycodestyle (Error) + "W", # pycodestyle (Warning) + "F", # pyflakes + "I", # isort + "PL", # pylint + "N", # pep8-naming + "FBT", # flake8-boolean-trap + "A", # flake8-builtins + "DTZ", # flake8-datetimez + "LOG", # flake8-logging + "Q", # flake8-quotes + "G", # flake8-logging-format + "PIE", # flake8-pie + "PT", # flake8-pytest-style +] +ignore = [ + "E501", # line-too-long + "N802", # invalid-function-name + "N803", # invalid-argument-name + "N806", # non-lowercase-variable-in-function + "N815", # mixed-case-variable-in-class-scope + "N816", # mixed-case-variable-in-global-scope +] [tool.pyright] ignore = ["**/__debug*.*"] From a9d7681ee7e3b3ef293c75f840d7175015a30ea8 Mon Sep 17 00:00:00 2001 From: 283375 Date: Thu, 20 Jun 2024 21:30:21 +0800 Subject: [PATCH 2/7] refactor: moving `ui.extends` to `core` * Settings and Singletons moved --- core/__init__.py | 0 core/settings/__init__.py | 5 + core/settings/base.py | 44 +++++++ core/settings/keys.py | 26 ++++ core/settings/values.py | 17 +++ {ui/extends/shared => core}/singleton.py | 2 +- index.py | 15 ++- ui/extends/shared/data.py | 8 +- ui/extends/shared/settings.py | 122 ------------------ ui/extends/tabs/tabOcr/tabOcr_Device.py | 6 +- ui/implements/components/fileSelector.py | 10 +- .../components/ocrQueueOptionsDialog.py | 15 ++- ui/implements/settings/settingsAndreal.py | 19 ++- ui/implements/settings/settingsBaseWidget.py | 4 +- ui/implements/settings/settingsGeneral.py | 22 ++-- ui/implements/settings/settingsOcr.py | 29 +++-- ui/implements/tabs/tabOcr/tabOcr_B30.py | 18 +-- ui/implements/tabs/tabOcr/tabOcr_Device.py | 10 +- .../tabs/tabTools/tabTools_Andreal.py | 10 +- ui/startup/databaseChecker.py | 22 ++-- 20 files changed, 188 insertions(+), 216 deletions(-) create mode 100644 core/__init__.py create mode 100644 core/settings/__init__.py create mode 100644 core/settings/base.py create mode 100644 core/settings/keys.py create mode 100644 core/settings/values.py rename {ui/extends/shared => core}/singleton.py (86%) delete mode 100644 ui/extends/shared/settings.py diff --git a/core/__init__.py b/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/core/settings/__init__.py b/core/settings/__init__.py new file mode 100644 index 0000000..1333403 --- /dev/null +++ b/core/settings/__init__.py @@ -0,0 +1,5 @@ +from .base import Settings, settings +from .keys import SettingsKeys +from .values import SettingsValues + +__all__ = ["settings", "Settings", "SettingsKeys", "SettingsValues"] diff --git a/core/settings/base.py b/core/settings/base.py new file mode 100644 index 0000000..7cd944c --- /dev/null +++ b/core/settings/base.py @@ -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() diff --git a/core/settings/keys.py b/core/settings/keys.py new file mode 100644 index 0000000..d0f2f54 --- /dev/null +++ b/core/settings/keys.py @@ -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 diff --git a/core/settings/values.py b/core/settings/values.py new file mode 100644 index 0000000..1505a30 --- /dev/null +++ b/core/settings/values.py @@ -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() diff --git a/ui/extends/shared/singleton.py b/core/singleton.py similarity index 86% rename from ui/extends/shared/singleton.py rename to core/singleton.py index 2ef9da9..a4dcbba 100644 --- a/ui/extends/shared/singleton.py +++ b/core/singleton.py @@ -14,5 +14,5 @@ class Singleton(type, Generic[T]): return cls._instance -class QObjectSingleton(type(QObject), Singleton): +class QSingleton(type(QObject), Singleton): pass diff --git a/index.py b/index.py index 640c258..046cc81 100644 --- a/index.py +++ b/index.py @@ -9,9 +9,9 @@ from PySide6.QtCore import QCoreApplication, QLocale from PySide6.QtGui import QFontDatabase, QIcon from PySide6.QtWidgets import QApplication, QDialog, QMessageBox -import ui.resources.resources_rc +import ui.resources.resources_rc # noqa: F401 +from core.settings import SettingsKeys, settings from ui.extends.shared.language import changeAppLanguage -from ui.extends.shared.settings import Settings from ui.implements.mainwindow import MainWindow from ui.startup.databaseChecker import DatabaseChecker, DatabaseCheckerResult @@ -59,16 +59,19 @@ if __name__ == "__main__": rootLogger.addHandler(rootLoggerStdOutHandler) app = QApplication(sys.argv) - locale = ( - QLocale(Settings().language()) if Settings().language() else QLocale.system() - ) + settingsLanguage = settings.stringValue(SettingsKeys.General.Language) + locale = QLocale(settingsLanguage) if settingsLanguage else QLocale.system() changeAppLanguage(locale) QFontDatabase.addApplicationFont(":/fonts/GeosansLight.ttf") databaseChecker = DatabaseChecker() databaseChecker.setWindowIcon(QIcon(":/images/icon.png")) - databaseCheckResult = databaseChecker.confirmDb() if Settings().databaseUrl() else 0 + databaseCheckResult = ( + databaseChecker.confirmDb() + if settings.stringValue(SettingsKeys.General.DatabaseUrl) + else 0 + ) if not databaseCheckResult & DatabaseCheckerResult.Initted: result = databaseChecker.exec() diff --git a/ui/extends/shared/data.py b/ui/extends/shared/data.py index b05452d..7e21c96 100644 --- a/ui/extends/shared/data.py +++ b/ui/extends/shared/data.py @@ -7,7 +7,7 @@ from typing import Literal, Optional, overload from arcaea_offline.models import Chart, Difficulty, Song from PySide6.QtCore import QFile -from .singleton import Singleton +from core.singleton import Singleton TPartnerModifier = dict[str, Literal[0, 1, 2]] @@ -48,14 +48,12 @@ class Data(metaclass=Singleton): return self.dataPath / "Arcaea" @overload - def getJacketPath(self, chart: Chart, /) -> Path | None: - ... + def getJacketPath(self, chart: Chart, /) -> Path | None: ... @overload def getJacketPath( self, song: Song, difficulty: Optional[Difficulty] = None, / - ) -> Path | None: - ... + ) -> Path | None: ... def getJacketPath(self, *args) -> Path | None: if isinstance(args[0], Chart): diff --git a/ui/extends/shared/settings.py b/ui/extends/shared/settings.py deleted file mode 100644 index 8c645de..0000000 --- a/ui/extends/shared/settings.py +++ /dev/null @@ -1,122 +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", - "SCORE_DATE_SOURCE", - "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" -SCORE_DATE_SOURCE = "Ocr/DateSource" - -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 scoreDateSource(self): - return self._strItem(SCORE_DATE_SOURCE) - - def setScoreDateSource(self, value: str): - self._setStrItem(SCORE_DATE_SOURCE, value) - - def resetScoreDateSource(self): - self._resetStrItem(SCORE_DATE_SOURCE) - - 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) diff --git a/ui/extends/tabs/tabOcr/tabOcr_Device.py b/ui/extends/tabs/tabOcr/tabOcr_Device.py index 1287fab..ce58781 100644 --- a/ui/extends/tabs/tabOcr/tabOcr_Device.py +++ b/ui/extends/tabs/tabOcr/tabOcr_Device.py @@ -19,9 +19,9 @@ from arcaea_offline_ocr.phash_db import ImagePhashDatabase from arcaea_offline_ocr.utils import imread_unicode from PySide6.QtCore import QDateTime, QFileInfo +from core.settings import SettingsKeys, SettingsValues, settings from ui.extends.components.ocrQueue import OcrRunnable from ui.extends.shared.data import Data -from ui.extends.shared.settings import Settings logger = logging.getLogger(__name__) @@ -70,8 +70,8 @@ def getImageDate(imagePath: str) -> QDateTime: datetime = QDateTime.fromString(datetimeStr, "yyyy:MM:dd hh:mm:ss") if not isinstance(datetime, QDateTime): - dateSource = Settings().scoreDateSource() - if dateSource == "lastModified": + dateSource = settings.stringValue(SettingsKeys.Ocr.DateSource) + if dateSource == SettingsValues.Ocr.DateSource.FileLastModified: datetime = QFileInfo(imagePath).lastModified() else: datetime = QFileInfo(imagePath).birthTime() diff --git a/ui/implements/components/fileSelector.py b/ui/implements/components/fileSelector.py index 45ee3e6..6e86c04 100644 --- a/ui/implements/components/fileSelector.py +++ b/ui/implements/components/fileSelector.py @@ -2,9 +2,9 @@ from PySide6.QtCore import QDir, QFileInfo, Qt, Signal, Slot from PySide6.QtGui import QDragEnterEvent, QDragLeaveEvent, QDropEvent from PySide6.QtWidgets import QFileDialog, QWidget +from core.settings import settings from ui.designer.components.fileSelector_ui import Ui_FileSelector from ui.extends.shared.language import LanguageChangeEventFilter -from ui.extends.shared.settings import Settings class FileSelector(Ui_FileSelector, QWidget): @@ -122,13 +122,13 @@ class FileSelector(Ui_FileSelector, QWidget): if self.__selectedFiles: return - if value := Settings().value(self.settingsKey): + if value := settings.value(self.settingsKey): self.selectFile(value) - Settings().updated.connect(self.settingsUpdated) + settings.updated.connect(self.settingsUpdated) def disconnectSettings(self): - Settings().updated.disconnect(self.settingsUpdated) + settings.updated.disconnect(self.settingsUpdated) self.settingsKey = None def settingsUpdated(self, key: str): @@ -139,4 +139,4 @@ class FileSelector(Ui_FileSelector, QWidget): if self.__selectedFiles: return - self.selectFile(Settings().value(self.settingsKey)) + self.selectFile(settings.value(self.settingsKey)) diff --git a/ui/implements/components/ocrQueueOptionsDialog.py b/ui/implements/components/ocrQueueOptionsDialog.py index dcb1b6a..f477838 100644 --- a/ui/implements/components/ocrQueueOptionsDialog.py +++ b/ui/implements/components/ocrQueueOptionsDialog.py @@ -1,8 +1,8 @@ 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 -from ui.extends.shared.settings import Settings class OcrQueueOptionsDialog(QDialog, Ui_OcrQueueOptionsDialog): @@ -29,13 +29,12 @@ class OcrQueueOptionsDialog(QDialog, Ui_OcrQueueOptionsDialog): self.on_scoreDateSourceButtonGroup_buttonClicked ) - self.settings = Settings() - self.settings.updated.connect(self.syncCheckboxesFromSettings) + settings.updated.connect(self.syncCheckboxesFromSettings) self.syncCheckboxesFromSettings() def syncCheckboxesFromSettings(self): - scoreDateSource = self.settings.scoreDateSource() - if scoreDateSource == "lastModified": + scoreDateSource = settings.stringValue(SettingsKeys.Ocr.DateSource) + if scoreDateSource == SettingsValues.Ocr.DateSource.FileLastModified: self.dateUseModifyDateRadioButton.setChecked(True) else: self.dateUseCreationDateRadioButton.setChecked(True) @@ -43,6 +42,8 @@ class OcrQueueOptionsDialog(QDialog, Ui_OcrQueueOptionsDialog): def on_scoreDateSourceButtonGroup_buttonClicked(self, button): buttonId = self.scoreDateSourceButtonGroup.id(button) if buttonId == 1: - self.settings.setScoreDateSource("lastModified") + value = SettingsValues.Ocr.DateSource.FileLastModified else: - self.settings.setScoreDateSource("birthTime") + value = SettingsValues.Ocr.DateSource.FileCreated + + settings.setValue(SettingsKeys.Ocr.DateSource, value) diff --git a/ui/implements/settings/settingsAndreal.py b/ui/implements/settings/settingsAndreal.py index 5373ed8..dd02861 100644 --- a/ui/implements/settings/settingsAndreal.py +++ b/ui/implements/settings/settingsAndreal.py @@ -1,6 +1,7 @@ from PySide6.QtCore import QCoreApplication from PySide6.QtWidgets import QLabel, QPushButton +from core.settings import SettingsKeys, settings from ui.implements.components.fileSelector import FileSelector from ui.implements.settings.settingsBaseWidget import SettingsBaseWidget @@ -14,8 +15,8 @@ class SettingsAndreal(SettingsBaseWidget): self.andrealFolderValueWidget.setMode( self.andrealFolderValueWidget.getExistingDirectory ) - if self.settings.andrealFolder(): - self.andrealFolderValueWidget.selectFile(self.settings.andrealFolder()) + if andrealFolder := settings.stringValue(SettingsKeys.Andreal.Folder): + self.andrealFolderValueWidget.selectFile(andrealFolder) self.andrealFolderValueWidget.filesSelected.connect(self.setAndrealFolder) self.andrealFolderResetButton.clicked.connect(self.resetAndrealFolder) self.insertItem( @@ -25,10 +26,8 @@ class SettingsAndreal(SettingsBaseWidget): self.andrealFolderResetButton, ) - if self.settings.andrealExecutable(): - self.andrealExecutableValueWidget.selectFile( - self.settings.andrealExecutable() - ) + if andrealExecutable := settings.stringValue(SettingsKeys.Andreal.Executable): + self.andrealExecutableValueWidget.selectFile(andrealExecutable) self.andrealExecutableValueWidget.filesSelected.connect( self.setAndrealExecutable ) @@ -44,21 +43,21 @@ class SettingsAndreal(SettingsBaseWidget): selectedFile = self.andrealFolderValueWidget.selectedFiles() if selectedFile and selectedFile[0]: file = selectedFile[0] - self.settings.setAndrealFolder(file) + settings.setValue(SettingsKeys.Andreal.Folder, file) def resetAndrealFolder(self): self.andrealFolderValueWidget.reset() - self.settings.resetAndrealFolder() + settings.setValue(SettingsKeys.Andreal.Folder, None) def setAndrealExecutable(self): selectedFile = self.andrealExecutableValueWidget.selectedFiles() if selectedFile and selectedFile[0]: file = selectedFile[0] - self.settings.setAndrealExecutable(file) + settings.setValue(SettingsKeys.Andreal.Executable, file) def resetAndrealExecutable(self): self.andrealExecutableValueWidget.reset() - self.settings.resetAndrealExecutable() + settings.setValue(SettingsKeys.Andreal.Executable, None) def setupUi(self, *args): self.andrealFolderLabel = QLabel(self) diff --git a/ui/implements/settings/settingsBaseWidget.py b/ui/implements/settings/settingsBaseWidget.py index 8b6bc2a..d096557 100644 --- a/ui/implements/settings/settingsBaseWidget.py +++ b/ui/implements/settings/settingsBaseWidget.py @@ -1,15 +1,15 @@ from PySide6.QtCore import Qt from PySide6.QtWidgets import QLabel, QPushButton, QWidget +from core.settings import settings from ui.designer.settings.settingsBaseWidget_ui import Ui_SettingsBaseWidget from ui.extends.shared.language import LanguageChangeEventFilter -from ui.extends.shared.settings import Settings class SettingsBaseWidget(Ui_SettingsBaseWidget, QWidget): def __init__(self, parent=None): super().__init__(parent) - self.settings = Settings() + self.settings = settings self.languageChangeEventFilter = LanguageChangeEventFilter(self) self.installEventFilter(self.languageChangeEventFilter) diff --git a/ui/implements/settings/settingsGeneral.py b/ui/implements/settings/settingsGeneral.py index c84ac9f..b8afef4 100644 --- a/ui/implements/settings/settingsGeneral.py +++ b/ui/implements/settings/settingsGeneral.py @@ -1,6 +1,4 @@ -import sys - -from PySide6.QtCore import QCoreApplication, QDir, QLocale, QProcess +from PySide6.QtCore import QCoreApplication, QDir, QLocale from PySide6.QtWidgets import ( QApplication, QCheckBox, @@ -10,8 +8,8 @@ from PySide6.QtWidgets import ( QPushButton, ) +from core.settings import SettingsKeys, settings from ui.extends.shared.language import changeAppLanguage, localeToCode, localeToFullName -from ui.extends.shared.settings import DATABASE_URL, LANGUAGE from ui.implements.settings.settingsBaseWidget import SettingsBaseWidget @@ -33,8 +31,8 @@ class SettingsGeneral(SettingsBaseWidget): self.languageFollowSystemCheckBox.toggled.connect( self.changeLanguageFollowSystem ) - if self.settings.language(): - locale = QLocale(self.settings.language()) + if language := settings.stringValue(SettingsKeys.General.Language): + locale = QLocale(language) index = self.languageValueWidget.findData(locale) if index > -1: self.languageValueWidget.setCurrentIndex(index) @@ -51,7 +49,7 @@ class SettingsGeneral(SettingsBaseWidget): self.insertItem( "dbUrl", self.dbUrlLabel, - QLabel(self.settings.databaseUrl()), + QLabel(settings.stringValue(SettingsKeys.General.DatabaseUrl)), self.dbUrlResetButton, ) @@ -59,13 +57,13 @@ class SettingsGeneral(SettingsBaseWidget): locale = self.languageValueWidget.currentData() if locale: changeAppLanguage(locale) - self.settings.setLanguage(localeToCode(locale)) + settings.setValue(SettingsKeys.General.Language, localeToCode(locale)) def changeLanguageFollowSystem(self): followSystem = self.languageFollowSystemCheckBox.isChecked() self.languageValueWidget.setCurrentIndex(-1) if followSystem: - self.settings.remove(LANGUAGE) + settings.remove(SettingsKeys.General.Language) changeAppLanguage(QLocale.system()) self.languageValueWidget.setEnabled(False) else: @@ -80,7 +78,7 @@ class SettingsGeneral(SettingsBaseWidget): QMessageBox.StandardButton.No, ) if userConfirm == QMessageBox.StandardButton.Yes: - self.settings.remove(DATABASE_URL) + settings.remove(SettingsKeys.General.DatabaseUrl) QApplication.instance().quit() def setupUi(self, *args): @@ -99,10 +97,10 @@ class SettingsGeneral(SettingsBaseWidget): # fmt: off self.setTitle(QCoreApplication.translate("Settings", "general.title")) - + self.languageLabel.setText(QCoreApplication.translate("Settings", "general.language.label")) self.languageFollowSystemCheckBox.setText(QCoreApplication.translate("Settings", "general.language.followSystem")) - + self.dbUrlLabel.setText(QCoreApplication.translate("Settings", "general.dbUrl.label")) self.dbUrlResetButton.setText(QCoreApplication.translate("Settings", "resetButton")) # fmt: on diff --git a/ui/implements/settings/settingsOcr.py b/ui/implements/settings/settingsOcr.py index 803e4a3..66694e5 100644 --- a/ui/implements/settings/settingsOcr.py +++ b/ui/implements/settings/settingsOcr.py @@ -1,6 +1,7 @@ from PySide6.QtCore import QCoreApplication from PySide6.QtWidgets import QLabel, QPushButton +from core.settings import SettingsKeys, settings from ui.implements.components.fileSelector import FileSelector from ui.implements.settings.settingsBaseWidget import SettingsBaseWidget @@ -11,8 +12,8 @@ class SettingsOcr(SettingsBaseWidget): self.setupUi(self) - if self.settings.knnModelFile(): - self.knnModelFileValueWidget.selectFile(self.settings.knnModelFile()) + if knnModelFile := settings.stringValue(SettingsKeys.Ocr.KnnModelFile): + self.knnModelFileValueWidget.selectFile(knnModelFile) self.knnModelFileValueWidget.filesSelected.connect(self.setKnnModelFile) self.knnModelFileResetButton.clicked.connect(self.resetKnnModelFile) self.insertItem( @@ -22,8 +23,8 @@ class SettingsOcr(SettingsBaseWidget): self.knnModelFileResetButton, ) - if self.settings.b30KnnModelFile(): - self.b30KnnModelFileValueWidget.selectFile(self.settings.b30KnnModelFile()) + if b30KnnModelFile := settings.stringValue(SettingsKeys.Ocr.B30KnnModelFile): + self.b30KnnModelFileValueWidget.selectFile(b30KnnModelFile) self.b30KnnModelFileValueWidget.filesSelected.connect(self.setB30KnnModelFile) self.b30KnnModelFileResetButton.clicked.connect(self.resetB30KnnModelFile) self.insertItem( @@ -33,10 +34,10 @@ class SettingsOcr(SettingsBaseWidget): self.b30KnnModelFileResetButton, ) - if self.settings.phashDatabaseFile(): - self.phashDatabaseFileValueWidget.selectFile( - self.settings.phashDatabaseFile() - ) + if phashDatabaseFile := settings.stringValue( + SettingsKeys.Ocr.PhashDatabaseFile + ): + self.phashDatabaseFileValueWidget.selectFile(phashDatabaseFile) self.phashDatabaseFileValueWidget.filesSelected.connect( self.setPHashDatabaseFile ) @@ -52,31 +53,31 @@ class SettingsOcr(SettingsBaseWidget): selectedFile = self.knnModelFileValueWidget.selectedFiles() if selectedFile and selectedFile[0]: file = selectedFile[0] - self.settings.setKnnModelFile(file) + settings.setValue(SettingsKeys.Ocr.KnnModelFile, file) def resetKnnModelFile(self): self.knnModelFileValueWidget.reset() - self.settings.resetKnnModelFile() + settings.setValue(SettingsKeys.Ocr.KnnModelFile, None) def setB30KnnModelFile(self): selectedFile = self.b30KnnModelFileValueWidget.selectedFiles() if selectedFile and selectedFile[0]: file = selectedFile[0] - self.settings.setB30KnnModelFile(file) + settings.setValue(SettingsKeys.Ocr.B30KnnModelFile, file) def resetB30KnnModelFile(self): self.b30KnnModelFileValueWidget.reset() - self.settings.resetB30KnnModelFile() + settings.setValue(SettingsKeys.Ocr.B30KnnModelFile, None) def setPHashDatabaseFile(self): selectedFile = self.phashDatabaseFileValueWidget.selectedFiles() if selectedFile and selectedFile[0]: file = selectedFile[0] - self.settings.setPHashDatabaseFile(file) + settings.setValue(SettingsKeys.Ocr.PhashDatabaseFile, file) def resetPHashDatabaseFile(self): self.phashDatabaseFileValueWidget.reset() - self.settings.resetPHashDatabaseFile() + settings.setValue(SettingsKeys.Ocr.PhashDatabaseFile, None) def setupUi(self, *args): self.knnModelFileLabel = QLabel(self) diff --git a/ui/implements/tabs/tabOcr/tabOcr_B30.py b/ui/implements/tabs/tabOcr/tabOcr_B30.py index 0473c7b..7d5c965 100644 --- a/ui/implements/tabs/tabOcr/tabOcr_B30.py +++ b/ui/implements/tabs/tabOcr/tabOcr_B30.py @@ -9,6 +9,7 @@ from PIL import Image from PySide6.QtCore import Signal, Slot from PySide6.QtWidgets import QFileDialog, QMessageBox, QWidget +from core.settings import SettingsKeys from ui.designer.tabs.tabOcr.tabOcr_B30_ui import Ui_TabOcr_B30 from ui.extends.components.ocrQueue import OcrQueueModel from ui.extends.ocr.dependencies import ( @@ -16,11 +17,6 @@ from ui.extends.ocr.dependencies import ( getPhashDatabaseStatusText, ) from ui.extends.shared.language import LanguageChangeEventFilter -from ui.extends.shared.settings import ( - B30_KNN_MODEL_FILE, - KNN_MODEL_FILE, - PHASH_DATABASE_FILE, -) from ui.extends.tabs.tabOcr.tabOcr_B30 import ChieriV4OcrRunnable, b30ResultToScore logger = logging.getLogger(__name__) @@ -55,9 +51,15 @@ class TabOcr_B30(Ui_TabOcr_B30, QWidget): self.ocr = None logger.info("Applying settings...") - self.dependencies_knnModelSelector.connectSettings(KNN_MODEL_FILE) - self.dependencies_b30KnnModelSelector.connectSettings(B30_KNN_MODEL_FILE) - self.dependencies_phashDatabaseSelector.connectSettings(PHASH_DATABASE_FILE) + self.dependencies_knnModelSelector.connectSettings( + SettingsKeys.Ocr.KnnModelFile + ) + self.dependencies_b30KnnModelSelector.connectSettings( + SettingsKeys.Ocr.B30KnnModelFile + ) + self.dependencies_phashDatabaseSelector.connectSettings( + SettingsKeys.Ocr.PhashDatabaseFile + ) self.ocrQueueModel = OcrQueueModel(self) self.ocrQueue.setModel(self.ocrQueueModel) diff --git a/ui/implements/tabs/tabOcr/tabOcr_Device.py b/ui/implements/tabs/tabOcr/tabOcr_Device.py index 6c5635e..fcbf35a 100644 --- a/ui/implements/tabs/tabOcr/tabOcr_Device.py +++ b/ui/implements/tabs/tabOcr/tabOcr_Device.py @@ -11,10 +11,10 @@ from arcaea_offline_ocr.phash_db import ImagePhashDatabase from PySide6.QtCore import Slot from PySide6.QtWidgets import QApplication, QFileDialog, QMessageBox, QWidget +from core.settings import SettingsKeys from ui.designer.tabs.tabOcr.tabOcr_Device_ui import Ui_TabOcr_Device from ui.extends.components.ocrQueue import OcrQueueModel from ui.extends.shared.language import LanguageChangeEventFilter -from ui.extends.shared.settings import KNN_MODEL_FILE, PHASH_DATABASE_FILE from ui.extends.tabs.tabOcr.tabOcr_Device import ScoreConverter, TabDeviceOcrRunnable logger = logging.getLogger(__name__) @@ -54,8 +54,12 @@ class TabOcr_Device(Ui_TabOcr_Device, QWidget): ) logger.info("Applying settings...") - self.dependencies_knnModelSelector.connectSettings(KNN_MODEL_FILE) - self.dependencies_phashDatabaseSelector.connectSettings(PHASH_DATABASE_FILE) + self.dependencies_knnModelSelector.connectSettings( + SettingsKeys.Ocr.KnnModelFile + ) + self.dependencies_phashDatabaseSelector.connectSettings( + SettingsKeys.Ocr.PhashDatabaseFile + ) self.options_usePresetCheckBox.setChecked(True) self.options_usePresetCheckBox.setEnabled(False) diff --git a/ui/implements/tabs/tabTools/tabTools_Andreal.py b/ui/implements/tabs/tabTools/tabTools_Andreal.py index 8df74e9..e72ad78 100644 --- a/ui/implements/tabs/tabTools/tabTools_Andreal.py +++ b/ui/implements/tabs/tabTools/tabTools_Andreal.py @@ -11,9 +11,9 @@ from PySide6.QtCore import QCoreApplication, QDir, QFileInfo, Qt, Slot from PySide6.QtGui import QGuiApplication, QImage, QPainter, QPaintEvent, QPixmap from PySide6.QtWidgets import QButtonGroup, QFileDialog, QLabel, QMessageBox, QWidget +from core.settings import SettingsKeys from ui.designer.tabs.tabTools.tabTools_Andreal_ui import Ui_TabTools_Andreal from ui.extends.shared.language import LanguageChangeEventFilter -from ui.extends.shared.settings import ANDREAL_EXECUTABLE, ANDREAL_FOLDER from ui.extends.tabs.tabTools.tabTools_Andreal import AndrealHelper from ui.implements.components.chartSelector import ChartSelector from ui.implements.components.songIdSelector import SongIdSelectorMode @@ -80,8 +80,8 @@ class TabTools_Andreal(Ui_TabTools_Andreal, QWidget): self.andrealFolderSelector.filesSelected.connect(self.setHelperPaths) self.andrealExecutableSelector.filesSelected.connect(self.setHelperPaths) - self.andrealFolderSelector.connectSettings(ANDREAL_FOLDER) - self.andrealExecutableSelector.connectSettings(ANDREAL_EXECUTABLE) + self.andrealFolderSelector.connectSettings(SettingsKeys.Andreal.Folder) + self.andrealExecutableSelector.connectSettings(SettingsKeys.Andreal.Executable) self.generatePreviewButton.clicked.connect(self.requestPreview) self.generateImageButton.clicked.connect(self.requestGenerate) @@ -134,10 +134,8 @@ class TabTools_Andreal(Ui_TabTools_Andreal, QWidget): QMessageBox.information( self, None, - # fmt: off QCoreApplication.translate("TabTools_Andreal", "imageWhatIsThisDialog.description"), - # fmt: on - ) + ) # fmt: skip def imageFormat(self): buttonId = self.imageFormatButtonGroup.checkedId() diff --git a/ui/startup/databaseChecker.py b/ui/startup/databaseChecker.py index e62385d..d785063 100644 --- a/ui/startup/databaseChecker.py +++ b/ui/startup/databaseChecker.py @@ -6,8 +6,8 @@ from arcaea_offline.database import Database from PySide6.QtCore import QCoreApplication, QDir, QFileInfo, QSysInfo, Qt, QUrl, Slot from PySide6.QtWidgets import QDialog, QMessageBox +from core.settings import SettingsKeys, settings from ui.extends.shared.database import create_engine -from ui.extends.shared.settings import Settings from .databaseChecker_ui import Ui_DatabaseChecker @@ -29,8 +29,7 @@ class DatabaseChecker(Ui_DatabaseChecker, QDialog): self.dbDirSelector.setMode(self.dbDirSelector.getExistingDirectory) self.confirmDbByExistingSettings = False - self.settings = Settings(self) - if dbUrlString := self.settings.databaseUrl(): + if dbUrlString := settings.stringValue(SettingsKeys.General.DatabaseUrl): dbFileUrl = QUrl(dbUrlString.replace("sqlite://", "file://")) dbFileInfo = QFileInfo(dbFileUrl.toLocalFile()) if dbFileInfo.exists(): @@ -45,6 +44,9 @@ class DatabaseChecker(Ui_DatabaseChecker, QDialog): self.dbDirSelector.selectFile(QDir.currentPath()) self.dbFilenameLineEdit.setText("arcaea_offline.db") + def writeDatabaseUrlToSettings(self, databaseUrl: str): + settings.setValue(SettingsKeys.General.DatabaseUrl, databaseUrl) + def dbPath(self): return QDir(self.dbDirSelector.selectedFiles()[0]) @@ -79,7 +81,7 @@ class DatabaseChecker(Ui_DatabaseChecker, QDialog): db = Database(create_engine(dbSqliteUrl)) if db.check_init(): flags |= DatabaseCheckerResult.Initted - self.settings.setDatabaseUrl(self.dbSqliteUrl().toString()) + self.writeDatabaseUrlToSettings(self.dbSqliteUrl().toString()) return flags @@ -108,20 +110,18 @@ class DatabaseChecker(Ui_DatabaseChecker, QDialog): @Slot() def on_confirmDbPathButton_clicked(self): dbSqliteUrl = self.dbSqliteUrl() - self.settings.setDatabaseUrl(dbSqliteUrl.toString()) + self.writeDatabaseUrlToSettings(dbSqliteUrl.toString()) result = self.confirmDb() if result & DatabaseCheckerResult.Initted: if not self.confirmDbByExistingSettings: - self.settings.setDatabaseUrl(dbSqliteUrl.toString()) + self.writeDatabaseUrlToSettings(dbSqliteUrl.toString()) elif result & DatabaseCheckerResult.FileExist: confirm_try_init = QMessageBox.question( self, None, - # fmt: off QCoreApplication.translate("DatabaseChecker", "dialog.tryInitExistingDatabase"), - # fmt: on - ) + ) # fmt: skip if confirm_try_init == QMessageBox.StandardButton.Yes: try: Database().init(checkfirst=True) @@ -134,10 +134,8 @@ class DatabaseChecker(Ui_DatabaseChecker, QDialog): confirm_new_database = QMessageBox.question( self, None, - # fmt: off QCoreApplication.translate("DatabaseChecker", "dialog.confirmNewDatabase"), - # fmt: on - ) + ) # fmt: skip if confirm_new_database == QMessageBox.StandardButton.Yes: db = Database(create_engine(dbSqliteUrl)) db.init() From 09063cc987c886d01750ad3df3eda1a107e8a96c Mon Sep 17 00:00:00 2001 From: 283375 Date: Thu, 20 Jun 2024 22:30:39 +0800 Subject: [PATCH 3/7] disable strict ruff rules --- pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 8ad189a..f202a8d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,7 +36,6 @@ select = [ "I", # isort "PL", # pylint "N", # pep8-naming - "FBT", # flake8-boolean-trap "A", # flake8-builtins "DTZ", # flake8-datetimez "LOG", # flake8-logging From fe3f6108781f384d1ec4abcd775bd657b6b20da9 Mon Sep 17 00:00:00 2001 From: 283375 Date: Fri, 28 Jun 2024 20:05:10 +0800 Subject: [PATCH 4/7] core.color --- core/color.py | 10 ++++++++++ ui/extends/shared/color.py | 16 ---------------- .../components/ratingClassRadioButton.py | 4 ++-- 3 files changed, 12 insertions(+), 18 deletions(-) create mode 100644 core/color.py delete mode 100644 ui/extends/shared/color.py diff --git a/core/color.py b/core/color.py new file mode 100644 index 0000000..af27649 --- /dev/null +++ b/core/color.py @@ -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) diff --git a/ui/extends/shared/color.py b/ui/extends/shared/color.py deleted file mode 100644 index 9d36507..0000000 --- a/ui/extends/shared/color.py +++ /dev/null @@ -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) diff --git a/ui/implements/components/ratingClassRadioButton.py b/ui/implements/components/ratingClassRadioButton.py index be2b2f4..476ddae 100644 --- a/ui/implements/components/ratingClassRadioButton.py +++ b/ui/implements/components/ratingClassRadioButton.py @@ -2,7 +2,7 @@ from PySide6.QtCore import Slot from PySide6.QtGui import QColor from PySide6.QtWidgets import QGraphicsColorizeEffect, QRadioButton -from ui.extends.shared.color import mix_color +from core.color import mixColor STYLESHEET = """ QRadioButton {{ @@ -40,7 +40,7 @@ class RatingClassRadioButton(QRadioButton): def setColors(self, dark_color: QColor, text_color: QColor): self._dark_color = dark_color self._text_color = text_color - self._mid_color = mix_color(dark_color, text_color, 0.616) + self._mid_color = mixColor(dark_color, text_color, 0.616) self.updateEffects() def isColorsSet(self) -> bool: From 0c883020537cacabfb43c9f7dbaef2d08baf8fad Mon Sep 17 00:00:00 2001 From: 283375 Date: Fri, 28 Jun 2024 20:08:08 +0800 Subject: [PATCH 5/7] change logging format to `%` --- index.py | 15 +++++++------ ui/extends/components/ocrQueue.py | 22 ++++++++++--------- ui/extends/shared/models/tables/score.py | 6 +++-- ui/extends/tabs/tabOcr/tabOcr_B30.py | 4 ++-- ui/extends/tabs/tabTools/tabTools_Andreal.py | 7 ++++-- .../components/ratingClassSelector.py | 2 +- ui/implements/components/songIdSelector.py | 5 +++-- ui/implements/tabs/tabDb/tabDb_Manage.py | 12 ++++++---- 8 files changed, 43 insertions(+), 30 deletions(-) diff --git a/index.py b/index.py index 046cc81..f853d6e 100644 --- a/index.py +++ b/index.py @@ -18,16 +18,12 @@ from ui.startup.databaseChecker import DatabaseChecker, DatabaseCheckerResult rootLogger = logging.getLogger("root") rootLogger.setLevel(logging.DEBUG) -rootLoggerFormatter = logging.Formatter( - "[{levelname}]{asctime}|{name}: {msg}", "%m-%d %H:%M:%S", "{" -) - def handle_exception(exc_type, exc_value, exc_traceback): - if issubclass(exc_type, KeyboardInterrupt): - sys.__excepthook__(exc_type, exc_value, exc_traceback) - return + sys.__excepthook__(exc_type, exc_value, exc_traceback) + if issubclass(exc_type, KeyboardInterrupt): + return rootLogger.critical( "Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback) ) @@ -46,6 +42,11 @@ if __name__ == "__main__": ymd = now.strftime("%Y%m%d") hms = now.strftime("%H%M%S") + rootLoggerFormatter = logging.Formatter( + "[%(asctime)s/%(levelname)s]%(name)s: %(message)s", + "%m-%d %H:%M:%S", + style="%", + ) rootLoggerFileHandler = logging.FileHandler( str(logFolder / f"arcaea-offline-pyside-ui-{ymd}-{hms}_debug.log"), encoding="utf-8", diff --git a/ui/extends/components/ocrQueue.py b/ui/extends/components/ocrQueue.py index 61c6e6c..39acbdf 100644 --- a/ui/extends/components/ocrQueue.py +++ b/ui/extends/components/ocrQueue.py @@ -140,7 +140,11 @@ class OcrQueueModel(QAbstractListModel): return True else: logger.warning( - f"{repr(self)} setData at row {index.row()} with role {role} and value {value} rejected." + "%r setData at row %d with role %d and value %s rejected!", + self, + index.row(), + role, + value, ) return False @@ -150,7 +154,7 @@ class OcrQueueModel(QAbstractListModel): @iccOption.setter def iccOption(self, opt: IccOption): - logger.debug(f"ICC option changed to {opt}") + logger.debug("ICC option changed to %s", opt) self.__iccOption = opt @overload @@ -159,8 +163,7 @@ class OcrQueueModel(QAbstractListModel): image: str, runnable: OcrRunnable = None, process_func: Callable[[Optional[str], QImage, Any], Score] = None, - ): - ... + ): ... @overload def addItem( @@ -168,8 +171,7 @@ class OcrQueueModel(QAbstractListModel): image: QImage, runnable: OcrRunnable = None, process_func: Callable[[Optional[str], QImage, Any], Score] = None, - ): - ... + ): ... def addItem( self, @@ -179,7 +181,7 @@ class OcrQueueModel(QAbstractListModel): ): if isinstance(image, str): if image in self.imagePaths or not QFileInfo(image).exists(): - logger.warning(f"Attempting to add an invalid file {image}") + logger.warning("Attempting to add an invalid file %s", image) return imagePath = image if self.iccOption == IccOption.TryFix: @@ -223,7 +225,7 @@ class OcrQueueModel(QAbstractListModel): index = self.index(row, 0) imagePath: str = index.data(self.ImagePathRole) qImage: QImage = index.data(self.ImageQImageRole) - logger.debug(f"update request: {result}@row{row}") + logger.debug("update request: %r@row%d", result, row) processOcrResultFunc = index.data(self.ProcessOcrResultFuncRole) chart, scoreInsert = processOcrResultFunc(imagePath, qImage, result) @@ -294,8 +296,8 @@ class OcrQueueModel(QAbstractListModel): self.__items.pop(row) self.endRemoveRows() return - except Exception as e: - logger.exception(f"Error accepting {repr(item)}") + except Exception: + logger.exception("Error accepting %r", item) return def acceptItems(self, __rows: list[int], ignoreValidate: bool = False): diff --git a/ui/extends/shared/models/tables/score.py b/ui/extends/shared/models/tables/score.py index 69b7c39..cc3d0ee 100644 --- a/ui/extends/shared/models/tables/score.py +++ b/ui/extends/shared/models/tables/score.py @@ -28,7 +28,9 @@ class DbScoreTableModel(DbTableModel): QCoreApplication.translate("DbScoreTableModel", "horizontalHeader.id"), QCoreApplication.translate("DbScoreTableModel", "horizontalHeader.chart"), QCoreApplication.translate("DbScoreTableModel", "horizontalHeader.score"), - QCoreApplication.translate("DbScoreTableModel", "horizontalHeader.potential"), + QCoreApplication.translate( + "DbScoreTableModel", "horizontalHeader.potential" + ), # fmt: on ] @@ -154,7 +156,7 @@ class DbScoreTableModel(DbTableModel): self.syncDb() return True except Exception: - logger.exception(f"Table[Score]: Cannot remove row {row}") + logger.exception("Table[Score]: Cannot remove row %s", row) return False def removeRow(self, row: int, parent=...): diff --git a/ui/extends/tabs/tabOcr/tabOcr_B30.py b/ui/extends/tabs/tabOcr/tabOcr_B30.py index 04861c7..a2c6f05 100644 --- a/ui/extends/tabs/tabOcr/tabOcr_B30.py +++ b/ui/extends/tabs/tabOcr/tabOcr_B30.py @@ -6,10 +6,10 @@ from arcaea_offline_ocr.b30.chieri.v4.ocr import ChieriBotV4Ocr from arcaea_offline_ocr.b30.shared import B30OcrResultItem from PySide6.QtGui import QImage -logger = logging.getLogger(__name__) - from ui.extends.components.ocrQueue import OcrRunnable +logger = logging.getLogger(__name__) + class ChieriV4OcrRunnable(OcrRunnable): def __init__(self, ocr: ChieriBotV4Ocr, component): diff --git a/ui/extends/tabs/tabTools/tabTools_Andreal.py b/ui/extends/tabs/tabTools/tabTools_Andreal.py index cac8c7c..e8fc6dc 100644 --- a/ui/extends/tabs/tabTools/tabTools_Andreal.py +++ b/ui/extends/tabs/tabTools/tabTools_Andreal.py @@ -41,7 +41,7 @@ class AndrealExecuteRunnable(QRunnable): self.signals.completed.emit(self.jsonPath, imageBytes) except Exception as e: imageBytes = None - logger.exception(f"{self.__class__.__name__} error") + logger.exception("%s error", self.__class__.__name__) self.signals.error.emit(self.jsonPath, str(e)) finally: os.unlink(self.jsonPath) @@ -84,7 +84,10 @@ class AndrealHelper(QObject): def request(self, jsonPath: str, arguments: list[str]): logger.debug( - f"{self.__class__.__name__} received request {jsonPath=} {arguments=}" + "%s received request jsonPath=%r arguments=%r", + self.__class__.__name__, + jsonPath, + arguments, ) runnable = AndrealExecuteRunnable(self.andrealExecutable, jsonPath, arguments) runnable.signals.error.connect(self.error) diff --git a/ui/implements/components/ratingClassSelector.py b/ui/implements/components/ratingClassSelector.py index 9107473..4ae6641 100644 --- a/ui/implements/components/ratingClassSelector.py +++ b/ui/implements/components/ratingClassSelector.py @@ -126,7 +126,7 @@ class RatingClassSelector(QWidget): elif ratingClass in range(len(self.buttons)): button = self.buttons[ratingClass] else: - logger.debug(f"Cannot select {ratingClass=}, condition check failed") + logger.debug("Cannot select ratingClass=%s, condition check failed", ratingClass) return if not button.isEnabled(): diff --git a/ui/implements/components/songIdSelector.py b/ui/implements/components/songIdSelector.py index e681f76..b44aac8 100644 --- a/ui/implements/components/songIdSelector.py +++ b/ui/implements/components/songIdSelector.py @@ -192,7 +192,7 @@ class SongIdSelector(Ui_SongIdSelector, QWidget): self.fillSongIdComboBox() return True else: - logger.warning(f'Attempting to select an unknown pack "{packId}"') + logger.warning("Attempting to select an unknown pack [%s]", packId) return False def selectSongId(self, songId: str) -> bool: @@ -202,7 +202,8 @@ class SongIdSelector(Ui_SongIdSelector, QWidget): return True else: logger.warning( - f'Attempting to select an unknown song "{songId}", maybe try selecting a pack first?' + "Attempting to select an unknown song [%s], maybe try selecting a pack first?", + songId, ) return False diff --git a/ui/implements/tabs/tabDb/tabDb_Manage.py b/ui/implements/tabs/tabDb/tabDb_Manage.py index 92ff571..7822755 100644 --- a/ui/implements/tabs/tabDb/tabDb_Manage.py +++ b/ui/implements/tabs/tabDb/tabDb_Manage.py @@ -91,7 +91,7 @@ class TabDb_Manage(Ui_TabDb_Manage, QWidget): session.commit() databaseUpdateSignals.songAddOrDelete.emit() itemNum = len([item for item in parser.parse() if isinstance(item, instance)]) - logger.info(f"updated {itemNum} {logName} from {path}") + logger.info("updated %d %s from %s", itemNum, logName, path) return itemNum def importPacklist(self, packlistPath): @@ -161,7 +161,7 @@ class TabDb_Manage(Ui_TabDb_Manage, QWidget): return try: - logger.info(f"Importing {apkFile}") + logger.info("Importing %s", apkFile) with zipfile.ZipFile(apkFile) as zf: packlistPath = zipfile.Path(zf) / "assets" / "songs" / "packlist" @@ -193,7 +193,9 @@ class TabDb_Manage(Ui_TabDb_Manage, QWidget): db = Database() parser = St3ScoreParser(dbFile) logger.info( - f"Got {len(parser.parse())} items from {dbFile}, writing into database..." + "Got %d items from %s, writing into database...", + len(parser.parse()), + dbFile, ) with db.sessionmaker() as session: parser.write_database(session) @@ -218,7 +220,9 @@ class TabDb_Manage(Ui_TabDb_Manage, QWidget): db = Database() parser = ArcaeaOnlineParser(apiResultFile) logger.info( - f"Got {len(parser.parse())} items from {apiResultFile}, writing into database..." + "Got %d items from %s, writing into database...", + len(parser.parse()), + apiResultFile, ) with db.sessionmaker() as session: parser.write_database(session) From b2a10d02cef7ff40652fa7b0eb82b14087d0443d Mon Sep 17 00:00:00 2001 From: 283375 Date: Fri, 28 Jun 2024 20:20:26 +0800 Subject: [PATCH 6/7] change `# fmt:` labels to ruff compatible --- ui/extends/components/ocrQueue.py | 13 +++------- ui/extends/shared/models/tables/b30.py | 4 +-- ui/extends/shared/models/tables/score.py | 8 ++---- ui/implements/components/scoreEditor.py | 22 +++------------- .../tabs/tabDb/tabDb_ChartInfoEditor.py | 16 +++--------- .../tabs/tabDb/tabDb_RemoveDuplicateScores.py | 25 +++++++------------ ui/implements/tabs/tabOverview.py | 4 +-- .../tabs/tabTools/tabTools_Andreal.py | 7 ++---- .../tabs/tabTools/tabTools_StepCalculator.py | 4 +-- 9 files changed, 27 insertions(+), 76 deletions(-) diff --git a/ui/extends/components/ocrQueue.py b/ui/extends/components/ocrQueue.py index 39acbdf..920cbf2 100644 --- a/ui/extends/components/ocrQueue.py +++ b/ui/extends/components/ocrQueue.py @@ -5,7 +5,6 @@ from typing import Any, Callable, Optional, overload from arcaea_offline.calculate import calculate_score_range from arcaea_offline.database import Database from arcaea_offline.models import Chart, Score -from arcaea_offline_ocr.b30.shared import B30OcrResultItem from arcaea_offline_ocr.device.common import DeviceOcrResult from PIL import Image from PIL.ImageQt import ImageQt @@ -346,17 +345,11 @@ class OcrQueueTableProxyModel(QAbstractTableModel): def retranslateHeaders(self): self.__horizontalHeaders = [ - # fmt: off - QCoreApplication.translate( - "OcrTableModel", "horizontalHeader.title.select" - ), - QCoreApplication.translate( - "OcrTableModel", "horizontalHeader.title.imagePreview" - ), + QCoreApplication.translate("OcrTableModel", "horizontalHeader.title.select"), + QCoreApplication.translate("OcrTableModel", "horizontalHeader.title.imagePreview"), QCoreApplication.translate("OcrTableModel", "horizontalHeader.title.chart"), QCoreApplication.translate("OcrTableModel", "horizontalHeader.title.score"), - # fmt: on - ] + ] # fmt: skip def sourceModel(self) -> OcrQueueModel: return self.__sourceModel diff --git a/ui/extends/shared/models/tables/b30.py b/ui/extends/shared/models/tables/b30.py index 0509991..d2ab507 100644 --- a/ui/extends/shared/models/tables/b30.py +++ b/ui/extends/shared/models/tables/b30.py @@ -16,13 +16,11 @@ class DbB30TableModel(DbTableModel): def retranslateHeaders(self): self._horizontalHeaders = [ - # fmt: off QCoreApplication.translate("DbB30TableModel", "horizontalHeader.id"), QCoreApplication.translate("DbB30TableModel", "horizontalHeader.chart"), QCoreApplication.translate("DbB30TableModel", "horizontalHeader.score"), QCoreApplication.translate("DbB30TableModel", "horizontalHeader.potential"), - # fmt: on - ] + ] # fmt: skip def syncDb(self): self.beginResetModel() diff --git a/ui/extends/shared/models/tables/score.py b/ui/extends/shared/models/tables/score.py index cc3d0ee..f70986b 100644 --- a/ui/extends/shared/models/tables/score.py +++ b/ui/extends/shared/models/tables/score.py @@ -24,15 +24,11 @@ class DbScoreTableModel(DbTableModel): def retranslateHeaders(self): self._horizontalHeaders = [ - # fmt: off QCoreApplication.translate("DbScoreTableModel", "horizontalHeader.id"), QCoreApplication.translate("DbScoreTableModel", "horizontalHeader.chart"), QCoreApplication.translate("DbScoreTableModel", "horizontalHeader.score"), - QCoreApplication.translate( - "DbScoreTableModel", "horizontalHeader.potential" - ), - # fmt: on - ] + QCoreApplication.translate("DbScoreTableModel", "horizontalHeader.potential"), + ] # fmt: skip def syncDb(self): self.beginResetModel() diff --git a/ui/implements/components/scoreEditor.py b/ui/implements/components/scoreEditor.py index dbf7375..7441ba2 100644 --- a/ui/implements/components/scoreEditor.py +++ b/ui/implements/components/scoreEditor.py @@ -61,30 +61,22 @@ class ScoreEditor(Ui_ScoreEditor, QWidget): VALIDATION_ITEMS_TEXT = [ [ - # fmt: off lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.chartIncomplete.title"), lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.chartIncomplete.text"), - # fmt: on ], [ - # fmt: off lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.scoreMismatch.title"), lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.scoreMismatch.text"), - # fmt: on ], [ - # fmt: off lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.emptyScore.title"), lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.emptyScore.text"), - # fmt: on ], [ - # fmt: off lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.scoreIncompleteForValidate.title"), lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.scoreIncompleteForValidate.text"), - # fmt: on, ], - ] + ] # fmt: skip def __init__(self, parent=None): super().__init__(parent) @@ -208,20 +200,16 @@ class ScoreEditor(Ui_ScoreEditor, QWidget): if validate & ScoreValidateResult.ChartNotSet: self.__triggerMessageBox( "critical", - # fmt: off QCoreApplication.translate("ScoreEditor", "confirmDialog.chartNotSet.title"), QCoreApplication.translate("ScoreEditor", "confirmDialog.chartNotSet.text"), - # fmt: on - ) + ) # fmt: skip return False if validate & ScoreValidateResult.ScoreIncomplete: self.__triggerMessageBox( "critical", - # fmt: off QCoreApplication.translate("ScoreEditor", "confirmDialog.scoreIncomplete.title"), QCoreApplication.translate("ScoreEditor", "confirmDialog.scoreIncomplete.text"), - # fmt: on - ) + ) # fmt: skip return False # since validate may have multiple results @@ -347,10 +335,8 @@ class ScoreEditor(Ui_ScoreEditor, QWidget): ) if validate & ScoreValidateResult.ScoreIncompleteForValidate: texts.append( - # fmt: off QCoreApplication.translate("ScoreEditor", "validate.scoreIncompleteForValidate") - # fmt: on - ) + ) # fmt: skip if not texts: texts.append( diff --git a/ui/implements/tabs/tabDb/tabDb_ChartInfoEditor.py b/ui/implements/tabs/tabDb/tabDb_ChartInfoEditor.py index d3c32be..eb18c11 100644 --- a/ui/implements/tabs/tabDb/tabDb_ChartInfoEditor.py +++ b/ui/implements/tabs/tabDb/tabDb_ChartInfoEditor.py @@ -163,19 +163,15 @@ class TabDb_ChartInfoEditor(Ui_TabDb_ChartInfoEditor, QWidget): QMessageBox.critical( self, None, - # fmt: off QCoreApplication.translate("TabDb_ChartInfoEditor", "commit.chartNotSelected"), - # fmt: on - ) + ) # fmt: skip return if not self.constantLineEdit.hasAcceptableInput(): QMessageBox.critical( self, None, - # fmt: off QCoreApplication.translate("TabDb_ChartInfoEditor", "commit.constantRequired"), - # fmt: on - ) + ) # fmt: skip return constant = int(self.constantLineEdit.text()) @@ -202,10 +198,8 @@ class TabDb_ChartInfoEditor(Ui_TabDb_ChartInfoEditor, QWidget): QMessageBox.critical( self, None, - # fmt: off QCoreApplication.translate("TabDb_ChartInfoEditor", "commit.chartNotSelected"), - # fmt: on - ) + ) # fmt: skip return chartInfo = self.db.get_chart_info(chart.song_id, chart.rating_class) @@ -213,12 +207,10 @@ class TabDb_ChartInfoEditor(Ui_TabDb_ChartInfoEditor, QWidget): result = QMessageBox.warning( self, None, - # fmt: off QCoreApplication.translate("TabDb_ChartInfoEditor", "deleteConfirm"), - # fmt: on QMessageBox.StandardButton.Yes, QMessageBox.StandardButton.No, - ) + ) # fmt: skip if result == QMessageBox.StandardButton.Yes: with self.db.sessionmaker() as session: session.delete(chartInfo) diff --git a/ui/implements/tabs/tabDb/tabDb_RemoveDuplicateScores.py b/ui/implements/tabs/tabDb/tabDb_RemoveDuplicateScores.py index fb390bb..f40babd 100644 --- a/ui/implements/tabs/tabDb/tabDb_RemoveDuplicateScores.py +++ b/ui/implements/tabs/tabDb/tabDb_RemoveDuplicateScores.py @@ -154,23 +154,17 @@ class TabDb_RemoveDuplicateScores(Ui_TabDb_RemoveDuplicateScores, QWidget): self.treeView.setItemDelegateForColumn(1, self.treeViewProxyDelegate) self.quickSelect_comboBox.addItem( - # fmt: off QCoreApplication.translate("TabDb_RemoveDuplicateScores", "quickSelectComboBox.idEarlier"), - # fmt: on QuickSelectComboBoxValues.ID_EARLIER - ) + ) # fmt: skip self.quickSelect_comboBox.addItem( - # fmt: off QCoreApplication.translate("TabDb_RemoveDuplicateScores", "quickSelectComboBox.dateEarlier"), - # fmt: on QuickSelectComboBoxValues.DATE_EARLIER - ) + ) # fmt: skip self.quickSelect_comboBox.addItem( - # fmt: off QCoreApplication.translate("TabDb_RemoveDuplicateScores", "quickSelectComboBox.columnsIntegral"), - # fmt: on QuickSelectComboBoxValues.COLUMNS_INTEGRAL - ) + ) # fmt: skip def getQueryColumns(self): columns: list[InstrumentedAttribute] = [Score.song_id, Score.rating_class] @@ -291,12 +285,12 @@ class TabDb_RemoveDuplicateScores(Ui_TabDb_RemoveDuplicateScores, QWidget): confirm = QMessageBox.warning( self, None, - # fmt: off - QCoreApplication.translate("TabDb_RemoveDuplicateScores", "deleteSelectionDialog.content {}").format(len(selectedScores)), - # fmt: on + QCoreApplication.translate( + "TabDb_RemoveDuplicateScores", "deleteSelectionDialog.content {}" + ).format(len(selectedScores)), QMessageBox.StandardButton.Yes, QMessageBox.StandardButton.No, - ) + ) # fmt: skip if confirm != QMessageBox.StandardButton.Yes: return @@ -310,12 +304,11 @@ class TabDb_RemoveDuplicateScores(Ui_TabDb_RemoveDuplicateScores, QWidget): @Slot() def on_scan_scanButton_clicked(self): if len(self.getQueryColumns()) <= 2: + message = QCoreApplication.translate("TabDb_RemoveDuplicateScores", "scan_noColumnsDialog.content") # fmt: skip result = QMessageBox.warning( self, None, - # fmt: off - QCoreApplication.translate("TabDb_RemoveDuplicateScores", "scan_noColumnsDialog.content"), - # fmt: on + message, QMessageBox.StandardButton.Yes, QMessageBox.StandardButton.No, ) diff --git a/ui/implements/tabs/tabOverview.py b/ui/implements/tabs/tabOverview.py index 4c99b19..67448b9 100644 --- a/ui/implements/tabs/tabOverview.py +++ b/ui/implements/tabs/tabOverview.py @@ -50,6 +50,4 @@ class TabOverview(Ui_TabOverview, QWidget): def retranslateUi(self, *args): super().retranslateUi(self) - # fmt: off - self.describeFormatString = QCoreApplication.translate("TabOverview", "databaseDescribeLabel {} {} {} {} {} {}") - # fmt: on + self.describeFormatString = QCoreApplication.translate("TabOverview", "databaseDescribeLabel {} {} {} {} {} {}") # fmt: skip diff --git a/ui/implements/tabs/tabTools/tabTools_Andreal.py b/ui/implements/tabs/tabTools/tabTools_Andreal.py index e72ad78..fad800c 100644 --- a/ui/implements/tabs/tabTools/tabTools_Andreal.py +++ b/ui/implements/tabs/tabTools/tabTools_Andreal.py @@ -131,11 +131,8 @@ class TabTools_Andreal(Ui_TabTools_Andreal, QWidget): @Slot() def on_imageTypeWhatIsThisButton_clicked(self): - QMessageBox.information( - self, - None, - QCoreApplication.translate("TabTools_Andreal", "imageWhatIsThisDialog.description"), - ) # fmt: skip + message = QCoreApplication.translate("TabTools_Andreal", "imageWhatIsThisDialog.description") # fmt: skip + QMessageBox.information(self, None, message) def imageFormat(self): buttonId = self.imageFormatButtonGroup.checkedId() diff --git a/ui/implements/tabs/tabTools/tabTools_StepCalculator.py b/ui/implements/tabs/tabTools/tabTools_StepCalculator.py index 14cd30b..d641b7f 100644 --- a/ui/implements/tabs/tabTools/tabTools_StepCalculator.py +++ b/ui/implements/tabs/tabTools/tabTools_StepCalculator.py @@ -90,10 +90,8 @@ class PlayRatingCalculatorDialog(QDialog): self.acceptButton = QPushButton(self) self.acceptButton.setText( - # fmt: off QCoreApplication.translate("StepCalculator", "playRatingCalculatorDialog.acceptButton") - # fmt: on - ) + ) # fmt: skip self.acceptButton.setEnabled(False) self.verticalLayout.addWidget(self.acceptButton) From 29eb135752d3a071c294ba99474e2cb8c4791d70 Mon Sep 17 00:00:00 2001 From: 283375 Date: Fri, 28 Jun 2024 22:24:57 +0800 Subject: [PATCH 7/7] log tag support --- index.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.py b/index.py index f853d6e..0fd2b39 100644 --- a/index.py +++ b/index.py @@ -43,9 +43,9 @@ if __name__ == "__main__": hms = now.strftime("%H%M%S") rootLoggerFormatter = logging.Formatter( - "[%(asctime)s/%(levelname)s]%(name)s: %(message)s", + "[%(asctime)s/%(levelname)s] %(name)s (%(tag)s): %(message)s", "%m-%d %H:%M:%S", - style="%", + defaults={"tag": "/"}, ) rootLoggerFileHandler = logging.FileHandler( str(logFolder / f"arcaea-offline-pyside-ui-{ymd}-{hms}_debug.log"),