diff --git a/ui/designer/tabs/tabTools/tabTools_Andreal.ui b/ui/designer/tabs/tabTools/tabTools_Andreal.ui new file mode 100644 index 0000000..faa61c5 --- /dev/null +++ b/ui/designer/tabs/tabTools/tabTools_Andreal.ui @@ -0,0 +1,400 @@ + + + TabTools_Andreal + + + + 0 + 0 + 551 + 500 + + + + TabTools_Andreal + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + andrealFolder + + + + + + + + + + andrealExecutable + + + + + + + + + + imageType + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + /a + + + true + + + + + + + /a b30 + + + + + + + /a info + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + imageTypeWhatIsThisButton + + + + + + + + + + chart + + + + + + + imageVersion + + + + + + + + 100 + 16777215 + + + + + + + + imageFormat + + + + + + + jpgQuality + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + exportJsonButton + + + + + + + generatePreviewButton + + + + + + + + true + + + + generateImageButton + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + 0 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + JPG + + + true + + + + + + + PNG + + + + + + + + + + preview + + + + + + + + 0 + 0 + + + + QLabel { color: red } + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 10 + + + 100 + + + 90 + + + Qt::Horizontal + + + + + + + 10 + + + 100 + + + 90 + + + + + + + + + + false + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 100 + 100 + + + + chart.selectButton + + + + + + + ... + + + + + + + + + + + FileSelector + QWidget +
ui.implements.components.fileSelector
+ 1 +
+
+ + + + imageFormat_jpgRadioButton + toggled(bool) + jpgQualityHolderWidget + setEnabled(bool) + + + 141 + 149 + + + 330 + 175 + + + + + imageType_bestRadioButton + toggled(bool) + chartHolderWidget + setEnabled(bool) + + + 258 + 64 + + + 330 + 94 + + + + +
diff --git a/ui/designer/tabs/tabTools/tabTools_Andreal_ui.py b/ui/designer/tabs/tabTools/tabTools_Andreal_ui.py new file mode 100644 index 0000000..11a8761 --- /dev/null +++ b/ui/designer/tabs/tabTools/tabTools_Andreal_ui.py @@ -0,0 +1,264 @@ +# -*- coding: utf-8 -*- + +################################################################################ +## Form generated from reading UI file 'tabTools_Andreal.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, QComboBox, QFormLayout, QHBoxLayout, + QLabel, QPushButton, QRadioButton, QSizePolicy, + QSlider, QSpacerItem, QSpinBox, QWidget) + +from ui.implements.components.fileSelector import FileSelector + +class Ui_TabTools_Andreal(object): + def setupUi(self, TabTools_Andreal): + if not TabTools_Andreal.objectName(): + TabTools_Andreal.setObjectName(u"TabTools_Andreal") + TabTools_Andreal.resize(551, 500) + TabTools_Andreal.setWindowTitle(u"TabTools_Andreal") + self.formLayout = QFormLayout(TabTools_Andreal) + self.formLayout.setObjectName(u"formLayout") + self.formLayout.setLabelAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter) + self.label = QLabel(TabTools_Andreal) + self.label.setObjectName(u"label") + + self.formLayout.setWidget(0, QFormLayout.LabelRole, self.label) + + self.andrealFolderSelector = FileSelector(TabTools_Andreal) + self.andrealFolderSelector.setObjectName(u"andrealFolderSelector") + + self.formLayout.setWidget(0, QFormLayout.FieldRole, self.andrealFolderSelector) + + self.label_2 = QLabel(TabTools_Andreal) + self.label_2.setObjectName(u"label_2") + + self.formLayout.setWidget(1, QFormLayout.LabelRole, self.label_2) + + self.andrealExecutableSelector = FileSelector(TabTools_Andreal) + self.andrealExecutableSelector.setObjectName(u"andrealExecutableSelector") + + self.formLayout.setWidget(1, QFormLayout.FieldRole, self.andrealExecutableSelector) + + self.label_3 = QLabel(TabTools_Andreal) + self.label_3.setObjectName(u"label_3") + + self.formLayout.setWidget(2, QFormLayout.LabelRole, self.label_3) + + self.horizontalWidget = QWidget(TabTools_Andreal) + self.horizontalWidget.setObjectName(u"horizontalWidget") + self.horizontalLayout = QHBoxLayout(self.horizontalWidget) + self.horizontalLayout.setObjectName(u"horizontalLayout") + self.horizontalLayout.setContentsMargins(0, 0, 0, 0) + self.imageType_infoRadioButton = QRadioButton(self.horizontalWidget) + self.imageType_infoRadioButton.setObjectName(u"imageType_infoRadioButton") + self.imageType_infoRadioButton.setChecked(True) + + self.horizontalLayout.addWidget(self.imageType_infoRadioButton) + + self.imageType_best30RadioButton = QRadioButton(self.horizontalWidget) + self.imageType_best30RadioButton.setObjectName(u"imageType_best30RadioButton") + + self.horizontalLayout.addWidget(self.imageType_best30RadioButton) + + self.imageType_bestRadioButton = QRadioButton(self.horizontalWidget) + self.imageType_bestRadioButton.setObjectName(u"imageType_bestRadioButton") + + self.horizontalLayout.addWidget(self.imageType_bestRadioButton) + + self.horizontalSpacer_3 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + + self.horizontalLayout.addItem(self.horizontalSpacer_3) + + self.imageTypeWhatIsThisButton = QPushButton(self.horizontalWidget) + self.imageTypeWhatIsThisButton.setObjectName(u"imageTypeWhatIsThisButton") + + self.horizontalLayout.addWidget(self.imageTypeWhatIsThisButton) + + + self.formLayout.setWidget(2, QFormLayout.FieldRole, self.horizontalWidget) + + self.chartFormLabel = QLabel(TabTools_Andreal) + self.chartFormLabel.setObjectName(u"chartFormLabel") + + self.formLayout.setWidget(3, QFormLayout.LabelRole, self.chartFormLabel) + + self.label_5 = QLabel(TabTools_Andreal) + self.label_5.setObjectName(u"label_5") + + self.formLayout.setWidget(4, QFormLayout.LabelRole, self.label_5) + + self.imageVersionComboBox = QComboBox(TabTools_Andreal) + self.imageVersionComboBox.setObjectName(u"imageVersionComboBox") + self.imageVersionComboBox.setMaximumSize(QSize(100, 16777215)) + + self.formLayout.setWidget(4, QFormLayout.FieldRole, self.imageVersionComboBox) + + self.label_6 = QLabel(TabTools_Andreal) + self.label_6.setObjectName(u"label_6") + + self.formLayout.setWidget(5, QFormLayout.LabelRole, self.label_6) + + self.jpgQualityFormLabel = QLabel(TabTools_Andreal) + self.jpgQualityFormLabel.setObjectName(u"jpgQualityFormLabel") + + self.formLayout.setWidget(6, QFormLayout.LabelRole, self.jpgQualityFormLabel) + + self.horizontalLayout_5 = QHBoxLayout() + self.horizontalLayout_5.setObjectName(u"horizontalLayout_5") + self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + + self.horizontalLayout_5.addItem(self.horizontalSpacer) + + self.exportJsonButton = QPushButton(TabTools_Andreal) + self.exportJsonButton.setObjectName(u"exportJsonButton") + + self.horizontalLayout_5.addWidget(self.exportJsonButton) + + self.generatePreviewButton = QPushButton(TabTools_Andreal) + self.generatePreviewButton.setObjectName(u"generatePreviewButton") + + self.horizontalLayout_5.addWidget(self.generatePreviewButton) + + self.generateImageButton = QPushButton(TabTools_Andreal) + self.generateImageButton.setObjectName(u"generateImageButton") + font = QFont() + font.setBold(True) + self.generateImageButton.setFont(font) + + self.horizontalLayout_5.addWidget(self.generateImageButton) + + self.horizontalSpacer_2 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + + self.horizontalLayout_5.addItem(self.horizontalSpacer_2) + + + self.formLayout.setLayout(7, QFormLayout.SpanningRole, self.horizontalLayout_5) + + self.horizontalWidget_2 = QWidget(TabTools_Andreal) + self.horizontalWidget_2.setObjectName(u"horizontalWidget_2") + sizePolicy = QSizePolicy(QSizePolicy.Maximum, QSizePolicy.Preferred) + 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.imageFormat_jpgRadioButton = QRadioButton(self.horizontalWidget_2) + self.imageFormat_jpgRadioButton.setObjectName(u"imageFormat_jpgRadioButton") + self.imageFormat_jpgRadioButton.setText(u"JPG") + self.imageFormat_jpgRadioButton.setChecked(True) + + self.horizontalLayout_2.addWidget(self.imageFormat_jpgRadioButton) + + self.imageFormat_pngRadioButton = QRadioButton(self.horizontalWidget_2) + self.imageFormat_pngRadioButton.setObjectName(u"imageFormat_pngRadioButton") + self.imageFormat_pngRadioButton.setText(u"PNG") + + self.horizontalLayout_2.addWidget(self.imageFormat_pngRadioButton) + + + self.formLayout.setWidget(5, QFormLayout.FieldRole, self.horizontalWidget_2) + + self.label_8 = QLabel(TabTools_Andreal) + self.label_8.setObjectName(u"label_8") + + self.formLayout.setWidget(8, QFormLayout.LabelRole, self.label_8) + + self.previewLabel = QLabel(TabTools_Andreal) + self.previewLabel.setObjectName(u"previewLabel") + sizePolicy1 = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding) + sizePolicy1.setHorizontalStretch(0) + sizePolicy1.setVerticalStretch(0) + sizePolicy1.setHeightForWidth(self.previewLabel.sizePolicy().hasHeightForWidth()) + self.previewLabel.setSizePolicy(sizePolicy1) + self.previewLabel.setStyleSheet(u"QLabel { color: red }") + self.previewLabel.setText(u"") + + self.formLayout.setWidget(8, QFormLayout.FieldRole, self.previewLabel) + + self.jpgQualityHolderWidget = QWidget(TabTools_Andreal) + self.jpgQualityHolderWidget.setObjectName(u"jpgQualityHolderWidget") + self.horizontalLayout_3 = QHBoxLayout(self.jpgQualityHolderWidget) + self.horizontalLayout_3.setObjectName(u"horizontalLayout_3") + self.horizontalLayout_3.setContentsMargins(0, 0, 0, 0) + self.jpgQualitySlider = QSlider(self.jpgQualityHolderWidget) + self.jpgQualitySlider.setObjectName(u"jpgQualitySlider") + self.jpgQualitySlider.setMinimum(10) + self.jpgQualitySlider.setMaximum(100) + self.jpgQualitySlider.setValue(90) + self.jpgQualitySlider.setOrientation(Qt.Horizontal) + + self.horizontalLayout_3.addWidget(self.jpgQualitySlider) + + self.jpgQualitySpinBox = QSpinBox(self.jpgQualityHolderWidget) + self.jpgQualitySpinBox.setObjectName(u"jpgQualitySpinBox") + self.jpgQualitySpinBox.setMinimum(10) + self.jpgQualitySpinBox.setMaximum(100) + self.jpgQualitySpinBox.setValue(90) + + self.horizontalLayout_3.addWidget(self.jpgQualitySpinBox) + + + self.formLayout.setWidget(6, QFormLayout.FieldRole, self.jpgQualityHolderWidget) + + self.chartHolderWidget = QWidget(TabTools_Andreal) + self.chartHolderWidget.setObjectName(u"chartHolderWidget") + self.chartHolderWidget.setEnabled(False) + self.horizontalLayout_8 = QHBoxLayout(self.chartHolderWidget) + self.horizontalLayout_8.setObjectName(u"horizontalLayout_8") + self.horizontalLayout_8.setContentsMargins(0, 0, 0, 0) + self.chartSelectButton = QPushButton(self.chartHolderWidget) + self.chartSelectButton.setObjectName(u"chartSelectButton") + self.chartSelectButton.setMaximumSize(QSize(100, 100)) + + self.horizontalLayout_8.addWidget(self.chartSelectButton) + + self.chartSelectLabel = QLabel(self.chartHolderWidget) + self.chartSelectLabel.setObjectName(u"chartSelectLabel") + self.chartSelectLabel.setText(u"...") + + self.horizontalLayout_8.addWidget(self.chartSelectLabel) + + + self.formLayout.setWidget(3, QFormLayout.FieldRole, self.chartHolderWidget) + + + self.retranslateUi(TabTools_Andreal) + self.imageFormat_jpgRadioButton.toggled.connect(self.jpgQualityHolderWidget.setEnabled) + self.imageType_bestRadioButton.toggled.connect(self.chartHolderWidget.setEnabled) + + QMetaObject.connectSlotsByName(TabTools_Andreal) + # setupUi + + def retranslateUi(self, TabTools_Andreal): + self.label.setText(QCoreApplication.translate("TabTools_Andreal", u"andrealFolder", None)) + self.label_2.setText(QCoreApplication.translate("TabTools_Andreal", u"andrealExecutable", None)) + self.label_3.setText(QCoreApplication.translate("TabTools_Andreal", u"imageType", None)) + self.imageType_infoRadioButton.setText(QCoreApplication.translate("TabTools_Andreal", u"/a", None)) + self.imageType_best30RadioButton.setText(QCoreApplication.translate("TabTools_Andreal", u"/a b30", None)) + self.imageType_bestRadioButton.setText(QCoreApplication.translate("TabTools_Andreal", u"/a info", None)) + self.imageTypeWhatIsThisButton.setText(QCoreApplication.translate("TabTools_Andreal", u"imageTypeWhatIsThisButton", None)) + self.chartFormLabel.setText(QCoreApplication.translate("TabTools_Andreal", u"chart", None)) + self.label_5.setText(QCoreApplication.translate("TabTools_Andreal", u"imageVersion", None)) + self.label_6.setText(QCoreApplication.translate("TabTools_Andreal", u"imageFormat", None)) + self.jpgQualityFormLabel.setText(QCoreApplication.translate("TabTools_Andreal", u"jpgQuality", None)) + self.exportJsonButton.setText(QCoreApplication.translate("TabTools_Andreal", u"exportJsonButton", None)) + self.generatePreviewButton.setText(QCoreApplication.translate("TabTools_Andreal", u"generatePreviewButton", None)) + self.generateImageButton.setText(QCoreApplication.translate("TabTools_Andreal", u"generateImageButton", None)) + self.label_8.setText(QCoreApplication.translate("TabTools_Andreal", u"preview", None)) + self.chartSelectButton.setText(QCoreApplication.translate("TabTools_Andreal", u"chart.selectButton", None)) + pass + # retranslateUi + diff --git a/ui/designer/tabs/tabToolsEntry.ui b/ui/designer/tabs/tabToolsEntry.ui index 1378563..ae30179 100644 --- a/ui/designer/tabs/tabToolsEntry.ui +++ b/ui/designer/tabs/tabToolsEntry.ui @@ -29,6 +29,11 @@ tab.stepCalculator + + + tab.andreal + + @@ -46,6 +51,12 @@
ui.implements.tabs.tabTools.tabTools_StepCalculator
1 + + TabTools_Andreal + QWidget +
ui.implements.tabs.tabTools.tabTools_Andreal
+ 1 +
diff --git a/ui/designer/tabs/tabToolsEntry_ui.py b/ui/designer/tabs/tabToolsEntry_ui.py index b0a30e0..7607b9c 100644 --- a/ui/designer/tabs/tabToolsEntry_ui.py +++ b/ui/designer/tabs/tabToolsEntry_ui.py @@ -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.tabTools.tabTools_Andreal import TabTools_Andreal from ui.implements.tabs.tabTools.tabTools_InfoLookup import TabTools_InfoLookup from ui.implements.tabs.tabTools.tabTools_StepCalculator import TabTools_StepCalculator @@ -37,6 +38,9 @@ class Ui_TabToolsEntry(object): self.tab_2 = TabTools_StepCalculator() self.tab_2.setObjectName(u"tab_2") self.tabWidget.addTab(self.tab_2, "") + self.tab_3 = TabTools_Andreal() + self.tab_3.setObjectName(u"tab_3") + self.tabWidget.addTab(self.tab_3, "") self.verticalLayout.addWidget(self.tabWidget) @@ -52,6 +56,7 @@ class Ui_TabToolsEntry(object): def retranslateUi(self, TabToolsEntry): self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), QCoreApplication.translate("TabToolsEntry", u"tab.infoLookup", None)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), QCoreApplication.translate("TabToolsEntry", u"tab.stepCalculator", None)) + self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_3), QCoreApplication.translate("TabToolsEntry", u"tab.andreal", None)) pass # retranslateUi diff --git a/ui/extends/tabs/tabTools/tabTools_Andreal.py b/ui/extends/tabs/tabTools/tabTools_Andreal.py new file mode 100644 index 0000000..6b233b0 --- /dev/null +++ b/ui/extends/tabs/tabTools/tabTools_Andreal.py @@ -0,0 +1,81 @@ +import base64 +import logging +import os +import re + +from PySide6.QtCore import QObject, QProcess, QRunnable, QThreadPool, Signal + +logger = logging.getLogger(__name__) + + +class AndrealExecuteRunnableSignals(QObject): + error = Signal(str, str) + completed = Signal(str, bytes) + finished = Signal() + + +class AndrealExecuteRunnable(QRunnable): + def __init__(self, executePath, jsonPath, arguments): + super().__init__() + self.signals = AndrealExecuteRunnableSignals() + self.executePath = executePath + self.jsonPath = jsonPath + self.arguments = arguments + + def run(self): + try: + result = os.popen(f"{self.executePath} {' '.join(self.arguments)}").read() + b64Result = [s for s in result.split("\n") if s] + imageBytes = base64.b64decode( + re.sub(r"data:image/.*;base64,", "", b64Result[-1]) + ) + self.signals.completed.emit(self.jsonPath, imageBytes) + except Exception as e: + imageBytes = None + logger.exception(f"{self.__class__.__name__} error") + self.signals.error.emit(self.jsonPath, str(e)) + finally: + os.unlink(self.jsonPath) + self.signals.finished.emit() + + +class AndrealHelper(QObject): + error = Signal(str, str) + ready = Signal(str, bytes) + finished = Signal() + + def __init__(self, parent=None): + super().__init__(parent) + + self.andrealFolder = None + self.__andrealExecutable = None + + self.andrealTestProcess = QProcess(self) + self.__andrealTestResult: bool = False + + @property + def andrealExecutable(self): + return self.__andrealExecutable + + @andrealExecutable.setter + def andrealExecutable(self, value: str): + self.__andrealExecutable = value + self.andrealTestProcess.start(self.__andrealExecutable, ["--help"]) + self.andrealTestProcess.waitForFinished() + result = bytes(self.andrealTestProcess.readAll()).decode("utf-8") + self.__andrealTestResult = "Andreal" in result and "--json-file" in result + + @property + def andrealTestResult(self): + return self.__andrealTestResult + + def andrealOutputToImage(self, output: str): + b64result = output.split("\n")[-1] + return base64.b64decode(b64result) + + def request(self, jsonPath: str, arguments: list[str]): + runnable = AndrealExecuteRunnable(self.andrealExecutable, jsonPath, arguments) + runnable.signals.error.connect(self.error) + runnable.signals.completed.connect(self.ready) + runnable.signals.finished.connect(self.finished) + QThreadPool.globalInstance().start(runnable) diff --git a/ui/implements/settings/settingsAndreal.py b/ui/implements/settings/settingsAndreal.py index 2d6244d..5373ed8 100644 --- a/ui/implements/settings/settingsAndreal.py +++ b/ui/implements/settings/settingsAndreal.py @@ -11,6 +11,9 @@ class SettingsAndreal(SettingsBaseWidget): self.setupUi(self) + self.andrealFolderValueWidget.setMode( + self.andrealFolderValueWidget.getExistingDirectory + ) if self.settings.andrealFolder(): self.andrealFolderValueWidget.selectFile(self.settings.andrealFolder()) self.andrealFolderValueWidget.filesSelected.connect(self.setAndrealFolder) diff --git a/ui/implements/tabs/tabTools/tabTools_Andreal.py b/ui/implements/tabs/tabTools/tabTools_Andreal.py new file mode 100644 index 0000000..ff53822 --- /dev/null +++ b/ui/implements/tabs/tabTools/tabTools_Andreal.py @@ -0,0 +1,239 @@ +import json +import logging +import time + +from arcaea_offline.database import Database +from arcaea_offline.external.andreal.api_data import ( + AndrealImageGeneratorApiDataConverter, +) +from arcaea_offline.models import Chart +from arcaea_offline.utils.rating import rating_class_to_short_text +from PySide6.QtCore import QDir, QFileInfo, Qt, Slot +from PySide6.QtGui import QImage +from PySide6.QtWidgets import QButtonGroup, QFileDialog, QLabel, QMessageBox, QWidget + +from ui.designer.tabs.tabTools.tabTools_Andreal_ui import Ui_TabTools_Andreal +from ui.extends.shared.settings import ANDREAL_EXECUTABLE, ANDREAL_FOLDER +from ui.extends.tabs.tabTools.tabTools_Andreal import AndrealHelper +from ui.implements.components.chartSelector import ChartSelector +from ui.implements.components.songIdSelector import SongIdSelectorMode + +logger = logging.getLogger(__name__) + + +class PreviewLabel(QLabel): + ... + + +class ChartSelectorDialog(ChartSelector): + def __init__(self, parent=None): + super().__init__(parent) + self.setWindowFlag(Qt.WindowType.Dialog, True) + self.setSongIdSelectorMode(SongIdSelectorMode.Chart) + + +class TabTools_Andreal(Ui_TabTools_Andreal, QWidget): + def __init__(self, parent=None): + super().__init__(parent) + self.setupUi(self) + + self.db = Database() + + self.andrealHelper = AndrealHelper(self) + + self.andrealFolderSelector.setMode( + self.andrealFolderSelector.getExistingDirectory + ) + + self.andrealFolderSelector.filesSelected.connect(self.setHelperPaths) + self.andrealExecutableSelector.filesSelected.connect(self.setHelperPaths) + + self.andrealFolderSelector.connectSettings(ANDREAL_FOLDER) + self.andrealExecutableSelector.connectSettings(ANDREAL_EXECUTABLE) + + self.generateImageButton.clicked.connect(self.requestGenerate) + + self.infoChart: Chart | None = None + + self.previewJsonPath = None + self.generateJsonPath = None + self.generateImageFormat = None + + self.andrealHelper.error.connect(self.generateError) + self.andrealHelper.ready.connect(self.generateReady) + self.andrealHelper.finished.connect(self.generateFinished) + + self.imageTypeButtonGroup = QButtonGroup(self) + self.imageTypeButtonGroup.addButton(self.imageType_infoRadioButton, 0) + self.imageTypeButtonGroup.addButton(self.imageType_bestRadioButton, 1) + self.imageTypeButtonGroup.addButton(self.imageType_best30RadioButton, 2) + + self.imageFormatButtonGroup = QButtonGroup(self) + self.imageFormatButtonGroup.addButton(self.imageFormat_jpgRadioButton, 0) + self.imageFormatButtonGroup.addButton(self.imageFormat_pngRadioButton, 1) + + self.imageTypeButtonGroup.idToggled.connect(self.fillImageVersionComboBox) + self.fillImageVersionComboBox() + + def setHelperPaths(self): + if selectedFiles := self.andrealFolderSelector.selectedFiles(): + self.andrealHelper.andrealFolder = selectedFiles[0] + if selectedFiles := self.andrealExecutableSelector.selectedFiles(): + self.andrealHelper.andrealExecutable = selectedFiles[0] + + def imageFormat(self): + buttonId = self.imageFormatButtonGroup.checkedId() + return ["jpg", "png"][buttonId] if buttonId > -1 else None + + def imageType(self): + buttonId = self.imageTypeButtonGroup.checkedId() + return ["info", "best", "best30"][buttonId] if buttonId > -1 else None + + def fillImageVersionComboBox(self): + imageType = self.imageType() + if not imageType: + return + + self.imageVersionComboBox.clear() + if imageType in ["info", "best"]: + self.imageVersionComboBox.addItem("3", 3) + self.imageVersionComboBox.addItem("2", 2) + self.imageVersionComboBox.addItem("1", 1) + elif imageType == "best30": + self.imageVersionComboBox.addItem("2", 2) + self.imageVersionComboBox.addItem("1", 1) + + def imageVersion(self): + return self.imageVersionComboBox.currentData() + + def requestComplete(self) -> bool: + if not self.imageType(): + return False + + imageType = self.imageType() + if imageType == "best" and not self.infoChart: + return False + + return self.imageVersion() is not None + + def getAndrealArguments(self, jsonFile: str, *, preview: bool = False): + if not self.requestComplete(): + return + + arguments = [ + str(self.imageType()), + f'--json-file="{jsonFile}"', + f"--img-version={self.imageVersion()}", + ] + if self.andrealFolderSelector.selectedFiles(): + arguments.append( + f'--path="{self.andrealFolderSelector.selectedFiles()[0]}"' + ) + if preview: + arguments.extend(["--img-format=jpg", "--img-quality=20"]) + else: + arguments.append(f"--img-format={self.imageFormat()}") + if self.imageFormat() == "jpg": + arguments.append(f"--img-quality={self.jpgQualitySpinBox.value()}") + return arguments + + def getAndrealJsonContent(self): + if not self.requestComplete(): + return None + + imageType = self.imageType() + if imageType == "best" and not self.infoChart: + return + + jsonContentDict = {} + with self.db.sessionmaker() as session: + converter = AndrealImageGeneratorApiDataConverter(session) + if imageType == "info": + jsonContentDict = converter.user_info() + elif imageType == "best": + jsonContentDict = converter.user_best( + self.infoChart.song_id, self.infoChart.rating_class + ) + elif imageType == "best30": + jsonContentDict = converter.user_best30() + return json.dumps(jsonContentDict, ensure_ascii=False) + + def getAndrealJsonFileName(self): + if not self.requestComplete(): + return None + + imageType = self.imageType() + timestamp = int(time.time() * 1000) + + fileNameParts = ["andreal", imageType] + if imageType == "best": + fileNameParts.extend( + [ + self.infoChart.song_id, + rating_class_to_short_text(self.infoChart.rating_class).lower(), + ] + ) + fileNameParts.append(timestamp) + fileNameParts = [str(i) for i in fileNameParts] + fileName = "-".join(fileNameParts) + return f"{fileName}.json" + + def getTempAndrealJsonPath(self): + if fileName := self.getAndrealJsonFileName(): + return QDir.temp().filePath(fileName) + else: + return None + + @Slot() + def on_exportJsonButton_clicked(self): + content = self.getAndrealJsonContent() + fileName = self.getAndrealJsonFileName() + if not content or not fileName: + return + + saveFileName, _ = QFileDialog.getSaveFileName(self, None, fileName) + if not saveFileName: + return + with open(saveFileName, "w", encoding="utf-8") as jf: + jf.write(content) + + def requestGenerate(self): + jsonPath = self.getTempAndrealJsonPath() + if not jsonPath: + return + + self.generateImageButton.setEnabled(False) + self.generateJsonPath = jsonPath + self.generateImageFormat = self.imageFormat() + with open(jsonPath, "w", encoding="utf-8") as jf: + jf.write(self.getAndrealJsonContent()) + + self.andrealHelper.request(jsonPath, self.getAndrealArguments(jsonPath)) + + def generateFinished(self): + self.generateImageButton.setEnabled(True) + + def generateError(self, jsonPath: str, errorMsg: str): + if jsonPath != self.generateJsonPath: + return + QMessageBox.critical(self, "Generate Error", errorMsg) + + def generateReady(self, jsonPath: str, imageBytes: bytes): + if jsonPath != self.generateJsonPath: + return + + if not imageBytes: + QMessageBox.critical(self, "Generate Error", "Empty bytes received.") + return + + qImage = QImage.fromData(imageBytes) + + filePathParts = jsonPath.split(".") + filePathParts[-1] = self.generateImageFormat + filePath = ".".join(filePathParts) + fileName = QFileInfo(filePath).fileName() + + saveFileName, _ = QFileDialog.getSaveFileName(self, None, fileName) + if not saveFileName: + return + qImage.save(saveFileName, self.generateImageFormat)