mirror of
https://github.com/283375/arcaea-offline-pyside-ui.git
synced 2025-07-01 12:26:26 +00:00
feat: TabTools_ChartInfoEditor
This commit is contained in:
226
ui/implements/tabs/tabDb/tabDb_ChartInfoEditor.py
Normal file
226
ui/implements/tabs/tabDb/tabDb_ChartInfoEditor.py
Normal file
@ -0,0 +1,226 @@
|
||||
import logging
|
||||
|
||||
from arcaea_offline.database import Database
|
||||
from arcaea_offline.models import Chart, ChartInfo, Difficulty, Song
|
||||
from arcaea_offline.utils.rating import rating_class_to_text
|
||||
from PySide6.QtCore import QCoreApplication, QModelIndex, Qt, Slot
|
||||
from PySide6.QtGui import QPixmap, QRegularExpressionValidator, QStandardItem
|
||||
from PySide6.QtWidgets import QApplication, QMessageBox, QStyledItemDelegate, QWidget
|
||||
from sqlalchemy import select
|
||||
|
||||
from ui.designer.tabs.tabDb.tabDb_ChartInfoEditor_ui import Ui_TabDb_ChartInfoEditor
|
||||
from ui.extends.shared.data import Data
|
||||
from ui.extends.shared.database import databaseUpdateSignals
|
||||
from ui.extends.shared.language import LanguageChangeEventFilter
|
||||
from ui.extends.tabs.tabDb.tabDb_ChartInfoEditor import (
|
||||
ChartInfoAbsentModel,
|
||||
ListViewDelegate,
|
||||
)
|
||||
from ui.implements.components.songIdSelector import SongIdSelectorMode
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TabDb_ChartInfoEditor(Ui_TabDb_ChartInfoEditor, QWidget):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setupUi(self)
|
||||
|
||||
self.languageChangeEventFilter = LanguageChangeEventFilter(self)
|
||||
self.installEventFilter(self.languageChangeEventFilter)
|
||||
|
||||
self.db = Database()
|
||||
|
||||
self.numberRegexValidator = QRegularExpressionValidator(r"^\d+$", self)
|
||||
self.constantLineEdit.setValidator(self.numberRegexValidator)
|
||||
self.notesLineEdit.setValidator(self.numberRegexValidator)
|
||||
|
||||
self.constantLineEdit.textChanged.connect(self.updateConstantPreviewLabel)
|
||||
|
||||
self.chartInfoAbsentModel = ChartInfoAbsentModel(self)
|
||||
self.listView.setModel(self.chartInfoAbsentModel)
|
||||
self.listViewDelegate = ListViewDelegate(self)
|
||||
|
||||
self.listView.selectionModel().currentChanged.connect(
|
||||
self.listViewSelectionChanged
|
||||
)
|
||||
|
||||
self.chartSelector.setSongIdSelectorMode(SongIdSelectorMode.SongId)
|
||||
self.chartSelector.valueChanged.connect(self.chartSelectorValueChanged)
|
||||
|
||||
databaseUpdateSignals.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()
|
Reference in New Issue
Block a user