##// END OF EJS Templates
variable time is now set to range graphe displayed when it is displayed...
perrinel -
r314:85f427f84e81
parent child
Show More
@@ -1,63 +1,64
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 namespace Ui {
20 20 class VisualizationGraphWidget;
21 21 } // namespace Ui
22 22
23 23 class VisualizationGraphWidget : public QWidget, public IVisualizationWidget {
24 24 Q_OBJECT
25 25
26 26 public:
27 27 explicit VisualizationGraphWidget(const QString &name = {}, QWidget *parent = 0);
28 28 virtual ~VisualizationGraphWidget();
29 29
30 30 void addVariable(std::shared_ptr<Variable> variable);
31 void addVariableUsingGraph(std::shared_ptr<Variable> variable);
31 32 /// Removes a variable from the graph
32 33 void removeVariable(std::shared_ptr<Variable> variable) noexcept;
33 34
34 35 // IVisualizationWidget interface
35 36 void accept(IVisualizationWidgetVisitor *visitor) override;
36 37 bool canDrop(const Variable &variable) const override;
37 38 QString name() const override;
38 39
39 40 void updateDisplay(std::shared_ptr<Variable> variable);
40 41
41 42 signals:
42 43 void requestDataLoading(std::shared_ptr<Variable> variable, const SqpDateTime &dateTime);
43 44
44 45
45 46 private:
46 47 Ui::VisualizationGraphWidget *ui;
47 48
48 49 class VisualizationGraphWidgetPrivate;
49 50 spimpl::unique_impl_ptr<VisualizationGraphWidgetPrivate> impl;
50 51
51 52 private slots:
52 53 /// Slot called when right clicking on the graph (displays a menu)
53 54 void onGraphMenuRequested(const QPoint &pos) noexcept;
54 55
55 56 void onRangeChanged(const QCPRange &t1, const QCPRange &t2);
56 57
57 58 /// Slot called when a mouse wheel was made, to perform some processing before the zoom is done
58 59 void onMouseWheel(QWheelEvent *event) noexcept;
59 60
60 61 void onDataCacheVariableUpdated();
61 62 };
62 63
63 64 #endif // SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
@@ -1,236 +1,268
1 1 #include "Visualization/VisualizationGraphWidget.h"
2 2 #include "Visualization/IVisualizationWidgetVisitor.h"
3 3 #include "Visualization/VisualizationGraphHelper.h"
4 4 #include "ui_VisualizationGraphWidget.h"
5 5
6 6 #include <Data/ArrayData.h>
7 7 #include <Data/IDataSeries.h>
8 8 #include <SqpApplication.h>
9 9 #include <Variable/Variable.h>
10 10 #include <Variable/VariableController.h>
11 11
12 12 #include <unordered_map>
13 13
14 14 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
15 15
16 16 namespace {
17 17
18 18 /// Key pressed to enable zoom on horizontal axis
19 19 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::NoModifier;
20 20
21 21 /// Key pressed to enable zoom on vertical axis
22 22 const auto VERTICAL_ZOOM_MODIFIER = Qt::ControlModifier;
23 23
24 24 } // namespace
25 25
26 26 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
27 27
28 28 // 1 variable -> n qcpplot
29 29 std::multimap<std::shared_ptr<Variable>, QCPAbstractPlottable *> m_VariableToPlotMultiMap;
30 30 };
31 31
32 32 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
33 33 : QWidget{parent},
34 34 ui{new Ui::VisualizationGraphWidget},
35 35 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>()}
36 36 {
37 37 ui->setupUi(this);
38 38
39 39 ui->graphNameLabel->setText(name);
40 40
41 41 // 'Close' options : widget is deleted when closed
42 42 setAttribute(Qt::WA_DeleteOnClose);
43 43 connect(ui->closeButton, &QToolButton::clicked, this, &VisualizationGraphWidget::close);
44 44 ui->closeButton->setIcon(sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton));
45 45
46 46 // Set qcpplot properties :
47 47 // - Drag (on x-axis) and zoom are enabled
48 48 // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation
49 49 ui->widget->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom);
50 50 ui->widget->axisRect()->setRangeDrag(Qt::Horizontal);
51 51 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
52 52 connect(ui->widget->xAxis, static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(
53 53 &QCPAxis::rangeChanged),
54 54 this, &VisualizationGraphWidget::onRangeChanged);
55 55
56 56 // Activates menu when right clicking on the graph
57 57 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
58 58 connect(ui->widget, &QCustomPlot::customContextMenuRequested, this,
59 59 &VisualizationGraphWidget::onGraphMenuRequested);
60 60
61 61 connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
62 62 &VariableController::onRequestDataLoading);
63 63 }
64 64
65 65
66 66 VisualizationGraphWidget::~VisualizationGraphWidget()
67 67 {
68 68 delete ui;
69 69 }
70 70
71 71 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable)
72 72 {
73 73 // Uses delegate to create the qcpplot components according to the variable
74 74 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
75 75
76 76 for (auto createdPlottable : qAsConst(createdPlottables)) {
77 77 impl->m_VariableToPlotMultiMap.insert({variable, createdPlottable});
78 78 }
79 79
80 80 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
81 81 }
82 82
83 void VisualizationGraphWidget::addVariableUsingGraph(std::shared_ptr<Variable> variable)
84 {
85
86 // when adding a variable, we need to set its time range to the current graph range
87 auto grapheRange = ui->widget->xAxis->range();
88 auto dateTime = SqpDateTime{grapheRange.lower, grapheRange.upper};
89 variable->setDateTime(dateTime);
90 qCInfo(LOG_VisualizationGraphWidget()) << "ADD Variable with range : " << dateTime;
91
92 auto variableDateTimeWithTolerance = dateTime;
93
94 // add 10% tolerance for each side
95 auto tolerance = 0.1 * (dateTime.m_TEnd - dateTime.m_TStart);
96 variableDateTimeWithTolerance.m_TStart -= tolerance;
97 variableDateTimeWithTolerance.m_TEnd += tolerance;
98
99 qCInfo(LOG_VisualizationGraphWidget()) << "ADD Variable with range TOL: "
100 << variableDateTimeWithTolerance;
101
102 // Uses delegate to create the qcpplot components according to the variable
103 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
104
105 for (auto createdPlottable : qAsConst(createdPlottables)) {
106 impl->m_VariableToPlotMultiMap.insert({variable, createdPlottable});
107 }
108
109 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
110
111 // CHangement detected, we need to ask controller to request data loading
112 emit requestDataLoading(variable, variableDateTimeWithTolerance);
113 }
114
83 115 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
84 116 {
85 117 // Each component associated to the variable :
86 118 // - is removed from qcpplot (which deletes it)
87 119 // - is no longer referenced in the map
88 120 auto componentsIt = impl->m_VariableToPlotMultiMap.equal_range(variable);
89 121 for (auto it = componentsIt.first; it != componentsIt.second;) {
90 122 ui->widget->removePlottable(it->second);
91 123 it = impl->m_VariableToPlotMultiMap.erase(it);
92 124 }
93 125
94 126 // Updates graph
95 127 ui->widget->replot();
96 128 }
97 129
98 130 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
99 131 {
100 132 if (visitor) {
101 133 visitor->visit(this);
102 134 }
103 135 else {
104 136 qCCritical(LOG_VisualizationGraphWidget())
105 137 << tr("Can't visit widget : the visitor is null");
106 138 }
107 139 }
108 140
109 141 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
110 142 {
111 143 /// @todo : for the moment, a graph can always accomodate a variable
112 144 Q_UNUSED(variable);
113 145 return true;
114 146 }
115 147
116 148 QString VisualizationGraphWidget::name() const
117 149 {
118 150 return ui->graphNameLabel->text();
119 151 }
120 152
121 153 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
122 154 {
123 155 QMenu graphMenu{};
124 156
125 157 // Iterates on variables (unique keys)
126 158 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
127 159 end = impl->m_VariableToPlotMultiMap.cend();
128 160 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
129 161 // 'Remove variable' action
130 162 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
131 163 [ this, var = it->first ]() { removeVariable(var); });
132 164 }
133 165
134 166 if (!graphMenu.isEmpty()) {
135 167 graphMenu.exec(mapToGlobal(pos));
136 168 }
137 169 }
138 170
139 171 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
140 172 {
141 173
142 174 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::onRangeChanged");
143 175
144 176 for (auto it = impl->m_VariableToPlotMultiMap.cbegin();
145 177 it != impl->m_VariableToPlotMultiMap.cend(); ++it) {
146 178
147 179 auto variable = it->first;
148 180 auto dateTime = SqpDateTime{t2.lower, t2.upper};
149 181
150 182 if (!variable->contains(dateTime)) {
151 183
152 184 auto variableDateTimeWithTolerance = dateTime;
153 185 if (variable->intersect(dateTime)) {
154 186 auto variableDateTime = variable->dateTime();
155 187 if (variableDateTime.m_TStart < dateTime.m_TStart) {
156 188
157 189 auto diffEndToKeepDelta = dateTime.m_TEnd - variableDateTime.m_TEnd;
158 190 dateTime.m_TStart = variableDateTime.m_TStart + diffEndToKeepDelta;
159 191 // Tolerance have to be added to the right
160 192 // add 10% tolerance for right (end) side
161 193 auto tolerance = 0.1 * (dateTime.m_TEnd - dateTime.m_TStart);
162 194 variableDateTimeWithTolerance.m_TEnd += tolerance;
163 195 }
164 196 if (variableDateTime.m_TEnd > dateTime.m_TEnd) {
165 197 auto diffStartToKeepDelta = variableDateTime.m_TStart - dateTime.m_TStart;
166 198 dateTime.m_TEnd = variableDateTime.m_TEnd - diffStartToKeepDelta;
167 199 // Tolerance have to be added to the left
168 200 // add 10% tolerance for left (start) side
169 201 auto tolerance = 0.1 * (dateTime.m_TEnd - dateTime.m_TStart);
170 202 variableDateTimeWithTolerance.m_TStart -= tolerance;
171 203 }
172 204 }
173 205 else {
174 206 // add 10% tolerance for each side
175 207 auto tolerance = 0.1 * (dateTime.m_TEnd - dateTime.m_TStart);
176 208 variableDateTimeWithTolerance.m_TStart -= tolerance;
177 209 variableDateTimeWithTolerance.m_TEnd += tolerance;
178 210 }
179 211 variable->setDateTime(dateTime);
180 212
181 213 // CHangement detected, we need to ask controller to request data loading
182 214 emit requestDataLoading(variable, variableDateTimeWithTolerance);
183 215 }
184 216 }
185 217 }
186 218
187 219 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
188 220 {
189 221 auto zoomOrientations = QFlags<Qt::Orientation>{};
190 222
191 223 // Lambda that enables a zoom orientation if the key modifier related to this orientation
192 224 // has
193 225 // been pressed
194 226 auto enableOrientation
195 227 = [&zoomOrientations, event](const auto &orientation, const auto &modifier) {
196 228 auto orientationEnabled = event->modifiers().testFlag(modifier);
197 229 zoomOrientations.setFlag(orientation, orientationEnabled);
198 230 };
199 231 enableOrientation(Qt::Vertical, VERTICAL_ZOOM_MODIFIER);
200 232 enableOrientation(Qt::Horizontal, HORIZONTAL_ZOOM_MODIFIER);
201 233
202 234 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
203 235 }
204 236
205 237 void VisualizationGraphWidget::onDataCacheVariableUpdated()
206 238 {
207 239 // NOTE:
208 240 // We don't want to call the method for each component of a variable unitarily, but for
209 241 // all
210 242 // its components at once (eg its three components in the case of a vector).
211 243
212 244 // The unordered_multimap does not do this easily, so the question is whether to:
213 245 // - use an ordered_multimap and the algos of std to group the values by key
214 246 // - use a map (unique keys) and store as values directly the list of components
215 247
216 248 for (auto it = impl->m_VariableToPlotMultiMap.cbegin();
217 249 it != impl->m_VariableToPlotMultiMap.cend(); ++it) {
218 250 auto variable = it->first;
219 251 VisualizationGraphHelper::updateData(QVector<QCPAbstractPlottable *>{} << it->second,
220 252 variable->dataSeries(), variable->dateTime());
221 253 }
222 254 }
223 255
224 256 void VisualizationGraphWidget::updateDisplay(std::shared_ptr<Variable> variable)
225 257 {
226 258 auto abstractPlotableItPair = impl->m_VariableToPlotMultiMap.equal_range(variable);
227 259
228 260 auto abstractPlotableVect = QVector<QCPAbstractPlottable *>{};
229 261
230 262 for (auto it = abstractPlotableItPair.first; it != abstractPlotableItPair.second; ++it) {
231 263 abstractPlotableVect.push_back(it->second);
232 264 }
233 265
234 266 VisualizationGraphHelper::updateData(abstractPlotableVect, variable->dataSeries(),
235 267 variable->dateTime());
236 268 }
@@ -1,146 +1,146
1 1 #include "Visualization/operations/GenerateVariableMenuOperation.h"
2 2 #include "Visualization/operations/MenuBuilder.h"
3 3
4 4 #include "Visualization/VisualizationGraphWidget.h"
5 5 #include "Visualization/VisualizationTabWidget.h"
6 6 #include "Visualization/VisualizationZoneWidget.h"
7 7
8 8 #include <Variable/Variable.h>
9 9
10 10 #include <QMenu>
11 11 #include <QStack>
12 12
13 13 Q_LOGGING_CATEGORY(LOG_GenerateVariableMenuOperation, "GenerateVariableMenuOperation")
14 14
15 15 struct GenerateVariableMenuOperation::GenerateVariableMenuOperationPrivate {
16 16 explicit GenerateVariableMenuOperationPrivate(QMenu *menu, std::shared_ptr<Variable> variable)
17 17 : m_Variable{variable}, m_MenuBuilder{menu}
18 18 {
19 19 }
20 20
21 21 void visitRootEnter()
22 22 {
23 23 // Creates the root menu
24 24 m_MenuBuilder.addMenu(QObject::tr("Plot"));
25 25 }
26 26
27 27 void visitRootLeave()
28 28 {
29 29 // Closes the root menu
30 30 m_MenuBuilder.closeMenu();
31 31 }
32 32
33 33 void visitNodeEnter(const IVisualizationWidget &container)
34 34 {
35 35 // Opens a new menu associated to the node
36 36 m_MenuBuilder.addMenu(container.name());
37 37 }
38 38
39 39 template <typename ActionFun>
40 40 void visitNodeLeave(const IVisualizationWidget &container, const QString &actionName,
41 41 ActionFun actionFunction)
42 42 {
43 43 if (m_Variable && container.canDrop(*m_Variable)) {
44 44 m_MenuBuilder.addSeparator();
45 45 m_MenuBuilder.addAction(actionName, actionFunction);
46 46 }
47 47
48 48 // Closes the menu associated to the node
49 49 m_MenuBuilder.closeMenu();
50 50 }
51 51
52 52 template <typename ActionFun>
53 53 void visitLeaf(const IVisualizationWidget &container, const QString &actionName,
54 54 ActionFun actionFunction)
55 55 {
56 56 if (m_Variable && container.canDrop(*m_Variable)) {
57 57 m_MenuBuilder.addAction(actionName, actionFunction);
58 58 }
59 59 }
60 60
61 61 std::shared_ptr<Variable> m_Variable;
62 62 MenuBuilder m_MenuBuilder;
63 63 };
64 64
65 65 GenerateVariableMenuOperation::GenerateVariableMenuOperation(QMenu *menu,
66 66 std::shared_ptr<Variable> variable)
67 67 : impl{spimpl::make_unique_impl<GenerateVariableMenuOperationPrivate>(menu, variable)}
68 68 {
69 69 }
70 70
71 71 void GenerateVariableMenuOperation::visitEnter(VisualizationWidget *widget)
72 72 {
73 73 // VisualizationWidget is not intended to accommodate a variable
74 74 Q_UNUSED(widget)
75 75
76 76 impl->visitRootEnter();
77 77 }
78 78
79 79 void GenerateVariableMenuOperation::visitLeave(VisualizationWidget *widget)
80 80 {
81 81 // VisualizationWidget is not intended to accommodate a variable
82 82 Q_UNUSED(widget)
83 83
84 84 impl->visitRootLeave();
85 85 }
86 86
87 87 void GenerateVariableMenuOperation::visitEnter(VisualizationTabWidget *tabWidget)
88 88 {
89 89 if (tabWidget) {
90 90 impl->visitNodeEnter(*tabWidget);
91 91 }
92 92 else {
93 93 qCCritical(LOG_GenerateVariableMenuOperation(),
94 94 "Can't visit enter VisualizationTabWidget : the widget is null");
95 95 }
96 96 }
97 97
98 98 void GenerateVariableMenuOperation::visitLeave(VisualizationTabWidget *tabWidget)
99 99 {
100 100 if (tabWidget) {
101 101 impl->visitNodeLeave(
102 102 *tabWidget, QObject::tr("Open in a new zone"),
103 103 [ var = impl->m_Variable, tabWidget ]() { tabWidget->createZone(var); });
104 104 }
105 105 else {
106 106 qCCritical(LOG_GenerateVariableMenuOperation(),
107 107 "Can't visit leave VisualizationTabWidget : the widget is null");
108 108 }
109 109 }
110 110
111 111 void GenerateVariableMenuOperation::visitEnter(VisualizationZoneWidget *zoneWidget)
112 112 {
113 113 if (zoneWidget) {
114 114 impl->visitNodeEnter(*zoneWidget);
115 115 }
116 116 else {
117 117 qCCritical(LOG_GenerateVariableMenuOperation(),
118 118 "Can't visit enter VisualizationZoneWidget : the widget is null");
119 119 }
120 120 }
121 121
122 122 void GenerateVariableMenuOperation::visitLeave(VisualizationZoneWidget *zoneWidget)
123 123 {
124 124 if (zoneWidget) {
125 125 impl->visitNodeLeave(
126 126 *zoneWidget, QObject::tr("Open in a new graph"),
127 127 [ var = impl->m_Variable, zoneWidget ]() { zoneWidget->createGraph(var); });
128 128 }
129 129 else {
130 130 qCCritical(LOG_GenerateVariableMenuOperation(),
131 131 "Can't visit leave VisualizationZoneWidget : the widget is null");
132 132 }
133 133 }
134 134
135 135 void GenerateVariableMenuOperation::visit(VisualizationGraphWidget *graphWidget)
136 136 {
137 137 if (graphWidget) {
138 138 impl->visitLeaf(
139 139 *graphWidget, QObject::tr("Open in %1").arg(graphWidget->name()),
140 [ var = impl->m_Variable, graphWidget ]() { graphWidget->addVariable(var); });
140 [ var = impl->m_Variable, graphWidget ]() { graphWidget->addVariableUsingGraph(var); });
141 141 }
142 142 else {
143 143 qCCritical(LOG_GenerateVariableMenuOperation(),
144 144 "Can't visit VisualizationGraphWidget : the widget is null");
145 145 }
146 146 }
General Comments 0
You need to be logged in to leave comments. Login now