##// END OF EJS Templates
PySide2 bindings + some GUI clean...
jeandet -
r1478:6e3f56cd8c8b
parent child
Show More
@@ -1,79 +1,79
1 find_package(PythonLibs 3 REQUIRED)
1 find_package(PythonLibs 3 REQUIRED)
2 find_package(PythonInterp 3 REQUIRED)
2 find_package(PythonInterp 3 REQUIRED)
3 find_package(PySide2 REQUIRED)
3 find_package(PySide2 REQUIRED)
4 find_package(Shiboken2 REQUIRED)
4 find_package(Shiboken2 REQUIRED)
5 include(PythonInfo)
5 include(PythonInfo)
6 find_python_site_packages(PYTHON_SITE_PACKAGES)
6 find_python_site_packages(PYTHON_SITE_PACKAGES)
7
7
8 set(BINDINGS_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
8 set(BINDINGS_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
9 set(BINDINGS_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}")
9 set(BINDINGS_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}")
10
10
11 configure_file("${BINDINGS_SRC_DIR}/bindings.xml" "${BINDINGS_BUILD_DIR}/bindings.xml" COPYONLY)
11 configure_file("${BINDINGS_SRC_DIR}/bindings.xml" "${BINDINGS_BUILD_DIR}/bindings.xml" COPYONLY)
12 configure_file("${BINDINGS_SRC_DIR}/main.py" "${BINDINGS_BUILD_DIR}/main.py" COPYONLY)
12 configure_file("${BINDINGS_SRC_DIR}/main.py" "${BINDINGS_BUILD_DIR}/main.py" COPYONLY)
13
13
14 execute_process(COMMAND "${PYTHON_EXECUTABLE}" "${BINDINGS_SRC_DIR}/src_list.py" cmake "${BINDINGS_BUILD_DIR}" OUTPUT_VARIABLE BINDINGS_SOURCE)
14 execute_process(COMMAND "${PYTHON_EXECUTABLE}" "${BINDINGS_SRC_DIR}/src_list.py" cmake "${BINDINGS_BUILD_DIR}" OUTPUT_VARIABLE BINDINGS_SOURCE)
15
15
16 set_property(SOURCE ${BINDINGS_SOURCE} PROPERTY SKIP_AUTOGEN ON)
16 set_property(SOURCE ${BINDINGS_SOURCE} PROPERTY SKIP_AUTOGEN ON)
17
17
18 list(APPEND BINDINGS_INCLUDE_DIRS
18 list(APPEND BINDINGS_INCLUDE_DIRS
19 ${PYTHON_INCLUDE_DIRS}
19 ${PYTHON_INCLUDE_DIRS}
20 ${Qt5Core_INCLUDE_DIRS}
20 ${Qt5Core_INCLUDE_DIRS}
21 ${Qt5Widgets_INCLUDE_DIRS}
21 ${Qt5Widgets_INCLUDE_DIRS}
22 ${Qt5Gui_INCLUDE_DIRS}
22 ${Qt5Gui_INCLUDE_DIRS}
23 ${CMAKE_CURRENT_SOURCE_DIR}/../../gui/include
23 ${CMAKE_CURRENT_SOURCE_DIR}/../../gui/include
24 ${CMAKE_CURRENT_SOURCE_DIR}/../../core/include
24 ${CMAKE_CURRENT_SOURCE_DIR}/../../core/include
25 ${CMAKE_CURRENT_SOURCE_DIR}/../../core/external/TimeSeries/include
25 ${CMAKE_CURRENT_SOURCE_DIR}/../../core/external/TimeSeries/include
26 )
26 )
27 list(REMOVE_DUPLICATES BINDINGS_INCLUDE_DIRS)
27 list(REMOVE_DUPLICATES BINDINGS_INCLUDE_DIRS)
28 foreach(DIR ${BINDINGS_INCLUDE_DIRS})
28 foreach(DIR ${BINDINGS_INCLUDE_DIRS})
29 list(APPEND BINDINGS_INCLUDE_DIRS_ARGS "-I${DIR}")
29 list(APPEND BINDINGS_INCLUDE_DIRS_ARGS "-I${DIR}")
30 endforeach()
30 endforeach()
31
31
32 set(SHIBOKEN_OPTIONS --generator-set=shiboken
32 set(SHIBOKEN_OPTIONS --generator-set=shiboken
33 --enable-parent-ctor-heuristic
33 --enable-parent-ctor-heuristic
34 --enable-return-value-heuristic
34 --enable-return-value-heuristic
35 --use-isnull-as-nb_nonzero
35 --use-isnull-as-nb_nonzero
36 --avoid-protected-hack
36 --avoid-protected-hack
37 --enable-pyside-extensions
37 --enable-pyside-extensions
38 -std=c++17)
38 -std=c++17)
39 add_custom_command(
39 add_custom_command(
40 OUTPUT ${BINDINGS_SOURCE}
40 OUTPUT ${BINDINGS_SOURCE}
41 COMMAND Shiboken2::shiboken2 ${SHIBOKEN_OPTIONS}
41 COMMAND Shiboken2::shiboken2 ${SHIBOKEN_OPTIONS}
42 ${BINDINGS_INCLUDE_DIRS_ARGS}
42 ${BINDINGS_INCLUDE_DIRS_ARGS}
43 --typesystem-paths=${PYSIDE_TYPESYSTEMS}
43 --typesystem-paths=${PYSIDE_TYPESYSTEMS}
44 --output-directory=${CMAKE_CURRENT_BINARY_DIR}
44 --output-directory=${CMAKE_CURRENT_BINARY_DIR}
45 ${CMAKE_CURRENT_SOURCE_DIR}/bindings.h ${CMAKE_CURRENT_SOURCE_DIR}/bindings.xml
45 ${CMAKE_CURRENT_SOURCE_DIR}/bindings.h ${CMAKE_CURRENT_SOURCE_DIR}/bindings.xml
46
46
47 DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/bindings.xml"
47 DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/bindings.xml"
48 IMPLICIT_DEPENDS CXX "${CMAKE_CURRENT_SOURCE_DIR}/bindings.h"
48 IMPLICIT_DEPENDS CXX "${CMAKE_CURRENT_SOURCE_DIR}/bindings.h"
49 WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
49 WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
50 COMMENT "Generating Python bindings with shiboken2")
50 COMMENT "Generating Python bindings with shiboken2")
51
51
52 include_directories(
52 include_directories(
53 ${PYSIDE_INCLUDE_DIR}/QtCore
53 ${PYSIDE_INCLUDE_DIR}/QtCore
54 ${PYSIDE_INCLUDE_DIR}/QtGui
54 ${PYSIDE_INCLUDE_DIR}/QtGui
55 ${PYSIDE_INCLUDE_DIR}/QtWidgets)
55 ${PYSIDE_INCLUDE_DIR}/QtWidgets)
56
56
57 include_directories(
57 include_directories(
58 ${PYTHON_SITE_PACKAGES}/numpy/core/include/
58 ${PYTHON_SITE_PACKAGES}/numpy/core/include/
59 ${PYTHON_INCLUDE_DIRS}
59 ${PYTHON_INCLUDE_DIRS}
60 ${SHIBOKEN_INCLUDE_DIR}
60 ${SHIBOKEN_INCLUDE_DIR}
61 ${PYSIDE_INCLUDE_DIR}
61 ${PYSIDE_INCLUDE_DIR}
62 ${PYSIDE_INCLUDE_DIR}/QtCore
62 ${PYSIDE_INCLUDE_DIR}/QtCore
63 ${PYSIDE_INCLUDE_DIR}/QtGui
63 ${PYSIDE_INCLUDE_DIR}/QtGui
64 ${PYSIDE_INCLUDE_DIR}/QtWidgets)
64 ${PYSIDE_INCLUDE_DIR}/QtWidgets)
65
65
66 add_library(SciQLopBindings MODULE ${BINDINGS_SOURCE} numpy_wrappers.h)
66 add_library(SciQLopBindings MODULE ${BINDINGS_SOURCE} numpy_wrappers.h PyDataProvider.h)
67 set_target_properties(
67 set_target_properties(
68 SciQLopBindings
68 SciQLopBindings
69 PROPERTIES
69 PROPERTIES
70 PREFIX ""
70 PREFIX ""
71 OUTPUT_NAME "SciQLopBindings"
71 OUTPUT_NAME "SciQLopBindings"
72 )
72 )
73 target_link_libraries(SciQLopBindings sciqlopapp)
73 target_link_libraries(SciQLopBindings sciqlopapp)
74 target_link_libraries(SciQLopBindings Shiboken2::libshiboken)
74 target_link_libraries(SciQLopBindings Shiboken2::libshiboken)
75 target_link_libraries(SciQLopBindings PySide2::pyside2)
75 target_link_libraries(SciQLopBindings PySide2::pyside2)
76
76
77 add_executable(debug_sciqlop_app main.cpp )
77 add_executable(debug_sciqlop_app main.cpp )
78 find_package (Python3 COMPONENTS Development)
78 find_package (Python3 COMPONENTS Development)
79 target_link_libraries(debug_sciqlop_app PRIVATE Python3::Python)
79 target_link_libraries(debug_sciqlop_app PRIVATE Python3::Python)
@@ -1,16 +1,89
1 #pragma once
1 #pragma once
2 #include <Data/DataProviderParameters.h>
2 #include <Data/IDataProvider.h>
3 #include <Data/IDataProvider.h>
4 #include <DataSource/DataSourceController.h>
5 #include <DataSource/DataSourceItem.h>
6 #include <DataSource/DataSourceItemAction.h>
7 #include <QPair>
8 #include <SqpApplication.h>
9 // must be included last because of Python/Qt definition of slots
10 #include "numpy_wrappers.h"
11
12 struct Product
13 {
14 QString path;
15 std::vector<std::string> components;
16 QMap<QString, QString> metadata;
17 Product() = default;
18 explicit Product(const QString& path, const std::vector<std::string>& components,
19 const QMap<QString, QString>& metadata)
20 : path { path }, components { components }, metadata { metadata }
21 {
22 }
23 virtual ~Product() = default;
24 };
3
25
4 class PyDataProvider : public IDataProvider
26 class PyDataProvider : public IDataProvider
5 {
27 {
6 public:
28 public:
7 PyDataProvider() {}
29 PyDataProvider()
30 {
31 auto& dataSourceController = sqpApp->dataSourceController();
32 dataSourceController.registerProvider(this);
33 }
34
35 virtual ~PyDataProvider() {}
8
36
9 virtual TimeSeries::ITimeSerie getData(const std::string& key, double start_time, double stop_time)
37 virtual QPair<NpArray, NpArray> getData(
10 {}
38 const std::string& key, double start_time, double stop_time)
39 {
40 (void)key, (void)start_time, (void)stop_time;
41 return {};
42 }
11
43
12 virtual TimeSeries::ITimeSerie* getData(const DataProviderParameters& parameters)
44 virtual TimeSeries::ITimeSerie* getData(const DataProviderParameters& parameters) override
13 {
45 {
46 if (parameters.m_Data.contains("name"))
47 {
48 auto data = getData(parameters.m_Data["name"].toString().toStdString(),
49 parameters.m_Range.m_TStart, parameters.m_Range.m_TEnd);
50 // TODO add shape/type switch
51 return new ScalarTimeSerie { data.first.to_std_vect(), data.second.to_std_vect() };
52 }
14 return nullptr;
53 return nullptr;
15 }
54 }
55
56
57 inline void register_products(const QVector<Product*>& products)
58 {
59 auto& dataSourceController = sqpApp->dataSourceController();
60 auto id = this->id();
61 auto data_source_name = this->name();
62 std::for_each(std::cbegin(products), std::cend(products),
63 [&id, &dataSourceController](const Product* product) {
64 dataSourceController.setDataSourceItem(id, product->path, product->metadata);
65 });
66 }
16 };
67 };
68
69
70 struct Providers
71 {
72 Providers() = default;
73 virtual ~Providers() = default;
74 inline void register_provider(PyDataProvider* provider)
75 {
76 auto& dataSourceController = sqpApp->dataSourceController();
77 dataSourceController.setDataProvider(
78 provider->id(), std::unique_ptr<IDataProvider>(provider));
79 }
80 };
81
82
83 inline ScalarTimeSerie test_PyDataProvider(PyDataProvider& prov)
84 {
85 auto v = prov.getData("", 0., 0.);
86 ScalarTimeSerie s;
87 s.set_data(v.first.to_std_vect(), v.second.to_std_vect());
88 return s;
89 }
@@ -1,72 +1,91
1 <?xml version="1.0"?>
1 <?xml version="1.0"?>
2 <typesystem package="SciQLopBindings">
2 <typesystem package="SciQLopBindings">
3 <load-typesystem name="typesystem_core.xml" generate="no" />
3 <load-typesystem name="typesystem_core.xml" generate="no" />
4 <load-typesystem name="typesystem_gui.xml" generate="no" />
4 <load-typesystem name="typesystem_gui.xml" generate="no" />
5 <load-typesystem name="typesystem_widgets.xml" generate="no" />
5 <load-typesystem name="typesystem_widgets.xml" generate="no" />
6 <primitive-type name="std::string"/>
6 <primitive-type name="std::string"/>
7 <primitive-type name="std::size_t"/>
7 <primitive-type name="std::size_t"/>
8 <container-type name="std::vector" type="vector">
8 <container-type name="std::vector" type="vector">
9 <include file-name="vector" location="global"/>
9 <include file-name="vector" location="global"/>
10 <conversion-rule>
10 <conversion-rule>
11 <native-to-target>
11 <native-to-target>
12 %INTYPE::size_type vectorSize = %in.size();
12 %INTYPE::size_type vectorSize = %in.size();
13 PyObject* %out = PyList_New((int) vectorSize);
13 PyObject* %out = PyList_New((int) vectorSize);
14 for (%INTYPE::size_type idx = 0; idx &lt; vectorSize; ++idx) {
14 for (%INTYPE::size_type idx = 0; idx &lt; vectorSize; ++idx) {
15 %INTYPE_0 cppItem(%in[idx]);
15 %INTYPE_0 cppItem(%in[idx]);
16 PyList_SET_ITEM(%out, idx, %CONVERTTOPYTHON[%INTYPE_0](cppItem));
16 PyList_SET_ITEM(%out, idx, %CONVERTTOPYTHON[%INTYPE_0](cppItem));
17 }
17 }
18 return %out;
18 return %out;
19 </native-to-target>
19 </native-to-target>
20 <target-to-native>
20 <target-to-native>
21 <add-conversion type="PySequence">
21 <add-conversion type="PySequence">
22 Shiboken::AutoDecRef seq(PySequence_Fast(%in, 0));
22 Shiboken::AutoDecRef seq(PySequence_Fast(%in, 0));
23 int vectorSize = PySequence_Fast_GET_SIZE(seq.object());
23 int vectorSize = PySequence_Fast_GET_SIZE(seq.object());
24 %out.reserve(vectorSize);
24 %out.reserve(vectorSize);
25 for (int idx = 0; idx &lt; vectorSize; ++idx ) {
25 for (int idx = 0; idx &lt; vectorSize; ++idx ) {
26 PyObject* pyItem = PySequence_Fast_GET_ITEM(seq.object(), idx);
26 PyObject* pyItem = PySequence_Fast_GET_ITEM(seq.object(), idx);
27 %OUTTYPE_0 cppItem = %CONVERTTOCPP[%OUTTYPE_0](pyItem);
27 %OUTTYPE_0 cppItem = %CONVERTTOCPP[%OUTTYPE_0](pyItem);
28 %out.push_back(cppItem);
28 %out.push_back(cppItem);
29 }
29 }
30 </add-conversion>
30 </add-conversion>
31 </target-to-native>
31 </target-to-native>
32 </conversion-rule>
32 </conversion-rule>
33 </container-type>
33 </container-type>
34 <object-type name="PyDataProvider" />
35 <object-type name="Product" />
34 <object-type name="MainWindow" />
36 <object-type name="MainWindow" />
35 <object-type name="SqpApplication">
37 <object-type name="SqpApplication">
36 <modify-function signature="SqpApplication(int&amp;,char**)" access="private"/>
38 <modify-function signature="SqpApplication(int&amp;,char**)" access="private"/>
37 </object-type>
39 </object-type>
38 <object-type name="PyDataProvider" />
40 <object-type name="Providers">
39 <function signature="SqpApplication_ctor()"/>
41 <modify-function signature="register_provider(PyDataProvider*)">
42 <modify-argument index="1">
43 <define-ownership owner="c++" />
44 </modify-argument>
45 </modify-function>
46 </object-type>
47 <function signature="SqpApplication_ctor()" return-type="SqpApplication*"/>
48 <add-function signature="SqpApplication_ctor(PySequence)" return-type="SqpApplication*">
49 <inject-code class="target">
50 static int argc;
51 static char **argv;
52 Shiboken::listToArgcArgv(%1, &amp;argc, &amp;argv, "PySideApp");
53 auto retval = new SqpApplication(argc,argv);
54 %PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](retval);
55 </inject-code>
56 </add-function>
40 <function signature="init_resources()"/>
57 <function signature="init_resources()"/>
41 <primitive-type name="NpArray" target-lang-api-name="PyObject">
58 <primitive-type name="NpArray" target-lang-api-name="PyObject">
42 <include file-name="numpy_wrappers.h" location="local"/>
59 <include file-name="numpy_wrappers.h" location="local"/>
43 <conversion-rule>
60 <conversion-rule>
44 <native-to-target>
61 <native-to-target>
45 return %in.py_object();
62 auto result = %in.py_object();
63 return result;
46 </native-to-target>
64 </native-to-target>
47 <target-to-native>
65 <target-to-native>
48 <add-conversion type="PyObject" check="NpArray::isNpArray(%in)">
66 <add-conversion type="PyObject" check="NpArray::isNpArray(%in)">
49 %out = %OUTTYPE(%in);
67 %out = %OUTTYPE(%in);
50 </add-conversion>
68 </add-conversion>
51 </target-to-native>
69 </target-to-native>
52 </conversion-rule>
70 </conversion-rule>
53 </primitive-type>
71 </primitive-type>
54 <function signature="load_plugins(const SqpApplication&amp;)"/>
72 <function signature="load_plugins(const SqpApplication&amp;)"/>
55 <object-type name="ScalarTimeSerie">
73 <object-type name="ScalarTimeSerie">
56 <add-function signature="ScalarTimeSerie(NpArray&amp;,NpArray&amp;)" return-type="ScalarTimeSerie">
74 <add-function signature="ScalarTimeSerie(NpArray&amp;,NpArray&amp;)" return-type="ScalarTimeSerie">
57 <inject-code class="target">
75 <inject-code class="target">
58 %BEGIN_ALLOW_THREADS
76 %BEGIN_ALLOW_THREADS
59 %0 = new ScalarTimeSerieWrapper();
77 %0 = new ScalarTimeSerieWrapper();
60 %0.set_data(%1.to_std_vect(),%2.to_std_vect());
78 %0.set_data(%1.to_std_vect(),%2.to_std_vect());
61 %END_ALLOW_THREADS
79 %END_ALLOW_THREADS
62 </inject-code>
80 </inject-code>
63 </add-function>
81 </add-function>
64 <add-function signature="size()" return-type="int" access="public" static="no">
82 <add-function signature="size()" return-type="int" access="public" static="no">
65 <inject-code class="target">
83 <inject-code class="target">
66 %RETURN_TYPE %0 = %CPPSELF.%FUNCTION_NAME();
84 %RETURN_TYPE %0 = %CPPSELF.%FUNCTION_NAME();
67 %PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0);
85 %PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0);
68 </inject-code>
86 </inject-code>
69 </add-function>
87 </add-function>
70 </object-type>
88 </object-type>
89 <function signature="test_PyDataProvider(PyDataProvider&amp;)"/>
71 <function signature="test_np_array(NpArray&amp;)"/>
90 <function signature="test_np_array(NpArray&amp;)"/>
72 </typesystem>
91 </typesystem>
@@ -1,26 +1,39
1 #include <fstream>
1 #include <fstream>
2 #include <iostream>
2 #include <iostream>
3 #define PY_SSIZE_T_CLEAN
3 #define PY_SSIZE_T_CLEAN
4 #define Py_DEBUG
4 #define Py_DEBUG
5 #include <Python.h>
5 #include <Python.h>
6
6
7 wchar_t** decode_argv(int argc, char** argv)
8 {
9 wchar_t** _argv = static_cast<wchar_t**>(PyMem_Malloc(sizeof(wchar_t*) * argc));
10 for (int i = 0; i < argc; i++)
11 {
12 wchar_t* arg = Py_DecodeLocale(argv[i], NULL);
13 _argv[i] = arg;
14 }
15 return _argv;
16 }
17
7 int main(int argc, char** argv)
18 int main(int argc, char** argv)
8 {
19 {
9 wchar_t* program = Py_DecodeLocale(argv[0], NULL);
20 wchar_t* program = Py_DecodeLocale(argv[0], NULL);
10 if (program == NULL)
21 if (program == NULL)
11 {
22 {
12 fprintf(stderr, "Fatal error: cannot decode argv[0]\n");
23 fprintf(stderr, "Fatal error: cannot decode argv[0]\n");
13 exit(1);
24 exit(1);
14 }
25 }
26
15 Py_SetProgramName(program); /* optional but recommended */
27 Py_SetProgramName(program); /* optional but recommended */
16 Py_Initialize();
28 Py_Initialize();
29 PySys_SetArgv(argc, decode_argv(argc, argv));
17 std::ifstream t(argv[1]);
30 std::ifstream t(argv[1]);
18 std::string str((std::istreambuf_iterator<char>(t)), std::istreambuf_iterator<char>());
31 std::string str((std::istreambuf_iterator<char>(t)), std::istreambuf_iterator<char>());
19 PyRun_SimpleString(str.data());
32 PyRun_SimpleString(str.data());
20 if (Py_FinalizeEx() < 0)
33 if (Py_FinalizeEx() < 0)
21 {
34 {
22 exit(120);
35 exit(120);
23 }
36 }
24 PyMem_RawFree(program);
37 PyMem_RawFree(program);
25 return 0;
38 return 0;
26 }
39 }
@@ -1,102 +1,103
1 # This Python file uses the following encoding: utf-8
1 # This Python file uses the following encoding: utf-8
2 import os
2 import os
3 print(os.getcwd())
3 print(os.getcwd())
4 import sys
4 import sys
5 from PySide2.QtWidgets import QApplication, QMainWindow, QDockWidget
5 from PySide2.QtWidgets import QApplication, QMainWindow, QDockWidget
6 from PySide2.QtCore import QSize, Qt
6 from PySide2.QtCore import QSize, Qt
7 from PySide2 import QtGui
7 from PySide2 import QtGui
8 import os
8 import os
9 sys.path.append(os.getcwd())
9 from SciQLopBindings import SqpApplication, MainWindow, init_resources, load_plugins, SqpApplication_ctor
10 from SciQLopBindings import SqpApplication, MainWindow, init_resources, load_plugins, SqpApplication_ctor
10 from qtconsole.rich_ipython_widget import RichJupyterWidget
11 from qtconsole.rich_ipython_widget import RichJupyterWidget
11 from qtconsole.inprocess import QtInProcessKernelManager
12 from qtconsole.inprocess import QtInProcessKernelManager
12
13
13
14
14 class IPythonWidget(RichJupyterWidget):
15 class IPythonWidget(RichJupyterWidget):
15 """Live IPython console widget.
16 """Live IPython console widget.
16
17
17 .. image:: img/IPythonWidget.png
18 .. image:: img/IPythonWidget.png
18
19
19 :param custom_banner: Custom welcome message to be printed at the top of
20 :param custom_banner: Custom welcome message to be printed at the top of
20 the console.
21 the console.
21 """
22 """
22
23
23 def __init__(self, parent=None, custom_banner=None, *args, **kwargs):
24 def __init__(self, parent=None, custom_banner=None, *args, **kwargs):
24 if parent is not None:
25 if parent is not None:
25 kwargs["parent"] = parent
26 kwargs["parent"] = parent
26 super(IPythonWidget, self).__init__(*args, **kwargs)
27 super(IPythonWidget, self).__init__(*args, **kwargs)
27 if custom_banner is not None:
28 if custom_banner is not None:
28 self.banner = custom_banner
29 self.banner = custom_banner
29 self.setWindowTitle(self.banner)
30 self.setWindowTitle(self.banner)
30 self.kernel_manager = kernel_manager = QtInProcessKernelManager()
31 self.kernel_manager = kernel_manager = QtInProcessKernelManager()
31 kernel_manager.start_kernel()
32 kernel_manager.start_kernel()
32 self.kernel_client = kernel_client = self._kernel_manager.client()
33 self.kernel_client = kernel_client = self._kernel_manager.client()
33 kernel_client.start_channels()
34 kernel_client.start_channels()
34
35
35 def stop():
36 def stop():
36 kernel_client.stop_channels()
37 kernel_client.stop_channels()
37 kernel_manager.shutdown_kernel()
38 kernel_manager.shutdown_kernel()
38 self.exit_requested.connect(stop)
39 self.exit_requested.connect(stop)
39
40
40 def sizeHint(self):
41 def sizeHint(self):
41 """Return a reasonable default size for usage in :class:`PlotWindow`"""
42 """Return a reasonable default size for usage in :class:`PlotWindow`"""
42 return QSize(500, 300)
43 return QSize(500, 300)
43
44
44 def pushVariables(self, variable_dict):
45 def pushVariables(self, variable_dict):
45 """ Given a dictionary containing name / value pairs, push those
46 """ Given a dictionary containing name / value pairs, push those
46 variables to the IPython console widget.
47 variables to the IPython console widget.
47
48
48 :param variable_dict: Dictionary of variables to be pushed to the
49 :param variable_dict: Dictionary of variables to be pushed to the
49 console's interactive namespace (```{variable_name: object, …}```)
50 console's interactive namespace (```{variable_name: object, …}```)
50 """
51 """
51 self.kernel_manager.kernel.shell.push(variable_dict)
52 self.kernel_manager.kernel.shell.push(variable_dict)
52
53
53
54
54 class IPythonDockWidget(QDockWidget):
55 class IPythonDockWidget(QDockWidget):
55 """Dock Widget including a :class:`IPythonWidget` inside
56 """Dock Widget including a :class:`IPythonWidget` inside
56 a vertical layout.
57 a vertical layout.
57
58
58 .. image:: img/IPythonDockWidget.png
59 .. image:: img/IPythonDockWidget.png
59
60
60 :param available_vars: Dictionary of variables to be pushed to the
61 :param available_vars: Dictionary of variables to be pushed to the
61 console's interactive namespace: ``{"variable_name": object, …}``
62 console's interactive namespace: ``{"variable_name": object, …}``
62 :param custom_banner: Custom welcome message to be printed at the top of
63 :param custom_banner: Custom welcome message to be printed at the top of
63 the console
64 the console
64 :param title: Dock widget title
65 :param title: Dock widget title
65 :param parent: Parent :class:`qt.QMainWindow` containing this
66 :param parent: Parent :class:`qt.QMainWindow` containing this
66 :class:`qt.QDockWidget`
67 :class:`qt.QDockWidget`
67 """
68 """
68 def __init__(self, parent=None, available_vars=None, custom_banner=None,
69 def __init__(self, parent=None, available_vars=None, custom_banner=None,
69 title="Console"):
70 title="Console"):
70 super(IPythonDockWidget, self).__init__(title, parent)
71 super(IPythonDockWidget, self).__init__(title, parent)
71
72
72 self.ipyconsole = IPythonWidget(custom_banner=custom_banner)
73 self.ipyconsole = IPythonWidget(custom_banner=custom_banner)
73
74
74 self.layout().setContentsMargins(0, 0, 0, 0)
75 self.layout().setContentsMargins(0, 0, 0, 0)
75 self.setWidget(self.ipyconsole)
76 self.setWidget(self.ipyconsole)
76
77
77 if available_vars is not None:
78 if available_vars is not None:
78 self.ipyconsole.pushVariables(available_vars)
79 self.ipyconsole.pushVariables(available_vars)
79 self.ipyconsole.pushVariables({"blah":self})
80 self.ipyconsole.pushVariables({"blah":self})
80
81
81 def showEvent(self, event):
82 def showEvent(self, event):
82 """Make sure this widget is raised when it is shown
83 """Make sure this widget is raised when it is shown
83 (when it is first created as a tab in PlotWindow or when it is shown
84 (when it is first created as a tab in PlotWindow or when it is shown
84 again after hiding).
85 again after hiding).
85 """
86 """
86 self.raise_()
87 self.raise_()
87
88
88 def print_process_id():
89 def print_process_id():
89 print ('Process ID is:', os.getpid())
90 print ('Process ID is:', os.getpid())
90
91
91
92
92 if __name__ == "__main__":
93 if __name__ == "__main__":
93 init_resources()
94 init_resources()
94 app = SqpApplication_ctor()
95 app = SqpApplication_ctor(sys.argv)
95 QtGui.qApp = app
96 QtGui.qApp = app
96 load_plugins(app)
97 load_plugins(app)
97 main_window = MainWindow()
98 main_window = MainWindow()
98 term = IPythonDockWidget(available_vars={"app":app, "main_window":main_window}, custom_banner="SciQLop IPython Console ")
99 term = IPythonDockWidget(available_vars={"app":app, "main_window":main_window}, custom_banner="SciQLop IPython Console ")
99 main_window.addDockWidget(Qt.BottomDockWidgetArea, term)
100 main_window.addDockWidget(Qt.BottomDockWidgetArea, term)
100 main_window.show()
101 main_window.show()
101 sys.exit(app.exec_())
102 sys.exit(app.exec_())
102
103
@@ -1,178 +1,179
1 #ifndef NUMPY_WRAPPERS_H
1 #ifndef NUMPY_WRAPPERS_H
2 #define NUMPY_WRAPPERS_H
2 #define NUMPY_WRAPPERS_H
3 #include <Data/ScalarTimeSerie.h>
3 #include <Data/ScalarTimeSerie.h>
4 #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
4 #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
5 #if defined(slots) && (defined(__GNUC__) || defined(_MSC_VER) || defined(__clang__))
5 #if defined(slots) && (defined(__GNUC__) || defined(_MSC_VER) || defined(__clang__))
6 #pragma push_macro("slots")
6 #pragma push_macro("slots")
7 #undef slots
7 #undef slots
8 extern "C"
8 extern "C"
9 {
9 {
10 /*
10 /*
11 * Python 2 uses the "register" keyword, which is deprecated in C++ 11
11 * Python 2 uses the "register" keyword, which is deprecated in C++ 11
12 * and forbidden in C++17.
12 * and forbidden in C++17.
13 */
13 */
14 #if defined(__clang__)
14 #if defined(__clang__)
15 #pragma clang diagnostic push
15 #pragma clang diagnostic push
16 #pragma clang diagnostic ignored "-Wdeprecated-register"
16 #pragma clang diagnostic ignored "-Wdeprecated-register"
17 #endif
17 #endif
18
18
19 #include <Python.h>
19 #include <Python.h>
20 #include <numpy/arrayobject.h>
20 #include <numpy/arrayobject.h>
21
21
22 #if defined(__clang__)
22 #if defined(__clang__)
23 #pragma clang diagnostic pop
23 #pragma clang diagnostic pop
24 #endif
24 #endif
25 }
25 }
26 #else
26 #else
27 #include <Python.h>
27 #include <Python.h>
28 #include <numpy/arrayobject.h>
28 #include <numpy/arrayobject.h>
29 #endif
29 #endif
30 #include <assert.h>
30 #include <assert.h>
31
31
32 #include <map>
33
32 inline int init_numpy()
34 inline int init_numpy()
33 {
35 {
34 import_array(); // PyError if not successful
36 import_array(); // PyError if not successful
35 return 0;
37 return 0;
36 }
38 }
37 const static int numpy_initialized = init_numpy();
39 const static int numpy_initialized = init_numpy();
38 template <typename dest_type = PyObject>
40 template <typename dest_type = PyObject>
39 struct PyObjectWrapper
41 struct PyObjectWrapper
40 {
42 {
41 private:
43 private:
42 PyObject* _py_obj;
44 PyObject* _py_obj = nullptr;
43
44 void inc_refcount()
45 void inc_refcount()
45 {
46 {
46 if (_py_obj)
47 Py_XINCREF(_py_obj);
47 Py_IncRef(_py_obj);
48 }
48 }
49 void dec_refcount()
49 void dec_refcount()
50 {
50 {
51 if (_py_obj)
51 Py_XDECREF(_py_obj);
52 Py_DecRef(_py_obj);
52 _py_obj = nullptr;
53 }
53 }
54
54
55 public:
55 public:
56 PyObjectWrapper() : _py_obj { nullptr } {}
56 PyObjectWrapper() : _py_obj { nullptr } {}
57 PyObjectWrapper(const PyObjectWrapper& other) : _py_obj { other._py_obj } { inc_refcount(); };
57 PyObjectWrapper(const PyObjectWrapper& other) : _py_obj { other._py_obj } { inc_refcount(); }
58 PyObjectWrapper(PyObjectWrapper&& other) : _py_obj { other._py_obj }
58 PyObjectWrapper(PyObjectWrapper&& other) : _py_obj { other._py_obj } { inc_refcount(); }
59 explicit PyObjectWrapper(PyObject* obj) : _py_obj { obj }
59 {
60 {
60 other._py_obj = nullptr;
61 inc_refcount();
61 }
62 }
62 PyObjectWrapper(PyObject* obj) : _py_obj { obj } { inc_refcount(); }
63 ~PyObjectWrapper() { dec_refcount(); }
63 ~PyObjectWrapper() { dec_refcount(); }
64 PyObjectWrapper& operator=(PyObjectWrapper&& other)
64 PyObjectWrapper& operator=(PyObjectWrapper&& other)
65 {
65 {
66 dec_refcount();
66 this->_py_obj = other._py_obj;
67 this->_py_obj = other._py_obj;
67 other._py_obj = nullptr;
68 inc_refcount();
68 return *this;
69 return *this;
69 }
70 }
70 PyObjectWrapper& operator=(const PyObjectWrapper& other)
71 PyObjectWrapper& operator=(const PyObjectWrapper& other)
71 {
72 {
72 dec_refcount();
73 dec_refcount();
73 this->_py_obj = other._py_obj;
74 this->_py_obj = other._py_obj;
74 inc_refcount();
75 inc_refcount();
75 return *this;
76 return *this;
76 }
77 }
77
78
78 PyObject* py_object() { return _py_obj; }
79 PyObject* py_object() { return _py_obj; }
79 inline dest_type* get() { return reinterpret_cast<dest_type*>(_py_obj); }
80 inline dest_type* get() { return reinterpret_cast<dest_type*>(_py_obj); }
80 inline bool is_null() { return _py_obj == nullptr; }
81 inline bool is_null() { return _py_obj == nullptr; }
81 };
82 };
82
83
83 struct NpArray
84 struct NpArray
84 {
85 {
85 private:
86 private:
86 PyObjectWrapper<PyArrayObject> _py_obj;
87 PyObjectWrapper<PyArrayObject> _py_obj;
87 NpArray(NpArray& other) = delete;
88 NpArray(NpArray& other) = delete;
88 NpArray(const NpArray& other) = delete;
89 NpArray(const NpArray& other) = delete;
89 NpArray(const NpArray&& other) = delete;
90 NpArray(const NpArray&& other) = delete;
90
91
91 public:
92 public:
92 static bool isNpArray(PyObject* obj)
93 static bool isNpArray(PyObject* obj)
93 {
94 {
94 return obj && PyArray_Check(reinterpret_cast<PyArrayObject*>(obj))
95 auto arr = reinterpret_cast<PyArrayObject*>(obj);
95 && PyArray_IS_C_CONTIGUOUS(reinterpret_cast<PyArrayObject*>(obj));
96 auto is_c_aray = obj && PyArray_Check(arr) && PyArray_ISCARRAY(arr);
97 return is_c_aray;
96 }
98 }
97 NpArray() : _py_obj { nullptr } {}
99 NpArray() : _py_obj { nullptr } {}
98 NpArray(NpArray&& other) : _py_obj { std::move(other._py_obj) } {}
100 NpArray(NpArray&& other) : _py_obj { other._py_obj } {}
99 explicit NpArray(PyObject* obj) : _py_obj { obj }
101 explicit NpArray(PyObject* obj) : _py_obj { obj }
100 {
102 {
101 std::cout << "NpArray ctor" << std::endl;
102 assert(isNpArray(obj));
103 assert(isNpArray(obj));
103 assert(PyArray_ISFLOAT(_py_obj.get()));
104 assert(PyArray_ISFLOAT(_py_obj.get()));
104 }
105 }
105
106
106 NpArray& operator=(const NpArray& other)
107 NpArray& operator=(const NpArray& other)
107 {
108 {
108 this->_py_obj = other._py_obj;
109 this->_py_obj = other._py_obj;
109 return *this;
110 return *this;
110 }
111 }
111
112
112 NpArray& operator=(NpArray&& other)
113 NpArray& operator=(NpArray&& other)
113 {
114 {
114 this->_py_obj = std::move(other._py_obj);
115 this->_py_obj = other._py_obj;
115 return *this;
116 return *this;
116 }
117 }
117
118
118 std::vector<std::size_t> shape()
119 std::vector<std::size_t> shape()
119 {
120 {
120 std::vector<std::size_t> shape;
121 std::vector<std::size_t> shape;
121 if (!_py_obj.is_null())
122 if (!_py_obj.is_null())
122 {
123 {
123 if (int ndim = PyArray_NDIM(_py_obj.get()); ndim > 0)
124 if (int ndim = PyArray_NDIM(_py_obj.get()); ndim > 0)
124 {
125 {
125 if (ndim < 10)
126 if (ndim < 10)
126 {
127 {
127 shape.resize(ndim);
128 shape.resize(ndim);
128 std::copy_n(PyArray_SHAPE(_py_obj.get()), ndim, std::begin(shape));
129 std::copy_n(PyArray_SHAPE(_py_obj.get()), ndim, std::begin(shape));
129 }
130 }
130 }
131 }
131 }
132 }
132 return shape;
133 return shape;
133 }
134 }
134
135
135 std::size_t flat_size()
136 std::size_t flat_size()
136 {
137 {
137 auto s = this->shape();
138 auto s = this->shape();
138 return std::accumulate(std::cbegin(s), std::cend(s), 0);
139 return std::accumulate(std::cbegin(s), std::cend(s), 0);
139 }
140 }
140
141
141 double data(std::size_t pos)
142 double data(std::size_t pos)
142 {
143 {
143 if (!_py_obj.is_null())
144 if (!_py_obj.is_null())
144 {
145 {
145 return reinterpret_cast<double*>(PyArray_DATA(_py_obj.get()))[pos];
146 return reinterpret_cast<double*>(PyArray_DATA(_py_obj.get()))[pos];
146 }
147 }
147 return nan("NAN");
148 return nan("NAN");
148 }
149 }
149
150
150 std::vector<double> to_std_vect()
151 std::vector<double> to_std_vect()
151 {
152 {
152 auto sz = flat_size();
153 auto sz = flat_size();
153 std::vector<double> v(sz);
154 std::vector<double> v(sz);
154 auto d_ptr = reinterpret_cast<double*>(PyArray_DATA(_py_obj.get()));
155 auto d_ptr = reinterpret_cast<double*>(PyArray_DATA(_py_obj.get()));
155 std::copy(d_ptr, d_ptr + sz, std::begin(v));
156 std::copy(d_ptr, d_ptr + sz, std::begin(v));
156 return v;
157 return v;
157 }
158 }
158
159
159 PyObject* py_object() { return _py_obj.py_object(); }
160 PyObject* py_object() { return _py_obj.py_object(); }
160 };
161 };
161
162
162 inline int test_np_array(NpArray& arr)
163 inline int test_np_array(NpArray& arr)
163 {
164 {
164 auto shape = arr.shape();
165 auto shape = arr.shape();
165 std::cout << "len(shape)=" << shape.size() << std::endl;
166 std::cout << "len(shape)=" << shape.size() << std::endl;
166 std::for_each(std::cbegin(shape), std::cend(shape), [](auto sz) {
167 std::for_each(std::cbegin(shape), std::cend(shape), [](auto sz) {
167 static int i = 0;
168 static int i = 0;
168 std::cout << "shape[" << i++ << "]=" << sz << std::endl;
169 std::cout << "shape[" << i++ << "]=" << sz << std::endl;
169 });
170 });
170 auto flatsize = std::accumulate(std::cbegin(shape), std::cend(shape), 0);
171 auto flatsize = std::accumulate(std::cbegin(shape), std::cend(shape), 0);
171 for (auto i = 0; i < flatsize; i++)
172 for (auto i = 0; i < flatsize; i++)
172 {
173 {
173 std::cout << "data[" << i << "]=" << arr.data(i) << std::endl;
174 std::cout << "data[" << i << "]=" << arr.data(i) << std::endl;
174 }
175 }
175 return 1;
176 return 1;
176 }
177 }
177
178
178 #endif //#ifndef NUMPY_WRAPPERS_H
179 #endif //#ifndef NUMPY_WRAPPERS_H
@@ -1,37 +1,37
1 #ifndef TOOLBAR_H
1 #ifndef TOOLBAR_H
2 #define TOOLBAR_H
2 #define TOOLBAR_H
3
3
4 #include <Data/DateTimeRange.h>
4 #include <Data/DateTimeRange.h>
5 #include <QAction>
5 #include <QAction>
6 #include <QActionGroup>
6 #include <QActionGroup>
7 #include <QObject>
7 #include <QObject>
8 #include <QToolBar>
8 #include <QToolBar>
9 #include <QWidget>
9 #include <QWidget>
10 #include <TimeWidget/TimeWidget.h>
10 #include <TimeWidget/TimeWidget.h>
11
11
12 // @TODO remove this, shouldn't need to include SqpApplication to get PlotsInteractionMode
12 // TODO remove this, shouldn't need to include SqpApplication to get PlotsInteractionMode
13 #include <SqpApplication.h>
13 #include <SqpApplication.h>
14
14
15 class ToolBar : public QToolBar
15 class ToolBar : public QToolBar
16 {
16 {
17 Q_OBJECT
17 Q_OBJECT
18 public:
18 public:
19 explicit ToolBar(QWidget* parent = nullptr);
19 explicit ToolBar(QWidget* parent = nullptr);
20
20
21 QAction* timeRange;
21 QAction* timeRange;
22 QAction* pointerMode;
22 QAction* pointerMode;
23 QAction* zoomMode;
23 QAction* zoomMode;
24 QAction* organizationMode;
24 QAction* organizationMode;
25 QAction* zonesMode;
25 QAction* zonesMode;
26 QAction* cursorsActn;
26 QAction* cursorsActn;
27 QAction* cataloguesActn;
27 QAction* cataloguesActn;
28
28
29 TimeWidgetAction* timeWidget;
29 TimeWidgetAction* timeWidget;
30 signals:
30 signals:
31 void setPlotsInteractionMode(SqpApplication::PlotsInteractionMode);
31 void setPlotsInteractionMode(SqpApplication::PlotsInteractionMode);
32 void setPlotsCursorMode(SqpApplication::PlotsCursorMode);
32 void setPlotsCursorMode(SqpApplication::PlotsCursorMode);
33 void timeUpdated(DateTimeRange time);
33 void timeUpdated(DateTimeRange time);
34 void showCataloguesBrowser();
34 void showCataloguesBrowser();
35 };
35 };
36
36
37 #endif // TOOLBAR_H
37 #endif // TOOLBAR_H
@@ -1,281 +1,208
1 /*------------------------------------------------------------------------------
1 /*------------------------------------------------------------------------------
2 -- This file is a part of the SciQLop Software
2 -- This file is a part of the SciQLop Software
3 -- Copyright (C) 2017, Plasma Physics Laboratory - CNRS
3 -- Copyright (C) 2017, Plasma Physics Laboratory - CNRS
4 --
4 --
5 -- This program is free software; you can redistribute it and/or modify
5 -- This program is free software; you can redistribute it and/or modify
6 -- it under the terms of the GNU General Public License as published by
6 -- it under the terms of the GNU General Public License as published by
7 -- the Free Software Foundation; either version 2 of the License, or
7 -- the Free Software Foundation; either version 2 of the License, or
8 -- (at your option) any later version.
8 -- (at your option) any later version.
9 --
9 --
10 -- This program is distributed in the hope that it will be useful,
10 -- This program is distributed in the hope that it will be useful,
11 -- but WITHOUT ANY WARRANTY; without even the implied warranty of
11 -- but WITHOUT ANY WARRANTY; without even the implied warranty of
12 -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 -- GNU General Public License for more details.
13 -- GNU General Public License for more details.
14 --
14 --
15 -- You should have received a copy of the GNU General Public License
15 -- You should have received a copy of the GNU General Public License
16 -- along with this program; if not, write to the Free Software
16 -- along with this program; if not, write to the Free Software
17 -- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 -- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 -------------------------------------------------------------------------------*/
18 -------------------------------------------------------------------------------*/
19 /*-- Author : Alexis Jeandet
19 /*-- Author : Alexis Jeandet
20 -- Mail : alexis.jeandet@member.fsf.org
20 -- Mail : alexis.jeandet@member.fsf.org
21 ----------------------------------------------------------------------------*/
21 ----------------------------------------------------------------------------*/
22 #include "MainWindow.h"
22 #include "MainWindow.h"
23 #include "ui_MainWindow.h"
23 #include "ui_MainWindow.h"
24
24
25 #include <Catalogue/CatalogueController.h>
25 #include <Catalogue/CatalogueController.h>
26 #include <Catalogue2/browser.h>
26 #include <Catalogue2/browser.h>
27 #include <DataSource/DataSourceController.h>
27 #include <DataSource/DataSourceController.h>
28 #include <DataSource/DataSourceItem.h>
28 #include <DataSource/DataSourceWidget.h>
29 #include <DataSource/DataSourceWidget.h>
29 #include <Settings/SqpSettingsDialog.h>
30 #include <Settings/SqpSettingsDialog.h>
30 #include <Settings/SqpSettingsGeneralWidget.h>
31 #include <Settings/SqpSettingsGeneralWidget.h>
31 #include <SidePane/SqpSidePane.h>
32 #include <SidePane/SqpSidePane.h>
32 #include <SqpApplication.h>
33 #include <SqpApplication.h>
33 #include <Time/TimeController.h>
34 #include <Time/TimeController.h>
34 #include <TimeWidget/TimeWidget.h>
35 #include <TimeWidget/TimeWidget.h>
35
36
36 #include "toolbar.h"
37 #include "toolbar.h"
37
38
38 #include <QAction>
39 #include <QAction>
39 #include <QCloseEvent>
40 #include <QCloseEvent>
40 #include <QDate>
41 #include <QDate>
41 #include <QDir>
42 #include <QDir>
42 #include <QFileDialog>
43 #include <QFileDialog>
43 #include <QMessageBox>
44 #include <QMessageBox>
44 #include <QToolBar>
45 #include <QToolBar>
45 #include <QToolButton>
46 #include <QToolButton>
46 #include <memory.h>
47 #include <memory.h>
47
48
48
49
49 Q_LOGGING_CATEGORY(LOG_MainWindow, "MainWindow")
50 Q_LOGGING_CATEGORY(LOG_MainWindow, "MainWindow")
50
51
51 namespace
52 {
53 const auto LEFTMAININSPECTORWIDGETSPLITTERINDEX = 0;
54 const auto LEFTINSPECTORSIDEPANESPLITTERINDEX = 1;
55 const auto VIEWPLITTERINDEX = 2;
56 const auto RIGHTINSPECTORSIDEPANESPLITTERINDEX = 3;
57 const auto RIGHTMAININSPECTORWIDGETSPLITTERINDEX = 4;
58 }
59
60 class MainWindow::MainWindowPrivate
52 class MainWindow::MainWindowPrivate
61 {
53 {
62 public:
54 public:
63 explicit MainWindowPrivate(MainWindow* mainWindow)
55 explicit MainWindowPrivate(MainWindow* mainWindow)
64 : m_LastOpenLeftInspectorSize {}
56 : m_GeneralSettingsWidget { new SqpSettingsGeneralWidget { mainWindow } }
65 , m_LastOpenRightInspectorSize {}
66 , m_GeneralSettingsWidget { new SqpSettingsGeneralWidget { mainWindow } }
67 , m_SettingsDialog { new SqpSettingsDialog { mainWindow } }
57 , m_SettingsDialog { new SqpSettingsDialog { mainWindow } }
68 , m_CatalogExplorer { new CataloguesBrowser { mainWindow } }
58 , m_CatalogExplorer { new CataloguesBrowser { mainWindow } }
69 {
59 {
70 }
60 }
71
61
72 QSize m_LastOpenLeftInspectorSize;
73 QSize m_LastOpenRightInspectorSize;
74 /// General settings widget. MainWindow has the ownership
62 /// General settings widget. MainWindow has the ownership
75 SqpSettingsGeneralWidget* m_GeneralSettingsWidget;
63 SqpSettingsGeneralWidget* m_GeneralSettingsWidget;
76 /// Settings dialog. MainWindow has the ownership
64 /// Settings dialog. MainWindow has the ownership
77 SqpSettingsDialog* m_SettingsDialog;
65 SqpSettingsDialog* m_SettingsDialog;
78 /// Catalogue dialog. MainWindow has the ownership
66 /// Catalogue dialog. MainWindow has the ownership
79 CataloguesBrowser* m_CatalogExplorer;
67 CataloguesBrowser* m_CatalogExplorer;
80
68
81 bool checkDataToSave(QWidget* parentWidget);
69 bool checkDataToSave(QWidget* parentWidget);
82 };
70 };
83
71
84 MainWindow::MainWindow(QWidget* parent)
72 MainWindow::MainWindow(QWidget* parent)
85 : QMainWindow { parent }
73 : QMainWindow { parent }
86 , m_Ui { new Ui::MainWindow }
74 , m_Ui { new Ui::MainWindow }
87 , impl { spimpl::make_unique_impl<MainWindowPrivate>(this) }
75 , impl { spimpl::make_unique_impl<MainWindowPrivate>(this) }
88 {
76 {
89 m_Ui->setupUi(this);
77 m_Ui->setupUi(this);
90 setWindowTitle(QString("SciQLop v%1").arg(SCIQLOP_VERSION));
78 setWindowTitle(QString("SciQLop v%1").arg(SCIQLOP_VERSION));
91
79
92 m_Ui->splitter->setCollapsible(LEFTINSPECTORSIDEPANESPLITTERINDEX, false);
93 m_Ui->splitter->setCollapsible(RIGHTINSPECTORSIDEPANESPLITTERINDEX, false);
94
95 // impl->m_CatalogExplorer->setVisualizationWidget(m_Ui->view);
96
97
98 auto spacerLeftTop = new QWidget {};
99 spacerLeftTop->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
100
101 auto spacerLeftBottom = new QWidget {};
102 spacerLeftBottom->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
103
104
105 auto spacerRightTop = new QWidget {};
106 spacerRightTop->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
107
108 auto spacerRightBottom = new QWidget {};
109 spacerRightBottom->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
110
111
112 auto openInspector = [this](bool checked, bool right, auto action) {
113 action->setIcon(
114 QIcon { (checked ^ right) ? ":/icones/next.png" : ":/icones/previous.png" });
115
116 auto& lastInspectorSize
117 = right ? impl->m_LastOpenRightInspectorSize : impl->m_LastOpenLeftInspectorSize;
118
119 auto nextInspectorSize = right ? m_Ui->rightMainInspectorWidget->size()
120 : m_Ui->leftMainInspectorWidget->size();
121
122 // Update of the last opened geometry
123 if (checked)
124 {
125 lastInspectorSize = nextInspectorSize;
126 }
127
128 auto startSize = lastInspectorSize;
129 auto endSize = startSize;
130 endSize.setWidth(0);
131
132 auto splitterInspectorIndex
133 = right ? RIGHTMAININSPECTORWIDGETSPLITTERINDEX : LEFTMAININSPECTORWIDGETSPLITTERINDEX;
134
135 auto currentSizes = m_Ui->splitter->sizes();
136 if (checked)
137 {
138 // adjust sizes individually here, e.g.
139 currentSizes[splitterInspectorIndex] -= lastInspectorSize.width();
140 currentSizes[VIEWPLITTERINDEX] += lastInspectorSize.width();
141 m_Ui->splitter->setSizes(currentSizes);
142 }
143 else
144 {
145 // adjust sizes individually here, e.g.
146 currentSizes[splitterInspectorIndex] += lastInspectorSize.width();
147 currentSizes[VIEWPLITTERINDEX] -= lastInspectorSize.width();
148 m_Ui->splitter->setSizes(currentSizes);
149 }
150 };
151
152
153 // //////////////// //
80 // //////////////// //
154 // Menu and Toolbar //
81 // Menu and Toolbar //
155 // //////////////// //
82 // //////////////// //
156 this->menuBar()->addAction(tr("File"));
83 this->menuBar()->addAction(tr("File"));
157 auto toolsMenu = this->menuBar()->addMenu(tr("Tools"));
84 auto toolsMenu = this->menuBar()->addMenu(tr("Tools"));
158 toolsMenu->addAction(tr("Settings..."), [this]() {
85 toolsMenu->addAction(tr("Settings..."), [this]() {
159 // Loads settings
86 // Loads settings
160 impl->m_SettingsDialog->loadSettings();
87 impl->m_SettingsDialog->loadSettings();
161
88
162 // Open settings dialog and save settings if the dialog is accepted
89 // Open settings dialog and save settings if the dialog is accepted
163 if (impl->m_SettingsDialog->exec() == QDialog::Accepted)
90 if (impl->m_SettingsDialog->exec() == QDialog::Accepted)
164 {
91 {
165 impl->m_SettingsDialog->saveSettings();
92 impl->m_SettingsDialog->saveSettings();
166 }
93 }
167 });
94 });
168 auto mainToolBar = new ToolBar(this);
95 auto mainToolBar = new ToolBar(this);
169 this->addToolBar(mainToolBar);
96 this->addToolBar(mainToolBar);
170 connect(mainToolBar, &ToolBar::setPlotsInteractionMode, sqpApp,
97 connect(mainToolBar, &ToolBar::setPlotsInteractionMode, sqpApp,
171 &SqpApplication::setPlotsInteractionMode);
98 &SqpApplication::setPlotsInteractionMode);
172 connect(mainToolBar, &ToolBar::setPlotsCursorMode, sqpApp, &SqpApplication::setPlotsCursorMode);
99 connect(mainToolBar, &ToolBar::setPlotsCursorMode, sqpApp, &SqpApplication::setPlotsCursorMode);
173 connect(mainToolBar, &ToolBar::showCataloguesBrowser,
100 connect(mainToolBar, &ToolBar::showCataloguesBrowser,
174 [this]() { impl->m_CatalogExplorer->show(); });
101 [this]() { impl->m_CatalogExplorer->show(); });
175
102
176 // //////// //
103 // //////// //
177 // Settings //
104 // Settings //
178 // //////// //
105 // //////// //
179
106
180 // Registers "general settings" widget to the settings dialog
107 // Registers "general settings" widget to the settings dialog
181 impl->m_SettingsDialog->registerWidget(
108 impl->m_SettingsDialog->registerWidget(
182 QStringLiteral("General"), impl->m_GeneralSettingsWidget);
109 QStringLiteral("General"), impl->m_GeneralSettingsWidget);
183
110
184 // /////////// //
111 // /////////// //
185 // Connections //
112 // Connections //
186 // /////////// //
113 // /////////// //
187
114
188 // Widgets / controllers connections
115 // Widgets / controllers connections
189
116
190 // DataSource
117 // DataSource
191 connect(&sqpApp->dataSourceController(), SIGNAL(dataSourceItemSet(DataSourceItem*)),
118 connect(&sqpApp->dataSourceController(), &DataSourceController::dataSourceItemSet,
192 m_Ui->dataSourceWidget, SLOT(addDataSource(DataSourceItem*)));
119 m_Ui->dataSourceWidget, &DataSourceWidget::addDataSource);
193
120
194 // Time
121 // Time
195 // connect(timeWidget, SIGNAL(timeUpdated(DateTimeRange)), &sqpApp->timeController(),
122 // connect(timeWidget, SIGNAL(timeUpdated(DateTimeRange)), &sqpApp->timeController(),
196 // SLOT(onTimeToUpdate(DateTimeRange)));
123 // SLOT(onTimeToUpdate(DateTimeRange)));
197 connect(mainToolBar, &ToolBar::timeUpdated, &sqpApp->timeController(),
124 connect(mainToolBar, &ToolBar::timeUpdated, &sqpApp->timeController(),
198 &TimeController::setDateTimeRange);
125 &TimeController::setDateTimeRange);
199
126
200 // Widgets / widgets connections
127 // Widgets / widgets connections
201
128
202 // For the following connections, we use DirectConnection to allow each widget that can
129 // For the following connections, we use DirectConnection to allow each widget that can
203 // potentially attach a menu to the variable's menu to do so before this menu is displayed.
130 // potentially attach a menu to the variable's menu to do so before this menu is displayed.
204 // The order of connections is also important, since it determines the order in which each
131 // The order of connections is also important, since it determines the order in which each
205 // widget will attach its menu
132 // widget will attach its menu
206 connect(m_Ui->variableInspectorWidget,
133 connect(m_Ui->variableInspectorWidget,
207 SIGNAL(tableMenuAboutToBeDisplayed(QMenu*, const QVector<std::shared_ptr<Variable>>&)),
134 SIGNAL(tableMenuAboutToBeDisplayed(QMenu*, const QVector<std::shared_ptr<Variable>>&)),
208 m_Ui->view, SLOT(attachVariableMenu(QMenu*, const QVector<std::shared_ptr<Variable>>&)),
135 m_Ui->view, SLOT(attachVariableMenu(QMenu*, const QVector<std::shared_ptr<Variable>>&)),
209 Qt::DirectConnection);
136 Qt::DirectConnection);
210 }
137 }
211
138
212 MainWindow::~MainWindow() {}
139 MainWindow::~MainWindow() {}
213
140
214 void MainWindow::changeEvent(QEvent* e)
141 void MainWindow::changeEvent(QEvent* e)
215 {
142 {
216 QMainWindow::changeEvent(e);
143 QMainWindow::changeEvent(e);
217 switch (e->type())
144 switch (e->type())
218 {
145 {
219 case QEvent::LanguageChange:
146 case QEvent::LanguageChange:
220 m_Ui->retranslateUi(this);
147 m_Ui->retranslateUi(this);
221 break;
148 break;
222 default:
149 default:
223 break;
150 break;
224 }
151 }
225 }
152 }
226
153
227 void MainWindow::closeEvent(QCloseEvent* event)
154 void MainWindow::closeEvent(QCloseEvent* event)
228 {
155 {
229 if (!impl->checkDataToSave(this))
156 if (!impl->checkDataToSave(this))
230 {
157 {
231 event->ignore();
158 event->ignore();
232 }
159 }
233 else
160 else
234 {
161 {
235 event->accept();
162 event->accept();
236 }
163 }
237 }
164 }
238
165
239 void MainWindow::keyPressEvent(QKeyEvent* event)
166 void MainWindow::keyPressEvent(QKeyEvent* event)
240 {
167 {
241 switch (event->key())
168 switch (event->key())
242 {
169 {
243 case Qt::Key_F11:
170 case Qt::Key_F11:
244 if (this->isFullScreen())
171 if (this->isFullScreen())
245 {
172 {
246 this->showNormal();
173 this->showNormal();
247 }
174 }
248 else
175 else
249 {
176 {
250 this->showFullScreen();
177 this->showFullScreen();
251 }
178 }
252 break;
179 break;
253 default:
180 default:
254 break;
181 break;
255 }
182 }
256 }
183 }
257
184
258 bool MainWindow::MainWindowPrivate::checkDataToSave(QWidget* parentWidget)
185 bool MainWindow::MainWindowPrivate::checkDataToSave(QWidget* parentWidget)
259 {
186 {
260 // auto hasChanges = sqpApp->catalogueController().hasChanges();
187 // auto hasChanges = sqpApp->catalogueController().hasChanges();
261 // if (hasChanges)
188 // if (hasChanges)
262 // {
189 // {
263 // // There are some unsaved changes
190 // // There are some unsaved changes
264 // switch (QMessageBox::question(parentWidget, tr("Save changes"),
191 // switch (QMessageBox::question(parentWidget, tr("Save changes"),
265 // tr("The catalogue controller has unsaved changes.\nDo you want to save them ?"),
192 // tr("The catalogue controller has unsaved changes.\nDo you want to save them ?"),
266 // QMessageBox::SaveAll | QMessageBox::Discard | QMessageBox::Cancel,
193 // QMessageBox::SaveAll | QMessageBox::Discard | QMessageBox::Cancel,
267 // QMessageBox::SaveAll))
194 // QMessageBox::SaveAll))
268 // {
195 // {
269 // case QMessageBox::SaveAll:
196 // case QMessageBox::SaveAll:
270 // sqpApp->catalogueController().saveAll();
197 // sqpApp->catalogueController().saveAll();
271 // break;
198 // break;
272 // case QMessageBox::Discard:
199 // case QMessageBox::Discard:
273 // break;
200 // break;
274 // case QMessageBox::Cancel:
201 // case QMessageBox::Cancel:
275 // default:
202 // default:
276 // return false;
203 // return false;
277 // }
204 // }
278 // }
205 // }
279
206
280 return true;
207 return true;
281 }
208 }
@@ -1,177 +1,110
1 <?xml version="1.0" encoding="UTF-8"?>
1 <?xml version="1.0" encoding="UTF-8"?>
2 <ui version="4.0">
2 <ui version="4.0">
3 <class>MainWindow</class>
3 <class>MainWindow</class>
4 <widget class="QMainWindow" name="MainWindow">
4 <widget class="QMainWindow" name="MainWindow">
5 <property name="geometry">
5 <property name="geometry">
6 <rect>
6 <rect>
7 <x>0</x>
7 <x>0</x>
8 <y>0</y>
8 <y>0</y>
9 <width>800</width>
9 <width>800</width>
10 <height>600</height>
10 <height>600</height>
11 </rect>
11 </rect>
12 </property>
12 </property>
13 <property name="windowTitle">
13 <property name="windowTitle">
14 <string>SciQlop</string>
14 <string>SciQlop</string>
15 </property>
15 </property>
16 <property name="dockNestingEnabled">
16 <property name="dockNestingEnabled">
17 <bool>true</bool>
17 <bool>true</bool>
18 </property>
18 </property>
19 <widget class="QWidget" name="centralWidget">
19 <widget class="VisualizationWidget" name="view">
20 <property name="enabled">
20 <property name="enabled">
21 <bool>true</bool>
21 <bool>true</bool>
22 </property>
22 </property>
23 <property name="sizePolicy">
23 <property name="sizePolicy">
24 <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
24 <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
25 <horstretch>0</horstretch>
25 <horstretch>0</horstretch>
26 <verstretch>0</verstretch>
26 <verstretch>0</verstretch>
27 </sizepolicy>
27 </sizepolicy>
28 </property>
28 </property>
29 <property name="maximumSize">
29 <property name="maximumSize">
30 <size>
30 <size>
31 <width>16777215</width>
31 <width>16777215</width>
32 <height>16777215</height>
32 <height>16777215</height>
33 </size>
33 </size>
34 </property>
34 </property>
35 <layout class="QHBoxLayout" name="horizontalLayout">
36 <property name="spacing">
37 <number>0</number>
38 </property>
39 <property name="leftMargin">
40 <number>0</number>
41 </property>
42 <property name="topMargin">
43 <number>0</number>
44 </property>
45 <property name="rightMargin">
46 <number>0</number>
47 </property>
48 <property name="bottomMargin">
49 <number>0</number>
50 </property>
51 <item>
52 <widget class="QSplitter" name="splitter">
53 <property name="orientation">
54 <enum>Qt::Horizontal</enum>
55 </property>
56 <widget class="QWidget" name="leftMainInspectorWidget" native="true">
57 <layout class="QVBoxLayout" name="verticalLayout">
58 <property name="spacing">
59 <number>0</number>
60 </property>
61 <property name="leftMargin">
62 <number>0</number>
63 </property>
64 <property name="topMargin">
65 <number>0</number>
66 </property>
67 <property name="rightMargin">
68 <number>0</number>
69 </property>
70 <property name="bottomMargin">
71 <number>0</number>
72 </property>
73 <item>
74 <widget class="DataSourceWidget" name="dataSourceWidget" native="true"/>
75 </item>
76 <item>
77 <widget class="QWidget" name="dateTimeWidget" native="true"/>
78 </item>
79 <item>
80 <widget class="VariableInspectorWidget" name="variableInspectorWidget" native="true"/>
81 </item>
82 </layout>
83 </widget>
84 <widget class="VisualizationWidget" name="view" native="true">
85 <property name="sizePolicy">
86 <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
87 <horstretch>0</horstretch>
88 <verstretch>0</verstretch>
89 </sizepolicy>
90 </property>
91 </widget>
92 <widget class="QWidget" name="rightMainInspectorWidget" native="true">
93 <layout class="QVBoxLayout" name="verticalLayout_3">
94 <property name="spacing">
95 <number>0</number>
96 </property>
97 <property name="leftMargin">
98 <number>0</number>
99 </property>
100 <property name="topMargin">
101 <number>0</number>
102 </property>
103 <property name="rightMargin">
104 <number>0</number>
105 </property>
106 <property name="bottomMargin">
107 <number>0</number>
108 </property>
109 <item>
110 <widget class="QWidget" name="commonPropertyInspectorWidget" native="true"/>
111 </item>
112 <item>
113 <widget class="QTreeWidget" name="catalogWidget">
114 <column>
115 <property name="text">
116 <string notr="true">Name</string>
117 </property>
118 </column>
119 </widget>
120 </item>
121 </layout>
122 </widget>
123 </widget>
124 </item>
125 </layout>
126 </widget>
35 </widget>
127 <widget class="QMenuBar" name="menuBar">
36 <widget class="QMenuBar" name="menuBar">
128 <property name="geometry">
37 <property name="geometry">
129 <rect>
38 <rect>
130 <x>0</x>
39 <x>0</x>
131 <y>0</y>
40 <y>0</y>
132 <width>800</width>
41 <width>800</width>
133 <height>27</height>
42 <height>24</height>
134 </rect>
43 </rect>
135 </property>
44 </property>
136 <property name="sizePolicy">
45 <property name="sizePolicy">
137 <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
46 <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
138 <horstretch>0</horstretch>
47 <horstretch>0</horstretch>
139 <verstretch>0</verstretch>
48 <verstretch>0</verstretch>
140 </sizepolicy>
49 </sizepolicy>
141 </property>
50 </property>
142 <property name="minimumSize">
51 <property name="minimumSize">
143 <size>
52 <size>
144 <width>0</width>
53 <width>0</width>
145 <height>0</height>
54 <height>0</height>
146 </size>
55 </size>
147 </property>
56 </property>
148 <property name="nativeMenuBar">
57 <property name="nativeMenuBar">
149 <bool>true</bool>
58 <bool>true</bool>
150 </property>
59 </property>
151 </widget>
60 </widget>
152 <widget class="QStatusBar" name="statusBar"/>
61 <widget class="QStatusBar" name="statusBar"/>
62 <widget class="QDockWidget" name="dockWidget">
63 <property name="features">
64 <set>QDockWidget::DockWidgetMovable</set>
65 </property>
66 <property name="windowTitle">
67 <string>Products</string>
68 </property>
69 <attribute name="dockWidgetArea">
70 <number>1</number>
71 </attribute>
72 <widget class="DataSourceWidget" name="dataSourceWidget"/>
73 </widget>
74 <widget class="QDockWidget" name="dockWidget_2">
75 <property name="features">
76 <set>QDockWidget::DockWidgetMovable</set>
77 </property>
78 <property name="windowTitle">
79 <string>Variables</string>
80 </property>
81 <attribute name="dockWidgetArea">
82 <number>1</number>
83 </attribute>
84 <widget class="VariableInspectorWidget" name="variableInspectorWidget"/>
85 </widget>
153 </widget>
86 </widget>
154 <layoutdefault spacing="6" margin="11"/>
87 <layoutdefault spacing="6" margin="11"/>
155 <customwidgets>
88 <customwidgets>
156 <customwidget>
89 <customwidget>
157 <class>VisualizationWidget</class>
90 <class>VisualizationWidget</class>
158 <extends>QWidget</extends>
91 <extends>QWidget</extends>
159 <header location="global">Visualization/VisualizationWidget.h</header>
92 <header location="global">Visualization/VisualizationWidget.h</header>
160 <container>1</container>
93 <container>1</container>
161 </customwidget>
94 </customwidget>
162 <customwidget>
95 <customwidget>
163 <class>DataSourceWidget</class>
96 <class>DataSourceWidget</class>
164 <extends>QWidget</extends>
97 <extends>QWidget</extends>
165 <header location="global">DataSource/DataSourceWidget.h</header>
98 <header location="global">DataSource/DataSourceWidget.h</header>
166 <container>1</container>
99 <container>1</container>
167 </customwidget>
100 </customwidget>
168 <customwidget>
101 <customwidget>
169 <class>VariableInspectorWidget</class>
102 <class>VariableInspectorWidget</class>
170 <extends>QWidget</extends>
103 <extends>QWidget</extends>
171 <header location="global">Variable/VariableInspectorWidget.h</header>
104 <header location="global">Variable/VariableInspectorWidget.h</header>
172 <container>1</container>
105 <container>1</container>
173 </customwidget>
106 </customwidget>
174 </customwidgets>
107 </customwidgets>
175 <resources/>
108 <resources/>
176 <connections/>
109 <connections/>
177 </ui>
110 </ui>
@@ -1,1 +1,1
1 Subproject commit c4e273750e74336d1d125c02989c922f2032e489
1 Subproject commit 9ff5f48e3d71ce2d3a8b2156f778f3f9be3ea389
@@ -1,225 +1,225
1 #include <DataSource/DataSourceItem.h>
1 #include <DataSource/DataSourceItem.h>
2 #include <DataSource/DataSourceItemAction.h>
2 #include <DataSource/DataSourceItemAction.h>
3 #include <DataSource/DataSourceTreeWidgetItem.h>
3 #include <DataSource/DataSourceTreeWidgetItem.h>
4
4
5 #include <QAction>
5 #include <QAction>
6
6
7 Q_LOGGING_CATEGORY(LOG_DataSourceTreeWidgetItem, "DataSourceTreeWidgetItem")
7 Q_LOGGING_CATEGORY(LOG_DataSourceTreeWidgetItem, "DataSourceTreeWidgetItem")
8
8
9 namespace {
9 namespace {
10
10
11 // Column indexes
11 // Column indexes
12 const auto NAME_COLUMN = 0;
12 const auto NAME_COLUMN = 0;
13
13
14 /**
14 /**
15 * Generates the full name of an item.
15 * Generates the full name of an item.
16 *
16 *
17 * The full name of an item is its name possibly suffixed by the name of its plugin, in case there
17 * The full name of an item is its name possibly suffixed by the name of its plugin, in case there
18 * are items of the same name in its relatives
18 * are items of the same name in its relatives
19 * @param item the item for which to generate the complete name
19 * @param item the item for which to generate the complete name
20 * @return the complete name of the item
20 * @return the complete name of the item
21 */
21 */
22 QString completeName(const DataSourceItem &item)
22 QString completeName(const DataSourceItem &item)
23 {
23 {
24 auto name = item.name();
24 auto name = item.name();
25
25
26 if (item.type() == DataSourceItemType::NODE) {
26 if (item.type() == DataSourceItemType::NODE) {
27 return name;
27 return name;
28 }
28 }
29
29
30 auto parentItem = item.parentItem();
30 auto parentItem = item.parentItem();
31 if (!parentItem) {
31 if (!parentItem) {
32 return name;
32 return name;
33 }
33 }
34
34
35 // Finds in item's relatives items that have the same name
35 // Finds in item's relatives items that have the same name
36 bool foundSameName = false;
36 bool foundSameName = false;
37 for (auto i = 0, count = parentItem->childCount(); i < count && !foundSameName; ++i) {
37 for (auto i = 0, count = parentItem->childCount(); i < count && !foundSameName; ++i) {
38 auto child = parentItem->child(i);
38 auto child = parentItem->child(i);
39 foundSameName = child != &item
39 foundSameName = child != &item
40 && QString::compare(child->name(), item.name(), Qt::CaseInsensitive) == 0;
40 && QString::compare(child->name(), item.name(), Qt::CaseInsensitive) == 0;
41 }
41 }
42
42
43 // If the name of the item is not unique, it is completed by the plugin suffix
43 // If the name of the item is not unique, it is completed by the plugin suffix
44 return foundSameName
44 return foundSameName
45 ? QString{"%1 (%2)"}.arg(name, item.data(DataSourceItem::PLUGIN_DATA_KEY).toString())
45 ? QString{"%1 (%2)"}.arg(name, item.data(DataSourceItem::PLUGIN_DATA_KEY).toString())
46 : name;
46 : name;
47 }
47 }
48
48
49 QIcon itemIcon(const DataSourceItem *dataSource)
49 QIcon itemIcon(const DataSourceItem *dataSource)
50 {
50 {
51 if (dataSource) {
51 if (dataSource) {
52 auto dataSourceType = dataSource->type();
52 auto dataSourceType = dataSource->type();
53 switch (dataSourceType) {
53 switch (dataSourceType) {
54 case DataSourceItemType::NODE: {
54 case DataSourceItemType::NODE: {
55 return dataSource->isRoot() ? QIcon{":/icones/dataSourceRoot.png"}
55 return dataSource->isRoot() ? QIcon{":/icones/dataSourceRoot.png"}
56 : QIcon{":/icones/dataSourceNode.png"};
56 : QIcon{":/icones/dataSourceNode.png"};
57 }
57 }
58 case DataSourceItemType::PRODUCT:
58 case DataSourceItemType::PRODUCT:
59 return QIcon{":/icones/dataSourceProduct.png"};
59 return QIcon{":/icones/dataSourceProduct.png"};
60 case DataSourceItemType::COMPONENT:
60 case DataSourceItemType::COMPONENT:
61 return QIcon{":/icones/dataSourceComponent.png"};
61 return QIcon{":/icones/dataSourceComponent.png"};
62 default:
62 default:
63 // No action
63 // No action
64 break;
64 break;
65 }
65 }
66
66
67 qCWarning(LOG_DataSourceTreeWidgetItem())
67 qCWarning(LOG_DataSourceTreeWidgetItem())
68 << QObject::tr("Can't set data source icon : unknown data source type");
68 << QObject::tr("Can't set data source icon : unknown data source type");
69 }
69 }
70 else {
70 else {
71 qCCritical(LOG_DataSourceTreeWidgetItem())
71 qCCritical(LOG_DataSourceTreeWidgetItem())
72 << QObject::tr("Can't set data source icon : the data source is null");
72 << QObject::tr("Can't set data source icon : the data source is null");
73 }
73 }
74
74
75 // Default cases
75 // Default cases
76 return QIcon{};
76 return QIcon{};
77 }
77 }
78
78
79 /// @return the tooltip text for a variant. The text depends on whether the data is a simple variant
79 /// @return the tooltip text for a variant. The text depends on whether the data is a simple variant
80 /// or a list of variants
80 /// or a list of variants
81 QString tooltipValue(const QVariant &variant) noexcept
81 QString tooltipValue(const QVariant &variant) noexcept
82 {
82 {
83 // If the variant is a list of variants, the text of the tooltip is of the form: {val1, val2,
83 // If the variant is a list of variants, the text of the tooltip is of the form: {val1, val2,
84 // ...}
84 // ...}
85 if (variant.canConvert<QVariantList>()) {
85 if (variant.canConvert<QVariantList>()) {
86 auto valueString = QStringLiteral("{");
86 auto valueString = QStringLiteral("{");
87
87
88 auto variantList = variant.value<QVariantList>();
88 auto variantList = variant.value<QVariantList>();
89 for (auto it = variantList.cbegin(), end = variantList.cend(); it != end; ++it) {
89 for (auto it = variantList.cbegin(), end = variantList.cend(); it != end; ++it) {
90 valueString.append(it->toString());
90 valueString.append(it->toString());
91
91
92 if (std::distance(it, end) != 1) {
92 if (std::distance(it, end) != 1) {
93 valueString.append(", ");
93 valueString.append(", ");
94 }
94 }
95 }
95 }
96
96
97 valueString.append(QStringLiteral("}"));
97 valueString.append(QStringLiteral("}"));
98
98
99 return valueString;
99 return valueString;
100 }
100 }
101 else {
101 else {
102 return variant.toString();
102 return variant.toString();
103 }
103 }
104 }
104 }
105
105
106 QString itemTooltip(const DataSourceItem *dataSource) noexcept
106 QString itemTooltip(const DataSourceItem *dataSource) noexcept
107 {
107 {
108 // The tooltip displays all item's data
108 // The tooltip displays all item's data
109 if (dataSource) {
109 if (dataSource) {
110 auto result = QString{};
110 auto result = QString{};
111
111
112 const auto &data = dataSource->data();
112 const auto &data = dataSource->data();
113 for (auto it = data.cbegin(), end = data.cend(); it != end; ++it) {
113 for (auto it = data.cbegin(), end = data.cend(); it != end; ++it) {
114 result.append(QString{"<b>%1:</b> %2<br/>"}.arg(it.key(), tooltipValue(it.value())));
114 result.append(QString{"<b>%1:</b> %2<br/>"}.arg(it.key(), tooltipValue(it.value())));
115 }
115 }
116
116
117 return result;
117 return result;
118 }
118 }
119 else {
119 else {
120 qCCritical(LOG_DataSourceTreeWidgetItem())
120 qCCritical(LOG_DataSourceTreeWidgetItem())
121 << QObject::tr("Can't set data source tooltip : the data source is null");
121 << QObject::tr("Can't set data source tooltip : the data source is null");
122
122
123 return QString{};
123 return QString{};
124 }
124 }
125 }
125 }
126
126
127 } // namespace
127 } // namespace
128
128
129 struct DataSourceTreeWidgetItem::DataSourceTreeWidgetItemPrivate {
129 struct DataSourceTreeWidgetItem::DataSourceTreeWidgetItemPrivate {
130 explicit DataSourceTreeWidgetItemPrivate(const DataSourceItem *data)
130 explicit DataSourceTreeWidgetItemPrivate(const DataSourceItem *data)
131 : m_Data{data}, m_Name{completeName(*m_Data)}
131 : m_Data{data}, m_Name{completeName(*m_Data)}
132 {
132 {
133 }
133 }
134
134
135 /// Model used to retrieve data source information
135 /// Model used to retrieve data source information
136 const DataSourceItem *m_Data;
136 const DataSourceItem *m_Data;
137 /// Name displayed
137 /// Name displayed
138 QString m_Name;
138 QString m_Name;
139 /// Actions associated to the item. The parent of the item (QTreeWidget) takes the ownership of
139 /// Actions associated to the item. The parent of the item (QTreeWidget) takes the ownership of
140 /// the actions
140 /// the actions
141 QList<QAction *> m_Actions;
141 QList<QAction *> m_Actions; //TODO check if no memory leak here
142 };
142 };
143
143
144 DataSourceTreeWidgetItem::DataSourceTreeWidgetItem(const DataSourceItem *data, int type)
144 DataSourceTreeWidgetItem::DataSourceTreeWidgetItem(const DataSourceItem *data, int type)
145 : DataSourceTreeWidgetItem{nullptr, data, type}
145 : DataSourceTreeWidgetItem{nullptr, data, type}
146 {
146 {
147 }
147 }
148
148
149 DataSourceTreeWidgetItem::DataSourceTreeWidgetItem(QTreeWidget *parent, const DataSourceItem *data,
149 DataSourceTreeWidgetItem::DataSourceTreeWidgetItem(QTreeWidget *parent, const DataSourceItem *data,
150 int type)
150 int type)
151 : QTreeWidgetItem{parent, type},
151 : QTreeWidgetItem{parent, type},
152 impl{spimpl::make_unique_impl<DataSourceTreeWidgetItemPrivate>(data)}
152 impl{spimpl::make_unique_impl<DataSourceTreeWidgetItemPrivate>(data)}
153 {
153 {
154 // Sets the icon and the tooltip depending on the data source
154 // Sets the icon and the tooltip depending on the data source
155 setIcon(0, itemIcon(impl->m_Data));
155 setIcon(0, itemIcon(impl->m_Data));
156 setToolTip(0, itemTooltip(impl->m_Data));
156 setToolTip(0, itemTooltip(impl->m_Data));
157
157
158 // Generates tree actions based on the item actions
158 // Generates tree actions based on the item actions
159 auto createTreeAction = [this, &parent](const auto &itemAction) {
159 auto createTreeAction = [this, &parent](const auto &itemAction) {
160 auto treeAction = new QAction{itemAction->name(), parent};
160 auto treeAction = new QAction{itemAction->name(), parent};
161
161
162 // Executes item action when tree action is triggered
162 // Executes item action when tree action is triggered
163 QObject::connect(treeAction, &QAction::triggered, itemAction,
163 QObject::connect(treeAction, &QAction::triggered, itemAction,
164 &DataSourceItemAction::execute);
164 &DataSourceItemAction::execute);
165
165
166 return treeAction;
166 return treeAction;
167 };
167 };
168
168
169 auto itemActions = impl->m_Data->actions();
169 auto itemActions = impl->m_Data->actions();
170 std::transform(std::cbegin(itemActions), std::cend(itemActions),
170 std::transform(std::cbegin(itemActions), std::cend(itemActions),
171 std::back_inserter(impl->m_Actions), createTreeAction);
171 std::back_inserter(impl->m_Actions), createTreeAction);
172
172
173 // Sets the flags of the items
173 // Sets the flags of the items
174 auto flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
174 auto flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
175 if (data->type() == DataSourceItemType::COMPONENT
175 if (data->type() == DataSourceItemType::COMPONENT
176 || data->type() == DataSourceItemType::PRODUCT) {
176 || data->type() == DataSourceItemType::PRODUCT) {
177 flags |= Qt::ItemIsDragEnabled;
177 flags |= Qt::ItemIsDragEnabled;
178 }
178 }
179
179
180 setFlags(flags);
180 setFlags(flags);
181 }
181 }
182
182
183 const DataSourceItem *DataSourceTreeWidgetItem::data() const
183 const DataSourceItem *DataSourceTreeWidgetItem::data() const
184 {
184 {
185 return impl->m_Data;
185 return impl->m_Data;
186 }
186 }
187
187
188 QVariant DataSourceTreeWidgetItem::data(int column, int role) const
188 QVariant DataSourceTreeWidgetItem::data(int column, int role) const
189 {
189 {
190 if (role == Qt::DisplayRole) {
190 if (role == Qt::DisplayRole) {
191 if (impl->m_Data) {
191 if (impl->m_Data) {
192 switch (column) {
192 switch (column) {
193 case NAME_COLUMN:
193 case NAME_COLUMN:
194 return impl->m_Name;
194 return impl->m_Name;
195 default:
195 default:
196 // No action
196 // No action
197 break;
197 break;
198 }
198 }
199
199
200 qCWarning(LOG_DataSourceTreeWidgetItem())
200 qCWarning(LOG_DataSourceTreeWidgetItem())
201 << QObject::tr("Can't get data (unknown column %1)").arg(column);
201 << QObject::tr("Can't get data (unknown column %1)").arg(column);
202 }
202 }
203 else {
203 else {
204 qCCritical(LOG_DataSourceTreeWidgetItem()) << QObject::tr("Can't get data (null item)");
204 qCCritical(LOG_DataSourceTreeWidgetItem()) << QObject::tr("Can't get data (null item)");
205 }
205 }
206
206
207 return QVariant{};
207 return QVariant{};
208 }
208 }
209 else {
209 else {
210 return QTreeWidgetItem::data(column, role);
210 return QTreeWidgetItem::data(column, role);
211 }
211 }
212 }
212 }
213
213
214 void DataSourceTreeWidgetItem::setData(int column, int role, const QVariant &value)
214 void DataSourceTreeWidgetItem::setData(int column, int role, const QVariant &value)
215 {
215 {
216 // Data can't be changed by edition
216 // Data can't be changed by edition
217 if (role != Qt::EditRole) {
217 if (role != Qt::EditRole) {
218 QTreeWidgetItem::setData(column, role, value);
218 QTreeWidgetItem::setData(column, role, value);
219 }
219 }
220 }
220 }
221
221
222 QList<QAction *> DataSourceTreeWidgetItem::actions() const noexcept
222 QList<QAction *> DataSourceTreeWidgetItem::actions() const noexcept
223 {
223 {
224 return impl->m_Actions;
224 return impl->m_Actions;
225 }
225 }
@@ -1,123 +1,124
1 #include <DataSource/DataSourceWidget.h>
1 #include <DataSource/DataSourceWidget.h>
2
2
3 #include <ui_DataSourceWidget.h>
3 #include <ui_DataSourceWidget.h>
4
4
5 #include <DataSource/DataSourceItem.h>
5 #include <DataSource/DataSourceItem.h>
6 #include <DataSource/DataSourceTreeWidgetHelper.h>
6 #include <DataSource/DataSourceTreeWidgetHelper.h>
7 #include <DataSource/DataSourceTreeWidgetItem.h>
7 #include <DataSource/DataSourceTreeWidgetItem.h>
8
8
9 #include <QMenu>
9 #include <QMenu>
10
10
11 namespace {
11 namespace
12 {
12
13
13 /// Number of columns displayed in the tree
14 /// Number of columns displayed in the tree
14 const auto TREE_NB_COLUMNS = 1;
15 const auto TREE_NB_COLUMNS = 1;
15
16
16 /// Header labels for the tree
17 /// Header labels for the tree
17 const auto TREE_HEADER_LABELS = QStringList{QObject::tr("Name")};
18 const auto TREE_HEADER_LABELS = QStringList { QObject::tr("Name") };
18
19
19 /**
20 /**
20 * Creates the item associated to a data source
21 * Creates the item associated to a data source
21 * @param dataSource the data source for which to create the item
22 * @param dataSource the data source for which to create the item
22 * @return the new item
23 * @return the new item
23 */
24 */
24 DataSourceTreeWidgetItem *createTreeWidgetItem(DataSourceItem *dataSource)
25 DataSourceTreeWidgetItem* createTreeWidgetItem(DataSourceItem* dataSource)
25 {
26 {
26 // Creates item for the data source
27 // Creates item for the data source
27 auto item = new DataSourceTreeWidgetItem{dataSource};
28 auto item = new DataSourceTreeWidgetItem { dataSource };
28
29 // Generates items for the children of the data source
29 // Generates items for the children of the data source
30 for (auto i = 0; i < dataSource->childCount(); ++i) {
30 std::for_each(dataSource->cbegin(), dataSource->cend(),
31 item->addChild(createTreeWidgetItem(dataSource->child(i)));
31 [&item](const std::unique_ptr<DataSourceItem>& child) {
32 }
32 item->addChild(createTreeWidgetItem(child.get()));
33
33 });
34 return item;
34 return item;
35 }
35 }
36
36
37 } // namespace
37 } // namespace
38
38
39 DataSourceWidget::DataSourceWidget(QWidget *parent)
39 DataSourceWidget::DataSourceWidget(QWidget* parent)
40 : QWidget{parent},
40 : QWidget { parent }
41 ui{new Ui::DataSourceWidget},
41 , ui { new Ui::DataSourceWidget }
42 m_Root{
42 , m_Root { std::make_unique<DataSourceItem>(
43 std::make_unique<DataSourceItem>(DataSourceItemType::NODE, QStringLiteral("Sources"))}
43 DataSourceItemType::NODE, QStringLiteral("Sources")) }
44 {
44 {
45 ui->setupUi(this);
45 ui->setupUi(this);
46
46
47 // Set tree properties
47 // Set tree properties
48 ui->treeWidget->setColumnCount(TREE_NB_COLUMNS);
48 ui->treeWidget->setColumnCount(TREE_NB_COLUMNS);
49 ui->treeWidget->setHeaderLabels(TREE_HEADER_LABELS);
49 ui->treeWidget->setHeaderLabels(TREE_HEADER_LABELS);
50 ui->treeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
50 ui->treeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
51
51
52 // Connection to show a menu when right clicking on the tree
52 // Connection to show a menu when right clicking on the tree
53 connect(ui->treeWidget, &QTreeWidget::customContextMenuRequested, this,
53 connect(ui->treeWidget, &QTreeWidget::customContextMenuRequested, this,
54 &DataSourceWidget::onTreeMenuRequested);
54 &DataSourceWidget::onTreeMenuRequested);
55
55
56 // Connection to filter tree
56 // Connection to filter tree
57 connect(ui->filterLineEdit, &QLineEdit::textChanged, this, &DataSourceWidget::filterChanged);
57 connect(ui->filterLineEdit, &QLineEdit::textChanged, this, &DataSourceWidget::filterChanged);
58
58
59 // First init
59 // First init
60 updateTreeWidget();
60 updateTreeWidget();
61 }
61 }
62
62
63 DataSourceWidget::~DataSourceWidget() noexcept
63 DataSourceWidget::~DataSourceWidget() noexcept
64 {
64 {
65 delete ui;
65 delete ui;
66 }
66 }
67
67
68 void DataSourceWidget::addDataSource(DataSourceItem *dataSource) noexcept
68 void DataSourceWidget::addDataSource(DataSourceItem* dataSource) noexcept
69 {
69 {
70 // Merges the data source (without taking its root)
70 // Merges the data source (without taking its root)
71 if (dataSource) {
71 if (dataSource)
72 for (auto i = 0, count = dataSource->childCount(); i < count; ++i) {
72 {
73 m_Root->merge(*dataSource->child(i));
73 std::for_each(std::cbegin(*dataSource), std::cend(*dataSource),
74 }
74 [this](const auto& child) { this->m_Root->merge(*child.get()); });
75
76 updateTreeWidget();
75 updateTreeWidget();
77 }
76 }
78 }
77 }
79
78
80 void DataSourceWidget::updateTreeWidget() noexcept
79 void DataSourceWidget::updateTreeWidget() noexcept
81 {
80 {
82 ui->treeWidget->clear();
81 ui->treeWidget->clear();
83
82
84 auto rootItem = createTreeWidgetItem(m_Root.get());
83 auto rootItem = createTreeWidgetItem(m_Root.get());
85 ui->treeWidget->addTopLevelItem(rootItem);
84 ui->treeWidget->addTopLevelItem(rootItem);
86 rootItem->setExpanded(true);
85 rootItem->setExpanded(true);
87
86
88 // Sorts tree
87 // Sorts tree
89 ui->treeWidget->setSortingEnabled(true);
88 ui->treeWidget->setSortingEnabled(true);
90 ui->treeWidget->sortByColumn(0, Qt::AscendingOrder);
89 ui->treeWidget->sortByColumn(0, Qt::AscendingOrder);
91 }
90 }
92
91
93 void DataSourceWidget::filterChanged(const QString &text) noexcept
92 void DataSourceWidget::filterChanged(const QString& text) noexcept
94 {
93 {
95 auto validateItem = [&text](const DataSourceTreeWidgetItem &item) {
94 auto validateItem = [&text](const DataSourceTreeWidgetItem& item) {
96 auto regExp = QRegExp{text, Qt::CaseInsensitive, QRegExp::Wildcard};
95 auto regExp = QRegExp { text, Qt::CaseInsensitive, QRegExp::Wildcard };
97
96
98 // An item is valid if any of its metadata validates the text filter
97 // An item is valid if any of its metadata validates the text filter
99 auto itemMetadata = item.data()->data();
98 auto itemMetadata = item.data()->data();
100 auto itemMetadataEnd = itemMetadata.cend();
99 auto itemMetadataEnd = itemMetadata.cend();
101 auto acceptFilter
100 auto acceptFilter
102 = [&regExp](const auto &variant) { return variant.toString().contains(regExp); };
101 = [&regExp](const auto& variant) { return variant.toString().contains(regExp); };
103
102
104 return std::find_if(itemMetadata.cbegin(), itemMetadataEnd, acceptFilter)
103 return std::find_if(itemMetadata.cbegin(), itemMetadataEnd, acceptFilter)
105 != itemMetadataEnd;
104 != itemMetadataEnd;
106 };
105 };
107
106
108 // Applies filter on tree widget
107 // Applies filter on tree widget
109 DataSourceTreeWidgetHelper::filter(*ui->treeWidget, validateItem);
108 DataSourceTreeWidgetHelper::filter(*ui->treeWidget, validateItem);
110 }
109 }
111
110
112 void DataSourceWidget::onTreeMenuRequested(const QPoint &pos) noexcept
111 void DataSourceWidget::onTreeMenuRequested(const QPoint& pos) noexcept
113 {
112 {
114 // Retrieves the selected item in the tree, and build the menu from its actions
113 // Retrieves the selected item in the tree, and build the menu from its actions
115 if (auto selectedItem = dynamic_cast<DataSourceTreeWidgetItem *>(ui->treeWidget->itemAt(pos))) {
114 if (auto selectedItem = dynamic_cast<DataSourceTreeWidgetItem*>(ui->treeWidget->itemAt(pos)))
116 QMenu treeMenu{};
115 {
116 QMenu treeMenu {};
117 treeMenu.addActions(selectedItem->actions());
117 treeMenu.addActions(selectedItem->actions());
118
118
119 if (!treeMenu.isEmpty()) {
119 if (!treeMenu.isEmpty())
120 {
120 treeMenu.exec(QCursor::pos());
121 treeMenu.exec(QCursor::pos());
121 }
122 }
122 }
123 }
123 }
124 }
@@ -1,1577 +1,1577
1 #include "Visualization/VisualizationGraphWidget.h"
1 #include "Visualization/VisualizationGraphWidget.h"
2 #include "Visualization/IVisualizationWidgetVisitor.h"
2 #include "Visualization/IVisualizationWidgetVisitor.h"
3 #include "Visualization/VisualizationCursorItem.h"
3 #include "Visualization/VisualizationCursorItem.h"
4 #include "Visualization/VisualizationDefs.h"
4 #include "Visualization/VisualizationDefs.h"
5 #include "Visualization/VisualizationGraphHelper.h"
5 #include "Visualization/VisualizationGraphHelper.h"
6 #include "Visualization/VisualizationGraphRenderingDelegate.h"
6 #include "Visualization/VisualizationGraphRenderingDelegate.h"
7 #include "Visualization/VisualizationMultiZoneSelectionDialog.h"
7 #include "Visualization/VisualizationMultiZoneSelectionDialog.h"
8 #include "Visualization/VisualizationSelectionZoneItem.h"
8 #include "Visualization/VisualizationSelectionZoneItem.h"
9 #include "Visualization/VisualizationSelectionZoneManager.h"
9 #include "Visualization/VisualizationSelectionZoneManager.h"
10 #include "Visualization/VisualizationWidget.h"
10 #include "Visualization/VisualizationWidget.h"
11 #include "Visualization/VisualizationZoneWidget.h"
11 #include "Visualization/VisualizationZoneWidget.h"
12 #include "ui_VisualizationGraphWidget.h"
12 #include "ui_VisualizationGraphWidget.h"
13
13
14 #include <Actions/ActionsGuiController.h>
14 #include <Actions/ActionsGuiController.h>
15 #include <Actions/FilteringAction.h>
15 #include <Actions/FilteringAction.h>
16 #include <Common/MimeTypesDef.h>
16 #include <Common/MimeTypesDef.h>
17 #include <Common/containers.h>
17 #include <Common/containers.h>
18 #include <Data/DateTimeRangeHelper.h>
18 #include <Data/DateTimeRangeHelper.h>
19 #include <DragAndDrop/DragDropGuiController.h>
19 #include <DragAndDrop/DragDropGuiController.h>
20 #include <Settings/SqpSettingsDefs.h>
20 #include <Settings/SqpSettingsDefs.h>
21 #include <SqpApplication.h>
21 #include <SqpApplication.h>
22 #include <Time/TimeController.h>
22 #include <Time/TimeController.h>
23 #include <Variable/Variable2.h>
23 #include <Variable/Variable2.h>
24 #include <Variable/VariableController2.h>
24 #include <Variable/VariableController2.h>
25
25
26 #include <unordered_map>
26 #include <unordered_map>
27
27
28 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
28 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
29
29
30 namespace
30 namespace
31 {
31 {
32
32
33 /// Key pressed to enable drag&drop in all modes
33 /// Key pressed to enable drag&drop in all modes
34 const auto DRAG_DROP_MODIFIER = Qt::AltModifier;
34 const auto DRAG_DROP_MODIFIER = Qt::AltModifier;
35
35
36 /// Key pressed to enable zoom on horizontal axis
36 /// Key pressed to enable zoom on horizontal axis
37 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier;
37 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier;
38
38
39 /// Key pressed to enable zoom on vertical axis
39 /// Key pressed to enable zoom on vertical axis
40 const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier;
40 const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier;
41
41
42 /// Speed of a step of a wheel event for a pan, in percentage of the axis range
42 /// Speed of a step of a wheel event for a pan, in percentage of the axis range
43 const auto PAN_SPEED = 5;
43 const auto PAN_SPEED = 5;
44
44
45 /// Key pressed to enable a calibration pan
45 /// Key pressed to enable a calibration pan
46 const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier;
46 const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier;
47
47
48 /// Key pressed to enable multi selection of selection zones
48 /// Key pressed to enable multi selection of selection zones
49 const auto MULTI_ZONE_SELECTION_MODIFIER = Qt::ControlModifier;
49 const auto MULTI_ZONE_SELECTION_MODIFIER = Qt::ControlModifier;
50
50
51 /// Minimum size for the zoom box, in percentage of the axis range
51 /// Minimum size for the zoom box, in percentage of the axis range
52 const auto ZOOM_BOX_MIN_SIZE = 0.8;
52 const auto ZOOM_BOX_MIN_SIZE = 0.8;
53
53
54 /// Format of the dates appearing in the label of a cursor
54 /// Format of the dates appearing in the label of a cursor
55 const auto CURSOR_LABELS_DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd\nhh:mm:ss:zzz");
55 const auto CURSOR_LABELS_DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd\nhh:mm:ss:zzz");
56
56
57 } // namespace
57 } // namespace
58
58
59 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate
59 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate
60 {
60 {
61
61
62 explicit VisualizationGraphWidgetPrivate(const QString& name)
62 explicit VisualizationGraphWidgetPrivate(const QString& name)
63 : m_Name { name }
63 : m_Name { name }
64 , m_Flags { GraphFlag::EnableAll }
64 , m_Flags { GraphFlag::EnableAll }
65 , m_IsCalibration { false }
65 , m_IsCalibration { false }
66 , m_RenderingDelegate { nullptr }
66 , m_RenderingDelegate { nullptr }
67 {
67 {
68 m_plot = new QCustomPlot();
68 m_plot = new QCustomPlot();
69 // Necessary for all platform since Qt::AA_EnableHighDpiScaling is enable.
69 // Necessary for all platform since Qt::AA_EnableHighDpiScaling is enable.
70 m_plot->setPlottingHint(QCP::phFastPolylines, true);
70 m_plot->setPlottingHint(QCP::phFastPolylines, true);
71 }
71 }
72
72
73 void updateData(
73 void updateData(
74 PlottablesMap& plottables, std::shared_ptr<Variable2> variable, const DateTimeRange& range)
74 PlottablesMap& plottables, std::shared_ptr<Variable2> variable, const DateTimeRange& range)
75 {
75 {
76 VisualizationGraphHelper::updateData(plottables, variable, range);
76 VisualizationGraphHelper::updateData(plottables, variable, range);
77
77
78 // Prevents that data has changed to update rendering
78 // Prevents that data has changed to update rendering
79 m_RenderingDelegate->onPlotUpdated();
79 m_RenderingDelegate->onPlotUpdated();
80 }
80 }
81
81
82 QString m_Name;
82 QString m_Name;
83 // 1 variable -> n qcpplot
83 // 1 variable -> n qcpplot
84 std::map<std::shared_ptr<Variable2>, PlottablesMap> m_VariableToPlotMultiMap;
84 std::map<std::shared_ptr<Variable2>, PlottablesMap> m_VariableToPlotMultiMap;
85 GraphFlags m_Flags;
85 GraphFlags m_Flags;
86 bool m_IsCalibration;
86 bool m_IsCalibration;
87 QCustomPlot* m_plot;
87 QCustomPlot* m_plot;
88 QPoint m_lastMousePos;
88 QPoint m_lastMousePos;
89 QCPRange m_lastXRange;
89 QCPRange m_lastXRange;
90 QCPRange m_lastYRange;
90 QCPRange m_lastYRange;
91 /// Delegate used to attach rendering features to the plot
91 /// Delegate used to attach rendering features to the plot
92 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
92 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
93
93
94 QCPItemRect* m_DrawingZoomRect = nullptr;
94 QCPItemRect* m_DrawingZoomRect = nullptr;
95 QStack<QPair<QCPRange, QCPRange>> m_ZoomStack;
95 QStack<QPair<QCPRange, QCPRange>> m_ZoomStack;
96
96
97 std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr;
97 std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr;
98 std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr;
98 std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr;
99
99
100 VisualizationSelectionZoneItem* m_DrawingZone = nullptr;
100 VisualizationSelectionZoneItem* m_DrawingZone = nullptr;
101 VisualizationSelectionZoneItem* m_HoveredZone = nullptr;
101 VisualizationSelectionZoneItem* m_HoveredZone = nullptr;
102 QVector<VisualizationSelectionZoneItem*> m_SelectionZones;
102 QVector<VisualizationSelectionZoneItem*> m_SelectionZones;
103
103
104 bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even
104 bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even
105
105
106 bool m_VariableAutoRangeOnInit = true;
106 bool m_VariableAutoRangeOnInit = true;
107
107
108 inline void enterPlotDrag(const QPoint& position)
108 inline void enterPlotDrag(const QPoint& position)
109 {
109 {
110 m_lastMousePos = m_plot->mapFromParent(position);
110 m_lastMousePos = m_plot->mapFromParent(position);
111 m_lastXRange = m_plot->xAxis->range();
111 m_lastXRange = m_plot->xAxis->range();
112 m_lastYRange = m_plot->yAxis->range();
112 m_lastYRange = m_plot->yAxis->range();
113 }
113 }
114
114
115 inline bool isDrawingZoomRect() { return m_DrawingZoomRect != nullptr; }
115 inline bool isDrawingZoomRect() { return m_DrawingZoomRect != nullptr; }
116 void updateZoomRect(const QPoint& newPos)
116 void updateZoomRect(const QPoint& newPos)
117 {
117 {
118 QPointF pos { m_plot->xAxis->pixelToCoord(newPos.x()),
118 QPointF pos { m_plot->xAxis->pixelToCoord(newPos.x()),
119 m_plot->yAxis->pixelToCoord(newPos.y()) };
119 m_plot->yAxis->pixelToCoord(newPos.y()) };
120 m_DrawingZoomRect->bottomRight->setCoords(pos);
120 m_DrawingZoomRect->bottomRight->setCoords(pos);
121 m_plot->replot(QCustomPlot::rpQueuedReplot);
121 m_plot->replot(QCustomPlot::rpQueuedReplot);
122 }
122 }
123
123
124 void applyZoomRect()
124 void applyZoomRect()
125 {
125 {
126 auto axisX = m_plot->axisRect()->axis(QCPAxis::atBottom);
126 auto axisX = m_plot->axisRect()->axis(QCPAxis::atBottom);
127 auto axisY = m_plot->axisRect()->axis(QCPAxis::atLeft);
127 auto axisY = m_plot->axisRect()->axis(QCPAxis::atLeft);
128
128
129 auto newAxisXRange = QCPRange { m_DrawingZoomRect->topLeft->coords().x(),
129 auto newAxisXRange = QCPRange { m_DrawingZoomRect->topLeft->coords().x(),
130 m_DrawingZoomRect->bottomRight->coords().x() };
130 m_DrawingZoomRect->bottomRight->coords().x() };
131
131
132 auto newAxisYRange = QCPRange { m_DrawingZoomRect->topLeft->coords().y(),
132 auto newAxisYRange = QCPRange { m_DrawingZoomRect->topLeft->coords().y(),
133 m_DrawingZoomRect->bottomRight->coords().y() };
133 m_DrawingZoomRect->bottomRight->coords().y() };
134
134
135 removeDrawingRect();
135 removeDrawingRect();
136
136
137 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
137 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
138 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0))
138 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0))
139 {
139 {
140 m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
140 m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
141 axisX->setRange(newAxisXRange);
141 axisX->setRange(newAxisXRange);
142 axisY->setRange(newAxisYRange);
142 axisY->setRange(newAxisYRange);
143
143
144 m_plot->replot(QCustomPlot::rpQueuedReplot);
144 m_plot->replot(QCustomPlot::rpQueuedReplot);
145 }
145 }
146 }
146 }
147
147
148 inline bool isDrawingZoneRect() { return m_DrawingZone != nullptr; }
148 inline bool isDrawingZoneRect() { return m_DrawingZone != nullptr; }
149 void updateZoneRect(const QPoint& newPos)
149 void updateZoneRect(const QPoint& newPos)
150 {
150 {
151 m_DrawingZone->setEnd(m_plot->xAxis->pixelToCoord(newPos.x()));
151 m_DrawingZone->setEnd(m_plot->xAxis->pixelToCoord(newPos.x()));
152 m_plot->replot(QCustomPlot::rpQueuedReplot);
152 m_plot->replot(QCustomPlot::rpQueuedReplot);
153 }
153 }
154
154
155 void startDrawingRect(const QPoint& pos)
155 void startDrawingRect(const QPoint& pos)
156 {
156 {
157 removeDrawingRect();
157 removeDrawingRect();
158
158
159 auto axisPos = posToAxisPos(pos);
159 auto axisPos = posToAxisPos(pos);
160
160
161 m_DrawingZoomRect = new QCPItemRect { m_plot };
161 m_DrawingZoomRect = new QCPItemRect { m_plot };
162 QPen p;
162 QPen p;
163 p.setWidth(2);
163 p.setWidth(2);
164 m_DrawingZoomRect->setPen(p);
164 m_DrawingZoomRect->setPen(p);
165
165
166 m_DrawingZoomRect->topLeft->setCoords(axisPos);
166 m_DrawingZoomRect->topLeft->setCoords(axisPos);
167 m_DrawingZoomRect->bottomRight->setCoords(axisPos);
167 m_DrawingZoomRect->bottomRight->setCoords(axisPos);
168 }
168 }
169
169
170 void removeDrawingRect()
170 void removeDrawingRect()
171 {
171 {
172 if (m_DrawingZoomRect)
172 if (m_DrawingZoomRect)
173 {
173 {
174 m_plot->removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot
174 m_plot->removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot
175 m_DrawingZoomRect = nullptr;
175 m_DrawingZoomRect = nullptr;
176 m_plot->replot(QCustomPlot::rpQueuedReplot);
176 m_plot->replot(QCustomPlot::rpQueuedReplot);
177 }
177 }
178 }
178 }
179
179
180 void selectZone(const QPoint& pos)
180 void selectZone(const QPoint& pos)
181 {
181 {
182 auto zoneAtPos = selectionZoneAt(pos);
182 auto zoneAtPos = selectionZoneAt(pos);
183 setSelectionZonesEditionEnabled(
183 setSelectionZonesEditionEnabled(
184 sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones);
184 sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones);
185 }
185 }
186
186
187 void startDrawingZone(const QPoint& pos)
187 void startDrawingZone(const QPoint& pos)
188 {
188 {
189 endDrawingZone();
189 endDrawingZone();
190
190
191 auto axisPos = posToAxisPos(pos);
191 auto axisPos = posToAxisPos(pos);
192
192
193 m_DrawingZone = new VisualizationSelectionZoneItem { m_plot };
193 m_DrawingZone = new VisualizationSelectionZoneItem { m_plot };
194 m_DrawingZone->setRange(axisPos.x(), axisPos.x());
194 m_DrawingZone->setRange(axisPos.x(), axisPos.x());
195 m_DrawingZone->setEditionEnabled(false);
195 m_DrawingZone->setEditionEnabled(false);
196 }
196 }
197
197
198 void endDrawingZone()
198 void endDrawingZone()
199 {
199 {
200 if (m_DrawingZone)
200 if (m_DrawingZone)
201 {
201 {
202 auto drawingZoneRange = m_DrawingZone->range();
202 auto drawingZoneRange = m_DrawingZone->range();
203 if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0)
203 if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0)
204 {
204 {
205 m_DrawingZone->setEditionEnabled(true);
205 m_DrawingZone->setEditionEnabled(true);
206 addSelectionZone(m_DrawingZone);
206 addSelectionZone(m_DrawingZone);
207 }
207 }
208 else
208 else
209 {
209 {
210 m_plot->removeItem(m_DrawingZone);
210 m_plot->removeItem(m_DrawingZone);
211 }
211 }
212
212
213 m_plot->replot(QCustomPlot::rpQueuedReplot);
213 m_plot->replot(QCustomPlot::rpQueuedReplot);
214 m_DrawingZone = nullptr;
214 m_DrawingZone = nullptr;
215 }
215 }
216 }
216 }
217
217
218 void moveSelectionZone(const QPoint& destination)
218 void moveSelectionZone(const QPoint& destination)
219 {
219 {
220 /*
220 /*
221 * I give up on this for now
221 * I give up on this for now
222 * @TODO implement this, the difficulty is that selection zones have their own
222 * TODO implement this, the difficulty is that selection zones have their own
223 * event handling code which seems to rely on QCP GUI event handling propagation
223 * event handling code which seems to rely on QCP GUI event handling propagation
224 * which was a realy bad design choice.
224 * which was a realy bad design choice.
225 */
225 */
226 }
226 }
227
227
228 void setSelectionZonesEditionEnabled(bool value)
228 void setSelectionZonesEditionEnabled(bool value)
229 {
229 {
230 for (auto s : m_SelectionZones)
230 for (auto s : m_SelectionZones)
231 {
231 {
232 s->setEditionEnabled(value);
232 s->setEditionEnabled(value);
233 }
233 }
234 }
234 }
235
235
236 void addSelectionZone(VisualizationSelectionZoneItem* zone) { m_SelectionZones << zone; }
236 void addSelectionZone(VisualizationSelectionZoneItem* zone) { m_SelectionZones << zone; }
237
237
238 VisualizationSelectionZoneItem* selectionZoneAt(const QPoint& pos) const
238 VisualizationSelectionZoneItem* selectionZoneAt(const QPoint& pos) const
239 {
239 {
240 VisualizationSelectionZoneItem* selectionZoneItemUnderCursor = nullptr;
240 VisualizationSelectionZoneItem* selectionZoneItemUnderCursor = nullptr;
241 auto minDistanceToZone = -1;
241 auto minDistanceToZone = -1;
242 for (auto zone : m_SelectionZones)
242 for (auto zone : m_SelectionZones)
243 {
243 {
244 auto distanceToZone = zone->selectTest(pos, false);
244 auto distanceToZone = zone->selectTest(pos, false);
245 if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone)
245 if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone)
246 && distanceToZone >= 0 && distanceToZone < m_plot->selectionTolerance())
246 && distanceToZone >= 0 && distanceToZone < m_plot->selectionTolerance())
247 {
247 {
248 selectionZoneItemUnderCursor = zone;
248 selectionZoneItemUnderCursor = zone;
249 }
249 }
250 }
250 }
251
251
252 return selectionZoneItemUnderCursor;
252 return selectionZoneItemUnderCursor;
253 }
253 }
254
254
255 QVector<VisualizationSelectionZoneItem*> selectionZonesAt(
255 QVector<VisualizationSelectionZoneItem*> selectionZonesAt(
256 const QPoint& pos, const QCustomPlot& plot) const
256 const QPoint& pos, const QCustomPlot& plot) const
257 {
257 {
258 QVector<VisualizationSelectionZoneItem*> zones;
258 QVector<VisualizationSelectionZoneItem*> zones;
259 for (auto zone : m_SelectionZones)
259 for (auto zone : m_SelectionZones)
260 {
260 {
261 auto distanceToZone = zone->selectTest(pos, false);
261 auto distanceToZone = zone->selectTest(pos, false);
262 if (distanceToZone >= 0 && distanceToZone < plot.selectionTolerance())
262 if (distanceToZone >= 0 && distanceToZone < plot.selectionTolerance())
263 {
263 {
264 zones << zone;
264 zones << zone;
265 }
265 }
266 }
266 }
267
267
268 return zones;
268 return zones;
269 }
269 }
270
270
271 void moveSelectionZoneOnTop(VisualizationSelectionZoneItem* zone, QCustomPlot& plot)
271 void moveSelectionZoneOnTop(VisualizationSelectionZoneItem* zone, QCustomPlot& plot)
272 {
272 {
273 if (!m_SelectionZones.isEmpty() && m_SelectionZones.last() != zone)
273 if (!m_SelectionZones.isEmpty() && m_SelectionZones.last() != zone)
274 {
274 {
275 zone->moveToTop();
275 zone->moveToTop();
276 m_SelectionZones.removeAll(zone);
276 m_SelectionZones.removeAll(zone);
277 m_SelectionZones.append(zone);
277 m_SelectionZones.append(zone);
278 }
278 }
279 }
279 }
280
280
281 QPointF posToAxisPos(const QPoint& pos) const
281 QPointF posToAxisPos(const QPoint& pos) const
282 {
282 {
283 auto axisX = m_plot->axisRect()->axis(QCPAxis::atBottom);
283 auto axisX = m_plot->axisRect()->axis(QCPAxis::atBottom);
284 auto axisY = m_plot->axisRect()->axis(QCPAxis::atLeft);
284 auto axisY = m_plot->axisRect()->axis(QCPAxis::atLeft);
285 return QPointF { axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y()) };
285 return QPointF { axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y()) };
286 }
286 }
287
287
288 bool pointIsInAxisRect(const QPointF& axisPoint, QCustomPlot& plot) const
288 bool pointIsInAxisRect(const QPointF& axisPoint, QCustomPlot& plot) const
289 {
289 {
290 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
290 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
291 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
291 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
292 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
292 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
293 }
293 }
294
294
295 inline QCPRange _pixDistanceToRange(double pos1, double pos2, QCPAxis* axis)
295 inline QCPRange _pixDistanceToRange(double pos1, double pos2, QCPAxis* axis)
296 {
296 {
297 if (axis->scaleType() == QCPAxis::stLinear)
297 if (axis->scaleType() == QCPAxis::stLinear)
298 {
298 {
299 auto diff = axis->pixelToCoord(pos1) - axis->pixelToCoord(pos2);
299 auto diff = axis->pixelToCoord(pos1) - axis->pixelToCoord(pos2);
300 return QCPRange { axis->range().lower + diff, axis->range().upper + diff };
300 return QCPRange { axis->range().lower + diff, axis->range().upper + diff };
301 }
301 }
302 else
302 else
303 {
303 {
304 auto diff = axis->pixelToCoord(pos1) / axis->pixelToCoord(pos2);
304 auto diff = axis->pixelToCoord(pos1) / axis->pixelToCoord(pos2);
305 return QCPRange { axis->range().lower * diff, axis->range().upper * diff };
305 return QCPRange { axis->range().lower * diff, axis->range().upper * diff };
306 }
306 }
307 }
307 }
308
308
309 void setRange(const DateTimeRange& newRange, bool updateVar = true)
309 void setRange(const DateTimeRange& newRange, bool updateVar = true)
310 {
310 {
311 this->m_plot->xAxis->setRange(newRange.m_TStart, newRange.m_TEnd);
311 this->m_plot->xAxis->setRange(newRange.m_TStart, newRange.m_TEnd);
312 if (updateVar)
312 if (updateVar)
313 {
313 {
314 for (auto it = m_VariableToPlotMultiMap.begin(), end = m_VariableToPlotMultiMap.end();
314 for (auto it = m_VariableToPlotMultiMap.begin(), end = m_VariableToPlotMultiMap.end();
315 it != end; it = m_VariableToPlotMultiMap.upper_bound(it->first))
315 it != end; it = m_VariableToPlotMultiMap.upper_bound(it->first))
316 {
316 {
317 sqpApp->variableController().asyncChangeRange(it->first, newRange);
317 sqpApp->variableController().asyncChangeRange(it->first, newRange);
318 }
318 }
319 }
319 }
320 m_plot->replot(QCustomPlot::rpQueuedReplot);
320 m_plot->replot(QCustomPlot::rpQueuedReplot);
321 }
321 }
322
322
323 void setRange(const QCPRange& newRange)
323 void setRange(const QCPRange& newRange)
324 {
324 {
325 auto graphRange = DateTimeRange { newRange.lower, newRange.upper };
325 auto graphRange = DateTimeRange { newRange.lower, newRange.upper };
326 setRange(graphRange);
326 setRange(graphRange);
327 }
327 }
328
328
329 void rescaleY() { m_plot->yAxis->rescale(true); }
329 void rescaleY() { m_plot->yAxis->rescale(true); }
330
330
331 std::tuple<double, double> moveGraph(const QPoint& destination)
331 std::tuple<double, double> moveGraph(const QPoint& destination)
332 {
332 {
333 auto currentPos = m_plot->mapFromParent(destination);
333 auto currentPos = m_plot->mapFromParent(destination);
334 auto xAxis = m_plot->axisRect()->rangeDragAxis(Qt::Horizontal);
334 auto xAxis = m_plot->axisRect()->rangeDragAxis(Qt::Horizontal);
335 auto yAxis = m_plot->axisRect()->rangeDragAxis(Qt::Vertical);
335 auto yAxis = m_plot->axisRect()->rangeDragAxis(Qt::Vertical);
336 auto oldXRange = xAxis->range();
336 auto oldXRange = xAxis->range();
337 auto oldYRange = yAxis->range();
337 auto oldYRange = yAxis->range();
338 double dx = xAxis->pixelToCoord(m_lastMousePos.x()) - xAxis->pixelToCoord(currentPos.x());
338 double dx = xAxis->pixelToCoord(m_lastMousePos.x()) - xAxis->pixelToCoord(currentPos.x());
339 xAxis->setRange(m_lastXRange.lower + dx, m_lastXRange.upper + dx);
339 xAxis->setRange(m_lastXRange.lower + dx, m_lastXRange.upper + dx);
340 if (yAxis->scaleType() == QCPAxis::stLinear)
340 if (yAxis->scaleType() == QCPAxis::stLinear)
341 {
341 {
342 double dy
342 double dy
343 = yAxis->pixelToCoord(m_lastMousePos.y()) - yAxis->pixelToCoord(currentPos.y());
343 = yAxis->pixelToCoord(m_lastMousePos.y()) - yAxis->pixelToCoord(currentPos.y());
344 yAxis->setRange(m_lastYRange.lower + dy, m_lastYRange.upper + dy);
344 yAxis->setRange(m_lastYRange.lower + dy, m_lastYRange.upper + dy);
345 }
345 }
346 else
346 else
347 {
347 {
348 double dy
348 double dy
349 = yAxis->pixelToCoord(m_lastMousePos.y()) / yAxis->pixelToCoord(currentPos.y());
349 = yAxis->pixelToCoord(m_lastMousePos.y()) / yAxis->pixelToCoord(currentPos.y());
350 yAxis->setRange(m_lastYRange.lower * dy, m_lastYRange.upper * dy);
350 yAxis->setRange(m_lastYRange.lower * dy, m_lastYRange.upper * dy);
351 }
351 }
352 auto newXRange = xAxis->range();
352 auto newXRange = xAxis->range();
353 auto newYRange = yAxis->range();
353 auto newYRange = yAxis->range();
354 setRange(xAxis->range());
354 setRange(xAxis->range());
355 // m_lastMousePos = currentPos;
355 // m_lastMousePos = currentPos;
356 return { newXRange.lower - oldXRange.lower, newYRange.lower - oldYRange.lower };
356 return { newXRange.lower - oldXRange.lower, newYRange.lower - oldYRange.lower };
357 }
357 }
358
358
359 void zoom(double factor, int center, Qt::Orientation orientation)
359 void zoom(double factor, int center, Qt::Orientation orientation)
360 {
360 {
361 QCPAxis* axis = m_plot->axisRect()->rangeZoomAxis(orientation);
361 QCPAxis* axis = m_plot->axisRect()->rangeZoomAxis(orientation);
362 axis->scaleRange(factor, axis->pixelToCoord(center));
362 axis->scaleRange(factor, axis->pixelToCoord(center));
363 if (orientation == Qt::Horizontal)
363 if (orientation == Qt::Horizontal)
364 setRange(axis->range());
364 setRange(axis->range());
365 m_plot->replot(QCustomPlot::rpQueuedReplot);
365 m_plot->replot(QCustomPlot::rpQueuedReplot);
366 }
366 }
367
367
368 void transform(const DateTimeRangeTransformation& tranformation)
368 void transform(const DateTimeRangeTransformation& tranformation)
369 {
369 {
370 auto graphRange = m_plot->xAxis->range();
370 auto graphRange = m_plot->xAxis->range();
371 DateTimeRange range { graphRange.lower, graphRange.upper };
371 DateTimeRange range { graphRange.lower, graphRange.upper };
372 range = range.transform(tranformation);
372 range = range.transform(tranformation);
373 setRange(range);
373 setRange(range);
374 m_plot->replot(QCustomPlot::rpQueuedReplot);
374 m_plot->replot(QCustomPlot::rpQueuedReplot);
375 }
375 }
376
376
377 void move(double dx, double dy)
377 void move(double dx, double dy)
378 {
378 {
379 auto xAxis = m_plot->axisRect()->rangeDragAxis(Qt::Horizontal);
379 auto xAxis = m_plot->axisRect()->rangeDragAxis(Qt::Horizontal);
380 auto yAxis = m_plot->axisRect()->rangeDragAxis(Qt::Vertical);
380 auto yAxis = m_plot->axisRect()->rangeDragAxis(Qt::Vertical);
381 xAxis->setRange(QCPRange(xAxis->range().lower + dx, xAxis->range().upper + dx));
381 xAxis->setRange(QCPRange(xAxis->range().lower + dx, xAxis->range().upper + dx));
382 yAxis->setRange(QCPRange(yAxis->range().lower + dy, yAxis->range().upper + dy));
382 yAxis->setRange(QCPRange(yAxis->range().lower + dy, yAxis->range().upper + dy));
383 setRange(xAxis->range());
383 setRange(xAxis->range());
384 m_plot->replot(QCustomPlot::rpQueuedReplot);
384 m_plot->replot(QCustomPlot::rpQueuedReplot);
385 }
385 }
386
386
387 void move(double factor, Qt::Orientation orientation)
387 void move(double factor, Qt::Orientation orientation)
388 {
388 {
389 auto oldRange = m_plot->xAxis->range();
389 auto oldRange = m_plot->xAxis->range();
390 QCPAxis* axis = m_plot->axisRect()->rangeDragAxis(orientation);
390 QCPAxis* axis = m_plot->axisRect()->rangeDragAxis(orientation);
391 if (m_plot->xAxis->scaleType() == QCPAxis::stLinear)
391 if (m_plot->xAxis->scaleType() == QCPAxis::stLinear)
392 {
392 {
393 double rg = (axis->range().upper - axis->range().lower) * (factor / 10);
393 double rg = (axis->range().upper - axis->range().lower) * (factor / 10);
394 axis->setRange(axis->range().lower + (rg), axis->range().upper + (rg));
394 axis->setRange(axis->range().lower + (rg), axis->range().upper + (rg));
395 }
395 }
396 else if (m_plot->xAxis->scaleType() == QCPAxis::stLogarithmic)
396 else if (m_plot->xAxis->scaleType() == QCPAxis::stLogarithmic)
397 {
397 {
398 int start = 0, stop = 0;
398 int start = 0, stop = 0;
399 double diff = 0.;
399 double diff = 0.;
400 if (factor > 0.0)
400 if (factor > 0.0)
401 {
401 {
402 stop = m_plot->width() * factor / 10;
402 stop = m_plot->width() * factor / 10;
403 start = 2 * m_plot->width() * factor / 10;
403 start = 2 * m_plot->width() * factor / 10;
404 }
404 }
405 if (factor < 0.0)
405 if (factor < 0.0)
406 {
406 {
407 factor *= -1.0;
407 factor *= -1.0;
408 start = m_plot->width() * factor / 10;
408 start = m_plot->width() * factor / 10;
409 stop = 2 * m_plot->width() * factor / 10;
409 stop = 2 * m_plot->width() * factor / 10;
410 }
410 }
411 diff = axis->pixelToCoord(start) / axis->pixelToCoord(stop);
411 diff = axis->pixelToCoord(start) / axis->pixelToCoord(stop);
412 axis->setRange(m_plot->axisRect()->rangeDragAxis(orientation)->range().lower * diff,
412 axis->setRange(m_plot->axisRect()->rangeDragAxis(orientation)->range().lower * diff,
413 m_plot->axisRect()->rangeDragAxis(orientation)->range().upper * diff);
413 m_plot->axisRect()->rangeDragAxis(orientation)->range().upper * diff);
414 }
414 }
415 if (orientation == Qt::Horizontal)
415 if (orientation == Qt::Horizontal)
416 setRange(axis->range());
416 setRange(axis->range());
417 m_plot->replot(QCustomPlot::rpQueuedReplot);
417 m_plot->replot(QCustomPlot::rpQueuedReplot);
418 }
418 }
419 };
419 };
420
420
421 VisualizationGraphWidget::VisualizationGraphWidget(const QString& name, QWidget* parent)
421 VisualizationGraphWidget::VisualizationGraphWidget(const QString& name, QWidget* parent)
422 : VisualizationDragWidget { parent }
422 : VisualizationDragWidget { parent }
423 , ui { new Ui::VisualizationGraphWidget }
423 , ui { new Ui::VisualizationGraphWidget }
424 , impl { spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name) }
424 , impl { spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name) }
425 {
425 {
426 ui->setupUi(this);
426 ui->setupUi(this);
427 this->layout()->addWidget(impl->m_plot);
427 this->layout()->addWidget(impl->m_plot);
428 // 'Close' options : widget is deleted when closed
428 // 'Close' options : widget is deleted when closed
429 setAttribute(Qt::WA_DeleteOnClose);
429 setAttribute(Qt::WA_DeleteOnClose);
430
430
431 // The delegate must be initialized after the ui as it uses the plot
431 // The delegate must be initialized after the ui as it uses the plot
432 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
432 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
433
433
434 // Init the cursors
434 // Init the cursors
435 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
435 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
436 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
436 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
437 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
437 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
438 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
438 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
439
439
440 this->setFocusPolicy(Qt::WheelFocus);
440 this->setFocusPolicy(Qt::WheelFocus);
441 this->setMouseTracking(true);
441 this->setMouseTracking(true);
442 impl->m_plot->setAttribute(Qt::WA_TransparentForMouseEvents);
442 impl->m_plot->setAttribute(Qt::WA_TransparentForMouseEvents);
443 impl->m_plot->setContextMenuPolicy(Qt::CustomContextMenu);
443 impl->m_plot->setContextMenuPolicy(Qt::CustomContextMenu);
444 impl->m_plot->setParent(this);
444 impl->m_plot->setParent(this);
445
445
446 connect(&sqpApp->variableController(), &VariableController2::variableDeleted, this,
446 connect(&sqpApp->variableController(), &VariableController2::variableDeleted, this,
447 &VisualizationGraphWidget::variableDeleted);
447 &VisualizationGraphWidget::variableDeleted);
448 }
448 }
449
449
450
450
451 VisualizationGraphWidget::~VisualizationGraphWidget()
451 VisualizationGraphWidget::~VisualizationGraphWidget()
452 {
452 {
453 delete ui;
453 delete ui;
454 }
454 }
455
455
456 VisualizationZoneWidget* VisualizationGraphWidget::parentZoneWidget() const noexcept
456 VisualizationZoneWidget* VisualizationGraphWidget::parentZoneWidget() const noexcept
457 {
457 {
458 auto parent = parentWidget();
458 auto parent = parentWidget();
459 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget*>(parent))
459 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget*>(parent))
460 {
460 {
461 parent = parent->parentWidget();
461 parent = parent->parentWidget();
462 }
462 }
463
463
464 return qobject_cast<VisualizationZoneWidget*>(parent);
464 return qobject_cast<VisualizationZoneWidget*>(parent);
465 }
465 }
466
466
467 VisualizationWidget* VisualizationGraphWidget::parentVisualizationWidget() const
467 VisualizationWidget* VisualizationGraphWidget::parentVisualizationWidget() const
468 {
468 {
469 auto parent = parentWidget();
469 auto parent = parentWidget();
470 while (parent != nullptr && !qobject_cast<VisualizationWidget*>(parent))
470 while (parent != nullptr && !qobject_cast<VisualizationWidget*>(parent))
471 {
471 {
472 parent = parent->parentWidget();
472 parent = parent->parentWidget();
473 }
473 }
474
474
475 return qobject_cast<VisualizationWidget*>(parent);
475 return qobject_cast<VisualizationWidget*>(parent);
476 }
476 }
477
477
478 void VisualizationGraphWidget::setFlags(GraphFlags flags)
478 void VisualizationGraphWidget::setFlags(GraphFlags flags)
479 {
479 {
480 impl->m_Flags = std::move(flags);
480 impl->m_Flags = std::move(flags);
481 }
481 }
482
482
483 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable2> variable, DateTimeRange range)
483 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable2> variable, DateTimeRange range)
484 {
484 {
485 // Uses delegate to create the qcpplot components according to the variable
485 // Uses delegate to create the qcpplot components according to the variable
486 auto createdPlottables = VisualizationGraphHelper::create(variable, *impl->m_plot);
486 auto createdPlottables = VisualizationGraphHelper::create(variable, *impl->m_plot);
487
487
488 // Sets graph properties
488 // Sets graph properties
489 impl->m_RenderingDelegate->setGraphProperties(*variable, createdPlottables);
489 impl->m_RenderingDelegate->setGraphProperties(*variable, createdPlottables);
490
490
491 impl->m_VariableToPlotMultiMap.insert({ variable, std::move(createdPlottables) });
491 impl->m_VariableToPlotMultiMap.insert({ variable, std::move(createdPlottables) });
492
492
493 setGraphRange(range);
493 setGraphRange(range);
494 // If the variable already has its data loaded, load its units and its range in the graph
494 // If the variable already has its data loaded, load its units and its range in the graph
495 if (variable->data() != nullptr)
495 if (variable->data() != nullptr)
496 {
496 {
497 impl->m_RenderingDelegate->setAxesUnits(*variable);
497 impl->m_RenderingDelegate->setAxesUnits(*variable);
498 }
498 }
499 else
499 else
500 {
500 {
501 auto context = new QObject { this };
501 auto context = new QObject { this };
502 connect(
502 connect(
503 variable.get(), &Variable2::updated, context, [this, variable, context, range](QUuid) {
503 variable.get(), &Variable2::updated, context, [this, variable, context, range](QUuid) {
504 this->impl->m_RenderingDelegate->setAxesUnits(*variable);
504 this->impl->m_RenderingDelegate->setAxesUnits(*variable);
505 this->impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
505 this->impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
506 delete context;
506 delete context;
507 });
507 });
508 }
508 }
509 //@TODO this is bad! when variable is moved to another graph it still fires
509 //TODO this is bad! when variable is moved to another graph it still fires
510 // even if this has been deleted
510 // even if this has been deleted
511 connect(variable.get(), &Variable2::updated, this, &VisualizationGraphWidget::variableUpdated);
511 connect(variable.get(), &Variable2::updated, this, &VisualizationGraphWidget::variableUpdated);
512 this->onUpdateVarDisplaying(variable, range); // My bullshit
512 this->onUpdateVarDisplaying(variable, range); // My bullshit
513 emit variableAdded(variable);
513 emit variableAdded(variable);
514 }
514 }
515
515
516 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable2> variable) noexcept
516 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable2> variable) noexcept
517 {
517 {
518 // Each component associated to the variable :
518 // Each component associated to the variable :
519 // - is removed from qcpplot (which deletes it)
519 // - is removed from qcpplot (which deletes it)
520 // - is no longer referenced in the map
520 // - is no longer referenced in the map
521 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
521 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
522 if (variableIt != impl->m_VariableToPlotMultiMap.cend())
522 if (variableIt != impl->m_VariableToPlotMultiMap.cend())
523 {
523 {
524 emit variableAboutToBeRemoved(variable);
524 emit variableAboutToBeRemoved(variable);
525
525
526 auto& plottablesMap = variableIt->second;
526 auto& plottablesMap = variableIt->second;
527
527
528 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
528 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
529 plottableIt != plottableEnd;)
529 plottableIt != plottableEnd;)
530 {
530 {
531 impl->m_plot->removePlottable(plottableIt->second);
531 impl->m_plot->removePlottable(plottableIt->second);
532 plottableIt = plottablesMap.erase(plottableIt);
532 plottableIt = plottablesMap.erase(plottableIt);
533 }
533 }
534
534
535 impl->m_VariableToPlotMultiMap.erase(variableIt);
535 impl->m_VariableToPlotMultiMap.erase(variableIt);
536 }
536 }
537
537
538 // Updates graph
538 // Updates graph
539 impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
539 impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
540 }
540 }
541
541
542 std::vector<std::shared_ptr<Variable2>> VisualizationGraphWidget::variables() const
542 std::vector<std::shared_ptr<Variable2>> VisualizationGraphWidget::variables() const
543 {
543 {
544 auto variables = std::vector<std::shared_ptr<Variable2>> {};
544 auto variables = std::vector<std::shared_ptr<Variable2>> {};
545 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
545 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
546 it != std::cend(impl->m_VariableToPlotMultiMap); ++it)
546 it != std::cend(impl->m_VariableToPlotMultiMap); ++it)
547 {
547 {
548 variables.push_back(it->first);
548 variables.push_back(it->first);
549 }
549 }
550
550
551 return variables;
551 return variables;
552 }
552 }
553
553
554 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable2> variable)
554 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable2> variable)
555 {
555 {
556 if (!variable)
556 if (!variable)
557 {
557 {
558 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
558 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
559 return;
559 return;
560 }
560 }
561
561
562 VisualizationGraphHelper::setYAxisRange(variable, *impl->m_plot);
562 VisualizationGraphHelper::setYAxisRange(variable, *impl->m_plot);
563 }
563 }
564
564
565 DateTimeRange VisualizationGraphWidget::graphRange() const noexcept
565 DateTimeRange VisualizationGraphWidget::graphRange() const noexcept
566 {
566 {
567 auto graphRange = impl->m_plot->xAxis->range();
567 auto graphRange = impl->m_plot->xAxis->range();
568 return DateTimeRange { graphRange.lower, graphRange.upper };
568 return DateTimeRange { graphRange.lower, graphRange.upper };
569 }
569 }
570
570
571 void VisualizationGraphWidget::setGraphRange(
571 void VisualizationGraphWidget::setGraphRange(
572 const DateTimeRange& range, bool updateVar, bool forward)
572 const DateTimeRange& range, bool updateVar, bool forward)
573 {
573 {
574 impl->setRange(range, updateVar);
574 impl->setRange(range, updateVar);
575 if (forward)
575 if (forward)
576 {
576 {
577 emit this->setrange_sig(this->graphRange(), true, false);
577 emit this->setrange_sig(this->graphRange(), true, false);
578 }
578 }
579 }
579 }
580
580
581 void VisualizationGraphWidget::setAutoRangeOnVariableInitialization(bool value)
581 void VisualizationGraphWidget::setAutoRangeOnVariableInitialization(bool value)
582 {
582 {
583 impl->m_VariableAutoRangeOnInit = value;
583 impl->m_VariableAutoRangeOnInit = value;
584 }
584 }
585
585
586 QVector<DateTimeRange> VisualizationGraphWidget::selectionZoneRanges() const
586 QVector<DateTimeRange> VisualizationGraphWidget::selectionZoneRanges() const
587 {
587 {
588 QVector<DateTimeRange> ranges;
588 QVector<DateTimeRange> ranges;
589 for (auto zone : impl->m_SelectionZones)
589 for (auto zone : impl->m_SelectionZones)
590 {
590 {
591 ranges << zone->range();
591 ranges << zone->range();
592 }
592 }
593
593
594 return ranges;
594 return ranges;
595 }
595 }
596
596
597 void VisualizationGraphWidget::addSelectionZones(const QVector<DateTimeRange>& ranges)
597 void VisualizationGraphWidget::addSelectionZones(const QVector<DateTimeRange>& ranges)
598 {
598 {
599 for (const auto& range : ranges)
599 for (const auto& range : ranges)
600 {
600 {
601 // note: ownership is transfered to QCustomPlot
601 // note: ownership is transfered to QCustomPlot
602 auto zone = new VisualizationSelectionZoneItem(&plot());
602 auto zone = new VisualizationSelectionZoneItem(&plot());
603 zone->setRange(range.m_TStart, range.m_TEnd);
603 zone->setRange(range.m_TStart, range.m_TEnd);
604 impl->addSelectionZone(zone);
604 impl->addSelectionZone(zone);
605 }
605 }
606
606
607 plot().replot(QCustomPlot::rpQueuedReplot);
607 plot().replot(QCustomPlot::rpQueuedReplot);
608 }
608 }
609
609
610 VisualizationSelectionZoneItem* VisualizationGraphWidget::addSelectionZone(
610 VisualizationSelectionZoneItem* VisualizationGraphWidget::addSelectionZone(
611 const QString& name, const DateTimeRange& range)
611 const QString& name, const DateTimeRange& range)
612 {
612 {
613 // note: ownership is transfered to QCustomPlot
613 // note: ownership is transfered to QCustomPlot
614 auto zone = new VisualizationSelectionZoneItem(&plot());
614 auto zone = new VisualizationSelectionZoneItem(&plot());
615 zone->setName(name);
615 zone->setName(name);
616 zone->setRange(range.m_TStart, range.m_TEnd);
616 zone->setRange(range.m_TStart, range.m_TEnd);
617 impl->addSelectionZone(zone);
617 impl->addSelectionZone(zone);
618
618
619 plot().replot(QCustomPlot::rpQueuedReplot);
619 plot().replot(QCustomPlot::rpQueuedReplot);
620
620
621 return zone;
621 return zone;
622 }
622 }
623
623
624 void VisualizationGraphWidget::removeSelectionZone(VisualizationSelectionZoneItem* selectionZone)
624 void VisualizationGraphWidget::removeSelectionZone(VisualizationSelectionZoneItem* selectionZone)
625 {
625 {
626 parentVisualizationWidget()->selectionZoneManager().setSelected(selectionZone, false);
626 parentVisualizationWidget()->selectionZoneManager().setSelected(selectionZone, false);
627
627
628 if (impl->m_HoveredZone == selectionZone)
628 if (impl->m_HoveredZone == selectionZone)
629 {
629 {
630 impl->m_HoveredZone = nullptr;
630 impl->m_HoveredZone = nullptr;
631 setCursor(Qt::ArrowCursor);
631 setCursor(Qt::ArrowCursor);
632 }
632 }
633
633
634 impl->m_SelectionZones.removeAll(selectionZone);
634 impl->m_SelectionZones.removeAll(selectionZone);
635 plot().removeItem(selectionZone);
635 plot().removeItem(selectionZone);
636 plot().replot(QCustomPlot::rpQueuedReplot);
636 plot().replot(QCustomPlot::rpQueuedReplot);
637 }
637 }
638
638
639 void VisualizationGraphWidget::undoZoom()
639 void VisualizationGraphWidget::undoZoom()
640 {
640 {
641 auto zoom = impl->m_ZoomStack.pop();
641 auto zoom = impl->m_ZoomStack.pop();
642 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
642 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
643 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
643 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
644
644
645 axisX->setRange(zoom.first);
645 axisX->setRange(zoom.first);
646 axisY->setRange(zoom.second);
646 axisY->setRange(zoom.second);
647
647
648 plot().replot(QCustomPlot::rpQueuedReplot);
648 plot().replot(QCustomPlot::rpQueuedReplot);
649 }
649 }
650
650
651 void VisualizationGraphWidget::zoom(
651 void VisualizationGraphWidget::zoom(
652 double factor, int center, Qt::Orientation orientation, bool forward)
652 double factor, int center, Qt::Orientation orientation, bool forward)
653 {
653 {
654 impl->zoom(factor, center, orientation);
654 impl->zoom(factor, center, orientation);
655 if (forward && orientation == Qt::Horizontal)
655 if (forward && orientation == Qt::Horizontal)
656 emit this->setrange_sig(this->graphRange(), true, false);
656 emit this->setrange_sig(this->graphRange(), true, false);
657 }
657 }
658
658
659 void VisualizationGraphWidget::move(double factor, Qt::Orientation orientation, bool forward)
659 void VisualizationGraphWidget::move(double factor, Qt::Orientation orientation, bool forward)
660 {
660 {
661 impl->move(factor, orientation);
661 impl->move(factor, orientation);
662 if (forward)
662 if (forward)
663 emit this->setrange_sig(this->graphRange(), true, false);
663 emit this->setrange_sig(this->graphRange(), true, false);
664 }
664 }
665
665
666 void VisualizationGraphWidget::move(double dx, double dy, bool forward)
666 void VisualizationGraphWidget::move(double dx, double dy, bool forward)
667 {
667 {
668 impl->move(dx, dy);
668 impl->move(dx, dy);
669 if (forward)
669 if (forward)
670 emit this->setrange_sig(this->graphRange(), true, false);
670 emit this->setrange_sig(this->graphRange(), true, false);
671 }
671 }
672
672
673 void VisualizationGraphWidget::transform(
673 void VisualizationGraphWidget::transform(
674 const DateTimeRangeTransformation& tranformation, bool forward)
674 const DateTimeRangeTransformation& tranformation, bool forward)
675 {
675 {
676 impl->transform(tranformation);
676 impl->transform(tranformation);
677 if (forward)
677 if (forward)
678 emit this->setrange_sig(this->graphRange(), true, false);
678 emit this->setrange_sig(this->graphRange(), true, false);
679 }
679 }
680
680
681 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor* visitor)
681 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor* visitor)
682 {
682 {
683 if (visitor)
683 if (visitor)
684 {
684 {
685 visitor->visit(this);
685 visitor->visit(this);
686 }
686 }
687 else
687 else
688 {
688 {
689 qCCritical(LOG_VisualizationGraphWidget())
689 qCCritical(LOG_VisualizationGraphWidget())
690 << tr("Can't visit widget : the visitor is null");
690 << tr("Can't visit widget : the visitor is null");
691 }
691 }
692 }
692 }
693
693
694 bool VisualizationGraphWidget::canDrop(Variable2& variable) const
694 bool VisualizationGraphWidget::canDrop(Variable2& variable) const
695 {
695 {
696 auto isSpectrogram
696 auto isSpectrogram
697 = [](auto& variable) { return variable.type() == DataSeriesType::SPECTROGRAM; };
697 = [](auto& variable) { return variable.type() == DataSeriesType::SPECTROGRAM; };
698
698
699 // - A spectrogram series can't be dropped on graph with existing plottables
699 // - A spectrogram series can't be dropped on graph with existing plottables
700 // - No data series can be dropped on graph with existing spectrogram series
700 // - No data series can be dropped on graph with existing spectrogram series
701 return isSpectrogram(variable)
701 return isSpectrogram(variable)
702 ? impl->m_VariableToPlotMultiMap.empty()
702 ? impl->m_VariableToPlotMultiMap.empty()
703 : std::none_of(impl->m_VariableToPlotMultiMap.cbegin(),
703 : std::none_of(impl->m_VariableToPlotMultiMap.cbegin(),
704 impl->m_VariableToPlotMultiMap.cend(),
704 impl->m_VariableToPlotMultiMap.cend(),
705 [isSpectrogram](const auto& entry) { return isSpectrogram(*entry.first); });
705 [isSpectrogram](const auto& entry) { return isSpectrogram(*entry.first); });
706 }
706 }
707
707
708 bool VisualizationGraphWidget::contains(Variable2& variable) const
708 bool VisualizationGraphWidget::contains(Variable2& variable) const
709 {
709 {
710 // Finds the variable among the keys of the map
710 // Finds the variable among the keys of the map
711 auto variablePtr = &variable;
711 auto variablePtr = &variable;
712 auto findVariable
712 auto findVariable
713 = [variablePtr](const auto& entry) { return variablePtr == entry.first.get(); };
713 = [variablePtr](const auto& entry) { return variablePtr == entry.first.get(); };
714
714
715 auto end = impl->m_VariableToPlotMultiMap.cend();
715 auto end = impl->m_VariableToPlotMultiMap.cend();
716 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
716 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
717 return it != end;
717 return it != end;
718 }
718 }
719
719
720 QString VisualizationGraphWidget::name() const
720 QString VisualizationGraphWidget::name() const
721 {
721 {
722 return impl->m_Name;
722 return impl->m_Name;
723 }
723 }
724
724
725 QMimeData* VisualizationGraphWidget::mimeData(const QPoint& position) const
725 QMimeData* VisualizationGraphWidget::mimeData(const QPoint& position) const
726 {
726 {
727 auto mimeData = new QMimeData;
727 auto mimeData = new QMimeData;
728
728
729 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position);
729 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position);
730 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
730 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
731 && selectionZoneItemUnderCursor)
731 && selectionZoneItemUnderCursor)
732 {
732 {
733 mimeData->setData(MIME_TYPE_TIME_RANGE,
733 mimeData->setData(MIME_TYPE_TIME_RANGE,
734 TimeController::mimeDataForTimeRange(selectionZoneItemUnderCursor->range()));
734 TimeController::mimeDataForTimeRange(selectionZoneItemUnderCursor->range()));
735 mimeData->setData(MIME_TYPE_SELECTION_ZONE,
735 mimeData->setData(MIME_TYPE_SELECTION_ZONE,
736 TimeController::mimeDataForTimeRange(selectionZoneItemUnderCursor->range()));
736 TimeController::mimeDataForTimeRange(selectionZoneItemUnderCursor->range()));
737 }
737 }
738 else
738 else
739 {
739 {
740 mimeData->setData(MIME_TYPE_GRAPH, QByteArray {});
740 mimeData->setData(MIME_TYPE_GRAPH, QByteArray {});
741
741
742 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
742 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
743 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
743 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
744 }
744 }
745
745
746 return mimeData;
746 return mimeData;
747 }
747 }
748
748
749 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint& dragPosition)
749 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint& dragPosition)
750 {
750 {
751 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition);
751 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition);
752 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
752 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
753 && selectionZoneItemUnderCursor)
753 && selectionZoneItemUnderCursor)
754 {
754 {
755
755
756 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
756 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
757 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
757 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
758
758
759 auto zoneSize = QSizeF { qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
759 auto zoneSize = QSizeF { qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
760 qAbs(zoneBottomRight.y() - zoneTopLeft.y()) }
760 qAbs(zoneBottomRight.y() - zoneTopLeft.y()) }
761 .toSize();
761 .toSize();
762
762
763 auto pixmap = QPixmap(zoneSize);
763 auto pixmap = QPixmap(zoneSize);
764 render(&pixmap, QPoint(), QRegion { QRect { zoneTopLeft.toPoint(), zoneSize } });
764 render(&pixmap, QPoint(), QRegion { QRect { zoneTopLeft.toPoint(), zoneSize } });
765
765
766 return pixmap;
766 return pixmap;
767 }
767 }
768
768
769 return QPixmap();
769 return QPixmap();
770 }
770 }
771
771
772 bool VisualizationGraphWidget::isDragAllowed() const
772 bool VisualizationGraphWidget::isDragAllowed() const
773 {
773 {
774 return true;
774 return true;
775 }
775 }
776
776
777 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
777 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
778 {
778 {
779 if (highlighted)
779 if (highlighted)
780 {
780 {
781 plot().setBackground(QBrush(QColor("#BBD5EE")));
781 plot().setBackground(QBrush(QColor("#BBD5EE")));
782 }
782 }
783 else
783 else
784 {
784 {
785 plot().setBackground(QBrush(Qt::white));
785 plot().setBackground(QBrush(Qt::white));
786 }
786 }
787
787
788 plot().update();
788 plot().update();
789 }
789 }
790
790
791 void VisualizationGraphWidget::addVerticalCursor(double time)
791 void VisualizationGraphWidget::addVerticalCursor(double time)
792 {
792 {
793 impl->m_VerticalCursor->setPosition(time);
793 impl->m_VerticalCursor->setPosition(time);
794 impl->m_VerticalCursor->setVisible(true);
794 impl->m_VerticalCursor->setVisible(true);
795
795
796 auto text
796 auto text
797 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
797 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
798 impl->m_VerticalCursor->setLabelText(text);
798 impl->m_VerticalCursor->setLabelText(text);
799 }
799 }
800
800
801 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
801 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
802 {
802 {
803 impl->m_VerticalCursor->setAbsolutePosition(position);
803 impl->m_VerticalCursor->setAbsolutePosition(position);
804 impl->m_VerticalCursor->setVisible(true);
804 impl->m_VerticalCursor->setVisible(true);
805
805
806 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
806 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
807 auto text
807 auto text
808 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
808 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
809 impl->m_VerticalCursor->setLabelText(text);
809 impl->m_VerticalCursor->setLabelText(text);
810 }
810 }
811
811
812 void VisualizationGraphWidget::removeVerticalCursor()
812 void VisualizationGraphWidget::removeVerticalCursor()
813 {
813 {
814 impl->m_VerticalCursor->setVisible(false);
814 impl->m_VerticalCursor->setVisible(false);
815 plot().replot(QCustomPlot::rpQueuedReplot);
815 plot().replot(QCustomPlot::rpQueuedReplot);
816 }
816 }
817
817
818 void VisualizationGraphWidget::addHorizontalCursor(double value)
818 void VisualizationGraphWidget::addHorizontalCursor(double value)
819 {
819 {
820 impl->m_HorizontalCursor->setPosition(value);
820 impl->m_HorizontalCursor->setPosition(value);
821 impl->m_HorizontalCursor->setVisible(true);
821 impl->m_HorizontalCursor->setVisible(true);
822 impl->m_HorizontalCursor->setLabelText(QString::number(value));
822 impl->m_HorizontalCursor->setLabelText(QString::number(value));
823 }
823 }
824
824
825 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
825 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
826 {
826 {
827 impl->m_HorizontalCursor->setAbsolutePosition(position);
827 impl->m_HorizontalCursor->setAbsolutePosition(position);
828 impl->m_HorizontalCursor->setVisible(true);
828 impl->m_HorizontalCursor->setVisible(true);
829
829
830 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
830 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
831 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
831 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
832 }
832 }
833
833
834 void VisualizationGraphWidget::removeHorizontalCursor()
834 void VisualizationGraphWidget::removeHorizontalCursor()
835 {
835 {
836 impl->m_HorizontalCursor->setVisible(false);
836 impl->m_HorizontalCursor->setVisible(false);
837 plot().replot(QCustomPlot::rpQueuedReplot);
837 plot().replot(QCustomPlot::rpQueuedReplot);
838 }
838 }
839
839
840 void VisualizationGraphWidget::closeEvent(QCloseEvent* event)
840 void VisualizationGraphWidget::closeEvent(QCloseEvent* event)
841 {
841 {
842 Q_UNUSED(event);
842 Q_UNUSED(event);
843
843
844 for (auto i : impl->m_SelectionZones)
844 for (auto i : impl->m_SelectionZones)
845 {
845 {
846 parentVisualizationWidget()->selectionZoneManager().setSelected(i, false);
846 parentVisualizationWidget()->selectionZoneManager().setSelected(i, false);
847 }
847 }
848
848
849 // Prevents that all variables will be removed from graph when it will be closed
849 // Prevents that all variables will be removed from graph when it will be closed
850 for (auto& variableEntry : impl->m_VariableToPlotMultiMap)
850 for (auto& variableEntry : impl->m_VariableToPlotMultiMap)
851 {
851 {
852 emit variableAboutToBeRemoved(variableEntry.first);
852 emit variableAboutToBeRemoved(variableEntry.first);
853 }
853 }
854 }
854 }
855
855
856 void VisualizationGraphWidget::enterEvent(QEvent* event)
856 void VisualizationGraphWidget::enterEvent(QEvent* event)
857 {
857 {
858 Q_UNUSED(event);
858 Q_UNUSED(event);
859 impl->m_RenderingDelegate->showGraphOverlay(true);
859 impl->m_RenderingDelegate->showGraphOverlay(true);
860 }
860 }
861
861
862 void VisualizationGraphWidget::leaveEvent(QEvent* event)
862 void VisualizationGraphWidget::leaveEvent(QEvent* event)
863 {
863 {
864 Q_UNUSED(event);
864 Q_UNUSED(event);
865 impl->m_RenderingDelegate->showGraphOverlay(false);
865 impl->m_RenderingDelegate->showGraphOverlay(false);
866
866
867 if (auto parentZone = parentZoneWidget())
867 if (auto parentZone = parentZoneWidget())
868 {
868 {
869 parentZone->notifyMouseLeaveGraph(this);
869 parentZone->notifyMouseLeaveGraph(this);
870 }
870 }
871 else
871 else
872 {
872 {
873 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
873 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
874 }
874 }
875
875
876 if (impl->m_HoveredZone)
876 if (impl->m_HoveredZone)
877 {
877 {
878 impl->m_HoveredZone->setHovered(false);
878 impl->m_HoveredZone->setHovered(false);
879 impl->m_HoveredZone = nullptr;
879 impl->m_HoveredZone = nullptr;
880 }
880 }
881 }
881 }
882
882
883 void VisualizationGraphWidget::wheelEvent(QWheelEvent* event)
883 void VisualizationGraphWidget::wheelEvent(QWheelEvent* event)
884 {
884 {
885 double factor;
885 double factor;
886 double wheelSteps = event->delta() / 120.0; // a single step delta is +/-120 usually
886 double wheelSteps = event->delta() / 120.0; // a single step delta is +/-120 usually
887 if (event->modifiers() == Qt::ControlModifier)
887 if (event->modifiers() == Qt::ControlModifier)
888 {
888 {
889 if (event->orientation() == Qt::Vertical) // mRangeZoom.testFlag(Qt::Vertical))
889 if (event->orientation() == Qt::Vertical) // mRangeZoom.testFlag(Qt::Vertical))
890 {
890 {
891 setCursor(Qt::SizeVerCursor);
891 setCursor(Qt::SizeVerCursor);
892 factor = pow(impl->m_plot->axisRect()->rangeZoomFactor(Qt::Vertical), wheelSteps);
892 factor = pow(impl->m_plot->axisRect()->rangeZoomFactor(Qt::Vertical), wheelSteps);
893 zoom(factor, event->pos().y(), Qt::Vertical);
893 zoom(factor, event->pos().y(), Qt::Vertical);
894 }
894 }
895 }
895 }
896 else if (event->modifiers() == Qt::ShiftModifier)
896 else if (event->modifiers() == Qt::ShiftModifier)
897 {
897 {
898 if (event->orientation() == Qt::Vertical) // mRangeZoom.testFlag(Qt::Vertical))
898 if (event->orientation() == Qt::Vertical) // mRangeZoom.testFlag(Qt::Vertical))
899 {
899 {
900 setCursor(Qt::SizeHorCursor);
900 setCursor(Qt::SizeHorCursor);
901 factor = pow(impl->m_plot->axisRect()->rangeZoomFactor(Qt::Horizontal), wheelSteps);
901 factor = pow(impl->m_plot->axisRect()->rangeZoomFactor(Qt::Horizontal), wheelSteps);
902 zoom(factor, event->pos().x(), Qt::Horizontal);
902 zoom(factor, event->pos().x(), Qt::Horizontal);
903 }
903 }
904 }
904 }
905 else
905 else
906 {
906 {
907 move(wheelSteps, Qt::Horizontal);
907 move(wheelSteps, Qt::Horizontal);
908 }
908 }
909 event->accept();
909 event->accept();
910 }
910 }
911
911
912
912
913 void VisualizationGraphWidget::mouseMoveEvent(QMouseEvent* event)
913 void VisualizationGraphWidget::mouseMoveEvent(QMouseEvent* event)
914 {
914 {
915 if (impl->isDrawingZoomRect())
915 if (impl->isDrawingZoomRect())
916 {
916 {
917 impl->updateZoomRect(event->pos());
917 impl->updateZoomRect(event->pos());
918 }
918 }
919 else if (impl->isDrawingZoneRect())
919 else if (impl->isDrawingZoneRect())
920 {
920 {
921 impl->updateZoneRect(event->pos());
921 impl->updateZoneRect(event->pos());
922 }
922 }
923 else if (event->buttons() == Qt::LeftButton)
923 else if (event->buttons() == Qt::LeftButton)
924 {
924 {
925 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::None)
925 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::None)
926 {
926 {
927 auto [dx, dy] = impl->moveGraph(event->pos());
927 auto [dx, dy] = impl->moveGraph(event->pos());
928 emit this->setrange_sig(this->graphRange(), true, false);
928 emit this->setrange_sig(this->graphRange(), true, false);
929 }
929 }
930 else if (sqpApp->plotsInteractionMode()
930 else if (sqpApp->plotsInteractionMode()
931 == SqpApplication::PlotsInteractionMode::SelectionZones)
931 == SqpApplication::PlotsInteractionMode::SelectionZones)
932 {
932 {
933 auto posInPlot = this->impl->m_plot->mapFromParent(event->pos());
933 auto posInPlot = this->impl->m_plot->mapFromParent(event->pos());
934 if (auto item = impl->m_plot->itemAt(posInPlot))
934 if (auto item = impl->m_plot->itemAt(posInPlot))
935 {
935 {
936 if (qobject_cast<VisualizationSelectionZoneItem*>(item))
936 if (qobject_cast<VisualizationSelectionZoneItem*>(item))
937 {
937 {
938 QMouseEvent e { QEvent::MouseMove, posInPlot, event->button(), event->buttons(),
938 QMouseEvent e { QEvent::MouseMove, posInPlot, event->button(), event->buttons(),
939 event->modifiers() };
939 event->modifiers() };
940 sqpApp->sendEvent(this->impl->m_plot, &e);
940 sqpApp->sendEvent(this->impl->m_plot, &e);
941 this->impl->m_plot->replot(QCustomPlot::rpImmediateRefresh);
941 this->impl->m_plot->replot(QCustomPlot::rpImmediateRefresh);
942 }
942 }
943 }
943 }
944 }
944 }
945 }
945 }
946 else
946 else
947 {
947 {
948 impl->m_RenderingDelegate->updateTooltip(event);
948 impl->m_RenderingDelegate->updateTooltip(event);
949 }
949 }
950 // event->accept();
950 // event->accept();
951 QWidget::mouseMoveEvent(event);
951 QWidget::mouseMoveEvent(event);
952 }
952 }
953
953
954 void VisualizationGraphWidget::mouseReleaseEvent(QMouseEvent* event)
954 void VisualizationGraphWidget::mouseReleaseEvent(QMouseEvent* event)
955 {
955 {
956 if (impl->isDrawingZoomRect())
956 if (impl->isDrawingZoomRect())
957 {
957 {
958 auto oldRange = this->graphRange();
958 auto oldRange = this->graphRange();
959 impl->applyZoomRect();
959 impl->applyZoomRect();
960 auto newRange = this->graphRange();
960 auto newRange = this->graphRange();
961 if (auto tf = DateTimeRangeHelper::computeTransformation(oldRange, newRange))
961 if (auto tf = DateTimeRangeHelper::computeTransformation(oldRange, newRange))
962 emit this->transform_sig(tf.value(), false);
962 emit this->transform_sig(tf.value(), false);
963 }
963 }
964 else if (impl->isDrawingZoneRect())
964 else if (impl->isDrawingZoneRect())
965 {
965 {
966 impl->endDrawingZone();
966 impl->endDrawingZone();
967 }
967 }
968 else
968 else
969 {
969 {
970 setCursor(Qt::ArrowCursor);
970 setCursor(Qt::ArrowCursor);
971 }
971 }
972 auto posInPlot = this->impl->m_plot->mapFromParent(event->pos());
972 auto posInPlot = this->impl->m_plot->mapFromParent(event->pos());
973 if (auto item = impl->m_plot->itemAt(posInPlot))
973 if (auto item = impl->m_plot->itemAt(posInPlot))
974 {
974 {
975 if (qobject_cast<VisualizationSelectionZoneItem*>(item))
975 if (qobject_cast<VisualizationSelectionZoneItem*>(item))
976 {
976 {
977 QMouseEvent e { QEvent::MouseButtonRelease, posInPlot, event->button(),
977 QMouseEvent e { QEvent::MouseButtonRelease, posInPlot, event->button(),
978 event->buttons(), event->modifiers() };
978 event->buttons(), event->modifiers() };
979 sqpApp->sendEvent(this->impl->m_plot, &e);
979 sqpApp->sendEvent(this->impl->m_plot, &e);
980 }
980 }
981 }
981 }
982 event->accept();
982 event->accept();
983 }
983 }
984
984
985 void VisualizationGraphWidget::mousePressEvent(QMouseEvent* event)
985 void VisualizationGraphWidget::mousePressEvent(QMouseEvent* event)
986 {
986 {
987 if (event->button() == Qt::RightButton)
987 if (event->button() == Qt::RightButton)
988 {
988 {
989 onGraphMenuRequested(event->pos());
989 onGraphMenuRequested(event->pos());
990 }
990 }
991 else
991 else
992 {
992 {
993 auto selectedZone = impl->selectionZoneAt(event->pos());
993 auto selectedZone = impl->selectionZoneAt(event->pos());
994 switch (sqpApp->plotsInteractionMode())
994 switch (sqpApp->plotsInteractionMode())
995 {
995 {
996 case SqpApplication::PlotsInteractionMode::DragAndDrop:
996 case SqpApplication::PlotsInteractionMode::DragAndDrop:
997 break;
997 break;
998 case SqpApplication::PlotsInteractionMode::SelectionZones:
998 case SqpApplication::PlotsInteractionMode::SelectionZones:
999 impl->setSelectionZonesEditionEnabled(true);
999 impl->setSelectionZonesEditionEnabled(true);
1000 if ((event->modifiers() == Qt::ControlModifier) && (selectedZone != nullptr))
1000 if ((event->modifiers() == Qt::ControlModifier) && (selectedZone != nullptr))
1001 {
1001 {
1002 auto alreadySelectedZones
1002 auto alreadySelectedZones
1003 = parentVisualizationWidget()->selectionZoneManager().selectedItems();
1003 = parentVisualizationWidget()->selectionZoneManager().selectedItems();
1004 selectedZone->setAssociatedEditedZones(alreadySelectedZones);
1004 selectedZone->setAssociatedEditedZones(alreadySelectedZones);
1005 if (SciQLop::containers::contains(alreadySelectedZones, selectedZone))
1005 if (SciQLop::containers::contains(alreadySelectedZones, selectedZone))
1006 {
1006 {
1007 alreadySelectedZones.removeOne(selectedZone);
1007 alreadySelectedZones.removeOne(selectedZone);
1008 }
1008 }
1009 else
1009 else
1010 {
1010 {
1011 alreadySelectedZones.append(selectedZone);
1011 alreadySelectedZones.append(selectedZone);
1012 }
1012 }
1013 parentVisualizationWidget()->selectionZoneManager().select(
1013 parentVisualizationWidget()->selectionZoneManager().select(
1014 alreadySelectedZones);
1014 alreadySelectedZones);
1015 }
1015 }
1016 else
1016 else
1017 {
1017 {
1018 if (!selectedZone)
1018 if (!selectedZone)
1019 {
1019 {
1020 parentVisualizationWidget()->selectionZoneManager().clearSelection();
1020 parentVisualizationWidget()->selectionZoneManager().clearSelection();
1021 impl->startDrawingZone(event->pos());
1021 impl->startDrawingZone(event->pos());
1022 }
1022 }
1023 else
1023 else
1024 {
1024 {
1025 parentVisualizationWidget()->selectionZoneManager().select(
1025 parentVisualizationWidget()->selectionZoneManager().select(
1026 { selectedZone });
1026 { selectedZone });
1027 }
1027 }
1028 }
1028 }
1029 {
1029 {
1030 auto posInPlot = this->impl->m_plot->mapFromParent(event->pos());
1030 auto posInPlot = this->impl->m_plot->mapFromParent(event->pos());
1031 if (auto item = impl->m_plot->itemAt(posInPlot))
1031 if (auto item = impl->m_plot->itemAt(posInPlot))
1032 {
1032 {
1033 if (qobject_cast<VisualizationSelectionZoneItem*>(item))
1033 if (qobject_cast<VisualizationSelectionZoneItem*>(item))
1034 {
1034 {
1035 QMouseEvent e { QEvent::MouseButtonPress, posInPlot, event->button(),
1035 QMouseEvent e { QEvent::MouseButtonPress, posInPlot, event->button(),
1036 event->buttons(), event->modifiers() };
1036 event->buttons(), event->modifiers() };
1037 sqpApp->sendEvent(this->impl->m_plot, &e);
1037 sqpApp->sendEvent(this->impl->m_plot, &e);
1038 }
1038 }
1039 }
1039 }
1040 }
1040 }
1041 break;
1041 break;
1042 case SqpApplication::PlotsInteractionMode::ZoomBox:
1042 case SqpApplication::PlotsInteractionMode::ZoomBox:
1043 impl->startDrawingRect(event->pos());
1043 impl->startDrawingRect(event->pos());
1044 break;
1044 break;
1045 default:
1045 default:
1046 if (auto item = impl->m_plot->itemAt(event->pos()))
1046 if (auto item = impl->m_plot->itemAt(event->pos()))
1047 {
1047 {
1048 emit impl->m_plot->itemClick(item, event);
1048 emit impl->m_plot->itemClick(item, event);
1049 if (qobject_cast<VisualizationSelectionZoneItem*>(item))
1049 if (qobject_cast<VisualizationSelectionZoneItem*>(item))
1050 {
1050 {
1051 setCursor(Qt::ClosedHandCursor);
1051 setCursor(Qt::ClosedHandCursor);
1052 impl->enterPlotDrag(event->pos());
1052 impl->enterPlotDrag(event->pos());
1053 }
1053 }
1054 }
1054 }
1055 else
1055 else
1056 {
1056 {
1057 setCursor(Qt::ClosedHandCursor);
1057 setCursor(Qt::ClosedHandCursor);
1058 impl->enterPlotDrag(event->pos());
1058 impl->enterPlotDrag(event->pos());
1059 }
1059 }
1060 }
1060 }
1061 }
1061 }
1062 // event->accept();
1062 // event->accept();
1063 QWidget::mousePressEvent(event);
1063 QWidget::mousePressEvent(event);
1064 }
1064 }
1065
1065
1066 void VisualizationGraphWidget::mouseDoubleClickEvent(QMouseEvent* event)
1066 void VisualizationGraphWidget::mouseDoubleClickEvent(QMouseEvent* event)
1067 {
1067 {
1068 impl->m_RenderingDelegate->onMouseDoubleClick(event);
1068 impl->m_RenderingDelegate->onMouseDoubleClick(event);
1069 }
1069 }
1070
1070
1071 void VisualizationGraphWidget::keyReleaseEvent(QKeyEvent* event)
1071 void VisualizationGraphWidget::keyReleaseEvent(QKeyEvent* event)
1072 {
1072 {
1073 switch (event->key())
1073 switch (event->key())
1074 {
1074 {
1075 case Qt::Key_Control:
1075 case Qt::Key_Control:
1076 event->accept();
1076 event->accept();
1077 break;
1077 break;
1078 case Qt::Key_Shift:
1078 case Qt::Key_Shift:
1079 event->accept();
1079 event->accept();
1080 break;
1080 break;
1081 default:
1081 default:
1082 QWidget::keyReleaseEvent(event);
1082 QWidget::keyReleaseEvent(event);
1083 break;
1083 break;
1084 }
1084 }
1085 setCursor(Qt::ArrowCursor);
1085 setCursor(Qt::ArrowCursor);
1086 // event->accept();
1086 // event->accept();
1087 }
1087 }
1088
1088
1089 void VisualizationGraphWidget::keyPressEvent(QKeyEvent* event)
1089 void VisualizationGraphWidget::keyPressEvent(QKeyEvent* event)
1090 {
1090 {
1091 switch (event->key())
1091 switch (event->key())
1092 {
1092 {
1093 case Qt::Key_Control:
1093 case Qt::Key_Control:
1094 setCursor(Qt::CrossCursor);
1094 setCursor(Qt::CrossCursor);
1095 break;
1095 break;
1096 case Qt::Key_Shift:
1096 case Qt::Key_Shift:
1097 break;
1097 break;
1098 case Qt::Key_M:
1098 case Qt::Key_M:
1099 impl->rescaleY();
1099 impl->rescaleY();
1100 impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
1100 impl->m_plot->replot(QCustomPlot::rpQueuedReplot);
1101 break;
1101 break;
1102 case Qt::Key_Left:
1102 case Qt::Key_Left:
1103 if (event->modifiers() != Qt::ControlModifier)
1103 if (event->modifiers() != Qt::ControlModifier)
1104 {
1104 {
1105 move(-0.1, Qt::Horizontal);
1105 move(-0.1, Qt::Horizontal);
1106 }
1106 }
1107 else
1107 else
1108 {
1108 {
1109 zoom(2, this->width() / 2, Qt::Horizontal);
1109 zoom(2, this->width() / 2, Qt::Horizontal);
1110 }
1110 }
1111 break;
1111 break;
1112 case Qt::Key_Right:
1112 case Qt::Key_Right:
1113 if (event->modifiers() != Qt::ControlModifier)
1113 if (event->modifiers() != Qt::ControlModifier)
1114 {
1114 {
1115 move(0.1, Qt::Horizontal);
1115 move(0.1, Qt::Horizontal);
1116 }
1116 }
1117 else
1117 else
1118 {
1118 {
1119 zoom(0.5, this->width() / 2, Qt::Horizontal);
1119 zoom(0.5, this->width() / 2, Qt::Horizontal);
1120 }
1120 }
1121 break;
1121 break;
1122 case Qt::Key_Up:
1122 case Qt::Key_Up:
1123 if (event->modifiers() != Qt::ControlModifier)
1123 if (event->modifiers() != Qt::ControlModifier)
1124 {
1124 {
1125 move(0.1, Qt::Vertical);
1125 move(0.1, Qt::Vertical);
1126 }
1126 }
1127 else
1127 else
1128 {
1128 {
1129 zoom(0.5, this->height() / 2, Qt::Vertical);
1129 zoom(0.5, this->height() / 2, Qt::Vertical);
1130 }
1130 }
1131 break;
1131 break;
1132 case Qt::Key_Down:
1132 case Qt::Key_Down:
1133 if (event->modifiers() != Qt::ControlModifier)
1133 if (event->modifiers() != Qt::ControlModifier)
1134 {
1134 {
1135 move(-0.1, Qt::Vertical);
1135 move(-0.1, Qt::Vertical);
1136 }
1136 }
1137 else
1137 else
1138 {
1138 {
1139 zoom(2, this->height() / 2, Qt::Vertical);
1139 zoom(2, this->height() / 2, Qt::Vertical);
1140 }
1140 }
1141 break;
1141 break;
1142 default:
1142 default:
1143 QWidget::keyPressEvent(event);
1143 QWidget::keyPressEvent(event);
1144 break;
1144 break;
1145 }
1145 }
1146 }
1146 }
1147
1147
1148 QCustomPlot& VisualizationGraphWidget::plot() const noexcept
1148 QCustomPlot& VisualizationGraphWidget::plot() const noexcept
1149 {
1149 {
1150 return *impl->m_plot;
1150 return *impl->m_plot;
1151 }
1151 }
1152
1152
1153 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint& pos) noexcept
1153 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint& pos) noexcept
1154 {
1154 {
1155 QMenu graphMenu {};
1155 QMenu graphMenu {};
1156
1156
1157 // Iterates on variables (unique keys)
1157 // Iterates on variables (unique keys)
1158 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
1158 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
1159 end = impl->m_VariableToPlotMultiMap.cend();
1159 end = impl->m_VariableToPlotMultiMap.cend();
1160 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first))
1160 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first))
1161 {
1161 {
1162 // 'Remove variable' action
1162 // 'Remove variable' action
1163 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
1163 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
1164 [this, var = it->first]() { removeVariable(var); });
1164 [this, var = it->first]() { removeVariable(var); });
1165 }
1165 }
1166
1166
1167 if (!impl->m_ZoomStack.isEmpty())
1167 if (!impl->m_ZoomStack.isEmpty())
1168 {
1168 {
1169 if (!graphMenu.isEmpty())
1169 if (!graphMenu.isEmpty())
1170 {
1170 {
1171 graphMenu.addSeparator();
1171 graphMenu.addSeparator();
1172 }
1172 }
1173
1173
1174 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
1174 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
1175 }
1175 }
1176
1176
1177 // Selection Zone Actions
1177 // Selection Zone Actions
1178 auto selectionZoneItem = impl->selectionZoneAt(pos);
1178 auto selectionZoneItem = impl->selectionZoneAt(pos);
1179 if (selectionZoneItem)
1179 if (selectionZoneItem)
1180 {
1180 {
1181 auto selectedItems = parentVisualizationWidget()->selectionZoneManager().selectedItems();
1181 auto selectedItems = parentVisualizationWidget()->selectionZoneManager().selectedItems();
1182 selectedItems.removeAll(selectionZoneItem);
1182 selectedItems.removeAll(selectionZoneItem);
1183 selectedItems.prepend(selectionZoneItem); // Put the current selection zone first
1183 selectedItems.prepend(selectionZoneItem); // Put the current selection zone first
1184
1184
1185 auto zoneActions = sqpApp->actionsGuiController().selectionZoneActions();
1185 auto zoneActions = sqpApp->actionsGuiController().selectionZoneActions();
1186 if (!zoneActions.isEmpty() && !graphMenu.isEmpty())
1186 if (!zoneActions.isEmpty() && !graphMenu.isEmpty())
1187 {
1187 {
1188 graphMenu.addSeparator();
1188 graphMenu.addSeparator();
1189 }
1189 }
1190
1190
1191 QHash<QString, QMenu*> subMenus;
1191 QHash<QString, QMenu*> subMenus;
1192 QHash<QString, bool> subMenusEnabled;
1192 QHash<QString, bool> subMenusEnabled;
1193 QHash<QString, FilteringAction*> filteredMenu;
1193 QHash<QString, FilteringAction*> filteredMenu;
1194
1194
1195 for (auto zoneAction : zoneActions)
1195 for (auto zoneAction : zoneActions)
1196 {
1196 {
1197
1197
1198 auto isEnabled = zoneAction->isEnabled(selectedItems);
1198 auto isEnabled = zoneAction->isEnabled(selectedItems);
1199
1199
1200 auto menu = &graphMenu;
1200 auto menu = &graphMenu;
1201 QString menuPath;
1201 QString menuPath;
1202 for (auto subMenuName : zoneAction->subMenuList())
1202 for (auto subMenuName : zoneAction->subMenuList())
1203 {
1203 {
1204 menuPath += '/';
1204 menuPath += '/';
1205 menuPath += subMenuName;
1205 menuPath += subMenuName;
1206
1206
1207 if (!subMenus.contains(menuPath))
1207 if (!subMenus.contains(menuPath))
1208 {
1208 {
1209 menu = menu->addMenu(subMenuName);
1209 menu = menu->addMenu(subMenuName);
1210 subMenus[menuPath] = menu;
1210 subMenus[menuPath] = menu;
1211 subMenusEnabled[menuPath] = isEnabled;
1211 subMenusEnabled[menuPath] = isEnabled;
1212 }
1212 }
1213 else
1213 else
1214 {
1214 {
1215 menu = subMenus.value(menuPath);
1215 menu = subMenus.value(menuPath);
1216 if (isEnabled)
1216 if (isEnabled)
1217 {
1217 {
1218 // The sub menu is enabled if at least one of its actions is enabled
1218 // The sub menu is enabled if at least one of its actions is enabled
1219 subMenusEnabled[menuPath] = true;
1219 subMenusEnabled[menuPath] = true;
1220 }
1220 }
1221 }
1221 }
1222 }
1222 }
1223
1223
1224 FilteringAction* filterAction = nullptr;
1224 FilteringAction* filterAction = nullptr;
1225 if (sqpApp->actionsGuiController().isMenuFiltered(zoneAction->subMenuList()))
1225 if (sqpApp->actionsGuiController().isMenuFiltered(zoneAction->subMenuList()))
1226 {
1226 {
1227 filterAction = filteredMenu.value(menuPath);
1227 filterAction = filteredMenu.value(menuPath);
1228 if (!filterAction)
1228 if (!filterAction)
1229 {
1229 {
1230 filterAction = new FilteringAction { this };
1230 filterAction = new FilteringAction { this };
1231 filteredMenu[menuPath] = filterAction;
1231 filteredMenu[menuPath] = filterAction;
1232 menu->addAction(filterAction);
1232 menu->addAction(filterAction);
1233 }
1233 }
1234 }
1234 }
1235
1235
1236 auto action = menu->addAction(zoneAction->name());
1236 auto action = menu->addAction(zoneAction->name());
1237 action->setEnabled(isEnabled);
1237 action->setEnabled(isEnabled);
1238 action->setShortcut(zoneAction->displayedShortcut());
1238 action->setShortcut(zoneAction->displayedShortcut());
1239 QObject::connect(action, &QAction::triggered,
1239 QObject::connect(action, &QAction::triggered,
1240 [zoneAction, selectedItems]() { zoneAction->execute(selectedItems); });
1240 [zoneAction, selectedItems]() { zoneAction->execute(selectedItems); });
1241
1241
1242 if (filterAction && zoneAction->isFilteringAllowed())
1242 if (filterAction && zoneAction->isFilteringAllowed())
1243 {
1243 {
1244 filterAction->addActionToFilter(action);
1244 filterAction->addActionToFilter(action);
1245 }
1245 }
1246 }
1246 }
1247
1247
1248 for (auto it = subMenus.cbegin(); it != subMenus.cend(); ++it)
1248 for (auto it = subMenus.cbegin(); it != subMenus.cend(); ++it)
1249 {
1249 {
1250 it.value()->setEnabled(subMenusEnabled[it.key()]);
1250 it.value()->setEnabled(subMenusEnabled[it.key()]);
1251 }
1251 }
1252 }
1252 }
1253
1253
1254 if (!graphMenu.isEmpty())
1254 if (!graphMenu.isEmpty())
1255 {
1255 {
1256 graphMenu.exec(QCursor::pos());
1256 graphMenu.exec(QCursor::pos());
1257 }
1257 }
1258 }
1258 }
1259
1259
1260 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent* event) noexcept
1260 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent* event) noexcept
1261 {
1261 {
1262 impl->m_RenderingDelegate->onMouseDoubleClick(event);
1262 impl->m_RenderingDelegate->onMouseDoubleClick(event);
1263 }
1263 }
1264
1264
1265 void VisualizationGraphWidget::onMouseMove(QMouseEvent* event) noexcept
1265 void VisualizationGraphWidget::onMouseMove(QMouseEvent* event) noexcept
1266 {
1266 {
1267 // Handles plot rendering when mouse is moving
1267 // Handles plot rendering when mouse is moving
1268 impl->m_RenderingDelegate->updateTooltip(event);
1268 impl->m_RenderingDelegate->updateTooltip(event);
1269
1269
1270 auto axisPos = impl->posToAxisPos(event->pos());
1270 auto axisPos = impl->posToAxisPos(event->pos());
1271
1271
1272 // Zoom box and zone drawing
1272 // Zoom box and zone drawing
1273 if (impl->m_DrawingZoomRect)
1273 if (impl->m_DrawingZoomRect)
1274 {
1274 {
1275 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
1275 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
1276 }
1276 }
1277 else if (impl->m_DrawingZone)
1277 else if (impl->m_DrawingZone)
1278 {
1278 {
1279 impl->m_DrawingZone->setEnd(axisPos.x());
1279 impl->m_DrawingZone->setEnd(axisPos.x());
1280 }
1280 }
1281
1281
1282 // Cursor
1282 // Cursor
1283 if (auto parentZone = parentZoneWidget())
1283 if (auto parentZone = parentZoneWidget())
1284 {
1284 {
1285 if (impl->pointIsInAxisRect(axisPos, plot()))
1285 if (impl->pointIsInAxisRect(axisPos, plot()))
1286 {
1286 {
1287 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
1287 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
1288 }
1288 }
1289 else
1289 else
1290 {
1290 {
1291 parentZone->notifyMouseLeaveGraph(this);
1291 parentZone->notifyMouseLeaveGraph(this);
1292 }
1292 }
1293 }
1293 }
1294
1294
1295 // Search for the selection zone under the mouse
1295 // Search for the selection zone under the mouse
1296 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos());
1296 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos());
1297 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
1297 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
1298 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones)
1298 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones)
1299 {
1299 {
1300
1300
1301 // Sets the appropriate cursor shape
1301 // Sets the appropriate cursor shape
1302 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
1302 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
1303 setCursor(cursorShape);
1303 setCursor(cursorShape);
1304
1304
1305 // Manages the hovered zone
1305 // Manages the hovered zone
1306 if (selectionZoneItemUnderCursor != impl->m_HoveredZone)
1306 if (selectionZoneItemUnderCursor != impl->m_HoveredZone)
1307 {
1307 {
1308 if (impl->m_HoveredZone)
1308 if (impl->m_HoveredZone)
1309 {
1309 {
1310 impl->m_HoveredZone->setHovered(false);
1310 impl->m_HoveredZone->setHovered(false);
1311 }
1311 }
1312 selectionZoneItemUnderCursor->setHovered(true);
1312 selectionZoneItemUnderCursor->setHovered(true);
1313 impl->m_HoveredZone = selectionZoneItemUnderCursor;
1313 impl->m_HoveredZone = selectionZoneItemUnderCursor;
1314 plot().replot(QCustomPlot::rpQueuedReplot);
1314 plot().replot(QCustomPlot::rpQueuedReplot);
1315 }
1315 }
1316 }
1316 }
1317 else
1317 else
1318 {
1318 {
1319 // There is no zone under the mouse or the interaction mode is not "selection zones"
1319 // There is no zone under the mouse or the interaction mode is not "selection zones"
1320 if (impl->m_HoveredZone)
1320 if (impl->m_HoveredZone)
1321 {
1321 {
1322 impl->m_HoveredZone->setHovered(false);
1322 impl->m_HoveredZone->setHovered(false);
1323 impl->m_HoveredZone = nullptr;
1323 impl->m_HoveredZone = nullptr;
1324 }
1324 }
1325
1325
1326 setCursor(Qt::ArrowCursor);
1326 setCursor(Qt::ArrowCursor);
1327 }
1327 }
1328
1328
1329 impl->m_HasMovedMouse = true;
1329 impl->m_HasMovedMouse = true;
1330 VisualizationDragWidget::mouseMoveEvent(event);
1330 VisualizationDragWidget::mouseMoveEvent(event);
1331 }
1331 }
1332
1332
1333 void VisualizationGraphWidget::onMouseWheel(QWheelEvent* event) noexcept
1333 void VisualizationGraphWidget::onMouseWheel(QWheelEvent* event) noexcept
1334 {
1334 {
1335 // Processes event only if the wheel occurs on axis rect
1335 // Processes event only if the wheel occurs on axis rect
1336 if (!dynamic_cast<QCPAxisRect*>(impl->m_plot->layoutElementAt(event->posF())))
1336 if (!dynamic_cast<QCPAxisRect*>(impl->m_plot->layoutElementAt(event->posF())))
1337 {
1337 {
1338 return;
1338 return;
1339 }
1339 }
1340
1340
1341 auto value = event->angleDelta().x() + event->angleDelta().y();
1341 auto value = event->angleDelta().x() + event->angleDelta().y();
1342 if (value != 0)
1342 if (value != 0)
1343 {
1343 {
1344
1344
1345 auto direction = value > 0 ? 1.0 : -1.0;
1345 auto direction = value > 0 ? 1.0 : -1.0;
1346 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
1346 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
1347 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
1347 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
1348 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
1348 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
1349
1349
1350 auto zoomOrientations = QFlags<Qt::Orientation> {};
1350 auto zoomOrientations = QFlags<Qt::Orientation> {};
1351 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
1351 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
1352 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
1352 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
1353
1353
1354 impl->m_plot->axisRect()->setRangeZoom(zoomOrientations);
1354 impl->m_plot->axisRect()->setRangeZoom(zoomOrientations);
1355
1355
1356 if (!isZoomX && !isZoomY)
1356 if (!isZoomX && !isZoomY)
1357 {
1357 {
1358 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
1358 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
1359 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
1359 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
1360
1360
1361 axis->setRange(axis->range() + diff);
1361 axis->setRange(axis->range() + diff);
1362
1362
1363 if (plot().noAntialiasingOnDrag())
1363 if (plot().noAntialiasingOnDrag())
1364 {
1364 {
1365 plot().setNotAntialiasedElements(QCP::aeAll);
1365 plot().setNotAntialiasedElements(QCP::aeAll);
1366 }
1366 }
1367
1367
1368 // plot().replot(QCustomPlot::rpQueuedReplot);
1368 // plot().replot(QCustomPlot::rpQueuedReplot);
1369 }
1369 }
1370 }
1370 }
1371 }
1371 }
1372
1372
1373 void VisualizationGraphWidget::onMousePress(QMouseEvent* event) noexcept
1373 void VisualizationGraphWidget::onMousePress(QMouseEvent* event) noexcept
1374 {
1374 {
1375 auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
1375 auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
1376 auto isSelectionZoneMode
1376 auto isSelectionZoneMode
1377 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
1377 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
1378 auto isLeftClick = event->buttons().testFlag(Qt::LeftButton);
1378 auto isLeftClick = event->buttons().testFlag(Qt::LeftButton);
1379
1379
1380 if (!isDragDropClick && isLeftClick)
1380 if (!isDragDropClick && isLeftClick)
1381 {
1381 {
1382 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox)
1382 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox)
1383 {
1383 {
1384 // Starts a zoom box
1384 // Starts a zoom box
1385 impl->startDrawingRect(event->pos());
1385 impl->startDrawingRect(event->pos());
1386 }
1386 }
1387 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr)
1387 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr)
1388 {
1388 {
1389 // Starts a new selection zone
1389 // Starts a new selection zone
1390 auto zoneAtPos = impl->selectionZoneAt(event->pos());
1390 auto zoneAtPos = impl->selectionZoneAt(event->pos());
1391 if (!zoneAtPos)
1391 if (!zoneAtPos)
1392 {
1392 {
1393 impl->startDrawingZone(event->pos());
1393 impl->startDrawingZone(event->pos());
1394 }
1394 }
1395 }
1395 }
1396 }
1396 }
1397
1397
1398
1398
1399 // Allows zone edition only in selection zone mode without drag&drop
1399 // Allows zone edition only in selection zone mode without drag&drop
1400 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
1400 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
1401
1401
1402 // Selection / Deselection
1402 // Selection / Deselection
1403 if (isSelectionZoneMode)
1403 if (isSelectionZoneMode)
1404 {
1404 {
1405 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
1405 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
1406 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos());
1406 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos());
1407
1407
1408
1408
1409 if (selectionZoneItemUnderCursor && !selectionZoneItemUnderCursor->selected()
1409 if (selectionZoneItemUnderCursor && !selectionZoneItemUnderCursor->selected()
1410 && !isMultiSelectionClick)
1410 && !isMultiSelectionClick)
1411 {
1411 {
1412 parentVisualizationWidget()->selectionZoneManager().select(
1412 parentVisualizationWidget()->selectionZoneManager().select(
1413 { selectionZoneItemUnderCursor });
1413 { selectionZoneItemUnderCursor });
1414 }
1414 }
1415 else if (!selectionZoneItemUnderCursor && !isMultiSelectionClick && isLeftClick)
1415 else if (!selectionZoneItemUnderCursor && !isMultiSelectionClick && isLeftClick)
1416 {
1416 {
1417 parentVisualizationWidget()->selectionZoneManager().clearSelection();
1417 parentVisualizationWidget()->selectionZoneManager().clearSelection();
1418 }
1418 }
1419 else
1419 else
1420 {
1420 {
1421 // No selection change
1421 // No selection change
1422 }
1422 }
1423
1423
1424 if (selectionZoneItemUnderCursor && isLeftClick)
1424 if (selectionZoneItemUnderCursor && isLeftClick)
1425 {
1425 {
1426 selectionZoneItemUnderCursor->setAssociatedEditedZones(
1426 selectionZoneItemUnderCursor->setAssociatedEditedZones(
1427 parentVisualizationWidget()->selectionZoneManager().selectedItems());
1427 parentVisualizationWidget()->selectionZoneManager().selectedItems());
1428 }
1428 }
1429 }
1429 }
1430
1430
1431
1431
1432 impl->m_HasMovedMouse = false;
1432 impl->m_HasMovedMouse = false;
1433 VisualizationDragWidget::mousePressEvent(event);
1433 VisualizationDragWidget::mousePressEvent(event);
1434 }
1434 }
1435
1435
1436 void VisualizationGraphWidget::onMouseRelease(QMouseEvent* event) noexcept
1436 void VisualizationGraphWidget::onMouseRelease(QMouseEvent* event) noexcept
1437 {
1437 {
1438 if (impl->m_DrawingZoomRect)
1438 if (impl->m_DrawingZoomRect)
1439 {
1439 {
1440
1440
1441 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
1441 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
1442 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
1442 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
1443
1443
1444 auto newAxisXRange = QCPRange { impl->m_DrawingZoomRect->topLeft->coords().x(),
1444 auto newAxisXRange = QCPRange { impl->m_DrawingZoomRect->topLeft->coords().x(),
1445 impl->m_DrawingZoomRect->bottomRight->coords().x() };
1445 impl->m_DrawingZoomRect->bottomRight->coords().x() };
1446
1446
1447 auto newAxisYRange = QCPRange { impl->m_DrawingZoomRect->topLeft->coords().y(),
1447 auto newAxisYRange = QCPRange { impl->m_DrawingZoomRect->topLeft->coords().y(),
1448 impl->m_DrawingZoomRect->bottomRight->coords().y() };
1448 impl->m_DrawingZoomRect->bottomRight->coords().y() };
1449
1449
1450 impl->removeDrawingRect();
1450 impl->removeDrawingRect();
1451
1451
1452 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
1452 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
1453 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0))
1453 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0))
1454 {
1454 {
1455 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
1455 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
1456 axisX->setRange(newAxisXRange);
1456 axisX->setRange(newAxisXRange);
1457 axisY->setRange(newAxisYRange);
1457 axisY->setRange(newAxisYRange);
1458
1458
1459 plot().replot(QCustomPlot::rpQueuedReplot);
1459 plot().replot(QCustomPlot::rpQueuedReplot);
1460 }
1460 }
1461 }
1461 }
1462
1462
1463 impl->endDrawingZone();
1463 impl->endDrawingZone();
1464
1464
1465 // Selection / Deselection
1465 // Selection / Deselection
1466 auto isSelectionZoneMode
1466 auto isSelectionZoneMode
1467 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
1467 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
1468 if (isSelectionZoneMode)
1468 if (isSelectionZoneMode)
1469 {
1469 {
1470 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
1470 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
1471 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos());
1471 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos());
1472 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton
1472 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton
1473 && !impl->m_HasMovedMouse)
1473 && !impl->m_HasMovedMouse)
1474 {
1474 {
1475
1475
1476 auto zonesUnderCursor = impl->selectionZonesAt(event->pos(), plot());
1476 auto zonesUnderCursor = impl->selectionZonesAt(event->pos(), plot());
1477 if (zonesUnderCursor.count() > 1)
1477 if (zonesUnderCursor.count() > 1)
1478 {
1478 {
1479 // There are multiple zones under the mouse.
1479 // There are multiple zones under the mouse.
1480 // Performs the selection with a selection dialog.
1480 // Performs the selection with a selection dialog.
1481 VisualizationMultiZoneSelectionDialog dialog { this };
1481 VisualizationMultiZoneSelectionDialog dialog { this };
1482 dialog.setZones(zonesUnderCursor);
1482 dialog.setZones(zonesUnderCursor);
1483 dialog.move(mapToGlobal(event->pos() - QPoint(dialog.width() / 2, 20)));
1483 dialog.move(mapToGlobal(event->pos() - QPoint(dialog.width() / 2, 20)));
1484 dialog.activateWindow();
1484 dialog.activateWindow();
1485 dialog.raise();
1485 dialog.raise();
1486 if (dialog.exec() == QDialog::Accepted)
1486 if (dialog.exec() == QDialog::Accepted)
1487 {
1487 {
1488 auto selection = dialog.selectedZones();
1488 auto selection = dialog.selectedZones();
1489
1489
1490 if (!isMultiSelectionClick)
1490 if (!isMultiSelectionClick)
1491 {
1491 {
1492 parentVisualizationWidget()->selectionZoneManager().clearSelection();
1492 parentVisualizationWidget()->selectionZoneManager().clearSelection();
1493 }
1493 }
1494
1494
1495 for (auto it = selection.cbegin(); it != selection.cend(); ++it)
1495 for (auto it = selection.cbegin(); it != selection.cend(); ++it)
1496 {
1496 {
1497 auto zone = it.key();
1497 auto zone = it.key();
1498 auto isSelected = it.value();
1498 auto isSelected = it.value();
1499 parentVisualizationWidget()->selectionZoneManager().setSelected(
1499 parentVisualizationWidget()->selectionZoneManager().setSelected(
1500 zone, isSelected);
1500 zone, isSelected);
1501
1501
1502 if (isSelected)
1502 if (isSelected)
1503 {
1503 {
1504 // Puts the zone on top of the stack so it can be moved or resized
1504 // Puts the zone on top of the stack so it can be moved or resized
1505 impl->moveSelectionZoneOnTop(zone, plot());
1505 impl->moveSelectionZoneOnTop(zone, plot());
1506 }
1506 }
1507 }
1507 }
1508 }
1508 }
1509 }
1509 }
1510 else
1510 else
1511 {
1511 {
1512 if (!isMultiSelectionClick)
1512 if (!isMultiSelectionClick)
1513 {
1513 {
1514 parentVisualizationWidget()->selectionZoneManager().select(
1514 parentVisualizationWidget()->selectionZoneManager().select(
1515 { selectionZoneItemUnderCursor });
1515 { selectionZoneItemUnderCursor });
1516 impl->moveSelectionZoneOnTop(selectionZoneItemUnderCursor, plot());
1516 impl->moveSelectionZoneOnTop(selectionZoneItemUnderCursor, plot());
1517 }
1517 }
1518 else
1518 else
1519 {
1519 {
1520 parentVisualizationWidget()->selectionZoneManager().setSelected(
1520 parentVisualizationWidget()->selectionZoneManager().setSelected(
1521 selectionZoneItemUnderCursor,
1521 selectionZoneItemUnderCursor,
1522 !selectionZoneItemUnderCursor->selected()
1522 !selectionZoneItemUnderCursor->selected()
1523 || event->button() == Qt::RightButton);
1523 || event->button() == Qt::RightButton);
1524 }
1524 }
1525 }
1525 }
1526 }
1526 }
1527 else
1527 else
1528 {
1528 {
1529 // No selection change
1529 // No selection change
1530 }
1530 }
1531 }
1531 }
1532 }
1532 }
1533
1533
1534 void VisualizationGraphWidget::onDataCacheVariableUpdated()
1534 void VisualizationGraphWidget::onDataCacheVariableUpdated()
1535 {
1535 {
1536 auto graphRange = impl->m_plot->xAxis->range();
1536 auto graphRange = impl->m_plot->xAxis->range();
1537 auto dateTime = DateTimeRange { graphRange.lower, graphRange.upper };
1537 auto dateTime = DateTimeRange { graphRange.lower, graphRange.upper };
1538
1538
1539 for (auto& variableEntry : impl->m_VariableToPlotMultiMap)
1539 for (auto& variableEntry : impl->m_VariableToPlotMultiMap)
1540 {
1540 {
1541 auto variable = variableEntry.first;
1541 auto variable = variableEntry.first;
1542 qCDebug(LOG_VisualizationGraphWidget())
1542 qCDebug(LOG_VisualizationGraphWidget())
1543 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
1543 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
1544 qCDebug(LOG_VisualizationGraphWidget())
1544 qCDebug(LOG_VisualizationGraphWidget())
1545 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
1545 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
1546 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range()))
1546 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range()))
1547 {
1547 {
1548 impl->updateData(variableEntry.second, variable, variable->range());
1548 impl->updateData(variableEntry.second, variable, variable->range());
1549 }
1549 }
1550 }
1550 }
1551 }
1551 }
1552
1552
1553 void VisualizationGraphWidget::onUpdateVarDisplaying(
1553 void VisualizationGraphWidget::onUpdateVarDisplaying(
1554 std::shared_ptr<Variable2> variable, const DateTimeRange& range)
1554 std::shared_ptr<Variable2> variable, const DateTimeRange& range)
1555 {
1555 {
1556 auto it = impl->m_VariableToPlotMultiMap.find(variable);
1556 auto it = impl->m_VariableToPlotMultiMap.find(variable);
1557 if (it != impl->m_VariableToPlotMultiMap.end())
1557 if (it != impl->m_VariableToPlotMultiMap.end())
1558 {
1558 {
1559 impl->updateData(it->second, variable, range);
1559 impl->updateData(it->second, variable, range);
1560 }
1560 }
1561 }
1561 }
1562
1562
1563 void VisualizationGraphWidget::variableUpdated(QUuid id)
1563 void VisualizationGraphWidget::variableUpdated(QUuid id)
1564 {
1564 {
1565 for (auto& [var, plotables] : impl->m_VariableToPlotMultiMap)
1565 for (auto& [var, plotables] : impl->m_VariableToPlotMultiMap)
1566 {
1566 {
1567 if (var->ID() == id)
1567 if (var->ID() == id)
1568 {
1568 {
1569 impl->updateData(plotables, var, this->graphRange());
1569 impl->updateData(plotables, var, this->graphRange());
1570 }
1570 }
1571 }
1571 }
1572 }
1572 }
1573
1573
1574 void VisualizationGraphWidget::variableDeleted(const std::shared_ptr<Variable2>& variable)
1574 void VisualizationGraphWidget::variableDeleted(const std::shared_ptr<Variable2>& variable)
1575 {
1575 {
1576 this->removeVariable(variable);
1576 this->removeVariable(variable);
1577 }
1577 }
General Comments 0
You need to be logged in to leave comments. Login now