##// END OF EJS Templates
refactoring + fix created graph range
trabillard -
r1346:b5241a465dc2
parent child
Show More
@@ -1,155 +1,156
1 #ifndef SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
1 #ifndef SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
2 #define SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
2 #define SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
3
3
4 #include "Visualization/IVisualizationWidget.h"
4 #include "Visualization/IVisualizationWidget.h"
5 #include "Visualization/VisualizationDragWidget.h"
5 #include "Visualization/VisualizationDragWidget.h"
6
6
7 #include <QLoggingCategory>
7 #include <QLoggingCategory>
8 #include <QWidget>
8 #include <QWidget>
9
9
10 #include <memory>
10 #include <memory>
11
11
12 #include <Common/spimpl.h>
12 #include <Common/spimpl.h>
13
13
14 Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationGraphWidget)
14 Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationGraphWidget)
15
15
16 class QCPRange;
16 class QCPRange;
17 class QCustomPlot;
17 class QCustomPlot;
18 class SqpRange;
18 class SqpRange;
19 class Variable;
19 class Variable;
20 class VisualizationWidget;
20 class VisualizationWidget;
21 class VisualizationZoneWidget;
21 class VisualizationZoneWidget;
22 class VisualizationSelectionZoneItem;
22 class VisualizationSelectionZoneItem;
23
23
24 namespace Ui {
24 namespace Ui {
25 class VisualizationGraphWidget;
25 class VisualizationGraphWidget;
26 } // namespace Ui
26 } // namespace Ui
27
27
28 /// Defines options that can be associated with the graph
28 /// Defines options that can be associated with the graph
29 enum GraphFlag {
29 enum GraphFlag {
30 DisableAll = 0x0, ///< Disables acquisition and synchronization
30 DisableAll = 0x0, ///< Disables acquisition and synchronization
31 EnableAcquisition = 0x1, ///< When this flag is set, the change of the graph's range leads to
31 EnableAcquisition = 0x1, ///< When this flag is set, the change of the graph's range leads to
32 /// the acquisition of data
32 /// the acquisition of data
33 EnableSynchronization = 0x2, ///< When this flag is set, the change of the graph's range causes
33 EnableSynchronization = 0x2, ///< When this flag is set, the change of the graph's range causes
34 /// the call to the synchronization of the graphs contained in the
34 /// the call to the synchronization of the graphs contained in the
35 /// same zone of this graph
35 /// same zone of this graph
36 EnableAll = ~DisableAll ///< Enables acquisition and synchronization
36 EnableAll = ~DisableAll ///< Enables acquisition and synchronization
37 };
37 };
38
38
39 Q_DECLARE_FLAGS(GraphFlags, GraphFlag)
39 Q_DECLARE_FLAGS(GraphFlags, GraphFlag)
40 Q_DECLARE_OPERATORS_FOR_FLAGS(GraphFlags)
40 Q_DECLARE_OPERATORS_FOR_FLAGS(GraphFlags)
41
41
42 class VisualizationGraphWidget : public VisualizationDragWidget, public IVisualizationWidget {
42 class VisualizationGraphWidget : public VisualizationDragWidget, public IVisualizationWidget {
43 Q_OBJECT
43 Q_OBJECT
44
44
45 friend class QCustomPlotSynchronizer;
45 friend class QCustomPlotSynchronizer;
46 friend class VisualizationGraphRenderingDelegate;
46 friend class VisualizationGraphRenderingDelegate;
47
47
48 public:
48 public:
49 explicit VisualizationGraphWidget(const QString &name = {}, QWidget *parent = 0);
49 explicit VisualizationGraphWidget(const QString &name = {}, QWidget *parent = 0);
50 virtual ~VisualizationGraphWidget();
50 virtual ~VisualizationGraphWidget();
51
51
52 /// Returns the VisualizationZoneWidget which contains the graph or nullptr
52 /// Returns the VisualizationZoneWidget which contains the graph or nullptr
53 VisualizationZoneWidget *parentZoneWidget() const noexcept;
53 VisualizationZoneWidget *parentZoneWidget() const noexcept;
54
54
55 /// Returns the main VisualizationWidget which contains the graph or nullptr
55 /// Returns the main VisualizationWidget which contains the graph or nullptr
56 VisualizationWidget *parentVisualizationWidget() const;
56 VisualizationWidget *parentVisualizationWidget() const;
57
57
58 /// Sets graph options
58 /// Sets graph options
59 void setFlags(GraphFlags flags);
59 void setFlags(GraphFlags flags);
60
60
61 void addVariable(std::shared_ptr<Variable> variable, SqpRange range);
61 void addVariable(std::shared_ptr<Variable> variable, SqpRange range);
62
62
63 /// Removes a variable from the graph
63 /// Removes a variable from the graph
64 void removeVariable(std::shared_ptr<Variable> variable) noexcept;
64 void removeVariable(std::shared_ptr<Variable> variable) noexcept;
65
65
66 /// Returns the list of all variables used in the graph
66 /// Returns the list of all variables used in the graph
67 QList<std::shared_ptr<Variable> > variables() const;
67 QList<std::shared_ptr<Variable> > variables() const;
68
68
69 /// Sets the y-axis range based on the data of a variable
69 /// Sets the y-axis range based on the data of a variable
70 void setYRange(std::shared_ptr<Variable> variable);
70 void setYRange(std::shared_ptr<Variable> variable);
71 SqpRange graphRange() const noexcept;
71 SqpRange graphRange() const noexcept;
72 void setGraphRange(const SqpRange &range, bool calibration = false);
72 void setGraphRange(const SqpRange &range, bool calibration = false);
73 void setAutoRangeOnVariableInitialization(bool value);
73
74
74 // Zones
75 // Zones
75 /// Returns the ranges of all the selection zones on the graph
76 /// Returns the ranges of all the selection zones on the graph
76 QVector<SqpRange> selectionZoneRanges() const;
77 QVector<SqpRange> selectionZoneRanges() const;
77 /// Adds new selection zones in the graph
78 /// Adds new selection zones in the graph
78 void addSelectionZones(const QVector<SqpRange> &ranges);
79 void addSelectionZones(const QVector<SqpRange> &ranges);
79 /// Removes the specified selection zone
80 /// Removes the specified selection zone
80 void removeSelectionZone(VisualizationSelectionZoneItem *selectionZone);
81 void removeSelectionZone(VisualizationSelectionZoneItem *selectionZone);
81
82
82 /// Undo the last zoom done with a zoom box
83 /// Undo the last zoom done with a zoom box
83 void undoZoom();
84 void undoZoom();
84
85
85 // IVisualizationWidget interface
86 // IVisualizationWidget interface
86 void accept(IVisualizationWidgetVisitor *visitor) override;
87 void accept(IVisualizationWidgetVisitor *visitor) override;
87 bool canDrop(const Variable &variable) const override;
88 bool canDrop(const Variable &variable) const override;
88 bool contains(const Variable &variable) const override;
89 bool contains(const Variable &variable) const override;
89 QString name() const override;
90 QString name() const override;
90
91
91 // VisualisationDragWidget
92 // VisualisationDragWidget
92 QMimeData *mimeData(const QPoint &position) const override;
93 QMimeData *mimeData(const QPoint &position) const override;
93 QPixmap customDragPixmap(const QPoint &dragPosition) override;
94 QPixmap customDragPixmap(const QPoint &dragPosition) override;
94 bool isDragAllowed() const override;
95 bool isDragAllowed() const override;
95 void highlightForMerge(bool highlighted) override;
96 void highlightForMerge(bool highlighted) override;
96
97
97 // Cursors
98 // Cursors
98 /// Adds or moves the vertical cursor at the specified value on the x-axis
99 /// Adds or moves the vertical cursor at the specified value on the x-axis
99 void addVerticalCursor(double time);
100 void addVerticalCursor(double time);
100 /// Adds or moves the vertical cursor at the specified value on the x-axis
101 /// Adds or moves the vertical cursor at the specified value on the x-axis
101 void addVerticalCursorAtViewportPosition(double position);
102 void addVerticalCursorAtViewportPosition(double position);
102 void removeVerticalCursor();
103 void removeVerticalCursor();
103 /// Adds or moves the vertical cursor at the specified value on the y-axis
104 /// Adds or moves the vertical cursor at the specified value on the y-axis
104 void addHorizontalCursor(double value);
105 void addHorizontalCursor(double value);
105 /// Adds or moves the vertical cursor at the specified value on the y-axis
106 /// Adds or moves the vertical cursor at the specified value on the y-axis
106 void addHorizontalCursorAtViewportPosition(double position);
107 void addHorizontalCursorAtViewportPosition(double position);
107 void removeHorizontalCursor();
108 void removeHorizontalCursor();
108
109
109 signals:
110 signals:
110 void synchronize(const SqpRange &range, const SqpRange &oldRange);
111 void synchronize(const SqpRange &range, const SqpRange &oldRange);
111 void requestDataLoading(QVector<std::shared_ptr<Variable> > variable, const SqpRange &range,
112 void requestDataLoading(QVector<std::shared_ptr<Variable> > variable, const SqpRange &range,
112 bool synchronise);
113 bool synchronise);
113
114
114 /// Signal emitted when the variable is about to be removed from the graph
115 /// Signal emitted when the variable is about to be removed from the graph
115 void variableAboutToBeRemoved(std::shared_ptr<Variable> var);
116 void variableAboutToBeRemoved(std::shared_ptr<Variable> var);
116 /// Signal emitted when the variable has been added to the graph
117 /// Signal emitted when the variable has been added to the graph
117 void variableAdded(std::shared_ptr<Variable> var);
118 void variableAdded(std::shared_ptr<Variable> var);
118
119
119 protected:
120 protected:
120 void closeEvent(QCloseEvent *event) override;
121 void closeEvent(QCloseEvent *event) override;
121 void enterEvent(QEvent *event) override;
122 void enterEvent(QEvent *event) override;
122 void leaveEvent(QEvent *event) override;
123 void leaveEvent(QEvent *event) override;
123
124
124 QCustomPlot &plot() const noexcept;
125 QCustomPlot &plot() const noexcept;
125
126
126 private:
127 private:
127 Ui::VisualizationGraphWidget *ui;
128 Ui::VisualizationGraphWidget *ui;
128
129
129 class VisualizationGraphWidgetPrivate;
130 class VisualizationGraphWidgetPrivate;
130 spimpl::unique_impl_ptr<VisualizationGraphWidgetPrivate> impl;
131 spimpl::unique_impl_ptr<VisualizationGraphWidgetPrivate> impl;
131
132
132 private slots:
133 private slots:
133 /// Slot called when right clicking on the graph (displays a menu)
134 /// Slot called when right clicking on the graph (displays a menu)
134 void onGraphMenuRequested(const QPoint &pos) noexcept;
135 void onGraphMenuRequested(const QPoint &pos) noexcept;
135
136
136 /// Rescale the X axe to range parameter
137 /// Rescale the X axe to range parameter
137 void onRangeChanged(const QCPRange &t1, const QCPRange &t2);
138 void onRangeChanged(const QCPRange &t1, const QCPRange &t2);
138
139
139 /// Slot called when a mouse double click was made
140 /// Slot called when a mouse double click was made
140 void onMouseDoubleClick(QMouseEvent *event) noexcept;
141 void onMouseDoubleClick(QMouseEvent *event) noexcept;
141 /// Slot called when a mouse move was made
142 /// Slot called when a mouse move was made
142 void onMouseMove(QMouseEvent *event) noexcept;
143 void onMouseMove(QMouseEvent *event) noexcept;
143 /// Slot called when a mouse wheel was made, to perform some processing before the zoom is done
144 /// Slot called when a mouse wheel was made, to perform some processing before the zoom is done
144 void onMouseWheel(QWheelEvent *event) noexcept;
145 void onMouseWheel(QWheelEvent *event) noexcept;
145 /// Slot called when a mouse press was made, to activate the calibration of a graph
146 /// Slot called when a mouse press was made, to activate the calibration of a graph
146 void onMousePress(QMouseEvent *event) noexcept;
147 void onMousePress(QMouseEvent *event) noexcept;
147 /// Slot called when a mouse release was made, to deactivate the calibration of a graph
148 /// Slot called when a mouse release was made, to deactivate the calibration of a graph
148 void onMouseRelease(QMouseEvent *event) noexcept;
149 void onMouseRelease(QMouseEvent *event) noexcept;
149
150
150 void onDataCacheVariableUpdated();
151 void onDataCacheVariableUpdated();
151
152
152 void onUpdateVarDisplaying(std::shared_ptr<Variable> variable, const SqpRange &range);
153 void onUpdateVarDisplaying(std::shared_ptr<Variable> variable, const SqpRange &range);
153 };
154 };
154
155
155 #endif // SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
156 #endif // SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
@@ -1,545 +1,579
1 #include "Catalogue/CatalogueEventsWidget.h"
1 #include "Catalogue/CatalogueEventsWidget.h"
2 #include "ui_CatalogueEventsWidget.h"
2 #include "ui_CatalogueEventsWidget.h"
3
3
4 #include <Catalogue/CatalogueController.h>
4 #include <Catalogue/CatalogueController.h>
5 #include <Catalogue/CatalogueEventsModel.h>
5 #include <Catalogue/CatalogueEventsModel.h>
6 #include <Catalogue/CatalogueExplorerHelper.h>
6 #include <Catalogue/CatalogueExplorerHelper.h>
7 #include <CatalogueDao.h>
7 #include <CatalogueDao.h>
8 #include <DBCatalogue.h>
8 #include <DBCatalogue.h>
9 #include <DBEventProduct.h>
9 #include <DBEventProduct.h>
10 #include <DataSource/DataSourceController.h>
10 #include <DataSource/DataSourceController.h>
11 #include <DataSource/DataSourceItem.h>
11 #include <DataSource/DataSourceItem.h>
12 #include <SqpApplication.h>
12 #include <SqpApplication.h>
13 #include <Variable/Variable.h>
13 #include <Variable/Variable.h>
14 #include <Variable/VariableController.h>
14 #include <Variable/VariableController.h>
15 #include <Visualization/VisualizationGraphWidget.h>
15 #include <Visualization/VisualizationGraphWidget.h>
16 #include <Visualization/VisualizationTabWidget.h>
16 #include <Visualization/VisualizationTabWidget.h>
17 #include <Visualization/VisualizationWidget.h>
17 #include <Visualization/VisualizationWidget.h>
18 #include <Visualization/VisualizationZoneWidget.h>
18 #include <Visualization/VisualizationZoneWidget.h>
19
19
20 #include <QDialog>
20 #include <QDialog>
21 #include <QDialogButtonBox>
21 #include <QDialogButtonBox>
22 #include <QListWidget>
22 #include <QListWidget>
23 #include <QMessageBox>
23 #include <QMessageBox>
24
24
25 Q_LOGGING_CATEGORY(LOG_CatalogueEventsWidget, "CatalogueEventsWidget")
25 Q_LOGGING_CATEGORY(LOG_CatalogueEventsWidget, "CatalogueEventsWidget")
26
26
27 /// Fixed size of the validation column
27 /// Fixed size of the validation column
28 const auto VALIDATION_COLUMN_SIZE = 35;
28 const auto VALIDATION_COLUMN_SIZE = 35;
29
29
30 /// Percentage added to the range of a event when it is displayed
31 const auto EVENT_RANGE_MARGE = 30; // in %
32
30 struct CatalogueEventsWidget::CatalogueEventsWidgetPrivate {
33 struct CatalogueEventsWidget::CatalogueEventsWidgetPrivate {
31
34
32 CatalogueEventsModel *m_Model = nullptr;
35 CatalogueEventsModel *m_Model = nullptr;
33 QStringList m_ZonesForTimeMode;
36 QStringList m_ZonesForTimeMode;
34 QString m_ZoneForGraphMode;
37 QString m_ZoneForGraphMode;
35 QVector<std::shared_ptr<DBCatalogue> > m_DisplayedCatalogues;
38 QVector<std::shared_ptr<DBCatalogue> > m_DisplayedCatalogues;
36 bool m_AllEventDisplayed = false;
39 bool m_AllEventDisplayed = false;
37 QVector<VisualizationGraphWidget *> m_CustomGraphs;
40 QVector<VisualizationGraphWidget *> m_CustomGraphs;
38
41
39 VisualizationWidget *m_VisualizationWidget = nullptr;
42 VisualizationWidget *m_VisualizationWidget = nullptr;
40
43
41 void setEvents(const QVector<std::shared_ptr<DBEvent> > &events, CatalogueEventsWidget *widget)
44 void setEvents(const QVector<std::shared_ptr<DBEvent> > &events, CatalogueEventsWidget *widget)
42 {
45 {
43 widget->ui->treeView->setSortingEnabled(false);
46 widget->ui->treeView->setSortingEnabled(false);
44 m_Model->setEvents(events);
47 m_Model->setEvents(events);
45 widget->ui->treeView->setSortingEnabled(true);
48 widget->ui->treeView->setSortingEnabled(true);
46
49
47 for (auto event : events) {
50 for (auto event : events) {
48 if (sqpApp->catalogueController().eventHasChanges(event)) {
51 if (sqpApp->catalogueController().eventHasChanges(event)) {
49 auto index = m_Model->indexOf(event);
52 auto index = m_Model->indexOf(event);
50 widget->setEventChanges(event, true);
53 widget->setEventChanges(event, true);
51 }
54 }
52 }
55 }
53 }
56 }
54
57
55 void addEvent(const std::shared_ptr<DBEvent> &event, QTreeView *treeView)
58 void addEvent(const std::shared_ptr<DBEvent> &event, QTreeView *treeView)
56 {
59 {
57 treeView->setSortingEnabled(false);
60 treeView->setSortingEnabled(false);
58 m_Model->addEvent(event);
61 m_Model->addEvent(event);
59 treeView->setSortingEnabled(true);
62 treeView->setSortingEnabled(true);
60 }
63 }
61
64
62 void removeEvent(const std::shared_ptr<DBEvent> &event, QTreeView *treeView)
65 void removeEvent(const std::shared_ptr<DBEvent> &event, QTreeView *treeView)
63 {
66 {
64 treeView->setSortingEnabled(false);
67 treeView->setSortingEnabled(false);
65 m_Model->removeEvent(event);
68 m_Model->removeEvent(event);
66 treeView->setSortingEnabled(true);
69 treeView->setSortingEnabled(true);
67 }
70 }
68
71
69 QStringList getAvailableVisualizationZoneList() const
72 QStringList getAvailableVisualizationZoneList() const
70 {
73 {
71 if (m_VisualizationWidget) {
74 if (m_VisualizationWidget) {
72 if (auto tab = m_VisualizationWidget->currentTabWidget()) {
75 if (auto tab = m_VisualizationWidget->currentTabWidget()) {
73 return tab->availableZoneWidgets();
76 return tab->availableZoneWidgets();
74 }
77 }
75 }
78 }
76
79
77 return QStringList{};
80 return QStringList{};
78 }
81 }
79
82
80 QStringList selectZone(QWidget *parent, const QStringList &selectedZones,
83 QStringList selectZone(QWidget *parent, const QStringList &selectedZones,
81 bool allowMultiSelection, const QPoint &location)
84 bool allowMultiSelection, const QPoint &location)
82 {
85 {
83 auto availableZones = getAvailableVisualizationZoneList();
86 auto availableZones = getAvailableVisualizationZoneList();
84 if (availableZones.isEmpty()) {
87 if (availableZones.isEmpty()) {
85 return QStringList{};
88 return QStringList{};
86 }
89 }
87
90
88 QDialog d(parent, Qt::Tool);
91 QDialog d(parent, Qt::Tool);
89 d.setWindowTitle("Choose a zone");
92 d.setWindowTitle("Choose a zone");
90 auto layout = new QVBoxLayout{&d};
93 auto layout = new QVBoxLayout{&d};
91 layout->setContentsMargins(0, 0, 0, 0);
94 layout->setContentsMargins(0, 0, 0, 0);
92 auto listWidget = new QListWidget{&d};
95 auto listWidget = new QListWidget{&d};
93 layout->addWidget(listWidget);
96 layout->addWidget(listWidget);
94
97
95 QSet<QListWidgetItem *> checkedItems;
98 QSet<QListWidgetItem *> checkedItems;
96 for (auto zone : availableZones) {
99 for (auto zone : availableZones) {
97 auto item = new QListWidgetItem{zone};
100 auto item = new QListWidgetItem{zone};
98 item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsUserCheckable);
101 item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsUserCheckable);
99 if (selectedZones.contains(zone)) {
102 if (selectedZones.contains(zone)) {
100 item->setCheckState(Qt::Checked);
103 item->setCheckState(Qt::Checked);
101 checkedItems << item;
104 checkedItems << item;
102 }
105 }
103 else {
106 else {
104 item->setCheckState(Qt::Unchecked);
107 item->setCheckState(Qt::Unchecked);
105 }
108 }
106
109
107 listWidget->addItem(item);
110 listWidget->addItem(item);
108 }
111 }
109
112
110 auto buttonBox = new QDialogButtonBox{QDialogButtonBox::Ok, &d};
113 auto buttonBox = new QDialogButtonBox{QDialogButtonBox::Ok, &d};
111 layout->addWidget(buttonBox);
114 layout->addWidget(buttonBox);
112
115
113 QObject::connect(buttonBox, &QDialogButtonBox::accepted, &d, &QDialog::accept);
116 QObject::connect(buttonBox, &QDialogButtonBox::accepted, &d, &QDialog::accept);
114 QObject::connect(buttonBox, &QDialogButtonBox::rejected, &d, &QDialog::reject);
117 QObject::connect(buttonBox, &QDialogButtonBox::rejected, &d, &QDialog::reject);
115
118
116 QObject::connect(listWidget, &QListWidget::itemChanged,
119 QObject::connect(listWidget, &QListWidget::itemChanged,
117 [&checkedItems, allowMultiSelection, listWidget](auto item) {
120 [&checkedItems, allowMultiSelection, listWidget](auto item) {
118 if (item->checkState() == Qt::Checked) {
121 if (item->checkState() == Qt::Checked) {
119 if (!allowMultiSelection) {
122 if (!allowMultiSelection) {
120 for (auto checkedItem : checkedItems) {
123 for (auto checkedItem : checkedItems) {
121 listWidget->blockSignals(true);
124 listWidget->blockSignals(true);
122 checkedItem->setCheckState(Qt::Unchecked);
125 checkedItem->setCheckState(Qt::Unchecked);
123 listWidget->blockSignals(false);
126 listWidget->blockSignals(false);
124 }
127 }
125
128
126 checkedItems.clear();
129 checkedItems.clear();
127 }
130 }
128 checkedItems << item;
131 checkedItems << item;
129 }
132 }
130 else {
133 else {
131 checkedItems.remove(item);
134 checkedItems.remove(item);
132 }
135 }
133 });
136 });
134
137
135 QStringList result;
138 QStringList result;
136
139
137 d.setMinimumWidth(120);
140 d.setMinimumWidth(120);
138 d.resize(d.minimumSizeHint());
141 d.resize(d.minimumSizeHint());
139 d.move(location);
142 d.move(location);
140 if (d.exec() == QDialog::Accepted) {
143 if (d.exec() == QDialog::Accepted) {
141 for (auto item : checkedItems) {
144 for (auto item : checkedItems) {
142 result += item->text();
145 result += item->text();
143 }
146 }
144 }
147 }
145 else {
148 else {
146 result = selectedZones;
149 result = selectedZones;
147 }
150 }
148
151
149 return result;
152 return result;
150 }
153 }
151
154
152 void updateForTimeMode(QTreeView *treeView)
155 void updateForTimeMode(QTreeView *treeView)
153 {
156 {
154 auto selectedRows = treeView->selectionModel()->selectedRows();
157 auto selectedRows = treeView->selectionModel()->selectedRows();
155
158
156 if (selectedRows.count() == 1) {
159 if (selectedRows.count() == 1) {
157 auto event = m_Model->getEvent(selectedRows.first());
160 auto event = m_Model->getEvent(selectedRows.first());
158 if (event) {
161 if (event) {
159 if (m_VisualizationWidget) {
162 if (m_VisualizationWidget) {
160 if (auto tab = m_VisualizationWidget->currentTabWidget()) {
163 if (auto tab = m_VisualizationWidget->currentTabWidget()) {
161
164
162 for (auto zoneName : m_ZonesForTimeMode) {
165 for (auto zoneName : m_ZonesForTimeMode) {
163 if (auto zone = tab->getZoneWithName(zoneName)) {
166 if (auto zone = tab->getZoneWithName(zoneName)) {
164 SqpRange eventRange;
167 SqpRange eventRange;
165 eventRange.m_TStart = event->getTStart();
168 eventRange.m_TStart = event->getTStart();
166 eventRange.m_TEnd = event->getTEnd();
169 eventRange.m_TEnd = event->getTEnd();
167 zone->setZoneRange(eventRange);
170 zone->setZoneRange(eventRange);
168 }
171 }
169 }
172 }
170 }
173 }
171 else {
174 else {
172 qCWarning(LOG_CatalogueEventsWidget())
175 qCWarning(LOG_CatalogueEventsWidget())
173 << "updateTimeZone: no tab found in the visualization";
176 << "updateTimeZone: no tab found in the visualization";
174 }
177 }
175 }
178 }
176 else {
179 else {
177 qCWarning(LOG_CatalogueEventsWidget())
180 qCWarning(LOG_CatalogueEventsWidget())
178 << "updateTimeZone: visualization widget not found";
181 << "updateTimeZone: visualization widget not found";
179 }
182 }
180 }
183 }
181 }
184 }
182 else {
185 else {
183 qCWarning(LOG_CatalogueEventsWidget())
186 qCWarning(LOG_CatalogueEventsWidget())
184 << "updateTimeZone: not compatible with multiple events selected";
187 << "updateTimeZone: not compatible with multiple events selected";
185 }
188 }
186 }
189 }
187
190
188 void updateForGraphMode(QTreeView *treeView)
191 QVector<SqpRange> getGraphRanges(const std::shared_ptr<DBEvent> &event)
189 {
192 {
190 auto selectedRows = treeView->selectionModel()->selectedRows();
193 // Retrieves the range of each product and the maximum size
191
192 if (selectedRows.count() == 1) {
193 auto event = m_Model->getEvent(selectedRows.first());
194 if (m_VisualizationWidget && event) {
195 if (auto tab = m_VisualizationWidget->currentTabWidget()) {
196 if (auto zone = tab->getZoneWithName(m_ZoneForGraphMode)) {
197
198 for (auto graph : m_CustomGraphs) {
199 graph->close();
200 auto variables = graph->variables().toVector();
201
202 QMetaObject::invokeMethod(
203 &sqpApp->variableController(), "deleteVariables",
204 Qt::QueuedConnection,
205 Q_ARG(QVector<std::shared_ptr<Variable> >, variables));
206 }
207 m_CustomGraphs.clear();
208
209 QVector<SqpRange> graphRanges;
194 QVector<SqpRange> graphRanges;
210 double maxDt = 0;
195 double maxDt = 0;
211 for (auto eventProduct : event->getEventProducts()) {
196 for (auto eventProduct : event->getEventProducts()) {
212 SqpRange eventRange;
197 SqpRange eventRange;
213 eventRange.m_TStart = eventProduct.getTStart();
198 eventRange.m_TStart = eventProduct.getTStart();
214 eventRange.m_TEnd = eventProduct.getTEnd();
199 eventRange.m_TEnd = eventProduct.getTEnd();
215 graphRanges << eventRange;
200 graphRanges << eventRange;
216
201
217 auto dt = eventRange.m_TEnd - eventRange.m_TStart;
202 auto dt = eventRange.m_TEnd - eventRange.m_TStart;
218 if (dt > maxDt) {
203 if (dt > maxDt) {
219 maxDt = dt;
204 maxDt = dt;
220 }
205 }
221 }
206 }
222
207
208 // Adds the marge
209 maxDt *= (100.0 + EVENT_RANGE_MARGE) / 100.0;
210
211 // Corrects the graph ranges so that they all have the same size
223 QVector<SqpRange> correctedGraphRanges;
212 QVector<SqpRange> correctedGraphRanges;
224 for (auto range : graphRanges) {
213 for (auto range : graphRanges) {
225 auto dt = range.m_TEnd - range.m_TStart;
214 auto dt = range.m_TEnd - range.m_TStart;
226 auto diff = qAbs((maxDt - dt) / 2.0);
215 auto diff = qAbs((maxDt - dt) / 2.0);
227
216
228 SqpRange correctedRange;
217 SqpRange correctedRange;
229 correctedRange.m_TStart = range.m_TStart - diff;
218 correctedRange.m_TStart = range.m_TStart - diff;
230 correctedRange.m_TEnd = range.m_TEnd + diff;
219 correctedRange.m_TEnd = range.m_TEnd + diff;
231
220
232 correctedGraphRanges << correctedRange;
221 correctedGraphRanges << correctedRange;
233 }
222 }
234
223
235 auto itRange = correctedGraphRanges.cbegin();
224 return correctedGraphRanges;
225 }
226
227 void updateForGraphMode(QTreeView *treeView)
228 {
229 auto selectedRows = treeView->selectionModel()->selectedRows();
230 if (selectedRows.count() != 1) {
231 qCWarning(LOG_CatalogueEventsWidget())
232 << "updateGraphMode: not compatible with multiple events selected";
233 return;
234 }
235
236 if (!m_VisualizationWidget) {
237 qCWarning(LOG_CatalogueEventsWidget())
238 << "updateGraphMode: visualization widget not found";
239 return;
240 }
241
242 auto event = m_Model->getEvent(selectedRows.first());
243 if (!event) {
244 // A event product is probably selected
245 qCInfo(LOG_CatalogueEventsWidget()) << "updateGraphMode: no events are selected";
246 return;
247 }
248
249 auto tab = m_VisualizationWidget->currentTabWidget();
250 if (!tab) {
251 qCWarning(LOG_CatalogueEventsWidget())
252 << "updateGraphMode: no tab found in the visualization";
253 return;
254 }
255
256 auto zone = tab->getZoneWithName(m_ZoneForGraphMode);
257 if (!zone) {
258 qCWarning(LOG_CatalogueEventsWidget()) << "updateGraphMode: zone not found";
259 return;
260 }
261
262 // Close the previous graph and delete the asociated variables
263 for (auto graph : m_CustomGraphs) {
264 graph->close();
265 auto variables = graph->variables().toVector();
266
267 QMetaObject::invokeMethod(&sqpApp->variableController(), "deleteVariables",
268 Qt::QueuedConnection,
269 Q_ARG(QVector<std::shared_ptr<Variable> >, variables));
270 }
271 m_CustomGraphs.clear();
272
273 // Calculates the range of each graph which will be created
274 auto graphRange = getGraphRanges(event);
275
276 // Loops through the event products and create the graph
277 auto itRange = graphRange.cbegin();
236 for (auto eventProduct : event->getEventProducts()) {
278 for (auto eventProduct : event->getEventProducts()) {
237 auto productId = eventProduct.getProductId();
279 auto productId = eventProduct.getProductId();
238
280
239 auto range = *itRange;
281 auto range = *itRange;
240 ++itRange;
282 ++itRange;
241
283
284 SqpRange productRange;
285 productRange.m_TStart = eventProduct.getTStart();
286 productRange.m_TEnd = eventProduct.getTEnd();
287
242 auto context = new QObject{treeView};
288 auto context = new QObject{treeView};
243 QObject::connect(
289 QObject::connect(
244 &sqpApp->variableController(), &VariableController::variableAdded,
290 &sqpApp->variableController(), &VariableController::variableAdded, context,
245 context,
291 [this, zone, context, range, productRange, productId](auto variable) {
246 [this, zone, context, range, productId](auto variable) {
247
292
248 if (variable->metadata()
293 if (variable->metadata().value(DataSourceItem::ID_DATA_KEY).toString()
249 .value(DataSourceItem::ID_DATA_KEY, "UnknownID")
250 .toString()
251 == productId) {
294 == productId) {
252 auto graph = zone->createGraph(variable);
295 auto graph = zone->createGraph(variable);
296 graph->setAutoRangeOnVariableInitialization(false);
297
298 graph->addSelectionZones({productRange});
253 m_CustomGraphs << graph;
299 m_CustomGraphs << graph;
254
300
255 graph->setGraphRange(range, true);
301 graph->setGraphRange(range, true);
256
302
303 // Removes the graph from the graph list if it is closed manually
304 QObject::connect(graph, &VisualizationGraphWidget::destroyed,
305 [this, graph]() { m_CustomGraphs.removeAll(graph); });
306
257 delete context; // removes the connection
307 delete context; // removes the connection
258 }
308 }
259 },
309 },
260 Qt::QueuedConnection);
310 Qt::QueuedConnection);
261
311
262 QMetaObject::invokeMethod(
312 QMetaObject::invokeMethod(&sqpApp->dataSourceController(),
263 &sqpApp->dataSourceController(), "requestVariableFromProductIdKey",
313 "requestVariableFromProductIdKey", Qt::QueuedConnection,
264 Qt::QueuedConnection, Q_ARG(QString, productId));
314 Q_ARG(QString, productId));
265 }
266 }
267 }
268 else {
269 qCWarning(LOG_CatalogueEventsWidget())
270 << "updateGraphMode: no tab found in the visualization";
271 }
272 }
273 else {
274 qCWarning(LOG_CatalogueEventsWidget())
275 << "updateGraphMode: visualization widget not found";
276 }
277 }
278 else {
279 qCWarning(LOG_CatalogueEventsWidget())
280 << "updateGraphMode: not compatible with multiple events selected";
281 }
315 }
282 }
316 }
283
317
284 void getSelectedItems(
318 void getSelectedItems(
285 QTreeView *treeView, QVector<std::shared_ptr<DBEvent> > &events,
319 QTreeView *treeView, QVector<std::shared_ptr<DBEvent> > &events,
286 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > &eventProducts)
320 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > &eventProducts)
287 {
321 {
288 for (auto rowIndex : treeView->selectionModel()->selectedRows()) {
322 for (auto rowIndex : treeView->selectionModel()->selectedRows()) {
289 auto itemType = m_Model->itemTypeOf(rowIndex);
323 auto itemType = m_Model->itemTypeOf(rowIndex);
290 if (itemType == CatalogueEventsModel::ItemType::Event) {
324 if (itemType == CatalogueEventsModel::ItemType::Event) {
291 events << m_Model->getEvent(rowIndex);
325 events << m_Model->getEvent(rowIndex);
292 }
326 }
293 else if (itemType == CatalogueEventsModel::ItemType::EventProduct) {
327 else if (itemType == CatalogueEventsModel::ItemType::EventProduct) {
294 eventProducts << qMakePair(m_Model->getParentEvent(rowIndex),
328 eventProducts << qMakePair(m_Model->getParentEvent(rowIndex),
295 m_Model->getEventProduct(rowIndex));
329 m_Model->getEventProduct(rowIndex));
296 }
330 }
297 }
331 }
298 }
332 }
299 };
333 };
300
334
301 CatalogueEventsWidget::CatalogueEventsWidget(QWidget *parent)
335 CatalogueEventsWidget::CatalogueEventsWidget(QWidget *parent)
302 : QWidget(parent),
336 : QWidget(parent),
303 ui(new Ui::CatalogueEventsWidget),
337 ui(new Ui::CatalogueEventsWidget),
304 impl{spimpl::make_unique_impl<CatalogueEventsWidgetPrivate>()}
338 impl{spimpl::make_unique_impl<CatalogueEventsWidgetPrivate>()}
305 {
339 {
306 ui->setupUi(this);
340 ui->setupUi(this);
307
341
308 impl->m_Model = new CatalogueEventsModel{this};
342 impl->m_Model = new CatalogueEventsModel{this};
309 ui->treeView->setModel(impl->m_Model);
343 ui->treeView->setModel(impl->m_Model);
310
344
311 ui->treeView->setSortingEnabled(true);
345 ui->treeView->setSortingEnabled(true);
312 ui->treeView->setDragDropMode(QAbstractItemView::DragDrop);
346 ui->treeView->setDragDropMode(QAbstractItemView::DragDrop);
313 ui->treeView->setDragEnabled(true);
347 ui->treeView->setDragEnabled(true);
314
348
315 connect(ui->btnTime, &QToolButton::clicked, [this](auto checked) {
349 connect(ui->btnTime, &QToolButton::clicked, [this](auto checked) {
316 if (checked) {
350 if (checked) {
317 ui->btnChart->setChecked(false);
351 ui->btnChart->setChecked(false);
318 impl->m_ZonesForTimeMode
352 impl->m_ZonesForTimeMode
319 = impl->selectZone(this, impl->m_ZonesForTimeMode, true,
353 = impl->selectZone(this, impl->m_ZonesForTimeMode, true,
320 this->mapToGlobal(ui->btnTime->frameGeometry().center()));
354 this->mapToGlobal(ui->btnTime->frameGeometry().center()));
321
355
322 impl->updateForTimeMode(ui->treeView);
356 impl->updateForTimeMode(ui->treeView);
323 }
357 }
324 });
358 });
325
359
326 connect(ui->btnChart, &QToolButton::clicked, [this](auto checked) {
360 connect(ui->btnChart, &QToolButton::clicked, [this](auto checked) {
327 if (checked) {
361 if (checked) {
328 ui->btnTime->setChecked(false);
362 ui->btnTime->setChecked(false);
329 impl->m_ZoneForGraphMode
363 impl->m_ZoneForGraphMode
330 = impl->selectZone(this, {impl->m_ZoneForGraphMode}, false,
364 = impl->selectZone(this, {impl->m_ZoneForGraphMode}, false,
331 this->mapToGlobal(ui->btnChart->frameGeometry().center()))
365 this->mapToGlobal(ui->btnChart->frameGeometry().center()))
332 .value(0);
366 .value(0);
333
367
334 impl->updateForGraphMode(ui->treeView);
368 impl->updateForGraphMode(ui->treeView);
335 }
369 }
336 });
370 });
337
371
338 connect(ui->btnRemove, &QToolButton::clicked, [this]() {
372 connect(ui->btnRemove, &QToolButton::clicked, [this]() {
339 QVector<std::shared_ptr<DBEvent> > events;
373 QVector<std::shared_ptr<DBEvent> > events;
340 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > eventProducts;
374 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > eventProducts;
341 impl->getSelectedItems(ui->treeView, events, eventProducts);
375 impl->getSelectedItems(ui->treeView, events, eventProducts);
342
376
343 if (!events.isEmpty() && eventProducts.isEmpty()) {
377 if (!events.isEmpty() && eventProducts.isEmpty()) {
344
378
345 if (QMessageBox::warning(this, tr("Remove Event(s)"),
379 if (QMessageBox::warning(this, tr("Remove Event(s)"),
346 tr("The selected event(s) will be permanently removed "
380 tr("The selected event(s) will be permanently removed "
347 "from the repository!\nAre you sure you want to continue?"),
381 "from the repository!\nAre you sure you want to continue?"),
348 QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
382 QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
349 == QMessageBox::Yes) {
383 == QMessageBox::Yes) {
350
384
351 for (auto event : events) {
385 for (auto event : events) {
352 sqpApp->catalogueController().removeEvent(event);
386 sqpApp->catalogueController().removeEvent(event);
353 impl->removeEvent(event, ui->treeView);
387 impl->removeEvent(event, ui->treeView);
354 }
388 }
355 }
389 }
356 }
390 }
357 });
391 });
358
392
359 connect(ui->treeView, &QTreeView::clicked, this, &CatalogueEventsWidget::emitSelection);
393 connect(ui->treeView, &QTreeView::clicked, this, &CatalogueEventsWidget::emitSelection);
360 connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this,
394 connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this,
361 &CatalogueEventsWidget::emitSelection);
395 &CatalogueEventsWidget::emitSelection);
362
396
363 ui->btnRemove->setEnabled(false); // Disabled by default when nothing is selected
397 ui->btnRemove->setEnabled(false); // Disabled by default when nothing is selected
364 connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, [this]() {
398 connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, [this]() {
365 auto isNotMultiSelection = ui->treeView->selectionModel()->selectedRows().count() <= 1;
399 auto isNotMultiSelection = ui->treeView->selectionModel()->selectedRows().count() <= 1;
366 ui->btnChart->setEnabled(isNotMultiSelection);
400 ui->btnChart->setEnabled(isNotMultiSelection);
367 ui->btnTime->setEnabled(isNotMultiSelection);
401 ui->btnTime->setEnabled(isNotMultiSelection);
368
402
369 if (isNotMultiSelection && ui->btnTime->isChecked()) {
403 if (isNotMultiSelection && ui->btnTime->isChecked()) {
370 impl->updateForTimeMode(ui->treeView);
404 impl->updateForTimeMode(ui->treeView);
371 }
405 }
372 else if (isNotMultiSelection && ui->btnChart->isChecked()) {
406 else if (isNotMultiSelection && ui->btnChart->isChecked()) {
373 impl->updateForGraphMode(ui->treeView);
407 impl->updateForGraphMode(ui->treeView);
374 }
408 }
375
409
376 QVector<std::shared_ptr<DBEvent> > events;
410 QVector<std::shared_ptr<DBEvent> > events;
377 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > eventProducts;
411 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > eventProducts;
378 impl->getSelectedItems(ui->treeView, events, eventProducts);
412 impl->getSelectedItems(ui->treeView, events, eventProducts);
379 ui->btnRemove->setEnabled(!events.isEmpty() && eventProducts.isEmpty());
413 ui->btnRemove->setEnabled(!events.isEmpty() && eventProducts.isEmpty());
380 });
414 });
381
415
382 ui->treeView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
416 ui->treeView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
383 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Tags,
417 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Tags,
384 QHeaderView::Stretch);
418 QHeaderView::Stretch);
385 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Validation,
419 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Validation,
386 QHeaderView::Fixed);
420 QHeaderView::Fixed);
387 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Name,
421 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Name,
388 QHeaderView::Interactive);
422 QHeaderView::Interactive);
389 ui->treeView->header()->resizeSection((int)CatalogueEventsModel::Column::Validation,
423 ui->treeView->header()->resizeSection((int)CatalogueEventsModel::Column::Validation,
390 VALIDATION_COLUMN_SIZE);
424 VALIDATION_COLUMN_SIZE);
391 ui->treeView->header()->setSortIndicatorShown(true);
425 ui->treeView->header()->setSortIndicatorShown(true);
392
426
393 connect(impl->m_Model, &CatalogueEventsModel::modelSorted, [this]() {
427 connect(impl->m_Model, &CatalogueEventsModel::modelSorted, [this]() {
394 auto allEvents = impl->m_Model->events();
428 auto allEvents = impl->m_Model->events();
395 for (auto event : allEvents) {
429 for (auto event : allEvents) {
396 setEventChanges(event, sqpApp->catalogueController().eventHasChanges(event));
430 setEventChanges(event, sqpApp->catalogueController().eventHasChanges(event));
397 }
431 }
398 });
432 });
399
433
400 populateWithAllEvents();
434 populateWithAllEvents();
401 }
435 }
402
436
403 CatalogueEventsWidget::~CatalogueEventsWidget()
437 CatalogueEventsWidget::~CatalogueEventsWidget()
404 {
438 {
405 delete ui;
439 delete ui;
406 }
440 }
407
441
408 void CatalogueEventsWidget::setVisualizationWidget(VisualizationWidget *visualization)
442 void CatalogueEventsWidget::setVisualizationWidget(VisualizationWidget *visualization)
409 {
443 {
410 impl->m_VisualizationWidget = visualization;
444 impl->m_VisualizationWidget = visualization;
411 }
445 }
412
446
413 void CatalogueEventsWidget::addEvent(const std::shared_ptr<DBEvent> &event)
447 void CatalogueEventsWidget::addEvent(const std::shared_ptr<DBEvent> &event)
414 {
448 {
415 impl->addEvent(event, ui->treeView);
449 impl->addEvent(event, ui->treeView);
416 }
450 }
417
451
418 void CatalogueEventsWidget::setEventChanges(const std::shared_ptr<DBEvent> &event, bool hasChanges)
452 void CatalogueEventsWidget::setEventChanges(const std::shared_ptr<DBEvent> &event, bool hasChanges)
419 {
453 {
420 impl->m_Model->refreshEvent(event);
454 impl->m_Model->refreshEvent(event);
421
455
422 auto eventIndex = impl->m_Model->indexOf(event);
456 auto eventIndex = impl->m_Model->indexOf(event);
423 auto validationIndex
457 auto validationIndex
424 = eventIndex.sibling(eventIndex.row(), (int)CatalogueEventsModel::Column::Validation);
458 = eventIndex.sibling(eventIndex.row(), (int)CatalogueEventsModel::Column::Validation);
425
459
426 if (validationIndex.isValid()) {
460 if (validationIndex.isValid()) {
427 if (hasChanges) {
461 if (hasChanges) {
428 if (ui->treeView->indexWidget(validationIndex) == nullptr) {
462 if (ui->treeView->indexWidget(validationIndex) == nullptr) {
429 auto widget = CatalogueExplorerHelper::buildValidationWidget(
463 auto widget = CatalogueExplorerHelper::buildValidationWidget(
430 ui->treeView,
464 ui->treeView,
431 [this, event]() {
465 [this, event]() {
432 sqpApp->catalogueController().saveEvent(event);
466 sqpApp->catalogueController().saveEvent(event);
433 setEventChanges(event, false);
467 setEventChanges(event, false);
434 },
468 },
435 [this, event]() {
469 [this, event]() {
436 bool removed = false;
470 bool removed = false;
437 sqpApp->catalogueController().discardEvent(event, removed);
471 sqpApp->catalogueController().discardEvent(event, removed);
438 if (removed) {
472 if (removed) {
439 impl->m_Model->removeEvent(event);
473 impl->m_Model->removeEvent(event);
440 }
474 }
441 else {
475 else {
442 setEventChanges(event, false);
476 setEventChanges(event, false);
443 impl->m_Model->refreshEvent(event, true);
477 impl->m_Model->refreshEvent(event, true);
444 }
478 }
445 emitSelection();
479 emitSelection();
446 });
480 });
447 ui->treeView->setIndexWidget(validationIndex, widget);
481 ui->treeView->setIndexWidget(validationIndex, widget);
448 }
482 }
449 }
483 }
450 else {
484 else {
451 // Note: the widget is destroyed
485 // Note: the widget is destroyed
452 ui->treeView->setIndexWidget(validationIndex, nullptr);
486 ui->treeView->setIndexWidget(validationIndex, nullptr);
453 }
487 }
454 }
488 }
455 else {
489 else {
456 qCWarning(LOG_CatalogueEventsWidget())
490 qCWarning(LOG_CatalogueEventsWidget())
457 << "setEventChanges: the event is not displayed in the model.";
491 << "setEventChanges: the event is not displayed in the model.";
458 }
492 }
459 }
493 }
460
494
461 QVector<std::shared_ptr<DBCatalogue> > CatalogueEventsWidget::displayedCatalogues() const
495 QVector<std::shared_ptr<DBCatalogue> > CatalogueEventsWidget::displayedCatalogues() const
462 {
496 {
463 return impl->m_DisplayedCatalogues;
497 return impl->m_DisplayedCatalogues;
464 }
498 }
465
499
466 bool CatalogueEventsWidget::isAllEventsDisplayed() const
500 bool CatalogueEventsWidget::isAllEventsDisplayed() const
467 {
501 {
468 return impl->m_AllEventDisplayed;
502 return impl->m_AllEventDisplayed;
469 }
503 }
470
504
471 bool CatalogueEventsWidget::isEventDisplayed(const std::shared_ptr<DBEvent> &event) const
505 bool CatalogueEventsWidget::isEventDisplayed(const std::shared_ptr<DBEvent> &event) const
472 {
506 {
473 return impl->m_Model->indexOf(event).isValid();
507 return impl->m_Model->indexOf(event).isValid();
474 }
508 }
475
509
476 void CatalogueEventsWidget::populateWithCatalogues(
510 void CatalogueEventsWidget::populateWithCatalogues(
477 const QVector<std::shared_ptr<DBCatalogue> > &catalogues)
511 const QVector<std::shared_ptr<DBCatalogue> > &catalogues)
478 {
512 {
479 impl->m_DisplayedCatalogues = catalogues;
513 impl->m_DisplayedCatalogues = catalogues;
480 impl->m_AllEventDisplayed = false;
514 impl->m_AllEventDisplayed = false;
481
515
482 QSet<QUuid> eventIds;
516 QSet<QUuid> eventIds;
483 QVector<std::shared_ptr<DBEvent> > events;
517 QVector<std::shared_ptr<DBEvent> > events;
484
518
485 for (auto catalogue : catalogues) {
519 for (auto catalogue : catalogues) {
486 auto catalogueEvents = sqpApp->catalogueController().retrieveEventsFromCatalogue(catalogue);
520 auto catalogueEvents = sqpApp->catalogueController().retrieveEventsFromCatalogue(catalogue);
487 for (auto event : catalogueEvents) {
521 for (auto event : catalogueEvents) {
488 if (!eventIds.contains(event->getUniqId())) {
522 if (!eventIds.contains(event->getUniqId())) {
489 events << event;
523 events << event;
490 eventIds.insert(event->getUniqId());
524 eventIds.insert(event->getUniqId());
491 }
525 }
492 }
526 }
493 }
527 }
494
528
495 impl->setEvents(events, this);
529 impl->setEvents(events, this);
496 }
530 }
497
531
498 void CatalogueEventsWidget::populateWithAllEvents()
532 void CatalogueEventsWidget::populateWithAllEvents()
499 {
533 {
500 impl->m_DisplayedCatalogues.clear();
534 impl->m_DisplayedCatalogues.clear();
501 impl->m_AllEventDisplayed = true;
535 impl->m_AllEventDisplayed = true;
502
536
503 auto allEvents = sqpApp->catalogueController().retrieveAllEvents();
537 auto allEvents = sqpApp->catalogueController().retrieveAllEvents();
504
538
505 QVector<std::shared_ptr<DBEvent> > events;
539 QVector<std::shared_ptr<DBEvent> > events;
506 for (auto event : allEvents) {
540 for (auto event : allEvents) {
507 events << event;
541 events << event;
508 }
542 }
509
543
510 impl->setEvents(events, this);
544 impl->setEvents(events, this);
511 }
545 }
512
546
513 void CatalogueEventsWidget::clear()
547 void CatalogueEventsWidget::clear()
514 {
548 {
515 impl->m_DisplayedCatalogues.clear();
549 impl->m_DisplayedCatalogues.clear();
516 impl->m_AllEventDisplayed = false;
550 impl->m_AllEventDisplayed = false;
517 impl->setEvents({}, this);
551 impl->setEvents({}, this);
518 }
552 }
519
553
520 void CatalogueEventsWidget::refresh()
554 void CatalogueEventsWidget::refresh()
521 {
555 {
522 if (isAllEventsDisplayed()) {
556 if (isAllEventsDisplayed()) {
523 populateWithAllEvents();
557 populateWithAllEvents();
524 }
558 }
525 else if (!impl->m_DisplayedCatalogues.isEmpty()) {
559 else if (!impl->m_DisplayedCatalogues.isEmpty()) {
526 populateWithCatalogues(impl->m_DisplayedCatalogues);
560 populateWithCatalogues(impl->m_DisplayedCatalogues);
527 }
561 }
528 }
562 }
529
563
530 void CatalogueEventsWidget::emitSelection()
564 void CatalogueEventsWidget::emitSelection()
531 {
565 {
532 QVector<std::shared_ptr<DBEvent> > events;
566 QVector<std::shared_ptr<DBEvent> > events;
533 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > eventProducts;
567 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > eventProducts;
534 impl->getSelectedItems(ui->treeView, events, eventProducts);
568 impl->getSelectedItems(ui->treeView, events, eventProducts);
535
569
536 if (!events.isEmpty() && eventProducts.isEmpty()) {
570 if (!events.isEmpty() && eventProducts.isEmpty()) {
537 emit eventsSelected(events);
571 emit eventsSelected(events);
538 }
572 }
539 else if (events.isEmpty() && !eventProducts.isEmpty()) {
573 else if (events.isEmpty() && !eventProducts.isEmpty()) {
540 emit eventProductsSelected(eventProducts);
574 emit eventProductsSelected(eventProducts);
541 }
575 }
542 else {
576 else {
543 emit selectionCleared();
577 emit selectionCleared();
544 }
578 }
545 }
579 }
@@ -1,1031 +1,1041
1 #include "Visualization/VisualizationGraphWidget.h"
1 #include "Visualization/VisualizationGraphWidget.h"
2 #include "Visualization/IVisualizationWidgetVisitor.h"
2 #include "Visualization/IVisualizationWidgetVisitor.h"
3 #include "Visualization/VisualizationCursorItem.h"
3 #include "Visualization/VisualizationCursorItem.h"
4 #include "Visualization/VisualizationDefs.h"
4 #include "Visualization/VisualizationDefs.h"
5 #include "Visualization/VisualizationGraphHelper.h"
5 #include "Visualization/VisualizationGraphHelper.h"
6 #include "Visualization/VisualizationGraphRenderingDelegate.h"
6 #include "Visualization/VisualizationGraphRenderingDelegate.h"
7 #include "Visualization/VisualizationMultiZoneSelectionDialog.h"
7 #include "Visualization/VisualizationMultiZoneSelectionDialog.h"
8 #include "Visualization/VisualizationSelectionZoneItem.h"
8 #include "Visualization/VisualizationSelectionZoneItem.h"
9 #include "Visualization/VisualizationSelectionZoneManager.h"
9 #include "Visualization/VisualizationSelectionZoneManager.h"
10 #include "Visualization/VisualizationWidget.h"
10 #include "Visualization/VisualizationWidget.h"
11 #include "Visualization/VisualizationZoneWidget.h"
11 #include "Visualization/VisualizationZoneWidget.h"
12 #include "ui_VisualizationGraphWidget.h"
12 #include "ui_VisualizationGraphWidget.h"
13
13
14 #include <Actions/ActionsGuiController.h>
14 #include <Actions/ActionsGuiController.h>
15 #include <Common/MimeTypesDef.h>
15 #include <Common/MimeTypesDef.h>
16 #include <Data/ArrayData.h>
16 #include <Data/ArrayData.h>
17 #include <Data/IDataSeries.h>
17 #include <Data/IDataSeries.h>
18 #include <Data/SpectrogramSeries.h>
18 #include <Data/SpectrogramSeries.h>
19 #include <DragAndDrop/DragDropGuiController.h>
19 #include <DragAndDrop/DragDropGuiController.h>
20 #include <Settings/SqpSettingsDefs.h>
20 #include <Settings/SqpSettingsDefs.h>
21 #include <SqpApplication.h>
21 #include <SqpApplication.h>
22 #include <Time/TimeController.h>
22 #include <Time/TimeController.h>
23 #include <Variable/Variable.h>
23 #include <Variable/Variable.h>
24 #include <Variable/VariableController.h>
24 #include <Variable/VariableController.h>
25
25
26 #include <unordered_map>
26 #include <unordered_map>
27
27
28 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
28 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
29
29
30 namespace {
30 namespace {
31
31
32 /// Key pressed to enable drag&drop in all modes
32 /// Key pressed to enable drag&drop in all modes
33 const auto DRAG_DROP_MODIFIER = Qt::AltModifier;
33 const auto DRAG_DROP_MODIFIER = Qt::AltModifier;
34
34
35 /// Key pressed to enable zoom on horizontal axis
35 /// Key pressed to enable zoom on horizontal axis
36 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier;
36 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier;
37
37
38 /// Key pressed to enable zoom on vertical axis
38 /// Key pressed to enable zoom on vertical axis
39 const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier;
39 const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier;
40
40
41 /// Speed of a step of a wheel event for a pan, in percentage of the axis range
41 /// Speed of a step of a wheel event for a pan, in percentage of the axis range
42 const auto PAN_SPEED = 5;
42 const auto PAN_SPEED = 5;
43
43
44 /// Key pressed to enable a calibration pan
44 /// Key pressed to enable a calibration pan
45 const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier;
45 const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier;
46
46
47 /// Key pressed to enable multi selection of selection zones
47 /// Key pressed to enable multi selection of selection zones
48 const auto MULTI_ZONE_SELECTION_MODIFIER = Qt::ControlModifier;
48 const auto MULTI_ZONE_SELECTION_MODIFIER = Qt::ControlModifier;
49
49
50 /// Minimum size for the zoom box, in percentage of the axis range
50 /// Minimum size for the zoom box, in percentage of the axis range
51 const auto ZOOM_BOX_MIN_SIZE = 0.8;
51 const auto ZOOM_BOX_MIN_SIZE = 0.8;
52
52
53 /// Format of the dates appearing in the label of a cursor
53 /// Format of the dates appearing in the label of a cursor
54 const auto CURSOR_LABELS_DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd\nhh:mm:ss:zzz");
54 const auto CURSOR_LABELS_DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd\nhh:mm:ss:zzz");
55
55
56 } // namespace
56 } // namespace
57
57
58 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
58 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
59
59
60 explicit VisualizationGraphWidgetPrivate(const QString &name)
60 explicit VisualizationGraphWidgetPrivate(const QString &name)
61 : m_Name{name},
61 : m_Name{name},
62 m_Flags{GraphFlag::EnableAll},
62 m_Flags{GraphFlag::EnableAll},
63 m_IsCalibration{false},
63 m_IsCalibration{false},
64 m_RenderingDelegate{nullptr}
64 m_RenderingDelegate{nullptr}
65 {
65 {
66 }
66 }
67
67
68 void updateData(PlottablesMap &plottables, std::shared_ptr<Variable> variable,
68 void updateData(PlottablesMap &plottables, std::shared_ptr<Variable> variable,
69 const SqpRange &range)
69 const SqpRange &range)
70 {
70 {
71 VisualizationGraphHelper::updateData(plottables, variable, range);
71 VisualizationGraphHelper::updateData(plottables, variable, range);
72
72
73 // Prevents that data has changed to update rendering
73 // Prevents that data has changed to update rendering
74 m_RenderingDelegate->onPlotUpdated();
74 m_RenderingDelegate->onPlotUpdated();
75 }
75 }
76
76
77 QString m_Name;
77 QString m_Name;
78 // 1 variable -> n qcpplot
78 // 1 variable -> n qcpplot
79 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
79 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
80 GraphFlags m_Flags;
80 GraphFlags m_Flags;
81 bool m_IsCalibration;
81 bool m_IsCalibration;
82 /// Delegate used to attach rendering features to the plot
82 /// Delegate used to attach rendering features to the plot
83 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
83 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
84
84
85 QCPItemRect *m_DrawingZoomRect = nullptr;
85 QCPItemRect *m_DrawingZoomRect = nullptr;
86 QStack<QPair<QCPRange, QCPRange> > m_ZoomStack;
86 QStack<QPair<QCPRange, QCPRange> > m_ZoomStack;
87
87
88 std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr;
88 std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr;
89 std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr;
89 std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr;
90
90
91 VisualizationSelectionZoneItem *m_DrawingZone = nullptr;
91 VisualizationSelectionZoneItem *m_DrawingZone = nullptr;
92 VisualizationSelectionZoneItem *m_HoveredZone = nullptr;
92 VisualizationSelectionZoneItem *m_HoveredZone = nullptr;
93 QVector<VisualizationSelectionZoneItem *> m_SelectionZones;
93 QVector<VisualizationSelectionZoneItem *> m_SelectionZones;
94
94
95 bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even
95 bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even
96
96
97 bool m_VariableAutoRangeOnInit = true;
98
97 void startDrawingRect(const QPoint &pos, QCustomPlot &plot)
99 void startDrawingRect(const QPoint &pos, QCustomPlot &plot)
98 {
100 {
99 removeDrawingRect(plot);
101 removeDrawingRect(plot);
100
102
101 auto axisPos = posToAxisPos(pos, plot);
103 auto axisPos = posToAxisPos(pos, plot);
102
104
103 m_DrawingZoomRect = new QCPItemRect{&plot};
105 m_DrawingZoomRect = new QCPItemRect{&plot};
104 QPen p;
106 QPen p;
105 p.setWidth(2);
107 p.setWidth(2);
106 m_DrawingZoomRect->setPen(p);
108 m_DrawingZoomRect->setPen(p);
107
109
108 m_DrawingZoomRect->topLeft->setCoords(axisPos);
110 m_DrawingZoomRect->topLeft->setCoords(axisPos);
109 m_DrawingZoomRect->bottomRight->setCoords(axisPos);
111 m_DrawingZoomRect->bottomRight->setCoords(axisPos);
110 }
112 }
111
113
112 void removeDrawingRect(QCustomPlot &plot)
114 void removeDrawingRect(QCustomPlot &plot)
113 {
115 {
114 if (m_DrawingZoomRect) {
116 if (m_DrawingZoomRect) {
115 plot.removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot
117 plot.removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot
116 m_DrawingZoomRect = nullptr;
118 m_DrawingZoomRect = nullptr;
117 plot.replot(QCustomPlot::rpQueuedReplot);
119 plot.replot(QCustomPlot::rpQueuedReplot);
118 }
120 }
119 }
121 }
120
122
121 void startDrawingZone(const QPoint &pos, VisualizationGraphWidget *graph)
123 void startDrawingZone(const QPoint &pos, VisualizationGraphWidget *graph)
122 {
124 {
123 endDrawingZone(graph);
125 endDrawingZone(graph);
124
126
125 auto axisPos = posToAxisPos(pos, graph->plot());
127 auto axisPos = posToAxisPos(pos, graph->plot());
126
128
127 m_DrawingZone = new VisualizationSelectionZoneItem{&graph->plot()};
129 m_DrawingZone = new VisualizationSelectionZoneItem{&graph->plot()};
128 m_DrawingZone->setRange(axisPos.x(), axisPos.x());
130 m_DrawingZone->setRange(axisPos.x(), axisPos.x());
129 m_DrawingZone->setEditionEnabled(false);
131 m_DrawingZone->setEditionEnabled(false);
130 }
132 }
131
133
132 void endDrawingZone(VisualizationGraphWidget *graph)
134 void endDrawingZone(VisualizationGraphWidget *graph)
133 {
135 {
134 if (m_DrawingZone) {
136 if (m_DrawingZone) {
135 auto drawingZoneRange = m_DrawingZone->range();
137 auto drawingZoneRange = m_DrawingZone->range();
136 if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0) {
138 if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0) {
137 m_DrawingZone->setEditionEnabled(true);
139 m_DrawingZone->setEditionEnabled(true);
138 addSelectionZone(m_DrawingZone);
140 addSelectionZone(m_DrawingZone);
139 }
141 }
140 else {
142 else {
141 graph->plot().removeItem(m_DrawingZone); // the item is deleted by QCustomPlot
143 graph->plot().removeItem(m_DrawingZone); // the item is deleted by QCustomPlot
142 }
144 }
143
145
144 graph->plot().replot(QCustomPlot::rpQueuedReplot);
146 graph->plot().replot(QCustomPlot::rpQueuedReplot);
145 m_DrawingZone = nullptr;
147 m_DrawingZone = nullptr;
146 }
148 }
147 }
149 }
148
150
149 void setSelectionZonesEditionEnabled(bool value)
151 void setSelectionZonesEditionEnabled(bool value)
150 {
152 {
151 for (auto s : m_SelectionZones) {
153 for (auto s : m_SelectionZones) {
152 s->setEditionEnabled(value);
154 s->setEditionEnabled(value);
153 }
155 }
154 }
156 }
155
157
156 void addSelectionZone(VisualizationSelectionZoneItem *zone) { m_SelectionZones << zone; }
158 void addSelectionZone(VisualizationSelectionZoneItem *zone) { m_SelectionZones << zone; }
157
159
158 VisualizationSelectionZoneItem *selectionZoneAt(const QPoint &pos,
160 VisualizationSelectionZoneItem *selectionZoneAt(const QPoint &pos,
159 const QCustomPlot &plot) const
161 const QCustomPlot &plot) const
160 {
162 {
161 VisualizationSelectionZoneItem *selectionZoneItemUnderCursor = nullptr;
163 VisualizationSelectionZoneItem *selectionZoneItemUnderCursor = nullptr;
162 auto minDistanceToZone = -1;
164 auto minDistanceToZone = -1;
163 for (auto zone : m_SelectionZones) {
165 for (auto zone : m_SelectionZones) {
164 auto distanceToZone = zone->selectTest(pos, false);
166 auto distanceToZone = zone->selectTest(pos, false);
165 if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone)
167 if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone)
166 && distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
168 && distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
167 selectionZoneItemUnderCursor = zone;
169 selectionZoneItemUnderCursor = zone;
168 }
170 }
169 }
171 }
170
172
171 return selectionZoneItemUnderCursor;
173 return selectionZoneItemUnderCursor;
172 }
174 }
173
175
174 QVector<VisualizationSelectionZoneItem *> selectionZonesAt(const QPoint &pos,
176 QVector<VisualizationSelectionZoneItem *> selectionZonesAt(const QPoint &pos,
175 const QCustomPlot &plot) const
177 const QCustomPlot &plot) const
176 {
178 {
177 QVector<VisualizationSelectionZoneItem *> zones;
179 QVector<VisualizationSelectionZoneItem *> zones;
178 for (auto zone : m_SelectionZones) {
180 for (auto zone : m_SelectionZones) {
179 auto distanceToZone = zone->selectTest(pos, false);
181 auto distanceToZone = zone->selectTest(pos, false);
180 if (distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
182 if (distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
181 zones << zone;
183 zones << zone;
182 }
184 }
183 }
185 }
184
186
185 return zones;
187 return zones;
186 }
188 }
187
189
188 void moveSelectionZoneOnTop(VisualizationSelectionZoneItem *zone, QCustomPlot &plot)
190 void moveSelectionZoneOnTop(VisualizationSelectionZoneItem *zone, QCustomPlot &plot)
189 {
191 {
190 if (!m_SelectionZones.isEmpty() && m_SelectionZones.last() != zone) {
192 if (!m_SelectionZones.isEmpty() && m_SelectionZones.last() != zone) {
191 zone->moveToTop();
193 zone->moveToTop();
192 m_SelectionZones.removeAll(zone);
194 m_SelectionZones.removeAll(zone);
193 m_SelectionZones.append(zone);
195 m_SelectionZones.append(zone);
194 }
196 }
195 }
197 }
196
198
197 QPointF posToAxisPos(const QPoint &pos, QCustomPlot &plot) const
199 QPointF posToAxisPos(const QPoint &pos, QCustomPlot &plot) const
198 {
200 {
199 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
201 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
200 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
202 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
201 return QPointF{axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y())};
203 return QPointF{axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y())};
202 }
204 }
203
205
204 bool pointIsInAxisRect(const QPointF &axisPoint, QCustomPlot &plot) const
206 bool pointIsInAxisRect(const QPointF &axisPoint, QCustomPlot &plot) const
205 {
207 {
206 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
208 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
207 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
209 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
208 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
210 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
209 }
211 }
210 };
212 };
211
213
212 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
214 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
213 : VisualizationDragWidget{parent},
215 : VisualizationDragWidget{parent},
214 ui{new Ui::VisualizationGraphWidget},
216 ui{new Ui::VisualizationGraphWidget},
215 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
217 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
216 {
218 {
217 ui->setupUi(this);
219 ui->setupUi(this);
218
220
219 // 'Close' options : widget is deleted when closed
221 // 'Close' options : widget is deleted when closed
220 setAttribute(Qt::WA_DeleteOnClose);
222 setAttribute(Qt::WA_DeleteOnClose);
221
223
222 // Set qcpplot properties :
224 // Set qcpplot properties :
223 // - zoom is enabled
225 // - zoom is enabled
224 // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation
226 // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation
225 ui->widget->setInteractions(QCP::iRangeZoom);
227 ui->widget->setInteractions(QCP::iRangeZoom);
226 ui->widget->axisRect()->setRangeDrag(Qt::Horizontal | Qt::Vertical);
228 ui->widget->axisRect()->setRangeDrag(Qt::Horizontal | Qt::Vertical);
227
229
228 // The delegate must be initialized after the ui as it uses the plot
230 // The delegate must be initialized after the ui as it uses the plot
229 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
231 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
230
232
231 // Init the cursors
233 // Init the cursors
232 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
234 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
233 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
235 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
234 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
236 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
235 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
237 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
236
238
237 connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress);
239 connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress);
238 connect(ui->widget, &QCustomPlot::mouseRelease, this,
240 connect(ui->widget, &QCustomPlot::mouseRelease, this,
239 &VisualizationGraphWidget::onMouseRelease);
241 &VisualizationGraphWidget::onMouseRelease);
240 connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove);
242 connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove);
241 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
243 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
242 connect(ui->widget, &QCustomPlot::mouseDoubleClick, this,
244 connect(ui->widget, &QCustomPlot::mouseDoubleClick, this,
243 &VisualizationGraphWidget::onMouseDoubleClick);
245 &VisualizationGraphWidget::onMouseDoubleClick);
244 connect(ui->widget->xAxis, static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(
246 connect(ui->widget->xAxis, static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(
245 &QCPAxis::rangeChanged),
247 &QCPAxis::rangeChanged),
246 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
248 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
247
249
248 // Activates menu when right clicking on the graph
250 // Activates menu when right clicking on the graph
249 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
251 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
250 connect(ui->widget, &QCustomPlot::customContextMenuRequested, this,
252 connect(ui->widget, &QCustomPlot::customContextMenuRequested, this,
251 &VisualizationGraphWidget::onGraphMenuRequested);
253 &VisualizationGraphWidget::onGraphMenuRequested);
252
254
253 connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
255 connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
254 &VariableController::onRequestDataLoading);
256 &VariableController::onRequestDataLoading);
255
257
256 connect(&sqpApp->variableController(), &VariableController::updateVarDisplaying, this,
258 connect(&sqpApp->variableController(), &VariableController::updateVarDisplaying, this,
257 &VisualizationGraphWidget::onUpdateVarDisplaying);
259 &VisualizationGraphWidget::onUpdateVarDisplaying);
258
260
259 #ifdef Q_OS_MAC
261 #ifdef Q_OS_MAC
260 plot().setPlottingHint(QCP::phFastPolylines, true);
262 plot().setPlottingHint(QCP::phFastPolylines, true);
261 #endif
263 #endif
262 }
264 }
263
265
264
266
265 VisualizationGraphWidget::~VisualizationGraphWidget()
267 VisualizationGraphWidget::~VisualizationGraphWidget()
266 {
268 {
267 delete ui;
269 delete ui;
268 }
270 }
269
271
270 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
272 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
271 {
273 {
272 auto parent = parentWidget();
274 auto parent = parentWidget();
273 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
275 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
274 parent = parent->parentWidget();
276 parent = parent->parentWidget();
275 }
277 }
276
278
277 return qobject_cast<VisualizationZoneWidget *>(parent);
279 return qobject_cast<VisualizationZoneWidget *>(parent);
278 }
280 }
279
281
280 VisualizationWidget *VisualizationGraphWidget::parentVisualizationWidget() const
282 VisualizationWidget *VisualizationGraphWidget::parentVisualizationWidget() const
281 {
283 {
282 auto parent = parentWidget();
284 auto parent = parentWidget();
283 while (parent != nullptr && !qobject_cast<VisualizationWidget *>(parent)) {
285 while (parent != nullptr && !qobject_cast<VisualizationWidget *>(parent)) {
284 parent = parent->parentWidget();
286 parent = parent->parentWidget();
285 }
287 }
286
288
287 return qobject_cast<VisualizationWidget *>(parent);
289 return qobject_cast<VisualizationWidget *>(parent);
288 }
290 }
289
291
290 void VisualizationGraphWidget::setFlags(GraphFlags flags)
292 void VisualizationGraphWidget::setFlags(GraphFlags flags)
291 {
293 {
292 impl->m_Flags = std::move(flags);
294 impl->m_Flags = std::move(flags);
293 }
295 }
294
296
295 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, SqpRange range)
297 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, SqpRange range)
296 {
298 {
297 /// Lambda used to set graph's units and range according to the variable passed in parameter
299 /// Lambda used to set graph's units and range according to the variable passed in parameter
298 auto loadRange = [this](std::shared_ptr<Variable> variable, const SqpRange &range) {
300 auto loadRange = [this](std::shared_ptr<Variable> variable, const SqpRange &range) {
299 impl->m_RenderingDelegate->setAxesUnits(*variable);
301 impl->m_RenderingDelegate->setAxesUnits(*variable);
300
302
301 this->setFlags(GraphFlag::DisableAll);
303 this->setFlags(GraphFlag::DisableAll);
302 setGraphRange(range);
304 setGraphRange(range);
303 this->setFlags(GraphFlag::EnableAll);
305 this->setFlags(GraphFlag::EnableAll);
304
306
305 emit requestDataLoading({variable}, range, false);
307 emit requestDataLoading({variable}, range, false);
306 };
308 };
307
309
308 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
310 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
309
311
310 // Calls update of graph's range and units when the data of the variable have been initialized.
312 // Calls update of graph's range and units when the data of the variable have been initialized.
311 // Note: we use QueuedConnection here as the update event must be called in the UI thread
313 // Note: we use QueuedConnection here as the update event must be called in the UI thread
312 connect(variable.get(), &Variable::dataInitialized, this,
314 connect(variable.get(), &Variable::dataInitialized, this,
313 [ varW = std::weak_ptr<Variable>{variable}, range, loadRange, this ]() {
315 [ varW = std::weak_ptr<Variable>{variable}, range, loadRange, this ]() {
314 if (auto var = varW.lock()) {
316 if (auto var = varW.lock()) {
315 // If the variable is the first added in the graph, we load its range
317 // If the variable is the first added in the graph, we load its range
316 auto firstVariableInGraph = range == INVALID_RANGE;
318 auto firstVariableInGraph = range == INVALID_RANGE;
317 auto loadedRange = firstVariableInGraph ? var->range() : range;
319 auto loadedRange = graphRange();
320 if (impl->m_VariableAutoRangeOnInit) {
321 loadedRange = firstVariableInGraph ? var->range() : range;
322 }
318 loadRange(var, loadedRange);
323 loadRange(var, loadedRange);
319 setYRange(var);
324 setYRange(var);
320 }
325 }
321 },
326 },
322 Qt::QueuedConnection);
327 Qt::QueuedConnection);
323
328
324 // Uses delegate to create the qcpplot components according to the variable
329 // Uses delegate to create the qcpplot components according to the variable
325 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
330 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
326
331
327 // Sets graph properties
332 // Sets graph properties
328 impl->m_RenderingDelegate->setGraphProperties(*variable, createdPlottables);
333 impl->m_RenderingDelegate->setGraphProperties(*variable, createdPlottables);
329
334
330 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
335 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
331
336
332 // If the variable already has its data loaded, load its units and its range in the graph
337 // If the variable already has its data loaded, load its units and its range in the graph
333 if (variable->dataSeries() != nullptr) {
338 if (variable->dataSeries() != nullptr) {
334 loadRange(variable, range);
339 loadRange(variable, range);
335 }
340 }
336
341
337 emit variableAdded(variable);
342 emit variableAdded(variable);
338 }
343 }
339
344
340 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
345 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
341 {
346 {
342 // Each component associated to the variable :
347 // Each component associated to the variable :
343 // - is removed from qcpplot (which deletes it)
348 // - is removed from qcpplot (which deletes it)
344 // - is no longer referenced in the map
349 // - is no longer referenced in the map
345 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
350 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
346 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
351 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
347 emit variableAboutToBeRemoved(variable);
352 emit variableAboutToBeRemoved(variable);
348
353
349 auto &plottablesMap = variableIt->second;
354 auto &plottablesMap = variableIt->second;
350
355
351 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
356 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
352 plottableIt != plottableEnd;) {
357 plottableIt != plottableEnd;) {
353 ui->widget->removePlottable(plottableIt->second);
358 ui->widget->removePlottable(plottableIt->second);
354 plottableIt = plottablesMap.erase(plottableIt);
359 plottableIt = plottablesMap.erase(plottableIt);
355 }
360 }
356
361
357 impl->m_VariableToPlotMultiMap.erase(variableIt);
362 impl->m_VariableToPlotMultiMap.erase(variableIt);
358 }
363 }
359
364
360 // Updates graph
365 // Updates graph
361 ui->widget->replot();
366 ui->widget->replot();
362 }
367 }
363
368
364 QList<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
369 QList<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
365 {
370 {
366 auto variables = QList<std::shared_ptr<Variable> >{};
371 auto variables = QList<std::shared_ptr<Variable> >{};
367 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
372 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
368 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
373 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
369 variables << it->first;
374 variables << it->first;
370 }
375 }
371
376
372 return variables;
377 return variables;
373 }
378 }
374
379
375 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
380 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
376 {
381 {
377 if (!variable) {
382 if (!variable) {
378 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
383 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
379 return;
384 return;
380 }
385 }
381
386
382 VisualizationGraphHelper::setYAxisRange(variable, *ui->widget);
387 VisualizationGraphHelper::setYAxisRange(variable, *ui->widget);
383 }
388 }
384
389
385 SqpRange VisualizationGraphWidget::graphRange() const noexcept
390 SqpRange VisualizationGraphWidget::graphRange() const noexcept
386 {
391 {
387 auto graphRange = ui->widget->xAxis->range();
392 auto graphRange = ui->widget->xAxis->range();
388 return SqpRange{graphRange.lower, graphRange.upper};
393 return SqpRange{graphRange.lower, graphRange.upper};
389 }
394 }
390
395
391 void VisualizationGraphWidget::setGraphRange(const SqpRange &range, bool calibration)
396 void VisualizationGraphWidget::setGraphRange(const SqpRange &range, bool calibration)
392 {
397 {
393 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange START");
398 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange START");
394
399
395 if (calibration) {
400 if (calibration) {
396 impl->m_IsCalibration = true;
401 impl->m_IsCalibration = true;
397 }
402 }
398
403
399 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
404 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
400 ui->widget->replot();
405 ui->widget->replot();
401
406
402 if (calibration) {
407 if (calibration) {
403 impl->m_IsCalibration = false;
408 impl->m_IsCalibration = false;
404 }
409 }
405
410
406 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange END");
411 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange END");
407 }
412 }
408
413
414 void VisualizationGraphWidget::setAutoRangeOnVariableInitialization(bool value)
415 {
416 impl->m_VariableAutoRangeOnInit = value;
417 }
418
409 QVector<SqpRange> VisualizationGraphWidget::selectionZoneRanges() const
419 QVector<SqpRange> VisualizationGraphWidget::selectionZoneRanges() const
410 {
420 {
411 QVector<SqpRange> ranges;
421 QVector<SqpRange> ranges;
412 for (auto zone : impl->m_SelectionZones) {
422 for (auto zone : impl->m_SelectionZones) {
413 ranges << zone->range();
423 ranges << zone->range();
414 }
424 }
415
425
416 return ranges;
426 return ranges;
417 }
427 }
418
428
419 void VisualizationGraphWidget::addSelectionZones(const QVector<SqpRange> &ranges)
429 void VisualizationGraphWidget::addSelectionZones(const QVector<SqpRange> &ranges)
420 {
430 {
421 for (const auto &range : ranges) {
431 for (const auto &range : ranges) {
422 // note: ownership is transfered to QCustomPlot
432 // note: ownership is transfered to QCustomPlot
423 auto zone = new VisualizationSelectionZoneItem(&plot());
433 auto zone = new VisualizationSelectionZoneItem(&plot());
424 zone->setRange(range.m_TStart, range.m_TEnd);
434 zone->setRange(range.m_TStart, range.m_TEnd);
425 impl->addSelectionZone(zone);
435 impl->addSelectionZone(zone);
426 }
436 }
427
437
428 plot().replot(QCustomPlot::rpQueuedReplot);
438 plot().replot(QCustomPlot::rpQueuedReplot);
429 }
439 }
430
440
431 void VisualizationGraphWidget::removeSelectionZone(VisualizationSelectionZoneItem *selectionZone)
441 void VisualizationGraphWidget::removeSelectionZone(VisualizationSelectionZoneItem *selectionZone)
432 {
442 {
433 parentVisualizationWidget()->selectionZoneManager().setSelected(selectionZone, false);
443 parentVisualizationWidget()->selectionZoneManager().setSelected(selectionZone, false);
434
444
435 if (impl->m_HoveredZone == selectionZone) {
445 if (impl->m_HoveredZone == selectionZone) {
436 impl->m_HoveredZone = nullptr;
446 impl->m_HoveredZone = nullptr;
437 setCursor(Qt::ArrowCursor);
447 setCursor(Qt::ArrowCursor);
438 }
448 }
439
449
440 impl->m_SelectionZones.removeAll(selectionZone);
450 impl->m_SelectionZones.removeAll(selectionZone);
441 plot().removeItem(selectionZone);
451 plot().removeItem(selectionZone);
442 plot().replot(QCustomPlot::rpQueuedReplot);
452 plot().replot(QCustomPlot::rpQueuedReplot);
443 }
453 }
444
454
445 void VisualizationGraphWidget::undoZoom()
455 void VisualizationGraphWidget::undoZoom()
446 {
456 {
447 auto zoom = impl->m_ZoomStack.pop();
457 auto zoom = impl->m_ZoomStack.pop();
448 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
458 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
449 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
459 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
450
460
451 axisX->setRange(zoom.first);
461 axisX->setRange(zoom.first);
452 axisY->setRange(zoom.second);
462 axisY->setRange(zoom.second);
453
463
454 plot().replot(QCustomPlot::rpQueuedReplot);
464 plot().replot(QCustomPlot::rpQueuedReplot);
455 }
465 }
456
466
457 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
467 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
458 {
468 {
459 if (visitor) {
469 if (visitor) {
460 visitor->visit(this);
470 visitor->visit(this);
461 }
471 }
462 else {
472 else {
463 qCCritical(LOG_VisualizationGraphWidget())
473 qCCritical(LOG_VisualizationGraphWidget())
464 << tr("Can't visit widget : the visitor is null");
474 << tr("Can't visit widget : the visitor is null");
465 }
475 }
466 }
476 }
467
477
468 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
478 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
469 {
479 {
470 auto isSpectrogram = [](const auto &variable) {
480 auto isSpectrogram = [](const auto &variable) {
471 return std::dynamic_pointer_cast<SpectrogramSeries>(variable.dataSeries()) != nullptr;
481 return std::dynamic_pointer_cast<SpectrogramSeries>(variable.dataSeries()) != nullptr;
472 };
482 };
473
483
474 // - A spectrogram series can't be dropped on graph with existing plottables
484 // - A spectrogram series can't be dropped on graph with existing plottables
475 // - No data series can be dropped on graph with existing spectrogram series
485 // - No data series can be dropped on graph with existing spectrogram series
476 return isSpectrogram(variable)
486 return isSpectrogram(variable)
477 ? impl->m_VariableToPlotMultiMap.empty()
487 ? impl->m_VariableToPlotMultiMap.empty()
478 : std::none_of(
488 : std::none_of(
479 impl->m_VariableToPlotMultiMap.cbegin(), impl->m_VariableToPlotMultiMap.cend(),
489 impl->m_VariableToPlotMultiMap.cbegin(), impl->m_VariableToPlotMultiMap.cend(),
480 [isSpectrogram](const auto &entry) { return isSpectrogram(*entry.first); });
490 [isSpectrogram](const auto &entry) { return isSpectrogram(*entry.first); });
481 }
491 }
482
492
483 bool VisualizationGraphWidget::contains(const Variable &variable) const
493 bool VisualizationGraphWidget::contains(const Variable &variable) const
484 {
494 {
485 // Finds the variable among the keys of the map
495 // Finds the variable among the keys of the map
486 auto variablePtr = &variable;
496 auto variablePtr = &variable;
487 auto findVariable
497 auto findVariable
488 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
498 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
489
499
490 auto end = impl->m_VariableToPlotMultiMap.cend();
500 auto end = impl->m_VariableToPlotMultiMap.cend();
491 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
501 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
492 return it != end;
502 return it != end;
493 }
503 }
494
504
495 QString VisualizationGraphWidget::name() const
505 QString VisualizationGraphWidget::name() const
496 {
506 {
497 return impl->m_Name;
507 return impl->m_Name;
498 }
508 }
499
509
500 QMimeData *VisualizationGraphWidget::mimeData(const QPoint &position) const
510 QMimeData *VisualizationGraphWidget::mimeData(const QPoint &position) const
501 {
511 {
502 auto mimeData = new QMimeData;
512 auto mimeData = new QMimeData;
503
513
504 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position, plot());
514 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position, plot());
505 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
515 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
506 && selectionZoneItemUnderCursor) {
516 && selectionZoneItemUnderCursor) {
507 mimeData->setData(MIME_TYPE_TIME_RANGE, TimeController::mimeDataForTimeRange(
517 mimeData->setData(MIME_TYPE_TIME_RANGE, TimeController::mimeDataForTimeRange(
508 selectionZoneItemUnderCursor->range()));
518 selectionZoneItemUnderCursor->range()));
509 mimeData->setData(MIME_TYPE_SELECTION_ZONE, TimeController::mimeDataForTimeRange(
519 mimeData->setData(MIME_TYPE_SELECTION_ZONE, TimeController::mimeDataForTimeRange(
510 selectionZoneItemUnderCursor->range()));
520 selectionZoneItemUnderCursor->range()));
511 }
521 }
512 else {
522 else {
513 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
523 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
514
524
515 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
525 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
516 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
526 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
517 }
527 }
518
528
519 return mimeData;
529 return mimeData;
520 }
530 }
521
531
522 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint &dragPosition)
532 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint &dragPosition)
523 {
533 {
524 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition, plot());
534 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition, plot());
525 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
535 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
526 && selectionZoneItemUnderCursor) {
536 && selectionZoneItemUnderCursor) {
527
537
528 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
538 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
529 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
539 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
530
540
531 auto zoneSize = QSizeF{qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
541 auto zoneSize = QSizeF{qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
532 qAbs(zoneBottomRight.y() - zoneTopLeft.y())}
542 qAbs(zoneBottomRight.y() - zoneTopLeft.y())}
533 .toSize();
543 .toSize();
534
544
535 auto pixmap = QPixmap(zoneSize);
545 auto pixmap = QPixmap(zoneSize);
536 render(&pixmap, QPoint(), QRegion{QRect{zoneTopLeft.toPoint(), zoneSize}});
546 render(&pixmap, QPoint(), QRegion{QRect{zoneTopLeft.toPoint(), zoneSize}});
537
547
538 return pixmap;
548 return pixmap;
539 }
549 }
540
550
541 return QPixmap();
551 return QPixmap();
542 }
552 }
543
553
544 bool VisualizationGraphWidget::isDragAllowed() const
554 bool VisualizationGraphWidget::isDragAllowed() const
545 {
555 {
546 return true;
556 return true;
547 }
557 }
548
558
549 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
559 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
550 {
560 {
551 if (highlighted) {
561 if (highlighted) {
552 plot().setBackground(QBrush(QColor("#BBD5EE")));
562 plot().setBackground(QBrush(QColor("#BBD5EE")));
553 }
563 }
554 else {
564 else {
555 plot().setBackground(QBrush(Qt::white));
565 plot().setBackground(QBrush(Qt::white));
556 }
566 }
557
567
558 plot().update();
568 plot().update();
559 }
569 }
560
570
561 void VisualizationGraphWidget::addVerticalCursor(double time)
571 void VisualizationGraphWidget::addVerticalCursor(double time)
562 {
572 {
563 impl->m_VerticalCursor->setPosition(time);
573 impl->m_VerticalCursor->setPosition(time);
564 impl->m_VerticalCursor->setVisible(true);
574 impl->m_VerticalCursor->setVisible(true);
565
575
566 auto text
576 auto text
567 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
577 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
568 impl->m_VerticalCursor->setLabelText(text);
578 impl->m_VerticalCursor->setLabelText(text);
569 }
579 }
570
580
571 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
581 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
572 {
582 {
573 impl->m_VerticalCursor->setAbsolutePosition(position);
583 impl->m_VerticalCursor->setAbsolutePosition(position);
574 impl->m_VerticalCursor->setVisible(true);
584 impl->m_VerticalCursor->setVisible(true);
575
585
576 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
586 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
577 auto text
587 auto text
578 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
588 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
579 impl->m_VerticalCursor->setLabelText(text);
589 impl->m_VerticalCursor->setLabelText(text);
580 }
590 }
581
591
582 void VisualizationGraphWidget::removeVerticalCursor()
592 void VisualizationGraphWidget::removeVerticalCursor()
583 {
593 {
584 impl->m_VerticalCursor->setVisible(false);
594 impl->m_VerticalCursor->setVisible(false);
585 plot().replot(QCustomPlot::rpQueuedReplot);
595 plot().replot(QCustomPlot::rpQueuedReplot);
586 }
596 }
587
597
588 void VisualizationGraphWidget::addHorizontalCursor(double value)
598 void VisualizationGraphWidget::addHorizontalCursor(double value)
589 {
599 {
590 impl->m_HorizontalCursor->setPosition(value);
600 impl->m_HorizontalCursor->setPosition(value);
591 impl->m_HorizontalCursor->setVisible(true);
601 impl->m_HorizontalCursor->setVisible(true);
592 impl->m_HorizontalCursor->setLabelText(QString::number(value));
602 impl->m_HorizontalCursor->setLabelText(QString::number(value));
593 }
603 }
594
604
595 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
605 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
596 {
606 {
597 impl->m_HorizontalCursor->setAbsolutePosition(position);
607 impl->m_HorizontalCursor->setAbsolutePosition(position);
598 impl->m_HorizontalCursor->setVisible(true);
608 impl->m_HorizontalCursor->setVisible(true);
599
609
600 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
610 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
601 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
611 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
602 }
612 }
603
613
604 void VisualizationGraphWidget::removeHorizontalCursor()
614 void VisualizationGraphWidget::removeHorizontalCursor()
605 {
615 {
606 impl->m_HorizontalCursor->setVisible(false);
616 impl->m_HorizontalCursor->setVisible(false);
607 plot().replot(QCustomPlot::rpQueuedReplot);
617 plot().replot(QCustomPlot::rpQueuedReplot);
608 }
618 }
609
619
610 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
620 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
611 {
621 {
612 Q_UNUSED(event);
622 Q_UNUSED(event);
613
623
614 // Prevents that all variables will be removed from graph when it will be closed
624 // Prevents that all variables will be removed from graph when it will be closed
615 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
625 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
616 emit variableAboutToBeRemoved(variableEntry.first);
626 emit variableAboutToBeRemoved(variableEntry.first);
617 }
627 }
618 }
628 }
619
629
620 void VisualizationGraphWidget::enterEvent(QEvent *event)
630 void VisualizationGraphWidget::enterEvent(QEvent *event)
621 {
631 {
622 Q_UNUSED(event);
632 Q_UNUSED(event);
623 impl->m_RenderingDelegate->showGraphOverlay(true);
633 impl->m_RenderingDelegate->showGraphOverlay(true);
624 }
634 }
625
635
626 void VisualizationGraphWidget::leaveEvent(QEvent *event)
636 void VisualizationGraphWidget::leaveEvent(QEvent *event)
627 {
637 {
628 Q_UNUSED(event);
638 Q_UNUSED(event);
629 impl->m_RenderingDelegate->showGraphOverlay(false);
639 impl->m_RenderingDelegate->showGraphOverlay(false);
630
640
631 if (auto parentZone = parentZoneWidget()) {
641 if (auto parentZone = parentZoneWidget()) {
632 parentZone->notifyMouseLeaveGraph(this);
642 parentZone->notifyMouseLeaveGraph(this);
633 }
643 }
634 else {
644 else {
635 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
645 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
636 }
646 }
637
647
638 if (impl->m_HoveredZone) {
648 if (impl->m_HoveredZone) {
639 impl->m_HoveredZone->setHovered(false);
649 impl->m_HoveredZone->setHovered(false);
640 impl->m_HoveredZone = nullptr;
650 impl->m_HoveredZone = nullptr;
641 }
651 }
642 }
652 }
643
653
644 QCustomPlot &VisualizationGraphWidget::plot() const noexcept
654 QCustomPlot &VisualizationGraphWidget::plot() const noexcept
645 {
655 {
646 return *ui->widget;
656 return *ui->widget;
647 }
657 }
648
658
649 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
659 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
650 {
660 {
651 QMenu graphMenu{};
661 QMenu graphMenu{};
652
662
653 // Iterates on variables (unique keys)
663 // Iterates on variables (unique keys)
654 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
664 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
655 end = impl->m_VariableToPlotMultiMap.cend();
665 end = impl->m_VariableToPlotMultiMap.cend();
656 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
666 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
657 // 'Remove variable' action
667 // 'Remove variable' action
658 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
668 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
659 [ this, var = it->first ]() { removeVariable(var); });
669 [ this, var = it->first ]() { removeVariable(var); });
660 }
670 }
661
671
662 if (!impl->m_ZoomStack.isEmpty()) {
672 if (!impl->m_ZoomStack.isEmpty()) {
663 if (!graphMenu.isEmpty()) {
673 if (!graphMenu.isEmpty()) {
664 graphMenu.addSeparator();
674 graphMenu.addSeparator();
665 }
675 }
666
676
667 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
677 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
668 }
678 }
669
679
670 // Selection Zone Actions
680 // Selection Zone Actions
671 auto selectionZoneItem = impl->selectionZoneAt(pos, plot());
681 auto selectionZoneItem = impl->selectionZoneAt(pos, plot());
672 if (selectionZoneItem) {
682 if (selectionZoneItem) {
673 auto selectedItems = parentVisualizationWidget()->selectionZoneManager().selectedItems();
683 auto selectedItems = parentVisualizationWidget()->selectionZoneManager().selectedItems();
674 selectedItems.removeAll(selectionZoneItem);
684 selectedItems.removeAll(selectionZoneItem);
675 selectedItems.prepend(selectionZoneItem); // Put the current selection zone first
685 selectedItems.prepend(selectionZoneItem); // Put the current selection zone first
676
686
677 auto zoneActions = sqpApp->actionsGuiController().selectionZoneActions();
687 auto zoneActions = sqpApp->actionsGuiController().selectionZoneActions();
678 if (!zoneActions.isEmpty() && !graphMenu.isEmpty()) {
688 if (!zoneActions.isEmpty() && !graphMenu.isEmpty()) {
679 graphMenu.addSeparator();
689 graphMenu.addSeparator();
680 }
690 }
681
691
682 QHash<QString, QMenu *> subMenus;
692 QHash<QString, QMenu *> subMenus;
683 QHash<QString, bool> subMenusEnabled;
693 QHash<QString, bool> subMenusEnabled;
684
694
685 for (auto zoneAction : zoneActions) {
695 for (auto zoneAction : zoneActions) {
686
696
687 auto isEnabled = zoneAction->isEnabled(selectedItems);
697 auto isEnabled = zoneAction->isEnabled(selectedItems);
688
698
689 auto menu = &graphMenu;
699 auto menu = &graphMenu;
690 for (auto subMenuName : zoneAction->subMenuList()) {
700 for (auto subMenuName : zoneAction->subMenuList()) {
691 if (!subMenus.contains(subMenuName)) {
701 if (!subMenus.contains(subMenuName)) {
692 menu = menu->addMenu(subMenuName);
702 menu = menu->addMenu(subMenuName);
693 subMenus[subMenuName] = menu;
703 subMenus[subMenuName] = menu;
694 subMenusEnabled[subMenuName] = isEnabled;
704 subMenusEnabled[subMenuName] = isEnabled;
695 }
705 }
696 else {
706 else {
697 menu = subMenus.value(subMenuName);
707 menu = subMenus.value(subMenuName);
698 if (isEnabled) {
708 if (isEnabled) {
699 // The sub menu is enabled if at least one of its actions is enabled
709 // The sub menu is enabled if at least one of its actions is enabled
700 subMenusEnabled[subMenuName] = true;
710 subMenusEnabled[subMenuName] = true;
701 }
711 }
702 }
712 }
703 }
713 }
704
714
705 auto action = menu->addAction(zoneAction->name());
715 auto action = menu->addAction(zoneAction->name());
706 action->setEnabled(isEnabled);
716 action->setEnabled(isEnabled);
707 action->setShortcut(zoneAction->displayedShortcut());
717 action->setShortcut(zoneAction->displayedShortcut());
708 QObject::connect(action, &QAction::triggered,
718 QObject::connect(action, &QAction::triggered,
709 [zoneAction, selectedItems]() { zoneAction->execute(selectedItems); });
719 [zoneAction, selectedItems]() { zoneAction->execute(selectedItems); });
710 }
720 }
711
721
712 for (auto it = subMenus.cbegin(); it != subMenus.cend(); ++it) {
722 for (auto it = subMenus.cbegin(); it != subMenus.cend(); ++it) {
713 it.value()->setEnabled(subMenusEnabled[it.key()]);
723 it.value()->setEnabled(subMenusEnabled[it.key()]);
714 }
724 }
715 }
725 }
716
726
717 if (!graphMenu.isEmpty()) {
727 if (!graphMenu.isEmpty()) {
718 graphMenu.exec(QCursor::pos());
728 graphMenu.exec(QCursor::pos());
719 }
729 }
720 }
730 }
721
731
722 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
732 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
723 {
733 {
724 qCDebug(LOG_VisualizationGraphWidget()) << tr("TORM: VisualizationGraphWidget::onRangeChanged")
734 qCDebug(LOG_VisualizationGraphWidget()) << tr("TORM: VisualizationGraphWidget::onRangeChanged")
725 << QThread::currentThread()->objectName() << "DoAcqui"
735 << QThread::currentThread()->objectName() << "DoAcqui"
726 << impl->m_Flags.testFlag(GraphFlag::EnableAcquisition);
736 << impl->m_Flags.testFlag(GraphFlag::EnableAcquisition);
727
737
728 auto graphRange = SqpRange{t1.lower, t1.upper};
738 auto graphRange = SqpRange{t1.lower, t1.upper};
729 auto oldGraphRange = SqpRange{t2.lower, t2.upper};
739 auto oldGraphRange = SqpRange{t2.lower, t2.upper};
730
740
731 if (impl->m_Flags.testFlag(GraphFlag::EnableAcquisition)) {
741 if (impl->m_Flags.testFlag(GraphFlag::EnableAcquisition)) {
732 QVector<std::shared_ptr<Variable> > variableUnderGraphVector;
742 QVector<std::shared_ptr<Variable> > variableUnderGraphVector;
733
743
734 for (auto it = impl->m_VariableToPlotMultiMap.begin(),
744 for (auto it = impl->m_VariableToPlotMultiMap.begin(),
735 end = impl->m_VariableToPlotMultiMap.end();
745 end = impl->m_VariableToPlotMultiMap.end();
736 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
746 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
737 variableUnderGraphVector.push_back(it->first);
747 variableUnderGraphVector.push_back(it->first);
738 }
748 }
739 emit requestDataLoading(std::move(variableUnderGraphVector), graphRange,
749 emit requestDataLoading(std::move(variableUnderGraphVector), graphRange,
740 !impl->m_IsCalibration);
750 !impl->m_IsCalibration);
741 }
751 }
742
752
743 if (impl->m_Flags.testFlag(GraphFlag::EnableSynchronization) && !impl->m_IsCalibration) {
753 if (impl->m_Flags.testFlag(GraphFlag::EnableSynchronization) && !impl->m_IsCalibration) {
744 qCDebug(LOG_VisualizationGraphWidget())
754 qCDebug(LOG_VisualizationGraphWidget())
745 << tr("TORM: VisualizationGraphWidget::Synchronize notify !!")
755 << tr("TORM: VisualizationGraphWidget::Synchronize notify !!")
746 << QThread::currentThread()->objectName() << graphRange << oldGraphRange;
756 << QThread::currentThread()->objectName() << graphRange << oldGraphRange;
747 emit synchronize(graphRange, oldGraphRange);
757 emit synchronize(graphRange, oldGraphRange);
748 }
758 }
749
759
750 auto pos = mapFromGlobal(QCursor::pos());
760 auto pos = mapFromGlobal(QCursor::pos());
751 auto axisPos = impl->posToAxisPos(pos, plot());
761 auto axisPos = impl->posToAxisPos(pos, plot());
752 if (auto parentZone = parentZoneWidget()) {
762 if (auto parentZone = parentZoneWidget()) {
753 if (impl->pointIsInAxisRect(axisPos, plot())) {
763 if (impl->pointIsInAxisRect(axisPos, plot())) {
754 parentZone->notifyMouseMoveInGraph(pos, axisPos, this);
764 parentZone->notifyMouseMoveInGraph(pos, axisPos, this);
755 }
765 }
756 else {
766 else {
757 parentZone->notifyMouseLeaveGraph(this);
767 parentZone->notifyMouseLeaveGraph(this);
758 }
768 }
759 }
769 }
760 else {
770 else {
761 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
771 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
762 }
772 }
763
773
764 // Quits calibration
774 // Quits calibration
765 impl->m_IsCalibration = false;
775 impl->m_IsCalibration = false;
766 }
776 }
767
777
768 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept
778 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept
769 {
779 {
770 impl->m_RenderingDelegate->onMouseDoubleClick(event);
780 impl->m_RenderingDelegate->onMouseDoubleClick(event);
771 }
781 }
772
782
773 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
783 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
774 {
784 {
775 // Handles plot rendering when mouse is moving
785 // Handles plot rendering when mouse is moving
776 impl->m_RenderingDelegate->onMouseMove(event);
786 impl->m_RenderingDelegate->onMouseMove(event);
777
787
778 auto axisPos = impl->posToAxisPos(event->pos(), plot());
788 auto axisPos = impl->posToAxisPos(event->pos(), plot());
779
789
780 // Zoom box and zone drawing
790 // Zoom box and zone drawing
781 if (impl->m_DrawingZoomRect) {
791 if (impl->m_DrawingZoomRect) {
782 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
792 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
783 }
793 }
784 else if (impl->m_DrawingZone) {
794 else if (impl->m_DrawingZone) {
785 impl->m_DrawingZone->setEnd(axisPos.x());
795 impl->m_DrawingZone->setEnd(axisPos.x());
786 }
796 }
787
797
788 // Cursor
798 // Cursor
789 if (auto parentZone = parentZoneWidget()) {
799 if (auto parentZone = parentZoneWidget()) {
790 if (impl->pointIsInAxisRect(axisPos, plot())) {
800 if (impl->pointIsInAxisRect(axisPos, plot())) {
791 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
801 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
792 }
802 }
793 else {
803 else {
794 parentZone->notifyMouseLeaveGraph(this);
804 parentZone->notifyMouseLeaveGraph(this);
795 }
805 }
796 }
806 }
797 else {
807 else {
798 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
808 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
799 }
809 }
800
810
801 // Search for the selection zone under the mouse
811 // Search for the selection zone under the mouse
802 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
812 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
803 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
813 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
804 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) {
814 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) {
805
815
806 // Sets the appropriate cursor shape
816 // Sets the appropriate cursor shape
807 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
817 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
808 setCursor(cursorShape);
818 setCursor(cursorShape);
809
819
810 // Manages the hovered zone
820 // Manages the hovered zone
811 if (selectionZoneItemUnderCursor != impl->m_HoveredZone) {
821 if (selectionZoneItemUnderCursor != impl->m_HoveredZone) {
812 if (impl->m_HoveredZone) {
822 if (impl->m_HoveredZone) {
813 impl->m_HoveredZone->setHovered(false);
823 impl->m_HoveredZone->setHovered(false);
814 }
824 }
815 selectionZoneItemUnderCursor->setHovered(true);
825 selectionZoneItemUnderCursor->setHovered(true);
816 impl->m_HoveredZone = selectionZoneItemUnderCursor;
826 impl->m_HoveredZone = selectionZoneItemUnderCursor;
817 plot().replot(QCustomPlot::rpQueuedReplot);
827 plot().replot(QCustomPlot::rpQueuedReplot);
818 }
828 }
819 }
829 }
820 else {
830 else {
821 // There is no zone under the mouse or the interaction mode is not "selection zones"
831 // There is no zone under the mouse or the interaction mode is not "selection zones"
822 if (impl->m_HoveredZone) {
832 if (impl->m_HoveredZone) {
823 impl->m_HoveredZone->setHovered(false);
833 impl->m_HoveredZone->setHovered(false);
824 impl->m_HoveredZone = nullptr;
834 impl->m_HoveredZone = nullptr;
825 }
835 }
826
836
827 setCursor(Qt::ArrowCursor);
837 setCursor(Qt::ArrowCursor);
828 }
838 }
829
839
830 impl->m_HasMovedMouse = true;
840 impl->m_HasMovedMouse = true;
831 VisualizationDragWidget::mouseMoveEvent(event);
841 VisualizationDragWidget::mouseMoveEvent(event);
832 }
842 }
833
843
834 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
844 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
835 {
845 {
836 auto value = event->angleDelta().x() + event->angleDelta().y();
846 auto value = event->angleDelta().x() + event->angleDelta().y();
837 if (value != 0) {
847 if (value != 0) {
838
848
839 auto direction = value > 0 ? 1.0 : -1.0;
849 auto direction = value > 0 ? 1.0 : -1.0;
840 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
850 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
841 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
851 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
842 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
852 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
843
853
844 auto zoomOrientations = QFlags<Qt::Orientation>{};
854 auto zoomOrientations = QFlags<Qt::Orientation>{};
845 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
855 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
846 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
856 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
847
857
848 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
858 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
849
859
850 if (!isZoomX && !isZoomY) {
860 if (!isZoomX && !isZoomY) {
851 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
861 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
852 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
862 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
853
863
854 axis->setRange(axis->range() + diff);
864 axis->setRange(axis->range() + diff);
855
865
856 if (plot().noAntialiasingOnDrag()) {
866 if (plot().noAntialiasingOnDrag()) {
857 plot().setNotAntialiasedElements(QCP::aeAll);
867 plot().setNotAntialiasedElements(QCP::aeAll);
858 }
868 }
859
869
860 plot().replot(QCustomPlot::rpQueuedReplot);
870 plot().replot(QCustomPlot::rpQueuedReplot);
861 }
871 }
862 }
872 }
863 }
873 }
864
874
865 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
875 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
866 {
876 {
867 auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
877 auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
868 auto isSelectionZoneMode
878 auto isSelectionZoneMode
869 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
879 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
870 auto isLeftClick = event->buttons().testFlag(Qt::LeftButton);
880 auto isLeftClick = event->buttons().testFlag(Qt::LeftButton);
871
881
872 if (!isDragDropClick && isLeftClick) {
882 if (!isDragDropClick && isLeftClick) {
873 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) {
883 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) {
874 // Starts a zoom box
884 // Starts a zoom box
875 impl->startDrawingRect(event->pos(), plot());
885 impl->startDrawingRect(event->pos(), plot());
876 }
886 }
877 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr) {
887 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr) {
878 // Starts a new selection zone
888 // Starts a new selection zone
879 auto zoneAtPos = impl->selectionZoneAt(event->pos(), plot());
889 auto zoneAtPos = impl->selectionZoneAt(event->pos(), plot());
880 if (!zoneAtPos) {
890 if (!zoneAtPos) {
881 impl->startDrawingZone(event->pos(), this);
891 impl->startDrawingZone(event->pos(), this);
882 }
892 }
883 }
893 }
884 }
894 }
885
895
886 // Allows mouse panning only in default mode
896 // Allows mouse panning only in default mode
887 plot().setInteraction(QCP::iRangeDrag, sqpApp->plotsInteractionMode()
897 plot().setInteraction(QCP::iRangeDrag, sqpApp->plotsInteractionMode()
888 == SqpApplication::PlotsInteractionMode::None
898 == SqpApplication::PlotsInteractionMode::None
889 && !isDragDropClick);
899 && !isDragDropClick);
890
900
891 // Allows zone edition only in selection zone mode without drag&drop
901 // Allows zone edition only in selection zone mode without drag&drop
892 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
902 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
893
903
894 // Selection / Deselection
904 // Selection / Deselection
895 if (isSelectionZoneMode) {
905 if (isSelectionZoneMode) {
896 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
906 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
897 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
907 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
898
908
899
909
900 if (selectionZoneItemUnderCursor && !selectionZoneItemUnderCursor->selected()
910 if (selectionZoneItemUnderCursor && !selectionZoneItemUnderCursor->selected()
901 && !isMultiSelectionClick) {
911 && !isMultiSelectionClick) {
902 parentVisualizationWidget()->selectionZoneManager().select(
912 parentVisualizationWidget()->selectionZoneManager().select(
903 {selectionZoneItemUnderCursor});
913 {selectionZoneItemUnderCursor});
904 }
914 }
905 else if (!selectionZoneItemUnderCursor && !isMultiSelectionClick && isLeftClick) {
915 else if (!selectionZoneItemUnderCursor && !isMultiSelectionClick && isLeftClick) {
906 parentVisualizationWidget()->selectionZoneManager().clearSelection();
916 parentVisualizationWidget()->selectionZoneManager().clearSelection();
907 }
917 }
908 else {
918 else {
909 // No selection change
919 // No selection change
910 }
920 }
911
921
912 if (selectionZoneItemUnderCursor && isLeftClick) {
922 if (selectionZoneItemUnderCursor && isLeftClick) {
913 selectionZoneItemUnderCursor->setAssociatedEditedZones(
923 selectionZoneItemUnderCursor->setAssociatedEditedZones(
914 parentVisualizationWidget()->selectionZoneManager().selectedItems());
924 parentVisualizationWidget()->selectionZoneManager().selectedItems());
915 }
925 }
916 }
926 }
917
927
918
928
919 impl->m_HasMovedMouse = false;
929 impl->m_HasMovedMouse = false;
920 VisualizationDragWidget::mousePressEvent(event);
930 VisualizationDragWidget::mousePressEvent(event);
921 }
931 }
922
932
923 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
933 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
924 {
934 {
925 if (impl->m_DrawingZoomRect) {
935 if (impl->m_DrawingZoomRect) {
926
936
927 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
937 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
928 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
938 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
929
939
930 auto newAxisXRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().x(),
940 auto newAxisXRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().x(),
931 impl->m_DrawingZoomRect->bottomRight->coords().x()};
941 impl->m_DrawingZoomRect->bottomRight->coords().x()};
932
942
933 auto newAxisYRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().y(),
943 auto newAxisYRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().y(),
934 impl->m_DrawingZoomRect->bottomRight->coords().y()};
944 impl->m_DrawingZoomRect->bottomRight->coords().y()};
935
945
936 impl->removeDrawingRect(plot());
946 impl->removeDrawingRect(plot());
937
947
938 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
948 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
939 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
949 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
940 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
950 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
941 axisX->setRange(newAxisXRange);
951 axisX->setRange(newAxisXRange);
942 axisY->setRange(newAxisYRange);
952 axisY->setRange(newAxisYRange);
943
953
944 plot().replot(QCustomPlot::rpQueuedReplot);
954 plot().replot(QCustomPlot::rpQueuedReplot);
945 }
955 }
946 }
956 }
947
957
948 impl->endDrawingZone(this);
958 impl->endDrawingZone(this);
949
959
950 // Selection / Deselection
960 // Selection / Deselection
951 auto isSelectionZoneMode
961 auto isSelectionZoneMode
952 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
962 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
953 if (isSelectionZoneMode) {
963 if (isSelectionZoneMode) {
954 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
964 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
955 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
965 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
956 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton
966 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton
957 && !impl->m_HasMovedMouse) {
967 && !impl->m_HasMovedMouse) {
958
968
959 auto zonesUnderCursor = impl->selectionZonesAt(event->pos(), plot());
969 auto zonesUnderCursor = impl->selectionZonesAt(event->pos(), plot());
960 if (zonesUnderCursor.count() > 1) {
970 if (zonesUnderCursor.count() > 1) {
961 // There are multiple zones under the mouse.
971 // There are multiple zones under the mouse.
962 // Performs the selection with a selection dialog.
972 // Performs the selection with a selection dialog.
963 VisualizationMultiZoneSelectionDialog dialog{this};
973 VisualizationMultiZoneSelectionDialog dialog{this};
964 dialog.setZones(zonesUnderCursor);
974 dialog.setZones(zonesUnderCursor);
965 dialog.move(mapToGlobal(event->pos() - QPoint(dialog.width() / 2, 20)));
975 dialog.move(mapToGlobal(event->pos() - QPoint(dialog.width() / 2, 20)));
966 dialog.activateWindow();
976 dialog.activateWindow();
967 dialog.raise();
977 dialog.raise();
968 if (dialog.exec() == QDialog::Accepted) {
978 if (dialog.exec() == QDialog::Accepted) {
969 auto selection = dialog.selectedZones();
979 auto selection = dialog.selectedZones();
970
980
971 if (!isMultiSelectionClick) {
981 if (!isMultiSelectionClick) {
972 parentVisualizationWidget()->selectionZoneManager().clearSelection();
982 parentVisualizationWidget()->selectionZoneManager().clearSelection();
973 }
983 }
974
984
975 for (auto it = selection.cbegin(); it != selection.cend(); ++it) {
985 for (auto it = selection.cbegin(); it != selection.cend(); ++it) {
976 auto zone = it.key();
986 auto zone = it.key();
977 auto isSelected = it.value();
987 auto isSelected = it.value();
978 parentVisualizationWidget()->selectionZoneManager().setSelected(zone,
988 parentVisualizationWidget()->selectionZoneManager().setSelected(zone,
979 isSelected);
989 isSelected);
980
990
981 if (isSelected) {
991 if (isSelected) {
982 // Puts the zone on top of the stack so it can be moved or resized
992 // Puts the zone on top of the stack so it can be moved or resized
983 impl->moveSelectionZoneOnTop(zone, plot());
993 impl->moveSelectionZoneOnTop(zone, plot());
984 }
994 }
985 }
995 }
986 }
996 }
987 }
997 }
988 else {
998 else {
989 if (!isMultiSelectionClick) {
999 if (!isMultiSelectionClick) {
990 parentVisualizationWidget()->selectionZoneManager().select(
1000 parentVisualizationWidget()->selectionZoneManager().select(
991 {selectionZoneItemUnderCursor});
1001 {selectionZoneItemUnderCursor});
992 impl->moveSelectionZoneOnTop(selectionZoneItemUnderCursor, plot());
1002 impl->moveSelectionZoneOnTop(selectionZoneItemUnderCursor, plot());
993 }
1003 }
994 else {
1004 else {
995 parentVisualizationWidget()->selectionZoneManager().setSelected(
1005 parentVisualizationWidget()->selectionZoneManager().setSelected(
996 selectionZoneItemUnderCursor, !selectionZoneItemUnderCursor->selected()
1006 selectionZoneItemUnderCursor, !selectionZoneItemUnderCursor->selected()
997 || event->button() == Qt::RightButton);
1007 || event->button() == Qt::RightButton);
998 }
1008 }
999 }
1009 }
1000 }
1010 }
1001 else {
1011 else {
1002 // No selection change
1012 // No selection change
1003 }
1013 }
1004 }
1014 }
1005 }
1015 }
1006
1016
1007 void VisualizationGraphWidget::onDataCacheVariableUpdated()
1017 void VisualizationGraphWidget::onDataCacheVariableUpdated()
1008 {
1018 {
1009 auto graphRange = ui->widget->xAxis->range();
1019 auto graphRange = ui->widget->xAxis->range();
1010 auto dateTime = SqpRange{graphRange.lower, graphRange.upper};
1020 auto dateTime = SqpRange{graphRange.lower, graphRange.upper};
1011
1021
1012 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
1022 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
1013 auto variable = variableEntry.first;
1023 auto variable = variableEntry.first;
1014 qCDebug(LOG_VisualizationGraphWidget())
1024 qCDebug(LOG_VisualizationGraphWidget())
1015 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
1025 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
1016 qCDebug(LOG_VisualizationGraphWidget())
1026 qCDebug(LOG_VisualizationGraphWidget())
1017 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
1027 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
1018 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
1028 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
1019 impl->updateData(variableEntry.second, variable, variable->range());
1029 impl->updateData(variableEntry.second, variable, variable->range());
1020 }
1030 }
1021 }
1031 }
1022 }
1032 }
1023
1033
1024 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
1034 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
1025 const SqpRange &range)
1035 const SqpRange &range)
1026 {
1036 {
1027 auto it = impl->m_VariableToPlotMultiMap.find(variable);
1037 auto it = impl->m_VariableToPlotMultiMap.find(variable);
1028 if (it != impl->m_VariableToPlotMultiMap.end()) {
1038 if (it != impl->m_VariableToPlotMultiMap.end()) {
1029 impl->updateData(it->second, variable, range);
1039 impl->updateData(it->second, variable, range);
1030 }
1040 }
1031 }
1041 }
General Comments 0
You need to be logged in to leave comments. Login now