Compare commits
42 Commits
39ee379010
...
qml-rewrit
| Author | SHA1 | Date | |
|---|---|---|---|
|
2819c12ce0
|
|||
|
a086573c0a
|
|||
|
ef61ecf6ae
|
|||
|
71e9f05632
|
|||
|
0966a3eb40
|
|||
|
a2148c7d24
|
|||
|
65dab51734
|
|||
|
3679831201
|
|||
|
7a3c186743
|
|||
|
4409986687
|
|||
|
9c96714c8f
|
|||
|
9a761f1191
|
|||
|
42b3447b43
|
|||
|
d90a165df8
|
|||
|
87304b02f7
|
|||
|
5b44696fd7
|
|||
|
de385e9b26
|
|||
|
1453686de6
|
|||
|
5db2207ee0
|
|||
|
06a1ca00bd
|
|||
|
806acd5793
|
|||
|
ceb6e2932e
|
|||
| 035e2157a8 | |||
| e964a38e3d | |||
| 0e2026ff1c | |||
| 3ce4c7bed9 | |||
| ad09c95b03 | |||
| c664ed7e8d | |||
| 4e7d54fbef | |||
|
0c6f4f4961
|
|||
|
10fb98d530
|
|||
|
bf034d1397
|
|||
|
4f864611ee
|
|||
|
d9c163431c
|
|||
|
d5895fe230
|
|||
|
cd2e3f51ca
|
|||
| 4a09dc210a | |||
|
cc8ab11b78
|
|||
| 48c5682e55 | |||
|
ee03770764
|
|||
|
b45c7f7de5
|
|||
|
15bc56e6f9
|
@ -1,37 +1,65 @@
|
|||||||
name: Build UI from latest `arcaea-offline-*` dependencies
|
name: Build Executable from latest `arcaea-offline-*` dependencies
|
||||||
run-name: ${{ github.actor }} started a build request.
|
run-name: ${{ github.actor }} started a build request.
|
||||||
on: [workflow_dispatch]
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
discussions: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-windows:
|
build:
|
||||||
runs-on: windows-2022
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest]
|
||||||
|
fail-fast: false
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- uses: actions/setup-python@v4
|
|
||||||
|
- uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: '3.11'
|
python-version: "3.11"
|
||||||
|
cache: "pip"
|
||||||
|
|
||||||
# install dependencies
|
# install dependencies
|
||||||
- run: 'pip install -r requirements.txt'
|
- run: "pip install -r requirements.txt"
|
||||||
- run: 'pip uninstall arcaea-offline arcaea-offline-ocr -y'
|
- 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"
|
||||||
- run: 'pip install git+https://github.com/283375/arcaea-offline-ocr'
|
- run: "pip install git+https://github.com/283375/arcaea-offline-ocr"
|
||||||
- run: 'pip install nuitka imageio'
|
- run: "pip install imageio"
|
||||||
- name: Install UPX
|
- name: Install UPX
|
||||||
uses: crazy-max/ghaction-upx@v3
|
uses: crazy-max/ghaction-upx@v3
|
||||||
with:
|
with:
|
||||||
install-only: true
|
install-only: true
|
||||||
|
|
||||||
# release builtin files
|
- name: Release builtin files
|
||||||
- run: 'pyside6-lrelease.exe .\ui\resources\lang\en_US.ts .\ui\resources\lang\zh_CN.ts'
|
run: |
|
||||||
- run: 'python prebuild.py'
|
pyside6-lrelease ui/resources/lang/en_US.ts ui/resources/lang/zh_CN.ts
|
||||||
- run: 'pyside6-rcc.exe .\ui\resources\resources.qrc -o .\ui\resources\resources_rc.py'
|
python prebuild.py
|
||||||
|
pyside6-rcc ui/resources/resources.qrc -o ui/resources/resources_rc.py
|
||||||
|
|
||||||
# build
|
- name: Build Executable
|
||||||
- run: 'python -m nuitka --plugin-enable=upx --enable-plugin=pyside6 --assume-yes-for-downloads --windows-icon-from-ico=./ui/resources/images/icon.png --standalone --onefile index.py'
|
uses: Nuitka/Nuitka-Action@main
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v3
|
|
||||||
with:
|
with:
|
||||||
name: build-windows
|
nuitka-version: main
|
||||||
path: index.exe
|
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/**/*
|
||||||
|
|||||||
85
.github/workflows/build.yml
vendored
@ -1,35 +1,78 @@
|
|||||||
name: Build UI
|
name: Build Executable
|
||||||
run-name: ${{ github.actor }} started a build request.
|
run-name: ${{ github.actor }} started a build request.
|
||||||
on: [workflow_dispatch]
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- "v[0-9]+.[0-9]+.[0-9]+"
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
discussions: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-windows:
|
build:
|
||||||
runs-on: windows-2022
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest]
|
||||||
|
fail-fast: false
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- uses: actions/setup-python@v4
|
|
||||||
with:
|
|
||||||
python-version: '3.11'
|
|
||||||
cache: 'pip'
|
|
||||||
|
|
||||||
# install dependencies
|
- uses: actions/setup-python@v5
|
||||||
- run: 'pip install -r requirements.txt'
|
with:
|
||||||
- run: 'pip install nuitka imageio'
|
python-version: "3.11"
|
||||||
|
cache: "pip"
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
pip install -r requirements.txt
|
||||||
|
pip install imageio
|
||||||
|
|
||||||
- name: Install UPX
|
- name: Install UPX
|
||||||
uses: crazy-max/ghaction-upx@v3
|
uses: crazy-max/ghaction-upx@v3
|
||||||
with:
|
with:
|
||||||
install-only: true
|
install-only: true
|
||||||
|
|
||||||
# release builtin files
|
- name: Release builtin files
|
||||||
- run: 'pyside6-lrelease.exe .\ui\resources\lang\en_US.ts .\ui\resources\lang\zh_CN.ts'
|
run: |
|
||||||
- run: 'python prebuild.py'
|
pyside6-lrelease ui/resources/lang/en_US.ts ui/resources/lang/zh_CN.ts
|
||||||
- run: 'pyside6-rcc.exe .\ui\resources\resources.qrc -o .\ui\resources\resources_rc.py'
|
python prebuild.py
|
||||||
|
pyside6-rcc ui/resources/resources.qrc -o ui/resources/resources_rc.py
|
||||||
|
|
||||||
# build
|
- name: Build Executable
|
||||||
- run: 'python -m nuitka --plugin-enable=upx --enable-plugin=pyside6 --assume-yes-for-downloads --windows-icon-from-ico=./ui/resources/images/icon.png --standalone --onefile index.py'
|
uses: Nuitka/Nuitka-Action@main
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v3
|
|
||||||
with:
|
with:
|
||||||
name: build-windows
|
nuitka-version: main
|
||||||
path: index.exe
|
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/**/*
|
||||||
|
|||||||
3
.gitignore
vendored
@ -1,6 +1,9 @@
|
|||||||
__debug*
|
__debug*
|
||||||
.vscode/
|
.vscode/
|
||||||
|
|
||||||
|
# QML type cache
|
||||||
|
internal/
|
||||||
|
|
||||||
arcaea_offline.db
|
arcaea_offline.db
|
||||||
arcaea_offline.ini
|
arcaea_offline.ini
|
||||||
/data
|
/data
|
||||||
|
|||||||
@ -1,14 +1,13 @@
|
|||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
rev: v4.4.0
|
rev: v6.0.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: end-of-file-fixer
|
- id: end-of-file-fixer
|
||||||
- id: trailing-whitespace
|
- id: trailing-whitespace
|
||||||
- repo: https://github.com/psf/black
|
|
||||||
rev: 23.1.0
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
|
rev: v0.13.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: black
|
- id: ruff
|
||||||
- repo: https://github.com/PyCQA/isort
|
args: ["--fix"]
|
||||||
rev: 5.12.0
|
- id: ruff-format
|
||||||
hooks:
|
|
||||||
- id: isort
|
|
||||||
|
|||||||
24
README.md
@ -1,9 +1,29 @@
|
|||||||
# Arcaea Offline PySide UI
|
# Arcaea Offline PySide UI
|
||||||
|
|
||||||
GUI for both [283375/arcaea-offline](https://github.com/283375/arcaea-offline) and [283375/arcaea-offline-ocr](https://github.com/283375/arcaea-offline-ocr)
|
[](https://github.com/astral-sh/uv)
|
||||||
|
|
||||||
## Before you run `python index.py`...
|
GUI for both [283375/arcaea-offline](https://github.com/283375/arcaea-offline) and [ArcaeaOffline/core-ocr](https://github.com/ArcaeaOffline/core-ocr).
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
* Install requirements
|
* Install requirements
|
||||||
* Release translation files from `ui/resources/lang/*.ts`
|
* Release translation files from `ui/resources/lang/*.ts`
|
||||||
|
* Run `prebuild.py`
|
||||||
* Compile `ui/resources/resources.qrc` to `ui/resources/resources_rc.py`
|
* Compile `ui/resources/resources.qrc` to `ui/resources/resources_rc.py`
|
||||||
|
|
||||||
|
You can refer to the [GitHub Actions file](./.github/workflows/build.yml) for a rough reference.
|
||||||
|
|
||||||
|
```
|
||||||
|
pip install -r ./requirements.txt
|
||||||
|
pyside6-lrelease ./ui/resources/lang/en_US.ts ./ui/resources/lang/zh_CN.ts
|
||||||
|
python prebuild.py
|
||||||
|
pyside6-rcc ./ui/resources/resources.qrc -o ./ui/resources/resources_rc.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Sometimes you have to install the latest, unpublished version of `arcaea-offline` and `arcaea-offline-ocr`.
|
||||||
|
|
||||||
|
```
|
||||||
|
pip uninstall -y arcaea-offline arcaea-offline-ocr
|
||||||
|
pip install git+https://github.com/283375/arcaea-offline
|
||||||
|
pip install git+https://github.com/ArcaeaOffline/core-ocr
|
||||||
|
```
|
||||||
|
|||||||
91
app.py
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import structlog
|
||||||
|
from PySide6.QtCore import QCoreApplication, QEvent, QObject, Qt, QUrl
|
||||||
|
from PySide6.QtGui import QGuiApplication, QIcon
|
||||||
|
from PySide6.QtQml import QQmlApplicationEngine
|
||||||
|
from PySide6.QtQuickControls2 import QQuickStyle
|
||||||
|
|
||||||
|
from ui.resources import resources_rc # noqa: F401
|
||||||
|
from ui.theme import ThemeManager
|
||||||
|
from ui.utils import url # noqa: F401
|
||||||
|
from ui.viewmodels import overview # noqa: F401
|
||||||
|
|
||||||
|
CURRENT_DIRECTORY = Path(__file__).resolve().parent
|
||||||
|
DEFAULT_FONTS = ["微软雅黑", "Microsoft YaHei UI", "Microsoft YaHei", "Segoe UI"]
|
||||||
|
|
||||||
|
|
||||||
|
logger: structlog.stdlib.BoundLogger = structlog.get_logger()
|
||||||
|
|
||||||
|
|
||||||
|
class ThemeChangeEventFilter(QObject):
|
||||||
|
logger: structlog.stdlib.BoundLogger = structlog.get_logger(
|
||||||
|
tag="ThemeChangeEventFilter",
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, *, themeManager: ThemeManager):
|
||||||
|
super().__init__(None)
|
||||||
|
self.themeManager = themeManager
|
||||||
|
|
||||||
|
self.scheme = self.themeManager.getCurrentScheme()
|
||||||
|
|
||||||
|
def doSomething(self) -> None:
|
||||||
|
scheme = self.themeManager.getCurrentScheme()
|
||||||
|
if scheme == self.scheme:
|
||||||
|
self.logger.debug("Ignored same scheme event (%r)", scheme)
|
||||||
|
return
|
||||||
|
|
||||||
|
self.scheme = scheme
|
||||||
|
self.themeManager.updateTheme()
|
||||||
|
self.logger.debug("something done")
|
||||||
|
|
||||||
|
def eventFilter(self, obj: QObject, event: QEvent) -> bool:
|
||||||
|
if event.type() == QEvent.Type.ThemeChange:
|
||||||
|
self.doSomething()
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
app = QGuiApplication(sys.argv)
|
||||||
|
app.setFont(DEFAULT_FONTS)
|
||||||
|
app.setApplicationName("arcaea-offline-pyside-ui")
|
||||||
|
app.setApplicationDisplayName("Arcaea Offline")
|
||||||
|
app.setWindowIcon(QIcon(":/images/icon.png"))
|
||||||
|
|
||||||
|
engine = QQmlApplicationEngine()
|
||||||
|
|
||||||
|
themeManager = ThemeManager(parent=app)
|
||||||
|
|
||||||
|
def onThemeManagerThemeChanged():
|
||||||
|
logger.debug("App palette changed")
|
||||||
|
app.setPalette(themeManager.qPalette) # pyright: ignore[reportArgumentType]
|
||||||
|
engine.rootContext().setContextProperty("appTheme", themeManager.qmlExposer)
|
||||||
|
|
||||||
|
onThemeManagerThemeChanged()
|
||||||
|
themeManager.themeChanged.connect(onThemeManagerThemeChanged)
|
||||||
|
|
||||||
|
QQuickStyle.setStyle("Fusion")
|
||||||
|
|
||||||
|
def onEngineObjectCreated(obj: QObject | None, objUrl: QUrl) -> None:
|
||||||
|
if obj is None:
|
||||||
|
logger.critical("rootObject is None! Exiting!")
|
||||||
|
QCoreApplication.exit(-1)
|
||||||
|
|
||||||
|
engine.objectCreated.connect(
|
||||||
|
onEngineObjectCreated,
|
||||||
|
Qt.ConnectionType.QueuedConnection,
|
||||||
|
)
|
||||||
|
|
||||||
|
engine.load("ui/qmls/App.qml")
|
||||||
|
|
||||||
|
rootObject = engine.rootObjects()[0]
|
||||||
|
ef = ThemeChangeEventFilter(themeManager=themeManager)
|
||||||
|
rootObject.installEventFilter(ef)
|
||||||
|
|
||||||
|
sys.exit(app.exec())
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
10
core/color.py
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
from PySide6.QtGui import QColor
|
||||||
|
|
||||||
|
|
||||||
|
def mixColor(source: QColor, mix: QColor, ratio: float = 0.5):
|
||||||
|
r = round((mix.red() - source.red()) * ratio + source.red())
|
||||||
|
g = round((mix.green() - source.green()) * ratio + source.green())
|
||||||
|
b = round((mix.blue() - source.blue()) * ratio + source.blue())
|
||||||
|
a = round((mix.alpha() - source.alpha()) * ratio + source.alpha())
|
||||||
|
|
||||||
|
return QColor(r, g, b, a)
|
||||||
12
core/database/__init__.py
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
from .conn import Database
|
||||||
|
from .init_checker import DatabaseInitCheckResult, check_db_init
|
||||||
|
from .utils import create_engine, db_path_to_sqlite_url, sqlite_url_to_db_path
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"check_db_init",
|
||||||
|
"create_engine",
|
||||||
|
"db_path_to_sqlite_url",
|
||||||
|
"Database",
|
||||||
|
"DatabaseInitCheckResult",
|
||||||
|
"sqlite_url_to_db_path",
|
||||||
|
]
|
||||||
31
core/database/conn.py
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from arcaea_offline.models import CalculatedPotential
|
||||||
|
from sqlalchemy import select
|
||||||
|
from sqlalchemy.orm import sessionmaker
|
||||||
|
|
||||||
|
from core.settings import SettingsKeys, settings
|
||||||
|
|
||||||
|
from .utils import create_engine, db_path_to_sqlite_url
|
||||||
|
|
||||||
|
|
||||||
|
class Database:
|
||||||
|
def __init__(self):
|
||||||
|
if settings.stringValue(SettingsKeys.General.DatabaseType) != "file":
|
||||||
|
raise ValueError("DatabaseType is not file")
|
||||||
|
|
||||||
|
db_path = settings.stringValue(SettingsKeys.General.DatabaseConn)
|
||||||
|
if not db_path:
|
||||||
|
raise ValueError("DatabaseConn is empty")
|
||||||
|
|
||||||
|
db_path = Path(db_path)
|
||||||
|
if not db_path.exists():
|
||||||
|
raise FileNotFoundError(f"{db_path} does not exist")
|
||||||
|
|
||||||
|
self.engine = create_engine(db_path_to_sqlite_url(db_path))
|
||||||
|
self.sessionmaker = sessionmaker(bind=self.engine)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def b30(self) -> float | None:
|
||||||
|
with self.sessionmaker() as session:
|
||||||
|
return session.scalar(select(CalculatedPotential.b30))
|
||||||
30
core/database/init_checker.py
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
from enum import Flag, auto
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from arcaea_offline.database import Database
|
||||||
|
|
||||||
|
from .utils import create_engine, db_path_to_sqlite_url
|
||||||
|
|
||||||
|
|
||||||
|
class DatabaseInitCheckResult(Flag):
|
||||||
|
NONE = 0
|
||||||
|
FILE_EXISTS = auto()
|
||||||
|
INITIALIZED = auto()
|
||||||
|
|
||||||
|
OK = FILE_EXISTS | INITIALIZED
|
||||||
|
|
||||||
|
|
||||||
|
def check_db_init(file: Path) -> DatabaseInitCheckResult:
|
||||||
|
flags = DatabaseInitCheckResult.NONE
|
||||||
|
|
||||||
|
if not file.exists():
|
||||||
|
return flags
|
||||||
|
|
||||||
|
flags |= DatabaseInitCheckResult.FILE_EXISTS
|
||||||
|
|
||||||
|
db_url = db_path_to_sqlite_url(file)
|
||||||
|
db = Database(create_engine(db_url))
|
||||||
|
if db.check_init():
|
||||||
|
flags |= DatabaseInitCheckResult.INITIALIZED
|
||||||
|
|
||||||
|
return flags
|
||||||
28
core/database/utils.py
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from PySide6.QtCore import QSysInfo, QUrl
|
||||||
|
from sqlalchemy import Engine
|
||||||
|
from sqlalchemy import create_engine as sa_create_engine
|
||||||
|
from sqlalchemy.pool import NullPool, Pool
|
||||||
|
|
||||||
|
|
||||||
|
def db_path_to_sqlite_url(file: Path) -> QUrl:
|
||||||
|
kernelType = QSysInfo.kernelType()
|
||||||
|
|
||||||
|
# the slash count varies depending on the kernel
|
||||||
|
# https://docs.sqlalchemy.org/en/20/core/engines.html#sqlite
|
||||||
|
uri = file.resolve().as_uri()
|
||||||
|
if kernelType == "winnt":
|
||||||
|
return QUrl(uri.replace("file://", "sqlite://"))
|
||||||
|
else:
|
||||||
|
return QUrl(uri.replace("file://", "sqlite:///"))
|
||||||
|
|
||||||
|
|
||||||
|
def sqlite_url_to_db_path(url: str) -> Path:
|
||||||
|
db_file_url = url.replace("sqlite://", "file://")
|
||||||
|
return Path(QUrl(db_file_url).toLocalFile()).resolve()
|
||||||
|
|
||||||
|
|
||||||
|
def create_engine(_url: str | QUrl, pool: type[Pool] = NullPool) -> Engine:
|
||||||
|
url = _url.toString() if isinstance(_url, QUrl) else _url
|
||||||
|
return sa_create_engine(url, poolclass=pool)
|
||||||
5
core/settings/__init__.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
from .base import Settings, settings
|
||||||
|
from .keys import SettingsKeys
|
||||||
|
from .values import SettingsValues
|
||||||
|
|
||||||
|
__all__ = ["settings", "Settings", "SettingsKeys", "SettingsValues"]
|
||||||
44
core/settings/base.py
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import sys
|
||||||
|
from enum import Enum
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from PySide6.QtCore import QFileInfo, QSettings, Signal
|
||||||
|
|
||||||
|
from core.singleton import QSingleton
|
||||||
|
|
||||||
|
__all__ = ["Settings"]
|
||||||
|
|
||||||
|
TSettingsKey = str | Enum
|
||||||
|
|
||||||
|
|
||||||
|
class Settings(QSettings, metaclass=QSingleton):
|
||||||
|
updated = Signal(str)
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(
|
||||||
|
QFileInfo(sys.argv[0]).dir().absoluteFilePath("arcaea_offline.ini"),
|
||||||
|
QSettings.Format.IniFormat,
|
||||||
|
parent,
|
||||||
|
)
|
||||||
|
|
||||||
|
def __settingsKey(self, key: TSettingsKey) -> str:
|
||||||
|
if isinstance(key, Enum):
|
||||||
|
return self.__settingsKey(key.value)
|
||||||
|
|
||||||
|
if isinstance(key, str):
|
||||||
|
return key
|
||||||
|
|
||||||
|
raise TypeError(f"{key!r} is not a valid key")
|
||||||
|
|
||||||
|
def setValue(self, key: TSettingsKey, value: Any) -> None:
|
||||||
|
_key = self.__settingsKey(key)
|
||||||
|
|
||||||
|
super().setValue(_key, value)
|
||||||
|
self.updated.emit(_key)
|
||||||
|
|
||||||
|
def stringValue(self, key: TSettingsKey) -> str | None:
|
||||||
|
_key = self.__settingsKey(key)
|
||||||
|
return self.value(_key, None, type=str)
|
||||||
|
|
||||||
|
|
||||||
|
settings = Settings()
|
||||||
27
core/settings/keys.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
from dataclasses import dataclass
|
||||||
|
from enum import StrEnum
|
||||||
|
|
||||||
|
|
||||||
|
class _General(StrEnum):
|
||||||
|
Language = "Language"
|
||||||
|
DatabaseType = "DatabaseType"
|
||||||
|
DatabaseConn = "DatabaseConn"
|
||||||
|
|
||||||
|
|
||||||
|
class _Ocr(StrEnum):
|
||||||
|
KnnModelFile = "Ocr/KnnModelFile"
|
||||||
|
B30KnnModelFile = "Ocr/B30KnnModelFile"
|
||||||
|
PhashDatabaseFile = "Ocr/PHashDatabaseFile"
|
||||||
|
DateSource = "Ocr/DateSource"
|
||||||
|
|
||||||
|
|
||||||
|
class _Andreal(StrEnum):
|
||||||
|
Folder = "Andreal/AndrealFolder"
|
||||||
|
Executable = "Andreal/AndrealExecutable"
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class SettingsKeys:
|
||||||
|
General = _General
|
||||||
|
Ocr = _Ocr
|
||||||
|
Andreal = _Andreal
|
||||||
23
core/settings/values.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
from dataclasses import dataclass
|
||||||
|
from enum import StrEnum
|
||||||
|
|
||||||
|
|
||||||
|
class GeneralDatabaseType(StrEnum):
|
||||||
|
FILE = "file"
|
||||||
|
URL = "url"
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class _Ocr_ScoreDateSource:
|
||||||
|
FileCreated: str = "FileCreated"
|
||||||
|
FileLastModified: str = "FileLastModified"
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class _Ocr:
|
||||||
|
DateSource = _Ocr_ScoreDateSource()
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class SettingsValues:
|
||||||
|
Ocr = _Ocr()
|
||||||
@ -14,5 +14,5 @@ class Singleton(type, Generic[T]):
|
|||||||
return cls._instance
|
return cls._instance
|
||||||
|
|
||||||
|
|
||||||
class QObjectSingleton(type(QObject), Singleton):
|
class QSingleton(type(QObject), Singleton):
|
||||||
pass
|
pass
|
||||||
34
index.py
@ -9,25 +9,21 @@ from PySide6.QtCore import QCoreApplication, QLocale
|
|||||||
from PySide6.QtGui import QFontDatabase, QIcon
|
from PySide6.QtGui import QFontDatabase, QIcon
|
||||||
from PySide6.QtWidgets import QApplication, QDialog, QMessageBox
|
from PySide6.QtWidgets import QApplication, QDialog, QMessageBox
|
||||||
|
|
||||||
import ui.resources.resources_rc
|
import ui.resources.resources_rc # noqa: F401
|
||||||
|
from core.settings import SettingsKeys, settings
|
||||||
from ui.extends.shared.language import changeAppLanguage
|
from ui.extends.shared.language import changeAppLanguage
|
||||||
from ui.extends.shared.settings import Settings
|
|
||||||
from ui.implements.mainwindow import MainWindow
|
from ui.implements.mainwindow import MainWindow
|
||||||
from ui.startup.databaseChecker import DatabaseChecker, DatabaseCheckerResult
|
from ui.startup.databaseChecker import DatabaseChecker, DatabaseInitCheckResult
|
||||||
|
|
||||||
rootLogger = logging.getLogger("root")
|
rootLogger = logging.getLogger("root")
|
||||||
rootLogger.setLevel(logging.DEBUG)
|
rootLogger.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
rootLoggerFormatter = logging.Formatter(
|
|
||||||
"[{levelname}]{asctime}|{name}: {msg}", "%m-%d %H:%M:%S", "{"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def handle_exception(exc_type, exc_value, exc_traceback):
|
def handle_exception(exc_type, exc_value, exc_traceback):
|
||||||
if issubclass(exc_type, KeyboardInterrupt):
|
sys.__excepthook__(exc_type, exc_value, exc_traceback)
|
||||||
sys.__excepthook__(exc_type, exc_value, exc_traceback)
|
|
||||||
return
|
|
||||||
|
|
||||||
|
if issubclass(exc_type, KeyboardInterrupt):
|
||||||
|
return
|
||||||
rootLogger.critical(
|
rootLogger.critical(
|
||||||
"Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback)
|
"Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback)
|
||||||
)
|
)
|
||||||
@ -46,6 +42,11 @@ if __name__ == "__main__":
|
|||||||
ymd = now.strftime("%Y%m%d")
|
ymd = now.strftime("%Y%m%d")
|
||||||
hms = now.strftime("%H%M%S")
|
hms = now.strftime("%H%M%S")
|
||||||
|
|
||||||
|
rootLoggerFormatter = logging.Formatter(
|
||||||
|
"[%(asctime)s/%(levelname)s] %(name)s (%(tag)s): %(message)s",
|
||||||
|
"%m-%d %H:%M:%S",
|
||||||
|
defaults={"tag": "/"},
|
||||||
|
)
|
||||||
rootLoggerFileHandler = logging.FileHandler(
|
rootLoggerFileHandler = logging.FileHandler(
|
||||||
str(logFolder / f"arcaea-offline-pyside-ui-{ymd}-{hms}_debug.log"),
|
str(logFolder / f"arcaea-offline-pyside-ui-{ymd}-{hms}_debug.log"),
|
||||||
encoding="utf-8",
|
encoding="utf-8",
|
||||||
@ -59,18 +60,21 @@ if __name__ == "__main__":
|
|||||||
rootLogger.addHandler(rootLoggerStdOutHandler)
|
rootLogger.addHandler(rootLoggerStdOutHandler)
|
||||||
|
|
||||||
app = QApplication(sys.argv)
|
app = QApplication(sys.argv)
|
||||||
locale = (
|
settingsLanguage = settings.stringValue(SettingsKeys.General.Language)
|
||||||
QLocale(Settings().language()) if Settings().language() else QLocale.system()
|
locale = QLocale(settingsLanguage) if settingsLanguage else QLocale.system()
|
||||||
)
|
|
||||||
changeAppLanguage(locale)
|
changeAppLanguage(locale)
|
||||||
|
|
||||||
QFontDatabase.addApplicationFont(":/fonts/GeosansLight.ttf")
|
QFontDatabase.addApplicationFont(":/fonts/GeosansLight.ttf")
|
||||||
|
|
||||||
databaseChecker = DatabaseChecker()
|
databaseChecker = DatabaseChecker()
|
||||||
databaseChecker.setWindowIcon(QIcon(":/images/icon.png"))
|
databaseChecker.setWindowIcon(QIcon(":/images/icon.png"))
|
||||||
databaseCheckResult = databaseChecker.confirmDb() if Settings().databaseUrl() else 0
|
databaseCheckResult = (
|
||||||
|
databaseChecker.confirmDb()
|
||||||
|
if settings.stringValue(SettingsKeys.General.DatabaseUrl)
|
||||||
|
else DatabaseInitCheckResult.NONE
|
||||||
|
)
|
||||||
|
|
||||||
if not databaseCheckResult & DatabaseCheckerResult.Initted:
|
if not databaseCheckResult & DatabaseInitCheckResult.INITIALIZED:
|
||||||
result = databaseChecker.exec()
|
result = databaseChecker.exec()
|
||||||
|
|
||||||
if result == QDialog.DialogCode.Accepted:
|
if result == QDialog.DialogCode.Accepted:
|
||||||
|
|||||||
@ -1,42 +1,84 @@
|
|||||||
[build-system]
|
|
||||||
requires = ["setuptools>=61.0"]
|
|
||||||
build-backend = "setuptools.build_meta"
|
|
||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "arcaea-offline-pyside-ui"
|
name = "arcaea-offline-pyside-ui"
|
||||||
version = "0.1.0"
|
version = "0.3.9"
|
||||||
authors = [{ name = "283375", email = "log_283375@163.com" }]
|
authors = [{ name = "283375", email = "log_283375@163.com" }]
|
||||||
description = "No description."
|
description = "No description."
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.9"
|
requires-python = ">=3.9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arcaea-offline==0.2.1",
|
"arcaea-offline==0.2.2",
|
||||||
"arcaea-offline-ocr==0.0.97",
|
"arcaea-offline-ocr==0.0.99",
|
||||||
"exif==1.6.0",
|
|
||||||
"PySide6==6.5.2",
|
"structlog~=25.4",
|
||||||
|
"colorama~=0.4.6",
|
||||||
|
"rich~=14.2",
|
||||||
|
|
||||||
|
"exif~=1.6.0",
|
||||||
|
"PySide6==6.10.0",
|
||||||
|
"Pillow~=10.1.0",
|
||||||
|
"materialyoucolor~=2.0.10",
|
||||||
]
|
]
|
||||||
classifiers = [
|
classifiers = [
|
||||||
"Development Status :: 3 - Alpha",
|
"Development Status :: 3 - Alpha",
|
||||||
"Programming Language :: Python :: 3",
|
"Programming Language :: Python :: 3",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[project.optional-dependencies]
|
||||||
|
dev = [
|
||||||
|
"ruff>=0.14.2",
|
||||||
|
"pre-commit>=4.3.0",
|
||||||
|
|
||||||
|
"imageio",
|
||||||
|
"Nuitka~=2.7.6",
|
||||||
|
]
|
||||||
|
|
||||||
[project.urls]
|
[project.urls]
|
||||||
"Homepage" = "https://github.com/283375/arcaea-offline-pyside-ui"
|
"Homepage" = "https://github.com/ArcaeaOffline/client-pyside6"
|
||||||
"Bug Tracker" = "https://github.com/283375/arcaea-offline-pyside-ui/issues"
|
"Bug Tracker" = "https://github.com/ArcaeaOffline/client-pyside6/issues"
|
||||||
|
|
||||||
[tool.black]
|
[tool.ruff]
|
||||||
force-exclude = '''
|
exclude = ["*_ui.py", "*_rc.py"]
|
||||||
(
|
|
||||||
ui/designer
|
|
||||||
| .*_ui.py
|
|
||||||
| .*_rc.py
|
|
||||||
)
|
|
||||||
'''
|
|
||||||
|
|
||||||
[tool.isort]
|
[tool.ruff.lint]
|
||||||
profile = "black"
|
# Full list: https://docs.astral.sh/ruff/rules
|
||||||
extend_skip = ["ui/designer"]
|
select = [
|
||||||
extend_skip_glob = ["*_ui.py", "*_rc.py"]
|
"E", # pycodestyle (Error)
|
||||||
|
"W", # pycodestyle (Warning)
|
||||||
|
"F", # pyflakes
|
||||||
|
"I", # isort
|
||||||
|
"PL", # pylint
|
||||||
|
"N", # pep8-naming
|
||||||
|
"A", # flake8-builtins
|
||||||
|
"DTZ", # flake8-datetimez
|
||||||
|
"LOG", # flake8-logging
|
||||||
|
"Q", # flake8-quotes
|
||||||
|
"G", # flake8-logging-format
|
||||||
|
"PIE", # flake8-pie
|
||||||
|
"PT", # flake8-pytest-style
|
||||||
|
]
|
||||||
|
ignore = [
|
||||||
|
"E501", # line-too-long
|
||||||
|
"N802", # invalid-function-name
|
||||||
|
"N803", # invalid-argument-name
|
||||||
|
"N806", # non-lowercase-variable-in-function
|
||||||
|
"N815", # mixed-case-variable-in-class-scope
|
||||||
|
"N816", # mixed-case-variable-in-global-scope
|
||||||
|
"N999", # invalid-module-name
|
||||||
|
]
|
||||||
|
|
||||||
[tool.pyright]
|
[tool.pyright]
|
||||||
ignore = ["**/__debug*.*"]
|
ignore = ["**/__debug*.*"]
|
||||||
|
|
||||||
|
[tool.pyside6-project]
|
||||||
|
files = [
|
||||||
|
"app.py",
|
||||||
|
"ui/qmls/App.qml",
|
||||||
|
"ui/qmls/AppMain.qml",
|
||||||
|
|
||||||
|
"ui/qmls/Components/PlayResultDelegate.qml",
|
||||||
|
|
||||||
|
"ui/viewmodels/overview.py",
|
||||||
|
"ui/qmls/Overview.qml",
|
||||||
|
|
||||||
|
"ui/qmls/404.qml",
|
||||||
|
]
|
||||||
|
|||||||
@ -1,4 +0,0 @@
|
|||||||
black == 23.7.0
|
|
||||||
isort == 5.12.0
|
|
||||||
imageio==2.31.4
|
|
||||||
Nuitka==1.8.4
|
|
||||||
@ -1,4 +0,0 @@
|
|||||||
arcaea-offline==0.2.1
|
|
||||||
arcaea-offline-ocr==0.0.97
|
|
||||||
exif==1.6.0
|
|
||||||
PySide6==6.5.2
|
|
||||||
@ -22,39 +22,6 @@
|
|||||||
<string>queue.title</string>
|
<string>queue.title</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
<item>
|
|
||||||
<widget class="QGroupBox" name="groupBox">
|
|
||||||
<property name="title">
|
|
||||||
<string>iccOptionsGroupBox</string>
|
|
||||||
</property>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
|
||||||
<item>
|
|
||||||
<widget class="QRadioButton" name="iccUseQtRadioButton">
|
|
||||||
<property name="text">
|
|
||||||
<string>icc.useQt</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QRadioButton" name="iccUsePILRadioButton">
|
|
||||||
<property name="text">
|
|
||||||
<string>icc.usePIL</string>
|
|
||||||
</property>
|
|
||||||
<property name="checked">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QRadioButton" name="iccTryFixRadioButton">
|
|
||||||
<property name="text">
|
|
||||||
<string>icc.tryFix</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="ocr_addImageButton">
|
<widget class="QPushButton" name="ocr_addImageButton">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@ -95,6 +62,13 @@
|
|||||||
</property>
|
</property>
|
||||||
</spacer>
|
</spacer>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="optionsDialogButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>queue.optionsButton</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="ocr_startButton">
|
<widget class="QPushButton" name="ocr_startButton">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
|||||||
140
ui/designer/components/ocrQueueOptionsDialog.ui
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>OcrQueueOptionsDialog</class>
|
||||||
|
<widget class="QDialog" name="OcrQueueOptionsDialog">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>331</width>
|
||||||
|
<height>157</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>OCR Options</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="groupBox">
|
||||||
|
<property name="title">
|
||||||
|
<string>iccOptionsGroupBox</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="iccUseQtRadioButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>icc.useQt</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="iccUsePILRadioButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>icc.usePIL</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="iccTryFixRadioButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>icc.tryFix</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="groupBox_2">
|
||||||
|
<property name="title">
|
||||||
|
<string>dateOptionsGroupBox</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="dateReadFromExifCheckbox">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>date.readFromExif</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="dateUseCreationDateRadioButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>date.useCreationDate</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="dateUseModifyDateRadioButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>date.useModifyDate</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>accepted()</signal>
|
||||||
|
<receiver>OcrQueueOptionsDialog</receiver>
|
||||||
|
<slot>accept()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>248</x>
|
||||||
|
<y>254</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>157</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>rejected()</signal>
|
||||||
|
<receiver>OcrQueueOptionsDialog</receiver>
|
||||||
|
<slot>reject()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>316</x>
|
||||||
|
<y>260</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>286</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
||||||
107
ui/designer/components/ocrQueueOptionsDialog_ui.py
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
## Form generated from reading UI file 'ocrQueueOptionsDialog.ui'
|
||||||
|
##
|
||||||
|
## Created by: Qt User Interface Compiler version 6.5.2
|
||||||
|
##
|
||||||
|
## WARNING! All changes made in this file will be lost when recompiling UI file!
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
|
||||||
|
QMetaObject, QObject, QPoint, QRect,
|
||||||
|
QSize, QTime, QUrl, Qt)
|
||||||
|
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
|
||||||
|
QFont, QFontDatabase, QGradient, QIcon,
|
||||||
|
QImage, QKeySequence, QLinearGradient, QPainter,
|
||||||
|
QPalette, QPixmap, QRadialGradient, QTransform)
|
||||||
|
from PySide6.QtWidgets import (QAbstractButton, QApplication, QCheckBox, QDialog,
|
||||||
|
QDialogButtonBox, QGroupBox, QHBoxLayout, QRadioButton,
|
||||||
|
QSizePolicy, QVBoxLayout, QWidget)
|
||||||
|
|
||||||
|
class Ui_OcrQueueOptionsDialog(object):
|
||||||
|
def setupUi(self, OcrQueueOptionsDialog):
|
||||||
|
if not OcrQueueOptionsDialog.objectName():
|
||||||
|
OcrQueueOptionsDialog.setObjectName(u"OcrQueueOptionsDialog")
|
||||||
|
OcrQueueOptionsDialog.resize(331, 157)
|
||||||
|
self.verticalLayout = QVBoxLayout(OcrQueueOptionsDialog)
|
||||||
|
self.verticalLayout.setObjectName(u"verticalLayout")
|
||||||
|
self.horizontalLayout = QHBoxLayout()
|
||||||
|
self.horizontalLayout.setObjectName(u"horizontalLayout")
|
||||||
|
self.groupBox = QGroupBox(OcrQueueOptionsDialog)
|
||||||
|
self.groupBox.setObjectName(u"groupBox")
|
||||||
|
self.verticalLayout_2 = QVBoxLayout(self.groupBox)
|
||||||
|
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
|
||||||
|
self.iccUseQtRadioButton = QRadioButton(self.groupBox)
|
||||||
|
self.iccUseQtRadioButton.setObjectName(u"iccUseQtRadioButton")
|
||||||
|
|
||||||
|
self.verticalLayout_2.addWidget(self.iccUseQtRadioButton)
|
||||||
|
|
||||||
|
self.iccUsePILRadioButton = QRadioButton(self.groupBox)
|
||||||
|
self.iccUsePILRadioButton.setObjectName(u"iccUsePILRadioButton")
|
||||||
|
self.iccUsePILRadioButton.setChecked(True)
|
||||||
|
|
||||||
|
self.verticalLayout_2.addWidget(self.iccUsePILRadioButton)
|
||||||
|
|
||||||
|
self.iccTryFixRadioButton = QRadioButton(self.groupBox)
|
||||||
|
self.iccTryFixRadioButton.setObjectName(u"iccTryFixRadioButton")
|
||||||
|
|
||||||
|
self.verticalLayout_2.addWidget(self.iccTryFixRadioButton)
|
||||||
|
|
||||||
|
|
||||||
|
self.horizontalLayout.addWidget(self.groupBox)
|
||||||
|
|
||||||
|
self.groupBox_2 = QGroupBox(OcrQueueOptionsDialog)
|
||||||
|
self.groupBox_2.setObjectName(u"groupBox_2")
|
||||||
|
self.verticalLayout_3 = QVBoxLayout(self.groupBox_2)
|
||||||
|
self.verticalLayout_3.setObjectName(u"verticalLayout_3")
|
||||||
|
self.dateReadFromExifCheckbox = QCheckBox(self.groupBox_2)
|
||||||
|
self.dateReadFromExifCheckbox.setObjectName(u"dateReadFromExifCheckbox")
|
||||||
|
self.dateReadFromExifCheckbox.setEnabled(False)
|
||||||
|
self.dateReadFromExifCheckbox.setChecked(True)
|
||||||
|
|
||||||
|
self.verticalLayout_3.addWidget(self.dateReadFromExifCheckbox)
|
||||||
|
|
||||||
|
self.dateUseCreationDateRadioButton = QRadioButton(self.groupBox_2)
|
||||||
|
self.dateUseCreationDateRadioButton.setObjectName(u"dateUseCreationDateRadioButton")
|
||||||
|
self.dateUseCreationDateRadioButton.setChecked(True)
|
||||||
|
|
||||||
|
self.verticalLayout_3.addWidget(self.dateUseCreationDateRadioButton)
|
||||||
|
|
||||||
|
self.dateUseModifyDateRadioButton = QRadioButton(self.groupBox_2)
|
||||||
|
self.dateUseModifyDateRadioButton.setObjectName(u"dateUseModifyDateRadioButton")
|
||||||
|
|
||||||
|
self.verticalLayout_3.addWidget(self.dateUseModifyDateRadioButton)
|
||||||
|
|
||||||
|
|
||||||
|
self.horizontalLayout.addWidget(self.groupBox_2)
|
||||||
|
|
||||||
|
|
||||||
|
self.verticalLayout.addLayout(self.horizontalLayout)
|
||||||
|
|
||||||
|
self.buttonBox = QDialogButtonBox(OcrQueueOptionsDialog)
|
||||||
|
self.buttonBox.setObjectName(u"buttonBox")
|
||||||
|
self.buttonBox.setOrientation(Qt.Horizontal)
|
||||||
|
self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel|QDialogButtonBox.Ok)
|
||||||
|
|
||||||
|
self.verticalLayout.addWidget(self.buttonBox)
|
||||||
|
|
||||||
|
|
||||||
|
self.retranslateUi(OcrQueueOptionsDialog)
|
||||||
|
self.buttonBox.accepted.connect(OcrQueueOptionsDialog.accept)
|
||||||
|
self.buttonBox.rejected.connect(OcrQueueOptionsDialog.reject)
|
||||||
|
|
||||||
|
QMetaObject.connectSlotsByName(OcrQueueOptionsDialog)
|
||||||
|
# setupUi
|
||||||
|
|
||||||
|
def retranslateUi(self, OcrQueueOptionsDialog):
|
||||||
|
OcrQueueOptionsDialog.setWindowTitle(QCoreApplication.translate("OcrQueueOptionsDialog", u"OCR Options", None))
|
||||||
|
self.groupBox.setTitle(QCoreApplication.translate("OcrQueueOptionsDialog", u"iccOptionsGroupBox", None))
|
||||||
|
self.iccUseQtRadioButton.setText(QCoreApplication.translate("OcrQueueOptionsDialog", u"icc.useQt", None))
|
||||||
|
self.iccUsePILRadioButton.setText(QCoreApplication.translate("OcrQueueOptionsDialog", u"icc.usePIL", None))
|
||||||
|
self.iccTryFixRadioButton.setText(QCoreApplication.translate("OcrQueueOptionsDialog", u"icc.tryFix", None))
|
||||||
|
self.groupBox_2.setTitle(QCoreApplication.translate("OcrQueueOptionsDialog", u"dateOptionsGroupBox", None))
|
||||||
|
self.dateReadFromExifCheckbox.setText(QCoreApplication.translate("OcrQueueOptionsDialog", u"date.readFromExif", None))
|
||||||
|
self.dateUseCreationDateRadioButton.setText(QCoreApplication.translate("OcrQueueOptionsDialog", u"date.useCreationDate", None))
|
||||||
|
self.dateUseModifyDateRadioButton.setText(QCoreApplication.translate("OcrQueueOptionsDialog", u"date.useModifyDate", None))
|
||||||
|
# retranslateUi
|
||||||
@ -17,8 +17,8 @@ from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
|
|||||||
QPalette, QPixmap, QRadialGradient, QTransform)
|
QPalette, QPixmap, QRadialGradient, QTransform)
|
||||||
from PySide6.QtWidgets import (QAbstractItemView, QApplication, QCheckBox, QGroupBox,
|
from PySide6.QtWidgets import (QAbstractItemView, QApplication, QCheckBox, QGroupBox,
|
||||||
QHBoxLayout, QHeaderView, QLabel, QProgressBar,
|
QHBoxLayout, QHeaderView, QLabel, QProgressBar,
|
||||||
QPushButton, QRadioButton, QSizePolicy, QSpacerItem,
|
QPushButton, QSizePolicy, QSpacerItem, QTableView,
|
||||||
QTableView, QVBoxLayout, QWidget)
|
QVBoxLayout, QWidget)
|
||||||
|
|
||||||
class Ui_OcrQueue(object):
|
class Ui_OcrQueue(object):
|
||||||
def setupUi(self, OcrQueue):
|
def setupUi(self, OcrQueue):
|
||||||
@ -34,29 +34,6 @@ class Ui_OcrQueue(object):
|
|||||||
self.groupBox_3.setObjectName(u"groupBox_3")
|
self.groupBox_3.setObjectName(u"groupBox_3")
|
||||||
self.verticalLayout_2 = QVBoxLayout(self.groupBox_3)
|
self.verticalLayout_2 = QVBoxLayout(self.groupBox_3)
|
||||||
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
|
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
|
||||||
self.groupBox = QGroupBox(self.groupBox_3)
|
|
||||||
self.groupBox.setObjectName(u"groupBox")
|
|
||||||
self.verticalLayout = QVBoxLayout(self.groupBox)
|
|
||||||
self.verticalLayout.setObjectName(u"verticalLayout")
|
|
||||||
self.iccUseQtRadioButton = QRadioButton(self.groupBox)
|
|
||||||
self.iccUseQtRadioButton.setObjectName(u"iccUseQtRadioButton")
|
|
||||||
|
|
||||||
self.verticalLayout.addWidget(self.iccUseQtRadioButton)
|
|
||||||
|
|
||||||
self.iccUsePILRadioButton = QRadioButton(self.groupBox)
|
|
||||||
self.iccUsePILRadioButton.setObjectName(u"iccUsePILRadioButton")
|
|
||||||
self.iccUsePILRadioButton.setChecked(True)
|
|
||||||
|
|
||||||
self.verticalLayout.addWidget(self.iccUsePILRadioButton)
|
|
||||||
|
|
||||||
self.iccTryFixRadioButton = QRadioButton(self.groupBox)
|
|
||||||
self.iccTryFixRadioButton.setObjectName(u"iccTryFixRadioButton")
|
|
||||||
|
|
||||||
self.verticalLayout.addWidget(self.iccTryFixRadioButton)
|
|
||||||
|
|
||||||
|
|
||||||
self.verticalLayout_2.addWidget(self.groupBox)
|
|
||||||
|
|
||||||
self.ocr_addImageButton = QPushButton(self.groupBox_3)
|
self.ocr_addImageButton = QPushButton(self.groupBox_3)
|
||||||
self.ocr_addImageButton.setObjectName(u"ocr_addImageButton")
|
self.ocr_addImageButton.setObjectName(u"ocr_addImageButton")
|
||||||
|
|
||||||
@ -78,6 +55,11 @@ class Ui_OcrQueue(object):
|
|||||||
|
|
||||||
self.verticalLayout_2.addItem(self.verticalSpacer)
|
self.verticalLayout_2.addItem(self.verticalSpacer)
|
||||||
|
|
||||||
|
self.optionsDialogButton = QPushButton(self.groupBox_3)
|
||||||
|
self.optionsDialogButton.setObjectName(u"optionsDialogButton")
|
||||||
|
|
||||||
|
self.verticalLayout_2.addWidget(self.optionsDialogButton)
|
||||||
|
|
||||||
self.ocr_startButton = QPushButton(self.groupBox_3)
|
self.ocr_startButton = QPushButton(self.groupBox_3)
|
||||||
self.ocr_startButton.setObjectName(u"ocr_startButton")
|
self.ocr_startButton.setObjectName(u"ocr_startButton")
|
||||||
|
|
||||||
@ -154,13 +136,10 @@ class Ui_OcrQueue(object):
|
|||||||
|
|
||||||
def retranslateUi(self, OcrQueue):
|
def retranslateUi(self, OcrQueue):
|
||||||
self.groupBox_3.setTitle(QCoreApplication.translate("OcrQueue", u"queue.title", None))
|
self.groupBox_3.setTitle(QCoreApplication.translate("OcrQueue", u"queue.title", None))
|
||||||
self.groupBox.setTitle(QCoreApplication.translate("OcrQueue", u"iccOptionsGroupBox", None))
|
|
||||||
self.iccUseQtRadioButton.setText(QCoreApplication.translate("OcrQueue", u"icc.useQt", None))
|
|
||||||
self.iccUsePILRadioButton.setText(QCoreApplication.translate("OcrQueue", u"icc.usePIL", None))
|
|
||||||
self.iccTryFixRadioButton.setText(QCoreApplication.translate("OcrQueue", u"icc.tryFix", None))
|
|
||||||
self.ocr_addImageButton.setText(QCoreApplication.translate("OcrQueue", u"queue.addImageButton", None))
|
self.ocr_addImageButton.setText(QCoreApplication.translate("OcrQueue", u"queue.addImageButton", None))
|
||||||
self.ocr_removeSelectedButton.setText(QCoreApplication.translate("OcrQueue", u"queue.removeSelected", None))
|
self.ocr_removeSelectedButton.setText(QCoreApplication.translate("OcrQueue", u"queue.removeSelected", None))
|
||||||
self.ocr_removeAllButton.setText(QCoreApplication.translate("OcrQueue", u"queue.removeAll", None))
|
self.ocr_removeAllButton.setText(QCoreApplication.translate("OcrQueue", u"queue.removeAll", None))
|
||||||
|
self.optionsDialogButton.setText(QCoreApplication.translate("OcrQueue", u"queue.optionsButton", None))
|
||||||
self.ocr_startButton.setText(QCoreApplication.translate("OcrQueue", u"queue.startOcrButton", None))
|
self.ocr_startButton.setText(QCoreApplication.translate("OcrQueue", u"queue.startOcrButton", None))
|
||||||
self.groupBox_5.setTitle(QCoreApplication.translate("OcrQueue", u"results", None))
|
self.groupBox_5.setTitle(QCoreApplication.translate("OcrQueue", u"results", None))
|
||||||
self.ocr_acceptSelectedButton.setText(QCoreApplication.translate("OcrQueue", u"results.acceptSelectedButton", None))
|
self.ocr_acceptSelectedButton.setText(QCoreApplication.translate("OcrQueue", u"results.acceptSelectedButton", None))
|
||||||
@ -169,4 +148,3 @@ class Ui_OcrQueue(object):
|
|||||||
self.statusLabel.setText("")
|
self.statusLabel.setText("")
|
||||||
pass
|
pass
|
||||||
# retranslateUi
|
# retranslateUi
|
||||||
|
|
||||||
|
|||||||
@ -1,124 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>TabOverview</class>
|
|
||||||
<widget class="QWidget" name="TabOverview">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>696</width>
|
|
||||||
<height>509</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string notr="true">TabOverview</string>
|
|
||||||
</property>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
|
||||||
<item>
|
|
||||||
<widget class="QWidget" name="widget" native="true"/>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="databaseDescribeLabel">
|
|
||||||
<property name="text">
|
|
||||||
<string notr="true" extracomment="This database now have {} packs, {} songs, {} difficulties, {} chart info ({} complete) and {} scores.">...</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QWidget" name="widget_2" native="true">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
|
||||||
<item>
|
|
||||||
<widget class="QWidget" name="widget_3" native="true">
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="b30Label">
|
|
||||||
<property name="font">
|
|
||||||
<font>
|
|
||||||
<pointsize>30</pointsize>
|
|
||||||
</font>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string notr="true">0.00</string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignBottom|Qt::AlignHCenter</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label_2">
|
|
||||||
<property name="font">
|
|
||||||
<font>
|
|
||||||
<pointsize>20</pointsize>
|
|
||||||
</font>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string notr="true">B30</string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignHCenter|Qt::AlignTop</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QWidget" name="widget_4" native="true">
|
|
||||||
<property name="enabled">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="r10Label">
|
|
||||||
<property name="enabled">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="font">
|
|
||||||
<font>
|
|
||||||
<pointsize>30</pointsize>
|
|
||||||
</font>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string notr="true">--</string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignBottom|Qt::AlignHCenter</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label_4">
|
|
||||||
<property name="enabled">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="font">
|
|
||||||
<font>
|
|
||||||
<pointsize>20</pointsize>
|
|
||||||
</font>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string notr="true">R10</string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignHCenter|Qt::AlignTop</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
<resources/>
|
|
||||||
<connections/>
|
|
||||||
</ui>
|
|
||||||
@ -1,114 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
################################################################################
|
|
||||||
## Form generated from reading UI file 'tabOverview.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, QHBoxLayout, QLabel, QSizePolicy,
|
|
||||||
QVBoxLayout, QWidget)
|
|
||||||
|
|
||||||
class Ui_TabOverview(object):
|
|
||||||
def setupUi(self, TabOverview):
|
|
||||||
if not TabOverview.objectName():
|
|
||||||
TabOverview.setObjectName(u"TabOverview")
|
|
||||||
TabOverview.resize(696, 509)
|
|
||||||
TabOverview.setWindowTitle(u"TabOverview")
|
|
||||||
self.verticalLayout = QVBoxLayout(TabOverview)
|
|
||||||
self.verticalLayout.setObjectName(u"verticalLayout")
|
|
||||||
self.widget = QWidget(TabOverview)
|
|
||||||
self.widget.setObjectName(u"widget")
|
|
||||||
|
|
||||||
self.verticalLayout.addWidget(self.widget)
|
|
||||||
|
|
||||||
self.databaseDescribeLabel = QLabel(TabOverview)
|
|
||||||
self.databaseDescribeLabel.setObjectName(u"databaseDescribeLabel")
|
|
||||||
self.databaseDescribeLabel.setText(u"...")
|
|
||||||
|
|
||||||
self.verticalLayout.addWidget(self.databaseDescribeLabel)
|
|
||||||
|
|
||||||
self.widget_2 = QWidget(TabOverview)
|
|
||||||
self.widget_2.setObjectName(u"widget_2")
|
|
||||||
sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding)
|
|
||||||
sizePolicy.setHorizontalStretch(0)
|
|
||||||
sizePolicy.setVerticalStretch(0)
|
|
||||||
sizePolicy.setHeightForWidth(self.widget_2.sizePolicy().hasHeightForWidth())
|
|
||||||
self.widget_2.setSizePolicy(sizePolicy)
|
|
||||||
self.horizontalLayout = QHBoxLayout(self.widget_2)
|
|
||||||
self.horizontalLayout.setObjectName(u"horizontalLayout")
|
|
||||||
self.widget_3 = QWidget(self.widget_2)
|
|
||||||
self.widget_3.setObjectName(u"widget_3")
|
|
||||||
self.verticalLayout_2 = QVBoxLayout(self.widget_3)
|
|
||||||
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
|
|
||||||
self.b30Label = QLabel(self.widget_3)
|
|
||||||
self.b30Label.setObjectName(u"b30Label")
|
|
||||||
font = QFont()
|
|
||||||
font.setPointSize(30)
|
|
||||||
self.b30Label.setFont(font)
|
|
||||||
self.b30Label.setText(u"0.00")
|
|
||||||
self.b30Label.setAlignment(Qt.AlignBottom|Qt.AlignHCenter)
|
|
||||||
|
|
||||||
self.verticalLayout_2.addWidget(self.b30Label)
|
|
||||||
|
|
||||||
self.label_2 = QLabel(self.widget_3)
|
|
||||||
self.label_2.setObjectName(u"label_2")
|
|
||||||
font1 = QFont()
|
|
||||||
font1.setPointSize(20)
|
|
||||||
self.label_2.setFont(font1)
|
|
||||||
self.label_2.setText(u"B30")
|
|
||||||
self.label_2.setAlignment(Qt.AlignHCenter|Qt.AlignTop)
|
|
||||||
|
|
||||||
self.verticalLayout_2.addWidget(self.label_2)
|
|
||||||
|
|
||||||
|
|
||||||
self.horizontalLayout.addWidget(self.widget_3)
|
|
||||||
|
|
||||||
self.widget_4 = QWidget(self.widget_2)
|
|
||||||
self.widget_4.setObjectName(u"widget_4")
|
|
||||||
self.widget_4.setEnabled(False)
|
|
||||||
self.verticalLayout_3 = QVBoxLayout(self.widget_4)
|
|
||||||
self.verticalLayout_3.setObjectName(u"verticalLayout_3")
|
|
||||||
self.r10Label = QLabel(self.widget_4)
|
|
||||||
self.r10Label.setObjectName(u"r10Label")
|
|
||||||
self.r10Label.setEnabled(False)
|
|
||||||
self.r10Label.setFont(font)
|
|
||||||
self.r10Label.setText(u"--")
|
|
||||||
self.r10Label.setAlignment(Qt.AlignBottom|Qt.AlignHCenter)
|
|
||||||
|
|
||||||
self.verticalLayout_3.addWidget(self.r10Label)
|
|
||||||
|
|
||||||
self.label_4 = QLabel(self.widget_4)
|
|
||||||
self.label_4.setObjectName(u"label_4")
|
|
||||||
self.label_4.setEnabled(False)
|
|
||||||
self.label_4.setFont(font1)
|
|
||||||
self.label_4.setText(u"R10")
|
|
||||||
self.label_4.setAlignment(Qt.AlignHCenter|Qt.AlignTop)
|
|
||||||
|
|
||||||
self.verticalLayout_3.addWidget(self.label_4)
|
|
||||||
|
|
||||||
|
|
||||||
self.horizontalLayout.addWidget(self.widget_4)
|
|
||||||
|
|
||||||
|
|
||||||
self.verticalLayout.addWidget(self.widget_2)
|
|
||||||
|
|
||||||
|
|
||||||
self.retranslateUi(TabOverview)
|
|
||||||
|
|
||||||
QMetaObject.connectSlotsByName(TabOverview)
|
|
||||||
# setupUi
|
|
||||||
|
|
||||||
def retranslateUi(self, TabOverview):
|
|
||||||
pass
|
|
||||||
# retranslateUi
|
|
||||||
|
|
||||||
@ -5,7 +5,6 @@ from typing import Any, Callable, Optional, overload
|
|||||||
from arcaea_offline.calculate import calculate_score_range
|
from arcaea_offline.calculate import calculate_score_range
|
||||||
from arcaea_offline.database import Database
|
from arcaea_offline.database import Database
|
||||||
from arcaea_offline.models import Chart, Score
|
from arcaea_offline.models import Chart, Score
|
||||||
from arcaea_offline_ocr.b30.shared import B30OcrResultItem
|
|
||||||
from arcaea_offline_ocr.device.common import DeviceOcrResult
|
from arcaea_offline_ocr.device.common import DeviceOcrResult
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from PIL.ImageQt import ImageQt
|
from PIL.ImageQt import ImageQt
|
||||||
@ -140,7 +139,11 @@ class OcrQueueModel(QAbstractListModel):
|
|||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
f"{repr(self)} setData at row {index.row()} with role {role} and value {value} rejected."
|
"%r setData at row %d with role %d and value %s rejected!",
|
||||||
|
self,
|
||||||
|
index.row(),
|
||||||
|
role,
|
||||||
|
value,
|
||||||
)
|
)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -150,6 +153,7 @@ class OcrQueueModel(QAbstractListModel):
|
|||||||
|
|
||||||
@iccOption.setter
|
@iccOption.setter
|
||||||
def iccOption(self, opt: IccOption):
|
def iccOption(self, opt: IccOption):
|
||||||
|
logger.debug("ICC option changed to %s", opt)
|
||||||
self.__iccOption = opt
|
self.__iccOption = opt
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
@ -158,8 +162,7 @@ class OcrQueueModel(QAbstractListModel):
|
|||||||
image: str,
|
image: str,
|
||||||
runnable: OcrRunnable = None,
|
runnable: OcrRunnable = None,
|
||||||
process_func: Callable[[Optional[str], QImage, Any], Score] = None,
|
process_func: Callable[[Optional[str], QImage, Any], Score] = None,
|
||||||
):
|
): ...
|
||||||
...
|
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def addItem(
|
def addItem(
|
||||||
@ -167,8 +170,7 @@ class OcrQueueModel(QAbstractListModel):
|
|||||||
image: QImage,
|
image: QImage,
|
||||||
runnable: OcrRunnable = None,
|
runnable: OcrRunnable = None,
|
||||||
process_func: Callable[[Optional[str], QImage, Any], Score] = None,
|
process_func: Callable[[Optional[str], QImage, Any], Score] = None,
|
||||||
):
|
): ...
|
||||||
...
|
|
||||||
|
|
||||||
def addItem(
|
def addItem(
|
||||||
self,
|
self,
|
||||||
@ -178,7 +180,7 @@ class OcrQueueModel(QAbstractListModel):
|
|||||||
):
|
):
|
||||||
if isinstance(image, str):
|
if isinstance(image, str):
|
||||||
if image in self.imagePaths or not QFileInfo(image).exists():
|
if image in self.imagePaths or not QFileInfo(image).exists():
|
||||||
logger.warning(f"Attempting to add an invalid file {image}")
|
logger.warning("Attempting to add an invalid file %s", image)
|
||||||
return
|
return
|
||||||
imagePath = image
|
imagePath = image
|
||||||
if self.iccOption == IccOption.TryFix:
|
if self.iccOption == IccOption.TryFix:
|
||||||
@ -222,7 +224,7 @@ class OcrQueueModel(QAbstractListModel):
|
|||||||
index = self.index(row, 0)
|
index = self.index(row, 0)
|
||||||
imagePath: str = index.data(self.ImagePathRole)
|
imagePath: str = index.data(self.ImagePathRole)
|
||||||
qImage: QImage = index.data(self.ImageQImageRole)
|
qImage: QImage = index.data(self.ImageQImageRole)
|
||||||
logger.debug(f"update request: {result}@row{row}")
|
logger.debug("update request: %r@row%d", result, row)
|
||||||
processOcrResultFunc = index.data(self.ProcessOcrResultFuncRole)
|
processOcrResultFunc = index.data(self.ProcessOcrResultFuncRole)
|
||||||
|
|
||||||
chart, scoreInsert = processOcrResultFunc(imagePath, qImage, result)
|
chart, scoreInsert = processOcrResultFunc(imagePath, qImage, result)
|
||||||
@ -293,8 +295,8 @@ class OcrQueueModel(QAbstractListModel):
|
|||||||
self.__items.pop(row)
|
self.__items.pop(row)
|
||||||
self.endRemoveRows()
|
self.endRemoveRows()
|
||||||
return
|
return
|
||||||
except Exception as e:
|
except Exception:
|
||||||
logger.exception(f"Error accepting {repr(item)}")
|
logger.exception("Error accepting %r", item)
|
||||||
return
|
return
|
||||||
|
|
||||||
def acceptItems(self, __rows: list[int], ignoreValidate: bool = False):
|
def acceptItems(self, __rows: list[int], ignoreValidate: bool = False):
|
||||||
@ -343,13 +345,11 @@ class OcrQueueTableProxyModel(QAbstractTableModel):
|
|||||||
|
|
||||||
def retranslateHeaders(self):
|
def retranslateHeaders(self):
|
||||||
self.__horizontalHeaders = [
|
self.__horizontalHeaders = [
|
||||||
# fmt: off
|
|
||||||
QCoreApplication.translate("OcrTableModel", "horizontalHeader.title.select"),
|
QCoreApplication.translate("OcrTableModel", "horizontalHeader.title.select"),
|
||||||
QCoreApplication.translate("OcrTableModel", "horizontalHeader.title.imagePreview"),
|
QCoreApplication.translate("OcrTableModel", "horizontalHeader.title.imagePreview"),
|
||||||
QCoreApplication.translate("OcrTableModel", "horizontalHeader.title.chart"),
|
QCoreApplication.translate("OcrTableModel", "horizontalHeader.title.chart"),
|
||||||
QCoreApplication.translate("OcrTableModel", "horizontalHeader.title.score"),
|
QCoreApplication.translate("OcrTableModel", "horizontalHeader.title.score"),
|
||||||
# fmt: on
|
] # fmt: skip
|
||||||
]
|
|
||||||
|
|
||||||
def sourceModel(self) -> OcrQueueModel:
|
def sourceModel(self) -> OcrQueueModel:
|
||||||
return self.__sourceModel
|
return self.__sourceModel
|
||||||
|
|||||||
@ -1,39 +0,0 @@
|
|||||||
from arcaea_offline.database import Database
|
|
||||||
from arcaea_offline.models import Chart
|
|
||||||
from arcaea_offline.searcher import Searcher
|
|
||||||
from arcaea_offline.utils.rating import rating_class_to_short_text
|
|
||||||
from PySide6.QtCore import Qt
|
|
||||||
from PySide6.QtGui import QStandardItem, QStandardItemModel
|
|
||||||
|
|
||||||
|
|
||||||
class SearchCompleterModel(QStandardItemModel):
|
|
||||||
def __init__(self, parent=None):
|
|
||||||
super().__init__(parent)
|
|
||||||
self.searcher = Searcher()
|
|
||||||
self.db = Database()
|
|
||||||
|
|
||||||
def updateSearcherSongs(self):
|
|
||||||
with self.db.sessionmaker() as session:
|
|
||||||
self.searcher.import_songs(session)
|
|
||||||
|
|
||||||
def getSearchResult(self, kw: str):
|
|
||||||
self.clear()
|
|
||||||
|
|
||||||
songIds = self.searcher.search(kw)
|
|
||||||
|
|
||||||
charts: list[Chart] = []
|
|
||||||
for songId in songIds:
|
|
||||||
_charts = self.db.get_charts_by_song_id(songId)
|
|
||||||
_charts = sorted(_charts, key=lambda c: c.rating_class, reverse=True)
|
|
||||||
charts += _charts
|
|
||||||
|
|
||||||
for chart in charts:
|
|
||||||
displayText = (
|
|
||||||
f"{chart.title} [{rating_class_to_short_text(chart.rating_class)}]"
|
|
||||||
)
|
|
||||||
item = QStandardItem(kw)
|
|
||||||
item.setData(kw)
|
|
||||||
item.setData(displayText, Qt.ItemDataRole.UserRole + 75)
|
|
||||||
item.setData(f"{chart.song_id}, {chart.set}", Qt.ItemDataRole.UserRole + 76)
|
|
||||||
item.setData(chart, Qt.ItemDataRole.UserRole + 10)
|
|
||||||
self.appendRow(item)
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
from PySide6.QtGui import QColor
|
|
||||||
|
|
||||||
|
|
||||||
def mix_color(source_color: QColor, mix_color: QColor, mix_ratio: float = 0.5):
|
|
||||||
r = round((mix_color.red() - source_color.red()) * mix_ratio + source_color.red())
|
|
||||||
g = round(
|
|
||||||
(mix_color.green() - source_color.green()) * mix_ratio + source_color.green()
|
|
||||||
)
|
|
||||||
b = round(
|
|
||||||
(mix_color.blue() - source_color.blue()) * mix_ratio + source_color.blue()
|
|
||||||
)
|
|
||||||
a = round(
|
|
||||||
(mix_color.alpha() - source_color.alpha()) * mix_ratio + source_color.alpha()
|
|
||||||
)
|
|
||||||
|
|
||||||
return QColor(r, g, b, a)
|
|
||||||
@ -7,7 +7,7 @@ from typing import Literal, Optional, overload
|
|||||||
from arcaea_offline.models import Chart, Difficulty, Song
|
from arcaea_offline.models import Chart, Difficulty, Song
|
||||||
from PySide6.QtCore import QFile
|
from PySide6.QtCore import QFile
|
||||||
|
|
||||||
from .singleton import Singleton
|
from core.singleton import Singleton
|
||||||
|
|
||||||
TPartnerModifier = dict[str, Literal[0, 1, 2]]
|
TPartnerModifier = dict[str, Literal[0, 1, 2]]
|
||||||
|
|
||||||
@ -48,14 +48,12 @@ class Data(metaclass=Singleton):
|
|||||||
return self.dataPath / "Arcaea"
|
return self.dataPath / "Arcaea"
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def getJacketPath(self, chart: Chart, /) -> Path | None:
|
def getJacketPath(self, chart: Chart, /) -> Path | None: ...
|
||||||
...
|
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def getJacketPath(
|
def getJacketPath(
|
||||||
self, song: Song, difficulty: Optional[Difficulty] = None, /
|
self, song: Song, difficulty: Optional[Difficulty] = None, /
|
||||||
) -> Path | None:
|
) -> Path | None: ...
|
||||||
...
|
|
||||||
|
|
||||||
def getJacketPath(self, *args) -> Path | None:
|
def getJacketPath(self, *args) -> Path | None:
|
||||||
if isinstance(args[0], Chart):
|
if isinstance(args[0], Chart):
|
||||||
|
|||||||
@ -1,14 +1,4 @@
|
|||||||
from typing import Type
|
from PySide6.QtCore import QObject, Signal
|
||||||
|
|
||||||
from PySide6.QtCore import QObject, QUrl, Signal
|
|
||||||
from sqlalchemy import Engine
|
|
||||||
from sqlalchemy import create_engine as sa_create_engine
|
|
||||||
from sqlalchemy.pool import NullPool, Pool
|
|
||||||
|
|
||||||
|
|
||||||
def create_engine(_url: str | QUrl, pool: Type[Pool] = NullPool) -> Engine:
|
|
||||||
url = _url.toString() if isinstance(_url, QUrl) else _url
|
|
||||||
return sa_create_engine(url, poolclass=pool)
|
|
||||||
|
|
||||||
|
|
||||||
class DatabaseUpdateSignals(QObject):
|
class DatabaseUpdateSignals(QObject):
|
||||||
|
|||||||
@ -16,13 +16,11 @@ class DbB30TableModel(DbTableModel):
|
|||||||
|
|
||||||
def retranslateHeaders(self):
|
def retranslateHeaders(self):
|
||||||
self._horizontalHeaders = [
|
self._horizontalHeaders = [
|
||||||
# fmt: off
|
|
||||||
QCoreApplication.translate("DbB30TableModel", "horizontalHeader.id"),
|
QCoreApplication.translate("DbB30TableModel", "horizontalHeader.id"),
|
||||||
QCoreApplication.translate("DbB30TableModel", "horizontalHeader.chart"),
|
QCoreApplication.translate("DbB30TableModel", "horizontalHeader.chart"),
|
||||||
QCoreApplication.translate("DbB30TableModel", "horizontalHeader.score"),
|
QCoreApplication.translate("DbB30TableModel", "horizontalHeader.score"),
|
||||||
QCoreApplication.translate("DbB30TableModel", "horizontalHeader.potential"),
|
QCoreApplication.translate("DbB30TableModel", "horizontalHeader.potential"),
|
||||||
# fmt: on
|
] # fmt: skip
|
||||||
]
|
|
||||||
|
|
||||||
def syncDb(self):
|
def syncDb(self):
|
||||||
self.beginResetModel()
|
self.beginResetModel()
|
||||||
@ -39,7 +37,7 @@ class DbB30TableModel(DbTableModel):
|
|||||||
(ScoreBest.song_id == Chart.song_id)
|
(ScoreBest.song_id == Chart.song_id)
|
||||||
& (ScoreBest.rating_class == Chart.rating_class),
|
& (ScoreBest.rating_class == Chart.rating_class),
|
||||||
)
|
)
|
||||||
.order_by(ScoreBest.potential)
|
.order_by(ScoreBest.potential.desc())
|
||||||
.limit(50)
|
.limit(50)
|
||||||
.all()
|
.all()
|
||||||
)
|
)
|
||||||
|
|||||||
@ -24,13 +24,11 @@ class DbScoreTableModel(DbTableModel):
|
|||||||
|
|
||||||
def retranslateHeaders(self):
|
def retranslateHeaders(self):
|
||||||
self._horizontalHeaders = [
|
self._horizontalHeaders = [
|
||||||
# fmt: off
|
|
||||||
QCoreApplication.translate("DbScoreTableModel", "horizontalHeader.id"),
|
QCoreApplication.translate("DbScoreTableModel", "horizontalHeader.id"),
|
||||||
QCoreApplication.translate("DbScoreTableModel", "horizontalHeader.chart"),
|
QCoreApplication.translate("DbScoreTableModel", "horizontalHeader.chart"),
|
||||||
QCoreApplication.translate("DbScoreTableModel", "horizontalHeader.score"),
|
QCoreApplication.translate("DbScoreTableModel", "horizontalHeader.score"),
|
||||||
QCoreApplication.translate("DbScoreTableModel", "horizontalHeader.potential"),
|
QCoreApplication.translate("DbScoreTableModel", "horizontalHeader.potential"),
|
||||||
# fmt: on
|
] # fmt: skip
|
||||||
]
|
|
||||||
|
|
||||||
def syncDb(self):
|
def syncDb(self):
|
||||||
self.beginResetModel()
|
self.beginResetModel()
|
||||||
@ -154,7 +152,7 @@ class DbScoreTableModel(DbTableModel):
|
|||||||
self.syncDb()
|
self.syncDb()
|
||||||
return True
|
return True
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception(f"Table[Score]: Cannot remove row {row}")
|
logger.exception("Table[Score]: Cannot remove row %s", row)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def removeRow(self, row: int, parent=...):
|
def removeRow(self, row: int, parent=...):
|
||||||
|
|||||||
@ -1,111 +0,0 @@
|
|||||||
import sys
|
|
||||||
|
|
||||||
from PySide6.QtCore import QFileInfo, QSettings, Signal
|
|
||||||
|
|
||||||
from .singleton import QObjectSingleton
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
"LANGUAGE",
|
|
||||||
"DATABASE_URL",
|
|
||||||
"KNN_MODEL_FILE",
|
|
||||||
"B30_KNN_MODEL_FILE",
|
|
||||||
"PHASH_DATABASE_FILE",
|
|
||||||
"ANDREAL_FOLDER",
|
|
||||||
"ANDREAL_EXECUTABLE",
|
|
||||||
"Settings",
|
|
||||||
]
|
|
||||||
|
|
||||||
# a key without slashes will appear in the "General" section
|
|
||||||
# see https://doc.qt.io/qt-6/qsettings.html#Format-enum for details
|
|
||||||
LANGUAGE = "Language"
|
|
||||||
DATABASE_URL = "DatabaseUrl"
|
|
||||||
|
|
||||||
KNN_MODEL_FILE = "Ocr/KnnModelFile"
|
|
||||||
B30_KNN_MODEL_FILE = "Ocr/B30KnnModelFile"
|
|
||||||
PHASH_DATABASE_FILE = "Ocr/PHashDatabaseFile"
|
|
||||||
|
|
||||||
ANDREAL_FOLDER = "Andreal/AndrealFolder"
|
|
||||||
ANDREAL_EXECUTABLE = "Andreal/AndrealExecutable"
|
|
||||||
|
|
||||||
|
|
||||||
class Settings(QSettings, metaclass=QObjectSingleton):
|
|
||||||
updated = Signal(str)
|
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
|
||||||
super().__init__(
|
|
||||||
QFileInfo(sys.argv[0]).dir().absoluteFilePath("arcaea_offline.ini"),
|
|
||||||
QSettings.Format.IniFormat,
|
|
||||||
parent,
|
|
||||||
)
|
|
||||||
|
|
||||||
def setValue(self, key: str, value) -> None:
|
|
||||||
super().setValue(key, value)
|
|
||||||
self.updated.emit(key)
|
|
||||||
|
|
||||||
def _strItem(self, key: str) -> str | None:
|
|
||||||
return self.value(key, None, str)
|
|
||||||
|
|
||||||
def _setStrItem(self, key: str, value: str):
|
|
||||||
self.setValue(key, value)
|
|
||||||
self.sync()
|
|
||||||
|
|
||||||
def _resetStrItem(self, key: str):
|
|
||||||
self.setValue(key, None)
|
|
||||||
self.sync()
|
|
||||||
|
|
||||||
def language(self):
|
|
||||||
return self._strItem(LANGUAGE)
|
|
||||||
|
|
||||||
def setLanguage(self, value: str):
|
|
||||||
self._setStrItem(LANGUAGE, value)
|
|
||||||
|
|
||||||
def databaseUrl(self):
|
|
||||||
return self._strItem(DATABASE_URL)
|
|
||||||
|
|
||||||
def setDatabaseUrl(self, value: str):
|
|
||||||
self._setStrItem(DATABASE_URL, value)
|
|
||||||
|
|
||||||
def knnModelFile(self):
|
|
||||||
return self._strItem(KNN_MODEL_FILE)
|
|
||||||
|
|
||||||
def setKnnModelFile(self, value: str):
|
|
||||||
self._setStrItem(KNN_MODEL_FILE, value)
|
|
||||||
|
|
||||||
def resetKnnModelFile(self):
|
|
||||||
self._resetStrItem(KNN_MODEL_FILE)
|
|
||||||
|
|
||||||
def b30KnnModelFile(self):
|
|
||||||
return self._strItem(B30_KNN_MODEL_FILE)
|
|
||||||
|
|
||||||
def setB30KnnModelFile(self, value: str):
|
|
||||||
self._setStrItem(B30_KNN_MODEL_FILE, value)
|
|
||||||
|
|
||||||
def resetB30KnnModelFile(self):
|
|
||||||
self._resetStrItem(B30_KNN_MODEL_FILE)
|
|
||||||
|
|
||||||
def phashDatabaseFile(self):
|
|
||||||
return self._strItem(PHASH_DATABASE_FILE)
|
|
||||||
|
|
||||||
def setPHashDatabaseFile(self, value: str):
|
|
||||||
self._setStrItem(PHASH_DATABASE_FILE, value)
|
|
||||||
|
|
||||||
def resetPHashDatabaseFile(self):
|
|
||||||
self._resetStrItem(PHASH_DATABASE_FILE)
|
|
||||||
|
|
||||||
def andrealFolder(self):
|
|
||||||
return self._strItem(ANDREAL_FOLDER)
|
|
||||||
|
|
||||||
def setAndrealFolder(self, value: str):
|
|
||||||
self._setStrItem(ANDREAL_FOLDER, value)
|
|
||||||
|
|
||||||
def resetAndrealFolder(self):
|
|
||||||
self._resetStrItem(ANDREAL_FOLDER)
|
|
||||||
|
|
||||||
def andrealExecutable(self):
|
|
||||||
return self._strItem(ANDREAL_EXECUTABLE)
|
|
||||||
|
|
||||||
def setAndrealExecutable(self, value: str):
|
|
||||||
self._setStrItem(ANDREAL_EXECUTABLE, value)
|
|
||||||
|
|
||||||
def resetAndrealExecutable(self):
|
|
||||||
self._resetStrItem(ANDREAL_EXECUTABLE)
|
|
||||||
@ -6,10 +6,10 @@ from arcaea_offline_ocr.b30.chieri.v4.ocr import ChieriBotV4Ocr
|
|||||||
from arcaea_offline_ocr.b30.shared import B30OcrResultItem
|
from arcaea_offline_ocr.b30.shared import B30OcrResultItem
|
||||||
from PySide6.QtGui import QImage
|
from PySide6.QtGui import QImage
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
from ui.extends.components.ocrQueue import OcrRunnable
|
from ui.extends.components.ocrQueue import OcrRunnable
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class ChieriV4OcrRunnable(OcrRunnable):
|
class ChieriV4OcrRunnable(OcrRunnable):
|
||||||
def __init__(self, ocr: ChieriBotV4Ocr, component):
|
def __init__(self, ocr: ChieriBotV4Ocr, component):
|
||||||
|
|||||||
@ -19,6 +19,7 @@ from arcaea_offline_ocr.phash_db import ImagePhashDatabase
|
|||||||
from arcaea_offline_ocr.utils import imread_unicode
|
from arcaea_offline_ocr.utils import imread_unicode
|
||||||
from PySide6.QtCore import QDateTime, QFileInfo
|
from PySide6.QtCore import QDateTime, QFileInfo
|
||||||
|
|
||||||
|
from core.settings import SettingsKeys, SettingsValues, settings
|
||||||
from ui.extends.components.ocrQueue import OcrRunnable
|
from ui.extends.components.ocrQueue import OcrRunnable
|
||||||
from ui.extends.shared.data import Data
|
from ui.extends.shared.data import Data
|
||||||
|
|
||||||
@ -67,8 +68,14 @@ def getImageDate(imagePath: str) -> QDateTime:
|
|||||||
if exifImage.has_exif and exifImage.get("datetime_original"):
|
if exifImage.has_exif and exifImage.get("datetime_original"):
|
||||||
datetimeStr = exifImage.get("datetime_original")
|
datetimeStr = exifImage.get("datetime_original")
|
||||||
datetime = QDateTime.fromString(datetimeStr, "yyyy:MM:dd hh:mm:ss")
|
datetime = QDateTime.fromString(datetimeStr, "yyyy:MM:dd hh:mm:ss")
|
||||||
|
|
||||||
if not isinstance(datetime, QDateTime):
|
if not isinstance(datetime, QDateTime):
|
||||||
datetime = QFileInfo(imagePath).birthTime()
|
dateSource = settings.stringValue(SettingsKeys.Ocr.DateSource)
|
||||||
|
if dateSource == SettingsValues.Ocr.DateSource.FileLastModified:
|
||||||
|
datetime = QFileInfo(imagePath).lastModified()
|
||||||
|
else:
|
||||||
|
datetime = QFileInfo(imagePath).birthTime()
|
||||||
|
|
||||||
return datetime
|
return datetime
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -41,7 +41,7 @@ class AndrealExecuteRunnable(QRunnable):
|
|||||||
self.signals.completed.emit(self.jsonPath, imageBytes)
|
self.signals.completed.emit(self.jsonPath, imageBytes)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
imageBytes = None
|
imageBytes = None
|
||||||
logger.exception(f"{self.__class__.__name__} error")
|
logger.exception("%s error", self.__class__.__name__)
|
||||||
self.signals.error.emit(self.jsonPath, str(e))
|
self.signals.error.emit(self.jsonPath, str(e))
|
||||||
finally:
|
finally:
|
||||||
os.unlink(self.jsonPath)
|
os.unlink(self.jsonPath)
|
||||||
@ -84,7 +84,10 @@ class AndrealHelper(QObject):
|
|||||||
|
|
||||||
def request(self, jsonPath: str, arguments: list[str]):
|
def request(self, jsonPath: str, arguments: list[str]):
|
||||||
logger.debug(
|
logger.debug(
|
||||||
f"{self.__class__.__name__} received request {jsonPath=} {arguments=}"
|
"%s received request jsonPath=%r arguments=%r",
|
||||||
|
self.__class__.__name__,
|
||||||
|
jsonPath,
|
||||||
|
arguments,
|
||||||
)
|
)
|
||||||
runnable = AndrealExecuteRunnable(self.andrealExecutable, jsonPath, arguments)
|
runnable = AndrealExecuteRunnable(self.andrealExecutable, jsonPath, arguments)
|
||||||
runnable.signals.error.connect(self.error)
|
runnable.signals.error.connect(self.error)
|
||||||
|
|||||||
@ -2,9 +2,9 @@ from PySide6.QtCore import QDir, QFileInfo, Qt, Signal, Slot
|
|||||||
from PySide6.QtGui import QDragEnterEvent, QDragLeaveEvent, QDropEvent
|
from PySide6.QtGui import QDragEnterEvent, QDragLeaveEvent, QDropEvent
|
||||||
from PySide6.QtWidgets import QFileDialog, QWidget
|
from PySide6.QtWidgets import QFileDialog, QWidget
|
||||||
|
|
||||||
|
from core.settings import settings
|
||||||
from ui.designer.components.fileSelector_ui import Ui_FileSelector
|
from ui.designer.components.fileSelector_ui import Ui_FileSelector
|
||||||
from ui.extends.shared.language import LanguageChangeEventFilter
|
from ui.extends.shared.language import LanguageChangeEventFilter
|
||||||
from ui.extends.shared.settings import Settings
|
|
||||||
|
|
||||||
|
|
||||||
class FileSelector(Ui_FileSelector, QWidget):
|
class FileSelector(Ui_FileSelector, QWidget):
|
||||||
@ -122,13 +122,13 @@ class FileSelector(Ui_FileSelector, QWidget):
|
|||||||
if self.__selectedFiles:
|
if self.__selectedFiles:
|
||||||
return
|
return
|
||||||
|
|
||||||
if value := Settings().value(self.settingsKey):
|
if value := settings.value(self.settingsKey):
|
||||||
self.selectFile(value)
|
self.selectFile(value)
|
||||||
|
|
||||||
Settings().updated.connect(self.settingsUpdated)
|
settings.updated.connect(self.settingsUpdated)
|
||||||
|
|
||||||
def disconnectSettings(self):
|
def disconnectSettings(self):
|
||||||
Settings().updated.disconnect(self.settingsUpdated)
|
settings.updated.disconnect(self.settingsUpdated)
|
||||||
self.settingsKey = None
|
self.settingsKey = None
|
||||||
|
|
||||||
def settingsUpdated(self, key: str):
|
def settingsUpdated(self, key: str):
|
||||||
@ -139,4 +139,4 @@ class FileSelector(Ui_FileSelector, QWidget):
|
|||||||
if self.__selectedFiles:
|
if self.__selectedFiles:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.selectFile(Settings().value(self.settingsKey))
|
self.selectFile(settings.value(self.settingsKey))
|
||||||
|
|||||||
@ -2,7 +2,7 @@ from typing import Optional
|
|||||||
|
|
||||||
from PySide6.QtCore import Qt, QTimer, Slot
|
from PySide6.QtCore import Qt, QTimer, Slot
|
||||||
from PySide6.QtGui import QColor, QPalette
|
from PySide6.QtGui import QColor, QPalette
|
||||||
from PySide6.QtWidgets import QButtonGroup, QWidget
|
from PySide6.QtWidgets import QWidget
|
||||||
|
|
||||||
from ui.designer.components.ocrQueue_ui import Ui_OcrQueue
|
from ui.designer.components.ocrQueue_ui import Ui_OcrQueue
|
||||||
from ui.extends.components.ocrQueue import (
|
from ui.extends.components.ocrQueue import (
|
||||||
@ -13,6 +13,7 @@ from ui.extends.components.ocrQueue import (
|
|||||||
OcrScoreDelegate,
|
OcrScoreDelegate,
|
||||||
)
|
)
|
||||||
from ui.extends.shared.language import LanguageChangeEventFilter
|
from ui.extends.shared.language import LanguageChangeEventFilter
|
||||||
|
from ui.implements.components.ocrQueueOptionsDialog import OcrQueueOptionsDialog
|
||||||
|
|
||||||
|
|
||||||
class OcrQueue(Ui_OcrQueue, QWidget):
|
class OcrQueue(Ui_OcrQueue, QWidget):
|
||||||
@ -26,6 +27,9 @@ class OcrQueue(Ui_OcrQueue, QWidget):
|
|||||||
self.__model: Optional[OcrQueueModel] = None
|
self.__model: Optional[OcrQueueModel] = None
|
||||||
self.__tableProxyModel: Optional[OcrQueueTableProxyModel] = None
|
self.__tableProxyModel: Optional[OcrQueueTableProxyModel] = None
|
||||||
|
|
||||||
|
self.optionsDialog = OcrQueueOptionsDialog(self)
|
||||||
|
self.optionsDialog.iccOptionsChanged.connect(self.setIccOption)
|
||||||
|
|
||||||
self.__firstResizeDone = False
|
self.__firstResizeDone = False
|
||||||
self.resizeTimer = QTimer(self)
|
self.resizeTimer = QTimer(self)
|
||||||
self.resizeTimer.timeout.connect(self.tableView.resizeRowsToContents)
|
self.resizeTimer.timeout.connect(self.tableView.resizeRowsToContents)
|
||||||
@ -41,13 +45,6 @@ class OcrQueue(Ui_OcrQueue, QWidget):
|
|||||||
tableViewPalette.setColor(QPalette.ColorRole.Highlight, highlightColor)
|
tableViewPalette.setColor(QPalette.ColorRole.Highlight, highlightColor)
|
||||||
self.tableView.setPalette(tableViewPalette)
|
self.tableView.setPalette(tableViewPalette)
|
||||||
|
|
||||||
self.iccOptionButtonGroup = QButtonGroup(self)
|
|
||||||
self.iccOptionButtonGroup.buttonToggled.connect(self.updateIccOption)
|
|
||||||
self.iccOptionButtonGroup.addButton(self.iccUseQtRadioButton, 0)
|
|
||||||
self.iccOptionButtonGroup.addButton(self.iccUsePILRadioButton, 1)
|
|
||||||
self.iccOptionButtonGroup.addButton(self.iccTryFixRadioButton, 2)
|
|
||||||
self.updateIccOption()
|
|
||||||
|
|
||||||
self.statusLabelClearTimer = QTimer(self)
|
self.statusLabelClearTimer = QTimer(self)
|
||||||
self.statusLabelClearTimer.setSingleShot(True)
|
self.statusLabelClearTimer.setSingleShot(True)
|
||||||
self.statusLabelClearTimer.timeout.connect(self.clearStatusMessage)
|
self.statusLabelClearTimer.timeout.connect(self.clearStatusMessage)
|
||||||
@ -93,9 +90,10 @@ class OcrQueue(Ui_OcrQueue, QWidget):
|
|||||||
self.ocr_acceptAllButton.setEnabled(__bool)
|
self.ocr_acceptAllButton.setEnabled(__bool)
|
||||||
self.ocr_ignoreValidateCheckBox.setEnabled(__bool)
|
self.ocr_ignoreValidateCheckBox.setEnabled(__bool)
|
||||||
|
|
||||||
def updateIccOption(self):
|
@Slot(int)
|
||||||
|
def setIccOption(self, option):
|
||||||
if self.model():
|
if self.model():
|
||||||
self.model().iccOption = self.iccOptionButtonGroup.checkedId()
|
self.model().iccOption = option
|
||||||
|
|
||||||
def showStatusMessage(self, message: str):
|
def showStatusMessage(self, message: str):
|
||||||
self.statusLabel.setText(message)
|
self.statusLabel.setText(message)
|
||||||
@ -131,6 +129,10 @@ class OcrQueue(Ui_OcrQueue, QWidget):
|
|||||||
def modelReseted(self):
|
def modelReseted(self):
|
||||||
self.progressBar.setMaximum(0)
|
self.progressBar.setMaximum(0)
|
||||||
|
|
||||||
|
@Slot()
|
||||||
|
def on_optionsDialogButton_clicked(self):
|
||||||
|
self.optionsDialog.exec()
|
||||||
|
|
||||||
@Slot()
|
@Slot()
|
||||||
def on_ocr_removeSelectedButton_clicked(self):
|
def on_ocr_removeSelectedButton_clicked(self):
|
||||||
if self.model():
|
if self.model():
|
||||||
|
|||||||
49
ui/implements/components/ocrQueueOptionsDialog.py
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
from PySide6.QtCore import Signal
|
||||||
|
from PySide6.QtWidgets import QButtonGroup, QDialog
|
||||||
|
|
||||||
|
from core.settings import SettingsKeys, SettingsValues, settings
|
||||||
|
from ui.designer.components.ocrQueueOptionsDialog_ui import Ui_OcrQueueOptionsDialog
|
||||||
|
|
||||||
|
|
||||||
|
class OcrQueueOptionsDialog(QDialog, Ui_OcrQueueOptionsDialog):
|
||||||
|
iccOptionsChanged = Signal(int)
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super(OcrQueueOptionsDialog, self).__init__(parent)
|
||||||
|
self.setupUi(self)
|
||||||
|
|
||||||
|
self.iccOptionButtonGroup = QButtonGroup(self)
|
||||||
|
self.iccOptionButtonGroup.buttonToggled.connect(
|
||||||
|
lambda: self.iccOptionsChanged.emit(self.iccOptionButtonGroup.checkedId())
|
||||||
|
)
|
||||||
|
self.iccOptionButtonGroup.addButton(self.iccUseQtRadioButton, 0)
|
||||||
|
self.iccOptionButtonGroup.addButton(self.iccUsePILRadioButton, 1)
|
||||||
|
self.iccOptionButtonGroup.addButton(self.iccTryFixRadioButton, 2)
|
||||||
|
|
||||||
|
self.scoreDateSourceButtonGroup = QButtonGroup(self)
|
||||||
|
self.scoreDateSourceButtonGroup.addButton(
|
||||||
|
self.dateUseCreationDateRadioButton, 0
|
||||||
|
)
|
||||||
|
self.scoreDateSourceButtonGroup.addButton(self.dateUseModifyDateRadioButton, 1)
|
||||||
|
self.scoreDateSourceButtonGroup.buttonClicked.connect(
|
||||||
|
self.on_scoreDateSourceButtonGroup_buttonClicked
|
||||||
|
)
|
||||||
|
|
||||||
|
settings.updated.connect(self.syncCheckboxesFromSettings)
|
||||||
|
self.syncCheckboxesFromSettings()
|
||||||
|
|
||||||
|
def syncCheckboxesFromSettings(self):
|
||||||
|
scoreDateSource = settings.stringValue(SettingsKeys.Ocr.DateSource)
|
||||||
|
if scoreDateSource == SettingsValues.Ocr.DateSource.FileLastModified:
|
||||||
|
self.dateUseModifyDateRadioButton.setChecked(True)
|
||||||
|
else:
|
||||||
|
self.dateUseCreationDateRadioButton.setChecked(True)
|
||||||
|
|
||||||
|
def on_scoreDateSourceButtonGroup_buttonClicked(self, button):
|
||||||
|
buttonId = self.scoreDateSourceButtonGroup.id(button)
|
||||||
|
if buttonId == 1:
|
||||||
|
value = SettingsValues.Ocr.DateSource.FileLastModified
|
||||||
|
else:
|
||||||
|
value = SettingsValues.Ocr.DateSource.FileCreated
|
||||||
|
|
||||||
|
settings.setValue(SettingsKeys.Ocr.DateSource, value)
|
||||||
@ -2,7 +2,7 @@ from PySide6.QtCore import Slot
|
|||||||
from PySide6.QtGui import QColor
|
from PySide6.QtGui import QColor
|
||||||
from PySide6.QtWidgets import QGraphicsColorizeEffect, QRadioButton
|
from PySide6.QtWidgets import QGraphicsColorizeEffect, QRadioButton
|
||||||
|
|
||||||
from ui.extends.shared.color import mix_color
|
from core.color import mixColor
|
||||||
|
|
||||||
STYLESHEET = """
|
STYLESHEET = """
|
||||||
QRadioButton {{
|
QRadioButton {{
|
||||||
@ -40,7 +40,7 @@ class RatingClassRadioButton(QRadioButton):
|
|||||||
def setColors(self, dark_color: QColor, text_color: QColor):
|
def setColors(self, dark_color: QColor, text_color: QColor):
|
||||||
self._dark_color = dark_color
|
self._dark_color = dark_color
|
||||||
self._text_color = text_color
|
self._text_color = text_color
|
||||||
self._mid_color = mix_color(dark_color, text_color, 0.616)
|
self._mid_color = mixColor(dark_color, text_color, 0.616)
|
||||||
self.updateEffects()
|
self.updateEffects()
|
||||||
|
|
||||||
def isColorsSet(self) -> bool:
|
def isColorsSet(self) -> bool:
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import logging
|
||||||
from typing import Type
|
from typing import Type
|
||||||
|
|
||||||
from PySide6.QtCore import Signal
|
from PySide6.QtCore import Signal
|
||||||
@ -6,6 +7,8 @@ from PySide6.QtWidgets import QHBoxLayout, QSizePolicy, QVBoxLayout, QWidget
|
|||||||
|
|
||||||
from ui.implements.components.ratingClassRadioButton import RatingClassRadioButton
|
from ui.implements.components.ratingClassRadioButton import RatingClassRadioButton
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class RatingClassSelector(QWidget):
|
class RatingClassSelector(QWidget):
|
||||||
valueChanged = Signal()
|
valueChanged = Signal()
|
||||||
@ -120,9 +123,10 @@ class RatingClassSelector(QWidget):
|
|||||||
|
|
||||||
if ratingClass is None or isinstance(ratingClass, bool):
|
if ratingClass is None or isinstance(ratingClass, bool):
|
||||||
button = self.sender()
|
button = self.sender()
|
||||||
elif ratingClass in range(4):
|
elif ratingClass in range(len(self.buttons)):
|
||||||
button = self.buttons[ratingClass]
|
button = self.buttons[ratingClass]
|
||||||
else:
|
else:
|
||||||
|
logger.debug("Cannot select ratingClass=%s, condition check failed", ratingClass)
|
||||||
return
|
return
|
||||||
|
|
||||||
if not button.isEnabled():
|
if not button.isEnabled():
|
||||||
|
|||||||
@ -61,30 +61,22 @@ class ScoreEditor(Ui_ScoreEditor, QWidget):
|
|||||||
|
|
||||||
VALIDATION_ITEMS_TEXT = [
|
VALIDATION_ITEMS_TEXT = [
|
||||||
[
|
[
|
||||||
# fmt: off
|
|
||||||
lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.chartIncomplete.title"),
|
lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.chartIncomplete.title"),
|
||||||
lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.chartIncomplete.text"),
|
lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.chartIncomplete.text"),
|
||||||
# fmt: on
|
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
# fmt: off
|
|
||||||
lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.scoreMismatch.title"),
|
lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.scoreMismatch.title"),
|
||||||
lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.scoreMismatch.text"),
|
lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.scoreMismatch.text"),
|
||||||
# fmt: on
|
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
# fmt: off
|
|
||||||
lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.emptyScore.title"),
|
lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.emptyScore.title"),
|
||||||
lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.emptyScore.text"),
|
lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.emptyScore.text"),
|
||||||
# fmt: on
|
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
# fmt: off
|
|
||||||
lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.scoreIncompleteForValidate.title"),
|
lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.scoreIncompleteForValidate.title"),
|
||||||
lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.scoreIncompleteForValidate.text"),
|
lambda: QCoreApplication.translate("ScoreEditor", "confirmDialog.scoreIncompleteForValidate.text"),
|
||||||
# fmt: on,
|
|
||||||
],
|
],
|
||||||
]
|
] # fmt: skip
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
@ -208,20 +200,16 @@ class ScoreEditor(Ui_ScoreEditor, QWidget):
|
|||||||
if validate & ScoreValidateResult.ChartNotSet:
|
if validate & ScoreValidateResult.ChartNotSet:
|
||||||
self.__triggerMessageBox(
|
self.__triggerMessageBox(
|
||||||
"critical",
|
"critical",
|
||||||
# fmt: off
|
|
||||||
QCoreApplication.translate("ScoreEditor", "confirmDialog.chartNotSet.title"),
|
QCoreApplication.translate("ScoreEditor", "confirmDialog.chartNotSet.title"),
|
||||||
QCoreApplication.translate("ScoreEditor", "confirmDialog.chartNotSet.text"),
|
QCoreApplication.translate("ScoreEditor", "confirmDialog.chartNotSet.text"),
|
||||||
# fmt: on
|
) # fmt: skip
|
||||||
)
|
|
||||||
return False
|
return False
|
||||||
if validate & ScoreValidateResult.ScoreIncomplete:
|
if validate & ScoreValidateResult.ScoreIncomplete:
|
||||||
self.__triggerMessageBox(
|
self.__triggerMessageBox(
|
||||||
"critical",
|
"critical",
|
||||||
# fmt: off
|
|
||||||
QCoreApplication.translate("ScoreEditor", "confirmDialog.scoreIncomplete.title"),
|
QCoreApplication.translate("ScoreEditor", "confirmDialog.scoreIncomplete.title"),
|
||||||
QCoreApplication.translate("ScoreEditor", "confirmDialog.scoreIncomplete.text"),
|
QCoreApplication.translate("ScoreEditor", "confirmDialog.scoreIncomplete.text"),
|
||||||
# fmt: on
|
) # fmt: skip
|
||||||
)
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# since validate may have multiple results
|
# since validate may have multiple results
|
||||||
@ -347,10 +335,8 @@ class ScoreEditor(Ui_ScoreEditor, QWidget):
|
|||||||
)
|
)
|
||||||
if validate & ScoreValidateResult.ScoreIncompleteForValidate:
|
if validate & ScoreValidateResult.ScoreIncompleteForValidate:
|
||||||
texts.append(
|
texts.append(
|
||||||
# fmt: off
|
|
||||||
QCoreApplication.translate("ScoreEditor", "validate.scoreIncompleteForValidate")
|
QCoreApplication.translate("ScoreEditor", "validate.scoreIncompleteForValidate")
|
||||||
# fmt: on
|
) # fmt: skip
|
||||||
)
|
|
||||||
|
|
||||||
if not texts:
|
if not texts:
|
||||||
texts.append(
|
texts.append(
|
||||||
|
|||||||
@ -1,14 +1,18 @@
|
|||||||
|
import dataclasses
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
from enum import IntEnum
|
from enum import IntEnum
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from arcaea_offline.database import Database
|
from arcaea_offline.database import Database
|
||||||
from arcaea_offline.models import Chart
|
from arcaea_offline.models import Chart
|
||||||
from PySide6.QtCore import QModelIndex, QSignalMapper, Qt, Signal, Slot
|
from arcaea_offline.searcher import Searcher
|
||||||
from PySide6.QtWidgets import QCompleter, QWidget
|
from arcaea_offline.utils.rating import rating_class_to_short_text
|
||||||
|
from PySide6.QtCore import QModelIndex, QObject, QSignalMapper, Qt, Signal, Slot
|
||||||
|
from PySide6.QtGui import QStandardItem, QStandardItemModel
|
||||||
|
from PySide6.QtWidgets import QComboBox, QCompleter, QWidget
|
||||||
|
|
||||||
from ui.designer.components.songIdSelector_ui import Ui_SongIdSelector
|
from ui.designer.components.songIdSelector_ui import Ui_SongIdSelector
|
||||||
from ui.extends.components.songIdSelector import SearchCompleterModel
|
|
||||||
from ui.extends.shared.database import databaseUpdateSignals
|
from ui.extends.shared.database import databaseUpdateSignals
|
||||||
from ui.extends.shared.delegates.descriptionDelegate import DescriptionDelegate
|
from ui.extends.shared.delegates.descriptionDelegate import DescriptionDelegate
|
||||||
from ui.extends.shared.language import LanguageChangeEventFilter
|
from ui.extends.shared.language import LanguageChangeEventFilter
|
||||||
@ -21,6 +25,168 @@ class SongIdSelectorMode(IntEnum):
|
|||||||
Chart = 1
|
Chart = 1
|
||||||
|
|
||||||
|
|
||||||
|
# region logics
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class _ComboBoxItem:
|
||||||
|
text: str
|
||||||
|
userData: Any
|
||||||
|
additionalData: dict[int, Any]
|
||||||
|
|
||||||
|
def apply(self, comboBox: QComboBox):
|
||||||
|
comboBox.addItem(self.text, self.userData)
|
||||||
|
|
||||||
|
index = comboBox.findText(self.text)
|
||||||
|
if index > -1:
|
||||||
|
for role, value in self.additionalData.items():
|
||||||
|
comboBox.setItemData(index, value, role)
|
||||||
|
|
||||||
|
|
||||||
|
class SearchCompleterModel(QStandardItemModel):
|
||||||
|
ChartRole = Qt.ItemDataRole.UserRole + 10
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.searcher = Searcher()
|
||||||
|
self.db = Database()
|
||||||
|
|
||||||
|
def updateSearcherSongs(self):
|
||||||
|
with self.db.sessionmaker() as session:
|
||||||
|
self.searcher.import_songs(session)
|
||||||
|
|
||||||
|
def getSearchResult(self, kw: str):
|
||||||
|
self.clear()
|
||||||
|
|
||||||
|
songIds = self.searcher.search(kw)
|
||||||
|
|
||||||
|
charts: list[Chart] = []
|
||||||
|
for songId in songIds:
|
||||||
|
_charts = self.db.get_charts_by_song_id(songId)
|
||||||
|
_charts = sorted(_charts, key=lambda c: c.rating_class, reverse=True)
|
||||||
|
charts += _charts
|
||||||
|
|
||||||
|
for chart in charts:
|
||||||
|
displayText = (
|
||||||
|
f"{chart.title} [{rating_class_to_short_text(chart.rating_class)}]"
|
||||||
|
)
|
||||||
|
item = QStandardItem(kw)
|
||||||
|
item.setData(kw)
|
||||||
|
item.setData(displayText, DescriptionDelegate.MainTextRole)
|
||||||
|
item.setData(
|
||||||
|
f"{chart.song_id}, {chart.set}", DescriptionDelegate.DescriptionTextRole
|
||||||
|
)
|
||||||
|
item.setData(chart, self.ChartRole)
|
||||||
|
self.appendRow(item)
|
||||||
|
|
||||||
|
|
||||||
|
class SongIdSelectorViewModel(QObject):
|
||||||
|
packComboBoxItemsReady = Signal()
|
||||||
|
songIdComboBoxItemsReady = Signal()
|
||||||
|
|
||||||
|
def __init__(self, parent=None, *, db: Database):
|
||||||
|
super().__init__(parent)
|
||||||
|
|
||||||
|
if not isinstance(db, Database):
|
||||||
|
raise TypeError(
|
||||||
|
"`db` should be an instance of arcaea_offline.database.Database"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.__db: Database = None # type: ignore
|
||||||
|
self.__mode = SongIdSelectorMode.SongId
|
||||||
|
|
||||||
|
self.__packComboBoxItems: list[_ComboBoxItem] = []
|
||||||
|
self.__songIdComboBoxItems: list[_ComboBoxItem] = []
|
||||||
|
|
||||||
|
self.setDatabase(db)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def packComboBoxItems(self):
|
||||||
|
return self.__packComboBoxItems
|
||||||
|
|
||||||
|
@property
|
||||||
|
def songIdComboBoxItems(self):
|
||||||
|
return self.__songIdComboBoxItems
|
||||||
|
|
||||||
|
@property
|
||||||
|
def mode(self):
|
||||||
|
return self.__mode
|
||||||
|
|
||||||
|
@mode.setter
|
||||||
|
def mode(self, value):
|
||||||
|
if not isinstance(value, SongIdSelectorMode):
|
||||||
|
raise TypeError("value is not SongIdSelectorMode")
|
||||||
|
self.__mode = value
|
||||||
|
|
||||||
|
def setDatabase(self, db: Database):
|
||||||
|
self.__db = db
|
||||||
|
|
||||||
|
def updatePackComboBoxItems(self):
|
||||||
|
packs = self.__db.get_packs()
|
||||||
|
|
||||||
|
self.__packComboBoxItems.clear()
|
||||||
|
for pack in packs:
|
||||||
|
if re.search(r"_append_.*$", pack.id):
|
||||||
|
basePackId = re.sub(r"_append_.*$", "", pack.id)
|
||||||
|
basePackName = self.__db.get_pack(basePackId).name # type: ignore
|
||||||
|
packName = f"{basePackName} - {pack.name}"
|
||||||
|
else:
|
||||||
|
packName = pack.name
|
||||||
|
|
||||||
|
self.__packComboBoxItems.append(
|
||||||
|
_ComboBoxItem(
|
||||||
|
text=f"{packName} ({pack.id})",
|
||||||
|
userData=pack.id,
|
||||||
|
additionalData={
|
||||||
|
DescriptionDelegate.MainTextRole: packName,
|
||||||
|
DescriptionDelegate.DescriptionTextRole: pack.id,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.packComboBoxItemsReady.emit()
|
||||||
|
|
||||||
|
def updateSongIdComboBoxItems(self, packId: str):
|
||||||
|
self.__songIdComboBoxItems.clear()
|
||||||
|
|
||||||
|
items = []
|
||||||
|
if self.mode == SongIdSelectorMode.SongId:
|
||||||
|
items = self.__db.get_songs_by_pack_id(packId)
|
||||||
|
elif self.mode == SongIdSelectorMode.Chart:
|
||||||
|
items = self.__db.get_charts_by_pack_id(packId)
|
||||||
|
else:
|
||||||
|
assert not "reachable"
|
||||||
|
|
||||||
|
insertedSongIds = []
|
||||||
|
|
||||||
|
for item in items:
|
||||||
|
if self.mode == SongIdSelectorMode.SongId:
|
||||||
|
itemId = item.id # type: ignore
|
||||||
|
elif self.mode == SongIdSelectorMode.Chart:
|
||||||
|
itemId = item.song_id # type: ignore
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if itemId not in insertedSongIds:
|
||||||
|
self.__songIdComboBoxItems.append(
|
||||||
|
_ComboBoxItem(
|
||||||
|
text=f"{item.title} ({itemId})",
|
||||||
|
userData=itemId,
|
||||||
|
additionalData={
|
||||||
|
DescriptionDelegate.MainTextRole: item.title,
|
||||||
|
DescriptionDelegate.DescriptionTextRole: itemId,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
insertedSongIds.append(itemId)
|
||||||
|
|
||||||
|
self.songIdComboBoxItemsReady.emit()
|
||||||
|
|
||||||
|
|
||||||
|
# endregion
|
||||||
|
|
||||||
|
|
||||||
class SongIdSelector(Ui_SongIdSelector, QWidget):
|
class SongIdSelector(Ui_SongIdSelector, QWidget):
|
||||||
valueChanged = Signal()
|
valueChanged = Signal()
|
||||||
quickSearchActivated = Signal(Chart)
|
quickSearchActivated = Signal(Chart)
|
||||||
@ -33,6 +199,10 @@ class SongIdSelector(Ui_SongIdSelector, QWidget):
|
|||||||
self.languageChangeEventFilter = LanguageChangeEventFilter(self)
|
self.languageChangeEventFilter = LanguageChangeEventFilter(self)
|
||||||
self.installEventFilter(self.languageChangeEventFilter)
|
self.installEventFilter(self.languageChangeEventFilter)
|
||||||
|
|
||||||
|
self.vm = SongIdSelectorViewModel(self, db=self.db)
|
||||||
|
self.vm.packComboBoxItemsReady.connect(self.fillPackComboBox)
|
||||||
|
self.vm.songIdComboBoxItemsReady.connect(self.fillSongIdComboBox)
|
||||||
|
|
||||||
# quick switch bindings
|
# quick switch bindings
|
||||||
self.quickSwitchSignalMapper = QSignalMapper(self)
|
self.quickSwitchSignalMapper = QSignalMapper(self)
|
||||||
self.previousPackageButton.clicked.connect(self.quickSwitchSignalMapper.map)
|
self.previousPackageButton.clicked.connect(self.quickSwitchSignalMapper.map)
|
||||||
@ -75,6 +245,7 @@ class SongIdSelector(Ui_SongIdSelector, QWidget):
|
|||||||
databaseUpdateSignals.songAddOrDelete.connect(self.updateDatabase)
|
databaseUpdateSignals.songAddOrDelete.connect(self.updateDatabase)
|
||||||
|
|
||||||
def setMode(self, mode: SongIdSelectorMode):
|
def setMode(self, mode: SongIdSelectorMode):
|
||||||
|
self.vm.mode = mode
|
||||||
self.mode = mode
|
self.mode = mode
|
||||||
|
|
||||||
@Slot(str)
|
@Slot(str)
|
||||||
@ -116,7 +287,7 @@ class SongIdSelector(Ui_SongIdSelector, QWidget):
|
|||||||
pack = self.packComboBox.currentData()
|
pack = self.packComboBox.currentData()
|
||||||
songId = self.songIdComboBox.currentData()
|
songId = self.songIdComboBox.currentData()
|
||||||
|
|
||||||
self.fillPackComboBox()
|
self.vm.updatePackComboBoxItems()
|
||||||
|
|
||||||
if pack:
|
if pack:
|
||||||
self.selectPack(pack)
|
self.selectPack(pack)
|
||||||
@ -125,58 +296,24 @@ class SongIdSelector(Ui_SongIdSelector, QWidget):
|
|||||||
|
|
||||||
def fillPackComboBox(self):
|
def fillPackComboBox(self):
|
||||||
self.packComboBox.clear()
|
self.packComboBox.clear()
|
||||||
packs = self.db.get_packs()
|
|
||||||
for pack in packs:
|
for item in self.vm.packComboBoxItems:
|
||||||
if isAppendPack := re.search(r"_append_.*$", pack.id):
|
item.apply(self.packComboBox)
|
||||||
basePackId = re.sub(r"_append_.*$", "", pack.id)
|
|
||||||
basePackName = self.db.get_pack(basePackId).name
|
|
||||||
packName = f"{basePackName} - {pack.name}"
|
|
||||||
else:
|
|
||||||
packName = pack.name
|
|
||||||
self.packComboBox.addItem(f"{packName} ({pack.id})", pack.id)
|
|
||||||
row = self.packComboBox.count() - 1
|
|
||||||
self.packComboBox.setItemData(
|
|
||||||
row, packName, DescriptionDelegate.MainTextRole
|
|
||||||
)
|
|
||||||
self.packComboBox.setItemData(
|
|
||||||
row, pack.id, DescriptionDelegate.DescriptionTextRole
|
|
||||||
)
|
|
||||||
|
|
||||||
self.packComboBox.setCurrentIndex(-1)
|
self.packComboBox.setCurrentIndex(-1)
|
||||||
|
|
||||||
def fillSongIdComboBox(self):
|
def fillSongIdComboBox(self):
|
||||||
self.songIdComboBox.clear()
|
self.songIdComboBox.clear()
|
||||||
if packId := self.packComboBox.currentData():
|
|
||||||
if self.mode == SongIdSelectorMode.SongId:
|
|
||||||
items = self.db.get_songs_by_pack_id(packId)
|
|
||||||
elif self.mode == SongIdSelectorMode.Chart:
|
|
||||||
items = self.db.get_charts_by_pack_id(packId)
|
|
||||||
else:
|
|
||||||
raise ValueError("Unknown SongIdSelectorMode.")
|
|
||||||
insertedSongIds = []
|
|
||||||
for item in items:
|
|
||||||
if self.mode == SongIdSelectorMode.SongId:
|
|
||||||
itemId = item.id
|
|
||||||
elif self.mode == SongIdSelectorMode.Chart:
|
|
||||||
itemId = item.song_id
|
|
||||||
else:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if itemId not in insertedSongIds:
|
for item in self.vm.songIdComboBoxItems:
|
||||||
self.songIdComboBox.addItem(f"{item.title} ({itemId})", itemId)
|
item.apply(self.songIdComboBox)
|
||||||
insertedSongIds.append(itemId)
|
|
||||||
row = self.songIdComboBox.count() - 1
|
|
||||||
self.songIdComboBox.setItemData(
|
|
||||||
row, item.title, DescriptionDelegate.MainTextRole
|
|
||||||
)
|
|
||||||
self.songIdComboBox.setItemData(
|
|
||||||
row, itemId, DescriptionDelegate.DescriptionTextRole
|
|
||||||
)
|
|
||||||
self.songIdComboBox.setCurrentIndex(-1)
|
self.songIdComboBox.setCurrentIndex(-1)
|
||||||
|
|
||||||
@Slot()
|
@Slot()
|
||||||
def on_packComboBox_currentIndexChanged(self):
|
def on_packComboBox_currentIndexChanged(self):
|
||||||
self.fillSongIdComboBox()
|
if packId := self.packComboBox.currentData():
|
||||||
|
self.vm.updateSongIdComboBoxItems(packId)
|
||||||
|
|
||||||
@Slot(str)
|
@Slot(str)
|
||||||
def on_searchLineEdit_textChanged(self, text: str):
|
def on_searchLineEdit_textChanged(self, text: str):
|
||||||
@ -189,10 +326,10 @@ class SongIdSelector(Ui_SongIdSelector, QWidget):
|
|||||||
packIdIndex = self.packComboBox.findData(packId)
|
packIdIndex = self.packComboBox.findData(packId)
|
||||||
if packIdIndex > -1:
|
if packIdIndex > -1:
|
||||||
self.packComboBox.setCurrentIndex(packIdIndex)
|
self.packComboBox.setCurrentIndex(packIdIndex)
|
||||||
self.fillSongIdComboBox()
|
self.vm.updateSongIdComboBoxItems(packId)
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
logger.warning(f'Attempting to select an unknown pack "{packId}"')
|
logger.warning("Attempting to select an unknown pack [%s]", packId)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def selectSongId(self, songId: str) -> bool:
|
def selectSongId(self, songId: str) -> bool:
|
||||||
@ -202,7 +339,8 @@ class SongIdSelector(Ui_SongIdSelector, QWidget):
|
|||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
f'Attempting to select an unknown song "{songId}", maybe try selecting a pack first?'
|
"Attempting to select an unknown song [%s], maybe try selecting a pack first?",
|
||||||
|
songId,
|
||||||
)
|
)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -213,7 +351,7 @@ class SongIdSelector(Ui_SongIdSelector, QWidget):
|
|||||||
|
|
||||||
@Slot(QModelIndex)
|
@Slot(QModelIndex)
|
||||||
def searchCompleterSetSelection(self, index: QModelIndex):
|
def searchCompleterSetSelection(self, index: QModelIndex):
|
||||||
chart: Chart = index.data(Qt.ItemDataRole.UserRole + 10)
|
chart: Chart = index.data(SearchCompleterModel.ChartRole)
|
||||||
self.selectChart(chart)
|
self.selectChart(chart)
|
||||||
self.quickSearchActivated.emit(chart)
|
self.quickSearchActivated.emit(chart)
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
from PySide6.QtCore import QCoreApplication
|
from PySide6.QtCore import QCoreApplication
|
||||||
from PySide6.QtWidgets import QLabel, QPushButton
|
from PySide6.QtWidgets import QLabel, QPushButton
|
||||||
|
|
||||||
|
from core.settings import SettingsKeys, settings
|
||||||
from ui.implements.components.fileSelector import FileSelector
|
from ui.implements.components.fileSelector import FileSelector
|
||||||
from ui.implements.settings.settingsBaseWidget import SettingsBaseWidget
|
from ui.implements.settings.settingsBaseWidget import SettingsBaseWidget
|
||||||
|
|
||||||
@ -14,8 +15,8 @@ class SettingsAndreal(SettingsBaseWidget):
|
|||||||
self.andrealFolderValueWidget.setMode(
|
self.andrealFolderValueWidget.setMode(
|
||||||
self.andrealFolderValueWidget.getExistingDirectory
|
self.andrealFolderValueWidget.getExistingDirectory
|
||||||
)
|
)
|
||||||
if self.settings.andrealFolder():
|
if andrealFolder := settings.stringValue(SettingsKeys.Andreal.Folder):
|
||||||
self.andrealFolderValueWidget.selectFile(self.settings.andrealFolder())
|
self.andrealFolderValueWidget.selectFile(andrealFolder)
|
||||||
self.andrealFolderValueWidget.filesSelected.connect(self.setAndrealFolder)
|
self.andrealFolderValueWidget.filesSelected.connect(self.setAndrealFolder)
|
||||||
self.andrealFolderResetButton.clicked.connect(self.resetAndrealFolder)
|
self.andrealFolderResetButton.clicked.connect(self.resetAndrealFolder)
|
||||||
self.insertItem(
|
self.insertItem(
|
||||||
@ -25,10 +26,8 @@ class SettingsAndreal(SettingsBaseWidget):
|
|||||||
self.andrealFolderResetButton,
|
self.andrealFolderResetButton,
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.settings.andrealExecutable():
|
if andrealExecutable := settings.stringValue(SettingsKeys.Andreal.Executable):
|
||||||
self.andrealExecutableValueWidget.selectFile(
|
self.andrealExecutableValueWidget.selectFile(andrealExecutable)
|
||||||
self.settings.andrealExecutable()
|
|
||||||
)
|
|
||||||
self.andrealExecutableValueWidget.filesSelected.connect(
|
self.andrealExecutableValueWidget.filesSelected.connect(
|
||||||
self.setAndrealExecutable
|
self.setAndrealExecutable
|
||||||
)
|
)
|
||||||
@ -44,21 +43,21 @@ class SettingsAndreal(SettingsBaseWidget):
|
|||||||
selectedFile = self.andrealFolderValueWidget.selectedFiles()
|
selectedFile = self.andrealFolderValueWidget.selectedFiles()
|
||||||
if selectedFile and selectedFile[0]:
|
if selectedFile and selectedFile[0]:
|
||||||
file = selectedFile[0]
|
file = selectedFile[0]
|
||||||
self.settings.setAndrealFolder(file)
|
settings.setValue(SettingsKeys.Andreal.Folder, file)
|
||||||
|
|
||||||
def resetAndrealFolder(self):
|
def resetAndrealFolder(self):
|
||||||
self.andrealFolderValueWidget.reset()
|
self.andrealFolderValueWidget.reset()
|
||||||
self.settings.resetAndrealFolder()
|
settings.setValue(SettingsKeys.Andreal.Folder, None)
|
||||||
|
|
||||||
def setAndrealExecutable(self):
|
def setAndrealExecutable(self):
|
||||||
selectedFile = self.andrealExecutableValueWidget.selectedFiles()
|
selectedFile = self.andrealExecutableValueWidget.selectedFiles()
|
||||||
if selectedFile and selectedFile[0]:
|
if selectedFile and selectedFile[0]:
|
||||||
file = selectedFile[0]
|
file = selectedFile[0]
|
||||||
self.settings.setAndrealExecutable(file)
|
settings.setValue(SettingsKeys.Andreal.Executable, file)
|
||||||
|
|
||||||
def resetAndrealExecutable(self):
|
def resetAndrealExecutable(self):
|
||||||
self.andrealExecutableValueWidget.reset()
|
self.andrealExecutableValueWidget.reset()
|
||||||
self.settings.resetAndrealExecutable()
|
settings.setValue(SettingsKeys.Andreal.Executable, None)
|
||||||
|
|
||||||
def setupUi(self, *args):
|
def setupUi(self, *args):
|
||||||
self.andrealFolderLabel = QLabel(self)
|
self.andrealFolderLabel = QLabel(self)
|
||||||
|
|||||||
@ -1,15 +1,15 @@
|
|||||||
from PySide6.QtCore import Qt
|
from PySide6.QtCore import Qt
|
||||||
from PySide6.QtWidgets import QLabel, QPushButton, QWidget
|
from PySide6.QtWidgets import QLabel, QPushButton, QWidget
|
||||||
|
|
||||||
|
from core.settings import settings
|
||||||
from ui.designer.settings.settingsBaseWidget_ui import Ui_SettingsBaseWidget
|
from ui.designer.settings.settingsBaseWidget_ui import Ui_SettingsBaseWidget
|
||||||
from ui.extends.shared.language import LanguageChangeEventFilter
|
from ui.extends.shared.language import LanguageChangeEventFilter
|
||||||
from ui.extends.shared.settings import Settings
|
|
||||||
|
|
||||||
|
|
||||||
class SettingsBaseWidget(Ui_SettingsBaseWidget, QWidget):
|
class SettingsBaseWidget(Ui_SettingsBaseWidget, QWidget):
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.settings = Settings()
|
self.settings = settings
|
||||||
|
|
||||||
self.languageChangeEventFilter = LanguageChangeEventFilter(self)
|
self.languageChangeEventFilter = LanguageChangeEventFilter(self)
|
||||||
self.installEventFilter(self.languageChangeEventFilter)
|
self.installEventFilter(self.languageChangeEventFilter)
|
||||||
|
|||||||
@ -1,6 +1,4 @@
|
|||||||
import sys
|
from PySide6.QtCore import QCoreApplication, QDir, QLocale
|
||||||
|
|
||||||
from PySide6.QtCore import QCoreApplication, QDir, QLocale, QProcess
|
|
||||||
from PySide6.QtWidgets import (
|
from PySide6.QtWidgets import (
|
||||||
QApplication,
|
QApplication,
|
||||||
QCheckBox,
|
QCheckBox,
|
||||||
@ -10,8 +8,8 @@ from PySide6.QtWidgets import (
|
|||||||
QPushButton,
|
QPushButton,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from core.settings import SettingsKeys, settings
|
||||||
from ui.extends.shared.language import changeAppLanguage, localeToCode, localeToFullName
|
from ui.extends.shared.language import changeAppLanguage, localeToCode, localeToFullName
|
||||||
from ui.extends.shared.settings import DATABASE_URL, LANGUAGE
|
|
||||||
from ui.implements.settings.settingsBaseWidget import SettingsBaseWidget
|
from ui.implements.settings.settingsBaseWidget import SettingsBaseWidget
|
||||||
|
|
||||||
|
|
||||||
@ -33,8 +31,8 @@ class SettingsGeneral(SettingsBaseWidget):
|
|||||||
self.languageFollowSystemCheckBox.toggled.connect(
|
self.languageFollowSystemCheckBox.toggled.connect(
|
||||||
self.changeLanguageFollowSystem
|
self.changeLanguageFollowSystem
|
||||||
)
|
)
|
||||||
if self.settings.language():
|
if language := settings.stringValue(SettingsKeys.General.Language):
|
||||||
locale = QLocale(self.settings.language())
|
locale = QLocale(language)
|
||||||
index = self.languageValueWidget.findData(locale)
|
index = self.languageValueWidget.findData(locale)
|
||||||
if index > -1:
|
if index > -1:
|
||||||
self.languageValueWidget.setCurrentIndex(index)
|
self.languageValueWidget.setCurrentIndex(index)
|
||||||
@ -51,7 +49,7 @@ class SettingsGeneral(SettingsBaseWidget):
|
|||||||
self.insertItem(
|
self.insertItem(
|
||||||
"dbUrl",
|
"dbUrl",
|
||||||
self.dbUrlLabel,
|
self.dbUrlLabel,
|
||||||
QLabel(self.settings.databaseUrl()),
|
QLabel(settings.stringValue(SettingsKeys.General.DatabaseUrl)),
|
||||||
self.dbUrlResetButton,
|
self.dbUrlResetButton,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -59,13 +57,13 @@ class SettingsGeneral(SettingsBaseWidget):
|
|||||||
locale = self.languageValueWidget.currentData()
|
locale = self.languageValueWidget.currentData()
|
||||||
if locale:
|
if locale:
|
||||||
changeAppLanguage(locale)
|
changeAppLanguage(locale)
|
||||||
self.settings.setLanguage(localeToCode(locale))
|
settings.setValue(SettingsKeys.General.Language, localeToCode(locale))
|
||||||
|
|
||||||
def changeLanguageFollowSystem(self):
|
def changeLanguageFollowSystem(self):
|
||||||
followSystem = self.languageFollowSystemCheckBox.isChecked()
|
followSystem = self.languageFollowSystemCheckBox.isChecked()
|
||||||
self.languageValueWidget.setCurrentIndex(-1)
|
self.languageValueWidget.setCurrentIndex(-1)
|
||||||
if followSystem:
|
if followSystem:
|
||||||
self.settings.remove(LANGUAGE)
|
settings.remove(SettingsKeys.General.Language)
|
||||||
changeAppLanguage(QLocale.system())
|
changeAppLanguage(QLocale.system())
|
||||||
self.languageValueWidget.setEnabled(False)
|
self.languageValueWidget.setEnabled(False)
|
||||||
else:
|
else:
|
||||||
@ -80,7 +78,7 @@ class SettingsGeneral(SettingsBaseWidget):
|
|||||||
QMessageBox.StandardButton.No,
|
QMessageBox.StandardButton.No,
|
||||||
)
|
)
|
||||||
if userConfirm == QMessageBox.StandardButton.Yes:
|
if userConfirm == QMessageBox.StandardButton.Yes:
|
||||||
self.settings.remove(DATABASE_URL)
|
settings.remove(SettingsKeys.General.DatabaseUrl)
|
||||||
QApplication.instance().quit()
|
QApplication.instance().quit()
|
||||||
|
|
||||||
def setupUi(self, *args):
|
def setupUi(self, *args):
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
from PySide6.QtCore import QCoreApplication
|
from PySide6.QtCore import QCoreApplication
|
||||||
from PySide6.QtWidgets import QLabel, QPushButton
|
from PySide6.QtWidgets import QLabel, QPushButton
|
||||||
|
|
||||||
|
from core.settings import SettingsKeys, settings
|
||||||
from ui.implements.components.fileSelector import FileSelector
|
from ui.implements.components.fileSelector import FileSelector
|
||||||
from ui.implements.settings.settingsBaseWidget import SettingsBaseWidget
|
from ui.implements.settings.settingsBaseWidget import SettingsBaseWidget
|
||||||
|
|
||||||
@ -11,8 +12,8 @@ class SettingsOcr(SettingsBaseWidget):
|
|||||||
|
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
|
|
||||||
if self.settings.knnModelFile():
|
if knnModelFile := settings.stringValue(SettingsKeys.Ocr.KnnModelFile):
|
||||||
self.knnModelFileValueWidget.selectFile(self.settings.knnModelFile())
|
self.knnModelFileValueWidget.selectFile(knnModelFile)
|
||||||
self.knnModelFileValueWidget.filesSelected.connect(self.setKnnModelFile)
|
self.knnModelFileValueWidget.filesSelected.connect(self.setKnnModelFile)
|
||||||
self.knnModelFileResetButton.clicked.connect(self.resetKnnModelFile)
|
self.knnModelFileResetButton.clicked.connect(self.resetKnnModelFile)
|
||||||
self.insertItem(
|
self.insertItem(
|
||||||
@ -22,8 +23,8 @@ class SettingsOcr(SettingsBaseWidget):
|
|||||||
self.knnModelFileResetButton,
|
self.knnModelFileResetButton,
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.settings.b30KnnModelFile():
|
if b30KnnModelFile := settings.stringValue(SettingsKeys.Ocr.B30KnnModelFile):
|
||||||
self.b30KnnModelFileValueWidget.selectFile(self.settings.b30KnnModelFile())
|
self.b30KnnModelFileValueWidget.selectFile(b30KnnModelFile)
|
||||||
self.b30KnnModelFileValueWidget.filesSelected.connect(self.setB30KnnModelFile)
|
self.b30KnnModelFileValueWidget.filesSelected.connect(self.setB30KnnModelFile)
|
||||||
self.b30KnnModelFileResetButton.clicked.connect(self.resetB30KnnModelFile)
|
self.b30KnnModelFileResetButton.clicked.connect(self.resetB30KnnModelFile)
|
||||||
self.insertItem(
|
self.insertItem(
|
||||||
@ -33,10 +34,10 @@ class SettingsOcr(SettingsBaseWidget):
|
|||||||
self.b30KnnModelFileResetButton,
|
self.b30KnnModelFileResetButton,
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.settings.phashDatabaseFile():
|
if phashDatabaseFile := settings.stringValue(
|
||||||
self.phashDatabaseFileValueWidget.selectFile(
|
SettingsKeys.Ocr.PhashDatabaseFile
|
||||||
self.settings.phashDatabaseFile()
|
):
|
||||||
)
|
self.phashDatabaseFileValueWidget.selectFile(phashDatabaseFile)
|
||||||
self.phashDatabaseFileValueWidget.filesSelected.connect(
|
self.phashDatabaseFileValueWidget.filesSelected.connect(
|
||||||
self.setPHashDatabaseFile
|
self.setPHashDatabaseFile
|
||||||
)
|
)
|
||||||
@ -52,31 +53,31 @@ class SettingsOcr(SettingsBaseWidget):
|
|||||||
selectedFile = self.knnModelFileValueWidget.selectedFiles()
|
selectedFile = self.knnModelFileValueWidget.selectedFiles()
|
||||||
if selectedFile and selectedFile[0]:
|
if selectedFile and selectedFile[0]:
|
||||||
file = selectedFile[0]
|
file = selectedFile[0]
|
||||||
self.settings.setKnnModelFile(file)
|
settings.setValue(SettingsKeys.Ocr.KnnModelFile, file)
|
||||||
|
|
||||||
def resetKnnModelFile(self):
|
def resetKnnModelFile(self):
|
||||||
self.knnModelFileValueWidget.reset()
|
self.knnModelFileValueWidget.reset()
|
||||||
self.settings.resetKnnModelFile()
|
settings.setValue(SettingsKeys.Ocr.KnnModelFile, None)
|
||||||
|
|
||||||
def setB30KnnModelFile(self):
|
def setB30KnnModelFile(self):
|
||||||
selectedFile = self.b30KnnModelFileValueWidget.selectedFiles()
|
selectedFile = self.b30KnnModelFileValueWidget.selectedFiles()
|
||||||
if selectedFile and selectedFile[0]:
|
if selectedFile and selectedFile[0]:
|
||||||
file = selectedFile[0]
|
file = selectedFile[0]
|
||||||
self.settings.setB30KnnModelFile(file)
|
settings.setValue(SettingsKeys.Ocr.B30KnnModelFile, file)
|
||||||
|
|
||||||
def resetB30KnnModelFile(self):
|
def resetB30KnnModelFile(self):
|
||||||
self.b30KnnModelFileValueWidget.reset()
|
self.b30KnnModelFileValueWidget.reset()
|
||||||
self.settings.resetB30KnnModelFile()
|
settings.setValue(SettingsKeys.Ocr.B30KnnModelFile, None)
|
||||||
|
|
||||||
def setPHashDatabaseFile(self):
|
def setPHashDatabaseFile(self):
|
||||||
selectedFile = self.phashDatabaseFileValueWidget.selectedFiles()
|
selectedFile = self.phashDatabaseFileValueWidget.selectedFiles()
|
||||||
if selectedFile and selectedFile[0]:
|
if selectedFile and selectedFile[0]:
|
||||||
file = selectedFile[0]
|
file = selectedFile[0]
|
||||||
self.settings.setPHashDatabaseFile(file)
|
settings.setValue(SettingsKeys.Ocr.PhashDatabaseFile, file)
|
||||||
|
|
||||||
def resetPHashDatabaseFile(self):
|
def resetPHashDatabaseFile(self):
|
||||||
self.phashDatabaseFileValueWidget.reset()
|
self.phashDatabaseFileValueWidget.reset()
|
||||||
self.settings.resetPHashDatabaseFile()
|
settings.setValue(SettingsKeys.Ocr.PhashDatabaseFile, None)
|
||||||
|
|
||||||
def setupUi(self, *args):
|
def setupUi(self, *args):
|
||||||
self.knnModelFileLabel = QLabel(self)
|
self.knnModelFileLabel = QLabel(self)
|
||||||
|
|||||||
@ -163,19 +163,15 @@ class TabDb_ChartInfoEditor(Ui_TabDb_ChartInfoEditor, QWidget):
|
|||||||
QMessageBox.critical(
|
QMessageBox.critical(
|
||||||
self,
|
self,
|
||||||
None,
|
None,
|
||||||
# fmt: off
|
|
||||||
QCoreApplication.translate("TabDb_ChartInfoEditor", "commit.chartNotSelected"),
|
QCoreApplication.translate("TabDb_ChartInfoEditor", "commit.chartNotSelected"),
|
||||||
# fmt: on
|
) # fmt: skip
|
||||||
)
|
|
||||||
return
|
return
|
||||||
if not self.constantLineEdit.hasAcceptableInput():
|
if not self.constantLineEdit.hasAcceptableInput():
|
||||||
QMessageBox.critical(
|
QMessageBox.critical(
|
||||||
self,
|
self,
|
||||||
None,
|
None,
|
||||||
# fmt: off
|
|
||||||
QCoreApplication.translate("TabDb_ChartInfoEditor", "commit.constantRequired"),
|
QCoreApplication.translate("TabDb_ChartInfoEditor", "commit.constantRequired"),
|
||||||
# fmt: on
|
) # fmt: skip
|
||||||
)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
constant = int(self.constantLineEdit.text())
|
constant = int(self.constantLineEdit.text())
|
||||||
@ -202,10 +198,8 @@ class TabDb_ChartInfoEditor(Ui_TabDb_ChartInfoEditor, QWidget):
|
|||||||
QMessageBox.critical(
|
QMessageBox.critical(
|
||||||
self,
|
self,
|
||||||
None,
|
None,
|
||||||
# fmt: off
|
|
||||||
QCoreApplication.translate("TabDb_ChartInfoEditor", "commit.chartNotSelected"),
|
QCoreApplication.translate("TabDb_ChartInfoEditor", "commit.chartNotSelected"),
|
||||||
# fmt: on
|
) # fmt: skip
|
||||||
)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
chartInfo = self.db.get_chart_info(chart.song_id, chart.rating_class)
|
chartInfo = self.db.get_chart_info(chart.song_id, chart.rating_class)
|
||||||
@ -213,12 +207,10 @@ class TabDb_ChartInfoEditor(Ui_TabDb_ChartInfoEditor, QWidget):
|
|||||||
result = QMessageBox.warning(
|
result = QMessageBox.warning(
|
||||||
self,
|
self,
|
||||||
None,
|
None,
|
||||||
# fmt: off
|
|
||||||
QCoreApplication.translate("TabDb_ChartInfoEditor", "deleteConfirm"),
|
QCoreApplication.translate("TabDb_ChartInfoEditor", "deleteConfirm"),
|
||||||
# fmt: on
|
|
||||||
QMessageBox.StandardButton.Yes,
|
QMessageBox.StandardButton.Yes,
|
||||||
QMessageBox.StandardButton.No,
|
QMessageBox.StandardButton.No,
|
||||||
)
|
) # fmt: skip
|
||||||
if result == QMessageBox.StandardButton.Yes:
|
if result == QMessageBox.StandardButton.Yes:
|
||||||
with self.db.sessionmaker() as session:
|
with self.db.sessionmaker() as session:
|
||||||
session.delete(chartInfo)
|
session.delete(chartInfo)
|
||||||
|
|||||||
@ -91,7 +91,7 @@ class TabDb_Manage(Ui_TabDb_Manage, QWidget):
|
|||||||
session.commit()
|
session.commit()
|
||||||
databaseUpdateSignals.songAddOrDelete.emit()
|
databaseUpdateSignals.songAddOrDelete.emit()
|
||||||
itemNum = len([item for item in parser.parse() if isinstance(item, instance)])
|
itemNum = len([item for item in parser.parse() if isinstance(item, instance)])
|
||||||
logger.info(f"updated {itemNum} {logName} from {path}")
|
logger.info("updated %d %s from %s", itemNum, logName, path)
|
||||||
return itemNum
|
return itemNum
|
||||||
|
|
||||||
def importPacklist(self, packlistPath):
|
def importPacklist(self, packlistPath):
|
||||||
@ -161,7 +161,7 @@ class TabDb_Manage(Ui_TabDb_Manage, QWidget):
|
|||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
logger.info(f"Importing {apkFile}")
|
logger.info("Importing %s", apkFile)
|
||||||
|
|
||||||
with zipfile.ZipFile(apkFile) as zf:
|
with zipfile.ZipFile(apkFile) as zf:
|
||||||
packlistPath = zipfile.Path(zf) / "assets" / "songs" / "packlist"
|
packlistPath = zipfile.Path(zf) / "assets" / "songs" / "packlist"
|
||||||
@ -193,7 +193,9 @@ class TabDb_Manage(Ui_TabDb_Manage, QWidget):
|
|||||||
db = Database()
|
db = Database()
|
||||||
parser = St3ScoreParser(dbFile)
|
parser = St3ScoreParser(dbFile)
|
||||||
logger.info(
|
logger.info(
|
||||||
f"Got {len(parser.parse())} items from {dbFile}, writing into database..."
|
"Got %d items from %s, writing into database...",
|
||||||
|
len(parser.parse()),
|
||||||
|
dbFile,
|
||||||
)
|
)
|
||||||
with db.sessionmaker() as session:
|
with db.sessionmaker() as session:
|
||||||
parser.write_database(session)
|
parser.write_database(session)
|
||||||
@ -218,7 +220,9 @@ class TabDb_Manage(Ui_TabDb_Manage, QWidget):
|
|||||||
db = Database()
|
db = Database()
|
||||||
parser = ArcaeaOnlineParser(apiResultFile)
|
parser = ArcaeaOnlineParser(apiResultFile)
|
||||||
logger.info(
|
logger.info(
|
||||||
f"Got {len(parser.parse())} items from {apiResultFile}, writing into database..."
|
"Got %d items from %s, writing into database...",
|
||||||
|
len(parser.parse()),
|
||||||
|
apiResultFile,
|
||||||
)
|
)
|
||||||
with db.sessionmaker() as session:
|
with db.sessionmaker() as session:
|
||||||
parser.write_database(session)
|
parser.write_database(session)
|
||||||
|
|||||||
@ -154,23 +154,17 @@ class TabDb_RemoveDuplicateScores(Ui_TabDb_RemoveDuplicateScores, QWidget):
|
|||||||
self.treeView.setItemDelegateForColumn(1, self.treeViewProxyDelegate)
|
self.treeView.setItemDelegateForColumn(1, self.treeViewProxyDelegate)
|
||||||
|
|
||||||
self.quickSelect_comboBox.addItem(
|
self.quickSelect_comboBox.addItem(
|
||||||
# fmt: off
|
|
||||||
QCoreApplication.translate("TabDb_RemoveDuplicateScores", "quickSelectComboBox.idEarlier"),
|
QCoreApplication.translate("TabDb_RemoveDuplicateScores", "quickSelectComboBox.idEarlier"),
|
||||||
# fmt: on
|
|
||||||
QuickSelectComboBoxValues.ID_EARLIER
|
QuickSelectComboBoxValues.ID_EARLIER
|
||||||
)
|
) # fmt: skip
|
||||||
self.quickSelect_comboBox.addItem(
|
self.quickSelect_comboBox.addItem(
|
||||||
# fmt: off
|
|
||||||
QCoreApplication.translate("TabDb_RemoveDuplicateScores", "quickSelectComboBox.dateEarlier"),
|
QCoreApplication.translate("TabDb_RemoveDuplicateScores", "quickSelectComboBox.dateEarlier"),
|
||||||
# fmt: on
|
|
||||||
QuickSelectComboBoxValues.DATE_EARLIER
|
QuickSelectComboBoxValues.DATE_EARLIER
|
||||||
)
|
) # fmt: skip
|
||||||
self.quickSelect_comboBox.addItem(
|
self.quickSelect_comboBox.addItem(
|
||||||
# fmt: off
|
|
||||||
QCoreApplication.translate("TabDb_RemoveDuplicateScores", "quickSelectComboBox.columnsIntegral"),
|
QCoreApplication.translate("TabDb_RemoveDuplicateScores", "quickSelectComboBox.columnsIntegral"),
|
||||||
# fmt: on
|
|
||||||
QuickSelectComboBoxValues.COLUMNS_INTEGRAL
|
QuickSelectComboBoxValues.COLUMNS_INTEGRAL
|
||||||
)
|
) # fmt: skip
|
||||||
|
|
||||||
def getQueryColumns(self):
|
def getQueryColumns(self):
|
||||||
columns: list[InstrumentedAttribute] = [Score.song_id, Score.rating_class]
|
columns: list[InstrumentedAttribute] = [Score.song_id, Score.rating_class]
|
||||||
@ -291,12 +285,12 @@ class TabDb_RemoveDuplicateScores(Ui_TabDb_RemoveDuplicateScores, QWidget):
|
|||||||
confirm = QMessageBox.warning(
|
confirm = QMessageBox.warning(
|
||||||
self,
|
self,
|
||||||
None,
|
None,
|
||||||
# fmt: off
|
QCoreApplication.translate(
|
||||||
QCoreApplication.translate("TabDb_RemoveDuplicateScores", "deleteSelectionDialog.content {}").format(len(selectedScores)),
|
"TabDb_RemoveDuplicateScores", "deleteSelectionDialog.content {}"
|
||||||
# fmt: on
|
).format(len(selectedScores)),
|
||||||
QMessageBox.StandardButton.Yes,
|
QMessageBox.StandardButton.Yes,
|
||||||
QMessageBox.StandardButton.No,
|
QMessageBox.StandardButton.No,
|
||||||
)
|
) # fmt: skip
|
||||||
if confirm != QMessageBox.StandardButton.Yes:
|
if confirm != QMessageBox.StandardButton.Yes:
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -310,12 +304,11 @@ class TabDb_RemoveDuplicateScores(Ui_TabDb_RemoveDuplicateScores, QWidget):
|
|||||||
@Slot()
|
@Slot()
|
||||||
def on_scan_scanButton_clicked(self):
|
def on_scan_scanButton_clicked(self):
|
||||||
if len(self.getQueryColumns()) <= 2:
|
if len(self.getQueryColumns()) <= 2:
|
||||||
|
message = QCoreApplication.translate("TabDb_RemoveDuplicateScores", "scan_noColumnsDialog.content") # fmt: skip
|
||||||
result = QMessageBox.warning(
|
result = QMessageBox.warning(
|
||||||
self,
|
self,
|
||||||
None,
|
None,
|
||||||
# fmt: off
|
message,
|
||||||
QCoreApplication.translate("TabDb_RemoveDuplicateScores", "scan_noColumnsDialog.content"),
|
|
||||||
# fmt: on
|
|
||||||
QMessageBox.StandardButton.Yes,
|
QMessageBox.StandardButton.Yes,
|
||||||
QMessageBox.StandardButton.No,
|
QMessageBox.StandardButton.No,
|
||||||
)
|
)
|
||||||
|
|||||||
@ -9,6 +9,7 @@ from PIL import Image
|
|||||||
from PySide6.QtCore import Signal, Slot
|
from PySide6.QtCore import Signal, Slot
|
||||||
from PySide6.QtWidgets import QFileDialog, QMessageBox, QWidget
|
from PySide6.QtWidgets import QFileDialog, QMessageBox, QWidget
|
||||||
|
|
||||||
|
from core.settings import SettingsKeys
|
||||||
from ui.designer.tabs.tabOcr.tabOcr_B30_ui import Ui_TabOcr_B30
|
from ui.designer.tabs.tabOcr.tabOcr_B30_ui import Ui_TabOcr_B30
|
||||||
from ui.extends.components.ocrQueue import OcrQueueModel
|
from ui.extends.components.ocrQueue import OcrQueueModel
|
||||||
from ui.extends.ocr.dependencies import (
|
from ui.extends.ocr.dependencies import (
|
||||||
@ -16,11 +17,6 @@ from ui.extends.ocr.dependencies import (
|
|||||||
getPhashDatabaseStatusText,
|
getPhashDatabaseStatusText,
|
||||||
)
|
)
|
||||||
from ui.extends.shared.language import LanguageChangeEventFilter
|
from ui.extends.shared.language import LanguageChangeEventFilter
|
||||||
from ui.extends.shared.settings import (
|
|
||||||
B30_KNN_MODEL_FILE,
|
|
||||||
KNN_MODEL_FILE,
|
|
||||||
PHASH_DATABASE_FILE,
|
|
||||||
)
|
|
||||||
from ui.extends.tabs.tabOcr.tabOcr_B30 import ChieriV4OcrRunnable, b30ResultToScore
|
from ui.extends.tabs.tabOcr.tabOcr_B30 import ChieriV4OcrRunnable, b30ResultToScore
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@ -55,9 +51,15 @@ class TabOcr_B30(Ui_TabOcr_B30, QWidget):
|
|||||||
self.ocr = None
|
self.ocr = None
|
||||||
|
|
||||||
logger.info("Applying settings...")
|
logger.info("Applying settings...")
|
||||||
self.dependencies_knnModelSelector.connectSettings(KNN_MODEL_FILE)
|
self.dependencies_knnModelSelector.connectSettings(
|
||||||
self.dependencies_b30KnnModelSelector.connectSettings(B30_KNN_MODEL_FILE)
|
SettingsKeys.Ocr.KnnModelFile
|
||||||
self.dependencies_phashDatabaseSelector.connectSettings(PHASH_DATABASE_FILE)
|
)
|
||||||
|
self.dependencies_b30KnnModelSelector.connectSettings(
|
||||||
|
SettingsKeys.Ocr.B30KnnModelFile
|
||||||
|
)
|
||||||
|
self.dependencies_phashDatabaseSelector.connectSettings(
|
||||||
|
SettingsKeys.Ocr.PhashDatabaseFile
|
||||||
|
)
|
||||||
|
|
||||||
self.ocrQueueModel = OcrQueueModel(self)
|
self.ocrQueueModel = OcrQueueModel(self)
|
||||||
self.ocrQueue.setModel(self.ocrQueueModel)
|
self.ocrQueue.setModel(self.ocrQueueModel)
|
||||||
|
|||||||
@ -11,10 +11,10 @@ from arcaea_offline_ocr.phash_db import ImagePhashDatabase
|
|||||||
from PySide6.QtCore import Slot
|
from PySide6.QtCore import Slot
|
||||||
from PySide6.QtWidgets import QApplication, QFileDialog, QMessageBox, QWidget
|
from PySide6.QtWidgets import QApplication, QFileDialog, QMessageBox, QWidget
|
||||||
|
|
||||||
|
from core.settings import SettingsKeys
|
||||||
from ui.designer.tabs.tabOcr.tabOcr_Device_ui import Ui_TabOcr_Device
|
from ui.designer.tabs.tabOcr.tabOcr_Device_ui import Ui_TabOcr_Device
|
||||||
from ui.extends.components.ocrQueue import OcrQueueModel
|
from ui.extends.components.ocrQueue import OcrQueueModel
|
||||||
from ui.extends.shared.language import LanguageChangeEventFilter
|
from ui.extends.shared.language import LanguageChangeEventFilter
|
||||||
from ui.extends.shared.settings import KNN_MODEL_FILE, PHASH_DATABASE_FILE
|
|
||||||
from ui.extends.tabs.tabOcr.tabOcr_Device import ScoreConverter, TabDeviceOcrRunnable
|
from ui.extends.tabs.tabOcr.tabOcr_Device import ScoreConverter, TabDeviceOcrRunnable
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@ -54,8 +54,12 @@ class TabOcr_Device(Ui_TabOcr_Device, QWidget):
|
|||||||
)
|
)
|
||||||
|
|
||||||
logger.info("Applying settings...")
|
logger.info("Applying settings...")
|
||||||
self.dependencies_knnModelSelector.connectSettings(KNN_MODEL_FILE)
|
self.dependencies_knnModelSelector.connectSettings(
|
||||||
self.dependencies_phashDatabaseSelector.connectSettings(PHASH_DATABASE_FILE)
|
SettingsKeys.Ocr.KnnModelFile
|
||||||
|
)
|
||||||
|
self.dependencies_phashDatabaseSelector.connectSettings(
|
||||||
|
SettingsKeys.Ocr.PhashDatabaseFile
|
||||||
|
)
|
||||||
|
|
||||||
self.options_usePresetCheckBox.setChecked(True)
|
self.options_usePresetCheckBox.setChecked(True)
|
||||||
self.options_usePresetCheckBox.setEnabled(False)
|
self.options_usePresetCheckBox.setEnabled(False)
|
||||||
|
|||||||
@ -1,55 +0,0 @@
|
|||||||
import logging
|
|
||||||
|
|
||||||
from arcaea_offline.database import Database
|
|
||||||
from PySide6.QtCore import QCoreApplication
|
|
||||||
from PySide6.QtGui import QShowEvent
|
|
||||||
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):
|
|
||||||
super().__init__(parent)
|
|
||||||
self.setupUi(self)
|
|
||||||
|
|
||||||
self.languageChangeEventFilter = LanguageChangeEventFilter(self)
|
|
||||||
self.installEventFilter(self.languageChangeEventFilter)
|
|
||||||
|
|
||||||
self.db = Database()
|
|
||||||
|
|
||||||
def showEvent(self, event: QShowEvent) -> None:
|
|
||||||
self.updateOverview()
|
|
||||||
return super().showEvent(event)
|
|
||||||
|
|
||||||
def updateOverview(self):
|
|
||||||
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)
|
|
||||||
# fmt: off
|
|
||||||
self.describeFormatString = QCoreApplication.translate("TabOverview", "databaseDescribeLabel {} {} {} {} {} {}")
|
|
||||||
# fmt: on
|
|
||||||
@ -11,9 +11,9 @@ from PySide6.QtCore import QCoreApplication, QDir, QFileInfo, Qt, Slot
|
|||||||
from PySide6.QtGui import QGuiApplication, QImage, QPainter, QPaintEvent, QPixmap
|
from PySide6.QtGui import QGuiApplication, QImage, QPainter, QPaintEvent, QPixmap
|
||||||
from PySide6.QtWidgets import QButtonGroup, QFileDialog, QLabel, QMessageBox, QWidget
|
from PySide6.QtWidgets import QButtonGroup, QFileDialog, QLabel, QMessageBox, QWidget
|
||||||
|
|
||||||
|
from core.settings import SettingsKeys
|
||||||
from ui.designer.tabs.tabTools.tabTools_Andreal_ui import Ui_TabTools_Andreal
|
from ui.designer.tabs.tabTools.tabTools_Andreal_ui import Ui_TabTools_Andreal
|
||||||
from ui.extends.shared.language import LanguageChangeEventFilter
|
from ui.extends.shared.language import LanguageChangeEventFilter
|
||||||
from ui.extends.shared.settings import ANDREAL_EXECUTABLE, ANDREAL_FOLDER
|
|
||||||
from ui.extends.tabs.tabTools.tabTools_Andreal import AndrealHelper
|
from ui.extends.tabs.tabTools.tabTools_Andreal import AndrealHelper
|
||||||
from ui.implements.components.chartSelector import ChartSelector
|
from ui.implements.components.chartSelector import ChartSelector
|
||||||
from ui.implements.components.songIdSelector import SongIdSelectorMode
|
from ui.implements.components.songIdSelector import SongIdSelectorMode
|
||||||
@ -80,8 +80,8 @@ class TabTools_Andreal(Ui_TabTools_Andreal, QWidget):
|
|||||||
self.andrealFolderSelector.filesSelected.connect(self.setHelperPaths)
|
self.andrealFolderSelector.filesSelected.connect(self.setHelperPaths)
|
||||||
self.andrealExecutableSelector.filesSelected.connect(self.setHelperPaths)
|
self.andrealExecutableSelector.filesSelected.connect(self.setHelperPaths)
|
||||||
|
|
||||||
self.andrealFolderSelector.connectSettings(ANDREAL_FOLDER)
|
self.andrealFolderSelector.connectSettings(SettingsKeys.Andreal.Folder)
|
||||||
self.andrealExecutableSelector.connectSettings(ANDREAL_EXECUTABLE)
|
self.andrealExecutableSelector.connectSettings(SettingsKeys.Andreal.Executable)
|
||||||
|
|
||||||
self.generatePreviewButton.clicked.connect(self.requestPreview)
|
self.generatePreviewButton.clicked.connect(self.requestPreview)
|
||||||
self.generateImageButton.clicked.connect(self.requestGenerate)
|
self.generateImageButton.clicked.connect(self.requestGenerate)
|
||||||
@ -131,13 +131,8 @@ class TabTools_Andreal(Ui_TabTools_Andreal, QWidget):
|
|||||||
|
|
||||||
@Slot()
|
@Slot()
|
||||||
def on_imageTypeWhatIsThisButton_clicked(self):
|
def on_imageTypeWhatIsThisButton_clicked(self):
|
||||||
QMessageBox.information(
|
message = QCoreApplication.translate("TabTools_Andreal", "imageWhatIsThisDialog.description") # fmt: skip
|
||||||
self,
|
QMessageBox.information(self, None, message)
|
||||||
None,
|
|
||||||
# fmt: off
|
|
||||||
QCoreApplication.translate("TabTools_Andreal", "imageWhatIsThisDialog.description"),
|
|
||||||
# fmt: on
|
|
||||||
)
|
|
||||||
|
|
||||||
def imageFormat(self):
|
def imageFormat(self):
|
||||||
buttonId = self.imageFormatButtonGroup.checkedId()
|
buttonId = self.imageFormatButtonGroup.checkedId()
|
||||||
|
|||||||
@ -90,10 +90,8 @@ class PlayRatingCalculatorDialog(QDialog):
|
|||||||
|
|
||||||
self.acceptButton = QPushButton(self)
|
self.acceptButton = QPushButton(self)
|
||||||
self.acceptButton.setText(
|
self.acceptButton.setText(
|
||||||
# fmt: off
|
|
||||||
QCoreApplication.translate("StepCalculator", "playRatingCalculatorDialog.acceptButton")
|
QCoreApplication.translate("StepCalculator", "playRatingCalculatorDialog.acceptButton")
|
||||||
# fmt: on
|
) # fmt: skip
|
||||||
)
|
|
||||||
self.acceptButton.setEnabled(False)
|
self.acceptButton.setEnabled(False)
|
||||||
self.verticalLayout.addWidget(self.acceptButton)
|
self.verticalLayout.addWidget(self.acceptButton)
|
||||||
|
|
||||||
|
|||||||
22
ui/qmls/404.qml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
|
||||||
|
Page {
|
||||||
|
Column {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
Label {
|
||||||
|
width: parent.width
|
||||||
|
text: '?'
|
||||||
|
font.pointSize: 50
|
||||||
|
horizontalAlignment: Qt.AlignCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
width: parent.width
|
||||||
|
text: 'Placeholder page'
|
||||||
|
horizontalAlignment: Qt.AlignCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
63
ui/qmls/App.qml
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
import "./DatabaseChecker" as DatabaseChecker
|
||||||
|
|
||||||
|
ApplicationWindow {
|
||||||
|
visible: true
|
||||||
|
|
||||||
|
width: 800
|
||||||
|
height: 600
|
||||||
|
|
||||||
|
SystemPalette {
|
||||||
|
id: systemPaletteActive
|
||||||
|
colorGroup: SystemPalette.Active
|
||||||
|
}
|
||||||
|
|
||||||
|
SystemPalette {
|
||||||
|
id: systemPaletteDisabled
|
||||||
|
colorGroup: SystemPalette.Disabled
|
||||||
|
}
|
||||||
|
|
||||||
|
palette {
|
||||||
|
accent: systemPaletteActive.accent
|
||||||
|
alternateBase: systemPaletteActive.alternateBase
|
||||||
|
base: systemPaletteActive.base
|
||||||
|
button: systemPaletteActive.button
|
||||||
|
buttonText: systemPaletteActive.buttonText
|
||||||
|
dark: systemPaletteActive.dark
|
||||||
|
highlight: systemPaletteActive.highlight
|
||||||
|
highlightedText: systemPaletteActive.highlightedText
|
||||||
|
light: systemPaletteActive.light
|
||||||
|
mid: systemPaletteActive.mid
|
||||||
|
midlight: systemPaletteActive.midlight
|
||||||
|
placeholderText: systemPaletteActive.placeholderText
|
||||||
|
shadow: systemPaletteActive.shadow
|
||||||
|
text: systemPaletteActive.text
|
||||||
|
window: systemPaletteActive.window
|
||||||
|
windowText: systemPaletteActive.windowText
|
||||||
|
|
||||||
|
disabled {
|
||||||
|
button: systemPaletteDisabled.button
|
||||||
|
buttonText: systemPaletteDisabled.buttonText
|
||||||
|
}
|
||||||
|
|
||||||
|
inactive {
|
||||||
|
button: systemPaletteDisabled.button
|
||||||
|
buttonText: systemPaletteDisabled.buttonText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StackLayout {
|
||||||
|
id: stackLayout
|
||||||
|
anchors.fill: parent
|
||||||
|
currentIndex: 0
|
||||||
|
|
||||||
|
DatabaseChecker.Index {
|
||||||
|
onReady: parent.currentIndex = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
AppMain {}
|
||||||
|
}
|
||||||
|
}
|
||||||
93
ui/qmls/AppMain.qml
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: layout
|
||||||
|
spacing: 5
|
||||||
|
|
||||||
|
SystemPalette {
|
||||||
|
id: systemPalette
|
||||||
|
}
|
||||||
|
|
||||||
|
ListModel {
|
||||||
|
id: navListModel
|
||||||
|
|
||||||
|
ListElement {
|
||||||
|
_id: 'home'
|
||||||
|
label: 'Overview'
|
||||||
|
qmlSource: 'Overview.qml'
|
||||||
|
}
|
||||||
|
ListElement {
|
||||||
|
_id: 'database'
|
||||||
|
label: 'Database'
|
||||||
|
qmlSource: '404.qml'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
id: navListView
|
||||||
|
|
||||||
|
Layout.preferredWidth: 200
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
model: navListModel
|
||||||
|
focus: true
|
||||||
|
|
||||||
|
delegate: Item {
|
||||||
|
id: navListItem
|
||||||
|
required property int index
|
||||||
|
required property string label
|
||||||
|
|
||||||
|
property bool isActive: navListView.currentIndex === index
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
height: 30
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: () => {
|
||||||
|
navListView.currentIndex = navListItem.index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
anchors.margins: 5
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
text: parent.label
|
||||||
|
color: parent.isActive ? systemPalette.highlightedText : systemPalette.text
|
||||||
|
z: 10
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: 150
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: parent.isActive ? parent.width : 0
|
||||||
|
Behavior on width {
|
||||||
|
NumberAnimation {
|
||||||
|
easing.type: Easing.OutQuad
|
||||||
|
duration: 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
height: parent.height
|
||||||
|
color: systemPalette.highlight
|
||||||
|
z: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
Layout.preferredWidth: 500
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
source: navListView.currentIndex > -1 ? navListModel.get(navListView.currentIndex).qmlSource : '404.qml'
|
||||||
|
}
|
||||||
|
}
|
||||||
25
ui/qmls/Components/DirectorySelector.qml
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Dialogs
|
||||||
|
|
||||||
|
import internal.ui.utils
|
||||||
|
|
||||||
|
SelectorBase {
|
||||||
|
id: base
|
||||||
|
|
||||||
|
FolderDialog {
|
||||||
|
id: folderDialog
|
||||||
|
selectedFolder: base.directoryUrl
|
||||||
|
onAccepted: {
|
||||||
|
base.directoryUrl = this.selectedFolder;
|
||||||
|
this.currentFolder = this.selectedFolder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
property alias directoryUrl: base.url
|
||||||
|
|
||||||
|
shouldAcceptUrl: url => UrlUtils.isDir(url)
|
||||||
|
onBrowseButtonClicked: {
|
||||||
|
folderDialog.open();
|
||||||
|
}
|
||||||
|
placeholderText: '<font color="gray">Select a directory…</font>'
|
||||||
|
}
|
||||||
33
ui/qmls/Components/FileSelector.qml
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Dialogs
|
||||||
|
|
||||||
|
import internal.ui.utils
|
||||||
|
|
||||||
|
SelectorBase {
|
||||||
|
id: base
|
||||||
|
|
||||||
|
FileDialog {
|
||||||
|
id: fileDialog
|
||||||
|
onAccepted: {
|
||||||
|
base.url = this.selectedFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isFileUrlValid(url: url): bool {
|
||||||
|
return url.toString().startsWith("file://");
|
||||||
|
}
|
||||||
|
|
||||||
|
property alias fileUrl: base.url
|
||||||
|
onFileUrlChanged: {
|
||||||
|
if (isFileUrlValid(fileUrl)) {
|
||||||
|
fileDialog.selectedFile = fileUrl;
|
||||||
|
fileDialog.currentFolder = UrlUtils.parent(fileUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
shouldAcceptUrl: url => UrlUtils.isFile(url)
|
||||||
|
onBrowseButtonClicked: {
|
||||||
|
fileDialog.open();
|
||||||
|
}
|
||||||
|
placeholderText: '<font color="gray">Select a file…</font>'
|
||||||
|
}
|
||||||
124
ui/qmls/Components/PlayResultDelegate.qml
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import QtQuick.VectorImage
|
||||||
|
|
||||||
|
import "../libs/formatters.mjs" as Formatters
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property var playResult
|
||||||
|
property alias pr: root.playResult
|
||||||
|
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
|
SystemPalette {
|
||||||
|
id: systemPalette
|
||||||
|
}
|
||||||
|
|
||||||
|
component PFLLabel: RowLayout {
|
||||||
|
required property string label
|
||||||
|
required property var value
|
||||||
|
property color color: systemPalette.text
|
||||||
|
|
||||||
|
spacing: 0.5
|
||||||
|
|
||||||
|
Label {
|
||||||
|
Layout.alignment: Qt.AlignBaseline
|
||||||
|
|
||||||
|
text: parent.label
|
||||||
|
font.pointSize: 8
|
||||||
|
font.bold: true
|
||||||
|
color: parent.color
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
Layout.alignment: Qt.AlignBaseline
|
||||||
|
text: parent.value ?? '-'
|
||||||
|
color: parent.color
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getGradeIcon(gradeLabel: string): string {
|
||||||
|
const scheme = Application.styleHints.colorScheme == Qt.ColorScheme.Dark ? 'dark' : 'light';
|
||||||
|
const filenameMap = {
|
||||||
|
'EX+': 'ex-plus',
|
||||||
|
'EX': 'ex',
|
||||||
|
'AA': 'aa',
|
||||||
|
'A': 'a',
|
||||||
|
'B': 'b',
|
||||||
|
'C': 'c',
|
||||||
|
'D': 'd'
|
||||||
|
};
|
||||||
|
|
||||||
|
let filenameBase = filenameMap[gradeLabel];
|
||||||
|
if (scheme === 'dark') {
|
||||||
|
filenameBase += '-dark';
|
||||||
|
}
|
||||||
|
|
||||||
|
return `qrc:/images/grades/${filenameBase}.svg`;
|
||||||
|
}
|
||||||
|
|
||||||
|
TextMetrics {
|
||||||
|
id: gradeTextMetrics
|
||||||
|
text: 'EX+'
|
||||||
|
|
||||||
|
font.pointSize: 18
|
||||||
|
font.bold: true
|
||||||
|
}
|
||||||
|
|
||||||
|
VectorImage {
|
||||||
|
id: gradeIcon
|
||||||
|
Layout.preferredWidth: gradeTextMetrics.width
|
||||||
|
Layout.preferredHeight: gradeTextMetrics.width
|
||||||
|
|
||||||
|
fillMode: VectorImage.PreserveAspectFit
|
||||||
|
preferredRendererType: VectorImage.CurveRenderer
|
||||||
|
source: root.getGradeIcon(Formatters.scoreToGrade(root.pr.score))
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: 1
|
||||||
|
|
||||||
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: root.pr.score
|
||||||
|
font.pointSize: 16
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: 2
|
||||||
|
spacing: 5
|
||||||
|
|
||||||
|
PFLLabel {
|
||||||
|
label: 'P'
|
||||||
|
value: root.pr.pure
|
||||||
|
color: appTheme.pure
|
||||||
|
}
|
||||||
|
|
||||||
|
PFLLabel {
|
||||||
|
label: 'F'
|
||||||
|
value: root.pr.far
|
||||||
|
color: appTheme.far
|
||||||
|
}
|
||||||
|
|
||||||
|
PFLLabel {
|
||||||
|
label: 'L'
|
||||||
|
value: root.pr.lost
|
||||||
|
color: appTheme.lost
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PFLLabel {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: 2
|
||||||
|
|
||||||
|
label: 'MR'
|
||||||
|
value: root.pr.maxRecall
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
13
ui/qmls/Components/SectionTitle.qml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
Label {
|
||||||
|
Layout.topMargin: 7
|
||||||
|
Layout.bottomMargin: 10
|
||||||
|
|
||||||
|
anchors.topMargin: 7
|
||||||
|
anchors.bottomMargin: 10
|
||||||
|
|
||||||
|
font.pointSize: 14
|
||||||
|
font.bold: true
|
||||||
|
}
|
||||||
64
ui/qmls/Components/SelectorBase.qml
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
import internal.ui.utils
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property var shouldAcceptUrl // function (url)
|
||||||
|
signal browseButtonClicked
|
||||||
|
property string placeholderText: '<font color="gray">…</font>'
|
||||||
|
|
||||||
|
required property url url
|
||||||
|
onUrlChanged: {
|
||||||
|
Qt.callLater(() => {
|
||||||
|
updateLabel();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateLabel(): void {
|
||||||
|
urlLabel.text = url.toString().length > 0 ? UrlFormatUtils.toLocalFile(url) : root.placeholderText;
|
||||||
|
}
|
||||||
|
|
||||||
|
spacing: 2
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: urlLabel
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: root.placeholderText
|
||||||
|
|
||||||
|
DropArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
onEntered: drag => {
|
||||||
|
if (!drag.hasUrls || drag.urls.length !== 1) {
|
||||||
|
drag.accepted = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = drag.urls[0];
|
||||||
|
const shouldAccept = root.shouldAcceptUrl(url);
|
||||||
|
if (!shouldAccept) {
|
||||||
|
drag.accepted = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
urlLabel.text = `<font color="gray">Drop "<font color="text">${UrlUtils.name(url)}</font>"?</font>`;
|
||||||
|
}
|
||||||
|
onExited: {
|
||||||
|
root.updateLabel();
|
||||||
|
}
|
||||||
|
onDropped: drop => {
|
||||||
|
root.url = drop.urls[0];
|
||||||
|
root.updateLabel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
text: "Browse"
|
||||||
|
onClicked: root.browseButtonClicked()
|
||||||
|
}
|
||||||
|
}
|
||||||
9
ui/qmls/Components/qmldir
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
module Components
|
||||||
|
|
||||||
|
internal SelectorBase SelectorBase.qml
|
||||||
|
DirectorySelector 1.0 DirectorySelector.qml
|
||||||
|
FileSelector 1.0 FileSelector.qml
|
||||||
|
|
||||||
|
SectionTitle 1.0 SectionTitle.qml
|
||||||
|
|
||||||
|
PlayResultDelegate 1.0 PlayResultDelegate.qml
|
||||||
56
ui/qmls/DatabaseChecker/DatabaseFileCreator.qml
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
import "../Components"
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
signal confirm
|
||||||
|
|
||||||
|
required property url directoryUrl
|
||||||
|
required property string filename
|
||||||
|
|
||||||
|
GridLayout {
|
||||||
|
columns: 2
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: "Directory"
|
||||||
|
}
|
||||||
|
|
||||||
|
DirectorySelector {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
directoryUrl: root.directoryUrl
|
||||||
|
onDirectoryUrlChanged: {
|
||||||
|
root.directoryUrl = this.directoryUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: "Filename"
|
||||||
|
}
|
||||||
|
|
||||||
|
TextField {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
text: root.filename
|
||||||
|
placeholderText: 'Please enter…'
|
||||||
|
onEditingFinished: {
|
||||||
|
root.filename = this.text;
|
||||||
|
}
|
||||||
|
onAccepted: {
|
||||||
|
confirmButton.click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
id: confirmButton
|
||||||
|
Layout.alignment: Qt.AlignRight
|
||||||
|
|
||||||
|
text: 'Confirm'
|
||||||
|
onClicked: root.confirm()
|
||||||
|
}
|
||||||
|
}
|
||||||
31
ui/qmls/DatabaseChecker/Dialog_ConfirmConnection.qml
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
Dialog {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property string databaseUrl
|
||||||
|
|
||||||
|
padding: 10
|
||||||
|
|
||||||
|
title: qsTr('Confirm Database Connection')
|
||||||
|
standardButtons: Dialog.Ok | Dialog.Cancel
|
||||||
|
modal: true
|
||||||
|
Overlay.modal: Rectangle {
|
||||||
|
color: Qt.alpha('gray', 0.2)
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Label {
|
||||||
|
text: 'The connection below will be saved to settings. Confirm?'
|
||||||
|
}
|
||||||
|
|
||||||
|
Pane {
|
||||||
|
Label {
|
||||||
|
id: databaseUrlLabel
|
||||||
|
text: root.databaseUrl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
34
ui/qmls/DatabaseChecker/Dialog_Error.qml
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
Dialog {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property string errorTitle
|
||||||
|
required property string errorMessage
|
||||||
|
|
||||||
|
padding: 10
|
||||||
|
|
||||||
|
title: qsTr('Error')
|
||||||
|
standardButtons: Dialog.Ok
|
||||||
|
modal: true
|
||||||
|
Overlay.modal: Rectangle {
|
||||||
|
color: Qt.alpha('darkgray', 0.5)
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
spacing: 5
|
||||||
|
|
||||||
|
Label {
|
||||||
|
font.pointSize: 12
|
||||||
|
font.bold: true
|
||||||
|
|
||||||
|
text: root.errorTitle
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: root.errorMessage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
13
ui/qmls/DatabaseChecker/HorizontalDivider.qml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 1
|
||||||
|
|
||||||
|
Layout.topMargin: 5
|
||||||
|
Layout.bottomMargin: 5
|
||||||
|
|
||||||
|
color: "lightgray"
|
||||||
|
opacity: 0.5
|
||||||
|
}
|
||||||
124
ui/qmls/DatabaseChecker/Index.qml
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
import internal.ui.vm 1.0
|
||||||
|
import "../Components"
|
||||||
|
|
||||||
|
Page {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property bool showConfirmConnectionDialog: false
|
||||||
|
|
||||||
|
signal ready
|
||||||
|
|
||||||
|
function confirm(source: string): void {
|
||||||
|
if (!source)
|
||||||
|
throw new Error("source is required");
|
||||||
|
|
||||||
|
const shouldConfirm = (source === 'continueButton' && vm.canConfirmSilently) || source === 'dialog';
|
||||||
|
|
||||||
|
if (shouldConfirm) {
|
||||||
|
vm.confirmCurrentConnection();
|
||||||
|
root.ready();
|
||||||
|
} else {
|
||||||
|
root.showConfirmConnectionDialog = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DatabaseInitViewModel {
|
||||||
|
id: vm
|
||||||
|
|
||||||
|
onSelectFileUrlChanged: {
|
||||||
|
selectOrCreate.selectFileUrl = this.selectFileUrl;
|
||||||
|
}
|
||||||
|
onDirectoryUrlChanged: {
|
||||||
|
selectOrCreate.directoryUrl = this.directoryUrl;
|
||||||
|
}
|
||||||
|
onFilenameChanged: {
|
||||||
|
selectOrCreate.filename = this.filename;
|
||||||
|
}
|
||||||
|
onDatabaseUrlChanged: {
|
||||||
|
confirmConnectionDialog.databaseUrl = this.databaseUrl;
|
||||||
|
}
|
||||||
|
onDatabaseInfoChanged: {
|
||||||
|
databaseInfo.info = this.databaseInfo;
|
||||||
|
}
|
||||||
|
onCanContinueChanged: {
|
||||||
|
continueButton.enabled = this.canContinue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Page {
|
||||||
|
padding: 10
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
Dialog_ConfirmConnection {
|
||||||
|
id: confirmConnectionDialog
|
||||||
|
|
||||||
|
anchors.centerIn: parent
|
||||||
|
visible: root.showConfirmConnectionDialog
|
||||||
|
|
||||||
|
onAccepted: root.confirm('dialog')
|
||||||
|
onClosed: root.showConfirmConnectionDialog = false
|
||||||
|
|
||||||
|
databaseUrl: vm.databaseUrl
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
width: parent.width
|
||||||
|
spacing: 2
|
||||||
|
|
||||||
|
SectionTitle {
|
||||||
|
text: qsTr('Select or Create Database')
|
||||||
|
}
|
||||||
|
|
||||||
|
Section_SelectOrCreate {
|
||||||
|
id: selectOrCreate
|
||||||
|
selectFileUrl: vm.selectFileUrl
|
||||||
|
createDirectoryUrl: vm.directoryUrl
|
||||||
|
createFilename: vm.filename
|
||||||
|
|
||||||
|
onUiModeChanged: uiMode => vm.uiMode = uiMode
|
||||||
|
onSelectFileUrlChanged: {
|
||||||
|
vm.selectFileUrl = selectFileUrl;
|
||||||
|
}
|
||||||
|
onCreateDirectoryUrlChanged: {
|
||||||
|
vm.directoryUrl = createDirectoryUrl;
|
||||||
|
}
|
||||||
|
onCreateFilenameChanged: {
|
||||||
|
vm.filename = createFilename;
|
||||||
|
}
|
||||||
|
onConfirmCreate: {
|
||||||
|
vm.loadDatabaseInfo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HorizontalDivider {}
|
||||||
|
|
||||||
|
SectionTitle {
|
||||||
|
text: 'Database Status'
|
||||||
|
}
|
||||||
|
|
||||||
|
Section_DatabaseInfo {
|
||||||
|
id: databaseInfo
|
||||||
|
info: vm.databaseInfo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
footer: Pane {
|
||||||
|
padding: 5
|
||||||
|
RowLayout {
|
||||||
|
Button {
|
||||||
|
id: continueButton
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
|
||||||
|
text: qsTr('Continue')
|
||||||
|
enabled: vm.canContinue
|
||||||
|
|
||||||
|
onClicked: root.confirm('continueButton')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
137
ui/qmls/DatabaseChecker/Section_DatabaseInfo.qml
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property var info: {
|
||||||
|
'url': undefined,
|
||||||
|
'error': {
|
||||||
|
'title': undefined,
|
||||||
|
'message': undefined
|
||||||
|
},
|
||||||
|
'initialized': undefined,
|
||||||
|
'version': undefined
|
||||||
|
}
|
||||||
|
property bool showErrorDialog: false
|
||||||
|
|
||||||
|
function hasError(): bool {
|
||||||
|
return info?.error?.title !== undefined || info?.error?.message !== undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function displayText(value): string {
|
||||||
|
return value ?? '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
function displayBool(value): string {
|
||||||
|
if (value === undefined)
|
||||||
|
return '-';
|
||||||
|
return value ? `<font color="${appTheme.success}">Yes</font>` : `<font color="${appTheme.error}">No</font>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
component LabelLabel: Label {
|
||||||
|
Layout.alignment: Qt.AlignRight | Qt.AlignBaseline
|
||||||
|
font.pointSize: 10
|
||||||
|
}
|
||||||
|
|
||||||
|
SystemPalette {
|
||||||
|
id: palette
|
||||||
|
}
|
||||||
|
|
||||||
|
Dialog_Error {
|
||||||
|
parent: Overlay.overlay
|
||||||
|
|
||||||
|
anchors.centerIn: parent
|
||||||
|
visible: root.hasError() && root.showErrorDialog
|
||||||
|
|
||||||
|
errorTitle: root.displayText(root.info?.error?.title)
|
||||||
|
errorMessage: root.displayText(root.info?.error?.message)
|
||||||
|
|
||||||
|
onClosed: root.showErrorDialog = false
|
||||||
|
}
|
||||||
|
|
||||||
|
Pane {
|
||||||
|
clip: true
|
||||||
|
background: Rectangle {
|
||||||
|
color: Qt.darker(Qt.alpha(palette.window, 0.9), 0.2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Layout.preferredHeight: root.hasError() ? this.implicitHeight : 0
|
||||||
|
Layout.preferredHeight: 0
|
||||||
|
Behavior on Layout.preferredHeight {
|
||||||
|
PropertyAnimation {
|
||||||
|
duration: 300
|
||||||
|
easing.type: Easing.InOutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
Label {
|
||||||
|
font.bold: true
|
||||||
|
text: root.displayText(root.info.error?.title)
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: root.displayText(root.info.error?.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GridLayout {
|
||||||
|
columns: 2
|
||||||
|
columnSpacing: 10
|
||||||
|
|
||||||
|
LabelLabel {
|
||||||
|
text: 'Connection'
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: root.info.url
|
||||||
|
}
|
||||||
|
|
||||||
|
LabelLabel {
|
||||||
|
text: 'Initialized'
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: root.displayBool(root.info?.initialized)
|
||||||
|
}
|
||||||
|
|
||||||
|
LabelLabel {
|
||||||
|
text: 'Version'
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: root.displayText(root.info?.version)
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
Layout.alignment: Qt.AlignRight | Qt.AlignBaseline
|
||||||
|
|
||||||
|
LabelLabel {
|
||||||
|
text: 'Error'
|
||||||
|
}
|
||||||
|
|
||||||
|
ToolButton {
|
||||||
|
Layout.preferredWidth: root.hasError() ? this.implicitWidth : 0
|
||||||
|
Behavior on Layout.preferredWidth {
|
||||||
|
PropertyAnimation {
|
||||||
|
duration: 300
|
||||||
|
easing.type: Easing.InOutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
text: '[?]'
|
||||||
|
onClicked: root.showErrorDialog = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
Layout.alignment: Qt.AlignBaseline
|
||||||
|
text: root.displayText(root.info?.error?.title)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
69
ui/qmls/DatabaseChecker/Section_SelectOrCreate.qml
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
import "../Components"
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property alias selectFileUrl: fileSelector.url
|
||||||
|
property alias createDirectoryUrl: fileCreator.directoryUrl
|
||||||
|
property alias createFilename: fileCreator.filename
|
||||||
|
|
||||||
|
signal uiModeChanged(string value)
|
||||||
|
signal confirmCreate
|
||||||
|
|
||||||
|
ListModel {
|
||||||
|
id: uiModeModel
|
||||||
|
ListElement {
|
||||||
|
value: 'select'
|
||||||
|
}
|
||||||
|
ListElement {
|
||||||
|
value: 'create'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TabBar {
|
||||||
|
id: tabBar
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
TabButton {
|
||||||
|
text: qsTr("Select Existing")
|
||||||
|
width: implicitWidth + 10
|
||||||
|
}
|
||||||
|
TabButton {
|
||||||
|
text: qsTr("Create New File")
|
||||||
|
width: implicitWidth + 10
|
||||||
|
}
|
||||||
|
|
||||||
|
onCurrentIndexChanged: {
|
||||||
|
const idx = this.currentIndex;
|
||||||
|
root.uiModeChanged(uiModeModel.get(idx).value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StackLayout {
|
||||||
|
currentIndex: tabBar.currentIndex
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: children[currentIndex].height
|
||||||
|
Behavior on Layout.preferredHeight {
|
||||||
|
PropertyAnimation {
|
||||||
|
duration: 300
|
||||||
|
easing.type: Easing.InOutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
FileSelector {
|
||||||
|
id: fileSelector
|
||||||
|
}
|
||||||
|
|
||||||
|
DatabaseFileCreator {
|
||||||
|
id: fileCreator
|
||||||
|
|
||||||
|
onConfirm: root.confirmCreate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
62
ui/qmls/Overview.qml
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
import internal.ui.vm 1.0
|
||||||
|
|
||||||
|
Page {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property alias b30: vm.b30
|
||||||
|
|
||||||
|
OverviewViewModel {
|
||||||
|
id: vm
|
||||||
|
}
|
||||||
|
|
||||||
|
component Display: RowLayout {
|
||||||
|
required property string label
|
||||||
|
required property string value
|
||||||
|
|
||||||
|
spacing: 5
|
||||||
|
|
||||||
|
// implicitHeight: valueText.implicitHeight
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: parent.label
|
||||||
|
Layout.alignment: Qt.AlignBaseline
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: valueText
|
||||||
|
|
||||||
|
text: parent.value
|
||||||
|
font.pointSize: 18
|
||||||
|
Layout.alignment: Qt.AlignBaseline
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.alignment: Qt.AlignBottom
|
||||||
|
|
||||||
|
Display {
|
||||||
|
label: 'B30'
|
||||||
|
value: root.b30 >= 0 ? root.b30.toFixed(3) : 'N/A'
|
||||||
|
}
|
||||||
|
|
||||||
|
Display {
|
||||||
|
label: 'R10'
|
||||||
|
value: 'Not supported'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
Layout.alignment: Qt.AlignBottom
|
||||||
|
|
||||||
|
// TODO: icon
|
||||||
|
text: 'Reload'
|
||||||
|
onClicked: vm.reload()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
ui/qmls/libs/formatters.mjs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
export function scoreToGrade(score) {
|
||||||
|
const gradeThresholds = [
|
||||||
|
{ minimum: 9900000, grade: "EX+" },
|
||||||
|
{ minimum: 9800000, grade: "EX" },
|
||||||
|
{ minimum: 9500000, grade: "AA" },
|
||||||
|
{ minimum: 9200000, grade: "A" },
|
||||||
|
{ minimum: 8900000, grade: "B" },
|
||||||
|
{ minimum: 8600000, grade: "C" },
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const threshold of gradeThresholds) {
|
||||||
|
if (score >= threshold.minimum) {
|
||||||
|
return threshold.grade;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "D";
|
||||||
|
}
|
||||||
126
ui/resources/images/grades/a-dark.svg
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="1 1 23 23"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
|
||||||
|
sodipodi:docname="a-dark.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview1"
|
||||||
|
pagecolor="#505050"
|
||||||
|
bordercolor="#eeeeee"
|
||||||
|
borderopacity="1"
|
||||||
|
inkscape:showpageshadow="false"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pagecheckerboard="true"
|
||||||
|
inkscape:deskcolor="#505050"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
labelstyle="default"
|
||||||
|
inkscape:clip-to-page="false"
|
||||||
|
inkscape:zoom="32"
|
||||||
|
inkscape:cx="9.3125"
|
||||||
|
inkscape:cy="16.015625"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1001"
|
||||||
|
inkscape:window-x="-9"
|
||||||
|
inkscape:window-y="-9"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showguides="true">
|
||||||
|
<sodipodi:guide
|
||||||
|
position="3.0000001,19"
|
||||||
|
orientation="14,0"
|
||||||
|
id="guide1"
|
||||||
|
inkscape:locked="false" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="3.0000001,4.0000001"
|
||||||
|
orientation="0,1"
|
||||||
|
id="guide2"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="21,5.0000001"
|
||||||
|
orientation="-14,0"
|
||||||
|
id="guide3"
|
||||||
|
inkscape:locked="false" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="21,20"
|
||||||
|
orientation="0,1"
|
||||||
|
id="guide4"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="12,9.0000002"
|
||||||
|
orientation="0,1"
|
||||||
|
id="guide10"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="15.5,20.4"
|
||||||
|
orientation="0,1"
|
||||||
|
id="guide19"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="12,20"
|
||||||
|
orientation="-1,0"
|
||||||
|
id="guide5"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<defs
|
||||||
|
id="defs1">
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient34"
|
||||||
|
inkscape:collect="always">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#62476c;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop34" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#ab73a3;stop-opacity:1;"
|
||||||
|
offset="1"
|
||||||
|
id="stop35" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient34"
|
||||||
|
id="linearGradient35"
|
||||||
|
x1="11.470703"
|
||||||
|
y1="3.5996094"
|
||||||
|
x2="11.470703"
|
||||||
|
y2="20"
|
||||||
|
gradientUnits="userSpaceOnUse" />
|
||||||
|
</defs>
|
||||||
|
<g
|
||||||
|
inkscape:label="base"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
style="display:inline;fill:url(#linearGradient35);fill-opacity:1;stroke:none">
|
||||||
|
<path
|
||||||
|
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#333333;fill-opacity:0.8;stroke:none;stroke-linecap:square;stroke-miterlimit:10;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
|
||||||
|
d="M 10.722656,3.0996094 5.0820312,19.513672 4.7421875,20.5 h 3.171875 l 1.375,-4 h 5.4218755 l 1.374999,4 h 3.171875 L 18.917969,19.513672 13.277344,3.0996094 Z M 12,8.6113281 13.679688,13.5 h -3.359375 z"
|
||||||
|
id="path6"
|
||||||
|
inkscape:label="stroke"
|
||||||
|
sodipodi:nodetypes="ccccccccccccccc" />
|
||||||
|
<path
|
||||||
|
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:url(#linearGradient35);fill-opacity:1;stroke:none;stroke-linecap:square;stroke-miterlimit:10;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
|
||||||
|
d="M 11.080078,3.6000004 5.5546869,19.676172 5.4433588,20.000391 h 2.1132816 l 1.3750004,-4 h 6.1367182 l 1.375,4 h 2.113282 L 18.445312,19.676172 12.919922,3.6000004 Z M 12,7.0746098 14.380859,14.000391 H 9.6191408 Z"
|
||||||
|
id="path7"
|
||||||
|
inkscape:label="A" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 4.3 KiB |
126
ui/resources/images/grades/a.svg
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="1 1 23 23"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
|
||||||
|
sodipodi:docname="a.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview1"
|
||||||
|
pagecolor="#505050"
|
||||||
|
bordercolor="#eeeeee"
|
||||||
|
borderopacity="1"
|
||||||
|
inkscape:showpageshadow="false"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pagecheckerboard="true"
|
||||||
|
inkscape:deskcolor="#505050"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
labelstyle="default"
|
||||||
|
inkscape:clip-to-page="false"
|
||||||
|
inkscape:zoom="45.254834"
|
||||||
|
inkscape:cx="7.2036503"
|
||||||
|
inkscape:cy="15.280136"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1001"
|
||||||
|
inkscape:window-x="-9"
|
||||||
|
inkscape:window-y="-9"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showguides="true">
|
||||||
|
<sodipodi:guide
|
||||||
|
position="3.0000001,19"
|
||||||
|
orientation="14,0"
|
||||||
|
id="guide1"
|
||||||
|
inkscape:locked="false" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="3.0000001,4.0000001"
|
||||||
|
orientation="0,1"
|
||||||
|
id="guide2"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="21,5.0000001"
|
||||||
|
orientation="-14,0"
|
||||||
|
id="guide3"
|
||||||
|
inkscape:locked="false" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="21,20"
|
||||||
|
orientation="0,1"
|
||||||
|
id="guide4"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="12,9.0000002"
|
||||||
|
orientation="0,1"
|
||||||
|
id="guide10"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="15.5,20.4"
|
||||||
|
orientation="0,1"
|
||||||
|
id="guide19"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="12,20"
|
||||||
|
orientation="-1,0"
|
||||||
|
id="guide5"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<defs
|
||||||
|
id="defs1">
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient34"
|
||||||
|
inkscape:collect="always">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#46324d;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop34" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#92588a;stop-opacity:1;"
|
||||||
|
offset="1"
|
||||||
|
id="stop35" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient34"
|
||||||
|
id="linearGradient35"
|
||||||
|
x1="11.470703"
|
||||||
|
y1="3.5996094"
|
||||||
|
x2="11.470703"
|
||||||
|
y2="20"
|
||||||
|
gradientUnits="userSpaceOnUse" />
|
||||||
|
</defs>
|
||||||
|
<g
|
||||||
|
inkscape:label="base"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
style="display:inline;stroke:none;fill:url(#linearGradient35);fill-opacity:1">
|
||||||
|
<path
|
||||||
|
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#cccccc;fill-opacity:0.8;stroke:none;stroke-linecap:square;stroke-miterlimit:10;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
|
||||||
|
d="M 10.722656,3.0996094 5.0820312,19.513672 4.7421875,20.5 h 3.171875 l 1.375,-4 h 5.4218755 l 1.374999,4 h 3.171875 L 18.917969,19.513672 13.277344,3.0996094 Z M 12,8.6113281 13.679688,13.5 h -3.359375 z"
|
||||||
|
id="path6"
|
||||||
|
inkscape:label="stroke"
|
||||||
|
sodipodi:nodetypes="ccccccccccccccc" />
|
||||||
|
<path
|
||||||
|
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:url(#linearGradient35);fill-opacity:1;stroke:none;stroke-linecap:square;stroke-miterlimit:10;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
|
||||||
|
d="M 11.080078,3.6000004 5.5546869,19.676172 5.4433588,20.000391 h 2.1132816 l 1.3750004,-4 h 6.1367182 l 1.375,4 h 2.113282 L 18.445312,19.676172 12.919922,3.6000004 Z M 12,7.0746098 14.380859,14.000391 H 9.6191408 Z"
|
||||||
|
id="path7"
|
||||||
|
inkscape:label="A" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 4.3 KiB |
138
ui/resources/images/grades/aa-dark.svg
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="1 1 23 23"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
|
||||||
|
sodipodi:docname="aa-dark.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview1"
|
||||||
|
pagecolor="#505050"
|
||||||
|
bordercolor="#eeeeee"
|
||||||
|
borderopacity="1"
|
||||||
|
inkscape:showpageshadow="false"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pagecheckerboard="true"
|
||||||
|
inkscape:deskcolor="#505050"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
labelstyle="default"
|
||||||
|
inkscape:clip-to-page="false"
|
||||||
|
inkscape:zoom="32"
|
||||||
|
inkscape:cx="9.75"
|
||||||
|
inkscape:cy="13.078125"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1001"
|
||||||
|
inkscape:window-x="-9"
|
||||||
|
inkscape:window-y="-9"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showguides="true">
|
||||||
|
<sodipodi:guide
|
||||||
|
position="3.0000001,19"
|
||||||
|
orientation="14,0"
|
||||||
|
id="guide1"
|
||||||
|
inkscape:locked="false" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="3.0000001,4.0000001"
|
||||||
|
orientation="0,1"
|
||||||
|
id="guide2"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="21,5.0000001"
|
||||||
|
orientation="-14,0"
|
||||||
|
id="guide3"
|
||||||
|
inkscape:locked="false" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="21,20"
|
||||||
|
orientation="0,1"
|
||||||
|
id="guide4"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="15.5,19"
|
||||||
|
orientation="-1,0"
|
||||||
|
id="guide7"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="10,19"
|
||||||
|
orientation="-1,0"
|
||||||
|
id="guide8"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="12,9.0000002"
|
||||||
|
orientation="0,1"
|
||||||
|
id="guide10"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="15.5,20.4"
|
||||||
|
orientation="0,1"
|
||||||
|
id="guide19"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<defs
|
||||||
|
id="defs1">
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient34"
|
||||||
|
inkscape:collect="always">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#785880;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop34" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#b464a6;stop-opacity:1;"
|
||||||
|
offset="1"
|
||||||
|
id="stop35" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient34"
|
||||||
|
id="linearGradient35"
|
||||||
|
x1="11.470703"
|
||||||
|
y1="3.5996094"
|
||||||
|
x2="11.470703"
|
||||||
|
y2="20"
|
||||||
|
gradientUnits="userSpaceOnUse" />
|
||||||
|
</defs>
|
||||||
|
<path
|
||||||
|
id="path29"
|
||||||
|
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#333333;fill-opacity:0.80000001;stroke-linecap:square;stroke-miterlimit:10;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
|
||||||
|
d="M 7.2226562 3.0996094 L 1.5820312 19.513672 L 1.2421875 20.5 L 4.4140625 20.5 L 5.7890625 16.5 L 9.6171875 16.5 L 8.5820312 19.513672 L 8.2421875 20.5 L 11.414062 20.5 L 12.789062 16.5 L 18.210938 16.5 L 19.585938 20.5 L 22.757812 20.5 L 22.417969 19.513672 L 16.777344 3.0996094 L 14.222656 3.0996094 L 12 9.5683594 C 11.259181 7.4122493 10.518269 5.2556825 9.7773438 3.0996094 L 7.2226562 3.0996094 z M 8.5 8.6113281 L 10.179688 13.5 L 6.8203125 13.5 L 8.5 8.6113281 z M 15.5 8.6113281 L 17.179688 13.5 L 13.820312 13.5 L 15.5 8.6113281 z "
|
||||||
|
inkscape:label="stroke" />
|
||||||
|
<g
|
||||||
|
inkscape:label="base"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
style="display:inline;stroke:none;fill:url(#linearGradient35);fill-opacity:1">
|
||||||
|
<path
|
||||||
|
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:url(#linearGradient35);stroke-linecap:square;stroke-miterlimit:10;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1;stroke:none;fill-opacity:1"
|
||||||
|
d="M 14.580078,3.5996094 9.0546875,19.675781 8.9433594,20 h 2.1132816 l 1.375,-4 h 6.136718 l 1.375,4 h 2.113282 L 21.945312,19.675781 16.419922,3.5996094 Z M 15.5,7.0742188 17.880859,14 h -4.761718 z"
|
||||||
|
id="path30"
|
||||||
|
inkscape:label="A" />
|
||||||
|
<path
|
||||||
|
id="path23"
|
||||||
|
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:url(#linearGradient35);stroke-linecap:square;stroke-miterlimit:10;enable-background:accumulate;stop-color:#000000;stop-opacity:1;stroke:none;fill-opacity:1"
|
||||||
|
d="M 7.5800781,3.5996094 2.0546875,19.675781 1.9433594,20 h 2.1132812 l 1.375,-4 h 3.8300782 l 0.6875,-2 H 6.1191406 L 8.5,7.0742188 10.414062,12.644531 11.470703,9.5683594 9.4199219,3.5996094 Z"
|
||||||
|
sodipodi:nodetypes="ccccccccccccc"
|
||||||
|
inkscape:label="A_cut" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 5.3 KiB |
138
ui/resources/images/grades/aa.svg
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="1 1 23 23"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
|
||||||
|
sodipodi:docname="aa.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview1"
|
||||||
|
pagecolor="#505050"
|
||||||
|
bordercolor="#eeeeee"
|
||||||
|
borderopacity="1"
|
||||||
|
inkscape:showpageshadow="false"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pagecheckerboard="true"
|
||||||
|
inkscape:deskcolor="#505050"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
labelstyle="default"
|
||||||
|
inkscape:clip-to-page="false"
|
||||||
|
inkscape:zoom="32"
|
||||||
|
inkscape:cx="9.75"
|
||||||
|
inkscape:cy="13.078125"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1001"
|
||||||
|
inkscape:window-x="-9"
|
||||||
|
inkscape:window-y="-9"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg1"
|
||||||
|
showguides="true">
|
||||||
|
<sodipodi:guide
|
||||||
|
position="3.0000001,19"
|
||||||
|
orientation="14,0"
|
||||||
|
id="guide1"
|
||||||
|
inkscape:locked="false" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="3.0000001,4.0000001"
|
||||||
|
orientation="0,1"
|
||||||
|
id="guide2"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="21,5.0000001"
|
||||||
|
orientation="-14,0"
|
||||||
|
id="guide3"
|
||||||
|
inkscape:locked="false" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="21,20"
|
||||||
|
orientation="0,1"
|
||||||
|
id="guide4"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="15.5,19"
|
||||||
|
orientation="-1,0"
|
||||||
|
id="guide7"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="10,19"
|
||||||
|
orientation="-1,0"
|
||||||
|
id="guide8"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="12,9.0000002"
|
||||||
|
orientation="0,1"
|
||||||
|
id="guide10"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="15.5,20.4"
|
||||||
|
orientation="0,1"
|
||||||
|
id="guide19"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<defs
|
||||||
|
id="defs1">
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient34"
|
||||||
|
inkscape:collect="always">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#5a3463;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop34" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#9b4b8d;stop-opacity:1;"
|
||||||
|
offset="1"
|
||||||
|
id="stop35" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient34"
|
||||||
|
id="linearGradient35"
|
||||||
|
x1="11.470703"
|
||||||
|
y1="3.5996094"
|
||||||
|
x2="11.470703"
|
||||||
|
y2="20"
|
||||||
|
gradientUnits="userSpaceOnUse" />
|
||||||
|
</defs>
|
||||||
|
<path
|
||||||
|
id="path29"
|
||||||
|
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#cccccc;fill-opacity:0.8;stroke-linecap:square;stroke-miterlimit:10;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
|
||||||
|
d="M 7.2226562 3.0996094 L 1.5820312 19.513672 L 1.2421875 20.5 L 4.4140625 20.5 L 5.7890625 16.5 L 9.6171875 16.5 L 8.5820312 19.513672 L 8.2421875 20.5 L 11.414062 20.5 L 12.789062 16.5 L 18.210938 16.5 L 19.585938 20.5 L 22.757812 20.5 L 22.417969 19.513672 L 16.777344 3.0996094 L 14.222656 3.0996094 L 12 9.5683594 C 11.259181 7.4122493 10.518269 5.2556825 9.7773438 3.0996094 L 7.2226562 3.0996094 z M 8.5 8.6113281 L 10.179688 13.5 L 6.8203125 13.5 L 8.5 8.6113281 z M 15.5 8.6113281 L 17.179688 13.5 L 13.820312 13.5 L 15.5 8.6113281 z "
|
||||||
|
inkscape:label="stroke" />
|
||||||
|
<g
|
||||||
|
inkscape:label="base"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
style="display:inline;stroke:none;fill:url(#linearGradient35);fill-opacity:1">
|
||||||
|
<path
|
||||||
|
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:url(#linearGradient35);stroke-linecap:square;stroke-miterlimit:10;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1;stroke:none;fill-opacity:1"
|
||||||
|
d="M 14.580078,3.5996094 9.0546875,19.675781 8.9433594,20 h 2.1132816 l 1.375,-4 h 6.136718 l 1.375,4 h 2.113282 L 21.945312,19.675781 16.419922,3.5996094 Z M 15.5,7.0742188 17.880859,14 h -4.761718 z"
|
||||||
|
id="path30"
|
||||||
|
inkscape:label="A" />
|
||||||
|
<path
|
||||||
|
id="path23"
|
||||||
|
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:url(#linearGradient35);stroke-linecap:square;stroke-miterlimit:10;enable-background:accumulate;stop-color:#000000;stop-opacity:1;stroke:none;fill-opacity:1"
|
||||||
|
d="M 7.5800781,3.5996094 2.0546875,19.675781 1.9433594,20 h 2.1132812 l 1.375,-4 h 3.8300782 l 0.6875,-2 H 6.1191406 L 8.5,7.0742188 10.414062,12.644531 11.470703,9.5683594 9.4199219,3.5996094 Z"
|
||||||
|
sodipodi:nodetypes="ccccccccccccc"
|
||||||
|
inkscape:label="A_cut" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 5.3 KiB |
133
ui/resources/images/grades/b-dark.svg
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="1 1 23 23"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
|
||||||
|
sodipodi:docname="b-dark.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview1"
|
||||||
|
pagecolor="#505050"
|
||||||
|
bordercolor="#eeeeee"
|
||||||
|
borderopacity="1"
|
||||||
|
inkscape:showpageshadow="false"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pagecheckerboard="true"
|
||||||
|
inkscape:deskcolor="#505050"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
labelstyle="default"
|
||||||
|
inkscape:clip-to-page="false"
|
||||||
|
inkscape:zoom="32"
|
||||||
|
inkscape:cx="10.625"
|
||||||
|
inkscape:cy="13.640625"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1001"
|
||||||
|
inkscape:window-x="-9"
|
||||||
|
inkscape:window-y="-9"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showguides="true">
|
||||||
|
<sodipodi:guide
|
||||||
|
position="3.0000001,5.0000001"
|
||||||
|
orientation="0,1"
|
||||||
|
id="guide2"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="21,19"
|
||||||
|
orientation="0,1"
|
||||||
|
id="guide4"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="12,12.5"
|
||||||
|
orientation="0,1"
|
||||||
|
id="guide10"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="12,20"
|
||||||
|
orientation="-1,0"
|
||||||
|
id="guide5"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="7.0000001,20"
|
||||||
|
orientation="-1,0"
|
||||||
|
id="guide11"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="17.5,3.9996091"
|
||||||
|
orientation="-1,0"
|
||||||
|
id="guide13"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<defs
|
||||||
|
id="defs1">
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient34"
|
||||||
|
inkscape:collect="always">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#62476c;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop34" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#ab73a3;stop-opacity:1;"
|
||||||
|
offset="1"
|
||||||
|
id="stop35" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient34"
|
||||||
|
id="linearGradient35"
|
||||||
|
x1="11.470703"
|
||||||
|
y1="3.5996094"
|
||||||
|
x2="11.470703"
|
||||||
|
y2="20"
|
||||||
|
gradientUnits="userSpaceOnUse" />
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient34"
|
||||||
|
id="linearGradient1"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="11.470703"
|
||||||
|
y1="3.5996094"
|
||||||
|
x2="11.470703"
|
||||||
|
y2="20" />
|
||||||
|
</defs>
|
||||||
|
<g
|
||||||
|
inkscape:label="base"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
style="display:inline;fill:url(#linearGradient35);fill-opacity:1;stroke:none"
|
||||||
|
transform="translate(0.375)">
|
||||||
|
<path
|
||||||
|
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#333333;fill-opacity:0.8;stroke:none;stroke-miterlimit:10;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
|
||||||
|
d="m 5.5,3.5 v 17 h 6.75 c 2.973855,0 5.5,-2.314875 5.5,-5.25 0,-1.721917 -0.943723,-3.16211 -2.261719,-4.113281 C 16.093694,10.320901 16.5,9.3384477 16.5,8.25 16.5,5.7007829 14.533131,3.5 12,3.5 Z m 3,3 H 12 c 0.774581,0 1.5,0.6927751 1.5,1.75 C 13.5,9.3072249 12.774581,10 12,10 H 8.5 Z m 0,6.5 h 3.25 0.25 0.25 c 1.456677,0 2.5,1.044783 2.5,2.25 0,1.205217 -1.043323,2.25 -2.5,2.25 H 8.5 Z"
|
||||||
|
id="path22"
|
||||||
|
inkscape:label="stroke"
|
||||||
|
sodipodi:nodetypes="ccsscssccssscccccssscc" />
|
||||||
|
<path
|
||||||
|
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:url(#linearGradient1);fill-opacity:1;stroke:none;stroke-miterlimit:10;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
|
||||||
|
d="m 6,4 v 16 h 6.25 c 2.715802,0 5,-2.102463 5,-4.75 0,-1.719783 -0.962948,-3.210042 -2.382812,-4.042969 C 15.571798,10.433759 16,9.3833207 16,8.25 16,5.9562068 14.242331,4 12,4 Z m 2,2 h 4 c 1.071377,0 2,0.9539424 2,2.25 0,1.2960576 -0.928623,2.25 -2,2.25 H 8 Z m 4.25,6.486328 V 12.5 c 1.702476,0 3,1.255402 3,2.75 0,1.494598 -1.297524,2.75 -3,2.75 H 8 v -5.5 h 4 c 0.08442,0 0.167024,-0.0082 0.25,-0.01367 z"
|
||||||
|
id="path23"
|
||||||
|
inkscape:label="B" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 4.8 KiB |
133
ui/resources/images/grades/b.svg
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="1 1 23 23"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
|
||||||
|
sodipodi:docname="b.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview1"
|
||||||
|
pagecolor="#505050"
|
||||||
|
bordercolor="#eeeeee"
|
||||||
|
borderopacity="1"
|
||||||
|
inkscape:showpageshadow="false"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pagecheckerboard="true"
|
||||||
|
inkscape:deskcolor="#505050"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
labelstyle="default"
|
||||||
|
inkscape:clip-to-page="false"
|
||||||
|
inkscape:zoom="32"
|
||||||
|
inkscape:cx="9.546875"
|
||||||
|
inkscape:cy="13.828125"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1001"
|
||||||
|
inkscape:window-x="-9"
|
||||||
|
inkscape:window-y="-9"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showguides="true">
|
||||||
|
<sodipodi:guide
|
||||||
|
position="3.0000001,5.0000001"
|
||||||
|
orientation="0,1"
|
||||||
|
id="guide2"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="21,19"
|
||||||
|
orientation="0,1"
|
||||||
|
id="guide4"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="12,12.5"
|
||||||
|
orientation="0,1"
|
||||||
|
id="guide10"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="12,20"
|
||||||
|
orientation="-1,0"
|
||||||
|
id="guide5"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="7.0000001,20"
|
||||||
|
orientation="-1,0"
|
||||||
|
id="guide11"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="17.5,3.9996091"
|
||||||
|
orientation="-1,0"
|
||||||
|
id="guide13"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<defs
|
||||||
|
id="defs1">
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient34"
|
||||||
|
inkscape:collect="always">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#43334a;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop34" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#755b7c;stop-opacity:1;"
|
||||||
|
offset="1"
|
||||||
|
id="stop35" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient34"
|
||||||
|
id="linearGradient35"
|
||||||
|
x1="11.470703"
|
||||||
|
y1="3.5996094"
|
||||||
|
x2="11.470703"
|
||||||
|
y2="20"
|
||||||
|
gradientUnits="userSpaceOnUse" />
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient34"
|
||||||
|
id="linearGradient23"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="11.470703"
|
||||||
|
y1="3.5996094"
|
||||||
|
x2="11.470703"
|
||||||
|
y2="20" />
|
||||||
|
</defs>
|
||||||
|
<g
|
||||||
|
inkscape:label="base"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
style="display:inline;fill:url(#linearGradient35);fill-opacity:1;stroke:none"
|
||||||
|
transform="translate(0.375)">
|
||||||
|
<path
|
||||||
|
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#cccccc;fill-opacity:0.8;stroke:none;stroke-miterlimit:10;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
|
||||||
|
d="m 5.5,3.5 v 17 h 6.75 c 2.973855,0 5.5,-2.314875 5.5,-5.25 0,-1.721917 -0.943723,-3.16211 -2.261719,-4.113281 C 16.093694,10.320901 16.5,9.3384477 16.5,8.25 16.5,5.7007829 14.533131,3.5 12,3.5 Z m 3,3 H 12 c 0.774581,0 1.5,0.6927751 1.5,1.75 C 13.5,9.3072249 12.774581,10 12,10 H 8.5 Z m 0,6.5 h 3.25 0.25 0.25 c 1.456677,0 2.5,1.044783 2.5,2.25 0,1.205217 -1.043323,2.25 -2.5,2.25 H 8.5 Z"
|
||||||
|
id="path22"
|
||||||
|
inkscape:label="stroke"
|
||||||
|
sodipodi:nodetypes="ccsscssccssscccccssscc" />
|
||||||
|
<path
|
||||||
|
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:url(#linearGradient23);fill-opacity:1;stroke:none;stroke-miterlimit:10;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
|
||||||
|
d="m 6,4 v 16 h 6.25 c 2.715802,0 5,-2.102463 5,-4.75 0,-1.719783 -0.962948,-3.210042 -2.382812,-4.042969 C 15.571798,10.433759 16,9.3833207 16,8.25 16,5.9562068 14.242331,4 12,4 Z m 2,2 h 4 c 1.071377,0 2,0.9539424 2,2.25 0,1.2960576 -0.928623,2.25 -2,2.25 H 8 Z m 4.25,6.486328 V 12.5 c 1.702476,0 3,1.255402 3,2.75 0,1.494598 -1.297524,2.75 -3,2.75 H 8 v -5.5 h 4 c 0.08442,0 0.167024,-0.0082 0.25,-0.01367 z"
|
||||||
|
id="path23"
|
||||||
|
inkscape:label="B" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 4.8 KiB |
123
ui/resources/images/grades/c-dark.svg
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="1 1 23 23"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
|
||||||
|
sodipodi:docname="c-dark.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview1"
|
||||||
|
pagecolor="#505050"
|
||||||
|
bordercolor="#eeeeee"
|
||||||
|
borderopacity="1"
|
||||||
|
inkscape:showpageshadow="false"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pagecheckerboard="true"
|
||||||
|
inkscape:deskcolor="#505050"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
labelstyle="default"
|
||||||
|
inkscape:clip-to-page="false"
|
||||||
|
inkscape:zoom="22.627417"
|
||||||
|
inkscape:cx="8.8167377"
|
||||||
|
inkscape:cy="14.164233"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1001"
|
||||||
|
inkscape:window-x="-9"
|
||||||
|
inkscape:window-y="-9"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showguides="true">
|
||||||
|
<sodipodi:guide
|
||||||
|
position="12,20"
|
||||||
|
orientation="-1,0"
|
||||||
|
id="guide5"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="12,12"
|
||||||
|
orientation="0.60181502,0.79863551"
|
||||||
|
id="guide6"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="12,12"
|
||||||
|
orientation="-0.60181502,0.79863551"
|
||||||
|
id="guide7"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<defs
|
||||||
|
id="defs1">
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient9"
|
||||||
|
inkscape:collect="always">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#5c433d;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop9" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#9d6c85;stop-opacity:1;"
|
||||||
|
offset="1"
|
||||||
|
id="stop10" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient34"
|
||||||
|
inkscape:collect="always">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#46324d;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop34" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#92588a;stop-opacity:1;"
|
||||||
|
offset="1"
|
||||||
|
id="stop35" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient34"
|
||||||
|
id="linearGradient35"
|
||||||
|
x1="11.470703"
|
||||||
|
y1="3.5996094"
|
||||||
|
x2="11.470703"
|
||||||
|
y2="20"
|
||||||
|
gradientUnits="userSpaceOnUse" />
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient9"
|
||||||
|
id="linearGradient10"
|
||||||
|
x1="12"
|
||||||
|
y1="3.5163455"
|
||||||
|
x2="12"
|
||||||
|
y2="20.483654"
|
||||||
|
gradientUnits="userSpaceOnUse" />
|
||||||
|
</defs>
|
||||||
|
<g
|
||||||
|
inkscape:label="base"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
style="display:inline;stroke:none;fill:url(#linearGradient35);fill-opacity:1">
|
||||||
|
<path
|
||||||
|
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#333333;fill-opacity:0.80000001;stroke:none;stroke-miterlimit:10;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
|
||||||
|
d="M 12.5,3 C 7.7659933,3 4,7.0984281 4,12 c 0,4.901572 3.7659934,9 8.5,9 2.899254,0 5.460617,-1.548293 6.980469,-3.859375 l 0.27539,-0.417969 -2.507812,-1.65039 -0.275391,0.417968 C 15.95623,17.035809 14.33618,18 12.5,18 9.5033162,18 7,15.389122 7,12 7,8.6108784 9.5033164,6 12.5,6 c 1.836197,0 3.456233,0.9641762 4.472656,2.5097656 L 17.248047,8.9277344 19.755859,7.2773437 19.480469,6.859375 C 17.96062,4.5482711 15.399265,3 12.5,3 Z"
|
||||||
|
id="path8"
|
||||||
|
inkscape:label="stroke"
|
||||||
|
sodipodi:nodetypes="sssccccsssccccs" />
|
||||||
|
<path
|
||||||
|
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:url(#linearGradient10);stroke:none;stroke-miterlimit:10;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
|
||||||
|
d="m 12.5,3.5 c -4.4450362,0 -8,3.8534536 -8,8.5 0,4.646547 3.5549639,8.5 8,8.5 2.721277,0 5.127673,-1.452972 6.5625,-3.634766 L 17.390625,15.765625 C 16.291564,17.436853 14.512289,18.5 12.5,18.5 c -3.2869503,0 -6,-2.862275 -6,-6.5 0,-3.6377248 2.7130498,-6.5 6,-6.5 2.012302,0 3.791567,1.0631305 4.890625,2.734375 L 19.0625,7.1347656 C 17.627676,4.9529509 15.221292,3.5 12.5,3.5 Z"
|
||||||
|
id="path9"
|
||||||
|
inkscape:label="C" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 4.6 KiB |
123
ui/resources/images/grades/c.svg
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="1 1 23 23"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
|
||||||
|
sodipodi:docname="c.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview1"
|
||||||
|
pagecolor="#505050"
|
||||||
|
bordercolor="#eeeeee"
|
||||||
|
borderopacity="1"
|
||||||
|
inkscape:showpageshadow="false"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pagecheckerboard="true"
|
||||||
|
inkscape:deskcolor="#505050"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
labelstyle="default"
|
||||||
|
inkscape:clip-to-page="false"
|
||||||
|
inkscape:zoom="32"
|
||||||
|
inkscape:cx="9.828125"
|
||||||
|
inkscape:cy="12.421875"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1001"
|
||||||
|
inkscape:window-x="-9"
|
||||||
|
inkscape:window-y="-9"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showguides="true">
|
||||||
|
<sodipodi:guide
|
||||||
|
position="12,20"
|
||||||
|
orientation="-1,0"
|
||||||
|
id="guide5"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="12,12"
|
||||||
|
orientation="0.60181502,0.79863551"
|
||||||
|
id="guide6"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="12,12"
|
||||||
|
orientation="-0.60181502,0.79863551"
|
||||||
|
id="guide7"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<defs
|
||||||
|
id="defs1">
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient9"
|
||||||
|
inkscape:collect="always">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#3b2b27;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop9" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#80566b;stop-opacity:1;"
|
||||||
|
offset="1"
|
||||||
|
id="stop10" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient34"
|
||||||
|
inkscape:collect="always">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#46324d;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop34" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#92588a;stop-opacity:1;"
|
||||||
|
offset="1"
|
||||||
|
id="stop35" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient34"
|
||||||
|
id="linearGradient35"
|
||||||
|
x1="11.470703"
|
||||||
|
y1="3.5996094"
|
||||||
|
x2="11.470703"
|
||||||
|
y2="20"
|
||||||
|
gradientUnits="userSpaceOnUse" />
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient9"
|
||||||
|
id="linearGradient10"
|
||||||
|
x1="12"
|
||||||
|
y1="3.5163455"
|
||||||
|
x2="12"
|
||||||
|
y2="20.483654"
|
||||||
|
gradientUnits="userSpaceOnUse" />
|
||||||
|
</defs>
|
||||||
|
<g
|
||||||
|
inkscape:label="base"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
style="display:inline;stroke:none;fill:url(#linearGradient35);fill-opacity:1">
|
||||||
|
<path
|
||||||
|
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#cccccc;fill-opacity:0.8;stroke:none;stroke-miterlimit:10;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
|
||||||
|
d="M 12.5,3 C 7.7659933,3 4,7.0984281 4,12 c 0,4.901572 3.7659934,9 8.5,9 2.899254,0 5.460617,-1.548293 6.980469,-3.859375 l 0.27539,-0.417969 -2.507812,-1.65039 -0.275391,0.417968 C 15.95623,17.035809 14.33618,18 12.5,18 9.5033162,18 7,15.389122 7,12 7,8.6108784 9.5033164,6 12.5,6 c 1.836197,0 3.456233,0.9641762 4.472656,2.5097656 L 17.248047,8.9277344 19.755859,7.2773437 19.480469,6.859375 C 17.96062,4.5482711 15.399265,3 12.5,3 Z"
|
||||||
|
id="path8"
|
||||||
|
inkscape:label="stroke"
|
||||||
|
sodipodi:nodetypes="sssccccsssccccs" />
|
||||||
|
<path
|
||||||
|
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:url(#linearGradient10);stroke:none;stroke-miterlimit:10;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
|
||||||
|
d="m 12.5,3.5 c -4.4450362,0 -8,3.8534536 -8,8.5 0,4.646547 3.5549639,8.5 8,8.5 2.721277,0 5.127673,-1.452972 6.5625,-3.634766 L 17.390625,15.765625 C 16.291564,17.436853 14.512289,18.5 12.5,18.5 c -3.2869503,0 -6,-2.862275 -6,-6.5 0,-3.6377248 2.7130498,-6.5 6,-6.5 2.012302,0 3.791567,1.0631305 4.890625,2.734375 L 19.0625,7.1347656 C 17.627676,4.9529509 15.221292,3.5 12.5,3.5 Z"
|
||||||
|
id="path9"
|
||||||
|
inkscape:label="C" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 4.5 KiB |
136
ui/resources/images/grades/d-dark.svg
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="1 1 23 23"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
|
||||||
|
sodipodi:docname="d-dark.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview1"
|
||||||
|
pagecolor="#505050"
|
||||||
|
bordercolor="#eeeeee"
|
||||||
|
borderopacity="1"
|
||||||
|
inkscape:showpageshadow="false"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pagecheckerboard="true"
|
||||||
|
inkscape:deskcolor="#505050"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
labelstyle="default"
|
||||||
|
inkscape:clip-to-page="false"
|
||||||
|
inkscape:zoom="32"
|
||||||
|
inkscape:cx="12.1875"
|
||||||
|
inkscape:cy="12.109375"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1001"
|
||||||
|
inkscape:window-x="-9"
|
||||||
|
inkscape:window-y="-9"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showguides="true">
|
||||||
|
<sodipodi:guide
|
||||||
|
position="3.0000001,5.0000001"
|
||||||
|
orientation="0,1"
|
||||||
|
id="guide2"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="21,19"
|
||||||
|
orientation="0,1"
|
||||||
|
id="guide4"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="12,12.5"
|
||||||
|
orientation="0,1"
|
||||||
|
id="guide10"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="12,20"
|
||||||
|
orientation="-1,0"
|
||||||
|
id="guide5"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="7.0000001,20"
|
||||||
|
orientation="-1,0"
|
||||||
|
id="guide11"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="17.5,3.9996091"
|
||||||
|
orientation="-1,0"
|
||||||
|
id="guide13"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<defs
|
||||||
|
id="defs1">
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient8"
|
||||||
|
inkscape:collect="always">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#842a4b;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop7" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#bd516c;stop-opacity:1;"
|
||||||
|
offset="1"
|
||||||
|
id="stop8" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient5"
|
||||||
|
inkscape:collect="always">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop5" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop6" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient8"
|
||||||
|
id="linearGradient6"
|
||||||
|
x1="11"
|
||||||
|
y1="4"
|
||||||
|
x2="11"
|
||||||
|
y2="20"
|
||||||
|
gradientUnits="userSpaceOnUse" />
|
||||||
|
</defs>
|
||||||
|
<g
|
||||||
|
inkscape:label="base"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
style="display:inline;fill:url(#linearGradient5);fill-opacity:1;stroke:none"
|
||||||
|
transform="translate(0.375)">
|
||||||
|
<path
|
||||||
|
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#333333;fill-opacity:0.80000001;stroke:none;stroke-miterlimit:10;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
|
||||||
|
d="m 5.5,3.5 v 17 H 11 c 4.462075,0 8,-3.878181 8,-8.5 0,-4.621819 -3.537925,-8.5 -8,-8.5 z m 3,3 H 11 c 2.715731,0 5,2.3827768 5,5.5 0,3.117223 -2.284269,5.5 -5,5.5 H 8.5 Z"
|
||||||
|
id="path4"
|
||||||
|
inkscape:label="stroke"
|
||||||
|
sodipodi:nodetypes="ccsssccssscc" />
|
||||||
|
<path
|
||||||
|
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:url(#linearGradient6);fill-opacity:1;stroke:none;stroke-miterlimit:10;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
|
||||||
|
d="m 6,4 v 16 h 5 c 4.171863,0 7.5,-3.63217 7.5,-8 0,-4.3678304 -3.328137,-8 -7.5,-8 z m 2,2 h 3 c 3.007839,0 5.5,2.635844 5.5,6 0,3.364156 -2.492161,6 -5.5,6 H 8 Z"
|
||||||
|
id="path5"
|
||||||
|
inkscape:label="D" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 4.4 KiB |
136
ui/resources/images/grades/d.svg
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="1 1 23 23"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
|
||||||
|
sodipodi:docname="d.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview1"
|
||||||
|
pagecolor="#505050"
|
||||||
|
bordercolor="#eeeeee"
|
||||||
|
borderopacity="1"
|
||||||
|
inkscape:showpageshadow="false"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pagecheckerboard="true"
|
||||||
|
inkscape:deskcolor="#505050"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
labelstyle="default"
|
||||||
|
inkscape:clip-to-page="false"
|
||||||
|
inkscape:zoom="32"
|
||||||
|
inkscape:cx="12.1875"
|
||||||
|
inkscape:cy="12.109375"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1001"
|
||||||
|
inkscape:window-x="-9"
|
||||||
|
inkscape:window-y="-9"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showguides="true">
|
||||||
|
<sodipodi:guide
|
||||||
|
position="3.0000001,5.0000001"
|
||||||
|
orientation="0,1"
|
||||||
|
id="guide2"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="21,19"
|
||||||
|
orientation="0,1"
|
||||||
|
id="guide4"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="12,12.5"
|
||||||
|
orientation="0,1"
|
||||||
|
id="guide10"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="12,20"
|
||||||
|
orientation="-1,0"
|
||||||
|
id="guide5"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="7.0000001,20"
|
||||||
|
orientation="-1,0"
|
||||||
|
id="guide11"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="17.5,3.9996091"
|
||||||
|
orientation="-1,0"
|
||||||
|
id="guide13"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<defs
|
||||||
|
id="defs1">
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient8"
|
||||||
|
inkscape:collect="always">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#5d1d35;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop7" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#9f3c55;stop-opacity:1;"
|
||||||
|
offset="1"
|
||||||
|
id="stop8" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient5"
|
||||||
|
inkscape:collect="always">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop5" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop6" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient8"
|
||||||
|
id="linearGradient6"
|
||||||
|
x1="11"
|
||||||
|
y1="4"
|
||||||
|
x2="11"
|
||||||
|
y2="20"
|
||||||
|
gradientUnits="userSpaceOnUse" />
|
||||||
|
</defs>
|
||||||
|
<g
|
||||||
|
inkscape:label="base"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
style="display:inline;fill:url(#linearGradient5);fill-opacity:1;stroke:none"
|
||||||
|
transform="translate(0.375)">
|
||||||
|
<path
|
||||||
|
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#cccccc;fill-opacity:1;stroke:none;stroke-miterlimit:10;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
|
||||||
|
d="m 5.5,3.5 v 17 H 11 c 4.462075,0 8,-3.878181 8,-8.5 0,-4.621819 -3.537925,-8.5 -8,-8.5 z m 3,3 H 11 c 2.715731,0 5,2.3827768 5,5.5 0,3.117223 -2.284269,5.5 -5,5.5 H 8.5 Z"
|
||||||
|
id="path4"
|
||||||
|
inkscape:label="stroke"
|
||||||
|
sodipodi:nodetypes="ccsssccssscc" />
|
||||||
|
<path
|
||||||
|
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:url(#linearGradient6);fill-opacity:1;stroke:none;stroke-miterlimit:10;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
|
||||||
|
d="m 6,4 v 16 h 5 c 4.171863,0 7.5,-3.63217 7.5,-8 0,-4.3678304 -3.328137,-8 -7.5,-8 z m 2,2 h 3 c 3.007839,0 5.5,2.635844 5.5,6 0,3.364156 -2.492161,6 -5.5,6 H 8 Z"
|
||||||
|
id="path5"
|
||||||
|
inkscape:label="D" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 4.4 KiB |
119
ui/resources/images/grades/ex-dark.svg
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="1 1 23 23"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
|
||||||
|
sodipodi:docname="ex-dark.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview1"
|
||||||
|
pagecolor="#505050"
|
||||||
|
bordercolor="#eeeeee"
|
||||||
|
borderopacity="1"
|
||||||
|
inkscape:showpageshadow="false"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pagecheckerboard="true"
|
||||||
|
inkscape:deskcolor="#505050"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
labelstyle="default"
|
||||||
|
inkscape:clip-to-page="false"
|
||||||
|
inkscape:zoom="22.627418"
|
||||||
|
inkscape:cx="14.429397"
|
||||||
|
inkscape:cy="14.517786"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1001"
|
||||||
|
inkscape:window-x="-9"
|
||||||
|
inkscape:window-y="-9"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showguides="true" />
|
||||||
|
<defs
|
||||||
|
id="defs1">
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient3"
|
||||||
|
inkscape:collect="always">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#ba2cae;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop2" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#397fc6;stop-opacity:1;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3" />
|
||||||
|
</linearGradient>
|
||||||
|
<inkscape:path-effect
|
||||||
|
effect="spiro"
|
||||||
|
id="path-effect13"
|
||||||
|
is_visible="true"
|
||||||
|
lpeversion="1" />
|
||||||
|
<inkscape:path-effect
|
||||||
|
effect="spiro"
|
||||||
|
id="path-effect11"
|
||||||
|
is_visible="true"
|
||||||
|
lpeversion="1" />
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3"
|
||||||
|
id="linearGradient23"
|
||||||
|
x1="10.353516"
|
||||||
|
y1="4.000001"
|
||||||
|
x2="17.814453"
|
||||||
|
y2="20"
|
||||||
|
gradientUnits="userSpaceOnUse" />
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3"
|
||||||
|
id="linearGradient1"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="10.353516"
|
||||||
|
y1="4.000001"
|
||||||
|
x2="17.814453"
|
||||||
|
y2="20"
|
||||||
|
gradientTransform="translate(3.030469)" />
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3"
|
||||||
|
id="linearGradient2"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="10.353516"
|
||||||
|
y1="4.000001"
|
||||||
|
x2="17.814453"
|
||||||
|
y2="20"
|
||||||
|
gradientTransform="translate(3.030469)" />
|
||||||
|
</defs>
|
||||||
|
<g
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer2"
|
||||||
|
inkscape:label="strokes">
|
||||||
|
<path
|
||||||
|
id="path6"
|
||||||
|
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#333333;fill-opacity:0.8;stroke:none;stroke-width:0;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
|
||||||
|
inkscape:label="EX-stroke"
|
||||||
|
d="m 2.8,3.5 v 17 h 7.714844 1.285157 2.02539 L 16.133984,15.548828 18.442578,20.5 h 3.310547 L 17.78828,12 21.753125,3.5 H 18.442578 L 16.133984,8.4511719 13.825391,3.5 h -2.02539 -1.285157 z m 3,3 h 6.113281 l 2.566407,5.5 -2.566407,5.5 H 5.8 v -4 h 5 v -3 h -5 z" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
inkscape:label="base"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
style="display:inline;fill:url(#linearGradient23);stroke:none;stroke-width:0;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill">
|
||||||
|
<path
|
||||||
|
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;fill:url(#linearGradient1);stroke:#cccccc;stroke-width:0;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
|
||||||
|
d="m 3.3,4 v 16 h 8.000001 V 18 H 5.3 v -5 h 5.000001 V 11 H 5.3 V 6 h 6.000001 V 4 Z"
|
||||||
|
id="path7"
|
||||||
|
inkscape:label="E" />
|
||||||
|
<path
|
||||||
|
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;fill:url(#linearGradient2);stroke:#cccccc;stroke-width:0;stroke-linecap:square;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
|
||||||
|
d="M 11.3,4.0000008 15.030469,12 11.3,20 h 2.207031 L 16.133984,14.367188 18.760937,20 h 2.207031 L 17.2375,12 20.967968,4.0000008 H 18.760937 L 16.133984,9.6328133 13.507031,4.0000008 Z"
|
||||||
|
id="path4"
|
||||||
|
inkscape:label="X" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 4.5 KiB |
115
ui/resources/images/grades/ex-plus-dark.svg
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0.755 1 23 23"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
|
||||||
|
sodipodi:docname="exPlus-dark.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview1"
|
||||||
|
pagecolor="#505050"
|
||||||
|
bordercolor="#eeeeee"
|
||||||
|
borderopacity="1"
|
||||||
|
inkscape:showpageshadow="false"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pagecheckerboard="true"
|
||||||
|
inkscape:deskcolor="#505050"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
labelstyle="default"
|
||||||
|
inkscape:clip-to-page="false"
|
||||||
|
inkscape:zoom="181.01934"
|
||||||
|
inkscape:cx="9.7890095"
|
||||||
|
inkscape:cy="6.7534222"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1001"
|
||||||
|
inkscape:window-x="-9"
|
||||||
|
inkscape:window-y="-9"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="layer2"
|
||||||
|
showguides="true" />
|
||||||
|
<defs
|
||||||
|
id="defs1">
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient22"
|
||||||
|
inkscape:collect="always">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#bf33cc;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop22" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#4791d1;stop-opacity:1;"
|
||||||
|
offset="1"
|
||||||
|
id="stop23" />
|
||||||
|
</linearGradient>
|
||||||
|
<inkscape:path-effect
|
||||||
|
effect="spiro"
|
||||||
|
id="path-effect13"
|
||||||
|
is_visible="true"
|
||||||
|
lpeversion="1" />
|
||||||
|
<inkscape:path-effect
|
||||||
|
effect="spiro"
|
||||||
|
id="path-effect11"
|
||||||
|
is_visible="true"
|
||||||
|
lpeversion="1" />
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient22"
|
||||||
|
id="linearGradient23"
|
||||||
|
x1="10.353516"
|
||||||
|
y1="4.000001"
|
||||||
|
x2="17.814453"
|
||||||
|
y2="20"
|
||||||
|
gradientUnits="userSpaceOnUse" />
|
||||||
|
</defs>
|
||||||
|
<g
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer2"
|
||||||
|
inkscape:label="strokes">
|
||||||
|
<path
|
||||||
|
id="path6"
|
||||||
|
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#333333;fill-opacity:0.80000001;stroke:none;stroke-width:0;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
|
||||||
|
inkscape:label="EX-stroke"
|
||||||
|
d="M -0.230469 3.5 L -0.230469 20.5 L 7.4843747 20.5 L 8.769531 20.5 L 10.794922 20.5 L 13.103515 15.548828 L 15.412109 20.5 L 18.722656 20.5 L 14.757812 12 L 18.722656 3.5 L 15.412109 3.5 L 13.103515 8.4511719 L 10.794922 3.5 L 8.769531 3.5 L 7.4843747 3.5 L -0.230469 3.5 z M 2.769531 6.5 L 8.8828123 6.5 L 11.449219 12 L 8.8828123 17.5 L 2.769531 17.5 L 2.769531 13.5 L 7.769531 13.5 L 7.769531 10.5 L 2.769531 10.5 L 2.769531 6.5 z "
|
||||||
|
transform="translate(0.980469)" />
|
||||||
|
<path
|
||||||
|
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#333333;fill-opacity:0.80000001;stroke:none;stroke-width:0;stroke-linecap:square;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
|
||||||
|
d="m 17.769531,8.5 v 2 h -2 v 3 h 2 v 2 h 3 v -2 h 2 v -3 h -2 v -2 z"
|
||||||
|
id="path1"
|
||||||
|
inkscape:label="plus-stroke"
|
||||||
|
transform="translate(0.980469)"
|
||||||
|
sodipodi:nodetypes="ccccccccccccc" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
inkscape:label="base"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
style="display:inline;fill:url(#linearGradient23);stroke:none;stroke-width:0;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill">
|
||||||
|
<path
|
||||||
|
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;fill:url(#linearGradient23);stroke:#cccccc;stroke-width:0;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
|
||||||
|
d="m 0.269531,4 v 16 h 8 v -2 h -6 v -5 h 5 v -2 h -5 V 6 h 6 V 4 Z"
|
||||||
|
id="path7"
|
||||||
|
inkscape:label="E"
|
||||||
|
transform="translate(0.980469)" />
|
||||||
|
<path
|
||||||
|
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;fill:url(#linearGradient23);stroke:#cccccc;stroke-width:0;stroke-linecap:square;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
|
||||||
|
d="M 8.2695306,4.0000008 12,12 8.2695306,20 H 10.476562 L 13.103515,14.367188 15.730468,20 H 17.9375 L 14.207031,12 17.9375,4.0000008 H 15.730468 L 13.103515,9.6328133 10.476562,4.0000008 Z"
|
||||||
|
id="path4"
|
||||||
|
inkscape:label="X"
|
||||||
|
transform="translate(0.980469)" />
|
||||||
|
<path
|
||||||
|
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;fill:url(#linearGradient23);stroke:#cccccc;stroke-width:0;stroke-linecap:square;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
|
||||||
|
d="m 18.269531,9 v 1 1 h -1 -1 v 2 h 1 1 v 1 1 h 2 v -1 -1 h 1 1 v -2 h -1 -1 V 10 9 Z"
|
||||||
|
id="path2"
|
||||||
|
inkscape:label="plus"
|
||||||
|
transform="translate(0.980469)" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 5.3 KiB |
115
ui/resources/images/grades/ex-plus.svg
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0.755 1 23 23"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
|
||||||
|
sodipodi:docname="ex-plus.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview1"
|
||||||
|
pagecolor="#505050"
|
||||||
|
bordercolor="#eeeeee"
|
||||||
|
borderopacity="1"
|
||||||
|
inkscape:showpageshadow="false"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pagecheckerboard="true"
|
||||||
|
inkscape:deskcolor="#505050"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
labelstyle="default"
|
||||||
|
inkscape:clip-to-page="false"
|
||||||
|
inkscape:zoom="32"
|
||||||
|
inkscape:cx="2.75"
|
||||||
|
inkscape:cy="12.296875"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1001"
|
||||||
|
inkscape:window-x="-9"
|
||||||
|
inkscape:window-y="-9"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="layer3"
|
||||||
|
showguides="true" />
|
||||||
|
<defs
|
||||||
|
id="defs1">
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient22"
|
||||||
|
inkscape:collect="always">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#83238c;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop22" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#2c72ae;stop-opacity:1;"
|
||||||
|
offset="1"
|
||||||
|
id="stop23" />
|
||||||
|
</linearGradient>
|
||||||
|
<inkscape:path-effect
|
||||||
|
effect="spiro"
|
||||||
|
id="path-effect13"
|
||||||
|
is_visible="true"
|
||||||
|
lpeversion="1" />
|
||||||
|
<inkscape:path-effect
|
||||||
|
effect="spiro"
|
||||||
|
id="path-effect11"
|
||||||
|
is_visible="true"
|
||||||
|
lpeversion="1" />
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient22"
|
||||||
|
id="linearGradient23"
|
||||||
|
x1="10.353516"
|
||||||
|
y1="4.000001"
|
||||||
|
x2="17.814453"
|
||||||
|
y2="20"
|
||||||
|
gradientUnits="userSpaceOnUse" />
|
||||||
|
</defs>
|
||||||
|
<g
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer3"
|
||||||
|
inkscape:label="strokes">
|
||||||
|
<path
|
||||||
|
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#cccccc;fill-opacity:0.8;stroke:none;stroke-width:0;stroke-linecap:square;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
|
||||||
|
d="m 17.769531,8.5 v 2 h -2 v 3 h 2 v 2 h 3 v -2 h 2 v -3 h -2 v -2 z"
|
||||||
|
id="path26"
|
||||||
|
inkscape:label="plus-stroke"
|
||||||
|
transform="translate(0.980469)"
|
||||||
|
sodipodi:nodetypes="ccccccccccccc" />
|
||||||
|
<path
|
||||||
|
id="path28"
|
||||||
|
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#cccccc;fill-opacity:0.8;stroke:none;stroke-width:0;stroke-linecap:square;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
|
||||||
|
inkscape:label="X-stroke"
|
||||||
|
d="m -0.230469,3.5 v 17 H 7.4843747 8.769531 10.794922 L 13.103515,15.548828 15.412109,20.5 h 3.310547 L 14.757812,12 18.722656,3.5 H 15.412109 L 13.103515,8.4511719 10.794922,3.5 H 8.769531 7.4843747 Z m 3,3 H 8.8828123 L 11.449219,12 8.8828123,17.5 H 2.769531 v -4 h 5 v -3 h -5 z"
|
||||||
|
transform="translate(0.980469)" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
inkscape:label="base"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
style="display:inline;fill:url(#linearGradient23);stroke:none;stroke-width:0;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill">
|
||||||
|
<path
|
||||||
|
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:url(#linearGradient23);stroke:none;stroke-width:0;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
|
||||||
|
d="m 0.269531,4 v 16 h 8 v -2 h -6 v -5 h 5 v -2 h -5 V 6 h 6 V 4 Z"
|
||||||
|
id="path31"
|
||||||
|
inkscape:label="E"
|
||||||
|
transform="translate(0.980469)" />
|
||||||
|
<path
|
||||||
|
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:url(#linearGradient23);stroke:none;stroke-width:0;stroke-linecap:square;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
|
||||||
|
d="M 8.2695306,4.0000008 12,12 8.2695306,20 H 10.476562 L 13.103515,14.367188 15.730468,20 H 17.9375 L 14.207031,12 17.9375,4.0000008 H 15.730468 L 13.103515,9.6328133 10.476562,4.0000008 Z"
|
||||||
|
id="path29"
|
||||||
|
inkscape:label="X"
|
||||||
|
transform="translate(0.980469)" />
|
||||||
|
<path
|
||||||
|
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:url(#linearGradient23);stroke:none;stroke-width:0;stroke-linecap:square;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
|
||||||
|
d="m 18.269531,9 v 1 1 h -1 -1 v 2 h 1 1 v 1 1 h 2 v -1 -1 h 1 1 v -2 h -1 -1 V 10 9 Z"
|
||||||
|
id="path27"
|
||||||
|
inkscape:label="plus"
|
||||||
|
transform="translate(0.980469)" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 5.2 KiB |
119
ui/resources/images/grades/ex.svg
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="1 1 23 23"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
|
||||||
|
sodipodi:docname="ex.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview1"
|
||||||
|
pagecolor="#505050"
|
||||||
|
bordercolor="#eeeeee"
|
||||||
|
borderopacity="1"
|
||||||
|
inkscape:showpageshadow="false"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pagecheckerboard="true"
|
||||||
|
inkscape:deskcolor="#505050"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
labelstyle="default"
|
||||||
|
inkscape:clip-to-page="false"
|
||||||
|
inkscape:zoom="32.791667"
|
||||||
|
inkscape:cx="12"
|
||||||
|
inkscape:cy="12"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1001"
|
||||||
|
inkscape:window-x="-9"
|
||||||
|
inkscape:window-y="-9"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showguides="true" />
|
||||||
|
<defs
|
||||||
|
id="defs1">
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient22"
|
||||||
|
inkscape:collect="always">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#721b6b;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop22" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#295b8d;stop-opacity:1;"
|
||||||
|
offset="1"
|
||||||
|
id="stop23" />
|
||||||
|
</linearGradient>
|
||||||
|
<inkscape:path-effect
|
||||||
|
effect="spiro"
|
||||||
|
id="path-effect13"
|
||||||
|
is_visible="true"
|
||||||
|
lpeversion="1" />
|
||||||
|
<inkscape:path-effect
|
||||||
|
effect="spiro"
|
||||||
|
id="path-effect11"
|
||||||
|
is_visible="true"
|
||||||
|
lpeversion="1" />
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient22"
|
||||||
|
id="linearGradient23"
|
||||||
|
x1="10.353516"
|
||||||
|
y1="4.000001"
|
||||||
|
x2="17.814453"
|
||||||
|
y2="20"
|
||||||
|
gradientUnits="userSpaceOnUse" />
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient22"
|
||||||
|
id="linearGradient1"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="10.353516"
|
||||||
|
y1="4.000001"
|
||||||
|
x2="17.814453"
|
||||||
|
y2="20"
|
||||||
|
gradientTransform="translate(3.130469)" />
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient22"
|
||||||
|
id="linearGradient2"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="10.353516"
|
||||||
|
y1="4.000001"
|
||||||
|
x2="17.814453"
|
||||||
|
y2="20"
|
||||||
|
gradientTransform="translate(3.130469)" />
|
||||||
|
</defs>
|
||||||
|
<g
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer3"
|
||||||
|
inkscape:label="strokes">
|
||||||
|
<path
|
||||||
|
id="path28"
|
||||||
|
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#cccccc;fill-opacity:0.8;stroke:none;stroke-width:0;stroke-linecap:square;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
|
||||||
|
inkscape:label="X-stroke"
|
||||||
|
d="m 2.9,3.5 v 17 H 10.614844 11.9 13.925391 L 16.233984,15.548828 18.542578,20.5 h 3.310547 L 17.888281,12 21.853125,3.5 H 18.542578 L 16.233984,8.4511719 13.925391,3.5 H 11.9 10.614844 Z m 3,3 h 6.113281 l 2.566407,5.5 -2.566407,5.5 H 5.9 v -4 h 5 v -3 h -5 z" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
inkscape:label="base"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
style="display:inline;fill:url(#linearGradient23);stroke:none;stroke-width:0;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill">
|
||||||
|
<path
|
||||||
|
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:url(#linearGradient1);stroke:none;stroke-width:0;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
|
||||||
|
d="m 3.4,4 v 16 h 8 v -2 h -6 v -5 h 5 v -2 h -5 V 6 h 6 V 4 Z"
|
||||||
|
id="path31"
|
||||||
|
inkscape:label="E" />
|
||||||
|
<path
|
||||||
|
style="baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:url(#linearGradient2);stroke:none;stroke-width:0;stroke-linecap:square;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
|
||||||
|
d="M 11.4,4.0000008 15.130469,12 11.4,20 h 2.207031 L 16.233984,14.367188 18.860937,20 h 2.207032 L 17.3375,12 21.067969,4.0000008 H 18.860937 L 16.233984,9.6328133 13.607031,4.0000008 Z"
|
||||||
|
id="path29"
|
||||||
|
inkscape:label="X" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 4.5 KiB |
72
ui/resources/images/grades/grade-template.svg
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="1 1 23 23"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
|
||||||
|
sodipodi:docname="grade-template.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview1"
|
||||||
|
pagecolor="#505050"
|
||||||
|
bordercolor="#eeeeee"
|
||||||
|
borderopacity="1"
|
||||||
|
inkscape:showpageshadow="false"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pagecheckerboard="true"
|
||||||
|
inkscape:deskcolor="#505050"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
labelstyle="default"
|
||||||
|
inkscape:clip-to-page="false"
|
||||||
|
inkscape:zoom="22.435241"
|
||||||
|
inkscape:cx="10.563738"
|
||||||
|
inkscape:cy="11.789488"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1001"
|
||||||
|
inkscape:window-x="-9"
|
||||||
|
inkscape:window-y="-9"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showguides="true">
|
||||||
|
<sodipodi:guide
|
||||||
|
position="3.0000001,19"
|
||||||
|
orientation="14,0"
|
||||||
|
id="guide1"
|
||||||
|
inkscape:locked="false" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="3.0000001,5.0000001"
|
||||||
|
orientation="0,18"
|
||||||
|
id="guide2"
|
||||||
|
inkscape:locked="false" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="21,5.0000001"
|
||||||
|
orientation="-14,0"
|
||||||
|
id="guide3"
|
||||||
|
inkscape:locked="false" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="21,19"
|
||||||
|
orientation="0,-18"
|
||||||
|
id="guide4"
|
||||||
|
inkscape:locked="false" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="12,12.000001"
|
||||||
|
orientation="-1,0"
|
||||||
|
id="guide5"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,134,229)" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<defs
|
||||||
|
id="defs1" />
|
||||||
|
<g
|
||||||
|
inkscape:label="图层 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.0 KiB |
43
ui/resources/images/grades/preview.html
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Grade Previews</title>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<style>
|
||||||
|
.display {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 150px;
|
||||||
|
height: 150px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="main" style="display: flex; flex-direction: column">
|
||||||
|
<div class="gallery">
|
||||||
|
<img src="./ex-plus.svg" />
|
||||||
|
<img src="./ex.svg" />
|
||||||
|
<img src="./aa.svg" />
|
||||||
|
<img src="./a.svg" />
|
||||||
|
<img src="./b.svg" />
|
||||||
|
<img src="./c.svg" />
|
||||||
|
<img src="./d.svg" />
|
||||||
|
</div>
|
||||||
|
<div class="gallery" style="background-color: #282828">
|
||||||
|
<img src="./ex-plus-dark.svg" />
|
||||||
|
<img src="./ex-dark.svg" />
|
||||||
|
<img src="./aa-dark.svg" />
|
||||||
|
<img src="./a-dark.svg" />
|
||||||
|
<img src="./b-dark.svg" />
|
||||||
|
<img src="./c-dark.svg" />
|
||||||
|
<img src="./d-dark.svg" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@ -70,12 +70,12 @@
|
|||||||
<translation>Continue</translation>
|
<translation>Continue</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../startup/databaseChecker.py" line="117"/>
|
<location filename="../../startup/databaseChecker.py" line="122"/>
|
||||||
<source>dialog.tryInitExistingDatabase</source>
|
<source>dialog.tryInitExistingDatabase</source>
|
||||||
<translation>The existing database doesn't seem to be initialized properly, try initialize again?</translation>
|
<translation>The existing database doesn't seem to be initialized properly, try initialize again?</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../startup/databaseChecker.py" line="133"/>
|
<location filename="../../startup/databaseChecker.py" line="138"/>
|
||||||
<source>dialog.confirmNewDatabase</source>
|
<source>dialog.confirmNewDatabase</source>
|
||||||
<translation>Database file does not exist. Create now?</translation>
|
<translation>Database file does not exist. Create now?</translation>
|
||||||
</message>
|
</message>
|
||||||
@ -224,85 +224,118 @@
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/components/ocrQueue.ui" line="28"/>
|
<location filename="../../designer/components/ocrQueue.ui" line="28"/>
|
||||||
<source>iccOptionsGroupBox</source>
|
|
||||||
<translation>ICC Profile Options</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<location filename="../../designer/components/ocrQueue.ui" line="34"/>
|
|
||||||
<source>icc.useQt</source>
|
|
||||||
<translation>Use Qt</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<location filename="../../designer/components/ocrQueue.ui" line="41"/>
|
|
||||||
<source>icc.usePIL</source>
|
|
||||||
<translation>Use PIL</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<location filename="../../designer/components/ocrQueue.ui" line="51"/>
|
|
||||||
<source>icc.tryFix</source>
|
|
||||||
<translation>Try fix</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<location filename="../../designer/components/ocrQueue.ui" line="61"/>
|
|
||||||
<source>queue.addImageButton</source>
|
<source>queue.addImageButton</source>
|
||||||
<translation>Add Image</translation>
|
<translation>Add Image</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/components/ocrQueue.ui" line="71"/>
|
<location filename="../../designer/components/ocrQueue.ui" line="38"/>
|
||||||
<source>queue.removeSelected</source>
|
<source>queue.removeSelected</source>
|
||||||
<translation>Remove Selected</translation>
|
<translation>Remove Selected</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/components/ocrQueue.ui" line="81"/>
|
<location filename="../../designer/components/ocrQueue.ui" line="48"/>
|
||||||
<source>queue.removeAll</source>
|
<source>queue.removeAll</source>
|
||||||
<translation>Remove All</translation>
|
<translation>Remove All</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/components/ocrQueue.ui" line="101"/>
|
<location filename="../../designer/components/ocrQueue.ui" line="68"/>
|
||||||
|
<source>queue.optionsButton</source>
|
||||||
|
<translation>Options</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../../designer/components/ocrQueue.ui" line="75"/>
|
||||||
<source>queue.startOcrButton</source>
|
<source>queue.startOcrButton</source>
|
||||||
<translation>Start OCR</translation>
|
<translation>Start OCR</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/components/ocrQueue.ui" line="153"/>
|
<location filename="../../designer/components/ocrQueue.ui" line="127"/>
|
||||||
<source>results</source>
|
<source>results</source>
|
||||||
<translation>Results</translation>
|
<translation>Results</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/components/ocrQueue.ui" line="162"/>
|
<location filename="../../designer/components/ocrQueue.ui" line="136"/>
|
||||||
<source>results.acceptSelectedButton</source>
|
<source>results.acceptSelectedButton</source>
|
||||||
<translation>Accept Selected</translation>
|
<translation>Accept Selected</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/components/ocrQueue.ui" line="169"/>
|
<location filename="../../designer/components/ocrQueue.ui" line="143"/>
|
||||||
<source>results.acceptAllButton</source>
|
<source>results.acceptAllButton</source>
|
||||||
<translation>Accept All</translation>
|
<translation>Accept All</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/components/ocrQueue.ui" line="189"/>
|
<location filename="../../designer/components/ocrQueue.ui" line="163"/>
|
||||||
<source>results.ignoreValidate</source>
|
<source>results.ignoreValidate</source>
|
||||||
<translation>Ignore
|
<translation>Ignore
|
||||||
validation</translation>
|
validation</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>OcrQueueOptionsDialog</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="14"/>
|
||||||
|
<source>OCR Options</source>
|
||||||
|
<translation>OCR Options</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="22"/>
|
||||||
|
<source>iccOptionsGroupBox</source>
|
||||||
|
<translation>ICC Profile Options</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="28"/>
|
||||||
|
<source>icc.useQt</source>
|
||||||
|
<translation>Use Qt</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="35"/>
|
||||||
|
<source>icc.usePIL</source>
|
||||||
|
<translation>Use PIL</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="45"/>
|
||||||
|
<source>icc.tryFix</source>
|
||||||
|
<translation>Try fix</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="55"/>
|
||||||
|
<source>dateOptionsGroupBox</source>
|
||||||
|
<translation>Date Source</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="64"/>
|
||||||
|
<source>date.readFromExif</source>
|
||||||
|
<translation>Read from image EXIF</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="74"/>
|
||||||
|
<source>date.useCreationDate</source>
|
||||||
|
<translation>File creation time</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="84"/>
|
||||||
|
<source>date.useModifyDate</source>
|
||||||
|
<translation>File last modification time</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>OcrTableModel</name>
|
<name>OcrTableModel</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../extends/components/ocrQueue.py" line="347"/>
|
<location filename="../../extends/components/ocrQueue.py" line="348"/>
|
||||||
<source>horizontalHeader.title.select</source>
|
<source>horizontalHeader.title.select</source>
|
||||||
<translation>Select</translation>
|
<translation>Select</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../extends/components/ocrQueue.py" line="348"/>
|
<location filename="../../extends/components/ocrQueue.py" line="351"/>
|
||||||
<source>horizontalHeader.title.imagePreview</source>
|
<source>horizontalHeader.title.imagePreview</source>
|
||||||
<translation>Image Preview</translation>
|
<translation>Image Preview</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../extends/components/ocrQueue.py" line="349"/>
|
<location filename="../../extends/components/ocrQueue.py" line="352"/>
|
||||||
<source>horizontalHeader.title.chart</source>
|
<source>horizontalHeader.title.chart</source>
|
||||||
<translation>Chart</translation>
|
<translation>Chart</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../extends/components/ocrQueue.py" line="350"/>
|
<location filename="../../extends/components/ocrQueue.py" line="353"/>
|
||||||
<source>horizontalHeader.title.score</source>
|
<source>horizontalHeader.title.score</source>
|
||||||
<translation>Score</translation>
|
<translation>Score</translation>
|
||||||
</message>
|
</message>
|
||||||
|
|||||||
@ -70,12 +70,12 @@
|
|||||||
<translation>继续</translation>
|
<translation>继续</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../startup/databaseChecker.py" line="117"/>
|
<location filename="../../startup/databaseChecker.py" line="122"/>
|
||||||
<source>dialog.tryInitExistingDatabase</source>
|
<source>dialog.tryInitExistingDatabase</source>
|
||||||
<translation>现有的数据库似乎没有正确初始化,是否尝试再次初始化?</translation>
|
<translation>现有的数据库似乎没有正确初始化,是否尝试再次初始化?</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../startup/databaseChecker.py" line="133"/>
|
<location filename="../../startup/databaseChecker.py" line="138"/>
|
||||||
<source>dialog.confirmNewDatabase</source>
|
<source>dialog.confirmNewDatabase</source>
|
||||||
<translation>数据库文件不存在,是否创建?</translation>
|
<translation>数据库文件不存在,是否创建?</translation>
|
||||||
</message>
|
</message>
|
||||||
@ -224,84 +224,117 @@
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/components/ocrQueue.ui" line="28"/>
|
<location filename="../../designer/components/ocrQueue.ui" line="28"/>
|
||||||
<source>iccOptionsGroupBox</source>
|
|
||||||
<translation>ICC 文件选项</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<location filename="../../designer/components/ocrQueue.ui" line="34"/>
|
|
||||||
<source>icc.useQt</source>
|
|
||||||
<translation>使用 Qt</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<location filename="../../designer/components/ocrQueue.ui" line="41"/>
|
|
||||||
<source>icc.usePIL</source>
|
|
||||||
<translation>使用 PIL</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<location filename="../../designer/components/ocrQueue.ui" line="51"/>
|
|
||||||
<source>icc.tryFix</source>
|
|
||||||
<translation>尝试修复</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<location filename="../../designer/components/ocrQueue.ui" line="61"/>
|
|
||||||
<source>queue.addImageButton</source>
|
<source>queue.addImageButton</source>
|
||||||
<translation>添加图像文件</translation>
|
<translation>添加图像文件</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/components/ocrQueue.ui" line="71"/>
|
<location filename="../../designer/components/ocrQueue.ui" line="38"/>
|
||||||
<source>queue.removeSelected</source>
|
<source>queue.removeSelected</source>
|
||||||
<translation>移除选中</translation>
|
<translation>移除选中</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/components/ocrQueue.ui" line="81"/>
|
<location filename="../../designer/components/ocrQueue.ui" line="48"/>
|
||||||
<source>queue.removeAll</source>
|
<source>queue.removeAll</source>
|
||||||
<translation>移除所有</translation>
|
<translation>移除所有</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/components/ocrQueue.ui" line="101"/>
|
<location filename="../../designer/components/ocrQueue.ui" line="68"/>
|
||||||
|
<source>queue.optionsButton</source>
|
||||||
|
<translation>选项</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../../designer/components/ocrQueue.ui" line="75"/>
|
||||||
<source>queue.startOcrButton</source>
|
<source>queue.startOcrButton</source>
|
||||||
<translation>开始 OCR</translation>
|
<translation>开始 OCR</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/components/ocrQueue.ui" line="153"/>
|
<location filename="../../designer/components/ocrQueue.ui" line="127"/>
|
||||||
<source>results</source>
|
<source>results</source>
|
||||||
<translation>结果</translation>
|
<translation>结果</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/components/ocrQueue.ui" line="162"/>
|
<location filename="../../designer/components/ocrQueue.ui" line="136"/>
|
||||||
<source>results.acceptSelectedButton</source>
|
<source>results.acceptSelectedButton</source>
|
||||||
<translation>提交选中</translation>
|
<translation>提交选中</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/components/ocrQueue.ui" line="169"/>
|
<location filename="../../designer/components/ocrQueue.ui" line="143"/>
|
||||||
<source>results.acceptAllButton</source>
|
<source>results.acceptAllButton</source>
|
||||||
<translation>提交所有</translation>
|
<translation>提交所有</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../designer/components/ocrQueue.ui" line="189"/>
|
<location filename="../../designer/components/ocrQueue.ui" line="163"/>
|
||||||
<source>results.ignoreValidate</source>
|
<source>results.ignoreValidate</source>
|
||||||
<translation>忽略验证</translation>
|
<translation>忽略验证</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>OcrQueueOptionsDialog</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="14"/>
|
||||||
|
<source>OCR Options</source>
|
||||||
|
<translation>OCR 选项</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="22"/>
|
||||||
|
<source>iccOptionsGroupBox</source>
|
||||||
|
<translation>ICC 文件选项</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="28"/>
|
||||||
|
<source>icc.useQt</source>
|
||||||
|
<translation>使用 Qt</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="35"/>
|
||||||
|
<source>icc.usePIL</source>
|
||||||
|
<translation>使用 PIL</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="45"/>
|
||||||
|
<source>icc.tryFix</source>
|
||||||
|
<translation>尝试修复</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="55"/>
|
||||||
|
<source>dateOptionsGroupBox</source>
|
||||||
|
<translation>日期来源</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="64"/>
|
||||||
|
<source>date.readFromExif</source>
|
||||||
|
<translation>从 EXIF 读取</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="74"/>
|
||||||
|
<source>date.useCreationDate</source>
|
||||||
|
<translation>文件创建时间</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="84"/>
|
||||||
|
<source>date.useModifyDate</source>
|
||||||
|
<translation>文件最后修改时间</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>OcrTableModel</name>
|
<name>OcrTableModel</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../extends/components/ocrQueue.py" line="347"/>
|
<location filename="../../extends/components/ocrQueue.py" line="348"/>
|
||||||
<source>horizontalHeader.title.select</source>
|
<source>horizontalHeader.title.select</source>
|
||||||
<translation>选择</translation>
|
<translation>选择</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../extends/components/ocrQueue.py" line="348"/>
|
<location filename="../../extends/components/ocrQueue.py" line="351"/>
|
||||||
<source>horizontalHeader.title.imagePreview</source>
|
<source>horizontalHeader.title.imagePreview</source>
|
||||||
<translation>图像预览</translation>
|
<translation>图像预览</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../extends/components/ocrQueue.py" line="349"/>
|
<location filename="../../extends/components/ocrQueue.py" line="352"/>
|
||||||
<source>horizontalHeader.title.chart</source>
|
<source>horizontalHeader.title.chart</source>
|
||||||
<translation>谱面</translation>
|
<translation>谱面</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../extends/components/ocrQueue.py" line="350"/>
|
<location filename="../../extends/components/ocrQueue.py" line="353"/>
|
||||||
<source>horizontalHeader.title.score</source>
|
<source>horizontalHeader.title.score</source>
|
||||||
<translation>分数</translation>
|
<translation>分数</translation>
|
||||||
</message>
|
</message>
|
||||||
|
|||||||
@ -10,6 +10,20 @@
|
|||||||
|
|
||||||
<file>images/icon.png</file>
|
<file>images/icon.png</file>
|
||||||
<file>images/logo.png</file>
|
<file>images/logo.png</file>
|
||||||
|
<file>images/grades/ex-plus.svg</file>
|
||||||
|
<file>images/grades/ex-plus-dark.svg</file>
|
||||||
|
<file>images/grades/ex.svg</file>
|
||||||
|
<file>images/grades/ex-dark.svg</file>
|
||||||
|
<file>images/grades/aa.svg</file>
|
||||||
|
<file>images/grades/aa-dark.svg</file>
|
||||||
|
<file>images/grades/a.svg</file>
|
||||||
|
<file>images/grades/a-dark.svg</file>
|
||||||
|
<file>images/grades/b.svg</file>
|
||||||
|
<file>images/grades/b-dark.svg</file>
|
||||||
|
<file>images/grades/c.svg</file>
|
||||||
|
<file>images/grades/c-dark.svg</file>
|
||||||
|
<file>images/grades/d.svg</file>
|
||||||
|
<file>images/grades/d-dark.svg</file>
|
||||||
<file>images/jacket-placeholder.png</file>
|
<file>images/jacket-placeholder.png</file>
|
||||||
<file>images/stepCalculator/stamina.png</file>
|
<file>images/stepCalculator/stamina.png</file>
|
||||||
<file>images/stepCalculator/play.png</file>
|
<file>images/stepCalculator/play.png</file>
|
||||||
@ -19,5 +33,10 @@
|
|||||||
|
|
||||||
<file>lang/zh_CN.qm</file>
|
<file>lang/zh_CN.qm</file>
|
||||||
<file>lang/en_US.qm</file>
|
<file>lang/en_US.qm</file>
|
||||||
|
|
||||||
|
<file>themes/m3-dynamic_default.json</file>
|
||||||
|
<file>themes/m3-dynamic_tempest.json</file>
|
||||||
|
<file>themes/m3-dynamic_devilun.json</file>
|
||||||
|
<file>themes/m3-dynamic_kupya.json</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|||||||
8
ui/resources/themes/m3-dynamic_default.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"id": "default",
|
||||||
|
"name": "Default",
|
||||||
|
"colors": {
|
||||||
|
"primary": "#4e486c"
|
||||||
|
},
|
||||||
|
"options": {}
|
||||||
|
}
|
||||||
11
ui/resources/themes/m3-dynamic_devilun.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"id": "devilun",
|
||||||
|
"name": "Devilun",
|
||||||
|
"colors": {
|
||||||
|
"primary": "#d1779d",
|
||||||
|
"secondary": "#85a9a5"
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"forceScheme": "dark"
|
||||||
|
}
|
||||||
|
}
|
||||||
10
ui/resources/themes/m3-dynamic_kupya.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"id": "kupya",
|
||||||
|
"name": "Kupya",
|
||||||
|
"colors": {
|
||||||
|
"primary": "#ffeb9e"
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"forceScheme": "light"
|
||||||
|
}
|
||||||
|
}
|
||||||
8
ui/resources/themes/m3-dynamic_tempest.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"id": "tempest",
|
||||||
|
"name": "Tempest",
|
||||||
|
"colors": {
|
||||||
|
"primary": "#186d98"
|
||||||
|
},
|
||||||
|
"options": {}
|
||||||
|
}
|
||||||
@ -1,148 +0,0 @@
|
|||||||
import logging
|
|
||||||
import traceback
|
|
||||||
from enum import IntEnum
|
|
||||||
|
|
||||||
from arcaea_offline.database import Database
|
|
||||||
from PySide6.QtCore import QCoreApplication, QDir, QFileInfo, Qt, QUrl, Slot
|
|
||||||
from PySide6.QtWidgets import QDialog, QMessageBox
|
|
||||||
|
|
||||||
from ui.extends.shared.database import create_engine
|
|
||||||
from ui.extends.shared.settings import Settings
|
|
||||||
|
|
||||||
from .databaseChecker_ui import Ui_DatabaseChecker
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class DatabaseCheckerResult(IntEnum):
|
|
||||||
FileExist = 0x001
|
|
||||||
Initted = 0x002
|
|
||||||
|
|
||||||
|
|
||||||
class DatabaseChecker(Ui_DatabaseChecker, QDialog):
|
|
||||||
def __init__(self, parent=None):
|
|
||||||
super().__init__(parent)
|
|
||||||
self.setupUi(self)
|
|
||||||
self.setWindowFlag(Qt.WindowType.WindowMinimizeButtonHint, False)
|
|
||||||
self.setWindowFlag(Qt.WindowType.WindowMaximizeButtonHint, False)
|
|
||||||
self.setWindowFlag(Qt.WindowType.WindowCloseButtonHint, True)
|
|
||||||
self.dbDirSelector.setMode(self.dbDirSelector.getExistingDirectory)
|
|
||||||
|
|
||||||
self.confirmDbByExistingSettings = False
|
|
||||||
self.settings = Settings(self)
|
|
||||||
if dbUrlString := self.settings.databaseUrl():
|
|
||||||
dbFileUrl = QUrl(dbUrlString.replace("sqlite://", "file://"))
|
|
||||||
dbFileInfo = QFileInfo(dbFileUrl.toLocalFile())
|
|
||||||
if dbFileInfo.exists():
|
|
||||||
self.dbDirSelector.selectFile(dbFileInfo.path())
|
|
||||||
self.dbFilenameLineEdit.setText(dbFileInfo.fileName())
|
|
||||||
self.confirmDbByExistingSettings = True
|
|
||||||
self.confirmDbPathButton.click()
|
|
||||||
else:
|
|
||||||
self.dbDirSelector.selectFile(QDir.currentPath())
|
|
||||||
self.dbFilenameLineEdit.setText("arcaea_offline.db")
|
|
||||||
else:
|
|
||||||
self.dbDirSelector.selectFile(QDir.currentPath())
|
|
||||||
self.dbFilenameLineEdit.setText("arcaea_offline.db")
|
|
||||||
|
|
||||||
def dbPath(self):
|
|
||||||
return QDir(self.dbDirSelector.selectedFiles()[0])
|
|
||||||
|
|
||||||
def dbFileInfo(self):
|
|
||||||
return QFileInfo(
|
|
||||||
QDir.cleanPath(
|
|
||||||
self.dbPath().absoluteFilePath(self.dbFilenameLineEdit.text())
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def dbFileUrl(self):
|
|
||||||
return QUrl.fromLocalFile(self.dbFileInfo().filePath())
|
|
||||||
|
|
||||||
def dbSqliteUrl(self):
|
|
||||||
# dbSqliteUrl.setScheme("sqlite")
|
|
||||||
return QUrl(self.dbFileUrl().toString().replace("file://", "sqlite://"))
|
|
||||||
|
|
||||||
def confirmDb(self) -> DatabaseCheckerResult:
|
|
||||||
flags = 0x000
|
|
||||||
|
|
||||||
dbFileInfo = self.dbFileInfo()
|
|
||||||
dbSqliteUrl = self.dbSqliteUrl()
|
|
||||||
if not dbFileInfo.exists():
|
|
||||||
return flags
|
|
||||||
|
|
||||||
flags |= DatabaseCheckerResult.FileExist
|
|
||||||
db = Database(create_engine(dbSqliteUrl))
|
|
||||||
if db.check_init():
|
|
||||||
flags |= DatabaseCheckerResult.Initted
|
|
||||||
self.settings.setDatabaseUrl(self.dbSqliteUrl().toString())
|
|
||||||
|
|
||||||
return flags
|
|
||||||
|
|
||||||
def updateLabels(self):
|
|
||||||
result = self.confirmDb()
|
|
||||||
try:
|
|
||||||
db = Database()
|
|
||||||
version = db.version()
|
|
||||||
initted = db.check_init()
|
|
||||||
self.dbVersionLabel.setText(str(version))
|
|
||||||
self.dbCheckConnLabel.setText(
|
|
||||||
'<font color="green">OK</font>'
|
|
||||||
if initted
|
|
||||||
else '<font color="red">Not initted</font>'
|
|
||||||
)
|
|
||||||
self.continueButton.setEnabled(initted)
|
|
||||||
except Exception as e:
|
|
||||||
self.dbVersionLabel.setText("-")
|
|
||||||
self.dbCheckConnLabel.setText(
|
|
||||||
f'<font color="red">Error: {e}</font>'
|
|
||||||
if result & DatabaseCheckerResult.FileExist
|
|
||||||
else "-"
|
|
||||||
)
|
|
||||||
self.continueButton.setEnabled(False)
|
|
||||||
|
|
||||||
@Slot()
|
|
||||||
def on_confirmDbPathButton_clicked(self):
|
|
||||||
dbSqliteUrl = self.dbSqliteUrl()
|
|
||||||
self.settings.setDatabaseUrl(dbSqliteUrl.toString())
|
|
||||||
|
|
||||||
result = self.confirmDb()
|
|
||||||
if result & DatabaseCheckerResult.Initted:
|
|
||||||
if not self.confirmDbByExistingSettings:
|
|
||||||
self.settings.setDatabaseUrl(dbSqliteUrl.toString())
|
|
||||||
elif result & DatabaseCheckerResult.FileExist:
|
|
||||||
confirm_try_init = QMessageBox.question(
|
|
||||||
self,
|
|
||||||
None,
|
|
||||||
# fmt: off
|
|
||||||
QCoreApplication.translate("DatabaseChecker", "dialog.tryInitExistingDatabase"),
|
|
||||||
# fmt: on
|
|
||||||
)
|
|
||||||
if confirm_try_init == QMessageBox.StandardButton.Yes:
|
|
||||||
try:
|
|
||||||
Database().init(checkfirst=True)
|
|
||||||
except Exception as e:
|
|
||||||
logger.exception("Error while initializing an existing database")
|
|
||||||
QMessageBox.critical(
|
|
||||||
self, None, "\n".join(traceback.format_exception(e))
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
confirm_new_database = QMessageBox.question(
|
|
||||||
self,
|
|
||||||
None,
|
|
||||||
# fmt: off
|
|
||||||
QCoreApplication.translate("DatabaseChecker", "dialog.confirmNewDatabase"),
|
|
||||||
# fmt: on
|
|
||||||
)
|
|
||||||
if confirm_new_database == QMessageBox.StandardButton.Yes:
|
|
||||||
db = Database(create_engine(dbSqliteUrl))
|
|
||||||
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()
|
|
||||||
@ -1,154 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>DatabaseChecker</class>
|
|
||||||
<widget class="QWidget" name="DatabaseChecker">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>350</width>
|
|
||||||
<height>250</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string notr="true">DatabaseChecker</string>
|
|
||||||
</property>
|
|
||||||
<layout class="QFormLayout" name="formLayout">
|
|
||||||
<property name="labelAlignment">
|
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
|
||||||
</property>
|
|
||||||
<item row="0" column="0">
|
|
||||||
<widget class="QLabel" name="label">
|
|
||||||
<property name="text">
|
|
||||||
<string>dbPathLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="1">
|
|
||||||
<widget class="FileSelector" name="dbDirSelector" native="true"/>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="0">
|
|
||||||
<widget class="QLabel" name="label_3">
|
|
||||||
<property name="text">
|
|
||||||
<string>dbFilenameLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="1">
|
|
||||||
<widget class="QLineEdit" name="dbFilenameLineEdit"/>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="1">
|
|
||||||
<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="confirmDbPathButton">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>confirmDbPathButton</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item row="4" column="1">
|
|
||||||
<widget class="QLabel" name="dbVersionLabel">
|
|
||||||
<property name="text">
|
|
||||||
<string notr="true">-</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="6" column="1">
|
|
||||||
<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 row="7" column="0">
|
|
||||||
<widget class="QLabel" name="label_5">
|
|
||||||
<property name="text">
|
|
||||||
<string>dbCheckConnLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="7" column="1">
|
|
||||||
<widget class="QLabel" name="dbCheckConnLabel">
|
|
||||||
<property name="text">
|
|
||||||
<string notr="true">...</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="8" column="0" colspan="2">
|
|
||||||
<widget class="QPushButton" name="continueButton">
|
|
||||||
<property name="enabled">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>continueButton</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="4" column="0">
|
|
||||||
<widget class="QLabel" name="label_2">
|
|
||||||
<property name="text">
|
|
||||||
<string>dbVersionLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="0" colspan="2">
|
|
||||||
<widget class="Line" name="line">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</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>
|
|
||||||
<customwidget>
|
|
||||||
<class>FileSelector</class>
|
|
||||||
<extends>QWidget</extends>
|
|
||||||
<header>ui.implements.components.fileSelector</header>
|
|
||||||
<container>1</container>
|
|
||||||
</customwidget>
|
|
||||||
</customwidgets>
|
|
||||||
<resources/>
|
|
||||||
<connections/>
|
|
||||||
</ui>
|
|
||||||