#include "batchprocessing.h" #include "ui_batchprocessing.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "scriptengine.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(); } void BatchProcessing::plot(const QVector &points) { QDialog *diag = new QDialog(this); diag->setAttribute(Qt::WA_DeleteOnClose); diag->setModal(false); diag->setWindowTitle(tr("Chart")); QChartView *chartView = new QChartView(diag); diag->setLayout(new QVBoxLayout); diag->layout()->addWidget(chartView); QChart *chart = new QChart; chart->setParent(chartView); auto series = new QLineSeries(chartView); series->append(points); chart->addSeries(series); chart->createDefaultAxes(); chart->setTitle("Simple line graph"); chart->legend()->hide(); chartView->setChart(chart); chartView->setRenderHint(QPainter::Antialiasing); diag->resize(640, 480); diag->show(); } 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 }