##// END OF EJS Templates
Sets sames margin sides for graphs in a same zone
Alexandre Leroux -
r671:2d96f9af06b9
parent child
Show More
@@ -0,0 +1,24
1 #ifndef SCIQLOP_IGRAPHSYNCHRONIZER_H
2 #define SCIQLOP_IGRAPHSYNCHRONIZER_H
3
4 class VisualizationGraphWidget;
5
6 /**
7 * @brief The IVisualizationSynchronizer interface represents a delegate used to manage the
8 * synchronization between graphs, applying them processes or properties to ensure their
9 * synchronization
10 */
11 class IGraphSynchronizer {
12
13 public:
14 virtual ~IGraphSynchronizer() = default;
15
16 /**
17 * Adds a graph as a new synchronized element, and sets its properties so that its
18 * synchronization is maintained with all other graphs managed by the synchonizer
19 * @param graph the graph to add in the synchronization
20 */
21 virtual void addGraph(VisualizationGraphWidget &graph) const = 0;
22 };
23
24 #endif // SCIQLOP_IGRAPHSYNCHRONIZER_H
@@ -0,0 +1,26
1 #ifndef SCIQLOP_QCUSTOMPLOTSYNCHRONIZER_H
2 #define SCIQLOP_QCUSTOMPLOTSYNCHRONIZER_H
3
4 #include "Visualization/IGraphSynchronizer.h"
5
6 #include <Common/spimpl.h>
7
8 /**
9 * @brief The QCustomPlotSynchronizer class is an implementation of IGraphSynchronizer that handles
10 * graphs using QCustomPlot elements
11 * @sa IGraphSynchronizer
12 * @sa QCustomPlot
13 */
14 class QCustomPlotSynchronizer : public IGraphSynchronizer {
15 public:
16 explicit QCustomPlotSynchronizer();
17
18 /// @sa IGraphSynchronizer::addGraph()
19 virtual void addGraph(VisualizationGraphWidget &graph) const override;
20
21 private:
22 class QCustomPlotSynchronizerPrivate;
23 spimpl::unique_impl_ptr<QCustomPlotSynchronizerPrivate> impl;
24 };
25
26 #endif // SCIQLOP_QCUSTOMPLOTSYNCHRONIZER_H
@@ -0,0 +1,30
1 #include "Visualization/QCustomPlotSynchronizer.h"
2
3 #include "Visualization/VisualizationGraphWidget.h"
4 #include "Visualization/qcustomplot.h"
5
6 struct QCustomPlotSynchronizer::QCustomPlotSynchronizerPrivate {
7 explicit QCustomPlotSynchronizerPrivate()
8 : m_MarginGroup{std::make_unique<QCPMarginGroup>(nullptr)}
9 {
10 }
11
12 /// Sets the same margin sides for all added plot elements
13 std::unique_ptr<QCPMarginGroup> m_MarginGroup;
14 };
15
16 QCustomPlotSynchronizer::QCustomPlotSynchronizer()
17 : impl{spimpl::make_unique_impl<QCustomPlotSynchronizerPrivate>()}
18 {
19 }
20
21 void QCustomPlotSynchronizer::addGraph(VisualizationGraphWidget &graph) const
22 {
23 // Adds all plot elements of the graph to the margin group: all these elements will then have
24 // the same margin sides
25 auto &plot = graph.plot();
26 for (auto axisRect : plot.axisRects()) {
27 // Sames margin sides at left and right
28 axisRect->setMarginGroup(QCP::msLeft | QCP::msRight, impl->m_MarginGroup.get());
29 }
30 }
@@ -1,93 +1,94
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 QCustomPlot;
17 17 class SqpRange;
18 18 class Variable;
19 19
20 20 namespace Ui {
21 21 class VisualizationGraphWidget;
22 22 } // namespace Ui
23 23
24 24 class VisualizationGraphWidget : public QWidget, public IVisualizationWidget {
25 25 Q_OBJECT
26 26
27 friend class QCustomPlotSynchronizer;
27 28 friend class VisualizationGraphRenderingDelegate;
28 29
29 30 public:
30 31 explicit VisualizationGraphWidget(const QString &name = {}, QWidget *parent = 0);
31 32 virtual ~VisualizationGraphWidget();
32 33
33 34 /// If acquisition isn't enable, requestDataLoading signal cannot be emit
34 35 void enableAcquisition(bool enable);
35 36
36 37 void addVariable(std::shared_ptr<Variable> variable, SqpRange range);
37 38
38 39 /// Removes a variable from the graph
39 40 void removeVariable(std::shared_ptr<Variable> variable) noexcept;
40 41
41 42 void setRange(std::shared_ptr<Variable> variable, const SqpRange &range);
42 43 void setYRange(const SqpRange &range);
43 44 SqpRange graphRange() const noexcept;
44 45 void setGraphRange(const SqpRange &range);
45 46
46 47 // IVisualizationWidget interface
47 48 void accept(IVisualizationWidgetVisitor *visitor) override;
48 49 bool canDrop(const Variable &variable) const override;
49 50 bool contains(const Variable &variable) const override;
50 51 QString name() const override;
51 52
52 53
53 54 signals:
54 55 void synchronize(const SqpRange &range, const SqpRange &oldRange);
55 56 void requestDataLoading(QVector<std::shared_ptr<Variable> > variable, const SqpRange &range,
56 57 const SqpRange &oldRange, bool synchronise);
57 58
58 59 void variableAdded(std::shared_ptr<Variable> var);
59 60
60 61 protected:
61 62 void enterEvent(QEvent *event) override;
62 63 void leaveEvent(QEvent *event) override;
63 64
64 65 QCustomPlot &plot() noexcept;
65 66
66 67 private:
67 68 Ui::VisualizationGraphWidget *ui;
68 69
69 70 class VisualizationGraphWidgetPrivate;
70 71 spimpl::unique_impl_ptr<VisualizationGraphWidgetPrivate> impl;
71 72
72 73 private slots:
73 74 /// Slot called when right clicking on the graph (displays a menu)
74 75 void onGraphMenuRequested(const QPoint &pos) noexcept;
75 76
76 77 /// Rescale the X axe to range parameter
77 78 void onRangeChanged(const QCPRange &t1, const QCPRange &t2);
78 79
79 80 /// Slot called when a mouse move was made
80 81 void onMouseMove(QMouseEvent *event) noexcept;
81 82 /// Slot called when a mouse wheel was made, to perform some processing before the zoom is done
82 83 void onMouseWheel(QWheelEvent *event) noexcept;
83 84 /// Slot called when a mouse press was made, to activate the calibration of a graph
84 85 void onMousePress(QMouseEvent *event) noexcept;
85 86 /// Slot called when a mouse release was made, to deactivate the calibration of a graph
86 87 void onMouseRelease(QMouseEvent *event) noexcept;
87 88
88 89 void onDataCacheVariableUpdated();
89 90
90 91 void onUpdateVarDisplaying(std::shared_ptr<Variable> variable, const SqpRange &range);
91 92 };
92 93
93 94 #endif // SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
@@ -1,75 +1,76
1 1
2 2 gui_moc_headers = [
3 3 'include/DataSource/DataSourceWidget.h',
4 4 'include/Settings/SqpSettingsDialog.h',
5 5 'include/Settings/SqpSettingsGeneralWidget.h',
6 6 'include/SidePane/SqpSidePane.h',
7 7 'include/SqpApplication.h',
8 8 'include/TimeWidget/TimeWidget.h',
9 9 'include/Variable/VariableInspectorWidget.h',
10 10 'include/Visualization/qcustomplot.h',
11 11 'include/Visualization/VisualizationGraphWidget.h',
12 12 'include/Visualization/VisualizationTabWidget.h',
13 13 'include/Visualization/VisualizationWidget.h',
14 14 'include/Visualization/VisualizationZoneWidget.h'
15 15 ]
16 16
17 17 gui_ui_files = [
18 18 'ui/DataSource/DataSourceWidget.ui',
19 19 'ui/Settings/SqpSettingsDialog.ui',
20 20 'ui/Settings/SqpSettingsGeneralWidget.ui',
21 21 'ui/SidePane/SqpSidePane.ui',
22 22 'ui/TimeWidget/TimeWidget.ui',
23 23 'ui/Variable/VariableInspectorWidget.ui',
24 24 'ui/Variable/VariableMenuHeaderWidget.ui',
25 25 'ui/Visualization/VisualizationGraphWidget.ui',
26 26 'ui/Visualization/VisualizationTabWidget.ui',
27 27 'ui/Visualization/VisualizationWidget.ui',
28 28 'ui/Visualization/VisualizationZoneWidget.ui'
29 29 ]
30 30
31 31 gui_qresources = ['resources/sqpguiresources.qrc']
32 32
33 33 gui_moc_files = qt5.preprocess(moc_headers : gui_moc_headers,
34 34 ui_files : gui_ui_files,
35 35 qresources : gui_qresources)
36 36
37 37 gui_sources = [
38 38 'src/SqpApplication.cpp',
39 39 'src/Common/ColorUtils.cpp',
40 40 'src/DataSource/DataSourceTreeWidgetItem.cpp',
41 41 'src/DataSource/DataSourceTreeWidgetHelper.cpp',
42 42 'src/DataSource/DataSourceWidget.cpp',
43 43 'src/Settings/SqpSettingsDialog.cpp',
44 44 'src/Settings/SqpSettingsGeneralWidget.cpp',
45 45 'src/SidePane/SqpSidePane.cpp',
46 46 'src/TimeWidget/TimeWidget.cpp',
47 47 'src/Variable/VariableInspectorWidget.cpp',
48 48 'src/Variable/VariableMenuHeaderWidget.cpp',
49 49 'src/Visualization/VisualizationGraphHelper.cpp',
50 50 'src/Visualization/VisualizationGraphRenderingDelegate.cpp',
51 51 'src/Visualization/VisualizationGraphWidget.cpp',
52 52 'src/Visualization/VisualizationTabWidget.cpp',
53 53 'src/Visualization/VisualizationWidget.cpp',
54 54 'src/Visualization/VisualizationZoneWidget.cpp',
55 55 'src/Visualization/qcustomplot.cpp',
56 'src/Visualization/QCustomPlotSynchronizer.cpp',
56 57 'src/Visualization/operations/GenerateVariableMenuOperation.cpp',
57 58 'src/Visualization/operations/MenuBuilder.cpp',
58 59 'src/Visualization/operations/RemoveVariableOperation.cpp',
59 60 'src/Visualization/operations/RescaleAxeOperation.cpp'
60 61 ]
61 62
62 63 gui_inc = include_directories(['include'])
63 64
64 65 sciqlop_gui_lib = library('sciqlopgui',
65 66 gui_sources,
66 67 gui_moc_files,
67 68 include_directories : [gui_inc],
68 69 dependencies : [ qt5printsupport, qt5gui, qt5widgets, qt5svg, sciqlop_core],
69 70 install : true
70 71 )
71 72
72 73 sciqlop_gui = declare_dependency(link_with : sciqlop_gui_lib,
73 74 include_directories : gui_inc,
74 75 dependencies : [qt5printsupport, qt5gui, qt5widgets, qt5svg, sciqlop_core])
75 76
@@ -1,260 +1,268
1 1 #include "Visualization/VisualizationZoneWidget.h"
2 2
3
4 3 #include "Visualization/IVisualizationWidgetVisitor.h"
4 #include "Visualization/QCustomPlotSynchronizer.h"
5 5 #include "Visualization/VisualizationGraphWidget.h"
6 6 #include "ui_VisualizationZoneWidget.h"
7 7
8 8 #include <Data/SqpRange.h>
9 9 #include <Variable/Variable.h>
10 10 #include <Variable/VariableController.h>
11 11
12 12 #include <QUuid>
13 13 #include <SqpApplication.h>
14 14 #include <cmath>
15 15
16 16 Q_LOGGING_CATEGORY(LOG_VisualizationZoneWidget, "VisualizationZoneWidget")
17 17
18 18 namespace {
19 19
20 20 /// Minimum height for graph added in zones (in pixels)
21 21 const auto GRAPH_MINIMUM_HEIGHT = 300;
22 22
23 23 /// Generates a default name for a new graph, according to the number of graphs already displayed in
24 24 /// the zone
25 25 QString defaultGraphName(const QLayout &layout)
26 26 {
27 27 auto count = 0;
28 28 for (auto i = 0; i < layout.count(); ++i) {
29 29 if (dynamic_cast<VisualizationGraphWidget *>(layout.itemAt(i)->widget())) {
30 30 count++;
31 31 }
32 32 }
33 33
34 34 return QObject::tr("Graph %1").arg(count + 1);
35 35 }
36 36
37 37 } // namespace
38 38
39 39 struct VisualizationZoneWidget::VisualizationZoneWidgetPrivate {
40 40
41 explicit VisualizationZoneWidgetPrivate() : m_SynchronisationGroupId{QUuid::createUuid()} {}
41 explicit VisualizationZoneWidgetPrivate()
42 : m_SynchronisationGroupId{QUuid::createUuid()},
43 m_Synchronizer{std::make_unique<QCustomPlotSynchronizer>()}
44 {
45 }
42 46 QUuid m_SynchronisationGroupId;
47 std::unique_ptr<IGraphSynchronizer> m_Synchronizer;
43 48 };
44 49
45 50 VisualizationZoneWidget::VisualizationZoneWidget(const QString &name, QWidget *parent)
46 51 : QWidget{parent},
47 52 ui{new Ui::VisualizationZoneWidget},
48 53 impl{spimpl::make_unique_impl<VisualizationZoneWidgetPrivate>()}
49 54 {
50 55 ui->setupUi(this);
51 56
52 57 ui->zoneNameLabel->setText(name);
53 58
54 59 // 'Close' options : widget is deleted when closed
55 60 setAttribute(Qt::WA_DeleteOnClose);
56 61 connect(ui->closeButton, &QToolButton::clicked, this, &VisualizationZoneWidget::close);
57 62 ui->closeButton->setIcon(sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton));
58 63
59 64 // Synchronisation id
60 65 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronizationGroupId",
61 66 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
62 67 }
63 68
64 69 VisualizationZoneWidget::~VisualizationZoneWidget()
65 70 {
66 71 delete ui;
67 72 }
68 73
69 74 void VisualizationZoneWidget::addGraph(VisualizationGraphWidget *graphWidget)
70 75 {
76 // Synchronize new graph with others in the zone
77 impl->m_Synchronizer->addGraph(*graphWidget);
78
71 79 ui->visualizationZoneFrame->layout()->addWidget(graphWidget);
72 80 }
73 81
74 82 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable)
75 83 {
76 84 auto graphWidget = new VisualizationGraphWidget{
77 85 defaultGraphName(*ui->visualizationZoneFrame->layout()), this};
78 86
79 87
80 88 // Set graph properties
81 89 graphWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
82 90 graphWidget->setMinimumHeight(GRAPH_MINIMUM_HEIGHT);
83 91
84 92
85 93 // Lambda to synchronize zone widget
86 94 auto synchronizeZoneWidget = [this, graphWidget](const SqpRange &graphRange,
87 95 const SqpRange &oldGraphRange) {
88 96
89 97 auto zoomType = VariableController::getZoomType(graphRange, oldGraphRange);
90 98 auto frameLayout = ui->visualizationZoneFrame->layout();
91 99 for (auto i = 0; i < frameLayout->count(); ++i) {
92 100 auto graphChild
93 101 = dynamic_cast<VisualizationGraphWidget *>(frameLayout->itemAt(i)->widget());
94 102 if (graphChild && (graphChild != graphWidget)) {
95 103
96 104 auto graphChildRange = graphChild->graphRange();
97 105 switch (zoomType) {
98 106 case AcquisitionZoomType::ZoomIn: {
99 107 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
100 108 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
101 109 graphChildRange.m_TStart += deltaLeft;
102 110 graphChildRange.m_TEnd -= deltaRight;
103 111 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomIn");
104 112 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
105 113 << deltaLeft;
106 114 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
107 115 << deltaRight;
108 116 qCDebug(LOG_VisualizationZoneWidget())
109 117 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
110 118
111 119 break;
112 120 }
113 121
114 122 case AcquisitionZoomType::ZoomOut: {
115 123 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomOut");
116 124 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
117 125 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
118 126 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
119 127 << deltaLeft;
120 128 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
121 129 << deltaRight;
122 130 qCDebug(LOG_VisualizationZoneWidget())
123 131 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
124 132 graphChildRange.m_TStart -= deltaLeft;
125 133 graphChildRange.m_TEnd += deltaRight;
126 134 break;
127 135 }
128 136 case AcquisitionZoomType::PanRight: {
129 137 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: PanRight");
130 138 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
131 139 graphChildRange.m_TStart += deltaRight;
132 140 graphChildRange.m_TEnd += deltaRight;
133 141 qCDebug(LOG_VisualizationZoneWidget())
134 142 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
135 143 break;
136 144 }
137 145 case AcquisitionZoomType::PanLeft: {
138 146 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: PanLeft");
139 147 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
140 148 graphChildRange.m_TStart -= deltaLeft;
141 149 graphChildRange.m_TEnd -= deltaLeft;
142 150 break;
143 151 }
144 152 case AcquisitionZoomType::Unknown: {
145 153 qCDebug(LOG_VisualizationZoneWidget())
146 154 << tr("Impossible to synchronize: zoom type unknown");
147 155 break;
148 156 }
149 157 default:
150 158 qCCritical(LOG_VisualizationZoneWidget())
151 159 << tr("Impossible to synchronize: zoom type not take into account");
152 160 // No action
153 161 break;
154 162 }
155 163 graphChild->enableAcquisition(false);
156 164 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: Range before: ")
157 165 << graphChild->graphRange();
158 166 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: Range after : ")
159 167 << graphChildRange;
160 168 qCDebug(LOG_VisualizationZoneWidget())
161 169 << tr("TORM: child dt") << graphChildRange.m_TEnd - graphChildRange.m_TStart;
162 170 graphChild->setGraphRange(graphChildRange);
163 171 graphChild->enableAcquisition(true);
164 172 }
165 173 }
166 174 };
167 175
168 176 // connection for synchronization
169 177 connect(graphWidget, &VisualizationGraphWidget::synchronize, synchronizeZoneWidget);
170 178 connect(graphWidget, &VisualizationGraphWidget::variableAdded, this,
171 179 &VisualizationZoneWidget::onVariableAdded);
172 180
173 181 auto range = SqpRange{};
174 182
175 183 // Apply visitor to graph children
176 184 auto layout = ui->visualizationZoneFrame->layout();
177 185 if (layout->count() > 0) {
178 186 // Case of a new graph in a existant zone
179 187 if (auto visualizationGraphWidget
180 188 = dynamic_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
181 189 range = visualizationGraphWidget->graphRange();
182 190 }
183 191 }
184 192 else {
185 193 // Case of a new graph as the first of the zone
186 194 range = variable->range();
187 195 }
188 196
189 197 this->addGraph(graphWidget);
190 198
191 199 graphWidget->addVariable(variable, range);
192 200
193 201 // get y using variable range
194 202 if (auto dataSeries = variable->dataSeries()) {
195 203 dataSeries->lockRead();
196 204 auto valuesBounds
197 205 = dataSeries->valuesBounds(variable->range().m_TStart, variable->range().m_TEnd);
198 206 auto end = dataSeries->cend();
199 207 if (valuesBounds.first != end && valuesBounds.second != end) {
200 208 auto rangeValue = [](const auto &value) { return std::isnan(value) ? 0. : value; };
201 209
202 210 auto minValue = rangeValue(valuesBounds.first->minValue());
203 211 auto maxValue = rangeValue(valuesBounds.second->maxValue());
204 212
205 213 graphWidget->setYRange(SqpRange{minValue, maxValue});
206 214 }
207 215 dataSeries->unlock();
208 216 }
209 217
210 218 return graphWidget;
211 219 }
212 220
213 221 void VisualizationZoneWidget::accept(IVisualizationWidgetVisitor *visitor)
214 222 {
215 223 if (visitor) {
216 224 visitor->visitEnter(this);
217 225
218 226 // Apply visitor to graph children
219 227 auto layout = ui->visualizationZoneFrame->layout();
220 228 for (auto i = 0; i < layout->count(); ++i) {
221 229 if (auto item = layout->itemAt(i)) {
222 230 // Widgets different from graphs are not visited (no action)
223 231 if (auto visualizationGraphWidget
224 232 = dynamic_cast<VisualizationGraphWidget *>(item->widget())) {
225 233 visualizationGraphWidget->accept(visitor);
226 234 }
227 235 }
228 236 }
229 237
230 238 visitor->visitLeave(this);
231 239 }
232 240 else {
233 241 qCCritical(LOG_VisualizationZoneWidget()) << tr("Can't visit widget : the visitor is null");
234 242 }
235 243 }
236 244
237 245 bool VisualizationZoneWidget::canDrop(const Variable &variable) const
238 246 {
239 247 // A tab can always accomodate a variable
240 248 Q_UNUSED(variable);
241 249 return true;
242 250 }
243 251
244 252 bool VisualizationZoneWidget::contains(const Variable &variable) const
245 253 {
246 254 Q_UNUSED(variable);
247 255 return false;
248 256 }
249 257
250 258 QString VisualizationZoneWidget::name() const
251 259 {
252 260 return ui->zoneNameLabel->text();
253 261 }
254 262
255 263 void VisualizationZoneWidget::onVariableAdded(std::shared_ptr<Variable> variable)
256 264 {
257 265 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronized",
258 266 Qt::QueuedConnection, Q_ARG(std::shared_ptr<Variable>, variable),
259 267 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
260 268 }
General Comments 0
You need to be logged in to leave comments. Login now