diff --git a/core/include/Variable/VariableController.h b/core/include/Variable/VariableController.h index 3350511..1fd9c9c 100644 --- a/core/include/Variable/VariableController.h +++ b/core/include/Variable/VariableController.h @@ -111,11 +111,15 @@ public slots: /// Cancel the current request for the variable void onAbortProgressRequested(std::shared_ptr variable); - /// synchronization group methods + // synchronization group methods void onAddSynchronizationGroupId(QUuid synchronizationGroupId); void onRemoveSynchronizationGroupId(QUuid synchronizationGroupId); void onAddSynchronized(std::shared_ptr variable, QUuid synchronizationGroupId); + /// Desynchronizes the variable of the group whose identifier is passed in parameter + /// @remarks the method does nothing if the variable is not part of the group + void desynchronize(std::shared_ptr variable, QUuid synchronizationGroupId); + void initialize(); void finalize(); diff --git a/core/src/Variable/VariableController.cpp b/core/src/Variable/VariableController.cpp index 50b88cc..3aa437b 100644 --- a/core/src/Variable/VariableController.cpp +++ b/core/src/Variable/VariableController.cpp @@ -389,6 +389,36 @@ void VariableController::onAddSynchronized(std::shared_ptr variable, } } +void VariableController::desynchronize(std::shared_ptr variable, + QUuid synchronizationGroupId) +{ + // Gets variable id + auto variableIt = impl->m_VariableToIdentifierMap.find(variable); + if (variableIt == impl->m_VariableToIdentifierMap.cend()) { + qCCritical(LOG_VariableController()) + << tr("Can't desynchronize variable %1: variable identifier not found") + .arg(variable->name()); + return; + } + + // Gets synchronization group + auto groupIt = impl->m_GroupIdToVariableSynchronizationGroupMap.find(synchronizationGroupId); + if (groupIt == impl->m_GroupIdToVariableSynchronizationGroupMap.cend()) { + qCCritical(LOG_VariableController()) + << tr("Can't desynchronize variable %1: unknown synchronization group") + .arg(variable->name()); + return; + } + + auto variableId = variableIt->second; + + // Removes variable from synchronization group + auto synchronizationGroup = groupIt->second; + synchronizationGroup->removeVariableId(variableId); + + // Removes link between variable and synchronization group + impl->m_VariableIdGroupIdMap.erase(variableId); +} void VariableController::onRequestDataLoading(QVector > variables, const SqpRange &range, const SqpRange &oldRange, diff --git a/gui/include/Visualization/VisualizationGraphWidget.h b/gui/include/Visualization/VisualizationGraphWidget.h index 3d2892f..5b7b250 100644 --- a/gui/include/Visualization/VisualizationGraphWidget.h +++ b/gui/include/Visualization/VisualizationGraphWidget.h @@ -56,9 +56,13 @@ signals: void requestDataLoading(QVector > variable, const SqpRange &range, const SqpRange &oldRange, bool synchronise); + /// Signal emitted when the variable is about to be removed from the graph + void variableAboutToBeRemoved(std::shared_ptr var); + /// Signal emitted when the variable has been added to the graph void variableAdded(std::shared_ptr var); protected: + void closeEvent(QCloseEvent *event) override; void enterEvent(QEvent *event) override; void leaveEvent(QEvent *event) override; diff --git a/gui/include/Visualization/VisualizationTabWidget.h b/gui/include/Visualization/VisualizationTabWidget.h index ca9b4c8..c3623e2 100644 --- a/gui/include/Visualization/VisualizationTabWidget.h +++ b/gui/include/Visualization/VisualizationTabWidget.h @@ -41,6 +41,9 @@ public: bool contains(const Variable &variable) const override; QString name() const override; +protected: + void closeEvent(QCloseEvent *event) override; + private: /// @return the layout of tab in which zones are added QLayout &tabLayout() const noexcept; diff --git a/gui/include/Visualization/VisualizationWidget.h b/gui/include/Visualization/VisualizationWidget.h index 2d89820..3310990 100644 --- a/gui/include/Visualization/VisualizationWidget.h +++ b/gui/include/Visualization/VisualizationWidget.h @@ -44,6 +44,9 @@ public slots: void onRangeChanged(std::shared_ptr variable, const SqpRange &range) noexcept; +protected: + void closeEvent(QCloseEvent *event) override; + private: Ui::VisualizationWidget *ui; }; diff --git a/gui/include/Visualization/VisualizationZoneWidget.h b/gui/include/Visualization/VisualizationZoneWidget.h index 6518706..02aa6ba 100644 --- a/gui/include/Visualization/VisualizationZoneWidget.h +++ b/gui/include/Visualization/VisualizationZoneWidget.h @@ -42,6 +42,9 @@ public: bool contains(const Variable &variable) const override; QString name() const override; +protected: + void closeEvent(QCloseEvent *event) override; + private: Ui::VisualizationZoneWidget *ui; @@ -50,6 +53,8 @@ private: private slots: void onVariableAdded(std::shared_ptr variable); + /// Slot called when a variable is about to be removed from a graph contained in the zone + void onVariableAboutToBeRemoved(std::shared_ptr variable); }; #endif // SCIQLOP_VISUALIZATIONZONEWIDGET_H diff --git a/gui/include/Visualization/operations/FindVariableOperation.h b/gui/include/Visualization/operations/FindVariableOperation.h new file mode 100644 index 0000000..afad39e --- /dev/null +++ b/gui/include/Visualization/operations/FindVariableOperation.h @@ -0,0 +1,42 @@ +#ifndef SCIQLOP_FINDVARIABLEOPERATION_H +#define SCIQLOP_FINDVARIABLEOPERATION_H + +#include "Visualization/IVisualizationWidgetVisitor.h" + +#include + +#include + +class IVisualizationWidget; +class Variable; + +/** + * @brief The FindVariableOperation class defines an operation that traverses all of visualization + * widgets to determine which ones contain the variable passed as parameter. The result of the + * operation is the list of widgets that contain the variable. + */ +class FindVariableOperation : public IVisualizationWidgetVisitor { +public: + /** + * Ctor + * @param variable the variable to find + */ + explicit FindVariableOperation(std::shared_ptr variable); + + void visitEnter(VisualizationWidget *widget) override final; + void visitLeave(VisualizationWidget *widget) override final; + void visitEnter(VisualizationTabWidget *tabWidget) override final; + void visitLeave(VisualizationTabWidget *tabWidget) override final; + void visitEnter(VisualizationZoneWidget *zoneWidget) override final; + void visitLeave(VisualizationZoneWidget *zoneWidget) override final; + void visit(VisualizationGraphWidget *graphWidget) override final; + + /// @return the widgets that contain the variable + std::set result() const noexcept; + +private: + class FindVariableOperationPrivate; + spimpl::unique_impl_ptr impl; +}; + +#endif // SCIQLOP_FINDVARIABLEOPERATION_H diff --git a/gui/include/Visualization/operations/GenerateVariableMenuOperation.h b/gui/include/Visualization/operations/GenerateVariableMenuOperation.h index 6849015..53475ba 100644 --- a/gui/include/Visualization/operations/GenerateVariableMenuOperation.h +++ b/gui/include/Visualization/operations/GenerateVariableMenuOperation.h @@ -7,7 +7,10 @@ #include +#include + class QMenu; +class IVisualizationWidget; class Variable; Q_DECLARE_LOGGING_CATEGORY(LOG_GenerateVariableMenuOperation) @@ -23,8 +26,11 @@ public: * Ctor * @param menu the menu to which to attach the generated menu * @param variable the variable for which to generate the menu + * @param variableContainers the containers that already contain the variable for which to + * generate the menu */ - explicit GenerateVariableMenuOperation(QMenu *menu, std::shared_ptr variable); + explicit GenerateVariableMenuOperation(QMenu *menu, std::shared_ptr variable, + std::set variableContainers); void visitEnter(VisualizationWidget *widget) override final; void visitLeave(VisualizationWidget *widget) override final; diff --git a/gui/meson.build b/gui/meson.build index a8ece56..903a518 100644 --- a/gui/meson.build +++ b/gui/meson.build @@ -54,6 +54,7 @@ gui_sources = [ 'src/Visualization/VisualizationZoneWidget.cpp', 'src/Visualization/qcustomplot.cpp', 'src/Visualization/QCustomPlotSynchronizer.cpp', + 'src/Visualization/operations/FindVariableOperation.cpp', 'src/Visualization/operations/GenerateVariableMenuOperation.cpp', 'src/Visualization/operations/MenuBuilder.cpp', 'src/Visualization/operations/RemoveVariableOperation.cpp', diff --git a/gui/src/Visualization/VisualizationGraphWidget.cpp b/gui/src/Visualization/VisualizationGraphWidget.cpp index b7ae3c0..fc99f91 100644 --- a/gui/src/Visualization/VisualizationGraphWidget.cpp +++ b/gui/src/Visualization/VisualizationGraphWidget.cpp @@ -138,6 +138,8 @@ void VisualizationGraphWidget::removeVariable(std::shared_ptr variable // - is no longer referenced in the map auto variableIt = impl->m_VariableToPlotMultiMap.find(variable); if (variableIt != impl->m_VariableToPlotMultiMap.cend()) { + emit variableAboutToBeRemoved(variable); + auto &plottablesMap = variableIt->second; for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend(); @@ -218,6 +220,16 @@ QString VisualizationGraphWidget::name() const return impl->m_Name; } +void VisualizationGraphWidget::closeEvent(QCloseEvent *event) +{ + Q_UNUSED(event); + + // Prevents that all variables will be removed from graph when it will be closed + for (auto &variableEntry : impl->m_VariableToPlotMultiMap) { + emit variableAboutToBeRemoved(variableEntry.first); + } +} + void VisualizationGraphWidget::enterEvent(QEvent *event) { Q_UNUSED(event); diff --git a/gui/src/Visualization/VisualizationTabWidget.cpp b/gui/src/Visualization/VisualizationTabWidget.cpp index f516cbb..84751e9 100644 --- a/gui/src/Visualization/VisualizationTabWidget.cpp +++ b/gui/src/Visualization/VisualizationTabWidget.cpp @@ -22,6 +22,24 @@ QString defaultZoneName(const QLayout &layout) return QObject::tr("Zone %1").arg(count + 1); } +/** + * Applies a function to all zones of the tab represented by its layout + * @param layout the layout that contains zones + * @param fun the function to apply to each zone + */ +template +void processZones(QLayout &layout, Fun fun) +{ + for (auto i = 0; i < layout.count(); ++i) { + if (auto item = layout.itemAt(i)) { + if (auto visualizationZoneWidget + = dynamic_cast(item->widget())) { + fun(*visualizationZoneWidget); + } + } + } +} + } // namespace struct VisualizationTabWidget::VisualizationTabWidgetPrivate { @@ -67,17 +85,10 @@ void VisualizationTabWidget::accept(IVisualizationWidgetVisitor *visitor) if (visitor) { visitor->visitEnter(this); - // Apply visitor to zone children - auto &layout = tabLayout(); - for (auto i = 0; i < layout.count(); ++i) { - if (auto item = layout.itemAt(i)) { - // Widgets different from zones are not visited (no action) - if (auto visualizationZoneWidget - = dynamic_cast(item->widget())) { - visualizationZoneWidget->accept(visitor); - } - } - } + // Apply visitor to zone children: widgets different from zones are not visited (no action) + processZones(tabLayout(), [visitor](VisualizationZoneWidget &zoneWidget) { + zoneWidget.accept(visitor); + }); visitor->visitLeave(this); } @@ -104,6 +115,14 @@ QString VisualizationTabWidget::name() const return impl->m_Name; } +void VisualizationTabWidget::closeEvent(QCloseEvent *event) +{ + // Closes zones in the tab + processZones(tabLayout(), [](VisualizationZoneWidget &zoneWidget) { zoneWidget.close(); }); + + QWidget::closeEvent(event); +} + QLayout &VisualizationTabWidget::tabLayout() const noexcept { return *ui->scrollAreaWidgetContents->layout(); diff --git a/gui/src/Visualization/VisualizationWidget.cpp b/gui/src/Visualization/VisualizationWidget.cpp index 7708ca1..b215461 100644 --- a/gui/src/Visualization/VisualizationWidget.cpp +++ b/gui/src/Visualization/VisualizationWidget.cpp @@ -3,6 +3,7 @@ #include "Visualization/VisualizationGraphWidget.h" #include "Visualization/VisualizationTabWidget.h" #include "Visualization/VisualizationZoneWidget.h" +#include "Visualization/operations/FindVariableOperation.h" #include "Visualization/operations/GenerateVariableMenuOperation.h" #include "Visualization/operations/RemoveVariableOperation.h" #include "Visualization/operations/RescaleAxeOperation.h" @@ -120,8 +121,14 @@ void VisualizationWidget::attachVariableMenu( // Menu is generated only if there is a single variable if (variables.size() == 1) { if (auto variable = variables.first()) { + // Gets the containers of the variable + FindVariableOperation findVariableOperation{variable}; + accept(&findVariableOperation); + auto variableContainers = findVariableOperation.result(); + // Generates the actions that make it possible to visualize the variable - auto generateVariableMenuOperation = GenerateVariableMenuOperation{menu, variable}; + GenerateVariableMenuOperation generateVariableMenuOperation{ + menu, variable, std::move(variableContainers)}; accept(&generateVariableMenuOperation); } else { @@ -150,3 +157,16 @@ void VisualizationWidget::onRangeChanged(std::shared_ptr variable, auto rescaleVariableOperation = RescaleAxeOperation{variable, range}; accept(&rescaleVariableOperation); } + +void VisualizationWidget::closeEvent(QCloseEvent *event) +{ + // Closes tabs in the widget + for (auto i = 0; i < ui->tabWidget->count(); ++i) { + if (auto visualizationTabWidget + = dynamic_cast(ui->tabWidget->widget(i))) { + visualizationTabWidget->close(); + } + } + + QWidget::closeEvent(event); +} diff --git a/gui/src/Visualization/VisualizationZoneWidget.cpp b/gui/src/Visualization/VisualizationZoneWidget.cpp index cb254e2..aacba29 100644 --- a/gui/src/Visualization/VisualizationZoneWidget.cpp +++ b/gui/src/Visualization/VisualizationZoneWidget.cpp @@ -34,6 +34,24 @@ QString defaultGraphName(const QLayout &layout) return QObject::tr("Graph %1").arg(count + 1); } +/** + * Applies a function to all graphs of the zone represented by its layout + * @param layout the layout that contains graphs + * @param fun the function to apply to each graph + */ +template +void processGraphs(QLayout &layout, Fun fun) +{ + for (auto i = 0; i < layout.count(); ++i) { + if (auto item = layout.itemAt(i)) { + if (auto visualizationGraphWidget + = dynamic_cast(item->widget())) { + fun(*visualizationGraphWidget); + } + } + } +} + } // namespace struct VisualizationZoneWidget::VisualizationZoneWidgetPrivate { @@ -177,6 +195,8 @@ VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptrvisitEnter(this); - // Apply visitor to graph children - auto layout = ui->visualizationZoneFrame->layout(); - for (auto i = 0; i < layout->count(); ++i) { - if (auto item = layout->itemAt(i)) { - // Widgets different from graphs are not visited (no action) - if (auto visualizationGraphWidget - = dynamic_cast(item->widget())) { - visualizationGraphWidget->accept(visitor); - } - } - } + // Apply visitor to graph children: widgets different from graphs are not visited (no + // action) + processGraphs( + *ui->visualizationZoneFrame->layout(), + [visitor](VisualizationGraphWidget &graphWidget) { graphWidget.accept(visitor); }); visitor->visitLeave(this); } @@ -260,9 +274,29 @@ QString VisualizationZoneWidget::name() const return ui->zoneNameLabel->text(); } +void VisualizationZoneWidget::closeEvent(QCloseEvent *event) +{ + // Closes graphs in the zone + processGraphs(*ui->visualizationZoneFrame->layout(), + [](VisualizationGraphWidget &graphWidget) { graphWidget.close(); }); + + // Delete synchronization group from variable controller + QMetaObject::invokeMethod(&sqpApp->variableController(), "onRemoveSynchronizationGroupId", + Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId)); + + QWidget::closeEvent(event); +} + void VisualizationZoneWidget::onVariableAdded(std::shared_ptr variable) { QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronized", Qt::QueuedConnection, Q_ARG(std::shared_ptr, variable), Q_ARG(QUuid, impl->m_SynchronisationGroupId)); } + +void VisualizationZoneWidget::onVariableAboutToBeRemoved(std::shared_ptr variable) +{ + QMetaObject::invokeMethod(&sqpApp->variableController(), "desynchronize", Qt::QueuedConnection, + Q_ARG(std::shared_ptr, variable), + Q_ARG(QUuid, impl->m_SynchronisationGroupId)); +} diff --git a/gui/src/Visualization/operations/FindVariableOperation.cpp b/gui/src/Visualization/operations/FindVariableOperation.cpp new file mode 100644 index 0000000..eeb4f69 --- /dev/null +++ b/gui/src/Visualization/operations/FindVariableOperation.cpp @@ -0,0 +1,72 @@ +#include "Visualization/operations/FindVariableOperation.h" + +#include "Visualization/VisualizationGraphWidget.h" +#include "Visualization/VisualizationTabWidget.h" +#include "Visualization/VisualizationWidget.h" +#include "Visualization/VisualizationZoneWidget.h" + +#include + +struct FindVariableOperation::FindVariableOperationPrivate { + explicit FindVariableOperationPrivate(std::shared_ptr variable) : m_Variable{variable} + { + } + + void visit(IVisualizationWidget *widget) + { + if (m_Variable && widget && widget->contains(*m_Variable)) { + m_Containers.insert(widget); + } + } + + std::shared_ptr m_Variable; ///< Variable to find + std::set m_Containers; ///< Containers found for the variable +}; + +FindVariableOperation::FindVariableOperation(std::shared_ptr variable) + : impl{spimpl::make_unique_impl(variable)} +{ +} + +void FindVariableOperation::visitEnter(VisualizationWidget *widget) +{ + impl->visit(widget); +} + +void FindVariableOperation::visitLeave(VisualizationWidget *widget) +{ + // Does nothing + Q_UNUSED(widget); +} + +void FindVariableOperation::visitEnter(VisualizationTabWidget *tabWidget) +{ + impl->visit(tabWidget); +} + +void FindVariableOperation::visitLeave(VisualizationTabWidget *tabWidget) +{ + // Does nothing + Q_UNUSED(tabWidget); +} + +void FindVariableOperation::visitEnter(VisualizationZoneWidget *zoneWidget) +{ + impl->visit(zoneWidget); +} + +void FindVariableOperation::visitLeave(VisualizationZoneWidget *zoneWidget) +{ + // Does nothing + Q_UNUSED(zoneWidget); +} + +void FindVariableOperation::visit(VisualizationGraphWidget *graphWidget) +{ + impl->visit(graphWidget); +} + +std::set FindVariableOperation::result() const noexcept +{ + return impl->m_Containers; +} diff --git a/gui/src/Visualization/operations/GenerateVariableMenuOperation.cpp b/gui/src/Visualization/operations/GenerateVariableMenuOperation.cpp index f9f4d37..e6bb455 100644 --- a/gui/src/Visualization/operations/GenerateVariableMenuOperation.cpp +++ b/gui/src/Visualization/operations/GenerateVariableMenuOperation.cpp @@ -13,8 +13,13 @@ Q_LOGGING_CATEGORY(LOG_GenerateVariableMenuOperation, "GenerateVariableMenuOperation") struct GenerateVariableMenuOperation::GenerateVariableMenuOperationPrivate { - explicit GenerateVariableMenuOperationPrivate(QMenu *menu, std::shared_ptr variable) - : m_Variable{variable}, m_PlotMenuBuilder{menu}, m_UnplotMenuBuilder{menu} + explicit GenerateVariableMenuOperationPrivate( + QMenu *menu, std::shared_ptr variable, + std::set variableContainers) + : m_Variable{variable}, + m_PlotMenuBuilder{menu}, + m_UnplotMenuBuilder{menu}, + m_VariableContainers{std::move(variableContainers)} { } @@ -47,7 +52,7 @@ struct GenerateVariableMenuOperation::GenerateVariableMenuOperationPrivate { void visitNodeLeavePlot(const IVisualizationWidget &container, const QString &actionName, ActionFun actionFunction) { - if (m_Variable && container.canDrop(*m_Variable)) { + if (isValidContainer(container)) { m_PlotMenuBuilder.addSeparator(); m_PlotMenuBuilder.addAction(actionName, actionFunction); } @@ -66,28 +71,39 @@ struct GenerateVariableMenuOperation::GenerateVariableMenuOperationPrivate { void visitLeafPlot(const IVisualizationWidget &container, const QString &actionName, ActionFun actionFunction) { - if (m_Variable && container.canDrop(*m_Variable)) { + if (isValidContainer(container)) { m_PlotMenuBuilder.addAction(actionName, actionFunction); } } template - void visitLeafUnplot(const IVisualizationWidget &container, const QString &actionName, + void visitLeafUnplot(IVisualizationWidget *container, const QString &actionName, ActionFun actionFunction) { - if (m_Variable && container.contains(*m_Variable)) { + // If the container contains the variable, we generate 'unplot' action + if (m_VariableContainers.count(container) == 1) { m_UnplotMenuBuilder.addAction(actionName, actionFunction); } } + bool isValidContainer(const IVisualizationWidget &container) const noexcept + { + // A container is valid if it can contain the variable and if the variable is not already + // contained in another container + return m_Variable && m_VariableContainers.size() == 0 && container.canDrop(*m_Variable); + } + std::shared_ptr m_Variable; + std::set m_VariableContainers; MenuBuilder m_PlotMenuBuilder; ///< Builder for the 'Plot' menu MenuBuilder m_UnplotMenuBuilder; ///< Builder for the 'Unplot' menu }; -GenerateVariableMenuOperation::GenerateVariableMenuOperation(QMenu *menu, - std::shared_ptr variable) - : impl{spimpl::make_unique_impl(menu, variable)} +GenerateVariableMenuOperation::GenerateVariableMenuOperation( + QMenu *menu, std::shared_ptr variable, + std::set variableContainers) + : impl{spimpl::make_unique_impl( + menu, variable, std::move(variableContainers))} { } @@ -155,7 +171,6 @@ void GenerateVariableMenuOperation::visitEnter(VisualizationZoneWidget *zoneWidg void GenerateVariableMenuOperation::visitLeave(VisualizationZoneWidget *zoneWidget) { - qCCritical(LOG_GenerateVariableMenuOperation(), "Open in a new graph DETECTED !!"); if (zoneWidget) { // 'Plot' menu impl->visitNodeLeavePlot( @@ -187,7 +202,7 @@ void GenerateVariableMenuOperation::visit(VisualizationGraphWidget *graphWidget) }); // 'Unplot' menu - impl->visitLeafUnplot(*graphWidget, QObject::tr("Remove from %1").arg(graphWidget->name()), + impl->visitLeafUnplot(graphWidget, QObject::tr("Remove from %1").arg(graphWidget->name()), [ varW = std::weak_ptr{impl->m_Variable}, graphWidget ]() { if (auto var = varW.lock()) { graphWidget->removeVariable(var);