wip: arcaea-offline==0.2.0

- fix imports
This commit is contained in:
2023-08-28 22:45:50 +08:00
parent a9941e9c87
commit 2e6ca98f7d
17 changed files with 227 additions and 155 deletions

View File

@ -1,6 +1,6 @@
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 arcaea_offline.utils import rating_class_to_short_text from arcaea_offline.utils.rating import rating_class_to_short_text
from PySide6.QtCore import Qt from PySide6.QtCore import Qt
from PySide6.QtGui import QStandardItem, QStandardItemModel from PySide6.QtGui import QStandardItem, QStandardItemModel

View File

@ -3,7 +3,7 @@ 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, ScoreInsert from arcaea_offline.models import Chart, Score
from arcaea_offline_ocr.b30.shared import B30OcrResultItem from arcaea_offline_ocr.b30.shared import B30OcrResultItem
from arcaea_offline_ocr.device.shared import DeviceOcrResult from arcaea_offline_ocr.device.shared import DeviceOcrResult
from PySide6.QtCore import ( from PySide6.QtCore import (
@ -47,14 +47,14 @@ class OcrQueueModel(QAbstractListModel):
ImagePixmapRole = Qt.ItemDataRole.UserRole + 3 ImagePixmapRole = Qt.ItemDataRole.UserRole + 3
OcrResultRole = Qt.ItemDataRole.UserRole + 10 OcrResultRole = Qt.ItemDataRole.UserRole + 10
ScoreInsertRole = Qt.ItemDataRole.UserRole + 11 ScoreRole = Qt.ItemDataRole.UserRole + 11
ChartRole = Qt.ItemDataRole.UserRole + 12 ChartRole = Qt.ItemDataRole.UserRole + 12
ScoreValidateOkRole = Qt.ItemDataRole.UserRole + 13 ScoreValidateOkRole = Qt.ItemDataRole.UserRole + 13
OcrRunnableRole = Qt.ItemDataRole.UserRole + 20 OcrRunnableRole = Qt.ItemDataRole.UserRole + 20
ProcessOcrResultFuncRole = ( ProcessOcrResultFuncRole = (
Qt.ItemDataRole.UserRole + 21 Qt.ItemDataRole.UserRole + 21
) # Callable[[imageStr, DeviceOcrResult], tuple[Chart, ScoreInsert]] ) # Callable[[imageStr, DeviceOcrResult], tuple[Chart, Score]]
started = Signal() started = Signal()
progress = Signal(int) progress = Signal(int)
@ -107,8 +107,8 @@ class OcrQueueModel(QAbstractListModel):
self.updateScoreValidateOk(index.row()) self.updateScoreValidateOk(index.row())
updateRole = role updateRole = role
if role == self.ScoreInsertRole and isinstance(value, ScoreInsert): if role == self.ScoreRole and isinstance(value, Score):
item[self.ScoreInsertRole] = value item[self.ScoreRole] = value
self.updateScoreValidateOk(index.row()) self.updateScoreValidateOk(index.row())
updateRole = role updateRole = role
@ -138,7 +138,7 @@ class OcrQueueModel(QAbstractListModel):
self, self,
image: str, image: str,
runnable: OcrRunnable = None, runnable: OcrRunnable = None,
process_func: Callable[[Optional[str], QImage, Any], ScoreInsert] = None, process_func: Callable[[Optional[str], QImage, Any], Score] = None,
): ):
... ...
@ -147,7 +147,7 @@ class OcrQueueModel(QAbstractListModel):
self, self,
image: QImage, image: QImage,
runnable: OcrRunnable = None, runnable: OcrRunnable = None,
process_func: Callable[[Optional[str], QImage, Any], ScoreInsert] = None, process_func: Callable[[Optional[str], QImage, Any], Score] = None,
): ):
... ...
@ -178,7 +178,7 @@ class OcrQueueModel(QAbstractListModel):
self.ImageQImageRole: qImage, self.ImageQImageRole: qImage,
self.ImagePixmapRole: qPixmap, self.ImagePixmapRole: qPixmap,
self.OcrResultRole: None, self.OcrResultRole: None,
self.ScoreInsertRole: None, self.ScoreRole: None,
self.ChartRole: None, self.ChartRole: None,
self.ScoreValidateOkRole: False, self.ScoreValidateOkRole: False,
self.OcrRunnableRole: runnable, self.OcrRunnableRole: runnable,
@ -201,7 +201,7 @@ class OcrQueueModel(QAbstractListModel):
self.setData(index, result, self.OcrResultRole) self.setData(index, result, self.OcrResultRole)
self.setData(index, chart, self.ChartRole) self.setData(index, chart, self.ChartRole)
self.setData(index, scoreInsert, self.ScoreInsertRole) self.setData(index, scoreInsert, self.ScoreRole)
return True return True
@Slot(DeviceOcrResult) @Slot(DeviceOcrResult)
@ -234,8 +234,8 @@ class OcrQueueModel(QAbstractListModel):
index = self.index(row, 0) index = self.index(row, 0)
chart = index.data(self.ChartRole) chart = index.data(self.ChartRole)
score = index.data(self.ScoreInsertRole) score = index.data(self.ScoreRole)
if isinstance(chart, Chart) and isinstance(score, ScoreInsert): if isinstance(chart, Chart) and isinstance(score, Score):
scoreRange = calculate_score_range(chart, score.pure, score.far) scoreRange = calculate_score_range(chart, score.pure, score.far)
scoreValidateOk = scoreRange[0] <= score.score <= scoreRange[1] scoreValidateOk = scoreRange[0] <= score.score <= scoreRange[1]
self.setData(index, scoreValidateOk, self.ScoreValidateOkRole) self.setData(index, scoreValidateOk, self.ScoreValidateOkRole)
@ -247,8 +247,8 @@ class OcrQueueModel(QAbstractListModel):
return return
item = self.__items[row] item = self.__items[row]
score = item[self.ScoreInsertRole] score = item[self.ScoreRole]
if not isinstance(score, ScoreInsert) or ( if not isinstance(score, Score) or (
not item[self.ScoreValidateOkRole] and not ignoreValidate not item[self.ScoreValidateOkRole] and not ignoreValidate
): ):
return return
@ -301,7 +301,7 @@ class OcrQueueTableProxyModel(QAbstractTableModel):
], ],
[ [
OcrQueueModel.OcrResultRole, OcrQueueModel.OcrResultRole,
OcrQueueModel.ScoreInsertRole, OcrQueueModel.ScoreRole,
OcrQueueModel.ChartRole, OcrQueueModel.ChartRole,
OcrQueueModel.ScoreValidateOkRole, OcrQueueModel.ScoreValidateOkRole,
], ],
@ -364,7 +364,7 @@ class OcrQueueTableProxyModel(QAbstractTableModel):
def setData(self, index, value, role): def setData(self, index, value, role):
if index.column() == 2 and role == OcrQueueModel.ChartRole: if index.column() == 2 and role == OcrQueueModel.ChartRole:
return self.sourceModel().setData(index, value, role) return self.sourceModel().setData(index, value, role)
if index.column() == 3 and role == OcrQueueModel.ScoreInsertRole: if index.column() == 3 and role == OcrQueueModel.ScoreRole:
return self.sourceModel().setData(index, value, role) return self.sourceModel().setData(index, value, role)
return False return False
@ -403,8 +403,8 @@ class OcrChartDelegate(ChartDelegate):
class OcrScoreDelegate(ScoreDelegate): class OcrScoreDelegate(ScoreDelegate):
def getScoreInsert(self, index: QModelIndex): def getScore(self, index: QModelIndex):
return index.data(OcrQueueModel.ScoreInsertRole) return index.data(OcrQueueModel.ScoreRole)
def getChart(self, index: QModelIndex): def getChart(self, index: QModelIndex):
return index.data(OcrQueueModel.ChartRole) return index.data(OcrQueueModel.ChartRole)
@ -415,7 +415,7 @@ class OcrScoreDelegate(ScoreDelegate):
def paintWarningBackground(self, index: QModelIndex) -> bool: def paintWarningBackground(self, index: QModelIndex) -> bool:
return True return True
# return isinstance(self.getChart(index), Chart) and isinstance( # return isinstance(self.getChart(index), Chart) and isinstance(
# self.getScore(index), ScoreInsert # self.getScore(index), Score
# ) # )
# return isinstance( # return isinstance(
# index.data(OcrQueueModel.OcrResultRole), (DeviceOcrResult, B30OcrResultItem) # index.data(OcrQueueModel.OcrResultRole), (DeviceOcrResult, B30OcrResultItem)
@ -423,4 +423,4 @@ class OcrScoreDelegate(ScoreDelegate):
def setModelData(self, editor, model: OcrQueueTableProxyModel, index): def setModelData(self, editor, model: OcrQueueTableProxyModel, index):
if super().confirmSetModelData(editor): if super().confirmSetModelData(editor):
model.setData(index, editor.value(), OcrQueueModel.ScoreInsertRole) model.setData(index, editor.value(), OcrQueueModel.ScoreRole)

View File

@ -0,0 +1,11 @@
from typing import Type
from PySide6.QtCore import QUrl
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)

View File

@ -1,9 +1,7 @@
from typing import Union
from arcaea_offline.models import Chart from arcaea_offline.models import Chart
from arcaea_offline.utils import rating_class_to_short_text, rating_class_to_text from arcaea_offline.utils.rating import rating_class_to_short_text, rating_class_to_text
from PySide6.QtCore import QDateTime, QModelIndex, Qt, Signal from PySide6.QtCore import QModelIndex, Qt, Signal
from PySide6.QtGui import QBrush, QColor from PySide6.QtGui import QColor
from PySide6.QtWidgets import ( from PySide6.QtWidgets import (
QFrame, QFrame,
QHBoxLayout, QHBoxLayout,

View File

@ -1,16 +1,12 @@
from typing import Union from typing import Union
from arcaea_offline.calculate import calculate_score_range from arcaea_offline.calculate import calculate_score_range
from arcaea_offline.models import Chart, Score, ScoreInsert from arcaea_offline.models import Chart, Score
from arcaea_offline.utils import ( from arcaea_offline.utils.rating import rating_class_to_text
rating_class_to_text, from arcaea_offline.utils.score import score_to_grade_text, zip_score_grade
score_to_grade_text,
zip_score_grade,
)
from PySide6.QtCore import QAbstractItemModel, QDateTime, QModelIndex, Qt, Signal from PySide6.QtCore import QAbstractItemModel, QDateTime, QModelIndex, Qt, Signal
from PySide6.QtGui import QColor, QFont, QLinearGradient from PySide6.QtGui import QColor, QFont, QLinearGradient
from PySide6.QtWidgets import ( from PySide6.QtWidgets import (
QAbstractItemDelegate,
QFrame, QFrame,
QHBoxLayout, QHBoxLayout,
QLabel, QLabel,
@ -53,7 +49,7 @@ class ScoreEditorDelegateWrapper(ScoreEditor):
self.formLayout.insertRow(0, self.delegateHeader) self.formLayout.insertRow(0, self.delegateHeader)
def setText(self, score: Score | ScoreInsert, _extra: str = None): def setText(self, score: Score, _extra: str = None):
text = "Editing " text = "Editing "
text += _extra or "" text += _extra or ""
text += f"score {score.score}" text += f"score {score.score}"
@ -91,22 +87,14 @@ class ScoreDelegate(TextSegmentDelegate):
def getScore(self, index: QModelIndex) -> Score | None: def getScore(self, index: QModelIndex) -> Score | None:
return None return None
def getScoreInsert(self, index: QModelIndex) -> ScoreInsert | None:
return None
def _getScore(self, index: QModelIndex):
score = self.getScore(index)
scoreInsert = self.getScoreInsert(index)
return scoreInsert if score is None else score
def getChart(self, index: QModelIndex) -> Chart | None: def getChart(self, index: QModelIndex) -> Chart | None:
return None return None
def getScoreValidateOk(self, index: QModelIndex) -> bool | None: def getScoreValidateOk(self, index: QModelIndex) -> bool | None:
score = self._getScore(index) score = self.getScore(index)
chart = self.getChart(index) chart = self.getChart(index)
if isinstance(score, (Score, ScoreInsert)) and isinstance(chart, Chart): if isinstance(score, Score) and isinstance(chart, Chart):
scoreRange = calculate_score_range(chart, score.pure, score.far) scoreRange = calculate_score_range(chart, score.pure, score.far)
return scoreRange[0] <= score.score <= scoreRange[1] return scoreRange[0] <= score.score <= scoreRange[1]
@ -114,9 +102,9 @@ class ScoreDelegate(TextSegmentDelegate):
return zip_score_grade(score, self.GradeGradientsWrappers) return zip_score_grade(score, self.GradeGradientsWrappers)
def getTextSegments(self, index, option): def getTextSegments(self, index, option):
score = self._getScore(index) score = self.getScore(index)
chart = self.getChart(index) chart = self.getChart(index)
if not (isinstance(score, (Score, ScoreInsert)) and isinstance(chart, Chart)): if not (isinstance(score, Score) and isinstance(chart, Chart)):
return [ return [
[ [
{ {
@ -176,10 +164,10 @@ class ScoreDelegate(TextSegmentDelegate):
def paint(self, painter, option, index): def paint(self, painter, option, index):
# draw scoreMismatch warning background # draw scoreMismatch warning background
score = self._getScore(index) score = self.getScore(index)
chart = self.getChart(index) chart = self.getChart(index)
if ( if (
isinstance(score, (Score, ScoreInsert)) isinstance(score, Score)
and isinstance(chart, Chart) and isinstance(chart, Chart)
and self.paintWarningBackground(index) and self.paintWarningBackground(index)
): ):
@ -206,16 +194,16 @@ class ScoreDelegate(TextSegmentDelegate):
self.closeEditor.emit(editor) self.closeEditor.emit(editor)
def createEditor(self, parent, option, index) -> ScoreEditorDelegateWrapper: def createEditor(self, parent, option, index) -> ScoreEditorDelegateWrapper:
score = self._getScore(index) score = self.getScore(index)
chart = self.getChart(index) chart = self.getChart(index)
if isinstance(score, (Score, ScoreInsert)) and isinstance(chart, Chart): if isinstance(score, Score) and isinstance(chart, Chart):
editor = ScoreEditorDelegateWrapper(parent) editor = ScoreEditorDelegateWrapper(parent)
editor.setWindowFlag(Qt.WindowType.Sheet, True) editor.setWindowFlag(Qt.WindowType.Sheet, True)
editor.setWindowFlag(Qt.WindowType.FramelessWindowHint, True) editor.setWindowFlag(Qt.WindowType.FramelessWindowHint, True)
editor.setWindowTitle( editor.setWindowTitle(
f"{chart.name_en}({chart.song_id}) | {rating_class_to_text(chart.rating_class)} | {chart.package_id}" f"{chart.name_en}({chart.song_id}) | {rating_class_to_text(chart.rating_class)} | {chart.package_id}"
) )
editor.setText(self._getScore(index)) editor.setText(self.getScore(index))
editor.setValidateBeforeAccept(False) editor.setValidateBeforeAccept(False)
editor.move(parent.mapToGlobal(parent.pos())) editor.move(parent.mapToGlobal(parent.pos()))
editor.accepted.connect(self._commitEditor) editor.accepted.connect(self._commitEditor)
@ -231,9 +219,9 @@ class ScoreDelegate(TextSegmentDelegate):
keepWidgetInScreen(editor) keepWidgetInScreen(editor)
def setEditorData(self, editor: ScoreEditorDelegateWrapper, index) -> None: def setEditorData(self, editor: ScoreEditorDelegateWrapper, index) -> None:
score = self._getScore(index) score = self.getScore(index)
chart = self.getChart(index) chart = self.getChart(index)
if isinstance(score, (Score, ScoreInsert)) and isinstance(chart, Chart): if isinstance(score, Score) and isinstance(chart, Chart):
editor.setChart(chart) editor.setChart(chart)
editor.setValue(score) editor.setValue(score)

View File

@ -1,5 +1,5 @@
from arcaea_offline.calculate import calculate_score # from arcaea_offline.calculate import calculate_score
from arcaea_offline.models import Chart, Score, ScoreInsert from arcaea_offline.models import Chart, Score
from PySide6.QtCore import QCoreApplication, QModelIndex, QSortFilterProxyModel, Qt from PySide6.QtCore import QCoreApplication, QModelIndex, QSortFilterProxyModel, Qt
from .base import DbTableModel from .base import DbTableModel

View File

@ -1,7 +1,7 @@
from PySide6.QtCore import QDir, QSettings from PySide6.QtCore import QDir, QSettings, QUrl
__all__ = [ __all__ = [
"DATABASE_PATH", "DATABASE_URL",
"DEVICES_JSON_FILE", "DEVICES_JSON_FILE",
"DEVICE_UUID", "DEVICE_UUID",
"TESSERACT_FILE", "TESSERACT_FILE",
@ -10,7 +10,7 @@ __all__ = [
"Settings", "Settings",
] ]
DATABASE_PATH = "General/DatabasePath" DATABASE_URL = "General/DatabaseUrl"
DEVICES_JSON_FILE = "Ocr/DevicesJsonFile" DEVICES_JSON_FILE = "Ocr/DevicesJsonFile"
DEVICE_UUID = "Ocr/DeviceUuid" DEVICE_UUID = "Ocr/DeviceUuid"
@ -27,6 +27,13 @@ class Settings(QSettings):
parent, parent,
) )
def databaseUrl(self) -> str | None:
return self.value(DATABASE_URL, None, str)
def setDatabaseUrl(self, value: str):
self.setValue(DATABASE_URL, value)
self.sync()
def devicesJsonFile(self) -> str | None: def devicesJsonFile(self) -> str | None:
return self.value(DEVICES_JSON_FILE, None, str) return self.value(DEVICES_JSON_FILE, None, str)

View File

@ -1,7 +1,7 @@
import logging import logging
from arcaea_offline.database import Database from arcaea_offline.database import Database
from arcaea_offline.models import Chart, ScoreInsert from arcaea_offline.models import Chart, Score
from arcaea_offline_ocr.b30.chieri.v4.ocr import ChieriBotV4Ocr 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

View File

@ -3,7 +3,7 @@ import logging
from typing import Tuple from typing import Tuple
from arcaea_offline.database import Database from arcaea_offline.database import Database
from arcaea_offline.models import Chart, ScoreInsert from arcaea_offline.models import Chart, Score
from arcaea_offline_ocr.device.shared import DeviceOcrResult from arcaea_offline_ocr.device.shared import DeviceOcrResult
from arcaea_offline_ocr.device.v2.ocr import DeviceV2Ocr from arcaea_offline_ocr.device.v2.ocr import DeviceV2Ocr
from arcaea_offline_ocr.device.v2.rois import DeviceV2Rois from arcaea_offline_ocr.device.v2.rois import DeviceV2Rois
@ -52,9 +52,7 @@ def getImageDate(imagePath: str) -> QDateTime:
class ScoreInsertConverter: class ScoreInsertConverter:
@staticmethod @staticmethod
def deviceV2( def deviceV2(imagePath: str, _, result: DeviceOcrResult) -> Tuple[Chart, Score]:
imagePath: str, _, result: DeviceOcrResult
) -> Tuple[Chart, ScoreInsert]:
db = Database() db = Database()
scoreInsert = ScoreInsert( scoreInsert = ScoreInsert(
song_id=result.song_id, song_id=result.song_id,

View File

@ -1,10 +1,11 @@
import re
from typing import Literal from typing import Literal
from arcaea_offline.database import Database from arcaea_offline.database import Database
from arcaea_offline.models import Chart, Package from arcaea_offline.models import Chart, Pack
from arcaea_offline.utils import rating_class_to_text from arcaea_offline.utils.rating import rating_class_to_text
from PySide6.QtCore import QModelIndex, Qt, Signal, Slot from PySide6.QtCore import QModelIndex, Qt, Signal, Slot
from PySide6.QtGui import QColor from PySide6.QtGui import QColor, QShowEvent
from PySide6.QtWidgets import QCompleter, QWidget from PySide6.QtWidgets import QCompleter, QWidget
from ui.designer.components.chartSelector_ui import Ui_ChartSelector from ui.designer.components.chartSelector_ui import Ui_ChartSelector
@ -19,7 +20,6 @@ class ChartSelector(Ui_ChartSelector, QWidget):
def __init__(self, parent=None): def __init__(self, parent=None):
super().__init__(parent) super().__init__(parent)
self.db = Database() self.db = Database()
self.db.register_update_hook(self.fillPackageComboBox)
self.setupUi(self) self.setupUi(self)
self.pstButton.setColors(QColor("#399bb2"), QColor("#f0f8fa")) self.pstButton.setColors(QColor("#399bb2"), QColor("#f0f8fa"))
@ -107,19 +107,28 @@ class ChartSelector(Ui_ChartSelector, QWidget):
ratingClass = self.selectedRatingClass() ratingClass = self.selectedRatingClass()
if packageId and songId and isinstance(ratingClass, int): if packageId and songId and isinstance(ratingClass, int):
return Chart.from_db_row(self.db.get_chart(songId, ratingClass)) return self.db.get_chart(songId, ratingClass)
return None return None
def showEvent(self, event: QShowEvent):
# update database results when widget visible
self.fillPackageComboBox()
return super().showEvent(event)
@Slot() @Slot()
def updateResultLabel(self): def updateResultLabel(self):
chart = self.value() chart = self.value()
if isinstance(chart, Chart): if isinstance(chart, Chart):
package = Package.from_db_row( pack = self.db.get_pack_by_id(chart.set)
self.db.get_package_by_package_id(chart.package_id)
)
texts = [ texts = [
[package.name, chart.name_en, rating_class_to_text(chart.rating_class)], [
[package.id, chart.song_id, str(chart.rating_class)], pack.name,
chart.title,
f"{rating_class_to_text(chart.rating_class)} "
f"{chart.rating}{'+' if chart.rating_plus else ''}"
f"({chart.constant / 10})",
],
[pack.id, chart.song_id, str(chart.rating_class)],
] ]
texts = [" | ".join(t) for t in texts] texts = [" | ".join(t) for t in texts]
text = f'{texts[0]}<br><font color="gray">{texts[1]}</font>' text = f'{texts[0]}<br><font color="gray">{texts[1]}</font>'
@ -129,37 +138,41 @@ class ChartSelector(Ui_ChartSelector, QWidget):
def fillPackageComboBox(self): def fillPackageComboBox(self):
self.packageComboBox.clear() self.packageComboBox.clear()
packages = [Package.from_db_row(dbRow) for dbRow in self.db.get_packages()] packs = self.db.get_packs()
for package in packages: for pack in packs:
self.packageComboBox.addItem(f"{package.name} ({package.id})", package.id) isAppendPack = re.search(r"_append_.*$", pack.id)
if isAppendPack:
basePackId = re.sub(r"_append_.*$", "", pack.id)
basePackName = self.db.get_pack_by_id(basePackId).name
packName = f"{basePackName} - {pack.name}"
else:
packName = pack.name
self.packageComboBox.addItem(f"{packName} ({pack.id})", pack.id)
row = self.packageComboBox.count() - 1 row = self.packageComboBox.count() - 1
self.packageComboBox.setItemData( self.packageComboBox.setItemData(
row, package.name, DescriptionDelegate.MainTextRole row, packName, DescriptionDelegate.MainTextRole
) )
self.packageComboBox.setItemData( self.packageComboBox.setItemData(
row, package.id, DescriptionDelegate.DescriptionTextRole row, pack.id, DescriptionDelegate.DescriptionTextRole
) )
self.packageComboBox.setCurrentIndex(-1) self.packageComboBox.setCurrentIndex(-1)
def fillSongIdComboBox(self): def fillSongIdComboBox(self):
self.songIdComboBox.clear() self.songIdComboBox.clear()
packageId = self.packageComboBox.currentData() packId = self.packageComboBox.currentData()
if packageId: if packId:
charts = [ charts = self.db.get_charts_by_pack_id(packId)
Chart.from_db_row(dbRow)
for dbRow in self.db.get_charts_by_package_id(packageId)
]
inserted_song_ids = [] inserted_song_ids = []
for chart in charts: for chart in charts:
if chart.song_id not in inserted_song_ids: if chart.song_id not in inserted_song_ids:
self.songIdComboBox.addItem( self.songIdComboBox.addItem(
f"{chart.name_en} ({chart.song_id})", chart.song_id f"{chart.title} ({chart.song_id})", chart.song_id
) )
inserted_song_ids.append(chart.song_id) inserted_song_ids.append(chart.song_id)
row = self.songIdComboBox.count() - 1 row = self.songIdComboBox.count() - 1
self.songIdComboBox.setItemData( self.songIdComboBox.setItemData(
row, chart.name_en, DescriptionDelegate.MainTextRole row, chart.title, DescriptionDelegate.MainTextRole
) )
self.songIdComboBox.setItemData( self.songIdComboBox.setItemData(
row, chart.song_id, DescriptionDelegate.DescriptionTextRole row, chart.song_id, DescriptionDelegate.DescriptionTextRole
@ -174,12 +187,7 @@ class ChartSelector(Ui_ChartSelector, QWidget):
def on_songIdComboBox_currentIndexChanged(self, index: int): def on_songIdComboBox_currentIndexChanged(self, index: int):
rating_classes = [] rating_classes = []
if index > -1: if index > -1:
charts = [ charts = self.db.get_charts_by_song_id(self.songIdComboBox.currentData())
Chart.from_db_row(dbRow)
for dbRow in self.db.get_charts_by_song_id(
self.songIdComboBox.currentData()
)
]
rating_classes = [chart.rating_class for chart in charts] rating_classes = [chart.rating_class for chart in charts]
self.updateRatingClassButtonsEnabled(rating_classes) self.updateRatingClassButtonsEnabled(rating_classes)
@ -196,7 +204,7 @@ class ChartSelector(Ui_ChartSelector, QWidget):
self.fuzzySearchCompleterModel.clear() self.fuzzySearchCompleterModel.clear()
def selectChart(self, chart: Chart): def selectChart(self, chart: Chart):
packageIdIndex = self.packageComboBox.findData(chart.package_id) packageIdIndex = self.packageComboBox.findData(chart.set)
if packageIdIndex > -1: if packageIdIndex > -1:
self.packageComboBox.setCurrentIndex(packageIdIndex) self.packageComboBox.setCurrentIndex(packageIdIndex)
else: else:

View File

@ -2,7 +2,7 @@ from enum import IntEnum
from typing import Optional from typing import Optional
from arcaea_offline.calculate import calculate_score_range from arcaea_offline.calculate import calculate_score_range
from arcaea_offline.models import Chart, Score, ScoreInsert from arcaea_offline.models import Chart, Score
from PySide6.QtCore import QCoreApplication, QDateTime, Signal, Slot from PySide6.QtCore import QCoreApplication, QDateTime, Signal, Slot
from PySide6.QtWidgets import QMessageBox, QWidget from PySide6.QtWidgets import QMessageBox, QWidget
@ -37,9 +37,9 @@ class ScoreEditor(Ui_ScoreEditor, QWidget):
self.valueChanged.connect(self.validateScore) self.valueChanged.connect(self.validateScore)
self.valueChanged.connect(self.updateValidateLabel) self.valueChanged.connect(self.updateValidateLabel)
self.clearTypeComboBox.addItem("HARD LOST", -1)
self.clearTypeComboBox.addItem("TRACK LOST", 0) self.clearTypeComboBox.addItem("TRACK LOST", 0)
self.clearTypeComboBox.addItem("TRACK COMPLETE", 1) self.clearTypeComboBox.addItem("TRACK COMPLETE", 1)
self.clearTypeComboBox.addItem("HARD LOST", 2)
self.clearTypeComboBox.setCurrentIndex(-1) self.clearTypeComboBox.setCurrentIndex(-1)
def setValidateBeforeAccept(self, __bool: bool): def setValidateBeforeAccept(self, __bool: bool):
@ -159,33 +159,33 @@ class ScoreEditor(Ui_ScoreEditor, QWidget):
def value(self): def value(self):
if isinstance(self.__chart, Chart): if isinstance(self.__chart, Chart):
return ScoreInsert( return Score(
song_id=self.__chart.song_id, song_id=self.__chart.song_id,
rating_class=self.__chart.rating_class, rating_class=self.__chart.rating_class,
score=self.score(), score=self.score(),
pure=self.pureSpinBox.value(), pure=self.pureSpinBox.value(),
far=self.farSpinBox.value(), far=self.farSpinBox.value(),
lost=self.lostSpinBox.value(), lost=self.lostSpinBox.value(),
time=self.dateTimeEdit.dateTime().toSecsSinceEpoch(), date=self.dateTimeEdit.dateTime().toSecsSinceEpoch(),
max_recall=self.maxRecallSpinBox.value() max_recall=self.maxRecallSpinBox.value()
if self.maxRecallSpinBox.value() > -1 if self.maxRecallSpinBox.value() > -1
else None, else None,
clear_type=None, r10_clear_type=None,
) )
def setValue(self, score: Score | ScoreInsert): def setValue(self, score: Score):
if isinstance(score, (Score, ScoreInsert)): if isinstance(score, Score):
scoreText = str(score.score) scoreText = str(score.score)
scoreText = scoreText.rjust(8, "0") scoreText = scoreText.rjust(8, "0")
self.scoreLineEdit.setText(scoreText) self.scoreLineEdit.setText(scoreText)
self.pureSpinBox.setValue(score.pure) self.pureSpinBox.setValue(score.pure)
self.farSpinBox.setValue(score.far) self.farSpinBox.setValue(score.far)
self.lostSpinBox.setValue(score.lost) self.lostSpinBox.setValue(score.lost)
self.dateTimeEdit.setDateTime(QDateTime.fromSecsSinceEpoch(score.time)) self.dateTimeEdit.setDateTime(QDateTime.fromSecsSinceEpoch(score.date))
if score.max_recall is not None: if score.max_recall is not None:
self.maxRecallSpinBox.setValue(score.max_recall) self.maxRecallSpinBox.setValue(score.max_recall)
if score.clear_type is not None: if score.r10_clear_type is not None:
self.clearTypeComboBox.setCurrentIndex(score.clear_type) self.clearTypeComboBox.setCurrentIndex(score.r10_clear_type)
def reset(self): def reset(self):
self.setChart(None) self.setChart(None)

View File

@ -1,4 +1,3 @@
from arcaea_offline.models import ScoreInsert
from PySide6.QtCore import QModelIndex, Qt, Slot from PySide6.QtCore import QModelIndex, Qt, Slot
from PySide6.QtGui import QColor, QPalette from PySide6.QtGui import QColor, QPalette
from PySide6.QtWidgets import QMessageBox from PySide6.QtWidgets import QMessageBox
@ -21,7 +20,7 @@ class TableScoreDelegate(ScoreDelegate):
def getChart(self, index): def getChart(self, index):
return index.data(DbB30TableModel.ChartRole) return index.data(DbB30TableModel.ChartRole)
def getScoreInsert(self, index: QModelIndex) -> ScoreInsert | None: def getScoreInsert(self, index: QModelIndex):
return super().getScoreInsert(index) return super().getScoreInsert(index)
def getScore(self, index): def getScore(self, index):

View File

@ -2,7 +2,8 @@ import logging
import traceback import traceback
from arcaea_offline.database import Database from arcaea_offline.database import Database
from arcaea_offline.external import St3ScoreSource from arcaea_offline.external.arcaea.st3 import St3ScoreParser
from arcaea_offline.external.arcsong import ArcsongDbParser
from PySide6.QtCore import Slot from PySide6.QtCore import Slot
from PySide6.QtWidgets import QFileDialog, QMessageBox, QWidget from PySide6.QtWidgets import QFileDialog, QMessageBox, QWidget
@ -26,7 +27,11 @@ class TabDb_Manage(Ui_TabDb_Manage, QWidget):
return return
try: try:
Database().update_arcsong_db(dbFile) db = Database()
parser = ArcsongDbParser(dbFile)
with db.sessionmaker() as session:
parser.write_database(session)
session.commit()
QMessageBox.information(self, None, "OK") QMessageBox.information(self, None, "OK")
except Exception as e: except Exception as e:
logging.exception("Sync arcsong.db error") logging.exception("Sync arcsong.db error")
@ -42,11 +47,14 @@ class TabDb_Manage(Ui_TabDb_Manage, QWidget):
return return
try: try:
scores = St3ScoreSource(dbFile).get_score_items() db = Database()
parser = St3ScoreParser(dbFile)
logger.info( logger.info(
f"Got {len(scores)} items from {dbFile}, writing into database..." f"Got {len(parser.parse())} items from {dbFile}, writing into database..."
) )
Database().import_external(scores) with db.sessionmaker() as session:
parser.write_database(session)
session.commit()
QMessageBox.information(self, None, "OK") QMessageBox.information(self, None, "OK")
except Exception as e: except Exception as e:
logging.exception("import st3 error") logging.exception("import st3 error")

View File

@ -1,4 +1,4 @@
from arcaea_offline.models import ScoreInsert from arcaea_offline.models import Score
from PySide6.QtCore import QModelIndex, Qt, Slot from PySide6.QtCore import QModelIndex, Qt, Slot
from PySide6.QtGui import QColor, QPalette from PySide6.QtGui import QColor, QPalette
from PySide6.QtWidgets import QMessageBox from PySide6.QtWidgets import QMessageBox
@ -21,9 +21,6 @@ class TableScoreDelegate(ScoreDelegate):
def getChart(self, index): def getChart(self, index):
return index.data(DbScoreTableModel.ChartRole) return index.data(DbScoreTableModel.ChartRole)
def getScoreInsert(self, index: QModelIndex) -> ScoreInsert | None:
return super().getScoreInsert(index)
def getScore(self, index): def getScore(self, index):
return index.data(DbScoreTableModel.ScoreRole) return index.data(DbScoreTableModel.ScoreRole)

View File

@ -1,7 +1,6 @@
import logging import logging
import cv2 import cv2
import pytesseract
# from arcaea_offline_ocr_device_creation_wizard.implements.wizard import Wizard # from arcaea_offline_ocr_device_creation_wizard.implements.wizard import Wizard
from arcaea_offline_ocr.device.v1.definition import DeviceV1 from arcaea_offline_ocr.device.v1.definition import DeviceV1

View File

@ -1,4 +1,5 @@
from arcaea_offline.database import Database from arcaea_offline.database import Database
from PySide6.QtGui import QShowEvent
from PySide6.QtWidgets import QWidget from PySide6.QtWidgets import QWidget
from ui.designer.tabs.tabOverview_ui import Ui_TabOverview from ui.designer.tabs.tabOverview_ui import Ui_TabOverview
@ -10,8 +11,12 @@ class TabOverview(Ui_TabOverview, QWidget):
self.setupUi(self) self.setupUi(self)
self.db = Database() self.db = Database()
self.db.register_update_hook(self.updateOverview) # self.db.register_update_hook(self.updateOverview)
# self.updateOverview()
def showEvent(self, event: QShowEvent) -> None:
self.updateOverview() self.updateOverview()
return super().showEvent(event)
def updateOverview(self): def updateOverview(self):
b30 = self.db.get_b30() or 0.00 b30 = self.db.get_b30() or 0.00

View File

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