18 Commits

Author SHA1 Message Date
86b1653fe3 impr: minor improvements 2023-10-15 02:47:15 +08:00
3e2e96b00b impr: TabTools_ChartRecommend ui 2023-10-15 00:24:17 +08:00
9bb6f5b3d9 fix: ChartSelector not selecting rating class 2023-10-14 17:58:35 +08:00
8628399469 impr: TextSegmentDelegate improvements 2023-10-14 16:52:41 +08:00
cf913d296e impr: minor improvement 2023-10-14 00:55:12 +08:00
1060590e03 wip: show song jacket in ChartDelegate 2023-10-14 00:55:01 +08:00
858abe3415 refactor: TabOcr_B30 2023-10-13 20:15:16 +08:00
cd4ed51826 chore: minor improvements 2023-10-12 18:12:35 +08:00
ad5e5ec694 wip: arcaea-offline-ocr==0.1.0
settings
2023-10-12 17:37:55 +08:00
5c5c1a227d wip: arcaea-offline-ocr==0.1.0
API changes, modifier & clear_type support
2023-10-12 17:05:04 +08:00
cde8a047a7 feat: show modifier and clear_type in ScoreDelegate 2023-10-10 22:10:42 +08:00
19cd526814 fix: correct phash database labels 2023-10-10 19:50:59 +08:00
8b6f64e041 wip: arcaea-offline-ocr==0.1.0
ui changes
2023-10-10 19:50:27 +08:00
6dbb7cbfef impr: use QSignalMapper for SongIdSelector's quick switch actions 2023-10-10 18:38:48 +08:00
94e4d73a95 impr: TabOcr_BuildPHashDatabase 2023-10-10 01:26:20 +08:00
4a1e20a45f feat: TabOcr_BuildPHashDatabase 2023-10-09 22:48:08 +08:00
de8c5d28a7 fix: use subprocess instead of os.popen for andreal calling 2023-10-01 02:47:16 +08:00
bce48a03a7 fix: write database url into settings 2023-09-28 17:59:33 +08:00
43 changed files with 2863 additions and 1305 deletions

1
.gitignore vendored
View File

@ -3,6 +3,7 @@ __debug*
arcaea_offline.db arcaea_offline.db
arcaea_offline.ini arcaea_offline.ini
/data
ui/resources/VERSION ui/resources/VERSION

View File

@ -29,9 +29,9 @@
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<item> <item>
<widget class="QRadioButton" name="iccIgnoreRadioButton"> <widget class="QRadioButton" name="iccUseQtRadioButton">
<property name="text"> <property name="text">
<string>icc.ignore</string> <string>icc.useQt</string>
</property> </property>
</widget> </widget>
</item> </item>

View File

@ -38,10 +38,10 @@ class Ui_OcrQueue(object):
self.groupBox.setObjectName(u"groupBox") self.groupBox.setObjectName(u"groupBox")
self.verticalLayout = QVBoxLayout(self.groupBox) self.verticalLayout = QVBoxLayout(self.groupBox)
self.verticalLayout.setObjectName(u"verticalLayout") self.verticalLayout.setObjectName(u"verticalLayout")
self.iccIgnoreRadioButton = QRadioButton(self.groupBox) self.iccUseQtRadioButton = QRadioButton(self.groupBox)
self.iccIgnoreRadioButton.setObjectName(u"iccIgnoreRadioButton") self.iccUseQtRadioButton.setObjectName(u"iccUseQtRadioButton")
self.verticalLayout.addWidget(self.iccIgnoreRadioButton) self.verticalLayout.addWidget(self.iccUseQtRadioButton)
self.iccUsePILRadioButton = QRadioButton(self.groupBox) self.iccUsePILRadioButton = QRadioButton(self.groupBox)
self.iccUsePILRadioButton.setObjectName(u"iccUsePILRadioButton") self.iccUsePILRadioButton.setObjectName(u"iccUsePILRadioButton")
@ -155,7 +155,7 @@ class Ui_OcrQueue(object):
def retranslateUi(self, OcrQueue): def retranslateUi(self, OcrQueue):
self.groupBox_3.setTitle(QCoreApplication.translate("OcrQueue", u"queue.title", None)) self.groupBox_3.setTitle(QCoreApplication.translate("OcrQueue", u"queue.title", None))
self.groupBox.setTitle(QCoreApplication.translate("OcrQueue", u"iccOptionsGroupBox", None)) self.groupBox.setTitle(QCoreApplication.translate("OcrQueue", u"iccOptionsGroupBox", None))
self.iccIgnoreRadioButton.setText(QCoreApplication.translate("OcrQueue", u"icc.ignore", None)) self.iccUseQtRadioButton.setText(QCoreApplication.translate("OcrQueue", u"icc.useQt", None))
self.iccUsePILRadioButton.setText(QCoreApplication.translate("OcrQueue", u"icc.usePIL", None)) self.iccUsePILRadioButton.setText(QCoreApplication.translate("OcrQueue", u"icc.usePIL", None))
self.iccTryFixRadioButton.setText(QCoreApplication.translate("OcrQueue", u"icc.tryFix", None)) self.iccTryFixRadioButton.setText(QCoreApplication.translate("OcrQueue", u"icc.tryFix", None))
self.ocr_addImageButton.setText(QCoreApplication.translate("OcrQueue", u"queue.addImageButton", None)) self.ocr_addImageButton.setText(QCoreApplication.translate("OcrQueue", u"queue.addImageButton", None))

View File

@ -13,7 +13,7 @@
<property name="windowTitle"> <property name="windowTitle">
<string notr="true">TabOcr_B30</string> <string notr="true">TabOcr_B30</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_3"> <layout class="QVBoxLayout" name="verticalLayout_2">
<item> <item>
<widget class="QGroupBox" name="groupBox"> <widget class="QGroupBox" name="groupBox">
<property name="title"> <property name="title">
@ -27,60 +27,87 @@
</widget> </widget>
</item> </item>
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout"> <widget class="QGroupBox" name="groupBox_6">
<item> <property name="title">
<widget class="QGroupBox" name="groupBox_3"> <string>dependencies.title</string>
<property name="title"> </property>
<string>knnModelSelector.title</string> <layout class="QGridLayout" name="gridLayout" columnstretch="0,0,0,0,1">
</property> <item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout_4"> <widget class="QLabel" name="label">
<item> <property name="text">
<widget class="FileSelector" name="knnModelSelector" native="true"/> <string>dependencies.knnModel</string>
</item> </property>
</layout> <property name="alignment">
</widget> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</item> </property>
<item> </widget>
<widget class="QGroupBox" name="groupBox_5"> </item>
<property name="title"> <item row="0" column="2">
<string>b30KnnModelSelector.title</string> <widget class="QLabel" name="dependencies_knnModelStatusLabel">
</property> <property name="text">
<layout class="QVBoxLayout" name="verticalLayout_6"> <string notr="true">...</string>
<item> </property>
<widget class="FileSelector" name="b30KnnModelSelector" native="true"/> </widget>
</item> </item>
</layout> <item row="0" column="3" rowspan="3">
</widget> <widget class="Line" name="line_2">
</item> <property name="orientation">
</layout> <enum>Qt::Vertical</enum>
</item> </property>
<item> </widget>
<layout class="QHBoxLayout" name="horizontalLayout_3"> </item>
<item> <item row="1" column="0">
<widget class="QGroupBox" name="groupBox_4"> <widget class="QLabel" name="label_2">
<property name="title"> <property name="text">
<string>phashDatabaseSelector.title</string> <string>dependencies.b30KnnModel</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_5"> <property name="alignment">
<item> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
<widget class="FileSelector" name="phashDatabaseSelector" native="true"/> </property>
</item> </widget>
</layout> </item>
</widget> <item row="2" column="2">
</item> <widget class="QLabel" name="dependencies_phashDatabaseStatusLabel">
<item> <property name="text">
<widget class="QGroupBox" name="groupBox_2"> <string notr="true">...</string>
<property name="title"> </property>
<string>imageSelector.title</string> </widget>
</property> </item>
<layout class="QVBoxLayout" name="verticalLayout_2"> <item row="0" column="1" rowspan="3">
<item> <widget class="Line" name="line">
<widget class="FileSelector" name="imageSelector" native="true"/> <property name="orientation">
</item> <enum>Qt::Vertical</enum>
</layout> </property>
</widget> </widget>
</item> </item>
</layout> <item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>dependencies.phashDatabase</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="dependencies_b30KnnModelStatusLabel">
<property name="text">
<string notr="true">...</string>
</property>
</widget>
</item>
<item row="0" column="4">
<widget class="FileSelector" name="dependencies_knnModelSelector" native="true"/>
</item>
<item row="1" column="4">
<widget class="FileSelector" name="dependencies_b30KnnModelSelector" native="true"/>
</item>
<item row="2" column="4">
<widget class="FileSelector" name="dependencies_phashDatabaseSelector" native="true"/>
</item>
</layout>
</widget>
</item> </item>
<item> <item>
<widget class="OcrQueue" name="ocrQueue" native="true"> <widget class="OcrQueue" name="ocrQueue" native="true">

View File

@ -15,8 +15,9 @@ from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
QFont, QFontDatabase, QGradient, QIcon, QFont, QFontDatabase, QGradient, QIcon,
QImage, QKeySequence, QLinearGradient, QPainter, QImage, QKeySequence, QLinearGradient, QPainter,
QPalette, QPixmap, QRadialGradient, QTransform) QPalette, QPixmap, QRadialGradient, QTransform)
from PySide6.QtWidgets import (QApplication, QComboBox, QGroupBox, QHBoxLayout, from PySide6.QtWidgets import (QApplication, QComboBox, QFrame, QGridLayout,
QSizePolicy, QVBoxLayout, QWidget) QGroupBox, QLabel, QSizePolicy, QVBoxLayout,
QWidget)
from ui.implements.components.fileSelector import FileSelector from ui.implements.components.fileSelector import FileSelector
from ui.implements.components.ocrQueue import OcrQueue from ui.implements.components.ocrQueue import OcrQueue
@ -27,8 +28,8 @@ class Ui_TabOcr_B30(object):
TabOcr_B30.setObjectName(u"TabOcr_B30") TabOcr_B30.setObjectName(u"TabOcr_B30")
TabOcr_B30.resize(555, 461) TabOcr_B30.resize(555, 461)
TabOcr_B30.setWindowTitle(u"TabOcr_B30") TabOcr_B30.setWindowTitle(u"TabOcr_B30")
self.verticalLayout_3 = QVBoxLayout(TabOcr_B30) self.verticalLayout_2 = QVBoxLayout(TabOcr_B30)
self.verticalLayout_3.setObjectName(u"verticalLayout_3") self.verticalLayout_2.setObjectName(u"verticalLayout_2")
self.groupBox = QGroupBox(TabOcr_B30) self.groupBox = QGroupBox(TabOcr_B30)
self.groupBox.setObjectName(u"groupBox") self.groupBox.setObjectName(u"groupBox")
self.verticalLayout = QVBoxLayout(self.groupBox) self.verticalLayout = QVBoxLayout(self.groupBox)
@ -39,65 +40,80 @@ class Ui_TabOcr_B30(object):
self.verticalLayout.addWidget(self.b30TypeComboBox) self.verticalLayout.addWidget(self.b30TypeComboBox)
self.verticalLayout_3.addWidget(self.groupBox) self.verticalLayout_2.addWidget(self.groupBox)
self.horizontalLayout = QHBoxLayout() self.groupBox_6 = QGroupBox(TabOcr_B30)
self.horizontalLayout.setObjectName(u"horizontalLayout") self.groupBox_6.setObjectName(u"groupBox_6")
self.groupBox_3 = QGroupBox(TabOcr_B30) self.gridLayout = QGridLayout(self.groupBox_6)
self.groupBox_3.setObjectName(u"groupBox_3") self.gridLayout.setObjectName(u"gridLayout")
self.verticalLayout_4 = QVBoxLayout(self.groupBox_3) self.label = QLabel(self.groupBox_6)
self.verticalLayout_4.setObjectName(u"verticalLayout_4") self.label.setObjectName(u"label")
self.knnModelSelector = FileSelector(self.groupBox_3) self.label.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
self.knnModelSelector.setObjectName(u"knnModelSelector")
self.verticalLayout_4.addWidget(self.knnModelSelector) self.gridLayout.addWidget(self.label, 0, 0, 1, 1)
self.dependencies_knnModelStatusLabel = QLabel(self.groupBox_6)
self.dependencies_knnModelStatusLabel.setObjectName(u"dependencies_knnModelStatusLabel")
self.dependencies_knnModelStatusLabel.setText(u"...")
self.horizontalLayout.addWidget(self.groupBox_3) self.gridLayout.addWidget(self.dependencies_knnModelStatusLabel, 0, 2, 1, 1)
self.groupBox_5 = QGroupBox(TabOcr_B30) self.line_2 = QFrame(self.groupBox_6)
self.groupBox_5.setObjectName(u"groupBox_5") self.line_2.setObjectName(u"line_2")
self.verticalLayout_6 = QVBoxLayout(self.groupBox_5) self.line_2.setFrameShape(QFrame.VLine)
self.verticalLayout_6.setObjectName(u"verticalLayout_6") self.line_2.setFrameShadow(QFrame.Sunken)
self.b30KnnModelSelector = FileSelector(self.groupBox_5)
self.b30KnnModelSelector.setObjectName(u"b30KnnModelSelector")
self.verticalLayout_6.addWidget(self.b30KnnModelSelector) self.gridLayout.addWidget(self.line_2, 0, 3, 3, 1)
self.label_2 = QLabel(self.groupBox_6)
self.label_2.setObjectName(u"label_2")
self.label_2.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
self.horizontalLayout.addWidget(self.groupBox_5) self.gridLayout.addWidget(self.label_2, 1, 0, 1, 1)
self.dependencies_phashDatabaseStatusLabel = QLabel(self.groupBox_6)
self.dependencies_phashDatabaseStatusLabel.setObjectName(u"dependencies_phashDatabaseStatusLabel")
self.dependencies_phashDatabaseStatusLabel.setText(u"...")
self.verticalLayout_3.addLayout(self.horizontalLayout) self.gridLayout.addWidget(self.dependencies_phashDatabaseStatusLabel, 2, 2, 1, 1)
self.horizontalLayout_3 = QHBoxLayout() self.line = QFrame(self.groupBox_6)
self.horizontalLayout_3.setObjectName(u"horizontalLayout_3") self.line.setObjectName(u"line")
self.groupBox_4 = QGroupBox(TabOcr_B30) self.line.setFrameShape(QFrame.VLine)
self.groupBox_4.setObjectName(u"groupBox_4") self.line.setFrameShadow(QFrame.Sunken)
self.verticalLayout_5 = QVBoxLayout(self.groupBox_4)
self.verticalLayout_5.setObjectName(u"verticalLayout_5")
self.phashDatabaseSelector = FileSelector(self.groupBox_4)
self.phashDatabaseSelector.setObjectName(u"phashDatabaseSelector")
self.verticalLayout_5.addWidget(self.phashDatabaseSelector) self.gridLayout.addWidget(self.line, 0, 1, 3, 1)
self.label_3 = QLabel(self.groupBox_6)
self.label_3.setObjectName(u"label_3")
self.label_3.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
self.horizontalLayout_3.addWidget(self.groupBox_4) self.gridLayout.addWidget(self.label_3, 2, 0, 1, 1)
self.groupBox_2 = QGroupBox(TabOcr_B30) self.dependencies_b30KnnModelStatusLabel = QLabel(self.groupBox_6)
self.groupBox_2.setObjectName(u"groupBox_2") self.dependencies_b30KnnModelStatusLabel.setObjectName(u"dependencies_b30KnnModelStatusLabel")
self.verticalLayout_2 = QVBoxLayout(self.groupBox_2) self.dependencies_b30KnnModelStatusLabel.setText(u"...")
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
self.imageSelector = FileSelector(self.groupBox_2)
self.imageSelector.setObjectName(u"imageSelector")
self.verticalLayout_2.addWidget(self.imageSelector) self.gridLayout.addWidget(self.dependencies_b30KnnModelStatusLabel, 1, 2, 1, 1)
self.dependencies_knnModelSelector = FileSelector(self.groupBox_6)
self.dependencies_knnModelSelector.setObjectName(u"dependencies_knnModelSelector")
self.horizontalLayout_3.addWidget(self.groupBox_2) self.gridLayout.addWidget(self.dependencies_knnModelSelector, 0, 4, 1, 1)
self.dependencies_b30KnnModelSelector = FileSelector(self.groupBox_6)
self.dependencies_b30KnnModelSelector.setObjectName(u"dependencies_b30KnnModelSelector")
self.verticalLayout_3.addLayout(self.horizontalLayout_3) self.gridLayout.addWidget(self.dependencies_b30KnnModelSelector, 1, 4, 1, 1)
self.dependencies_phashDatabaseSelector = FileSelector(self.groupBox_6)
self.dependencies_phashDatabaseSelector.setObjectName(u"dependencies_phashDatabaseSelector")
self.gridLayout.addWidget(self.dependencies_phashDatabaseSelector, 2, 4, 1, 1)
self.gridLayout.setColumnStretch(4, 1)
self.verticalLayout_2.addWidget(self.groupBox_6)
self.ocrQueue = OcrQueue(TabOcr_B30) self.ocrQueue = OcrQueue(TabOcr_B30)
self.ocrQueue.setObjectName(u"ocrQueue") self.ocrQueue.setObjectName(u"ocrQueue")
@ -107,7 +123,7 @@ class Ui_TabOcr_B30(object):
sizePolicy.setHeightForWidth(self.ocrQueue.sizePolicy().hasHeightForWidth()) sizePolicy.setHeightForWidth(self.ocrQueue.sizePolicy().hasHeightForWidth())
self.ocrQueue.setSizePolicy(sizePolicy) self.ocrQueue.setSizePolicy(sizePolicy)
self.verticalLayout_3.addWidget(self.ocrQueue) self.verticalLayout_2.addWidget(self.ocrQueue)
self.retranslateUi(TabOcr_B30) self.retranslateUi(TabOcr_B30)
@ -117,10 +133,10 @@ class Ui_TabOcr_B30(object):
def retranslateUi(self, TabOcr_B30): def retranslateUi(self, TabOcr_B30):
self.groupBox.setTitle(QCoreApplication.translate("TabOcr_B30", u"b30type", None)) self.groupBox.setTitle(QCoreApplication.translate("TabOcr_B30", u"b30type", None))
self.groupBox_3.setTitle(QCoreApplication.translate("TabOcr_B30", u"knnModelSelector.title", None)) self.groupBox_6.setTitle(QCoreApplication.translate("TabOcr_B30", u"dependencies.title", None))
self.groupBox_5.setTitle(QCoreApplication.translate("TabOcr_B30", u"b30KnnModelSelector.title", None)) self.label.setText(QCoreApplication.translate("TabOcr_B30", u"dependencies.knnModel", None))
self.groupBox_4.setTitle(QCoreApplication.translate("TabOcr_B30", u"phashDatabaseSelector.title", None)) self.label_2.setText(QCoreApplication.translate("TabOcr_B30", u"dependencies.b30KnnModel", None))
self.groupBox_2.setTitle(QCoreApplication.translate("TabOcr_B30", u"imageSelector.title", None)) self.label_3.setText(QCoreApplication.translate("TabOcr_B30", u"dependencies.phashDatabase", None))
pass pass
# retranslateUi # retranslateUi

View File

@ -0,0 +1,281 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TabOcr_BuildPHashDatabase</class>
<widget class="QWidget" name="TabOcr_BuildPHashDatabase">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>632</width>
<height>551</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">TabOcr_BuildPHashDatabase</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>folders.title</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>folders.songDir</string>
</property>
</widget>
</item>
<item>
<widget class="FileSelector" name="songDirSelector" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>folders.charIconDir</string>
</property>
</widget>
</item>
<item>
<widget class="FileSelector" name="charIconDirSelector" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>options.title</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string notr="true">hash_size</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="hashSizeSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimum">
<number>2</number>
</property>
<property name="maximum">
<number>64</number>
</property>
<property name="value">
<number>16</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string notr="true">highfreq_factor</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="highfreqFactorSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximum">
<number>32</number>
</property>
<property name="value">
<number>4</number>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="preprocessCharIconCheckBox">
<property name="text">
<string>options.preprocessCharIcon</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="optionsResetButton">
<property name="text">
<string>resetButton</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QProgressBar" name="readImageProgressBar">
<property name="maximum">
<number>0</number>
</property>
<property name="value">
<number>0</number>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="format">
<string>[Reading images] %v/%m - %p%</string>
</property>
</widget>
</item>
<item>
<widget class="QProgressBar" name="calculateHashProgressBar">
<property name="maximum">
<number>0</number>
</property>
<property name="value">
<number>0</number>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="format">
<string>[Calculate hashes] %v/%m - %p%</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="buildButton">
<property name="text">
<string>buildButton</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>FileSelector</class>
<extends>QWidget</extends>
<header>ui.implements.components.fileSelector</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,209 @@
# -*- coding: utf-8 -*-
################################################################################
## Form generated from reading UI file 'tabOcr_BuildPHashDatabase.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, QCheckBox, QGroupBox, QHBoxLayout,
QLabel, QProgressBar, QPushButton, QSizePolicy,
QSpacerItem, QSpinBox, QVBoxLayout, QWidget)
from ui.implements.components.fileSelector import FileSelector
class Ui_TabOcr_BuildPHashDatabase(object):
def setupUi(self, TabOcr_BuildPHashDatabase):
if not TabOcr_BuildPHashDatabase.objectName():
TabOcr_BuildPHashDatabase.setObjectName(u"TabOcr_BuildPHashDatabase")
TabOcr_BuildPHashDatabase.resize(632, 551)
TabOcr_BuildPHashDatabase.setWindowTitle(u"TabOcr_BuildPHashDatabase")
self.verticalLayout_3 = QVBoxLayout(TabOcr_BuildPHashDatabase)
self.verticalLayout_3.setObjectName(u"verticalLayout_3")
self.verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
self.verticalLayout_3.addItem(self.verticalSpacer)
self.groupBox = QGroupBox(TabOcr_BuildPHashDatabase)
self.groupBox.setObjectName(u"groupBox")
self.verticalLayout = QVBoxLayout(self.groupBox)
self.verticalLayout.setObjectName(u"verticalLayout")
self.horizontalLayout = QHBoxLayout()
self.horizontalLayout.setObjectName(u"horizontalLayout")
self.label = QLabel(self.groupBox)
self.label.setObjectName(u"label")
self.horizontalLayout.addWidget(self.label)
self.songDirSelector = FileSelector(self.groupBox)
self.songDirSelector.setObjectName(u"songDirSelector")
sizePolicy = QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.songDirSelector.sizePolicy().hasHeightForWidth())
self.songDirSelector.setSizePolicy(sizePolicy)
self.horizontalLayout.addWidget(self.songDirSelector)
self.verticalLayout.addLayout(self.horizontalLayout)
self.horizontalLayout_2 = QHBoxLayout()
self.horizontalLayout_2.setObjectName(u"horizontalLayout_2")
self.label_2 = QLabel(self.groupBox)
self.label_2.setObjectName(u"label_2")
self.horizontalLayout_2.addWidget(self.label_2)
self.charIconDirSelector = FileSelector(self.groupBox)
self.charIconDirSelector.setObjectName(u"charIconDirSelector")
sizePolicy.setHeightForWidth(self.charIconDirSelector.sizePolicy().hasHeightForWidth())
self.charIconDirSelector.setSizePolicy(sizePolicy)
self.horizontalLayout_2.addWidget(self.charIconDirSelector)
self.verticalLayout.addLayout(self.horizontalLayout_2)
self.verticalLayout_3.addWidget(self.groupBox)
self.groupBox_2 = QGroupBox(TabOcr_BuildPHashDatabase)
self.groupBox_2.setObjectName(u"groupBox_2")
self.horizontalLayout_3 = QHBoxLayout(self.groupBox_2)
self.horizontalLayout_3.setObjectName(u"horizontalLayout_3")
self.verticalLayout_2 = QVBoxLayout()
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
self.horizontalLayout_6 = QHBoxLayout()
self.horizontalLayout_6.setObjectName(u"horizontalLayout_6")
self.label_3 = QLabel(self.groupBox_2)
self.label_3.setObjectName(u"label_3")
self.label_3.setText(u"hash_size")
self.label_3.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
self.horizontalLayout_6.addWidget(self.label_3)
self.hashSizeSpinBox = QSpinBox(self.groupBox_2)
self.hashSizeSpinBox.setObjectName(u"hashSizeSpinBox")
sizePolicy1 = QSizePolicy(QSizePolicy.Maximum, QSizePolicy.Fixed)
sizePolicy1.setHorizontalStretch(0)
sizePolicy1.setVerticalStretch(0)
sizePolicy1.setHeightForWidth(self.hashSizeSpinBox.sizePolicy().hasHeightForWidth())
self.hashSizeSpinBox.setSizePolicy(sizePolicy1)
self.hashSizeSpinBox.setMinimum(2)
self.hashSizeSpinBox.setMaximum(64)
self.hashSizeSpinBox.setValue(16)
self.horizontalLayout_6.addWidget(self.hashSizeSpinBox)
self.verticalLayout_2.addLayout(self.horizontalLayout_6)
self.horizontalLayout_7 = QHBoxLayout()
self.horizontalLayout_7.setObjectName(u"horizontalLayout_7")
self.label_4 = QLabel(self.groupBox_2)
self.label_4.setObjectName(u"label_4")
self.label_4.setText(u"highfreq_factor")
self.label_4.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
self.horizontalLayout_7.addWidget(self.label_4)
self.highfreqFactorSpinBox = QSpinBox(self.groupBox_2)
self.highfreqFactorSpinBox.setObjectName(u"highfreqFactorSpinBox")
sizePolicy1.setHeightForWidth(self.highfreqFactorSpinBox.sizePolicy().hasHeightForWidth())
self.highfreqFactorSpinBox.setSizePolicy(sizePolicy1)
self.highfreqFactorSpinBox.setMaximum(32)
self.highfreqFactorSpinBox.setValue(4)
self.horizontalLayout_7.addWidget(self.highfreqFactorSpinBox)
self.verticalLayout_2.addLayout(self.horizontalLayout_7)
self.horizontalLayout_3.addLayout(self.verticalLayout_2)
self.preprocessCharIconCheckBox = QCheckBox(self.groupBox_2)
self.preprocessCharIconCheckBox.setObjectName(u"preprocessCharIconCheckBox")
self.preprocessCharIconCheckBox.setChecked(True)
self.horizontalLayout_3.addWidget(self.preprocessCharIconCheckBox)
self.horizontalSpacer_3 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
self.horizontalLayout_3.addItem(self.horizontalSpacer_3)
self.optionsResetButton = QPushButton(self.groupBox_2)
self.optionsResetButton.setObjectName(u"optionsResetButton")
self.horizontalLayout_3.addWidget(self.optionsResetButton)
self.verticalLayout_3.addWidget(self.groupBox_2)
self.readImageProgressBar = QProgressBar(TabOcr_BuildPHashDatabase)
self.readImageProgressBar.setObjectName(u"readImageProgressBar")
self.readImageProgressBar.setMaximum(0)
self.readImageProgressBar.setValue(0)
self.readImageProgressBar.setAlignment(Qt.AlignCenter)
self.verticalLayout_3.addWidget(self.readImageProgressBar)
self.calculateHashProgressBar = QProgressBar(TabOcr_BuildPHashDatabase)
self.calculateHashProgressBar.setObjectName(u"calculateHashProgressBar")
self.calculateHashProgressBar.setMaximum(0)
self.calculateHashProgressBar.setValue(0)
self.calculateHashProgressBar.setAlignment(Qt.AlignCenter)
self.verticalLayout_3.addWidget(self.calculateHashProgressBar)
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.buildButton = QPushButton(TabOcr_BuildPHashDatabase)
self.buildButton.setObjectName(u"buildButton")
self.horizontalLayout_5.addWidget(self.buildButton)
self.horizontalSpacer_2 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
self.horizontalLayout_5.addItem(self.horizontalSpacer_2)
self.verticalLayout_3.addLayout(self.horizontalLayout_5)
self.verticalSpacer_2 = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
self.verticalLayout_3.addItem(self.verticalSpacer_2)
self.retranslateUi(TabOcr_BuildPHashDatabase)
QMetaObject.connectSlotsByName(TabOcr_BuildPHashDatabase)
# setupUi
def retranslateUi(self, TabOcr_BuildPHashDatabase):
self.groupBox.setTitle(QCoreApplication.translate("TabOcr_BuildPHashDatabase", u"folders.title", None))
self.label.setText(QCoreApplication.translate("TabOcr_BuildPHashDatabase", u"folders.songDir", None))
self.label_2.setText(QCoreApplication.translate("TabOcr_BuildPHashDatabase", u"folders.charIconDir", None))
self.groupBox_2.setTitle(QCoreApplication.translate("TabOcr_BuildPHashDatabase", u"options.title", None))
self.preprocessCharIconCheckBox.setText(QCoreApplication.translate("TabOcr_BuildPHashDatabase", u"options.preprocessCharIcon", None))
self.optionsResetButton.setText(QCoreApplication.translate("TabOcr_BuildPHashDatabase", u"resetButton", None))
self.readImageProgressBar.setFormat(QCoreApplication.translate("TabOcr_BuildPHashDatabase", u"[Reading images] %v/%m - %p%", None))
self.calculateHashProgressBar.setFormat(QCoreApplication.translate("TabOcr_BuildPHashDatabase", u"[Calculate hashes] %v/%m - %p%", None))
self.buildButton.setText(QCoreApplication.translate("TabOcr_BuildPHashDatabase", u"buildButton", None))
pass
# retranslateUi

View File

@ -24,130 +24,249 @@
<item> <item>
<widget class="QGroupBox" name="groupBox"> <widget class="QGroupBox" name="groupBox">
<property name="title"> <property name="title">
<string>deviceSelector.title</string> <string>options.title</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QHBoxLayout" name="horizontalLayout" stretch="0,0,0">
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<item> <item>
<widget class="QCheckBox" name="deviceUseAutoFactorCheckBox"> <widget class="QCheckBox" name="options_usePresetCheckBox">
<property name="text"> <property name="text">
<string>deviceSelector.useAutoFactor</string> <string>options.usePreset</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QCheckBox" name="deviceSizesV2CheckBox"> <widget class="QComboBox" name="options_presetComboBox">
<property name="text"> <property name="enabled">
<string notr="true">SizesV2</string> <bool>false</bool>
</property> </property>
</widget> </widget>
</item> </item>
</layout> </layout>
</item> </item>
<item> <item>
<widget class="FileSelector" name="deviceFileSelector" native="true"/> <widget class="Line" name="line_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item> </item>
<item> <item>
<widget class="DevicesComboBox" name="deviceComboBox"/> <widget class="QWidget" name="options_preciseControlWidget" native="true">
<layout class="QGridLayout" name="gridLayout_2" columnstretch="0,1,0">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>options.rois</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QStackedWidget" name="options_roisStackedWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="page">
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QComboBox" name="options_roisComboBox"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="page_2">
<layout class="QVBoxLayout" name="verticalLayout_4">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="FileSelector" name="options_roisCustomSelector" native="true"/>
</item>
</layout>
</widget>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>options.masker</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QCheckBox" name="options_roisUseCustomCheckBox">
<property name="text">
<string>options.useCustom</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QCheckBox" name="options_maskerUseCustomCheckBox">
<property name="text">
<string>options.useCustom</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QStackedWidget" name="options_maskerStackedWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="page_3">
<layout class="QVBoxLayout" name="verticalLayout_5">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QComboBox" name="options_maskerComboBox"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="page_4">
<layout class="QVBoxLayout" name="verticalLayout_6">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="FileSelector" name="options_maskerCustomSelector" native="true"/>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</item> </item>
</layout> </layout>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QWidget" name="horizontalWidget" native="true"> <widget class="QGroupBox" name="groupBox_2">
<property name="sizePolicy"> <property name="title">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <string>dependencies.title</string>
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property> </property>
<layout class="QHBoxLayout" name="horizontalLayout_2"> <layout class="QGridLayout" name="gridLayout" columnstretch="0,0,0,0,1">
<property name="leftMargin"> <item row="0" column="0">
<number>0</number> <widget class="QLabel" name="label_3">
</property> <property name="text">
<property name="topMargin"> <string>dependencies.knnModel</string>
<number>0</number> </property>
</property> <property name="alignment">
<property name="rightMargin"> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QGroupBox" name="groupBox_6">
<property name="title">
<string>knnModelSelector.title</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="FileSelector" name="knnModelSelector" native="true"/>
</item>
</layout>
</widget> </widget>
</item> </item>
<item> <item row="0" column="2">
<widget class="QStackedWidget" name="deviceDependenciesStackedWidget"> <widget class="QLabel" name="dependencies_knnModelStatusLabel">
<property name="currentIndex"> <property name="text">
<number>0</number> <string notr="true">...</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="dependencies_phashDatabaseStatusLabel">
<property name="text">
<string notr="true">...</string>
</property>
</widget>
</item>
<item row="1" column="4">
<widget class="FileSelector" name="dependencies_phashDatabaseSelector" native="true"/>
</item>
<item row="0" column="1" rowspan="2">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item row="0" column="4">
<widget class="FileSelector" name="dependencies_knnModelSelector" native="true"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>dependencies.phashDatabase</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="3" rowspan="2">
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property> </property>
<widget class="QWidget" name="deviceV1">
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QGroupBox" name="groupBox_4">
<property name="title">
<string>tesseractSelector.title</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="FileSelector" name="tesseractFileSelector" native="true"/>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="deviceV2">
<layout class="QVBoxLayout" name="verticalLayout_4">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QGroupBox" name="groupBox_5">
<property name="title">
<string>phashDatabaseSelector.title</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_7">
<item>
<widget class="FileSelector" name="phashDatabaseSelector" native="true"/>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget> </widget>
</item> </item>
</layout> </layout>
@ -178,12 +297,24 @@
<header>ui.implements.components.ocrQueue</header> <header>ui.implements.components.ocrQueue</header>
<container>1</container> <container>1</container>
</customwidget> </customwidget>
<customwidget>
<class>DevicesComboBox</class>
<extends>QComboBox</extends>
<header>ui.implements.components.devicesComboBox</header>
</customwidget>
</customwidgets> </customwidgets>
<resources/> <resources/>
<connections/> <connections>
<connection>
<sender>options_usePresetCheckBox</sender>
<signal>toggled(bool)</signal>
<receiver>options_presetComboBox</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>82</x>
<y>111</y>
</hint>
<hint type="destinationlabel">
<x>82</x>
<y>175</y>
</hint>
</hints>
</connection>
</connections>
</ui> </ui>

View File

@ -15,11 +15,11 @@ from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
QFont, QFontDatabase, QGradient, QIcon, QFont, QFontDatabase, QGradient, QIcon,
QImage, QKeySequence, QLinearGradient, QPainter, QImage, QKeySequence, QLinearGradient, QPainter,
QPalette, QPixmap, QRadialGradient, QTransform) QPalette, QPixmap, QRadialGradient, QTransform)
from PySide6.QtWidgets import (QApplication, QCheckBox, QGroupBox, QHBoxLayout, from PySide6.QtWidgets import (QApplication, QCheckBox, QComboBox, QFrame,
QGridLayout, QGroupBox, QHBoxLayout, QLabel,
QPushButton, QSizePolicy, QStackedWidget, QVBoxLayout, QPushButton, QSizePolicy, QStackedWidget, QVBoxLayout,
QWidget) QWidget)
from ui.implements.components.devicesComboBox import DevicesComboBox
from ui.implements.components.fileSelector import FileSelector from ui.implements.components.fileSelector import FileSelector
from ui.implements.components.ocrQueue import OcrQueue from ui.implements.components.ocrQueue import OcrQueue
@ -38,102 +38,181 @@ class Ui_TabOcr_Device(object):
self.groupBox = QGroupBox(TabOcr_Device) self.groupBox = QGroupBox(TabOcr_Device)
self.groupBox.setObjectName(u"groupBox") self.groupBox.setObjectName(u"groupBox")
self.verticalLayout = QVBoxLayout(self.groupBox) self.horizontalLayout = QHBoxLayout(self.groupBox)
self.verticalLayout.setObjectName(u"verticalLayout")
self.horizontalLayout = QHBoxLayout()
self.horizontalLayout.setObjectName(u"horizontalLayout") self.horizontalLayout.setObjectName(u"horizontalLayout")
self.deviceUseAutoFactorCheckBox = QCheckBox(self.groupBox) self.verticalLayout = QVBoxLayout()
self.deviceUseAutoFactorCheckBox.setObjectName(u"deviceUseAutoFactorCheckBox") self.verticalLayout.setObjectName(u"verticalLayout")
self.options_usePresetCheckBox = QCheckBox(self.groupBox)
self.options_usePresetCheckBox.setObjectName(u"options_usePresetCheckBox")
self.horizontalLayout.addWidget(self.deviceUseAutoFactorCheckBox) self.verticalLayout.addWidget(self.options_usePresetCheckBox)
self.deviceSizesV2CheckBox = QCheckBox(self.groupBox) self.options_presetComboBox = QComboBox(self.groupBox)
self.deviceSizesV2CheckBox.setObjectName(u"deviceSizesV2CheckBox") self.options_presetComboBox.setObjectName(u"options_presetComboBox")
self.deviceSizesV2CheckBox.setText(u"SizesV2") self.options_presetComboBox.setEnabled(False)
self.horizontalLayout.addWidget(self.deviceSizesV2CheckBox) self.verticalLayout.addWidget(self.options_presetComboBox)
self.verticalLayout.addLayout(self.horizontalLayout) self.horizontalLayout.addLayout(self.verticalLayout)
self.deviceFileSelector = FileSelector(self.groupBox) self.line_3 = QFrame(self.groupBox)
self.deviceFileSelector.setObjectName(u"deviceFileSelector") self.line_3.setObjectName(u"line_3")
self.line_3.setFrameShape(QFrame.VLine)
self.line_3.setFrameShadow(QFrame.Sunken)
self.verticalLayout.addWidget(self.deviceFileSelector) self.horizontalLayout.addWidget(self.line_3)
self.deviceComboBox = DevicesComboBox(self.groupBox) self.options_preciseControlWidget = QWidget(self.groupBox)
self.deviceComboBox.setObjectName(u"deviceComboBox") self.options_preciseControlWidget.setObjectName(u"options_preciseControlWidget")
self.gridLayout_2 = QGridLayout(self.options_preciseControlWidget)
self.gridLayout_2.setObjectName(u"gridLayout_2")
self.gridLayout_2.setContentsMargins(0, 0, 0, 0)
self.label = QLabel(self.options_preciseControlWidget)
self.label.setObjectName(u"label")
self.label.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
self.verticalLayout.addWidget(self.deviceComboBox) self.gridLayout_2.addWidget(self.label, 0, 0, 1, 1)
self.options_roisStackedWidget = QStackedWidget(self.options_preciseControlWidget)
self.options_roisStackedWidget.setObjectName(u"options_roisStackedWidget")
sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Maximum)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.options_roisStackedWidget.sizePolicy().hasHeightForWidth())
self.options_roisStackedWidget.setSizePolicy(sizePolicy)
self.page = QWidget()
self.page.setObjectName(u"page")
self.verticalLayout_2 = QVBoxLayout(self.page)
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)
self.options_roisComboBox = QComboBox(self.page)
self.options_roisComboBox.setObjectName(u"options_roisComboBox")
self.verticalLayout_2.addWidget(self.options_roisComboBox)
self.options_roisStackedWidget.addWidget(self.page)
self.page_2 = QWidget()
self.page_2.setObjectName(u"page_2")
self.verticalLayout_4 = QVBoxLayout(self.page_2)
self.verticalLayout_4.setObjectName(u"verticalLayout_4")
self.verticalLayout_4.setContentsMargins(0, 0, 0, 0)
self.options_roisCustomSelector = FileSelector(self.page_2)
self.options_roisCustomSelector.setObjectName(u"options_roisCustomSelector")
self.verticalLayout_4.addWidget(self.options_roisCustomSelector)
self.options_roisStackedWidget.addWidget(self.page_2)
self.gridLayout_2.addWidget(self.options_roisStackedWidget, 0, 1, 1, 1)
self.label_2 = QLabel(self.options_preciseControlWidget)
self.label_2.setObjectName(u"label_2")
self.label_2.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
self.gridLayout_2.addWidget(self.label_2, 1, 0, 1, 1)
self.options_roisUseCustomCheckBox = QCheckBox(self.options_preciseControlWidget)
self.options_roisUseCustomCheckBox.setObjectName(u"options_roisUseCustomCheckBox")
self.gridLayout_2.addWidget(self.options_roisUseCustomCheckBox, 0, 2, 1, 1)
self.options_maskerUseCustomCheckBox = QCheckBox(self.options_preciseControlWidget)
self.options_maskerUseCustomCheckBox.setObjectName(u"options_maskerUseCustomCheckBox")
self.gridLayout_2.addWidget(self.options_maskerUseCustomCheckBox, 1, 2, 1, 1)
self.options_maskerStackedWidget = QStackedWidget(self.options_preciseControlWidget)
self.options_maskerStackedWidget.setObjectName(u"options_maskerStackedWidget")
sizePolicy.setHeightForWidth(self.options_maskerStackedWidget.sizePolicy().hasHeightForWidth())
self.options_maskerStackedWidget.setSizePolicy(sizePolicy)
self.page_3 = QWidget()
self.page_3.setObjectName(u"page_3")
self.verticalLayout_5 = QVBoxLayout(self.page_3)
self.verticalLayout_5.setObjectName(u"verticalLayout_5")
self.verticalLayout_5.setContentsMargins(0, 0, 0, 0)
self.options_maskerComboBox = QComboBox(self.page_3)
self.options_maskerComboBox.setObjectName(u"options_maskerComboBox")
self.verticalLayout_5.addWidget(self.options_maskerComboBox)
self.options_maskerStackedWidget.addWidget(self.page_3)
self.page_4 = QWidget()
self.page_4.setObjectName(u"page_4")
self.verticalLayout_6 = QVBoxLayout(self.page_4)
self.verticalLayout_6.setObjectName(u"verticalLayout_6")
self.verticalLayout_6.setContentsMargins(0, 0, 0, 0)
self.options_maskerCustomSelector = FileSelector(self.page_4)
self.options_maskerCustomSelector.setObjectName(u"options_maskerCustomSelector")
self.verticalLayout_6.addWidget(self.options_maskerCustomSelector)
self.options_maskerStackedWidget.addWidget(self.page_4)
self.gridLayout_2.addWidget(self.options_maskerStackedWidget, 1, 1, 1, 1)
self.gridLayout_2.setColumnStretch(1, 1)
self.horizontalLayout.addWidget(self.options_preciseControlWidget)
self.verticalLayout_3.addWidget(self.groupBox) self.verticalLayout_3.addWidget(self.groupBox)
self.horizontalWidget = QWidget(TabOcr_Device) self.groupBox_2 = QGroupBox(TabOcr_Device)
self.horizontalWidget.setObjectName(u"horizontalWidget") self.groupBox_2.setObjectName(u"groupBox_2")
sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Maximum) self.gridLayout = QGridLayout(self.groupBox_2)
sizePolicy.setHorizontalStretch(0) self.gridLayout.setObjectName(u"gridLayout")
sizePolicy.setVerticalStretch(0) self.label_3 = QLabel(self.groupBox_2)
sizePolicy.setHeightForWidth(self.horizontalWidget.sizePolicy().hasHeightForWidth()) self.label_3.setObjectName(u"label_3")
self.horizontalWidget.setSizePolicy(sizePolicy) self.label_3.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
self.horizontalLayout_2 = QHBoxLayout(self.horizontalWidget)
self.horizontalLayout_2.setObjectName(u"horizontalLayout_2")
self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
self.groupBox_6 = QGroupBox(self.horizontalWidget)
self.groupBox_6.setObjectName(u"groupBox_6")
self.verticalLayout_6 = QVBoxLayout(self.groupBox_6)
self.verticalLayout_6.setObjectName(u"verticalLayout_6")
self.knnModelSelector = FileSelector(self.groupBox_6)
self.knnModelSelector.setObjectName(u"knnModelSelector")
self.verticalLayout_6.addWidget(self.knnModelSelector) self.gridLayout.addWidget(self.label_3, 0, 0, 1, 1)
self.dependencies_knnModelStatusLabel = QLabel(self.groupBox_2)
self.dependencies_knnModelStatusLabel.setObjectName(u"dependencies_knnModelStatusLabel")
self.dependencies_knnModelStatusLabel.setText(u"...")
self.horizontalLayout_2.addWidget(self.groupBox_6) self.gridLayout.addWidget(self.dependencies_knnModelStatusLabel, 0, 2, 1, 1)
self.deviceDependenciesStackedWidget = QStackedWidget(self.horizontalWidget) self.dependencies_phashDatabaseStatusLabel = QLabel(self.groupBox_2)
self.deviceDependenciesStackedWidget.setObjectName(u"deviceDependenciesStackedWidget") self.dependencies_phashDatabaseStatusLabel.setObjectName(u"dependencies_phashDatabaseStatusLabel")
self.deviceV1 = QWidget() self.dependencies_phashDatabaseStatusLabel.setText(u"...")
self.deviceV1.setObjectName(u"deviceV1")
self.verticalLayout_2 = QVBoxLayout(self.deviceV1)
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)
self.groupBox_4 = QGroupBox(self.deviceV1)
self.groupBox_4.setObjectName(u"groupBox_4")
self.verticalLayout_5 = QVBoxLayout(self.groupBox_4)
self.verticalLayout_5.setObjectName(u"verticalLayout_5")
self.tesseractFileSelector = FileSelector(self.groupBox_4)
self.tesseractFileSelector.setObjectName(u"tesseractFileSelector")
self.verticalLayout_5.addWidget(self.tesseractFileSelector) self.gridLayout.addWidget(self.dependencies_phashDatabaseStatusLabel, 1, 2, 1, 1)
self.dependencies_phashDatabaseSelector = FileSelector(self.groupBox_2)
self.dependencies_phashDatabaseSelector.setObjectName(u"dependencies_phashDatabaseSelector")
self.verticalLayout_2.addWidget(self.groupBox_4) self.gridLayout.addWidget(self.dependencies_phashDatabaseSelector, 1, 4, 1, 1)
self.deviceDependenciesStackedWidget.addWidget(self.deviceV1) self.line = QFrame(self.groupBox_2)
self.deviceV2 = QWidget() self.line.setObjectName(u"line")
self.deviceV2.setObjectName(u"deviceV2") self.line.setFrameShape(QFrame.VLine)
self.verticalLayout_4 = QVBoxLayout(self.deviceV2) self.line.setFrameShadow(QFrame.Sunken)
self.verticalLayout_4.setObjectName(u"verticalLayout_4")
self.verticalLayout_4.setContentsMargins(0, 0, 0, 0)
self.groupBox_5 = QGroupBox(self.deviceV2)
self.groupBox_5.setObjectName(u"groupBox_5")
self.verticalLayout_7 = QVBoxLayout(self.groupBox_5)
self.verticalLayout_7.setObjectName(u"verticalLayout_7")
self.phashDatabaseSelector = FileSelector(self.groupBox_5)
self.phashDatabaseSelector.setObjectName(u"phashDatabaseSelector")
self.verticalLayout_7.addWidget(self.phashDatabaseSelector) self.gridLayout.addWidget(self.line, 0, 1, 2, 1)
self.dependencies_knnModelSelector = FileSelector(self.groupBox_2)
self.dependencies_knnModelSelector.setObjectName(u"dependencies_knnModelSelector")
self.verticalLayout_4.addWidget(self.groupBox_5) self.gridLayout.addWidget(self.dependencies_knnModelSelector, 0, 4, 1, 1)
self.deviceDependenciesStackedWidget.addWidget(self.deviceV2) self.label_4 = QLabel(self.groupBox_2)
self.label_4.setObjectName(u"label_4")
self.label_4.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
self.horizontalLayout_2.addWidget(self.deviceDependenciesStackedWidget) self.gridLayout.addWidget(self.label_4, 1, 0, 1, 1)
self.line_2 = QFrame(self.groupBox_2)
self.line_2.setObjectName(u"line_2")
self.line_2.setFrameShape(QFrame.VLine)
self.line_2.setFrameShadow(QFrame.Sunken)
self.verticalLayout_3.addWidget(self.horizontalWidget) self.gridLayout.addWidget(self.line_2, 0, 3, 2, 1)
self.gridLayout.setColumnStretch(4, 1)
self.verticalLayout_3.addWidget(self.groupBox_2)
self.ocrQueue = OcrQueue(TabOcr_Device) self.ocrQueue = OcrQueue(TabOcr_Device)
self.ocrQueue.setObjectName(u"ocrQueue") self.ocrQueue.setObjectName(u"ocrQueue")
@ -147,8 +226,10 @@ class Ui_TabOcr_Device(object):
self.retranslateUi(TabOcr_Device) self.retranslateUi(TabOcr_Device)
self.options_usePresetCheckBox.toggled.connect(self.options_presetComboBox.setEnabled)
self.deviceDependenciesStackedWidget.setCurrentIndex(0) self.options_roisStackedWidget.setCurrentIndex(0)
self.options_maskerStackedWidget.setCurrentIndex(0)
QMetaObject.connectSlotsByName(TabOcr_Device) QMetaObject.connectSlotsByName(TabOcr_Device)
@ -156,11 +237,15 @@ class Ui_TabOcr_Device(object):
def retranslateUi(self, TabOcr_Device): def retranslateUi(self, TabOcr_Device):
self.openWizardButton.setText(QCoreApplication.translate("TabOcr_Device", u"openWizardButton", None)) self.openWizardButton.setText(QCoreApplication.translate("TabOcr_Device", u"openWizardButton", None))
self.groupBox.setTitle(QCoreApplication.translate("TabOcr_Device", u"deviceSelector.title", None)) self.groupBox.setTitle(QCoreApplication.translate("TabOcr_Device", u"options.title", None))
self.deviceUseAutoFactorCheckBox.setText(QCoreApplication.translate("TabOcr_Device", u"deviceSelector.useAutoFactor", None)) self.options_usePresetCheckBox.setText(QCoreApplication.translate("TabOcr_Device", u"options.usePreset", None))
self.groupBox_6.setTitle(QCoreApplication.translate("TabOcr_Device", u"knnModelSelector.title", None)) self.label.setText(QCoreApplication.translate("TabOcr_Device", u"options.rois", None))
self.groupBox_4.setTitle(QCoreApplication.translate("TabOcr_Device", u"tesseractSelector.title", None)) self.label_2.setText(QCoreApplication.translate("TabOcr_Device", u"options.masker", None))
self.groupBox_5.setTitle(QCoreApplication.translate("TabOcr_Device", u"phashDatabaseSelector.title", None)) self.options_roisUseCustomCheckBox.setText(QCoreApplication.translate("TabOcr_Device", u"options.useCustom", None))
self.options_maskerUseCustomCheckBox.setText(QCoreApplication.translate("TabOcr_Device", u"options.useCustom", None))
self.groupBox_2.setTitle(QCoreApplication.translate("TabOcr_Device", u"dependencies.title", None))
self.label_3.setText(QCoreApplication.translate("TabOcr_Device", u"dependencies.knnModel", None))
self.label_4.setText(QCoreApplication.translate("TabOcr_Device", u"dependencies.phashDatabase", None))
pass pass
# retranslateUi # retranslateUi

View File

@ -29,6 +29,11 @@
<string>tab.b30</string> <string>tab.b30</string>
</attribute> </attribute>
</widget> </widget>
<widget class="TabOcr_BuildPHashDatabase" name="tab_3">
<attribute name="title">
<string>tab.buildPHashDatabase</string>
</attribute>
</widget>
</widget> </widget>
</item> </item>
</layout> </layout>
@ -46,6 +51,12 @@
<header>ui.implements.tabs.tabOcr.tabOcr_B30</header> <header>ui.implements.tabs.tabOcr.tabOcr_B30</header>
<container>1</container> <container>1</container>
</customwidget> </customwidget>
<customwidget>
<class>TabOcr_BuildPHashDatabase</class>
<extends>QWidget</extends>
<header>ui.implements.tabs.tabOcr.tabOcr_BuildPHashDatabase</header>
<container>1</container>
</customwidget>
</customwidgets> </customwidgets>
<resources/> <resources/>
<connections/> <connections/>

View File

@ -3,7 +3,7 @@
################################################################################ ################################################################################
## Form generated from reading UI file 'tabOcrEntry.ui' ## Form generated from reading UI file 'tabOcrEntry.ui'
## ##
## Created by: Qt User Interface Compiler version 6.5.1 ## Created by: Qt User Interface Compiler version 6.5.2
## ##
## 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!
################################################################################ ################################################################################
@ -19,6 +19,7 @@ from PySide6.QtWidgets import (QApplication, QSizePolicy, QTabWidget, QVBoxLayou
QWidget) QWidget)
from ui.implements.tabs.tabOcr.tabOcr_B30 import TabOcr_B30 from ui.implements.tabs.tabOcr.tabOcr_B30 import TabOcr_B30
from ui.implements.tabs.tabOcr.tabOcr_BuildPHashDatabase import TabOcr_BuildPHashDatabase
from ui.implements.tabs.tabOcr.tabOcr_Device import TabOcr_Device from ui.implements.tabs.tabOcr.tabOcr_Device import TabOcr_Device
class Ui_TabOcrEntry(object): class Ui_TabOcrEntry(object):
@ -37,6 +38,9 @@ class Ui_TabOcrEntry(object):
self.tab_2 = TabOcr_B30() self.tab_2 = TabOcr_B30()
self.tab_2.setObjectName(u"tab_2") self.tab_2.setObjectName(u"tab_2")
self.tabWidget.addTab(self.tab_2, "") self.tabWidget.addTab(self.tab_2, "")
self.tab_3 = TabOcr_BuildPHashDatabase()
self.tab_3.setObjectName(u"tab_3")
self.tabWidget.addTab(self.tab_3, "")
self.verticalLayout.addWidget(self.tabWidget) self.verticalLayout.addWidget(self.tabWidget)
@ -52,6 +56,7 @@ class Ui_TabOcrEntry(object):
def retranslateUi(self, TabOcrEntry): def retranslateUi(self, TabOcrEntry):
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), QCoreApplication.translate("TabOcrEntry", u"tab.device", None)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), QCoreApplication.translate("TabOcrEntry", u"tab.device", None))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), QCoreApplication.translate("TabOcrEntry", u"tab.b30", None)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), QCoreApplication.translate("TabOcrEntry", u"tab.b30", None))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_3), QCoreApplication.translate("TabOcrEntry", u"tab.buildPHashDatabase", None))
pass pass
# retranslateUi # retranslateUi

View File

@ -6,113 +6,15 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>668</width> <width>616</width>
<height>546</height> <height>500</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string notr="true">TabTools_ChartRecommend</string> <string notr="true">TabTools_ChartRecommend</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QGridLayout" name="gridLayout">
<item> <item row="0" column="1">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>constantRangeFromPlayRating</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QDoubleSpinBox" name="rangeFromPlayRating_playRatingSpinBox">
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="maximum">
<double>100.000000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="2">
<widget class="QLabel" name="label_3">
<property name="text">
<string notr="true">AA</string>
</property>
<property name="alignment">
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_2">
<property name="text">
<string notr="true">EX</string>
</property>
<property name="alignment">
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string notr="true">EX+</string>
</property>
<property name="alignment">
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="rangeFromPlayRating_ExPlusLabel">
<property name="text">
<string notr="true">...</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="rangeFromPlayRating_ExLabel">
<property name="text">
<string notr="true">...</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="rangeFromPlayRating_AaLabel">
<property name="text">
<string notr="true">...</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2"> <widget class="QGroupBox" name="groupBox_2">
<property name="title"> <property name="title">
<string>chartsByConstant</string> <string>chartsByConstant</string>
@ -147,51 +49,197 @@
</item> </item>
<item> <item>
<widget class="QLabel" name="chartsByConstant_numLabel"> <widget class="QLabel" name="chartsByConstant_numLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text"> <property name="text">
<string notr="true">...</string> <string notr="true">...</string>
</property> </property>
</widget> </widget>
</item> </item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="chartsByConstant_refreshButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>refreshButton</string>
</property>
</widget>
</item>
</layout> </layout>
</item> </item>
<item> <item>
<widget class="QWidget" name="widget" native="true"> <widget class="QListView" name="chartsByConstant_modelView">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding"> <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch> <horstretch>0</horstretch>
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<layout class="QGridLayout" name="chartsByConstant_gridLayout"/> <property name="minimumSize">
<size>
<width>150</width>
<height>0</height>
</size>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::NoSelection</enum>
</property>
<property name="verticalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
<property name="horizontalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
</widget> </widget>
</item> </item>
</layout> </layout>
</widget> </widget>
</item> </item>
<item> <item row="0" column="0">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>constantRangeFromPlayRating</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QDoubleSpinBox" name="rangeFromPlayRating_playRatingSpinBox">
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="maximum">
<double>100.000000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
</widget>
</item>
<item>
<layout class="QFormLayout" name="formLayout">
<property name="labelAlignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="formAlignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string notr="true">EX+</string>
</property>
<property name="alignment">
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="rangeFromPlayRating_ExPlusLabel">
<property name="text">
<string notr="true">...</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string notr="true">EX</string>
</property>
<property name="alignment">
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="rangeFromPlayRating_ExLabel">
<property name="text">
<string notr="true">...</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string notr="true">AA</string>
</property>
<property name="alignment">
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="rangeFromPlayRating_AaLabel">
<property name="text">
<string notr="true">...</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string notr="true">A</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="rangeFromPlayRating_ALabel">
<property name="text">
<string notr="true">...</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string notr="true">B</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="rangeFromPlayRating_BLabel">
<property name="text">
<string notr="true">...</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string notr="true">C</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLabel" name="rangeFromPlayRating_CLabel">
<property name="text">
<string notr="true">...</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox_3"> <widget class="QGroupBox" name="groupBox_3">
<property name="title"> <property name="title">
<string>chartsRecommendFromPlayRating</string> <string>chartsRecommendFromPlayRating</string>
@ -246,45 +294,48 @@
</item> </item>
<item> <item>
<widget class="QLabel" name="chartsRecommendFromPlayRating_numLabel"> <widget class="QLabel" name="chartsRecommendFromPlayRating_numLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text"> <property name="text">
<string notr="true">...</string> <string notr="true">...</string>
</property> </property>
</widget> </widget>
</item> </item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="chartsRecommendFromPlayRating_refreshButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>refreshButton</string>
</property>
</widget>
</item>
</layout> </layout>
</item> </item>
<item> <item>
<widget class="QWidget" name="widget_2" native="true"> <widget class="QTableView" name="chartsRecommendFromPlayRating_modelView">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding"> <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch> <horstretch>0</horstretch>
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<layout class="QGridLayout" name="chartsRecommendFromPlayRating_gridLayout"/> <property name="minimumSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="verticalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
<property name="horizontalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
</widget> </widget>
</item> </item>
</layout> </layout>

View File

@ -15,82 +15,19 @@ from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
QFont, QFontDatabase, QGradient, QIcon, QFont, QFontDatabase, QGradient, QIcon,
QImage, QKeySequence, QLinearGradient, QPainter, QImage, QKeySequence, QLinearGradient, QPainter,
QPalette, QPixmap, QRadialGradient, QTransform) QPalette, QPixmap, QRadialGradient, QTransform)
from PySide6.QtWidgets import (QApplication, QDoubleSpinBox, QGridLayout, QGroupBox, from PySide6.QtWidgets import (QAbstractItemView, QApplication, QDoubleSpinBox, QFormLayout,
QHBoxLayout, QLabel, QPushButton, QSizePolicy, QGridLayout, QGroupBox, QHBoxLayout, QHeaderView,
QSpacerItem, QVBoxLayout, QWidget) QLabel, QListView, QSizePolicy, QTableView,
QVBoxLayout, QWidget)
class Ui_TabTools_ChartRecommend(object): class Ui_TabTools_ChartRecommend(object):
def setupUi(self, TabTools_ChartRecommend): def setupUi(self, TabTools_ChartRecommend):
if not TabTools_ChartRecommend.objectName(): if not TabTools_ChartRecommend.objectName():
TabTools_ChartRecommend.setObjectName(u"TabTools_ChartRecommend") TabTools_ChartRecommend.setObjectName(u"TabTools_ChartRecommend")
TabTools_ChartRecommend.resize(668, 546) TabTools_ChartRecommend.resize(616, 500)
TabTools_ChartRecommend.setWindowTitle(u"TabTools_ChartRecommend") TabTools_ChartRecommend.setWindowTitle(u"TabTools_ChartRecommend")
self.verticalLayout = QVBoxLayout(TabTools_ChartRecommend) self.gridLayout = QGridLayout(TabTools_ChartRecommend)
self.verticalLayout.setObjectName(u"verticalLayout") self.gridLayout.setObjectName(u"gridLayout")
self.groupBox = QGroupBox(TabTools_ChartRecommend)
self.groupBox.setObjectName(u"groupBox")
self.verticalLayout_2 = QVBoxLayout(self.groupBox)
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
self.rangeFromPlayRating_playRatingSpinBox = QDoubleSpinBox(self.groupBox)
self.rangeFromPlayRating_playRatingSpinBox.setObjectName(u"rangeFromPlayRating_playRatingSpinBox")
self.rangeFromPlayRating_playRatingSpinBox.setMinimumSize(QSize(100, 0))
self.rangeFromPlayRating_playRatingSpinBox.setMaximumSize(QSize(100, 16777215))
self.rangeFromPlayRating_playRatingSpinBox.setDecimals(3)
self.rangeFromPlayRating_playRatingSpinBox.setMaximum(100.000000000000000)
self.rangeFromPlayRating_playRatingSpinBox.setSingleStep(0.100000000000000)
self.verticalLayout_2.addWidget(self.rangeFromPlayRating_playRatingSpinBox)
self.gridLayout_3 = QGridLayout()
self.gridLayout_3.setObjectName(u"gridLayout_3")
self.label_3 = QLabel(self.groupBox)
self.label_3.setObjectName(u"label_3")
self.label_3.setText(u"AA")
self.label_3.setAlignment(Qt.AlignBottom|Qt.AlignLeading|Qt.AlignLeft)
self.gridLayout_3.addWidget(self.label_3, 0, 2, 1, 1)
self.label_2 = QLabel(self.groupBox)
self.label_2.setObjectName(u"label_2")
self.label_2.setText(u"EX")
self.label_2.setAlignment(Qt.AlignBottom|Qt.AlignLeading|Qt.AlignLeft)
self.gridLayout_3.addWidget(self.label_2, 0, 1, 1, 1)
self.label = QLabel(self.groupBox)
self.label.setObjectName(u"label")
self.label.setText(u"EX+")
self.label.setAlignment(Qt.AlignBottom|Qt.AlignLeading|Qt.AlignLeft)
self.gridLayout_3.addWidget(self.label, 0, 0, 1, 1)
self.rangeFromPlayRating_ExPlusLabel = QLabel(self.groupBox)
self.rangeFromPlayRating_ExPlusLabel.setObjectName(u"rangeFromPlayRating_ExPlusLabel")
self.rangeFromPlayRating_ExPlusLabel.setText(u"...")
self.rangeFromPlayRating_ExPlusLabel.setAlignment(Qt.AlignLeading|Qt.AlignLeft|Qt.AlignTop)
self.gridLayout_3.addWidget(self.rangeFromPlayRating_ExPlusLabel, 1, 0, 1, 1)
self.rangeFromPlayRating_ExLabel = QLabel(self.groupBox)
self.rangeFromPlayRating_ExLabel.setObjectName(u"rangeFromPlayRating_ExLabel")
self.rangeFromPlayRating_ExLabel.setText(u"...")
self.rangeFromPlayRating_ExLabel.setAlignment(Qt.AlignLeading|Qt.AlignLeft|Qt.AlignTop)
self.gridLayout_3.addWidget(self.rangeFromPlayRating_ExLabel, 1, 1, 1, 1)
self.rangeFromPlayRating_AaLabel = QLabel(self.groupBox)
self.rangeFromPlayRating_AaLabel.setObjectName(u"rangeFromPlayRating_AaLabel")
self.rangeFromPlayRating_AaLabel.setText(u"...")
self.rangeFromPlayRating_AaLabel.setAlignment(Qt.AlignLeading|Qt.AlignLeft|Qt.AlignTop)
self.gridLayout_3.addWidget(self.rangeFromPlayRating_AaLabel, 1, 2, 1, 1)
self.verticalLayout_2.addLayout(self.gridLayout_3)
self.verticalLayout.addWidget(self.groupBox)
self.groupBox_2 = QGroupBox(TabTools_ChartRecommend) self.groupBox_2 = QGroupBox(TabTools_ChartRecommend)
self.groupBox_2.setObjectName(u"groupBox_2") self.groupBox_2.setObjectName(u"groupBox_2")
self.verticalLayout_3 = QVBoxLayout(self.groupBox_2) self.verticalLayout_3 = QVBoxLayout(self.groupBox_2)
@ -109,37 +46,137 @@ class Ui_TabTools_ChartRecommend(object):
self.chartsByConstant_numLabel = QLabel(self.groupBox_2) self.chartsByConstant_numLabel = QLabel(self.groupBox_2)
self.chartsByConstant_numLabel.setObjectName(u"chartsByConstant_numLabel") self.chartsByConstant_numLabel.setObjectName(u"chartsByConstant_numLabel")
sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.chartsByConstant_numLabel.sizePolicy().hasHeightForWidth())
self.chartsByConstant_numLabel.setSizePolicy(sizePolicy)
self.chartsByConstant_numLabel.setText(u"...") self.chartsByConstant_numLabel.setText(u"...")
self.horizontalLayout_3.addWidget(self.chartsByConstant_numLabel) self.horizontalLayout_3.addWidget(self.chartsByConstant_numLabel)
self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
self.horizontalLayout_3.addItem(self.horizontalSpacer)
self.chartsByConstant_refreshButton = QPushButton(self.groupBox_2)
self.chartsByConstant_refreshButton.setObjectName(u"chartsByConstant_refreshButton")
self.chartsByConstant_refreshButton.setEnabled(False)
self.horizontalLayout_3.addWidget(self.chartsByConstant_refreshButton)
self.verticalLayout_3.addLayout(self.horizontalLayout_3) self.verticalLayout_3.addLayout(self.horizontalLayout_3)
self.widget = QWidget(self.groupBox_2) self.chartsByConstant_modelView = QListView(self.groupBox_2)
self.widget.setObjectName(u"widget") self.chartsByConstant_modelView.setObjectName(u"chartsByConstant_modelView")
sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding) sizePolicy1 = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0) sizePolicy1.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0) sizePolicy1.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.widget.sizePolicy().hasHeightForWidth()) sizePolicy1.setHeightForWidth(self.chartsByConstant_modelView.sizePolicy().hasHeightForWidth())
self.widget.setSizePolicy(sizePolicy) self.chartsByConstant_modelView.setSizePolicy(sizePolicy1)
self.chartsByConstant_gridLayout = QGridLayout(self.widget) self.chartsByConstant_modelView.setMinimumSize(QSize(150, 0))
self.chartsByConstant_gridLayout.setObjectName(u"chartsByConstant_gridLayout") self.chartsByConstant_modelView.setEditTriggers(QAbstractItemView.NoEditTriggers)
self.chartsByConstant_modelView.setSelectionMode(QAbstractItemView.NoSelection)
self.chartsByConstant_modelView.setVerticalScrollMode(QAbstractItemView.ScrollPerPixel)
self.chartsByConstant_modelView.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel)
self.verticalLayout_3.addWidget(self.widget) self.verticalLayout_3.addWidget(self.chartsByConstant_modelView)
self.verticalLayout.addWidget(self.groupBox_2) self.gridLayout.addWidget(self.groupBox_2, 0, 1, 1, 1)
self.groupBox = QGroupBox(TabTools_ChartRecommend)
self.groupBox.setObjectName(u"groupBox")
self.verticalLayout_2 = QVBoxLayout(self.groupBox)
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
self.rangeFromPlayRating_playRatingSpinBox = QDoubleSpinBox(self.groupBox)
self.rangeFromPlayRating_playRatingSpinBox.setObjectName(u"rangeFromPlayRating_playRatingSpinBox")
self.rangeFromPlayRating_playRatingSpinBox.setMinimumSize(QSize(100, 0))
self.rangeFromPlayRating_playRatingSpinBox.setMaximumSize(QSize(100, 16777215))
self.rangeFromPlayRating_playRatingSpinBox.setDecimals(3)
self.rangeFromPlayRating_playRatingSpinBox.setMaximum(100.000000000000000)
self.rangeFromPlayRating_playRatingSpinBox.setSingleStep(0.100000000000000)
self.verticalLayout_2.addWidget(self.rangeFromPlayRating_playRatingSpinBox)
self.formLayout = QFormLayout()
self.formLayout.setObjectName(u"formLayout")
self.formLayout.setLabelAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
self.formLayout.setFormAlignment(Qt.AlignLeading|Qt.AlignLeft|Qt.AlignVCenter)
self.label = QLabel(self.groupBox)
self.label.setObjectName(u"label")
self.label.setText(u"EX+")
self.label.setAlignment(Qt.AlignBottom|Qt.AlignLeading|Qt.AlignLeft)
self.formLayout.setWidget(0, QFormLayout.LabelRole, self.label)
self.rangeFromPlayRating_ExPlusLabel = QLabel(self.groupBox)
self.rangeFromPlayRating_ExPlusLabel.setObjectName(u"rangeFromPlayRating_ExPlusLabel")
self.rangeFromPlayRating_ExPlusLabel.setText(u"...")
self.rangeFromPlayRating_ExPlusLabel.setAlignment(Qt.AlignLeading|Qt.AlignLeft|Qt.AlignTop)
self.formLayout.setWidget(0, QFormLayout.FieldRole, self.rangeFromPlayRating_ExPlusLabel)
self.label_2 = QLabel(self.groupBox)
self.label_2.setObjectName(u"label_2")
self.label_2.setText(u"EX")
self.label_2.setAlignment(Qt.AlignBottom|Qt.AlignLeading|Qt.AlignLeft)
self.formLayout.setWidget(1, QFormLayout.LabelRole, self.label_2)
self.rangeFromPlayRating_ExLabel = QLabel(self.groupBox)
self.rangeFromPlayRating_ExLabel.setObjectName(u"rangeFromPlayRating_ExLabel")
self.rangeFromPlayRating_ExLabel.setText(u"...")
self.rangeFromPlayRating_ExLabel.setAlignment(Qt.AlignLeading|Qt.AlignLeft|Qt.AlignTop)
self.formLayout.setWidget(1, QFormLayout.FieldRole, self.rangeFromPlayRating_ExLabel)
self.label_3 = QLabel(self.groupBox)
self.label_3.setObjectName(u"label_3")
self.label_3.setText(u"AA")
self.label_3.setAlignment(Qt.AlignBottom|Qt.AlignLeading|Qt.AlignLeft)
self.formLayout.setWidget(2, QFormLayout.LabelRole, self.label_3)
self.rangeFromPlayRating_AaLabel = QLabel(self.groupBox)
self.rangeFromPlayRating_AaLabel.setObjectName(u"rangeFromPlayRating_AaLabel")
self.rangeFromPlayRating_AaLabel.setText(u"...")
self.rangeFromPlayRating_AaLabel.setAlignment(Qt.AlignLeading|Qt.AlignLeft|Qt.AlignTop)
self.formLayout.setWidget(2, QFormLayout.FieldRole, self.rangeFromPlayRating_AaLabel)
self.label_5 = QLabel(self.groupBox)
self.label_5.setObjectName(u"label_5")
self.label_5.setText(u"A")
self.formLayout.setWidget(3, QFormLayout.LabelRole, self.label_5)
self.rangeFromPlayRating_ALabel = QLabel(self.groupBox)
self.rangeFromPlayRating_ALabel.setObjectName(u"rangeFromPlayRating_ALabel")
self.rangeFromPlayRating_ALabel.setText(u"...")
self.formLayout.setWidget(3, QFormLayout.FieldRole, self.rangeFromPlayRating_ALabel)
self.label_8 = QLabel(self.groupBox)
self.label_8.setObjectName(u"label_8")
self.label_8.setText(u"B")
self.formLayout.setWidget(4, QFormLayout.LabelRole, self.label_8)
self.rangeFromPlayRating_BLabel = QLabel(self.groupBox)
self.rangeFromPlayRating_BLabel.setObjectName(u"rangeFromPlayRating_BLabel")
self.rangeFromPlayRating_BLabel.setText(u"...")
self.formLayout.setWidget(4, QFormLayout.FieldRole, self.rangeFromPlayRating_BLabel)
self.label_10 = QLabel(self.groupBox)
self.label_10.setObjectName(u"label_10")
self.label_10.setText(u"C")
self.formLayout.setWidget(5, QFormLayout.LabelRole, self.label_10)
self.rangeFromPlayRating_CLabel = QLabel(self.groupBox)
self.rangeFromPlayRating_CLabel.setObjectName(u"rangeFromPlayRating_CLabel")
self.rangeFromPlayRating_CLabel.setText(u"...")
self.formLayout.setWidget(5, QFormLayout.FieldRole, self.rangeFromPlayRating_CLabel)
self.verticalLayout_2.addLayout(self.formLayout)
self.gridLayout.addWidget(self.groupBox, 0, 0, 1, 1)
self.groupBox_3 = QGroupBox(TabTools_ChartRecommend) self.groupBox_3 = QGroupBox(TabTools_ChartRecommend)
self.groupBox_3.setObjectName(u"groupBox_3") self.groupBox_3.setObjectName(u"groupBox_3")
@ -173,34 +210,33 @@ class Ui_TabTools_ChartRecommend(object):
self.chartsRecommendFromPlayRating_numLabel = QLabel(self.groupBox_3) self.chartsRecommendFromPlayRating_numLabel = QLabel(self.groupBox_3)
self.chartsRecommendFromPlayRating_numLabel.setObjectName(u"chartsRecommendFromPlayRating_numLabel") self.chartsRecommendFromPlayRating_numLabel.setObjectName(u"chartsRecommendFromPlayRating_numLabel")
sizePolicy.setHeightForWidth(self.chartsRecommendFromPlayRating_numLabel.sizePolicy().hasHeightForWidth())
self.chartsRecommendFromPlayRating_numLabel.setSizePolicy(sizePolicy)
self.chartsRecommendFromPlayRating_numLabel.setText(u"...") self.chartsRecommendFromPlayRating_numLabel.setText(u"...")
self.horizontalLayout_2.addWidget(self.chartsRecommendFromPlayRating_numLabel) self.horizontalLayout_2.addWidget(self.chartsRecommendFromPlayRating_numLabel)
self.horizontalSpacer_2 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
self.horizontalLayout_2.addItem(self.horizontalSpacer_2)
self.chartsRecommendFromPlayRating_refreshButton = QPushButton(self.groupBox_3)
self.chartsRecommendFromPlayRating_refreshButton.setObjectName(u"chartsRecommendFromPlayRating_refreshButton")
self.chartsRecommendFromPlayRating_refreshButton.setEnabled(False)
self.horizontalLayout_2.addWidget(self.chartsRecommendFromPlayRating_refreshButton)
self.verticalLayout_4.addLayout(self.horizontalLayout_2) self.verticalLayout_4.addLayout(self.horizontalLayout_2)
self.widget_2 = QWidget(self.groupBox_3) self.chartsRecommendFromPlayRating_modelView = QTableView(self.groupBox_3)
self.widget_2.setObjectName(u"widget_2") self.chartsRecommendFromPlayRating_modelView.setObjectName(u"chartsRecommendFromPlayRating_modelView")
sizePolicy.setHeightForWidth(self.widget_2.sizePolicy().hasHeightForWidth()) sizePolicy2 = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
self.widget_2.setSizePolicy(sizePolicy) sizePolicy2.setHorizontalStretch(0)
self.chartsRecommendFromPlayRating_gridLayout = QGridLayout(self.widget_2) sizePolicy2.setVerticalStretch(0)
self.chartsRecommendFromPlayRating_gridLayout.setObjectName(u"chartsRecommendFromPlayRating_gridLayout") sizePolicy2.setHeightForWidth(self.chartsRecommendFromPlayRating_modelView.sizePolicy().hasHeightForWidth())
self.chartsRecommendFromPlayRating_modelView.setSizePolicy(sizePolicy2)
self.chartsRecommendFromPlayRating_modelView.setMinimumSize(QSize(200, 0))
self.chartsRecommendFromPlayRating_modelView.setEditTriggers(QAbstractItemView.NoEditTriggers)
self.chartsRecommendFromPlayRating_modelView.setSelectionMode(QAbstractItemView.SingleSelection)
self.chartsRecommendFromPlayRating_modelView.setSelectionBehavior(QAbstractItemView.SelectRows)
self.chartsRecommendFromPlayRating_modelView.setVerticalScrollMode(QAbstractItemView.ScrollPerPixel)
self.chartsRecommendFromPlayRating_modelView.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel)
self.verticalLayout_4.addWidget(self.widget_2) self.verticalLayout_4.addWidget(self.chartsRecommendFromPlayRating_modelView)
self.verticalLayout.addWidget(self.groupBox_3) self.gridLayout.addWidget(self.groupBox_3, 1, 0, 1, 2)
self.retranslateUi(TabTools_ChartRecommend) self.retranslateUi(TabTools_ChartRecommend)
@ -209,11 +245,9 @@ class Ui_TabTools_ChartRecommend(object):
# setupUi # setupUi
def retranslateUi(self, TabTools_ChartRecommend): def retranslateUi(self, TabTools_ChartRecommend):
self.groupBox.setTitle(QCoreApplication.translate("TabTools_ChartRecommend", u"constantRangeFromPlayRating", None))
self.groupBox_2.setTitle(QCoreApplication.translate("TabTools_ChartRecommend", u"chartsByConstant", None)) self.groupBox_2.setTitle(QCoreApplication.translate("TabTools_ChartRecommend", u"chartsByConstant", None))
self.chartsByConstant_refreshButton.setText(QCoreApplication.translate("TabTools_ChartRecommend", u"refreshButton", None)) self.groupBox.setTitle(QCoreApplication.translate("TabTools_ChartRecommend", u"constantRangeFromPlayRating", None))
self.groupBox_3.setTitle(QCoreApplication.translate("TabTools_ChartRecommend", u"chartsRecommendFromPlayRating", None)) self.groupBox_3.setTitle(QCoreApplication.translate("TabTools_ChartRecommend", u"chartsRecommendFromPlayRating", None))
self.chartsRecommendFromPlayRating_refreshButton.setText(QCoreApplication.translate("TabTools_ChartRecommend", u"refreshButton", None))
pass pass
# retranslateUi # retranslateUi

View File

@ -6,8 +6,7 @@ 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, Score 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.common import DeviceOcrResult
from arcaea_offline_ocr.utils import convert_to_srgb
from PIL import Image from PIL import Image
from PIL.ImageQt import ImageQt from PIL.ImageQt import ImageQt
from PySide6.QtCore import ( from PySide6.QtCore import (
@ -25,6 +24,7 @@ from PySide6.QtCore import (
) )
from PySide6.QtGui import QImage, QPixmap from PySide6.QtGui import QImage, QPixmap
from ui.extends.ocr import convert_to_srgb
from ui.extends.shared.delegates.chartDelegate import ChartDelegate from ui.extends.shared.delegates.chartDelegate import ChartDelegate
from ui.extends.shared.delegates.imageDelegate import ImageDelegate from ui.extends.shared.delegates.imageDelegate import ImageDelegate
from ui.extends.shared.delegates.scoreDelegate import ScoreDelegate from ui.extends.shared.delegates.scoreDelegate import ScoreDelegate
@ -46,7 +46,7 @@ class OcrRunnable(QRunnable):
class IccOption(IntEnum): class IccOption(IntEnum):
Ignore = 0 UseQt = 0
UsePIL = 1 UsePIL = 1
TryFix = 2 TryFix = 2

View File

@ -1,26 +0,0 @@
try:
import json
from arcaea_offline_ocr.device.v1.definition import DeviceV1
from arcaea_offline_ocr.device.v2.definition import DeviceV2
def load_devices_json(filepath: str) -> list[DeviceV1]:
with open(filepath, "r", encoding="utf-8") as f:
file_content = f.read()
if len(file_content) == 0:
return []
content = json.loads(file_content)
assert isinstance(content, list)
devices = []
for item in content:
version = item["version"]
if version == 1:
devices.append(DeviceV1(**item))
elif version == 2:
devices.append(DeviceV2(**item))
return devices
except Exception:
def load_devices_json(*args, **kwargs):
pass

View File

@ -0,0 +1,27 @@
import io
from PIL import Image, ImageCms
from .build_phash import build_image_phash_database
def convert_to_srgb(pil_img: Image.Image):
"""
Convert PIL image to sRGB color space (if possible)
and save the converted file.
https://stackoverflow.com/a/65667797/16484891
CC BY-SA 4.0
"""
icc = pil_img.info.get("icc_profile", "")
icc_conv = ""
if icc:
io_handle = io.BytesIO(icc) # virtual file
src_profile = ImageCms.ImageCmsProfile(io_handle)
dst_profile = ImageCms.createProfile("sRGB")
img_conv = ImageCms.profileToProfile(pil_img, src_profile, dst_profile)
icc_conv = img_conv.info.get("icc_profile", "")
return img_conv if icc != icc_conv else pil_img

View File

@ -0,0 +1,76 @@
import sqlite3
import time
from typing import Any, Callable, Optional
import cv2
import numpy as np
from arcaea_offline_ocr.phash_db import phash_opencv
def preprocess_char_icon(img_gray: cv2.Mat):
h, w = img_gray.shape[:2]
img = cv2.fillPoly(
img_gray,
[
np.array([[0, 0], [round(w / 2), 0], [0, round(h / 2)]], np.int32),
np.array([[w, 0], [round(w / 2), 0], [w, round(h / 2)]], np.int32),
np.array([[0, h], [round(w / 2), h], [0, round(h / 2)]], np.int32),
np.array([[w, h], [round(w / 2), h], [w, round(h / 2)]], np.int32),
],
(128),
)
return img
def build_image_phash_database(
images: list[cv2.Mat],
labels: list[str],
*,
hash_size: int = 16,
highfreq_factor: int = 4,
progress_func: Optional[Callable[[int, int], Any]] = None,
):
assert len(images) == len(labels)
conn = sqlite3.connect(":memory:", check_same_thread=False)
with conn:
cursor = conn.cursor()
cursor.execute("CREATE TABLE properties (key TEXT, value TEXT)")
cursor.executemany(
"INSERT INTO properties VALUES (?, ?)",
[
("hash_size", hash_size),
("highfreq_factor", highfreq_factor),
],
)
image_num = len(images)
id_hashes = []
for i, label, image in zip(range(image_num), labels, images):
image_hash = phash_opencv(
image,
hash_size=hash_size,
highfreq_factor=highfreq_factor,
)
image_hash_bytes = image_hash.flatten().tobytes()
id_hashes.append([label, image_hash_bytes])
if progress_func:
progress_func(i + 1, image_num)
hash_length = len(id_hashes[0][1])
cursor.execute(f"CREATE TABLE hashes (id TEXT, hash BLOB({hash_length}))")
cursor.executemany(
"INSERT INTO hashes VALUES (?, ?)",
id_hashes,
)
cursor.executemany(
"INSERT INTO properties VALUES (?, ?)",
[("built_timestamp", int(time.time()))],
)
conn.commit()
return conn

View File

@ -0,0 +1,28 @@
import cv2
from arcaea_offline_ocr.phash_db import ImagePhashDatabase
def getCv2StatModelStatusText(model: cv2.ml.StatModel):
if not isinstance(model, cv2.ml.StatModel):
return '<font color="red">ERROR</font>'
varCount = model.getVarCount()
if varCount != 81:
return f'<font color="darkorange">WARN</font>, varCount {varCount}'
else:
return f'<font color="green">OK</font>, varCount {varCount}'
def getPhashDatabaseStatusText(db: ImagePhashDatabase):
if not isinstance(db, ImagePhashDatabase):
return '<font color="red">ERROR</font>'
jacketCount = len(db.jacket_hashes)
partnerIconCount = len(db.partner_icon_hashes)
statusText = f"J{jacketCount} PI{partnerIconCount}"
if partnerIconCount <= 0:
return f'<font color="darkorange">WARN</font>, {statusText}'
else:
return f'<font color="green">OK</font>, {statusText}'

View File

@ -1,28 +0,0 @@
import cv2
import numpy as np
from PySide6.QtGui import QImage
def cv2BgrMatToQImage(mat) -> QImage:
arr = np.ascontiguousarray(mat)
return QImage(
arr.data,
arr.shape[1],
arr.shape[0],
arr.strides[0],
QImage.Format.Format_RGB888,
).rgbSwapped()
def qImageToCvMatBgr(qImg: QImage):
# from Bing AI, references
# 1: https://stackoverflow.com/q/384759/16484891 | CC BY-SA 4.0
# 2: https://stackoverflow.com/q/37552924/16484891 | CC BY-SA 3.0
qImg = qImg.convertToFormat(QImage.Format.Format_RGB888)
qImg = qImg.copy().rgbSwapped()
return np.ndarray(
(qImg.height(), qImg.width(), 3),
buffer=qImg.constBits(),
strides=[qImg.bytesPerLine(), 3, 1],
dtype=np.uint8,
)

86
ui/extends/shared/data.py Normal file
View File

@ -0,0 +1,86 @@
import json
import sys
from functools import cached_property
from pathlib import Path
from typing import Literal, Optional, overload
from arcaea_offline.models import Chart, Difficulty, Song
from PySide6.QtCore import QFile
from .singleton import Singleton
TPartnerModifier = dict[str, Literal[0, 1, 2]]
class Data(metaclass=Singleton):
def __init__(self):
root = Path(sys.argv[0]).parent
self.__dataPath = (root / "data").resolve()
@property
def dataPath(self):
return self.__dataPath
@cached_property
def partnerModifiers(self) -> TPartnerModifier:
data = {}
builtinFile = QFile(":/partnerModifiers.json")
builtinFile.open(QFile.OpenModeFlag.ReadOnly)
builtinData = json.loads(str(builtinFile.readAll(), encoding="utf-8"))
builtinFile.close()
data |= builtinData
customFile = self.dataPath / "partnerModifiers.json"
if customFile.exists():
with open(customFile, "r", encoding="utf-8") as f:
customData = json.loads(f.read())
data |= customData
return data
def expirePartnerModifiersCache(self):
# expire property caches
# https://stackoverflow.com/a/69367025/16484891, CC BY-SA 4.0
self.__dict__.pop("partnerModifiers", None)
@property
def arcaeaPath(self):
return self.dataPath / "Arcaea"
@overload
def getJacketPath(self, chart: Chart, /) -> Path | None:
...
@overload
def getJacketPath(
self, song: Song, difficulty: Optional[Difficulty] = None, /
) -> Path | None:
...
def getJacketPath(self, *args) -> Path | None:
if isinstance(args[0], Chart):
chart = args[0]
ratingSpecified = f"{chart.song_id}_{chart.rating_class}"
base = chart.song_id
elif isinstance(args[0], Song):
song = args[0]
difficulty = args[1]
ratingSpecified = (
f"{song.id}_{difficulty.rating_class}"
if isinstance(difficulty, Difficulty)
else song.id
)
base = song.id
else:
raise ValueError()
ratingSpecified += ".jpg"
base += ".jpg"
jacketsPath = self.arcaeaPath / "Song"
if (jacketsPath / ratingSpecified).exists():
return jacketsPath / ratingSpecified
elif (jacketsPath / base).exists():
return jacketsPath / base
else:
return None

View File

@ -1,8 +1,15 @@
from typing import Callable from enum import IntEnum
from typing import Callable, Literal
from PySide6.QtCore import QEvent, QModelIndex, QObject, QPoint, QSize, Qt from PySide6.QtCore import QModelIndex, QPoint, QSize, Qt
from PySide6.QtGui import QBrush, QColor, QFont, QFontMetrics, QLinearGradient, QPainter from PySide6.QtGui import QBrush, QColor, QFont, QFontMetrics, QLinearGradient, QPainter
from PySide6.QtWidgets import QApplication, QStyledItemDelegate, QStyleOptionViewItem from PySide6.QtWidgets import QStyledItemDelegate, QStyleOptionViewItem
class TextSegmentDelegateVerticalAlign(IntEnum):
Top = 0
Middle = 1
Bottom = 2
class TextSegmentDelegate(QStyledItemDelegate): class TextSegmentDelegate(QStyledItemDelegate):
@ -15,6 +22,44 @@ class TextSegmentDelegate(QStyledItemDelegate):
GradientWrapperRole = TextRole + 3 GradientWrapperRole = TextRole + 3
FontRole = TextRole + 20 FontRole = TextRole + 20
def __init__(self, parent=None):
super().__init__(parent)
self.baseXOffsets: dict[str, int] = {}
self.baseYOffsets: dict[str, int] = {}
self.verticalAlign = TextSegmentDelegateVerticalAlign.Middle
def indexOffsetKey(self, index: QModelIndex):
return f"{index.row()},{index.column()}"
def setBaseXOffset(self, index: QModelIndex, offset: int):
key = self.indexOffsetKey(index)
if not offset:
self.baseXOffsets.pop(key, None)
else:
self.baseXOffsets[key] = offset
def setBaseYOffset(self, index: QModelIndex, offset: int):
key = self.indexOffsetKey(index)
if not offset:
self.baseYOffsets.pop(key, None)
else:
self.baseYOffsets[key] = offset
def setVerticalAlign(self, align: Literal["top", "middle", "bottom"]):
if not isinstance(align, str) and align not in ["top", "middle", "bottom"]:
raise ValueError(
"TextSegment only supports top/middle/bottom vertical aligning."
)
if align == "top":
self.verticalAlign = TextSegmentDelegateVerticalAlign.Top
elif align == "middle":
self.verticalAlign = TextSegmentDelegateVerticalAlign.Middle
elif align == "bottom":
self.verticalAlign = TextSegmentDelegateVerticalAlign.Bottom
def getTextSegments( def getTextSegments(
self, index: QModelIndex, option self, index: QModelIndex, option
) -> list[ ) -> list[
@ -31,12 +76,14 @@ class TextSegmentDelegate(QStyledItemDelegate):
]: ]:
return [] return []
def sizeHint(self, option, index) -> QSize: def textsSizeHint(self, option: QStyleOptionViewItem, index: QModelIndex) -> QSize:
width = 0 width = 0
height = self.VerticalPadding height = 0
fm: QFontMetrics = option.fontMetrics fm: QFontMetrics = option.fontMetrics
for line in self.getTextSegments(index, option): segments = self.getTextSegments(index, option)
lineWidth = 4 * self.HorizontalPadding for i in range(len(segments)):
line = segments[i]
lineWidth = 2 * self.HorizontalPadding
lineHeight = 0 lineHeight = 0
for textFrag in line: for textFrag in line:
font = textFrag.get(self.FontRole) font = textFrag.get(self.FontRole)
@ -47,17 +94,55 @@ class TextSegmentDelegate(QStyledItemDelegate):
lineWidth += textWidth lineWidth += textWidth
lineHeight = max(lineHeight, textHeight) lineHeight = max(lineHeight, textHeight)
width = max(lineWidth, width) width = max(lineWidth, width)
height += lineHeight + self.VerticalPadding height += lineHeight
if i != len(segments) - 1:
height += self.VerticalPadding
return QSize(width, height) return QSize(width, height)
def sizeHint(self, option: QStyleOptionViewItem, index: QModelIndex) -> QSize:
width = self.HorizontalPadding * 2
height = self.VerticalPadding * 2
textsSizeHint = self.textsSizeHint(option, index)
return QSize(textsSizeHint.width() + width, textsSizeHint.height() + height)
def baseX(self, option: QStyleOptionViewItem, index: QModelIndex):
return (
option.rect.x()
+ self.HorizontalPadding
+ self.baseXOffsets.get(self.indexOffsetKey(index), 0)
)
def baseY(self, option: QStyleOptionViewItem, index: QModelIndex):
baseY = (
option.rect.y()
+ self.VerticalPadding
+ self.baseYOffsets.get(self.indexOffsetKey(index), 0)
)
if self.verticalAlign != TextSegmentDelegateVerticalAlign.Top:
paintAreaSize: QSize = option.rect.size()
delegateSize = self.sizeHint(option, index)
if self.verticalAlign == TextSegmentDelegateVerticalAlign.Middle:
baseY += round((paintAreaSize.height() - delegateSize.height()) / 2)
elif self.verticalAlign == TextSegmentDelegateVerticalAlign.Bottom:
baseY += paintAreaSize.height() - delegateSize.height()
return baseY
def textMaxWidth(self, option: QStyleOptionViewItem, index: QModelIndex):
return (
option.rect.width()
- (2 * self.HorizontalPadding)
- self.baseXOffsets.get(self.indexOffsetKey(index), 0)
)
def paint( def paint(
self, painter: QPainter, option: QStyleOptionViewItem, index: QModelIndex self, painter: QPainter, option: QStyleOptionViewItem, index: QModelIndex
): ):
self.initStyleOption(option, index) self.initStyleOption(option, index)
# draw text only
baseX = option.rect.x() + self.HorizontalPadding baseX = self.baseX(option, index)
baseY = option.rect.y() + self.VerticalPadding baseY = self.baseY(option, index)
maxWidth = option.rect.width() - (2 * self.HorizontalPadding) maxWidth = self.textMaxWidth(option, index)
fm: QFontMetrics = option.fontMetrics fm: QFontMetrics = option.fontMetrics
painter.save() painter.save()
for line in self.getTextSegments(index, option): for line in self.getTextSegments(index, option):
@ -69,8 +154,7 @@ class TextSegmentDelegate(QStyledItemDelegate):
# elide text, get font values # elide text, get font values
text = textFrag[self.TextRole] text = textFrag[self.TextRole]
fragMaxWidth = maxWidth - (lineBaseX - baseX) fragMaxWidth = maxWidth - (lineBaseX - baseX)
font = textFrag.get(self.FontRole) if font := textFrag.get(self.FontRole):
if font:
painter.setFont(font) painter.setFont(font)
_fm = QFontMetrics(font) _fm = QFontMetrics(font)
else: else:
@ -116,37 +200,3 @@ class TextSegmentDelegate(QStyledItemDelegate):
def super_styledItemDelegate_paint(self, painter, option, index): def super_styledItemDelegate_paint(self, painter, option, index):
return super().paint(painter, option, index) return super().paint(painter, option, index)
class NoCommitWhenFocusOutEventFilter(QObject):
"""
--DEPRECATED--
The default QAbstractItemDelegate implementation has a private function
`editorEventFilter()`, when editor sends focusOut/hide event, it emits the
`commitData(editor)` signal. We don't want this since we need to validate
the input, so we filter the event out and handle it by ourselves.
Reimplement `checkIsEditor(self, val) -> bool` to ensure this filter is
working. The default implementation always return `False`.
"""
def checkIsEditor(self, val) -> bool:
return False
def eventFilter(self, object: QObject, event: QEvent) -> bool:
if self.checkIsEditor(object) and event.type() in [
QEvent.Type.FocusOut,
QEvent.Type.Hide,
]:
widget = QApplication.focusWidget()
while widget:
# check if focus changed into editor's child
if self.checkIsEditor(widget):
return False
widget = widget.parentWidget()
object.hide()
object.deleteLater()
return True
return False

View File

@ -1,7 +1,8 @@
from arcaea_offline.models import Chart from arcaea_offline.models import Chart, Difficulty, Song
from arcaea_offline.utils.rating 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 QModelIndex, Qt, Signal from PIL import Image
from PySide6.QtGui import QColor from PySide6.QtCore import QModelIndex, QRect, Qt, Signal
from PySide6.QtGui import QColor, QPainter, QPixmap
from PySide6.QtWidgets import ( from PySide6.QtWidgets import (
QFrame, QFrame,
QHBoxLayout, QHBoxLayout,
@ -13,6 +14,7 @@ from PySide6.QtWidgets import (
QWidget, QWidget,
) )
from ui.extends.shared.data import Data
from ui.implements.components.chartSelector import ChartSelector from ui.implements.components.chartSelector import ChartSelector
from ..utils import keepWidgetInScreen from ..utils import keepWidgetInScreen
@ -86,51 +88,152 @@ class ChartDelegate(TextSegmentDelegate):
def getChart(self, index: QModelIndex) -> Chart | None: def getChart(self, index: QModelIndex) -> Chart | None:
return None return None
def getSong(self, index: QModelIndex) -> Song | None:
return None
def getDifficulty(self, index: QModelIndex) -> Difficulty | None:
return None
def getTextSegments(self, index: QModelIndex, option): def getTextSegments(self, index: QModelIndex, option):
chart = self.getChart(index) chart = self.getChart(index)
if not isinstance(chart, Chart): song = self.getSong(index)
difficulty = self.getDifficulty(index)
chartValid = isinstance(chart, Chart)
songValid = isinstance(song, Song)
difficultyValid = isinstance(difficulty, Difficulty)
if not chartValid and not songValid:
return [ return [
[{self.TextRole: "Chart Invalid", self.ColorRole: QColor("#ff0000")}] [
{
self.TextRole: "Chart/Song not set",
self.ColorRole: QColor("#ff0000"),
}
]
] ]
chartConstantString = ( # get texts
f"{chart.constant / 10:.1f}" if chartValid:
if chart.constant is not None and chart.constant > 0 title = chart.title
else "?" else:
title = (
difficulty.title if difficultyValid and difficulty.title else song.title
)
if chartValid and chart.constant is not None:
chartConstantString = f"{chart.constant / 10:.1f}"
elif difficultyValid:
chartConstantString = str(difficulty.rating)
if difficulty.rating_plus:
chartConstantString += "+"
else:
chartConstantString = "?"
if chartValid:
ratingClass = chart.rating_class
elif difficultyValid:
ratingClass = difficulty.rating_class
else:
ratingClass = None
ratingText = (
f"{rating_class_to_text(ratingClass)} {chartConstantString}"
if ratingClass is not None
else "Unknown ?"
) )
if chartValid:
descText = f"({chart.song_id}, {chart.set})"
else:
descText = f"({song.id}, {song.set})"
# get attributes
ratingClassColor = (
self.RatingClassColors[ratingClass] if ratingClass is not None else None
)
return [ return [
[ [
{self.TextRole: f"{chart.title}"}, {self.TextRole: str(title)},
], ],
[ [
{ {
self.TextRole: f"{rating_class_to_text(chart.rating_class)} {chartConstantString}", self.TextRole: ratingText,
self.ColorRole: self.RatingClassColors[chart.rating_class], self.ColorRole: ratingClassColor,
}, },
], ],
[ [
{ {
self.TextRole: f"({chart.song_id}, {chart.set})", self.TextRole: descText,
self.ColorRole: option.widget.palette().placeholderText().color(), self.ColorRole: option.widget.palette().placeholderText().color(),
}, },
], ],
] ]
def paintWarningBackground(self, index: QModelIndex) -> bool: def sizeHint(self, option, index):
return True size = super().sizeHint(option, index)
minWidth = size.height() + 2 * self.HorizontalPadding # jacket size
width = size.width() + self.HorizontalPadding + size.height()
size.setWidth(max(minWidth, width))
return size
def paint(self, painter, option, index): def paint(self, painter, option, index):
# draw chartInvalid warning background
chart = self.getChart(index)
if not isinstance(chart, Chart) and self.paintWarningBackground(index):
painter.save()
painter.setPen(Qt.PenStyle.NoPen)
bgColor = QColor(self.ChartInvalidBackgroundColor)
bgColor.setAlpha(50)
painter.setBrush(bgColor)
painter.drawRect(option.rect)
painter.restore()
option.text = "" option.text = ""
data = Data()
chart = self.getChart(index)
song = self.getSong(index)
difficulty = self.getDifficulty(index)
if isinstance(chart, Chart):
jacketPath = data.getJacketPath(chart)
elif isinstance(song, Song):
jacketPath = data.getJacketPath(song, difficulty)
else:
jacketPath = "__TEXT_ONLY__"
if jacketPath == "__TEXT_ONLY__":
self.setBaseXOffset(index, 0)
super().paint(painter, option, index)
return
textsSizeHint = super().textsSizeHint(option, index)
jacketSize = textsSizeHint.height()
self.setBaseXOffset(index, self.HorizontalPadding + jacketSize)
jacketSizeTuple = (jacketSize, jacketSize)
if jacketPath:
pixmap = (
Image.open(str(jacketPath.resolve()))
.resize(jacketSizeTuple, Image.BICUBIC)
.toqpixmap()
)
else:
pixmap = (
Image.fromqpixmap(QPixmap(":/images/jacket-placeholder.png"))
.resize(jacketSizeTuple, Image.BICUBIC)
.toqpixmap()
)
pixmapAvailableWidth = option.rect.width() - self.HorizontalPadding
pixmapAvailableHeight = option.rect.height()
if pixmapAvailableWidth < jacketSize or pixmapAvailableHeight < jacketSize:
cropRect = QRect(0, 0, pixmapAvailableWidth, pixmapAvailableHeight)
pixmap = pixmap.copy(cropRect)
painter.save()
painter.setRenderHint(QPainter.RenderHint.LosslessImageRendering, True)
painter.setRenderHint(QPainter.RenderHint.Antialiasing, True)
pixmapBaseY = self.baseY(option, index)
painter.drawPixmap(
option.rect.x() + self.HorizontalPadding,
pixmapBaseY,
pixmap,
)
painter.restore()
super().paint(painter, option, index) super().paint(painter, option, index)
def checkIsEditor(self, val): def checkIsEditor(self, val):

View File

@ -1,19 +1,15 @@
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 from arcaea_offline.models import Chart, Score, ScoreBest
from arcaea_offline.utils.rating import rating_class_to_text from arcaea_offline.utils.rating import rating_class_to_text
from arcaea_offline.utils.score import score_to_grade_text, zip_score_grade from arcaea_offline.utils.score import (
clear_type_to_text,
modifier_to_text,
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 QHBoxLayout, QLabel, QPushButton, QSizePolicy, QWidget
QFrame,
QHBoxLayout,
QLabel,
QPushButton,
QSizePolicy,
QWidget,
)
from ui.implements.components.scoreEditor import ScoreEditor from ui.implements.components.scoreEditor import ScoreEditor
@ -27,12 +23,6 @@ class ScoreEditorDelegateWrapper(ScoreEditor):
def __init__(self, parent=None): def __init__(self, parent=None):
super().__init__(parent) super().__init__(parent)
# self.hLine = QFrame(self)
# self.hLine.setFrameShape(QFrame.Shape.HLine)
# self.hLine.setFrameShadow(QFrame.Shadow.Plain)
# self.hLine.setFixedHeight(5)
# self.gridLayout.addWidget(self.hLine, self.gridLayout.rowCount(), 0, -1, -1)
self.delegateHeader = QWidget(self) self.delegateHeader = QWidget(self)
self.delegateHeaderHBoxLayout = QHBoxLayout(self.delegateHeader) self.delegateHeaderHBoxLayout = QHBoxLayout(self.delegateHeader)
self.delegateHeaderHBoxLayout.setContentsMargins(0, 0, 0, 0) self.delegateHeaderHBoxLayout.setContentsMargins(0, 0, 0, 0)
@ -55,7 +45,9 @@ class ScoreEditorDelegateWrapper(ScoreEditor):
text = "Editing " text = "Editing "
text += _extra or "" text += _extra or ""
text += f"score {score.score}" text += f"score {score.score}"
text += f"<br>(P{score.pure} F{score.far} L{score.lost} | MR{score.max_recall})" text += (
f"<br>(P{score.pure} F{score.far} L{score.lost} | MR {score.max_recall})"
)
self.editorLabel.setText(text) self.editorLabel.setText(text)
@ -86,18 +78,21 @@ class ScoreDelegate(TextSegmentDelegate):
createGradeGradientWrapper(QColor("#5d1d35"), QColor("#9f3c55")), createGradeGradientWrapper(QColor("#5d1d35"), QColor("#9f3c55")),
] ]
def getScore(self, index: QModelIndex) -> Score | None: def getScore(self, index: QModelIndex) -> Score | ScoreBest | None:
return None return None
def getChart(self, index: QModelIndex) -> Chart | None: def getChart(self, index: QModelIndex) -> Chart | None:
return None return None
def isScoreInstance(self, index: QModelIndex) -> bool:
return isinstance(self.getScore(index), (Score, ScoreBest))
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 ( if (
isinstance(score, Score) self.isScoreInstance(index)
and isinstance(chart, Chart) and isinstance(chart, Chart)
and chart.notes is not None and chart.notes is not None
and score.pure is not None and score.pure is not None
@ -111,12 +106,12 @@ class ScoreDelegate(TextSegmentDelegate):
def getTextSegments(self, index, option): def getTextSegments(self, index, option):
score = self.getScore(index) score = self.getScore(index)
chart = self.getChart(index)
if not (isinstance(score, Score) and isinstance(chart, Chart)): if not self.isScoreInstance(index):
return [ return [
[ [
{ {
self.TextRole: "Chart/Score Invalid", self.TextRole: "Score Invalid",
self.ColorRole: QColor("#ff0000"), self.ColorRole: QColor("#ff0000"),
} }
] ]
@ -128,7 +123,9 @@ class ScoreDelegate(TextSegmentDelegate):
score_font.setPointSize(12) score_font.setPointSize(12)
score_grade_font = QFont(score_font) score_grade_font = QFont(score_font)
score_grade_font.setBold(True) score_grade_font.setBold(True)
return [ placeholderColor = option.widget.palette().placeholderText().color()
segments = [
[ [
{ {
self.TextRole: score_to_grade_text(score.score), self.TextRole: score_to_grade_text(score.score),
@ -156,19 +153,47 @@ class ScoreDelegate(TextSegmentDelegate):
self.ColorRole: self.PureFarLostColors[2], self.ColorRole: self.PureFarLostColors[2],
}, },
{self.TextRole: " | "}, {self.TextRole: " | "},
{self.TextRole: f"MAX RECALL {score.max_recall}"}, {self.TextRole: f"MR {score.max_recall}"},
],
[
{
self.TextRole: QDateTime.fromSecsSinceEpoch(score.date).toString(
"yyyy-MM-dd hh:mm:ss"
)
if score.date
else "-- No Date --"
}
], ],
] ]
if score.date is not None:
segments.append(
[
{
self.TextRole: QDateTime.fromSecsSinceEpoch(
score.date
).toString("yyyy-MM-dd hh:mm:ss")
}
],
)
else:
segments.append(
[{self.TextRole: "-- No Date --", self.ColorRole: placeholderColor}],
)
modifierClearTypeSegments = []
if score.modifier is not None:
modifierClearTypeSegments.append(
{self.TextRole: modifier_to_text(score.modifier)}
)
else:
modifierClearTypeSegments.append(
{self.TextRole: "Modifier None", self.ColorRole: placeholderColor}
)
modifierClearTypeSegments.append({self.TextRole: ", "})
if score.clear_type is not None:
modifierClearTypeSegments.append(
{self.TextRole: clear_type_to_text(score.clear_type)}
)
else:
modifierClearTypeSegments.append(
{self.TextRole: "Clear Type None", self.ColorRole: placeholderColor}
)
segments.append(modifierClearTypeSegments)
return segments
def paintWarningBackground(self, index: QModelIndex) -> bool: def paintWarningBackground(self, index: QModelIndex) -> bool:
return True return True
@ -177,7 +202,7 @@ class ScoreDelegate(TextSegmentDelegate):
score = self.getScore(index) score = self.getScore(index)
chart = self.getChart(index) chart = self.getChart(index)
if ( if (
isinstance(score, Score) self.isScoreInstance(index)
and isinstance(chart, Chart) and isinstance(chart, Chart)
and self.paintWarningBackground(index) and self.paintWarningBackground(index)
): ):
@ -217,7 +242,7 @@ class ScoreDelegate(TextSegmentDelegate):
else: else:
editor.setWindowTitle("-") editor.setWindowTitle("-")
if isinstance(score, Score): if self.isScoreInstance(index):
editor.setText(score) editor.setText(score)
editor.setValidateBeforeAccept(False) editor.setValidateBeforeAccept(False)
@ -238,7 +263,7 @@ class ScoreDelegate(TextSegmentDelegate):
chart = self.getChart(index) chart = self.getChart(index)
if isinstance(chart, Chart): if isinstance(chart, Chart):
editor.setChart(chart) editor.setChart(chart)
if isinstance(score, Score): if self.isScoreInstance(index):
editor.setValue(score) editor.setValue(score)
def confirmSetModelData(self, editor: ScoreEditorDelegateWrapper): def confirmSetModelData(self, editor: ScoreEditorDelegateWrapper):

View File

@ -5,12 +5,11 @@ from PySide6.QtCore import QFileInfo, QSettings, Signal
from .singleton import QObjectSingleton from .singleton import QObjectSingleton
__all__ = [ __all__ = [
"LANGUAGE",
"DATABASE_URL", "DATABASE_URL",
"DEVICES_JSON_FILE",
"DEVICE_UUID",
"TESSERACT_FILE",
"KNN_MODEL_FILE", "KNN_MODEL_FILE",
"SIFT_DATABASE_FILE", "B30_KNN_MODEL_FILE",
"PHASH_DATABASE_FILE",
"ANDREAL_FOLDER", "ANDREAL_FOLDER",
"ANDREAL_EXECUTABLE", "ANDREAL_EXECUTABLE",
"Settings", "Settings",
@ -21,12 +20,8 @@ __all__ = [
LANGUAGE = "Language" LANGUAGE = "Language"
DATABASE_URL = "DatabaseUrl" DATABASE_URL = "DatabaseUrl"
DEVICES_JSON_FILE = "Ocr/DevicesJsonFile"
DEVICE_UUID = "Ocr/DeviceUuid"
TESSERACT_FILE = "Ocr/TesseractFile"
KNN_MODEL_FILE = "Ocr/KnnModelFile" KNN_MODEL_FILE = "Ocr/KnnModelFile"
B30_KNN_MODEL_FILE = "Ocr/B30KnnModelFile" B30_KNN_MODEL_FILE = "Ocr/B30KnnModelFile"
SIFT_DATABASE_FILE = "Ocr/SiftDatabaseFile"
PHASH_DATABASE_FILE = "Ocr/PHashDatabaseFile" PHASH_DATABASE_FILE = "Ocr/PHashDatabaseFile"
ANDREAL_FOLDER = "Andreal/AndrealFolder" ANDREAL_FOLDER = "Andreal/AndrealFolder"
@ -70,33 +65,6 @@ class Settings(QSettings, metaclass=QObjectSingleton):
def setDatabaseUrl(self, value: str): def setDatabaseUrl(self, value: str):
self._setStrItem(DATABASE_URL, value) self._setStrItem(DATABASE_URL, value)
def devicesJsonFile(self):
return self._strItem(DEVICES_JSON_FILE)
def setDevicesJsonFile(self, value: str):
self._setStrItem(DEVICES_JSON_FILE, value)
def resetDevicesJsonFile(self):
self._resetStrItem(DEVICES_JSON_FILE)
def deviceUuid(self):
return self._strItem(DEVICE_UUID)
def setDeviceUuid(self, value: str):
self._setStrItem(DEVICE_UUID, value)
def resetDeviceUuid(self):
self._resetStrItem(DEVICE_UUID)
def tesseractPath(self):
return self._strItem(TESSERACT_FILE)
def setTesseractPath(self, value: str):
self._setStrItem(TESSERACT_FILE, value)
def resetTesseractPath(self):
self._resetStrItem(TESSERACT_FILE)
def knnModelFile(self): def knnModelFile(self):
return self._strItem(KNN_MODEL_FILE) return self._strItem(KNN_MODEL_FILE)
@ -115,15 +83,6 @@ class Settings(QSettings, metaclass=QObjectSingleton):
def resetB30KnnModelFile(self): def resetB30KnnModelFile(self):
self._resetStrItem(B30_KNN_MODEL_FILE) self._resetStrItem(B30_KNN_MODEL_FILE)
def siftDatabaseFile(self):
return self._strItem(SIFT_DATABASE_FILE)
def setSiftDatabaseFile(self, value: str):
self._setStrItem(SIFT_DATABASE_FILE, value)
def resetSiftDatabaseFile(self):
self._resetStrItem(SIFT_DATABASE_FILE)
def phashDatabaseFile(self): def phashDatabaseFile(self):
return self._strItem(PHASH_DATABASE_FILE) return self._strItem(PHASH_DATABASE_FILE)

View File

@ -1,69 +1,58 @@
import contextlib import contextlib
import logging import logging
from typing import Tuple from typing import Tuple, Type
import cv2 import cv2
import exif
from arcaea_offline.database import Database from arcaea_offline.database import Database
from arcaea_offline.models import Chart, Score from arcaea_offline.models import Chart, Score
from arcaea_offline_ocr.device.shared import DeviceOcrResult from arcaea_offline.utils.partner import KanaeDayNight, kanae_day_night
from arcaea_offline_ocr.device.v2 import DeviceV2AutoRois, DeviceV2Ocr, DeviceV2Rois from arcaea_offline_ocr.device import DeviceOcr, DeviceOcrResult
from arcaea_offline_ocr.device.v2.sizes import SizesV1, SizesV2 from arcaea_offline_ocr.device.rois import (
DeviceRois,
DeviceRoisAuto,
DeviceRoisExtractor,
DeviceRoisMasker,
)
from arcaea_offline_ocr.phash_db import ImagePhashDatabase
from arcaea_offline_ocr.utils import imread_unicode from arcaea_offline_ocr.utils import imread_unicode
from PySide6.QtCore import QDateTime, QFileInfo from PySide6.QtCore import QDateTime, QFileInfo
from ui.extends.components.ocrQueue import OcrRunnable from ui.extends.components.ocrQueue import OcrRunnable
from ui.extends.shared.data import Data
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
import exif
class TabDeviceOcrRunnable(OcrRunnable):
class TabDeviceV2OcrRunnable(OcrRunnable): def __init__(
def __init__(self, imagePath, device, knnModel, phashDb, *, sizesV2: bool): self,
imagePath: str,
rois: DeviceRois | Type[DeviceRoisAuto],
masker: DeviceRoisMasker,
knnModel: cv2.ml.KNearest,
phashDb: ImagePhashDatabase,
):
super().__init__() super().__init__()
self.imagePath = imagePath self.imagePath = imagePath
self.device = device self.rois = rois
self.masker = masker
self.knnModel = knnModel self.knnModel = knnModel
self.phashDb = phashDb self.phashDb = phashDb
self.sizesV2 = sizesV2
def run(self): def run(self):
try: try:
rois = DeviceV2Rois( img = imread_unicode(self.imagePath, cv2.IMREAD_COLOR)
self.device, imread_unicode(self.imagePath, cv2.IMREAD_COLOR) if isinstance(self.rois, type) and issubclass(self.rois, DeviceRoisAuto):
) rois = self.rois(img.shape[1], img.shape[0])
rois.sizes = ( else:
SizesV2(self.device.factor) rois = self.rois
if self.sizesV2 extractor = DeviceRoisExtractor(img, rois)
else SizesV1(self.device.factor) ocr = DeviceOcr(extractor, self.masker, self.knnModel, self.phashDb)
) result = ocr.ocr()
ocr = DeviceV2Ocr(self.knnModel, self.phashDb)
result = ocr.ocr(rois)
self.signals.resultReady.emit(result) self.signals.resultReady.emit(result)
except Exception: except Exception:
logger.exception(f"DeviceV2 ocr {self.imagePath} error") logger.exception("DeviceOcr error:")
finally:
self.signals.finished.emit()
class TabDeviceV2AutoRoisOcrRunnable(OcrRunnable):
def __init__(self, imagePath, knnModel, phashDb, *, sizesV2: bool):
super().__init__()
self.imagePath = imagePath
self.knnModel = knnModel
self.phashDb = phashDb
self.sizesV2 = sizesV2
def run(self):
try:
rois = DeviceV2AutoRois(imread_unicode(self.imagePath, cv2.IMREAD_COLOR))
factor = rois.sizes.factor
rois.sizes = SizesV2(factor) if self.sizesV2 else SizesV1(factor)
ocr = DeviceV2Ocr(self.knnModel, self.phashDb)
result = ocr.ocr(rois)
self.signals.resultReady.emit(result)
except Exception:
logger.exception(f"DeviceV2AutoRois ocr {self.imagePath} error")
finally: finally:
self.signals.finished.emit() self.signals.finished.emit()
@ -83,7 +72,24 @@ def getImageDate(imagePath: str) -> QDateTime:
class ScoreConverter: class ScoreConverter:
@staticmethod @staticmethod
def deviceV2(imagePath: str, _, result: DeviceOcrResult) -> Tuple[Chart, Score]: def device(imagePath: str, _, result: DeviceOcrResult) -> Tuple[Chart, Score]:
partnerModifiers = Data().partnerModifiers
imageDate = getImageDate(imagePath)
# calculate clear type
if result.partner_id == "50":
dayNight = kanae_day_night(imageDate)
modifier = 1 if dayNight == KanaeDayNight.Day else 2
else:
modifier = partnerModifiers.get(result.partner_id, 0)
if result.clear_status == 1 and modifier == 1:
clearType = 4
elif result.clear_status == 1 and modifier == 2:
clearType = 5
else:
clearType = result.clear_status
db = Database() db = Database()
score = Score( score = Score(
song_id=result.song_id, song_id=result.song_id,
@ -92,16 +98,16 @@ class ScoreConverter:
pure=result.pure, pure=result.pure,
far=result.far, far=result.far,
lost=result.lost, lost=result.lost,
date=getImageDate(imagePath).toSecsSinceEpoch(), date=imageDate.toSecsSinceEpoch(),
max_recall=result.max_recall, max_recall=result.max_recall,
modifier=modifier,
clear_type=clearType,
comment=f"OCR {QFileInfo(imagePath).fileName()}", comment=f"OCR {QFileInfo(imagePath).fileName()}",
) )
chart = db.get_chart(score.song_id, score.rating_class) chart = db.get_chart(score.song_id, score.rating_class) or Chart(
if not chart: song_id=result.song_id,
chart = Chart( rating_class=result.rating_class,
song_id=result.song_id, title=result.song_id,
rating_class=result.rating_class, constant=0.0,
title=result.song_id, )
constant=0.0,
)
return (chart, score) return (chart, score)

View File

@ -2,6 +2,7 @@ import base64
import logging import logging
import os import os
import re import re
import subprocess
from PySide6.QtCore import QObject, QProcess, QRunnable, QThreadPool, Signal from PySide6.QtCore import QObject, QProcess, QRunnable, QThreadPool, Signal
@ -24,7 +25,12 @@ class AndrealExecuteRunnable(QRunnable):
def run(self): def run(self):
try: try:
result = os.popen(f"{self.executePath} {' '.join(self.arguments)}").read() subp = subprocess.run(
[self.executePath, *self.arguments],
capture_output=True,
encoding="utf-8",
)
result = subp.stdout
b64Result = [s for s in result.split("\n") if s] b64Result = [s for s in result.split("\n") if s]
imageBytes = base64.b64decode( imageBytes = base64.b64decode(
re.sub(r"data:image/.*;base64,", "", b64Result[-1]) re.sub(r"data:image/.*;base64,", "", b64Result[-1])

View File

@ -0,0 +1,120 @@
import re
from typing import Any
from arcaea_offline.database import Database
from arcaea_offline.models import Chart, ScoreBest
from arcaea_offline.utils.rating import rating_class_to_text
from PySide6.QtCore import QAbstractListModel, QModelIndex, Qt
from PySide6.QtGui import QStandardItem, QStandardItemModel
from ui.extends.shared.delegates.chartDelegate import ChartDelegate
from ui.extends.shared.delegates.scoreDelegate import ScoreDelegate
class ChartsModel(QAbstractListModel):
ChartRole = Qt.ItemDataRole.UserRole
def __init__(self, parent=None):
super().__init__(parent)
self.__data: list[dict[int, Any]] = []
def rowCount(self, *args) -> int:
return len(self.__data)
def columnCount(self, *args) -> int:
return 1
def headerData(self, *args):
return None
def data(self, index: QModelIndex, role: int):
if not self.checkIndex(index):
return None
return self.__data[index.row()].get(role, None)
def clear(self):
self.beginResetModel()
self.beginRemoveRows(QModelIndex(), 0, self.rowCount())
self.__data.clear()
self.endRemoveRows()
self.endResetModel()
def setCharts(self, charts: list[Chart]):
self.clear()
db = Database()
self.beginInsertRows(QModelIndex(), 0, len(charts))
for chart in charts:
pack = db.get_pack(chart.set)
if re.search(r"_append_.*$", pack.id):
basePackId = re.sub(r"_append_.*$", "", pack.id)
basePackName = db.get_pack(basePackId).name
packName = f"{basePackName} - {pack.name}"
else:
packName = pack.name
tooltip = (
f"{chart.title}@{packName} [{rating_class_to_text(chart.rating_class)}]"
)
self.__data.append(
{
Qt.ItemDataRole.ToolTipRole: tooltip,
self.ChartRole: chart,
}
)
self.endInsertRows()
class CustomChartDelegate(ChartDelegate):
def getChart(self, index: QModelIndex) -> Chart | None:
return index.data(ChartsModel.ChartRole)
class ChartsWithScoreBestModel(QStandardItemModel):
ChartRole = Qt.ItemDataRole.UserRole
ScoreBestRole = Qt.ItemDataRole.UserRole + 10
def columnCount(self, *args) -> int:
return 3
def headerData(self, *args):
return None
def setChartAndScore(self, charts: list[Chart], scoreBests: list[ScoreBest]):
self.clear()
db = Database()
self.beginInsertRows(QModelIndex(), 0, len(charts))
for chart, scoreBest in zip(charts, scoreBests):
pack = db.get_pack(chart.set)
if re.search(r"_append_.*$", pack.id):
basePackId = re.sub(r"_append_.*$", "", pack.id)
basePackName = db.get_pack(basePackId).name
packName = f"{basePackName} - {pack.name}"
else:
packName = pack.name
tooltip = (
f"{chart.title}@{packName} [{rating_class_to_text(chart.rating_class)}]\n"
f"{scoreBest.score} > {scoreBest.potential}"
)
chartItem = QStandardItem()
chartItem.setData(tooltip, Qt.ItemDataRole.ToolTipRole)
chartItem.setData(chart, self.ChartRole)
scoreBestItem = QStandardItem()
scoreBestItem.setData(tooltip, Qt.ItemDataRole.ToolTipRole)
scoreBestItem.setData(scoreBest, self.ScoreBestRole)
potentialTextItem = QStandardItem()
potentialTextItem.setText(f"{scoreBest.potential}")
self.appendRow([chartItem, scoreBestItem, potentialTextItem])
class CustomScoreBestDelegate(ScoreDelegate):
def getScore(self, index: QModelIndex):
return index.data(ChartsWithScoreBestModel.ScoreBestRole)

View File

@ -27,6 +27,9 @@ class ChartSelector(Ui_ChartSelector, QWidget):
self.valueChanged.connect(self.updateResultLabel) self.valueChanged.connect(self.updateResultLabel)
self.songIdSelector.valueChanged.connect(self.updateRatingClassEnabled) self.songIdSelector.valueChanged.connect(self.updateRatingClassEnabled)
self.songIdSelector.quickSearchActivated.connect(
self.__songIdSelectedQuickSearchActivated
)
self.songIdSelector.valueChanged.connect(self.valueChanged) self.songIdSelector.valueChanged.connect(self.valueChanged)
self.ratingClassSelector.valueChanged.connect(self.valueChanged) self.ratingClassSelector.valueChanged.connect(self.valueChanged)
@ -85,15 +88,17 @@ class ChartSelector(Ui_ChartSelector, QWidget):
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>'
else: else:
text = f'No chart data<br><font color="gray">{chart.set} | {chart.song_id} | {chart.rating_class}</font>' text = (
"No chart data<br>"
f'<font color="gray">{chart.set} | {chart.song_id} | {chart.rating_class}</font>'
)
self.resultLabel.setText(text) self.resultLabel.setText(text)
else: else:
self.resultLabel.setText("...") self.resultLabel.setText("...")
def updateRatingClassEnabled(self): def updateRatingClassEnabled(self):
ratingClasses = [] ratingClasses = []
songId = self.songIdSelector.songId() if songId := self.songIdSelector.songId():
if songId:
if self.songIdSelector.mode == SongIdSelectorMode.Chart: if self.songIdSelector.mode == SongIdSelectorMode.Chart:
items = self.db.get_charts_by_song_id(songId) items = self.db.get_charts_by_song_id(songId)
else: else:
@ -106,9 +111,8 @@ class ChartSelector(Ui_ChartSelector, QWidget):
self.songIdSelector.reset() self.songIdSelector.reset()
def selectChart(self, chart: Chart): def selectChart(self, chart: Chart):
if not self.songIdSelector.selectPack(chart.set): self.songIdSelector.selectChart(chart)
return False self.ratingClassSelector.select(chart.rating_class)
if not self.songIdSelector.selectSongId(chart.song_id):
return False def __songIdSelectedQuickSearchActivated(self, chart: Chart):
self.ratingClassSelector.select(chart.rating_class) self.ratingClassSelector.select(chart.rating_class)
return True

View File

@ -1,32 +0,0 @@
from arcaea_offline_ocr.device.v1.definition import DeviceV1
from PySide6.QtCore import Qt
from PySide6.QtWidgets import QComboBox
from ui.extends.ocr import load_devices_json
from ui.extends.shared.delegates.descriptionDelegate import DescriptionDelegate
class DevicesComboBox(QComboBox):
DeviceUuidRole = Qt.ItemDataRole.UserRole + 10
def __init__(self, parent=None):
super().__init__(parent)
self.setItemDelegate(DescriptionDelegate(self))
def setDevices(self, devices: list[DeviceV1]):
self.clear()
for device in devices:
self.addItem(f"{device.name} ({device.uuid})", device)
row = self.count() - 1
self.setItemData(row, device.uuid, self.DeviceUuidRole)
self.setItemData(row, device.name, DescriptionDelegate.MainTextRole)
self.setItemData(row, device.uuid, DescriptionDelegate.DescriptionTextRole)
self.setCurrentIndex(-1)
def loadDevicesJson(self, path: str):
devices = load_devices_json(path)
self.setDevices(devices)
def selectDevice(self, deviceUuid: str):
index = self.findData(deviceUuid, self.DeviceUuidRole)
self.setCurrentIndex(index)

View File

@ -43,7 +43,7 @@ class OcrQueue(Ui_OcrQueue, QWidget):
self.iccOptionButtonGroup = QButtonGroup(self) self.iccOptionButtonGroup = QButtonGroup(self)
self.iccOptionButtonGroup.buttonToggled.connect(self.updateIccOption) self.iccOptionButtonGroup.buttonToggled.connect(self.updateIccOption)
self.iccOptionButtonGroup.addButton(self.iccIgnoreRadioButton, 0) self.iccOptionButtonGroup.addButton(self.iccUseQtRadioButton, 0)
self.iccOptionButtonGroup.addButton(self.iccUsePILRadioButton, 1) self.iccOptionButtonGroup.addButton(self.iccUsePILRadioButton, 1)
self.iccOptionButtonGroup.addButton(self.iccTryFixRadioButton, 2) self.iccOptionButtonGroup.addButton(self.iccTryFixRadioButton, 2)
self.updateIccOption() self.updateIccOption()

View File

@ -1,11 +1,10 @@
import logging import logging
import re import re
from enum import IntEnum from enum import IntEnum
from typing import Literal
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 PySide6.QtCore import QModelIndex, Qt, Signal, Slot from PySide6.QtCore import QModelIndex, QSignalMapper, Qt, Signal, Slot
from PySide6.QtWidgets import QCompleter, QWidget from PySide6.QtWidgets import QCompleter, QWidget
from ui.designer.components.songIdSelector_ui import Ui_SongIdSelector from ui.designer.components.songIdSelector_ui import Ui_SongIdSelector
@ -24,6 +23,7 @@ class SongIdSelectorMode(IntEnum):
class SongIdSelector(Ui_SongIdSelector, QWidget): class SongIdSelector(Ui_SongIdSelector, QWidget):
valueChanged = Signal() valueChanged = Signal()
quickSearchActivated = Signal(Chart)
def __init__(self, parent=None): def __init__(self, parent=None):
super().__init__(parent) super().__init__(parent)
@ -33,18 +33,22 @@ class SongIdSelector(Ui_SongIdSelector, QWidget):
self.languageChangeEventFilter = LanguageChangeEventFilter(self) self.languageChangeEventFilter = LanguageChangeEventFilter(self)
self.installEventFilter(self.languageChangeEventFilter) self.installEventFilter(self.languageChangeEventFilter)
self.previousPackageButton.clicked.connect( # quick switch bindings
lambda: self.quickSwitchSelection("previous", "package") self.quickSwitchSignalMapper = QSignalMapper(self)
self.previousPackageButton.clicked.connect(self.quickSwitchSignalMapper.map)
self.quickSwitchSignalMapper.setMapping(
self.previousPackageButton, "package||previous"
) )
self.previousSongIdButton.clicked.connect( self.nextPackageButton.clicked.connect(self.quickSwitchSignalMapper.map)
lambda: self.quickSwitchSelection("previous", "songId") self.quickSwitchSignalMapper.setMapping(self.nextPackageButton, "package||next")
) self.previousSongIdButton.clicked.connect(self.quickSwitchSignalMapper.map)
self.nextSongIdButton.clicked.connect( self.quickSwitchSignalMapper.setMapping(
lambda: self.quickSwitchSelection("next", "songId") self.previousSongIdButton, "songId||previous"
)
self.nextPackageButton.clicked.connect(
lambda: self.quickSwitchSelection("next", "package")
) )
self.nextSongIdButton.clicked.connect(self.quickSwitchSignalMapper.map)
self.quickSwitchSignalMapper.setMapping(self.nextSongIdButton, "songId||next")
self.quickSwitchSignalMapper.mappedString.connect(self.quickSwitchSlot)
self.mode = SongIdSelectorMode.SongId self.mode = SongIdSelectorMode.SongId
@ -73,12 +77,11 @@ class SongIdSelector(Ui_SongIdSelector, QWidget):
def setMode(self, mode: SongIdSelectorMode): def setMode(self, mode: SongIdSelectorMode):
self.mode = mode self.mode = mode
def quickSwitchSelection( @Slot(str)
self, def quickSwitchSlot(self, action: str):
direction: Literal["previous", "next"], model, direction = action.split("||")
model: Literal["package", "songId"],
): minIndex = -1
minIndex = 0
if model == "package": if model == "package":
maxIndex = self.packComboBox.count() - 1 maxIndex = self.packComboBox.count() - 1
currentIndex = self.packComboBox.currentIndex() + ( currentIndex = self.packComboBox.currentIndex() + (
@ -124,8 +127,7 @@ class SongIdSelector(Ui_SongIdSelector, QWidget):
self.packComboBox.clear() self.packComboBox.clear()
packs = self.db.get_packs() packs = self.db.get_packs()
for pack in packs: for pack in packs:
isAppendPack = re.search(r"_append_.*$", pack.id) if isAppendPack := re.search(r"_append_.*$", pack.id):
if isAppendPack:
basePackId = re.sub(r"_append_.*$", "", pack.id) basePackId = re.sub(r"_append_.*$", "", pack.id)
basePackName = self.db.get_pack(basePackId).name basePackName = self.db.get_pack(basePackId).name
packName = f"{basePackName} - {pack.name}" packName = f"{basePackName} - {pack.name}"
@ -144,8 +146,7 @@ class SongIdSelector(Ui_SongIdSelector, QWidget):
def fillSongIdComboBox(self): def fillSongIdComboBox(self):
self.songIdComboBox.clear() self.songIdComboBox.clear()
packId = self.packComboBox.currentData() if packId := self.packComboBox.currentData():
if packId:
if self.mode == SongIdSelectorMode.SongId: if self.mode == SongIdSelectorMode.SongId:
items = self.db.get_songs_by_pack_id(packId) items = self.db.get_songs_by_pack_id(packId)
elif self.mode == SongIdSelectorMode.Chart: elif self.mode == SongIdSelectorMode.Chart:
@ -174,7 +175,7 @@ class SongIdSelector(Ui_SongIdSelector, QWidget):
self.songIdComboBox.setCurrentIndex(-1) self.songIdComboBox.setCurrentIndex(-1)
@Slot() @Slot()
def on_packComboBox_activated(self): def on_packComboBox_currentIndexChanged(self):
self.fillSongIdComboBox() self.fillSongIdComboBox()
@Slot(str) @Slot(str)
@ -206,14 +207,15 @@ class SongIdSelector(Ui_SongIdSelector, QWidget):
return False return False
def selectChart(self, chart: Chart): def selectChart(self, chart: Chart):
if not self.selectPack(chart.set): packSelected = self.selectPack(chart.set)
return False songIdSelected = self.selectSongId(chart.song_id)
return self.selectSongId(chart.song_id) return packSelected and songIdSelected
@Slot(QModelIndex) @Slot(QModelIndex)
def searchCompleterSetSelection(self, index: QModelIndex): def searchCompleterSetSelection(self, index: QModelIndex):
chart = index.data(Qt.ItemDataRole.UserRole + 10) # type: Chart chart: Chart = index.data(Qt.ItemDataRole.UserRole + 10)
self.selectChart(chart) self.selectChart(chart)
self.quickSearchActivated.emit(chart)
self.searchLineEdit.clear() self.searchLineEdit.clear()
self.searchLineEdit.clearFocus() self.searchLineEdit.clearFocus()

View File

@ -1,7 +1,6 @@
from PySide6.QtCore import QCoreApplication from PySide6.QtCore import QCoreApplication
from PySide6.QtWidgets import QLabel, QPushButton from PySide6.QtWidgets import QLabel, QPushButton
from ui.implements.components.devicesComboBox import DevicesComboBox
from ui.implements.components.fileSelector import FileSelector from ui.implements.components.fileSelector import FileSelector
from ui.implements.settings.settingsBaseWidget import SettingsBaseWidget from ui.implements.settings.settingsBaseWidget import SettingsBaseWidget
@ -12,28 +11,6 @@ class SettingsOcr(SettingsBaseWidget):
self.setupUi(self) self.setupUi(self)
if self.settings.devicesJsonFile():
self.devicesJsonValueWidget.selectFile(self.settings.devicesJsonFile())
self.devicesJsonValueWidget.filesSelected.connect(self.setDevicesJson)
self.devicesJsonResetButton.clicked.connect(self.resetDevicesJson)
self.insertItem(
"devicesJson",
self.devicesJsonLabel,
self.devicesJsonValueWidget,
self.devicesJsonResetButton,
)
if self.settings.deviceUuid():
self.deviceUuidValueWidget.selectDevice(self.settings.deviceUuid())
self.deviceUuidValueWidget.activated.connect(self.setDeviceUuid)
self.deviceUuidResetButton.clicked.connect(self.resetDeviceUuid)
self.insertItem(
"deviceUuid",
self.deviceUuidLabel,
self.deviceUuidValueWidget,
self.deviceUuidResetButton,
)
if self.settings.knnModelFile(): if self.settings.knnModelFile():
self.knnModelFileValueWidget.selectFile(self.settings.knnModelFile()) self.knnModelFileValueWidget.selectFile(self.settings.knnModelFile())
self.knnModelFileValueWidget.filesSelected.connect(self.setKnnModelFile) self.knnModelFileValueWidget.filesSelected.connect(self.setKnnModelFile)
@ -56,19 +33,6 @@ class SettingsOcr(SettingsBaseWidget):
self.b30KnnModelFileResetButton, self.b30KnnModelFileResetButton,
) )
if self.settings.siftDatabaseFile():
self.siftDatabaseFileValueWidget.selectFile(
self.settings.siftDatabaseFile()
)
self.siftDatabaseFileValueWidget.filesSelected.connect(self.setSiftDatabaseFile)
self.siftDatabaseFileResetButton.clicked.connect(self.resetSiftDatabaseFile)
self.insertItem(
"siftDatabaseFile",
self.siftDatabaseFileLabel,
self.siftDatabaseFileValueWidget,
self.siftDatabaseFileResetButton,
)
if self.settings.phashDatabaseFile(): if self.settings.phashDatabaseFile():
self.phashDatabaseFileValueWidget.selectFile( self.phashDatabaseFileValueWidget.selectFile(
self.settings.phashDatabaseFile() self.settings.phashDatabaseFile()
@ -84,34 +48,6 @@ class SettingsOcr(SettingsBaseWidget):
self.phashDatabaseFileResetButton, self.phashDatabaseFileResetButton,
) )
def setDevicesJson(self):
selectedFile = self.devicesJsonValueWidget.selectedFiles()
if selectedFile and selectedFile[0]:
file = selectedFile[0]
self.settings.setDevicesJsonFile(file)
self.fillDeviceUuidComboBox()
def fillDeviceUuidComboBox(self):
devicesJsonPath = self.devicesJsonValueWidget.selectedFiles()[0]
self.deviceUuidValueWidget.loadDevicesJson(devicesJsonPath)
storedDeviceUuid = self.settings.deviceUuid()
self.deviceUuidValueWidget.selectDevice(storedDeviceUuid)
def resetDevicesJson(self):
self.deviceUuidValueWidget.clear()
self.devicesJsonValueWidget.reset()
self.settings.resetDeviceUuid()
self.settings.resetDevicesJsonFile()
def setDeviceUuid(self):
if device := self.deviceUuidValueWidget.currentData():
self.settings.setDeviceUuid(device.uuid)
def resetDeviceUuid(self):
self.deviceUuidValueWidget.setCurrentIndex(-1)
self.settings.resetDeviceUuid()
def setKnnModelFile(self): def setKnnModelFile(self):
selectedFile = self.knnModelFileValueWidget.selectedFiles() selectedFile = self.knnModelFileValueWidget.selectedFiles()
if selectedFile and selectedFile[0]: if selectedFile and selectedFile[0]:
@ -132,16 +68,6 @@ class SettingsOcr(SettingsBaseWidget):
self.b30KnnModelFileValueWidget.reset() self.b30KnnModelFileValueWidget.reset()
self.settings.resetB30KnnModelFile() self.settings.resetB30KnnModelFile()
def setSiftDatabaseFile(self):
selectedFile = self.siftDatabaseFileValueWidget.selectedFiles()
if selectedFile and selectedFile[0]:
file = selectedFile[0]
self.settings.setSiftDatabaseFile(file)
def resetSiftDatabaseFile(self):
self.siftDatabaseFileValueWidget.reset()
self.settings.resetSiftDatabaseFile()
def setPHashDatabaseFile(self): def setPHashDatabaseFile(self):
selectedFile = self.phashDatabaseFileValueWidget.selectedFiles() selectedFile = self.phashDatabaseFileValueWidget.selectedFiles()
if selectedFile and selectedFile[0]: if selectedFile and selectedFile[0]:
@ -153,14 +79,6 @@ class SettingsOcr(SettingsBaseWidget):
self.settings.resetPHashDatabaseFile() self.settings.resetPHashDatabaseFile()
def setupUi(self, *args): def setupUi(self, *args):
self.devicesJsonLabel = QLabel(self)
self.devicesJsonValueWidget = FileSelector(self)
self.devicesJsonResetButton = QPushButton(self)
self.deviceUuidLabel = QLabel(self)
self.deviceUuidValueWidget = DevicesComboBox(self)
self.deviceUuidResetButton = QPushButton(self)
self.knnModelFileLabel = QLabel(self) self.knnModelFileLabel = QLabel(self)
self.knnModelFileValueWidget = FileSelector(self) self.knnModelFileValueWidget = FileSelector(self)
self.knnModelFileResetButton = QPushButton(self) self.knnModelFileResetButton = QPushButton(self)
@ -169,10 +87,6 @@ class SettingsOcr(SettingsBaseWidget):
self.b30KnnModelFileValueWidget = FileSelector(self) self.b30KnnModelFileValueWidget = FileSelector(self)
self.b30KnnModelFileResetButton = QPushButton(self) self.b30KnnModelFileResetButton = QPushButton(self)
self.siftDatabaseFileLabel = QLabel(self)
self.siftDatabaseFileValueWidget = FileSelector(self)
self.siftDatabaseFileResetButton = QPushButton(self)
self.phashDatabaseFileLabel = QLabel(self) self.phashDatabaseFileLabel = QLabel(self)
self.phashDatabaseFileValueWidget = FileSelector(self) self.phashDatabaseFileValueWidget = FileSelector(self)
self.phashDatabaseFileResetButton = QPushButton(self) self.phashDatabaseFileResetButton = QPushButton(self)
@ -186,21 +100,12 @@ class SettingsOcr(SettingsBaseWidget):
# fmt: off # fmt: off
self.setTitle(QCoreApplication.translate("Settings", "ocr.title")) self.setTitle(QCoreApplication.translate("Settings", "ocr.title"))
self.devicesJsonLabel.setText(QCoreApplication.translate("Settings", "ocr.devicesJson.label"))
self.devicesJsonResetButton.setText(QCoreApplication.translate("Settings", "resetButton"))
self.deviceUuidLabel.setText(QCoreApplication.translate("Settings", "ocr.deviceUuid.label"))
self.deviceUuidResetButton.setText(QCoreApplication.translate("Settings", "resetButton"))
self.knnModelFileLabel.setText(QCoreApplication.translate("Settings", "ocr.knnModelFile.label")) self.knnModelFileLabel.setText(QCoreApplication.translate("Settings", "ocr.knnModelFile.label"))
self.knnModelFileResetButton.setText(QCoreApplication.translate("Settings", "resetButton")) self.knnModelFileResetButton.setText(QCoreApplication.translate("Settings", "resetButton"))
self.b30KnnModelFileLabel.setText(QCoreApplication.translate("Settings", "ocr.b30KnnModelFile.label")) self.b30KnnModelFileLabel.setText(QCoreApplication.translate("Settings", "ocr.b30KnnModelFile.label"))
self.b30KnnModelFileResetButton.setText(QCoreApplication.translate("Settings", "resetButton")) self.b30KnnModelFileResetButton.setText(QCoreApplication.translate("Settings", "resetButton"))
self.siftDatabaseFileLabel.setText(QCoreApplication.translate("Settings", "ocr.siftDatabaseFile.label"))
self.siftDatabaseFileResetButton.setText(QCoreApplication.translate("Settings", "resetButton"))
self.phashDatabaseFileLabel.setText(QCoreApplication.translate("Settings", "ocr.phashDatabaseFile.label")) self.phashDatabaseFileLabel.setText(QCoreApplication.translate("Settings", "ocr.phashDatabaseFile.label"))
self.phashDatabaseFileResetButton.setText(QCoreApplication.translate("Settings", "resetButton")) self.phashDatabaseFileResetButton.setText(QCoreApplication.translate("Settings", "resetButton"))
# fmt: on # fmt: on

View File

@ -1,16 +1,20 @@
import logging import logging
import cv2 import cv2
import numpy as np
from arcaea_offline_ocr.b30.chieri.v4.ocr import ChieriBotV4Ocr from arcaea_offline_ocr.b30.chieri.v4.ocr import ChieriBotV4Ocr
from arcaea_offline_ocr.phash_db import ImagePHashDatabase from arcaea_offline_ocr.phash_db import ImagePhashDatabase
from arcaea_offline_ocr.sift_db import SIFTDatabase
from arcaea_offline_ocr.utils import imread_unicode from arcaea_offline_ocr.utils import imread_unicode
from PIL import Image
from PySide6.QtCore import Signal, Slot from PySide6.QtCore import Signal, Slot
from PySide6.QtWidgets import QWidget from PySide6.QtWidgets import QFileDialog, QMessageBox, QWidget
from ui.designer.tabs.tabOcr.tabOcr_B30_ui import Ui_TabOcr_B30 from ui.designer.tabs.tabOcr.tabOcr_B30_ui import Ui_TabOcr_B30
from ui.extends.components.ocrQueue import OcrQueueModel from ui.extends.components.ocrQueue import OcrQueueModel
from ui.extends.shared.cv2_utils import cv2BgrMatToQImage, qImageToCvMatBgr from ui.extends.ocr.dependencies import (
getCv2StatModelStatusText,
getPhashDatabaseStatusText,
)
from ui.extends.shared.language import LanguageChangeEventFilter from ui.extends.shared.language import LanguageChangeEventFilter
from ui.extends.shared.settings import ( from ui.extends.shared.settings import (
B30_KNN_MODEL_FILE, B30_KNN_MODEL_FILE,
@ -36,97 +40,119 @@ class TabOcr_B30(Ui_TabOcr_B30, QWidget):
self.b30TypeComboBox.setCurrentIndex(0) self.b30TypeComboBox.setCurrentIndex(0)
self.b30TypeComboBox.setEnabled(False) self.b30TypeComboBox.setEnabled(False)
self.imageSelector.filesSelected.connect(self.imageSelected) self.dependencies_knnModelSelector.filesSelected.connect(self.knnModelSelected)
self.knnModelSelector.filesSelected.connect(self.knnModelSelected) self.dependencies_b30KnnModelSelector.filesSelected.connect(
self.b30KnnModelSelector.filesSelected.connect(self.b30KnnModelSelected) self.b30KnnModelSelected
self.phashDatabaseSelector.filesSelected.connect(self.phashDatabaseSelected) )
self.dependencies_phashDatabaseSelector.filesSelected.connect(
self.phashDatabaseSelected
)
self.imagePath = None # for checking only
self.img = None
self.paddleFolder = None
self.paddle = None
self.knnModel = None self.knnModel = None
self.b30KnnModel = None self.b30KnnModel = None
# self.siftDatabase = None
self.phashDatabase = None self.phashDatabase = None
self.ocr = None self.ocr = None
self.tryPrepareOcr.connect(self.prepareOcr)
logger.info("Applying settings...") logger.info("Applying settings...")
self.knnModelSelector.connectSettings(KNN_MODEL_FILE) self.dependencies_knnModelSelector.connectSettings(KNN_MODEL_FILE)
self.b30KnnModelSelector.connectSettings(B30_KNN_MODEL_FILE) self.dependencies_b30KnnModelSelector.connectSettings(B30_KNN_MODEL_FILE)
self.phashDatabaseSelector.connectSettings(PHASH_DATABASE_FILE) self.dependencies_phashDatabaseSelector.connectSettings(PHASH_DATABASE_FILE)
self.ocrQueueModel = OcrQueueModel(self) self.ocrQueueModel = OcrQueueModel(self)
self.ocrQueue.setModel(self.ocrQueueModel) self.ocrQueue.setModel(self.ocrQueueModel)
def imageSelected(self): # def imageSelected(self):
if selectedFiles := self.imageSelector.selectedFiles(): # if selectedFiles := self.imageSelector.selectedFiles():
imagePath = selectedFiles[0] # imagePath = selectedFiles[0]
self.imagePath = imagePath # self.imagePath = imagePath
self.img = imread_unicode(imagePath) # self.img = imread_unicode(imagePath)
self.tryPrepareOcr.emit() # self.tryPrepareOcr.emit()
def knnModelSelected(self): def knnModelSelected(self):
if selectedFiles := self.knnModelSelector.selectedFiles(): try:
knnModelPath = selectedFiles[0] filePath = self.dependencies_knnModelSelector.selectedFiles()[0]
self.knnModel = cv2.ml.KNearest.load(knnModelPath) self.knnModel = cv2.ml.KNearest.load(filePath)
self.tryPrepareOcr.emit() except Exception:
self.knnModel = None
logger.exception("Error loading knn model:")
finally:
self.dependencies_knnModelStatusLabel.setText(
getCv2StatModelStatusText(self.knnModel)
)
def b30KnnModelSelected(self): def b30KnnModelSelected(self):
if selectedFiles := self.b30KnnModelSelector.selectedFiles(): try:
b30KnnModelPath = selectedFiles[0] filePath = self.dependencies_b30KnnModelSelector.selectedFiles()[0]
self.b30KnnModel = cv2.ml.KNearest.load(b30KnnModelPath) self.b30KnnModel = cv2.ml.KNearest.load(filePath)
self.tryPrepareOcr.emit() except Exception:
self.b30KnnModel = None
logger.exception("Error loading b30 knn model:")
finally:
self.dependencies_b30KnnModelStatusLabel.setText(
getCv2StatModelStatusText(self.b30KnnModel)
)
def phashDatabaseSelected(self): def phashDatabaseSelected(self):
if selectedFiles := self.phashDatabaseSelector.selectedFiles(): try:
phashDatabasePath = selectedFiles[0] filePath = self.dependencies_phashDatabaseSelector.selectedFiles()[0]
self.phashDatabase = ImagePHashDatabase(phashDatabasePath) self.phashDatabase = ImagePhashDatabase(filePath)
self.tryPrepareOcr.emit() except Exception:
self.phashDatabase = None
logger.exception("Error loading phash database:")
finally:
self.dependencies_phashDatabaseStatusLabel.setText(
getPhashDatabaseStatusText(self.phashDatabase)
)
def prepareOcr(self): def checkDependencies(self):
b30Type = self.b30TypeComboBox.currentData() b30Type = self.b30TypeComboBox.currentData()
if not b30Type: if not b30Type:
return False
elif b30Type == "chieri_v4":
return (
self.knnModel is not None
and self.b30KnnModel is not None
and self.phashDatabase is not None
)
else:
return False
@Slot()
def on_ocr_addImageButton_clicked(self):
if not self.checkDependencies():
QMessageBox.critical(self, None, "Dependencies not configured.")
return return
if b30Type == "chieri_v4": imagePath, _ = QFileDialog.getOpenFileName(
if ( self, None, "", "Image Files (*.png *.jpg *.jpeg *.bmp *.webp);;*"
not self.imagePath )
or not self.knnModel
or not self.b30KnnModel
or not self.phashDatabase
):
return
self.ocrQueueModel.clear() if not imagePath:
return
ocr = ChieriBotV4Ocr(self.knnModel, self.b30KnnModel, self.phashDatabase) self.ocrQueueModel.clear()
ocr.set_factor(self.img)
self.ocr = ocr
roi = ocr.rois img = imread_unicode(imagePath, cv2.IMREAD_COLOR)
for component in roi.components(self.img): ocr = ChieriBotV4Ocr(self.knnModel, self.b30KnnModel, self.phashDatabase)
qImage = cv2BgrMatToQImage(component.copy()) ocr.set_factor(img)
self.ocrQueueModel.addItem(qImage) self.ocr = ocr
roi = ocr.rois
for component in roi.components(img):
qImage = Image.fromarray(component.copy()).toqimage()
self.ocrQueueModel.addItem(qImage)
self.ocrQueue.resizeTableView() self.ocrQueue.resizeTableView()
@Slot() @Slot()
def on_ocr_startButton_clicked(self): def on_ocr_startButton_clicked(self):
if ( if not self.ocr:
not self.imagePath
or not self.knnModel
or not self.b30KnnModel
or not self.phashDatabase
):
return return
for row in range(self.ocrQueueModel.rowCount()): for row in range(self.ocrQueueModel.rowCount()):
index = self.ocrQueueModel.index(row, 0) index = self.ocrQueueModel.index(row, 0)
qImage = index.data(OcrQueueModel.ImageQImageRole) qImage = index.data(OcrQueueModel.ImageQImageRole)
cv2Mat = qImageToCvMatBgr(qImage) cv2Mat = np.array(Image.fromqimage(qImage))
runnable = ChieriV4OcrRunnable(self.ocr, cv2Mat) runnable = ChieriV4OcrRunnable(self.ocr, cv2Mat)
self.ocrQueueModel.setData(index, runnable, OcrQueueModel.OcrRunnableRole) self.ocrQueueModel.setData(index, runnable, OcrQueueModel.OcrRunnableRole)
self.ocrQueueModel.setData( self.ocrQueueModel.setData(

View File

@ -0,0 +1,164 @@
import logging
import re
import sqlite3
import time
from pathlib import Path
import cv2
from PySide6.QtCore import QThread, Signal, Slot
from PySide6.QtWidgets import QFileDialog, QMessageBox, QWidget
from ui.designer.tabs.tabOcr.tabOcr_BuildPHashDatabase_ui import (
Ui_TabOcr_BuildPHashDatabase,
)
from ui.extends.ocr.build_phash import build_image_phash_database, preprocess_char_icon
from ui.extends.shared.language import LanguageChangeEventFilter
logger = logging.getLogger(__name__)
class BuildDatabaseThread(QThread):
conn: sqlite3.Connection
progress = Signal(int, int)
success = Signal()
error = Signal(str)
finished = Signal()
def __init__(
self,
images: list[Path],
labels: list[str],
*,
hashSize: int | None = None,
highfreqFactor: int | None = None,
):
super().__init__()
self.images = images
self.labels = labels
self.hashSize = hashSize
self.highfreqFactor = highfreqFactor
def run(self):
try:
progressFunc = lambda i, total: self.progress.emit(i, total)
kwargsDict = {}
if self.hashSize is not None:
kwargsDict["hash_size"] = self.hashSize
if self.highfreqFactor is not None:
kwargsDict["highfreq_factor"] = self.highfreqFactor
self.conn = build_image_phash_database(
self.images, self.labels, progress_func=progressFunc, **kwargsDict
)
self.success.emit()
except Exception as e:
logger.exception("Error during pHash database build")
self.error.emit(str(e))
finally:
self.finished.emit()
class TabOcr_BuildPHashDatabase(Ui_TabOcr_BuildPHashDatabase, QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setupUi(self)
self.languageChangeEventFilter = LanguageChangeEventFilter(self)
self.installEventFilter(self.languageChangeEventFilter)
self.songDirSelector.setMode(self.songDirSelector.getExistingDirectory)
self.charIconDirSelector.setMode(self.charIconDirSelector.getExistingDirectory)
self.buildButton.clicked.connect(self.databaseBuildStart)
@Slot()
def on_optionsResetButton_clicked(self):
self.hashSizeSpinBox.setValue(16)
self.highfreqFactorSpinBox.setValue(4)
def databaseFileName(self):
return f"image-phash-{int(time.time() * 1000)}.db"
def databaseBuildStart(self):
if not self.songDirSelector.selectedFiles():
QMessageBox.critical(self, None, "Song directory not selected.")
return
if not self.charIconDirSelector.selectedFiles():
QMessageBox.critical(self, None, "Char icon directory not selected.")
return
songDir = self.songDirSelector.selectedFiles()[0]
charIconDir = self.charIconDirSelector.selectedFiles()[0]
acceptExts = [".jpg", ".png"]
songFilePaths = [
p for p in Path(songDir).glob("**/*") if p.suffix in acceptExts
]
charIconFilePaths = [
p for p in Path(charIconDir).glob("**/*") if p.suffix in acceptExts
]
self.readImageProgressBar.setMaximum(
len(songFilePaths) + len(charIconFilePaths)
)
i = 0
songMats = []
charIconMats = []
for image_path in songFilePaths:
songMats.append(cv2.imread(str(image_path.resolve()), cv2.IMREAD_GRAYSCALE))
i += 1
self.readImageProgressBar.setValue(i)
for image_path in charIconFilePaths:
mat = cv2.imread(str(image_path.resolve()), cv2.IMREAD_GRAYSCALE)
if self.preprocessCharIconCheckBox.isChecked():
mat = preprocess_char_icon(mat)
charIconMats.append(mat)
i += 1
self.readImageProgressBar.setValue(i)
songLabels = [re.sub(r"_.*$", "", p.stem) for p in songFilePaths]
charLabels = [f"partner_icon||{p.stem}" for p in charIconFilePaths]
self.databaseBuildThread = BuildDatabaseThread(
songMats + charIconMats, songLabels + charLabels
)
self.databaseBuildThread.progress.connect(self.databaseBuildProgress)
self.databaseBuildThread.success.connect(self.databaseBuildSuccess)
self.databaseBuildThread.error.connect(self.databaseBuildError)
self.buildButton.setEnabled(False)
self.databaseBuildThread.start()
@Slot(int, int)
def databaseBuildProgress(self, i: int, total: int):
if i < 5:
self.calculateHashProgressBar.setMaximum(total)
self.calculateHashProgressBar.setValue(i)
@Slot(str)
def databaseBuildError(self, msg: str):
QMessageBox.critical(self, "Error", msg)
self.databaseBuildCleanUp()
@Slot()
def databaseBuildSuccess(self):
dbMemory = self.databaseBuildThread.conn
dbFileName, _ = QFileDialog.getSaveFileName(self, None, self.databaseFileName())
if not dbFileName:
self.databaseBuildCleanUp()
QMessageBox.information(self, None, "User canceled operation.")
return
dbDisk = sqlite3.connect(dbFileName)
dbMemory.backup(dbDisk)
self.databaseBuildCleanUp()
def databaseBuildCleanUp(self):
self.databaseBuildThread.deleteLater()
self.databaseBuildThread = None
self.readImageProgressBar.setMaximum(0)
self.readImageProgressBar.setValue(0)
self.calculateHashProgressBar.setMaximum(0)
self.calculateHashProgressBar.setValue(0)
self.buildButton.setEnabled(True)

View File

@ -1,30 +1,21 @@
import logging import logging
import cv2 import cv2
from arcaea_offline_ocr.device.rois import (
# from arcaea_offline_ocr_device_creation_wizard.implements.wizard import Wizard DeviceRoisAutoT1,
from arcaea_offline_ocr.device.v1.definition import DeviceV1 DeviceRoisAutoT2,
from arcaea_offline_ocr.device.v2.definition import DeviceV2 DeviceRoisMaskerAutoT1,
from arcaea_offline_ocr.phash_db import ImagePHashDatabase DeviceRoisMaskerAutoT2,
from arcaea_offline_ocr.sift_db import SIFTDatabase )
from PySide6.QtCore import Qt, Slot from arcaea_offline_ocr.phash_db import ImagePhashDatabase
from PySide6.QtWidgets import QApplication, QFileDialog, QWidget from PySide6.QtCore import Slot
from PySide6.QtWidgets import QApplication, QFileDialog, QMessageBox, QWidget
from ui.designer.tabs.tabOcr.tabOcr_Device_ui import Ui_TabOcr_Device from ui.designer.tabs.tabOcr.tabOcr_Device_ui import Ui_TabOcr_Device
from ui.extends.components.ocrQueue import OcrQueueModel from ui.extends.components.ocrQueue import OcrQueueModel
from ui.extends.shared.language import LanguageChangeEventFilter from ui.extends.shared.language import LanguageChangeEventFilter
from ui.extends.shared.settings import ( from ui.extends.shared.settings import KNN_MODEL_FILE, PHASH_DATABASE_FILE
DEVICES_JSON_FILE, from ui.extends.tabs.tabOcr.tabOcr_Device import ScoreConverter, TabDeviceOcrRunnable
KNN_MODEL_FILE,
PHASH_DATABASE_FILE,
TESSERACT_FILE,
Settings,
)
from ui.extends.tabs.tabOcr.tabOcr_Device import (
ScoreConverter,
TabDeviceV2AutoRoisOcrRunnable,
TabDeviceV2OcrRunnable,
)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -38,17 +29,36 @@ class TabOcr_Device(Ui_TabOcr_Device, QWidget):
self.languageChangeEventFilter = LanguageChangeEventFilter(self) self.languageChangeEventFilter = LanguageChangeEventFilter(self)
self.installEventFilter(self.languageChangeEventFilter) self.installEventFilter(self.languageChangeEventFilter)
self.deviceFileSelector.filesSelected.connect(self.deviceFileSelected) # connect options checkBoxes & comboBoxes
self.knnModelSelector.filesSelected.connect(self.knnModelFileSelected) self.options_roisUseCustomCheckBox.toggled.connect(
self.phashDatabaseSelector.filesSelected.connect(self.phashDatabaseFileSelected) lambda useCustom: self.options_roisStackedWidget.setCurrentIndex(
1 if useCustom else 0
)
)
self.options_maskerUseCustomCheckBox.toggled.connect(
lambda useCustom: self.options_maskerStackedWidget.setCurrentIndex(
1 if useCustom else 0
)
)
self.options_usePresetCheckBox.toggled.connect(self.options_setUsePreset)
self.options_presetComboBox.currentIndexChanged.connect(
self.options_presetSelected
)
# fill option values
self.options_fillComboBoxes()
self.dependencies_knnModelSelector.filesSelected.connect(self.knnModelSelected)
self.dependencies_phashDatabaseSelector.filesSelected.connect(
self.phashDatabaseSelected
)
logger.info("Applying settings...") logger.info("Applying settings...")
self.deviceFileSelector.connectSettings(DEVICES_JSON_FILE) self.dependencies_knnModelSelector.connectSettings(KNN_MODEL_FILE)
self.knnModelSelector.connectSettings(KNN_MODEL_FILE) self.dependencies_phashDatabaseSelector.connectSettings(PHASH_DATABASE_FILE)
self.tesseractFileSelector.connectSettings(TESSERACT_FILE)
self.phashDatabaseSelector.connectSettings(PHASH_DATABASE_FILE) self.options_usePresetCheckBox.setChecked(True)
settings = Settings() self.options_usePresetCheckBox.setEnabled(False)
self.deviceComboBox.selectDevice(settings.deviceUuid())
self.ocrQueueModel = OcrQueueModel(self) self.ocrQueueModel = OcrQueueModel(self)
self.ocrQueue.setModel(self.ocrQueueModel) self.ocrQueue.setModel(self.ocrQueueModel)
@ -60,43 +70,76 @@ class TabOcr_Device(Ui_TabOcr_Device, QWidget):
# wizard.open() # wizard.open()
pass pass
@Slot() @Slot(bool)
def on_deviceUseAutoFactorCheckBox_stateChanged(self): def options_setUsePreset(self, usePreset: bool):
checkState = self.deviceUseAutoFactorCheckBox.checkState() self.options_roisUseCustomCheckBox.setChecked(not usePreset)
if checkState == Qt.CheckState.Checked: self.options_maskerUseCustomCheckBox.setChecked(not usePreset)
self.deviceDependenciesStackedWidget.setCurrentIndex(1) self.options_preciseControlWidget.setEnabled(not usePreset)
self.deviceComboBox.setCurrentIndex(-1) if not usePreset:
self.deviceFileSelector.setEnabled(False) self.options_presetComboBox.setCurrentIndex(-1)
self.deviceComboBox.setEnabled(False)
else:
self.deviceFileSelector.setEnabled(True)
self.deviceComboBox.setEnabled(True)
@Slot() @Slot(int)
def on_deviceComboBox_currentIndexChanged(self): def options_presetSelected(self, index: int):
self.changeDeviceDepStackedWidget() if index < 0:
self.options_roisComboBox.setCurrentIndex(-1)
self.options_maskerComboBox.setCurrentIndex(-1)
def changeDeviceDepStackedWidget(self): autoTypeString = self.options_presetComboBox.currentData()
device = self.deviceComboBox.currentData() roisAutoTypeIndex = self.options_roisComboBox.findData(autoTypeString)
if isinstance(device, (DeviceV1, DeviceV2)): maskerAutoTypeIndex = self.options_maskerComboBox.findData(autoTypeString)
self.deviceDependenciesStackedWidget.setCurrentIndex(device.version - 1) self.options_roisComboBox.setCurrentIndex(roisAutoTypeIndex)
self.options_maskerComboBox.setCurrentIndex(maskerAutoTypeIndex)
def deviceFileSelected(self): def options_fillComboBoxes(self):
if selectedFiles := self.deviceFileSelector.selectedFiles(): self.options_roisComboBox.addItem("RoisAutoT1", "AutoT1")
file = selectedFiles[0] self.options_roisComboBox.addItem("RoisAutoT2", "AutoT2")
self.deviceComboBox.loadDevicesJson(file) self.options_roisComboBox.setCurrentIndex(-1)
def knnModelFileSelected(self): self.options_maskerComboBox.addItem("MaskerAutoT1", "AutoT1")
if selectedFiles := self.knnModelSelector.selectedFiles(): self.options_maskerComboBox.addItem("MaskerAutoT2", "AutoT2")
self.knnModel = cv2.ml.KNearest.load(selectedFiles[0]) self.options_maskerComboBox.setCurrentIndex(-1)
def phashDatabaseFileSelected(self): self.options_presetComboBox.addItem("AutoT1 (ver <= 4.7.2)", "AutoT1")
if selectedFiles := self.phashDatabaseSelector.selectedFiles(): self.options_presetComboBox.addItem("AutoT2 (ver >= 5.0.0)", "AutoT2")
self.phashDatabase = ImagePHashDatabase(selectedFiles[0]) self.options_presetComboBox.setCurrentIndex(1)
def knnModelSelected(self):
try:
knnModelFile = self.dependencies_knnModelSelector.selectedFiles()[0]
self.knnModel = cv2.ml.KNearest.load(knnModelFile)
varCount = self.knnModel.getVarCount()
if varCount != 81:
self.dependencies_knnModelStatusLabel.setText(
f'<font color="darkorange">WARN</font>, varCount {varCount}'
)
else:
self.dependencies_knnModelStatusLabel.setText(
f'<font color="green">OK</font>, varCount {varCount}'
)
except Exception:
logger.exception("Error loading knn model:")
self.dependencies_knnModelStatusLabel.setText(
'<font color="red">Error</font>'
)
def phashDatabaseSelected(self):
try:
phashDbFile = self.dependencies_phashDatabaseSelector.selectedFiles()[0]
self.phashDatabase = ImagePhashDatabase(phashDbFile)
self.dependencies_phashDatabaseStatusLabel.setText(
f'<font color="green">OK</font>, '
f"J{len(self.phashDatabase.jacket_hashes)} "
f"PI{len(self.phashDatabase.partner_icon_hashes)}"
)
except Exception:
logger.exception("Error loading phash database:")
self.dependencies_phashDatabaseStatusLabel.setText(
'<font color="red">Error</font>'
)
@Slot() @Slot()
def on_ocr_addImageButton_clicked(self): def on_ocr_addImageButton_clicked(self):
files, _filter = QFileDialog.getOpenFileNames( files, _ = QFileDialog.getOpenFileNames(
self, None, "", "Image Files (*.png *.jpg *.jpeg *.bmp *.webp);;*" self, None, "", "Image Files (*.png *.jpg *.jpeg *.bmp *.webp);;*"
) )
filesNum = len(files) filesNum = len(files)
@ -114,30 +157,51 @@ class TabOcr_Device(Ui_TabOcr_Device, QWidget):
QApplication.processEvents() QApplication.processEvents()
self.ocrQueue.resizeTableView() self.ocrQueue.resizeTableView()
def deviceRois(self):
if self.options_roisUseCustomCheckBox.isChecked():
...
else:
selectedPreset = self.options_roisComboBox.currentData()
if selectedPreset == "AutoT1":
return DeviceRoisAutoT1
elif selectedPreset == "AutoT2":
return DeviceRoisAutoT2
else:
QMessageBox.critical(self, None, "Select a Rois preset first.")
return None
def deviceRoisMasker(self):
if self.options_maskerUseCustomCheckBox.isChecked():
...
else:
selectedPreset = self.options_maskerComboBox.currentData()
if selectedPreset == "AutoT1":
return DeviceRoisMaskerAutoT1()
elif selectedPreset == "AutoT2":
return DeviceRoisMaskerAutoT2()
else:
QMessageBox.critical(self, None, "Select a Masker preset first.")
return None
@Slot() @Slot()
def on_ocr_startButton_clicked(self): def on_ocr_startButton_clicked(self):
for row in range(self.ocrQueueModel.rowCount()): for row in range(self.ocrQueueModel.rowCount()):
index = self.ocrQueueModel.index(row, 0) index = self.ocrQueueModel.index(row, 0)
imagePath = index.data(OcrQueueModel.ImagePathRole) imagePath = index.data(OcrQueueModel.ImagePathRole)
if self.deviceUseAutoFactorCheckBox.checkState() == Qt.CheckState.Checked:
runnable = TabDeviceV2AutoRoisOcrRunnable( rois = self.deviceRois()
imagePath, masker = self.deviceRoisMasker()
self.knnModel,
self.phashDatabase, if rois is None or masker is None:
sizesV2=self.deviceSizesV2CheckBox.isChecked(), return
)
else: runnable = TabDeviceOcrRunnable(
runnable = TabDeviceV2OcrRunnable( imagePath, rois, masker, self.knnModel, self.phashDatabase
imagePath, )
self.deviceComboBox.currentData(),
self.knnModel,
self.phashDatabase,
sizesV2=self.deviceSizesV2CheckBox.isChecked(),
)
self.ocrQueueModel.setData(index, runnable, OcrQueueModel.OcrRunnableRole) self.ocrQueueModel.setData(index, runnable, OcrQueueModel.OcrRunnableRole)
self.ocrQueueModel.setData( self.ocrQueueModel.setData(
index, index,
ScoreConverter.deviceV2, ScoreConverter.device,
OcrQueueModel.ProcessOcrResultFuncRole, OcrQueueModel.ProcessOcrResultFuncRole,
) )
self.ocrQueueModel.startQueue() self.ocrQueueModel.startQueue()

View File

@ -180,19 +180,20 @@ class TabTools_Andreal(Ui_TabTools_Andreal, QWidget):
arguments = [ arguments = [
str(self.imageType()), str(self.imageType()),
f'--json-file="{jsonFile}"', "--json-file",
f"--img-version={self.imageVersion()}", jsonFile,
"--img-version",
str(self.imageVersion()),
] ]
if self.andrealFolderSelector.selectedFiles(): if self.andrealFolderSelector.selectedFiles():
arguments.append( arguments.append("--path")
f'--path="{self.andrealFolderSelector.selectedFiles()[0]}"' arguments.append(self.andrealFolderSelector.selectedFiles()[0])
)
if preview: if preview:
arguments.extend(["--img-format=jpg", "--img-quality=20"]) arguments.extend(["--img-format", "jpg", "--img-quality", "20"])
else: else:
arguments.append(f"--img-format={self.imageFormat()}") arguments.extend(["--img-format", self.imageFormat()])
if self.imageFormat() == "jpg": if self.imageFormat() == "jpg":
arguments.append(f"--img-quality={self.jpgQualitySpinBox.value()}") arguments.extend(["--img-quality", str(self.jpgQualitySpinBox.value())])
return arguments return arguments
def getAndrealJsonContent(self): def getAndrealJsonContent(self):

View File

@ -1,5 +1,4 @@
import logging import logging
import random
from arcaea_offline.calculate import calculate_constants_from_play_rating from arcaea_offline.calculate import calculate_constants_from_play_rating
from arcaea_offline.database import Database from arcaea_offline.database import Database
@ -7,11 +6,17 @@ from arcaea_offline.models import Chart, ScoreBest
from arcaea_offline.utils.rating import rating_class_to_text from arcaea_offline.utils.rating import rating_class_to_text
from arcaea_offline.utils.score import score_to_grade_text from arcaea_offline.utils.score import score_to_grade_text
from PySide6.QtCore import Slot from PySide6.QtCore import Slot
from PySide6.QtWidgets import QLabel, QWidget from PySide6.QtWidgets import QWidget
from ui.designer.tabs.tabTools.tabTools_ChartRecommend_ui import ( from ui.designer.tabs.tabTools.tabTools_ChartRecommend_ui import (
Ui_TabTools_ChartRecommend, Ui_TabTools_ChartRecommend,
) )
from ui.extends.tabs.tabTools.tabTools_ChartRecommend import (
ChartsModel,
ChartsWithScoreBestModel,
CustomChartDelegate,
CustomScoreBestDelegate,
)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -31,11 +36,25 @@ class TabTools_ChartRecommend(Ui_TabTools_ChartRecommend, QWidget):
self.db = Database() self.db = Database()
self.chartsByConstant = [] self.chartsByConstantModel = ChartsModel(self)
self.chartsRecommendFromPlayRating = [] self.chartsRecommendFromPlayRatingModel = ChartsWithScoreBestModel(self)
self.numLabelFormatString = "{} charts" self.numLabelFormatString = "{} charts"
self.chartDelegate = CustomChartDelegate(self)
self.scoreBestDelegate = CustomScoreBestDelegate(self)
self.chartsByConstant_modelView.setModel(self.chartsByConstantModel)
self.chartsByConstant_modelView.setItemDelegate(self.chartDelegate)
self.chartsRecommendFromPlayRating_modelView.setModel(
self.chartsRecommendFromPlayRatingModel
)
self.chartsRecommendFromPlayRating_modelView.setItemDelegateForColumn(
0, self.chartDelegate
)
self.chartsRecommendFromPlayRating_modelView.setItemDelegateForColumn(
1, self.scoreBestDelegate
)
self.chartsRecommendFromPlayRating_playRatingSpinBox.valueChanged.connect( self.chartsRecommendFromPlayRating_playRatingSpinBox.valueChanged.connect(
self.updateChartsRecommendFromPlayRating self.updateChartsRecommendFromPlayRating
) )
@ -43,81 +62,61 @@ class TabTools_ChartRecommend(Ui_TabTools_ChartRecommend, QWidget):
self.updateChartsRecommendFromPlayRating self.updateChartsRecommendFromPlayRating
) )
self.chartsByConstant_refreshButton.clicked.connect(self.fillChartsByConstant)
self.chartsRecommendFromPlayRating_refreshButton.clicked.connect(
self.fillChartsRecommendFromPlayRating
)
@Slot(float) @Slot(float)
def on_rangeFromPlayRating_playRatingSpinBox_valueChanged(self, value: float): def on_rangeFromPlayRating_playRatingSpinBox_valueChanged(self, value: float):
try: try:
result = calculate_constants_from_play_rating(value) constant = round(
exPlusLower, exPlusUpper = result.EXPlus value, self.rangeFromPlayRating_playRatingSpinBox.decimals()
exLower, exUpper = result.EX
aaLower, aaUpper = result.AA
self.rangeFromPlayRating_ExPlusLabel.setText(
f"{exPlusLower:.3f}~{exPlusUpper:.3f}"
) )
self.rangeFromPlayRating_ExLabel.setText(f"{exLower:.3f}~{exUpper:.3f}") result = calculate_constants_from_play_rating(constant)
self.rangeFromPlayRating_AaLabel.setText(f"{aaLower:.3f}~{aaUpper:.3f}") labels = [
self.rangeFromPlayRating_ExPlusLabel,
self.rangeFromPlayRating_ExLabel,
self.rangeFromPlayRating_AaLabel,
self.rangeFromPlayRating_ALabel,
self.rangeFromPlayRating_BLabel,
self.rangeFromPlayRating_CLabel,
]
for label, constantRange in zip(
labels,
[result.EXPlus, result.EX, result.AA, result.A, result.B, result.C],
):
label.setText(f"{constantRange[0]:.3f}~{constantRange[1]:.3f}")
except Exception: except Exception:
logging.exception("cannot calculate constant from play rating") logging.exception("Cannot calculate constant from play rating:")
self.rangeFromPlayRating_ExPlusLabel.setText("...") self.rangeFromPlayRating_ExPlusLabel.setText("...")
self.rangeFromPlayRating_ExLabel.setText("...") self.rangeFromPlayRating_ExLabel.setText("...")
self.rangeFromPlayRating_AaLabel.setText("...") self.rangeFromPlayRating_AaLabel.setText("...")
def fillChartsByConstant(self):
while item := self.chartsByConstant_gridLayout.takeAt(0):
item.widget().deleteLater()
charts = random.sample(
self.chartsByConstant, k=min(len(self.chartsByConstant), 6)
)
row = 0
for i, chart in enumerate(charts):
if i % 3 == 0:
row += 1
label = QLabel(self)
label.setText(chartToText(chart))
self.chartsByConstant_gridLayout.addWidget(label, row, i % 3)
@Slot(float) @Slot(float)
def on_chartsByConstant_constantSpinBox_valueChanged(self, value: float): def on_chartsByConstant_constantSpinBox_valueChanged(self, value: float):
self.chartsByConstant = self.db.get_charts_by_constant(int(value * 10)) constant = round(value, self.chartsByConstant_constantSpinBox.decimals())
chartsNum = len(self.chartsByConstant) charts = self.db.get_charts_by_constant(int(constant * 10))
self.chartsByConstant_refreshButton.setEnabled(chartsNum > 6) chartsNum = len(charts)
self.chartsByConstant_numLabel.setText( self.chartsByConstant_numLabel.setText(
self.numLabelFormatString.format(chartsNum) self.numLabelFormatString.format(chartsNum)
) )
self.fillChartsByConstant() self.chartsByConstantModel.setCharts(charts)
def fillChartsRecommendFromPlayRating(self):
while item := self.chartsRecommendFromPlayRating_gridLayout.takeAt(0):
item.widget().deleteLater()
charts = random.sample(
self.chartsRecommendFromPlayRating,
k=min(len(self.chartsRecommendFromPlayRating), 6),
)
row = 0
for i, chart in enumerate(charts):
if i % 3 == 0:
row += 1
scoreBest = self.db.get_score_best(chart.song_id, chart.rating_class)
label = QLabel(self)
label.setText(f"{chartToText(chart)}<br>-<br>{scoreBestToText(scoreBest)}")
self.chartsRecommendFromPlayRating_gridLayout.addWidget(label, row, i % 3)
def updateChartsRecommendFromPlayRating(self): def updateChartsRecommendFromPlayRating(self):
playRating = self.chartsRecommendFromPlayRating_playRatingSpinBox.value() playRating = self.chartsRecommendFromPlayRating_playRatingSpinBox.value()
bounds = self.chartsRecommendFromPlayRating_boundsSpinBox.value() bounds = self.chartsRecommendFromPlayRating_boundsSpinBox.value()
self.chartsRecommendFromPlayRating = self.db.recommend_charts( charts = self.db.recommend_charts(
playRating, bounds round(
playRating,
self.chartsRecommendFromPlayRating_playRatingSpinBox.decimals(),
),
round(
bounds,
self.chartsRecommendFromPlayRating_boundsSpinBox.decimals(),
),
) )
chartsNum = len(self.chartsRecommendFromPlayRating) chartsNum = len(charts)
self.chartsRecommendFromPlayRating_refreshButton.setEnabled(chartsNum > 6)
self.chartsRecommendFromPlayRating_numLabel.setText( self.chartsRecommendFromPlayRating_numLabel.setText(
self.numLabelFormatString.format(chartsNum) self.numLabelFormatString.format(chartsNum)
) )
self.fillChartsRecommendFromPlayRating() scores = [self.db.get_score_best(c.song_id, c.rating_class) for c in charts]
self.chartsRecommendFromPlayRatingModel.setChartAndScore(charts, scores)
self.chartsRecommendFromPlayRating_modelView.resizeRowsToContents()
self.chartsRecommendFromPlayRating_modelView.resizeColumnsToContents()

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

View File

@ -89,12 +89,12 @@
<translation>Continue</translation> <translation>Continue</translation>
</message> </message>
<message> <message>
<location filename="../../startup/databaseChecker.py" line="115"/> <location filename="../../startup/databaseChecker.py" line="117"/>
<source>dialog.tryInitExistingDatabase</source> <source>dialog.tryInitExistingDatabase</source>
<translation>The existing database doesn&apos;t seem to be initialized properly, try initialize again?</translation> <translation>The existing database doesn&apos;t seem to be initialized properly, try initialize again?</translation>
</message> </message>
<message> <message>
<location filename="../../startup/databaseChecker.py" line="131"/> <location filename="../../startup/databaseChecker.py" line="133"/>
<source>dialog.confirmNewDatabase</source> <source>dialog.confirmNewDatabase</source>
<translation>Database file does not exist. Create now?</translation> <translation>Database file does not exist. Create now?</translation>
</message> </message>
@ -225,8 +225,8 @@
</message> </message>
<message> <message>
<location filename="../../designer/components/ocrQueue.ui" line="34"/> <location filename="../../designer/components/ocrQueue.ui" line="34"/>
<source>icc.ignore</source> <source>icc.useQt</source>
<translation>Ignore</translation> <translation>Use Qt</translation>
</message> </message>
<message> <message>
<location filename="../../designer/components/ocrQueue.ui" line="41"/> <location filename="../../designer/components/ocrQueue.ui" line="41"/>
@ -477,12 +477,9 @@ validation</translation>
<location filename="../../implements/settings/settingsAndreal.py" line="82"/> <location filename="../../implements/settings/settingsAndreal.py" line="82"/>
<location filename="../../implements/settings/settingsAndreal.py" line="85"/> <location filename="../../implements/settings/settingsAndreal.py" line="85"/>
<location filename="../../implements/settings/settingsGeneral.py" line="107"/> <location filename="../../implements/settings/settingsGeneral.py" line="107"/>
<location filename="../../implements/settings/settingsOcr.py" line="190"/> <location filename="../../implements/settings/settingsOcr.py" line="104"/>
<location filename="../../implements/settings/settingsOcr.py" line="193"/> <location filename="../../implements/settings/settingsOcr.py" line="107"/>
<location filename="../../implements/settings/settingsOcr.py" line="196"/> <location filename="../../implements/settings/settingsOcr.py" line="110"/>
<location filename="../../implements/settings/settingsOcr.py" line="199"/>
<location filename="../../implements/settings/settingsOcr.py" line="202"/>
<location filename="../../implements/settings/settingsOcr.py" line="205"/>
<source>resetButton</source> <source>resetButton</source>
<translation>Reset</translation> <translation>Reset</translation>
</message> </message>
@ -517,37 +514,22 @@ validation</translation>
<translation>Database URL</translation> <translation>Database URL</translation>
</message> </message>
<message> <message>
<location filename="../../implements/settings/settingsOcr.py" line="187"/> <location filename="../../implements/settings/settingsOcr.py" line="101"/>
<source>ocr.title</source> <source>ocr.title</source>
<translation>OCR</translation> <translation>OCR</translation>
</message> </message>
<message> <message>
<location filename="../../implements/settings/settingsOcr.py" line="189"/> <location filename="../../implements/settings/settingsOcr.py" line="103"/>
<source>ocr.devicesJson.label</source>
<translation>Default devices.json</translation>
</message>
<message>
<location filename="../../implements/settings/settingsOcr.py" line="192"/>
<source>ocr.deviceUuid.label</source>
<translation>Default device</translation>
</message>
<message>
<location filename="../../implements/settings/settingsOcr.py" line="195"/>
<source>ocr.knnModelFile.label</source> <source>ocr.knnModelFile.label</source>
<translation>Default KNearest model</translation> <translation>Default KNearest model</translation>
</message> </message>
<message> <message>
<location filename="../../implements/settings/settingsOcr.py" line="198"/> <location filename="../../implements/settings/settingsOcr.py" line="106"/>
<source>ocr.b30KnnModelFile.label</source> <source>ocr.b30KnnModelFile.label</source>
<translation>Default B30 KNearest model</translation> <translation>Default B30 KNearest model</translation>
</message> </message>
<message> <message>
<location filename="../../implements/settings/settingsOcr.py" line="201"/> <location filename="../../implements/settings/settingsOcr.py" line="109"/>
<source>ocr.siftDatabaseFile.label</source>
<translation>Default SIFT database file</translation>
</message>
<message>
<location filename="../../implements/settings/settingsOcr.py" line="204"/>
<source>ocr.phashDatabaseFile.label</source> <source>ocr.phashDatabaseFile.label</source>
<translation>Default image PHash database</translation> <translation>Default image PHash database</translation>
</message> </message>
@ -696,6 +678,11 @@ validation</translation>
<source>tab.b30</source> <source>tab.b30</source>
<translation>B30</translation> <translation>B30</translation>
</message> </message>
<message>
<location filename="../../designer/tabs/tabOcrEntry.ui" line="34"/>
<source>tab.buildPHashDatabase</source>
<translation>Build pHash Database</translation>
</message>
</context> </context>
<context> <context>
<name>TabOcr_B30</name> <name>TabOcr_B30</name>
@ -705,24 +692,72 @@ validation</translation>
<translation>B30 Image Type</translation> <translation>B30 Image Type</translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabOcr/tabOcr_B30.ui" line="34"/> <location filename="../../designer/tabs/tabOcr/tabOcr_B30.ui" line="32"/>
<source>knnModelSelector.title</source> <source>dependencies.title</source>
<translation>Select KNearest Model</translation> <translation>OCR Dependencies</translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabOcr/tabOcr_B30.ui" line="46"/> <location filename="../../designer/tabs/tabOcr/tabOcr_B30.ui" line="38"/>
<source>b30KnnModelSelector.title</source> <source>dependencies.knnModel</source>
<translation>Select B30 Specialized KNearest Model</translation> <translation>KNearest model</translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabOcr/tabOcr_B30.ui" line="62"/> <location filename="../../designer/tabs/tabOcr/tabOcr_B30.ui" line="62"/>
<source>phashDatabaseSelector.title</source> <source>dependencies.b30KnnModel</source>
<translation>Select Image PHash Database</translation> <translation>B30 KNearest model</translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabOcr/tabOcr_B30.ui" line="74"/> <location filename="../../designer/tabs/tabOcr/tabOcr_B30.ui" line="86"/>
<source>imageSelector.title</source> <source>dependencies.phashDatabase</source>
<translation>Select Image</translation> <translation>Image pHash database</translation>
</message>
</context>
<context>
<name>TabOcr_BuildPHashDatabase</name>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_BuildPHashDatabase.ui" line="33"/>
<source>folders.title</source>
<translation>Data Folders</translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_BuildPHashDatabase.ui" line="41"/>
<source>folders.songDir</source>
<translation>Song jackets</translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_BuildPHashDatabase.ui" line="62"/>
<source>folders.charIconDir</source>
<translation>Partner icons</translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_BuildPHashDatabase.ui" line="84"/>
<source>options.title</source>
<translation>Options</translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_BuildPHashDatabase.ui" line="157"/>
<source>options.preprocessCharIcon</source>
<translation>Preprocess partner icons</translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_BuildPHashDatabase.ui" line="180"/>
<source>resetButton</source>
<translation>Reset</translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_BuildPHashDatabase.ui" line="199"/>
<source>[Reading images] %v/%m - %p%</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_BuildPHashDatabase.ui" line="215"/>
<source>[Calculate hashes] %v/%m - %p%</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_BuildPHashDatabase.ui" line="237"/>
<source>buildButton</source>
<translation>Build</translation>
</message> </message>
</context> </context>
<context> <context>
@ -734,28 +769,44 @@ validation</translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="27"/> <location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="27"/>
<source>deviceSelector.title</source> <source>options.title</source>
<translation>Select Device</translation> <translation>Options</translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="35"/> <location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="35"/>
<source>deviceSelector.useAutoFactor</source> <source>options.usePreset</source>
<translation>Auto calculate factor</translation> <translation>Use preset</translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="81"/> <location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="73"/>
<source>knnModelSelector.title</source> <source>options.rois</source>
<translation>Select KNearest Model</translation> <translation>Rois</translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="112"/> <location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="128"/>
<source>tesseractSelector.title</source> <source>options.masker</source>
<translation>Select tesseract Path</translation> <translation>Masker</translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="140"/> <location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="138"/>
<source>phashDatabaseSelector.title</source> <location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="145"/>
<translation>Select Image PHash Database</translation> <source>options.useCustom</source>
<translation>Use custom options</translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="203"/>
<source>dependencies.title</source>
<translation>OCR Dependencies</translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="209"/>
<source>dependencies.knnModel</source>
<translation>KNearest model</translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="246"/>
<source>dependencies.phashDatabase</source>
<translation>Image pHash database</translation>
</message> </message>
</context> </context>
<context> <context>
@ -1132,17 +1183,17 @@ validation</translation>
<translation>Calculate to Step</translation> <translation>Calculate to Step</translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabTools/tabTools_StepCalculator.ui" line="386"/> <location filename="../../designer/tabs/tabTools/tabTools_StepCalculator.ui" line="367"/>
<source>calculate.toStep.playResultLabel</source> <source>calculate.toStep.playResultLabel</source>
<translation>Play result</translation> <translation>Play result</translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabTools/tabTools_StepCalculator.ui" line="419"/> <location filename="../../designer/tabs/tabTools/tabTools_StepCalculator.ui" line="393"/>
<source>calculate.toStep.calculatePlayResultFromScoreButton</source> <source>calculate.toStep.calculatePlayResultFromScoreButton</source>
<translation>Calculate from Score</translation> <translation>Calculate from Score</translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabTools/tabTools_StepCalculator.ui" line="399"/> <location filename="../../designer/tabs/tabTools/tabTools_StepCalculator.ui" line="406"/>
<source>calculate.toStep.resultLabel</source> <source>calculate.toStep.resultLabel</source>
<translation>Result</translation> <translation>Result</translation>
</message> </message>
@ -1152,17 +1203,17 @@ validation</translation>
<translation>Detailed log output</translation> <translation>Detailed log output</translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabTools/tabTools_StepCalculator.ui" line="429"/> <location filename="../../designer/tabs/tabTools/tabTools_StepCalculator.ui" line="448"/>
<source>calculate.fromStep</source> <source>calculate.fromStep</source>
<translation>Calculate from Step</translation> <translation>Calculate from Step</translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabTools/tabTools_StepCalculator.ui" line="438"/> <location filename="../../designer/tabs/tabTools/tabTools_StepCalculator.ui" line="457"/>
<source>calculate.fromStep.targetStepLabel</source> <source>calculate.fromStep.targetStepLabel</source>
<translation>Target step value</translation> <translation>Target step value</translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabTools/tabTools_StepCalculator.ui" line="461"/> <location filename="../../designer/tabs/tabTools/tabTools_StepCalculator.ui" line="480"/>
<source>calculate.fromStep.resultLabel</source> <source>calculate.fromStep.resultLabel</source>
<translation>Result (play rating)</translation> <translation>Result (play rating)</translation>
</message> </message>

View File

@ -89,12 +89,12 @@
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../../startup/databaseChecker.py" line="115"/> <location filename="../../startup/databaseChecker.py" line="117"/>
<source>dialog.tryInitExistingDatabase</source> <source>dialog.tryInitExistingDatabase</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../../startup/databaseChecker.py" line="131"/> <location filename="../../startup/databaseChecker.py" line="133"/>
<source>dialog.confirmNewDatabase</source> <source>dialog.confirmNewDatabase</source>
<translation></translation> <translation></translation>
</message> </message>
@ -225,13 +225,13 @@
</message> </message>
<message> <message>
<location filename="../../designer/components/ocrQueue.ui" line="34"/> <location filename="../../designer/components/ocrQueue.ui" line="34"/>
<source>icc.ignore</source> <source>icc.useQt</source>
<translation></translation> <translation>使 Qt</translation>
</message> </message>
<message> <message>
<location filename="../../designer/components/ocrQueue.ui" line="41"/> <location filename="../../designer/components/ocrQueue.ui" line="41"/>
<source>icc.usePIL</source> <source>icc.usePIL</source>
<translation>使 PIL </translation> <translation>使 PIL</translation>
</message> </message>
<message> <message>
<location filename="../../designer/components/ocrQueue.ui" line="51"/> <location filename="../../designer/components/ocrQueue.ui" line="51"/>
@ -476,12 +476,9 @@
<location filename="../../implements/settings/settingsAndreal.py" line="82"/> <location filename="../../implements/settings/settingsAndreal.py" line="82"/>
<location filename="../../implements/settings/settingsAndreal.py" line="85"/> <location filename="../../implements/settings/settingsAndreal.py" line="85"/>
<location filename="../../implements/settings/settingsGeneral.py" line="107"/> <location filename="../../implements/settings/settingsGeneral.py" line="107"/>
<location filename="../../implements/settings/settingsOcr.py" line="190"/> <location filename="../../implements/settings/settingsOcr.py" line="104"/>
<location filename="../../implements/settings/settingsOcr.py" line="193"/> <location filename="../../implements/settings/settingsOcr.py" line="107"/>
<location filename="../../implements/settings/settingsOcr.py" line="196"/> <location filename="../../implements/settings/settingsOcr.py" line="110"/>
<location filename="../../implements/settings/settingsOcr.py" line="199"/>
<location filename="../../implements/settings/settingsOcr.py" line="202"/>
<location filename="../../implements/settings/settingsOcr.py" line="205"/>
<source>resetButton</source> <source>resetButton</source>
<translation></translation> <translation></translation>
</message> </message>
@ -516,37 +513,22 @@
<translation> URL</translation> <translation> URL</translation>
</message> </message>
<message> <message>
<location filename="../../implements/settings/settingsOcr.py" line="187"/> <location filename="../../implements/settings/settingsOcr.py" line="101"/>
<source>ocr.title</source> <source>ocr.title</source>
<translation>OCR</translation> <translation>OCR</translation>
</message> </message>
<message> <message>
<location filename="../../implements/settings/settingsOcr.py" line="189"/> <location filename="../../implements/settings/settingsOcr.py" line="103"/>
<source>ocr.devicesJson.label</source>
<translation></translation>
</message>
<message>
<location filename="../../implements/settings/settingsOcr.py" line="192"/>
<source>ocr.deviceUuid.label</source>
<translation></translation>
</message>
<message>
<location filename="../../implements/settings/settingsOcr.py" line="195"/>
<source>ocr.knnModelFile.label</source> <source>ocr.knnModelFile.label</source>
<translation> KNearest </translation> <translation> KNearest </translation>
</message> </message>
<message> <message>
<location filename="../../implements/settings/settingsOcr.py" line="198"/> <location filename="../../implements/settings/settingsOcr.py" line="106"/>
<source>ocr.b30KnnModelFile.label</source> <source>ocr.b30KnnModelFile.label</source>
<translation> B30 KNearest </translation> <translation> B30 KNearest </translation>
</message> </message>
<message> <message>
<location filename="../../implements/settings/settingsOcr.py" line="201"/> <location filename="../../implements/settings/settingsOcr.py" line="109"/>
<source>ocr.siftDatabaseFile.label</source>
<translation> SIFT </translation>
</message>
<message>
<location filename="../../implements/settings/settingsOcr.py" line="204"/>
<source>ocr.phashDatabaseFile.label</source> <source>ocr.phashDatabaseFile.label</source>
<translation> PHash </translation> <translation> PHash </translation>
</message> </message>
@ -695,6 +677,11 @@
<source>tab.b30</source> <source>tab.b30</source>
<translation>B30</translation> <translation>B30</translation>
</message> </message>
<message>
<location filename="../../designer/tabs/tabOcrEntry.ui" line="34"/>
<source>tab.buildPHashDatabase</source>
<translation> pHash </translation>
</message>
</context> </context>
<context> <context>
<name>TabOcr_B30</name> <name>TabOcr_B30</name>
@ -704,24 +691,72 @@
<translation>B30 </translation> <translation>B30 </translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabOcr/tabOcr_B30.ui" line="34"/> <location filename="../../designer/tabs/tabOcr/tabOcr_B30.ui" line="32"/>
<source>knnModelSelector.title</source> <source>dependencies.title</source>
<translation> KNearest </translation> <translation>OCR </translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabOcr/tabOcr_B30.ui" line="46"/> <location filename="../../designer/tabs/tabOcr/tabOcr_B30.ui" line="38"/>
<source>b30KnnModelSelector.title</source> <source>dependencies.knnModel</source>
<translation> B30 KNearest </translation> <translation>KNearest </translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabOcr/tabOcr_B30.ui" line="62"/> <location filename="../../designer/tabs/tabOcr/tabOcr_B30.ui" line="62"/>
<source>phashDatabaseSelector.title</source> <source>dependencies.b30KnnModel</source>
<translation> PHash </translation> <translation>B30 KNearest </translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabOcr/tabOcr_B30.ui" line="74"/> <location filename="../../designer/tabs/tabOcr/tabOcr_B30.ui" line="86"/>
<source>imageSelector.title</source> <source>dependencies.phashDatabase</source>
<translation></translation> <translation> pHash </translation>
</message>
</context>
<context>
<name>TabOcr_BuildPHashDatabase</name>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_BuildPHashDatabase.ui" line="33"/>
<source>folders.title</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_BuildPHashDatabase.ui" line="41"/>
<source>folders.songDir</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_BuildPHashDatabase.ui" line="62"/>
<source>folders.charIconDir</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_BuildPHashDatabase.ui" line="84"/>
<source>options.title</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_BuildPHashDatabase.ui" line="157"/>
<source>options.preprocessCharIcon</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_BuildPHashDatabase.ui" line="180"/>
<source>resetButton</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_BuildPHashDatabase.ui" line="199"/>
<source>[Reading images] %v/%m - %p%</source>
<translation>[] %v/%m - %p%</translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_BuildPHashDatabase.ui" line="215"/>
<source>[Calculate hashes] %v/%m - %p%</source>
<translation>[] %v/%m - %p%</translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_BuildPHashDatabase.ui" line="237"/>
<source>buildButton</source>
<translation></translation>
</message> </message>
</context> </context>
<context> <context>
@ -733,28 +768,44 @@
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="27"/> <location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="27"/>
<source>deviceSelector.title</source> <source>options.title</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="35"/> <location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="35"/>
<source>deviceSelector.useAutoFactor</source> <source>options.usePreset</source>
<translation> factor</translation> <translation>使</translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="81"/> <location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="73"/>
<source>knnModelSelector.title</source> <source>options.rois</source>
<translation> KNearest </translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="112"/> <location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="128"/>
<source>tesseractSelector.title</source> <source>options.masker</source>
<translation> tesseract </translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="140"/> <location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="138"/>
<source>phashDatabaseSelector.title</source> <location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="145"/>
<translation> PHash </translation> <source>options.useCustom</source>
<translation>使</translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="203"/>
<source>dependencies.title</source>
<translation>OCR </translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="209"/>
<source>dependencies.knnModel</source>
<translation>KNearest </translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_Device.ui" line="246"/>
<source>dependencies.phashDatabase</source>
<translation> pHash </translation>
</message> </message>
</context> </context>
<context> <context>
@ -1131,17 +1182,17 @@
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabTools/tabTools_StepCalculator.ui" line="386"/> <location filename="../../designer/tabs/tabTools/tabTools_StepCalculator.ui" line="367"/>
<source>calculate.toStep.playResultLabel</source> <source>calculate.toStep.playResultLabel</source>
<translation> PTT</translation> <translation> PTT</translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabTools/tabTools_StepCalculator.ui" line="419"/> <location filename="../../designer/tabs/tabTools/tabTools_StepCalculator.ui" line="393"/>
<source>calculate.toStep.calculatePlayResultFromScoreButton</source> <source>calculate.toStep.calculatePlayResultFromScoreButton</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabTools/tabTools_StepCalculator.ui" line="399"/> <location filename="../../designer/tabs/tabTools/tabTools_StepCalculator.ui" line="406"/>
<source>calculate.toStep.resultLabel</source> <source>calculate.toStep.resultLabel</source>
<translation></translation> <translation></translation>
</message> </message>
@ -1151,17 +1202,17 @@
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabTools/tabTools_StepCalculator.ui" line="429"/> <location filename="../../designer/tabs/tabTools/tabTools_StepCalculator.ui" line="448"/>
<source>calculate.fromStep</source> <source>calculate.fromStep</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabTools/tabTools_StepCalculator.ui" line="438"/> <location filename="../../designer/tabs/tabTools/tabTools_StepCalculator.ui" line="457"/>
<source>calculate.fromStep.targetStepLabel</source> <source>calculate.fromStep.targetStepLabel</source>
<translation> STEP </translation> <translation> STEP </translation>
</message> </message>
<message> <message>
<location filename="../../designer/tabs/tabTools/tabTools_StepCalculator.ui" line="461"/> <location filename="../../designer/tabs/tabTools/tabTools_StepCalculator.ui" line="480"/>
<source>calculate.fromStep.resultLabel</source> <source>calculate.fromStep.resultLabel</source>
<translation> PTT</translation> <translation> PTT</translation>
</message> </message>

View File

@ -0,0 +1,35 @@
{
"__COMMENT__": "1: EASY, 2: HARD",
"0": 1,
"0u": 1,
"7": 2,
"9": 1,
"10": 2,
"10u": 2,
"15": 1,
"16": 1,
"20": 1,
"28": 2,
"28u": 2,
"29": 2,
"29u": 2,
"35": 2,
"36": 2,
"36u": 2,
"37": 2,
"41": 2,
"42": 2,
"42u": 2,
"43": 2,
"43u": 2,
"54": 2,
"55": 2,
"57": 2,
"61": 2,
"64": 2,
"66": 2,
"66u": 2,
"67": 2,
"68": 1,
"70": 2
}

View File

@ -4,8 +4,11 @@
<file>VERSION</file> <file>VERSION</file>
<file>LICENSE</file> <file>LICENSE</file>
<file>partnerModifiers.json</file>
<file>images/icon.png</file> <file>images/icon.png</file>
<file>images/logo.png</file> <file>images/logo.png</file>
<file>images/jacket-placeholder.png</file>
<file>images/stepCalculator/stamina.png</file> <file>images/stepCalculator/stamina.png</file>
<file>images/stepCalculator/play.png</file> <file>images/stepCalculator/play.png</file>
<file>images/stepCalculator/memory-boost.png</file> <file>images/stepCalculator/memory-boost.png</file>

View File

@ -74,6 +74,7 @@ class DatabaseChecker(Ui_DatabaseChecker, QDialog):
db = Database(create_engine(dbSqliteUrl)) db = Database(create_engine(dbSqliteUrl))
if db.check_init(): if db.check_init():
flags |= DatabaseCheckerResult.Initted flags |= DatabaseCheckerResult.Initted
self.settings.setDatabaseUrl(self.dbSqliteUrl().toString())
return flags return flags
@ -102,6 +103,7 @@ class DatabaseChecker(Ui_DatabaseChecker, QDialog):
@Slot() @Slot()
def on_confirmDbPathButton_clicked(self): def on_confirmDbPathButton_clicked(self):
dbSqliteUrl = self.dbSqliteUrl() dbSqliteUrl = self.dbSqliteUrl()
self.settings.setDatabaseUrl(dbSqliteUrl.toString())
result = self.confirmDb() result = self.confirmDb()
if result & DatabaseCheckerResult.Initted: if result & DatabaseCheckerResult.Initted: