##// END OF EJS Templates
Refactoring handling of axes properties (2)...
Alexandre Leroux -
r917:3bf91527f27d
parent child
Show More
@@ -1,31 +1,34
1 1 #ifndef SCIQLOP_AXISRENDERINGUTILS_H
2 2 #define SCIQLOP_AXISRENDERINGUTILS_H
3 3
4 4 #include <memory>
5 5
6 6 #include <QtCore/QString>
7 7
8 8 class IDataSeries;
9 9 class QCPAxis;
10 10 class QCPColorScale;
11 11 class QCustomPlot;
12 12
13 /// Formats a data value according to the axis on which it is present
14 QString formatValue(double value, const QCPAxis &axis);
15
13 16 /**
14 17 * Helper used to handle axes rendering
15 18 */
16 19 struct IAxisHelper {
17 20 virtual ~IAxisHelper() noexcept = default;
18 21
19 22 /// Set properties of the plot's axes and the color scale associated to plot passed as
20 23 /// parameters
21 24 /// @param plot the plot for which to set axe properties
22 25 /// @param colorScale the color scale for which to set properties
23 26 virtual void setProperties(QCustomPlot &plot, QCPColorScale &colorScale) = 0;
24 27 };
25 28
26 29 struct IAxisHelperFactory {
27 30 /// Creates IAxisHelper according to a data series
28 31 static std::unique_ptr<IAxisHelper> create(std::shared_ptr<IDataSeries> dataSeries) noexcept;
29 32 };
30 33
31 34 #endif // SCIQLOP_AXISRENDERINGUTILS_H
@@ -1,31 +1,35
1 1 #ifndef SCIQLOP_VISUALIZATIONGRAPHRENDERINGDELEGATE_H
2 2 #define SCIQLOP_VISUALIZATIONGRAPHRENDERINGDELEGATE_H
3 3
4 4 #include <Common/spimpl.h>
5 5
6 #include <Visualization/VisualizationDefs.h>
7
8 class IDataSeries;
6 9 class QCustomPlot;
7 10 class QMouseEvent;
8 11 class Unit;
9 12 class VisualizationGraphWidget;
10 13
11 14 class VisualizationGraphRenderingDelegate {
12 15 public:
13 16 /// Ctor
14 17 /// @param graphWidget the graph widget to which the delegate is associated
15 18 /// @remarks the graph widget must exist throughout the life cycle of the delegate
16 19 explicit VisualizationGraphRenderingDelegate(VisualizationGraphWidget &graphWidget);
17 20
18 21 void onMouseMove(QMouseEvent *event) noexcept;
19 22
20 /// Sets properties of the plot's axes
21 void setAxesProperties(const Unit &xAxisUnit, const Unit &valuesUnit) noexcept;
23 /// Sets properties of the plot's axes from the data series passed as parameter
24 void setAxesProperties(std::shared_ptr<IDataSeries> dataSeries) noexcept;
25
22 26
23 27 /// Shows or hides graph overlay (name, close button, etc.)
24 28 void showGraphOverlay(bool show) noexcept;
25 29
26 30 private:
27 31 class VisualizationGraphRenderingDelegatePrivate;
28 32 spimpl::unique_impl_ptr<VisualizationGraphRenderingDelegatePrivate> impl;
29 33 };
30 34
31 35 #endif // SCIQLOP_VISUALIZATIONGRAPHRENDERINGDELEGATE_H
@@ -1,65 +1,123
1 1 #include "Visualization/AxisRenderingUtils.h"
2 2
3 3 #include <Data/ScalarSeries.h>
4 4 #include <Data/VectorSeries.h>
5 5
6 6 #include <Visualization/qcustomplot.h>
7 7
8 8 namespace {
9 9
10 const auto DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd hh:mm:ss:zzz");
11
12 /// Format for datetimes on a axis
13 const auto DATETIME_TICKER_FORMAT = QStringLiteral("yyyy/MM/dd \nhh:mm:ss");
14
15 /// Generates the appropriate ticker for an axis, depending on whether the axis displays time or
16 /// non-time data
17 QSharedPointer<QCPAxisTicker> axisTicker(bool isTimeAxis)
18 {
19 if (isTimeAxis) {
20 auto dateTicker = QSharedPointer<QCPAxisTickerDateTime>::create();
21 dateTicker->setDateTimeFormat(DATETIME_TICKER_FORMAT);
22 dateTicker->setDateTimeSpec(Qt::UTC);
23
24 return dateTicker;
25 }
26 else {
27 // default ticker
28 return QSharedPointer<QCPAxisTicker>::create();
29 }
30 }
31
32 /**
33 * Sets properties of the axis passed as parameter
34 * @param axis the axis to set
35 * @param unit the unit to set for the axis
36 * @param scaleType the scale type to set for the axis
37 */
38 void setAxisProperties(QCPAxis &axis, const Unit &unit,
39 QCPAxis::ScaleType scaleType = QCPAxis::stLinear)
40 {
41 // label (unit name)
42 axis.setLabel(unit.m_Name);
43
44 // scale type
45 axis.setScaleType(scaleType);
46
47 // ticker (depending on the type of unit)
48 axis.setTicker(axisTicker(unit.m_TimeUnit));
49 }
50
10 51 /**
11 52 * Delegate used to set axes properties
12 53 */
13 54 template <typename T, typename Enabled = void>
14 55 struct AxisSetter {
15 56 static void setProperties(T &, QCustomPlot &, QCPColorScale &)
16 57 {
17 58 // Default implementation does nothing
18 59 }
19 60 };
20 61
21 62 /**
22 63 * Specialization of AxisSetter for scalars and vectors
23 64 * @sa ScalarSeries
24 65 * @sa VectorSeries
25 66 */
26 67 template <typename T>
27 68 struct AxisSetter<T, typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value
28 69 or std::is_base_of<VectorSeries, T>::value> > {
29 70 static void setProperties(T &dataSeries, QCustomPlot &plot, QCPColorScale &)
30 71 {
31 /// @todo ALX
72 dataSeries.lockRead();
73 auto xAxisUnit = dataSeries.xAxisUnit();
74 auto valuesUnit = dataSeries.valuesUnit();
75 dataSeries.unlock();
76
77 setAxisProperties(*plot.xAxis, xAxisUnit);
78 setAxisProperties(*plot.yAxis, valuesUnit);
32 79 }
33 80 };
34 81
35 82 /**
36 83 * Default implementation of IAxisHelper, which takes data series to set axes properties
37 84 * @tparam T the data series' type
38 85 */
39 86 template <typename T>
40 87 struct AxisHelper : public IAxisHelper {
41 88 explicit AxisHelper(T &dataSeries) : m_DataSeries{dataSeries} {}
42 89
43 90 void setProperties(QCustomPlot &plot, QCPColorScale &colorScale) override
44 91 {
45 92 AxisSetter<T>::setProperties(m_DataSeries, plot, colorScale);
46 93 }
47 94
48 95 T &m_DataSeries;
49 96 };
50 97
51 98 } // namespace
52 99
100 QString formatValue(double value, const QCPAxis &axis)
101 {
102 // If the axis is a time axis, formats the value as a date
103 if (auto axisTicker = qSharedPointerDynamicCast<QCPAxisTickerDateTime>(axis.ticker())) {
104 return DateUtils::dateTime(value, axisTicker->dateTimeSpec()).toString(DATETIME_FORMAT);
105 }
106 else {
107 return QString::number(value);
108 }
109 }
110
53 111 std::unique_ptr<IAxisHelper>
54 112 IAxisHelperFactory::create(std::shared_ptr<IDataSeries> dataSeries) noexcept
55 113 {
56 114 if (auto scalarSeries = std::dynamic_pointer_cast<ScalarSeries>(dataSeries)) {
57 115 return std::make_unique<AxisHelper<ScalarSeries> >(*scalarSeries);
58 116 }
59 117 else if (auto vectorSeries = std::dynamic_pointer_cast<VectorSeries>(dataSeries)) {
60 118 return std::make_unique<AxisHelper<VectorSeries> >(*vectorSeries);
61 119 }
62 120 else {
63 121 return std::make_unique<AxisHelper<IDataSeries> >(*dataSeries);
64 122 }
65 123 }
@@ -1,275 +1,235
1 1 #include "Visualization/VisualizationGraphRenderingDelegate.h"
2 #include "Visualization/AxisRenderingUtils.h"
2 3 #include "Visualization/VisualizationGraphWidget.h"
3 4 #include "Visualization/qcustomplot.h"
4 5
5 6 #include <Common/DateUtils.h>
6 7
7 8 #include <Data/IDataSeries.h>
8 9
9 10 #include <SqpApplication.h>
10 11
11 12 namespace {
12 13
13 14 /// Name of the axes layer in QCustomPlot
14 15 const auto AXES_LAYER = QStringLiteral("axes");
15 16
16 const auto DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd hh:mm:ss:zzz");
17
18 /// Format for datetimes on a axis
19 const auto DATETIME_TICKER_FORMAT = QStringLiteral("yyyy/MM/dd \nhh:mm:ss");
20
21 17 /// Icon used to show x-axis properties
22 18 const auto HIDE_AXIS_ICON_PATH = QStringLiteral(":/icones/down.png");
23 19
24 20 /// Name of the overlay layer in QCustomPlot
25 21 const auto OVERLAY_LAYER = QStringLiteral("overlay");
26 22
27 23 /// Pixmap used to show x-axis properties
28 24 const auto SHOW_AXIS_ICON_PATH = QStringLiteral(":/icones/up.png");
29 25
30 26 const auto TOOLTIP_FORMAT = QStringLiteral("key: %1\nvalue: %2");
31 27
32 28 /// Offset used to shift the tooltip of the mouse
33 29 const auto TOOLTIP_OFFSET = QPoint{20, 20};
34 30
35 31 /// Tooltip display rectangle (the tooltip is hidden when the mouse leaves this rectangle)
36 32 const auto TOOLTIP_RECT = QRect{10, 10, 10, 10};
37 33
38 34 /// Timeout after which the tooltip is displayed
39 35 const auto TOOLTIP_TIMEOUT = 500;
40 36
41 /// Generates the appropriate ticker for an axis, depending on whether the axis displays time or
42 /// non-time data
43 QSharedPointer<QCPAxisTicker> axisTicker(bool isTimeAxis)
44 {
45 if (isTimeAxis) {
46 auto dateTicker = QSharedPointer<QCPAxisTickerDateTime>::create();
47 dateTicker->setDateTimeFormat(DATETIME_TICKER_FORMAT);
48 dateTicker->setDateTimeSpec(Qt::UTC);
49
50 return dateTicker;
51 }
52 else {
53 // default ticker
54 return QSharedPointer<QCPAxisTicker>::create();
55 }
56 }
57
58 /// Formats a data value according to the axis on which it is present
59 QString formatValue(double value, const QCPAxis &axis)
60 {
61 // If the axis is a time axis, formats the value as a date
62 if (auto axisTicker = qSharedPointerDynamicCast<QCPAxisTickerDateTime>(axis.ticker())) {
63 return DateUtils::dateTime(value, axisTicker->dateTimeSpec()).toString(DATETIME_FORMAT);
64 }
65 else {
66 return QString::number(value);
67 }
68 }
69
70 37 void initPointTracerStyle(QCPItemTracer &tracer) noexcept
71 38 {
72 39 tracer.setInterpolating(false);
73 40 tracer.setStyle(QCPItemTracer::tsCircle);
74 41 tracer.setSize(3);
75 42 tracer.setPen(QPen(Qt::black));
76 43 tracer.setBrush(Qt::black);
77 44 }
78 45
79 46 QPixmap pixmap(const QString &iconPath) noexcept
80 47 {
81 48 return QIcon{iconPath}.pixmap(QSize{16, 16});
82 49 }
83 50
84 51 void initClosePixmapStyle(QCPItemPixmap &pixmap) noexcept
85 52 {
86 53 // Icon
87 54 pixmap.setPixmap(
88 55 sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton).pixmap(QSize{16, 16}));
89 56
90 57 // Position
91 58 pixmap.topLeft->setType(QCPItemPosition::ptAxisRectRatio);
92 59 pixmap.topLeft->setCoords(1, 0);
93 60 pixmap.setClipToAxisRect(false);
94 61
95 62 // Can be selected
96 63 pixmap.setSelectable(true);
97 64 }
98 65
99 66 void initXAxisPixmapStyle(QCPItemPixmap &itemPixmap) noexcept
100 67 {
101 68 // Icon
102 69 itemPixmap.setPixmap(pixmap(HIDE_AXIS_ICON_PATH));
103 70
104 71 // Position
105 72 itemPixmap.topLeft->setType(QCPItemPosition::ptAxisRectRatio);
106 73 itemPixmap.topLeft->setCoords(0, 1);
107 74 itemPixmap.setClipToAxisRect(false);
108 75
109 76 // Can be selected
110 77 itemPixmap.setSelectable(true);
111 78 }
112 79
113 80 void initTitleTextStyle(QCPItemText &text) noexcept
114 81 {
115 82 // Font and background styles
116 83 text.setColor(Qt::gray);
117 84 text.setBrush(Qt::white);
118 85
119 86 // Position
120 87 text.setPositionAlignment(Qt::AlignTop | Qt::AlignLeft);
121 88 text.position->setType(QCPItemPosition::ptAxisRectRatio);
122 89 text.position->setCoords(0.5, 0);
123 90 }
124 91
125 92 } // namespace
126 93
127 94 struct VisualizationGraphRenderingDelegate::VisualizationGraphRenderingDelegatePrivate {
128 95 explicit VisualizationGraphRenderingDelegatePrivate(VisualizationGraphWidget &graphWidget)
129 96 : m_Plot{graphWidget.plot()},
130 97 m_PointTracer{new QCPItemTracer{&m_Plot}},
131 98 m_TracerTimer{},
132 99 m_ClosePixmap{new QCPItemPixmap{&m_Plot}},
133 100 m_TitleText{new QCPItemText{&m_Plot}},
134 101 m_XAxisPixmap{new QCPItemPixmap{&m_Plot}},
135 102 m_ShowXAxis{true},
136 103 m_XAxisLabel{}
137 104 {
138 105 initPointTracerStyle(*m_PointTracer);
139 106
140 107 m_TracerTimer.setInterval(TOOLTIP_TIMEOUT);
141 108 m_TracerTimer.setSingleShot(true);
142 109
143 110 // Inits "close button" in plot overlay
144 111 m_ClosePixmap->setLayer(OVERLAY_LAYER);
145 112 initClosePixmapStyle(*m_ClosePixmap);
146 113
147 114 // Connects pixmap selection to graph widget closing
148 115 QObject::connect(m_ClosePixmap, &QCPItemPixmap::selectionChanged,
149 116 [&graphWidget](bool selected) {
150 117 if (selected) {
151 118 graphWidget.close();
152 119 }
153 120 });
154 121
155 122 // Inits graph name in plot overlay
156 123 m_TitleText->setLayer(OVERLAY_LAYER);
157 124 m_TitleText->setText(graphWidget.name());
158 125 initTitleTextStyle(*m_TitleText);
159 126
160 127 // Inits "show x-axis button" in plot overlay
161 128 m_XAxisPixmap->setLayer(OVERLAY_LAYER);
162 129 initXAxisPixmapStyle(*m_XAxisPixmap);
163 130
164 131 // Connects pixmap selection to graph x-axis showing/hiding
165 132 QObject::connect(m_XAxisPixmap, &QCPItemPixmap::selectionChanged, [this]() {
166 133 if (m_XAxisPixmap->selected()) {
167 134 // Changes the selection state and refreshes the x-axis
168 135 m_ShowXAxis = !m_ShowXAxis;
169 136 updateXAxisState();
170 137 m_Plot.layer(AXES_LAYER)->replot();
171 138
172 139 // Deselects the x-axis pixmap and updates icon
173 140 m_XAxisPixmap->setSelected(false);
174 141 m_XAxisPixmap->setPixmap(
175 142 pixmap(m_ShowXAxis ? HIDE_AXIS_ICON_PATH : SHOW_AXIS_ICON_PATH));
176 143 m_Plot.layer(OVERLAY_LAYER)->replot();
177 144 }
178 145 });
179 146 }
180 147
181 148 /// Updates state of x-axis according to the current selection of x-axis pixmap
182 149 /// @remarks the method doesn't call plot refresh
183 150 void updateXAxisState() noexcept
184 151 {
185 152 m_Plot.xAxis->setTickLabels(m_ShowXAxis);
186 153 m_Plot.xAxis->setLabel(m_ShowXAxis ? m_XAxisLabel : QString{});
187 154 }
188 155
189 156 QCustomPlot &m_Plot;
190 157 QCPItemTracer *m_PointTracer;
191 158 QTimer m_TracerTimer;
192 159 QCPItemPixmap *m_ClosePixmap; /// Graph's close button
193 160 QCPItemText *m_TitleText; /// Graph's title
194 161 QCPItemPixmap *m_XAxisPixmap;
195 162 bool m_ShowXAxis; /// X-axis properties are shown or hidden
196 163 QString m_XAxisLabel;
197 164 };
198 165
199 166 VisualizationGraphRenderingDelegate::VisualizationGraphRenderingDelegate(
200 167 VisualizationGraphWidget &graphWidget)
201 168 : impl{spimpl::make_unique_impl<VisualizationGraphRenderingDelegatePrivate>(graphWidget)}
202 169 {
203 170 }
204 171
205 172 void VisualizationGraphRenderingDelegate::onMouseMove(QMouseEvent *event) noexcept
206 173 {
207 174 // Cancels pending refresh
208 175 impl->m_TracerTimer.disconnect();
209 176
210 177 // Reinits tracers
211 178 impl->m_PointTracer->setGraph(nullptr);
212 179 impl->m_PointTracer->setVisible(false);
213 180 impl->m_Plot.replot();
214 181
215 182 // Gets the graph under the mouse position
216 183 auto eventPos = event->pos();
217 184 if (auto graph = qobject_cast<QCPGraph *>(impl->m_Plot.plottableAt(eventPos))) {
218 185 auto mouseKey = graph->keyAxis()->pixelToCoord(eventPos.x());
219 186 auto graphData = graph->data();
220 187
221 188 // Gets the closest data point to the mouse
222 189 auto graphDataIt = graphData->findBegin(mouseKey);
223 190 if (graphDataIt != graphData->constEnd()) {
224 191 auto key = formatValue(graphDataIt->key, *graph->keyAxis());
225 192 auto value = formatValue(graphDataIt->value, *graph->valueAxis());
226 193
227 194 // Displays point tracer
228 195 impl->m_PointTracer->setGraph(graph);
229 196 impl->m_PointTracer->setGraphKey(graphDataIt->key);
230 197 impl->m_PointTracer->setLayer(
231 198 impl->m_Plot.layer("main")); // Tracer is set on top of the plot's main layer
232 199 impl->m_PointTracer->setVisible(true);
233 200 impl->m_Plot.replot();
234 201
235 202 // Starts timer to show tooltip after timeout
236 203 auto showTooltip = [ tooltip = TOOLTIP_FORMAT.arg(key, value), eventPos, this ]()
237 204 {
238 205 QToolTip::showText(impl->m_Plot.mapToGlobal(eventPos) + TOOLTIP_OFFSET, tooltip,
239 206 &impl->m_Plot, TOOLTIP_RECT);
240 207 };
241 208
242 209 QObject::connect(&impl->m_TracerTimer, &QTimer::timeout, showTooltip);
243 210 impl->m_TracerTimer.start();
244 211 }
245 212 }
246 213 }
247 214
248 void VisualizationGraphRenderingDelegate::setAxesProperties(const Unit &xAxisUnit,
249 const Unit &valuesUnit) noexcept
215 void VisualizationGraphRenderingDelegate::setAxesProperties(
216 std::shared_ptr<IDataSeries> dataSeries) noexcept
250 217 {
251 218 // Stores x-axis label to be able to retrieve it when x-axis pixmap is unselected
252 impl->m_XAxisLabel = xAxisUnit.m_Name;
253
254 auto setAxisProperties = [](auto axis, const auto &unit) {
255 // label (unit name)
256 axis->setLabel(unit.m_Name);
219 impl->m_XAxisLabel = dataSeries->xAxisUnit().m_Name;
257 220
258 // ticker (depending on the type of unit)
259 axis->setTicker(axisTicker(unit.m_TimeUnit));
260 };
261 setAxisProperties(impl->m_Plot.xAxis, xAxisUnit);
262 setAxisProperties(impl->m_Plot.yAxis, valuesUnit);
221 auto axisHelper = IAxisHelperFactory::create(dataSeries);
222 axisHelper->setProperties(impl->m_Plot, *impl->m_ColorScale);
263 223
264 224 // Updates x-axis state
265 225 impl->updateXAxisState();
266 226
267 227 impl->m_Plot.layer(AXES_LAYER)->replot();
268 228 }
269 229
270 230 void VisualizationGraphRenderingDelegate::showGraphOverlay(bool show) noexcept
271 231 {
272 232 auto overlay = impl->m_Plot.layer(OVERLAY_LAYER);
273 233 overlay->setVisible(show);
274 234 overlay->replot();
275 235 }
@@ -1,405 +1,397
1 1 #include "Visualization/VisualizationGraphWidget.h"
2 2 #include "Visualization/IVisualizationWidgetVisitor.h"
3 3 #include "Visualization/VisualizationDefs.h"
4 4 #include "Visualization/VisualizationGraphHelper.h"
5 5 #include "Visualization/VisualizationGraphRenderingDelegate.h"
6 6 #include "Visualization/VisualizationZoneWidget.h"
7 7 #include "ui_VisualizationGraphWidget.h"
8 8
9 9 #include <Common/MimeTypesDef.h>
10 10 #include <Data/ArrayData.h>
11 11 #include <Data/IDataSeries.h>
12 12 #include <DragAndDrop/DragDropHelper.h>
13 13 #include <Settings/SqpSettingsDefs.h>
14 14 #include <SqpApplication.h>
15 15 #include <Time/TimeController.h>
16 16 #include <Variable/Variable.h>
17 17 #include <Variable/VariableController.h>
18 18
19 19 #include <unordered_map>
20 20
21 21 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
22 22
23 23 namespace {
24 24
25 25 /// Key pressed to enable zoom on horizontal axis
26 26 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::NoModifier;
27 27
28 28 /// Key pressed to enable zoom on vertical axis
29 29 const auto VERTICAL_ZOOM_MODIFIER = Qt::ControlModifier;
30 30
31 31 } // namespace
32 32
33 33 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
34 34
35 35 explicit VisualizationGraphWidgetPrivate(const QString &name)
36 36 : m_Name{name},
37 37 m_DoAcquisition{true},
38 38 m_IsCalibration{false},
39 39 m_RenderingDelegate{nullptr}
40 40 {
41 41 }
42 42
43 43 QString m_Name;
44 44 // 1 variable -> n qcpplot
45 45 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
46 46 bool m_DoAcquisition;
47 47 bool m_IsCalibration;
48 48 /// Delegate used to attach rendering features to the plot
49 49 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
50 50 };
51 51
52 52 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
53 53 : VisualizationDragWidget{parent},
54 54 ui{new Ui::VisualizationGraphWidget},
55 55 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
56 56 {
57 57 ui->setupUi(this);
58 58
59 59 // 'Close' options : widget is deleted when closed
60 60 setAttribute(Qt::WA_DeleteOnClose);
61 61
62 62 // Set qcpplot properties :
63 63 // - Drag (on x-axis) and zoom are enabled
64 64 // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation
65 65 ui->widget->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectItems);
66 66 ui->widget->axisRect()->setRangeDrag(Qt::Horizontal);
67 67
68 68 // The delegate must be initialized after the ui as it uses the plot
69 69 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
70 70
71 71 connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress);
72 72 connect(ui->widget, &QCustomPlot::mouseRelease, this,
73 73 &VisualizationGraphWidget::onMouseRelease);
74 74 connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove);
75 75 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
76 76 connect(ui->widget->xAxis, static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(
77 77 &QCPAxis::rangeChanged),
78 78 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
79 79
80 80 // Activates menu when right clicking on the graph
81 81 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
82 82 connect(ui->widget, &QCustomPlot::customContextMenuRequested, this,
83 83 &VisualizationGraphWidget::onGraphMenuRequested);
84 84
85 85 connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
86 86 &VariableController::onRequestDataLoading);
87 87
88 88 connect(&sqpApp->variableController(), &VariableController::updateVarDisplaying, this,
89 89 &VisualizationGraphWidget::onUpdateVarDisplaying);
90 90 }
91 91
92 92
93 93 VisualizationGraphWidget::~VisualizationGraphWidget()
94 94 {
95 95 delete ui;
96 96 }
97 97
98 98 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
99 99 {
100 100 auto parent = parentWidget();
101 101 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
102 102 parent = parent->parentWidget();
103 103 }
104 104
105 105 return qobject_cast<VisualizationZoneWidget *>(parent);
106 106 }
107 107
108 108 void VisualizationGraphWidget::enableAcquisition(bool enable)
109 109 {
110 110 impl->m_DoAcquisition = enable;
111 111 }
112 112
113 113 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, SqpRange range)
114 114 {
115 115 // Uses delegate to create the qcpplot components according to the variable
116 116 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
117 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
118
119 // Set axes properties according to the units of the data series
120 /// @todo : for the moment, no control is performed on the axes: the units and the tickers
121 /// are fixed for the default x-axis and y-axis of the plot, and according to the new graph
122 auto xAxisUnit = Unit{};
123 auto valuesUnit = Unit{};
124 117
125 118 if (auto dataSeries = variable->dataSeries()) {
126 dataSeries->lockRead();
127 xAxisUnit = dataSeries->xAxisUnit();
128 valuesUnit = dataSeries->valuesUnit();
129 dataSeries->unlock();
119 // Set axes properties according to the units of the data series
120 impl->m_RenderingDelegate->setAxesProperties(dataSeries);
130 121 }
131 impl->m_RenderingDelegate->setAxesProperties(xAxisUnit, valuesUnit);
122
123 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
132 124
133 125 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
134 126
135 127 this->enableAcquisition(false);
136 128 this->setGraphRange(range);
137 129 this->enableAcquisition(true);
138 130
139 131 emit requestDataLoading(QVector<std::shared_ptr<Variable> >() << variable, range, false);
140 132
141 133 emit variableAdded(variable);
142 134 }
143 135
144 136 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
145 137 {
146 138 // Each component associated to the variable :
147 139 // - is removed from qcpplot (which deletes it)
148 140 // - is no longer referenced in the map
149 141 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
150 142 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
151 143 emit variableAboutToBeRemoved(variable);
152 144
153 145 auto &plottablesMap = variableIt->second;
154 146
155 147 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
156 148 plottableIt != plottableEnd;) {
157 149 ui->widget->removePlottable(plottableIt->second);
158 150 plottableIt = plottablesMap.erase(plottableIt);
159 151 }
160 152
161 153 impl->m_VariableToPlotMultiMap.erase(variableIt);
162 154 }
163 155
164 156 // Updates graph
165 157 ui->widget->replot();
166 158 }
167 159
168 160 QList<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
169 161 {
170 162 auto variables = QList<std::shared_ptr<Variable> >{};
171 163 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
172 164 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
173 165 variables << it->first;
174 166 }
175 167
176 168 return variables;
177 169 }
178 170
179 171 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
180 172 {
181 173 if (!variable) {
182 174 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
183 175 return;
184 176 }
185 177
186 178 VisualizationGraphHelper::setYAxisRange(variable, *ui->widget);
187 179 }
188 180
189 181 SqpRange VisualizationGraphWidget::graphRange() const noexcept
190 182 {
191 183 auto graphRange = ui->widget->xAxis->range();
192 184 return SqpRange{graphRange.lower, graphRange.upper};
193 185 }
194 186
195 187 void VisualizationGraphWidget::setGraphRange(const SqpRange &range)
196 188 {
197 189 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange START");
198 190 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
199 191 ui->widget->replot();
200 192 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange END");
201 193 }
202 194
203 195 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
204 196 {
205 197 if (visitor) {
206 198 visitor->visit(this);
207 199 }
208 200 else {
209 201 qCCritical(LOG_VisualizationGraphWidget())
210 202 << tr("Can't visit widget : the visitor is null");
211 203 }
212 204 }
213 205
214 206 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
215 207 {
216 208 /// @todo : for the moment, a graph can always accomodate a variable
217 209 Q_UNUSED(variable);
218 210 return true;
219 211 }
220 212
221 213 bool VisualizationGraphWidget::contains(const Variable &variable) const
222 214 {
223 215 // Finds the variable among the keys of the map
224 216 auto variablePtr = &variable;
225 217 auto findVariable
226 218 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
227 219
228 220 auto end = impl->m_VariableToPlotMultiMap.cend();
229 221 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
230 222 return it != end;
231 223 }
232 224
233 225 QString VisualizationGraphWidget::name() const
234 226 {
235 227 return impl->m_Name;
236 228 }
237 229
238 230 QMimeData *VisualizationGraphWidget::mimeData() const
239 231 {
240 232 auto mimeData = new QMimeData;
241 233 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
242 234
243 235 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
244 236 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
245 237
246 238 return mimeData;
247 239 }
248 240
249 241 bool VisualizationGraphWidget::isDragAllowed() const
250 242 {
251 243 return true;
252 244 }
253 245
254 246 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
255 247 {
256 248 if (highlighted) {
257 249 plot().setBackground(QBrush(QColor("#BBD5EE")));
258 250 }
259 251 else {
260 252 plot().setBackground(QBrush(Qt::white));
261 253 }
262 254
263 255 plot().update();
264 256 }
265 257
266 258 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
267 259 {
268 260 Q_UNUSED(event);
269 261
270 262 // Prevents that all variables will be removed from graph when it will be closed
271 263 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
272 264 emit variableAboutToBeRemoved(variableEntry.first);
273 265 }
274 266 }
275 267
276 268 void VisualizationGraphWidget::enterEvent(QEvent *event)
277 269 {
278 270 Q_UNUSED(event);
279 271 impl->m_RenderingDelegate->showGraphOverlay(true);
280 272 }
281 273
282 274 void VisualizationGraphWidget::leaveEvent(QEvent *event)
283 275 {
284 276 Q_UNUSED(event);
285 277 impl->m_RenderingDelegate->showGraphOverlay(false);
286 278 }
287 279
288 280 QCustomPlot &VisualizationGraphWidget::plot() noexcept
289 281 {
290 282 return *ui->widget;
291 283 }
292 284
293 285 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
294 286 {
295 287 QMenu graphMenu{};
296 288
297 289 // Iterates on variables (unique keys)
298 290 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
299 291 end = impl->m_VariableToPlotMultiMap.cend();
300 292 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
301 293 // 'Remove variable' action
302 294 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
303 295 [ this, var = it->first ]() { removeVariable(var); });
304 296 }
305 297
306 298 if (!graphMenu.isEmpty()) {
307 299 graphMenu.exec(QCursor::pos());
308 300 }
309 301 }
310 302
311 303 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
312 304 {
313 305 qCDebug(LOG_VisualizationGraphWidget()) << tr("TORM: VisualizationGraphWidget::onRangeChanged")
314 306 << QThread::currentThread()->objectName() << "DoAcqui"
315 307 << impl->m_DoAcquisition;
316 308
317 309 auto graphRange = SqpRange{t1.lower, t1.upper};
318 310 auto oldGraphRange = SqpRange{t2.lower, t2.upper};
319 311
320 312 if (impl->m_DoAcquisition) {
321 313 QVector<std::shared_ptr<Variable> > variableUnderGraphVector;
322 314
323 315 for (auto it = impl->m_VariableToPlotMultiMap.begin(),
324 316 end = impl->m_VariableToPlotMultiMap.end();
325 317 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
326 318 variableUnderGraphVector.push_back(it->first);
327 319 }
328 320 emit requestDataLoading(std::move(variableUnderGraphVector), graphRange,
329 321 !impl->m_IsCalibration);
330 322
331 323 if (!impl->m_IsCalibration) {
332 324 qCDebug(LOG_VisualizationGraphWidget())
333 325 << tr("TORM: VisualizationGraphWidget::Synchronize notify !!")
334 326 << QThread::currentThread()->objectName() << graphRange << oldGraphRange;
335 327 emit synchronize(graphRange, oldGraphRange);
336 328 }
337 329 }
338 330 }
339 331
340 332 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
341 333 {
342 334 // Handles plot rendering when mouse is moving
343 335 impl->m_RenderingDelegate->onMouseMove(event);
344 336
345 337 VisualizationDragWidget::mouseMoveEvent(event);
346 338 }
347 339
348 340 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
349 341 {
350 342 auto zoomOrientations = QFlags<Qt::Orientation>{};
351 343
352 344 // Lambda that enables a zoom orientation if the key modifier related to this orientation
353 345 // has
354 346 // been pressed
355 347 auto enableOrientation
356 348 = [&zoomOrientations, event](const auto &orientation, const auto &modifier) {
357 349 auto orientationEnabled = event->modifiers().testFlag(modifier);
358 350 zoomOrientations.setFlag(orientation, orientationEnabled);
359 351 };
360 352 enableOrientation(Qt::Vertical, VERTICAL_ZOOM_MODIFIER);
361 353 enableOrientation(Qt::Horizontal, HORIZONTAL_ZOOM_MODIFIER);
362 354
363 355 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
364 356 }
365 357
366 358 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
367 359 {
368 360 impl->m_IsCalibration = event->modifiers().testFlag(Qt::ControlModifier);
369 361
370 362 plot().setInteraction(QCP::iRangeDrag, !event->modifiers().testFlag(Qt::AltModifier));
371 363
372 364 VisualizationDragWidget::mousePressEvent(event);
373 365 }
374 366
375 367 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
376 368 {
377 369 impl->m_IsCalibration = false;
378 370 }
379 371
380 372 void VisualizationGraphWidget::onDataCacheVariableUpdated()
381 373 {
382 374 auto graphRange = ui->widget->xAxis->range();
383 375 auto dateTime = SqpRange{graphRange.lower, graphRange.upper};
384 376
385 377 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
386 378 auto variable = variableEntry.first;
387 379 qCDebug(LOG_VisualizationGraphWidget())
388 380 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
389 381 qCDebug(LOG_VisualizationGraphWidget())
390 382 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
391 383 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
392 384 VisualizationGraphHelper::updateData(variableEntry.second, variable->dataSeries(),
393 385 variable->range());
394 386 }
395 387 }
396 388 }
397 389
398 390 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
399 391 const SqpRange &range)
400 392 {
401 393 auto it = impl->m_VariableToPlotMultiMap.find(variable);
402 394 if (it != impl->m_VariableToPlotMultiMap.end()) {
403 395 VisualizationGraphHelper::updateData(it->second, variable->dataSeries(), range);
404 396 }
405 397 }
General Comments 0
You need to be logged in to leave comments. Login now