diff --git a/ui/designer/tabs/tabDb/tabDb_ChartInfoEditor.ui b/ui/designer/tabs/tabDb/tabDb_ChartInfoEditor.ui
new file mode 100644
index 0000000..8290eb4
--- /dev/null
+++ b/ui/designer/tabs/tabDb/tabDb_ChartInfoEditor.ui
@@ -0,0 +1,263 @@
+
+
+ TabDb_ChartInfoEditor
+
+
+
+ 0
+ 0
+ 659
+ 570
+
+
+
+ TabDb_ChartInfoEditor
+
+
+ -
+
+
+ editor.title
+
+
+
-
+
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
-
+
+
+ editor.constant
+
+
+
+ -
+
+
+ editor.notes
+
+
+
+ -
+
+
+
+ GeosansLight
+ 14
+ true
+
+
+
+
+ -
+
+
+
+ 100
+ 100
+
+
+
+
+ 100
+ 100
+
+
+
+
+
+
+
+ -
+
+
-
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 0
+
+
+
+
+ -
+
+
+ ...
+
+
+
+ -
+
+
+ ...
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 0
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+
+ GeosansLight
+ 14
+ true
+
+
+
+
+ -
+
+
+ > ...
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ editor.tip
+
+
+
+ -
+
+
+ editor.tip.content
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+ -
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ editor.delete
+
+
+
+ -
+
+
+ editor.commit
+
+
+
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ QAbstractItemView::NoEditTriggers
+
+
+ QAbstractItemView::ScrollPerPixel
+
+
+ QAbstractItemView::ScrollPerPixel
+
+
+
+
+
+
+
+ ChartSelector
+ QWidget
+ ui.implements.components.chartSelector
+ 1
+
+
+
+
+
diff --git a/ui/designer/tabs/tabDb/tabDb_ChartInfoEditor_ui.py b/ui/designer/tabs/tabDb/tabDb_ChartInfoEditor_ui.py
new file mode 100644
index 0000000..3e086c8
--- /dev/null
+++ b/ui/designer/tabs/tabDb/tabDb_ChartInfoEditor_ui.py
@@ -0,0 +1,198 @@
+# -*- coding: utf-8 -*-
+
+################################################################################
+## Form generated from reading UI file 'tabDb_ChartInfoEditor.ui'
+##
+## Created by: Qt User Interface Compiler version 6.5.2
+##
+## WARNING! All changes made in this file will be lost when recompiling UI file!
+################################################################################
+
+from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
+ QMetaObject, QObject, QPoint, QRect,
+ QSize, QTime, QUrl, Qt)
+from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
+ QFont, QFontDatabase, QGradient, QIcon,
+ QImage, QKeySequence, QLinearGradient, QPainter,
+ QPalette, QPixmap, QRadialGradient, QTransform)
+from PySide6.QtWidgets import (QAbstractItemView, QApplication, QFormLayout, QGridLayout,
+ QGroupBox, QHBoxLayout, QLabel, QLineEdit,
+ QListView, QPushButton, QSizePolicy, QSpacerItem,
+ QVBoxLayout, QWidget)
+
+from ui.implements.components.chartSelector import ChartSelector
+
+class Ui_TabDb_ChartInfoEditor(object):
+ def setupUi(self, TabDb_ChartInfoEditor):
+ if not TabDb_ChartInfoEditor.objectName():
+ TabDb_ChartInfoEditor.setObjectName(u"TabDb_ChartInfoEditor")
+ TabDb_ChartInfoEditor.resize(659, 570)
+ TabDb_ChartInfoEditor.setWindowTitle(u"TabDb_ChartInfoEditor")
+ self.gridLayout = QGridLayout(TabDb_ChartInfoEditor)
+ self.gridLayout.setObjectName(u"gridLayout")
+ self.groupBox = QGroupBox(TabDb_ChartInfoEditor)
+ self.groupBox.setObjectName(u"groupBox")
+ self.verticalLayout_2 = QVBoxLayout(self.groupBox)
+ self.verticalLayout_2.setObjectName(u"verticalLayout_2")
+ self.widget = QWidget(self.groupBox)
+ self.widget.setObjectName(u"widget")
+ self.formLayout = QFormLayout(self.widget)
+ self.formLayout.setObjectName(u"formLayout")
+ self.formLayout.setLabelAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
+ self.label = QLabel(self.widget)
+ self.label.setObjectName(u"label")
+
+ self.formLayout.setWidget(1, QFormLayout.LabelRole, self.label)
+
+ self.label_2 = QLabel(self.widget)
+ self.label_2.setObjectName(u"label_2")
+
+ self.formLayout.setWidget(2, QFormLayout.LabelRole, self.label_2)
+
+ self.notesLineEdit = QLineEdit(self.widget)
+ self.notesLineEdit.setObjectName(u"notesLineEdit")
+ font = QFont()
+ font.setFamilies([u"GeosansLight"])
+ font.setPointSize(14)
+ font.setBold(True)
+ self.notesLineEdit.setFont(font)
+
+ self.formLayout.setWidget(2, QFormLayout.FieldRole, self.notesLineEdit)
+
+ self.jacketLabel = QLabel(self.widget)
+ self.jacketLabel.setObjectName(u"jacketLabel")
+ self.jacketLabel.setMinimumSize(QSize(100, 100))
+ self.jacketLabel.setMaximumSize(QSize(100, 100))
+ self.jacketLabel.setText(u"")
+
+ self.formLayout.setWidget(0, QFormLayout.LabelRole, self.jacketLabel)
+
+ self.verticalLayout = QVBoxLayout()
+ self.verticalLayout.setObjectName(u"verticalLayout")
+ self.verticalSpacer = QSpacerItem(20, 0, QSizePolicy.Minimum, QSizePolicy.Expanding)
+
+ self.verticalLayout.addItem(self.verticalSpacer)
+
+ self.titleLabel = QLabel(self.widget)
+ self.titleLabel.setObjectName(u"titleLabel")
+ self.titleLabel.setText(u"...")
+
+ self.verticalLayout.addWidget(self.titleLabel)
+
+ self.ratingLabel = QLabel(self.widget)
+ self.ratingLabel.setObjectName(u"ratingLabel")
+ self.ratingLabel.setText(u"...")
+
+ self.verticalLayout.addWidget(self.ratingLabel)
+
+ self.verticalSpacer_2 = QSpacerItem(20, 0, QSizePolicy.Minimum, QSizePolicy.Expanding)
+
+ self.verticalLayout.addItem(self.verticalSpacer_2)
+
+
+ self.formLayout.setLayout(0, QFormLayout.FieldRole, self.verticalLayout)
+
+ self.horizontalWidget_2 = QWidget(self.widget)
+ self.horizontalWidget_2.setObjectName(u"horizontalWidget_2")
+ sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Maximum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.horizontalWidget_2.sizePolicy().hasHeightForWidth())
+ self.horizontalWidget_2.setSizePolicy(sizePolicy)
+ self.horizontalLayout_2 = QHBoxLayout(self.horizontalWidget_2)
+ self.horizontalLayout_2.setObjectName(u"horizontalLayout_2")
+ self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
+ self.constantLineEdit = QLineEdit(self.horizontalWidget_2)
+ self.constantLineEdit.setObjectName(u"constantLineEdit")
+ self.constantLineEdit.setFont(font)
+
+ self.horizontalLayout_2.addWidget(self.constantLineEdit)
+
+ self.constantPreviewLabel = QLabel(self.horizontalWidget_2)
+ self.constantPreviewLabel.setObjectName(u"constantPreviewLabel")
+ self.constantPreviewLabel.setText(u"> ...")
+
+ self.horizontalLayout_2.addWidget(self.constantPreviewLabel)
+
+
+ self.formLayout.setWidget(1, QFormLayout.FieldRole, self.horizontalWidget_2)
+
+ self.label_3 = QLabel(self.widget)
+ self.label_3.setObjectName(u"label_3")
+ sizePolicy1 = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.MinimumExpanding)
+ sizePolicy1.setHorizontalStretch(0)
+ sizePolicy1.setVerticalStretch(0)
+ sizePolicy1.setHeightForWidth(self.label_3.sizePolicy().hasHeightForWidth())
+ self.label_3.setSizePolicy(sizePolicy1)
+
+ self.formLayout.setWidget(3, QFormLayout.LabelRole, self.label_3)
+
+ self.label_4 = QLabel(self.widget)
+ self.label_4.setObjectName(u"label_4")
+
+ self.formLayout.setWidget(3, QFormLayout.FieldRole, self.label_4)
+
+
+ self.verticalLayout_2.addWidget(self.widget)
+
+ self.verticalSpacer_3 = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
+
+ self.verticalLayout_2.addItem(self.verticalSpacer_3)
+
+ self.horizontalLayout = QHBoxLayout()
+ self.horizontalLayout.setObjectName(u"horizontalLayout")
+ self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
+
+ self.horizontalLayout.addItem(self.horizontalSpacer)
+
+ self.deleteButton = QPushButton(self.groupBox)
+ self.deleteButton.setObjectName(u"deleteButton")
+
+ self.horizontalLayout.addWidget(self.deleteButton)
+
+ self.commitButton = QPushButton(self.groupBox)
+ self.commitButton.setObjectName(u"commitButton")
+
+ self.horizontalLayout.addWidget(self.commitButton)
+
+
+ self.verticalLayout_2.addLayout(self.horizontalLayout)
+
+
+ self.gridLayout.addWidget(self.groupBox, 1, 1, 1, 1)
+
+ self.chartSelector = ChartSelector(TabDb_ChartInfoEditor)
+ self.chartSelector.setObjectName(u"chartSelector")
+
+ self.gridLayout.addWidget(self.chartSelector, 0, 0, 1, 2)
+
+ self.listView = QListView(TabDb_ChartInfoEditor)
+ self.listView.setObjectName(u"listView")
+ sizePolicy2 = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding)
+ sizePolicy2.setHorizontalStretch(0)
+ sizePolicy2.setVerticalStretch(0)
+ sizePolicy2.setHeightForWidth(self.listView.sizePolicy().hasHeightForWidth())
+ self.listView.setSizePolicy(sizePolicy2)
+ self.listView.setEditTriggers(QAbstractItemView.NoEditTriggers)
+ self.listView.setVerticalScrollMode(QAbstractItemView.ScrollPerPixel)
+ self.listView.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel)
+
+ self.gridLayout.addWidget(self.listView, 1, 0, 1, 1)
+
+
+ self.retranslateUi(TabDb_ChartInfoEditor)
+
+ QMetaObject.connectSlotsByName(TabDb_ChartInfoEditor)
+ # setupUi
+
+ def retranslateUi(self, TabDb_ChartInfoEditor):
+ self.groupBox.setTitle(QCoreApplication.translate("TabDb_ChartInfoEditor", u"editor.title", None))
+ self.label.setText(QCoreApplication.translate("TabDb_ChartInfoEditor", u"editor.constant", None))
+ self.label_2.setText(QCoreApplication.translate("TabDb_ChartInfoEditor", u"editor.notes", None))
+ self.label_3.setText(QCoreApplication.translate("TabDb_ChartInfoEditor", u"editor.tip", None))
+ self.label_4.setText(QCoreApplication.translate("TabDb_ChartInfoEditor", u"editor.tip.content", None))
+ self.deleteButton.setText(QCoreApplication.translate("TabDb_ChartInfoEditor", u"editor.delete", None))
+ self.commitButton.setText(QCoreApplication.translate("TabDb_ChartInfoEditor", u"editor.commit", None))
+ pass
+ # retranslateUi
+
diff --git a/ui/designer/tabs/tabDbEntry.ui b/ui/designer/tabs/tabDbEntry.ui
index be1da1d..89befa5 100644
--- a/ui/designer/tabs/tabDbEntry.ui
+++ b/ui/designer/tabs/tabDbEntry.ui
@@ -24,6 +24,11 @@
tab.manage
+
+
+ tab.chartInfoEditor
+
+
@@ -35,6 +40,12 @@
ui.implements.tabs.tabDb.tabDb_Manage
1
+
+ TabDb_ChartInfoEditor
+ QWidget
+ ui.implements.tabs.tabDb.tabDb_ChartInfoEditor
+ 1
+
diff --git a/ui/designer/tabs/tabDbEntry_ui.py b/ui/designer/tabs/tabDbEntry_ui.py
index 44c2e1d..423da36 100644
--- a/ui/designer/tabs/tabDbEntry_ui.py
+++ b/ui/designer/tabs/tabDbEntry_ui.py
@@ -3,7 +3,7 @@
################################################################################
## Form generated from reading UI file 'tabDbEntry.ui'
##
-## Created by: Qt User Interface Compiler version 6.5.0
+## Created by: Qt User Interface Compiler version 6.5.2
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
@@ -18,6 +18,7 @@ from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
from PySide6.QtWidgets import (QApplication, QSizePolicy, QTabWidget, QVBoxLayout,
QWidget)
+from ui.implements.tabs.tabDb.tabDb_ChartInfoEditor import TabDb_ChartInfoEditor
from ui.implements.tabs.tabDb.tabDb_Manage import TabDb_Manage
class Ui_TabDbEntry(object):
@@ -33,6 +34,9 @@ class Ui_TabDbEntry(object):
self.tab_manage = TabDb_Manage()
self.tab_manage.setObjectName(u"tab_manage")
self.tabWidget.addTab(self.tab_manage, "")
+ self.tab_chartInfoEditor = TabDb_ChartInfoEditor()
+ self.tab_chartInfoEditor.setObjectName(u"tab_chartInfoEditor")
+ self.tabWidget.addTab(self.tab_chartInfoEditor, "")
self.verticalLayout.addWidget(self.tabWidget)
@@ -47,6 +51,7 @@ class Ui_TabDbEntry(object):
def retranslateUi(self, TabDbEntry):
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_manage), QCoreApplication.translate("TabDbEntry", u"tab.manage", None))
+ self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_chartInfoEditor), QCoreApplication.translate("TabDbEntry", u"tab.chartInfoEditor", None))
pass
# retranslateUi
diff --git a/ui/extends/tabs/tabDb/tabDb_ChartInfoEditor.py b/ui/extends/tabs/tabDb/tabDb_ChartInfoEditor.py
new file mode 100644
index 0000000..5241dd1
--- /dev/null
+++ b/ui/extends/tabs/tabDb/tabDb_ChartInfoEditor.py
@@ -0,0 +1,32 @@
+from arcaea_offline.models import Difficulty, Song
+from PySide6.QtCore import QModelIndex, Qt
+from PySide6.QtGui import QStandardItem, QStandardItemModel
+
+from ui.extends.shared.delegates.chartDelegate import ChartDelegate
+
+
+class ChartInfoAbsentModel(QStandardItemModel):
+ SongRole = Qt.ItemDataRole.UserRole
+ DifficultyRole = Qt.ItemDataRole.UserRole + 1
+
+ def setCustomData(self, songs: list[Song], difficulties: list[Difficulty]):
+ self.clear()
+
+ for song, difficulty in zip(songs, difficulties):
+ item = QStandardItem()
+ item.setData(song, self.SongRole)
+ item.setData(difficulty, self.DifficultyRole)
+ self.appendRow(item)
+
+ def setLoading(self):
+ self.clear()
+
+ self.appendRow(QStandardItem("Loading..."))
+
+
+class ListViewDelegate(ChartDelegate):
+ def getSong(self, index: QModelIndex):
+ return index.data(ChartInfoAbsentModel.SongRole)
+
+ def getDifficulty(self, index: QModelIndex):
+ return index.data(ChartInfoAbsentModel.DifficultyRole)
diff --git a/ui/implements/tabs/tabDb/tabDb_ChartInfoEditor.py b/ui/implements/tabs/tabDb/tabDb_ChartInfoEditor.py
new file mode 100644
index 0000000..d636ae6
--- /dev/null
+++ b/ui/implements/tabs/tabDb/tabDb_ChartInfoEditor.py
@@ -0,0 +1,226 @@
+import logging
+
+from arcaea_offline.database import Database
+from arcaea_offline.models import Chart, ChartInfo, Difficulty, Song
+from arcaea_offline.utils.rating import rating_class_to_text
+from PySide6.QtCore import QCoreApplication, QModelIndex, Qt, Slot
+from PySide6.QtGui import QPixmap, QRegularExpressionValidator, QStandardItem
+from PySide6.QtWidgets import QApplication, QMessageBox, QStyledItemDelegate, QWidget
+from sqlalchemy import select
+
+from ui.designer.tabs.tabDb.tabDb_ChartInfoEditor_ui import Ui_TabDb_ChartInfoEditor
+from ui.extends.shared.data import Data
+from ui.extends.shared.database import databaseUpdateSignals
+from ui.extends.shared.language import LanguageChangeEventFilter
+from ui.extends.tabs.tabDb.tabDb_ChartInfoEditor import (
+ ChartInfoAbsentModel,
+ ListViewDelegate,
+)
+from ui.implements.components.songIdSelector import SongIdSelectorMode
+
+logger = logging.getLogger(__name__)
+
+
+class TabDb_ChartInfoEditor(Ui_TabDb_ChartInfoEditor, QWidget):
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self.setupUi(self)
+
+ self.languageChangeEventFilter = LanguageChangeEventFilter(self)
+ self.installEventFilter(self.languageChangeEventFilter)
+
+ self.db = Database()
+
+ self.numberRegexValidator = QRegularExpressionValidator(r"^\d+$", self)
+ self.constantLineEdit.setValidator(self.numberRegexValidator)
+ self.notesLineEdit.setValidator(self.numberRegexValidator)
+
+ self.constantLineEdit.textChanged.connect(self.updateConstantPreviewLabel)
+
+ self.chartInfoAbsentModel = ChartInfoAbsentModel(self)
+ self.listView.setModel(self.chartInfoAbsentModel)
+ self.listViewDelegate = ListViewDelegate(self)
+
+ self.listView.selectionModel().currentChanged.connect(
+ self.listViewSelectionChanged
+ )
+
+ self.chartSelector.setSongIdSelectorMode(SongIdSelectorMode.SongId)
+ self.chartSelector.valueChanged.connect(self.chartSelectorValueChanged)
+
+ databaseUpdateSignals.songDataUpdated.connect(self.updateChartInfoAbsentModel)
+ self.updateChartInfoAbsentModel()
+
+ self.commitButton.clicked.connect(self.commitChartInfo)
+ self.deleteButton.clicked.connect(self.deleteChartInfo)
+
+ def updateConstantPreviewLabel(self):
+ text = self.constantLineEdit.text()
+ if self.constantLineEdit.hasAcceptableInput():
+ self.constantPreviewLabel.setText(f"> {int(text) / 10:.1f}")
+ else:
+ self.constantPreviewLabel.setText("> ...")
+
+ def reset(self):
+ self.jacketLabel.clear()
+ self.titleLabel.setText("...")
+ self.ratingLabel.setText("...")
+ self.constantLineEdit.setText("")
+ self.notesLineEdit.setText("")
+
+ def updateChartInfoAbsentModel(self):
+ self.listView.setItemDelegate(QStyledItemDelegate())
+ self.chartInfoAbsentModel.clear()
+ self.chartInfoAbsentModel.appendRow(QStandardItem("Loading..."))
+ QApplication.processEvents()
+
+ with self.db.sessionmaker() as session:
+ stmt = (
+ select(Difficulty)
+ .join(
+ ChartInfo,
+ (Difficulty.song_id == ChartInfo.song_id)
+ & (Difficulty.rating_class == ChartInfo.rating_class),
+ isouter=True,
+ )
+ .where(ChartInfo.notes.is_(None))
+ )
+ absentInfoDifficulties = sorted(
+ list(session.scalars(stmt)),
+ key=lambda d: f"{d.song_id},{d.rating_class}",
+ )
+ songIds = sorted(list(set(d.song_id for d in absentInfoDifficulties)))
+ songsStmt = select(Song).where(Song.id.in_(songIds))
+ songs = sorted(list(session.scalars(songsStmt)), key=lambda s: s.id)
+
+ modelSongs = []
+ for difficulty in absentInfoDifficulties:
+ songIndex = songIds.index(difficulty.song_id)
+ modelSongs.append(songs[songIndex])
+ self.chartInfoAbsentModel.setCustomData(modelSongs, absentInfoDifficulties)
+ self.listView.setItemDelegate(self.listViewDelegate)
+
+ @Slot(QModelIndex)
+ def listViewSelectionChanged(self, current: QModelIndex):
+ if current.row() < 0 or current.column() < 0:
+ return
+
+ song: Song = current.data(ChartInfoAbsentModel.SongRole)
+ difficulty: Difficulty = current.data(ChartInfoAbsentModel.DifficultyRole)
+ self.chartSelector.selectChart(
+ Chart(
+ song_id=difficulty.song_id,
+ rating_class=difficulty.rating_class,
+ set=song.set,
+ )
+ )
+
+ def chartSelectorValueChanged(self):
+ if chart := self.chartSelector.value():
+ self.fillChartInfo(chart.song_id, chart.rating_class)
+ else:
+ self.reset()
+
+ def fillChartInfo(self, songId: str, ratingClass: int):
+ song = self.db.get_song(songId)
+ difficulty = self.db.get_difficulty(songId, ratingClass)
+
+ self.titleLabel.setText(difficulty.title or song.title)
+ self.ratingLabel.setText(
+ rating_class_to_text(difficulty.rating_class)
+ + " "
+ + str(difficulty.rating)
+ + ("+" if difficulty.rating_plus else "")
+ )
+
+ jacketPath = Data().getJacketPath(song, difficulty)
+ if not jacketPath:
+ pixmap = QPixmap(":/images/jacket-placeholder.png")
+ else:
+ pixmap = QPixmap(str(jacketPath.resolve()))
+ self.jacketLabel.setPixmap(
+ pixmap.scaled(
+ self.jacketLabel.size(),
+ Qt.AspectRatioMode.KeepAspectRatio,
+ Qt.TransformationMode.SmoothTransformation,
+ )
+ )
+
+ chartInfo = self.db.get_chart_info(songId, ratingClass)
+ if chartInfo is not None:
+ if chartInfo.constant is not None:
+ self.constantLineEdit.setText(str(chartInfo.constant))
+ if chartInfo.notes is not None:
+ self.notesLineEdit.setText(str(chartInfo.notes))
+ else:
+ self.constantLineEdit.setText("")
+ self.notesLineEdit.setText("")
+
+ def commitChartInfo(self):
+ chart = self.chartSelector.value()
+
+ if not chart:
+ QMessageBox.critical(
+ self,
+ None,
+ # fmt: off
+ QCoreApplication.translate("TabDb_ChartInfoEditor", "commit.chartNotSelected"),
+ # fmt: on
+ )
+ return
+ if not self.constantLineEdit.hasAcceptableInput():
+ QMessageBox.critical(
+ self,
+ None,
+ # fmt: off
+ QCoreApplication.translate("TabDb_ChartInfoEditor", "commit.constantRequired"),
+ # fmt: on
+ )
+ return
+
+ constant = int(self.constantLineEdit.text())
+ notes = (
+ int(self.notesLineEdit.text())
+ if self.notesLineEdit.hasAcceptableInput()
+ else None
+ )
+ chartInfo = ChartInfo(
+ song_id=chart.song_id,
+ rating_class=chart.rating_class,
+ constant=constant,
+ notes=notes,
+ )
+ with self.db.sessionmaker() as session:
+ session.merge(chartInfo)
+ session.commit()
+ databaseUpdateSignals.songDataUpdated.emit()
+
+ def deleteChartInfo(self):
+ chart = self.chartSelector.value()
+
+ if not chart:
+ QMessageBox.critical(
+ self,
+ None,
+ # fmt: off
+ QCoreApplication.translate("TabDb_ChartInfoEditor", "commit.chartNotSelected"),
+ # fmt: on
+ )
+ return
+
+ chartInfo = self.db.get_chart_info(chart.song_id, chart.rating_class)
+ if chartInfo:
+ result = QMessageBox.warning(
+ self,
+ None,
+ # fmt: off
+ QCoreApplication.translate("TabDb_ChartInfoEditor", "deleteConfirm"),
+ # fmt: on
+ QMessageBox.StandardButton.Yes,
+ QMessageBox.StandardButton.No,
+ )
+ if result == QMessageBox.StandardButton.Yes:
+ with self.db.sessionmaker() as session:
+ session.delete(chartInfo)
+ session.commit()
+ databaseUpdateSignals.songDataUpdated.emit()
diff --git a/ui/resources/lang/en_US.ts b/ui/resources/lang/en_US.ts
index 973c9a2..291544a 100644
--- a/ui/resources/lang/en_US.ts
+++ b/ui/resources/lang/en_US.ts
@@ -17,12 +17,12 @@
ChartSelector
-
+
songIdSelector.title
Select a Song
-
+
resetButton
Reset
@@ -542,14 +542,14 @@ validation
Search...
-
-
+
+
previous
Previous
-
-
+
+
next
Next
@@ -574,6 +574,11 @@ validation
tab.manage
Manage
+
+
+ tab.chartInfoEditor
+ Chart Info Editor
+
tab.scoreTableViewer
@@ -585,6 +590,60 @@ validation
Table [B30]
+
+ TabDb_ChartInfoEditor
+
+
+ editor.title
+ Editor
+
+
+
+ editor.constant
+ Constant
+
+
+
+ editor.notes
+ Notes
+
+
+
+ editor.tip
+ Tip
+
+
+
+ editor.tip.content
+ Due to the special data structure,<br>please fill in 10 times of the constant value.<br>For example, Testify [BYD] is a 12.0,<br>then fill "120" instead of "12.0".
+
+
+
+ editor.delete
+ Delete
+
+
+
+ editor.commit
+ Commit
+
+
+
+
+ commit.chartNotSelected
+ Please select a chart first.
+
+
+
+ commit.constantRequired
+ Constant field is required.
+
+
+
+ deleteConfirm
+ Are you sure to delete this chart data?
+
+
TabDb_Manage
@@ -783,28 +842,28 @@ validation
Rois
-
+
options.masker
Masker
-
-
+
+
options.useCustom
Use custom options
-
+
dependencies.title
OCR Dependencies
-
+
dependencies.knnModel
KNearest model
-
+
dependencies.phashDatabase
Image pHash database
@@ -812,7 +871,7 @@ validation
TabOverview
-
+
databaseDescribeLabel {} {} {} {} {} {}
There are {} packs, {} songs, {} difficulties, {} chart info ({} complete) and {} scores in database.
@@ -911,23 +970,17 @@ validation
TabTools_ChartRecommend
-
+
constantRangeFromPlayRating
Chart Constant Range from Play Rating
-
+
chartsByConstant
Charts by Constant
-
-
- refreshButton
- Roll
-
-
-
+
chartsRecommendFromPlayRating
Chart from Play Rating Based on Best Score
diff --git a/ui/resources/lang/zh_CN.ts b/ui/resources/lang/zh_CN.ts
index 0064c23..06b81c4 100644
--- a/ui/resources/lang/zh_CN.ts
+++ b/ui/resources/lang/zh_CN.ts
@@ -17,12 +17,12 @@
ChartSelector
-
+
songIdSelector.title
选择一首歌曲
-
+
resetButton
重置
@@ -541,14 +541,14 @@
搜索……
-
-
+
+
previous
上一个
-
-
+
+
next
下一个
@@ -573,6 +573,11 @@
tab.manage
管理
+
+
+ tab.chartInfoEditor
+ 谱面信息编辑器
+
tab.scoreTableViewer
@@ -584,6 +589,60 @@
表 [B30]
+
+ TabDb_ChartInfoEditor
+
+
+ editor.title
+ 编辑器
+
+
+
+ editor.constant
+ 定数
+
+
+
+ editor.notes
+ note 数
+
+
+
+ editor.tip
+ 提示
+
+
+
+ editor.tip.content
+ 由于特殊的数据结构,请在编辑<br>定数时填入原数值的 10 倍。<br>举例:Testify [BYD] 的定数是 12.0,<br>则填入 120,而非 12.0。
+
+
+
+ editor.delete
+ 删除
+
+
+
+ editor.commit
+ 提交
+
+
+
+
+ commit.chartNotSelected
+ 请先选择一个谱面
+
+
+
+ commit.constantRequired
+ 定数字段为必填项
+
+
+
+ deleteConfirm
+ 确定删除该谱面数据吗?
+
+
TabDb_Manage
@@ -782,28 +841,28 @@
定位器
-
+
options.masker
遮罩器
-
-
+
+
options.useCustom
使用自定义设置
-
+
dependencies.title
OCR 依赖
-
+
dependencies.knnModel
KNearest 模型
-
+
dependencies.phashDatabase
图像 pHash 数据库
@@ -811,7 +870,7 @@
TabOverview
-
+
databaseDescribeLabel {} {} {} {} {} {}
数据库中有 {} 个曲包,{} 首歌曲,{} 个难度,{} 个谱面信息({} 个完整),{} 个分数记录。
@@ -910,23 +969,17 @@
TabTools_ChartRecommend
-
+
constantRangeFromPlayRating
由单曲 PTT 逆算谱面定数范围
-
+
chartsByConstant
按定数查谱
-
-
- refreshButton
- 换一批
-
-
-
+
chartsRecommendFromPlayRating
由单曲 PTT 结合最好成绩推荐谱面