diff --git a/index.py b/index.py
index 94d0584..de517b3 100644
--- a/index.py
+++ b/index.py
@@ -7,10 +7,10 @@ from PySide6.QtCore import QLibraryInfo, QLocale, QTranslator
from PySide6.QtGui import QIcon
from PySide6.QtWidgets import QApplication, QDialog, QMessageBox
-from ui.startup.databaseChecker import DatabaseChecker
-from ui.implements.mainwindow import MainWindow
import ui.resources.images.images_rc
import ui.resources.translations.translations_rc
+from ui.implements.mainwindow import MainWindow
+from ui.startup.databaseChecker import DatabaseChecker
logging.basicConfig(level=logging.INFO, stream=sys.stdout, force=True)
diff --git a/ui/designer/components/ocrQueue.ui b/ui/designer/components/ocrQueue.ui
index 1d17689..03980ad 100644
--- a/ui/designer/components/ocrQueue.ui
+++ b/ui/designer/components/ocrQueue.ui
@@ -11,7 +11,7 @@
- OcrQueue
+ OcrQueue
-
diff --git a/ui/designer/components/ocrQueue_ui.py b/ui/designer/components/ocrQueue_ui.py
index eba0cb6..cccc531 100644
--- a/ui/designer/components/ocrQueue_ui.py
+++ b/ui/designer/components/ocrQueue_ui.py
@@ -25,6 +25,7 @@ class Ui_OcrQueue(object):
if not OcrQueue.objectName():
OcrQueue.setObjectName(u"OcrQueue")
OcrQueue.resize(741, 372)
+ OcrQueue.setWindowTitle(u"OcrQueue")
self.horizontalLayout_2 = QHBoxLayout(OcrQueue)
self.horizontalLayout_2.setObjectName(u"horizontalLayout_2")
self.groupBox_3 = QGroupBox(OcrQueue)
@@ -119,7 +120,6 @@ class Ui_OcrQueue(object):
# setupUi
def retranslateUi(self, OcrQueue):
- OcrQueue.setWindowTitle(QCoreApplication.translate("OcrQueue", u"OcrQueue", None))
self.groupBox_3.setTitle(QCoreApplication.translate("OcrQueue", u"ocr.queue.title", None))
self.ocr_addImageButton.setText(QCoreApplication.translate("OcrQueue", u"ocr.queue.addImageButton", None))
self.ocr_removeSelectedButton.setText(QCoreApplication.translate("OcrQueue", u"ocr.queue.removeSelected", None))
@@ -129,5 +129,6 @@ class Ui_OcrQueue(object):
self.ocr_acceptSelectedButton.setText(QCoreApplication.translate("OcrQueue", u"ocr.results.acceptSelectedButton", None))
self.ocr_acceptAllButton.setText(QCoreApplication.translate("OcrQueue", u"ocr.results.acceptAllButton", None))
self.ocr_ignoreValidateCheckBox.setText(QCoreApplication.translate("OcrQueue", u"ocr.results.ignoreValidate", None))
+ pass
# retranslateUi
diff --git a/ui/designer/mainwindow.ui b/ui/designer/mainwindow.ui
index 4aaa966..2752238 100644
--- a/ui/designer/mainwindow.ui
+++ b/ui/designer/mainwindow.ui
@@ -35,7 +35,7 @@
tab.db
-
+
tab.ocr
@@ -86,6 +86,12 @@
ui.implements.tabs.tabDbEntry
1
+
+ TabOcrEntry
+ QWidget
+ ui.implements.tabs.tabOcrEntry
+ 1
+
diff --git a/ui/designer/mainwindow_ui.py b/ui/designer/mainwindow_ui.py
index 506e2a2..3e2a6fc 100644
--- a/ui/designer/mainwindow_ui.py
+++ b/ui/designer/mainwindow_ui.py
@@ -3,7 +3,7 @@
################################################################################
## Form generated from reading UI file 'mainwindow.ui'
##
-## Created by: Qt User Interface Compiler version 6.5.0
+## Created by: Qt User Interface Compiler version 6.5.1
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
@@ -21,6 +21,7 @@ from PySide6.QtWidgets import (QApplication, QMainWindow, QSizePolicy, QTabWidge
from ui.implements.tabs.tabAbout import TabAbout
from ui.implements.tabs.tabDbEntry import TabDbEntry
from ui.implements.tabs.tabInputScore import TabInputScore
+from ui.implements.tabs.tabOcrEntry import TabOcrEntry
from ui.implements.tabs.tabOverview import TabOverview
from ui.implements.tabs.tabSettings import TabSettings
@@ -45,7 +46,7 @@ class Ui_MainWindow(object):
self.tab_db = TabDbEntry()
self.tab_db.setObjectName(u"tab_db")
self.tabWidget.addTab(self.tab_db, "")
- self.tab_ocr = QWidget()
+ self.tab_ocr = TabOcrEntry()
self.tab_ocr.setObjectName(u"tab_ocr")
self.tabWidget.addTab(self.tab_ocr, "")
self.tab_settings = TabSettings()
diff --git a/ui/designer/tabs/tabOcr/tabOcr_B30.ui b/ui/designer/tabs/tabOcr/tabOcr_B30.ui
new file mode 100644
index 0000000..42df52c
--- /dev/null
+++ b/ui/designer/tabs/tabOcr/tabOcr_B30.ui
@@ -0,0 +1,113 @@
+
+
+ TabOcr_B30
+
+
+
+ 0
+ 0
+ 555
+ 461
+
+
+
+ TabOcr_B30
+
+
+
-
+
+
+ b30type
+
+
+
-
+
+
+
+
+
+ -
+
+
-
+
+
+ knnModelSelector.title
+
+
+
-
+
+
+
+
+
+ -
+
+
+ b30KnnModelSelector.title
+
+
+
-
+
+
+
+
+
+
+
+ -
+
+
-
+
+
+ siftDatabaseSelector.title
+
+
+
-
+
+
+
+
+
+ -
+
+
+ imageSelector.title
+
+
+
-
+
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+
+
+ FileSelector
+ QWidget
+ ui.implements.components.fileSelector
+ 1
+
+
+ OcrQueue
+ QWidget
+ ui.implements.components.ocrQueue
+ 1
+
+
+
+
+
diff --git a/ui/designer/tabs/tabOcr/tabOcr_B30_ui.py b/ui/designer/tabs/tabOcr/tabOcr_B30_ui.py
new file mode 100644
index 0000000..adf42f1
--- /dev/null
+++ b/ui/designer/tabs/tabOcr/tabOcr_B30_ui.py
@@ -0,0 +1,126 @@
+# -*- coding: utf-8 -*-
+
+################################################################################
+## Form generated from reading UI file 'tabOcr_B30.ui'
+##
+## Created by: Qt User Interface Compiler version 6.5.1
+##
+## WARNING! All changes made in this file will be lost when recompiling UI file!
+################################################################################
+
+from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
+ QMetaObject, QObject, QPoint, QRect,
+ QSize, QTime, QUrl, Qt)
+from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
+ QFont, QFontDatabase, QGradient, QIcon,
+ QImage, QKeySequence, QLinearGradient, QPainter,
+ QPalette, QPixmap, QRadialGradient, QTransform)
+from PySide6.QtWidgets import (QApplication, QComboBox, QGroupBox, QHBoxLayout,
+ QSizePolicy, QVBoxLayout, QWidget)
+
+from ui.implements.components.fileSelector import FileSelector
+from ui.implements.components.ocrQueue import OcrQueue
+
+class Ui_TabOcr_B30(object):
+ def setupUi(self, TabOcr_B30):
+ if not TabOcr_B30.objectName():
+ TabOcr_B30.setObjectName(u"TabOcr_B30")
+ TabOcr_B30.resize(555, 461)
+ TabOcr_B30.setWindowTitle(u"TabOcr_B30")
+ self.verticalLayout_3 = QVBoxLayout(TabOcr_B30)
+ self.verticalLayout_3.setObjectName(u"verticalLayout_3")
+ self.groupBox = QGroupBox(TabOcr_B30)
+ self.groupBox.setObjectName(u"groupBox")
+ self.verticalLayout = QVBoxLayout(self.groupBox)
+ self.verticalLayout.setObjectName(u"verticalLayout")
+ self.b30TypeComboBox = QComboBox(self.groupBox)
+ self.b30TypeComboBox.setObjectName(u"b30TypeComboBox")
+
+ self.verticalLayout.addWidget(self.b30TypeComboBox)
+
+
+ self.verticalLayout_3.addWidget(self.groupBox)
+
+ self.horizontalLayout = QHBoxLayout()
+ self.horizontalLayout.setObjectName(u"horizontalLayout")
+ self.groupBox_3 = QGroupBox(TabOcr_B30)
+ self.groupBox_3.setObjectName(u"groupBox_3")
+ self.verticalLayout_4 = QVBoxLayout(self.groupBox_3)
+ self.verticalLayout_4.setObjectName(u"verticalLayout_4")
+ self.knnModelSelector = FileSelector(self.groupBox_3)
+ self.knnModelSelector.setObjectName(u"knnModelSelector")
+
+ self.verticalLayout_4.addWidget(self.knnModelSelector)
+
+
+ self.horizontalLayout.addWidget(self.groupBox_3)
+
+ self.groupBox_5 = QGroupBox(TabOcr_B30)
+ self.groupBox_5.setObjectName(u"groupBox_5")
+ self.verticalLayout_6 = QVBoxLayout(self.groupBox_5)
+ self.verticalLayout_6.setObjectName(u"verticalLayout_6")
+ self.b30KnnModelSelector = FileSelector(self.groupBox_5)
+ self.b30KnnModelSelector.setObjectName(u"b30KnnModelSelector")
+
+ self.verticalLayout_6.addWidget(self.b30KnnModelSelector)
+
+
+ self.horizontalLayout.addWidget(self.groupBox_5)
+
+
+ self.verticalLayout_3.addLayout(self.horizontalLayout)
+
+ self.horizontalLayout_3 = QHBoxLayout()
+ self.horizontalLayout_3.setObjectName(u"horizontalLayout_3")
+ self.groupBox_4 = QGroupBox(TabOcr_B30)
+ self.groupBox_4.setObjectName(u"groupBox_4")
+ self.verticalLayout_5 = QVBoxLayout(self.groupBox_4)
+ self.verticalLayout_5.setObjectName(u"verticalLayout_5")
+ self.siftDatabaseSelector = FileSelector(self.groupBox_4)
+ self.siftDatabaseSelector.setObjectName(u"siftDatabaseSelector")
+
+ self.verticalLayout_5.addWidget(self.siftDatabaseSelector)
+
+
+ self.horizontalLayout_3.addWidget(self.groupBox_4)
+
+ self.groupBox_2 = QGroupBox(TabOcr_B30)
+ self.groupBox_2.setObjectName(u"groupBox_2")
+ self.verticalLayout_2 = QVBoxLayout(self.groupBox_2)
+ 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.horizontalLayout_3.addWidget(self.groupBox_2)
+
+
+ self.verticalLayout_3.addLayout(self.horizontalLayout_3)
+
+ self.ocrQueue = OcrQueue(TabOcr_B30)
+ self.ocrQueue.setObjectName(u"ocrQueue")
+ sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.ocrQueue.sizePolicy().hasHeightForWidth())
+ self.ocrQueue.setSizePolicy(sizePolicy)
+
+ self.verticalLayout_3.addWidget(self.ocrQueue)
+
+
+ self.retranslateUi(TabOcr_B30)
+
+ QMetaObject.connectSlotsByName(TabOcr_B30)
+ # setupUi
+
+ def retranslateUi(self, TabOcr_B30):
+ self.groupBox.setTitle(QCoreApplication.translate("TabOcr_B30", u"b30type", None))
+ self.groupBox_3.setTitle(QCoreApplication.translate("TabOcr_B30", u"knnModelSelector.title", None))
+ self.groupBox_5.setTitle(QCoreApplication.translate("TabOcr_B30", u"b30KnnModelSelector.title", None))
+ self.groupBox_4.setTitle(QCoreApplication.translate("TabOcr_B30", u"siftDatabaseSelector.title", None))
+ self.groupBox_2.setTitle(QCoreApplication.translate("TabOcr_B30", u"imageSelector.title", None))
+ pass
+ # retranslateUi
+
diff --git a/ui/designer/tabs/tabOcr.ui b/ui/designer/tabs/tabOcr/tabOcr_Device.ui
similarity index 92%
rename from ui/designer/tabs/tabOcr.ui
rename to ui/designer/tabs/tabOcr/tabOcr_Device.ui
index 82c92ec..09be870 100644
--- a/ui/designer/tabs/tabOcr.ui
+++ b/ui/designer/tabs/tabOcr/tabOcr_Device.ui
@@ -1,7 +1,7 @@
- TabOcr
-
+ TabOcr_Device
+
0
@@ -11,7 +11,7 @@
- TabOcr
+ TabOcr_Device
-
@@ -136,21 +136,13 @@
-
-
+
0
0
-
- ocr.title
-
-
-
-
-
-
-
diff --git a/ui/designer/tabs/tabOcr_ui.py b/ui/designer/tabs/tabOcr/tabOcr_Device_ui.py
similarity index 79%
rename from ui/designer/tabs/tabOcr_ui.py
rename to ui/designer/tabs/tabOcr/tabOcr_Device_ui.py
index a72a97e..7c4f81c 100644
--- a/ui/designer/tabs/tabOcr_ui.py
+++ b/ui/designer/tabs/tabOcr/tabOcr_Device_ui.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
################################################################################
-## Form generated from reading UI file 'tabOcr.ui'
+## Form generated from reading UI file 'tabOcr_Device.ui'
##
## Created by: Qt User Interface Compiler version 6.5.1
##
@@ -22,20 +22,20 @@ from ui.implements.components.devicesComboBox import DevicesComboBox
from ui.implements.components.fileSelector import FileSelector
from ui.implements.components.ocrQueue import OcrQueue
-class Ui_TabOcr(object):
- def setupUi(self, TabOcr):
- if not TabOcr.objectName():
- TabOcr.setObjectName(u"TabOcr")
- TabOcr.resize(632, 527)
- TabOcr.setWindowTitle(u"TabOcr")
- self.verticalLayout_3 = QVBoxLayout(TabOcr)
+class Ui_TabOcr_Device(object):
+ def setupUi(self, TabOcr_Device):
+ if not TabOcr_Device.objectName():
+ TabOcr_Device.setObjectName(u"TabOcr_Device")
+ TabOcr_Device.resize(632, 527)
+ TabOcr_Device.setWindowTitle(u"TabOcr_Device")
+ self.verticalLayout_3 = QVBoxLayout(TabOcr_Device)
self.verticalLayout_3.setObjectName(u"verticalLayout_3")
- self.openWizardButton = QPushButton(TabOcr)
+ self.openWizardButton = QPushButton(TabOcr_Device)
self.openWizardButton.setObjectName(u"openWizardButton")
self.verticalLayout_3.addWidget(self.openWizardButton)
- self.groupBox = QGroupBox(TabOcr)
+ self.groupBox = QGroupBox(TabOcr_Device)
self.groupBox.setObjectName(u"groupBox")
self.verticalLayout = QVBoxLayout(self.groupBox)
self.verticalLayout.setObjectName(u"verticalLayout")
@@ -52,7 +52,7 @@ class Ui_TabOcr(object):
self.verticalLayout_3.addWidget(self.groupBox)
- self.horizontalWidget = QWidget(TabOcr)
+ self.horizontalWidget = QWidget(TabOcr_Device)
self.horizontalWidget.setObjectName(u"horizontalWidget")
sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Maximum)
sizePolicy.setHorizontalStretch(0)
@@ -118,39 +118,31 @@ class Ui_TabOcr(object):
self.verticalLayout_3.addWidget(self.horizontalWidget)
- self.groupBox_2 = QGroupBox(TabOcr)
- self.groupBox_2.setObjectName(u"groupBox_2")
+ self.ocrQueue = OcrQueue(TabOcr_Device)
+ self.ocrQueue.setObjectName(u"ocrQueue")
sizePolicy1 = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding)
sizePolicy1.setHorizontalStretch(0)
sizePolicy1.setVerticalStretch(0)
- sizePolicy1.setHeightForWidth(self.groupBox_2.sizePolicy().hasHeightForWidth())
- self.groupBox_2.setSizePolicy(sizePolicy1)
- self.horizontalLayout = QHBoxLayout(self.groupBox_2)
- self.horizontalLayout.setObjectName(u"horizontalLayout")
- self.ocrQueue = OcrQueue(self.groupBox_2)
- self.ocrQueue.setObjectName(u"ocrQueue")
+ sizePolicy1.setHeightForWidth(self.ocrQueue.sizePolicy().hasHeightForWidth())
+ self.ocrQueue.setSizePolicy(sizePolicy1)
- self.horizontalLayout.addWidget(self.ocrQueue)
+ self.verticalLayout_3.addWidget(self.ocrQueue)
- self.verticalLayout_3.addWidget(self.groupBox_2)
-
-
- self.retranslateUi(TabOcr)
+ self.retranslateUi(TabOcr_Device)
self.deviceDependenciesStackedWidget.setCurrentIndex(0)
- QMetaObject.connectSlotsByName(TabOcr)
+ QMetaObject.connectSlotsByName(TabOcr_Device)
# setupUi
- def retranslateUi(self, TabOcr):
- self.openWizardButton.setText(QCoreApplication.translate("TabOcr", u"openWizardButton", None))
- self.groupBox.setTitle(QCoreApplication.translate("TabOcr", u"deviceSelector.title", None))
- self.groupBox_6.setTitle(QCoreApplication.translate("TabOcr", u"knnModelSelector.title", None))
- self.groupBox_4.setTitle(QCoreApplication.translate("TabOcr", u"tesseractSelector.title", None))
- self.groupBox_5.setTitle(QCoreApplication.translate("TabOcr", u"siftDatabaseSelector.title", None))
- self.groupBox_2.setTitle(QCoreApplication.translate("TabOcr", u"ocr.title", None))
+ def retranslateUi(self, TabOcr_Device):
+ self.openWizardButton.setText(QCoreApplication.translate("TabOcr_Device", u"openWizardButton", None))
+ self.groupBox.setTitle(QCoreApplication.translate("TabOcr_Device", u"deviceSelector.title", None))
+ self.groupBox_6.setTitle(QCoreApplication.translate("TabOcr_Device", u"knnModelSelector.title", None))
+ self.groupBox_4.setTitle(QCoreApplication.translate("TabOcr_Device", u"tesseractSelector.title", None))
+ self.groupBox_5.setTitle(QCoreApplication.translate("TabOcr_Device", u"siftDatabaseSelector.title", None))
pass
# retranslateUi
diff --git a/ui/designer/tabs/tabOcrEntry.ui b/ui/designer/tabs/tabOcrEntry.ui
new file mode 100644
index 0000000..7314699
--- /dev/null
+++ b/ui/designer/tabs/tabOcrEntry.ui
@@ -0,0 +1,52 @@
+
+
+ TabOcrEntry
+
+
+
+ 0
+ 0
+ 600
+ 478
+
+
+
+ TabOcrEntry
+
+
+ -
+
+
+ 0
+
+
+
+ tab.device
+
+
+
+
+ tab.b30
+
+
+
+
+
+
+
+
+ TabOcr_Device
+ QWidget
+ ui.implements.tabs.tabOcr.tabOcr_Device
+ 1
+
+
+ TabOcr_B30
+ QWidget
+ ui.implements.tabs.tabOcr.tabOcr_B30
+ 1
+
+
+
+
+
diff --git a/ui/designer/tabs/tabOcrEntry_ui.py b/ui/designer/tabs/tabOcrEntry_ui.py
new file mode 100644
index 0000000..6144e6a
--- /dev/null
+++ b/ui/designer/tabs/tabOcrEntry_ui.py
@@ -0,0 +1,57 @@
+# -*- coding: utf-8 -*-
+
+################################################################################
+## Form generated from reading UI file 'tabOcrEntry.ui'
+##
+## Created by: Qt User Interface Compiler version 6.5.1
+##
+## 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, QSizePolicy, QTabWidget, QVBoxLayout,
+ QWidget)
+
+from ui.implements.tabs.tabOcr.tabOcr_B30 import TabOcr_B30
+from ui.implements.tabs.tabOcr.tabOcr_Device import TabOcr_Device
+
+class Ui_TabOcrEntry(object):
+ def setupUi(self, TabOcrEntry):
+ if not TabOcrEntry.objectName():
+ TabOcrEntry.setObjectName(u"TabOcrEntry")
+ TabOcrEntry.resize(600, 478)
+ TabOcrEntry.setWindowTitle(u"TabOcrEntry")
+ self.verticalLayout = QVBoxLayout(TabOcrEntry)
+ self.verticalLayout.setObjectName(u"verticalLayout")
+ self.tabWidget = QTabWidget(TabOcrEntry)
+ self.tabWidget.setObjectName(u"tabWidget")
+ self.tab = TabOcr_Device()
+ self.tab.setObjectName(u"tab")
+ self.tabWidget.addTab(self.tab, "")
+ self.tab_2 = TabOcr_B30()
+ self.tab_2.setObjectName(u"tab_2")
+ self.tabWidget.addTab(self.tab_2, "")
+
+ self.verticalLayout.addWidget(self.tabWidget)
+
+
+ self.retranslateUi(TabOcrEntry)
+
+ self.tabWidget.setCurrentIndex(0)
+
+
+ QMetaObject.connectSlotsByName(TabOcrEntry)
+ # setupUi
+
+ 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_2), QCoreApplication.translate("TabOcrEntry", u"tab.b30", None))
+ pass
+ # retranslateUi
+
diff --git a/ui/extends/components/ocrQueue/__init__.py b/ui/extends/components/ocrQueue/__init__.py
index 08060d8..abe8b66 100644
--- a/ui/extends/components/ocrQueue/__init__.py
+++ b/ui/extends/components/ocrQueue/__init__.py
@@ -1,9 +1,10 @@
import logging
-from typing import Any, Callable
+from typing import Any, Callable, Optional, overload
from arcaea_offline.calculate import calculate_score_range
from arcaea_offline.database import Database
from arcaea_offline.models import Chart, ScoreInsert
+from arcaea_offline_ocr.b30.shared import B30OcrResultItem
from arcaea_offline_ocr.device.shared import DeviceOcrResult
from PySide6.QtCore import (
QAbstractListModel,
@@ -30,7 +31,7 @@ logger = logging.getLogger(__name__)
class OcrRunnableSignals(QObject):
rowId: int = -1
- resultReady = Signal(DeviceOcrResult)
+ resultReady = Signal("QVariant")
finished = Signal()
@@ -45,7 +46,7 @@ class OcrQueueModel(QAbstractListModel):
ImageQImageRole = Qt.ItemDataRole.UserRole + 2
ImagePixmapRole = Qt.ItemDataRole.UserRole + 3
- DeviceOcrResultRole = Qt.ItemDataRole.UserRole + 10
+ OcrResultRole = Qt.ItemDataRole.UserRole + 10
ScoreInsertRole = Qt.ItemDataRole.UserRole + 11
ChartRole = Qt.ItemDataRole.UserRole + 12
ScoreValidateOkRole = Qt.ItemDataRole.UserRole + 13
@@ -97,55 +98,86 @@ class OcrQueueModel(QAbstractListModel):
item = self.__items[index.row()]
updateRole = None
- if role == self.DeviceOcrResultRole and isinstance(value, DeviceOcrResult):
- item[self.DeviceOcrResultRole] = value
- self.updateRole = role
+ if role == self.OcrResultRole:
+ item[self.OcrResultRole] = value
+ updateRole = role
if role == self.ChartRole and isinstance(value, Chart):
item[self.ChartRole] = value
self.updateScoreValidateOk(index.row())
- self.updateRole = role
+ updateRole = role
if role == self.ScoreInsertRole and isinstance(value, ScoreInsert):
item[self.ScoreInsertRole] = value
self.updateScoreValidateOk(index.row())
- self.updateRole = role
+ updateRole = role
if role == self.ScoreValidateOkRole and isinstance(value, bool):
item[self.ScoreValidateOkRole] = value
- self.updateRole = role
+ updateRole = role
if role == self.OcrRunnableRole and isinstance(value, OcrRunnable):
item[self.OcrRunnableRole] = value
- self.updateRole = role
+ updateRole = role
if role == self.ProcessOcrResultFuncRole and callable(value):
item[self.ProcessOcrResultFuncRole] = value
- self.updateRole = role
+ updateRole = role
if updateRole is not None:
self.dataChanged.emit(index, index, [updateRole])
return True
else:
+ logger.warning(
+ f"{repr(self)} setData at row {index.row()} with role {role} and value {value} rejected."
+ )
return False
+ @overload
+ def addItem(
+ self,
+ image: str,
+ runnable: OcrRunnable = None,
+ process_func: Callable[[Optional[str], QImage, Any], ScoreInsert] = None,
+ ):
+ ...
+
+ @overload
+ def addItem(
+ self,
+ image: QImage,
+ runnable: OcrRunnable = None,
+ process_func: Callable[[Optional[str], QImage, Any], ScoreInsert] = None,
+ ):
+ ...
+
def addItem(
self,
- imagePath: str,
- runnable: OcrRunnable = None,
- process_func: Callable = None,
+ image,
+ runnable=None,
+ process_func=None,
):
- if imagePath in self.imagePaths or not QFileInfo(imagePath).exists():
- logger.warning(f"Attempting to add an invalid file {imagePath}")
- return
+ if isinstance(image, str):
+ if image in self.imagePaths or not QFileInfo(image).exists():
+ logger.warning(f"Attempting to add an invalid file {image}")
+ return
+ imagePath = image
+ qImage = QImage(image)
+ qPixmap = QPixmap(image)
+ elif isinstance(image, QImage):
+ imagePath = None
+ qImage = image.copy()
+ qPixmap = QPixmap(qImage)
+ else:
+ raise ValueError("Unsupported type for `image`")
self.beginInsertRows(QModelIndex(), self.rowCount(), self.rowCount())
self.__items.append(
{
self.ImagePathRole: imagePath,
- self.ImageQImageRole: QImage(imagePath),
- self.ImagePixmapRole: QPixmap(imagePath),
- self.DeviceOcrResultRole: None,
+ self.ImageQImageRole: qImage,
+ self.ImagePixmapRole: qPixmap,
+ self.OcrResultRole: None,
self.ScoreInsertRole: None,
self.ChartRole: None,
self.ScoreValidateOkRole: False,
@@ -155,19 +187,19 @@ class OcrQueueModel(QAbstractListModel):
)
self.endInsertRows()
- def updateOcrResult(self, row: int, result: DeviceOcrResult) -> bool:
- if not 0 <= row < self.rowCount() or not isinstance(result, DeviceOcrResult):
+ def updateOcrResult(self, row: int, result: Any) -> bool:
+ if not 0 <= row < self.rowCount():
return False
index = self.index(row, 0)
imagePath: str = index.data(self.ImagePathRole)
+ qImage: QImage = index.data(self.ImageQImageRole)
+ print(row, result)
processOcrResultFunc = index.data(self.ProcessOcrResultFuncRole)
- chart, scoreInsert = processOcrResultFunc(imagePath, result)
+ chart, scoreInsert = processOcrResultFunc(imagePath, qImage, result)
- # song_id = self.__db.fuzzy_search_song_id(result.title)[0][0]
-
- self.setData(index, result, self.DeviceOcrResultRole)
+ self.setData(index, result, self.OcrResultRole)
self.setData(index, chart, self.ChartRole)
self.setData(index, scoreInsert, self.ScoreInsertRole)
return True
@@ -175,7 +207,6 @@ class OcrQueueModel(QAbstractListModel):
@Slot(DeviceOcrResult)
def ocrTaskReady(self, result: DeviceOcrResult):
row = self.sender().rowId
- print(row)
self.updateOcrResult(row, result)
@Slot()
@@ -184,7 +215,6 @@ class OcrQueueModel(QAbstractListModel):
self.progress.emit(self.__taskFinishedNum)
if self.__taskFinishedNum == self.__taskNum:
self.finished.emit()
- print("model finished")
def startQueue(self):
self.__taskNum = self.rowCount()
@@ -266,11 +296,11 @@ class OcrQueueTableProxyModel(QAbstractTableModel):
OcrQueueModel.ImagePixmapRole,
],
[
- OcrQueueModel.DeviceOcrResultRole,
+ OcrQueueModel.OcrResultRole,
OcrQueueModel.ChartRole,
],
[
- OcrQueueModel.DeviceOcrResultRole,
+ OcrQueueModel.OcrResultRole,
OcrQueueModel.ScoreInsertRole,
OcrQueueModel.ChartRole,
OcrQueueModel.ScoreValidateOkRole,
@@ -333,9 +363,9 @@ class OcrQueueTableProxyModel(QAbstractTableModel):
def setData(self, index, value, role):
if index.column() == 2 and role == OcrQueueModel.ChartRole:
- return self.sourceModel().setItemChart(index.row(), value)
+ return self.sourceModel().setData(index, value, role)
if index.column() == 3 and role == OcrQueueModel.ScoreInsertRole:
- return self.sourceModel().setItemScore(index.row(), value)
+ return self.sourceModel().setData(index, value, role)
return False
def flags(self, index: QModelIndex) -> Qt.ItemFlag:
@@ -365,9 +395,7 @@ class OcrChartDelegate(ChartDelegate):
return index.data(OcrQueueModel.ChartRole)
def paintWarningBackground(self, index: QModelIndex) -> bool:
- return isinstance(
- index.data(OcrQueueModel.DeviceOcrResultRole), DeviceOcrResult
- )
+ return isinstance(index.data(OcrQueueModel.OcrResultRole), DeviceOcrResult)
def setModelData(self, editor, model: OcrQueueTableProxyModel, index):
if editor.validate():
@@ -385,9 +413,13 @@ class OcrScoreDelegate(ScoreDelegate):
return index.data(OcrQueueModel.ScoreValidateOkRole)
def paintWarningBackground(self, index: QModelIndex) -> bool:
- return isinstance(
- index.data(OcrQueueModel.DeviceOcrResultRole), DeviceOcrResult
- )
+ return True
+ # return isinstance(self.getChart(index), Chart) and isinstance(
+ # self.getScore(index), ScoreInsert
+ # )
+ # return isinstance(
+ # index.data(OcrQueueModel.OcrResultRole), (DeviceOcrResult, B30OcrResultItem)
+ # )
def setModelData(self, editor, model: OcrQueueTableProxyModel, index):
if super().confirmSetModelData(editor):
diff --git a/ui/extends/shared/cv2_utils.py b/ui/extends/shared/cv2_utils.py
new file mode 100644
index 0000000..c587a4d
--- /dev/null
+++ b/ui/extends/shared/cv2_utils.py
@@ -0,0 +1,28 @@
+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,
+ )
diff --git a/ui/extends/shared/delegates/imageDelegate.py b/ui/extends/shared/delegates/imageDelegate.py
index 64694c6..80481da 100644
--- a/ui/extends/shared/delegates/imageDelegate.py
+++ b/ui/extends/shared/delegates/imageDelegate.py
@@ -51,7 +51,10 @@ class ImageDelegate(QStyledItemDelegate):
label.setWindowFlag(Qt.WindowType.WindowMinimizeButtonHint, False)
label.setWindowFlag(Qt.WindowType.WindowMaximizeButtonHint, False)
label.setWindowFlag(Qt.WindowType.WindowCloseButtonHint, True)
- label.setWindowTitle(QFileInfo(self.getImagePath(index)).fileName())
+ imagePath = self.getImagePath(index)
+ label.setWindowTitle(
+ QFileInfo(imagePath).fileName() if imagePath else "Preview"
+ )
pixmap = pixmap.scaled(
800,
800,
diff --git a/ui/extends/tabs/tabOcr/tabOcr_B30.py b/ui/extends/tabs/tabOcr/tabOcr_B30.py
new file mode 100644
index 0000000..c58dab2
--- /dev/null
+++ b/ui/extends/tabs/tabOcr/tabOcr_B30.py
@@ -0,0 +1,51 @@
+import logging
+
+from arcaea_offline.database import Database
+from arcaea_offline.models import Chart, ScoreInsert
+from arcaea_offline_ocr.b30.chieri.v4.ocr import ChieriBotV4Ocr
+from arcaea_offline_ocr.b30.shared import B30OcrResultItem
+from PySide6.QtGui import QImage
+
+logger = logging.getLogger(__name__)
+
+from ui.extends.components.ocrQueue import OcrRunnable
+
+
+class ChieriV4OcrRunnable(OcrRunnable):
+ def __init__(self, ocr: ChieriBotV4Ocr, component):
+ super().__init__()
+ self.ocr = ocr
+ self.component = component.copy()
+
+ def run(self):
+ try:
+ result = self.ocr.ocr_component(self.component)
+ self.signals.resultReady.emit(result)
+ except Exception:
+ logger.exception("ChieriV4 ocr component error")
+ finally:
+ self.signals.finished.emit()
+
+
+def b30ResultToScoreInsert(_, qImage: QImage, result: B30OcrResultItem):
+ if not result.song_id and not result.title:
+ raise ValueError("no title or song_id")
+
+ db = Database()
+ if not result.song_id:
+ song_id = db.fuzzy_search_song_id(result.title)[0][0]
+ else:
+ song_id = result.song_id
+
+ chart = Chart.from_db_row(db.get_chart(song_id, result.rating_class))
+ score = ScoreInsert(
+ song_id=song_id,
+ rating_class=result.rating_class,
+ score=result.score,
+ time=1485014400,
+ pure=result.pure,
+ far=result.far,
+ lost=result.lost,
+ )
+
+ return (chart, score)
diff --git a/ui/extends/tabs/tabOcr.py b/ui/extends/tabs/tabOcr/tabOcr_Device.py
similarity index 93%
rename from ui/extends/tabs/tabOcr.py
rename to ui/extends/tabs/tabOcr/tabOcr_Device.py
index b0bebe4..14a0598 100644
--- a/ui/extends/tabs/tabOcr.py
+++ b/ui/extends/tabs/tabOcr/tabOcr_Device.py
@@ -3,7 +3,7 @@ import logging
from typing import Tuple
from arcaea_offline.database import Database
-from arcaea_offline.models import ScoreInsert, Chart
+from arcaea_offline.models import Chart, ScoreInsert
from arcaea_offline_ocr.device.shared import DeviceOcrResult
from arcaea_offline_ocr.device.v2.ocr import DeviceV2Ocr
from arcaea_offline_ocr.device.v2.rois import DeviceV2Rois
@@ -52,7 +52,9 @@ def getImageDate(imagePath: str) -> QDateTime:
class ScoreInsertConverter:
@staticmethod
- def deviceV2(imagePath: str, result: DeviceOcrResult) -> Tuple[Chart, ScoreInsert]:
+ def deviceV2(
+ imagePath: str, _, result: DeviceOcrResult
+ ) -> Tuple[Chart, ScoreInsert]:
db = Database()
scoreInsert = ScoreInsert(
song_id=result.song_id,
diff --git a/ui/implements/mainwindow.py b/ui/implements/mainwindow.py
index 0770a5c..8cb2f36 100644
--- a/ui/implements/mainwindow.py
+++ b/ui/implements/mainwindow.py
@@ -3,7 +3,7 @@ from traceback import format_exception
from PySide6.QtWidgets import QMainWindow
from ui.designer.mainwindow_ui import Ui_MainWindow
-from ui.implements.tabs.tabOcr import TabOcr
+from ui.implements.tabs.tabOcrEntry import TabOcrEntry
# try:
# import arcaea_offline_ocr
@@ -32,7 +32,7 @@ class MainWindow(Ui_MainWindow, QMainWindow):
# else:
# self.tab_ocr = TabOcrDisabled(self.tabWidget)
# self.tab_ocr.contentLabel.setText(OCR_ERROR_TEXT)
- self.tab_ocr = TabOcr(self.tabWidget)
+ self.tab_ocr = TabOcrEntry(self.tabWidget)
self.tabWidget.insertTab(ocrTabIndex, self.tab_ocr, "")
self.tabWidget.setCurrentIndex(currentIndex)
self.retranslateUi(self)
diff --git a/ui/implements/tabs/tabOcr/tabOcr_B30.py b/ui/implements/tabs/tabOcr/tabOcr_B30.py
new file mode 100644
index 0000000..190a989
--- /dev/null
+++ b/ui/implements/tabs/tabOcr/tabOcr_B30.py
@@ -0,0 +1,161 @@
+import logging
+from pathlib import Path
+
+import cv2
+from arcaea_offline_ocr.b30.chieri.v4.ocr import ChieriBotV4Ocr
+from arcaea_offline_ocr.sift_db import SIFTDatabase
+from arcaea_offline_ocr.utils import imread_unicode
+
+# from paddleocr import PaddleOCR
+from PySide6.QtCore import Signal, Slot
+from PySide6.QtWidgets import QWidget
+
+from ui.designer.tabs.tabOcr.tabOcr_B30_ui import Ui_TabOcr_B30
+from ui.extends.components.ocrQueue import OcrQueueModel
+from ui.extends.shared.cv2_utils import cv2BgrMatToQImage, qImageToCvMatBgr
+from ui.extends.tabs.tabOcr.tabOcr_B30 import (
+ ChieriV4OcrRunnable,
+ b30ResultToScoreInsert,
+)
+
+logger = logging.getLogger(__name__)
+
+
+class TabOcr_B30(Ui_TabOcr_B30, QWidget):
+ tryPrepareOcr = Signal()
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self.setupUi(self)
+
+ self.b30TypeComboBox.addItem("ChieriV4", "chieri_v4")
+ self.b30TypeComboBox.setCurrentIndex(0)
+ self.b30TypeComboBox.setEnabled(False)
+
+ # self.paddleFolderSelector.setMode(
+ # self.paddleFolderSelector.getExistingDirectory
+ # )
+
+ self.imageSelector.filesSelected.connect(self.imageSelected)
+ self.knnModelSelector.filesSelected.connect(self.knnModelSelected)
+ self.b30KnnModelSelector.filesSelected.connect(self.b30KnnModelSelected)
+ # self.paddleFolderSelector.filesSelected.connect(self.paddleFolderSelected)
+ self.siftDatabaseSelector.filesSelected.connect(self.siftDatabaseSelected)
+
+ self.imagePath = None # for checking only
+ self.img = None
+ self.paddleFolder = None
+ self.paddle = None
+ self.knnModel = None
+ self.b30KnnModel = None
+ self.siftDatabase = None
+
+ self.ocr = None
+
+ self.tryPrepareOcr.connect(self.prepareOcr)
+
+ self.ocrQueueModel = OcrQueueModel(self)
+ self.ocrQueue.setModel(self.ocrQueueModel)
+
+ def imageSelected(self):
+ selectedFiles = self.imageSelector.selectedFiles()
+ if selectedFiles:
+ imagePath = selectedFiles[0]
+ self.imagePath = imagePath
+ self.img = imread_unicode(imagePath)
+ self.tryPrepareOcr.emit()
+
+ def knnModelSelected(self):
+ selectedFiles = self.knnModelSelector.selectedFiles()
+ if selectedFiles:
+ knnModelPath = selectedFiles[0]
+ self.knnModel = cv2.ml.KNearest.load(knnModelPath)
+ self.tryPrepareOcr.emit()
+
+ def b30KnnModelSelected(self):
+ selectedFiles = self.b30KnnModelSelector.selectedFiles()
+ if selectedFiles:
+ b30KnnModelPath = selectedFiles[0]
+ self.b30KnnModel = cv2.ml.KNearest.load(b30KnnModelPath)
+ self.tryPrepareOcr.emit()
+
+ def siftDatabaseSelected(self):
+ selectedFiles = self.siftDatabaseSelector.selectedFiles()
+ if selectedFiles:
+ siftDatabasePath = selectedFiles[0]
+ self.siftDatabase = SIFTDatabase(siftDatabasePath)
+ self.tryPrepareOcr.emit()
+
+ def paddleFolderSelected(self):
+ selectedFiles = self.paddleFolderSelector.selectedFiles()
+ if selectedFiles:
+ self.paddleFolder = selectedFiles[0]
+ self.initPaddle()
+ self.tryPrepareOcr.emit()
+
+ def initPaddle(self):
+ paddleFolder = Path(self.paddleFolder)
+ paddleDetFolder = paddleFolder / "det"
+ paddleClsFolder = paddleFolder / "cls"
+ paddleRecFolder = paddleFolder / "rec"
+
+ if not (paddleDetFolder.exists() and paddleRecFolder.exists()):
+ logger.warning("paddleocr folder incomplete, aborting.")
+ return
+
+ self.paddle = PaddleOCR(
+ show_log=False,
+ use_angle_cls=False,
+ det_model_dir=str(paddleDetFolder),
+ cls_model_dir=str(paddleClsFolder),
+ rec_model_dir=str(paddleRecFolder),
+ )
+
+ def prepareOcr(self):
+ b30Type = self.b30TypeComboBox.currentData()
+ if not b30Type:
+ return
+
+ if b30Type == "chieri_v4":
+ if (
+ not self.imagePath
+ or not self.knnModel
+ or not self.b30KnnModel
+ or not self.siftDatabase
+ ):
+ return
+
+ self.ocrQueueModel.clear()
+
+ ocr = ChieriBotV4Ocr(self.knnModel, self.b30KnnModel, self.siftDatabase)
+ ocr.set_factor(self.img)
+ self.ocr = ocr
+
+ roi = ocr.rois
+ for component in roi.components(self.img):
+ qImage = cv2BgrMatToQImage(component.copy())
+ self.ocrQueueModel.addItem(qImage)
+ self.ocrQueue.resizeTableView()
+
+ @Slot()
+ def on_ocr_startButton_clicked(self):
+ if (
+ not self.imagePath
+ or not self.knnModel
+ or not self.b30KnnModel
+ or not self.siftDatabase
+ ):
+ return
+
+ for row in range(self.ocrQueueModel.rowCount()):
+ index = self.ocrQueueModel.index(row, 0)
+ qImage = index.data(OcrQueueModel.ImageQImageRole)
+ cv2Mat = qImageToCvMatBgr(qImage)
+ runnable = ChieriV4OcrRunnable(self.ocr, cv2Mat)
+ self.ocrQueueModel.setData(index, runnable, OcrQueueModel.OcrRunnableRole)
+ self.ocrQueueModel.setData(
+ index,
+ b30ResultToScoreInsert,
+ OcrQueueModel.ProcessOcrResultFuncRole,
+ )
+ self.ocrQueueModel.startQueue()
diff --git a/ui/implements/tabs/tabOcr.py b/ui/implements/tabs/tabOcr/tabOcr_Device.py
similarity index 94%
rename from ui/implements/tabs/tabOcr.py
rename to ui/implements/tabs/tabOcr/tabOcr_Device.py
index aa55473..2b4d5bd 100644
--- a/ui/implements/tabs/tabOcr.py
+++ b/ui/implements/tabs/tabOcr/tabOcr_Device.py
@@ -8,13 +8,16 @@ from arcaea_offline_ocr.sift_db import SIFTDatabase
from PySide6.QtCore import Slot
from PySide6.QtWidgets import QFileDialog, QWidget
-from ui.designer.tabs.tabOcr_ui import Ui_TabOcr
+from ui.designer.tabs.tabOcr.tabOcr_Device_ui import Ui_TabOcr_Device
from ui.extends.components.ocrQueue import OcrQueueModel
from ui.extends.settings import Settings
-from ui.extends.tabs.tabOcr import ScoreInsertConverter, TabDeviceV2OcrRunnable
+from ui.extends.tabs.tabOcr.tabOcr_Device import (
+ ScoreInsertConverter,
+ TabDeviceV2OcrRunnable,
+)
-class TabOcr(Ui_TabOcr, QWidget):
+class TabOcr_Device(Ui_TabOcr_Device, QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setupUi(self)
diff --git a/ui/implements/tabs/tabOcrEntry.py b/ui/implements/tabs/tabOcrEntry.py
new file mode 100644
index 0000000..9be167f
--- /dev/null
+++ b/ui/implements/tabs/tabOcrEntry.py
@@ -0,0 +1,9 @@
+from PySide6.QtWidgets import QWidget
+
+from ui.designer.tabs.tabOcrEntry_ui import Ui_TabOcrEntry
+
+
+class TabOcrEntry(Ui_TabOcrEntry, QWidget):
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self.setupUi(self)