diff --git a/camera_thread.py b/camera_thread.py
index f9dcd62..ec96320 100644
--- a/camera_thread.py
+++ b/camera_thread.py
@@ -206,6 +206,13 @@ def __init__(
self.fps_alpha = 0.1 # Smoothing factor
self.updateOnChange = True
self.crop = FrameCropAndRotation()
+ self.speed = 1
+
+ def getSpeed(self):
+ return self.speed
+
+ def setSpeed(self, speed):
+ self.speed = speed
def setUpdateFrameInterval(self, cadence):
self.update_frame_interval = 1000 / cadence
@@ -303,6 +310,13 @@ def run(self):
self.sleep_fps_target()
continue
+ if self.camera_info.type == CameraInfo.CameraType.FILE:
+ if self.speed != 1:
+ self.video_capture.set(
+ cv2.CAP_PROP_POS_FRAMES,
+ self.video_capture.get(cv2.CAP_PROP_POS_FRAMES) + self.speed,
+ )
+
if not ret:
self.retry_count += 1
if self.camera_info.type == CameraInfo.CameraType.FILE:
diff --git a/mainwindow.py b/mainwindow.py
index a4497be..89092bc 100644
--- a/mainwindow.py
+++ b/mainwindow.py
@@ -407,10 +407,34 @@ def __init__(self, translator: QTranslator, parent: QObject):
fetch_data("scoresight.json", "save_ocr_training_data", False)
)
+ self.ui.toolButton_speed.clicked.connect(self.toggleSpeed)
+
self.update_sources.connect(self.updateSources)
self.get_sources.connect(self.getSources)
self.get_sources.emit()
+ def toggleSpeed(self):
+ # check the current speed and toggle it
+ # possible speeds are x2, x4, x8, x16, x32 and back to x1
+ # change the button text to the current speed
+ # change the speed of the image viewer
+ if self.image_viewer:
+ speed = self.image_viewer.timerThread.getSpeed()
+ if speed == 1:
+ speed = 2
+ elif speed == 2:
+ speed = 4
+ elif speed == 4:
+ speed = 8
+ elif speed == 8:
+ speed = 16
+ elif speed == 16:
+ speed = 32
+ else:
+ speed = 1
+ self.image_viewer.timerThread.setSpeed(speed)
+ self.ui.toolButton_speed.setText(f"x{speed}")
+
def saveOCRTrainingData(self):
self.globalSettingsChanged(
"save_ocr_training_data", self.ui.pushButton_saveOCRTrainingData.isChecked()
diff --git a/mainwindow.ui b/mainwindow.ui
index 7668999..647e46c 100644
--- a/mainwindow.ui
+++ b/mainwindow.ui
@@ -2058,6 +2058,20 @@
+ -
+
+
+ Speed
+
+
+
+ -
+
+
+ x1
+
+
+
-
diff --git a/ocr_training_data.py b/ocr_training_data.py
index 40308dd..760748f 100644
--- a/ocr_training_data.py
+++ b/ocr_training_data.py
@@ -135,6 +135,18 @@ def __init__(self):
)
self.ui.pushButton_openFolder.clicked.connect(self.open_folder)
self.ui.pushButton_saveZipFile.clicked.connect(self.save_zip_file)
+ self.ui.pushButton_openTrainingDojo.clicked.connect(self.open_training_dojo)
+
+ def open_training_dojo(self):
+ logger.debug("Opening OCR training dojo")
+ folder = self.ui.lineEdit_saveFolder.text()
+ if folder:
+ from training_dojo import TrainingDojo
+
+ training_dojo = TrainingDojo(folder)
+ training_dojo.exec_()
+ else:
+ logger.error("No OCR training data folder set")
def save_zip_file(self):
logger.debug("Saving OCR training data zip file")
diff --git a/ocr_training_data_dialog.ui b/ocr_training_data_dialog.ui
index 8781bc2..6518c30 100644
--- a/ocr_training_data_dialog.ui
+++ b/ocr_training_data_dialog.ui
@@ -6,8 +6,8 @@
0
0
- 267
- 132
+ 377
+ 164
@@ -19,25 +19,21 @@
650
- 16777215
+ 300
Dialog
-
-
-
-
- Qt::Horizontal
-
-
- QDialogButtonBox::Cancel|QDialogButtonBox::Ok
-
-
-
-
+
+
+ 0
+ 0
+
+
-
@@ -132,9 +128,39 @@
+ -
+
+
+ Open OCR Training Dojo
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
diff --git a/scoresight.spec b/scoresight.spec
index baa404b..c0f709e 100644
--- a/scoresight.spec
+++ b/scoresight.spec
@@ -71,6 +71,7 @@ sources = [
'storage.py',
'tesseract.py',
'text_detection_target.py',
+ 'training_dojo.py',
'video_settings.py',
'ui_about.py',
'ui_connect_obs.py',
@@ -78,6 +79,7 @@ sources = [
'ui_mainwindow.py',
'ui_ocr_training_data_dialog.py',
'ui_screen_capture.py',
+ 'ui_training_dojo.py',
'ui_update_available.py',
'ui_url_source.py',
'ui_video_settings.py',
diff --git a/training_dojo.py b/training_dojo.py
new file mode 100644
index 0000000..5ae2b7f
--- /dev/null
+++ b/training_dojo.py
@@ -0,0 +1,339 @@
+import os
+import json
+from PySide6.QtWidgets import QDialog, QListWidgetItem, QLabel, QVBoxLayout, QLineEdit
+from PySide6.QtGui import QPixmap, QKeyEvent, QColor, QBrush, QPainter
+from PySide6.QtCore import Qt, QSize
+from ui_training_dojo import Ui_TrainingDojo
+
+
+class AspectRatioPixmapLabel(QLabel):
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self.setScaledContents(False)
+
+ def setPixmap(self, pixmap):
+ self.pix = pixmap
+ self.update()
+
+ def paintEvent(self, event):
+ if hasattr(self, "pix"):
+ painter = QPainter(self)
+ painter.setRenderHint(QPainter.SmoothPixmapTransform)
+ scaled_pixmap = self.pix.scaled(
+ self.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation
+ )
+ x = (self.width() - scaled_pixmap.width()) // 2
+ y = (self.height() - scaled_pixmap.height()) // 2
+ painter.drawPixmap(x, y, scaled_pixmap)
+
+
+class CustomLineEdit(QLineEdit):
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self.parent_dialog = parent
+
+ def keyPressEvent(self, event: QKeyEvent):
+ if self.parent_dialog.handle_key_event(event):
+ return
+ super().keyPressEvent(event)
+
+
+class TrainingDojo(QDialog):
+ def __init__(self, folder_path):
+ super().__init__()
+ self.ui = Ui_TrainingDojo()
+ self.ui.setupUi(self)
+
+ self.folder_path = folder_path
+ self.image_files = []
+ self.current_index = -1
+ self.approved_annotations = self.load_approved_annotations()
+ self.show_only_undone = False
+
+ # Replace the default QLineEdit with our CustomLineEdit
+ self.custom_line_edit = CustomLineEdit(self)
+ self.custom_line_edit.setObjectName("lineEdit_text")
+ self.ui.lineEdit_text.setParent(None)
+ self.ui.lineEdit_text = self.custom_line_edit
+ self.ui.verticalLayout.insertWidget(1, self.custom_line_edit)
+
+ # Create an AspectRatioPixmapLabel for displaying the image
+ self.image_label = AspectRatioPixmapLabel()
+ self.image_label.setAlignment(Qt.AlignCenter)
+
+ # Set up layout for the image widget
+ layout = QVBoxLayout(self.ui.widget_image)
+ layout.addWidget(self.image_label)
+ self.ui.widget_image.setLayout(layout)
+
+ self.load_files()
+ self.setup_connections()
+
+ def load_approved_annotations(self):
+ tracking_file = os.path.join(self.folder_path, "approved_annotations.json")
+ if os.path.exists(tracking_file):
+ with open(tracking_file, "r") as f:
+ return json.load(f)
+ return {}
+
+ def save_approved_annotations(self):
+ tracking_file = os.path.join(self.folder_path, "approved_annotations.json")
+ with open(tracking_file, "w") as f:
+ json.dump(self.approved_annotations, f)
+
+ def load_files(self):
+ self.ui.listWidget_files.clear() # Clear existing items
+ approved_items = []
+ unapproved_items = []
+ for file in os.listdir(self.folder_path):
+ if file.endswith(".png"):
+ base_name = os.path.splitext(file)[0]
+ if os.path.exists(os.path.join(self.folder_path, f"{base_name}.txt")):
+ self.image_files.append(base_name)
+ item = QListWidgetItem(base_name)
+ if base_name in self.approved_annotations:
+ item.setForeground(QBrush(QColor("green")))
+ item.setText(f"✓ {base_name}")
+ approved_items.append(item)
+ else:
+ unapproved_items.append(item)
+
+ # Add approved items first, then unapproved items
+ for item in approved_items:
+ self.ui.listWidget_files.addItem(item)
+ for item in unapproved_items:
+ self.ui.listWidget_files.addItem(item)
+
+ if self.image_files:
+ self.current_index = 0
+ self.load_current_item()
+
+ def setup_connections(self):
+ self.ui.listWidget_files.itemClicked.connect(self.load_selected_item)
+ self.ui.pushButton_next.clicked.connect(self.next_image)
+ self.ui.pushButton_prev.clicked.connect(self.prev_image)
+ self.ui.lineEdit_text.editingFinished.connect(self.save_text)
+ self.ui.pushButton_onlyUndone.clicked.connect(self.toggle_undone_filter)
+
+ def load_current_item(self):
+ if 0 <= self.current_index < len(self.image_files):
+ base_name = self.image_files[self.current_index]
+
+ # Load image
+ image_path = os.path.join(self.folder_path, f"{base_name}.png")
+ pixmap = QPixmap(image_path)
+ self.image_label.setPixmap(pixmap)
+
+ # Load text
+ text_path = os.path.join(self.folder_path, f"{base_name}.txt")
+ with open(text_path, "r") as f:
+ text = f.read().strip()
+ self.ui.lineEdit_text.setText(text)
+
+ # Highlight if approved
+ self.highlight_approved()
+
+ # Highlight and scroll to current item in list
+ list_index = self.ui.listWidget_files.findItems(
+ base_name, Qt.MatchContains
+ )[0]
+ self.ui.listWidget_files.setCurrentItem(list_index)
+ self.ui.listWidget_files.scrollToItem(list_index)
+
+ # Focus on lineEdit_text
+ self.ui.lineEdit_text.setFocus()
+ else:
+ # Clear the UI if no valid item is selected
+ self.image_label.clear()
+ self.ui.lineEdit_text.clear()
+
+ def load_selected_item(self, item):
+ self.current_index = self.ui.listWidget_files.row(item)
+ self.load_current_item()
+
+ def load_files(self):
+ self.ui.listWidget_files.clear() # Clear existing items
+ self.image_files = []
+
+ for file in os.listdir(self.folder_path):
+ if file.endswith(".png"):
+ base_name = os.path.splitext(file)[0]
+ if os.path.exists(os.path.join(self.folder_path, f"{base_name}.txt")):
+ self.image_files.append(base_name)
+
+ # Sort files by filename
+ self.image_files.sort()
+
+ self.update_list_widget()
+
+ if self.image_files:
+ self.current_index = 0
+ self.load_current_item()
+
+ def update_list_widget(self):
+ self.ui.listWidget_files.clear()
+ for base_name in self.image_files:
+ if not self.show_only_undone or base_name not in self.approved_annotations:
+ item = QListWidgetItem(base_name)
+ if base_name in self.approved_annotations:
+ item.setForeground(QBrush(QColor("green")))
+ item.setText(f"✓ {base_name}")
+ self.ui.listWidget_files.addItem(item)
+
+ def toggle_undone_filter(self):
+ self.show_only_undone = self.ui.pushButton_onlyUndone.isChecked()
+ self.update_list_widget()
+ self.update_current_index()
+ self.load_current_item()
+
+ def highlight_approved(self):
+ base_name = self.image_files[self.current_index]
+ if base_name in self.approved_annotations:
+ self.ui.lineEdit_text.setStyleSheet("border: 2px solid green;")
+ self.image_label.setStyleSheet("border: 3px solid green;")
+ else:
+ self.ui.lineEdit_text.setStyleSheet("")
+ self.image_label.setStyleSheet("")
+
+ def update_current_index(self):
+ if self.show_only_undone:
+ # Find the first undone item
+ for i, base_name in enumerate(self.image_files):
+ if base_name not in self.approved_annotations:
+ self.current_index = i
+ return
+ # If all items are done, set current_index to -1
+ self.current_index = -1
+ else:
+ # If there are items, set current_index to 0, otherwise -1
+ self.current_index = 0 if self.image_files else -1
+
+ def next_image(self):
+ self.save_text()
+ start_index = self.current_index
+ while True:
+ self.current_index = (self.current_index + 1) % len(self.image_files)
+ if (
+ not self.show_only_undone
+ or self.image_files[self.current_index] not in self.approved_annotations
+ ):
+ break
+ if self.current_index == start_index:
+ # We've gone through all images and found nothing
+ return
+ self.load_current_item()
+
+ def prev_image(self):
+ self.save_text()
+ start_index = self.current_index
+ while True:
+ self.current_index = (self.current_index - 1) % len(self.image_files)
+ if (
+ not self.show_only_undone
+ or self.image_files[self.current_index] not in self.approved_annotations
+ ):
+ break
+ if self.current_index == start_index:
+ # We've gone through all images and found nothing
+ return
+ self.load_current_item()
+
+ def save_text(self):
+ if 0 <= self.current_index < len(self.image_files):
+ base_name = self.image_files[self.current_index]
+ text_path = os.path.join(self.folder_path, f"{base_name}.txt")
+ with open(text_path, "w") as f:
+ f.write(self.ui.lineEdit_text.text())
+
+ def approve_annotation(self):
+ if 0 <= self.current_index < len(self.image_files):
+ base_name = self.image_files[self.current_index]
+ self.approved_annotations[base_name] = self.ui.lineEdit_text.text()
+ self.save_approved_annotations()
+ self.highlight_approved()
+
+ if self.show_only_undone:
+ self.update_list_widget()
+ self.next_image()
+ else:
+ # Update list widget item
+ items = self.ui.listWidget_files.findItems(base_name, Qt.MatchContains)
+ if items:
+ item = items[0]
+ item.setForeground(QBrush(QColor("green")))
+ item.setText(f"✓ {base_name}")
+
+ def unapprove_annotation(self):
+ if 0 <= self.current_index < len(self.image_files):
+ base_name = self.image_files[self.current_index]
+ if base_name in self.approved_annotations:
+ del self.approved_annotations[base_name]
+ self.save_approved_annotations()
+ self.highlight_approved()
+
+ # Update list widget item
+ item = self.ui.listWidget_files.item(self.current_index)
+ item.setForeground(QBrush(QColor("black")))
+ item.setText(base_name)
+
+ def handle_key_event(self, event: QKeyEvent):
+ if event.key() == Qt.Key_N:
+ self.next_image()
+ return True
+ elif event.key() == Qt.Key_B:
+ self.prev_image()
+ return True
+ elif event.key() == Qt.Key_Return or event.key() == Qt.Key_Enter:
+ if event.modifiers() & Qt.ShiftModifier:
+ self.prev_image()
+ else:
+ self.approve_annotation()
+ self.next_image()
+ return True
+ elif event.key() == Qt.Key_Up:
+ if event.modifiers() & Qt.ControlModifier:
+ self.jump_to_prev_unapproved()
+ else:
+ self.prev_image()
+ return True
+ elif event.key() == Qt.Key_Down:
+ if event.modifiers() & Qt.ControlModifier:
+ self.jump_to_next_unapproved()
+ else:
+ self.next_image()
+ return True
+ elif event.key() == Qt.Key_U:
+ self.unapprove_annotation()
+ return True
+ return False
+
+ def jump_to_next_unapproved(self):
+ start_index = (self.current_index + 1) % len(self.image_files)
+ for i in range(len(self.image_files)):
+ index = (start_index + i) % len(self.image_files)
+ if self.image_files[index] not in self.approved_annotations:
+ self.current_index = index
+ self.load_current_item()
+ return
+ # If we get here, all items are approved
+ print("All items are approved.")
+
+ def jump_to_prev_unapproved(self):
+ start_index = (self.current_index - 1) % len(self.image_files)
+ for i in range(len(self.image_files)):
+ index = (start_index - i) % len(self.image_files)
+ if self.image_files[index] not in self.approved_annotations:
+ self.current_index = index
+ self.load_current_item()
+ return
+ # If we get here, all items are approved
+ print("All items are approved.")
+
+ def keyPressEvent(self, event: QKeyEvent):
+ if not self.handle_key_event(event):
+ super().keyPressEvent(event)
+
+ def resizeEvent(self, event):
+ super().resizeEvent(event)
+ if hasattr(self, "image_label"):
+ self.image_label.setFixedSize(self.ui.widget_image.size())
diff --git a/training_dojo.ui b/training_dojo.ui
new file mode 100644
index 0000000..7ac5f0b
--- /dev/null
+++ b/training_dojo.ui
@@ -0,0 +1,94 @@
+
+
+ TrainingDojo
+
+
+
+ 0
+ 0
+ 652
+ 410
+
+
+
+ Dialog
+
+
+ -
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+
+ -
+
+
+
+ 29
+
+
+
+ Text...
+
+
+
+ -
+
+
+
-
+
+
+ <
+
+
+
+ -
+
+
+ >
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Only Undone
+
+
+ true
+
+
+
+
+
+
+
+
diff --git a/ui_mainwindow.py b/ui_mainwindow.py
index 20c61ac..ae9f8c8 100644
--- a/ui_mainwindow.py
+++ b/ui_mainwindow.py
@@ -1058,6 +1058,16 @@ def setupUi(self, MainWindow):
self.horizontalLayout_26.addWidget(self.spinBox_bottomCrop)
+ self.label_22 = QLabel(self.widget_cropPanel)
+ self.label_22.setObjectName(u"label_22")
+
+ self.horizontalLayout_26.addWidget(self.label_22)
+
+ self.toolButton_speed = QToolButton(self.widget_cropPanel)
+ self.toolButton_speed.setObjectName(u"toolButton_speed")
+
+ self.horizontalLayout_26.addWidget(self.toolButton_speed)
+
self.horizontalSpacer_4 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
self.horizontalLayout_26.addItem(self.horizontalSpacer_4)
@@ -1271,6 +1281,8 @@ def retranslateUi(self, MainWindow):
self.spinBox_rightCrop.setSuffix(QCoreApplication.translate("MainWindow", u"px", None))
self.label_19.setText(QCoreApplication.translate("MainWindow", u"Bottom", None))
self.spinBox_bottomCrop.setSuffix(QCoreApplication.translate("MainWindow", u"px", None))
+ self.label_22.setText(QCoreApplication.translate("MainWindow", u"Speed", None))
+ self.toolButton_speed.setText(QCoreApplication.translate("MainWindow", u"x1", None))
self.label_12.setText(QCoreApplication.translate("MainWindow", u"### Open a Camera or Load a File", None))
# retranslateUi
diff --git a/ui_ocr_training_data_dialog.py b/ui_ocr_training_data_dialog.py
index 5f51d1a..2fb6dc9 100644
--- a/ui_ocr_training_data_dialog.py
+++ b/ui_ocr_training_data_dialog.py
@@ -17,27 +17,25 @@
QPalette, QPixmap, QRadialGradient, QTransform)
from PySide6.QtWidgets import (QAbstractButton, QApplication, QDialog, QDialogButtonBox,
QFormLayout, QGridLayout, QHBoxLayout, QLabel,
- QLineEdit, QPushButton, QSizePolicy, QSpinBox,
- QToolButton, QWidget)
+ QLineEdit, QPushButton, QSizePolicy, QSpacerItem,
+ QSpinBox, QToolButton, QWidget)
class Ui_OCRTrainingDataDialog(object):
def setupUi(self, OCRTrainingDataDialog):
if not OCRTrainingDataDialog.objectName():
OCRTrainingDataDialog.setObjectName(u"OCRTrainingDataDialog")
- OCRTrainingDataDialog.resize(267, 132)
+ OCRTrainingDataDialog.resize(377, 164)
OCRTrainingDataDialog.setMinimumSize(QSize(267, 0))
- OCRTrainingDataDialog.setMaximumSize(QSize(650, 16777215))
+ OCRTrainingDataDialog.setMaximumSize(QSize(650, 300))
self.gridLayout = QGridLayout(OCRTrainingDataDialog)
self.gridLayout.setObjectName(u"gridLayout")
- self.buttonBox = QDialogButtonBox(OCRTrainingDataDialog)
- self.buttonBox.setObjectName(u"buttonBox")
- self.buttonBox.setOrientation(Qt.Horizontal)
- self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel|QDialogButtonBox.Ok)
-
- self.gridLayout.addWidget(self.buttonBox, 2, 0, 1, 1)
-
self.widget = QWidget(OCRTrainingDataDialog)
self.widget.setObjectName(u"widget")
+ sizePolicy = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.widget.sizePolicy().hasHeightForWidth())
+ self.widget.setSizePolicy(sizePolicy)
self.formLayout = QFormLayout(self.widget)
self.formLayout.setObjectName(u"formLayout")
self.label = QLabel(self.widget)
@@ -47,9 +45,6 @@ def setupUi(self, OCRTrainingDataDialog):
self.widget_2 = QWidget(self.widget)
self.widget_2.setObjectName(u"widget_2")
- sizePolicy = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed)
- sizePolicy.setHorizontalStretch(0)
- sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.widget_2.sizePolicy().hasHeightForWidth())
self.widget_2.setSizePolicy(sizePolicy)
self.horizontalLayout = QHBoxLayout(self.widget_2)
@@ -98,9 +93,25 @@ def setupUi(self, OCRTrainingDataDialog):
self.formLayout.setWidget(2, QFormLayout.FieldRole, self.spinBox_maxSize)
+ self.pushButton_openTrainingDojo = QPushButton(self.widget)
+ self.pushButton_openTrainingDojo.setObjectName(u"pushButton_openTrainingDojo")
+
+ self.formLayout.setWidget(3, QFormLayout.FieldRole, self.pushButton_openTrainingDojo)
+
self.gridLayout.addWidget(self.widget, 1, 0, 1, 1)
+ self.buttonBox = QDialogButtonBox(OCRTrainingDataDialog)
+ self.buttonBox.setObjectName(u"buttonBox")
+ self.buttonBox.setOrientation(Qt.Horizontal)
+ self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel|QDialogButtonBox.Ok)
+
+ self.gridLayout.addWidget(self.buttonBox, 3, 0, 1, 1)
+
+ self.verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding)
+
+ self.gridLayout.addItem(self.verticalSpacer, 2, 0, 1, 1)
+
self.retranslateUi(OCRTrainingDataDialog)
self.buttonBox.accepted.connect(OCRTrainingDataDialog.accept)
@@ -117,5 +128,6 @@ def retranslateUi(self, OCRTrainingDataDialog):
self.pushButton_saveZipFile.setText(QCoreApplication.translate("OCRTrainingDataDialog", u"Save Zip File", None))
self.label_2.setText(QCoreApplication.translate("OCRTrainingDataDialog", u"Max Size", None))
self.spinBox_maxSize.setSuffix(QCoreApplication.translate("OCRTrainingDataDialog", u"Mb", None))
+ self.pushButton_openTrainingDojo.setText(QCoreApplication.translate("OCRTrainingDataDialog", u"Open OCR Training Dojo", None))
# retranslateUi
diff --git a/ui_training_dojo.py b/ui_training_dojo.py
new file mode 100644
index 0000000..4d614d1
--- /dev/null
+++ b/ui_training_dojo.py
@@ -0,0 +1,105 @@
+# -*- coding: utf-8 -*-
+
+################################################################################
+## Form generated from reading UI file 'training_dojo.ui'
+##
+## Created by: Qt User Interface Compiler version 6.7.2
+##
+## WARNING! All changes made in this file will be lost when recompiling UI file!
+################################################################################
+
+from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
+ QMetaObject, QObject, QPoint, QRect,
+ QSize, QTime, QUrl, Qt)
+from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
+ QFont, QFontDatabase, QGradient, QIcon,
+ QImage, QKeySequence, QLinearGradient, QPainter,
+ QPalette, QPixmap, QRadialGradient, QTransform)
+from PySide6.QtWidgets import (QApplication, QDialog, QGridLayout, QHBoxLayout,
+ QLineEdit, QListWidget, QListWidgetItem, QPushButton,
+ QSizePolicy, QVBoxLayout, QWidget)
+
+class Ui_TrainingDojo(object):
+ def setupUi(self, TrainingDojo):
+ if not TrainingDojo.objectName():
+ TrainingDojo.setObjectName(u"TrainingDojo")
+ TrainingDojo.resize(652, 410)
+ self.gridLayout = QGridLayout(TrainingDojo)
+ self.gridLayout.setObjectName(u"gridLayout")
+ self.listWidget_files = QListWidget(TrainingDojo)
+ self.listWidget_files.setObjectName(u"listWidget_files")
+
+ self.gridLayout.addWidget(self.listWidget_files, 1, 0, 1, 1)
+
+ self.widget = QWidget(TrainingDojo)
+ self.widget.setObjectName(u"widget")
+ sizePolicy = QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Preferred)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.widget.sizePolicy().hasHeightForWidth())
+ self.widget.setSizePolicy(sizePolicy)
+ self.verticalLayout = QVBoxLayout(self.widget)
+ self.verticalLayout.setObjectName(u"verticalLayout")
+ self.widget_image = QWidget(self.widget)
+ self.widget_image.setObjectName(u"widget_image")
+ sizePolicy1 = QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Expanding)
+ sizePolicy1.setHorizontalStretch(0)
+ sizePolicy1.setVerticalStretch(0)
+ sizePolicy1.setHeightForWidth(self.widget_image.sizePolicy().hasHeightForWidth())
+ self.widget_image.setSizePolicy(sizePolicy1)
+
+ self.verticalLayout.addWidget(self.widget_image)
+
+ self.lineEdit_text = QLineEdit(self.widget)
+ self.lineEdit_text.setObjectName(u"lineEdit_text")
+ font = QFont()
+ font.setPointSize(29)
+ self.lineEdit_text.setFont(font)
+
+ self.verticalLayout.addWidget(self.lineEdit_text)
+
+ self.widget_3 = QWidget(self.widget)
+ self.widget_3.setObjectName(u"widget_3")
+ self.horizontalLayout = QHBoxLayout(self.widget_3)
+ self.horizontalLayout.setObjectName(u"horizontalLayout")
+ self.pushButton_prev = QPushButton(self.widget_3)
+ self.pushButton_prev.setObjectName(u"pushButton_prev")
+
+ self.horizontalLayout.addWidget(self.pushButton_prev)
+
+ self.pushButton_next = QPushButton(self.widget_3)
+ self.pushButton_next.setObjectName(u"pushButton_next")
+
+ self.horizontalLayout.addWidget(self.pushButton_next)
+
+
+ self.verticalLayout.addWidget(self.widget_3)
+
+
+ self.gridLayout.addWidget(self.widget, 1, 1, 1, 1)
+
+ self.pushButton_onlyUndone = QPushButton(TrainingDojo)
+ self.pushButton_onlyUndone.setObjectName(u"pushButton_onlyUndone")
+ sizePolicy2 = QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
+ sizePolicy2.setHorizontalStretch(0)
+ sizePolicy2.setVerticalStretch(0)
+ sizePolicy2.setHeightForWidth(self.pushButton_onlyUndone.sizePolicy().hasHeightForWidth())
+ self.pushButton_onlyUndone.setSizePolicy(sizePolicy2)
+ self.pushButton_onlyUndone.setCheckable(True)
+
+ self.gridLayout.addWidget(self.pushButton_onlyUndone, 2, 0, 1, 1)
+
+
+ self.retranslateUi(TrainingDojo)
+
+ QMetaObject.connectSlotsByName(TrainingDojo)
+ # setupUi
+
+ def retranslateUi(self, TrainingDojo):
+ TrainingDojo.setWindowTitle(QCoreApplication.translate("TrainingDojo", u"Dialog", None))
+ self.lineEdit_text.setPlaceholderText(QCoreApplication.translate("TrainingDojo", u"Text...", None))
+ self.pushButton_prev.setText(QCoreApplication.translate("TrainingDojo", u"<", None))
+ self.pushButton_next.setText(QCoreApplication.translate("TrainingDojo", u">", None))
+ self.pushButton_onlyUndone.setText(QCoreApplication.translate("TrainingDojo", u"Only Undone", None))
+ # retranslateUi
+