##// END OF EJS Templates
Refactors VisualizationGraphWidget::setYRange()...
Alexandre Leroux -
r903:8f094eaa3c04
parent child
Show More
@@ -1,39 +1,41
1 1 #ifndef SCIQLOP_VISUALIZATIONGRAPHHELPER_H
2 2 #define SCIQLOP_VISUALIZATIONGRAPHHELPER_H
3 3
4 4 #include "Visualization/VisualizationDefs.h"
5 5
6 6 #include <Data/SqpRange.h>
7 7
8 8 #include <QLoggingCategory>
9 9 #include <QVector>
10 10
11 11 #include <memory>
12 12
13 13 Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationGraphHelper)
14 14
15 15 class IDataSeries;
16 16 class QCPAbstractPlottable;
17 17 class QCustomPlot;
18 18 class Variable;
19 19
20 20 /**
21 21 * @brief The VisualizationGraphHelper class aims to create the QCustomPlot components relative to a
22 22 * variable, depending on the data series of this variable
23 23 */
24 24 struct VisualizationGraphHelper {
25 25 /**
26 26 * Creates (if possible) the QCustomPlot components relative to the variable passed in
27 27 * parameter, and adds these to the plot passed in parameter.
28 28 * @param variable the variable for which to create the components
29 29 * @param plot the plot in which to add the created components. It takes ownership of these
30 30 * components.
31 31 * @return the list of the components created
32 32 */
33 33 static PlottablesMap create(std::shared_ptr<Variable> variable, QCustomPlot &plot) noexcept;
34 34
35 35 static void updateData(PlottablesMap &plottables, std::shared_ptr<IDataSeries> dataSeries,
36 36 const SqpRange &dateTime);
37
38 static void setYAxisRange(std::shared_ptr<Variable> variable, QCustomPlot &plot) noexcept;
37 39 };
38 40
39 41 #endif // SCIQLOP_VISUALIZATIONGRAPHHELPER_H
@@ -1,108 +1,109
1 1 #ifndef SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
2 2 #define SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
3 3
4 4 #include "Visualization/IVisualizationWidget.h"
5 5 #include "Visualization/VisualizationDragWidget.h"
6 6
7 7 #include <QLoggingCategory>
8 8 #include <QWidget>
9 9
10 10 #include <memory>
11 11
12 12 #include <Common/spimpl.h>
13 13
14 14 Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationGraphWidget)
15 15
16 16 class QCPRange;
17 17 class QCustomPlot;
18 18 class SqpRange;
19 19 class Variable;
20 20 class VisualizationZoneWidget;
21 21
22 22 namespace Ui {
23 23 class VisualizationGraphWidget;
24 24 } // namespace Ui
25 25
26 26 class VisualizationGraphWidget : public VisualizationDragWidget, public IVisualizationWidget {
27 27 Q_OBJECT
28 28
29 29 friend class QCustomPlotSynchronizer;
30 30 friend class VisualizationGraphRenderingDelegate;
31 31
32 32 public:
33 33 explicit VisualizationGraphWidget(const QString &name = {}, QWidget *parent = 0);
34 34 virtual ~VisualizationGraphWidget();
35 35
36 36 VisualizationZoneWidget *parentZoneWidget() const noexcept;
37 37
38 38 /// If acquisition isn't enable, requestDataLoading signal cannot be emit
39 39 void enableAcquisition(bool enable);
40 40
41 41 void addVariable(std::shared_ptr<Variable> variable, SqpRange range);
42 42
43 43 /// Removes a variable from the graph
44 44 void removeVariable(std::shared_ptr<Variable> variable) noexcept;
45 45
46 46 /// Returns the list of all variables used in the graph
47 47 QList<std::shared_ptr<Variable> > variables() const;
48 48
49 void setYRange(const SqpRange &range);
49 /// Sets the y-axis range based on the data of a variable
50 void setYRange(std::shared_ptr<Variable> variable);
50 51 SqpRange graphRange() const noexcept;
51 52 void setGraphRange(const SqpRange &range);
52 53
53 54 // IVisualizationWidget interface
54 55 void accept(IVisualizationWidgetVisitor *visitor) override;
55 56 bool canDrop(const Variable &variable) const override;
56 57 bool contains(const Variable &variable) const override;
57 58 QString name() const override;
58 59
59 60 // VisualisationDragWidget
60 61 QMimeData *mimeData() const override;
61 62 bool isDragAllowed() const override;
62 63 void highlightForMerge(bool highlighted) override;
63 64
64 65 signals:
65 66 void synchronize(const SqpRange &range, const SqpRange &oldRange);
66 67 void requestDataLoading(QVector<std::shared_ptr<Variable> > variable, const SqpRange &range,
67 68 bool synchronise);
68 69
69 70 /// Signal emitted when the variable is about to be removed from the graph
70 71 void variableAboutToBeRemoved(std::shared_ptr<Variable> var);
71 72 /// Signal emitted when the variable has been added to the graph
72 73 void variableAdded(std::shared_ptr<Variable> var);
73 74
74 75 protected:
75 76 void closeEvent(QCloseEvent *event) override;
76 77 void enterEvent(QEvent *event) override;
77 78 void leaveEvent(QEvent *event) override;
78 79
79 80 QCustomPlot &plot() noexcept;
80 81
81 82 private:
82 83 Ui::VisualizationGraphWidget *ui;
83 84
84 85 class VisualizationGraphWidgetPrivate;
85 86 spimpl::unique_impl_ptr<VisualizationGraphWidgetPrivate> impl;
86 87
87 88 private slots:
88 89 /// Slot called when right clicking on the graph (displays a menu)
89 90 void onGraphMenuRequested(const QPoint &pos) noexcept;
90 91
91 92 /// Rescale the X axe to range parameter
92 93 void onRangeChanged(const QCPRange &t1, const QCPRange &t2);
93 94
94 95 /// Slot called when a mouse move was made
95 96 void onMouseMove(QMouseEvent *event) noexcept;
96 97 /// Slot called when a mouse wheel was made, to perform some processing before the zoom is done
97 98 void onMouseWheel(QWheelEvent *event) noexcept;
98 99 /// Slot called when a mouse press was made, to activate the calibration of a graph
99 100 void onMousePress(QMouseEvent *event) noexcept;
100 101 /// Slot called when a mouse release was made, to deactivate the calibration of a graph
101 102 void onMouseRelease(QMouseEvent *event) noexcept;
102 103
103 104 void onDataCacheVariableUpdated();
104 105
105 106 void onUpdateVarDisplaying(std::shared_ptr<Variable> variable, const SqpRange &range);
106 107 };
107 108
108 109 #endif // SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
@@ -1,204 +1,247
1 1 #include "Visualization/VisualizationGraphHelper.h"
2 2 #include "Visualization/qcustomplot.h"
3 3
4 4 #include <Common/ColorUtils.h>
5 5
6 6 #include <Data/ScalarSeries.h>
7 7 #include <Data/VectorSeries.h>
8 8
9 9 #include <Variable/Variable.h>
10 10
11 11 Q_LOGGING_CATEGORY(LOG_VisualizationGraphHelper, "VisualizationGraphHelper")
12 12
13 13 namespace {
14 14
15 15 class SqpDataContainer : public QCPGraphDataContainer {
16 16 public:
17 17 void appendGraphData(const QCPGraphData &data) { mData.append(data); }
18 18 };
19 19
20 20 /**
21 21 * Struct used to create plottables, depending on the type of the data series from which to create
22 22 * them
23 23 * @tparam T the data series' type
24 24 * @remarks Default implementation can't create plottables
25 25 */
26 26 template <typename T, typename Enabled = void>
27 27 struct PlottablesCreator {
28 28 static PlottablesMap createPlottables(T &, QCustomPlot &)
29 29 {
30 30 qCCritical(LOG_DataSeries())
31 31 << QObject::tr("Can't create plottables: unmanaged data series type");
32 32 return {};
33 33 }
34 34 };
35 35
36 36 /**
37 37 * Specialization of PlottablesCreator for scalars and vectors
38 38 * @sa ScalarSeries
39 39 * @sa VectorSeries
40 40 */
41 41 template <typename T>
42 42 struct PlottablesCreator<T,
43 43 typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value
44 44 or std::is_base_of<VectorSeries, T>::value> > {
45 45 static PlottablesMap createPlottables(T &dataSeries, QCustomPlot &plot)
46 46 {
47 47 PlottablesMap result{};
48 48
49 49 // Gets the number of components of the data series
50 50 dataSeries.lockRead();
51 51 auto componentCount = dataSeries.valuesData()->componentCount();
52 52 dataSeries.unlock();
53 53
54 54 auto colors = ColorUtils::colors(Qt::blue, Qt::red, componentCount);
55 55
56 56 // For each component of the data series, creates a QCPGraph to add to the plot
57 57 for (auto i = 0; i < componentCount; ++i) {
58 58 auto graph = plot.addGraph();
59 59 graph->setPen(QPen{colors.at(i)});
60 60
61 61 result.insert({i, graph});
62 62 }
63 63
64 64 plot.replot();
65 65
66 66 return result;
67 67 }
68 68 };
69 69
70 70 /**
71 71 * Struct used to update plottables, depending on the type of the data series from which to update
72 72 * them
73 73 * @tparam T the data series' type
74 74 * @remarks Default implementation can't update plottables
75 75 */
76 76 template <typename T, typename Enabled = void>
77 77 struct PlottablesUpdater {
78 static void setPlotYAxisRange(T &, const SqpRange &, QCustomPlot &)
79 {
80 qCCritical(LOG_DataSeries())
81 << QObject::tr("Can't set plot y-axis range: unmanaged data series type");
82 }
83
78 84 static void updatePlottables(T &, PlottablesMap &, const SqpRange &, bool)
79 85 {
80 86 qCCritical(LOG_DataSeries())
81 87 << QObject::tr("Can't update plottables: unmanaged data series type");
82 88 }
83 89 };
84 90
85 91 /**
86 92 * Specialization of PlottablesUpdater for scalars and vectors
87 93 * @sa ScalarSeries
88 94 * @sa VectorSeries
89 95 */
90 96 template <typename T>
91 97 struct PlottablesUpdater<T,
92 98 typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value
93 99 or std::is_base_of<VectorSeries, T>::value> > {
100 static void setPlotYAxisRange(T &dataSeries, const SqpRange &xAxisRange, QCustomPlot &plot)
101 {
102 auto minValue = 0., maxValue = 0.;
103
104 dataSeries.lockRead();
105 auto valuesBounds = dataSeries.valuesBounds(xAxisRange.m_TStart, xAxisRange.m_TEnd);
106 auto end = dataSeries.cend();
107 if (valuesBounds.first != end && valuesBounds.second != end) {
108 auto rangeValue = [](const auto &value) { return std::isnan(value) ? 0. : value; };
109
110 minValue = rangeValue(valuesBounds.first->minValue());
111 maxValue = rangeValue(valuesBounds.second->maxValue());
112 }
113 dataSeries.unlock();
114
115 plot.yAxis->setRange(QCPRange{minValue, maxValue});
116 }
117
94 118 static void updatePlottables(T &dataSeries, PlottablesMap &plottables, const SqpRange &range,
95 119 bool rescaleAxes)
96 120 {
97 121
98 122 // For each plottable to update, resets its data
99 123 std::map<int, QSharedPointer<SqpDataContainer> > dataContainers{};
100 124 for (const auto &plottable : plottables) {
101 125 if (auto graph = dynamic_cast<QCPGraph *>(plottable.second)) {
102 126 auto dataContainer = QSharedPointer<SqpDataContainer>::create();
103 127 graph->setData(dataContainer);
104 128
105 129 dataContainers.insert({plottable.first, dataContainer});
106 130 }
107 131 }
108 132 dataSeries.lockRead();
109 133
110 134 // - Gets the data of the series included in the current range
111 135 // - Updates each plottable by adding, for each data item, a point that takes x-axis data
112 136 // and value data. The correct value is retrieved according to the index of the component
113 137 auto subDataIts = dataSeries.xAxisRange(range.m_TStart, range.m_TEnd);
114 138 for (auto it = subDataIts.first; it != subDataIts.second; ++it) {
115 139 for (const auto &dataContainer : dataContainers) {
116 140 auto componentIndex = dataContainer.first;
117 141 dataContainer.second->appendGraphData(
118 142 QCPGraphData(it->x(), it->value(componentIndex)));
119 143 }
120 144 }
121 145
122 146 dataSeries.unlock();
123 147
124 148 if (!plottables.empty()) {
125 149 auto plot = plottables.begin()->second->parentPlot();
126 150
127 151 if (rescaleAxes) {
128 152 plot->rescaleAxes();
129 153 }
130 154
131 155 plot->replot();
132 156 }
133 157 }
134 158 };
135 159
136 160 /**
137 161 * Helper used to create/update plottables
138 162 */
139 163 struct IPlottablesHelper {
140 164 virtual ~IPlottablesHelper() noexcept = default;
141 165 virtual PlottablesMap create(QCustomPlot &plot) const = 0;
166 virtual void setYAxisRange(const SqpRange &xAxisRange, QCustomPlot &plot) const = 0;
142 167 virtual void update(PlottablesMap &plottables, const SqpRange &range,
143 168 bool rescaleAxes = false) const = 0;
144 169 };
145 170
146 171 /**
147 172 * Default implementation of IPlottablesHelper, which takes data series to create/update plottables
148 173 * @tparam T the data series' type
149 174 */
150 175 template <typename T>
151 176 struct PlottablesHelper : public IPlottablesHelper {
152 177 explicit PlottablesHelper(T &dataSeries) : m_DataSeries{dataSeries} {}
153 178
154 179 PlottablesMap create(QCustomPlot &plot) const override
155 180 {
156 181 return PlottablesCreator<T>::createPlottables(m_DataSeries, plot);
157 182 }
158 183
159 184 void update(PlottablesMap &plottables, const SqpRange &range, bool rescaleAxes) const override
160 185 {
161 186 PlottablesUpdater<T>::updatePlottables(m_DataSeries, plottables, range, rescaleAxes);
162 187 }
163 188
189 void setYAxisRange(const SqpRange &xAxisRange, QCustomPlot &plot) const override
190 {
191 return PlottablesUpdater<T>::setPlotYAxisRange(m_DataSeries, xAxisRange, plot);
192 }
193
164 194 T &m_DataSeries;
165 195 };
166 196
167 197 /// Creates IPlottablesHelper according to a data series
168 198 std::unique_ptr<IPlottablesHelper> createHelper(std::shared_ptr<IDataSeries> dataSeries) noexcept
169 199 {
170 200 if (auto scalarSeries = std::dynamic_pointer_cast<ScalarSeries>(dataSeries)) {
171 201 return std::make_unique<PlottablesHelper<ScalarSeries> >(*scalarSeries);
172 202 }
173 203 else if (auto vectorSeries = std::dynamic_pointer_cast<VectorSeries>(dataSeries)) {
174 204 return std::make_unique<PlottablesHelper<VectorSeries> >(*vectorSeries);
175 205 }
176 206 else {
177 207 return std::make_unique<PlottablesHelper<IDataSeries> >(*dataSeries);
178 208 }
179 209 }
180 210
181 211 } // namespace
182 212
183 213 PlottablesMap VisualizationGraphHelper::create(std::shared_ptr<Variable> variable,
184 214 QCustomPlot &plot) noexcept
185 215 {
186 216 if (variable) {
187 217 auto helper = createHelper(variable->dataSeries());
188 218 auto plottables = helper->create(plot);
189 219 return plottables;
190 220 }
191 221 else {
192 222 qCDebug(LOG_VisualizationGraphHelper())
193 223 << QObject::tr("Can't create graph plottables : the variable is null");
194 224 return PlottablesMap{};
195 225 }
196 226 }
197 227
228 void VisualizationGraphHelper::setYAxisRange(std::shared_ptr<Variable> variable,
229 QCustomPlot &plot) noexcept
230 {
231 if (variable) {
232 auto helper = createHelper(variable->dataSeries());
233 helper->setYAxisRange(variable->range(), plot);
234 }
235 else {
236 qCDebug(LOG_VisualizationGraphHelper())
237 << QObject::tr("Can't set y-axis range of plot: the variable is null");
238 }
239 }
240
198 241 void VisualizationGraphHelper::updateData(PlottablesMap &plottables,
199 242 std::shared_ptr<IDataSeries> dataSeries,
200 243 const SqpRange &dateTime)
201 244 {
202 245 auto helper = createHelper(dataSeries);
203 246 helper->update(plottables, dateTime);
204 247 }
@@ -1,401 +1,406
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 QCPItemTracer *m_TextTracer;
49 49 /// Delegate used to attach rendering features to the plot
50 50 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
51 51 };
52 52
53 53 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
54 54 : VisualizationDragWidget{parent},
55 55 ui{new Ui::VisualizationGraphWidget},
56 56 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
57 57 {
58 58 ui->setupUi(this);
59 59
60 60 // 'Close' options : widget is deleted when closed
61 61 setAttribute(Qt::WA_DeleteOnClose);
62 62
63 63 // Set qcpplot properties :
64 64 // - Drag (on x-axis) and zoom are enabled
65 65 // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation
66 66 ui->widget->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectItems);
67 67 ui->widget->axisRect()->setRangeDrag(Qt::Horizontal);
68 68
69 69 // The delegate must be initialized after the ui as it uses the plot
70 70 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
71 71
72 72 connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress);
73 73 connect(ui->widget, &QCustomPlot::mouseRelease, this,
74 74 &VisualizationGraphWidget::onMouseRelease);
75 75 connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove);
76 76 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
77 77 connect(ui->widget->xAxis, static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(
78 78 &QCPAxis::rangeChanged),
79 79 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
80 80
81 81 // Activates menu when right clicking on the graph
82 82 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
83 83 connect(ui->widget, &QCustomPlot::customContextMenuRequested, this,
84 84 &VisualizationGraphWidget::onGraphMenuRequested);
85 85
86 86 connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
87 87 &VariableController::onRequestDataLoading);
88 88
89 89 connect(&sqpApp->variableController(), &VariableController::updateVarDisplaying, this,
90 90 &VisualizationGraphWidget::onUpdateVarDisplaying);
91 91 }
92 92
93 93
94 94 VisualizationGraphWidget::~VisualizationGraphWidget()
95 95 {
96 96 delete ui;
97 97 }
98 98
99 99 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
100 100 {
101 101 auto parent = parentWidget();
102 102 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
103 103 parent = parent->parentWidget();
104 104 }
105 105
106 106 return qobject_cast<VisualizationZoneWidget *>(parent);
107 107 }
108 108
109 109 void VisualizationGraphWidget::enableAcquisition(bool enable)
110 110 {
111 111 impl->m_DoAcquisition = enable;
112 112 }
113 113
114 114 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, SqpRange range)
115 115 {
116 116 // Uses delegate to create the qcpplot components according to the variable
117 117 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
118 118 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
119 119
120 120 // Set axes properties according to the units of the data series
121 121 /// @todo : for the moment, no control is performed on the axes: the units and the tickers
122 122 /// are fixed for the default x-axis and y-axis of the plot, and according to the new graph
123 123 auto xAxisUnit = Unit{};
124 124 auto valuesUnit = Unit{};
125 125
126 126 if (auto dataSeries = variable->dataSeries()) {
127 127 dataSeries->lockRead();
128 128 xAxisUnit = dataSeries->xAxisUnit();
129 129 valuesUnit = dataSeries->valuesUnit();
130 130 dataSeries->unlock();
131 131 }
132 132 impl->m_RenderingDelegate->setAxesProperties(xAxisUnit, valuesUnit);
133 133
134 134 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
135 135
136 136 this->enableAcquisition(false);
137 137 this->setGraphRange(range);
138 138 this->enableAcquisition(true);
139 139
140 140 emit requestDataLoading(QVector<std::shared_ptr<Variable> >() << variable, range, false);
141 141
142 142 emit variableAdded(variable);
143 143 }
144 144
145 145 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
146 146 {
147 147 // Each component associated to the variable :
148 148 // - is removed from qcpplot (which deletes it)
149 149 // - is no longer referenced in the map
150 150 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
151 151 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
152 152 emit variableAboutToBeRemoved(variable);
153 153
154 154 auto &plottablesMap = variableIt->second;
155 155
156 156 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
157 157 plottableIt != plottableEnd;) {
158 158 ui->widget->removePlottable(plottableIt->second);
159 159 plottableIt = plottablesMap.erase(plottableIt);
160 160 }
161 161
162 162 impl->m_VariableToPlotMultiMap.erase(variableIt);
163 163 }
164 164
165 165 // Updates graph
166 166 ui->widget->replot();
167 167 }
168 168
169 169 QList<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
170 170 {
171 171 auto variables = QList<std::shared_ptr<Variable> >{};
172 172 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
173 173 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
174 174 variables << it->first;
175 175 }
176 176
177 177 return variables;
178 178 }
179 179
180 void VisualizationGraphWidget::setYRange(const SqpRange &range)
180 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
181 181 {
182 ui->widget->yAxis->setRange(range.m_TStart, range.m_TEnd);
182 if (!variable) {
183 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
184 return;
185 }
186
187 VisualizationGraphHelper::setYAxisRange(variable, *ui->widget);
183 188 }
184 189
185 190 SqpRange VisualizationGraphWidget::graphRange() const noexcept
186 191 {
187 192 auto graphRange = ui->widget->xAxis->range();
188 193 return SqpRange{graphRange.lower, graphRange.upper};
189 194 }
190 195
191 196 void VisualizationGraphWidget::setGraphRange(const SqpRange &range)
192 197 {
193 198 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange START");
194 199 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
195 200 ui->widget->replot();
196 201 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange END");
197 202 }
198 203
199 204 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
200 205 {
201 206 if (visitor) {
202 207 visitor->visit(this);
203 208 }
204 209 else {
205 210 qCCritical(LOG_VisualizationGraphWidget())
206 211 << tr("Can't visit widget : the visitor is null");
207 212 }
208 213 }
209 214
210 215 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
211 216 {
212 217 /// @todo : for the moment, a graph can always accomodate a variable
213 218 Q_UNUSED(variable);
214 219 return true;
215 220 }
216 221
217 222 bool VisualizationGraphWidget::contains(const Variable &variable) const
218 223 {
219 224 // Finds the variable among the keys of the map
220 225 auto variablePtr = &variable;
221 226 auto findVariable
222 227 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
223 228
224 229 auto end = impl->m_VariableToPlotMultiMap.cend();
225 230 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
226 231 return it != end;
227 232 }
228 233
229 234 QString VisualizationGraphWidget::name() const
230 235 {
231 236 return impl->m_Name;
232 237 }
233 238
234 239 QMimeData *VisualizationGraphWidget::mimeData() const
235 240 {
236 241 auto mimeData = new QMimeData;
237 242 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
238 243
239 244 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
240 245 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
241 246
242 247 return mimeData;
243 248 }
244 249
245 250 bool VisualizationGraphWidget::isDragAllowed() const
246 251 {
247 252 return true;
248 253 }
249 254
250 255 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
251 256 {
252 257 if (highlighted) {
253 258 plot().setBackground(QBrush(QColor("#BBD5EE")));
254 259 }
255 260 else {
256 261 plot().setBackground(QBrush(Qt::white));
257 262 }
258 263
259 264 plot().update();
260 265 }
261 266
262 267 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
263 268 {
264 269 Q_UNUSED(event);
265 270
266 271 // Prevents that all variables will be removed from graph when it will be closed
267 272 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
268 273 emit variableAboutToBeRemoved(variableEntry.first);
269 274 }
270 275 }
271 276
272 277 void VisualizationGraphWidget::enterEvent(QEvent *event)
273 278 {
274 279 Q_UNUSED(event);
275 280 impl->m_RenderingDelegate->showGraphOverlay(true);
276 281 }
277 282
278 283 void VisualizationGraphWidget::leaveEvent(QEvent *event)
279 284 {
280 285 Q_UNUSED(event);
281 286 impl->m_RenderingDelegate->showGraphOverlay(false);
282 287 }
283 288
284 289 QCustomPlot &VisualizationGraphWidget::plot() noexcept
285 290 {
286 291 return *ui->widget;
287 292 }
288 293
289 294 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
290 295 {
291 296 QMenu graphMenu{};
292 297
293 298 // Iterates on variables (unique keys)
294 299 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
295 300 end = impl->m_VariableToPlotMultiMap.cend();
296 301 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
297 302 // 'Remove variable' action
298 303 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
299 304 [ this, var = it->first ]() { removeVariable(var); });
300 305 }
301 306
302 307 if (!graphMenu.isEmpty()) {
303 308 graphMenu.exec(QCursor::pos());
304 309 }
305 310 }
306 311
307 312 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
308 313 {
309 314 qCDebug(LOG_VisualizationGraphWidget()) << tr("TORM: VisualizationGraphWidget::onRangeChanged")
310 315 << QThread::currentThread()->objectName() << "DoAcqui"
311 316 << impl->m_DoAcquisition;
312 317
313 318 auto graphRange = SqpRange{t1.lower, t1.upper};
314 319 auto oldGraphRange = SqpRange{t2.lower, t2.upper};
315 320
316 321 if (impl->m_DoAcquisition) {
317 322 QVector<std::shared_ptr<Variable> > variableUnderGraphVector;
318 323
319 324 for (auto it = impl->m_VariableToPlotMultiMap.begin(),
320 325 end = impl->m_VariableToPlotMultiMap.end();
321 326 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
322 327 variableUnderGraphVector.push_back(it->first);
323 328 }
324 329 emit requestDataLoading(std::move(variableUnderGraphVector), graphRange,
325 330 !impl->m_IsCalibration);
326 331
327 332 if (!impl->m_IsCalibration) {
328 333 qCDebug(LOG_VisualizationGraphWidget())
329 334 << tr("TORM: VisualizationGraphWidget::Synchronize notify !!")
330 335 << QThread::currentThread()->objectName() << graphRange << oldGraphRange;
331 336 emit synchronize(graphRange, oldGraphRange);
332 337 }
333 338 }
334 339 }
335 340
336 341 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
337 342 {
338 343 // Handles plot rendering when mouse is moving
339 344 impl->m_RenderingDelegate->onMouseMove(event);
340 345
341 346 VisualizationDragWidget::mouseMoveEvent(event);
342 347 }
343 348
344 349 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
345 350 {
346 351 auto zoomOrientations = QFlags<Qt::Orientation>{};
347 352
348 353 // Lambda that enables a zoom orientation if the key modifier related to this orientation
349 354 // has
350 355 // been pressed
351 356 auto enableOrientation
352 357 = [&zoomOrientations, event](const auto &orientation, const auto &modifier) {
353 358 auto orientationEnabled = event->modifiers().testFlag(modifier);
354 359 zoomOrientations.setFlag(orientation, orientationEnabled);
355 360 };
356 361 enableOrientation(Qt::Vertical, VERTICAL_ZOOM_MODIFIER);
357 362 enableOrientation(Qt::Horizontal, HORIZONTAL_ZOOM_MODIFIER);
358 363
359 364 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
360 365 }
361 366
362 367 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
363 368 {
364 369 impl->m_IsCalibration = event->modifiers().testFlag(Qt::ControlModifier);
365 370
366 371 plot().setInteraction(QCP::iRangeDrag, !event->modifiers().testFlag(Qt::AltModifier));
367 372
368 373 VisualizationDragWidget::mousePressEvent(event);
369 374 }
370 375
371 376 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
372 377 {
373 378 impl->m_IsCalibration = false;
374 379 }
375 380
376 381 void VisualizationGraphWidget::onDataCacheVariableUpdated()
377 382 {
378 383 auto graphRange = ui->widget->xAxis->range();
379 384 auto dateTime = SqpRange{graphRange.lower, graphRange.upper};
380 385
381 386 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
382 387 auto variable = variableEntry.first;
383 388 qCDebug(LOG_VisualizationGraphWidget())
384 389 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
385 390 qCDebug(LOG_VisualizationGraphWidget())
386 391 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
387 392 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
388 393 VisualizationGraphHelper::updateData(variableEntry.second, variable->dataSeries(),
389 394 variable->range());
390 395 }
391 396 }
392 397 }
393 398
394 399 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
395 400 const SqpRange &range)
396 401 {
397 402 auto it = impl->m_VariableToPlotMultiMap.find(variable);
398 403 if (it != impl->m_VariableToPlotMultiMap.end()) {
399 404 VisualizationGraphHelper::updateData(it->second, variable->dataSeries(), range);
400 405 }
401 406 }
@@ -1,516 +1,500
1 1 #include "Visualization/VisualizationZoneWidget.h"
2 2
3 3 #include "Visualization/IVisualizationWidgetVisitor.h"
4 4 #include "Visualization/QCustomPlotSynchronizer.h"
5 5 #include "Visualization/VisualizationGraphWidget.h"
6 6 #include "Visualization/VisualizationWidget.h"
7 7 #include "ui_VisualizationZoneWidget.h"
8 8
9 9 #include "Common/MimeTypesDef.h"
10 10 #include "Common/VisualizationDef.h"
11 11
12 12 #include <Data/SqpRange.h>
13 13 #include <Time/TimeController.h>
14 14 #include <Variable/Variable.h>
15 15 #include <Variable/VariableController.h>
16 16
17 17 #include <Visualization/operations/FindVariableOperation.h>
18 18
19 19 #include <DragAndDrop/DragDropHelper.h>
20 20 #include <QUuid>
21 21 #include <SqpApplication.h>
22 22 #include <cmath>
23 23
24 24 #include <QLayout>
25 25
26 26 Q_LOGGING_CATEGORY(LOG_VisualizationZoneWidget, "VisualizationZoneWidget")
27 27
28 28 namespace {
29 29
30 30
31 31 /// Generates a default name for a new graph, according to the number of graphs already displayed in
32 32 /// the zone
33 33 QString defaultGraphName(const QLayout &layout)
34 34 {
35 35 auto count = 0;
36 36 for (auto i = 0; i < layout.count(); ++i) {
37 37 if (dynamic_cast<VisualizationGraphWidget *>(layout.itemAt(i)->widget())) {
38 38 count++;
39 39 }
40 40 }
41 41
42 42 return QObject::tr("Graph %1").arg(count + 1);
43 43 }
44 44
45 45 /**
46 46 * Applies a function to all graphs of the zone represented by its layout
47 47 * @param layout the layout that contains graphs
48 48 * @param fun the function to apply to each graph
49 49 */
50 50 template <typename Fun>
51 51 void processGraphs(QLayout &layout, Fun fun)
52 52 {
53 53 for (auto i = 0; i < layout.count(); ++i) {
54 54 if (auto item = layout.itemAt(i)) {
55 55 if (auto visualizationGraphWidget
56 56 = dynamic_cast<VisualizationGraphWidget *>(item->widget())) {
57 57 fun(*visualizationGraphWidget);
58 58 }
59 59 }
60 60 }
61 61 }
62 62
63 63 } // namespace
64 64
65 65 struct VisualizationZoneWidget::VisualizationZoneWidgetPrivate {
66 66
67 67 explicit VisualizationZoneWidgetPrivate()
68 68 : m_SynchronisationGroupId{QUuid::createUuid()},
69 69 m_Synchronizer{std::make_unique<QCustomPlotSynchronizer>()}
70 70 {
71 71 }
72 72 QUuid m_SynchronisationGroupId;
73 73 std::unique_ptr<IGraphSynchronizer> m_Synchronizer;
74 74
75 75 // Returns the first graph in the zone or nullptr if there is no graph inside
76 76 VisualizationGraphWidget *firstGraph(const VisualizationZoneWidget *zoneWidget) const
77 77 {
78 78 VisualizationGraphWidget *firstGraph = nullptr;
79 79 auto layout = zoneWidget->ui->dragDropContainer->layout();
80 80 if (layout->count() > 0) {
81 81 if (auto visualizationGraphWidget
82 82 = qobject_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
83 83 firstGraph = visualizationGraphWidget;
84 84 }
85 85 }
86 86
87 87 return firstGraph;
88 88 }
89 89
90 90 void dropGraph(int index, VisualizationZoneWidget *zoneWidget);
91 91 void dropVariables(const QList<std::shared_ptr<Variable> > &variables, int index,
92 92 VisualizationZoneWidget *zoneWidget);
93 93 };
94 94
95 95 VisualizationZoneWidget::VisualizationZoneWidget(const QString &name, QWidget *parent)
96 96 : VisualizationDragWidget{parent},
97 97 ui{new Ui::VisualizationZoneWidget},
98 98 impl{spimpl::make_unique_impl<VisualizationZoneWidgetPrivate>()}
99 99 {
100 100 ui->setupUi(this);
101 101
102 102 ui->zoneNameLabel->setText(name);
103 103
104 104 ui->dragDropContainer->setPlaceHolderType(DragDropHelper::PlaceHolderType::Graph);
105 105 ui->dragDropContainer->addAcceptedMimeType(
106 106 MIME_TYPE_GRAPH, VisualizationDragDropContainer::DropBehavior::Inserted);
107 107 ui->dragDropContainer->addAcceptedMimeType(
108 108 MIME_TYPE_VARIABLE_LIST, VisualizationDragDropContainer::DropBehavior::InsertedAndMerged);
109 109 ui->dragDropContainer->addAcceptedMimeType(
110 110 MIME_TYPE_TIME_RANGE, VisualizationDragDropContainer::DropBehavior::Merged);
111 111 ui->dragDropContainer->setAcceptMimeDataFunction([this](auto mimeData) {
112 112 return sqpApp->dragDropHelper().checkMimeDataForVisualization(mimeData,
113 113 ui->dragDropContainer);
114 114 });
115 115
116 116 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredInContainer, this,
117 117 &VisualizationZoneWidget::dropMimeData);
118 118 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredOnWidget, this,
119 119 &VisualizationZoneWidget::dropMimeDataOnGraph);
120 120
121 121 // 'Close' options : widget is deleted when closed
122 122 setAttribute(Qt::WA_DeleteOnClose);
123 123 connect(ui->closeButton, &QToolButton::clicked, this, &VisualizationZoneWidget::close);
124 124 ui->closeButton->setIcon(sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton));
125 125
126 126 // Synchronisation id
127 127 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronizationGroupId",
128 128 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
129 129 }
130 130
131 131 VisualizationZoneWidget::~VisualizationZoneWidget()
132 132 {
133 133 delete ui;
134 134 }
135 135
136 136 void VisualizationZoneWidget::addGraph(VisualizationGraphWidget *graphWidget)
137 137 {
138 138 // Synchronize new graph with others in the zone
139 139 impl->m_Synchronizer->addGraph(*graphWidget);
140 140
141 141 ui->dragDropContainer->addDragWidget(graphWidget);
142 142 }
143 143
144 144 void VisualizationZoneWidget::insertGraph(int index, VisualizationGraphWidget *graphWidget)
145 145 {
146 146 // Synchronize new graph with others in the zone
147 147 impl->m_Synchronizer->addGraph(*graphWidget);
148 148
149 149 ui->dragDropContainer->insertDragWidget(index, graphWidget);
150 150 }
151 151
152 152 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable)
153 153 {
154 154 return createGraph(variable, -1);
155 155 }
156 156
157 157 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable,
158 158 int index)
159 159 {
160 160 auto graphWidget
161 161 = new VisualizationGraphWidget{defaultGraphName(*ui->dragDropContainer->layout()), this};
162 162
163 163
164 164 // Set graph properties
165 165 graphWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
166 166 graphWidget->setMinimumHeight(GRAPH_MINIMUM_HEIGHT);
167 167
168 168
169 169 // Lambda to synchronize zone widget
170 170 auto synchronizeZoneWidget = [this, graphWidget](const SqpRange &graphRange,
171 171 const SqpRange &oldGraphRange) {
172 172
173 173 auto zoomType = VariableController::getZoomType(graphRange, oldGraphRange);
174 174 auto frameLayout = ui->dragDropContainer->layout();
175 175 for (auto i = 0; i < frameLayout->count(); ++i) {
176 176 auto graphChild
177 177 = dynamic_cast<VisualizationGraphWidget *>(frameLayout->itemAt(i)->widget());
178 178 if (graphChild && (graphChild != graphWidget)) {
179 179
180 180 auto graphChildRange = graphChild->graphRange();
181 181 switch (zoomType) {
182 182 case AcquisitionZoomType::ZoomIn: {
183 183 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
184 184 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
185 185 graphChildRange.m_TStart += deltaLeft;
186 186 graphChildRange.m_TEnd -= deltaRight;
187 187 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomIn");
188 188 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
189 189 << deltaLeft;
190 190 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
191 191 << deltaRight;
192 192 qCDebug(LOG_VisualizationZoneWidget())
193 193 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
194 194
195 195 break;
196 196 }
197 197
198 198 case AcquisitionZoomType::ZoomOut: {
199 199 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomOut");
200 200 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
201 201 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
202 202 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
203 203 << deltaLeft;
204 204 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
205 205 << deltaRight;
206 206 qCDebug(LOG_VisualizationZoneWidget())
207 207 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
208 208 graphChildRange.m_TStart -= deltaLeft;
209 209 graphChildRange.m_TEnd += deltaRight;
210 210 break;
211 211 }
212 212 case AcquisitionZoomType::PanRight: {
213 213 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: PanRight");
214 214 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
215 215 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
216 216 graphChildRange.m_TStart += deltaLeft;
217 217 graphChildRange.m_TEnd += deltaRight;
218 218 qCDebug(LOG_VisualizationZoneWidget())
219 219 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
220 220 break;
221 221 }
222 222 case AcquisitionZoomType::PanLeft: {
223 223 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: PanLeft");
224 224 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
225 225 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
226 226 graphChildRange.m_TStart -= deltaLeft;
227 227 graphChildRange.m_TEnd -= deltaRight;
228 228 break;
229 229 }
230 230 case AcquisitionZoomType::Unknown: {
231 231 qCDebug(LOG_VisualizationZoneWidget())
232 232 << tr("Impossible to synchronize: zoom type unknown");
233 233 break;
234 234 }
235 235 default:
236 236 qCCritical(LOG_VisualizationZoneWidget())
237 237 << tr("Impossible to synchronize: zoom type not take into account");
238 238 // No action
239 239 break;
240 240 }
241 241 graphChild->enableAcquisition(false);
242 242 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: Range before: ")
243 243 << graphChild->graphRange();
244 244 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: Range after : ")
245 245 << graphChildRange;
246 246 qCDebug(LOG_VisualizationZoneWidget())
247 247 << tr("TORM: child dt") << graphChildRange.m_TEnd - graphChildRange.m_TStart;
248 248 graphChild->setGraphRange(graphChildRange);
249 249 graphChild->enableAcquisition(true);
250 250 }
251 251 }
252 252 };
253 253
254 254 // connection for synchronization
255 255 connect(graphWidget, &VisualizationGraphWidget::synchronize, synchronizeZoneWidget);
256 256 connect(graphWidget, &VisualizationGraphWidget::variableAdded, this,
257 257 &VisualizationZoneWidget::onVariableAdded);
258 258 connect(graphWidget, &VisualizationGraphWidget::variableAboutToBeRemoved, this,
259 259 &VisualizationZoneWidget::onVariableAboutToBeRemoved);
260 260
261 261 auto range = SqpRange{};
262 262 if (auto firstGraph = impl->firstGraph(this)) {
263 263 // Case of a new graph in a existant zone
264 264 range = firstGraph->graphRange();
265 265 }
266 266 else {
267 267 // Case of a new graph as the first of the zone
268 268 range = variable->range();
269 269 }
270 270
271 271 this->insertGraph(index, graphWidget);
272 272
273 273 graphWidget->addVariable(variable, range);
274
275 // get y using variable range
276 if (auto dataSeries = variable->dataSeries()) {
277 dataSeries->lockRead();
278 auto valuesBounds
279 = dataSeries->valuesBounds(variable->range().m_TStart, variable->range().m_TEnd);
280 auto end = dataSeries->cend();
281 if (valuesBounds.first != end && valuesBounds.second != end) {
282 auto rangeValue = [](const auto &value) { return std::isnan(value) ? 0. : value; };
283
284 auto minValue = rangeValue(valuesBounds.first->minValue());
285 auto maxValue = rangeValue(valuesBounds.second->maxValue());
286
287 graphWidget->setYRange(SqpRange{minValue, maxValue});
288 }
289 dataSeries->unlock();
290 }
274 graphWidget->setYRange(variable);
291 275
292 276 return graphWidget;
293 277 }
294 278
295 279 VisualizationGraphWidget *
296 280 VisualizationZoneWidget::createGraph(const QList<std::shared_ptr<Variable> > variables, int index)
297 281 {
298 282 if (variables.isEmpty()) {
299 283 return nullptr;
300 284 }
301 285
302 286 auto graphWidget = createGraph(variables.first(), index);
303 287 for (auto variableIt = variables.cbegin() + 1; variableIt != variables.cend(); ++variableIt) {
304 288 graphWidget->addVariable(*variableIt, graphWidget->graphRange());
305 289 }
306 290
307 291 return graphWidget;
308 292 }
309 293
310 294 void VisualizationZoneWidget::accept(IVisualizationWidgetVisitor *visitor)
311 295 {
312 296 if (visitor) {
313 297 visitor->visitEnter(this);
314 298
315 299 // Apply visitor to graph children: widgets different from graphs are not visited (no
316 300 // action)
317 301 processGraphs(
318 302 *ui->dragDropContainer->layout(),
319 303 [visitor](VisualizationGraphWidget &graphWidget) { graphWidget.accept(visitor); });
320 304
321 305 visitor->visitLeave(this);
322 306 }
323 307 else {
324 308 qCCritical(LOG_VisualizationZoneWidget()) << tr("Can't visit widget : the visitor is null");
325 309 }
326 310 }
327 311
328 312 bool VisualizationZoneWidget::canDrop(const Variable &variable) const
329 313 {
330 314 // A tab can always accomodate a variable
331 315 Q_UNUSED(variable);
332 316 return true;
333 317 }
334 318
335 319 bool VisualizationZoneWidget::contains(const Variable &variable) const
336 320 {
337 321 Q_UNUSED(variable);
338 322 return false;
339 323 }
340 324
341 325 QString VisualizationZoneWidget::name() const
342 326 {
343 327 return ui->zoneNameLabel->text();
344 328 }
345 329
346 330 QMimeData *VisualizationZoneWidget::mimeData() const
347 331 {
348 332 auto mimeData = new QMimeData;
349 333 mimeData->setData(MIME_TYPE_ZONE, QByteArray{});
350 334
351 335 return mimeData;
352 336 }
353 337
354 338 bool VisualizationZoneWidget::isDragAllowed() const
355 339 {
356 340 return true;
357 341 }
358 342
359 343 void VisualizationZoneWidget::closeEvent(QCloseEvent *event)
360 344 {
361 345 // Closes graphs in the zone
362 346 processGraphs(*ui->dragDropContainer->layout(),
363 347 [](VisualizationGraphWidget &graphWidget) { graphWidget.close(); });
364 348
365 349 // Delete synchronization group from variable controller
366 350 QMetaObject::invokeMethod(&sqpApp->variableController(), "onRemoveSynchronizationGroupId",
367 351 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
368 352
369 353 QWidget::closeEvent(event);
370 354 }
371 355
372 356 void VisualizationZoneWidget::onVariableAdded(std::shared_ptr<Variable> variable)
373 357 {
374 358 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronized",
375 359 Qt::QueuedConnection, Q_ARG(std::shared_ptr<Variable>, variable),
376 360 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
377 361 }
378 362
379 363 void VisualizationZoneWidget::onVariableAboutToBeRemoved(std::shared_ptr<Variable> variable)
380 364 {
381 365 QMetaObject::invokeMethod(&sqpApp->variableController(), "desynchronize", Qt::QueuedConnection,
382 366 Q_ARG(std::shared_ptr<Variable>, variable),
383 367 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
384 368 }
385 369
386 370 void VisualizationZoneWidget::dropMimeData(int index, const QMimeData *mimeData)
387 371 {
388 372 if (mimeData->hasFormat(MIME_TYPE_GRAPH)) {
389 373 impl->dropGraph(index, this);
390 374 }
391 375 else if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
392 376 auto variables = sqpApp->variableController().variablesForMimeData(
393 377 mimeData->data(MIME_TYPE_VARIABLE_LIST));
394 378 impl->dropVariables(variables, index, this);
395 379 }
396 380 else {
397 381 qCWarning(LOG_VisualizationZoneWidget())
398 382 << tr("VisualizationZoneWidget::dropMimeData, unknown MIME data received.");
399 383 }
400 384 }
401 385
402 386 void VisualizationZoneWidget::dropMimeDataOnGraph(VisualizationDragWidget *dragWidget,
403 387 const QMimeData *mimeData)
404 388 {
405 389 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(dragWidget);
406 390 if (!graphWidget) {
407 391 qCWarning(LOG_VisualizationZoneWidget())
408 392 << tr("VisualizationZoneWidget::dropMimeDataOnGraph, dropping in an unknown widget, "
409 393 "drop aborted");
410 394 Q_ASSERT(false);
411 395 return;
412 396 }
413 397
414 398 if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
415 399 auto variables = sqpApp->variableController().variablesForMimeData(
416 400 mimeData->data(MIME_TYPE_VARIABLE_LIST));
417 401 for (const auto &var : variables) {
418 402 graphWidget->addVariable(var, graphWidget->graphRange());
419 403 }
420 404 }
421 405 else if (mimeData->hasFormat(MIME_TYPE_TIME_RANGE)) {
422 406 auto range = TimeController::timeRangeForMimeData(mimeData->data(MIME_TYPE_TIME_RANGE));
423 407 graphWidget->setGraphRange(range);
424 408 }
425 409 else {
426 410 qCWarning(LOG_VisualizationZoneWidget())
427 411 << tr("VisualizationZoneWidget::dropMimeDataOnGraph, unknown MIME data received.");
428 412 }
429 413 }
430 414
431 415 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropGraph(
432 416 int index, VisualizationZoneWidget *zoneWidget)
433 417 {
434 418 auto &helper = sqpApp->dragDropHelper();
435 419
436 420 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(helper.getCurrentDragWidget());
437 421 if (!graphWidget) {
438 422 qCWarning(LOG_VisualizationZoneWidget())
439 423 << tr("VisualizationZoneWidget::dropGraph, drop aborted, the dropped graph is not "
440 424 "found or invalid.");
441 425 Q_ASSERT(false);
442 426 return;
443 427 }
444 428
445 429 auto parentDragDropContainer
446 430 = qobject_cast<VisualizationDragDropContainer *>(graphWidget->parentWidget());
447 431 if (!parentDragDropContainer) {
448 432 qCWarning(LOG_VisualizationZoneWidget())
449 433 << tr("VisualizationZoneWidget::dropGraph, drop aborted, the parent container of "
450 434 "the dropped graph is not found.");
451 435 Q_ASSERT(false);
452 436 return;
453 437 }
454 438
455 439 const auto &variables = graphWidget->variables();
456 440
457 441 if (parentDragDropContainer != zoneWidget->ui->dragDropContainer && !variables.isEmpty()) {
458 442 // The drop didn't occur in the same zone
459 443
460 444 // Abort the requests for the variables (if any)
461 445 // Commented, because it's not sure if it's needed or not
462 446 // for (const auto& var : variables)
463 447 //{
464 448 // sqpApp->variableController().onAbortProgressRequested(var);
465 449 //}
466 450
467 451 auto previousParentZoneWidget = graphWidget->parentZoneWidget();
468 452 auto nbGraph = parentDragDropContainer->countDragWidget();
469 453 if (nbGraph == 1) {
470 454 // This is the only graph in the previous zone, close the zone
471 455 previousParentZoneWidget->close();
472 456 }
473 457 else {
474 458 // Close the graph
475 459 graphWidget->close();
476 460 }
477 461
478 462 // Creates the new graph in the zone
479 463 zoneWidget->createGraph(variables, index);
480 464 }
481 465 else {
482 466 // The drop occurred in the same zone or the graph is empty
483 467 // Simple move of the graph, no variable operation associated
484 468 parentDragDropContainer->layout()->removeWidget(graphWidget);
485 469
486 470 if (variables.isEmpty() && parentDragDropContainer != zoneWidget->ui->dragDropContainer) {
487 471 // The graph is empty and dropped in a different zone.
488 472 // Take the range of the first graph in the zone (if existing).
489 473 auto layout = zoneWidget->ui->dragDropContainer->layout();
490 474 if (layout->count() > 0) {
491 475 if (auto visualizationGraphWidget
492 476 = qobject_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
493 477 graphWidget->setGraphRange(visualizationGraphWidget->graphRange());
494 478 }
495 479 }
496 480 }
497 481
498 482 zoneWidget->ui->dragDropContainer->insertDragWidget(index, graphWidget);
499 483 }
500 484 }
501 485
502 486 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropVariables(
503 487 const QList<std::shared_ptr<Variable> > &variables, int index,
504 488 VisualizationZoneWidget *zoneWidget)
505 489 {
506 490 // Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and
507 491 // compatible variable here
508 492 if (variables.count() > 1) {
509 493 qCWarning(LOG_VisualizationZoneWidget())
510 494 << tr("VisualizationZoneWidget::dropVariables, dropping multiple variables, operation "
511 495 "aborted.");
512 496 return;
513 497 }
514 498
515 499 zoneWidget->createGraph(variables, index);
516 500 }
General Comments 0
You need to be logged in to leave comments. Login now