mirror of
https://github.com/283375/arcaea-offline-pyside-ui.git
synced 2025-07-01 04:16:26 +00:00
wip: refactor
This commit is contained in:
174
ui/designer/components/ocrQueue.ui
Normal file
174
ui/designer/components/ocrQueue.ui
Normal file
@ -0,0 +1,174 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>OcrQueue</class>
|
||||
<widget class="QWidget" name="OcrQueue">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>741</width>
|
||||
<height>372</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>OcrQueue</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_2">
|
||||
<property name="title">
|
||||
<string>ocr.title</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_3">
|
||||
<property name="title">
|
||||
<string>ocr.queue.title</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QPushButton" name="ocr_addImageButton">
|
||||
<property name="text">
|
||||
<string>ocr.queue.addImageButton</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="ocr_removeSelectedButton">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>ocr.queue.removeSelected</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="ocr_removeAllButton">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>ocr.queue.removeAll</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<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="QPushButton" name="ocr_startButton">
|
||||
<property name="text">
|
||||
<string>ocr.queue.startOcrButton</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QTableView" name="tableView">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed</set>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::MultiSelection</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>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QProgressBar" name="progressBar">
|
||||
<property name="minimum">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<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 notr="true">%v/%m - %p%</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_5">
|
||||
<property name="title">
|
||||
<string>ocr.results</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<widget class="QPushButton" name="ocr_acceptSelectedButton">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>ocr.results.acceptSelectedButton</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="ocr_acceptAllButton">
|
||||
<property name="text">
|
||||
<string>ocr.results.acceptAllButton</string>
|
||||
</property>
|
||||
</widget>
|
||||
</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>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="ocr_ignoreValidateCheckBox">
|
||||
<property name="text">
|
||||
<string>ocr.results.ignoreValidate</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
141
ui/designer/components/ocrQueue_ui.py
Normal file
141
ui/designer/components/ocrQueue_ui.py
Normal file
@ -0,0 +1,141 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
################################################################################
|
||||
## Form generated from reading UI file 'ocrQueue.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 (QAbstractItemView, QApplication, QCheckBox, QGroupBox,
|
||||
QHBoxLayout, QHeaderView, QProgressBar, QPushButton,
|
||||
QSizePolicy, QSpacerItem, QTableView, QVBoxLayout,
|
||||
QWidget)
|
||||
|
||||
class Ui_OcrQueue(object):
|
||||
def setupUi(self, OcrQueue):
|
||||
if not OcrQueue.objectName():
|
||||
OcrQueue.setObjectName(u"OcrQueue")
|
||||
OcrQueue.resize(741, 372)
|
||||
self.verticalLayout = QVBoxLayout(OcrQueue)
|
||||
self.verticalLayout.setObjectName(u"verticalLayout")
|
||||
self.groupBox_2 = QGroupBox(OcrQueue)
|
||||
self.groupBox_2.setObjectName(u"groupBox_2")
|
||||
self.horizontalLayout = QHBoxLayout(self.groupBox_2)
|
||||
self.horizontalLayout.setObjectName(u"horizontalLayout")
|
||||
self.groupBox_3 = QGroupBox(self.groupBox_2)
|
||||
self.groupBox_3.setObjectName(u"groupBox_3")
|
||||
self.verticalLayout_2 = QVBoxLayout(self.groupBox_3)
|
||||
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
|
||||
self.ocr_addImageButton = QPushButton(self.groupBox_3)
|
||||
self.ocr_addImageButton.setObjectName(u"ocr_addImageButton")
|
||||
|
||||
self.verticalLayout_2.addWidget(self.ocr_addImageButton)
|
||||
|
||||
self.ocr_removeSelectedButton = QPushButton(self.groupBox_3)
|
||||
self.ocr_removeSelectedButton.setObjectName(u"ocr_removeSelectedButton")
|
||||
self.ocr_removeSelectedButton.setEnabled(True)
|
||||
|
||||
self.verticalLayout_2.addWidget(self.ocr_removeSelectedButton)
|
||||
|
||||
self.ocr_removeAllButton = QPushButton(self.groupBox_3)
|
||||
self.ocr_removeAllButton.setObjectName(u"ocr_removeAllButton")
|
||||
self.ocr_removeAllButton.setEnabled(True)
|
||||
|
||||
self.verticalLayout_2.addWidget(self.ocr_removeAllButton)
|
||||
|
||||
self.verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
|
||||
|
||||
self.verticalLayout_2.addItem(self.verticalSpacer)
|
||||
|
||||
self.ocr_startButton = QPushButton(self.groupBox_3)
|
||||
self.ocr_startButton.setObjectName(u"ocr_startButton")
|
||||
|
||||
self.verticalLayout_2.addWidget(self.ocr_startButton)
|
||||
|
||||
|
||||
self.horizontalLayout.addWidget(self.groupBox_3)
|
||||
|
||||
self.verticalLayout_3 = QVBoxLayout()
|
||||
self.verticalLayout_3.setObjectName(u"verticalLayout_3")
|
||||
self.tableView = QTableView(self.groupBox_2)
|
||||
self.tableView.setObjectName(u"tableView")
|
||||
self.tableView.setEditTriggers(QAbstractItemView.DoubleClicked|QAbstractItemView.EditKeyPressed)
|
||||
self.tableView.setSelectionMode(QAbstractItemView.MultiSelection)
|
||||
self.tableView.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||
self.tableView.setVerticalScrollMode(QAbstractItemView.ScrollPerPixel)
|
||||
self.tableView.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel)
|
||||
|
||||
self.verticalLayout_3.addWidget(self.tableView)
|
||||
|
||||
self.progressBar = QProgressBar(self.groupBox_2)
|
||||
self.progressBar.setObjectName(u"progressBar")
|
||||
self.progressBar.setMinimum(0)
|
||||
self.progressBar.setMaximum(0)
|
||||
self.progressBar.setValue(0)
|
||||
self.progressBar.setAlignment(Qt.AlignCenter)
|
||||
self.progressBar.setFormat(u"%v/%m - %p%")
|
||||
|
||||
self.verticalLayout_3.addWidget(self.progressBar)
|
||||
|
||||
|
||||
self.horizontalLayout.addLayout(self.verticalLayout_3)
|
||||
|
||||
self.groupBox_5 = QGroupBox(self.groupBox_2)
|
||||
self.groupBox_5.setObjectName(u"groupBox_5")
|
||||
self.verticalLayout_4 = QVBoxLayout(self.groupBox_5)
|
||||
self.verticalLayout_4.setObjectName(u"verticalLayout_4")
|
||||
self.ocr_acceptSelectedButton = QPushButton(self.groupBox_5)
|
||||
self.ocr_acceptSelectedButton.setObjectName(u"ocr_acceptSelectedButton")
|
||||
self.ocr_acceptSelectedButton.setEnabled(True)
|
||||
|
||||
self.verticalLayout_4.addWidget(self.ocr_acceptSelectedButton)
|
||||
|
||||
self.ocr_acceptAllButton = QPushButton(self.groupBox_5)
|
||||
self.ocr_acceptAllButton.setObjectName(u"ocr_acceptAllButton")
|
||||
|
||||
self.verticalLayout_4.addWidget(self.ocr_acceptAllButton)
|
||||
|
||||
self.verticalSpacer_2 = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
|
||||
|
||||
self.verticalLayout_4.addItem(self.verticalSpacer_2)
|
||||
|
||||
self.ocr_ignoreValidateCheckBox = QCheckBox(self.groupBox_5)
|
||||
self.ocr_ignoreValidateCheckBox.setObjectName(u"ocr_ignoreValidateCheckBox")
|
||||
|
||||
self.verticalLayout_4.addWidget(self.ocr_ignoreValidateCheckBox)
|
||||
|
||||
|
||||
self.horizontalLayout.addWidget(self.groupBox_5)
|
||||
|
||||
|
||||
self.verticalLayout.addWidget(self.groupBox_2)
|
||||
|
||||
|
||||
self.retranslateUi(OcrQueue)
|
||||
|
||||
QMetaObject.connectSlotsByName(OcrQueue)
|
||||
# setupUi
|
||||
|
||||
def retranslateUi(self, OcrQueue):
|
||||
OcrQueue.setWindowTitle(QCoreApplication.translate("OcrQueue", u"OcrQueue", None))
|
||||
self.groupBox_2.setTitle(QCoreApplication.translate("OcrQueue", u"ocr.title", 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))
|
||||
self.ocr_removeAllButton.setText(QCoreApplication.translate("OcrQueue", u"ocr.queue.removeAll", None))
|
||||
self.ocr_startButton.setText(QCoreApplication.translate("OcrQueue", u"ocr.queue.startOcrButton", None))
|
||||
self.groupBox_5.setTitle(QCoreApplication.translate("OcrQueue", u"ocr.results", None))
|
||||
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))
|
||||
# retranslateUi
|
||||
|
@ -55,125 +55,7 @@
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_3">
|
||||
<property name="title">
|
||||
<string>ocr.queue.title</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QPushButton" name="ocr_addImageButton">
|
||||
<property name="text">
|
||||
<string>ocr.queue.addImageButton</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="ocr_removeSelectedButton">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>ocr.queue.removeSelected</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="ocr_removeAllButton">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>ocr.queue.removeAll</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<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="QPushButton" name="ocr_startButton">
|
||||
<property name="text">
|
||||
<string>ocr.queue.startOcrButton</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTableView" name="tableView">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed</set>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::MultiSelection</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>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_5">
|
||||
<property name="title">
|
||||
<string>ocr.results</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<widget class="QPushButton" name="ocr_acceptSelectedButton">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>ocr.results.acceptSelectedButton</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="ocr_acceptAllButton">
|
||||
<property name="text">
|
||||
<string>ocr.results.acceptAllButton</string>
|
||||
</property>
|
||||
</widget>
|
||||
</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>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="ocr_ignoreValidateCheckBox">
|
||||
<property name="text">
|
||||
<string>ocr.results.ignoreValidate</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="OcrQueue" name="ocrQueue" native="true"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
@ -192,6 +74,12 @@
|
||||
<extends>QComboBox</extends>
|
||||
<header>ui.implements.components</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>OcrQueue</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>ui.implements.components</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
|
@ -3,7 +3,7 @@
|
||||
################################################################################
|
||||
## Form generated from reading UI file 'tabOcr.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!
|
||||
################################################################################
|
||||
@ -15,11 +15,10 @@ from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
|
||||
QFont, QFontDatabase, QGradient, QIcon,
|
||||
QImage, QKeySequence, QLinearGradient, QPainter,
|
||||
QPalette, QPixmap, QRadialGradient, QTransform)
|
||||
from PySide6.QtWidgets import (QAbstractItemView, QApplication, QCheckBox, QGroupBox,
|
||||
QHBoxLayout, QHeaderView, QPushButton, QSizePolicy,
|
||||
QSpacerItem, QTableView, QVBoxLayout, QWidget)
|
||||
from PySide6.QtWidgets import (QApplication, QGroupBox, QHBoxLayout, QPushButton,
|
||||
QSizePolicy, QVBoxLayout, QWidget)
|
||||
|
||||
from ui.implements.components import (DevicesComboBox, FileSelector)
|
||||
from ui.implements.components import (DevicesComboBox, FileSelector, OcrQueue)
|
||||
|
||||
class Ui_TabOcr(object):
|
||||
def setupUi(self, TabOcr):
|
||||
@ -67,75 +66,10 @@ class Ui_TabOcr(object):
|
||||
self.groupBox_2.setObjectName(u"groupBox_2")
|
||||
self.horizontalLayout = QHBoxLayout(self.groupBox_2)
|
||||
self.horizontalLayout.setObjectName(u"horizontalLayout")
|
||||
self.groupBox_3 = QGroupBox(self.groupBox_2)
|
||||
self.groupBox_3.setObjectName(u"groupBox_3")
|
||||
self.verticalLayout_2 = QVBoxLayout(self.groupBox_3)
|
||||
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
|
||||
self.ocr_addImageButton = QPushButton(self.groupBox_3)
|
||||
self.ocr_addImageButton.setObjectName(u"ocr_addImageButton")
|
||||
self.ocrQueue = OcrQueue(self.groupBox_2)
|
||||
self.ocrQueue.setObjectName(u"ocrQueue")
|
||||
|
||||
self.verticalLayout_2.addWidget(self.ocr_addImageButton)
|
||||
|
||||
self.ocr_removeSelectedButton = QPushButton(self.groupBox_3)
|
||||
self.ocr_removeSelectedButton.setObjectName(u"ocr_removeSelectedButton")
|
||||
self.ocr_removeSelectedButton.setEnabled(True)
|
||||
|
||||
self.verticalLayout_2.addWidget(self.ocr_removeSelectedButton)
|
||||
|
||||
self.ocr_removeAllButton = QPushButton(self.groupBox_3)
|
||||
self.ocr_removeAllButton.setObjectName(u"ocr_removeAllButton")
|
||||
self.ocr_removeAllButton.setEnabled(True)
|
||||
|
||||
self.verticalLayout_2.addWidget(self.ocr_removeAllButton)
|
||||
|
||||
self.verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
|
||||
|
||||
self.verticalLayout_2.addItem(self.verticalSpacer)
|
||||
|
||||
self.ocr_startButton = QPushButton(self.groupBox_3)
|
||||
self.ocr_startButton.setObjectName(u"ocr_startButton")
|
||||
|
||||
self.verticalLayout_2.addWidget(self.ocr_startButton)
|
||||
|
||||
|
||||
self.horizontalLayout.addWidget(self.groupBox_3)
|
||||
|
||||
self.tableView = QTableView(self.groupBox_2)
|
||||
self.tableView.setObjectName(u"tableView")
|
||||
self.tableView.setEditTriggers(QAbstractItemView.DoubleClicked|QAbstractItemView.EditKeyPressed)
|
||||
self.tableView.setSelectionMode(QAbstractItemView.MultiSelection)
|
||||
self.tableView.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||
self.tableView.setVerticalScrollMode(QAbstractItemView.ScrollPerPixel)
|
||||
self.tableView.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel)
|
||||
|
||||
self.horizontalLayout.addWidget(self.tableView)
|
||||
|
||||
self.groupBox_5 = QGroupBox(self.groupBox_2)
|
||||
self.groupBox_5.setObjectName(u"groupBox_5")
|
||||
self.verticalLayout_4 = QVBoxLayout(self.groupBox_5)
|
||||
self.verticalLayout_4.setObjectName(u"verticalLayout_4")
|
||||
self.ocr_acceptSelectedButton = QPushButton(self.groupBox_5)
|
||||
self.ocr_acceptSelectedButton.setObjectName(u"ocr_acceptSelectedButton")
|
||||
self.ocr_acceptSelectedButton.setEnabled(True)
|
||||
|
||||
self.verticalLayout_4.addWidget(self.ocr_acceptSelectedButton)
|
||||
|
||||
self.ocr_acceptAllButton = QPushButton(self.groupBox_5)
|
||||
self.ocr_acceptAllButton.setObjectName(u"ocr_acceptAllButton")
|
||||
|
||||
self.verticalLayout_4.addWidget(self.ocr_acceptAllButton)
|
||||
|
||||
self.verticalSpacer_2 = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
|
||||
|
||||
self.verticalLayout_4.addItem(self.verticalSpacer_2)
|
||||
|
||||
self.ocr_ignoreValidateCheckBox = QCheckBox(self.groupBox_5)
|
||||
self.ocr_ignoreValidateCheckBox.setObjectName(u"ocr_ignoreValidateCheckBox")
|
||||
|
||||
self.verticalLayout_4.addWidget(self.ocr_ignoreValidateCheckBox)
|
||||
|
||||
|
||||
self.horizontalLayout.addWidget(self.groupBox_5)
|
||||
self.horizontalLayout.addWidget(self.ocrQueue)
|
||||
|
||||
|
||||
self.verticalLayout_3.addWidget(self.groupBox_2)
|
||||
@ -151,15 +85,6 @@ class Ui_TabOcr(object):
|
||||
self.groupBox.setTitle(QCoreApplication.translate("TabOcr", u"deviceSelector.title", None))
|
||||
self.groupBox_4.setTitle(QCoreApplication.translate("TabOcr", u"tesseractSelector.title", None))
|
||||
self.groupBox_2.setTitle(QCoreApplication.translate("TabOcr", u"ocr.title", None))
|
||||
self.groupBox_3.setTitle(QCoreApplication.translate("TabOcr", u"ocr.queue.title", None))
|
||||
self.ocr_addImageButton.setText(QCoreApplication.translate("TabOcr", u"ocr.queue.addImageButton", None))
|
||||
self.ocr_removeSelectedButton.setText(QCoreApplication.translate("TabOcr", u"ocr.queue.removeSelected", None))
|
||||
self.ocr_removeAllButton.setText(QCoreApplication.translate("TabOcr", u"ocr.queue.removeAll", None))
|
||||
self.ocr_startButton.setText(QCoreApplication.translate("TabOcr", u"ocr.queue.startOcrButton", None))
|
||||
self.groupBox_5.setTitle(QCoreApplication.translate("TabOcr", u"ocr.results", None))
|
||||
self.ocr_acceptSelectedButton.setText(QCoreApplication.translate("TabOcr", u"ocr.results.acceptSelectedButton", None))
|
||||
self.ocr_acceptAllButton.setText(QCoreApplication.translate("TabOcr", u"ocr.results.acceptAllButton", None))
|
||||
self.ocr_ignoreValidateCheckBox.setText(QCoreApplication.translate("TabOcr", u"ocr.results.ignoreValidate", None))
|
||||
pass
|
||||
# retranslateUi
|
||||
|
||||
|
394
ui/extends/components/ocrQueue/__init__.py
Normal file
394
ui/extends/components/ocrQueue/__init__.py
Normal file
@ -0,0 +1,394 @@
|
||||
import logging
|
||||
from typing import Any, Callable
|
||||
|
||||
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.device.shared import DeviceOcrResult
|
||||
from PySide6.QtCore import (
|
||||
QAbstractListModel,
|
||||
QAbstractTableModel,
|
||||
QCoreApplication,
|
||||
QFileInfo,
|
||||
QModelIndex,
|
||||
QObject,
|
||||
QRunnable,
|
||||
Qt,
|
||||
QThreadPool,
|
||||
Signal,
|
||||
Slot,
|
||||
)
|
||||
from PySide6.QtGui import QImage, QPixmap
|
||||
|
||||
from ui.extends.shared.delegates.chartDelegate import ChartDelegate
|
||||
from ui.extends.shared.delegates.imageDelegate import ImageDelegate
|
||||
from ui.extends.shared.delegates.scoreDelegate import ScoreDelegate
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class OcrRunnableSignals(QObject):
|
||||
rowId: int = -1
|
||||
|
||||
resultReady = Signal(DeviceOcrResult)
|
||||
finished = Signal()
|
||||
|
||||
|
||||
class OcrRunnable(QRunnable):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.signals = OcrRunnableSignals()
|
||||
|
||||
|
||||
class OcrQueueModel(QAbstractListModel):
|
||||
ImagePathRole = Qt.ItemDataRole.UserRole + 1
|
||||
ImageQImageRole = Qt.ItemDataRole.UserRole + 2
|
||||
ImagePixmapRole = Qt.ItemDataRole.UserRole + 3
|
||||
|
||||
DeviceOcrResultRole = Qt.ItemDataRole.UserRole + 10
|
||||
ScoreInsertRole = Qt.ItemDataRole.UserRole + 11
|
||||
ChartRole = Qt.ItemDataRole.UserRole + 12
|
||||
ScoreValidateOkRole = Qt.ItemDataRole.UserRole + 13
|
||||
|
||||
OcrRunnableRole = Qt.ItemDataRole.UserRole + 20
|
||||
ProcessOcrResultFuncRole = (
|
||||
Qt.ItemDataRole.UserRole + 21
|
||||
) # Callable[[imageStr, DeviceOcrResult], tuple[Chart, ScoreInsert]]
|
||||
|
||||
started = Signal()
|
||||
progress = Signal(int)
|
||||
finished = Signal()
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.__db = Database()
|
||||
self.__items: list[dict[int, Any]] = []
|
||||
|
||||
self.__taskFinishedNum = 0
|
||||
|
||||
@property
|
||||
def imagePaths(self):
|
||||
return [item.get(self.ImagePathRole) for item in self.__items]
|
||||
|
||||
def clear(self):
|
||||
self.beginResetModel()
|
||||
self.beginRemoveRows(QModelIndex(), 0, self.rowCount() - 1)
|
||||
self.__items.clear()
|
||||
self.endRemoveRows()
|
||||
self.__taskFinishedNum = 0
|
||||
self.endResetModel()
|
||||
|
||||
def rowCount(self, *args):
|
||||
return len(self.__items)
|
||||
|
||||
def data(self, index, role):
|
||||
if (
|
||||
index.isValid()
|
||||
and 0 <= index.row() < self.rowCount()
|
||||
and index.column() == 0
|
||||
):
|
||||
return self.__items[index.row()].get(role)
|
||||
return None
|
||||
|
||||
def setData(self, index: QModelIndex, value: Any, role: int):
|
||||
if not 0 <= index.row() < self.rowCount():
|
||||
return False
|
||||
|
||||
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.ChartRole and isinstance(value, Chart):
|
||||
item[self.ChartRole] = value
|
||||
self.updateScoreValidateOk(index.row())
|
||||
self.updateRole = role
|
||||
|
||||
if role == self.ScoreInsertRole and isinstance(value, ScoreInsert):
|
||||
item[self.ScoreInsertRole] = value
|
||||
self.updateScoreValidateOk(index.row())
|
||||
self.updateRole = role
|
||||
|
||||
if role == self.ScoreValidateOkRole and isinstance(value, bool):
|
||||
item[self.ScoreValidateOkRole] = value
|
||||
self.updateRole = role
|
||||
|
||||
if role == self.OcrRunnableRole and isinstance(value, OcrRunnable):
|
||||
item[self.OcrRunnableRole] = value
|
||||
self.updateRole = role
|
||||
|
||||
if role == self.ProcessOcrResultFuncRole and callable(value):
|
||||
item[self.ProcessOcrResultFuncRole] = value
|
||||
self.updateRole = role
|
||||
|
||||
if updateRole is not None:
|
||||
self.dataChanged.emit(index, index, [updateRole])
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def addItem(
|
||||
self,
|
||||
imagePath: str,
|
||||
runnable: OcrRunnable = None,
|
||||
process_func: Callable = None,
|
||||
):
|
||||
if imagePath in self.imagePaths or not QFileInfo(imagePath).exists():
|
||||
logger.warning(f"Attempting to add an invalid file {imagePath}")
|
||||
return
|
||||
|
||||
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.ScoreInsertRole: None,
|
||||
self.ChartRole: None,
|
||||
self.ScoreValidateOkRole: False,
|
||||
self.OcrRunnableRole: runnable,
|
||||
self.ProcessOcrResultFuncRole: process_func,
|
||||
}
|
||||
)
|
||||
self.endInsertRows()
|
||||
|
||||
def updateOcrResult(self, row: int, result: DeviceOcrResult) -> bool:
|
||||
if not 0 <= row < self.rowCount() or not isinstance(result, DeviceOcrResult):
|
||||
return False
|
||||
|
||||
index = self.index(row, 0)
|
||||
imagePath: str = index.data(self.ImagePathRole)
|
||||
processOcrResultFunc = index.data(self.ProcessOcrResultFuncRole)
|
||||
|
||||
chart, scoreInsert = processOcrResultFunc(imagePath, result)
|
||||
|
||||
# song_id = self.__db.fuzzy_search_song_id(result.title)[0][0]
|
||||
|
||||
self.setData(index, result, self.DeviceOcrResultRole)
|
||||
self.setData(index, chart, self.ChartRole)
|
||||
self.setData(index, scoreInsert, self.ScoreInsertRole)
|
||||
return True
|
||||
|
||||
@Slot(DeviceOcrResult)
|
||||
def ocrTaskReady(self, result: DeviceOcrResult):
|
||||
row = self.sender().rowId
|
||||
print(row)
|
||||
self.updateOcrResult(row, result)
|
||||
|
||||
@Slot()
|
||||
def ocrTaskFinished(self):
|
||||
self.__taskFinishedNum += 1
|
||||
self.progress.emit(self.__taskFinishedNum)
|
||||
if self.__taskFinishedNum == self.__taskNum:
|
||||
self.finished.emit()
|
||||
print("model finished")
|
||||
|
||||
def startQueue(self):
|
||||
self.__taskNum = self.rowCount()
|
||||
self.__taskFinishedNum = 0
|
||||
self.started.emit()
|
||||
for row in range(self.rowCount()):
|
||||
modelIndex = self.index(row, 0)
|
||||
runnable: OcrRunnable = modelIndex.data(self.OcrRunnableRole)
|
||||
runnable.signals.rowId = row
|
||||
runnable.signals.resultReady.connect(self.ocrTaskReady)
|
||||
runnable.signals.finished.connect(self.ocrTaskFinished)
|
||||
QThreadPool.globalInstance().start(runnable)
|
||||
|
||||
def updateScoreValidateOk(self, row: int):
|
||||
if not 0 <= row < self.rowCount():
|
||||
return
|
||||
|
||||
index = self.index(row, 0)
|
||||
chart = index.data(self.ChartRole)
|
||||
score = index.data(self.ScoreInsertRole)
|
||||
if isinstance(chart, Chart) and isinstance(score, ScoreInsert):
|
||||
scoreRange = calculate_score_range(chart, score.pure, score.far)
|
||||
scoreValidateOk = scoreRange[0] <= score.score <= scoreRange[1]
|
||||
self.setData(index, scoreValidateOk, self.ScoreValidateOkRole)
|
||||
else:
|
||||
self.setData(index, False, self.ScoreValidateOkRole)
|
||||
|
||||
def acceptItem(self, row: int, ignoreValidate: bool = False):
|
||||
if not 0 <= row < self.rowCount():
|
||||
return
|
||||
|
||||
item = self.__items[row]
|
||||
score = item[self.ScoreInsertRole]
|
||||
if not isinstance(score, ScoreInsert) or (
|
||||
not item[self.ScoreValidateOkRole] and not ignoreValidate
|
||||
):
|
||||
return
|
||||
|
||||
try:
|
||||
self.__db.insert_score(score)
|
||||
self.beginRemoveRows(QModelIndex(), row, row)
|
||||
self.__items.pop(row)
|
||||
self.endRemoveRows()
|
||||
return
|
||||
except Exception as e:
|
||||
logger.exception(f"Error accepting {repr(item)}")
|
||||
return
|
||||
|
||||
def acceptItems(self, __rows: list[int], ignoreValidate: bool = False):
|
||||
items = sorted(__rows, reverse=True)
|
||||
[self.acceptItem(item, ignoreValidate) for item in items]
|
||||
|
||||
def acceptAllItems(self, ignoreValidate: bool = False):
|
||||
self.acceptItems([*range(self.rowCount())], ignoreValidate)
|
||||
|
||||
def removeItem(self, row: int):
|
||||
if not 0 <= row < self.rowCount():
|
||||
return
|
||||
|
||||
self.beginRemoveRows(QModelIndex(), row, row)
|
||||
self.__items.pop(row)
|
||||
self.endRemoveRows()
|
||||
|
||||
def removeItems(self, __rows: list[int]):
|
||||
rows = sorted(__rows, reverse=True)
|
||||
[self.removeItem(row) for row in rows]
|
||||
|
||||
|
||||
class OcrQueueTableProxyModel(QAbstractTableModel):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.retranslateHeaders()
|
||||
self.__sourceModel = None
|
||||
self.__columnRoleMapping = [
|
||||
[Qt.ItemDataRole.CheckStateRole],
|
||||
[
|
||||
OcrQueueModel.ImagePathRole,
|
||||
OcrQueueModel.ImageQImageRole,
|
||||
OcrQueueModel.ImagePixmapRole,
|
||||
],
|
||||
[
|
||||
OcrQueueModel.DeviceOcrResultRole,
|
||||
OcrQueueModel.ChartRole,
|
||||
],
|
||||
[
|
||||
OcrQueueModel.DeviceOcrResultRole,
|
||||
OcrQueueModel.ScoreInsertRole,
|
||||
OcrQueueModel.ChartRole,
|
||||
OcrQueueModel.ScoreValidateOkRole,
|
||||
],
|
||||
]
|
||||
|
||||
def retranslateHeaders(self):
|
||||
self.__horizontalHeaders = [
|
||||
# fmt: off
|
||||
QCoreApplication.translate("OcrTableModel", "horizontalHeader.title.select"),
|
||||
QCoreApplication.translate("OcrTableModel", "horizontalHeader.title.imagePreview"),
|
||||
QCoreApplication.translate("OcrTableModel", "horizontalHeader.title.chart"),
|
||||
QCoreApplication.translate("OcrTableModel", "horizontalHeader.title.score"),
|
||||
# fmt: on
|
||||
]
|
||||
|
||||
def sourceModel(self) -> OcrQueueModel:
|
||||
return self.__sourceModel
|
||||
|
||||
def setSourceModel(self, sourceModel):
|
||||
if not isinstance(sourceModel, OcrQueueModel):
|
||||
return False
|
||||
|
||||
# connect signals
|
||||
sourceModel.rowsAboutToBeInserted.connect(self.rowsAboutToBeInserted)
|
||||
sourceModel.rowsInserted.connect(self.rowsInserted)
|
||||
sourceModel.rowsAboutToBeRemoved.connect(self.rowsAboutToBeRemoved)
|
||||
sourceModel.rowsRemoved.connect(self.rowsRemoved)
|
||||
sourceModel.dataChanged.connect(self.dataChanged)
|
||||
sourceModel.layoutAboutToBeChanged.connect(self.layoutAboutToBeChanged)
|
||||
sourceModel.layoutChanged.connect(self.layoutChanged)
|
||||
|
||||
self.__sourceModel = sourceModel
|
||||
return True
|
||||
|
||||
def rowCount(self, *args):
|
||||
return self.sourceModel().rowCount()
|
||||
|
||||
def columnCount(self, *args):
|
||||
return len(self.__horizontalHeaders)
|
||||
|
||||
def headerData(self, section: int, orientation: Qt.Orientation, role: int):
|
||||
if (
|
||||
orientation == Qt.Orientation.Horizontal
|
||||
and 0 <= section < len(self.__horizontalHeaders)
|
||||
and role == Qt.ItemDataRole.DisplayRole
|
||||
):
|
||||
return self.__horizontalHeaders[section]
|
||||
return None
|
||||
|
||||
def data(self, index, role):
|
||||
if (
|
||||
0 <= index.row() < self.rowCount()
|
||||
and 0 <= index.column() < self.columnCount()
|
||||
and role in self.__columnRoleMapping[index.column()]
|
||||
):
|
||||
srcIndex = self.sourceModel().index(index.row(), 0)
|
||||
return srcIndex.data(role)
|
||||
return None
|
||||
|
||||
def setData(self, index, value, role):
|
||||
if index.column() == 2 and role == OcrQueueModel.ChartRole:
|
||||
return self.sourceModel().setItemChart(index.row(), value)
|
||||
if index.column() == 3 and role == OcrQueueModel.ScoreInsertRole:
|
||||
return self.sourceModel().setItemScore(index.row(), value)
|
||||
return False
|
||||
|
||||
def flags(self, index: QModelIndex) -> Qt.ItemFlag:
|
||||
flags = (
|
||||
self.sourceModel().flags(index)
|
||||
if isinstance(self.sourceModel(), OcrQueueModel)
|
||||
else super().flags(index)
|
||||
)
|
||||
flags = flags | Qt.ItemFlag.ItemIsEnabled
|
||||
flags = flags | Qt.ItemFlag.ItemIsEditable
|
||||
flags = flags | Qt.ItemFlag.ItemIsSelectable
|
||||
if index.column() == 0:
|
||||
flags = flags & ~Qt.ItemFlag.ItemIsEnabled & ~Qt.ItemFlag.ItemIsEditable
|
||||
return flags
|
||||
|
||||
|
||||
class OcrImageDelegate(ImageDelegate):
|
||||
def getPixmap(self, index: QModelIndex):
|
||||
return index.data(OcrQueueModel.ImagePixmapRole)
|
||||
|
||||
def getImagePath(self, index: QModelIndex):
|
||||
return index.data(OcrQueueModel.ImagePathRole)
|
||||
|
||||
|
||||
class OcrChartDelegate(ChartDelegate):
|
||||
def getChart(self, index: QModelIndex) -> Chart | None:
|
||||
return index.data(OcrQueueModel.ChartRole)
|
||||
|
||||
def paintWarningBackground(self, index: QModelIndex) -> bool:
|
||||
return isinstance(
|
||||
index.data(OcrQueueModel.DeviceOcrResultRole), DeviceOcrResult
|
||||
)
|
||||
|
||||
def setModelData(self, editor, model: OcrQueueTableProxyModel, index):
|
||||
if editor.validate():
|
||||
model.setData(index, editor.value(), OcrQueueModel.ChartRole)
|
||||
|
||||
|
||||
class OcrScoreDelegate(ScoreDelegate):
|
||||
def getScoreInsert(self, index: QModelIndex):
|
||||
return index.data(OcrQueueModel.ScoreInsertRole)
|
||||
|
||||
def getChart(self, index: QModelIndex):
|
||||
return index.data(OcrQueueModel.ChartRole)
|
||||
|
||||
def getScoreValidateOk(self, index: QModelIndex):
|
||||
return index.data(OcrQueueModel.ScoreValidateOkRole)
|
||||
|
||||
def paintWarningBackground(self, index: QModelIndex) -> bool:
|
||||
return isinstance(
|
||||
index.data(OcrQueueModel.DeviceOcrResultRole), DeviceOcrResult
|
||||
)
|
||||
|
||||
def setModelData(self, editor, model: OcrQueueTableProxyModel, index):
|
||||
if super().confirmSetModelData(editor):
|
||||
model.setData(index, editor.value(), OcrQueueModel.ScoreInsertRole)
|
@ -1,16 +1,24 @@
|
||||
try:
|
||||
import json
|
||||
|
||||
from arcaea_offline_ocr.device import Device
|
||||
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[Device]:
|
||||
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)
|
||||
return [Device.from_json_object(item) for item in content]
|
||||
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:
|
||||
|
||||
|
70
ui/extends/shared/delegates/imageDelegate.py
Normal file
70
ui/extends/shared/delegates/imageDelegate.py
Normal file
@ -0,0 +1,70 @@
|
||||
from PySide6.QtCore import QFileInfo, QModelIndex, QRect, QSize, Qt
|
||||
from PySide6.QtGui import QPixmap
|
||||
from PySide6.QtWidgets import QLabel, QStyledItemDelegate, QWidget
|
||||
|
||||
|
||||
class ImageDelegate(QStyledItemDelegate):
|
||||
def getPixmap(self, index: QModelIndex):
|
||||
raise NotImplementedError("getPixmap not implemented.")
|
||||
|
||||
def getImagePath(self, index: QModelIndex):
|
||||
raise NotImplementedError("getImagePath not implemented.")
|
||||
|
||||
def scalePixmap(self, pixmap: QPixmap):
|
||||
return pixmap.scaled(
|
||||
100,
|
||||
100,
|
||||
Qt.AspectRatioMode.KeepAspectRatio,
|
||||
Qt.TransformationMode.SmoothTransformation,
|
||||
)
|
||||
|
||||
def paint(self, painter, option, index):
|
||||
pixmap = self.getPixmap(index)
|
||||
if not isinstance(pixmap, QPixmap):
|
||||
imagePath = self.getImagePath(index)
|
||||
option.text = imagePath
|
||||
super().paint(painter, option, index)
|
||||
else:
|
||||
pixmap = self.scalePixmap(pixmap)
|
||||
# https://stackoverflow.com/a/32047499/16484891
|
||||
# CC BY-SA 3.0
|
||||
x = option.rect.center().x() - pixmap.rect().width() / 2
|
||||
y = option.rect.center().y() - pixmap.rect().height() / 2
|
||||
|
||||
painter.drawPixmap(
|
||||
QRect(x, y, pixmap.rect().width(), pixmap.rect().height()), pixmap
|
||||
)
|
||||
|
||||
def sizeHint(self, option, index) -> QSize:
|
||||
pixmap = self.getPixmap(index)
|
||||
if isinstance(pixmap, QPixmap):
|
||||
pixmap = self.scalePixmap(pixmap)
|
||||
return pixmap.size()
|
||||
else:
|
||||
return QSize(100, 75)
|
||||
|
||||
def createEditor(self, parent, option, index) -> QWidget:
|
||||
pixmap = self.getPixmap(index)
|
||||
if isinstance(pixmap, QPixmap):
|
||||
label = QLabel(parent)
|
||||
label.setWindowFlags(Qt.WindowType.Window)
|
||||
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())
|
||||
pixmap = pixmap.scaled(
|
||||
800,
|
||||
800,
|
||||
Qt.AspectRatioMode.KeepAspectRatio,
|
||||
Qt.TransformationMode.SmoothTransformation,
|
||||
)
|
||||
label.setMinimumSize(pixmap.size())
|
||||
label.setPixmap(pixmap)
|
||||
label.move(parent.mapToGlobal(parent.pos()))
|
||||
return label
|
||||
|
||||
def setModelData(self, *args):
|
||||
...
|
||||
|
||||
def updateEditorGeometry(self, *args):
|
||||
...
|
@ -126,6 +126,8 @@ class ScoreDelegate(TextSegmentDelegate):
|
||||
]
|
||||
]
|
||||
|
||||
score_str = str(score.score).rjust(8, "0")
|
||||
score_str = f"{score_str[:-6]}'{score_str[-6:-3]}'{score_str[-3:]}"
|
||||
score_font = QFont(option.font)
|
||||
score_font.setPointSize(12)
|
||||
score_grade_font = QFont(score_font)
|
||||
@ -140,7 +142,7 @@ class ScoreDelegate(TextSegmentDelegate):
|
||||
self.FontRole: score_grade_font,
|
||||
},
|
||||
{self.TextRole: " | "},
|
||||
{self.TextRole: str(score.score), self.FontRole: score_font},
|
||||
{self.TextRole: score_str, self.FontRole: score_font},
|
||||
],
|
||||
[
|
||||
{
|
||||
|
@ -1,133 +1,43 @@
|
||||
import contextlib
|
||||
import logging
|
||||
from typing import Any
|
||||
from typing import Tuple
|
||||
|
||||
import exif
|
||||
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.device import Device
|
||||
from arcaea_offline_ocr.recognize import RecognizeResult, recognize
|
||||
from PySide6.QtCore import (
|
||||
QAbstractListModel,
|
||||
QAbstractTableModel,
|
||||
QCoreApplication,
|
||||
QDateTime,
|
||||
QFileInfo,
|
||||
QModelIndex,
|
||||
QObject,
|
||||
QRect,
|
||||
QRunnable,
|
||||
QSize,
|
||||
Qt,
|
||||
QThreadPool,
|
||||
Signal,
|
||||
Slot,
|
||||
)
|
||||
from PySide6.QtGui import QPixmap
|
||||
from PySide6.QtWidgets import QLabel, QStyledItemDelegate, QWidget
|
||||
from arcaea_offline.models import ScoreInsert, Chart
|
||||
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
|
||||
from arcaea_offline_ocr.utils import imread_unicode
|
||||
from PySide6.QtCore import QDateTime, QFileInfo
|
||||
|
||||
from ui.extends.shared.delegates.chartDelegate import ChartDelegate
|
||||
from ui.extends.shared.delegates.scoreDelegate import ScoreDelegate
|
||||
from ui.implements.components.scoreEditor import ScoreEditor
|
||||
from ui.extends.components.ocrQueue import OcrRunnable
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class OcrTaskSignals(QObject):
|
||||
resultReady = Signal(int, RecognizeResult)
|
||||
finished = Signal(int)
|
||||
import exif
|
||||
|
||||
|
||||
class OcrTask(QRunnable):
|
||||
def __init__(self, index: int, device: Device, imagePath: str):
|
||||
class TabDeviceV2OcrRunnable(OcrRunnable):
|
||||
def __init__(self, imagePath, device, knnModel, siftDb):
|
||||
super().__init__()
|
||||
self.index = index
|
||||
self.device = device
|
||||
self.imagePath = imagePath
|
||||
self.signals = OcrTaskSignals()
|
||||
self.device = device
|
||||
self.knnModel = knnModel
|
||||
self.siftDb = siftDb
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
result = recognize(self.imagePath, self.device)
|
||||
self.signals.resultReady.emit(self.index, result)
|
||||
logger.info(
|
||||
f"OcrTask {self.imagePath} with {repr(self.device)} got result {repr(result)}"
|
||||
)
|
||||
except Exception as e:
|
||||
logger.exception(
|
||||
f"OcrTask {self.imagePath} with {repr(self.device)} failed"
|
||||
)
|
||||
rois = DeviceV2Rois(self.device, imread_unicode(self.imagePath))
|
||||
ocr = DeviceV2Ocr(self.knnModel, self.siftDb)
|
||||
result = ocr.ocr(rois)
|
||||
self.signals.resultReady.emit(result)
|
||||
except Exception:
|
||||
logger.exception(f"DeviceV2 ocr {self.imagePath} error")
|
||||
finally:
|
||||
self.signals.finished.emit(self.index)
|
||||
self.signals.finished.emit()
|
||||
|
||||
|
||||
class OcrQueueModel(QAbstractListModel):
|
||||
ImagePathRole = Qt.ItemDataRole.UserRole + 1
|
||||
ImagePixmapRole = Qt.ItemDataRole.UserRole + 2
|
||||
RecognizeResultRole = Qt.ItemDataRole.UserRole + 10
|
||||
ScoreInsertRole = Qt.ItemDataRole.UserRole + 11
|
||||
ChartRole = Qt.ItemDataRole.UserRole + 12
|
||||
ScoreValidateOkRole = Qt.ItemDataRole.UserRole + 13
|
||||
|
||||
started = Signal()
|
||||
finished = Signal()
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.__db = Database()
|
||||
self.__items: list[dict[int, Any]] = []
|
||||
|
||||
@property
|
||||
def imagePaths(self):
|
||||
return [item.get(self.ImagePathRole) for item in self.__items]
|
||||
|
||||
def clear(self):
|
||||
self.beginResetModel()
|
||||
self.beginRemoveRows(QModelIndex(), 0, self.rowCount() - 1)
|
||||
self.__items.clear()
|
||||
self.endRemoveRows()
|
||||
self.endResetModel()
|
||||
|
||||
def rowCount(self, *args):
|
||||
return len(self.__items)
|
||||
|
||||
def data(self, index, role):
|
||||
if (
|
||||
index.isValid()
|
||||
and 0 <= index.row() < self.rowCount()
|
||||
and index.column() == 0
|
||||
):
|
||||
return self.__items[index.row()].get(role)
|
||||
return None
|
||||
|
||||
def setData(self, *args):
|
||||
return False
|
||||
|
||||
def addItem(self, imagePath: str):
|
||||
if imagePath in self.imagePaths or not QFileInfo(imagePath).exists():
|
||||
return
|
||||
|
||||
self.beginInsertRows(QModelIndex(), self.rowCount(), self.rowCount())
|
||||
self.__items.append(
|
||||
{
|
||||
self.ImagePathRole: imagePath,
|
||||
self.ImagePixmapRole: QPixmap(imagePath),
|
||||
self.RecognizeResultRole: None,
|
||||
self.ScoreInsertRole: None,
|
||||
self.ChartRole: None,
|
||||
self.ScoreValidateOkRole: False,
|
||||
}
|
||||
)
|
||||
self.endInsertRows()
|
||||
|
||||
def updateOcrResult(self, row: int, result: RecognizeResult) -> bool:
|
||||
if not 0 <= row < self.rowCount() or not isinstance(result, RecognizeResult):
|
||||
return False
|
||||
|
||||
item = self.__items[row]
|
||||
|
||||
imagePath: str = item[self.ImagePathRole]
|
||||
def getImageDate(imagePath: str) -> QDateTime:
|
||||
datetime = None
|
||||
with contextlib.suppress(Exception):
|
||||
with open(imagePath, "rb") as imgf:
|
||||
@ -137,340 +47,25 @@ class OcrQueueModel(QAbstractListModel):
|
||||
datetime = QDateTime.fromString(datetimeStr, "yyyy:MM:dd hh:mm:ss")
|
||||
if not isinstance(datetime, QDateTime):
|
||||
datetime = QFileInfo(imagePath).birthTime()
|
||||
return datetime
|
||||
|
||||
score = ScoreInsert(
|
||||
song_id=self.__db.fuzzy_search_song_id(result.title)[0][0],
|
||||
|
||||
class ScoreInsertConverter:
|
||||
@staticmethod
|
||||
def deviceV2(imagePath: str, result: DeviceOcrResult) -> Tuple[Chart, ScoreInsert]:
|
||||
db = Database()
|
||||
scoreInsert = ScoreInsert(
|
||||
song_id=result.song_id,
|
||||
rating_class=result.rating_class,
|
||||
score=result.score,
|
||||
pure=result.pure,
|
||||
far=result.far,
|
||||
lost=result.lost,
|
||||
time=datetime.toSecsSinceEpoch(),
|
||||
time=getImageDate(imagePath).toSecsSinceEpoch(),
|
||||
max_recall=result.max_recall,
|
||||
clear_type=None,
|
||||
)
|
||||
chart = Chart.from_db_row(
|
||||
self.__db.get_chart(score.song_id, score.rating_class)
|
||||
db.get_chart(scoreInsert.song_id, scoreInsert.rating_class)
|
||||
)
|
||||
|
||||
item[self.RecognizeResultRole] = result
|
||||
self.setItemChart(row, chart)
|
||||
self.setItemScore(row, score)
|
||||
modelIndex = self.index(row, 0)
|
||||
self.dataChanged.emit(
|
||||
modelIndex,
|
||||
modelIndex,
|
||||
[self.RecognizeResultRole, self.ScoreInsertRole, self.ChartRole],
|
||||
)
|
||||
return True
|
||||
|
||||
@Slot(int, RecognizeResult)
|
||||
def ocrTaskReady(self, row: int, result: RecognizeResult):
|
||||
self.updateOcrResult(row, result)
|
||||
|
||||
@Slot(int)
|
||||
def ocrTaskFinished(self, row: int):
|
||||
self.__taskFinishedNum += 1
|
||||
if self.__taskFinishedNum == self.__taskNum:
|
||||
self.finished.emit()
|
||||
|
||||
def startQueue(self, device: Device):
|
||||
self.__taskNum = self.rowCount()
|
||||
self.__taskFinishedNum = 0
|
||||
self.started.emit()
|
||||
for row in range(self.rowCount()):
|
||||
modelIndex = self.index(row, 0)
|
||||
imagePath: str = modelIndex.data(self.ImagePathRole)
|
||||
task = OcrTask(row, device, imagePath)
|
||||
task.signals.resultReady.connect(self.ocrTaskReady)
|
||||
task.signals.finished.connect(self.ocrTaskFinished)
|
||||
QThreadPool.globalInstance().start(task)
|
||||
|
||||
def updateScoreValidateOk(self, row: int):
|
||||
if not 0 <= row < self.rowCount():
|
||||
return
|
||||
|
||||
item = self.__items[row]
|
||||
chart = item[self.ChartRole]
|
||||
score = item[self.ScoreInsertRole]
|
||||
if isinstance(chart, Chart) and isinstance(score, ScoreInsert):
|
||||
scoreRange = calculate_score_range(chart, score.pure, score.far)
|
||||
scoreValidateOk = scoreRange[0] <= score.score <= scoreRange[1]
|
||||
item[self.ScoreValidateOkRole] = scoreValidateOk
|
||||
else:
|
||||
item[self.ScoreValidateOkRole] = False
|
||||
|
||||
modelIndex = self.index(row, 0)
|
||||
self.dataChanged.emit(modelIndex, modelIndex, [self.ScoreValidateOkRole])
|
||||
|
||||
def setItemChart(self, row: int, chart: Chart):
|
||||
if not 0 <= row < self.rowCount() or not isinstance(chart, Chart):
|
||||
return False
|
||||
|
||||
item = self.__items[row]
|
||||
item[self.ChartRole] = chart
|
||||
updatedRoles = [self.ChartRole]
|
||||
|
||||
self.updateScoreValidateOk(row)
|
||||
|
||||
modelIndex = self.index(row, 0)
|
||||
self.dataChanged.emit(modelIndex, modelIndex, updatedRoles)
|
||||
return True
|
||||
|
||||
def setItemScore(self, row: int, score: ScoreInsert) -> bool:
|
||||
if not 0 <= row < self.rowCount() or not isinstance(score, ScoreInsert):
|
||||
return False
|
||||
|
||||
item = self.__items[row]
|
||||
item[self.ScoreInsertRole] = score
|
||||
updatedRoles = [self.ScoreInsertRole]
|
||||
|
||||
self.updateScoreValidateOk(row)
|
||||
|
||||
modelIndex = self.index(row, 0)
|
||||
self.dataChanged.emit(modelIndex, modelIndex, updatedRoles)
|
||||
return True
|
||||
|
||||
def acceptItem(self, row: int, ignoreValidate: bool = False):
|
||||
if not 0 <= row < self.rowCount():
|
||||
return
|
||||
|
||||
item = self.__items[row]
|
||||
score = item[self.ScoreInsertRole]
|
||||
if not isinstance(score, ScoreInsert) or (
|
||||
not item[self.ScoreValidateOkRole] and not ignoreValidate
|
||||
):
|
||||
return
|
||||
|
||||
try:
|
||||
self.__db.insert_score(score)
|
||||
self.beginRemoveRows(QModelIndex(), row, row)
|
||||
self.__items.pop(row)
|
||||
self.endRemoveRows()
|
||||
return
|
||||
except Exception as e:
|
||||
logger.exception(f"Error accepting {repr(item)}")
|
||||
return
|
||||
|
||||
def acceptItems(self, __rows: list[int], ignoreValidate: bool = False):
|
||||
items = sorted(__rows, reverse=True)
|
||||
[self.acceptItem(item, ignoreValidate) for item in items]
|
||||
|
||||
def acceptAllItems(self, ignoreValidate: bool = False):
|
||||
self.acceptItems([*range(self.rowCount())], ignoreValidate)
|
||||
|
||||
def removeItem(self, row: int):
|
||||
if not 0 <= row < self.rowCount():
|
||||
return
|
||||
|
||||
self.beginRemoveRows(QModelIndex(), row, row)
|
||||
self.__items.pop(row)
|
||||
self.endRemoveRows()
|
||||
|
||||
def removeItems(self, __rows: list[int]):
|
||||
rows = sorted(__rows, reverse=True)
|
||||
[self.removeItem(row) for row in rows]
|
||||
|
||||
|
||||
class OcrQueueTableProxyModel(QAbstractTableModel):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.retranslateHeaders()
|
||||
self.__sourceModel = None
|
||||
self.__columnRoleMapping = [
|
||||
[Qt.ItemDataRole.CheckStateRole],
|
||||
[OcrQueueModel.ImagePathRole, OcrQueueModel.ImagePixmapRole],
|
||||
[
|
||||
OcrQueueModel.RecognizeResultRole,
|
||||
OcrQueueModel.ChartRole,
|
||||
],
|
||||
[
|
||||
OcrQueueModel.RecognizeResultRole,
|
||||
OcrQueueModel.ScoreInsertRole,
|
||||
OcrQueueModel.ChartRole,
|
||||
OcrQueueModel.ScoreValidateOkRole,
|
||||
],
|
||||
]
|
||||
|
||||
def retranslateHeaders(self):
|
||||
self.__horizontalHeaders = [
|
||||
# fmt: off
|
||||
QCoreApplication.translate("OcrTableModel", "horizontalHeader.title.select"),
|
||||
QCoreApplication.translate("OcrTableModel", "horizontalHeader.title.imagePreview"),
|
||||
QCoreApplication.translate("OcrTableModel", "horizontalHeader.title.chart"),
|
||||
QCoreApplication.translate("OcrTableModel", "horizontalHeader.title.score"),
|
||||
# fmt: on
|
||||
]
|
||||
|
||||
def sourceModel(self) -> OcrQueueModel:
|
||||
return self.__sourceModel
|
||||
|
||||
def setSourceModel(self, sourceModel):
|
||||
if not isinstance(sourceModel, OcrQueueModel):
|
||||
return False
|
||||
|
||||
# connect signals
|
||||
sourceModel.rowsAboutToBeInserted.connect(self.rowsAboutToBeInserted)
|
||||
sourceModel.rowsInserted.connect(self.rowsInserted)
|
||||
sourceModel.rowsAboutToBeRemoved.connect(self.rowsAboutToBeRemoved)
|
||||
sourceModel.rowsRemoved.connect(self.rowsRemoved)
|
||||
sourceModel.dataChanged.connect(self.dataChanged)
|
||||
sourceModel.layoutAboutToBeChanged.connect(self.layoutAboutToBeChanged)
|
||||
sourceModel.layoutChanged.connect(self.layoutChanged)
|
||||
|
||||
self.__sourceModel = sourceModel
|
||||
return True
|
||||
|
||||
def rowCount(self, *args):
|
||||
return self.sourceModel().rowCount()
|
||||
|
||||
def columnCount(self, *args):
|
||||
return len(self.__horizontalHeaders)
|
||||
|
||||
def headerData(self, section: int, orientation: Qt.Orientation, role: int):
|
||||
if (
|
||||
orientation == Qt.Orientation.Horizontal
|
||||
and 0 <= section < len(self.__horizontalHeaders)
|
||||
and role == Qt.ItemDataRole.DisplayRole
|
||||
):
|
||||
return self.__horizontalHeaders[section]
|
||||
return None
|
||||
|
||||
def data(self, index, role):
|
||||
if (
|
||||
0 <= index.row() < self.rowCount()
|
||||
and 0 <= index.column() < self.columnCount()
|
||||
and role in self.__columnRoleMapping[index.column()]
|
||||
):
|
||||
srcIndex = self.sourceModel().index(index.row(), 0)
|
||||
return srcIndex.data(role)
|
||||
return None
|
||||
|
||||
def setData(self, index, value, role):
|
||||
if index.column() == 2 and role == OcrQueueModel.ChartRole:
|
||||
return self.sourceModel().setItemChart(index.row(), value)
|
||||
if index.column() == 3 and role == OcrQueueModel.ScoreInsertRole:
|
||||
return self.sourceModel().setItemScore(index.row(), value)
|
||||
return False
|
||||
|
||||
def flags(self, index: QModelIndex) -> Qt.ItemFlag:
|
||||
flags = (
|
||||
self.sourceModel().flags(index)
|
||||
if isinstance(self.sourceModel(), OcrQueueModel)
|
||||
else super().flags(index)
|
||||
)
|
||||
flags = flags | Qt.ItemFlag.ItemIsEnabled
|
||||
flags = flags | Qt.ItemFlag.ItemIsEditable
|
||||
flags = flags | Qt.ItemFlag.ItemIsSelectable
|
||||
if index.column() == 0:
|
||||
flags = flags & ~Qt.ItemFlag.ItemIsEnabled & ~Qt.ItemFlag.ItemIsEditable
|
||||
return flags
|
||||
|
||||
|
||||
class ImageDelegate(QStyledItemDelegate):
|
||||
def getPixmap(self, index: QModelIndex):
|
||||
return index.data(OcrQueueModel.ImagePixmapRole)
|
||||
|
||||
def getImagePath(self, index: QModelIndex):
|
||||
return index.data(OcrQueueModel.ImagePathRole)
|
||||
|
||||
def scalePixmap(self, pixmap: QPixmap):
|
||||
return pixmap.scaled(
|
||||
100,
|
||||
100,
|
||||
Qt.AspectRatioMode.KeepAspectRatio,
|
||||
Qt.TransformationMode.SmoothTransformation,
|
||||
)
|
||||
|
||||
def paint(self, painter, option, index):
|
||||
pixmap = self.getPixmap(index)
|
||||
if not isinstance(pixmap, QPixmap):
|
||||
imagePath = self.getImagePath(index)
|
||||
option.text = imagePath
|
||||
super().paint(painter, option, index)
|
||||
else:
|
||||
pixmap = self.scalePixmap(pixmap)
|
||||
# https://stackoverflow.com/a/32047499/16484891
|
||||
# CC BY-SA 3.0
|
||||
x = option.rect.center().x() - pixmap.rect().width() / 2
|
||||
y = option.rect.center().y() - pixmap.rect().height() / 2
|
||||
|
||||
painter.drawPixmap(
|
||||
QRect(x, y, pixmap.rect().width(), pixmap.rect().height()), pixmap
|
||||
)
|
||||
|
||||
def sizeHint(self, option, index) -> QSize:
|
||||
pixmap = self.getPixmap(index)
|
||||
if isinstance(pixmap, QPixmap):
|
||||
pixmap = self.scalePixmap(pixmap)
|
||||
return pixmap.size()
|
||||
else:
|
||||
return QSize(100, 75)
|
||||
|
||||
def createEditor(self, parent, option, index) -> QWidget:
|
||||
pixmap = self.getPixmap(index)
|
||||
if isinstance(pixmap, QPixmap):
|
||||
label = QLabel(parent)
|
||||
label.setWindowFlags(Qt.WindowType.Window)
|
||||
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())
|
||||
pixmap = pixmap.scaled(
|
||||
800,
|
||||
800,
|
||||
Qt.AspectRatioMode.KeepAspectRatio,
|
||||
Qt.TransformationMode.SmoothTransformation,
|
||||
)
|
||||
label.setMinimumSize(pixmap.size())
|
||||
label.setPixmap(pixmap)
|
||||
label.move(parent.mapToGlobal(parent.pos()))
|
||||
return label
|
||||
|
||||
def setModelData(self, *args):
|
||||
...
|
||||
|
||||
def updateEditorGeometry(self, *args):
|
||||
...
|
||||
|
||||
|
||||
class TableChartDelegate(ChartDelegate):
|
||||
def getChart(self, index: QModelIndex) -> Chart | None:
|
||||
return index.data(OcrQueueModel.ChartRole)
|
||||
|
||||
def paintWarningBackground(self, index: QModelIndex) -> bool:
|
||||
return isinstance(
|
||||
index.data(OcrQueueModel.RecognizeResultRole), RecognizeResult
|
||||
)
|
||||
|
||||
def setModelData(self, editor, model: OcrQueueTableProxyModel, index):
|
||||
if editor.validate():
|
||||
model.setData(index, editor.value(), OcrQueueModel.ChartRole)
|
||||
|
||||
|
||||
class TableScoreDelegate(ScoreDelegate):
|
||||
def getScoreInsert(self, index: QModelIndex):
|
||||
return index.data(OcrQueueModel.ScoreInsertRole)
|
||||
|
||||
def getChart(self, index: QModelIndex):
|
||||
return index.data(OcrQueueModel.ChartRole)
|
||||
|
||||
def getScoreValidateOk(self, index: QModelIndex):
|
||||
return index.data(OcrQueueModel.ScoreValidateOkRole)
|
||||
|
||||
def paintWarningBackground(self, index: QModelIndex) -> bool:
|
||||
return isinstance(
|
||||
index.data(OcrQueueModel.RecognizeResultRole), RecognizeResult
|
||||
)
|
||||
|
||||
# def createEditor(self, parent, option, index):
|
||||
# editor = super().createEditor(parent, option, index)
|
||||
# editor.setManualHandleCommit(True)
|
||||
# return editor
|
||||
|
||||
def setModelData(self, editor, model: OcrQueueTableProxyModel, index):
|
||||
# userAcceptMessageBox = editor.triggerValidateMessageBox()
|
||||
# if userAcceptMessageBox:
|
||||
# model.setData(index, editor.value(), OcrQueueModel.ScoreInsertRole)
|
||||
if super().confirmSetModelData(editor):
|
||||
model.setData(index, editor.value(), OcrQueueModel.ScoreInsertRole)
|
||||
return (chart, scoreInsert)
|
||||
|
@ -4,3 +4,4 @@ from .elidedLabel import ElidedLabel
|
||||
from .fileSelector import FileSelector
|
||||
from .ratingClassRadioButton import RatingClassRadioButton
|
||||
from .scoreEditor import ScoreEditor
|
||||
from .ocrQueue import OcrQueue
|
||||
|
@ -1,4 +1,4 @@
|
||||
from arcaea_offline_ocr.device import Device
|
||||
from arcaea_offline_ocr.device.v1.definition import DeviceV1
|
||||
from PySide6.QtCore import Qt
|
||||
from PySide6.QtWidgets import QComboBox
|
||||
|
||||
@ -13,7 +13,7 @@ class DevicesComboBox(QComboBox):
|
||||
super().__init__(parent)
|
||||
self.setItemDelegate(DescriptionDelegate(self))
|
||||
|
||||
def setDevices(self, devices: list[Device]):
|
||||
def setDevices(self, devices: list[DeviceV1]):
|
||||
self.clear()
|
||||
for device in devices:
|
||||
self.addItem(f"{device.name} ({device.uuid})", device)
|
||||
|
133
ui/implements/components/ocrQueue.py
Normal file
133
ui/implements/components/ocrQueue.py
Normal file
@ -0,0 +1,133 @@
|
||||
from typing import Optional
|
||||
|
||||
from PySide6.QtCore import Qt, QTimer, Slot
|
||||
from PySide6.QtGui import QColor, QPalette
|
||||
from PySide6.QtWidgets import QWidget
|
||||
|
||||
from ui.designer.components.ocrQueue_ui import Ui_OcrQueue
|
||||
from ui.extends.components.ocrQueue import (
|
||||
OcrChartDelegate,
|
||||
OcrImageDelegate,
|
||||
OcrQueueModel,
|
||||
OcrQueueTableProxyModel,
|
||||
OcrScoreDelegate,
|
||||
)
|
||||
|
||||
|
||||
class OcrQueue(Ui_OcrQueue, QWidget):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setupUi(self)
|
||||
self.__model: Optional[OcrQueueModel] = None
|
||||
self.__tableProxyModel: Optional[OcrQueueTableProxyModel] = None
|
||||
|
||||
self.__firstResizeDone = False
|
||||
self.resizeTimer = QTimer(self)
|
||||
self.resizeTimer.timeout.connect(self.tableView.resizeRowsToContents)
|
||||
self.resizeTimer.timeout.connect(self.tableView.resizeColumnsToContents)
|
||||
|
||||
self.tableView.setItemDelegateForColumn(1, OcrImageDelegate(self.tableView))
|
||||
self.tableView.setItemDelegateForColumn(2, OcrChartDelegate(self.tableView))
|
||||
self.tableView.setItemDelegateForColumn(3, OcrScoreDelegate(self.tableView))
|
||||
|
||||
tableViewPalette = QPalette(self.tableView.palette())
|
||||
highlightColor = QColor(tableViewPalette.color(QPalette.ColorRole.Highlight))
|
||||
highlightColor.setAlpha(25)
|
||||
tableViewPalette.setColor(QPalette.ColorRole.Highlight, highlightColor)
|
||||
self.tableView.setPalette(tableViewPalette)
|
||||
|
||||
def model(self):
|
||||
return self.__model
|
||||
|
||||
def setModel(self, model: OcrQueueModel):
|
||||
model.dataChanged.connect(self.resizeViewWhenDataChanged)
|
||||
model.started.connect(self.ocrStarted)
|
||||
model.progress.connect(self.ocrProgress)
|
||||
model.finished.connect(self.ocrFinished)
|
||||
model.rowsInserted.connect(self.updateProgressBarMaximum)
|
||||
model.rowsRemoved.connect(self.updateProgressBarMaximum)
|
||||
model.modelReset.connect(self.modelReseted)
|
||||
proxyModel = OcrQueueTableProxyModel(self)
|
||||
proxyModel.setSourceModel(model)
|
||||
self.tableView.setModel(proxyModel)
|
||||
|
||||
if self.__model:
|
||||
self.__model.dataChanged.disconnect(self.resizeViewWhenDataChanged)
|
||||
self.__model.started.disconnect(self.ocrStarted)
|
||||
self.__model.progress.disconnect(self.ocrProgress)
|
||||
self.__model.finished.disconnect(self.ocrFinished)
|
||||
self.__model.rowsInserted.disconnect(self.updateProgressBarMaximum)
|
||||
self.__model.rowsRemoved.disconnect(self.updateProgressBarMaximum)
|
||||
self.__model.modelReset.disconnect(self.modelReseted)
|
||||
if self.__tableProxyModel:
|
||||
self.__tableProxyModel.deleteLater()
|
||||
|
||||
self.__model = model
|
||||
self.__tableProxyModel = proxyModel
|
||||
|
||||
def tableProxyModel(self):
|
||||
return self.__tableProxyModel
|
||||
|
||||
def setOcrButtonsEnabled(self, __bool: bool):
|
||||
self.ocr_addImageButton.setEnabled(__bool)
|
||||
self.ocr_removeSelectedButton.setEnabled(__bool)
|
||||
self.ocr_removeAllButton.setEnabled(__bool)
|
||||
self.ocr_startButton.setEnabled(__bool)
|
||||
self.ocr_acceptSelectedButton.setEnabled(__bool)
|
||||
self.ocr_acceptAllButton.setEnabled(__bool)
|
||||
self.ocr_ignoreValidateCheckBox.setEnabled(__bool)
|
||||
|
||||
def resizeTableView(self):
|
||||
self.tableView.resizeRowsToContents()
|
||||
self.tableView.resizeColumnsToContents()
|
||||
|
||||
def resizeViewWhenDataChanged(self):
|
||||
if not self.__firstResizeDone:
|
||||
self.resizeTableView()
|
||||
self.__firstResizeDone = True
|
||||
|
||||
def ocrStarted(self):
|
||||
self.setOcrButtonsEnabled(False)
|
||||
|
||||
def updateProgressBarMaximum(self):
|
||||
self.progressBar.setMaximum(self.model().rowCount())
|
||||
|
||||
@Slot(int)
|
||||
def ocrProgress(self, progress: int):
|
||||
self.progressBar.setValue(progress)
|
||||
|
||||
def ocrFinished(self):
|
||||
self.resizeTableView()
|
||||
self.setOcrButtonsEnabled(True)
|
||||
|
||||
def modelReseted(self):
|
||||
self.progressBar.setMaximum(0)
|
||||
|
||||
@Slot()
|
||||
def on_ocr_removeSelectedButton_clicked(self):
|
||||
if self.model():
|
||||
rows = [
|
||||
modelIndex.row()
|
||||
for modelIndex in self.tableView.selectionModel().selectedRows(0)
|
||||
]
|
||||
self.model().removeItems(rows)
|
||||
|
||||
@Slot()
|
||||
def on_ocr_acceptSelectedButton_clicked(self):
|
||||
if self.model():
|
||||
ignoreValidate = (
|
||||
self.ocr_ignoreValidateCheckBox.checkState() == Qt.CheckState.Checked
|
||||
)
|
||||
rows = [
|
||||
modelIndex.row()
|
||||
for modelIndex in self.tableView.selectionModel().selectedRows(0)
|
||||
]
|
||||
self.model().acceptItems(rows, ignoreValidate)
|
||||
|
||||
@Slot()
|
||||
def on_ocr_acceptAllButton_clicked(self):
|
||||
if self.model():
|
||||
ignoreValidate = (
|
||||
self.ocr_ignoreValidateCheckBox.checkState() == Qt.CheckState.Checked
|
||||
)
|
||||
self.model().acceptAllItems(ignoreValidate)
|
@ -1,24 +1,21 @@
|
||||
import pytesseract
|
||||
from arcaea_offline_ocr_device_creation_wizard.implements.wizard import Wizard
|
||||
|
||||
# from arcaea_offline_ocr_device_creation_wizard.implements.wizard import Wizard
|
||||
from PySide6.QtCore import QModelIndex, Qt, Slot
|
||||
from PySide6.QtGui import QColor, QPalette
|
||||
from PySide6.QtWidgets import QFileDialog, QWidget
|
||||
from PySide6.QtGui import QColor
|
||||
from PySide6.QtWidgets import QFileDialog, QHeaderView, QWidget
|
||||
|
||||
from ui.designer.tabs.tabOcr_ui import Ui_TabOcr
|
||||
from ui.extends.components.ocrQueue import OcrQueueModel
|
||||
from ui.extends.settings import Settings
|
||||
from ui.extends.tabs.tabOcr import (
|
||||
ImageDelegate,
|
||||
OcrQueueModel,
|
||||
OcrQueueTableProxyModel,
|
||||
TableChartDelegate,
|
||||
TableScoreDelegate,
|
||||
)
|
||||
from ui.extends.tabs.tabOcr import TabDeviceV2OcrRunnable, ScoreInsertConverter
|
||||
|
||||
|
||||
class TabOcr(Ui_TabOcr, QWidget):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setupUi(self)
|
||||
self.openWizardButton.setEnabled(False)
|
||||
|
||||
self.deviceFileSelector.filesSelected.connect(self.deviceFileSelected)
|
||||
self.tesseractFileSelector.filesSelected.connect(
|
||||
@ -31,36 +28,14 @@ class TabOcr(Ui_TabOcr, QWidget):
|
||||
self.deviceComboBox.selectDevice(settings.deviceUuid())
|
||||
|
||||
self.ocrQueueModel = OcrQueueModel(self)
|
||||
self.ocrQueueModel.dataChanged.connect(self.resizeViewWhenScoreChanged)
|
||||
self.ocrQueueModel.started.connect(self.ocrStarted)
|
||||
self.ocrQueueModel.finished.connect(self.ocrFinished)
|
||||
self.ocrQueueProxyModel = OcrQueueTableProxyModel(self)
|
||||
self.ocrQueueProxyModel.setSourceModel(self.ocrQueueModel)
|
||||
|
||||
self.tableView.setModel(self.ocrQueueProxyModel)
|
||||
self.tableView.setItemDelegateForColumn(1, ImageDelegate(self.tableView))
|
||||
self.tableView.setItemDelegateForColumn(2, TableChartDelegate(self.tableView))
|
||||
self.tableView.setItemDelegateForColumn(3, TableScoreDelegate(self.tableView))
|
||||
|
||||
tableViewPalette = QPalette(self.tableView.palette())
|
||||
highlightColor = QColor(tableViewPalette.color(QPalette.ColorRole.Highlight))
|
||||
highlightColor.setAlpha(25)
|
||||
tableViewPalette.setColor(QPalette.ColorRole.Highlight, highlightColor)
|
||||
self.tableView.setPalette(tableViewPalette)
|
||||
|
||||
@Slot(QModelIndex, QModelIndex, list)
|
||||
def resizeViewWhenScoreChanged(
|
||||
self, topleft: QModelIndex, bottomRight: QModelIndex, roles: list[int]
|
||||
):
|
||||
if OcrQueueModel.ScoreInsertRole in roles:
|
||||
rows = [*range(topleft.row(), bottomRight.row() + 1)]
|
||||
[self.tableView.resizeRowToContents(row) for row in rows]
|
||||
self.tableView.resizeColumnsToContents()
|
||||
self.ocrQueue.setModel(self.ocrQueueModel)
|
||||
self.ocrQueueProxyModel = self.ocrQueue.tableProxyModel()
|
||||
|
||||
@Slot()
|
||||
def on_openWizardButton_clicked(self):
|
||||
wizard = Wizard(self)
|
||||
wizard.open()
|
||||
# wizard = Wizard(self)
|
||||
# wizard.open()
|
||||
pass
|
||||
|
||||
def deviceFileSelected(self):
|
||||
selectedFiles = self.deviceFileSelector.selectedFiles()
|
||||
@ -73,15 +48,6 @@ class TabOcr(Ui_TabOcr, QWidget):
|
||||
if selectedFiles:
|
||||
pytesseract.pytesseract.tesseract_cmd = selectedFiles[0]
|
||||
|
||||
def setOcrButtonsEnabled(self, __bool: bool):
|
||||
self.ocr_addImageButton.setEnabled(__bool)
|
||||
self.ocr_removeSelectedButton.setEnabled(__bool)
|
||||
self.ocr_removeAllButton.setEnabled(__bool)
|
||||
self.ocr_startButton.setEnabled(__bool)
|
||||
self.ocr_acceptSelectedButton.setEnabled(__bool)
|
||||
self.ocr_acceptAllButton.setEnabled(__bool)
|
||||
self.ocr_ignoreValidateCheckBox.setEnabled(__bool)
|
||||
|
||||
@Slot()
|
||||
def on_ocr_addImageButton_clicked(self):
|
||||
files, _filter = QFileDialog.getOpenFileNames(
|
||||
@ -89,45 +55,24 @@ class TabOcr(Ui_TabOcr, QWidget):
|
||||
)
|
||||
for file in files:
|
||||
self.ocrQueueModel.addItem(file)
|
||||
self.tableView.resizeRowsToContents()
|
||||
self.tableView.resizeColumnsToContents()
|
||||
self.ocrQueue.resizeTableView()
|
||||
|
||||
@Slot()
|
||||
def on_ocr_startButton_clicked(self):
|
||||
self.ocrQueueModel.startQueue(self.deviceComboBox.currentData())
|
||||
|
||||
def ocrStarted(self):
|
||||
self.setOcrButtonsEnabled(False)
|
||||
|
||||
def ocrFinished(self):
|
||||
self.setOcrButtonsEnabled(True)
|
||||
|
||||
@Slot()
|
||||
def on_ocr_removeSelectedButton_clicked(self):
|
||||
rows = [
|
||||
modelIndex.row()
|
||||
for modelIndex in self.tableView.selectionModel().selectedRows(0)
|
||||
]
|
||||
self.ocrQueueModel.removeItems(rows)
|
||||
for row in range(self.ocrQueueModel.rowCount()):
|
||||
index = self.ocrQueueModel.index(row, 0)
|
||||
imagePath = index.data(OcrQueueModel.ImagePathRole)
|
||||
runnable = TabDeviceV2OcrRunnable(
|
||||
imagePath, self.deviceComboBox.currentData(), self.knn, self.siftDb
|
||||
)
|
||||
self.ocrQueueModel.setData(index, runnable, OcrQueueModel.OcrRunnableRole)
|
||||
self.ocrQueueModel.setData(
|
||||
index,
|
||||
ScoreInsertConverter.deviceV2,
|
||||
OcrQueueModel.ProcessOcrResultFuncRole,
|
||||
)
|
||||
self.ocrQueueModel.startQueue()
|
||||
|
||||
@Slot()
|
||||
def on_ocr_removeAllButton_clicked(self):
|
||||
self.ocrQueueModel.clear()
|
||||
|
||||
@Slot()
|
||||
def on_ocr_acceptSelectedButton_clicked(self):
|
||||
ignoreValidate = (
|
||||
self.ocr_ignoreValidateCheckBox.checkState() == Qt.CheckState.Checked
|
||||
)
|
||||
rows = [
|
||||
modelIndex.row()
|
||||
for modelIndex in self.tableView.selectionModel().selectedRows(0)
|
||||
]
|
||||
self.ocrQueueModel.acceptItems(rows, ignoreValidate)
|
||||
|
||||
@Slot()
|
||||
def on_ocr_acceptAllButton_clicked(self):
|
||||
ignoreValidate = (
|
||||
self.ocr_ignoreValidateCheckBox.checkState() == Qt.CheckState.Checked
|
||||
)
|
||||
self.ocrQueueModel.acceptAllItems(ignoreValidate)
|
||||
|
Reference in New Issue
Block a user