5 Commits

Author SHA1 Message Date
0c6f4f4961 chore: v0.3.9 2024-06-20 00:04:46 +08:00
10fb98d530 chore: upgrade dependencies 2024-06-20 00:03:19 +08:00
bf034d1397 ci: update build actions
* Switch to official Nuitka GitHub Actions

* Adding Linux build support

* Upgrade deprecated actions
2024-06-20 00:00:28 +08:00
4f864611ee fix: B30 table order (#11) 2024-06-19 22:20:31 +08:00
d9c163431c feat: OCR score date source (#9)
* New settings entries

* Choose `birthTime`/`lastModified` for OCR score date source if the image EXIF fails
2024-06-19 22:18:25 +08:00
16 changed files with 565 additions and 173 deletions

View File

@ -1,40 +1,65 @@
name: Build UI from latest `arcaea-offline-*` dependencies name: Build Executable from latest `arcaea-offline-*` dependencies
run-name: ${{ github.actor }} started a build request. run-name: ${{ github.actor }} started a build request.
on: on:
workflow_dispatch: workflow_dispatch:
permissions:
contents: write
discussions: write
jobs: jobs:
build-windows: build:
runs-on: windows-2022 strategy:
matrix:
os: [ubuntu-latest, windows-latest]
fail-fast: false
runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
- uses: actions/setup-python@v5 - uses: actions/setup-python@v5
with: with:
python-version: "3.11" python-version: "3.11"
cache: "pip"
# install dependencies # install dependencies
- run: "pip install -r requirements.txt" - run: "pip install -r requirements.txt"
- run: "pip uninstall arcaea-offline arcaea-offline-ocr -y" - run: "pip uninstall arcaea-offline arcaea-offline-ocr -y"
- run: "pip install git+https://github.com/283375/arcaea-offline" - run: "pip install git+https://github.com/283375/arcaea-offline"
- run: "pip install git+https://github.com/283375/arcaea-offline-ocr" - run: "pip install git+https://github.com/283375/arcaea-offline-ocr"
- run: "pip install nuitka imageio" - run: "pip install imageio"
- name: Install UPX - name: Install UPX
uses: crazy-max/ghaction-upx@v3 uses: crazy-max/ghaction-upx@v3
with: with:
install-only: true install-only: true
# release builtin files - name: Release builtin files
- run: 'pyside6-lrelease.exe .\ui\resources\lang\en_US.ts .\ui\resources\lang\zh_CN.ts' run: |
- run: "python prebuild.py" pyside6-lrelease ui/resources/lang/en_US.ts ui/resources/lang/zh_CN.ts
- run: 'pyside6-rcc.exe .\ui\resources\resources.qrc -o .\ui\resources\resources_rc.py' python prebuild.py
pyside6-rcc ui/resources/resources.qrc -o ui/resources/resources_rc.py
# build - name: Build Executable
- run: "python -m nuitka --plugin-enable=upx --enable-plugin=pyside6 --assume-yes-for-downloads --windows-icon-from-ico=./ui/resources/images/icon.png --standalone --onefile index.py" uses: Nuitka/Nuitka-Action@main
- uses: actions/upload-artifact@v3
with: with:
name: build-windows nuitka-version: main
path: index.exe script-name: index.py
standalone: true
onefile: true
enable-plugins: pyside6,upx
windows-icon-from-ico: ui/resources/images/icon.png
linux-icon: ui/resources/images/icon.png
- name: Upload Artifacts
uses: actions/upload-artifact@v4
with:
name: ${{ runner.os }} Build
path: |
build/*.exe
build/*.bin
build/*.app/**/*

View File

@ -1,4 +1,4 @@
name: Build UI name: Build Executable
run-name: ${{ github.actor }} started a build request. run-name: ${{ github.actor }} started a build request.
on: on:
@ -12,32 +12,59 @@ permissions:
discussions: write discussions: write
jobs: jobs:
build-windows: build:
runs-on: windows-2022 strategy:
matrix:
os: [ubuntu-latest, windows-latest]
fail-fast: false
runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
- uses: actions/setup-python@v5 - uses: actions/setup-python@v5
with: with:
python-version: "3.11" python-version: "3.11"
cache: "pip" cache: "pip"
# install dependencies - name: Install dependencies
- run: "pip install -r requirements.txt" run: |
- run: "pip install nuitka imageio" pip install -r requirements.txt
pip install imageio
- name: Install UPX - name: Install UPX
uses: crazy-max/ghaction-upx@v3 uses: crazy-max/ghaction-upx@v3
with: with:
install-only: true install-only: true
# release builtin files - name: Release builtin files
- run: 'pyside6-lrelease.exe .\ui\resources\lang\en_US.ts .\ui\resources\lang\zh_CN.ts' run: |
- run: "python prebuild.py" pyside6-lrelease ui/resources/lang/en_US.ts ui/resources/lang/zh_CN.ts
- run: 'pyside6-rcc.exe .\ui\resources\resources.qrc -o .\ui\resources\resources_rc.py' python prebuild.py
pyside6-rcc ui/resources/resources.qrc -o ui/resources/resources_rc.py
# build - name: Build Executable
- run: "python -m nuitka --plugin-enable=upx --enable-plugin=pyside6 --assume-yes-for-downloads --windows-icon-from-ico=./ui/resources/images/icon.png --standalone --onefile index.py" uses: Nuitka/Nuitka-Action@main
with:
nuitka-version: main
script-name: index.py
standalone: true
onefile: true
enable-plugins: pyside6,upx
windows-icon-from-ico: ui/resources/images/icon.png
linux-icon: ui/resources/images/icon.png
- name: Upload Artifacts
uses: actions/upload-artifact@v4
with:
name: ${{ runner.os }} Build
path: |
build/*.exe
build/*.bin
build/*.app/**/*
- name: Draft a release - name: Draft a release
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v2
@ -46,4 +73,6 @@ jobs:
draft: true draft: true
generate_release_notes: true generate_release_notes: true
files: | files: |
index.exe build/*.exe
build/*.bin
build/*.app/**/*

View File

@ -4,14 +4,14 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "arcaea-offline-pyside-ui" name = "arcaea-offline-pyside-ui"
version = "0.3.8" version = "0.3.9"
authors = [{ name = "283375", email = "log_283375@163.com" }] authors = [{ name = "283375", email = "log_283375@163.com" }]
description = "No description." description = "No description."
readme = "README.md" readme = "README.md"
requires-python = ">=3.9" requires-python = ">=3.9"
dependencies = [ dependencies = [
"arcaea-offline==0.2.2", "arcaea-offline==0.2.2",
"arcaea-offline-ocr==0.0.98", "arcaea-offline-ocr==0.0.99",
"exif==1.6.0", "exif==1.6.0",
"PySide6==6.5.2", "PySide6==6.5.2",
] ]

View File

@ -1,5 +1,5 @@
arcaea-offline==0.2.2 arcaea-offline==0.2.2
arcaea-offline-ocr==0.0.98 arcaea-offline-ocr==0.0.99
exif==1.6.0 exif==1.6.0
Pillow==10.1.0 Pillow==10.1.0
PySide6==6.5.2 PySide6==6.5.2

View File

@ -22,39 +22,6 @@
<string>queue.title</string> <string>queue.title</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_2"> <layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>iccOptionsGroupBox</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QRadioButton" name="iccUseQtRadioButton">
<property name="text">
<string>icc.useQt</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="iccUsePILRadioButton">
<property name="text">
<string>icc.usePIL</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="iccTryFixRadioButton">
<property name="text">
<string>icc.tryFix</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item> <item>
<widget class="QPushButton" name="ocr_addImageButton"> <widget class="QPushButton" name="ocr_addImageButton">
<property name="text"> <property name="text">
@ -95,6 +62,13 @@
</property> </property>
</spacer> </spacer>
</item> </item>
<item>
<widget class="QPushButton" name="optionsDialogButton">
<property name="text">
<string>queue.optionsButton</string>
</property>
</widget>
</item>
<item> <item>
<widget class="QPushButton" name="ocr_startButton"> <widget class="QPushButton" name="ocr_startButton">
<property name="text"> <property name="text">

View File

@ -0,0 +1,140 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>OcrQueueOptionsDialog</class>
<widget class="QDialog" name="OcrQueueOptionsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>331</width>
<height>157</height>
</rect>
</property>
<property name="windowTitle">
<string>OCR Options</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>iccOptionsGroupBox</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QRadioButton" name="iccUseQtRadioButton">
<property name="text">
<string>icc.useQt</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="iccUsePILRadioButton">
<property name="text">
<string>icc.usePIL</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="iccTryFixRadioButton">
<property name="text">
<string>icc.tryFix</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>dateOptionsGroupBox</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QCheckBox" name="dateReadFromExifCheckbox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>date.readFromExif</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="dateUseCreationDateRadioButton">
<property name="text">
<string>date.useCreationDate</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="dateUseModifyDateRadioButton">
<property name="text">
<string>date.useModifyDate</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>OcrQueueOptionsDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>OcrQueueOptionsDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -0,0 +1,107 @@
# -*- coding: utf-8 -*-
################################################################################
## Form generated from reading UI file 'ocrQueueOptionsDialog.ui'
##
## Created by: Qt User Interface Compiler version 6.5.2
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
QMetaObject, QObject, QPoint, QRect,
QSize, QTime, QUrl, Qt)
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
QFont, QFontDatabase, QGradient, QIcon,
QImage, QKeySequence, QLinearGradient, QPainter,
QPalette, QPixmap, QRadialGradient, QTransform)
from PySide6.QtWidgets import (QAbstractButton, QApplication, QCheckBox, QDialog,
QDialogButtonBox, QGroupBox, QHBoxLayout, QRadioButton,
QSizePolicy, QVBoxLayout, QWidget)
class Ui_OcrQueueOptionsDialog(object):
def setupUi(self, OcrQueueOptionsDialog):
if not OcrQueueOptionsDialog.objectName():
OcrQueueOptionsDialog.setObjectName(u"OcrQueueOptionsDialog")
OcrQueueOptionsDialog.resize(331, 157)
self.verticalLayout = QVBoxLayout(OcrQueueOptionsDialog)
self.verticalLayout.setObjectName(u"verticalLayout")
self.horizontalLayout = QHBoxLayout()
self.horizontalLayout.setObjectName(u"horizontalLayout")
self.groupBox = QGroupBox(OcrQueueOptionsDialog)
self.groupBox.setObjectName(u"groupBox")
self.verticalLayout_2 = QVBoxLayout(self.groupBox)
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
self.iccUseQtRadioButton = QRadioButton(self.groupBox)
self.iccUseQtRadioButton.setObjectName(u"iccUseQtRadioButton")
self.verticalLayout_2.addWidget(self.iccUseQtRadioButton)
self.iccUsePILRadioButton = QRadioButton(self.groupBox)
self.iccUsePILRadioButton.setObjectName(u"iccUsePILRadioButton")
self.iccUsePILRadioButton.setChecked(True)
self.verticalLayout_2.addWidget(self.iccUsePILRadioButton)
self.iccTryFixRadioButton = QRadioButton(self.groupBox)
self.iccTryFixRadioButton.setObjectName(u"iccTryFixRadioButton")
self.verticalLayout_2.addWidget(self.iccTryFixRadioButton)
self.horizontalLayout.addWidget(self.groupBox)
self.groupBox_2 = QGroupBox(OcrQueueOptionsDialog)
self.groupBox_2.setObjectName(u"groupBox_2")
self.verticalLayout_3 = QVBoxLayout(self.groupBox_2)
self.verticalLayout_3.setObjectName(u"verticalLayout_3")
self.dateReadFromExifCheckbox = QCheckBox(self.groupBox_2)
self.dateReadFromExifCheckbox.setObjectName(u"dateReadFromExifCheckbox")
self.dateReadFromExifCheckbox.setEnabled(False)
self.dateReadFromExifCheckbox.setChecked(True)
self.verticalLayout_3.addWidget(self.dateReadFromExifCheckbox)
self.dateUseCreationDateRadioButton = QRadioButton(self.groupBox_2)
self.dateUseCreationDateRadioButton.setObjectName(u"dateUseCreationDateRadioButton")
self.dateUseCreationDateRadioButton.setChecked(True)
self.verticalLayout_3.addWidget(self.dateUseCreationDateRadioButton)
self.dateUseModifyDateRadioButton = QRadioButton(self.groupBox_2)
self.dateUseModifyDateRadioButton.setObjectName(u"dateUseModifyDateRadioButton")
self.verticalLayout_3.addWidget(self.dateUseModifyDateRadioButton)
self.horizontalLayout.addWidget(self.groupBox_2)
self.verticalLayout.addLayout(self.horizontalLayout)
self.buttonBox = QDialogButtonBox(OcrQueueOptionsDialog)
self.buttonBox.setObjectName(u"buttonBox")
self.buttonBox.setOrientation(Qt.Horizontal)
self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel|QDialogButtonBox.Ok)
self.verticalLayout.addWidget(self.buttonBox)
self.retranslateUi(OcrQueueOptionsDialog)
self.buttonBox.accepted.connect(OcrQueueOptionsDialog.accept)
self.buttonBox.rejected.connect(OcrQueueOptionsDialog.reject)
QMetaObject.connectSlotsByName(OcrQueueOptionsDialog)
# setupUi
def retranslateUi(self, OcrQueueOptionsDialog):
OcrQueueOptionsDialog.setWindowTitle(QCoreApplication.translate("OcrQueueOptionsDialog", u"OCR Options", None))
self.groupBox.setTitle(QCoreApplication.translate("OcrQueueOptionsDialog", u"iccOptionsGroupBox", None))
self.iccUseQtRadioButton.setText(QCoreApplication.translate("OcrQueueOptionsDialog", u"icc.useQt", None))
self.iccUsePILRadioButton.setText(QCoreApplication.translate("OcrQueueOptionsDialog", u"icc.usePIL", None))
self.iccTryFixRadioButton.setText(QCoreApplication.translate("OcrQueueOptionsDialog", u"icc.tryFix", None))
self.groupBox_2.setTitle(QCoreApplication.translate("OcrQueueOptionsDialog", u"dateOptionsGroupBox", None))
self.dateReadFromExifCheckbox.setText(QCoreApplication.translate("OcrQueueOptionsDialog", u"date.readFromExif", None))
self.dateUseCreationDateRadioButton.setText(QCoreApplication.translate("OcrQueueOptionsDialog", u"date.useCreationDate", None))
self.dateUseModifyDateRadioButton.setText(QCoreApplication.translate("OcrQueueOptionsDialog", u"date.useModifyDate", None))
# retranslateUi

View File

@ -17,8 +17,8 @@ from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
QPalette, QPixmap, QRadialGradient, QTransform) QPalette, QPixmap, QRadialGradient, QTransform)
from PySide6.QtWidgets import (QAbstractItemView, QApplication, QCheckBox, QGroupBox, from PySide6.QtWidgets import (QAbstractItemView, QApplication, QCheckBox, QGroupBox,
QHBoxLayout, QHeaderView, QLabel, QProgressBar, QHBoxLayout, QHeaderView, QLabel, QProgressBar,
QPushButton, QRadioButton, QSizePolicy, QSpacerItem, QPushButton, QSizePolicy, QSpacerItem, QTableView,
QTableView, QVBoxLayout, QWidget) QVBoxLayout, QWidget)
class Ui_OcrQueue(object): class Ui_OcrQueue(object):
def setupUi(self, OcrQueue): def setupUi(self, OcrQueue):
@ -34,29 +34,6 @@ class Ui_OcrQueue(object):
self.groupBox_3.setObjectName(u"groupBox_3") self.groupBox_3.setObjectName(u"groupBox_3")
self.verticalLayout_2 = QVBoxLayout(self.groupBox_3) self.verticalLayout_2 = QVBoxLayout(self.groupBox_3)
self.verticalLayout_2.setObjectName(u"verticalLayout_2") self.verticalLayout_2.setObjectName(u"verticalLayout_2")
self.groupBox = QGroupBox(self.groupBox_3)
self.groupBox.setObjectName(u"groupBox")
self.verticalLayout = QVBoxLayout(self.groupBox)
self.verticalLayout.setObjectName(u"verticalLayout")
self.iccUseQtRadioButton = QRadioButton(self.groupBox)
self.iccUseQtRadioButton.setObjectName(u"iccUseQtRadioButton")
self.verticalLayout.addWidget(self.iccUseQtRadioButton)
self.iccUsePILRadioButton = QRadioButton(self.groupBox)
self.iccUsePILRadioButton.setObjectName(u"iccUsePILRadioButton")
self.iccUsePILRadioButton.setChecked(True)
self.verticalLayout.addWidget(self.iccUsePILRadioButton)
self.iccTryFixRadioButton = QRadioButton(self.groupBox)
self.iccTryFixRadioButton.setObjectName(u"iccTryFixRadioButton")
self.verticalLayout.addWidget(self.iccTryFixRadioButton)
self.verticalLayout_2.addWidget(self.groupBox)
self.ocr_addImageButton = QPushButton(self.groupBox_3) self.ocr_addImageButton = QPushButton(self.groupBox_3)
self.ocr_addImageButton.setObjectName(u"ocr_addImageButton") self.ocr_addImageButton.setObjectName(u"ocr_addImageButton")
@ -78,6 +55,11 @@ class Ui_OcrQueue(object):
self.verticalLayout_2.addItem(self.verticalSpacer) self.verticalLayout_2.addItem(self.verticalSpacer)
self.optionsDialogButton = QPushButton(self.groupBox_3)
self.optionsDialogButton.setObjectName(u"optionsDialogButton")
self.verticalLayout_2.addWidget(self.optionsDialogButton)
self.ocr_startButton = QPushButton(self.groupBox_3) self.ocr_startButton = QPushButton(self.groupBox_3)
self.ocr_startButton.setObjectName(u"ocr_startButton") self.ocr_startButton.setObjectName(u"ocr_startButton")
@ -154,13 +136,10 @@ class Ui_OcrQueue(object):
def retranslateUi(self, OcrQueue): def retranslateUi(self, OcrQueue):
self.groupBox_3.setTitle(QCoreApplication.translate("OcrQueue", u"queue.title", None)) self.groupBox_3.setTitle(QCoreApplication.translate("OcrQueue", u"queue.title", None))
self.groupBox.setTitle(QCoreApplication.translate("OcrQueue", u"iccOptionsGroupBox", None))
self.iccUseQtRadioButton.setText(QCoreApplication.translate("OcrQueue", u"icc.useQt", None))
self.iccUsePILRadioButton.setText(QCoreApplication.translate("OcrQueue", u"icc.usePIL", None))
self.iccTryFixRadioButton.setText(QCoreApplication.translate("OcrQueue", u"icc.tryFix", None))
self.ocr_addImageButton.setText(QCoreApplication.translate("OcrQueue", u"queue.addImageButton", None)) self.ocr_addImageButton.setText(QCoreApplication.translate("OcrQueue", u"queue.addImageButton", None))
self.ocr_removeSelectedButton.setText(QCoreApplication.translate("OcrQueue", u"queue.removeSelected", None)) self.ocr_removeSelectedButton.setText(QCoreApplication.translate("OcrQueue", u"queue.removeSelected", None))
self.ocr_removeAllButton.setText(QCoreApplication.translate("OcrQueue", u"queue.removeAll", None)) self.ocr_removeAllButton.setText(QCoreApplication.translate("OcrQueue", u"queue.removeAll", None))
self.optionsDialogButton.setText(QCoreApplication.translate("OcrQueue", u"queue.optionsButton", None))
self.ocr_startButton.setText(QCoreApplication.translate("OcrQueue", u"queue.startOcrButton", None)) self.ocr_startButton.setText(QCoreApplication.translate("OcrQueue", u"queue.startOcrButton", None))
self.groupBox_5.setTitle(QCoreApplication.translate("OcrQueue", u"results", None)) self.groupBox_5.setTitle(QCoreApplication.translate("OcrQueue", u"results", None))
self.ocr_acceptSelectedButton.setText(QCoreApplication.translate("OcrQueue", u"results.acceptSelectedButton", None)) self.ocr_acceptSelectedButton.setText(QCoreApplication.translate("OcrQueue", u"results.acceptSelectedButton", None))
@ -169,4 +148,3 @@ class Ui_OcrQueue(object):
self.statusLabel.setText("") self.statusLabel.setText("")
pass pass
# retranslateUi # retranslateUi

View File

@ -150,6 +150,7 @@ class OcrQueueModel(QAbstractListModel):
@iccOption.setter @iccOption.setter
def iccOption(self, opt: IccOption): def iccOption(self, opt: IccOption):
logger.debug(f"ICC option changed to {opt}")
self.__iccOption = opt self.__iccOption = opt
@overload @overload
@ -344,8 +345,12 @@ class OcrQueueTableProxyModel(QAbstractTableModel):
def retranslateHeaders(self): def retranslateHeaders(self):
self.__horizontalHeaders = [ self.__horizontalHeaders = [
# fmt: off # fmt: off
QCoreApplication.translate("OcrTableModel", "horizontalHeader.title.select"), QCoreApplication.translate(
QCoreApplication.translate("OcrTableModel", "horizontalHeader.title.imagePreview"), "OcrTableModel", "horizontalHeader.title.select"
),
QCoreApplication.translate(
"OcrTableModel", "horizontalHeader.title.imagePreview"
),
QCoreApplication.translate("OcrTableModel", "horizontalHeader.title.chart"), QCoreApplication.translate("OcrTableModel", "horizontalHeader.title.chart"),
QCoreApplication.translate("OcrTableModel", "horizontalHeader.title.score"), QCoreApplication.translate("OcrTableModel", "horizontalHeader.title.score"),
# fmt: on # fmt: on

View File

@ -39,7 +39,7 @@ class DbB30TableModel(DbTableModel):
(ScoreBest.song_id == Chart.song_id) (ScoreBest.song_id == Chart.song_id)
& (ScoreBest.rating_class == Chart.rating_class), & (ScoreBest.rating_class == Chart.rating_class),
) )
.order_by(ScoreBest.potential) .order_by(ScoreBest.potential.desc())
.limit(50) .limit(50)
.all() .all()
) )

View File

@ -10,6 +10,7 @@ __all__ = [
"KNN_MODEL_FILE", "KNN_MODEL_FILE",
"B30_KNN_MODEL_FILE", "B30_KNN_MODEL_FILE",
"PHASH_DATABASE_FILE", "PHASH_DATABASE_FILE",
"SCORE_DATE_SOURCE",
"ANDREAL_FOLDER", "ANDREAL_FOLDER",
"ANDREAL_EXECUTABLE", "ANDREAL_EXECUTABLE",
"Settings", "Settings",
@ -23,6 +24,7 @@ DATABASE_URL = "DatabaseUrl"
KNN_MODEL_FILE = "Ocr/KnnModelFile" KNN_MODEL_FILE = "Ocr/KnnModelFile"
B30_KNN_MODEL_FILE = "Ocr/B30KnnModelFile" B30_KNN_MODEL_FILE = "Ocr/B30KnnModelFile"
PHASH_DATABASE_FILE = "Ocr/PHashDatabaseFile" PHASH_DATABASE_FILE = "Ocr/PHashDatabaseFile"
SCORE_DATE_SOURCE = "Ocr/DateSource"
ANDREAL_FOLDER = "Andreal/AndrealFolder" ANDREAL_FOLDER = "Andreal/AndrealFolder"
ANDREAL_EXECUTABLE = "Andreal/AndrealExecutable" ANDREAL_EXECUTABLE = "Andreal/AndrealExecutable"
@ -92,6 +94,15 @@ class Settings(QSettings, metaclass=QObjectSingleton):
def resetPHashDatabaseFile(self): def resetPHashDatabaseFile(self):
self._resetStrItem(PHASH_DATABASE_FILE) self._resetStrItem(PHASH_DATABASE_FILE)
def scoreDateSource(self):
return self._strItem(SCORE_DATE_SOURCE)
def setScoreDateSource(self, value: str):
self._setStrItem(SCORE_DATE_SOURCE, value)
def resetScoreDateSource(self):
self._resetStrItem(SCORE_DATE_SOURCE)
def andrealFolder(self): def andrealFolder(self):
return self._strItem(ANDREAL_FOLDER) return self._strItem(ANDREAL_FOLDER)

View File

@ -21,6 +21,7 @@ from PySide6.QtCore import QDateTime, QFileInfo
from ui.extends.components.ocrQueue import OcrRunnable from ui.extends.components.ocrQueue import OcrRunnable
from ui.extends.shared.data import Data from ui.extends.shared.data import Data
from ui.extends.shared.settings import Settings
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -67,8 +68,14 @@ def getImageDate(imagePath: str) -> QDateTime:
if exifImage.has_exif and exifImage.get("datetime_original"): if exifImage.has_exif and exifImage.get("datetime_original"):
datetimeStr = exifImage.get("datetime_original") datetimeStr = exifImage.get("datetime_original")
datetime = QDateTime.fromString(datetimeStr, "yyyy:MM:dd hh:mm:ss") datetime = QDateTime.fromString(datetimeStr, "yyyy:MM:dd hh:mm:ss")
if not isinstance(datetime, QDateTime): if not isinstance(datetime, QDateTime):
dateSource = Settings().scoreDateSource()
if dateSource == "lastModified":
datetime = QFileInfo(imagePath).lastModified()
else:
datetime = QFileInfo(imagePath).birthTime() datetime = QFileInfo(imagePath).birthTime()
return datetime return datetime

View File

@ -2,7 +2,7 @@ from typing import Optional
from PySide6.QtCore import Qt, QTimer, Slot from PySide6.QtCore import Qt, QTimer, Slot
from PySide6.QtGui import QColor, QPalette from PySide6.QtGui import QColor, QPalette
from PySide6.QtWidgets import QButtonGroup, QWidget from PySide6.QtWidgets import QWidget
from ui.designer.components.ocrQueue_ui import Ui_OcrQueue from ui.designer.components.ocrQueue_ui import Ui_OcrQueue
from ui.extends.components.ocrQueue import ( from ui.extends.components.ocrQueue import (
@ -13,6 +13,7 @@ from ui.extends.components.ocrQueue import (
OcrScoreDelegate, OcrScoreDelegate,
) )
from ui.extends.shared.language import LanguageChangeEventFilter from ui.extends.shared.language import LanguageChangeEventFilter
from ui.implements.components.ocrQueueOptionsDialog import OcrQueueOptionsDialog
class OcrQueue(Ui_OcrQueue, QWidget): class OcrQueue(Ui_OcrQueue, QWidget):
@ -26,6 +27,9 @@ class OcrQueue(Ui_OcrQueue, QWidget):
self.__model: Optional[OcrQueueModel] = None self.__model: Optional[OcrQueueModel] = None
self.__tableProxyModel: Optional[OcrQueueTableProxyModel] = None self.__tableProxyModel: Optional[OcrQueueTableProxyModel] = None
self.optionsDialog = OcrQueueOptionsDialog(self)
self.optionsDialog.iccOptionsChanged.connect(self.setIccOption)
self.__firstResizeDone = False self.__firstResizeDone = False
self.resizeTimer = QTimer(self) self.resizeTimer = QTimer(self)
self.resizeTimer.timeout.connect(self.tableView.resizeRowsToContents) self.resizeTimer.timeout.connect(self.tableView.resizeRowsToContents)
@ -41,13 +45,6 @@ class OcrQueue(Ui_OcrQueue, QWidget):
tableViewPalette.setColor(QPalette.ColorRole.Highlight, highlightColor) tableViewPalette.setColor(QPalette.ColorRole.Highlight, highlightColor)
self.tableView.setPalette(tableViewPalette) self.tableView.setPalette(tableViewPalette)
self.iccOptionButtonGroup = QButtonGroup(self)
self.iccOptionButtonGroup.buttonToggled.connect(self.updateIccOption)
self.iccOptionButtonGroup.addButton(self.iccUseQtRadioButton, 0)
self.iccOptionButtonGroup.addButton(self.iccUsePILRadioButton, 1)
self.iccOptionButtonGroup.addButton(self.iccTryFixRadioButton, 2)
self.updateIccOption()
self.statusLabelClearTimer = QTimer(self) self.statusLabelClearTimer = QTimer(self)
self.statusLabelClearTimer.setSingleShot(True) self.statusLabelClearTimer.setSingleShot(True)
self.statusLabelClearTimer.timeout.connect(self.clearStatusMessage) self.statusLabelClearTimer.timeout.connect(self.clearStatusMessage)
@ -93,9 +90,10 @@ class OcrQueue(Ui_OcrQueue, QWidget):
self.ocr_acceptAllButton.setEnabled(__bool) self.ocr_acceptAllButton.setEnabled(__bool)
self.ocr_ignoreValidateCheckBox.setEnabled(__bool) self.ocr_ignoreValidateCheckBox.setEnabled(__bool)
def updateIccOption(self): @Slot(int)
def setIccOption(self, option):
if self.model(): if self.model():
self.model().iccOption = self.iccOptionButtonGroup.checkedId() self.model().iccOption = option
def showStatusMessage(self, message: str): def showStatusMessage(self, message: str):
self.statusLabel.setText(message) self.statusLabel.setText(message)
@ -131,6 +129,10 @@ class OcrQueue(Ui_OcrQueue, QWidget):
def modelReseted(self): def modelReseted(self):
self.progressBar.setMaximum(0) self.progressBar.setMaximum(0)
@Slot()
def on_optionsDialogButton_clicked(self):
self.optionsDialog.exec()
@Slot() @Slot()
def on_ocr_removeSelectedButton_clicked(self): def on_ocr_removeSelectedButton_clicked(self):
if self.model(): if self.model():

View File

@ -0,0 +1,48 @@
from PySide6.QtCore import Signal
from PySide6.QtWidgets import QButtonGroup, QDialog
from ui.designer.components.ocrQueueOptionsDialog_ui import Ui_OcrQueueOptionsDialog
from ui.extends.shared.settings import Settings
class OcrQueueOptionsDialog(QDialog, Ui_OcrQueueOptionsDialog):
iccOptionsChanged = Signal(int)
def __init__(self, parent=None):
super(OcrQueueOptionsDialog, self).__init__(parent)
self.setupUi(self)
self.iccOptionButtonGroup = QButtonGroup(self)
self.iccOptionButtonGroup.buttonToggled.connect(
lambda: self.iccOptionsChanged.emit(self.iccOptionButtonGroup.checkedId())
)
self.iccOptionButtonGroup.addButton(self.iccUseQtRadioButton, 0)
self.iccOptionButtonGroup.addButton(self.iccUsePILRadioButton, 1)
self.iccOptionButtonGroup.addButton(self.iccTryFixRadioButton, 2)
self.scoreDateSourceButtonGroup = QButtonGroup(self)
self.scoreDateSourceButtonGroup.addButton(
self.dateUseCreationDateRadioButton, 0
)
self.scoreDateSourceButtonGroup.addButton(self.dateUseModifyDateRadioButton, 1)
self.scoreDateSourceButtonGroup.buttonClicked.connect(
self.on_scoreDateSourceButtonGroup_buttonClicked
)
self.settings = Settings()
self.settings.updated.connect(self.syncCheckboxesFromSettings)
self.syncCheckboxesFromSettings()
def syncCheckboxesFromSettings(self):
scoreDateSource = self.settings.scoreDateSource()
if scoreDateSource == "lastModified":
self.dateUseModifyDateRadioButton.setChecked(True)
else:
self.dateUseCreationDateRadioButton.setChecked(True)
def on_scoreDateSourceButtonGroup_buttonClicked(self, button):
buttonId = self.scoreDateSourceButtonGroup.id(button)
if buttonId == 1:
self.settings.setScoreDateSource("lastModified")
else:
self.settings.setScoreDateSource("birthTime")

View File

@ -70,12 +70,12 @@
<translation>Continue</translation> <translation>Continue</translation>
</message> </message>
<message> <message>
<location filename="../../startup/databaseChecker.py" line="117"/> <location filename="../../startup/databaseChecker.py" line="122"/>
<source>dialog.tryInitExistingDatabase</source> <source>dialog.tryInitExistingDatabase</source>
<translation>The existing database doesn&apos;t seem to be initialized properly, try initialize again?</translation> <translation>The existing database doesn&apos;t seem to be initialized properly, try initialize again?</translation>
</message> </message>
<message> <message>
<location filename="../../startup/databaseChecker.py" line="133"/> <location filename="../../startup/databaseChecker.py" line="138"/>
<source>dialog.confirmNewDatabase</source> <source>dialog.confirmNewDatabase</source>
<translation>Database file does not exist. Create now?</translation> <translation>Database file does not exist. Create now?</translation>
</message> </message>
@ -224,85 +224,118 @@
</message> </message>
<message> <message>
<location filename="../../designer/components/ocrQueue.ui" line="28"/> <location filename="../../designer/components/ocrQueue.ui" line="28"/>
<source>iccOptionsGroupBox</source>
<translation>ICC Profile Options</translation>
</message>
<message>
<location filename="../../designer/components/ocrQueue.ui" line="34"/>
<source>icc.useQt</source>
<translation>Use Qt</translation>
</message>
<message>
<location filename="../../designer/components/ocrQueue.ui" line="41"/>
<source>icc.usePIL</source>
<translation>Use PIL</translation>
</message>
<message>
<location filename="../../designer/components/ocrQueue.ui" line="51"/>
<source>icc.tryFix</source>
<translation>Try fix</translation>
</message>
<message>
<location filename="../../designer/components/ocrQueue.ui" line="61"/>
<source>queue.addImageButton</source> <source>queue.addImageButton</source>
<translation>Add Image</translation> <translation>Add Image</translation>
</message> </message>
<message> <message>
<location filename="../../designer/components/ocrQueue.ui" line="71"/> <location filename="../../designer/components/ocrQueue.ui" line="38"/>
<source>queue.removeSelected</source> <source>queue.removeSelected</source>
<translation>Remove Selected</translation> <translation>Remove Selected</translation>
</message> </message>
<message> <message>
<location filename="../../designer/components/ocrQueue.ui" line="81"/> <location filename="../../designer/components/ocrQueue.ui" line="48"/>
<source>queue.removeAll</source> <source>queue.removeAll</source>
<translation>Remove All</translation> <translation>Remove All</translation>
</message> </message>
<message> <message>
<location filename="../../designer/components/ocrQueue.ui" line="101"/> <location filename="../../designer/components/ocrQueue.ui" line="68"/>
<source>queue.optionsButton</source>
<translation>Options</translation>
</message>
<message>
<location filename="../../designer/components/ocrQueue.ui" line="75"/>
<source>queue.startOcrButton</source> <source>queue.startOcrButton</source>
<translation>Start OCR</translation> <translation>Start OCR</translation>
</message> </message>
<message> <message>
<location filename="../../designer/components/ocrQueue.ui" line="153"/> <location filename="../../designer/components/ocrQueue.ui" line="127"/>
<source>results</source> <source>results</source>
<translation>Results</translation> <translation>Results</translation>
</message> </message>
<message> <message>
<location filename="../../designer/components/ocrQueue.ui" line="162"/> <location filename="../../designer/components/ocrQueue.ui" line="136"/>
<source>results.acceptSelectedButton</source> <source>results.acceptSelectedButton</source>
<translation>Accept Selected</translation> <translation>Accept Selected</translation>
</message> </message>
<message> <message>
<location filename="../../designer/components/ocrQueue.ui" line="169"/> <location filename="../../designer/components/ocrQueue.ui" line="143"/>
<source>results.acceptAllButton</source> <source>results.acceptAllButton</source>
<translation>Accept All</translation> <translation>Accept All</translation>
</message> </message>
<message> <message>
<location filename="../../designer/components/ocrQueue.ui" line="189"/> <location filename="../../designer/components/ocrQueue.ui" line="163"/>
<source>results.ignoreValidate</source> <source>results.ignoreValidate</source>
<translation>Ignore <translation>Ignore
validation</translation> validation</translation>
</message> </message>
</context> </context>
<context>
<name>OcrQueueOptionsDialog</name>
<message>
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="14"/>
<source>OCR Options</source>
<translation>OCR Options</translation>
</message>
<message>
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="22"/>
<source>iccOptionsGroupBox</source>
<translation>ICC Profile Options</translation>
</message>
<message>
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="28"/>
<source>icc.useQt</source>
<translation>Use Qt</translation>
</message>
<message>
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="35"/>
<source>icc.usePIL</source>
<translation>Use PIL</translation>
</message>
<message>
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="45"/>
<source>icc.tryFix</source>
<translation>Try fix</translation>
</message>
<message>
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="55"/>
<source>dateOptionsGroupBox</source>
<translation>Date Source</translation>
</message>
<message>
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="64"/>
<source>date.readFromExif</source>
<translation>Read from image EXIF</translation>
</message>
<message>
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="74"/>
<source>date.useCreationDate</source>
<translation>File creation time</translation>
</message>
<message>
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="84"/>
<source>date.useModifyDate</source>
<translation>File last modification time</translation>
</message>
</context>
<context> <context>
<name>OcrTableModel</name> <name>OcrTableModel</name>
<message> <message>
<location filename="../../extends/components/ocrQueue.py" line="347"/> <location filename="../../extends/components/ocrQueue.py" line="348"/>
<source>horizontalHeader.title.select</source> <source>horizontalHeader.title.select</source>
<translation>Select</translation> <translation>Select</translation>
</message> </message>
<message> <message>
<location filename="../../extends/components/ocrQueue.py" line="348"/> <location filename="../../extends/components/ocrQueue.py" line="351"/>
<source>horizontalHeader.title.imagePreview</source> <source>horizontalHeader.title.imagePreview</source>
<translation>Image Preview</translation> <translation>Image Preview</translation>
</message> </message>
<message> <message>
<location filename="../../extends/components/ocrQueue.py" line="349"/> <location filename="../../extends/components/ocrQueue.py" line="352"/>
<source>horizontalHeader.title.chart</source> <source>horizontalHeader.title.chart</source>
<translation>Chart</translation> <translation>Chart</translation>
</message> </message>
<message> <message>
<location filename="../../extends/components/ocrQueue.py" line="350"/> <location filename="../../extends/components/ocrQueue.py" line="353"/>
<source>horizontalHeader.title.score</source> <source>horizontalHeader.title.score</source>
<translation>Score</translation> <translation>Score</translation>
</message> </message>

View File

@ -70,12 +70,12 @@
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../../startup/databaseChecker.py" line="117"/> <location filename="../../startup/databaseChecker.py" line="122"/>
<source>dialog.tryInitExistingDatabase</source> <source>dialog.tryInitExistingDatabase</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../../startup/databaseChecker.py" line="133"/> <location filename="../../startup/databaseChecker.py" line="138"/>
<source>dialog.confirmNewDatabase</source> <source>dialog.confirmNewDatabase</source>
<translation></translation> <translation></translation>
</message> </message>
@ -224,84 +224,117 @@
</message> </message>
<message> <message>
<location filename="../../designer/components/ocrQueue.ui" line="28"/> <location filename="../../designer/components/ocrQueue.ui" line="28"/>
<source>iccOptionsGroupBox</source>
<translation>ICC </translation>
</message>
<message>
<location filename="../../designer/components/ocrQueue.ui" line="34"/>
<source>icc.useQt</source>
<translation>使 Qt</translation>
</message>
<message>
<location filename="../../designer/components/ocrQueue.ui" line="41"/>
<source>icc.usePIL</source>
<translation>使 PIL</translation>
</message>
<message>
<location filename="../../designer/components/ocrQueue.ui" line="51"/>
<source>icc.tryFix</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/components/ocrQueue.ui" line="61"/>
<source>queue.addImageButton</source> <source>queue.addImageButton</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../../designer/components/ocrQueue.ui" line="71"/> <location filename="../../designer/components/ocrQueue.ui" line="38"/>
<source>queue.removeSelected</source> <source>queue.removeSelected</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../../designer/components/ocrQueue.ui" line="81"/> <location filename="../../designer/components/ocrQueue.ui" line="48"/>
<source>queue.removeAll</source> <source>queue.removeAll</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../../designer/components/ocrQueue.ui" line="101"/> <location filename="../../designer/components/ocrQueue.ui" line="68"/>
<source>queue.optionsButton</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/components/ocrQueue.ui" line="75"/>
<source>queue.startOcrButton</source> <source>queue.startOcrButton</source>
<translation> OCR</translation> <translation> OCR</translation>
</message> </message>
<message> <message>
<location filename="../../designer/components/ocrQueue.ui" line="153"/> <location filename="../../designer/components/ocrQueue.ui" line="127"/>
<source>results</source> <source>results</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../../designer/components/ocrQueue.ui" line="162"/> <location filename="../../designer/components/ocrQueue.ui" line="136"/>
<source>results.acceptSelectedButton</source> <source>results.acceptSelectedButton</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../../designer/components/ocrQueue.ui" line="169"/> <location filename="../../designer/components/ocrQueue.ui" line="143"/>
<source>results.acceptAllButton</source> <source>results.acceptAllButton</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../../designer/components/ocrQueue.ui" line="189"/> <location filename="../../designer/components/ocrQueue.ui" line="163"/>
<source>results.ignoreValidate</source> <source>results.ignoreValidate</source>
<translation></translation> <translation></translation>
</message> </message>
</context> </context>
<context>
<name>OcrQueueOptionsDialog</name>
<message>
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="14"/>
<source>OCR Options</source>
<translation>OCR </translation>
</message>
<message>
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="22"/>
<source>iccOptionsGroupBox</source>
<translation>ICC </translation>
</message>
<message>
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="28"/>
<source>icc.useQt</source>
<translation>使 Qt</translation>
</message>
<message>
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="35"/>
<source>icc.usePIL</source>
<translation>使 PIL</translation>
</message>
<message>
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="45"/>
<source>icc.tryFix</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="55"/>
<source>dateOptionsGroupBox</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="64"/>
<source>date.readFromExif</source>
<translation> EXIF </translation>
</message>
<message>
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="74"/>
<source>date.useCreationDate</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/components/ocrQueueOptionsDialog.ui" line="84"/>
<source>date.useModifyDate</source>
<translation></translation>
</message>
</context>
<context> <context>
<name>OcrTableModel</name> <name>OcrTableModel</name>
<message> <message>
<location filename="../../extends/components/ocrQueue.py" line="347"/> <location filename="../../extends/components/ocrQueue.py" line="348"/>
<source>horizontalHeader.title.select</source> <source>horizontalHeader.title.select</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../../extends/components/ocrQueue.py" line="348"/> <location filename="../../extends/components/ocrQueue.py" line="351"/>
<source>horizontalHeader.title.imagePreview</source> <source>horizontalHeader.title.imagePreview</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../../extends/components/ocrQueue.py" line="349"/> <location filename="../../extends/components/ocrQueue.py" line="352"/>
<source>horizontalHeader.title.chart</source> <source>horizontalHeader.title.chart</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../../extends/components/ocrQueue.py" line="350"/> <location filename="../../extends/components/ocrQueue.py" line="353"/>
<source>horizontalHeader.title.score</source> <source>horizontalHeader.title.score</source>
<translation></translation> <translation></translation>
</message> </message>