##// END OF EJS Templates
Merge branch 'feature/GraphTooltip' into develop
Alexandre Leroux -
r486:7eb12825208d merge
parent child
Show More
@@ -0,0 +1,20
1 #ifndef SCIQLOP_VISUALIZATIONGRAPHRENDERINGDELEGATE_H
2 #define SCIQLOP_VISUALIZATIONGRAPHRENDERINGDELEGATE_H
3
4 #include <Common/spimpl.h>
5
6 class QCustomPlot;
7 class QMouseEvent;
8
9 class VisualizationGraphRenderingDelegate {
10 public:
11 explicit VisualizationGraphRenderingDelegate(QCustomPlot &plot);
12
13 void onMouseMove(QMouseEvent *event) noexcept;
14
15 private:
16 class VisualizationGraphRenderingDelegatePrivate;
17 spimpl::unique_impl_ptr<VisualizationGraphRenderingDelegatePrivate> impl;
18 };
19
20 #endif // SCIQLOP_VISUALIZATIONGRAPHRENDERINGDELEGATE_H
@@ -0,0 +1,118
1 #include "Visualization/VisualizationGraphRenderingDelegate.h"
2 #include "Visualization/qcustomplot.h"
3
4 namespace {
5
6 const auto DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd hh:mm:ss:zzz");
7
8 const auto TEXT_TRACER_FORMAT = QStringLiteral("key: %1\nvalue: %2");
9
10 /// Timeout after which a tracer is displayed
11 const auto TRACER_TIMEOUT = 500;
12
13 /// Formats a data value according to the axis on which it is present
14 QString formatValue(double value, const QCPAxis &axis)
15 {
16 // If the axis is a time axis, formats the value as a date
17 return qSharedPointerDynamicCast<QCPAxisTickerDateTime>(axis.ticker())
18 ? QCPAxisTickerDateTime::keyToDateTime(value).toString(DATETIME_FORMAT)
19 : QString::number(value);
20 }
21
22 void initPointTracerStyle(QCPItemTracer &tracer) noexcept
23 {
24 tracer.setInterpolating(false);
25 tracer.setStyle(QCPItemTracer::tsPlus);
26 tracer.setPen(QPen(Qt::black));
27 tracer.setBrush(Qt::black);
28 tracer.setSize(10);
29 }
30
31 void initTextTracerStyle(QCPItemText &tracer) noexcept
32 {
33 tracer.setPen(QPen{Qt::gray});
34 tracer.setBrush(Qt::white);
35 tracer.setPadding(QMargins{6, 6, 6, 6});
36 tracer.setPositionAlignment(Qt::AlignTop | Qt::AlignLeft);
37 tracer.setTextAlignment(Qt::AlignLeft);
38 }
39
40 } // namespace
41
42 struct VisualizationGraphRenderingDelegate::VisualizationGraphRenderingDelegatePrivate {
43 explicit VisualizationGraphRenderingDelegatePrivate(QCustomPlot &plot)
44 : m_Plot{plot},
45 m_PointTracer{new QCPItemTracer{&plot}},
46 m_TextTracer{new QCPItemText{&plot}},
47 m_TracerTimer{}
48 {
49 initPointTracerStyle(*m_PointTracer);
50 initTextTracerStyle(*m_TextTracer);
51
52 m_TracerTimer.setInterval(TRACER_TIMEOUT);
53 m_TracerTimer.setSingleShot(true);
54 }
55
56 QCustomPlot &m_Plot;
57 QCPItemTracer *m_PointTracer;
58 QCPItemText *m_TextTracer;
59 QTimer m_TracerTimer;
60 };
61
62 VisualizationGraphRenderingDelegate::VisualizationGraphRenderingDelegate(QCustomPlot &plot)
63 : impl{spimpl::make_unique_impl<VisualizationGraphRenderingDelegatePrivate>(plot)}
64 {
65 }
66
67 void VisualizationGraphRenderingDelegate::onMouseMove(QMouseEvent *event) noexcept
68 {
69 // Cancels pending refresh
70 impl->m_TracerTimer.disconnect();
71
72 auto showTracers = [ eventPos = event->pos(), this ]()
73 {
74 // Lambda function to display a tracer
75 auto displayTracer = [this](auto &tracer) {
76 // Tracer is set on top of the plot's main layer
77 tracer.setLayer(impl->m_Plot.layer("main"));
78 tracer.setVisible(true);
79 impl->m_Plot.replot();
80 };
81
82 // Reinits tracers
83 impl->m_PointTracer->setGraph(nullptr);
84 impl->m_PointTracer->setVisible(false);
85 impl->m_TextTracer->setVisible(false);
86 impl->m_Plot.replot();
87
88 // Gets the graph under the mouse position
89 if (auto graph = qobject_cast<QCPGraph *>(impl->m_Plot.plottableAt(eventPos))) {
90 auto mouseKey = graph->keyAxis()->pixelToCoord(eventPos.x());
91 auto graphData = graph->data();
92
93 // Gets the closest data point to the mouse
94 auto graphDataIt = graphData->findBegin(mouseKey);
95 if (graphDataIt != graphData->constEnd()) {
96 auto key = formatValue(graphDataIt->key, *graph->keyAxis());
97 auto value = formatValue(graphDataIt->value, *graph->valueAxis());
98 impl->m_TextTracer->setText(TEXT_TRACER_FORMAT.arg(key, value));
99
100 // Displays point tracer
101 impl->m_PointTracer->setGraph(graph);
102 impl->m_PointTracer->setGraphKey(mouseKey);
103 displayTracer(*impl->m_PointTracer);
104
105 // Displays text tracer
106 auto tracerPosition = impl->m_TextTracer->position;
107 tracerPosition->setAxes(graph->keyAxis(), graph->valueAxis());
108 tracerPosition->setCoords(impl->m_PointTracer->position->key(),
109 impl->m_PointTracer->position->value());
110 displayTracer(*impl->m_TextTracer);
111 }
112 }
113 };
114
115 // Starts the timer to display tracers at timeout
116 QObject::connect(&impl->m_TracerTimer, &QTimer::timeout, showTracers);
117 impl->m_TracerTimer.start();
118 }
@@ -1,82 +1,84
1 1 #ifndef SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
2 2 #define SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
3 3
4 4 #include "Visualization/IVisualizationWidget.h"
5 5
6 6 #include <QLoggingCategory>
7 7 #include <QWidget>
8 8
9 9 #include <memory>
10 10
11 11 #include <Common/spimpl.h>
12 12
13 13 Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationGraphWidget)
14 14
15 15 class QCPRange;
16 16 class SqpDateTime;
17 17 class Variable;
18 18
19 19 /**
20 20 * Possible types of zoom operation
21 21 */
22 22 enum class VisualizationGraphWidgetZoomType { ZoomOut, ZoomIn, PanRight, PanLeft, Unknown };
23 23
24 24 namespace Ui {
25 25 class VisualizationGraphWidget;
26 26 } // namespace Ui
27 27
28 28 class VisualizationGraphWidget : public QWidget, public IVisualizationWidget {
29 29 Q_OBJECT
30 30
31 31 public:
32 32 explicit VisualizationGraphWidget(const QString &name = {}, QWidget *parent = 0);
33 33 virtual ~VisualizationGraphWidget();
34 34
35 35 void enableSynchronize(bool enable);
36 36
37 37 void addVariable(std::shared_ptr<Variable> variable);
38 38 void addVariableUsingGraph(std::shared_ptr<Variable> variable);
39 39 /// Removes a variable from the graph
40 40 void removeVariable(std::shared_ptr<Variable> variable) noexcept;
41 41
42 42 void setRange(std::shared_ptr<Variable> variable, const SqpDateTime &range);
43 43 SqpDateTime graphRange() const noexcept;
44 44 void setGraphRange(const SqpDateTime &range);
45 45
46 46 // IVisualizationWidget interface
47 47 void accept(IVisualizationWidgetVisitor *visitor) override;
48 48 bool canDrop(const Variable &variable) const override;
49 49 bool contains(const Variable &variable) const override;
50 50 QString name() const override;
51 51
52 52
53 53 signals:
54 54 void requestDataLoading(std::shared_ptr<Variable> variable, const SqpDateTime &dateTime);
55 55 void synchronize(const SqpDateTime &dateTime, const SqpDateTime &oldDateTime,
56 56 VisualizationGraphWidgetZoomType zoomType);
57 57
58 58
59 59 private:
60 60 Ui::VisualizationGraphWidget *ui;
61 61
62 62 class VisualizationGraphWidgetPrivate;
63 63 spimpl::unique_impl_ptr<VisualizationGraphWidgetPrivate> impl;
64 64
65 65 private slots:
66 66 /// Slot called when right clicking on the graph (displays a menu)
67 67 void onGraphMenuRequested(const QPoint &pos) noexcept;
68 68
69 69 /// Rescale the X axe to range parameter
70 70 void onRangeChanged(const QCPRange &t1, const QCPRange &t2);
71 71
72 /// Slot called when a mouse move was made
73 void onMouseMove(QMouseEvent *event) noexcept;
72 74 /// Slot called when a mouse wheel was made, to perform some processing before the zoom is done
73 75 void onMouseWheel(QWheelEvent *event) noexcept;
74 76 /// Slot called when a mouse press was made, to activate the calibration of a graph
75 77 void onMousePress(QMouseEvent *event) noexcept;
76 78 /// Slot called when a mouse release was made, to deactivate the calibration of a graph
77 79 void onMouseRelease(QMouseEvent *event) noexcept;
78 80
79 81 void onDataCacheVariableUpdated();
80 82 };
81 83
82 84 #endif // SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
@@ -1,412 +1,428
1 1 #include "Visualization/VisualizationGraphWidget.h"
2 2 #include "Visualization/IVisualizationWidgetVisitor.h"
3 3 #include "Visualization/VisualizationGraphHelper.h"
4 #include "Visualization/VisualizationGraphRenderingDelegate.h"
4 5 #include "ui_VisualizationGraphWidget.h"
5 6
6 7 #include <Data/ArrayData.h>
7 8 #include <Data/IDataSeries.h>
8 9 #include <Settings/SqpSettingsDefs.h>
9 10 #include <SqpApplication.h>
10 11 #include <Variable/Variable.h>
11 12 #include <Variable/VariableController.h>
12 13
13 14 #include <unordered_map>
14 15
15 16 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
16 17
17 18 namespace {
18 19
19 20 /// Key pressed to enable zoom on horizontal axis
20 21 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::NoModifier;
21 22
22 23 /// Key pressed to enable zoom on vertical axis
23 24 const auto VERTICAL_ZOOM_MODIFIER = Qt::ControlModifier;
24 25
25 26 /// Gets a tolerance value from application settings. If the setting can't be found, the default
26 27 /// value passed in parameter is returned
27 28 double toleranceValue(const QString &key, double defaultValue) noexcept
28 29 {
29 30 return QSettings{}.value(key, defaultValue).toDouble();
30 31 }
31 32
32 33 } // namespace
33 34
34 35 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
35 36
36 explicit VisualizationGraphWidgetPrivate() : m_DoSynchronize{true}, m_IsCalibration{false} {}
37
37 explicit VisualizationGraphWidgetPrivate()
38 : m_DoSynchronize{true}, m_IsCalibration{false}, m_RenderingDelegate{nullptr}
39 {
40 }
38 41
39 42 // Return the operation when range changed
40 43 VisualizationGraphWidgetZoomType getZoomType(const QCPRange &t1, const QCPRange &t2);
41 44
42 45 // 1 variable -> n qcpplot
43 46 std::multimap<std::shared_ptr<Variable>, QCPAbstractPlottable *> m_VariableToPlotMultiMap;
44
45 47 bool m_DoSynchronize;
46 48 bool m_IsCalibration;
49 QCPItemTracer *m_TextTracer;
50 /// Delegate used to attach rendering features to the plot
51 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
47 52 };
48 53
49 54 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
50 55 : QWidget{parent},
51 56 ui{new Ui::VisualizationGraphWidget},
52 57 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>()}
53 58 {
54 59 ui->setupUi(this);
55 60
61 // The delegate must be initialized after the ui as it uses the plot
62 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*ui->widget);
63
56 64 ui->graphNameLabel->setText(name);
57 65
58 66 // 'Close' options : widget is deleted when closed
59 67 setAttribute(Qt::WA_DeleteOnClose);
60 68 connect(ui->closeButton, &QToolButton::clicked, this, &VisualizationGraphWidget::close);
61 69 ui->closeButton->setIcon(sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton));
62 70
63 71 // Set qcpplot properties :
64 72 // - Drag (on x-axis) and zoom are enabled
65 73 // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation
66 74 ui->widget->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom);
67 75 ui->widget->axisRect()->setRangeDrag(Qt::Horizontal);
76
68 77 connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress);
69 78 connect(ui->widget, &QCustomPlot::mouseRelease, this,
70 79 &VisualizationGraphWidget::onMouseRelease);
80 connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove);
71 81 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
72 82 connect(ui->widget->xAxis, static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(
73 83 &QCPAxis::rangeChanged),
74 84 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
75 85
76 86 // Activates menu when right clicking on the graph
77 87 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
78 88 connect(ui->widget, &QCustomPlot::customContextMenuRequested, this,
79 89 &VisualizationGraphWidget::onGraphMenuRequested);
80 90
81 91 connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
82 92 &VariableController::onRequestDataLoading);
83 93 }
84 94
85 95
86 96 VisualizationGraphWidget::~VisualizationGraphWidget()
87 97 {
88 98 delete ui;
89 99 }
90 100
91 101 void VisualizationGraphWidget::enableSynchronize(bool enable)
92 102 {
93 103 impl->m_DoSynchronize = enable;
94 104 }
95 105
96 106 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable)
97 107 {
98 108 // Uses delegate to create the qcpplot components according to the variable
99 109 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
100 110
101 111 for (auto createdPlottable : qAsConst(createdPlottables)) {
102 112 impl->m_VariableToPlotMultiMap.insert({variable, createdPlottable});
103 113 }
104 114
105 115 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
106 116 }
107 117 void VisualizationGraphWidget::addVariableUsingGraph(std::shared_ptr<Variable> variable)
108 118 {
109 119
110 120 // when adding a variable, we need to set its time range to the current graph range
111 121 auto grapheRange = ui->widget->xAxis->range();
112 122 auto dateTime = SqpDateTime{grapheRange.lower, grapheRange.upper};
113 123 variable->setDateTime(dateTime);
114 124
115 125 auto variableDateTimeWithTolerance = dateTime;
116 126
117 127 // add tolerance for each side
118 128 auto toleranceFactor
119 129 = toleranceValue(GENERAL_TOLERANCE_AT_INIT_KEY, GENERAL_TOLERANCE_AT_INIT_DEFAULT_VALUE);
120 130 auto tolerance = toleranceFactor * (dateTime.m_TEnd - dateTime.m_TStart);
121 131 variableDateTimeWithTolerance.m_TStart -= tolerance;
122 132 variableDateTimeWithTolerance.m_TEnd += tolerance;
123 133
124 134 // Uses delegate to create the qcpplot components according to the variable
125 135 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
126 136
127 137 for (auto createdPlottable : qAsConst(createdPlottables)) {
128 138 impl->m_VariableToPlotMultiMap.insert({variable, createdPlottable});
129 139 }
130 140
131 141 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
132 142
133 143 // CHangement detected, we need to ask controller to request data loading
134 144 emit requestDataLoading(variable, variableDateTimeWithTolerance);
135 145 }
136 146
137 147 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
138 148 {
139 149 // Each component associated to the variable :
140 150 // - is removed from qcpplot (which deletes it)
141 151 // - is no longer referenced in the map
142 152 auto componentsIt = impl->m_VariableToPlotMultiMap.equal_range(variable);
143 153 for (auto it = componentsIt.first; it != componentsIt.second;) {
144 154 ui->widget->removePlottable(it->second);
145 155 it = impl->m_VariableToPlotMultiMap.erase(it);
146 156 }
147 157
148 158 // Updates graph
149 159 ui->widget->replot();
150 160 }
151 161
152 162 void VisualizationGraphWidget::setRange(std::shared_ptr<Variable> variable,
153 163 const SqpDateTime &range)
154 164 {
155 165 // Note: in case of different axes that depends on variable, we could start with a code like
156 166 // that:
157 167 // auto componentsIt = impl->m_VariableToPlotMultiMap.equal_range(variable);
158 168 // for (auto it = componentsIt.first; it != componentsIt.second;) {
159 169 // }
160 170 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
161 171 ui->widget->replot();
162 172 }
163 173
164 174 SqpDateTime VisualizationGraphWidget::graphRange() const noexcept
165 175 {
166 176 auto grapheRange = ui->widget->xAxis->range();
167 177 return SqpDateTime{grapheRange.lower, grapheRange.upper};
168 178 }
169 179
170 180 void VisualizationGraphWidget::setGraphRange(const SqpDateTime &range)
171 181 {
172 182 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange START");
173 183 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
174 184 ui->widget->replot();
175 185 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange END");
176 186 }
177 187
178 188 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
179 189 {
180 190 if (visitor) {
181 191 visitor->visit(this);
182 192 }
183 193 else {
184 194 qCCritical(LOG_VisualizationGraphWidget())
185 195 << tr("Can't visit widget : the visitor is null");
186 196 }
187 197 }
188 198
189 199 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
190 200 {
191 201 /// @todo : for the moment, a graph can always accomodate a variable
192 202 Q_UNUSED(variable);
193 203 return true;
194 204 }
195 205
196 206 bool VisualizationGraphWidget::contains(const Variable &variable) const
197 207 {
198 208 // Finds the variable among the keys of the map
199 209 auto variablePtr = &variable;
200 210 auto findVariable
201 211 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
202 212
203 213 auto end = impl->m_VariableToPlotMultiMap.cend();
204 214 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
205 215 return it != end;
206 216 }
207 217
208 218 QString VisualizationGraphWidget::name() const
209 219 {
210 220 return ui->graphNameLabel->text();
211 221 }
212 222
213 223 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
214 224 {
215 225 QMenu graphMenu{};
216 226
217 227 // Iterates on variables (unique keys)
218 228 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
219 229 end = impl->m_VariableToPlotMultiMap.cend();
220 230 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
221 231 // 'Remove variable' action
222 232 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
223 233 [ this, var = it->first ]() { removeVariable(var); });
224 234 }
225 235
226 236 if (!graphMenu.isEmpty()) {
227 237 graphMenu.exec(mapToGlobal(pos));
228 238 }
229 239 }
230 240
231 241 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
232 242 {
233 243 qCInfo(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::onRangeChanged")
234 244 << QThread::currentThread()->objectName();
235 245
236 246 auto dateTimeRange = SqpDateTime{t1.lower, t1.upper};
237 247
238 248 auto zoomType = impl->getZoomType(t1, t2);
239 249 for (auto it = impl->m_VariableToPlotMultiMap.cbegin();
240 250 it != impl->m_VariableToPlotMultiMap.cend(); ++it) {
241 251
242 252 auto variable = it->first;
243 253 auto currentDateTime = dateTimeRange;
244 254
245 255 auto toleranceFactor = toleranceValue(GENERAL_TOLERANCE_AT_UPDATE_KEY,
246 256 GENERAL_TOLERANCE_AT_UPDATE_DEFAULT_VALUE);
247 257 auto tolerance = toleranceFactor * (currentDateTime.m_TEnd - currentDateTime.m_TStart);
248 258 auto variableDateTimeWithTolerance = currentDateTime;
249 259 variableDateTimeWithTolerance.m_TStart -= tolerance;
250 260 variableDateTimeWithTolerance.m_TEnd += tolerance;
251 261
252 262 qCDebug(LOG_VisualizationGraphWidget()) << "r" << currentDateTime;
253 263 qCDebug(LOG_VisualizationGraphWidget()) << "t" << variableDateTimeWithTolerance;
254 264 qCDebug(LOG_VisualizationGraphWidget()) << "v" << variable->dateTime();
255 265 // If new range with tol is upper than variable datetime parameters. we need to request new
256 266 // data
257 267 if (!variable->contains(variableDateTimeWithTolerance)) {
258 268
259 269 auto variableDateTimeWithTolerance = currentDateTime;
260 270 if (!variable->isInside(currentDateTime)) {
261 271 auto variableDateTime = variable->dateTime();
262 272 if (variable->contains(variableDateTimeWithTolerance)) {
263 273 qCDebug(LOG_VisualizationGraphWidget())
264 274 << tr("TORM: Detection zoom in that need request:");
265 275 // add tolerance for each side
266 276 tolerance
267 277 = toleranceFactor * (currentDateTime.m_TEnd - currentDateTime.m_TStart);
268 278 variableDateTimeWithTolerance.m_TStart -= tolerance;
269 279 variableDateTimeWithTolerance.m_TEnd += tolerance;
270 280 }
271 281 else if (variableDateTime.m_TStart < currentDateTime.m_TStart) {
272 282 qCInfo(LOG_VisualizationGraphWidget()) << tr("TORM: Detection pan to right:");
273 283
274 284 auto diffEndToKeepDelta = currentDateTime.m_TEnd - variableDateTime.m_TEnd;
275 285 currentDateTime.m_TStart = variableDateTime.m_TStart + diffEndToKeepDelta;
276 286 // Tolerance have to be added to the right
277 287 // add tolerance for right (end) side
278 288 tolerance
279 289 = toleranceFactor * (currentDateTime.m_TEnd - currentDateTime.m_TStart);
280 290 variableDateTimeWithTolerance.m_TEnd += tolerance;
281 291 }
282 292 else if (variableDateTime.m_TEnd > currentDateTime.m_TEnd) {
283 293 qCDebug(LOG_VisualizationGraphWidget()) << tr("TORM: Detection pan to left: ");
284 294 auto diffStartToKeepDelta
285 295 = variableDateTime.m_TStart - currentDateTime.m_TStart;
286 296 currentDateTime.m_TEnd = variableDateTime.m_TEnd - diffStartToKeepDelta;
287 297 // Tolerance have to be added to the left
288 298 // add tolerance for left (start) side
289 299 tolerance
290 300 = toleranceFactor * (currentDateTime.m_TEnd - currentDateTime.m_TStart);
291 301 variableDateTimeWithTolerance.m_TStart -= tolerance;
292 302 }
293 303 else {
294 304 qCCritical(LOG_VisualizationGraphWidget())
295 305 << tr("Detection anormal zoom detection: ");
296 306 }
297 307 }
298 308 else {
299 309 qCDebug(LOG_VisualizationGraphWidget()) << tr("TORM: Detection zoom out: ");
300 310 // add tolerance for each side
301 311 tolerance = toleranceFactor * (currentDateTime.m_TEnd - currentDateTime.m_TStart);
302 312 variableDateTimeWithTolerance.m_TStart -= tolerance;
303 313 variableDateTimeWithTolerance.m_TEnd += tolerance;
304 314 zoomType = VisualizationGraphWidgetZoomType::ZoomOut;
305 315 }
306 316 if (!variable->contains(dateTimeRange)) {
307 317 qCDebug(LOG_VisualizationGraphWidget())
308 318 << "TORM: Modif on variable datetime detected" << currentDateTime;
309 319 variable->setDateTime(currentDateTime);
310 320 }
311 321
312 322 qCDebug(LOG_VisualizationGraphWidget()) << tr("TORM: Request data detection: ");
313 323 // CHangement detected, we need to ask controller to request data loading
314 324 emit requestDataLoading(variable, variableDateTimeWithTolerance);
315 325 }
316 326 else {
317 327 qCInfo(LOG_VisualizationGraphWidget())
318 328 << tr("TORM: Detection zoom in that doesn't need request: ");
319 329 zoomType = VisualizationGraphWidgetZoomType::ZoomIn;
320 330 }
321 331 }
322 332
323 333 if (impl->m_DoSynchronize && !impl->m_IsCalibration) {
324 334 auto oldDateTime = SqpDateTime{t2.lower, t2.upper};
325 335 qCDebug(LOG_VisualizationGraphWidget())
326 336 << tr("TORM: VisualizationGraphWidget::Synchronize notify !!")
327 337 << QThread::currentThread()->objectName();
328 338 emit synchronize(dateTimeRange, oldDateTime, zoomType);
329 339 }
330 340 }
331 341
342 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
343 {
344 // Handles plot rendering when mouse is moving
345 impl->m_RenderingDelegate->onMouseMove(event);
346 }
347
332 348 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
333 349 {
334 350 auto zoomOrientations = QFlags<Qt::Orientation>{};
335 351
336 352 // Lambda that enables a zoom orientation if the key modifier related to this orientation
337 353 // has
338 354 // been pressed
339 355 auto enableOrientation
340 356 = [&zoomOrientations, event](const auto &orientation, const auto &modifier) {
341 357 auto orientationEnabled = event->modifiers().testFlag(modifier);
342 358 zoomOrientations.setFlag(orientation, orientationEnabled);
343 359 };
344 360 enableOrientation(Qt::Vertical, VERTICAL_ZOOM_MODIFIER);
345 361 enableOrientation(Qt::Horizontal, HORIZONTAL_ZOOM_MODIFIER);
346 362
347 363 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
348 364 }
349 365
350 366 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
351 367 {
352 368 impl->m_IsCalibration = event->modifiers().testFlag(Qt::ControlModifier);
353 369 }
354 370
355 371 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
356 372 {
357 373 impl->m_IsCalibration = false;
358 374 }
359 375
360 376 void VisualizationGraphWidget::onDataCacheVariableUpdated()
361 377 {
362 378 // NOTE:
363 379 // We don't want to call the method for each component of a variable unitarily, but for
364 380 // all
365 381 // its components at once (eg its three components in the case of a vector).
366 382
367 383 // The unordered_multimap does not do this easily, so the question is whether to:
368 384 // - use an ordered_multimap and the algos of std to group the values by key
369 385 // - use a map (unique keys) and store as values directly the list of components
370 386
371 387 auto grapheRange = ui->widget->xAxis->range();
372 388 auto dateTime = SqpDateTime{grapheRange.lower, grapheRange.upper};
373 389
374 390 for (auto it = impl->m_VariableToPlotMultiMap.cbegin();
375 391 it != impl->m_VariableToPlotMultiMap.cend(); ++it) {
376 392 auto variable = it->first;
377 393 qCDebug(LOG_VisualizationGraphWidget())
378 394 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S"
379 395 << variable->dateTime();
380 396 qCDebug(LOG_VisualizationGraphWidget())
381 397 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
382 398 if (dateTime.contains(variable->dateTime()) || dateTime.intersect(variable->dateTime())) {
383 399
384 400 VisualizationGraphHelper::updateData(QVector<QCPAbstractPlottable *>{} << it->second,
385 401 variable->dataSeries(), variable->dateTime());
386 402 }
387 403 }
388 404 }
389 405
390 406 VisualizationGraphWidgetZoomType
391 407 VisualizationGraphWidget::VisualizationGraphWidgetPrivate::getZoomType(const QCPRange &t1,
392 408 const QCPRange &t2)
393 409 {
394 410 // t1.lower <= t2.lower && t2.upper <= t1.upper
395 411 auto zoomType = VisualizationGraphWidgetZoomType::Unknown;
396 412 if (t1.lower <= t2.lower && t2.upper <= t1.upper) {
397 413 zoomType = VisualizationGraphWidgetZoomType::ZoomOut;
398 414 }
399 415 else if (t1.lower > t2.lower && t1.upper > t2.upper) {
400 416 zoomType = VisualizationGraphWidgetZoomType::PanRight;
401 417 }
402 418 else if (t1.lower < t2.lower && t1.upper < t2.upper) {
403 419 zoomType = VisualizationGraphWidgetZoomType::PanLeft;
404 420 }
405 421 else if (t1.lower > t2.lower && t2.upper > t1.upper) {
406 422 zoomType = VisualizationGraphWidgetZoomType::ZoomIn;
407 423 }
408 424 else {
409 425 qCCritical(LOG_VisualizationGraphWidget()) << "getZoomType: Unknown type detected";
410 426 }
411 427 return zoomType;
412 428 }
General Comments 0
You need to be logged in to leave comments. Login now