From 8fbbe5fee4f4525c2615abad89c92db7559d2466 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Mon, 16 Feb 2026 14:59:40 +0100 Subject: [PATCH 01/16] Do not update the HUD during de-serialization, do it post-de-serialization. Put guard in updateHeadsUpDisplay --- src/ScatterplotPlugin.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/ScatterplotPlugin.cpp b/src/ScatterplotPlugin.cpp index 94d4826..5357bbc 100644 --- a/src/ScatterplotPlugin.cpp +++ b/src/ScatterplotPlugin.cpp @@ -288,6 +288,8 @@ ScatterplotPlugin::ScatterplotPlugin(const PluginFactory* factory) : getLearningCenterAction().addVideos(QStringList({ "Practitioner", "Developer" })); setOverlayActionsTargetWidget(_scatterPlotWidget); + + connect(&mv::projects(), &AbstractProjectManager::projectOpened, this, &ScatterplotPlugin::updateHeadsUpDisplay); } ScatterplotPlugin::~ScatterplotPlugin() @@ -1016,6 +1018,9 @@ void ScatterplotPlugin::updateSelection() void ScatterplotPlugin::updateHeadsUpDisplay() { + if (mv::projects().isOpeningProject()) + return; + getHeadsUpDisplayAction().removeAllHeadsUpDisplayItems(); if (_positionDataset.isValid()) { @@ -1037,6 +1042,7 @@ void ScatterplotPlugin::updateHeadsUpDisplay() } else { getHeadsUpDisplayAction().addHeadsUpDisplayItem("No datasets loaded", "", ""); } + } void ScatterplotPlugin::updateHeadsUpDisplayTextColor() @@ -1065,12 +1071,7 @@ void ScatterplotPlugin::fromVariantMap(const QVariantMap& variantMap) _primaryToolbarAction.fromParentVariantMap(variantMap); _settingsAction.fromParentVariantMap(variantMap); - updateHeadsUpDisplay(); - if (pointRenderer.getNavigator().getNavigationAction().getSerializationCountFrom() == 0) { - qDebug() << "Resetting view"; - - _scatterPlotWidget->update(); } From c1c8e06ee33e29bd5764b7320643ebf89dad96f2 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Mon, 16 Feb 2026 16:28:04 +0100 Subject: [PATCH 02/16] Properly sync HUD with loaded color, point size and point opacity dataset(s) --- src/ScatterplotPlugin.cpp | 36 +++++++++++++++++++++++++++++------- src/ScatterplotPlugin.h | 4 +++- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/src/ScatterplotPlugin.cpp b/src/ScatterplotPlugin.cpp index 5357bbc..9adb621 100644 --- a/src/ScatterplotPlugin.cpp +++ b/src/ScatterplotPlugin.cpp @@ -344,9 +344,6 @@ void ScatterplotPlugin::init() connect(&_positionDataset, &Dataset<>::guiNameChanged, this, &ScatterplotPlugin::updateHeadsUpDisplay); const auto currentColorDatasetChanged = [this](Dataset currentColorDataset) -> void { - if (_colorDataset == currentColorDataset) - return; - if (_colorDataset.isValid()) disconnect(&_colorDataset, &Dataset<>::guiNameChanged, this, nullptr); @@ -362,8 +359,35 @@ void ScatterplotPlugin::init() currentColorDatasetChanged(_settingsAction.getColoringAction().getCurrentColorDataset()); }); - connect(&_settingsAction.getPlotAction().getPointPlotAction().getSizeAction(), &ScalarAction::sourceDataChanged, this, &ScatterplotPlugin::updateHeadsUpDisplay); - connect(&_settingsAction.getPlotAction().getPointPlotAction().getOpacityAction(), &ScalarAction::sourceDataChanged, this, &ScatterplotPlugin::updateHeadsUpDisplay); + const auto currentPointSizeDatasetChanged = [this]() -> void { + auto currentPointSizeDataset = _settingsAction.getPlotAction().getPointPlotAction().getSizeAction().getCurrentDataset(); + + if (_pointSizeDataset.isValid()) + disconnect(&_pointSizeDataset, &Dataset<>::guiNameChanged, this, nullptr); + + _pointSizeDataset = currentPointSizeDataset; + + connect(&_pointSizeDataset, &Dataset<>::guiNameChanged, this, &ScatterplotPlugin::updateHeadsUpDisplay); + + updateHeadsUpDisplay(); + }; + + connect(&_settingsAction.getPlotAction().getPointPlotAction().getSizeAction(), &ScalarAction::sourceSelectionChanged, this, currentPointSizeDatasetChanged); + + const auto currentPointOpacityDatasetChanged = [this]() -> void { + auto currentPointOpacityDataset = _settingsAction.getPlotAction().getPointPlotAction().getOpacityAction().getCurrentDataset(); + + if (_pointOpacityDataset.isValid()) + disconnect(&_pointOpacityDataset, &Dataset<>::guiNameChanged, this, nullptr); + + _pointOpacityDataset = currentPointOpacityDataset; + + connect(&_pointOpacityDataset, &Dataset<>::guiNameChanged, this, &ScatterplotPlugin::updateHeadsUpDisplay); + + updateHeadsUpDisplay(); + }; + + connect(&_settingsAction.getPlotAction().getPointPlotAction().getOpacityAction(), &ScalarAction::sourceSelectionChanged, this, currentPointOpacityDatasetChanged); connect(&_settingsAction.getMiscellaneousAction().getBackgroundColorAction(), &ColorAction::colorChanged, this, &ScatterplotPlugin::updateHeadsUpDisplayTextColor); @@ -1038,11 +1062,9 @@ void ScatterplotPlugin::updateHeadsUpDisplay() addMetaDataToHeadsUpDisplay("Size", _settingsAction.getPlotAction().getPointPlotAction().getSizeAction().getCurrentDataset(), datasetsItem); addMetaDataToHeadsUpDisplay("Opacity", _settingsAction.getPlotAction().getPointPlotAction().getOpacityAction().getCurrentDataset(), datasetsItem); - } else { getHeadsUpDisplayAction().addHeadsUpDisplayItem("No datasets loaded", "", ""); } - } void ScatterplotPlugin::updateHeadsUpDisplayTextColor() diff --git a/src/ScatterplotPlugin.h b/src/ScatterplotPlugin.h index 4155039..301364d 100644 --- a/src/ScatterplotPlugin.h +++ b/src/ScatterplotPlugin.h @@ -116,7 +116,9 @@ class ScatterplotPlugin : public ViewPlugin ScatterplotWidget* _scatterPlotWidget; /** The visualization widget */ Dataset _positionDataset; /** Smart pointer to points dataset for point position */ Dataset _positionSourceDataset; /** Smart pointer to source of the points dataset for point position (if any) */ - Dataset _colorDataset; /** Smart pointer to dataset used for coloring (if any) */ + Dataset _colorDataset; /** Smart pointer to dataset used for coloring (if any) */ + Dataset _pointSizeDataset; /** Smart pointer to dataset for driving point size (if any) */ + Dataset _pointOpacityDataset; /** Smart pointer to dataset for driving point opacity (if any) */ std::vector _positions; /** Point positions */ unsigned int _numPoints; /** Number of point positions */ SettingsAction _settingsAction; /** Group action for all settings */ From bfcab8b3df806adb630404df0dbeb2fe9e1624f1 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Mon, 16 Feb 2026 14:59:40 +0100 Subject: [PATCH 03/16] Do not update the HUD during de-serialization, do it post-de-serialization. Put guard in updateHeadsUpDisplay --- src/ScatterplotPlugin.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ScatterplotPlugin.cpp b/src/ScatterplotPlugin.cpp index 9adb621..2975b63 100644 --- a/src/ScatterplotPlugin.cpp +++ b/src/ScatterplotPlugin.cpp @@ -1065,6 +1065,7 @@ void ScatterplotPlugin::updateHeadsUpDisplay() } else { getHeadsUpDisplayAction().addHeadsUpDisplayItem("No datasets loaded", "", ""); } + } void ScatterplotPlugin::updateHeadsUpDisplayTextColor() From ec29018eba40cff549d4e7c0f31e9dc90278a20c Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Mon, 16 Feb 2026 16:28:04 +0100 Subject: [PATCH 04/16] Properly sync HUD with loaded color, point size and point opacity dataset(s) --- src/ScatterplotPlugin.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ScatterplotPlugin.cpp b/src/ScatterplotPlugin.cpp index 2975b63..9adb621 100644 --- a/src/ScatterplotPlugin.cpp +++ b/src/ScatterplotPlugin.cpp @@ -1065,7 +1065,6 @@ void ScatterplotPlugin::updateHeadsUpDisplay() } else { getHeadsUpDisplayAction().addHeadsUpDisplayItem("No datasets loaded", "", ""); } - } void ScatterplotPlugin::updateHeadsUpDisplayTextColor() From 8a70eaac334a1c9fac449f34d434a822dfd4aa67 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Tue, 17 Feb 2026 09:55:00 +0100 Subject: [PATCH 05/16] Addd point size and opacity dataset picker actions and implement the synchronization logic --- src/DatasetsAction.cpp | 55 +++++++++++++++++++++++++++++++++++++++--- src/DatasetsAction.h | 4 +++ 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/src/DatasetsAction.cpp b/src/DatasetsAction.cpp index 3cc6381..247f338 100644 --- a/src/DatasetsAction.cpp +++ b/src/DatasetsAction.cpp @@ -14,7 +14,9 @@ DatasetsAction::DatasetsAction(QObject* parent, const QString& title) : GroupAction(parent, title), _scatterplotPlugin(dynamic_cast(parent->parent())), _positionDatasetPickerAction(this, "Position"), - _colorDatasetPickerAction(this, "Color") + _colorDatasetPickerAction(this, "Color"), + _pointSizeDatasetPickerAction(this, "Point size"), + _pointOpacityDatasetPickerAction(this, "Point opacity") { setIconByName("database"); setToolTip("Manage loaded datasets for position and color"); @@ -23,15 +25,24 @@ DatasetsAction::DatasetsAction(QObject* parent, const QString& title) : addAction(&_positionDatasetPickerAction); addAction(&_colorDatasetPickerAction); + addAction(&_pointSizeDatasetPickerAction); + addAction(&_pointOpacityDatasetPickerAction); _positionDatasetPickerAction.setFilterFunction([this](mv::Dataset dataset) -> bool { return dataset->getDataType() == PointType; - }); + }); _colorDatasetPickerAction.setFilterFunction([this](mv::Dataset dataset) -> bool { return (dataset->getDataType() == PointType || dataset->getDataType() == ColorType || dataset->getDataType() == ClusterType); - }); + }); + + _pointSizeDatasetPickerAction.setFilterFunction([this](mv::Dataset dataset) -> bool { + return dataset->getDataType() == PointType; + }); + _pointOpacityDatasetPickerAction.setFilterFunction([this](mv::Dataset dataset) -> bool { + return dataset->getDataType() == PointType; + }); auto scatterplotPlugin = dynamic_cast(parent->parent()); @@ -53,6 +64,44 @@ DatasetsAction::DatasetsAction(QObject* parent, const QString& title) : connect(&scatterplotPlugin->getSettingsAction().getColoringAction(), &ColoringAction::currentColorDatasetChanged, this, [this](Dataset currentColorDataset) -> void { _colorDatasetPickerAction.setCurrentDataset(currentColorDataset); }); + + auto& pointPlotAction = scatterplotPlugin->getSettingsAction().getPlotAction().getPointPlotAction(); + auto& pointSizeAction = pointPlotAction.getSizeAction(); + auto& pointOpacityAction = pointPlotAction.getOpacityAction(); + + const auto pointSizeSourceChanged = [this, &pointSizeAction]() -> void { + _pointSizeDatasetPickerAction.setCurrentDataset(pointSizeAction.isSourceDataset() ? pointSizeAction.getCurrentDataset() : nullptr); + + if (!pointSizeAction.isSourceDataset()) + _pointSizeDatasetPickerAction.setCurrentIndex(-1); + }; + + connect(&pointSizeAction, &ScalarAction::sourceSelectionChanged, this, pointSizeSourceChanged); + connect(&pointSizeAction, &ScalarAction::sourceDataChanged, this, pointSizeSourceChanged); + + connect(&_pointSizeDatasetPickerAction, &DatasetPickerAction::datasetPicked, this, [this, &pointSizeAction](Dataset dataset) -> void { + if (!dataset.isValid()) + return; + + pointSizeAction.setCurrentDataset(dataset); + }); + + const auto pointOpacitySourceChanged = [this, &pointOpacityAction]() -> void { + _pointOpacityDatasetPickerAction.setCurrentDataset(pointOpacityAction.isSourceDataset() ? pointOpacityAction.getCurrentDataset() : nullptr); + + if (!pointOpacityAction.isSourceDataset()) + _pointOpacityDatasetPickerAction.setCurrentIndex(-1); + }; + + connect(&pointOpacityAction, &ScalarAction::sourceSelectionChanged, this, pointOpacitySourceChanged); + connect(&pointOpacityAction, &ScalarAction::sourceDataChanged, this, pointOpacitySourceChanged); + + connect(&_pointOpacityDatasetPickerAction, &DatasetPickerAction::datasetPicked, this, [this, &pointOpacityAction](Dataset dataset) -> void { + if (!dataset.isValid()) + return; + + pointOpacityAction.setCurrentDataset(dataset); + }); } void DatasetsAction::connectToPublicAction(WidgetAction* publicAction, bool recursive) diff --git a/src/DatasetsAction.h b/src/DatasetsAction.h index 1730a06..db10a3e 100644 --- a/src/DatasetsAction.h +++ b/src/DatasetsAction.h @@ -53,11 +53,15 @@ class DatasetsAction : public GroupAction DatasetPickerAction& getPositionDatasetPickerAction() { return _positionDatasetPickerAction; } DatasetPickerAction& getColorDatasetPickerAction() { return _colorDatasetPickerAction; } + DatasetPickerAction& getPointSizeDatasetPickerAction() { return _pointSizeDatasetPickerAction; } + DatasetPickerAction& getPointOpacityDatasetPickerAction() { return _pointOpacityDatasetPickerAction; } private: ScatterplotPlugin* _scatterplotPlugin; /** Pointer to scatter plot plugin */ DatasetPickerAction _positionDatasetPickerAction; /** Dataset picker action for position dataset */ DatasetPickerAction _colorDatasetPickerAction; /** Dataset picker action for color dataset */ + DatasetPickerAction _pointSizeDatasetPickerAction; /** Dataset picker action for point size */ + DatasetPickerAction _pointOpacityDatasetPickerAction; /** Dataset picker action for point opacity */ friend class mv::AbstractActionsManager; }; From 3c65a7e6cf743bc2dfb3f52b2c3eeaeb4b89bda5 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Tue, 17 Feb 2026 09:56:21 +0100 Subject: [PATCH 06/16] Add connection logic --- src/DatasetsAction.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/DatasetsAction.cpp b/src/DatasetsAction.cpp index 247f338..095442d 100644 --- a/src/DatasetsAction.cpp +++ b/src/DatasetsAction.cpp @@ -116,6 +116,8 @@ void DatasetsAction::connectToPublicAction(WidgetAction* publicAction, bool recu if (recursive) { actions().connectPrivateActionToPublicAction(&_positionDatasetPickerAction, &publicDatasetsAction->getPositionDatasetPickerAction(), recursive); actions().connectPrivateActionToPublicAction(&_colorDatasetPickerAction, &publicDatasetsAction->getColorDatasetPickerAction(), recursive); + actions().connectPrivateActionToPublicAction(&_pointSizeDatasetPickerAction, &publicDatasetsAction->getPointSizeDatasetPickerAction(), recursive); + actions().connectPrivateActionToPublicAction(&_pointOpacityDatasetPickerAction, &publicDatasetsAction->getPointOpacityDatasetPickerAction(), recursive); } GroupAction::connectToPublicAction(publicAction, recursive); @@ -129,6 +131,8 @@ void DatasetsAction::disconnectFromPublicAction(bool recursive) if (recursive) { actions().disconnectPrivateActionFromPublicAction(&_positionDatasetPickerAction, recursive); actions().disconnectPrivateActionFromPublicAction(&_colorDatasetPickerAction, recursive); + actions().disconnectPrivateActionFromPublicAction(&_pointSizeDatasetPickerAction, recursive); + actions().disconnectPrivateActionFromPublicAction(&_pointOpacityDatasetPickerAction, recursive); } GroupAction::disconnectFromPublicAction(recursive); From 18c339ea846409271d52dc777f5d5358e064c2ea Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Tue, 17 Feb 2026 10:37:42 +0100 Subject: [PATCH 07/16] Fix point size and opacity dataset selection reset logic --- src/DatasetsAction.cpp | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/DatasetsAction.cpp b/src/DatasetsAction.cpp index 095442d..e77793e 100644 --- a/src/DatasetsAction.cpp +++ b/src/DatasetsAction.cpp @@ -28,6 +28,10 @@ DatasetsAction::DatasetsAction(QObject* parent, const QString& title) : addAction(&_pointSizeDatasetPickerAction); addAction(&_pointOpacityDatasetPickerAction); + _colorDatasetPickerAction.setDefaultWidgetFlag(OptionAction::Clearable); + _pointSizeDatasetPickerAction.setDefaultWidgetFlag(OptionAction::Clearable); + _pointOpacityDatasetPickerAction.setDefaultWidgetFlag(OptionAction::Clearable); + _positionDatasetPickerAction.setFilterFunction([this](mv::Dataset dataset) -> bool { return dataset->getDataType() == PointType; }); @@ -56,9 +60,12 @@ DatasetsAction::DatasetsAction(QObject* parent, const QString& title) : connect(&scatterplotPlugin->getPositionDataset(), &Dataset::changed, this, [this](DatasetImpl* dataset) -> void { _positionDatasetPickerAction.setCurrentDataset(dataset); }); - - connect(&_colorDatasetPickerAction, &DatasetPickerAction::datasetPicked, [this, scatterplotPlugin](Dataset pickedDataset) -> void { - scatterplotPlugin->getSettingsAction().getColoringAction().setCurrentColorDataset(pickedDataset); + + auto& coloringAction = scatterplotPlugin->getSettingsAction().getColoringAction(); + + connect(&_colorDatasetPickerAction, &DatasetPickerAction::datasetPicked, [this, &coloringAction](Dataset pickedDataset) -> void { + coloringAction.getColorByAction().setCurrentIndex(pickedDataset.isValid() ? 2 : 0); + coloringAction.setCurrentColorDataset(pickedDataset); }); connect(&scatterplotPlugin->getSettingsAction().getColoringAction(), &ColoringAction::currentColorDatasetChanged, this, [this](Dataset currentColorDataset) -> void { @@ -79,11 +86,11 @@ DatasetsAction::DatasetsAction(QObject* parent, const QString& title) : connect(&pointSizeAction, &ScalarAction::sourceSelectionChanged, this, pointSizeSourceChanged); connect(&pointSizeAction, &ScalarAction::sourceDataChanged, this, pointSizeSourceChanged); - connect(&_pointSizeDatasetPickerAction, &DatasetPickerAction::datasetPicked, this, [this, &pointSizeAction](Dataset dataset) -> void { - if (!dataset.isValid()) - return; + connect(&_pointSizeDatasetPickerAction, &DatasetPickerAction::currentIndexChanged, this, [this, &pointSizeAction](const int32_t& currentIndex) -> void { + pointSizeAction.setCurrentDataset(_pointSizeDatasetPickerAction.getCurrentDataset()); - pointSizeAction.setCurrentDataset(dataset); + if (currentIndex < 0) + pointSizeAction.setCurrentSourceIndex(ScalarSourceModel::DefaultRow::Constant); }); const auto pointOpacitySourceChanged = [this, &pointOpacityAction]() -> void { @@ -96,11 +103,11 @@ DatasetsAction::DatasetsAction(QObject* parent, const QString& title) : connect(&pointOpacityAction, &ScalarAction::sourceSelectionChanged, this, pointOpacitySourceChanged); connect(&pointOpacityAction, &ScalarAction::sourceDataChanged, this, pointOpacitySourceChanged); - connect(&_pointOpacityDatasetPickerAction, &DatasetPickerAction::datasetPicked, this, [this, &pointOpacityAction](Dataset dataset) -> void { - if (!dataset.isValid()) - return; + connect(&_pointOpacityDatasetPickerAction, &DatasetPickerAction::currentIndexChanged, this, [this, &pointOpacityAction](const int32_t& currentIndex) -> void { + pointOpacityAction.setCurrentDataset(_pointOpacityDatasetPickerAction.getCurrentDataset()); - pointOpacityAction.setCurrentDataset(dataset); + if (currentIndex < 0) + pointOpacityAction.setCurrentSourceIndex(ScalarSourceModel::DefaultRow::Constant); }); } From c5929319b7588858e099d2ba2ada589da98c9b2b Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Tue, 17 Feb 2026 16:29:18 +0100 Subject: [PATCH 08/16] Avoid setting color dataset during project open Only call setCurrentColorDataset when the picked dataset is valid and a project is not currently opening. Previously the code unconditionally updated the current color dataset in the DatasetPickerAction handler; this change prevents dataset changes from being applied while a project load is in progress, avoiding potential race conditions or invalid state during project open. --- src/DatasetsAction.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/DatasetsAction.cpp b/src/DatasetsAction.cpp index e77793e..c4ffbe0 100644 --- a/src/DatasetsAction.cpp +++ b/src/DatasetsAction.cpp @@ -65,7 +65,9 @@ DatasetsAction::DatasetsAction(QObject* parent, const QString& title) : connect(&_colorDatasetPickerAction, &DatasetPickerAction::datasetPicked, [this, &coloringAction](Dataset pickedDataset) -> void { coloringAction.getColorByAction().setCurrentIndex(pickedDataset.isValid() ? 2 : 0); - coloringAction.setCurrentColorDataset(pickedDataset); + + if (pickedDataset.isValid() && !mv::projects().isOpeningProject()) + coloringAction.setCurrentColorDataset(pickedDataset); }); connect(&scatterplotPlugin->getSettingsAction().getColoringAction(), &ColoringAction::currentColorDatasetChanged, this, [this](Dataset currentColorDataset) -> void { From ed65894a423133a2ac4b5c5764502b7e5d14e234 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Wed, 18 Feb 2026 10:52:29 +0100 Subject: [PATCH 09/16] Move dataset tracking into DatasetsAction Refactor dataset handling by moving color/size/opacity dataset setup and tracking out of ScatterplotPlugin into DatasetsAction. Add setup helper methods, getters, serialization (from/to QVariantMap), and connect/disconnect logic for public actions. Update ScatterplotPlugin to use the SettingsAction's coloring state when building the HUD and remove redundant dataset members and signal handlers from the plugin. This improves separation of concerns and centralizes dataset-related UI wiring in DatasetsAction. --- src/DatasetsAction.cpp | 205 ++++++++++++++++++++++++++------------ src/DatasetsAction.h | 26 +++-- src/ScatterplotPlugin.cpp | 52 +--------- src/ScatterplotPlugin.h | 3 - 4 files changed, 165 insertions(+), 121 deletions(-) diff --git a/src/DatasetsAction.cpp b/src/DatasetsAction.cpp index c4ffbe0..4b8f02a 100644 --- a/src/DatasetsAction.cpp +++ b/src/DatasetsAction.cpp @@ -32,27 +32,74 @@ DatasetsAction::DatasetsAction(QObject* parent, const QString& title) : _pointSizeDatasetPickerAction.setDefaultWidgetFlag(OptionAction::Clearable); _pointOpacityDatasetPickerAction.setDefaultWidgetFlag(OptionAction::Clearable); - _positionDatasetPickerAction.setFilterFunction([this](mv::Dataset dataset) -> bool { - return dataset->getDataType() == PointType; - }); + auto scatterplotPlugin = dynamic_cast(parent->parent()); - _colorDatasetPickerAction.setFilterFunction([this](mv::Dataset dataset) -> bool { - return (dataset->getDataType() == PointType || dataset->getDataType() == ColorType || dataset->getDataType() == ClusterType); - }); + Q_ASSERT(scatterplotPlugin); - _pointSizeDatasetPickerAction.setFilterFunction([this](mv::Dataset dataset) -> bool { - return dataset->getDataType() == PointType; - }); + if (scatterplotPlugin == nullptr) + return; - _pointOpacityDatasetPickerAction.setFilterFunction([this](mv::Dataset dataset) -> bool { - return dataset->getDataType() == PointType; - }); + setupPositionDatasetPickerAction(scatterplotPlugin); +} - auto scatterplotPlugin = dynamic_cast(parent->parent()); +void DatasetsAction::connectToPublicAction(WidgetAction* publicAction, bool recursive) +{ + auto publicDatasetsAction = dynamic_cast(publicAction); - if (scatterplotPlugin == nullptr) + Q_ASSERT(publicDatasetsAction != nullptr); + + if (publicDatasetsAction == nullptr) + return; + + if (recursive) { + actions().connectPrivateActionToPublicAction(&_positionDatasetPickerAction, &publicDatasetsAction->getPositionDatasetPickerAction(), recursive); + actions().connectPrivateActionToPublicAction(&_colorDatasetPickerAction, &publicDatasetsAction->getColorDatasetPickerAction(), recursive); + actions().connectPrivateActionToPublicAction(&_pointSizeDatasetPickerAction, &publicDatasetsAction->getPointSizeDatasetPickerAction(), recursive); + actions().connectPrivateActionToPublicAction(&_pointOpacityDatasetPickerAction, &publicDatasetsAction->getPointOpacityDatasetPickerAction(), recursive); + } + + GroupAction::connectToPublicAction(publicAction, recursive); +} + +void DatasetsAction::disconnectFromPublicAction(bool recursive) +{ + if (!isConnected()) return; + if (recursive) { + actions().disconnectPrivateActionFromPublicAction(&_positionDatasetPickerAction, recursive); + actions().disconnectPrivateActionFromPublicAction(&_colorDatasetPickerAction, recursive); + actions().disconnectPrivateActionFromPublicAction(&_pointSizeDatasetPickerAction, recursive); + actions().disconnectPrivateActionFromPublicAction(&_pointOpacityDatasetPickerAction, recursive); + } + + GroupAction::disconnectFromPublicAction(recursive); +} + +void DatasetsAction::fromVariantMap(const QVariantMap& variantMap) +{ + GroupAction::fromVariantMap(variantMap); + + _positionDatasetPickerAction.fromParentVariantMap(variantMap); + _colorDatasetPickerAction.fromParentVariantMap(variantMap); +} + +QVariantMap DatasetsAction::toVariantMap() const +{ + auto variantMap = GroupAction::toVariantMap(); + + _positionDatasetPickerAction.insertIntoVariantMap(variantMap); + _colorDatasetPickerAction.insertIntoVariantMap(variantMap); + + return variantMap; +} + +void DatasetsAction::setupPositionDatasetPickerAction(ScatterplotPlugin* scatterplotPlugin) +{ + _positionDatasetPickerAction.setFilterFunction([this](mv::Dataset dataset) -> bool { + return dataset->getDataType() == PointType; + }); + connect(&_positionDatasetPickerAction, &DatasetPickerAction::datasetPicked, [this, scatterplotPlugin](Dataset pickedDataset) -> void { scatterplotPlugin->getPositionDataset() = pickedDataset; }); @@ -60,30 +107,70 @@ DatasetsAction::DatasetsAction(QObject* parent, const QString& title) : connect(&scatterplotPlugin->getPositionDataset(), &Dataset::changed, this, [this](DatasetImpl* dataset) -> void { _positionDatasetPickerAction.setCurrentDataset(dataset); }); +} - auto& coloringAction = scatterplotPlugin->getSettingsAction().getColoringAction(); +void DatasetsAction::setupColorDatasetPickerAction(ScatterplotPlugin* scatterplotPlugin) +{ + auto& settingsAction = scatterplotPlugin->getSettingsAction(); + + _colorDatasetPickerAction.setFilterFunction([this](mv::Dataset dataset) -> bool { + return (dataset->getDataType() == PointType || dataset->getDataType() == ColorType || dataset->getDataType() == ClusterType); + }); + + /* + auto& coloringAction = settingsAction.getColoringAction(); connect(&_colorDatasetPickerAction, &DatasetPickerAction::datasetPicked, [this, &coloringAction](Dataset pickedDataset) -> void { coloringAction.getColorByAction().setCurrentIndex(pickedDataset.isValid() ? 2 : 0); if (pickedDataset.isValid() && !mv::projects().isOpeningProject()) - coloringAction.setCurrentColorDataset(pickedDataset); - }); - - connect(&scatterplotPlugin->getSettingsAction().getColoringAction(), &ColoringAction::currentColorDatasetChanged, this, [this](Dataset currentColorDataset) -> void { + coloringAction.setCurrentColorDataset(pickedDataset); + }); + + connect(&settingsAction.getColoringAction(), &ColoringAction::currentColorDatasetChanged, this, [this](Dataset currentColorDataset) -> void { _colorDatasetPickerAction.setCurrentDataset(currentColorDataset); + }); + + const auto currentColorDatasetChanged = [this](Dataset currentColorDataset) -> void { + if (_colorDataset.isValid()) + disconnect(&_colorDataset, &Dataset<>::guiNameChanged, this, nullptr); + + _colorDataset = currentColorDataset; + + connect(&_colorDataset, &Dataset<>::guiNameChanged, this, &ScatterplotPlugin::updateHeadsUpDisplay); + + //updateHeadsUpDisplay(); + }; + + connect(&settingsAction.getColoringAction(), &ColoringAction::currentColorDatasetChanged, this, currentColorDatasetChanged); + + connect(&settingsAction.getColoringAction().getColorByAction(), &OptionAction::currentIndexChanged, this, [this, &settingsAction, currentColorDatasetChanged](const std::int32_t& currentIndex) -> void { + _colorDataset = settingsAction.getColoringAction().getCurrentColorDataset(); + + updateHeadsUpDisplay(); }); + */ +} - auto& pointPlotAction = scatterplotPlugin->getSettingsAction().getPlotAction().getPointPlotAction(); - auto& pointSizeAction = pointPlotAction.getSizeAction(); - auto& pointOpacityAction = pointPlotAction.getOpacityAction(); +void DatasetsAction::setupPointSizeDataset(ScatterplotPlugin* scatterplotPlugin) +{ + auto& settingsAction = scatterplotPlugin->getSettingsAction(); + + _pointSizeDatasetPickerAction.setFilterFunction([this](mv::Dataset dataset) -> bool { + return dataset->getDataType() == PointType; + }); + + /* + auto& pointPlotAction = scatterplotPlugin->getSettingsAction().getPlotAction().getPointPlotAction(); + auto& pointSizeAction = pointPlotAction.getSizeAction(); + auto& pointOpacityAction = pointPlotAction.getOpacityAction(); const auto pointSizeSourceChanged = [this, &pointSizeAction]() -> void { _pointSizeDatasetPickerAction.setCurrentDataset(pointSizeAction.isSourceDataset() ? pointSizeAction.getCurrentDataset() : nullptr); if (!pointSizeAction.isSourceDataset()) _pointSizeDatasetPickerAction.setCurrentIndex(-1); - }; + }; connect(&pointSizeAction, &ScalarAction::sourceSelectionChanged, this, pointSizeSourceChanged); connect(&pointSizeAction, &ScalarAction::sourceDataChanged, this, pointSizeSourceChanged); @@ -93,14 +180,14 @@ DatasetsAction::DatasetsAction(QObject* parent, const QString& title) : if (currentIndex < 0) pointSizeAction.setCurrentSourceIndex(ScalarSourceModel::DefaultRow::Constant); - }); + }); const auto pointOpacitySourceChanged = [this, &pointOpacityAction]() -> void { _pointOpacityDatasetPickerAction.setCurrentDataset(pointOpacityAction.isSourceDataset() ? pointOpacityAction.getCurrentDataset() : nullptr); if (!pointOpacityAction.isSourceDataset()) _pointOpacityDatasetPickerAction.setCurrentIndex(-1); - }; + }; connect(&pointOpacityAction, &ScalarAction::sourceSelectionChanged, this, pointOpacitySourceChanged); connect(&pointOpacityAction, &ScalarAction::sourceDataChanged, this, pointOpacitySourceChanged); @@ -110,57 +197,47 @@ DatasetsAction::DatasetsAction(QObject* parent, const QString& title) : if (currentIndex < 0) pointOpacityAction.setCurrentSourceIndex(ScalarSourceModel::DefaultRow::Constant); - }); -} + }); -void DatasetsAction::connectToPublicAction(WidgetAction* publicAction, bool recursive) -{ - auto publicDatasetsAction = dynamic_cast(publicAction); - Q_ASSERT(publicDatasetsAction != nullptr); - if (publicDatasetsAction == nullptr) - return; + const auto currentPointSizeDatasetChanged = [this]() -> void { + auto currentPointSizeDataset = _settingsAction.getPlotAction().getPointPlotAction().getSizeAction().getCurrentDataset(); - if (recursive) { - actions().connectPrivateActionToPublicAction(&_positionDatasetPickerAction, &publicDatasetsAction->getPositionDatasetPickerAction(), recursive); - actions().connectPrivateActionToPublicAction(&_colorDatasetPickerAction, &publicDatasetsAction->getColorDatasetPickerAction(), recursive); - actions().connectPrivateActionToPublicAction(&_pointSizeDatasetPickerAction, &publicDatasetsAction->getPointSizeDatasetPickerAction(), recursive); - actions().connectPrivateActionToPublicAction(&_pointOpacityDatasetPickerAction, &publicDatasetsAction->getPointOpacityDatasetPickerAction(), recursive); - } + if (_pointSizeDataset.isValid()) + disconnect(&_pointSizeDataset, &Dataset<>::guiNameChanged, this, nullptr); - GroupAction::connectToPublicAction(publicAction, recursive); -} + _pointSizeDataset = currentPointSizeDataset; -void DatasetsAction::disconnectFromPublicAction(bool recursive) -{ - if (!isConnected()) - return; + connect(&_pointSizeDataset, &Dataset<>::guiNameChanged, this, &ScatterplotPlugin::updateHeadsUpDisplay); - if (recursive) { - actions().disconnectPrivateActionFromPublicAction(&_positionDatasetPickerAction, recursive); - actions().disconnectPrivateActionFromPublicAction(&_colorDatasetPickerAction, recursive); - actions().disconnectPrivateActionFromPublicAction(&_pointSizeDatasetPickerAction, recursive); - actions().disconnectPrivateActionFromPublicAction(&_pointOpacityDatasetPickerAction, recursive); - } + updateHeadsUpDisplay(); + }; - GroupAction::disconnectFromPublicAction(recursive); -} + connect(&_settingsAction.getPlotAction().getPointPlotAction().getSizeAction(), &ScalarAction::sourceSelectionChanged, this, currentPointSizeDatasetChanged); -void DatasetsAction::fromVariantMap(const QVariantMap& variantMap) -{ - GroupAction::fromVariantMap(variantMap); + const auto currentPointOpacityDatasetChanged = [this]() -> void { + auto currentPointOpacityDataset = _settingsAction.getPlotAction().getPointPlotAction().getOpacityAction().getCurrentDataset(); - _positionDatasetPickerAction.fromParentVariantMap(variantMap); - _colorDatasetPickerAction.fromParentVariantMap(variantMap); + if (_pointOpacityDataset.isValid()) + disconnect(&_pointOpacityDataset, &Dataset<>::guiNameChanged, this, nullptr); + + _pointOpacityDataset = currentPointOpacityDataset; + + connect(&_pointOpacityDataset, &Dataset<>::guiNameChanged, this, &ScatterplotPlugin::updateHeadsUpDisplay); + + updateHeadsUpDisplay(); + }; + + connect(&_settingsAction.getPlotAction().getPointPlotAction().getOpacityAction(), &ScalarAction::sourceSelectionChanged, this, currentPointOpacityDatasetChanged);*/ } -QVariantMap DatasetsAction::toVariantMap() const +void DatasetsAction::setupPointOpacityDataset(ScatterplotPlugin* scatterplotPlugin) { - auto variantMap = GroupAction::toVariantMap(); + auto& settingsAction = scatterplotPlugin->getSettingsAction(); - _positionDatasetPickerAction.insertIntoVariantMap(variantMap); - _colorDatasetPickerAction.insertIntoVariantMap(variantMap); + _pointOpacityDatasetPickerAction.setFilterFunction([this](mv::Dataset dataset) -> bool { + return dataset->getDataType() == PointType; + }); - return variantMap; -} \ No newline at end of file +} diff --git a/src/DatasetsAction.h b/src/DatasetsAction.h index db10a3e..921965e 100644 --- a/src/DatasetsAction.h +++ b/src/DatasetsAction.h @@ -20,6 +20,10 @@ class DatasetsAction : public GroupAction */ Q_INVOKABLE DatasetsAction(QObject* parent, const QString& title); + mv::Dataset getColorDataset() { return _colorDataset; } + mv::Dataset getPointSizeDataset() { return _pointSizeDataset; } + mv::Dataset getPointOpacityDataset() { return _pointOpacityDataset; } + protected: // Linking /** @@ -39,7 +43,7 @@ class DatasetsAction : public GroupAction /** * Load widget action from variant map - * @param Variant map representation of the widget action + * @param variantMap Variant map representation of the widget action */ void fromVariantMap(const QVariantMap& variantMap) override; @@ -49,6 +53,13 @@ class DatasetsAction : public GroupAction */ QVariantMap toVariantMap() const override; +private: + + void setupPositionDatasetPickerAction(ScatterplotPlugin* scatterplotPlugin); /** Set up the position dataset picker action */ + void setupColorDatasetPickerAction(ScatterplotPlugin* scatterplotPlugin); /** Set up the color dataset picker action */ + void setupPointSizeDataset(ScatterplotPlugin* scatterplotPlugin); /** Set up the point size dataset picker action */ + void setupPointOpacityDataset(ScatterplotPlugin* scatterplotPlugin); /** Set up the point opacity dataset picker action */ + public: // Action getters DatasetPickerAction& getPositionDatasetPickerAction() { return _positionDatasetPickerAction; } @@ -57,11 +68,14 @@ class DatasetsAction : public GroupAction DatasetPickerAction& getPointOpacityDatasetPickerAction() { return _pointOpacityDatasetPickerAction; } private: - ScatterplotPlugin* _scatterplotPlugin; /** Pointer to scatter plot plugin */ - DatasetPickerAction _positionDatasetPickerAction; /** Dataset picker action for position dataset */ - DatasetPickerAction _colorDatasetPickerAction; /** Dataset picker action for color dataset */ - DatasetPickerAction _pointSizeDatasetPickerAction; /** Dataset picker action for point size */ - DatasetPickerAction _pointOpacityDatasetPickerAction; /** Dataset picker action for point opacity */ + ScatterplotPlugin* _scatterplotPlugin; /** Pointer to scatter plot plugin */ + DatasetPickerAction _positionDatasetPickerAction; /** Dataset picker action for position dataset */ + DatasetPickerAction _colorDatasetPickerAction; /** Dataset picker action for color dataset */ + DatasetPickerAction _pointSizeDatasetPickerAction; /** Dataset picker action for point size */ + DatasetPickerAction _pointOpacityDatasetPickerAction; /** Dataset picker action for point opacity */ + mv::Dataset _colorDataset; /** Smart pointer to dataset used for coloring (if any) */ + mv::Dataset _pointSizeDataset; /** Smart pointer to dataset for driving point size (if any) */ + mv::Dataset _pointOpacityDataset; /** Smart pointer to dataset for driving point opacity (if any) */ friend class mv::AbstractActionsManager; }; diff --git a/src/ScatterplotPlugin.cpp b/src/ScatterplotPlugin.cpp index 9adb621..8bf3f94 100644 --- a/src/ScatterplotPlugin.cpp +++ b/src/ScatterplotPlugin.cpp @@ -343,52 +343,6 @@ void ScatterplotPlugin::init() connect(&_positionDataset, &Dataset::dataSelectionChanged, this, &ScatterplotPlugin::updateSelection); connect(&_positionDataset, &Dataset<>::guiNameChanged, this, &ScatterplotPlugin::updateHeadsUpDisplay); - const auto currentColorDatasetChanged = [this](Dataset currentColorDataset) -> void { - if (_colorDataset.isValid()) - disconnect(&_colorDataset, &Dataset<>::guiNameChanged, this, nullptr); - - _colorDataset = currentColorDataset; - - connect(&_colorDataset, &Dataset<>::guiNameChanged, this, &ScatterplotPlugin::updateHeadsUpDisplay); - - updateHeadsUpDisplay(); - }; - - connect(&_settingsAction.getColoringAction(), &ColoringAction::currentColorDatasetChanged, this, currentColorDatasetChanged); - connect(&_settingsAction.getColoringAction().getColorByAction(), &OptionAction::currentIndexChanged, this, [this, currentColorDatasetChanged](const std::int32_t& currentIndex) -> void { - currentColorDatasetChanged(_settingsAction.getColoringAction().getCurrentColorDataset()); - }); - - const auto currentPointSizeDatasetChanged = [this]() -> void { - auto currentPointSizeDataset = _settingsAction.getPlotAction().getPointPlotAction().getSizeAction().getCurrentDataset(); - - if (_pointSizeDataset.isValid()) - disconnect(&_pointSizeDataset, &Dataset<>::guiNameChanged, this, nullptr); - - _pointSizeDataset = currentPointSizeDataset; - - connect(&_pointSizeDataset, &Dataset<>::guiNameChanged, this, &ScatterplotPlugin::updateHeadsUpDisplay); - - updateHeadsUpDisplay(); - }; - - connect(&_settingsAction.getPlotAction().getPointPlotAction().getSizeAction(), &ScalarAction::sourceSelectionChanged, this, currentPointSizeDatasetChanged); - - const auto currentPointOpacityDatasetChanged = [this]() -> void { - auto currentPointOpacityDataset = _settingsAction.getPlotAction().getPointPlotAction().getOpacityAction().getCurrentDataset(); - - if (_pointOpacityDataset.isValid()) - disconnect(&_pointOpacityDataset, &Dataset<>::guiNameChanged, this, nullptr); - - _pointOpacityDataset = currentPointOpacityDataset; - - connect(&_pointOpacityDataset, &Dataset<>::guiNameChanged, this, &ScatterplotPlugin::updateHeadsUpDisplay); - - updateHeadsUpDisplay(); - }; - - connect(&_settingsAction.getPlotAction().getPointPlotAction().getOpacityAction(), &ScalarAction::sourceSelectionChanged, this, currentPointOpacityDatasetChanged); - connect(&_settingsAction.getMiscellaneousAction().getBackgroundColorAction(), &ColorAction::colorChanged, this, &ScatterplotPlugin::updateHeadsUpDisplayTextColor); connect(&getScatterplotWidget().getPointRendererNavigator().getNavigationAction().getZoomSelectionAction(), &TriggerAction::triggered, this, [this]() -> void { @@ -1047,6 +1001,8 @@ void ScatterplotPlugin::updateHeadsUpDisplay() getHeadsUpDisplayAction().removeAllHeadsUpDisplayItems(); + auto& coloringAction = _settingsAction.getColoringAction(); + if (_positionDataset.isValid()) { const auto datasetsItem = getHeadsUpDisplayAction().addHeadsUpDisplayItem("Datasets", "", ""); @@ -1057,8 +1013,8 @@ void ScatterplotPlugin::updateHeadsUpDisplay() getHeadsUpDisplayAction().addHeadsUpDisplayItem(QString("%1 by:").arg(metaDataName), data->getGuiName(), "", itemPtr); }; - if (_settingsAction.getColoringAction().getColorByAction().getCurrentIndex() >= 2) - addMetaDataToHeadsUpDisplay("Color", _colorDataset, datasetsItem); + if (coloringAction.getColorByAction().getCurrentIndex() >= 2) + addMetaDataToHeadsUpDisplay("Color", coloringAction.getCurrentColorDataset(), datasetsItem); addMetaDataToHeadsUpDisplay("Size", _settingsAction.getPlotAction().getPointPlotAction().getSizeAction().getCurrentDataset(), datasetsItem); addMetaDataToHeadsUpDisplay("Opacity", _settingsAction.getPlotAction().getPointPlotAction().getOpacityAction().getCurrentDataset(), datasetsItem); diff --git a/src/ScatterplotPlugin.h b/src/ScatterplotPlugin.h index 301364d..e795c72 100644 --- a/src/ScatterplotPlugin.h +++ b/src/ScatterplotPlugin.h @@ -116,9 +116,6 @@ class ScatterplotPlugin : public ViewPlugin ScatterplotWidget* _scatterPlotWidget; /** The visualization widget */ Dataset _positionDataset; /** Smart pointer to points dataset for point position */ Dataset _positionSourceDataset; /** Smart pointer to source of the points dataset for point position (if any) */ - Dataset _colorDataset; /** Smart pointer to dataset used for coloring (if any) */ - Dataset _pointSizeDataset; /** Smart pointer to dataset for driving point size (if any) */ - Dataset _pointOpacityDataset; /** Smart pointer to dataset for driving point opacity (if any) */ std::vector _positions; /** Point positions */ unsigned int _numPoints; /** Number of point positions */ SettingsAction _settingsAction; /** Group action for all settings */ From 3c63e44373f0033bbc92498be982f6c21118d1dc Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Wed, 18 Feb 2026 10:54:35 +0100 Subject: [PATCH 10/16] Connect dataset pickers to HUD update Hook dataset picker changes to the heads-up display so the HUD updates immediately when datasets are picked. Adds connections from position, color, point size, and point opacity DatasetPickerAction::datasetPicked signals to ScatterplotPlugin::updateHeadsUpDisplay in init() (src/ScatterplotPlugin.cpp). --- src/ScatterplotPlugin.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/ScatterplotPlugin.cpp b/src/ScatterplotPlugin.cpp index 8bf3f94..4504d54 100644 --- a/src/ScatterplotPlugin.cpp +++ b/src/ScatterplotPlugin.cpp @@ -398,6 +398,11 @@ void ScatterplotPlugin::init() }); #endif + connect(&_settingsAction.getDatasetsAction().getPositionDatasetPickerAction(), &DatasetPickerAction::datasetPicked, this, &ScatterplotPlugin::updateHeadsUpDisplay); + connect(&_settingsAction.getDatasetsAction().getColorDatasetPickerAction(), &DatasetPickerAction::datasetPicked, this, &ScatterplotPlugin::updateHeadsUpDisplay); + connect(&_settingsAction.getDatasetsAction().getPointSizeDatasetPickerAction(), &DatasetPickerAction::datasetPicked, this, &ScatterplotPlugin::updateHeadsUpDisplay); + connect(&_settingsAction.getDatasetsAction().getPointOpacityDatasetPickerAction(), &DatasetPickerAction::datasetPicked, this, &ScatterplotPlugin::updateHeadsUpDisplay); + updateHeadsUpDisplay(); updateHeadsUpDisplayTextColor(); } From 361e6d5a6367a9cd43c10fab204fbb8746b9ea07 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Thu, 19 Feb 2026 08:12:58 +0100 Subject: [PATCH 11/16] Refactor dataset pickers and HUD updates Add and wire up dataset picker actions for color, point size and point opacity; rename setup helpers to *PickerAction variants and re-run setup when a project is opened. Replace older datasetPicked connections with currentIndexChanged/sourceSelectionChanged signals for more reliable heads-up-display (HUD) updates, and connect dataset guiNameChanged directly to ScatterplotPlugin::updateHeadsUpDisplay. Add filter checks to ensure point-size/opacity datasets match position dataset sizes. Clean up commented/old code and move updateHeadsUpDisplayTextColor visibility to align with the refactor. --- src/DatasetsAction.cpp | 137 ++++++++++++++++---------------------- src/DatasetsAction.h | 8 +-- src/ScatterplotPlugin.cpp | 18 +++-- src/ScatterplotPlugin.h | 4 +- 4 files changed, 76 insertions(+), 91 deletions(-) diff --git a/src/DatasetsAction.cpp b/src/DatasetsAction.cpp index 4b8f02a..ea0b704 100644 --- a/src/DatasetsAction.cpp +++ b/src/DatasetsAction.cpp @@ -40,6 +40,16 @@ DatasetsAction::DatasetsAction(QObject* parent, const QString& title) : return; setupPositionDatasetPickerAction(scatterplotPlugin); + setupColorDatasetPickerAction(scatterplotPlugin); + setupPointSizeDatasetPickerAction(scatterplotPlugin); + setupPointOpacityDatasetPickerAction(scatterplotPlugin); + + connect(&mv::projects(), &AbstractProjectManager::projectOpened, this, [this, scatterplotPlugin]() -> void { + setupPositionDatasetPickerAction(scatterplotPlugin); + setupColorDatasetPickerAction(scatterplotPlugin); + setupPointSizeDatasetPickerAction(scatterplotPlugin); + setupPointOpacityDatasetPickerAction(scatterplotPlugin); + }); } void DatasetsAction::connectToPublicAction(WidgetAction* publicAction, bool recursive) @@ -117,42 +127,28 @@ void DatasetsAction::setupColorDatasetPickerAction(ScatterplotPlugin* scatterplo return (dataset->getDataType() == PointType || dataset->getDataType() == ColorType || dataset->getDataType() == ClusterType); }); - /* auto& coloringAction = settingsAction.getColoringAction(); - connect(&_colorDatasetPickerAction, &DatasetPickerAction::datasetPicked, [this, &coloringAction](Dataset pickedDataset) -> void { - coloringAction.getColorByAction().setCurrentIndex(pickedDataset.isValid() ? 2 : 0); - - if (pickedDataset.isValid() && !mv::projects().isOpeningProject()) - coloringAction.setCurrentColorDataset(pickedDataset); - }); - - connect(&settingsAction.getColoringAction(), &ColoringAction::currentColorDatasetChanged, this, [this](Dataset currentColorDataset) -> void { - _colorDatasetPickerAction.setCurrentDataset(currentColorDataset); - }); - - const auto currentColorDatasetChanged = [this](Dataset currentColorDataset) -> void { + connect(&_colorDatasetPickerAction, &DatasetPickerAction::datasetPicked, [this, &coloringAction, scatterplotPlugin](Dataset pickedDataset) -> void { if (_colorDataset.isValid()) disconnect(&_colorDataset, &Dataset<>::guiNameChanged, this, nullptr); - _colorDataset = currentColorDataset; - - connect(&_colorDataset, &Dataset<>::guiNameChanged, this, &ScatterplotPlugin::updateHeadsUpDisplay); + _colorDataset = pickedDataset; - //updateHeadsUpDisplay(); - }; + connect(&_colorDataset, &Dataset<>::guiNameChanged, scatterplotPlugin, &ScatterplotPlugin::updateHeadsUpDisplay); - connect(&settingsAction.getColoringAction(), &ColoringAction::currentColorDatasetChanged, this, currentColorDatasetChanged); + coloringAction.setCurrentColorDataset(pickedDataset); - connect(&settingsAction.getColoringAction().getColorByAction(), &OptionAction::currentIndexChanged, this, [this, &settingsAction, currentColorDatasetChanged](const std::int32_t& currentIndex) -> void { - _colorDataset = settingsAction.getColoringAction().getCurrentColorDataset(); + if (!pickedDataset.isValid()) + coloringAction.getColorByAction().setCurrentIndex(0); + }); - updateHeadsUpDisplay(); + connect(&settingsAction.getColoringAction(), &ColoringAction::currentColorDatasetChanged, this, [this](Dataset currentColorDataset) -> void { + _colorDatasetPickerAction.setCurrentDataset(currentColorDataset); }); - */ } -void DatasetsAction::setupPointSizeDataset(ScatterplotPlugin* scatterplotPlugin) +void DatasetsAction::setupPointSizeDatasetPickerAction(ScatterplotPlugin* scatterplotPlugin) { auto& settingsAction = scatterplotPlugin->getSettingsAction(); @@ -160,84 +156,65 @@ void DatasetsAction::setupPointSizeDataset(ScatterplotPlugin* scatterplotPlugin) return dataset->getDataType() == PointType; }); - /* - auto& pointPlotAction = scatterplotPlugin->getSettingsAction().getPlotAction().getPointPlotAction(); - auto& pointSizeAction = pointPlotAction.getSizeAction(); - auto& pointOpacityAction = pointPlotAction.getOpacityAction(); + auto& pointPlotAction = settingsAction.getPlotAction().getPointPlotAction(); + auto& pointSizeAction = pointPlotAction.getSizeAction(); + + + connect(&pointSizeAction, &ScalarAction::sourceSelectionChanged, this, [this, &pointSizeAction, scatterplotPlugin](const uint32_t& sourceSelectionIndex) -> void { + if (_pointSizeDataset.isValid()) + disconnect(&_pointSizeDataset, &Dataset<>::guiNameChanged, this, nullptr); + + _pointSizeDataset = pointSizeAction.getCurrentDataset(); + + connect(&_pointSizeDataset, &Dataset<>::guiNameChanged, scatterplotPlugin, &ScatterplotPlugin::updateHeadsUpDisplay); - const auto pointSizeSourceChanged = [this, &pointSizeAction]() -> void { _pointSizeDatasetPickerAction.setCurrentDataset(pointSizeAction.isSourceDataset() ? pointSizeAction.getCurrentDataset() : nullptr); if (!pointSizeAction.isSourceDataset()) _pointSizeDatasetPickerAction.setCurrentIndex(-1); - }; - - connect(&pointSizeAction, &ScalarAction::sourceSelectionChanged, this, pointSizeSourceChanged); - connect(&pointSizeAction, &ScalarAction::sourceDataChanged, this, pointSizeSourceChanged); + }); connect(&_pointSizeDatasetPickerAction, &DatasetPickerAction::currentIndexChanged, this, [this, &pointSizeAction](const int32_t& currentIndex) -> void { pointSizeAction.setCurrentDataset(_pointSizeDatasetPickerAction.getCurrentDataset()); if (currentIndex < 0) pointSizeAction.setCurrentSourceIndex(ScalarSourceModel::DefaultRow::Constant); - }); - - const auto pointOpacitySourceChanged = [this, &pointOpacityAction]() -> void { - _pointOpacityDatasetPickerAction.setCurrentDataset(pointOpacityAction.isSourceDataset() ? pointOpacityAction.getCurrentDataset() : nullptr); - - if (!pointOpacityAction.isSourceDataset()) - _pointOpacityDatasetPickerAction.setCurrentIndex(-1); - }; - - connect(&pointOpacityAction, &ScalarAction::sourceSelectionChanged, this, pointOpacitySourceChanged); - connect(&pointOpacityAction, &ScalarAction::sourceDataChanged, this, pointOpacitySourceChanged); - - connect(&_pointOpacityDatasetPickerAction, &DatasetPickerAction::currentIndexChanged, this, [this, &pointOpacityAction](const int32_t& currentIndex) -> void { - pointOpacityAction.setCurrentDataset(_pointOpacityDatasetPickerAction.getCurrentDataset()); - - if (currentIndex < 0) - pointOpacityAction.setCurrentSourceIndex(ScalarSourceModel::DefaultRow::Constant); - }); - - + }); +} - const auto currentPointSizeDatasetChanged = [this]() -> void { - auto currentPointSizeDataset = _settingsAction.getPlotAction().getPointPlotAction().getSizeAction().getCurrentDataset(); +void DatasetsAction::setupPointOpacityDatasetPickerAction(ScatterplotPlugin* scatterplotPlugin) +{ + auto& settingsAction = scatterplotPlugin->getSettingsAction(); + auto& pointPlotAction = settingsAction.getPlotAction().getPointPlotAction(); + auto& pointOpacityAction = pointPlotAction.getOpacityAction(); - if (_pointSizeDataset.isValid()) - disconnect(&_pointSizeDataset, &Dataset<>::guiNameChanged, this, nullptr); + _pointOpacityDatasetPickerAction.setFilterFunction([this, scatterplotPlugin](mv::Dataset dataset) -> bool { + if (dataset->getDataType() != PointType) + return false; + + const auto positionDataset = scatterplotPlugin->getPositionSourceDataset(); - _pointSizeDataset = currentPointSizeDataset; + if (!positionDataset.isValid()) + return false; - connect(&_pointSizeDataset, &Dataset<>::guiNameChanged, this, &ScatterplotPlugin::updateHeadsUpDisplay); + const mv::Dataset candidatePoints(dataset); - updateHeadsUpDisplay(); - }; + if (candidatePoints->getNumPoints() != positionDataset->getNumPoints()) + return false; - connect(&_settingsAction.getPlotAction().getPointPlotAction().getSizeAction(), &ScalarAction::sourceSelectionChanged, this, currentPointSizeDatasetChanged); + return true; + }); - const auto currentPointOpacityDatasetChanged = [this]() -> void { - auto currentPointOpacityDataset = _settingsAction.getPlotAction().getPointPlotAction().getOpacityAction().getCurrentDataset(); + connect(&_pointOpacityDatasetPickerAction, &DatasetPickerAction::datasetPicked, this, [this, &pointPlotAction, &pointOpacityAction, scatterplotPlugin](mv::Dataset<> pickedDataset) -> void { + const auto& pointOpacityDataset = _pointOpacityDatasetPickerAction.getCurrentDataset(); - if (_pointOpacityDataset.isValid()) + if (pointOpacityDataset.isValid()) disconnect(&_pointOpacityDataset, &Dataset<>::guiNameChanged, this, nullptr); - _pointOpacityDataset = currentPointOpacityDataset; + _pointOpacityDataset = pointOpacityDataset; - connect(&_pointOpacityDataset, &Dataset<>::guiNameChanged, this, &ScatterplotPlugin::updateHeadsUpDisplay); - - updateHeadsUpDisplay(); - }; - - connect(&_settingsAction.getPlotAction().getPointPlotAction().getOpacityAction(), &ScalarAction::sourceSelectionChanged, this, currentPointOpacityDatasetChanged);*/ -} + connect(&_pointOpacityDataset, &Dataset<>::guiNameChanged, scatterplotPlugin, &ScatterplotPlugin::updateHeadsUpDisplay); -void DatasetsAction::setupPointOpacityDataset(ScatterplotPlugin* scatterplotPlugin) -{ - auto& settingsAction = scatterplotPlugin->getSettingsAction(); - - _pointOpacityDatasetPickerAction.setFilterFunction([this](mv::Dataset dataset) -> bool { - return dataset->getDataType() == PointType; + pointPlotAction.setCurrentPointOpacityDataset(_pointOpacityDataset); }); - } diff --git a/src/DatasetsAction.h b/src/DatasetsAction.h index 921965e..cc23218 100644 --- a/src/DatasetsAction.h +++ b/src/DatasetsAction.h @@ -55,10 +55,10 @@ class DatasetsAction : public GroupAction private: - void setupPositionDatasetPickerAction(ScatterplotPlugin* scatterplotPlugin); /** Set up the position dataset picker action */ - void setupColorDatasetPickerAction(ScatterplotPlugin* scatterplotPlugin); /** Set up the color dataset picker action */ - void setupPointSizeDataset(ScatterplotPlugin* scatterplotPlugin); /** Set up the point size dataset picker action */ - void setupPointOpacityDataset(ScatterplotPlugin* scatterplotPlugin); /** Set up the point opacity dataset picker action */ + void setupPositionDatasetPickerAction(ScatterplotPlugin* scatterplotPlugin); /** Set up the position dataset picker action */ + void setupColorDatasetPickerAction(ScatterplotPlugin* scatterplotPlugin); /** Set up the color dataset picker action */ + void setupPointSizeDatasetPickerAction(ScatterplotPlugin* scatterplotPlugin); /** Set up the point size dataset picker action */ + void setupPointOpacityDatasetPickerAction(ScatterplotPlugin* scatterplotPlugin); /** Set up the point opacity dataset picker action */ public: // Action getters diff --git a/src/ScatterplotPlugin.cpp b/src/ScatterplotPlugin.cpp index 4504d54..63a9e7b 100644 --- a/src/ScatterplotPlugin.cpp +++ b/src/ScatterplotPlugin.cpp @@ -398,10 +398,13 @@ void ScatterplotPlugin::init() }); #endif - connect(&_settingsAction.getDatasetsAction().getPositionDatasetPickerAction(), &DatasetPickerAction::datasetPicked, this, &ScatterplotPlugin::updateHeadsUpDisplay); - connect(&_settingsAction.getDatasetsAction().getColorDatasetPickerAction(), &DatasetPickerAction::datasetPicked, this, &ScatterplotPlugin::updateHeadsUpDisplay); - connect(&_settingsAction.getDatasetsAction().getPointSizeDatasetPickerAction(), &DatasetPickerAction::datasetPicked, this, &ScatterplotPlugin::updateHeadsUpDisplay); - connect(&_settingsAction.getDatasetsAction().getPointOpacityDatasetPickerAction(), &DatasetPickerAction::datasetPicked, this, &ScatterplotPlugin::updateHeadsUpDisplay); + auto& datasetsAction = _settingsAction.getDatasetsAction(); + auto& pointPlotAction = _settingsAction.getPlotAction().getPointPlotAction(); + + connect(&datasetsAction.getPositionDatasetPickerAction(), &DatasetPickerAction::currentIndexChanged, this, &ScatterplotPlugin::updateHeadsUpDisplay); + connect(&datasetsAction.getColorDatasetPickerAction(), &DatasetPickerAction::currentIndexChanged, this, &ScatterplotPlugin::updateHeadsUpDisplay); + connect(&datasetsAction.getPointSizeDatasetPickerAction(), &DatasetPickerAction::currentIndexChanged, this, &ScatterplotPlugin::updateHeadsUpDisplay); + connect(&pointPlotAction.getOpacityAction(), &ScalarAction::sourceSelectionChanged, this, &ScatterplotPlugin::updateHeadsUpDisplay); updateHeadsUpDisplay(); updateHeadsUpDisplayTextColor(); @@ -1021,8 +1024,11 @@ void ScatterplotPlugin::updateHeadsUpDisplay() if (coloringAction.getColorByAction().getCurrentIndex() >= 2) addMetaDataToHeadsUpDisplay("Color", coloringAction.getCurrentColorDataset(), datasetsItem); - addMetaDataToHeadsUpDisplay("Size", _settingsAction.getPlotAction().getPointPlotAction().getSizeAction().getCurrentDataset(), datasetsItem); - addMetaDataToHeadsUpDisplay("Opacity", _settingsAction.getPlotAction().getPointPlotAction().getOpacityAction().getCurrentDataset(), datasetsItem); + auto& pointPlotAction = _settingsAction.getPlotAction().getPointPlotAction(); + + //qDebug() << "ScatterplotPlugin::updateHeadsUpDisplay: point size dataset: " << pointPlotAction.getSizeAction().getCurrentDataset().isValid() << ", opacity dataset: " << pointPlotAction.getOpacityAction().getCurrentDataset().isValid(); + addMetaDataToHeadsUpDisplay("Size", pointPlotAction.getSizeAction().getCurrentDataset(), datasetsItem); + addMetaDataToHeadsUpDisplay("Opacity", pointPlotAction.getOpacityAction().getCurrentDataset(), datasetsItem); } else { getHeadsUpDisplayAction().addHeadsUpDisplayItem("No datasets loaded", "", ""); } diff --git a/src/ScatterplotPlugin.h b/src/ScatterplotPlugin.h index e795c72..a3966c5 100644 --- a/src/ScatterplotPlugin.h +++ b/src/ScatterplotPlugin.h @@ -93,9 +93,11 @@ class ScatterplotPlugin : public ViewPlugin private: void updateData(); void updateSelection(); + void updateHeadsUpDisplayTextColor(); + +public: void updateHeadsUpDisplay(); - void updateHeadsUpDisplayTextColor(); public: // Serialization From 5ae5fa96bd7bc741c7e77630493ed03fd92a6e5d Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Thu, 19 Feb 2026 08:26:25 +0100 Subject: [PATCH 12/16] Refactor dataset picker setup and opacity sync Extracted repeated dataset picker initializations into setupDatasetPickerActions and call it on construction and when a project opens to reduce duplication. Switched the point opacity picker connection to currentIndexChanged and added logic to synchronize the picker with the ScalarAction (set default Constant source when no dataset, update picker when sourceSelection changes, clear index when not using a dataset). Added descriptive comments for the new helper and existing setup methods in the header. --- src/DatasetsAction.cpp | 32 ++++++++++++++++++++++---------- src/DatasetsAction.h | 35 ++++++++++++++++++++++++++++++----- 2 files changed, 52 insertions(+), 15 deletions(-) diff --git a/src/DatasetsAction.cpp b/src/DatasetsAction.cpp index ea0b704..9ef2cd4 100644 --- a/src/DatasetsAction.cpp +++ b/src/DatasetsAction.cpp @@ -39,17 +39,11 @@ DatasetsAction::DatasetsAction(QObject* parent, const QString& title) : if (scatterplotPlugin == nullptr) return; - setupPositionDatasetPickerAction(scatterplotPlugin); - setupColorDatasetPickerAction(scatterplotPlugin); - setupPointSizeDatasetPickerAction(scatterplotPlugin); - setupPointOpacityDatasetPickerAction(scatterplotPlugin); + setupDatasetPickerActions(scatterplotPlugin); connect(&mv::projects(), &AbstractProjectManager::projectOpened, this, [this, scatterplotPlugin]() -> void { - setupPositionDatasetPickerAction(scatterplotPlugin); - setupColorDatasetPickerAction(scatterplotPlugin); - setupPointSizeDatasetPickerAction(scatterplotPlugin); - setupPointOpacityDatasetPickerAction(scatterplotPlugin); - }); + setupDatasetPickerActions(scatterplotPlugin); + }); } void DatasetsAction::connectToPublicAction(WidgetAction* publicAction, bool recursive) @@ -104,6 +98,14 @@ QVariantMap DatasetsAction::toVariantMap() const return variantMap; } +void DatasetsAction::setupDatasetPickerActions(ScatterplotPlugin* scatterplotPlugin) +{ + setupPositionDatasetPickerAction(scatterplotPlugin); + setupColorDatasetPickerAction(scatterplotPlugin); + setupPointSizeDatasetPickerAction(scatterplotPlugin); + setupPointOpacityDatasetPickerAction(scatterplotPlugin); +} + void DatasetsAction::setupPositionDatasetPickerAction(ScatterplotPlugin* scatterplotPlugin) { _positionDatasetPickerAction.setFilterFunction([this](mv::Dataset dataset) -> bool { @@ -205,7 +207,7 @@ void DatasetsAction::setupPointOpacityDatasetPickerAction(ScatterplotPlugin* sca return true; }); - connect(&_pointOpacityDatasetPickerAction, &DatasetPickerAction::datasetPicked, this, [this, &pointPlotAction, &pointOpacityAction, scatterplotPlugin](mv::Dataset<> pickedDataset) -> void { + connect(&_pointOpacityDatasetPickerAction, &DatasetPickerAction::currentIndexChanged, this, [this, &pointPlotAction, &pointOpacityAction, scatterplotPlugin](const int32_t& currentIndex) -> void { const auto& pointOpacityDataset = _pointOpacityDatasetPickerAction.getCurrentDataset(); if (pointOpacityDataset.isValid()) @@ -216,5 +218,15 @@ void DatasetsAction::setupPointOpacityDatasetPickerAction(ScatterplotPlugin* sca connect(&_pointOpacityDataset, &Dataset<>::guiNameChanged, scatterplotPlugin, &ScatterplotPlugin::updateHeadsUpDisplay); pointPlotAction.setCurrentPointOpacityDataset(_pointOpacityDataset); + + if (!_pointOpacityDataset.isValid()) + pointOpacityAction.setCurrentSourceIndex(ScalarSourceModel::DefaultRow::Constant); + }); + + connect(&pointOpacityAction, &ScalarAction::sourceSelectionChanged, this, [this, &pointOpacityAction](const uint32_t& sourceSelectionIndex) -> void { + _pointOpacityDatasetPickerAction.setCurrentDataset(pointOpacityAction.isSourceDataset() ? pointOpacityAction.getCurrentDataset() : nullptr); + + if (!pointOpacityAction.isSourceDataset()) + _pointOpacityDatasetPickerAction.setCurrentIndex(-1); }); } diff --git a/src/DatasetsAction.h b/src/DatasetsAction.h index cc23218..82bd03c 100644 --- a/src/DatasetsAction.h +++ b/src/DatasetsAction.h @@ -53,12 +53,37 @@ class DatasetsAction : public GroupAction */ QVariantMap toVariantMap() const override; -private: +private: // Dataset picker action setup + + /** + * Set up the dataset picker actions with the datasets from the scatter plot plugin + * @param scatterplotPlugin Pointer to scatter plot plugin whose datasets are used to populate the dataset picker actions + */ + void setupDatasetPickerActions(ScatterplotPlugin* scatterplotPlugin); - void setupPositionDatasetPickerAction(ScatterplotPlugin* scatterplotPlugin); /** Set up the position dataset picker action */ - void setupColorDatasetPickerAction(ScatterplotPlugin* scatterplotPlugin); /** Set up the color dataset picker action */ - void setupPointSizeDatasetPickerAction(ScatterplotPlugin* scatterplotPlugin); /** Set up the point size dataset picker action */ - void setupPointOpacityDatasetPickerAction(ScatterplotPlugin* scatterplotPlugin); /** Set up the point opacity dataset picker action */ + /** + * Set up the position dataset picker action with the position datasets from the scatter plot plugin + * @param scatterplotPlugin Pointer to scatter plot plugin whose position datasets are used to populate the dataset picker action + */ + void setupPositionDatasetPickerAction(ScatterplotPlugin* scatterplotPlugin); + + /** + * Set up the color dataset picker action with the color datasets from the scatter plot plugin + * @param scatterplotPlugin Pointer to scatter plot plugin whose color datasets are used to populate the dataset picker action + */ + void setupColorDatasetPickerAction(ScatterplotPlugin* scatterplotPlugin); + + /** + * Set up the point size dataset picker action with the point size datasets from the scatter plot plugin + * @param scatterplotPlugin Pointer to scatter plot plugin whose point size datasets are used to populate the dataset picker action + */ + void setupPointSizeDatasetPickerAction(ScatterplotPlugin* scatterplotPlugin); + + /** + * Set up the point opacity dataset picker action with the point opacity datasets from the scatter plot plugin + * @param scatterplotPlugin Pointer to scatter plot plugin whose point opacity datasets are used to populate the dataset picker action + */ + void setupPointOpacityDatasetPickerAction(ScatterplotPlugin* scatterplotPlugin); public: // Action getters From ec9315b2f33d438107aa0c956d2eef1ea3e65fc4 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Thu, 19 Feb 2026 08:47:42 +0100 Subject: [PATCH 13/16] Move dataset picker setup into ScatterplotPlugin Move control of dataset picker initialization from DatasetsAction to ScatterplotPlugin so the plugin can defer or re-run setup during project open. Make setupDatasetPickerActions protected and add ScatterplotPlugin as a friend so the plugin can call it. Refactor point-size dataset picker: tighten the filter to require PointType, a valid position dataset, and matching point counts; rework signal wiring to keep the DatasetPickerAction and ScalarAction synchronized and to set a default constant source when no dataset is selected. Minor debug logging added to aid troubleshooting. --- src/DatasetsAction.cpp | 54 ++++++++++++++++++++++++--------------- src/DatasetsAction.h | 3 ++- src/ScatterplotPlugin.cpp | 8 ++++++ 3 files changed, 44 insertions(+), 21 deletions(-) diff --git a/src/DatasetsAction.cpp b/src/DatasetsAction.cpp index 9ef2cd4..64915f4 100644 --- a/src/DatasetsAction.cpp +++ b/src/DatasetsAction.cpp @@ -40,10 +40,6 @@ DatasetsAction::DatasetsAction(QObject* parent, const QString& title) : return; setupDatasetPickerActions(scatterplotPlugin); - - connect(&mv::projects(), &AbstractProjectManager::projectOpened, this, [this, scatterplotPlugin]() -> void { - setupDatasetPickerActions(scatterplotPlugin); - }); } void DatasetsAction::connectToPublicAction(WidgetAction* publicAction, bool recursive) @@ -152,35 +148,53 @@ void DatasetsAction::setupColorDatasetPickerAction(ScatterplotPlugin* scatterplo void DatasetsAction::setupPointSizeDatasetPickerAction(ScatterplotPlugin* scatterplotPlugin) { - auto& settingsAction = scatterplotPlugin->getSettingsAction(); + auto& settingsAction = scatterplotPlugin->getSettingsAction(); + auto& pointPlotAction = settingsAction.getPlotAction().getPointPlotAction(); + auto& pointSizeAction = pointPlotAction.getSizeAction(); - _pointSizeDatasetPickerAction.setFilterFunction([this](mv::Dataset dataset) -> bool { - return dataset->getDataType() == PointType; - }); + _pointSizeDatasetPickerAction.setFilterFunction([this, scatterplotPlugin](mv::Dataset dataset) -> bool { + if (dataset->getDataType() != PointType) + return false; - auto& pointPlotAction = settingsAction.getPlotAction().getPointPlotAction(); - auto& pointSizeAction = pointPlotAction.getSizeAction(); + qDebug() << dataset->getGuiName() << "A"; + const auto positionDataset = scatterplotPlugin->getPositionSourceDataset(); + + if (!positionDataset.isValid()) + return false; + qDebug() << dataset->getGuiName() << "B"; - connect(&pointSizeAction, &ScalarAction::sourceSelectionChanged, this, [this, &pointSizeAction, scatterplotPlugin](const uint32_t& sourceSelectionIndex) -> void { - if (_pointSizeDataset.isValid()) + const mv::Dataset candidatePoints(dataset); + + if (candidatePoints->getNumPoints() != positionDataset->getNumPoints()) + return false; + + qDebug() << dataset->getGuiName() << "C"; + + return true; + }); + + connect(&_pointSizeDatasetPickerAction, &DatasetPickerAction::currentIndexChanged, this, [this, &pointPlotAction, &pointSizeAction, scatterplotPlugin](const int32_t& currentIndex) -> void { + const auto& pointSizeDataset = _pointSizeDatasetPickerAction.getCurrentDataset(); + + if (pointSizeDataset.isValid()) disconnect(&_pointSizeDataset, &Dataset<>::guiNameChanged, this, nullptr); - _pointSizeDataset = pointSizeAction.getCurrentDataset(); + _pointSizeDataset = pointSizeDataset; connect(&_pointSizeDataset, &Dataset<>::guiNameChanged, scatterplotPlugin, &ScatterplotPlugin::updateHeadsUpDisplay); + pointPlotAction.setCurrentPointSizeDataset(_pointSizeDataset); + + if (!_pointSizeDataset.isValid()) + pointSizeAction.setCurrentSourceIndex(ScalarSourceModel::DefaultRow::Constant); + }); + + connect(&pointSizeAction, &ScalarAction::sourceSelectionChanged, this, [this, &pointSizeAction](const uint32_t& sourceSelectionIndex) -> void { _pointSizeDatasetPickerAction.setCurrentDataset(pointSizeAction.isSourceDataset() ? pointSizeAction.getCurrentDataset() : nullptr); if (!pointSizeAction.isSourceDataset()) _pointSizeDatasetPickerAction.setCurrentIndex(-1); - }); - - connect(&_pointSizeDatasetPickerAction, &DatasetPickerAction::currentIndexChanged, this, [this, &pointSizeAction](const int32_t& currentIndex) -> void { - pointSizeAction.setCurrentDataset(_pointSizeDatasetPickerAction.getCurrentDataset()); - - if (currentIndex < 0) - pointSizeAction.setCurrentSourceIndex(ScalarSourceModel::DefaultRow::Constant); }); } diff --git a/src/DatasetsAction.h b/src/DatasetsAction.h index 82bd03c..aa6ab87 100644 --- a/src/DatasetsAction.h +++ b/src/DatasetsAction.h @@ -53,7 +53,7 @@ class DatasetsAction : public GroupAction */ QVariantMap toVariantMap() const override; -private: // Dataset picker action setup +protected: // Dataset picker action setup /** * Set up the dataset picker actions with the datasets from the scatter plot plugin @@ -103,6 +103,7 @@ class DatasetsAction : public GroupAction mv::Dataset _pointOpacityDataset; /** Smart pointer to dataset for driving point opacity (if any) */ friend class mv::AbstractActionsManager; + friend class ScatterplotPlugin; }; Q_DECLARE_METATYPE(DatasetsAction) diff --git a/src/ScatterplotPlugin.cpp b/src/ScatterplotPlugin.cpp index 63a9e7b..03dcdbc 100644 --- a/src/ScatterplotPlugin.cpp +++ b/src/ScatterplotPlugin.cpp @@ -408,6 +408,14 @@ void ScatterplotPlugin::init() updateHeadsUpDisplay(); updateHeadsUpDisplayTextColor(); + + if (mv::projects().isOpeningProject()) { + connect(&mv::projects(), &AbstractProjectManager::projectOpened, this, [this, &datasetsAction]() -> void { + datasetsAction.setupDatasetPickerActions(this); + }); + } else { + datasetsAction.setupDatasetPickerActions(this); + } } void ScatterplotPlugin::loadData(const Datasets& datasets) From ede22db3e0f6516923c91492b140edf3486998ef Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Thu, 19 Feb 2026 08:50:32 +0100 Subject: [PATCH 14/16] Invalidate dataset picker filters Add DatasetsAction::invalidateDatasetPickerActionFilters() (declared in the header and implemented) which calls invalidateFilter() on all dataset picker actions (_position, _color, _pointSize, _pointOpacity). Update ScatterplotPlugin::init() to call this new method instead of setupDatasetPickerActions() when initializing so picker filters are refreshed to reflect the current datasets without reinitializing the actions. --- src/DatasetsAction.cpp | 8 ++++++++ src/DatasetsAction.h | 3 +++ src/ScatterplotPlugin.cpp | 4 ++-- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/DatasetsAction.cpp b/src/DatasetsAction.cpp index 64915f4..82a6fc6 100644 --- a/src/DatasetsAction.cpp +++ b/src/DatasetsAction.cpp @@ -244,3 +244,11 @@ void DatasetsAction::setupPointOpacityDatasetPickerAction(ScatterplotPlugin* sca _pointOpacityDatasetPickerAction.setCurrentIndex(-1); }); } + +void DatasetsAction::invalidateDatasetPickerActionFilters() +{ + _positionDatasetPickerAction.invalidateFilter(); + _colorDatasetPickerAction.invalidateFilter(); + _pointSizeDatasetPickerAction.invalidateFilter(); + _pointOpacityDatasetPickerAction.invalidateFilter(); +} diff --git a/src/DatasetsAction.h b/src/DatasetsAction.h index aa6ab87..c71acbf 100644 --- a/src/DatasetsAction.h +++ b/src/DatasetsAction.h @@ -85,6 +85,9 @@ class DatasetsAction : public GroupAction */ void setupPointOpacityDatasetPickerAction(ScatterplotPlugin* scatterplotPlugin); + /** Update the filters of the dataset picker actions based on the current datasets in the scatter plot plugin */ + void invalidateDatasetPickerActionFilters(); + public: // Action getters DatasetPickerAction& getPositionDatasetPickerAction() { return _positionDatasetPickerAction; } diff --git a/src/ScatterplotPlugin.cpp b/src/ScatterplotPlugin.cpp index 03dcdbc..12cfe97 100644 --- a/src/ScatterplotPlugin.cpp +++ b/src/ScatterplotPlugin.cpp @@ -411,10 +411,10 @@ void ScatterplotPlugin::init() if (mv::projects().isOpeningProject()) { connect(&mv::projects(), &AbstractProjectManager::projectOpened, this, [this, &datasetsAction]() -> void { - datasetsAction.setupDatasetPickerActions(this); + datasetsAction.invalidateDatasetPickerActionFilters(); }); } else { - datasetsAction.setupDatasetPickerActions(this); + datasetsAction.invalidateDatasetPickerActionFilters(); } } From dc8d623cbafa5621c15a66d4389444586e24a805 Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Thu, 19 Feb 2026 09:17:25 +0100 Subject: [PATCH 15/16] Clear color dataset and refresh dependent pickers When the position dataset changes, clear the current color dataset and refresh dependent dataset pickers (color, point size, point opacity). Mark the position picker as Clearable, connect its datasetPicked signal to invalidate the other pickers, and invalidate them initially. Update filter callbacks to require a valid position dataset (and replace getPositionSourceDataset() calls with getPositionDataset()). This ensures pickers reflect the current position dataset and prevents stale/invalid color selections after position changes. --- src/DatasetsAction.cpp | 27 +++++++++++++++++++++++---- src/ScatterplotPlugin.cpp | 8 +++++--- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/DatasetsAction.cpp b/src/DatasetsAction.cpp index 82a6fc6..ee7d215 100644 --- a/src/DatasetsAction.cpp +++ b/src/DatasetsAction.cpp @@ -28,6 +28,7 @@ DatasetsAction::DatasetsAction(QObject* parent, const QString& title) : addAction(&_pointSizeDatasetPickerAction); addAction(&_pointOpacityDatasetPickerAction); + _positionDatasetPickerAction.setDefaultWidgetFlag(OptionAction::Clearable); _colorDatasetPickerAction.setDefaultWidgetFlag(OptionAction::Clearable); _pointSizeDatasetPickerAction.setDefaultWidgetFlag(OptionAction::Clearable); _pointOpacityDatasetPickerAction.setDefaultWidgetFlag(OptionAction::Clearable); @@ -40,6 +41,16 @@ DatasetsAction::DatasetsAction(QObject* parent, const QString& title) : return; setupDatasetPickerActions(scatterplotPlugin); + + connect(&_positionDatasetPickerAction, &DatasetPickerAction::datasetPicked, [this, scatterplotPlugin](Dataset pickedDataset) -> void { + _colorDatasetPickerAction.invalidateFilter(); + _pointSizeDatasetPickerAction.invalidateFilter(); + _pointOpacityDatasetPickerAction.invalidateFilter(); + }); + + _colorDatasetPickerAction.invalidateFilter(); + _pointSizeDatasetPickerAction.invalidateFilter(); + _pointOpacityDatasetPickerAction.invalidateFilter(); } void DatasetsAction::connectToPublicAction(WidgetAction* publicAction, bool recursive) @@ -121,8 +132,16 @@ void DatasetsAction::setupColorDatasetPickerAction(ScatterplotPlugin* scatterplo { auto& settingsAction = scatterplotPlugin->getSettingsAction(); - _colorDatasetPickerAction.setFilterFunction([this](mv::Dataset dataset) -> bool { - return (dataset->getDataType() == PointType || dataset->getDataType() == ColorType || dataset->getDataType() == ClusterType); + _colorDatasetPickerAction.setFilterFunction([this, scatterplotPlugin](mv::Dataset dataset) -> bool { + if (!(dataset->getDataType() == PointType || dataset->getDataType() == ColorType || dataset->getDataType() == ClusterType)) + return false; + + const auto positionDataset = scatterplotPlugin->getPositionDataset(); + + if (!positionDataset.isValid()) + return false; + + return true; }); auto& coloringAction = settingsAction.getColoringAction(); @@ -157,7 +176,7 @@ void DatasetsAction::setupPointSizeDatasetPickerAction(ScatterplotPlugin* scatte return false; qDebug() << dataset->getGuiName() << "A"; - const auto positionDataset = scatterplotPlugin->getPositionSourceDataset(); + const auto positionDataset = scatterplotPlugin->getPositionDataset(); if (!positionDataset.isValid()) return false; @@ -208,7 +227,7 @@ void DatasetsAction::setupPointOpacityDatasetPickerAction(ScatterplotPlugin* sca if (dataset->getDataType() != PointType) return false; - const auto positionDataset = scatterplotPlugin->getPositionSourceDataset(); + const auto positionDataset = scatterplotPlugin->getPositionDataset(); if (!positionDataset.isValid()) return false; diff --git a/src/ScatterplotPlugin.cpp b/src/ScatterplotPlugin.cpp index 12cfe97..62b4d77 100644 --- a/src/ScatterplotPlugin.cpp +++ b/src/ScatterplotPlugin.cpp @@ -165,7 +165,8 @@ ScatterplotPlugin::ScatterplotPlugin(const PluginFactory* factory) : // Load as point positions when no dataset is currently loaded dropRegions << new DropWidget::DropRegion(this, "Point position", description, "map-marker-alt", true, [this, candidateDataset]() { _positionDataset = candidateDataset; - }); + _settingsAction.getColoringAction().setCurrentColorDataset(nullptr); + }); } else { if (_positionDataset != candidateDataset && candidateDataset->getNumDimensions() >= 2) { @@ -173,8 +174,9 @@ ScatterplotPlugin::ScatterplotPlugin(const PluginFactory* factory) : // The number of points is equal, so offer the option to replace the existing points dataset dropRegions << new DropWidget::DropRegion(this, "Point position", description, "map-marker-alt", true, [this, candidateDataset]() { _positionDataset = candidateDataset; - }); - } + _settingsAction.getColoringAction().setCurrentColorDataset(nullptr); + }); + } // Accept for recoloring: // 1. data with the same number of points From ae308cb12853abc3ea6e92eeda88d26883b82bba Mon Sep 17 00:00:00 2001 From: Thomas Kroes Date: Fri, 20 Feb 2026 13:27:16 +0100 Subject: [PATCH 16/16] Remove debug qDebug logs in dataset check Remove leftover qDebug() calls from DatasetsAction::setupPointSizeDatasetPickerAction to clean up noisy debug output during dataset validation. The changes eliminate three intermediate debug prints while preserving the validation logic that ensures the dataset is of PointType and matches the position dataset point count. --- src/DatasetsAction.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/DatasetsAction.cpp b/src/DatasetsAction.cpp index ee7d215..e31ce00 100644 --- a/src/DatasetsAction.cpp +++ b/src/DatasetsAction.cpp @@ -175,21 +175,16 @@ void DatasetsAction::setupPointSizeDatasetPickerAction(ScatterplotPlugin* scatte if (dataset->getDataType() != PointType) return false; - qDebug() << dataset->getGuiName() << "A"; const auto positionDataset = scatterplotPlugin->getPositionDataset(); if (!positionDataset.isValid()) return false; - qDebug() << dataset->getGuiName() << "B"; - const mv::Dataset candidatePoints(dataset); if (candidatePoints->getNumPoints() != positionDataset->getNumPoints()) return false; - qDebug() << dataset->getGuiName() << "C"; - return true; });