diff --git a/gui/include/Visualization/VisualizationGraphWidget.h b/gui/include/Visualization/VisualizationGraphWidget.h index 98a1c1d..c8f0adc 100644 --- a/gui/include/Visualization/VisualizationGraphWidget.h +++ b/gui/include/Visualization/VisualizationGraphWidget.h @@ -2,6 +2,7 @@ #define SCIQLOP_VISUALIZATIONGRAPHWIDGET_H #include "Visualization/IVisualizationWidget.h" +#include "Visualization/VisualizationDragWidget.h" #include #include @@ -16,12 +17,13 @@ class QCPRange; class QCustomPlot; class SqpRange; class Variable; +class VisualizationZoneWidget; namespace Ui { class VisualizationGraphWidget; } // namespace Ui -class VisualizationGraphWidget : public QWidget, public IVisualizationWidget { +class VisualizationGraphWidget : public VisualizationDragWidget, public IVisualizationWidget { Q_OBJECT friend class QCustomPlotSynchronizer; @@ -31,6 +33,8 @@ public: explicit VisualizationGraphWidget(const QString &name = {}, QWidget *parent = 0); virtual ~VisualizationGraphWidget(); + VisualizationZoneWidget* parentZoneWidget() const noexcept; + /// If acquisition isn't enable, requestDataLoading signal cannot be emit void enableAcquisition(bool enable); @@ -39,6 +43,9 @@ public: /// Removes a variable from the graph void removeVariable(std::shared_ptr variable) noexcept; + /// Returns the list of all variables used in the graph + QList> variables() const; + void setYRange(const SqpRange &range); SqpRange graphRange() const noexcept; void setGraphRange(const SqpRange &range); @@ -49,6 +56,9 @@ public: bool contains(const Variable &variable) const override; QString name() const override; + // VisualisationDragWidget + QMimeData* mimeData() const override; + bool isDragAllowed() const override; signals: void synchronize(const SqpRange &range, const SqpRange &oldRange); diff --git a/gui/include/Visualization/VisualizationTabWidget.h b/gui/include/Visualization/VisualizationTabWidget.h index c3623e2..e743b81 100644 --- a/gui/include/Visualization/VisualizationTabWidget.h +++ b/gui/include/Visualization/VisualizationTabWidget.h @@ -7,6 +7,7 @@ #include #include +#include Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationTabWidget) @@ -27,14 +28,25 @@ public: /// Add a zone widget void addZone(VisualizationZoneWidget *zoneWidget); + void insertZone(int index, VisualizationZoneWidget *zoneWidget); + /** * Creates a zone using a variable. The variable will be displayed in a new graph of the new - * zone. + * zone. The zone is added at the end. * @param variable the variable for which to create the zone * @return the pointer to the created zone */ VisualizationZoneWidget *createZone(std::shared_ptr variable); + /** + * Creates a zone using a list of variables. The variables will be displayed in a new graph of the new + * zone. The zone is inserted at the specified index. + * @param variables the variables for which to create the zone + * @param index The index where the zone should be inserted in the layout + * @return the pointer to the created zone + */ + VisualizationZoneWidget *createZone(const QList>& variables, int index); + // IVisualizationWidget interface void accept(IVisualizationWidgetVisitor *visitor) override; bool canDrop(const Variable &variable) const override; @@ -52,6 +64,9 @@ private: class VisualizationTabWidgetPrivate; spimpl::unique_impl_ptr impl; + +private slots: + void dropMimeData(int index, const QMimeData *mimeData); }; #endif // SCIQLOP_VISUALIZATIONTABWIDGET_H diff --git a/gui/include/Visualization/VisualizationZoneWidget.h b/gui/include/Visualization/VisualizationZoneWidget.h index 02aa6ba..a2dac14 100644 --- a/gui/include/Visualization/VisualizationZoneWidget.h +++ b/gui/include/Visualization/VisualizationZoneWidget.h @@ -2,6 +2,7 @@ #define SCIQLOP_VISUALIZATIONZONEWIDGET_H #include "Visualization/IVisualizationWidget.h" +#include "Visualization/VisualizationDragWidget.h" #include #include @@ -19,29 +20,55 @@ class VisualizationZoneWidget; class Variable; class VisualizationGraphWidget; -class VisualizationZoneWidget : public QWidget, public IVisualizationWidget { +class VisualizationZoneWidget : public VisualizationDragWidget, public IVisualizationWidget { Q_OBJECT public: explicit VisualizationZoneWidget(const QString &name = {}, QWidget *parent = 0); virtual ~VisualizationZoneWidget(); - /// Add a graph widget + /// Adds a graph widget void addGraph(VisualizationGraphWidget *graphWidget); + /// Inserts a graph widget + void insertGraph(int index, VisualizationGraphWidget *graphWidget); + /** * Creates a graph using a variable. The variable will be displayed in the new graph. + * The graph is added at the end. * @param variable the variable for which to create the graph * @return the pointer to the created graph */ VisualizationGraphWidget *createGraph(std::shared_ptr variable); + /** + * Creates a graph using a variable. The variable will be displayed in the new graph. + * The graph is inserted at the specified index. + * @param variable the variable for which to create the graph + * @param index The index where the graph should be inserted in the layout + * @return the pointer to the created graph + */ + VisualizationGraphWidget *createGraph(std::shared_ptr variable, int index); + + /** + * Creates a graph using a list of variables. The variables will be displayed in the new graph. + * The graph is inserted at the specified index. + * @param variables List of variables to be added to the graph + * @param index The index where the graph should be inserted in the layout + * @return the pointer to the created graph + */ + VisualizationGraphWidget *createGraph(const QList> variables, int index); + // IVisualizationWidget interface void accept(IVisualizationWidgetVisitor *visitor) override; bool canDrop(const Variable &variable) const override; bool contains(const Variable &variable) const override; QString name() const override; + // VisualisationDragWidget + QMimeData* mimeData() const override; + bool isDragAllowed() const override; + protected: void closeEvent(QCloseEvent *event) override; @@ -55,6 +82,8 @@ 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); + + void dropMimeData(int index, const QMimeData* mimeData); }; #endif // SCIQLOP_VISUALIZATIONZONEWIDGET_H diff --git a/gui/src/Visualization/VisualizationGraphWidget.cpp b/gui/src/Visualization/VisualizationGraphWidget.cpp index 50d0562..a0f598e 100644 --- a/gui/src/Visualization/VisualizationGraphWidget.cpp +++ b/gui/src/Visualization/VisualizationGraphWidget.cpp @@ -3,12 +3,14 @@ #include "Visualization/VisualizationDefs.h" #include "Visualization/VisualizationGraphHelper.h" #include "Visualization/VisualizationGraphRenderingDelegate.h" +#include "Visualization/VisualizationZoneWidget.h" #include "ui_VisualizationGraphWidget.h" #include #include #include #include +#include #include #include @@ -47,7 +49,7 @@ struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate { }; VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent) - : QWidget{parent}, + : VisualizationDragWidget{parent}, ui{new Ui::VisualizationGraphWidget}, impl{spimpl::make_unique_impl(name)} { @@ -92,6 +94,17 @@ VisualizationGraphWidget::~VisualizationGraphWidget() delete ui; } +VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept +{ + auto parent = parentWidget(); + do + { + parent = parent->parentWidget(); + } while (parent != nullptr && !qobject_cast(parent)); + + return qobject_cast(parent); +} + void VisualizationGraphWidget::enableAcquisition(bool enable) { impl->m_DoAcquisition = enable; @@ -152,6 +165,17 @@ void VisualizationGraphWidget::removeVariable(std::shared_ptr variable ui->widget->replot(); } +QList> VisualizationGraphWidget::variables() const +{ + auto variables = QList>{}; + for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap); it != std::cend(impl->m_VariableToPlotMultiMap); ++it) + { + variables << it->first; + } + + return variables; +} + void VisualizationGraphWidget::setYRange(const SqpRange &range) { ui->widget->yAxis->setRange(range.m_TStart, range.m_TEnd); @@ -206,6 +230,19 @@ QString VisualizationGraphWidget::name() const return impl->m_Name; } +QMimeData *VisualizationGraphWidget::mimeData() const +{ + auto *mimeData = new QMimeData; + mimeData->setData(DragDropHelper::MIME_TYPE_GRAPH, QByteArray()); + + return mimeData; +} + +bool VisualizationGraphWidget::isDragAllowed() const +{ + return true; +} + void VisualizationGraphWidget::closeEvent(QCloseEvent *event) { Q_UNUSED(event); @@ -284,6 +321,8 @@ void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept { // Handles plot rendering when mouse is moving impl->m_RenderingDelegate->onMouseMove(event); + + VisualizationDragWidget::mouseMoveEvent(event); } void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept @@ -307,6 +346,10 @@ void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept { impl->m_IsCalibration = event->modifiers().testFlag(Qt::ControlModifier); + + plot().setInteraction(QCP::iRangeDrag, !event->modifiers().testFlag(Qt::AltModifier)); + + VisualizationDragWidget::mousePressEvent(event); } void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept diff --git a/gui/src/Visualization/VisualizationTabWidget.cpp b/gui/src/Visualization/VisualizationTabWidget.cpp index 84751e9..bb4c90d 100644 --- a/gui/src/Visualization/VisualizationTabWidget.cpp +++ b/gui/src/Visualization/VisualizationTabWidget.cpp @@ -3,6 +3,10 @@ #include "ui_VisualizationTabWidget.h" #include "Visualization/VisualizationZoneWidget.h" +#include "Visualization/VisualizationGraphWidget.h" + +#include "SqpApplication.h" +#include "DragDropHelper.h" Q_LOGGING_CATEGORY(LOG_VisualizationTabWidget, "VisualizationTabWidget") @@ -55,6 +59,9 @@ VisualizationTabWidget::VisualizationTabWidget(const QString &name, QWidget *par { ui->setupUi(this); + ui->dragDropContainer->setAcceptedMimeTypes({DragDropHelper::MIME_TYPE_GRAPH, DragDropHelper::MIME_TYPE_ZONE}); + connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccured, this, &VisualizationTabWidget::dropMimeData); + // Widget is deleted when closed setAttribute(Qt::WA_DeleteOnClose); } @@ -66,16 +73,26 @@ VisualizationTabWidget::~VisualizationTabWidget() void VisualizationTabWidget::addZone(VisualizationZoneWidget *zoneWidget) { - tabLayout().addWidget(zoneWidget); + ui->dragDropContainer->addDragWidget(zoneWidget); +} + +void VisualizationTabWidget::insertZone(int index, VisualizationZoneWidget *zoneWidget) +{ + ui->dragDropContainer->insertDragWidget(index, zoneWidget); } VisualizationZoneWidget *VisualizationTabWidget::createZone(std::shared_ptr variable) { - auto zoneWidget = new VisualizationZoneWidget{defaultZoneName(tabLayout()), this}; - this->addZone(zoneWidget); + return createZone({variable}, -1); +} + +VisualizationZoneWidget *VisualizationTabWidget::createZone(const QList > &variables, int index) +{ + auto zoneWidget = new VisualizationZoneWidget{defaultZoneName(*ui->dragDropContainer->layout()), this}; + this->insertZone(index, zoneWidget); // Creates a new graph into the zone - zoneWidget->createGraph(variable); + zoneWidget->createGraph(variables, index); return zoneWidget; } @@ -125,5 +142,40 @@ void VisualizationTabWidget::closeEvent(QCloseEvent *event) QLayout &VisualizationTabWidget::tabLayout() const noexcept { - return *ui->scrollAreaWidgetContents->layout(); + return *ui->dragDropContainer->layout(); +} + +void VisualizationTabWidget::dropMimeData(int index, const QMimeData *mimeData) +{ + auto& helper = sqpApp->dragDropHelper(); + if (mimeData->hasFormat(DragDropHelper::MIME_TYPE_GRAPH)) + { + auto graphWidget = static_cast(helper.getCurrentDragWidget()); + auto parentDragDropContainer = qobject_cast(graphWidget->parentWidget()); + Q_ASSERT(parentDragDropContainer); + + auto nbGraph = parentDragDropContainer->countDragWidget(); + if (nbGraph == 1) + { + //This is the only graph in the previous zone, close the zone + graphWidget->parentZoneWidget()->close(); + } + else + { + //Close the graph + graphWidget->close(); + } + + const auto& variables = graphWidget->variables(); + createZone(variables, index); + } + else if (mimeData->hasFormat(DragDropHelper::MIME_TYPE_ZONE)) + { + //Simple move of the zone, no variable operation associated + auto zoneWidget = static_cast(helper.getCurrentDragWidget()); + auto parentDragDropContainer = zoneWidget->parentWidget(); + parentDragDropContainer->layout()->removeWidget(zoneWidget); + + ui->dragDropContainer->insertDragWidget(index, zoneWidget); + } } diff --git a/gui/src/Visualization/VisualizationZoneWidget.cpp b/gui/src/Visualization/VisualizationZoneWidget.cpp index 7d1998a..f300c06 100644 --- a/gui/src/Visualization/VisualizationZoneWidget.cpp +++ b/gui/src/Visualization/VisualizationZoneWidget.cpp @@ -11,8 +11,11 @@ #include #include +#include #include +#include + Q_LOGGING_CATEGORY(LOG_VisualizationZoneWidget, "VisualizationZoneWidget") namespace { @@ -66,7 +69,7 @@ struct VisualizationZoneWidget::VisualizationZoneWidgetPrivate { }; VisualizationZoneWidget::VisualizationZoneWidget(const QString &name, QWidget *parent) - : QWidget{parent}, + : VisualizationDragWidget{parent}, ui{new Ui::VisualizationZoneWidget}, impl{spimpl::make_unique_impl()} { @@ -74,6 +77,9 @@ VisualizationZoneWidget::VisualizationZoneWidget(const QString &name, QWidget *p ui->zoneNameLabel->setText(name); + ui->dragDropContainer->setAcceptedMimeTypes({DragDropHelper::MIME_TYPE_GRAPH}); + connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccured, this, &VisualizationZoneWidget::dropMimeData); + // 'Close' options : widget is deleted when closed setAttribute(Qt::WA_DeleteOnClose); connect(ui->closeButton, &QToolButton::clicked, this, &VisualizationZoneWidget::close); @@ -94,13 +100,26 @@ void VisualizationZoneWidget::addGraph(VisualizationGraphWidget *graphWidget) // Synchronize new graph with others in the zone impl->m_Synchronizer->addGraph(*graphWidget); - ui->visualizationZoneFrame->layout()->addWidget(graphWidget); + ui->dragDropContainer->addDragWidget(graphWidget); +} + +void VisualizationZoneWidget::insertGraph(int index, VisualizationGraphWidget *graphWidget) +{ + // Synchronize new graph with others in the zone + impl->m_Synchronizer->addGraph(*graphWidget); + + ui->dragDropContainer->insertDragWidget(index, graphWidget); } VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr variable) { + return createGraph(variable, -1); +} + +VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr variable, int index) +{ auto graphWidget = new VisualizationGraphWidget{ - defaultGraphName(*ui->visualizationZoneFrame->layout()), this}; + defaultGraphName(*ui->dragDropContainer->layout()), this}; // Set graph properties @@ -113,7 +132,7 @@ VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptrvisualizationZoneFrame->layout(); + auto frameLayout = ui->dragDropContainer->layout(); for (auto i = 0; i < frameLayout->count(); ++i) { auto graphChild = dynamic_cast(frameLayout->itemAt(i)->widget()); @@ -203,7 +222,7 @@ VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptrvisualizationZoneFrame->layout(); + auto layout = ui->dragDropContainer->layout(); if (layout->count() > 0) { // Case of a new graph in a existant zone if (auto visualizationGraphWidget @@ -216,7 +235,7 @@ VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptrrange(); } - this->addGraph(graphWidget); + this->insertGraph(index, graphWidget); graphWidget->addVariable(variable, range); @@ -240,6 +259,20 @@ VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr > variables, int index) +{ + if (variables.isEmpty()) + return nullptr; + + auto graphWidget = createGraph(variables.first(), index); + for (auto variableIt = variables.cbegin() + 1; variableIt != variables.cend(); ++variableIt) + { + graphWidget->addVariable(*variableIt, graphWidget->graphRange()); + } + + return graphWidget; +} + void VisualizationZoneWidget::accept(IVisualizationWidgetVisitor *visitor) { if (visitor) { @@ -248,7 +281,7 @@ void VisualizationZoneWidget::accept(IVisualizationWidgetVisitor *visitor) // Apply visitor to graph children: widgets different from graphs are not visited (no // action) processGraphs( - *ui->visualizationZoneFrame->layout(), + *ui->dragDropContainer->layout(), [visitor](VisualizationGraphWidget &graphWidget) { graphWidget.accept(visitor); }); visitor->visitLeave(this); @@ -276,10 +309,23 @@ QString VisualizationZoneWidget::name() const return ui->zoneNameLabel->text(); } +QMimeData *VisualizationZoneWidget::mimeData() const +{ + auto *mimeData = new QMimeData; + mimeData->setData(DragDropHelper::MIME_TYPE_ZONE, QByteArray()); + + return mimeData; +} + +bool VisualizationZoneWidget::isDragAllowed() const +{ + return true; +} + void VisualizationZoneWidget::closeEvent(QCloseEvent *event) { // Closes graphs in the zone - processGraphs(*ui->visualizationZoneFrame->layout(), + processGraphs(*ui->dragDropContainer->layout(), [](VisualizationGraphWidget &graphWidget) { graphWidget.close(); }); // Delete synchronization group from variable controller @@ -302,3 +348,47 @@ void VisualizationZoneWidget::onVariableAboutToBeRemoved(std::shared_ptr, variable), Q_ARG(QUuid, impl->m_SynchronisationGroupId)); } + +void VisualizationZoneWidget::dropMimeData(int index, const QMimeData *mimeData) +{ + auto& helper = sqpApp->dragDropHelper(); + if (mimeData->hasFormat(DragDropHelper::MIME_TYPE_GRAPH)) + { + auto graphWidget = static_cast(helper.getCurrentDragWidget()); + auto parentDragDropContainer = qobject_cast(graphWidget->parentWidget()); + Q_ASSERT(parentDragDropContainer); + + const auto& variables = graphWidget->variables(); + + if (!variables.empty()) + { + if (parentDragDropContainer != ui->dragDropContainer) + { + //The drop didn't occur in the same zone + + auto previousParentZoneWidget = graphWidget->parentZoneWidget(); + auto nbGraph = parentDragDropContainer->countDragWidget(); + if (nbGraph == 1) + { + //This is the only graph in the previous zone, close the zone + previousParentZoneWidget->close(); + } + else + { + //Close the graph + graphWidget->close(); + } + + //Creates the new graph in the zone + createGraph(variables, index); + } + else + { + //The drop occurred in the same zone + //Simple move of the graph, no variable operation associated + parentDragDropContainer->layout()->removeWidget(graphWidget); + ui->dragDropContainer->insertDragWidget(index, graphWidget); + } + } + } +} diff --git a/gui/ui/Visualization/VisualizationTabWidget.ui b/gui/ui/Visualization/VisualizationTabWidget.ui index 60b3e1c..afdd13e 100644 --- a/gui/ui/Visualization/VisualizationTabWidget.ui +++ b/gui/ui/Visualization/VisualizationTabWidget.ui @@ -62,12 +62,23 @@ 0 + + + + + + VisualizationDragDropContainer + QWidget +
Visualization/VisualizationDragDropContainer.h
+ 1 +
+
diff --git a/gui/ui/Visualization/VisualizationZoneWidget.ui b/gui/ui/Visualization/VisualizationZoneWidget.ui index 79d6be0..9746ca6 100644 --- a/gui/ui/Visualization/VisualizationZoneWidget.ui +++ b/gui/ui/Visualization/VisualizationZoneWidget.ui @@ -107,11 +107,22 @@ 0 + + + + + + VisualizationDragDropContainer + QWidget +
Visualization/VisualizationDragDropContainer.h
+ 1 +
+