##// END OF EJS Templates
Add Qt::AA_EnableHighDpiScaling and QCP::phFastPolylines
perrinel -
r1374:c2f8a1a0873b
parent child
Show More
@@ -1,205 +1,207
1 1 #include "SqpApplication.h"
2 2
3 3 #include <Actions/ActionsGuiController.h>
4 4 #include <Catalogue/CatalogueController.h>
5 5 #include <Data/IDataProvider.h>
6 6 #include <DataSource/DataSourceController.h>
7 7 #include <DragAndDrop/DragDropGuiController.h>
8 8 #include <Network/NetworkController.h>
9 9 #include <QThread>
10 10 #include <Time/TimeController.h>
11 11 #include <Variable/Variable.h>
12 12 #include <Variable/VariableController.h>
13 13 #include <Variable/VariableModel.h>
14 14 #include <Visualization/VisualizationController.h>
15 15
16 16 Q_LOGGING_CATEGORY(LOG_SqpApplication, "SqpApplication")
17 17
18 18 class SqpApplication::SqpApplicationPrivate {
19 19 public:
20 20 SqpApplicationPrivate()
21 21 : m_DataSourceController{std::make_unique<DataSourceController>()},
22 22 m_VariableController{std::make_unique<VariableController>()},
23 23 m_TimeController{std::make_unique<TimeController>()},
24 24 m_NetworkController{std::make_unique<NetworkController>()},
25 25 m_VisualizationController{std::make_unique<VisualizationController>()},
26 26 m_DragDropGuiController{std::make_unique<DragDropGuiController>()},
27 27 m_CatalogueController{std::make_unique<CatalogueController>()},
28 28 m_ActionsGuiController{std::make_unique<ActionsGuiController>()},
29 29 m_PlotInterractionMode(SqpApplication::PlotsInteractionMode::None),
30 30 m_PlotCursorMode(SqpApplication::PlotsCursorMode::NoCursor)
31 31 {
32 32 // /////////////////////////////// //
33 33 // Connections between controllers //
34 34 // /////////////////////////////// //
35 35
36 36 // VariableController <-> DataSourceController
37 37 connect(m_DataSourceController.get(),
38 38 SIGNAL(variableCreationRequested(const QString &, const QVariantHash &,
39 39 std::shared_ptr<IDataProvider>)),
40 40 m_VariableController.get(),
41 41 SLOT(createVariable(const QString &, const QVariantHash &,
42 42 std::shared_ptr<IDataProvider>)));
43 43
44 44 connect(m_VariableController->variableModel(), &VariableModel::requestVariable,
45 45 m_DataSourceController.get(), &DataSourceController::requestVariable);
46 46
47 47 // VariableController <-> VisualizationController
48 48 connect(m_VariableController.get(),
49 49 SIGNAL(variableAboutToBeDeleted(std::shared_ptr<Variable>)),
50 50 m_VisualizationController.get(),
51 51 SIGNAL(variableAboutToBeDeleted(std::shared_ptr<Variable>)), Qt::DirectConnection);
52 52
53 53 connect(m_VariableController.get(),
54 54 SIGNAL(rangeChanged(std::shared_ptr<Variable>, const SqpRange &)),
55 55 m_VisualizationController.get(),
56 56 SIGNAL(rangeChanged(std::shared_ptr<Variable>, const SqpRange &)));
57 57
58 58
59 59 m_DataSourceController->moveToThread(&m_DataSourceControllerThread);
60 60 m_DataSourceControllerThread.setObjectName("DataSourceControllerThread");
61 61 m_NetworkController->moveToThread(&m_NetworkControllerThread);
62 62 m_NetworkControllerThread.setObjectName("NetworkControllerThread");
63 63 m_VariableController->moveToThread(&m_VariableControllerThread);
64 64 m_VariableControllerThread.setObjectName("VariableControllerThread");
65 65 m_VisualizationController->moveToThread(&m_VisualizationControllerThread);
66 66 m_VisualizationControllerThread.setObjectName("VsualizationControllerThread");
67 67
68 68 // Additionnal init
69 69 m_VariableController->setTimeController(m_TimeController.get());
70 70 }
71 71
72 72 virtual ~SqpApplicationPrivate()
73 73 {
74 74 m_DataSourceControllerThread.quit();
75 75 m_DataSourceControllerThread.wait();
76 76
77 77 m_NetworkControllerThread.quit();
78 78 m_NetworkControllerThread.wait();
79 79
80 80 m_VariableControllerThread.quit();
81 81 m_VariableControllerThread.wait();
82 82
83 83 m_VisualizationControllerThread.quit();
84 84 m_VisualizationControllerThread.wait();
85 85 }
86 86
87 87 std::unique_ptr<DataSourceController> m_DataSourceController;
88 88 std::unique_ptr<VariableController> m_VariableController;
89 89 std::unique_ptr<TimeController> m_TimeController;
90 90 std::unique_ptr<NetworkController> m_NetworkController;
91 91 std::unique_ptr<VisualizationController> m_VisualizationController;
92 92 std::unique_ptr<CatalogueController> m_CatalogueController;
93 93
94 94 QThread m_DataSourceControllerThread;
95 95 QThread m_NetworkControllerThread;
96 96 QThread m_VariableControllerThread;
97 97 QThread m_VisualizationControllerThread;
98 98
99 99 std::unique_ptr<DragDropGuiController> m_DragDropGuiController;
100 100 std::unique_ptr<ActionsGuiController> m_ActionsGuiController;
101 101
102 102 SqpApplication::PlotsInteractionMode m_PlotInterractionMode;
103 103 SqpApplication::PlotsCursorMode m_PlotCursorMode;
104 104 };
105 105
106 106
107 107 SqpApplication::SqpApplication(int &argc, char **argv)
108 108 : QApplication{argc, argv}, impl{spimpl::make_unique_impl<SqpApplicationPrivate>()}
109 109 {
110 110 qCDebug(LOG_SqpApplication()) << tr("SqpApplication construction") << QThread::currentThread();
111 111
112 QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
113
112 114 connect(&impl->m_DataSourceControllerThread, &QThread::started,
113 115 impl->m_DataSourceController.get(), &DataSourceController::initialize);
114 116 connect(&impl->m_DataSourceControllerThread, &QThread::finished,
115 117 impl->m_DataSourceController.get(), &DataSourceController::finalize);
116 118
117 119 connect(&impl->m_NetworkControllerThread, &QThread::started, impl->m_NetworkController.get(),
118 120 &NetworkController::initialize);
119 121 connect(&impl->m_NetworkControllerThread, &QThread::finished, impl->m_NetworkController.get(),
120 122 &NetworkController::finalize);
121 123
122 124 connect(&impl->m_VariableControllerThread, &QThread::started, impl->m_VariableController.get(),
123 125 &VariableController::initialize);
124 126 connect(&impl->m_VariableControllerThread, &QThread::finished, impl->m_VariableController.get(),
125 127 &VariableController::finalize);
126 128
127 129 connect(&impl->m_VisualizationControllerThread, &QThread::started,
128 130 impl->m_VisualizationController.get(), &VisualizationController::initialize);
129 131 connect(&impl->m_VisualizationControllerThread, &QThread::finished,
130 132 impl->m_VisualizationController.get(), &VisualizationController::finalize);
131 133
132 134 impl->m_DataSourceControllerThread.start();
133 135 impl->m_NetworkControllerThread.start();
134 136 impl->m_VariableControllerThread.start();
135 137 impl->m_VisualizationControllerThread.start();
136 138 impl->m_CatalogueController->initialize();
137 139 }
138 140
139 141 SqpApplication::~SqpApplication()
140 142 {
141 143 }
142 144
143 145 void SqpApplication::initialize()
144 146 {
145 147 }
146 148
147 149 DataSourceController &SqpApplication::dataSourceController() noexcept
148 150 {
149 151 return *impl->m_DataSourceController;
150 152 }
151 153
152 154 NetworkController &SqpApplication::networkController() noexcept
153 155 {
154 156 return *impl->m_NetworkController;
155 157 }
156 158
157 159 TimeController &SqpApplication::timeController() noexcept
158 160 {
159 161 return *impl->m_TimeController;
160 162 }
161 163
162 164 VariableController &SqpApplication::variableController() noexcept
163 165 {
164 166 return *impl->m_VariableController;
165 167 }
166 168
167 169 VisualizationController &SqpApplication::visualizationController() noexcept
168 170 {
169 171 return *impl->m_VisualizationController;
170 172 }
171 173
172 174 CatalogueController &SqpApplication::catalogueController() noexcept
173 175 {
174 176 return *impl->m_CatalogueController;
175 177 }
176 178
177 179 DragDropGuiController &SqpApplication::dragDropGuiController() noexcept
178 180 {
179 181 return *impl->m_DragDropGuiController;
180 182 }
181 183
182 184 ActionsGuiController &SqpApplication::actionsGuiController() noexcept
183 185 {
184 186 return *impl->m_ActionsGuiController;
185 187 }
186 188
187 189 SqpApplication::PlotsInteractionMode SqpApplication::plotsInteractionMode() const
188 190 {
189 191 return impl->m_PlotInterractionMode;
190 192 }
191 193
192 194 void SqpApplication::setPlotsInteractionMode(SqpApplication::PlotsInteractionMode mode)
193 195 {
194 196 impl->m_PlotInterractionMode = mode;
195 197 }
196 198
197 199 SqpApplication::PlotsCursorMode SqpApplication::plotsCursorMode() const
198 200 {
199 201 return impl->m_PlotCursorMode;
200 202 }
201 203
202 204 void SqpApplication::setPlotsCursorMode(SqpApplication::PlotsCursorMode mode)
203 205 {
204 206 impl->m_PlotCursorMode = mode;
205 207 }
@@ -1,1054 +1,1053
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 bool m_VariableAutoRangeOnInit = true;
98 98
99 99 void startDrawingRect(const QPoint &pos, QCustomPlot &plot)
100 100 {
101 101 removeDrawingRect(plot);
102 102
103 103 auto axisPos = posToAxisPos(pos, plot);
104 104
105 105 m_DrawingZoomRect = new QCPItemRect{&plot};
106 106 QPen p;
107 107 p.setWidth(2);
108 108 m_DrawingZoomRect->setPen(p);
109 109
110 110 m_DrawingZoomRect->topLeft->setCoords(axisPos);
111 111 m_DrawingZoomRect->bottomRight->setCoords(axisPos);
112 112 }
113 113
114 114 void removeDrawingRect(QCustomPlot &plot)
115 115 {
116 116 if (m_DrawingZoomRect) {
117 117 plot.removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot
118 118 m_DrawingZoomRect = nullptr;
119 119 plot.replot(QCustomPlot::rpQueuedReplot);
120 120 }
121 121 }
122 122
123 123 void startDrawingZone(const QPoint &pos, VisualizationGraphWidget *graph)
124 124 {
125 125 endDrawingZone(graph);
126 126
127 127 auto axisPos = posToAxisPos(pos, graph->plot());
128 128
129 129 m_DrawingZone = new VisualizationSelectionZoneItem{&graph->plot()};
130 130 m_DrawingZone->setRange(axisPos.x(), axisPos.x());
131 131 m_DrawingZone->setEditionEnabled(false);
132 132 }
133 133
134 134 void endDrawingZone(VisualizationGraphWidget *graph)
135 135 {
136 136 if (m_DrawingZone) {
137 137 auto drawingZoneRange = m_DrawingZone->range();
138 138 if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0) {
139 139 m_DrawingZone->setEditionEnabled(true);
140 140 addSelectionZone(m_DrawingZone);
141 141 }
142 142 else {
143 143 graph->plot().removeItem(m_DrawingZone); // the item is deleted by QCustomPlot
144 144 }
145 145
146 146 graph->plot().replot(QCustomPlot::rpQueuedReplot);
147 147 m_DrawingZone = nullptr;
148 148 }
149 149 }
150 150
151 151 void setSelectionZonesEditionEnabled(bool value)
152 152 {
153 153 for (auto s : m_SelectionZones) {
154 154 s->setEditionEnabled(value);
155 155 }
156 156 }
157 157
158 158 void addSelectionZone(VisualizationSelectionZoneItem *zone) { m_SelectionZones << zone; }
159 159
160 160 VisualizationSelectionZoneItem *selectionZoneAt(const QPoint &pos,
161 161 const QCustomPlot &plot) const
162 162 {
163 163 VisualizationSelectionZoneItem *selectionZoneItemUnderCursor = nullptr;
164 164 auto minDistanceToZone = -1;
165 165 for (auto zone : m_SelectionZones) {
166 166 auto distanceToZone = zone->selectTest(pos, false);
167 167 if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone)
168 168 && distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
169 169 selectionZoneItemUnderCursor = zone;
170 170 }
171 171 }
172 172
173 173 return selectionZoneItemUnderCursor;
174 174 }
175 175
176 176 QVector<VisualizationSelectionZoneItem *> selectionZonesAt(const QPoint &pos,
177 177 const QCustomPlot &plot) const
178 178 {
179 179 QVector<VisualizationSelectionZoneItem *> zones;
180 180 for (auto zone : m_SelectionZones) {
181 181 auto distanceToZone = zone->selectTest(pos, false);
182 182 if (distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
183 183 zones << zone;
184 184 }
185 185 }
186 186
187 187 return zones;
188 188 }
189 189
190 190 void moveSelectionZoneOnTop(VisualizationSelectionZoneItem *zone, QCustomPlot &plot)
191 191 {
192 192 if (!m_SelectionZones.isEmpty() && m_SelectionZones.last() != zone) {
193 193 zone->moveToTop();
194 194 m_SelectionZones.removeAll(zone);
195 195 m_SelectionZones.append(zone);
196 196 }
197 197 }
198 198
199 199 QPointF posToAxisPos(const QPoint &pos, QCustomPlot &plot) const
200 200 {
201 201 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
202 202 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
203 203 return QPointF{axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y())};
204 204 }
205 205
206 206 bool pointIsInAxisRect(const QPointF &axisPoint, QCustomPlot &plot) const
207 207 {
208 208 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
209 209 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
210 210 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
211 211 }
212 212 };
213 213
214 214 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
215 215 : VisualizationDragWidget{parent},
216 216 ui{new Ui::VisualizationGraphWidget},
217 217 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
218 218 {
219 219 ui->setupUi(this);
220 220
221 221 // 'Close' options : widget is deleted when closed
222 222 setAttribute(Qt::WA_DeleteOnClose);
223 223
224 224 // Set qcpplot properties :
225 225 // - zoom is enabled
226 226 // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation
227 227 ui->widget->setInteractions(QCP::iRangeZoom);
228 228 ui->widget->axisRect()->setRangeDrag(Qt::Horizontal | Qt::Vertical);
229 229
230 230 // The delegate must be initialized after the ui as it uses the plot
231 231 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
232 232
233 233 // Init the cursors
234 234 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
235 235 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
236 236 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
237 237 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
238 238
239 239 connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress);
240 240 connect(ui->widget, &QCustomPlot::mouseRelease, this,
241 241 &VisualizationGraphWidget::onMouseRelease);
242 242 connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove);
243 243 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
244 244 connect(ui->widget, &QCustomPlot::mouseDoubleClick, this,
245 245 &VisualizationGraphWidget::onMouseDoubleClick);
246 246 connect(ui->widget->xAxis, static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(
247 247 &QCPAxis::rangeChanged),
248 248 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
249 249
250 250 // Activates menu when right clicking on the graph
251 251 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
252 252 connect(ui->widget, &QCustomPlot::customContextMenuRequested, this,
253 253 &VisualizationGraphWidget::onGraphMenuRequested);
254 254
255 255 connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
256 256 &VariableController::onRequestDataLoading);
257 257
258 258 connect(&sqpApp->variableController(), &VariableController::updateVarDisplaying, this,
259 259 &VisualizationGraphWidget::onUpdateVarDisplaying);
260 260
261 #ifdef Q_OS_MAC
261 // Necessary for all platform since Qt::AA_EnableHighDpiScaling is enable.
262 262 plot().setPlottingHint(QCP::phFastPolylines, true);
263 #endif
264 263 }
265 264
266 265
267 266 VisualizationGraphWidget::~VisualizationGraphWidget()
268 267 {
269 268 delete ui;
270 269 }
271 270
272 271 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
273 272 {
274 273 auto parent = parentWidget();
275 274 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
276 275 parent = parent->parentWidget();
277 276 }
278 277
279 278 return qobject_cast<VisualizationZoneWidget *>(parent);
280 279 }
281 280
282 281 VisualizationWidget *VisualizationGraphWidget::parentVisualizationWidget() const
283 282 {
284 283 auto parent = parentWidget();
285 284 while (parent != nullptr && !qobject_cast<VisualizationWidget *>(parent)) {
286 285 parent = parent->parentWidget();
287 286 }
288 287
289 288 return qobject_cast<VisualizationWidget *>(parent);
290 289 }
291 290
292 291 void VisualizationGraphWidget::setFlags(GraphFlags flags)
293 292 {
294 293 impl->m_Flags = std::move(flags);
295 294 }
296 295
297 296 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, SqpRange range)
298 297 {
299 298 /// Lambda used to set graph's units and range according to the variable passed in parameter
300 299 auto loadRange = [this](std::shared_ptr<Variable> variable, const SqpRange &range) {
301 300 impl->m_RenderingDelegate->setAxesUnits(*variable);
302 301
303 302 this->setFlags(GraphFlag::DisableAll);
304 303 setGraphRange(range);
305 304 this->setFlags(GraphFlag::EnableAll);
306 305 emit requestDataLoading({variable}, range, false);
307 306 };
308 307
309 308 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
310 309
311 310 // Calls update of graph's range and units when the data of the variable have been initialized.
312 311 // Note: we use QueuedConnection here as the update event must be called in the UI thread
313 312 connect(variable.get(), &Variable::dataInitialized, this,
314 313 [ varW = std::weak_ptr<Variable>{variable}, range, loadRange, this ]() {
315 314 if (auto var = varW.lock()) {
316 315 // If the variable is the first added in the graph, we load its range
317 316 auto firstVariableInGraph = range == INVALID_RANGE;
318 317 auto loadedRange = graphRange();
319 318 if (impl->m_VariableAutoRangeOnInit) {
320 319 loadedRange = firstVariableInGraph ? var->range() : range;
321 320 }
322 321 loadRange(var, loadedRange);
323 322 setYRange(var);
324 323 }
325 324 },
326 325 Qt::QueuedConnection);
327 326
328 327 // Uses delegate to create the qcpplot components according to the variable
329 328 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
330 329
331 330 // Sets graph properties
332 331 impl->m_RenderingDelegate->setGraphProperties(*variable, createdPlottables);
333 332
334 333 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
335 334
336 335 // If the variable already has its data loaded, load its units and its range in the graph
337 336 if (variable->dataSeries() != nullptr) {
338 337 loadRange(variable, range);
339 338 }
340 339
341 340 emit variableAdded(variable);
342 341 }
343 342
344 343 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
345 344 {
346 345 // Each component associated to the variable :
347 346 // - is removed from qcpplot (which deletes it)
348 347 // - is no longer referenced in the map
349 348 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
350 349 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
351 350 emit variableAboutToBeRemoved(variable);
352 351
353 352 auto &plottablesMap = variableIt->second;
354 353
355 354 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
356 355 plottableIt != plottableEnd;) {
357 356 ui->widget->removePlottable(plottableIt->second);
358 357 plottableIt = plottablesMap.erase(plottableIt);
359 358 }
360 359
361 360 impl->m_VariableToPlotMultiMap.erase(variableIt);
362 361 }
363 362
364 363 // Updates graph
365 364 ui->widget->replot();
366 365 }
367 366
368 367 QList<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
369 368 {
370 369 auto variables = QList<std::shared_ptr<Variable> >{};
371 370 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
372 371 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
373 372 variables << it->first;
374 373 }
375 374
376 375 return variables;
377 376 }
378 377
379 378 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
380 379 {
381 380 if (!variable) {
382 381 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
383 382 return;
384 383 }
385 384
386 385 VisualizationGraphHelper::setYAxisRange(variable, *ui->widget);
387 386 }
388 387
389 388 SqpRange VisualizationGraphWidget::graphRange() const noexcept
390 389 {
391 390 auto graphRange = ui->widget->xAxis->range();
392 391 return SqpRange{graphRange.lower, graphRange.upper};
393 392 }
394 393
395 394 void VisualizationGraphWidget::setGraphRange(const SqpRange &range, bool calibration)
396 395 {
397 396 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange START");
398 397
399 398 if (calibration) {
400 399 impl->m_IsCalibration = true;
401 400 }
402 401
403 402 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
404 403 ui->widget->replot();
405 404
406 405 if (calibration) {
407 406 impl->m_IsCalibration = false;
408 407 }
409 408
410 409 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange END");
411 410 }
412 411
413 412 void VisualizationGraphWidget::setAutoRangeOnVariableInitialization(bool value)
414 413 {
415 414 impl->m_VariableAutoRangeOnInit = value;
416 415 }
417 416
418 417 QVector<SqpRange> VisualizationGraphWidget::selectionZoneRanges() const
419 418 {
420 419 QVector<SqpRange> ranges;
421 420 for (auto zone : impl->m_SelectionZones) {
422 421 ranges << zone->range();
423 422 }
424 423
425 424 return ranges;
426 425 }
427 426
428 427 void VisualizationGraphWidget::addSelectionZones(const QVector<SqpRange> &ranges)
429 428 {
430 429 for (const auto &range : ranges) {
431 430 // note: ownership is transfered to QCustomPlot
432 431 auto zone = new VisualizationSelectionZoneItem(&plot());
433 432 zone->setRange(range.m_TStart, range.m_TEnd);
434 433 impl->addSelectionZone(zone);
435 434 }
436 435
437 436 plot().replot(QCustomPlot::rpQueuedReplot);
438 437 }
439 438
440 439 VisualizationSelectionZoneItem *VisualizationGraphWidget::addSelectionZone(const QString &name,
441 440 const SqpRange &range)
442 441 {
443 442 // note: ownership is transfered to QCustomPlot
444 443 auto zone = new VisualizationSelectionZoneItem(&plot());
445 444 zone->setName(name);
446 445 zone->setRange(range.m_TStart, range.m_TEnd);
447 446 impl->addSelectionZone(zone);
448 447
449 448 plot().replot(QCustomPlot::rpQueuedReplot);
450 449
451 450 return zone;
452 451 }
453 452
454 453 void VisualizationGraphWidget::removeSelectionZone(VisualizationSelectionZoneItem *selectionZone)
455 454 {
456 455 parentVisualizationWidget()->selectionZoneManager().setSelected(selectionZone, false);
457 456
458 457 if (impl->m_HoveredZone == selectionZone) {
459 458 impl->m_HoveredZone = nullptr;
460 459 setCursor(Qt::ArrowCursor);
461 460 }
462 461
463 462 impl->m_SelectionZones.removeAll(selectionZone);
464 463 plot().removeItem(selectionZone);
465 464 plot().replot(QCustomPlot::rpQueuedReplot);
466 465 }
467 466
468 467 void VisualizationGraphWidget::undoZoom()
469 468 {
470 469 auto zoom = impl->m_ZoomStack.pop();
471 470 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
472 471 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
473 472
474 473 axisX->setRange(zoom.first);
475 474 axisY->setRange(zoom.second);
476 475
477 476 plot().replot(QCustomPlot::rpQueuedReplot);
478 477 }
479 478
480 479 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
481 480 {
482 481 if (visitor) {
483 482 visitor->visit(this);
484 483 }
485 484 else {
486 485 qCCritical(LOG_VisualizationGraphWidget())
487 486 << tr("Can't visit widget : the visitor is null");
488 487 }
489 488 }
490 489
491 490 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
492 491 {
493 492 auto isSpectrogram = [](const auto &variable) {
494 493 return std::dynamic_pointer_cast<SpectrogramSeries>(variable.dataSeries()) != nullptr;
495 494 };
496 495
497 496 // - A spectrogram series can't be dropped on graph with existing plottables
498 497 // - No data series can be dropped on graph with existing spectrogram series
499 498 return isSpectrogram(variable)
500 499 ? impl->m_VariableToPlotMultiMap.empty()
501 500 : std::none_of(
502 501 impl->m_VariableToPlotMultiMap.cbegin(), impl->m_VariableToPlotMultiMap.cend(),
503 502 [isSpectrogram](const auto &entry) { return isSpectrogram(*entry.first); });
504 503 }
505 504
506 505 bool VisualizationGraphWidget::contains(const Variable &variable) const
507 506 {
508 507 // Finds the variable among the keys of the map
509 508 auto variablePtr = &variable;
510 509 auto findVariable
511 510 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
512 511
513 512 auto end = impl->m_VariableToPlotMultiMap.cend();
514 513 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
515 514 return it != end;
516 515 }
517 516
518 517 QString VisualizationGraphWidget::name() const
519 518 {
520 519 return impl->m_Name;
521 520 }
522 521
523 522 QMimeData *VisualizationGraphWidget::mimeData(const QPoint &position) const
524 523 {
525 524 auto mimeData = new QMimeData;
526 525
527 526 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position, plot());
528 527 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
529 528 && selectionZoneItemUnderCursor) {
530 529 mimeData->setData(MIME_TYPE_TIME_RANGE, TimeController::mimeDataForTimeRange(
531 530 selectionZoneItemUnderCursor->range()));
532 531 mimeData->setData(MIME_TYPE_SELECTION_ZONE, TimeController::mimeDataForTimeRange(
533 532 selectionZoneItemUnderCursor->range()));
534 533 }
535 534 else {
536 535 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
537 536
538 537 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
539 538 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
540 539 }
541 540
542 541 return mimeData;
543 542 }
544 543
545 544 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint &dragPosition)
546 545 {
547 546 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition, plot());
548 547 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
549 548 && selectionZoneItemUnderCursor) {
550 549
551 550 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
552 551 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
553 552
554 553 auto zoneSize = QSizeF{qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
555 554 qAbs(zoneBottomRight.y() - zoneTopLeft.y())}
556 555 .toSize();
557 556
558 557 auto pixmap = QPixmap(zoneSize);
559 558 render(&pixmap, QPoint(), QRegion{QRect{zoneTopLeft.toPoint(), zoneSize}});
560 559
561 560 return pixmap;
562 561 }
563 562
564 563 return QPixmap();
565 564 }
566 565
567 566 bool VisualizationGraphWidget::isDragAllowed() const
568 567 {
569 568 return true;
570 569 }
571 570
572 571 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
573 572 {
574 573 if (highlighted) {
575 574 plot().setBackground(QBrush(QColor("#BBD5EE")));
576 575 }
577 576 else {
578 577 plot().setBackground(QBrush(Qt::white));
579 578 }
580 579
581 580 plot().update();
582 581 }
583 582
584 583 void VisualizationGraphWidget::addVerticalCursor(double time)
585 584 {
586 585 impl->m_VerticalCursor->setPosition(time);
587 586 impl->m_VerticalCursor->setVisible(true);
588 587
589 588 auto text
590 589 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
591 590 impl->m_VerticalCursor->setLabelText(text);
592 591 }
593 592
594 593 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
595 594 {
596 595 impl->m_VerticalCursor->setAbsolutePosition(position);
597 596 impl->m_VerticalCursor->setVisible(true);
598 597
599 598 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
600 599 auto text
601 600 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
602 601 impl->m_VerticalCursor->setLabelText(text);
603 602 }
604 603
605 604 void VisualizationGraphWidget::removeVerticalCursor()
606 605 {
607 606 impl->m_VerticalCursor->setVisible(false);
608 607 plot().replot(QCustomPlot::rpQueuedReplot);
609 608 }
610 609
611 610 void VisualizationGraphWidget::addHorizontalCursor(double value)
612 611 {
613 612 impl->m_HorizontalCursor->setPosition(value);
614 613 impl->m_HorizontalCursor->setVisible(true);
615 614 impl->m_HorizontalCursor->setLabelText(QString::number(value));
616 615 }
617 616
618 617 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
619 618 {
620 619 impl->m_HorizontalCursor->setAbsolutePosition(position);
621 620 impl->m_HorizontalCursor->setVisible(true);
622 621
623 622 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
624 623 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
625 624 }
626 625
627 626 void VisualizationGraphWidget::removeHorizontalCursor()
628 627 {
629 628 impl->m_HorizontalCursor->setVisible(false);
630 629 plot().replot(QCustomPlot::rpQueuedReplot);
631 630 }
632 631
633 632 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
634 633 {
635 634 Q_UNUSED(event);
636 635
637 636 // Prevents that all variables will be removed from graph when it will be closed
638 637 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
639 638 emit variableAboutToBeRemoved(variableEntry.first);
640 639 }
641 640 }
642 641
643 642 void VisualizationGraphWidget::enterEvent(QEvent *event)
644 643 {
645 644 Q_UNUSED(event);
646 645 impl->m_RenderingDelegate->showGraphOverlay(true);
647 646 }
648 647
649 648 void VisualizationGraphWidget::leaveEvent(QEvent *event)
650 649 {
651 650 Q_UNUSED(event);
652 651 impl->m_RenderingDelegate->showGraphOverlay(false);
653 652
654 653 if (auto parentZone = parentZoneWidget()) {
655 654 parentZone->notifyMouseLeaveGraph(this);
656 655 }
657 656 else {
658 657 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
659 658 }
660 659
661 660 if (impl->m_HoveredZone) {
662 661 impl->m_HoveredZone->setHovered(false);
663 662 impl->m_HoveredZone = nullptr;
664 663 }
665 664 }
666 665
667 666 QCustomPlot &VisualizationGraphWidget::plot() const noexcept
668 667 {
669 668 return *ui->widget;
670 669 }
671 670
672 671 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
673 672 {
674 673 QMenu graphMenu{};
675 674
676 675 // Iterates on variables (unique keys)
677 676 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
678 677 end = impl->m_VariableToPlotMultiMap.cend();
679 678 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
680 679 // 'Remove variable' action
681 680 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
682 681 [ this, var = it->first ]() { removeVariable(var); });
683 682 }
684 683
685 684 if (!impl->m_ZoomStack.isEmpty()) {
686 685 if (!graphMenu.isEmpty()) {
687 686 graphMenu.addSeparator();
688 687 }
689 688
690 689 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
691 690 }
692 691
693 692 // Selection Zone Actions
694 693 auto selectionZoneItem = impl->selectionZoneAt(pos, plot());
695 694 if (selectionZoneItem) {
696 695 auto selectedItems = parentVisualizationWidget()->selectionZoneManager().selectedItems();
697 696 selectedItems.removeAll(selectionZoneItem);
698 697 selectedItems.prepend(selectionZoneItem); // Put the current selection zone first
699 698
700 699 auto zoneActions = sqpApp->actionsGuiController().selectionZoneActions();
701 700 if (!zoneActions.isEmpty() && !graphMenu.isEmpty()) {
702 701 graphMenu.addSeparator();
703 702 }
704 703
705 704 QHash<QString, QMenu *> subMenus;
706 705 QHash<QString, bool> subMenusEnabled;
707 706
708 707 for (auto zoneAction : zoneActions) {
709 708
710 709 auto isEnabled = zoneAction->isEnabled(selectedItems);
711 710
712 711 auto menu = &graphMenu;
713 712 for (auto subMenuName : zoneAction->subMenuList()) {
714 713 if (!subMenus.contains(subMenuName)) {
715 714 menu = menu->addMenu(subMenuName);
716 715 subMenus[subMenuName] = menu;
717 716 subMenusEnabled[subMenuName] = isEnabled;
718 717 }
719 718 else {
720 719 menu = subMenus.value(subMenuName);
721 720 if (isEnabled) {
722 721 // The sub menu is enabled if at least one of its actions is enabled
723 722 subMenusEnabled[subMenuName] = true;
724 723 }
725 724 }
726 725 }
727 726
728 727 auto action = menu->addAction(zoneAction->name());
729 728 action->setEnabled(isEnabled);
730 729 action->setShortcut(zoneAction->displayedShortcut());
731 730 QObject::connect(action, &QAction::triggered,
732 731 [zoneAction, selectedItems]() { zoneAction->execute(selectedItems); });
733 732 }
734 733
735 734 for (auto it = subMenus.cbegin(); it != subMenus.cend(); ++it) {
736 735 it.value()->setEnabled(subMenusEnabled[it.key()]);
737 736 }
738 737 }
739 738
740 739 if (!graphMenu.isEmpty()) {
741 740 graphMenu.exec(QCursor::pos());
742 741 }
743 742 }
744 743
745 744 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
746 745 {
747 746 qCDebug(LOG_VisualizationGraphWidget()) << tr("TORM: VisualizationGraphWidget::onRangeChanged")
748 747 << QThread::currentThread()->objectName() << "DoAcqui"
749 748 << impl->m_Flags.testFlag(GraphFlag::EnableAcquisition);
750 749
751 750 auto graphRange = SqpRange{t1.lower, t1.upper};
752 751 auto oldGraphRange = SqpRange{t2.lower, t2.upper};
753 752
754 753 if (impl->m_Flags.testFlag(GraphFlag::EnableAcquisition)) {
755 754 QVector<std::shared_ptr<Variable> > variableUnderGraphVector;
756 755
757 756 for (auto it = impl->m_VariableToPlotMultiMap.begin(),
758 757 end = impl->m_VariableToPlotMultiMap.end();
759 758 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
760 759 variableUnderGraphVector.push_back(it->first);
761 760 }
762 761 emit requestDataLoading(std::move(variableUnderGraphVector), graphRange,
763 762 !impl->m_IsCalibration);
764 763 }
765 764
766 765 if (impl->m_Flags.testFlag(GraphFlag::EnableSynchronization) && !impl->m_IsCalibration) {
767 766 qCDebug(LOG_VisualizationGraphWidget())
768 767 << tr("TORM: VisualizationGraphWidget::Synchronize notify !!")
769 768 << QThread::currentThread()->objectName() << graphRange << oldGraphRange;
770 769 emit synchronize(graphRange, oldGraphRange);
771 770 }
772 771
773 772 auto pos = mapFromGlobal(QCursor::pos());
774 773 auto axisPos = impl->posToAxisPos(pos, plot());
775 774 if (auto parentZone = parentZoneWidget()) {
776 775 if (impl->pointIsInAxisRect(axisPos, plot())) {
777 776 parentZone->notifyMouseMoveInGraph(pos, axisPos, this);
778 777 }
779 778 else {
780 779 parentZone->notifyMouseLeaveGraph(this);
781 780 }
782 781 }
783 782 else {
784 783 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
785 784 }
786 785
787 786 // Quits calibration
788 787 impl->m_IsCalibration = false;
789 788 }
790 789
791 790 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept
792 791 {
793 792 impl->m_RenderingDelegate->onMouseDoubleClick(event);
794 793 }
795 794
796 795 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
797 796 {
798 797 // Handles plot rendering when mouse is moving
799 798 impl->m_RenderingDelegate->onMouseMove(event);
800 799
801 800 auto axisPos = impl->posToAxisPos(event->pos(), plot());
802 801
803 802 // Zoom box and zone drawing
804 803 if (impl->m_DrawingZoomRect) {
805 804 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
806 805 }
807 806 else if (impl->m_DrawingZone) {
808 807 impl->m_DrawingZone->setEnd(axisPos.x());
809 808 }
810 809
811 810 // Cursor
812 811 if (auto parentZone = parentZoneWidget()) {
813 812 if (impl->pointIsInAxisRect(axisPos, plot())) {
814 813 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
815 814 }
816 815 else {
817 816 parentZone->notifyMouseLeaveGraph(this);
818 817 }
819 818 }
820 819 else {
821 820 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
822 821 }
823 822
824 823 // Search for the selection zone under the mouse
825 824 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
826 825 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
827 826 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) {
828 827
829 828 // Sets the appropriate cursor shape
830 829 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
831 830 setCursor(cursorShape);
832 831
833 832 // Manages the hovered zone
834 833 if (selectionZoneItemUnderCursor != impl->m_HoveredZone) {
835 834 if (impl->m_HoveredZone) {
836 835 impl->m_HoveredZone->setHovered(false);
837 836 }
838 837 selectionZoneItemUnderCursor->setHovered(true);
839 838 impl->m_HoveredZone = selectionZoneItemUnderCursor;
840 839 plot().replot(QCustomPlot::rpQueuedReplot);
841 840 }
842 841 }
843 842 else {
844 843 // There is no zone under the mouse or the interaction mode is not "selection zones"
845 844 if (impl->m_HoveredZone) {
846 845 impl->m_HoveredZone->setHovered(false);
847 846 impl->m_HoveredZone = nullptr;
848 847 }
849 848
850 849 setCursor(Qt::ArrowCursor);
851 850 }
852 851
853 852 impl->m_HasMovedMouse = true;
854 853 VisualizationDragWidget::mouseMoveEvent(event);
855 854 }
856 855
857 856 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
858 857 {
859 858 auto value = event->angleDelta().x() + event->angleDelta().y();
860 859 if (value != 0) {
861 860
862 861 auto direction = value > 0 ? 1.0 : -1.0;
863 862 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
864 863 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
865 864 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
866 865
867 866 auto zoomOrientations = QFlags<Qt::Orientation>{};
868 867 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
869 868 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
870 869
871 870 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
872 871
873 872 if (!isZoomX && !isZoomY) {
874 873 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
875 874 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
876 875
877 876 axis->setRange(axis->range() + diff);
878 877
879 878 if (plot().noAntialiasingOnDrag()) {
880 879 plot().setNotAntialiasedElements(QCP::aeAll);
881 880 }
882 881
883 882 plot().replot(QCustomPlot::rpQueuedReplot);
884 883 }
885 884 }
886 885 }
887 886
888 887 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
889 888 {
890 889 auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
891 890 auto isSelectionZoneMode
892 891 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
893 892 auto isLeftClick = event->buttons().testFlag(Qt::LeftButton);
894 893
895 894 if (!isDragDropClick && isLeftClick) {
896 895 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) {
897 896 // Starts a zoom box
898 897 impl->startDrawingRect(event->pos(), plot());
899 898 }
900 899 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr) {
901 900 // Starts a new selection zone
902 901 auto zoneAtPos = impl->selectionZoneAt(event->pos(), plot());
903 902 if (!zoneAtPos) {
904 903 impl->startDrawingZone(event->pos(), this);
905 904 }
906 905 }
907 906 }
908 907
909 908 // Allows mouse panning only in default mode
910 909 plot().setInteraction(QCP::iRangeDrag, sqpApp->plotsInteractionMode()
911 910 == SqpApplication::PlotsInteractionMode::None
912 911 && !isDragDropClick);
913 912
914 913 // Allows zone edition only in selection zone mode without drag&drop
915 914 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
916 915
917 916 // Selection / Deselection
918 917 if (isSelectionZoneMode) {
919 918 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
920 919 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
921 920
922 921
923 922 if (selectionZoneItemUnderCursor && !selectionZoneItemUnderCursor->selected()
924 923 && !isMultiSelectionClick) {
925 924 parentVisualizationWidget()->selectionZoneManager().select(
926 925 {selectionZoneItemUnderCursor});
927 926 }
928 927 else if (!selectionZoneItemUnderCursor && !isMultiSelectionClick && isLeftClick) {
929 928 parentVisualizationWidget()->selectionZoneManager().clearSelection();
930 929 }
931 930 else {
932 931 // No selection change
933 932 }
934 933
935 934 if (selectionZoneItemUnderCursor && isLeftClick) {
936 935 selectionZoneItemUnderCursor->setAssociatedEditedZones(
937 936 parentVisualizationWidget()->selectionZoneManager().selectedItems());
938 937 }
939 938 }
940 939
941 940
942 941 impl->m_HasMovedMouse = false;
943 942 VisualizationDragWidget::mousePressEvent(event);
944 943 }
945 944
946 945 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
947 946 {
948 947 if (impl->m_DrawingZoomRect) {
949 948
950 949 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
951 950 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
952 951
953 952 auto newAxisXRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().x(),
954 953 impl->m_DrawingZoomRect->bottomRight->coords().x()};
955 954
956 955 auto newAxisYRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().y(),
957 956 impl->m_DrawingZoomRect->bottomRight->coords().y()};
958 957
959 958 impl->removeDrawingRect(plot());
960 959
961 960 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
962 961 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
963 962 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
964 963 axisX->setRange(newAxisXRange);
965 964 axisY->setRange(newAxisYRange);
966 965
967 966 plot().replot(QCustomPlot::rpQueuedReplot);
968 967 }
969 968 }
970 969
971 970 impl->endDrawingZone(this);
972 971
973 972 // Selection / Deselection
974 973 auto isSelectionZoneMode
975 974 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
976 975 if (isSelectionZoneMode) {
977 976 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
978 977 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
979 978 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton
980 979 && !impl->m_HasMovedMouse) {
981 980
982 981 auto zonesUnderCursor = impl->selectionZonesAt(event->pos(), plot());
983 982 if (zonesUnderCursor.count() > 1) {
984 983 // There are multiple zones under the mouse.
985 984 // Performs the selection with a selection dialog.
986 985 VisualizationMultiZoneSelectionDialog dialog{this};
987 986 dialog.setZones(zonesUnderCursor);
988 987 dialog.move(mapToGlobal(event->pos() - QPoint(dialog.width() / 2, 20)));
989 988 dialog.activateWindow();
990 989 dialog.raise();
991 990 if (dialog.exec() == QDialog::Accepted) {
992 991 auto selection = dialog.selectedZones();
993 992
994 993 if (!isMultiSelectionClick) {
995 994 parentVisualizationWidget()->selectionZoneManager().clearSelection();
996 995 }
997 996
998 997 for (auto it = selection.cbegin(); it != selection.cend(); ++it) {
999 998 auto zone = it.key();
1000 999 auto isSelected = it.value();
1001 1000 parentVisualizationWidget()->selectionZoneManager().setSelected(zone,
1002 1001 isSelected);
1003 1002
1004 1003 if (isSelected) {
1005 1004 // Puts the zone on top of the stack so it can be moved or resized
1006 1005 impl->moveSelectionZoneOnTop(zone, plot());
1007 1006 }
1008 1007 }
1009 1008 }
1010 1009 }
1011 1010 else {
1012 1011 if (!isMultiSelectionClick) {
1013 1012 parentVisualizationWidget()->selectionZoneManager().select(
1014 1013 {selectionZoneItemUnderCursor});
1015 1014 impl->moveSelectionZoneOnTop(selectionZoneItemUnderCursor, plot());
1016 1015 }
1017 1016 else {
1018 1017 parentVisualizationWidget()->selectionZoneManager().setSelected(
1019 1018 selectionZoneItemUnderCursor, !selectionZoneItemUnderCursor->selected()
1020 1019 || event->button() == Qt::RightButton);
1021 1020 }
1022 1021 }
1023 1022 }
1024 1023 else {
1025 1024 // No selection change
1026 1025 }
1027 1026 }
1028 1027 }
1029 1028
1030 1029 void VisualizationGraphWidget::onDataCacheVariableUpdated()
1031 1030 {
1032 1031 auto graphRange = ui->widget->xAxis->range();
1033 1032 auto dateTime = SqpRange{graphRange.lower, graphRange.upper};
1034 1033
1035 1034 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
1036 1035 auto variable = variableEntry.first;
1037 1036 qCDebug(LOG_VisualizationGraphWidget())
1038 1037 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
1039 1038 qCDebug(LOG_VisualizationGraphWidget())
1040 1039 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
1041 1040 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
1042 1041 impl->updateData(variableEntry.second, variable, variable->range());
1043 1042 }
1044 1043 }
1045 1044 }
1046 1045
1047 1046 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
1048 1047 const SqpRange &range)
1049 1048 {
1050 1049 auto it = impl->m_VariableToPlotMultiMap.find(variable);
1051 1050 if (it != impl->m_VariableToPlotMultiMap.end()) {
1052 1051 impl->updateData(it->second, variable, range);
1053 1052 }
1054 1053 }
General Comments 0
You need to be logged in to leave comments. Login now