@@ -1,35 +1,41 | |||
|
1 | 1 | include_directories(include) |
|
2 | 2 | |
|
3 | 3 | FILE (GLOB_RECURSE app_SRCS |
|
4 | 4 | include/*.h |
|
5 | 5 | src/*.cpp |
|
6 | 6 | resources/*.qrc |
|
7 | 7 | ) |
|
8 | 8 | |
|
9 | 9 | QT5_WRAP_UI(UiGenerated_SRCS |
|
10 | 10 | ui/MainWindow.ui |
|
11 | 11 | ) |
|
12 | 12 | |
|
13 | 13 | add_executable(sciqlopapp WIN32 ${app_SRCS} ${UiGenerated_SRCS}) |
|
14 | 14 | if(NOT BUILD_SHARED_LIBS) |
|
15 | 15 | add_definitions(-DQT_STATICPLUGIN) |
|
16 | target_link_libraries(sciqlopapp mockplugin) | |
|
17 |
target_link_libraries(sciqlopapp |
|
|
16 | if(BUILD_PLUGINS) | |
|
17 | target_link_libraries(sciqlopapp mockplugin) | |
|
18 | target_link_libraries(sciqlopapp amdaplugin) | |
|
19 | endif() | |
|
20 | endif() | |
|
21 | ||
|
22 | if(NOT BUILD_PLUGINS) | |
|
23 | add_definitions(-DSQP_NO_PLUGINS) | |
|
18 | 24 | endif() |
|
19 | 25 | |
|
20 | 26 | target_link_libraries(sciqlopapp |
|
21 | 27 | Qt5::Core |
|
22 | 28 | Qt5::Widgets |
|
23 | 29 | Qt5::Network |
|
24 | 30 | Qt5::PrintSupport |
|
25 | 31 | Qt5::Svg |
|
26 | 32 | sciqlopgui |
|
27 | 33 | sciqlopcore |
|
28 | 34 | ) |
|
29 | 35 | |
|
30 | 36 | install(TARGETS sciqlopapp DESTINATION ${CMAKE_INSTALL_BINDIR}) |
|
31 | 37 | install(FILES resources/SciQLOP.desktop DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications/) |
|
32 | 38 | install(FILES resources/SciQLOP.appdata.xml DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/metainfo/) |
|
33 | 39 | install(FILES resources/sciqlopLOGO.svg DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/SciQLOP/icons/) |
|
34 | 40 | |
|
35 | 41 |
@@ -1,97 +1,99 | |||
|
1 | 1 | /*------------------------------------------------------------------------------ |
|
2 | 2 | -- This file is a part of the QLop Software |
|
3 | 3 | -- Copyright (C) 2015, Plasma Physics Laboratory - CNRS |
|
4 | 4 | -- |
|
5 | 5 | -- This program is free software; you can redistribute it and/or modify |
|
6 | 6 | -- it under the terms of the GNU General Public License as published by |
|
7 | 7 | -- the Free Software Foundation; either version 2 of the License, or |
|
8 | 8 | -- (at your option) any later version. |
|
9 | 9 | -- |
|
10 | 10 | -- This program is distributed in the hope that it will be useful, |
|
11 | 11 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
12 | 12 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
13 | 13 | -- GNU General Public License for more details. |
|
14 | 14 | -- |
|
15 | 15 | -- You should have received a copy of the GNU General Public License |
|
16 | 16 | -- along with this program; if not, write to the Free Software |
|
17 | 17 | -- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|
18 | 18 | -------------------------------------------------------------------------------*/ |
|
19 | 19 | /*-- Author : Alexis Jeandet |
|
20 | 20 | -- Mail : alexis.jeandet@member.fsf.org |
|
21 | 21 | ----------------------------------------------------------------------------*/ |
|
22 | 22 | #include "MainWindow.h" |
|
23 | 23 | #include <QProcessEnvironment> |
|
24 | 24 | #include <QThread> |
|
25 | 25 | #include <SqpApplication.h> |
|
26 | 26 | #include <qglobal.h> |
|
27 | 27 | |
|
28 | 28 | #include <PluginManager/PluginManager.h> |
|
29 | 29 | #include <QDir> |
|
30 | 30 | #include <QtPlugin> |
|
31 | 31 | |
|
32 | 32 | #include <QLoggingCategory> |
|
33 | 33 | |
|
34 | 34 | Q_LOGGING_CATEGORY(LOG_Main, "Main") |
|
35 | 35 | |
|
36 | 36 | namespace |
|
37 | 37 | { |
|
38 | 38 | |
|
39 | 39 | const auto PLUGIN_DIRECTORY_NAME = QStringLiteral("plugins"); |
|
40 | 40 | |
|
41 | 41 | |
|
42 | 42 | } // namespace |
|
43 | 43 | |
|
44 | 44 | int main(int argc, char* argv[]) |
|
45 | 45 | { |
|
46 | 46 | #ifdef QT_STATICPLUGIN |
|
47 | #ifndef SQP_NO_PLUGINS | |
|
47 | 48 | Q_IMPORT_PLUGIN(MockPlugin) |
|
48 | 49 | Q_IMPORT_PLUGIN(AmdaPlugin) |
|
49 | 50 | Q_INIT_RESOURCE(amdaresources); |
|
50 | 51 | #endif |
|
52 | #endif | |
|
51 | 53 | Q_INIT_RESOURCE(sqpguiresources); |
|
52 | 54 | |
|
53 | 55 | SqpApplication::setOrganizationName("LPP"); |
|
54 | 56 | SqpApplication::setOrganizationDomain("lpp.fr"); |
|
55 | 57 | SqpApplication::setApplicationName("SciQLop"); |
|
56 | 58 | |
|
57 | 59 | QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); |
|
58 | 60 | |
|
59 | 61 | SqpApplication a { argc, argv }; |
|
60 | 62 | |
|
61 | 63 | MainWindow w; |
|
62 | 64 | w.show(); |
|
63 | 65 | |
|
64 | 66 | // Loads plugins |
|
65 | 67 | auto pluginDir = QDir { a.applicationDirPath() }; |
|
66 | 68 | auto pluginLookupPath = { |
|
67 | 69 | #if _WIN32 || _WIN64 |
|
68 | 70 | a.applicationDirPath() + "/SciQLop" |
|
69 | 71 | #else |
|
70 | 72 | a.applicationDirPath() + "/../lib64/SciQLop", |
|
71 | 73 | a.applicationDirPath() + "/../lib64/sciqlop", |
|
72 | 74 | a.applicationDirPath() + "/../lib/SciQLop", |
|
73 | 75 | a.applicationDirPath() + "/../lib/sciqlop", |
|
74 | 76 | #endif |
|
75 | 77 | }; |
|
76 | 78 | |
|
77 | 79 | #if _WIN32 || _WIN64 |
|
78 | 80 | pluginDir.mkdir(PLUGIN_DIRECTORY_NAME); |
|
79 | 81 | pluginDir.cd(PLUGIN_DIRECTORY_NAME); |
|
80 | 82 | #endif |
|
81 | 83 | |
|
82 | 84 | PluginManager pluginManager {}; |
|
83 | 85 | |
|
84 | 86 | for (auto&& path : pluginLookupPath) |
|
85 | 87 | { |
|
86 | 88 | QDir directory { path }; |
|
87 | 89 | if (directory.exists()) |
|
88 | 90 | { |
|
89 | 91 | qCDebug(LOG_Main()) |
|
90 | 92 | << QObject::tr("Plugin directory: %1").arg(directory.absolutePath()); |
|
91 | 93 | pluginManager.loadPlugins(directory); |
|
92 | 94 | } |
|
93 | 95 | } |
|
94 | 96 | pluginManager.loadStaticPlugins(); |
|
95 | 97 | |
|
96 | 98 | return a.exec(); |
|
97 | 99 | } |
@@ -1,1 +1,1 | |||
|
1 | Subproject commit 00b9e6e4b7f57c09175c396576ac6e30f45a14ff | |
|
1 | Subproject commit a8a4e48c21af5a28ad3f56582db7bbec0fe76978 |
@@ -1,59 +1,61 | |||
|
1 | 1 | #ifndef SCIQLOP_VARIABLEINSPECTORWIDGET_H |
|
2 | 2 | #define SCIQLOP_VARIABLEINSPECTORWIDGET_H |
|
3 | 3 | |
|
4 | 4 | #include <QLoggingCategory> |
|
5 | 5 | #include <QMenu> |
|
6 | 6 | #include <QWidget> |
|
7 | 7 | |
|
8 | 8 | #include <memory> |
|
9 | 9 | |
|
10 | 10 | #include <Variable/VariableModel2.h> |
|
11 | 11 | |
|
12 | 12 | Q_DECLARE_LOGGING_CATEGORY(LOG_VariableInspectorWidget) |
|
13 | 13 | |
|
14 | class Variable; | |
|
14 | class Variable2; | |
|
15 | 15 | |
|
16 | 16 | class QProgressBarItemDelegate; |
|
17 | 17 | |
|
18 |
namespace Ui |
|
|
18 | namespace Ui | |
|
19 | { | |
|
19 | 20 | class VariableInspectorWidget; |
|
20 | 21 | } // Ui |
|
21 | 22 | |
|
22 | 23 | /** |
|
23 | 24 | * @brief The VariableInspectorWidget class representes represents the variable inspector, from |
|
24 | 25 | * which it is possible to view the loaded variables, handle them or trigger their display in |
|
25 | 26 | * visualization |
|
26 | 27 | */ |
|
27 |
class VariableInspectorWidget : public QWidget |
|
|
28 | class VariableInspectorWidget : public QWidget | |
|
29 | { | |
|
28 | 30 | Q_OBJECT |
|
29 | 31 | |
|
30 | 32 | public: |
|
31 |
explicit VariableInspectorWidget(QWidget |
|
|
33 | explicit VariableInspectorWidget(QWidget* parent = 0); | |
|
32 | 34 | virtual ~VariableInspectorWidget(); |
|
33 | 35 | |
|
34 | 36 | signals: |
|
35 | 37 | /** |
|
36 | 38 | * Signal emitted before a menu concerning variables is displayed. It is used for other widgets |
|
37 | 39 | * to complete the menu. |
|
38 | 40 | * @param tableMenu the menu to be completed |
|
39 | 41 | * @param variables the variables concerned by the menu |
|
40 | 42 | * @remarks To make the dynamic addition of menus work, the connections to this signal must be |
|
41 | 43 | * in Qt :: DirectConnection |
|
42 | 44 | */ |
|
43 |
void tableMenuAboutToBeDisplayed( |
|
|
44 |
|
|
|
45 | void tableMenuAboutToBeDisplayed( | |
|
46 | QMenu* tableMenu, const QVector<std::shared_ptr<Variable2>>& variables); | |
|
45 | 47 | |
|
46 | 48 | private: |
|
47 |
Ui::VariableInspectorWidget |
|
|
49 | Ui::VariableInspectorWidget* ui; | |
|
48 | 50 | |
|
49 |
QProgressBarItemDelegate |
|
|
51 | QProgressBarItemDelegate* m_ProgressBarItemDelegate; | |
|
50 | 52 | VariableModel2* m_model; |
|
51 | 53 | |
|
52 | 54 | private slots: |
|
53 | 55 | /// Slot called when right clicking on an variable in the table (displays a menu) |
|
54 |
void onTableMenuRequested(const QPoint |
|
|
56 | void onTableMenuRequested(const QPoint& pos) noexcept; | |
|
55 | 57 | /// Refreshes instantly the variable view |
|
56 | 58 | void refresh() noexcept; |
|
57 | 59 | }; |
|
58 | 60 | |
|
59 | 61 | #endif // SCIQLOP_VARIABLEINSPECTORWIDGET_H |
@@ -1,37 +1,39 | |||
|
1 | 1 | #ifndef SCIQLOP_VARIABLEMENUHEADERWIDGET_H |
|
2 | 2 | #define SCIQLOP_VARIABLEMENUHEADERWIDGET_H |
|
3 | 3 | |
|
4 | 4 | #include <QLoggingCategory> |
|
5 | 5 | #include <QWidget> |
|
6 | 6 | |
|
7 | 7 | #include <memory> |
|
8 | 8 | |
|
9 |
namespace Ui |
|
|
9 | namespace Ui | |
|
10 | { | |
|
10 | 11 | class VariableMenuHeaderWidget; |
|
11 | 12 | } // Ui |
|
12 | 13 | |
|
13 | class Variable; | |
|
14 | class Variable2; | |
|
14 | 15 | |
|
15 | 16 | Q_DECLARE_LOGGING_CATEGORY(LOG_VariableMenuHeaderWidget) |
|
16 | 17 | |
|
17 | 18 | /** |
|
18 | 19 | * @brief The VariableMenuHeaderWidget class represents the widget used as a header of a menu in the |
|
19 | 20 | * variable inspector |
|
20 | 21 | * @sa VariableInspectorWidget |
|
21 | 22 | */ |
|
22 |
class VariableMenuHeaderWidget : public QWidget |
|
|
23 | class VariableMenuHeaderWidget : public QWidget | |
|
24 | { | |
|
23 | 25 | public: |
|
24 | 26 | /** |
|
25 | 27 | * Ctor |
|
26 | 28 | * @param variables the list of variables used to generate the header |
|
27 | 29 | * @param parent the parent widget |
|
28 | 30 | */ |
|
29 | explicit VariableMenuHeaderWidget(const QVector<std::shared_ptr<Variable> > &variables, | |
|
30 | QWidget *parent = 0); | |
|
31 | explicit VariableMenuHeaderWidget( | |
|
32 | const QVector<std::shared_ptr<Variable2>>& variables, QWidget* parent = 0); | |
|
31 | 33 | virtual ~VariableMenuHeaderWidget() noexcept; |
|
32 | 34 | |
|
33 | 35 | private: |
|
34 |
Ui::VariableMenuHeaderWidget |
|
|
36 | Ui::VariableMenuHeaderWidget* ui; | |
|
35 | 37 | }; |
|
36 | 38 | |
|
37 | 39 | #endif // SCIQLOP_VARIABLEMENUHEADERWIDGET_H |
@@ -1,44 +1,46 | |||
|
1 | 1 | #ifndef SCIQLOP_AXISRENDERINGUTILS_H |
|
2 | 2 | #define SCIQLOP_AXISRENDERINGUTILS_H |
|
3 | 3 | |
|
4 | 4 | #include <memory> |
|
5 | 5 | |
|
6 | 6 | #include <QtCore/QLoggingCategory> |
|
7 | 7 | #include <QtCore/QString> |
|
8 | 8 | |
|
9 | 9 | Q_DECLARE_LOGGING_CATEGORY(LOG_AxisRenderingUtils) |
|
10 | 10 | |
|
11 | 11 | class IDataSeries; |
|
12 | 12 | class QCPAxis; |
|
13 | 13 | class QCustomPlot; |
|
14 | 14 | class SqpColorScale; |
|
15 | class Variable; | |
|
15 | class Variable2; | |
|
16 | 16 | |
|
17 | 17 | /// Formats a data value according to the axis on which it is present |
|
18 |
QString formatValue(double value, const QCPAxis |
|
|
18 | QString formatValue(double value, const QCPAxis& axis); | |
|
19 | 19 | |
|
20 | 20 | /** |
|
21 | 21 | * Helper used to handle axes rendering |
|
22 | 22 | */ |
|
23 |
struct IAxisHelper |
|
|
23 | struct IAxisHelper | |
|
24 | { | |
|
24 | 25 | virtual ~IAxisHelper() noexcept = default; |
|
25 | 26 | |
|
26 | 27 | /// Set properties of the plot's axes and the color scale associated to plot passed as |
|
27 | 28 | /// parameters |
|
28 | 29 | /// @param plot the plot for which to set axe properties |
|
29 | 30 | /// @param colorScale the color scale for which to set properties |
|
30 |
virtual void setProperties(QCustomPlot |
|
|
31 | virtual void setProperties(QCustomPlot& plot, SqpColorScale& colorScale) = 0; | |
|
31 | 32 | |
|
32 | 33 | /// Set the units of the plot's axes and the color scale associated to plot passed as |
|
33 | 34 | /// parameters |
|
34 | 35 | /// @param plot the plot for which to set axe units |
|
35 | 36 | /// @param colorScale the color scale for which to set unit |
|
36 |
virtual void setUnits(QCustomPlot |
|
|
37 | virtual void setUnits(QCustomPlot& plot, SqpColorScale& colorScale) = 0; | |
|
37 | 38 | }; |
|
38 | 39 | |
|
39 |
struct IAxisHelperFactory |
|
|
40 | struct IAxisHelperFactory | |
|
41 | { | |
|
40 | 42 | /// Creates IPlottablesHelper according to the type of data series a variable holds |
|
41 |
static std::unique_ptr<IAxisHelper> create( |
|
|
43 | static std::unique_ptr<IAxisHelper> create(Variable2& variable) noexcept; | |
|
42 | 44 | }; |
|
43 | 45 | |
|
44 | 46 | #endif // SCIQLOP_AXISRENDERINGUTILS_H |
@@ -1,22 +1,23 | |||
|
1 | 1 | #ifndef SCIQLOP_IVARIABLECONTAINER_H |
|
2 | 2 | #define SCIQLOP_IVARIABLECONTAINER_H |
|
3 | 3 | |
|
4 | class Variable; | |
|
4 | class Variable2; | |
|
5 | 5 | |
|
6 | 6 | /** |
|
7 | 7 | * @brief The IVariableContainer interface represents an UI object that can accommodate a variable |
|
8 | 8 | */ |
|
9 |
class IVariableContainer |
|
|
9 | class IVariableContainer | |
|
10 | { | |
|
10 | 11 | |
|
11 | 12 | public: |
|
12 | 13 | virtual ~IVariableContainer() = default; |
|
13 | 14 | |
|
14 | 15 | /// Checks if the container can handle the variable passed in parameter |
|
15 |
virtual bool canDrop( |
|
|
16 | virtual bool canDrop(Variable2& variable) const = 0; | |
|
16 | 17 | |
|
17 | 18 | /// Checks if the container contains the variable passed in parameter |
|
18 |
virtual bool contains( |
|
|
19 | virtual bool contains(Variable2& variable) const = 0; | |
|
19 | 20 | }; |
|
20 | 21 | |
|
21 | 22 | |
|
22 | 23 | #endif // SCIQLOP_IVARIABLECONTAINER_H |
@@ -1,34 +1,36 | |||
|
1 | 1 | #ifndef SCIQLOP_PLOTTABLESRENDERINGUTILS_H |
|
2 | 2 | #define SCIQLOP_PLOTTABLESRENDERINGUTILS_H |
|
3 | 3 | |
|
4 | 4 | #include <Data/DataSeriesType.h> |
|
5 | 5 | |
|
6 | 6 | #include <Visualization/VisualizationDefs.h> |
|
7 | 7 | |
|
8 | 8 | #include <memory> |
|
9 | 9 | |
|
10 | 10 | #include <QtCore/QLoggingCategory> |
|
11 | 11 | |
|
12 | 12 | Q_DECLARE_LOGGING_CATEGORY(LOG_PlottablesRenderingUtils) |
|
13 | 13 | |
|
14 | 14 | class QCPColorScale; |
|
15 | 15 | class QCustomPlot; |
|
16 | class Variable; | |
|
16 | class Variable2; | |
|
17 | 17 | |
|
18 | 18 | /** |
|
19 | 19 | * Helper used to handle plottables rendering |
|
20 | 20 | */ |
|
21 |
struct IPlottablesHelper |
|
|
21 | struct IPlottablesHelper | |
|
22 | { | |
|
22 | 23 | virtual ~IPlottablesHelper() noexcept = default; |
|
23 | 24 | |
|
24 | 25 | /// Set properties of the plottables passed as parameter |
|
25 | 26 | /// @param plottables the plottables for which to set properties |
|
26 |
virtual void setProperties(PlottablesMap |
|
|
27 | virtual void setProperties(PlottablesMap& plottables) = 0; | |
|
27 | 28 | }; |
|
28 | 29 | |
|
29 |
struct IPlottablesHelperFactory |
|
|
30 | struct IPlottablesHelperFactory | |
|
31 | { | |
|
30 | 32 | /// Creates IPlottablesHelper according to the type of data series a variable holds |
|
31 |
static std::unique_ptr<IPlottablesHelper> create( |
|
|
33 | static std::unique_ptr<IPlottablesHelper> create(Variable2& variable) noexcept; | |
|
32 | 34 | }; |
|
33 | 35 | |
|
34 | 36 | #endif // SCIQLOP_PLOTTABLESRENDERINGUTILS_H |
@@ -1,41 +1,42 | |||
|
1 | 1 | #ifndef SCIQLOP_VISUALIZATIONGRAPHHELPER_H |
|
2 | 2 | #define SCIQLOP_VISUALIZATIONGRAPHHELPER_H |
|
3 | 3 | |
|
4 | 4 | #include "Visualization/VisualizationDefs.h" |
|
5 | 5 | |
|
6 | 6 | #include <Data/DateTimeRange.h> |
|
7 | 7 | |
|
8 | 8 | #include <QLoggingCategory> |
|
9 | 9 | #include <QVector> |
|
10 | 10 | |
|
11 | 11 | #include <memory> |
|
12 | 12 | |
|
13 | 13 | Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationGraphHelper) |
|
14 | 14 | |
|
15 | 15 | class IDataSeries; |
|
16 | 16 | class QCPAbstractPlottable; |
|
17 | 17 | class QCustomPlot; |
|
18 | class Variable; | |
|
18 | class Variable2; | |
|
19 | 19 | |
|
20 | 20 | /** |
|
21 | 21 | * @brief The VisualizationGraphHelper class aims to create the QCustomPlot components relative to a |
|
22 | 22 | * variable, depending on the data series of this variable |
|
23 | 23 | */ |
|
24 |
struct VisualizationGraphHelper |
|
|
24 | struct VisualizationGraphHelper | |
|
25 | { | |
|
25 | 26 | /** |
|
26 | 27 | * Creates (if possible) the QCustomPlot components relative to the variable passed in |
|
27 | 28 | * parameter, and adds these to the plot passed in parameter. |
|
28 | 29 | * @param variable the variable for which to create the components |
|
29 | 30 | * @param plot the plot in which to add the created components. It takes ownership of these |
|
30 | 31 | * components. |
|
31 | 32 | * @return the list of the components created |
|
32 | 33 | */ |
|
33 |
static PlottablesMap create(std::shared_ptr<Variable> variable, QCustomPlot |
|
|
34 | static PlottablesMap create(std::shared_ptr<Variable2> variable, QCustomPlot& plot) noexcept; | |
|
34 | 35 | |
|
35 |
static void updateData(PlottablesMap |
|
|
36 |
|
|
|
36 | static void updateData(PlottablesMap& plottables, std::shared_ptr<Variable2> variable, | |
|
37 | const DateTimeRange& dateTime); | |
|
37 | 38 | |
|
38 |
static void setYAxisRange(std::shared_ptr<Variable> variable, QCustomPlot |
|
|
39 | static void setYAxisRange(std::shared_ptr<Variable2> variable, QCustomPlot& plot) noexcept; | |
|
39 | 40 | }; |
|
40 | 41 | |
|
41 | 42 | #endif // SCIQLOP_VISUALIZATIONGRAPHHELPER_H |
@@ -1,44 +1,45 | |||
|
1 | 1 | #ifndef SCIQLOP_VISUALIZATIONGRAPHRENDERINGDELEGATE_H |
|
2 | 2 | #define SCIQLOP_VISUALIZATIONGRAPHRENDERINGDELEGATE_H |
|
3 | 3 | |
|
4 | 4 | #include <Common/spimpl.h> |
|
5 | 5 | |
|
6 | 6 | #include <Visualization/VisualizationDefs.h> |
|
7 | 7 | |
|
8 | 8 | class IDataSeries; |
|
9 | 9 | class QCustomPlot; |
|
10 | 10 | class QMouseEvent; |
|
11 | 11 | class Unit; |
|
12 | class Variable; | |
|
12 | class Variable2; | |
|
13 | 13 | class VisualizationGraphWidget; |
|
14 | 14 | |
|
15 |
class VisualizationGraphRenderingDelegate |
|
|
15 | class VisualizationGraphRenderingDelegate | |
|
16 | { | |
|
16 | 17 | public: |
|
17 | 18 | /// Ctor |
|
18 | 19 | /// @param graphWidget the graph widget to which the delegate is associated |
|
19 | 20 | /// @remarks the graph widget must exist throughout the life cycle of the delegate |
|
20 |
explicit VisualizationGraphRenderingDelegate(VisualizationGraphWidget |
|
|
21 | explicit VisualizationGraphRenderingDelegate(VisualizationGraphWidget& graphWidget); | |
|
21 | 22 | |
|
22 |
void onMouseDoubleClick(QMouseEvent |
|
|
23 |
void updateTooltip(QMouseEvent |
|
|
23 | void onMouseDoubleClick(QMouseEvent* event) noexcept; | |
|
24 | void updateTooltip(QMouseEvent* event) noexcept; | |
|
24 | 25 | /// Updates rendering when data of plot changed |
|
25 | 26 | void onPlotUpdated() noexcept; |
|
26 | 27 | |
|
27 | 28 | /// Sets units of the plot's axes according to the properties of the variable passed as |
|
28 | 29 | /// parameter |
|
29 |
void setAxesUnits( |
|
|
30 | void setAxesUnits(Variable2& variable) noexcept; | |
|
30 | 31 | |
|
31 | 32 | /// Sets graph properties of the plottables passed as parameter, from the variable that |
|
32 | 33 | /// generated these |
|
33 |
void setGraphProperties( |
|
|
34 | void setGraphProperties(Variable2& variable, PlottablesMap& plottables) noexcept; | |
|
34 | 35 | |
|
35 | 36 | |
|
36 | 37 | /// Shows or hides graph overlay (name, close button, etc.) |
|
37 | 38 | void showGraphOverlay(bool show) noexcept; |
|
38 | 39 | |
|
39 | 40 | private: |
|
40 | 41 | class VisualizationGraphRenderingDelegatePrivate; |
|
41 | 42 | spimpl::unique_impl_ptr<VisualizationGraphRenderingDelegatePrivate> impl; |
|
42 | 43 | }; |
|
43 | 44 | |
|
44 | 45 | #endif // SCIQLOP_VISUALIZATIONGRAPHRENDERINGDELEGATE_H |
@@ -1,181 +1,185 | |||
|
1 | 1 | #ifndef SCIQLOP_VISUALIZATIONGRAPHWIDGET_H |
|
2 | 2 | #define SCIQLOP_VISUALIZATIONGRAPHWIDGET_H |
|
3 | 3 | |
|
4 | 4 | #include "Visualization/IVisualizationWidget.h" |
|
5 | 5 | #include "Visualization/VisualizationDragWidget.h" |
|
6 | 6 | |
|
7 | 7 | #include <QLoggingCategory> |
|
8 | #include <QWidget> | |
|
9 | 8 | #include <QUuid> |
|
9 | #include <QWidget> | |
|
10 | 10 | |
|
11 | 11 | #include <memory> |
|
12 | 12 | |
|
13 | 13 | #include <Common/spimpl.h> |
|
14 | 14 | |
|
15 | 15 | #include <Data/DateTimeRange.h> |
|
16 | 16 | |
|
17 | 17 | Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationGraphWidget) |
|
18 | 18 | |
|
19 | 19 | class QCPRange; |
|
20 | 20 | class QCustomPlot; |
|
21 | class Variable; | |
|
21 | class Variable2; | |
|
22 | 22 | class VisualizationWidget; |
|
23 | 23 | class VisualizationZoneWidget; |
|
24 | 24 | class VisualizationSelectionZoneItem; |
|
25 | 25 | |
|
26 |
namespace Ui |
|
|
26 | namespace Ui | |
|
27 | { | |
|
27 | 28 | class VisualizationGraphWidget; |
|
28 | 29 | } // namespace Ui |
|
29 | 30 | |
|
30 | 31 | /// Defines options that can be associated with the graph |
|
31 |
enum GraphFlag |
|
|
32 | DisableAll = 0x0, ///< Disables acquisition and synchronization | |
|
32 | enum GraphFlag | |
|
33 | { | |
|
34 | DisableAll = 0x0, ///< Disables acquisition and synchronization | |
|
33 | 35 | EnableAcquisition = 0x1, ///< When this flag is set, the change of the graph's range leads to |
|
34 | 36 | /// the acquisition of data |
|
35 | 37 | EnableSynchronization = 0x2, ///< When this flag is set, the change of the graph's range causes |
|
36 | 38 | /// the call to the synchronization of the graphs contained in the |
|
37 | 39 | /// same zone of this graph |
|
38 | 40 | EnableAll = ~DisableAll ///< Enables acquisition and synchronization |
|
39 | 41 | }; |
|
40 | 42 | |
|
41 | 43 | Q_DECLARE_FLAGS(GraphFlags, GraphFlag) |
|
42 | 44 | Q_DECLARE_OPERATORS_FOR_FLAGS(GraphFlags) |
|
43 | 45 | |
|
44 |
class VisualizationGraphWidget : public VisualizationDragWidget, public IVisualizationWidget |
|
|
46 | class VisualizationGraphWidget : public VisualizationDragWidget, public IVisualizationWidget | |
|
47 | { | |
|
45 | 48 | Q_OBJECT |
|
46 | 49 | |
|
47 | 50 | friend class QCustomPlotSynchronizer; |
|
48 | 51 | friend class VisualizationGraphRenderingDelegate; |
|
49 | 52 | |
|
50 | 53 | public: |
|
51 |
explicit VisualizationGraphWidget(const QString |
|
|
54 | explicit VisualizationGraphWidget(const QString& name = {}, QWidget* parent = 0); | |
|
52 | 55 | virtual ~VisualizationGraphWidget(); |
|
53 | 56 | |
|
54 | 57 | /// Returns the VisualizationZoneWidget which contains the graph or nullptr |
|
55 |
VisualizationZoneWidget |
|
|
58 | VisualizationZoneWidget* parentZoneWidget() const noexcept; | |
|
56 | 59 | |
|
57 | 60 | /// Returns the main VisualizationWidget which contains the graph or nullptr |
|
58 |
VisualizationWidget |
|
|
61 | VisualizationWidget* parentVisualizationWidget() const; | |
|
59 | 62 | |
|
60 | 63 | /// Sets graph options |
|
61 | 64 | void setFlags(GraphFlags flags); |
|
62 | 65 | |
|
63 | void addVariable(std::shared_ptr<Variable> variable, DateTimeRange range); | |
|
66 | void addVariable(std::shared_ptr<Variable2> variable, DateTimeRange range); | |
|
64 | 67 | |
|
65 | 68 | /// Removes a variable from the graph |
|
66 | void removeVariable(std::shared_ptr<Variable> variable) noexcept; | |
|
69 | void removeVariable(std::shared_ptr<Variable2> variable) noexcept; | |
|
67 | 70 | |
|
68 | 71 | /// Returns the list of all variables used in the graph |
|
69 |
std::vector<std::shared_ptr<Variable> |
|
|
72 | std::vector<std::shared_ptr<Variable2>> variables() const; | |
|
70 | 73 | |
|
71 | 74 | /// Sets the y-axis range based on the data of a variable |
|
72 | void setYRange(std::shared_ptr<Variable> variable); | |
|
75 | void setYRange(std::shared_ptr<Variable2> variable); | |
|
73 | 76 | DateTimeRange graphRange() const noexcept; |
|
74 |
void setGraphRange(const DateTimeRange |
|
|
77 | void setGraphRange(const DateTimeRange& range, bool updateVar = false, bool forward = false); | |
|
75 | 78 | void setAutoRangeOnVariableInitialization(bool value); |
|
76 | 79 | |
|
77 | 80 | // Zones |
|
78 | 81 | /// Returns the ranges of all the selection zones on the graph |
|
79 | 82 | QVector<DateTimeRange> selectionZoneRanges() const; |
|
80 | 83 | /// Adds new selection zones in the graph |
|
81 |
void addSelectionZones(const QVector<DateTimeRange> |
|
|
84 | void addSelectionZones(const QVector<DateTimeRange>& ranges); | |
|
82 | 85 | /// Adds a new selection zone in the graph |
|
83 |
VisualizationSelectionZoneItem |
|
|
86 | VisualizationSelectionZoneItem* addSelectionZone( | |
|
87 | const QString& name, const DateTimeRange& range); | |
|
84 | 88 | /// Removes the specified selection zone |
|
85 |
void removeSelectionZone(VisualizationSelectionZoneItem |
|
|
89 | void removeSelectionZone(VisualizationSelectionZoneItem* selectionZone); | |
|
86 | 90 | |
|
87 | 91 | /// Undo the last zoom done with a zoom box |
|
88 | 92 | void undoZoom(); |
|
89 | 93 | |
|
90 | void zoom(double factor, int center, Qt::Orientation orientation, bool forward=true); | |
|
91 | void move(double factor, Qt::Orientation orientation, bool forward=true); | |
|
92 | void move(double dx, double dy, bool forward=true); | |
|
93 | void transform(const DateTimeRangeTransformation& tranformation, bool forward=true); | |
|
94 | void zoom(double factor, int center, Qt::Orientation orientation, bool forward = true); | |
|
95 | void move(double factor, Qt::Orientation orientation, bool forward = true); | |
|
96 | void move(double dx, double dy, bool forward = true); | |
|
97 | void transform(const DateTimeRangeTransformation& tranformation, bool forward = true); | |
|
94 | 98 | |
|
95 | 99 | // IVisualizationWidget interface |
|
96 |
void accept(IVisualizationWidgetVisitor |
|
|
97 |
bool canDrop( |
|
|
98 |
bool contains( |
|
|
100 | void accept(IVisualizationWidgetVisitor* visitor) override; | |
|
101 | bool canDrop(Variable2& variable) const override; | |
|
102 | bool contains(Variable2& variable) const override; | |
|
99 | 103 | QString name() const override; |
|
100 | 104 | |
|
101 | 105 | // VisualisationDragWidget |
|
102 |
QMimeData |
|
|
103 |
QPixmap customDragPixmap(const QPoint |
|
|
106 | QMimeData* mimeData(const QPoint& position) const override; | |
|
107 | QPixmap customDragPixmap(const QPoint& dragPosition) override; | |
|
104 | 108 | bool isDragAllowed() const override; |
|
105 | 109 | void highlightForMerge(bool highlighted) override; |
|
106 | 110 | |
|
107 | 111 | // Cursors |
|
108 | 112 | /// Adds or moves the vertical cursor at the specified value on the x-axis |
|
109 | 113 | void addVerticalCursor(double time); |
|
110 | 114 | /// Adds or moves the vertical cursor at the specified value on the x-axis |
|
111 | 115 | void addVerticalCursorAtViewportPosition(double position); |
|
112 | 116 | void removeVerticalCursor(); |
|
113 | 117 | /// Adds or moves the vertical cursor at the specified value on the y-axis |
|
114 | 118 | void addHorizontalCursor(double value); |
|
115 | 119 | /// Adds or moves the vertical cursor at the specified value on the y-axis |
|
116 | 120 | void addHorizontalCursorAtViewportPosition(double position); |
|
117 | 121 | void removeHorizontalCursor(); |
|
118 | 122 | |
|
119 | 123 | signals: |
|
120 |
void synchronize(const DateTimeRange |
|
|
121 |
void changeRange(const std::shared_ptr<Variable>& variable, const DateTimeRange |
|
|
124 | void synchronize(const DateTimeRange& range, const DateTimeRange& oldRange); | |
|
125 | void changeRange(const std::shared_ptr<Variable2>& variable, const DateTimeRange& range); | |
|
122 | 126 | |
|
123 | 127 | /// Signal emitted when the variable is about to be removed from the graph |
|
124 | void variableAboutToBeRemoved(std::shared_ptr<Variable> var); | |
|
128 | void variableAboutToBeRemoved(std::shared_ptr<Variable2> var); | |
|
125 | 129 | /// Signal emitted when the variable has been added to the graph |
|
126 | void variableAdded(std::shared_ptr<Variable> var); | |
|
130 | void variableAdded(std::shared_ptr<Variable2> var); | |
|
127 | 131 | |
|
128 | 132 | |
|
129 | void zoom_sig(double factor, int center, Qt::Orientation orientation, bool forward=true); | |
|
130 | void move_sig(double factor, Qt::Orientation orientation, bool forward=true); | |
|
131 | void move_sig(double dx, double dy, bool forward=true); | |
|
132 | void transform_sig(const DateTimeRangeTransformation& tranformation, bool forward=true); | |
|
133 | void setrange_sig(const DateTimeRange& range, bool updateVar=false, bool forward=true); | |
|
133 | void zoom_sig(double factor, int center, Qt::Orientation orientation, bool forward = true); | |
|
134 | void move_sig(double factor, Qt::Orientation orientation, bool forward = true); | |
|
135 | void move_sig(double dx, double dy, bool forward = true); | |
|
136 | void transform_sig(const DateTimeRangeTransformation& tranformation, bool forward = true); | |
|
137 | void setrange_sig(const DateTimeRange& range, bool updateVar = false, bool forward = true); | |
|
134 | 138 | |
|
135 | 139 | protected: |
|
136 |
void closeEvent(QCloseEvent |
|
|
137 |
void enterEvent(QEvent |
|
|
138 |
void leaveEvent(QEvent |
|
|
139 |
void wheelEvent(QWheelEvent |
|
|
140 |
void mouseMoveEvent(QMouseEvent |
|
|
141 |
void mouseReleaseEvent(QMouseEvent |
|
|
142 |
void mousePressEvent(QMouseEvent |
|
|
143 |
void mouseDoubleClickEvent(QMouseEvent |
|
|
144 |
void keyReleaseEvent(QKeyEvent |
|
|
145 |
void keyPressEvent(QKeyEvent |
|
|
146 | ||
|
147 |
QCustomPlot |
|
|
140 | void closeEvent(QCloseEvent* event) override; | |
|
141 | void enterEvent(QEvent* event) override; | |
|
142 | void leaveEvent(QEvent* event) override; | |
|
143 | void wheelEvent(QWheelEvent* event) override; | |
|
144 | void mouseMoveEvent(QMouseEvent* event) override; | |
|
145 | void mouseReleaseEvent(QMouseEvent* event) override; | |
|
146 | void mousePressEvent(QMouseEvent* event) override; | |
|
147 | void mouseDoubleClickEvent(QMouseEvent* event) override; | |
|
148 | void keyReleaseEvent(QKeyEvent* event) override; | |
|
149 | void keyPressEvent(QKeyEvent* event) override; | |
|
150 | ||
|
151 | QCustomPlot& plot() const noexcept; | |
|
148 | 152 | |
|
149 | 153 | private: |
|
150 |
Ui::VisualizationGraphWidget |
|
|
154 | Ui::VisualizationGraphWidget* ui; | |
|
151 | 155 | |
|
152 | 156 | class VisualizationGraphWidgetPrivate; |
|
153 | 157 | spimpl::unique_impl_ptr<VisualizationGraphWidgetPrivate> impl; |
|
154 | 158 | |
|
155 | 159 | private slots: |
|
156 | 160 | /// Slot called when right clicking on the graph (displays a menu) |
|
157 |
void onGraphMenuRequested(const QPoint |
|
|
161 | void onGraphMenuRequested(const QPoint& pos) noexcept; | |
|
158 | 162 | |
|
159 | 163 | /// Rescale the X axe to range parameter |
|
160 | // void onRangeChanged(const QCPRange &newRange, const QCPRange &oldRange); | |
|
164 | // void onRangeChanged(const QCPRange &newRange, const QCPRange &oldRange); | |
|
161 | 165 | |
|
162 | 166 | /// Slot called when a mouse double click was made |
|
163 |
void onMouseDoubleClick(QMouseEvent |
|
|
167 | void onMouseDoubleClick(QMouseEvent* event) noexcept; | |
|
164 | 168 | /// Slot called when a mouse move was made |
|
165 |
void onMouseMove(QMouseEvent |
|
|
169 | void onMouseMove(QMouseEvent* event) noexcept; | |
|
166 | 170 | /// Slot called when a mouse wheel was made, to perform some processing before the zoom is done |
|
167 |
void onMouseWheel(QWheelEvent |
|
|
171 | void onMouseWheel(QWheelEvent* event) noexcept; | |
|
168 | 172 | /// Slot called when a mouse press was made, to activate the calibration of a graph |
|
169 |
void onMousePress(QMouseEvent |
|
|
173 | void onMousePress(QMouseEvent* event) noexcept; | |
|
170 | 174 | /// Slot called when a mouse release was made, to deactivate the calibration of a graph |
|
171 |
void onMouseRelease(QMouseEvent |
|
|
175 | void onMouseRelease(QMouseEvent* event) noexcept; | |
|
172 | 176 | |
|
173 | 177 | void onDataCacheVariableUpdated(); |
|
174 | 178 | |
|
175 |
void onUpdateVarDisplaying(std::shared_ptr<Variable> variable, const DateTimeRange |
|
|
179 | void onUpdateVarDisplaying(std::shared_ptr<Variable2> variable, const DateTimeRange& range); | |
|
176 | 180 | |
|
177 | 181 | void variableUpdated(QUuid id); |
|
178 | void variableDeleted(const std::shared_ptr<Variable>&); | |
|
182 | void variableDeleted(const std::shared_ptr<Variable2>&); | |
|
179 | 183 | }; |
|
180 | 184 | |
|
181 | 185 | #endif // SCIQLOP_VISUALIZATIONGRAPHWIDGET_H |
@@ -1,88 +1,90 | |||
|
1 | 1 | #ifndef SCIQLOP_VISUALIZATIONTABWIDGET_H |
|
2 | 2 | #define SCIQLOP_VISUALIZATIONTABWIDGET_H |
|
3 | 3 | |
|
4 | 4 | #include "Visualization/IVisualizationWidget.h" |
|
5 | 5 | |
|
6 | 6 | #include <Common/spimpl.h> |
|
7 | 7 | |
|
8 | 8 | #include <QLoggingCategory> |
|
9 | 9 | #include <QMimeData> |
|
10 | 10 | #include <QWidget> |
|
11 | 11 | |
|
12 | 12 | Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationTabWidget) |
|
13 | 13 | |
|
14 | 14 | class Variable; |
|
15 | 15 | class VisualizationZoneWidget; |
|
16 | 16 | |
|
17 |
namespace Ui |
|
|
17 | namespace Ui | |
|
18 | { | |
|
18 | 19 | class VisualizationTabWidget; |
|
19 | 20 | } // namespace Ui |
|
20 | 21 | |
|
21 |
class VisualizationTabWidget : public QWidget, public IVisualizationWidget |
|
|
22 | class VisualizationTabWidget : public QWidget, public IVisualizationWidget | |
|
23 | { | |
|
22 | 24 | Q_OBJECT |
|
23 | 25 | |
|
24 | 26 | public: |
|
25 |
explicit VisualizationTabWidget(const QString |
|
|
27 | explicit VisualizationTabWidget(const QString& name = {}, QWidget* parent = 0); | |
|
26 | 28 | virtual ~VisualizationTabWidget(); |
|
27 | 29 | |
|
28 | 30 | /// Adds a zone widget |
|
29 |
void addZone(VisualizationZoneWidget |
|
|
31 | void addZone(VisualizationZoneWidget* zoneWidget); | |
|
30 | 32 | |
|
31 | 33 | /// Inserts a zone widget at the specified position |
|
32 |
void insertZone(int index, VisualizationZoneWidget |
|
|
34 | void insertZone(int index, VisualizationZoneWidget* zoneWidget); | |
|
33 | 35 | |
|
34 | 36 | /// Returns the list of zone widget names in the order they are displayed |
|
35 | 37 | QStringList availableZoneWidgets() const; |
|
36 | 38 | |
|
37 | 39 | /// Returns the zone with the specified name. |
|
38 | 40 | /// If multiple zone with the same name exist, the first one is returned. |
|
39 |
VisualizationZoneWidget |
|
|
41 | VisualizationZoneWidget* getZoneWithName(const QString& zoneName); | |
|
40 | 42 | |
|
41 | 43 | /** |
|
42 | 44 | * Creates a zone using a variable. The variable will be displayed in a new graph of the new |
|
43 | 45 | * zone. The zone is added at the end. |
|
44 | 46 | * @param variable the variable for which to create the zone |
|
45 | 47 | * @return the pointer to the created zone |
|
46 | 48 | */ |
|
47 |
VisualizationZoneWidget |
|
|
49 | VisualizationZoneWidget* createZone(std::shared_ptr<Variable2> variable); | |
|
48 | 50 | |
|
49 | 51 | /** |
|
50 | 52 | * Creates a zone using a list of variables. The variables will be displayed in a new graph of |
|
51 | 53 | * the new zone. The zone is inserted at the specified index. |
|
52 | 54 | * @param variables the variables for which to create the zone |
|
53 | 55 | * @param index The index where the zone should be inserted in the layout |
|
54 | 56 | * @return the pointer to the created zone |
|
55 | 57 | */ |
|
56 | VisualizationZoneWidget *createZone(const std::vector<std::shared_ptr<Variable> > &variables, | |
|
57 | int index); | |
|
58 | VisualizationZoneWidget* createZone( | |
|
59 | const std::vector<std::shared_ptr<Variable2>>& variables, int index); | |
|
58 | 60 | |
|
59 | 61 | /** |
|
60 | 62 | * Creates a zone which is empty (no variables). The zone is inserted at the specified index. |
|
61 | 63 | * @param index The index where the zone should be inserted in the layout |
|
62 | 64 | * @return the pointer to the created zone |
|
63 | 65 | */ |
|
64 |
VisualizationZoneWidget |
|
|
66 | VisualizationZoneWidget* createEmptyZone(int index); | |
|
65 | 67 | |
|
66 | 68 | // IVisualizationWidget interface |
|
67 |
void accept(IVisualizationWidgetVisitor |
|
|
68 |
bool canDrop( |
|
|
69 |
bool contains( |
|
|
69 | void accept(IVisualizationWidgetVisitor* visitor) override; | |
|
70 | bool canDrop(Variable2& variable) const override; | |
|
71 | bool contains(Variable2& variable) const override; | |
|
70 | 72 | QString name() const override; |
|
71 | 73 | |
|
72 | 74 | protected: |
|
73 |
void closeEvent(QCloseEvent |
|
|
75 | void closeEvent(QCloseEvent* event) override; | |
|
74 | 76 | |
|
75 | 77 | private: |
|
76 | 78 | /// @return the layout of tab in which zones are added |
|
77 |
QLayout |
|
|
79 | QLayout& tabLayout() const noexcept; | |
|
78 | 80 | |
|
79 |
Ui::VisualizationTabWidget |
|
|
81 | Ui::VisualizationTabWidget* ui; | |
|
80 | 82 | |
|
81 | 83 | class VisualizationTabWidgetPrivate; |
|
82 | 84 | spimpl::unique_impl_ptr<VisualizationTabWidgetPrivate> impl; |
|
83 | 85 | |
|
84 | 86 | private slots: |
|
85 |
void dropMimeData(int index, const QMimeData |
|
|
87 | void dropMimeData(int index, const QMimeData* mimeData); | |
|
86 | 88 | }; |
|
87 | 89 | |
|
88 | 90 | #endif // SCIQLOP_VISUALIZATIONTABWIDGET_H |
@@ -1,65 +1,67 | |||
|
1 | 1 | #ifndef SCIQLOP_VISUALIZATIONWIDGET_H |
|
2 | 2 | #define SCIQLOP_VISUALIZATIONWIDGET_H |
|
3 | 3 | |
|
4 | 4 | #include "Visualization/IVisualizationWidget.h" |
|
5 | 5 | #include <Data/DateTimeRange.h> |
|
6 | 6 | |
|
7 | 7 | #include <QLoggingCategory> |
|
8 | 8 | #include <QWidget> |
|
9 | 9 | |
|
10 | 10 | #include <Common/spimpl.h> |
|
11 | 11 | |
|
12 | 12 | Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationWidget) |
|
13 | 13 | |
|
14 | 14 | class QMenu; |
|
15 | class Variable; | |
|
15 | class Variable2; | |
|
16 | 16 | class VisualizationTabWidget; |
|
17 | 17 | class VisualizationSelectionZoneManager; |
|
18 | 18 | |
|
19 |
namespace Ui |
|
|
19 | namespace Ui | |
|
20 | { | |
|
20 | 21 | class VisualizationWidget; |
|
21 | 22 | } // namespace Ui |
|
22 | 23 | |
|
23 |
class VisualizationWidget : public QWidget, public IVisualizationWidget |
|
|
24 | class VisualizationWidget : public QWidget, public IVisualizationWidget | |
|
25 | { | |
|
24 | 26 | Q_OBJECT |
|
25 | 27 | |
|
26 | 28 | public: |
|
27 |
explicit VisualizationWidget(QWidget |
|
|
29 | explicit VisualizationWidget(QWidget* parent = 0); | |
|
28 | 30 | virtual ~VisualizationWidget(); |
|
29 | 31 | |
|
30 | 32 | /// Returns the class which manage the selection of selection zone across the visualization |
|
31 |
VisualizationSelectionZoneManager |
|
|
33 | VisualizationSelectionZoneManager& selectionZoneManager() const; | |
|
32 | 34 | |
|
33 |
VisualizationTabWidget |
|
|
35 | VisualizationTabWidget* currentTabWidget() const; | |
|
34 | 36 | |
|
35 | 37 | // IVisualizationWidget interface |
|
36 |
void accept(IVisualizationWidgetVisitor |
|
|
37 |
bool canDrop( |
|
|
38 |
bool contains( |
|
|
38 | void accept(IVisualizationWidgetVisitor* visitor) override; | |
|
39 | bool canDrop(Variable2& variable) const override; | |
|
40 | bool contains(Variable2& variable) const override; | |
|
39 | 41 | QString name() const override; |
|
40 | 42 | |
|
41 | 43 | public slots: |
|
42 | 44 | /** |
|
43 | 45 | * Attaches to a menu the menu relative to the visualization of variables |
|
44 | 46 | * @param menu the parent menu of the generated menu |
|
45 | 47 | * @param variables the variables for which to generate the menu |
|
46 | 48 | */ |
|
47 |
void attachVariableMenu( |
|
|
48 |
|
|
|
49 | void attachVariableMenu( | |
|
50 | QMenu* menu, const QVector<std::shared_ptr<Variable2>>& variables) noexcept; | |
|
49 | 51 | |
|
50 | 52 | /// Slot called when a variable is about to be deleted from SciQlop |
|
51 | void onVariableAboutToBeDeleted(std::shared_ptr<Variable> variable) noexcept; | |
|
53 | void onVariableAboutToBeDeleted(std::shared_ptr<Variable2> variable) noexcept; | |
|
52 | 54 | |
|
53 |
void onRangeChanged(std::shared_ptr<Variable> variable, const DateTimeRange |
|
|
55 | void onRangeChanged(std::shared_ptr<Variable2> variable, const DateTimeRange& range) noexcept; | |
|
54 | 56 | |
|
55 | 57 | protected: |
|
56 |
void closeEvent(QCloseEvent |
|
|
58 | void closeEvent(QCloseEvent* event) override; | |
|
57 | 59 | |
|
58 | 60 | private: |
|
59 |
Ui::VisualizationWidget |
|
|
61 | Ui::VisualizationWidget* ui; | |
|
60 | 62 | |
|
61 | 63 | class VisualizationWidgetPrivate; |
|
62 | 64 | spimpl::unique_impl_ptr<VisualizationWidgetPrivate> impl; |
|
63 | 65 | }; |
|
64 | 66 | |
|
65 | 67 | #endif // VISUALIZATIONWIDGET_H |
@@ -1,106 +1,108 | |||
|
1 | 1 | #ifndef SCIQLOP_VISUALIZATIONZONEWIDGET_H |
|
2 | 2 | #define SCIQLOP_VISUALIZATIONZONEWIDGET_H |
|
3 | 3 | |
|
4 | 4 | #include "Data/DateTimeRange.h" |
|
5 | 5 | #include "Visualization/IVisualizationWidget.h" |
|
6 | 6 | #include "Visualization/VisualizationDragWidget.h" |
|
7 | 7 | |
|
8 | 8 | #include <QLoggingCategory> |
|
9 | 9 | #include <QWidget> |
|
10 | 10 | |
|
11 | 11 | #include <memory> |
|
12 | 12 | |
|
13 | 13 | #include <Common/spimpl.h> |
|
14 | 14 | |
|
15 | 15 | Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationZoneWidget) |
|
16 | 16 | |
|
17 |
namespace Ui |
|
|
17 | namespace Ui | |
|
18 | { | |
|
18 | 19 | class VisualizationZoneWidget; |
|
19 | 20 | } // namespace Ui |
|
20 | 21 | |
|
21 | class Variable; | |
|
22 | class Variable2; | |
|
22 | 23 | class VisualizationGraphWidget; |
|
23 | 24 | |
|
24 |
class VisualizationZoneWidget : public VisualizationDragWidget, public IVisualizationWidget |
|
|
25 | class VisualizationZoneWidget : public VisualizationDragWidget, public IVisualizationWidget | |
|
26 | { | |
|
25 | 27 | Q_OBJECT |
|
26 | 28 | |
|
27 | 29 | public: |
|
28 |
explicit VisualizationZoneWidget(const QString |
|
|
30 | explicit VisualizationZoneWidget(const QString& name = {}, QWidget* parent = 0); | |
|
29 | 31 | virtual ~VisualizationZoneWidget(); |
|
30 | 32 | |
|
31 | 33 | /// Sets the range of the zone, only works if there is at least one graph in the zone |
|
32 | 34 | /// Note: calibrations between graphs are lost. |
|
33 |
void setZoneRange(const DateTimeRange |
|
|
35 | void setZoneRange(const DateTimeRange& range); | |
|
34 | 36 | |
|
35 | 37 | /// Adds a graph widget |
|
36 |
void addGraph(VisualizationGraphWidget |
|
|
38 | void addGraph(VisualizationGraphWidget* graphWidget); | |
|
37 | 39 | |
|
38 | 40 | /// Inserts a graph widget |
|
39 |
void insertGraph(int index, VisualizationGraphWidget |
|
|
41 | void insertGraph(int index, VisualizationGraphWidget* graphWidget); | |
|
40 | 42 | |
|
41 | 43 | /** |
|
42 | 44 | * Creates a graph using a variable. The variable will be displayed in the new graph. |
|
43 | 45 | * The graph is added at the end. |
|
44 | 46 | * @param variable the variable for which to create the graph |
|
45 | 47 | * @return the pointer to the created graph |
|
46 | 48 | */ |
|
47 |
VisualizationGraphWidget |
|
|
49 | VisualizationGraphWidget* createGraph(std::shared_ptr<Variable2> variable); | |
|
48 | 50 | |
|
49 | 51 | /** |
|
50 | 52 | * Creates a graph using a variable. The variable will be displayed in the new graph. |
|
51 | 53 | * The graph is inserted at the specified index. |
|
52 | 54 | * @param variable the variable for which to create the graph |
|
53 | 55 | * @param index The index where the graph should be inserted in the layout |
|
54 | 56 | * @return the pointer to the created graph |
|
55 | 57 | */ |
|
56 |
VisualizationGraphWidget |
|
|
58 | VisualizationGraphWidget* createGraph(std::shared_ptr<Variable2> variable, int index); | |
|
57 | 59 | |
|
58 | 60 | /** |
|
59 | 61 | * Creates a graph using a list of variables. The variables will be displayed in the new graph. |
|
60 | 62 | * The graph is inserted at the specified index. |
|
61 | 63 | * @param variables List of variables to be added to the graph |
|
62 | 64 | * @param index The index where the graph should be inserted in the layout |
|
63 | 65 | * @return the pointer to the created graph |
|
64 | 66 | */ |
|
65 |
VisualizationGraphWidget |
|
|
66 | int index); | |
|
67 | VisualizationGraphWidget* createGraph( | |
|
68 | std::vector<std::shared_ptr<Variable2>> variables, int index); | |
|
67 | 69 | |
|
68 | 70 | /// Returns the first graph in the zone or nullptr if there is no graph inside |
|
69 |
VisualizationGraphWidget |
|
|
71 | VisualizationGraphWidget* firstGraph() const; | |
|
70 | 72 | |
|
71 | 73 | /// Closes all graphes inside the zone |
|
72 | 74 | void closeAllGraphs(); |
|
73 | 75 | |
|
74 | 76 | // IVisualizationWidget interface |
|
75 |
void accept(IVisualizationWidgetVisitor |
|
|
76 |
bool canDrop( |
|
|
77 |
bool contains( |
|
|
77 | void accept(IVisualizationWidgetVisitor* visitor) override; | |
|
78 | bool canDrop(Variable2& variable) const override; | |
|
79 | bool contains(Variable2& variable) const override; | |
|
78 | 80 | QString name() const override; |
|
79 | 81 | |
|
80 | 82 | // VisualisationDragWidget |
|
81 |
QMimeData |
|
|
83 | QMimeData* mimeData(const QPoint& position) const override; | |
|
82 | 84 | bool isDragAllowed() const override; |
|
83 | 85 | |
|
84 |
void notifyMouseMoveInGraph(const QPointF |
|
|
85 |
|
|
|
86 |
void notifyMouseLeaveGraph(VisualizationGraphWidget |
|
|
86 | void notifyMouseMoveInGraph(const QPointF& graphPosition, const QPointF& plotPosition, | |
|
87 | VisualizationGraphWidget* graphWidget); | |
|
88 | void notifyMouseLeaveGraph(VisualizationGraphWidget* graphWidget); | |
|
87 | 89 | |
|
88 | 90 | protected: |
|
89 |
void closeEvent(QCloseEvent |
|
|
91 | void closeEvent(QCloseEvent* event) override; | |
|
90 | 92 | |
|
91 | 93 | private: |
|
92 |
Ui::VisualizationZoneWidget |
|
|
94 | Ui::VisualizationZoneWidget* ui; | |
|
93 | 95 | |
|
94 | 96 | class VisualizationZoneWidgetPrivate; |
|
95 | 97 | spimpl::unique_impl_ptr<VisualizationZoneWidgetPrivate> impl; |
|
96 | 98 | |
|
97 | 99 | private slots: |
|
98 | void onVariableAdded(std::shared_ptr<Variable> variable); | |
|
100 | void onVariableAdded(std::shared_ptr<Variable2> variable); | |
|
99 | 101 | /// Slot called when a variable is about to be removed from a graph contained in the zone |
|
100 | void onVariableAboutToBeRemoved(std::shared_ptr<Variable> variable); | |
|
102 | void onVariableAboutToBeRemoved(std::shared_ptr<Variable2> variable); | |
|
101 | 103 | |
|
102 |
void dropMimeData(int index, const QMimeData |
|
|
103 |
void dropMimeDataOnGraph(VisualizationDragWidget |
|
|
104 | void dropMimeData(int index, const QMimeData* mimeData); | |
|
105 | void dropMimeDataOnGraph(VisualizationDragWidget* dragWidget, const QMimeData* mimeData); | |
|
104 | 106 | }; |
|
105 | 107 | |
|
106 | 108 | #endif // SCIQLOP_VISUALIZATIONZONEWIDGET_H |
@@ -1,42 +1,43 | |||
|
1 | 1 | #ifndef SCIQLOP_FINDVARIABLEOPERATION_H |
|
2 | 2 | #define SCIQLOP_FINDVARIABLEOPERATION_H |
|
3 | 3 | |
|
4 | 4 | #include "Visualization/IVisualizationWidgetVisitor.h" |
|
5 | 5 | |
|
6 | 6 | #include <Common/spimpl.h> |
|
7 | 7 | |
|
8 | 8 | #include <set> |
|
9 | 9 | |
|
10 | 10 | class IVisualizationWidget; |
|
11 | class Variable; | |
|
11 | class Variable2; | |
|
12 | 12 | |
|
13 | 13 | /** |
|
14 | 14 | * @brief The FindVariableOperation class defines an operation that traverses all of visualization |
|
15 | 15 | * widgets to determine which ones contain the variable passed as parameter. The result of the |
|
16 | 16 | * operation is the list of widgets that contain the variable. |
|
17 | 17 | */ |
|
18 |
class FindVariableOperation : public IVisualizationWidgetVisitor |
|
|
18 | class FindVariableOperation : public IVisualizationWidgetVisitor | |
|
19 | { | |
|
19 | 20 | public: |
|
20 | 21 | /** |
|
21 | 22 | * Ctor |
|
22 | 23 | * @param variable the variable to find |
|
23 | 24 | */ |
|
24 | explicit FindVariableOperation(std::shared_ptr<Variable> variable); | |
|
25 | explicit FindVariableOperation(std::shared_ptr<Variable2> variable); | |
|
25 | 26 | |
|
26 |
void visitEnter(VisualizationWidget |
|
|
27 |
void visitLeave(VisualizationWidget |
|
|
28 |
void visitEnter(VisualizationTabWidget |
|
|
29 |
void visitLeave(VisualizationTabWidget |
|
|
30 |
void visitEnter(VisualizationZoneWidget |
|
|
31 |
void visitLeave(VisualizationZoneWidget |
|
|
32 |
void visit(VisualizationGraphWidget |
|
|
27 | void visitEnter(VisualizationWidget* widget) override final; | |
|
28 | void visitLeave(VisualizationWidget* widget) override final; | |
|
29 | void visitEnter(VisualizationTabWidget* tabWidget) override final; | |
|
30 | void visitLeave(VisualizationTabWidget* tabWidget) override final; | |
|
31 | void visitEnter(VisualizationZoneWidget* zoneWidget) override final; | |
|
32 | void visitLeave(VisualizationZoneWidget* zoneWidget) override final; | |
|
33 | void visit(VisualizationGraphWidget* graphWidget) override final; | |
|
33 | 34 | |
|
34 | 35 | /// @return the widgets that contain the variable |
|
35 |
std::set<IVisualizationWidget |
|
|
36 | std::set<IVisualizationWidget*> result() const noexcept; | |
|
36 | 37 | |
|
37 | 38 | private: |
|
38 | 39 | class FindVariableOperationPrivate; |
|
39 | 40 | spimpl::unique_impl_ptr<FindVariableOperationPrivate> impl; |
|
40 | 41 | }; |
|
41 | 42 | |
|
42 | 43 | #endif // SCIQLOP_FINDVARIABLEOPERATION_H |
@@ -1,48 +1,49 | |||
|
1 | 1 | #ifndef SCIQLOP_GENERATEVARIABLEMENUOPERATION_H |
|
2 | 2 | #define SCIQLOP_GENERATEVARIABLEMENUOPERATION_H |
|
3 | 3 | |
|
4 | 4 | #include "Visualization/IVisualizationWidgetVisitor.h" |
|
5 | 5 | |
|
6 | 6 | #include <Common/spimpl.h> |
|
7 | 7 | |
|
8 | 8 | #include <QLoggingCategory> |
|
9 | 9 | |
|
10 | 10 | #include <set> |
|
11 | 11 | |
|
12 | 12 | class QMenu; |
|
13 | 13 | class IVisualizationWidget; |
|
14 | class Variable; | |
|
14 | class Variable2; | |
|
15 | 15 | |
|
16 | 16 | Q_DECLARE_LOGGING_CATEGORY(LOG_GenerateVariableMenuOperation) |
|
17 | 17 | |
|
18 | 18 | /** |
|
19 | 19 | * @brief The GenerateVariableMenuOperation class defines an operation that traverses all of |
|
20 | 20 | * visualization widgets to determine which can accommodate a variable. The result of the operation |
|
21 | 21 | * is a menu that contains actions to add the variable into the containers. |
|
22 | 22 | */ |
|
23 |
class GenerateVariableMenuOperation : public IVisualizationWidgetVisitor |
|
|
23 | class GenerateVariableMenuOperation : public IVisualizationWidgetVisitor | |
|
24 | { | |
|
24 | 25 | public: |
|
25 | 26 | /** |
|
26 | 27 | * Ctor |
|
27 | 28 | * @param menu the menu to which to attach the generated menu |
|
28 | 29 | * @param variable the variable for which to generate the menu |
|
29 | 30 | * @param variableContainers the containers that already contain the variable for which to |
|
30 | 31 | * generate the menu |
|
31 | 32 | */ |
|
32 |
explicit GenerateVariableMenuOperation(QMenu |
|
|
33 |
|
|
|
34 | ||
|
35 |
void visitEnter(VisualizationWidget |
|
|
36 |
void visitLeave(VisualizationWidget |
|
|
37 |
void visitEnter(VisualizationTabWidget |
|
|
38 |
void visitLeave(VisualizationTabWidget |
|
|
39 |
void visitEnter(VisualizationZoneWidget |
|
|
40 |
void visitLeave(VisualizationZoneWidget |
|
|
41 |
void visit(VisualizationGraphWidget |
|
|
33 | explicit GenerateVariableMenuOperation(QMenu* menu, std::shared_ptr<Variable2> variable, | |
|
34 | std::set<IVisualizationWidget*> variableContainers); | |
|
35 | ||
|
36 | void visitEnter(VisualizationWidget* widget) override final; | |
|
37 | void visitLeave(VisualizationWidget* widget) override final; | |
|
38 | void visitEnter(VisualizationTabWidget* tabWidget) override final; | |
|
39 | void visitLeave(VisualizationTabWidget* tabWidget) override final; | |
|
40 | void visitEnter(VisualizationZoneWidget* zoneWidget) override final; | |
|
41 | void visitLeave(VisualizationZoneWidget* zoneWidget) override final; | |
|
42 | void visit(VisualizationGraphWidget* graphWidget) override final; | |
|
42 | 43 | |
|
43 | 44 | private: |
|
44 | 45 | class GenerateVariableMenuOperationPrivate; |
|
45 | 46 | spimpl::unique_impl_ptr<GenerateVariableMenuOperationPrivate> impl; |
|
46 | 47 | }; |
|
47 | 48 | |
|
48 | 49 | #endif // SCIQLOP_GENERATEVARIABLEMENUOPERATION_H |
@@ -1,41 +1,42 | |||
|
1 | 1 | #ifndef SCIQLOP_REMOVEVARIABLEOPERATION_H |
|
2 | 2 | #define SCIQLOP_REMOVEVARIABLEOPERATION_H |
|
3 | 3 | |
|
4 | 4 | #include "Visualization/IVisualizationWidgetVisitor.h" |
|
5 | 5 | |
|
6 | 6 | #include <Common/spimpl.h> |
|
7 | 7 | |
|
8 | 8 | #include <QLoggingCategory> |
|
9 | 9 | |
|
10 | 10 | #include <memory> |
|
11 | 11 | |
|
12 | class Variable; | |
|
12 | class Variable2; | |
|
13 | 13 | |
|
14 | 14 | Q_DECLARE_LOGGING_CATEGORY(LOG_RemoveVariableOperation) |
|
15 | 15 | |
|
16 | 16 | /** |
|
17 | 17 | * @brief The RemoveVariableOperation class defines an operation that traverses all of visualization |
|
18 | 18 | * widgets to remove a variable if they contain it |
|
19 | 19 | */ |
|
20 |
class RemoveVariableOperation : public IVisualizationWidgetVisitor |
|
|
20 | class RemoveVariableOperation : public IVisualizationWidgetVisitor | |
|
21 | { | |
|
21 | 22 | public: |
|
22 | 23 | /** |
|
23 | 24 | * Ctor |
|
24 | 25 | * @param variable the variable to remove from widgets |
|
25 | 26 | */ |
|
26 | explicit RemoveVariableOperation(std::shared_ptr<Variable> variable); | |
|
27 | ||
|
28 |
void visitEnter(VisualizationWidget |
|
|
29 |
void visitLeave(VisualizationWidget |
|
|
30 |
void visitEnter(VisualizationTabWidget |
|
|
31 |
void visitLeave(VisualizationTabWidget |
|
|
32 |
void visitEnter(VisualizationZoneWidget |
|
|
33 |
void visitLeave(VisualizationZoneWidget |
|
|
34 |
void visit(VisualizationGraphWidget |
|
|
27 | explicit RemoveVariableOperation(std::shared_ptr<Variable2> variable); | |
|
28 | ||
|
29 | void visitEnter(VisualizationWidget* widget) override final; | |
|
30 | void visitLeave(VisualizationWidget* widget) override final; | |
|
31 | void visitEnter(VisualizationTabWidget* tabWidget) override final; | |
|
32 | void visitLeave(VisualizationTabWidget* tabWidget) override final; | |
|
33 | void visitEnter(VisualizationZoneWidget* zoneWidget) override final; | |
|
34 | void visitLeave(VisualizationZoneWidget* zoneWidget) override final; | |
|
35 | void visit(VisualizationGraphWidget* graphWidget) override final; | |
|
35 | 36 | |
|
36 | 37 | private: |
|
37 | 38 | class RemoveVariableOperationPrivate; |
|
38 | 39 | spimpl::unique_impl_ptr<RemoveVariableOperationPrivate> impl; |
|
39 | 40 | }; |
|
40 | 41 | |
|
41 | 42 | #endif // SCIQLOP_REMOVEVARIABLEOPERATION_H |
@@ -1,42 +1,43 | |||
|
1 | 1 | #ifndef SCIQLOP_RESCALEAXEOPERATION_H |
|
2 | 2 | #define SCIQLOP_RESCALEAXEOPERATION_H |
|
3 | 3 | |
|
4 | 4 | #include "Visualization/IVisualizationWidgetVisitor.h" |
|
5 | 5 | #include <Data/DateTimeRange.h> |
|
6 | 6 | |
|
7 | 7 | #include <Common/spimpl.h> |
|
8 | 8 | |
|
9 | 9 | #include <QLoggingCategory> |
|
10 | 10 | |
|
11 | 11 | #include <memory> |
|
12 | 12 | |
|
13 | class Variable; | |
|
13 | class Variable2; | |
|
14 | 14 | |
|
15 | 15 | Q_DECLARE_LOGGING_CATEGORY(LOG_RescaleAxeOperation) |
|
16 | 16 | |
|
17 | 17 | /** |
|
18 | 18 | * @brief The RescaleAxeOperation class defines an operation that traverses all of visualization |
|
19 | 19 | * widgets to remove a variable if they contain it |
|
20 | 20 | */ |
|
21 |
class RescaleAxeOperation : public IVisualizationWidgetVisitor |
|
|
21 | class RescaleAxeOperation : public IVisualizationWidgetVisitor | |
|
22 | { | |
|
22 | 23 | public: |
|
23 | 24 | /** |
|
24 | 25 | * Ctor |
|
25 | 26 | * @param variable the variable to remove from widgets |
|
26 | 27 | */ |
|
27 |
explicit RescaleAxeOperation(std::shared_ptr<Variable> variable, const DateTimeRange |
|
|
28 | ||
|
29 |
void visitEnter(VisualizationWidget |
|
|
30 |
void visitLeave(VisualizationWidget |
|
|
31 |
void visitEnter(VisualizationTabWidget |
|
|
32 |
void visitLeave(VisualizationTabWidget |
|
|
33 |
void visitEnter(VisualizationZoneWidget |
|
|
34 |
void visitLeave(VisualizationZoneWidget |
|
|
35 |
void visit(VisualizationGraphWidget |
|
|
28 | explicit RescaleAxeOperation(std::shared_ptr<Variable2> variable, const DateTimeRange& range); | |
|
29 | ||
|
30 | void visitEnter(VisualizationWidget* widget) override final; | |
|
31 | void visitLeave(VisualizationWidget* widget) override final; | |
|
32 | void visitEnter(VisualizationTabWidget* tabWidget) override final; | |
|
33 | void visitLeave(VisualizationTabWidget* tabWidget) override final; | |
|
34 | void visitEnter(VisualizationZoneWidget* zoneWidget) override final; | |
|
35 | void visitLeave(VisualizationZoneWidget* zoneWidget) override final; | |
|
36 | void visit(VisualizationGraphWidget* graphWidget) override final; | |
|
36 | 37 | |
|
37 | 38 | private: |
|
38 | 39 | class RescaleAxeOperationPrivate; |
|
39 | 40 | spimpl::unique_impl_ptr<RescaleAxeOperationPrivate> impl; |
|
40 | 41 | }; |
|
41 | 42 | |
|
42 | 43 | #endif // SCIQLOP_RESCALEAXEOPERATION_H |
@@ -1,308 +1,332 | |||
|
1 | 1 | #include "DragAndDrop/DragDropGuiController.h" |
|
2 | 2 | #include "DragAndDrop/DragDropScroller.h" |
|
3 | 3 | #include "DragAndDrop/DragDropTabSwitcher.h" |
|
4 | 4 | #include "SqpApplication.h" |
|
5 | 5 | #include "Visualization/VisualizationDragDropContainer.h" |
|
6 | 6 | #include "Visualization/VisualizationDragWidget.h" |
|
7 | 7 | #include "Visualization/VisualizationWidget.h" |
|
8 | 8 | #include "Visualization/operations/FindVariableOperation.h" |
|
9 | 9 | |
|
10 | 10 | #include "DataSource/DataSourceController.h" |
|
11 | 11 | #include "Variable/Variable.h" |
|
12 | 12 | #include "Variable/VariableController2.h" |
|
13 | 13 | |
|
14 | 14 | #include "Common/MimeTypesDef.h" |
|
15 | 15 | #include "Common/VisualizationDef.h" |
|
16 | 16 | |
|
17 | 17 | #include <QDir> |
|
18 | 18 | #include <QLabel> |
|
19 | 19 | #include <QUrl> |
|
20 | 20 | #include <QVBoxLayout> |
|
21 | 21 | |
|
22 | 22 | |
|
23 | 23 | Q_LOGGING_CATEGORY(LOG_DragDropGuiController, "DragDropGuiController") |
|
24 | 24 | |
|
25 | 25 | |
|
26 |
struct DragDropGuiController::DragDropGuiControllerPrivate |
|
|
26 | struct DragDropGuiController::DragDropGuiControllerPrivate | |
|
27 | { | |
|
27 | 28 | |
|
28 |
VisualizationDragWidget |
|
|
29 | VisualizationDragWidget* m_CurrentDragWidget = nullptr; | |
|
29 | 30 | std::unique_ptr<QWidget> m_PlaceHolder = nullptr; |
|
30 |
QLabel |
|
|
31 |
QWidget |
|
|
31 | QLabel* m_PlaceHolderLabel; | |
|
32 | QWidget* m_PlaceBackground; | |
|
32 | 33 | std::unique_ptr<DragDropScroller> m_DragDropScroller = nullptr; |
|
33 | 34 | std::unique_ptr<DragDropTabSwitcher> m_DragDropTabSwitcher = nullptr; |
|
34 | 35 | QString m_ImageTempUrl; // Temporary file for image url generated by the drag & drop. Not using |
|
35 | 36 | // QTemporaryFile to have a name which is not generated. |
|
36 | 37 | |
|
37 |
VisualizationDragWidget |
|
|
38 | VisualizationDragWidget* m_HighlightedDragWidget = nullptr; | |
|
38 | 39 | |
|
39 | 40 | QMetaObject::Connection m_DragWidgetDestroyedConnection; |
|
40 | 41 | QMetaObject::Connection m_HighlightedWidgetDestroyedConnection; |
|
41 | 42 | |
|
42 |
QList<QWidget |
|
|
43 | QList<QWidget*> m_WidgetToClose; | |
|
43 | 44 | |
|
44 | 45 | explicit DragDropGuiControllerPrivate() |
|
45 |
: m_PlaceHolder{std::make_unique<QWidget>()} |
|
|
46 |
|
|
|
47 |
|
|
|
46 | : m_PlaceHolder { std::make_unique<QWidget>() } | |
|
47 | , m_DragDropScroller { std::make_unique<DragDropScroller>() } | |
|
48 | , m_DragDropTabSwitcher { std::make_unique<DragDropTabSwitcher>() } | |
|
48 | 49 | { |
|
49 | 50 | |
|
50 | auto layout = new QVBoxLayout{m_PlaceHolder.get()}; | |
|
51 | auto layout = new QVBoxLayout { m_PlaceHolder.get() }; | |
|
51 | 52 | layout->setSpacing(0); |
|
52 | 53 | layout->setContentsMargins(0, 0, 0, 0); |
|
53 | 54 | |
|
54 | m_PlaceHolderLabel = new QLabel{"", m_PlaceHolder.get()}; | |
|
55 | m_PlaceHolderLabel = new QLabel { "", m_PlaceHolder.get() }; | |
|
55 | 56 | m_PlaceHolderLabel->setMinimumHeight(25); |
|
56 | 57 | layout->addWidget(m_PlaceHolderLabel); |
|
57 | 58 | |
|
58 | m_PlaceBackground = new QWidget{m_PlaceHolder.get()}; | |
|
59 | m_PlaceBackground = new QWidget { m_PlaceHolder.get() }; | |
|
59 | 60 | m_PlaceBackground->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); |
|
60 | 61 | layout->addWidget(m_PlaceBackground); |
|
61 | 62 | |
|
62 | 63 | sqpApp->installEventFilter(m_DragDropScroller.get()); |
|
63 | 64 | sqpApp->installEventFilter(m_DragDropTabSwitcher.get()); |
|
64 | 65 | |
|
65 | 66 | m_ImageTempUrl = QDir::temp().absoluteFilePath("Sciqlop_graph.png"); |
|
66 | 67 | } |
|
67 | 68 | |
|
68 | void preparePlaceHolder(DragDropGuiController::PlaceHolderType type, | |
|
69 | const QString &topLabelText) const | |
|
69 | void preparePlaceHolder( | |
|
70 | DragDropGuiController::PlaceHolderType type, const QString& topLabelText) const | |
|
70 | 71 | { |
|
71 |
if (m_CurrentDragWidget) |
|
|
72 | if (m_CurrentDragWidget) | |
|
73 | { | |
|
72 | 74 | m_PlaceHolder->setMinimumSize(m_CurrentDragWidget->size()); |
|
73 | 75 | m_PlaceHolder->setSizePolicy(m_CurrentDragWidget->sizePolicy()); |
|
74 | 76 | } |
|
75 |
else |
|
|
77 | else | |
|
78 | { | |
|
76 | 79 | // Configuration of the placeHolder when there is no dragWidget |
|
77 | 80 | // (for instance with a drag from a variable) |
|
78 | 81 | |
|
79 | 82 | m_PlaceHolder->setMinimumSize(0, GRAPH_MINIMUM_HEIGHT); |
|
80 | 83 | m_PlaceHolder->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); |
|
81 | 84 | } |
|
82 | 85 | |
|
83 |
switch (type) |
|
|
86 | switch (type) | |
|
87 | { | |
|
84 | 88 | case DragDropGuiController::PlaceHolderType::Graph: |
|
85 | 89 | m_PlaceBackground->setStyleSheet( |
|
86 | 90 | "background-color: #BBD5EE; border: 1px solid #2A7FD4"); |
|
87 | 91 | break; |
|
88 | 92 | case DragDropGuiController::PlaceHolderType::Zone: |
|
89 | 93 | case DragDropGuiController::PlaceHolderType::Default: |
|
90 | 94 | m_PlaceBackground->setStyleSheet( |
|
91 | 95 | "background-color: #BBD5EE; border: 2px solid #2A7FD4"); |
|
92 | 96 | m_PlaceHolderLabel->setStyleSheet("color: #2A7FD4"); |
|
93 | 97 | break; |
|
94 | 98 | } |
|
95 | 99 | |
|
96 | 100 | m_PlaceHolderLabel->setText(topLabelText); |
|
97 | 101 | m_PlaceHolderLabel->setVisible(!topLabelText.isEmpty()); |
|
98 | 102 | } |
|
99 | 103 | }; |
|
100 | 104 | |
|
101 | 105 | |
|
102 | 106 | DragDropGuiController::DragDropGuiController() |
|
103 | : impl{spimpl::make_unique_impl<DragDropGuiControllerPrivate>()} | |
|
107 | : impl { spimpl::make_unique_impl<DragDropGuiControllerPrivate>() } | |
|
104 | 108 | { |
|
105 | 109 | } |
|
106 | 110 | |
|
107 | 111 | DragDropGuiController::~DragDropGuiController() |
|
108 | 112 | { |
|
109 | 113 | QFile::remove(impl->m_ImageTempUrl); |
|
110 | 114 | } |
|
111 | 115 | |
|
112 | 116 | void DragDropGuiController::resetDragAndDrop() |
|
113 | 117 | { |
|
114 | 118 | setCurrentDragWidget(nullptr); |
|
115 | 119 | impl->m_HighlightedDragWidget = nullptr; |
|
116 | 120 | } |
|
117 | 121 | |
|
118 |
void DragDropGuiController::setCurrentDragWidget(VisualizationDragWidget |
|
|
122 | void DragDropGuiController::setCurrentDragWidget(VisualizationDragWidget* dragWidget) | |
|
119 | 123 | { |
|
120 |
if (impl->m_CurrentDragWidget) |
|
|
124 | if (impl->m_CurrentDragWidget) | |
|
125 | { | |
|
121 | 126 | |
|
122 | 127 | QObject::disconnect(impl->m_DragWidgetDestroyedConnection); |
|
123 | 128 | } |
|
124 | 129 | |
|
125 |
if (dragWidget) |
|
|
130 | if (dragWidget) | |
|
131 | { | |
|
126 | 132 | // ensures the impl->m_CurrentDragWidget is reset when the widget is destroyed |
|
127 | impl->m_DragWidgetDestroyedConnection | |
|
128 | = QObject::connect(dragWidget, &VisualizationDragWidget::destroyed, | |
|
129 | [this]() { impl->m_CurrentDragWidget = nullptr; }); | |
|
133 | impl->m_DragWidgetDestroyedConnection = QObject::connect(dragWidget, | |
|
134 | &VisualizationDragWidget::destroyed, [this]() { impl->m_CurrentDragWidget = nullptr; }); | |
|
130 | 135 | } |
|
131 | 136 | |
|
132 | 137 | impl->m_CurrentDragWidget = dragWidget; |
|
133 | 138 | } |
|
134 | 139 | |
|
135 |
VisualizationDragWidget |
|
|
140 | VisualizationDragWidget* DragDropGuiController::getCurrentDragWidget() const | |
|
136 | 141 | { |
|
137 | 142 | return impl->m_CurrentDragWidget; |
|
138 | 143 | } |
|
139 | 144 | |
|
140 |
QWidget |
|
|
145 | QWidget& DragDropGuiController::placeHolder() const | |
|
141 | 146 | { |
|
142 | 147 | return *impl->m_PlaceHolder; |
|
143 | 148 | } |
|
144 | 149 | |
|
145 |
void DragDropGuiController::insertPlaceHolder( |
|
|
146 | const QString &topLabelText) | |
|
150 | void DragDropGuiController::insertPlaceHolder( | |
|
151 | QVBoxLayout* layout, int index, PlaceHolderType type, const QString& topLabelText) | |
|
147 | 152 | { |
|
148 | 153 | removePlaceHolder(); |
|
149 | 154 | impl->preparePlaceHolder(type, topLabelText); |
|
150 | 155 | layout->insertWidget(index, impl->m_PlaceHolder.get()); |
|
151 | 156 | impl->m_PlaceHolder->show(); |
|
152 | 157 | } |
|
153 | 158 | |
|
154 | 159 | void DragDropGuiController::removePlaceHolder() |
|
155 | 160 | { |
|
156 | 161 | auto parentWidget = impl->m_PlaceHolder->parentWidget(); |
|
157 |
if (parentWidget) |
|
|
162 | if (parentWidget) | |
|
163 | { | |
|
158 | 164 | parentWidget->layout()->removeWidget(impl->m_PlaceHolder.get()); |
|
159 | 165 | impl->m_PlaceHolder->setParent(nullptr); |
|
160 | 166 | impl->m_PlaceHolder->hide(); |
|
161 | 167 | } |
|
162 | 168 | } |
|
163 | 169 | |
|
164 | 170 | bool DragDropGuiController::isPlaceHolderSet() const |
|
165 | 171 | { |
|
166 | 172 | return impl->m_PlaceHolder->parentWidget(); |
|
167 | 173 | } |
|
168 | 174 | |
|
169 |
void DragDropGuiController::addDragDropScrollArea(QScrollArea |
|
|
175 | void DragDropGuiController::addDragDropScrollArea(QScrollArea* scrollArea) | |
|
170 | 176 | { |
|
171 | 177 | impl->m_DragDropScroller->addScrollArea(scrollArea); |
|
172 | 178 | } |
|
173 | 179 | |
|
174 |
void DragDropGuiController::removeDragDropScrollArea(QScrollArea |
|
|
180 | void DragDropGuiController::removeDragDropScrollArea(QScrollArea* scrollArea) | |
|
175 | 181 | { |
|
176 | 182 | impl->m_DragDropScroller->removeScrollArea(scrollArea); |
|
177 | 183 | } |
|
178 | 184 | |
|
179 |
void DragDropGuiController::addDragDropTabBar(QTabBar |
|
|
185 | void DragDropGuiController::addDragDropTabBar(QTabBar* tabBar) | |
|
180 | 186 | { |
|
181 | 187 | impl->m_DragDropTabSwitcher->addTabBar(tabBar); |
|
182 | 188 | } |
|
183 | 189 | |
|
184 |
void DragDropGuiController::removeDragDropTabBar(QTabBar |
|
|
190 | void DragDropGuiController::removeDragDropTabBar(QTabBar* tabBar) | |
|
185 | 191 | { |
|
186 | 192 | impl->m_DragDropTabSwitcher->removeTabBar(tabBar); |
|
187 | 193 | } |
|
188 | 194 | |
|
189 |
QUrl DragDropGuiController::imageTemporaryUrl(const QImage |
|
|
195 | QUrl DragDropGuiController::imageTemporaryUrl(const QImage& image) const | |
|
190 | 196 | { |
|
191 | 197 | image.save(impl->m_ImageTempUrl); |
|
192 | 198 | return QUrl::fromLocalFile(impl->m_ImageTempUrl); |
|
193 | 199 | } |
|
194 | 200 | |
|
195 |
void DragDropGuiController::setHightlightedDragWidget(VisualizationDragWidget |
|
|
201 | void DragDropGuiController::setHightlightedDragWidget(VisualizationDragWidget* dragWidget) | |
|
196 | 202 | { |
|
197 |
if (impl->m_HighlightedDragWidget) |
|
|
203 | if (impl->m_HighlightedDragWidget) | |
|
204 | { | |
|
198 | 205 | impl->m_HighlightedDragWidget->highlightForMerge(false); |
|
199 | 206 | QObject::disconnect(impl->m_HighlightedWidgetDestroyedConnection); |
|
200 | 207 | } |
|
201 | 208 | |
|
202 |
if (dragWidget) |
|
|
209 | if (dragWidget) | |
|
210 | { | |
|
203 | 211 | dragWidget->highlightForMerge(true); |
|
204 | 212 | |
|
205 | 213 | // ensures the impl->m_HighlightedDragWidget is reset when the widget is destroyed |
|
206 | 214 | impl->m_DragWidgetDestroyedConnection |
|
207 | 215 | = QObject::connect(dragWidget, &VisualizationDragWidget::destroyed, |
|
208 |
|
|
|
216 | [this]() { impl->m_HighlightedDragWidget = nullptr; }); | |
|
209 | 217 | } |
|
210 | 218 | |
|
211 | 219 | impl->m_HighlightedDragWidget = dragWidget; |
|
212 | 220 | } |
|
213 | 221 | |
|
214 |
VisualizationDragWidget |
|
|
222 | VisualizationDragWidget* DragDropGuiController::getHightlightedDragWidget() const | |
|
215 | 223 | { |
|
216 | 224 | return impl->m_HighlightedDragWidget; |
|
217 | 225 | } |
|
218 | 226 | |
|
219 |
void DragDropGuiController::delayedCloseWidget(QWidget |
|
|
227 | void DragDropGuiController::delayedCloseWidget(QWidget* widget) | |
|
220 | 228 | { |
|
221 | 229 | widget->hide(); |
|
222 | 230 | impl->m_WidgetToClose << widget; |
|
223 | 231 | } |
|
224 | 232 | |
|
225 | 233 | void DragDropGuiController::doCloseWidgets() |
|
226 | 234 | { |
|
227 |
for (auto widget : impl->m_WidgetToClose) |
|
|
235 | for (auto widget : impl->m_WidgetToClose) | |
|
236 | { | |
|
228 | 237 | widget->close(); |
|
229 | 238 | } |
|
230 | 239 | |
|
231 | 240 | impl->m_WidgetToClose.clear(); |
|
232 | 241 | } |
|
233 | 242 | |
|
234 | 243 | bool DragDropGuiController::checkMimeDataForVisualization( |
|
235 |
const QMimeData |
|
|
244 | const QMimeData* mimeData, VisualizationDragDropContainer* dropContainer) | |
|
236 | 245 | { |
|
237 |
if (!mimeData || !dropContainer) |
|
|
246 | if (!mimeData || !dropContainer) | |
|
247 | { | |
|
238 | 248 | qCWarning(LOG_DragDropGuiController()) << QObject::tr( |
|
239 | 249 | "DragDropGuiController::checkMimeDataForVisualization, invalid input parameters."); |
|
240 | 250 | Q_ASSERT(false); |
|
241 | 251 | return false; |
|
242 | 252 | } |
|
243 | 253 | |
|
244 | 254 | auto result = false; |
|
245 | 255 | |
|
246 |
if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) |
|
|
256 | if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) | |
|
257 | { | |
|
247 | 258 | auto variables = sqpApp->variableController().variables( |
|
248 | 259 | Variable::variablesIDs(mimeData->data(MIME_TYPE_VARIABLE_LIST))); |
|
249 | 260 | |
|
250 |
if (variables.size() == 1) |
|
|
261 | if (variables.size() == 1) | |
|
262 | { | |
|
251 | 263 | |
|
252 | 264 | auto variable = variables[0]; |
|
253 |
if (variable->data |
|
|
265 | if (variable->data() != nullptr) | |
|
266 | { | |
|
254 | 267 | |
|
255 | 268 | // Check that the variable is not already in a graph |
|
256 | 269 | |
|
257 | 270 | auto parent = dropContainer->parentWidget(); |
|
258 |
while (parent && qobject_cast<VisualizationWidget |
|
|
271 | while (parent && qobject_cast<VisualizationWidget*>(parent) == nullptr) | |
|
272 | { | |
|
259 | 273 | parent = parent->parentWidget(); // Search for the top level VisualizationWidget |
|
260 | 274 | } |
|
261 | 275 | |
|
262 |
if (parent) |
|
|
263 | auto visualizationWidget = static_cast<VisualizationWidget *>(parent); | |
|
276 | if (parent) | |
|
277 | { | |
|
278 | auto visualizationWidget = static_cast<VisualizationWidget*>(parent); | |
|
264 | 279 | |
|
265 | FindVariableOperation findVariableOperation{variable}; | |
|
280 | FindVariableOperation findVariableOperation { variable }; | |
|
266 | 281 | visualizationWidget->accept(&findVariableOperation); |
|
267 | 282 | auto variableContainers = findVariableOperation.result(); |
|
268 |
if (variableContainers.empty()) |
|
|
283 | if (variableContainers.empty()) | |
|
284 | { | |
|
269 | 285 | result = true; |
|
270 | 286 | } |
|
271 |
else |
|
|
287 | else | |
|
288 | { | |
|
272 | 289 | // result = false: the variable already exist in the visualisation |
|
273 | 290 | } |
|
274 | 291 | } |
|
275 |
else |
|
|
292 | else | |
|
293 | { | |
|
276 | 294 | qCWarning(LOG_DragDropGuiController()) << QObject::tr( |
|
277 | 295 | "DragDropGuiController::checkMimeDataForVisualization, the parent " |
|
278 | 296 | "VisualizationWidget cannot be found. Cannot check if the variable is " |
|
279 | 297 | "already used or not."); |
|
280 | 298 | } |
|
281 | 299 | } |
|
282 |
else |
|
|
300 | else | |
|
301 | { | |
|
283 | 302 | // result = false: the variable is not fully loaded |
|
284 | 303 | } |
|
285 | 304 | } |
|
286 |
else |
|
|
305 | else | |
|
306 | { | |
|
287 | 307 | // result = false: cannot drop multiple variables in the visualisation |
|
288 | 308 | } |
|
289 | 309 | } |
|
290 |
else if (mimeData->hasFormat(MIME_TYPE_PRODUCT_LIST)) |
|
|
310 | else if (mimeData->hasFormat(MIME_TYPE_PRODUCT_LIST)) | |
|
311 | { | |
|
291 | 312 | auto productDataList = sqpApp->dataSourceController().productsDataForMimeData( |
|
292 | 313 | mimeData->data(MIME_TYPE_PRODUCT_LIST)); |
|
293 |
if (productDataList.count() == 1) |
|
|
314 | if (productDataList.count() == 1) | |
|
315 | { | |
|
294 | 316 | result = true; |
|
295 | 317 | } |
|
296 |
else |
|
|
318 | else | |
|
319 | { | |
|
297 | 320 | // result = false: cannot drop multiple products in the visualisation |
|
298 | 321 | } |
|
299 | 322 | } |
|
300 |
else |
|
|
323 | else | |
|
324 | { | |
|
301 | 325 | // Other MIME data |
|
302 | 326 | // no special rules, accepted by default |
|
303 | 327 | result = true; |
|
304 | 328 | } |
|
305 | 329 | |
|
306 | 330 | |
|
307 | 331 | return result; |
|
308 | 332 | } |
@@ -1,248 +1,266 | |||
|
1 | #include <DataSource/DataSourceController.h> | |
|
1 | 2 | #include <Variable/RenameVariableDialog.h> |
|
2 | 3 | #include <Variable/Variable.h> |
|
3 | 4 | #include <Variable/VariableController2.h> |
|
4 | 5 | #include <Variable/VariableInspectorWidget.h> |
|
5 | 6 | #include <Variable/VariableMenuHeaderWidget.h> |
|
6 | 7 | #include <Variable/VariableModel2.h> |
|
7 | #include <DataSource/DataSourceController.h> | |
|
8 | 8 | |
|
9 | 9 | #include <ui_VariableInspectorWidget.h> |
|
10 | 10 | |
|
11 | 11 | #include <QMouseEvent> |
|
12 | 12 | #include <QSortFilterProxyModel> |
|
13 | 13 | #include <QStyledItemDelegate> |
|
14 | 14 | #include <QWidgetAction> |
|
15 | 15 | |
|
16 | 16 | #include <DragAndDrop/DragDropGuiController.h> |
|
17 | 17 | #include <SqpApplication.h> |
|
18 | 18 | |
|
19 | 19 | Q_LOGGING_CATEGORY(LOG_VariableInspectorWidget, "VariableInspectorWidget") |
|
20 | 20 | |
|
21 | 21 | |
|
22 |
class QProgressBarItemDelegate : public QStyledItemDelegate |
|
|
22 | class QProgressBarItemDelegate : public QStyledItemDelegate | |
|
23 | { | |
|
23 | 24 | |
|
24 | 25 | public: |
|
25 |
QProgressBarItemDelegate(QObject |
|
|
26 | QProgressBarItemDelegate(QObject* parent) : QStyledItemDelegate { parent } {} | |
|
26 | 27 | |
|
27 | void paint(QPainter *painter, const QStyleOptionViewItem &option, | |
|
28 |
|
|
|
28 | void paint( | |
|
29 | QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const | |
|
29 | 30 | { |
|
30 | 31 | auto data = index.data(Qt::DisplayRole); |
|
31 | 32 | auto progressData = index.data(VariableRoles::ProgressRole); |
|
32 |
if (data.isValid() && progressData.isValid()) |
|
|
33 | if (data.isValid() && progressData.isValid()) | |
|
34 | { | |
|
33 | 35 | auto name = data.value<QString>(); |
|
34 | 36 | auto progress = progressData.value<double>(); |
|
35 |
if (progress > 0) |
|
|
37 | if (progress > 0) | |
|
38 | { | |
|
36 | 39 | auto cancelButtonWidth = 20; |
|
37 | auto progressBarOption = QStyleOptionProgressBar{}; | |
|
40 | auto progressBarOption = QStyleOptionProgressBar {}; | |
|
38 | 41 | auto progressRect = option.rect; |
|
39 | 42 | progressRect.setWidth(progressRect.width() - cancelButtonWidth); |
|
40 | 43 | progressBarOption.rect = progressRect; |
|
41 | 44 | progressBarOption.minimum = 0; |
|
42 | 45 | progressBarOption.maximum = 100; |
|
43 | 46 | progressBarOption.progress = progress; |
|
44 | 47 | progressBarOption.text |
|
45 | 48 | = QString("%1 %2").arg(name).arg(QString::number(progress, 'f', 2) + "%"); |
|
46 | 49 | progressBarOption.textVisible = true; |
|
47 | 50 | progressBarOption.textAlignment = Qt::AlignCenter; |
|
48 | 51 | |
|
49 | 52 | |
|
50 |
QApplication::style()->drawControl( |
|
|
51 | painter); | |
|
53 | QApplication::style()->drawControl( | |
|
54 | QStyle::CE_ProgressBar, &progressBarOption, painter); | |
|
52 | 55 | |
|
53 | 56 | // Cancel button |
|
54 | 57 | auto buttonRect = QRect(progressRect.right(), option.rect.top(), cancelButtonWidth, |
|
55 |
|
|
|
56 | auto buttonOption = QStyleOptionButton{}; | |
|
58 | option.rect.height()); | |
|
59 | auto buttonOption = QStyleOptionButton {}; | |
|
57 | 60 | buttonOption.rect = buttonRect; |
|
58 | 61 | buttonOption.text = "X"; |
|
59 | 62 | |
|
60 | 63 | QApplication::style()->drawControl(QStyle::CE_PushButton, &buttonOption, painter); |
|
61 | 64 | } |
|
62 |
else |
|
|
65 | else | |
|
66 | { | |
|
63 | 67 | QStyledItemDelegate::paint(painter, option, index); |
|
64 | 68 | } |
|
65 | 69 | } |
|
66 |
else |
|
|
70 | else | |
|
71 | { | |
|
67 | 72 | QStyledItemDelegate::paint(painter, option, index); |
|
68 | 73 | } |
|
69 | 74 | } |
|
70 | 75 | |
|
71 |
bool editorEvent(QEvent |
|
|
72 |
|
|
|
76 | bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, | |
|
77 | const QModelIndex& index) | |
|
73 | 78 | { |
|
74 |
if (event->type() == QEvent::MouseButtonRelease) |
|
|
79 | if (event->type() == QEvent::MouseButtonRelease) | |
|
80 | { | |
|
75 | 81 | auto data = index.data(Qt::DisplayRole); |
|
76 | 82 | auto progressData = index.data(VariableRoles::ProgressRole); |
|
77 |
if (data.isValid() && progressData.isValid()) |
|
|
83 | if (data.isValid() && progressData.isValid()) | |
|
84 | { | |
|
78 | 85 | auto cancelButtonWidth = 20; |
|
79 | 86 | auto progressRect = option.rect; |
|
80 | 87 | progressRect.setWidth(progressRect.width() - cancelButtonWidth); |
|
81 | 88 | // Cancel button |
|
82 | 89 | auto buttonRect = QRect(progressRect.right(), option.rect.top(), cancelButtonWidth, |
|
83 |
|
|
|
90 | option.rect.height()); | |
|
84 | 91 | |
|
85 |
auto e = (QMouseEvent |
|
|
92 | auto e = (QMouseEvent*)event; | |
|
86 | 93 | auto clickX = e->x(); |
|
87 | 94 | auto clickY = e->y(); |
|
88 | 95 | |
|
89 |
auto x = buttonRect.left(); |
|
|
90 |
auto y = buttonRect.top(); |
|
|
91 |
auto w = buttonRect.width(); |
|
|
96 | auto x = buttonRect.left(); // the X coordinate | |
|
97 | auto y = buttonRect.top(); // the Y coordinate | |
|
98 | auto w = buttonRect.width(); // button width | |
|
92 | 99 | auto h = buttonRect.height(); // button height |
|
93 | 100 | |
|
94 |
if (clickX > x && clickX < x + w) |
|
|
95 | if (clickY > y && clickY < y + h) { | |
|
96 | //auto& variableModel = sqpApp->variableModel(); | |
|
97 | //variableModel->abortProgress(index); | |
|
101 | if (clickX > x && clickX < x + w) | |
|
102 | { | |
|
103 | if (clickY > y && clickY < y + h) | |
|
104 | { | |
|
105 | // auto& variableModel = sqpApp->variableModel(); | |
|
106 | // variableModel->abortProgress(index); | |
|
98 | 107 | } |
|
99 | 108 | return true; |
|
100 | 109 | } |
|
101 |
else |
|
|
110 | else | |
|
111 | { | |
|
102 | 112 | return QStyledItemDelegate::editorEvent(event, model, option, index); |
|
103 | 113 | } |
|
104 | 114 | } |
|
105 |
else |
|
|
115 | else | |
|
116 | { | |
|
106 | 117 | return QStyledItemDelegate::editorEvent(event, model, option, index); |
|
107 | 118 | } |
|
108 | 119 | } |
|
109 |
else |
|
|
120 | else | |
|
121 | { | |
|
110 | 122 | return QStyledItemDelegate::editorEvent(event, model, option, index); |
|
111 | 123 | } |
|
112 | 124 | |
|
113 | 125 | |
|
114 | 126 | return QStyledItemDelegate::editorEvent(event, model, option, index); |
|
115 | 127 | } |
|
116 | 128 | }; |
|
117 | 129 | |
|
118 |
VariableInspectorWidget::VariableInspectorWidget(QWidget |
|
|
119 |
: QWidget{parent} |
|
|
120 |
|
|
|
121 |
|
|
|
130 | VariableInspectorWidget::VariableInspectorWidget(QWidget* parent) | |
|
131 | : QWidget { parent } | |
|
132 | , ui { new Ui::VariableInspectorWidget } | |
|
133 | , m_ProgressBarItemDelegate { new QProgressBarItemDelegate { this } } | |
|
122 | 134 | { |
|
123 | 135 | ui->setupUi(this); |
|
124 | 136 | |
|
125 | 137 | // Sets model for table |
|
126 | 138 | // auto sortFilterModel = new QSortFilterProxyModel{this}; |
|
127 | 139 | // sortFilterModel->setSourceModel(sqpApp->variableController().variableModel()); |
|
128 | 140 | |
|
129 | 141 | m_model = new VariableModel2(); |
|
130 | 142 | ui->tableView->setModel(m_model); |
|
131 | connect(m_model, &VariableModel2::createVariable, | |
|
132 | [](const QVariantHash &productData) | |
|
133 | { | |
|
143 | connect(m_model, &VariableModel2::createVariable, [](const QVariantHash& productData) { | |
|
134 | 144 | sqpApp->dataSourceController().requestVariable(productData); |
|
135 | 145 | }); |
|
136 | 146 | auto vc = &(sqpApp->variableController()); |
|
137 | 147 | connect(vc, &VariableController2::variableAdded, m_model, &VariableModel2::variableAdded); |
|
138 | 148 | connect(vc, &VariableController2::variableDeleted, m_model, &VariableModel2::variableDeleted); |
|
139 | 149 | connect(m_model, &VariableModel2::asyncChangeRange, vc, &VariableController2::asyncChangeRange); |
|
140 | 150 | |
|
141 | 151 | // Adds extra signal/slot between view and model, so the view can be updated instantly when |
|
142 | 152 | // there is a change of data in the model |
|
143 | //connect(m_model, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), this, | |
|
153 | // connect(m_model, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), this, | |
|
144 | 154 | // SLOT(refresh())); |
|
145 | 155 | |
|
146 | //ui->tableView->setSelectionModel(sqpApp->variableController().variableSelectionModel()); | |
|
156 | // ui->tableView->setSelectionModel(sqpApp->variableController().variableSelectionModel()); | |
|
147 | 157 | ui->tableView->setItemDelegateForColumn(0, m_ProgressBarItemDelegate); |
|
148 | 158 | |
|
149 | 159 | // Fixes column sizes |
|
150 | 160 | auto model = ui->tableView->model(); |
|
151 | 161 | const auto count = model->columnCount(); |
|
152 |
for (auto i = 0; i < count; ++i) |
|
|
162 | for (auto i = 0; i < count; ++i) | |
|
163 | { | |
|
153 | 164 | ui->tableView->setColumnWidth( |
|
154 | 165 | i, model->headerData(i, Qt::Horizontal, Qt::SizeHintRole).toSize().width()); |
|
155 | 166 | } |
|
156 | 167 | |
|
157 | 168 | // Sets selection options |
|
158 | 169 | ui->tableView->setSelectionBehavior(QTableView::SelectRows); |
|
159 | 170 | ui->tableView->setSelectionMode(QTableView::ExtendedSelection); |
|
160 | 171 | |
|
161 | 172 | // Connection to show a menu when right clicking on the tree |
|
162 | 173 | ui->tableView->setContextMenuPolicy(Qt::CustomContextMenu); |
|
163 | 174 | connect(ui->tableView, &QTableView::customContextMenuRequested, this, |
|
164 |
|
|
|
175 | &VariableInspectorWidget::onTableMenuRequested); | |
|
165 | 176 | } |
|
166 | 177 | |
|
167 | 178 | VariableInspectorWidget::~VariableInspectorWidget() |
|
168 | 179 | { |
|
169 | 180 | delete ui; |
|
170 | 181 | } |
|
171 | 182 | |
|
172 |
void VariableInspectorWidget::onTableMenuRequested(const QPoint |
|
|
183 | void VariableInspectorWidget::onTableMenuRequested(const QPoint& pos) noexcept | |
|
173 | 184 | { |
|
174 | 185 | auto selectedRows = ui->tableView->selectionModel()->selectedRows(); |
|
175 |
auto selectedVariables = QVector<std::shared_ptr<Variable> |
|
|
176 |
for (const auto |
|
|
177 | if (auto selectedVariable = this->m_model->variables()[selectedRow.row()]) { | |
|
186 | auto selectedVariables = QVector<std::shared_ptr<Variable2>> {}; | |
|
187 | for (const auto& selectedRow : qAsConst(selectedRows)) | |
|
188 | { | |
|
189 | if (auto selectedVariable = this->m_model->variables()[selectedRow.row()]) | |
|
190 | { | |
|
178 | 191 | selectedVariables.push_back(selectedVariable); |
|
179 | 192 | } |
|
180 | 193 | } |
|
181 | 194 | |
|
182 | QMenu tableMenu{}; | |
|
195 | QMenu tableMenu {}; | |
|
183 | 196 | |
|
184 | 197 | // Emits a signal so that potential receivers can populate the menu before displaying it |
|
185 | 198 | emit tableMenuAboutToBeDisplayed(&tableMenu, selectedVariables); |
|
186 | 199 | |
|
187 | 200 | // Adds menu-specific actions |
|
188 |
if (!selectedVariables.isEmpty()) |
|
|
201 | if (!selectedVariables.isEmpty()) | |
|
202 | { | |
|
189 | 203 | tableMenu.addSeparator(); |
|
190 | 204 | |
|
191 | 205 | // 'Rename' and 'Duplicate' actions (only if one variable selected) |
|
192 |
if (selectedVariables.size() == 1) |
|
|
206 | if (selectedVariables.size() == 1) | |
|
207 | { | |
|
193 | 208 | auto selectedVariable = selectedVariables.front(); |
|
194 | 209 | |
|
195 | auto duplicateFun = [varW = std::weak_ptr<Variable>(selectedVariable)]() | |
|
196 | { | |
|
197 | if (auto var = varW.lock()) { | |
|
210 | auto duplicateFun = [varW = std::weak_ptr<Variable2>(selectedVariable)]() { | |
|
211 | if (auto var = varW.lock()) | |
|
212 | { | |
|
198 | 213 | sqpApp->variableController().cloneVariable(var); |
|
199 | 214 | } |
|
200 | 215 | }; |
|
201 | 216 | |
|
202 | 217 | tableMenu.addAction(tr("Duplicate"), duplicateFun); |
|
203 | 218 | |
|
204 |
auto renameFun = [ |
|
|
205 | { | |
|
206 | if (auto var = varW.lock()) { | |
|
219 | auto renameFun = [varW = std::weak_ptr<Variable2>(selectedVariable), this]() { | |
|
220 | if (auto var = varW.lock()) | |
|
221 | { | |
|
207 | 222 | // Generates forbidden names (names associated to existing variables) |
|
208 | 223 | auto allVariables = sqpApp->variableController().variables(); |
|
209 | 224 | auto forbiddenNames = QVector<QString>(allVariables.size()); |
|
210 | 225 | std::transform(allVariables.cbegin(), allVariables.cend(), |
|
211 |
|
|
|
212 |
|
|
|
226 | forbiddenNames.begin(), | |
|
227 | [](const auto& variable) { return variable->name(); }); | |
|
213 | 228 | |
|
214 | RenameVariableDialog dialog{var->name(), forbiddenNames, this}; | |
|
215 |
if (dialog.exec() == QDialog::Accepted) |
|
|
229 | RenameVariableDialog dialog { var->name(), forbiddenNames, this }; | |
|
230 | if (dialog.exec() == QDialog::Accepted) | |
|
231 | { | |
|
216 | 232 | var->setName(dialog.name()); |
|
217 | 233 | } |
|
218 | 234 | } |
|
219 | 235 | }; |
|
220 | 236 | |
|
221 | 237 | tableMenu.addAction(tr("Rename..."), renameFun); |
|
222 | 238 | } |
|
223 | 239 | |
|
224 | 240 | // 'Delete' action |
|
225 | 241 | auto deleteFun = [&selectedVariables]() { |
|
226 | for(const auto& var:selectedVariables) | |
|
242 | for (const auto& var : selectedVariables) | |
|
227 | 243 | sqpApp->variableController().deleteVariable(var); |
|
228 | 244 | }; |
|
229 | 245 | |
|
230 | tableMenu.addAction(QIcon{":/icones/delete.png"}, tr("Delete"), deleteFun); | |
|
246 | tableMenu.addAction(QIcon { ":/icones/delete.png" }, tr("Delete"), deleteFun); | |
|
231 | 247 | } |
|
232 | 248 | |
|
233 |
if (!tableMenu.isEmpty()) |
|
|
249 | if (!tableMenu.isEmpty()) | |
|
250 | { | |
|
234 | 251 | // Generates menu header (inserted before first action) |
|
235 | 252 | auto firstAction = tableMenu.actions().first(); |
|
236 | auto headerAction = new QWidgetAction{&tableMenu}; | |
|
237 | headerAction->setDefaultWidget(new VariableMenuHeaderWidget{selectedVariables, &tableMenu}); | |
|
253 | auto headerAction = new QWidgetAction { &tableMenu }; | |
|
254 | headerAction->setDefaultWidget( | |
|
255 | new VariableMenuHeaderWidget { selectedVariables, &tableMenu }); | |
|
238 | 256 | tableMenu.insertAction(firstAction, headerAction); |
|
239 | 257 | |
|
240 | 258 | // Displays menu |
|
241 | 259 | tableMenu.exec(QCursor::pos()); |
|
242 | 260 | } |
|
243 | 261 | } |
|
244 | 262 | |
|
245 | 263 | void VariableInspectorWidget::refresh() noexcept |
|
246 | 264 | { |
|
247 | 265 | ui->tableView->viewport()->update(); |
|
248 | 266 | } |
@@ -1,38 +1,43 | |||
|
1 | 1 | #include "Variable/VariableMenuHeaderWidget.h" |
|
2 | #include "Variable/Variable.h" | |
|
2 | #include "Variable/Variable2.h" | |
|
3 | 3 | |
|
4 | 4 | #include <ui_VariableMenuHeaderWidget.h> |
|
5 | 5 | |
|
6 | 6 | Q_LOGGING_CATEGORY(LOG_VariableMenuHeaderWidget, "VariableMenuHeaderWidget") |
|
7 | 7 | |
|
8 | 8 | VariableMenuHeaderWidget::VariableMenuHeaderWidget( |
|
9 |
const QVector<std::shared_ptr<Variable> |
|
|
10 | : QWidget{parent}, ui{new Ui::VariableMenuHeaderWidget} | |
|
9 | const QVector<std::shared_ptr<Variable2>>& variables, QWidget* parent) | |
|
10 | : QWidget { parent }, ui { new Ui::VariableMenuHeaderWidget } | |
|
11 | 11 | { |
|
12 | 12 | ui->setupUi(this); |
|
13 | 13 | |
|
14 | 14 | // Generates label according to the state of the variables. The label contains : |
|
15 | 15 | // - the variable name if there is only one variable in the list |
|
16 | 16 | // - 'x variables' where x is the number of variables otherwise |
|
17 | 17 | const auto nbVariables = variables.size(); |
|
18 |
if (nbVariables == 1) |
|
|
19 | if (auto variable = variables.first()) { | |
|
18 | if (nbVariables == 1) | |
|
19 | { | |
|
20 | if (auto variable = variables.first()) | |
|
21 | { | |
|
20 | 22 | ui->label->setText(variable->name()); |
|
21 | 23 | } |
|
22 |
else |
|
|
24 | else | |
|
25 | { | |
|
23 | 26 | qCCritical(LOG_VariableMenuHeaderWidget()) |
|
24 | 27 | << tr("Can't get the name of the variable : variable is null"); |
|
25 | 28 | } |
|
26 | 29 | } |
|
27 |
else if (nbVariables > 1) |
|
|
30 | else if (nbVariables > 1) | |
|
31 | { | |
|
28 | 32 | ui->label->setText(tr("%1 variables").arg(nbVariables)); |
|
29 | 33 | } |
|
30 |
else |
|
|
34 | else | |
|
35 | { | |
|
31 | 36 | ui->label->setText(tr("No variable")); |
|
32 | 37 | } |
|
33 | 38 | } |
|
34 | 39 | |
|
35 | 40 | VariableMenuHeaderWidget::~VariableMenuHeaderWidget() noexcept |
|
36 | 41 | { |
|
37 | 42 | delete ui; |
|
38 | 43 | } |
@@ -1,209 +1,213 | |||
|
1 | 1 | #include "Visualization/AxisRenderingUtils.h" |
|
2 | 2 | |
|
3 |
#include <Data/ScalarSerie |
|
|
4 |
#include <Data/SpectrogramSerie |
|
|
5 |
#include <Data/VectorSerie |
|
|
3 | #include <Data/ScalarTimeSerie.h> | |
|
4 | #include <Data/SpectrogramTimeSerie.h> | |
|
5 | #include <Data/VectorTimeSerie.h> | |
|
6 | 6 | |
|
7 | #include <Variable/Variable.h> | |
|
7 | #include <Variable/Variable2.h> | |
|
8 | 8 | |
|
9 | 9 | #include <Visualization/SqpColorScale.h> |
|
10 | 10 | #include <Visualization/qcustomplot.h> |
|
11 | 11 | |
|
12 | 12 | Q_LOGGING_CATEGORY(LOG_AxisRenderingUtils, "AxisRenderingUtils") |
|
13 | 13 | |
|
14 |
namespace |
|
|
14 | namespace | |
|
15 | { | |
|
15 | 16 | |
|
16 | 17 | /// Format for datetimes on a axis |
|
17 | 18 | const auto DATETIME_TICKER_FORMAT = QStringLiteral("yyyy/MM/dd \nhh:mm:ss"); |
|
18 | 19 | |
|
19 | 20 | const auto NUMBER_FORMAT = 'g'; |
|
20 | 21 | const auto NUMBER_PRECISION = 9; |
|
21 | 22 | |
|
22 | 23 | /// Generates the appropriate ticker for an axis, depending on whether the axis displays time or |
|
23 | 24 | /// non-time data |
|
24 | 25 | QSharedPointer<QCPAxisTicker> axisTicker(bool isTimeAxis, QCPAxis::ScaleType scaleType) |
|
25 | 26 | { |
|
26 |
if (isTimeAxis) |
|
|
27 | if (isTimeAxis) | |
|
28 | { | |
|
27 | 29 | auto dateTicker = QSharedPointer<QCPAxisTickerDateTime>::create(); |
|
28 | 30 | dateTicker->setDateTimeFormat(DATETIME_TICKER_FORMAT); |
|
29 | 31 | dateTicker->setDateTimeSpec(Qt::UTC); |
|
30 | 32 | |
|
31 | 33 | return dateTicker; |
|
32 | 34 | } |
|
33 |
else if (scaleType == QCPAxis::stLogarithmic) |
|
|
35 | else if (scaleType == QCPAxis::stLogarithmic) | |
|
36 | { | |
|
34 | 37 | return QSharedPointer<QCPAxisTickerLog>::create(); |
|
35 | 38 | } |
|
36 |
else |
|
|
39 | else | |
|
40 | { | |
|
37 | 41 | // default ticker |
|
38 | 42 | return QSharedPointer<QCPAxisTicker>::create(); |
|
39 | 43 | } |
|
40 | 44 | } |
|
41 | 45 | |
|
42 | 46 | /** |
|
43 | 47 | * Sets properties of the axis passed as parameter |
|
44 | 48 | * @param axis the axis to set |
|
45 | 49 | * @param unit the unit to set for the axis |
|
46 | 50 | * @param scaleType the scale type to set for the axis |
|
47 | 51 | */ |
|
48 |
void setAxisProperties(QCPAxis |
|
|
49 |
|
|
|
52 | void setAxisProperties(QCPAxis& axis, const std::string& unit, bool isTime, | |
|
53 | QCPAxis::ScaleType scaleType = QCPAxis::stLinear) | |
|
50 | 54 | { |
|
51 | 55 | // label (unit name) |
|
52 |
axis.setLabel(unit |
|
|
56 | axis.setLabel(QString::fromStdString(unit)); | |
|
53 | 57 | |
|
54 | 58 | // scale type |
|
55 | 59 | axis.setScaleType(scaleType); |
|
56 |
if (scaleType == QCPAxis::stLogarithmic) |
|
|
60 | if (scaleType == QCPAxis::stLogarithmic) | |
|
61 | { | |
|
57 | 62 | // Scientific notation |
|
58 | 63 | axis.setNumberPrecision(0); |
|
59 | 64 | axis.setNumberFormat("eb"); |
|
60 | 65 | } |
|
61 | 66 | |
|
62 | 67 | // ticker (depending on the type of unit) |
|
63 |
axis.setTicker(axisTicker( |
|
|
68 | axis.setTicker(axisTicker(isTime, scaleType)); | |
|
64 | 69 | } |
|
65 | 70 | |
|
66 | 71 | /** |
|
67 | 72 | * Delegate used to set axes properties |
|
68 | 73 | */ |
|
69 | 74 | template <typename T, typename Enabled = void> |
|
70 |
struct AxisSetter |
|
|
71 | static void setProperties(QCustomPlot &, SqpColorScale &) | |
|
75 | struct AxisSetter | |
|
76 | { | |
|
77 | static void setProperties(QCustomPlot&, SqpColorScale&) | |
|
72 | 78 | { |
|
73 | 79 | // Default implementation does nothing |
|
74 | 80 | qCCritical(LOG_AxisRenderingUtils()) << "Can't set axis properties: unmanaged type of data"; |
|
75 | 81 | } |
|
76 | 82 | |
|
77 |
static void setUnits(T |
|
|
83 | static void setUnits(T&, QCustomPlot&, SqpColorScale&) | |
|
78 | 84 | { |
|
79 | 85 | // Default implementation does nothing |
|
80 | 86 | qCCritical(LOG_AxisRenderingUtils()) << "Can't set axis units: unmanaged type of data"; |
|
81 | 87 | } |
|
82 | 88 | }; |
|
83 | 89 | |
|
84 | 90 | /** |
|
85 | 91 | * Specialization of AxisSetter for scalars and vectors |
|
86 | 92 | * @sa ScalarSeries |
|
87 | 93 | * @sa VectorSeries |
|
88 | 94 | */ |
|
89 | 95 | template <typename T> |
|
90 | struct AxisSetter<T, typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value | |
|
91 | or std::is_base_of<VectorSeries, T>::value> > { | |
|
92 | static void setProperties(QCustomPlot &, SqpColorScale &) | |
|
96 | struct AxisSetter<T, | |
|
97 | typename std::enable_if_t<std::is_base_of<ScalarTimeSerie, T>::value | |
|
98 | or std::is_base_of<VectorTimeSerie, T>::value>> | |
|
99 | { | |
|
100 | static void setProperties(QCustomPlot&, SqpColorScale&) | |
|
93 | 101 | { |
|
94 | 102 | // Nothing to do |
|
95 | 103 | } |
|
96 | 104 | |
|
97 |
static void setUnits(T |
|
|
105 | static void setUnits(T& dataSeries, QCustomPlot& plot, SqpColorScale&) | |
|
98 | 106 | { |
|
99 | dataSeries.lockRead(); | |
|
100 | auto xAxisUnit = dataSeries.xAxisUnit(); | |
|
101 | auto valuesUnit = dataSeries.valuesUnit(); | |
|
102 | dataSeries.unlock(); | |
|
103 | ||
|
104 | // setAxisProperties(*plot.xAxis, xAxisUnit); | |
|
105 | // This is cheating but it's ok ;) | |
|
106 | setAxisProperties(*plot.xAxis, Unit{"s", true}); | |
|
107 | setAxisProperties(*plot.yAxis, valuesUnit); | |
|
107 | auto serie = dynamic_cast<TimeSeries::ITimeSerie*>(&dataSeries); | |
|
108 | setAxisProperties(*plot.xAxis, "s", true); | |
|
109 | setAxisProperties(*plot.yAxis, serie->unit(1), false); | |
|
108 | 110 | } |
|
109 | 111 | }; |
|
110 | 112 | |
|
111 | 113 | /** |
|
112 | 114 | * Specialization of AxisSetter for spectrograms |
|
113 | 115 | * @sa SpectrogramSeries |
|
114 | 116 | */ |
|
115 | 117 | template <typename T> |
|
116 |
struct AxisSetter<T, typename std::enable_if_t<std::is_base_of<SpectrogramSerie |
|
|
117 | static void setProperties(QCustomPlot &plot, SqpColorScale &colorScale) | |
|
118 | struct AxisSetter<T, typename std::enable_if_t<std::is_base_of<SpectrogramTimeSerie, T>::value>> | |
|
119 | { | |
|
120 | static void setProperties(QCustomPlot& plot, SqpColorScale& colorScale) | |
|
118 | 121 | { |
|
119 | 122 | // Displays color scale in plot |
|
120 | 123 | plot.plotLayout()->insertRow(0); |
|
121 | 124 | plot.plotLayout()->addElement(0, 0, colorScale.m_Scale); |
|
122 | 125 | colorScale.m_Scale->setType(QCPAxis::atTop); |
|
123 | colorScale.m_Scale->setMinimumMargins(QMargins{0, 0, 0, 0}); | |
|
126 | colorScale.m_Scale->setMinimumMargins(QMargins { 0, 0, 0, 0 }); | |
|
124 | 127 | |
|
125 | 128 | // Aligns color scale with axes |
|
126 | 129 | auto marginGroups = plot.axisRect()->marginGroups(); |
|
127 |
for (auto it = marginGroups.begin(), end = marginGroups.end(); it != end; ++it) |
|
|
130 | for (auto it = marginGroups.begin(), end = marginGroups.end(); it != end; ++it) | |
|
131 | { | |
|
128 | 132 | colorScale.m_Scale->setMarginGroup(it.key(), it.value()); |
|
129 | 133 | } |
|
130 | 134 | |
|
131 | 135 | // Set color scale properties |
|
132 | 136 | colorScale.m_AutomaticThreshold = true; |
|
133 | 137 | } |
|
134 | 138 | |
|
135 |
static void setUnits(T |
|
|
139 | static void setUnits(T& dataSeries, QCustomPlot& plot, SqpColorScale& colorScale) | |
|
136 | 140 | { |
|
137 | dataSeries.lockRead(); | |
|
138 | auto xAxisUnit = dataSeries.xAxisUnit(); | |
|
139 | auto yAxisUnit = dataSeries.yAxisUnit(); | |
|
140 | auto valuesUnit = dataSeries.valuesUnit(); | |
|
141 | dataSeries.unlock(); | |
|
142 | ||
|
143 | //setAxisProperties(*plot.xAxis, xAxisUnit); | |
|
144 | // This is cheating but it's ok ;) | |
|
145 | setAxisProperties(*plot.xAxis, Unit{"s", true}); | |
|
146 | setAxisProperties(*plot.yAxis, yAxisUnit, QCPAxis::stLogarithmic); | |
|
147 | setAxisProperties(*colorScale.m_Scale->axis(), valuesUnit, QCPAxis::stLogarithmic); | |
|
141 | auto serie = dynamic_cast<TimeSeries::ITimeSerie*>(&dataSeries); | |
|
142 | setAxisProperties(*plot.xAxis, "s", true); | |
|
143 | setAxisProperties(*plot.yAxis, serie->unit(1), false, QCPAxis::stLogarithmic); | |
|
144 | setAxisProperties( | |
|
145 | *colorScale.m_Scale->axis(), serie->unit(2), false, QCPAxis::stLogarithmic); | |
|
148 | 146 | } |
|
149 | 147 | }; |
|
150 | 148 | |
|
151 | 149 | /** |
|
152 | 150 | * Default implementation of IAxisHelper, which takes data series to set axes properties |
|
153 | 151 | * @tparam T the data series' type |
|
154 | 152 | */ |
|
155 | 153 | template <typename T> |
|
156 |
struct AxisHelper : public IAxisHelper |
|
|
157 | explicit AxisHelper(std::shared_ptr<T> dataSeries) : m_DataSeries{dataSeries} {} | |
|
154 | struct AxisHelper : public IAxisHelper | |
|
155 | { | |
|
156 | explicit AxisHelper(T* dataSeries) : m_DataSeries { dataSeries } {} | |
|
158 | 157 | |
|
159 |
void setProperties(QCustomPlot |
|
|
158 | void setProperties(QCustomPlot& plot, SqpColorScale& colorScale) override | |
|
160 | 159 | { |
|
161 | 160 | AxisSetter<T>::setProperties(plot, colorScale); |
|
162 | 161 | } |
|
163 | 162 | |
|
164 |
void setUnits(QCustomPlot |
|
|
163 | void setUnits(QCustomPlot& plot, SqpColorScale& colorScale) override | |
|
165 | 164 | { |
|
166 |
if (m_DataSeries) |
|
|
165 | if (m_DataSeries) | |
|
166 | { | |
|
167 | 167 | AxisSetter<T>::setUnits(*m_DataSeries, plot, colorScale); |
|
168 | 168 | } |
|
169 |
else |
|
|
169 | else | |
|
170 | { | |
|
170 | 171 | qCCritical(LOG_AxisRenderingUtils()) << "Can't set units: inconsistency between the " |
|
171 | 172 | "type of data series and the type supposed"; |
|
172 | 173 | } |
|
173 | 174 | } |
|
174 | 175 | |
|
175 |
|
|
|
176 | T* m_DataSeries; | |
|
176 | 177 | }; |
|
177 | 178 | |
|
178 | 179 | } // namespace |
|
179 | 180 | |
|
180 |
QString formatValue(double value, const QCPAxis |
|
|
181 | QString formatValue(double value, const QCPAxis& axis) | |
|
181 | 182 | { |
|
182 | 183 | // If the axis is a time axis, formats the value as a date |
|
183 |
if (auto axisTicker = qSharedPointerDynamicCast<QCPAxisTickerDateTime>(axis.ticker())) |
|
|
184 | if (auto axisTicker = qSharedPointerDynamicCast<QCPAxisTickerDateTime>(axis.ticker())) | |
|
185 | { | |
|
184 | 186 | return DateUtils::dateTime(value, axisTicker->dateTimeSpec()).toString(DATETIME_FORMAT); |
|
185 | 187 | } |
|
186 |
else |
|
|
188 | else | |
|
189 | { | |
|
187 | 190 | return QString::number(value, NUMBER_FORMAT, NUMBER_PRECISION); |
|
188 | 191 | } |
|
189 | 192 | } |
|
190 | 193 | |
|
191 |
std::unique_ptr<IAxisHelper> IAxisHelperFactory::create( |
|
|
194 | std::unique_ptr<IAxisHelper> IAxisHelperFactory::create(Variable2& variable) noexcept | |
|
192 | 195 | { |
|
193 |
switch (variable.type()) |
|
|
196 | switch (variable.type()) | |
|
197 | { | |
|
194 | 198 | case DataSeriesType::SCALAR: |
|
195 |
return std::make_unique<AxisHelper<ScalarSerie |
|
|
196 |
|
|
|
199 | return std::make_unique<AxisHelper<ScalarTimeSerie>>( | |
|
200 | dynamic_cast<ScalarTimeSerie*>(variable.data()->base())); | |
|
197 | 201 | case DataSeriesType::SPECTROGRAM: |
|
198 |
return std::make_unique<AxisHelper<SpectrogramSerie |
|
|
199 |
|
|
|
202 | return std::make_unique<AxisHelper<SpectrogramTimeSerie>>( | |
|
203 | dynamic_cast<SpectrogramTimeSerie*>(variable.data()->base())); | |
|
200 | 204 | case DataSeriesType::VECTOR: |
|
201 |
return std::make_unique<AxisHelper<VectorSerie |
|
|
202 |
|
|
|
205 | return std::make_unique<AxisHelper<VectorTimeSerie>>( | |
|
206 | dynamic_cast<VectorTimeSerie*>(variable.data()->base())); | |
|
203 | 207 | default: |
|
204 | 208 | // Creates default helper |
|
205 | 209 | break; |
|
206 | 210 | } |
|
207 | 211 | |
|
208 |
return std::make_unique<AxisHelper< |
|
|
212 | return std::make_unique<AxisHelper<TimeSeries::ITimeSerie>>(nullptr); | |
|
209 | 213 | } |
@@ -1,126 +1,137 | |||
|
1 | 1 | #include "Visualization/PlottablesRenderingUtils.h" |
|
2 | 2 | |
|
3 | 3 | #include <Common/ColorUtils.h> |
|
4 | 4 | |
|
5 | 5 | #include <Data/ScalarSeries.h> |
|
6 | 6 | #include <Data/SpectrogramSeries.h> |
|
7 | 7 | #include <Data/VectorSeries.h> |
|
8 | 8 | |
|
9 | #include <Variable/Variable.h> | |
|
9 | #include <Variable/Variable2.h> | |
|
10 | 10 | |
|
11 | 11 | #include <Visualization/qcustomplot.h> |
|
12 | 12 | |
|
13 | 13 | Q_LOGGING_CATEGORY(LOG_PlottablesRenderingUtils, "PlottablesRenderingUtils") |
|
14 | 14 | |
|
15 |
namespace |
|
|
15 | namespace | |
|
16 | { | |
|
16 | 17 | |
|
17 | 18 | /** |
|
18 | 19 | * Delegate used to set plottables properties |
|
19 | 20 | */ |
|
20 | 21 | template <typename T, typename Enabled = void> |
|
21 |
struct PlottablesSetter |
|
|
22 | static void setProperties(PlottablesMap &) | |
|
22 | struct PlottablesSetter | |
|
23 | { | |
|
24 | static void setProperties(PlottablesMap&) | |
|
23 | 25 | { |
|
24 | 26 | // Default implementation does nothing |
|
25 | 27 | qCCritical(LOG_PlottablesRenderingUtils()) |
|
26 | 28 | << "Can't set plottables properties: unmanaged type of data"; |
|
27 | 29 | } |
|
28 | 30 | }; |
|
29 | 31 | |
|
30 | 32 | /** |
|
31 | 33 | * Specialization of PlottablesSetter for scalars and vectors |
|
32 | 34 | * @sa ScalarSeries |
|
33 | 35 | * @sa VectorSeries |
|
34 | 36 | */ |
|
35 | 37 | template <typename T> |
|
36 | struct PlottablesSetter<T, typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value | |
|
37 | or std::is_base_of<VectorSeries, T>::value> > { | |
|
38 | static void setProperties(PlottablesMap &plottables) | |
|
38 | struct PlottablesSetter<T, | |
|
39 | typename std::enable_if_t<std::is_base_of<ScalarTimeSerie, T>::value | |
|
40 | or std::is_base_of<VectorTimeSerie, T>::value>> | |
|
41 | { | |
|
42 | static void setProperties(PlottablesMap& plottables) | |
|
39 | 43 | { |
|
40 | 44 | // Finds the plottable with the highest index to determine the number of colors to generate |
|
41 | 45 | auto end = plottables.cend(); |
|
42 | auto maxPlottableIndexIt | |
|
43 | = std::max_element(plottables.cbegin(), end, [](const auto &it1, const auto &it2) { | |
|
44 | return it1.first < it2.first; | |
|
45 | }); | |
|
46 | auto maxPlottableIndexIt = std::max_element(plottables.cbegin(), end, | |
|
47 | [](const auto& it1, const auto& it2) { return it1.first < it2.first; }); | |
|
46 | 48 | auto componentCount = maxPlottableIndexIt != end ? maxPlottableIndexIt->first + 1 : 0; |
|
47 | 49 | |
|
48 | 50 | // Generates colors for each component |
|
49 | 51 | auto colors = ColorUtils::colors(Qt::blue, Qt::red, componentCount); |
|
50 | 52 | |
|
51 | 53 | // For each component of the data series, creates a QCPGraph to add to the plot |
|
52 |
for (auto i = 0; i < componentCount; ++i) |
|
|
54 | for (auto i = 0; i < componentCount; ++i) | |
|
55 | { | |
|
53 | 56 | auto graphIt = plottables.find(i); |
|
54 |
if (graphIt != end) |
|
|
55 | graphIt->second->setPen(QPen{colors.at(i)}); | |
|
57 | if (graphIt != end) | |
|
58 | { | |
|
59 | graphIt->second->setPen(QPen { colors.at(i) }); | |
|
56 | 60 | } |
|
57 | 61 | } |
|
58 | 62 | } |
|
59 | 63 | }; |
|
60 | 64 | |
|
61 | 65 | /** |
|
62 | 66 | * Specialization of PlottablesSetter for spectrograms |
|
63 | 67 | * @sa SpectrogramSeries |
|
64 | 68 | */ |
|
65 | 69 | template <typename T> |
|
66 | 70 | struct PlottablesSetter<T, |
|
67 |
|
|
|
68 | static void setProperties(PlottablesMap &plottables) | |
|
71 | typename std::enable_if_t<std::is_base_of<SpectrogramTimeSerie, T>::value>> | |
|
72 | { | |
|
73 | static void setProperties(PlottablesMap& plottables) | |
|
69 | 74 | { |
|
70 | 75 | // Checks that for a spectrogram there is only one plottable, that is a colormap |
|
71 |
if (plottables.size() != 1) |
|
|
76 | if (plottables.size() != 1) | |
|
77 | { | |
|
72 | 78 | return; |
|
73 | 79 | } |
|
74 | 80 | |
|
75 |
if (auto colormap = dynamic_cast<QCPColorMap |
|
|
81 | if (auto colormap = dynamic_cast<QCPColorMap*>(plottables.begin()->second)) | |
|
82 | { | |
|
76 | 83 | colormap->setInterpolate(false); // No value interpolation |
|
77 | 84 | colormap->setTightBoundary(true); |
|
78 | 85 | |
|
79 | 86 | // Finds color scale in the colormap's plot to associate with it |
|
80 | 87 | auto plot = colormap->parentPlot(); |
|
81 | 88 | auto plotElements = plot->plotLayout()->elements(false); |
|
82 |
for (auto plotElement : plotElements) |
|
|
83 | if (auto colorScale = dynamic_cast<QCPColorScale *>(plotElement)) { | |
|
89 | for (auto plotElement : plotElements) | |
|
90 | { | |
|
91 | if (auto colorScale = dynamic_cast<QCPColorScale*>(plotElement)) | |
|
92 | { | |
|
84 | 93 | colormap->setColorScale(colorScale); |
|
85 | 94 | } |
|
86 | 95 | } |
|
87 | 96 | |
|
88 | 97 | colormap->rescaleDataRange(); |
|
89 | 98 | } |
|
90 |
else |
|
|
99 | else | |
|
100 | { | |
|
91 | 101 | qCCritical(LOG_PlottablesRenderingUtils()) << "Can't get colormap of the spectrogram"; |
|
92 | 102 | } |
|
93 | 103 | } |
|
94 | 104 | }; |
|
95 | 105 | |
|
96 | 106 | /** |
|
97 | 107 | * Default implementation of IPlottablesHelper, which takes data series to set plottables properties |
|
98 | 108 | * @tparam T the data series' type |
|
99 | 109 | */ |
|
100 | 110 | template <typename T> |
|
101 |
struct PlottablesHelper : public IPlottablesHelper |
|
|
102 | void setProperties(PlottablesMap &plottables) override | |
|
111 | struct PlottablesHelper : public IPlottablesHelper | |
|
112 | { | |
|
113 | void setProperties(PlottablesMap& plottables) override | |
|
103 | 114 | { |
|
104 | 115 | PlottablesSetter<T>::setProperties(plottables); |
|
105 | 116 | } |
|
106 | 117 | }; |
|
107 | 118 | |
|
108 | 119 | } // namespace |
|
109 | 120 | |
|
110 | std::unique_ptr<IPlottablesHelper> | |
|
111 | IPlottablesHelperFactory::create(const Variable &variable) noexcept | |
|
121 | std::unique_ptr<IPlottablesHelper> IPlottablesHelperFactory::create(Variable2& variable) noexcept | |
|
112 | 122 | { |
|
113 |
switch (variable.type()) |
|
|
123 | switch (variable.type()) | |
|
124 | { | |
|
114 | 125 | case DataSeriesType::SCALAR: |
|
115 |
return std::make_unique<PlottablesHelper<ScalarSerie |
|
|
126 | return std::make_unique<PlottablesHelper<ScalarTimeSerie>>(); | |
|
116 | 127 | case DataSeriesType::SPECTROGRAM: |
|
117 |
return std::make_unique<PlottablesHelper<SpectrogramSerie |
|
|
128 | return std::make_unique<PlottablesHelper<SpectrogramTimeSerie>>(); | |
|
118 | 129 | case DataSeriesType::VECTOR: |
|
119 |
return std::make_unique<PlottablesHelper<VectorSerie |
|
|
130 | return std::make_unique<PlottablesHelper<VectorTimeSerie>>(); | |
|
120 | 131 | default: |
|
121 | 132 | // Returns default helper |
|
122 | 133 | break; |
|
123 | 134 | } |
|
124 | 135 | |
|
125 |
return std::make_unique<PlottablesHelper<IDataSeries> |
|
|
136 | return std::make_unique<PlottablesHelper<IDataSeries>>(); | |
|
126 | 137 | } |
@@ -1,361 +1,443 | |||
|
1 | 1 | #include "Visualization/VisualizationGraphHelper.h" |
|
2 | 2 | #include "Visualization/qcustomplot.h" |
|
3 | 3 | |
|
4 | 4 | #include <Data/DataSeriesUtils.h> |
|
5 |
#include <Data/ScalarSerie |
|
|
6 |
#include <Data/SpectrogramSerie |
|
|
7 |
#include <Data/VectorSerie |
|
|
5 | #include <Data/ScalarTimeSerie.h> | |
|
6 | #include <Data/SpectrogramTimeSerie.h> | |
|
7 | #include <Data/VectorTimeSerie.h> | |
|
8 | 8 | |
|
9 | #include <Variable/Variable.h> | |
|
9 | #include <Variable/Variable2.h> | |
|
10 | 10 | |
|
11 | 11 | Q_LOGGING_CATEGORY(LOG_VisualizationGraphHelper, "VisualizationGraphHelper") |
|
12 | 12 | |
|
13 |
namespace |
|
|
13 | namespace | |
|
14 | { | |
|
14 | 15 | |
|
15 |
class SqpDataContainer : public QCPGraphDataContainer |
|
|
16 | class SqpDataContainer : public QCPGraphDataContainer | |
|
17 | { | |
|
16 | 18 | public: |
|
17 |
void appendGraphData(const QCPGraphData |
|
|
19 | void appendGraphData(const QCPGraphData& data) { mData.append(data); } | |
|
18 | 20 | }; |
|
19 | 21 | |
|
20 | 22 | /** |
|
21 | 23 | * Struct used to create plottables, depending on the type of the data series from which to create |
|
22 | 24 | * them |
|
23 | 25 | * @tparam T the data series' type |
|
24 | 26 | * @remarks Default implementation can't create plottables |
|
25 | 27 | */ |
|
26 | 28 | template <typename T, typename Enabled = void> |
|
27 |
struct PlottablesCreator |
|
|
28 | static PlottablesMap createPlottables(QCustomPlot &) | |
|
29 | { | |
|
30 | qCCritical(LOG_DataSeries()) | |
|
31 | << QObject::tr("Can't create plottables: unmanaged data series type"); | |
|
32 | return {}; | |
|
33 | } | |
|
29 | struct PlottablesCreator | |
|
30 | { | |
|
31 | static PlottablesMap createPlottables(QCustomPlot&) { return {}; } | |
|
34 | 32 | }; |
|
35 | 33 | |
|
36 |
PlottablesMap createGraphs(QCustomPlot |
|
|
34 | PlottablesMap createGraphs(QCustomPlot& plot, int nbGraphs) | |
|
37 | 35 | { |
|
38 | PlottablesMap result{}; | |
|
36 | PlottablesMap result {}; | |
|
39 | 37 | |
|
40 | 38 | // Creates {nbGraphs} QCPGraph to add to the plot |
|
41 |
for (auto i = 0; i < nbGraphs; ++i) |
|
|
39 | for (auto i = 0; i < nbGraphs; ++i) | |
|
40 | { | |
|
42 | 41 | auto graph = plot.addGraph(); |
|
43 | result.insert({i, graph}); | |
|
42 | result.insert({ i, graph }); | |
|
44 | 43 | } |
|
45 | 44 | |
|
46 | 45 | plot.replot(); |
|
47 | 46 | |
|
48 | 47 | return result; |
|
49 | 48 | } |
|
50 | 49 | |
|
51 | 50 | /** |
|
52 | 51 | * Specialization of PlottablesCreator for scalars |
|
53 | 52 | * @sa ScalarSeries |
|
54 | 53 | */ |
|
55 | 54 | template <typename T> |
|
56 |
struct PlottablesCreator<T, typename std::enable_if_t<std::is_base_of<ScalarSerie |
|
|
57 | static PlottablesMap createPlottables(QCustomPlot &plot) { return createGraphs(plot, 1); } | |
|
55 | struct PlottablesCreator<T, typename std::enable_if_t<std::is_base_of<ScalarTimeSerie, T>::value>> | |
|
56 | { | |
|
57 | static PlottablesMap createPlottables(QCustomPlot& plot) { return createGraphs(plot, 1); } | |
|
58 | 58 | }; |
|
59 | 59 | |
|
60 | 60 | /** |
|
61 | 61 | * Specialization of PlottablesCreator for vectors |
|
62 | 62 | * @sa VectorSeries |
|
63 | 63 | */ |
|
64 | 64 | template <typename T> |
|
65 |
struct PlottablesCreator<T, typename std::enable_if_t<std::is_base_of<VectorSerie |
|
|
66 | static PlottablesMap createPlottables(QCustomPlot &plot) { return createGraphs(plot, 3); } | |
|
65 | struct PlottablesCreator<T, typename std::enable_if_t<std::is_base_of<VectorTimeSerie, T>::value>> | |
|
66 | { | |
|
67 | static PlottablesMap createPlottables(QCustomPlot& plot) { return createGraphs(plot, 3); } | |
|
67 | 68 | }; |
|
68 | 69 | |
|
69 | 70 | /** |
|
70 | 71 | * Specialization of PlottablesCreator for spectrograms |
|
71 | 72 | * @sa SpectrogramSeries |
|
72 | 73 | */ |
|
73 | 74 | template <typename T> |
|
74 | 75 | struct PlottablesCreator<T, |
|
75 |
|
|
|
76 | static PlottablesMap createPlottables(QCustomPlot &plot) | |
|
76 | typename std::enable_if_t<std::is_base_of<SpectrogramTimeSerie, T>::value>> | |
|
77 | { | |
|
78 | static PlottablesMap createPlottables(QCustomPlot& plot) | |
|
77 | 79 | { |
|
78 | PlottablesMap result{}; | |
|
79 | result.insert({0, new QCPColorMap{plot.xAxis, plot.yAxis}}); | |
|
80 | PlottablesMap result {}; | |
|
81 | result.insert({ 0, new QCPColorMap { plot.xAxis, plot.yAxis } }); | |
|
80 | 82 | |
|
81 | 83 | plot.replot(); |
|
82 | 84 | |
|
83 | 85 | return result; |
|
84 | 86 | } |
|
85 | 87 | }; |
|
86 | 88 | |
|
87 | 89 | /** |
|
88 | 90 | * Struct used to update plottables, depending on the type of the data series from which to update |
|
89 | 91 | * them |
|
90 | 92 | * @tparam T the data series' type |
|
91 | 93 | * @remarks Default implementation can't update plottables |
|
92 | 94 | */ |
|
93 | 95 | template <typename T, typename Enabled = void> |
|
94 |
struct PlottablesUpdater |
|
|
95 | static void setPlotYAxisRange(T &, const DateTimeRange &, QCustomPlot &) | |
|
96 | struct PlottablesUpdater | |
|
97 | { | |
|
98 | static void setPlotYAxisRange(T&, const DateTimeRange&, QCustomPlot&) | |
|
96 | 99 | { |
|
97 | 100 | qCCritical(LOG_VisualizationGraphHelper()) |
|
98 | 101 | << QObject::tr("Can't set plot y-axis range: unmanaged data series type"); |
|
99 | 102 | } |
|
100 | 103 | |
|
101 |
static void updatePlottables(T |
|
|
104 | static void updatePlottables(T&, PlottablesMap&, const DateTimeRange&, bool) | |
|
102 | 105 | { |
|
103 | 106 | qCCritical(LOG_VisualizationGraphHelper()) |
|
104 | 107 | << QObject::tr("Can't update plottables: unmanaged data series type"); |
|
105 | 108 | } |
|
106 | 109 | }; |
|
107 | 110 | |
|
108 | 111 | /** |
|
109 | 112 | * Specialization of PlottablesUpdater for scalars and vectors |
|
110 | 113 | * @sa ScalarSeries |
|
111 | 114 | * @sa VectorSeries |
|
112 | 115 | */ |
|
113 | 116 | template <typename T> |
|
114 | struct PlottablesUpdater<T, | |
|
115 | typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value | |
|
116 | or std::is_base_of<VectorSeries, T>::value> > { | |
|
117 | static void setPlotYAxisRange(T &dataSeries, const DateTimeRange &xAxisRange, QCustomPlot &plot) | |
|
117 | struct PlottablesUpdater<T, typename std::enable_if_t<std::is_base_of<ScalarTimeSerie, T>::value>> | |
|
118 | { | |
|
119 | static void setPlotYAxisRange(T& dataSeries, const DateTimeRange& xAxisRange, QCustomPlot& plot) | |
|
118 | 120 | { |
|
119 | 121 | auto minValue = 0., maxValue = 0.; |
|
120 | ||
|
121 | dataSeries.lockRead(); | |
|
122 | auto valuesBounds = dataSeries.valuesBounds(xAxisRange.m_TStart, xAxisRange.m_TEnd); | |
|
123 | auto end = dataSeries.cend(); | |
|
124 | if (valuesBounds.first != end && valuesBounds.second != end) { | |
|
125 | auto rangeValue = [](const auto &value) { return std::isnan(value) ? 0. : value; }; | |
|
126 | ||
|
127 | minValue = rangeValue(valuesBounds.first->minValue()); | |
|
128 | maxValue = rangeValue(valuesBounds.second->maxValue()); | |
|
122 | if (auto serie = dynamic_cast<ScalarTimeSerie*>(&dataSeries)) | |
|
123 | { | |
|
124 | maxValue = (*std::max_element(std::begin(*serie), std::end(*serie))).v(); | |
|
125 | minValue = (*std::min_element(std::begin(*serie), std::end(*serie))).v(); | |
|
129 | 126 | } |
|
130 | dataSeries.unlock(); | |
|
131 | ||
|
132 | plot.yAxis->setRange(QCPRange{minValue, maxValue}); | |
|
127 | plot.yAxis->setRange(QCPRange { minValue, maxValue }); | |
|
133 | 128 | } |
|
134 | 129 | |
|
135 | static void updatePlottables(T &dataSeries, PlottablesMap &plottables, const DateTimeRange &range, | |
|
136 | bool rescaleAxes) | |
|
130 | static void updatePlottables( | |
|
131 | T& dataSeries, PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes) | |
|
137 | 132 | { |
|
138 | 133 | |
|
139 | 134 | // For each plottable to update, resets its data |
|
140 | std::map<int, QSharedPointer<SqpDataContainer> > dataContainers{}; | |
|
141 | for (const auto &plottable : plottables) { | |
|
142 |
if (auto graph = dynamic_cast<QCPGraph |
|
|
135 | for (const auto& plottable : plottables) | |
|
136 | { | |
|
137 | if (auto graph = dynamic_cast<QCPGraph*>(plottable.second)) | |
|
138 | { | |
|
143 | 139 | auto dataContainer = QSharedPointer<SqpDataContainer>::create(); |
|
140 | if (auto serie = dynamic_cast<ScalarTimeSerie*>(&dataSeries)) | |
|
141 | { | |
|
142 | std::for_each( | |
|
143 | std::begin(*serie), std::end(*serie), [&dataContainer](const auto& value) { | |
|
144 | dataContainer->appendGraphData(QCPGraphData(value.t(), value.v())); | |
|
145 | }); | |
|
146 | } | |
|
144 | 147 | graph->setData(dataContainer); |
|
145 | ||
|
146 | dataContainers.insert({plottable.first, dataContainer}); | |
|
147 | } | |
|
148 | } | |
|
149 | dataSeries.lockRead(); | |
|
150 | ||
|
151 | // - Gets the data of the series included in the current range | |
|
152 | // - Updates each plottable by adding, for each data item, a point that takes x-axis data | |
|
153 | // and value data. The correct value is retrieved according to the index of the component | |
|
154 | auto subDataIts = dataSeries.xAxisRange(range.m_TStart, range.m_TEnd); | |
|
155 | for (auto it = subDataIts.first; it != subDataIts.second; ++it) { | |
|
156 | for (const auto &dataContainer : dataContainers) { | |
|
157 | auto componentIndex = dataContainer.first; | |
|
158 | dataContainer.second->appendGraphData( | |
|
159 | QCPGraphData(it->x(), it->value(componentIndex))); | |
|
160 | 148 | } |
|
161 | 149 | } |
|
162 | 150 | |
|
163 | dataSeries.unlock(); | |
|
164 | ||
|
165 | if (!plottables.empty()) { | |
|
151 | if (!plottables.empty()) | |
|
152 | { | |
|
166 | 153 | auto plot = plottables.begin()->second->parentPlot(); |
|
167 | 154 | |
|
168 |
if (rescaleAxes) |
|
|
155 | if (rescaleAxes) | |
|
156 | { | |
|
169 | 157 | plot->rescaleAxes(); |
|
170 | 158 | } |
|
171 | 159 | } |
|
172 | 160 | } |
|
173 | 161 | }; |
|
174 | 162 | |
|
175 | /** | |
|
176 | * Specialization of PlottablesUpdater for spectrograms | |
|
177 | * @sa SpectrogramSeries | |
|
178 | */ | |
|
163 | ||
|
179 | 164 | template <typename T> |
|
180 | struct PlottablesUpdater<T, | |
|
181 | typename std::enable_if_t<std::is_base_of<SpectrogramSeries, T>::value> > { | |
|
182 |
static void setPlotYAxisRange(T |
|
|
165 | struct PlottablesUpdater<T, typename std::enable_if_t<std::is_base_of<VectorTimeSerie, T>::value>> | |
|
166 | { | |
|
167 | static void setPlotYAxisRange(T& dataSeries, const DateTimeRange& xAxisRange, QCustomPlot& plot) | |
|
183 | 168 | { |
|
184 | double min, max; | |
|
185 | std::tie(min, max) = dataSeries.yBounds(); | |
|
186 | ||
|
187 | if (!std::isnan(min) && !std::isnan(max)) { | |
|
188 | plot.yAxis->setRange(QCPRange{min, max}); | |
|
169 | double minValue = 0., maxValue = 0.; | |
|
170 | if (auto serie = dynamic_cast<VectorTimeSerie*>(&dataSeries)) | |
|
171 | { | |
|
172 | std::for_each( | |
|
173 | std::begin(*serie), std::end(*serie), [&minValue, &maxValue](const auto& v) { | |
|
174 | minValue = std::min({ minValue, v.v().x, v.v().y, v.v().z }); | |
|
175 | maxValue = std::max({ maxValue, v.v().x, v.v().y, v.v().z }); | |
|
176 | }); | |
|
189 | 177 | } |
|
178 | ||
|
179 | plot.yAxis->setRange(QCPRange { minValue, maxValue }); | |
|
190 | 180 | } |
|
191 | 181 | |
|
192 | static void updatePlottables(T &dataSeries, PlottablesMap &plottables, const DateTimeRange &range, | |
|
193 | bool rescaleAxes) | |
|
182 | static void updatePlottables( | |
|
183 | T& dataSeries, PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes) | |
|
194 | 184 | { |
|
195 | if (plottables.empty()) { | |
|
196 | qCDebug(LOG_VisualizationGraphHelper()) | |
|
197 | << QObject::tr("Can't update spectrogram: no colormap has been associated"); | |
|
198 | return; | |
|
199 | } | |
|
200 | ||
|
201 | // Gets the colormap to update (normally there is only one colormap) | |
|
202 | Q_ASSERT(plottables.size() == 1); | |
|
203 | auto colormap = dynamic_cast<QCPColorMap *>(plottables.at(0)); | |
|
204 | Q_ASSERT(colormap != nullptr); | |
|
205 | ||
|
206 | dataSeries.lockRead(); | |
|
207 | ||
|
208 | // Processing spectrogram data for display in QCustomPlot | |
|
209 | auto its = dataSeries.xAxisRange(range.m_TStart, range.m_TEnd); | |
|
210 | 185 | |
|
211 | // Computes logarithmic y-axis resolution for the spectrogram | |
|
212 | auto yData = its.first->y(); | |
|
213 | auto yResolution = DataSeriesUtils::resolution(yData.begin(), yData.end(), true); | |
|
214 | ||
|
215 | // Generates mesh for colormap | |
|
216 | auto mesh = DataSeriesUtils::regularMesh( | |
|
217 | its.first, its.second, DataSeriesUtils::Resolution{dataSeries.xResolution()}, | |
|
218 | yResolution); | |
|
219 | ||
|
220 | dataSeries.unlock(); | |
|
221 | ||
|
222 | colormap->data()->setSize(mesh.m_NbX, mesh.m_NbY); | |
|
223 | if (!mesh.isEmpty()) { | |
|
224 | colormap->data()->setRange( | |
|
225 | QCPRange{mesh.m_XMin, mesh.xMax()}, | |
|
226 | // y-axis range is converted to linear values | |
|
227 | QCPRange{std::pow(10, mesh.m_YMin), std::pow(10, mesh.yMax())}); | |
|
228 | ||
|
229 | // Sets values | |
|
230 | auto index = 0; | |
|
231 | for (auto it = mesh.m_Data.begin(), end = mesh.m_Data.end(); it != end; ++it, ++index) { | |
|
232 | auto xIndex = index % mesh.m_NbX; | |
|
233 | auto yIndex = index / mesh.m_NbX; | |
|
234 | ||
|
235 | colormap->data()->setCell(xIndex, yIndex, *it); | |
|
236 | ||
|
237 | // Makes the NaN values to be transparent in the colormap | |
|
238 | if (std::isnan(*it)) { | |
|
239 | colormap->data()->setAlpha(xIndex, yIndex, 0); | |
|
186 | // For each plottable to update, resets its data | |
|
187 | for (const auto& plottable : plottables) | |
|
188 | { | |
|
189 | if (auto graph = dynamic_cast<QCPGraph*>(plottable.second)) | |
|
190 | { | |
|
191 | auto dataContainer = QSharedPointer<SqpDataContainer>::create(); | |
|
192 | if (auto serie = dynamic_cast<VectorTimeSerie*>(&dataSeries)) | |
|
193 | { | |
|
194 | switch (plottable.first) | |
|
195 | { | |
|
196 | case 0: | |
|
197 | std::for_each(std::begin(*serie), std::end(*serie), | |
|
198 | [&dataContainer](const auto& value) { | |
|
199 | dataContainer->appendGraphData( | |
|
200 | QCPGraphData(value.t(), value.v().x)); | |
|
201 | }); | |
|
202 | break; | |
|
203 | case 1: | |
|
204 | std::for_each(std::begin(*serie), std::end(*serie), | |
|
205 | [&dataContainer](const auto& value) { | |
|
206 | dataContainer->appendGraphData( | |
|
207 | QCPGraphData(value.t(), value.v().y)); | |
|
208 | }); | |
|
209 | break; | |
|
210 | case 2: | |
|
211 | std::for_each(std::begin(*serie), std::end(*serie), | |
|
212 | [&dataContainer](const auto& value) { | |
|
213 | dataContainer->appendGraphData( | |
|
214 | QCPGraphData(value.t(), value.v().z)); | |
|
215 | }); | |
|
216 | break; | |
|
217 | default: | |
|
218 | break; | |
|
219 | } | |
|
240 | 220 | } |
|
221 | graph->setData(dataContainer); | |
|
241 | 222 | } |
|
242 | 223 | } |
|
243 | 224 | |
|
244 | // Rescales axes | |
|
245 | auto plot = colormap->parentPlot(); | |
|
225 | if (!plottables.empty()) | |
|
226 | { | |
|
227 | auto plot = plottables.begin()->second->parentPlot(); | |
|
246 | 228 | |
|
247 |
if (rescaleAxes) |
|
|
248 | plot->rescaleAxes(); | |
|
229 | if (rescaleAxes) | |
|
230 | { | |
|
231 | plot->rescaleAxes(); | |
|
232 | } | |
|
249 | 233 | } |
|
250 | 234 | } |
|
251 | 235 | }; |
|
252 | 236 | |
|
253 | 237 | /** |
|
238 | * Specialization of PlottablesUpdater for spectrograms | |
|
239 | * @sa SpectrogramSeries | |
|
240 | */ | |
|
241 | template <typename T> | |
|
242 | struct PlottablesUpdater<T, | |
|
243 | typename std::enable_if_t<std::is_base_of<SpectrogramTimeSerie, T>::value>> | |
|
244 | { | |
|
245 | static void setPlotYAxisRange(T& dataSeries, const DateTimeRange& xAxisRange, QCustomPlot& plot) | |
|
246 | { | |
|
247 | // TODO | |
|
248 | // double min, max; | |
|
249 | // std::tie(min, max) = dataSeries.yBounds(); | |
|
250 | ||
|
251 | // if (!std::isnan(min) && !std::isnan(max)) | |
|
252 | // { | |
|
253 | // plot.yAxis->setRange(QCPRange { min, max }); | |
|
254 | // } | |
|
255 | } | |
|
256 | ||
|
257 | static void updatePlottables( | |
|
258 | T& dataSeries, PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes) | |
|
259 | { | |
|
260 | // TODO | |
|
261 | // if (plottables.empty()) | |
|
262 | // { | |
|
263 | // qCDebug(LOG_VisualizationGraphHelper()) | |
|
264 | // << QObject::tr("Can't update spectrogram: no colormap has been | |
|
265 | // associated"); | |
|
266 | // return; | |
|
267 | // } | |
|
268 | ||
|
269 | // // Gets the colormap to update (normally there is only one colormap) | |
|
270 | // Q_ASSERT(plottables.size() == 1); | |
|
271 | // auto colormap = dynamic_cast<QCPColorMap*>(plottables.at(0)); | |
|
272 | // Q_ASSERT(colormap != nullptr); | |
|
273 | ||
|
274 | // dataSeries.lockRead(); | |
|
275 | ||
|
276 | // // Processing spectrogram data for display in QCustomPlot | |
|
277 | // auto its = dataSeries.xAxisRange(range.m_TStart, range.m_TEnd); | |
|
278 | ||
|
279 | // // Computes logarithmic y-axis resolution for the spectrogram | |
|
280 | // auto yData = its.first->y(); | |
|
281 | // auto yResolution = DataSeriesUtils::resolution(yData.begin(), yData.end(), true); | |
|
282 | ||
|
283 | // // Generates mesh for colormap | |
|
284 | // auto mesh = DataSeriesUtils::regularMesh(its.first, its.second, | |
|
285 | // DataSeriesUtils::Resolution { dataSeries.xResolution() }, yResolution); | |
|
286 | ||
|
287 | // dataSeries.unlock(); | |
|
288 | ||
|
289 | // colormap->data()->setSize(mesh.m_NbX, mesh.m_NbY); | |
|
290 | // if (!mesh.isEmpty()) | |
|
291 | // { | |
|
292 | // colormap->data()->setRange(QCPRange { mesh.m_XMin, mesh.xMax() }, | |
|
293 | // // y-axis range is converted to linear values | |
|
294 | // QCPRange { std::pow(10, mesh.m_YMin), std::pow(10, mesh.yMax()) }); | |
|
295 | ||
|
296 | // // Sets values | |
|
297 | // auto index = 0; | |
|
298 | // for (auto it = mesh.m_Data.begin(), end = mesh.m_Data.end(); it != end; ++it, | |
|
299 | // ++index) | |
|
300 | // { | |
|
301 | // auto xIndex = index % mesh.m_NbX; | |
|
302 | // auto yIndex = index / mesh.m_NbX; | |
|
303 | ||
|
304 | // colormap->data()->setCell(xIndex, yIndex, *it); | |
|
305 | ||
|
306 | // // Makes the NaN values to be transparent in the colormap | |
|
307 | // if (std::isnan(*it)) | |
|
308 | // { | |
|
309 | // colormap->data()->setAlpha(xIndex, yIndex, 0); | |
|
310 | // } | |
|
311 | // } | |
|
312 | // } | |
|
313 | ||
|
314 | // // Rescales axes | |
|
315 | // auto plot = colormap->parentPlot(); | |
|
316 | ||
|
317 | // if (rescaleAxes) | |
|
318 | // { | |
|
319 | // plot->rescaleAxes(); | |
|
320 | // } | |
|
321 | } | |
|
322 | }; | |
|
323 | ||
|
324 | /** | |
|
254 | 325 | * Helper used to create/update plottables |
|
255 | 326 | */ |
|
256 |
struct IPlottablesHelper |
|
|
327 | struct IPlottablesHelper | |
|
328 | { | |
|
257 | 329 | virtual ~IPlottablesHelper() noexcept = default; |
|
258 |
virtual PlottablesMap create(QCustomPlot |
|
|
259 |
virtual void setYAxisRange(const DateTimeRange |
|
|
260 | virtual void update(PlottablesMap &plottables, const DateTimeRange &range, | |
|
261 |
|
|
|
330 | virtual PlottablesMap create(QCustomPlot& plot) const = 0; | |
|
331 | virtual void setYAxisRange(const DateTimeRange& xAxisRange, QCustomPlot& plot) const = 0; | |
|
332 | virtual void update( | |
|
333 | PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes = false) const = 0; | |
|
262 | 334 | }; |
|
263 | 335 | |
|
264 | 336 | /** |
|
265 | 337 | * Default implementation of IPlottablesHelper, which takes data series to create/update plottables |
|
266 | 338 | * @tparam T the data series' type |
|
267 | 339 | */ |
|
268 | 340 | template <typename T> |
|
269 |
struct PlottablesHelper : public IPlottablesHelper |
|
|
270 | explicit PlottablesHelper(std::shared_ptr<T> dataSeries) : m_DataSeries{dataSeries} {} | |
|
341 | struct PlottablesHelper : public IPlottablesHelper | |
|
342 | { | |
|
343 | explicit PlottablesHelper(T* dataSeries) : m_DataSeries { dataSeries } {} | |
|
271 | 344 | |
|
272 |
PlottablesMap create(QCustomPlot |
|
|
345 | PlottablesMap create(QCustomPlot& plot) const override | |
|
273 | 346 | { |
|
274 | 347 | return PlottablesCreator<T>::createPlottables(plot); |
|
275 | 348 | } |
|
276 | 349 | |
|
277 | void update(PlottablesMap &plottables, const DateTimeRange &range, bool rescaleAxes) const override | |
|
350 | void update( | |
|
351 | PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes) const override | |
|
278 | 352 | { |
|
279 |
if (m_DataSeries) |
|
|
353 | if (m_DataSeries) | |
|
354 | { | |
|
280 | 355 | PlottablesUpdater<T>::updatePlottables(*m_DataSeries, plottables, range, rescaleAxes); |
|
281 | 356 | } |
|
282 |
else |
|
|
357 | else | |
|
358 | { | |
|
283 | 359 | qCCritical(LOG_VisualizationGraphHelper()) << "Can't update plottables: inconsistency " |
|
284 | 360 | "between the type of data series and the " |
|
285 | 361 | "type supposed"; |
|
286 | 362 | } |
|
287 | 363 | } |
|
288 | 364 | |
|
289 |
void setYAxisRange(const DateTimeRange |
|
|
365 | void setYAxisRange(const DateTimeRange& xAxisRange, QCustomPlot& plot) const override | |
|
290 | 366 | { |
|
291 |
if (m_DataSeries) |
|
|
367 | if (m_DataSeries) | |
|
368 | { | |
|
292 | 369 | PlottablesUpdater<T>::setPlotYAxisRange(*m_DataSeries, xAxisRange, plot); |
|
293 | 370 | } |
|
294 |
else |
|
|
371 | else | |
|
372 | { | |
|
295 | 373 | qCCritical(LOG_VisualizationGraphHelper()) << "Can't update plottables: inconsistency " |
|
296 | 374 | "between the type of data series and the " |
|
297 | 375 | "type supposed"; |
|
298 | 376 | } |
|
299 | 377 | } |
|
300 | 378 | |
|
301 |
|
|
|
379 | T* m_DataSeries; | |
|
302 | 380 | }; |
|
303 | 381 | |
|
304 | 382 | /// Creates IPlottablesHelper according to the type of data series a variable holds |
|
305 | std::unique_ptr<IPlottablesHelper> createHelper(std::shared_ptr<Variable> variable) noexcept | |
|
383 | std::unique_ptr<IPlottablesHelper> createHelper(std::shared_ptr<Variable2> variable) noexcept | |
|
306 | 384 | { |
|
307 |
switch (variable->type()) |
|
|
385 | switch (variable->type()) | |
|
386 | { | |
|
308 | 387 | case DataSeriesType::SCALAR: |
|
309 |
return std::make_unique<PlottablesHelper<ScalarSerie |
|
|
310 |
|
|
|
388 | return std::make_unique<PlottablesHelper<ScalarTimeSerie>>( | |
|
389 | dynamic_cast<ScalarTimeSerie*>(variable->data()->base())); | |
|
311 | 390 | case DataSeriesType::SPECTROGRAM: |
|
312 |
return std::make_unique<PlottablesHelper<SpectrogramSerie |
|
|
313 |
|
|
|
391 | return std::make_unique<PlottablesHelper<SpectrogramTimeSerie>>( | |
|
392 | dynamic_cast<SpectrogramTimeSerie*>(variable->data()->base())); | |
|
314 | 393 | case DataSeriesType::VECTOR: |
|
315 |
return std::make_unique<PlottablesHelper<VectorSerie |
|
|
316 |
|
|
|
394 | return std::make_unique<PlottablesHelper<VectorTimeSerie>>( | |
|
395 | dynamic_cast<VectorTimeSerie*>(variable->data()->base())); | |
|
317 | 396 | default: |
|
318 | 397 | // Creates default helper |
|
319 | 398 | break; |
|
320 | 399 | } |
|
321 | 400 | |
|
322 |
return std::make_unique<PlottablesHelper< |
|
|
401 | return std::make_unique<PlottablesHelper<TimeSeries::ITimeSerie>>(nullptr); | |
|
323 | 402 | } |
|
324 | 403 | |
|
325 | 404 | } // namespace |
|
326 | 405 | |
|
327 |
PlottablesMap VisualizationGraphHelper::create( |
|
|
328 | QCustomPlot &plot) noexcept | |
|
406 | PlottablesMap VisualizationGraphHelper::create( | |
|
407 | std::shared_ptr<Variable2> variable, QCustomPlot& plot) noexcept | |
|
329 | 408 | { |
|
330 |
if (variable) |
|
|
409 | if (variable) | |
|
410 | { | |
|
331 | 411 | auto helper = createHelper(variable); |
|
332 | 412 | auto plottables = helper->create(plot); |
|
333 | 413 | return plottables; |
|
334 | 414 | } |
|
335 |
else |
|
|
415 | else | |
|
416 | { | |
|
336 | 417 | qCDebug(LOG_VisualizationGraphHelper()) |
|
337 | 418 | << QObject::tr("Can't create graph plottables : the variable is null"); |
|
338 | return PlottablesMap{}; | |
|
419 | return PlottablesMap {}; | |
|
339 | 420 | } |
|
340 | 421 | } |
|
341 | 422 | |
|
342 |
void VisualizationGraphHelper::setYAxisRange( |
|
|
343 | QCustomPlot &plot) noexcept | |
|
423 | void VisualizationGraphHelper::setYAxisRange( | |
|
424 | std::shared_ptr<Variable2> variable, QCustomPlot& plot) noexcept | |
|
344 | 425 | { |
|
345 |
if (variable) |
|
|
426 | if (variable) | |
|
427 | { | |
|
346 | 428 | auto helper = createHelper(variable); |
|
347 | 429 | helper->setYAxisRange(variable->range(), plot); |
|
348 | 430 | } |
|
349 |
else |
|
|
431 | else | |
|
432 | { | |
|
350 | 433 | qCDebug(LOG_VisualizationGraphHelper()) |
|
351 | 434 | << QObject::tr("Can't set y-axis range of plot: the variable is null"); |
|
352 | 435 | } |
|
353 | 436 | } |
|
354 | 437 | |
|
355 |
void VisualizationGraphHelper::updateData( |
|
|
356 | std::shared_ptr<Variable> variable, | |
|
357 | const DateTimeRange &dateTime) | |
|
438 | void VisualizationGraphHelper::updateData( | |
|
439 | PlottablesMap& plottables, std::shared_ptr<Variable2> variable, const DateTimeRange& dateTime) | |
|
358 | 440 | { |
|
359 | 441 | auto helper = createHelper(variable); |
|
360 | 442 | helper->update(plottables, dateTime); |
|
361 | 443 | } |
@@ -1,338 +1,352 | |||
|
1 | 1 | #include "Visualization/VisualizationGraphRenderingDelegate.h" |
|
2 | 2 | #include "Visualization/AxisRenderingUtils.h" |
|
3 | 3 | #include "Visualization/ColorScaleEditor.h" |
|
4 | 4 | #include "Visualization/PlottablesRenderingUtils.h" |
|
5 | 5 | #include "Visualization/SqpColorScale.h" |
|
6 | 6 | #include "Visualization/VisualizationGraphWidget.h" |
|
7 | 7 | #include "Visualization/qcustomplot.h" |
|
8 | 8 | |
|
9 | 9 | #include <Common/DateUtils.h> |
|
10 | 10 | |
|
11 | 11 | #include <Data/IDataSeries.h> |
|
12 | 12 | #include <Variable/Variable.h> |
|
13 | 13 | |
|
14 | 14 | #include <SqpApplication.h> |
|
15 | 15 | |
|
16 |
namespace |
|
|
16 | namespace | |
|
17 | { | |
|
17 | 18 | |
|
18 | 19 | /// Name of the axes layer in QCustomPlot |
|
19 | 20 | const auto AXES_LAYER = QStringLiteral("axes"); |
|
20 | 21 | |
|
21 | 22 | /// Icon used to show x-axis properties |
|
22 | 23 | const auto HIDE_AXIS_ICON_PATH = QStringLiteral(":/icones/down.png"); |
|
23 | 24 | |
|
24 | 25 | /// Name of the overlay layer in QCustomPlot |
|
25 | 26 | const auto OVERLAY_LAYER = QStringLiteral("overlay"); |
|
26 | 27 | |
|
27 | 28 | /// Pixmap used to show x-axis properties |
|
28 | 29 | const auto SHOW_AXIS_ICON_PATH = QStringLiteral(":/icones/up.png"); |
|
29 | 30 | |
|
30 | 31 | /// Tooltip format for graphs |
|
31 | 32 | const auto GRAPH_TOOLTIP_FORMAT = QStringLiteral("key: %1\nvalue: %2"); |
|
32 | 33 | |
|
33 | 34 | /// Tooltip format for colormaps |
|
34 | 35 | const auto COLORMAP_TOOLTIP_FORMAT = QStringLiteral("x: %1\ny: %2\nvalue: %3"); |
|
35 | 36 | |
|
36 | 37 | /// Offset used to shift the tooltip of the mouse |
|
37 | const auto TOOLTIP_OFFSET = QPoint{20, 20}; | |
|
38 | const auto TOOLTIP_OFFSET = QPoint { 20, 20 }; | |
|
38 | 39 | |
|
39 | 40 | /// Tooltip display rectangle (the tooltip is hidden when the mouse leaves this rectangle) |
|
40 | const auto TOOLTIP_RECT = QRect{10, 10, 10, 10}; | |
|
41 | const auto TOOLTIP_RECT = QRect { 10, 10, 10, 10 }; | |
|
41 | 42 | |
|
42 | 43 | /// Timeout after which the tooltip is displayed |
|
43 | 44 | const auto TOOLTIP_TIMEOUT = 500; |
|
44 | 45 | |
|
45 |
void initPointTracerStyle(QCPItemTracer |
|
|
46 | void initPointTracerStyle(QCPItemTracer& tracer) noexcept | |
|
46 | 47 | { |
|
47 | 48 | tracer.setInterpolating(false); |
|
48 | 49 | tracer.setStyle(QCPItemTracer::tsCircle); |
|
49 | 50 | tracer.setSize(3); |
|
50 | 51 | tracer.setPen(QPen(Qt::black)); |
|
51 | 52 | tracer.setBrush(Qt::black); |
|
52 | 53 | tracer.setSelectable(false); |
|
53 | 54 | } |
|
54 | 55 | |
|
55 |
QPixmap pixmap(const QString |
|
|
56 | QPixmap pixmap(const QString& iconPath) noexcept | |
|
56 | 57 | { |
|
57 | return QIcon{iconPath}.pixmap(QSize{16, 16}); | |
|
58 | return QIcon { iconPath }.pixmap(QSize { 16, 16 }); | |
|
58 | 59 | } |
|
59 | 60 | |
|
60 |
void initClosePixmapStyle(QCPItemPixmap |
|
|
61 | void initClosePixmapStyle(QCPItemPixmap& pixmap) noexcept | |
|
61 | 62 | { |
|
62 | 63 | // Icon |
|
63 | 64 | pixmap.setPixmap( |
|
64 | sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton).pixmap(QSize{16, 16})); | |
|
65 | sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton).pixmap(QSize { 16, 16 })); | |
|
65 | 66 | |
|
66 | 67 | // Position |
|
67 | 68 | pixmap.topLeft->setType(QCPItemPosition::ptAxisRectRatio); |
|
68 | 69 | pixmap.topLeft->setCoords(1, 0); |
|
69 | 70 | pixmap.setClipToAxisRect(false); |
|
70 | 71 | |
|
71 | 72 | // Can be selected |
|
72 | 73 | pixmap.setSelectable(true); |
|
73 | 74 | } |
|
74 | 75 | |
|
75 |
void initXAxisPixmapStyle(QCPItemPixmap |
|
|
76 | void initXAxisPixmapStyle(QCPItemPixmap& itemPixmap) noexcept | |
|
76 | 77 | { |
|
77 | 78 | // Icon |
|
78 | 79 | itemPixmap.setPixmap(pixmap(HIDE_AXIS_ICON_PATH)); |
|
79 | 80 | |
|
80 | 81 | // Position |
|
81 | 82 | itemPixmap.topLeft->setType(QCPItemPosition::ptAxisRectRatio); |
|
82 | 83 | itemPixmap.topLeft->setCoords(0, 1); |
|
83 | 84 | itemPixmap.setClipToAxisRect(false); |
|
84 | 85 | |
|
85 | 86 | // Can be selected |
|
86 | 87 | itemPixmap.setSelectable(true); |
|
87 | 88 | } |
|
88 | 89 | |
|
89 |
void initTitleTextStyle(QCPItemText |
|
|
90 | void initTitleTextStyle(QCPItemText& text) noexcept | |
|
90 | 91 | { |
|
91 | 92 | // Font and background styles |
|
92 | 93 | text.setColor(Qt::gray); |
|
93 | 94 | text.setBrush(Qt::white); |
|
94 | 95 | |
|
95 | 96 | // Position |
|
96 | 97 | text.setPositionAlignment(Qt::AlignTop | Qt::AlignLeft); |
|
97 | 98 | text.position->setType(QCPItemPosition::ptAxisRectRatio); |
|
98 | 99 | text.position->setCoords(0.5, 0); |
|
99 | 100 | text.setSelectable(false); |
|
100 | 101 | } |
|
101 | 102 | |
|
102 | 103 | /** |
|
103 | 104 | * Returns the cell index (x or y) of a colormap according to the coordinate passed in parameter. |
|
104 | 105 | * This method handles the fact that a colormap axis can be logarithmic or linear. |
|
105 | 106 | * @param colormap the colormap for which to calculate the index |
|
106 | 107 | * @param coord the coord to convert to cell index |
|
107 | 108 | * @param xCoord calculates the x index if true, calculates y index if false |
|
108 | 109 | * @return the cell index |
|
109 | 110 | */ |
|
110 |
int colorMapCellIndex(const QCPColorMap |
|
|
111 | int colorMapCellIndex(const QCPColorMap& colormap, double coord, bool xCoord) | |
|
111 | 112 | { |
|
112 | 113 | // Determines the axis of the colormap according to xCoord, and whether it is logarithmic or not |
|
113 | 114 | auto isLogarithmic = (xCoord ? colormap.keyAxis() : colormap.valueAxis())->scaleType() |
|
114 |
|
|
|
115 | == QCPAxis::stLogarithmic; | |
|
115 | 116 | |
|
116 |
if (isLogarithmic) |
|
|
117 | if (isLogarithmic) | |
|
118 | { | |
|
117 | 119 | // For a logarithmic axis we can't use the conversion method of colormap, so we calculate |
|
118 | 120 | // the index manually based on the position of the coordinate on the axis |
|
119 | 121 | |
|
120 | 122 | // Gets the axis range and the number of values between range bounds to calculate the step |
|
121 | 123 | // between each value of the range |
|
122 | 124 | auto range = xCoord ? colormap.data()->keyRange() : colormap.data()->valueRange(); |
|
123 | 125 | auto nbValues = (xCoord ? colormap.data()->keySize() : colormap.data()->valueSize()) - 1; |
|
124 | 126 | auto valueStep |
|
125 | 127 | = (std::log10(range.upper) - std::log10(range.lower)) / static_cast<double>(nbValues); |
|
126 | 128 | |
|
127 | 129 | // According to the coord position, calculates the closest index in the range |
|
128 | 130 | return std::round((std::log10(coord) - std::log10(range.lower)) / valueStep); |
|
129 | 131 | } |
|
130 |
else |
|
|
132 | else | |
|
133 | { | |
|
131 | 134 | // For a linear axis, we use the conversion method of colormap |
|
132 | 135 | int index; |
|
133 |
if (xCoord) |
|
|
136 | if (xCoord) | |
|
137 | { | |
|
134 | 138 | colormap.data()->coordToCell(coord, 0., &index, nullptr); |
|
135 | 139 | } |
|
136 |
else |
|
|
140 | else | |
|
141 | { | |
|
137 | 142 | colormap.data()->coordToCell(0., coord, nullptr, &index); |
|
138 | 143 | } |
|
139 | 144 | |
|
140 | 145 | return index; |
|
141 | 146 | } |
|
142 | 147 | } |
|
143 | 148 | |
|
144 | 149 | } // namespace |
|
145 | 150 | |
|
146 |
struct VisualizationGraphRenderingDelegate::VisualizationGraphRenderingDelegatePrivate |
|
|
147 | explicit VisualizationGraphRenderingDelegatePrivate(VisualizationGraphWidget &graphWidget) | |
|
148 | : m_Plot{graphWidget.plot()}, | |
|
149 | m_PointTracer{new QCPItemTracer{&m_Plot}}, | |
|
150 | m_TracerTimer{}, | |
|
151 | m_ClosePixmap{new QCPItemPixmap{&m_Plot}}, | |
|
152 |
|
|
|
153 |
|
|
|
154 | m_ShowXAxis{true}, | |
|
155 |
|
|
|
156 | m_ColorScale{SqpColorScale{m_Plot}} | |
|
151 | struct VisualizationGraphRenderingDelegate::VisualizationGraphRenderingDelegatePrivate | |
|
152 | { | |
|
153 | explicit VisualizationGraphRenderingDelegatePrivate(VisualizationGraphWidget& graphWidget) | |
|
154 | : m_Plot { graphWidget.plot() } | |
|
155 | , m_PointTracer { new QCPItemTracer { &m_Plot } } | |
|
156 | , m_TracerTimer {} | |
|
157 | , m_ClosePixmap { new QCPItemPixmap { &m_Plot } } | |
|
158 | , m_TitleText { new QCPItemText { &m_Plot } } | |
|
159 | , m_XAxisPixmap { new QCPItemPixmap { &m_Plot } } | |
|
160 | , m_ShowXAxis { true } | |
|
161 | , m_XAxisLabel {} | |
|
162 | , m_ColorScale { SqpColorScale { m_Plot } } | |
|
157 | 163 | { |
|
158 | 164 | initPointTracerStyle(*m_PointTracer); |
|
159 | 165 | |
|
160 | 166 | m_TracerTimer.setInterval(TOOLTIP_TIMEOUT); |
|
161 | 167 | m_TracerTimer.setSingleShot(true); |
|
162 | 168 | |
|
163 | 169 | // Inits "close button" in plot overlay |
|
164 | 170 | m_ClosePixmap->setLayer(OVERLAY_LAYER); |
|
165 | 171 | initClosePixmapStyle(*m_ClosePixmap); |
|
166 | 172 | |
|
167 | 173 | // Connects pixmap selection to graph widget closing |
|
168 |
QObject::connect( |
|
|
169 |
|
|
|
170 |
|
|
|
171 | graphWidget.close(); | |
|
172 | } | |
|
173 |
|
|
|
174 | QObject::connect( | |
|
175 | &m_Plot, &QCustomPlot::itemClick, [&graphWidget, this](auto item, auto mouseEvent) { | |
|
176 | if (item == m_ClosePixmap) | |
|
177 | { | |
|
178 | graphWidget.close(); | |
|
179 | } | |
|
180 | }); | |
|
174 | 181 | |
|
175 | 182 | // Inits graph name in plot overlay |
|
176 | 183 | m_TitleText->setLayer(OVERLAY_LAYER); |
|
177 | 184 | m_TitleText->setText(graphWidget.name()); |
|
178 | 185 | initTitleTextStyle(*m_TitleText); |
|
179 | 186 | |
|
180 | 187 | // Inits "show x-axis button" in plot overlay |
|
181 | 188 | m_XAxisPixmap->setLayer(OVERLAY_LAYER); |
|
182 | 189 | initXAxisPixmapStyle(*m_XAxisPixmap); |
|
183 | 190 | |
|
184 | 191 | // Connects pixmap selection to graph x-axis showing/hiding |
|
185 | 192 | QObject::connect(&m_Plot, &QCustomPlot::itemClick, [this](auto item, auto mouseEvent) { |
|
186 |
if (m_XAxisPixmap == item) |
|
|
193 | if (m_XAxisPixmap == item) | |
|
194 | { | |
|
187 | 195 | // Changes the selection state and refreshes the x-axis |
|
188 | 196 | m_ShowXAxis = !m_ShowXAxis; |
|
189 | 197 | this->updateXAxisState(); |
|
190 | 198 | m_Plot.layer(AXES_LAYER)->replot(); |
|
191 | 199 | |
|
192 | 200 | // Deselects the x-axis pixmap and updates icon |
|
193 | 201 | m_XAxisPixmap->setPixmap( |
|
194 | 202 | pixmap(m_ShowXAxis ? HIDE_AXIS_ICON_PATH : SHOW_AXIS_ICON_PATH)); |
|
195 | 203 | m_Plot.layer(OVERLAY_LAYER)->replot(); |
|
196 | 204 | } |
|
197 | 205 | }); |
|
198 | 206 | } |
|
199 | 207 | |
|
200 | 208 | /// Updates state of x-axis according to the current selection of x-axis pixmap |
|
201 | 209 | /// @remarks the method doesn't call plot refresh |
|
202 | 210 | void updateXAxisState() noexcept |
|
203 | 211 | { |
|
204 | 212 | m_Plot.xAxis->setTickLabels(m_ShowXAxis); |
|
205 | m_Plot.xAxis->setLabel(m_ShowXAxis ? m_XAxisLabel : QString{}); | |
|
213 | m_Plot.xAxis->setLabel(m_ShowXAxis ? m_XAxisLabel : QString {}); | |
|
206 | 214 | } |
|
207 | 215 | |
|
208 |
QCustomPlot |
|
|
209 |
QCPItemTracer |
|
|
216 | QCustomPlot& m_Plot; | |
|
217 | QCPItemTracer* m_PointTracer; | |
|
210 | 218 | QTimer m_TracerTimer; |
|
211 |
QCPItemPixmap |
|
|
212 |
QCPItemText |
|
|
213 |
QCPItemPixmap |
|
|
219 | QCPItemPixmap* m_ClosePixmap; /// Graph's close button | |
|
220 | QCPItemText* m_TitleText; /// Graph's title | |
|
221 | QCPItemPixmap* m_XAxisPixmap; | |
|
214 | 222 | bool m_ShowXAxis; /// X-axis properties are shown or hidden |
|
215 | 223 | QString m_XAxisLabel; |
|
216 | 224 | SqpColorScale m_ColorScale; /// Color scale used for some types of graphs (as spectrograms) |
|
217 | 225 | }; |
|
218 | 226 | |
|
219 | 227 | VisualizationGraphRenderingDelegate::VisualizationGraphRenderingDelegate( |
|
220 |
VisualizationGraphWidget |
|
|
221 | : impl{spimpl::make_unique_impl<VisualizationGraphRenderingDelegatePrivate>(graphWidget)} | |
|
228 | VisualizationGraphWidget& graphWidget) | |
|
229 | : impl { spimpl::make_unique_impl<VisualizationGraphRenderingDelegatePrivate>(graphWidget) } | |
|
222 | 230 | { |
|
223 | 231 | } |
|
224 | 232 | |
|
225 |
void VisualizationGraphRenderingDelegate::onMouseDoubleClick(QMouseEvent |
|
|
233 | void VisualizationGraphRenderingDelegate::onMouseDoubleClick(QMouseEvent* event) noexcept | |
|
226 | 234 | { |
|
227 | 235 | // Opens color scale editor if color scale is double clicked |
|
228 |
auto colorScale = static_cast<QCPColorScale |
|
|
229 |
if (impl->m_ColorScale.m_Scale == colorScale) |
|
|
230 | if (ColorScaleEditor{impl->m_ColorScale}.exec() == QDialog::Accepted) { | |
|
236 | auto colorScale = static_cast<QCPColorScale*>(impl->m_Plot.layoutElementAt(event->pos())); | |
|
237 | if (impl->m_ColorScale.m_Scale == colorScale) | |
|
238 | { | |
|
239 | if (ColorScaleEditor { impl->m_ColorScale }.exec() == QDialog::Accepted) | |
|
240 | { | |
|
231 | 241 | impl->m_Plot.replot(); |
|
232 | 242 | } |
|
233 | 243 | } |
|
234 | 244 | } |
|
235 | 245 | |
|
236 |
void VisualizationGraphRenderingDelegate::updateTooltip(QMouseEvent |
|
|
246 | void VisualizationGraphRenderingDelegate::updateTooltip(QMouseEvent* event) noexcept | |
|
237 | 247 | { |
|
238 | 248 | // Cancels pending refresh |
|
239 | 249 | impl->m_TracerTimer.disconnect(); |
|
240 | 250 | |
|
241 | 251 | // Reinits tracers |
|
242 | 252 | impl->m_PointTracer->setGraph(nullptr); |
|
243 | 253 | impl->m_PointTracer->setVisible(false); |
|
244 | 254 | impl->m_Plot.replot(); |
|
245 | 255 | |
|
246 | QString tooltip{}; | |
|
256 | QString tooltip {}; | |
|
247 | 257 | |
|
248 | 258 | // Gets the graph under the mouse position |
|
249 | 259 | auto eventPos = event->pos(); |
|
250 |
if (auto graph = qobject_cast<QCPGraph |
|
|
260 | if (auto graph = qobject_cast<QCPGraph*>(impl->m_Plot.plottableAt(eventPos))) | |
|
261 | { | |
|
251 | 262 | auto mouseKey = graph->keyAxis()->pixelToCoord(eventPos.x()); |
|
252 | 263 | auto graphData = graph->data(); |
|
253 | 264 | |
|
254 | 265 | // Gets the closest data point to the mouse |
|
255 | 266 | auto graphDataIt = graphData->findBegin(mouseKey); |
|
256 |
if (graphDataIt != graphData->constEnd()) |
|
|
267 | if (graphDataIt != graphData->constEnd()) | |
|
268 | { | |
|
257 | 269 | // Sets tooltip |
|
258 | 270 | auto key = formatValue(graphDataIt->key, *graph->keyAxis()); |
|
259 | 271 | auto value = formatValue(graphDataIt->value, *graph->valueAxis()); |
|
260 | 272 | tooltip = GRAPH_TOOLTIP_FORMAT.arg(key, value); |
|
261 | 273 | |
|
262 | 274 | // Displays point tracer |
|
263 | 275 | impl->m_PointTracer->setGraph(graph); |
|
264 | 276 | impl->m_PointTracer->setGraphKey(graphDataIt->key); |
|
265 | 277 | impl->m_PointTracer->setLayer( |
|
266 | 278 | impl->m_Plot.layer("main")); // Tracer is set on top of the plot's main layer |
|
267 | 279 | impl->m_PointTracer->setVisible(true); |
|
268 | 280 | impl->m_Plot.replot(); |
|
269 | 281 | } |
|
270 | 282 | } |
|
271 |
else if (auto colorMap = qobject_cast<QCPColorMap |
|
|
283 | else if (auto colorMap = qobject_cast<QCPColorMap*>(impl->m_Plot.plottableAt(eventPos))) | |
|
284 | { | |
|
272 | 285 | // Gets x and y coords |
|
273 | 286 | auto x = colorMap->keyAxis()->pixelToCoord(eventPos.x()); |
|
274 | 287 | auto y = colorMap->valueAxis()->pixelToCoord(eventPos.y()); |
|
275 | 288 | |
|
276 | 289 | // Calculates x and y cell indexes, and retrieves the underlying value |
|
277 | 290 | auto xCellIndex = colorMapCellIndex(*colorMap, x, true); |
|
278 | 291 | auto yCellIndex = colorMapCellIndex(*colorMap, y, false); |
|
279 | 292 | auto value = colorMap->data()->cell(xCellIndex, yCellIndex); |
|
280 | 293 | |
|
281 | 294 | // Sets tooltips |
|
282 | 295 | tooltip = COLORMAP_TOOLTIP_FORMAT.arg(formatValue(x, *colorMap->keyAxis()), |
|
283 |
|
|
|
284 |
|
|
|
296 | formatValue(y, *colorMap->valueAxis()), | |
|
297 | formatValue(value, *colorMap->colorScale()->axis())); | |
|
285 | 298 | } |
|
286 | 299 | |
|
287 |
if (!tooltip.isEmpty()) |
|
|
300 | if (!tooltip.isEmpty()) | |
|
301 | { | |
|
288 | 302 | // Starts timer to show tooltip after timeout |
|
289 | 303 | auto showTooltip = [tooltip, eventPos, this]() { |
|
290 | 304 | QToolTip::showText(impl->m_Plot.mapToGlobal(eventPos) + TOOLTIP_OFFSET, tooltip, |
|
291 |
|
|
|
305 | &impl->m_Plot, TOOLTIP_RECT); | |
|
292 | 306 | }; |
|
293 | 307 | |
|
294 | 308 | QObject::connect(&impl->m_TracerTimer, &QTimer::timeout, showTooltip); |
|
295 | 309 | impl->m_TracerTimer.start(); |
|
296 | 310 | } |
|
297 | 311 | } |
|
298 | 312 | |
|
299 | 313 | void VisualizationGraphRenderingDelegate::onPlotUpdated() noexcept |
|
300 | 314 | { |
|
301 | 315 | // Updates color scale bounds |
|
302 | 316 | impl->m_ColorScale.updateDataRange(); |
|
303 | 317 | impl->m_Plot.replot(); |
|
304 | 318 | } |
|
305 | 319 | |
|
306 |
void VisualizationGraphRenderingDelegate::setAxesUnits( |
|
|
320 | void VisualizationGraphRenderingDelegate::setAxesUnits(Variable2& variable) noexcept | |
|
307 | 321 | { |
|
308 | 322 | |
|
309 | 323 | auto axisHelper = IAxisHelperFactory::create(variable); |
|
310 | 324 | axisHelper->setUnits(impl->m_Plot, impl->m_ColorScale); |
|
311 | 325 | |
|
312 | 326 | // Stores x-axis label to be able to retrieve it when x-axis pixmap is unselected |
|
313 | 327 | impl->m_XAxisLabel = impl->m_Plot.xAxis->label(); |
|
314 | 328 | |
|
315 | 329 | // Updates x-axis state |
|
316 | 330 | impl->updateXAxisState(); |
|
317 | 331 | |
|
318 | 332 | impl->m_Plot.layer(AXES_LAYER)->replot(); |
|
319 | 333 | } |
|
320 | 334 | |
|
321 |
void VisualizationGraphRenderingDelegate::setGraphProperties( |
|
|
322 | PlottablesMap &plottables) noexcept | |
|
335 | void VisualizationGraphRenderingDelegate::setGraphProperties( | |
|
336 | Variable2& variable, PlottablesMap& plottables) noexcept | |
|
323 | 337 | { |
|
324 | 338 | // Axes' properties |
|
325 | 339 | auto axisHelper = IAxisHelperFactory::create(variable); |
|
326 | 340 | axisHelper->setProperties(impl->m_Plot, impl->m_ColorScale); |
|
327 | 341 | |
|
328 | 342 | // Plottables' properties |
|
329 | 343 | auto plottablesHelper = IPlottablesHelperFactory::create(variable); |
|
330 | 344 | plottablesHelper->setProperties(plottables); |
|
331 | 345 | } |
|
332 | 346 | |
|
333 | 347 | void VisualizationGraphRenderingDelegate::showGraphOverlay(bool show) noexcept |
|
334 | 348 | { |
|
335 | 349 | auto overlay = impl->m_Plot.layer(OVERLAY_LAYER); |
|
336 | 350 | overlay->setVisible(show); |
|
337 | 351 | overlay->replot(); |
|
338 | 352 | } |
This diff has been collapsed as it changes many lines, (787 lines changed) Show them Hide them | |||
@@ -1,1467 +1,1580 | |||
|
1 | 1 | #include "Visualization/VisualizationGraphWidget.h" |
|
2 | 2 | #include "Visualization/IVisualizationWidgetVisitor.h" |
|
3 | 3 | #include "Visualization/VisualizationCursorItem.h" |
|
4 | 4 | #include "Visualization/VisualizationDefs.h" |
|
5 | 5 | #include "Visualization/VisualizationGraphHelper.h" |
|
6 | 6 | #include "Visualization/VisualizationGraphRenderingDelegate.h" |
|
7 | 7 | #include "Visualization/VisualizationMultiZoneSelectionDialog.h" |
|
8 | 8 | #include "Visualization/VisualizationSelectionZoneItem.h" |
|
9 | 9 | #include "Visualization/VisualizationSelectionZoneManager.h" |
|
10 | 10 | #include "Visualization/VisualizationWidget.h" |
|
11 | 11 | #include "Visualization/VisualizationZoneWidget.h" |
|
12 | 12 | #include "ui_VisualizationGraphWidget.h" |
|
13 | 13 | |
|
14 | 14 | #include <Actions/ActionsGuiController.h> |
|
15 | 15 | #include <Actions/FilteringAction.h> |
|
16 | 16 | #include <Common/MimeTypesDef.h> |
|
17 | 17 | #include <Common/containers.h> |
|
18 | 18 | #include <Data/ArrayData.h> |
|
19 | #include <Data/DateTimeRangeHelper.h> | |
|
19 | 20 | #include <Data/IDataSeries.h> |
|
20 | 21 | #include <Data/SpectrogramSeries.h> |
|
21 | 22 | #include <DragAndDrop/DragDropGuiController.h> |
|
22 | 23 | #include <Settings/SqpSettingsDefs.h> |
|
23 | 24 | #include <SqpApplication.h> |
|
24 | 25 | #include <Time/TimeController.h> |
|
25 | #include <Variable/Variable.h> | |
|
26 | #include <Variable/Variable2.h> | |
|
26 | 27 | #include <Variable/VariableController2.h> |
|
27 | #include <Data/DateTimeRangeHelper.h> | |
|
28 | 28 | |
|
29 | 29 | #include <unordered_map> |
|
30 | 30 | |
|
31 | 31 | Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget") |
|
32 | 32 | |
|
33 |
namespace |
|
|
33 | namespace | |
|
34 | { | |
|
34 | 35 | |
|
35 | 36 | /// Key pressed to enable drag&drop in all modes |
|
36 | 37 | const auto DRAG_DROP_MODIFIER = Qt::AltModifier; |
|
37 | 38 | |
|
38 | 39 | /// Key pressed to enable zoom on horizontal axis |
|
39 | 40 | const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier; |
|
40 | 41 | |
|
41 | 42 | /// Key pressed to enable zoom on vertical axis |
|
42 | 43 | const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier; |
|
43 | 44 | |
|
44 | 45 | /// Speed of a step of a wheel event for a pan, in percentage of the axis range |
|
45 | 46 | const auto PAN_SPEED = 5; |
|
46 | 47 | |
|
47 | 48 | /// Key pressed to enable a calibration pan |
|
48 | 49 | const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier; |
|
49 | 50 | |
|
50 | 51 | /// Key pressed to enable multi selection of selection zones |
|
51 | 52 | const auto MULTI_ZONE_SELECTION_MODIFIER = Qt::ControlModifier; |
|
52 | 53 | |
|
53 | 54 | /// Minimum size for the zoom box, in percentage of the axis range |
|
54 | 55 | const auto ZOOM_BOX_MIN_SIZE = 0.8; |
|
55 | 56 | |
|
56 | 57 | /// Format of the dates appearing in the label of a cursor |
|
57 | 58 | const auto CURSOR_LABELS_DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd\nhh:mm:ss:zzz"); |
|
58 | 59 | |
|
59 | 60 | } // namespace |
|
60 | 61 | |
|
61 |
struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate |
|
|
62 | struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate | |
|
63 | { | |
|
62 | 64 | |
|
63 |
explicit VisualizationGraphWidgetPrivate(const QString |
|
|
64 |
: m_Name{name} |
|
|
65 |
|
|
|
66 |
|
|
|
67 |
|
|
|
65 | explicit VisualizationGraphWidgetPrivate(const QString& name) | |
|
66 | : m_Name { name } | |
|
67 | , m_Flags { GraphFlag::EnableAll } | |
|
68 | , m_IsCalibration { false } | |
|
69 | , m_RenderingDelegate { nullptr } | |
|
68 | 70 | { |
|
69 | 71 | m_plot = new QCustomPlot(); |
|
70 | 72 | // Necessary for all platform since Qt::AA_EnableHighDpiScaling is enable. |
|
71 | 73 | m_plot->setPlottingHint(QCP::phFastPolylines, true); |
|
72 | 74 | } |
|
73 | 75 | |
|
74 | void updateData(PlottablesMap &plottables, std::shared_ptr<Variable> variable, | |
|
75 | const DateTimeRange &range) | |
|
76 | void updateData( | |
|
77 | PlottablesMap& plottables, std::shared_ptr<Variable2> variable, const DateTimeRange& range) | |
|
76 | 78 | { |
|
77 | 79 | VisualizationGraphHelper::updateData(plottables, variable, range); |
|
78 | 80 | |
|
79 | 81 | // Prevents that data has changed to update rendering |
|
80 | 82 | m_RenderingDelegate->onPlotUpdated(); |
|
81 | 83 | } |
|
82 | 84 | |
|
83 | 85 | QString m_Name; |
|
84 | 86 | // 1 variable -> n qcpplot |
|
85 | std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap; | |
|
87 | std::map<std::shared_ptr<Variable2>, PlottablesMap> m_VariableToPlotMultiMap; | |
|
86 | 88 | GraphFlags m_Flags; |
|
87 | 89 | bool m_IsCalibration; |
|
88 | 90 | QCustomPlot* m_plot; |
|
89 | 91 | QPoint m_lastMousePos; |
|
90 | 92 | QCPRange m_lastXRange; |
|
91 | 93 | QCPRange m_lastYRange; |
|
92 | 94 | /// Delegate used to attach rendering features to the plot |
|
93 | 95 | std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate; |
|
94 | 96 | |
|
95 | 97 | QCPItemRect* m_DrawingZoomRect = nullptr; |
|
96 |
QStack<QPair<QCPRange, QCPRange> |
|
|
98 | QStack<QPair<QCPRange, QCPRange>> m_ZoomStack; | |
|
97 | 99 | |
|
98 | 100 | std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr; |
|
99 | 101 | std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr; |
|
100 | 102 | |
|
101 |
VisualizationSelectionZoneItem |
|
|
102 |
VisualizationSelectionZoneItem |
|
|
103 |
QVector<VisualizationSelectionZoneItem |
|
|
103 | VisualizationSelectionZoneItem* m_DrawingZone = nullptr; | |
|
104 | VisualizationSelectionZoneItem* m_HoveredZone = nullptr; | |
|
105 | QVector<VisualizationSelectionZoneItem*> m_SelectionZones; | |
|
104 | 106 | |
|
105 | 107 | bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even |
|
106 | 108 | |
|
107 | 109 | bool m_VariableAutoRangeOnInit = true; |
|
108 | 110 | |
|
109 | 111 | inline void enterPlotDrag(const QPoint& position) |
|
110 | 112 | { |
|
111 | 113 | m_lastMousePos = m_plot->mapFromParent(position); |
|
112 | 114 | m_lastXRange = m_plot->xAxis->range(); |
|
113 | 115 | m_lastYRange = m_plot->yAxis->range(); |
|
114 | ||
|
115 | 116 | } |
|
116 | 117 | |
|
117 | inline bool isDrawingZoomRect(){return m_DrawingZoomRect!=nullptr;} | |
|
118 | inline bool isDrawingZoomRect() { return m_DrawingZoomRect != nullptr; } | |
|
118 | 119 | void updateZoomRect(const QPoint& newPos) |
|
119 | 120 | { |
|
120 |
QPointF pos{m_plot->xAxis->pixelToCoord(newPos.x()), |
|
|
121 | QPointF pos { m_plot->xAxis->pixelToCoord(newPos.x()), | |
|
122 | m_plot->yAxis->pixelToCoord(newPos.y()) }; | |
|
121 | 123 | m_DrawingZoomRect->bottomRight->setCoords(pos); |
|
122 | 124 | m_plot->replot(QCustomPlot::rpQueuedReplot); |
|
123 | 125 | } |
|
124 | 126 | |
|
125 | 127 | void applyZoomRect() |
|
126 | 128 | { |
|
127 | 129 | auto axisX = m_plot->axisRect()->axis(QCPAxis::atBottom); |
|
128 | 130 | auto axisY = m_plot->axisRect()->axis(QCPAxis::atLeft); |
|
129 | 131 | |
|
130 | auto newAxisXRange = QCPRange{m_DrawingZoomRect->topLeft->coords().x(), | |
|
131 |
|
|
|
132 | auto newAxisXRange = QCPRange { m_DrawingZoomRect->topLeft->coords().x(), | |
|
133 | m_DrawingZoomRect->bottomRight->coords().x() }; | |
|
132 | 134 | |
|
133 | auto newAxisYRange = QCPRange{m_DrawingZoomRect->topLeft->coords().y(), | |
|
134 |
|
|
|
135 | auto newAxisYRange = QCPRange { m_DrawingZoomRect->topLeft->coords().y(), | |
|
136 | m_DrawingZoomRect->bottomRight->coords().y() }; | |
|
135 | 137 | |
|
136 | 138 | removeDrawingRect(); |
|
137 | 139 | |
|
138 | 140 | if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0) |
|
139 |
&& newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) |
|
|
141 | && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) | |
|
142 | { | |
|
140 | 143 | m_ZoomStack.push(qMakePair(axisX->range(), axisY->range())); |
|
141 | 144 | axisX->setRange(newAxisXRange); |
|
142 | 145 | axisY->setRange(newAxisYRange); |
|
143 | 146 | |
|
144 | 147 | m_plot->replot(QCustomPlot::rpQueuedReplot); |
|
145 | 148 | } |
|
146 | 149 | } |
|
147 | 150 | |
|
148 | inline bool isDrawingZoneRect(){return m_DrawingZone!=nullptr;} | |
|
151 | inline bool isDrawingZoneRect() { return m_DrawingZone != nullptr; } | |
|
149 | 152 | void updateZoneRect(const QPoint& newPos) |
|
150 | 153 | { |
|
151 | 154 | m_DrawingZone->setEnd(m_plot->xAxis->pixelToCoord(newPos.x())); |
|
152 | 155 | m_plot->replot(QCustomPlot::rpQueuedReplot); |
|
153 | 156 | } |
|
154 | 157 | |
|
155 |
void startDrawingRect(const QPoint |
|
|
158 | void startDrawingRect(const QPoint& pos) | |
|
156 | 159 | { |
|
157 | 160 | removeDrawingRect(); |
|
158 | 161 | |
|
159 | 162 | auto axisPos = posToAxisPos(pos); |
|
160 | 163 | |
|
161 | m_DrawingZoomRect = new QCPItemRect{m_plot}; | |
|
164 | m_DrawingZoomRect = new QCPItemRect { m_plot }; | |
|
162 | 165 | QPen p; |
|
163 | 166 | p.setWidth(2); |
|
164 | 167 | m_DrawingZoomRect->setPen(p); |
|
165 | 168 | |
|
166 | 169 | m_DrawingZoomRect->topLeft->setCoords(axisPos); |
|
167 | 170 | m_DrawingZoomRect->bottomRight->setCoords(axisPos); |
|
168 | 171 | } |
|
169 | 172 | |
|
170 | 173 | void removeDrawingRect() |
|
171 | 174 | { |
|
172 |
if (m_DrawingZoomRect) |
|
|
175 | if (m_DrawingZoomRect) | |
|
176 | { | |
|
173 | 177 | m_plot->removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot |
|
174 | 178 | m_DrawingZoomRect = nullptr; |
|
175 | 179 | m_plot->replot(QCustomPlot::rpQueuedReplot); |
|
176 | 180 | } |
|
177 | 181 | } |
|
178 | 182 | |
|
179 |
void selectZone(const QPoint |
|
|
183 | void selectZone(const QPoint& pos) | |
|
180 | 184 | { |
|
181 | 185 | auto zoneAtPos = selectionZoneAt(pos); |
|
182 | setSelectionZonesEditionEnabled(sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones); | |
|
186 | setSelectionZonesEditionEnabled( | |
|
187 | sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones); | |
|
183 | 188 | } |
|
184 | 189 | |
|
185 |
void startDrawingZone(const QPoint |
|
|
190 | void startDrawingZone(const QPoint& pos) | |
|
186 | 191 | { |
|
187 | 192 | endDrawingZone(); |
|
188 | 193 | |
|
189 | 194 | auto axisPos = posToAxisPos(pos); |
|
190 | 195 | |
|
191 | m_DrawingZone = new VisualizationSelectionZoneItem{m_plot}; | |
|
196 | m_DrawingZone = new VisualizationSelectionZoneItem { m_plot }; | |
|
192 | 197 | m_DrawingZone->setRange(axisPos.x(), axisPos.x()); |
|
193 | 198 | m_DrawingZone->setEditionEnabled(false); |
|
194 | 199 | } |
|
195 | 200 | |
|
196 | 201 | void endDrawingZone() |
|
197 | 202 | { |
|
198 |
if (m_DrawingZone) |
|
|
203 | if (m_DrawingZone) | |
|
204 | { | |
|
199 | 205 | auto drawingZoneRange = m_DrawingZone->range(); |
|
200 |
if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0) |
|
|
206 | if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0) | |
|
207 | { | |
|
201 | 208 | m_DrawingZone->setEditionEnabled(true); |
|
202 | 209 | addSelectionZone(m_DrawingZone); |
|
203 | 210 | } |
|
204 |
else |
|
|
211 | else | |
|
212 | { | |
|
205 | 213 | m_plot->removeItem(m_DrawingZone); |
|
206 | 214 | } |
|
207 | 215 | |
|
208 | 216 | m_plot->replot(QCustomPlot::rpQueuedReplot); |
|
209 | 217 | m_DrawingZone = nullptr; |
|
210 | 218 | } |
|
211 | 219 | } |
|
212 | 220 | |
|
213 | 221 | void moveSelectionZone(const QPoint& destination) |
|
214 | 222 | { |
|
215 | 223 | /* |
|
216 | 224 | * I give up on this for now |
|
217 | 225 | * @TODO implement this, the difficulty is that selection zones have their own |
|
218 | 226 | * event handling code which seems to rely on QCP GUI event handling propagation |
|
219 | 227 | * which was a realy bad design choice. |
|
220 | */ | |
|
228 | */ | |
|
221 | 229 | } |
|
222 | 230 | |
|
223 | 231 | void setSelectionZonesEditionEnabled(bool value) |
|
224 | 232 | { |
|
225 |
for (auto s : m_SelectionZones) |
|
|
233 | for (auto s : m_SelectionZones) | |
|
234 | { | |
|
226 | 235 | s->setEditionEnabled(value); |
|
227 | 236 | } |
|
228 | 237 | } |
|
229 | 238 | |
|
230 |
void addSelectionZone(VisualizationSelectionZoneItem |
|
|
239 | void addSelectionZone(VisualizationSelectionZoneItem* zone) { m_SelectionZones << zone; } | |
|
231 | 240 | |
|
232 |
VisualizationSelectionZoneItem |
|
|
241 | VisualizationSelectionZoneItem* selectionZoneAt(const QPoint& pos) const | |
|
233 | 242 | { |
|
234 |
VisualizationSelectionZoneItem |
|
|
243 | VisualizationSelectionZoneItem* selectionZoneItemUnderCursor = nullptr; | |
|
235 | 244 | auto minDistanceToZone = -1; |
|
236 |
for (auto zone : m_SelectionZones) |
|
|
245 | for (auto zone : m_SelectionZones) | |
|
246 | { | |
|
237 | 247 | auto distanceToZone = zone->selectTest(pos, false); |
|
238 | 248 | if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone) |
|
239 |
&& distanceToZone >= 0 && distanceToZone < m_plot->selectionTolerance()) |
|
|
249 | && distanceToZone >= 0 && distanceToZone < m_plot->selectionTolerance()) | |
|
250 | { | |
|
240 | 251 | selectionZoneItemUnderCursor = zone; |
|
241 | 252 | } |
|
242 | 253 | } |
|
243 | 254 | |
|
244 | 255 | return selectionZoneItemUnderCursor; |
|
245 | 256 | } |
|
246 | 257 | |
|
247 |
QVector<VisualizationSelectionZoneItem |
|
|
248 | const QCustomPlot &plot) const | |
|
258 | QVector<VisualizationSelectionZoneItem*> selectionZonesAt( | |
|
259 | const QPoint& pos, const QCustomPlot& plot) const | |
|
249 | 260 | { |
|
250 |
QVector<VisualizationSelectionZoneItem |
|
|
251 |
for (auto zone : m_SelectionZones) |
|
|
261 | QVector<VisualizationSelectionZoneItem*> zones; | |
|
262 | for (auto zone : m_SelectionZones) | |
|
263 | { | |
|
252 | 264 | auto distanceToZone = zone->selectTest(pos, false); |
|
253 |
if (distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) |
|
|
265 | if (distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) | |
|
266 | { | |
|
254 | 267 | zones << zone; |
|
255 | 268 | } |
|
256 | 269 | } |
|
257 | 270 | |
|
258 | 271 | return zones; |
|
259 | 272 | } |
|
260 | 273 | |
|
261 |
void moveSelectionZoneOnTop(VisualizationSelectionZoneItem |
|
|
274 | void moveSelectionZoneOnTop(VisualizationSelectionZoneItem* zone, QCustomPlot& plot) | |
|
262 | 275 | { |
|
263 |
if (!m_SelectionZones.isEmpty() && m_SelectionZones.last() != zone) |
|
|
276 | if (!m_SelectionZones.isEmpty() && m_SelectionZones.last() != zone) | |
|
277 | { | |
|
264 | 278 | zone->moveToTop(); |
|
265 | 279 | m_SelectionZones.removeAll(zone); |
|
266 | 280 | m_SelectionZones.append(zone); |
|
267 | 281 | } |
|
268 | 282 | } |
|
269 | 283 | |
|
270 |
QPointF posToAxisPos(const QPoint |
|
|
284 | QPointF posToAxisPos(const QPoint& pos) const | |
|
271 | 285 | { |
|
272 | 286 | auto axisX = m_plot->axisRect()->axis(QCPAxis::atBottom); |
|
273 | 287 | auto axisY = m_plot->axisRect()->axis(QCPAxis::atLeft); |
|
274 | return QPointF{axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y())}; | |
|
288 | return QPointF { axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y()) }; | |
|
275 | 289 | } |
|
276 | 290 | |
|
277 |
bool pointIsInAxisRect(const QPointF |
|
|
291 | bool pointIsInAxisRect(const QPointF& axisPoint, QCustomPlot& plot) const | |
|
278 | 292 | { |
|
279 | 293 | auto axisX = plot.axisRect()->axis(QCPAxis::atBottom); |
|
280 | 294 | auto axisY = plot.axisRect()->axis(QCPAxis::atLeft); |
|
281 | 295 | return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y()); |
|
282 | 296 | } |
|
283 | 297 | |
|
284 |
inline QCPRange _pixDistanceToRange(double pos1, double pos2, QCPAxis |
|
|
298 | inline QCPRange _pixDistanceToRange(double pos1, double pos2, QCPAxis* axis) | |
|
285 | 299 | { |
|
286 | 300 | if (axis->scaleType() == QCPAxis::stLinear) |
|
287 | 301 | { |
|
288 | 302 | auto diff = axis->pixelToCoord(pos1) - axis->pixelToCoord(pos2); |
|
289 | return QCPRange{axis->range().lower + diff, axis->range().upper + diff}; | |
|
303 | return QCPRange { axis->range().lower + diff, axis->range().upper + diff }; | |
|
290 | 304 | } |
|
291 | 305 | else |
|
292 | 306 | { |
|
293 | 307 | auto diff = axis->pixelToCoord(pos1) / axis->pixelToCoord(pos2); |
|
294 | return QCPRange{axis->range().lower * diff, axis->range().upper * diff}; | |
|
308 | return QCPRange { axis->range().lower * diff, axis->range().upper * diff }; | |
|
295 | 309 | } |
|
296 | 310 | } |
|
297 | 311 | |
|
298 |
void setRange(const DateTimeRange |
|
|
312 | void setRange(const DateTimeRange& newRange, bool updateVar = true) | |
|
299 | 313 | { |
|
300 | 314 | this->m_plot->xAxis->setRange(newRange.m_TStart, newRange.m_TEnd); |
|
301 | if(updateVar) | |
|
315 | if (updateVar) | |
|
302 | 316 | { |
|
303 | for (auto it = m_VariableToPlotMultiMap.begin(), | |
|
304 | end = m_VariableToPlotMultiMap.end(); | |
|
317 | for (auto it = m_VariableToPlotMultiMap.begin(), end = m_VariableToPlotMultiMap.end(); | |
|
305 | 318 | it != end; it = m_VariableToPlotMultiMap.upper_bound(it->first)) |
|
306 | 319 | { |
|
307 | 320 | sqpApp->variableController().asyncChangeRange(it->first, newRange); |
|
308 | 321 | } |
|
309 | 322 | } |
|
310 | 323 | m_plot->replot(QCustomPlot::rpQueuedReplot); |
|
311 | 324 | } |
|
312 | 325 | |
|
313 |
void setRange(const QCPRange |
|
|
326 | void setRange(const QCPRange& newRange) | |
|
314 | 327 | { |
|
315 | auto graphRange = DateTimeRange{newRange.lower, newRange.upper}; | |
|
328 | auto graphRange = DateTimeRange { newRange.lower, newRange.upper }; | |
|
316 | 329 | setRange(graphRange); |
|
317 | 330 | } |
|
318 | 331 | |
|
319 | void rescaleY() | |
|
320 | { | |
|
321 | m_plot->yAxis->rescale(true); | |
|
322 | } | |
|
332 | void rescaleY() { m_plot->yAxis->rescale(true); } | |
|
323 | 333 | |
|
324 | std::tuple<double,double> moveGraph(const QPoint& destination) | |
|
334 | std::tuple<double, double> moveGraph(const QPoint& destination) | |
|
325 | 335 | { |
|
326 | 336 | auto currentPos = m_plot->mapFromParent(destination); |
|
327 | 337 | auto xAxis = m_plot->axisRect()->rangeDragAxis(Qt::Horizontal); |
|
328 | 338 | auto yAxis = m_plot->axisRect()->rangeDragAxis(Qt::Vertical); |
|
329 | 339 | auto oldXRange = xAxis->range(); |
|
330 | 340 | auto oldYRange = yAxis->range(); |
|
331 | 341 | double dx = xAxis->pixelToCoord(m_lastMousePos.x()) - xAxis->pixelToCoord(currentPos.x()); |
|
332 | xAxis->setRange(m_lastXRange.lower+dx, m_lastXRange.upper+dx); | |
|
333 | if(yAxis->scaleType() == QCPAxis::stLinear) | |
|
342 | xAxis->setRange(m_lastXRange.lower + dx, m_lastXRange.upper + dx); | |
|
343 | if (yAxis->scaleType() == QCPAxis::stLinear) | |
|
334 | 344 | { |
|
335 | double dy = yAxis->pixelToCoord(m_lastMousePos.y()) - yAxis->pixelToCoord(currentPos.y()); | |
|
336 | yAxis->setRange(m_lastYRange.lower+dy, m_lastYRange.upper+dy); | |
|
345 | double dy | |
|
346 | = yAxis->pixelToCoord(m_lastMousePos.y()) - yAxis->pixelToCoord(currentPos.y()); | |
|
347 | yAxis->setRange(m_lastYRange.lower + dy, m_lastYRange.upper + dy); | |
|
337 | 348 | } |
|
338 | 349 | else |
|
339 | 350 | { |
|
340 | double dy = yAxis->pixelToCoord(m_lastMousePos.y()) / yAxis->pixelToCoord(currentPos.y()); | |
|
341 | yAxis->setRange(m_lastYRange.lower*dy, m_lastYRange.upper*dy); | |
|
351 | double dy | |
|
352 | = yAxis->pixelToCoord(m_lastMousePos.y()) / yAxis->pixelToCoord(currentPos.y()); | |
|
353 | yAxis->setRange(m_lastYRange.lower * dy, m_lastYRange.upper * dy); | |
|
342 | 354 | } |
|
343 | 355 | auto newXRange = xAxis->range(); |
|
344 | 356 | auto newYRange = yAxis->range(); |
|
345 | 357 | setRange(xAxis->range()); |
|
346 | //m_lastMousePos = currentPos; | |
|
347 | return {newXRange.lower - oldXRange.lower, newYRange.lower - oldYRange.lower}; | |
|
358 | // m_lastMousePos = currentPos; | |
|
359 | return { newXRange.lower - oldXRange.lower, newYRange.lower - oldYRange.lower }; | |
|
348 | 360 | } |
|
349 | 361 | |
|
350 | 362 | void zoom(double factor, int center, Qt::Orientation orientation) |
|
351 | 363 | { |
|
352 |
QCPAxis |
|
|
364 | QCPAxis* axis = m_plot->axisRect()->rangeZoomAxis(orientation); | |
|
353 | 365 | axis->scaleRange(factor, axis->pixelToCoord(center)); |
|
354 | 366 | if (orientation == Qt::Horizontal) |
|
355 | 367 | setRange(axis->range()); |
|
356 | 368 | m_plot->replot(QCustomPlot::rpQueuedReplot); |
|
357 | 369 | } |
|
358 | 370 | |
|
359 |
void transform(const DateTimeRangeTransformation |
|
|
371 | void transform(const DateTimeRangeTransformation& tranformation) | |
|
360 | 372 | { |
|
361 | 373 | auto graphRange = m_plot->xAxis->range(); |
|
362 | DateTimeRange range{graphRange.lower, graphRange.upper}; | |
|
374 | DateTimeRange range { graphRange.lower, graphRange.upper }; | |
|
363 | 375 | range = range.transform(tranformation); |
|
364 | 376 | setRange(range); |
|
365 | 377 | m_plot->replot(QCustomPlot::rpQueuedReplot); |
|
366 | 378 | } |
|
367 | 379 | |
|
368 | 380 | void move(double dx, double dy) |
|
369 | 381 | { |
|
370 | 382 | auto xAxis = m_plot->axisRect()->rangeDragAxis(Qt::Horizontal); |
|
371 | 383 | auto yAxis = m_plot->axisRect()->rangeDragAxis(Qt::Vertical); |
|
372 | xAxis->setRange(QCPRange(xAxis->range().lower+dx, xAxis->range().upper+dx)); | |
|
373 | yAxis->setRange(QCPRange(yAxis->range().lower+dy, yAxis->range().upper+dy)); | |
|
384 | xAxis->setRange(QCPRange(xAxis->range().lower + dx, xAxis->range().upper + dx)); | |
|
385 | yAxis->setRange(QCPRange(yAxis->range().lower + dy, yAxis->range().upper + dy)); | |
|
374 | 386 | setRange(xAxis->range()); |
|
375 | 387 | m_plot->replot(QCustomPlot::rpQueuedReplot); |
|
376 | 388 | } |
|
377 | 389 | |
|
378 | 390 | void move(double factor, Qt::Orientation orientation) |
|
379 | 391 | { |
|
380 | 392 | auto oldRange = m_plot->xAxis->range(); |
|
381 |
QCPAxis |
|
|
382 |
if (m_plot->xAxis->scaleType() == QCPAxis::stLinear) |
|
|
393 | QCPAxis* axis = m_plot->axisRect()->rangeDragAxis(orientation); | |
|
394 | if (m_plot->xAxis->scaleType() == QCPAxis::stLinear) | |
|
395 | { | |
|
383 | 396 | double rg = (axis->range().upper - axis->range().lower) * (factor / 10); |
|
384 | 397 | axis->setRange(axis->range().lower + (rg), axis->range().upper + (rg)); |
|
385 | 398 | } |
|
386 |
else if (m_plot->xAxis->scaleType() == QCPAxis::stLogarithmic) |
|
|
399 | else if (m_plot->xAxis->scaleType() == QCPAxis::stLogarithmic) | |
|
400 | { | |
|
387 | 401 | int start = 0, stop = 0; |
|
388 | 402 | double diff = 0.; |
|
389 |
if (factor > 0.0) |
|
|
403 | if (factor > 0.0) | |
|
404 | { | |
|
390 | 405 | stop = m_plot->width() * factor / 10; |
|
391 | 406 | start = 2 * m_plot->width() * factor / 10; |
|
392 | 407 | } |
|
393 |
if (factor < 0.0) |
|
|
408 | if (factor < 0.0) | |
|
409 | { | |
|
394 | 410 | factor *= -1.0; |
|
395 | 411 | start = m_plot->width() * factor / 10; |
|
396 | 412 | stop = 2 * m_plot->width() * factor / 10; |
|
397 | 413 | } |
|
398 | 414 | diff = axis->pixelToCoord(start) / axis->pixelToCoord(stop); |
|
399 | 415 | axis->setRange(m_plot->axisRect()->rangeDragAxis(orientation)->range().lower * diff, |
|
400 |
|
|
|
416 | m_plot->axisRect()->rangeDragAxis(orientation)->range().upper * diff); | |
|
401 | 417 | } |
|
402 | 418 | if (orientation == Qt::Horizontal) |
|
403 | 419 | setRange(axis->range()); |
|
404 | 420 | m_plot->replot(QCustomPlot::rpQueuedReplot); |
|
405 | 421 | } |
|
406 | 422 | }; |
|
407 | 423 | |
|
408 |
VisualizationGraphWidget::VisualizationGraphWidget(const QString |
|
|
409 |
: VisualizationDragWidget{parent} |
|
|
410 |
|
|
|
411 |
|
|
|
424 | VisualizationGraphWidget::VisualizationGraphWidget(const QString& name, QWidget* parent) | |
|
425 | : VisualizationDragWidget { parent } | |
|
426 | , ui { new Ui::VisualizationGraphWidget } | |
|
427 | , impl { spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name) } | |
|
412 | 428 | { |
|
413 | 429 | ui->setupUi(this); |
|
414 | 430 | this->layout()->addWidget(impl->m_plot); |
|
415 | 431 | // 'Close' options : widget is deleted when closed |
|
416 | 432 | setAttribute(Qt::WA_DeleteOnClose); |
|
417 | 433 | |
|
418 | 434 | // The delegate must be initialized after the ui as it uses the plot |
|
419 | 435 | impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this); |
|
420 | 436 | |
|
421 | 437 | // Init the cursors |
|
422 | 438 | impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot()); |
|
423 | 439 | impl->m_HorizontalCursor->setOrientation(Qt::Horizontal); |
|
424 | 440 | impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot()); |
|
425 | 441 | impl->m_VerticalCursor->setOrientation(Qt::Vertical); |
|
426 | 442 | |
|
427 | 443 | this->setFocusPolicy(Qt::WheelFocus); |
|
428 | 444 | this->setMouseTracking(true); |
|
429 | 445 | impl->m_plot->setAttribute(Qt::WA_TransparentForMouseEvents); |
|
430 | 446 | impl->m_plot->setContextMenuPolicy(Qt::CustomContextMenu); |
|
431 | 447 | impl->m_plot->setParent(this); |
|
432 | 448 | |
|
433 |
connect(&sqpApp->variableController(), &VariableController2::variableDeleted, this, |
|
|
449 | connect(&sqpApp->variableController(), &VariableController2::variableDeleted, this, | |
|
450 | &VisualizationGraphWidget::variableDeleted); | |
|
434 | 451 | } |
|
435 | 452 | |
|
436 | 453 | |
|
437 | 454 | VisualizationGraphWidget::~VisualizationGraphWidget() |
|
438 | 455 | { |
|
439 | 456 | delete ui; |
|
440 | 457 | } |
|
441 | 458 | |
|
442 |
VisualizationZoneWidget |
|
|
459 | VisualizationZoneWidget* VisualizationGraphWidget::parentZoneWidget() const noexcept | |
|
443 | 460 | { |
|
444 | 461 | auto parent = parentWidget(); |
|
445 |
while (parent != nullptr && !qobject_cast<VisualizationZoneWidget |
|
|
462 | while (parent != nullptr && !qobject_cast<VisualizationZoneWidget*>(parent)) | |
|
463 | { | |
|
446 | 464 | parent = parent->parentWidget(); |
|
447 | 465 | } |
|
448 | 466 | |
|
449 |
return qobject_cast<VisualizationZoneWidget |
|
|
467 | return qobject_cast<VisualizationZoneWidget*>(parent); | |
|
450 | 468 | } |
|
451 | 469 | |
|
452 |
VisualizationWidget |
|
|
470 | VisualizationWidget* VisualizationGraphWidget::parentVisualizationWidget() const | |
|
453 | 471 | { |
|
454 | 472 | auto parent = parentWidget(); |
|
455 |
while (parent != nullptr && !qobject_cast<VisualizationWidget |
|
|
473 | while (parent != nullptr && !qobject_cast<VisualizationWidget*>(parent)) | |
|
474 | { | |
|
456 | 475 | parent = parent->parentWidget(); |
|
457 | 476 | } |
|
458 | 477 | |
|
459 |
return qobject_cast<VisualizationWidget |
|
|
478 | return qobject_cast<VisualizationWidget*>(parent); | |
|
460 | 479 | } |
|
461 | 480 | |
|
462 | 481 | void VisualizationGraphWidget::setFlags(GraphFlags flags) |
|
463 | 482 | { |
|
464 | 483 | impl->m_Flags = std::move(flags); |
|
465 | 484 | } |
|
466 | 485 | |
|
467 | void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, DateTimeRange range) | |
|
486 | void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable2> variable, DateTimeRange range) | |
|
468 | 487 | { |
|
469 | 488 | // Uses delegate to create the qcpplot components according to the variable |
|
470 | 489 | auto createdPlottables = VisualizationGraphHelper::create(variable, *impl->m_plot); |
|
471 | 490 | |
|
472 | 491 | // Sets graph properties |
|
473 | 492 | impl->m_RenderingDelegate->setGraphProperties(*variable, createdPlottables); |
|
474 | 493 | |
|
475 | impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)}); | |
|
494 | impl->m_VariableToPlotMultiMap.insert({ variable, std::move(createdPlottables) }); | |
|
476 | 495 | |
|
477 | 496 | setGraphRange(range); |
|
478 | 497 | // If the variable already has its data loaded, load its units and its range in the graph |
|
479 |
if (variable->data |
|
|
498 | if (variable->data() != nullptr) | |
|
499 | { | |
|
480 | 500 | impl->m_RenderingDelegate->setAxesUnits(*variable); |
|
481 | 501 | } |
|
482 | 502 | else |
|
483 | 503 | { |
|
484 | auto context = new QObject{this}; | |
|
485 | connect(variable.get(), &Variable::updated, context, | |
|
486 |
|
|
|
487 | { | |
|
488 | this->impl->m_RenderingDelegate->setAxesUnits(*variable); | |
|
489 | this->impl->m_plot->replot(QCustomPlot::rpQueuedReplot); | |
|
490 | delete context; | |
|
491 | } | |
|
492 | ); | |
|
504 | auto context = new QObject { this }; | |
|
505 | connect( | |
|
506 | variable.get(), &Variable2::updated, context, [this, variable, context, range](QUuid) { | |
|
507 | this->impl->m_RenderingDelegate->setAxesUnits(*variable); | |
|
508 | this->impl->m_plot->replot(QCustomPlot::rpQueuedReplot); | |
|
509 | delete context; | |
|
510 | }); | |
|
493 | 511 | } |
|
494 | 512 | //@TODO this is bad! when variable is moved to another graph it still fires |
|
495 | 513 | // even if this has been deleted |
|
496 | connect(variable.get(), &Variable::updated, this, &VisualizationGraphWidget::variableUpdated); | |
|
514 | connect(variable.get(), &Variable2::updated, this, &VisualizationGraphWidget::variableUpdated); | |
|
497 | 515 | this->onUpdateVarDisplaying(variable, range); // My bullshit |
|
498 | 516 | emit variableAdded(variable); |
|
499 | 517 | } |
|
500 | 518 | |
|
501 | void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept | |
|
519 | void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable2> variable) noexcept | |
|
502 | 520 | { |
|
503 | 521 | // Each component associated to the variable : |
|
504 | 522 | // - is removed from qcpplot (which deletes it) |
|
505 | 523 | // - is no longer referenced in the map |
|
506 | 524 | auto variableIt = impl->m_VariableToPlotMultiMap.find(variable); |
|
507 |
if (variableIt != impl->m_VariableToPlotMultiMap.cend()) |
|
|
525 | if (variableIt != impl->m_VariableToPlotMultiMap.cend()) | |
|
526 | { | |
|
508 | 527 | emit variableAboutToBeRemoved(variable); |
|
509 | 528 | |
|
510 |
auto |
|
|
529 | auto& plottablesMap = variableIt->second; | |
|
511 | 530 | |
|
512 | 531 | for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend(); |
|
513 |
plottableIt != plottableEnd;) |
|
|
532 | plottableIt != plottableEnd;) | |
|
533 | { | |
|
514 | 534 | impl->m_plot->removePlottable(plottableIt->second); |
|
515 | 535 | plottableIt = plottablesMap.erase(plottableIt); |
|
516 | 536 | } |
|
517 | 537 | |
|
518 | 538 | impl->m_VariableToPlotMultiMap.erase(variableIt); |
|
519 | 539 | } |
|
520 | 540 | |
|
521 | 541 | // Updates graph |
|
522 | 542 | impl->m_plot->replot(QCustomPlot::rpQueuedReplot); |
|
523 | 543 | } |
|
524 | 544 | |
|
525 |
std::vector<std::shared_ptr<Variable> |
|
|
545 | std::vector<std::shared_ptr<Variable2>> VisualizationGraphWidget::variables() const | |
|
526 | 546 | { |
|
527 |
auto variables = std::vector<std::shared_ptr<Variable> |
|
|
547 | auto variables = std::vector<std::shared_ptr<Variable2>> {}; | |
|
528 | 548 | for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap); |
|
529 |
it != std::cend(impl->m_VariableToPlotMultiMap); ++it) |
|
|
549 | it != std::cend(impl->m_VariableToPlotMultiMap); ++it) | |
|
550 | { | |
|
530 | 551 | variables.push_back(it->first); |
|
531 | 552 | } |
|
532 | 553 | |
|
533 | 554 | return variables; |
|
534 | 555 | } |
|
535 | 556 | |
|
536 | void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable) | |
|
557 | void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable2> variable) | |
|
537 | 558 | { |
|
538 |
if (!variable) |
|
|
559 | if (!variable) | |
|
560 | { | |
|
539 | 561 | qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null"; |
|
540 | 562 | return; |
|
541 | 563 | } |
|
542 | 564 | |
|
543 | 565 | VisualizationGraphHelper::setYAxisRange(variable, *impl->m_plot); |
|
544 | 566 | } |
|
545 | 567 | |
|
546 | 568 | DateTimeRange VisualizationGraphWidget::graphRange() const noexcept |
|
547 | 569 | { |
|
548 | 570 | auto graphRange = impl->m_plot->xAxis->range(); |
|
549 | return DateTimeRange{graphRange.lower, graphRange.upper}; | |
|
571 | return DateTimeRange { graphRange.lower, graphRange.upper }; | |
|
550 | 572 | } |
|
551 | 573 | |
|
552 |
void VisualizationGraphWidget::setGraphRange( |
|
|
574 | void VisualizationGraphWidget::setGraphRange( | |
|
575 | const DateTimeRange& range, bool updateVar, bool forward) | |
|
553 | 576 | { |
|
554 | 577 | impl->setRange(range, updateVar); |
|
555 | if(forward) | |
|
578 | if (forward) | |
|
556 | 579 | { |
|
557 | 580 | emit this->setrange_sig(this->graphRange(), true, false); |
|
558 | 581 | } |
|
559 | ||
|
560 | 582 | } |
|
561 | 583 | |
|
562 | 584 | void VisualizationGraphWidget::setAutoRangeOnVariableInitialization(bool value) |
|
563 | 585 | { |
|
564 | 586 | impl->m_VariableAutoRangeOnInit = value; |
|
565 | 587 | } |
|
566 | 588 | |
|
567 | 589 | QVector<DateTimeRange> VisualizationGraphWidget::selectionZoneRanges() const |
|
568 | 590 | { |
|
569 | 591 | QVector<DateTimeRange> ranges; |
|
570 |
for (auto zone : impl->m_SelectionZones) |
|
|
592 | for (auto zone : impl->m_SelectionZones) | |
|
593 | { | |
|
571 | 594 | ranges << zone->range(); |
|
572 | 595 | } |
|
573 | 596 | |
|
574 | 597 | return ranges; |
|
575 | 598 | } |
|
576 | 599 | |
|
577 |
void VisualizationGraphWidget::addSelectionZones(const QVector<DateTimeRange> |
|
|
600 | void VisualizationGraphWidget::addSelectionZones(const QVector<DateTimeRange>& ranges) | |
|
578 | 601 | { |
|
579 |
for (const auto |
|
|
602 | for (const auto& range : ranges) | |
|
603 | { | |
|
580 | 604 | // note: ownership is transfered to QCustomPlot |
|
581 | 605 | auto zone = new VisualizationSelectionZoneItem(&plot()); |
|
582 | 606 | zone->setRange(range.m_TStart, range.m_TEnd); |
|
583 | 607 | impl->addSelectionZone(zone); |
|
584 | 608 | } |
|
585 | 609 | |
|
586 | 610 | plot().replot(QCustomPlot::rpQueuedReplot); |
|
587 | 611 | } |
|
588 | 612 | |
|
589 | VisualizationSelectionZoneItem * | |
|
590 |
|
|
|
613 | VisualizationSelectionZoneItem* VisualizationGraphWidget::addSelectionZone( | |
|
614 | const QString& name, const DateTimeRange& range) | |
|
591 | 615 | { |
|
592 | 616 | // note: ownership is transfered to QCustomPlot |
|
593 | 617 | auto zone = new VisualizationSelectionZoneItem(&plot()); |
|
594 | 618 | zone->setName(name); |
|
595 | 619 | zone->setRange(range.m_TStart, range.m_TEnd); |
|
596 | 620 | impl->addSelectionZone(zone); |
|
597 | 621 | |
|
598 | 622 | plot().replot(QCustomPlot::rpQueuedReplot); |
|
599 | 623 | |
|
600 | 624 | return zone; |
|
601 | 625 | } |
|
602 | 626 | |
|
603 |
void VisualizationGraphWidget::removeSelectionZone(VisualizationSelectionZoneItem |
|
|
627 | void VisualizationGraphWidget::removeSelectionZone(VisualizationSelectionZoneItem* selectionZone) | |
|
604 | 628 | { |
|
605 | 629 | parentVisualizationWidget()->selectionZoneManager().setSelected(selectionZone, false); |
|
606 | 630 | |
|
607 |
if (impl->m_HoveredZone == selectionZone) |
|
|
631 | if (impl->m_HoveredZone == selectionZone) | |
|
632 | { | |
|
608 | 633 | impl->m_HoveredZone = nullptr; |
|
609 | 634 | setCursor(Qt::ArrowCursor); |
|
610 | 635 | } |
|
611 | 636 | |
|
612 | 637 | impl->m_SelectionZones.removeAll(selectionZone); |
|
613 | 638 | plot().removeItem(selectionZone); |
|
614 | 639 | plot().replot(QCustomPlot::rpQueuedReplot); |
|
615 | 640 | } |
|
616 | 641 | |
|
617 | 642 | void VisualizationGraphWidget::undoZoom() |
|
618 | 643 | { |
|
619 | 644 | auto zoom = impl->m_ZoomStack.pop(); |
|
620 | 645 | auto axisX = plot().axisRect()->axis(QCPAxis::atBottom); |
|
621 | 646 | auto axisY = plot().axisRect()->axis(QCPAxis::atLeft); |
|
622 | 647 | |
|
623 | 648 | axisX->setRange(zoom.first); |
|
624 | 649 | axisY->setRange(zoom.second); |
|
625 | 650 | |
|
626 | 651 | plot().replot(QCustomPlot::rpQueuedReplot); |
|
627 | 652 | } |
|
628 | 653 | |
|
629 | void VisualizationGraphWidget::zoom(double factor, int center, Qt::Orientation orientation, bool forward) | |
|
654 | void VisualizationGraphWidget::zoom( | |
|
655 | double factor, int center, Qt::Orientation orientation, bool forward) | |
|
630 | 656 | { |
|
631 | 657 | impl->zoom(factor, center, orientation); |
|
632 | if(forward && orientation==Qt::Horizontal) | |
|
658 | if (forward && orientation == Qt::Horizontal) | |
|
633 | 659 | emit this->setrange_sig(this->graphRange(), true, false); |
|
634 | 660 | } |
|
635 | 661 | |
|
636 | 662 | void VisualizationGraphWidget::move(double factor, Qt::Orientation orientation, bool forward) |
|
637 | 663 | { |
|
638 | 664 | impl->move(factor, orientation); |
|
639 | if(forward) | |
|
665 | if (forward) | |
|
640 | 666 | emit this->setrange_sig(this->graphRange(), true, false); |
|
641 | 667 | } |
|
642 | 668 | |
|
643 | 669 | void VisualizationGraphWidget::move(double dx, double dy, bool forward) |
|
644 | 670 | { |
|
645 | 671 | impl->move(dx, dy); |
|
646 | if(forward) | |
|
672 | if (forward) | |
|
647 | 673 | emit this->setrange_sig(this->graphRange(), true, false); |
|
648 | 674 | } |
|
649 | 675 | |
|
650 | void VisualizationGraphWidget::transform(const DateTimeRangeTransformation &tranformation, bool forward) | |
|
676 | void VisualizationGraphWidget::transform( | |
|
677 | const DateTimeRangeTransformation& tranformation, bool forward) | |
|
651 | 678 | { |
|
652 | 679 | impl->transform(tranformation); |
|
653 | if(forward) | |
|
680 | if (forward) | |
|
654 | 681 | emit this->setrange_sig(this->graphRange(), true, false); |
|
655 | 682 | } |
|
656 | 683 | |
|
657 |
void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor |
|
|
684 | void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor* visitor) | |
|
658 | 685 | { |
|
659 |
if (visitor) |
|
|
686 | if (visitor) | |
|
687 | { | |
|
660 | 688 | visitor->visit(this); |
|
661 | 689 | } |
|
662 |
else |
|
|
690 | else | |
|
691 | { | |
|
663 | 692 | qCCritical(LOG_VisualizationGraphWidget()) |
|
664 | 693 | << tr("Can't visit widget : the visitor is null"); |
|
665 | 694 | } |
|
666 | 695 | } |
|
667 | 696 | |
|
668 |
bool VisualizationGraphWidget::canDrop( |
|
|
697 | bool VisualizationGraphWidget::canDrop(Variable2& variable) const | |
|
669 | 698 | { |
|
670 |
auto isSpectrogram |
|
|
671 | return std::dynamic_pointer_cast<SpectrogramSeries>(variable.dataSeries()) != nullptr; | |
|
672 | }; | |
|
699 | auto isSpectrogram | |
|
700 | = [](auto& variable) { return variable.type() == DataSeriesType::SPECTROGRAM; }; | |
|
673 | 701 | |
|
674 | 702 | // - A spectrogram series can't be dropped on graph with existing plottables |
|
675 | 703 | // - No data series can be dropped on graph with existing spectrogram series |
|
676 | 704 | return isSpectrogram(variable) |
|
677 |
|
|
|
678 | : std::none_of( | |
|
679 |
|
|
|
680 |
|
|
|
705 | ? impl->m_VariableToPlotMultiMap.empty() | |
|
706 | : std::none_of(impl->m_VariableToPlotMultiMap.cbegin(), | |
|
707 | impl->m_VariableToPlotMultiMap.cend(), | |
|
708 | [isSpectrogram](const auto& entry) { return isSpectrogram(*entry.first); }); | |
|
681 | 709 | } |
|
682 | 710 | |
|
683 |
bool VisualizationGraphWidget::contains( |
|
|
711 | bool VisualizationGraphWidget::contains(Variable2& variable) const | |
|
684 | 712 | { |
|
685 | 713 | // Finds the variable among the keys of the map |
|
686 | 714 | auto variablePtr = &variable; |
|
687 | 715 | auto findVariable |
|
688 |
= [variablePtr](const auto |
|
|
716 | = [variablePtr](const auto& entry) { return variablePtr == entry.first.get(); }; | |
|
689 | 717 | |
|
690 | 718 | auto end = impl->m_VariableToPlotMultiMap.cend(); |
|
691 | 719 | auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable); |
|
692 | 720 | return it != end; |
|
693 | 721 | } |
|
694 | 722 | |
|
695 | 723 | QString VisualizationGraphWidget::name() const |
|
696 | 724 | { |
|
697 | 725 | return impl->m_Name; |
|
698 | 726 | } |
|
699 | 727 | |
|
700 |
QMimeData |
|
|
728 | QMimeData* VisualizationGraphWidget::mimeData(const QPoint& position) const | |
|
701 | 729 | { |
|
702 | 730 | auto mimeData = new QMimeData; |
|
703 | 731 | |
|
704 | 732 | auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position); |
|
705 | 733 | if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones |
|
706 |
&& selectionZoneItemUnderCursor) |
|
|
707 | mimeData->setData(MIME_TYPE_TIME_RANGE, TimeController::mimeDataForTimeRange( | |
|
708 | selectionZoneItemUnderCursor->range())); | |
|
709 | mimeData->setData(MIME_TYPE_SELECTION_ZONE, TimeController::mimeDataForTimeRange( | |
|
710 | selectionZoneItemUnderCursor->range())); | |
|
734 | && selectionZoneItemUnderCursor) | |
|
735 | { | |
|
736 | mimeData->setData(MIME_TYPE_TIME_RANGE, | |
|
737 | TimeController::mimeDataForTimeRange(selectionZoneItemUnderCursor->range())); | |
|
738 | mimeData->setData(MIME_TYPE_SELECTION_ZONE, | |
|
739 | TimeController::mimeDataForTimeRange(selectionZoneItemUnderCursor->range())); | |
|
711 | 740 | } |
|
712 |
else |
|
|
713 | mimeData->setData(MIME_TYPE_GRAPH, QByteArray{}); | |
|
741 | else | |
|
742 | { | |
|
743 | mimeData->setData(MIME_TYPE_GRAPH, QByteArray {}); | |
|
714 | 744 | |
|
715 | 745 | auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange()); |
|
716 | 746 | mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData); |
|
717 | 747 | } |
|
718 | 748 | |
|
719 | 749 | return mimeData; |
|
720 | 750 | } |
|
721 | 751 | |
|
722 |
QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint |
|
|
752 | QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint& dragPosition) | |
|
723 | 753 | { |
|
724 | 754 | auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition); |
|
725 | 755 | if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones |
|
726 |
&& selectionZoneItemUnderCursor) |
|
|
756 | && selectionZoneItemUnderCursor) | |
|
757 | { | |
|
727 | 758 | |
|
728 | 759 | auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition(); |
|
729 | 760 | auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition(); |
|
730 | 761 | |
|
731 | auto zoneSize = QSizeF{qAbs(zoneBottomRight.x() - zoneTopLeft.x()), | |
|
732 |
|
|
|
762 | auto zoneSize = QSizeF { qAbs(zoneBottomRight.x() - zoneTopLeft.x()), | |
|
763 | qAbs(zoneBottomRight.y() - zoneTopLeft.y()) } | |
|
733 | 764 | .toSize(); |
|
734 | 765 | |
|
735 | 766 | auto pixmap = QPixmap(zoneSize); |
|
736 | render(&pixmap, QPoint(), QRegion{QRect{zoneTopLeft.toPoint(), zoneSize}}); | |
|
767 | render(&pixmap, QPoint(), QRegion { QRect { zoneTopLeft.toPoint(), zoneSize } }); | |
|
737 | 768 | |
|
738 | 769 | return pixmap; |
|
739 | 770 | } |
|
740 | 771 | |
|
741 | 772 | return QPixmap(); |
|
742 | 773 | } |
|
743 | 774 | |
|
744 | 775 | bool VisualizationGraphWidget::isDragAllowed() const |
|
745 | 776 | { |
|
746 | 777 | return true; |
|
747 | 778 | } |
|
748 | 779 | |
|
749 | 780 | void VisualizationGraphWidget::highlightForMerge(bool highlighted) |
|
750 | 781 | { |
|
751 |
if (highlighted) |
|
|
782 | if (highlighted) | |
|
783 | { | |
|
752 | 784 | plot().setBackground(QBrush(QColor("#BBD5EE"))); |
|
753 | 785 | } |
|
754 |
else |
|
|
786 | else | |
|
787 | { | |
|
755 | 788 | plot().setBackground(QBrush(Qt::white)); |
|
756 | 789 | } |
|
757 | 790 | |
|
758 | 791 | plot().update(); |
|
759 | 792 | } |
|
760 | 793 | |
|
761 | 794 | void VisualizationGraphWidget::addVerticalCursor(double time) |
|
762 | 795 | { |
|
763 | 796 | impl->m_VerticalCursor->setPosition(time); |
|
764 | 797 | impl->m_VerticalCursor->setVisible(true); |
|
765 | 798 | |
|
766 | 799 | auto text |
|
767 | 800 | = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n'); |
|
768 | 801 | impl->m_VerticalCursor->setLabelText(text); |
|
769 | 802 | } |
|
770 | 803 | |
|
771 | 804 | void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position) |
|
772 | 805 | { |
|
773 | 806 | impl->m_VerticalCursor->setAbsolutePosition(position); |
|
774 | 807 | impl->m_VerticalCursor->setVisible(true); |
|
775 | 808 | |
|
776 | 809 | auto axis = plot().axisRect()->axis(QCPAxis::atBottom); |
|
777 | 810 | auto text |
|
778 | 811 | = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT); |
|
779 | 812 | impl->m_VerticalCursor->setLabelText(text); |
|
780 | 813 | } |
|
781 | 814 | |
|
782 | 815 | void VisualizationGraphWidget::removeVerticalCursor() |
|
783 | 816 | { |
|
784 | 817 | impl->m_VerticalCursor->setVisible(false); |
|
785 | 818 | plot().replot(QCustomPlot::rpQueuedReplot); |
|
786 | 819 | } |
|
787 | 820 | |
|
788 | 821 | void VisualizationGraphWidget::addHorizontalCursor(double value) |
|
789 | 822 | { |
|
790 | 823 | impl->m_HorizontalCursor->setPosition(value); |
|
791 | 824 | impl->m_HorizontalCursor->setVisible(true); |
|
792 | 825 | impl->m_HorizontalCursor->setLabelText(QString::number(value)); |
|
793 | 826 | } |
|
794 | 827 | |
|
795 | 828 | void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position) |
|
796 | 829 | { |
|
797 | 830 | impl->m_HorizontalCursor->setAbsolutePosition(position); |
|
798 | 831 | impl->m_HorizontalCursor->setVisible(true); |
|
799 | 832 | |
|
800 | 833 | auto axis = plot().axisRect()->axis(QCPAxis::atLeft); |
|
801 | 834 | impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position))); |
|
802 | 835 | } |
|
803 | 836 | |
|
804 | 837 | void VisualizationGraphWidget::removeHorizontalCursor() |
|
805 | 838 | { |
|
806 | 839 | impl->m_HorizontalCursor->setVisible(false); |
|
807 | 840 | plot().replot(QCustomPlot::rpQueuedReplot); |
|
808 | 841 | } |
|
809 | 842 | |
|
810 |
void VisualizationGraphWidget::closeEvent(QCloseEvent |
|
|
843 | void VisualizationGraphWidget::closeEvent(QCloseEvent* event) | |
|
811 | 844 | { |
|
812 | 845 | Q_UNUSED(event); |
|
813 | 846 | |
|
814 |
for (auto i : impl->m_SelectionZones) |
|
|
847 | for (auto i : impl->m_SelectionZones) | |
|
848 | { | |
|
815 | 849 | parentVisualizationWidget()->selectionZoneManager().setSelected(i, false); |
|
816 | 850 | } |
|
817 | 851 | |
|
818 | 852 | // Prevents that all variables will be removed from graph when it will be closed |
|
819 |
for (auto |
|
|
853 | for (auto& variableEntry : impl->m_VariableToPlotMultiMap) | |
|
854 | { | |
|
820 | 855 | emit variableAboutToBeRemoved(variableEntry.first); |
|
821 | 856 | } |
|
822 | 857 | } |
|
823 | 858 | |
|
824 |
void VisualizationGraphWidget::enterEvent(QEvent |
|
|
859 | void VisualizationGraphWidget::enterEvent(QEvent* event) | |
|
825 | 860 | { |
|
826 | 861 | Q_UNUSED(event); |
|
827 | 862 | impl->m_RenderingDelegate->showGraphOverlay(true); |
|
828 | 863 | } |
|
829 | 864 | |
|
830 |
void VisualizationGraphWidget::leaveEvent(QEvent |
|
|
865 | void VisualizationGraphWidget::leaveEvent(QEvent* event) | |
|
831 | 866 | { |
|
832 | 867 | Q_UNUSED(event); |
|
833 | 868 | impl->m_RenderingDelegate->showGraphOverlay(false); |
|
834 | 869 | |
|
835 |
if (auto parentZone = parentZoneWidget()) |
|
|
870 | if (auto parentZone = parentZoneWidget()) | |
|
871 | { | |
|
836 | 872 | parentZone->notifyMouseLeaveGraph(this); |
|
837 | 873 | } |
|
838 |
else |
|
|
874 | else | |
|
875 | { | |
|
839 | 876 | qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget"; |
|
840 | 877 | } |
|
841 | 878 | |
|
842 |
if (impl->m_HoveredZone) |
|
|
879 | if (impl->m_HoveredZone) | |
|
880 | { | |
|
843 | 881 | impl->m_HoveredZone->setHovered(false); |
|
844 | 882 | impl->m_HoveredZone = nullptr; |
|
845 | 883 | } |
|
846 | 884 | } |
|
847 | 885 | |
|
848 |
void VisualizationGraphWidget::wheelEvent(QWheelEvent |
|
|
886 | void VisualizationGraphWidget::wheelEvent(QWheelEvent* event) | |
|
849 | 887 | { |
|
850 | 888 | double factor; |
|
851 | 889 | double wheelSteps = event->delta() / 120.0; // a single step delta is +/-120 usually |
|
852 |
if (event->modifiers() == Qt::ControlModifier) |
|
|
890 | if (event->modifiers() == Qt::ControlModifier) | |
|
891 | { | |
|
853 | 892 | if (event->orientation() == Qt::Vertical) // mRangeZoom.testFlag(Qt::Vertical)) |
|
854 | 893 | { |
|
855 | 894 | setCursor(Qt::SizeVerCursor); |
|
856 | 895 | factor = pow(impl->m_plot->axisRect()->rangeZoomFactor(Qt::Vertical), wheelSteps); |
|
857 | 896 | zoom(factor, event->pos().y(), Qt::Vertical); |
|
858 | 897 | } |
|
859 | 898 | } |
|
860 |
else if (event->modifiers() == Qt::ShiftModifier) |
|
|
899 | else if (event->modifiers() == Qt::ShiftModifier) | |
|
900 | { | |
|
861 | 901 | if (event->orientation() == Qt::Vertical) // mRangeZoom.testFlag(Qt::Vertical)) |
|
862 | 902 | { |
|
863 | 903 | setCursor(Qt::SizeHorCursor); |
|
864 | 904 | factor = pow(impl->m_plot->axisRect()->rangeZoomFactor(Qt::Horizontal), wheelSteps); |
|
865 | 905 | zoom(factor, event->pos().x(), Qt::Horizontal); |
|
866 | 906 | } |
|
867 | 907 | } |
|
868 |
else |
|
|
908 | else | |
|
909 | { | |
|
869 | 910 | move(wheelSteps, Qt::Horizontal); |
|
870 | 911 | } |
|
871 | 912 | event->accept(); |
|
872 | 913 | } |
|
873 | 914 | |
|
874 | 915 | |
|
875 | ||
|
876 | void VisualizationGraphWidget::mouseMoveEvent(QMouseEvent *event) | |
|
916 | void VisualizationGraphWidget::mouseMoveEvent(QMouseEvent* event) | |
|
877 | 917 | { |
|
878 | if(impl->isDrawingZoomRect()) | |
|
918 | if (impl->isDrawingZoomRect()) | |
|
879 | 919 | { |
|
880 | 920 | impl->updateZoomRect(event->pos()); |
|
881 | 921 | } |
|
882 | 922 | else if (impl->isDrawingZoneRect()) |
|
883 | 923 | { |
|
884 | 924 | impl->updateZoneRect(event->pos()); |
|
885 | 925 | } |
|
886 | 926 | else if (event->buttons() == Qt::LeftButton) |
|
887 | 927 | { |
|
888 | if(sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::None) | |
|
928 | if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::None) | |
|
889 | 929 | { |
|
890 | auto [dx,dy] = impl->moveGraph(event->pos()); | |
|
930 | auto [dx, dy] = impl->moveGraph(event->pos()); | |
|
891 | 931 | emit this->setrange_sig(this->graphRange(), true, false); |
|
892 | 932 | } |
|
893 |
else if(sqpApp->plotsInteractionMode() |
|
|
933 | else if (sqpApp->plotsInteractionMode() | |
|
934 | == SqpApplication::PlotsInteractionMode::SelectionZones) | |
|
894 | 935 | { |
|
895 | 936 | auto posInPlot = this->impl->m_plot->mapFromParent(event->pos()); |
|
896 | if(auto item = impl->m_plot->itemAt(posInPlot)) | |
|
937 | if (auto item = impl->m_plot->itemAt(posInPlot)) | |
|
897 | 938 | { |
|
898 | if(qobject_cast<VisualizationSelectionZoneItem*>(item)) | |
|
939 | if (qobject_cast<VisualizationSelectionZoneItem*>(item)) | |
|
899 | 940 | { |
|
900 |
QMouseEvent e{QEvent::MouseMove, posInPlot, event->button(),event->buttons(), |
|
|
941 | QMouseEvent e { QEvent::MouseMove, posInPlot, event->button(), event->buttons(), | |
|
942 | event->modifiers() }; | |
|
901 | 943 | sqpApp->sendEvent(this->impl->m_plot, &e); |
|
902 | 944 | this->impl->m_plot->replot(QCustomPlot::rpImmediateRefresh); |
|
903 | 945 | } |
|
904 | 946 | } |
|
905 | ||
|
906 | 947 | } |
|
907 | 948 | } |
|
908 | 949 | else |
|
909 | 950 | { |
|
910 | 951 | impl->m_RenderingDelegate->updateTooltip(event); |
|
911 | 952 | } |
|
912 | //event->accept(); | |
|
953 | // event->accept(); | |
|
913 | 954 | QWidget::mouseMoveEvent(event); |
|
914 | 955 | } |
|
915 | 956 | |
|
916 |
void VisualizationGraphWidget::mouseReleaseEvent(QMouseEvent |
|
|
957 | void VisualizationGraphWidget::mouseReleaseEvent(QMouseEvent* event) | |
|
917 | 958 | { |
|
918 | if(impl->isDrawingZoomRect()) | |
|
959 | if (impl->isDrawingZoomRect()) | |
|
919 | 960 | { |
|
920 | 961 | auto oldRange = this->graphRange(); |
|
921 | 962 | impl->applyZoomRect(); |
|
922 | 963 | auto newRange = this->graphRange(); |
|
923 | if(auto tf = DateTimeRangeHelper::computeTransformation(oldRange,newRange)) | |
|
964 | if (auto tf = DateTimeRangeHelper::computeTransformation(oldRange, newRange)) | |
|
924 | 965 | emit this->transform_sig(tf.value(), false); |
|
925 | 966 | } |
|
926 | else if(impl->isDrawingZoneRect()) | |
|
967 | else if (impl->isDrawingZoneRect()) | |
|
927 | 968 | { |
|
928 | 969 | impl->endDrawingZone(); |
|
929 | 970 | } |
|
930 | 971 | else |
|
931 | 972 | { |
|
932 | 973 | setCursor(Qt::ArrowCursor); |
|
933 | 974 | } |
|
934 | 975 | auto posInPlot = this->impl->m_plot->mapFromParent(event->pos()); |
|
935 | if(auto item = impl->m_plot->itemAt(posInPlot)) | |
|
976 | if (auto item = impl->m_plot->itemAt(posInPlot)) | |
|
936 | 977 | { |
|
937 | if(qobject_cast<VisualizationSelectionZoneItem*>(item)) | |
|
978 | if (qobject_cast<VisualizationSelectionZoneItem*>(item)) | |
|
938 | 979 | { |
|
939 |
QMouseEvent e{QEvent::MouseButtonRelease, posInPlot, event->button(), |
|
|
980 | QMouseEvent e { QEvent::MouseButtonRelease, posInPlot, event->button(), | |
|
981 | event->buttons(), event->modifiers() }; | |
|
940 | 982 | sqpApp->sendEvent(this->impl->m_plot, &e); |
|
941 | 983 | } |
|
942 | 984 | } |
|
943 | 985 | event->accept(); |
|
944 | 986 | } |
|
945 | 987 | |
|
946 |
void VisualizationGraphWidget::mousePressEvent(QMouseEvent |
|
|
988 | void VisualizationGraphWidget::mousePressEvent(QMouseEvent* event) | |
|
947 | 989 | { |
|
948 | if (event->button()==Qt::RightButton) | |
|
990 | if (event->button() == Qt::RightButton) | |
|
949 | 991 | { |
|
950 | 992 | onGraphMenuRequested(event->pos()); |
|
951 | 993 | } |
|
952 | 994 | else |
|
953 | 995 | { |
|
954 | 996 | auto selectedZone = impl->selectionZoneAt(event->pos()); |
|
955 | 997 | switch (sqpApp->plotsInteractionMode()) |
|
956 | 998 | { |
|
957 |
case SqpApplication::PlotsInteractionMode::DragAndDrop |
|
|
958 | break; | |
|
959 |
case SqpApplication::PlotsInteractionMode::SelectionZones |
|
|
960 | impl->setSelectionZonesEditionEnabled(true); | |
|
961 | if ((event->modifiers() == Qt::ControlModifier) && (selectedZone != nullptr)) | |
|
962 | { | |
|
963 | auto alreadySelectedZones = parentVisualizationWidget()->selectionZoneManager().selectedItems(); | |
|
999 | case SqpApplication::PlotsInteractionMode::DragAndDrop: | |
|
1000 | break; | |
|
1001 | case SqpApplication::PlotsInteractionMode::SelectionZones: | |
|
1002 | impl->setSelectionZonesEditionEnabled(true); | |
|
1003 | if ((event->modifiers() == Qt::ControlModifier) && (selectedZone != nullptr)) | |
|
1004 | { | |
|
1005 | auto alreadySelectedZones | |
|
1006 | = parentVisualizationWidget()->selectionZoneManager().selectedItems(); | |
|
964 | 1007 | selectedZone->setAssociatedEditedZones(alreadySelectedZones); |
|
965 | if(SciQLop::containers::contains(alreadySelectedZones, selectedZone)) | |
|
1008 | if (SciQLop::containers::contains(alreadySelectedZones, selectedZone)) | |
|
966 | 1009 | { |
|
967 | 1010 | alreadySelectedZones.removeOne(selectedZone); |
|
968 | 1011 | } |
|
969 | 1012 | else |
|
970 | 1013 | { |
|
971 | 1014 | alreadySelectedZones.append(selectedZone); |
|
972 | 1015 | } |
|
973 |
parentVisualizationWidget()->selectionZoneManager().select( |
|
|
974 | } | |
|
975 | else | |
|
976 | { | |
|
977 | if (!selectedZone) | |
|
978 | { | |
|
979 | parentVisualizationWidget()->selectionZoneManager().clearSelection(); | |
|
980 | impl->startDrawingZone(event->pos()); | |
|
1016 | parentVisualizationWidget()->selectionZoneManager().select( | |
|
1017 | alreadySelectedZones); | |
|
981 | 1018 | } |
|
982 | 1019 | else |
|
983 | 1020 | { |
|
984 | parentVisualizationWidget()->selectionZoneManager().select({ selectedZone }); | |
|
1021 | if (!selectedZone) | |
|
1022 | { | |
|
1023 | parentVisualizationWidget()->selectionZoneManager().clearSelection(); | |
|
1024 | impl->startDrawingZone(event->pos()); | |
|
1025 | } | |
|
1026 | else | |
|
1027 | { | |
|
1028 | parentVisualizationWidget()->selectionZoneManager().select( | |
|
1029 | { selectedZone }); | |
|
1030 | } | |
|
985 | 1031 | } |
|
986 | } | |
|
987 | { | |
|
988 | auto posInPlot = this->impl->m_plot->mapFromParent(event->pos()); | |
|
989 | if(auto item = impl->m_plot->itemAt(posInPlot)) | |
|
990 | 1032 | { |
|
991 | if(qobject_cast<VisualizationSelectionZoneItem*>(item)) | |
|
1033 | auto posInPlot = this->impl->m_plot->mapFromParent(event->pos()); | |
|
1034 | if (auto item = impl->m_plot->itemAt(posInPlot)) | |
|
992 | 1035 | { |
|
993 | QMouseEvent e{QEvent::MouseButtonPress, posInPlot, event->button(), event->buttons(), event->modifiers()}; | |
|
994 | sqpApp->sendEvent(this->impl->m_plot, &e); | |
|
1036 | if (qobject_cast<VisualizationSelectionZoneItem*>(item)) | |
|
1037 | { | |
|
1038 | QMouseEvent e { QEvent::MouseButtonPress, posInPlot, event->button(), | |
|
1039 | event->buttons(), event->modifiers() }; | |
|
1040 | sqpApp->sendEvent(this->impl->m_plot, &e); | |
|
1041 | } | |
|
995 | 1042 | } |
|
996 | 1043 | } |
|
997 |
|
|
|
998 | break; | |
|
999 | case SqpApplication::PlotsInteractionMode::ZoomBox : | |
|
1000 | impl->startDrawingRect(event->pos()); | |
|
1001 |
|
|
|
1002 | default: | |
|
1003 | if(auto item = impl->m_plot->itemAt(event->pos())) | |
|
1004 | { | |
|
1005 | emit impl->m_plot->itemClick(item,event); | |
|
1006 | if(qobject_cast<VisualizationSelectionZoneItem*>(item)) | |
|
1044 | break; | |
|
1045 | case SqpApplication::PlotsInteractionMode::ZoomBox: | |
|
1046 | impl->startDrawingRect(event->pos()); | |
|
1047 | break; | |
|
1048 | default: | |
|
1049 | if (auto item = impl->m_plot->itemAt(event->pos())) | |
|
1050 | { | |
|
1051 | emit impl->m_plot->itemClick(item, event); | |
|
1052 | if (qobject_cast<VisualizationSelectionZoneItem*>(item)) | |
|
1053 | { | |
|
1054 | setCursor(Qt::ClosedHandCursor); | |
|
1055 | impl->enterPlotDrag(event->pos()); | |
|
1056 | } | |
|
1057 | } | |
|
1058 | else | |
|
1007 | 1059 | { |
|
1008 | 1060 | setCursor(Qt::ClosedHandCursor); |
|
1009 | 1061 | impl->enterPlotDrag(event->pos()); |
|
1010 | 1062 | } |
|
1011 | } | |
|
1012 | else | |
|
1013 | { | |
|
1014 | setCursor(Qt::ClosedHandCursor); | |
|
1015 | impl->enterPlotDrag(event->pos()); | |
|
1016 | } | |
|
1017 | 1063 | } |
|
1018 | 1064 | } |
|
1019 | //event->accept(); | |
|
1065 | // event->accept(); | |
|
1020 | 1066 | QWidget::mousePressEvent(event); |
|
1021 | 1067 | } |
|
1022 | 1068 | |
|
1023 |
void VisualizationGraphWidget::mouseDoubleClickEvent(QMouseEvent |
|
|
1069 | void VisualizationGraphWidget::mouseDoubleClickEvent(QMouseEvent* event) | |
|
1024 | 1070 | { |
|
1025 | 1071 | impl->m_RenderingDelegate->onMouseDoubleClick(event); |
|
1026 | 1072 | } |
|
1027 | 1073 | |
|
1028 |
void VisualizationGraphWidget::keyReleaseEvent(QKeyEvent |
|
|
1074 | void VisualizationGraphWidget::keyReleaseEvent(QKeyEvent* event) | |
|
1029 | 1075 | { |
|
1030 |
switch (event->key()) |
|
|
1076 | switch (event->key()) | |
|
1077 | { | |
|
1031 | 1078 | case Qt::Key_Control: |
|
1032 | 1079 | event->accept(); |
|
1033 | 1080 | break; |
|
1034 | 1081 | case Qt::Key_Shift: |
|
1035 | 1082 | event->accept(); |
|
1036 | 1083 | break; |
|
1037 | 1084 | default: |
|
1038 | 1085 | QWidget::keyReleaseEvent(event); |
|
1039 | 1086 | break; |
|
1040 | 1087 | } |
|
1041 | 1088 | setCursor(Qt::ArrowCursor); |
|
1042 | //event->accept(); | |
|
1089 | // event->accept(); | |
|
1043 | 1090 | } |
|
1044 | 1091 | |
|
1045 |
void VisualizationGraphWidget::keyPressEvent(QKeyEvent |
|
|
1092 | void VisualizationGraphWidget::keyPressEvent(QKeyEvent* event) | |
|
1046 | 1093 | { |
|
1047 |
switch (event->key()) |
|
|
1094 | switch (event->key()) | |
|
1095 | { | |
|
1048 | 1096 | case Qt::Key_Control: |
|
1049 | 1097 | setCursor(Qt::CrossCursor); |
|
1050 | 1098 | break; |
|
1051 | 1099 | case Qt::Key_Shift: |
|
1052 | 1100 | break; |
|
1053 | 1101 | case Qt::Key_M: |
|
1054 | 1102 | impl->rescaleY(); |
|
1055 | 1103 | impl->m_plot->replot(QCustomPlot::rpQueuedReplot); |
|
1056 | 1104 | break; |
|
1057 | 1105 | case Qt::Key_Left: |
|
1058 |
if (event->modifiers() != Qt::ControlModifier) |
|
|
1106 | if (event->modifiers() != Qt::ControlModifier) | |
|
1107 | { | |
|
1059 | 1108 | move(-0.1, Qt::Horizontal); |
|
1060 | 1109 | } |
|
1061 |
else |
|
|
1110 | else | |
|
1111 | { | |
|
1062 | 1112 | zoom(2, this->width() / 2, Qt::Horizontal); |
|
1063 | 1113 | } |
|
1064 | 1114 | break; |
|
1065 | 1115 | case Qt::Key_Right: |
|
1066 |
if (event->modifiers() != Qt::ControlModifier) |
|
|
1116 | if (event->modifiers() != Qt::ControlModifier) | |
|
1117 | { | |
|
1067 | 1118 | move(0.1, Qt::Horizontal); |
|
1068 | 1119 | } |
|
1069 |
else |
|
|
1120 | else | |
|
1121 | { | |
|
1070 | 1122 | zoom(0.5, this->width() / 2, Qt::Horizontal); |
|
1071 | 1123 | } |
|
1072 | 1124 | break; |
|
1073 | 1125 | case Qt::Key_Up: |
|
1074 |
if (event->modifiers() != Qt::ControlModifier) |
|
|
1126 | if (event->modifiers() != Qt::ControlModifier) | |
|
1127 | { | |
|
1075 | 1128 | move(0.1, Qt::Vertical); |
|
1076 | 1129 | } |
|
1077 |
else |
|
|
1130 | else | |
|
1131 | { | |
|
1078 | 1132 | zoom(0.5, this->height() / 2, Qt::Vertical); |
|
1079 | 1133 | } |
|
1080 | 1134 | break; |
|
1081 | 1135 | case Qt::Key_Down: |
|
1082 |
if (event->modifiers() != Qt::ControlModifier) |
|
|
1136 | if (event->modifiers() != Qt::ControlModifier) | |
|
1137 | { | |
|
1083 | 1138 | move(-0.1, Qt::Vertical); |
|
1084 | 1139 | } |
|
1085 |
else |
|
|
1140 | else | |
|
1141 | { | |
|
1086 | 1142 | zoom(2, this->height() / 2, Qt::Vertical); |
|
1087 | 1143 | } |
|
1088 | 1144 | break; |
|
1089 | 1145 | default: |
|
1090 | 1146 | QWidget::keyPressEvent(event); |
|
1091 | 1147 | break; |
|
1092 | 1148 | } |
|
1093 | 1149 | } |
|
1094 | 1150 | |
|
1095 |
QCustomPlot |
|
|
1151 | QCustomPlot& VisualizationGraphWidget::plot() const noexcept | |
|
1096 | 1152 | { |
|
1097 | 1153 | return *impl->m_plot; |
|
1098 | 1154 | } |
|
1099 | 1155 | |
|
1100 |
void VisualizationGraphWidget::onGraphMenuRequested(const QPoint |
|
|
1156 | void VisualizationGraphWidget::onGraphMenuRequested(const QPoint& pos) noexcept | |
|
1101 | 1157 | { |
|
1102 | QMenu graphMenu{}; | |
|
1158 | QMenu graphMenu {}; | |
|
1103 | 1159 | |
|
1104 | 1160 | // Iterates on variables (unique keys) |
|
1105 | 1161 | for (auto it = impl->m_VariableToPlotMultiMap.cbegin(), |
|
1106 | end = impl->m_VariableToPlotMultiMap.cend(); | |
|
1107 |
it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) |
|
|
1162 | end = impl->m_VariableToPlotMultiMap.cend(); | |
|
1163 | it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) | |
|
1164 | { | |
|
1108 | 1165 | // 'Remove variable' action |
|
1109 | 1166 | graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()), |
|
1110 |
|
|
|
1167 | [this, var = it->first]() { removeVariable(var); }); | |
|
1111 | 1168 | } |
|
1112 | 1169 | |
|
1113 |
if (!impl->m_ZoomStack.isEmpty()) |
|
|
1114 | if (!graphMenu.isEmpty()) { | |
|
1170 | if (!impl->m_ZoomStack.isEmpty()) | |
|
1171 | { | |
|
1172 | if (!graphMenu.isEmpty()) | |
|
1173 | { | |
|
1115 | 1174 | graphMenu.addSeparator(); |
|
1116 | 1175 | } |
|
1117 | 1176 | |
|
1118 | 1177 | graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); }); |
|
1119 | 1178 | } |
|
1120 | 1179 | |
|
1121 | 1180 | // Selection Zone Actions |
|
1122 | 1181 | auto selectionZoneItem = impl->selectionZoneAt(pos); |
|
1123 |
if (selectionZoneItem) |
|
|
1182 | if (selectionZoneItem) | |
|
1183 | { | |
|
1124 | 1184 | auto selectedItems = parentVisualizationWidget()->selectionZoneManager().selectedItems(); |
|
1125 | 1185 | selectedItems.removeAll(selectionZoneItem); |
|
1126 | 1186 | selectedItems.prepend(selectionZoneItem); // Put the current selection zone first |
|
1127 | 1187 | |
|
1128 | 1188 | auto zoneActions = sqpApp->actionsGuiController().selectionZoneActions(); |
|
1129 |
if (!zoneActions.isEmpty() && !graphMenu.isEmpty()) |
|
|
1189 | if (!zoneActions.isEmpty() && !graphMenu.isEmpty()) | |
|
1190 | { | |
|
1130 | 1191 | graphMenu.addSeparator(); |
|
1131 | 1192 | } |
|
1132 | 1193 | |
|
1133 |
QHash<QString, QMenu |
|
|
1194 | QHash<QString, QMenu*> subMenus; | |
|
1134 | 1195 | QHash<QString, bool> subMenusEnabled; |
|
1135 |
QHash<QString, FilteringAction |
|
|
1196 | QHash<QString, FilteringAction*> filteredMenu; | |
|
1136 | 1197 | |
|
1137 |
for (auto zoneAction : zoneActions) |
|
|
1198 | for (auto zoneAction : zoneActions) | |
|
1199 | { | |
|
1138 | 1200 | |
|
1139 | 1201 | auto isEnabled = zoneAction->isEnabled(selectedItems); |
|
1140 | 1202 | |
|
1141 | 1203 | auto menu = &graphMenu; |
|
1142 | 1204 | QString menuPath; |
|
1143 |
for (auto subMenuName : zoneAction->subMenuList()) |
|
|
1205 | for (auto subMenuName : zoneAction->subMenuList()) | |
|
1206 | { | |
|
1144 | 1207 | menuPath += '/'; |
|
1145 | 1208 | menuPath += subMenuName; |
|
1146 | 1209 | |
|
1147 |
if (!subMenus.contains(menuPath)) |
|
|
1210 | if (!subMenus.contains(menuPath)) | |
|
1211 | { | |
|
1148 | 1212 | menu = menu->addMenu(subMenuName); |
|
1149 | 1213 | subMenus[menuPath] = menu; |
|
1150 | 1214 | subMenusEnabled[menuPath] = isEnabled; |
|
1151 | 1215 | } |
|
1152 |
else |
|
|
1216 | else | |
|
1217 | { | |
|
1153 | 1218 | menu = subMenus.value(menuPath); |
|
1154 |
if (isEnabled) |
|
|
1219 | if (isEnabled) | |
|
1220 | { | |
|
1155 | 1221 | // The sub menu is enabled if at least one of its actions is enabled |
|
1156 | 1222 | subMenusEnabled[menuPath] = true; |
|
1157 | 1223 | } |
|
1158 | 1224 | } |
|
1159 | 1225 | } |
|
1160 | 1226 | |
|
1161 |
FilteringAction |
|
|
1162 |
if (sqpApp->actionsGuiController().isMenuFiltered(zoneAction->subMenuList())) |
|
|
1227 | FilteringAction* filterAction = nullptr; | |
|
1228 | if (sqpApp->actionsGuiController().isMenuFiltered(zoneAction->subMenuList())) | |
|
1229 | { | |
|
1163 | 1230 | filterAction = filteredMenu.value(menuPath); |
|
1164 |
if (!filterAction) |
|
|
1165 | filterAction = new FilteringAction{this}; | |
|
1231 | if (!filterAction) | |
|
1232 | { | |
|
1233 | filterAction = new FilteringAction { this }; | |
|
1166 | 1234 | filteredMenu[menuPath] = filterAction; |
|
1167 | 1235 | menu->addAction(filterAction); |
|
1168 | 1236 | } |
|
1169 | 1237 | } |
|
1170 | 1238 | |
|
1171 | 1239 | auto action = menu->addAction(zoneAction->name()); |
|
1172 | 1240 | action->setEnabled(isEnabled); |
|
1173 | 1241 | action->setShortcut(zoneAction->displayedShortcut()); |
|
1174 | 1242 | QObject::connect(action, &QAction::triggered, |
|
1175 |
|
|
|
1243 | [zoneAction, selectedItems]() { zoneAction->execute(selectedItems); }); | |
|
1176 | 1244 | |
|
1177 |
if (filterAction && zoneAction->isFilteringAllowed()) |
|
|
1245 | if (filterAction && zoneAction->isFilteringAllowed()) | |
|
1246 | { | |
|
1178 | 1247 | filterAction->addActionToFilter(action); |
|
1179 | 1248 | } |
|
1180 | 1249 | } |
|
1181 | 1250 | |
|
1182 |
for (auto it = subMenus.cbegin(); it != subMenus.cend(); ++it) |
|
|
1251 | for (auto it = subMenus.cbegin(); it != subMenus.cend(); ++it) | |
|
1252 | { | |
|
1183 | 1253 | it.value()->setEnabled(subMenusEnabled[it.key()]); |
|
1184 | 1254 | } |
|
1185 | 1255 | } |
|
1186 | 1256 | |
|
1187 |
if (!graphMenu.isEmpty()) |
|
|
1257 | if (!graphMenu.isEmpty()) | |
|
1258 | { | |
|
1188 | 1259 | graphMenu.exec(QCursor::pos()); |
|
1189 | 1260 | } |
|
1190 | 1261 | } |
|
1191 | 1262 | |
|
1192 |
void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent |
|
|
1263 | void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent* event) noexcept | |
|
1193 | 1264 | { |
|
1194 | 1265 | impl->m_RenderingDelegate->onMouseDoubleClick(event); |
|
1195 | 1266 | } |
|
1196 | 1267 | |
|
1197 |
void VisualizationGraphWidget::onMouseMove(QMouseEvent |
|
|
1268 | void VisualizationGraphWidget::onMouseMove(QMouseEvent* event) noexcept | |
|
1198 | 1269 | { |
|
1199 | 1270 | // Handles plot rendering when mouse is moving |
|
1200 | 1271 | impl->m_RenderingDelegate->updateTooltip(event); |
|
1201 | 1272 | |
|
1202 | 1273 | auto axisPos = impl->posToAxisPos(event->pos()); |
|
1203 | 1274 | |
|
1204 | 1275 | // Zoom box and zone drawing |
|
1205 |
if (impl->m_DrawingZoomRect) |
|
|
1276 | if (impl->m_DrawingZoomRect) | |
|
1277 | { | |
|
1206 | 1278 | impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos); |
|
1207 | 1279 | } |
|
1208 |
else if (impl->m_DrawingZone) |
|
|
1280 | else if (impl->m_DrawingZone) | |
|
1281 | { | |
|
1209 | 1282 | impl->m_DrawingZone->setEnd(axisPos.x()); |
|
1210 | 1283 | } |
|
1211 | 1284 | |
|
1212 | 1285 | // Cursor |
|
1213 |
if (auto parentZone = parentZoneWidget()) |
|
|
1214 | if (impl->pointIsInAxisRect(axisPos, plot())) { | |
|
1286 | if (auto parentZone = parentZoneWidget()) | |
|
1287 | { | |
|
1288 | if (impl->pointIsInAxisRect(axisPos, plot())) | |
|
1289 | { | |
|
1215 | 1290 | parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this); |
|
1216 | 1291 | } |
|
1217 |
else |
|
|
1292 | else | |
|
1293 | { | |
|
1218 | 1294 | parentZone->notifyMouseLeaveGraph(this); |
|
1219 | 1295 | } |
|
1220 | 1296 | } |
|
1221 | 1297 | |
|
1222 | 1298 | // Search for the selection zone under the mouse |
|
1223 | 1299 | auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos()); |
|
1224 | 1300 | if (selectionZoneItemUnderCursor && !impl->m_DrawingZone |
|
1225 |
&& sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) |
|
|
1301 | && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) | |
|
1302 | { | |
|
1226 | 1303 | |
|
1227 | 1304 | // Sets the appropriate cursor shape |
|
1228 | 1305 | auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos()); |
|
1229 | 1306 | setCursor(cursorShape); |
|
1230 | 1307 | |
|
1231 | 1308 | // Manages the hovered zone |
|
1232 |
if (selectionZoneItemUnderCursor != impl->m_HoveredZone) |
|
|
1233 | if (impl->m_HoveredZone) { | |
|
1309 | if (selectionZoneItemUnderCursor != impl->m_HoveredZone) | |
|
1310 | { | |
|
1311 | if (impl->m_HoveredZone) | |
|
1312 | { | |
|
1234 | 1313 | impl->m_HoveredZone->setHovered(false); |
|
1235 | 1314 | } |
|
1236 | 1315 | selectionZoneItemUnderCursor->setHovered(true); |
|
1237 | 1316 | impl->m_HoveredZone = selectionZoneItemUnderCursor; |
|
1238 | 1317 | plot().replot(QCustomPlot::rpQueuedReplot); |
|
1239 | 1318 | } |
|
1240 | 1319 | } |
|
1241 |
else |
|
|
1320 | else | |
|
1321 | { | |
|
1242 | 1322 | // There is no zone under the mouse or the interaction mode is not "selection zones" |
|
1243 |
if (impl->m_HoveredZone) |
|
|
1323 | if (impl->m_HoveredZone) | |
|
1324 | { | |
|
1244 | 1325 | impl->m_HoveredZone->setHovered(false); |
|
1245 | 1326 | impl->m_HoveredZone = nullptr; |
|
1246 | 1327 | } |
|
1247 | 1328 | |
|
1248 | 1329 | setCursor(Qt::ArrowCursor); |
|
1249 | 1330 | } |
|
1250 | 1331 | |
|
1251 | 1332 | impl->m_HasMovedMouse = true; |
|
1252 | 1333 | VisualizationDragWidget::mouseMoveEvent(event); |
|
1253 | 1334 | } |
|
1254 | 1335 | |
|
1255 |
void VisualizationGraphWidget::onMouseWheel(QWheelEvent |
|
|
1336 | void VisualizationGraphWidget::onMouseWheel(QWheelEvent* event) noexcept | |
|
1256 | 1337 | { |
|
1257 | 1338 | // Processes event only if the wheel occurs on axis rect |
|
1258 |
if (!dynamic_cast<QCPAxisRect |
|
|
1339 | if (!dynamic_cast<QCPAxisRect*>(impl->m_plot->layoutElementAt(event->posF()))) | |
|
1340 | { | |
|
1259 | 1341 | return; |
|
1260 | 1342 | } |
|
1261 | 1343 | |
|
1262 | 1344 | auto value = event->angleDelta().x() + event->angleDelta().y(); |
|
1263 |
if (value != 0) |
|
|
1345 | if (value != 0) | |
|
1346 | { | |
|
1264 | 1347 | |
|
1265 | 1348 | auto direction = value > 0 ? 1.0 : -1.0; |
|
1266 | 1349 | auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER); |
|
1267 | 1350 | auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER); |
|
1268 | 1351 | impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER); |
|
1269 | 1352 | |
|
1270 | auto zoomOrientations = QFlags<Qt::Orientation>{}; | |
|
1353 | auto zoomOrientations = QFlags<Qt::Orientation> {}; | |
|
1271 | 1354 | zoomOrientations.setFlag(Qt::Horizontal, isZoomX); |
|
1272 | 1355 | zoomOrientations.setFlag(Qt::Vertical, isZoomY); |
|
1273 | 1356 | |
|
1274 | 1357 | impl->m_plot->axisRect()->setRangeZoom(zoomOrientations); |
|
1275 | 1358 | |
|
1276 |
if (!isZoomX && !isZoomY) |
|
|
1359 | if (!isZoomX && !isZoomY) | |
|
1360 | { | |
|
1277 | 1361 | auto axis = plot().axisRect()->axis(QCPAxis::atBottom); |
|
1278 | 1362 | auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0)); |
|
1279 | 1363 | |
|
1280 | 1364 | axis->setRange(axis->range() + diff); |
|
1281 | 1365 | |
|
1282 |
if (plot().noAntialiasingOnDrag()) |
|
|
1366 | if (plot().noAntialiasingOnDrag()) | |
|
1367 | { | |
|
1283 | 1368 | plot().setNotAntialiasedElements(QCP::aeAll); |
|
1284 | 1369 | } |
|
1285 | 1370 | |
|
1286 | 1371 | // plot().replot(QCustomPlot::rpQueuedReplot); |
|
1287 | 1372 | } |
|
1288 | 1373 | } |
|
1289 | 1374 | } |
|
1290 | 1375 | |
|
1291 |
void VisualizationGraphWidget::onMousePress(QMouseEvent |
|
|
1376 | void VisualizationGraphWidget::onMousePress(QMouseEvent* event) noexcept | |
|
1292 | 1377 | { |
|
1293 | 1378 | auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER); |
|
1294 | 1379 | auto isSelectionZoneMode |
|
1295 | 1380 | = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones; |
|
1296 | 1381 | auto isLeftClick = event->buttons().testFlag(Qt::LeftButton); |
|
1297 | 1382 | |
|
1298 |
if (!isDragDropClick && isLeftClick) |
|
|
1299 | if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) { | |
|
1383 | if (!isDragDropClick && isLeftClick) | |
|
1384 | { | |
|
1385 | if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) | |
|
1386 | { | |
|
1300 | 1387 | // Starts a zoom box |
|
1301 | 1388 | impl->startDrawingRect(event->pos()); |
|
1302 | 1389 | } |
|
1303 |
else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr) |
|
|
1390 | else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr) | |
|
1391 | { | |
|
1304 | 1392 | // Starts a new selection zone |
|
1305 | 1393 | auto zoneAtPos = impl->selectionZoneAt(event->pos()); |
|
1306 |
if (!zoneAtPos) |
|
|
1394 | if (!zoneAtPos) | |
|
1395 | { | |
|
1307 | 1396 | impl->startDrawingZone(event->pos()); |
|
1308 | 1397 | } |
|
1309 | 1398 | } |
|
1310 | 1399 | } |
|
1311 | 1400 | |
|
1312 | 1401 | |
|
1313 | 1402 | // Allows zone edition only in selection zone mode without drag&drop |
|
1314 | 1403 | impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick); |
|
1315 | 1404 | |
|
1316 | 1405 | // Selection / Deselection |
|
1317 |
if (isSelectionZoneMode) |
|
|
1406 | if (isSelectionZoneMode) | |
|
1407 | { | |
|
1318 | 1408 | auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER); |
|
1319 | 1409 | auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos()); |
|
1320 | 1410 | |
|
1321 | 1411 | |
|
1322 | 1412 | if (selectionZoneItemUnderCursor && !selectionZoneItemUnderCursor->selected() |
|
1323 |
&& !isMultiSelectionClick) |
|
|
1413 | && !isMultiSelectionClick) | |
|
1414 | { | |
|
1324 | 1415 | parentVisualizationWidget()->selectionZoneManager().select( |
|
1325 | {selectionZoneItemUnderCursor}); | |
|
1416 | { selectionZoneItemUnderCursor }); | |
|
1326 | 1417 | } |
|
1327 |
else if (!selectionZoneItemUnderCursor && !isMultiSelectionClick && isLeftClick) |
|
|
1418 | else if (!selectionZoneItemUnderCursor && !isMultiSelectionClick && isLeftClick) | |
|
1419 | { | |
|
1328 | 1420 | parentVisualizationWidget()->selectionZoneManager().clearSelection(); |
|
1329 | 1421 | } |
|
1330 |
else |
|
|
1422 | else | |
|
1423 | { | |
|
1331 | 1424 | // No selection change |
|
1332 | 1425 | } |
|
1333 | 1426 | |
|
1334 |
if (selectionZoneItemUnderCursor && isLeftClick) |
|
|
1427 | if (selectionZoneItemUnderCursor && isLeftClick) | |
|
1428 | { | |
|
1335 | 1429 | selectionZoneItemUnderCursor->setAssociatedEditedZones( |
|
1336 | 1430 | parentVisualizationWidget()->selectionZoneManager().selectedItems()); |
|
1337 | 1431 | } |
|
1338 | 1432 | } |
|
1339 | 1433 | |
|
1340 | 1434 | |
|
1341 | 1435 | impl->m_HasMovedMouse = false; |
|
1342 | 1436 | VisualizationDragWidget::mousePressEvent(event); |
|
1343 | 1437 | } |
|
1344 | 1438 | |
|
1345 |
void VisualizationGraphWidget::onMouseRelease(QMouseEvent |
|
|
1439 | void VisualizationGraphWidget::onMouseRelease(QMouseEvent* event) noexcept | |
|
1346 | 1440 | { |
|
1347 |
if (impl->m_DrawingZoomRect) |
|
|
1441 | if (impl->m_DrawingZoomRect) | |
|
1442 | { | |
|
1348 | 1443 | |
|
1349 | 1444 | auto axisX = plot().axisRect()->axis(QCPAxis::atBottom); |
|
1350 | 1445 | auto axisY = plot().axisRect()->axis(QCPAxis::atLeft); |
|
1351 | 1446 | |
|
1352 | auto newAxisXRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().x(), | |
|
1353 |
|
|
|
1447 | auto newAxisXRange = QCPRange { impl->m_DrawingZoomRect->topLeft->coords().x(), | |
|
1448 | impl->m_DrawingZoomRect->bottomRight->coords().x() }; | |
|
1354 | 1449 | |
|
1355 | auto newAxisYRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().y(), | |
|
1356 |
|
|
|
1450 | auto newAxisYRange = QCPRange { impl->m_DrawingZoomRect->topLeft->coords().y(), | |
|
1451 | impl->m_DrawingZoomRect->bottomRight->coords().y() }; | |
|
1357 | 1452 | |
|
1358 | 1453 | impl->removeDrawingRect(); |
|
1359 | 1454 | |
|
1360 | 1455 | if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0) |
|
1361 |
&& newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) |
|
|
1456 | && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) | |
|
1457 | { | |
|
1362 | 1458 | impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range())); |
|
1363 | 1459 | axisX->setRange(newAxisXRange); |
|
1364 | 1460 | axisY->setRange(newAxisYRange); |
|
1365 | 1461 | |
|
1366 | 1462 | plot().replot(QCustomPlot::rpQueuedReplot); |
|
1367 | 1463 | } |
|
1368 | 1464 | } |
|
1369 | 1465 | |
|
1370 | 1466 | impl->endDrawingZone(); |
|
1371 | 1467 | |
|
1372 | 1468 | // Selection / Deselection |
|
1373 | 1469 | auto isSelectionZoneMode |
|
1374 | 1470 | = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones; |
|
1375 |
if (isSelectionZoneMode) |
|
|
1471 | if (isSelectionZoneMode) | |
|
1472 | { | |
|
1376 | 1473 | auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER); |
|
1377 | 1474 | auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos()); |
|
1378 | 1475 | if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton |
|
1379 |
&& !impl->m_HasMovedMouse) |
|
|
1476 | && !impl->m_HasMovedMouse) | |
|
1477 | { | |
|
1380 | 1478 | |
|
1381 | 1479 | auto zonesUnderCursor = impl->selectionZonesAt(event->pos(), plot()); |
|
1382 |
if (zonesUnderCursor.count() > 1) |
|
|
1480 | if (zonesUnderCursor.count() > 1) | |
|
1481 | { | |
|
1383 | 1482 | // There are multiple zones under the mouse. |
|
1384 | 1483 | // Performs the selection with a selection dialog. |
|
1385 | VisualizationMultiZoneSelectionDialog dialog{this}; | |
|
1484 | VisualizationMultiZoneSelectionDialog dialog { this }; | |
|
1386 | 1485 | dialog.setZones(zonesUnderCursor); |
|
1387 | 1486 | dialog.move(mapToGlobal(event->pos() - QPoint(dialog.width() / 2, 20))); |
|
1388 | 1487 | dialog.activateWindow(); |
|
1389 | 1488 | dialog.raise(); |
|
1390 |
if (dialog.exec() == QDialog::Accepted) |
|
|
1489 | if (dialog.exec() == QDialog::Accepted) | |
|
1490 | { | |
|
1391 | 1491 | auto selection = dialog.selectedZones(); |
|
1392 | 1492 | |
|
1393 |
if (!isMultiSelectionClick) |
|
|
1493 | if (!isMultiSelectionClick) | |
|
1494 | { | |
|
1394 | 1495 | parentVisualizationWidget()->selectionZoneManager().clearSelection(); |
|
1395 | 1496 | } |
|
1396 | 1497 | |
|
1397 |
for (auto it = selection.cbegin(); it != selection.cend(); ++it) |
|
|
1498 | for (auto it = selection.cbegin(); it != selection.cend(); ++it) | |
|
1499 | { | |
|
1398 | 1500 | auto zone = it.key(); |
|
1399 | 1501 | auto isSelected = it.value(); |
|
1400 |
parentVisualizationWidget()->selectionZoneManager().setSelected( |
|
|
1401 | isSelected); | |
|
1502 | parentVisualizationWidget()->selectionZoneManager().setSelected( | |
|
1503 | zone, isSelected); | |
|
1402 | 1504 | |
|
1403 |
if (isSelected) |
|
|
1505 | if (isSelected) | |
|
1506 | { | |
|
1404 | 1507 | // Puts the zone on top of the stack so it can be moved or resized |
|
1405 | 1508 | impl->moveSelectionZoneOnTop(zone, plot()); |
|
1406 | 1509 | } |
|
1407 | 1510 | } |
|
1408 | 1511 | } |
|
1409 | 1512 | } |
|
1410 |
else |
|
|
1411 | if (!isMultiSelectionClick) { | |
|
1513 | else | |
|
1514 | { | |
|
1515 | if (!isMultiSelectionClick) | |
|
1516 | { | |
|
1412 | 1517 | parentVisualizationWidget()->selectionZoneManager().select( |
|
1413 | {selectionZoneItemUnderCursor}); | |
|
1518 | { selectionZoneItemUnderCursor }); | |
|
1414 | 1519 | impl->moveSelectionZoneOnTop(selectionZoneItemUnderCursor, plot()); |
|
1415 | 1520 | } |
|
1416 |
else |
|
|
1521 | else | |
|
1522 | { | |
|
1417 | 1523 | parentVisualizationWidget()->selectionZoneManager().setSelected( |
|
1418 |
selectionZoneItemUnderCursor, |
|
|
1419 | || event->button() == Qt::RightButton); | |
|
1524 | selectionZoneItemUnderCursor, | |
|
1525 | !selectionZoneItemUnderCursor->selected() | |
|
1526 | || event->button() == Qt::RightButton); | |
|
1420 | 1527 | } |
|
1421 | 1528 | } |
|
1422 | 1529 | } |
|
1423 |
else |
|
|
1530 | else | |
|
1531 | { | |
|
1424 | 1532 | // No selection change |
|
1425 | 1533 | } |
|
1426 | 1534 | } |
|
1427 | 1535 | } |
|
1428 | 1536 | |
|
1429 | 1537 | void VisualizationGraphWidget::onDataCacheVariableUpdated() |
|
1430 | 1538 | { |
|
1431 | 1539 | auto graphRange = impl->m_plot->xAxis->range(); |
|
1432 | auto dateTime = DateTimeRange{graphRange.lower, graphRange.upper}; | |
|
1540 | auto dateTime = DateTimeRange { graphRange.lower, graphRange.upper }; | |
|
1433 | 1541 | |
|
1434 |
for (auto |
|
|
1542 | for (auto& variableEntry : impl->m_VariableToPlotMultiMap) | |
|
1543 | { | |
|
1435 | 1544 | auto variable = variableEntry.first; |
|
1436 | 1545 | qCDebug(LOG_VisualizationGraphWidget()) |
|
1437 | 1546 | << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range(); |
|
1438 | 1547 | qCDebug(LOG_VisualizationGraphWidget()) |
|
1439 | 1548 | << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime; |
|
1440 |
if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) |
|
|
1549 | if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) | |
|
1550 | { | |
|
1441 | 1551 | impl->updateData(variableEntry.second, variable, variable->range()); |
|
1442 | 1552 | } |
|
1443 | 1553 | } |
|
1444 | 1554 | } |
|
1445 | 1555 | |
|
1446 |
void VisualizationGraphWidget::onUpdateVarDisplaying( |
|
|
1447 | const DateTimeRange &range) | |
|
1556 | void VisualizationGraphWidget::onUpdateVarDisplaying( | |
|
1557 | std::shared_ptr<Variable2> variable, const DateTimeRange& range) | |
|
1448 | 1558 | { |
|
1449 | 1559 | auto it = impl->m_VariableToPlotMultiMap.find(variable); |
|
1450 |
if (it != impl->m_VariableToPlotMultiMap.end()) |
|
|
1560 | if (it != impl->m_VariableToPlotMultiMap.end()) | |
|
1561 | { | |
|
1451 | 1562 | impl->updateData(it->second, variable, range); |
|
1452 | 1563 | } |
|
1453 | 1564 | } |
|
1454 | 1565 | |
|
1455 | 1566 | void VisualizationGraphWidget::variableUpdated(QUuid id) |
|
1456 | 1567 | { |
|
1457 |
for (auto |
|
|
1458 | if (var->ID() == id) { | |
|
1568 | for (auto& [var, plotables] : impl->m_VariableToPlotMultiMap) | |
|
1569 | { | |
|
1570 | if (var->ID() == id) | |
|
1571 | { | |
|
1459 | 1572 | impl->updateData(plotables, var, this->graphRange()); |
|
1460 | 1573 | } |
|
1461 | 1574 | } |
|
1462 | 1575 | } |
|
1463 | 1576 | |
|
1464 |
void VisualizationGraphWidget::variableDeleted(const std::shared_ptr<Variable> |
|
|
1577 | void VisualizationGraphWidget::variableDeleted(const std::shared_ptr<Variable2>& variable) | |
|
1465 | 1578 | { |
|
1466 | 1579 | this->removeVariable(variable); |
|
1467 | 1580 | } |
@@ -1,389 +1,415 | |||
|
1 | 1 | #include "Visualization/VisualizationTabWidget.h" |
|
2 | 2 | #include "Visualization/IVisualizationWidgetVisitor.h" |
|
3 | 3 | #include "ui_VisualizationTabWidget.h" |
|
4 | 4 | |
|
5 | 5 | #include "Visualization/VisualizationGraphWidget.h" |
|
6 | 6 | #include "Visualization/VisualizationZoneWidget.h" |
|
7 | 7 | |
|
8 | 8 | #include "Visualization/MacScrollBarStyle.h" |
|
9 | 9 | |
|
10 | 10 | #include "DataSource/DataSourceController.h" |
|
11 | 11 | #include "Variable/VariableController2.h" |
|
12 | 12 | |
|
13 | 13 | #include "Common/MimeTypesDef.h" |
|
14 | 14 | |
|
15 | 15 | #include "DragAndDrop/DragDropGuiController.h" |
|
16 | 16 | #include "SqpApplication.h" |
|
17 | 17 | |
|
18 | 18 | Q_LOGGING_CATEGORY(LOG_VisualizationTabWidget, "VisualizationTabWidget") |
|
19 | 19 | |
|
20 |
namespace |
|
|
20 | namespace | |
|
21 | { | |
|
21 | 22 | |
|
22 | 23 | /** |
|
23 | 24 | * Applies a function to all zones of the tab represented by its layout |
|
24 | 25 | * @param layout the layout that contains zones |
|
25 | 26 | * @param fun the function to apply to each zone |
|
26 | 27 | */ |
|
27 | 28 | template <typename Fun> |
|
28 |
void processZones(QLayout |
|
|
29 | void processZones(QLayout& layout, Fun fun) | |
|
29 | 30 | { |
|
30 |
for (auto i = 0; i < layout.count(); ++i) |
|
|
31 | if (auto item = layout.itemAt(i)) { | |
|
31 | for (auto i = 0; i < layout.count(); ++i) | |
|
32 | { | |
|
33 | if (auto item = layout.itemAt(i)) | |
|
34 | { | |
|
32 | 35 | if (auto visualizationZoneWidget |
|
33 |
= qobject_cast<VisualizationZoneWidget |
|
|
36 | = qobject_cast<VisualizationZoneWidget*>(item->widget())) | |
|
37 | { | |
|
34 | 38 | fun(*visualizationZoneWidget); |
|
35 | 39 | } |
|
36 | 40 | } |
|
37 | 41 | } |
|
38 | 42 | } |
|
39 | 43 | |
|
40 | 44 | /// Generates a default name for a new zone, according to the number of zones already displayed in |
|
41 | 45 | /// the tab |
|
42 |
QString defaultZoneName(QLayout |
|
|
46 | QString defaultZoneName(QLayout& layout) | |
|
43 | 47 | { |
|
44 | 48 | QSet<QString> existingNames; |
|
45 |
processZones( |
|
|
46 |
|
|
|
49 | processZones( | |
|
50 | layout, [&existingNames](auto& zoneWidget) { existingNames.insert(zoneWidget.name()); }); | |
|
47 | 51 | |
|
48 | 52 | int zoneNum = 1; |
|
49 | 53 | QString name; |
|
50 |
do |
|
|
54 | do | |
|
55 | { | |
|
51 | 56 | name = QObject::tr("Zone ").append(QString::number(zoneNum)); |
|
52 | 57 | ++zoneNum; |
|
53 | 58 | } while (existingNames.contains(name)); |
|
54 | 59 | |
|
55 | 60 | return name; |
|
56 | 61 | } |
|
57 | 62 | |
|
58 | 63 | } // namespace |
|
59 | 64 | |
|
60 |
struct VisualizationTabWidget::VisualizationTabWidgetPrivate |
|
|
61 | explicit VisualizationTabWidgetPrivate(const QString &name) : m_Name{name} {} | |
|
65 | struct VisualizationTabWidget::VisualizationTabWidgetPrivate | |
|
66 | { | |
|
67 | explicit VisualizationTabWidgetPrivate(const QString& name) : m_Name { name } {} | |
|
62 | 68 | |
|
63 | 69 | QString m_Name; |
|
64 | 70 | |
|
65 | 71 | #ifdef Q_OS_MAC |
|
66 | 72 | std::unique_ptr<MacScrollBarStyle> m_MacScrollBarStyle = std::make_unique<MacScrollBarStyle>(); |
|
67 | 73 | #endif |
|
68 | 74 | |
|
69 |
void dropGraph(int index, VisualizationTabWidget |
|
|
70 |
void dropZone(int index, VisualizationTabWidget |
|
|
71 |
void dropVariables(const std::vector<std::shared_ptr<Variable> |
|
|
72 |
|
|
|
73 | void dropProducts(const QVariantList &productsMetaData, int index, | |
|
74 |
|
|
|
75 | void dropGraph(int index, VisualizationTabWidget* tabWidget); | |
|
76 | void dropZone(int index, VisualizationTabWidget* tabWidget); | |
|
77 | void dropVariables(const std::vector<std::shared_ptr<Variable2>>& variables, int index, | |
|
78 | VisualizationTabWidget* tabWidget); | |
|
79 | void dropProducts( | |
|
80 | const QVariantList& productsMetaData, int index, VisualizationTabWidget* tabWidget); | |
|
75 | 81 | }; |
|
76 | 82 | |
|
77 |
VisualizationTabWidget::VisualizationTabWidget(const QString |
|
|
78 |
: QWidget{parent} |
|
|
79 |
|
|
|
80 |
|
|
|
83 | VisualizationTabWidget::VisualizationTabWidget(const QString& name, QWidget* parent) | |
|
84 | : QWidget { parent } | |
|
85 | , ui { new Ui::VisualizationTabWidget } | |
|
86 | , impl { spimpl::make_unique_impl<VisualizationTabWidgetPrivate>(name) } | |
|
81 | 87 | { |
|
82 | 88 | ui->setupUi(this); |
|
83 | 89 | |
|
84 | 90 | #ifdef Q_OS_MAC |
|
85 | 91 | impl->m_MacScrollBarStyle->selfInstallOn(ui->scrollArea, true); |
|
86 | 92 | #endif |
|
87 | 93 | |
|
88 | 94 | ui->dragDropContainer->setPlaceHolderType(DragDropGuiController::PlaceHolderType::Zone, "Zone"); |
|
89 | 95 | ui->dragDropContainer->layout()->setContentsMargins(0, 0, 0, 12); |
|
90 | 96 | ui->dragDropContainer->layout()->setSpacing(0); |
|
91 |
ui->dragDropContainer->setMimeType( |
|
|
92 |
|
|
|
93 |
ui->dragDropContainer->setMimeType( |
|
|
94 |
|
|
|
95 |
ui->dragDropContainer->setMimeType( |
|
|
96 |
|
|
|
97 |
ui->dragDropContainer->setMimeType( |
|
|
98 |
|
|
|
97 | ui->dragDropContainer->setMimeType( | |
|
98 | MIME_TYPE_GRAPH, VisualizationDragDropContainer::DropBehavior::Inserted); | |
|
99 | ui->dragDropContainer->setMimeType( | |
|
100 | MIME_TYPE_ZONE, VisualizationDragDropContainer::DropBehavior::Inserted); | |
|
101 | ui->dragDropContainer->setMimeType( | |
|
102 | MIME_TYPE_VARIABLE_LIST, VisualizationDragDropContainer::DropBehavior::Inserted); | |
|
103 | ui->dragDropContainer->setMimeType( | |
|
104 | MIME_TYPE_PRODUCT_LIST, VisualizationDragDropContainer::DropBehavior::Inserted); | |
|
99 | 105 | |
|
100 | 106 | ui->dragDropContainer->setAcceptMimeDataFunction([this](auto mimeData) { |
|
101 |
return sqpApp->dragDropGuiController().checkMimeDataForVisualization( |
|
|
102 | ui->dragDropContainer); | |
|
107 | return sqpApp->dragDropGuiController().checkMimeDataForVisualization( | |
|
108 | mimeData, ui->dragDropContainer); | |
|
103 | 109 | }); |
|
104 | 110 | |
|
105 | 111 | connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredInContainer, this, |
|
106 |
|
|
|
112 | &VisualizationTabWidget::dropMimeData); | |
|
107 | 113 | |
|
108 | 114 | sqpApp->dragDropGuiController().addDragDropScrollArea(ui->scrollArea); |
|
109 | 115 | |
|
110 | 116 | // Widget is deleted when closed |
|
111 | 117 | setAttribute(Qt::WA_DeleteOnClose); |
|
112 | 118 | } |
|
113 | 119 | |
|
114 | 120 | VisualizationTabWidget::~VisualizationTabWidget() |
|
115 | 121 | { |
|
116 | 122 | sqpApp->dragDropGuiController().removeDragDropScrollArea(ui->scrollArea); |
|
117 | 123 | delete ui; |
|
118 | 124 | } |
|
119 | 125 | |
|
120 |
void VisualizationTabWidget::addZone(VisualizationZoneWidget |
|
|
126 | void VisualizationTabWidget::addZone(VisualizationZoneWidget* zoneWidget) | |
|
121 | 127 | { |
|
122 | 128 | ui->dragDropContainer->addDragWidget(zoneWidget); |
|
123 | 129 | } |
|
124 | 130 | |
|
125 |
void VisualizationTabWidget::insertZone(int index, VisualizationZoneWidget |
|
|
131 | void VisualizationTabWidget::insertZone(int index, VisualizationZoneWidget* zoneWidget) | |
|
126 | 132 | { |
|
127 | 133 | ui->dragDropContainer->insertDragWidget(index, zoneWidget); |
|
128 | 134 | } |
|
129 | 135 | |
|
130 | 136 | QStringList VisualizationTabWidget::availableZoneWidgets() const |
|
131 | 137 | { |
|
132 | 138 | QStringList zones; |
|
133 |
processZones( |
|
|
134 |
|
|
|
139 | processZones( | |
|
140 | tabLayout(), [&zones](VisualizationZoneWidget& zoneWidget) { zones << zoneWidget.name(); }); | |
|
135 | 141 | |
|
136 | 142 | return zones; |
|
137 | 143 | } |
|
138 | 144 | |
|
139 |
VisualizationZoneWidget |
|
|
145 | VisualizationZoneWidget* VisualizationTabWidget::getZoneWithName(const QString& zoneName) | |
|
140 | 146 | { |
|
141 |
VisualizationZoneWidget |
|
|
142 |
processZones(tabLayout(), [&zoneName, &result](VisualizationZoneWidget |
|
|
143 |
if (!result && zoneWidget.name() == zoneName) |
|
|
147 | VisualizationZoneWidget* result = nullptr; | |
|
148 | processZones(tabLayout(), [&zoneName, &result](VisualizationZoneWidget& zoneWidget) { | |
|
149 | if (!result && zoneWidget.name() == zoneName) | |
|
150 | { | |
|
144 | 151 | result = &zoneWidget; |
|
145 | 152 | } |
|
146 | 153 | }); |
|
147 | 154 | |
|
148 | 155 | return result; |
|
149 | 156 | } |
|
150 | 157 | |
|
151 |
VisualizationZoneWidget |
|
|
158 | VisualizationZoneWidget* VisualizationTabWidget::createZone(std::shared_ptr<Variable2> variable) | |
|
152 | 159 | { |
|
153 | return createZone({variable}, -1); | |
|
160 | return createZone({ variable }, -1); | |
|
154 | 161 | } |
|
155 | 162 | |
|
156 | VisualizationZoneWidget * | |
|
157 |
|
|
|
163 | VisualizationZoneWidget* VisualizationTabWidget::createZone( | |
|
164 | const std::vector<std::shared_ptr<Variable2>>& variables, int index) | |
|
158 | 165 | { |
|
159 | 166 | auto zoneWidget = createEmptyZone(index); |
|
160 | 167 | |
|
161 | 168 | // Creates a new graph into the zone |
|
162 | 169 | zoneWidget->createGraph(variables, index); |
|
163 | 170 | |
|
164 | 171 | return zoneWidget; |
|
165 | 172 | } |
|
166 | 173 | |
|
167 |
VisualizationZoneWidget |
|
|
174 | VisualizationZoneWidget* VisualizationTabWidget::createEmptyZone(int index) | |
|
168 | 175 | { |
|
169 | 176 | auto zoneWidget |
|
170 | = new VisualizationZoneWidget{defaultZoneName(*ui->dragDropContainer->layout()), this}; | |
|
177 | = new VisualizationZoneWidget { defaultZoneName(*ui->dragDropContainer->layout()), this }; | |
|
171 | 178 | this->insertZone(index, zoneWidget); |
|
172 | 179 | |
|
173 | 180 | return zoneWidget; |
|
174 | 181 | } |
|
175 | 182 | |
|
176 |
void VisualizationTabWidget::accept(IVisualizationWidgetVisitor |
|
|
183 | void VisualizationTabWidget::accept(IVisualizationWidgetVisitor* visitor) | |
|
177 | 184 | { |
|
178 |
if (visitor) |
|
|
185 | if (visitor) | |
|
186 | { | |
|
179 | 187 | visitor->visitEnter(this); |
|
180 | 188 | |
|
181 | 189 | // Apply visitor to zone children: widgets different from zones are not visited (no action) |
|
182 | processZones(tabLayout(), [visitor](VisualizationZoneWidget &zoneWidget) { | |
|
183 | zoneWidget.accept(visitor); | |
|
184 | }); | |
|
190 | processZones(tabLayout(), | |
|
191 | [visitor](VisualizationZoneWidget& zoneWidget) { zoneWidget.accept(visitor); }); | |
|
185 | 192 | |
|
186 | 193 | visitor->visitLeave(this); |
|
187 | 194 | } |
|
188 |
else |
|
|
195 | else | |
|
196 | { | |
|
189 | 197 | qCCritical(LOG_VisualizationTabWidget()) << tr("Can't visit widget : the visitor is null"); |
|
190 | 198 | } |
|
191 | 199 | } |
|
192 | 200 | |
|
193 |
bool VisualizationTabWidget::canDrop( |
|
|
201 | bool VisualizationTabWidget::canDrop(Variable2& variable) const | |
|
194 | 202 | { |
|
195 | 203 | // A tab can always accomodate a variable |
|
196 | 204 | Q_UNUSED(variable); |
|
197 | 205 | return true; |
|
198 | 206 | } |
|
199 | 207 | |
|
200 |
bool VisualizationTabWidget::contains( |
|
|
208 | bool VisualizationTabWidget::contains(Variable2& variable) const | |
|
201 | 209 | { |
|
202 | 210 | Q_UNUSED(variable); |
|
203 | 211 | return false; |
|
204 | 212 | } |
|
205 | 213 | |
|
206 | 214 | QString VisualizationTabWidget::name() const |
|
207 | 215 | { |
|
208 | 216 | return impl->m_Name; |
|
209 | 217 | } |
|
210 | 218 | |
|
211 |
void VisualizationTabWidget::closeEvent(QCloseEvent |
|
|
219 | void VisualizationTabWidget::closeEvent(QCloseEvent* event) | |
|
212 | 220 | { |
|
213 | 221 | // Closes zones in the tab |
|
214 |
processZones(tabLayout(), [](VisualizationZoneWidget |
|
|
222 | processZones(tabLayout(), [](VisualizationZoneWidget& zoneWidget) { zoneWidget.close(); }); | |
|
215 | 223 | |
|
216 | 224 | QWidget::closeEvent(event); |
|
217 | 225 | } |
|
218 | 226 | |
|
219 |
QLayout |
|
|
227 | QLayout& VisualizationTabWidget::tabLayout() const noexcept | |
|
220 | 228 | { |
|
221 | 229 | return *ui->dragDropContainer->layout(); |
|
222 | 230 | } |
|
223 | 231 | |
|
224 |
void VisualizationTabWidget::dropMimeData(int index, const QMimeData |
|
|
232 | void VisualizationTabWidget::dropMimeData(int index, const QMimeData* mimeData) | |
|
225 | 233 | { |
|
226 |
if (mimeData->hasFormat(MIME_TYPE_GRAPH)) |
|
|
234 | if (mimeData->hasFormat(MIME_TYPE_GRAPH)) | |
|
235 | { | |
|
227 | 236 | impl->dropGraph(index, this); |
|
228 | 237 | } |
|
229 |
else if (mimeData->hasFormat(MIME_TYPE_ZONE)) |
|
|
238 | else if (mimeData->hasFormat(MIME_TYPE_ZONE)) | |
|
239 | { | |
|
230 | 240 | impl->dropZone(index, this); |
|
231 | 241 | } |
|
232 |
else if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) |
|
|
242 | else if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) | |
|
243 | { | |
|
233 | 244 | auto variables = sqpApp->variableController().variables( |
|
234 |
Variable:: |
|
|
245 | Variable2::IDs(mimeData->data(MIME_TYPE_VARIABLE_LIST))); | |
|
235 | 246 | impl->dropVariables(variables, index, this); |
|
236 | 247 | } |
|
237 |
else if (mimeData->hasFormat(MIME_TYPE_PRODUCT_LIST)) |
|
|
248 | else if (mimeData->hasFormat(MIME_TYPE_PRODUCT_LIST)) | |
|
249 | { | |
|
238 | 250 | auto productsData = sqpApp->dataSourceController().productsDataForMimeData( |
|
239 | 251 | mimeData->data(MIME_TYPE_PRODUCT_LIST)); |
|
240 | 252 | impl->dropProducts(productsData, index, this); |
|
241 | 253 | } |
|
242 |
else |
|
|
254 | else | |
|
255 | { | |
|
243 | 256 | qCWarning(LOG_VisualizationZoneWidget()) |
|
244 | 257 | << tr("VisualizationTabWidget::dropMimeData, unknown MIME data received."); |
|
245 | 258 | } |
|
246 | 259 | } |
|
247 | 260 | |
|
248 | 261 | void VisualizationTabWidget::VisualizationTabWidgetPrivate::dropGraph( |
|
249 |
int index, VisualizationTabWidget |
|
|
262 | int index, VisualizationTabWidget* tabWidget) | |
|
250 | 263 | { |
|
251 |
auto |
|
|
264 | auto& helper = sqpApp->dragDropGuiController(); | |
|
252 | 265 | |
|
253 |
auto graphWidget = qobject_cast<VisualizationGraphWidget |
|
|
254 |
if (!graphWidget) |
|
|
266 | auto graphWidget = qobject_cast<VisualizationGraphWidget*>(helper.getCurrentDragWidget()); | |
|
267 | if (!graphWidget) | |
|
268 | { | |
|
255 | 269 | qCWarning(LOG_VisualizationZoneWidget()) |
|
256 | 270 | << tr("VisualizationTabWidget::dropGraph, drop aborted, the dropped graph is not " |
|
257 | 271 | "found or invalid."); |
|
258 | 272 | Q_ASSERT(false); |
|
259 | 273 | return; |
|
260 | 274 | } |
|
261 | 275 | |
|
262 | 276 | auto parentDragDropContainer |
|
263 |
= qobject_cast<VisualizationDragDropContainer |
|
|
264 |
if (!parentDragDropContainer) |
|
|
277 | = qobject_cast<VisualizationDragDropContainer*>(graphWidget->parentWidget()); | |
|
278 | if (!parentDragDropContainer) | |
|
279 | { | |
|
265 | 280 | qCWarning(LOG_VisualizationZoneWidget()) |
|
266 | 281 | << tr("VisualizationTabWidget::dropGraph, drop aborted, the parent container of " |
|
267 | 282 | "the dropped graph is not found."); |
|
268 | 283 | Q_ASSERT(false); |
|
269 | 284 | return; |
|
270 | 285 | } |
|
271 | 286 | |
|
272 | 287 | auto nbGraph = parentDragDropContainer->countDragWidget(); |
|
273 | 288 | |
|
274 |
const auto |
|
|
289 | const auto& variables = graphWidget->variables(); | |
|
275 | 290 | |
|
276 |
if (!variables.empty()) |
|
|
291 | if (!variables.empty()) | |
|
292 | { | |
|
277 | 293 | // Abort the requests for the variables (if any) |
|
278 | 294 | // Commented, because it's not sure if it's needed or not |
|
279 | 295 | // for (const auto& var : variables) |
|
280 | 296 | //{ |
|
281 | 297 | // sqpApp->variableController().onAbortProgressRequested(var); |
|
282 | 298 | //} |
|
283 | 299 | |
|
284 |
if (nbGraph == 1) |
|
|
300 | if (nbGraph == 1) | |
|
301 | { | |
|
285 | 302 | // This is the only graph in the previous zone, close the zone |
|
286 | 303 | helper.delayedCloseWidget(graphWidget->parentZoneWidget()); |
|
287 | 304 | } |
|
288 |
else |
|
|
305 | else | |
|
306 | { | |
|
289 | 307 | // Close the graph |
|
290 | 308 | helper.delayedCloseWidget(graphWidget); |
|
291 | 309 | } |
|
292 | 310 | |
|
293 | 311 | auto zoneWidget = tabWidget->createZone(variables, index); |
|
294 | 312 | auto firstGraph = zoneWidget->firstGraph(); |
|
295 |
if (firstGraph) |
|
|
313 | if (firstGraph) | |
|
314 | { | |
|
296 | 315 | firstGraph->addSelectionZones(graphWidget->selectionZoneRanges()); |
|
297 | 316 | } |
|
298 |
else |
|
|
317 | else | |
|
318 | { | |
|
299 | 319 | qCWarning(LOG_VisualizationZoneWidget()) |
|
300 | 320 | << tr("VisualizationTabWidget::dropGraph, no graph added in the widget."); |
|
301 | 321 | Q_ASSERT(false); |
|
302 | 322 | } |
|
303 | 323 | } |
|
304 |
else |
|
|
324 | else | |
|
325 | { | |
|
305 | 326 | // The graph is empty, create an empty zone and move the graph inside |
|
306 | 327 | |
|
307 | 328 | auto parentZoneWidget = graphWidget->parentZoneWidget(); |
|
308 | 329 | |
|
309 | 330 | parentDragDropContainer->layout()->removeWidget(graphWidget); |
|
310 | 331 | |
|
311 | 332 | auto zoneWidget = tabWidget->createEmptyZone(index); |
|
312 | 333 | zoneWidget->addGraph(graphWidget); |
|
313 | 334 | |
|
314 | 335 | // Close the old zone if it was the only graph inside |
|
315 |
if (nbGraph == 1) |
|
|
336 | if (nbGraph == 1) | |
|
337 | { | |
|
316 | 338 | helper.delayedCloseWidget(parentZoneWidget); |
|
317 | 339 | } |
|
318 | 340 | } |
|
319 | 341 | } |
|
320 | 342 | |
|
321 | 343 | void VisualizationTabWidget::VisualizationTabWidgetPrivate::dropZone( |
|
322 |
int index, VisualizationTabWidget |
|
|
344 | int index, VisualizationTabWidget* tabWidget) | |
|
323 | 345 | { |
|
324 |
auto |
|
|
346 | auto& helper = sqpApp->dragDropGuiController(); | |
|
325 | 347 | |
|
326 |
auto zoneWidget = qobject_cast<VisualizationZoneWidget |
|
|
327 |
if (!zoneWidget) |
|
|
348 | auto zoneWidget = qobject_cast<VisualizationZoneWidget*>(helper.getCurrentDragWidget()); | |
|
349 | if (!zoneWidget) | |
|
350 | { | |
|
328 | 351 | qCWarning(LOG_VisualizationZoneWidget()) |
|
329 | 352 | << tr("VisualizationTabWidget::dropZone, drop aborted, the dropped zone is not " |
|
330 | 353 | "found or invalid."); |
|
331 | 354 | Q_ASSERT(false); |
|
332 | 355 | return; |
|
333 | 356 | } |
|
334 | 357 | |
|
335 | 358 | auto parentDragDropContainer |
|
336 |
= qobject_cast<VisualizationDragDropContainer |
|
|
337 |
if (!parentDragDropContainer) |
|
|
359 | = qobject_cast<VisualizationDragDropContainer*>(zoneWidget->parentWidget()); | |
|
360 | if (!parentDragDropContainer) | |
|
361 | { | |
|
338 | 362 | qCWarning(LOG_VisualizationZoneWidget()) |
|
339 | 363 | << tr("VisualizationTabWidget::dropZone, drop aborted, the parent container of " |
|
340 | 364 | "the dropped zone is not found."); |
|
341 | 365 | Q_ASSERT(false); |
|
342 | 366 | return; |
|
343 | 367 | } |
|
344 | 368 | |
|
345 | 369 | // Simple move of the zone, no variable operation associated |
|
346 | 370 | parentDragDropContainer->layout()->removeWidget(zoneWidget); |
|
347 | 371 | tabWidget->ui->dragDropContainer->insertDragWidget(index, zoneWidget); |
|
348 | 372 | } |
|
349 | 373 | |
|
350 | 374 | void VisualizationTabWidget::VisualizationTabWidgetPrivate::dropVariables( |
|
351 |
const std::vector<std::shared_ptr<Variable> |
|
|
352 |
VisualizationTabWidget |
|
|
375 | const std::vector<std::shared_ptr<Variable2>>& variables, int index, | |
|
376 | VisualizationTabWidget* tabWidget) | |
|
353 | 377 | { |
|
354 | 378 | // Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and |
|
355 | 379 | // compatible variable here |
|
356 |
if (variables.size() > 1) |
|
|
380 | if (variables.size() > 1) | |
|
381 | { | |
|
357 | 382 | qCWarning(LOG_VisualizationZoneWidget()) |
|
358 | 383 | << tr("VisualizationTabWidget::dropVariables, dropping multiple variables, operation " |
|
359 | 384 | "aborted."); |
|
360 | 385 | return; |
|
361 | 386 | } |
|
362 | 387 | |
|
363 | 388 | tabWidget->createZone(variables, index); |
|
364 | 389 | } |
|
365 | 390 | |
|
366 | 391 | void VisualizationTabWidget::VisualizationTabWidgetPrivate::dropProducts( |
|
367 |
const QVariantList |
|
|
392 | const QVariantList& productsMetaData, int index, VisualizationTabWidget* tabWidget) | |
|
368 | 393 | { |
|
369 | 394 | // Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and |
|
370 | 395 | // compatible variable here |
|
371 |
if (productsMetaData.count() != 1) |
|
|
396 | if (productsMetaData.count() != 1) | |
|
397 | { | |
|
372 | 398 | qCWarning(LOG_VisualizationZoneWidget()) |
|
373 | 399 | << tr("VisualizationTabWidget::dropProducts, dropping multiple products, operation " |
|
374 | 400 | "aborted."); |
|
375 | 401 | return; |
|
376 | 402 | } |
|
377 | 403 | |
|
378 | auto context = new QObject{tabWidget}; | |
|
404 | auto context = new QObject { tabWidget }; | |
|
379 | 405 | connect(&sqpApp->variableController(), &VariableController2::variableAdded, context, |
|
380 |
|
|
|
381 |
|
|
|
382 |
|
|
|
383 |
|
|
|
384 |
|
|
|
406 | [this, index, tabWidget, context](auto variable) { | |
|
407 | tabWidget->createZone({ variable }, index); | |
|
408 | delete context; // removes the connection | |
|
409 | }, | |
|
410 | Qt::QueuedConnection); | |
|
385 | 411 | |
|
386 | 412 | auto productData = productsMetaData.first().toHash(); |
|
387 | 413 | QMetaObject::invokeMethod(&sqpApp->dataSourceController(), "requestVariable", |
|
388 |
|
|
|
414 | Qt::QueuedConnection, Q_ARG(QVariantHash, productData)); | |
|
389 | 415 | } |
@@ -1,224 +1,240 | |||
|
1 | 1 | #include "Visualization/VisualizationWidget.h" |
|
2 | 2 | #include "Visualization/IVisualizationWidgetVisitor.h" |
|
3 | 3 | #include "Visualization/VisualizationActionManager.h" |
|
4 | 4 | #include "Visualization/VisualizationGraphWidget.h" |
|
5 | 5 | #include "Visualization/VisualizationSelectionZoneItem.h" |
|
6 | 6 | #include "Visualization/VisualizationSelectionZoneManager.h" |
|
7 | 7 | #include "Visualization/VisualizationTabWidget.h" |
|
8 | 8 | #include "Visualization/VisualizationZoneWidget.h" |
|
9 | 9 | #include "Visualization/operations/FindVariableOperation.h" |
|
10 | 10 | #include "Visualization/operations/GenerateVariableMenuOperation.h" |
|
11 | 11 | #include "Visualization/operations/RemoveVariableOperation.h" |
|
12 | 12 | #include "Visualization/operations/RescaleAxeOperation.h" |
|
13 | 13 | #include "Visualization/qcustomplot.h" |
|
14 | 14 | |
|
15 | 15 | #include "ui_VisualizationWidget.h" |
|
16 | 16 | |
|
17 | 17 | #include "DragAndDrop/DragDropGuiController.h" |
|
18 | 18 | #include "SqpApplication.h" |
|
19 | 19 | |
|
20 | 20 | #include <QToolButton> |
|
21 | 21 | |
|
22 | 22 | #include <memory> |
|
23 | 23 | |
|
24 | 24 | Q_LOGGING_CATEGORY(LOG_VisualizationWidget, "VisualizationWidget") |
|
25 | 25 | |
|
26 |
struct VisualizationWidget::VisualizationWidgetPrivate |
|
|
26 | struct VisualizationWidget::VisualizationWidgetPrivate | |
|
27 | { | |
|
27 | 28 | std::unique_ptr<VisualizationSelectionZoneManager> m_ZoneSelectionManager = nullptr; |
|
28 | 29 | VisualizationActionManager m_ActionManager; |
|
29 | 30 | |
|
30 | 31 | VisualizationWidgetPrivate() |
|
31 | 32 | : m_ZoneSelectionManager(std::make_unique<VisualizationSelectionZoneManager>()) |
|
32 | 33 | { |
|
33 | 34 | } |
|
34 | 35 | }; |
|
35 | 36 | |
|
36 |
VisualizationWidget::VisualizationWidget(QWidget |
|
|
37 |
: QWidget{parent} |
|
|
38 |
|
|
|
39 |
|
|
|
37 | VisualizationWidget::VisualizationWidget(QWidget* parent) | |
|
38 | : QWidget { parent } | |
|
39 | , ui { new Ui::VisualizationWidget } | |
|
40 | , impl { spimpl::make_unique_impl<VisualizationWidgetPrivate>() } | |
|
40 | 41 | { |
|
41 | 42 | ui->setupUi(this); |
|
42 | 43 | |
|
43 | auto addTabViewButton = new QToolButton{ui->tabWidget}; | |
|
44 | auto addTabViewButton = new QToolButton { ui->tabWidget }; | |
|
44 | 45 | addTabViewButton->setText(tr("Add View")); |
|
45 | 46 | addTabViewButton->setCursor(Qt::ArrowCursor); |
|
46 | 47 | ui->tabWidget->setCornerWidget(addTabViewButton, Qt::TopRightCorner); |
|
47 | 48 | |
|
48 | 49 | auto enableMinimumCornerWidgetSize = [this](bool enable) { |
|
49 | ||
|
50 | 50 | auto tabViewCornerWidget = ui->tabWidget->cornerWidget(); |
|
51 | 51 | auto width = enable ? tabViewCornerWidget->width() : 0; |
|
52 | 52 | auto height = enable ? tabViewCornerWidget->height() : 0; |
|
53 | 53 | tabViewCornerWidget->setMinimumHeight(height); |
|
54 | 54 | tabViewCornerWidget->setMinimumWidth(width); |
|
55 | 55 | ui->tabWidget->setMinimumHeight(height); |
|
56 | 56 | ui->tabWidget->setMinimumWidth(width); |
|
57 | 57 | }; |
|
58 | 58 | |
|
59 | 59 | auto addTabView = [this, enableMinimumCornerWidgetSize]() { |
|
60 | auto widget = new VisualizationTabWidget{QString{"View %1"}.arg(ui->tabWidget->count() + 1), | |
|
61 | ui->tabWidget}; | |
|
60 | auto widget | |
|
61 | = new VisualizationTabWidget { QString { "View %1" }.arg(ui->tabWidget->count() + 1), | |
|
62 | ui->tabWidget }; | |
|
62 | 63 | auto index = ui->tabWidget->addTab(widget, widget->name()); |
|
63 |
if (ui->tabWidget->count() > 0) |
|
|
64 | if (ui->tabWidget->count() > 0) | |
|
65 | { | |
|
64 | 66 | enableMinimumCornerWidgetSize(false); |
|
65 | 67 | } |
|
66 | 68 | qCInfo(LOG_VisualizationWidget()) << tr("add the tab of index %1").arg(index); |
|
67 | 69 | }; |
|
68 | 70 | |
|
69 | 71 | auto removeTabView = [this, enableMinimumCornerWidgetSize](int index) { |
|
70 |
if (ui->tabWidget->count() == 1) |
|
|
72 | if (ui->tabWidget->count() == 1) | |
|
73 | { | |
|
71 | 74 | enableMinimumCornerWidgetSize(true); |
|
72 | 75 | } |
|
73 | 76 | |
|
74 | 77 | // Removes widget from tab and closes it |
|
75 | 78 | auto widget = ui->tabWidget->widget(index); |
|
76 | 79 | ui->tabWidget->removeTab(index); |
|
77 |
if (widget) |
|
|
80 | if (widget) | |
|
81 | { | |
|
78 | 82 | widget->close(); |
|
79 | 83 | } |
|
80 | 84 | |
|
81 | 85 | qCInfo(LOG_VisualizationWidget()) << tr("remove the tab of index %1").arg(index); |
|
82 | ||
|
83 | 86 | }; |
|
84 | 87 | |
|
85 | 88 | ui->tabWidget->setTabsClosable(true); |
|
86 | 89 | |
|
87 | 90 | connect(addTabViewButton, &QToolButton::clicked, addTabView); |
|
88 | 91 | connect(ui->tabWidget, &QTabWidget::tabCloseRequested, removeTabView); |
|
89 | 92 | |
|
90 | 93 | sqpApp->dragDropGuiController().addDragDropTabBar(ui->tabWidget->tabBar()); |
|
91 | 94 | |
|
92 | 95 | // Actions |
|
93 | 96 | impl->m_ActionManager.installSelectionZoneActions(); |
|
94 | 97 | |
|
95 | 98 | auto removeZoneAction = new QAction("Remove selected zone(s)"); |
|
96 | 99 | removeZoneAction->setShortcut(QKeySequence::Delete); |
|
97 | 100 | connect(removeZoneAction, &QAction::triggered, [this]() { |
|
98 | 101 | auto selection = impl->m_ZoneSelectionManager->selectedItems(); |
|
99 |
for (auto selectionZone : selection) |
|
|
100 | if (auto graph = selectionZone->parentGraphWidget()) { | |
|
102 | for (auto selectionZone : selection) | |
|
103 | { | |
|
104 | if (auto graph = selectionZone->parentGraphWidget()) | |
|
105 | { | |
|
101 | 106 | graph->removeSelectionZone(selectionZone); |
|
102 | 107 | } |
|
103 | 108 | } |
|
104 | 109 | }); |
|
105 | 110 | addAction(removeZoneAction); |
|
106 | 111 | |
|
107 | 112 | // Adds default tab |
|
108 | 113 | addTabView(); |
|
109 | 114 | } |
|
110 | 115 | |
|
111 | 116 | VisualizationWidget::~VisualizationWidget() |
|
112 | 117 | { |
|
113 | 118 | sqpApp->dragDropGuiController().removeDragDropTabBar(ui->tabWidget->tabBar()); |
|
114 | 119 | delete ui; |
|
115 | 120 | } |
|
116 | 121 | |
|
117 |
VisualizationSelectionZoneManager |
|
|
122 | VisualizationSelectionZoneManager& VisualizationWidget::selectionZoneManager() const | |
|
118 | 123 | { |
|
119 | 124 | return *impl->m_ZoneSelectionManager.get(); |
|
120 | 125 | } |
|
121 | 126 | |
|
122 |
VisualizationTabWidget |
|
|
127 | VisualizationTabWidget* VisualizationWidget::currentTabWidget() const | |
|
123 | 128 | { |
|
124 |
if (auto tab = qobject_cast<VisualizationTabWidget |
|
|
129 | if (auto tab = qobject_cast<VisualizationTabWidget*>(ui->tabWidget->currentWidget())) | |
|
130 | { | |
|
125 | 131 | return tab; |
|
126 | 132 | } |
|
127 | 133 | |
|
128 | 134 | return nullptr; |
|
129 | 135 | } |
|
130 | 136 | |
|
131 |
void VisualizationWidget::accept(IVisualizationWidgetVisitor |
|
|
137 | void VisualizationWidget::accept(IVisualizationWidgetVisitor* visitor) | |
|
132 | 138 | { |
|
133 |
if (visitor) |
|
|
139 | if (visitor) | |
|
140 | { | |
|
134 | 141 | visitor->visitEnter(this); |
|
135 | 142 | |
|
136 | 143 | // Apply visitor for tab children |
|
137 |
for (auto i = 0; i < ui->tabWidget->count(); ++i) |
|
|
144 | for (auto i = 0; i < ui->tabWidget->count(); ++i) | |
|
145 | { | |
|
138 | 146 | // Widgets different from tabs are not visited (no action) |
|
139 | 147 | if (auto visualizationTabWidget |
|
140 |
= dynamic_cast<VisualizationTabWidget |
|
|
148 | = dynamic_cast<VisualizationTabWidget*>(ui->tabWidget->widget(i))) | |
|
149 | { | |
|
141 | 150 | visualizationTabWidget->accept(visitor); |
|
142 | 151 | } |
|
143 | 152 | } |
|
144 | 153 | |
|
145 | 154 | visitor->visitLeave(this); |
|
146 | 155 | } |
|
147 |
else |
|
|
156 | else | |
|
157 | { | |
|
148 | 158 | qCCritical(LOG_VisualizationWidget()) << tr("Can't visit widget : the visitor is null"); |
|
149 | 159 | } |
|
150 | 160 | } |
|
151 | 161 | |
|
152 |
bool VisualizationWidget::canDrop( |
|
|
162 | bool VisualizationWidget::canDrop(Variable2& variable) const | |
|
153 | 163 | { |
|
154 | 164 | // The main widget can never accomodate a variable |
|
155 | 165 | Q_UNUSED(variable); |
|
156 | 166 | return false; |
|
157 | 167 | } |
|
158 | 168 | |
|
159 |
bool VisualizationWidget::contains( |
|
|
169 | bool VisualizationWidget::contains(Variable2& variable) const | |
|
160 | 170 | { |
|
161 | 171 | Q_UNUSED(variable); |
|
162 | 172 | return false; |
|
163 | 173 | } |
|
164 | 174 | |
|
165 | 175 | QString VisualizationWidget::name() const |
|
166 | 176 | { |
|
167 | 177 | return QStringLiteral("MainView"); |
|
168 | 178 | } |
|
169 | 179 | |
|
170 | 180 | void VisualizationWidget::attachVariableMenu( |
|
171 |
QMenu |
|
|
181 | QMenu* menu, const QVector<std::shared_ptr<Variable2>>& variables) noexcept | |
|
172 | 182 | { |
|
173 | 183 | // Menu is generated only if there is a single variable |
|
174 |
if (variables.size() == 1) |
|
|
175 | if (auto variable = variables.first()) { | |
|
184 | if (variables.size() == 1) | |
|
185 | { | |
|
186 | if (auto variable = variables.first()) | |
|
187 | { | |
|
176 | 188 | // Gets the containers of the variable |
|
177 | FindVariableOperation findVariableOperation{variable}; | |
|
189 | FindVariableOperation findVariableOperation { variable }; | |
|
178 | 190 | accept(&findVariableOperation); |
|
179 | 191 | auto variableContainers = findVariableOperation.result(); |
|
180 | 192 | |
|
181 | 193 | // Generates the actions that make it possible to visualize the variable |
|
182 | GenerateVariableMenuOperation generateVariableMenuOperation{ | |
|
183 |
|
|
|
194 | GenerateVariableMenuOperation generateVariableMenuOperation { menu, variable, | |
|
195 | std::move(variableContainers) }; | |
|
184 | 196 | accept(&generateVariableMenuOperation); |
|
185 | 197 | } |
|
186 |
else |
|
|
198 | else | |
|
199 | { | |
|
187 | 200 | qCCritical(LOG_VisualizationWidget()) << tr( |
|
188 | 201 | "Can't generate the menu relative to the visualization: the variable is null"); |
|
189 | 202 | } |
|
190 | 203 | } |
|
191 |
else |
|
|
204 | else | |
|
205 | { | |
|
192 | 206 | qCDebug(LOG_VisualizationWidget()) |
|
193 | 207 | << tr("No generation of the menu related to the visualization: several variables are " |
|
194 | 208 | "selected"); |
|
195 | 209 | } |
|
196 | 210 | } |
|
197 | 211 | |
|
198 | void VisualizationWidget::onVariableAboutToBeDeleted(std::shared_ptr<Variable> variable) noexcept | |
|
212 | void VisualizationWidget::onVariableAboutToBeDeleted(std::shared_ptr<Variable2> variable) noexcept | |
|
199 | 213 | { |
|
200 | 214 | // Calls the operation of removing all references to the variable in the visualization |
|
201 | auto removeVariableOperation = RemoveVariableOperation{variable}; | |
|
215 | auto removeVariableOperation = RemoveVariableOperation { variable }; | |
|
202 | 216 | accept(&removeVariableOperation); |
|
203 | 217 | } |
|
204 | 218 | |
|
205 |
void VisualizationWidget::onRangeChanged( |
|
|
206 | const DateTimeRange &range) noexcept | |
|
219 | void VisualizationWidget::onRangeChanged( | |
|
220 | std::shared_ptr<Variable2> variable, const DateTimeRange& range) noexcept | |
|
207 | 221 | { |
|
208 | 222 | // Calls the operation of rescaling all graph that contrains variable in the visualization |
|
209 | auto rescaleVariableOperation = RescaleAxeOperation{variable, range}; | |
|
223 | auto rescaleVariableOperation = RescaleAxeOperation { variable, range }; | |
|
210 | 224 | accept(&rescaleVariableOperation); |
|
211 | 225 | } |
|
212 | 226 | |
|
213 |
void VisualizationWidget::closeEvent(QCloseEvent |
|
|
227 | void VisualizationWidget::closeEvent(QCloseEvent* event) | |
|
214 | 228 | { |
|
215 | 229 | // Closes tabs in the widget |
|
216 |
for (auto i = 0; i < ui->tabWidget->count(); ++i) |
|
|
230 | for (auto i = 0; i < ui->tabWidget->count(); ++i) | |
|
231 | { | |
|
217 | 232 | if (auto visualizationTabWidget |
|
218 |
= dynamic_cast<VisualizationTabWidget |
|
|
233 | = dynamic_cast<VisualizationTabWidget*>(ui->tabWidget->widget(i))) | |
|
234 | { | |
|
219 | 235 | visualizationTabWidget->close(); |
|
220 | 236 | } |
|
221 | 237 | } |
|
222 | 238 | |
|
223 | 239 | QWidget::closeEvent(event); |
|
224 | 240 | } |
This diff has been collapsed as it changes many lines, (553 lines changed) Show them Hide them | |||
@@ -1,662 +1,695 | |||
|
1 | 1 | #include "Visualization/VisualizationZoneWidget.h" |
|
2 | 2 | |
|
3 | 3 | #include "Visualization/IVisualizationWidgetVisitor.h" |
|
4 | 4 | #include "Visualization/QCustomPlotSynchronizer.h" |
|
5 | 5 | #include "Visualization/VisualizationGraphWidget.h" |
|
6 | 6 | #include "Visualization/VisualizationWidget.h" |
|
7 | 7 | #include "ui_VisualizationZoneWidget.h" |
|
8 | 8 | |
|
9 | 9 | #include "Common/MimeTypesDef.h" |
|
10 | 10 | #include "Common/VisualizationDef.h" |
|
11 | 11 | |
|
12 | 12 | #include <Data/DateTimeRange.h> |
|
13 | 13 | #include <Data/DateTimeRangeHelper.h> |
|
14 | 14 | #include <DataSource/DataSourceController.h> |
|
15 | 15 | #include <Time/TimeController.h> |
|
16 | #include <Variable/Variable.h> | |
|
16 | #include <Variable/Variable2.h> | |
|
17 | 17 | #include <Variable/VariableController2.h> |
|
18 | 18 | |
|
19 | 19 | #include <Visualization/operations/FindVariableOperation.h> |
|
20 | 20 | |
|
21 | 21 | #include <DragAndDrop/DragDropGuiController.h> |
|
22 | 22 | #include <QUuid> |
|
23 | 23 | #include <SqpApplication.h> |
|
24 | 24 | #include <cmath> |
|
25 | 25 | |
|
26 | 26 | #include <QLayout> |
|
27 | 27 | #include <QStyle> |
|
28 | 28 | |
|
29 | 29 | Q_LOGGING_CATEGORY(LOG_VisualizationZoneWidget, "VisualizationZoneWidget") |
|
30 | 30 | |
|
31 |
namespace |
|
|
31 | namespace | |
|
32 | { | |
|
32 | 33 | |
|
33 | 34 | /** |
|
34 | 35 | * Applies a function to all graphs of the zone represented by its layout |
|
35 | 36 | * @param layout the layout that contains graphs |
|
36 | 37 | * @param fun the function to apply to each graph |
|
37 | 38 | */ |
|
38 | 39 | template <typename Fun> |
|
39 |
void processGraphs(QLayout |
|
|
40 | void processGraphs(QLayout& layout, Fun fun) | |
|
40 | 41 | { |
|
41 |
for (auto i = 0; i < layout.count(); ++i) |
|
|
42 | if (auto item = layout.itemAt(i)) { | |
|
42 | for (auto i = 0; i < layout.count(); ++i) | |
|
43 | { | |
|
44 | if (auto item = layout.itemAt(i)) | |
|
45 | { | |
|
43 | 46 | if (auto visualizationGraphWidget |
|
44 |
= qobject_cast<VisualizationGraphWidget |
|
|
47 | = qobject_cast<VisualizationGraphWidget*>(item->widget())) | |
|
48 | { | |
|
45 | 49 | fun(*visualizationGraphWidget); |
|
46 | 50 | } |
|
47 | 51 | } |
|
48 | 52 | } |
|
49 | 53 | } |
|
50 | 54 | |
|
51 | 55 | /// Generates a default name for a new graph, according to the number of graphs already displayed in |
|
52 | 56 | /// the zone |
|
53 |
QString defaultGraphName(QLayout |
|
|
57 | QString defaultGraphName(QLayout& layout) | |
|
54 | 58 | { |
|
55 | 59 | QSet<QString> existingNames; |
|
56 | 60 | processGraphs( |
|
57 |
layout, [&existingNames](auto |
|
|
61 | layout, [&existingNames](auto& graphWidget) { existingNames.insert(graphWidget.name()); }); | |
|
58 | 62 | |
|
59 | 63 | int zoneNum = 1; |
|
60 | 64 | QString name; |
|
61 |
do |
|
|
65 | do | |
|
66 | { | |
|
62 | 67 | name = QObject::tr("Graph ").append(QString::number(zoneNum)); |
|
63 | 68 | ++zoneNum; |
|
64 | 69 | } while (existingNames.contains(name)); |
|
65 | 70 | |
|
66 | 71 | return name; |
|
67 | 72 | } |
|
68 | 73 | |
|
69 | 74 | } // namespace |
|
70 | 75 | |
|
71 |
struct VisualizationZoneWidget::VisualizationZoneWidgetPrivate |
|
|
76 | struct VisualizationZoneWidget::VisualizationZoneWidgetPrivate | |
|
77 | { | |
|
72 | 78 | |
|
73 | 79 | explicit VisualizationZoneWidgetPrivate() |
|
74 |
: m_SynchronisationGroupId{QUuid::createUuid()} |
|
|
75 |
|
|
|
80 | : m_SynchronisationGroupId { QUuid::createUuid() } | |
|
81 | , m_Synchronizer { std::make_unique<QCustomPlotSynchronizer>() } | |
|
76 | 82 | { |
|
77 | 83 | } |
|
78 | 84 | QUuid m_SynchronisationGroupId; |
|
79 | 85 | std::unique_ptr<IGraphSynchronizer> m_Synchronizer; |
|
80 | 86 | |
|
81 |
void dropGraph(int index, VisualizationZoneWidget |
|
|
82 |
void dropVariables(const std::vector<std::shared_ptr<Variable> |
|
|
83 |
|
|
|
84 | void dropProducts(const QVariantList &productsData, int index, | |
|
85 |
|
|
|
87 | void dropGraph(int index, VisualizationZoneWidget* zoneWidget); | |
|
88 | void dropVariables(const std::vector<std::shared_ptr<Variable2>>& variables, int index, | |
|
89 | VisualizationZoneWidget* zoneWidget); | |
|
90 | void dropProducts( | |
|
91 | const QVariantList& productsData, int index, VisualizationZoneWidget* zoneWidget); | |
|
86 | 92 | }; |
|
87 | 93 | |
|
88 |
VisualizationZoneWidget::VisualizationZoneWidget(const QString |
|
|
89 |
: VisualizationDragWidget{parent} |
|
|
90 |
|
|
|
91 |
|
|
|
94 | VisualizationZoneWidget::VisualizationZoneWidget(const QString& name, QWidget* parent) | |
|
95 | : VisualizationDragWidget { parent } | |
|
96 | , ui { new Ui::VisualizationZoneWidget } | |
|
97 | , impl { spimpl::make_unique_impl<VisualizationZoneWidgetPrivate>() } | |
|
92 | 98 | { |
|
93 | 99 | ui->setupUi(this); |
|
94 | 100 | |
|
95 | 101 | ui->zoneNameLabel->setText(name); |
|
96 | 102 | |
|
97 | 103 | ui->dragDropContainer->setPlaceHolderType(DragDropGuiController::PlaceHolderType::Graph); |
|
98 |
ui->dragDropContainer->setMimeType( |
|
|
99 |
|
|
|
104 | ui->dragDropContainer->setMimeType( | |
|
105 | MIME_TYPE_GRAPH, VisualizationDragDropContainer::DropBehavior::Inserted); | |
|
100 | 106 | ui->dragDropContainer->setMimeType( |
|
101 | 107 | MIME_TYPE_VARIABLE_LIST, VisualizationDragDropContainer::DropBehavior::InsertedAndMerged); |
|
102 | 108 | ui->dragDropContainer->setMimeType( |
|
103 | 109 | MIME_TYPE_PRODUCT_LIST, VisualizationDragDropContainer::DropBehavior::InsertedAndMerged); |
|
104 |
ui->dragDropContainer->setMimeType( |
|
|
105 |
|
|
|
106 |
ui->dragDropContainer->setMimeType( |
|
|
107 |
|
|
|
108 |
ui->dragDropContainer->setMimeType( |
|
|
109 |
|
|
|
110 | ui->dragDropContainer->setMimeType( | |
|
111 | MIME_TYPE_TIME_RANGE, VisualizationDragDropContainer::DropBehavior::Merged); | |
|
112 | ui->dragDropContainer->setMimeType( | |
|
113 | MIME_TYPE_ZONE, VisualizationDragDropContainer::DropBehavior::Forbidden); | |
|
114 | ui->dragDropContainer->setMimeType( | |
|
115 | MIME_TYPE_SELECTION_ZONE, VisualizationDragDropContainer::DropBehavior::Forbidden); | |
|
110 | 116 | ui->dragDropContainer->setAcceptMimeDataFunction([this](auto mimeData) { |
|
111 |
return sqpApp->dragDropGuiController().checkMimeDataForVisualization( |
|
|
112 | ui->dragDropContainer); | |
|
117 | return sqpApp->dragDropGuiController().checkMimeDataForVisualization( | |
|
118 | mimeData, ui->dragDropContainer); | |
|
113 | 119 | }); |
|
114 | 120 | |
|
115 | 121 | auto acceptDragWidgetFun = [](auto dragWidget, auto mimeData) { |
|
116 |
if (!mimeData) |
|
|
122 | if (!mimeData) | |
|
123 | { | |
|
117 | 124 | return false; |
|
118 | 125 | } |
|
119 | 126 | |
|
120 |
if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) |
|
|
127 | if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) | |
|
128 | { | |
|
121 | 129 | auto variables = sqpApp->variableController().variables( |
|
122 |
Variable:: |
|
|
130 | Variable2::IDs(mimeData->data(MIME_TYPE_VARIABLE_LIST))); | |
|
123 | 131 | |
|
124 |
if (variables.size() != 1) |
|
|
132 | if (variables.size() != 1) | |
|
133 | { | |
|
125 | 134 | return false; |
|
126 | 135 | } |
|
127 | 136 | auto variable = variables.front(); |
|
128 | 137 | |
|
129 |
if (auto graphWidget = dynamic_cast<const VisualizationGraphWidget |
|
|
138 | if (auto graphWidget = dynamic_cast<const VisualizationGraphWidget*>(dragWidget)) | |
|
139 | { | |
|
130 | 140 | return graphWidget->canDrop(*variable); |
|
131 | 141 | } |
|
132 | 142 | } |
|
133 | 143 | |
|
134 | 144 | return true; |
|
135 | 145 | }; |
|
136 | 146 | ui->dragDropContainer->setAcceptDragWidgetFunction(acceptDragWidgetFun); |
|
137 | 147 | |
|
138 | 148 | connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredInContainer, this, |
|
139 |
|
|
|
149 | &VisualizationZoneWidget::dropMimeData); | |
|
140 | 150 | connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredOnWidget, this, |
|
141 |
|
|
|
151 | &VisualizationZoneWidget::dropMimeDataOnGraph); | |
|
142 | 152 | |
|
143 | 153 | // 'Close' options : widget is deleted when closed |
|
144 | 154 | setAttribute(Qt::WA_DeleteOnClose); |
|
145 | 155 | connect(ui->closeButton, &QToolButton::clicked, this, &VisualizationZoneWidget::close); |
|
146 | 156 | ui->closeButton->setIcon(sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton)); |
|
147 | 157 | |
|
148 | 158 | // Synchronisation id |
|
149 | QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronizationGroupId", | |
|
150 |
|
|
|
159 | // QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronizationGroupId", | |
|
160 | // Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId)); | |
|
151 | 161 | } |
|
152 | 162 | |
|
153 | 163 | VisualizationZoneWidget::~VisualizationZoneWidget() |
|
154 | 164 | { |
|
155 | 165 | delete ui; |
|
156 | 166 | } |
|
157 | 167 | |
|
158 |
void VisualizationZoneWidget::setZoneRange(const DateTimeRange |
|
|
168 | void VisualizationZoneWidget::setZoneRange(const DateTimeRange& range) | |
|
159 | 169 | { |
|
160 |
if (auto graph = firstGraph()) |
|
|
170 | if (auto graph = firstGraph()) | |
|
171 | { | |
|
161 | 172 | graph->setGraphRange(range); |
|
162 | 173 | } |
|
163 |
else |
|
|
174 | else | |
|
175 | { | |
|
164 | 176 | qCWarning(LOG_VisualizationZoneWidget()) |
|
165 | 177 | << tr("setZoneRange:Cannot set the range of an empty zone."); |
|
166 | 178 | } |
|
167 | 179 | } |
|
168 | 180 | |
|
169 |
void VisualizationZoneWidget::addGraph(VisualizationGraphWidget |
|
|
181 | void VisualizationZoneWidget::addGraph(VisualizationGraphWidget* graphWidget) | |
|
170 | 182 | { |
|
171 | 183 | // Synchronize new graph with others in the zone |
|
172 | // impl->m_Synchronizer->addGraph(*graphWidget); | |
|
173 | ||
|
174 | // ui->dragDropContainer->addDragWidget(graphWidget); | |
|
175 | insertGraph(0,graphWidget); | |
|
184 | // impl->m_Synchronizer->addGraph(*graphWidget); | |
|
176 | 185 | |
|
186 | // ui->dragDropContainer->addDragWidget(graphWidget); | |
|
187 | insertGraph(0, graphWidget); | |
|
177 | 188 | } |
|
178 | 189 | |
|
179 |
void VisualizationZoneWidget::insertGraph(int index, VisualizationGraphWidget |
|
|
190 | void VisualizationZoneWidget::insertGraph(int index, VisualizationGraphWidget* graphWidget) | |
|
180 | 191 | { |
|
181 | 192 | DEPRECATE( |
|
182 | auto layout = ui->dragDropContainer->layout(); | |
|
183 | for(int i=0;i<layout->count();i++) | |
|
184 | { | |
|
185 | auto graph = qobject_cast<VisualizationGraphWidget *>(layout->itemAt(i)->widget()); | |
|
186 |
connect(graph |
|
|
187 |
|
|
|
188 | } | |
|
189 | if(auto graph = firstGraph()) | |
|
190 | { | |
|
191 | graphWidget->setGraphRange(graph->graphRange(), true); | |
|
192 | } | |
|
193 | ) | |
|
193 | auto layout = ui->dragDropContainer->layout(); for (int i = 0; i < layout->count(); i++) { | |
|
194 | auto graph = qobject_cast<VisualizationGraphWidget*>(layout->itemAt(i)->widget()); | |
|
195 | connect(graphWidget, &VisualizationGraphWidget::setrange_sig, graph, | |
|
196 | &VisualizationGraphWidget::setGraphRange); | |
|
197 | connect(graph, &VisualizationGraphWidget::setrange_sig, graphWidget, | |
|
198 | &VisualizationGraphWidget::setGraphRange); | |
|
199 | } if (auto graph = firstGraph()) { graphWidget->setGraphRange(graph->graphRange(), true); }) | |
|
194 | 200 | |
|
195 | 201 | // Synchronize new graph with others in the zone |
|
196 | 202 | impl->m_Synchronizer->addGraph(*graphWidget); |
|
197 | 203 | |
|
198 | 204 | ui->dragDropContainer->insertDragWidget(index, graphWidget); |
|
199 | ||
|
200 | 205 | } |
|
201 | 206 | |
|
202 |
VisualizationGraphWidget |
|
|
207 | VisualizationGraphWidget* VisualizationZoneWidget::createGraph(std::shared_ptr<Variable2> variable) | |
|
203 | 208 | { |
|
204 | 209 | return createGraph(variable, -1); |
|
205 | 210 | } |
|
206 | 211 | |
|
207 |
VisualizationGraphWidget |
|
|
208 | int index) | |
|
212 | VisualizationGraphWidget* VisualizationZoneWidget::createGraph( | |
|
213 | std::shared_ptr<Variable2> variable, int index) | |
|
209 | 214 | { |
|
210 | 215 | auto graphWidget |
|
211 | = new VisualizationGraphWidget{defaultGraphName(*ui->dragDropContainer->layout()), this}; | |
|
216 | = new VisualizationGraphWidget { defaultGraphName(*ui->dragDropContainer->layout()), this }; | |
|
212 | 217 | |
|
213 | 218 | |
|
214 | 219 | // Set graph properties |
|
215 | 220 | graphWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding); |
|
216 | 221 | graphWidget->setMinimumHeight(GRAPH_MINIMUM_HEIGHT); |
|
217 | 222 | |
|
218 | 223 | |
|
219 | 224 | // Lambda to synchronize zone widget |
|
220 | // auto synchronizeZoneWidget = [this, graphWidget](const DateTimeRange &graphRange, | |
|
221 | // const DateTimeRange &oldGraphRange) { | |
|
222 | ||
|
223 | // auto zoomType = DateTimeRangeHelper::getTransformationType(oldGraphRange, graphRange); | |
|
224 | // auto frameLayout = ui->dragDropContainer->layout(); | |
|
225 | // for (auto i = 0; i < frameLayout->count(); ++i) { | |
|
226 | // auto graphChild | |
|
227 | // = dynamic_cast<VisualizationGraphWidget *>(frameLayout->itemAt(i)->widget()); | |
|
228 | // if (graphChild && (graphChild != graphWidget)) { | |
|
229 | ||
|
230 | // auto graphChildRange = graphChild->graphRange(); | |
|
231 | // switch (zoomType) { | |
|
232 | // case TransformationType::ZoomIn: { | |
|
233 | // auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart; | |
|
234 | // auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd; | |
|
235 | // graphChildRange.m_TStart += deltaLeft; | |
|
236 | // graphChildRange.m_TEnd -= deltaRight; | |
|
237 | // break; | |
|
238 | // } | |
|
239 | ||
|
240 | // case TransformationType::ZoomOut: { | |
|
241 | // auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart; | |
|
242 | // auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd; | |
|
243 | // graphChildRange.m_TStart -= deltaLeft; | |
|
244 | // graphChildRange.m_TEnd += deltaRight; | |
|
245 | // break; | |
|
246 | // } | |
|
247 | // case TransformationType::PanRight: { | |
|
248 | // auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart; | |
|
249 | // auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd; | |
|
250 | // graphChildRange.m_TStart += deltaLeft; | |
|
251 | // graphChildRange.m_TEnd += deltaRight; | |
|
252 | // break; | |
|
253 | // } | |
|
254 | // case TransformationType::PanLeft: { | |
|
255 | // auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart; | |
|
256 | // auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd; | |
|
257 | // graphChildRange.m_TStart -= deltaLeft; | |
|
258 | // graphChildRange.m_TEnd -= deltaRight; | |
|
259 | // break; | |
|
260 | // } | |
|
261 | // case TransformationType::Unknown: { | |
|
262 | // break; | |
|
263 | // } | |
|
264 | // default: | |
|
265 | // qCCritical(LOG_VisualizationZoneWidget()) | |
|
266 |
// << tr("Impossible to synchronize: zoom type not take into |
|
|
267 |
// |
|
|
268 |
// |
|
|
269 |
// |
|
|
270 | // graphChild->setFlags(GraphFlag::DisableAll); | |
|
271 |
// graphChild->set |
|
|
272 |
// graphChild->set |
|
|
273 | // } | |
|
274 | // } | |
|
275 |
// } |
|
|
225 | // auto synchronizeZoneWidget = [this, graphWidget](const DateTimeRange &graphRange, | |
|
226 | // const DateTimeRange &oldGraphRange) { | |
|
227 | ||
|
228 | // auto zoomType = DateTimeRangeHelper::getTransformationType(oldGraphRange, graphRange); | |
|
229 | // auto frameLayout = ui->dragDropContainer->layout(); | |
|
230 | // for (auto i = 0; i < frameLayout->count(); ++i) { | |
|
231 | // auto graphChild | |
|
232 | // = dynamic_cast<VisualizationGraphWidget *>(frameLayout->itemAt(i)->widget()); | |
|
233 | // if (graphChild && (graphChild != graphWidget)) { | |
|
234 | ||
|
235 | // auto graphChildRange = graphChild->graphRange(); | |
|
236 | // switch (zoomType) { | |
|
237 | // case TransformationType::ZoomIn: { | |
|
238 | // auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart; | |
|
239 | // auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd; | |
|
240 | // graphChildRange.m_TStart += deltaLeft; | |
|
241 | // graphChildRange.m_TEnd -= deltaRight; | |
|
242 | // break; | |
|
243 | // } | |
|
244 | ||
|
245 | // case TransformationType::ZoomOut: { | |
|
246 | // auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart; | |
|
247 | // auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd; | |
|
248 | // graphChildRange.m_TStart -= deltaLeft; | |
|
249 | // graphChildRange.m_TEnd += deltaRight; | |
|
250 | // break; | |
|
251 | // } | |
|
252 | // case TransformationType::PanRight: { | |
|
253 | // auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart; | |
|
254 | // auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd; | |
|
255 | // graphChildRange.m_TStart += deltaLeft; | |
|
256 | // graphChildRange.m_TEnd += deltaRight; | |
|
257 | // break; | |
|
258 | // } | |
|
259 | // case TransformationType::PanLeft: { | |
|
260 | // auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart; | |
|
261 | // auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd; | |
|
262 | // graphChildRange.m_TStart -= deltaLeft; | |
|
263 | // graphChildRange.m_TEnd -= deltaRight; | |
|
264 | // break; | |
|
265 | // } | |
|
266 | // case TransformationType::Unknown: { | |
|
267 | // break; | |
|
268 | // } | |
|
269 | // default: | |
|
270 | // qCCritical(LOG_VisualizationZoneWidget()) | |
|
271 | // << tr("Impossible to synchronize: zoom type not take into | |
|
272 | // account"); | |
|
273 | // // No action | |
|
274 | // break; | |
|
275 | // } | |
|
276 | // graphChild->setFlags(GraphFlag::DisableAll); | |
|
277 | // graphChild->setGraphRange(graphChildRange, true); | |
|
278 | // graphChild->setFlags(GraphFlag::EnableAll); | |
|
279 | // } | |
|
280 | // } | |
|
281 | // }; | |
|
276 | 282 | |
|
277 | 283 | // connection for synchronization |
|
278 | //connect(graphWidget, &VisualizationGraphWidget::synchronize, synchronizeZoneWidget); | |
|
284 | // connect(graphWidget, &VisualizationGraphWidget::synchronize, synchronizeZoneWidget); | |
|
279 | 285 | connect(graphWidget, &VisualizationGraphWidget::variableAdded, this, |
|
280 |
|
|
|
286 | &VisualizationZoneWidget::onVariableAdded); | |
|
281 | 287 | connect(graphWidget, &VisualizationGraphWidget::variableAboutToBeRemoved, this, |
|
282 |
|
|
|
288 | &VisualizationZoneWidget::onVariableAboutToBeRemoved); | |
|
283 | 289 | |
|
284 | auto range = DateTimeRange{}; | |
|
285 |
if (auto firstGraph = this->firstGraph()) |
|
|
290 | auto range = DateTimeRange {}; | |
|
291 | if (auto firstGraph = this->firstGraph()) | |
|
292 | { | |
|
286 | 293 | // Case of a new graph in a existant zone |
|
287 | 294 | range = firstGraph->graphRange(); |
|
288 | 295 | } |
|
289 |
else |
|
|
296 | else | |
|
297 | { | |
|
290 | 298 | // Case of a new graph as the first of the zone |
|
291 | 299 | range = variable->range(); |
|
292 | 300 | } |
|
293 | 301 | |
|
294 | 302 | this->insertGraph(index, graphWidget); |
|
295 | 303 | |
|
296 | 304 | graphWidget->addVariable(variable, range); |
|
297 | 305 | graphWidget->setYRange(variable); |
|
298 | 306 | |
|
299 | 307 | return graphWidget; |
|
300 | 308 | } |
|
301 | 309 | |
|
302 | VisualizationGraphWidget * | |
|
303 |
|
|
|
310 | VisualizationGraphWidget* VisualizationZoneWidget::createGraph( | |
|
311 | const std::vector<std::shared_ptr<Variable2>> variables, int index) | |
|
304 | 312 | { |
|
305 |
if (variables.empty()) |
|
|
313 | if (variables.empty()) | |
|
314 | { | |
|
306 | 315 | return nullptr; |
|
307 | 316 | } |
|
308 | 317 | |
|
309 | 318 | auto graphWidget = createGraph(variables.front(), index); |
|
310 |
for (auto variableIt = variables.cbegin() + 1; variableIt != variables.cend(); ++variableIt) |
|
|
319 | for (auto variableIt = variables.cbegin() + 1; variableIt != variables.cend(); ++variableIt) | |
|
320 | { | |
|
311 | 321 | graphWidget->addVariable(*variableIt, graphWidget->graphRange()); |
|
312 | 322 | } |
|
313 | 323 | |
|
314 | 324 | return graphWidget; |
|
315 | 325 | } |
|
316 | 326 | |
|
317 |
VisualizationGraphWidget |
|
|
327 | VisualizationGraphWidget* VisualizationZoneWidget::firstGraph() const | |
|
318 | 328 | { |
|
319 |
VisualizationGraphWidget |
|
|
329 | VisualizationGraphWidget* firstGraph = nullptr; | |
|
320 | 330 | auto layout = ui->dragDropContainer->layout(); |
|
321 |
if (layout->count() > 0) |
|
|
331 | if (layout->count() > 0) | |
|
332 | { | |
|
322 | 333 | if (auto visualizationGraphWidget |
|
323 |
= qobject_cast<VisualizationGraphWidget |
|
|
334 | = qobject_cast<VisualizationGraphWidget*>(layout->itemAt(0)->widget())) | |
|
335 | { | |
|
324 | 336 | firstGraph = visualizationGraphWidget; |
|
325 | 337 | } |
|
326 | 338 | } |
|
327 | 339 | |
|
328 | 340 | return firstGraph; |
|
329 | 341 | } |
|
330 | 342 | |
|
331 | 343 | void VisualizationZoneWidget::closeAllGraphs() |
|
332 | 344 | { |
|
333 | 345 | processGraphs(*ui->dragDropContainer->layout(), |
|
334 |
|
|
|
346 | [](VisualizationGraphWidget& graphWidget) { graphWidget.close(); }); | |
|
335 | 347 | } |
|
336 | 348 | |
|
337 |
void VisualizationZoneWidget::accept(IVisualizationWidgetVisitor |
|
|
349 | void VisualizationZoneWidget::accept(IVisualizationWidgetVisitor* visitor) | |
|
338 | 350 | { |
|
339 |
if (visitor) |
|
|
351 | if (visitor) | |
|
352 | { | |
|
340 | 353 | visitor->visitEnter(this); |
|
341 | 354 | |
|
342 | 355 | // Apply visitor to graph children: widgets different from graphs are not visited (no |
|
343 | 356 | // action) |
|
344 | processGraphs( | |
|
345 | *ui->dragDropContainer->layout(), | |
|
346 | [visitor](VisualizationGraphWidget &graphWidget) { graphWidget.accept(visitor); }); | |
|
357 | processGraphs(*ui->dragDropContainer->layout(), | |
|
358 | [visitor](VisualizationGraphWidget& graphWidget) { graphWidget.accept(visitor); }); | |
|
347 | 359 | |
|
348 | 360 | visitor->visitLeave(this); |
|
349 | 361 | } |
|
350 |
else |
|
|
362 | else | |
|
363 | { | |
|
351 | 364 | qCCritical(LOG_VisualizationZoneWidget()) << tr("Can't visit widget : the visitor is null"); |
|
352 | 365 | } |
|
353 | 366 | } |
|
354 | 367 | |
|
355 |
bool VisualizationZoneWidget::canDrop( |
|
|
368 | bool VisualizationZoneWidget::canDrop(Variable2& variable) const | |
|
356 | 369 | { |
|
357 | 370 | // A tab can always accomodate a variable |
|
358 | 371 | Q_UNUSED(variable); |
|
359 | 372 | return true; |
|
360 | 373 | } |
|
361 | 374 | |
|
362 |
bool VisualizationZoneWidget::contains( |
|
|
375 | bool VisualizationZoneWidget::contains(Variable2& variable) const | |
|
363 | 376 | { |
|
364 | 377 | Q_UNUSED(variable); |
|
365 | 378 | return false; |
|
366 | 379 | } |
|
367 | 380 | |
|
368 | 381 | QString VisualizationZoneWidget::name() const |
|
369 | 382 | { |
|
370 | 383 | return ui->zoneNameLabel->text(); |
|
371 | 384 | } |
|
372 | 385 | |
|
373 |
QMimeData |
|
|
386 | QMimeData* VisualizationZoneWidget::mimeData(const QPoint& position) const | |
|
374 | 387 | { |
|
375 | 388 | Q_UNUSED(position); |
|
376 | 389 | |
|
377 | 390 | auto mimeData = new QMimeData; |
|
378 | mimeData->setData(MIME_TYPE_ZONE, QByteArray{}); | |
|
391 | mimeData->setData(MIME_TYPE_ZONE, QByteArray {}); | |
|
379 | 392 | |
|
380 |
if (auto firstGraph = this->firstGraph()) |
|
|
393 | if (auto firstGraph = this->firstGraph()) | |
|
394 | { | |
|
381 | 395 | auto timeRangeData = TimeController::mimeDataForTimeRange(firstGraph->graphRange()); |
|
382 | 396 | mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData); |
|
383 | 397 | } |
|
384 | 398 | |
|
385 | 399 | return mimeData; |
|
386 | 400 | } |
|
387 | 401 | |
|
388 | 402 | bool VisualizationZoneWidget::isDragAllowed() const |
|
389 | 403 | { |
|
390 | 404 | return true; |
|
391 | 405 | } |
|
392 | 406 | |
|
393 |
void VisualizationZoneWidget::notifyMouseMoveInGraph(const QPointF |
|
|
394 | const QPointF &plotPosition, | |
|
395 | VisualizationGraphWidget *graphWidget) | |
|
407 | void VisualizationZoneWidget::notifyMouseMoveInGraph(const QPointF& graphPosition, | |
|
408 | const QPointF& plotPosition, VisualizationGraphWidget* graphWidget) | |
|
396 | 409 | { |
|
397 |
processGraphs(*ui->dragDropContainer->layout(), |
|
|
398 | VisualizationGraphWidget &processedGraph) { | |
|
399 | ||
|
400 | switch (sqpApp->plotsCursorMode()) { | |
|
401 | case SqpApplication::PlotsCursorMode::Vertical: | |
|
402 | processedGraph.removeHorizontalCursor(); | |
|
403 | processedGraph.addVerticalCursorAtViewportPosition(graphPosition.x()); | |
|
404 | break; | |
|
405 | case SqpApplication::PlotsCursorMode::Temporal: | |
|
406 | processedGraph.addVerticalCursor(plotPosition.x()); | |
|
407 | processedGraph.removeHorizontalCursor(); | |
|
408 | break; | |
|
409 | case SqpApplication::PlotsCursorMode::Horizontal: | |
|
410 | processedGraph.removeVerticalCursor(); | |
|
411 | if (&processedGraph == graphWidget) { | |
|
412 | processedGraph.addHorizontalCursorAtViewportPosition(graphPosition.y()); | |
|
413 | } | |
|
414 | else { | |
|
410 | processGraphs(*ui->dragDropContainer->layout(), | |
|
411 | [&graphPosition, &plotPosition, &graphWidget](VisualizationGraphWidget& processedGraph) { | |
|
412 | switch (sqpApp->plotsCursorMode()) | |
|
413 | { | |
|
414 | case SqpApplication::PlotsCursorMode::Vertical: | |
|
415 | 415 | processedGraph.removeHorizontalCursor(); |
|
416 | } | |
|
417 | break; | |
|
418 | case SqpApplication::PlotsCursorMode::Cross: | |
|
419 | if (&processedGraph == graphWidget) { | |
|
420 | 416 | processedGraph.addVerticalCursorAtViewportPosition(graphPosition.x()); |
|
421 | processedGraph.addHorizontalCursorAtViewportPosition(graphPosition.y()); | |
|
422 | } | |
|
423 | else { | |
|
417 | break; | |
|
418 | case SqpApplication::PlotsCursorMode::Temporal: | |
|
419 | processedGraph.addVerticalCursor(plotPosition.x()); | |
|
424 | 420 | processedGraph.removeHorizontalCursor(); |
|
421 | break; | |
|
422 | case SqpApplication::PlotsCursorMode::Horizontal: | |
|
425 | 423 | processedGraph.removeVerticalCursor(); |
|
426 | } | |
|
427 |
|
|
|
428 | case SqpApplication::PlotsCursorMode::NoCursor: | |
|
429 | processedGraph.removeHorizontalCursor(); | |
|
430 | processedGraph.removeVerticalCursor(); | |
|
431 |
|
|
|
432 | } | |
|
433 | ||
|
434 | ||
|
435 | }); | |
|
424 | if (&processedGraph == graphWidget) | |
|
425 | { | |
|
426 | processedGraph.addHorizontalCursorAtViewportPosition(graphPosition.y()); | |
|
427 | } | |
|
428 | else | |
|
429 | { | |
|
430 | processedGraph.removeHorizontalCursor(); | |
|
431 | } | |
|
432 | break; | |
|
433 | case SqpApplication::PlotsCursorMode::Cross: | |
|
434 | if (&processedGraph == graphWidget) | |
|
435 | { | |
|
436 | processedGraph.addVerticalCursorAtViewportPosition(graphPosition.x()); | |
|
437 | processedGraph.addHorizontalCursorAtViewportPosition(graphPosition.y()); | |
|
438 | } | |
|
439 | else | |
|
440 | { | |
|
441 | processedGraph.removeHorizontalCursor(); | |
|
442 | processedGraph.removeVerticalCursor(); | |
|
443 | } | |
|
444 | break; | |
|
445 | case SqpApplication::PlotsCursorMode::NoCursor: | |
|
446 | processedGraph.removeHorizontalCursor(); | |
|
447 | processedGraph.removeVerticalCursor(); | |
|
448 | break; | |
|
449 | } | |
|
450 | }); | |
|
436 | 451 | } |
|
437 | 452 | |
|
438 |
void VisualizationZoneWidget::notifyMouseLeaveGraph(VisualizationGraphWidget |
|
|
453 | void VisualizationZoneWidget::notifyMouseLeaveGraph(VisualizationGraphWidget* graphWidget) | |
|
439 | 454 | { |
|
440 |
processGraphs(*ui->dragDropContainer->layout(), [](VisualizationGraphWidget |
|
|
455 | processGraphs(*ui->dragDropContainer->layout(), [](VisualizationGraphWidget& processedGraph) { | |
|
441 | 456 | processedGraph.removeHorizontalCursor(); |
|
442 | 457 | processedGraph.removeVerticalCursor(); |
|
443 | 458 | }); |
|
444 | 459 | } |
|
445 | 460 | |
|
446 |
void VisualizationZoneWidget::closeEvent(QCloseEvent |
|
|
461 | void VisualizationZoneWidget::closeEvent(QCloseEvent* event) | |
|
447 | 462 | { |
|
448 | 463 | // Closes graphs in the zone |
|
449 | 464 | processGraphs(*ui->dragDropContainer->layout(), |
|
450 |
|
|
|
465 | [](VisualizationGraphWidget& graphWidget) { graphWidget.close(); }); | |
|
451 | 466 | |
|
452 | 467 | // Delete synchronization group from variable controller |
|
453 | 468 | QMetaObject::invokeMethod(&sqpApp->variableController(), "onRemoveSynchronizationGroupId", |
|
454 |
|
|
|
469 | Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId)); | |
|
455 | 470 | |
|
456 | 471 | QWidget::closeEvent(event); |
|
457 | 472 | } |
|
458 | 473 | |
|
459 | void VisualizationZoneWidget::onVariableAdded(std::shared_ptr<Variable> variable) | |
|
474 | void VisualizationZoneWidget::onVariableAdded(std::shared_ptr<Variable2> variable) | |
|
460 | 475 | { |
|
461 | 476 | QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronized", |
|
462 |
|
|
|
463 |
|
|
|
477 | Qt::QueuedConnection, Q_ARG(std::shared_ptr<Variable2>, variable), | |
|
478 | Q_ARG(QUuid, impl->m_SynchronisationGroupId)); | |
|
464 | 479 | } |
|
465 | 480 | |
|
466 | void VisualizationZoneWidget::onVariableAboutToBeRemoved(std::shared_ptr<Variable> variable) | |
|
481 | void VisualizationZoneWidget::onVariableAboutToBeRemoved(std::shared_ptr<Variable2> variable) | |
|
467 | 482 | { |
|
468 | 483 | QMetaObject::invokeMethod(&sqpApp->variableController(), "desynchronize", Qt::QueuedConnection, |
|
469 | Q_ARG(std::shared_ptr<Variable>, variable), | |
|
470 | Q_ARG(QUuid, impl->m_SynchronisationGroupId)); | |
|
484 | Q_ARG(std::shared_ptr<Variable2>, variable), Q_ARG(QUuid, impl->m_SynchronisationGroupId)); | |
|
471 | 485 | } |
|
472 | 486 | |
|
473 |
void VisualizationZoneWidget::dropMimeData(int index, const QMimeData |
|
|
487 | void VisualizationZoneWidget::dropMimeData(int index, const QMimeData* mimeData) | |
|
474 | 488 | { |
|
475 |
if (mimeData->hasFormat(MIME_TYPE_GRAPH)) |
|
|
489 | if (mimeData->hasFormat(MIME_TYPE_GRAPH)) | |
|
490 | { | |
|
476 | 491 | impl->dropGraph(index, this); |
|
477 | 492 | } |
|
478 |
else if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) |
|
|
493 | else if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) | |
|
494 | { | |
|
479 | 495 | auto variables = sqpApp->variableController().variables( |
|
480 |
Variable:: |
|
|
496 | Variable2::IDs(mimeData->data(MIME_TYPE_VARIABLE_LIST))); | |
|
481 | 497 | impl->dropVariables(variables, index, this); |
|
482 | 498 | } |
|
483 |
else if (mimeData->hasFormat(MIME_TYPE_PRODUCT_LIST)) |
|
|
499 | else if (mimeData->hasFormat(MIME_TYPE_PRODUCT_LIST)) | |
|
500 | { | |
|
484 | 501 | auto products = sqpApp->dataSourceController().productsDataForMimeData( |
|
485 | 502 | mimeData->data(MIME_TYPE_PRODUCT_LIST)); |
|
486 | 503 | impl->dropProducts(products, index, this); |
|
487 | 504 | } |
|
488 |
else |
|
|
505 | else | |
|
506 | { | |
|
489 | 507 | qCWarning(LOG_VisualizationZoneWidget()) |
|
490 | 508 | << tr("VisualizationZoneWidget::dropMimeData, unknown MIME data received."); |
|
491 | 509 | } |
|
492 | 510 | } |
|
493 | 511 | |
|
494 |
void VisualizationZoneWidget::dropMimeDataOnGraph( |
|
|
495 | const QMimeData *mimeData) | |
|
512 | void VisualizationZoneWidget::dropMimeDataOnGraph( | |
|
513 | VisualizationDragWidget* dragWidget, const QMimeData* mimeData) | |
|
496 | 514 | { |
|
497 |
auto graphWidget = qobject_cast<VisualizationGraphWidget |
|
|
498 |
if (!graphWidget) |
|
|
515 | auto graphWidget = qobject_cast<VisualizationGraphWidget*>(dragWidget); | |
|
516 | if (!graphWidget) | |
|
517 | { | |
|
499 | 518 | qCWarning(LOG_VisualizationZoneWidget()) |
|
500 | 519 | << tr("VisualizationZoneWidget::dropMimeDataOnGraph, dropping in an unknown widget, " |
|
501 | 520 | "drop aborted"); |
|
502 | 521 | Q_ASSERT(false); |
|
503 | 522 | return; |
|
504 | 523 | } |
|
505 | 524 | |
|
506 |
if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) |
|
|
525 | if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) | |
|
526 | { | |
|
507 | 527 | auto variables = sqpApp->variableController().variables( |
|
508 |
Variable:: |
|
|
509 |
for (const auto |
|
|
528 | Variable2::IDs(mimeData->data(MIME_TYPE_VARIABLE_LIST))); | |
|
529 | for (const auto& var : variables) | |
|
530 | { | |
|
510 | 531 | graphWidget->addVariable(var, graphWidget->graphRange()); |
|
511 | 532 | } |
|
512 | 533 | } |
|
513 |
else if (mimeData->hasFormat(MIME_TYPE_PRODUCT_LIST)) |
|
|
534 | else if (mimeData->hasFormat(MIME_TYPE_PRODUCT_LIST)) | |
|
535 | { | |
|
514 | 536 | auto products = sqpApp->dataSourceController().productsDataForMimeData( |
|
515 | 537 | mimeData->data(MIME_TYPE_PRODUCT_LIST)); |
|
516 | 538 | |
|
517 | auto context = new QObject{this}; | |
|
539 | auto context = new QObject { this }; | |
|
518 | 540 | auto range = TimeController::timeRangeForMimeData(mimeData->data(MIME_TYPE_TIME_RANGE)); |
|
519 | 541 | // BTW this is really dangerous, this assumes the next created variable will be this one... |
|
520 | 542 | connect(&sqpApp->variableController(), &VariableController2::variableAdded, context, |
|
521 |
|
|
|
522 |
|
|
|
523 |
|
|
|
524 |
|
|
|
525 |
|
|
|
526 |
|
|
|
527 |
|
|
|
528 |
|
|
|
529 |
|
|
|
530 |
|
|
|
531 |
|
|
|
532 | { | |
|
533 | graphWidget->addVariable(variable, range); | |
|
534 |
|
|
|
535 |
|
|
|
536 |
|
|
|
537 | ||
|
538 | }, | |
|
539 | Qt::QueuedConnection); | |
|
543 | [this, graphWidget, context, range](auto variable) { | |
|
544 | if (sqpApp->variableController().isReady(variable)) | |
|
545 | { | |
|
546 | graphWidget->addVariable(variable, range); | |
|
547 | delete context; | |
|
548 | } | |
|
549 | else | |
|
550 | { | |
|
551 | // -> this is pure insanity! this is a workaround to make a bad design work | |
|
552 | QObject::connect(variable.get(), &Variable2::updated, context, | |
|
553 | [graphWidget, context, range, variable]() { | |
|
554 | graphWidget->addVariable(variable, range); | |
|
555 | delete context; | |
|
556 | }); | |
|
557 | } | |
|
558 | }, | |
|
559 | Qt::QueuedConnection); | |
|
540 | 560 | |
|
541 | 561 | auto productData = products.first().toHash(); |
|
542 | 562 | QMetaObject::invokeMethod(&sqpApp->dataSourceController(), "requestVariable", |
|
543 |
|
|
|
563 | Qt::QueuedConnection, Q_ARG(QVariantHash, productData)); | |
|
544 | 564 | } |
|
545 |
else if (mimeData->hasFormat(MIME_TYPE_TIME_RANGE)) |
|
|
565 | else if (mimeData->hasFormat(MIME_TYPE_TIME_RANGE)) | |
|
566 | { | |
|
546 | 567 | auto range = TimeController::timeRangeForMimeData(mimeData->data(MIME_TYPE_TIME_RANGE)); |
|
547 | 568 | graphWidget->setGraphRange(range, true, true); |
|
548 | 569 | } |
|
549 |
else |
|
|
570 | else | |
|
571 | { | |
|
550 | 572 | qCWarning(LOG_VisualizationZoneWidget()) |
|
551 | 573 | << tr("VisualizationZoneWidget::dropMimeDataOnGraph, unknown MIME data received."); |
|
552 | 574 | } |
|
553 | 575 | } |
|
554 | 576 | |
|
555 | 577 | void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropGraph( |
|
556 |
int index, VisualizationZoneWidget |
|
|
578 | int index, VisualizationZoneWidget* zoneWidget) | |
|
557 | 579 | { |
|
558 |
auto |
|
|
580 | auto& helper = sqpApp->dragDropGuiController(); | |
|
559 | 581 | |
|
560 |
auto graphWidget = qobject_cast<VisualizationGraphWidget |
|
|
561 |
if (!graphWidget) |
|
|
582 | auto graphWidget = qobject_cast<VisualizationGraphWidget*>(helper.getCurrentDragWidget()); | |
|
583 | if (!graphWidget) | |
|
584 | { | |
|
562 | 585 | qCWarning(LOG_VisualizationZoneWidget()) |
|
563 | 586 | << tr("VisualizationZoneWidget::dropGraph, drop aborted, the dropped graph is not " |
|
564 | 587 | "found or invalid."); |
|
565 | 588 | Q_ASSERT(false); |
|
566 | 589 | return; |
|
567 | 590 | } |
|
568 | 591 | |
|
569 | 592 | auto parentDragDropContainer |
|
570 |
= qobject_cast<VisualizationDragDropContainer |
|
|
571 |
if (!parentDragDropContainer) |
|
|
593 | = qobject_cast<VisualizationDragDropContainer*>(graphWidget->parentWidget()); | |
|
594 | if (!parentDragDropContainer) | |
|
595 | { | |
|
572 | 596 | qCWarning(LOG_VisualizationZoneWidget()) |
|
573 | 597 | << tr("VisualizationZoneWidget::dropGraph, drop aborted, the parent container of " |
|
574 | 598 | "the dropped graph is not found."); |
|
575 | 599 | Q_ASSERT(false); |
|
576 | 600 | return; |
|
577 | 601 | } |
|
578 | 602 | |
|
579 |
const auto |
|
|
603 | const auto& variables = graphWidget->variables(); | |
|
580 | 604 | |
|
581 |
if (parentDragDropContainer != zoneWidget->ui->dragDropContainer && !variables.empty()) |
|
|
605 | if (parentDragDropContainer != zoneWidget->ui->dragDropContainer && !variables.empty()) | |
|
606 | { | |
|
582 | 607 | // The drop didn't occur in the same zone |
|
583 | 608 | |
|
584 | 609 | // Abort the requests for the variables (if any) |
|
585 | 610 | // Commented, because it's not sure if it's needed or not |
|
586 | 611 | // for (const auto& var : variables) |
|
587 | 612 | //{ |
|
588 | 613 | // sqpApp->variableController().onAbortProgressRequested(var); |
|
589 | 614 | //} |
|
590 | 615 | |
|
591 | 616 | auto previousParentZoneWidget = graphWidget->parentZoneWidget(); |
|
592 | 617 | auto nbGraph = parentDragDropContainer->countDragWidget(); |
|
593 |
if (nbGraph == 1) |
|
|
618 | if (nbGraph == 1) | |
|
619 | { | |
|
594 | 620 | // This is the only graph in the previous zone, close the zone |
|
595 | 621 | helper.delayedCloseWidget(previousParentZoneWidget); |
|
596 | 622 | } |
|
597 |
else |
|
|
623 | else | |
|
624 | { | |
|
598 | 625 | // Close the graph |
|
599 | 626 | helper.delayedCloseWidget(graphWidget); |
|
600 | 627 | } |
|
601 | 628 | |
|
602 | 629 | // Creates the new graph in the zone |
|
603 | 630 | auto newGraphWidget = zoneWidget->createGraph(variables, index); |
|
604 | 631 | newGraphWidget->addSelectionZones(graphWidget->selectionZoneRanges()); |
|
605 | 632 | } |
|
606 |
else |
|
|
633 | else | |
|
634 | { | |
|
607 | 635 | // The drop occurred in the same zone or the graph is empty |
|
608 | 636 | // Simple move of the graph, no variable operation associated |
|
609 | 637 | parentDragDropContainer->layout()->removeWidget(graphWidget); |
|
610 | 638 | |
|
611 |
if (variables.empty() && parentDragDropContainer != zoneWidget->ui->dragDropContainer) |
|
|
639 | if (variables.empty() && parentDragDropContainer != zoneWidget->ui->dragDropContainer) | |
|
640 | { | |
|
612 | 641 | // The graph is empty and dropped in a different zone. |
|
613 | 642 | // Take the range of the first graph in the zone (if existing). |
|
614 | 643 | auto layout = zoneWidget->ui->dragDropContainer->layout(); |
|
615 |
if (layout->count() > 0) |
|
|
644 | if (layout->count() > 0) | |
|
645 | { | |
|
616 | 646 | if (auto visualizationGraphWidget |
|
617 |
= qobject_cast<VisualizationGraphWidget |
|
|
647 | = qobject_cast<VisualizationGraphWidget*>(layout->itemAt(0)->widget())) | |
|
648 | { | |
|
618 | 649 | graphWidget->setGraphRange(visualizationGraphWidget->graphRange()); |
|
619 | 650 | } |
|
620 | 651 | } |
|
621 | 652 | } |
|
622 | 653 | |
|
623 | 654 | zoneWidget->ui->dragDropContainer->insertDragWidget(index, graphWidget); |
|
624 | 655 | } |
|
625 | 656 | } |
|
626 | 657 | |
|
627 | 658 | void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropVariables( |
|
628 |
const std::vector<std::shared_ptr<Variable> |
|
|
629 |
VisualizationZoneWidget |
|
|
659 | const std::vector<std::shared_ptr<Variable2>>& variables, int index, | |
|
660 | VisualizationZoneWidget* zoneWidget) | |
|
630 | 661 | { |
|
631 | 662 | // Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and |
|
632 | 663 | // compatible variable here |
|
633 |
if (variables.size() > 1) |
|
|
664 | if (variables.size() > 1) | |
|
665 | { | |
|
634 | 666 | return; |
|
635 | 667 | } |
|
636 | 668 | zoneWidget->createGraph(variables, index); |
|
637 | 669 | } |
|
638 | 670 | |
|
639 | 671 | void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropProducts( |
|
640 |
const QVariantList |
|
|
672 | const QVariantList& productsData, int index, VisualizationZoneWidget* zoneWidget) | |
|
641 | 673 | { |
|
642 | 674 | // Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and |
|
643 | 675 | // compatible variable here |
|
644 |
if (productsData.count() != 1) |
|
|
676 | if (productsData.count() != 1) | |
|
677 | { | |
|
645 | 678 | qCWarning(LOG_VisualizationZoneWidget()) |
|
646 | 679 | << tr("VisualizationTabWidget::dropProducts, dropping multiple products, operation " |
|
647 | 680 | "aborted."); |
|
648 | 681 | return; |
|
649 | 682 | } |
|
650 | 683 | |
|
651 | auto context = new QObject{zoneWidget}; | |
|
684 | auto context = new QObject { zoneWidget }; | |
|
652 | 685 | connect(&sqpApp->variableController(), &VariableController2::variableAdded, context, |
|
653 |
|
|
|
654 |
|
|
|
655 |
|
|
|
656 |
|
|
|
657 |
|
|
|
686 | [this, index, zoneWidget, context](auto variable) { | |
|
687 | zoneWidget->createGraph(variable, index); | |
|
688 | delete context; // removes the connection | |
|
689 | }, | |
|
690 | Qt::QueuedConnection); | |
|
658 | 691 | |
|
659 | 692 | auto productData = productsData.first().toHash(); |
|
660 | 693 | QMetaObject::invokeMethod(&sqpApp->dataSourceController(), "requestVariable", |
|
661 |
|
|
|
694 | Qt::QueuedConnection, Q_ARG(QVariantHash, productData)); | |
|
662 | 695 | } |
@@ -1,72 +1,75 | |||
|
1 | 1 | #include "Visualization/operations/FindVariableOperation.h" |
|
2 | 2 | |
|
3 | 3 | #include "Visualization/VisualizationGraphWidget.h" |
|
4 | 4 | #include "Visualization/VisualizationTabWidget.h" |
|
5 | 5 | #include "Visualization/VisualizationWidget.h" |
|
6 | 6 | #include "Visualization/VisualizationZoneWidget.h" |
|
7 | 7 | |
|
8 | #include <Variable/Variable.h> | |
|
8 | #include <Variable/Variable2.h> | |
|
9 | 9 | |
|
10 |
struct FindVariableOperation::FindVariableOperationPrivate |
|
|
11 | explicit FindVariableOperationPrivate(std::shared_ptr<Variable> variable) : m_Variable{variable} | |
|
10 | struct FindVariableOperation::FindVariableOperationPrivate | |
|
11 | { | |
|
12 | explicit FindVariableOperationPrivate(std::shared_ptr<Variable2> variable) | |
|
13 | : m_Variable { variable } | |
|
12 | 14 | { |
|
13 | 15 | } |
|
14 | 16 | |
|
15 |
void visit(IVisualizationWidget |
|
|
17 | void visit(IVisualizationWidget* widget) | |
|
16 | 18 | { |
|
17 |
if (m_Variable && widget && widget->contains(*m_Variable)) |
|
|
19 | if (m_Variable && widget && widget->contains(*m_Variable)) | |
|
20 | { | |
|
18 | 21 | m_Containers.insert(widget); |
|
19 | 22 | } |
|
20 | 23 | } |
|
21 | 24 | |
|
22 |
std::shared_ptr<Variable> m_Variable; |
|
|
23 |
std::set<IVisualizationWidget |
|
|
25 | std::shared_ptr<Variable2> m_Variable; ///< Variable to find | |
|
26 | std::set<IVisualizationWidget*> m_Containers; ///< Containers found for the variable | |
|
24 | 27 | }; |
|
25 | 28 | |
|
26 | FindVariableOperation::FindVariableOperation(std::shared_ptr<Variable> variable) | |
|
27 | : impl{spimpl::make_unique_impl<FindVariableOperationPrivate>(variable)} | |
|
29 | FindVariableOperation::FindVariableOperation(std::shared_ptr<Variable2> variable) | |
|
30 | : impl { spimpl::make_unique_impl<FindVariableOperationPrivate>(variable) } | |
|
28 | 31 | { |
|
29 | 32 | } |
|
30 | 33 | |
|
31 |
void FindVariableOperation::visitEnter(VisualizationWidget |
|
|
34 | void FindVariableOperation::visitEnter(VisualizationWidget* widget) | |
|
32 | 35 | { |
|
33 | 36 | impl->visit(widget); |
|
34 | 37 | } |
|
35 | 38 | |
|
36 |
void FindVariableOperation::visitLeave(VisualizationWidget |
|
|
39 | void FindVariableOperation::visitLeave(VisualizationWidget* widget) | |
|
37 | 40 | { |
|
38 | 41 | // Does nothing |
|
39 | 42 | Q_UNUSED(widget); |
|
40 | 43 | } |
|
41 | 44 | |
|
42 |
void FindVariableOperation::visitEnter(VisualizationTabWidget |
|
|
45 | void FindVariableOperation::visitEnter(VisualizationTabWidget* tabWidget) | |
|
43 | 46 | { |
|
44 | 47 | impl->visit(tabWidget); |
|
45 | 48 | } |
|
46 | 49 | |
|
47 |
void FindVariableOperation::visitLeave(VisualizationTabWidget |
|
|
50 | void FindVariableOperation::visitLeave(VisualizationTabWidget* tabWidget) | |
|
48 | 51 | { |
|
49 | 52 | // Does nothing |
|
50 | 53 | Q_UNUSED(tabWidget); |
|
51 | 54 | } |
|
52 | 55 | |
|
53 |
void FindVariableOperation::visitEnter(VisualizationZoneWidget |
|
|
56 | void FindVariableOperation::visitEnter(VisualizationZoneWidget* zoneWidget) | |
|
54 | 57 | { |
|
55 | 58 | impl->visit(zoneWidget); |
|
56 | 59 | } |
|
57 | 60 | |
|
58 |
void FindVariableOperation::visitLeave(VisualizationZoneWidget |
|
|
61 | void FindVariableOperation::visitLeave(VisualizationZoneWidget* zoneWidget) | |
|
59 | 62 | { |
|
60 | 63 | // Does nothing |
|
61 | 64 | Q_UNUSED(zoneWidget); |
|
62 | 65 | } |
|
63 | 66 | |
|
64 |
void FindVariableOperation::visit(VisualizationGraphWidget |
|
|
67 | void FindVariableOperation::visit(VisualizationGraphWidget* graphWidget) | |
|
65 | 68 | { |
|
66 | 69 | impl->visit(graphWidget); |
|
67 | 70 | } |
|
68 | 71 | |
|
69 |
std::set<IVisualizationWidget |
|
|
72 | std::set<IVisualizationWidget*> FindVariableOperation::result() const noexcept | |
|
70 | 73 | { |
|
71 | 74 | return impl->m_Containers; |
|
72 | 75 | } |
@@ -1,216 +1,232 | |||
|
1 | 1 | #include "Visualization/operations/GenerateVariableMenuOperation.h" |
|
2 | 2 | #include "Visualization/operations/MenuBuilder.h" |
|
3 | 3 | |
|
4 | 4 | #include "Visualization/VisualizationGraphWidget.h" |
|
5 | 5 | #include "Visualization/VisualizationTabWidget.h" |
|
6 | 6 | #include "Visualization/VisualizationZoneWidget.h" |
|
7 | 7 | |
|
8 | #include <Variable/Variable.h> | |
|
8 | #include <Variable/Variable2.h> | |
|
9 | 9 | |
|
10 | 10 | #include <QMenu> |
|
11 | 11 | #include <QStack> |
|
12 | 12 | |
|
13 | 13 | Q_LOGGING_CATEGORY(LOG_GenerateVariableMenuOperation, "GenerateVariableMenuOperation") |
|
14 | 14 | |
|
15 |
struct GenerateVariableMenuOperation::GenerateVariableMenuOperationPrivate |
|
|
16 | explicit GenerateVariableMenuOperationPrivate( | |
|
17 |
|
|
|
18 |
std::set<IVisualizationWidget |
|
|
19 |
: m_Variable{variable} |
|
|
20 |
|
|
|
21 |
|
|
|
22 |
|
|
|
15 | struct GenerateVariableMenuOperation::GenerateVariableMenuOperationPrivate | |
|
16 | { | |
|
17 | explicit GenerateVariableMenuOperationPrivate(QMenu* menu, std::shared_ptr<Variable2> variable, | |
|
18 | std::set<IVisualizationWidget*> variableContainers) | |
|
19 | : m_Variable { variable } | |
|
20 | , m_VariableContainers { std::move(variableContainers) } | |
|
21 | , m_PlotMenuBuilder { menu } | |
|
22 | , m_UnplotMenuBuilder { menu } | |
|
23 | 23 | { |
|
24 | 24 | } |
|
25 | 25 | |
|
26 | 26 | void visitRootEnter() |
|
27 | 27 | { |
|
28 | 28 | // Creates the root menu |
|
29 | 29 | if (auto plotMenu |
|
30 |
= m_PlotMenuBuilder.addMenu(QObject::tr("Plot"), QIcon{":/icones/plot.png"})) |
|
|
31 | plotMenu->setEnabled(m_Variable && m_Variable->dataSeries() != nullptr); | |
|
30 | = m_PlotMenuBuilder.addMenu(QObject::tr("Plot"), QIcon { ":/icones/plot.png" })) | |
|
31 | { | |
|
32 | plotMenu->setEnabled(m_Variable && m_Variable->data() != nullptr); | |
|
32 | 33 | } |
|
33 | 34 | |
|
34 | m_UnplotMenuBuilder.addMenu(QObject::tr("Unplot"), QIcon{":/icones/unplot.png"}); | |
|
35 | m_UnplotMenuBuilder.addMenu(QObject::tr("Unplot"), QIcon { ":/icones/unplot.png" }); | |
|
35 | 36 | } |
|
36 | 37 | |
|
37 | 38 | void visitRootLeave() |
|
38 | 39 | { |
|
39 | 40 | // Closes the root menu |
|
40 | 41 | m_PlotMenuBuilder.closeMenu(); |
|
41 | 42 | m_UnplotMenuBuilder.closeMenu(); |
|
42 | 43 | } |
|
43 | 44 | |
|
44 |
void visitNodeEnter(const IVisualizationWidget |
|
|
45 | void visitNodeEnter(const IVisualizationWidget& container) | |
|
45 | 46 | { |
|
46 | 47 | // Opens a new menu associated to the node |
|
47 | 48 | m_PlotMenuBuilder.addMenu(container.name()); |
|
48 | 49 | m_UnplotMenuBuilder.addMenu(container.name()); |
|
49 | 50 | } |
|
50 | 51 | |
|
51 | 52 | template <typename ActionFun> |
|
52 | void visitNodeLeavePlot(const IVisualizationWidget &container, const QString &actionName, | |
|
53 | ActionFun actionFunction) | |
|
53 | void visitNodeLeavePlot( | |
|
54 | const IVisualizationWidget& container, const QString& actionName, ActionFun actionFunction) | |
|
54 | 55 | { |
|
55 |
if (isValidContainer(container)) |
|
|
56 | if (isValidContainer(container)) | |
|
57 | { | |
|
56 | 58 | m_PlotMenuBuilder.addSeparator(); |
|
57 | 59 | m_PlotMenuBuilder.addAction(actionName, actionFunction); |
|
58 | 60 | } |
|
59 | 61 | |
|
60 | 62 | // Closes the menu associated to the node |
|
61 | 63 | m_PlotMenuBuilder.closeMenu(); |
|
62 | 64 | } |
|
63 | 65 | |
|
64 | 66 | void visitNodeLeaveUnplot() |
|
65 | 67 | { |
|
66 | 68 | // Closes the menu associated to the node |
|
67 | 69 | m_UnplotMenuBuilder.closeMenu(); |
|
68 | 70 | } |
|
69 | 71 | |
|
70 | 72 | template <typename ActionFun> |
|
71 | void visitLeafPlot(const IVisualizationWidget &container, const QString &actionName, | |
|
72 | ActionFun actionFunction) | |
|
73 | void visitLeafPlot( | |
|
74 | const IVisualizationWidget& container, const QString& actionName, ActionFun actionFunction) | |
|
73 | 75 | { |
|
74 |
if (isValidContainer(container)) |
|
|
76 | if (isValidContainer(container)) | |
|
77 | { | |
|
75 | 78 | m_PlotMenuBuilder.addAction(actionName, actionFunction); |
|
76 | 79 | } |
|
77 | 80 | } |
|
78 | 81 | |
|
79 | 82 | template <typename ActionFun> |
|
80 | void visitLeafUnplot(IVisualizationWidget *container, const QString &actionName, | |
|
81 | ActionFun actionFunction) | |
|
83 | void visitLeafUnplot( | |
|
84 | IVisualizationWidget* container, const QString& actionName, ActionFun actionFunction) | |
|
82 | 85 | { |
|
83 | 86 | // If the container contains the variable, we generate 'unplot' action |
|
84 |
if (m_VariableContainers.count(container) == 1) |
|
|
87 | if (m_VariableContainers.count(container) == 1) | |
|
88 | { | |
|
85 | 89 | m_UnplotMenuBuilder.addAction(actionName, actionFunction); |
|
86 | 90 | } |
|
87 | 91 | } |
|
88 | 92 | |
|
89 |
bool isValidContainer(const IVisualizationWidget |
|
|
93 | bool isValidContainer(const IVisualizationWidget& container) const noexcept | |
|
90 | 94 | { |
|
91 | 95 | // A container is valid if it can contain the variable and if the variable is not already |
|
92 | 96 | // contained in another container |
|
93 | 97 | return m_Variable && m_VariableContainers.size() == 0 && container.canDrop(*m_Variable); |
|
94 | 98 | } |
|
95 | 99 | |
|
96 | std::shared_ptr<Variable> m_Variable; | |
|
97 |
std::set<IVisualizationWidget |
|
|
98 |
MenuBuilder m_PlotMenuBuilder; |
|
|
100 | std::shared_ptr<Variable2> m_Variable; | |
|
101 | std::set<IVisualizationWidget*> m_VariableContainers; | |
|
102 | MenuBuilder m_PlotMenuBuilder; ///< Builder for the 'Plot' menu | |
|
99 | 103 | MenuBuilder m_UnplotMenuBuilder; ///< Builder for the 'Unplot' menu |
|
100 | 104 | }; |
|
101 | 105 | |
|
102 | GenerateVariableMenuOperation::GenerateVariableMenuOperation( | |
|
103 | QMenu *menu, std::shared_ptr<Variable> variable, | |
|
104 | std::set<IVisualizationWidget *> variableContainers) | |
|
105 | : impl{spimpl::make_unique_impl<GenerateVariableMenuOperationPrivate>( | |
|
106 | menu, variable, std::move(variableContainers))} | |
|
106 | GenerateVariableMenuOperation::GenerateVariableMenuOperation(QMenu* menu, | |
|
107 | std::shared_ptr<Variable2> variable, std::set<IVisualizationWidget*> variableContainers) | |
|
108 | : impl { spimpl::make_unique_impl<GenerateVariableMenuOperationPrivate>( | |
|
109 | menu, variable, std::move(variableContainers)) } | |
|
107 | 110 | { |
|
108 | 111 | } |
|
109 | 112 | |
|
110 |
void GenerateVariableMenuOperation::visitEnter(VisualizationWidget |
|
|
113 | void GenerateVariableMenuOperation::visitEnter(VisualizationWidget* widget) | |
|
111 | 114 | { |
|
112 | 115 | // VisualizationWidget is not intended to accommodate a variable |
|
113 | 116 | Q_UNUSED(widget) |
|
114 | 117 | |
|
115 | 118 | // 'Plot' and 'Unplot' menus |
|
116 | 119 | impl->visitRootEnter(); |
|
117 | 120 | } |
|
118 | 121 | |
|
119 |
void GenerateVariableMenuOperation::visitLeave(VisualizationWidget |
|
|
122 | void GenerateVariableMenuOperation::visitLeave(VisualizationWidget* widget) | |
|
120 | 123 | { |
|
121 | 124 | // VisualizationWidget is not intended to accommodate a variable |
|
122 | 125 | Q_UNUSED(widget) |
|
123 | 126 | |
|
124 | 127 | // 'Plot' and 'Unplot' menus |
|
125 | 128 | impl->visitRootLeave(); |
|
126 | 129 | } |
|
127 | 130 | |
|
128 |
void GenerateVariableMenuOperation::visitEnter(VisualizationTabWidget |
|
|
131 | void GenerateVariableMenuOperation::visitEnter(VisualizationTabWidget* tabWidget) | |
|
129 | 132 | { |
|
130 |
if (tabWidget) |
|
|
133 | if (tabWidget) | |
|
134 | { | |
|
131 | 135 | // 'Plot' and 'Unplot' menus |
|
132 | 136 | impl->visitNodeEnter(*tabWidget); |
|
133 | 137 | } |
|
134 |
else |
|
|
138 | else | |
|
139 | { | |
|
135 | 140 | qCCritical(LOG_GenerateVariableMenuOperation(), |
|
136 |
|
|
|
141 | "Can't visit enter VisualizationTabWidget : the widget is null"); | |
|
137 | 142 | } |
|
138 | 143 | } |
|
139 | 144 | |
|
140 |
void GenerateVariableMenuOperation::visitLeave(VisualizationTabWidget |
|
|
145 | void GenerateVariableMenuOperation::visitLeave(VisualizationTabWidget* tabWidget) | |
|
141 | 146 | { |
|
142 |
if (tabWidget) |
|
|
147 | if (tabWidget) | |
|
148 | { | |
|
143 | 149 | // 'Plot' menu |
|
144 | 150 | impl->visitNodeLeavePlot(*tabWidget, QObject::tr("Open in a new zone"), |
|
145 |
|
|
|
146 |
|
|
|
147 | tabWidget->createZone(var); | |
|
148 | } | |
|
149 |
|
|
|
151 | [varW = std::weak_ptr<Variable2> { impl->m_Variable }, tabWidget]() { | |
|
152 | if (auto var = varW.lock()) | |
|
153 | { | |
|
154 | tabWidget->createZone(var); | |
|
155 | } | |
|
156 | }); | |
|
150 | 157 | |
|
151 | 158 | // 'Unplot' menu |
|
152 | 159 | impl->visitNodeLeaveUnplot(); |
|
153 | 160 | } |
|
154 |
else |
|
|
161 | else | |
|
162 | { | |
|
155 | 163 | qCCritical(LOG_GenerateVariableMenuOperation(), |
|
156 |
|
|
|
164 | "Can't visit leave VisualizationTabWidget : the widget is null"); | |
|
157 | 165 | } |
|
158 | 166 | } |
|
159 | 167 | |
|
160 |
void GenerateVariableMenuOperation::visitEnter(VisualizationZoneWidget |
|
|
168 | void GenerateVariableMenuOperation::visitEnter(VisualizationZoneWidget* zoneWidget) | |
|
161 | 169 | { |
|
162 |
if (zoneWidget) |
|
|
170 | if (zoneWidget) | |
|
171 | { | |
|
163 | 172 | // 'Plot' and 'Unplot' menus |
|
164 | 173 | impl->visitNodeEnter(*zoneWidget); |
|
165 | 174 | } |
|
166 |
else |
|
|
175 | else | |
|
176 | { | |
|
167 | 177 | qCCritical(LOG_GenerateVariableMenuOperation(), |
|
168 |
|
|
|
178 | "Can't visit enter VisualizationZoneWidget : the widget is null"); | |
|
169 | 179 | } |
|
170 | 180 | } |
|
171 | 181 | |
|
172 |
void GenerateVariableMenuOperation::visitLeave(VisualizationZoneWidget |
|
|
182 | void GenerateVariableMenuOperation::visitLeave(VisualizationZoneWidget* zoneWidget) | |
|
173 | 183 | { |
|
174 |
if (zoneWidget) |
|
|
184 | if (zoneWidget) | |
|
185 | { | |
|
175 | 186 | // 'Plot' menu |
|
176 | impl->visitNodeLeavePlot( | |
|
177 | *zoneWidget, QObject::tr("Open in a new graph"), | |
|
178 | [ varW = std::weak_ptr<Variable>{impl->m_Variable}, zoneWidget ]() { | |
|
179 | if (auto var = varW.lock()) { | |
|
187 | impl->visitNodeLeavePlot(*zoneWidget, QObject::tr("Open in a new graph"), | |
|
188 | [varW = std::weak_ptr<Variable2> { impl->m_Variable }, zoneWidget]() { | |
|
189 | if (auto var = varW.lock()) | |
|
190 | { | |
|
180 | 191 | zoneWidget->createGraph(var); |
|
181 | 192 | } |
|
182 | 193 | }); |
|
183 | 194 | |
|
184 | 195 | // 'Unplot' menu |
|
185 | 196 | impl->visitNodeLeaveUnplot(); |
|
186 | 197 | } |
|
187 |
else |
|
|
198 | else | |
|
199 | { | |
|
188 | 200 | qCCritical(LOG_GenerateVariableMenuOperation(), |
|
189 |
|
|
|
201 | "Can't visit leave VisualizationZoneWidget : the widget is null"); | |
|
190 | 202 | } |
|
191 | 203 | } |
|
192 | 204 | |
|
193 |
void GenerateVariableMenuOperation::visit(VisualizationGraphWidget |
|
|
205 | void GenerateVariableMenuOperation::visit(VisualizationGraphWidget* graphWidget) | |
|
194 | 206 | { |
|
195 |
if (graphWidget) |
|
|
207 | if (graphWidget) | |
|
208 | { | |
|
196 | 209 | // 'Plot' menu |
|
197 | 210 | impl->visitLeafPlot(*graphWidget, QObject::tr("Open in %1").arg(graphWidget->name()), |
|
198 |
|
|
|
199 |
|
|
|
200 | graphWidget->addVariable(var, graphWidget->graphRange()); | |
|
201 | } | |
|
202 |
|
|
|
211 | [varW = std::weak_ptr<Variable2> { impl->m_Variable }, graphWidget]() { | |
|
212 | if (auto var = varW.lock()) | |
|
213 | { | |
|
214 | graphWidget->addVariable(var, graphWidget->graphRange()); | |
|
215 | } | |
|
216 | }); | |
|
203 | 217 | |
|
204 | 218 | // 'Unplot' menu |
|
205 | 219 | impl->visitLeafUnplot(graphWidget, QObject::tr("Remove from %1").arg(graphWidget->name()), |
|
206 |
|
|
|
207 |
|
|
|
208 | graphWidget->removeVariable(var); | |
|
209 | } | |
|
210 |
|
|
|
220 | [varW = std::weak_ptr<Variable2> { impl->m_Variable }, graphWidget]() { | |
|
221 | if (auto var = varW.lock()) | |
|
222 | { | |
|
223 | graphWidget->removeVariable(var); | |
|
224 | } | |
|
225 | }); | |
|
211 | 226 | } |
|
212 |
else |
|
|
227 | else | |
|
228 | { | |
|
213 | 229 | qCCritical(LOG_GenerateVariableMenuOperation(), |
|
214 |
|
|
|
230 | "Can't visit VisualizationGraphWidget : the widget is null"); | |
|
215 | 231 | } |
|
216 | 232 | } |
@@ -1,70 +1,74 | |||
|
1 | 1 | #include "Visualization/operations/RemoveVariableOperation.h" |
|
2 | 2 | #include "Visualization/VisualizationGraphWidget.h" |
|
3 | 3 | |
|
4 | #include <Variable/Variable.h> | |
|
4 | #include <Variable/Variable2.h> | |
|
5 | 5 | |
|
6 | 6 | Q_LOGGING_CATEGORY(LOG_RemoveVariableOperation, "RemoveVariableOperation") |
|
7 | 7 | |
|
8 |
struct RemoveVariableOperation::RemoveVariableOperationPrivate |
|
|
9 | explicit RemoveVariableOperationPrivate(std::shared_ptr<Variable> variable) | |
|
8 | struct RemoveVariableOperation::RemoveVariableOperationPrivate | |
|
9 | { | |
|
10 | explicit RemoveVariableOperationPrivate(std::shared_ptr<Variable2> variable) | |
|
10 | 11 | : m_Variable(variable) |
|
11 | 12 | { |
|
12 | 13 | } |
|
13 | 14 | |
|
14 | std::shared_ptr<Variable> m_Variable; | |
|
15 | std::shared_ptr<Variable2> m_Variable; | |
|
15 | 16 | }; |
|
16 | 17 | |
|
17 | RemoveVariableOperation::RemoveVariableOperation(std::shared_ptr<Variable> variable) | |
|
18 | : impl{spimpl::make_unique_impl<RemoveVariableOperationPrivate>(variable)} | |
|
18 | RemoveVariableOperation::RemoveVariableOperation(std::shared_ptr<Variable2> variable) | |
|
19 | : impl { spimpl::make_unique_impl<RemoveVariableOperationPrivate>(variable) } | |
|
19 | 20 | { |
|
20 | 21 | } |
|
21 | 22 | |
|
22 |
void RemoveVariableOperation::visitEnter(VisualizationWidget |
|
|
23 | void RemoveVariableOperation::visitEnter(VisualizationWidget* widget) | |
|
23 | 24 | { |
|
24 | 25 | // VisualizationWidget is not intended to contain a variable |
|
25 | 26 | Q_UNUSED(widget) |
|
26 | 27 | } |
|
27 | 28 | |
|
28 |
void RemoveVariableOperation::visitLeave(VisualizationWidget |
|
|
29 | void RemoveVariableOperation::visitLeave(VisualizationWidget* widget) | |
|
29 | 30 | { |
|
30 | 31 | // VisualizationWidget is not intended to contain a variable |
|
31 | 32 | Q_UNUSED(widget) |
|
32 | 33 | } |
|
33 | 34 | |
|
34 |
void RemoveVariableOperation::visitEnter(VisualizationTabWidget |
|
|
35 | void RemoveVariableOperation::visitEnter(VisualizationTabWidget* tabWidget) | |
|
35 | 36 | { |
|
36 | 37 | // VisualizationTabWidget is not intended to contain a variable |
|
37 | 38 | Q_UNUSED(tabWidget) |
|
38 | 39 | } |
|
39 | 40 | |
|
40 |
void RemoveVariableOperation::visitLeave(VisualizationTabWidget |
|
|
41 | void RemoveVariableOperation::visitLeave(VisualizationTabWidget* tabWidget) | |
|
41 | 42 | { |
|
42 | 43 | // VisualizationTabWidget is not intended to contain a variable |
|
43 | 44 | Q_UNUSED(tabWidget) |
|
44 | 45 | } |
|
45 | 46 | |
|
46 |
void RemoveVariableOperation::visitEnter(VisualizationZoneWidget |
|
|
47 | void RemoveVariableOperation::visitEnter(VisualizationZoneWidget* zoneWidget) | |
|
47 | 48 | { |
|
48 | 49 | // VisualizationZoneWidget is not intended to contain a variable |
|
49 | 50 | Q_UNUSED(zoneWidget) |
|
50 | 51 | } |
|
51 | 52 | |
|
52 |
void RemoveVariableOperation::visitLeave(VisualizationZoneWidget |
|
|
53 | void RemoveVariableOperation::visitLeave(VisualizationZoneWidget* zoneWidget) | |
|
53 | 54 | { |
|
54 | 55 | // VisualizationZoneWidget is not intended to contain a variable |
|
55 | 56 | Q_UNUSED(zoneWidget) |
|
56 | 57 | } |
|
57 | 58 | |
|
58 |
void RemoveVariableOperation::visit(VisualizationGraphWidget |
|
|
59 | void RemoveVariableOperation::visit(VisualizationGraphWidget* graphWidget) | |
|
59 | 60 | { |
|
60 |
if (graphWidget) |
|
|
61 | if (graphWidget) | |
|
62 | { | |
|
61 | 63 | // If the widget contains the variable, removes it |
|
62 |
if (impl->m_Variable && graphWidget->contains(*impl->m_Variable)) |
|
|
64 | if (impl->m_Variable && graphWidget->contains(*impl->m_Variable)) | |
|
65 | { | |
|
63 | 66 | graphWidget->removeVariable(impl->m_Variable); |
|
64 | 67 | } |
|
65 | 68 | } |
|
66 |
else |
|
|
69 | else | |
|
70 | { | |
|
67 | 71 | qCCritical(LOG_RemoveVariableOperation(), |
|
68 |
|
|
|
72 | "Can't visit VisualizationGraphWidget : the widget is null"); | |
|
69 | 73 | } |
|
70 | 74 | } |
@@ -1,73 +1,79 | |||
|
1 | 1 | #include "Visualization/operations/RescaleAxeOperation.h" |
|
2 | 2 | #include "Visualization/VisualizationGraphWidget.h" |
|
3 | 3 | |
|
4 | 4 | Q_LOGGING_CATEGORY(LOG_RescaleAxeOperation, "RescaleAxeOperation") |
|
5 | 5 | |
|
6 |
struct RescaleAxeOperation::RescaleAxeOperationPrivate |
|
|
7 | explicit RescaleAxeOperationPrivate(std::shared_ptr<Variable> variable, const DateTimeRange &range) | |
|
8 | : m_Variable{variable}, m_Range{range} | |
|
6 | struct RescaleAxeOperation::RescaleAxeOperationPrivate | |
|
7 | { | |
|
8 | explicit RescaleAxeOperationPrivate( | |
|
9 | std::shared_ptr<Variable2> variable, const DateTimeRange& range) | |
|
10 | : m_Variable { variable }, m_Range { range } | |
|
9 | 11 | { |
|
10 | 12 | } |
|
11 | 13 | |
|
12 | std::shared_ptr<Variable> m_Variable; | |
|
14 | std::shared_ptr<Variable2> m_Variable; | |
|
13 | 15 | DateTimeRange m_Range; |
|
14 | 16 | }; |
|
15 | 17 | |
|
16 | RescaleAxeOperation::RescaleAxeOperation(std::shared_ptr<Variable> variable, const DateTimeRange &range) | |
|
17 | : impl{spimpl::make_unique_impl<RescaleAxeOperationPrivate>(variable, range)} | |
|
18 | RescaleAxeOperation::RescaleAxeOperation( | |
|
19 | std::shared_ptr<Variable2> variable, const DateTimeRange& range) | |
|
20 | : impl { spimpl::make_unique_impl<RescaleAxeOperationPrivate>(variable, range) } | |
|
18 | 21 | { |
|
19 | 22 | } |
|
20 | 23 | |
|
21 |
void RescaleAxeOperation::visitEnter(VisualizationWidget |
|
|
24 | void RescaleAxeOperation::visitEnter(VisualizationWidget* widget) | |
|
22 | 25 | { |
|
23 | 26 | // VisualizationWidget is not intended to contain a variable |
|
24 | 27 | Q_UNUSED(widget) |
|
25 | 28 | } |
|
26 | 29 | |
|
27 |
void RescaleAxeOperation::visitLeave(VisualizationWidget |
|
|
30 | void RescaleAxeOperation::visitLeave(VisualizationWidget* widget) | |
|
28 | 31 | { |
|
29 | 32 | // VisualizationWidget is not intended to contain a variable |
|
30 | 33 | Q_UNUSED(widget) |
|
31 | 34 | } |
|
32 | 35 | |
|
33 |
void RescaleAxeOperation::visitEnter(VisualizationTabWidget |
|
|
36 | void RescaleAxeOperation::visitEnter(VisualizationTabWidget* tabWidget) | |
|
34 | 37 | { |
|
35 | 38 | // VisualizationTabWidget is not intended to contain a variable |
|
36 | 39 | Q_UNUSED(tabWidget) |
|
37 | 40 | } |
|
38 | 41 | |
|
39 |
void RescaleAxeOperation::visitLeave(VisualizationTabWidget |
|
|
42 | void RescaleAxeOperation::visitLeave(VisualizationTabWidget* tabWidget) | |
|
40 | 43 | { |
|
41 | 44 | // VisualizationTabWidget is not intended to contain a variable |
|
42 | 45 | Q_UNUSED(tabWidget) |
|
43 | 46 | } |
|
44 | 47 | |
|
45 |
void RescaleAxeOperation::visitEnter(VisualizationZoneWidget |
|
|
48 | void RescaleAxeOperation::visitEnter(VisualizationZoneWidget* zoneWidget) | |
|
46 | 49 | { |
|
47 | 50 | // VisualizationZoneWidget is not intended to contain a variable |
|
48 | 51 | Q_UNUSED(zoneWidget) |
|
49 | 52 | } |
|
50 | 53 | |
|
51 |
void RescaleAxeOperation::visitLeave(VisualizationZoneWidget |
|
|
54 | void RescaleAxeOperation::visitLeave(VisualizationZoneWidget* zoneWidget) | |
|
52 | 55 | { |
|
53 | 56 | // VisualizationZoneWidget is not intended to contain a variable |
|
54 | 57 | Q_UNUSED(zoneWidget) |
|
55 | 58 | } |
|
56 | 59 | |
|
57 |
void RescaleAxeOperation::visit(VisualizationGraphWidget |
|
|
60 | void RescaleAxeOperation::visit(VisualizationGraphWidget* graphWidget) | |
|
58 | 61 | { |
|
59 |
if (graphWidget) |
|
|
62 | if (graphWidget) | |
|
63 | { | |
|
60 | 64 | // If the widget contains the variable, rescale it |
|
61 |
if (impl->m_Variable && graphWidget->contains(*impl->m_Variable)) |
|
|
65 | if (impl->m_Variable && graphWidget->contains(*impl->m_Variable)) | |
|
66 | { | |
|
62 | 67 | // During rescale, acquisition for the graph is disabled but synchronization is still |
|
63 | 68 | // enabled |
|
64 | 69 | graphWidget->setFlags(GraphFlag::EnableSynchronization); |
|
65 | 70 | graphWidget->setGraphRange(impl->m_Range, true); |
|
66 | 71 | graphWidget->setFlags(GraphFlag::EnableAll); |
|
67 | 72 | } |
|
68 | 73 | } |
|
69 |
else |
|
|
70 | qCCritical(LOG_RescaleAxeOperation(), | |
|
71 | "Can't visit VisualizationGraphWidget : the widget is null"); | |
|
74 | else | |
|
75 | { | |
|
76 | qCCritical( | |
|
77 | LOG_RescaleAxeOperation(), "Can't visit VisualizationGraphWidget : the widget is null"); | |
|
72 | 78 | } |
|
73 | 79 | } |
@@ -1,172 +1,172 | |||
|
1 | 1 | #ifndef GUITESTUTILS_H |
|
2 | 2 | #define GUITESTUTILS_H |
|
3 | 3 | |
|
4 | 4 | #include <Common/cpp_utils.h> |
|
5 | 5 | #include <QCoreApplication> |
|
6 | 6 | #include <QCursor> |
|
7 | 7 | #include <QDesktopWidget> |
|
8 | 8 | #include <QMouseEvent> |
|
9 | 9 | #include <QPoint> |
|
10 | 10 | #include <QtTest> |
|
11 | 11 | |
|
12 | 12 | #include <SqpApplication.h> |
|
13 | 13 | #include <Variable/Variable.h> |
|
14 | 14 | #include <Variable/VariableController2.h> |
|
15 | 15 | #include <qcustomplot.h> |
|
16 | 16 | |
|
17 | 17 | template <typename T> |
|
18 | 18 | QPoint center(T* widget) |
|
19 | 19 | { |
|
20 | 20 | return QPoint { widget->width() / 2, widget->height() / 2 }; |
|
21 | 21 | } |
|
22 | 22 | |
|
23 | 23 | HAS_METHOD(viewport) |
|
24 | 24 | |
|
25 | 25 | template <typename T> |
|
26 | 26 | static inline constexpr bool is_QWidgetOrDerived = std::is_base_of<QWidget, T>::value; |
|
27 | 27 | |
|
28 | 28 | template <typename T> |
|
29 | 29 | using viewport_type = decltype(std::declval<T>().viewport()); |
|
30 | 30 | |
|
31 | 31 | HAS_METHOD(topLevelItem) |
|
32 | 32 | |
|
33 | 33 | template <typename T> |
|
34 | 34 | void mouseMove(T* widget, QPoint pos, Qt::MouseButton mouseModifier) |
|
35 | 35 | { |
|
36 | 36 | QCursor::setPos(widget->mapToGlobal(pos)); |
|
37 | 37 | QMouseEvent event(QEvent::MouseMove, pos, Qt::NoButton, mouseModifier, Qt::NoModifier); |
|
38 | 38 | if constexpr (has_viewport<T>) |
|
39 | 39 | { |
|
40 | 40 | if constexpr (is_QWidgetOrDerived<viewport_type<T>>) |
|
41 | 41 | { |
|
42 | 42 | qApp->sendEvent(widget->viewport(), &event); |
|
43 | 43 | } |
|
44 | 44 | else |
|
45 | 45 | { |
|
46 | 46 | qApp->sendEvent(widget, &event); |
|
47 | 47 | } |
|
48 | 48 | } |
|
49 | 49 | else |
|
50 | 50 | { |
|
51 | 51 | qApp->sendEvent(widget, &event); |
|
52 | 52 | } |
|
53 | 53 | qApp->processEvents(); |
|
54 | 54 | } |
|
55 | 55 | |
|
56 | 56 | |
|
57 | 57 | template <typename T> |
|
58 | 58 | void setMouseTracking(T* widget) |
|
59 | 59 | { |
|
60 | 60 | if constexpr (has_viewport<T>) |
|
61 | 61 | { |
|
62 | 62 | if constexpr (is_QWidgetOrDerived<viewport_type<T>>) |
|
63 | 63 | { |
|
64 | 64 | widget->viewport()->setMouseTracking(true); |
|
65 | 65 | } |
|
66 | 66 | else |
|
67 | 67 | { |
|
68 | 68 | widget->setMouseTracking(true); |
|
69 | 69 | } |
|
70 | 70 | } |
|
71 | 71 | else |
|
72 | 72 | { |
|
73 | 73 | widget->setMouseTracking(true); |
|
74 | 74 | } |
|
75 | 75 | } |
|
76 | 76 | |
|
77 | 77 | template <typename T, typename T2> |
|
78 | 78 | auto getItem(T* widget, T2 itemIndex) |
|
79 | 79 | { |
|
80 | 80 | if constexpr (has_topLevelItem<T>) |
|
81 | 81 | { |
|
82 | 82 | return widget->topLevelItem(itemIndex); |
|
83 | 83 | } |
|
84 | 84 | else |
|
85 | 85 | { |
|
86 | 86 | return widget->item(itemIndex); |
|
87 | 87 | } |
|
88 | 88 | } |
|
89 | 89 | |
|
90 | 90 | #define SELECT_ITEM(widget, itemIndex, item) \ |
|
91 | 91 | auto item = getItem(widget, itemIndex); \ |
|
92 | 92 | { \ |
|
93 | 93 | auto itemCenterPos = widget->visualItemRect(item).center(); \ |
|
94 | 94 | QTest::mouseClick(widget->viewport(), Qt::LeftButton, Qt::NoModifier, itemCenterPos); \ |
|
95 | 95 | QVERIFY(widget->selectedItems().size() > 0); \ |
|
96 | 96 | QVERIFY(widget->selectedItems().contains(item)); \ |
|
97 | 97 | } |
|
98 | 98 | |
|
99 | 99 | |
|
100 | 100 | #define GET_CHILD_WIDGET_FOR_GUI_TESTS(parent, child, childType, childName) \ |
|
101 | 101 | childType* child = parent.findChild<childType*>(childName); \ |
|
102 | 102 | QVERIFY(child != Q_NULLPTR); \ |
|
103 | 103 | setMouseTracking(child); |
|
104 | 104 | |
|
105 | 105 | template <typename T1, typename T2, typename T3, typename T4 = void> |
|
106 | 106 | void dragnDropItem(T1* sourceWidget, T2* destWidget, T3* item, T4* destItem = Q_NULLPTR) |
|
107 | 107 | { |
|
108 | 108 | auto itemCenterPos = sourceWidget->visualItemRect(item).center(); |
|
109 | 109 | if constexpr (has_viewport<T1>) |
|
110 | 110 | { |
|
111 | 111 | QTest::mousePress(sourceWidget->viewport(), Qt::LeftButton, Qt::NoModifier, itemCenterPos); |
|
112 | 112 | } |
|
113 | 113 | else |
|
114 | 114 | { |
|
115 | 115 | QTest::mousePress(sourceWidget, Qt::LeftButton, Qt::NoModifier, itemCenterPos); |
|
116 | 116 | } |
|
117 | 117 | mouseMove(sourceWidget, itemCenterPos, Qt::LeftButton); |
|
118 | 118 | itemCenterPos += QPoint(0, -10); |
|
119 | 119 | QTimer::singleShot(100, [destWidget, destItem]() { |
|
120 | 120 | mouseMove(destWidget, destWidget->rect().center(), Qt::LeftButton); |
|
121 | 121 | mouseMove(destWidget, destWidget->rect().center() + QPoint(0, -10), Qt::LeftButton); |
|
122 | 122 | if constexpr (!std::is_same_v<void, T4>) |
|
123 | 123 | { |
|
124 | 124 | auto destItemCenterPos = destWidget->visualItemRect(destItem).center(); |
|
125 | 125 | QTest::mouseRelease(destWidget, Qt::LeftButton, Qt::NoModifier, destItemCenterPos); |
|
126 | 126 | } |
|
127 | 127 | else if constexpr (has_viewport<T2>) |
|
128 | 128 | { |
|
129 | 129 | QTest::mouseRelease(destWidget->viewport(), Qt::LeftButton); |
|
130 | 130 | } |
|
131 | 131 | else |
|
132 | 132 | { |
|
133 | 133 | QTest::mouseRelease(destWidget, Qt::LeftButton); |
|
134 | 134 | } |
|
135 | 135 | QTest::mouseRelease(destWidget->viewport(), Qt::LeftButton); |
|
136 | 136 | }); |
|
137 | 137 | mouseMove(sourceWidget, itemCenterPos, Qt::LeftButton); |
|
138 | 138 | } |
|
139 | 139 | |
|
140 | 140 | template <typename T> |
|
141 | 141 | void scroll_graph(T* w, int dx) |
|
142 | 142 | { |
|
143 | 143 | auto cent = center(w); |
|
144 | 144 | QTest::mousePress(w, Qt::LeftButton, Qt::NoModifier, cent, 1); |
|
145 | 145 | mouseMove(w, { cent.x() + dx, cent.y() }, Qt::LeftButton); |
|
146 | 146 | QTest::mouseRelease(w, Qt::LeftButton); |
|
147 | 147 | } |
|
148 | 148 | |
|
149 | 149 | ALIAS_TEMPLATE_FUNCTION(isReady, static_cast<SqpApplication*>(qApp)->variableController().isReady) |
|
150 | 150 | |
|
151 | void waitForVar(std::shared_ptr<Variable> var) | |
|
151 | void waitForVar(std::shared_ptr<Variable2> var) | |
|
152 | 152 | { |
|
153 | 153 | while (!isReady(var)) |
|
154 | 154 | QCoreApplication::processEvents(); |
|
155 | 155 | } |
|
156 | 156 | |
|
157 | 157 | template <typename T> |
|
158 | 158 | bool prepare_gui_test(T* w) |
|
159 | 159 | { |
|
160 | 160 | w->setGeometry(QRect( |
|
161 | 161 | QPoint(QApplication::desktop()->geometry().center() - QPoint(250, 250)), QSize(500, 500))); |
|
162 | 162 | w->show(); |
|
163 | 163 | qApp->setActiveWindow(w); |
|
164 | 164 | return QTest::qWaitForWindowActive(w); |
|
165 | 165 | } |
|
166 | 166 | |
|
167 | 167 | #define GET_CHILD_WIDGET_FOR_GUI_TESTS(parent, child, childType, childName) \ |
|
168 | 168 | childType* child = parent.findChild<childType*>(childName); \ |
|
169 | 169 | QVERIFY(child != Q_NULLPTR); \ |
|
170 | 170 | setMouseTracking(child); |
|
171 | 171 | |
|
172 | 172 | #endif |
@@ -1,109 +1,111 | |||
|
1 |
#include <Q |
|
|
1 | #include <QMainWindow> | |
|
2 | 2 | #include <QObject> |
|
3 | #include <QString> | |
|
4 | 3 | #include <QScreen> |
|
5 |
#include <Q |
|
|
4 | #include <QString> | |
|
6 | 5 | #include <QWheelEvent> |
|
6 | #include <QtTest> | |
|
7 | 7 | |
|
8 | 8 | #include <qcustomplot.h> |
|
9 | 9 | |
|
10 | #include <Common/cpp_utils.h> | |
|
10 | 11 | #include <SqpApplication.h> |
|
11 | 12 | #include <Variable/VariableController2.h> |
|
12 | #include <Common/cpp_utils.h> | |
|
13 | 13 | |
|
14 | #include <Visualization/VisualizationZoneWidget.h> | |
|
15 | #include <Visualization/VisualizationGraphWidget.h> | |
|
16 | #include <TestProviders.h> | |
|
17 | 14 | #include <GUITestUtils.h> |
|
15 | #include <TestProviders.h> | |
|
16 | #include <Visualization/VisualizationGraphWidget.h> | |
|
17 | #include <Visualization/VisualizationZoneWidget.h> | |
|
18 | 18 | |
|
19 | template <int GraphCount=2> | |
|
20 |
std::tuple< |
|
|
21 | std::vector<std::shared_ptr<Variable>>, | |
|
22 | std::vector<VisualizationGraphWidget*>, | |
|
23 | DateTimeRange> | |
|
19 | template <int GraphCount = 2> | |
|
20 | std::tuple<std::unique_ptr<VisualizationZoneWidget>, std::vector<std::shared_ptr<Variable2>>, | |
|
21 | std::vector<VisualizationGraphWidget*>, DateTimeRange> | |
|
24 | 22 | build_multi_graph_test() |
|
25 | 23 | { |
|
26 | 24 | auto w = std::make_unique<VisualizationZoneWidget>(); |
|
27 |
auto provider = std::make_shared<SimpleRange<10> |
|
|
28 | auto range = DateTimeRange::fromDateTime(QDate(2018, 8, 7), QTime(14, 00), QDate(2018, 8, 7),QTime(16, 00)); | |
|
29 | std::vector<std::shared_ptr<Variable>> variables; | |
|
25 | auto provider = std::make_shared<SimpleRange<10>>(); | |
|
26 | auto range = DateTimeRange::fromDateTime( | |
|
27 | QDate(2018, 8, 7), QTime(14, 00), QDate(2018, 8, 7), QTime(16, 00)); | |
|
28 | std::vector<std::shared_ptr<Variable2>> variables; | |
|
30 | 29 | std::vector<VisualizationGraphWidget*> graphs; |
|
31 | for(auto i=0;i<GraphCount;i++) | |
|
30 | for (auto i = 0; i < GraphCount; i++) | |
|
32 | 31 | { |
|
33 |
auto var = static_cast<SqpApplication |
|
|
34 |
|
|
|
32 | auto var = static_cast<SqpApplication*>(qApp)->variableController().createVariable( | |
|
33 | QString("V%1").arg(i), { { "", "scalar" } }, provider, range); | |
|
35 | 34 | auto graph = new VisualizationGraphWidget(); |
|
36 | 35 | graph->addVariable(var, range); |
|
37 | 36 | while (!isReady(var)) |
|
38 | 37 | QCoreApplication::processEvents(); |
|
39 | 38 | variables.push_back(var); |
|
40 | 39 | graphs.push_back(graph); |
|
41 | 40 | w->addGraph(graph); |
|
42 | 41 | } |
|
43 | return {std::move(w), variables, graphs, range}; | |
|
42 | return { std::move(w), variables, graphs, range }; | |
|
44 | 43 | } |
|
45 | 44 | |
|
46 | 45 | |
|
47 |
class A_MultipleSyncGraphs : public QObject |
|
|
46 | class A_MultipleSyncGraphs : public QObject | |
|
47 | { | |
|
48 | 48 | Q_OBJECT |
|
49 | 49 | public: |
|
50 |
explicit A_MultipleSyncGraphs(QObject |
|
|
50 | explicit A_MultipleSyncGraphs(QObject* parent = Q_NULLPTR) : QObject(parent) {} | |
|
51 | 51 | |
|
52 | 52 | private slots: |
|
53 | 53 | void scrolls_left_with_mouse() |
|
54 | 54 | { |
|
55 | 55 | auto [w, variables, graphs, range] = build_multi_graph_test<3>(); |
|
56 |
auto var |
|
|
56 | auto var = variables.front(); | |
|
57 | 57 | auto graph = graphs.front(); |
|
58 | 58 | QVERIFY(prepare_gui_test(w.get())); |
|
59 |
for (auto i = 0; i < 100; i++) |
|
|
59 | for (auto i = 0; i < 100; i++) | |
|
60 | { | |
|
60 | 61 | scroll_graph(graph, -200); |
|
61 | 62 | waitForVar(var); |
|
62 | 63 | } |
|
63 | 64 | auto r = variables.back()->range(); |
|
64 | 65 | |
|
65 | 66 | /* |
|
66 | 67 | * Scrolling to the left implies going back in time |
|
67 | 68 | * Scroll only implies keeping the same delta T -> shit only transformation |
|
68 | */ | |
|
69 | */ | |
|
69 | 70 | QVERIFY(r.m_TEnd < range.m_TEnd); |
|
70 | QVERIFY(SciQLop::numeric::almost_equal<double>(r.delta(),range.delta(),1)); | |
|
71 | QVERIFY(SciQLop::numeric::almost_equal<double>(r.delta(), range.delta(), 1)); | |
|
71 | 72 | } |
|
72 | 73 | |
|
73 | 74 | void scrolls_right_with_mouse() |
|
74 | 75 | { |
|
75 | 76 | auto [w, variables, graphs, range] = build_multi_graph_test<3>(); |
|
76 |
auto var |
|
|
77 | auto var = variables.front(); | |
|
77 | 78 | auto graph = graphs.front(); |
|
78 | 79 | QVERIFY(prepare_gui_test(w.get())); |
|
79 |
for (auto i = 0; i < 100; i++) |
|
|
80 | for (auto i = 0; i < 100; i++) | |
|
81 | { | |
|
80 | 82 | scroll_graph(graph, 200); |
|
81 | 83 | waitForVar(var); |
|
82 | 84 | } |
|
83 | 85 | auto r = variables.back()->range(); |
|
84 | 86 | |
|
85 | 87 | /* |
|
86 | 88 | * Scrolling to the right implies going forward in time |
|
87 | 89 | * Scroll only implies keeping the same delta T -> shit only transformation |
|
88 | */ | |
|
90 | */ | |
|
89 | 91 | QVERIFY(r.m_TEnd > range.m_TEnd); |
|
90 | QVERIFY(SciQLop::numeric::almost_equal<double>(r.delta(),range.delta(),1)); | |
|
92 | QVERIFY(SciQLop::numeric::almost_equal<double>(r.delta(), range.delta(), 1)); | |
|
91 | 93 | } |
|
92 | 94 | }; |
|
93 | 95 | |
|
94 | 96 | |
|
95 | 97 | QT_BEGIN_NAMESPACE |
|
96 | 98 | QTEST_ADD_GPU_BLACKLIST_SUPPORT_DEFS |
|
97 | 99 | QT_END_NAMESPACE |
|
98 |
int main(int argc, char |
|
|
100 | int main(int argc, char* argv[]) | |
|
99 | 101 | { |
|
100 | SqpApplication app{argc, argv}; | |
|
102 | SqpApplication app { argc, argv }; | |
|
101 | 103 | app.setAttribute(Qt::AA_Use96Dpi, true); |
|
102 | 104 | QTEST_DISABLE_KEYPAD_NAVIGATION; |
|
103 | 105 | QTEST_ADD_GPU_BLACKLIST_SUPPORT; |
|
104 | 106 | A_MultipleSyncGraphs tc; |
|
105 | 107 | QTEST_SET_MAIN_SOURCE_PATH; |
|
106 | 108 | return QTest::qExec(&tc, argc, argv); |
|
107 | 109 | } |
|
108 | 110 | |
|
109 | 111 | #include "main.moc" |
@@ -1,90 +1,95 | |||
|
1 |
#include <Q |
|
|
1 | #include <QMainWindow> | |
|
2 | 2 | #include <QObject> |
|
3 | #include <QString> | |
|
4 | 3 | #include <QScreen> |
|
5 |
#include <Q |
|
|
4 | #include <QString> | |
|
6 | 5 | #include <QWheelEvent> |
|
6 | #include <QtTest> | |
|
7 | 7 | |
|
8 | 8 | #include <qcustomplot.h> |
|
9 | 9 | |
|
10 | #include <Common/cpp_utils.h> | |
|
10 | 11 | #include <SqpApplication.h> |
|
11 | 12 | #include <Variable/VariableController2.h> |
|
12 | #include <Common/cpp_utils.h> | |
|
13 | 13 | |
|
14 | #include <Visualization/VisualizationGraphWidget.h> | |
|
15 | #include <TestProviders.h> | |
|
16 | 14 | #include <GUITestUtils.h> |
|
17 |
#include < |
|
|
15 | #include <TestProviders.h> | |
|
16 | #include <Variable/Variable2.h> | |
|
17 | #include <Visualization/VisualizationGraphWidget.h> | |
|
18 | 18 | |
|
19 |
std::tuple< |
|
|
20 | std::shared_ptr<Variable>, | |
|
21 | DateTimeRange > | |
|
19 | std::tuple<std::unique_ptr<VisualizationGraphWidget>, std::shared_ptr<Variable2>, DateTimeRange> | |
|
22 | 20 | build_simple_graph_test() |
|
23 | 21 | { |
|
24 | 22 | auto w = std::make_unique<VisualizationGraphWidget>(); |
|
25 |
auto provider = std::make_shared<SimpleRange<10> |
|
|
26 | auto range = DateTimeRange::fromDateTime(QDate(2018, 8, 7), QTime(14, 00), QDate(2018, 8, 7),QTime(16, 00)); | |
|
27 | auto var = static_cast<SqpApplication *>(qApp)->variableController().createVariable("V1", {{"", "scalar"}}, provider, range); | |
|
28 | while (!isReady(var)) QCoreApplication::processEvents(); | |
|
23 | auto provider = std::make_shared<SimpleRange<10>>(); | |
|
24 | auto range = DateTimeRange::fromDateTime( | |
|
25 | QDate(2018, 8, 7), QTime(14, 00), QDate(2018, 8, 7), QTime(16, 00)); | |
|
26 | auto var = static_cast<SqpApplication*>(qApp)->variableController().createVariable( | |
|
27 | "V1", { { "", "scalar" } }, provider, range); | |
|
28 | while (!isReady(var)) | |
|
29 | QCoreApplication::processEvents(); | |
|
29 | 30 | w->addVariable(var, range); |
|
30 | while (!isReady(var)) QCoreApplication::processEvents(); | |
|
31 | return {std::move(w), var, range}; | |
|
31 | while (!isReady(var)) | |
|
32 | QCoreApplication::processEvents(); | |
|
33 | return { std::move(w), var, range }; | |
|
32 | 34 | } |
|
33 | 35 | |
|
34 | 36 | |
|
35 |
class A_SimpleGraph : public QObject |
|
|
37 | class A_SimpleGraph : public QObject | |
|
38 | { | |
|
36 | 39 | Q_OBJECT |
|
37 | 40 | public: |
|
38 |
explicit A_SimpleGraph(QObject |
|
|
41 | explicit A_SimpleGraph(QObject* parent = Q_NULLPTR) : QObject(parent) {} | |
|
39 | 42 | |
|
40 | 43 | private slots: |
|
41 | 44 | void scrolls_left_with_mouse() |
|
42 | 45 | { |
|
43 | 46 | auto [w, var, range] = build_simple_graph_test(); |
|
44 | 47 | QVERIFY(prepare_gui_test(w.get())); |
|
45 |
for (auto i = 0; i < 100; i++) |
|
|
48 | for (auto i = 0; i < 100; i++) | |
|
49 | { | |
|
46 | 50 | scroll_graph(w.get(), 200); |
|
47 | 51 | waitForVar(var); |
|
48 | 52 | } |
|
49 | 53 | auto r = var->range(); |
|
50 | 54 | /* |
|
51 | 55 | * Scrolling to the left implies going back in time |
|
52 | 56 | * Scroll only implies keeping the same delta T -> shit only transformation |
|
53 | */ | |
|
57 | */ | |
|
54 | 58 | QVERIFY(r.m_TEnd < range.m_TEnd); |
|
55 | QVERIFY(SciQLop::numeric::almost_equal<double>(r.delta(),range.delta(),1)); | |
|
59 | QVERIFY(SciQLop::numeric::almost_equal<double>(r.delta(), range.delta(), 1)); | |
|
56 | 60 | } |
|
57 | 61 | |
|
58 | 62 | void scrolls_right_with_mouse() |
|
59 | 63 | { |
|
60 | 64 | auto [w, var, range] = build_simple_graph_test(); |
|
61 | 65 | QVERIFY(prepare_gui_test(w.get())); |
|
62 |
for (auto i = 0; i < 100; i++) |
|
|
66 | for (auto i = 0; i < 100; i++) | |
|
67 | { | |
|
63 | 68 | scroll_graph(w.get(), -200); |
|
64 | 69 | waitForVar(var); |
|
65 | 70 | } |
|
66 | 71 | auto r = var->range(); |
|
67 | 72 | /* |
|
68 | 73 | * Scrolling to the right implies going forward in time |
|
69 | 74 | * Scroll only implies keeping the same delta T -> shit only transformation |
|
70 | */ | |
|
75 | */ | |
|
71 | 76 | QVERIFY(r.m_TEnd > range.m_TEnd); |
|
72 | QVERIFY(SciQLop::numeric::almost_equal<double>(r.delta(),range.delta(),1)); | |
|
77 | QVERIFY(SciQLop::numeric::almost_equal<double>(r.delta(), range.delta(), 1)); | |
|
73 | 78 | } |
|
74 | 79 | }; |
|
75 | 80 | |
|
76 | 81 | QT_BEGIN_NAMESPACE |
|
77 | 82 | QTEST_ADD_GPU_BLACKLIST_SUPPORT_DEFS |
|
78 | 83 | QT_END_NAMESPACE |
|
79 |
int main(int argc, char |
|
|
84 | int main(int argc, char* argv[]) | |
|
80 | 85 | { |
|
81 | SqpApplication app{argc, argv}; | |
|
86 | SqpApplication app { argc, argv }; | |
|
82 | 87 | app.setAttribute(Qt::AA_Use96Dpi, true); |
|
83 | 88 | QTEST_DISABLE_KEYPAD_NAVIGATION; |
|
84 | 89 | QTEST_ADD_GPU_BLACKLIST_SUPPORT; |
|
85 | 90 | A_SimpleGraph tc; |
|
86 | 91 | QTEST_SET_MAIN_SOURCE_PATH; |
|
87 | 92 | return QTest::qExec(&tc, argc, argv); |
|
88 | 93 | } |
|
89 | 94 | |
|
90 | 95 | #include "main.moc" |
General Comments 0
You need to be logged in to leave comments.
Login now