##// END OF EJS Templates
Adds a close button to a graph widget + calls close() method when clicked...
Alexandre Leroux -
r266:64be3fdecdc1
parent child
Show More
@@ -1,202 +1,200
1 #include "Visualization/VisualizationGraphWidget.h"
1 #include "Visualization/VisualizationGraphWidget.h"
2 #include "Visualization/IVisualizationWidgetVisitor.h"
2 #include "Visualization/IVisualizationWidgetVisitor.h"
3 #include "Visualization/VisualizationGraphHelper.h"
3 #include "Visualization/VisualizationGraphHelper.h"
4 #include "ui_VisualizationGraphWidget.h"
4 #include "ui_VisualizationGraphWidget.h"
5
5
6 #include <Data/ArrayData.h>
6 #include <Data/ArrayData.h>
7 #include <Data/IDataSeries.h>
7 #include <Data/IDataSeries.h>
8 #include <SqpApplication.h>
8 #include <SqpApplication.h>
9 #include <Variable/Variable.h>
9 #include <Variable/Variable.h>
10 #include <Variable/VariableController.h>
10 #include <Variable/VariableController.h>
11
11
12 #include <unordered_map>
12 #include <unordered_map>
13
13
14 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
14 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
15
15
16 namespace {
16 namespace {
17
17
18 /// Key pressed to enable zoom on horizontal axis
18 /// Key pressed to enable zoom on horizontal axis
19 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::NoModifier;
19 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::NoModifier;
20
20
21 /// Key pressed to enable zoom on vertical axis
21 /// Key pressed to enable zoom on vertical axis
22 const auto VERTICAL_ZOOM_MODIFIER = Qt::ControlModifier;
22 const auto VERTICAL_ZOOM_MODIFIER = Qt::ControlModifier;
23
23
24 } // namespace
24 } // namespace
25
25
26 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
26 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
27
27
28 // 1 variable -> n qcpplot
28 // 1 variable -> n qcpplot
29 std::unordered_multimap<std::shared_ptr<Variable>, QCPAbstractPlottable *>
29 std::unordered_multimap<std::shared_ptr<Variable>, QCPAbstractPlottable *>
30 m_VariableToPlotMultiMap;
30 m_VariableToPlotMultiMap;
31 };
31 };
32
32
33 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
33 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
34 : QWidget{parent},
34 : QWidget{parent},
35 ui{new Ui::VisualizationGraphWidget},
35 ui{new Ui::VisualizationGraphWidget},
36 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>()}
36 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>()}
37 {
37 {
38 ui->setupUi(this);
38 ui->setupUi(this);
39
39
40 // qcpplot title
40 ui->graphNameLabel->setText(name);
41 ui->widget->plotLayout()->insertRow(0);
41
42 ui->widget->plotLayout()->addElement(0, 0, new QCPTextElement{ui->widget, name});
42 // 'Close' options : widget is deleted when closed
43 setAttribute(Qt::WA_DeleteOnClose);
44 connect(ui->closeButton, &QToolButton::clicked, this, &VisualizationGraphWidget::close);
45 ui->closeButton->setIcon(sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton));
43
46
44 // Set qcpplot properties :
47 // Set qcpplot properties :
45 // - Drag (on x-axis) and zoom are enabled
48 // - Drag (on x-axis) and zoom are enabled
46 // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation
49 // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation
47 ui->widget->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom);
50 ui->widget->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom);
48 ui->widget->axisRect()->setRangeDrag(Qt::Horizontal);
51 ui->widget->axisRect()->setRangeDrag(Qt::Horizontal);
49 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
52 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
50 connect(ui->widget->xAxis, static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(
53 connect(ui->widget->xAxis, static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(
51 &QCPAxis::rangeChanged),
54 &QCPAxis::rangeChanged),
52 this, &VisualizationGraphWidget::onRangeChanged);
55 this, &VisualizationGraphWidget::onRangeChanged);
53 }
56 }
54
57
55
58
56 VisualizationGraphWidget::~VisualizationGraphWidget()
59 VisualizationGraphWidget::~VisualizationGraphWidget()
57 {
60 {
58 delete ui;
61 delete ui;
59 }
62 }
60
63
61 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable)
64 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable)
62 {
65 {
63 // Uses delegate to create the qcpplot components according to the variable
66 // Uses delegate to create the qcpplot components according to the variable
64 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
67 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
65
68
66 for (auto createdPlottable : qAsConst(createdPlottables)) {
69 for (auto createdPlottable : qAsConst(createdPlottables)) {
67 impl->m_VariableToPlotMultiMap.insert({variable, createdPlottable});
70 impl->m_VariableToPlotMultiMap.insert({variable, createdPlottable});
68 }
71 }
69
72
70 connect(variable.get(), SIGNAL(dataCacheUpdated()), this, SLOT(onDataCacheVariableUpdated()));
73 connect(variable.get(), SIGNAL(dataCacheUpdated()), this, SLOT(onDataCacheVariableUpdated()));
71 }
74 }
72
75
73 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
76 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
74 {
77 {
75 if (visitor) {
78 if (visitor) {
76 visitor->visit(this);
79 visitor->visit(this);
77 }
80 }
78 else {
81 else {
79 qCCritical(LOG_VisualizationGraphWidget())
82 qCCritical(LOG_VisualizationGraphWidget())
80 << tr("Can't visit widget : the visitor is null");
83 << tr("Can't visit widget : the visitor is null");
81 }
84 }
82 }
85 }
83
86
84 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
87 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
85 {
88 {
86 /// @todo : for the moment, a graph can always accomodate a variable
89 /// @todo : for the moment, a graph can always accomodate a variable
87 Q_UNUSED(variable);
90 Q_UNUSED(variable);
88 return true;
91 return true;
89 }
92 }
90
93
91 QString VisualizationGraphWidget::name() const
94 QString VisualizationGraphWidget::name() const
92 {
95 {
93 if (auto title = dynamic_cast<QCPTextElement *>(ui->widget->plotLayout()->elementAt(0))) {
96 return ui->graphNameLabel->text();
94 return title->text();
95 }
96 else {
97 return QString{};
98 }
99 }
97 }
100
98
101 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
99 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
102 {
100 {
103
101
104 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::onRangeChanged");
102 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::onRangeChanged");
105
103
106 for (auto it = impl->m_VariableToPlotMultiMap.cbegin();
104 for (auto it = impl->m_VariableToPlotMultiMap.cbegin();
107 it != impl->m_VariableToPlotMultiMap.cend(); ++it) {
105 it != impl->m_VariableToPlotMultiMap.cend(); ++it) {
108
106
109 auto variable = it->first;
107 auto variable = it->first;
110 qCInfo(LOG_VisualizationGraphWidget())
108 qCInfo(LOG_VisualizationGraphWidget())
111 << tr("TORM: VisualizationGraphWidget::onRangeChanged")
109 << tr("TORM: VisualizationGraphWidget::onRangeChanged")
112 << variable->dataSeries()->xAxisData()->size();
110 << variable->dataSeries()->xAxisData()->size();
113 auto dateTime = SqpDateTime{t2.lower, t2.upper};
111 auto dateTime = SqpDateTime{t2.lower, t2.upper};
114
112
115 if (!variable->contains(dateTime)) {
113 if (!variable->contains(dateTime)) {
116
114
117 auto variableDateTimeWithTolerance = dateTime;
115 auto variableDateTimeWithTolerance = dateTime;
118 if (variable->intersect(dateTime)) {
116 if (variable->intersect(dateTime)) {
119 auto variableDateTime = variable->dateTime();
117 auto variableDateTime = variable->dateTime();
120 if (variableDateTime.m_TStart < dateTime.m_TStart) {
118 if (variableDateTime.m_TStart < dateTime.m_TStart) {
121
119
122 auto diffEndToKeepDelta = dateTime.m_TEnd - variableDateTime.m_TEnd;
120 auto diffEndToKeepDelta = dateTime.m_TEnd - variableDateTime.m_TEnd;
123 dateTime.m_TStart = variableDateTime.m_TStart + diffEndToKeepDelta;
121 dateTime.m_TStart = variableDateTime.m_TStart + diffEndToKeepDelta;
124 // Tolerance have to be added to the right
122 // Tolerance have to be added to the right
125 // add 10% tolerance for right (end) side
123 // add 10% tolerance for right (end) side
126 auto tolerance = 0.1 * (dateTime.m_TEnd - dateTime.m_TStart);
124 auto tolerance = 0.1 * (dateTime.m_TEnd - dateTime.m_TStart);
127 variableDateTimeWithTolerance.m_TEnd += tolerance;
125 variableDateTimeWithTolerance.m_TEnd += tolerance;
128 }
126 }
129 if (variableDateTime.m_TEnd > dateTime.m_TEnd) {
127 if (variableDateTime.m_TEnd > dateTime.m_TEnd) {
130 auto diffStartToKeepDelta = dateTime.m_TStart - dateTime.m_TStart;
128 auto diffStartToKeepDelta = dateTime.m_TStart - dateTime.m_TStart;
131 dateTime.m_TEnd = variableDateTime.m_TEnd - diffStartToKeepDelta;
129 dateTime.m_TEnd = variableDateTime.m_TEnd - diffStartToKeepDelta;
132 // Tolerance have to be added to the left
130 // Tolerance have to be added to the left
133 // add 10% tolerance for left (start) side
131 // add 10% tolerance for left (start) side
134 auto tolerance = 0.1 * (dateTime.m_TEnd - dateTime.m_TStart);
132 auto tolerance = 0.1 * (dateTime.m_TEnd - dateTime.m_TStart);
135 variableDateTimeWithTolerance.m_TStart -= tolerance;
133 variableDateTimeWithTolerance.m_TStart -= tolerance;
136 }
134 }
137 }
135 }
138 else {
136 else {
139 // add 10% tolerance for each side
137 // add 10% tolerance for each side
140 auto tolerance = 0.1 * (dateTime.m_TEnd - dateTime.m_TStart);
138 auto tolerance = 0.1 * (dateTime.m_TEnd - dateTime.m_TStart);
141 variableDateTimeWithTolerance.m_TStart -= tolerance;
139 variableDateTimeWithTolerance.m_TStart -= tolerance;
142 variableDateTimeWithTolerance.m_TEnd += tolerance;
140 variableDateTimeWithTolerance.m_TEnd += tolerance;
143 }
141 }
144 variable->setDateTime(dateTime);
142 variable->setDateTime(dateTime);
145
143
146 // CHangement detected, we need to ask controller to request data loading
144 // CHangement detected, we need to ask controller to request data loading
147 sqpApp->variableController().requestDataLoading(variable,
145 sqpApp->variableController().requestDataLoading(variable,
148 variableDateTimeWithTolerance);
146 variableDateTimeWithTolerance);
149 }
147 }
150 }
148 }
151 }
149 }
152
150
153 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
151 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
154 {
152 {
155 auto zoomOrientations = QFlags<Qt::Orientation>{};
153 auto zoomOrientations = QFlags<Qt::Orientation>{};
156
154
157 // Lambda that enables a zoom orientation if the key modifier related to this orientation
155 // Lambda that enables a zoom orientation if the key modifier related to this orientation
158 // has
156 // has
159 // been pressed
157 // been pressed
160 auto enableOrientation
158 auto enableOrientation
161 = [&zoomOrientations, event](const auto &orientation, const auto &modifier) {
159 = [&zoomOrientations, event](const auto &orientation, const auto &modifier) {
162 auto orientationEnabled = event->modifiers().testFlag(modifier);
160 auto orientationEnabled = event->modifiers().testFlag(modifier);
163 zoomOrientations.setFlag(orientation, orientationEnabled);
161 zoomOrientations.setFlag(orientation, orientationEnabled);
164 };
162 };
165 enableOrientation(Qt::Vertical, VERTICAL_ZOOM_MODIFIER);
163 enableOrientation(Qt::Vertical, VERTICAL_ZOOM_MODIFIER);
166 enableOrientation(Qt::Horizontal, HORIZONTAL_ZOOM_MODIFIER);
164 enableOrientation(Qt::Horizontal, HORIZONTAL_ZOOM_MODIFIER);
167
165
168 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
166 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
169 }
167 }
170
168
171 void VisualizationGraphWidget::onDataCacheVariableUpdated()
169 void VisualizationGraphWidget::onDataCacheVariableUpdated()
172 {
170 {
173 // NOTE:
171 // NOTE:
174 // We don't want to call the method for each component of a variable unitarily, but for
172 // We don't want to call the method for each component of a variable unitarily, but for
175 // all
173 // all
176 // its components at once (eg its three components in the case of a vector).
174 // its components at once (eg its three components in the case of a vector).
177
175
178 // The unordered_multimap does not do this easily, so the question is whether to:
176 // The unordered_multimap does not do this easily, so the question is whether to:
179 // - use an ordered_multimap and the algos of std to group the values by key
177 // - use an ordered_multimap and the algos of std to group the values by key
180 // - use a map (unique keys) and store as values directly the list of components
178 // - use a map (unique keys) and store as values directly the list of components
181
179
182 for (auto it = impl->m_VariableToPlotMultiMap.cbegin();
180 for (auto it = impl->m_VariableToPlotMultiMap.cbegin();
183 it != impl->m_VariableToPlotMultiMap.cend(); ++it) {
181 it != impl->m_VariableToPlotMultiMap.cend(); ++it) {
184 auto variable = it->first;
182 auto variable = it->first;
185 VisualizationGraphHelper::updateData(QVector<QCPAbstractPlottable *>{} << it->second,
183 VisualizationGraphHelper::updateData(QVector<QCPAbstractPlottable *>{} << it->second,
186 variable->dataSeries(), variable->dateTime());
184 variable->dataSeries(), variable->dateTime());
187 }
185 }
188 }
186 }
189
187
190 void VisualizationGraphWidget::updateDisplay(std::shared_ptr<Variable> variable)
188 void VisualizationGraphWidget::updateDisplay(std::shared_ptr<Variable> variable)
191 {
189 {
192 auto abstractPlotableItPair = impl->m_VariableToPlotMultiMap.equal_range(variable);
190 auto abstractPlotableItPair = impl->m_VariableToPlotMultiMap.equal_range(variable);
193
191
194 auto abstractPlotableVect = QVector<QCPAbstractPlottable *>{};
192 auto abstractPlotableVect = QVector<QCPAbstractPlottable *>{};
195
193
196 for (auto it = abstractPlotableItPair.first; it != abstractPlotableItPair.second; ++it) {
194 for (auto it = abstractPlotableItPair.first; it != abstractPlotableItPair.second; ++it) {
197 abstractPlotableVect.push_back(it->second);
195 abstractPlotableVect.push_back(it->second);
198 }
196 }
199
197
200 VisualizationGraphHelper::updateData(abstractPlotableVect, variable->dataSeries(),
198 VisualizationGraphHelper::updateData(abstractPlotableVect, variable->dataSeries(),
201 variable->dateTime());
199 variable->dateTime());
202 }
200 }
@@ -1,32 +1,83
1 <?xml version="1.0" encoding="UTF-8"?>
1 <?xml version="1.0" encoding="UTF-8"?>
2 <ui version="4.0">
2 <ui version="4.0">
3 <class>VisualizationGraphWidget</class>
3 <class>VisualizationGraphWidget</class>
4 <widget class="QWidget" name="VisualizationGraphWidget">
4 <widget class="QWidget" name="VisualizationGraphWidget">
5 <property name="geometry">
5 <property name="geometry">
6 <rect>
6 <rect>
7 <x>0</x>
7 <x>0</x>
8 <y>0</y>
8 <y>0</y>
9 <width>400</width>
9 <width>400</width>
10 <height>300</height>
10 <height>300</height>
11 </rect>
11 </rect>
12 </property>
12 </property>
13 <property name="windowTitle">
13 <property name="windowTitle">
14 <string>Form</string>
14 <string>Form</string>
15 </property>
15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout">
16 <layout class="QVBoxLayout" name="verticalLayout">
17 <item>
17 <item>
18 <widget class="QCustomPlot" name="widget" native="true"/>
18 <widget class="QWidget" name="infobar" native="true">
19 <layout class="QHBoxLayout" name="horizontalLayout_2">
20 <property name="leftMargin">
21 <number>0</number>
22 </property>
23 <property name="topMargin">
24 <number>0</number>
25 </property>
26 <property name="rightMargin">
27 <number>0</number>
28 </property>
29 <property name="bottomMargin">
30 <number>0</number>
31 </property>
32 <item>
33 <widget class="QLabel" name="graphNameLabel">
34 <property name="styleSheet">
35 <string notr="true">font: 75 9pt &quot;MS Shell Dlg 2&quot;;</string>
36 </property>
37 <property name="text">
38 <string>TextLabel</string>
39 </property>
40 <property name="textFormat">
41 <enum>Qt::AutoText</enum>
42 </property>
43 <property name="alignment">
44 <set>Qt::AlignCenter</set>
45 </property>
46 </widget>
47 </item>
48 <item>
49 <widget class="QToolButton" name="closeButton">
50 <property name="styleSheet">
51 <string notr="true">background-color: transparent;</string>
52 </property>
53 <property name="text">
54 <string>Close</string>
55 </property>
56 </widget>
57 </item>
58 </layout>
59 </widget>
60 </item>
61 <item>
62 <widget class="QCustomPlot" name="widget" native="true">
63 <property name="sizePolicy">
64 <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
65 <horstretch>0</horstretch>
66 <verstretch>0</verstretch>
67 </sizepolicy>
68 </property>
69 </widget>
19 </item>
70 </item>
20 </layout>
71 </layout>
21 </widget>
72 </widget>
22 <customwidgets>
73 <customwidgets>
23 <customwidget>
74 <customwidget>
24 <class>QCustomPlot</class>
75 <class>QCustomPlot</class>
25 <extends>QWidget</extends>
76 <extends>QWidget</extends>
26 <header>Visualization/qcustomplot.h</header>
77 <header>Visualization/qcustomplot.h</header>
27 <container>1</container>
78 <container>1</container>
28 </customwidget>
79 </customwidget>
29 </customwidgets>
80 </customwidgets>
30 <resources/>
81 <resources/>
31 <connections/>
82 <connections/>
32 </ui>
83 </ui>
General Comments 0
You need to be logged in to leave comments. Login now