##// END OF EJS Templates
Remove old graph from selected zones
trabillard -
r1345:4139acc0b06b
parent child
Show More
@@ -1,144 +1,144
1 #ifndef SCIQLOP_VARIABLECONTROLLER_H
1 #ifndef SCIQLOP_VARIABLECONTROLLER_H
2 #define SCIQLOP_VARIABLECONTROLLER_H
2 #define SCIQLOP_VARIABLECONTROLLER_H
3
3
4 #include "CoreGlobal.h"
4 #include "CoreGlobal.h"
5
5
6 #include <Data/AcquisitionDataPacket.h>
6 #include <Data/AcquisitionDataPacket.h>
7 #include <Data/SqpRange.h>
7 #include <Data/SqpRange.h>
8
8
9 #include <QLoggingCategory>
9 #include <QLoggingCategory>
10 #include <QObject>
10 #include <QObject>
11 #include <QUuid>
11 #include <QUuid>
12
12
13 #include <Common/spimpl.h>
13 #include <Common/spimpl.h>
14
14
15 class IDataProvider;
15 class IDataProvider;
16 class QItemSelectionModel;
16 class QItemSelectionModel;
17 class TimeController;
17 class TimeController;
18 class Variable;
18 class Variable;
19 class VariableModel;
19 class VariableModel;
20
20
21 Q_DECLARE_LOGGING_CATEGORY(LOG_VariableController)
21 Q_DECLARE_LOGGING_CATEGORY(LOG_VariableController)
22
22
23
23
24 /**
24 /**
25 * Possible types of zoom operation
25 * Possible types of zoom operation
26 */
26 */
27 enum class AcquisitionZoomType { ZoomOut, ZoomIn, PanRight, PanLeft, Unknown };
27 enum class AcquisitionZoomType { ZoomOut, ZoomIn, PanRight, PanLeft, Unknown };
28
28
29
29
30 /**
30 /**
31 * @brief The VariableController class aims to handle the variables in SciQlop.
31 * @brief The VariableController class aims to handle the variables in SciQlop.
32 */
32 */
33 class SCIQLOP_CORE_EXPORT VariableController : public QObject {
33 class SCIQLOP_CORE_EXPORT VariableController : public QObject {
34 Q_OBJECT
34 Q_OBJECT
35 public:
35 public:
36 explicit VariableController(QObject *parent = 0);
36 explicit VariableController(QObject *parent = 0);
37 virtual ~VariableController();
37 virtual ~VariableController();
38
38
39 VariableModel *variableModel() noexcept;
39 VariableModel *variableModel() noexcept;
40 QItemSelectionModel *variableSelectionModel() noexcept;
40 QItemSelectionModel *variableSelectionModel() noexcept;
41
41
42 void setTimeController(TimeController *timeController) noexcept;
42 void setTimeController(TimeController *timeController) noexcept;
43
43
44 /**
44 /**
45 * Clones the variable passed in parameter and adds the duplicate to the controller
45 * Clones the variable passed in parameter and adds the duplicate to the controller
46 * @param variable the variable to duplicate
46 * @param variable the variable to duplicate
47 * @return the duplicate created, nullptr if the variable couldn't be created
47 * @return the duplicate created, nullptr if the variable couldn't be created
48 */
48 */
49 std::shared_ptr<Variable> cloneVariable(std::shared_ptr<Variable> variable) noexcept;
49 std::shared_ptr<Variable> cloneVariable(std::shared_ptr<Variable> variable) noexcept;
50
50
51 /**
52 * Deletes from the controller the variable passed in parameter.
53 *
54 * Delete a variable includes:
55 * - the deletion of the various references to the variable in SciQlop
56 * - the deletion of the model variable
57 * - the deletion of the provider associated with the variable
58 * - removing the cache associated with the variable
59 *
60 * @param variable the variable to delete from the controller.
61 */
62 void deleteVariable(std::shared_ptr<Variable> variable) noexcept;
63
64 /**
65 * Deletes from the controller the variables passed in parameter.
66 * @param variables the variables to delete from the controller.
67 * @sa deleteVariable()
68 */
69 void deleteVariables(const QVector<std::shared_ptr<Variable> > &variables) noexcept;
70
71 /// Returns the MIME data associated to a list of variables
51 /// Returns the MIME data associated to a list of variables
72 QByteArray mimeDataForVariables(const QList<std::shared_ptr<Variable> > &variables) const;
52 QByteArray mimeDataForVariables(const QList<std::shared_ptr<Variable> > &variables) const;
73
53
74 /// Returns the list of variables contained in a MIME data
54 /// Returns the list of variables contained in a MIME data
75 QList<std::shared_ptr<Variable> > variablesForMimeData(const QByteArray &mimeData) const;
55 QList<std::shared_ptr<Variable> > variablesForMimeData(const QByteArray &mimeData) const;
76
56
77 static AcquisitionZoomType getZoomType(const SqpRange &range, const SqpRange &oldRange);
57 static AcquisitionZoomType getZoomType(const SqpRange &range, const SqpRange &oldRange);
78 signals:
58 signals:
79 /// Signal emitted when a variable is about to be deleted from the controller
59 /// Signal emitted when a variable is about to be deleted from the controller
80 void variableAboutToBeDeleted(std::shared_ptr<Variable> variable);
60 void variableAboutToBeDeleted(std::shared_ptr<Variable> variable);
81
61
82 /// Signal emitted when a data acquisition is requested on a range for a variable
62 /// Signal emitted when a data acquisition is requested on a range for a variable
83 void rangeChanged(std::shared_ptr<Variable> variable, const SqpRange &range);
63 void rangeChanged(std::shared_ptr<Variable> variable, const SqpRange &range);
84
64
85 /// Signal emitted when a sub range of the cacheRange of the variable can be displayed
65 /// Signal emitted when a sub range of the cacheRange of the variable can be displayed
86 void updateVarDisplaying(std::shared_ptr<Variable> variable, const SqpRange &range);
66 void updateVarDisplaying(std::shared_ptr<Variable> variable, const SqpRange &range);
87
67
88 /// Signal emitted when all acquisitions related to the variables have been completed (whether
68 /// Signal emitted when all acquisitions related to the variables have been completed (whether
89 /// validated, canceled, or failed)
69 /// validated, canceled, or failed)
90 void acquisitionFinished();
70 void acquisitionFinished();
91
71
92 void variableAdded(const std::shared_ptr<Variable> &variable);
72 void variableAdded(const std::shared_ptr<Variable> &variable);
93
73
94 public slots:
74 public slots:
75 /**
76 * Deletes from the controller the variable passed in parameter.
77 *
78 * Delete a variable includes:
79 * - the deletion of the various references to the variable in SciQlop
80 * - the deletion of the model variable
81 * - the deletion of the provider associated with the variable
82 * - removing the cache associated with the variable
83 *
84 * @param variable the variable to delete from the controller.
85 */
86 void deleteVariable(std::shared_ptr<Variable> variable) noexcept;
87
88 /**
89 * Deletes from the controller the variables passed in parameter.
90 * @param variables the variables to delete from the controller.
91 * @sa deleteVariable()
92 */
93 void deleteVariables(const QVector<std::shared_ptr<Variable> > &variables) noexcept;
94
95 /// Request the data loading of the variable whithin range
95 /// Request the data loading of the variable whithin range
96 void onRequestDataLoading(QVector<std::shared_ptr<Variable> > variables, const SqpRange &range,
96 void onRequestDataLoading(QVector<std::shared_ptr<Variable> > variables, const SqpRange &range,
97 bool synchronise);
97 bool synchronise);
98 /**
98 /**
99 * Creates a new variable and adds it to the model
99 * Creates a new variable and adds it to the model
100 * @param name the name of the new variable
100 * @param name the name of the new variable
101 * @param metadata the metadata of the new variable
101 * @param metadata the metadata of the new variable
102 * @param provider the data provider for the new variable
102 * @param provider the data provider for the new variable
103 * @return the pointer to the new variable or nullptr if the creation failed
103 * @return the pointer to the new variable or nullptr if the creation failed
104 */
104 */
105 std::shared_ptr<Variable> createVariable(const QString &name, const QVariantHash &metadata,
105 std::shared_ptr<Variable> createVariable(const QString &name, const QVariantHash &metadata,
106 std::shared_ptr<IDataProvider> provider) noexcept;
106 std::shared_ptr<IDataProvider> provider) noexcept;
107
107
108 /// Update the temporal parameters of every selected variable to dateTime
108 /// Update the temporal parameters of every selected variable to dateTime
109 void onDateTimeOnSelection(const SqpRange &dateTime);
109 void onDateTimeOnSelection(const SqpRange &dateTime);
110
110
111 /// Update the temporal parameters of the specified variable
111 /// Update the temporal parameters of the specified variable
112 void onUpdateDateTime(std::shared_ptr<Variable> variable, const SqpRange &dateTime);
112 void onUpdateDateTime(std::shared_ptr<Variable> variable, const SqpRange &dateTime);
113
113
114
114
115 void onDataProvided(QUuid vIdentifier, const SqpRange &rangeRequested,
115 void onDataProvided(QUuid vIdentifier, const SqpRange &rangeRequested,
116 const SqpRange &cacheRangeRequested,
116 const SqpRange &cacheRangeRequested,
117 QVector<AcquisitionDataPacket> dataAcquired);
117 QVector<AcquisitionDataPacket> dataAcquired);
118
118
119 void onVariableRetrieveDataInProgress(QUuid identifier, double progress);
119 void onVariableRetrieveDataInProgress(QUuid identifier, double progress);
120
120
121 /// Cancel the current request for the variable
121 /// Cancel the current request for the variable
122 void onAbortProgressRequested(std::shared_ptr<Variable> variable);
122 void onAbortProgressRequested(std::shared_ptr<Variable> variable);
123 void onAbortAcquisitionRequested(QUuid vIdentifier);
123 void onAbortAcquisitionRequested(QUuid vIdentifier);
124
124
125 // synchronization group methods
125 // synchronization group methods
126 void onAddSynchronizationGroupId(QUuid synchronizationGroupId);
126 void onAddSynchronizationGroupId(QUuid synchronizationGroupId);
127 void onRemoveSynchronizationGroupId(QUuid synchronizationGroupId);
127 void onRemoveSynchronizationGroupId(QUuid synchronizationGroupId);
128 void onAddSynchronized(std::shared_ptr<Variable> variable, QUuid synchronizationGroupId);
128 void onAddSynchronized(std::shared_ptr<Variable> variable, QUuid synchronizationGroupId);
129
129
130 /// Desynchronizes the variable of the group whose identifier is passed in parameter
130 /// Desynchronizes the variable of the group whose identifier is passed in parameter
131 /// @remarks the method does nothing if the variable is not part of the group
131 /// @remarks the method does nothing if the variable is not part of the group
132 void desynchronize(std::shared_ptr<Variable> variable, QUuid synchronizationGroupId);
132 void desynchronize(std::shared_ptr<Variable> variable, QUuid synchronizationGroupId);
133
133
134 void initialize();
134 void initialize();
135 void finalize();
135 void finalize();
136
136
137 private:
137 private:
138 void waitForFinish();
138 void waitForFinish();
139
139
140 class VariableControllerPrivate;
140 class VariableControllerPrivate;
141 spimpl::unique_impl_ptr<VariableControllerPrivate> impl;
141 spimpl::unique_impl_ptr<VariableControllerPrivate> impl;
142 };
142 };
143
143
144 #endif // SCIQLOP_VARIABLECONTROLLER_H
144 #endif // SCIQLOP_VARIABLECONTROLLER_H
@@ -1,155 +1,155
1 #ifndef SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
1 #ifndef SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
2 #define SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
2 #define SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
3
3
4 #include "Visualization/IVisualizationWidget.h"
4 #include "Visualization/IVisualizationWidget.h"
5 #include "Visualization/VisualizationDragWidget.h"
5 #include "Visualization/VisualizationDragWidget.h"
6
6
7 #include <QLoggingCategory>
7 #include <QLoggingCategory>
8 #include <QWidget>
8 #include <QWidget>
9
9
10 #include <memory>
10 #include <memory>
11
11
12 #include <Common/spimpl.h>
12 #include <Common/spimpl.h>
13
13
14 Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationGraphWidget)
14 Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationGraphWidget)
15
15
16 class QCPRange;
16 class QCPRange;
17 class QCustomPlot;
17 class QCustomPlot;
18 class SqpRange;
18 class SqpRange;
19 class Variable;
19 class Variable;
20 class VisualizationWidget;
20 class VisualizationWidget;
21 class VisualizationZoneWidget;
21 class VisualizationZoneWidget;
22 class VisualizationSelectionZoneItem;
22 class VisualizationSelectionZoneItem;
23
23
24 namespace Ui {
24 namespace Ui {
25 class VisualizationGraphWidget;
25 class VisualizationGraphWidget;
26 } // namespace Ui
26 } // namespace Ui
27
27
28 /// Defines options that can be associated with the graph
28 /// Defines options that can be associated with the graph
29 enum GraphFlag {
29 enum GraphFlag {
30 DisableAll = 0x0, ///< Disables acquisition and synchronization
30 DisableAll = 0x0, ///< Disables acquisition and synchronization
31 EnableAcquisition = 0x1, ///< When this flag is set, the change of the graph's range leads to
31 EnableAcquisition = 0x1, ///< When this flag is set, the change of the graph's range leads to
32 /// the acquisition of data
32 /// the acquisition of data
33 EnableSynchronization = 0x2, ///< When this flag is set, the change of the graph's range causes
33 EnableSynchronization = 0x2, ///< When this flag is set, the change of the graph's range causes
34 /// the call to the synchronization of the graphs contained in the
34 /// the call to the synchronization of the graphs contained in the
35 /// same zone of this graph
35 /// same zone of this graph
36 EnableAll = ~DisableAll ///< Enables acquisition and synchronization
36 EnableAll = ~DisableAll ///< Enables acquisition and synchronization
37 };
37 };
38
38
39 Q_DECLARE_FLAGS(GraphFlags, GraphFlag)
39 Q_DECLARE_FLAGS(GraphFlags, GraphFlag)
40 Q_DECLARE_OPERATORS_FOR_FLAGS(GraphFlags)
40 Q_DECLARE_OPERATORS_FOR_FLAGS(GraphFlags)
41
41
42 class VisualizationGraphWidget : public VisualizationDragWidget, public IVisualizationWidget {
42 class VisualizationGraphWidget : public VisualizationDragWidget, public IVisualizationWidget {
43 Q_OBJECT
43 Q_OBJECT
44
44
45 friend class QCustomPlotSynchronizer;
45 friend class QCustomPlotSynchronizer;
46 friend class VisualizationGraphRenderingDelegate;
46 friend class VisualizationGraphRenderingDelegate;
47
47
48 public:
48 public:
49 explicit VisualizationGraphWidget(const QString &name = {}, QWidget *parent = 0);
49 explicit VisualizationGraphWidget(const QString &name = {}, QWidget *parent = 0);
50 virtual ~VisualizationGraphWidget();
50 virtual ~VisualizationGraphWidget();
51
51
52 /// Returns the VisualizationZoneWidget which contains the graph or nullptr
52 /// Returns the VisualizationZoneWidget which contains the graph or nullptr
53 VisualizationZoneWidget *parentZoneWidget() const noexcept;
53 VisualizationZoneWidget *parentZoneWidget() const noexcept;
54
54
55 /// Returns the main VisualizationWidget which contains the graph or nullptr
55 /// Returns the main VisualizationWidget which contains the graph or nullptr
56 VisualizationWidget *parentVisualizationWidget() const;
56 VisualizationWidget *parentVisualizationWidget() const;
57
57
58 /// Sets graph options
58 /// Sets graph options
59 void setFlags(GraphFlags flags);
59 void setFlags(GraphFlags flags);
60
60
61 void addVariable(std::shared_ptr<Variable> variable, SqpRange range);
61 void addVariable(std::shared_ptr<Variable> variable, SqpRange range);
62
62
63 /// Removes a variable from the graph
63 /// Removes a variable from the graph
64 void removeVariable(std::shared_ptr<Variable> variable) noexcept;
64 void removeVariable(std::shared_ptr<Variable> variable) noexcept;
65
65
66 /// Returns the list of all variables used in the graph
66 /// Returns the list of all variables used in the graph
67 QList<std::shared_ptr<Variable> > variables() const;
67 QList<std::shared_ptr<Variable> > variables() const;
68
68
69 /// Sets the y-axis range based on the data of a variable
69 /// Sets the y-axis range based on the data of a variable
70 void setYRange(std::shared_ptr<Variable> variable);
70 void setYRange(std::shared_ptr<Variable> variable);
71 SqpRange graphRange() const noexcept;
71 SqpRange graphRange() const noexcept;
72 void setGraphRange(const SqpRange &range);
72 void setGraphRange(const SqpRange &range, bool calibration = false);
73
73
74 // Zones
74 // Zones
75 /// Returns the ranges of all the selection zones on the graph
75 /// Returns the ranges of all the selection zones on the graph
76 QVector<SqpRange> selectionZoneRanges() const;
76 QVector<SqpRange> selectionZoneRanges() const;
77 /// Adds new selection zones in the graph
77 /// Adds new selection zones in the graph
78 void addSelectionZones(const QVector<SqpRange> &ranges);
78 void addSelectionZones(const QVector<SqpRange> &ranges);
79 /// Removes the specified selection zone
79 /// Removes the specified selection zone
80 void removeSelectionZone(VisualizationSelectionZoneItem *selectionZone);
80 void removeSelectionZone(VisualizationSelectionZoneItem *selectionZone);
81
81
82 /// Undo the last zoom done with a zoom box
82 /// Undo the last zoom done with a zoom box
83 void undoZoom();
83 void undoZoom();
84
84
85 // IVisualizationWidget interface
85 // IVisualizationWidget interface
86 void accept(IVisualizationWidgetVisitor *visitor) override;
86 void accept(IVisualizationWidgetVisitor *visitor) override;
87 bool canDrop(const Variable &variable) const override;
87 bool canDrop(const Variable &variable) const override;
88 bool contains(const Variable &variable) const override;
88 bool contains(const Variable &variable) const override;
89 QString name() const override;
89 QString name() const override;
90
90
91 // VisualisationDragWidget
91 // VisualisationDragWidget
92 QMimeData *mimeData(const QPoint &position) const override;
92 QMimeData *mimeData(const QPoint &position) const override;
93 QPixmap customDragPixmap(const QPoint &dragPosition) override;
93 QPixmap customDragPixmap(const QPoint &dragPosition) override;
94 bool isDragAllowed() const override;
94 bool isDragAllowed() const override;
95 void highlightForMerge(bool highlighted) override;
95 void highlightForMerge(bool highlighted) override;
96
96
97 // Cursors
97 // Cursors
98 /// Adds or moves the vertical cursor at the specified value on the x-axis
98 /// Adds or moves the vertical cursor at the specified value on the x-axis
99 void addVerticalCursor(double time);
99 void addVerticalCursor(double time);
100 /// Adds or moves the vertical cursor at the specified value on the x-axis
100 /// Adds or moves the vertical cursor at the specified value on the x-axis
101 void addVerticalCursorAtViewportPosition(double position);
101 void addVerticalCursorAtViewportPosition(double position);
102 void removeVerticalCursor();
102 void removeVerticalCursor();
103 /// Adds or moves the vertical cursor at the specified value on the y-axis
103 /// Adds or moves the vertical cursor at the specified value on the y-axis
104 void addHorizontalCursor(double value);
104 void addHorizontalCursor(double value);
105 /// Adds or moves the vertical cursor at the specified value on the y-axis
105 /// Adds or moves the vertical cursor at the specified value on the y-axis
106 void addHorizontalCursorAtViewportPosition(double position);
106 void addHorizontalCursorAtViewportPosition(double position);
107 void removeHorizontalCursor();
107 void removeHorizontalCursor();
108
108
109 signals:
109 signals:
110 void synchronize(const SqpRange &range, const SqpRange &oldRange);
110 void synchronize(const SqpRange &range, const SqpRange &oldRange);
111 void requestDataLoading(QVector<std::shared_ptr<Variable> > variable, const SqpRange &range,
111 void requestDataLoading(QVector<std::shared_ptr<Variable> > variable, const SqpRange &range,
112 bool synchronise);
112 bool synchronise);
113
113
114 /// Signal emitted when the variable is about to be removed from the graph
114 /// Signal emitted when the variable is about to be removed from the graph
115 void variableAboutToBeRemoved(std::shared_ptr<Variable> var);
115 void variableAboutToBeRemoved(std::shared_ptr<Variable> var);
116 /// Signal emitted when the variable has been added to the graph
116 /// Signal emitted when the variable has been added to the graph
117 void variableAdded(std::shared_ptr<Variable> var);
117 void variableAdded(std::shared_ptr<Variable> var);
118
118
119 protected:
119 protected:
120 void closeEvent(QCloseEvent *event) override;
120 void closeEvent(QCloseEvent *event) override;
121 void enterEvent(QEvent *event) override;
121 void enterEvent(QEvent *event) override;
122 void leaveEvent(QEvent *event) override;
122 void leaveEvent(QEvent *event) override;
123
123
124 QCustomPlot &plot() const noexcept;
124 QCustomPlot &plot() const noexcept;
125
125
126 private:
126 private:
127 Ui::VisualizationGraphWidget *ui;
127 Ui::VisualizationGraphWidget *ui;
128
128
129 class VisualizationGraphWidgetPrivate;
129 class VisualizationGraphWidgetPrivate;
130 spimpl::unique_impl_ptr<VisualizationGraphWidgetPrivate> impl;
130 spimpl::unique_impl_ptr<VisualizationGraphWidgetPrivate> impl;
131
131
132 private slots:
132 private slots:
133 /// Slot called when right clicking on the graph (displays a menu)
133 /// Slot called when right clicking on the graph (displays a menu)
134 void onGraphMenuRequested(const QPoint &pos) noexcept;
134 void onGraphMenuRequested(const QPoint &pos) noexcept;
135
135
136 /// Rescale the X axe to range parameter
136 /// Rescale the X axe to range parameter
137 void onRangeChanged(const QCPRange &t1, const QCPRange &t2);
137 void onRangeChanged(const QCPRange &t1, const QCPRange &t2);
138
138
139 /// Slot called when a mouse double click was made
139 /// Slot called when a mouse double click was made
140 void onMouseDoubleClick(QMouseEvent *event) noexcept;
140 void onMouseDoubleClick(QMouseEvent *event) noexcept;
141 /// Slot called when a mouse move was made
141 /// Slot called when a mouse move was made
142 void onMouseMove(QMouseEvent *event) noexcept;
142 void onMouseMove(QMouseEvent *event) noexcept;
143 /// Slot called when a mouse wheel was made, to perform some processing before the zoom is done
143 /// Slot called when a mouse wheel was made, to perform some processing before the zoom is done
144 void onMouseWheel(QWheelEvent *event) noexcept;
144 void onMouseWheel(QWheelEvent *event) noexcept;
145 /// Slot called when a mouse press was made, to activate the calibration of a graph
145 /// Slot called when a mouse press was made, to activate the calibration of a graph
146 void onMousePress(QMouseEvent *event) noexcept;
146 void onMousePress(QMouseEvent *event) noexcept;
147 /// Slot called when a mouse release was made, to deactivate the calibration of a graph
147 /// Slot called when a mouse release was made, to deactivate the calibration of a graph
148 void onMouseRelease(QMouseEvent *event) noexcept;
148 void onMouseRelease(QMouseEvent *event) noexcept;
149
149
150 void onDataCacheVariableUpdated();
150 void onDataCacheVariableUpdated();
151
151
152 void onUpdateVarDisplaying(std::shared_ptr<Variable> variable, const SqpRange &range);
152 void onUpdateVarDisplaying(std::shared_ptr<Variable> variable, const SqpRange &range);
153 };
153 };
154
154
155 #endif // SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
155 #endif // SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
@@ -1,498 +1,545
1 #include "Catalogue/CatalogueEventsWidget.h"
1 #include "Catalogue/CatalogueEventsWidget.h"
2 #include "ui_CatalogueEventsWidget.h"
2 #include "ui_CatalogueEventsWidget.h"
3
3
4 #include <Catalogue/CatalogueController.h>
4 #include <Catalogue/CatalogueController.h>
5 #include <Catalogue/CatalogueEventsModel.h>
5 #include <Catalogue/CatalogueEventsModel.h>
6 #include <Catalogue/CatalogueExplorerHelper.h>
6 #include <Catalogue/CatalogueExplorerHelper.h>
7 #include <CatalogueDao.h>
7 #include <CatalogueDao.h>
8 #include <DBCatalogue.h>
8 #include <DBCatalogue.h>
9 #include <DBEventProduct.h>
9 #include <DBEventProduct.h>
10 #include <DataSource/DataSourceController.h>
10 #include <DataSource/DataSourceController.h>
11 #include <DataSource/DataSourceItem.h>
11 #include <SqpApplication.h>
12 #include <SqpApplication.h>
12 #include <Variable/Variable.h>
13 #include <Variable/Variable.h>
13 #include <Variable/VariableController.h>
14 #include <Variable/VariableController.h>
15 #include <Visualization/VisualizationGraphWidget.h>
14 #include <Visualization/VisualizationTabWidget.h>
16 #include <Visualization/VisualizationTabWidget.h>
15 #include <Visualization/VisualizationWidget.h>
17 #include <Visualization/VisualizationWidget.h>
16 #include <Visualization/VisualizationZoneWidget.h>
18 #include <Visualization/VisualizationZoneWidget.h>
17 #include <Visualization/VisualizationGraphWidget.h>
18
19
19 #include <QDialog>
20 #include <QDialog>
20 #include <QDialogButtonBox>
21 #include <QDialogButtonBox>
21 #include <QListWidget>
22 #include <QListWidget>
22 #include <QMessageBox>
23 #include <QMessageBox>
23
24
24 Q_LOGGING_CATEGORY(LOG_CatalogueEventsWidget, "CatalogueEventsWidget")
25 Q_LOGGING_CATEGORY(LOG_CatalogueEventsWidget, "CatalogueEventsWidget")
25
26
26 /// Fixed size of the validation column
27 /// Fixed size of the validation column
27 const auto VALIDATION_COLUMN_SIZE = 35;
28 const auto VALIDATION_COLUMN_SIZE = 35;
28
29
29 struct CatalogueEventsWidget::CatalogueEventsWidgetPrivate {
30 struct CatalogueEventsWidget::CatalogueEventsWidgetPrivate {
30
31
31 CatalogueEventsModel *m_Model = nullptr;
32 CatalogueEventsModel *m_Model = nullptr;
32 QStringList m_ZonesForTimeMode;
33 QStringList m_ZonesForTimeMode;
33 QString m_ZoneForGraphMode;
34 QString m_ZoneForGraphMode;
34 QVector<std::shared_ptr<DBCatalogue> > m_DisplayedCatalogues;
35 QVector<std::shared_ptr<DBCatalogue> > m_DisplayedCatalogues;
35 bool m_AllEventDisplayed = false;
36 bool m_AllEventDisplayed = false;
36 QVector<VisualizationGraphWidget *> m_CustomGraphs;
37 QVector<VisualizationGraphWidget *> m_CustomGraphs;
37
38
38 VisualizationWidget *m_VisualizationWidget = nullptr;
39 VisualizationWidget *m_VisualizationWidget = nullptr;
39
40
40 void setEvents(const QVector<std::shared_ptr<DBEvent> > &events, CatalogueEventsWidget *widget)
41 void setEvents(const QVector<std::shared_ptr<DBEvent> > &events, CatalogueEventsWidget *widget)
41 {
42 {
42 widget->ui->treeView->setSortingEnabled(false);
43 widget->ui->treeView->setSortingEnabled(false);
43 m_Model->setEvents(events);
44 m_Model->setEvents(events);
44 widget->ui->treeView->setSortingEnabled(true);
45 widget->ui->treeView->setSortingEnabled(true);
45
46
46 for (auto event : events) {
47 for (auto event : events) {
47 if (sqpApp->catalogueController().eventHasChanges(event)) {
48 if (sqpApp->catalogueController().eventHasChanges(event)) {
48 auto index = m_Model->indexOf(event);
49 auto index = m_Model->indexOf(event);
49 widget->setEventChanges(event, true);
50 widget->setEventChanges(event, true);
50 }
51 }
51 }
52 }
52 }
53 }
53
54
54 void addEvent(const std::shared_ptr<DBEvent> &event, QTreeView *treeView)
55 void addEvent(const std::shared_ptr<DBEvent> &event, QTreeView *treeView)
55 {
56 {
56 treeView->setSortingEnabled(false);
57 treeView->setSortingEnabled(false);
57 m_Model->addEvent(event);
58 m_Model->addEvent(event);
58 treeView->setSortingEnabled(true);
59 treeView->setSortingEnabled(true);
59 }
60 }
60
61
61 void removeEvent(const std::shared_ptr<DBEvent> &event, QTreeView *treeView)
62 void removeEvent(const std::shared_ptr<DBEvent> &event, QTreeView *treeView)
62 {
63 {
63 treeView->setSortingEnabled(false);
64 treeView->setSortingEnabled(false);
64 m_Model->removeEvent(event);
65 m_Model->removeEvent(event);
65 treeView->setSortingEnabled(true);
66 treeView->setSortingEnabled(true);
66 }
67 }
67
68
68 QStringList getAvailableVisualizationZoneList() const
69 QStringList getAvailableVisualizationZoneList() const
69 {
70 {
70 if (m_VisualizationWidget) {
71 if (m_VisualizationWidget) {
71 if (auto tab = m_VisualizationWidget->currentTabWidget()) {
72 if (auto tab = m_VisualizationWidget->currentTabWidget()) {
72 return tab->availableZoneWidgets();
73 return tab->availableZoneWidgets();
73 }
74 }
74 }
75 }
75
76
76 return QStringList{};
77 return QStringList{};
77 }
78 }
78
79
79 QStringList selectZone(QWidget *parent, const QStringList &selectedZones,
80 QStringList selectZone(QWidget *parent, const QStringList &selectedZones,
80 bool allowMultiSelection, const QPoint &location)
81 bool allowMultiSelection, const QPoint &location)
81 {
82 {
82 auto availableZones = getAvailableVisualizationZoneList();
83 auto availableZones = getAvailableVisualizationZoneList();
83 if (availableZones.isEmpty()) {
84 if (availableZones.isEmpty()) {
84 return QStringList{};
85 return QStringList{};
85 }
86 }
86
87
87 QDialog d(parent, Qt::Tool);
88 QDialog d(parent, Qt::Tool);
88 d.setWindowTitle("Choose a zone");
89 d.setWindowTitle("Choose a zone");
89 auto layout = new QVBoxLayout{&d};
90 auto layout = new QVBoxLayout{&d};
90 layout->setContentsMargins(0, 0, 0, 0);
91 layout->setContentsMargins(0, 0, 0, 0);
91 auto listWidget = new QListWidget{&d};
92 auto listWidget = new QListWidget{&d};
92 layout->addWidget(listWidget);
93 layout->addWidget(listWidget);
93
94
94 QSet<QListWidgetItem *> checkedItems;
95 QSet<QListWidgetItem *> checkedItems;
95 for (auto zone : availableZones) {
96 for (auto zone : availableZones) {
96 auto item = new QListWidgetItem{zone};
97 auto item = new QListWidgetItem{zone};
97 item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsUserCheckable);
98 item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsUserCheckable);
98 if (selectedZones.contains(zone)) {
99 if (selectedZones.contains(zone)) {
99 item->setCheckState(Qt::Checked);
100 item->setCheckState(Qt::Checked);
100 checkedItems << item;
101 checkedItems << item;
101 }
102 }
102 else {
103 else {
103 item->setCheckState(Qt::Unchecked);
104 item->setCheckState(Qt::Unchecked);
104 }
105 }
105
106
106 listWidget->addItem(item);
107 listWidget->addItem(item);
107 }
108 }
108
109
109 auto buttonBox = new QDialogButtonBox{QDialogButtonBox::Ok, &d};
110 auto buttonBox = new QDialogButtonBox{QDialogButtonBox::Ok, &d};
110 layout->addWidget(buttonBox);
111 layout->addWidget(buttonBox);
111
112
112 QObject::connect(buttonBox, &QDialogButtonBox::accepted, &d, &QDialog::accept);
113 QObject::connect(buttonBox, &QDialogButtonBox::accepted, &d, &QDialog::accept);
113 QObject::connect(buttonBox, &QDialogButtonBox::rejected, &d, &QDialog::reject);
114 QObject::connect(buttonBox, &QDialogButtonBox::rejected, &d, &QDialog::reject);
114
115
115 QObject::connect(listWidget, &QListWidget::itemChanged,
116 QObject::connect(listWidget, &QListWidget::itemChanged,
116 [&checkedItems, allowMultiSelection, listWidget](auto item) {
117 [&checkedItems, allowMultiSelection, listWidget](auto item) {
117 if (item->checkState() == Qt::Checked) {
118 if (item->checkState() == Qt::Checked) {
118 if (!allowMultiSelection) {
119 if (!allowMultiSelection) {
119 for (auto checkedItem : checkedItems) {
120 for (auto checkedItem : checkedItems) {
120 listWidget->blockSignals(true);
121 listWidget->blockSignals(true);
121 checkedItem->setCheckState(Qt::Unchecked);
122 checkedItem->setCheckState(Qt::Unchecked);
122 listWidget->blockSignals(false);
123 listWidget->blockSignals(false);
123 }
124 }
124
125
125 checkedItems.clear();
126 checkedItems.clear();
126 }
127 }
127 checkedItems << item;
128 checkedItems << item;
128 }
129 }
129 else {
130 else {
130 checkedItems.remove(item);
131 checkedItems.remove(item);
131 }
132 }
132 });
133 });
133
134
134 QStringList result;
135 QStringList result;
135
136
136 d.setMinimumWidth(120);
137 d.setMinimumWidth(120);
137 d.resize(d.minimumSizeHint());
138 d.resize(d.minimumSizeHint());
138 d.move(location);
139 d.move(location);
139 if (d.exec() == QDialog::Accepted) {
140 if (d.exec() == QDialog::Accepted) {
140 for (auto item : checkedItems) {
141 for (auto item : checkedItems) {
141 result += item->text();
142 result += item->text();
142 }
143 }
143 }
144 }
144 else {
145 else {
145 result = selectedZones;
146 result = selectedZones;
146 }
147 }
147
148
148 return result;
149 return result;
149 }
150 }
150
151
151 void updateForTimeMode(QTreeView *treeView)
152 void updateForTimeMode(QTreeView *treeView)
152 {
153 {
153 auto selectedRows = treeView->selectionModel()->selectedRows();
154 auto selectedRows = treeView->selectionModel()->selectedRows();
154
155
155 if (selectedRows.count() == 1) {
156 if (selectedRows.count() == 1) {
156 auto event = m_Model->getEvent(selectedRows.first());
157 auto event = m_Model->getEvent(selectedRows.first());
157 if (event) {
158 if (event) {
158 if (m_VisualizationWidget) {
159 if (m_VisualizationWidget) {
159 if (auto tab = m_VisualizationWidget->currentTabWidget()) {
160 if (auto tab = m_VisualizationWidget->currentTabWidget()) {
160
161
161 for (auto zoneName : m_ZonesForTimeMode) {
162 for (auto zoneName : m_ZonesForTimeMode) {
162 if (auto zone = tab->getZoneWithName(zoneName)) {
163 if (auto zone = tab->getZoneWithName(zoneName)) {
163 SqpRange eventRange;
164 SqpRange eventRange;
164 eventRange.m_TStart = event->getTStart();
165 eventRange.m_TStart = event->getTStart();
165 eventRange.m_TEnd = event->getTEnd();
166 eventRange.m_TEnd = event->getTEnd();
166 zone->setZoneRange(eventRange);
167 zone->setZoneRange(eventRange);
167 }
168 }
168 }
169 }
169 }
170 }
170 else {
171 else {
171 qCWarning(LOG_CatalogueEventsWidget())
172 qCWarning(LOG_CatalogueEventsWidget())
172 << "updateTimeZone: no tab found in the visualization";
173 << "updateTimeZone: no tab found in the visualization";
173 }
174 }
174 }
175 }
175 else {
176 else {
176 qCWarning(LOG_CatalogueEventsWidget())
177 qCWarning(LOG_CatalogueEventsWidget())
177 << "updateTimeZone: visualization widget not found";
178 << "updateTimeZone: visualization widget not found";
178 }
179 }
179 }
180 }
180 }
181 }
181 else {
182 else {
182 qCWarning(LOG_CatalogueEventsWidget())
183 qCWarning(LOG_CatalogueEventsWidget())
183 << "updateTimeZone: not compatible with multiple events selected";
184 << "updateTimeZone: not compatible with multiple events selected";
184 }
185 }
185 }
186 }
186
187
187 void updateForGraphMode(QTreeView *treeView)
188 void updateForGraphMode(QTreeView *treeView)
188 {
189 {
189 auto selectedRows = treeView->selectionModel()->selectedRows();
190 auto selectedRows = treeView->selectionModel()->selectedRows();
190
191
191 if (selectedRows.count() == 1) {
192 if (selectedRows.count() == 1) {
192 auto event = m_Model->getEvent(selectedRows.first());
193 auto event = m_Model->getEvent(selectedRows.first());
193 if (m_VisualizationWidget) {
194 if (m_VisualizationWidget && event) {
194 if (auto tab = m_VisualizationWidget->currentTabWidget()) {
195 if (auto tab = m_VisualizationWidget->currentTabWidget()) {
195 if (auto zone = tab->getZoneWithName(m_ZoneForGraphMode)) {
196 if (auto zone = tab->getZoneWithName(m_ZoneForGraphMode)) {
196
197
197 for (auto graph : m_CustomGraphs) {
198 for (auto graph : m_CustomGraphs) {
198 graph->close();
199 graph->close();
200 auto variables = graph->variables().toVector();
201
202 QMetaObject::invokeMethod(
203 &sqpApp->variableController(), "deleteVariables",
204 Qt::QueuedConnection,
205 Q_ARG(QVector<std::shared_ptr<Variable> >, variables));
199 }
206 }
200 m_CustomGraphs.clear();
207 m_CustomGraphs.clear();
201
208
209 QVector<SqpRange> graphRanges;
210 double maxDt = 0;
211 for (auto eventProduct : event->getEventProducts()) {
212 SqpRange eventRange;
213 eventRange.m_TStart = eventProduct.getTStart();
214 eventRange.m_TEnd = eventProduct.getTEnd();
215 graphRanges << eventRange;
216
217 auto dt = eventRange.m_TEnd - eventRange.m_TStart;
218 if (dt > maxDt) {
219 maxDt = dt;
220 }
221 }
222
223 QVector<SqpRange> correctedGraphRanges;
224 for (auto range : graphRanges) {
225 auto dt = range.m_TEnd - range.m_TStart;
226 auto diff = qAbs((maxDt - dt) / 2.0);
227
228 SqpRange correctedRange;
229 correctedRange.m_TStart = range.m_TStart - diff;
230 correctedRange.m_TEnd = range.m_TEnd + diff;
231
232 correctedGraphRanges << correctedRange;
233 }
234
235 auto itRange = correctedGraphRanges.cbegin();
202 for (auto eventProduct : event->getEventProducts()) {
236 for (auto eventProduct : event->getEventProducts()) {
203 auto productId = eventProduct.getProductId();
237 auto productId = eventProduct.getProductId();
204
238
239 auto range = *itRange;
240 ++itRange;
241
205 auto context = new QObject{treeView};
242 auto context = new QObject{treeView};
206 QObject::connect(&sqpApp->variableController(),
243 QObject::connect(
207 &VariableController::variableAdded, context,
244 &sqpApp->variableController(), &VariableController::variableAdded,
208 [this, zone, context](auto variable) {
245 context,
209 auto graph = zone->createGraph(variable);
246 [this, zone, context, range, productId](auto variable) {
210 m_CustomGraphs << graph;
247
211 delete context; // removes the connection
248 if (variable->metadata()
212 },
249 .value(DataSourceItem::ID_DATA_KEY, "UnknownID")
213 Qt::QueuedConnection);
250 .toString()
251 == productId) {
252 auto graph = zone->createGraph(variable);
253 m_CustomGraphs << graph;
254
255 graph->setGraphRange(range, true);
256
257 delete context; // removes the connection
258 }
259 },
260 Qt::QueuedConnection);
214
261
215 QMetaObject::invokeMethod(
262 QMetaObject::invokeMethod(
216 &sqpApp->dataSourceController(), "requestVariableFromProductIdKey",
263 &sqpApp->dataSourceController(), "requestVariableFromProductIdKey",
217 Qt::QueuedConnection, Q_ARG(QString, productId));
264 Qt::QueuedConnection, Q_ARG(QString, productId));
218 }
265 }
219 }
266 }
220 }
267 }
221 else {
268 else {
222 qCWarning(LOG_CatalogueEventsWidget())
269 qCWarning(LOG_CatalogueEventsWidget())
223 << "updateGraphMode: no tab found in the visualization";
270 << "updateGraphMode: no tab found in the visualization";
224 }
271 }
225 }
272 }
226 else {
273 else {
227 qCWarning(LOG_CatalogueEventsWidget())
274 qCWarning(LOG_CatalogueEventsWidget())
228 << "updateGraphMode: visualization widget not found";
275 << "updateGraphMode: visualization widget not found";
229 }
276 }
230 }
277 }
231 else {
278 else {
232 qCWarning(LOG_CatalogueEventsWidget())
279 qCWarning(LOG_CatalogueEventsWidget())
233 << "updateGraphMode: not compatible with multiple events selected";
280 << "updateGraphMode: not compatible with multiple events selected";
234 }
281 }
235 }
282 }
236
283
237 void getSelectedItems(
284 void getSelectedItems(
238 QTreeView *treeView, QVector<std::shared_ptr<DBEvent> > &events,
285 QTreeView *treeView, QVector<std::shared_ptr<DBEvent> > &events,
239 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > &eventProducts)
286 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > &eventProducts)
240 {
287 {
241 for (auto rowIndex : treeView->selectionModel()->selectedRows()) {
288 for (auto rowIndex : treeView->selectionModel()->selectedRows()) {
242 auto itemType = m_Model->itemTypeOf(rowIndex);
289 auto itemType = m_Model->itemTypeOf(rowIndex);
243 if (itemType == CatalogueEventsModel::ItemType::Event) {
290 if (itemType == CatalogueEventsModel::ItemType::Event) {
244 events << m_Model->getEvent(rowIndex);
291 events << m_Model->getEvent(rowIndex);
245 }
292 }
246 else if (itemType == CatalogueEventsModel::ItemType::EventProduct) {
293 else if (itemType == CatalogueEventsModel::ItemType::EventProduct) {
247 eventProducts << qMakePair(m_Model->getParentEvent(rowIndex),
294 eventProducts << qMakePair(m_Model->getParentEvent(rowIndex),
248 m_Model->getEventProduct(rowIndex));
295 m_Model->getEventProduct(rowIndex));
249 }
296 }
250 }
297 }
251 }
298 }
252 };
299 };
253
300
254 CatalogueEventsWidget::CatalogueEventsWidget(QWidget *parent)
301 CatalogueEventsWidget::CatalogueEventsWidget(QWidget *parent)
255 : QWidget(parent),
302 : QWidget(parent),
256 ui(new Ui::CatalogueEventsWidget),
303 ui(new Ui::CatalogueEventsWidget),
257 impl{spimpl::make_unique_impl<CatalogueEventsWidgetPrivate>()}
304 impl{spimpl::make_unique_impl<CatalogueEventsWidgetPrivate>()}
258 {
305 {
259 ui->setupUi(this);
306 ui->setupUi(this);
260
307
261 impl->m_Model = new CatalogueEventsModel{this};
308 impl->m_Model = new CatalogueEventsModel{this};
262 ui->treeView->setModel(impl->m_Model);
309 ui->treeView->setModel(impl->m_Model);
263
310
264 ui->treeView->setSortingEnabled(true);
311 ui->treeView->setSortingEnabled(true);
265 ui->treeView->setDragDropMode(QAbstractItemView::DragDrop);
312 ui->treeView->setDragDropMode(QAbstractItemView::DragDrop);
266 ui->treeView->setDragEnabled(true);
313 ui->treeView->setDragEnabled(true);
267
314
268 connect(ui->btnTime, &QToolButton::clicked, [this](auto checked) {
315 connect(ui->btnTime, &QToolButton::clicked, [this](auto checked) {
269 if (checked) {
316 if (checked) {
270 ui->btnChart->setChecked(false);
317 ui->btnChart->setChecked(false);
271 impl->m_ZonesForTimeMode
318 impl->m_ZonesForTimeMode
272 = impl->selectZone(this, impl->m_ZonesForTimeMode, true,
319 = impl->selectZone(this, impl->m_ZonesForTimeMode, true,
273 this->mapToGlobal(ui->btnTime->frameGeometry().center()));
320 this->mapToGlobal(ui->btnTime->frameGeometry().center()));
274
321
275 impl->updateForTimeMode(ui->treeView);
322 impl->updateForTimeMode(ui->treeView);
276 }
323 }
277 });
324 });
278
325
279 connect(ui->btnChart, &QToolButton::clicked, [this](auto checked) {
326 connect(ui->btnChart, &QToolButton::clicked, [this](auto checked) {
280 if (checked) {
327 if (checked) {
281 ui->btnTime->setChecked(false);
328 ui->btnTime->setChecked(false);
282 impl->m_ZoneForGraphMode
329 impl->m_ZoneForGraphMode
283 = impl->selectZone(this, {impl->m_ZoneForGraphMode}, false,
330 = impl->selectZone(this, {impl->m_ZoneForGraphMode}, false,
284 this->mapToGlobal(ui->btnChart->frameGeometry().center()))
331 this->mapToGlobal(ui->btnChart->frameGeometry().center()))
285 .value(0);
332 .value(0);
286
333
287 impl->updateForGraphMode(ui->treeView);
334 impl->updateForGraphMode(ui->treeView);
288 }
335 }
289 });
336 });
290
337
291 connect(ui->btnRemove, &QToolButton::clicked, [this]() {
338 connect(ui->btnRemove, &QToolButton::clicked, [this]() {
292 QVector<std::shared_ptr<DBEvent> > events;
339 QVector<std::shared_ptr<DBEvent> > events;
293 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > eventProducts;
340 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > eventProducts;
294 impl->getSelectedItems(ui->treeView, events, eventProducts);
341 impl->getSelectedItems(ui->treeView, events, eventProducts);
295
342
296 if (!events.isEmpty() && eventProducts.isEmpty()) {
343 if (!events.isEmpty() && eventProducts.isEmpty()) {
297
344
298 if (QMessageBox::warning(this, tr("Remove Event(s)"),
345 if (QMessageBox::warning(this, tr("Remove Event(s)"),
299 tr("The selected event(s) will be permanently removed "
346 tr("The selected event(s) will be permanently removed "
300 "from the repository!\nAre you sure you want to continue?"),
347 "from the repository!\nAre you sure you want to continue?"),
301 QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
348 QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
302 == QMessageBox::Yes) {
349 == QMessageBox::Yes) {
303
350
304 for (auto event : events) {
351 for (auto event : events) {
305 sqpApp->catalogueController().removeEvent(event);
352 sqpApp->catalogueController().removeEvent(event);
306 impl->removeEvent(event, ui->treeView);
353 impl->removeEvent(event, ui->treeView);
307 }
354 }
308 }
355 }
309 }
356 }
310 });
357 });
311
358
312 connect(ui->treeView, &QTreeView::clicked, this, &CatalogueEventsWidget::emitSelection);
359 connect(ui->treeView, &QTreeView::clicked, this, &CatalogueEventsWidget::emitSelection);
313 connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this,
360 connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this,
314 &CatalogueEventsWidget::emitSelection);
361 &CatalogueEventsWidget::emitSelection);
315
362
316 ui->btnRemove->setEnabled(false); // Disabled by default when nothing is selected
363 ui->btnRemove->setEnabled(false); // Disabled by default when nothing is selected
317 connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, [this]() {
364 connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, [this]() {
318 auto isNotMultiSelection = ui->treeView->selectionModel()->selectedRows().count() <= 1;
365 auto isNotMultiSelection = ui->treeView->selectionModel()->selectedRows().count() <= 1;
319 ui->btnChart->setEnabled(isNotMultiSelection);
366 ui->btnChart->setEnabled(isNotMultiSelection);
320 ui->btnTime->setEnabled(isNotMultiSelection);
367 ui->btnTime->setEnabled(isNotMultiSelection);
321
368
322 if (isNotMultiSelection && ui->btnTime->isChecked()) {
369 if (isNotMultiSelection && ui->btnTime->isChecked()) {
323 impl->updateForTimeMode(ui->treeView);
370 impl->updateForTimeMode(ui->treeView);
324 }
371 }
325 else if (isNotMultiSelection && ui->btnChart->isChecked()) {
372 else if (isNotMultiSelection && ui->btnChart->isChecked()) {
326 impl->updateForGraphMode(ui->treeView);
373 impl->updateForGraphMode(ui->treeView);
327 }
374 }
328
375
329 QVector<std::shared_ptr<DBEvent> > events;
376 QVector<std::shared_ptr<DBEvent> > events;
330 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > eventProducts;
377 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > eventProducts;
331 impl->getSelectedItems(ui->treeView, events, eventProducts);
378 impl->getSelectedItems(ui->treeView, events, eventProducts);
332 ui->btnRemove->setEnabled(!events.isEmpty() && eventProducts.isEmpty());
379 ui->btnRemove->setEnabled(!events.isEmpty() && eventProducts.isEmpty());
333 });
380 });
334
381
335 ui->treeView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
382 ui->treeView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
336 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Tags,
383 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Tags,
337 QHeaderView::Stretch);
384 QHeaderView::Stretch);
338 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Validation,
385 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Validation,
339 QHeaderView::Fixed);
386 QHeaderView::Fixed);
340 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Name,
387 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Name,
341 QHeaderView::Interactive);
388 QHeaderView::Interactive);
342 ui->treeView->header()->resizeSection((int)CatalogueEventsModel::Column::Validation,
389 ui->treeView->header()->resizeSection((int)CatalogueEventsModel::Column::Validation,
343 VALIDATION_COLUMN_SIZE);
390 VALIDATION_COLUMN_SIZE);
344 ui->treeView->header()->setSortIndicatorShown(true);
391 ui->treeView->header()->setSortIndicatorShown(true);
345
392
346 connect(impl->m_Model, &CatalogueEventsModel::modelSorted, [this]() {
393 connect(impl->m_Model, &CatalogueEventsModel::modelSorted, [this]() {
347 auto allEvents = impl->m_Model->events();
394 auto allEvents = impl->m_Model->events();
348 for (auto event : allEvents) {
395 for (auto event : allEvents) {
349 setEventChanges(event, sqpApp->catalogueController().eventHasChanges(event));
396 setEventChanges(event, sqpApp->catalogueController().eventHasChanges(event));
350 }
397 }
351 });
398 });
352
399
353 populateWithAllEvents();
400 populateWithAllEvents();
354 }
401 }
355
402
356 CatalogueEventsWidget::~CatalogueEventsWidget()
403 CatalogueEventsWidget::~CatalogueEventsWidget()
357 {
404 {
358 delete ui;
405 delete ui;
359 }
406 }
360
407
361 void CatalogueEventsWidget::setVisualizationWidget(VisualizationWidget *visualization)
408 void CatalogueEventsWidget::setVisualizationWidget(VisualizationWidget *visualization)
362 {
409 {
363 impl->m_VisualizationWidget = visualization;
410 impl->m_VisualizationWidget = visualization;
364 }
411 }
365
412
366 void CatalogueEventsWidget::addEvent(const std::shared_ptr<DBEvent> &event)
413 void CatalogueEventsWidget::addEvent(const std::shared_ptr<DBEvent> &event)
367 {
414 {
368 impl->addEvent(event, ui->treeView);
415 impl->addEvent(event, ui->treeView);
369 }
416 }
370
417
371 void CatalogueEventsWidget::setEventChanges(const std::shared_ptr<DBEvent> &event, bool hasChanges)
418 void CatalogueEventsWidget::setEventChanges(const std::shared_ptr<DBEvent> &event, bool hasChanges)
372 {
419 {
373 impl->m_Model->refreshEvent(event);
420 impl->m_Model->refreshEvent(event);
374
421
375 auto eventIndex = impl->m_Model->indexOf(event);
422 auto eventIndex = impl->m_Model->indexOf(event);
376 auto validationIndex
423 auto validationIndex
377 = eventIndex.sibling(eventIndex.row(), (int)CatalogueEventsModel::Column::Validation);
424 = eventIndex.sibling(eventIndex.row(), (int)CatalogueEventsModel::Column::Validation);
378
425
379 if (validationIndex.isValid()) {
426 if (validationIndex.isValid()) {
380 if (hasChanges) {
427 if (hasChanges) {
381 if (ui->treeView->indexWidget(validationIndex) == nullptr) {
428 if (ui->treeView->indexWidget(validationIndex) == nullptr) {
382 auto widget = CatalogueExplorerHelper::buildValidationWidget(
429 auto widget = CatalogueExplorerHelper::buildValidationWidget(
383 ui->treeView,
430 ui->treeView,
384 [this, event]() {
431 [this, event]() {
385 sqpApp->catalogueController().saveEvent(event);
432 sqpApp->catalogueController().saveEvent(event);
386 setEventChanges(event, false);
433 setEventChanges(event, false);
387 },
434 },
388 [this, event]() {
435 [this, event]() {
389 bool removed = false;
436 bool removed = false;
390 sqpApp->catalogueController().discardEvent(event, removed);
437 sqpApp->catalogueController().discardEvent(event, removed);
391 if (removed) {
438 if (removed) {
392 impl->m_Model->removeEvent(event);
439 impl->m_Model->removeEvent(event);
393 }
440 }
394 else {
441 else {
395 setEventChanges(event, false);
442 setEventChanges(event, false);
396 impl->m_Model->refreshEvent(event, true);
443 impl->m_Model->refreshEvent(event, true);
397 }
444 }
398 emitSelection();
445 emitSelection();
399 });
446 });
400 ui->treeView->setIndexWidget(validationIndex, widget);
447 ui->treeView->setIndexWidget(validationIndex, widget);
401 }
448 }
402 }
449 }
403 else {
450 else {
404 // Note: the widget is destroyed
451 // Note: the widget is destroyed
405 ui->treeView->setIndexWidget(validationIndex, nullptr);
452 ui->treeView->setIndexWidget(validationIndex, nullptr);
406 }
453 }
407 }
454 }
408 else {
455 else {
409 qCWarning(LOG_CatalogueEventsWidget())
456 qCWarning(LOG_CatalogueEventsWidget())
410 << "setEventChanges: the event is not displayed in the model.";
457 << "setEventChanges: the event is not displayed in the model.";
411 }
458 }
412 }
459 }
413
460
414 QVector<std::shared_ptr<DBCatalogue> > CatalogueEventsWidget::displayedCatalogues() const
461 QVector<std::shared_ptr<DBCatalogue> > CatalogueEventsWidget::displayedCatalogues() const
415 {
462 {
416 return impl->m_DisplayedCatalogues;
463 return impl->m_DisplayedCatalogues;
417 }
464 }
418
465
419 bool CatalogueEventsWidget::isAllEventsDisplayed() const
466 bool CatalogueEventsWidget::isAllEventsDisplayed() const
420 {
467 {
421 return impl->m_AllEventDisplayed;
468 return impl->m_AllEventDisplayed;
422 }
469 }
423
470
424 bool CatalogueEventsWidget::isEventDisplayed(const std::shared_ptr<DBEvent> &event) const
471 bool CatalogueEventsWidget::isEventDisplayed(const std::shared_ptr<DBEvent> &event) const
425 {
472 {
426 return impl->m_Model->indexOf(event).isValid();
473 return impl->m_Model->indexOf(event).isValid();
427 }
474 }
428
475
429 void CatalogueEventsWidget::populateWithCatalogues(
476 void CatalogueEventsWidget::populateWithCatalogues(
430 const QVector<std::shared_ptr<DBCatalogue> > &catalogues)
477 const QVector<std::shared_ptr<DBCatalogue> > &catalogues)
431 {
478 {
432 impl->m_DisplayedCatalogues = catalogues;
479 impl->m_DisplayedCatalogues = catalogues;
433 impl->m_AllEventDisplayed = false;
480 impl->m_AllEventDisplayed = false;
434
481
435 QSet<QUuid> eventIds;
482 QSet<QUuid> eventIds;
436 QVector<std::shared_ptr<DBEvent> > events;
483 QVector<std::shared_ptr<DBEvent> > events;
437
484
438 for (auto catalogue : catalogues) {
485 for (auto catalogue : catalogues) {
439 auto catalogueEvents = sqpApp->catalogueController().retrieveEventsFromCatalogue(catalogue);
486 auto catalogueEvents = sqpApp->catalogueController().retrieveEventsFromCatalogue(catalogue);
440 for (auto event : catalogueEvents) {
487 for (auto event : catalogueEvents) {
441 if (!eventIds.contains(event->getUniqId())) {
488 if (!eventIds.contains(event->getUniqId())) {
442 events << event;
489 events << event;
443 eventIds.insert(event->getUniqId());
490 eventIds.insert(event->getUniqId());
444 }
491 }
445 }
492 }
446 }
493 }
447
494
448 impl->setEvents(events, this);
495 impl->setEvents(events, this);
449 }
496 }
450
497
451 void CatalogueEventsWidget::populateWithAllEvents()
498 void CatalogueEventsWidget::populateWithAllEvents()
452 {
499 {
453 impl->m_DisplayedCatalogues.clear();
500 impl->m_DisplayedCatalogues.clear();
454 impl->m_AllEventDisplayed = true;
501 impl->m_AllEventDisplayed = true;
455
502
456 auto allEvents = sqpApp->catalogueController().retrieveAllEvents();
503 auto allEvents = sqpApp->catalogueController().retrieveAllEvents();
457
504
458 QVector<std::shared_ptr<DBEvent> > events;
505 QVector<std::shared_ptr<DBEvent> > events;
459 for (auto event : allEvents) {
506 for (auto event : allEvents) {
460 events << event;
507 events << event;
461 }
508 }
462
509
463 impl->setEvents(events, this);
510 impl->setEvents(events, this);
464 }
511 }
465
512
466 void CatalogueEventsWidget::clear()
513 void CatalogueEventsWidget::clear()
467 {
514 {
468 impl->m_DisplayedCatalogues.clear();
515 impl->m_DisplayedCatalogues.clear();
469 impl->m_AllEventDisplayed = false;
516 impl->m_AllEventDisplayed = false;
470 impl->setEvents({}, this);
517 impl->setEvents({}, this);
471 }
518 }
472
519
473 void CatalogueEventsWidget::refresh()
520 void CatalogueEventsWidget::refresh()
474 {
521 {
475 if (isAllEventsDisplayed()) {
522 if (isAllEventsDisplayed()) {
476 populateWithAllEvents();
523 populateWithAllEvents();
477 }
524 }
478 else if (!impl->m_DisplayedCatalogues.isEmpty()) {
525 else if (!impl->m_DisplayedCatalogues.isEmpty()) {
479 populateWithCatalogues(impl->m_DisplayedCatalogues);
526 populateWithCatalogues(impl->m_DisplayedCatalogues);
480 }
527 }
481 }
528 }
482
529
483 void CatalogueEventsWidget::emitSelection()
530 void CatalogueEventsWidget::emitSelection()
484 {
531 {
485 QVector<std::shared_ptr<DBEvent> > events;
532 QVector<std::shared_ptr<DBEvent> > events;
486 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > eventProducts;
533 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > eventProducts;
487 impl->getSelectedItems(ui->treeView, events, eventProducts);
534 impl->getSelectedItems(ui->treeView, events, eventProducts);
488
535
489 if (!events.isEmpty() && eventProducts.isEmpty()) {
536 if (!events.isEmpty() && eventProducts.isEmpty()) {
490 emit eventsSelected(events);
537 emit eventsSelected(events);
491 }
538 }
492 else if (events.isEmpty() && !eventProducts.isEmpty()) {
539 else if (events.isEmpty() && !eventProducts.isEmpty()) {
493 emit eventProductsSelected(eventProducts);
540 emit eventProductsSelected(eventProducts);
494 }
541 }
495 else {
542 else {
496 emit selectionCleared();
543 emit selectionCleared();
497 }
544 }
498 }
545 }
@@ -1,1021 +1,1031
1 #include "Visualization/VisualizationGraphWidget.h"
1 #include "Visualization/VisualizationGraphWidget.h"
2 #include "Visualization/IVisualizationWidgetVisitor.h"
2 #include "Visualization/IVisualizationWidgetVisitor.h"
3 #include "Visualization/VisualizationCursorItem.h"
3 #include "Visualization/VisualizationCursorItem.h"
4 #include "Visualization/VisualizationDefs.h"
4 #include "Visualization/VisualizationDefs.h"
5 #include "Visualization/VisualizationGraphHelper.h"
5 #include "Visualization/VisualizationGraphHelper.h"
6 #include "Visualization/VisualizationGraphRenderingDelegate.h"
6 #include "Visualization/VisualizationGraphRenderingDelegate.h"
7 #include "Visualization/VisualizationMultiZoneSelectionDialog.h"
7 #include "Visualization/VisualizationMultiZoneSelectionDialog.h"
8 #include "Visualization/VisualizationSelectionZoneItem.h"
8 #include "Visualization/VisualizationSelectionZoneItem.h"
9 #include "Visualization/VisualizationSelectionZoneManager.h"
9 #include "Visualization/VisualizationSelectionZoneManager.h"
10 #include "Visualization/VisualizationWidget.h"
10 #include "Visualization/VisualizationWidget.h"
11 #include "Visualization/VisualizationZoneWidget.h"
11 #include "Visualization/VisualizationZoneWidget.h"
12 #include "ui_VisualizationGraphWidget.h"
12 #include "ui_VisualizationGraphWidget.h"
13
13
14 #include <Actions/ActionsGuiController.h>
14 #include <Actions/ActionsGuiController.h>
15 #include <Common/MimeTypesDef.h>
15 #include <Common/MimeTypesDef.h>
16 #include <Data/ArrayData.h>
16 #include <Data/ArrayData.h>
17 #include <Data/IDataSeries.h>
17 #include <Data/IDataSeries.h>
18 #include <Data/SpectrogramSeries.h>
18 #include <Data/SpectrogramSeries.h>
19 #include <DragAndDrop/DragDropGuiController.h>
19 #include <DragAndDrop/DragDropGuiController.h>
20 #include <Settings/SqpSettingsDefs.h>
20 #include <Settings/SqpSettingsDefs.h>
21 #include <SqpApplication.h>
21 #include <SqpApplication.h>
22 #include <Time/TimeController.h>
22 #include <Time/TimeController.h>
23 #include <Variable/Variable.h>
23 #include <Variable/Variable.h>
24 #include <Variable/VariableController.h>
24 #include <Variable/VariableController.h>
25
25
26 #include <unordered_map>
26 #include <unordered_map>
27
27
28 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
28 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
29
29
30 namespace {
30 namespace {
31
31
32 /// Key pressed to enable drag&drop in all modes
32 /// Key pressed to enable drag&drop in all modes
33 const auto DRAG_DROP_MODIFIER = Qt::AltModifier;
33 const auto DRAG_DROP_MODIFIER = Qt::AltModifier;
34
34
35 /// Key pressed to enable zoom on horizontal axis
35 /// Key pressed to enable zoom on horizontal axis
36 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier;
36 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier;
37
37
38 /// Key pressed to enable zoom on vertical axis
38 /// Key pressed to enable zoom on vertical axis
39 const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier;
39 const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier;
40
40
41 /// Speed of a step of a wheel event for a pan, in percentage of the axis range
41 /// Speed of a step of a wheel event for a pan, in percentage of the axis range
42 const auto PAN_SPEED = 5;
42 const auto PAN_SPEED = 5;
43
43
44 /// Key pressed to enable a calibration pan
44 /// Key pressed to enable a calibration pan
45 const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier;
45 const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier;
46
46
47 /// Key pressed to enable multi selection of selection zones
47 /// Key pressed to enable multi selection of selection zones
48 const auto MULTI_ZONE_SELECTION_MODIFIER = Qt::ControlModifier;
48 const auto MULTI_ZONE_SELECTION_MODIFIER = Qt::ControlModifier;
49
49
50 /// Minimum size for the zoom box, in percentage of the axis range
50 /// Minimum size for the zoom box, in percentage of the axis range
51 const auto ZOOM_BOX_MIN_SIZE = 0.8;
51 const auto ZOOM_BOX_MIN_SIZE = 0.8;
52
52
53 /// Format of the dates appearing in the label of a cursor
53 /// Format of the dates appearing in the label of a cursor
54 const auto CURSOR_LABELS_DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd\nhh:mm:ss:zzz");
54 const auto CURSOR_LABELS_DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd\nhh:mm:ss:zzz");
55
55
56 } // namespace
56 } // namespace
57
57
58 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
58 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
59
59
60 explicit VisualizationGraphWidgetPrivate(const QString &name)
60 explicit VisualizationGraphWidgetPrivate(const QString &name)
61 : m_Name{name},
61 : m_Name{name},
62 m_Flags{GraphFlag::EnableAll},
62 m_Flags{GraphFlag::EnableAll},
63 m_IsCalibration{false},
63 m_IsCalibration{false},
64 m_RenderingDelegate{nullptr}
64 m_RenderingDelegate{nullptr}
65 {
65 {
66 }
66 }
67
67
68 void updateData(PlottablesMap &plottables, std::shared_ptr<Variable> variable,
68 void updateData(PlottablesMap &plottables, std::shared_ptr<Variable> variable,
69 const SqpRange &range)
69 const SqpRange &range)
70 {
70 {
71 VisualizationGraphHelper::updateData(plottables, variable, range);
71 VisualizationGraphHelper::updateData(plottables, variable, range);
72
72
73 // Prevents that data has changed to update rendering
73 // Prevents that data has changed to update rendering
74 m_RenderingDelegate->onPlotUpdated();
74 m_RenderingDelegate->onPlotUpdated();
75 }
75 }
76
76
77 QString m_Name;
77 QString m_Name;
78 // 1 variable -> n qcpplot
78 // 1 variable -> n qcpplot
79 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
79 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
80 GraphFlags m_Flags;
80 GraphFlags m_Flags;
81 bool m_IsCalibration;
81 bool m_IsCalibration;
82 /// Delegate used to attach rendering features to the plot
82 /// Delegate used to attach rendering features to the plot
83 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
83 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
84
84
85 QCPItemRect *m_DrawingZoomRect = nullptr;
85 QCPItemRect *m_DrawingZoomRect = nullptr;
86 QStack<QPair<QCPRange, QCPRange> > m_ZoomStack;
86 QStack<QPair<QCPRange, QCPRange> > m_ZoomStack;
87
87
88 std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr;
88 std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr;
89 std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr;
89 std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr;
90
90
91 VisualizationSelectionZoneItem *m_DrawingZone = nullptr;
91 VisualizationSelectionZoneItem *m_DrawingZone = nullptr;
92 VisualizationSelectionZoneItem *m_HoveredZone = nullptr;
92 VisualizationSelectionZoneItem *m_HoveredZone = nullptr;
93 QVector<VisualizationSelectionZoneItem *> m_SelectionZones;
93 QVector<VisualizationSelectionZoneItem *> m_SelectionZones;
94
94
95 bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even
95 bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even
96
96
97 void startDrawingRect(const QPoint &pos, QCustomPlot &plot)
97 void startDrawingRect(const QPoint &pos, QCustomPlot &plot)
98 {
98 {
99 removeDrawingRect(plot);
99 removeDrawingRect(plot);
100
100
101 auto axisPos = posToAxisPos(pos, plot);
101 auto axisPos = posToAxisPos(pos, plot);
102
102
103 m_DrawingZoomRect = new QCPItemRect{&plot};
103 m_DrawingZoomRect = new QCPItemRect{&plot};
104 QPen p;
104 QPen p;
105 p.setWidth(2);
105 p.setWidth(2);
106 m_DrawingZoomRect->setPen(p);
106 m_DrawingZoomRect->setPen(p);
107
107
108 m_DrawingZoomRect->topLeft->setCoords(axisPos);
108 m_DrawingZoomRect->topLeft->setCoords(axisPos);
109 m_DrawingZoomRect->bottomRight->setCoords(axisPos);
109 m_DrawingZoomRect->bottomRight->setCoords(axisPos);
110 }
110 }
111
111
112 void removeDrawingRect(QCustomPlot &plot)
112 void removeDrawingRect(QCustomPlot &plot)
113 {
113 {
114 if (m_DrawingZoomRect) {
114 if (m_DrawingZoomRect) {
115 plot.removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot
115 plot.removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot
116 m_DrawingZoomRect = nullptr;
116 m_DrawingZoomRect = nullptr;
117 plot.replot(QCustomPlot::rpQueuedReplot);
117 plot.replot(QCustomPlot::rpQueuedReplot);
118 }
118 }
119 }
119 }
120
120
121 void startDrawingZone(const QPoint &pos, VisualizationGraphWidget *graph)
121 void startDrawingZone(const QPoint &pos, VisualizationGraphWidget *graph)
122 {
122 {
123 endDrawingZone(graph);
123 endDrawingZone(graph);
124
124
125 auto axisPos = posToAxisPos(pos, graph->plot());
125 auto axisPos = posToAxisPos(pos, graph->plot());
126
126
127 m_DrawingZone = new VisualizationSelectionZoneItem{&graph->plot()};
127 m_DrawingZone = new VisualizationSelectionZoneItem{&graph->plot()};
128 m_DrawingZone->setRange(axisPos.x(), axisPos.x());
128 m_DrawingZone->setRange(axisPos.x(), axisPos.x());
129 m_DrawingZone->setEditionEnabled(false);
129 m_DrawingZone->setEditionEnabled(false);
130 }
130 }
131
131
132 void endDrawingZone(VisualizationGraphWidget *graph)
132 void endDrawingZone(VisualizationGraphWidget *graph)
133 {
133 {
134 if (m_DrawingZone) {
134 if (m_DrawingZone) {
135 auto drawingZoneRange = m_DrawingZone->range();
135 auto drawingZoneRange = m_DrawingZone->range();
136 if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0) {
136 if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0) {
137 m_DrawingZone->setEditionEnabled(true);
137 m_DrawingZone->setEditionEnabled(true);
138 addSelectionZone(m_DrawingZone);
138 addSelectionZone(m_DrawingZone);
139 }
139 }
140 else {
140 else {
141 graph->plot().removeItem(m_DrawingZone); // the item is deleted by QCustomPlot
141 graph->plot().removeItem(m_DrawingZone); // the item is deleted by QCustomPlot
142 }
142 }
143
143
144 graph->plot().replot(QCustomPlot::rpQueuedReplot);
144 graph->plot().replot(QCustomPlot::rpQueuedReplot);
145 m_DrawingZone = nullptr;
145 m_DrawingZone = nullptr;
146 }
146 }
147 }
147 }
148
148
149 void setSelectionZonesEditionEnabled(bool value)
149 void setSelectionZonesEditionEnabled(bool value)
150 {
150 {
151 for (auto s : m_SelectionZones) {
151 for (auto s : m_SelectionZones) {
152 s->setEditionEnabled(value);
152 s->setEditionEnabled(value);
153 }
153 }
154 }
154 }
155
155
156 void addSelectionZone(VisualizationSelectionZoneItem *zone) { m_SelectionZones << zone; }
156 void addSelectionZone(VisualizationSelectionZoneItem *zone) { m_SelectionZones << zone; }
157
157
158 VisualizationSelectionZoneItem *selectionZoneAt(const QPoint &pos,
158 VisualizationSelectionZoneItem *selectionZoneAt(const QPoint &pos,
159 const QCustomPlot &plot) const
159 const QCustomPlot &plot) const
160 {
160 {
161 VisualizationSelectionZoneItem *selectionZoneItemUnderCursor = nullptr;
161 VisualizationSelectionZoneItem *selectionZoneItemUnderCursor = nullptr;
162 auto minDistanceToZone = -1;
162 auto minDistanceToZone = -1;
163 for (auto zone : m_SelectionZones) {
163 for (auto zone : m_SelectionZones) {
164 auto distanceToZone = zone->selectTest(pos, false);
164 auto distanceToZone = zone->selectTest(pos, false);
165 if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone)
165 if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone)
166 && distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
166 && distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
167 selectionZoneItemUnderCursor = zone;
167 selectionZoneItemUnderCursor = zone;
168 }
168 }
169 }
169 }
170
170
171 return selectionZoneItemUnderCursor;
171 return selectionZoneItemUnderCursor;
172 }
172 }
173
173
174 QVector<VisualizationSelectionZoneItem *> selectionZonesAt(const QPoint &pos,
174 QVector<VisualizationSelectionZoneItem *> selectionZonesAt(const QPoint &pos,
175 const QCustomPlot &plot) const
175 const QCustomPlot &plot) const
176 {
176 {
177 QVector<VisualizationSelectionZoneItem *> zones;
177 QVector<VisualizationSelectionZoneItem *> zones;
178 for (auto zone : m_SelectionZones) {
178 for (auto zone : m_SelectionZones) {
179 auto distanceToZone = zone->selectTest(pos, false);
179 auto distanceToZone = zone->selectTest(pos, false);
180 if (distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
180 if (distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
181 zones << zone;
181 zones << zone;
182 }
182 }
183 }
183 }
184
184
185 return zones;
185 return zones;
186 }
186 }
187
187
188 void moveSelectionZoneOnTop(VisualizationSelectionZoneItem *zone, QCustomPlot &plot)
188 void moveSelectionZoneOnTop(VisualizationSelectionZoneItem *zone, QCustomPlot &plot)
189 {
189 {
190 if (!m_SelectionZones.isEmpty() && m_SelectionZones.last() != zone) {
190 if (!m_SelectionZones.isEmpty() && m_SelectionZones.last() != zone) {
191 zone->moveToTop();
191 zone->moveToTop();
192 m_SelectionZones.removeAll(zone);
192 m_SelectionZones.removeAll(zone);
193 m_SelectionZones.append(zone);
193 m_SelectionZones.append(zone);
194 }
194 }
195 }
195 }
196
196
197 QPointF posToAxisPos(const QPoint &pos, QCustomPlot &plot) const
197 QPointF posToAxisPos(const QPoint &pos, QCustomPlot &plot) const
198 {
198 {
199 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
199 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
200 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
200 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
201 return QPointF{axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y())};
201 return QPointF{axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y())};
202 }
202 }
203
203
204 bool pointIsInAxisRect(const QPointF &axisPoint, QCustomPlot &plot) const
204 bool pointIsInAxisRect(const QPointF &axisPoint, QCustomPlot &plot) const
205 {
205 {
206 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
206 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
207 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
207 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
208 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
208 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
209 }
209 }
210 };
210 };
211
211
212 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
212 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
213 : VisualizationDragWidget{parent},
213 : VisualizationDragWidget{parent},
214 ui{new Ui::VisualizationGraphWidget},
214 ui{new Ui::VisualizationGraphWidget},
215 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
215 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
216 {
216 {
217 ui->setupUi(this);
217 ui->setupUi(this);
218
218
219 // 'Close' options : widget is deleted when closed
219 // 'Close' options : widget is deleted when closed
220 setAttribute(Qt::WA_DeleteOnClose);
220 setAttribute(Qt::WA_DeleteOnClose);
221
221
222 // Set qcpplot properties :
222 // Set qcpplot properties :
223 // - zoom is enabled
223 // - zoom is enabled
224 // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation
224 // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation
225 ui->widget->setInteractions(QCP::iRangeZoom);
225 ui->widget->setInteractions(QCP::iRangeZoom);
226 ui->widget->axisRect()->setRangeDrag(Qt::Horizontal | Qt::Vertical);
226 ui->widget->axisRect()->setRangeDrag(Qt::Horizontal | Qt::Vertical);
227
227
228 // The delegate must be initialized after the ui as it uses the plot
228 // The delegate must be initialized after the ui as it uses the plot
229 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
229 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
230
230
231 // Init the cursors
231 // Init the cursors
232 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
232 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
233 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
233 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
234 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
234 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
235 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
235 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
236
236
237 connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress);
237 connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress);
238 connect(ui->widget, &QCustomPlot::mouseRelease, this,
238 connect(ui->widget, &QCustomPlot::mouseRelease, this,
239 &VisualizationGraphWidget::onMouseRelease);
239 &VisualizationGraphWidget::onMouseRelease);
240 connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove);
240 connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove);
241 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
241 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
242 connect(ui->widget, &QCustomPlot::mouseDoubleClick, this,
242 connect(ui->widget, &QCustomPlot::mouseDoubleClick, this,
243 &VisualizationGraphWidget::onMouseDoubleClick);
243 &VisualizationGraphWidget::onMouseDoubleClick);
244 connect(ui->widget->xAxis, static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(
244 connect(ui->widget->xAxis, static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(
245 &QCPAxis::rangeChanged),
245 &QCPAxis::rangeChanged),
246 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
246 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
247
247
248 // Activates menu when right clicking on the graph
248 // Activates menu when right clicking on the graph
249 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
249 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
250 connect(ui->widget, &QCustomPlot::customContextMenuRequested, this,
250 connect(ui->widget, &QCustomPlot::customContextMenuRequested, this,
251 &VisualizationGraphWidget::onGraphMenuRequested);
251 &VisualizationGraphWidget::onGraphMenuRequested);
252
252
253 connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
253 connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
254 &VariableController::onRequestDataLoading);
254 &VariableController::onRequestDataLoading);
255
255
256 connect(&sqpApp->variableController(), &VariableController::updateVarDisplaying, this,
256 connect(&sqpApp->variableController(), &VariableController::updateVarDisplaying, this,
257 &VisualizationGraphWidget::onUpdateVarDisplaying);
257 &VisualizationGraphWidget::onUpdateVarDisplaying);
258
258
259 #ifdef Q_OS_MAC
259 #ifdef Q_OS_MAC
260 plot().setPlottingHint(QCP::phFastPolylines, true);
260 plot().setPlottingHint(QCP::phFastPolylines, true);
261 #endif
261 #endif
262 }
262 }
263
263
264
264
265 VisualizationGraphWidget::~VisualizationGraphWidget()
265 VisualizationGraphWidget::~VisualizationGraphWidget()
266 {
266 {
267 delete ui;
267 delete ui;
268 }
268 }
269
269
270 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
270 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
271 {
271 {
272 auto parent = parentWidget();
272 auto parent = parentWidget();
273 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
273 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
274 parent = parent->parentWidget();
274 parent = parent->parentWidget();
275 }
275 }
276
276
277 return qobject_cast<VisualizationZoneWidget *>(parent);
277 return qobject_cast<VisualizationZoneWidget *>(parent);
278 }
278 }
279
279
280 VisualizationWidget *VisualizationGraphWidget::parentVisualizationWidget() const
280 VisualizationWidget *VisualizationGraphWidget::parentVisualizationWidget() const
281 {
281 {
282 auto parent = parentWidget();
282 auto parent = parentWidget();
283 while (parent != nullptr && !qobject_cast<VisualizationWidget *>(parent)) {
283 while (parent != nullptr && !qobject_cast<VisualizationWidget *>(parent)) {
284 parent = parent->parentWidget();
284 parent = parent->parentWidget();
285 }
285 }
286
286
287 return qobject_cast<VisualizationWidget *>(parent);
287 return qobject_cast<VisualizationWidget *>(parent);
288 }
288 }
289
289
290 void VisualizationGraphWidget::setFlags(GraphFlags flags)
290 void VisualizationGraphWidget::setFlags(GraphFlags flags)
291 {
291 {
292 impl->m_Flags = std::move(flags);
292 impl->m_Flags = std::move(flags);
293 }
293 }
294
294
295 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, SqpRange range)
295 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, SqpRange range)
296 {
296 {
297 /// Lambda used to set graph's units and range according to the variable passed in parameter
297 /// Lambda used to set graph's units and range according to the variable passed in parameter
298 auto loadRange = [this](std::shared_ptr<Variable> variable, const SqpRange &range) {
298 auto loadRange = [this](std::shared_ptr<Variable> variable, const SqpRange &range) {
299 impl->m_RenderingDelegate->setAxesUnits(*variable);
299 impl->m_RenderingDelegate->setAxesUnits(*variable);
300
300
301 this->setFlags(GraphFlag::DisableAll);
301 this->setFlags(GraphFlag::DisableAll);
302 setGraphRange(range);
302 setGraphRange(range);
303 this->setFlags(GraphFlag::EnableAll);
303 this->setFlags(GraphFlag::EnableAll);
304
304
305 emit requestDataLoading({variable}, range, false);
305 emit requestDataLoading({variable}, range, false);
306 };
306 };
307
307
308 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
308 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
309
309
310 // Calls update of graph's range and units when the data of the variable have been initialized.
310 // Calls update of graph's range and units when the data of the variable have been initialized.
311 // Note: we use QueuedConnection here as the update event must be called in the UI thread
311 // Note: we use QueuedConnection here as the update event must be called in the UI thread
312 connect(variable.get(), &Variable::dataInitialized, this,
312 connect(variable.get(), &Variable::dataInitialized, this,
313 [ varW = std::weak_ptr<Variable>{variable}, range, loadRange, this ]() {
313 [ varW = std::weak_ptr<Variable>{variable}, range, loadRange, this ]() {
314 if (auto var = varW.lock()) {
314 if (auto var = varW.lock()) {
315 // If the variable is the first added in the graph, we load its range
315 // If the variable is the first added in the graph, we load its range
316 auto firstVariableInGraph = range == INVALID_RANGE;
316 auto firstVariableInGraph = range == INVALID_RANGE;
317 auto loadedRange = firstVariableInGraph ? var->range() : range;
317 auto loadedRange = firstVariableInGraph ? var->range() : range;
318 loadRange(var, loadedRange);
318 loadRange(var, loadedRange);
319 setYRange(var);
319 setYRange(var);
320 }
320 }
321 },
321 },
322 Qt::QueuedConnection);
322 Qt::QueuedConnection);
323
323
324 // Uses delegate to create the qcpplot components according to the variable
324 // Uses delegate to create the qcpplot components according to the variable
325 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
325 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
326
326
327 // Sets graph properties
327 // Sets graph properties
328 impl->m_RenderingDelegate->setGraphProperties(*variable, createdPlottables);
328 impl->m_RenderingDelegate->setGraphProperties(*variable, createdPlottables);
329
329
330 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
330 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
331
331
332 // If the variable already has its data loaded, load its units and its range in the graph
332 // If the variable already has its data loaded, load its units and its range in the graph
333 if (variable->dataSeries() != nullptr) {
333 if (variable->dataSeries() != nullptr) {
334 loadRange(variable, range);
334 loadRange(variable, range);
335 }
335 }
336
336
337 emit variableAdded(variable);
337 emit variableAdded(variable);
338 }
338 }
339
339
340 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
340 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
341 {
341 {
342 // Each component associated to the variable :
342 // Each component associated to the variable :
343 // - is removed from qcpplot (which deletes it)
343 // - is removed from qcpplot (which deletes it)
344 // - is no longer referenced in the map
344 // - is no longer referenced in the map
345 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
345 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
346 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
346 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
347 emit variableAboutToBeRemoved(variable);
347 emit variableAboutToBeRemoved(variable);
348
348
349 auto &plottablesMap = variableIt->second;
349 auto &plottablesMap = variableIt->second;
350
350
351 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
351 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
352 plottableIt != plottableEnd;) {
352 plottableIt != plottableEnd;) {
353 ui->widget->removePlottable(plottableIt->second);
353 ui->widget->removePlottable(plottableIt->second);
354 plottableIt = plottablesMap.erase(plottableIt);
354 plottableIt = plottablesMap.erase(plottableIt);
355 }
355 }
356
356
357 impl->m_VariableToPlotMultiMap.erase(variableIt);
357 impl->m_VariableToPlotMultiMap.erase(variableIt);
358 }
358 }
359
359
360 // Updates graph
360 // Updates graph
361 ui->widget->replot();
361 ui->widget->replot();
362 }
362 }
363
363
364 QList<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
364 QList<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
365 {
365 {
366 auto variables = QList<std::shared_ptr<Variable> >{};
366 auto variables = QList<std::shared_ptr<Variable> >{};
367 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
367 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
368 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
368 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
369 variables << it->first;
369 variables << it->first;
370 }
370 }
371
371
372 return variables;
372 return variables;
373 }
373 }
374
374
375 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
375 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
376 {
376 {
377 if (!variable) {
377 if (!variable) {
378 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
378 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
379 return;
379 return;
380 }
380 }
381
381
382 VisualizationGraphHelper::setYAxisRange(variable, *ui->widget);
382 VisualizationGraphHelper::setYAxisRange(variable, *ui->widget);
383 }
383 }
384
384
385 SqpRange VisualizationGraphWidget::graphRange() const noexcept
385 SqpRange VisualizationGraphWidget::graphRange() const noexcept
386 {
386 {
387 auto graphRange = ui->widget->xAxis->range();
387 auto graphRange = ui->widget->xAxis->range();
388 return SqpRange{graphRange.lower, graphRange.upper};
388 return SqpRange{graphRange.lower, graphRange.upper};
389 }
389 }
390
390
391 void VisualizationGraphWidget::setGraphRange(const SqpRange &range)
391 void VisualizationGraphWidget::setGraphRange(const SqpRange &range, bool calibration)
392 {
392 {
393 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange START");
393 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange START");
394
395 if (calibration) {
396 impl->m_IsCalibration = true;
397 }
398
394 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
399 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
395 ui->widget->replot();
400 ui->widget->replot();
401
402 if (calibration) {
403 impl->m_IsCalibration = false;
404 }
405
396 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange END");
406 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange END");
397 }
407 }
398
408
399 QVector<SqpRange> VisualizationGraphWidget::selectionZoneRanges() const
409 QVector<SqpRange> VisualizationGraphWidget::selectionZoneRanges() const
400 {
410 {
401 QVector<SqpRange> ranges;
411 QVector<SqpRange> ranges;
402 for (auto zone : impl->m_SelectionZones) {
412 for (auto zone : impl->m_SelectionZones) {
403 ranges << zone->range();
413 ranges << zone->range();
404 }
414 }
405
415
406 return ranges;
416 return ranges;
407 }
417 }
408
418
409 void VisualizationGraphWidget::addSelectionZones(const QVector<SqpRange> &ranges)
419 void VisualizationGraphWidget::addSelectionZones(const QVector<SqpRange> &ranges)
410 {
420 {
411 for (const auto &range : ranges) {
421 for (const auto &range : ranges) {
412 // note: ownership is transfered to QCustomPlot
422 // note: ownership is transfered to QCustomPlot
413 auto zone = new VisualizationSelectionZoneItem(&plot());
423 auto zone = new VisualizationSelectionZoneItem(&plot());
414 zone->setRange(range.m_TStart, range.m_TEnd);
424 zone->setRange(range.m_TStart, range.m_TEnd);
415 impl->addSelectionZone(zone);
425 impl->addSelectionZone(zone);
416 }
426 }
417
427
418 plot().replot(QCustomPlot::rpQueuedReplot);
428 plot().replot(QCustomPlot::rpQueuedReplot);
419 }
429 }
420
430
421 void VisualizationGraphWidget::removeSelectionZone(VisualizationSelectionZoneItem *selectionZone)
431 void VisualizationGraphWidget::removeSelectionZone(VisualizationSelectionZoneItem *selectionZone)
422 {
432 {
423 parentVisualizationWidget()->selectionZoneManager().setSelected(selectionZone, false);
433 parentVisualizationWidget()->selectionZoneManager().setSelected(selectionZone, false);
424
434
425 if (impl->m_HoveredZone == selectionZone) {
435 if (impl->m_HoveredZone == selectionZone) {
426 impl->m_HoveredZone = nullptr;
436 impl->m_HoveredZone = nullptr;
427 setCursor(Qt::ArrowCursor);
437 setCursor(Qt::ArrowCursor);
428 }
438 }
429
439
430 impl->m_SelectionZones.removeAll(selectionZone);
440 impl->m_SelectionZones.removeAll(selectionZone);
431 plot().removeItem(selectionZone);
441 plot().removeItem(selectionZone);
432 plot().replot(QCustomPlot::rpQueuedReplot);
442 plot().replot(QCustomPlot::rpQueuedReplot);
433 }
443 }
434
444
435 void VisualizationGraphWidget::undoZoom()
445 void VisualizationGraphWidget::undoZoom()
436 {
446 {
437 auto zoom = impl->m_ZoomStack.pop();
447 auto zoom = impl->m_ZoomStack.pop();
438 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
448 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
439 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
449 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
440
450
441 axisX->setRange(zoom.first);
451 axisX->setRange(zoom.first);
442 axisY->setRange(zoom.second);
452 axisY->setRange(zoom.second);
443
453
444 plot().replot(QCustomPlot::rpQueuedReplot);
454 plot().replot(QCustomPlot::rpQueuedReplot);
445 }
455 }
446
456
447 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
457 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
448 {
458 {
449 if (visitor) {
459 if (visitor) {
450 visitor->visit(this);
460 visitor->visit(this);
451 }
461 }
452 else {
462 else {
453 qCCritical(LOG_VisualizationGraphWidget())
463 qCCritical(LOG_VisualizationGraphWidget())
454 << tr("Can't visit widget : the visitor is null");
464 << tr("Can't visit widget : the visitor is null");
455 }
465 }
456 }
466 }
457
467
458 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
468 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
459 {
469 {
460 auto isSpectrogram = [](const auto &variable) {
470 auto isSpectrogram = [](const auto &variable) {
461 return std::dynamic_pointer_cast<SpectrogramSeries>(variable.dataSeries()) != nullptr;
471 return std::dynamic_pointer_cast<SpectrogramSeries>(variable.dataSeries()) != nullptr;
462 };
472 };
463
473
464 // - A spectrogram series can't be dropped on graph with existing plottables
474 // - A spectrogram series can't be dropped on graph with existing plottables
465 // - No data series can be dropped on graph with existing spectrogram series
475 // - No data series can be dropped on graph with existing spectrogram series
466 return isSpectrogram(variable)
476 return isSpectrogram(variable)
467 ? impl->m_VariableToPlotMultiMap.empty()
477 ? impl->m_VariableToPlotMultiMap.empty()
468 : std::none_of(
478 : std::none_of(
469 impl->m_VariableToPlotMultiMap.cbegin(), impl->m_VariableToPlotMultiMap.cend(),
479 impl->m_VariableToPlotMultiMap.cbegin(), impl->m_VariableToPlotMultiMap.cend(),
470 [isSpectrogram](const auto &entry) { return isSpectrogram(*entry.first); });
480 [isSpectrogram](const auto &entry) { return isSpectrogram(*entry.first); });
471 }
481 }
472
482
473 bool VisualizationGraphWidget::contains(const Variable &variable) const
483 bool VisualizationGraphWidget::contains(const Variable &variable) const
474 {
484 {
475 // Finds the variable among the keys of the map
485 // Finds the variable among the keys of the map
476 auto variablePtr = &variable;
486 auto variablePtr = &variable;
477 auto findVariable
487 auto findVariable
478 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
488 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
479
489
480 auto end = impl->m_VariableToPlotMultiMap.cend();
490 auto end = impl->m_VariableToPlotMultiMap.cend();
481 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
491 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
482 return it != end;
492 return it != end;
483 }
493 }
484
494
485 QString VisualizationGraphWidget::name() const
495 QString VisualizationGraphWidget::name() const
486 {
496 {
487 return impl->m_Name;
497 return impl->m_Name;
488 }
498 }
489
499
490 QMimeData *VisualizationGraphWidget::mimeData(const QPoint &position) const
500 QMimeData *VisualizationGraphWidget::mimeData(const QPoint &position) const
491 {
501 {
492 auto mimeData = new QMimeData;
502 auto mimeData = new QMimeData;
493
503
494 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position, plot());
504 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position, plot());
495 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
505 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
496 && selectionZoneItemUnderCursor) {
506 && selectionZoneItemUnderCursor) {
497 mimeData->setData(MIME_TYPE_TIME_RANGE, TimeController::mimeDataForTimeRange(
507 mimeData->setData(MIME_TYPE_TIME_RANGE, TimeController::mimeDataForTimeRange(
498 selectionZoneItemUnderCursor->range()));
508 selectionZoneItemUnderCursor->range()));
499 mimeData->setData(MIME_TYPE_SELECTION_ZONE, TimeController::mimeDataForTimeRange(
509 mimeData->setData(MIME_TYPE_SELECTION_ZONE, TimeController::mimeDataForTimeRange(
500 selectionZoneItemUnderCursor->range()));
510 selectionZoneItemUnderCursor->range()));
501 }
511 }
502 else {
512 else {
503 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
513 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
504
514
505 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
515 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
506 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
516 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
507 }
517 }
508
518
509 return mimeData;
519 return mimeData;
510 }
520 }
511
521
512 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint &dragPosition)
522 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint &dragPosition)
513 {
523 {
514 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition, plot());
524 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition, plot());
515 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
525 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
516 && selectionZoneItemUnderCursor) {
526 && selectionZoneItemUnderCursor) {
517
527
518 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
528 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
519 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
529 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
520
530
521 auto zoneSize = QSizeF{qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
531 auto zoneSize = QSizeF{qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
522 qAbs(zoneBottomRight.y() - zoneTopLeft.y())}
532 qAbs(zoneBottomRight.y() - zoneTopLeft.y())}
523 .toSize();
533 .toSize();
524
534
525 auto pixmap = QPixmap(zoneSize);
535 auto pixmap = QPixmap(zoneSize);
526 render(&pixmap, QPoint(), QRegion{QRect{zoneTopLeft.toPoint(), zoneSize}});
536 render(&pixmap, QPoint(), QRegion{QRect{zoneTopLeft.toPoint(), zoneSize}});
527
537
528 return pixmap;
538 return pixmap;
529 }
539 }
530
540
531 return QPixmap();
541 return QPixmap();
532 }
542 }
533
543
534 bool VisualizationGraphWidget::isDragAllowed() const
544 bool VisualizationGraphWidget::isDragAllowed() const
535 {
545 {
536 return true;
546 return true;
537 }
547 }
538
548
539 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
549 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
540 {
550 {
541 if (highlighted) {
551 if (highlighted) {
542 plot().setBackground(QBrush(QColor("#BBD5EE")));
552 plot().setBackground(QBrush(QColor("#BBD5EE")));
543 }
553 }
544 else {
554 else {
545 plot().setBackground(QBrush(Qt::white));
555 plot().setBackground(QBrush(Qt::white));
546 }
556 }
547
557
548 plot().update();
558 plot().update();
549 }
559 }
550
560
551 void VisualizationGraphWidget::addVerticalCursor(double time)
561 void VisualizationGraphWidget::addVerticalCursor(double time)
552 {
562 {
553 impl->m_VerticalCursor->setPosition(time);
563 impl->m_VerticalCursor->setPosition(time);
554 impl->m_VerticalCursor->setVisible(true);
564 impl->m_VerticalCursor->setVisible(true);
555
565
556 auto text
566 auto text
557 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
567 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
558 impl->m_VerticalCursor->setLabelText(text);
568 impl->m_VerticalCursor->setLabelText(text);
559 }
569 }
560
570
561 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
571 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
562 {
572 {
563 impl->m_VerticalCursor->setAbsolutePosition(position);
573 impl->m_VerticalCursor->setAbsolutePosition(position);
564 impl->m_VerticalCursor->setVisible(true);
574 impl->m_VerticalCursor->setVisible(true);
565
575
566 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
576 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
567 auto text
577 auto text
568 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
578 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
569 impl->m_VerticalCursor->setLabelText(text);
579 impl->m_VerticalCursor->setLabelText(text);
570 }
580 }
571
581
572 void VisualizationGraphWidget::removeVerticalCursor()
582 void VisualizationGraphWidget::removeVerticalCursor()
573 {
583 {
574 impl->m_VerticalCursor->setVisible(false);
584 impl->m_VerticalCursor->setVisible(false);
575 plot().replot(QCustomPlot::rpQueuedReplot);
585 plot().replot(QCustomPlot::rpQueuedReplot);
576 }
586 }
577
587
578 void VisualizationGraphWidget::addHorizontalCursor(double value)
588 void VisualizationGraphWidget::addHorizontalCursor(double value)
579 {
589 {
580 impl->m_HorizontalCursor->setPosition(value);
590 impl->m_HorizontalCursor->setPosition(value);
581 impl->m_HorizontalCursor->setVisible(true);
591 impl->m_HorizontalCursor->setVisible(true);
582 impl->m_HorizontalCursor->setLabelText(QString::number(value));
592 impl->m_HorizontalCursor->setLabelText(QString::number(value));
583 }
593 }
584
594
585 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
595 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
586 {
596 {
587 impl->m_HorizontalCursor->setAbsolutePosition(position);
597 impl->m_HorizontalCursor->setAbsolutePosition(position);
588 impl->m_HorizontalCursor->setVisible(true);
598 impl->m_HorizontalCursor->setVisible(true);
589
599
590 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
600 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
591 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
601 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
592 }
602 }
593
603
594 void VisualizationGraphWidget::removeHorizontalCursor()
604 void VisualizationGraphWidget::removeHorizontalCursor()
595 {
605 {
596 impl->m_HorizontalCursor->setVisible(false);
606 impl->m_HorizontalCursor->setVisible(false);
597 plot().replot(QCustomPlot::rpQueuedReplot);
607 plot().replot(QCustomPlot::rpQueuedReplot);
598 }
608 }
599
609
600 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
610 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
601 {
611 {
602 Q_UNUSED(event);
612 Q_UNUSED(event);
603
613
604 // Prevents that all variables will be removed from graph when it will be closed
614 // Prevents that all variables will be removed from graph when it will be closed
605 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
615 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
606 emit variableAboutToBeRemoved(variableEntry.first);
616 emit variableAboutToBeRemoved(variableEntry.first);
607 }
617 }
608 }
618 }
609
619
610 void VisualizationGraphWidget::enterEvent(QEvent *event)
620 void VisualizationGraphWidget::enterEvent(QEvent *event)
611 {
621 {
612 Q_UNUSED(event);
622 Q_UNUSED(event);
613 impl->m_RenderingDelegate->showGraphOverlay(true);
623 impl->m_RenderingDelegate->showGraphOverlay(true);
614 }
624 }
615
625
616 void VisualizationGraphWidget::leaveEvent(QEvent *event)
626 void VisualizationGraphWidget::leaveEvent(QEvent *event)
617 {
627 {
618 Q_UNUSED(event);
628 Q_UNUSED(event);
619 impl->m_RenderingDelegate->showGraphOverlay(false);
629 impl->m_RenderingDelegate->showGraphOverlay(false);
620
630
621 if (auto parentZone = parentZoneWidget()) {
631 if (auto parentZone = parentZoneWidget()) {
622 parentZone->notifyMouseLeaveGraph(this);
632 parentZone->notifyMouseLeaveGraph(this);
623 }
633 }
624 else {
634 else {
625 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
635 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
626 }
636 }
627
637
628 if (impl->m_HoveredZone) {
638 if (impl->m_HoveredZone) {
629 impl->m_HoveredZone->setHovered(false);
639 impl->m_HoveredZone->setHovered(false);
630 impl->m_HoveredZone = nullptr;
640 impl->m_HoveredZone = nullptr;
631 }
641 }
632 }
642 }
633
643
634 QCustomPlot &VisualizationGraphWidget::plot() const noexcept
644 QCustomPlot &VisualizationGraphWidget::plot() const noexcept
635 {
645 {
636 return *ui->widget;
646 return *ui->widget;
637 }
647 }
638
648
639 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
649 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
640 {
650 {
641 QMenu graphMenu{};
651 QMenu graphMenu{};
642
652
643 // Iterates on variables (unique keys)
653 // Iterates on variables (unique keys)
644 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
654 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
645 end = impl->m_VariableToPlotMultiMap.cend();
655 end = impl->m_VariableToPlotMultiMap.cend();
646 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
656 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
647 // 'Remove variable' action
657 // 'Remove variable' action
648 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
658 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
649 [ this, var = it->first ]() { removeVariable(var); });
659 [ this, var = it->first ]() { removeVariable(var); });
650 }
660 }
651
661
652 if (!impl->m_ZoomStack.isEmpty()) {
662 if (!impl->m_ZoomStack.isEmpty()) {
653 if (!graphMenu.isEmpty()) {
663 if (!graphMenu.isEmpty()) {
654 graphMenu.addSeparator();
664 graphMenu.addSeparator();
655 }
665 }
656
666
657 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
667 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
658 }
668 }
659
669
660 // Selection Zone Actions
670 // Selection Zone Actions
661 auto selectionZoneItem = impl->selectionZoneAt(pos, plot());
671 auto selectionZoneItem = impl->selectionZoneAt(pos, plot());
662 if (selectionZoneItem) {
672 if (selectionZoneItem) {
663 auto selectedItems = parentVisualizationWidget()->selectionZoneManager().selectedItems();
673 auto selectedItems = parentVisualizationWidget()->selectionZoneManager().selectedItems();
664 selectedItems.removeAll(selectionZoneItem);
674 selectedItems.removeAll(selectionZoneItem);
665 selectedItems.prepend(selectionZoneItem); // Put the current selection zone first
675 selectedItems.prepend(selectionZoneItem); // Put the current selection zone first
666
676
667 auto zoneActions = sqpApp->actionsGuiController().selectionZoneActions();
677 auto zoneActions = sqpApp->actionsGuiController().selectionZoneActions();
668 if (!zoneActions.isEmpty() && !graphMenu.isEmpty()) {
678 if (!zoneActions.isEmpty() && !graphMenu.isEmpty()) {
669 graphMenu.addSeparator();
679 graphMenu.addSeparator();
670 }
680 }
671
681
672 QHash<QString, QMenu *> subMenus;
682 QHash<QString, QMenu *> subMenus;
673 QHash<QString, bool> subMenusEnabled;
683 QHash<QString, bool> subMenusEnabled;
674
684
675 for (auto zoneAction : zoneActions) {
685 for (auto zoneAction : zoneActions) {
676
686
677 auto isEnabled = zoneAction->isEnabled(selectedItems);
687 auto isEnabled = zoneAction->isEnabled(selectedItems);
678
688
679 auto menu = &graphMenu;
689 auto menu = &graphMenu;
680 for (auto subMenuName : zoneAction->subMenuList()) {
690 for (auto subMenuName : zoneAction->subMenuList()) {
681 if (!subMenus.contains(subMenuName)) {
691 if (!subMenus.contains(subMenuName)) {
682 menu = menu->addMenu(subMenuName);
692 menu = menu->addMenu(subMenuName);
683 subMenus[subMenuName] = menu;
693 subMenus[subMenuName] = menu;
684 subMenusEnabled[subMenuName] = isEnabled;
694 subMenusEnabled[subMenuName] = isEnabled;
685 }
695 }
686 else {
696 else {
687 menu = subMenus.value(subMenuName);
697 menu = subMenus.value(subMenuName);
688 if (isEnabled) {
698 if (isEnabled) {
689 // The sub menu is enabled if at least one of its actions is enabled
699 // The sub menu is enabled if at least one of its actions is enabled
690 subMenusEnabled[subMenuName] = true;
700 subMenusEnabled[subMenuName] = true;
691 }
701 }
692 }
702 }
693 }
703 }
694
704
695 auto action = menu->addAction(zoneAction->name());
705 auto action = menu->addAction(zoneAction->name());
696 action->setEnabled(isEnabled);
706 action->setEnabled(isEnabled);
697 action->setShortcut(zoneAction->displayedShortcut());
707 action->setShortcut(zoneAction->displayedShortcut());
698 QObject::connect(action, &QAction::triggered,
708 QObject::connect(action, &QAction::triggered,
699 [zoneAction, selectedItems]() { zoneAction->execute(selectedItems); });
709 [zoneAction, selectedItems]() { zoneAction->execute(selectedItems); });
700 }
710 }
701
711
702 for (auto it = subMenus.cbegin(); it != subMenus.cend(); ++it) {
712 for (auto it = subMenus.cbegin(); it != subMenus.cend(); ++it) {
703 it.value()->setEnabled(subMenusEnabled[it.key()]);
713 it.value()->setEnabled(subMenusEnabled[it.key()]);
704 }
714 }
705 }
715 }
706
716
707 if (!graphMenu.isEmpty()) {
717 if (!graphMenu.isEmpty()) {
708 graphMenu.exec(QCursor::pos());
718 graphMenu.exec(QCursor::pos());
709 }
719 }
710 }
720 }
711
721
712 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
722 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
713 {
723 {
714 qCDebug(LOG_VisualizationGraphWidget()) << tr("TORM: VisualizationGraphWidget::onRangeChanged")
724 qCDebug(LOG_VisualizationGraphWidget()) << tr("TORM: VisualizationGraphWidget::onRangeChanged")
715 << QThread::currentThread()->objectName() << "DoAcqui"
725 << QThread::currentThread()->objectName() << "DoAcqui"
716 << impl->m_Flags.testFlag(GraphFlag::EnableAcquisition);
726 << impl->m_Flags.testFlag(GraphFlag::EnableAcquisition);
717
727
718 auto graphRange = SqpRange{t1.lower, t1.upper};
728 auto graphRange = SqpRange{t1.lower, t1.upper};
719 auto oldGraphRange = SqpRange{t2.lower, t2.upper};
729 auto oldGraphRange = SqpRange{t2.lower, t2.upper};
720
730
721 if (impl->m_Flags.testFlag(GraphFlag::EnableAcquisition)) {
731 if (impl->m_Flags.testFlag(GraphFlag::EnableAcquisition)) {
722 QVector<std::shared_ptr<Variable> > variableUnderGraphVector;
732 QVector<std::shared_ptr<Variable> > variableUnderGraphVector;
723
733
724 for (auto it = impl->m_VariableToPlotMultiMap.begin(),
734 for (auto it = impl->m_VariableToPlotMultiMap.begin(),
725 end = impl->m_VariableToPlotMultiMap.end();
735 end = impl->m_VariableToPlotMultiMap.end();
726 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
736 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
727 variableUnderGraphVector.push_back(it->first);
737 variableUnderGraphVector.push_back(it->first);
728 }
738 }
729 emit requestDataLoading(std::move(variableUnderGraphVector), graphRange,
739 emit requestDataLoading(std::move(variableUnderGraphVector), graphRange,
730 !impl->m_IsCalibration);
740 !impl->m_IsCalibration);
731 }
741 }
732
742
733 if (impl->m_Flags.testFlag(GraphFlag::EnableSynchronization) && !impl->m_IsCalibration) {
743 if (impl->m_Flags.testFlag(GraphFlag::EnableSynchronization) && !impl->m_IsCalibration) {
734 qCDebug(LOG_VisualizationGraphWidget())
744 qCDebug(LOG_VisualizationGraphWidget())
735 << tr("TORM: VisualizationGraphWidget::Synchronize notify !!")
745 << tr("TORM: VisualizationGraphWidget::Synchronize notify !!")
736 << QThread::currentThread()->objectName() << graphRange << oldGraphRange;
746 << QThread::currentThread()->objectName() << graphRange << oldGraphRange;
737 emit synchronize(graphRange, oldGraphRange);
747 emit synchronize(graphRange, oldGraphRange);
738 }
748 }
739
749
740 auto pos = mapFromGlobal(QCursor::pos());
750 auto pos = mapFromGlobal(QCursor::pos());
741 auto axisPos = impl->posToAxisPos(pos, plot());
751 auto axisPos = impl->posToAxisPos(pos, plot());
742 if (auto parentZone = parentZoneWidget()) {
752 if (auto parentZone = parentZoneWidget()) {
743 if (impl->pointIsInAxisRect(axisPos, plot())) {
753 if (impl->pointIsInAxisRect(axisPos, plot())) {
744 parentZone->notifyMouseMoveInGraph(pos, axisPos, this);
754 parentZone->notifyMouseMoveInGraph(pos, axisPos, this);
745 }
755 }
746 else {
756 else {
747 parentZone->notifyMouseLeaveGraph(this);
757 parentZone->notifyMouseLeaveGraph(this);
748 }
758 }
749 }
759 }
750 else {
760 else {
751 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
761 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
752 }
762 }
753
763
754 // Quits calibration
764 // Quits calibration
755 impl->m_IsCalibration = false;
765 impl->m_IsCalibration = false;
756 }
766 }
757
767
758 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept
768 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept
759 {
769 {
760 impl->m_RenderingDelegate->onMouseDoubleClick(event);
770 impl->m_RenderingDelegate->onMouseDoubleClick(event);
761 }
771 }
762
772
763 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
773 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
764 {
774 {
765 // Handles plot rendering when mouse is moving
775 // Handles plot rendering when mouse is moving
766 impl->m_RenderingDelegate->onMouseMove(event);
776 impl->m_RenderingDelegate->onMouseMove(event);
767
777
768 auto axisPos = impl->posToAxisPos(event->pos(), plot());
778 auto axisPos = impl->posToAxisPos(event->pos(), plot());
769
779
770 // Zoom box and zone drawing
780 // Zoom box and zone drawing
771 if (impl->m_DrawingZoomRect) {
781 if (impl->m_DrawingZoomRect) {
772 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
782 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
773 }
783 }
774 else if (impl->m_DrawingZone) {
784 else if (impl->m_DrawingZone) {
775 impl->m_DrawingZone->setEnd(axisPos.x());
785 impl->m_DrawingZone->setEnd(axisPos.x());
776 }
786 }
777
787
778 // Cursor
788 // Cursor
779 if (auto parentZone = parentZoneWidget()) {
789 if (auto parentZone = parentZoneWidget()) {
780 if (impl->pointIsInAxisRect(axisPos, plot())) {
790 if (impl->pointIsInAxisRect(axisPos, plot())) {
781 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
791 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
782 }
792 }
783 else {
793 else {
784 parentZone->notifyMouseLeaveGraph(this);
794 parentZone->notifyMouseLeaveGraph(this);
785 }
795 }
786 }
796 }
787 else {
797 else {
788 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
798 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
789 }
799 }
790
800
791 // Search for the selection zone under the mouse
801 // Search for the selection zone under the mouse
792 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
802 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
793 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
803 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
794 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) {
804 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) {
795
805
796 // Sets the appropriate cursor shape
806 // Sets the appropriate cursor shape
797 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
807 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
798 setCursor(cursorShape);
808 setCursor(cursorShape);
799
809
800 // Manages the hovered zone
810 // Manages the hovered zone
801 if (selectionZoneItemUnderCursor != impl->m_HoveredZone) {
811 if (selectionZoneItemUnderCursor != impl->m_HoveredZone) {
802 if (impl->m_HoveredZone) {
812 if (impl->m_HoveredZone) {
803 impl->m_HoveredZone->setHovered(false);
813 impl->m_HoveredZone->setHovered(false);
804 }
814 }
805 selectionZoneItemUnderCursor->setHovered(true);
815 selectionZoneItemUnderCursor->setHovered(true);
806 impl->m_HoveredZone = selectionZoneItemUnderCursor;
816 impl->m_HoveredZone = selectionZoneItemUnderCursor;
807 plot().replot(QCustomPlot::rpQueuedReplot);
817 plot().replot(QCustomPlot::rpQueuedReplot);
808 }
818 }
809 }
819 }
810 else {
820 else {
811 // There is no zone under the mouse or the interaction mode is not "selection zones"
821 // There is no zone under the mouse or the interaction mode is not "selection zones"
812 if (impl->m_HoveredZone) {
822 if (impl->m_HoveredZone) {
813 impl->m_HoveredZone->setHovered(false);
823 impl->m_HoveredZone->setHovered(false);
814 impl->m_HoveredZone = nullptr;
824 impl->m_HoveredZone = nullptr;
815 }
825 }
816
826
817 setCursor(Qt::ArrowCursor);
827 setCursor(Qt::ArrowCursor);
818 }
828 }
819
829
820 impl->m_HasMovedMouse = true;
830 impl->m_HasMovedMouse = true;
821 VisualizationDragWidget::mouseMoveEvent(event);
831 VisualizationDragWidget::mouseMoveEvent(event);
822 }
832 }
823
833
824 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
834 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
825 {
835 {
826 auto value = event->angleDelta().x() + event->angleDelta().y();
836 auto value = event->angleDelta().x() + event->angleDelta().y();
827 if (value != 0) {
837 if (value != 0) {
828
838
829 auto direction = value > 0 ? 1.0 : -1.0;
839 auto direction = value > 0 ? 1.0 : -1.0;
830 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
840 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
831 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
841 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
832 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
842 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
833
843
834 auto zoomOrientations = QFlags<Qt::Orientation>{};
844 auto zoomOrientations = QFlags<Qt::Orientation>{};
835 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
845 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
836 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
846 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
837
847
838 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
848 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
839
849
840 if (!isZoomX && !isZoomY) {
850 if (!isZoomX && !isZoomY) {
841 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
851 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
842 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
852 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
843
853
844 axis->setRange(axis->range() + diff);
854 axis->setRange(axis->range() + diff);
845
855
846 if (plot().noAntialiasingOnDrag()) {
856 if (plot().noAntialiasingOnDrag()) {
847 plot().setNotAntialiasedElements(QCP::aeAll);
857 plot().setNotAntialiasedElements(QCP::aeAll);
848 }
858 }
849
859
850 plot().replot(QCustomPlot::rpQueuedReplot);
860 plot().replot(QCustomPlot::rpQueuedReplot);
851 }
861 }
852 }
862 }
853 }
863 }
854
864
855 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
865 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
856 {
866 {
857 auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
867 auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
858 auto isSelectionZoneMode
868 auto isSelectionZoneMode
859 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
869 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
860 auto isLeftClick = event->buttons().testFlag(Qt::LeftButton);
870 auto isLeftClick = event->buttons().testFlag(Qt::LeftButton);
861
871
862 if (!isDragDropClick && isLeftClick) {
872 if (!isDragDropClick && isLeftClick) {
863 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) {
873 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) {
864 // Starts a zoom box
874 // Starts a zoom box
865 impl->startDrawingRect(event->pos(), plot());
875 impl->startDrawingRect(event->pos(), plot());
866 }
876 }
867 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr) {
877 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr) {
868 // Starts a new selection zone
878 // Starts a new selection zone
869 auto zoneAtPos = impl->selectionZoneAt(event->pos(), plot());
879 auto zoneAtPos = impl->selectionZoneAt(event->pos(), plot());
870 if (!zoneAtPos) {
880 if (!zoneAtPos) {
871 impl->startDrawingZone(event->pos(), this);
881 impl->startDrawingZone(event->pos(), this);
872 }
882 }
873 }
883 }
874 }
884 }
875
885
876 // Allows mouse panning only in default mode
886 // Allows mouse panning only in default mode
877 plot().setInteraction(QCP::iRangeDrag, sqpApp->plotsInteractionMode()
887 plot().setInteraction(QCP::iRangeDrag, sqpApp->plotsInteractionMode()
878 == SqpApplication::PlotsInteractionMode::None
888 == SqpApplication::PlotsInteractionMode::None
879 && !isDragDropClick);
889 && !isDragDropClick);
880
890
881 // Allows zone edition only in selection zone mode without drag&drop
891 // Allows zone edition only in selection zone mode without drag&drop
882 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
892 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
883
893
884 // Selection / Deselection
894 // Selection / Deselection
885 if (isSelectionZoneMode) {
895 if (isSelectionZoneMode) {
886 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
896 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
887 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
897 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
888
898
889
899
890 if (selectionZoneItemUnderCursor && !selectionZoneItemUnderCursor->selected()
900 if (selectionZoneItemUnderCursor && !selectionZoneItemUnderCursor->selected()
891 && !isMultiSelectionClick) {
901 && !isMultiSelectionClick) {
892 parentVisualizationWidget()->selectionZoneManager().select(
902 parentVisualizationWidget()->selectionZoneManager().select(
893 {selectionZoneItemUnderCursor});
903 {selectionZoneItemUnderCursor});
894 }
904 }
895 else if (!selectionZoneItemUnderCursor && !isMultiSelectionClick && isLeftClick) {
905 else if (!selectionZoneItemUnderCursor && !isMultiSelectionClick && isLeftClick) {
896 parentVisualizationWidget()->selectionZoneManager().clearSelection();
906 parentVisualizationWidget()->selectionZoneManager().clearSelection();
897 }
907 }
898 else {
908 else {
899 // No selection change
909 // No selection change
900 }
910 }
901
911
902 if (selectionZoneItemUnderCursor && isLeftClick) {
912 if (selectionZoneItemUnderCursor && isLeftClick) {
903 selectionZoneItemUnderCursor->setAssociatedEditedZones(
913 selectionZoneItemUnderCursor->setAssociatedEditedZones(
904 parentVisualizationWidget()->selectionZoneManager().selectedItems());
914 parentVisualizationWidget()->selectionZoneManager().selectedItems());
905 }
915 }
906 }
916 }
907
917
908
918
909 impl->m_HasMovedMouse = false;
919 impl->m_HasMovedMouse = false;
910 VisualizationDragWidget::mousePressEvent(event);
920 VisualizationDragWidget::mousePressEvent(event);
911 }
921 }
912
922
913 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
923 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
914 {
924 {
915 if (impl->m_DrawingZoomRect) {
925 if (impl->m_DrawingZoomRect) {
916
926
917 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
927 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
918 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
928 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
919
929
920 auto newAxisXRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().x(),
930 auto newAxisXRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().x(),
921 impl->m_DrawingZoomRect->bottomRight->coords().x()};
931 impl->m_DrawingZoomRect->bottomRight->coords().x()};
922
932
923 auto newAxisYRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().y(),
933 auto newAxisYRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().y(),
924 impl->m_DrawingZoomRect->bottomRight->coords().y()};
934 impl->m_DrawingZoomRect->bottomRight->coords().y()};
925
935
926 impl->removeDrawingRect(plot());
936 impl->removeDrawingRect(plot());
927
937
928 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
938 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
929 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
939 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
930 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
940 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
931 axisX->setRange(newAxisXRange);
941 axisX->setRange(newAxisXRange);
932 axisY->setRange(newAxisYRange);
942 axisY->setRange(newAxisYRange);
933
943
934 plot().replot(QCustomPlot::rpQueuedReplot);
944 plot().replot(QCustomPlot::rpQueuedReplot);
935 }
945 }
936 }
946 }
937
947
938 impl->endDrawingZone(this);
948 impl->endDrawingZone(this);
939
949
940 // Selection / Deselection
950 // Selection / Deselection
941 auto isSelectionZoneMode
951 auto isSelectionZoneMode
942 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
952 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
943 if (isSelectionZoneMode) {
953 if (isSelectionZoneMode) {
944 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
954 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
945 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
955 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
946 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton
956 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton
947 && !impl->m_HasMovedMouse) {
957 && !impl->m_HasMovedMouse) {
948
958
949 auto zonesUnderCursor = impl->selectionZonesAt(event->pos(), plot());
959 auto zonesUnderCursor = impl->selectionZonesAt(event->pos(), plot());
950 if (zonesUnderCursor.count() > 1) {
960 if (zonesUnderCursor.count() > 1) {
951 // There are multiple zones under the mouse.
961 // There are multiple zones under the mouse.
952 // Performs the selection with a selection dialog.
962 // Performs the selection with a selection dialog.
953 VisualizationMultiZoneSelectionDialog dialog{this};
963 VisualizationMultiZoneSelectionDialog dialog{this};
954 dialog.setZones(zonesUnderCursor);
964 dialog.setZones(zonesUnderCursor);
955 dialog.move(mapToGlobal(event->pos() - QPoint(dialog.width() / 2, 20)));
965 dialog.move(mapToGlobal(event->pos() - QPoint(dialog.width() / 2, 20)));
956 dialog.activateWindow();
966 dialog.activateWindow();
957 dialog.raise();
967 dialog.raise();
958 if (dialog.exec() == QDialog::Accepted) {
968 if (dialog.exec() == QDialog::Accepted) {
959 auto selection = dialog.selectedZones();
969 auto selection = dialog.selectedZones();
960
970
961 if (!isMultiSelectionClick) {
971 if (!isMultiSelectionClick) {
962 parentVisualizationWidget()->selectionZoneManager().clearSelection();
972 parentVisualizationWidget()->selectionZoneManager().clearSelection();
963 }
973 }
964
974
965 for (auto it = selection.cbegin(); it != selection.cend(); ++it) {
975 for (auto it = selection.cbegin(); it != selection.cend(); ++it) {
966 auto zone = it.key();
976 auto zone = it.key();
967 auto isSelected = it.value();
977 auto isSelected = it.value();
968 parentVisualizationWidget()->selectionZoneManager().setSelected(zone,
978 parentVisualizationWidget()->selectionZoneManager().setSelected(zone,
969 isSelected);
979 isSelected);
970
980
971 if (isSelected) {
981 if (isSelected) {
972 // Puts the zone on top of the stack so it can be moved or resized
982 // Puts the zone on top of the stack so it can be moved or resized
973 impl->moveSelectionZoneOnTop(zone, plot());
983 impl->moveSelectionZoneOnTop(zone, plot());
974 }
984 }
975 }
985 }
976 }
986 }
977 }
987 }
978 else {
988 else {
979 if (!isMultiSelectionClick) {
989 if (!isMultiSelectionClick) {
980 parentVisualizationWidget()->selectionZoneManager().select(
990 parentVisualizationWidget()->selectionZoneManager().select(
981 {selectionZoneItemUnderCursor});
991 {selectionZoneItemUnderCursor});
982 impl->moveSelectionZoneOnTop(selectionZoneItemUnderCursor, plot());
992 impl->moveSelectionZoneOnTop(selectionZoneItemUnderCursor, plot());
983 }
993 }
984 else {
994 else {
985 parentVisualizationWidget()->selectionZoneManager().setSelected(
995 parentVisualizationWidget()->selectionZoneManager().setSelected(
986 selectionZoneItemUnderCursor, !selectionZoneItemUnderCursor->selected()
996 selectionZoneItemUnderCursor, !selectionZoneItemUnderCursor->selected()
987 || event->button() == Qt::RightButton);
997 || event->button() == Qt::RightButton);
988 }
998 }
989 }
999 }
990 }
1000 }
991 else {
1001 else {
992 // No selection change
1002 // No selection change
993 }
1003 }
994 }
1004 }
995 }
1005 }
996
1006
997 void VisualizationGraphWidget::onDataCacheVariableUpdated()
1007 void VisualizationGraphWidget::onDataCacheVariableUpdated()
998 {
1008 {
999 auto graphRange = ui->widget->xAxis->range();
1009 auto graphRange = ui->widget->xAxis->range();
1000 auto dateTime = SqpRange{graphRange.lower, graphRange.upper};
1010 auto dateTime = SqpRange{graphRange.lower, graphRange.upper};
1001
1011
1002 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
1012 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
1003 auto variable = variableEntry.first;
1013 auto variable = variableEntry.first;
1004 qCDebug(LOG_VisualizationGraphWidget())
1014 qCDebug(LOG_VisualizationGraphWidget())
1005 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
1015 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
1006 qCDebug(LOG_VisualizationGraphWidget())
1016 qCDebug(LOG_VisualizationGraphWidget())
1007 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
1017 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
1008 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
1018 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
1009 impl->updateData(variableEntry.second, variable, variable->range());
1019 impl->updateData(variableEntry.second, variable, variable->range());
1010 }
1020 }
1011 }
1021 }
1012 }
1022 }
1013
1023
1014 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
1024 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
1015 const SqpRange &range)
1025 const SqpRange &range)
1016 {
1026 {
1017 auto it = impl->m_VariableToPlotMultiMap.find(variable);
1027 auto it = impl->m_VariableToPlotMultiMap.find(variable);
1018 if (it != impl->m_VariableToPlotMultiMap.end()) {
1028 if (it != impl->m_VariableToPlotMultiMap.end()) {
1019 impl->updateData(it->second, variable, range);
1029 impl->updateData(it->second, variable, range);
1020 }
1030 }
1021 }
1031 }
General Comments 0
You need to be logged in to leave comments. Login now