#include "batchprocessing.h" #include "ui_batchprocessing.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "scriptengine.h" #include "chartgraph.h" #ifdef Q_OS_LINUX #include #include #include #endif QList> scanDirectories(const QStringList &paths) { QList> files; QStringList scannedDirs; std::function scanDirectory = [&](const QString &root, const QString &path) { QFileInfo info(path); if(info.isDir() && !scannedDirs.contains(info.canonicalFilePath())) { scannedDirs.append(info.canonicalFilePath()); QDir dir(path); QStringList entries = dir.entryList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot); for(QString &entry : entries) scanDirectory(root, dir.absoluteFilePath(entry)); } else if(info.isFile()) { if(path == root) files.append({path, info.absolutePath()}); else files.append({path, root}); } }; for(const QString &path : paths) scanDirectory(path, path); return files; } void BatchProcessing::scanScriptDir() { QString current; if(_ui->scriptsList->currentItem()) current = _ui->scriptsList->currentItem()->text(); _ui->scriptsList->clear(); QDir dir(_scriptBasePath); QDir embededDir(":/scripts"); QStringList scripts = dir.entryList(QDir::Files | QDir::Readable); scripts.append(embededDir.entryList(QDir::Files)); scripts.removeDuplicates(); _ui->scriptsList->addItems(scripts); int idx = scripts.indexOf(current); if(idx>=0)_ui->scriptsList->setCurrentRow(idx); } BatchProcessing::BatchProcessing(Database *database, QWidget *parent) : QDialog(parent) , _database(database) { _ui = new Ui::BatchProcessing; _ui->setupUi(this); QStringList scriptsPath = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation); if(scriptsPath.size()) { QDir dir(scriptsPath.first()); if(!dir.exists("scripts")) { if(!dir.mkpath("scripts")) qWarning() << "Failed to create scripts directory"; } dir.cd("scripts"); _scriptBasePath = dir.absolutePath() + "/"; scanScriptDir(); _fileWatcher.addPath(_scriptBasePath); connect(&_fileWatcher, &QFileSystemWatcher::directoryChanged, this, &BatchProcessing::scanScriptDir); } else { qWarning() << "Failed to get app data location"; } connect(_ui->addFilesButton, &QPushButton::released, this, &BatchProcessing::addFiles); connect(_ui->addDirButton, &QPushButton::released, this, &BatchProcessing::addDir); connect(_ui->removeButton, &QPushButton::released, this, &BatchProcessing::removePath); connect(_ui->removeAllButton, &QPushButton::released, this, &BatchProcessing::removeAllPaths); connect(_ui->startButton, &QPushButton::released, this, &BatchProcessing::runScript); connect(_ui->stopButton, &QPushButton::released, this, &BatchProcessing::stopScript); connect(_ui->browseButton, &QPushButton::released, this, &BatchProcessing::browse); connect(_ui->openScriptsButton, &QPushButton::released, this, &BatchProcessing::openScriptDir); _textColor = _ui->log->palette().text().color(); QSettings settings; _ui->outputPath->setText(settings.value("batchprocessing/outputpath", QStandardPaths::standardLocations(QStandardPaths::PicturesLocation).first()).toString()); } BatchProcessing::~BatchProcessing() { delete _engineThread; QSettings settings; settings.setValue("batchprocessing/outputpath", _ui->outputPath->text()); delete _ui; } void BatchProcessing::closeEvent(QCloseEvent *event) { if(_engineThread) { QMessageBox::StandardButton ret = QMessageBox::question(this, tr("Interrupt running script?"), tr("Interrupt running script?")); if(ret == QMessageBox::StandardButton::Yes) { _engineThread->interrupt(); event->accept(); } else { event->ignore(); } } else { event->accept(); } } void BatchProcessing::addFiles() { QSettings settings; QStringList files = QFileDialog::getOpenFileNames(this, tr("Select files"), settings.value("batchprocessing/inputpath", QDir::homePath()).toString()); if(!files.isEmpty()) { _ui->pathsList->addItems(files); settings.setValue("batchprocessing/inputpath", QFileInfo(files.first()).absolutePath()); } } void BatchProcessing::addDir() { QSettings settings; QString dir = QFileDialog::getExistingDirectory(this, tr("Select directory"), settings.value("batchprocessing/inputpath", QDir::homePath()).toString()); if(!dir.isEmpty()) { _ui->pathsList->addItem(dir); settings.setValue("batchprocessing/inputpath", dir); } } void BatchProcessing::removePath() { for(auto &item : _ui->pathsList->selectedItems()) delete item; } void BatchProcessing::removeAllPaths() { _ui->pathsList->clear(); } void BatchProcessing::browse() { QString output = QFileDialog::getExistingDirectory(this, tr("Select output directory"), _ui->outputPath->text()); if(!output.isEmpty()) _ui->outputPath->setText(output); } void BatchProcessing::openScriptDir() { openDir(_scriptBasePath); } void BatchProcessing::runScript() { _ui->log->clear(); auto selectedItems = _ui->scriptsList->selectedItems(); if(selectedItems.size()) { _engineThread = new Script::ScriptEngineThread(_database, this); connect(_engineThread, &Script::ScriptEngineThread::newMessage, this, &BatchProcessing::newMessage); connect(_engineThread, &Script::ScriptEngineThread::finished, this, &BatchProcessing::scriptFinished); QStringList paths; for(int i=0; i<_ui->pathsList->count(); i++) paths.append(_ui->pathsList->item(i)->text()); QFileInfo outDir(_ui->outputPath->text()); if(outDir.exists() && outDir.isWritable()) { QString script = selectedItems.first()->text(); if(QDir(_scriptBasePath).exists(script)) script = _scriptBasePath + script; else script = ":/scripts/" + script; _engineThread->setParams(script, scanDirectories(paths), _ui->outputPath->text()); _engineThread->start(); _ui->startButton->setEnabled(false); _ui->stopButton->setEnabled(true); } else { QMessageBox::warning(this, tr("Invalid output directory"), tr("Output directory path doesn't exist or is not writable")); } } } void BatchProcessing::stopScript() { qDebug() << "Stop script"; if(_engineThread) _engineThread->interrupt(); } void BatchProcessing::scriptFinished() { _ui->startButton->setEnabled(true); _ui->stopButton->setEnabled(false); qDebug() << "script finished"; _engineThread->deleteLater(); _engineThread = nullptr; } void BatchProcessing::newMessage(const QString &message, bool error) { if(error)_ui->log->setTextColor(Qt::red); else _ui->log->setTextColor(_textColor); _ui->log->append(message); } QJSValue BatchProcessing::getString(const QString &label, const QString &text) { bool ok = false; QString ret = QInputDialog::getText(this, tr("Enter text"), label, QLineEdit::Normal, text, &ok); return ok ? ret : QJSValue(); } QJSValue BatchProcessing::getInt(const QString &label, int value) { bool ok = false; int ret = QInputDialog::getInt(this, tr("Enter integer number"), label, value, INT_MIN, INT_MAX, 1, &ok); return ok ? ret : QJSValue(); } QJSValue BatchProcessing::getFloat(const QString &label, double value, int decimals) { bool ok = false; double ret = QInputDialog::getDouble(this, tr("Enter float number"), label, value, -INFINITY, INFINITY, decimals, &ok); return ok ? ret : QJSValue(); } QJSValue BatchProcessing::getItem(const QStringList &items, const QString &label, int current) { bool ok = false; QString ret = QInputDialog::getItem(this, tr("Select item"), label, items, current, false, &ok); return ok ? ret : QJSValue(); } QJSValue BatchProcessing::question(const QString &question, const QStringList &buttons, const QString &title) { QMessageBox::StandardButtons standardButtons = QMessageBox::NoButton; if(buttons.contains("ok"))standardButtons |= QMessageBox::Ok; if(buttons.contains("yes"))standardButtons |= QMessageBox::Yes; if(buttons.contains("no"))standardButtons |= QMessageBox::No; if(buttons.contains("yesall"))standardButtons |= QMessageBox::YesToAll; if(buttons.contains("noall"))standardButtons |= QMessageBox::NoToAll; if(buttons.contains("abort"))standardButtons |= QMessageBox::Abort; if(buttons.contains("retry"))standardButtons |= QMessageBox::Retry; if(buttons.contains("ignore"))standardButtons |= QMessageBox::Ignore; if(buttons.contains("cancel"))standardButtons |= QMessageBox::Cancel; if(buttons.contains("discard"))standardButtons |= QMessageBox::Discard; if(buttons.contains("apply"))standardButtons |= QMessageBox::Apply; if(buttons.contains("reset"))standardButtons |= QMessageBox::Reset; if(standardButtons == QMessageBox::NoButton)standardButtons = QMessageBox::Ok; QMessageBox::StandardButton button = QMessageBox::question(this, title, question, standardButtons); QJSValue ret; switch(button) { default: case QMessageBox::Ok: ret = "ok"; break; case QMessageBox::Yes: ret = "yes"; break; case QMessageBox::No: ret = "no"; break; case QMessageBox::YesToAll: ret = "yesall"; break; case QMessageBox::NoToAll: ret = "noall"; break; case QMessageBox::Abort: ret = "abort"; break; case QMessageBox::Retry: ret = "retry"; break; case QMessageBox::Ignore: ret = "ignore"; break; case QMessageBox::Cancel: ret = "cancel"; break; case QMessageBox::Discard: ret = "discard"; break; case QMessageBox::Apply: ret = "apply"; break; case QMessageBox::Reset: ret = "reset"; break; } return ret; } void BatchProcessing::plot(const QVariant &graph) { ChartGraph *chart = new ChartGraph(this); chart->plot(graph); } void openDir(const QString &path) { #ifdef Q_OS_LINUX QDBusConnection con = QDBusConnection::sessionBus(); QDBusMessage message = QDBusMessage::createMethodCall("org.freedesktop.FileManager1", "/org/freedesktop/FileManager1", "org.freedesktop.FileManager1", "ShowFolders"); QList args = {QStringList(QUrl::fromLocalFile(path).toString()), QString()}; message.setArguments(args); con.call(message); #endif #ifdef Q_OS_WINDOWS QProcess::startDetached("explorer.exe", {QDir::toNativeSeparators(path)}); #endif #ifdef Q_OS_MACOS QDesktopServices::openUrl(QUrl::fromLocalFile(path)); #endif }