##// END OF EJS Templates
Fix the close button on the graphs
trabillard -
r1053:1dd1afe40e34
parent child
Show More
@@ -1,353 +1,353
1 1 /*------------------------------------------------------------------------------
2 2 -- This file is a part of the SciQLop Software
3 3 -- Copyright (C) 2017, Plasma Physics Laboratory - CNRS
4 4 --
5 5 -- This program is free software; you can redistribute it and/or modify
6 6 -- it under the terms of the GNU General Public License as published by
7 7 -- the Free Software Foundation; either version 2 of the License, or
8 8 -- (at your option) any later version.
9 9 --
10 10 -- This program is distributed in the hope that it will be useful,
11 11 -- but WITHOUT ANY WARRANTY; without even the implied warranty of
12 12 -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 13 -- GNU General Public License for more details.
14 14 --
15 15 -- You should have received a copy of the GNU General Public License
16 16 -- along with this program; if not, write to the Free Software
17 17 -- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 18 -------------------------------------------------------------------------------*/
19 19 /*-- Author : Alexis Jeandet
20 20 -- Mail : alexis.jeandet@member.fsf.org
21 21 ----------------------------------------------------------------------------*/
22 22 #include "MainWindow.h"
23 23 #include "ui_MainWindow.h"
24 24
25 25 #include <DataSource/DataSourceController.h>
26 26 #include <DataSource/DataSourceWidget.h>
27 27 #include <Settings/SqpSettingsDialog.h>
28 28 #include <Settings/SqpSettingsGeneralWidget.h>
29 29 #include <SidePane/SqpSidePane.h>
30 30 #include <SqpApplication.h>
31 31 #include <Time/TimeController.h>
32 32 #include <TimeWidget/TimeWidget.h>
33 33 #include <Variable/Variable.h>
34 34 #include <Variable/VariableController.h>
35 35 #include <Visualization/VisualizationController.h>
36 36
37 37 #include <QAction>
38 38 #include <QDate>
39 39 #include <QDir>
40 40 #include <QFileDialog>
41 41 #include <QToolBar>
42 42 #include <QToolButton>
43 43 #include <memory.h>
44 44
45 45 #include "iostream"
46 46
47 47 Q_LOGGING_CATEGORY(LOG_MainWindow, "MainWindow")
48 48
49 49 namespace {
50 50 const auto LEFTMAININSPECTORWIDGETSPLITTERINDEX = 0;
51 51 const auto LEFTINSPECTORSIDEPANESPLITTERINDEX = 1;
52 52 const auto VIEWPLITTERINDEX = 2;
53 53 const auto RIGHTINSPECTORSIDEPANESPLITTERINDEX = 3;
54 54 const auto RIGHTMAININSPECTORWIDGETSPLITTERINDEX = 4;
55 55 }
56 56
57 57 class MainWindow::MainWindowPrivate {
58 58 public:
59 59 explicit MainWindowPrivate(MainWindow *mainWindow)
60 60 : m_LastOpenLeftInspectorSize{},
61 61 m_LastOpenRightInspectorSize{},
62 62 m_GeneralSettingsWidget{new SqpSettingsGeneralWidget{mainWindow}},
63 63 m_SettingsDialog{new SqpSettingsDialog{mainWindow}}
64 64 {
65 65 }
66 66
67 67 QSize m_LastOpenLeftInspectorSize;
68 68 QSize m_LastOpenRightInspectorSize;
69 69 /// General settings widget. MainWindow has the ownership
70 70 SqpSettingsGeneralWidget *m_GeneralSettingsWidget;
71 71 /// Settings dialog. MainWindow has the ownership
72 72 SqpSettingsDialog *m_SettingsDialog;
73 73 };
74 74
75 75 MainWindow::MainWindow(QWidget *parent)
76 76 : QMainWindow{parent},
77 77 m_Ui{new Ui::MainWindow},
78 78 impl{spimpl::make_unique_impl<MainWindowPrivate>(this)}
79 79 {
80 80 m_Ui->setupUi(this);
81 81
82 82 m_Ui->splitter->setCollapsible(LEFTINSPECTORSIDEPANESPLITTERINDEX, false);
83 83 m_Ui->splitter->setCollapsible(RIGHTINSPECTORSIDEPANESPLITTERINDEX, false);
84 84
85 85
86 86 auto leftSidePane = m_Ui->leftInspectorSidePane->sidePane();
87 87 auto openLeftInspectorAction = new QAction{QIcon{
88 88 ":/icones/previous.png",
89 89 },
90 90 tr("Show/hide the left inspector"), this};
91 91
92 92
93 93 auto spacerLeftTop = new QWidget{};
94 94 spacerLeftTop->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
95 95
96 96 auto spacerLeftBottom = new QWidget{};
97 97 spacerLeftBottom->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
98 98
99 99 leftSidePane->addWidget(spacerLeftTop);
100 100 leftSidePane->addAction(openLeftInspectorAction);
101 101 leftSidePane->addWidget(spacerLeftBottom);
102 102
103 103
104 104 auto rightSidePane = m_Ui->rightInspectorSidePane->sidePane();
105 105 auto openRightInspectorAction = new QAction{QIcon{
106 106 ":/icones/next.png",
107 107 },
108 108 tr("Show/hide the right inspector"), this};
109 109
110 110 auto spacerRightTop = new QWidget{};
111 111 spacerRightTop->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
112 112
113 113 auto spacerRightBottom = new QWidget{};
114 114 spacerRightBottom->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
115 115
116 116 rightSidePane->addWidget(spacerRightTop);
117 117 rightSidePane->addAction(openRightInspectorAction);
118 118 rightSidePane->addWidget(spacerRightBottom);
119 119
120 120 openLeftInspectorAction->setCheckable(true);
121 121 openRightInspectorAction->setCheckable(true);
122 122
123 123 auto openInspector = [this](bool checked, bool right, auto action) {
124 124
125 125 action->setIcon(QIcon{(checked xor right) ? ":/icones/next.png" : ":/icones/previous.png"});
126 126
127 127 auto &lastInspectorSize
128 128 = right ? impl->m_LastOpenRightInspectorSize : impl->m_LastOpenLeftInspectorSize;
129 129
130 130 auto nextInspectorSize = right ? m_Ui->rightMainInspectorWidget->size()
131 131 : m_Ui->leftMainInspectorWidget->size();
132 132
133 133 // Update of the last opened geometry
134 134 if (checked) {
135 135 lastInspectorSize = nextInspectorSize;
136 136 }
137 137
138 138 auto startSize = lastInspectorSize;
139 139 auto endSize = startSize;
140 140 endSize.setWidth(0);
141 141
142 142 auto splitterInspectorIndex
143 143 = right ? RIGHTMAININSPECTORWIDGETSPLITTERINDEX : LEFTMAININSPECTORWIDGETSPLITTERINDEX;
144 144
145 145 auto currentSizes = m_Ui->splitter->sizes();
146 146 if (checked) {
147 147 // adjust sizes individually here, e.g.
148 148 currentSizes[splitterInspectorIndex] -= lastInspectorSize.width();
149 149 currentSizes[VIEWPLITTERINDEX] += lastInspectorSize.width();
150 150 m_Ui->splitter->setSizes(currentSizes);
151 151 }
152 152 else {
153 153 // adjust sizes individually here, e.g.
154 154 currentSizes[splitterInspectorIndex] += lastInspectorSize.width();
155 155 currentSizes[VIEWPLITTERINDEX] -= lastInspectorSize.width();
156 156 m_Ui->splitter->setSizes(currentSizes);
157 157 }
158 158
159 159 };
160 160
161 161
162 162 connect(openLeftInspectorAction, &QAction::triggered,
163 163 [openInspector, openLeftInspectorAction](bool checked) {
164 164 openInspector(checked, false, openLeftInspectorAction);
165 165 });
166 166 connect(openRightInspectorAction, &QAction::triggered,
167 167 [openInspector, openRightInspectorAction](bool checked) {
168 168 openInspector(checked, true, openRightInspectorAction);
169 169 });
170 170
171 171 // //////////////// //
172 172 // Menu and Toolbar //
173 173 // //////////////// //
174 174 this->menuBar()->addAction(tr("File"));
175 175 auto toolsMenu = this->menuBar()->addMenu(tr("Tools"));
176 176 toolsMenu->addAction(tr("Settings..."), [this]() {
177 177 // Loads settings
178 178 impl->m_SettingsDialog->loadSettings();
179 179
180 180 // Open settings dialog and save settings if the dialog is accepted
181 181 if (impl->m_SettingsDialog->exec() == QDialog::Accepted) {
182 182 impl->m_SettingsDialog->saveSettings();
183 183 }
184 184
185 185 });
186 186
187 187 auto mainToolBar = this->addToolBar(QStringLiteral("MainToolBar"));
188 188
189 189 auto timeWidget = new TimeWidget{};
190 190 mainToolBar->addWidget(timeWidget);
191 191
192 auto actionPointerMode = new QAction{QIcon(":/icones/pointer.png"), "Pointer", this};
192 auto actionPointerMode = new QAction{QIcon(":/icones/pointer.png"), "Move", this};
193 193 actionPointerMode->setCheckable(true);
194 194 actionPointerMode->setChecked(sqpApp->plotsInteractionMode()
195 195 == SqpApplication::PlotsInteractionMode::None);
196 196 connect(actionPointerMode, &QAction::triggered,
197 197 []() { sqpApp->setPlotsInteractionMode(SqpApplication::PlotsInteractionMode::None); });
198 198
199 199 auto actionZoomMode = new QAction{QIcon(":/icones/zoom.png"), "Zoom", this};
200 200 actionZoomMode->setCheckable(true);
201 201 actionZoomMode->setChecked(sqpApp->plotsInteractionMode()
202 202 == SqpApplication::PlotsInteractionMode::ZoomBox);
203 203 connect(actionZoomMode, &QAction::triggered, []() {
204 204 sqpApp->setPlotsInteractionMode(SqpApplication::PlotsInteractionMode::ZoomBox);
205 205 });
206 206
207 207 auto actionOrganisationMode = new QAction{QIcon(":/icones/drag.png"), "Organize", this};
208 208 actionOrganisationMode->setCheckable(true);
209 209 actionOrganisationMode->setChecked(sqpApp->plotsInteractionMode()
210 210 == SqpApplication::PlotsInteractionMode::DragAndDrop);
211 211 connect(actionOrganisationMode, &QAction::triggered, []() {
212 212 sqpApp->setPlotsInteractionMode(SqpApplication::PlotsInteractionMode::DragAndDrop);
213 213 });
214 214
215 215 auto actionZonesMode = new QAction{QIcon(":/icones/rectangle.png"), "Zones", this};
216 216 actionZonesMode->setCheckable(true);
217 217 actionZonesMode->setChecked(sqpApp->plotsInteractionMode()
218 218 == SqpApplication::PlotsInteractionMode::SelectionZones);
219 219 connect(actionZonesMode, &QAction::triggered, []() {
220 220 sqpApp->setPlotsInteractionMode(SqpApplication::PlotsInteractionMode::SelectionZones);
221 221 });
222 222
223 223 auto modeActionGroup = new QActionGroup{this};
224 224 modeActionGroup->addAction(actionZoomMode);
225 225 modeActionGroup->addAction(actionZonesMode);
226 226 modeActionGroup->addAction(actionOrganisationMode);
227 227 modeActionGroup->addAction(actionPointerMode);
228 228 modeActionGroup->setExclusive(true);
229 229
230 230 mainToolBar->addSeparator();
231 231 mainToolBar->addAction(actionPointerMode);
232 232 mainToolBar->addAction(actionZoomMode);
233 233 mainToolBar->addAction(actionOrganisationMode);
234 234 mainToolBar->addAction(actionZonesMode);
235 235 mainToolBar->addSeparator();
236 236
237 237 auto btnCursor = new QToolButton{this};
238 238 btnCursor->setIcon(QIcon(":/icones/cursor.png"));
239 239 btnCursor->setText("Cursor");
240 240 btnCursor->setToolTip("Cursor");
241 241 btnCursor->setPopupMode(QToolButton::InstantPopup);
242 242 auto cursorMenu = new QMenu("CursorMenu", this);
243 243 btnCursor->setMenu(cursorMenu);
244 244
245 245 auto noCursorAction = cursorMenu->addAction("No Cursor");
246 246 noCursorAction->setCheckable(true);
247 247 noCursorAction->setChecked(sqpApp->plotsCursorMode()
248 248 == SqpApplication::PlotsCursorMode::NoCursor);
249 249 connect(noCursorAction, &QAction::triggered,
250 250 []() { sqpApp->setPlotsCursorMode(SqpApplication::PlotsCursorMode::NoCursor); });
251 251
252 252 cursorMenu->addSeparator();
253 253 auto verticalCursorAction = cursorMenu->addAction("Vertical Cursor");
254 254 verticalCursorAction->setCheckable(true);
255 255 verticalCursorAction->setChecked(sqpApp->plotsCursorMode()
256 256 == SqpApplication::PlotsCursorMode::Vertical);
257 257 connect(verticalCursorAction, &QAction::triggered,
258 258 []() { sqpApp->setPlotsCursorMode(SqpApplication::PlotsCursorMode::Vertical); });
259 259
260 260 auto temporalCursorAction = cursorMenu->addAction("Temporal Cursor");
261 261 temporalCursorAction->setCheckable(true);
262 262 temporalCursorAction->setChecked(sqpApp->plotsCursorMode()
263 263 == SqpApplication::PlotsCursorMode::Temporal);
264 264 connect(temporalCursorAction, &QAction::triggered,
265 265 []() { sqpApp->setPlotsCursorMode(SqpApplication::PlotsCursorMode::Temporal); });
266 266
267 267 auto horizontalCursorAction = cursorMenu->addAction("Horizontal Cursor");
268 268 horizontalCursorAction->setCheckable(true);
269 269 horizontalCursorAction->setChecked(sqpApp->plotsCursorMode()
270 270 == SqpApplication::PlotsCursorMode::Horizontal);
271 271 connect(horizontalCursorAction, &QAction::triggered,
272 272 []() { sqpApp->setPlotsCursorMode(SqpApplication::PlotsCursorMode::Horizontal); });
273 273
274 274 auto crossCursorAction = cursorMenu->addAction("Cross Cursor");
275 275 crossCursorAction->setCheckable(true);
276 276 crossCursorAction->setChecked(sqpApp->plotsCursorMode()
277 277 == SqpApplication::PlotsCursorMode::Cross);
278 278 connect(crossCursorAction, &QAction::triggered,
279 279 []() { sqpApp->setPlotsCursorMode(SqpApplication::PlotsCursorMode::Cross); });
280 280
281 281 mainToolBar->addWidget(btnCursor);
282 282
283 283 auto cursorModeActionGroup = new QActionGroup{this};
284 284 cursorModeActionGroup->setExclusive(true);
285 285 cursorModeActionGroup->addAction(noCursorAction);
286 286 cursorModeActionGroup->addAction(verticalCursorAction);
287 287 cursorModeActionGroup->addAction(temporalCursorAction);
288 288 cursorModeActionGroup->addAction(horizontalCursorAction);
289 289 cursorModeActionGroup->addAction(crossCursorAction);
290 290
291 291 // //////// //
292 292 // Settings //
293 293 // //////// //
294 294
295 295 // Registers "general settings" widget to the settings dialog
296 296 impl->m_SettingsDialog->registerWidget(QStringLiteral("General"),
297 297 impl->m_GeneralSettingsWidget);
298 298
299 299 // /////////// //
300 300 // Connections //
301 301 // /////////// //
302 302
303 303 // Controllers / controllers connections
304 304 connect(&sqpApp->timeController(), SIGNAL(timeUpdated(SqpRange)), &sqpApp->variableController(),
305 305 SLOT(onDateTimeOnSelection(SqpRange)));
306 306
307 307 // Widgets / controllers connections
308 308
309 309 // DataSource
310 310 connect(&sqpApp->dataSourceController(), SIGNAL(dataSourceItemSet(DataSourceItem *)),
311 311 m_Ui->dataSourceWidget, SLOT(addDataSource(DataSourceItem *)));
312 312
313 313 // Time
314 314 connect(timeWidget, SIGNAL(timeUpdated(SqpRange)), &sqpApp->timeController(),
315 315 SLOT(onTimeToUpdate(SqpRange)));
316 316
317 317 // Visualization
318 318 connect(&sqpApp->visualizationController(),
319 319 SIGNAL(variableAboutToBeDeleted(std::shared_ptr<Variable>)), m_Ui->view,
320 320 SLOT(onVariableAboutToBeDeleted(std::shared_ptr<Variable>)));
321 321
322 322 connect(&sqpApp->visualizationController(),
323 323 SIGNAL(rangeChanged(std::shared_ptr<Variable>, const SqpRange &)), m_Ui->view,
324 324 SLOT(onRangeChanged(std::shared_ptr<Variable>, const SqpRange &)));
325 325
326 326 // Widgets / widgets connections
327 327
328 328 // For the following connections, we use DirectConnection to allow each widget that can
329 329 // potentially attach a menu to the variable's menu to do so before this menu is displayed.
330 330 // The order of connections is also important, since it determines the order in which each
331 331 // widget will attach its menu
332 332 connect(
333 333 m_Ui->variableInspectorWidget,
334 334 SIGNAL(tableMenuAboutToBeDisplayed(QMenu *, const QVector<std::shared_ptr<Variable> > &)),
335 335 m_Ui->view, SLOT(attachVariableMenu(QMenu *, const QVector<std::shared_ptr<Variable> > &)),
336 336 Qt::DirectConnection);
337 337 }
338 338
339 339 MainWindow::~MainWindow()
340 340 {
341 341 }
342 342
343 343 void MainWindow::changeEvent(QEvent *e)
344 344 {
345 345 QMainWindow::changeEvent(e);
346 346 switch (e->type()) {
347 347 case QEvent::LanguageChange:
348 348 m_Ui->retranslateUi(this);
349 349 break;
350 350 default:
351 351 break;
352 352 }
353 353 }
@@ -1,876 +1,876
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/VisualizationSelectionZoneItem.h"
8 8 #include "Visualization/VisualizationSelectionZoneManager.h"
9 9 #include "Visualization/VisualizationWidget.h"
10 10 #include "Visualization/VisualizationZoneWidget.h"
11 11 #include "ui_VisualizationGraphWidget.h"
12 12
13 13 #include <Common/MimeTypesDef.h>
14 14 #include <Data/ArrayData.h>
15 15 #include <Data/IDataSeries.h>
16 16 #include <Data/SpectrogramSeries.h>
17 17 #include <DragAndDrop/DragDropHelper.h>
18 18 #include <Settings/SqpSettingsDefs.h>
19 19 #include <SqpApplication.h>
20 20 #include <Time/TimeController.h>
21 21 #include <Variable/Variable.h>
22 22 #include <Variable/VariableController.h>
23 23
24 24 #include <unordered_map>
25 25
26 26 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
27 27
28 28 namespace {
29 29
30 30 /// Key pressed to enable drag&drop in all modes
31 31 const auto DRAG_DROP_MODIFIER = Qt::AltModifier;
32 32
33 33 /// Key pressed to enable zoom on horizontal axis
34 34 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier;
35 35
36 36 /// Key pressed to enable zoom on vertical axis
37 37 const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier;
38 38
39 39 /// Speed of a step of a wheel event for a pan, in percentage of the axis range
40 40 const auto PAN_SPEED = 5;
41 41
42 42 /// Key pressed to enable a calibration pan
43 43 const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier;
44 44
45 45 /// Key pressed to enable multi selection of selection zones
46 46 const auto MULTI_ZONE_SELECTION_MODIFIER = Qt::ControlModifier;
47 47
48 48 /// Minimum size for the zoom box, in percentage of the axis range
49 49 const auto ZOOM_BOX_MIN_SIZE = 0.8;
50 50
51 51 /// Format of the dates appearing in the label of a cursor
52 52 const auto CURSOR_LABELS_DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd\nhh:mm:ss:zzz");
53 53
54 54 } // namespace
55 55
56 56 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
57 57
58 58 explicit VisualizationGraphWidgetPrivate(const QString &name)
59 59 : m_Name{name},
60 60 m_DoAcquisition{true},
61 61 m_IsCalibration{false},
62 62 m_RenderingDelegate{nullptr}
63 63 {
64 64 }
65 65
66 66 void updateData(PlottablesMap &plottables, std::shared_ptr<IDataSeries> dataSeries,
67 67 const SqpRange &range)
68 68 {
69 69 VisualizationGraphHelper::updateData(plottables, dataSeries, range);
70 70
71 71 // Prevents that data has changed to update rendering
72 72 m_RenderingDelegate->onPlotUpdated();
73 73 }
74 74
75 75 QString m_Name;
76 76 // 1 variable -> n qcpplot
77 77 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
78 78 bool m_DoAcquisition;
79 79 bool m_IsCalibration;
80 80 /// Delegate used to attach rendering features to the plot
81 81 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
82 82
83 83 QCPItemRect *m_DrawingZoomRect = nullptr;
84 84 QStack<QPair<QCPRange, QCPRange> > m_ZoomStack;
85 85
86 86 std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr;
87 87 std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr;
88 88
89 89 VisualizationSelectionZoneItem *m_DrawingZone = nullptr;
90 90 VisualizationSelectionZoneItem *m_HoveredZone = nullptr;
91 91 QVector<VisualizationSelectionZoneItem *> m_SelectionZones;
92 92
93 93 bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even
94 94
95 95 void startDrawingRect(const QPoint &pos, QCustomPlot &plot)
96 96 {
97 97 removeDrawingRect(plot);
98 98
99 99 auto axisPos = posToAxisPos(pos, plot);
100 100
101 101 m_DrawingZoomRect = new QCPItemRect{&plot};
102 102 QPen p;
103 103 p.setWidth(2);
104 104 m_DrawingZoomRect->setPen(p);
105 105
106 106 m_DrawingZoomRect->topLeft->setCoords(axisPos);
107 107 m_DrawingZoomRect->bottomRight->setCoords(axisPos);
108 108 }
109 109
110 110 void removeDrawingRect(QCustomPlot &plot)
111 111 {
112 112 if (m_DrawingZoomRect) {
113 113 plot.removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot
114 114 m_DrawingZoomRect = nullptr;
115 115 plot.replot(QCustomPlot::rpQueuedReplot);
116 116 }
117 117 }
118 118
119 119 void startDrawingZone(const QPoint &pos, VisualizationGraphWidget *graph)
120 120 {
121 121 endDrawingZone(graph);
122 122
123 123 auto axisPos = posToAxisPos(pos, graph->plot());
124 124
125 125 m_DrawingZone = new VisualizationSelectionZoneItem{&graph->plot()};
126 126 m_DrawingZone->setRange(axisPos.x(), axisPos.x());
127 127 m_DrawingZone->setEditionEnabled(false);
128 128 }
129 129
130 130 void endDrawingZone(VisualizationGraphWidget *graph)
131 131 {
132 132 if (m_DrawingZone) {
133 133 auto drawingZoneRange = m_DrawingZone->range();
134 134 if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0) {
135 135 m_DrawingZone->setEditionEnabled(true);
136 136 addSelectionZone(m_DrawingZone);
137 137 }
138 138 else {
139 139 graph->plot().removeItem(m_DrawingZone); // the item is deleted by QCustomPlot
140 140 }
141 141
142 142 graph->plot().replot(QCustomPlot::rpQueuedReplot);
143 143 m_DrawingZone = nullptr;
144 144 }
145 145 }
146 146
147 147 void setSelectionZonesEditionEnabled(bool value)
148 148 {
149 149 for (auto s : m_SelectionZones) {
150 150 s->setEditionEnabled(value);
151 151 }
152 152 }
153 153
154 154 void addSelectionZone(VisualizationSelectionZoneItem *zone) { m_SelectionZones << zone; }
155 155
156 156 VisualizationSelectionZoneItem *selectionZoneAt(const QPoint &pos,
157 157 const QCustomPlot &plot) const
158 158 {
159 159 VisualizationSelectionZoneItem *selectionZoneItemUnderCursor = nullptr;
160 160 auto minDistanceToZone = -1;
161 161 for (auto zone : m_SelectionZones) {
162 162 auto distanceToZone = zone->selectTest(pos, false);
163 163 if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone)
164 164 && distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
165 165 selectionZoneItemUnderCursor = zone;
166 166 }
167 167 }
168 168
169 169 return selectionZoneItemUnderCursor;
170 170 }
171 171
172 172 QPointF posToAxisPos(const QPoint &pos, QCustomPlot &plot) const
173 173 {
174 174 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
175 175 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
176 176 return QPointF{axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y())};
177 177 }
178 178
179 179 bool pointIsInAxisRect(const QPointF &axisPoint, QCustomPlot &plot) const
180 180 {
181 181 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
182 182 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
183 183 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
184 184 }
185 185 };
186 186
187 187 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
188 188 : VisualizationDragWidget{parent},
189 189 ui{new Ui::VisualizationGraphWidget},
190 190 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
191 191 {
192 192 ui->setupUi(this);
193 193
194 194 // 'Close' options : widget is deleted when closed
195 195 setAttribute(Qt::WA_DeleteOnClose);
196 196
197 197 // Set qcpplot properties :
198 198 // - zoom is enabled
199 199 // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation
200 ui->widget->setInteractions(QCP::iRangeZoom);
200 ui->widget->setInteractions(QCP::iRangeZoom | QCP::iSelectItems);
201 201 ui->widget->axisRect()->setRangeDrag(Qt::Horizontal | Qt::Vertical);
202 202
203 203 // The delegate must be initialized after the ui as it uses the plot
204 204 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
205 205
206 206 // Init the cursors
207 207 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
208 208 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
209 209 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
210 210 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
211 211
212 212 connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress);
213 213 connect(ui->widget, &QCustomPlot::mouseRelease, this,
214 214 &VisualizationGraphWidget::onMouseRelease);
215 215 connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove);
216 216 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
217 217 connect(ui->widget, &QCustomPlot::mouseDoubleClick, this,
218 218 &VisualizationGraphWidget::onMouseDoubleClick);
219 219 connect(
220 220 ui->widget->xAxis,
221 221 static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(&QCPAxis::rangeChanged),
222 222 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
223 223
224 224 // Activates menu when right clicking on the graph
225 225 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
226 226 connect(ui->widget, &QCustomPlot::customContextMenuRequested, this,
227 227 &VisualizationGraphWidget::onGraphMenuRequested);
228 228
229 229 connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
230 230 &VariableController::onRequestDataLoading);
231 231
232 232 connect(&sqpApp->variableController(), &VariableController::updateVarDisplaying, this,
233 233 &VisualizationGraphWidget::onUpdateVarDisplaying);
234 234
235 235 #ifdef Q_OS_MAC
236 236 plot().setPlottingHint(QCP::phFastPolylines, true);
237 237 #endif
238 238 }
239 239
240 240
241 241 VisualizationGraphWidget::~VisualizationGraphWidget()
242 242 {
243 243 delete ui;
244 244 }
245 245
246 246 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
247 247 {
248 248 auto parent = parentWidget();
249 249 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
250 250 parent = parent->parentWidget();
251 251 }
252 252
253 253 return qobject_cast<VisualizationZoneWidget *>(parent);
254 254 }
255 255
256 256 VisualizationWidget *VisualizationGraphWidget::parentVisualizationWidget() const
257 257 {
258 258 auto parent = parentWidget();
259 259 while (parent != nullptr && !qobject_cast<VisualizationWidget *>(parent)) {
260 260 parent = parent->parentWidget();
261 261 }
262 262
263 263 return qobject_cast<VisualizationWidget *>(parent);
264 264 }
265 265
266 266 void VisualizationGraphWidget::enableAcquisition(bool enable)
267 267 {
268 268 impl->m_DoAcquisition = enable;
269 269 }
270 270
271 271 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, SqpRange range)
272 272 {
273 273 // Uses delegate to create the qcpplot components according to the variable
274 274 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
275 275
276 276 if (auto dataSeries = variable->dataSeries()) {
277 277 // Set axes properties according to the units of the data series
278 278 impl->m_RenderingDelegate->setAxesProperties(dataSeries);
279 279
280 280 // Sets rendering properties for the new plottables
281 281 // Warning: this method must be called after setAxesProperties(), as it can access to some
282 282 // axes properties that have to be initialized
283 283 impl->m_RenderingDelegate->setPlottablesProperties(dataSeries, createdPlottables);
284 284 }
285 285
286 286 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
287 287
288 288 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
289 289
290 290 this->enableAcquisition(false);
291 291 this->setGraphRange(range);
292 292 this->enableAcquisition(true);
293 293
294 294 emit requestDataLoading(QVector<std::shared_ptr<Variable> >() << variable, range, false);
295 295
296 296 emit variableAdded(variable);
297 297 }
298 298
299 299 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
300 300 {
301 301 // Each component associated to the variable :
302 302 // - is removed from qcpplot (which deletes it)
303 303 // - is no longer referenced in the map
304 304 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
305 305 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
306 306 emit variableAboutToBeRemoved(variable);
307 307
308 308 auto &plottablesMap = variableIt->second;
309 309
310 310 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
311 311 plottableIt != plottableEnd;) {
312 312 ui->widget->removePlottable(plottableIt->second);
313 313 plottableIt = plottablesMap.erase(plottableIt);
314 314 }
315 315
316 316 impl->m_VariableToPlotMultiMap.erase(variableIt);
317 317 }
318 318
319 319 // Updates graph
320 320 ui->widget->replot();
321 321 }
322 322
323 323 QList<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
324 324 {
325 325 auto variables = QList<std::shared_ptr<Variable> >{};
326 326 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
327 327 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
328 328 variables << it->first;
329 329 }
330 330
331 331 return variables;
332 332 }
333 333
334 334 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
335 335 {
336 336 if (!variable) {
337 337 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
338 338 return;
339 339 }
340 340
341 341 VisualizationGraphHelper::setYAxisRange(variable, *ui->widget);
342 342 }
343 343
344 344 SqpRange VisualizationGraphWidget::graphRange() const noexcept
345 345 {
346 346 auto graphRange = ui->widget->xAxis->range();
347 347 return SqpRange{graphRange.lower, graphRange.upper};
348 348 }
349 349
350 350 void VisualizationGraphWidget::setGraphRange(const SqpRange &range)
351 351 {
352 352 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange START");
353 353 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
354 354 ui->widget->replot();
355 355 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange END");
356 356 }
357 357
358 358 QVector<SqpRange> VisualizationGraphWidget::selectionZoneRanges() const
359 359 {
360 360 QVector<SqpRange> ranges;
361 361 for (auto zone : impl->m_SelectionZones) {
362 362 ranges << zone->range();
363 363 }
364 364
365 365 return ranges;
366 366 }
367 367
368 368 void VisualizationGraphWidget::addSelectionZones(const QVector<SqpRange> &ranges)
369 369 {
370 370 for (const auto &range : ranges) {
371 371 // note: ownership is transfered to QCustomPlot
372 372 auto zone = new VisualizationSelectionZoneItem(&plot());
373 373 zone->setRange(range.m_TStart, range.m_TEnd);
374 374 impl->addSelectionZone(zone);
375 375 }
376 376
377 377 plot().replot(QCustomPlot::rpQueuedReplot);
378 378 }
379 379
380 380 void VisualizationGraphWidget::undoZoom()
381 381 {
382 382 auto zoom = impl->m_ZoomStack.pop();
383 383 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
384 384 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
385 385
386 386 axisX->setRange(zoom.first);
387 387 axisY->setRange(zoom.second);
388 388
389 389 plot().replot(QCustomPlot::rpQueuedReplot);
390 390 }
391 391
392 392 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
393 393 {
394 394 if (visitor) {
395 395 visitor->visit(this);
396 396 }
397 397 else {
398 398 qCCritical(LOG_VisualizationGraphWidget())
399 399 << tr("Can't visit widget : the visitor is null");
400 400 }
401 401 }
402 402
403 403 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
404 404 {
405 405 auto isSpectrogram = [](const auto &variable) {
406 406 return std::dynamic_pointer_cast<SpectrogramSeries>(variable.dataSeries()) != nullptr;
407 407 };
408 408
409 409 // - A spectrogram series can't be dropped on graph with existing plottables
410 410 // - No data series can be dropped on graph with existing spectrogram series
411 411 return isSpectrogram(variable)
412 412 ? impl->m_VariableToPlotMultiMap.empty()
413 413 : std::none_of(
414 414 impl->m_VariableToPlotMultiMap.cbegin(), impl->m_VariableToPlotMultiMap.cend(),
415 415 [isSpectrogram](const auto &entry) { return isSpectrogram(*entry.first); });
416 416 }
417 417
418 418 bool VisualizationGraphWidget::contains(const Variable &variable) const
419 419 {
420 420 // Finds the variable among the keys of the map
421 421 auto variablePtr = &variable;
422 422 auto findVariable
423 423 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
424 424
425 425 auto end = impl->m_VariableToPlotMultiMap.cend();
426 426 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
427 427 return it != end;
428 428 }
429 429
430 430 QString VisualizationGraphWidget::name() const
431 431 {
432 432 return impl->m_Name;
433 433 }
434 434
435 435 QMimeData *VisualizationGraphWidget::mimeData(const QPoint &position) const
436 436 {
437 437 auto mimeData = new QMimeData;
438 438
439 439 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position, plot());
440 440 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
441 441 && selectionZoneItemUnderCursor) {
442 442 mimeData->setData(MIME_TYPE_TIME_RANGE, TimeController::mimeDataForTimeRange(
443 443 selectionZoneItemUnderCursor->range()));
444 444 mimeData->setData(MIME_TYPE_SELECTION_ZONE, TimeController::mimeDataForTimeRange(
445 445 selectionZoneItemUnderCursor->range()));
446 446 }
447 447 else {
448 448 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
449 449
450 450 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
451 451 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
452 452 }
453 453
454 454 return mimeData;
455 455 }
456 456
457 457 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint &dragPosition)
458 458 {
459 459 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition, plot());
460 460 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
461 461 && selectionZoneItemUnderCursor) {
462 462
463 463 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
464 464 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
465 465
466 466 auto zoneSize = QSizeF{qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
467 467 qAbs(zoneBottomRight.y() - zoneTopLeft.y())}
468 468 .toSize();
469 469
470 470 auto pixmap = QPixmap(zoneSize);
471 471 render(&pixmap, QPoint(), QRegion{QRect{zoneTopLeft.toPoint(), zoneSize}});
472 472
473 473 return pixmap;
474 474 }
475 475
476 476 return QPixmap();
477 477 }
478 478
479 479 bool VisualizationGraphWidget::isDragAllowed() const
480 480 {
481 481 return true;
482 482 }
483 483
484 484 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
485 485 {
486 486 if (highlighted) {
487 487 plot().setBackground(QBrush(QColor("#BBD5EE")));
488 488 }
489 489 else {
490 490 plot().setBackground(QBrush(Qt::white));
491 491 }
492 492
493 493 plot().update();
494 494 }
495 495
496 496 void VisualizationGraphWidget::addVerticalCursor(double time)
497 497 {
498 498 impl->m_VerticalCursor->setPosition(time);
499 499 impl->m_VerticalCursor->setVisible(true);
500 500
501 501 auto text
502 502 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
503 503 impl->m_VerticalCursor->setLabelText(text);
504 504 }
505 505
506 506 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
507 507 {
508 508 impl->m_VerticalCursor->setAbsolutePosition(position);
509 509 impl->m_VerticalCursor->setVisible(true);
510 510
511 511 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
512 512 auto text
513 513 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
514 514 impl->m_VerticalCursor->setLabelText(text);
515 515 }
516 516
517 517 void VisualizationGraphWidget::removeVerticalCursor()
518 518 {
519 519 impl->m_VerticalCursor->setVisible(false);
520 520 plot().replot(QCustomPlot::rpQueuedReplot);
521 521 }
522 522
523 523 void VisualizationGraphWidget::addHorizontalCursor(double value)
524 524 {
525 525 impl->m_HorizontalCursor->setPosition(value);
526 526 impl->m_HorizontalCursor->setVisible(true);
527 527 impl->m_HorizontalCursor->setLabelText(QString::number(value));
528 528 }
529 529
530 530 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
531 531 {
532 532 impl->m_HorizontalCursor->setAbsolutePosition(position);
533 533 impl->m_HorizontalCursor->setVisible(true);
534 534
535 535 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
536 536 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
537 537 }
538 538
539 539 void VisualizationGraphWidget::removeHorizontalCursor()
540 540 {
541 541 impl->m_HorizontalCursor->setVisible(false);
542 542 plot().replot(QCustomPlot::rpQueuedReplot);
543 543 }
544 544
545 545 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
546 546 {
547 547 Q_UNUSED(event);
548 548
549 549 // Prevents that all variables will be removed from graph when it will be closed
550 550 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
551 551 emit variableAboutToBeRemoved(variableEntry.first);
552 552 }
553 553 }
554 554
555 555 void VisualizationGraphWidget::enterEvent(QEvent *event)
556 556 {
557 557 Q_UNUSED(event);
558 558 impl->m_RenderingDelegate->showGraphOverlay(true);
559 559 }
560 560
561 561 void VisualizationGraphWidget::leaveEvent(QEvent *event)
562 562 {
563 563 Q_UNUSED(event);
564 564 impl->m_RenderingDelegate->showGraphOverlay(false);
565 565
566 566 if (auto parentZone = parentZoneWidget()) {
567 567 parentZone->notifyMouseLeaveGraph(this);
568 568 }
569 569 else {
570 570 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
571 571 }
572 572
573 573 if (impl->m_HoveredZone) {
574 574 impl->m_HoveredZone->setHovered(false);
575 575 impl->m_HoveredZone = nullptr;
576 576 }
577 577 }
578 578
579 579 QCustomPlot &VisualizationGraphWidget::plot() const noexcept
580 580 {
581 581 return *ui->widget;
582 582 }
583 583
584 584 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
585 585 {
586 586 QMenu graphMenu{};
587 587
588 588 // Iterates on variables (unique keys)
589 589 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
590 590 end = impl->m_VariableToPlotMultiMap.cend();
591 591 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
592 592 // 'Remove variable' action
593 593 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
594 594 [ this, var = it->first ]() { removeVariable(var); });
595 595 }
596 596
597 597 if (!impl->m_ZoomStack.isEmpty()) {
598 598 if (!graphMenu.isEmpty()) {
599 599 graphMenu.addSeparator();
600 600 }
601 601
602 602 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
603 603 }
604 604
605 605 if (!graphMenu.isEmpty()) {
606 606 graphMenu.exec(QCursor::pos());
607 607 }
608 608 }
609 609
610 610 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
611 611 {
612 612 qCDebug(LOG_VisualizationGraphWidget())
613 613 << tr("TORM: VisualizationGraphWidget::onRangeChanged")
614 614 << QThread::currentThread()->objectName() << "DoAcqui" << impl->m_DoAcquisition;
615 615
616 616 auto graphRange = SqpRange{t1.lower, t1.upper};
617 617 auto oldGraphRange = SqpRange{t2.lower, t2.upper};
618 618
619 619 if (impl->m_DoAcquisition) {
620 620 QVector<std::shared_ptr<Variable> > variableUnderGraphVector;
621 621
622 622 for (auto it = impl->m_VariableToPlotMultiMap.begin(),
623 623 end = impl->m_VariableToPlotMultiMap.end();
624 624 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
625 625 variableUnderGraphVector.push_back(it->first);
626 626 }
627 627 emit requestDataLoading(std::move(variableUnderGraphVector), graphRange,
628 628 !impl->m_IsCalibration);
629 629
630 630 if (!impl->m_IsCalibration) {
631 631 qCDebug(LOG_VisualizationGraphWidget())
632 632 << tr("TORM: VisualizationGraphWidget::Synchronize notify !!")
633 633 << QThread::currentThread()->objectName() << graphRange << oldGraphRange;
634 634 emit synchronize(graphRange, oldGraphRange);
635 635 }
636 636 }
637 637
638 638 auto pos = mapFromGlobal(QCursor::pos());
639 639 auto axisPos = impl->posToAxisPos(pos, plot());
640 640 if (auto parentZone = parentZoneWidget()) {
641 641 if (impl->pointIsInAxisRect(axisPos, plot())) {
642 642 parentZone->notifyMouseMoveInGraph(pos, axisPos, this);
643 643 }
644 644 else {
645 645 parentZone->notifyMouseLeaveGraph(this);
646 646 }
647 647 }
648 648 else {
649 649 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
650 650 }
651 651 }
652 652
653 653 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept
654 654 {
655 655 impl->m_RenderingDelegate->onMouseDoubleClick(event);
656 656 }
657 657
658 658 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
659 659 {
660 660 // Handles plot rendering when mouse is moving
661 661 impl->m_RenderingDelegate->onMouseMove(event);
662 662
663 663 auto axisPos = impl->posToAxisPos(event->pos(), plot());
664 664
665 665 // Zoom box and zone drawing
666 666 if (impl->m_DrawingZoomRect) {
667 667 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
668 668 }
669 669 else if (impl->m_DrawingZone) {
670 670 impl->m_DrawingZone->setEnd(axisPos.x());
671 671 }
672 672
673 673 // Cursor
674 674 if (auto parentZone = parentZoneWidget()) {
675 675 if (impl->pointIsInAxisRect(axisPos, plot())) {
676 676 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
677 677 }
678 678 else {
679 679 parentZone->notifyMouseLeaveGraph(this);
680 680 }
681 681 }
682 682 else {
683 683 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
684 684 }
685 685
686 686 // Search for the selection zone under the mouse
687 687 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
688 688 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
689 689 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) {
690 690
691 691 // Sets the appropriate cursor shape
692 692 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
693 693 setCursor(cursorShape);
694 694
695 695 // Manages the hovered zone
696 696 if (selectionZoneItemUnderCursor != impl->m_HoveredZone) {
697 697 if (impl->m_HoveredZone) {
698 698 impl->m_HoveredZone->setHovered(false);
699 699 }
700 700 selectionZoneItemUnderCursor->setHovered(true);
701 701 impl->m_HoveredZone = selectionZoneItemUnderCursor;
702 702 plot().replot(QCustomPlot::rpQueuedReplot);
703 703 }
704 704 }
705 705 else {
706 706 // There is no zone under the mouse or the interaction mode is not "selection zones"
707 707 if (impl->m_HoveredZone) {
708 708 impl->m_HoveredZone->setHovered(false);
709 709 impl->m_HoveredZone = nullptr;
710 710 }
711 711
712 712 setCursor(Qt::ArrowCursor);
713 713 }
714 714
715 715 impl->m_HasMovedMouse = true;
716 716 VisualizationDragWidget::mouseMoveEvent(event);
717 717 }
718 718
719 719 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
720 720 {
721 721 auto value = event->angleDelta().x() + event->angleDelta().y();
722 722 if (value != 0) {
723 723
724 724 auto direction = value > 0 ? 1.0 : -1.0;
725 725 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
726 726 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
727 727 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
728 728
729 729 auto zoomOrientations = QFlags<Qt::Orientation>{};
730 730 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
731 731 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
732 732
733 733 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
734 734
735 735 if (!isZoomX && !isZoomY) {
736 736 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
737 737 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
738 738
739 739 axis->setRange(axis->range() + diff);
740 740
741 741 if (plot().noAntialiasingOnDrag()) {
742 742 plot().setNotAntialiasedElements(QCP::aeAll);
743 743 }
744 744
745 745 plot().replot(QCustomPlot::rpQueuedReplot);
746 746 }
747 747 }
748 748 }
749 749
750 750 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
751 751 {
752 752 auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
753 753 auto isSelectionZoneMode
754 754 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
755 755 auto isLeftClick = event->buttons().testFlag(Qt::LeftButton);
756 756
757 757 if (!isDragDropClick && isLeftClick) {
758 758 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) {
759 759 // Starts a zoom box
760 760 impl->startDrawingRect(event->pos(), plot());
761 761 }
762 762 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr) {
763 763 // Starts a new selection zone
764 764 auto zoneAtPos = impl->selectionZoneAt(event->pos(), plot());
765 765 if (!zoneAtPos) {
766 766 impl->startDrawingZone(event->pos(), this);
767 767 }
768 768 }
769 769 }
770 770
771 771 // Allows mouse panning only in default mode
772 772 plot().setInteraction(QCP::iRangeDrag, sqpApp->plotsInteractionMode()
773 773 == SqpApplication::PlotsInteractionMode::None
774 774 && !isDragDropClick);
775 775
776 776 // Allows zone edition only in selection zone mode without drag&drop
777 777 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
778 778
779 779 // Selection / Deselection
780 780 if (isSelectionZoneMode) {
781 781 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
782 782 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
783 783 if (selectionZoneItemUnderCursor && isLeftClick) {
784 784 selectionZoneItemUnderCursor->setAssociatedEditedZones(
785 785 parentVisualizationWidget()->selectionZoneManager().selectedItems());
786 786 }
787 787 else if (!isMultiSelectionClick && isLeftClick) {
788 788 parentVisualizationWidget()->selectionZoneManager().clearSelection();
789 789 }
790 790 else {
791 791 // No selection change
792 792 }
793 793 }
794 794
795 795
796 796 impl->m_HasMovedMouse = false;
797 797 VisualizationDragWidget::mousePressEvent(event);
798 798 }
799 799
800 800 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
801 801 {
802 802 if (impl->m_DrawingZoomRect) {
803 803
804 804 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
805 805 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
806 806
807 807 auto newAxisXRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().x(),
808 808 impl->m_DrawingZoomRect->bottomRight->coords().x()};
809 809
810 810 auto newAxisYRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().y(),
811 811 impl->m_DrawingZoomRect->bottomRight->coords().y()};
812 812
813 813 impl->removeDrawingRect(plot());
814 814
815 815 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
816 816 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
817 817 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
818 818 axisX->setRange(newAxisXRange);
819 819 axisY->setRange(newAxisYRange);
820 820
821 821 plot().replot(QCustomPlot::rpQueuedReplot);
822 822 }
823 823 }
824 824
825 825 impl->endDrawingZone(this);
826 826
827 827 impl->m_IsCalibration = false;
828 828
829 829 // Selection / Deselection
830 830 auto isSelectionZoneMode
831 831 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
832 832 if (isSelectionZoneMode) {
833 833 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
834 834 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
835 835 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton) {
836 836 if (!isMultiSelectionClick && !impl->m_HasMovedMouse) {
837 837 parentVisualizationWidget()->selectionZoneManager().select(
838 838 {selectionZoneItemUnderCursor});
839 839 }
840 840 else if (!impl->m_HasMovedMouse) {
841 841 parentVisualizationWidget()->selectionZoneManager().setSelected(
842 842 selectionZoneItemUnderCursor, !selectionZoneItemUnderCursor->selected()
843 843 || event->button() == Qt::RightButton);
844 844 }
845 845 }
846 846 else {
847 847 // No selection change
848 848 }
849 849 }
850 850 }
851 851
852 852 void VisualizationGraphWidget::onDataCacheVariableUpdated()
853 853 {
854 854 auto graphRange = ui->widget->xAxis->range();
855 855 auto dateTime = SqpRange{graphRange.lower, graphRange.upper};
856 856
857 857 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
858 858 auto variable = variableEntry.first;
859 859 qCDebug(LOG_VisualizationGraphWidget())
860 860 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
861 861 qCDebug(LOG_VisualizationGraphWidget())
862 862 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
863 863 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
864 864 impl->updateData(variableEntry.second, variable->dataSeries(), variable->range());
865 865 }
866 866 }
867 867 }
868 868
869 869 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
870 870 const SqpRange &range)
871 871 {
872 872 auto it = impl->m_VariableToPlotMultiMap.find(variable);
873 873 if (it != impl->m_VariableToPlotMultiMap.end()) {
874 874 impl->updateData(it->second, variable->dataSeries(), range);
875 875 }
876 876 }
General Comments 0
You need to be logged in to leave comments. Login now