mirror of
https://github.com/283375/arcaea-offline-pyside-ui.git
synced 2025-04-19 17:20:17 +00:00
Merge 29eb135752d3a071c294ba99474e2cb8c4791d70 into 0c6f4f496162211a3febd63cb400a33325a5c58a
This commit is contained in:
commit
4a9ee7b512
@ -4,11 +4,10 @@ repos:
|
|||||||
hooks:
|
hooks:
|
||||||
- id: end-of-file-fixer
|
- id: end-of-file-fixer
|
||||||
- id: trailing-whitespace
|
- 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:
|
hooks:
|
||||||
- id: black
|
- id: ruff
|
||||||
- repo: https://github.com/PyCQA/isort
|
args: ["--fix"]
|
||||||
rev: 5.12.0
|
- id: ruff-format
|
||||||
hooks:
|
|
||||||
- id: isort
|
|
||||||
|
0
core/__init__.py
Normal file
0
core/__init__.py
Normal file
10
core/color.py
Normal file
10
core/color.py
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
from PySide6.QtGui import QColor
|
||||||
|
|
||||||
|
|
||||||
|
def mixColor(source: QColor, mix: QColor, ratio: float = 0.5):
|
||||||
|
r = round((mix.red() - source.red()) * ratio + source.red())
|
||||||
|
g = round((mix.green() - source.green()) * ratio + source.green())
|
||||||
|
b = round((mix.blue() - source.blue()) * ratio + source.blue())
|
||||||
|
a = round((mix.alpha() - source.alpha()) * ratio + source.alpha())
|
||||||
|
|
||||||
|
return QColor(r, g, b, a)
|
5
core/settings/__init__.py
Normal file
5
core/settings/__init__.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
from .base import Settings, settings
|
||||||
|
from .keys import SettingsKeys
|
||||||
|
from .values import SettingsValues
|
||||||
|
|
||||||
|
__all__ = ["settings", "Settings", "SettingsKeys", "SettingsValues"]
|
44
core/settings/base.py
Normal file
44
core/settings/base.py
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import sys
|
||||||
|
from enum import Enum
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from PySide6.QtCore import QFileInfo, QSettings, Signal
|
||||||
|
|
||||||
|
from core.singleton import QSingleton
|
||||||
|
|
||||||
|
__all__ = ["Settings"]
|
||||||
|
|
||||||
|
TSettingsKey = str | Enum
|
||||||
|
|
||||||
|
|
||||||
|
class Settings(QSettings, metaclass=QSingleton):
|
||||||
|
updated = Signal(str)
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(
|
||||||
|
QFileInfo(sys.argv[0]).dir().absoluteFilePath("arcaea_offline.ini"),
|
||||||
|
QSettings.Format.IniFormat,
|
||||||
|
parent,
|
||||||
|
)
|
||||||
|
|
||||||
|
def __settingsKey(self, key: TSettingsKey) -> str:
|
||||||
|
if isinstance(key, Enum):
|
||||||
|
return self.__settingsKey(key.value)
|
||||||
|
|
||||||
|
if isinstance(key, str):
|
||||||
|
return key
|
||||||
|
|
||||||
|
raise TypeError(f"{key!r} is not a valid key")
|
||||||
|
|
||||||
|
def setValue(self, key: TSettingsKey, value: Any) -> None:
|
||||||
|
_key = self.__settingsKey(key)
|
||||||
|
|
||||||
|
super().setValue(_key, value)
|
||||||
|
self.updated.emit(_key)
|
||||||
|
|
||||||
|
def stringValue(self, key: TSettingsKey) -> str | None:
|
||||||
|
_key = self.__settingsKey(key)
|
||||||
|
return self.value(_key, None, type=str)
|
||||||
|
|
||||||
|
|
||||||
|
settings = Settings()
|
26
core/settings/keys.py
Normal file
26
core/settings/keys.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
from dataclasses import dataclass
|
||||||
|
from enum import StrEnum
|
||||||
|
|
||||||
|
|
||||||
|
class _General(StrEnum):
|
||||||
|
Language = "Language"
|
||||||
|
DatabaseUrl = "DatabaseUrl"
|
||||||
|
|
||||||
|
|
||||||
|
class _Ocr(StrEnum):
|
||||||
|
KnnModelFile = "Ocr/KnnModelFile"
|
||||||
|
B30KnnModelFile = "Ocr/B30KnnModelFile"
|
||||||
|
PhashDatabaseFile = "Ocr/PHashDatabaseFile"
|
||||||
|
DateSource = "Ocr/DateSource"
|
||||||
|
|
||||||
|
|
||||||
|
class _Andreal(StrEnum):
|
||||||
|
Folder = "Andreal/AndrealFolder"
|
||||||
|
Executable = "Andreal/AndrealExecutable"
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class SettingsKeys:
|
||||||
|
General = _General
|
||||||
|
Ocr = _Ocr
|
||||||
|
Andreal = _Andreal
|
17
core/settings/values.py
Normal file
17
core/settings/values.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class _Ocr_ScoreDateSource:
|
||||||
|
FileCreated: str = "FileCreated"
|
||||||
|
FileLastModified: str = "FileLastModified"
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class _Ocr:
|
||||||
|
DateSource = _Ocr_ScoreDateSource()
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class SettingsValues:
|
||||||
|
Ocr = _Ocr()
|
@ -14,5 +14,5 @@ class Singleton(type, Generic[T]):
|
|||||||
return cls._instance
|
return cls._instance
|
||||||
|
|
||||||
|
|
||||||
class QObjectSingleton(type(QObject), Singleton):
|
class QSingleton(type(QObject), Singleton):
|
||||||
pass
|
pass
|
30
index.py
30
index.py
@ -9,25 +9,21 @@ from PySide6.QtCore import QCoreApplication, QLocale
|
|||||||
from PySide6.QtGui import QFontDatabase, QIcon
|
from PySide6.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() if Settings().databaseUrl() else 0
|
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()
|
||||||
|
@ -24,19 +24,34 @@ classifiers = [
|
|||||||
"Homepage" = "https://github.com/ArcaeaOffline/client-pyside6"
|
"Homepage" = "https://github.com/ArcaeaOffline/client-pyside6"
|
||||||
"Bug Tracker" = "https://github.com/ArcaeaOffline/client-pyside6/issues"
|
"Bug Tracker" = "https://github.com/ArcaeaOffline/client-pyside6/issues"
|
||||||
|
|
||||||
[tool.black]
|
[tool.ruff]
|
||||||
force-exclude = '''
|
exclude = ["*_ui.py", "*_rc.py"]
|
||||||
(
|
|
||||||
ui/designer
|
|
||||||
| .*_ui.py
|
|
||||||
| .*_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 = ["*_ui.py", "*_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*.*"]
|
||||||
|
@ -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,7 +153,7 @@ class OcrQueueModel(QAbstractListModel):
|
|||||||
|
|
||||||
@iccOption.setter
|
@iccOption.setter
|
||||||
def iccOption(self, opt: IccOption):
|
def iccOption(self, opt: IccOption):
|
||||||
logger.debug(f"ICC option changed to {opt}")
|
logger.debug("ICC option changed to %s", opt)
|
||||||
self.__iccOption = opt
|
self.__iccOption = opt
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
@ -159,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(
|
||||||
@ -168,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,
|
||||||
@ -179,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:
|
||||||
@ -223,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)
|
||||||
@ -294,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):
|
||||||
@ -344,17 +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(
|
QCoreApplication.translate("OcrTableModel", "horizontalHeader.title.imagePreview"),
|
||||||
"OcrTableModel", "horizontalHeader.title.select"
|
|
||||||
),
|
|
||||||
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
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
from PySide6.QtGui import QColor
|
|
||||||
|
|
||||||
|
|
||||||
def mix_color(source_color: QColor, mix_color: QColor, mix_ratio: float = 0.5):
|
|
||||||
r = round((mix_color.red() - source_color.red()) * mix_ratio + source_color.red())
|
|
||||||
g = round(
|
|
||||||
(mix_color.green() - source_color.green()) * mix_ratio + source_color.green()
|
|
||||||
)
|
|
||||||
b = round(
|
|
||||||
(mix_color.blue() - source_color.blue()) * mix_ratio + source_color.blue()
|
|
||||||
)
|
|
||||||
a = round(
|
|
||||||
(mix_color.alpha() - source_color.alpha()) * mix_ratio + source_color.alpha()
|
|
||||||
)
|
|
||||||
|
|
||||||
return QColor(r, g, b, a)
|
|
@ -7,7 +7,7 @@ from typing import Literal, Optional, overload
|
|||||||
from arcaea_offline.models import Chart, Difficulty, Song
|
from 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):
|
||||||
|
@ -16,13 +16,11 @@ class DbB30TableModel(DbTableModel):
|
|||||||
|
|
||||||
def retranslateHeaders(self):
|
def retranslateHeaders(self):
|
||||||
self._horizontalHeaders = [
|
self._horizontalHeaders = [
|
||||||
# fmt: off
|
|
||||||
QCoreApplication.translate("DbB30TableModel", "horizontalHeader.id"),
|
QCoreApplication.translate("DbB30TableModel", "horizontalHeader.id"),
|
||||||
QCoreApplication.translate("DbB30TableModel", "horizontalHeader.chart"),
|
QCoreApplication.translate("DbB30TableModel", "horizontalHeader.chart"),
|
||||||
QCoreApplication.translate("DbB30TableModel", "horizontalHeader.score"),
|
QCoreApplication.translate("DbB30TableModel", "horizontalHeader.score"),
|
||||||
QCoreApplication.translate("DbB30TableModel", "horizontalHeader.potential"),
|
QCoreApplication.translate("DbB30TableModel", "horizontalHeader.potential"),
|
||||||
# fmt: on
|
] # fmt: skip
|
||||||
]
|
|
||||||
|
|
||||||
def syncDb(self):
|
def syncDb(self):
|
||||||
self.beginResetModel()
|
self.beginResetModel()
|
||||||
|
@ -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=...):
|
||||||
|
@ -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)
|
|
@ -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):
|
||||||
|
@ -19,9 +19,9 @@ 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
|
||||||
from ui.extends.shared.settings import Settings
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -70,8 +70,8 @@ def getImageDate(imagePath: str) -> QDateTime:
|
|||||||
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):
|
||||||
dateSource = Settings().scoreDateSource()
|
dateSource = settings.stringValue(SettingsKeys.Ocr.DateSource)
|
||||||
if dateSource == "lastModified":
|
if dateSource == SettingsValues.Ocr.DateSource.FileLastModified:
|
||||||
datetime = QFileInfo(imagePath).lastModified()
|
datetime = QFileInfo(imagePath).lastModified()
|
||||||
else:
|
else:
|
||||||
datetime = QFileInfo(imagePath).birthTime()
|
datetime = QFileInfo(imagePath).birthTime()
|
||||||
|
@ -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)
|
||||||
|
@ -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))
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
from PySide6.QtCore import Signal
|
from PySide6.QtCore import Signal
|
||||||
from PySide6.QtWidgets import QButtonGroup, QDialog
|
from PySide6.QtWidgets import QButtonGroup, QDialog
|
||||||
|
|
||||||
|
from core.settings import SettingsKeys, SettingsValues, settings
|
||||||
from ui.designer.components.ocrQueueOptionsDialog_ui import Ui_OcrQueueOptionsDialog
|
from ui.designer.components.ocrQueueOptionsDialog_ui import Ui_OcrQueueOptionsDialog
|
||||||
from ui.extends.shared.settings import Settings
|
|
||||||
|
|
||||||
|
|
||||||
class OcrQueueOptionsDialog(QDialog, Ui_OcrQueueOptionsDialog):
|
class OcrQueueOptionsDialog(QDialog, Ui_OcrQueueOptionsDialog):
|
||||||
@ -29,13 +29,12 @@ class OcrQueueOptionsDialog(QDialog, Ui_OcrQueueOptionsDialog):
|
|||||||
self.on_scoreDateSourceButtonGroup_buttonClicked
|
self.on_scoreDateSourceButtonGroup_buttonClicked
|
||||||
)
|
)
|
||||||
|
|
||||||
self.settings = Settings()
|
settings.updated.connect(self.syncCheckboxesFromSettings)
|
||||||
self.settings.updated.connect(self.syncCheckboxesFromSettings)
|
|
||||||
self.syncCheckboxesFromSettings()
|
self.syncCheckboxesFromSettings()
|
||||||
|
|
||||||
def syncCheckboxesFromSettings(self):
|
def syncCheckboxesFromSettings(self):
|
||||||
scoreDateSource = self.settings.scoreDateSource()
|
scoreDateSource = settings.stringValue(SettingsKeys.Ocr.DateSource)
|
||||||
if scoreDateSource == "lastModified":
|
if scoreDateSource == SettingsValues.Ocr.DateSource.FileLastModified:
|
||||||
self.dateUseModifyDateRadioButton.setChecked(True)
|
self.dateUseModifyDateRadioButton.setChecked(True)
|
||||||
else:
|
else:
|
||||||
self.dateUseCreationDateRadioButton.setChecked(True)
|
self.dateUseCreationDateRadioButton.setChecked(True)
|
||||||
@ -43,6 +42,8 @@ class OcrQueueOptionsDialog(QDialog, Ui_OcrQueueOptionsDialog):
|
|||||||
def on_scoreDateSourceButtonGroup_buttonClicked(self, button):
|
def on_scoreDateSourceButtonGroup_buttonClicked(self, button):
|
||||||
buttonId = self.scoreDateSourceButtonGroup.id(button)
|
buttonId = self.scoreDateSourceButtonGroup.id(button)
|
||||||
if buttonId == 1:
|
if buttonId == 1:
|
||||||
self.settings.setScoreDateSource("lastModified")
|
value = SettingsValues.Ocr.DateSource.FileLastModified
|
||||||
else:
|
else:
|
||||||
self.settings.setScoreDateSource("birthTime")
|
value = SettingsValues.Ocr.DateSource.FileCreated
|
||||||
|
|
||||||
|
settings.setValue(SettingsKeys.Ocr.DateSource, value)
|
||||||
|
@ -2,7 +2,7 @@ from PySide6.QtCore import Slot
|
|||||||
from PySide6.QtGui import QColor
|
from PySide6.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:
|
||||||
|
@ -126,7 +126,7 @@ class RatingClassSelector(QWidget):
|
|||||||
elif ratingClass in range(len(self.buttons)):
|
elif ratingClass in range(len(self.buttons)):
|
||||||
button = self.buttons[ratingClass]
|
button = self.buttons[ratingClass]
|
||||||
else:
|
else:
|
||||||
logger.debug(f"Cannot select {ratingClass=}, condition check failed")
|
logger.debug("Cannot select ratingClass=%s, condition check failed", ratingClass)
|
||||||
return
|
return
|
||||||
|
|
||||||
if not button.isEnabled():
|
if not button.isEnabled():
|
||||||
|
@ -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(
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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):
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -91,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):
|
||||||
@ -161,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"
|
||||||
@ -193,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)
|
||||||
@ -218,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)
|
||||||
|
@ -154,23 +154,17 @@ class TabDb_RemoveDuplicateScores(Ui_TabDb_RemoveDuplicateScores, QWidget):
|
|||||||
self.treeView.setItemDelegateForColumn(1, self.treeViewProxyDelegate)
|
self.treeView.setItemDelegateForColumn(1, self.treeViewProxyDelegate)
|
||||||
|
|
||||||
self.quickSelect_comboBox.addItem(
|
self.quickSelect_comboBox.addItem(
|
||||||
# fmt: off
|
|
||||||
QCoreApplication.translate("TabDb_RemoveDuplicateScores", "quickSelectComboBox.idEarlier"),
|
QCoreApplication.translate("TabDb_RemoveDuplicateScores", "quickSelectComboBox.idEarlier"),
|
||||||
# fmt: on
|
|
||||||
QuickSelectComboBoxValues.ID_EARLIER
|
QuickSelectComboBoxValues.ID_EARLIER
|
||||||
)
|
) # fmt: skip
|
||||||
self.quickSelect_comboBox.addItem(
|
self.quickSelect_comboBox.addItem(
|
||||||
# fmt: off
|
|
||||||
QCoreApplication.translate("TabDb_RemoveDuplicateScores", "quickSelectComboBox.dateEarlier"),
|
QCoreApplication.translate("TabDb_RemoveDuplicateScores", "quickSelectComboBox.dateEarlier"),
|
||||||
# fmt: on
|
|
||||||
QuickSelectComboBoxValues.DATE_EARLIER
|
QuickSelectComboBoxValues.DATE_EARLIER
|
||||||
)
|
) # fmt: skip
|
||||||
self.quickSelect_comboBox.addItem(
|
self.quickSelect_comboBox.addItem(
|
||||||
# fmt: off
|
|
||||||
QCoreApplication.translate("TabDb_RemoveDuplicateScores", "quickSelectComboBox.columnsIntegral"),
|
QCoreApplication.translate("TabDb_RemoveDuplicateScores", "quickSelectComboBox.columnsIntegral"),
|
||||||
# fmt: on
|
|
||||||
QuickSelectComboBoxValues.COLUMNS_INTEGRAL
|
QuickSelectComboBoxValues.COLUMNS_INTEGRAL
|
||||||
)
|
) # fmt: skip
|
||||||
|
|
||||||
def getQueryColumns(self):
|
def getQueryColumns(self):
|
||||||
columns: list[InstrumentedAttribute] = [Score.song_id, Score.rating_class]
|
columns: list[InstrumentedAttribute] = [Score.song_id, Score.rating_class]
|
||||||
@ -291,12 +285,12 @@ class TabDb_RemoveDuplicateScores(Ui_TabDb_RemoveDuplicateScores, QWidget):
|
|||||||
confirm = QMessageBox.warning(
|
confirm = QMessageBox.warning(
|
||||||
self,
|
self,
|
||||||
None,
|
None,
|
||||||
# fmt: off
|
QCoreApplication.translate(
|
||||||
QCoreApplication.translate("TabDb_RemoveDuplicateScores", "deleteSelectionDialog.content {}").format(len(selectedScores)),
|
"TabDb_RemoveDuplicateScores", "deleteSelectionDialog.content {}"
|
||||||
# fmt: on
|
).format(len(selectedScores)),
|
||||||
QMessageBox.StandardButton.Yes,
|
QMessageBox.StandardButton.Yes,
|
||||||
QMessageBox.StandardButton.No,
|
QMessageBox.StandardButton.No,
|
||||||
)
|
) # fmt: skip
|
||||||
if confirm != QMessageBox.StandardButton.Yes:
|
if confirm != QMessageBox.StandardButton.Yes:
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -310,12 +304,11 @@ class TabDb_RemoveDuplicateScores(Ui_TabDb_RemoveDuplicateScores, QWidget):
|
|||||||
@Slot()
|
@Slot()
|
||||||
def on_scan_scanButton_clicked(self):
|
def on_scan_scanButton_clicked(self):
|
||||||
if len(self.getQueryColumns()) <= 2:
|
if len(self.getQueryColumns()) <= 2:
|
||||||
|
message = QCoreApplication.translate("TabDb_RemoveDuplicateScores", "scan_noColumnsDialog.content") # fmt: skip
|
||||||
result = QMessageBox.warning(
|
result = QMessageBox.warning(
|
||||||
self,
|
self,
|
||||||
None,
|
None,
|
||||||
# fmt: off
|
message,
|
||||||
QCoreApplication.translate("TabDb_RemoveDuplicateScores", "scan_noColumnsDialog.content"),
|
|
||||||
# fmt: on
|
|
||||||
QMessageBox.StandardButton.Yes,
|
QMessageBox.StandardButton.Yes,
|
||||||
QMessageBox.StandardButton.No,
|
QMessageBox.StandardButton.No,
|
||||||
)
|
)
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -50,6 +50,4 @@ class TabOverview(Ui_TabOverview, QWidget):
|
|||||||
|
|
||||||
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
|
|
||||||
|
@ -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()
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -6,8 +6,8 @@ from arcaea_offline.database import Database
|
|||||||
from PySide6.QtCore import QCoreApplication, QDir, QFileInfo, QSysInfo, 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])
|
||||||
|
|
||||||
@ -79,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
|
||||||
|
|
||||||
@ -108,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)
|
||||||
@ -134,10 +134,8 @@ 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()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user