From 40a9f3dc5d37f89debe9dec67cab83bae951c32d Mon Sep 17 00:00:00 2001 From: yzinchuk Date: Fri, 19 Jun 2026 16:45:22 -0400 Subject: [PATCH] gitignore, dialogues --- .gitignore | 61 +++++++++++++ src/updatedatabasedialog.cpp | 167 +++++++++++++++++++++++++++++++++++ src/updatedatabasedialog.h | 57 ++++++++++++ 3 files changed, 285 insertions(+) create mode 100644 .gitignore create mode 100644 src/updatedatabasedialog.cpp create mode 100644 src/updatedatabasedialog.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fd78f24 --- /dev/null +++ b/.gitignore @@ -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 diff --git a/src/updatedatabasedialog.cpp b/src/updatedatabasedialog.cpp new file mode 100644 index 0000000..92e9897 --- /dev/null +++ b/src/updatedatabasedialog.cpp @@ -0,0 +1,167 @@ +#include "updatedatabasedialog.h" + +#include +#include +#include +#include +#include +#include + +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::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()); +} diff --git a/src/updatedatabasedialog.h b/src/updatedatabasedialog.h new file mode 100644 index 0000000..dad3881 --- /dev/null +++ b/src/updatedatabasedialog.h @@ -0,0 +1,57 @@ +#ifndef UPDATEDATABASEDIALOG_H +#define UPDATEDATABASEDIALOG_H + +#include +#include +#include +#include +#include +#include +#include + +/** + * @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