gitignore, dialogues
This commit is contained in:
+61
@@ -0,0 +1,61 @@
|
|||||||
|
# CMake build directory
|
||||||
|
build/
|
||||||
|
|
||||||
|
# Qt MOC files
|
||||||
|
moc_*.cpp
|
||||||
|
*.moc
|
||||||
|
|
||||||
|
# Qt resource compiler
|
||||||
|
qrc_*.cpp
|
||||||
|
|
||||||
|
# Qt UI files
|
||||||
|
ui_*.h
|
||||||
|
|
||||||
|
# Object files
|
||||||
|
*.o
|
||||||
|
*.obj
|
||||||
|
|
||||||
|
# Libraries
|
||||||
|
*.a
|
||||||
|
*.lib
|
||||||
|
|
||||||
|
# Executables
|
||||||
|
*.exe
|
||||||
|
*.out
|
||||||
|
*.app
|
||||||
|
screenshot-gallery
|
||||||
|
|
||||||
|
# Qt specific
|
||||||
|
*.autosave
|
||||||
|
*.qmlc
|
||||||
|
*.jsc
|
||||||
|
|
||||||
|
# IDE and editor files
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.pro.user
|
||||||
|
*.pro.user.*
|
||||||
|
*~
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Cache and temporary files
|
||||||
|
.cache/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*.tmp
|
||||||
|
|
||||||
|
# CMake files (if generated in source directory)
|
||||||
|
CMakeFiles/
|
||||||
|
CMakeCache.txt
|
||||||
|
cmake_install.cmake
|
||||||
|
Makefile
|
||||||
|
*.cmake
|
||||||
|
|
||||||
|
# Backup files
|
||||||
|
*.bak
|
||||||
|
*.backup
|
||||||
|
*~
|
||||||
|
|
||||||
|
# System files
|
||||||
|
.directory
|
||||||
|
Thumbs.db
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
#include "updatedatabasedialog.h"
|
||||||
|
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QScrollBar>
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
UpdateDatabaseDialog::UpdateDatabaseDialog(const QString &scriptPath,
|
||||||
|
const QString &dbPath,
|
||||||
|
const QString &screenshotsDir,
|
||||||
|
QWidget *parent)
|
||||||
|
: QDialog(parent)
|
||||||
|
, m_scriptPath(scriptPath)
|
||||||
|
, m_dbPath(dbPath)
|
||||||
|
, m_screenshotsDir(screenshotsDir)
|
||||||
|
, m_process(nullptr)
|
||||||
|
{
|
||||||
|
setWindowTitle(tr("Update Database"));
|
||||||
|
setMinimumSize(600, 400);
|
||||||
|
// Non-modal: caller uses show(), not exec()
|
||||||
|
setWindowModality(Qt::NonModal);
|
||||||
|
|
||||||
|
// ----- Layout -----
|
||||||
|
auto *mainLayout = new QVBoxLayout(this);
|
||||||
|
|
||||||
|
m_statusLabel = new QLabel(tr("Starting OCR process…"), this);
|
||||||
|
mainLayout->addWidget(m_statusLabel);
|
||||||
|
|
||||||
|
m_progressBar = new QProgressBar(this);
|
||||||
|
m_progressBar->setRange(0, 0); // indeterminate
|
||||||
|
mainLayout->addWidget(m_progressBar);
|
||||||
|
|
||||||
|
m_logView = new QTextEdit(this);
|
||||||
|
m_logView->setReadOnly(true);
|
||||||
|
m_logView->setFont(QFont("monospace", 9));
|
||||||
|
mainLayout->addWidget(m_logView);
|
||||||
|
|
||||||
|
auto *btnLayout = new QHBoxLayout();
|
||||||
|
btnLayout->addStretch();
|
||||||
|
|
||||||
|
m_cancelButton = new QPushButton(tr("Cancel"), this);
|
||||||
|
btnLayout->addWidget(m_cancelButton);
|
||||||
|
|
||||||
|
m_closeButton = new QPushButton(tr("Close"), this);
|
||||||
|
m_closeButton->setEnabled(false);
|
||||||
|
btnLayout->addWidget(m_closeButton);
|
||||||
|
|
||||||
|
mainLayout->addLayout(btnLayout);
|
||||||
|
|
||||||
|
connect(m_cancelButton, &QPushButton::clicked, this, &UpdateDatabaseDialog::onCancelClicked);
|
||||||
|
connect(m_closeButton, &QPushButton::clicked, this, &QDialog::accept);
|
||||||
|
|
||||||
|
startProcess();
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateDatabaseDialog::~UpdateDatabaseDialog()
|
||||||
|
{
|
||||||
|
if (m_process && m_process->state() != QProcess::NotRunning) {
|
||||||
|
m_process->kill();
|
||||||
|
m_process->waitForFinished(3000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateDatabaseDialog::startProcess()
|
||||||
|
{
|
||||||
|
QFileInfo scriptInfo(m_scriptPath);
|
||||||
|
if (!scriptInfo.exists()) {
|
||||||
|
appendLog(tr("Error: script not found at %1").arg(m_scriptPath), true);
|
||||||
|
m_progressBar->setRange(0, 1);
|
||||||
|
m_progressBar->setValue(1);
|
||||||
|
m_statusLabel->setText(tr("Failed – script not found."));
|
||||||
|
m_cancelButton->setEnabled(false);
|
||||||
|
m_closeButton->setEnabled(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_process = new QProcess(this);
|
||||||
|
m_process->setProcessChannelMode(QProcess::SeparateChannels);
|
||||||
|
|
||||||
|
connect(m_process, &QProcess::readyReadStandardOutput,
|
||||||
|
this, &UpdateDatabaseDialog::onReadyReadStdOut);
|
||||||
|
connect(m_process, &QProcess::readyReadStandardError,
|
||||||
|
this, &UpdateDatabaseDialog::onReadyReadStdErr);
|
||||||
|
connect(m_process,
|
||||||
|
QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
|
||||||
|
this, &UpdateDatabaseDialog::onProcessFinished);
|
||||||
|
|
||||||
|
const QStringList args = {
|
||||||
|
m_scriptPath,
|
||||||
|
"--db", m_dbPath,
|
||||||
|
"--screenshots-dir", m_screenshotsDir
|
||||||
|
};
|
||||||
|
|
||||||
|
appendLog(tr("Running: python3 %1").arg(args.join(' ')));
|
||||||
|
appendLog(QString());
|
||||||
|
|
||||||
|
m_process->start("python3", args);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateDatabaseDialog::onReadyReadStdOut()
|
||||||
|
{
|
||||||
|
const QString text = QString::fromUtf8(m_process->readAllStandardOutput());
|
||||||
|
for (const QString &line : text.split('\n', Qt::SkipEmptyParts))
|
||||||
|
appendLog(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateDatabaseDialog::onReadyReadStdErr()
|
||||||
|
{
|
||||||
|
const QString text = QString::fromUtf8(m_process->readAllStandardError());
|
||||||
|
for (const QString &line : text.split('\n', Qt::SkipEmptyParts))
|
||||||
|
appendLog(line, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateDatabaseDialog::onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus)
|
||||||
|
{
|
||||||
|
m_progressBar->setRange(0, 1);
|
||||||
|
m_progressBar->setValue(1);
|
||||||
|
m_cancelButton->setEnabled(false);
|
||||||
|
m_closeButton->setEnabled(true);
|
||||||
|
|
||||||
|
if (exitStatus == QProcess::CrashExit || exitCode != 0) {
|
||||||
|
m_statusLabel->setText(tr("OCR process failed (exit code %1).").arg(exitCode));
|
||||||
|
appendLog(tr("\nProcess exited with code %1.").arg(exitCode), true);
|
||||||
|
} else {
|
||||||
|
m_statusLabel->setText(tr("OCR process completed successfully."));
|
||||||
|
appendLog(tr("\nDone."));
|
||||||
|
emit updateFinished();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateDatabaseDialog::onCancelClicked()
|
||||||
|
{
|
||||||
|
if (m_process && m_process->state() != QProcess::NotRunning) {
|
||||||
|
appendLog(tr("\nCancelling…"), true);
|
||||||
|
m_cancelButton->setEnabled(false);
|
||||||
|
m_statusLabel->setText(tr("Cancelling…"));
|
||||||
|
|
||||||
|
// SIGTERM – Python script's signal handler will clean up and exit
|
||||||
|
m_process->terminate();
|
||||||
|
|
||||||
|
// If it hasn't exited after 3 s, send SIGKILL
|
||||||
|
QTimer::singleShot(3000, this, [this]() {
|
||||||
|
if (m_process && m_process->state() != QProcess::NotRunning) {
|
||||||
|
appendLog(tr("Force-killing process…"), true);
|
||||||
|
m_process->kill();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
reject();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateDatabaseDialog::appendLog(const QString &text, bool isError)
|
||||||
|
{
|
||||||
|
if (isError)
|
||||||
|
m_logView->setTextColor(QColor("#cc3333"));
|
||||||
|
else
|
||||||
|
m_logView->setTextColor(m_logView->palette().color(QPalette::Text));
|
||||||
|
|
||||||
|
m_logView->append(text);
|
||||||
|
|
||||||
|
// Auto-scroll to bottom
|
||||||
|
QScrollBar *sb = m_logView->verticalScrollBar();
|
||||||
|
sb->setValue(sb->maximum());
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
#ifndef UPDATEDATABASEDIALOG_H
|
||||||
|
#define UPDATEDATABASEDIALOG_H
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QTextEdit>
|
||||||
|
#include <QProgressBar>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QProcess>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Non-modal dialog that runs the OCR update script and streams its output.
|
||||||
|
*
|
||||||
|
* The dialog stays open after the process finishes so the user can read the log.
|
||||||
|
* It emits updateFinished() when the script exits successfully so the caller can
|
||||||
|
* reload the gallery.
|
||||||
|
*/
|
||||||
|
class UpdateDatabaseDialog : public QDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit UpdateDatabaseDialog(const QString &scriptPath,
|
||||||
|
const QString &dbPath,
|
||||||
|
const QString &screenshotsDir,
|
||||||
|
QWidget *parent = nullptr);
|
||||||
|
~UpdateDatabaseDialog();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
/** Emitted when the OCR script exits with code 0. */
|
||||||
|
void updateFinished();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onReadyReadStdOut();
|
||||||
|
void onReadyReadStdErr();
|
||||||
|
void onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
|
||||||
|
void onCancelClicked();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void startProcess();
|
||||||
|
void appendLog(const QString &text, bool isError = false);
|
||||||
|
|
||||||
|
QString m_scriptPath;
|
||||||
|
QString m_dbPath;
|
||||||
|
QString m_screenshotsDir;
|
||||||
|
|
||||||
|
QLabel *m_statusLabel;
|
||||||
|
QTextEdit *m_logView;
|
||||||
|
QProgressBar*m_progressBar;
|
||||||
|
QPushButton *m_cancelButton;
|
||||||
|
QPushButton *m_closeButton;
|
||||||
|
|
||||||
|
QProcess *m_process;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // UPDATEDATABASEDIALOG_H
|
||||||
Reference in New Issue
Block a user