##// END OF EJS Templates
Selection of stacked zone via a dialog box
trabillard -
r1120:a02153d618e2
parent child
Show More
@@ -0,0 +1,30
1 #ifndef SCIQLOP_VISUALIZATIONMULTIZONESELECTIONDIALOG_H
2 #define SCIQLOP_VISUALIZATIONMULTIZONESELECTIONDIALOG_H
3
4 #include <Common/spimpl.h>
5 #include <QDialog>
6
7 namespace Ui {
8 class VisualizationMultiZoneSelectionDialog;
9 }
10
11 class VisualizationSelectionZoneItem;
12
13 class VisualizationMultiZoneSelectionDialog : public QDialog {
14 Q_OBJECT
15
16 public:
17 explicit VisualizationMultiZoneSelectionDialog(QWidget *parent = 0);
18 ~VisualizationMultiZoneSelectionDialog();
19
20 void setZones(const QVector<VisualizationSelectionZoneItem *> &zones);
21 QMap<VisualizationSelectionZoneItem *, bool> selectedZones() const;
22
23 private:
24 Ui::VisualizationMultiZoneSelectionDialog *ui;
25
26 class VisualizationMultiZoneSelectionDialogPrivate;
27 spimpl::unique_impl_ptr<VisualizationMultiZoneSelectionDialogPrivate> impl;
28 };
29
30 #endif // SCIQLOP_VISUALIZATIONMULTIZONESELECTIONDIALOG_H
@@ -0,0 +1,69
1 #include "Visualization/VisualizationMultiZoneSelectionDialog.h"
2 #include "ui_VisualizationMultiZoneSelectionDialog.h"
3
4 #include "Common/DateUtils.h"
5 #include "Visualization/VisualizationSelectionZoneItem.h"
6
7 const auto DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd hh:mm:ss");
8
9 struct VisualizationMultiZoneSelectionDialog::VisualizationMultiZoneSelectionDialogPrivate {
10 QVector<VisualizationSelectionZoneItem *> m_Zones;
11 };
12
13 VisualizationMultiZoneSelectionDialog::VisualizationMultiZoneSelectionDialog(QWidget *parent)
14 : QDialog(parent, Qt::Tool),
15 ui(new Ui::VisualizationMultiZoneSelectionDialog),
16 impl{spimpl::make_unique_impl<VisualizationMultiZoneSelectionDialogPrivate>()}
17 {
18 ui->setupUi(this);
19
20 connect(ui->buttonBox, &QDialogButtonBox::accepted, this,
21 &VisualizationMultiZoneSelectionDialog::accept);
22 connect(ui->buttonBox, &QDialogButtonBox::rejected, this,
23 &VisualizationMultiZoneSelectionDialog::reject);
24 }
25
26 VisualizationMultiZoneSelectionDialog::~VisualizationMultiZoneSelectionDialog()
27 {
28 delete ui;
29 }
30
31 void VisualizationMultiZoneSelectionDialog::setZones(
32 const QVector<VisualizationSelectionZoneItem *> &zones)
33 {
34 impl->m_Zones = zones;
35
36 // Sorts the zones to display them in temporal order
37 std::sort(impl->m_Zones.begin(), impl->m_Zones.end(), [](auto zone1, auto zone2) {
38 return zone1->range().m_TStart < zone2->range().m_TStart;
39 });
40
41 // Adds the zones in the listwidget
42 for (auto zone : impl->m_Zones) {
43 auto name = zone->name();
44 if (!name.isEmpty()) {
45 name += tr(": ");
46 }
47
48 auto range = zone->range();
49 name += DateUtils::dateTime(range.m_TStart).toString(DATETIME_FORMAT);
50 name += " - ";
51 name += DateUtils::dateTime(range.m_TEnd).toString(DATETIME_FORMAT);
52
53 auto item = new QListWidgetItem(name, ui->listWidget);
54 item->setSelected(zone->selected());
55 }
56 }
57
58 QMap<VisualizationSelectionZoneItem *, bool>
59 VisualizationMultiZoneSelectionDialog::selectedZones() const
60 {
61 QMap<VisualizationSelectionZoneItem *, bool> selectedZones;
62
63 for (auto i = 0; i < ui->listWidget->count(); ++i) {
64 auto item = ui->listWidget->item(i);
65 selectedZones[impl->m_Zones[i]] = item->isSelected();
66 }
67
68 return selectedZones;
69 }
@@ -0,0 +1,47
1 <?xml version="1.0" encoding="UTF-8"?>
2 <ui version="4.0">
3 <class>VisualizationMultiZoneSelectionDialog</class>
4 <widget class="QDialog" name="VisualizationMultiZoneSelectionDialog">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>270</width>
10 <height>175</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Select...</string>
15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout">
17 <property name="leftMargin">
18 <number>0</number>
19 </property>
20 <property name="topMargin">
21 <number>0</number>
22 </property>
23 <property name="rightMargin">
24 <number>0</number>
25 </property>
26 <property name="bottomMargin">
27 <number>0</number>
28 </property>
29 <item>
30 <widget class="QListWidget" name="listWidget">
31 <property name="selectionMode">
32 <enum>QAbstractItemView::ExtendedSelection</enum>
33 </property>
34 </widget>
35 </item>
36 <item>
37 <widget class="QDialogButtonBox" name="buttonBox">
38 <property name="standardButtons">
39 <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
40 </property>
41 </widget>
42 </item>
43 </layout>
44 </widget>
45 <resources/>
46 <connections/>
47 </ui>
@@ -1,64 +1,68
1 1 #ifndef SCIQLOP_VISUALIZATIONSELECTIONZONEITEM_H
2 2 #define SCIQLOP_VISUALIZATIONSELECTIONZONEITEM_H
3 3
4 4 #include <Common/spimpl.h>
5 5 #include <Data/SqpRange.h>
6 6 #include <Visualization/qcustomplot.h>
7 7
8 8 class VisualizationGraphWidget;
9 9
10 10 class VisualizationSelectionZoneItem : public QCPItemRect {
11 11
12 12 public:
13 13 VisualizationSelectionZoneItem(QCustomPlot *plot);
14 14 virtual ~VisualizationSelectionZoneItem();
15 15
16 16 VisualizationGraphWidget *parentGraphWidget() const noexcept;
17 17
18 18 void setName(const QString &name);
19 19 QString name() const;
20 20
21 21 SqpRange range() const;
22 22 void setRange(double tstart, double tend);
23 23 void setStart(double tstart);
24 24 void setEnd(double tend);
25 25
26 26 void setColor(const QColor &color);
27 27
28 28 void setEditionEnabled(bool value);
29 29 bool isEditionEnabled() const;
30 30
31 /// Moves the item at the top of its QCPLayer. It will then receive the mouse events if multiple
32 /// items are stacked on top of each others.
33 void moveToTop();
34
31 35 Qt::CursorShape curshorShapeForPosition(const QPoint &position) const;
32 36 void setHovered(bool value);
33 37
34 38 /// Sets the zones which should be moved or reisized together with this zone
35 39 void setAssociatedEditedZones(const QVector<VisualizationSelectionZoneItem *> &associatedZones);
36 40
37 41 /// Align the specified zones with this one, vertically with the left border
38 42 bool alignZonesVerticallyOnLeft(const QVector<VisualizationSelectionZoneItem *> &zonesToAlign,
39 43 bool allowResize);
40 44 /// Align the specified zones with this one, vertically with the right border
41 45 bool alignZonesVerticallyOnRight(const QVector<VisualizationSelectionZoneItem *> &zonesToAlign,
42 46 bool allowResize);
43 47 /// Align the specified zones with this one, temporally with the left border
44 48 bool alignZonesTemporallyOnLeft(const QVector<VisualizationSelectionZoneItem *> &zonesToAlign,
45 49 bool allowResize);
46 50 /// Align the specified zones with this one, temporally with the right border
47 51 bool alignZonesTemporallyOnRight(const QVector<VisualizationSelectionZoneItem *> &zonesToAlign,
48 52 bool allowResize);
49 53
50 54 protected:
51 55 void mousePressEvent(QMouseEvent *event, const QVariant &details) override;
52 56 void mouseMoveEvent(QMouseEvent *event, const QPointF &startPos) override;
53 57 void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos) override;
54 58
55 59 void resizeLeft(double pixelDiff);
56 60 void resizeRight(double pixelDiff);
57 61 void move(double pixelDiff);
58 62
59 63 private:
60 64 class VisualizationSelectionZoneItemPrivate;
61 65 spimpl::unique_impl_ptr<VisualizationSelectionZoneItemPrivate> impl;
62 66 };
63 67
64 68 #endif // SCIQLOP_VISUALIZATIONSELECTIONZONEITEM_H
@@ -1,107 +1,110
1 1
2 2 gui_moc_headers = [
3 3 'include/DataSource/DataSourceWidget.h',
4 4 'include/Settings/SqpSettingsDialog.h',
5 5 'include/Settings/SqpSettingsGeneralWidget.h',
6 6 'include/SidePane/SqpSidePane.h',
7 7 'include/SqpApplication.h',
8 8 'include/DragAndDrop/DragDropScroller.h',
9 9 'include/DragAndDrop/DragDropTabSwitcher.h',
10 10 'include/TimeWidget/TimeWidget.h',
11 11 'include/Variable/VariableInspectorWidget.h',
12 12 'include/Variable/RenameVariableDialog.h',
13 13 'include/Visualization/qcustomplot.h',
14 14 'include/Visualization/VisualizationGraphWidget.h',
15 15 'include/Visualization/VisualizationTabWidget.h',
16 16 'include/Visualization/VisualizationWidget.h',
17 17 'include/Visualization/VisualizationZoneWidget.h',
18 18 'include/Visualization/VisualizationDragDropContainer.h',
19 19 'include/Visualization/VisualizationDragWidget.h',
20 20 'include/Visualization/ColorScaleEditor.h',
21 'include/Visualization/SelectionZoneAction.h'
21 'include/Visualization/SelectionZoneAction.h',
22 'include/Visualization/VisualizationMultiZoneSelectionDialog.h'
22 23 ]
23 24
24 25 gui_ui_files = [
25 26 'ui/DataSource/DataSourceWidget.ui',
26 27 'ui/Settings/SqpSettingsDialog.ui',
27 28 'ui/Settings/SqpSettingsGeneralWidget.ui',
28 29 'ui/SidePane/SqpSidePane.ui',
29 30 'ui/TimeWidget/TimeWidget.ui',
30 31 'ui/Variable/VariableInspectorWidget.ui',
31 32 'ui/Variable/RenameVariableDialog.ui',
32 33 'ui/Variable/VariableMenuHeaderWidget.ui',
33 34 'ui/Visualization/VisualizationGraphWidget.ui',
34 35 'ui/Visualization/VisualizationTabWidget.ui',
35 36 'ui/Visualization/VisualizationWidget.ui',
36 37 'ui/Visualization/VisualizationZoneWidget.ui',
37 'ui/Visualization/ColorScaleEditor.ui'
38 'ui/Visualization/ColorScaleEditor.ui',
39 'ui/Visualization/VisualizationMultiZoneSelectionDialog.ui'
38 40 ]
39 41
40 42 gui_qresources = ['resources/sqpguiresources.qrc']
41 43
42 44 gui_moc_files = qt5.preprocess(moc_headers : gui_moc_headers,
43 45 ui_files : gui_ui_files,
44 46 qresources : gui_qresources)
45 47
46 48 gui_sources = [
47 49 'src/SqpApplication.cpp',
48 50 'src/DragAndDrop/DragDropHelper.cpp',
49 51 'src/DragAndDrop/DragDropScroller.cpp',
50 52 'src/DragAndDrop/DragDropTabSwitcher.cpp',
51 53 'src/Common/ColorUtils.cpp',
52 54 'src/Common/VisualizationDef.cpp',
53 55 'src/DataSource/DataSourceTreeWidgetItem.cpp',
54 56 'src/DataSource/DataSourceTreeWidgetHelper.cpp',
55 57 'src/DataSource/DataSourceWidget.cpp',
56 58 'src/DataSource/DataSourceTreeWidget.cpp',
57 59 'src/Settings/SqpSettingsDialog.cpp',
58 60 'src/Settings/SqpSettingsGeneralWidget.cpp',
59 61 'src/SidePane/SqpSidePane.cpp',
60 62 'src/TimeWidget/TimeWidget.cpp',
61 63 'src/Variable/VariableInspectorWidget.cpp',
62 64 'src/Variable/VariableInspectorTableView.cpp',
63 65 'src/Variable/VariableMenuHeaderWidget.cpp',
64 66 'src/Variable/RenameVariableDialog.cpp',
65 67 'src/Visualization/VisualizationGraphHelper.cpp',
66 68 'src/Visualization/VisualizationGraphRenderingDelegate.cpp',
67 69 'src/Visualization/VisualizationGraphWidget.cpp',
68 70 'src/Visualization/VisualizationTabWidget.cpp',
69 71 'src/Visualization/VisualizationWidget.cpp',
70 72 'src/Visualization/VisualizationZoneWidget.cpp',
71 73 'src/Visualization/qcustomplot.cpp',
72 74 'src/Visualization/QCustomPlotSynchronizer.cpp',
73 75 'src/Visualization/operations/FindVariableOperation.cpp',
74 76 'src/Visualization/operations/GenerateVariableMenuOperation.cpp',
75 77 'src/Visualization/operations/MenuBuilder.cpp',
76 78 'src/Visualization/operations/RemoveVariableOperation.cpp',
77 79 'src/Visualization/operations/RescaleAxeOperation.cpp',
78 80 'src/Visualization/VisualizationDragDropContainer.cpp',
79 81 'src/Visualization/VisualizationDragWidget.cpp',
80 82 'src/Visualization/AxisRenderingUtils.cpp',
81 83 'src/Visualization/PlottablesRenderingUtils.cpp',
82 84 'src/Visualization/MacScrollBarStyle.cpp',
83 85 'src/Visualization/VisualizationCursorItem.cpp',
84 86 'src/Visualization/ColorScaleEditor.cpp',
85 87 'src/Visualization/SqpColorScale.cpp',
86 88 'src/Visualization/QCPColorMapIterator.cpp',
87 89 'src/Visualization/VisualizationSelectionZoneItem.cpp',
88 90 'src/Visualization/VisualizationSelectionZoneManager.cpp',
89 91 'src/Visualization/SelectionZoneAction.cpp',
90 92 'src/Visualization/ActionsGuiController.cpp',
91 'src/Visualization/VisualizationActionManager.cpp'
93 'src/Visualization/VisualizationActionManager.cpp',
94 'src/Visualization/VisualizationMultiZoneSelectionDialog.cpp'
92 95 ]
93 96
94 97 gui_inc = include_directories(['include'])
95 98
96 99 sciqlop_gui_lib = library('sciqlopgui',
97 100 gui_sources,
98 101 gui_moc_files,
99 102 include_directories : [gui_inc],
100 103 dependencies : [ qt5printsupport, qt5gui, qt5widgets, qt5svg, sciqlop_core],
101 104 install : true
102 105 )
103 106
104 107 sciqlop_gui = declare_dependency(link_with : sciqlop_gui_lib,
105 108 include_directories : gui_inc,
106 109 dependencies : [qt5printsupport, qt5gui, qt5widgets, qt5svg, sciqlop_core])
107 110
@@ -1,946 +1,1004
1 1 #include "Visualization/VisualizationGraphWidget.h"
2 2 #include "Visualization/IVisualizationWidgetVisitor.h"
3 3 #include "Visualization/VisualizationCursorItem.h"
4 4 #include "Visualization/VisualizationDefs.h"
5 5 #include "Visualization/VisualizationGraphHelper.h"
6 6 #include "Visualization/VisualizationGraphRenderingDelegate.h"
7 #include "Visualization/VisualizationMultiZoneSelectionDialog.h"
7 8 #include "Visualization/VisualizationSelectionZoneItem.h"
8 9 #include "Visualization/VisualizationSelectionZoneManager.h"
9 10 #include "Visualization/VisualizationWidget.h"
10 11 #include "Visualization/VisualizationZoneWidget.h"
11 12 #include "ui_VisualizationGraphWidget.h"
12 13
13 14 #include <Actions/ActionsGuiController.h>
14 15 #include <Common/MimeTypesDef.h>
15 16 #include <Data/ArrayData.h>
16 17 #include <Data/IDataSeries.h>
17 18 #include <Data/SpectrogramSeries.h>
18 19 #include <DragAndDrop/DragDropGuiController.h>
19 20 #include <Settings/SqpSettingsDefs.h>
20 21 #include <SqpApplication.h>
21 22 #include <Time/TimeController.h>
22 23 #include <Variable/Variable.h>
23 24 #include <Variable/VariableController.h>
24 25
25 26 #include <unordered_map>
26 27
27 28 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
28 29
29 30 namespace {
30 31
31 32 /// Key pressed to enable drag&drop in all modes
32 33 const auto DRAG_DROP_MODIFIER = Qt::AltModifier;
33 34
34 35 /// Key pressed to enable zoom on horizontal axis
35 36 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier;
36 37
37 38 /// Key pressed to enable zoom on vertical axis
38 39 const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier;
39 40
40 41 /// Speed of a step of a wheel event for a pan, in percentage of the axis range
41 42 const auto PAN_SPEED = 5;
42 43
43 44 /// Key pressed to enable a calibration pan
44 45 const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier;
45 46
46 47 /// Key pressed to enable multi selection of selection zones
47 48 const auto MULTI_ZONE_SELECTION_MODIFIER = Qt::ControlModifier;
48 49
49 50 /// Minimum size for the zoom box, in percentage of the axis range
50 51 const auto ZOOM_BOX_MIN_SIZE = 0.8;
51 52
52 53 /// Format of the dates appearing in the label of a cursor
53 54 const auto CURSOR_LABELS_DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd\nhh:mm:ss:zzz");
54 55
55 56 } // namespace
56 57
57 58 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
58 59
59 60 explicit VisualizationGraphWidgetPrivate(const QString &name)
60 61 : m_Name{name},
61 62 m_DoAcquisition{true},
62 63 m_IsCalibration{false},
63 64 m_RenderingDelegate{nullptr}
64 65 {
65 66 }
66 67
67 68 void updateData(PlottablesMap &plottables, std::shared_ptr<IDataSeries> dataSeries,
68 69 const SqpRange &range)
69 70 {
70 71 VisualizationGraphHelper::updateData(plottables, dataSeries, range);
71 72
72 73 // Prevents that data has changed to update rendering
73 74 m_RenderingDelegate->onPlotUpdated();
74 75 }
75 76
76 77 QString m_Name;
77 78 // 1 variable -> n qcpplot
78 79 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
79 80 bool m_DoAcquisition;
80 81 bool m_IsCalibration;
81 82 /// Delegate used to attach rendering features to the plot
82 83 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
83 84
84 85 QCPItemRect *m_DrawingZoomRect = nullptr;
85 86 QStack<QPair<QCPRange, QCPRange> > m_ZoomStack;
86 87
87 88 std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr;
88 89 std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr;
89 90
90 91 VisualizationSelectionZoneItem *m_DrawingZone = nullptr;
91 92 VisualizationSelectionZoneItem *m_HoveredZone = nullptr;
92 93 QVector<VisualizationSelectionZoneItem *> m_SelectionZones;
93 94
94 95 bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even
95 96
96 97 void startDrawingRect(const QPoint &pos, QCustomPlot &plot)
97 98 {
98 99 removeDrawingRect(plot);
99 100
100 101 auto axisPos = posToAxisPos(pos, plot);
101 102
102 103 m_DrawingZoomRect = new QCPItemRect{&plot};
103 104 QPen p;
104 105 p.setWidth(2);
105 106 m_DrawingZoomRect->setPen(p);
106 107
107 108 m_DrawingZoomRect->topLeft->setCoords(axisPos);
108 109 m_DrawingZoomRect->bottomRight->setCoords(axisPos);
109 110 }
110 111
111 112 void removeDrawingRect(QCustomPlot &plot)
112 113 {
113 114 if (m_DrawingZoomRect) {
114 115 plot.removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot
115 116 m_DrawingZoomRect = nullptr;
116 117 plot.replot(QCustomPlot::rpQueuedReplot);
117 118 }
118 119 }
119 120
120 121 void startDrawingZone(const QPoint &pos, VisualizationGraphWidget *graph)
121 122 {
122 123 endDrawingZone(graph);
123 124
124 125 auto axisPos = posToAxisPos(pos, graph->plot());
125 126
126 127 m_DrawingZone = new VisualizationSelectionZoneItem{&graph->plot()};
127 128 m_DrawingZone->setRange(axisPos.x(), axisPos.x());
128 129 m_DrawingZone->setEditionEnabled(false);
129 130 }
130 131
131 132 void endDrawingZone(VisualizationGraphWidget *graph)
132 133 {
133 134 if (m_DrawingZone) {
134 135 auto drawingZoneRange = m_DrawingZone->range();
135 136 if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0) {
136 137 m_DrawingZone->setEditionEnabled(true);
137 138 addSelectionZone(m_DrawingZone);
138 139 }
139 140 else {
140 141 graph->plot().removeItem(m_DrawingZone); // the item is deleted by QCustomPlot
141 142 }
142 143
143 144 graph->plot().replot(QCustomPlot::rpQueuedReplot);
144 145 m_DrawingZone = nullptr;
145 146 }
146 147 }
147 148
148 149 void setSelectionZonesEditionEnabled(bool value)
149 150 {
150 151 for (auto s : m_SelectionZones) {
151 152 s->setEditionEnabled(value);
152 153 }
153 154 }
154 155
155 156 void addSelectionZone(VisualizationSelectionZoneItem *zone) { m_SelectionZones << zone; }
156 157
157 158 VisualizationSelectionZoneItem *selectionZoneAt(const QPoint &pos,
158 159 const QCustomPlot &plot) const
159 160 {
160 161 VisualizationSelectionZoneItem *selectionZoneItemUnderCursor = nullptr;
161 162 auto minDistanceToZone = -1;
162 163 for (auto zone : m_SelectionZones) {
163 164 auto distanceToZone = zone->selectTest(pos, false);
164 165 if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone)
165 166 && distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
166 167 selectionZoneItemUnderCursor = zone;
167 168 }
168 169 }
169 170
170 171 return selectionZoneItemUnderCursor;
171 172 }
172 173
174 QVector<VisualizationSelectionZoneItem *> selectionZonesAt(const QPoint &pos,
175 const QCustomPlot &plot) const
176 {
177 QVector<VisualizationSelectionZoneItem *> zones;
178 for (auto zone : m_SelectionZones) {
179 auto distanceToZone = zone->selectTest(pos, false);
180 if (distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
181 zones << zone;
182 }
183 }
184
185 return zones;
186 }
187
188 void moveSelectionZoneOnTop(VisualizationSelectionZoneItem *zone, QCustomPlot &plot)
189 {
190 if (!m_SelectionZones.isEmpty() && m_SelectionZones.last() != zone) {
191 zone->moveToTop();
192 m_SelectionZones.removeAll(zone);
193 m_SelectionZones.append(zone);
194 }
195 }
196
173 197 QPointF posToAxisPos(const QPoint &pos, QCustomPlot &plot) const
174 198 {
175 199 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
176 200 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
177 201 return QPointF{axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y())};
178 202 }
179 203
180 204 bool pointIsInAxisRect(const QPointF &axisPoint, QCustomPlot &plot) const
181 205 {
182 206 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
183 207 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
184 208 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
185 209 }
186 210 };
187 211
188 212 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
189 213 : VisualizationDragWidget{parent},
190 214 ui{new Ui::VisualizationGraphWidget},
191 215 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
192 216 {
193 217 ui->setupUi(this);
194 218
195 219 // 'Close' options : widget is deleted when closed
196 220 setAttribute(Qt::WA_DeleteOnClose);
197 221
198 222 // Set qcpplot properties :
199 223 // - zoom is enabled
200 224 // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation
201 225 ui->widget->setInteractions(QCP::iRangeZoom);
202 226 ui->widget->axisRect()->setRangeDrag(Qt::Horizontal | Qt::Vertical);
203 227
204 228 // The delegate must be initialized after the ui as it uses the plot
205 229 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
206 230
207 231 // Init the cursors
208 232 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
209 233 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
210 234 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
211 235 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
212 236
213 237 connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress);
214 238 connect(ui->widget, &QCustomPlot::mouseRelease, this,
215 239 &VisualizationGraphWidget::onMouseRelease);
216 240 connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove);
217 241 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
218 242 connect(ui->widget, &QCustomPlot::mouseDoubleClick, this,
219 243 &VisualizationGraphWidget::onMouseDoubleClick);
220 244 connect(
221 245 ui->widget->xAxis,
222 246 static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(&QCPAxis::rangeChanged),
223 247 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
224 248
225 249 // Activates menu when right clicking on the graph
226 250 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
227 251 connect(ui->widget, &QCustomPlot::customContextMenuRequested, this,
228 252 &VisualizationGraphWidget::onGraphMenuRequested);
229 253
230 254 connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
231 255 &VariableController::onRequestDataLoading);
232 256
233 257 connect(&sqpApp->variableController(), &VariableController::updateVarDisplaying, this,
234 258 &VisualizationGraphWidget::onUpdateVarDisplaying);
235 259
236 260 #ifdef Q_OS_MAC
237 261 plot().setPlottingHint(QCP::phFastPolylines, true);
238 262 #endif
239 263 }
240 264
241 265
242 266 VisualizationGraphWidget::~VisualizationGraphWidget()
243 267 {
244 268 delete ui;
245 269 }
246 270
247 271 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
248 272 {
249 273 auto parent = parentWidget();
250 274 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
251 275 parent = parent->parentWidget();
252 276 }
253 277
254 278 return qobject_cast<VisualizationZoneWidget *>(parent);
255 279 }
256 280
257 281 VisualizationWidget *VisualizationGraphWidget::parentVisualizationWidget() const
258 282 {
259 283 auto parent = parentWidget();
260 284 while (parent != nullptr && !qobject_cast<VisualizationWidget *>(parent)) {
261 285 parent = parent->parentWidget();
262 286 }
263 287
264 288 return qobject_cast<VisualizationWidget *>(parent);
265 289 }
266 290
267 291 void VisualizationGraphWidget::enableAcquisition(bool enable)
268 292 {
269 293 impl->m_DoAcquisition = enable;
270 294 }
271 295
272 296 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, SqpRange range)
273 297 {
274 298 // Uses delegate to create the qcpplot components according to the variable
275 299 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
276 300
277 301 if (auto dataSeries = variable->dataSeries()) {
278 302 // Set axes properties according to the units of the data series
279 303 impl->m_RenderingDelegate->setAxesProperties(dataSeries);
280 304
281 305 // Sets rendering properties for the new plottables
282 306 // Warning: this method must be called after setAxesProperties(), as it can access to some
283 307 // axes properties that have to be initialized
284 308 impl->m_RenderingDelegate->setPlottablesProperties(dataSeries, createdPlottables);
285 309 }
286 310
287 311 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
288 312
289 313 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
290 314
291 315 this->enableAcquisition(false);
292 316 this->setGraphRange(range);
293 317 this->enableAcquisition(true);
294 318
295 319 emit requestDataLoading(QVector<std::shared_ptr<Variable> >() << variable, range, false);
296 320
297 321 emit variableAdded(variable);
298 322 }
299 323
300 324 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
301 325 {
302 326 // Each component associated to the variable :
303 327 // - is removed from qcpplot (which deletes it)
304 328 // - is no longer referenced in the map
305 329 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
306 330 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
307 331 emit variableAboutToBeRemoved(variable);
308 332
309 333 auto &plottablesMap = variableIt->second;
310 334
311 335 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
312 336 plottableIt != plottableEnd;) {
313 337 ui->widget->removePlottable(plottableIt->second);
314 338 plottableIt = plottablesMap.erase(plottableIt);
315 339 }
316 340
317 341 impl->m_VariableToPlotMultiMap.erase(variableIt);
318 342 }
319 343
320 344 // Updates graph
321 345 ui->widget->replot();
322 346 }
323 347
324 348 QList<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
325 349 {
326 350 auto variables = QList<std::shared_ptr<Variable> >{};
327 351 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
328 352 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
329 353 variables << it->first;
330 354 }
331 355
332 356 return variables;
333 357 }
334 358
335 359 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
336 360 {
337 361 if (!variable) {
338 362 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
339 363 return;
340 364 }
341 365
342 366 VisualizationGraphHelper::setYAxisRange(variable, *ui->widget);
343 367 }
344 368
345 369 SqpRange VisualizationGraphWidget::graphRange() const noexcept
346 370 {
347 371 auto graphRange = ui->widget->xAxis->range();
348 372 return SqpRange{graphRange.lower, graphRange.upper};
349 373 }
350 374
351 375 void VisualizationGraphWidget::setGraphRange(const SqpRange &range)
352 376 {
353 377 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange START");
354 378 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
355 379 ui->widget->replot();
356 380 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange END");
357 381 }
358 382
359 383 QVector<SqpRange> VisualizationGraphWidget::selectionZoneRanges() const
360 384 {
361 385 QVector<SqpRange> ranges;
362 386 for (auto zone : impl->m_SelectionZones) {
363 387 ranges << zone->range();
364 388 }
365 389
366 390 return ranges;
367 391 }
368 392
369 393 void VisualizationGraphWidget::addSelectionZones(const QVector<SqpRange> &ranges)
370 394 {
371 395 for (const auto &range : ranges) {
372 396 // note: ownership is transfered to QCustomPlot
373 397 auto zone = new VisualizationSelectionZoneItem(&plot());
374 398 zone->setRange(range.m_TStart, range.m_TEnd);
375 399 impl->addSelectionZone(zone);
376 400 }
377 401
378 402 plot().replot(QCustomPlot::rpQueuedReplot);
379 403 }
380 404
381 405 void VisualizationGraphWidget::removeSelectionZone(VisualizationSelectionZoneItem *selectionZone)
382 406 {
383 407 parentVisualizationWidget()->selectionZoneManager().setSelected(selectionZone, false);
384 408
385 409 if (impl->m_HoveredZone == selectionZone) {
386 410 impl->m_HoveredZone = nullptr;
387 411 setCursor(Qt::ArrowCursor);
388 412 }
389 413
390 414 impl->m_SelectionZones.removeAll(selectionZone);
391 415 plot().removeItem(selectionZone);
392 416 plot().replot(QCustomPlot::rpQueuedReplot);
393 417 }
394 418
395 419 void VisualizationGraphWidget::undoZoom()
396 420 {
397 421 auto zoom = impl->m_ZoomStack.pop();
398 422 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
399 423 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
400 424
401 425 axisX->setRange(zoom.first);
402 426 axisY->setRange(zoom.second);
403 427
404 428 plot().replot(QCustomPlot::rpQueuedReplot);
405 429 }
406 430
407 431 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
408 432 {
409 433 if (visitor) {
410 434 visitor->visit(this);
411 435 }
412 436 else {
413 437 qCCritical(LOG_VisualizationGraphWidget())
414 438 << tr("Can't visit widget : the visitor is null");
415 439 }
416 440 }
417 441
418 442 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
419 443 {
420 444 auto isSpectrogram = [](const auto &variable) {
421 445 return std::dynamic_pointer_cast<SpectrogramSeries>(variable.dataSeries()) != nullptr;
422 446 };
423 447
424 448 // - A spectrogram series can't be dropped on graph with existing plottables
425 449 // - No data series can be dropped on graph with existing spectrogram series
426 450 return isSpectrogram(variable)
427 451 ? impl->m_VariableToPlotMultiMap.empty()
428 452 : std::none_of(
429 453 impl->m_VariableToPlotMultiMap.cbegin(), impl->m_VariableToPlotMultiMap.cend(),
430 454 [isSpectrogram](const auto &entry) { return isSpectrogram(*entry.first); });
431 455 }
432 456
433 457 bool VisualizationGraphWidget::contains(const Variable &variable) const
434 458 {
435 459 // Finds the variable among the keys of the map
436 460 auto variablePtr = &variable;
437 461 auto findVariable
438 462 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
439 463
440 464 auto end = impl->m_VariableToPlotMultiMap.cend();
441 465 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
442 466 return it != end;
443 467 }
444 468
445 469 QString VisualizationGraphWidget::name() const
446 470 {
447 471 return impl->m_Name;
448 472 }
449 473
450 474 QMimeData *VisualizationGraphWidget::mimeData(const QPoint &position) const
451 475 {
452 476 auto mimeData = new QMimeData;
453 477
454 478 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position, plot());
455 479 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
456 480 && selectionZoneItemUnderCursor) {
457 481 mimeData->setData(MIME_TYPE_TIME_RANGE, TimeController::mimeDataForTimeRange(
458 482 selectionZoneItemUnderCursor->range()));
459 483 mimeData->setData(MIME_TYPE_SELECTION_ZONE, TimeController::mimeDataForTimeRange(
460 484 selectionZoneItemUnderCursor->range()));
461 485 }
462 486 else {
463 487 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
464 488
465 489 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
466 490 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
467 491 }
468 492
469 493 return mimeData;
470 494 }
471 495
472 496 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint &dragPosition)
473 497 {
474 498 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition, plot());
475 499 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
476 500 && selectionZoneItemUnderCursor) {
477 501
478 502 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
479 503 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
480 504
481 505 auto zoneSize = QSizeF{qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
482 506 qAbs(zoneBottomRight.y() - zoneTopLeft.y())}
483 507 .toSize();
484 508
485 509 auto pixmap = QPixmap(zoneSize);
486 510 render(&pixmap, QPoint(), QRegion{QRect{zoneTopLeft.toPoint(), zoneSize}});
487 511
488 512 return pixmap;
489 513 }
490 514
491 515 return QPixmap();
492 516 }
493 517
494 518 bool VisualizationGraphWidget::isDragAllowed() const
495 519 {
496 520 return true;
497 521 }
498 522
499 523 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
500 524 {
501 525 if (highlighted) {
502 526 plot().setBackground(QBrush(QColor("#BBD5EE")));
503 527 }
504 528 else {
505 529 plot().setBackground(QBrush(Qt::white));
506 530 }
507 531
508 532 plot().update();
509 533 }
510 534
511 535 void VisualizationGraphWidget::addVerticalCursor(double time)
512 536 {
513 537 impl->m_VerticalCursor->setPosition(time);
514 538 impl->m_VerticalCursor->setVisible(true);
515 539
516 540 auto text
517 541 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
518 542 impl->m_VerticalCursor->setLabelText(text);
519 543 }
520 544
521 545 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
522 546 {
523 547 impl->m_VerticalCursor->setAbsolutePosition(position);
524 548 impl->m_VerticalCursor->setVisible(true);
525 549
526 550 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
527 551 auto text
528 552 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
529 553 impl->m_VerticalCursor->setLabelText(text);
530 554 }
531 555
532 556 void VisualizationGraphWidget::removeVerticalCursor()
533 557 {
534 558 impl->m_VerticalCursor->setVisible(false);
535 559 plot().replot(QCustomPlot::rpQueuedReplot);
536 560 }
537 561
538 562 void VisualizationGraphWidget::addHorizontalCursor(double value)
539 563 {
540 564 impl->m_HorizontalCursor->setPosition(value);
541 565 impl->m_HorizontalCursor->setVisible(true);
542 566 impl->m_HorizontalCursor->setLabelText(QString::number(value));
543 567 }
544 568
545 569 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
546 570 {
547 571 impl->m_HorizontalCursor->setAbsolutePosition(position);
548 572 impl->m_HorizontalCursor->setVisible(true);
549 573
550 574 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
551 575 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
552 576 }
553 577
554 578 void VisualizationGraphWidget::removeHorizontalCursor()
555 579 {
556 580 impl->m_HorizontalCursor->setVisible(false);
557 581 plot().replot(QCustomPlot::rpQueuedReplot);
558 582 }
559 583
560 584 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
561 585 {
562 586 Q_UNUSED(event);
563 587
564 588 // Prevents that all variables will be removed from graph when it will be closed
565 589 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
566 590 emit variableAboutToBeRemoved(variableEntry.first);
567 591 }
568 592 }
569 593
570 594 void VisualizationGraphWidget::enterEvent(QEvent *event)
571 595 {
572 596 Q_UNUSED(event);
573 597 impl->m_RenderingDelegate->showGraphOverlay(true);
574 598 }
575 599
576 600 void VisualizationGraphWidget::leaveEvent(QEvent *event)
577 601 {
578 602 Q_UNUSED(event);
579 603 impl->m_RenderingDelegate->showGraphOverlay(false);
580 604
581 605 if (auto parentZone = parentZoneWidget()) {
582 606 parentZone->notifyMouseLeaveGraph(this);
583 607 }
584 608 else {
585 609 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
586 610 }
587 611
588 612 if (impl->m_HoveredZone) {
589 613 impl->m_HoveredZone->setHovered(false);
590 614 impl->m_HoveredZone = nullptr;
591 615 }
592 616 }
593 617
594 618 QCustomPlot &VisualizationGraphWidget::plot() const noexcept
595 619 {
596 620 return *ui->widget;
597 621 }
598 622
599 623 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
600 624 {
601 625 QMenu graphMenu{};
602 626
603 627 // Iterates on variables (unique keys)
604 628 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
605 629 end = impl->m_VariableToPlotMultiMap.cend();
606 630 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
607 631 // 'Remove variable' action
608 632 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
609 633 [ this, var = it->first ]() { removeVariable(var); });
610 634 }
611 635
612 636 if (!impl->m_ZoomStack.isEmpty()) {
613 637 if (!graphMenu.isEmpty()) {
614 638 graphMenu.addSeparator();
615 639 }
616 640
617 641 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
618 642 }
619 643
620 644 // Selection Zone Actions
621 645 auto selectionZoneItem = impl->selectionZoneAt(pos, plot());
622 646 if (selectionZoneItem) {
623 647 auto selectedItems = parentVisualizationWidget()->selectionZoneManager().selectedItems();
624 648 selectedItems.removeAll(selectionZoneItem);
625 649 selectedItems.prepend(selectionZoneItem); // Put the current selection zone first
626 650
627 651 auto zoneActions = sqpApp->actionsGuiController().selectionZoneActions();
628 652 if (!zoneActions.isEmpty() && !graphMenu.isEmpty()) {
629 653 graphMenu.addSeparator();
630 654 }
631 655
632 656 QHash<QString, QMenu *> subMenus;
633 657 QHash<QString, bool> subMenusEnabled;
634 658
635 659 for (auto zoneAction : zoneActions) {
636 660
637 661 auto isEnabled = zoneAction->isEnabled(selectedItems);
638 662
639 663 auto menu = &graphMenu;
640 664 for (auto subMenuName : zoneAction->subMenuList()) {
641 665 if (!subMenus.contains(subMenuName)) {
642 666 menu = menu->addMenu(subMenuName);
643 667 subMenus[subMenuName] = menu;
644 668 subMenusEnabled[subMenuName] = isEnabled;
645 669 }
646 670 else {
647 671 menu = subMenus.value(subMenuName);
648 672 if (isEnabled) {
649 673 // The sub menu is enabled if at least one of its actions is enabled
650 674 subMenusEnabled[subMenuName] = true;
651 675 }
652 676 }
653 677 }
654 678
655 679 auto action = menu->addAction(zoneAction->name());
656 680 action->setEnabled(isEnabled);
657 681 action->setShortcut(zoneAction->displayedShortcut());
658 682 QObject::connect(action, &QAction::triggered,
659 683 [zoneAction, selectedItems]() { zoneAction->execute(selectedItems); });
660 684 }
661 685
662 686 for (auto it = subMenus.cbegin(); it != subMenus.cend(); ++it) {
663 687 it.value()->setEnabled(subMenusEnabled[it.key()]);
664 688 }
665 689 }
666 690
667 691 if (!graphMenu.isEmpty()) {
668 692 graphMenu.exec(QCursor::pos());
669 693 }
670 694 }
671 695
672 696 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
673 697 {
674 698 qCDebug(LOG_VisualizationGraphWidget())
675 699 << tr("TORM: VisualizationGraphWidget::onRangeChanged")
676 700 << QThread::currentThread()->objectName() << "DoAcqui" << impl->m_DoAcquisition;
677 701
678 702 auto graphRange = SqpRange{t1.lower, t1.upper};
679 703 auto oldGraphRange = SqpRange{t2.lower, t2.upper};
680 704
681 705 if (impl->m_DoAcquisition) {
682 706 QVector<std::shared_ptr<Variable> > variableUnderGraphVector;
683 707
684 708 for (auto it = impl->m_VariableToPlotMultiMap.begin(),
685 709 end = impl->m_VariableToPlotMultiMap.end();
686 710 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
687 711 variableUnderGraphVector.push_back(it->first);
688 712 }
689 713 emit requestDataLoading(std::move(variableUnderGraphVector), graphRange,
690 714 !impl->m_IsCalibration);
691 715
692 716 if (!impl->m_IsCalibration) {
693 717 qCDebug(LOG_VisualizationGraphWidget())
694 718 << tr("TORM: VisualizationGraphWidget::Synchronize notify !!")
695 719 << QThread::currentThread()->objectName() << graphRange << oldGraphRange;
696 720 emit synchronize(graphRange, oldGraphRange);
697 721 }
698 722 }
699 723
700 724 auto pos = mapFromGlobal(QCursor::pos());
701 725 auto axisPos = impl->posToAxisPos(pos, plot());
702 726 if (auto parentZone = parentZoneWidget()) {
703 727 if (impl->pointIsInAxisRect(axisPos, plot())) {
704 728 parentZone->notifyMouseMoveInGraph(pos, axisPos, this);
705 729 }
706 730 else {
707 731 parentZone->notifyMouseLeaveGraph(this);
708 732 }
709 733 }
710 734 else {
711 735 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
712 736 }
713 737 }
714 738
715 739 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept
716 740 {
717 741 impl->m_RenderingDelegate->onMouseDoubleClick(event);
718 742 }
719 743
720 744 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
721 745 {
722 746 // Handles plot rendering when mouse is moving
723 747 impl->m_RenderingDelegate->onMouseMove(event);
724 748
725 749 auto axisPos = impl->posToAxisPos(event->pos(), plot());
726 750
727 751 // Zoom box and zone drawing
728 752 if (impl->m_DrawingZoomRect) {
729 753 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
730 754 }
731 755 else if (impl->m_DrawingZone) {
732 756 impl->m_DrawingZone->setEnd(axisPos.x());
733 757 }
734 758
735 759 // Cursor
736 760 if (auto parentZone = parentZoneWidget()) {
737 761 if (impl->pointIsInAxisRect(axisPos, plot())) {
738 762 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
739 763 }
740 764 else {
741 765 parentZone->notifyMouseLeaveGraph(this);
742 766 }
743 767 }
744 768 else {
745 769 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
746 770 }
747 771
748 772 // Search for the selection zone under the mouse
749 773 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
750 774 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
751 775 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) {
752 776
753 777 // Sets the appropriate cursor shape
754 778 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
755 779 setCursor(cursorShape);
756 780
757 781 // Manages the hovered zone
758 782 if (selectionZoneItemUnderCursor != impl->m_HoveredZone) {
759 783 if (impl->m_HoveredZone) {
760 784 impl->m_HoveredZone->setHovered(false);
761 785 }
762 786 selectionZoneItemUnderCursor->setHovered(true);
763 787 impl->m_HoveredZone = selectionZoneItemUnderCursor;
764 788 plot().replot(QCustomPlot::rpQueuedReplot);
765 789 }
766 790 }
767 791 else {
768 792 // There is no zone under the mouse or the interaction mode is not "selection zones"
769 793 if (impl->m_HoveredZone) {
770 794 impl->m_HoveredZone->setHovered(false);
771 795 impl->m_HoveredZone = nullptr;
772 796 }
773 797
774 798 setCursor(Qt::ArrowCursor);
775 799 }
776 800
777 801 impl->m_HasMovedMouse = true;
778 802 VisualizationDragWidget::mouseMoveEvent(event);
779 803 }
780 804
781 805 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
782 806 {
783 807 auto value = event->angleDelta().x() + event->angleDelta().y();
784 808 if (value != 0) {
785 809
786 810 auto direction = value > 0 ? 1.0 : -1.0;
787 811 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
788 812 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
789 813 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
790 814
791 815 auto zoomOrientations = QFlags<Qt::Orientation>{};
792 816 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
793 817 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
794 818
795 819 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
796 820
797 821 if (!isZoomX && !isZoomY) {
798 822 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
799 823 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
800 824
801 825 axis->setRange(axis->range() + diff);
802 826
803 827 if (plot().noAntialiasingOnDrag()) {
804 828 plot().setNotAntialiasedElements(QCP::aeAll);
805 829 }
806 830
807 831 plot().replot(QCustomPlot::rpQueuedReplot);
808 832 }
809 833 }
810 834 }
811 835
812 836 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
813 837 {
814 838 auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
815 839 auto isSelectionZoneMode
816 840 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
817 841 auto isLeftClick = event->buttons().testFlag(Qt::LeftButton);
818 842
819 843 if (!isDragDropClick && isLeftClick) {
820 844 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) {
821 845 // Starts a zoom box
822 846 impl->startDrawingRect(event->pos(), plot());
823 847 }
824 848 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr) {
825 849 // Starts a new selection zone
826 850 auto zoneAtPos = impl->selectionZoneAt(event->pos(), plot());
827 851 if (!zoneAtPos) {
828 852 impl->startDrawingZone(event->pos(), this);
829 853 }
830 854 }
831 855 }
832 856
833 857 // Allows mouse panning only in default mode
834 858 plot().setInteraction(QCP::iRangeDrag, sqpApp->plotsInteractionMode()
835 859 == SqpApplication::PlotsInteractionMode::None
836 860 && !isDragDropClick);
837 861
838 862 // Allows zone edition only in selection zone mode without drag&drop
839 863 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
840 864
841 865 // Selection / Deselection
842 866 if (isSelectionZoneMode) {
843 867 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
844 868 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
845 869
846 870
847 871 if (selectionZoneItemUnderCursor && !selectionZoneItemUnderCursor->selected()
848 872 && !isMultiSelectionClick) {
849 873 parentVisualizationWidget()->selectionZoneManager().select(
850 874 {selectionZoneItemUnderCursor});
851 875 }
852 876 else if (!selectionZoneItemUnderCursor && !isMultiSelectionClick && isLeftClick) {
853 877 parentVisualizationWidget()->selectionZoneManager().clearSelection();
854 878 }
855 879 else {
856 880 // No selection change
857 881 }
858 882
859 883 if (selectionZoneItemUnderCursor && isLeftClick) {
860 884 selectionZoneItemUnderCursor->setAssociatedEditedZones(
861 885 parentVisualizationWidget()->selectionZoneManager().selectedItems());
862 886 }
863 887 }
864 888
865 889
866 890 impl->m_HasMovedMouse = false;
867 891 VisualizationDragWidget::mousePressEvent(event);
868 892 }
869 893
870 894 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
871 895 {
872 896 if (impl->m_DrawingZoomRect) {
873 897
874 898 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
875 899 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
876 900
877 901 auto newAxisXRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().x(),
878 902 impl->m_DrawingZoomRect->bottomRight->coords().x()};
879 903
880 904 auto newAxisYRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().y(),
881 905 impl->m_DrawingZoomRect->bottomRight->coords().y()};
882 906
883 907 impl->removeDrawingRect(plot());
884 908
885 909 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
886 910 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
887 911 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
888 912 axisX->setRange(newAxisXRange);
889 913 axisY->setRange(newAxisYRange);
890 914
891 915 plot().replot(QCustomPlot::rpQueuedReplot);
892 916 }
893 917 }
894 918
895 919 impl->endDrawingZone(this);
896 920
897 921 impl->m_IsCalibration = false;
898 922
899 923 // Selection / Deselection
900 924 auto isSelectionZoneMode
901 925 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
902 926 if (isSelectionZoneMode) {
903 927 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
904 928 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
905 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton) {
906 if (!isMultiSelectionClick && !impl->m_HasMovedMouse) {
907 parentVisualizationWidget()->selectionZoneManager().select(
908 {selectionZoneItemUnderCursor});
929 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton
930 && !impl->m_HasMovedMouse) {
931
932 auto zonesUnderCursor = impl->selectionZonesAt(event->pos(), plot());
933 if (zonesUnderCursor.count() > 1) {
934 // There are multiple zones under the mouse.
935 // Performs the selection with a selection dialog.
936 VisualizationMultiZoneSelectionDialog dialog{this};
937 dialog.setZones(zonesUnderCursor);
938 dialog.move(mapToGlobal(event->pos() - QPoint(dialog.width() / 2, 20)));
939 dialog.activateWindow();
940 dialog.raise();
941 if (dialog.exec() == QDialog::Accepted) {
942 auto selection = dialog.selectedZones();
943
944 if (!isMultiSelectionClick) {
945 parentVisualizationWidget()->selectionZoneManager().clearSelection();
946 }
947
948 for (auto it = selection.cbegin(); it != selection.cend(); ++it) {
949 auto zone = it.key();
950 auto isSelected = it.value();
951 parentVisualizationWidget()->selectionZoneManager().setSelected(zone,
952 isSelected);
953
954 if (isSelected) {
955 // Puts the zone on top of the stack so it can be moved or resized
956 impl->moveSelectionZoneOnTop(zone, plot());
957 }
958 }
959 }
909 960 }
910 else if (!impl->m_HasMovedMouse) {
911 parentVisualizationWidget()->selectionZoneManager().setSelected(
912 selectionZoneItemUnderCursor, !selectionZoneItemUnderCursor->selected()
913 || event->button() == Qt::RightButton);
961 else {
962 if (!isMultiSelectionClick) {
963 parentVisualizationWidget()->selectionZoneManager().select(
964 {selectionZoneItemUnderCursor});
965 impl->moveSelectionZoneOnTop(selectionZoneItemUnderCursor, plot());
966 }
967 else {
968 parentVisualizationWidget()->selectionZoneManager().setSelected(
969 selectionZoneItemUnderCursor, !selectionZoneItemUnderCursor->selected()
970 || event->button() == Qt::RightButton);
971 }
914 972 }
915 973 }
916 974 else {
917 975 // No selection change
918 976 }
919 977 }
920 978 }
921 979
922 980 void VisualizationGraphWidget::onDataCacheVariableUpdated()
923 981 {
924 982 auto graphRange = ui->widget->xAxis->range();
925 983 auto dateTime = SqpRange{graphRange.lower, graphRange.upper};
926 984
927 985 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
928 986 auto variable = variableEntry.first;
929 987 qCDebug(LOG_VisualizationGraphWidget())
930 988 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
931 989 qCDebug(LOG_VisualizationGraphWidget())
932 990 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
933 991 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
934 992 impl->updateData(variableEntry.second, variable->dataSeries(), variable->range());
935 993 }
936 994 }
937 995 }
938 996
939 997 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
940 998 const SqpRange &range)
941 999 {
942 1000 auto it = impl->m_VariableToPlotMultiMap.find(variable);
943 1001 if (it != impl->m_VariableToPlotMultiMap.end()) {
944 1002 impl->updateData(it->second, variable->dataSeries(), range);
945 1003 }
946 1004 }
@@ -1,433 +1,442
1 1 #include "Visualization/VisualizationSelectionZoneItem.h"
2 2 #include "Visualization/VisualizationGraphWidget.h"
3 3 #include "Visualization/VisualizationSelectionZoneManager.h"
4 4 #include "Visualization/VisualizationWidget.h"
5 5
6 6 const QString &DEFAULT_COLOR = QStringLiteral("#E79D41");
7 7
8 8 struct VisualizationSelectionZoneItem::VisualizationSelectionZoneItemPrivate {
9 9
10 10 QCustomPlot *m_Plot;
11 11 double m_T1 = 0;
12 12 double m_T2 = 0;
13 13 QColor m_Color;
14 14
15 15 bool m_IsEditionEnabled = true;
16 16 double m_MovedOrinalT1 = 0;
17 17 double m_MovedOrinalT2 = 0;
18 18
19 19 QCPItemStraightLine *m_LeftLine;
20 20 QCPItemStraightLine *m_RightLine;
21 21 QCPItemText *m_NameLabelItem = nullptr;
22 22
23 23 enum class EditionMode { NoEdition, ResizeLeft, ResizeRight, Move };
24 24 EditionMode m_CurrentEditionMode;
25 25
26 26 QVector<VisualizationSelectionZoneItem *> m_AssociatedEditedZones;
27 27
28 28 VisualizationSelectionZoneItemPrivate(QCustomPlot *plot)
29 29 : m_Plot(plot), m_Color(Qt::blue), m_CurrentEditionMode(EditionMode::NoEdition)
30 30 {
31 31 }
32 32
33 33 void updatePosition(VisualizationSelectionZoneItem *item)
34 34 {
35 35 item->topLeft->setCoords(m_T1, 0);
36 36 item->bottomRight->setCoords(m_T2, 1);
37 37 }
38 38
39 39 EditionMode getEditionMode(const QPoint &pos, const VisualizationSelectionZoneItem *zoneItem)
40 40 {
41 41 auto distanceLeft = m_LeftLine->selectTest(pos, false);
42 42 auto distanceRight = m_RightLine->selectTest(pos, false);
43 43 auto distance = zoneItem->selectTest(pos, false);
44 44
45 45 if (distanceRight <= distance) {
46 46 return VisualizationSelectionZoneItemPrivate::EditionMode::ResizeRight;
47 47 }
48 48 else if (distanceLeft <= distance) {
49 49 return VisualizationSelectionZoneItemPrivate::EditionMode::ResizeLeft;
50 50 }
51 51
52 52 return VisualizationSelectionZoneItemPrivate::EditionMode::Move;
53 53 }
54 54
55 55 double pixelSizeToAxisXSize(double pixels)
56 56 {
57 57 auto axis = m_Plot->axisRect()->axis(QCPAxis::atBottom);
58 58 return axis->pixelToCoord(pixels) - axis->pixelToCoord(0);
59 59 }
60 60
61 61 bool alignZones(VisualizationSelectionZoneItem *referenceZone,
62 62 const QVector<VisualizationSelectionZoneItem *> &zonesToAlign, bool alignOnLeft,
63 63 bool allowResize, bool vertically)
64 64 {
65 65 auto result = false;
66 66
67 67 auto referenceTime
68 68 = alignOnLeft ? referenceZone->range().m_TStart : referenceZone->range().m_TEnd;
69 69
70 70 auto referenceBottomAxis = m_Plot->axisRect()->axis(QCPAxis::atBottom);
71 71 auto referenceVerticalPosition = referenceBottomAxis->coordToPixel(referenceTime);
72 72
73 73 for (auto otherZone : zonesToAlign) {
74 74
75 75 auto otherZoneRange = otherZone->range();
76 76 auto newZoneStart = otherZoneRange.m_TStart;
77 77 auto newZoneEnd = otherZoneRange.m_TEnd;
78 78
79 79 auto alignedTime = referenceTime;
80 80 if (vertically) {
81 81 auto otherZoneAxis = otherZone->parentPlot()->axisRect()->axis(QCPAxis::atBottom);
82 82 alignedTime = otherZoneAxis->pixelToCoord(referenceVerticalPosition);
83 83 }
84 84
85 85 if (alignOnLeft) {
86 86 newZoneStart = alignedTime;
87 87 if (!allowResize) {
88 88 newZoneEnd = alignedTime + (otherZoneRange.m_TEnd - otherZoneRange.m_TStart);
89 89 }
90 90 }
91 91 else { // align on right
92 92 newZoneEnd = alignedTime;
93 93 if (!allowResize) {
94 94 newZoneStart = alignedTime - (otherZoneRange.m_TEnd - otherZoneRange.m_TStart);
95 95 }
96 96 }
97 97
98 98 if (newZoneStart < newZoneEnd) {
99 99 result = true;
100 100 otherZone->setRange(newZoneStart, newZoneEnd);
101 101 otherZone->parentPlot()->replot();
102 102 }
103 103 }
104 104
105 105 return result;
106 106 }
107 107 };
108 108
109 109 VisualizationSelectionZoneItem::VisualizationSelectionZoneItem(QCustomPlot *plot)
110 110 : QCPItemRect(plot),
111 111 impl{spimpl::make_unique_impl<VisualizationSelectionZoneItemPrivate>(plot)}
112 112 {
113 113 topLeft->setTypeX(QCPItemPosition::ptPlotCoords);
114 114 topLeft->setTypeY(QCPItemPosition::ptAxisRectRatio);
115 115 bottomRight->setTypeX(QCPItemPosition::ptPlotCoords);
116 116 bottomRight->setTypeY(QCPItemPosition::ptAxisRectRatio);
117 117 setSelectable(false);
118 118
119 119 impl->m_RightLine = new QCPItemStraightLine(plot);
120 120 impl->m_RightLine->point1->setParentAnchor(topRight);
121 121 impl->m_RightLine->point2->setParentAnchor(bottomRight);
122 122 impl->m_RightLine->point1->setTypeX(QCPItemPosition::ptAbsolute);
123 123 impl->m_RightLine->point1->setTypeY(QCPItemPosition::ptAbsolute);
124 124 impl->m_RightLine->point2->setTypeX(QCPItemPosition::ptAbsolute);
125 125 impl->m_RightLine->point2->setTypeY(QCPItemPosition::ptAbsolute);
126 126 impl->m_RightLine->setSelectable(false);
127 127
128 128 impl->m_LeftLine = new QCPItemStraightLine(plot);
129 129 impl->m_LeftLine->point1->setParentAnchor(topLeft);
130 130 impl->m_LeftLine->point2->setParentAnchor(bottomLeft);
131 131 impl->m_LeftLine->point1->setTypeX(QCPItemPosition::ptAbsolute);
132 132 impl->m_LeftLine->point1->setTypeY(QCPItemPosition::ptAbsolute);
133 133 impl->m_LeftLine->point2->setTypeX(QCPItemPosition::ptAbsolute);
134 134 impl->m_LeftLine->point2->setTypeY(QCPItemPosition::ptAbsolute);
135 135 impl->m_LeftLine->setSelectable(false);
136 136
137 137 connect(this, &VisualizationSelectionZoneItem::selectionChanged, impl->m_RightLine,
138 138 &QCPItemStraightLine::setSelected);
139 139 connect(this, &VisualizationSelectionZoneItem::selectionChanged, impl->m_LeftLine,
140 140 &QCPItemStraightLine::setSelected);
141 141
142 142 setColor(QColor(DEFAULT_COLOR));
143 143 }
144 144
145 145 VisualizationSelectionZoneItem::~VisualizationSelectionZoneItem()
146 146 {
147 147 impl->m_Plot->removeItem(impl->m_RightLine);
148 148 impl->m_Plot->removeItem(impl->m_LeftLine);
149 149 }
150 150
151 151 VisualizationGraphWidget *VisualizationSelectionZoneItem::parentGraphWidget() const noexcept
152 152 {
153 153 auto parent = impl->m_Plot->parentWidget();
154 154 while (parent != nullptr && !qobject_cast<VisualizationGraphWidget *>(parent)) {
155 155 parent = parent->parentWidget();
156 156 }
157 157
158 158 return qobject_cast<VisualizationGraphWidget *>(parent);
159 159 }
160 160
161 161 void VisualizationSelectionZoneItem::setName(const QString &name)
162 162 {
163 163 if (name.isEmpty() && impl->m_NameLabelItem) {
164 164 impl->m_Plot->removeItem(impl->m_NameLabelItem);
165 165 impl->m_NameLabelItem = nullptr;
166 166 }
167 167 else if (!impl->m_NameLabelItem) {
168 168 impl->m_NameLabelItem = new QCPItemText(impl->m_Plot);
169 169 impl->m_NameLabelItem->setText(name);
170 170 impl->m_NameLabelItem->setPositionAlignment(Qt::AlignHCenter | Qt::AlignTop);
171 171 impl->m_NameLabelItem->setColor(impl->m_Color);
172 172 impl->m_NameLabelItem->position->setParentAnchor(top);
173 173 }
174 174 }
175 175
176 176 QString VisualizationSelectionZoneItem::name() const
177 177 {
178 178 if (!impl->m_NameLabelItem) {
179 179 return QString();
180 180 }
181 181
182 182 return impl->m_NameLabelItem->text();
183 183 }
184 184
185 185 SqpRange VisualizationSelectionZoneItem::range() const
186 186 {
187 187 SqpRange range;
188 188 range.m_TStart = impl->m_T1 <= impl->m_T2 ? impl->m_T1 : impl->m_T2;
189 189 range.m_TEnd = impl->m_T1 > impl->m_T2 ? impl->m_T1 : impl->m_T2;
190 190 return range;
191 191 }
192 192
193 193 void VisualizationSelectionZoneItem::setRange(double tstart, double tend)
194 194 {
195 195 impl->m_T1 = tstart;
196 196 impl->m_T2 = tend;
197 197 impl->updatePosition(this);
198 198 }
199 199
200 200 void VisualizationSelectionZoneItem::setStart(double tstart)
201 201 {
202 202 impl->m_T1 = tstart;
203 203 impl->updatePosition(this);
204 204 }
205 205
206 206 void VisualizationSelectionZoneItem::setEnd(double tend)
207 207 {
208 208 impl->m_T2 = tend;
209 209 impl->updatePosition(this);
210 210 }
211 211
212 212 void VisualizationSelectionZoneItem::setColor(const QColor &color)
213 213 {
214 214 impl->m_Color = color;
215 215
216 216 auto brushColor = color;
217 217 brushColor.setAlpha(80);
218 218 setBrush(QBrush(brushColor));
219 219 setPen(QPen(Qt::NoPen));
220 220
221 221 auto selectedBrushColor = brushColor;
222 222 selectedBrushColor.setAlpha(150);
223 223 setSelectedBrush(QBrush(selectedBrushColor));
224 224 setSelectedPen(QPen(Qt::NoPen));
225 225
226 226 auto linePen = QPen(color);
227 227 linePen.setStyle(Qt::SolidLine);
228 228 linePen.setWidth(4);
229 229
230 230 auto selectedLinePen = linePen;
231 231 selectedLinePen.setColor(color.darker(120));
232 232 selectedLinePen.setWidth(4);
233 233
234 234 impl->m_LeftLine->setPen(linePen);
235 235 impl->m_RightLine->setPen(linePen);
236 236
237 237 impl->m_LeftLine->setSelectedPen(selectedLinePen);
238 238 impl->m_RightLine->setSelectedPen(selectedLinePen);
239 239 }
240 240
241 241 void VisualizationSelectionZoneItem::setEditionEnabled(bool value)
242 242 {
243 243 impl->m_IsEditionEnabled = value;
244 244 setSelectable(value);
245 245 if (!value) {
246 246 setSelected(false);
247 247 impl->m_CurrentEditionMode = VisualizationSelectionZoneItemPrivate::EditionMode::NoEdition;
248 248 }
249 249 }
250 250
251 251 bool VisualizationSelectionZoneItem::isEditionEnabled() const
252 252 {
253 253 return impl->m_IsEditionEnabled;
254 254 }
255 255
256 void VisualizationSelectionZoneItem::moveToTop()
257 {
258 moveToLayer(layer(), false);
259 }
260
256 261 Qt::CursorShape
257 262 VisualizationSelectionZoneItem::curshorShapeForPosition(const QPoint &position) const
258 263 {
259 264 auto mode = impl->m_CurrentEditionMode
260 265 == VisualizationSelectionZoneItemPrivate::EditionMode::NoEdition
261 266 ? impl->getEditionMode(position, this)
262 267 : impl->m_CurrentEditionMode;
263 268 switch (mode) {
264 269 case VisualizationSelectionZoneItemPrivate::EditionMode::Move:
265 270 return Qt::SizeAllCursor;
266 271 case VisualizationSelectionZoneItemPrivate::EditionMode::ResizeLeft:
267 272 case VisualizationSelectionZoneItemPrivate::EditionMode::ResizeRight: // fallthrough
268 273 return Qt::SizeHorCursor;
269 274 default:
270 275 return Qt::ArrowCursor;
271 276 }
272 277 }
273 278
274 279 void VisualizationSelectionZoneItem::setHovered(bool value)
275 280 {
276 281 if (value) {
277 282 auto linePen = impl->m_LeftLine->pen();
278 283 linePen.setStyle(Qt::DotLine);
279 284 linePen.setWidth(3);
280 285
281 286 auto selectedLinePen = impl->m_LeftLine->selectedPen();
282 287 ;
283 288 selectedLinePen.setStyle(Qt::DotLine);
284 289 selectedLinePen.setWidth(3);
285 290
286 291 impl->m_LeftLine->setPen(linePen);
287 292 impl->m_RightLine->setPen(linePen);
288 293
289 294 impl->m_LeftLine->setSelectedPen(selectedLinePen);
290 295 impl->m_RightLine->setSelectedPen(selectedLinePen);
291 296 }
292 297 else {
293 298 setColor(impl->m_Color);
294 299 }
295 300 }
296 301
297 302 void VisualizationSelectionZoneItem::setAssociatedEditedZones(
298 303 const QVector<VisualizationSelectionZoneItem *> &associatedZones)
299 304 {
300 305 impl->m_AssociatedEditedZones = associatedZones;
301 306 impl->m_AssociatedEditedZones.removeAll(this);
302 307 }
303 308
304 309 bool VisualizationSelectionZoneItem::alignZonesVerticallyOnLeft(
305 310 const QVector<VisualizationSelectionZoneItem *> &zonesToAlign, bool allowResize)
306 311 {
307 312 return impl->alignZones(this, zonesToAlign, true, allowResize, true);
308 313 }
309 314
310 315 bool VisualizationSelectionZoneItem::alignZonesVerticallyOnRight(
311 316 const QVector<VisualizationSelectionZoneItem *> &zonesToAlign, bool allowResize)
312 317 {
313 318 return impl->alignZones(this, zonesToAlign, false, allowResize, true);
314 319 }
315 320
316 321 bool VisualizationSelectionZoneItem::alignZonesTemporallyOnLeft(
317 322 const QVector<VisualizationSelectionZoneItem *> &zonesToAlign, bool allowResize)
318 323 {
319 324 return impl->alignZones(this, zonesToAlign, true, allowResize, false);
320 325 }
321 326
322 327 bool VisualizationSelectionZoneItem::alignZonesTemporallyOnRight(
323 328 const QVector<VisualizationSelectionZoneItem *> &zonesToAlign, bool allowResize)
324 329 {
325 330 return impl->alignZones(this, zonesToAlign, false, allowResize, false);
326 331 }
327 332
328 333 void VisualizationSelectionZoneItem::mousePressEvent(QMouseEvent *event, const QVariant &details)
329 334 {
335 Q_UNUSED(details);
336
330 337 if (isEditionEnabled() && event->button() == Qt::LeftButton) {
331 338 impl->m_CurrentEditionMode = impl->getEditionMode(event->pos(), this);
332 339
333 340 impl->m_MovedOrinalT1 = impl->m_T1;
334 341 impl->m_MovedOrinalT2 = impl->m_T2;
335 342 for (auto associatedZone : impl->m_AssociatedEditedZones) {
336 343 associatedZone->impl->m_MovedOrinalT1 = associatedZone->impl->m_T1;
337 344 associatedZone->impl->m_MovedOrinalT2 = associatedZone->impl->m_T2;
338 345 }
339 346 }
340 347 else {
341 348 impl->m_CurrentEditionMode = VisualizationSelectionZoneItemPrivate::EditionMode::NoEdition;
342 349 event->ignore();
343 350 }
344 351 }
345 352
346 353 void VisualizationSelectionZoneItem::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos)
347 354 {
348 355 if (isEditionEnabled()) {
349 356 if (!selected()) {
350 357 // Force the item to be selected during the edition
351 358 parentGraphWidget()->parentVisualizationWidget()->selectionZoneManager().setSelected(
352 359 this, true);
353 360 }
354 361
355 362 auto axis = impl->m_Plot->axisRect()->axis(QCPAxis::atBottom);
356 363 auto pixelDiff = event->pos().x() - startPos.x();
357 364 auto diff = impl->pixelSizeToAxisXSize(pixelDiff);
358 365
359 366 switch (impl->m_CurrentEditionMode) {
360 367 case VisualizationSelectionZoneItemPrivate::EditionMode::Move:
361 368 setRange(impl->m_MovedOrinalT1 + diff, impl->m_MovedOrinalT2 + diff);
362 369 for (auto associatedZone : impl->m_AssociatedEditedZones) {
363 370 associatedZone->move(pixelDiff);
364 371 }
365 372 break;
366 373 case VisualizationSelectionZoneItemPrivate::EditionMode::ResizeLeft:
367 374 setStart(impl->m_MovedOrinalT1 + diff);
368 375 for (auto associatedZone : impl->m_AssociatedEditedZones) {
369 376 impl->m_MovedOrinalT1 < impl->m_MovedOrinalT2
370 377 ? associatedZone->resizeLeft(pixelDiff)
371 378 : associatedZone->resizeRight(pixelDiff);
372 379 }
373 380 break;
374 381 case VisualizationSelectionZoneItemPrivate::EditionMode::ResizeRight:
375 382 setEnd(impl->m_MovedOrinalT2 + diff);
376 383 for (auto associatedZone : impl->m_AssociatedEditedZones) {
377 384 impl->m_MovedOrinalT1 < impl->m_MovedOrinalT2
378 385 ? associatedZone->resizeRight(pixelDiff)
379 386 : associatedZone->resizeLeft(pixelDiff);
380 387 }
381 388 break;
382 389 default:
383 390 break;
384 391 }
385 392
386 393 for (auto associatedZone : impl->m_AssociatedEditedZones) {
387 394 associatedZone->parentPlot()->replot();
388 395 }
389 396 }
390 397 else {
391 398 event->ignore();
392 399 }
393 400 }
394 401
395 402 void VisualizationSelectionZoneItem::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)
396 403 {
404 Q_UNUSED(startPos);
405
397 406 if (isEditionEnabled()) {
398 407 impl->m_CurrentEditionMode = VisualizationSelectionZoneItemPrivate::EditionMode::NoEdition;
399 408 }
400 409 else {
401 410 event->ignore();
402 411 }
403 412
404 413 impl->m_AssociatedEditedZones.clear();
405 414 }
406 415
407 416 void VisualizationSelectionZoneItem::resizeLeft(double pixelDiff)
408 417 {
409 418 auto diff = impl->pixelSizeToAxisXSize(pixelDiff);
410 419 if (impl->m_MovedOrinalT1 <= impl->m_MovedOrinalT2) {
411 420 setStart(impl->m_MovedOrinalT1 + diff);
412 421 }
413 422 else {
414 423 setEnd(impl->m_MovedOrinalT2 + diff);
415 424 }
416 425 }
417 426
418 427 void VisualizationSelectionZoneItem::resizeRight(double pixelDiff)
419 428 {
420 429 auto diff = impl->pixelSizeToAxisXSize(pixelDiff);
421 430 if (impl->m_MovedOrinalT1 > impl->m_MovedOrinalT2) {
422 431 setStart(impl->m_MovedOrinalT1 + diff);
423 432 }
424 433 else {
425 434 setEnd(impl->m_MovedOrinalT2 + diff);
426 435 }
427 436 }
428 437
429 438 void VisualizationSelectionZoneItem::move(double pixelDiff)
430 439 {
431 440 auto diff = impl->pixelSizeToAxisXSize(pixelDiff);
432 441 setRange(impl->m_MovedOrinalT1 + diff, impl->m_MovedOrinalT2 + diff);
433 442 }
@@ -1,51 +1,51
1 1 #include "Visualization/VisualizationSelectionZoneManager.h"
2 2 #include "Visualization/VisualizationSelectionZoneItem.h"
3 3
4 4 struct VisualizationSelectionZoneManager::VisualizationSelectionZoneManagerPrivate {
5 5 QVector<VisualizationSelectionZoneItem *> m_SelectedItems;
6 6 };
7 7
8 8 VisualizationSelectionZoneManager::VisualizationSelectionZoneManager()
9 9 : impl{spimpl::make_unique_impl<VisualizationSelectionZoneManagerPrivate>()}
10 10 {
11 11 }
12 12
13 13 void VisualizationSelectionZoneManager::select(
14 14 const QVector<VisualizationSelectionZoneItem *> &items)
15 15 {
16 16 clearSelection();
17 17 for (auto item : items) {
18 18 setSelected(item, true);
19 19 }
20 20 }
21 21
22 22 void VisualizationSelectionZoneManager::setSelected(VisualizationSelectionZoneItem *item,
23 23 bool value)
24 24 {
25 25 if (value != item->selected()) {
26 26 item->setSelected(value);
27 27 item->parentPlot()->replot(QCustomPlot::rpQueuedReplot);
28 28 }
29 29
30 30 if (!value && impl->m_SelectedItems.contains(item)) {
31 31 impl->m_SelectedItems.removeAll(item);
32 32 }
33 33 else if (value) {
34 34 impl->m_SelectedItems << item;
35 35 }
36 36 }
37 37
38 38 void VisualizationSelectionZoneManager::clearSelection()
39 39 {
40 40 for (auto item : impl->m_SelectedItems) {
41 41 item->setSelected(false);
42 item->parentPlot()->replot();
42 item->parentPlot()->replot(QCustomPlot::rpQueuedReplot);
43 43 }
44 44
45 45 impl->m_SelectedItems.clear();
46 46 }
47 47
48 48 QVector<VisualizationSelectionZoneItem *> VisualizationSelectionZoneManager::selectedItems() const
49 49 {
50 50 return impl->m_SelectedItems;
51 51 }
General Comments 4
Under Review
author

Auto status change to "Under Review"

Approved

Status change > Approved

Approved

Status change > Approved

You need to be logged in to leave comments. Login now