76 Commits

Author SHA1 Message Date
0c6f4f4961 chore: v0.3.9 2024-06-20 00:04:46 +08:00
10fb98d530 chore: upgrade dependencies 2024-06-20 00:03:19 +08:00
bf034d1397 ci: update build actions
* Switch to official Nuitka GitHub Actions

* Adding Linux build support

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

* Choose `birthTime`/`lastModified` for OCR score date source if the image EXIF fails
2024-06-19 22:18:25 +08:00
d5895fe230 chore: v0.3.8 2024-04-01 01:00:46 +08:00
cd2e3f51ca ci: update build actions 2024-04-01 01:00:27 +08:00
4a09dc210a Merge pull request #7 from ArcaeaOffline/fix-issue-6
fix: rating class selection logic
2024-03-24 16:40:24 +08:00
cc8ab11b78 fix: rating class selection logic 2024-03-24 16:17:48 +08:00
48c5682e55 Merge pull request #5 from ArcaeaOffline/fix-issue-4
fix: linux dbUrl issue
2024-03-23 19:08:23 +08:00
ee03770764 chore: update README 2024-03-23 18:21:48 +08:00
b45c7f7de5 chore: dependencies 2024-03-23 18:18:53 +08:00
15bc56e6f9 fix: linux dbUrl issue 2024-03-23 17:41:36 +08:00
39ee379010 feat: ETERNAL rating class support 2024-03-20 15:52:26 +08:00
5a71a5822b feat: sync chart info database 2024-03-16 02:14:47 +08:00
c888b312b3 feat: DEF v2 scores export support 2024-02-27 17:24:49 +08:00
8e4fdc30b5 refactor(ui): TabDb_Manage 2024-02-15 17:59:14 +08:00
1ca868cfc6 ci: get full repo history for VERSION generating 2023-11-09 21:53:55 +08:00
d63d2f0d8b ci: build actions improve 2023-11-01 21:30:31 +08:00
3cd187fde3 ci: github actions 2023-11-01 20:00:16 +08:00
cce918a121 chore: update dependencies 2023-11-01 15:57:11 +08:00
1ec302d98c Merge branch 'master' of github.com:283375/arcaea-offline-pyside-ui 2023-10-29 17:13:52 +08:00
3d6e5f997e pre-commit 2023-10-29 00:12:01 +08:00
495f6dc424 impr: TabDb_RemoveDuplicateScores chart selecting 2023-10-25 20:04:23 +08:00
0b599e3d9c fix: ensure database reset works 2023-10-25 18:58:13 +08:00
a51a67fae3 impr: minor improvements 2023-10-25 17:53:21 +08:00
b48e177ae8 feat: TabDb_RemoveDuplicateScores 2023-10-25 17:41:40 +08:00
865fc8b7c8 style: isort & black ignore files 2023-10-23 23:51:29 +08:00
1eeec6f745 wip: TabDb_RemoveDuplicateScores ui 2023-10-23 23:51:11 +08:00
8558f5e403 impr: handle exceptions in TabOverview 2023-10-23 16:18:35 +08:00
1a37310091 impr: TabTools_Andreal source code link 2023-10-23 16:15:26 +08:00
d460e935b4 fix: DbB30TableModel 2023-10-23 16:08:51 +08:00
38d2e4ad5a fix: translation file extraction script 2023-10-23 16:08:11 +08:00
28599cfb04 feat: DatabaseChecker re-init database button 2023-10-23 15:31:56 +08:00
1d01356327 impr: popup PlayRatingCalculator when double clicking an item in TabTools_ChartRecommend 2023-10-23 15:19:56 +08:00
738975a83d chore: translations 2023-10-23 14:39:46 +08:00
5adea908f9 impr: PlayRatingCalculator ui 2023-10-23 14:39:36 +08:00
21ca1018db impr: translation file extraction script 2023-10-23 14:39:11 +08:00
51e15c68e0 chore: dependencies 2023-10-23 13:28:42 +08:00
00f680edd3 fix: cv2.Mat type annotation 2023-10-23 13:28:33 +08:00
7dee8114bf impr: prebuild update 2023-10-23 13:28:06 +08:00
90e66a43fe impr: log Andreal executable output 2023-10-23 10:16:19 +08:00
381f27db87 fix: crop black edges before ocr 2023-10-22 01:57:18 +08:00
e0d92b7784 impr: TabTools_StepCalculator ui 2023-10-22 01:17:16 +08:00
5aedf91c09 impr: ChartInfoEditor tap stop order 2023-10-22 00:51:21 +08:00
b193b82d95 feat: PlayRatingCalculator component 2023-10-22 00:51:07 +08:00
01457f3559 feat: SmartRTE csv export 2023-10-21 23:03:59 +08:00
dd647d6963 fix: DbScoreTableModel score committing 2023-10-21 18:59:42 +08:00
7de2eda517 impr: score validation 2023-10-21 18:58:30 +08:00
d918032b9c impr: log uncaught exceptions 2023-10-21 14:00:27 +08:00
55ef2ba3bb feat: import from Online 2023-10-18 01:26:12 +08:00
105d5c1dfb impr: refine DatabaseUpdateSignals 2023-10-17 22:47:16 +08:00
52e618e664 impr: DbScoreTableModel database access 2023-10-17 19:58:14 +08:00
b1af1f622e fix: DbScoreTableModel score deletion 2023-10-17 19:49:05 +08:00
7271eaab55 fix: ScoreDelegate empty value checking when validating score 2023-10-17 19:09:50 +08:00
263386e2f1 feat: TabTools_ChartInfoEditor 2023-10-16 01:16:11 +08:00
dc795f739e feat: Geo Sans Light font
from https://www.dafont.com/geo-sans-light.font
2023-10-15 16:48:59 +08:00
6b28de247e impr: minor improvements 2023-10-15 03:14:22 +08:00
86b1653fe3 impr: minor improvements 2023-10-15 02:47:15 +08:00
3e2e96b00b impr: TabTools_ChartRecommend ui 2023-10-15 00:24:17 +08:00
9bb6f5b3d9 fix: ChartSelector not selecting rating class 2023-10-14 17:58:35 +08:00
8628399469 impr: TextSegmentDelegate improvements 2023-10-14 16:52:41 +08:00
cf913d296e impr: minor improvement 2023-10-14 00:55:12 +08:00
1060590e03 wip: show song jacket in ChartDelegate 2023-10-14 00:55:01 +08:00
858abe3415 refactor: TabOcr_B30 2023-10-13 20:15:16 +08:00
cd4ed51826 chore: minor improvements 2023-10-12 18:12:35 +08:00
ad5e5ec694 wip: arcaea-offline-ocr==0.1.0
settings
2023-10-12 17:37:55 +08:00
5c5c1a227d wip: arcaea-offline-ocr==0.1.0
API changes, modifier & clear_type support
2023-10-12 17:05:04 +08:00
cde8a047a7 feat: show modifier and clear_type in ScoreDelegate 2023-10-10 22:10:42 +08:00
19cd526814 fix: correct phash database labels 2023-10-10 19:50:59 +08:00
8b6f64e041 wip: arcaea-offline-ocr==0.1.0
ui changes
2023-10-10 19:50:27 +08:00
6dbb7cbfef impr: use QSignalMapper for SongIdSelector's quick switch actions 2023-10-10 18:38:48 +08:00
94e4d73a95 impr: TabOcr_BuildPHashDatabase 2023-10-10 01:26:20 +08:00
4a1e20a45f feat: TabOcr_BuildPHashDatabase 2023-10-09 22:48:08 +08:00
de8c5d28a7 fix: use subprocess instead of os.popen for andreal calling 2023-10-01 02:47:16 +08:00
bce48a03a7 fix: write database url into settings 2023-09-28 17:59:33 +08:00
92 changed files with 7100 additions and 2315 deletions

View File

@ -0,0 +1,65 @@
name: Build Executable from latest `arcaea-offline-*` dependencies
run-name: ${{ github.actor }} started a build request.
on:
workflow_dispatch:
permissions:
contents: write
discussions: write
jobs:
build:
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
fail-fast: false
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-python@v5
with:
python-version: "3.11"
cache: "pip"
# install dependencies
- run: "pip install -r requirements.txt"
- run: "pip uninstall arcaea-offline arcaea-offline-ocr -y"
- run: "pip install git+https://github.com/283375/arcaea-offline"
- run: "pip install git+https://github.com/283375/arcaea-offline-ocr"
- run: "pip install imageio"
- name: Install UPX
uses: crazy-max/ghaction-upx@v3
with:
install-only: true
- name: Release builtin files
run: |
pyside6-lrelease ui/resources/lang/en_US.ts ui/resources/lang/zh_CN.ts
python prebuild.py
pyside6-rcc ui/resources/resources.qrc -o ui/resources/resources_rc.py
- name: Build Executable
uses: Nuitka/Nuitka-Action@main
with:
nuitka-version: main
script-name: index.py
standalone: true
onefile: true
enable-plugins: pyside6,upx
windows-icon-from-ico: ui/resources/images/icon.png
linux-icon: ui/resources/images/icon.png
- name: Upload Artifacts
uses: actions/upload-artifact@v4
with:
name: ${{ runner.os }} Build
path: |
build/*.exe
build/*.bin
build/*.app/**/*

78
.github/workflows/build.yml vendored Normal file
View File

@ -0,0 +1,78 @@
name: Build Executable
run-name: ${{ github.actor }} started a build request.
on:
workflow_dispatch:
push:
tags:
- "v[0-9]+.[0-9]+.[0-9]+"
permissions:
contents: write
discussions: write
jobs:
build:
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
fail-fast: false
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-python@v5
with:
python-version: "3.11"
cache: "pip"
- name: Install dependencies
run: |
pip install -r requirements.txt
pip install imageio
- name: Install UPX
uses: crazy-max/ghaction-upx@v3
with:
install-only: true
- name: Release builtin files
run: |
pyside6-lrelease ui/resources/lang/en_US.ts ui/resources/lang/zh_CN.ts
python prebuild.py
pyside6-rcc ui/resources/resources.qrc -o ui/resources/resources_rc.py
- name: Build Executable
uses: Nuitka/Nuitka-Action@main
with:
nuitka-version: main
script-name: index.py
standalone: true
onefile: true
enable-plugins: pyside6,upx
windows-icon-from-ico: ui/resources/images/icon.png
linux-icon: ui/resources/images/icon.png
- name: Upload Artifacts
uses: actions/upload-artifact@v4
with:
name: ${{ runner.os }} Build
path: |
build/*.exe
build/*.bin
build/*.app/**/*
- name: Draft a release
uses: softprops/action-gh-release@v2
with:
discussion_category_name: New releases
draft: true
generate_release_notes: true
files: |
build/*.exe
build/*.bin
build/*.app/**/*

1
.gitignore vendored
View File

@ -3,6 +3,7 @@ __debug*
arcaea_offline.db
arcaea_offline.ini
/data
ui/resources/VERSION

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

@ -0,0 +1,14 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/psf/black
rev: 23.1.0
hooks:
- id: black
- repo: https://github.com/PyCQA/isort
rev: 5.12.0
hooks:
- id: isort

View File

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

View File

@ -6,7 +6,7 @@ from pathlib import Path
from arcaea_offline.database import Database
from PySide6.QtCore import QCoreApplication, QLocale
from PySide6.QtGui import QIcon
from PySide6.QtGui import QFontDatabase, QIcon
from PySide6.QtWidgets import QApplication, QDialog, QMessageBox
import ui.resources.resources_rc
@ -23,6 +23,19 @@ rootLoggerFormatter = logging.Formatter(
)
def handle_exception(exc_type, exc_value, exc_traceback):
if issubclass(exc_type, KeyboardInterrupt):
sys.__excepthook__(exc_type, exc_value, exc_traceback)
return
rootLogger.critical(
"Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback)
)
sys.excepthook = handle_exception
if __name__ == "__main__":
QCoreApplication.setApplicationName("Arcaea Offline")
@ -51,9 +64,11 @@ if __name__ == "__main__":
)
changeAppLanguage(locale)
QFontDatabase.addApplicationFont(":/fonts/GeosansLight.ttf")
databaseChecker = DatabaseChecker()
databaseChecker.setWindowIcon(QIcon(":/images/icon.png"))
databaseCheckResult = databaseChecker.confirmDb()
databaseCheckResult = databaseChecker.confirmDb() if Settings().databaseUrl() else 0
if not databaseCheckResult & DatabaseCheckerResult.Initted:
result = databaseChecker.exec()

View File

@ -1,70 +1,86 @@
import os
import platform
import subprocess
from importlib import metadata
from pathlib import Path
# fill VERSION file
versionFile = Path("ui/resources/VERSION")
assert versionFile.exists()
versionTexts = []
def getGitDesc():
gitDescribe = subprocess.run(
["git", "describe", "--tags", "--long"],
capture_output=True,
encoding="utf-8",
)
if gitDescribe.returncode == 0:
return gitDescribe.stdout.replace("\n", "")
projectVersionText = "arcaea-offline-pyside-ui\n"
gitDescribe = os.popen("git describe --tags --long")
gitDescribeContent = gitDescribe.read().replace("\n", "")
if gitDescribe.close() is None:
projectVersionText += f"{gitDescribeContent}"
else:
gitRevParse = os.popen("git rev-parse --short HEAD")
gitRevParseContent = gitRevParse.read().replace("\n", "")
projectVersionText += f"commit {gitRevParseContent}"
gitRevParse.close()
projectVersionText += "\n"
# describe failed, try rev-parse
gitRevParse = subprocess.run(
["git", "rev-parse", "--short", "HEAD"],
capture_output=True,
encoding="utf-8",
)
if gitRevParse.returncode == 0:
return f"commit {gitRevParse.stdout}".replace("\n", "")
versionTexts.append(projectVersionText)
return "version/commit unknown"
# detect pip
pipName = None
possiblePipNames = ["pip3", "pip"]
for possiblePipName in possiblePipNames:
result = os.popen(possiblePipName).read()
if (
"<command> [options]" in result
and "install" in result
and "--upgrade" in result
):
pipName = possiblePipName
break
def getBuildToolsVer():
texts = []
possibleBuildTools = ["Nuitka", "pyinstaller"]
for possibleBuildTool in possibleBuildTools:
try:
version = metadata.version(possibleBuildTool)
texts.append(f"{possibleBuildTool}=={version}")
except metadata.PackageNotFoundError:
texts.append(f"{possibleBuildTool} not installed")
return ", ".join(texts)
# if possiblePipName:
# pipFreezeLines = os.popen(f"{possiblePipName} freeze").read().split("\n")
# text = [
# pipFreezeResult
# for pipFreezeResult in pipFreezeLines
# if (
# "arcaea-offline" in pipFreezeResult
# or "PySide6" in pipFreezeResult
# or "exif" in pipFreezeResult
# or "opencv-python" in pipFreezeResult
# or "SQLAlchemy" in pipFreezeResult
# )
# ]
# versionTexts.append("\n".join(text))
def writeVersionFile():
versionFile = Path("ui/resources/VERSION")
importLibTexts = [
f"{module}=={metadata.version(module)}"
for module in [
"arcaea-offline",
"arcaea-offline-ocr",
"exif",
"opencv-python",
"PySide6",
"SQLAlchemy",
"SQLAlchemy-Utils",
versionText = (
"arcaea-offline-pyside-ui\n{gitDesc}\n{buildToolsVer}\n\n"
"{pythonVer}\n\n"
"{depsVer}\n"
)
gitDesc = getGitDesc()
buildToolsVer = getBuildToolsVer()
pythonVer = f"{platform.python_implementation()} {platform.python_version()} ({platform.python_build()[0]})"
importLibTexts = [
f"{module}=={metadata.version(module)}"
for module in sorted(
[
"arcaea-offline",
"arcaea-offline-ocr",
"exif",
"numpy",
"opencv-python",
"Pillow",
"PySide6",
"SQLAlchemy",
"SQLAlchemy-Utils",
"Whoosh",
],
key=lambda s: s.lower(),
)
]
]
versionTexts.append("\n".join(importLibTexts))
importLibText = "\n".join(importLibTexts)
with versionFile.open("w", encoding="utf-8") as vf:
vf.write("\n".join(versionTexts))
with versionFile.open("w", encoding="utf-8") as vf:
vf.write(
versionText.format(
gitDesc=gitDesc,
buildToolsVer=buildToolsVer,
pythonVer=pythonVer,
depsVer=importLibText,
)
)
writeVersionFile()

View File

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

View File

@ -1,2 +1,4 @@
black == 23.7.0
isort == 5.12.0
imageio==2.31.4
Nuitka==1.8.4

View File

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

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>476</width>
<height>347</height>
<height>93</height>
</rect>
</property>
<property name="windowTitle">
@ -16,12 +16,6 @@
<layout class="QVBoxLayout" name="mainVerticalLayout">
<item>
<widget class="QGroupBox" name="songIdSelectorGroupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>songIdSelector.title</string>
</property>

View File

@ -25,17 +25,12 @@ class Ui_ChartSelector(object):
def setupUi(self, ChartSelector):
if not ChartSelector.objectName():
ChartSelector.setObjectName(u"ChartSelector")
ChartSelector.resize(476, 347)
ChartSelector.resize(476, 93)
ChartSelector.setWindowTitle(u"ChartSelector")
self.mainVerticalLayout = QVBoxLayout(ChartSelector)
self.mainVerticalLayout.setObjectName(u"mainVerticalLayout")
self.songIdSelectorGroupBox = QGroupBox(ChartSelector)
self.songIdSelectorGroupBox.setObjectName(u"songIdSelectorGroupBox")
sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.songIdSelectorGroupBox.sizePolicy().hasHeightForWidth())
self.songIdSelectorGroupBox.setSizePolicy(sizePolicy)
self.verticalLayout_3 = QVBoxLayout(self.songIdSelectorGroupBox)
self.verticalLayout_3.setObjectName(u"verticalLayout_3")
self.verticalLayout_3.setContentsMargins(0, 0, 0, 0)
@ -56,11 +51,11 @@ class Ui_ChartSelector(object):
self.resultsHorizontalLayout.setObjectName(u"resultsHorizontalLayout")
self.resultLabel = QLabel(ChartSelector)
self.resultLabel.setObjectName(u"resultLabel")
sizePolicy1 = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
sizePolicy1.setHorizontalStretch(0)
sizePolicy1.setVerticalStretch(0)
sizePolicy1.setHeightForWidth(self.resultLabel.sizePolicy().hasHeightForWidth())
self.resultLabel.setSizePolicy(sizePolicy1)
sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.resultLabel.sizePolicy().hasHeightForWidth())
self.resultLabel.setSizePolicy(sizePolicy)
self.resultLabel.setText(u"...")
self.resultLabel.setTextFormat(Qt.RichText)

View File

@ -22,39 +22,6 @@
<string>queue.title</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>iccOptionsGroupBox</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QRadioButton" name="iccIgnoreRadioButton">
<property name="text">
<string>icc.ignore</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="iccUsePILRadioButton">
<property name="text">
<string>icc.usePIL</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="iccTryFixRadioButton">
<property name="text">
<string>icc.tryFix</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QPushButton" name="ocr_addImageButton">
<property name="text">
@ -95,6 +62,13 @@
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="optionsDialogButton">
<property name="text">
<string>queue.optionsButton</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="ocr_startButton">
<property name="text">

View File

@ -0,0 +1,140 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>OcrQueueOptionsDialog</class>
<widget class="QDialog" name="OcrQueueOptionsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>331</width>
<height>157</height>
</rect>
</property>
<property name="windowTitle">
<string>OCR Options</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>iccOptionsGroupBox</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QRadioButton" name="iccUseQtRadioButton">
<property name="text">
<string>icc.useQt</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="iccUsePILRadioButton">
<property name="text">
<string>icc.usePIL</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="iccTryFixRadioButton">
<property name="text">
<string>icc.tryFix</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>dateOptionsGroupBox</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QCheckBox" name="dateReadFromExifCheckbox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>date.readFromExif</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="dateUseCreationDateRadioButton">
<property name="text">
<string>date.useCreationDate</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="dateUseModifyDateRadioButton">
<property name="text">
<string>date.useModifyDate</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>OcrQueueOptionsDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>OcrQueueOptionsDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -0,0 +1,107 @@
# -*- coding: utf-8 -*-
################################################################################
## Form generated from reading UI file 'ocrQueueOptionsDialog.ui'
##
## Created by: Qt User Interface Compiler version 6.5.2
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
QMetaObject, QObject, QPoint, QRect,
QSize, QTime, QUrl, Qt)
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
QFont, QFontDatabase, QGradient, QIcon,
QImage, QKeySequence, QLinearGradient, QPainter,
QPalette, QPixmap, QRadialGradient, QTransform)
from PySide6.QtWidgets import (QAbstractButton, QApplication, QCheckBox, QDialog,
QDialogButtonBox, QGroupBox, QHBoxLayout, QRadioButton,
QSizePolicy, QVBoxLayout, QWidget)
class Ui_OcrQueueOptionsDialog(object):
def setupUi(self, OcrQueueOptionsDialog):
if not OcrQueueOptionsDialog.objectName():
OcrQueueOptionsDialog.setObjectName(u"OcrQueueOptionsDialog")
OcrQueueOptionsDialog.resize(331, 157)
self.verticalLayout = QVBoxLayout(OcrQueueOptionsDialog)
self.verticalLayout.setObjectName(u"verticalLayout")
self.horizontalLayout = QHBoxLayout()
self.horizontalLayout.setObjectName(u"horizontalLayout")
self.groupBox = QGroupBox(OcrQueueOptionsDialog)
self.groupBox.setObjectName(u"groupBox")
self.verticalLayout_2 = QVBoxLayout(self.groupBox)
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
self.iccUseQtRadioButton = QRadioButton(self.groupBox)
self.iccUseQtRadioButton.setObjectName(u"iccUseQtRadioButton")
self.verticalLayout_2.addWidget(self.iccUseQtRadioButton)
self.iccUsePILRadioButton = QRadioButton(self.groupBox)
self.iccUsePILRadioButton.setObjectName(u"iccUsePILRadioButton")
self.iccUsePILRadioButton.setChecked(True)
self.verticalLayout_2.addWidget(self.iccUsePILRadioButton)
self.iccTryFixRadioButton = QRadioButton(self.groupBox)
self.iccTryFixRadioButton.setObjectName(u"iccTryFixRadioButton")
self.verticalLayout_2.addWidget(self.iccTryFixRadioButton)
self.horizontalLayout.addWidget(self.groupBox)
self.groupBox_2 = QGroupBox(OcrQueueOptionsDialog)
self.groupBox_2.setObjectName(u"groupBox_2")
self.verticalLayout_3 = QVBoxLayout(self.groupBox_2)
self.verticalLayout_3.setObjectName(u"verticalLayout_3")
self.dateReadFromExifCheckbox = QCheckBox(self.groupBox_2)
self.dateReadFromExifCheckbox.setObjectName(u"dateReadFromExifCheckbox")
self.dateReadFromExifCheckbox.setEnabled(False)
self.dateReadFromExifCheckbox.setChecked(True)
self.verticalLayout_3.addWidget(self.dateReadFromExifCheckbox)
self.dateUseCreationDateRadioButton = QRadioButton(self.groupBox_2)
self.dateUseCreationDateRadioButton.setObjectName(u"dateUseCreationDateRadioButton")
self.dateUseCreationDateRadioButton.setChecked(True)
self.verticalLayout_3.addWidget(self.dateUseCreationDateRadioButton)
self.dateUseModifyDateRadioButton = QRadioButton(self.groupBox_2)
self.dateUseModifyDateRadioButton.setObjectName(u"dateUseModifyDateRadioButton")
self.verticalLayout_3.addWidget(self.dateUseModifyDateRadioButton)
self.horizontalLayout.addWidget(self.groupBox_2)
self.verticalLayout.addLayout(self.horizontalLayout)
self.buttonBox = QDialogButtonBox(OcrQueueOptionsDialog)
self.buttonBox.setObjectName(u"buttonBox")
self.buttonBox.setOrientation(Qt.Horizontal)
self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel|QDialogButtonBox.Ok)
self.verticalLayout.addWidget(self.buttonBox)
self.retranslateUi(OcrQueueOptionsDialog)
self.buttonBox.accepted.connect(OcrQueueOptionsDialog.accept)
self.buttonBox.rejected.connect(OcrQueueOptionsDialog.reject)
QMetaObject.connectSlotsByName(OcrQueueOptionsDialog)
# setupUi
def retranslateUi(self, OcrQueueOptionsDialog):
OcrQueueOptionsDialog.setWindowTitle(QCoreApplication.translate("OcrQueueOptionsDialog", u"OCR Options", None))
self.groupBox.setTitle(QCoreApplication.translate("OcrQueueOptionsDialog", u"iccOptionsGroupBox", None))
self.iccUseQtRadioButton.setText(QCoreApplication.translate("OcrQueueOptionsDialog", u"icc.useQt", None))
self.iccUsePILRadioButton.setText(QCoreApplication.translate("OcrQueueOptionsDialog", u"icc.usePIL", None))
self.iccTryFixRadioButton.setText(QCoreApplication.translate("OcrQueueOptionsDialog", u"icc.tryFix", None))
self.groupBox_2.setTitle(QCoreApplication.translate("OcrQueueOptionsDialog", u"dateOptionsGroupBox", None))
self.dateReadFromExifCheckbox.setText(QCoreApplication.translate("OcrQueueOptionsDialog", u"date.readFromExif", None))
self.dateUseCreationDateRadioButton.setText(QCoreApplication.translate("OcrQueueOptionsDialog", u"date.useCreationDate", None))
self.dateUseModifyDateRadioButton.setText(QCoreApplication.translate("OcrQueueOptionsDialog", u"date.useModifyDate", None))
# retranslateUi

View File

@ -17,8 +17,8 @@ from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
QPalette, QPixmap, QRadialGradient, QTransform)
from PySide6.QtWidgets import (QAbstractItemView, QApplication, QCheckBox, QGroupBox,
QHBoxLayout, QHeaderView, QLabel, QProgressBar,
QPushButton, QRadioButton, QSizePolicy, QSpacerItem,
QTableView, QVBoxLayout, QWidget)
QPushButton, QSizePolicy, QSpacerItem, QTableView,
QVBoxLayout, QWidget)
class Ui_OcrQueue(object):
def setupUi(self, OcrQueue):
@ -34,29 +34,6 @@ class Ui_OcrQueue(object):
self.groupBox_3.setObjectName(u"groupBox_3")
self.verticalLayout_2 = QVBoxLayout(self.groupBox_3)
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
self.groupBox = QGroupBox(self.groupBox_3)
self.groupBox.setObjectName(u"groupBox")
self.verticalLayout = QVBoxLayout(self.groupBox)
self.verticalLayout.setObjectName(u"verticalLayout")
self.iccIgnoreRadioButton = QRadioButton(self.groupBox)
self.iccIgnoreRadioButton.setObjectName(u"iccIgnoreRadioButton")
self.verticalLayout.addWidget(self.iccIgnoreRadioButton)
self.iccUsePILRadioButton = QRadioButton(self.groupBox)
self.iccUsePILRadioButton.setObjectName(u"iccUsePILRadioButton")
self.iccUsePILRadioButton.setChecked(True)
self.verticalLayout.addWidget(self.iccUsePILRadioButton)
self.iccTryFixRadioButton = QRadioButton(self.groupBox)
self.iccTryFixRadioButton.setObjectName(u"iccTryFixRadioButton")
self.verticalLayout.addWidget(self.iccTryFixRadioButton)
self.verticalLayout_2.addWidget(self.groupBox)
self.ocr_addImageButton = QPushButton(self.groupBox_3)
self.ocr_addImageButton.setObjectName(u"ocr_addImageButton")
@ -78,6 +55,11 @@ class Ui_OcrQueue(object):
self.verticalLayout_2.addItem(self.verticalSpacer)
self.optionsDialogButton = QPushButton(self.groupBox_3)
self.optionsDialogButton.setObjectName(u"optionsDialogButton")
self.verticalLayout_2.addWidget(self.optionsDialogButton)
self.ocr_startButton = QPushButton(self.groupBox_3)
self.ocr_startButton.setObjectName(u"ocr_startButton")
@ -154,13 +136,10 @@ class Ui_OcrQueue(object):
def retranslateUi(self, OcrQueue):
self.groupBox_3.setTitle(QCoreApplication.translate("OcrQueue", u"queue.title", None))
self.groupBox.setTitle(QCoreApplication.translate("OcrQueue", u"iccOptionsGroupBox", None))
self.iccIgnoreRadioButton.setText(QCoreApplication.translate("OcrQueue", u"icc.ignore", None))
self.iccUsePILRadioButton.setText(QCoreApplication.translate("OcrQueue", u"icc.usePIL", None))
self.iccTryFixRadioButton.setText(QCoreApplication.translate("OcrQueue", u"icc.tryFix", None))
self.ocr_addImageButton.setText(QCoreApplication.translate("OcrQueue", u"queue.addImageButton", None))
self.ocr_removeSelectedButton.setText(QCoreApplication.translate("OcrQueue", u"queue.removeSelected", None))
self.ocr_removeAllButton.setText(QCoreApplication.translate("OcrQueue", u"queue.removeAll", None))
self.optionsDialogButton.setText(QCoreApplication.translate("OcrQueue", u"queue.optionsButton", None))
self.ocr_startButton.setText(QCoreApplication.translate("OcrQueue", u"queue.startOcrButton", None))
self.groupBox_5.setTitle(QCoreApplication.translate("OcrQueue", u"results", None))
self.ocr_acceptSelectedButton.setText(QCoreApplication.translate("OcrQueue", u"results.acceptSelectedButton", None))
@ -169,4 +148,3 @@ class Ui_OcrQueue(object):
self.statusLabel.setText("")
pass
# retranslateUi

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>350</width>
<height>250</height>
<height>102</height>
</rect>
</property>
<property name="windowTitle">
@ -27,19 +27,6 @@
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>

View File

@ -16,14 +16,13 @@ from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
QImage, QKeySequence, QLinearGradient, QPainter,
QPalette, QPixmap, QRadialGradient, QTransform)
from PySide6.QtWidgets import (QApplication, QComboBox, QHBoxLayout, QLineEdit,
QPushButton, QSizePolicy, QSpacerItem, QVBoxLayout,
QWidget)
QPushButton, QSizePolicy, QVBoxLayout, QWidget)
class Ui_SongIdSelector(object):
def setupUi(self, SongIdSelector):
if not SongIdSelector.objectName():
SongIdSelector.setObjectName(u"SongIdSelector")
SongIdSelector.resize(350, 250)
SongIdSelector.resize(350, 102)
SongIdSelector.setWindowTitle(u"SongIdSelector")
self.verticalLayout_2 = QVBoxLayout(SongIdSelector)
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
@ -34,10 +33,6 @@ class Ui_SongIdSelector(object):
self.verticalLayout_2.addWidget(self.searchLineEdit)
self.verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
self.verticalLayout_2.addItem(self.verticalSpacer)
self.horizontalLayout_2 = QHBoxLayout()
self.horizontalLayout_2.setObjectName(u"horizontalLayout_2")
self.previousPackageButton = QPushButton(SongIdSelector)

View File

@ -0,0 +1,270 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TabDb_ChartInfoEditor</class>
<widget class="QWidget" name="TabDb_ChartInfoEditor">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>659</width>
<height>570</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">TabDb_ChartInfoEditor</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="1">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>editor.title</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QWidget" name="widget" native="true">
<layout class="QFormLayout" name="formLayout">
<property name="labelAlignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>editor.constant</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>editor.notes</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="notesLineEdit">
<property name="font">
<font>
<family>GeosansLight</family>
<pointsize>14</pointsize>
<bold>true</bold>
</font>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="jacketLabel">
<property name="minimumSize">
<size>
<width>100</width>
<height>100</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>100</width>
<height>100</height>
</size>
</property>
<property name="text">
<string notr="true"/>
</property>
</widget>
</item>
<item row="0" column="1">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="titleLabel">
<property name="text">
<string notr="true">...</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="ratingLabel">
<property name="text">
<string notr="true">...</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="1" column="1">
<widget class="QWidget" name="horizontalWidget_2" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLineEdit" name="constantLineEdit">
<property name="font">
<font>
<family>GeosansLight</family>
<pointsize>14</pointsize>
<bold>true</bold>
</font>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="constantPreviewLabel">
<property name="text">
<string notr="true">&gt; ...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_3">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>editor.tip</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="label_4">
<property name="text">
<string>editor.tip.content</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="deleteButton">
<property name="text">
<string>editor.delete</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="commitButton">
<property name="text">
<string>editor.commit</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="ChartSelector" name="chartSelector" native="true"/>
</item>
<item row="1" column="0">
<widget class="QListView" name="listView">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="verticalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
<property name="horizontalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>ChartSelector</class>
<extends>QWidget</extends>
<header>ui.implements.components.chartSelector</header>
<container>1</container>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>listView</tabstop>
<tabstop>constantLineEdit</tabstop>
<tabstop>notesLineEdit</tabstop>
<tabstop>commitButton</tabstop>
<tabstop>deleteButton</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,202 @@
# -*- coding: utf-8 -*-
################################################################################
## Form generated from reading UI file 'tabDb_ChartInfoEditor.ui'
##
## Created by: Qt User Interface Compiler version 6.5.2
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
QMetaObject, QObject, QPoint, QRect,
QSize, QTime, QUrl, Qt)
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
QFont, QFontDatabase, QGradient, QIcon,
QImage, QKeySequence, QLinearGradient, QPainter,
QPalette, QPixmap, QRadialGradient, QTransform)
from PySide6.QtWidgets import (QAbstractItemView, QApplication, QFormLayout, QGridLayout,
QGroupBox, QHBoxLayout, QLabel, QLineEdit,
QListView, QPushButton, QSizePolicy, QSpacerItem,
QVBoxLayout, QWidget)
from ui.implements.components.chartSelector import ChartSelector
class Ui_TabDb_ChartInfoEditor(object):
def setupUi(self, TabDb_ChartInfoEditor):
if not TabDb_ChartInfoEditor.objectName():
TabDb_ChartInfoEditor.setObjectName(u"TabDb_ChartInfoEditor")
TabDb_ChartInfoEditor.resize(659, 570)
TabDb_ChartInfoEditor.setWindowTitle(u"TabDb_ChartInfoEditor")
self.gridLayout = QGridLayout(TabDb_ChartInfoEditor)
self.gridLayout.setObjectName(u"gridLayout")
self.groupBox = QGroupBox(TabDb_ChartInfoEditor)
self.groupBox.setObjectName(u"groupBox")
self.verticalLayout_2 = QVBoxLayout(self.groupBox)
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
self.widget = QWidget(self.groupBox)
self.widget.setObjectName(u"widget")
self.formLayout = QFormLayout(self.widget)
self.formLayout.setObjectName(u"formLayout")
self.formLayout.setLabelAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
self.label = QLabel(self.widget)
self.label.setObjectName(u"label")
self.formLayout.setWidget(1, QFormLayout.LabelRole, self.label)
self.label_2 = QLabel(self.widget)
self.label_2.setObjectName(u"label_2")
self.formLayout.setWidget(2, QFormLayout.LabelRole, self.label_2)
self.notesLineEdit = QLineEdit(self.widget)
self.notesLineEdit.setObjectName(u"notesLineEdit")
font = QFont()
font.setFamilies([u"GeosansLight"])
font.setPointSize(14)
font.setBold(True)
self.notesLineEdit.setFont(font)
self.formLayout.setWidget(2, QFormLayout.FieldRole, self.notesLineEdit)
self.jacketLabel = QLabel(self.widget)
self.jacketLabel.setObjectName(u"jacketLabel")
self.jacketLabel.setMinimumSize(QSize(100, 100))
self.jacketLabel.setMaximumSize(QSize(100, 100))
self.jacketLabel.setText(u"")
self.formLayout.setWidget(0, QFormLayout.LabelRole, self.jacketLabel)
self.verticalLayout = QVBoxLayout()
self.verticalLayout.setObjectName(u"verticalLayout")
self.verticalSpacer = QSpacerItem(20, 0, QSizePolicy.Minimum, QSizePolicy.Expanding)
self.verticalLayout.addItem(self.verticalSpacer)
self.titleLabel = QLabel(self.widget)
self.titleLabel.setObjectName(u"titleLabel")
self.titleLabel.setText(u"...")
self.verticalLayout.addWidget(self.titleLabel)
self.ratingLabel = QLabel(self.widget)
self.ratingLabel.setObjectName(u"ratingLabel")
self.ratingLabel.setText(u"...")
self.verticalLayout.addWidget(self.ratingLabel)
self.verticalSpacer_2 = QSpacerItem(20, 0, QSizePolicy.Minimum, QSizePolicy.Expanding)
self.verticalLayout.addItem(self.verticalSpacer_2)
self.formLayout.setLayout(0, QFormLayout.FieldRole, self.verticalLayout)
self.horizontalWidget_2 = QWidget(self.widget)
self.horizontalWidget_2.setObjectName(u"horizontalWidget_2")
sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Maximum)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.horizontalWidget_2.sizePolicy().hasHeightForWidth())
self.horizontalWidget_2.setSizePolicy(sizePolicy)
self.horizontalLayout_2 = QHBoxLayout(self.horizontalWidget_2)
self.horizontalLayout_2.setObjectName(u"horizontalLayout_2")
self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
self.constantLineEdit = QLineEdit(self.horizontalWidget_2)
self.constantLineEdit.setObjectName(u"constantLineEdit")
self.constantLineEdit.setFont(font)
self.horizontalLayout_2.addWidget(self.constantLineEdit)
self.constantPreviewLabel = QLabel(self.horizontalWidget_2)
self.constantPreviewLabel.setObjectName(u"constantPreviewLabel")
self.constantPreviewLabel.setText(u"> ...")
self.horizontalLayout_2.addWidget(self.constantPreviewLabel)
self.formLayout.setWidget(1, QFormLayout.FieldRole, self.horizontalWidget_2)
self.label_3 = QLabel(self.widget)
self.label_3.setObjectName(u"label_3")
sizePolicy1 = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.MinimumExpanding)
sizePolicy1.setHorizontalStretch(0)
sizePolicy1.setVerticalStretch(0)
sizePolicy1.setHeightForWidth(self.label_3.sizePolicy().hasHeightForWidth())
self.label_3.setSizePolicy(sizePolicy1)
self.formLayout.setWidget(3, QFormLayout.LabelRole, self.label_3)
self.label_4 = QLabel(self.widget)
self.label_4.setObjectName(u"label_4")
self.formLayout.setWidget(3, QFormLayout.FieldRole, self.label_4)
self.verticalLayout_2.addWidget(self.widget)
self.verticalSpacer_3 = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
self.verticalLayout_2.addItem(self.verticalSpacer_3)
self.horizontalLayout = QHBoxLayout()
self.horizontalLayout.setObjectName(u"horizontalLayout")
self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
self.horizontalLayout.addItem(self.horizontalSpacer)
self.deleteButton = QPushButton(self.groupBox)
self.deleteButton.setObjectName(u"deleteButton")
self.horizontalLayout.addWidget(self.deleteButton)
self.commitButton = QPushButton(self.groupBox)
self.commitButton.setObjectName(u"commitButton")
self.horizontalLayout.addWidget(self.commitButton)
self.verticalLayout_2.addLayout(self.horizontalLayout)
self.gridLayout.addWidget(self.groupBox, 1, 1, 1, 1)
self.chartSelector = ChartSelector(TabDb_ChartInfoEditor)
self.chartSelector.setObjectName(u"chartSelector")
self.gridLayout.addWidget(self.chartSelector, 0, 0, 1, 2)
self.listView = QListView(TabDb_ChartInfoEditor)
self.listView.setObjectName(u"listView")
sizePolicy2 = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding)
sizePolicy2.setHorizontalStretch(0)
sizePolicy2.setVerticalStretch(0)
sizePolicy2.setHeightForWidth(self.listView.sizePolicy().hasHeightForWidth())
self.listView.setSizePolicy(sizePolicy2)
self.listView.setEditTriggers(QAbstractItemView.NoEditTriggers)
self.listView.setVerticalScrollMode(QAbstractItemView.ScrollPerPixel)
self.listView.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel)
self.gridLayout.addWidget(self.listView, 1, 0, 1, 1)
QWidget.setTabOrder(self.listView, self.constantLineEdit)
QWidget.setTabOrder(self.constantLineEdit, self.notesLineEdit)
QWidget.setTabOrder(self.notesLineEdit, self.commitButton)
QWidget.setTabOrder(self.commitButton, self.deleteButton)
self.retranslateUi(TabDb_ChartInfoEditor)
QMetaObject.connectSlotsByName(TabDb_ChartInfoEditor)
# setupUi
def retranslateUi(self, TabDb_ChartInfoEditor):
self.groupBox.setTitle(QCoreApplication.translate("TabDb_ChartInfoEditor", u"editor.title", None))
self.label.setText(QCoreApplication.translate("TabDb_ChartInfoEditor", u"editor.constant", None))
self.label_2.setText(QCoreApplication.translate("TabDb_ChartInfoEditor", u"editor.notes", None))
self.label_3.setText(QCoreApplication.translate("TabDb_ChartInfoEditor", u"editor.tip", None))
self.label_4.setText(QCoreApplication.translate("TabDb_ChartInfoEditor", u"editor.tip.content", None))
self.deleteButton.setText(QCoreApplication.translate("TabDb_ChartInfoEditor", u"editor.delete", None))
self.commitButton.setText(QCoreApplication.translate("TabDb_ChartInfoEditor", u"editor.commit", None))
pass
# retranslateUi

View File

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

View File

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

View File

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

View File

@ -0,0 +1,245 @@
# -*- coding: utf-8 -*-
################################################################################
## Form generated from reading UI file 'tabDb_RemoveDuplicateScores.ui'
##
## Created by: Qt User Interface Compiler version 6.5.2
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
QMetaObject, QObject, QPoint, QRect,
QSize, QTime, QUrl, Qt)
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
QFont, QFontDatabase, QGradient, QIcon,
QImage, QKeySequence, QLinearGradient, QPainter,
QPalette, QPixmap, QRadialGradient, QTransform)
from PySide6.QtWidgets import (QAbstractItemView, QApplication, QCheckBox, QComboBox,
QGroupBox, QHBoxLayout, QHeaderView, QLabel,
QPushButton, QSizePolicy, QSpacerItem, QTreeView,
QVBoxLayout, QWidget)
class Ui_TabDb_RemoveDuplicateScores(object):
def setupUi(self, TabDb_RemoveDuplicateScores):
if not TabDb_RemoveDuplicateScores.objectName():
TabDb_RemoveDuplicateScores.setObjectName(u"TabDb_RemoveDuplicateScores")
TabDb_RemoveDuplicateScores.resize(600, 500)
TabDb_RemoveDuplicateScores.setWindowTitle(u"TabDb_RemoveDuplicateScores")
self.verticalLayout_2 = QVBoxLayout(TabDb_RemoveDuplicateScores)
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
self.groupBox_2 = QGroupBox(TabDb_RemoveDuplicateScores)
self.groupBox_2.setObjectName(u"groupBox_2")
self.verticalLayout = QVBoxLayout(self.groupBox_2)
self.verticalLayout.setObjectName(u"verticalLayout")
self.verticalLayout_4 = QVBoxLayout()
self.verticalLayout_4.setObjectName(u"verticalLayout_4")
self.horizontalLayout_2 = QHBoxLayout()
self.horizontalLayout_2.setObjectName(u"horizontalLayout_2")
self.scan_option_scoreCheckBox = QCheckBox(self.groupBox_2)
self.scan_option_scoreCheckBox.setObjectName(u"scan_option_scoreCheckBox")
self.horizontalLayout_2.addWidget(self.scan_option_scoreCheckBox)
self.scan_option_pureCheckBox = QCheckBox(self.groupBox_2)
self.scan_option_pureCheckBox.setObjectName(u"scan_option_pureCheckBox")
self.scan_option_pureCheckBox.setText(u"PURE")
self.horizontalLayout_2.addWidget(self.scan_option_pureCheckBox)
self.scan_option_farCheckBox = QCheckBox(self.groupBox_2)
self.scan_option_farCheckBox.setObjectName(u"scan_option_farCheckBox")
self.scan_option_farCheckBox.setText(u"FAR")
self.horizontalLayout_2.addWidget(self.scan_option_farCheckBox)
self.scan_option_lostCheckBox = QCheckBox(self.groupBox_2)
self.scan_option_lostCheckBox.setObjectName(u"scan_option_lostCheckBox")
self.scan_option_lostCheckBox.setText(u"LOST")
self.horizontalLayout_2.addWidget(self.scan_option_lostCheckBox)
self.scan_option_maxRecallCheckBox = QCheckBox(self.groupBox_2)
self.scan_option_maxRecallCheckBox.setObjectName(u"scan_option_maxRecallCheckBox")
self.scan_option_maxRecallCheckBox.setText(u"MAX RECALL")
self.horizontalLayout_2.addWidget(self.scan_option_maxRecallCheckBox)
self.verticalLayout_4.addLayout(self.horizontalLayout_2)
self.verticalLayout.addLayout(self.verticalLayout_4)
self.horizontalLayout_3 = QHBoxLayout()
self.horizontalLayout_3.setObjectName(u"horizontalLayout_3")
self.scan_option_dateCheckBox = QCheckBox(self.groupBox_2)
self.scan_option_dateCheckBox.setObjectName(u"scan_option_dateCheckBox")
self.horizontalLayout_3.addWidget(self.scan_option_dateCheckBox)
self.scan_option_modifierCheckBox = QCheckBox(self.groupBox_2)
self.scan_option_modifierCheckBox.setObjectName(u"scan_option_modifierCheckBox")
self.horizontalLayout_3.addWidget(self.scan_option_modifierCheckBox)
self.scan_option_clearTypeCheckBox = QCheckBox(self.groupBox_2)
self.scan_option_clearTypeCheckBox.setObjectName(u"scan_option_clearTypeCheckBox")
self.horizontalLayout_3.addWidget(self.scan_option_clearTypeCheckBox)
self.verticalLayout.addLayout(self.horizontalLayout_3)
self.scan_scanButton = QPushButton(self.groupBox_2)
self.scan_scanButton.setObjectName(u"scan_scanButton")
self.verticalLayout.addWidget(self.scan_scanButton)
self.verticalLayout_2.addWidget(self.groupBox_2)
self.horizontalLayout = QHBoxLayout()
self.horizontalLayout.setObjectName(u"horizontalLayout")
self.treeView = QTreeView(TabDb_RemoveDuplicateScores)
self.treeView.setObjectName(u"treeView")
self.treeView.setEditTriggers(QAbstractItemView.NoEditTriggers)
self.treeView.setSelectionMode(QAbstractItemView.NoSelection)
self.treeView.setVerticalScrollMode(QAbstractItemView.ScrollPerPixel)
self.treeView.setHeaderHidden(True)
self.horizontalLayout.addWidget(self.treeView)
self.verticalLayout_6 = QVBoxLayout()
self.verticalLayout_6.setObjectName(u"verticalLayout_6")
self.groupBox = QGroupBox(TabDb_RemoveDuplicateScores)
self.groupBox.setObjectName(u"groupBox")
self.verticalLayout_3 = QVBoxLayout(self.groupBox)
self.verticalLayout_3.setObjectName(u"verticalLayout_3")
self.label = QLabel(self.groupBox)
self.label.setObjectName(u"label")
self.verticalLayout_3.addWidget(self.label)
self.quickSelect_comboBox = QComboBox(self.groupBox)
self.quickSelect_comboBox.setObjectName(u"quickSelect_comboBox")
sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.quickSelect_comboBox.sizePolicy().hasHeightForWidth())
self.quickSelect_comboBox.setSizePolicy(sizePolicy)
self.verticalLayout_3.addWidget(self.quickSelect_comboBox)
self.verticalSpacer_2 = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
self.verticalLayout_3.addItem(self.verticalSpacer_2)
self.quickSelect_selectButton = QPushButton(self.groupBox)
self.quickSelect_selectButton.setObjectName(u"quickSelect_selectButton")
sizePolicy.setHeightForWidth(self.quickSelect_selectButton.sizePolicy().hasHeightForWidth())
self.quickSelect_selectButton.setSizePolicy(sizePolicy)
self.verticalLayout_3.addWidget(self.quickSelect_selectButton)
self.verticalLayout_6.addWidget(self.groupBox)
self.groupBox_3 = QGroupBox(TabDb_RemoveDuplicateScores)
self.groupBox_3.setObjectName(u"groupBox_3")
self.verticalLayout_5 = QVBoxLayout(self.groupBox_3)
self.verticalLayout_5.setObjectName(u"verticalLayout_5")
self.deselectAllButton = QPushButton(self.groupBox_3)
self.deselectAllButton.setObjectName(u"deselectAllButton")
self.verticalLayout_5.addWidget(self.deselectAllButton)
self.reverseSelectionButton = QPushButton(self.groupBox_3)
self.reverseSelectionButton.setObjectName(u"reverseSelectionButton")
self.verticalLayout_5.addWidget(self.reverseSelectionButton)
self.verticalSpacer_3 = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
self.verticalLayout_5.addItem(self.verticalSpacer_3)
self.collapseAllButton = QPushButton(self.groupBox_3)
self.collapseAllButton.setObjectName(u"collapseAllButton")
self.verticalLayout_5.addWidget(self.collapseAllButton)
self.expandAllButton = QPushButton(self.groupBox_3)
self.expandAllButton.setObjectName(u"expandAllButton")
self.verticalLayout_5.addWidget(self.expandAllButton)
self.resetModelButton = QPushButton(self.groupBox_3)
self.resetModelButton.setObjectName(u"resetModelButton")
self.verticalLayout_5.addWidget(self.resetModelButton)
self.verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
self.verticalLayout_5.addItem(self.verticalSpacer)
self.deleteSelectionButton = QPushButton(self.groupBox_3)
self.deleteSelectionButton.setObjectName(u"deleteSelectionButton")
font = QFont()
font.setBold(True)
self.deleteSelectionButton.setFont(font)
self.deleteSelectionButton.setStyleSheet(u"QPushButton { color: red };")
self.verticalLayout_5.addWidget(self.deleteSelectionButton)
self.verticalLayout_6.addWidget(self.groupBox_3)
self.horizontalLayout.addLayout(self.verticalLayout_6)
self.verticalLayout_2.addLayout(self.horizontalLayout)
QWidget.setTabOrder(self.scan_option_scoreCheckBox, self.scan_option_pureCheckBox)
QWidget.setTabOrder(self.scan_option_pureCheckBox, self.scan_option_farCheckBox)
QWidget.setTabOrder(self.scan_option_farCheckBox, self.scan_option_lostCheckBox)
QWidget.setTabOrder(self.scan_option_lostCheckBox, self.scan_option_maxRecallCheckBox)
QWidget.setTabOrder(self.scan_option_maxRecallCheckBox, self.scan_option_dateCheckBox)
QWidget.setTabOrder(self.scan_option_dateCheckBox, self.scan_option_modifierCheckBox)
QWidget.setTabOrder(self.scan_option_modifierCheckBox, self.scan_option_clearTypeCheckBox)
QWidget.setTabOrder(self.scan_option_clearTypeCheckBox, self.scan_scanButton)
QWidget.setTabOrder(self.scan_scanButton, self.treeView)
QWidget.setTabOrder(self.treeView, self.quickSelect_comboBox)
QWidget.setTabOrder(self.quickSelect_comboBox, self.quickSelect_selectButton)
QWidget.setTabOrder(self.quickSelect_selectButton, self.deselectAllButton)
QWidget.setTabOrder(self.deselectAllButton, self.reverseSelectionButton)
QWidget.setTabOrder(self.reverseSelectionButton, self.collapseAllButton)
QWidget.setTabOrder(self.collapseAllButton, self.expandAllButton)
QWidget.setTabOrder(self.expandAllButton, self.resetModelButton)
QWidget.setTabOrder(self.resetModelButton, self.deleteSelectionButton)
self.retranslateUi(TabDb_RemoveDuplicateScores)
QMetaObject.connectSlotsByName(TabDb_RemoveDuplicateScores)
# setupUi
def retranslateUi(self, TabDb_RemoveDuplicateScores):
self.groupBox_2.setTitle(QCoreApplication.translate("TabDb_RemoveDuplicateScores", u"scan.title", None))
self.scan_option_scoreCheckBox.setText(QCoreApplication.translate("TabDb_RemoveDuplicateScores", u"scan.option.score", None))
self.scan_option_dateCheckBox.setText(QCoreApplication.translate("TabDb_RemoveDuplicateScores", u"scan.option.date", None))
self.scan_option_modifierCheckBox.setText(QCoreApplication.translate("TabDb_RemoveDuplicateScores", u"scan.option.modifier", None))
self.scan_option_clearTypeCheckBox.setText(QCoreApplication.translate("TabDb_RemoveDuplicateScores", u"scan.option.clearType", None))
self.scan_scanButton.setText(QCoreApplication.translate("TabDb_RemoveDuplicateScores", u"scan.scanButton", None))
self.groupBox.setTitle(QCoreApplication.translate("TabDb_RemoveDuplicateScores", u"quickSelect.title", None))
self.label.setText(QCoreApplication.translate("TabDb_RemoveDuplicateScores", u"quickSelect.description", None))
self.quickSelect_selectButton.setText(QCoreApplication.translate("TabDb_RemoveDuplicateScores", u"quickSelect.selectButton", None))
self.groupBox_3.setTitle("")
self.deselectAllButton.setText(QCoreApplication.translate("TabDb_RemoveDuplicateScores", u"deselectAllButton", None))
self.reverseSelectionButton.setText(QCoreApplication.translate("TabDb_RemoveDuplicateScores", u"reverseSelectionButton", None))
self.collapseAllButton.setText(QCoreApplication.translate("TabDb_RemoveDuplicateScores", u"collapseAllButton", None))
self.expandAllButton.setText(QCoreApplication.translate("TabDb_RemoveDuplicateScores", u"expandAllButton", None))
self.resetModelButton.setText(QCoreApplication.translate("TabDb_RemoveDuplicateScores", u"resetModelButton", None))
self.deleteSelectionButton.setText(QCoreApplication.translate("TabDb_RemoveDuplicateScores", u"deleteSelectionButton", None))
pass
# retranslateUi

View File

@ -24,6 +24,16 @@
<string>tab.manage</string>
</attribute>
</widget>
<widget class="TabDb_ChartInfoEditor" name="tab_chartInfoEditor">
<attribute name="title">
<string>tab.chartInfoEditor</string>
</attribute>
</widget>
<widget class="TabDb_RemoveDuplicateScores" name="tab_removeDuplicateScores">
<attribute name="title">
<string>tab.removeDuplicateScores</string>
</attribute>
</widget>
</widget>
</item>
</layout>
@ -35,6 +45,18 @@
<header>ui.implements.tabs.tabDb.tabDb_Manage</header>
<container>1</container>
</customwidget>
<customwidget>
<class>TabDb_ChartInfoEditor</class>
<extends>QWidget</extends>
<header>ui.implements.tabs.tabDb.tabDb_ChartInfoEditor</header>
<container>1</container>
</customwidget>
<customwidget>
<class>TabDb_RemoveDuplicateScores</class>
<extends>QWidget</extends>
<header>ui.implements.tabs.tabDb.tabDb_RemoveDuplicateScores</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>

View File

@ -3,7 +3,7 @@
################################################################################
## Form generated from reading UI file 'tabDbEntry.ui'
##
## Created by: Qt User Interface Compiler version 6.5.0
## Created by: Qt User Interface Compiler version 6.5.2
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
@ -18,7 +18,9 @@ from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
from PySide6.QtWidgets import (QApplication, QSizePolicy, QTabWidget, QVBoxLayout,
QWidget)
from ui.implements.tabs.tabDb.tabDb_ChartInfoEditor import TabDb_ChartInfoEditor
from ui.implements.tabs.tabDb.tabDb_Manage import TabDb_Manage
from ui.implements.tabs.tabDb.tabDb_RemoveDuplicateScores import TabDb_RemoveDuplicateScores
class Ui_TabDbEntry(object):
def setupUi(self, TabDbEntry):
@ -33,6 +35,12 @@ class Ui_TabDbEntry(object):
self.tab_manage = TabDb_Manage()
self.tab_manage.setObjectName(u"tab_manage")
self.tabWidget.addTab(self.tab_manage, "")
self.tab_chartInfoEditor = TabDb_ChartInfoEditor()
self.tab_chartInfoEditor.setObjectName(u"tab_chartInfoEditor")
self.tabWidget.addTab(self.tab_chartInfoEditor, "")
self.tab_removeDuplicateScores = TabDb_RemoveDuplicateScores()
self.tab_removeDuplicateScores.setObjectName(u"tab_removeDuplicateScores")
self.tabWidget.addTab(self.tab_removeDuplicateScores, "")
self.verticalLayout.addWidget(self.tabWidget)
@ -47,6 +55,8 @@ class Ui_TabDbEntry(object):
def retranslateUi(self, TabDbEntry):
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_manage), QCoreApplication.translate("TabDbEntry", u"tab.manage", None))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_chartInfoEditor), QCoreApplication.translate("TabDbEntry", u"tab.chartInfoEditor", None))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_removeDuplicateScores), QCoreApplication.translate("TabDbEntry", u"tab.removeDuplicateScores", None))
pass
# retranslateUi

View File

@ -13,7 +13,7 @@
<property name="windowTitle">
<string notr="true">TabOcr_B30</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
@ -27,60 +27,87 @@
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>knnModelSelector.title</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="FileSelector" name="knnModelSelector" native="true"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_5">
<property name="title">
<string>b30KnnModelSelector.title</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="FileSelector" name="b30KnnModelSelector" native="true"/>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QGroupBox" name="groupBox_4">
<property name="title">
<string>phashDatabaseSelector.title</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="FileSelector" name="phashDatabaseSelector" native="true"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>imageSelector.title</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="FileSelector" name="imageSelector" native="true"/>
</item>
</layout>
</widget>
</item>
</layout>
<widget class="QGroupBox" name="groupBox_6">
<property name="title">
<string>dependencies.title</string>
</property>
<layout class="QGridLayout" name="gridLayout" columnstretch="0,0,0,0,1">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>dependencies.knnModel</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="dependencies_knnModelStatusLabel">
<property name="text">
<string notr="true">...</string>
</property>
</widget>
</item>
<item row="0" column="3" rowspan="3">
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>dependencies.b30KnnModel</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLabel" name="dependencies_phashDatabaseStatusLabel">
<property name="text">
<string notr="true">...</string>
</property>
</widget>
</item>
<item row="0" column="1" rowspan="3">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>dependencies.phashDatabase</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="dependencies_b30KnnModelStatusLabel">
<property name="text">
<string notr="true">...</string>
</property>
</widget>
</item>
<item row="0" column="4">
<widget class="FileSelector" name="dependencies_knnModelSelector" native="true"/>
</item>
<item row="1" column="4">
<widget class="FileSelector" name="dependencies_b30KnnModelSelector" native="true"/>
</item>
<item row="2" column="4">
<widget class="FileSelector" name="dependencies_phashDatabaseSelector" native="true"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="OcrQueue" name="ocrQueue" native="true">

View File

@ -15,8 +15,9 @@ from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
QFont, QFontDatabase, QGradient, QIcon,
QImage, QKeySequence, QLinearGradient, QPainter,
QPalette, QPixmap, QRadialGradient, QTransform)
from PySide6.QtWidgets import (QApplication, QComboBox, QGroupBox, QHBoxLayout,
QSizePolicy, QVBoxLayout, QWidget)
from PySide6.QtWidgets import (QApplication, QComboBox, QFrame, QGridLayout,
QGroupBox, QLabel, QSizePolicy, QVBoxLayout,
QWidget)
from ui.implements.components.fileSelector import FileSelector
from ui.implements.components.ocrQueue import OcrQueue
@ -27,8 +28,8 @@ class Ui_TabOcr_B30(object):
TabOcr_B30.setObjectName(u"TabOcr_B30")
TabOcr_B30.resize(555, 461)
TabOcr_B30.setWindowTitle(u"TabOcr_B30")
self.verticalLayout_3 = QVBoxLayout(TabOcr_B30)
self.verticalLayout_3.setObjectName(u"verticalLayout_3")
self.verticalLayout_2 = QVBoxLayout(TabOcr_B30)
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
self.groupBox = QGroupBox(TabOcr_B30)
self.groupBox.setObjectName(u"groupBox")
self.verticalLayout = QVBoxLayout(self.groupBox)
@ -39,65 +40,80 @@ class Ui_TabOcr_B30(object):
self.verticalLayout.addWidget(self.b30TypeComboBox)
self.verticalLayout_3.addWidget(self.groupBox)
self.verticalLayout_2.addWidget(self.groupBox)
self.horizontalLayout = QHBoxLayout()
self.horizontalLayout.setObjectName(u"horizontalLayout")
self.groupBox_3 = QGroupBox(TabOcr_B30)
self.groupBox_3.setObjectName(u"groupBox_3")
self.verticalLayout_4 = QVBoxLayout(self.groupBox_3)
self.verticalLayout_4.setObjectName(u"verticalLayout_4")
self.knnModelSelector = FileSelector(self.groupBox_3)
self.knnModelSelector.setObjectName(u"knnModelSelector")
self.groupBox_6 = QGroupBox(TabOcr_B30)
self.groupBox_6.setObjectName(u"groupBox_6")
self.gridLayout = QGridLayout(self.groupBox_6)
self.gridLayout.setObjectName(u"gridLayout")
self.label = QLabel(self.groupBox_6)
self.label.setObjectName(u"label")
self.label.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
self.verticalLayout_4.addWidget(self.knnModelSelector)
self.gridLayout.addWidget(self.label, 0, 0, 1, 1)
self.dependencies_knnModelStatusLabel = QLabel(self.groupBox_6)
self.dependencies_knnModelStatusLabel.setObjectName(u"dependencies_knnModelStatusLabel")
self.dependencies_knnModelStatusLabel.setText(u"...")
self.horizontalLayout.addWidget(self.groupBox_3)
self.gridLayout.addWidget(self.dependencies_knnModelStatusLabel, 0, 2, 1, 1)
self.groupBox_5 = QGroupBox(TabOcr_B30)
self.groupBox_5.setObjectName(u"groupBox_5")
self.verticalLayout_6 = QVBoxLayout(self.groupBox_5)
self.verticalLayout_6.setObjectName(u"verticalLayout_6")
self.b30KnnModelSelector = FileSelector(self.groupBox_5)
self.b30KnnModelSelector.setObjectName(u"b30KnnModelSelector")
self.line_2 = QFrame(self.groupBox_6)
self.line_2.setObjectName(u"line_2")
self.line_2.setFrameShape(QFrame.VLine)
self.line_2.setFrameShadow(QFrame.Sunken)
self.verticalLayout_6.addWidget(self.b30KnnModelSelector)
self.gridLayout.addWidget(self.line_2, 0, 3, 3, 1)
self.label_2 = QLabel(self.groupBox_6)
self.label_2.setObjectName(u"label_2")
self.label_2.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
self.horizontalLayout.addWidget(self.groupBox_5)
self.gridLayout.addWidget(self.label_2, 1, 0, 1, 1)
self.dependencies_phashDatabaseStatusLabel = QLabel(self.groupBox_6)
self.dependencies_phashDatabaseStatusLabel.setObjectName(u"dependencies_phashDatabaseStatusLabel")
self.dependencies_phashDatabaseStatusLabel.setText(u"...")
self.verticalLayout_3.addLayout(self.horizontalLayout)
self.gridLayout.addWidget(self.dependencies_phashDatabaseStatusLabel, 2, 2, 1, 1)
self.horizontalLayout_3 = QHBoxLayout()
self.horizontalLayout_3.setObjectName(u"horizontalLayout_3")
self.groupBox_4 = QGroupBox(TabOcr_B30)
self.groupBox_4.setObjectName(u"groupBox_4")
self.verticalLayout_5 = QVBoxLayout(self.groupBox_4)
self.verticalLayout_5.setObjectName(u"verticalLayout_5")
self.phashDatabaseSelector = FileSelector(self.groupBox_4)
self.phashDatabaseSelector.setObjectName(u"phashDatabaseSelector")
self.line = QFrame(self.groupBox_6)
self.line.setObjectName(u"line")
self.line.setFrameShape(QFrame.VLine)
self.line.setFrameShadow(QFrame.Sunken)
self.verticalLayout_5.addWidget(self.phashDatabaseSelector)
self.gridLayout.addWidget(self.line, 0, 1, 3, 1)
self.label_3 = QLabel(self.groupBox_6)
self.label_3.setObjectName(u"label_3")
self.label_3.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
self.horizontalLayout_3.addWidget(self.groupBox_4)
self.gridLayout.addWidget(self.label_3, 2, 0, 1, 1)
self.groupBox_2 = QGroupBox(TabOcr_B30)
self.groupBox_2.setObjectName(u"groupBox_2")
self.verticalLayout_2 = QVBoxLayout(self.groupBox_2)
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
self.imageSelector = FileSelector(self.groupBox_2)
self.imageSelector.setObjectName(u"imageSelector")
self.dependencies_b30KnnModelStatusLabel = QLabel(self.groupBox_6)
self.dependencies_b30KnnModelStatusLabel.setObjectName(u"dependencies_b30KnnModelStatusLabel")
self.dependencies_b30KnnModelStatusLabel.setText(u"...")
self.verticalLayout_2.addWidget(self.imageSelector)
self.gridLayout.addWidget(self.dependencies_b30KnnModelStatusLabel, 1, 2, 1, 1)
self.dependencies_knnModelSelector = FileSelector(self.groupBox_6)
self.dependencies_knnModelSelector.setObjectName(u"dependencies_knnModelSelector")
self.horizontalLayout_3.addWidget(self.groupBox_2)
self.gridLayout.addWidget(self.dependencies_knnModelSelector, 0, 4, 1, 1)
self.dependencies_b30KnnModelSelector = FileSelector(self.groupBox_6)
self.dependencies_b30KnnModelSelector.setObjectName(u"dependencies_b30KnnModelSelector")
self.verticalLayout_3.addLayout(self.horizontalLayout_3)
self.gridLayout.addWidget(self.dependencies_b30KnnModelSelector, 1, 4, 1, 1)
self.dependencies_phashDatabaseSelector = FileSelector(self.groupBox_6)
self.dependencies_phashDatabaseSelector.setObjectName(u"dependencies_phashDatabaseSelector")
self.gridLayout.addWidget(self.dependencies_phashDatabaseSelector, 2, 4, 1, 1)
self.gridLayout.setColumnStretch(4, 1)
self.verticalLayout_2.addWidget(self.groupBox_6)
self.ocrQueue = OcrQueue(TabOcr_B30)
self.ocrQueue.setObjectName(u"ocrQueue")
@ -107,7 +123,7 @@ class Ui_TabOcr_B30(object):
sizePolicy.setHeightForWidth(self.ocrQueue.sizePolicy().hasHeightForWidth())
self.ocrQueue.setSizePolicy(sizePolicy)
self.verticalLayout_3.addWidget(self.ocrQueue)
self.verticalLayout_2.addWidget(self.ocrQueue)
self.retranslateUi(TabOcr_B30)
@ -117,10 +133,10 @@ class Ui_TabOcr_B30(object):
def retranslateUi(self, TabOcr_B30):
self.groupBox.setTitle(QCoreApplication.translate("TabOcr_B30", u"b30type", None))
self.groupBox_3.setTitle(QCoreApplication.translate("TabOcr_B30", u"knnModelSelector.title", None))
self.groupBox_5.setTitle(QCoreApplication.translate("TabOcr_B30", u"b30KnnModelSelector.title", None))
self.groupBox_4.setTitle(QCoreApplication.translate("TabOcr_B30", u"phashDatabaseSelector.title", None))
self.groupBox_2.setTitle(QCoreApplication.translate("TabOcr_B30", u"imageSelector.title", None))
self.groupBox_6.setTitle(QCoreApplication.translate("TabOcr_B30", u"dependencies.title", None))
self.label.setText(QCoreApplication.translate("TabOcr_B30", u"dependencies.knnModel", None))
self.label_2.setText(QCoreApplication.translate("TabOcr_B30", u"dependencies.b30KnnModel", None))
self.label_3.setText(QCoreApplication.translate("TabOcr_B30", u"dependencies.phashDatabase", None))
pass
# retranslateUi

View File

@ -0,0 +1,281 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TabOcr_BuildPHashDatabase</class>
<widget class="QWidget" name="TabOcr_BuildPHashDatabase">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>632</width>
<height>551</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">TabOcr_BuildPHashDatabase</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>folders.title</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>folders.songDir</string>
</property>
</widget>
</item>
<item>
<widget class="FileSelector" name="songDirSelector" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>folders.charIconDir</string>
</property>
</widget>
</item>
<item>
<widget class="FileSelector" name="charIconDirSelector" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>options.title</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string notr="true">hash_size</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="hashSizeSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimum">
<number>2</number>
</property>
<property name="maximum">
<number>64</number>
</property>
<property name="value">
<number>16</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string notr="true">highfreq_factor</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="highfreqFactorSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximum">
<number>32</number>
</property>
<property name="value">
<number>4</number>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="preprocessCharIconCheckBox">
<property name="text">
<string>options.preprocessCharIcon</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="optionsResetButton">
<property name="text">
<string>resetButton</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QProgressBar" name="readImageProgressBar">
<property name="maximum">
<number>0</number>
</property>
<property name="value">
<number>0</number>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="format">
<string>[Reading images] %v/%m - %p%</string>
</property>
</widget>
</item>
<item>
<widget class="QProgressBar" name="calculateHashProgressBar">
<property name="maximum">
<number>0</number>
</property>
<property name="value">
<number>0</number>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="format">
<string>[Calculate hashes] %v/%m - %p%</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="buildButton">
<property name="text">
<string>buildButton</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>FileSelector</class>
<extends>QWidget</extends>
<header>ui.implements.components.fileSelector</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,209 @@
# -*- coding: utf-8 -*-
################################################################################
## Form generated from reading UI file 'tabOcr_BuildPHashDatabase.ui'
##
## Created by: Qt User Interface Compiler version 6.5.2
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
QMetaObject, QObject, QPoint, QRect,
QSize, QTime, QUrl, Qt)
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
QFont, QFontDatabase, QGradient, QIcon,
QImage, QKeySequence, QLinearGradient, QPainter,
QPalette, QPixmap, QRadialGradient, QTransform)
from PySide6.QtWidgets import (QApplication, QCheckBox, QGroupBox, QHBoxLayout,
QLabel, QProgressBar, QPushButton, QSizePolicy,
QSpacerItem, QSpinBox, QVBoxLayout, QWidget)
from ui.implements.components.fileSelector import FileSelector
class Ui_TabOcr_BuildPHashDatabase(object):
def setupUi(self, TabOcr_BuildPHashDatabase):
if not TabOcr_BuildPHashDatabase.objectName():
TabOcr_BuildPHashDatabase.setObjectName(u"TabOcr_BuildPHashDatabase")
TabOcr_BuildPHashDatabase.resize(632, 551)
TabOcr_BuildPHashDatabase.setWindowTitle(u"TabOcr_BuildPHashDatabase")
self.verticalLayout_3 = QVBoxLayout(TabOcr_BuildPHashDatabase)
self.verticalLayout_3.setObjectName(u"verticalLayout_3")
self.verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
self.verticalLayout_3.addItem(self.verticalSpacer)
self.groupBox = QGroupBox(TabOcr_BuildPHashDatabase)
self.groupBox.setObjectName(u"groupBox")
self.verticalLayout = QVBoxLayout(self.groupBox)
self.verticalLayout.setObjectName(u"verticalLayout")
self.horizontalLayout = QHBoxLayout()
self.horizontalLayout.setObjectName(u"horizontalLayout")
self.label = QLabel(self.groupBox)
self.label.setObjectName(u"label")
self.horizontalLayout.addWidget(self.label)
self.songDirSelector = FileSelector(self.groupBox)
self.songDirSelector.setObjectName(u"songDirSelector")
sizePolicy = QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.songDirSelector.sizePolicy().hasHeightForWidth())
self.songDirSelector.setSizePolicy(sizePolicy)
self.horizontalLayout.addWidget(self.songDirSelector)
self.verticalLayout.addLayout(self.horizontalLayout)
self.horizontalLayout_2 = QHBoxLayout()
self.horizontalLayout_2.setObjectName(u"horizontalLayout_2")
self.label_2 = QLabel(self.groupBox)
self.label_2.setObjectName(u"label_2")
self.horizontalLayout_2.addWidget(self.label_2)
self.charIconDirSelector = FileSelector(self.groupBox)
self.charIconDirSelector.setObjectName(u"charIconDirSelector")
sizePolicy.setHeightForWidth(self.charIconDirSelector.sizePolicy().hasHeightForWidth())
self.charIconDirSelector.setSizePolicy(sizePolicy)
self.horizontalLayout_2.addWidget(self.charIconDirSelector)
self.verticalLayout.addLayout(self.horizontalLayout_2)
self.verticalLayout_3.addWidget(self.groupBox)
self.groupBox_2 = QGroupBox(TabOcr_BuildPHashDatabase)
self.groupBox_2.setObjectName(u"groupBox_2")
self.horizontalLayout_3 = QHBoxLayout(self.groupBox_2)
self.horizontalLayout_3.setObjectName(u"horizontalLayout_3")
self.verticalLayout_2 = QVBoxLayout()
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
self.horizontalLayout_6 = QHBoxLayout()
self.horizontalLayout_6.setObjectName(u"horizontalLayout_6")
self.label_3 = QLabel(self.groupBox_2)
self.label_3.setObjectName(u"label_3")
self.label_3.setText(u"hash_size")
self.label_3.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
self.horizontalLayout_6.addWidget(self.label_3)
self.hashSizeSpinBox = QSpinBox(self.groupBox_2)
self.hashSizeSpinBox.setObjectName(u"hashSizeSpinBox")
sizePolicy1 = QSizePolicy(QSizePolicy.Maximum, QSizePolicy.Fixed)
sizePolicy1.setHorizontalStretch(0)
sizePolicy1.setVerticalStretch(0)
sizePolicy1.setHeightForWidth(self.hashSizeSpinBox.sizePolicy().hasHeightForWidth())
self.hashSizeSpinBox.setSizePolicy(sizePolicy1)
self.hashSizeSpinBox.setMinimum(2)
self.hashSizeSpinBox.setMaximum(64)
self.hashSizeSpinBox.setValue(16)
self.horizontalLayout_6.addWidget(self.hashSizeSpinBox)
self.verticalLayout_2.addLayout(self.horizontalLayout_6)
self.horizontalLayout_7 = QHBoxLayout()
self.horizontalLayout_7.setObjectName(u"horizontalLayout_7")
self.label_4 = QLabel(self.groupBox_2)
self.label_4.setObjectName(u"label_4")
self.label_4.setText(u"highfreq_factor")
self.label_4.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
self.horizontalLayout_7.addWidget(self.label_4)
self.highfreqFactorSpinBox = QSpinBox(self.groupBox_2)
self.highfreqFactorSpinBox.setObjectName(u"highfreqFactorSpinBox")
sizePolicy1.setHeightForWidth(self.highfreqFactorSpinBox.sizePolicy().hasHeightForWidth())
self.highfreqFactorSpinBox.setSizePolicy(sizePolicy1)
self.highfreqFactorSpinBox.setMaximum(32)
self.highfreqFactorSpinBox.setValue(4)
self.horizontalLayout_7.addWidget(self.highfreqFactorSpinBox)
self.verticalLayout_2.addLayout(self.horizontalLayout_7)
self.horizontalLayout_3.addLayout(self.verticalLayout_2)
self.preprocessCharIconCheckBox = QCheckBox(self.groupBox_2)
self.preprocessCharIconCheckBox.setObjectName(u"preprocessCharIconCheckBox")
self.preprocessCharIconCheckBox.setChecked(True)
self.horizontalLayout_3.addWidget(self.preprocessCharIconCheckBox)
self.horizontalSpacer_3 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
self.horizontalLayout_3.addItem(self.horizontalSpacer_3)
self.optionsResetButton = QPushButton(self.groupBox_2)
self.optionsResetButton.setObjectName(u"optionsResetButton")
self.horizontalLayout_3.addWidget(self.optionsResetButton)
self.verticalLayout_3.addWidget(self.groupBox_2)
self.readImageProgressBar = QProgressBar(TabOcr_BuildPHashDatabase)
self.readImageProgressBar.setObjectName(u"readImageProgressBar")
self.readImageProgressBar.setMaximum(0)
self.readImageProgressBar.setValue(0)
self.readImageProgressBar.setAlignment(Qt.AlignCenter)
self.verticalLayout_3.addWidget(self.readImageProgressBar)
self.calculateHashProgressBar = QProgressBar(TabOcr_BuildPHashDatabase)
self.calculateHashProgressBar.setObjectName(u"calculateHashProgressBar")
self.calculateHashProgressBar.setMaximum(0)
self.calculateHashProgressBar.setValue(0)
self.calculateHashProgressBar.setAlignment(Qt.AlignCenter)
self.verticalLayout_3.addWidget(self.calculateHashProgressBar)
self.horizontalLayout_5 = QHBoxLayout()
self.horizontalLayout_5.setObjectName(u"horizontalLayout_5")
self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
self.horizontalLayout_5.addItem(self.horizontalSpacer)
self.buildButton = QPushButton(TabOcr_BuildPHashDatabase)
self.buildButton.setObjectName(u"buildButton")
self.horizontalLayout_5.addWidget(self.buildButton)
self.horizontalSpacer_2 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
self.horizontalLayout_5.addItem(self.horizontalSpacer_2)
self.verticalLayout_3.addLayout(self.horizontalLayout_5)
self.verticalSpacer_2 = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
self.verticalLayout_3.addItem(self.verticalSpacer_2)
self.retranslateUi(TabOcr_BuildPHashDatabase)
QMetaObject.connectSlotsByName(TabOcr_BuildPHashDatabase)
# setupUi
def retranslateUi(self, TabOcr_BuildPHashDatabase):
self.groupBox.setTitle(QCoreApplication.translate("TabOcr_BuildPHashDatabase", u"folders.title", None))
self.label.setText(QCoreApplication.translate("TabOcr_BuildPHashDatabase", u"folders.songDir", None))
self.label_2.setText(QCoreApplication.translate("TabOcr_BuildPHashDatabase", u"folders.charIconDir", None))
self.groupBox_2.setTitle(QCoreApplication.translate("TabOcr_BuildPHashDatabase", u"options.title", None))
self.preprocessCharIconCheckBox.setText(QCoreApplication.translate("TabOcr_BuildPHashDatabase", u"options.preprocessCharIcon", None))
self.optionsResetButton.setText(QCoreApplication.translate("TabOcr_BuildPHashDatabase", u"resetButton", None))
self.readImageProgressBar.setFormat(QCoreApplication.translate("TabOcr_BuildPHashDatabase", u"[Reading images] %v/%m - %p%", None))
self.calculateHashProgressBar.setFormat(QCoreApplication.translate("TabOcr_BuildPHashDatabase", u"[Calculate hashes] %v/%m - %p%", None))
self.buildButton.setText(QCoreApplication.translate("TabOcr_BuildPHashDatabase", u"buildButton", None))
pass
# retranslateUi

View File

@ -24,130 +24,249 @@
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>deviceSelector.title</string>
<string>options.title</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,0,0">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QCheckBox" name="deviceUseAutoFactorCheckBox">
<widget class="QCheckBox" name="options_usePresetCheckBox">
<property name="text">
<string>deviceSelector.useAutoFactor</string>
<string>options.usePreset</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="deviceSizesV2CheckBox">
<property name="text">
<string notr="true">SizesV2</string>
<widget class="QComboBox" name="options_presetComboBox">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="FileSelector" name="deviceFileSelector" native="true"/>
<widget class="Line" name="line_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="DevicesComboBox" name="deviceComboBox"/>
<widget class="QWidget" name="options_preciseControlWidget" native="true">
<layout class="QGridLayout" name="gridLayout_2" columnstretch="0,1,0">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>options.rois</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QStackedWidget" name="options_roisStackedWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="page">
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QComboBox" name="options_roisComboBox"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="page_2">
<layout class="QVBoxLayout" name="verticalLayout_4">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="FileSelector" name="options_roisCustomSelector" native="true"/>
</item>
</layout>
</widget>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>options.masker</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QCheckBox" name="options_roisUseCustomCheckBox">
<property name="text">
<string>options.useCustom</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QCheckBox" name="options_maskerUseCustomCheckBox">
<property name="text">
<string>options.useCustom</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QStackedWidget" name="options_maskerStackedWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="page_3">
<layout class="QVBoxLayout" name="verticalLayout_5">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QComboBox" name="options_maskerComboBox"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="page_4">
<layout class="QVBoxLayout" name="verticalLayout_6">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="FileSelector" name="options_maskerCustomSelector" native="true"/>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="horizontalWidget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>dependencies.title</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QGroupBox" name="groupBox_6">
<property name="title">
<string>knnModelSelector.title</string>
<layout class="QGridLayout" name="gridLayout" columnstretch="0,0,0,0,1">
<item row="0" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>dependencies.knnModel</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="FileSelector" name="knnModelSelector" native="true"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QStackedWidget" name="deviceDependenciesStackedWidget">
<property name="currentIndex">
<number>0</number>
<item row="0" column="2">
<widget class="QLabel" name="dependencies_knnModelStatusLabel">
<property name="text">
<string notr="true">...</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="dependencies_phashDatabaseStatusLabel">
<property name="text">
<string notr="true">...</string>
</property>
</widget>
</item>
<item row="1" column="4">
<widget class="FileSelector" name="dependencies_phashDatabaseSelector" native="true"/>
</item>
<item row="0" column="1" rowspan="2">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item row="0" column="4">
<widget class="FileSelector" name="dependencies_knnModelSelector" native="true"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>dependencies.phashDatabase</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="3" rowspan="2">
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<widget class="QWidget" name="deviceV1">
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QGroupBox" name="groupBox_4">
<property name="title">
<string>tesseractSelector.title</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="FileSelector" name="tesseractFileSelector" native="true"/>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="deviceV2">
<layout class="QVBoxLayout" name="verticalLayout_4">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QGroupBox" name="groupBox_5">
<property name="title">
<string>phashDatabaseSelector.title</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_7">
<item>
<widget class="FileSelector" name="phashDatabaseSelector" native="true"/>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
@ -178,12 +297,24 @@
<header>ui.implements.components.ocrQueue</header>
<container>1</container>
</customwidget>
<customwidget>
<class>DevicesComboBox</class>
<extends>QComboBox</extends>
<header>ui.implements.components.devicesComboBox</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
<connections>
<connection>
<sender>options_usePresetCheckBox</sender>
<signal>toggled(bool)</signal>
<receiver>options_presetComboBox</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>82</x>
<y>111</y>
</hint>
<hint type="destinationlabel">
<x>82</x>
<y>175</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -15,11 +15,11 @@ from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
QFont, QFontDatabase, QGradient, QIcon,
QImage, QKeySequence, QLinearGradient, QPainter,
QPalette, QPixmap, QRadialGradient, QTransform)
from PySide6.QtWidgets import (QApplication, QCheckBox, QGroupBox, QHBoxLayout,
from PySide6.QtWidgets import (QApplication, QCheckBox, QComboBox, QFrame,
QGridLayout, QGroupBox, QHBoxLayout, QLabel,
QPushButton, QSizePolicy, QStackedWidget, QVBoxLayout,
QWidget)
from ui.implements.components.devicesComboBox import DevicesComboBox
from ui.implements.components.fileSelector import FileSelector
from ui.implements.components.ocrQueue import OcrQueue
@ -38,102 +38,181 @@ class Ui_TabOcr_Device(object):
self.groupBox = QGroupBox(TabOcr_Device)
self.groupBox.setObjectName(u"groupBox")
self.verticalLayout = QVBoxLayout(self.groupBox)
self.verticalLayout.setObjectName(u"verticalLayout")
self.horizontalLayout = QHBoxLayout()
self.horizontalLayout = QHBoxLayout(self.groupBox)
self.horizontalLayout.setObjectName(u"horizontalLayout")
self.deviceUseAutoFactorCheckBox = QCheckBox(self.groupBox)
self.deviceUseAutoFactorCheckBox.setObjectName(u"deviceUseAutoFactorCheckBox")
self.verticalLayout = QVBoxLayout()
self.verticalLayout.setObjectName(u"verticalLayout")
self.options_usePresetCheckBox = QCheckBox(self.groupBox)
self.options_usePresetCheckBox.setObjectName(u"options_usePresetCheckBox")
self.horizontalLayout.addWidget(self.deviceUseAutoFactorCheckBox)
self.verticalLayout.addWidget(self.options_usePresetCheckBox)
self.deviceSizesV2CheckBox = QCheckBox(self.groupBox)
self.deviceSizesV2CheckBox.setObjectName(u"deviceSizesV2CheckBox")
self.deviceSizesV2CheckBox.setText(u"SizesV2")
self.options_presetComboBox = QComboBox(self.groupBox)
self.options_presetComboBox.setObjectName(u"options_presetComboBox")
self.options_presetComboBox.setEnabled(False)
self.horizontalLayout.addWidget(self.deviceSizesV2CheckBox)
self.verticalLayout.addWidget(self.options_presetComboBox)
self.verticalLayout.addLayout(self.horizontalLayout)
self.horizontalLayout.addLayout(self.verticalLayout)
self.deviceFileSelector = FileSelector(self.groupBox)
self.deviceFileSelector.setObjectName(u"deviceFileSelector")
self.line_3 = QFrame(self.groupBox)
self.line_3.setObjectName(u"line_3")
self.line_3.setFrameShape(QFrame.VLine)
self.line_3.setFrameShadow(QFrame.Sunken)
self.verticalLayout.addWidget(self.deviceFileSelector)
self.horizontalLayout.addWidget(self.line_3)
self.deviceComboBox = DevicesComboBox(self.groupBox)
self.deviceComboBox.setObjectName(u"deviceComboBox")
self.options_preciseControlWidget = QWidget(self.groupBox)
self.options_preciseControlWidget.setObjectName(u"options_preciseControlWidget")
self.gridLayout_2 = QGridLayout(self.options_preciseControlWidget)
self.gridLayout_2.setObjectName(u"gridLayout_2")
self.gridLayout_2.setContentsMargins(0, 0, 0, 0)
self.label = QLabel(self.options_preciseControlWidget)
self.label.setObjectName(u"label")
self.label.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
self.verticalLayout.addWidget(self.deviceComboBox)
self.gridLayout_2.addWidget(self.label, 0, 0, 1, 1)
self.options_roisStackedWidget = QStackedWidget(self.options_preciseControlWidget)
self.options_roisStackedWidget.setObjectName(u"options_roisStackedWidget")
sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Maximum)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.options_roisStackedWidget.sizePolicy().hasHeightForWidth())
self.options_roisStackedWidget.setSizePolicy(sizePolicy)
self.page = QWidget()
self.page.setObjectName(u"page")
self.verticalLayout_2 = QVBoxLayout(self.page)
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)
self.options_roisComboBox = QComboBox(self.page)
self.options_roisComboBox.setObjectName(u"options_roisComboBox")
self.verticalLayout_2.addWidget(self.options_roisComboBox)
self.options_roisStackedWidget.addWidget(self.page)
self.page_2 = QWidget()
self.page_2.setObjectName(u"page_2")
self.verticalLayout_4 = QVBoxLayout(self.page_2)
self.verticalLayout_4.setObjectName(u"verticalLayout_4")
self.verticalLayout_4.setContentsMargins(0, 0, 0, 0)
self.options_roisCustomSelector = FileSelector(self.page_2)
self.options_roisCustomSelector.setObjectName(u"options_roisCustomSelector")
self.verticalLayout_4.addWidget(self.options_roisCustomSelector)
self.options_roisStackedWidget.addWidget(self.page_2)
self.gridLayout_2.addWidget(self.options_roisStackedWidget, 0, 1, 1, 1)
self.label_2 = QLabel(self.options_preciseControlWidget)
self.label_2.setObjectName(u"label_2")
self.label_2.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
self.gridLayout_2.addWidget(self.label_2, 1, 0, 1, 1)
self.options_roisUseCustomCheckBox = QCheckBox(self.options_preciseControlWidget)
self.options_roisUseCustomCheckBox.setObjectName(u"options_roisUseCustomCheckBox")
self.gridLayout_2.addWidget(self.options_roisUseCustomCheckBox, 0, 2, 1, 1)
self.options_maskerUseCustomCheckBox = QCheckBox(self.options_preciseControlWidget)
self.options_maskerUseCustomCheckBox.setObjectName(u"options_maskerUseCustomCheckBox")
self.gridLayout_2.addWidget(self.options_maskerUseCustomCheckBox, 1, 2, 1, 1)
self.options_maskerStackedWidget = QStackedWidget(self.options_preciseControlWidget)
self.options_maskerStackedWidget.setObjectName(u"options_maskerStackedWidget")
sizePolicy.setHeightForWidth(self.options_maskerStackedWidget.sizePolicy().hasHeightForWidth())
self.options_maskerStackedWidget.setSizePolicy(sizePolicy)
self.page_3 = QWidget()
self.page_3.setObjectName(u"page_3")
self.verticalLayout_5 = QVBoxLayout(self.page_3)
self.verticalLayout_5.setObjectName(u"verticalLayout_5")
self.verticalLayout_5.setContentsMargins(0, 0, 0, 0)
self.options_maskerComboBox = QComboBox(self.page_3)
self.options_maskerComboBox.setObjectName(u"options_maskerComboBox")
self.verticalLayout_5.addWidget(self.options_maskerComboBox)
self.options_maskerStackedWidget.addWidget(self.page_3)
self.page_4 = QWidget()
self.page_4.setObjectName(u"page_4")
self.verticalLayout_6 = QVBoxLayout(self.page_4)
self.verticalLayout_6.setObjectName(u"verticalLayout_6")
self.verticalLayout_6.setContentsMargins(0, 0, 0, 0)
self.options_maskerCustomSelector = FileSelector(self.page_4)
self.options_maskerCustomSelector.setObjectName(u"options_maskerCustomSelector")
self.verticalLayout_6.addWidget(self.options_maskerCustomSelector)
self.options_maskerStackedWidget.addWidget(self.page_4)
self.gridLayout_2.addWidget(self.options_maskerStackedWidget, 1, 1, 1, 1)
self.gridLayout_2.setColumnStretch(1, 1)
self.horizontalLayout.addWidget(self.options_preciseControlWidget)
self.verticalLayout_3.addWidget(self.groupBox)
self.horizontalWidget = QWidget(TabOcr_Device)
self.horizontalWidget.setObjectName(u"horizontalWidget")
sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Maximum)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.horizontalWidget.sizePolicy().hasHeightForWidth())
self.horizontalWidget.setSizePolicy(sizePolicy)
self.horizontalLayout_2 = QHBoxLayout(self.horizontalWidget)
self.horizontalLayout_2.setObjectName(u"horizontalLayout_2")
self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
self.groupBox_6 = QGroupBox(self.horizontalWidget)
self.groupBox_6.setObjectName(u"groupBox_6")
self.verticalLayout_6 = QVBoxLayout(self.groupBox_6)
self.verticalLayout_6.setObjectName(u"verticalLayout_6")
self.knnModelSelector = FileSelector(self.groupBox_6)
self.knnModelSelector.setObjectName(u"knnModelSelector")
self.groupBox_2 = QGroupBox(TabOcr_Device)
self.groupBox_2.setObjectName(u"groupBox_2")
self.gridLayout = QGridLayout(self.groupBox_2)
self.gridLayout.setObjectName(u"gridLayout")
self.label_3 = QLabel(self.groupBox_2)
self.label_3.setObjectName(u"label_3")
self.label_3.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
self.verticalLayout_6.addWidget(self.knnModelSelector)
self.gridLayout.addWidget(self.label_3, 0, 0, 1, 1)
self.dependencies_knnModelStatusLabel = QLabel(self.groupBox_2)
self.dependencies_knnModelStatusLabel.setObjectName(u"dependencies_knnModelStatusLabel")
self.dependencies_knnModelStatusLabel.setText(u"...")
self.horizontalLayout_2.addWidget(self.groupBox_6)
self.gridLayout.addWidget(self.dependencies_knnModelStatusLabel, 0, 2, 1, 1)
self.deviceDependenciesStackedWidget = QStackedWidget(self.horizontalWidget)
self.deviceDependenciesStackedWidget.setObjectName(u"deviceDependenciesStackedWidget")
self.deviceV1 = QWidget()
self.deviceV1.setObjectName(u"deviceV1")
self.verticalLayout_2 = QVBoxLayout(self.deviceV1)
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)
self.groupBox_4 = QGroupBox(self.deviceV1)
self.groupBox_4.setObjectName(u"groupBox_4")
self.verticalLayout_5 = QVBoxLayout(self.groupBox_4)
self.verticalLayout_5.setObjectName(u"verticalLayout_5")
self.tesseractFileSelector = FileSelector(self.groupBox_4)
self.tesseractFileSelector.setObjectName(u"tesseractFileSelector")
self.dependencies_phashDatabaseStatusLabel = QLabel(self.groupBox_2)
self.dependencies_phashDatabaseStatusLabel.setObjectName(u"dependencies_phashDatabaseStatusLabel")
self.dependencies_phashDatabaseStatusLabel.setText(u"...")
self.verticalLayout_5.addWidget(self.tesseractFileSelector)
self.gridLayout.addWidget(self.dependencies_phashDatabaseStatusLabel, 1, 2, 1, 1)
self.dependencies_phashDatabaseSelector = FileSelector(self.groupBox_2)
self.dependencies_phashDatabaseSelector.setObjectName(u"dependencies_phashDatabaseSelector")
self.verticalLayout_2.addWidget(self.groupBox_4)
self.gridLayout.addWidget(self.dependencies_phashDatabaseSelector, 1, 4, 1, 1)
self.deviceDependenciesStackedWidget.addWidget(self.deviceV1)
self.deviceV2 = QWidget()
self.deviceV2.setObjectName(u"deviceV2")
self.verticalLayout_4 = QVBoxLayout(self.deviceV2)
self.verticalLayout_4.setObjectName(u"verticalLayout_4")
self.verticalLayout_4.setContentsMargins(0, 0, 0, 0)
self.groupBox_5 = QGroupBox(self.deviceV2)
self.groupBox_5.setObjectName(u"groupBox_5")
self.verticalLayout_7 = QVBoxLayout(self.groupBox_5)
self.verticalLayout_7.setObjectName(u"verticalLayout_7")
self.phashDatabaseSelector = FileSelector(self.groupBox_5)
self.phashDatabaseSelector.setObjectName(u"phashDatabaseSelector")
self.line = QFrame(self.groupBox_2)
self.line.setObjectName(u"line")
self.line.setFrameShape(QFrame.VLine)
self.line.setFrameShadow(QFrame.Sunken)
self.verticalLayout_7.addWidget(self.phashDatabaseSelector)
self.gridLayout.addWidget(self.line, 0, 1, 2, 1)
self.dependencies_knnModelSelector = FileSelector(self.groupBox_2)
self.dependencies_knnModelSelector.setObjectName(u"dependencies_knnModelSelector")
self.verticalLayout_4.addWidget(self.groupBox_5)
self.gridLayout.addWidget(self.dependencies_knnModelSelector, 0, 4, 1, 1)
self.deviceDependenciesStackedWidget.addWidget(self.deviceV2)
self.label_4 = QLabel(self.groupBox_2)
self.label_4.setObjectName(u"label_4")
self.label_4.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
self.horizontalLayout_2.addWidget(self.deviceDependenciesStackedWidget)
self.gridLayout.addWidget(self.label_4, 1, 0, 1, 1)
self.line_2 = QFrame(self.groupBox_2)
self.line_2.setObjectName(u"line_2")
self.line_2.setFrameShape(QFrame.VLine)
self.line_2.setFrameShadow(QFrame.Sunken)
self.verticalLayout_3.addWidget(self.horizontalWidget)
self.gridLayout.addWidget(self.line_2, 0, 3, 2, 1)
self.gridLayout.setColumnStretch(4, 1)
self.verticalLayout_3.addWidget(self.groupBox_2)
self.ocrQueue = OcrQueue(TabOcr_Device)
self.ocrQueue.setObjectName(u"ocrQueue")
@ -147,8 +226,10 @@ class Ui_TabOcr_Device(object):
self.retranslateUi(TabOcr_Device)
self.options_usePresetCheckBox.toggled.connect(self.options_presetComboBox.setEnabled)
self.deviceDependenciesStackedWidget.setCurrentIndex(0)
self.options_roisStackedWidget.setCurrentIndex(0)
self.options_maskerStackedWidget.setCurrentIndex(0)
QMetaObject.connectSlotsByName(TabOcr_Device)
@ -156,11 +237,15 @@ class Ui_TabOcr_Device(object):
def retranslateUi(self, TabOcr_Device):
self.openWizardButton.setText(QCoreApplication.translate("TabOcr_Device", u"openWizardButton", None))
self.groupBox.setTitle(QCoreApplication.translate("TabOcr_Device", u"deviceSelector.title", None))
self.deviceUseAutoFactorCheckBox.setText(QCoreApplication.translate("TabOcr_Device", u"deviceSelector.useAutoFactor", None))
self.groupBox_6.setTitle(QCoreApplication.translate("TabOcr_Device", u"knnModelSelector.title", None))
self.groupBox_4.setTitle(QCoreApplication.translate("TabOcr_Device", u"tesseractSelector.title", None))
self.groupBox_5.setTitle(QCoreApplication.translate("TabOcr_Device", u"phashDatabaseSelector.title", None))
self.groupBox.setTitle(QCoreApplication.translate("TabOcr_Device", u"options.title", None))
self.options_usePresetCheckBox.setText(QCoreApplication.translate("TabOcr_Device", u"options.usePreset", None))
self.label.setText(QCoreApplication.translate("TabOcr_Device", u"options.rois", None))
self.label_2.setText(QCoreApplication.translate("TabOcr_Device", u"options.masker", None))
self.options_roisUseCustomCheckBox.setText(QCoreApplication.translate("TabOcr_Device", u"options.useCustom", None))
self.options_maskerUseCustomCheckBox.setText(QCoreApplication.translate("TabOcr_Device", u"options.useCustom", None))
self.groupBox_2.setTitle(QCoreApplication.translate("TabOcr_Device", u"dependencies.title", None))
self.label_3.setText(QCoreApplication.translate("TabOcr_Device", u"dependencies.knnModel", None))
self.label_4.setText(QCoreApplication.translate("TabOcr_Device", u"dependencies.phashDatabase", None))
pass
# retranslateUi

View File

@ -29,6 +29,11 @@
<string>tab.b30</string>
</attribute>
</widget>
<widget class="TabOcr_BuildPHashDatabase" name="tab_3">
<attribute name="title">
<string>tab.buildPHashDatabase</string>
</attribute>
</widget>
</widget>
</item>
</layout>
@ -46,6 +51,12 @@
<header>ui.implements.tabs.tabOcr.tabOcr_B30</header>
<container>1</container>
</customwidget>
<customwidget>
<class>TabOcr_BuildPHashDatabase</class>
<extends>QWidget</extends>
<header>ui.implements.tabs.tabOcr.tabOcr_BuildPHashDatabase</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>

View File

@ -3,7 +3,7 @@
################################################################################
## Form generated from reading UI file 'tabOcrEntry.ui'
##
## Created by: Qt User Interface Compiler version 6.5.1
## Created by: Qt User Interface Compiler version 6.5.2
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
@ -19,6 +19,7 @@ from PySide6.QtWidgets import (QApplication, QSizePolicy, QTabWidget, QVBoxLayou
QWidget)
from ui.implements.tabs.tabOcr.tabOcr_B30 import TabOcr_B30
from ui.implements.tabs.tabOcr.tabOcr_BuildPHashDatabase import TabOcr_BuildPHashDatabase
from ui.implements.tabs.tabOcr.tabOcr_Device import TabOcr_Device
class Ui_TabOcrEntry(object):
@ -37,6 +38,9 @@ class Ui_TabOcrEntry(object):
self.tab_2 = TabOcr_B30()
self.tab_2.setObjectName(u"tab_2")
self.tabWidget.addTab(self.tab_2, "")
self.tab_3 = TabOcr_BuildPHashDatabase()
self.tab_3.setObjectName(u"tab_3")
self.tabWidget.addTab(self.tab_3, "")
self.verticalLayout.addWidget(self.tabWidget)
@ -52,6 +56,7 @@ class Ui_TabOcrEntry(object):
def retranslateUi(self, TabOcrEntry):
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), QCoreApplication.translate("TabOcrEntry", u"tab.device", None))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), QCoreApplication.translate("TabOcrEntry", u"tab.b30", None))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_3), QCoreApplication.translate("TabOcrEntry", u"tab.buildPHashDatabase", None))
pass
# retranslateUi

View File

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

View File

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

View File

@ -6,113 +6,15 @@
<rect>
<x>0</x>
<y>0</y>
<width>668</width>
<height>546</height>
<width>616</width>
<height>500</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">TabTools_ChartRecommend</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>constantRangeFromPlayRating</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QDoubleSpinBox" name="rangeFromPlayRating_playRatingSpinBox">
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="maximum">
<double>100.000000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="2">
<widget class="QLabel" name="label_3">
<property name="text">
<string notr="true">AA</string>
</property>
<property name="alignment">
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_2">
<property name="text">
<string notr="true">EX</string>
</property>
<property name="alignment">
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string notr="true">EX+</string>
</property>
<property name="alignment">
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="rangeFromPlayRating_ExPlusLabel">
<property name="text">
<string notr="true">...</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="rangeFromPlayRating_ExLabel">
<property name="text">
<string notr="true">...</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="rangeFromPlayRating_AaLabel">
<property name="text">
<string notr="true">...</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="1">
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>chartsByConstant</string>
@ -147,51 +49,200 @@
</item>
<item>
<widget class="QLabel" name="chartsByConstant_numLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true">...</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="chartsByConstant_refreshButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>refreshButton</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QWidget" name="widget" native="true">
<widget class="QListView" name="chartsByConstant_modelView">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QGridLayout" name="chartsByConstant_gridLayout"/>
<property name="minimumSize">
<size>
<width>150</width>
<height>0</height>
</size>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::NoSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="verticalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
<property name="horizontalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<item row="0" column="0">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>constantRangeFromPlayRating</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QDoubleSpinBox" name="rangeFromPlayRating_playRatingSpinBox">
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="maximum">
<double>100.000000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
</widget>
</item>
<item>
<layout class="QFormLayout" name="formLayout">
<property name="labelAlignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="formAlignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string notr="true">EX+</string>
</property>
<property name="alignment">
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="rangeFromPlayRating_ExPlusLabel">
<property name="text">
<string notr="true">...</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string notr="true">EX</string>
</property>
<property name="alignment">
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="rangeFromPlayRating_ExLabel">
<property name="text">
<string notr="true">...</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string notr="true">AA</string>
</property>
<property name="alignment">
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="rangeFromPlayRating_AaLabel">
<property name="text">
<string notr="true">...</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string notr="true">A</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="rangeFromPlayRating_ALabel">
<property name="text">
<string notr="true">...</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string notr="true">B</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="rangeFromPlayRating_BLabel">
<property name="text">
<string notr="true">...</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string notr="true">C</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLabel" name="rangeFromPlayRating_CLabel">
<property name="text">
<string notr="true">...</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>chartsRecommendFromPlayRating</string>
@ -246,45 +297,48 @@
</item>
<item>
<widget class="QLabel" name="chartsRecommendFromPlayRating_numLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true">...</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="chartsRecommendFromPlayRating_refreshButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>refreshButton</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QWidget" name="widget_2" native="true">
<widget class="QTableView" name="chartsRecommendFromPlayRating_modelView">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QGridLayout" name="chartsRecommendFromPlayRating_gridLayout"/>
<property name="minimumSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::NoSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="verticalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
<property name="horizontalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
</widget>
</item>
</layout>

View File

@ -15,82 +15,19 @@ from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
QFont, QFontDatabase, QGradient, QIcon,
QImage, QKeySequence, QLinearGradient, QPainter,
QPalette, QPixmap, QRadialGradient, QTransform)
from PySide6.QtWidgets import (QApplication, QDoubleSpinBox, QGridLayout, QGroupBox,
QHBoxLayout, QLabel, QPushButton, QSizePolicy,
QSpacerItem, QVBoxLayout, QWidget)
from PySide6.QtWidgets import (QAbstractItemView, QApplication, QDoubleSpinBox, QFormLayout,
QGridLayout, QGroupBox, QHBoxLayout, QHeaderView,
QLabel, QListView, QSizePolicy, QTableView,
QVBoxLayout, QWidget)
class Ui_TabTools_ChartRecommend(object):
def setupUi(self, TabTools_ChartRecommend):
if not TabTools_ChartRecommend.objectName():
TabTools_ChartRecommend.setObjectName(u"TabTools_ChartRecommend")
TabTools_ChartRecommend.resize(668, 546)
TabTools_ChartRecommend.resize(616, 500)
TabTools_ChartRecommend.setWindowTitle(u"TabTools_ChartRecommend")
self.verticalLayout = QVBoxLayout(TabTools_ChartRecommend)
self.verticalLayout.setObjectName(u"verticalLayout")
self.groupBox = QGroupBox(TabTools_ChartRecommend)
self.groupBox.setObjectName(u"groupBox")
self.verticalLayout_2 = QVBoxLayout(self.groupBox)
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
self.rangeFromPlayRating_playRatingSpinBox = QDoubleSpinBox(self.groupBox)
self.rangeFromPlayRating_playRatingSpinBox.setObjectName(u"rangeFromPlayRating_playRatingSpinBox")
self.rangeFromPlayRating_playRatingSpinBox.setMinimumSize(QSize(100, 0))
self.rangeFromPlayRating_playRatingSpinBox.setMaximumSize(QSize(100, 16777215))
self.rangeFromPlayRating_playRatingSpinBox.setDecimals(3)
self.rangeFromPlayRating_playRatingSpinBox.setMaximum(100.000000000000000)
self.rangeFromPlayRating_playRatingSpinBox.setSingleStep(0.100000000000000)
self.verticalLayout_2.addWidget(self.rangeFromPlayRating_playRatingSpinBox)
self.gridLayout_3 = QGridLayout()
self.gridLayout_3.setObjectName(u"gridLayout_3")
self.label_3 = QLabel(self.groupBox)
self.label_3.setObjectName(u"label_3")
self.label_3.setText(u"AA")
self.label_3.setAlignment(Qt.AlignBottom|Qt.AlignLeading|Qt.AlignLeft)
self.gridLayout_3.addWidget(self.label_3, 0, 2, 1, 1)
self.label_2 = QLabel(self.groupBox)
self.label_2.setObjectName(u"label_2")
self.label_2.setText(u"EX")
self.label_2.setAlignment(Qt.AlignBottom|Qt.AlignLeading|Qt.AlignLeft)
self.gridLayout_3.addWidget(self.label_2, 0, 1, 1, 1)
self.label = QLabel(self.groupBox)
self.label.setObjectName(u"label")
self.label.setText(u"EX+")
self.label.setAlignment(Qt.AlignBottom|Qt.AlignLeading|Qt.AlignLeft)
self.gridLayout_3.addWidget(self.label, 0, 0, 1, 1)
self.rangeFromPlayRating_ExPlusLabel = QLabel(self.groupBox)
self.rangeFromPlayRating_ExPlusLabel.setObjectName(u"rangeFromPlayRating_ExPlusLabel")
self.rangeFromPlayRating_ExPlusLabel.setText(u"...")
self.rangeFromPlayRating_ExPlusLabel.setAlignment(Qt.AlignLeading|Qt.AlignLeft|Qt.AlignTop)
self.gridLayout_3.addWidget(self.rangeFromPlayRating_ExPlusLabel, 1, 0, 1, 1)
self.rangeFromPlayRating_ExLabel = QLabel(self.groupBox)
self.rangeFromPlayRating_ExLabel.setObjectName(u"rangeFromPlayRating_ExLabel")
self.rangeFromPlayRating_ExLabel.setText(u"...")
self.rangeFromPlayRating_ExLabel.setAlignment(Qt.AlignLeading|Qt.AlignLeft|Qt.AlignTop)
self.gridLayout_3.addWidget(self.rangeFromPlayRating_ExLabel, 1, 1, 1, 1)
self.rangeFromPlayRating_AaLabel = QLabel(self.groupBox)
self.rangeFromPlayRating_AaLabel.setObjectName(u"rangeFromPlayRating_AaLabel")
self.rangeFromPlayRating_AaLabel.setText(u"...")
self.rangeFromPlayRating_AaLabel.setAlignment(Qt.AlignLeading|Qt.AlignLeft|Qt.AlignTop)
self.gridLayout_3.addWidget(self.rangeFromPlayRating_AaLabel, 1, 2, 1, 1)
self.verticalLayout_2.addLayout(self.gridLayout_3)
self.verticalLayout.addWidget(self.groupBox)
self.gridLayout = QGridLayout(TabTools_ChartRecommend)
self.gridLayout.setObjectName(u"gridLayout")
self.groupBox_2 = QGroupBox(TabTools_ChartRecommend)
self.groupBox_2.setObjectName(u"groupBox_2")
self.verticalLayout_3 = QVBoxLayout(self.groupBox_2)
@ -109,37 +46,138 @@ class Ui_TabTools_ChartRecommend(object):
self.chartsByConstant_numLabel = QLabel(self.groupBox_2)
self.chartsByConstant_numLabel.setObjectName(u"chartsByConstant_numLabel")
sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.chartsByConstant_numLabel.sizePolicy().hasHeightForWidth())
self.chartsByConstant_numLabel.setSizePolicy(sizePolicy)
self.chartsByConstant_numLabel.setText(u"...")
self.horizontalLayout_3.addWidget(self.chartsByConstant_numLabel)
self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
self.horizontalLayout_3.addItem(self.horizontalSpacer)
self.chartsByConstant_refreshButton = QPushButton(self.groupBox_2)
self.chartsByConstant_refreshButton.setObjectName(u"chartsByConstant_refreshButton")
self.chartsByConstant_refreshButton.setEnabled(False)
self.horizontalLayout_3.addWidget(self.chartsByConstant_refreshButton)
self.verticalLayout_3.addLayout(self.horizontalLayout_3)
self.widget = QWidget(self.groupBox_2)
self.widget.setObjectName(u"widget")
sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.widget.sizePolicy().hasHeightForWidth())
self.widget.setSizePolicy(sizePolicy)
self.chartsByConstant_gridLayout = QGridLayout(self.widget)
self.chartsByConstant_gridLayout.setObjectName(u"chartsByConstant_gridLayout")
self.chartsByConstant_modelView = QListView(self.groupBox_2)
self.chartsByConstant_modelView.setObjectName(u"chartsByConstant_modelView")
sizePolicy1 = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding)
sizePolicy1.setHorizontalStretch(0)
sizePolicy1.setVerticalStretch(0)
sizePolicy1.setHeightForWidth(self.chartsByConstant_modelView.sizePolicy().hasHeightForWidth())
self.chartsByConstant_modelView.setSizePolicy(sizePolicy1)
self.chartsByConstant_modelView.setMinimumSize(QSize(150, 0))
self.chartsByConstant_modelView.setEditTriggers(QAbstractItemView.NoEditTriggers)
self.chartsByConstant_modelView.setSelectionMode(QAbstractItemView.NoSelection)
self.chartsByConstant_modelView.setSelectionBehavior(QAbstractItemView.SelectRows)
self.chartsByConstant_modelView.setVerticalScrollMode(QAbstractItemView.ScrollPerPixel)
self.chartsByConstant_modelView.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel)
self.verticalLayout_3.addWidget(self.widget)
self.verticalLayout_3.addWidget(self.chartsByConstant_modelView)
self.verticalLayout.addWidget(self.groupBox_2)
self.gridLayout.addWidget(self.groupBox_2, 0, 1, 1, 1)
self.groupBox = QGroupBox(TabTools_ChartRecommend)
self.groupBox.setObjectName(u"groupBox")
self.verticalLayout_2 = QVBoxLayout(self.groupBox)
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
self.rangeFromPlayRating_playRatingSpinBox = QDoubleSpinBox(self.groupBox)
self.rangeFromPlayRating_playRatingSpinBox.setObjectName(u"rangeFromPlayRating_playRatingSpinBox")
self.rangeFromPlayRating_playRatingSpinBox.setMinimumSize(QSize(100, 0))
self.rangeFromPlayRating_playRatingSpinBox.setMaximumSize(QSize(100, 16777215))
self.rangeFromPlayRating_playRatingSpinBox.setDecimals(3)
self.rangeFromPlayRating_playRatingSpinBox.setMaximum(100.000000000000000)
self.rangeFromPlayRating_playRatingSpinBox.setSingleStep(0.100000000000000)
self.verticalLayout_2.addWidget(self.rangeFromPlayRating_playRatingSpinBox)
self.formLayout = QFormLayout()
self.formLayout.setObjectName(u"formLayout")
self.formLayout.setLabelAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
self.formLayout.setFormAlignment(Qt.AlignLeading|Qt.AlignLeft|Qt.AlignVCenter)
self.label = QLabel(self.groupBox)
self.label.setObjectName(u"label")
self.label.setText(u"EX+")
self.label.setAlignment(Qt.AlignBottom|Qt.AlignLeading|Qt.AlignLeft)
self.formLayout.setWidget(0, QFormLayout.LabelRole, self.label)
self.rangeFromPlayRating_ExPlusLabel = QLabel(self.groupBox)
self.rangeFromPlayRating_ExPlusLabel.setObjectName(u"rangeFromPlayRating_ExPlusLabel")
self.rangeFromPlayRating_ExPlusLabel.setText(u"...")
self.rangeFromPlayRating_ExPlusLabel.setAlignment(Qt.AlignLeading|Qt.AlignLeft|Qt.AlignTop)
self.formLayout.setWidget(0, QFormLayout.FieldRole, self.rangeFromPlayRating_ExPlusLabel)
self.label_2 = QLabel(self.groupBox)
self.label_2.setObjectName(u"label_2")
self.label_2.setText(u"EX")
self.label_2.setAlignment(Qt.AlignBottom|Qt.AlignLeading|Qt.AlignLeft)
self.formLayout.setWidget(1, QFormLayout.LabelRole, self.label_2)
self.rangeFromPlayRating_ExLabel = QLabel(self.groupBox)
self.rangeFromPlayRating_ExLabel.setObjectName(u"rangeFromPlayRating_ExLabel")
self.rangeFromPlayRating_ExLabel.setText(u"...")
self.rangeFromPlayRating_ExLabel.setAlignment(Qt.AlignLeading|Qt.AlignLeft|Qt.AlignTop)
self.formLayout.setWidget(1, QFormLayout.FieldRole, self.rangeFromPlayRating_ExLabel)
self.label_3 = QLabel(self.groupBox)
self.label_3.setObjectName(u"label_3")
self.label_3.setText(u"AA")
self.label_3.setAlignment(Qt.AlignBottom|Qt.AlignLeading|Qt.AlignLeft)
self.formLayout.setWidget(2, QFormLayout.LabelRole, self.label_3)
self.rangeFromPlayRating_AaLabel = QLabel(self.groupBox)
self.rangeFromPlayRating_AaLabel.setObjectName(u"rangeFromPlayRating_AaLabel")
self.rangeFromPlayRating_AaLabel.setText(u"...")
self.rangeFromPlayRating_AaLabel.setAlignment(Qt.AlignLeading|Qt.AlignLeft|Qt.AlignTop)
self.formLayout.setWidget(2, QFormLayout.FieldRole, self.rangeFromPlayRating_AaLabel)
self.label_5 = QLabel(self.groupBox)
self.label_5.setObjectName(u"label_5")
self.label_5.setText(u"A")
self.formLayout.setWidget(3, QFormLayout.LabelRole, self.label_5)
self.rangeFromPlayRating_ALabel = QLabel(self.groupBox)
self.rangeFromPlayRating_ALabel.setObjectName(u"rangeFromPlayRating_ALabel")
self.rangeFromPlayRating_ALabel.setText(u"...")
self.formLayout.setWidget(3, QFormLayout.FieldRole, self.rangeFromPlayRating_ALabel)
self.label_8 = QLabel(self.groupBox)
self.label_8.setObjectName(u"label_8")
self.label_8.setText(u"B")
self.formLayout.setWidget(4, QFormLayout.LabelRole, self.label_8)
self.rangeFromPlayRating_BLabel = QLabel(self.groupBox)
self.rangeFromPlayRating_BLabel.setObjectName(u"rangeFromPlayRating_BLabel")
self.rangeFromPlayRating_BLabel.setText(u"...")
self.formLayout.setWidget(4, QFormLayout.FieldRole, self.rangeFromPlayRating_BLabel)
self.label_10 = QLabel(self.groupBox)
self.label_10.setObjectName(u"label_10")
self.label_10.setText(u"C")
self.formLayout.setWidget(5, QFormLayout.LabelRole, self.label_10)
self.rangeFromPlayRating_CLabel = QLabel(self.groupBox)
self.rangeFromPlayRating_CLabel.setObjectName(u"rangeFromPlayRating_CLabel")
self.rangeFromPlayRating_CLabel.setText(u"...")
self.formLayout.setWidget(5, QFormLayout.FieldRole, self.rangeFromPlayRating_CLabel)
self.verticalLayout_2.addLayout(self.formLayout)
self.gridLayout.addWidget(self.groupBox, 0, 0, 1, 1)
self.groupBox_3 = QGroupBox(TabTools_ChartRecommend)
self.groupBox_3.setObjectName(u"groupBox_3")
@ -173,34 +211,33 @@ class Ui_TabTools_ChartRecommend(object):
self.chartsRecommendFromPlayRating_numLabel = QLabel(self.groupBox_3)
self.chartsRecommendFromPlayRating_numLabel.setObjectName(u"chartsRecommendFromPlayRating_numLabel")
sizePolicy.setHeightForWidth(self.chartsRecommendFromPlayRating_numLabel.sizePolicy().hasHeightForWidth())
self.chartsRecommendFromPlayRating_numLabel.setSizePolicy(sizePolicy)
self.chartsRecommendFromPlayRating_numLabel.setText(u"...")
self.horizontalLayout_2.addWidget(self.chartsRecommendFromPlayRating_numLabel)
self.horizontalSpacer_2 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
self.horizontalLayout_2.addItem(self.horizontalSpacer_2)
self.chartsRecommendFromPlayRating_refreshButton = QPushButton(self.groupBox_3)
self.chartsRecommendFromPlayRating_refreshButton.setObjectName(u"chartsRecommendFromPlayRating_refreshButton")
self.chartsRecommendFromPlayRating_refreshButton.setEnabled(False)
self.horizontalLayout_2.addWidget(self.chartsRecommendFromPlayRating_refreshButton)
self.verticalLayout_4.addLayout(self.horizontalLayout_2)
self.widget_2 = QWidget(self.groupBox_3)
self.widget_2.setObjectName(u"widget_2")
sizePolicy.setHeightForWidth(self.widget_2.sizePolicy().hasHeightForWidth())
self.widget_2.setSizePolicy(sizePolicy)
self.chartsRecommendFromPlayRating_gridLayout = QGridLayout(self.widget_2)
self.chartsRecommendFromPlayRating_gridLayout.setObjectName(u"chartsRecommendFromPlayRating_gridLayout")
self.chartsRecommendFromPlayRating_modelView = QTableView(self.groupBox_3)
self.chartsRecommendFromPlayRating_modelView.setObjectName(u"chartsRecommendFromPlayRating_modelView")
sizePolicy2 = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
sizePolicy2.setHorizontalStretch(0)
sizePolicy2.setVerticalStretch(0)
sizePolicy2.setHeightForWidth(self.chartsRecommendFromPlayRating_modelView.sizePolicy().hasHeightForWidth())
self.chartsRecommendFromPlayRating_modelView.setSizePolicy(sizePolicy2)
self.chartsRecommendFromPlayRating_modelView.setMinimumSize(QSize(200, 0))
self.chartsRecommendFromPlayRating_modelView.setEditTriggers(QAbstractItemView.NoEditTriggers)
self.chartsRecommendFromPlayRating_modelView.setSelectionMode(QAbstractItemView.NoSelection)
self.chartsRecommendFromPlayRating_modelView.setSelectionBehavior(QAbstractItemView.SelectRows)
self.chartsRecommendFromPlayRating_modelView.setVerticalScrollMode(QAbstractItemView.ScrollPerPixel)
self.chartsRecommendFromPlayRating_modelView.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel)
self.verticalLayout_4.addWidget(self.widget_2)
self.verticalLayout_4.addWidget(self.chartsRecommendFromPlayRating_modelView)
self.verticalLayout.addWidget(self.groupBox_3)
self.gridLayout.addWidget(self.groupBox_3, 1, 0, 1, 2)
self.retranslateUi(TabTools_ChartRecommend)
@ -209,11 +246,9 @@ class Ui_TabTools_ChartRecommend(object):
# setupUi
def retranslateUi(self, TabTools_ChartRecommend):
self.groupBox.setTitle(QCoreApplication.translate("TabTools_ChartRecommend", u"constantRangeFromPlayRating", None))
self.groupBox_2.setTitle(QCoreApplication.translate("TabTools_ChartRecommend", u"chartsByConstant", None))
self.chartsByConstant_refreshButton.setText(QCoreApplication.translate("TabTools_ChartRecommend", u"refreshButton", None))
self.groupBox.setTitle(QCoreApplication.translate("TabTools_ChartRecommend", u"constantRangeFromPlayRating", None))
self.groupBox_3.setTitle(QCoreApplication.translate("TabTools_ChartRecommend", u"chartsRecommendFromPlayRating", None))
self.chartsRecommendFromPlayRating_refreshButton.setText(QCoreApplication.translate("TabTools_ChartRecommend", u"refreshButton", None))
pass
# retranslateUi

View File

@ -694,57 +694,9 @@
<property name="title">
<string>playRatingCalculate</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLineEdit" name="playRatingCalculateScoreLineEdit">
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>150</width>
<height>16777215</height>
</size>
</property>
<property name="inputMask">
<string notr="true">B9'999'999;_</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string notr="true">&gt;</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="playRatingCalculateResultLabel">
<property name="font">
<font>
<bold>true</bold>
</font>
</property>
<property name="text">
<string notr="true">...</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
<widget class="PlayRatingCalculator" name="playRatingCalculator" native="true"/>
</item>
</layout>
</widget>
@ -764,6 +716,12 @@
<header>ui.implements.components.ratingClassSelector</header>
<container>1</container>
</customwidget>
<customwidget>
<class>PlayRatingCalculator</class>
<extends>QWidget</extends>
<header>ui.implements.components.playRatingCalculator</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>

View File

@ -16,9 +16,10 @@ from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
QImage, QKeySequence, QLinearGradient, QPainter,
QPalette, QPixmap, QRadialGradient, QTransform)
from PySide6.QtWidgets import (QApplication, QComboBox, QFrame, QGridLayout,
QGroupBox, QHBoxLayout, QLabel, QLineEdit,
QSizePolicy, QSpacerItem, QVBoxLayout, QWidget)
QGroupBox, QHBoxLayout, QLabel, QSizePolicy,
QVBoxLayout, QWidget)
from ui.implements.components.playRatingCalculator import PlayRatingCalculator
from ui.implements.components.ratingClassSelector import RatingClassSelector
from ui.implements.components.songIdSelector import SongIdSelector
@ -502,34 +503,12 @@ class Ui_TabTools_InfoLookup(object):
self.groupBox_6 = QGroupBox(TabTools_InfoLookup)
self.groupBox_6.setObjectName(u"groupBox_6")
self.horizontalLayout_5 = QHBoxLayout(self.groupBox_6)
self.horizontalLayout_5.setObjectName(u"horizontalLayout_5")
self.playRatingCalculateScoreLineEdit = QLineEdit(self.groupBox_6)
self.playRatingCalculateScoreLineEdit.setObjectName(u"playRatingCalculateScoreLineEdit")
self.playRatingCalculateScoreLineEdit.setMinimumSize(QSize(100, 0))
self.playRatingCalculateScoreLineEdit.setMaximumSize(QSize(150, 16777215))
self.playRatingCalculateScoreLineEdit.setInputMask(u"B9'999'999;_")
self.verticalLayout_2 = QVBoxLayout(self.groupBox_6)
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
self.playRatingCalculator = PlayRatingCalculator(self.groupBox_6)
self.playRatingCalculator.setObjectName(u"playRatingCalculator")
self.horizontalLayout_5.addWidget(self.playRatingCalculateScoreLineEdit)
self.label = QLabel(self.groupBox_6)
self.label.setObjectName(u"label")
self.label.setText(u">")
self.horizontalLayout_5.addWidget(self.label)
self.playRatingCalculateResultLabel = QLabel(self.groupBox_6)
self.playRatingCalculateResultLabel.setObjectName(u"playRatingCalculateResultLabel")
font = QFont()
font.setBold(True)
self.playRatingCalculateResultLabel.setFont(font)
self.playRatingCalculateResultLabel.setText(u"...")
self.horizontalLayout_5.addWidget(self.playRatingCalculateResultLabel)
self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
self.horizontalLayout_5.addItem(self.horizontalSpacer)
self.verticalLayout_2.addWidget(self.playRatingCalculator)
self.verticalLayout.addWidget(self.groupBox_6)

View File

@ -165,7 +165,7 @@
<widget class="QRadioButton" name="legacyPlayPlus_x125fragRadioButton">
<property name="text">
<string notr="true">x1.25
125</string>
250</string>
</property>
</widget>
</item>
@ -361,6 +361,13 @@
<property name="labelAlignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>calculate.toStep.playResultLabel</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QDoubleSpinBox" name="calculate_toStep_playResultSpinBox">
<property name="sizePolicy">
@ -380,10 +387,10 @@
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<item row="1" column="1">
<widget class="QPushButton" name="calculate_toStep_calculatePlayResultFromScoreButton">
<property name="text">
<string>calculate.toStep.playResultLabel</string>
<string>calculate.toStep.calculatePlayResultFromScoreButton</string>
</property>
</widget>
</item>
@ -401,24 +408,36 @@
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="calculate_toStep_resultLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true">...</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="calculate_toStep_calculatePlayResultFromScoreButton">
<property name="text">
<string>calculate.toStep.calculatePlayResultFromScoreButton</string>
</property>
</widget>
<layout class="QVBoxLayout" name="verticalLayout_9">
<item>
<widget class="QLabel" name="calculate_toStep_resultLabel">
<property name="text">
<string notr="true">...</string>
</property>
<property name="alignment">
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="calculate_toStep_detailedResultLabel">
<property name="font">
<font>
<pointsize>8</pointsize>
</font>
</property>
<property name="styleSheet">
<string notr="true">QLabel { color: gray; }</string>
</property>
<property name="text">
<string notr="true">...</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
@ -463,17 +482,36 @@
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="calculate_fromStep_resultLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true">...</string>
</property>
</widget>
<layout class="QVBoxLayout" name="verticalLayout_10">
<item>
<widget class="QLabel" name="calculate_fromStep_resultLabel">
<property name="text">
<string notr="true">...</string>
</property>
<property name="alignment">
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="calculate_fromStep_detailedResultLabel">
<property name="font">
<font>
<pointsize>8</pointsize>
</font>
</property>
<property name="styleSheet">
<string notr="true">QLabel { color: gray; }</string>
</property>
<property name="text">
<string notr="true">...</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>

View File

@ -137,7 +137,7 @@ class Ui_TabTools_StepCalculator(object):
self.legacyPlayPlus_x125fragRadioButton = QRadioButton(self.legacyPlayPlus_useFragmentsGroupBox)
self.legacyPlayPlus_x125fragRadioButton.setObjectName(u"legacyPlayPlus_x125fragRadioButton")
self.legacyPlayPlus_x125fragRadioButton.setText(u"x1.25\n"
"125")
"250")
self.horizontalLayout_3.addWidget(self.legacyPlayPlus_x125fragRadioButton)
@ -288,6 +288,11 @@ class Ui_TabTools_StepCalculator(object):
self.formLayout_2 = QFormLayout(self.groupBox)
self.formLayout_2.setObjectName(u"formLayout_2")
self.formLayout_2.setLabelAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
self.label_2 = QLabel(self.groupBox)
self.label_2.setObjectName(u"label_2")
self.formLayout_2.setWidget(0, QFormLayout.LabelRole, self.label_2)
self.calculate_toStep_playResultSpinBox = QDoubleSpinBox(self.groupBox)
self.calculate_toStep_playResultSpinBox.setObjectName(u"calculate_toStep_playResultSpinBox")
sizePolicy2.setHeightForWidth(self.calculate_toStep_playResultSpinBox.sizePolicy().hasHeightForWidth())
@ -298,10 +303,10 @@ class Ui_TabTools_StepCalculator(object):
self.formLayout_2.setWidget(0, QFormLayout.FieldRole, self.calculate_toStep_playResultSpinBox)
self.label_2 = QLabel(self.groupBox)
self.label_2.setObjectName(u"label_2")
self.calculate_toStep_calculatePlayResultFromScoreButton = QPushButton(self.groupBox)
self.calculate_toStep_calculatePlayResultFromScoreButton.setObjectName(u"calculate_toStep_calculatePlayResultFromScoreButton")
self.formLayout_2.setWidget(0, QFormLayout.LabelRole, self.label_2)
self.formLayout_2.setWidget(1, QFormLayout.FieldRole, self.calculate_toStep_calculatePlayResultFromScoreButton)
self.label_7 = QLabel(self.groupBox)
self.label_7.setObjectName(u"label_7")
@ -310,18 +315,28 @@ class Ui_TabTools_StepCalculator(object):
self.formLayout_2.setWidget(2, QFormLayout.LabelRole, self.label_7)
self.verticalLayout_9 = QVBoxLayout()
self.verticalLayout_9.setObjectName(u"verticalLayout_9")
self.calculate_toStep_resultLabel = QLabel(self.groupBox)
self.calculate_toStep_resultLabel.setObjectName(u"calculate_toStep_resultLabel")
sizePolicy4.setHeightForWidth(self.calculate_toStep_resultLabel.sizePolicy().hasHeightForWidth())
self.calculate_toStep_resultLabel.setSizePolicy(sizePolicy4)
self.calculate_toStep_resultLabel.setText(u"...")
self.calculate_toStep_resultLabel.setAlignment(Qt.AlignBottom|Qt.AlignLeading|Qt.AlignLeft)
self.formLayout_2.setWidget(2, QFormLayout.FieldRole, self.calculate_toStep_resultLabel)
self.verticalLayout_9.addWidget(self.calculate_toStep_resultLabel)
self.calculate_toStep_calculatePlayResultFromScoreButton = QPushButton(self.groupBox)
self.calculate_toStep_calculatePlayResultFromScoreButton.setObjectName(u"calculate_toStep_calculatePlayResultFromScoreButton")
self.calculate_toStep_detailedResultLabel = QLabel(self.groupBox)
self.calculate_toStep_detailedResultLabel.setObjectName(u"calculate_toStep_detailedResultLabel")
font = QFont()
font.setPointSize(8)
self.calculate_toStep_detailedResultLabel.setFont(font)
self.calculate_toStep_detailedResultLabel.setStyleSheet(u"QLabel { color: gray; }")
self.calculate_toStep_detailedResultLabel.setText(u"...")
self.calculate_toStep_detailedResultLabel.setAlignment(Qt.AlignLeading|Qt.AlignLeft|Qt.AlignTop)
self.formLayout_2.setWidget(1, QFormLayout.FieldRole, self.calculate_toStep_calculatePlayResultFromScoreButton)
self.verticalLayout_9.addWidget(self.calculate_toStep_detailedResultLabel)
self.formLayout_2.setLayout(2, QFormLayout.FieldRole, self.verticalLayout_9)
self.horizontalLayout_4.addWidget(self.groupBox)
@ -350,13 +365,26 @@ class Ui_TabTools_StepCalculator(object):
self.formLayout_3.setWidget(1, QFormLayout.LabelRole, self.label_9)
self.verticalLayout_10 = QVBoxLayout()
self.verticalLayout_10.setObjectName(u"verticalLayout_10")
self.calculate_fromStep_resultLabel = QLabel(self.groupBox_2)
self.calculate_fromStep_resultLabel.setObjectName(u"calculate_fromStep_resultLabel")
sizePolicy4.setHeightForWidth(self.calculate_fromStep_resultLabel.sizePolicy().hasHeightForWidth())
self.calculate_fromStep_resultLabel.setSizePolicy(sizePolicy4)
self.calculate_fromStep_resultLabel.setText(u"...")
self.calculate_fromStep_resultLabel.setAlignment(Qt.AlignBottom|Qt.AlignLeading|Qt.AlignLeft)
self.formLayout_3.setWidget(1, QFormLayout.FieldRole, self.calculate_fromStep_resultLabel)
self.verticalLayout_10.addWidget(self.calculate_fromStep_resultLabel)
self.calculate_fromStep_detailedResultLabel = QLabel(self.groupBox_2)
self.calculate_fromStep_detailedResultLabel.setObjectName(u"calculate_fromStep_detailedResultLabel")
self.calculate_fromStep_detailedResultLabel.setFont(font)
self.calculate_fromStep_detailedResultLabel.setStyleSheet(u"QLabel { color: gray; }")
self.calculate_fromStep_detailedResultLabel.setText(u"...")
self.calculate_fromStep_detailedResultLabel.setAlignment(Qt.AlignLeading|Qt.AlignLeft|Qt.AlignTop)
self.verticalLayout_10.addWidget(self.calculate_fromStep_detailedResultLabel)
self.formLayout_3.setLayout(1, QFormLayout.FieldRole, self.verticalLayout_10)
self.horizontalLayout_4.addWidget(self.groupBox_2)
@ -394,8 +422,8 @@ class Ui_TabTools_StepCalculator(object):
self.partnerSkillPresetButton_maya.setText(QCoreApplication.translate("TabTools_StepCalculator", u"partner.skill.presets.maya", None))
self.groupBox.setTitle(QCoreApplication.translate("TabTools_StepCalculator", u"calculate.toStep", None))
self.label_2.setText(QCoreApplication.translate("TabTools_StepCalculator", u"calculate.toStep.playResultLabel", None))
self.label_7.setText(QCoreApplication.translate("TabTools_StepCalculator", u"calculate.toStep.resultLabel", None))
self.calculate_toStep_calculatePlayResultFromScoreButton.setText(QCoreApplication.translate("TabTools_StepCalculator", u"calculate.toStep.calculatePlayResultFromScoreButton", None))
self.label_7.setText(QCoreApplication.translate("TabTools_StepCalculator", u"calculate.toStep.resultLabel", None))
self.groupBox_2.setTitle(QCoreApplication.translate("TabTools_StepCalculator", u"calculate.fromStep", None))
self.label_4.setText(QCoreApplication.translate("TabTools_StepCalculator", u"calculate.fromStep.targetStepLabel", None))
self.label_9.setText(QCoreApplication.translate("TabTools_StepCalculator", u"calculate.fromStep.resultLabel", None))

View File

@ -6,8 +6,7 @@ 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.shared import DeviceOcrResult
from arcaea_offline_ocr.utils import convert_to_srgb
from arcaea_offline_ocr.device.common import DeviceOcrResult
from PIL import Image
from PIL.ImageQt import ImageQt
from PySide6.QtCore import (
@ -25,6 +24,7 @@ from PySide6.QtCore import (
)
from PySide6.QtGui import QImage, QPixmap
from ui.extends.ocr import convert_to_srgb
from ui.extends.shared.delegates.chartDelegate import ChartDelegate
from ui.extends.shared.delegates.imageDelegate import ImageDelegate
from ui.extends.shared.delegates.scoreDelegate import ScoreDelegate
@ -46,7 +46,7 @@ class OcrRunnable(QRunnable):
class IccOption(IntEnum):
Ignore = 0
UseQt = 0
UsePIL = 1
TryFix = 2
@ -150,6 +150,7 @@ class OcrQueueModel(QAbstractListModel):
@iccOption.setter
def iccOption(self, opt: IccOption):
logger.debug(f"ICC option changed to {opt}")
self.__iccOption = opt
@overload
@ -344,8 +345,12 @@ 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

View File

@ -1,26 +0,0 @@
try:
import json
from arcaea_offline_ocr.device.v1.definition import DeviceV1
from arcaea_offline_ocr.device.v2.definition import DeviceV2
def load_devices_json(filepath: str) -> list[DeviceV1]:
with open(filepath, "r", encoding="utf-8") as f:
file_content = f.read()
if len(file_content) == 0:
return []
content = json.loads(file_content)
assert isinstance(content, list)
devices = []
for item in content:
version = item["version"]
if version == 1:
devices.append(DeviceV1(**item))
elif version == 2:
devices.append(DeviceV2(**item))
return devices
except Exception:
def load_devices_json(*args, **kwargs):
pass

View File

@ -0,0 +1,27 @@
import io
from PIL import Image, ImageCms
from .build_phash import build_image_phash_database
def convert_to_srgb(pil_img: Image.Image):
"""
Convert PIL image to sRGB color space (if possible)
and save the converted file.
https://stackoverflow.com/a/65667797/16484891
CC BY-SA 4.0
"""
icc = pil_img.info.get("icc_profile", "")
icc_conv = ""
if icc:
io_handle = io.BytesIO(icc) # virtual file
src_profile = ImageCms.ImageCmsProfile(io_handle)
dst_profile = ImageCms.createProfile("sRGB")
img_conv = ImageCms.profileToProfile(pil_img, src_profile, dst_profile)
icc_conv = img_conv.info.get("icc_profile", "")
return img_conv if icc != icc_conv else pil_img

View File

@ -0,0 +1,76 @@
import sqlite3
import time
from typing import Any, Callable, Optional
import cv2
import numpy as np
from arcaea_offline_ocr.phash_db import phash_opencv
def preprocess_char_icon(img_gray: np.ndarray):
h, w = img_gray.shape[:2]
img = cv2.fillPoly(
img_gray,
[
np.array([[0, 0], [round(w / 2), 0], [0, round(h / 2)]], np.int32),
np.array([[w, 0], [round(w / 2), 0], [w, round(h / 2)]], np.int32),
np.array([[0, h], [round(w / 2), h], [0, round(h / 2)]], np.int32),
np.array([[w, h], [round(w / 2), h], [w, round(h / 2)]], np.int32),
],
(128),
)
return img
def build_image_phash_database(
images: list[np.ndarray],
labels: list[str],
*,
hash_size: int = 16,
highfreq_factor: int = 4,
progress_func: Optional[Callable[[int, int], Any]] = None,
):
assert len(images) == len(labels)
conn = sqlite3.connect(":memory:", check_same_thread=False)
with conn:
cursor = conn.cursor()
cursor.execute("CREATE TABLE properties (key TEXT, value TEXT)")
cursor.executemany(
"INSERT INTO properties VALUES (?, ?)",
[
("hash_size", hash_size),
("highfreq_factor", highfreq_factor),
],
)
image_num = len(images)
id_hashes = []
for i, label, image in zip(range(image_num), labels, images):
image_hash = phash_opencv(
image,
hash_size=hash_size,
highfreq_factor=highfreq_factor,
)
image_hash_bytes = image_hash.flatten().tobytes()
id_hashes.append([label, image_hash_bytes])
if progress_func:
progress_func(i + 1, image_num)
hash_length = len(id_hashes[0][1])
cursor.execute(f"CREATE TABLE hashes (id TEXT, hash BLOB({hash_length}))")
cursor.executemany(
"INSERT INTO hashes VALUES (?, ?)",
id_hashes,
)
cursor.executemany(
"INSERT INTO properties VALUES (?, ?)",
[("built_timestamp", int(time.time()))],
)
conn.commit()
return conn

View File

@ -0,0 +1,28 @@
import cv2
from arcaea_offline_ocr.phash_db import ImagePhashDatabase
def getCv2StatModelStatusText(model: cv2.ml.StatModel):
if not isinstance(model, cv2.ml.StatModel):
return '<font color="red">ERROR</font>'
varCount = model.getVarCount()
if varCount != 81:
return f'<font color="darkorange">WARN</font>, varCount {varCount}'
else:
return f'<font color="green">OK</font>, varCount {varCount}'
def getPhashDatabaseStatusText(db: ImagePhashDatabase):
if not isinstance(db, ImagePhashDatabase):
return '<font color="red">ERROR</font>'
jacketCount = len(db.jacket_hashes)
partnerIconCount = len(db.partner_icon_hashes)
statusText = f"J{jacketCount} PI{partnerIconCount}"
if partnerIconCount <= 0:
return f'<font color="darkorange">WARN</font>, {statusText}'
else:
return f'<font color="green">OK</font>, {statusText}'

View File

@ -1,28 +0,0 @@
import cv2
import numpy as np
from PySide6.QtGui import QImage
def cv2BgrMatToQImage(mat) -> QImage:
arr = np.ascontiguousarray(mat)
return QImage(
arr.data,
arr.shape[1],
arr.shape[0],
arr.strides[0],
QImage.Format.Format_RGB888,
).rgbSwapped()
def qImageToCvMatBgr(qImg: QImage):
# from Bing AI, references
# 1: https://stackoverflow.com/q/384759/16484891 | CC BY-SA 4.0
# 2: https://stackoverflow.com/q/37552924/16484891 | CC BY-SA 3.0
qImg = qImg.convertToFormat(QImage.Format.Format_RGB888)
qImg = qImg.copy().rgbSwapped()
return np.ndarray(
(qImg.height(), qImg.width(), 3),
buffer=qImg.constBits(),
strides=[qImg.bytesPerLine(), 3, 1],
dtype=np.uint8,
)

86
ui/extends/shared/data.py Normal file
View File

@ -0,0 +1,86 @@
import json
import sys
from functools import cached_property
from pathlib import Path
from typing import Literal, Optional, overload
from arcaea_offline.models import Chart, Difficulty, Song
from PySide6.QtCore import QFile
from .singleton import Singleton
TPartnerModifier = dict[str, Literal[0, 1, 2]]
class Data(metaclass=Singleton):
def __init__(self):
root = Path(sys.argv[0]).parent
self.__dataPath = (root / "data").resolve()
@property
def dataPath(self):
return self.__dataPath
@cached_property
def partnerModifiers(self) -> TPartnerModifier:
data = {}
builtinFile = QFile(":/partnerModifiers.json")
builtinFile.open(QFile.OpenModeFlag.ReadOnly)
builtinData = json.loads(str(builtinFile.readAll(), encoding="utf-8"))
builtinFile.close()
data |= builtinData
customFile = self.dataPath / "partnerModifiers.json"
if customFile.exists():
with open(customFile, "r", encoding="utf-8") as f:
customData = json.loads(f.read())
data |= customData
return data
def expirePartnerModifiersCache(self):
# expire property caches
# https://stackoverflow.com/a/69367025/16484891, CC BY-SA 4.0
self.__dict__.pop("partnerModifiers", None)
@property
def arcaeaPath(self):
return self.dataPath / "Arcaea"
@overload
def getJacketPath(self, chart: Chart, /) -> Path | None:
...
@overload
def getJacketPath(
self, song: Song, difficulty: Optional[Difficulty] = None, /
) -> Path | None:
...
def getJacketPath(self, *args) -> Path | None:
if isinstance(args[0], Chart):
chart = args[0]
ratingSpecified = f"{chart.song_id}_{chart.rating_class}"
base = chart.song_id
elif isinstance(args[0], Song):
song = args[0]
difficulty = args[1]
ratingSpecified = (
f"{song.id}_{difficulty.rating_class}"
if isinstance(difficulty, Difficulty)
else song.id
)
base = song.id
else:
raise ValueError()
ratingSpecified += ".jpg"
base += ".jpg"
jacketsPath = self.arcaeaPath / "Song"
if (jacketsPath / ratingSpecified).exists():
return jacketsPath / ratingSpecified
elif (jacketsPath / base).exists():
return jacketsPath / base
else:
return None

View File

@ -12,7 +12,8 @@ def create_engine(_url: str | QUrl, pool: Type[Pool] = NullPool) -> Engine:
class DatabaseUpdateSignals(QObject):
songDataUpdated = Signal()
songAddOrDelete = Signal()
chartInfoUpdated = Signal()
databaseUpdateSignals = DatabaseUpdateSignals()

View File

@ -1,8 +1,15 @@
from typing import Callable
from enum import IntEnum
from typing import Callable, Literal
from PySide6.QtCore import QEvent, QModelIndex, QObject, QPoint, QSize, Qt
from PySide6.QtCore import QModelIndex, QPoint, QSize, Qt
from PySide6.QtGui import QBrush, QColor, QFont, QFontMetrics, QLinearGradient, QPainter
from PySide6.QtWidgets import QApplication, QStyledItemDelegate, QStyleOptionViewItem
from PySide6.QtWidgets import QStyledItemDelegate, QStyleOptionViewItem
class TextSegmentDelegateVerticalAlign(IntEnum):
Top = 0
Middle = 1
Bottom = 2
class TextSegmentDelegate(QStyledItemDelegate):
@ -15,6 +22,44 @@ class TextSegmentDelegate(QStyledItemDelegate):
GradientWrapperRole = TextRole + 3
FontRole = TextRole + 20
def __init__(self, parent=None):
super().__init__(parent)
self.baseXOffsets: dict[str, int] = {}
self.baseYOffsets: dict[str, int] = {}
self.verticalAlign = TextSegmentDelegateVerticalAlign.Middle
def indexOffsetKey(self, index: QModelIndex):
return f"{index.row()},{index.column()}"
def setBaseXOffset(self, index: QModelIndex, offset: int):
key = self.indexOffsetKey(index)
if not offset:
self.baseXOffsets.pop(key, None)
else:
self.baseXOffsets[key] = offset
def setBaseYOffset(self, index: QModelIndex, offset: int):
key = self.indexOffsetKey(index)
if not offset:
self.baseYOffsets.pop(key, None)
else:
self.baseYOffsets[key] = offset
def setVerticalAlign(self, align: Literal["top", "middle", "bottom"]):
if not isinstance(align, str) and align not in ["top", "middle", "bottom"]:
raise ValueError(
"TextSegment only supports top/middle/bottom vertical aligning."
)
if align == "top":
self.verticalAlign = TextSegmentDelegateVerticalAlign.Top
elif align == "middle":
self.verticalAlign = TextSegmentDelegateVerticalAlign.Middle
elif align == "bottom":
self.verticalAlign = TextSegmentDelegateVerticalAlign.Bottom
def getTextSegments(
self, index: QModelIndex, option
) -> list[
@ -31,12 +76,14 @@ class TextSegmentDelegate(QStyledItemDelegate):
]:
return []
def sizeHint(self, option, index) -> QSize:
def textsSizeHint(self, option: QStyleOptionViewItem, index: QModelIndex) -> QSize:
width = 0
height = self.VerticalPadding
height = 0
fm: QFontMetrics = option.fontMetrics
for line in self.getTextSegments(index, option):
lineWidth = 4 * self.HorizontalPadding
segments = self.getTextSegments(index, option)
for i in range(len(segments)):
line = segments[i]
lineWidth = 2 * self.HorizontalPadding
lineHeight = 0
for textFrag in line:
font = textFrag.get(self.FontRole)
@ -47,17 +94,55 @@ class TextSegmentDelegate(QStyledItemDelegate):
lineWidth += textWidth
lineHeight = max(lineHeight, textHeight)
width = max(lineWidth, width)
height += lineHeight + self.VerticalPadding
height += lineHeight
if i != len(segments) - 1:
height += self.VerticalPadding
return QSize(width, height)
def sizeHint(self, option: QStyleOptionViewItem, index: QModelIndex) -> QSize:
width = self.HorizontalPadding * 2
height = self.VerticalPadding * 2
textsSizeHint = self.textsSizeHint(option, index)
return QSize(textsSizeHint.width() + width, textsSizeHint.height() + height)
def baseX(self, option: QStyleOptionViewItem, index: QModelIndex):
return (
option.rect.x()
+ self.HorizontalPadding
+ self.baseXOffsets.get(self.indexOffsetKey(index), 0)
)
def baseY(self, option: QStyleOptionViewItem, index: QModelIndex):
baseY = (
option.rect.y()
+ self.VerticalPadding
+ self.baseYOffsets.get(self.indexOffsetKey(index), 0)
)
if self.verticalAlign != TextSegmentDelegateVerticalAlign.Top:
paintAreaSize: QSize = option.rect.size()
delegateSize = self.sizeHint(option, index)
if self.verticalAlign == TextSegmentDelegateVerticalAlign.Middle:
baseY += round((paintAreaSize.height() - delegateSize.height()) / 2)
elif self.verticalAlign == TextSegmentDelegateVerticalAlign.Bottom:
baseY += paintAreaSize.height() - delegateSize.height()
return baseY
def textMaxWidth(self, option: QStyleOptionViewItem, index: QModelIndex):
return (
option.rect.width()
- (2 * self.HorizontalPadding)
- self.baseXOffsets.get(self.indexOffsetKey(index), 0)
)
def paint(
self, painter: QPainter, option: QStyleOptionViewItem, index: QModelIndex
):
self.initStyleOption(option, index)
# draw text only
baseX = option.rect.x() + self.HorizontalPadding
baseY = option.rect.y() + self.VerticalPadding
maxWidth = option.rect.width() - (2 * self.HorizontalPadding)
baseX = self.baseX(option, index)
baseY = self.baseY(option, index)
maxWidth = self.textMaxWidth(option, index)
fm: QFontMetrics = option.fontMetrics
painter.save()
for line in self.getTextSegments(index, option):
@ -69,8 +154,7 @@ class TextSegmentDelegate(QStyledItemDelegate):
# elide text, get font values
text = textFrag[self.TextRole]
fragMaxWidth = maxWidth - (lineBaseX - baseX)
font = textFrag.get(self.FontRole)
if font:
if font := textFrag.get(self.FontRole):
painter.setFont(font)
_fm = QFontMetrics(font)
else:
@ -116,37 +200,3 @@ class TextSegmentDelegate(QStyledItemDelegate):
def super_styledItemDelegate_paint(self, painter, option, index):
return super().paint(painter, option, index)
class NoCommitWhenFocusOutEventFilter(QObject):
"""
--DEPRECATED--
The default QAbstractItemDelegate implementation has a private function
`editorEventFilter()`, when editor sends focusOut/hide event, it emits the
`commitData(editor)` signal. We don't want this since we need to validate
the input, so we filter the event out and handle it by ourselves.
Reimplement `checkIsEditor(self, val) -> bool` to ensure this filter is
working. The default implementation always return `False`.
"""
def checkIsEditor(self, val) -> bool:
return False
def eventFilter(self, object: QObject, event: QEvent) -> bool:
if self.checkIsEditor(object) and event.type() in [
QEvent.Type.FocusOut,
QEvent.Type.Hide,
]:
widget = QApplication.focusWidget()
while widget:
# check if focus changed into editor's child
if self.checkIsEditor(widget):
return False
widget = widget.parentWidget()
object.hide()
object.deleteLater()
return True
return False

View File

@ -1,7 +1,8 @@
from arcaea_offline.models import Chart
from arcaea_offline.models import Chart, Difficulty, Song
from arcaea_offline.utils.rating import rating_class_to_short_text, rating_class_to_text
from PySide6.QtCore import QModelIndex, Qt, Signal
from PySide6.QtGui import QColor
from PIL import Image
from PySide6.QtCore import QModelIndex, QRect, Qt, Signal
from PySide6.QtGui import QColor, QPainter, QPixmap
from PySide6.QtWidgets import (
QFrame,
QHBoxLayout,
@ -13,6 +14,7 @@ from PySide6.QtWidgets import (
QWidget,
)
from ui.extends.shared.data import Data
from ui.implements.components.chartSelector import ChartSelector
from ..utils import keepWidgetInScreen
@ -80,57 +82,159 @@ class ChartDelegate(TextSegmentDelegate):
QColor("#809955"),
QColor("#702d60"),
QColor("#710f25"),
QColor("#8b77a4"),
]
ChartInvalidBackgroundColor = QColor("#e6a23c")
def getChart(self, index: QModelIndex) -> Chart | None:
return None
def getSong(self, index: QModelIndex) -> Song | None:
return None
def getDifficulty(self, index: QModelIndex) -> Difficulty | None:
return None
def getTextSegments(self, index: QModelIndex, option):
chart = self.getChart(index)
if not isinstance(chart, Chart):
song = self.getSong(index)
difficulty = self.getDifficulty(index)
chartValid = isinstance(chart, Chart)
songValid = isinstance(song, Song)
difficultyValid = isinstance(difficulty, Difficulty)
if not chartValid and not songValid:
return [
[{self.TextRole: "Chart Invalid", self.ColorRole: QColor("#ff0000")}]
[
{
self.TextRole: "Chart/Song not set",
self.ColorRole: QColor("#ff0000"),
}
]
]
chartConstantString = (
f"{chart.constant / 10:.1f}"
if chart.constant is not None and chart.constant > 0
else "?"
# get texts
if chartValid:
title = chart.title
else:
title = (
difficulty.title if difficultyValid and difficulty.title else song.title
)
if chartValid and chart.constant is not None:
chartConstantString = f"{chart.constant / 10:.1f}"
elif difficultyValid:
chartConstantString = str(difficulty.rating)
if difficulty.rating_plus:
chartConstantString += "+"
else:
chartConstantString = "?"
if chartValid:
ratingClass = chart.rating_class
elif difficultyValid:
ratingClass = difficulty.rating_class
else:
ratingClass = None
ratingText = (
f"{rating_class_to_text(ratingClass)} {chartConstantString}"
if ratingClass is not None
else "Unknown ?"
)
if chartValid:
descText = f"({chart.song_id}, {chart.set})"
else:
descText = f"({song.id}, {song.set})"
# get attributes
ratingClassColor = (
self.RatingClassColors[ratingClass] if ratingClass is not None else None
)
return [
[
{self.TextRole: f"{chart.title}"},
{self.TextRole: str(title)},
],
[
{
self.TextRole: f"{rating_class_to_text(chart.rating_class)} {chartConstantString}",
self.ColorRole: self.RatingClassColors[chart.rating_class],
self.TextRole: ratingText,
self.ColorRole: ratingClassColor,
},
],
[
{
self.TextRole: f"({chart.song_id}, {chart.set})",
self.TextRole: descText,
self.ColorRole: option.widget.palette().placeholderText().color(),
},
],
]
def paintWarningBackground(self, index: QModelIndex) -> bool:
return True
def sizeHint(self, option, index):
size = super().sizeHint(option, index)
minWidth = size.height() + 2 * self.HorizontalPadding # jacket size
width = size.width() + self.HorizontalPadding + size.height()
size.setWidth(max(minWidth, width))
return size
def paint(self, painter, option, index):
# draw chartInvalid warning background
chart = self.getChart(index)
if not isinstance(chart, Chart) and self.paintWarningBackground(index):
painter.save()
painter.setPen(Qt.PenStyle.NoPen)
bgColor = QColor(self.ChartInvalidBackgroundColor)
bgColor.setAlpha(50)
painter.setBrush(bgColor)
painter.drawRect(option.rect)
painter.restore()
option.text = ""
data = Data()
chart = self.getChart(index)
song = self.getSong(index)
difficulty = self.getDifficulty(index)
if isinstance(chart, Chart):
jacketPath = data.getJacketPath(chart)
elif isinstance(song, Song):
jacketPath = data.getJacketPath(song, difficulty)
else:
jacketPath = "__TEXT_ONLY__"
if jacketPath == "__TEXT_ONLY__":
self.setBaseXOffset(index, 0)
super().paint(painter, option, index)
return
textsSizeHint = super().textsSizeHint(option, index)
jacketSize = textsSizeHint.height()
self.setBaseXOffset(index, self.HorizontalPadding + jacketSize)
jacketSizeTuple = (jacketSize, jacketSize)
if jacketPath:
pixmap = (
Image.open(str(jacketPath.resolve()))
.resize(jacketSizeTuple, Image.BICUBIC)
.toqpixmap()
)
else:
pixmap = (
Image.fromqpixmap(QPixmap(":/images/jacket-placeholder.png"))
.resize(jacketSizeTuple, Image.BICUBIC)
.toqpixmap()
)
pixmapAvailableWidth = option.rect.width() - self.HorizontalPadding
pixmapAvailableHeight = option.rect.height()
if pixmapAvailableWidth < jacketSize or pixmapAvailableHeight < jacketSize:
cropRect = QRect(0, 0, pixmapAvailableWidth, pixmapAvailableHeight)
pixmap = pixmap.copy(cropRect)
painter.save()
painter.setRenderHint(QPainter.RenderHint.LosslessImageRendering, True)
painter.setRenderHint(QPainter.RenderHint.Antialiasing, True)
pixmapBaseY = self.baseY(option, index)
painter.drawPixmap(
option.rect.x() + self.HorizontalPadding,
pixmapBaseY,
pixmap,
)
painter.restore()
super().paint(painter, option, index)
def checkIsEditor(self, val):

View File

@ -1,19 +1,15 @@
from typing import Union
from arcaea_offline.calculate import calculate_score_range
from arcaea_offline.models import Chart, Score
from arcaea_offline.models import Chart, Score, ScoreBest
from arcaea_offline.utils.rating import rating_class_to_text
from arcaea_offline.utils.score import score_to_grade_text, zip_score_grade
from arcaea_offline.utils.score import (
clear_type_to_text,
modifier_to_text,
score_to_grade_text,
zip_score_grade,
)
from PySide6.QtCore import QAbstractItemModel, QDateTime, QModelIndex, Qt, Signal
from PySide6.QtGui import QColor, QFont, QLinearGradient
from PySide6.QtWidgets import (
QFrame,
QHBoxLayout,
QLabel,
QPushButton,
QSizePolicy,
QWidget,
)
from PySide6.QtWidgets import QHBoxLayout, QLabel, QPushButton, QSizePolicy, QWidget
from ui.implements.components.scoreEditor import ScoreEditor
@ -27,12 +23,6 @@ class ScoreEditorDelegateWrapper(ScoreEditor):
def __init__(self, parent=None):
super().__init__(parent)
# self.hLine = QFrame(self)
# self.hLine.setFrameShape(QFrame.Shape.HLine)
# self.hLine.setFrameShadow(QFrame.Shadow.Plain)
# self.hLine.setFixedHeight(5)
# self.gridLayout.addWidget(self.hLine, self.gridLayout.rowCount(), 0, -1, -1)
self.delegateHeader = QWidget(self)
self.delegateHeaderHBoxLayout = QHBoxLayout(self.delegateHeader)
self.delegateHeaderHBoxLayout.setContentsMargins(0, 0, 0, 0)
@ -55,7 +45,9 @@ class ScoreEditorDelegateWrapper(ScoreEditor):
text = "Editing "
text += _extra or ""
text += f"score {score.score}"
text += f"<br>(P{score.pure} F{score.far} L{score.lost} | MR{score.max_recall})"
text += (
f"<br>(P{score.pure} F{score.far} L{score.lost} | MR {score.max_recall})"
)
self.editorLabel.setText(text)
@ -86,20 +78,24 @@ class ScoreDelegate(TextSegmentDelegate):
createGradeGradientWrapper(QColor("#5d1d35"), QColor("#9f3c55")),
]
def getScore(self, index: QModelIndex) -> Score | None:
def getScore(self, index: QModelIndex) -> Score | ScoreBest | None:
return None
def getChart(self, index: QModelIndex) -> Chart | None:
return None
def isScoreInstance(self, index: QModelIndex) -> bool:
return isinstance(self.getScore(index), (Score, ScoreBest))
def getScoreValidateOk(self, index: QModelIndex) -> bool | None:
score = self.getScore(index)
chart = self.getChart(index)
if (
isinstance(score, Score)
self.isScoreInstance(index)
and isinstance(chart, Chart)
and chart.notes is not None
and chart.notes != 0
and score.pure is not None
and score.far is not None
):
@ -111,12 +107,12 @@ class ScoreDelegate(TextSegmentDelegate):
def getTextSegments(self, index, option):
score = self.getScore(index)
chart = self.getChart(index)
if not (isinstance(score, Score) and isinstance(chart, Chart)):
if not self.isScoreInstance(index):
return [
[
{
self.TextRole: "Chart/Score Invalid",
self.TextRole: "Score Invalid",
self.ColorRole: QColor("#ff0000"),
}
]
@ -128,7 +124,9 @@ class ScoreDelegate(TextSegmentDelegate):
score_font.setPointSize(12)
score_grade_font = QFont(score_font)
score_grade_font.setBold(True)
return [
placeholderColor = option.widget.palette().placeholderText().color()
segments = [
[
{
self.TextRole: score_to_grade_text(score.score),
@ -156,19 +154,47 @@ class ScoreDelegate(TextSegmentDelegate):
self.ColorRole: self.PureFarLostColors[2],
},
{self.TextRole: " | "},
{self.TextRole: f"MAX RECALL {score.max_recall}"},
],
[
{
self.TextRole: QDateTime.fromSecsSinceEpoch(score.date).toString(
"yyyy-MM-dd hh:mm:ss"
)
if score.date
else "-- No Date --"
}
{self.TextRole: f"MR {score.max_recall}"},
],
]
if score.date is not None:
segments.append(
[
{
self.TextRole: QDateTime.fromSecsSinceEpoch(
score.date
).toString("yyyy-MM-dd hh:mm:ss")
}
],
)
else:
segments.append(
[{self.TextRole: "-- No Date --", self.ColorRole: placeholderColor}],
)
modifierClearTypeSegments = []
if score.modifier is not None:
modifierClearTypeSegments.append(
{self.TextRole: modifier_to_text(score.modifier)}
)
else:
modifierClearTypeSegments.append(
{self.TextRole: "Modifier None", self.ColorRole: placeholderColor}
)
modifierClearTypeSegments.append({self.TextRole: ", "})
if score.clear_type is not None:
modifierClearTypeSegments.append(
{self.TextRole: clear_type_to_text(score.clear_type)}
)
else:
modifierClearTypeSegments.append(
{self.TextRole: "Clear Type None", self.ColorRole: placeholderColor}
)
segments.append(modifierClearTypeSegments)
return segments
def paintWarningBackground(self, index: QModelIndex) -> bool:
return True
@ -177,7 +203,7 @@ class ScoreDelegate(TextSegmentDelegate):
score = self.getScore(index)
chart = self.getChart(index)
if (
isinstance(score, Score)
self.isScoreInstance(index)
and isinstance(chart, Chart)
and self.paintWarningBackground(index)
):
@ -217,7 +243,7 @@ class ScoreDelegate(TextSegmentDelegate):
else:
editor.setWindowTitle("-")
if isinstance(score, Score):
if self.isScoreInstance(index):
editor.setText(score)
editor.setValidateBeforeAccept(False)
@ -238,7 +264,7 @@ class ScoreDelegate(TextSegmentDelegate):
chart = self.getChart(index)
if isinstance(chart, Chart):
editor.setChart(chart)
if isinstance(score, Score):
if self.isScoreInstance(index):
editor.setValue(score)
def confirmSetModelData(self, editor: ScoreEditorDelegateWrapper):

View File

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

View File

@ -1,15 +1,21 @@
from arcaea_offline.calculate import calculate_play_rating
from arcaea_offline.models import Chart, Score
import logging
from arcaea_offline.models import Chart, Difficulty, Score, ScoreCalculated, Song
from PySide6.QtCore import QCoreApplication, QModelIndex, QSortFilterProxyModel, Qt
from sqlalchemy import select
from .base import DbTableModel
logger = logging.getLogger(__name__)
class DbScoreTableModel(DbTableModel):
IdRole = Qt.ItemDataRole.UserRole + 10
ChartRole = Qt.ItemDataRole.UserRole + 11
ScoreRole = Qt.ItemDataRole.UserRole + 12
PttRole = Qt.ItemDataRole.UserRole + 13
SongRole = Qt.ItemDataRole.UserRole + 14
DifficultyRole = Qt.ItemDataRole.UserRole + 15
def __init__(self, parent=None):
super().__init__(parent)
@ -27,81 +33,73 @@ class DbScoreTableModel(DbTableModel):
]
def syncDb(self):
newScores = self._db.get_scores()
newScores = sorted(newScores, key=lambda x: x.id)
newCharts = []
for score in newScores:
dbChart = self._db.get_chart(score.song_id, score.rating_class)
newCharts.append(
dbChart
if isinstance(dbChart, Chart)
else Chart(
song_id=score.song_id,
rating_class=score.rating_class,
title=score.song_id,
set="unknown",
self.beginResetModel()
self.beginRemoveRows(QModelIndex(), 0, self.rowCount())
self.__items.clear()
self.endRemoveRows()
self.endResetModel()
with self._db.sessionmaker() as session:
stmt = (
select(Score, Chart, Song, Difficulty, ScoreCalculated.potential)
.join(
ScoreCalculated,
(Score.id == ScoreCalculated.id),
isouter=True,
)
.join(
Chart,
(Score.song_id == Chart.song_id)
& (Score.rating_class == Chart.rating_class),
isouter=True,
)
.join(
Song,
(Score.song_id == Song.id),
isouter=True,
)
.join(
Difficulty,
(Score.song_id == Difficulty.song_id)
& (Score.rating_class == Difficulty.rating_class),
isouter=True,
)
)
newPtts = []
for chart, score in zip(newCharts, newScores):
if (
isinstance(chart, Chart)
and chart.constant is not None
and isinstance(score, Score)
):
newPtts.append(calculate_play_rating(chart.constant, score.score))
else:
newPtts.append(None)
results = session.execute(stmt).all()
newScoreIds = [score.id for score in newScores]
oldScoreIds = [item[self.ScoreRole].id for item in self.__items]
self.beginInsertRows(QModelIndex(), 0, len(results) - 1)
for result in results:
score, chart, song, difficulty, potential = result
deleteIds = list(set(oldScoreIds) - set(newScoreIds))
newIds = list(set(newScoreIds) - set(oldScoreIds))
deleteRowIndexes = [oldScoreIds.index(deleteId) for deleteId in deleteIds]
if chart:
chartInModel = chart
elif song and difficulty:
chartInModel = Chart(
song_id=song.id,
rating_class=difficulty.rating_class,
title=difficulty.title or song.title,
set=song.set,
)
else:
chartInModel = Chart(
song_id=score.song_id,
rating_class=score.rating_class,
title=score.song_id,
set="unknown",
)
# first delete rows
for deleteRowIndex in sorted(deleteRowIndexes, reverse=True):
self.beginRemoveRows(QModelIndex(), deleteRowIndex, deleteRowIndex)
self.__items.pop(deleteRowIndex)
self.endRemoveRows()
# now update existing datas
for oldItem, newChart, newScore, newPtt in zip(
self.__items, newCharts, newScores, newPtts
):
oldItem[self.IdRole] = newScore.id
oldItem[self.ChartRole] = newChart
oldItem[self.ScoreRole] = newScore
oldItem[self.PttRole] = newPtt
# finally insert new rows
for newId in newIds:
insertRowIndex = self.rowCount()
itemListIndex = newScoreIds.index(newId)
score = newScores[itemListIndex]
chart = newCharts[itemListIndex]
ptt = newPtts[itemListIndex]
self.beginInsertRows(QModelIndex(), insertRowIndex, insertRowIndex)
self.__items.append(
{
self.IdRole: score.id,
self.ChartRole: chart,
self.ScoreRole: score,
self.PttRole: ptt,
}
)
self.__items.append(
{
self.IdRole: score.id,
self.ScoreRole: score,
self.ChartRole: chartInModel,
self.SongRole: song,
self.DifficultyRole: difficulty,
self.PttRole: potential,
}
)
self.endInsertRows()
# trigger view update
topLeft = self.index(0, 0)
bottomRight = self.index(self.rowCount() - 1, self.columnCount() - 1)
self.dataChanged.emit(
topLeft,
bottomRight,
[Qt.ItemDataRole.DisplayRole, self.IdRole, self.ChartRole, self.ScoreRole],
)
def rowCount(self, *args):
return len(self.__items)
@ -112,8 +110,12 @@ class DbScoreTableModel(DbTableModel):
self.IdRole,
]:
return self.__items[index.row()][self.IdRole]
elif index.column() == 1 and role == self.ChartRole:
return self.__items[index.row()][self.ChartRole]
elif index.column() == 1 and role in [
self.ChartRole,
self.SongRole,
self.DifficultyRole,
]:
return self.__items[index.row()][role]
elif index.column() == 2 and role in [self.ChartRole, self.ScoreRole]:
return self.__items[index.row()][role]
elif index.column() == 3:
@ -129,7 +131,7 @@ class DbScoreTableModel(DbTableModel):
return False
if index.column() == 2 and isinstance(value, Score) and role == self.ScoreRole:
self._db.update_score(self.__items[index.row()][self.IdRole], value)
self._db.update_score(value)
self.syncDb()
return True
@ -147,11 +149,12 @@ class DbScoreTableModel(DbTableModel):
return False
try:
self._db.delete_score(self.__items[row][self.IdRole])
self._db.delete_score(self.__items[row][self.ScoreRole])
if syncDb:
self.syncDb()
return True
except Exception:
logger.exception(f"Table[Score]: Cannot remove row {row}")
return False
def removeRow(self, row: int, parent=...):
@ -180,7 +183,7 @@ class DbScoreTableSortFilterProxyModel(QSortFilterProxyModel):
Sort_C2_ScoreRole = Qt.ItemDataRole.UserRole + 75
Sort_C2_TimeRole = Qt.ItemDataRole.UserRole + 76
def lessThan(self, sourceLeft, sourceRight) -> bool:
def lessThan(self, sourceLeft: QModelIndex, sourceRight: QModelIndex) -> bool:
if sourceLeft.column() != sourceRight.column():
return

View File

@ -5,12 +5,12 @@ from PySide6.QtCore import QFileInfo, QSettings, Signal
from .singleton import QObjectSingleton
__all__ = [
"LANGUAGE",
"DATABASE_URL",
"DEVICES_JSON_FILE",
"DEVICE_UUID",
"TESSERACT_FILE",
"KNN_MODEL_FILE",
"SIFT_DATABASE_FILE",
"B30_KNN_MODEL_FILE",
"PHASH_DATABASE_FILE",
"SCORE_DATE_SOURCE",
"ANDREAL_FOLDER",
"ANDREAL_EXECUTABLE",
"Settings",
@ -21,13 +21,10 @@ __all__ = [
LANGUAGE = "Language"
DATABASE_URL = "DatabaseUrl"
DEVICES_JSON_FILE = "Ocr/DevicesJsonFile"
DEVICE_UUID = "Ocr/DeviceUuid"
TESSERACT_FILE = "Ocr/TesseractFile"
KNN_MODEL_FILE = "Ocr/KnnModelFile"
B30_KNN_MODEL_FILE = "Ocr/B30KnnModelFile"
SIFT_DATABASE_FILE = "Ocr/SiftDatabaseFile"
PHASH_DATABASE_FILE = "Ocr/PHashDatabaseFile"
SCORE_DATE_SOURCE = "Ocr/DateSource"
ANDREAL_FOLDER = "Andreal/AndrealFolder"
ANDREAL_EXECUTABLE = "Andreal/AndrealExecutable"
@ -70,33 +67,6 @@ class Settings(QSettings, metaclass=QObjectSingleton):
def setDatabaseUrl(self, value: str):
self._setStrItem(DATABASE_URL, value)
def devicesJsonFile(self):
return self._strItem(DEVICES_JSON_FILE)
def setDevicesJsonFile(self, value: str):
self._setStrItem(DEVICES_JSON_FILE, value)
def resetDevicesJsonFile(self):
self._resetStrItem(DEVICES_JSON_FILE)
def deviceUuid(self):
return self._strItem(DEVICE_UUID)
def setDeviceUuid(self, value: str):
self._setStrItem(DEVICE_UUID, value)
def resetDeviceUuid(self):
self._resetStrItem(DEVICE_UUID)
def tesseractPath(self):
return self._strItem(TESSERACT_FILE)
def setTesseractPath(self, value: str):
self._setStrItem(TESSERACT_FILE, value)
def resetTesseractPath(self):
self._resetStrItem(TESSERACT_FILE)
def knnModelFile(self):
return self._strItem(KNN_MODEL_FILE)
@ -115,15 +85,6 @@ class Settings(QSettings, metaclass=QObjectSingleton):
def resetB30KnnModelFile(self):
self._resetStrItem(B30_KNN_MODEL_FILE)
def siftDatabaseFile(self):
return self._strItem(SIFT_DATABASE_FILE)
def setSiftDatabaseFile(self, value: str):
self._setStrItem(SIFT_DATABASE_FILE, value)
def resetSiftDatabaseFile(self):
self._resetStrItem(SIFT_DATABASE_FILE)
def phashDatabaseFile(self):
return self._strItem(PHASH_DATABASE_FILE)
@ -133,6 +94,15 @@ class Settings(QSettings, metaclass=QObjectSingleton):
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)

View File

@ -0,0 +1,32 @@
from arcaea_offline.models import Difficulty, Song
from PySide6.QtCore import QModelIndex, Qt
from PySide6.QtGui import QStandardItem, QStandardItemModel
from ui.extends.shared.delegates.chartDelegate import ChartDelegate
class ChartInfoAbsentModel(QStandardItemModel):
SongRole = Qt.ItemDataRole.UserRole
DifficultyRole = Qt.ItemDataRole.UserRole + 1
def setCustomData(self, songs: list[Song], difficulties: list[Difficulty]):
self.clear()
for song, difficulty in zip(songs, difficulties):
item = QStandardItem()
item.setData(song, self.SongRole)
item.setData(difficulty, self.DifficultyRole)
self.appendRow(item)
def setLoading(self):
self.clear()
self.appendRow(QStandardItem("Loading..."))
class ListViewDelegate(ChartDelegate):
def getSong(self, index: QModelIndex):
return index.data(ChartInfoAbsentModel.SongRole)
def getDifficulty(self, index: QModelIndex):
return index.data(ChartInfoAbsentModel.DifficultyRole)

View File

@ -1,69 +1,61 @@
import contextlib
import logging
from typing import Tuple
from typing import Tuple, Type
import cv2
import exif
from arcaea_offline.database import Database
from arcaea_offline.models import Chart, Score
from arcaea_offline_ocr.device.shared import DeviceOcrResult
from arcaea_offline_ocr.device.v2 import DeviceV2AutoRois, DeviceV2Ocr, DeviceV2Rois
from arcaea_offline_ocr.device.v2.sizes import SizesV1, SizesV2
from arcaea_offline.utils.partner import KanaeDayNight, kanae_day_night
from arcaea_offline_ocr.crop import CropBlackEdges
from arcaea_offline_ocr.device import DeviceOcr, DeviceOcrResult
from arcaea_offline_ocr.device.rois import (
DeviceRois,
DeviceRoisAuto,
DeviceRoisExtractor,
DeviceRoisMasker,
)
from arcaea_offline_ocr.phash_db import ImagePhashDatabase
from arcaea_offline_ocr.utils import imread_unicode
from PySide6.QtCore import QDateTime, QFileInfo
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__)
import exif
class TabDeviceV2OcrRunnable(OcrRunnable):
def __init__(self, imagePath, device, knnModel, phashDb, *, sizesV2: bool):
class TabDeviceOcrRunnable(OcrRunnable):
def __init__(
self,
imagePath: str,
rois: DeviceRois | Type[DeviceRoisAuto],
masker: DeviceRoisMasker,
knnModel: cv2.ml.KNearest,
phashDb: ImagePhashDatabase,
):
super().__init__()
self.imagePath = imagePath
self.device = device
self.rois = rois
self.masker = masker
self.knnModel = knnModel
self.phashDb = phashDb
self.sizesV2 = sizesV2
def run(self):
try:
rois = DeviceV2Rois(
self.device, imread_unicode(self.imagePath, cv2.IMREAD_COLOR)
)
rois.sizes = (
SizesV2(self.device.factor)
if self.sizesV2
else SizesV1(self.device.factor)
)
ocr = DeviceV2Ocr(self.knnModel, self.phashDb)
result = ocr.ocr(rois)
img = imread_unicode(self.imagePath, cv2.IMREAD_COLOR)
img = CropBlackEdges.crop(img, cv2.COLOR_BGR2GRAY)
if isinstance(self.rois, type) and issubclass(self.rois, DeviceRoisAuto):
rois = self.rois(img.shape[1], img.shape[0])
else:
rois = self.rois
extractor = DeviceRoisExtractor(img, rois)
ocr = DeviceOcr(extractor, self.masker, self.knnModel, self.phashDb)
result = ocr.ocr()
self.signals.resultReady.emit(result)
except Exception:
logger.exception(f"DeviceV2 ocr {self.imagePath} error")
finally:
self.signals.finished.emit()
class TabDeviceV2AutoRoisOcrRunnable(OcrRunnable):
def __init__(self, imagePath, knnModel, phashDb, *, sizesV2: bool):
super().__init__()
self.imagePath = imagePath
self.knnModel = knnModel
self.phashDb = phashDb
self.sizesV2 = sizesV2
def run(self):
try:
rois = DeviceV2AutoRois(imread_unicode(self.imagePath, cv2.IMREAD_COLOR))
factor = rois.sizes.factor
rois.sizes = SizesV2(factor) if self.sizesV2 else SizesV1(factor)
ocr = DeviceV2Ocr(self.knnModel, self.phashDb)
result = ocr.ocr(rois)
self.signals.resultReady.emit(result)
except Exception:
logger.exception(f"DeviceV2AutoRois ocr {self.imagePath} error")
logger.exception("DeviceOcr error:")
finally:
self.signals.finished.emit()
@ -76,14 +68,37 @@ def getImageDate(imagePath: str) -> QDateTime:
if exifImage.has_exif and exifImage.get("datetime_original"):
datetimeStr = exifImage.get("datetime_original")
datetime = QDateTime.fromString(datetimeStr, "yyyy:MM:dd hh:mm:ss")
if not isinstance(datetime, QDateTime):
datetime = QFileInfo(imagePath).birthTime()
dateSource = Settings().scoreDateSource()
if dateSource == "lastModified":
datetime = QFileInfo(imagePath).lastModified()
else:
datetime = QFileInfo(imagePath).birthTime()
return datetime
class ScoreConverter:
@staticmethod
def deviceV2(imagePath: str, _, result: DeviceOcrResult) -> Tuple[Chart, Score]:
def device(imagePath: str, _, result: DeviceOcrResult) -> Tuple[Chart, Score]:
partnerModifiers = Data().partnerModifiers
imageDate = getImageDate(imagePath)
# calculate clear type
if result.partner_id == "50":
dayNight = kanae_day_night(imageDate)
modifier = 1 if dayNight == KanaeDayNight.Day else 2
else:
modifier = partnerModifiers.get(result.partner_id, 0)
if result.clear_status == 1 and modifier == 1:
clearType = 4
elif result.clear_status == 1 and modifier == 2:
clearType = 5
else:
clearType = result.clear_status
db = Database()
score = Score(
song_id=result.song_id,
@ -92,16 +107,16 @@ class ScoreConverter:
pure=result.pure,
far=result.far,
lost=result.lost,
date=getImageDate(imagePath).toSecsSinceEpoch(),
date=imageDate.toSecsSinceEpoch(),
max_recall=result.max_recall,
modifier=modifier,
clear_type=clearType,
comment=f"OCR {QFileInfo(imagePath).fileName()}",
)
chart = db.get_chart(score.song_id, score.rating_class)
if not chart:
chart = Chart(
song_id=result.song_id,
rating_class=result.rating_class,
title=result.song_id,
constant=0.0,
)
chart = db.get_chart(score.song_id, score.rating_class) or Chart(
song_id=result.song_id,
rating_class=result.rating_class,
title=result.song_id,
constant=0.0,
)
return (chart, score)

View File

@ -2,6 +2,7 @@ import base64
import logging
import os
import re
import subprocess
from PySide6.QtCore import QObject, QProcess, QRunnable, QThreadPool, Signal
@ -24,7 +25,15 @@ class AndrealExecuteRunnable(QRunnable):
def run(self):
try:
result = os.popen(f"{self.executePath} {' '.join(self.arguments)}").read()
subp = subprocess.run(
[self.executePath, *self.arguments],
capture_output=True,
encoding="utf-8",
)
result = subp.stdout
if subp.returncode != 0:
logger.error("AndrealImageGenerator Error: ")
logger.error(result)
b64Result = [s for s in result.split("\n") if s]
imageBytes = base64.b64decode(
re.sub(r"data:image/.*;base64,", "", b64Result[-1])

View File

@ -0,0 +1,120 @@
import re
from typing import Any
from arcaea_offline.database import Database
from arcaea_offline.models import Chart, ScoreBest
from arcaea_offline.utils.rating import rating_class_to_text
from PySide6.QtCore import QAbstractListModel, QModelIndex, Qt
from PySide6.QtGui import QStandardItem, QStandardItemModel
from ui.extends.shared.delegates.chartDelegate import ChartDelegate
from ui.extends.shared.delegates.scoreDelegate import ScoreDelegate
class ChartsModel(QAbstractListModel):
ChartRole = Qt.ItemDataRole.UserRole
def __init__(self, parent=None):
super().__init__(parent)
self.__data: list[dict[int, Any]] = []
def rowCount(self, *args) -> int:
return len(self.__data)
def columnCount(self, *args) -> int:
return 1
def headerData(self, *args):
return None
def data(self, index: QModelIndex, role: int):
if not self.checkIndex(index):
return None
return self.__data[index.row()].get(role, None)
def clear(self):
self.beginResetModel()
self.beginRemoveRows(QModelIndex(), 0, self.rowCount())
self.__data.clear()
self.endRemoveRows()
self.endResetModel()
def setCharts(self, charts: list[Chart]):
self.clear()
db = Database()
self.beginInsertRows(QModelIndex(), 0, len(charts))
for chart in charts:
pack = db.get_pack(chart.set)
if re.search(r"_append_.*$", pack.id):
basePackId = re.sub(r"_append_.*$", "", pack.id)
basePackName = db.get_pack(basePackId).name
packName = f"{basePackName} - {pack.name}"
else:
packName = pack.name
tooltip = (
f"{chart.title}@{packName} [{rating_class_to_text(chart.rating_class)}]"
)
self.__data.append(
{
Qt.ItemDataRole.ToolTipRole: tooltip,
self.ChartRole: chart,
}
)
self.endInsertRows()
class CustomChartDelegate(ChartDelegate):
def getChart(self, index: QModelIndex) -> Chart | None:
return index.data(ChartsModel.ChartRole)
class ChartsWithScoreBestModel(QStandardItemModel):
ChartRole = Qt.ItemDataRole.UserRole
ScoreBestRole = Qt.ItemDataRole.UserRole + 10
def columnCount(self, *args) -> int:
return 3
def headerData(self, *args):
return None
def setChartAndScore(self, charts: list[Chart], scoreBests: list[ScoreBest]):
self.clear()
db = Database()
self.beginInsertRows(QModelIndex(), 0, len(charts))
for chart, scoreBest in zip(charts, scoreBests):
pack = db.get_pack(chart.set)
if re.search(r"_append_.*$", pack.id):
basePackId = re.sub(r"_append_.*$", "", pack.id)
basePackName = db.get_pack(basePackId).name
packName = f"{basePackName} - {pack.name}"
else:
packName = pack.name
tooltip = (
f"{chart.title}@{packName} [{rating_class_to_text(chart.rating_class)}]\n"
f"{scoreBest.score} > {scoreBest.potential}"
)
chartItem = QStandardItem()
chartItem.setData(tooltip, Qt.ItemDataRole.ToolTipRole)
chartItem.setData(chart, self.ChartRole)
scoreBestItem = QStandardItem()
scoreBestItem.setData(tooltip, Qt.ItemDataRole.ToolTipRole)
scoreBestItem.setData(scoreBest, self.ScoreBestRole)
potentialTextItem = QStandardItem()
potentialTextItem.setText(f"{scoreBest.potential}")
self.appendRow([chartItem, scoreBestItem, potentialTextItem])
class CustomScoreBestDelegate(ScoreDelegate):
def getScore(self, index: QModelIndex):
return index.data(ChartsWithScoreBestModel.ScoreBestRole)

View File

@ -0,0 +1,23 @@
from PySide6.QtGui import QFont
from .focusSelectAllLineEdit import FocusSelectAllLineEdit
class ArcaeaScoreLineEdit(FocusSelectAllLineEdit):
def __init__(self, parent=None):
super().__init__(parent)
font = QFont("GeosansLight")
font.setPointSize(14)
font.setBold(True)
font.setStyleStrategy(
QFont.StyleStrategy.NoSubpixelAntialias
| QFont.StyleStrategy.PreferAntialias
)
self.setFont(font)
self.setInputMask("B9'999'999;_")
def score(self) -> int | None:
textWithoutMask = self.text().replace("'", "")
return int(textWithoutMask) if textWithoutMask else None

View File

@ -27,15 +27,19 @@ class ChartSelector(Ui_ChartSelector, QWidget):
self.valueChanged.connect(self.updateResultLabel)
self.songIdSelector.valueChanged.connect(self.updateRatingClassEnabled)
self.songIdSelector.quickSearchActivated.connect(
self.__songIdSelectedQuickSearchActivated
)
self.songIdSelector.valueChanged.connect(self.valueChanged)
self.ratingClassSelector.valueChanged.connect(self.valueChanged)
# handle `songIdSelector.updateDatabase` by this component
databaseUpdateSignals.songDataUpdated.disconnect(
databaseUpdateSignals.songAddOrDelete.disconnect(
self.songIdSelector.updateDatabase
)
databaseUpdateSignals.songDataUpdated.connect(self.updateDatabase)
databaseUpdateSignals.songAddOrDelete.connect(self.updateDatabase)
databaseUpdateSignals.chartInfoUpdated.connect(self.updateResultLabel)
def setSongIdSelectorMode(self, mode: SongIdSelectorMode):
self.songIdSelector.setMode(mode)
@ -85,15 +89,17 @@ class ChartSelector(Ui_ChartSelector, QWidget):
texts = [" | ".join(t) for t in texts]
text = f'{texts[0]}<br><font color="gray">{texts[1]}</font>'
else:
text = f'No chart data<br><font color="gray">{chart.set} | {chart.song_id} | {chart.rating_class}</font>'
text = (
"No chart data<br>"
f'<font color="gray">{chart.set} | {chart.song_id} | {chart.rating_class}</font>'
)
self.resultLabel.setText(text)
else:
self.resultLabel.setText("...")
def updateRatingClassEnabled(self):
ratingClasses = []
songId = self.songIdSelector.songId()
if songId:
if songId := self.songIdSelector.songId():
if self.songIdSelector.mode == SongIdSelectorMode.Chart:
items = self.db.get_charts_by_song_id(songId)
else:
@ -106,9 +112,8 @@ class ChartSelector(Ui_ChartSelector, QWidget):
self.songIdSelector.reset()
def selectChart(self, chart: Chart):
if not self.songIdSelector.selectPack(chart.set):
return False
if not self.songIdSelector.selectSongId(chart.song_id):
return False
self.songIdSelector.selectChart(chart)
self.ratingClassSelector.select(chart.rating_class)
def __songIdSelectedQuickSearchActivated(self, chart: Chart):
self.ratingClassSelector.select(chart.rating_class)
return True

View File

@ -1,32 +0,0 @@
from arcaea_offline_ocr.device.v1.definition import DeviceV1
from PySide6.QtCore import Qt
from PySide6.QtWidgets import QComboBox
from ui.extends.ocr import load_devices_json
from ui.extends.shared.delegates.descriptionDelegate import DescriptionDelegate
class DevicesComboBox(QComboBox):
DeviceUuidRole = Qt.ItemDataRole.UserRole + 10
def __init__(self, parent=None):
super().__init__(parent)
self.setItemDelegate(DescriptionDelegate(self))
def setDevices(self, devices: list[DeviceV1]):
self.clear()
for device in devices:
self.addItem(f"{device.name} ({device.uuid})", device)
row = self.count() - 1
self.setItemData(row, device.uuid, self.DeviceUuidRole)
self.setItemData(row, device.name, DescriptionDelegate.MainTextRole)
self.setItemData(row, device.uuid, DescriptionDelegate.DescriptionTextRole)
self.setCurrentIndex(-1)
def loadDevicesJson(self, path: str):
devices = load_devices_json(path)
self.setDevices(devices)
def selectDevice(self, deviceUuid: str):
index = self.findData(deviceUuid, self.DeviceUuidRole)
self.setCurrentIndex(index)

View File

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

View File

@ -0,0 +1,48 @@
from PySide6.QtCore import Signal
from PySide6.QtWidgets import QButtonGroup, QDialog
from ui.designer.components.ocrQueueOptionsDialog_ui import Ui_OcrQueueOptionsDialog
from ui.extends.shared.settings import Settings
class OcrQueueOptionsDialog(QDialog, Ui_OcrQueueOptionsDialog):
iccOptionsChanged = Signal(int)
def __init__(self, parent=None):
super(OcrQueueOptionsDialog, self).__init__(parent)
self.setupUi(self)
self.iccOptionButtonGroup = QButtonGroup(self)
self.iccOptionButtonGroup.buttonToggled.connect(
lambda: self.iccOptionsChanged.emit(self.iccOptionButtonGroup.checkedId())
)
self.iccOptionButtonGroup.addButton(self.iccUseQtRadioButton, 0)
self.iccOptionButtonGroup.addButton(self.iccUsePILRadioButton, 1)
self.iccOptionButtonGroup.addButton(self.iccTryFixRadioButton, 2)
self.scoreDateSourceButtonGroup = QButtonGroup(self)
self.scoreDateSourceButtonGroup.addButton(
self.dateUseCreationDateRadioButton, 0
)
self.scoreDateSourceButtonGroup.addButton(self.dateUseModifyDateRadioButton, 1)
self.scoreDateSourceButtonGroup.buttonClicked.connect(
self.on_scoreDateSourceButtonGroup_buttonClicked
)
self.settings = Settings()
self.settings.updated.connect(self.syncCheckboxesFromSettings)
self.syncCheckboxesFromSettings()
def syncCheckboxesFromSettings(self):
scoreDateSource = self.settings.scoreDateSource()
if scoreDateSource == "lastModified":
self.dateUseModifyDateRadioButton.setChecked(True)
else:
self.dateUseCreationDateRadioButton.setChecked(True)
def on_scoreDateSourceButtonGroup_buttonClicked(self, button):
buttonId = self.scoreDateSourceButtonGroup.id(button)
if buttonId == 1:
self.settings.setScoreDateSource("lastModified")
else:
self.settings.setScoreDateSource("birthTime")

View File

@ -0,0 +1,85 @@
from arcaea_offline.calculate import calculate_play_rating
from PySide6.QtCore import QCoreApplication
from PySide6.QtGui import QGuiApplication
from PySide6.QtWidgets import (
QHBoxLayout,
QLabel,
QPushButton,
QSizePolicy,
QSpacerItem,
QWidget,
)
from ui.extends.shared.language import LanguageChangeEventFilter
from .arcaeaScoreLineEdit import ArcaeaScoreLineEdit
class PlayRatingCalculator(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.languageChangeEventFilter = LanguageChangeEventFilter(self)
self.installEventFilter(self.languageChangeEventFilter)
self.setupUi()
self.arcaeaScoreLineEdit.textChanged.connect(self.updateResultLabel)
self.copyButton.clicked.connect(self.on_copyButton_clicked)
self.constant: int | None = None
def setConstant(self, constant: int | None):
self.constant = constant
self.updateResultLabel()
@property
def result(self):
if self.constant is None:
return None
score = self.arcaeaScoreLineEdit.score()
return None if score is None else calculate_play_rating(self.constant, score)
def updateResultLabel(self):
result = self.result
self.resultLabel.setText(str(round(result, 3)) if result is not None else "...")
self.resultLabel.setToolTip(str(result))
def on_copyButton_clicked(self):
result = self.result
if result is not None:
QGuiApplication.clipboard().setText(str(result))
def setupUi(self, *args):
self.horizontalLayout = QHBoxLayout(self)
self.arcaeaScoreLineEdit = ArcaeaScoreLineEdit(self)
self.horizontalLayout.addWidget(self.arcaeaScoreLineEdit)
self.label = QLabel(self)
self.label.setText(" > ")
self.horizontalLayout.addWidget(self.label)
self.resultLabel = QLabel(self)
self.resultLabel.setText("...")
self.resultLabel.setSizePolicy(
QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred
)
self.resultLabel.setMinimumWidth(100)
self.horizontalLayout.addWidget(self.resultLabel)
self.horizontalSpacer = QSpacerItem(
20, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred
)
self.horizontalLayout.addSpacerItem(self.horizontalSpacer)
self.copyButton = QPushButton(self)
self.horizontalLayout.addWidget(self.copyButton)
self.retranslateUi()
def retranslateUi(self, *args):
self.copyButton.setText(
QCoreApplication.translate("PotentialCalculator", "copyButton")
)

View File

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

View File

@ -302,7 +302,7 @@ class ScoreEditor(Ui_ScoreEditor, QWidget):
if score.score is None:
flags |= ScoreValidateResult.ScoreIncomplete
elif score.pure is None or score.far is None:
elif score.pure is None or score.far is None or score.lost is None:
flags |= ScoreValidateResult.ScoreIncompleteForValidate
elif self.__chart.notes is not None:
score_range = calculate_score_range(

View File

@ -1,11 +1,10 @@
import logging
import re
from enum import IntEnum
from typing import Literal
from arcaea_offline.database import Database
from arcaea_offline.models import Chart
from PySide6.QtCore import QModelIndex, Qt, Signal, Slot
from PySide6.QtCore import QModelIndex, QSignalMapper, Qt, Signal, Slot
from PySide6.QtWidgets import QCompleter, QWidget
from ui.designer.components.songIdSelector_ui import Ui_SongIdSelector
@ -24,6 +23,7 @@ class SongIdSelectorMode(IntEnum):
class SongIdSelector(Ui_SongIdSelector, QWidget):
valueChanged = Signal()
quickSearchActivated = Signal(Chart)
def __init__(self, parent=None):
super().__init__(parent)
@ -33,18 +33,22 @@ class SongIdSelector(Ui_SongIdSelector, QWidget):
self.languageChangeEventFilter = LanguageChangeEventFilter(self)
self.installEventFilter(self.languageChangeEventFilter)
self.previousPackageButton.clicked.connect(
lambda: self.quickSwitchSelection("previous", "package")
# quick switch bindings
self.quickSwitchSignalMapper = QSignalMapper(self)
self.previousPackageButton.clicked.connect(self.quickSwitchSignalMapper.map)
self.quickSwitchSignalMapper.setMapping(
self.previousPackageButton, "package||previous"
)
self.previousSongIdButton.clicked.connect(
lambda: self.quickSwitchSelection("previous", "songId")
)
self.nextSongIdButton.clicked.connect(
lambda: self.quickSwitchSelection("next", "songId")
)
self.nextPackageButton.clicked.connect(
lambda: self.quickSwitchSelection("next", "package")
self.nextPackageButton.clicked.connect(self.quickSwitchSignalMapper.map)
self.quickSwitchSignalMapper.setMapping(self.nextPackageButton, "package||next")
self.previousSongIdButton.clicked.connect(self.quickSwitchSignalMapper.map)
self.quickSwitchSignalMapper.setMapping(
self.previousSongIdButton, "songId||previous"
)
self.nextSongIdButton.clicked.connect(self.quickSwitchSignalMapper.map)
self.quickSwitchSignalMapper.setMapping(self.nextSongIdButton, "songId||next")
self.quickSwitchSignalMapper.mappedString.connect(self.quickSwitchSlot)
self.mode = SongIdSelectorMode.SongId
@ -68,17 +72,16 @@ class SongIdSelector(Ui_SongIdSelector, QWidget):
self.songIdComboBox.currentIndexChanged.connect(self.valueChanged)
self.updateDatabase()
databaseUpdateSignals.songDataUpdated.connect(self.updateDatabase)
databaseUpdateSignals.songAddOrDelete.connect(self.updateDatabase)
def setMode(self, mode: SongIdSelectorMode):
self.mode = mode
def quickSwitchSelection(
self,
direction: Literal["previous", "next"],
model: Literal["package", "songId"],
):
minIndex = 0
@Slot(str)
def quickSwitchSlot(self, action: str):
model, direction = action.split("||")
minIndex = -1
if model == "package":
maxIndex = self.packComboBox.count() - 1
currentIndex = self.packComboBox.currentIndex() + (
@ -124,8 +127,7 @@ class SongIdSelector(Ui_SongIdSelector, QWidget):
self.packComboBox.clear()
packs = self.db.get_packs()
for pack in packs:
isAppendPack = re.search(r"_append_.*$", pack.id)
if isAppendPack:
if isAppendPack := re.search(r"_append_.*$", pack.id):
basePackId = re.sub(r"_append_.*$", "", pack.id)
basePackName = self.db.get_pack(basePackId).name
packName = f"{basePackName} - {pack.name}"
@ -144,8 +146,7 @@ class SongIdSelector(Ui_SongIdSelector, QWidget):
def fillSongIdComboBox(self):
self.songIdComboBox.clear()
packId = self.packComboBox.currentData()
if packId:
if packId := self.packComboBox.currentData():
if self.mode == SongIdSelectorMode.SongId:
items = self.db.get_songs_by_pack_id(packId)
elif self.mode == SongIdSelectorMode.Chart:
@ -174,7 +175,7 @@ class SongIdSelector(Ui_SongIdSelector, QWidget):
self.songIdComboBox.setCurrentIndex(-1)
@Slot()
def on_packComboBox_activated(self):
def on_packComboBox_currentIndexChanged(self):
self.fillSongIdComboBox()
@Slot(str)
@ -206,14 +207,15 @@ class SongIdSelector(Ui_SongIdSelector, QWidget):
return False
def selectChart(self, chart: Chart):
if not self.selectPack(chart.set):
return False
return self.selectSongId(chart.song_id)
packSelected = self.selectPack(chart.set)
songIdSelected = self.selectSongId(chart.song_id)
return packSelected and songIdSelected
@Slot(QModelIndex)
def searchCompleterSetSelection(self, index: QModelIndex):
chart = index.data(Qt.ItemDataRole.UserRole + 10) # type: Chart
chart: Chart = index.data(Qt.ItemDataRole.UserRole + 10)
self.selectChart(chart)
self.quickSearchActivated.emit(chart)
self.searchLineEdit.clear()
self.searchLineEdit.clearFocus()

View File

@ -1,7 +1,6 @@
from PySide6.QtCore import QCoreApplication
from PySide6.QtWidgets import QLabel, QPushButton
from ui.implements.components.devicesComboBox import DevicesComboBox
from ui.implements.components.fileSelector import FileSelector
from ui.implements.settings.settingsBaseWidget import SettingsBaseWidget
@ -12,28 +11,6 @@ class SettingsOcr(SettingsBaseWidget):
self.setupUi(self)
if self.settings.devicesJsonFile():
self.devicesJsonValueWidget.selectFile(self.settings.devicesJsonFile())
self.devicesJsonValueWidget.filesSelected.connect(self.setDevicesJson)
self.devicesJsonResetButton.clicked.connect(self.resetDevicesJson)
self.insertItem(
"devicesJson",
self.devicesJsonLabel,
self.devicesJsonValueWidget,
self.devicesJsonResetButton,
)
if self.settings.deviceUuid():
self.deviceUuidValueWidget.selectDevice(self.settings.deviceUuid())
self.deviceUuidValueWidget.activated.connect(self.setDeviceUuid)
self.deviceUuidResetButton.clicked.connect(self.resetDeviceUuid)
self.insertItem(
"deviceUuid",
self.deviceUuidLabel,
self.deviceUuidValueWidget,
self.deviceUuidResetButton,
)
if self.settings.knnModelFile():
self.knnModelFileValueWidget.selectFile(self.settings.knnModelFile())
self.knnModelFileValueWidget.filesSelected.connect(self.setKnnModelFile)
@ -56,19 +33,6 @@ class SettingsOcr(SettingsBaseWidget):
self.b30KnnModelFileResetButton,
)
if self.settings.siftDatabaseFile():
self.siftDatabaseFileValueWidget.selectFile(
self.settings.siftDatabaseFile()
)
self.siftDatabaseFileValueWidget.filesSelected.connect(self.setSiftDatabaseFile)
self.siftDatabaseFileResetButton.clicked.connect(self.resetSiftDatabaseFile)
self.insertItem(
"siftDatabaseFile",
self.siftDatabaseFileLabel,
self.siftDatabaseFileValueWidget,
self.siftDatabaseFileResetButton,
)
if self.settings.phashDatabaseFile():
self.phashDatabaseFileValueWidget.selectFile(
self.settings.phashDatabaseFile()
@ -84,34 +48,6 @@ class SettingsOcr(SettingsBaseWidget):
self.phashDatabaseFileResetButton,
)
def setDevicesJson(self):
selectedFile = self.devicesJsonValueWidget.selectedFiles()
if selectedFile and selectedFile[0]:
file = selectedFile[0]
self.settings.setDevicesJsonFile(file)
self.fillDeviceUuidComboBox()
def fillDeviceUuidComboBox(self):
devicesJsonPath = self.devicesJsonValueWidget.selectedFiles()[0]
self.deviceUuidValueWidget.loadDevicesJson(devicesJsonPath)
storedDeviceUuid = self.settings.deviceUuid()
self.deviceUuidValueWidget.selectDevice(storedDeviceUuid)
def resetDevicesJson(self):
self.deviceUuidValueWidget.clear()
self.devicesJsonValueWidget.reset()
self.settings.resetDeviceUuid()
self.settings.resetDevicesJsonFile()
def setDeviceUuid(self):
if device := self.deviceUuidValueWidget.currentData():
self.settings.setDeviceUuid(device.uuid)
def resetDeviceUuid(self):
self.deviceUuidValueWidget.setCurrentIndex(-1)
self.settings.resetDeviceUuid()
def setKnnModelFile(self):
selectedFile = self.knnModelFileValueWidget.selectedFiles()
if selectedFile and selectedFile[0]:
@ -132,16 +68,6 @@ class SettingsOcr(SettingsBaseWidget):
self.b30KnnModelFileValueWidget.reset()
self.settings.resetB30KnnModelFile()
def setSiftDatabaseFile(self):
selectedFile = self.siftDatabaseFileValueWidget.selectedFiles()
if selectedFile and selectedFile[0]:
file = selectedFile[0]
self.settings.setSiftDatabaseFile(file)
def resetSiftDatabaseFile(self):
self.siftDatabaseFileValueWidget.reset()
self.settings.resetSiftDatabaseFile()
def setPHashDatabaseFile(self):
selectedFile = self.phashDatabaseFileValueWidget.selectedFiles()
if selectedFile and selectedFile[0]:
@ -153,14 +79,6 @@ class SettingsOcr(SettingsBaseWidget):
self.settings.resetPHashDatabaseFile()
def setupUi(self, *args):
self.devicesJsonLabel = QLabel(self)
self.devicesJsonValueWidget = FileSelector(self)
self.devicesJsonResetButton = QPushButton(self)
self.deviceUuidLabel = QLabel(self)
self.deviceUuidValueWidget = DevicesComboBox(self)
self.deviceUuidResetButton = QPushButton(self)
self.knnModelFileLabel = QLabel(self)
self.knnModelFileValueWidget = FileSelector(self)
self.knnModelFileResetButton = QPushButton(self)
@ -169,10 +87,6 @@ class SettingsOcr(SettingsBaseWidget):
self.b30KnnModelFileValueWidget = FileSelector(self)
self.b30KnnModelFileResetButton = QPushButton(self)
self.siftDatabaseFileLabel = QLabel(self)
self.siftDatabaseFileValueWidget = FileSelector(self)
self.siftDatabaseFileResetButton = QPushButton(self)
self.phashDatabaseFileLabel = QLabel(self)
self.phashDatabaseFileValueWidget = FileSelector(self)
self.phashDatabaseFileResetButton = QPushButton(self)
@ -186,21 +100,12 @@ class SettingsOcr(SettingsBaseWidget):
# fmt: off
self.setTitle(QCoreApplication.translate("Settings", "ocr.title"))
self.devicesJsonLabel.setText(QCoreApplication.translate("Settings", "ocr.devicesJson.label"))
self.devicesJsonResetButton.setText(QCoreApplication.translate("Settings", "resetButton"))
self.deviceUuidLabel.setText(QCoreApplication.translate("Settings", "ocr.deviceUuid.label"))
self.deviceUuidResetButton.setText(QCoreApplication.translate("Settings", "resetButton"))
self.knnModelFileLabel.setText(QCoreApplication.translate("Settings", "ocr.knnModelFile.label"))
self.knnModelFileResetButton.setText(QCoreApplication.translate("Settings", "resetButton"))
self.b30KnnModelFileLabel.setText(QCoreApplication.translate("Settings", "ocr.b30KnnModelFile.label"))
self.b30KnnModelFileResetButton.setText(QCoreApplication.translate("Settings", "resetButton"))
self.siftDatabaseFileLabel.setText(QCoreApplication.translate("Settings", "ocr.siftDatabaseFile.label"))
self.siftDatabaseFileResetButton.setText(QCoreApplication.translate("Settings", "resetButton"))
self.phashDatabaseFileLabel.setText(QCoreApplication.translate("Settings", "ocr.phashDatabaseFile.label"))
self.phashDatabaseFileResetButton.setText(QCoreApplication.translate("Settings", "resetButton"))
# fmt: on

View File

@ -0,0 +1,226 @@
import logging
from arcaea_offline.database import Database
from arcaea_offline.models import Chart, ChartInfo, Difficulty, Song
from arcaea_offline.utils.rating import rating_class_to_text
from PySide6.QtCore import QCoreApplication, QModelIndex, Qt, Slot
from PySide6.QtGui import QPixmap, QRegularExpressionValidator, QStandardItem
from PySide6.QtWidgets import QApplication, QMessageBox, QStyledItemDelegate, QWidget
from sqlalchemy import select
from ui.designer.tabs.tabDb.tabDb_ChartInfoEditor_ui import Ui_TabDb_ChartInfoEditor
from ui.extends.shared.data import Data
from ui.extends.shared.database import databaseUpdateSignals
from ui.extends.shared.language import LanguageChangeEventFilter
from ui.extends.tabs.tabDb.tabDb_ChartInfoEditor import (
ChartInfoAbsentModel,
ListViewDelegate,
)
from ui.implements.components.songIdSelector import SongIdSelectorMode
logger = logging.getLogger(__name__)
class TabDb_ChartInfoEditor(Ui_TabDb_ChartInfoEditor, QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setupUi(self)
self.languageChangeEventFilter = LanguageChangeEventFilter(self)
self.installEventFilter(self.languageChangeEventFilter)
self.db = Database()
self.numberRegexValidator = QRegularExpressionValidator(r"^\d+$", self)
self.constantLineEdit.setValidator(self.numberRegexValidator)
self.notesLineEdit.setValidator(self.numberRegexValidator)
self.constantLineEdit.textChanged.connect(self.updateConstantPreviewLabel)
self.chartInfoAbsentModel = ChartInfoAbsentModel(self)
self.listView.setModel(self.chartInfoAbsentModel)
self.listViewDelegate = ListViewDelegate(self)
self.listView.selectionModel().currentChanged.connect(
self.listViewSelectionChanged
)
self.chartSelector.setSongIdSelectorMode(SongIdSelectorMode.SongId)
self.chartSelector.valueChanged.connect(self.chartSelectorValueChanged)
databaseUpdateSignals.chartInfoUpdated.connect(self.updateChartInfoAbsentModel)
self.updateChartInfoAbsentModel()
self.commitButton.clicked.connect(self.commitChartInfo)
self.deleteButton.clicked.connect(self.deleteChartInfo)
def updateConstantPreviewLabel(self):
text = self.constantLineEdit.text()
if self.constantLineEdit.hasAcceptableInput():
self.constantPreviewLabel.setText(f"> {int(text) / 10:.1f}")
else:
self.constantPreviewLabel.setText("> ...")
def reset(self):
self.jacketLabel.clear()
self.titleLabel.setText("...")
self.ratingLabel.setText("...")
self.constantLineEdit.setText("")
self.notesLineEdit.setText("")
def updateChartInfoAbsentModel(self):
self.listView.setItemDelegate(QStyledItemDelegate())
self.chartInfoAbsentModel.clear()
self.chartInfoAbsentModel.appendRow(QStandardItem("Loading..."))
QApplication.processEvents()
with self.db.sessionmaker() as session:
stmt = (
select(Difficulty)
.join(
ChartInfo,
(Difficulty.song_id == ChartInfo.song_id)
& (Difficulty.rating_class == ChartInfo.rating_class),
isouter=True,
)
.where(ChartInfo.notes.is_(None))
)
absentInfoDifficulties = sorted(
list(session.scalars(stmt)),
key=lambda d: f"{d.song_id},{d.rating_class}",
)
songIds = sorted(list(set(d.song_id for d in absentInfoDifficulties)))
songsStmt = select(Song).where(Song.id.in_(songIds))
songs = sorted(list(session.scalars(songsStmt)), key=lambda s: s.id)
modelSongs = []
for difficulty in absentInfoDifficulties:
songIndex = songIds.index(difficulty.song_id)
modelSongs.append(songs[songIndex])
self.chartInfoAbsentModel.setCustomData(modelSongs, absentInfoDifficulties)
self.listView.setItemDelegate(self.listViewDelegate)
@Slot(QModelIndex)
def listViewSelectionChanged(self, current: QModelIndex):
if current.row() < 0 or current.column() < 0:
return
song: Song = current.data(ChartInfoAbsentModel.SongRole)
difficulty: Difficulty = current.data(ChartInfoAbsentModel.DifficultyRole)
self.chartSelector.selectChart(
Chart(
song_id=difficulty.song_id,
rating_class=difficulty.rating_class,
set=song.set,
)
)
def chartSelectorValueChanged(self):
if chart := self.chartSelector.value():
self.fillChartInfo(chart.song_id, chart.rating_class)
else:
self.reset()
def fillChartInfo(self, songId: str, ratingClass: int):
song = self.db.get_song(songId)
difficulty = self.db.get_difficulty(songId, ratingClass)
self.titleLabel.setText(difficulty.title or song.title)
self.ratingLabel.setText(
rating_class_to_text(difficulty.rating_class)
+ " "
+ str(difficulty.rating)
+ ("+" if difficulty.rating_plus else "")
)
jacketPath = Data().getJacketPath(song, difficulty)
if not jacketPath:
pixmap = QPixmap(":/images/jacket-placeholder.png")
else:
pixmap = QPixmap(str(jacketPath.resolve()))
self.jacketLabel.setPixmap(
pixmap.scaled(
self.jacketLabel.size(),
Qt.AspectRatioMode.KeepAspectRatio,
Qt.TransformationMode.SmoothTransformation,
)
)
chartInfo = self.db.get_chart_info(songId, ratingClass)
if chartInfo is not None:
if chartInfo.constant is not None:
self.constantLineEdit.setText(str(chartInfo.constant))
if chartInfo.notes is not None:
self.notesLineEdit.setText(str(chartInfo.notes))
else:
self.constantLineEdit.setText("")
self.notesLineEdit.setText("")
def commitChartInfo(self):
chart = self.chartSelector.value()
if not chart:
QMessageBox.critical(
self,
None,
# fmt: off
QCoreApplication.translate("TabDb_ChartInfoEditor", "commit.chartNotSelected"),
# fmt: on
)
return
if not self.constantLineEdit.hasAcceptableInput():
QMessageBox.critical(
self,
None,
# fmt: off
QCoreApplication.translate("TabDb_ChartInfoEditor", "commit.constantRequired"),
# fmt: on
)
return
constant = int(self.constantLineEdit.text())
notes = (
int(self.notesLineEdit.text())
if self.notesLineEdit.hasAcceptableInput()
else None
)
chartInfo = ChartInfo(
song_id=chart.song_id,
rating_class=chart.rating_class,
constant=constant,
notes=notes,
)
with self.db.sessionmaker() as session:
session.merge(chartInfo)
session.commit()
databaseUpdateSignals.chartInfoUpdated.emit()
def deleteChartInfo(self):
chart = self.chartSelector.value()
if not chart:
QMessageBox.critical(
self,
None,
# fmt: off
QCoreApplication.translate("TabDb_ChartInfoEditor", "commit.chartNotSelected"),
# fmt: on
)
return
chartInfo = self.db.get_chart_info(chart.song_id, chart.rating_class)
if chartInfo:
result = QMessageBox.warning(
self,
None,
# fmt: off
QCoreApplication.translate("TabDb_ChartInfoEditor", "deleteConfirm"),
# fmt: on
QMessageBox.StandardButton.Yes,
QMessageBox.StandardButton.No,
)
if result == QMessageBox.StandardButton.Yes:
with self.db.sessionmaker() as session:
session.delete(chartInfo)
session.commit()
databaseUpdateSignals.chartInfoUpdated.emit()

View File

@ -1,3 +1,4 @@
import csv
import json
import logging
import traceback
@ -5,6 +6,7 @@ import zipfile
from arcaea_offline.database import Database
from arcaea_offline.external.arcaea import (
ArcaeaOnlineParser,
PacklistParser,
SonglistDifficultiesParser,
SonglistParser,
@ -12,8 +14,10 @@ from arcaea_offline.external.arcaea import (
)
from arcaea_offline.external.arcaea.common import ArcaeaParser
from arcaea_offline.external.arcsong import ArcsongDbParser
from arcaea_offline.external.chart_info_db import ChartInfoDbParser
from arcaea_offline.external.smartrte import SmartRteB30CsvConverter
from arcaea_offline.models import Difficulty, Pack, Song
from PySide6.QtCore import QDir, Slot
from PySide6.QtCore import QDateTime, QDir, Slot
from PySide6.QtWidgets import QFileDialog, QMessageBox, QWidget
from ui.designer.tabs.tabDb.tabDb_Manage_ui import Ui_TabDb_Manage
@ -46,7 +50,7 @@ class TabDb_Manage(Ui_TabDb_Manage, QWidget):
with db.sessionmaker() as session:
parser.write_database(session)
session.commit()
databaseUpdateSignals.songDataUpdated.emit()
databaseUpdateSignals.chartInfoUpdated.emit()
QMessageBox.information(self, None, "OK")
except Exception as e:
logging.exception("Sync arcsong.db error")
@ -54,6 +58,29 @@ class TabDb_Manage(Ui_TabDb_Manage, QWidget):
self, "Sync Error", "\n".join(traceback.format_exception(e))
)
@Slot()
def on_syncChartInfoDbButton_clicked(self):
dbFile, filter = QFileDialog.getOpenFileName(
self, None, "", "DB File (*.db);;*"
)
if not dbFile:
return
try:
db = Database()
parser = ChartInfoDbParser(dbFile)
with db.sessionmaker() as session:
parser.write_database(session)
session.commit()
databaseUpdateSignals.chartInfoUpdated.emit()
QMessageBox.information(self, None, "OK")
except Exception as e:
logging.exception("Sync chart info database error")
QMessageBox.critical(
self, "Sync Error", "\n".join(traceback.format_exception(e))
)
def importFromArcaeaParser(
self, parser: ArcaeaParser, instance, logName, path
) -> int:
@ -62,7 +89,7 @@ class TabDb_Manage(Ui_TabDb_Manage, QWidget):
with db.sessionmaker() as session:
parser.write_database(session)
session.commit()
databaseUpdateSignals.songDataUpdated.emit()
databaseUpdateSignals.songAddOrDelete.emit()
itemNum = len([item for item in parser.parse() if isinstance(item, instance)])
logger.info(f"updated {itemNum} {logName} from {path}")
return itemNum
@ -178,16 +205,41 @@ class TabDb_Manage(Ui_TabDb_Manage, QWidget):
self, "Import Error", "\n".join(traceback.format_exception(e))
)
@Slot()
def on_importOnlineButton_clicked(self):
apiResultFile, filter = QFileDialog.getOpenFileName(
self, "Select API result JSON file"
)
if not apiResultFile:
return
try:
db = Database()
parser = ArcaeaOnlineParser(apiResultFile)
logger.info(
f"Got {len(parser.parse())} items from {apiResultFile}, writing into database..."
)
with db.sessionmaker() as session:
parser.write_database(session)
session.commit()
QMessageBox.information(self, None, "OK")
except Exception as e:
logging.exception("import Arcaea Online error")
QMessageBox.critical(
self, "Import Error", "\n".join(traceback.format_exception(e))
)
@Slot()
def on_exportScoresButton_clicked(self):
scores = Database().export_scores()
version = Database().version()
scores = Database().export_scores_def_v2()
timestamp = QDateTime.currentMSecsSinceEpoch()
content = json.dumps(scores, ensure_ascii=False)
exportLocation, _filter = QFileDialog.getSaveFileName(
self,
"Save your scores to...",
QDir.current().filePath(f"arcaea-offline-scores-v{version}.json"),
QDir.current().filePath(f"arcaea-offline-def-v2-scores-{timestamp}.json"),
"JSON (*.json);;*",
)
with open(exportLocation, "w", encoding="utf-8") as f:
@ -206,3 +258,27 @@ class TabDb_Manage(Ui_TabDb_Manage, QWidget):
)
with open(exportLocation, "w", encoding="utf-8") as f:
f.write(content)
@Slot()
def on_exportSmartRteB30Button_clicked(self):
try:
with Database().sessionmaker() as session:
converter = SmartRteB30CsvConverter(session)
csvRows = converter.rows()
exportLocation, _filter = QFileDialog.getSaveFileName(
self,
"Export CSV file",
QDir.current().filePath("smartrte_scores.csv"),
"CSV (*.csv);;*",
)
with open(exportLocation, "w", encoding="utf-8", newline="") as f:
csvWriter = csv.writer(f)
csvWriter.writerows(csvRows)
QMessageBox.information(self, None, "OK")
except Exception as e:
logging.exception("Export SmartRTE csv error:")
QMessageBox.critical(
self, "Export Error", "\n".join(traceback.format_exception(e))
)

View File

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

View File

@ -1,5 +1,4 @@
from arcaea_offline.models import Score
from PySide6.QtCore import QModelIndex, Qt, Slot
from PySide6.QtCore import Qt, Slot
from PySide6.QtGui import QColor, QPalette
from PySide6.QtWidgets import QMessageBox
@ -17,6 +16,12 @@ class TableChartDelegate(ChartDelegate):
def getChart(self, index):
return index.data(DbScoreTableModel.ChartRole)
def getSong(self, index):
return index.data(DbScoreTableModel.SongRole)
def getDifficulty(self, index):
return index.data(DbScoreTableModel.DifficultyRole)
class TableScoreDelegate(ScoreDelegate):
def getChart(self, index):
@ -49,6 +54,7 @@ class DbScoreTableViewer(DbTableViewer):
highlightColor.setAlpha(25)
tableViewPalette.setColor(QPalette.ColorRole.Highlight, highlightColor)
self.tableView.setPalette(tableViewPalette)
self.tableModel.rowsInserted.connect(self.resizeTableView)
self.tableModel.dataChanged.connect(self.resizeTableView)
self.fillSortComboBox()

View File

@ -1,16 +1,20 @@
import logging
import cv2
import numpy as np
from arcaea_offline_ocr.b30.chieri.v4.ocr import ChieriBotV4Ocr
from arcaea_offline_ocr.phash_db import ImagePHashDatabase
from arcaea_offline_ocr.sift_db import SIFTDatabase
from arcaea_offline_ocr.phash_db import ImagePhashDatabase
from arcaea_offline_ocr.utils import imread_unicode
from PIL import Image
from PySide6.QtCore import Signal, Slot
from PySide6.QtWidgets import QWidget
from PySide6.QtWidgets import QFileDialog, QMessageBox, QWidget
from ui.designer.tabs.tabOcr.tabOcr_B30_ui import Ui_TabOcr_B30
from ui.extends.components.ocrQueue import OcrQueueModel
from ui.extends.shared.cv2_utils import cv2BgrMatToQImage, qImageToCvMatBgr
from ui.extends.ocr.dependencies import (
getCv2StatModelStatusText,
getPhashDatabaseStatusText,
)
from ui.extends.shared.language import LanguageChangeEventFilter
from ui.extends.shared.settings import (
B30_KNN_MODEL_FILE,
@ -36,97 +40,119 @@ class TabOcr_B30(Ui_TabOcr_B30, QWidget):
self.b30TypeComboBox.setCurrentIndex(0)
self.b30TypeComboBox.setEnabled(False)
self.imageSelector.filesSelected.connect(self.imageSelected)
self.knnModelSelector.filesSelected.connect(self.knnModelSelected)
self.b30KnnModelSelector.filesSelected.connect(self.b30KnnModelSelected)
self.phashDatabaseSelector.filesSelected.connect(self.phashDatabaseSelected)
self.dependencies_knnModelSelector.filesSelected.connect(self.knnModelSelected)
self.dependencies_b30KnnModelSelector.filesSelected.connect(
self.b30KnnModelSelected
)
self.dependencies_phashDatabaseSelector.filesSelected.connect(
self.phashDatabaseSelected
)
self.imagePath = None # for checking only
self.img = None
self.paddleFolder = None
self.paddle = None
self.knnModel = None
self.b30KnnModel = None
# self.siftDatabase = None
self.phashDatabase = None
self.ocr = None
self.tryPrepareOcr.connect(self.prepareOcr)
logger.info("Applying settings...")
self.knnModelSelector.connectSettings(KNN_MODEL_FILE)
self.b30KnnModelSelector.connectSettings(B30_KNN_MODEL_FILE)
self.phashDatabaseSelector.connectSettings(PHASH_DATABASE_FILE)
self.dependencies_knnModelSelector.connectSettings(KNN_MODEL_FILE)
self.dependencies_b30KnnModelSelector.connectSettings(B30_KNN_MODEL_FILE)
self.dependencies_phashDatabaseSelector.connectSettings(PHASH_DATABASE_FILE)
self.ocrQueueModel = OcrQueueModel(self)
self.ocrQueue.setModel(self.ocrQueueModel)
def imageSelected(self):
if selectedFiles := self.imageSelector.selectedFiles():
imagePath = selectedFiles[0]
self.imagePath = imagePath
self.img = imread_unicode(imagePath)
self.tryPrepareOcr.emit()
# def imageSelected(self):
# if selectedFiles := self.imageSelector.selectedFiles():
# imagePath = selectedFiles[0]
# self.imagePath = imagePath
# self.img = imread_unicode(imagePath)
# self.tryPrepareOcr.emit()
def knnModelSelected(self):
if selectedFiles := self.knnModelSelector.selectedFiles():
knnModelPath = selectedFiles[0]
self.knnModel = cv2.ml.KNearest.load(knnModelPath)
self.tryPrepareOcr.emit()
try:
filePath = self.dependencies_knnModelSelector.selectedFiles()[0]
self.knnModel = cv2.ml.KNearest.load(filePath)
except Exception:
self.knnModel = None
logger.exception("Error loading knn model:")
finally:
self.dependencies_knnModelStatusLabel.setText(
getCv2StatModelStatusText(self.knnModel)
)
def b30KnnModelSelected(self):
if selectedFiles := self.b30KnnModelSelector.selectedFiles():
b30KnnModelPath = selectedFiles[0]
self.b30KnnModel = cv2.ml.KNearest.load(b30KnnModelPath)
self.tryPrepareOcr.emit()
try:
filePath = self.dependencies_b30KnnModelSelector.selectedFiles()[0]
self.b30KnnModel = cv2.ml.KNearest.load(filePath)
except Exception:
self.b30KnnModel = None
logger.exception("Error loading b30 knn model:")
finally:
self.dependencies_b30KnnModelStatusLabel.setText(
getCv2StatModelStatusText(self.b30KnnModel)
)
def phashDatabaseSelected(self):
if selectedFiles := self.phashDatabaseSelector.selectedFiles():
phashDatabasePath = selectedFiles[0]
self.phashDatabase = ImagePHashDatabase(phashDatabasePath)
self.tryPrepareOcr.emit()
try:
filePath = self.dependencies_phashDatabaseSelector.selectedFiles()[0]
self.phashDatabase = ImagePhashDatabase(filePath)
except Exception:
self.phashDatabase = None
logger.exception("Error loading phash database:")
finally:
self.dependencies_phashDatabaseStatusLabel.setText(
getPhashDatabaseStatusText(self.phashDatabase)
)
def prepareOcr(self):
def checkDependencies(self):
b30Type = self.b30TypeComboBox.currentData()
if not b30Type:
return False
elif b30Type == "chieri_v4":
return (
self.knnModel is not None
and self.b30KnnModel is not None
and self.phashDatabase is not None
)
else:
return False
@Slot()
def on_ocr_addImageButton_clicked(self):
if not self.checkDependencies():
QMessageBox.critical(self, None, "Dependencies not configured.")
return
if b30Type == "chieri_v4":
if (
not self.imagePath
or not self.knnModel
or not self.b30KnnModel
or not self.phashDatabase
):
return
imagePath, _ = QFileDialog.getOpenFileName(
self, None, "", "Image Files (*.png *.jpg *.jpeg *.bmp *.webp);;*"
)
self.ocrQueueModel.clear()
if not imagePath:
return
ocr = ChieriBotV4Ocr(self.knnModel, self.b30KnnModel, self.phashDatabase)
ocr.set_factor(self.img)
self.ocr = ocr
self.ocrQueueModel.clear()
roi = ocr.rois
for component in roi.components(self.img):
qImage = cv2BgrMatToQImage(component.copy())
self.ocrQueueModel.addItem(qImage)
img = imread_unicode(imagePath, cv2.IMREAD_COLOR)
ocr = ChieriBotV4Ocr(self.knnModel, self.b30KnnModel, self.phashDatabase)
ocr.set_factor(img)
self.ocr = ocr
roi = ocr.rois
for component in roi.components(img):
qImage = Image.fromarray(component.copy()).toqimage()
self.ocrQueueModel.addItem(qImage)
self.ocrQueue.resizeTableView()
@Slot()
def on_ocr_startButton_clicked(self):
if (
not self.imagePath
or not self.knnModel
or not self.b30KnnModel
or not self.phashDatabase
):
if not self.ocr:
return
for row in range(self.ocrQueueModel.rowCount()):
index = self.ocrQueueModel.index(row, 0)
qImage = index.data(OcrQueueModel.ImageQImageRole)
cv2Mat = qImageToCvMatBgr(qImage)
cv2Mat = np.array(Image.fromqimage(qImage))
runnable = ChieriV4OcrRunnable(self.ocr, cv2Mat)
self.ocrQueueModel.setData(index, runnable, OcrQueueModel.OcrRunnableRole)
self.ocrQueueModel.setData(

View File

@ -0,0 +1,164 @@
import logging
import re
import sqlite3
import time
from pathlib import Path
import cv2
from PySide6.QtCore import QThread, Signal, Slot
from PySide6.QtWidgets import QFileDialog, QMessageBox, QWidget
from ui.designer.tabs.tabOcr.tabOcr_BuildPHashDatabase_ui import (
Ui_TabOcr_BuildPHashDatabase,
)
from ui.extends.ocr.build_phash import build_image_phash_database, preprocess_char_icon
from ui.extends.shared.language import LanguageChangeEventFilter
logger = logging.getLogger(__name__)
class BuildDatabaseThread(QThread):
conn: sqlite3.Connection
progress = Signal(int, int)
success = Signal()
error = Signal(str)
finished = Signal()
def __init__(
self,
images: list[Path],
labels: list[str],
*,
hashSize: int | None = None,
highfreqFactor: int | None = None,
):
super().__init__()
self.images = images
self.labels = labels
self.hashSize = hashSize
self.highfreqFactor = highfreqFactor
def run(self):
try:
progressFunc = lambda i, total: self.progress.emit(i, total)
kwargsDict = {}
if self.hashSize is not None:
kwargsDict["hash_size"] = self.hashSize
if self.highfreqFactor is not None:
kwargsDict["highfreq_factor"] = self.highfreqFactor
self.conn = build_image_phash_database(
self.images, self.labels, progress_func=progressFunc, **kwargsDict
)
self.success.emit()
except Exception as e:
logger.exception("Error during pHash database build")
self.error.emit(str(e))
finally:
self.finished.emit()
class TabOcr_BuildPHashDatabase(Ui_TabOcr_BuildPHashDatabase, QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setupUi(self)
self.languageChangeEventFilter = LanguageChangeEventFilter(self)
self.installEventFilter(self.languageChangeEventFilter)
self.songDirSelector.setMode(self.songDirSelector.getExistingDirectory)
self.charIconDirSelector.setMode(self.charIconDirSelector.getExistingDirectory)
self.buildButton.clicked.connect(self.databaseBuildStart)
@Slot()
def on_optionsResetButton_clicked(self):
self.hashSizeSpinBox.setValue(16)
self.highfreqFactorSpinBox.setValue(4)
def databaseFileName(self):
return f"image-phash-{int(time.time() * 1000)}.db"
def databaseBuildStart(self):
if not self.songDirSelector.selectedFiles():
QMessageBox.critical(self, None, "Song directory not selected.")
return
if not self.charIconDirSelector.selectedFiles():
QMessageBox.critical(self, None, "Char icon directory not selected.")
return
songDir = self.songDirSelector.selectedFiles()[0]
charIconDir = self.charIconDirSelector.selectedFiles()[0]
acceptExts = [".jpg", ".png"]
songFilePaths = [
p for p in Path(songDir).glob("**/*") if p.suffix in acceptExts
]
charIconFilePaths = [
p for p in Path(charIconDir).glob("**/*") if p.suffix in acceptExts
]
self.readImageProgressBar.setMaximum(
len(songFilePaths) + len(charIconFilePaths)
)
i = 0
songMats = []
charIconMats = []
for image_path in songFilePaths:
songMats.append(cv2.imread(str(image_path.resolve()), cv2.IMREAD_GRAYSCALE))
i += 1
self.readImageProgressBar.setValue(i)
for image_path in charIconFilePaths:
mat = cv2.imread(str(image_path.resolve()), cv2.IMREAD_GRAYSCALE)
if self.preprocessCharIconCheckBox.isChecked():
mat = preprocess_char_icon(mat)
charIconMats.append(mat)
i += 1
self.readImageProgressBar.setValue(i)
songLabels = [re.sub(r"_.*$", "", p.stem) for p in songFilePaths]
charLabels = [f"partner_icon||{p.stem}" for p in charIconFilePaths]
self.databaseBuildThread = BuildDatabaseThread(
songMats + charIconMats, songLabels + charLabels
)
self.databaseBuildThread.progress.connect(self.databaseBuildProgress)
self.databaseBuildThread.success.connect(self.databaseBuildSuccess)
self.databaseBuildThread.error.connect(self.databaseBuildError)
self.buildButton.setEnabled(False)
self.databaseBuildThread.start()
@Slot(int, int)
def databaseBuildProgress(self, i: int, total: int):
if i < 5:
self.calculateHashProgressBar.setMaximum(total)
self.calculateHashProgressBar.setValue(i)
@Slot(str)
def databaseBuildError(self, msg: str):
QMessageBox.critical(self, "Error", msg)
self.databaseBuildCleanUp()
@Slot()
def databaseBuildSuccess(self):
dbMemory = self.databaseBuildThread.conn
dbFileName, _ = QFileDialog.getSaveFileName(self, None, self.databaseFileName())
if not dbFileName:
self.databaseBuildCleanUp()
QMessageBox.information(self, None, "User canceled operation.")
return
dbDisk = sqlite3.connect(dbFileName)
dbMemory.backup(dbDisk)
self.databaseBuildCleanUp()
def databaseBuildCleanUp(self):
self.databaseBuildThread.deleteLater()
self.databaseBuildThread = None
self.readImageProgressBar.setMaximum(0)
self.readImageProgressBar.setValue(0)
self.calculateHashProgressBar.setMaximum(0)
self.calculateHashProgressBar.setValue(0)
self.buildButton.setEnabled(True)

View File

@ -1,30 +1,21 @@
import logging
import cv2
# from arcaea_offline_ocr_device_creation_wizard.implements.wizard import Wizard
from arcaea_offline_ocr.device.v1.definition import DeviceV1
from arcaea_offline_ocr.device.v2.definition import DeviceV2
from arcaea_offline_ocr.phash_db import ImagePHashDatabase
from arcaea_offline_ocr.sift_db import SIFTDatabase
from PySide6.QtCore import Qt, Slot
from PySide6.QtWidgets import QApplication, QFileDialog, QWidget
from arcaea_offline_ocr.device.rois import (
DeviceRoisAutoT1,
DeviceRoisAutoT2,
DeviceRoisMaskerAutoT1,
DeviceRoisMaskerAutoT2,
)
from arcaea_offline_ocr.phash_db import ImagePhashDatabase
from PySide6.QtCore import Slot
from PySide6.QtWidgets import QApplication, QFileDialog, QMessageBox, QWidget
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 (
DEVICES_JSON_FILE,
KNN_MODEL_FILE,
PHASH_DATABASE_FILE,
TESSERACT_FILE,
Settings,
)
from ui.extends.tabs.tabOcr.tabOcr_Device import (
ScoreConverter,
TabDeviceV2AutoRoisOcrRunnable,
TabDeviceV2OcrRunnable,
)
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__)
@ -38,17 +29,36 @@ class TabOcr_Device(Ui_TabOcr_Device, QWidget):
self.languageChangeEventFilter = LanguageChangeEventFilter(self)
self.installEventFilter(self.languageChangeEventFilter)
self.deviceFileSelector.filesSelected.connect(self.deviceFileSelected)
self.knnModelSelector.filesSelected.connect(self.knnModelFileSelected)
self.phashDatabaseSelector.filesSelected.connect(self.phashDatabaseFileSelected)
# connect options checkBoxes & comboBoxes
self.options_roisUseCustomCheckBox.toggled.connect(
lambda useCustom: self.options_roisStackedWidget.setCurrentIndex(
1 if useCustom else 0
)
)
self.options_maskerUseCustomCheckBox.toggled.connect(
lambda useCustom: self.options_maskerStackedWidget.setCurrentIndex(
1 if useCustom else 0
)
)
self.options_usePresetCheckBox.toggled.connect(self.options_setUsePreset)
self.options_presetComboBox.currentIndexChanged.connect(
self.options_presetSelected
)
# fill option values
self.options_fillComboBoxes()
self.dependencies_knnModelSelector.filesSelected.connect(self.knnModelSelected)
self.dependencies_phashDatabaseSelector.filesSelected.connect(
self.phashDatabaseSelected
)
logger.info("Applying settings...")
self.deviceFileSelector.connectSettings(DEVICES_JSON_FILE)
self.knnModelSelector.connectSettings(KNN_MODEL_FILE)
self.tesseractFileSelector.connectSettings(TESSERACT_FILE)
self.phashDatabaseSelector.connectSettings(PHASH_DATABASE_FILE)
settings = Settings()
self.deviceComboBox.selectDevice(settings.deviceUuid())
self.dependencies_knnModelSelector.connectSettings(KNN_MODEL_FILE)
self.dependencies_phashDatabaseSelector.connectSettings(PHASH_DATABASE_FILE)
self.options_usePresetCheckBox.setChecked(True)
self.options_usePresetCheckBox.setEnabled(False)
self.ocrQueueModel = OcrQueueModel(self)
self.ocrQueue.setModel(self.ocrQueueModel)
@ -60,43 +70,76 @@ class TabOcr_Device(Ui_TabOcr_Device, QWidget):
# wizard.open()
pass
@Slot()
def on_deviceUseAutoFactorCheckBox_stateChanged(self):
checkState = self.deviceUseAutoFactorCheckBox.checkState()
if checkState == Qt.CheckState.Checked:
self.deviceDependenciesStackedWidget.setCurrentIndex(1)
self.deviceComboBox.setCurrentIndex(-1)
self.deviceFileSelector.setEnabled(False)
self.deviceComboBox.setEnabled(False)
else:
self.deviceFileSelector.setEnabled(True)
self.deviceComboBox.setEnabled(True)
@Slot(bool)
def options_setUsePreset(self, usePreset: bool):
self.options_roisUseCustomCheckBox.setChecked(not usePreset)
self.options_maskerUseCustomCheckBox.setChecked(not usePreset)
self.options_preciseControlWidget.setEnabled(not usePreset)
if not usePreset:
self.options_presetComboBox.setCurrentIndex(-1)
@Slot()
def on_deviceComboBox_currentIndexChanged(self):
self.changeDeviceDepStackedWidget()
@Slot(int)
def options_presetSelected(self, index: int):
if index < 0:
self.options_roisComboBox.setCurrentIndex(-1)
self.options_maskerComboBox.setCurrentIndex(-1)
def changeDeviceDepStackedWidget(self):
device = self.deviceComboBox.currentData()
if isinstance(device, (DeviceV1, DeviceV2)):
self.deviceDependenciesStackedWidget.setCurrentIndex(device.version - 1)
autoTypeString = self.options_presetComboBox.currentData()
roisAutoTypeIndex = self.options_roisComboBox.findData(autoTypeString)
maskerAutoTypeIndex = self.options_maskerComboBox.findData(autoTypeString)
self.options_roisComboBox.setCurrentIndex(roisAutoTypeIndex)
self.options_maskerComboBox.setCurrentIndex(maskerAutoTypeIndex)
def deviceFileSelected(self):
if selectedFiles := self.deviceFileSelector.selectedFiles():
file = selectedFiles[0]
self.deviceComboBox.loadDevicesJson(file)
def options_fillComboBoxes(self):
self.options_roisComboBox.addItem("RoisAutoT1", "AutoT1")
self.options_roisComboBox.addItem("RoisAutoT2", "AutoT2")
self.options_roisComboBox.setCurrentIndex(-1)
def knnModelFileSelected(self):
if selectedFiles := self.knnModelSelector.selectedFiles():
self.knnModel = cv2.ml.KNearest.load(selectedFiles[0])
self.options_maskerComboBox.addItem("MaskerAutoT1", "AutoT1")
self.options_maskerComboBox.addItem("MaskerAutoT2", "AutoT2")
self.options_maskerComboBox.setCurrentIndex(-1)
def phashDatabaseFileSelected(self):
if selectedFiles := self.phashDatabaseSelector.selectedFiles():
self.phashDatabase = ImagePHashDatabase(selectedFiles[0])
self.options_presetComboBox.addItem("AutoT1 (ver <= 4.7.2)", "AutoT1")
self.options_presetComboBox.addItem("AutoT2 (ver >= 5.0.0)", "AutoT2")
self.options_presetComboBox.setCurrentIndex(1)
def knnModelSelected(self):
try:
knnModelFile = self.dependencies_knnModelSelector.selectedFiles()[0]
self.knnModel = cv2.ml.KNearest.load(knnModelFile)
varCount = self.knnModel.getVarCount()
if varCount != 81:
self.dependencies_knnModelStatusLabel.setText(
f'<font color="darkorange">WARN</font>, varCount {varCount}'
)
else:
self.dependencies_knnModelStatusLabel.setText(
f'<font color="green">OK</font>, varCount {varCount}'
)
except Exception:
logger.exception("Error loading knn model:")
self.dependencies_knnModelStatusLabel.setText(
'<font color="red">Error</font>'
)
def phashDatabaseSelected(self):
try:
phashDbFile = self.dependencies_phashDatabaseSelector.selectedFiles()[0]
self.phashDatabase = ImagePhashDatabase(phashDbFile)
self.dependencies_phashDatabaseStatusLabel.setText(
f'<font color="green">OK</font>, '
f"J{len(self.phashDatabase.jacket_hashes)} "
f"PI{len(self.phashDatabase.partner_icon_hashes)}"
)
except Exception:
logger.exception("Error loading phash database:")
self.dependencies_phashDatabaseStatusLabel.setText(
'<font color="red">Error</font>'
)
@Slot()
def on_ocr_addImageButton_clicked(self):
files, _filter = QFileDialog.getOpenFileNames(
files, _ = QFileDialog.getOpenFileNames(
self, None, "", "Image Files (*.png *.jpg *.jpeg *.bmp *.webp);;*"
)
filesNum = len(files)
@ -114,30 +157,51 @@ class TabOcr_Device(Ui_TabOcr_Device, QWidget):
QApplication.processEvents()
self.ocrQueue.resizeTableView()
def deviceRois(self):
if self.options_roisUseCustomCheckBox.isChecked():
...
else:
selectedPreset = self.options_roisComboBox.currentData()
if selectedPreset == "AutoT1":
return DeviceRoisAutoT1
elif selectedPreset == "AutoT2":
return DeviceRoisAutoT2
else:
QMessageBox.critical(self, None, "Select a Rois preset first.")
return None
def deviceRoisMasker(self):
if self.options_maskerUseCustomCheckBox.isChecked():
...
else:
selectedPreset = self.options_maskerComboBox.currentData()
if selectedPreset == "AutoT1":
return DeviceRoisMaskerAutoT1()
elif selectedPreset == "AutoT2":
return DeviceRoisMaskerAutoT2()
else:
QMessageBox.critical(self, None, "Select a Masker preset first.")
return None
@Slot()
def on_ocr_startButton_clicked(self):
for row in range(self.ocrQueueModel.rowCount()):
index = self.ocrQueueModel.index(row, 0)
imagePath = index.data(OcrQueueModel.ImagePathRole)
if self.deviceUseAutoFactorCheckBox.checkState() == Qt.CheckState.Checked:
runnable = TabDeviceV2AutoRoisOcrRunnable(
imagePath,
self.knnModel,
self.phashDatabase,
sizesV2=self.deviceSizesV2CheckBox.isChecked(),
)
else:
runnable = TabDeviceV2OcrRunnable(
imagePath,
self.deviceComboBox.currentData(),
self.knnModel,
self.phashDatabase,
sizesV2=self.deviceSizesV2CheckBox.isChecked(),
)
rois = self.deviceRois()
masker = self.deviceRoisMasker()
if rois is None or masker is None:
return
runnable = TabDeviceOcrRunnable(
imagePath, rois, masker, self.knnModel, self.phashDatabase
)
self.ocrQueueModel.setData(index, runnable, OcrQueueModel.OcrRunnableRole)
self.ocrQueueModel.setData(
index,
ScoreConverter.deviceV2,
ScoreConverter.device,
OcrQueueModel.ProcessOcrResultFuncRole,
)
self.ocrQueueModel.startQueue()

View File

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

View File

@ -180,19 +180,20 @@ class TabTools_Andreal(Ui_TabTools_Andreal, QWidget):
arguments = [
str(self.imageType()),
f'--json-file="{jsonFile}"',
f"--img-version={self.imageVersion()}",
"--json-file",
jsonFile,
"--img-version",
str(self.imageVersion()),
]
if self.andrealFolderSelector.selectedFiles():
arguments.append(
f'--path="{self.andrealFolderSelector.selectedFiles()[0]}"'
)
arguments.append("--path")
arguments.append(self.andrealFolderSelector.selectedFiles()[0])
if preview:
arguments.extend(["--img-format=jpg", "--img-quality=20"])
arguments.extend(["--img-format", "jpg", "--img-quality", "20"])
else:
arguments.append(f"--img-format={self.imageFormat()}")
arguments.extend(["--img-format", self.imageFormat()])
if self.imageFormat() == "jpg":
arguments.append(f"--img-quality={self.jpgQualitySpinBox.value()}")
arguments.extend(["--img-quality", str(self.jpgQualitySpinBox.value())])
return arguments
def getAndrealJsonContent(self):

View File

@ -1,27 +1,53 @@
import logging
import random
from arcaea_offline.calculate import calculate_constants_from_play_rating
from arcaea_offline.database import Database
from arcaea_offline.models import Chart, ScoreBest
from arcaea_offline.models import Chart, Score
from arcaea_offline.utils.rating import rating_class_to_text
from arcaea_offline.utils.score import score_to_grade_text
from PySide6.QtCore import Slot
from PySide6.QtWidgets import QLabel, QWidget
from PySide6.QtCore import QModelIndex, Qt, Slot
from PySide6.QtWidgets import QDialog, QLabel, QVBoxLayout, QWidget
from ui.designer.tabs.tabTools.tabTools_ChartRecommend_ui import (
Ui_TabTools_ChartRecommend,
)
from ui.extends.shared.language import LanguageChangeEventFilter
from ui.extends.tabs.tabTools.tabTools_ChartRecommend import (
ChartsModel,
ChartsWithScoreBestModel,
CustomChartDelegate,
CustomScoreBestDelegate,
)
from ui.implements.components.playRatingCalculator import PlayRatingCalculator
logger = logging.getLogger(__name__)
def chartToText(chart: Chart):
return f"{chart.artist} - {chart.title}<br>({chart.song_id}) {rating_class_to_text(chart.rating_class)}"
class QuickPlayRatingCalculatorDialog(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.verticalLayout = QVBoxLayout(self)
def scoreBestToText(score: ScoreBest):
return f"{score_to_grade_text(score.score)} {score.score} > {score.potential:.4f}"
self.chartLabel = QLabel(self)
self.verticalLayout.addWidget(self.chartLabel)
self.playRatingCalculator = PlayRatingCalculator(self)
self.verticalLayout.addWidget(self.playRatingCalculator)
self.setMinimumWidth(400)
self.playRatingCalculator.arcaeaScoreLineEdit.setFocus(
Qt.FocusReason.PopupFocusReason
)
def setChart(self, chart: Chart):
self.chartLabel.setText(
f"{chart.title} {rating_class_to_text(chart.rating_class)} {chart.constant / 10}"
)
self.playRatingCalculator.setConstant(chart.constant)
def setScore(self, score: Score):
self.playRatingCalculator.arcaeaScoreLineEdit.setText(str(score))
class TabTools_ChartRecommend(Ui_TabTools_ChartRecommend, QWidget):
@ -29,13 +55,30 @@ class TabTools_ChartRecommend(Ui_TabTools_ChartRecommend, QWidget):
super().__init__(parent)
self.setupUi(self)
self.languageChangeEventFilter = LanguageChangeEventFilter(self)
self.installEventFilter(self.languageChangeEventFilter)
self.db = Database()
self.chartsByConstant = []
self.chartsRecommendFromPlayRating = []
self.chartsByConstantModel = ChartsModel(self)
self.chartsRecommendFromPlayRatingModel = ChartsWithScoreBestModel(self)
self.numLabelFormatString = "{} charts"
self.chartDelegate = CustomChartDelegate(self)
self.scoreBestDelegate = CustomScoreBestDelegate(self)
self.chartsByConstant_modelView.setModel(self.chartsByConstantModel)
self.chartsByConstant_modelView.setItemDelegate(self.chartDelegate)
self.chartsRecommendFromPlayRating_modelView.setModel(
self.chartsRecommendFromPlayRatingModel
)
self.chartsRecommendFromPlayRating_modelView.setItemDelegateForColumn(
0, self.chartDelegate
)
self.chartsRecommendFromPlayRating_modelView.setItemDelegateForColumn(
1, self.scoreBestDelegate
)
self.chartsRecommendFromPlayRating_playRatingSpinBox.valueChanged.connect(
self.updateChartsRecommendFromPlayRating
)
@ -43,81 +86,91 @@ class TabTools_ChartRecommend(Ui_TabTools_ChartRecommend, QWidget):
self.updateChartsRecommendFromPlayRating
)
self.chartsByConstant_refreshButton.clicked.connect(self.fillChartsByConstant)
self.chartsRecommendFromPlayRating_refreshButton.clicked.connect(
self.fillChartsRecommendFromPlayRating
self.chartsByConstant_modelView.doubleClicked.connect(
self.openQuickPlayRatingCalculator_chartsByConstant
)
self.chartsRecommendFromPlayRating_modelView.doubleClicked.connect(
self.openQuickPlayRatingCalculator_chartsRecommendFromPlayRating
)
@Slot(float)
def on_rangeFromPlayRating_playRatingSpinBox_valueChanged(self, value: float):
try:
result = calculate_constants_from_play_rating(value)
exPlusLower, exPlusUpper = result.EXPlus
exLower, exUpper = result.EX
aaLower, aaUpper = result.AA
self.rangeFromPlayRating_ExPlusLabel.setText(
f"{exPlusLower:.3f}~{exPlusUpper:.3f}"
constant = round(
value, self.rangeFromPlayRating_playRatingSpinBox.decimals()
)
self.rangeFromPlayRating_ExLabel.setText(f"{exLower:.3f}~{exUpper:.3f}")
self.rangeFromPlayRating_AaLabel.setText(f"{aaLower:.3f}~{aaUpper:.3f}")
result = calculate_constants_from_play_rating(constant)
labels = [
self.rangeFromPlayRating_ExPlusLabel,
self.rangeFromPlayRating_ExLabel,
self.rangeFromPlayRating_AaLabel,
self.rangeFromPlayRating_ALabel,
self.rangeFromPlayRating_BLabel,
self.rangeFromPlayRating_CLabel,
]
for label, constantRange in zip(
labels,
[result.EXPlus, result.EX, result.AA, result.A, result.B, result.C],
):
label.setText(f"{constantRange[0]:.3f}~{constantRange[1]:.3f}")
except Exception:
logging.exception("cannot calculate constant from play rating")
logging.exception("Cannot calculate constant from play rating:")
self.rangeFromPlayRating_ExPlusLabel.setText("...")
self.rangeFromPlayRating_ExLabel.setText("...")
self.rangeFromPlayRating_AaLabel.setText("...")
def fillChartsByConstant(self):
while item := self.chartsByConstant_gridLayout.takeAt(0):
item.widget().deleteLater()
charts = random.sample(
self.chartsByConstant, k=min(len(self.chartsByConstant), 6)
)
row = 0
for i, chart in enumerate(charts):
if i % 3 == 0:
row += 1
label = QLabel(self)
label.setText(chartToText(chart))
self.chartsByConstant_gridLayout.addWidget(label, row, i % 3)
@Slot(float)
def on_chartsByConstant_constantSpinBox_valueChanged(self, value: float):
self.chartsByConstant = self.db.get_charts_by_constant(int(value * 10))
chartsNum = len(self.chartsByConstant)
self.chartsByConstant_refreshButton.setEnabled(chartsNum > 6)
constant = round(value, self.chartsByConstant_constantSpinBox.decimals())
charts = self.db.get_charts_by_constant(int(constant * 10))
chartsNum = len(charts)
self.chartsByConstant_numLabel.setText(
self.numLabelFormatString.format(chartsNum)
)
self.fillChartsByConstant()
def fillChartsRecommendFromPlayRating(self):
while item := self.chartsRecommendFromPlayRating_gridLayout.takeAt(0):
item.widget().deleteLater()
charts = random.sample(
self.chartsRecommendFromPlayRating,
k=min(len(self.chartsRecommendFromPlayRating), 6),
)
row = 0
for i, chart in enumerate(charts):
if i % 3 == 0:
row += 1
scoreBest = self.db.get_score_best(chart.song_id, chart.rating_class)
label = QLabel(self)
label.setText(f"{chartToText(chart)}<br>-<br>{scoreBestToText(scoreBest)}")
self.chartsRecommendFromPlayRating_gridLayout.addWidget(label, row, i % 3)
self.chartsByConstantModel.setCharts(charts)
def updateChartsRecommendFromPlayRating(self):
playRating = self.chartsRecommendFromPlayRating_playRatingSpinBox.value()
bounds = self.chartsRecommendFromPlayRating_boundsSpinBox.value()
self.chartsRecommendFromPlayRating = self.db.recommend_charts(
playRating, bounds
charts = self.db.recommend_charts(
round(
playRating,
self.chartsRecommendFromPlayRating_playRatingSpinBox.decimals(),
),
round(
bounds,
self.chartsRecommendFromPlayRating_boundsSpinBox.decimals(),
),
)
chartsNum = len(self.chartsRecommendFromPlayRating)
self.chartsRecommendFromPlayRating_refreshButton.setEnabled(chartsNum > 6)
chartsNum = len(charts)
self.chartsRecommendFromPlayRating_numLabel.setText(
self.numLabelFormatString.format(chartsNum)
)
self.fillChartsRecommendFromPlayRating()
scores = [self.db.get_score_best(c.song_id, c.rating_class) for c in charts]
self.chartsRecommendFromPlayRatingModel.setChartAndScore(charts, scores)
self.chartsRecommendFromPlayRating_modelView.resizeRowsToContents()
self.chartsRecommendFromPlayRating_modelView.resizeColumnsToContents()
@Slot(QModelIndex)
def openQuickPlayRatingCalculator_chartsByConstant(self, index: QModelIndex):
dialog = QuickPlayRatingCalculatorDialog(self)
chart = index.data(ChartsModel.ChartRole)
dialog.setChart(chart)
dialog.show()
@Slot(QModelIndex)
def openQuickPlayRatingCalculator_chartsRecommendFromPlayRating(
self, index: QModelIndex
):
dialog = QuickPlayRatingCalculatorDialog(self)
row = index.row()
chartIndex = self.chartsRecommendFromPlayRatingModel.item(row, 0)
scoreIndex = self.chartsRecommendFromPlayRatingModel.item(row, 1)
chart = chartIndex.data(ChartsWithScoreBestModel.ChartRole)
score: Score = scoreIndex.data(ChartsWithScoreBestModel.ScoreBestRole)
dialog.setChart(chart)
dialog.setScore(score.score)
dialog.show()

View File

@ -1,6 +1,5 @@
import re
from arcaea_offline.calculate import calculate_play_rating
from arcaea_offline.database import Database
from PySide6.QtCore import QDateTime
from PySide6.QtWidgets import QVBoxLayout, QWidget
@ -27,15 +26,8 @@ class TabTools_InfoLookup(Ui_TabTools_InfoLookup, QWidget):
self.ratingClassSelector.valueChanged.connect(self.updateDifficultyLabels)
self.ratingClassSelector.valueChanged.connect(self.updateChartInfoLabels)
self.songIdSelector.valueChanged.connect(
self.updatePlayRatingCalculateResultLabel
)
self.ratingClassSelector.valueChanged.connect(
self.updatePlayRatingCalculateResultLabel
)
self.playRatingCalculateScoreLineEdit.textChanged.connect(
self.updatePlayRatingCalculateResultLabel
)
self.songIdSelector.valueChanged.connect(self.updatePlayRatingCalculator)
self.ratingClassSelector.valueChanged.connect(self.updatePlayRatingCalculator)
self.langSelectComboBox.addItem("En - English [en]", "en")
self.langSelectComboBox.addItem("あ - Japanese [ja]", "ja")
@ -213,29 +205,12 @@ class TabTools_InfoLookup(Ui_TabTools_InfoLookup, QWidget):
str(chartInfo.notes) if chartInfo.notes is not None else "-"
)
def resetPlayRatingCalculateResultLabel(self):
self.playRatingCalculateResultLabel.setText("...")
def updatePlayRatingCalculateResultLabel(self):
def updatePlayRatingCalculator(self):
songId = self.songIdSelector.songId()
ratingClass = self.ratingClassSelector.value()
if not songId or ratingClass is None:
self.resetPlayRatingCalculateResultLabel()
return
chartInfo = self.db.get_chart_info(songId, ratingClass)
if not chartInfo or not chartInfo.constant:
self.resetPlayRatingCalculateResultLabel()
return
if scoreText := self.playRatingCalculateScoreLineEdit.text().replace("'", ""):
score = int(scoreText)
self.playRatingCalculateResultLabel.setText(
f"{calculate_play_rating(chartInfo.constant, score):.3f}"
)
if not chartInfo:
self.playRatingCalculator.setConstant(None)
else:
self.resetPlayRatingCalculateResultLabel()
return
self.playRatingCalculator.setConstant(chartInfo.constant)

View File

@ -1,6 +1,5 @@
import logging
from arcaea_offline.calculate import calculate_play_rating
from arcaea_offline.calculate.world_step import (
AmaneBelowExPartnerBonus,
AwakenedEtoPartnerBonus,
@ -15,14 +14,22 @@ from arcaea_offline.calculate.world_step import (
calculate_step,
calculate_step_original,
)
from arcaea_offline.models import Chart, Score
from PySide6.QtCore import QEasingCurve, QObject, QSize, Qt, QTimeLine
from PySide6.QtGui import QIcon, QPainter, QPaintEvent, QPixmap
from PySide6.QtCore import (
QCoreApplication,
QEasingCurve,
QObject,
QSize,
QTimeLine,
Signal,
)
from PySide6.QtGui import QIcon, QPixmap
from PySide6.QtWidgets import (
QAbstractButton,
QButtonGroup,
QDialog,
QGraphicsColorizeEffect,
QLabel,
QPushButton,
QVBoxLayout,
QWidget,
)
@ -30,26 +37,13 @@ from ui.designer.tabs.tabTools.tabTools_StepCalculator_ui import (
Ui_TabTools_StepCalculator,
)
from ui.extends.shared.language import LanguageChangeEventFilter
from ui.implements.components.chartAndScoreInput import ChartAndScoreInput
from ui.implements.components.chartSelector import ChartSelector
from ui.implements.components.playRatingCalculator import PlayRatingCalculator
from ui.implements.components.songIdSelector import SongIdSelectorMode
logger = logging.getLogger(__name__)
class MapTypeListWidgetWidget(QLabel):
def paintEvent(self, e: QPaintEvent) -> None:
size = self.size()
painter = QPainter(self)
scaledPixmap = self.pixmap().scaled(
size,
Qt.AspectRatioMode.KeepAspectRatio,
Qt.TransformationMode.SmoothTransformation,
)
x = (size.width() - scaledPixmap.width()) / 2
y = (size.height() - scaledPixmap.height()) / 2
painter.drawPixmap(x, y, scaledPixmap)
class ButtonGrayscaleEffectApplier(QObject):
def __init__(self, parent: QAbstractButton):
super().__init__(parent)
@ -79,11 +73,56 @@ class ButtonGrayscaleEffectApplier(QObject):
target.setGraphicsEffect(effect)
class ChartAndScoreInputDialog(ChartAndScoreInput):
class PlayRatingCalculatorDialog(QDialog):
accepted = Signal()
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowFlag(Qt.WindowType.Dialog, True)
self.setSongIdSelectorMode(SongIdSelectorMode.Chart)
self.verticalLayout = QVBoxLayout(self)
self.chartSelector = ChartSelector(self)
self.chartSelector.setSongIdSelectorMode(SongIdSelectorMode.Chart)
self.verticalLayout.addWidget(self.chartSelector)
self.playRatingCalculator = PlayRatingCalculator(self)
self.verticalLayout.addWidget(self.playRatingCalculator)
self.acceptButton = QPushButton(self)
self.acceptButton.setText(
# fmt: off
QCoreApplication.translate("StepCalculator", "playRatingCalculatorDialog.acceptButton")
# fmt: on
)
self.acceptButton.setEnabled(False)
self.verticalLayout.addWidget(self.acceptButton)
self.chartSelector.valueChanged.connect(self.updatePlayRatingCalculator)
self.playRatingCalculator.arcaeaScoreLineEdit.textChanged.connect(
self.updateAcceptButton
)
self.acceptButton.clicked.connect(self.accepted)
def updatePlayRatingCalculator(self):
chart = self.chartSelector.value()
if chart is None:
self.playRatingCalculator.setConstant(None)
else:
self.playRatingCalculator.setConstant(chart.constant)
self.updateAcceptButton()
def updateAcceptButton(self):
if self.playRatingCalculator.result is None:
self.acceptButton.setEnabled(False)
else:
self.acceptButton.setEnabled(True)
def reset(self):
self.chartSelector.resetButton.click()
self.playRatingCalculator.arcaeaScoreLineEdit.clear()
def value(self):
return self.playRatingCalculator.result
class TabTools_StepCalculator(Ui_TabTools_StepCalculator, QWidget):
@ -203,23 +242,18 @@ class TabTools_StepCalculator(Ui_TabTools_StepCalculator, QWidget):
)
self.partnerSkillPresetButton_maya.clicked.connect(self.applyPartnerPreset)
def openChartAndScoreInputDialog(self):
dialog = ChartAndScoreInputDialog(self)
dialog.scoreCommited.connect(
lambda: self.setPlayResultFromChartAndScoreInput(dialog)
)
dialog.show()
self.playRatingCalculatorDialog = PlayRatingCalculatorDialog(self)
self.playRatingCalculatorDialog.accepted.connect(self.set_toStep_PlayRating)
def setPlayResultFromChartAndScoreInput(self, dialog: ChartAndScoreInputDialog):
if score := dialog.score():
chart = dialog.chart()
self.calculate_toStep_playResultSpinBox.setValue(
float(calculate_play_rating(chart.constant, score.score))
)
dialog.close()
dialog.deleteLater()
else:
return
def openChartAndScoreInputDialog(self):
self.playRatingCalculatorDialog.reset()
self.playRatingCalculatorDialog.show()
def set_toStep_PlayRating(self):
result = self.playRatingCalculatorDialog.value()
if result is not None:
self.calculate_toStep_playResultSpinBox.setValue(result)
self.playRatingCalculatorDialog.close()
def applyPartnerPreset(self):
if not self.sender():
@ -307,7 +341,8 @@ class TabTools_StepCalculator(Ui_TabTools_StepCalculator, QWidget):
step = calculate_step(
playResult, partner_bonus=partnerBonus, step_booster=stepBooster
)
self.calculate_toStep_resultLabel.setText(f"{step}<br>({stepOriginal})")
self.calculate_toStep_resultLabel.setText(str(step))
self.calculate_toStep_detailedResultLabel.setText(str(stepOriginal))
except Exception:
if self.detailedLogOutputCheckBox.isChecked():
logger.exception("Cannot calculate toStep")
@ -315,16 +350,14 @@ class TabTools_StepCalculator(Ui_TabTools_StepCalculator, QWidget):
# fromStep
try:
self.calculate_fromStep_resultLabel.setText(
str(
calculate_play_rating_from_step(
self.calculate_fromStep_targetStepSpinBox.value(),
self.partnerStepValueSpinBox.value(),
partner_bonus=partnerBonus,
step_booster=stepBooster,
)
)
fromStepResult = calculate_play_rating_from_step(
self.calculate_fromStep_targetStepSpinBox.value(),
self.partnerStepValueSpinBox.value(),
partner_bonus=partnerBonus,
step_booster=stepBooster,
)
self.calculate_fromStep_resultLabel.setText(str(round(fromStepResult, 2)))
self.calculate_fromStep_detailedResultLabel.setText(str(fromStepResult))
except Exception:
if self.detailedLogOutputCheckBox.isChecked():
logger.exception("Cannot calculate fromStep")

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

View File

@ -17,107 +17,111 @@
<context>
<name>ChartSelector</name>
<message>
<location filename="../../designer/components/chartSelector.ui" line="26"/>
<location filename="../../designer/components/chartSelector.ui" line="20"/>
<source>songIdSelector.title</source>
<translation>Select a Song</translation>
</message>
<message>
<location filename="../../designer/components/chartSelector.ui" line="71"/>
<location filename="../../designer/components/chartSelector.ui" line="65"/>
<source>resetButton</source>
<translation>Reset</translation>
</message>
</context>
<context>
<name>DB30TableModel</name>
<message>
<location filename="../../extends/shared/models/tables/b30.py" line="21"/>
<source>horizontalHeader.id</source>
<translation>ID</translation>
</message>
<message>
<location filename="../../extends/shared/models/tables/b30.py" line="22"/>
<source>horizontalHeader.chart</source>
<translation>Chart</translation>
</message>
<message>
<location filename="../../extends/shared/models/tables/b30.py" line="23"/>
<source>horizontalHeader.score</source>
<translation>Score</translation>
</message>
<message>
<location filename="../../extends/shared/models/tables/b30.py" line="24"/>
<source>horizontalHeader.potential</source>
<translation>Potential</translation>
</message>
</context>
<context>
<name>DatabaseChecker</name>
<message>
<location filename="../../startup/databaseChecker.ui" line="23"/>
<location filename="../../startup/databaseChecker_ui.py" line="162"/>
<source>dbPathLabel</source>
<translation>Database path</translation>
</message>
<message>
<location filename="../../startup/databaseChecker.ui" line="33"/>
<location filename="../../startup/databaseChecker_ui.py" line="165"/>
<source>dbFilenameLabel</source>
<translation>Database filename</translation>
</message>
<message>
<location filename="../../startup/databaseChecker.ui" line="64"/>
<location filename="../../startup/databaseChecker_ui.py" line="168"/>
<source>confirmDbPathButton</source>
<translation>Confirm</translation>
</message>
<message>
<location filename="../../startup/databaseChecker.ui" line="117"/>
<location filename="../../startup/databaseChecker_ui.py" line="177"/>
<source>dbVersionLabel</source>
<translation>Database version</translation>
</message>
<message>
<location filename="../../startup/databaseChecker.ui" line="131"/>
<source>dbReInitLabel</source>
<translation>Re-initialize database</translation>
</message>
<message>
<location filename="../../startup/databaseChecker.ui" line="138"/>
<source>dbReInitButton</source>
<translation>Re-initialize</translation>
</message>
<message>
<location filename="../../startup/databaseChecker.ui" line="93"/>
<location filename="../../startup/databaseChecker_ui.py" line="171"/>
<source>dbCheckConnLabel</source>
<translation>Database connection</translation>
</message>
<message>
<location filename="../../startup/databaseChecker.ui" line="110"/>
<location filename="../../startup/databaseChecker_ui.py" line="174"/>
<source>continueButton</source>
<translation>Continue</translation>
</message>
<message>
<location filename="../../startup/databaseChecker.py" line="115"/>
<location filename="../../startup/databaseChecker.py" line="122"/>
<source>dialog.tryInitExistingDatabase</source>
<translation>The existing database doesn&apos;t seem to be initialized properly, try initialize again?</translation>
</message>
<message>
<location filename="../../startup/databaseChecker.py" line="131"/>
<location filename="../../startup/databaseChecker.py" line="138"/>
<source>dialog.confirmNewDatabase</source>
<translation>Database file does not exist. Create now?</translation>
</message>
</context>
<context>
<name>DbScoreTableModel</name>
<name>DbB30TableModel</name>
<message>
<location filename="../../extends/shared/models/tables/score.py" line="22"/>
<location filename="../../extends/shared/models/tables/b30.py" line="20"/>
<source>horizontalHeader.id</source>
<translation>ID</translation>
</message>
<message>
<location filename="../../extends/shared/models/tables/score.py" line="23"/>
<location filename="../../extends/shared/models/tables/b30.py" line="21"/>
<source>horizontalHeader.chart</source>
<translation>Chart</translation>
</message>
<message>
<location filename="../../extends/shared/models/tables/score.py" line="24"/>
<location filename="../../extends/shared/models/tables/b30.py" line="22"/>
<source>horizontalHeader.score</source>
<translation>Score</translation>
</message>
<message>
<location filename="../../extends/shared/models/tables/score.py" line="25"/>
<location filename="../../extends/shared/models/tables/b30.py" line="23"/>
<source>horizontalHeader.potential</source>
<translation>Potential</translation>
</message>
</context>
<context>
<name>DbScoreTableModel</name>
<message>
<location filename="../../extends/shared/models/tables/score.py" line="28"/>
<source>horizontalHeader.id</source>
<translation>ID</translation>
</message>
<message>
<location filename="../../extends/shared/models/tables/score.py" line="29"/>
<source>horizontalHeader.chart</source>
<translation>Chart</translation>
</message>
<message>
<location filename="../../extends/shared/models/tables/score.py" line="30"/>
<source>horizontalHeader.score</source>
<translation>Score</translation>
</message>
<message>
<location filename="../../extends/shared/models/tables/score.py" line="31"/>
<source>horizontalHeader.potential</source>
<translation>Potential</translation>
</message>
@ -220,89 +224,130 @@
</message>
<message>
<location filename="../../designer/components/ocrQueue.ui" line="28"/>
<source>iccOptionsGroupBox</source>
<translation>ICC Profile Options</translation>
</message>
<message>
<location filename="../../designer/components/ocrQueue.ui" line="34"/>
<source>icc.ignore</source>
<translation>Ignore</translation>
</message>
<message>
<location filename="../../designer/components/ocrQueue.ui" line="41"/>
<source>icc.usePIL</source>
<translation>Use PIL</translation>
</message>
<message>
<location filename="../../designer/components/ocrQueue.ui" line="51"/>
<source>icc.tryFix</source>
<translation>Try fix</translation>
</message>
<message>
<location filename="../../designer/components/ocrQueue.ui" line="61"/>
<source>queue.addImageButton</source>
<translation>Add Image</translation>
</message>
<message>
<location filename="../../designer/components/ocrQueue.ui" line="71"/>
<location filename="../../designer/components/ocrQueue.ui" line="38"/>
<source>queue.removeSelected</source>
<translation>Remove Selected</translation>
</message>
<message>
<location filename="../../designer/components/ocrQueue.ui" line="81"/>
<location filename="../../designer/components/ocrQueue.ui" line="48"/>
<source>queue.removeAll</source>
<translation>Remove All</translation>
</message>
<message>
<location filename="../../designer/components/ocrQueue.ui" line="101"/>
<location filename="../../designer/components/ocrQueue.ui" line="68"/>
<source>queue.optionsButton</source>
<translation>Options</translation>
</message>
<message>
<location filename="../../designer/components/ocrQueue.ui" line="75"/>
<source>queue.startOcrButton</source>
<translation>Start OCR</translation>
</message>
<message>
<location filename="../../designer/components/ocrQueue.ui" line="153"/>
<location filename="../../designer/components/ocrQueue.ui" line="127"/>
<source>results</source>
<translation>Results</translation>
</message>
<message>
<location filename="../../designer/components/ocrQueue.ui" line="162"/>
<location filename="../../designer/components/ocrQueue.ui" line="136"/>
<source>results.acceptSelectedButton</source>
<translation>Accept Selected</translation>
</message>
<message>
<location filename="../../designer/components/ocrQueue.ui" line="169"/>
<location filename="../../designer/components/ocrQueue.ui" line="143"/>
<source>results.acceptAllButton</source>
<translation>Accept All</translation>
</message>
<message>
<location filename="../../designer/components/ocrQueue.ui" line="189"/>
<location filename="../../designer/components/ocrQueue.ui" line="163"/>
<source>results.ignoreValidate</source>
<translation>Ignore
validation</translation>
</message>
</context>
<context>
<name>OcrQueueOptionsDialog</name>
<message>
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="14"/>
<source>OCR Options</source>
<translation>OCR Options</translation>
</message>
<message>
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="22"/>
<source>iccOptionsGroupBox</source>
<translation>ICC Profile Options</translation>
</message>
<message>
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="28"/>
<source>icc.useQt</source>
<translation>Use Qt</translation>
</message>
<message>
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="35"/>
<source>icc.usePIL</source>
<translation>Use PIL</translation>
</message>
<message>
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="45"/>
<source>icc.tryFix</source>
<translation>Try fix</translation>
</message>
<message>
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="55"/>
<source>dateOptionsGroupBox</source>
<translation>Date Source</translation>
</message>
<message>
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="64"/>
<source>date.readFromExif</source>
<translation>Read from image EXIF</translation>
</message>
<message>
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="74"/>
<source>date.useCreationDate</source>
<translation>File creation time</translation>
</message>
<message>
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="84"/>
<source>date.useModifyDate</source>
<translation>File last modification time</translation>
</message>
</context>
<context>
<name>OcrTableModel</name>
<message>
<location filename="../../extends/components/ocrQueue.py" line="347"/>
<location filename="../../extends/components/ocrQueue.py" line="348"/>
<source>horizontalHeader.title.select</source>
<translation>Select</translation>
</message>
<message>
<location filename="../../extends/components/ocrQueue.py" line="348"/>
<location filename="../../extends/components/ocrQueue.py" line="351"/>
<source>horizontalHeader.title.imagePreview</source>
<translation>Image Preview</translation>
</message>
<message>
<location filename="../../extends/components/ocrQueue.py" line="349"/>
<location filename="../../extends/components/ocrQueue.py" line="352"/>
<source>horizontalHeader.title.chart</source>
<translation>Chart</translation>
</message>
<message>
<location filename="../../extends/components/ocrQueue.py" line="350"/>
<location filename="../../extends/components/ocrQueue.py" line="353"/>
<source>horizontalHeader.title.score</source>
<translation>Score</translation>
</message>
</context>
<context>
<name>PotentialCalculator</name>
<message>
<location filename="../../implements/components/playRatingCalculator.py" line="85"/>
<source>copyButton</source>
<translation>Copy</translation>
</message>
</context>
<context>
<name>ResettableItem</name>
<message>
@ -477,12 +522,9 @@ validation</translation>
<location filename="../../implements/settings/settingsAndreal.py" line="82"/>
<location filename="../../implements/settings/settingsAndreal.py" line="85"/>
<location filename="../../implements/settings/settingsGeneral.py" line="107"/>
<location filename="../../implements/settings/settingsOcr.py" line="190"/>
<location filename="../../implements/settings/settingsOcr.py" line="193"/>
<location filename="../../implements/settings/settingsOcr.py" line="196"/>
<location filename="../../implements/settings/settingsOcr.py" line="199"/>
<location filename="../../implements/settings/settingsOcr.py" line="202"/>
<location filename="../../implements/settings/settingsOcr.py" line="205"/>
<location filename="../../implements/settings/settingsOcr.py" line="104"/>
<location filename="../../implements/settings/settingsOcr.py" line="107"/>
<location filename="../../implements/settings/settingsOcr.py" line="110"/>
<source>resetButton</source>
<translation>Reset</translation>
</message>
@ -517,37 +559,22 @@ validation</translation>
<translation>Database URL</translation>
</message>
<message>
<location filename="../../implements/settings/settingsOcr.py" line="187"/>
<location filename="../../implements/settings/settingsOcr.py" line="101"/>
<source>ocr.title</source>
<translation>OCR</translation>
</message>
<message>
<location filename="../../implements/settings/settingsOcr.py" line="189"/>
<source>ocr.devicesJson.label</source>
<translation>Default devices.json</translation>
</message>
<message>
<location filename="../../implements/settings/settingsOcr.py" line="192"/>
<source>ocr.deviceUuid.label</source>
<translation>Default device</translation>
</message>
<message>
<location filename="../../implements/settings/settingsOcr.py" line="195"/>
<location filename="../../implements/settings/settingsOcr.py" line="103"/>
<source>ocr.knnModelFile.label</source>
<translation>Default KNearest model</translation>
</message>
<message>
<location filename="../../implements/settings/settingsOcr.py" line="198"/>
<location filename="../../implements/settings/settingsOcr.py" line="106"/>
<source>ocr.b30KnnModelFile.label</source>
<translation>Default B30 KNearest model</translation>
</message>
<message>
<location filename="../../implements/settings/settingsOcr.py" line="201"/>
<source>ocr.siftDatabaseFile.label</source>
<translation>Default SIFT database file</translation>
</message>
<message>
<location filename="../../implements/settings/settingsOcr.py" line="204"/>
<location filename="../../implements/settings/settingsOcr.py" line="109"/>
<source>ocr.phashDatabaseFile.label</source>
<translation>Default image PHash database</translation>
</message>
@ -560,18 +587,26 @@ validation</translation>
<translation>Search...</translation>
</message>
<message>
<location filename="../../designer/components/songIdSelector.ui" line="54"/>
<location filename="../../designer/components/songIdSelector.ui" line="94"/>
<location filename="../../designer/components/songIdSelector.ui" line="41"/>
<location filename="../../designer/components/songIdSelector.ui" line="81"/>
<source>previous</source>
<translation>Previous</translation>
</message>
<message>
<location filename="../../designer/components/songIdSelector.ui" line="77"/>
<location filename="../../designer/components/songIdSelector.ui" line="117"/>
<location filename="../../designer/components/songIdSelector.ui" line="64"/>
<location filename="../../designer/components/songIdSelector.ui" line="104"/>
<source>next</source>
<translation>Next</translation>
</message>
</context>
<context>
<name>StepCalculator</name>
<message>
<location filename="../../implements/tabs/tabTools/tabTools_StepCalculator.py" line="96"/>
<source>playRatingCalculatorDialog.acceptButton</source>
<translation>Accept</translation>
</message>
</context>
<context>
<name>TabAbout</name>
<message>
@ -592,6 +627,16 @@ validation</translation>
<source>tab.manage</source>
<translation>Manage</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDbEntry.ui" line="29"/>
<source>tab.chartInfoEditor</source>
<translation>Chart Info Editor</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDbEntry.ui" line="34"/>
<source>tab.removeDuplicateScores</source>
<translation>Remove Duplicate Scores</translation>
</message>
<message>
<location filename="../../implements/tabs/tabDbEntry.py" line="20"/>
<source>tab.scoreTableViewer</source>
@ -603,78 +648,290 @@ validation</translation>
<translation>Table [B30]</translation>
</message>
</context>
<context>
<name>TabDb_ChartInfoEditor</name>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_ChartInfoEditor.ui" line="20"/>
<source>editor.title</source>
<translation>Editor</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_ChartInfoEditor.ui" line="32"/>
<source>editor.constant</source>
<translation>Constant</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_ChartInfoEditor.ui" line="39"/>
<source>editor.notes</source>
<translation>Notes</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_ChartInfoEditor.ui" line="168"/>
<source>editor.tip</source>
<translation>Tip</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_ChartInfoEditor.ui" line="175"/>
<source>editor.tip.content</source>
<translation>Due to the special data structure,&lt;br&gt;please fill in 10 times of the constant value.&lt;br&gt;For example, Testify [BYD] is a 12.0,&lt;br&gt;then fill &quot;120&quot; instead of &quot;12.0&quot;.</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_ChartInfoEditor.ui" line="213"/>
<source>editor.delete</source>
<translation>Delete</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_ChartInfoEditor.ui" line="220"/>
<source>editor.commit</source>
<translation>Commit</translation>
</message>
<message>
<location filename="../../implements/tabs/tabDb/tabDb_ChartInfoEditor.py" line="167"/>
<location filename="../../implements/tabs/tabDb/tabDb_ChartInfoEditor.py" line="206"/>
<source>commit.chartNotSelected</source>
<translation>Please select a chart first.</translation>
</message>
<message>
<location filename="../../implements/tabs/tabDb/tabDb_ChartInfoEditor.py" line="176"/>
<source>commit.constantRequired</source>
<translation>Constant field is required.</translation>
</message>
<message>
<location filename="../../implements/tabs/tabDb/tabDb_ChartInfoEditor.py" line="217"/>
<source>deleteConfirm</source>
<translation>Are you sure to delete this chart data?</translation>
</message>
</context>
<context>
<name>TabDb_Manage</name>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="23"/>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="78"/>
<source>syncArcSongDbButton</source>
<translation>Sync arcsong.db</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="30"/>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="85"/>
<source>syncArcSongDb.description</source>
<translation>Update chart info</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="37"/>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="98"/>
<source>importScoreGroup</source>
<translation>Import Score</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="105"/>
<source>importSt3Button</source>
<translation>Import Score Database</translation>
<translation>Game Save Database</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="44"/>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="112"/>
<source>importSt3.description</source>
<translation>Import your local score database</translation>
<translation>Import scores from your game save database</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="58"/>
<source>exportScoresButton</source>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="139"/>
<source>exportScoreGroup</source>
<translation>Export Scores</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="65"/>
<source>exportScores.description</source>
<translation>Export all your scores to a JSON file</translation>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="146"/>
<source>exportScoresButton</source>
<translation>D.E.F. V2</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="79"/>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="153"/>
<source>exportScores.description</source>
<translation>Export all your scores in &lt;i&gt;Arcaea Offline Data Exchange Format V2&lt;/i&gt; formed JSON file</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="186"/>
<source>miscGroup</source>
<translation>Miscellaneous</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="284"/>
<source>syncChartInfoDbButton</source>
<translation>Sync Chart Info Database</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="291"/>
<source>syncChartInfoDb.description</source>
<translation>Update chart info</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="23"/>
<source>importPacklistButton</source>
<translation>Import packlist</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="86"/>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="37"/>
<source>importSonglistButton</source>
<translation>Import songlist</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="93"/>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="30"/>
<source>importPacklist.description</source>
<translation>Import packlist file</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="100"/>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="44"/>
<source>importSonglist.description</source>
<translation>Import songlist file</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="107"/>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="193"/>
<source>exportArcsongJsonButton</source>
<translation>Export arcsong.json</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="114"/>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="200"/>
<source>exportArcsongJson.description</source>
<translation>Export arcsong.json file</translation>
<translation>Export arcsong.json file based on the information in database</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="121"/>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="51"/>
<source>importApkButton</source>
<translation>Import APK</translation>
<translation>Import from APK</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="128"/>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="213"/>
<source>packSongInfoGroup</source>
<translation>Pack/Song Info</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="58"/>
<source>importApk.description</source>
<translation>Import packlist and songlist from .apk file</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="71"/>
<source>chartInfoGroup</source>
<translation>Chart Info</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="119"/>
<source>importOnlineButton</source>
<translation>Arcaea Online</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="126"/>
<source>importOnline.description</source>
<translation>Import scores from the Arcaea Online API result</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="160"/>
<source>exportSmartRteB30Button</source>
<translation>SmartRTE B30</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="167"/>
<source>exportSmartRteB30.description</source>
<translation>Export all your scores to &lt;a href=&quot;https://smartrte.github.io/b30gen.html&quot;&gt;smartrte.github.io&lt;/a&gt; compatible CSV file</translation>
</message>
</context>
<context>
<name>TabDb_RemoveDuplicateScores</name>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="20"/>
<source>scan.title</source>
<translation>Scan Options</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="30"/>
<source>scan.option.score</source>
<translation>Score</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="71"/>
<source>scan.option.date</source>
<translation>Date</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="78"/>
<source>scan.option.modifier</source>
<translation>Modifier</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="85"/>
<source>scan.option.clearType</source>
<translation>Clear Type</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="94"/>
<source>scan.scanButton</source>
<translation>Scan</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="124"/>
<source>quickSelect.title</source>
<translation>Quick Select</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="130"/>
<source>quickSelect.description</source>
<translation>Keep the first score item&lt;br&gt;that matches:</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="166"/>
<source>quickSelect.selectButton</source>
<translation>Select</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="182"/>
<source>deselectAllButton</source>
<translation>Clear Selection</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="189"/>
<source>reverseSelectionButton</source>
<translation>Reverse Selection</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="209"/>
<source>collapseAllButton</source>
<translation>Collapse All Groups</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="216"/>
<source>expandAllButton</source>
<translation>Expand All Groups</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="223"/>
<source>resetModelButton</source>
<translation>Reset Model</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="251"/>
<source>deleteSelectionButton</source>
<translation>Delete Selected Scores</translation>
</message>
<message>
<location filename="../../implements/tabs/tabDb/tabDb_RemoveDuplicateScores.py" line="158"/>
<source>quickSelectComboBox.idEarlier</source>
<translation>Earlier ID</translation>
</message>
<message>
<location filename="../../implements/tabs/tabDb/tabDb_RemoveDuplicateScores.py" line="164"/>
<source>quickSelectComboBox.dateEarlier</source>
<translation>Earlier date</translation>
</message>
<message>
<location filename="../../implements/tabs/tabDb/tabDb_RemoveDuplicateScores.py" line="170"/>
<source>quickSelectComboBox.columnsIntegral</source>
<translation>More complete data</translation>
</message>
<message>
<location filename="../../implements/tabs/tabDb/tabDb_RemoveDuplicateScores.py" line="295"/>
<source>deleteSelectionDialog.content {}</source>
<translation>Deleting {} scores from database, this cannot be undone!&lt;br&gt;Confirm?</translation>
</message>
<message>
<location filename="../../implements/tabs/tabDb/tabDb_RemoveDuplicateScores.py" line="317"/>
<source>scan_noColumnsDialog.content</source>
<translation>You haven&apos;t selected any column! Are you sure to continue?</translation>
</message>
</context>
<context>
<name>TabOcrDisabled</name>
@ -696,6 +953,11 @@ validation</translation>
<source>tab.b30</source>
<translation>B30</translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcrEntry.ui" line="34"/>
<source>tab.buildPHashDatabase</source>
<translation>Build pHash Database</translation>
</message>
</context>
<context>
<name>TabOcr_B30</name>
@ -705,24 +967,72 @@ validation</translation>
<translation>B30 Image Type</translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_B30.ui" line="34"/>
<source>knnModelSelector.title</source>
<translation>Select KNearest Model</translation>
<location filename="../../designer/tabs/tabOcr/tabOcr_B30.ui" line="32"/>
<source>dependencies.title</source>
<translation>OCR Dependencies</translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_B30.ui" line="46"/>
<source>b30KnnModelSelector.title</source>
<translation>Select B30 Specialized KNearest Model</translation>
<location filename="../../designer/tabs/tabOcr/tabOcr_B30.ui" line="38"/>
<source>dependencies.knnModel</source>
<translation>KNearest model</translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_B30.ui" line="62"/>
<source>phashDatabaseSelector.title</source>
<translation>Select Image PHash Database</translation>
<source>dependencies.b30KnnModel</source>
<translation>B30 KNearest model</translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_B30.ui" line="74"/>
<source>imageSelector.title</source>
<translation>Select Image</translation>
<location filename="../../designer/tabs/tabOcr/tabOcr_B30.ui" line="86"/>
<source>dependencies.phashDatabase</source>
<translation>Image pHash database</translation>
</message>
</context>
<context>
<name>TabOcr_BuildPHashDatabase</name>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_BuildPHashDatabase.ui" line="33"/>
<source>folders.title</source>
<translation>Data Folders</translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_BuildPHashDatabase.ui" line="41"/>
<source>folders.songDir</source>
<translation>Song jackets</translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_BuildPHashDatabase.ui" line="62"/>
<source>folders.charIconDir</source>
<translation>Partner icons</translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_BuildPHashDatabase.ui" line="84"/>
<source>options.title</source>
<translation>Options</translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_BuildPHashDatabase.ui" line="157"/>
<source>options.preprocessCharIcon</source>
<translation>Preprocess partner icons</translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_BuildPHashDatabase.ui" line="180"/>
<source>resetButton</source>
<translation>Reset</translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_BuildPHashDatabase.ui" line="199"/>
<source>[Reading images] %v/%m - %p%</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_BuildPHashDatabase.ui" line="215"/>
<source>[Calculate hashes] %v/%m - %p%</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_BuildPHashDatabase.ui" line="237"/>
<source>buildButton</source>
<translation>Build</translation>
</message>
</context>
<context>
@ -734,34 +1044,50 @@ validation</translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="27"/>
<source>deviceSelector.title</source>
<translation>Select Device</translation>
<source>options.title</source>
<translation>Options</translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="35"/>
<source>deviceSelector.useAutoFactor</source>
<translation>Auto calculate factor</translation>
<source>options.usePreset</source>
<translation>Use preset</translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="81"/>
<source>knnModelSelector.title</source>
<translation>Select KNearest Model</translation>
<location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="73"/>
<source>options.rois</source>
<translation>Rois</translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="112"/>
<source>tesseractSelector.title</source>
<translation>Select tesseract Path</translation>
<location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="134"/>
<source>options.masker</source>
<translation>Masker</translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="140"/>
<source>phashDatabaseSelector.title</source>
<translation>Select Image PHash Database</translation>
<location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="144"/>
<location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="151"/>
<source>options.useCustom</source>
<translation>Use custom options</translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="215"/>
<source>dependencies.title</source>
<translation>OCR Dependencies</translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="221"/>
<source>dependencies.knnModel</source>
<translation>KNearest model</translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="258"/>
<source>dependencies.phashDatabase</source>
<translation>Image pHash database</translation>
</message>
</context>
<context>
<name>TabOverview</name>
<message>
<location filename="../../implements/tabs/tabOverview.py" line="43"/>
<location filename="../../implements/tabs/tabOverview.py" line="56"/>
<source>databaseDescribeLabel {} {} {} {} {} {}</source>
<translation>There are {} packs, {} songs, {} difficulties, {} chart info ({} complete) and {} scores in database.</translation>
</message>
@ -851,6 +1177,11 @@ validation</translation>
<source>generateImageButton</source>
<translation>Generate</translation>
</message>
<message>
<location filename="../../designer/tabs/tabTools/tabTools_Andreal.ui" line="335"/>
<source>sourceCode</source>
<translation>Source code</translation>
</message>
<message>
<location filename="../../implements/tabs/tabTools/tabTools_Andreal.py" line="138"/>
<source>imageWhatIsThisDialog.description</source>
@ -860,23 +1191,17 @@ validation</translation>
<context>
<name>TabTools_ChartRecommend</name>
<message>
<location filename="../../designer/tabs/tabTools/tabTools_ChartRecommend.ui" line="20"/>
<location filename="../../designer/tabs/tabTools/tabTools_ChartRecommend.ui" line="102"/>
<source>constantRangeFromPlayRating</source>
<translation>Chart Constant Range from Play Rating</translation>
</message>
<message>
<location filename="../../designer/tabs/tabTools/tabTools_ChartRecommend.ui" line="118"/>
<location filename="../../designer/tabs/tabTools/tabTools_ChartRecommend.ui" line="20"/>
<source>chartsByConstant</source>
<translation>Charts by Constant</translation>
</message>
<message>
<location filename="../../designer/tabs/tabTools/tabTools_ChartRecommend.ui" line="174"/>
<location filename="../../designer/tabs/tabTools/tabTools_ChartRecommend.ui" line="273"/>
<source>refreshButton</source>
<translation>Roll</translation>
</message>
<message>
<location filename="../../designer/tabs/tabTools/tabTools_ChartRecommend.ui" line="197"/>
<location filename="../../designer/tabs/tabTools/tabTools_ChartRecommend.ui" line="248"/>
<source>chartsRecommendFromPlayRating</source>
<translation>Chart from Play Rating Based on Best Score</translation>
</message>
@ -1132,17 +1457,17 @@ validation</translation>
<translation>Calculate to Step</translation>
</message>
<message>
<location filename="../../designer/tabs/tabTools/tabTools_StepCalculator.ui" line="386"/>
<location filename="../../designer/tabs/tabTools/tabTools_StepCalculator.ui" line="367"/>
<source>calculate.toStep.playResultLabel</source>
<translation>Play result</translation>
</message>
<message>
<location filename="../../designer/tabs/tabTools/tabTools_StepCalculator.ui" line="419"/>
<location filename="../../designer/tabs/tabTools/tabTools_StepCalculator.ui" line="393"/>
<source>calculate.toStep.calculatePlayResultFromScoreButton</source>
<translation>Calculate from Score</translation>
</message>
<message>
<location filename="../../designer/tabs/tabTools/tabTools_StepCalculator.ui" line="399"/>
<location filename="../../designer/tabs/tabTools/tabTools_StepCalculator.ui" line="406"/>
<source>calculate.toStep.resultLabel</source>
<translation>Result</translation>
</message>
@ -1152,17 +1477,17 @@ validation</translation>
<translation>Detailed log output</translation>
</message>
<message>
<location filename="../../designer/tabs/tabTools/tabTools_StepCalculator.ui" line="429"/>
<location filename="../../designer/tabs/tabTools/tabTools_StepCalculator.ui" line="448"/>
<source>calculate.fromStep</source>
<translation>Calculate from Step</translation>
</message>
<message>
<location filename="../../designer/tabs/tabTools/tabTools_StepCalculator.ui" line="438"/>
<location filename="../../designer/tabs/tabTools/tabTools_StepCalculator.ui" line="457"/>
<source>calculate.fromStep.targetStepLabel</source>
<translation>Target step value</translation>
</message>
<message>
<location filename="../../designer/tabs/tabTools/tabTools_StepCalculator.ui" line="461"/>
<location filename="../../designer/tabs/tabTools/tabTools_StepCalculator.ui" line="480"/>
<source>calculate.fromStep.resultLabel</source>
<translation>Result (play rating)</translation>
</message>

View File

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

View File

@ -17,107 +17,111 @@
<context>
<name>ChartSelector</name>
<message>
<location filename="../../designer/components/chartSelector.ui" line="26"/>
<location filename="../../designer/components/chartSelector.ui" line="20"/>
<source>songIdSelector.title</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/components/chartSelector.ui" line="71"/>
<location filename="../../designer/components/chartSelector.ui" line="65"/>
<source>resetButton</source>
<translation></translation>
</message>
</context>
<context>
<name>DB30TableModel</name>
<message>
<location filename="../../extends/shared/models/tables/b30.py" line="21"/>
<source>horizontalHeader.id</source>
<translation>ID</translation>
</message>
<message>
<location filename="../../extends/shared/models/tables/b30.py" line="22"/>
<source>horizontalHeader.chart</source>
<translation></translation>
</message>
<message>
<location filename="../../extends/shared/models/tables/b30.py" line="23"/>
<source>horizontalHeader.score</source>
<translation></translation>
</message>
<message>
<location filename="../../extends/shared/models/tables/b30.py" line="24"/>
<source>horizontalHeader.potential</source>
<translation> PTT</translation>
</message>
</context>
<context>
<name>DatabaseChecker</name>
<message>
<location filename="../../startup/databaseChecker.ui" line="23"/>
<location filename="../../startup/databaseChecker_ui.py" line="162"/>
<source>dbPathLabel</source>
<translation></translation>
</message>
<message>
<location filename="../../startup/databaseChecker.ui" line="33"/>
<location filename="../../startup/databaseChecker_ui.py" line="165"/>
<source>dbFilenameLabel</source>
<translation></translation>
</message>
<message>
<location filename="../../startup/databaseChecker.ui" line="64"/>
<location filename="../../startup/databaseChecker_ui.py" line="168"/>
<source>confirmDbPathButton</source>
<translation></translation>
</message>
<message>
<location filename="../../startup/databaseChecker.ui" line="117"/>
<location filename="../../startup/databaseChecker_ui.py" line="177"/>
<source>dbVersionLabel</source>
<translation></translation>
</message>
<message>
<location filename="../../startup/databaseChecker.ui" line="131"/>
<source>dbReInitLabel</source>
<translation></translation>
</message>
<message>
<location filename="../../startup/databaseChecker.ui" line="138"/>
<source>dbReInitButton</source>
<translation></translation>
</message>
<message>
<location filename="../../startup/databaseChecker.ui" line="93"/>
<location filename="../../startup/databaseChecker_ui.py" line="171"/>
<source>dbCheckConnLabel</source>
<translation></translation>
</message>
<message>
<location filename="../../startup/databaseChecker.ui" line="110"/>
<location filename="../../startup/databaseChecker_ui.py" line="174"/>
<source>continueButton</source>
<translation></translation>
</message>
<message>
<location filename="../../startup/databaseChecker.py" line="115"/>
<location filename="../../startup/databaseChecker.py" line="122"/>
<source>dialog.tryInitExistingDatabase</source>
<translation></translation>
</message>
<message>
<location filename="../../startup/databaseChecker.py" line="131"/>
<location filename="../../startup/databaseChecker.py" line="138"/>
<source>dialog.confirmNewDatabase</source>
<translation></translation>
</message>
</context>
<context>
<name>DbScoreTableModel</name>
<name>DbB30TableModel</name>
<message>
<location filename="../../extends/shared/models/tables/score.py" line="22"/>
<location filename="../../extends/shared/models/tables/b30.py" line="20"/>
<source>horizontalHeader.id</source>
<translation>ID</translation>
</message>
<message>
<location filename="../../extends/shared/models/tables/score.py" line="23"/>
<location filename="../../extends/shared/models/tables/b30.py" line="21"/>
<source>horizontalHeader.chart</source>
<translation></translation>
</message>
<message>
<location filename="../../extends/shared/models/tables/score.py" line="24"/>
<location filename="../../extends/shared/models/tables/b30.py" line="22"/>
<source>horizontalHeader.score</source>
<translation></translation>
</message>
<message>
<location filename="../../extends/shared/models/tables/score.py" line="25"/>
<location filename="../../extends/shared/models/tables/b30.py" line="23"/>
<source>horizontalHeader.potential</source>
<translation> PTT</translation>
</message>
</context>
<context>
<name>DbScoreTableModel</name>
<message>
<location filename="../../extends/shared/models/tables/score.py" line="28"/>
<source>horizontalHeader.id</source>
<translation>ID</translation>
</message>
<message>
<location filename="../../extends/shared/models/tables/score.py" line="29"/>
<source>horizontalHeader.chart</source>
<translation></translation>
</message>
<message>
<location filename="../../extends/shared/models/tables/score.py" line="30"/>
<source>horizontalHeader.score</source>
<translation></translation>
</message>
<message>
<location filename="../../extends/shared/models/tables/score.py" line="31"/>
<source>horizontalHeader.potential</source>
<translation> PTT</translation>
</message>
@ -220,88 +224,129 @@
</message>
<message>
<location filename="../../designer/components/ocrQueue.ui" line="28"/>
<source>iccOptionsGroupBox</source>
<translation>ICC </translation>
</message>
<message>
<location filename="../../designer/components/ocrQueue.ui" line="34"/>
<source>icc.ignore</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/components/ocrQueue.ui" line="41"/>
<source>icc.usePIL</source>
<translation>使 PIL </translation>
</message>
<message>
<location filename="../../designer/components/ocrQueue.ui" line="51"/>
<source>icc.tryFix</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/components/ocrQueue.ui" line="61"/>
<source>queue.addImageButton</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/components/ocrQueue.ui" line="71"/>
<location filename="../../designer/components/ocrQueue.ui" line="38"/>
<source>queue.removeSelected</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/components/ocrQueue.ui" line="81"/>
<location filename="../../designer/components/ocrQueue.ui" line="48"/>
<source>queue.removeAll</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/components/ocrQueue.ui" line="101"/>
<location filename="../../designer/components/ocrQueue.ui" line="68"/>
<source>queue.optionsButton</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/components/ocrQueue.ui" line="75"/>
<source>queue.startOcrButton</source>
<translation> OCR</translation>
</message>
<message>
<location filename="../../designer/components/ocrQueue.ui" line="153"/>
<location filename="../../designer/components/ocrQueue.ui" line="127"/>
<source>results</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/components/ocrQueue.ui" line="162"/>
<location filename="../../designer/components/ocrQueue.ui" line="136"/>
<source>results.acceptSelectedButton</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/components/ocrQueue.ui" line="169"/>
<location filename="../../designer/components/ocrQueue.ui" line="143"/>
<source>results.acceptAllButton</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/components/ocrQueue.ui" line="189"/>
<location filename="../../designer/components/ocrQueue.ui" line="163"/>
<source>results.ignoreValidate</source>
<translation></translation>
</message>
</context>
<context>
<name>OcrQueueOptionsDialog</name>
<message>
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="14"/>
<source>OCR Options</source>
<translation>OCR </translation>
</message>
<message>
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="22"/>
<source>iccOptionsGroupBox</source>
<translation>ICC </translation>
</message>
<message>
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="28"/>
<source>icc.useQt</source>
<translation>使 Qt</translation>
</message>
<message>
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="35"/>
<source>icc.usePIL</source>
<translation>使 PIL</translation>
</message>
<message>
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="45"/>
<source>icc.tryFix</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="55"/>
<source>dateOptionsGroupBox</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="64"/>
<source>date.readFromExif</source>
<translation> EXIF </translation>
</message>
<message>
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="74"/>
<source>date.useCreationDate</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="84"/>
<source>date.useModifyDate</source>
<translation></translation>
</message>
</context>
<context>
<name>OcrTableModel</name>
<message>
<location filename="../../extends/components/ocrQueue.py" line="347"/>
<location filename="../../extends/components/ocrQueue.py" line="348"/>
<source>horizontalHeader.title.select</source>
<translation></translation>
</message>
<message>
<location filename="../../extends/components/ocrQueue.py" line="348"/>
<location filename="../../extends/components/ocrQueue.py" line="351"/>
<source>horizontalHeader.title.imagePreview</source>
<translation></translation>
</message>
<message>
<location filename="../../extends/components/ocrQueue.py" line="349"/>
<location filename="../../extends/components/ocrQueue.py" line="352"/>
<source>horizontalHeader.title.chart</source>
<translation></translation>
</message>
<message>
<location filename="../../extends/components/ocrQueue.py" line="350"/>
<location filename="../../extends/components/ocrQueue.py" line="353"/>
<source>horizontalHeader.title.score</source>
<translation></translation>
</message>
</context>
<context>
<name>PotentialCalculator</name>
<message>
<location filename="../../implements/components/playRatingCalculator.py" line="85"/>
<source>copyButton</source>
<translation></translation>
</message>
</context>
<context>
<name>ResettableItem</name>
<message>
@ -476,12 +521,9 @@
<location filename="../../implements/settings/settingsAndreal.py" line="82"/>
<location filename="../../implements/settings/settingsAndreal.py" line="85"/>
<location filename="../../implements/settings/settingsGeneral.py" line="107"/>
<location filename="../../implements/settings/settingsOcr.py" line="190"/>
<location filename="../../implements/settings/settingsOcr.py" line="193"/>
<location filename="../../implements/settings/settingsOcr.py" line="196"/>
<location filename="../../implements/settings/settingsOcr.py" line="199"/>
<location filename="../../implements/settings/settingsOcr.py" line="202"/>
<location filename="../../implements/settings/settingsOcr.py" line="205"/>
<location filename="../../implements/settings/settingsOcr.py" line="104"/>
<location filename="../../implements/settings/settingsOcr.py" line="107"/>
<location filename="../../implements/settings/settingsOcr.py" line="110"/>
<source>resetButton</source>
<translation></translation>
</message>
@ -516,37 +558,22 @@
<translation> URL</translation>
</message>
<message>
<location filename="../../implements/settings/settingsOcr.py" line="187"/>
<location filename="../../implements/settings/settingsOcr.py" line="101"/>
<source>ocr.title</source>
<translation>OCR</translation>
</message>
<message>
<location filename="../../implements/settings/settingsOcr.py" line="189"/>
<source>ocr.devicesJson.label</source>
<translation></translation>
</message>
<message>
<location filename="../../implements/settings/settingsOcr.py" line="192"/>
<source>ocr.deviceUuid.label</source>
<translation></translation>
</message>
<message>
<location filename="../../implements/settings/settingsOcr.py" line="195"/>
<location filename="../../implements/settings/settingsOcr.py" line="103"/>
<source>ocr.knnModelFile.label</source>
<translation> KNearest </translation>
</message>
<message>
<location filename="../../implements/settings/settingsOcr.py" line="198"/>
<location filename="../../implements/settings/settingsOcr.py" line="106"/>
<source>ocr.b30KnnModelFile.label</source>
<translation> B30 KNearest </translation>
</message>
<message>
<location filename="../../implements/settings/settingsOcr.py" line="201"/>
<source>ocr.siftDatabaseFile.label</source>
<translation> SIFT </translation>
</message>
<message>
<location filename="../../implements/settings/settingsOcr.py" line="204"/>
<location filename="../../implements/settings/settingsOcr.py" line="109"/>
<source>ocr.phashDatabaseFile.label</source>
<translation> PHash </translation>
</message>
@ -559,18 +586,26 @@
<translation></translation>
</message>
<message>
<location filename="../../designer/components/songIdSelector.ui" line="54"/>
<location filename="../../designer/components/songIdSelector.ui" line="94"/>
<location filename="../../designer/components/songIdSelector.ui" line="41"/>
<location filename="../../designer/components/songIdSelector.ui" line="81"/>
<source>previous</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/components/songIdSelector.ui" line="77"/>
<location filename="../../designer/components/songIdSelector.ui" line="117"/>
<location filename="../../designer/components/songIdSelector.ui" line="64"/>
<location filename="../../designer/components/songIdSelector.ui" line="104"/>
<source>next</source>
<translation></translation>
</message>
</context>
<context>
<name>StepCalculator</name>
<message>
<location filename="../../implements/tabs/tabTools/tabTools_StepCalculator.py" line="96"/>
<source>playRatingCalculatorDialog.acceptButton</source>
<translation></translation>
</message>
</context>
<context>
<name>TabAbout</name>
<message>
@ -591,6 +626,16 @@
<source>tab.manage</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabDbEntry.ui" line="29"/>
<source>tab.chartInfoEditor</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabDbEntry.ui" line="34"/>
<source>tab.removeDuplicateScores</source>
<translation></translation>
</message>
<message>
<location filename="../../implements/tabs/tabDbEntry.py" line="20"/>
<source>tab.scoreTableViewer</source>
@ -602,78 +647,290 @@
<translation> [B30]</translation>
</message>
</context>
<context>
<name>TabDb_ChartInfoEditor</name>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_ChartInfoEditor.ui" line="20"/>
<source>editor.title</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_ChartInfoEditor.ui" line="32"/>
<source>editor.constant</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_ChartInfoEditor.ui" line="39"/>
<source>editor.notes</source>
<translation>note </translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_ChartInfoEditor.ui" line="168"/>
<source>editor.tip</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_ChartInfoEditor.ui" line="175"/>
<source>editor.tip.content</source>
<translation>&lt;br&gt; 10 &lt;br&gt;Testify [BYD] 12.0&lt;br&gt; 120 12.0</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_ChartInfoEditor.ui" line="213"/>
<source>editor.delete</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_ChartInfoEditor.ui" line="220"/>
<source>editor.commit</source>
<translation></translation>
</message>
<message>
<location filename="../../implements/tabs/tabDb/tabDb_ChartInfoEditor.py" line="167"/>
<location filename="../../implements/tabs/tabDb/tabDb_ChartInfoEditor.py" line="206"/>
<source>commit.chartNotSelected</source>
<translation></translation>
</message>
<message>
<location filename="../../implements/tabs/tabDb/tabDb_ChartInfoEditor.py" line="176"/>
<source>commit.constantRequired</source>
<translation></translation>
</message>
<message>
<location filename="../../implements/tabs/tabDb/tabDb_ChartInfoEditor.py" line="217"/>
<source>deleteConfirm</source>
<translation></translation>
</message>
</context>
<context>
<name>TabDb_Manage</name>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="23"/>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="78"/>
<source>syncArcSongDbButton</source>
<translation> arcsong.db</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="30"/>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="85"/>
<source>syncArcSongDb.description</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="37"/>
<source>importSt3Button</source>
<translation></translation>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="98"/>
<source>importScoreGroup</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="44"/>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="105"/>
<source>importSt3Button</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="112"/>
<source>importSt3.description</source>
<translation>姿&lt;br&gt;退</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="58"/>
<source>exportScoresButton</source>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="139"/>
<source>exportScoreGroup</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="65"/>
<source>exportScores.description</source>
<translation> JSON </translation>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="146"/>
<source>exportScoresButton</source>
<translation> V2</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="79"/>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="153"/>
<source>exportScores.description</source>
<translation> &lt;i&gt;Arcaea Offline V2&lt;/i&gt; JSON </translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="186"/>
<source>miscGroup</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="284"/>
<source>syncChartInfoDbButton</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="291"/>
<source>syncChartInfoDb.description</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="23"/>
<source>importPacklistButton</source>
<translation> packlist</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="86"/>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="37"/>
<source>importSonglistButton</source>
<translation> songlist</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="93"/>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="30"/>
<source>importPacklist.description</source>
<translation> packlist </translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="100"/>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="44"/>
<source>importSonglist.description</source>
<translation> songlist </translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="107"/>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="193"/>
<source>exportArcsongJsonButton</source>
<translation> arcsong.json</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="114"/>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="200"/>
<source>exportArcsongJson.description</source>
<translation> arcsong.json </translation>
<translation> arcsong.json </translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="121"/>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="51"/>
<source>importApkButton</source>
<translation> APK</translation>
<translation> APK </translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="128"/>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="213"/>
<source>packSongInfoGroup</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="58"/>
<source>importApk.description</source>
<translation> .apk packlist songlist</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="71"/>
<source>chartInfoGroup</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="119"/>
<source>importOnlineButton</source>
<translation>Arcaea Online</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="126"/>
<source>importOnline.description</source>
<translation> Arcaea Online API </translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="160"/>
<source>exportSmartRteB30Button</source>
<translation>SmartRTE B30</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_Manage.ui" line="167"/>
<source>exportSmartRteB30.description</source>
<translation> &lt;a href=&quot;https://smartrte.github.io/b30gen.html&quot;&gt;smartrte.github.io&lt;/a&gt; 的 CSV 文件</translation>
</message>
</context>
<context>
<name>TabDb_RemoveDuplicateScores</name>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="20"/>
<source>scan.title</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="30"/>
<source>scan.option.score</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="71"/>
<source>scan.option.date</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="78"/>
<source>scan.option.modifier</source>
<translation>Modifier</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="85"/>
<source>scan.option.clearType</source>
<translation>Clear Type</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="94"/>
<source>scan.scanButton</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="124"/>
<source>quickSelect.title</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="130"/>
<source>quickSelect.description</source>
<translation>&lt;br&gt;</translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="166"/>
<source>quickSelect.selectButton</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="182"/>
<source>deselectAllButton</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="189"/>
<source>reverseSelectionButton</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="209"/>
<source>collapseAllButton</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="216"/>
<source>expandAllButton</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="223"/>
<source>resetModelButton</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabDb/tabDb_RemoveDuplicateScores.ui" line="251"/>
<source>deleteSelectionButton</source>
<translation></translation>
</message>
<message>
<location filename="../../implements/tabs/tabDb/tabDb_RemoveDuplicateScores.py" line="158"/>
<source>quickSelectComboBox.idEarlier</source>
<translation>ID </translation>
</message>
<message>
<location filename="../../implements/tabs/tabDb/tabDb_RemoveDuplicateScores.py" line="164"/>
<source>quickSelectComboBox.dateEarlier</source>
<translation></translation>
</message>
<message>
<location filename="../../implements/tabs/tabDb/tabDb_RemoveDuplicateScores.py" line="170"/>
<source>quickSelectComboBox.columnsIntegral</source>
<translation></translation>
</message>
<message>
<location filename="../../implements/tabs/tabDb/tabDb_RemoveDuplicateScores.py" line="295"/>
<source>deleteSelectionDialog.content {}</source>
<translation> {} &lt;br&gt;</translation>
</message>
<message>
<location filename="../../implements/tabs/tabDb/tabDb_RemoveDuplicateScores.py" line="317"/>
<source>scan_noColumnsDialog.content</source>
<translation></translation>
</message>
</context>
<context>
<name>TabOcrDisabled</name>
@ -695,6 +952,11 @@
<source>tab.b30</source>
<translation>B30</translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcrEntry.ui" line="34"/>
<source>tab.buildPHashDatabase</source>
<translation> pHash </translation>
</message>
</context>
<context>
<name>TabOcr_B30</name>
@ -704,24 +966,72 @@
<translation>B30 </translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_B30.ui" line="34"/>
<source>knnModelSelector.title</source>
<translation> KNearest </translation>
<location filename="../../designer/tabs/tabOcr/tabOcr_B30.ui" line="32"/>
<source>dependencies.title</source>
<translation>OCR </translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_B30.ui" line="46"/>
<source>b30KnnModelSelector.title</source>
<translation> B30 KNearest </translation>
<location filename="../../designer/tabs/tabOcr/tabOcr_B30.ui" line="38"/>
<source>dependencies.knnModel</source>
<translation>KNearest </translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_B30.ui" line="62"/>
<source>phashDatabaseSelector.title</source>
<translation> PHash </translation>
<source>dependencies.b30KnnModel</source>
<translation>B30 KNearest </translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_B30.ui" line="74"/>
<source>imageSelector.title</source>
<translation></translation>
<location filename="../../designer/tabs/tabOcr/tabOcr_B30.ui" line="86"/>
<source>dependencies.phashDatabase</source>
<translation> pHash </translation>
</message>
</context>
<context>
<name>TabOcr_BuildPHashDatabase</name>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_BuildPHashDatabase.ui" line="33"/>
<source>folders.title</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_BuildPHashDatabase.ui" line="41"/>
<source>folders.songDir</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_BuildPHashDatabase.ui" line="62"/>
<source>folders.charIconDir</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_BuildPHashDatabase.ui" line="84"/>
<source>options.title</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_BuildPHashDatabase.ui" line="157"/>
<source>options.preprocessCharIcon</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_BuildPHashDatabase.ui" line="180"/>
<source>resetButton</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_BuildPHashDatabase.ui" line="199"/>
<source>[Reading images] %v/%m - %p%</source>
<translation>[] %v/%m - %p%</translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_BuildPHashDatabase.ui" line="215"/>
<source>[Calculate hashes] %v/%m - %p%</source>
<translation>[] %v/%m - %p%</translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_BuildPHashDatabase.ui" line="237"/>
<source>buildButton</source>
<translation></translation>
</message>
</context>
<context>
@ -733,34 +1043,50 @@
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="27"/>
<source>deviceSelector.title</source>
<translation></translation>
<source>options.title</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="35"/>
<source>deviceSelector.useAutoFactor</source>
<translation> factor</translation>
<source>options.usePreset</source>
<translation>使</translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="81"/>
<source>knnModelSelector.title</source>
<translation> KNearest </translation>
<location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="73"/>
<source>options.rois</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="112"/>
<source>tesseractSelector.title</source>
<translation> tesseract </translation>
<location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="134"/>
<source>options.masker</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="140"/>
<source>phashDatabaseSelector.title</source>
<translation> PHash </translation>
<location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="144"/>
<location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="151"/>
<source>options.useCustom</source>
<translation>使</translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="215"/>
<source>dependencies.title</source>
<translation>OCR </translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="221"/>
<source>dependencies.knnModel</source>
<translation>KNearest </translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="258"/>
<source>dependencies.phashDatabase</source>
<translation> pHash </translation>
</message>
</context>
<context>
<name>TabOverview</name>
<message>
<location filename="../../implements/tabs/tabOverview.py" line="43"/>
<location filename="../../implements/tabs/tabOverview.py" line="56"/>
<source>databaseDescribeLabel {} {} {} {} {} {}</source>
<translation> {} {} {} {} {} {} </translation>
</message>
@ -850,6 +1176,11 @@
<source>generateImageButton</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabTools/tabTools_Andreal.ui" line="335"/>
<source>sourceCode</source>
<translation></translation>
</message>
<message>
<location filename="../../implements/tabs/tabTools/tabTools_Andreal.py" line="138"/>
<source>imageWhatIsThisDialog.description</source>
@ -859,23 +1190,17 @@
<context>
<name>TabTools_ChartRecommend</name>
<message>
<location filename="../../designer/tabs/tabTools/tabTools_ChartRecommend.ui" line="20"/>
<location filename="../../designer/tabs/tabTools/tabTools_ChartRecommend.ui" line="102"/>
<source>constantRangeFromPlayRating</source>
<translation> PTT </translation>
</message>
<message>
<location filename="../../designer/tabs/tabTools/tabTools_ChartRecommend.ui" line="118"/>
<location filename="../../designer/tabs/tabTools/tabTools_ChartRecommend.ui" line="20"/>
<source>chartsByConstant</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabTools/tabTools_ChartRecommend.ui" line="174"/>
<location filename="../../designer/tabs/tabTools/tabTools_ChartRecommend.ui" line="273"/>
<source>refreshButton</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabTools/tabTools_ChartRecommend.ui" line="197"/>
<location filename="../../designer/tabs/tabTools/tabTools_ChartRecommend.ui" line="248"/>
<source>chartsRecommendFromPlayRating</source>
<translation> PTT </translation>
</message>
@ -1131,17 +1456,17 @@
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabTools/tabTools_StepCalculator.ui" line="386"/>
<location filename="../../designer/tabs/tabTools/tabTools_StepCalculator.ui" line="367"/>
<source>calculate.toStep.playResultLabel</source>
<translation> PTT</translation>
</message>
<message>
<location filename="../../designer/tabs/tabTools/tabTools_StepCalculator.ui" line="419"/>
<location filename="../../designer/tabs/tabTools/tabTools_StepCalculator.ui" line="393"/>
<source>calculate.toStep.calculatePlayResultFromScoreButton</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabTools/tabTools_StepCalculator.ui" line="399"/>
<location filename="../../designer/tabs/tabTools/tabTools_StepCalculator.ui" line="406"/>
<source>calculate.toStep.resultLabel</source>
<translation></translation>
</message>
@ -1151,17 +1476,17 @@
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabTools/tabTools_StepCalculator.ui" line="429"/>
<location filename="../../designer/tabs/tabTools/tabTools_StepCalculator.ui" line="448"/>
<source>calculate.fromStep</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabTools/tabTools_StepCalculator.ui" line="438"/>
<location filename="../../designer/tabs/tabTools/tabTools_StepCalculator.ui" line="457"/>
<source>calculate.fromStep.targetStepLabel</source>
<translation> STEP </translation>
</message>
<message>
<location filename="../../designer/tabs/tabTools/tabTools_StepCalculator.ui" line="461"/>
<location filename="../../designer/tabs/tabTools/tabTools_StepCalculator.ui" line="480"/>
<source>calculate.fromStep.resultLabel</source>
<translation> PTT</translation>
</message>

View File

@ -0,0 +1,35 @@
{
"__COMMENT__": "1: EASY, 2: HARD",
"0": 1,
"0u": 1,
"7": 2,
"9": 1,
"10": 2,
"10u": 2,
"15": 1,
"16": 1,
"20": 1,
"28": 2,
"28u": 2,
"29": 2,
"29u": 2,
"35": 2,
"36": 2,
"36u": 2,
"37": 2,
"41": 2,
"42": 2,
"42u": 2,
"43": 2,
"43u": 2,
"54": 2,
"55": 2,
"57": 2,
"61": 2,
"64": 2,
"66": 2,
"66u": 2,
"67": 2,
"68": 1,
"70": 2
}

View File

@ -4,8 +4,13 @@
<file>VERSION</file>
<file>LICENSE</file>
<file>partnerModifiers.json</file>
<file>fonts/GeosansLight.ttf</file>
<file>images/icon.png</file>
<file>images/logo.png</file>
<file>images/jacket-placeholder.png</file>
<file>images/stepCalculator/stamina.png</file>
<file>images/stepCalculator/play.png</file>
<file>images/stepCalculator/memory-boost.png</file>

View File

@ -3,7 +3,7 @@ import traceback
from enum import IntEnum
from arcaea_offline.database import Database
from PySide6.QtCore import QCoreApplication, QDir, QFileInfo, Qt, QUrl, Slot
from PySide6.QtCore import QCoreApplication, QDir, QFileInfo, QSysInfo, Qt, QUrl, Slot
from PySide6.QtWidgets import QDialog, QMessageBox
from ui.extends.shared.database import create_engine
@ -59,8 +59,13 @@ class DatabaseChecker(Ui_DatabaseChecker, QDialog):
return QUrl.fromLocalFile(self.dbFileInfo().filePath())
def dbSqliteUrl(self):
# dbSqliteUrl.setScheme("sqlite")
return QUrl(self.dbFileUrl().toString().replace("file://", "sqlite://"))
kernelType = QSysInfo.kernelType()
# the slash count varies depending on the kernel
# https://docs.sqlalchemy.org/en/20/core/engines.html#sqlite
if kernelType == "winnt":
return QUrl(self.dbFileUrl().toString().replace("file://", "sqlite://"))
else:
return QUrl(self.dbFileUrl().toString().replace("file://", "sqlite:///"))
def confirmDb(self) -> DatabaseCheckerResult:
flags = 0x000
@ -74,6 +79,7 @@ class DatabaseChecker(Ui_DatabaseChecker, QDialog):
db = Database(create_engine(dbSqliteUrl))
if db.check_init():
flags |= DatabaseCheckerResult.Initted
self.settings.setDatabaseUrl(self.dbSqliteUrl().toString())
return flags
@ -102,6 +108,7 @@ class DatabaseChecker(Ui_DatabaseChecker, QDialog):
@Slot()
def on_confirmDbPathButton_clicked(self):
dbSqliteUrl = self.dbSqliteUrl()
self.settings.setDatabaseUrl(dbSqliteUrl.toString())
result = self.confirmDb()
if result & DatabaseCheckerResult.Initted:
@ -136,6 +143,11 @@ class DatabaseChecker(Ui_DatabaseChecker, QDialog):
db.init()
self.updateLabels()
@Slot()
def on_dbReInitButton_clicked(self):
Database().init(checkfirst=True)
QMessageBox.information(self, None, "OK")
@Slot()
def on_continueButton_clicked(self):
self.accept()

View File

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

View File

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