From 7c93fbbb6f08c6afd09c10eecf1b6d44791bea4c Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 16 Dec 2024 22:46:11 +1100 Subject: [PATCH 01/16] Framework for data filtering via plugin --- lumberjack.pro | 1 + src/plugins/plugin_filter.cpp | 26 ++++++++++++++++++++++++++ src/plugins/plugin_filter.hpp | 18 +++++++++++++++++- 3 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 src/plugins/plugin_filter.cpp diff --git a/lumberjack.pro b/lumberjack.pro index 8027def..e4fea73 100644 --- a/lumberjack.pro +++ b/lumberjack.pro @@ -46,6 +46,7 @@ SOURCES += \ src/main.cpp \ src/mainwindow.cpp \ src/plugins/plugin_exporter.cpp \ + src/plugins/plugin_filter.cpp \ src/plugins/plugin_importer.cpp \ src/plugins/plugin_registry.cpp \ src/widgets/about_dialog.cpp \ diff --git a/src/plugins/plugin_filter.cpp b/src/plugins/plugin_filter.cpp new file mode 100644 index 0000000..ecf70ef --- /dev/null +++ b/src/plugins/plugin_filter.cpp @@ -0,0 +1,26 @@ +#include "plugin_filter.hpp" + + +/** + * @brief FilterPlugin::setFilterInputs - Default implementation to set filter inputs + * @param inputs + * @param errors + * @return + */ +bool FilterPlugin::setFilterInputs(QList inputs, QStringList &errors) +{ + if (inputs.count() < getMinInputCount()) + { + errors.append(tr("Not enough inputs provided")); + return false; + } + + if (inputs.count() > getMaxInputCount()) + { + errors.append(tr("Too many inputs provided")); + return false; + } + + m_inputs = inputs; + return true; +} diff --git a/src/plugins/plugin_filter.hpp b/src/plugins/plugin_filter.hpp index 0db63bb..0e203f3 100644 --- a/src/plugins/plugin_filter.hpp +++ b/src/plugins/plugin_filter.hpp @@ -3,6 +3,7 @@ #include +#include "data_series.hpp" #include "plugin_base.hpp" #define FilterInterface_iid "org.lumberjack.plugins.FilterPlugin/1.0" @@ -24,8 +25,23 @@ class FilterPlugin : public PluginBase return QString(FilterInterface_iid); } + // Return the minimum number of inputs supported by this plugin + virtual unsigned int getMinInputCount(void) const = 0; - // TODO + // Return the maximum number of inputs supported by this plugin + virtual unsigned int getMaxInputCount(void) const = 0; + + // Set the filter inputs + virtual bool setFilterInputs(QList inputs, QStringList &errors); + + // Perform filtering operations on the provided set of inputs + virtual bool filterInputs(QStringList &errors) = 0; + + // Return the generated data series + virtual DataSeriesPointer getOutput(void) = 0; + +protected: + QList m_inputs; }; typedef QList> FilterPluginList; From 730695788b4c54769d883f712df56c7770aeb22d Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Wed, 18 Dec 2024 22:47:37 +1100 Subject: [PATCH 02/16] Refactor plugin architecture --- .../csv_exporter/lumberjack_csv_exporter.cpp | 6 ++--- .../csv_exporter/lumberjack_csv_exporter.hpp | 7 +++--- .../csv_importer/lumberjack_csv_importer.cpp | 8 +++--- .../csv_importer/lumberjack_csv_importer.hpp | 9 ++++--- plugins/plugins.pro | 4 +-- src/data_source_manager.cpp | 12 ++++----- src/plugins/plugin_base.hpp | 25 +++++++++++++++++++ src/plugins/plugin_exporter.hpp | 17 ++----------- src/plugins/plugin_filter.hpp | 6 ++--- src/plugins/plugin_importer.hpp | 15 +---------- 10 files changed, 55 insertions(+), 54 deletions(-) diff --git a/plugins/csv_exporter/lumberjack_csv_exporter.cpp b/plugins/csv_exporter/lumberjack_csv_exporter.cpp index 2cd83a7..98e3991 100644 --- a/plugins/csv_exporter/lumberjack_csv_exporter.cpp +++ b/plugins/csv_exporter/lumberjack_csv_exporter.cpp @@ -20,7 +20,7 @@ QStringList LumberjackCSVExporter::supportedFileTypes() const } -bool LumberjackCSVExporter::beforeExport(void) +bool LumberjackCSVExporter::beforeProcessStep(void) { // TODO: Set export options return true; @@ -238,13 +238,13 @@ QStringList LumberjackCSVExporter::nextDataRow(bool &valid) } -void LumberjackCSVExporter::cancelExport() +void LumberjackCSVExporter::cancelProcessing() { m_isExporting = false; } -uint8_t LumberjackCSVExporter::getExportProgress(void) const +uint8_t LumberjackCSVExporter::getProgress(void) const { double dt = m_maxTimestamp - m_minTimestamp; diff --git a/plugins/csv_exporter/lumberjack_csv_exporter.hpp b/plugins/csv_exporter/lumberjack_csv_exporter.hpp index 3fa05fa..f2b8753 100644 --- a/plugins/csv_exporter/lumberjack_csv_exporter.hpp +++ b/plugins/csv_exporter/lumberjack_csv_exporter.hpp @@ -21,10 +21,11 @@ class CSV_EXPORTER_EXPORT LumberjackCSVExporter : public ExportPlugin // Exporter plugin functionality virtual QStringList supportedFileTypes(void) const override; - virtual bool beforeExport(void) override; virtual bool exportData(QList &series, QStringList &errors) override; - virtual void cancelExport(void) override; - virtual uint8_t getExportProgress(void) const override; + + virtual bool beforeProcessStep(void) override; + virtual void cancelProcessing(void) override; + virtual uint8_t getProgress(void) const override; protected: const QString m_name = "CSV Exporter"; diff --git a/plugins/csv_importer/lumberjack_csv_importer.cpp b/plugins/csv_importer/lumberjack_csv_importer.cpp index 02f8ac9..38afea5 100644 --- a/plugins/csv_importer/lumberjack_csv_importer.cpp +++ b/plugins/csv_importer/lumberjack_csv_importer.cpp @@ -31,7 +31,7 @@ QStringList LumberjackCSVImporter::supportedFileTypes() const * @brief LumberjackCSVImporter::beforeLoadData - Open configuration dialog * @return */ -bool LumberjackCSVImporter::beforeImport(void) +bool LumberjackCSVImporter::beforeProcessStep(void) { CSVImportOptionsDialog dlg(m_filename); @@ -418,7 +418,7 @@ bool LumberjackCSVImporter::extractTimestamp(int rowIndex, const QStringList &ro } -void LumberjackCSVImporter::afterImport(void) +void LumberjackCSVImporter::afterProcessStep(void) { if (m_file) { @@ -433,13 +433,13 @@ void LumberjackCSVImporter::afterImport(void) } -void LumberjackCSVImporter::cancelImport(void) +void LumberjackCSVImporter::cancelProcessing(void) { m_isImporting = false; } -uint8_t LumberjackCSVImporter::getImportProgress(void) const +uint8_t LumberjackCSVImporter::getProgress(void) const { if (!m_isImporting) return 0; diff --git a/plugins/csv_importer/lumberjack_csv_importer.hpp b/plugins/csv_importer/lumberjack_csv_importer.hpp index 606cdbd..f546016 100644 --- a/plugins/csv_importer/lumberjack_csv_importer.hpp +++ b/plugins/csv_importer/lumberjack_csv_importer.hpp @@ -25,12 +25,13 @@ class CSV_IMPORTER_EXPORT LumberjackCSVImporter : public ImportPlugin // Importer plugin functionality virtual QStringList supportedFileTypes(void) const override; - virtual bool beforeImport(void) override; virtual bool importData(QStringList &errors) override; - virtual void afterImport(void) override; - virtual void cancelImport(void) override; - virtual uint8_t getImportProgress(void) const override; + virtual bool beforeProcessStep(void) override; + virtual void afterProcessStep(void) override; + virtual void cancelProcessing(void) override; + + virtual uint8_t getProgress(void) const override; virtual QList> getDataSeries(void) const override; diff --git a/plugins/plugins.pro b/plugins/plugins.pro index 8f01348..781bb1d 100644 --- a/plugins/plugins.pro +++ b/plugins/plugins.pro @@ -5,6 +5,6 @@ TEMPLATE = subdirs SUBDIRS += \ csv_importer \ csv_exporter \ - offset_filter \ - scaler_filter \ + #offset_filter \ + #scaler_filter \ diff --git a/src/data_source_manager.cpp b/src/data_source_manager.cpp index 6ff6514..db47fa7 100644 --- a/src/data_source_manager.cpp +++ b/src/data_source_manager.cpp @@ -39,7 +39,7 @@ void DataImportWorker::cancelImport() { if (m_plugin) { - m_plugin->cancelImport(); + m_plugin->cancelProcessing(); m_result = false; } @@ -77,7 +77,7 @@ void DataExportWorker::cancelExport() { if (m_plugin) { - m_plugin->cancelExport(); + m_plugin->cancelProcessing(); m_result = false; } @@ -351,7 +351,7 @@ bool DataSourceManager::importData(QString filename) importer->setFilename(filename); - if (!importer->beforeImport()) + if (!importer->beforeProcessStep()) { // TODO: error message? return false; @@ -392,7 +392,7 @@ bool DataSourceManager::importData(QString filename) thread->wait(); } - progress.setValue(importer->getImportProgress()); + progress.setValue(importer->getProgress()); QApplication::processEvents(); QThread::msleep(100); @@ -496,7 +496,7 @@ bool DataSourceManager::exportData(QList &series, QString fil exporter->setFilename(filename); - if (!exporter->beforeExport()) + if (!exporter->beforeProcessStep()) { // TODO: error mesage? return false; @@ -536,7 +536,7 @@ bool DataSourceManager::exportData(QList &series, QString fil thread->wait(); } - progress.setValue(exporter->getExportProgress()); + progress.setValue(exporter->getProgress()); QApplication::processEvents(); QThread::msleep(100); diff --git a/src/plugins/plugin_base.hpp b/src/plugins/plugin_base.hpp index 1a98859..96acb25 100644 --- a/src/plugins/plugin_base.hpp +++ b/src/plugins/plugin_base.hpp @@ -28,4 +28,29 @@ class PluginBase : public QObject typedef QList> PluginList; + +// Base plugin class which provides data processing functionality +class DataProcessingPlugin : public PluginBase +{ + Q_OBJECT + +public: + virtual ~DataProcessingPlugin() = default; + + // Optional function which is called before running data processing step + // Return False to cancel the process before it begins + virtual bool beforeProcessStep(void) { return true; } + + // Optional function which is called after running the data processing step + virtual void afterProcessStep(void) {} + + // Function to cancel the process step + // MUST be implemented by the particular plugin class + virtual void cancelProcessing(void) = 0; + + // Return the progress of the data processing stage (as a percentage {0:100}) + virtual uint8_t getProgress(void) const = 0; + +}; + #endif // PLUGIN_BASE_HPP diff --git a/src/plugins/plugin_exporter.hpp b/src/plugins/plugin_exporter.hpp index e27028b..70ac7fd 100644 --- a/src/plugins/plugin_exporter.hpp +++ b/src/plugins/plugin_exporter.hpp @@ -11,31 +11,18 @@ /** * @brief The ExportPlugin class defines an interface for exporting data */ -class ExportPlugin : public PluginBase +class ExportPlugin : public DataProcessingPlugin { Q_OBJECT public: virtual ~ExportPlugin() = default; // Return a list of the support file types e.g. ['csv', 'tsv'] - virtual QStringList supportedFileTypes(void) const = 0; - - // Optional function called before data export - // Return False to cancel the data export process - virtual bool beforeExport(void) { return true; } + virtual QStringList supportedFileTypes(void) const = 0; // Export data to the provided filename virtual bool exportData(QList &series, QStringList &errors) = 0; - // Optional function called after data export - virtual void afterExport(void) {} - - // Cancel import process - plugin is expected to perform cleanup - virtual void cancelExport(void) = 0; - - // Return the progress of the data export process (as a percentage {0:100}) - virtual uint8_t getExportProgress(void) const = 0; - virtual QString pluginIID(void) const override { return QString(ExporterInterface_iid); diff --git a/src/plugins/plugin_filter.hpp b/src/plugins/plugin_filter.hpp index 0e203f3..70016ec 100644 --- a/src/plugins/plugin_filter.hpp +++ b/src/plugins/plugin_filter.hpp @@ -13,7 +13,7 @@ /** * @brief The FilterPlugin class defines an interface for applying custom data filters */ -class FilterPlugin : public PluginBase +class FilterPlugin : public DataProcessingPlugin { Q_OBJECT public: @@ -37,8 +37,8 @@ class FilterPlugin : public PluginBase // Perform filtering operations on the provided set of inputs virtual bool filterInputs(QStringList &errors) = 0; - // Return the generated data series - virtual DataSeriesPointer getOutput(void) = 0; + // Return the generated data series output(s) + virtual QList getOutputs(void) = 0; protected: QList m_inputs; diff --git a/src/plugins/plugin_importer.hpp b/src/plugins/plugin_importer.hpp index 1d1f12a..d3a2200 100644 --- a/src/plugins/plugin_importer.hpp +++ b/src/plugins/plugin_importer.hpp @@ -13,7 +13,7 @@ /** * @brief The ImportPlugin class defines an interface for importing data */ -class ImportPlugin : public PluginBase +class ImportPlugin : public DataProcessingPlugin { Q_OBJECT public: @@ -27,22 +27,9 @@ class ImportPlugin : public PluginBase // but this can be extended by the plugin virtual bool validateFile(QString filename, QStringList &errors) const; - // Optional function called before data import - // Return False to cancel the data import process - virtual bool beforeImport() { return true; } - // Load data from the provided filename virtual bool importData(QStringList &errors) = 0; - // Optional function called after data import - virtual void afterImport(void) {} - - // Cancel import process - plugin is expected to perform cleanup - virtual void cancelImport(void) = 0; - - // Return the progress of the data import process (as a percentage {0:100}) - virtual uint8_t getImportProgress(void) const = 0; - // After import, plugin must return a list of DataSeries objects virtual QList getDataSeries(void) const = 0; From 88f6c0c6b3f3dba1ef797c87a801e1b9e5c86274 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Wed, 18 Dec 2024 23:05:15 +1100 Subject: [PATCH 03/16] Enhance plugin dialog --- src/plugins/plugin_filter.hpp | 4 ++-- src/widgets/plugins_dialog.cpp | 27 ++++++++++++++++++++++++++- ui/plugins_dialog.ui | 2 +- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/plugins/plugin_filter.hpp b/src/plugins/plugin_filter.hpp index 70016ec..b0848e3 100644 --- a/src/plugins/plugin_filter.hpp +++ b/src/plugins/plugin_filter.hpp @@ -35,10 +35,10 @@ class FilterPlugin : public DataProcessingPlugin virtual bool setFilterInputs(QList inputs, QStringList &errors); // Perform filtering operations on the provided set of inputs - virtual bool filterInputs(QStringList &errors) = 0; + virtual bool filterData(QStringList &errors) = 0; // Return the generated data series output(s) - virtual QList getOutputs(void) = 0; + virtual QList getFilterOutputs(void) = 0; protected: QList m_inputs; diff --git a/src/widgets/plugins_dialog.cpp b/src/widgets/plugins_dialog.cpp index 8a09dc4..5a2b20a 100644 --- a/src/widgets/plugins_dialog.cpp +++ b/src/widgets/plugins_dialog.cpp @@ -8,6 +8,8 @@ PluginsDialog::PluginsDialog(QWidget *parent) : QDialog(parent) initPluginsTable(); + ui.iid->clear(); + // Add in each type of plugin interface ui.plugin_select->addItem(tr("Data Import")); ui.plugin_select->addItem(tr("Data Export")); @@ -17,7 +19,15 @@ PluginsDialog::PluginsDialog(QWidget *parent) : QDialog(parent) connect(ui.closeButton, &QPushButton::released, this, &PluginsDialog::close); // Display plugin locations - QStringList paths = QCoreApplication::libraryPaths(); + QStringList paths; + + for (QString path : QCoreApplication::libraryPaths()) + { + if (!paths.contains(path)) + { + paths.append(path); + } + } ui.library_paths->setText( tr("Library Paths") + ":\n- " + paths.join("\n- ") @@ -72,30 +82,45 @@ void PluginsDialog::selectPluginType(int idx) PluginList plugins; + QString iid; + switch (idx) { case 1: // Importer plugins + iid = QString(ImporterInterface_iid); for (auto plugin : registry->ImportPlugins()) { plugins.append(plugin); } break; case 2: // Exporter plugins + iid = QString(ExporterInterface_iid); for (auto plugin : registry->ExportPlugins()) { plugins.append(plugin); } break; case 3: // Filter plugins + iid = QString(FilterInterface_iid); for (auto plugin : registry->FilterPlugins()) { plugins.append(plugin); } break; default: + iid = QString(); break; } loadPluginsTable(plugins); + + if (iid.isEmpty()) + { + ui.iid->clear(); + } + else + { + ui.iid->setText(tr("Plugin Version") + ": " + iid); + } } diff --git a/ui/plugins_dialog.ui b/ui/plugins_dialog.ui index ef86035..226ec58 100644 --- a/ui/plugins_dialog.ui +++ b/ui/plugins_dialog.ui @@ -37,7 +37,7 @@ - + <IID String> From b96583734be974264332653dca77fcf61171716f Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Wed, 18 Dec 2024 23:05:23 +1100 Subject: [PATCH 04/16] Plugin updates --- plugins/offset_filter/offset_filter.cpp | 7 - plugins/offset_filter/offset_filter.hpp | 31 --- plugins/offset_filter/offset_filter.json | 1 - plugins/offset_filter/offset_filter.pro | 49 ---- .../offset_scaler_filter.cpp | 50 ++++ .../offset_scaler_filter.hpp | 48 ++++ .../offset_scaler_filter.json | 1 + .../offset_scaler_filter.pro} | 10 +- .../offset_scaler_filter.user} | 0 .../offset_scaler_filter_global.h} | 0 plugins/plugins.pro | 3 +- plugins/scaler_filter/.gitignore | 2 - plugins/scaler_filter/scaler_filter.cpp | 8 - plugins/scaler_filter/scaler_filter.hpp | 31 --- plugins/scaler_filter/scaler_filter.json | 1 - plugins/scaler_filter/scaler_filter.user | 227 ------------------ plugins/scaler_filter/scaler_filter_global.h | 12 - 17 files changed, 105 insertions(+), 376 deletions(-) delete mode 100644 plugins/offset_filter/offset_filter.cpp delete mode 100644 plugins/offset_filter/offset_filter.hpp delete mode 100644 plugins/offset_filter/offset_filter.json delete mode 100644 plugins/offset_filter/offset_filter.pro create mode 100644 plugins/offset_scaler_filter/offset_scaler_filter.cpp create mode 100644 plugins/offset_scaler_filter/offset_scaler_filter.hpp create mode 100644 plugins/offset_scaler_filter/offset_scaler_filter.json rename plugins/{scaler_filter/scaler_filter.pro => offset_scaler_filter/offset_scaler_filter.pro} (82%) rename plugins/{offset_filter/offset_filter.user => offset_scaler_filter/offset_scaler_filter.user} (100%) rename plugins/{offset_filter/offset_filter_global.h => offset_scaler_filter/offset_scaler_filter_global.h} (100%) delete mode 100644 plugins/scaler_filter/.gitignore delete mode 100644 plugins/scaler_filter/scaler_filter.cpp delete mode 100644 plugins/scaler_filter/scaler_filter.hpp delete mode 100644 plugins/scaler_filter/scaler_filter.json delete mode 100644 plugins/scaler_filter/scaler_filter.user delete mode 100644 plugins/scaler_filter/scaler_filter_global.h diff --git a/plugins/offset_filter/offset_filter.cpp b/plugins/offset_filter/offset_filter.cpp deleted file mode 100644 index 93862e2..0000000 --- a/plugins/offset_filter/offset_filter.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "offset_filter.hpp" - - -OffsetFilter::OffsetFilter() -{ - // TODO -} diff --git a/plugins/offset_filter/offset_filter.hpp b/plugins/offset_filter/offset_filter.hpp deleted file mode 100644 index 2e1bf38..0000000 --- a/plugins/offset_filter/offset_filter.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef LUMBERJACK_CSV_IMPORTER_HPP -#define LUMBERJACK_CSV_IMPORTER_HPP - -#include "offset_filter_global.h" -#include "plugin_filter.hpp" - - -/** - * @brief The ScaleOffsetFilter class provides simple scaling and offset functionality - */ -class CUSTOM_FILTER_EXPORT OffsetFilter : public FilterPlugin -{ - Q_OBJECT - Q_PLUGIN_METADATA(IID FilterInterface_iid) - Q_INTERFACES(FilterPlugin) -public: - OffsetFilter(); - - // Base plugin functionality - virtual QString pluginName(void) const override { return m_name; } - virtual QString pluginDescription(void) const override { return m_description; } - virtual QString pluginVersion(void) const override { return m_version; } - -protected: - const QString m_name = "Offset Filter"; - const QString m_description = "Apply custom offset to a dataset"; - const QString m_version = "0.1.0"; - -}; - -#endif // LUMBERJACK_CSV_IMPORTER_HPP diff --git a/plugins/offset_filter/offset_filter.json b/plugins/offset_filter/offset_filter.json deleted file mode 100644 index 3aff03c..0000000 --- a/plugins/offset_filter/offset_filter.json +++ /dev/null @@ -1 +0,0 @@ -{ "Keys": [ "offset_filter" ] } diff --git a/plugins/offset_filter/offset_filter.pro b/plugins/offset_filter/offset_filter.pro deleted file mode 100644 index 638422a..0000000 --- a/plugins/offset_filter/offset_filter.pro +++ /dev/null @@ -1,49 +0,0 @@ -QT += gui - -TEMPLATE = lib -DEFINES += CSV_EXPORTER_LIBRARY - -CONFIG += c++17 -CONFIG -= debug_and_release - -DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 - -INCLUDEPATH += \ - ../../src \ - ../../src/plugins - -HEADERS += \ - offset_filter_global.h \ - offset_filter.hpp \ - ../../src/plugins/plugin_base.hpp \ - ../../src/plugins/plugin_filter.hpp \ - -SOURCES += \ - offset_filter.cpp - -# Default rules for deployment. -unix { - target.path = /usr/lib -} - -# Specify output directory -CONFIG(debug, debug|release) { - CONFIG += debug - DESTDIR = build/debug - -} else { - CONFIG += release - DESTDIR = ../build/release -} - -RCC_DIR = $$DESDIR -MOC_DIR = $$DESTDIR/moc -OBJECTS_DIR = $$DESTDIR/objects - -#Set the location for the generated ui_xxxx.h files -UI_DIR = build/ui - -!isEmpty(target.path): INSTALLS += target - -DISTFILES += \ - offset_filter.json diff --git a/plugins/offset_scaler_filter/offset_scaler_filter.cpp b/plugins/offset_scaler_filter/offset_scaler_filter.cpp new file mode 100644 index 0000000..7e66f4a --- /dev/null +++ b/plugins/offset_scaler_filter/offset_scaler_filter.cpp @@ -0,0 +1,50 @@ +#include "offset_scaler_filter.hpp" + + +bool OffsetScalerFilter::beforeProcessStep() +{ + // TODO - set filter options + return true; +} + + +void OffsetScalerFilter::afterProcessStep() +{ + // TODO +} + + +void OffsetScalerFilter::cancelProcessing() +{ + // TODO + m_processing = false; +} + + +uint8_t OffsetScalerFilter::getProgress() const +{ + // TODO + return 0; +} + + +bool OffsetScalerFilter::setFilterInputs(QList inputs, QStringList &errors) +{ + bool result = FilterPlugin::setFilterInputs(inputs, errors); + + // TODO: Custom checks here? + return result; +} + + +bool OffsetScalerFilter::filterData(QStringList &errors) +{ + // TODO - Filter the data! + return true; +} + + +QList OffsetScalerFilter::getFilterOutputs(void) +{ + return m_outputs; +} diff --git a/plugins/offset_scaler_filter/offset_scaler_filter.hpp b/plugins/offset_scaler_filter/offset_scaler_filter.hpp new file mode 100644 index 0000000..a4d9f09 --- /dev/null +++ b/plugins/offset_scaler_filter/offset_scaler_filter.hpp @@ -0,0 +1,48 @@ +#ifndef LUMBERJACK_CSV_IMPORTER_HPP +#define LUMBERJACK_CSV_IMPORTER_HPP + +#include "offset_scaler_filter_global.h" +#include "plugin_filter.hpp" + + +/** + * @brief The ScaleOffsetFilter class provides simple scaling and offset functionality + */ +class CUSTOM_FILTER_EXPORT OffsetScalerFilter : public FilterPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID FilterInterface_iid) + Q_INTERFACES(FilterPlugin) +public: + virtual ~OffsetScalerFilter() = default; + + // PluginBase functionality + virtual QString pluginName(void) const override { return m_name; } + virtual QString pluginDescription(void) const override { return m_description; } + virtual QString pluginVersion(void) const override { return m_version; } + + // DataProcessingPlugin functionality + virtual bool beforeProcessStep(void) override; + virtual void afterProcessStep(void) override; + virtual void cancelProcessing(void) override; + virtual uint8_t getProgress(void) const override; + + // FilterPlugin functionality + virtual unsigned int getMinInputCount(void) const override { return 1; } + virtual unsigned int getMaxInputCount(void) const override { return 1; } + virtual bool setFilterInputs(QList inputs, QStringList &errors) override; + virtual bool filterData(QStringList &errors) override; + virtual QList getFilterOutputs(void) override; + +protected: + const QString m_name = "Offset / Scaler Filter"; + const QString m_description = "Apply custom offset and scaler to a dataset"; + const QString m_version = "0.1.0"; + + bool m_processing = false; + + QList m_outputs; + +}; + +#endif // LUMBERJACK_CSV_IMPORTER_HPP diff --git a/plugins/offset_scaler_filter/offset_scaler_filter.json b/plugins/offset_scaler_filter/offset_scaler_filter.json new file mode 100644 index 0000000..b2880ff --- /dev/null +++ b/plugins/offset_scaler_filter/offset_scaler_filter.json @@ -0,0 +1 @@ +{ "Keys": [ "offset_scaler_filter" ] } diff --git a/plugins/scaler_filter/scaler_filter.pro b/plugins/offset_scaler_filter/offset_scaler_filter.pro similarity index 82% rename from plugins/scaler_filter/scaler_filter.pro rename to plugins/offset_scaler_filter/offset_scaler_filter.pro index 47b156d..f41c9ab 100644 --- a/plugins/scaler_filter/scaler_filter.pro +++ b/plugins/offset_scaler_filter/offset_scaler_filter.pro @@ -13,13 +13,14 @@ INCLUDEPATH += \ ../../src/plugins HEADERS += \ - scaler_filter_global.h \ - scaler_filter.hpp \ ../../src/plugins/plugin_base.hpp \ ../../src/plugins/plugin_filter.hpp \ + offset_scaler_filter_global.h \ + offset_scaler_filter.hpp \ SOURCES += \ - scaler_filter.cpp + ../../src/plugins/plugin_filter.cpp \ + offset_scaler_filter.cpp # Default rules for deployment. unix { @@ -30,7 +31,6 @@ unix { CONFIG(debug, debug|release) { CONFIG += debug DESTDIR = build/debug - } else { CONFIG += release DESTDIR = ../build/release @@ -46,4 +46,4 @@ UI_DIR = build/ui !isEmpty(target.path): INSTALLS += target DISTFILES += \ - scaler_filter.json + offset_scaler_filter.json diff --git a/plugins/offset_filter/offset_filter.user b/plugins/offset_scaler_filter/offset_scaler_filter.user similarity index 100% rename from plugins/offset_filter/offset_filter.user rename to plugins/offset_scaler_filter/offset_scaler_filter.user diff --git a/plugins/offset_filter/offset_filter_global.h b/plugins/offset_scaler_filter/offset_scaler_filter_global.h similarity index 100% rename from plugins/offset_filter/offset_filter_global.h rename to plugins/offset_scaler_filter/offset_scaler_filter_global.h diff --git a/plugins/plugins.pro b/plugins/plugins.pro index 781bb1d..260de40 100644 --- a/plugins/plugins.pro +++ b/plugins/plugins.pro @@ -5,6 +5,5 @@ TEMPLATE = subdirs SUBDIRS += \ csv_importer \ csv_exporter \ - #offset_filter \ - #scaler_filter \ + offset_scaler_filter \ diff --git a/plugins/scaler_filter/.gitignore b/plugins/scaler_filter/.gitignore deleted file mode 100644 index b182c97..0000000 --- a/plugins/scaler_filter/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# Build directories -./build/ \ No newline at end of file diff --git a/plugins/scaler_filter/scaler_filter.cpp b/plugins/scaler_filter/scaler_filter.cpp deleted file mode 100644 index 9bde7c0..0000000 --- a/plugins/scaler_filter/scaler_filter.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include "scaler_filter.hpp" - - - -ScalerFilter::ScalerFilter() -{ - // TODO -} diff --git a/plugins/scaler_filter/scaler_filter.hpp b/plugins/scaler_filter/scaler_filter.hpp deleted file mode 100644 index c597da0..0000000 --- a/plugins/scaler_filter/scaler_filter.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef LUMBERJACK_CSV_IMPORTER_HPP -#define LUMBERJACK_CSV_IMPORTER_HPP - -#include "scaler_filter_global.h" -#include "plugin_filter.hpp" - - -/** - * @brief The ScaleOffsetFilter class provides simple scaling and offset functionality - */ -class CUSTOM_FILTER_EXPORT ScalerFilter : public FilterPlugin -{ - Q_OBJECT - Q_PLUGIN_METADATA(IID FilterInterface_iid) - Q_INTERFACES(FilterPlugin) -public: - ScalerFilter(); - - // Base plugin functionality - virtual QString pluginName(void) const override { return m_name; } - virtual QString pluginDescription(void) const override { return m_description; } - virtual QString pluginVersion(void) const override { return m_version; } - -protected: - const QString m_name = "Scaler Filter"; - const QString m_description = "Apply custom scaler to a dataset"; - const QString m_version = "0.1.0"; - -}; - -#endif // LUMBERJACK_CSV_IMPORTER_HPP diff --git a/plugins/scaler_filter/scaler_filter.json b/plugins/scaler_filter/scaler_filter.json deleted file mode 100644 index cda4cf5..0000000 --- a/plugins/scaler_filter/scaler_filter.json +++ /dev/null @@ -1 +0,0 @@ -{ "Keys": [ "scaler_filter" ] } diff --git a/plugins/scaler_filter/scaler_filter.user b/plugins/scaler_filter/scaler_filter.user deleted file mode 100644 index ec10eea..0000000 --- a/plugins/scaler_filter/scaler_filter.user +++ /dev/null @@ -1,227 +0,0 @@ - - - - - - EnvironmentId - {cc3e2f05-85bf-4229-a4de-54f19ed6baad} - - - ProjectExplorer.Project.ActiveTarget - 0 - - - ProjectExplorer.Project.EditorSettings - - true - false - true - - Cpp - - CppGlobal - - - - QmlJS - - QmlJSGlobal - - - 2 - UTF-8 - false - 4 - false - 0 - 80 - true - true - 1 - 0 - false - true - false - 2 - true - true - 0 - 8 - true - false - 1 - true - true - true - *.md, *.MD, Makefile - false - true - true - - - - ProjectExplorer.Project.PluginSettings - - - true - false - true - true - true - true - - false - - - 0 - true - - true - true - Builtin.DefaultTidyAndClazy - 6 - true - - - - true - - - - - ProjectExplorer.Project.Target.0 - - Desktop - Desktop Qt 6.8.1 MinGW 64-bit - Desktop Qt 6.8.1 MinGW 64-bit - qt.qt6.681.win64_mingw_kit - 0 - 0 - 0 - - 0 - C:\lumberjack\plugins\scale_offset_filter - C:/lumberjack/plugins/scale_offset_filter - - - true - QtProjectManager.QMakeBuildStep - false - - - - true - Qt4ProjectManager.MakeStep - - 2 - Build - Build - ProjectExplorer.BuildSteps.Build - - - - true - Qt4ProjectManager.MakeStep - clean - - 1 - Clean - Clean - ProjectExplorer.BuildSteps.Clean - - 2 - false - - false - - Debug - Qt4ProjectManager.Qt4BuildConfiguration - 2 - - - C:\lumberjack\plugins\scale_offset_filter\build\Desktop_Qt_6_8_1_MinGW_64_bit-Release - C:/lumberjack/plugins/scale_offset_filter/build/Desktop_Qt_6_8_1_MinGW_64_bit-Release - - - true - QtProjectManager.QMakeBuildStep - false - - - - true - Qt4ProjectManager.MakeStep - - 2 - Build - Build - ProjectExplorer.BuildSteps.Build - - - - true - Qt4ProjectManager.MakeStep - clean - - 1 - Clean - Clean - ProjectExplorer.BuildSteps.Clean - - 2 - false - - false - - Release - Qt4ProjectManager.Qt4BuildConfiguration - 0 - 0 - - 2 - - - 0 - Deploy - Deploy - ProjectExplorer.BuildSteps.Deploy - - 1 - - false - ProjectExplorer.DefaultDeployConfiguration - - 1 - - true - true - 0 - true - - 2 - - false - -e cpu-cycles --call-graph "dwarf,4096" -F 250 - - ProjectExplorer.CustomExecutableRunConfiguration - - false - true - true - - 1 - - - - ProjectExplorer.Project.TargetCount - 1 - - - ProjectExplorer.Project.Updater.FileVersion - 22 - - - Version - 22 - - diff --git a/plugins/scaler_filter/scaler_filter_global.h b/plugins/scaler_filter/scaler_filter_global.h deleted file mode 100644 index dea6354..0000000 --- a/plugins/scaler_filter/scaler_filter_global.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef CSV_EXPORTER_GLOBAL_H -#define CSV_EXPORTER_GLOBAL_H - -#include - -#if defined(CSV_EXPORTER_LIBRARY) -#define CUSTOM_FILTER_EXPORT Q_DECL_EXPORT -#else -#define CUSTOM_FILTER_EXPORT Q_DECL_IMPORT -#endif - -#endif // CSV_EXPORTER_GLOBAL_H From be8651aa7db20df07dc1b03320036f15a584635a Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Wed, 18 Dec 2024 23:15:30 +1100 Subject: [PATCH 05/16] Generic filter thing --- .../offset_scaler_filter.cpp | 49 ++++++++++++++++--- .../offset_scaler_filter.hpp | 7 ++- .../offset_scaler_filter.pro | 2 + 3 files changed, 49 insertions(+), 9 deletions(-) diff --git a/plugins/offset_scaler_filter/offset_scaler_filter.cpp b/plugins/offset_scaler_filter/offset_scaler_filter.cpp index 7e66f4a..1888d28 100644 --- a/plugins/offset_scaler_filter/offset_scaler_filter.cpp +++ b/plugins/offset_scaler_filter/offset_scaler_filter.cpp @@ -23,28 +23,63 @@ void OffsetScalerFilter::cancelProcessing() uint8_t OffsetScalerFilter::getProgress() const { - // TODO - return 0; + if (m_input.isNull() || m_output.isNull()) return 0; + if (m_input->size() == 0) return 0; + + float progress = (float) m_output->size() / (float) m_input->size(); + + return (uint8_t) (progress * 100); } bool OffsetScalerFilter::setFilterInputs(QList inputs, QStringList &errors) { - bool result = FilterPlugin::setFilterInputs(inputs, errors); + if (!FilterPlugin::setFilterInputs(inputs, errors)) + { + return false; + } + + m_input = inputs.first(); - // TODO: Custom checks here? - return result; + if (m_input.isNull()) + { + errors.append(tr("Null data series provided")); + return false; + } + + return true; } bool OffsetScalerFilter::filterData(QStringList &errors) { - // TODO - Filter the data! + QString label = m_input->getLabel() + " - Scale + Offset"; + m_output = DataSeriesPointer(new DataSeries(label)); + + uint64_t idx = 0; + + m_processing = true; + + while (m_processing && idx < m_input->size()) + { + DataPoint point = m_input->getDataPoint(idx); + + point.value *= m_scaler; + point.value += m_offset; + + m_output->addData(point); + } + + m_processing = false; return true; + } QList OffsetScalerFilter::getFilterOutputs(void) { - return m_outputs; + QList outputs; + + outputs.append(m_output); + return outputs; } diff --git a/plugins/offset_scaler_filter/offset_scaler_filter.hpp b/plugins/offset_scaler_filter/offset_scaler_filter.hpp index a4d9f09..878b932 100644 --- a/plugins/offset_scaler_filter/offset_scaler_filter.hpp +++ b/plugins/offset_scaler_filter/offset_scaler_filter.hpp @@ -39,10 +39,13 @@ class CUSTOM_FILTER_EXPORT OffsetScalerFilter : public FilterPlugin const QString m_description = "Apply custom offset and scaler to a dataset"; const QString m_version = "0.1.0"; - bool m_processing = false; + double m_scaler = 1.0; + double m_offset = 0.0; - QList m_outputs; + bool m_processing = false; + DataSeriesPointer m_input; + DataSeriesPointer m_output; }; #endif // LUMBERJACK_CSV_IMPORTER_HPP diff --git a/plugins/offset_scaler_filter/offset_scaler_filter.pro b/plugins/offset_scaler_filter/offset_scaler_filter.pro index f41c9ab..3c46844 100644 --- a/plugins/offset_scaler_filter/offset_scaler_filter.pro +++ b/plugins/offset_scaler_filter/offset_scaler_filter.pro @@ -13,12 +13,14 @@ INCLUDEPATH += \ ../../src/plugins HEADERS += \ + ../../src/data_series.hpp \ ../../src/plugins/plugin_base.hpp \ ../../src/plugins/plugin_filter.hpp \ offset_scaler_filter_global.h \ offset_scaler_filter.hpp \ SOURCES += \ + ../../src/data_series.cpp \ ../../src/plugins/plugin_filter.cpp \ offset_scaler_filter.cpp From 98a902d691e47d1e3157cce1d99e92cce36f3d3d Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Wed, 18 Dec 2024 23:17:33 +1100 Subject: [PATCH 06/16] Random numbers for fun... --- plugins/offset_scaler_filter/offset_scaler_filter.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/plugins/offset_scaler_filter/offset_scaler_filter.cpp b/plugins/offset_scaler_filter/offset_scaler_filter.cpp index 1888d28..3a2e48e 100644 --- a/plugins/offset_scaler_filter/offset_scaler_filter.cpp +++ b/plugins/offset_scaler_filter/offset_scaler_filter.cpp @@ -1,9 +1,17 @@ +#include + + #include "offset_scaler_filter.hpp" bool OffsetScalerFilter::beforeProcessStep() { - // TODO - set filter options + // TODO - set filter options manually + auto gen = QRandomGenerator(); + + m_scaler = (double) gen.bounded(-10, 10) / 10; + m_offset = (double) gen.bounded(-100, 100) / 10; + return true; } From ec3442dead9198f9716309403f8e930b9368a12d Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 19 Dec 2024 00:04:58 +1100 Subject: [PATCH 07/16] Refactoring --- .../csv_exporter/lumberjack_csv_exporter.cpp | 15 +- .../csv_exporter/lumberjack_csv_exporter.hpp | 2 +- .../csv_importer/lumberjack_csv_importer.cpp | 31 +-- .../csv_importer/lumberjack_csv_importer.hpp | 8 +- .../offset_scaler_filter.cpp | 8 +- .../offset_scaler_filter.hpp | 2 +- src/data_source_manager.cpp | 211 ++++++++---------- src/data_source_manager.hpp | 61 ++--- src/plugins/plugin_base.hpp | 3 + src/plugins/plugin_exporter.hpp | 10 +- src/plugins/plugin_filter.hpp | 3 - src/plugins/plugin_importer.hpp | 3 - src/widgets/dataview_tree.cpp | 12 + 13 files changed, 159 insertions(+), 210 deletions(-) diff --git a/plugins/csv_exporter/lumberjack_csv_exporter.cpp b/plugins/csv_exporter/lumberjack_csv_exporter.cpp index 98e3991..400f485 100644 --- a/plugins/csv_exporter/lumberjack_csv_exporter.cpp +++ b/plugins/csv_exporter/lumberjack_csv_exporter.cpp @@ -22,6 +22,7 @@ QStringList LumberjackCSVExporter::supportedFileTypes() const bool LumberjackCSVExporter::beforeProcessStep(void) { + // TODO: Set export options return true; } @@ -30,19 +31,13 @@ bool LumberjackCSVExporter::beforeProcessStep(void) /* * Export the provided series to a CSV file */ -bool LumberjackCSVExporter::exportData(QList &series, QStringList &errors) +bool LumberjackCSVExporter::processData() { - if (m_filename.isEmpty()) - { - errors.append(tr("Filename is empty")); - return false; - } - QFile outputFile(m_filename); if (!outputFile.open(QIODevice::WriteOnly) || !outputFile.isOpen() || !outputFile.isWritable()) { - errors.append(tr("Could not open file for writing")); + qCritical() << tr("Could not open file for writing"); return false; } @@ -50,7 +45,7 @@ bool LumberjackCSVExporter::exportData(QList &series, QString m_data.clear(); m_indices.clear(); - for (auto s : series) + for (auto s : m_series) { if (!s.isNull()) { @@ -75,7 +70,7 @@ bool LumberjackCSVExporter::exportData(QList &series, QString double tMin = LONG_MAX; double tMax = -LONG_MAX; - for (auto s : series) + for (auto s : m_series) { if (s.isNull()) continue; if (s->size() == 0) continue; diff --git a/plugins/csv_exporter/lumberjack_csv_exporter.hpp b/plugins/csv_exporter/lumberjack_csv_exporter.hpp index f2b8753..34775b4 100644 --- a/plugins/csv_exporter/lumberjack_csv_exporter.hpp +++ b/plugins/csv_exporter/lumberjack_csv_exporter.hpp @@ -21,7 +21,7 @@ class CSV_EXPORTER_EXPORT LumberjackCSVExporter : public ExportPlugin // Exporter plugin functionality virtual QStringList supportedFileTypes(void) const override; - virtual bool exportData(QList &series, QStringList &errors) override; + virtual bool processData(void) override; virtual bool beforeProcessStep(void) override; virtual void cancelProcessing(void) override; diff --git a/plugins/csv_importer/lumberjack_csv_importer.cpp b/plugins/csv_importer/lumberjack_csv_importer.cpp index 38afea5..1bf02df 100644 --- a/plugins/csv_importer/lumberjack_csv_importer.cpp +++ b/plugins/csv_importer/lumberjack_csv_importer.cpp @@ -47,12 +47,12 @@ bool LumberjackCSVImporter::beforeProcessStep(void) /** - * @brief LumberjackCSVImporter::loadDataFile + * @brief LumberjackCSVImporter::processData * @param filename - The filename to load * @param errors - * @return */ -bool LumberjackCSVImporter::importData(QStringList &errors) +bool LumberjackCSVImporter::processData(void) { // Reset importer to initial conditions m_headers.clear(); @@ -64,7 +64,7 @@ bool LumberjackCSVImporter::importData(QStringList &errors) if (!fi.exists() || !fi.isFile()) { - errors.append(tr("File does not exist")); + qCritical() << tr("File does not exist"); return false; } @@ -75,7 +75,7 @@ bool LumberjackCSVImporter::importData(QStringList &errors) if (!m_file->open(QIODevice::ReadOnly) || !m_file->isOpen() || !m_file->isReadable()) { - errors.append(tr("Could not open file for reading")); + qCritical() << tr("Could not open file for reading"); m_file->close(); return false; } @@ -107,7 +107,7 @@ bool LumberjackCSVImporter::importData(QStringList &errors) row = line.split(delimiter); - if (!processRow(lineCount, row, errors)) + if (!processRow(lineCount, row)) { badLineCount++; } @@ -124,7 +124,7 @@ bool LumberjackCSVImporter::importData(QStringList &errors) if (badLineCount > 0) { - errors.append(QString("Lines with errors: " + QString::number(badLineCount))); + qWarning() << QString("Lines with errors: " + QString::number(badLineCount)); } return true; @@ -138,15 +138,12 @@ bool LumberjackCSVImporter::importData(QStringList &errors) * @param errors * @return */ -bool LumberjackCSVImporter::processRow(int rowIndex, const QStringList &row, QStringList& errors) +bool LumberjackCSVImporter::processRow(int rowIndex, const QStringList &row) { - // TODO: error messages - Q_UNUSED(errors) - if (rowIndex == m_options.rowHeaders) { - return extractHeaders(rowIndex, row, errors); + return extractHeaders(rowIndex, row); } else if (rowIndex == m_options.rowUnits) { @@ -156,7 +153,7 @@ bool LumberjackCSVImporter::processRow(int rowIndex, const QStringList &row, QSt } else if (rowIndex >= m_options.rowDataStart) { - return extractData(rowIndex, row, errors); + return extractData(rowIndex, row); } } @@ -168,13 +165,10 @@ bool LumberjackCSVImporter::processRow(int rowIndex, const QStringList &row, QSt * @param errors * @return */ -bool LumberjackCSVImporter::extractHeaders(int rowIndex, const QStringList &row, QStringList &errors) +bool LumberjackCSVImporter::extractHeaders(int rowIndex, const QStringList &row) { Q_UNUSED(rowIndex); - // TODO: Error messages - Q_UNUSED(errors); - m_headers.clear(); for (int ii = 0; ii < row.length(); ii++) @@ -219,11 +213,8 @@ bool LumberjackCSVImporter::extractHeaders(int rowIndex, const QStringList &row, * @param errors * @return */ -bool LumberjackCSVImporter::extractData(int rowIndex, const QStringList &row, QStringList &errors) +bool LumberjackCSVImporter::extractData(int rowIndex, const QStringList &row) { - // TODO: Error messages - Q_UNUSED(errors) - double timestamp = 0; QString text; diff --git a/plugins/csv_importer/lumberjack_csv_importer.hpp b/plugins/csv_importer/lumberjack_csv_importer.hpp index f546016..916c54d 100644 --- a/plugins/csv_importer/lumberjack_csv_importer.hpp +++ b/plugins/csv_importer/lumberjack_csv_importer.hpp @@ -25,7 +25,7 @@ class CSV_IMPORTER_EXPORT LumberjackCSVImporter : public ImportPlugin // Importer plugin functionality virtual QStringList supportedFileTypes(void) const override; - virtual bool importData(QStringList &errors) override; + virtual bool processData(void) override; virtual bool beforeProcessStep(void) override; virtual void afterProcessStep(void) override; @@ -47,9 +47,9 @@ class CSV_IMPORTER_EXPORT LumberjackCSVImporter : public ImportPlugin QStringList m_headers; //! Internal functions for processing data - bool processRow(int rowIndex, const QStringList &row, QStringList &errors); - bool extractHeaders(int rowIndex, const QStringList &row, QStringList &errors); - bool extractData(int rowIndex, const QStringList &row, QStringList &errors); + bool processRow(int rowIndex, const QStringList &row); + bool extractHeaders(int rowIndex, const QStringList &row); + bool extractData(int rowIndex, const QStringList &row); bool extractTimestamp(int rowIndex, const QStringList &row, double ×tamp); // Keep track of data columns while loading diff --git a/plugins/offset_scaler_filter/offset_scaler_filter.cpp b/plugins/offset_scaler_filter/offset_scaler_filter.cpp index 3a2e48e..05ed22c 100644 --- a/plugins/offset_scaler_filter/offset_scaler_filter.cpp +++ b/plugins/offset_scaler_filter/offset_scaler_filter.cpp @@ -59,7 +59,7 @@ bool OffsetScalerFilter::setFilterInputs(QList inputs, QStrin } -bool OffsetScalerFilter::filterData(QStringList &errors) +bool OffsetScalerFilter::processData() { QString label = m_input->getLabel() + " - Scale + Offset"; m_output = DataSeriesPointer(new DataSeries(label)); @@ -68,6 +68,8 @@ bool OffsetScalerFilter::filterData(QStringList &errors) m_processing = true; + qDebug() << "filtering data"; + while (m_processing && idx < m_input->size()) { DataPoint point = m_input->getDataPoint(idx); @@ -76,8 +78,12 @@ bool OffsetScalerFilter::filterData(QStringList &errors) point.value += m_offset; m_output->addData(point); + + idx++; } + qDebug() << "Done:" << m_input->size() << m_output->size(); + m_processing = false; return true; diff --git a/plugins/offset_scaler_filter/offset_scaler_filter.hpp b/plugins/offset_scaler_filter/offset_scaler_filter.hpp index 878b932..992c49c 100644 --- a/plugins/offset_scaler_filter/offset_scaler_filter.hpp +++ b/plugins/offset_scaler_filter/offset_scaler_filter.hpp @@ -31,7 +31,7 @@ class CUSTOM_FILTER_EXPORT OffsetScalerFilter : public FilterPlugin virtual unsigned int getMinInputCount(void) const override { return 1; } virtual unsigned int getMaxInputCount(void) const override { return 1; } virtual bool setFilterInputs(QList inputs, QStringList &errors) override; - virtual bool filterData(QStringList &errors) override; + virtual bool processData(void) override; virtual QList getFilterOutputs(void) override; protected: diff --git a/src/data_source_manager.cpp b/src/data_source_manager.cpp index db47fa7..dcfb767 100644 --- a/src/data_source_manager.cpp +++ b/src/data_source_manager.cpp @@ -11,18 +11,16 @@ -DataImportWorker::DataImportWorker(QSharedPointer plugin) : m_plugin(plugin) +DataProcessWorker::DataProcessWorker(QSharedPointer plugin) : m_plugin(plugin) { } -void DataImportWorker::runImport() +void DataProcessWorker::run() { - m_errors.clear(); - - if (m_plugin) + if (m_plugin && !m_plugin.isNull()) { - m_result = m_plugin->importData(m_errors); + m_result = m_plugin->processData(); } else { @@ -31,49 +29,11 @@ void DataImportWorker::runImport() m_complete = true; - emit importCompleted(); -} - - -void DataImportWorker::cancelImport() -{ - if (m_plugin) - { - m_plugin->cancelProcessing(); - m_result = false; - } - - m_errors.append(tr("Import process cancelled")); - - m_complete = true; -} - - -DataExportWorker::DataExportWorker(QSharedPointer plugin, QList &series) - : m_plugin(plugin), m_series(series) -{ -} - - -void DataExportWorker::runExport() -{ - m_errors.clear(); - - if (m_plugin) - { - m_result = m_plugin->exportData(m_series, m_errors); - } - else - { - m_result = false; - } - - m_complete = true; - emit exportCompleted(); + emit processingComplete(); } -void DataExportWorker::cancelExport() +void DataProcessWorker::cancel() { if (m_plugin) { @@ -81,8 +41,6 @@ void DataExportWorker::cancelExport() m_result = false; } - m_errors.append(tr("Export process cancelled")); - m_complete = true; } @@ -357,57 +315,13 @@ bool DataSourceManager::importData(QString filename) return false; } - QProgressDialog progress; - - progress.setWindowTitle(tr("Importing Data")); - progress.setMinimum(0); - progress.setMaximum(100); - progress.setValue(0); - progress.setLabelText(tr("Importing data from file")); - - progress.show(); - - QApplication::processEvents(); - - // Spawn a new thread for importing - auto worker = DataImportWorker(importer); - auto *thread = new QThread; - - worker.moveToThread(thread); - - connect(&worker, &DataImportWorker::importCompleted, thread, &QThread::quit); - connect(thread, &QThread::started, &worker, &DataImportWorker::runImport); - connect(thread, &QThread::finished, thread, &QThread::deleteLater); - - thread->start(); - - qDebug() << "Importing data from" << filename; - - while (!thread->isFinished() && !worker.isComplete()) - { - // Check for manual cancel of import process - if (progress.wasCanceled()) - { - worker.cancelImport(); - thread->wait(); - } - - progress.setValue(importer->getProgress()); - - QApplication::processEvents(); - QThread::msleep(100); - } - - progress.cancel(); - progress.close(); + bool result = runDataProcess( + importer, + tr("Importing Data"), + tr("Importing data from file") + ); - for (QString err : worker.getErrors()) - { - // TODO: Display these better? - qWarning() << "Import err:" << err; - } - - if (worker.getResult()) + if (result) { // Create a new instance of the provided importer DataSource *source = new DataSource( @@ -495,6 +409,7 @@ bool DataSourceManager::exportData(QList &series, QString fil } exporter->setFilename(filename); + exporter->setDataSeries(series); if (!exporter->beforeProcessStep()) { @@ -502,41 +417,107 @@ bool DataSourceManager::exportData(QList &series, QString fil return false; } + bool result = runDataProcess( + exporter, + tr("Exporting Data"), + tr("Exporting data to file") + ); + + return result; +} + + +bool DataSourceManager::filterData(QList &series) +{ + auto registry = PluginRegistry::getInstance(); + // TODO : Ensure correct plugin is selected + + // TODO : Handle selection of *other* data series (if required) + auto plugins = registry->FilterPlugins(); + + if (plugins.count() == 0) + { + return false; + } + + // TODO: Hack for now + auto plugin = plugins.first(); + + QStringList errors; + + if (!plugin->setFilterInputs(series, errors)) + { + // TODO: Error messages? + + return false; + } + + if (!plugin->beforeProcessStep()) + { + return false; + } + + bool result = runDataProcess( + plugin, + tr("Filtering Data"), + tr("Applying custom data filter") + ); + + if (result) + { + DataSourcePointer src = getSourceByLabel("internal"); + + if (src.isNull()) + { + src = DataSourcePointer(new DataSource("internal", "internal")); + addSource(src); + } + + for (auto output : plugin->getFilterOutputs()) + { + src->addSeries(output); + } + } + + return result; +} + + +bool DataSourceManager::runDataProcess(QSharedPointer plugin, QString title, QString message) +{ QProgressDialog progress; - progress.setWindowTitle(tr("Importing Data")); + progress.setWindowTitle(title); + progress.setLabelText(message); + + progress.setValue(0); progress.setMinimum(0); progress.setMaximum(100); - progress.setValue(0); - progress.setLabelText(tr("Importing data from file")); progress.show(); QApplication::processEvents(); - // Spawn a new thread for data export - auto worker = DataExportWorker(exporter, series); + // Spawn a new thread for data processing + auto worker = DataProcessWorker(plugin); auto *thread = new QThread(); worker.moveToThread(thread); - connect(&worker, &DataExportWorker::exportCompleted, thread, &QThread::quit); - connect(thread, &QThread::started, &worker, &DataExportWorker::runExport); + connect(&worker, &DataProcessWorker::processingComplete, thread, &QThread::quit); + connect(thread, &QThread::started, &worker, &DataProcessWorker::run); connect(thread, &QThread::finished, thread, &QThread::deleteLater); thread->start(); - qDebug() << "Exporting data to" << filename; - while (!thread->isFinished() && !worker.isComplete()) { if (progress.wasCanceled()) { - worker.cancelExport(); - thread->wait(); + worker.cancel(); } - progress.setValue(exporter->getProgress()); + progress.setValue(plugin->getProgress()); QApplication::processEvents(); QThread::msleep(100); @@ -545,13 +526,5 @@ bool DataSourceManager::exportData(QList &series, QString fil progress.cancel(); progress.close(); - for (QString err : worker.getErrors()) - { - // TODO: Display these better? - qWarning() << "Export err:" << err; - } - - bool result = worker.getResult(); - - return result; + return worker.getResult(); } diff --git a/src/data_source_manager.hpp b/src/data_source_manager.hpp index 7cb8ec0..05a9bb1 100644 --- a/src/data_source_manager.hpp +++ b/src/data_source_manager.hpp @@ -3,66 +3,36 @@ #include +#include "plugin_base.hpp" #include "data_source.hpp" -#include "plugin_importer.hpp" -#include "plugin_exporter.hpp" - - -class DataIOWorker : public QObject -{ -public: - bool getResult(void) const { return m_result; } - QStringList getErrors(void) const { return m_errors; } - bool isComplete(void) const { return m_complete; } -protected: - QStringList m_errors; - bool m_complete = false; - bool m_result = false; -}; - /** - * @brief The DataImportWorker class manages a data import session + * @brief The DataProcessWorker class is a simple worker class for managing data processing */ -class DataImportWorker : public DataIOWorker +class DataProcessWorker : public QObject { Q_OBJECT - public: - DataImportWorker(QSharedPointer plugin); - -public slots: - void runImport(void); - void cancelImport(void); - -signals: - void importCompleted(void); + DataProcessWorker(QSharedPointer plugin); -protected: - QSharedPointer m_plugin; -}; - - -class DataExportWorker : public DataIOWorker -{ - Q_OBJECT - -public: - DataExportWorker(QSharedPointer plugin, QList &series); + bool getResult(void) const { return m_result; } + bool isComplete(void) const { return m_complete; } public slots: - void runExport(void); - void cancelExport(void); + void run(); + void cancel(); signals: - void exportCompleted(void); + void processingComplete(); protected: - QSharedPointer m_plugin; - QList m_series; + QSharedPointer m_plugin; + bool m_complete = false; + bool m_result = false; }; + /* * Data source manager class: * - Manages all data sources @@ -125,6 +95,9 @@ public slots: // Data export functionality bool exportData(QList &series, QString filename = QString()); + // Data filtering functionality + bool filterData(QList &series); + void update(void) { emit sourcesChanged(); } signals: @@ -135,6 +108,8 @@ protected slots: protected: QVector sources; + + bool runDataProcess(QSharedPointer plugin, QString title, QString message); }; diff --git a/src/plugins/plugin_base.hpp b/src/plugins/plugin_base.hpp index 96acb25..5535b58 100644 --- a/src/plugins/plugin_base.hpp +++ b/src/plugins/plugin_base.hpp @@ -41,6 +41,9 @@ class DataProcessingPlugin : public PluginBase // Return False to cancel the process before it begins virtual bool beforeProcessStep(void) { return true; } + // Run the actual data processing + virtual bool processData(void) = 0; + // Optional function which is called after running the data processing step virtual void afterProcessStep(void) {} diff --git a/src/plugins/plugin_exporter.hpp b/src/plugins/plugin_exporter.hpp index 70ac7fd..8f51230 100644 --- a/src/plugins/plugin_exporter.hpp +++ b/src/plugins/plugin_exporter.hpp @@ -18,10 +18,7 @@ class ExportPlugin : public DataProcessingPlugin virtual ~ExportPlugin() = default; // Return a list of the support file types e.g. ['csv', 'tsv'] - virtual QStringList supportedFileTypes(void) const = 0; - - // Export data to the provided filename - virtual bool exportData(QList &series, QStringList &errors) = 0; + virtual QStringList supportedFileTypes(void) const = 0; virtual QString pluginIID(void) const override { @@ -32,12 +29,15 @@ class ExportPlugin : public DataProcessingPlugin bool supportsFileType(QString fileType) const; - void setFilename(QString filename) { m_filename = filename; } + virtual void setFilename(QString filename) { m_filename = filename; } + virtual void setDataSeries(QList seriesList) { m_series = seriesList; } QString getFilename(void) const { return m_filename; } protected: // Stored filename, destination of exported data QString m_filename; + + QList m_series; }; typedef QList> ExportPluginList; diff --git a/src/plugins/plugin_filter.hpp b/src/plugins/plugin_filter.hpp index b0848e3..0b691fe 100644 --- a/src/plugins/plugin_filter.hpp +++ b/src/plugins/plugin_filter.hpp @@ -34,9 +34,6 @@ class FilterPlugin : public DataProcessingPlugin // Set the filter inputs virtual bool setFilterInputs(QList inputs, QStringList &errors); - // Perform filtering operations on the provided set of inputs - virtual bool filterData(QStringList &errors) = 0; - // Return the generated data series output(s) virtual QList getFilterOutputs(void) = 0; diff --git a/src/plugins/plugin_importer.hpp b/src/plugins/plugin_importer.hpp index d3a2200..ead9bbb 100644 --- a/src/plugins/plugin_importer.hpp +++ b/src/plugins/plugin_importer.hpp @@ -27,9 +27,6 @@ class ImportPlugin : public DataProcessingPlugin // but this can be extended by the plugin virtual bool validateFile(QString filename, QStringList &errors) const; - // Load data from the provided filename - virtual bool importData(QStringList &errors) = 0; - // After import, plugin must return a list of DataSeries objects virtual QList getDataSeries(void) const = 0; diff --git a/src/widgets/dataview_tree.cpp b/src/widgets/dataview_tree.cpp index 7f2890f..7cd535e 100644 --- a/src/widgets/dataview_tree.cpp +++ b/src/widgets/dataview_tree.cpp @@ -4,6 +4,7 @@ #include #include +#include "plugin_registry.hpp" #include "datatable_widget.hpp" #include "series_editor_dialog.hpp" #include "dataview_tree.hpp" @@ -129,6 +130,8 @@ void DataViewTree::onContextMenu(const QPoint &pos) // Edit series QAction *editSeries = new QAction(tr("Edit Series"), &menu); + QAction *filterSeries = new QAction(tr("Apply Filter"), &menu); + // View data QAction *viewSeriesData = new QAction(tr("View Data"), &menu); @@ -138,6 +141,7 @@ void DataViewTree::onContextMenu(const QPoint &pos) menu.addAction(exportSeries); menu.addSeparator(); menu.addAction(editSeries); + menu.addAction(filterSeries); menu.addAction(viewSeriesData); menu.addSeparator(); menu.addAction(deleteSeries); @@ -155,6 +159,14 @@ void DataViewTree::onContextMenu(const QPoint &pos) { editDataSeries(series); } + else if (action == filterSeries) + { + QList seriesList; + seriesList << series; + + auto manager = DataSourceManager::getInstance(); + manager->filterData(seriesList); + } else if (action == viewSeriesData) { DataSeriesTableView *table = new DataSeriesTableView(series); From 1f8d2d8eb56bc3538ab5770b0eafd9550d92c1b0 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 19 Dec 2024 00:21:41 +1100 Subject: [PATCH 08/16] Adds a bitmasking filter plugin concept --- plugins/bitmask_filter/bitmask_filter.cpp | 100 ++++++++++++++++++ plugins/bitmask_filter/bitmask_filter.hpp | 57 ++++++++++ plugins/bitmask_filter/bitmask_filter.json | 1 + plugins/bitmask_filter/bitmask_filter.pro | 51 +++++++++ .../bitmask_filter/bitmask_filter_global.h | 12 +++ 5 files changed, 221 insertions(+) create mode 100644 plugins/bitmask_filter/bitmask_filter.cpp create mode 100644 plugins/bitmask_filter/bitmask_filter.hpp create mode 100644 plugins/bitmask_filter/bitmask_filter.json create mode 100644 plugins/bitmask_filter/bitmask_filter.pro create mode 100644 plugins/bitmask_filter/bitmask_filter_global.h diff --git a/plugins/bitmask_filter/bitmask_filter.cpp b/plugins/bitmask_filter/bitmask_filter.cpp new file mode 100644 index 0000000..fd19bac --- /dev/null +++ b/plugins/bitmask_filter/bitmask_filter.cpp @@ -0,0 +1,100 @@ +#include + + +#include "bitmask_filter.hpp" + + +bool BitmaskFilter::beforeProcessStep() +{ + // TODO - set filter options manually + auto gen = QRandomGenerator::securelySeeded(); + + // Generate a random mask + m_mask = gen.bounded(0xFFFFFFFF); + + return true; +} + + +void BitmaskFilter::afterProcessStep() +{ + // TODO +} + + +void BitmaskFilter::cancelProcessing() +{ + // TODO + m_processing = false; +} + + +uint8_t BitmaskFilter::getProgress() const +{ + if (m_input.isNull() || m_output.isNull()) return 0; + if (m_input->size() == 0) return 0; + + float progress = (float) m_output->size() / (float) m_input->size(); + + return (uint8_t) (progress * 100); +} + + +bool BitmaskFilter::setFilterInputs(QList inputs, QStringList &errors) +{ + if (!FilterPlugin::setFilterInputs(inputs, errors)) + { + return false; + } + + m_input = inputs.first(); + + if (m_input.isNull()) + { + errors.append(tr("Null data series provided")); + return false; + } + + return true; +} + + +bool BitmaskFilter::processData() +{ + QString label = m_input->getLabel() + " - Bitmask"; + m_output = DataSeriesPointer(new DataSeries(label)); + + uint64_t idx = 0; + + m_processing = true; + + while (m_processing && idx < m_input->size()) + { + DataPoint point = m_input->getDataPoint(idx); + + // TODO: Support negative values? + uint32_t value = point.value > 0 ? (uint32_t) point.value : 0; + + // Hack for now, apply a "random" AND mask + value &= m_mask; + + point.value = (double) value; + + m_output->addData(point); + + idx++; + } + + m_processing = false; + return true; + +} + + +QList BitmaskFilter::getFilterOutputs(void) +{ + QList outputs; + + outputs.append(m_output); + return outputs; +} diff --git a/plugins/bitmask_filter/bitmask_filter.hpp b/plugins/bitmask_filter/bitmask_filter.hpp new file mode 100644 index 0000000..ab5eb9a --- /dev/null +++ b/plugins/bitmask_filter/bitmask_filter.hpp @@ -0,0 +1,57 @@ +#ifndef LUMBERJACK_CSV_IMPORTER_HPP +#define LUMBERJACK_CSV_IMPORTER_HPP + +#include "bitmask_filter_global.h" +#include "plugin_filter.hpp" + + +/** + * TODO: + * - Apply different bitmask operations (XOR / AND / OR) + * - Specify bitmask + */ + + +/** + * @brief The ScaleOffsetFilter class provides simple scaling and offset functionality + */ +class CUSTOM_FILTER_EXPORT BitmaskFilter : public FilterPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID FilterInterface_iid) + Q_INTERFACES(FilterPlugin) +public: + virtual ~BitmaskFilter() = default; + + // PluginBase functionality + virtual QString pluginName(void) const override { return m_name; } + virtual QString pluginDescription(void) const override { return m_description; } + virtual QString pluginVersion(void) const override { return m_version; } + + // DataProcessingPlugin functionality + virtual bool beforeProcessStep(void) override; + virtual void afterProcessStep(void) override; + virtual void cancelProcessing(void) override; + virtual uint8_t getProgress(void) const override; + + // FilterPlugin functionality + virtual unsigned int getMinInputCount(void) const override { return 1; } + virtual unsigned int getMaxInputCount(void) const override { return 1; } + virtual bool setFilterInputs(QList inputs, QStringList &errors) override; + virtual bool processData(void) override; + virtual QList getFilterOutputs(void) override; + +protected: + const QString m_name = "Bitmask Filter"; + const QString m_description = "Apply custom bitmasking patterns"; + const QString m_version = "0.1.0"; + + uint32_t m_mask = 0x01234567; + + bool m_processing = false; + + DataSeriesPointer m_input; + DataSeriesPointer m_output; +}; + +#endif // LUMBERJACK_CSV_IMPORTER_HPP diff --git a/plugins/bitmask_filter/bitmask_filter.json b/plugins/bitmask_filter/bitmask_filter.json new file mode 100644 index 0000000..5119d14 --- /dev/null +++ b/plugins/bitmask_filter/bitmask_filter.json @@ -0,0 +1 @@ +{ "Keys": [ "bitmask_filter" ] } diff --git a/plugins/bitmask_filter/bitmask_filter.pro b/plugins/bitmask_filter/bitmask_filter.pro new file mode 100644 index 0000000..5f9481d --- /dev/null +++ b/plugins/bitmask_filter/bitmask_filter.pro @@ -0,0 +1,51 @@ +QT += gui + +TEMPLATE = lib +DEFINES += CSV_EXPORTER_LIBRARY + +CONFIG += c++17 +CONFIG -= debug_and_release + +DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +INCLUDEPATH += \ + ../../src \ + ../../src/plugins + +HEADERS += \ + ../../src/data_series.hpp \ + ../../src/plugins/plugin_base.hpp \ + ../../src/plugins/plugin_filter.hpp \ + bitmask_filter_global.h \ + bitmask_filter.hpp \ + +SOURCES += \ + ../../src/data_series.cpp \ + ../../src/plugins/plugin_filter.cpp \ + bitmask_filter.cpp + +# Default rules for deployment. +unix { + target.path = /usr/lib +} + +# Specify output directory +CONFIG(debug, debug|release) { + CONFIG += debug + DESTDIR = build/debug +} else { + CONFIG += release + DESTDIR = ../build/release +} + +RCC_DIR = $$DESDIR +MOC_DIR = $$DESTDIR/moc +OBJECTS_DIR = $$DESTDIR/objects + +#Set the location for the generated ui_xxxx.h files +UI_DIR = build/ui + +!isEmpty(target.path): INSTALLS += target + +DISTFILES += \ + bitmask_filter.json diff --git a/plugins/bitmask_filter/bitmask_filter_global.h b/plugins/bitmask_filter/bitmask_filter_global.h new file mode 100644 index 0000000..dea6354 --- /dev/null +++ b/plugins/bitmask_filter/bitmask_filter_global.h @@ -0,0 +1,12 @@ +#ifndef CSV_EXPORTER_GLOBAL_H +#define CSV_EXPORTER_GLOBAL_H + +#include + +#if defined(CSV_EXPORTER_LIBRARY) +#define CUSTOM_FILTER_EXPORT Q_DECL_EXPORT +#else +#define CUSTOM_FILTER_EXPORT Q_DECL_IMPORT +#endif + +#endif // CSV_EXPORTER_GLOBAL_H From c05ae9ec5c34598e416220d40635ef58db6d88a9 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 23 Dec 2024 09:23:28 +1100 Subject: [PATCH 09/16] Include plugin --- plugins/plugins.pro | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/plugins.pro b/plugins/plugins.pro index 260de40..73e4956 100644 --- a/plugins/plugins.pro +++ b/plugins/plugins.pro @@ -5,5 +5,6 @@ TEMPLATE = subdirs SUBDIRS += \ csv_importer \ csv_exporter \ + bitmask_filter \ offset_scaler_filter \ From ae96a3289e602b49b1575d7ee0b5a217a0545837 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 24 Dec 2024 12:27:52 +1100 Subject: [PATCH 10/16] Add placeholder "filters" widget --- lumberjack.pro | 5 ++++- src/mainwindow.cpp | 32 ++++++++++++++++++++++++++++++++ src/mainwindow.h | 3 +++ src/widgets/dataview_widget.hpp | 5 ----- src/widgets/filters_widget.cpp | 7 +++++++ src/widgets/filters_widget.hpp | 24 ++++++++++++++++++++++++ ui/filters_widget.ui | 19 +++++++++++++++++++ ui/mainwindow.ui | 8 +++++++- 8 files changed, 96 insertions(+), 7 deletions(-) create mode 100644 src/widgets/filters_widget.cpp create mode 100644 src/widgets/filters_widget.hpp create mode 100644 ui/filters_widget.ui diff --git a/lumberjack.pro b/lumberjack.pro index 06427f8..fe3826d 100644 --- a/lumberjack.pro +++ b/lumberjack.pro @@ -56,6 +56,7 @@ SOURCES += \ src/widgets/dataview_tree.cpp \ src/widgets/dataview_widget.cpp \ src/widgets/debug_widget.cpp \ + src/widgets/filters_widget.cpp \ src/widgets/plot_sampler.cpp \ src/widgets/plugins_dialog.cpp \ src/widgets/series_editor_dialog.cpp \ @@ -88,11 +89,12 @@ HEADERS += \ src/widgets/dataview_tree.hpp \ src/widgets/dataview_widget.hpp \ src/widgets/debug_widget.hpp \ + src/widgets/filters_widget.hpp \ src/widgets/plot_sampler.hpp \ src/widgets/plugins_dialog.hpp \ src/widgets/series_editor_dialog.hpp \ src/widgets/stats_widget.hpp \ - src/widgets/timeline_widget.hpp \ + src/widgets/timeline_widget.hpp # simple-fft includes HEADERS += \ @@ -110,6 +112,7 @@ FORMS += \ ui/curve_editor_dialog.ui \ ui/dataview_widget.ui \ ui/debug_widget.ui \ + ui/filters_widget.ui \ ui/mainwindow.ui \ ui/plugins_dialog.ui \ ui/stats_view.ui diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 4d2a652..8b08b7e 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -169,6 +169,7 @@ void MainWindow::saveWorkspaceSettings() auto *settings = LumberjackSettings::getInstance(); settings->saveSetting("mainwindow", "showDataView", dataView.isVisible()); + settings->saveSetting("mainwindow", "showFiltersView", filtersView.isVisible()); settings->saveSetting("mainwindow", "showTimelineView", timelineView.isVisible()); settings->saveSetting("mainwindow", "showStatsView", statsView.isVisible()); settings->saveSetting("mainwindow", "showDebugView", debugWidget.isVisible()); @@ -190,6 +191,7 @@ void MainWindow::initMenus() // View menu connect(ui->action_Data_View, &QAction::triggered, this, &MainWindow::toggleDataView); + connect(ui->action_Filters, &QAction::triggered, this, &MainWindow::toggleFiltersView); connect(ui->action_Timeline, &QAction::triggered, this, &MainWindow::toggleTimelineView); connect(ui->action_Statistics, &QAction::triggered, this, &MainWindow::toggleStatisticsView); connect(ui->action_FFT, &QAction::triggered, this, &MainWindow::toggleFftView); @@ -223,6 +225,11 @@ void MainWindow::initDocks() toggleDataView(); } + if (settings->loadBoolean("mainwindow", "showFiltersView")) + { + toggleFiltersView(); + } + if (settings->loadBoolean("mainwindow", "showTimelineView")) { toggleTimelineView(); @@ -500,6 +507,31 @@ void MainWindow::toggleFftView(void) } } +/** + * @brief MainWindow::toggleFiltersView toggles visibility of the "filters view" dock + */ +void MainWindow::toggleFiltersView(void) +{ + ui->action_Filters->setCheckable(true); + + if (filtersView.isVisible()) + { + hideDockedWidget(&filtersView); + ui->action_Filters->setChecked(false); + } + else + { + QDockWidget* dock = new QDockWidget(tr("Filters View"), this); + dock->setObjectName("filters-view"); + dock->setAllowedAreas(Qt::AllDockWidgetAreas); + dock->setWidget(&filtersView); + + addDockWidget(Qt::LeftDockWidgetArea, dock); + + ui->action_Filters->setChecked(true); + } +} + /** * @brief MainWindow::toggleDataView toggles visibility of the "data view" dock diff --git a/src/mainwindow.h b/src/mainwindow.h index 40a7abb..e83b9fe 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -11,6 +11,7 @@ #include "fft_widget.hpp" #include "stats_widget.hpp" #include "dataview_widget.hpp" +#include "filters_widget.hpp" #include "timeline_widget.hpp" QT_BEGIN_NAMESPACE @@ -53,6 +54,7 @@ protected slots: void toggleDebugView(void); void toggleDataView(void); + void toggleFiltersView(void); void toggleFftView(void); void toggleTimelineView(void); void toggleStatisticsView(void); @@ -77,6 +79,7 @@ protected slots: StatsWidget statsView; TimelineWidget timelineView; FFTWidget fftView; + FiltersWidget filtersView; DebugWidget debugWidget; }; diff --git a/src/widgets/dataview_widget.hpp b/src/widgets/dataview_widget.hpp index ffc91e6..e1dcea9 100644 --- a/src/widgets/dataview_widget.hpp +++ b/src/widgets/dataview_widget.hpp @@ -3,8 +3,6 @@ #include -#include "data_source.hpp" - #include "ui_dataview_widget.h" class DataviewWidget : public QWidget @@ -30,9 +28,6 @@ public slots: virtual void dropEvent(QDropEvent *event) override; virtual void dragEnterEvent(QDragEnterEvent *event) override; - -// virtual void dragMoveEvent(QDragMoveEvent *event) override; -// virtual void dragLeaveEvent(QDragLeaveEvent *event) override; }; diff --git a/src/widgets/filters_widget.cpp b/src/widgets/filters_widget.cpp new file mode 100644 index 0000000..4daf11f --- /dev/null +++ b/src/widgets/filters_widget.cpp @@ -0,0 +1,7 @@ +#include "filters_widget.hpp" + + +FiltersWidget::FiltersWidget(QWidget *parent) : QWidget(parent) +{ + ui.setupUi(this); +} diff --git a/src/widgets/filters_widget.hpp b/src/widgets/filters_widget.hpp new file mode 100644 index 0000000..e2b2688 --- /dev/null +++ b/src/widgets/filters_widget.hpp @@ -0,0 +1,24 @@ +#ifndef FILTERS_WIDGET_H +#define FILTERS_WIDGET_H + +#include + +#include "ui_filters_widget.h" + + +class FiltersWidget : public QWidget +{ + Q_OBJECT + +public: + FiltersWidget(QWidget *parent = nullptr); + +public slots: + +signals: + +protected: + Ui::filtersview ui; +}; + +#endif // FILTERS_WIDGET_H diff --git a/ui/filters_widget.ui b/ui/filters_widget.ui new file mode 100644 index 0000000..750be91 --- /dev/null +++ b/ui/filters_widget.ui @@ -0,0 +1,19 @@ + + + filtersview + + + + 0 + 0 + 400 + 300 + + + + Form + + + + + diff --git a/ui/mainwindow.ui b/ui/mainwindow.ui index 74517cd..c7c7b08 100644 --- a/ui/mainwindow.ui +++ b/ui/mainwindow.ui @@ -26,7 +26,7 @@ 0 0 600 - 22 + 21 @@ -54,6 +54,7 @@ + @@ -123,6 +124,11 @@ &Plugins + + + F&ilters + + From e149f3c320d9b90b3956e7876a7a15dd4f3ad347 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 24 Dec 2024 14:36:24 +1100 Subject: [PATCH 11/16] Add widget elements --- ui/filters_widget.ui | 89 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 87 insertions(+), 2 deletions(-) diff --git a/ui/filters_widget.ui b/ui/filters_widget.ui index 750be91..a7cb53e 100644 --- a/ui/filters_widget.ui +++ b/ui/filters_widget.ui @@ -6,13 +6,98 @@ 0 0 - 400 - 300 + 281 + 362 + + + 0 + 222 + + Form + + + + + + + + 100 + 0 + + + + Select Filter + + + + + + + + 1 + 0 + + + + + 150 + 0 + + + + + + + + + + Qt::Orientation::Horizontal + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + Apply Filter + + + + + + From c1281d4942c602a73a73a784797425e96561b0ad Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 24 Dec 2024 15:54:59 +1100 Subject: [PATCH 12/16] Drag and drop onto filter setup widget --- src/widgets/filters_widget.cpp | 101 +++++++++++++++++++++++++++++++++ src/widgets/filters_widget.hpp | 17 ++++++ ui/filters_widget.ui | 7 +++ 3 files changed, 125 insertions(+) diff --git a/src/widgets/filters_widget.cpp b/src/widgets/filters_widget.cpp index 4daf11f..f0cd791 100644 --- a/src/widgets/filters_widget.cpp +++ b/src/widgets/filters_widget.cpp @@ -1,7 +1,108 @@ +#include + #include "filters_widget.hpp" +#include "data_source_manager.hpp" FiltersWidget::FiltersWidget(QWidget *parent) : QWidget(parent) { ui.setupUi(this); + setAcceptDrops(true); + + connect(ui.applyFilterButton, &QPushButton::released, this, &FiltersWidget::applyFilter); + connect(ui.clearItemsButton, &QPushButton::released, this, &FiltersWidget::clearItems); + + refresh(); +} + + +void FiltersWidget::refresh() +{ + bool hasItems = !seriesList.isEmpty(); + + ui.applyFilterButton->setEnabled(hasItems); + ui.clearItemsButton->setEnabled(hasItems); +} + + +void FiltersWidget::clearItems() +{ + seriesList.clear(); + refresh(); + // TODO - other stuff too? +} + + +void FiltersWidget::applyFilter() +{ + // TODO: Apply selected filter + refresh(); +} + + +void FiltersWidget::addSeries(DataSeriesPointer series) +{ + seriesList.append(series); + refresh(); +} + + +/** + * @brief FiltersWidget::dragEnterEvent is called when a drag event enters the widget + * @param event + */ +void FiltersWidget::dragEnterEvent(QDragEnterEvent *event) +{ + auto *mime = event->mimeData(); + + // DataSeries is being dragged onto this PlotWidget + if (mime->hasFormat("source") && mime->hasFormat("series")) + { + event->acceptProposedAction(); + return; + } +} + + +/** + * @brief FiltersWidget::dragMoveEvent is called when an accepted drag event moves across the widget + * @param event + */ +void FiltersWidget::dragMoveEvent(QDragMoveEvent *event) +{ + Q_UNUSED(event) +} + + +/** + * @brief FiltersWidget::dropEvent is called when an accepted drag event is dropped on the widget + * @param event + */ +void FiltersWidget::dropEvent(QDropEvent *event) +{ + auto *mime = event->mimeData(); + auto *manager = DataSourceManager::getInstance(); + + if (!mime || !manager) + { + return; + } + + // // DataSeries is dropped onto this PlotWidget + if (mime->hasFormat("source") && mime->hasFormat("series")) + { + QString source_lbl = mime->data("source"); + QString series_lbl = mime->data("series"); + + auto series = manager->findSeries(source_lbl, series_lbl); + + if (series.isNull()) + { + qCritical() << "Could not find series matching" << source_lbl << ":" << series_lbl; + return; + } + + event->accept(); + addSeries(series); + } } diff --git a/src/widgets/filters_widget.hpp b/src/widgets/filters_widget.hpp index e2b2688..2636fe9 100644 --- a/src/widgets/filters_widget.hpp +++ b/src/widgets/filters_widget.hpp @@ -2,6 +2,11 @@ #define FILTERS_WIDGET_H #include +#include +#include +#include + +#include "data_series.hpp" #include "ui_filters_widget.h" @@ -14,11 +19,23 @@ class FiltersWidget : public QWidget FiltersWidget(QWidget *parent = nullptr); public slots: + void addSeries(DataSeriesPointer series); + + void refresh(void); + void clearItems(void); + void applyFilter(void); signals: protected: Ui::filtersview ui; + + QList seriesList; + + // Drag-n-drop actions + virtual void dragEnterEvent(QDragEnterEvent *event) override; + virtual void dragMoveEvent(QDragMoveEvent *event) override; + virtual void dropEvent(QDropEvent *event) override; }; #endif // FILTERS_WIDGET_H diff --git a/ui/filters_widget.ui b/ui/filters_widget.ui index a7cb53e..438fe3e 100644 --- a/ui/filters_widget.ui +++ b/ui/filters_widget.ui @@ -75,6 +75,13 @@ + + + + Clear Items + + + From c5318efdbc267aebb93c414324cf0cc99b1cad25 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 24 Dec 2024 20:17:49 +1100 Subject: [PATCH 13/16] Enumerate filter plugins --- src/lumberjack_settings.cpp | 6 ++++++ src/lumberjack_settings.hpp | 1 + src/mainwindow.cpp | 6 +++++- src/plot_widget.cpp | 2 +- src/plugins/plugin_registry.cpp | 6 ++++-- src/plugins/plugin_registry.hpp | 9 +++++++-- src/widgets/filters_widget.cpp | 31 +++++++++++++++++++++++++++++++ src/widgets/filters_widget.hpp | 4 +++- ui/filters_widget.ui | 3 +++ 9 files changed, 61 insertions(+), 7 deletions(-) diff --git a/src/lumberjack_settings.cpp b/src/lumberjack_settings.cpp index 99d258d..ef56438 100644 --- a/src/lumberjack_settings.cpp +++ b/src/lumberjack_settings.cpp @@ -80,6 +80,12 @@ QVariant LumberjackSettings::loadSetting(QString group, QString key, QVariant de } +QString LumberjackSettings::loadString(QString group, QString key, QString defaultValue) +{ + return loadSetting(group, key, defaultValue).toString(); +} + + bool LumberjackSettings::loadBoolean(QString group, QString key, bool defaultValue) { QVariant result = loadSetting(group, key, defaultValue); diff --git a/src/lumberjack_settings.hpp b/src/lumberjack_settings.hpp index 010b377..574695d 100644 --- a/src/lumberjack_settings.hpp +++ b/src/lumberjack_settings.hpp @@ -37,6 +37,7 @@ class LumberjackSettings } QVariant loadSetting(QString group, QString key, QVariant defaultValue); + QString loadString(QString group, QString key, QString defaultValue = QString()); bool loadBoolean(QString group, QString key, bool defaultValue = false); void saveSetting(QString group, QString key, QVariant value); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 8b08b7e..445c5d0 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -54,7 +54,11 @@ MainWindow::MainWindow(QWidget *parent) loadWorkspaceSettings(); // Load plugins - PluginRegistry::getInstance()->loadPlugins(); + auto registry = PluginRegistry::getInstance(); + + connect(registry, &PluginRegistry::pluginsLoaded, &filtersView, &FiltersWidget::loadPlugins); + + registry->loadPlugins(); } diff --git a/src/plot_widget.cpp b/src/plot_widget.cpp index d44ea76..c23571e 100644 --- a/src/plot_widget.cpp +++ b/src/plot_widget.cpp @@ -58,7 +58,7 @@ PlotWidget::PlotWidget() : QwtPlot() auto *settings = LumberjackSettings::getInstance(); - QString bgColor = settings->loadSetting("graph", "defaultBackgroundColor", "#F0F0F0").toString(); + QString bgColor = settings->loadString("graph", "defaultBackgroundColor", "#F0F0F0"); if (QColor::isValidColorName(bgColor)) { diff --git a/src/plugins/plugin_registry.cpp b/src/plugins/plugin_registry.cpp index 16e18a0..29bf618 100644 --- a/src/plugins/plugin_registry.cpp +++ b/src/plugins/plugin_registry.cpp @@ -61,6 +61,8 @@ void PluginRegistry::loadPlugins() } } } + + emit pluginsLoaded(); } @@ -140,7 +142,7 @@ QString PluginRegistry::getFilenameForImport(void) const dialog.setWindowTitle(tr("Import Data from File")); - QString lastDir = settings->loadSetting("import", "lastDirectory", QString()).toString(); + QString lastDir = settings->loadString("import", "lastDirectory"); if (!lastDir.isEmpty()) { @@ -194,7 +196,7 @@ QString PluginRegistry::getFilenameForExport(void) const dialog.setWindowTitle(tr("Export Data to File")); - QString lastDir = settings->loadSetting("export", "lastDirectory", QString()).toString(); + QString lastDir = settings->loadString("export", "lastDirectory"); if (!lastDir.isEmpty()) { diff --git a/src/plugins/plugin_registry.hpp b/src/plugins/plugin_registry.hpp index 97803f3..28aa708 100644 --- a/src/plugins/plugin_registry.hpp +++ b/src/plugins/plugin_registry.hpp @@ -37,8 +37,6 @@ class PluginRegistry : public QObject } } - void loadPlugins(void); - void clearRegistry(void); const ImportPluginList& ImportPlugins(void) { return m_ImportPlugins; } const ExportPluginList& ExportPlugins(void) { return m_ExportPlugins; } @@ -47,6 +45,13 @@ class PluginRegistry : public QObject QString getFilenameForImport(void) const; QString getFilenameForExport(void) const; +public slots: + void loadPlugins(void); + void clearRegistry(void); + +signals: + void pluginsLoaded(); + protected: bool loadImportPlugin(QObject *instance); diff --git a/src/widgets/filters_widget.cpp b/src/widgets/filters_widget.cpp index f0cd791..3722c85 100644 --- a/src/widgets/filters_widget.cpp +++ b/src/widgets/filters_widget.cpp @@ -1,6 +1,9 @@ #include #include "filters_widget.hpp" + +#include "plugin_registry.hpp" +#include "lumberjack_settings.hpp" #include "data_source_manager.hpp" @@ -9,8 +12,36 @@ FiltersWidget::FiltersWidget(QWidget *parent) : QWidget(parent) ui.setupUi(this); setAcceptDrops(true); + refresh(); + connect(ui.applyFilterButton, &QPushButton::released, this, &FiltersWidget::applyFilter); connect(ui.clearItemsButton, &QPushButton::released, this, &FiltersWidget::clearItems); +} + + +void FiltersWidget::loadPlugins() +{ + auto registry = PluginRegistry::getInstance(); + auto settings = LumberjackSettings::getInstance(); + + ui.filterSelect->clear(); + + QString selectedPlugin = settings->loadString("filters", "selectedFilter"); + + for (auto plugin : registry->FilterPlugins()) + { + ui.filterSelect->addItem(plugin->pluginName()); + } + + + for (int idx = 0; idx < ui.filterSelect->count(); idx++) + { + if (ui.filterSelect->itemText(idx) == selectedPlugin) + { + ui.filterSelect->setCurrentIndex(idx); + break; + } + } refresh(); } diff --git a/src/widgets/filters_widget.hpp b/src/widgets/filters_widget.hpp index 2636fe9..269320a 100644 --- a/src/widgets/filters_widget.hpp +++ b/src/widgets/filters_widget.hpp @@ -20,8 +20,8 @@ class FiltersWidget : public QWidget public slots: void addSeries(DataSeriesPointer series); + void loadPlugins(void); - void refresh(void); void clearItems(void); void applyFilter(void); @@ -36,6 +36,8 @@ public slots: virtual void dragEnterEvent(QDragEnterEvent *event) override; virtual void dragMoveEvent(QDragMoveEvent *event) override; virtual void dropEvent(QDropEvent *event) override; + + void refresh(void); }; #endif // FILTERS_WIDGET_H diff --git a/ui/filters_widget.ui b/ui/filters_widget.ui index 438fe3e..ed4c1f9 100644 --- a/ui/filters_widget.ui +++ b/ui/filters_widget.ui @@ -49,6 +49,9 @@ 0 + + false + From de5427eb70c1962375ecea7ed7906c6c23f9d2ec Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 6 Jan 2025 10:23:45 +1100 Subject: [PATCH 14/16] Display selected data series --- src/widgets/filters_widget.cpp | 20 +++++++++++ src/widgets/filters_widget.hpp | 1 + ui/dataview_widget.ui | 6 ++++ ui/filters_widget.ui | 63 +++++++++++++++++++++------------- 4 files changed, 67 insertions(+), 23 deletions(-) diff --git a/src/widgets/filters_widget.cpp b/src/widgets/filters_widget.cpp index 3722c85..b25bae1 100644 --- a/src/widgets/filters_widget.cpp +++ b/src/widgets/filters_widget.cpp @@ -53,6 +53,26 @@ void FiltersWidget::refresh() ui.applyFilterButton->setEnabled(hasItems); ui.clearItemsButton->setEnabled(hasItems); + + updateSeriesList(); +} + + +void FiltersWidget::updateSeriesList(void) +{ + auto *list = ui.seriesList; + + list->clear(); + + list->setSelectionMode(QAbstractItemView::ExtendedSelection); + + for (auto series : seriesList) + { + if (series.isNull()) continue; + + QListWidgetItem *item = new QListWidgetItem(series->getLabel(), list); + list->addItem(item); + } } diff --git a/src/widgets/filters_widget.hpp b/src/widgets/filters_widget.hpp index 269320a..6f183bd 100644 --- a/src/widgets/filters_widget.hpp +++ b/src/widgets/filters_widget.hpp @@ -38,6 +38,7 @@ public slots: virtual void dropEvent(QDropEvent *event) override; void refresh(void); + void updateSeriesList(void); }; #endif // FILTERS_WIDGET_H diff --git a/ui/dataview_widget.ui b/ui/dataview_widget.ui index c121d8a..468df69 100644 --- a/ui/dataview_widget.ui +++ b/ui/dataview_widget.ui @@ -10,6 +10,12 @@ 380 + + + 254 + 0 + + Form diff --git a/ui/filters_widget.ui b/ui/filters_widget.ui index ed4c1f9..d956e87 100644 --- a/ui/filters_widget.ui +++ b/ui/filters_widget.ui @@ -6,8 +6,8 @@ 0 0 - 281 - 362 + 411 + 462 @@ -56,27 +56,7 @@ - - - - Qt::Orientation::Horizontal - - - - - - - Qt::Orientation::Vertical - - - - 20 - 40 - - - - - + @@ -107,6 +87,43 @@ + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + Qt::Orientation::Horizontal + + + + + + + Qt::Orientation::Horizontal + + + + + + + + + + Selected Data Series + + + From 651eae11b124e514782f859c269832fe9c5cc6ee Mon Sep 17 00:00:00 2001 From: Oliver Date: Tue, 7 Jan 2025 11:09:08 +1100 Subject: [PATCH 15/16] Cleanup .pro file --- lumberjack.pro | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/lumberjack.pro b/lumberjack.pro index fe3826d..3250a2b 100644 --- a/lumberjack.pro +++ b/lumberjack.pro @@ -171,17 +171,19 @@ CONFIG(debug, debug|release) { COPIES += dllFiles -CONFIG(debug, debug | release) { - win32 { - # Copy required .DLL files - QMAKE_POST_LINK += $$[QT_INSTALL_BINS]\windeployqt --debug --opengl --openglwidgets --widgets --compiler-runtime $$shell_path($$quote($$DESTDIR))\lumberjack.exe $$escape_expand(\n\t) - } -} else { - win32 { - # Copy required .DLL files - QMAKE_POST_LINK += $$[QT_INSTALL_BINS]\windeployqt --release --opengl --openglwidgets --widgets --compiler-runtime $$shell_path($$quote($$DESTDIR))\lumberjack.exe $$escape_expand(\n\t) +win32 { + # Qt libraries required by windeployqt + QT_LIB = "--core --opengl --openglwidgets --qml --quick --quickwidgets --widgets --compiler-runtime" + + CONFIG(release, debug|release) { + # Release mode + QMAKE_POST_LINK += $$[QT_INSTALL_BINS]\windeployqt --release --force $$quote($$QT_LIB) $$shell_path($$quote($$DESTDIR))\lumberjack.exe $$escape_expand(\n\t) + } else { + # Debug mode + QMAKE_POST_LINK += $$[QT_INSTALL_BINS]\windeployqt --debug --force $$quote($$QT_LIB) $$shell_path($$quote($$DESTDIR))\lumberjack.exe $$escape_expand(\n\t) } } + RESOURCES += \ resources.qrc From f6976fef6c160007c09912c51cfc534a32c45cc5 Mon Sep 17 00:00:00 2001 From: Oliver Date: Tue, 7 Jan 2025 12:17:30 +1100 Subject: [PATCH 16/16] Updates --- lumberjack.pro | 8 ++++---- src/plot_legend.cpp | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lumberjack.pro b/lumberjack.pro index 3250a2b..303952f 100644 --- a/lumberjack.pro +++ b/lumberjack.pro @@ -176,11 +176,11 @@ win32 { QT_LIB = "--core --opengl --openglwidgets --qml --quick --quickwidgets --widgets --compiler-runtime" CONFIG(release, debug|release) { - # Release mode - QMAKE_POST_LINK += $$[QT_INSTALL_BINS]\windeployqt --release --force $$quote($$QT_LIB) $$shell_path($$quote($$DESTDIR))\lumberjack.exe $$escape_expand(\n\t) + # Release mode - run windeployqt + QMAKE_POST_LINK += $$[QT_INSTALL_BINS]\windeployqt --force $$quote($$QT_LIB) $$shell_path($$quote($$DESTDIR))\lumberjack.exe $$escape_expand(\n\t) } else { - # Debug mode - QMAKE_POST_LINK += $$[QT_INSTALL_BINS]\windeployqt --debug --force $$quote($$QT_LIB) $$shell_path($$quote($$DESTDIR))\lumberjack.exe $$escape_expand(\n\t) + # Debug mode - do not run windeployqt + # Expectation is that the code is run from within QtCreator } } diff --git a/src/plot_legend.cpp b/src/plot_legend.cpp index 8d34da4..b3de2cb 100644 --- a/src/plot_legend.cpp +++ b/src/plot_legend.cpp @@ -131,8 +131,8 @@ bool PlotLegend::handleMousePressEvent(const QMouseEvent *event) // Ignore if the legend is not displayed if (rect.width() < 0 || rect.height() < 0) return false; - int x = event->x(); - int y = event->y(); + int x = (int) event->position().x(); + int y = (int) event->position().y(); // Return early if click is outside legend bounds if (x < rect.left() || x > rect.right()) return false;