##// END OF EJS Templates
Refac + clang-format...
jeandet -
r1365:19075a214d07
parent child
Show More
@@ -1,26 +1,26
1 ---
1 ---
2 # See http://clang.llvm.org/docs/ClangFormatStyleOptions.html for a definition
2 # See http://clang.llvm.org/docs/ClangFormatStyleOptions.html for a definition
3 # of the options
3 # of the options
4 Language: Cpp
4 Language: Cpp
5 BasedOnStyle: LLVM
5 BasedOnStyle: WebKit
6
6
7 # Line length
7 # Line length
8 ColumnLimit: 100
8 ColumnLimit: 100
9
9
10 # Indent with 4 spaces
10 # Indent with 4 spaces
11 IndentWidth: 4
11 IndentWidth: 4
12 AccessModifierOffset: -4 # -IndentWidth
12 AccessModifierOffset: -4 # -IndentWidth
13 ConstructorInitializerIndentWidth: 8 # 2 * IndentWidth
13 ConstructorInitializerIndentWidth: 8 # 2 * IndentWidth
14
14
15 # Break only before function braces
15 BreakBeforeBraces: Allman
16 BreakBeforeBraces: Stroustrup
17
16
18 AllowShortFunctionsOnASingleLine: Inline
17 AllowShortFunctionsOnASingleLine: Inline
19 AlwaysBreakTemplateDeclarations: true
18 AlwaysBreakTemplateDeclarations: true
20 AlwaysBreakBeforeMultilineStrings: true
19 AlwaysBreakBeforeMultilineStrings: true
21 BreakBeforeBinaryOperators: true
20 BreakBeforeBinaryOperators: true
22 ConstructorInitializerAllOnOneLineOrOnePerLine: true
21 ConstructorInitializerAllOnOneLineOrOnePerLine: true
23 IndentCaseLabels: true
22 IndentCaseLabels: true
24 MaxEmptyLinesToKeep: 2
23 MaxEmptyLinesToKeep: 2
25 Standard: Cpp03
24 Standard: Cpp11
25 UseTab: Never
26
26
@@ -1,84 +1,84
1 cmake_minimum_required(VERSION 3.6)
1 cmake_minimum_required(VERSION 3.6)
2 project(SciQLOP CXX)
2 project(SciQLOP CXX)
3
3
4 include(GNUInstallDirs)
4 include(GNUInstallDirs)
5
5
6 SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_LIST_DIR}/cmake")
6 SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_LIST_DIR}/cmake")
7
7
8 OPTION (CPPCHECK "Analyzes the source code with cppcheck" OFF)
8 OPTION (CPPCHECK "Analyzes the source code with cppcheck" OFF)
9 OPTION (CLANG_TIDY "Analyzes the source code with Clang Tidy" OFF)
9 OPTION (CLANG_TIDY "Analyzes the source code with Clang Tidy" OFF)
10 OPTION (IWYU "Analyzes the source code with Include What You Use" OFF)
10 OPTION (IWYU "Analyzes the source code with Include What You Use" OFF)
11 OPTION (Coverage "Enables code coverage" OFF)
11 OPTION (Coverage "Enables code coverage" OFF)
12
12
13 set(CMAKE_CXX_STANDARD 17)
13 set(CMAKE_CXX_STANDARD 17)
14
14
15 set(CMAKE_AUTOMOC ON)
15 set(CMAKE_AUTOMOC ON)
16 #https://gitlab.kitware.com/cmake/cmake/issues/15227
16 #https://gitlab.kitware.com/cmake/cmake/issues/15227
17 #set(CMAKE_AUTOUIC ON)
17 #set(CMAKE_AUTOUIC ON)
18 if(POLICY CMP0071)
18 if(POLICY CMP0071)
19 cmake_policy(SET CMP0071 OLD)
19 cmake_policy(SET CMP0071 OLD)
20 endif()
20 endif()
21 set(CMAKE_AUTORCC ON)
21 set(CMAKE_AUTORCC ON)
22 set(CMAKE_INCLUDE_CURRENT_DIR ON)
22 set(CMAKE_INCLUDE_CURRENT_DIR ON)
23
23
24 if(NOT DEFINED CMAKE_INSTALL_RPATH_USE_LINK_PATH)
24 if(NOT DEFINED CMAKE_INSTALL_RPATH_USE_LINK_PATH)
25 set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
25 set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
26 endif()
26 endif()
27 if(NOT DEFINED CMAKE_MACOSX_RPATH)
27 if(NOT DEFINED CMAKE_MACOSX_RPATH)
28 set(CMAKE_MACOSX_RPATH TRUE)
28 set(CMAKE_MACOSX_RPATH TRUE)
29 endif()
29 endif()
30
30
31 if(NOT CMAKE_BUILD_TYPE)
31 if(NOT CMAKE_BUILD_TYPE)
32 set(CMAKE_BUILD_TYPE "Release" CACHE STRING "" FORCE)
32 set(CMAKE_BUILD_TYPE "Release" CACHE STRING "" FORCE)
33 endif()
33 endif()
34 set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0")
34 set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -g3")
35
35
36 find_package(Qt5 COMPONENTS Core Widgets Network PrintSupport Svg Test REQUIRED)
36 find_package(Qt5 COMPONENTS Core Widgets Network PrintSupport Svg Test REQUIRED)
37
37
38 IF(CPPCHECK)
38 IF(CPPCHECK)
39 set(CMAKE_CXX_CPPCHECK "cppcheck;--enable=warning,style")
39 set(CMAKE_CXX_CPPCHECK "cppcheck;--enable=warning,style")
40 ENDIF(CPPCHECK)
40 ENDIF(CPPCHECK)
41
41
42 IF(CLANG_TIDY)
42 IF(CLANG_TIDY)
43 set(CMAKE_CXX_CLANG_TIDY "clang-tidy;-style=file;-checks=*")
43 set(CMAKE_CXX_CLANG_TIDY "clang-tidy;-style=file;-checks=*")
44 ENDIF(CLANG_TIDY)
44 ENDIF(CLANG_TIDY)
45
45
46 IF(IWYU)
46 IF(IWYU)
47 set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE "include-what-you-use")
47 set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE "include-what-you-use")
48 ENDIF(IWYU)
48 ENDIF(IWYU)
49
49
50 IF(Coverage)
50 IF(Coverage)
51 set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -g -O0 -Wall -W -Wshadow -Wunused-variable -Wunused-parameter -Wunused-function -Wunused -Wno-system-headers -Wno-deprecated -Woverloaded-virtual -Wwrite-strings -fprofile-arcs -ftest-coverage")
51 set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -g -O0 -Wall -W -Wshadow -Wunused-variable -Wunused-parameter -Wunused-function -Wunused -Wno-system-headers -Wno-deprecated -Woverloaded-virtual -Wwrite-strings -fprofile-arcs -ftest-coverage")
52 set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g -O0 -Wall -W -Wshadow -Wunused-variable \
52 set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g -O0 -Wall -W -Wshadow -Wunused-variable \
53 -Wunused-parameter -Wunused-function -Wunused -Wno-system-headers \
53 -Wunused-parameter -Wunused-function -Wunused -Wno-system-headers \
54 -Wno-deprecated -Woverloaded-virtual -Wwrite-strings -fprofile-arcs -ftest-coverage")
54 -Wno-deprecated -Woverloaded-virtual -Wwrite-strings -fprofile-arcs -ftest-coverage")
55 set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fprofile-arcs -ftest-coverage")
55 set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fprofile-arcs -ftest-coverage")
56
56
57 add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/gcov.html
57 add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/gcov.html
58 COMMAND gcovr --exclude='.*Test.*' --exclude='.*external.*' --object-directory ${CMAKE_BINARY_DIR} -r ${CMAKE_SOURCE_DIR} --html --html-details -o ${CMAKE_CURRENT_BINARY_DIR}/gcov.html
58 COMMAND gcovr --exclude='.*Test.*' --exclude='.*external.*' --object-directory ${CMAKE_BINARY_DIR} -r ${CMAKE_SOURCE_DIR} --html --html-details -o ${CMAKE_CURRENT_BINARY_DIR}/gcov.html
59 )
59 )
60 add_custom_target(gcovr
60 add_custom_target(gcovr
61 DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/gcov.html gcovr
61 DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/gcov.html gcovr
62 )
62 )
63 add_custom_target(show_coverage
63 add_custom_target(show_coverage
64 COMMAND xdg-open ${CMAKE_CURRENT_BINARY_DIR}/gcov.html
64 COMMAND xdg-open ${CMAKE_CURRENT_BINARY_DIR}/gcov.html
65 DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/gcov.html gcovr
65 DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/gcov.html gcovr
66 )
66 )
67 ENDIF(Coverage)
67 ENDIF(Coverage)
68
68
69 enable_testing()
69 enable_testing()
70
70
71 find_package(SciQLOPCore CONFIG QUIET)
71 find_package(SciQLOPCore CONFIG QUIET)
72 if (NOT SciQLOPCore_FOUND)
72 if (NOT SciQLOPCore_FOUND)
73 if(NOT IS_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/core)
73 if(NOT IS_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/core)
74 message("Init submodule Core")
74 message("Init submodule Core")
75 execute_process(COMMAND git submodule init core WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
75 execute_process(COMMAND git submodule init core WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
76 execute_process(COMMAND git submodule update core WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
76 execute_process(COMMAND git submodule update core WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
77 endif()
77 endif()
78 add_subdirectory(core)
78 add_subdirectory(core)
79 endif()
79 endif()
80
80
81 add_subdirectory(gui)
81 add_subdirectory(gui)
82 add_subdirectory(app)
82 add_subdirectory(app)
83 add_subdirectory(plugins)
83 add_subdirectory(plugins)
84 #add_subdirectory(docs)
84 #add_subdirectory(docs)
@@ -1,1286 +1,1319
1 #include "Visualization/VisualizationGraphWidget.h"
1 #include "Visualization/VisualizationGraphWidget.h"
2 #include "Visualization/IVisualizationWidgetVisitor.h"
2 #include "Visualization/IVisualizationWidgetVisitor.h"
3 #include "Visualization/VisualizationCursorItem.h"
3 #include "Visualization/VisualizationCursorItem.h"
4 #include "Visualization/VisualizationDefs.h"
4 #include "Visualization/VisualizationDefs.h"
5 #include "Visualization/VisualizationGraphHelper.h"
5 #include "Visualization/VisualizationGraphHelper.h"
6 #include "Visualization/VisualizationGraphRenderingDelegate.h"
6 #include "Visualization/VisualizationGraphRenderingDelegate.h"
7 #include "Visualization/VisualizationMultiZoneSelectionDialog.h"
7 #include "Visualization/VisualizationMultiZoneSelectionDialog.h"
8 #include "Visualization/VisualizationSelectionZoneItem.h"
8 #include "Visualization/VisualizationSelectionZoneItem.h"
9 #include "Visualization/VisualizationSelectionZoneManager.h"
9 #include "Visualization/VisualizationSelectionZoneManager.h"
10 #include "Visualization/VisualizationWidget.h"
10 #include "Visualization/VisualizationWidget.h"
11 #include "Visualization/VisualizationZoneWidget.h"
11 #include "Visualization/VisualizationZoneWidget.h"
12 #include "ui_VisualizationGraphWidget.h"
12 #include "ui_VisualizationGraphWidget.h"
13
13
14 #include <Actions/ActionsGuiController.h>
14 #include <Actions/ActionsGuiController.h>
15 #include <Actions/FilteringAction.h>
15 #include <Actions/FilteringAction.h>
16 #include <Common/MimeTypesDef.h>
16 #include <Common/MimeTypesDef.h>
17 #include <Data/ArrayData.h>
17 #include <Data/ArrayData.h>
18 #include <Data/IDataSeries.h>
18 #include <Data/IDataSeries.h>
19 #include <Data/SpectrogramSeries.h>
19 #include <Data/SpectrogramSeries.h>
20 #include <DragAndDrop/DragDropGuiController.h>
20 #include <DragAndDrop/DragDropGuiController.h>
21 #include <Settings/SqpSettingsDefs.h>
21 #include <Settings/SqpSettingsDefs.h>
22 #include <SqpApplication.h>
22 #include <SqpApplication.h>
23 #include <Time/TimeController.h>
23 #include <Time/TimeController.h>
24 #include <Variable/Variable.h>
24 #include <Variable/Variable.h>
25 #include <Variable/VariableController2.h>
25 #include <Variable/VariableController2.h>
26
26
27 #include <unordered_map>
27 #include <unordered_map>
28
28
29 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
29 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
30
30
31 namespace {
31 namespace {
32
32
33 /// Key pressed to enable drag&drop in all modes
33 /// Key pressed to enable drag&drop in all modes
34 const auto DRAG_DROP_MODIFIER = Qt::AltModifier;
34 const auto DRAG_DROP_MODIFIER = Qt::AltModifier;
35
35
36 /// Key pressed to enable zoom on horizontal axis
36 /// Key pressed to enable zoom on horizontal axis
37 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier;
37 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier;
38
38
39 /// Key pressed to enable zoom on vertical axis
39 /// Key pressed to enable zoom on vertical axis
40 const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier;
40 const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier;
41
41
42 /// Speed of a step of a wheel event for a pan, in percentage of the axis range
42 /// Speed of a step of a wheel event for a pan, in percentage of the axis range
43 const auto PAN_SPEED = 5;
43 const auto PAN_SPEED = 5;
44
44
45 /// Key pressed to enable a calibration pan
45 /// Key pressed to enable a calibration pan
46 const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier;
46 const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier;
47
47
48 /// Key pressed to enable multi selection of selection zones
48 /// Key pressed to enable multi selection of selection zones
49 const auto MULTI_ZONE_SELECTION_MODIFIER = Qt::ControlModifier;
49 const auto MULTI_ZONE_SELECTION_MODIFIER = Qt::ControlModifier;
50
50
51 /// Minimum size for the zoom box, in percentage of the axis range
51 /// Minimum size for the zoom box, in percentage of the axis range
52 const auto ZOOM_BOX_MIN_SIZE = 0.8;
52 const auto ZOOM_BOX_MIN_SIZE = 0.8;
53
53
54 /// Format of the dates appearing in the label of a cursor
54 /// Format of the dates appearing in the label of a cursor
55 const auto CURSOR_LABELS_DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd\nhh:mm:ss:zzz");
55 const auto CURSOR_LABELS_DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd\nhh:mm:ss:zzz");
56
56
57 } // namespace
57 } // namespace
58
58
59 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
59 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
60
60
61 explicit VisualizationGraphWidgetPrivate(const QString &name)
61 explicit VisualizationGraphWidgetPrivate(const QString &name)
62 : m_Name{name},
62 : m_Name{name},
63 m_Flags{GraphFlag::EnableAll},
63 m_Flags{GraphFlag::EnableAll},
64 m_IsCalibration{false},
64 m_IsCalibration{false},
65 m_RenderingDelegate{nullptr}
65 m_RenderingDelegate{nullptr}
66 {
66 {
67 m_plot = new QCustomPlot();
67 m_plot = new QCustomPlot();
68 // Necessary for all platform since Qt::AA_EnableHighDpiScaling is enable.
68 // Necessary for all platform since Qt::AA_EnableHighDpiScaling is enable.
69 m_plot->setPlottingHint(QCP::phFastPolylines, true);
69 m_plot->setPlottingHint(QCP::phFastPolylines, true);
70 }
70 }
71
71
72 void updateData(PlottablesMap &plottables, std::shared_ptr<Variable> variable,
72 void updateData(PlottablesMap &plottables, std::shared_ptr<Variable> variable,
73 const DateTimeRange &range)
73 const DateTimeRange &range)
74 {
74 {
75 VisualizationGraphHelper::updateData(plottables, variable, range);
75 VisualizationGraphHelper::updateData(plottables, variable, range);
76
76
77 // Prevents that data has changed to update rendering
77 // Prevents that data has changed to update rendering
78 m_RenderingDelegate->onPlotUpdated();
78 m_RenderingDelegate->onPlotUpdated();
79 }
79 }
80
80
81 QString m_Name;
81 QString m_Name;
82 // 1 variable -> n qcpplot
82 // 1 variable -> n qcpplot
83 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
83 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
84 GraphFlags m_Flags;
84 GraphFlags m_Flags;
85 bool m_IsCalibration;
85 bool m_IsCalibration;
86 QCustomPlot* m_plot;
86 QCustomPlot* m_plot;
87 QPoint m_lastMousePos;
87 QPoint m_lastMousePos;
88 /// Delegate used to attach rendering features to the plot
88 /// Delegate used to attach rendering features to the plot
89 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
89 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
90
90
91 QCPItemRect* m_DrawingZoomRect = nullptr;
91 QCPItemRect* m_DrawingZoomRect = nullptr;
92 QStack<QPair<QCPRange, QCPRange> > m_ZoomStack;
92 QStack<QPair<QCPRange, QCPRange> > m_ZoomStack;
93
93
94 std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr;
94 std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr;
95 std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr;
95 std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr;
96
96
97 VisualizationSelectionZoneItem *m_DrawingZone = nullptr;
97 VisualizationSelectionZoneItem *m_DrawingZone = nullptr;
98 VisualizationSelectionZoneItem *m_HoveredZone = nullptr;
98 VisualizationSelectionZoneItem *m_HoveredZone = nullptr;
99 QVector<VisualizationSelectionZoneItem *> m_SelectionZones;
99 QVector<VisualizationSelectionZoneItem *> m_SelectionZones;
100
100
101 bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even
101 bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even
102
102
103 bool m_VariableAutoRangeOnInit = true;
103 bool m_VariableAutoRangeOnInit = true;
104
104
105 inline void updateMousePosition(const QPoint& position)
105 inline void updateMousePosition(const QPoint& position)
106 {
106 {
107 m_lastMousePos = position;
107 m_lastMousePos = position;
108 }
108 }
109
109
110 inline bool isDrawingZoomRect(){return m_DrawingZoomRect!=nullptr;}
110 inline bool isDrawingZoomRect(){return m_DrawingZoomRect!=nullptr;}
111 void updateZoomRect(const QPoint& newPos)
111 void updateZoomRect(const QPoint& newPos)
112 {
112 {
113 QPointF pos{m_plot->xAxis->pixelToCoord(newPos.x()), m_plot->yAxis->pixelToCoord(newPos.y())};
113 QPointF pos{m_plot->xAxis->pixelToCoord(newPos.x()), m_plot->yAxis->pixelToCoord(newPos.y())};
114 m_DrawingZoomRect->bottomRight->setCoords(pos);
114 m_DrawingZoomRect->bottomRight->setCoords(pos);
115 m_plot->replot(QCustomPlot::rpQueuedReplot);
115 m_plot->replot(QCustomPlot::rpQueuedReplot);
116 }
116 }
117
117
118 void applyZoomRect()
118 void applyZoomRect()
119 {
119 {
120 auto axisX = m_plot->axisRect()->axis(QCPAxis::atBottom);
120 auto axisX = m_plot->axisRect()->axis(QCPAxis::atBottom);
121 auto axisY = m_plot->axisRect()->axis(QCPAxis::atLeft);
121 auto axisY = m_plot->axisRect()->axis(QCPAxis::atLeft);
122
122
123 auto newAxisXRange = QCPRange{m_DrawingZoomRect->topLeft->coords().x(),
123 auto newAxisXRange = QCPRange{m_DrawingZoomRect->topLeft->coords().x(),
124 m_DrawingZoomRect->bottomRight->coords().x()};
124 m_DrawingZoomRect->bottomRight->coords().x()};
125
125
126 auto newAxisYRange = QCPRange{m_DrawingZoomRect->topLeft->coords().y(),
126 auto newAxisYRange = QCPRange{m_DrawingZoomRect->topLeft->coords().y(),
127 m_DrawingZoomRect->bottomRight->coords().y()};
127 m_DrawingZoomRect->bottomRight->coords().y()};
128
128
129 removeDrawingRect();
129 removeDrawingRect();
130
130
131 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
131 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
132 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
132 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
133 m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
133 m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
134 axisX->setRange(newAxisXRange);
134 axisX->setRange(newAxisXRange);
135 axisY->setRange(newAxisYRange);
135 axisY->setRange(newAxisYRange);
136
136
137 m_plot->replot(QCustomPlot::rpQueuedReplot);
137 m_plot->replot(QCustomPlot::rpQueuedReplot);
138 }
138 }
139 }
139 }
140
140
141 inline bool isDrawingZoneRect(){return m_DrawingZone!=nullptr;}
141 inline bool isDrawingZoneRect(){return m_DrawingZone!=nullptr;}
142 void updateZoneRect(const QPoint& newPos)
142 void updateZoneRect(const QPoint& newPos)
143 {
143 {
144 m_DrawingZone->setEnd(m_plot->xAxis->pixelToCoord(newPos.x()));
144 m_DrawingZone->setEnd(m_plot->xAxis->pixelToCoord(newPos.x()));
145 m_plot->replot(QCustomPlot::rpQueuedReplot);
145 m_plot->replot(QCustomPlot::rpQueuedReplot);
146 }
146 }
147
147
148 void startDrawingRect(const QPoint &pos)
148 void startDrawingRect(const QPoint &pos)
149 {
149 {
150 removeDrawingRect();
150 removeDrawingRect();
151
151
152 auto axisPos = posToAxisPos(pos);
152 auto axisPos = posToAxisPos(pos);
153
153
154 m_DrawingZoomRect = new QCPItemRect{m_plot};
154 m_DrawingZoomRect = new QCPItemRect{m_plot};
155 QPen p;
155 QPen p;
156 p.setWidth(2);
156 p.setWidth(2);
157 m_DrawingZoomRect->setPen(p);
157 m_DrawingZoomRect->setPen(p);
158
158
159 m_DrawingZoomRect->topLeft->setCoords(axisPos);
159 m_DrawingZoomRect->topLeft->setCoords(axisPos);
160 m_DrawingZoomRect->bottomRight->setCoords(axisPos);
160 m_DrawingZoomRect->bottomRight->setCoords(axisPos);
161 }
161 }
162
162
163 void removeDrawingRect()
163 void removeDrawingRect()
164 {
164 {
165 if (m_DrawingZoomRect) {
165 if (m_DrawingZoomRect) {
166 m_plot->removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot
166 m_plot->removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot
167 m_DrawingZoomRect = nullptr;
167 m_DrawingZoomRect = nullptr;
168 m_plot->replot(QCustomPlot::rpQueuedReplot);
168 m_plot->replot(QCustomPlot::rpQueuedReplot);
169 }
169 }
170 }
170 }
171
171
172 void startDrawingZone(const QPoint &pos, VisualizationGraphWidget *graph)
172 void selectZone(const QPoint &pos)
173 {
173 {
174 endDrawingZone(graph);
174 auto zoneAtPos = selectionZoneAt(pos);
175 setSelectionZonesEditionEnabled(sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones);
176 }
177
178 void startDrawingZone(const QPoint &pos)
179 {
180 endDrawingZone();
175
181
176 auto axisPos = posToAxisPos(pos);
182 auto axisPos = posToAxisPos(pos);
177
183
178 m_DrawingZone = new VisualizationSelectionZoneItem{m_plot};
184 m_DrawingZone = new VisualizationSelectionZoneItem{m_plot};
179 m_DrawingZone->setRange(axisPos.x(), axisPos.x());
185 m_DrawingZone->setRange(axisPos.x(), axisPos.x());
180 m_DrawingZone->setEditionEnabled(false);
186 m_DrawingZone->setEditionEnabled(false);
181 }
187 }
182
188
183 void endDrawingZone(VisualizationGraphWidget *graph)
189 void endDrawingZone()
184 {
190 {
185 if (m_DrawingZone) {
191 if (m_DrawingZone) {
186 auto drawingZoneRange = m_DrawingZone->range();
192 auto drawingZoneRange = m_DrawingZone->range();
187 if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0) {
193 if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0) {
188 m_DrawingZone->setEditionEnabled(true);
194 m_DrawingZone->setEditionEnabled(true);
189 addSelectionZone(m_DrawingZone);
195 addSelectionZone(m_DrawingZone);
190 }
196 }
191 else {
197 else {
192 graph->plot().removeItem(m_DrawingZone); // the item is deleted by QCustomPlot
198 m_plot->removeItem(m_DrawingZone);
193 }
199 }
194
200
195 graph->plot().replot(QCustomPlot::rpQueuedReplot);
201 m_plot->replot(QCustomPlot::rpQueuedReplot);
196 m_DrawingZone = nullptr;
202 m_DrawingZone = nullptr;
197 }
203 }
198 }
204 }
199
205
206 void moveSelectionZone(const QPoint& destination)
207 {
208 /*
209 * I give up on this for now
210 * @TODO implement this, the difficulty is that selection zones have their own
211 * event handling code which seems to rely on QCP GUI event handling propagation
212 * which was a realy bad design choice.
213 */
214 }
215
200 void setSelectionZonesEditionEnabled(bool value)
216 void setSelectionZonesEditionEnabled(bool value)
201 {
217 {
202 for (auto s : m_SelectionZones) {
218 for (auto s : m_SelectionZones) {
203 s->setEditionEnabled(value);
219 s->setEditionEnabled(value);
204 }
220 }
205 }
221 }
206
222
207 void addSelectionZone(VisualizationSelectionZoneItem *zone) { m_SelectionZones << zone; }
223 void addSelectionZone(VisualizationSelectionZoneItem *zone) { m_SelectionZones << zone; }
208
224
209 VisualizationSelectionZoneItem *selectionZoneAt(const QPoint &pos) const
225 VisualizationSelectionZoneItem *selectionZoneAt(const QPoint &pos) const
210 {
226 {
211 VisualizationSelectionZoneItem *selectionZoneItemUnderCursor = nullptr;
227 VisualizationSelectionZoneItem *selectionZoneItemUnderCursor = nullptr;
212 auto minDistanceToZone = -1;
228 auto minDistanceToZone = -1;
213 for (auto zone : m_SelectionZones) {
229 for (auto zone : m_SelectionZones) {
214 auto distanceToZone = zone->selectTest(pos, false);
230 auto distanceToZone = zone->selectTest(pos, false);
215 if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone)
231 if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone)
216 && distanceToZone >= 0 && distanceToZone < m_plot->selectionTolerance()) {
232 && distanceToZone >= 0 && distanceToZone < m_plot->selectionTolerance()) {
217 selectionZoneItemUnderCursor = zone;
233 selectionZoneItemUnderCursor = zone;
218 }
234 }
219 }
235 }
220
236
221 return selectionZoneItemUnderCursor;
237 return selectionZoneItemUnderCursor;
222 }
238 }
223
239
224 QVector<VisualizationSelectionZoneItem *> selectionZonesAt(const QPoint &pos,
240 QVector<VisualizationSelectionZoneItem *> selectionZonesAt(const QPoint &pos,
225 const QCustomPlot &plot) const
241 const QCustomPlot &plot) const
226 {
242 {
227 QVector<VisualizationSelectionZoneItem *> zones;
243 QVector<VisualizationSelectionZoneItem *> zones;
228 for (auto zone : m_SelectionZones) {
244 for (auto zone : m_SelectionZones) {
229 auto distanceToZone = zone->selectTest(pos, false);
245 auto distanceToZone = zone->selectTest(pos, false);
230 if (distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
246 if (distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
231 zones << zone;
247 zones << zone;
232 }
248 }
233 }
249 }
234
250
235 return zones;
251 return zones;
236 }
252 }
237
253
238 void moveSelectionZoneOnTop(VisualizationSelectionZoneItem *zone, QCustomPlot &plot)
254 void moveSelectionZoneOnTop(VisualizationSelectionZoneItem *zone, QCustomPlot &plot)
239 {
255 {
240 if (!m_SelectionZones.isEmpty() && m_SelectionZones.last() != zone) {
256 if (!m_SelectionZones.isEmpty() && m_SelectionZones.last() != zone) {
241 zone->moveToTop();
257 zone->moveToTop();
242 m_SelectionZones.removeAll(zone);
258 m_SelectionZones.removeAll(zone);
243 m_SelectionZones.append(zone);
259 m_SelectionZones.append(zone);
244 }
260 }
245 }
261 }
246
262
247 QPointF posToAxisPos(const QPoint &pos) const
263 QPointF posToAxisPos(const QPoint &pos) const
248 {
264 {
249 auto axisX = m_plot->axisRect()->axis(QCPAxis::atBottom);
265 auto axisX = m_plot->axisRect()->axis(QCPAxis::atBottom);
250 auto axisY = m_plot->axisRect()->axis(QCPAxis::atLeft);
266 auto axisY = m_plot->axisRect()->axis(QCPAxis::atLeft);
251 return QPointF{axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y())};
267 return QPointF{axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y())};
252 }
268 }
253
269
254 bool pointIsInAxisRect(const QPointF &axisPoint, QCustomPlot &plot) const
270 bool pointIsInAxisRect(const QPointF &axisPoint, QCustomPlot &plot) const
255 {
271 {
256 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
272 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
257 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
273 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
258 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
274 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
259 }
275 }
260
276
261 inline QCPRange _pixDistanceToRange(double pos1, double pos2, QCPAxis *axis)
277 inline QCPRange _pixDistanceToRange(double pos1, double pos2, QCPAxis *axis)
262 {
278 {
263 if (axis->scaleType() == QCPAxis::stLinear)
279 if (axis->scaleType() == QCPAxis::stLinear)
264 {
280 {
265 auto diff = axis->pixelToCoord(pos1) - axis->pixelToCoord(pos2);
281 auto diff = axis->pixelToCoord(pos1) - axis->pixelToCoord(pos2);
266 return QCPRange{axis->range().lower + diff, axis->range().upper + diff};
282 return QCPRange{axis->range().lower + diff, axis->range().upper + diff};
267 }
283 }
268 else
284 else
269 {
285 {
270 auto diff = axis->pixelToCoord(pos1) / axis->pixelToCoord(pos2);
286 auto diff = axis->pixelToCoord(pos1) / axis->pixelToCoord(pos2);
271 return QCPRange{axis->range().lower * diff, axis->range().upper * diff};
287 return QCPRange{axis->range().lower * diff, axis->range().upper * diff};
272 }
288 }
273 }
289 }
274
290
275 void setRange(const QCPRange &newRange)
291 void setRange(const QCPRange &newRange)
276 {
292 {
277 auto graphRange = DateTimeRange{newRange.lower, newRange.upper};
293 auto graphRange = DateTimeRange{newRange.lower, newRange.upper};
278
294
279 if (m_Flags.testFlag(GraphFlag::EnableAcquisition))
295 if (m_Flags.testFlag(GraphFlag::EnableAcquisition))
280 {
296 {
281 for (auto it = m_VariableToPlotMultiMap.begin(),
297 for (auto it = m_VariableToPlotMultiMap.begin(),
282 end = m_VariableToPlotMultiMap.end();
298 end = m_VariableToPlotMultiMap.end();
283 it != end; it = m_VariableToPlotMultiMap.upper_bound(it->first))
299 it != end; it = m_VariableToPlotMultiMap.upper_bound(it->first))
284 {
300 {
285 sqpApp->variableController().asyncChangeRange(it->first, graphRange);
301 sqpApp->variableController().asyncChangeRange(it->first, graphRange);
286 }
302 }
287 }
303 }
288 }
304 }
289
305
290 void moveGraph(const QPoint& destination)
306 void moveGraph(const QPoint& destination)
291 {
307 {
292 auto currentPos = destination;
308 auto currentPos = destination;
293 auto xAxis = m_plot->axisRect()->rangeDragAxis(Qt::Horizontal);
309 auto xAxis = m_plot->axisRect()->rangeDragAxis(Qt::Horizontal);
294 auto yAxis = m_plot->axisRect()->rangeDragAxis(Qt::Vertical);
310 auto yAxis = m_plot->axisRect()->rangeDragAxis(Qt::Vertical);
295 xAxis->setRange(_pixDistanceToRange(m_lastMousePos.x(), currentPos.x(), xAxis));
311 xAxis->setRange(_pixDistanceToRange(m_lastMousePos.x(), currentPos.x(), xAxis));
296 yAxis->setRange(_pixDistanceToRange(m_lastMousePos.y(), currentPos.y(), yAxis));
312 yAxis->setRange(_pixDistanceToRange(m_lastMousePos.y(), currentPos.y(), yAxis));
297 setRange(xAxis->range());
313 setRange(xAxis->range());
298 m_plot->replot(QCustomPlot::rpQueuedReplot);
314 m_plot->replot(QCustomPlot::rpQueuedReplot);
299 m_lastMousePos = destination;
315 m_lastMousePos = destination;
300 }
316 }
301 };
317 };
302
318
303 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
319 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
304 : VisualizationDragWidget{parent},
320 : VisualizationDragWidget{parent},
305 ui{new Ui::VisualizationGraphWidget},
321 ui{new Ui::VisualizationGraphWidget},
306 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
322 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
307 {
323 {
308 ui->setupUi(this);
324 ui->setupUi(this);
309 this->layout()->addWidget(impl->m_plot);
325 this->layout()->addWidget(impl->m_plot);
310 // 'Close' options : widget is deleted when closed
326 // 'Close' options : widget is deleted when closed
311 setAttribute(Qt::WA_DeleteOnClose);
327 setAttribute(Qt::WA_DeleteOnClose);
312
328
313 // The delegate must be initialized after the ui as it uses the plot
329 // The delegate must be initialized after the ui as it uses the plot
314 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
330 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
315
331
316 // Init the cursors
332 // Init the cursors
317 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
333 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
318 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
334 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
319 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
335 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
320 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
336 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
321
337
322 this->setFocusPolicy(Qt::WheelFocus);
338 this->setFocusPolicy(Qt::WheelFocus);
323 this->setMouseTracking(true);
339 this->setMouseTracking(true);
324 impl->m_plot->setAttribute(Qt::WA_TransparentForMouseEvents);
340 impl->m_plot->setAttribute(Qt::WA_TransparentForMouseEvents);
325 impl->m_plot->setContextMenuPolicy(Qt::CustomContextMenu);
341 impl->m_plot->setContextMenuPolicy(Qt::CustomContextMenu);
326 impl->m_plot->setParent(this);
342 impl->m_plot->setParent(this);
327 }
343 }
328
344
329
345
330 VisualizationGraphWidget::~VisualizationGraphWidget()
346 VisualizationGraphWidget::~VisualizationGraphWidget()
331 {
347 {
332 delete ui;
348 delete ui;
333 }
349 }
334
350
335 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
351 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
336 {
352 {
337 auto parent = parentWidget();
353 auto parent = parentWidget();
338 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
354 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
339 parent = parent->parentWidget();
355 parent = parent->parentWidget();
340 }
356 }
341
357
342 return qobject_cast<VisualizationZoneWidget *>(parent);
358 return qobject_cast<VisualizationZoneWidget *>(parent);
343 }
359 }
344
360
345 VisualizationWidget *VisualizationGraphWidget::parentVisualizationWidget() const
361 VisualizationWidget *VisualizationGraphWidget::parentVisualizationWidget() const
346 {
362 {
347 auto parent = parentWidget();
363 auto parent = parentWidget();
348 while (parent != nullptr && !qobject_cast<VisualizationWidget *>(parent)) {
364 while (parent != nullptr && !qobject_cast<VisualizationWidget *>(parent)) {
349 parent = parent->parentWidget();
365 parent = parent->parentWidget();
350 }
366 }
351
367
352 return qobject_cast<VisualizationWidget *>(parent);
368 return qobject_cast<VisualizationWidget *>(parent);
353 }
369 }
354
370
355 void VisualizationGraphWidget::setFlags(GraphFlags flags)
371 void VisualizationGraphWidget::setFlags(GraphFlags flags)
356 {
372 {
357 impl->m_Flags = std::move(flags);
373 impl->m_Flags = std::move(flags);
358 }
374 }
359
375
360 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, DateTimeRange range)
376 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, DateTimeRange range)
361 {
377 {
362 // Uses delegate to create the qcpplot components according to the variable
378 // Uses delegate to create the qcpplot components according to the variable
363 auto createdPlottables = VisualizationGraphHelper::create(variable, *impl->m_plot);
379 auto createdPlottables = VisualizationGraphHelper::create(variable, *impl->m_plot);
364
380
365 // Sets graph properties
381 // Sets graph properties
366 impl->m_RenderingDelegate->setGraphProperties(*variable, createdPlottables);
382 impl->m_RenderingDelegate->setGraphProperties(*variable, createdPlottables);
367
383
368 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
384 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
369
385
370 // If the variable already has its data loaded, load its units and its range in the graph
386 // If the variable already has its data loaded, load its units and its range in the graph
371 if (variable->dataSeries() != nullptr) {
387 if (variable->dataSeries() != nullptr) {
372 impl->m_RenderingDelegate->setAxesUnits(*variable);
388 impl->m_RenderingDelegate->setAxesUnits(*variable);
373 this->setFlags(GraphFlag::DisableAll);
389 this->setFlags(GraphFlag::DisableAll);
374 setGraphRange(range);
390 setGraphRange(range);
375 this->setFlags(GraphFlag::EnableAll);
391 this->setFlags(GraphFlag::EnableAll);
376 }
392 }
377 //@TODO this is bad! when variable is moved to another graph it still fires
393 //@TODO this is bad! when variable is moved to another graph it still fires
378 // even if this has been deleted
394 // even if this has been deleted
379 connect(variable.get(), &Variable::updated, this, &VisualizationGraphWidget::variableUpdated);
395 connect(variable.get(), &Variable::updated, this, &VisualizationGraphWidget::variableUpdated);
380 this->onUpdateVarDisplaying(variable, range); // My bullshit
396 this->onUpdateVarDisplaying(variable, range); // My bullshit
381 emit variableAdded(variable);
397 emit variableAdded(variable);
382 }
398 }
383
399
384 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
400 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
385 {
401 {
386 // Each component associated to the variable :
402 // Each component associated to the variable :
387 // - is removed from qcpplot (which deletes it)
403 // - is removed from qcpplot (which deletes it)
388 // - is no longer referenced in the map
404 // - is no longer referenced in the map
389 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
405 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
390 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
406 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
391 emit variableAboutToBeRemoved(variable);
407 emit variableAboutToBeRemoved(variable);
392
408
393 auto &plottablesMap = variableIt->second;
409 auto &plottablesMap = variableIt->second;
394
410
395 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
411 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
396 plottableIt != plottableEnd;) {
412 plottableIt != plottableEnd;) {
397 impl->m_plot->removePlottable(plottableIt->second);
413 impl->m_plot->removePlottable(plottableIt->second);
398 plottableIt = plottablesMap.erase(plottableIt);
414 plottableIt = plottablesMap.erase(plottableIt);
399 }
415 }
400
416
401 impl->m_VariableToPlotMultiMap.erase(variableIt);
417 impl->m_VariableToPlotMultiMap.erase(variableIt);
402 }
418 }
403
419
404 // Updates graph
420 // Updates graph
405 impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
421 impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
406 }
422 }
407
423
408 std::vector<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
424 std::vector<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
409 {
425 {
410 auto variables = std::vector<std::shared_ptr<Variable> >{};
426 auto variables = std::vector<std::shared_ptr<Variable> >{};
411 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
427 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
412 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
428 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
413 variables.push_back(it->first);
429 variables.push_back(it->first);
414 }
430 }
415
431
416 return variables;
432 return variables;
417 }
433 }
418
434
419 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
435 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
420 {
436 {
421 if (!variable) {
437 if (!variable) {
422 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
438 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
423 return;
439 return;
424 }
440 }
425
441
426 VisualizationGraphHelper::setYAxisRange(variable, *impl->m_plot);
442 VisualizationGraphHelper::setYAxisRange(variable, *impl->m_plot);
427 }
443 }
428
444
429 DateTimeRange VisualizationGraphWidget::graphRange() const noexcept
445 DateTimeRange VisualizationGraphWidget::graphRange() const noexcept
430 {
446 {
431 auto graphRange = impl->m_plot->xAxis->range();
447 auto graphRange = impl->m_plot->xAxis->range();
432 return DateTimeRange{graphRange.lower, graphRange.upper};
448 return DateTimeRange{graphRange.lower, graphRange.upper};
433 }
449 }
434
450
435 void VisualizationGraphWidget::setGraphRange(const DateTimeRange &range, bool calibration)
451 void VisualizationGraphWidget::setGraphRange(const DateTimeRange &range, bool calibration)
436 {
452 {
437 if (calibration) {
453 if (calibration) {
438 impl->m_IsCalibration = true;
454 impl->m_IsCalibration = true;
439 }
455 }
440
456
441 impl->m_plot->xAxis->setRange(range.m_TStart, range.m_TEnd);
457 impl->m_plot->xAxis->setRange(range.m_TStart, range.m_TEnd);
442 impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
458 impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
443
459
444 if (calibration) {
460 if (calibration) {
445 impl->m_IsCalibration = false;
461 impl->m_IsCalibration = false;
446 }
462 }
447 }
463 }
448
464
449 void VisualizationGraphWidget::setAutoRangeOnVariableInitialization(bool value)
465 void VisualizationGraphWidget::setAutoRangeOnVariableInitialization(bool value)
450 {
466 {
451 impl->m_VariableAutoRangeOnInit = value;
467 impl->m_VariableAutoRangeOnInit = value;
452 }
468 }
453
469
454 QVector<DateTimeRange> VisualizationGraphWidget::selectionZoneRanges() const
470 QVector<DateTimeRange> VisualizationGraphWidget::selectionZoneRanges() const
455 {
471 {
456 QVector<DateTimeRange> ranges;
472 QVector<DateTimeRange> ranges;
457 for (auto zone : impl->m_SelectionZones) {
473 for (auto zone : impl->m_SelectionZones) {
458 ranges << zone->range();
474 ranges << zone->range();
459 }
475 }
460
476
461 return ranges;
477 return ranges;
462 }
478 }
463
479
464 void VisualizationGraphWidget::addSelectionZones(const QVector<DateTimeRange> &ranges)
480 void VisualizationGraphWidget::addSelectionZones(const QVector<DateTimeRange> &ranges)
465 {
481 {
466 for (const auto &range : ranges) {
482 for (const auto &range : ranges) {
467 // note: ownership is transfered to QCustomPlot
483 // note: ownership is transfered to QCustomPlot
468 auto zone = new VisualizationSelectionZoneItem(&plot());
484 auto zone = new VisualizationSelectionZoneItem(&plot());
469 zone->setRange(range.m_TStart, range.m_TEnd);
485 zone->setRange(range.m_TStart, range.m_TEnd);
470 impl->addSelectionZone(zone);
486 impl->addSelectionZone(zone);
471 }
487 }
472
488
473 plot().replot(QCustomPlot::rpQueuedReplot);
489 plot().replot(QCustomPlot::rpQueuedReplot);
474 }
490 }
475
491
476 VisualizationSelectionZoneItem *
492 VisualizationSelectionZoneItem *
477 VisualizationGraphWidget::addSelectionZone(const QString &name, const DateTimeRange &range)
493 VisualizationGraphWidget::addSelectionZone(const QString &name, const DateTimeRange &range)
478 {
494 {
479 // note: ownership is transfered to QCustomPlot
495 // note: ownership is transfered to QCustomPlot
480 auto zone = new VisualizationSelectionZoneItem(&plot());
496 auto zone = new VisualizationSelectionZoneItem(&plot());
481 zone->setName(name);
497 zone->setName(name);
482 zone->setRange(range.m_TStart, range.m_TEnd);
498 zone->setRange(range.m_TStart, range.m_TEnd);
483 impl->addSelectionZone(zone);
499 impl->addSelectionZone(zone);
484
500
485 plot().replot(QCustomPlot::rpQueuedReplot);
501 plot().replot(QCustomPlot::rpQueuedReplot);
486
502
487 return zone;
503 return zone;
488 }
504 }
489
505
490 void VisualizationGraphWidget::removeSelectionZone(VisualizationSelectionZoneItem *selectionZone)
506 void VisualizationGraphWidget::removeSelectionZone(VisualizationSelectionZoneItem *selectionZone)
491 {
507 {
492 parentVisualizationWidget()->selectionZoneManager().setSelected(selectionZone, false);
508 parentVisualizationWidget()->selectionZoneManager().setSelected(selectionZone, false);
493
509
494 if (impl->m_HoveredZone == selectionZone) {
510 if (impl->m_HoveredZone == selectionZone) {
495 impl->m_HoveredZone = nullptr;
511 impl->m_HoveredZone = nullptr;
496 setCursor(Qt::ArrowCursor);
512 setCursor(Qt::ArrowCursor);
497 }
513 }
498
514
499 impl->m_SelectionZones.removeAll(selectionZone);
515 impl->m_SelectionZones.removeAll(selectionZone);
500 plot().removeItem(selectionZone);
516 plot().removeItem(selectionZone);
501 plot().replot(QCustomPlot::rpQueuedReplot);
517 plot().replot(QCustomPlot::rpQueuedReplot);
502 }
518 }
503
519
504 void VisualizationGraphWidget::undoZoom()
520 void VisualizationGraphWidget::undoZoom()
505 {
521 {
506 auto zoom = impl->m_ZoomStack.pop();
522 auto zoom = impl->m_ZoomStack.pop();
507 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
523 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
508 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
524 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
509
525
510 axisX->setRange(zoom.first);
526 axisX->setRange(zoom.first);
511 axisY->setRange(zoom.second);
527 axisY->setRange(zoom.second);
512
528
513 plot().replot(QCustomPlot::rpQueuedReplot);
529 plot().replot(QCustomPlot::rpQueuedReplot);
514 }
530 }
515
531
516 void VisualizationGraphWidget::zoom(double factor, int center, Qt::Orientation orientation)
532 void VisualizationGraphWidget::zoom(double factor, int center, Qt::Orientation orientation)
517
533
518 {
534 {
519 QCPAxis *axis = impl->m_plot->axisRect()->rangeZoomAxis(orientation);
535 QCPAxis *axis = impl->m_plot->axisRect()->rangeZoomAxis(orientation);
520 axis->scaleRange(factor, axis->pixelToCoord(center));
536 axis->scaleRange(factor, axis->pixelToCoord(center));
521 if (orientation == Qt::Horizontal)
537 if (orientation == Qt::Horizontal)
522 impl->setRange(axis->range());
538 impl->setRange(axis->range());
523 impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
539 impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
524 }
540 }
525
541
526 void VisualizationGraphWidget::move(double factor, Qt::Orientation orientation)
542 void VisualizationGraphWidget::move(double factor, Qt::Orientation orientation)
527 {
543 {
528 auto oldRange = impl->m_plot->xAxis->range();
544 auto oldRange = impl->m_plot->xAxis->range();
529 QCPAxis *axis = impl->m_plot->axisRect()->rangeDragAxis(orientation);
545 QCPAxis *axis = impl->m_plot->axisRect()->rangeDragAxis(orientation);
530 if (impl->m_plot->xAxis->scaleType() == QCPAxis::stLinear) {
546 if (impl->m_plot->xAxis->scaleType() == QCPAxis::stLinear) {
531 double rg = (axis->range().upper - axis->range().lower) * (factor / 10);
547 double rg = (axis->range().upper - axis->range().lower) * (factor / 10);
532 axis->setRange(axis->range().lower + (rg), axis->range().upper + (rg));
548 axis->setRange(axis->range().lower + (rg), axis->range().upper + (rg));
533 }
549 }
534 else if (impl->m_plot->xAxis->scaleType() == QCPAxis::stLogarithmic) {
550 else if (impl->m_plot->xAxis->scaleType() == QCPAxis::stLogarithmic) {
535 int start = 0, stop = 0;
551 int start = 0, stop = 0;
536 double diff = 0.;
552 double diff = 0.;
537 if (factor > 0.0) {
553 if (factor > 0.0) {
538 stop = this->width() * factor / 10;
554 stop = this->width() * factor / 10;
539 start = 2 * this->width() * factor / 10;
555 start = 2 * this->width() * factor / 10;
540 }
556 }
541 if (factor < 0.0) {
557 if (factor < 0.0) {
542 factor *= -1.0;
558 factor *= -1.0;
543 start = this->width() * factor / 10;
559 start = this->width() * factor / 10;
544 stop = 2 * this->width() * factor / 10;
560 stop = 2 * this->width() * factor / 10;
545 }
561 }
546 diff = axis->pixelToCoord(start) / axis->pixelToCoord(stop);
562 diff = axis->pixelToCoord(start) / axis->pixelToCoord(stop);
547 axis->setRange(impl->m_plot->axisRect()->rangeDragAxis(orientation)->range().lower * diff,
563 axis->setRange(impl->m_plot->axisRect()->rangeDragAxis(orientation)->range().lower * diff,
548 impl->m_plot->axisRect()->rangeDragAxis(orientation)->range().upper * diff);
564 impl->m_plot->axisRect()->rangeDragAxis(orientation)->range().upper * diff);
549 }
565 }
550 if (orientation == Qt::Horizontal)
566 if (orientation == Qt::Horizontal)
551 impl->setRange(axis->range());
567 impl->setRange(axis->range());
552 impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
568 impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
553 }
569 }
554
570
555 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
571 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
556 {
572 {
557 if (visitor) {
573 if (visitor) {
558 visitor->visit(this);
574 visitor->visit(this);
559 }
575 }
560 else {
576 else {
561 qCCritical(LOG_VisualizationGraphWidget())
577 qCCritical(LOG_VisualizationGraphWidget())
562 << tr("Can't visit widget : the visitor is null");
578 << tr("Can't visit widget : the visitor is null");
563 }
579 }
564 }
580 }
565
581
566 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
582 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
567 {
583 {
568 auto isSpectrogram = [](const auto &variable) {
584 auto isSpectrogram = [](const auto &variable) {
569 return std::dynamic_pointer_cast<SpectrogramSeries>(variable.dataSeries()) != nullptr;
585 return std::dynamic_pointer_cast<SpectrogramSeries>(variable.dataSeries()) != nullptr;
570 };
586 };
571
587
572 // - A spectrogram series can't be dropped on graph with existing plottables
588 // - A spectrogram series can't be dropped on graph with existing plottables
573 // - No data series can be dropped on graph with existing spectrogram series
589 // - No data series can be dropped on graph with existing spectrogram series
574 return isSpectrogram(variable)
590 return isSpectrogram(variable)
575 ? impl->m_VariableToPlotMultiMap.empty()
591 ? impl->m_VariableToPlotMultiMap.empty()
576 : std::none_of(
592 : std::none_of(
577 impl->m_VariableToPlotMultiMap.cbegin(), impl->m_VariableToPlotMultiMap.cend(),
593 impl->m_VariableToPlotMultiMap.cbegin(), impl->m_VariableToPlotMultiMap.cend(),
578 [isSpectrogram](const auto &entry) { return isSpectrogram(*entry.first); });
594 [isSpectrogram](const auto &entry) { return isSpectrogram(*entry.first); });
579 }
595 }
580
596
581 bool VisualizationGraphWidget::contains(const Variable &variable) const
597 bool VisualizationGraphWidget::contains(const Variable &variable) const
582 {
598 {
583 // Finds the variable among the keys of the map
599 // Finds the variable among the keys of the map
584 auto variablePtr = &variable;
600 auto variablePtr = &variable;
585 auto findVariable
601 auto findVariable
586 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
602 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
587
603
588 auto end = impl->m_VariableToPlotMultiMap.cend();
604 auto end = impl->m_VariableToPlotMultiMap.cend();
589 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
605 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
590 return it != end;
606 return it != end;
591 }
607 }
592
608
593 QString VisualizationGraphWidget::name() const
609 QString VisualizationGraphWidget::name() const
594 {
610 {
595 return impl->m_Name;
611 return impl->m_Name;
596 }
612 }
597
613
598 QMimeData *VisualizationGraphWidget::mimeData(const QPoint &position) const
614 QMimeData *VisualizationGraphWidget::mimeData(const QPoint &position) const
599 {
615 {
600 auto mimeData = new QMimeData;
616 auto mimeData = new QMimeData;
601
617
602 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position);
618 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position);
603 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
619 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
604 && selectionZoneItemUnderCursor) {
620 && selectionZoneItemUnderCursor) {
605 mimeData->setData(MIME_TYPE_TIME_RANGE, TimeController::mimeDataForTimeRange(
621 mimeData->setData(MIME_TYPE_TIME_RANGE, TimeController::mimeDataForTimeRange(
606 selectionZoneItemUnderCursor->range()));
622 selectionZoneItemUnderCursor->range()));
607 mimeData->setData(MIME_TYPE_SELECTION_ZONE, TimeController::mimeDataForTimeRange(
623 mimeData->setData(MIME_TYPE_SELECTION_ZONE, TimeController::mimeDataForTimeRange(
608 selectionZoneItemUnderCursor->range()));
624 selectionZoneItemUnderCursor->range()));
609 }
625 }
610 else {
626 else {
611 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
627 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
612
628
613 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
629 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
614 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
630 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
615 }
631 }
616
632
617 return mimeData;
633 return mimeData;
618 }
634 }
619
635
620 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint &dragPosition)
636 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint &dragPosition)
621 {
637 {
622 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition);
638 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition);
623 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
639 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
624 && selectionZoneItemUnderCursor) {
640 && selectionZoneItemUnderCursor) {
625
641
626 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
642 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
627 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
643 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
628
644
629 auto zoneSize = QSizeF{qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
645 auto zoneSize = QSizeF{qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
630 qAbs(zoneBottomRight.y() - zoneTopLeft.y())}
646 qAbs(zoneBottomRight.y() - zoneTopLeft.y())}
631 .toSize();
647 .toSize();
632
648
633 auto pixmap = QPixmap(zoneSize);
649 auto pixmap = QPixmap(zoneSize);
634 render(&pixmap, QPoint(), QRegion{QRect{zoneTopLeft.toPoint(), zoneSize}});
650 render(&pixmap, QPoint(), QRegion{QRect{zoneTopLeft.toPoint(), zoneSize}});
635
651
636 return pixmap;
652 return pixmap;
637 }
653 }
638
654
639 return QPixmap();
655 return QPixmap();
640 }
656 }
641
657
642 bool VisualizationGraphWidget::isDragAllowed() const
658 bool VisualizationGraphWidget::isDragAllowed() const
643 {
659 {
644 return true;
660 return true;
645 }
661 }
646
662
647 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
663 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
648 {
664 {
649 if (highlighted) {
665 if (highlighted) {
650 plot().setBackground(QBrush(QColor("#BBD5EE")));
666 plot().setBackground(QBrush(QColor("#BBD5EE")));
651 }
667 }
652 else {
668 else {
653 plot().setBackground(QBrush(Qt::white));
669 plot().setBackground(QBrush(Qt::white));
654 }
670 }
655
671
656 plot().update();
672 plot().update();
657 }
673 }
658
674
659 void VisualizationGraphWidget::addVerticalCursor(double time)
675 void VisualizationGraphWidget::addVerticalCursor(double time)
660 {
676 {
661 impl->m_VerticalCursor->setPosition(time);
677 impl->m_VerticalCursor->setPosition(time);
662 impl->m_VerticalCursor->setVisible(true);
678 impl->m_VerticalCursor->setVisible(true);
663
679
664 auto text
680 auto text
665 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
681 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
666 impl->m_VerticalCursor->setLabelText(text);
682 impl->m_VerticalCursor->setLabelText(text);
667 }
683 }
668
684
669 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
685 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
670 {
686 {
671 impl->m_VerticalCursor->setAbsolutePosition(position);
687 impl->m_VerticalCursor->setAbsolutePosition(position);
672 impl->m_VerticalCursor->setVisible(true);
688 impl->m_VerticalCursor->setVisible(true);
673
689
674 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
690 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
675 auto text
691 auto text
676 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
692 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
677 impl->m_VerticalCursor->setLabelText(text);
693 impl->m_VerticalCursor->setLabelText(text);
678 }
694 }
679
695
680 void VisualizationGraphWidget::removeVerticalCursor()
696 void VisualizationGraphWidget::removeVerticalCursor()
681 {
697 {
682 impl->m_VerticalCursor->setVisible(false);
698 impl->m_VerticalCursor->setVisible(false);
683 plot().replot(QCustomPlot::rpQueuedReplot);
699 plot().replot(QCustomPlot::rpQueuedReplot);
684 }
700 }
685
701
686 void VisualizationGraphWidget::addHorizontalCursor(double value)
702 void VisualizationGraphWidget::addHorizontalCursor(double value)
687 {
703 {
688 impl->m_HorizontalCursor->setPosition(value);
704 impl->m_HorizontalCursor->setPosition(value);
689 impl->m_HorizontalCursor->setVisible(true);
705 impl->m_HorizontalCursor->setVisible(true);
690 impl->m_HorizontalCursor->setLabelText(QString::number(value));
706 impl->m_HorizontalCursor->setLabelText(QString::number(value));
691 }
707 }
692
708
693 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
709 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
694 {
710 {
695 impl->m_HorizontalCursor->setAbsolutePosition(position);
711 impl->m_HorizontalCursor->setAbsolutePosition(position);
696 impl->m_HorizontalCursor->setVisible(true);
712 impl->m_HorizontalCursor->setVisible(true);
697
713
698 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
714 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
699 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
715 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
700 }
716 }
701
717
702 void VisualizationGraphWidget::removeHorizontalCursor()
718 void VisualizationGraphWidget::removeHorizontalCursor()
703 {
719 {
704 impl->m_HorizontalCursor->setVisible(false);
720 impl->m_HorizontalCursor->setVisible(false);
705 plot().replot(QCustomPlot::rpQueuedReplot);
721 plot().replot(QCustomPlot::rpQueuedReplot);
706 }
722 }
707
723
708 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
724 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
709 {
725 {
710 Q_UNUSED(event);
726 Q_UNUSED(event);
711
727
712 for (auto i : impl->m_SelectionZones) {
728 for (auto i : impl->m_SelectionZones) {
713 parentVisualizationWidget()->selectionZoneManager().setSelected(i, false);
729 parentVisualizationWidget()->selectionZoneManager().setSelected(i, false);
714 }
730 }
715
731
716 // Prevents that all variables will be removed from graph when it will be closed
732 // Prevents that all variables will be removed from graph when it will be closed
717 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
733 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
718 emit variableAboutToBeRemoved(variableEntry.first);
734 emit variableAboutToBeRemoved(variableEntry.first);
719 }
735 }
720 }
736 }
721
737
722 void VisualizationGraphWidget::enterEvent(QEvent *event)
738 void VisualizationGraphWidget::enterEvent(QEvent *event)
723 {
739 {
724 Q_UNUSED(event);
740 Q_UNUSED(event);
725 impl->m_RenderingDelegate->showGraphOverlay(true);
741 impl->m_RenderingDelegate->showGraphOverlay(true);
726 }
742 }
727
743
728 void VisualizationGraphWidget::leaveEvent(QEvent *event)
744 void VisualizationGraphWidget::leaveEvent(QEvent *event)
729 {
745 {
730 Q_UNUSED(event);
746 Q_UNUSED(event);
731 impl->m_RenderingDelegate->showGraphOverlay(false);
747 impl->m_RenderingDelegate->showGraphOverlay(false);
732
748
733 if (auto parentZone = parentZoneWidget()) {
749 if (auto parentZone = parentZoneWidget()) {
734 parentZone->notifyMouseLeaveGraph(this);
750 parentZone->notifyMouseLeaveGraph(this);
735 }
751 }
736 else {
752 else {
737 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
753 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
738 }
754 }
739
755
740 if (impl->m_HoveredZone) {
756 if (impl->m_HoveredZone) {
741 impl->m_HoveredZone->setHovered(false);
757 impl->m_HoveredZone->setHovered(false);
742 impl->m_HoveredZone = nullptr;
758 impl->m_HoveredZone = nullptr;
743 }
759 }
744 }
760 }
745
761
746 void VisualizationGraphWidget::wheelEvent(QWheelEvent *event)
762 void VisualizationGraphWidget::wheelEvent(QWheelEvent *event)
747 {
763 {
748 double factor;
764 double factor;
749 double wheelSteps = event->delta() / 120.0; // a single step delta is +/-120 usually
765 double wheelSteps = event->delta() / 120.0; // a single step delta is +/-120 usually
750 if (event->modifiers() == Qt::ControlModifier) {
766 if (event->modifiers() == Qt::ControlModifier) {
751 if (event->orientation() == Qt::Vertical) // mRangeZoom.testFlag(Qt::Vertical))
767 if (event->orientation() == Qt::Vertical) // mRangeZoom.testFlag(Qt::Vertical))
752 {
768 {
753 setCursor(Qt::SizeVerCursor);
769 setCursor(Qt::SizeVerCursor);
754 factor = pow(impl->m_plot->axisRect()->rangeZoomFactor(Qt::Vertical), wheelSteps);
770 factor = pow(impl->m_plot->axisRect()->rangeZoomFactor(Qt::Vertical), wheelSteps);
755 zoom(factor, event->pos().y(), Qt::Vertical);
771 zoom(factor, event->pos().y(), Qt::Vertical);
756 }
772 }
757 }
773 }
758 else if (event->modifiers() == Qt::ShiftModifier) {
774 else if (event->modifiers() == Qt::ShiftModifier) {
759 if (event->orientation() == Qt::Vertical) // mRangeZoom.testFlag(Qt::Vertical))
775 if (event->orientation() == Qt::Vertical) // mRangeZoom.testFlag(Qt::Vertical))
760 {
776 {
761 setCursor(Qt::SizeHorCursor);
777 setCursor(Qt::SizeHorCursor);
762 factor = pow(impl->m_plot->axisRect()->rangeZoomFactor(Qt::Horizontal), wheelSteps);
778 factor = pow(impl->m_plot->axisRect()->rangeZoomFactor(Qt::Horizontal), wheelSteps);
763 zoom(factor, event->pos().x(), Qt::Horizontal);
779 zoom(factor, event->pos().x(), Qt::Horizontal);
764 }
780 }
765 }
781 }
766 else {
782 else {
767 move(wheelSteps, Qt::Horizontal);
783 move(wheelSteps, Qt::Horizontal);
768 }
784 }
769 QWidget::wheelEvent(event);
785 QWidget::wheelEvent(event);
770 }
786 }
771
787
772
788
773
789
774 void VisualizationGraphWidget::mouseMoveEvent(QMouseEvent *event)
790 void VisualizationGraphWidget::mouseMoveEvent(QMouseEvent *event)
775 {
791 {
776 if(impl->isDrawingZoomRect())
792 if(impl->isDrawingZoomRect())
777 {
793 {
778 impl->updateZoomRect(event->pos());
794 impl->updateZoomRect(event->pos());
779 }
795 }
780 else if (impl->isDrawingZoneRect())
796 else if (impl->isDrawingZoneRect())
781 {
797 {
782 impl->updateZoneRect(event->pos());
798 impl->updateZoneRect(event->pos());
783 }
799 }
784 else if (event->buttons() == Qt::LeftButton)
800 else if (event->buttons() == Qt::LeftButton)
785 {
801 {
786 impl->moveGraph(event->pos());
802 switch (sqpApp->plotsInteractionMode())
803 {
804 case SqpApplication::PlotsInteractionMode::None:
805 impl->moveGraph(event->pos());
806 break;
807 case SqpApplication::PlotsInteractionMode::SelectionZones:
808
809 break;
810 default:
811 break;
812 }
787 }
813 }
788 else
814 else
789 {
815 {
790 impl->m_RenderingDelegate->updateTooltip(event);
816 impl->m_RenderingDelegate->updateTooltip(event);
791 }
817 }
792 event->accept();
793 QWidget::mouseMoveEvent(event);
818 QWidget::mouseMoveEvent(event);
794 }
819 }
795
820
796 void VisualizationGraphWidget::mouseReleaseEvent(QMouseEvent *event)
821 void VisualizationGraphWidget::mouseReleaseEvent(QMouseEvent *event)
797 {
822 {
798 if(impl->isDrawingZoomRect())
823 if(impl->isDrawingZoomRect())
799 {
824 {
800 impl->applyZoomRect();
825 impl->applyZoomRect();
801 }
826 }
802 else if(impl->isDrawingZoneRect())
827 else if(impl->isDrawingZoneRect())
803 {
828 {
804 impl->endDrawingZone(this);
829 impl->endDrawingZone();
805 }
830 }
806 else
831 else
807 {
832 {
808 setCursor(Qt::ArrowCursor);
833 setCursor(Qt::ArrowCursor);
809 }
834 }
810 QWidget::mouseReleaseEvent(event);
835 QWidget::mouseReleaseEvent(event);
811 }
836 }
812
837
813 void VisualizationGraphWidget::mousePressEvent(QMouseEvent *event)
838 void VisualizationGraphWidget::mousePressEvent(QMouseEvent *event)
814 {
839 {
815 if (event->button() == Qt::LeftButton) {
840 if (event->button()==Qt::RightButton)
816 if (event->modifiers() == Qt::ControlModifier) {
841 {
817 }
842 onGraphMenuRequested(event->pos());
818 else if (event->modifiers() == Qt::AltModifier) {
843 }
819
844 else
820 }
845 {
821 else
846 auto selectedZone = impl->selectionZoneAt(event->pos());
847 switch (sqpApp->plotsInteractionMode())
822 {
848 {
823 switch (sqpApp->plotsInteractionMode())
849 case SqpApplication::PlotsInteractionMode::DragAndDrop :
850 break;
851 case SqpApplication::PlotsInteractionMode::SelectionZones :
852 impl->setSelectionZonesEditionEnabled(true);
853 if ((event->modifiers() == Qt::ControlModifier) && (selectedZone != nullptr))
824 {
854 {
825 case SqpApplication::PlotsInteractionMode::DragAndDrop :
855 selectedZone->setAssociatedEditedZones(parentVisualizationWidget()->selectionZoneManager().selectedItems());
826 break;
856 }
827 case SqpApplication::PlotsInteractionMode::SelectionZones :
857 else
828 if (!impl->selectionZoneAt(event->pos())) {
858 {
829 impl->startDrawingZone(event->pos(), this);
859 if (!selectedZone)
860 {
861 parentVisualizationWidget()->selectionZoneManager().clearSelection();
862 impl->startDrawingZone(event->pos());
863 }
864 else
865 {
866 parentVisualizationWidget()->selectionZoneManager().select({ selectedZone });
830 }
867 }
831 break;
832 case SqpApplication::PlotsInteractionMode::ZoomBox :
833 impl->startDrawingRect(event->pos());
834 break;
835 default:
836 setCursor(Qt::ClosedHandCursor);
837 impl->updateMousePosition(event->pos());
838 }
868 }
869 break;
870 case SqpApplication::PlotsInteractionMode::ZoomBox :
871 impl->startDrawingRect(event->pos());
872 break;
873 default:
874 setCursor(Qt::ClosedHandCursor);
875 impl->updateMousePosition(event->pos());
839 }
876 }
840 }
877 }
841 else if (event->button()==Qt::RightButton)
842 {
843 onGraphMenuRequested(event->pos());
844 }
845 QWidget::mousePressEvent(event);
878 QWidget::mousePressEvent(event);
846 }
879 }
847
880
848 void VisualizationGraphWidget::mouseDoubleClickEvent(QMouseEvent *event)
881 void VisualizationGraphWidget::mouseDoubleClickEvent(QMouseEvent *event)
849 {
882 {
850 impl->m_RenderingDelegate->onMouseDoubleClick(event);
883 impl->m_RenderingDelegate->onMouseDoubleClick(event);
851 }
884 }
852
885
853 void VisualizationGraphWidget::keyReleaseEvent(QKeyEvent *event)
886 void VisualizationGraphWidget::keyReleaseEvent(QKeyEvent *event)
854 {
887 {
855 switch (event->key()) {
888 switch (event->key()) {
856 case Qt::Key_Control:
889 case Qt::Key_Control:
857 event->accept();
890 event->accept();
858 break;
891 break;
859 case Qt::Key_Shift:
892 case Qt::Key_Shift:
860 event->accept();
893 event->accept();
861 break;
894 break;
862 default:
895 default:
863 QWidget::keyReleaseEvent(event);
896 QWidget::keyReleaseEvent(event);
864 break;
897 break;
865 }
898 }
866 setCursor(Qt::ArrowCursor);
899 setCursor(Qt::ArrowCursor);
867 }
900 }
868
901
869 void VisualizationGraphWidget::keyPressEvent(QKeyEvent *event)
902 void VisualizationGraphWidget::keyPressEvent(QKeyEvent *event)
870 {
903 {
871 switch (event->key()) {
904 switch (event->key()) {
872 case Qt::Key_Control:
905 case Qt::Key_Control:
873 setCursor(Qt::CrossCursor);
906 setCursor(Qt::CrossCursor);
874 break;
907 break;
875 case Qt::Key_Shift:
908 case Qt::Key_Shift:
876 break;
909 break;
877 case Qt::Key_M:
910 case Qt::Key_M:
878 impl->m_plot->rescaleAxes();
911 impl->m_plot->rescaleAxes();
879 impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
912 impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
880 break;
913 break;
881 case Qt::Key_Left:
914 case Qt::Key_Left:
882 if (event->modifiers() != Qt::ControlModifier) {
915 if (event->modifiers() != Qt::ControlModifier) {
883 move(-0.1, Qt::Horizontal);
916 move(-0.1, Qt::Horizontal);
884 }
917 }
885 else {
918 else {
886 zoom(2, this->width() / 2, Qt::Horizontal);
919 zoom(2, this->width() / 2, Qt::Horizontal);
887 }
920 }
888 break;
921 break;
889 case Qt::Key_Right:
922 case Qt::Key_Right:
890 if (event->modifiers() != Qt::ControlModifier) {
923 if (event->modifiers() != Qt::ControlModifier) {
891 move(0.1, Qt::Horizontal);
924 move(0.1, Qt::Horizontal);
892 }
925 }
893 else {
926 else {
894 zoom(0.5, this->width() / 2, Qt::Horizontal);
927 zoom(0.5, this->width() / 2, Qt::Horizontal);
895 }
928 }
896 break;
929 break;
897 case Qt::Key_Up:
930 case Qt::Key_Up:
898 if (event->modifiers() != Qt::ControlModifier) {
931 if (event->modifiers() != Qt::ControlModifier) {
899 move(0.1, Qt::Vertical);
932 move(0.1, Qt::Vertical);
900 }
933 }
901 else {
934 else {
902 zoom(0.5, this->height() / 2, Qt::Vertical);
935 zoom(0.5, this->height() / 2, Qt::Vertical);
903 }
936 }
904 break;
937 break;
905 case Qt::Key_Down:
938 case Qt::Key_Down:
906 if (event->modifiers() != Qt::ControlModifier) {
939 if (event->modifiers() != Qt::ControlModifier) {
907 move(-0.1, Qt::Vertical);
940 move(-0.1, Qt::Vertical);
908 }
941 }
909 else {
942 else {
910 zoom(2, this->height() / 2, Qt::Vertical);
943 zoom(2, this->height() / 2, Qt::Vertical);
911 }
944 }
912 break;
945 break;
913 default:
946 default:
914 QWidget::keyPressEvent(event);
947 QWidget::keyPressEvent(event);
915 break;
948 break;
916 }
949 }
917 }
950 }
918
951
919 QCustomPlot &VisualizationGraphWidget::plot() const noexcept
952 QCustomPlot &VisualizationGraphWidget::plot() const noexcept
920 {
953 {
921 return *impl->m_plot;
954 return *impl->m_plot;
922 }
955 }
923
956
924 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
957 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
925 {
958 {
926 QMenu graphMenu{};
959 QMenu graphMenu{};
927
960
928 // Iterates on variables (unique keys)
961 // Iterates on variables (unique keys)
929 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
962 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
930 end = impl->m_VariableToPlotMultiMap.cend();
963 end = impl->m_VariableToPlotMultiMap.cend();
931 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
964 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
932 // 'Remove variable' action
965 // 'Remove variable' action
933 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
966 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
934 [this, var = it->first]() { removeVariable(var); });
967 [this, var = it->first]() { removeVariable(var); });
935 }
968 }
936
969
937 if (!impl->m_ZoomStack.isEmpty()) {
970 if (!impl->m_ZoomStack.isEmpty()) {
938 if (!graphMenu.isEmpty()) {
971 if (!graphMenu.isEmpty()) {
939 graphMenu.addSeparator();
972 graphMenu.addSeparator();
940 }
973 }
941
974
942 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
975 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
943 }
976 }
944
977
945 // Selection Zone Actions
978 // Selection Zone Actions
946 auto selectionZoneItem = impl->selectionZoneAt(pos);
979 auto selectionZoneItem = impl->selectionZoneAt(pos);
947 if (selectionZoneItem) {
980 if (selectionZoneItem) {
948 auto selectedItems = parentVisualizationWidget()->selectionZoneManager().selectedItems();
981 auto selectedItems = parentVisualizationWidget()->selectionZoneManager().selectedItems();
949 selectedItems.removeAll(selectionZoneItem);
982 selectedItems.removeAll(selectionZoneItem);
950 selectedItems.prepend(selectionZoneItem); // Put the current selection zone first
983 selectedItems.prepend(selectionZoneItem); // Put the current selection zone first
951
984
952 auto zoneActions = sqpApp->actionsGuiController().selectionZoneActions();
985 auto zoneActions = sqpApp->actionsGuiController().selectionZoneActions();
953 if (!zoneActions.isEmpty() && !graphMenu.isEmpty()) {
986 if (!zoneActions.isEmpty() && !graphMenu.isEmpty()) {
954 graphMenu.addSeparator();
987 graphMenu.addSeparator();
955 }
988 }
956
989
957 QHash<QString, QMenu *> subMenus;
990 QHash<QString, QMenu *> subMenus;
958 QHash<QString, bool> subMenusEnabled;
991 QHash<QString, bool> subMenusEnabled;
959 QHash<QString, FilteringAction *> filteredMenu;
992 QHash<QString, FilteringAction *> filteredMenu;
960
993
961 for (auto zoneAction : zoneActions) {
994 for (auto zoneAction : zoneActions) {
962
995
963 auto isEnabled = zoneAction->isEnabled(selectedItems);
996 auto isEnabled = zoneAction->isEnabled(selectedItems);
964
997
965 auto menu = &graphMenu;
998 auto menu = &graphMenu;
966 QString menuPath;
999 QString menuPath;
967 for (auto subMenuName : zoneAction->subMenuList()) {
1000 for (auto subMenuName : zoneAction->subMenuList()) {
968 menuPath += '/';
1001 menuPath += '/';
969 menuPath += subMenuName;
1002 menuPath += subMenuName;
970
1003
971 if (!subMenus.contains(menuPath)) {
1004 if (!subMenus.contains(menuPath)) {
972 menu = menu->addMenu(subMenuName);
1005 menu = menu->addMenu(subMenuName);
973 subMenus[menuPath] = menu;
1006 subMenus[menuPath] = menu;
974 subMenusEnabled[menuPath] = isEnabled;
1007 subMenusEnabled[menuPath] = isEnabled;
975 }
1008 }
976 else {
1009 else {
977 menu = subMenus.value(menuPath);
1010 menu = subMenus.value(menuPath);
978 if (isEnabled) {
1011 if (isEnabled) {
979 // The sub menu is enabled if at least one of its actions is enabled
1012 // The sub menu is enabled if at least one of its actions is enabled
980 subMenusEnabled[menuPath] = true;
1013 subMenusEnabled[menuPath] = true;
981 }
1014 }
982 }
1015 }
983 }
1016 }
984
1017
985 FilteringAction *filterAction = nullptr;
1018 FilteringAction *filterAction = nullptr;
986 if (sqpApp->actionsGuiController().isMenuFiltered(zoneAction->subMenuList())) {
1019 if (sqpApp->actionsGuiController().isMenuFiltered(zoneAction->subMenuList())) {
987 filterAction = filteredMenu.value(menuPath);
1020 filterAction = filteredMenu.value(menuPath);
988 if (!filterAction) {
1021 if (!filterAction) {
989 filterAction = new FilteringAction{this};
1022 filterAction = new FilteringAction{this};
990 filteredMenu[menuPath] = filterAction;
1023 filteredMenu[menuPath] = filterAction;
991 menu->addAction(filterAction);
1024 menu->addAction(filterAction);
992 }
1025 }
993 }
1026 }
994
1027
995 auto action = menu->addAction(zoneAction->name());
1028 auto action = menu->addAction(zoneAction->name());
996 action->setEnabled(isEnabled);
1029 action->setEnabled(isEnabled);
997 action->setShortcut(zoneAction->displayedShortcut());
1030 action->setShortcut(zoneAction->displayedShortcut());
998 QObject::connect(action, &QAction::triggered,
1031 QObject::connect(action, &QAction::triggered,
999 [zoneAction, selectedItems]() { zoneAction->execute(selectedItems); });
1032 [zoneAction, selectedItems]() { zoneAction->execute(selectedItems); });
1000
1033
1001 if (filterAction && zoneAction->isFilteringAllowed()) {
1034 if (filterAction && zoneAction->isFilteringAllowed()) {
1002 filterAction->addActionToFilter(action);
1035 filterAction->addActionToFilter(action);
1003 }
1036 }
1004 }
1037 }
1005
1038
1006 for (auto it = subMenus.cbegin(); it != subMenus.cend(); ++it) {
1039 for (auto it = subMenus.cbegin(); it != subMenus.cend(); ++it) {
1007 it.value()->setEnabled(subMenusEnabled[it.key()]);
1040 it.value()->setEnabled(subMenusEnabled[it.key()]);
1008 }
1041 }
1009 }
1042 }
1010
1043
1011 if (!graphMenu.isEmpty()) {
1044 if (!graphMenu.isEmpty()) {
1012 graphMenu.exec(QCursor::pos());
1045 graphMenu.exec(QCursor::pos());
1013 }
1046 }
1014 }
1047 }
1015
1048
1016 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept
1049 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept
1017 {
1050 {
1018 impl->m_RenderingDelegate->onMouseDoubleClick(event);
1051 impl->m_RenderingDelegate->onMouseDoubleClick(event);
1019 }
1052 }
1020
1053
1021 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
1054 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
1022 {
1055 {
1023 // Handles plot rendering when mouse is moving
1056 // Handles plot rendering when mouse is moving
1024 impl->m_RenderingDelegate->updateTooltip(event);
1057 impl->m_RenderingDelegate->updateTooltip(event);
1025
1058
1026 auto axisPos = impl->posToAxisPos(event->pos());
1059 auto axisPos = impl->posToAxisPos(event->pos());
1027
1060
1028 // Zoom box and zone drawing
1061 // Zoom box and zone drawing
1029 if (impl->m_DrawingZoomRect) {
1062 if (impl->m_DrawingZoomRect) {
1030 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
1063 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
1031 }
1064 }
1032 else if (impl->m_DrawingZone) {
1065 else if (impl->m_DrawingZone) {
1033 impl->m_DrawingZone->setEnd(axisPos.x());
1066 impl->m_DrawingZone->setEnd(axisPos.x());
1034 }
1067 }
1035
1068
1036 // Cursor
1069 // Cursor
1037 if (auto parentZone = parentZoneWidget()) {
1070 if (auto parentZone = parentZoneWidget()) {
1038 if (impl->pointIsInAxisRect(axisPos, plot())) {
1071 if (impl->pointIsInAxisRect(axisPos, plot())) {
1039 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
1072 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
1040 }
1073 }
1041 else {
1074 else {
1042 parentZone->notifyMouseLeaveGraph(this);
1075 parentZone->notifyMouseLeaveGraph(this);
1043 }
1076 }
1044 }
1077 }
1045
1078
1046 // Search for the selection zone under the mouse
1079 // Search for the selection zone under the mouse
1047 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos());
1080 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos());
1048 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
1081 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
1049 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) {
1082 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) {
1050
1083
1051 // Sets the appropriate cursor shape
1084 // Sets the appropriate cursor shape
1052 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
1085 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
1053 setCursor(cursorShape);
1086 setCursor(cursorShape);
1054
1087
1055 // Manages the hovered zone
1088 // Manages the hovered zone
1056 if (selectionZoneItemUnderCursor != impl->m_HoveredZone) {
1089 if (selectionZoneItemUnderCursor != impl->m_HoveredZone) {
1057 if (impl->m_HoveredZone) {
1090 if (impl->m_HoveredZone) {
1058 impl->m_HoveredZone->setHovered(false);
1091 impl->m_HoveredZone->setHovered(false);
1059 }
1092 }
1060 selectionZoneItemUnderCursor->setHovered(true);
1093 selectionZoneItemUnderCursor->setHovered(true);
1061 impl->m_HoveredZone = selectionZoneItemUnderCursor;
1094 impl->m_HoveredZone = selectionZoneItemUnderCursor;
1062 plot().replot(QCustomPlot::rpQueuedReplot);
1095 plot().replot(QCustomPlot::rpQueuedReplot);
1063 }
1096 }
1064 }
1097 }
1065 else {
1098 else {
1066 // There is no zone under the mouse or the interaction mode is not "selection zones"
1099 // There is no zone under the mouse or the interaction mode is not "selection zones"
1067 if (impl->m_HoveredZone) {
1100 if (impl->m_HoveredZone) {
1068 impl->m_HoveredZone->setHovered(false);
1101 impl->m_HoveredZone->setHovered(false);
1069 impl->m_HoveredZone = nullptr;
1102 impl->m_HoveredZone = nullptr;
1070 }
1103 }
1071
1104
1072 setCursor(Qt::ArrowCursor);
1105 setCursor(Qt::ArrowCursor);
1073 }
1106 }
1074
1107
1075 impl->m_HasMovedMouse = true;
1108 impl->m_HasMovedMouse = true;
1076 VisualizationDragWidget::mouseMoveEvent(event);
1109 VisualizationDragWidget::mouseMoveEvent(event);
1077 }
1110 }
1078
1111
1079 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
1112 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
1080 {
1113 {
1081 // Processes event only if the wheel occurs on axis rect
1114 // Processes event only if the wheel occurs on axis rect
1082 if (!dynamic_cast<QCPAxisRect *>(impl->m_plot->layoutElementAt(event->posF()))) {
1115 if (!dynamic_cast<QCPAxisRect *>(impl->m_plot->layoutElementAt(event->posF()))) {
1083 return;
1116 return;
1084 }
1117 }
1085
1118
1086 auto value = event->angleDelta().x() + event->angleDelta().y();
1119 auto value = event->angleDelta().x() + event->angleDelta().y();
1087 if (value != 0) {
1120 if (value != 0) {
1088
1121
1089 auto direction = value > 0 ? 1.0 : -1.0;
1122 auto direction = value > 0 ? 1.0 : -1.0;
1090 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
1123 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
1091 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
1124 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
1092 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
1125 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
1093
1126
1094 auto zoomOrientations = QFlags<Qt::Orientation>{};
1127 auto zoomOrientations = QFlags<Qt::Orientation>{};
1095 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
1128 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
1096 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
1129 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
1097
1130
1098 impl->m_plot->axisRect()->setRangeZoom(zoomOrientations);
1131 impl->m_plot->axisRect()->setRangeZoom(zoomOrientations);
1099
1132
1100 if (!isZoomX && !isZoomY) {
1133 if (!isZoomX && !isZoomY) {
1101 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
1134 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
1102 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
1135 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
1103
1136
1104 axis->setRange(axis->range() + diff);
1137 axis->setRange(axis->range() + diff);
1105
1138
1106 if (plot().noAntialiasingOnDrag()) {
1139 if (plot().noAntialiasingOnDrag()) {
1107 plot().setNotAntialiasedElements(QCP::aeAll);
1140 plot().setNotAntialiasedElements(QCP::aeAll);
1108 }
1141 }
1109
1142
1110 // plot().replot(QCustomPlot::rpQueuedReplot);
1143 // plot().replot(QCustomPlot::rpQueuedReplot);
1111 }
1144 }
1112 }
1145 }
1113 }
1146 }
1114
1147
1115 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
1148 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
1116 {
1149 {
1117 auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
1150 auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
1118 auto isSelectionZoneMode
1151 auto isSelectionZoneMode
1119 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
1152 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
1120 auto isLeftClick = event->buttons().testFlag(Qt::LeftButton);
1153 auto isLeftClick = event->buttons().testFlag(Qt::LeftButton);
1121
1154
1122 if (!isDragDropClick && isLeftClick) {
1155 if (!isDragDropClick && isLeftClick) {
1123 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) {
1156 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) {
1124 // Starts a zoom box
1157 // Starts a zoom box
1125 impl->startDrawingRect(event->pos());
1158 impl->startDrawingRect(event->pos());
1126 }
1159 }
1127 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr) {
1160 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr) {
1128 // Starts a new selection zone
1161 // Starts a new selection zone
1129 auto zoneAtPos = impl->selectionZoneAt(event->pos());
1162 auto zoneAtPos = impl->selectionZoneAt(event->pos());
1130 if (!zoneAtPos) {
1163 if (!zoneAtPos) {
1131 impl->startDrawingZone(event->pos(), this);
1164 impl->startDrawingZone(event->pos());
1132 }
1165 }
1133 }
1166 }
1134 }
1167 }
1135
1168
1136
1169
1137 // Allows zone edition only in selection zone mode without drag&drop
1170 // Allows zone edition only in selection zone mode without drag&drop
1138 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
1171 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
1139
1172
1140 // Selection / Deselection
1173 // Selection / Deselection
1141 if (isSelectionZoneMode) {
1174 if (isSelectionZoneMode) {
1142 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
1175 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
1143 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos());
1176 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos());
1144
1177
1145
1178
1146 if (selectionZoneItemUnderCursor && !selectionZoneItemUnderCursor->selected()
1179 if (selectionZoneItemUnderCursor && !selectionZoneItemUnderCursor->selected()
1147 && !isMultiSelectionClick) {
1180 && !isMultiSelectionClick) {
1148 parentVisualizationWidget()->selectionZoneManager().select(
1181 parentVisualizationWidget()->selectionZoneManager().select(
1149 {selectionZoneItemUnderCursor});
1182 {selectionZoneItemUnderCursor});
1150 }
1183 }
1151 else if (!selectionZoneItemUnderCursor && !isMultiSelectionClick && isLeftClick) {
1184 else if (!selectionZoneItemUnderCursor && !isMultiSelectionClick && isLeftClick) {
1152 parentVisualizationWidget()->selectionZoneManager().clearSelection();
1185 parentVisualizationWidget()->selectionZoneManager().clearSelection();
1153 }
1186 }
1154 else {
1187 else {
1155 // No selection change
1188 // No selection change
1156 }
1189 }
1157
1190
1158 if (selectionZoneItemUnderCursor && isLeftClick) {
1191 if (selectionZoneItemUnderCursor && isLeftClick) {
1159 selectionZoneItemUnderCursor->setAssociatedEditedZones(
1192 selectionZoneItemUnderCursor->setAssociatedEditedZones(
1160 parentVisualizationWidget()->selectionZoneManager().selectedItems());
1193 parentVisualizationWidget()->selectionZoneManager().selectedItems());
1161 }
1194 }
1162 }
1195 }
1163
1196
1164
1197
1165 impl->m_HasMovedMouse = false;
1198 impl->m_HasMovedMouse = false;
1166 VisualizationDragWidget::mousePressEvent(event);
1199 VisualizationDragWidget::mousePressEvent(event);
1167 }
1200 }
1168
1201
1169 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
1202 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
1170 {
1203 {
1171 if (impl->m_DrawingZoomRect) {
1204 if (impl->m_DrawingZoomRect) {
1172
1205
1173 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
1206 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
1174 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
1207 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
1175
1208
1176 auto newAxisXRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().x(),
1209 auto newAxisXRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().x(),
1177 impl->m_DrawingZoomRect->bottomRight->coords().x()};
1210 impl->m_DrawingZoomRect->bottomRight->coords().x()};
1178
1211
1179 auto newAxisYRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().y(),
1212 auto newAxisYRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().y(),
1180 impl->m_DrawingZoomRect->bottomRight->coords().y()};
1213 impl->m_DrawingZoomRect->bottomRight->coords().y()};
1181
1214
1182 impl->removeDrawingRect();
1215 impl->removeDrawingRect();
1183
1216
1184 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
1217 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
1185 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
1218 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
1186 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
1219 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
1187 axisX->setRange(newAxisXRange);
1220 axisX->setRange(newAxisXRange);
1188 axisY->setRange(newAxisYRange);
1221 axisY->setRange(newAxisYRange);
1189
1222
1190 plot().replot(QCustomPlot::rpQueuedReplot);
1223 plot().replot(QCustomPlot::rpQueuedReplot);
1191 }
1224 }
1192 }
1225 }
1193
1226
1194 impl->endDrawingZone(this);
1227 impl->endDrawingZone();
1195
1228
1196 // Selection / Deselection
1229 // Selection / Deselection
1197 auto isSelectionZoneMode
1230 auto isSelectionZoneMode
1198 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
1231 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
1199 if (isSelectionZoneMode) {
1232 if (isSelectionZoneMode) {
1200 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
1233 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
1201 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos());
1234 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos());
1202 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton
1235 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton
1203 && !impl->m_HasMovedMouse) {
1236 && !impl->m_HasMovedMouse) {
1204
1237
1205 auto zonesUnderCursor = impl->selectionZonesAt(event->pos(), plot());
1238 auto zonesUnderCursor = impl->selectionZonesAt(event->pos(), plot());
1206 if (zonesUnderCursor.count() > 1) {
1239 if (zonesUnderCursor.count() > 1) {
1207 // There are multiple zones under the mouse.
1240 // There are multiple zones under the mouse.
1208 // Performs the selection with a selection dialog.
1241 // Performs the selection with a selection dialog.
1209 VisualizationMultiZoneSelectionDialog dialog{this};
1242 VisualizationMultiZoneSelectionDialog dialog{this};
1210 dialog.setZones(zonesUnderCursor);
1243 dialog.setZones(zonesUnderCursor);
1211 dialog.move(mapToGlobal(event->pos() - QPoint(dialog.width() / 2, 20)));
1244 dialog.move(mapToGlobal(event->pos() - QPoint(dialog.width() / 2, 20)));
1212 dialog.activateWindow();
1245 dialog.activateWindow();
1213 dialog.raise();
1246 dialog.raise();
1214 if (dialog.exec() == QDialog::Accepted) {
1247 if (dialog.exec() == QDialog::Accepted) {
1215 auto selection = dialog.selectedZones();
1248 auto selection = dialog.selectedZones();
1216
1249
1217 if (!isMultiSelectionClick) {
1250 if (!isMultiSelectionClick) {
1218 parentVisualizationWidget()->selectionZoneManager().clearSelection();
1251 parentVisualizationWidget()->selectionZoneManager().clearSelection();
1219 }
1252 }
1220
1253
1221 for (auto it = selection.cbegin(); it != selection.cend(); ++it) {
1254 for (auto it = selection.cbegin(); it != selection.cend(); ++it) {
1222 auto zone = it.key();
1255 auto zone = it.key();
1223 auto isSelected = it.value();
1256 auto isSelected = it.value();
1224 parentVisualizationWidget()->selectionZoneManager().setSelected(zone,
1257 parentVisualizationWidget()->selectionZoneManager().setSelected(zone,
1225 isSelected);
1258 isSelected);
1226
1259
1227 if (isSelected) {
1260 if (isSelected) {
1228 // Puts the zone on top of the stack so it can be moved or resized
1261 // Puts the zone on top of the stack so it can be moved or resized
1229 impl->moveSelectionZoneOnTop(zone, plot());
1262 impl->moveSelectionZoneOnTop(zone, plot());
1230 }
1263 }
1231 }
1264 }
1232 }
1265 }
1233 }
1266 }
1234 else {
1267 else {
1235 if (!isMultiSelectionClick) {
1268 if (!isMultiSelectionClick) {
1236 parentVisualizationWidget()->selectionZoneManager().select(
1269 parentVisualizationWidget()->selectionZoneManager().select(
1237 {selectionZoneItemUnderCursor});
1270 {selectionZoneItemUnderCursor});
1238 impl->moveSelectionZoneOnTop(selectionZoneItemUnderCursor, plot());
1271 impl->moveSelectionZoneOnTop(selectionZoneItemUnderCursor, plot());
1239 }
1272 }
1240 else {
1273 else {
1241 parentVisualizationWidget()->selectionZoneManager().setSelected(
1274 parentVisualizationWidget()->selectionZoneManager().setSelected(
1242 selectionZoneItemUnderCursor, !selectionZoneItemUnderCursor->selected()
1275 selectionZoneItemUnderCursor, !selectionZoneItemUnderCursor->selected()
1243 || event->button() == Qt::RightButton);
1276 || event->button() == Qt::RightButton);
1244 }
1277 }
1245 }
1278 }
1246 }
1279 }
1247 else {
1280 else {
1248 // No selection change
1281 // No selection change
1249 }
1282 }
1250 }
1283 }
1251 }
1284 }
1252
1285
1253 void VisualizationGraphWidget::onDataCacheVariableUpdated()
1286 void VisualizationGraphWidget::onDataCacheVariableUpdated()
1254 {
1287 {
1255 auto graphRange = impl->m_plot->xAxis->range();
1288 auto graphRange = impl->m_plot->xAxis->range();
1256 auto dateTime = DateTimeRange{graphRange.lower, graphRange.upper};
1289 auto dateTime = DateTimeRange{graphRange.lower, graphRange.upper};
1257
1290
1258 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
1291 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
1259 auto variable = variableEntry.first;
1292 auto variable = variableEntry.first;
1260 qCDebug(LOG_VisualizationGraphWidget())
1293 qCDebug(LOG_VisualizationGraphWidget())
1261 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
1294 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
1262 qCDebug(LOG_VisualizationGraphWidget())
1295 qCDebug(LOG_VisualizationGraphWidget())
1263 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
1296 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
1264 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
1297 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
1265 impl->updateData(variableEntry.second, variable, variable->range());
1298 impl->updateData(variableEntry.second, variable, variable->range());
1266 }
1299 }
1267 }
1300 }
1268 }
1301 }
1269
1302
1270 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
1303 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
1271 const DateTimeRange &range)
1304 const DateTimeRange &range)
1272 {
1305 {
1273 auto it = impl->m_VariableToPlotMultiMap.find(variable);
1306 auto it = impl->m_VariableToPlotMultiMap.find(variable);
1274 if (it != impl->m_VariableToPlotMultiMap.end()) {
1307 if (it != impl->m_VariableToPlotMultiMap.end()) {
1275 impl->updateData(it->second, variable, range);
1308 impl->updateData(it->second, variable, range);
1276 }
1309 }
1277 }
1310 }
1278
1311
1279 void VisualizationGraphWidget::variableUpdated(QUuid id)
1312 void VisualizationGraphWidget::variableUpdated(QUuid id)
1280 {
1313 {
1281 for (auto &[var, plotables] : impl->m_VariableToPlotMultiMap) {
1314 for (auto &[var, plotables] : impl->m_VariableToPlotMultiMap) {
1282 if (var->ID() == id) {
1315 if (var->ID() == id) {
1283 impl->updateData(plotables, var, this->graphRange());
1316 impl->updateData(plotables, var, this->graphRange());
1284 }
1317 }
1285 }
1318 }
1286 }
1319 }
@@ -1,102 +1,101
1 #include <QtTest>
1 #include <QtTest>
2 #include <QObject>
2 #include <QObject>
3 #include <QString>
3 #include <QString>
4 #include <QScreen>
4 #include <QScreen>
5 #include <QMainWindow>
5 #include <QMainWindow>
6 #include <QWheelEvent>
6 #include <QWheelEvent>
7
7
8 #include <qcustomplot.h>
8 #include <qcustomplot.h>
9
9
10 #include <SqpApplication.h>
10 #include <SqpApplication.h>
11 #include <Variable/VariableController2.h>
11 #include <Variable/VariableController2.h>
12 #include <Common/cpp_utils.h>
12 #include <Common/cpp_utils.h>
13
13
14 #include <Visualization/VisualizationGraphWidget.h>
14 #include <Visualization/VisualizationGraphWidget.h>
15 #include <TestProviders.h>
15 #include <TestProviders.h>
16 #include <GUITestUtils.h>
16 #include <GUITestUtils.h>
17
17
18
18
19 ALIAS_TEMPLATE_FUNCTION(isReady, static_cast<SqpApplication *>(qApp)->variableController().isReady)
19 ALIAS_TEMPLATE_FUNCTION(isReady, static_cast<SqpApplication *>(qApp)->variableController().isReady)
20
20
21 #define A_SIMPLE_GRAPH_FIXTURE \
21 #define A_SIMPLE_GRAPH_FIXTURE \
22 VisualizationGraphWidget w;\
22 VisualizationGraphWidget w;\
23 PREPARE_GUI_TEST(w);\
23 PREPARE_GUI_TEST(w);\
24 auto provider = std::make_shared<SimpleRange<10> >();\
24 auto provider = std::make_shared<SimpleRange<10> >();\
25 auto range = DateTimeRange::fromDateTime(QDate(2018, 8, 7), QTime(14, 00), QDate(2018, 8, 7),\
25 auto range = DateTimeRange::fromDateTime(QDate(2018, 8, 7), QTime(14, 00), QDate(2018, 8, 7),\
26 QTime(16, 00));\
26 QTime(16, 00));\
27 auto var = static_cast<SqpApplication *>(qApp)->variableController().createVariable(\
27 auto var = static_cast<SqpApplication *>(qApp)->variableController().createVariable(\
28 "V1", {{"", "scalar"}}, provider, range);\
28 "V1", {{"", "scalar"}}, provider, range);\
29 while (!isReady(var))\
29 while (!isReady(var))\
30 QCoreApplication::processEvents();\
30 QCoreApplication::processEvents();\
31 w.addVariable(var, range);\
31 w.addVariable(var, range);\
32 GET_CHILD_WIDGET_FOR_GUI_TESTS(w, plot, QCustomPlot, "widget");\
33 auto cent = center(&w);
32 auto cent = center(&w);
34
33
35
34
36
35
37 class A_SimpleGraph : public QObject {
36 class A_SimpleGraph : public QObject {
38 Q_OBJECT
37 Q_OBJECT
39 public:
38 public:
40 explicit A_SimpleGraph(QObject *parent = Q_NULLPTR) : QObject(parent) {}
39 explicit A_SimpleGraph(QObject *parent = Q_NULLPTR) : QObject(parent) {}
41
40
42 private slots:
41 private slots:
43 void scrolls_left_with_mouse()
42 void scrolls_left_with_mouse()
44 {
43 {
45 A_SIMPLE_GRAPH_FIXTURE;
44 A_SIMPLE_GRAPH_FIXTURE;
46
45
47 for (auto i = 0; i < 100; i++) {
46 for (auto i = 0; i < 100; i++) {
48 QTest::mousePress(&w, Qt::LeftButton, Qt::NoModifier, cent, 1);
47 QTest::mousePress(&w, Qt::LeftButton, Qt::NoModifier, cent, 1);
49 mouseMove(&w, {cent.x() + 200, cent.y()}, Qt::LeftButton);
48 mouseMove(&w, {cent.x() + 200, cent.y()}, Qt::LeftButton);
50 QTest::mouseRelease(&w, Qt::LeftButton);
49 QTest::mouseRelease(&w, Qt::LeftButton);
51 while (!isReady(var))
50 while (!isReady(var))
52 QCoreApplication::processEvents();
51 QCoreApplication::processEvents();
53 }
52 }
54 while (!isReady(var))
53 while (!isReady(var))
55 QCoreApplication::processEvents();
54 QCoreApplication::processEvents();
56 auto r = var->range();
55 auto r = var->range();
57 /*
56 /*
58 * Scrolling to the left implies going back in time
57 * Scrolling to the left implies going back in time
59 * Scroll only implies keeping the same delta T -> shit only transformation
58 * Scroll only implies keeping the same delta T -> shit only transformation
60 */
59 */
61 QVERIFY(r.m_TEnd < range.m_TEnd);
60 QVERIFY(r.m_TEnd < range.m_TEnd);
62 QVERIFY(SciQLop::numeric::almost_equal<double>(r.delta(),range.delta(),1));
61 QVERIFY(SciQLop::numeric::almost_equal<double>(r.delta(),range.delta(),1));
63 }
62 }
64
63
65 void scrolls_right_with_mouse()
64 void scrolls_right_with_mouse()
66 {
65 {
67 A_SIMPLE_GRAPH_FIXTURE;
66 A_SIMPLE_GRAPH_FIXTURE;
68
67
69 for (auto i = 0; i < 100; i++) {
68 for (auto i = 0; i < 100; i++) {
70 QTest::mousePress(&w, Qt::LeftButton, Qt::NoModifier, cent, 1);
69 QTest::mousePress(&w, Qt::LeftButton, Qt::NoModifier, cent, 1);
71 mouseMove(&w, {cent.x() - 200, cent.y()}, Qt::LeftButton);
70 mouseMove(&w, {cent.x() - 200, cent.y()}, Qt::LeftButton);
72 QTest::mouseRelease(&w, Qt::LeftButton);
71 QTest::mouseRelease(&w, Qt::LeftButton);
73 while (!isReady(var))
72 while (!isReady(var))
74 QCoreApplication::processEvents();
73 QCoreApplication::processEvents();
75 }
74 }
76 while (!isReady(var))
75 while (!isReady(var))
77 QCoreApplication::processEvents();
76 QCoreApplication::processEvents();
78 auto r = var->range();
77 auto r = var->range();
79 /*
78 /*
80 * Scrolling to the right implies going forward in time
79 * Scrolling to the right implies going forward in time
81 * Scroll only implies keeping the same delta T -> shit only transformation
80 * Scroll only implies keeping the same delta T -> shit only transformation
82 */
81 */
83 QVERIFY(r.m_TEnd > range.m_TEnd);
82 QVERIFY(r.m_TEnd > range.m_TEnd);
84 QVERIFY(SciQLop::numeric::almost_equal<double>(r.delta(),range.delta(),1));
83 QVERIFY(SciQLop::numeric::almost_equal<double>(r.delta(),range.delta(),1));
85 }
84 }
86 };
85 };
87
86
88 QT_BEGIN_NAMESPACE
87 QT_BEGIN_NAMESPACE
89 QTEST_ADD_GPU_BLACKLIST_SUPPORT_DEFS
88 QTEST_ADD_GPU_BLACKLIST_SUPPORT_DEFS
90 QT_END_NAMESPACE
89 QT_END_NAMESPACE
91 int main(int argc, char *argv[])
90 int main(int argc, char *argv[])
92 {
91 {
93 SqpApplication app{argc, argv};
92 SqpApplication app{argc, argv};
94 app.setAttribute(Qt::AA_Use96Dpi, true);
93 app.setAttribute(Qt::AA_Use96Dpi, true);
95 QTEST_DISABLE_KEYPAD_NAVIGATION;
94 QTEST_DISABLE_KEYPAD_NAVIGATION;
96 QTEST_ADD_GPU_BLACKLIST_SUPPORT;
95 QTEST_ADD_GPU_BLACKLIST_SUPPORT;
97 A_SimpleGraph tc;
96 A_SimpleGraph tc;
98 QTEST_SET_MAIN_SOURCE_PATH;
97 QTEST_SET_MAIN_SOURCE_PATH;
99 return QTest::qExec(&tc, argc, argv);
98 return QTest::qExec(&tc, argc, argv);
100 }
99 }
101
100
102 #include "main.moc"
101 #include "main.moc"
General Comments 0
You need to be logged in to leave comments. Login now