mirror of
https://github.com/283375/arcaea-offline-pyside-ui.git
synced 2025-04-18 16:50:17 +00:00
227 lines
8.3 KiB
Python
227 lines
8.3 KiB
Python
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()
|