##// END OF EJS Templates
Put the align actions in sub menus
trabillard -
r1118:f354146de80e
parent child
Show More
@@ -1,22 +1,27
1 1 #ifndef SCIQLOP_ACTIONSGUICONTROLLER_H
2 2 #define SCIQLOP_ACTIONSGUICONTROLLER_H
3 3
4 4 #include <Actions/SelectionZoneAction.h>
5 5 #include <Common/spimpl.h>
6 6
7 7 #include <memory>
8 8
9 9 class ActionsGuiController {
10 10 public:
11 11 ActionsGuiController();
12 12
13 13 std::shared_ptr<SelectionZoneAction>
14 14 addSectionZoneAction(const QString &name, SelectionZoneAction::ExecuteFunction function);
15
16 std::shared_ptr<SelectionZoneAction>
17 addSectionZoneAction(const QStringList &subMenuList, const QString &name,
18 SelectionZoneAction::ExecuteFunction function);
19
15 20 QVector<std::shared_ptr<SelectionZoneAction> > selectionZoneActions() const;
16 21
17 22 private:
18 23 class ActionsGuiControllerPrivate;
19 24 spimpl::unique_impl_ptr<ActionsGuiControllerPrivate> impl;
20 25 };
21 26
22 27 #endif // SCIQLOP_ACTIONSGUICONTROLLER_H
@@ -1,63 +1,75
1 1 #ifndef SCIQLOP_SELECTIONZONEACTION_H
2 2 #define SCIQLOP_SELECTIONZONEACTION_H
3 3
4 4 #include <Common/spimpl.h>
5 5
6 6 #include <QLoggingCategory>
7 7 #include <QObject>
8 8
9 9 #include <functional>
10 10
11 11 class VisualizationSelectionZoneItem;
12 12
13 13 Q_DECLARE_LOGGING_CATEGORY(LOG_SelectionZoneAction)
14 14
15 15 /**
16 16 * @brief The SelectionZoneAction class represents an action on a selection zone in the
17 17 * visualization.
18 18 *
19 19 * The action is a function that will be executed when the slot execute() is called.
20 20 */
21 21 class SelectionZoneAction : public QObject {
22 22
23 23 Q_OBJECT
24 24
25 25 public:
26 26 /// Signature of the function associated to the action
27 27 using ExecuteFunction
28 28 = std::function<void(const QVector<VisualizationSelectionZoneItem *> &item)>;
29 29
30 30 using EnableFunction
31 31 = std::function<bool(const QVector<VisualizationSelectionZoneItem *> &item)>;
32 32
33 33 /**
34 34 * @param name the name of the action, displayed to the user
35 35 * @param fun the function that will be called when the action is executed
36 36 * @sa execute()
37 37 */
38 38 explicit SelectionZoneAction(const QString &name, ExecuteFunction fun);
39 39
40 /**
41 * @param name the name of the action, displayed to the user
42 * @param subMenusList the list of sub menus where the action should be inserted
43 * @param fun the function that will be called when the action is executed
44 * @sa execute()
45 */
46 explicit SelectionZoneAction(const QStringList &subMenuList, const QString &name,
47 ExecuteFunction fun);
48
40 49 /// Sets the function which determine if the action should be enabled or disabled
41 50 void setEnableFunction(EnableFunction fun);
42 51
43 52 /// Sets the shortcut displayed by the action.
44 53 /// Note: The shortcut is only displayed and not active because it is not permanently stored
45 54 void setDisplayedShortcut(const QKeySequence &shortcut);
46 55 QKeySequence displayedShortcut() const;
47 56
48 57 /// The name of the action
49 58 QString name() const noexcept;
50 59
60 /// The path in the sub menus, if any
61 QStringList subMenuList() const noexcept;
62
51 63 public slots:
52 64 /// Executes the action
53 65 void execute(const QVector<VisualizationSelectionZoneItem *> &item);
54 66
55 67 /// Returns true if the action is enabled
56 68 bool isEnabled(const QVector<VisualizationSelectionZoneItem *> &item);
57 69
58 70 private:
59 71 class SelectionZoneActionPrivate;
60 72 spimpl::unique_impl_ptr<SelectionZoneActionPrivate> impl;
61 73 };
62 74
63 75 #endif // SCIQLOP_SELECTIONZONEACTION_H
@@ -1,26 +1,36
1 1 #include "Actions/ActionsGuiController.h"
2 2
3 3 struct ActionsGuiController::ActionsGuiControllerPrivate {
4 4
5 5 QVector<std::shared_ptr<SelectionZoneAction> > m_SelectionZoneActions;
6 6 };
7 7
8 8 ActionsGuiController::ActionsGuiController()
9 9 : impl{spimpl::make_unique_impl<ActionsGuiControllerPrivate>()}
10 10 {
11 11 }
12 12
13 13 std::shared_ptr<SelectionZoneAction>
14 14 ActionsGuiController::addSectionZoneAction(const QString &name,
15 15 SelectionZoneAction::ExecuteFunction function)
16 16 {
17 17 auto action = std::make_shared<SelectionZoneAction>(name, function);
18 18 impl->m_SelectionZoneActions.push_back(action);
19 19
20 20 return action;
21 21 }
22 22
23 std::shared_ptr<SelectionZoneAction>
24 ActionsGuiController::addSectionZoneAction(const QStringList &subMenuList, const QString &name,
25 SelectionZoneAction::ExecuteFunction function)
26 {
27 auto action = std::make_shared<SelectionZoneAction>(subMenuList, name, function);
28 impl->m_SelectionZoneActions.push_back(action);
29
30 return action;
31 }
32
23 33 QVector<std::shared_ptr<SelectionZoneAction> > ActionsGuiController::selectionZoneActions() const
24 34 {
25 35 return impl->m_SelectionZoneActions;
26 36 }
@@ -1,52 +1,66
1 1 #include <Actions/SelectionZoneAction.h>
2 2 #include <Visualization/VisualizationSelectionZoneItem.h>
3 3
4 4 Q_LOGGING_CATEGORY(LOG_SelectionZoneAction, "SelectionZoneAction")
5 5
6 6 struct SelectionZoneAction::SelectionZoneActionPrivate {
7 explicit SelectionZoneActionPrivate(const QString &name,
7 explicit SelectionZoneActionPrivate(const QString &name, const QStringList &subMenuList,
8 8 SelectionZoneAction::ExecuteFunction fun)
9 : m_Name{name}, m_Fun{std::move(fun)}
9 : m_Name{name}, m_SubMenuList{subMenuList}, m_Fun{std::move(fun)}
10 10 {
11 11 }
12 12
13 13 QString m_Name;
14 QStringList m_SubMenuList;
14 15 QKeySequence m_DisplayedShortcut;
15 16 SelectionZoneAction::ExecuteFunction m_Fun;
16 17 SelectionZoneAction::EnableFunction m_EnableFun = [](auto zones) { return true; };
17 18 };
18 19
19 20 SelectionZoneAction::SelectionZoneAction(const QString &name, ExecuteFunction fun)
20 : impl{spimpl::make_unique_impl<SelectionZoneActionPrivate>(name, std::move(fun))}
21 : impl{spimpl::make_unique_impl<SelectionZoneActionPrivate>(name, QStringList{},
22 std::move(fun))}
23 {
24 }
25
26 SelectionZoneAction::SelectionZoneAction(const QStringList &subMenuList, const QString &name,
27 SelectionZoneAction::ExecuteFunction fun)
28 : impl{spimpl::make_unique_impl<SelectionZoneActionPrivate>(name, subMenuList,
29 std::move(fun))}
21 30 {
22 31 }
23 32
24 33 void SelectionZoneAction::setEnableFunction(EnableFunction fun)
25 34 {
26 35 impl->m_EnableFun = std::move(fun);
27 36 }
28 37
29 38 void SelectionZoneAction::setDisplayedShortcut(const QKeySequence &shortcut)
30 39 {
31 40 impl->m_DisplayedShortcut = shortcut;
32 41 }
33 42
34 43 QKeySequence SelectionZoneAction::displayedShortcut() const
35 44 {
36 45 return impl->m_DisplayedShortcut;
37 46 }
38 47
39 48 QString SelectionZoneAction::name() const noexcept
40 49 {
41 50 return impl->m_Name;
42 51 }
43 52
53 QStringList SelectionZoneAction::subMenuList() const noexcept
54 {
55 return impl->m_SubMenuList;
56 }
57
44 58 void SelectionZoneAction::execute(const QVector<VisualizationSelectionZoneItem *> &item)
45 59 {
46 60 impl->m_Fun(item);
47 61 }
48 62
49 63 bool SelectionZoneAction::isEnabled(const QVector<VisualizationSelectionZoneItem *> &item)
50 64 {
51 65 return impl->m_EnableFun(item);
52 66 }
@@ -1,109 +1,109
1 1 #include "Visualization/VisualizationActionManager.h"
2 2 #include "Visualization/VisualizationGraphWidget.h"
3 3 #include "Visualization/VisualizationSelectionZoneItem.h"
4 4
5 5 #include <Actions/ActionsGuiController.h>
6 6 #include <SqpApplication.h>
7 7
8 8 VisualizationActionManager::VisualizationActionManager() {}
9 9
10 10 void VisualizationActionManager::installSelectionZoneActions()
11 11 {
12 12 auto &actionController = sqpApp->actionsGuiController();
13 13
14 14 auto removeZonesAction
15 15 = actionController.addSectionZoneAction("Remove Selected Zone(s)", [](auto zones) {
16 16 for (auto selectionZone : zones) {
17 17 if (auto graph = selectionZone->parentGraphWidget()) {
18 18 graph->removeSelectionZone(selectionZone);
19 19 }
20 20 }
21 21 });
22 22 removeZonesAction->setDisplayedShortcut(QKeySequence::Delete);
23 23
24 24 auto alignEnableFuntion = [](auto items) { return items.count() > 1; };
25 25
26 26 // Vertical alignment actions
27 auto alignLeftAction
28 = actionController.addSectionZoneAction("Align Vertically / Left", [](auto zones) {
29 Q_ASSERT(zones.count() > 1);
30 auto ref = zones.takeFirst();
31 ref->alignZonesVerticallyOnLeft(zones, false);
32 });
27 auto alignLeftAction = actionController.addSectionZoneAction(
28 QStringList{"Align Vertically"}, "Left", [](auto zones) {
29 Q_ASSERT(zones.count() > 1);
30 auto ref = zones.takeFirst();
31 ref->alignZonesVerticallyOnLeft(zones, false);
32 });
33 33 alignLeftAction->setEnableFunction(alignEnableFuntion);
34 34
35 auto alignLeftBorderAction
36 = actionController.addSectionZoneAction("Align Vertically / Left Borders", [](auto zones) {
37 Q_ASSERT(zones.count() > 1);
38 auto ref = zones.takeFirst();
39 ref->alignZonesVerticallyOnLeft(zones, true);
40 });
35 auto alignLeftBorderAction = actionController.addSectionZoneAction(
36 QStringList{"Align Vertically"}, "Left Borders", [](auto zones) {
37 Q_ASSERT(zones.count() > 1);
38 auto ref = zones.takeFirst();
39 ref->alignZonesVerticallyOnLeft(zones, true);
40 });
41 41 alignLeftBorderAction->setEnableFunction(alignEnableFuntion);
42 42
43 auto alignRightAction
44 = actionController.addSectionZoneAction("Align Vertically / Right", [](auto zones) {
45 Q_ASSERT(zones.count() > 1);
46 auto ref = zones.takeFirst();
47 ref->alignZonesVerticallyOnRight(zones, false);
48 });
43 auto alignRightAction = actionController.addSectionZoneAction(
44 QStringList{"Align Vertically"}, "Right", [](auto zones) {
45 Q_ASSERT(zones.count() > 1);
46 auto ref = zones.takeFirst();
47 ref->alignZonesVerticallyOnRight(zones, false);
48 });
49 49 alignRightAction->setEnableFunction(alignEnableFuntion);
50 50
51 auto alignRightBorderAction
52 = actionController.addSectionZoneAction("Align Vertically / Right Borders", [](auto zones) {
53 Q_ASSERT(zones.count() > 1);
54 auto ref = zones.takeFirst();
55 ref->alignZonesVerticallyOnRight(zones, true);
56 });
51 auto alignRightBorderAction = actionController.addSectionZoneAction(
52 QStringList{"Align Vertically"}, "Right Borders", [](auto zones) {
53 Q_ASSERT(zones.count() > 1);
54 auto ref = zones.takeFirst();
55 ref->alignZonesVerticallyOnRight(zones, true);
56 });
57 57 alignRightBorderAction->setEnableFunction(alignEnableFuntion);
58 58
59 59 auto alignLeftAndRightAction = actionController.addSectionZoneAction(
60 "Align Vertically / Left and Right", [](auto zones) {
60 QStringList{"Align Vertically"}, "Left and Right", [](auto zones) {
61 61 Q_ASSERT(zones.count() > 1);
62 62 auto ref = zones.takeFirst();
63 63 ref->alignZonesVerticallyOnLeft(zones, false);
64 64 ref->alignZonesVerticallyOnRight(zones, true);
65 65 });
66 66 alignLeftAndRightAction->setEnableFunction(alignEnableFuntion);
67 67
68 68 // Temporal alignment actions
69 auto alignLeftTemporallyAction
70 = actionController.addSectionZoneAction("Align Temporally / Left", [](auto zones) {
71 Q_ASSERT(zones.count() > 1);
72 auto ref = zones.takeFirst();
73 ref->alignZonesTemporallyOnLeft(zones, false);
74 });
69 auto alignLeftTemporallyAction = actionController.addSectionZoneAction(
70 QStringList{"Align Temporally"}, "Left", [](auto zones) {
71 Q_ASSERT(zones.count() > 1);
72 auto ref = zones.takeFirst();
73 ref->alignZonesTemporallyOnLeft(zones, false);
74 });
75 75 alignLeftTemporallyAction->setEnableFunction(alignEnableFuntion);
76 76
77 auto alignLeftBorderTemporallyAction
78 = actionController.addSectionZoneAction("Align Temporally / Left Borders", [](auto zones) {
79 Q_ASSERT(zones.count() > 1);
80 auto ref = zones.takeFirst();
81 ref->alignZonesTemporallyOnLeft(zones, true);
82 });
77 auto alignLeftBorderTemporallyAction = actionController.addSectionZoneAction(
78 QStringList{"Align Temporally"}, "Left Borders", [](auto zones) {
79 Q_ASSERT(zones.count() > 1);
80 auto ref = zones.takeFirst();
81 ref->alignZonesTemporallyOnLeft(zones, true);
82 });
83 83 alignLeftBorderTemporallyAction->setEnableFunction(alignEnableFuntion);
84 84
85 auto alignRightTemporallyAction
86 = actionController.addSectionZoneAction("Align Temporally / Right", [](auto zones) {
87 Q_ASSERT(zones.count() > 1);
88 auto ref = zones.takeFirst();
89 ref->alignZonesTemporallyOnRight(zones, false);
90 });
85 auto alignRightTemporallyAction = actionController.addSectionZoneAction(
86 QStringList{"Align Temporally"}, "Right", [](auto zones) {
87 Q_ASSERT(zones.count() > 1);
88 auto ref = zones.takeFirst();
89 ref->alignZonesTemporallyOnRight(zones, false);
90 });
91 91 alignRightTemporallyAction->setEnableFunction(alignEnableFuntion);
92 92
93 auto alignRightBorderTemporallyAction
94 = actionController.addSectionZoneAction("Align Temporally / Right Borders", [](auto zones) {
95 Q_ASSERT(zones.count() > 1);
96 auto ref = zones.takeFirst();
97 ref->alignZonesTemporallyOnRight(zones, true);
98 });
93 auto alignRightBorderTemporallyAction = actionController.addSectionZoneAction(
94 QStringList{"Align Temporally"}, "Right Borders", [](auto zones) {
95 Q_ASSERT(zones.count() > 1);
96 auto ref = zones.takeFirst();
97 ref->alignZonesTemporallyOnRight(zones, true);
98 });
99 99 alignRightBorderTemporallyAction->setEnableFunction(alignEnableFuntion);
100 100
101 101 auto alignLeftAndRightTemporallyAction = actionController.addSectionZoneAction(
102 "Align Temporally / Left and Right", [](auto zones) {
102 QStringList{"Align Temporally"}, "Left and Right", [](auto zones) {
103 103 Q_ASSERT(zones.count() > 1);
104 104 auto ref = zones.takeFirst();
105 105 ref->alignZonesTemporallyOnLeft(zones, false);
106 106 ref->alignZonesTemporallyOnRight(zones, true);
107 107 });
108 108 alignLeftAndRightTemporallyAction->setEnableFunction(alignEnableFuntion);
109 109 }
@@ -1,911 +1,938
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 7 #include "Visualization/VisualizationSelectionZoneItem.h"
8 8 #include "Visualization/VisualizationSelectionZoneManager.h"
9 9 #include "Visualization/VisualizationWidget.h"
10 10 #include "Visualization/VisualizationZoneWidget.h"
11 11 #include "ui_VisualizationGraphWidget.h"
12 12
13 13 #include <Actions/ActionsGuiController.h>
14 14 #include <Common/MimeTypesDef.h>
15 15 #include <Data/ArrayData.h>
16 16 #include <Data/IDataSeries.h>
17 17 #include <Data/SpectrogramSeries.h>
18 18 #include <DragAndDrop/DragDropGuiController.h>
19 19 #include <Settings/SqpSettingsDefs.h>
20 20 #include <SqpApplication.h>
21 21 #include <Time/TimeController.h>
22 22 #include <Variable/Variable.h>
23 23 #include <Variable/VariableController.h>
24 24
25 25 #include <unordered_map>
26 26
27 27 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
28 28
29 29 namespace {
30 30
31 31 /// Key pressed to enable drag&drop in all modes
32 32 const auto DRAG_DROP_MODIFIER = Qt::AltModifier;
33 33
34 34 /// Key pressed to enable zoom on horizontal axis
35 35 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier;
36 36
37 37 /// Key pressed to enable zoom on vertical axis
38 38 const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier;
39 39
40 40 /// Speed of a step of a wheel event for a pan, in percentage of the axis range
41 41 const auto PAN_SPEED = 5;
42 42
43 43 /// Key pressed to enable a calibration pan
44 44 const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier;
45 45
46 46 /// Key pressed to enable multi selection of selection zones
47 47 const auto MULTI_ZONE_SELECTION_MODIFIER = Qt::ControlModifier;
48 48
49 49 /// Minimum size for the zoom box, in percentage of the axis range
50 50 const auto ZOOM_BOX_MIN_SIZE = 0.8;
51 51
52 52 /// Format of the dates appearing in the label of a cursor
53 53 const auto CURSOR_LABELS_DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd\nhh:mm:ss:zzz");
54 54
55 55 } // namespace
56 56
57 57 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
58 58
59 59 explicit VisualizationGraphWidgetPrivate(const QString &name)
60 60 : m_Name{name},
61 61 m_DoAcquisition{true},
62 62 m_IsCalibration{false},
63 63 m_RenderingDelegate{nullptr}
64 64 {
65 65 }
66 66
67 67 void updateData(PlottablesMap &plottables, std::shared_ptr<IDataSeries> dataSeries,
68 68 const SqpRange &range)
69 69 {
70 70 VisualizationGraphHelper::updateData(plottables, dataSeries, range);
71 71
72 72 // Prevents that data has changed to update rendering
73 73 m_RenderingDelegate->onPlotUpdated();
74 74 }
75 75
76 76 QString m_Name;
77 77 // 1 variable -> n qcpplot
78 78 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
79 79 bool m_DoAcquisition;
80 80 bool m_IsCalibration;
81 81 /// Delegate used to attach rendering features to the plot
82 82 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
83 83
84 84 QCPItemRect *m_DrawingZoomRect = nullptr;
85 85 QStack<QPair<QCPRange, QCPRange> > m_ZoomStack;
86 86
87 87 std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr;
88 88 std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr;
89 89
90 90 VisualizationSelectionZoneItem *m_DrawingZone = nullptr;
91 91 VisualizationSelectionZoneItem *m_HoveredZone = nullptr;
92 92 QVector<VisualizationSelectionZoneItem *> m_SelectionZones;
93 93
94 94 bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even
95 95
96 96 void startDrawingRect(const QPoint &pos, QCustomPlot &plot)
97 97 {
98 98 removeDrawingRect(plot);
99 99
100 100 auto axisPos = posToAxisPos(pos, plot);
101 101
102 102 m_DrawingZoomRect = new QCPItemRect{&plot};
103 103 QPen p;
104 104 p.setWidth(2);
105 105 m_DrawingZoomRect->setPen(p);
106 106
107 107 m_DrawingZoomRect->topLeft->setCoords(axisPos);
108 108 m_DrawingZoomRect->bottomRight->setCoords(axisPos);
109 109 }
110 110
111 111 void removeDrawingRect(QCustomPlot &plot)
112 112 {
113 113 if (m_DrawingZoomRect) {
114 114 plot.removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot
115 115 m_DrawingZoomRect = nullptr;
116 116 plot.replot(QCustomPlot::rpQueuedReplot);
117 117 }
118 118 }
119 119
120 120 void startDrawingZone(const QPoint &pos, VisualizationGraphWidget *graph)
121 121 {
122 122 endDrawingZone(graph);
123 123
124 124 auto axisPos = posToAxisPos(pos, graph->plot());
125 125
126 126 m_DrawingZone = new VisualizationSelectionZoneItem{&graph->plot()};
127 127 m_DrawingZone->setRange(axisPos.x(), axisPos.x());
128 128 m_DrawingZone->setEditionEnabled(false);
129 129 }
130 130
131 131 void endDrawingZone(VisualizationGraphWidget *graph)
132 132 {
133 133 if (m_DrawingZone) {
134 134 auto drawingZoneRange = m_DrawingZone->range();
135 135 if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0) {
136 136 m_DrawingZone->setEditionEnabled(true);
137 137 addSelectionZone(m_DrawingZone);
138 138 }
139 139 else {
140 140 graph->plot().removeItem(m_DrawingZone); // the item is deleted by QCustomPlot
141 141 }
142 142
143 143 graph->plot().replot(QCustomPlot::rpQueuedReplot);
144 144 m_DrawingZone = nullptr;
145 145 }
146 146 }
147 147
148 148 void setSelectionZonesEditionEnabled(bool value)
149 149 {
150 150 for (auto s : m_SelectionZones) {
151 151 s->setEditionEnabled(value);
152 152 }
153 153 }
154 154
155 155 void addSelectionZone(VisualizationSelectionZoneItem *zone) { m_SelectionZones << zone; }
156 156
157 157 VisualizationSelectionZoneItem *selectionZoneAt(const QPoint &pos,
158 158 const QCustomPlot &plot) const
159 159 {
160 160 VisualizationSelectionZoneItem *selectionZoneItemUnderCursor = nullptr;
161 161 auto minDistanceToZone = -1;
162 162 for (auto zone : m_SelectionZones) {
163 163 auto distanceToZone = zone->selectTest(pos, false);
164 164 if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone)
165 165 && distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
166 166 selectionZoneItemUnderCursor = zone;
167 167 }
168 168 }
169 169
170 170 return selectionZoneItemUnderCursor;
171 171 }
172 172
173 173 QPointF posToAxisPos(const QPoint &pos, QCustomPlot &plot) const
174 174 {
175 175 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
176 176 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
177 177 return QPointF{axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y())};
178 178 }
179 179
180 180 bool pointIsInAxisRect(const QPointF &axisPoint, QCustomPlot &plot) const
181 181 {
182 182 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
183 183 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
184 184 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
185 185 }
186 186 };
187 187
188 188 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
189 189 : VisualizationDragWidget{parent},
190 190 ui{new Ui::VisualizationGraphWidget},
191 191 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
192 192 {
193 193 ui->setupUi(this);
194 194
195 195 // 'Close' options : widget is deleted when closed
196 196 setAttribute(Qt::WA_DeleteOnClose);
197 197
198 198 // Set qcpplot properties :
199 199 // - zoom is enabled
200 200 // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation
201 201 ui->widget->setInteractions(QCP::iRangeZoom);
202 202 ui->widget->axisRect()->setRangeDrag(Qt::Horizontal | Qt::Vertical);
203 203
204 204 // The delegate must be initialized after the ui as it uses the plot
205 205 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
206 206
207 207 // Init the cursors
208 208 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
209 209 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
210 210 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
211 211 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
212 212
213 213 connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress);
214 214 connect(ui->widget, &QCustomPlot::mouseRelease, this,
215 215 &VisualizationGraphWidget::onMouseRelease);
216 216 connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove);
217 217 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
218 218 connect(ui->widget, &QCustomPlot::mouseDoubleClick, this,
219 219 &VisualizationGraphWidget::onMouseDoubleClick);
220 220 connect(
221 221 ui->widget->xAxis,
222 222 static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(&QCPAxis::rangeChanged),
223 223 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
224 224
225 225 // Activates menu when right clicking on the graph
226 226 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
227 227 connect(ui->widget, &QCustomPlot::customContextMenuRequested, this,
228 228 &VisualizationGraphWidget::onGraphMenuRequested);
229 229
230 230 connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
231 231 &VariableController::onRequestDataLoading);
232 232
233 233 connect(&sqpApp->variableController(), &VariableController::updateVarDisplaying, this,
234 234 &VisualizationGraphWidget::onUpdateVarDisplaying);
235 235
236 236 #ifdef Q_OS_MAC
237 237 plot().setPlottingHint(QCP::phFastPolylines, true);
238 238 #endif
239 239 }
240 240
241 241
242 242 VisualizationGraphWidget::~VisualizationGraphWidget()
243 243 {
244 244 delete ui;
245 245 }
246 246
247 247 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
248 248 {
249 249 auto parent = parentWidget();
250 250 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
251 251 parent = parent->parentWidget();
252 252 }
253 253
254 254 return qobject_cast<VisualizationZoneWidget *>(parent);
255 255 }
256 256
257 257 VisualizationWidget *VisualizationGraphWidget::parentVisualizationWidget() const
258 258 {
259 259 auto parent = parentWidget();
260 260 while (parent != nullptr && !qobject_cast<VisualizationWidget *>(parent)) {
261 261 parent = parent->parentWidget();
262 262 }
263 263
264 264 return qobject_cast<VisualizationWidget *>(parent);
265 265 }
266 266
267 267 void VisualizationGraphWidget::enableAcquisition(bool enable)
268 268 {
269 269 impl->m_DoAcquisition = enable;
270 270 }
271 271
272 272 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, SqpRange range)
273 273 {
274 274 // Uses delegate to create the qcpplot components according to the variable
275 275 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
276 276
277 277 if (auto dataSeries = variable->dataSeries()) {
278 278 // Set axes properties according to the units of the data series
279 279 impl->m_RenderingDelegate->setAxesProperties(dataSeries);
280 280
281 281 // Sets rendering properties for the new plottables
282 282 // Warning: this method must be called after setAxesProperties(), as it can access to some
283 283 // axes properties that have to be initialized
284 284 impl->m_RenderingDelegate->setPlottablesProperties(dataSeries, createdPlottables);
285 285 }
286 286
287 287 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
288 288
289 289 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
290 290
291 291 this->enableAcquisition(false);
292 292 this->setGraphRange(range);
293 293 this->enableAcquisition(true);
294 294
295 295 emit requestDataLoading(QVector<std::shared_ptr<Variable> >() << variable, range, false);
296 296
297 297 emit variableAdded(variable);
298 298 }
299 299
300 300 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
301 301 {
302 302 // Each component associated to the variable :
303 303 // - is removed from qcpplot (which deletes it)
304 304 // - is no longer referenced in the map
305 305 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
306 306 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
307 307 emit variableAboutToBeRemoved(variable);
308 308
309 309 auto &plottablesMap = variableIt->second;
310 310
311 311 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
312 312 plottableIt != plottableEnd;) {
313 313 ui->widget->removePlottable(plottableIt->second);
314 314 plottableIt = plottablesMap.erase(plottableIt);
315 315 }
316 316
317 317 impl->m_VariableToPlotMultiMap.erase(variableIt);
318 318 }
319 319
320 320 // Updates graph
321 321 ui->widget->replot();
322 322 }
323 323
324 324 QList<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
325 325 {
326 326 auto variables = QList<std::shared_ptr<Variable> >{};
327 327 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
328 328 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
329 329 variables << it->first;
330 330 }
331 331
332 332 return variables;
333 333 }
334 334
335 335 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
336 336 {
337 337 if (!variable) {
338 338 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
339 339 return;
340 340 }
341 341
342 342 VisualizationGraphHelper::setYAxisRange(variable, *ui->widget);
343 343 }
344 344
345 345 SqpRange VisualizationGraphWidget::graphRange() const noexcept
346 346 {
347 347 auto graphRange = ui->widget->xAxis->range();
348 348 return SqpRange{graphRange.lower, graphRange.upper};
349 349 }
350 350
351 351 void VisualizationGraphWidget::setGraphRange(const SqpRange &range)
352 352 {
353 353 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange START");
354 354 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
355 355 ui->widget->replot();
356 356 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange END");
357 357 }
358 358
359 359 QVector<SqpRange> VisualizationGraphWidget::selectionZoneRanges() const
360 360 {
361 361 QVector<SqpRange> ranges;
362 362 for (auto zone : impl->m_SelectionZones) {
363 363 ranges << zone->range();
364 364 }
365 365
366 366 return ranges;
367 367 }
368 368
369 369 void VisualizationGraphWidget::addSelectionZones(const QVector<SqpRange> &ranges)
370 370 {
371 371 for (const auto &range : ranges) {
372 372 // note: ownership is transfered to QCustomPlot
373 373 auto zone = new VisualizationSelectionZoneItem(&plot());
374 374 zone->setRange(range.m_TStart, range.m_TEnd);
375 375 impl->addSelectionZone(zone);
376 376 }
377 377
378 378 plot().replot(QCustomPlot::rpQueuedReplot);
379 379 }
380 380
381 381 void VisualizationGraphWidget::removeSelectionZone(VisualizationSelectionZoneItem *selectionZone)
382 382 {
383 383 parentVisualizationWidget()->selectionZoneManager().setSelected(selectionZone, false);
384 384
385 385 if (impl->m_HoveredZone == selectionZone) {
386 386 impl->m_HoveredZone = nullptr;
387 387 setCursor(Qt::ArrowCursor);
388 388 }
389 389
390 390 impl->m_SelectionZones.removeAll(selectionZone);
391 391 plot().removeItem(selectionZone);
392 392 plot().replot(QCustomPlot::rpQueuedReplot);
393 393 }
394 394
395 395 void VisualizationGraphWidget::undoZoom()
396 396 {
397 397 auto zoom = impl->m_ZoomStack.pop();
398 398 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
399 399 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
400 400
401 401 axisX->setRange(zoom.first);
402 402 axisY->setRange(zoom.second);
403 403
404 404 plot().replot(QCustomPlot::rpQueuedReplot);
405 405 }
406 406
407 407 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
408 408 {
409 409 if (visitor) {
410 410 visitor->visit(this);
411 411 }
412 412 else {
413 413 qCCritical(LOG_VisualizationGraphWidget())
414 414 << tr("Can't visit widget : the visitor is null");
415 415 }
416 416 }
417 417
418 418 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
419 419 {
420 420 auto isSpectrogram = [](const auto &variable) {
421 421 return std::dynamic_pointer_cast<SpectrogramSeries>(variable.dataSeries()) != nullptr;
422 422 };
423 423
424 424 // - A spectrogram series can't be dropped on graph with existing plottables
425 425 // - No data series can be dropped on graph with existing spectrogram series
426 426 return isSpectrogram(variable)
427 427 ? impl->m_VariableToPlotMultiMap.empty()
428 428 : std::none_of(
429 429 impl->m_VariableToPlotMultiMap.cbegin(), impl->m_VariableToPlotMultiMap.cend(),
430 430 [isSpectrogram](const auto &entry) { return isSpectrogram(*entry.first); });
431 431 }
432 432
433 433 bool VisualizationGraphWidget::contains(const Variable &variable) const
434 434 {
435 435 // Finds the variable among the keys of the map
436 436 auto variablePtr = &variable;
437 437 auto findVariable
438 438 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
439 439
440 440 auto end = impl->m_VariableToPlotMultiMap.cend();
441 441 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
442 442 return it != end;
443 443 }
444 444
445 445 QString VisualizationGraphWidget::name() const
446 446 {
447 447 return impl->m_Name;
448 448 }
449 449
450 450 QMimeData *VisualizationGraphWidget::mimeData(const QPoint &position) const
451 451 {
452 452 auto mimeData = new QMimeData;
453 453
454 454 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position, plot());
455 455 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
456 456 && selectionZoneItemUnderCursor) {
457 457 mimeData->setData(MIME_TYPE_TIME_RANGE, TimeController::mimeDataForTimeRange(
458 458 selectionZoneItemUnderCursor->range()));
459 459 mimeData->setData(MIME_TYPE_SELECTION_ZONE, TimeController::mimeDataForTimeRange(
460 460 selectionZoneItemUnderCursor->range()));
461 461 }
462 462 else {
463 463 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
464 464
465 465 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
466 466 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
467 467 }
468 468
469 469 return mimeData;
470 470 }
471 471
472 472 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint &dragPosition)
473 473 {
474 474 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition, plot());
475 475 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
476 476 && selectionZoneItemUnderCursor) {
477 477
478 478 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
479 479 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
480 480
481 481 auto zoneSize = QSizeF{qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
482 482 qAbs(zoneBottomRight.y() - zoneTopLeft.y())}
483 483 .toSize();
484 484
485 485 auto pixmap = QPixmap(zoneSize);
486 486 render(&pixmap, QPoint(), QRegion{QRect{zoneTopLeft.toPoint(), zoneSize}});
487 487
488 488 return pixmap;
489 489 }
490 490
491 491 return QPixmap();
492 492 }
493 493
494 494 bool VisualizationGraphWidget::isDragAllowed() const
495 495 {
496 496 return true;
497 497 }
498 498
499 499 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
500 500 {
501 501 if (highlighted) {
502 502 plot().setBackground(QBrush(QColor("#BBD5EE")));
503 503 }
504 504 else {
505 505 plot().setBackground(QBrush(Qt::white));
506 506 }
507 507
508 508 plot().update();
509 509 }
510 510
511 511 void VisualizationGraphWidget::addVerticalCursor(double time)
512 512 {
513 513 impl->m_VerticalCursor->setPosition(time);
514 514 impl->m_VerticalCursor->setVisible(true);
515 515
516 516 auto text
517 517 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
518 518 impl->m_VerticalCursor->setLabelText(text);
519 519 }
520 520
521 521 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
522 522 {
523 523 impl->m_VerticalCursor->setAbsolutePosition(position);
524 524 impl->m_VerticalCursor->setVisible(true);
525 525
526 526 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
527 527 auto text
528 528 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
529 529 impl->m_VerticalCursor->setLabelText(text);
530 530 }
531 531
532 532 void VisualizationGraphWidget::removeVerticalCursor()
533 533 {
534 534 impl->m_VerticalCursor->setVisible(false);
535 535 plot().replot(QCustomPlot::rpQueuedReplot);
536 536 }
537 537
538 538 void VisualizationGraphWidget::addHorizontalCursor(double value)
539 539 {
540 540 impl->m_HorizontalCursor->setPosition(value);
541 541 impl->m_HorizontalCursor->setVisible(true);
542 542 impl->m_HorizontalCursor->setLabelText(QString::number(value));
543 543 }
544 544
545 545 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
546 546 {
547 547 impl->m_HorizontalCursor->setAbsolutePosition(position);
548 548 impl->m_HorizontalCursor->setVisible(true);
549 549
550 550 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
551 551 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
552 552 }
553 553
554 554 void VisualizationGraphWidget::removeHorizontalCursor()
555 555 {
556 556 impl->m_HorizontalCursor->setVisible(false);
557 557 plot().replot(QCustomPlot::rpQueuedReplot);
558 558 }
559 559
560 560 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
561 561 {
562 562 Q_UNUSED(event);
563 563
564 564 // Prevents that all variables will be removed from graph when it will be closed
565 565 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
566 566 emit variableAboutToBeRemoved(variableEntry.first);
567 567 }
568 568 }
569 569
570 570 void VisualizationGraphWidget::enterEvent(QEvent *event)
571 571 {
572 572 Q_UNUSED(event);
573 573 impl->m_RenderingDelegate->showGraphOverlay(true);
574 574 }
575 575
576 576 void VisualizationGraphWidget::leaveEvent(QEvent *event)
577 577 {
578 578 Q_UNUSED(event);
579 579 impl->m_RenderingDelegate->showGraphOverlay(false);
580 580
581 581 if (auto parentZone = parentZoneWidget()) {
582 582 parentZone->notifyMouseLeaveGraph(this);
583 583 }
584 584 else {
585 585 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
586 586 }
587 587
588 588 if (impl->m_HoveredZone) {
589 589 impl->m_HoveredZone->setHovered(false);
590 590 impl->m_HoveredZone = nullptr;
591 591 }
592 592 }
593 593
594 594 QCustomPlot &VisualizationGraphWidget::plot() const noexcept
595 595 {
596 596 return *ui->widget;
597 597 }
598 598
599 599 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
600 600 {
601 601 QMenu graphMenu{};
602 602
603 603 // Iterates on variables (unique keys)
604 604 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
605 605 end = impl->m_VariableToPlotMultiMap.cend();
606 606 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
607 607 // 'Remove variable' action
608 608 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
609 609 [ this, var = it->first ]() { removeVariable(var); });
610 610 }
611 611
612 612 if (!impl->m_ZoomStack.isEmpty()) {
613 613 if (!graphMenu.isEmpty()) {
614 614 graphMenu.addSeparator();
615 615 }
616 616
617 617 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
618 618 }
619 619
620 // Selection Zone Actions
620 621 auto selectionZoneItem = impl->selectionZoneAt(pos, plot());
621 622 if (selectionZoneItem) {
622 623 auto selectedItems = parentVisualizationWidget()->selectionZoneManager().selectedItems();
623 624 selectedItems.removeAll(selectionZoneItem);
624 625 selectedItems.prepend(selectionZoneItem); // Put the current selection zone first
625 626
626 627 auto zoneActions = sqpApp->actionsGuiController().selectionZoneActions();
627 628 if (!zoneActions.isEmpty() && !graphMenu.isEmpty()) {
628 629 graphMenu.addSeparator();
629 630 }
630 631
632 QHash<QString, QMenu *> subMenus;
633 QHash<QString, bool> subMenusEnabled;
634
631 635 for (auto zoneAction : zoneActions) {
632 auto action = graphMenu.addAction(zoneAction->name());
633 action->setEnabled(zoneAction->isEnabled(selectedItems));
636
637 auto isEnabled = zoneAction->isEnabled(selectedItems);
638
639 auto menu = &graphMenu;
640 for (auto subMenuName : zoneAction->subMenuList()) {
641 if (!subMenus.contains(subMenuName)) {
642 menu = menu->addMenu(subMenuName);
643 subMenus[subMenuName] = menu;
644 subMenusEnabled[subMenuName] = isEnabled;
645 }
646 else {
647 menu = subMenus.value(subMenuName);
648 if (isEnabled) {
649 // The sub menu is enabled if at least one of its actions is enabled
650 subMenusEnabled[subMenuName] = true;
651 }
652 }
653 }
654
655 auto action = menu->addAction(zoneAction->name());
656 action->setEnabled(isEnabled);
634 657 action->setShortcut(zoneAction->displayedShortcut());
635 658 QObject::connect(action, &QAction::triggered,
636 659 [zoneAction, selectedItems]() { zoneAction->execute(selectedItems); });
637 660 }
661
662 for (auto it = subMenus.cbegin(); it != subMenus.cend(); ++it) {
663 it.value()->setEnabled(subMenusEnabled[it.key()]);
664 }
638 665 }
639 666
640 667 if (!graphMenu.isEmpty()) {
641 668 graphMenu.exec(QCursor::pos());
642 669 }
643 670 }
644 671
645 672 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
646 673 {
647 674 qCDebug(LOG_VisualizationGraphWidget())
648 675 << tr("TORM: VisualizationGraphWidget::onRangeChanged")
649 676 << QThread::currentThread()->objectName() << "DoAcqui" << impl->m_DoAcquisition;
650 677
651 678 auto graphRange = SqpRange{t1.lower, t1.upper};
652 679 auto oldGraphRange = SqpRange{t2.lower, t2.upper};
653 680
654 681 if (impl->m_DoAcquisition) {
655 682 QVector<std::shared_ptr<Variable> > variableUnderGraphVector;
656 683
657 684 for (auto it = impl->m_VariableToPlotMultiMap.begin(),
658 685 end = impl->m_VariableToPlotMultiMap.end();
659 686 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
660 687 variableUnderGraphVector.push_back(it->first);
661 688 }
662 689 emit requestDataLoading(std::move(variableUnderGraphVector), graphRange,
663 690 !impl->m_IsCalibration);
664 691
665 692 if (!impl->m_IsCalibration) {
666 693 qCDebug(LOG_VisualizationGraphWidget())
667 694 << tr("TORM: VisualizationGraphWidget::Synchronize notify !!")
668 695 << QThread::currentThread()->objectName() << graphRange << oldGraphRange;
669 696 emit synchronize(graphRange, oldGraphRange);
670 697 }
671 698 }
672 699
673 700 auto pos = mapFromGlobal(QCursor::pos());
674 701 auto axisPos = impl->posToAxisPos(pos, plot());
675 702 if (auto parentZone = parentZoneWidget()) {
676 703 if (impl->pointIsInAxisRect(axisPos, plot())) {
677 704 parentZone->notifyMouseMoveInGraph(pos, axisPos, this);
678 705 }
679 706 else {
680 707 parentZone->notifyMouseLeaveGraph(this);
681 708 }
682 709 }
683 710 else {
684 711 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
685 712 }
686 713 }
687 714
688 715 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept
689 716 {
690 717 impl->m_RenderingDelegate->onMouseDoubleClick(event);
691 718 }
692 719
693 720 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
694 721 {
695 722 // Handles plot rendering when mouse is moving
696 723 impl->m_RenderingDelegate->onMouseMove(event);
697 724
698 725 auto axisPos = impl->posToAxisPos(event->pos(), plot());
699 726
700 727 // Zoom box and zone drawing
701 728 if (impl->m_DrawingZoomRect) {
702 729 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
703 730 }
704 731 else if (impl->m_DrawingZone) {
705 732 impl->m_DrawingZone->setEnd(axisPos.x());
706 733 }
707 734
708 735 // Cursor
709 736 if (auto parentZone = parentZoneWidget()) {
710 737 if (impl->pointIsInAxisRect(axisPos, plot())) {
711 738 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
712 739 }
713 740 else {
714 741 parentZone->notifyMouseLeaveGraph(this);
715 742 }
716 743 }
717 744 else {
718 745 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
719 746 }
720 747
721 748 // Search for the selection zone under the mouse
722 749 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
723 750 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
724 751 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) {
725 752
726 753 // Sets the appropriate cursor shape
727 754 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
728 755 setCursor(cursorShape);
729 756
730 757 // Manages the hovered zone
731 758 if (selectionZoneItemUnderCursor != impl->m_HoveredZone) {
732 759 if (impl->m_HoveredZone) {
733 760 impl->m_HoveredZone->setHovered(false);
734 761 }
735 762 selectionZoneItemUnderCursor->setHovered(true);
736 763 impl->m_HoveredZone = selectionZoneItemUnderCursor;
737 764 plot().replot(QCustomPlot::rpQueuedReplot);
738 765 }
739 766 }
740 767 else {
741 768 // There is no zone under the mouse or the interaction mode is not "selection zones"
742 769 if (impl->m_HoveredZone) {
743 770 impl->m_HoveredZone->setHovered(false);
744 771 impl->m_HoveredZone = nullptr;
745 772 }
746 773
747 774 setCursor(Qt::ArrowCursor);
748 775 }
749 776
750 777 impl->m_HasMovedMouse = true;
751 778 VisualizationDragWidget::mouseMoveEvent(event);
752 779 }
753 780
754 781 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
755 782 {
756 783 auto value = event->angleDelta().x() + event->angleDelta().y();
757 784 if (value != 0) {
758 785
759 786 auto direction = value > 0 ? 1.0 : -1.0;
760 787 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
761 788 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
762 789 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
763 790
764 791 auto zoomOrientations = QFlags<Qt::Orientation>{};
765 792 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
766 793 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
767 794
768 795 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
769 796
770 797 if (!isZoomX && !isZoomY) {
771 798 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
772 799 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
773 800
774 801 axis->setRange(axis->range() + diff);
775 802
776 803 if (plot().noAntialiasingOnDrag()) {
777 804 plot().setNotAntialiasedElements(QCP::aeAll);
778 805 }
779 806
780 807 plot().replot(QCustomPlot::rpQueuedReplot);
781 808 }
782 809 }
783 810 }
784 811
785 812 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
786 813 {
787 814 auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
788 815 auto isSelectionZoneMode
789 816 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
790 817 auto isLeftClick = event->buttons().testFlag(Qt::LeftButton);
791 818
792 819 if (!isDragDropClick && isLeftClick) {
793 820 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) {
794 821 // Starts a zoom box
795 822 impl->startDrawingRect(event->pos(), plot());
796 823 }
797 824 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr) {
798 825 // Starts a new selection zone
799 826 auto zoneAtPos = impl->selectionZoneAt(event->pos(), plot());
800 827 if (!zoneAtPos) {
801 828 impl->startDrawingZone(event->pos(), this);
802 829 }
803 830 }
804 831 }
805 832
806 833 // Allows mouse panning only in default mode
807 834 plot().setInteraction(QCP::iRangeDrag, sqpApp->plotsInteractionMode()
808 835 == SqpApplication::PlotsInteractionMode::None
809 836 && !isDragDropClick);
810 837
811 838 // Allows zone edition only in selection zone mode without drag&drop
812 839 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
813 840
814 841 // Selection / Deselection
815 842 if (isSelectionZoneMode) {
816 843 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
817 844 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
818 845 if (selectionZoneItemUnderCursor && isLeftClick) {
819 846 selectionZoneItemUnderCursor->setAssociatedEditedZones(
820 847 parentVisualizationWidget()->selectionZoneManager().selectedItems());
821 848 }
822 849 else if (!isMultiSelectionClick && isLeftClick) {
823 850 parentVisualizationWidget()->selectionZoneManager().clearSelection();
824 851 }
825 852 else {
826 853 // No selection change
827 854 }
828 855 }
829 856
830 857
831 858 impl->m_HasMovedMouse = false;
832 859 VisualizationDragWidget::mousePressEvent(event);
833 860 }
834 861
835 862 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
836 863 {
837 864 if (impl->m_DrawingZoomRect) {
838 865
839 866 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
840 867 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
841 868
842 869 auto newAxisXRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().x(),
843 870 impl->m_DrawingZoomRect->bottomRight->coords().x()};
844 871
845 872 auto newAxisYRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().y(),
846 873 impl->m_DrawingZoomRect->bottomRight->coords().y()};
847 874
848 875 impl->removeDrawingRect(plot());
849 876
850 877 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
851 878 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
852 879 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
853 880 axisX->setRange(newAxisXRange);
854 881 axisY->setRange(newAxisYRange);
855 882
856 883 plot().replot(QCustomPlot::rpQueuedReplot);
857 884 }
858 885 }
859 886
860 887 impl->endDrawingZone(this);
861 888
862 889 impl->m_IsCalibration = false;
863 890
864 891 // Selection / Deselection
865 892 auto isSelectionZoneMode
866 893 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
867 894 if (isSelectionZoneMode) {
868 895 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
869 896 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
870 897 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton) {
871 898 if (!isMultiSelectionClick && !impl->m_HasMovedMouse) {
872 899 parentVisualizationWidget()->selectionZoneManager().select(
873 900 {selectionZoneItemUnderCursor});
874 901 }
875 902 else if (!impl->m_HasMovedMouse) {
876 903 parentVisualizationWidget()->selectionZoneManager().setSelected(
877 904 selectionZoneItemUnderCursor, !selectionZoneItemUnderCursor->selected()
878 905 || event->button() == Qt::RightButton);
879 906 }
880 907 }
881 908 else {
882 909 // No selection change
883 910 }
884 911 }
885 912 }
886 913
887 914 void VisualizationGraphWidget::onDataCacheVariableUpdated()
888 915 {
889 916 auto graphRange = ui->widget->xAxis->range();
890 917 auto dateTime = SqpRange{graphRange.lower, graphRange.upper};
891 918
892 919 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
893 920 auto variable = variableEntry.first;
894 921 qCDebug(LOG_VisualizationGraphWidget())
895 922 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
896 923 qCDebug(LOG_VisualizationGraphWidget())
897 924 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
898 925 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
899 926 impl->updateData(variableEntry.second, variable->dataSeries(), variable->range());
900 927 }
901 928 }
902 929 }
903 930
904 931 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
905 932 const SqpRange &range)
906 933 {
907 934 auto it = impl->m_VariableToPlotMultiMap.find(variable);
908 935 if (it != impl->m_VariableToPlotMultiMap.end()) {
909 936 impl->updateData(it->second, variable->dataSeries(), range);
910 937 }
911 938 }
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