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