##// END OF EJS Templates
Restored graph D&D and added full screen mode (F11)...
jeandet -
r1376:b8327d173f37
parent child
Show More
@@ -1,66 +1,68
1 /*------------------------------------------------------------------------------
1 /*------------------------------------------------------------------------------
2 -- This file is a part of the SciQLop Software
2 -- This file is a part of the SciQLop Software
3 -- Copyright (C) 2017, Plasma Physics Laboratory - CNRS
3 -- Copyright (C) 2017, Plasma Physics Laboratory - CNRS
4 --
4 --
5 -- This program is free software; you can redistribute it and/or modify
5 -- This program is free software; you can redistribute it and/or modify
6 -- it under the terms of the GNU General Public License as published by
6 -- it under the terms of the GNU General Public License as published by
7 -- the Free Software Foundation; either version 2 of the License, or
7 -- the Free Software Foundation; either version 2 of the License, or
8 -- (at your option) any later version.
8 -- (at your option) any later version.
9 --
9 --
10 -- This program is distributed in the hope that it will be useful,
10 -- This program is distributed in the hope that it will be useful,
11 -- but WITHOUT ANY WARRANTY; without even the implied warranty of
11 -- but WITHOUT ANY WARRANTY; without even the implied warranty of
12 -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 -- GNU General Public License for more details.
13 -- GNU General Public License for more details.
14 --
14 --
15 -- You should have received a copy of the GNU General Public License
15 -- You should have received a copy of the GNU General Public License
16 -- along with this program; if not, write to the Free Software
16 -- along with this program; if not, write to the Free Software
17 -- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 -- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 -------------------------------------------------------------------------------*/
18 -------------------------------------------------------------------------------*/
19 /*-- Author : Alexis Jeandet
19 /*-- Author : Alexis Jeandet
20 -- Mail : alexis.jeandet@member.fsf.org
20 -- Mail : alexis.jeandet@member.fsf.org
21 ----------------------------------------------------------------------------*/
21 ----------------------------------------------------------------------------*/
22 #ifndef SCIQLOP_MAINWINDOW_H
22 #ifndef SCIQLOP_MAINWINDOW_H
23 #define SCIQLOP_MAINWINDOW_H
23 #define SCIQLOP_MAINWINDOW_H
24
24
25 #include <QListWidgetItem>
25 #include <QListWidgetItem>
26 #include <QLoggingCategory>
26 #include <QLoggingCategory>
27 #include <QMainWindow>
27 #include <QMainWindow>
28 #include <QProgressBar>
28 #include <QProgressBar>
29 #include <QProgressDialog>
29 #include <QProgressDialog>
30 #include <QThread>
30 #include <QThread>
31 #include <QVBoxLayout>
31 #include <QVBoxLayout>
32 #include <QWidget>
32 #include <QWidget>
33
33
34 #include <Common/spimpl.h>
34 #include <Common/spimpl.h>
35
35
36 #include <memory>
36 #include <memory>
37
37
38 Q_DECLARE_LOGGING_CATEGORY(LOG_MainWindow)
38 Q_DECLARE_LOGGING_CATEGORY(LOG_MainWindow)
39
39
40 namespace Ui {
40 namespace Ui {
41 class MainWindow;
41 class MainWindow;
42 } // namespace Ui
42 } // namespace Ui
43
43
44
44
45 class MainWindow : public QMainWindow {
45 class MainWindow : public QMainWindow {
46 Q_OBJECT
46 Q_OBJECT
47
47
48 public:
48 public:
49 explicit MainWindow(QWidget *parent = 0);
49 explicit MainWindow(QWidget *parent = nullptr);
50 virtual ~MainWindow();
50 virtual ~MainWindow() override;
51 public slots:
51 public slots:
52
52
53 protected:
53 protected:
54 void changeEvent(QEvent *e);
54 void changeEvent(QEvent *e) override;
55 void closeEvent(QCloseEvent *event);
55 void closeEvent(QCloseEvent *event) override;
56
57 void keyPressEvent(QKeyEvent *event) override;
56
58
57 private:
59 private:
58 std::unique_ptr<Ui::MainWindow> m_Ui;
60 std::unique_ptr<Ui::MainWindow> m_Ui;
59 // QWidget *m_progressWidget;
61 // QWidget *m_progressWidget;
60 // QVBoxLayout *m_progressLayout;
62 // QVBoxLayout *m_progressLayout;
61 // QList<QLopService*> m_qlopServices;
63 // QList<QLopService*> m_qlopServices;
62 class MainWindowPrivate;
64 class MainWindowPrivate;
63 spimpl::unique_impl_ptr<MainWindowPrivate> impl;
65 spimpl::unique_impl_ptr<MainWindowPrivate> impl;
64 };
66 };
65
67
66 #endif // SCIQLOP_MAINWINDOW_H
68 #endif // SCIQLOP_MAINWINDOW_H
@@ -1,375 +1,394
1 /*------------------------------------------------------------------------------
1 /*------------------------------------------------------------------------------
2 -- This file is a part of the SciQLop Software
2 -- This file is a part of the SciQLop Software
3 -- Copyright (C) 2017, Plasma Physics Laboratory - CNRS
3 -- Copyright (C) 2017, Plasma Physics Laboratory - CNRS
4 --
4 --
5 -- This program is free software; you can redistribute it and/or modify
5 -- This program is free software; you can redistribute it and/or modify
6 -- it under the terms of the GNU General Public License as published by
6 -- it under the terms of the GNU General Public License as published by
7 -- the Free Software Foundation; either version 2 of the License, or
7 -- the Free Software Foundation; either version 2 of the License, or
8 -- (at your option) any later version.
8 -- (at your option) any later version.
9 --
9 --
10 -- This program is distributed in the hope that it will be useful,
10 -- This program is distributed in the hope that it will be useful,
11 -- but WITHOUT ANY WARRANTY; without even the implied warranty of
11 -- but WITHOUT ANY WARRANTY; without even the implied warranty of
12 -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 -- GNU General Public License for more details.
13 -- GNU General Public License for more details.
14 --
14 --
15 -- You should have received a copy of the GNU General Public License
15 -- You should have received a copy of the GNU General Public License
16 -- along with this program; if not, write to the Free Software
16 -- along with this program; if not, write to the Free Software
17 -- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 -- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 -------------------------------------------------------------------------------*/
18 -------------------------------------------------------------------------------*/
19 /*-- Author : Alexis Jeandet
19 /*-- Author : Alexis Jeandet
20 -- Mail : alexis.jeandet@member.fsf.org
20 -- Mail : alexis.jeandet@member.fsf.org
21 ----------------------------------------------------------------------------*/
21 ----------------------------------------------------------------------------*/
22 #include "MainWindow.h"
22 #include "MainWindow.h"
23 #include "ui_MainWindow.h"
23 #include "ui_MainWindow.h"
24
24
25 #include <Catalogue/CatalogueController.h>
25 #include <Catalogue/CatalogueController.h>
26 #include <Catalogue/CatalogueExplorer.h>
26 #include <Catalogue/CatalogueExplorer.h>
27 #include <DataSource/DataSourceController.h>
27 #include <DataSource/DataSourceController.h>
28 #include <DataSource/DataSourceWidget.h>
28 #include <DataSource/DataSourceWidget.h>
29 #include <Settings/SqpSettingsDialog.h>
29 #include <Settings/SqpSettingsDialog.h>
30 #include <Settings/SqpSettingsGeneralWidget.h>
30 #include <Settings/SqpSettingsGeneralWidget.h>
31 #include <SidePane/SqpSidePane.h>
31 #include <SidePane/SqpSidePane.h>
32 #include <SqpApplication.h>
32 #include <SqpApplication.h>
33 #include <Time/TimeController.h>
33 #include <Time/TimeController.h>
34 #include <TimeWidget/TimeWidget.h>
34 #include <TimeWidget/TimeWidget.h>
35 #include <Variable/Variable.h>
35 #include <Variable/Variable.h>
36 #include <Visualization/VisualizationController.h>
36 #include <Visualization/VisualizationController.h>
37
37
38 #include <QAction>
38 #include <QAction>
39 #include <QCloseEvent>
39 #include <QCloseEvent>
40 #include <QDate>
40 #include <QDate>
41 #include <QDir>
41 #include <QDir>
42 #include <QFileDialog>
42 #include <QFileDialog>
43 #include <QMessageBox>
43 #include <QMessageBox>
44 #include <QToolBar>
44 #include <QToolBar>
45 #include <QToolButton>
45 #include <QToolButton>
46 #include <memory.h>
46 #include <memory.h>
47
47
48 #include "iostream"
48 #include "iostream"
49
49
50 Q_LOGGING_CATEGORY(LOG_MainWindow, "MainWindow")
50 Q_LOGGING_CATEGORY(LOG_MainWindow, "MainWindow")
51
51
52 namespace {
52 namespace {
53 const auto LEFTMAININSPECTORWIDGETSPLITTERINDEX = 0;
53 const auto LEFTMAININSPECTORWIDGETSPLITTERINDEX = 0;
54 const auto LEFTINSPECTORSIDEPANESPLITTERINDEX = 1;
54 const auto LEFTINSPECTORSIDEPANESPLITTERINDEX = 1;
55 const auto VIEWPLITTERINDEX = 2;
55 const auto VIEWPLITTERINDEX = 2;
56 const auto RIGHTINSPECTORSIDEPANESPLITTERINDEX = 3;
56 const auto RIGHTINSPECTORSIDEPANESPLITTERINDEX = 3;
57 const auto RIGHTMAININSPECTORWIDGETSPLITTERINDEX = 4;
57 const auto RIGHTMAININSPECTORWIDGETSPLITTERINDEX = 4;
58 }
58 }
59
59
60 class MainWindow::MainWindowPrivate {
60 class MainWindow::MainWindowPrivate {
61 public:
61 public:
62 explicit MainWindowPrivate(MainWindow *mainWindow)
62 explicit MainWindowPrivate(MainWindow *mainWindow)
63 : m_LastOpenLeftInspectorSize{},
63 : m_LastOpenLeftInspectorSize{},
64 m_LastOpenRightInspectorSize{},
64 m_LastOpenRightInspectorSize{},
65 m_GeneralSettingsWidget{new SqpSettingsGeneralWidget{mainWindow}},
65 m_GeneralSettingsWidget{new SqpSettingsGeneralWidget{mainWindow}},
66 m_SettingsDialog{new SqpSettingsDialog{mainWindow}},
66 m_SettingsDialog{new SqpSettingsDialog{mainWindow}},
67 m_CatalogExplorer{new CatalogueExplorer{mainWindow}}
67 m_CatalogExplorer{new CatalogueExplorer{mainWindow}}
68 {
68 {
69 }
69 }
70
70
71 QSize m_LastOpenLeftInspectorSize;
71 QSize m_LastOpenLeftInspectorSize;
72 QSize m_LastOpenRightInspectorSize;
72 QSize m_LastOpenRightInspectorSize;
73 /// General settings widget. MainWindow has the ownership
73 /// General settings widget. MainWindow has the ownership
74 SqpSettingsGeneralWidget *m_GeneralSettingsWidget;
74 SqpSettingsGeneralWidget *m_GeneralSettingsWidget;
75 /// Settings dialog. MainWindow has the ownership
75 /// Settings dialog. MainWindow has the ownership
76 SqpSettingsDialog *m_SettingsDialog;
76 SqpSettingsDialog *m_SettingsDialog;
77 /// Catalogue dialog. MainWindow has the ownership
77 /// Catalogue dialog. MainWindow has the ownership
78 CatalogueExplorer *m_CatalogExplorer;
78 CatalogueExplorer *m_CatalogExplorer;
79
79
80 bool checkDataToSave(QWidget *parentWidget);
80 bool checkDataToSave(QWidget *parentWidget);
81 };
81 };
82
82
83 MainWindow::MainWindow(QWidget *parent)
83 MainWindow::MainWindow(QWidget *parent)
84 : QMainWindow{parent},
84 : QMainWindow{parent},
85 m_Ui{new Ui::MainWindow},
85 m_Ui{new Ui::MainWindow},
86 impl{spimpl::make_unique_impl<MainWindowPrivate>(this)}
86 impl{spimpl::make_unique_impl<MainWindowPrivate>(this)}
87 {
87 {
88 m_Ui->setupUi(this);
88 m_Ui->setupUi(this);
89
89
90 m_Ui->splitter->setCollapsible(LEFTINSPECTORSIDEPANESPLITTERINDEX, false);
90 m_Ui->splitter->setCollapsible(LEFTINSPECTORSIDEPANESPLITTERINDEX, false);
91 m_Ui->splitter->setCollapsible(RIGHTINSPECTORSIDEPANESPLITTERINDEX, false);
91 m_Ui->splitter->setCollapsible(RIGHTINSPECTORSIDEPANESPLITTERINDEX, false);
92
92
93 impl->m_CatalogExplorer->setVisualizationWidget(m_Ui->view);
93 impl->m_CatalogExplorer->setVisualizationWidget(m_Ui->view);
94
94
95
95
96
96
97
97
98
98
99 auto spacerLeftTop = new QWidget{};
99 auto spacerLeftTop = new QWidget{};
100 spacerLeftTop->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
100 spacerLeftTop->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
101
101
102 auto spacerLeftBottom = new QWidget{};
102 auto spacerLeftBottom = new QWidget{};
103 spacerLeftBottom->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
103 spacerLeftBottom->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
104
104
105
105
106 auto spacerRightTop = new QWidget{};
106 auto spacerRightTop = new QWidget{};
107 spacerRightTop->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
107 spacerRightTop->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
108
108
109 auto spacerRightBottom = new QWidget{};
109 auto spacerRightBottom = new QWidget{};
110 spacerRightBottom->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
110 spacerRightBottom->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
111
111
112
112
113 auto openInspector = [this](bool checked, bool right, auto action) {
113 auto openInspector = [this](bool checked, bool right, auto action) {
114
114
115 action->setIcon(QIcon{(checked ^ right) ? ":/icones/next.png" : ":/icones/previous.png"});
115 action->setIcon(QIcon{(checked ^ right) ? ":/icones/next.png" : ":/icones/previous.png"});
116
116
117 auto &lastInspectorSize
117 auto &lastInspectorSize
118 = right ? impl->m_LastOpenRightInspectorSize : impl->m_LastOpenLeftInspectorSize;
118 = right ? impl->m_LastOpenRightInspectorSize : impl->m_LastOpenLeftInspectorSize;
119
119
120 auto nextInspectorSize = right ? m_Ui->rightMainInspectorWidget->size()
120 auto nextInspectorSize = right ? m_Ui->rightMainInspectorWidget->size()
121 : m_Ui->leftMainInspectorWidget->size();
121 : m_Ui->leftMainInspectorWidget->size();
122
122
123 // Update of the last opened geometry
123 // Update of the last opened geometry
124 if (checked) {
124 if (checked) {
125 lastInspectorSize = nextInspectorSize;
125 lastInspectorSize = nextInspectorSize;
126 }
126 }
127
127
128 auto startSize = lastInspectorSize;
128 auto startSize = lastInspectorSize;
129 auto endSize = startSize;
129 auto endSize = startSize;
130 endSize.setWidth(0);
130 endSize.setWidth(0);
131
131
132 auto splitterInspectorIndex
132 auto splitterInspectorIndex
133 = right ? RIGHTMAININSPECTORWIDGETSPLITTERINDEX : LEFTMAININSPECTORWIDGETSPLITTERINDEX;
133 = right ? RIGHTMAININSPECTORWIDGETSPLITTERINDEX : LEFTMAININSPECTORWIDGETSPLITTERINDEX;
134
134
135 auto currentSizes = m_Ui->splitter->sizes();
135 auto currentSizes = m_Ui->splitter->sizes();
136 if (checked) {
136 if (checked) {
137 // adjust sizes individually here, e.g.
137 // adjust sizes individually here, e.g.
138 currentSizes[splitterInspectorIndex] -= lastInspectorSize.width();
138 currentSizes[splitterInspectorIndex] -= lastInspectorSize.width();
139 currentSizes[VIEWPLITTERINDEX] += lastInspectorSize.width();
139 currentSizes[VIEWPLITTERINDEX] += lastInspectorSize.width();
140 m_Ui->splitter->setSizes(currentSizes);
140 m_Ui->splitter->setSizes(currentSizes);
141 }
141 }
142 else {
142 else {
143 // adjust sizes individually here, e.g.
143 // adjust sizes individually here, e.g.
144 currentSizes[splitterInspectorIndex] += lastInspectorSize.width();
144 currentSizes[splitterInspectorIndex] += lastInspectorSize.width();
145 currentSizes[VIEWPLITTERINDEX] -= lastInspectorSize.width();
145 currentSizes[VIEWPLITTERINDEX] -= lastInspectorSize.width();
146 m_Ui->splitter->setSizes(currentSizes);
146 m_Ui->splitter->setSizes(currentSizes);
147 }
147 }
148
148
149 };
149 };
150
150
151
151
152 // //////////////// //
152 // //////////////// //
153 // Menu and Toolbar //
153 // Menu and Toolbar //
154 // //////////////// //
154 // //////////////// //
155 this->menuBar()->addAction(tr("File"));
155 this->menuBar()->addAction(tr("File"));
156 auto toolsMenu = this->menuBar()->addMenu(tr("Tools"));
156 auto toolsMenu = this->menuBar()->addMenu(tr("Tools"));
157 toolsMenu->addAction(tr("Settings..."), [this]() {
157 toolsMenu->addAction(tr("Settings..."), [this]() {
158 // Loads settings
158 // Loads settings
159 impl->m_SettingsDialog->loadSettings();
159 impl->m_SettingsDialog->loadSettings();
160
160
161 // Open settings dialog and save settings if the dialog is accepted
161 // Open settings dialog and save settings if the dialog is accepted
162 if (impl->m_SettingsDialog->exec() == QDialog::Accepted) {
162 if (impl->m_SettingsDialog->exec() == QDialog::Accepted) {
163 impl->m_SettingsDialog->saveSettings();
163 impl->m_SettingsDialog->saveSettings();
164 }
164 }
165
165
166 });
166 });
167
167
168 auto mainToolBar = this->addToolBar(QStringLiteral("MainToolBar"));
168 auto mainToolBar = this->addToolBar(QStringLiteral("MainToolBar"));
169
169
170 auto timeWidget = new TimeWidget{};
170 auto timeWidget = new TimeWidget{};
171 mainToolBar->addWidget(timeWidget);
171 mainToolBar->addWidget(timeWidget);
172
172
173 // Interaction modes
173 // Interaction modes
174 auto actionPointerMode = new QAction{QIcon(":/icones/pointer.png"), "Move", this};
174 auto actionPointerMode = new QAction{QIcon(":/icones/pointer.png"), "Move", this};
175 actionPointerMode->setCheckable(true);
175 actionPointerMode->setCheckable(true);
176 actionPointerMode->setChecked(sqpApp->plotsInteractionMode()
176 actionPointerMode->setChecked(sqpApp->plotsInteractionMode()
177 == SqpApplication::PlotsInteractionMode::None);
177 == SqpApplication::PlotsInteractionMode::None);
178 connect(actionPointerMode, &QAction::triggered,
178 connect(actionPointerMode, &QAction::triggered,
179 []() { sqpApp->setPlotsInteractionMode(SqpApplication::PlotsInteractionMode::None); });
179 []() { sqpApp->setPlotsInteractionMode(SqpApplication::PlotsInteractionMode::None); });
180
180
181 auto actionZoomMode = new QAction{QIcon(":/icones/zoom.png"), "Zoom", this};
181 auto actionZoomMode = new QAction{QIcon(":/icones/zoom.png"), "Zoom", this};
182 actionZoomMode->setCheckable(true);
182 actionZoomMode->setCheckable(true);
183 actionZoomMode->setChecked(sqpApp->plotsInteractionMode()
183 actionZoomMode->setChecked(sqpApp->plotsInteractionMode()
184 == SqpApplication::PlotsInteractionMode::ZoomBox);
184 == SqpApplication::PlotsInteractionMode::ZoomBox);
185 connect(actionZoomMode, &QAction::triggered, []() {
185 connect(actionZoomMode, &QAction::triggered, []() {
186 sqpApp->setPlotsInteractionMode(SqpApplication::PlotsInteractionMode::ZoomBox);
186 sqpApp->setPlotsInteractionMode(SqpApplication::PlotsInteractionMode::ZoomBox);
187 });
187 });
188
188
189 auto actionOrganisationMode = new QAction{QIcon(":/icones/drag.png"), "Organize", this};
189 auto actionOrganisationMode = new QAction{QIcon(":/icones/drag.png"), "Organize", this};
190 actionOrganisationMode->setCheckable(true);
190 actionOrganisationMode->setCheckable(true);
191 actionOrganisationMode->setChecked(sqpApp->plotsInteractionMode()
191 actionOrganisationMode->setChecked(sqpApp->plotsInteractionMode()
192 == SqpApplication::PlotsInteractionMode::DragAndDrop);
192 == SqpApplication::PlotsInteractionMode::DragAndDrop);
193 connect(actionOrganisationMode, &QAction::triggered, []() {
193 connect(actionOrganisationMode, &QAction::triggered, []() {
194 sqpApp->setPlotsInteractionMode(SqpApplication::PlotsInteractionMode::DragAndDrop);
194 sqpApp->setPlotsInteractionMode(SqpApplication::PlotsInteractionMode::DragAndDrop);
195 });
195 });
196
196
197 auto actionZonesMode = new QAction{QIcon(":/icones/rectangle.png"), "Zones", this};
197 auto actionZonesMode = new QAction{QIcon(":/icones/rectangle.png"), "Zones", this};
198 actionZonesMode->setCheckable(true);
198 actionZonesMode->setCheckable(true);
199 actionZonesMode->setChecked(sqpApp->plotsInteractionMode()
199 actionZonesMode->setChecked(sqpApp->plotsInteractionMode()
200 == SqpApplication::PlotsInteractionMode::SelectionZones);
200 == SqpApplication::PlotsInteractionMode::SelectionZones);
201 connect(actionZonesMode, &QAction::triggered, []() {
201 connect(actionZonesMode, &QAction::triggered, []() {
202 sqpApp->setPlotsInteractionMode(SqpApplication::PlotsInteractionMode::SelectionZones);
202 sqpApp->setPlotsInteractionMode(SqpApplication::PlotsInteractionMode::SelectionZones);
203 });
203 });
204
204
205 auto modeActionGroup = new QActionGroup{this};
205 auto modeActionGroup = new QActionGroup{this};
206 modeActionGroup->addAction(actionZoomMode);
206 modeActionGroup->addAction(actionZoomMode);
207 modeActionGroup->addAction(actionZonesMode);
207 modeActionGroup->addAction(actionZonesMode);
208 modeActionGroup->addAction(actionOrganisationMode);
208 modeActionGroup->addAction(actionOrganisationMode);
209 modeActionGroup->addAction(actionPointerMode);
209 modeActionGroup->addAction(actionPointerMode);
210 modeActionGroup->setExclusive(true);
210 modeActionGroup->setExclusive(true);
211
211
212 mainToolBar->addSeparator();
212 mainToolBar->addSeparator();
213 mainToolBar->addAction(actionPointerMode);
213 mainToolBar->addAction(actionPointerMode);
214 mainToolBar->addAction(actionZoomMode);
214 mainToolBar->addAction(actionZoomMode);
215 mainToolBar->addAction(actionOrganisationMode);
215 mainToolBar->addAction(actionOrganisationMode);
216 mainToolBar->addAction(actionZonesMode);
216 mainToolBar->addAction(actionZonesMode);
217 mainToolBar->addSeparator();
217 mainToolBar->addSeparator();
218
218
219 // Cursors
219 // Cursors
220 auto btnCursor = new QToolButton{this};
220 auto btnCursor = new QToolButton{this};
221 btnCursor->setIcon(QIcon(":/icones/cursor.png"));
221 btnCursor->setIcon(QIcon(":/icones/cursor.png"));
222 btnCursor->setText("Cursor");
222 btnCursor->setText("Cursor");
223 btnCursor->setToolTip("Cursor");
223 btnCursor->setToolTip("Cursor");
224 btnCursor->setPopupMode(QToolButton::InstantPopup);
224 btnCursor->setPopupMode(QToolButton::InstantPopup);
225 auto cursorMenu = new QMenu("CursorMenu", this);
225 auto cursorMenu = new QMenu("CursorMenu", this);
226 btnCursor->setMenu(cursorMenu);
226 btnCursor->setMenu(cursorMenu);
227
227
228 auto noCursorAction = cursorMenu->addAction("No Cursor");
228 auto noCursorAction = cursorMenu->addAction("No Cursor");
229 noCursorAction->setCheckable(true);
229 noCursorAction->setCheckable(true);
230 noCursorAction->setChecked(sqpApp->plotsCursorMode()
230 noCursorAction->setChecked(sqpApp->plotsCursorMode()
231 == SqpApplication::PlotsCursorMode::NoCursor);
231 == SqpApplication::PlotsCursorMode::NoCursor);
232 connect(noCursorAction, &QAction::triggered,
232 connect(noCursorAction, &QAction::triggered,
233 []() { sqpApp->setPlotsCursorMode(SqpApplication::PlotsCursorMode::NoCursor); });
233 []() { sqpApp->setPlotsCursorMode(SqpApplication::PlotsCursorMode::NoCursor); });
234
234
235 cursorMenu->addSeparator();
235 cursorMenu->addSeparator();
236 auto verticalCursorAction = cursorMenu->addAction("Vertical Cursor");
236 auto verticalCursorAction = cursorMenu->addAction("Vertical Cursor");
237 verticalCursorAction->setCheckable(true);
237 verticalCursorAction->setCheckable(true);
238 verticalCursorAction->setChecked(sqpApp->plotsCursorMode()
238 verticalCursorAction->setChecked(sqpApp->plotsCursorMode()
239 == SqpApplication::PlotsCursorMode::Vertical);
239 == SqpApplication::PlotsCursorMode::Vertical);
240 connect(verticalCursorAction, &QAction::triggered,
240 connect(verticalCursorAction, &QAction::triggered,
241 []() { sqpApp->setPlotsCursorMode(SqpApplication::PlotsCursorMode::Vertical); });
241 []() { sqpApp->setPlotsCursorMode(SqpApplication::PlotsCursorMode::Vertical); });
242
242
243 auto temporalCursorAction = cursorMenu->addAction("Temporal Cursor");
243 auto temporalCursorAction = cursorMenu->addAction("Temporal Cursor");
244 temporalCursorAction->setCheckable(true);
244 temporalCursorAction->setCheckable(true);
245 temporalCursorAction->setChecked(sqpApp->plotsCursorMode()
245 temporalCursorAction->setChecked(sqpApp->plotsCursorMode()
246 == SqpApplication::PlotsCursorMode::Temporal);
246 == SqpApplication::PlotsCursorMode::Temporal);
247 connect(temporalCursorAction, &QAction::triggered,
247 connect(temporalCursorAction, &QAction::triggered,
248 []() { sqpApp->setPlotsCursorMode(SqpApplication::PlotsCursorMode::Temporal); });
248 []() { sqpApp->setPlotsCursorMode(SqpApplication::PlotsCursorMode::Temporal); });
249
249
250 auto horizontalCursorAction = cursorMenu->addAction("Horizontal Cursor");
250 auto horizontalCursorAction = cursorMenu->addAction("Horizontal Cursor");
251 horizontalCursorAction->setCheckable(true);
251 horizontalCursorAction->setCheckable(true);
252 horizontalCursorAction->setChecked(sqpApp->plotsCursorMode()
252 horizontalCursorAction->setChecked(sqpApp->plotsCursorMode()
253 == SqpApplication::PlotsCursorMode::Horizontal);
253 == SqpApplication::PlotsCursorMode::Horizontal);
254 connect(horizontalCursorAction, &QAction::triggered,
254 connect(horizontalCursorAction, &QAction::triggered,
255 []() { sqpApp->setPlotsCursorMode(SqpApplication::PlotsCursorMode::Horizontal); });
255 []() { sqpApp->setPlotsCursorMode(SqpApplication::PlotsCursorMode::Horizontal); });
256
256
257 auto crossCursorAction = cursorMenu->addAction("Cross Cursor");
257 auto crossCursorAction = cursorMenu->addAction("Cross Cursor");
258 crossCursorAction->setCheckable(true);
258 crossCursorAction->setCheckable(true);
259 crossCursorAction->setChecked(sqpApp->plotsCursorMode()
259 crossCursorAction->setChecked(sqpApp->plotsCursorMode()
260 == SqpApplication::PlotsCursorMode::Cross);
260 == SqpApplication::PlotsCursorMode::Cross);
261 connect(crossCursorAction, &QAction::triggered,
261 connect(crossCursorAction, &QAction::triggered,
262 []() { sqpApp->setPlotsCursorMode(SqpApplication::PlotsCursorMode::Cross); });
262 []() { sqpApp->setPlotsCursorMode(SqpApplication::PlotsCursorMode::Cross); });
263
263
264 mainToolBar->addWidget(btnCursor);
264 mainToolBar->addWidget(btnCursor);
265
265
266 auto cursorModeActionGroup = new QActionGroup{this};
266 auto cursorModeActionGroup = new QActionGroup{this};
267 cursorModeActionGroup->setExclusive(true);
267 cursorModeActionGroup->setExclusive(true);
268 cursorModeActionGroup->addAction(noCursorAction);
268 cursorModeActionGroup->addAction(noCursorAction);
269 cursorModeActionGroup->addAction(verticalCursorAction);
269 cursorModeActionGroup->addAction(verticalCursorAction);
270 cursorModeActionGroup->addAction(temporalCursorAction);
270 cursorModeActionGroup->addAction(temporalCursorAction);
271 cursorModeActionGroup->addAction(horizontalCursorAction);
271 cursorModeActionGroup->addAction(horizontalCursorAction);
272 cursorModeActionGroup->addAction(crossCursorAction);
272 cursorModeActionGroup->addAction(crossCursorAction);
273
273
274 // Catalog
274 // Catalog
275 mainToolBar->addSeparator();
275 mainToolBar->addSeparator();
276 mainToolBar->addAction(QIcon(":/icones/catalogue.png"), "Catalogues",
276 mainToolBar->addAction(QIcon(":/icones/catalogue.png"), "Catalogues",
277 [this]() { impl->m_CatalogExplorer->show(); });
277 [this]() { impl->m_CatalogExplorer->show(); });
278
278
279 // //////// //
279 // //////// //
280 // Settings //
280 // Settings //
281 // //////// //
281 // //////// //
282
282
283 // Registers "general settings" widget to the settings dialog
283 // Registers "general settings" widget to the settings dialog
284 impl->m_SettingsDialog->registerWidget(QStringLiteral("General"),
284 impl->m_SettingsDialog->registerWidget(QStringLiteral("General"),
285 impl->m_GeneralSettingsWidget);
285 impl->m_GeneralSettingsWidget);
286
286
287 // /////////// //
287 // /////////// //
288 // Connections //
288 // Connections //
289 // /////////// //
289 // /////////// //
290
290
291 // Controllers / controllers connections
291 // Controllers / controllers connections
292 // connect(&sqpApp->timeController(), SIGNAL(timeUpdated(DateTimeRange)), &sqpApp->variableController(),
292 // connect(&sqpApp->timeController(), SIGNAL(timeUpdated(DateTimeRange)), &sqpApp->variableController(),
293 // SLOT(onDateTimeOnSelection(DateTimeRange)));
293 // SLOT(onDateTimeOnSelection(DateTimeRange)));
294
294
295 // Widgets / controllers connections
295 // Widgets / controllers connections
296
296
297 // DataSource
297 // DataSource
298 connect(&sqpApp->dataSourceController(), SIGNAL(dataSourceItemSet(DataSourceItem *)),
298 connect(&sqpApp->dataSourceController(), SIGNAL(dataSourceItemSet(DataSourceItem *)),
299 m_Ui->dataSourceWidget, SLOT(addDataSource(DataSourceItem *)));
299 m_Ui->dataSourceWidget, SLOT(addDataSource(DataSourceItem *)));
300
300
301 // Time
301 // Time
302 connect(timeWidget, SIGNAL(timeUpdated(DateTimeRange)), &sqpApp->timeController(),
302 connect(timeWidget, SIGNAL(timeUpdated(DateTimeRange)), &sqpApp->timeController(),
303 SLOT(onTimeToUpdate(DateTimeRange)));
303 SLOT(onTimeToUpdate(DateTimeRange)));
304
304
305 // Visualization
305 // Visualization
306 connect(&sqpApp->visualizationController(),
306 connect(&sqpApp->visualizationController(),
307 SIGNAL(variableAboutToBeDeleted(std::shared_ptr<Variable>)), m_Ui->view,
307 SIGNAL(variableAboutToBeDeleted(std::shared_ptr<Variable>)), m_Ui->view,
308 SLOT(onVariableAboutToBeDeleted(std::shared_ptr<Variable>)));
308 SLOT(onVariableAboutToBeDeleted(std::shared_ptr<Variable>)));
309
309
310 connect(&sqpApp->visualizationController(),
310 connect(&sqpApp->visualizationController(),
311 SIGNAL(rangeChanged(std::shared_ptr<Variable>, const DateTimeRange &)), m_Ui->view,
311 SIGNAL(rangeChanged(std::shared_ptr<Variable>, const DateTimeRange &)), m_Ui->view,
312 SLOT(onRangeChanged(std::shared_ptr<Variable>, const DateTimeRange &)));
312 SLOT(onRangeChanged(std::shared_ptr<Variable>, const DateTimeRange &)));
313
313
314 // Widgets / widgets connections
314 // Widgets / widgets connections
315
315
316 // For the following connections, we use DirectConnection to allow each widget that can
316 // For the following connections, we use DirectConnection to allow each widget that can
317 // potentially attach a menu to the variable's menu to do so before this menu is displayed.
317 // potentially attach a menu to the variable's menu to do so before this menu is displayed.
318 // The order of connections is also important, since it determines the order in which each
318 // The order of connections is also important, since it determines the order in which each
319 // widget will attach its menu
319 // widget will attach its menu
320 connect(
320 connect(
321 m_Ui->variableInspectorWidget,
321 m_Ui->variableInspectorWidget,
322 SIGNAL(tableMenuAboutToBeDisplayed(QMenu *, const QVector<std::shared_ptr<Variable> > &)),
322 SIGNAL(tableMenuAboutToBeDisplayed(QMenu *, const QVector<std::shared_ptr<Variable> > &)),
323 m_Ui->view, SLOT(attachVariableMenu(QMenu *, const QVector<std::shared_ptr<Variable> > &)),
323 m_Ui->view, SLOT(attachVariableMenu(QMenu *, const QVector<std::shared_ptr<Variable> > &)),
324 Qt::DirectConnection);
324 Qt::DirectConnection);
325 }
325 }
326
326
327 MainWindow::~MainWindow()
327 MainWindow::~MainWindow()
328 {
328 {
329 }
329 }
330
330
331 void MainWindow::changeEvent(QEvent *e)
331 void MainWindow::changeEvent(QEvent *e)
332 {
332 {
333 QMainWindow::changeEvent(e);
333 QMainWindow::changeEvent(e);
334 switch (e->type()) {
334 switch (e->type()) {
335 case QEvent::LanguageChange:
335 case QEvent::LanguageChange:
336 m_Ui->retranslateUi(this);
336 m_Ui->retranslateUi(this);
337 break;
337 break;
338 default:
338 default:
339 break;
339 break;
340 }
340 }
341 }
341 }
342
342
343 void MainWindow::closeEvent(QCloseEvent *event)
343 void MainWindow::closeEvent(QCloseEvent *event)
344 {
344 {
345 if (!impl->checkDataToSave(this)) {
345 if (!impl->checkDataToSave(this)) {
346 event->ignore();
346 event->ignore();
347 }
347 }
348 else {
348 else {
349 event->accept();
349 event->accept();
350 }
350 }
351 }
351 }
352
352
353 void MainWindow::keyPressEvent(QKeyEvent *event)
354 {
355 switch (event->key())
356 {
357 case Qt::Key_F11:
358 if(this->isFullScreen())
359 {
360 this->showNormal();
361 }
362 else
363 {
364 this->showFullScreen();
365 }
366 break;
367 default:
368 break;
369 }
370 }
371
353 bool MainWindow::MainWindowPrivate::checkDataToSave(QWidget *parentWidget)
372 bool MainWindow::MainWindowPrivate::checkDataToSave(QWidget *parentWidget)
354 {
373 {
355 auto hasChanges = sqpApp->catalogueController().hasChanges();
374 auto hasChanges = sqpApp->catalogueController().hasChanges();
356 if (hasChanges) {
375 if (hasChanges) {
357 // There are some unsaved changes
376 // There are some unsaved changes
358 switch (QMessageBox::question(
377 switch (QMessageBox::question(
359 parentWidget, tr("Save changes"),
378 parentWidget, tr("Save changes"),
360 tr("The catalogue controller has unsaved changes.\nDo you want to save them ?"),
379 tr("The catalogue controller has unsaved changes.\nDo you want to save them ?"),
361 QMessageBox::SaveAll | QMessageBox::Discard | QMessageBox::Cancel,
380 QMessageBox::SaveAll | QMessageBox::Discard | QMessageBox::Cancel,
362 QMessageBox::SaveAll)) {
381 QMessageBox::SaveAll)) {
363 case QMessageBox::SaveAll:
382 case QMessageBox::SaveAll:
364 sqpApp->catalogueController().saveAll();
383 sqpApp->catalogueController().saveAll();
365 break;
384 break;
366 case QMessageBox::Discard:
385 case QMessageBox::Discard:
367 break;
386 break;
368 case QMessageBox::Cancel:
387 case QMessageBox::Cancel:
369 default:
388 default:
370 return false;
389 return false;
371 }
390 }
372 }
391 }
373
392
374 return true;
393 return true;
375 }
394 }
@@ -1,1368 +1,1370
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 QCPRange m_lastXRange;
88 QCPRange m_lastXRange;
89 QCPRange m_lastYRange;
89 QCPRange m_lastYRange;
90 /// Delegate used to attach rendering features to the plot
90 /// Delegate used to attach rendering features to the plot
91 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
91 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
92
92
93 QCPItemRect* m_DrawingZoomRect = nullptr;
93 QCPItemRect* m_DrawingZoomRect = nullptr;
94 QStack<QPair<QCPRange, QCPRange> > m_ZoomStack;
94 QStack<QPair<QCPRange, QCPRange> > m_ZoomStack;
95
95
96 std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr;
96 std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr;
97 std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr;
97 std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr;
98
98
99 VisualizationSelectionZoneItem *m_DrawingZone = nullptr;
99 VisualizationSelectionZoneItem *m_DrawingZone = nullptr;
100 VisualizationSelectionZoneItem *m_HoveredZone = nullptr;
100 VisualizationSelectionZoneItem *m_HoveredZone = nullptr;
101 QVector<VisualizationSelectionZoneItem *> m_SelectionZones;
101 QVector<VisualizationSelectionZoneItem *> m_SelectionZones;
102
102
103 bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even
103 bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even
104
104
105 bool m_VariableAutoRangeOnInit = true;
105 bool m_VariableAutoRangeOnInit = true;
106
106
107 inline void enterPlotDrag(const QPoint& position)
107 inline void enterPlotDrag(const QPoint& position)
108 {
108 {
109 m_lastMousePos = m_plot->mapFromParent(position);
109 m_lastMousePos = m_plot->mapFromParent(position);
110 m_lastXRange = m_plot->xAxis->range();
110 m_lastXRange = m_plot->xAxis->range();
111 m_lastYRange = m_plot->yAxis->range();
111 m_lastYRange = m_plot->yAxis->range();
112
112
113 }
113 }
114
114
115 inline bool isDrawingZoomRect(){return m_DrawingZoomRect!=nullptr;}
115 inline bool isDrawingZoomRect(){return m_DrawingZoomRect!=nullptr;}
116 void updateZoomRect(const QPoint& newPos)
116 void updateZoomRect(const QPoint& newPos)
117 {
117 {
118 QPointF pos{m_plot->xAxis->pixelToCoord(newPos.x()), m_plot->yAxis->pixelToCoord(newPos.y())};
118 QPointF pos{m_plot->xAxis->pixelToCoord(newPos.x()), m_plot->yAxis->pixelToCoord(newPos.y())};
119 m_DrawingZoomRect->bottomRight->setCoords(pos);
119 m_DrawingZoomRect->bottomRight->setCoords(pos);
120 m_plot->replot(QCustomPlot::rpQueuedReplot);
120 m_plot->replot(QCustomPlot::rpQueuedReplot);
121 }
121 }
122
122
123 void applyZoomRect()
123 void applyZoomRect()
124 {
124 {
125 auto axisX = m_plot->axisRect()->axis(QCPAxis::atBottom);
125 auto axisX = m_plot->axisRect()->axis(QCPAxis::atBottom);
126 auto axisY = m_plot->axisRect()->axis(QCPAxis::atLeft);
126 auto axisY = m_plot->axisRect()->axis(QCPAxis::atLeft);
127
127
128 auto newAxisXRange = QCPRange{m_DrawingZoomRect->topLeft->coords().x(),
128 auto newAxisXRange = QCPRange{m_DrawingZoomRect->topLeft->coords().x(),
129 m_DrawingZoomRect->bottomRight->coords().x()};
129 m_DrawingZoomRect->bottomRight->coords().x()};
130
130
131 auto newAxisYRange = QCPRange{m_DrawingZoomRect->topLeft->coords().y(),
131 auto newAxisYRange = QCPRange{m_DrawingZoomRect->topLeft->coords().y(),
132 m_DrawingZoomRect->bottomRight->coords().y()};
132 m_DrawingZoomRect->bottomRight->coords().y()};
133
133
134 removeDrawingRect();
134 removeDrawingRect();
135
135
136 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
136 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
137 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
137 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
138 m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
138 m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
139 axisX->setRange(newAxisXRange);
139 axisX->setRange(newAxisXRange);
140 axisY->setRange(newAxisYRange);
140 axisY->setRange(newAxisYRange);
141
141
142 m_plot->replot(QCustomPlot::rpQueuedReplot);
142 m_plot->replot(QCustomPlot::rpQueuedReplot);
143 }
143 }
144 }
144 }
145
145
146 inline bool isDrawingZoneRect(){return m_DrawingZone!=nullptr;}
146 inline bool isDrawingZoneRect(){return m_DrawingZone!=nullptr;}
147 void updateZoneRect(const QPoint& newPos)
147 void updateZoneRect(const QPoint& newPos)
148 {
148 {
149 m_DrawingZone->setEnd(m_plot->xAxis->pixelToCoord(newPos.x()));
149 m_DrawingZone->setEnd(m_plot->xAxis->pixelToCoord(newPos.x()));
150 m_plot->replot(QCustomPlot::rpQueuedReplot);
150 m_plot->replot(QCustomPlot::rpQueuedReplot);
151 }
151 }
152
152
153 void startDrawingRect(const QPoint &pos)
153 void startDrawingRect(const QPoint &pos)
154 {
154 {
155 removeDrawingRect();
155 removeDrawingRect();
156
156
157 auto axisPos = posToAxisPos(pos);
157 auto axisPos = posToAxisPos(pos);
158
158
159 m_DrawingZoomRect = new QCPItemRect{m_plot};
159 m_DrawingZoomRect = new QCPItemRect{m_plot};
160 QPen p;
160 QPen p;
161 p.setWidth(2);
161 p.setWidth(2);
162 m_DrawingZoomRect->setPen(p);
162 m_DrawingZoomRect->setPen(p);
163
163
164 m_DrawingZoomRect->topLeft->setCoords(axisPos);
164 m_DrawingZoomRect->topLeft->setCoords(axisPos);
165 m_DrawingZoomRect->bottomRight->setCoords(axisPos);
165 m_DrawingZoomRect->bottomRight->setCoords(axisPos);
166 }
166 }
167
167
168 void removeDrawingRect()
168 void removeDrawingRect()
169 {
169 {
170 if (m_DrawingZoomRect) {
170 if (m_DrawingZoomRect) {
171 m_plot->removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot
171 m_plot->removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot
172 m_DrawingZoomRect = nullptr;
172 m_DrawingZoomRect = nullptr;
173 m_plot->replot(QCustomPlot::rpQueuedReplot);
173 m_plot->replot(QCustomPlot::rpQueuedReplot);
174 }
174 }
175 }
175 }
176
176
177 void selectZone(const QPoint &pos)
177 void selectZone(const QPoint &pos)
178 {
178 {
179 auto zoneAtPos = selectionZoneAt(pos);
179 auto zoneAtPos = selectionZoneAt(pos);
180 setSelectionZonesEditionEnabled(sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones);
180 setSelectionZonesEditionEnabled(sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones);
181 }
181 }
182
182
183 void startDrawingZone(const QPoint &pos)
183 void startDrawingZone(const QPoint &pos)
184 {
184 {
185 endDrawingZone();
185 endDrawingZone();
186
186
187 auto axisPos = posToAxisPos(pos);
187 auto axisPos = posToAxisPos(pos);
188
188
189 m_DrawingZone = new VisualizationSelectionZoneItem{m_plot};
189 m_DrawingZone = new VisualizationSelectionZoneItem{m_plot};
190 m_DrawingZone->setRange(axisPos.x(), axisPos.x());
190 m_DrawingZone->setRange(axisPos.x(), axisPos.x());
191 m_DrawingZone->setEditionEnabled(false);
191 m_DrawingZone->setEditionEnabled(false);
192 }
192 }
193
193
194 void endDrawingZone()
194 void endDrawingZone()
195 {
195 {
196 if (m_DrawingZone) {
196 if (m_DrawingZone) {
197 auto drawingZoneRange = m_DrawingZone->range();
197 auto drawingZoneRange = m_DrawingZone->range();
198 if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0) {
198 if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0) {
199 m_DrawingZone->setEditionEnabled(true);
199 m_DrawingZone->setEditionEnabled(true);
200 addSelectionZone(m_DrawingZone);
200 addSelectionZone(m_DrawingZone);
201 }
201 }
202 else {
202 else {
203 m_plot->removeItem(m_DrawingZone);
203 m_plot->removeItem(m_DrawingZone);
204 }
204 }
205
205
206 m_plot->replot(QCustomPlot::rpQueuedReplot);
206 m_plot->replot(QCustomPlot::rpQueuedReplot);
207 m_DrawingZone = nullptr;
207 m_DrawingZone = nullptr;
208 }
208 }
209 }
209 }
210
210
211 void moveSelectionZone(const QPoint& destination)
211 void moveSelectionZone(const QPoint& destination)
212 {
212 {
213 /*
213 /*
214 * I give up on this for now
214 * I give up on this for now
215 * @TODO implement this, the difficulty is that selection zones have their own
215 * @TODO implement this, the difficulty is that selection zones have their own
216 * event handling code which seems to rely on QCP GUI event handling propagation
216 * event handling code which seems to rely on QCP GUI event handling propagation
217 * which was a realy bad design choice.
217 * which was a realy bad design choice.
218 */
218 */
219 }
219 }
220
220
221 void setSelectionZonesEditionEnabled(bool value)
221 void setSelectionZonesEditionEnabled(bool value)
222 {
222 {
223 for (auto s : m_SelectionZones) {
223 for (auto s : m_SelectionZones) {
224 s->setEditionEnabled(value);
224 s->setEditionEnabled(value);
225 }
225 }
226 }
226 }
227
227
228 void addSelectionZone(VisualizationSelectionZoneItem *zone) { m_SelectionZones << zone; }
228 void addSelectionZone(VisualizationSelectionZoneItem *zone) { m_SelectionZones << zone; }
229
229
230 VisualizationSelectionZoneItem *selectionZoneAt(const QPoint &pos) const
230 VisualizationSelectionZoneItem *selectionZoneAt(const QPoint &pos) const
231 {
231 {
232 VisualizationSelectionZoneItem *selectionZoneItemUnderCursor = nullptr;
232 VisualizationSelectionZoneItem *selectionZoneItemUnderCursor = nullptr;
233 auto minDistanceToZone = -1;
233 auto minDistanceToZone = -1;
234 for (auto zone : m_SelectionZones) {
234 for (auto zone : m_SelectionZones) {
235 auto distanceToZone = zone->selectTest(pos, false);
235 auto distanceToZone = zone->selectTest(pos, false);
236 if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone)
236 if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone)
237 && distanceToZone >= 0 && distanceToZone < m_plot->selectionTolerance()) {
237 && distanceToZone >= 0 && distanceToZone < m_plot->selectionTolerance()) {
238 selectionZoneItemUnderCursor = zone;
238 selectionZoneItemUnderCursor = zone;
239 }
239 }
240 }
240 }
241
241
242 return selectionZoneItemUnderCursor;
242 return selectionZoneItemUnderCursor;
243 }
243 }
244
244
245 QVector<VisualizationSelectionZoneItem *> selectionZonesAt(const QPoint &pos,
245 QVector<VisualizationSelectionZoneItem *> selectionZonesAt(const QPoint &pos,
246 const QCustomPlot &plot) const
246 const QCustomPlot &plot) const
247 {
247 {
248 QVector<VisualizationSelectionZoneItem *> zones;
248 QVector<VisualizationSelectionZoneItem *> zones;
249 for (auto zone : m_SelectionZones) {
249 for (auto zone : m_SelectionZones) {
250 auto distanceToZone = zone->selectTest(pos, false);
250 auto distanceToZone = zone->selectTest(pos, false);
251 if (distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
251 if (distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
252 zones << zone;
252 zones << zone;
253 }
253 }
254 }
254 }
255
255
256 return zones;
256 return zones;
257 }
257 }
258
258
259 void moveSelectionZoneOnTop(VisualizationSelectionZoneItem *zone, QCustomPlot &plot)
259 void moveSelectionZoneOnTop(VisualizationSelectionZoneItem *zone, QCustomPlot &plot)
260 {
260 {
261 if (!m_SelectionZones.isEmpty() && m_SelectionZones.last() != zone) {
261 if (!m_SelectionZones.isEmpty() && m_SelectionZones.last() != zone) {
262 zone->moveToTop();
262 zone->moveToTop();
263 m_SelectionZones.removeAll(zone);
263 m_SelectionZones.removeAll(zone);
264 m_SelectionZones.append(zone);
264 m_SelectionZones.append(zone);
265 }
265 }
266 }
266 }
267
267
268 QPointF posToAxisPos(const QPoint &pos) const
268 QPointF posToAxisPos(const QPoint &pos) const
269 {
269 {
270 auto axisX = m_plot->axisRect()->axis(QCPAxis::atBottom);
270 auto axisX = m_plot->axisRect()->axis(QCPAxis::atBottom);
271 auto axisY = m_plot->axisRect()->axis(QCPAxis::atLeft);
271 auto axisY = m_plot->axisRect()->axis(QCPAxis::atLeft);
272 return QPointF{axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y())};
272 return QPointF{axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y())};
273 }
273 }
274
274
275 bool pointIsInAxisRect(const QPointF &axisPoint, QCustomPlot &plot) const
275 bool pointIsInAxisRect(const QPointF &axisPoint, QCustomPlot &plot) const
276 {
276 {
277 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
277 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
278 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
278 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
279 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
279 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
280 }
280 }
281
281
282 inline QCPRange _pixDistanceToRange(double pos1, double pos2, QCPAxis *axis)
282 inline QCPRange _pixDistanceToRange(double pos1, double pos2, QCPAxis *axis)
283 {
283 {
284 if (axis->scaleType() == QCPAxis::stLinear)
284 if (axis->scaleType() == QCPAxis::stLinear)
285 {
285 {
286 auto diff = axis->pixelToCoord(pos1) - axis->pixelToCoord(pos2);
286 auto diff = axis->pixelToCoord(pos1) - axis->pixelToCoord(pos2);
287 return QCPRange{axis->range().lower + diff, axis->range().upper + diff};
287 return QCPRange{axis->range().lower + diff, axis->range().upper + diff};
288 }
288 }
289 else
289 else
290 {
290 {
291 auto diff = axis->pixelToCoord(pos1) / axis->pixelToCoord(pos2);
291 auto diff = axis->pixelToCoord(pos1) / axis->pixelToCoord(pos2);
292 return QCPRange{axis->range().lower * diff, axis->range().upper * diff};
292 return QCPRange{axis->range().lower * diff, axis->range().upper * diff};
293 }
293 }
294 }
294 }
295
295
296 void setRange(const DateTimeRange &newRange)
296 void setRange(const DateTimeRange &newRange)
297 {
297 {
298 if (m_Flags.testFlag(GraphFlag::EnableAcquisition))
298 if (m_Flags.testFlag(GraphFlag::EnableAcquisition))
299 {
299 {
300 for (auto it = m_VariableToPlotMultiMap.begin(),
300 for (auto it = m_VariableToPlotMultiMap.begin(),
301 end = m_VariableToPlotMultiMap.end();
301 end = m_VariableToPlotMultiMap.end();
302 it != end; it = m_VariableToPlotMultiMap.upper_bound(it->first))
302 it != end; it = m_VariableToPlotMultiMap.upper_bound(it->first))
303 {
303 {
304 sqpApp->variableController().asyncChangeRange(it->first, newRange);
304 sqpApp->variableController().asyncChangeRange(it->first, newRange);
305 }
305 }
306 }
306 }
307 }
307 }
308
308
309 void setRange(const QCPRange &newRange)
309 void setRange(const QCPRange &newRange)
310 {
310 {
311 auto graphRange = DateTimeRange{newRange.lower, newRange.upper};
311 auto graphRange = DateTimeRange{newRange.lower, newRange.upper};
312 setRange(graphRange);
312 setRange(graphRange);
313 }
313 }
314
314
315 std::tuple<double,double> moveGraph(const QPoint& destination)
315 std::tuple<double,double> moveGraph(const QPoint& destination)
316 {
316 {
317 auto currentPos = m_plot->mapFromParent(destination);
317 auto currentPos = m_plot->mapFromParent(destination);
318 auto xAxis = m_plot->axisRect()->rangeDragAxis(Qt::Horizontal);
318 auto xAxis = m_plot->axisRect()->rangeDragAxis(Qt::Horizontal);
319 auto yAxis = m_plot->axisRect()->rangeDragAxis(Qt::Vertical);
319 auto yAxis = m_plot->axisRect()->rangeDragAxis(Qt::Vertical);
320 auto oldXRange = xAxis->range();
320 auto oldXRange = xAxis->range();
321 auto oldYRange = yAxis->range();
321 auto oldYRange = yAxis->range();
322 double dx = xAxis->pixelToCoord(m_lastMousePos.x()) - xAxis->pixelToCoord(currentPos.x());
322 double dx = xAxis->pixelToCoord(m_lastMousePos.x()) - xAxis->pixelToCoord(currentPos.x());
323 xAxis->setRange(m_lastXRange.lower+dx, m_lastXRange.upper+dx);
323 xAxis->setRange(m_lastXRange.lower+dx, m_lastXRange.upper+dx);
324 if(yAxis->scaleType() == QCPAxis::stLinear)
324 if(yAxis->scaleType() == QCPAxis::stLinear)
325 {
325 {
326 double dy = yAxis->pixelToCoord(m_lastMousePos.y()) - yAxis->pixelToCoord(currentPos.y());
326 double dy = yAxis->pixelToCoord(m_lastMousePos.y()) - yAxis->pixelToCoord(currentPos.y());
327 yAxis->setRange(m_lastYRange.lower+dy, m_lastYRange.upper+dy);
327 yAxis->setRange(m_lastYRange.lower+dy, m_lastYRange.upper+dy);
328 }
328 }
329 else
329 else
330 {
330 {
331 double dy = yAxis->pixelToCoord(m_lastMousePos.y()) / yAxis->pixelToCoord(currentPos.y());
331 double dy = yAxis->pixelToCoord(m_lastMousePos.y()) / yAxis->pixelToCoord(currentPos.y());
332 yAxis->setRange(m_lastYRange.lower*dy, m_lastYRange.upper*dy);
332 yAxis->setRange(m_lastYRange.lower*dy, m_lastYRange.upper*dy);
333 }
333 }
334 auto newXRange = xAxis->range();
334 auto newXRange = xAxis->range();
335 auto newYRange = yAxis->range();
335 auto newYRange = yAxis->range();
336 setRange(xAxis->range());
336 setRange(xAxis->range());
337 m_plot->replot(QCustomPlot::rpQueuedReplot);
337 m_plot->replot(QCustomPlot::rpQueuedReplot);
338 //m_lastMousePos = currentPos;
338 //m_lastMousePos = currentPos;
339 return {newXRange.lower - oldXRange.lower, newYRange.lower - oldYRange.lower};
339 return {newXRange.lower - oldXRange.lower, newYRange.lower - oldYRange.lower};
340 }
340 }
341
341
342 void zoom(double factor, int center, Qt::Orientation orientation)
342 void zoom(double factor, int center, Qt::Orientation orientation)
343 {
343 {
344 QCPAxis *axis = m_plot->axisRect()->rangeZoomAxis(orientation);
344 QCPAxis *axis = m_plot->axisRect()->rangeZoomAxis(orientation);
345 axis->scaleRange(factor, axis->pixelToCoord(center));
345 axis->scaleRange(factor, axis->pixelToCoord(center));
346 if (orientation == Qt::Horizontal)
346 if (orientation == Qt::Horizontal)
347 setRange(axis->range());
347 setRange(axis->range());
348 m_plot->replot(QCustomPlot::rpQueuedReplot);
348 m_plot->replot(QCustomPlot::rpQueuedReplot);
349 }
349 }
350
350
351 void move(double dx, double dy)
351 void move(double dx, double dy)
352 {
352 {
353 auto xAxis = m_plot->axisRect()->rangeDragAxis(Qt::Horizontal);
353 auto xAxis = m_plot->axisRect()->rangeDragAxis(Qt::Horizontal);
354 auto yAxis = m_plot->axisRect()->rangeDragAxis(Qt::Vertical);
354 auto yAxis = m_plot->axisRect()->rangeDragAxis(Qt::Vertical);
355 xAxis->setRange(QCPRange(xAxis->range().lower+dx, xAxis->range().upper+dx));
355 xAxis->setRange(QCPRange(xAxis->range().lower+dx, xAxis->range().upper+dx));
356 yAxis->setRange(QCPRange(yAxis->range().lower+dy, yAxis->range().upper+dy));
356 yAxis->setRange(QCPRange(yAxis->range().lower+dy, yAxis->range().upper+dy));
357 setRange(xAxis->range());
357 setRange(xAxis->range());
358 m_plot->replot(QCustomPlot::rpQueuedReplot);
358 m_plot->replot(QCustomPlot::rpQueuedReplot);
359 }
359 }
360
360
361 void move(double factor, Qt::Orientation orientation)
361 void move(double factor, Qt::Orientation orientation)
362 {
362 {
363 auto oldRange = m_plot->xAxis->range();
363 auto oldRange = m_plot->xAxis->range();
364 QCPAxis *axis = m_plot->axisRect()->rangeDragAxis(orientation);
364 QCPAxis *axis = m_plot->axisRect()->rangeDragAxis(orientation);
365 if (m_plot->xAxis->scaleType() == QCPAxis::stLinear) {
365 if (m_plot->xAxis->scaleType() == QCPAxis::stLinear) {
366 double rg = (axis->range().upper - axis->range().lower) * (factor / 10);
366 double rg = (axis->range().upper - axis->range().lower) * (factor / 10);
367 axis->setRange(axis->range().lower + (rg), axis->range().upper + (rg));
367 axis->setRange(axis->range().lower + (rg), axis->range().upper + (rg));
368 }
368 }
369 else if (m_plot->xAxis->scaleType() == QCPAxis::stLogarithmic) {
369 else if (m_plot->xAxis->scaleType() == QCPAxis::stLogarithmic) {
370 int start = 0, stop = 0;
370 int start = 0, stop = 0;
371 double diff = 0.;
371 double diff = 0.;
372 if (factor > 0.0) {
372 if (factor > 0.0) {
373 stop = m_plot->width() * factor / 10;
373 stop = m_plot->width() * factor / 10;
374 start = 2 * m_plot->width() * factor / 10;
374 start = 2 * m_plot->width() * factor / 10;
375 }
375 }
376 if (factor < 0.0) {
376 if (factor < 0.0) {
377 factor *= -1.0;
377 factor *= -1.0;
378 start = m_plot->width() * factor / 10;
378 start = m_plot->width() * factor / 10;
379 stop = 2 * m_plot->width() * factor / 10;
379 stop = 2 * m_plot->width() * factor / 10;
380 }
380 }
381 diff = axis->pixelToCoord(start) / axis->pixelToCoord(stop);
381 diff = axis->pixelToCoord(start) / axis->pixelToCoord(stop);
382 axis->setRange(m_plot->axisRect()->rangeDragAxis(orientation)->range().lower * diff,
382 axis->setRange(m_plot->axisRect()->rangeDragAxis(orientation)->range().lower * diff,
383 m_plot->axisRect()->rangeDragAxis(orientation)->range().upper * diff);
383 m_plot->axisRect()->rangeDragAxis(orientation)->range().upper * diff);
384 }
384 }
385 if (orientation == Qt::Horizontal)
385 if (orientation == Qt::Horizontal)
386 setRange(axis->range());
386 setRange(axis->range());
387 m_plot->replot(QCustomPlot::rpQueuedReplot);
387 m_plot->replot(QCustomPlot::rpQueuedReplot);
388 }
388 }
389 };
389 };
390
390
391 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
391 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
392 : VisualizationDragWidget{parent},
392 : VisualizationDragWidget{parent},
393 ui{new Ui::VisualizationGraphWidget},
393 ui{new Ui::VisualizationGraphWidget},
394 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
394 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
395 {
395 {
396 ui->setupUi(this);
396 ui->setupUi(this);
397 this->layout()->addWidget(impl->m_plot);
397 this->layout()->addWidget(impl->m_plot);
398 // 'Close' options : widget is deleted when closed
398 // 'Close' options : widget is deleted when closed
399 setAttribute(Qt::WA_DeleteOnClose);
399 setAttribute(Qt::WA_DeleteOnClose);
400
400
401 // The delegate must be initialized after the ui as it uses the plot
401 // The delegate must be initialized after the ui as it uses the plot
402 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
402 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
403
403
404 // Init the cursors
404 // Init the cursors
405 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
405 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
406 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
406 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
407 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
407 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
408 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
408 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
409
409
410 this->setFocusPolicy(Qt::WheelFocus);
410 this->setFocusPolicy(Qt::WheelFocus);
411 this->setMouseTracking(true);
411 this->setMouseTracking(true);
412 impl->m_plot->setAttribute(Qt::WA_TransparentForMouseEvents);
412 impl->m_plot->setAttribute(Qt::WA_TransparentForMouseEvents);
413 impl->m_plot->setContextMenuPolicy(Qt::CustomContextMenu);
413 impl->m_plot->setContextMenuPolicy(Qt::CustomContextMenu);
414 impl->m_plot->setParent(this);
414 impl->m_plot->setParent(this);
415 }
415 }
416
416
417
417
418 VisualizationGraphWidget::~VisualizationGraphWidget()
418 VisualizationGraphWidget::~VisualizationGraphWidget()
419 {
419 {
420 delete ui;
420 delete ui;
421 }
421 }
422
422
423 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
423 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
424 {
424 {
425 auto parent = parentWidget();
425 auto parent = parentWidget();
426 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
426 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
427 parent = parent->parentWidget();
427 parent = parent->parentWidget();
428 }
428 }
429
429
430 return qobject_cast<VisualizationZoneWidget *>(parent);
430 return qobject_cast<VisualizationZoneWidget *>(parent);
431 }
431 }
432
432
433 VisualizationWidget *VisualizationGraphWidget::parentVisualizationWidget() const
433 VisualizationWidget *VisualizationGraphWidget::parentVisualizationWidget() const
434 {
434 {
435 auto parent = parentWidget();
435 auto parent = parentWidget();
436 while (parent != nullptr && !qobject_cast<VisualizationWidget *>(parent)) {
436 while (parent != nullptr && !qobject_cast<VisualizationWidget *>(parent)) {
437 parent = parent->parentWidget();
437 parent = parent->parentWidget();
438 }
438 }
439
439
440 return qobject_cast<VisualizationWidget *>(parent);
440 return qobject_cast<VisualizationWidget *>(parent);
441 }
441 }
442
442
443 void VisualizationGraphWidget::setFlags(GraphFlags flags)
443 void VisualizationGraphWidget::setFlags(GraphFlags flags)
444 {
444 {
445 impl->m_Flags = std::move(flags);
445 impl->m_Flags = std::move(flags);
446 }
446 }
447
447
448 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, DateTimeRange range)
448 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, DateTimeRange range)
449 {
449 {
450 // Uses delegate to create the qcpplot components according to the variable
450 // Uses delegate to create the qcpplot components according to the variable
451 auto createdPlottables = VisualizationGraphHelper::create(variable, *impl->m_plot);
451 auto createdPlottables = VisualizationGraphHelper::create(variable, *impl->m_plot);
452
452
453 // Sets graph properties
453 // Sets graph properties
454 impl->m_RenderingDelegate->setGraphProperties(*variable, createdPlottables);
454 impl->m_RenderingDelegate->setGraphProperties(*variable, createdPlottables);
455
455
456 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
456 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
457
457
458 // If the variable already has its data loaded, load its units and its range in the graph
458 // If the variable already has its data loaded, load its units and its range in the graph
459 if (variable->dataSeries() != nullptr) {
459 if (variable->dataSeries() != nullptr) {
460 impl->m_RenderingDelegate->setAxesUnits(*variable);
460 impl->m_RenderingDelegate->setAxesUnits(*variable);
461 this->setFlags(GraphFlag::DisableAll);
461 this->setFlags(GraphFlag::DisableAll);
462 setGraphRange(range);
462 setGraphRange(range);
463 this->setFlags(GraphFlag::EnableAll);
463 this->setFlags(GraphFlag::EnableAll);
464 }
464 }
465 //@TODO this is bad! when variable is moved to another graph it still fires
465 //@TODO this is bad! when variable is moved to another graph it still fires
466 // even if this has been deleted
466 // even if this has been deleted
467 connect(variable.get(), &Variable::updated, this, &VisualizationGraphWidget::variableUpdated);
467 connect(variable.get(), &Variable::updated, this, &VisualizationGraphWidget::variableUpdated);
468 this->onUpdateVarDisplaying(variable, range); // My bullshit
468 this->onUpdateVarDisplaying(variable, range); // My bullshit
469 emit variableAdded(variable);
469 emit variableAdded(variable);
470 }
470 }
471
471
472 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
472 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
473 {
473 {
474 // Each component associated to the variable :
474 // Each component associated to the variable :
475 // - is removed from qcpplot (which deletes it)
475 // - is removed from qcpplot (which deletes it)
476 // - is no longer referenced in the map
476 // - is no longer referenced in the map
477 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
477 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
478 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
478 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
479 emit variableAboutToBeRemoved(variable);
479 emit variableAboutToBeRemoved(variable);
480
480
481 auto &plottablesMap = variableIt->second;
481 auto &plottablesMap = variableIt->second;
482
482
483 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
483 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
484 plottableIt != plottableEnd;) {
484 plottableIt != plottableEnd;) {
485 impl->m_plot->removePlottable(plottableIt->second);
485 impl->m_plot->removePlottable(plottableIt->second);
486 plottableIt = plottablesMap.erase(plottableIt);
486 plottableIt = plottablesMap.erase(plottableIt);
487 }
487 }
488
488
489 impl->m_VariableToPlotMultiMap.erase(variableIt);
489 impl->m_VariableToPlotMultiMap.erase(variableIt);
490 }
490 }
491
491
492 // Updates graph
492 // Updates graph
493 impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
493 impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
494 }
494 }
495
495
496 std::vector<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
496 std::vector<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
497 {
497 {
498 auto variables = std::vector<std::shared_ptr<Variable> >{};
498 auto variables = std::vector<std::shared_ptr<Variable> >{};
499 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
499 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
500 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
500 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
501 variables.push_back(it->first);
501 variables.push_back(it->first);
502 }
502 }
503
503
504 return variables;
504 return variables;
505 }
505 }
506
506
507 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
507 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
508 {
508 {
509 if (!variable) {
509 if (!variable) {
510 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
510 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
511 return;
511 return;
512 }
512 }
513
513
514 VisualizationGraphHelper::setYAxisRange(variable, *impl->m_plot);
514 VisualizationGraphHelper::setYAxisRange(variable, *impl->m_plot);
515 }
515 }
516
516
517 DateTimeRange VisualizationGraphWidget::graphRange() const noexcept
517 DateTimeRange VisualizationGraphWidget::graphRange() const noexcept
518 {
518 {
519 auto graphRange = impl->m_plot->xAxis->range();
519 auto graphRange = impl->m_plot->xAxis->range();
520 return DateTimeRange{graphRange.lower, graphRange.upper};
520 return DateTimeRange{graphRange.lower, graphRange.upper};
521 }
521 }
522
522
523 void VisualizationGraphWidget::setGraphRange(const DateTimeRange &range, bool updateVar)
523 void VisualizationGraphWidget::setGraphRange(const DateTimeRange &range, bool updateVar)
524 {
524 {
525
525
526 if(updateVar)
526 if(updateVar)
527 impl->setRange(range);
527 impl->setRange(range);
528 impl->m_plot->xAxis->setRange(range.m_TStart, range.m_TEnd);
528 impl->m_plot->xAxis->setRange(range.m_TStart, range.m_TEnd);
529 impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
529 impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
530
530
531 }
531 }
532
532
533 void VisualizationGraphWidget::setAutoRangeOnVariableInitialization(bool value)
533 void VisualizationGraphWidget::setAutoRangeOnVariableInitialization(bool value)
534 {
534 {
535 impl->m_VariableAutoRangeOnInit = value;
535 impl->m_VariableAutoRangeOnInit = value;
536 }
536 }
537
537
538 QVector<DateTimeRange> VisualizationGraphWidget::selectionZoneRanges() const
538 QVector<DateTimeRange> VisualizationGraphWidget::selectionZoneRanges() const
539 {
539 {
540 QVector<DateTimeRange> ranges;
540 QVector<DateTimeRange> ranges;
541 for (auto zone : impl->m_SelectionZones) {
541 for (auto zone : impl->m_SelectionZones) {
542 ranges << zone->range();
542 ranges << zone->range();
543 }
543 }
544
544
545 return ranges;
545 return ranges;
546 }
546 }
547
547
548 void VisualizationGraphWidget::addSelectionZones(const QVector<DateTimeRange> &ranges)
548 void VisualizationGraphWidget::addSelectionZones(const QVector<DateTimeRange> &ranges)
549 {
549 {
550 for (const auto &range : ranges) {
550 for (const auto &range : ranges) {
551 // note: ownership is transfered to QCustomPlot
551 // note: ownership is transfered to QCustomPlot
552 auto zone = new VisualizationSelectionZoneItem(&plot());
552 auto zone = new VisualizationSelectionZoneItem(&plot());
553 zone->setRange(range.m_TStart, range.m_TEnd);
553 zone->setRange(range.m_TStart, range.m_TEnd);
554 impl->addSelectionZone(zone);
554 impl->addSelectionZone(zone);
555 }
555 }
556
556
557 plot().replot(QCustomPlot::rpQueuedReplot);
557 plot().replot(QCustomPlot::rpQueuedReplot);
558 }
558 }
559
559
560 VisualizationSelectionZoneItem *
560 VisualizationSelectionZoneItem *
561 VisualizationGraphWidget::addSelectionZone(const QString &name, const DateTimeRange &range)
561 VisualizationGraphWidget::addSelectionZone(const QString &name, const DateTimeRange &range)
562 {
562 {
563 // note: ownership is transfered to QCustomPlot
563 // note: ownership is transfered to QCustomPlot
564 auto zone = new VisualizationSelectionZoneItem(&plot());
564 auto zone = new VisualizationSelectionZoneItem(&plot());
565 zone->setName(name);
565 zone->setName(name);
566 zone->setRange(range.m_TStart, range.m_TEnd);
566 zone->setRange(range.m_TStart, range.m_TEnd);
567 impl->addSelectionZone(zone);
567 impl->addSelectionZone(zone);
568
568
569 plot().replot(QCustomPlot::rpQueuedReplot);
569 plot().replot(QCustomPlot::rpQueuedReplot);
570
570
571 return zone;
571 return zone;
572 }
572 }
573
573
574 void VisualizationGraphWidget::removeSelectionZone(VisualizationSelectionZoneItem *selectionZone)
574 void VisualizationGraphWidget::removeSelectionZone(VisualizationSelectionZoneItem *selectionZone)
575 {
575 {
576 parentVisualizationWidget()->selectionZoneManager().setSelected(selectionZone, false);
576 parentVisualizationWidget()->selectionZoneManager().setSelected(selectionZone, false);
577
577
578 if (impl->m_HoveredZone == selectionZone) {
578 if (impl->m_HoveredZone == selectionZone) {
579 impl->m_HoveredZone = nullptr;
579 impl->m_HoveredZone = nullptr;
580 setCursor(Qt::ArrowCursor);
580 setCursor(Qt::ArrowCursor);
581 }
581 }
582
582
583 impl->m_SelectionZones.removeAll(selectionZone);
583 impl->m_SelectionZones.removeAll(selectionZone);
584 plot().removeItem(selectionZone);
584 plot().removeItem(selectionZone);
585 plot().replot(QCustomPlot::rpQueuedReplot);
585 plot().replot(QCustomPlot::rpQueuedReplot);
586 }
586 }
587
587
588 void VisualizationGraphWidget::undoZoom()
588 void VisualizationGraphWidget::undoZoom()
589 {
589 {
590 auto zoom = impl->m_ZoomStack.pop();
590 auto zoom = impl->m_ZoomStack.pop();
591 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
591 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
592 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
592 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
593
593
594 axisX->setRange(zoom.first);
594 axisX->setRange(zoom.first);
595 axisY->setRange(zoom.second);
595 axisY->setRange(zoom.second);
596
596
597 plot().replot(QCustomPlot::rpQueuedReplot);
597 plot().replot(QCustomPlot::rpQueuedReplot);
598 }
598 }
599
599
600 void VisualizationGraphWidget::zoom(double factor, int center, Qt::Orientation orientation, bool forward)
600 void VisualizationGraphWidget::zoom(double factor, int center, Qt::Orientation orientation, bool forward)
601 {
601 {
602 impl->zoom(factor, center, orientation);
602 impl->zoom(factor, center, orientation);
603 if(forward && orientation==Qt::Horizontal)
603 if(forward && orientation==Qt::Horizontal)
604 emit this->zoom_sig(factor, center, orientation, false);
604 emit this->zoom_sig(factor, center, orientation, false);
605 }
605 }
606
606
607 void VisualizationGraphWidget::move(double factor, Qt::Orientation orientation, bool forward)
607 void VisualizationGraphWidget::move(double factor, Qt::Orientation orientation, bool forward)
608 {
608 {
609 impl->move(factor, orientation);
609 impl->move(factor, orientation);
610 if(forward)
610 if(forward)
611 emit this->move_sig(factor, orientation, false);
611 emit this->move_sig(factor, orientation, false);
612 }
612 }
613
613
614 void VisualizationGraphWidget::move(double dx, double dy, bool forward)
614 void VisualizationGraphWidget::move(double dx, double dy, bool forward)
615 {
615 {
616 impl->move(dx, dy);
616 impl->move(dx, dy);
617 if(forward)
617 if(forward)
618 emit this->move_sig(dx, dy, false);
618 emit this->move_sig(dx, dy, false);
619 }
619 }
620
620
621 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
621 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
622 {
622 {
623 if (visitor) {
623 if (visitor) {
624 visitor->visit(this);
624 visitor->visit(this);
625 }
625 }
626 else {
626 else {
627 qCCritical(LOG_VisualizationGraphWidget())
627 qCCritical(LOG_VisualizationGraphWidget())
628 << tr("Can't visit widget : the visitor is null");
628 << tr("Can't visit widget : the visitor is null");
629 }
629 }
630 }
630 }
631
631
632 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
632 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
633 {
633 {
634 auto isSpectrogram = [](const auto &variable) {
634 auto isSpectrogram = [](const auto &variable) {
635 return std::dynamic_pointer_cast<SpectrogramSeries>(variable.dataSeries()) != nullptr;
635 return std::dynamic_pointer_cast<SpectrogramSeries>(variable.dataSeries()) != nullptr;
636 };
636 };
637
637
638 // - A spectrogram series can't be dropped on graph with existing plottables
638 // - A spectrogram series can't be dropped on graph with existing plottables
639 // - No data series can be dropped on graph with existing spectrogram series
639 // - No data series can be dropped on graph with existing spectrogram series
640 return isSpectrogram(variable)
640 return isSpectrogram(variable)
641 ? impl->m_VariableToPlotMultiMap.empty()
641 ? impl->m_VariableToPlotMultiMap.empty()
642 : std::none_of(
642 : std::none_of(
643 impl->m_VariableToPlotMultiMap.cbegin(), impl->m_VariableToPlotMultiMap.cend(),
643 impl->m_VariableToPlotMultiMap.cbegin(), impl->m_VariableToPlotMultiMap.cend(),
644 [isSpectrogram](const auto &entry) { return isSpectrogram(*entry.first); });
644 [isSpectrogram](const auto &entry) { return isSpectrogram(*entry.first); });
645 }
645 }
646
646
647 bool VisualizationGraphWidget::contains(const Variable &variable) const
647 bool VisualizationGraphWidget::contains(const Variable &variable) const
648 {
648 {
649 // Finds the variable among the keys of the map
649 // Finds the variable among the keys of the map
650 auto variablePtr = &variable;
650 auto variablePtr = &variable;
651 auto findVariable
651 auto findVariable
652 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
652 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
653
653
654 auto end = impl->m_VariableToPlotMultiMap.cend();
654 auto end = impl->m_VariableToPlotMultiMap.cend();
655 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
655 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
656 return it != end;
656 return it != end;
657 }
657 }
658
658
659 QString VisualizationGraphWidget::name() const
659 QString VisualizationGraphWidget::name() const
660 {
660 {
661 return impl->m_Name;
661 return impl->m_Name;
662 }
662 }
663
663
664 QMimeData *VisualizationGraphWidget::mimeData(const QPoint &position) const
664 QMimeData *VisualizationGraphWidget::mimeData(const QPoint &position) const
665 {
665 {
666 auto mimeData = new QMimeData;
666 auto mimeData = new QMimeData;
667
667
668 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position);
668 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position);
669 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
669 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
670 && selectionZoneItemUnderCursor) {
670 && selectionZoneItemUnderCursor) {
671 mimeData->setData(MIME_TYPE_TIME_RANGE, TimeController::mimeDataForTimeRange(
671 mimeData->setData(MIME_TYPE_TIME_RANGE, TimeController::mimeDataForTimeRange(
672 selectionZoneItemUnderCursor->range()));
672 selectionZoneItemUnderCursor->range()));
673 mimeData->setData(MIME_TYPE_SELECTION_ZONE, TimeController::mimeDataForTimeRange(
673 mimeData->setData(MIME_TYPE_SELECTION_ZONE, TimeController::mimeDataForTimeRange(
674 selectionZoneItemUnderCursor->range()));
674 selectionZoneItemUnderCursor->range()));
675 }
675 }
676 else {
676 else {
677 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
677 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
678
678
679 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
679 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
680 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
680 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
681 }
681 }
682
682
683 return mimeData;
683 return mimeData;
684 }
684 }
685
685
686 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint &dragPosition)
686 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint &dragPosition)
687 {
687 {
688 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition);
688 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition);
689 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
689 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
690 && selectionZoneItemUnderCursor) {
690 && selectionZoneItemUnderCursor) {
691
691
692 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
692 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
693 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
693 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
694
694
695 auto zoneSize = QSizeF{qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
695 auto zoneSize = QSizeF{qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
696 qAbs(zoneBottomRight.y() - zoneTopLeft.y())}
696 qAbs(zoneBottomRight.y() - zoneTopLeft.y())}
697 .toSize();
697 .toSize();
698
698
699 auto pixmap = QPixmap(zoneSize);
699 auto pixmap = QPixmap(zoneSize);
700 render(&pixmap, QPoint(), QRegion{QRect{zoneTopLeft.toPoint(), zoneSize}});
700 render(&pixmap, QPoint(), QRegion{QRect{zoneTopLeft.toPoint(), zoneSize}});
701
701
702 return pixmap;
702 return pixmap;
703 }
703 }
704
704
705 return QPixmap();
705 return QPixmap();
706 }
706 }
707
707
708 bool VisualizationGraphWidget::isDragAllowed() const
708 bool VisualizationGraphWidget::isDragAllowed() const
709 {
709 {
710 return true;
710 return true;
711 }
711 }
712
712
713 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
713 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
714 {
714 {
715 if (highlighted) {
715 if (highlighted) {
716 plot().setBackground(QBrush(QColor("#BBD5EE")));
716 plot().setBackground(QBrush(QColor("#BBD5EE")));
717 }
717 }
718 else {
718 else {
719 plot().setBackground(QBrush(Qt::white));
719 plot().setBackground(QBrush(Qt::white));
720 }
720 }
721
721
722 plot().update();
722 plot().update();
723 }
723 }
724
724
725 void VisualizationGraphWidget::addVerticalCursor(double time)
725 void VisualizationGraphWidget::addVerticalCursor(double time)
726 {
726 {
727 impl->m_VerticalCursor->setPosition(time);
727 impl->m_VerticalCursor->setPosition(time);
728 impl->m_VerticalCursor->setVisible(true);
728 impl->m_VerticalCursor->setVisible(true);
729
729
730 auto text
730 auto text
731 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
731 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
732 impl->m_VerticalCursor->setLabelText(text);
732 impl->m_VerticalCursor->setLabelText(text);
733 }
733 }
734
734
735 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
735 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
736 {
736 {
737 impl->m_VerticalCursor->setAbsolutePosition(position);
737 impl->m_VerticalCursor->setAbsolutePosition(position);
738 impl->m_VerticalCursor->setVisible(true);
738 impl->m_VerticalCursor->setVisible(true);
739
739
740 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
740 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
741 auto text
741 auto text
742 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
742 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
743 impl->m_VerticalCursor->setLabelText(text);
743 impl->m_VerticalCursor->setLabelText(text);
744 }
744 }
745
745
746 void VisualizationGraphWidget::removeVerticalCursor()
746 void VisualizationGraphWidget::removeVerticalCursor()
747 {
747 {
748 impl->m_VerticalCursor->setVisible(false);
748 impl->m_VerticalCursor->setVisible(false);
749 plot().replot(QCustomPlot::rpQueuedReplot);
749 plot().replot(QCustomPlot::rpQueuedReplot);
750 }
750 }
751
751
752 void VisualizationGraphWidget::addHorizontalCursor(double value)
752 void VisualizationGraphWidget::addHorizontalCursor(double value)
753 {
753 {
754 impl->m_HorizontalCursor->setPosition(value);
754 impl->m_HorizontalCursor->setPosition(value);
755 impl->m_HorizontalCursor->setVisible(true);
755 impl->m_HorizontalCursor->setVisible(true);
756 impl->m_HorizontalCursor->setLabelText(QString::number(value));
756 impl->m_HorizontalCursor->setLabelText(QString::number(value));
757 }
757 }
758
758
759 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
759 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
760 {
760 {
761 impl->m_HorizontalCursor->setAbsolutePosition(position);
761 impl->m_HorizontalCursor->setAbsolutePosition(position);
762 impl->m_HorizontalCursor->setVisible(true);
762 impl->m_HorizontalCursor->setVisible(true);
763
763
764 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
764 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
765 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
765 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
766 }
766 }
767
767
768 void VisualizationGraphWidget::removeHorizontalCursor()
768 void VisualizationGraphWidget::removeHorizontalCursor()
769 {
769 {
770 impl->m_HorizontalCursor->setVisible(false);
770 impl->m_HorizontalCursor->setVisible(false);
771 plot().replot(QCustomPlot::rpQueuedReplot);
771 plot().replot(QCustomPlot::rpQueuedReplot);
772 }
772 }
773
773
774 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
774 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
775 {
775 {
776 Q_UNUSED(event);
776 Q_UNUSED(event);
777
777
778 for (auto i : impl->m_SelectionZones) {
778 for (auto i : impl->m_SelectionZones) {
779 parentVisualizationWidget()->selectionZoneManager().setSelected(i, false);
779 parentVisualizationWidget()->selectionZoneManager().setSelected(i, false);
780 }
780 }
781
781
782 // Prevents that all variables will be removed from graph when it will be closed
782 // Prevents that all variables will be removed from graph when it will be closed
783 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
783 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
784 emit variableAboutToBeRemoved(variableEntry.first);
784 emit variableAboutToBeRemoved(variableEntry.first);
785 }
785 }
786 }
786 }
787
787
788 void VisualizationGraphWidget::enterEvent(QEvent *event)
788 void VisualizationGraphWidget::enterEvent(QEvent *event)
789 {
789 {
790 Q_UNUSED(event);
790 Q_UNUSED(event);
791 impl->m_RenderingDelegate->showGraphOverlay(true);
791 impl->m_RenderingDelegate->showGraphOverlay(true);
792 }
792 }
793
793
794 void VisualizationGraphWidget::leaveEvent(QEvent *event)
794 void VisualizationGraphWidget::leaveEvent(QEvent *event)
795 {
795 {
796 Q_UNUSED(event);
796 Q_UNUSED(event);
797 impl->m_RenderingDelegate->showGraphOverlay(false);
797 impl->m_RenderingDelegate->showGraphOverlay(false);
798
798
799 if (auto parentZone = parentZoneWidget()) {
799 if (auto parentZone = parentZoneWidget()) {
800 parentZone->notifyMouseLeaveGraph(this);
800 parentZone->notifyMouseLeaveGraph(this);
801 }
801 }
802 else {
802 else {
803 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
803 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
804 }
804 }
805
805
806 if (impl->m_HoveredZone) {
806 if (impl->m_HoveredZone) {
807 impl->m_HoveredZone->setHovered(false);
807 impl->m_HoveredZone->setHovered(false);
808 impl->m_HoveredZone = nullptr;
808 impl->m_HoveredZone = nullptr;
809 }
809 }
810 }
810 }
811
811
812 void VisualizationGraphWidget::wheelEvent(QWheelEvent *event)
812 void VisualizationGraphWidget::wheelEvent(QWheelEvent *event)
813 {
813 {
814 double factor;
814 double factor;
815 double wheelSteps = event->delta() / 120.0; // a single step delta is +/-120 usually
815 double wheelSteps = event->delta() / 120.0; // a single step delta is +/-120 usually
816 if (event->modifiers() == Qt::ControlModifier) {
816 if (event->modifiers() == Qt::ControlModifier) {
817 if (event->orientation() == Qt::Vertical) // mRangeZoom.testFlag(Qt::Vertical))
817 if (event->orientation() == Qt::Vertical) // mRangeZoom.testFlag(Qt::Vertical))
818 {
818 {
819 setCursor(Qt::SizeVerCursor);
819 setCursor(Qt::SizeVerCursor);
820 factor = pow(impl->m_plot->axisRect()->rangeZoomFactor(Qt::Vertical), wheelSteps);
820 factor = pow(impl->m_plot->axisRect()->rangeZoomFactor(Qt::Vertical), wheelSteps);
821 zoom(factor, event->pos().y(), Qt::Vertical);
821 zoom(factor, event->pos().y(), Qt::Vertical);
822 }
822 }
823 }
823 }
824 else if (event->modifiers() == Qt::ShiftModifier) {
824 else if (event->modifiers() == Qt::ShiftModifier) {
825 if (event->orientation() == Qt::Vertical) // mRangeZoom.testFlag(Qt::Vertical))
825 if (event->orientation() == Qt::Vertical) // mRangeZoom.testFlag(Qt::Vertical))
826 {
826 {
827 setCursor(Qt::SizeHorCursor);
827 setCursor(Qt::SizeHorCursor);
828 factor = pow(impl->m_plot->axisRect()->rangeZoomFactor(Qt::Horizontal), wheelSteps);
828 factor = pow(impl->m_plot->axisRect()->rangeZoomFactor(Qt::Horizontal), wheelSteps);
829 zoom(factor, event->pos().x(), Qt::Horizontal);
829 zoom(factor, event->pos().x(), Qt::Horizontal);
830 }
830 }
831 }
831 }
832 else {
832 else {
833 move(wheelSteps, Qt::Horizontal);
833 move(wheelSteps, Qt::Horizontal);
834 }
834 }
835 event->accept();
835 event->accept();
836 }
836 }
837
837
838
838
839
839
840 void VisualizationGraphWidget::mouseMoveEvent(QMouseEvent *event)
840 void VisualizationGraphWidget::mouseMoveEvent(QMouseEvent *event)
841 {
841 {
842 if(impl->isDrawingZoomRect())
842 if(impl->isDrawingZoomRect())
843 {
843 {
844 impl->updateZoomRect(event->pos());
844 impl->updateZoomRect(event->pos());
845 }
845 }
846 else if (impl->isDrawingZoneRect())
846 else if (impl->isDrawingZoneRect())
847 {
847 {
848 impl->updateZoneRect(event->pos());
848 impl->updateZoneRect(event->pos());
849 }
849 }
850 else if (event->buttons() == Qt::LeftButton)
850 else if (event->buttons() == Qt::LeftButton)
851 {
851 {
852 if(sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::None)
852 if(sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::None)
853 {
853 {
854 auto [dx,dy] = impl->moveGraph(event->pos());
854 auto [dx,dy] = impl->moveGraph(event->pos());
855 emit this->move_sig(dx,0., false); // don't sync Y transformations
855 emit this->move_sig(dx,0., false); // don't sync Y transformations
856 }
856 }
857 else if(sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones)
857 else if(sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones)
858 {
858 {
859
859
860 }
860 }
861 }
861 }
862 else
862 else
863 {
863 {
864 impl->m_RenderingDelegate->updateTooltip(event);
864 impl->m_RenderingDelegate->updateTooltip(event);
865 }
865 }
866 event->accept();
866 //event->accept();
867 QWidget::mouseMoveEvent(event);
867 }
868 }
868
869
869 void VisualizationGraphWidget::mouseReleaseEvent(QMouseEvent *event)
870 void VisualizationGraphWidget::mouseReleaseEvent(QMouseEvent *event)
870 {
871 {
871 if(impl->isDrawingZoomRect())
872 if(impl->isDrawingZoomRect())
872 {
873 {
873 impl->applyZoomRect();
874 impl->applyZoomRect();
874 }
875 }
875 else if(impl->isDrawingZoneRect())
876 else if(impl->isDrawingZoneRect())
876 {
877 {
877 impl->endDrawingZone();
878 impl->endDrawingZone();
878 }
879 }
879 else
880 else
880 {
881 {
881 setCursor(Qt::ArrowCursor);
882 setCursor(Qt::ArrowCursor);
882 }
883 }
883 event->accept();
884 event->accept();
884 }
885 }
885
886
886 void VisualizationGraphWidget::mousePressEvent(QMouseEvent *event)
887 void VisualizationGraphWidget::mousePressEvent(QMouseEvent *event)
887 {
888 {
888 if (event->button()==Qt::RightButton)
889 if (event->button()==Qt::RightButton)
889 {
890 {
890 onGraphMenuRequested(event->pos());
891 onGraphMenuRequested(event->pos());
891 }
892 }
892 else
893 else
893 {
894 {
894 auto selectedZone = impl->selectionZoneAt(event->pos());
895 auto selectedZone = impl->selectionZoneAt(event->pos());
895 switch (sqpApp->plotsInteractionMode())
896 switch (sqpApp->plotsInteractionMode())
896 {
897 {
897 case SqpApplication::PlotsInteractionMode::DragAndDrop :
898 case SqpApplication::PlotsInteractionMode::DragAndDrop :
898 break;
899 break;
899 case SqpApplication::PlotsInteractionMode::SelectionZones :
900 case SqpApplication::PlotsInteractionMode::SelectionZones :
900 impl->setSelectionZonesEditionEnabled(true);
901 impl->setSelectionZonesEditionEnabled(true);
901 if ((event->modifiers() == Qt::ControlModifier) && (selectedZone != nullptr))
902 if ((event->modifiers() == Qt::ControlModifier) && (selectedZone != nullptr))
902 {
903 {
903 selectedZone->setAssociatedEditedZones(parentVisualizationWidget()->selectionZoneManager().selectedItems());
904 selectedZone->setAssociatedEditedZones(parentVisualizationWidget()->selectionZoneManager().selectedItems());
904 }
905 }
905 else
906 else
906 {
907 {
907 if (!selectedZone)
908 if (!selectedZone)
908 {
909 {
909 parentVisualizationWidget()->selectionZoneManager().clearSelection();
910 parentVisualizationWidget()->selectionZoneManager().clearSelection();
910 impl->startDrawingZone(event->pos());
911 impl->startDrawingZone(event->pos());
911 }
912 }
912 else
913 else
913 {
914 {
914 parentVisualizationWidget()->selectionZoneManager().select({ selectedZone });
915 parentVisualizationWidget()->selectionZoneManager().select({ selectedZone });
915 }
916 }
916 }
917 }
917 break;
918 break;
918 case SqpApplication::PlotsInteractionMode::ZoomBox :
919 case SqpApplication::PlotsInteractionMode::ZoomBox :
919 impl->startDrawingRect(event->pos());
920 impl->startDrawingRect(event->pos());
920 break;
921 break;
921 default:
922 default:
922 setCursor(Qt::ClosedHandCursor);
923 setCursor(Qt::ClosedHandCursor);
923 impl->enterPlotDrag(event->pos());
924 impl->enterPlotDrag(event->pos());
924 }
925 }
925 }
926 }
926 event->accept();
927 //event->accept();
928 QWidget::mousePressEvent(event);
927 }
929 }
928
930
929 void VisualizationGraphWidget::mouseDoubleClickEvent(QMouseEvent *event)
931 void VisualizationGraphWidget::mouseDoubleClickEvent(QMouseEvent *event)
930 {
932 {
931 impl->m_RenderingDelegate->onMouseDoubleClick(event);
933 impl->m_RenderingDelegate->onMouseDoubleClick(event);
932 }
934 }
933
935
934 void VisualizationGraphWidget::keyReleaseEvent(QKeyEvent *event)
936 void VisualizationGraphWidget::keyReleaseEvent(QKeyEvent *event)
935 {
937 {
936 switch (event->key()) {
938 switch (event->key()) {
937 case Qt::Key_Control:
939 case Qt::Key_Control:
938 event->accept();
940 event->accept();
939 break;
941 break;
940 case Qt::Key_Shift:
942 case Qt::Key_Shift:
941 event->accept();
943 event->accept();
942 break;
944 break;
943 default:
945 default:
944 QWidget::keyReleaseEvent(event);
946 QWidget::keyReleaseEvent(event);
945 break;
947 break;
946 }
948 }
947 setCursor(Qt::ArrowCursor);
949 setCursor(Qt::ArrowCursor);
948 event->accept();
950 //event->accept();
949 }
951 }
950
952
951 void VisualizationGraphWidget::keyPressEvent(QKeyEvent *event)
953 void VisualizationGraphWidget::keyPressEvent(QKeyEvent *event)
952 {
954 {
953 switch (event->key()) {
955 switch (event->key()) {
954 case Qt::Key_Control:
956 case Qt::Key_Control:
955 setCursor(Qt::CrossCursor);
957 setCursor(Qt::CrossCursor);
956 break;
958 break;
957 case Qt::Key_Shift:
959 case Qt::Key_Shift:
958 break;
960 break;
959 case Qt::Key_M:
961 case Qt::Key_M:
960 impl->m_plot->rescaleAxes();
962 impl->m_plot->rescaleAxes();
961 impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
963 impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
962 break;
964 break;
963 case Qt::Key_Left:
965 case Qt::Key_Left:
964 if (event->modifiers() != Qt::ControlModifier) {
966 if (event->modifiers() != Qt::ControlModifier) {
965 move(-0.1, Qt::Horizontal);
967 move(-0.1, Qt::Horizontal);
966 }
968 }
967 else {
969 else {
968 zoom(2, this->width() / 2, Qt::Horizontal);
970 zoom(2, this->width() / 2, Qt::Horizontal);
969 }
971 }
970 break;
972 break;
971 case Qt::Key_Right:
973 case Qt::Key_Right:
972 if (event->modifiers() != Qt::ControlModifier) {
974 if (event->modifiers() != Qt::ControlModifier) {
973 move(0.1, Qt::Horizontal);
975 move(0.1, Qt::Horizontal);
974 }
976 }
975 else {
977 else {
976 zoom(0.5, this->width() / 2, Qt::Horizontal);
978 zoom(0.5, this->width() / 2, Qt::Horizontal);
977 }
979 }
978 break;
980 break;
979 case Qt::Key_Up:
981 case Qt::Key_Up:
980 if (event->modifiers() != Qt::ControlModifier) {
982 if (event->modifiers() != Qt::ControlModifier) {
981 move(0.1, Qt::Vertical);
983 move(0.1, Qt::Vertical);
982 }
984 }
983 else {
985 else {
984 zoom(0.5, this->height() / 2, Qt::Vertical);
986 zoom(0.5, this->height() / 2, Qt::Vertical);
985 }
987 }
986 break;
988 break;
987 case Qt::Key_Down:
989 case Qt::Key_Down:
988 if (event->modifiers() != Qt::ControlModifier) {
990 if (event->modifiers() != Qt::ControlModifier) {
989 move(-0.1, Qt::Vertical);
991 move(-0.1, Qt::Vertical);
990 }
992 }
991 else {
993 else {
992 zoom(2, this->height() / 2, Qt::Vertical);
994 zoom(2, this->height() / 2, Qt::Vertical);
993 }
995 }
994 break;
996 break;
995 default:
997 default:
996 QWidget::keyPressEvent(event);
998 QWidget::keyPressEvent(event);
997 break;
999 break;
998 }
1000 }
999 }
1001 }
1000
1002
1001 QCustomPlot &VisualizationGraphWidget::plot() const noexcept
1003 QCustomPlot &VisualizationGraphWidget::plot() const noexcept
1002 {
1004 {
1003 return *impl->m_plot;
1005 return *impl->m_plot;
1004 }
1006 }
1005
1007
1006 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
1008 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
1007 {
1009 {
1008 QMenu graphMenu{};
1010 QMenu graphMenu{};
1009
1011
1010 // Iterates on variables (unique keys)
1012 // Iterates on variables (unique keys)
1011 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
1013 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
1012 end = impl->m_VariableToPlotMultiMap.cend();
1014 end = impl->m_VariableToPlotMultiMap.cend();
1013 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
1015 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
1014 // 'Remove variable' action
1016 // 'Remove variable' action
1015 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
1017 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
1016 [this, var = it->first]() { removeVariable(var); });
1018 [this, var = it->first]() { removeVariable(var); });
1017 }
1019 }
1018
1020
1019 if (!impl->m_ZoomStack.isEmpty()) {
1021 if (!impl->m_ZoomStack.isEmpty()) {
1020 if (!graphMenu.isEmpty()) {
1022 if (!graphMenu.isEmpty()) {
1021 graphMenu.addSeparator();
1023 graphMenu.addSeparator();
1022 }
1024 }
1023
1025
1024 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
1026 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
1025 }
1027 }
1026
1028
1027 // Selection Zone Actions
1029 // Selection Zone Actions
1028 auto selectionZoneItem = impl->selectionZoneAt(pos);
1030 auto selectionZoneItem = impl->selectionZoneAt(pos);
1029 if (selectionZoneItem) {
1031 if (selectionZoneItem) {
1030 auto selectedItems = parentVisualizationWidget()->selectionZoneManager().selectedItems();
1032 auto selectedItems = parentVisualizationWidget()->selectionZoneManager().selectedItems();
1031 selectedItems.removeAll(selectionZoneItem);
1033 selectedItems.removeAll(selectionZoneItem);
1032 selectedItems.prepend(selectionZoneItem); // Put the current selection zone first
1034 selectedItems.prepend(selectionZoneItem); // Put the current selection zone first
1033
1035
1034 auto zoneActions = sqpApp->actionsGuiController().selectionZoneActions();
1036 auto zoneActions = sqpApp->actionsGuiController().selectionZoneActions();
1035 if (!zoneActions.isEmpty() && !graphMenu.isEmpty()) {
1037 if (!zoneActions.isEmpty() && !graphMenu.isEmpty()) {
1036 graphMenu.addSeparator();
1038 graphMenu.addSeparator();
1037 }
1039 }
1038
1040
1039 QHash<QString, QMenu *> subMenus;
1041 QHash<QString, QMenu *> subMenus;
1040 QHash<QString, bool> subMenusEnabled;
1042 QHash<QString, bool> subMenusEnabled;
1041 QHash<QString, FilteringAction *> filteredMenu;
1043 QHash<QString, FilteringAction *> filteredMenu;
1042
1044
1043 for (auto zoneAction : zoneActions) {
1045 for (auto zoneAction : zoneActions) {
1044
1046
1045 auto isEnabled = zoneAction->isEnabled(selectedItems);
1047 auto isEnabled = zoneAction->isEnabled(selectedItems);
1046
1048
1047 auto menu = &graphMenu;
1049 auto menu = &graphMenu;
1048 QString menuPath;
1050 QString menuPath;
1049 for (auto subMenuName : zoneAction->subMenuList()) {
1051 for (auto subMenuName : zoneAction->subMenuList()) {
1050 menuPath += '/';
1052 menuPath += '/';
1051 menuPath += subMenuName;
1053 menuPath += subMenuName;
1052
1054
1053 if (!subMenus.contains(menuPath)) {
1055 if (!subMenus.contains(menuPath)) {
1054 menu = menu->addMenu(subMenuName);
1056 menu = menu->addMenu(subMenuName);
1055 subMenus[menuPath] = menu;
1057 subMenus[menuPath] = menu;
1056 subMenusEnabled[menuPath] = isEnabled;
1058 subMenusEnabled[menuPath] = isEnabled;
1057 }
1059 }
1058 else {
1060 else {
1059 menu = subMenus.value(menuPath);
1061 menu = subMenus.value(menuPath);
1060 if (isEnabled) {
1062 if (isEnabled) {
1061 // The sub menu is enabled if at least one of its actions is enabled
1063 // The sub menu is enabled if at least one of its actions is enabled
1062 subMenusEnabled[menuPath] = true;
1064 subMenusEnabled[menuPath] = true;
1063 }
1065 }
1064 }
1066 }
1065 }
1067 }
1066
1068
1067 FilteringAction *filterAction = nullptr;
1069 FilteringAction *filterAction = nullptr;
1068 if (sqpApp->actionsGuiController().isMenuFiltered(zoneAction->subMenuList())) {
1070 if (sqpApp->actionsGuiController().isMenuFiltered(zoneAction->subMenuList())) {
1069 filterAction = filteredMenu.value(menuPath);
1071 filterAction = filteredMenu.value(menuPath);
1070 if (!filterAction) {
1072 if (!filterAction) {
1071 filterAction = new FilteringAction{this};
1073 filterAction = new FilteringAction{this};
1072 filteredMenu[menuPath] = filterAction;
1074 filteredMenu[menuPath] = filterAction;
1073 menu->addAction(filterAction);
1075 menu->addAction(filterAction);
1074 }
1076 }
1075 }
1077 }
1076
1078
1077 auto action = menu->addAction(zoneAction->name());
1079 auto action = menu->addAction(zoneAction->name());
1078 action->setEnabled(isEnabled);
1080 action->setEnabled(isEnabled);
1079 action->setShortcut(zoneAction->displayedShortcut());
1081 action->setShortcut(zoneAction->displayedShortcut());
1080 QObject::connect(action, &QAction::triggered,
1082 QObject::connect(action, &QAction::triggered,
1081 [zoneAction, selectedItems]() { zoneAction->execute(selectedItems); });
1083 [zoneAction, selectedItems]() { zoneAction->execute(selectedItems); });
1082
1084
1083 if (filterAction && zoneAction->isFilteringAllowed()) {
1085 if (filterAction && zoneAction->isFilteringAllowed()) {
1084 filterAction->addActionToFilter(action);
1086 filterAction->addActionToFilter(action);
1085 }
1087 }
1086 }
1088 }
1087
1089
1088 for (auto it = subMenus.cbegin(); it != subMenus.cend(); ++it) {
1090 for (auto it = subMenus.cbegin(); it != subMenus.cend(); ++it) {
1089 it.value()->setEnabled(subMenusEnabled[it.key()]);
1091 it.value()->setEnabled(subMenusEnabled[it.key()]);
1090 }
1092 }
1091 }
1093 }
1092
1094
1093 if (!graphMenu.isEmpty()) {
1095 if (!graphMenu.isEmpty()) {
1094 graphMenu.exec(QCursor::pos());
1096 graphMenu.exec(QCursor::pos());
1095 }
1097 }
1096 }
1098 }
1097
1099
1098 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept
1100 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept
1099 {
1101 {
1100 impl->m_RenderingDelegate->onMouseDoubleClick(event);
1102 impl->m_RenderingDelegate->onMouseDoubleClick(event);
1101 }
1103 }
1102
1104
1103 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
1105 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
1104 {
1106 {
1105 // Handles plot rendering when mouse is moving
1107 // Handles plot rendering when mouse is moving
1106 impl->m_RenderingDelegate->updateTooltip(event);
1108 impl->m_RenderingDelegate->updateTooltip(event);
1107
1109
1108 auto axisPos = impl->posToAxisPos(event->pos());
1110 auto axisPos = impl->posToAxisPos(event->pos());
1109
1111
1110 // Zoom box and zone drawing
1112 // Zoom box and zone drawing
1111 if (impl->m_DrawingZoomRect) {
1113 if (impl->m_DrawingZoomRect) {
1112 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
1114 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
1113 }
1115 }
1114 else if (impl->m_DrawingZone) {
1116 else if (impl->m_DrawingZone) {
1115 impl->m_DrawingZone->setEnd(axisPos.x());
1117 impl->m_DrawingZone->setEnd(axisPos.x());
1116 }
1118 }
1117
1119
1118 // Cursor
1120 // Cursor
1119 if (auto parentZone = parentZoneWidget()) {
1121 if (auto parentZone = parentZoneWidget()) {
1120 if (impl->pointIsInAxisRect(axisPos, plot())) {
1122 if (impl->pointIsInAxisRect(axisPos, plot())) {
1121 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
1123 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
1122 }
1124 }
1123 else {
1125 else {
1124 parentZone->notifyMouseLeaveGraph(this);
1126 parentZone->notifyMouseLeaveGraph(this);
1125 }
1127 }
1126 }
1128 }
1127
1129
1128 // Search for the selection zone under the mouse
1130 // Search for the selection zone under the mouse
1129 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos());
1131 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos());
1130 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
1132 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
1131 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) {
1133 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) {
1132
1134
1133 // Sets the appropriate cursor shape
1135 // Sets the appropriate cursor shape
1134 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
1136 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
1135 setCursor(cursorShape);
1137 setCursor(cursorShape);
1136
1138
1137 // Manages the hovered zone
1139 // Manages the hovered zone
1138 if (selectionZoneItemUnderCursor != impl->m_HoveredZone) {
1140 if (selectionZoneItemUnderCursor != impl->m_HoveredZone) {
1139 if (impl->m_HoveredZone) {
1141 if (impl->m_HoveredZone) {
1140 impl->m_HoveredZone->setHovered(false);
1142 impl->m_HoveredZone->setHovered(false);
1141 }
1143 }
1142 selectionZoneItemUnderCursor->setHovered(true);
1144 selectionZoneItemUnderCursor->setHovered(true);
1143 impl->m_HoveredZone = selectionZoneItemUnderCursor;
1145 impl->m_HoveredZone = selectionZoneItemUnderCursor;
1144 plot().replot(QCustomPlot::rpQueuedReplot);
1146 plot().replot(QCustomPlot::rpQueuedReplot);
1145 }
1147 }
1146 }
1148 }
1147 else {
1149 else {
1148 // There is no zone under the mouse or the interaction mode is not "selection zones"
1150 // There is no zone under the mouse or the interaction mode is not "selection zones"
1149 if (impl->m_HoveredZone) {
1151 if (impl->m_HoveredZone) {
1150 impl->m_HoveredZone->setHovered(false);
1152 impl->m_HoveredZone->setHovered(false);
1151 impl->m_HoveredZone = nullptr;
1153 impl->m_HoveredZone = nullptr;
1152 }
1154 }
1153
1155
1154 setCursor(Qt::ArrowCursor);
1156 setCursor(Qt::ArrowCursor);
1155 }
1157 }
1156
1158
1157 impl->m_HasMovedMouse = true;
1159 impl->m_HasMovedMouse = true;
1158 VisualizationDragWidget::mouseMoveEvent(event);
1160 VisualizationDragWidget::mouseMoveEvent(event);
1159 }
1161 }
1160
1162
1161 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
1163 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
1162 {
1164 {
1163 // Processes event only if the wheel occurs on axis rect
1165 // Processes event only if the wheel occurs on axis rect
1164 if (!dynamic_cast<QCPAxisRect *>(impl->m_plot->layoutElementAt(event->posF()))) {
1166 if (!dynamic_cast<QCPAxisRect *>(impl->m_plot->layoutElementAt(event->posF()))) {
1165 return;
1167 return;
1166 }
1168 }
1167
1169
1168 auto value = event->angleDelta().x() + event->angleDelta().y();
1170 auto value = event->angleDelta().x() + event->angleDelta().y();
1169 if (value != 0) {
1171 if (value != 0) {
1170
1172
1171 auto direction = value > 0 ? 1.0 : -1.0;
1173 auto direction = value > 0 ? 1.0 : -1.0;
1172 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
1174 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
1173 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
1175 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
1174 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
1176 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
1175
1177
1176 auto zoomOrientations = QFlags<Qt::Orientation>{};
1178 auto zoomOrientations = QFlags<Qt::Orientation>{};
1177 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
1179 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
1178 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
1180 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
1179
1181
1180 impl->m_plot->axisRect()->setRangeZoom(zoomOrientations);
1182 impl->m_plot->axisRect()->setRangeZoom(zoomOrientations);
1181
1183
1182 if (!isZoomX && !isZoomY) {
1184 if (!isZoomX && !isZoomY) {
1183 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
1185 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
1184 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
1186 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
1185
1187
1186 axis->setRange(axis->range() + diff);
1188 axis->setRange(axis->range() + diff);
1187
1189
1188 if (plot().noAntialiasingOnDrag()) {
1190 if (plot().noAntialiasingOnDrag()) {
1189 plot().setNotAntialiasedElements(QCP::aeAll);
1191 plot().setNotAntialiasedElements(QCP::aeAll);
1190 }
1192 }
1191
1193
1192 // plot().replot(QCustomPlot::rpQueuedReplot);
1194 // plot().replot(QCustomPlot::rpQueuedReplot);
1193 }
1195 }
1194 }
1196 }
1195 }
1197 }
1196
1198
1197 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
1199 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
1198 {
1200 {
1199 auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
1201 auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
1200 auto isSelectionZoneMode
1202 auto isSelectionZoneMode
1201 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
1203 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
1202 auto isLeftClick = event->buttons().testFlag(Qt::LeftButton);
1204 auto isLeftClick = event->buttons().testFlag(Qt::LeftButton);
1203
1205
1204 if (!isDragDropClick && isLeftClick) {
1206 if (!isDragDropClick && isLeftClick) {
1205 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) {
1207 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) {
1206 // Starts a zoom box
1208 // Starts a zoom box
1207 impl->startDrawingRect(event->pos());
1209 impl->startDrawingRect(event->pos());
1208 }
1210 }
1209 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr) {
1211 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr) {
1210 // Starts a new selection zone
1212 // Starts a new selection zone
1211 auto zoneAtPos = impl->selectionZoneAt(event->pos());
1213 auto zoneAtPos = impl->selectionZoneAt(event->pos());
1212 if (!zoneAtPos) {
1214 if (!zoneAtPos) {
1213 impl->startDrawingZone(event->pos());
1215 impl->startDrawingZone(event->pos());
1214 }
1216 }
1215 }
1217 }
1216 }
1218 }
1217
1219
1218
1220
1219 // Allows zone edition only in selection zone mode without drag&drop
1221 // Allows zone edition only in selection zone mode without drag&drop
1220 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
1222 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
1221
1223
1222 // Selection / Deselection
1224 // Selection / Deselection
1223 if (isSelectionZoneMode) {
1225 if (isSelectionZoneMode) {
1224 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
1226 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
1225 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos());
1227 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos());
1226
1228
1227
1229
1228 if (selectionZoneItemUnderCursor && !selectionZoneItemUnderCursor->selected()
1230 if (selectionZoneItemUnderCursor && !selectionZoneItemUnderCursor->selected()
1229 && !isMultiSelectionClick) {
1231 && !isMultiSelectionClick) {
1230 parentVisualizationWidget()->selectionZoneManager().select(
1232 parentVisualizationWidget()->selectionZoneManager().select(
1231 {selectionZoneItemUnderCursor});
1233 {selectionZoneItemUnderCursor});
1232 }
1234 }
1233 else if (!selectionZoneItemUnderCursor && !isMultiSelectionClick && isLeftClick) {
1235 else if (!selectionZoneItemUnderCursor && !isMultiSelectionClick && isLeftClick) {
1234 parentVisualizationWidget()->selectionZoneManager().clearSelection();
1236 parentVisualizationWidget()->selectionZoneManager().clearSelection();
1235 }
1237 }
1236 else {
1238 else {
1237 // No selection change
1239 // No selection change
1238 }
1240 }
1239
1241
1240 if (selectionZoneItemUnderCursor && isLeftClick) {
1242 if (selectionZoneItemUnderCursor && isLeftClick) {
1241 selectionZoneItemUnderCursor->setAssociatedEditedZones(
1243 selectionZoneItemUnderCursor->setAssociatedEditedZones(
1242 parentVisualizationWidget()->selectionZoneManager().selectedItems());
1244 parentVisualizationWidget()->selectionZoneManager().selectedItems());
1243 }
1245 }
1244 }
1246 }
1245
1247
1246
1248
1247 impl->m_HasMovedMouse = false;
1249 impl->m_HasMovedMouse = false;
1248 VisualizationDragWidget::mousePressEvent(event);
1250 VisualizationDragWidget::mousePressEvent(event);
1249 }
1251 }
1250
1252
1251 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
1253 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
1252 {
1254 {
1253 if (impl->m_DrawingZoomRect) {
1255 if (impl->m_DrawingZoomRect) {
1254
1256
1255 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
1257 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
1256 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
1258 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
1257
1259
1258 auto newAxisXRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().x(),
1260 auto newAxisXRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().x(),
1259 impl->m_DrawingZoomRect->bottomRight->coords().x()};
1261 impl->m_DrawingZoomRect->bottomRight->coords().x()};
1260
1262
1261 auto newAxisYRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().y(),
1263 auto newAxisYRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().y(),
1262 impl->m_DrawingZoomRect->bottomRight->coords().y()};
1264 impl->m_DrawingZoomRect->bottomRight->coords().y()};
1263
1265
1264 impl->removeDrawingRect();
1266 impl->removeDrawingRect();
1265
1267
1266 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
1268 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
1267 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
1269 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
1268 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
1270 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
1269 axisX->setRange(newAxisXRange);
1271 axisX->setRange(newAxisXRange);
1270 axisY->setRange(newAxisYRange);
1272 axisY->setRange(newAxisYRange);
1271
1273
1272 plot().replot(QCustomPlot::rpQueuedReplot);
1274 plot().replot(QCustomPlot::rpQueuedReplot);
1273 }
1275 }
1274 }
1276 }
1275
1277
1276 impl->endDrawingZone();
1278 impl->endDrawingZone();
1277
1279
1278 // Selection / Deselection
1280 // Selection / Deselection
1279 auto isSelectionZoneMode
1281 auto isSelectionZoneMode
1280 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
1282 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
1281 if (isSelectionZoneMode) {
1283 if (isSelectionZoneMode) {
1282 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
1284 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
1283 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos());
1285 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos());
1284 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton
1286 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton
1285 && !impl->m_HasMovedMouse) {
1287 && !impl->m_HasMovedMouse) {
1286
1288
1287 auto zonesUnderCursor = impl->selectionZonesAt(event->pos(), plot());
1289 auto zonesUnderCursor = impl->selectionZonesAt(event->pos(), plot());
1288 if (zonesUnderCursor.count() > 1) {
1290 if (zonesUnderCursor.count() > 1) {
1289 // There are multiple zones under the mouse.
1291 // There are multiple zones under the mouse.
1290 // Performs the selection with a selection dialog.
1292 // Performs the selection with a selection dialog.
1291 VisualizationMultiZoneSelectionDialog dialog{this};
1293 VisualizationMultiZoneSelectionDialog dialog{this};
1292 dialog.setZones(zonesUnderCursor);
1294 dialog.setZones(zonesUnderCursor);
1293 dialog.move(mapToGlobal(event->pos() - QPoint(dialog.width() / 2, 20)));
1295 dialog.move(mapToGlobal(event->pos() - QPoint(dialog.width() / 2, 20)));
1294 dialog.activateWindow();
1296 dialog.activateWindow();
1295 dialog.raise();
1297 dialog.raise();
1296 if (dialog.exec() == QDialog::Accepted) {
1298 if (dialog.exec() == QDialog::Accepted) {
1297 auto selection = dialog.selectedZones();
1299 auto selection = dialog.selectedZones();
1298
1300
1299 if (!isMultiSelectionClick) {
1301 if (!isMultiSelectionClick) {
1300 parentVisualizationWidget()->selectionZoneManager().clearSelection();
1302 parentVisualizationWidget()->selectionZoneManager().clearSelection();
1301 }
1303 }
1302
1304
1303 for (auto it = selection.cbegin(); it != selection.cend(); ++it) {
1305 for (auto it = selection.cbegin(); it != selection.cend(); ++it) {
1304 auto zone = it.key();
1306 auto zone = it.key();
1305 auto isSelected = it.value();
1307 auto isSelected = it.value();
1306 parentVisualizationWidget()->selectionZoneManager().setSelected(zone,
1308 parentVisualizationWidget()->selectionZoneManager().setSelected(zone,
1307 isSelected);
1309 isSelected);
1308
1310
1309 if (isSelected) {
1311 if (isSelected) {
1310 // Puts the zone on top of the stack so it can be moved or resized
1312 // Puts the zone on top of the stack so it can be moved or resized
1311 impl->moveSelectionZoneOnTop(zone, plot());
1313 impl->moveSelectionZoneOnTop(zone, plot());
1312 }
1314 }
1313 }
1315 }
1314 }
1316 }
1315 }
1317 }
1316 else {
1318 else {
1317 if (!isMultiSelectionClick) {
1319 if (!isMultiSelectionClick) {
1318 parentVisualizationWidget()->selectionZoneManager().select(
1320 parentVisualizationWidget()->selectionZoneManager().select(
1319 {selectionZoneItemUnderCursor});
1321 {selectionZoneItemUnderCursor});
1320 impl->moveSelectionZoneOnTop(selectionZoneItemUnderCursor, plot());
1322 impl->moveSelectionZoneOnTop(selectionZoneItemUnderCursor, plot());
1321 }
1323 }
1322 else {
1324 else {
1323 parentVisualizationWidget()->selectionZoneManager().setSelected(
1325 parentVisualizationWidget()->selectionZoneManager().setSelected(
1324 selectionZoneItemUnderCursor, !selectionZoneItemUnderCursor->selected()
1326 selectionZoneItemUnderCursor, !selectionZoneItemUnderCursor->selected()
1325 || event->button() == Qt::RightButton);
1327 || event->button() == Qt::RightButton);
1326 }
1328 }
1327 }
1329 }
1328 }
1330 }
1329 else {
1331 else {
1330 // No selection change
1332 // No selection change
1331 }
1333 }
1332 }
1334 }
1333 }
1335 }
1334
1336
1335 void VisualizationGraphWidget::onDataCacheVariableUpdated()
1337 void VisualizationGraphWidget::onDataCacheVariableUpdated()
1336 {
1338 {
1337 auto graphRange = impl->m_plot->xAxis->range();
1339 auto graphRange = impl->m_plot->xAxis->range();
1338 auto dateTime = DateTimeRange{graphRange.lower, graphRange.upper};
1340 auto dateTime = DateTimeRange{graphRange.lower, graphRange.upper};
1339
1341
1340 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
1342 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
1341 auto variable = variableEntry.first;
1343 auto variable = variableEntry.first;
1342 qCDebug(LOG_VisualizationGraphWidget())
1344 qCDebug(LOG_VisualizationGraphWidget())
1343 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
1345 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
1344 qCDebug(LOG_VisualizationGraphWidget())
1346 qCDebug(LOG_VisualizationGraphWidget())
1345 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
1347 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
1346 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
1348 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
1347 impl->updateData(variableEntry.second, variable, variable->range());
1349 impl->updateData(variableEntry.second, variable, variable->range());
1348 }
1350 }
1349 }
1351 }
1350 }
1352 }
1351
1353
1352 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
1354 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
1353 const DateTimeRange &range)
1355 const DateTimeRange &range)
1354 {
1356 {
1355 auto it = impl->m_VariableToPlotMultiMap.find(variable);
1357 auto it = impl->m_VariableToPlotMultiMap.find(variable);
1356 if (it != impl->m_VariableToPlotMultiMap.end()) {
1358 if (it != impl->m_VariableToPlotMultiMap.end()) {
1357 impl->updateData(it->second, variable, range);
1359 impl->updateData(it->second, variable, range);
1358 }
1360 }
1359 }
1361 }
1360
1362
1361 void VisualizationGraphWidget::variableUpdated(QUuid id)
1363 void VisualizationGraphWidget::variableUpdated(QUuid id)
1362 {
1364 {
1363 for (auto &[var, plotables] : impl->m_VariableToPlotMultiMap) {
1365 for (auto &[var, plotables] : impl->m_VariableToPlotMultiMap) {
1364 if (var->ID() == id) {
1366 if (var->ID() == id) {
1365 impl->updateData(plotables, var, this->graphRange());
1367 impl->updateData(plotables, var, this->graphRange());
1366 }
1368 }
1367 }
1369 }
1368 }
1370 }
General Comments 0
You need to be logged in to leave comments. Login now