348 lines
11 KiB
C++
348 lines
11 KiB
C++
#include "batchprocessing.h"
|
|
#include "ui_batchprocessing.h"
|
|
#include <functional>
|
|
#include <QDir>
|
|
#include <QFileDialog>
|
|
#include <QStandardPaths>
|
|
#include <QProcess>
|
|
#include <QSettings>
|
|
#include <QCloseEvent>
|
|
#include <QMessageBox>
|
|
#include <QDesktopServices>
|
|
#include <QInputDialog>
|
|
#include <QChart>
|
|
#include <QChartView>
|
|
#include <QLineSeries>
|
|
#include "scriptengine.h"
|
|
#include "chartgraph.h"
|
|
|
|
#ifdef Q_OS_LINUX
|
|
#include <QCloseEvent>
|
|
#include <QDBusConnection>
|
|
#include <QDBusMessage>
|
|
#endif
|
|
|
|
QList<QPair<QString, QString>> scanDirectories(const QStringList &paths)
|
|
{
|
|
QList<QPair<QString, QString>> files;
|
|
QStringList scannedDirs;
|
|
|
|
std::function<void(const QString &root, const QString &path)> 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->addMarkedButton, &QPushButton::released, this, &BatchProcessing::addMarked);
|
|
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::addMarked()
|
|
{
|
|
QStringList files = _database->getMarkedFiles();
|
|
for(const QString &file : files)
|
|
{
|
|
QFileInfo info(file);
|
|
if(info.exists() && info.isReadable())
|
|
_ui->pathsList->addItem(file);
|
|
};
|
|
}
|
|
|
|
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<QVariant> 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
|
|
}
|