##// END OF EJS Templates
LineEdit to filter the create catalogue menu
trabillard -
r1382:eb278710ae3b
parent child
Show More
@@ -0,0 +1,19
1 #ifndef SCIQLOP_FILTERINGACTION_H
2 #define SCIQLOP_FILTERINGACTION_H
3
4 #include <Common/spimpl.h>
5 #include <QWidgetAction>
6
7 /// A LineEdit inside an action which is able to filter other actions
8 class FilteringAction : public QWidgetAction {
9 public:
10 FilteringAction(QWidget *parent);
11
12 void addActionToFilter(QAction *action);
13
14 private:
15 class FilteringActionPrivate;
16 spimpl::unique_impl_ptr<FilteringActionPrivate> impl;
17 };
18
19 #endif // SCIQLOP_FILTERINGACTION_H
@@ -0,0 +1,27
1 #include "Actions/FilteringAction.h"
2
3 #include <QLineEdit>
4
5 struct FilteringAction::FilteringActionPrivate {
6 QLineEdit *m_FilterLineEdit;
7 QVector<QAction *> m_FilteredActions;
8 };
9
10 FilteringAction::FilteringAction(QWidget *parent)
11 : QWidgetAction(parent), impl{spimpl::make_unique_impl<FilteringActionPrivate>()}
12 {
13 impl->m_FilterLineEdit = new QLineEdit(parent);
14 setDefaultWidget(impl->m_FilterLineEdit);
15
16 connect(impl->m_FilterLineEdit, &QLineEdit::textEdited, [this](auto text) {
17 for (auto action : impl->m_FilteredActions) {
18 auto match = action->text().contains(text, Qt::CaseInsensitive);
19 action->setVisible(match);
20 }
21 });
22 }
23
24 void FilteringAction::addActionToFilter(QAction *action)
25 {
26 impl->m_FilteredActions << action;
27 }
@@ -1,29 +1,35
1 1 #ifndef SCIQLOP_ACTIONSGUICONTROLLER_H
2 2 #define SCIQLOP_ACTIONSGUICONTROLLER_H
3 3
4 4 #include <Actions/SelectionZoneAction.h>
5 5 #include <Common/spimpl.h>
6 6
7 7 #include <memory>
8 8
9 9 class ActionsGuiController {
10 10 public:
11 11 ActionsGuiController();
12 12
13 13 std::shared_ptr<SelectionZoneAction>
14 14 addSectionZoneAction(const QString &name, SelectionZoneAction::ExecuteFunction function);
15 15
16 16 std::shared_ptr<SelectionZoneAction>
17 17 addSectionZoneAction(const QStringList &subMenuList, const QString &name,
18 18 SelectionZoneAction::ExecuteFunction function);
19 19
20 20 QVector<std::shared_ptr<SelectionZoneAction> > selectionZoneActions() const;
21 21
22 22 void removeAction(const std::shared_ptr<SelectionZoneAction> &action);
23 23
24 /// Sets a flag to say that the specified menu can be filtered, usually via a FilteringAction
25 void addFilterForMenu(const QStringList &menuPath);
26
27 /// Returns true if the menu can be filtered
28 bool isMenuFiltered(const QStringList &menuPath) const;
29
24 30 private:
25 31 class ActionsGuiControllerPrivate;
26 32 spimpl::unique_impl_ptr<ActionsGuiControllerPrivate> impl;
27 33 };
28 34
29 35 #endif // SCIQLOP_ACTIONSGUICONTROLLER_H
@@ -1,75 +1,82
1 1 #ifndef SCIQLOP_SELECTIONZONEACTION_H
2 2 #define SCIQLOP_SELECTIONZONEACTION_H
3 3
4 4 #include <Common/spimpl.h>
5 5
6 6 #include <QLoggingCategory>
7 7 #include <QObject>
8 8
9 9 #include <functional>
10 10
11 11 class VisualizationSelectionZoneItem;
12 12
13 13 Q_DECLARE_LOGGING_CATEGORY(LOG_SelectionZoneAction)
14 14
15 15 /**
16 16 * @brief The SelectionZoneAction class represents an action on a selection zone in the
17 17 * visualization.
18 18 *
19 19 * The action is a function that will be executed when the slot execute() is called.
20 20 */
21 21 class SelectionZoneAction : public QObject {
22 22
23 23 Q_OBJECT
24 24
25 25 public:
26 26 /// Signature of the function associated to the action
27 27 using ExecuteFunction
28 28 = std::function<void(const QVector<VisualizationSelectionZoneItem *> &item)>;
29 29
30 30 using EnableFunction
31 31 = std::function<bool(const QVector<VisualizationSelectionZoneItem *> &item)>;
32 32
33 33 /**
34 34 * @param name the name of the action, displayed to the user
35 35 * @param fun the function that will be called when the action is executed
36 36 * @sa execute()
37 37 */
38 38 explicit SelectionZoneAction(const QString &name, ExecuteFunction fun);
39 39
40 40 /**
41 41 * @param name the name of the action, displayed to the user
42 42 * @param subMenusList the list of sub menus where the action should be inserted
43 43 * @param fun the function that will be called when the action is executed
44 44 * @sa execute()
45 45 */
46 46 explicit SelectionZoneAction(const QStringList &subMenuList, const QString &name,
47 47 ExecuteFunction fun);
48 48
49 49 /// Sets the function which determine if the action should be enabled or disabled
50 50 void setEnableFunction(EnableFunction fun);
51 51
52 52 /// Sets the shortcut displayed by the action.
53 53 /// Note: The shortcut is only displayed and not active because it is not permanently stored
54 54 void setDisplayedShortcut(const QKeySequence &shortcut);
55 55 QKeySequence displayedShortcut() const;
56 56
57 57 /// The name of the action
58 58 QString name() const noexcept;
59 59
60 60 /// The path in the sub menus, if any
61 61 QStringList subMenuList() const noexcept;
62 62
63 /// Sets if filtering the action is allowed via a FilteringAction
64 void setAllowedFiltering(bool value);
65
66 /// Returns true if filtering the action is allowed via a FilteringAction. By default it is
67 /// allowed.
68 bool isFilteringAllowed() const;
69
63 70 public slots:
64 71 /// Executes the action
65 72 void execute(const QVector<VisualizationSelectionZoneItem *> &item);
66 73
67 74 /// Returns true if the action is enabled
68 75 bool isEnabled(const QVector<VisualizationSelectionZoneItem *> &item);
69 76
70 77 private:
71 78 class SelectionZoneActionPrivate;
72 79 spimpl::unique_impl_ptr<SelectionZoneActionPrivate> impl;
73 80 };
74 81
75 82 #endif // SCIQLOP_SELECTIONZONEACTION_H
@@ -1,145 +1,147
1 1 qxorm_dep = dependency('QxOrm', required : true, fallback:['QxOrm','qxorm_dep'])
2 2 catalogueapi_dep = dependency('CatalogueAPI', required : true, fallback:['CatalogueAPI','CatalogueAPI_dep'])
3 3
4 4 gui_moc_headers = [
5 5 'include/DataSource/DataSourceWidget.h',
6 6 'include/Settings/SqpSettingsDialog.h',
7 7 'include/Settings/SqpSettingsGeneralWidget.h',
8 8 'include/SidePane/SqpSidePane.h',
9 9 'include/SqpApplication.h',
10 10 'include/DragAndDrop/DragDropScroller.h',
11 11 'include/DragAndDrop/DragDropTabSwitcher.h',
12 12 'include/TimeWidget/TimeWidget.h',
13 13 'include/Variable/VariableInspectorWidget.h',
14 14 'include/Variable/RenameVariableDialog.h',
15 15 'include/Visualization/qcustomplot.h',
16 16 'include/Visualization/VisualizationGraphWidget.h',
17 17 'include/Visualization/VisualizationTabWidget.h',
18 18 'include/Visualization/VisualizationWidget.h',
19 19 'include/Visualization/VisualizationZoneWidget.h',
20 20 'include/Visualization/VisualizationDragDropContainer.h',
21 21 'include/Visualization/VisualizationDragWidget.h',
22 22 'include/Visualization/ColorScaleEditor.h',
23 23 'include/Visualization/VisualizationSelectionZoneItem.h',
24 24 'include/Actions/SelectionZoneAction.h',
25 25 'include/Visualization/VisualizationMultiZoneSelectionDialog.h',
26 26 'include/Catalogue/CatalogueExplorer.h',
27 27 'include/Catalogue/CatalogueEventsWidget.h',
28 28 'include/Catalogue/CatalogueSideBarWidget.h',
29 29 'include/Catalogue/CatalogueInspectorWidget.h',
30 30 'include/Catalogue/CatalogueEventsModel.h',
31 'include/Catalogue/CatalogueTreeModel.h'
31 'include/Catalogue/CatalogueTreeModel.h',
32 'include/Actions/FilteringAction.h'
32 33 ]
33 34
34 35 gui_ui_files = [
35 36 'ui/DataSource/DataSourceWidget.ui',
36 37 'ui/Settings/SqpSettingsDialog.ui',
37 38 'ui/Settings/SqpSettingsGeneralWidget.ui',
38 39 'ui/SidePane/SqpSidePane.ui',
39 40 'ui/TimeWidget/TimeWidget.ui',
40 41 'ui/Variable/VariableInspectorWidget.ui',
41 42 'ui/Variable/RenameVariableDialog.ui',
42 43 'ui/Variable/VariableMenuHeaderWidget.ui',
43 44 'ui/Visualization/VisualizationGraphWidget.ui',
44 45 'ui/Visualization/VisualizationTabWidget.ui',
45 46 'ui/Visualization/VisualizationWidget.ui',
46 47 'ui/Visualization/VisualizationZoneWidget.ui',
47 48 'ui/Visualization/ColorScaleEditor.ui',
48 49 'ui/Visualization/VisualizationMultiZoneSelectionDialog.ui',
49 50 'ui/Catalogue/CatalogueExplorer.ui',
50 51 'ui/Catalogue/CatalogueEventsWidget.ui',
51 52 'ui/Catalogue/CatalogueSideBarWidget.ui',
52 53 'ui/Catalogue/CatalogueInspectorWidget.ui'
53 54 ]
54 55
55 56 gui_qresources = ['resources/sqpguiresources.qrc']
56 57
57 58 rcc_gen = generator(rcc,
58 59 output : 'qrc_@BASENAME@.cpp',
59 60 arguments : [
60 61 '--output',
61 62 '@OUTPUT@',
62 63 '@INPUT@',
63 64 '@EXTRA_ARGS@'])
64 65
65 66 rcc_files = rcc_gen.process(gui_qresources, extra_args : ['-name', 'sqpguiresources'])
66 67
67 68 gui_moc_files = qt5.preprocess(moc_headers : gui_moc_headers,
68 69 ui_files : gui_ui_files)
69 70
70 71 gui_sources = [
71 72 'src/SqpApplication.cpp',
72 73 'src/DragAndDrop/DragDropGuiController.cpp',
73 74 'src/DragAndDrop/DragDropScroller.cpp',
74 75 'src/DragAndDrop/DragDropTabSwitcher.cpp',
75 76 'src/Common/ColorUtils.cpp',
76 77 'src/Common/VisualizationDef.cpp',
77 78 'src/DataSource/DataSourceTreeWidgetItem.cpp',
78 79 'src/DataSource/DataSourceTreeWidgetHelper.cpp',
79 80 'src/DataSource/DataSourceWidget.cpp',
80 81 'src/DataSource/DataSourceTreeWidget.cpp',
81 82 'src/Settings/SqpSettingsDialog.cpp',
82 83 'src/Settings/SqpSettingsGeneralWidget.cpp',
83 84 'src/SidePane/SqpSidePane.cpp',
84 85 'src/TimeWidget/TimeWidget.cpp',
85 86 'src/Variable/VariableInspectorWidget.cpp',
86 87 'src/Variable/VariableInspectorTableView.cpp',
87 88 'src/Variable/VariableMenuHeaderWidget.cpp',
88 89 'src/Variable/RenameVariableDialog.cpp',
89 90 'src/Visualization/VisualizationGraphHelper.cpp',
90 91 'src/Visualization/VisualizationGraphRenderingDelegate.cpp',
91 92 'src/Visualization/VisualizationGraphWidget.cpp',
92 93 'src/Visualization/VisualizationTabWidget.cpp',
93 94 'src/Visualization/VisualizationWidget.cpp',
94 95 'src/Visualization/VisualizationZoneWidget.cpp',
95 96 'src/Visualization/qcustomplot.cpp',
96 97 'src/Visualization/QCustomPlotSynchronizer.cpp',
97 98 'src/Visualization/operations/FindVariableOperation.cpp',
98 99 'src/Visualization/operations/GenerateVariableMenuOperation.cpp',
99 100 'src/Visualization/operations/MenuBuilder.cpp',
100 101 'src/Visualization/operations/RemoveVariableOperation.cpp',
101 102 'src/Visualization/operations/RescaleAxeOperation.cpp',
102 103 'src/Visualization/VisualizationDragDropContainer.cpp',
103 104 'src/Visualization/VisualizationDragWidget.cpp',
104 105 'src/Visualization/AxisRenderingUtils.cpp',
105 106 'src/Visualization/PlottablesRenderingUtils.cpp',
106 107 'src/Visualization/MacScrollBarStyle.cpp',
107 108 'src/Visualization/VisualizationCursorItem.cpp',
108 109 'src/Visualization/ColorScaleEditor.cpp',
109 110 'src/Visualization/SqpColorScale.cpp',
110 111 'src/Visualization/QCPColorMapIterator.cpp',
111 112 'src/Visualization/VisualizationSelectionZoneItem.cpp',
112 113 'src/Visualization/VisualizationSelectionZoneManager.cpp',
113 114 'src/Actions/SelectionZoneAction.cpp',
114 115 'src/Actions/ActionsGuiController.cpp',
116 'src/Actions/FilteringAction.cpp',
115 117 'src/Visualization/VisualizationActionManager.cpp',
116 118 'src/Visualization/VisualizationMultiZoneSelectionDialog.cpp',
117 119 'src/Catalogue/CatalogueExplorer.cpp',
118 120 'src/Catalogue/CatalogueEventsWidget.cpp',
119 121 'src/Catalogue/CatalogueSideBarWidget.cpp',
120 122 'src/Catalogue/CatalogueInspectorWidget.cpp',
121 123 'src/Catalogue/CatalogueTreeItems/CatalogueAbstractTreeItem.cpp',
122 124 'src/Catalogue/CatalogueTreeItems/CatalogueTreeItem.cpp',
123 125 'src/Catalogue/CatalogueTreeItems/CatalogueTextTreeItem.cpp',
124 126 'src/Catalogue/CatalogueEventsModel.cpp',
125 127 'src/Catalogue/CatalogueExplorerHelper.cpp',
126 128 'src/Catalogue/CatalogueActionManager.cpp',
127 129 'src/Catalogue/CatalogueTreeModel.cpp'
128 130 ]
129 131
130 132 gui_inc = include_directories(['include'])
131 133
132 134 sciqlop_gui_lib = library('sciqlopgui',
133 135 gui_sources,
134 136 gui_moc_files,
135 137 rcc_files,
136 138 include_directories : [gui_inc],
137 139 dependencies : [ qt5printsupport, qt5gui, qt5widgets, qt5svg, sciqlop_core, catalogueapi_dep],
138 140 install : true
139 141 )
140 142
141 143 sciqlop_gui = declare_dependency(link_with : sciqlop_gui_lib,
142 144 include_directories : gui_inc,
143 145 dependencies : [qt5printsupport, qt5gui, qt5widgets, qt5svg, sciqlop_core, catalogueapi_dep])
144 146
145 147
@@ -1,41 +1,52
1 1 #include "Actions/ActionsGuiController.h"
2 2
3 3 struct ActionsGuiController::ActionsGuiControllerPrivate {
4 4
5 5 QVector<std::shared_ptr<SelectionZoneAction> > m_SelectionZoneActions;
6 QSet<QStringList> m_FilteredMenu;
6 7 };
7 8
8 9 ActionsGuiController::ActionsGuiController()
9 10 : impl{spimpl::make_unique_impl<ActionsGuiControllerPrivate>()}
10 11 {
11 12 }
12 13
13 14 std::shared_ptr<SelectionZoneAction>
14 15 ActionsGuiController::addSectionZoneAction(const QString &name,
15 16 SelectionZoneAction::ExecuteFunction function)
16 17 {
17 18 auto action = std::make_shared<SelectionZoneAction>(name, function);
18 19 impl->m_SelectionZoneActions.push_back(action);
19 20
20 21 return action;
21 22 }
22 23
23 24 std::shared_ptr<SelectionZoneAction>
24 25 ActionsGuiController::addSectionZoneAction(const QStringList &subMenuList, const QString &name,
25 26 SelectionZoneAction::ExecuteFunction function)
26 27 {
27 28 auto action = std::make_shared<SelectionZoneAction>(subMenuList, name, function);
28 29 impl->m_SelectionZoneActions.push_back(action);
29 30
30 31 return action;
31 32 }
32 33
33 34 QVector<std::shared_ptr<SelectionZoneAction> > ActionsGuiController::selectionZoneActions() const
34 35 {
35 36 return impl->m_SelectionZoneActions;
36 37 }
37 38
38 39 void ActionsGuiController::removeAction(const std::shared_ptr<SelectionZoneAction> &action)
39 40 {
40 41 impl->m_SelectionZoneActions.removeAll(action);
41 42 }
43
44 void ActionsGuiController::addFilterForMenu(const QStringList &menuPath)
45 {
46 impl->m_FilteredMenu.insert(menuPath);
47 }
48
49 bool ActionsGuiController::isMenuFiltered(const QStringList &menuPath) const
50 {
51 return impl->m_FilteredMenu.contains(menuPath);
52 }
@@ -1,66 +1,77
1 1 #include <Actions/SelectionZoneAction.h>
2 2 #include <Visualization/VisualizationSelectionZoneItem.h>
3 3
4 4 Q_LOGGING_CATEGORY(LOG_SelectionZoneAction, "SelectionZoneAction")
5 5
6 6 struct SelectionZoneAction::SelectionZoneActionPrivate {
7 7 explicit SelectionZoneActionPrivate(const QString &name, const QStringList &subMenuList,
8 8 SelectionZoneAction::ExecuteFunction fun)
9 9 : m_Name{name}, m_SubMenuList{subMenuList}, m_Fun{std::move(fun)}
10 10 {
11 11 }
12 12
13 13 QString m_Name;
14 14 QStringList m_SubMenuList;
15 15 QKeySequence m_DisplayedShortcut;
16 16 SelectionZoneAction::ExecuteFunction m_Fun;
17 17 SelectionZoneAction::EnableFunction m_EnableFun = [](auto zones) { return true; };
18 bool m_FilteringAllowed = true;
18 19 };
19 20
20 21 SelectionZoneAction::SelectionZoneAction(const QString &name, ExecuteFunction fun)
21 22 : impl{spimpl::make_unique_impl<SelectionZoneActionPrivate>(name, QStringList{},
22 23 std::move(fun))}
23 24 {
24 25 }
25 26
26 27 SelectionZoneAction::SelectionZoneAction(const QStringList &subMenuList, const QString &name,
27 28 SelectionZoneAction::ExecuteFunction fun)
28 29 : impl{spimpl::make_unique_impl<SelectionZoneActionPrivate>(name, subMenuList,
29 30 std::move(fun))}
30 31 {
31 32 }
32 33
33 34 void SelectionZoneAction::setEnableFunction(EnableFunction fun)
34 35 {
35 36 impl->m_EnableFun = std::move(fun);
36 37 }
37 38
38 39 void SelectionZoneAction::setDisplayedShortcut(const QKeySequence &shortcut)
39 40 {
40 41 impl->m_DisplayedShortcut = shortcut;
41 42 }
42 43
43 44 QKeySequence SelectionZoneAction::displayedShortcut() const
44 45 {
45 46 return impl->m_DisplayedShortcut;
46 47 }
47 48
48 49 QString SelectionZoneAction::name() const noexcept
49 50 {
50 51 return impl->m_Name;
51 52 }
52 53
53 54 QStringList SelectionZoneAction::subMenuList() const noexcept
54 55 {
55 56 return impl->m_SubMenuList;
56 57 }
57 58
59 void SelectionZoneAction::setAllowedFiltering(bool value)
60 {
61 impl->m_FilteringAllowed = value;
62 }
63
64 bool SelectionZoneAction::isFilteringAllowed() const
65 {
66 return impl->m_FilteringAllowed;
67 }
68
58 69 void SelectionZoneAction::execute(const QVector<VisualizationSelectionZoneItem *> &item)
59 70 {
60 71 impl->m_Fun(item);
61 72 }
62 73
63 74 bool SelectionZoneAction::isEnabled(const QVector<VisualizationSelectionZoneItem *> &item)
64 75 {
65 76 return impl->m_EnableFun(item);
66 77 }
@@ -1,171 +1,174
1 1 #include "Catalogue/CatalogueActionManager.h"
2 2
3 3 #include <Actions/ActionsGuiController.h>
4 4 #include <Catalogue/CatalogueController.h>
5 5 #include <DataSource/DataSourceItem.h>
6 6 #include <SqpApplication.h>
7 7 #include <Variable/Variable.h>
8 8 #include <Visualization/VisualizationGraphWidget.h>
9 9 #include <Visualization/VisualizationSelectionZoneItem.h>
10 10
11 11 #include <Catalogue/CatalogueEventsWidget.h>
12 12 #include <Catalogue/CatalogueExplorer.h>
13 13 #include <Catalogue/CatalogueSideBarWidget.h>
14 14
15 15 #include <CatalogueDao.h>
16 16 #include <DBCatalogue.h>
17 17 #include <DBEvent.h>
18 18 #include <DBEventProduct.h>
19 19
20 20 #include <QBoxLayout>
21 21 #include <QComboBox>
22 22 #include <QDialog>
23 23 #include <QDialogButtonBox>
24 24 #include <QLineEdit>
25 25 #include <memory>
26 26
27 27 const auto CATALOGUE_MENU_NAME = QObject::tr("Catalogues");
28 28 const auto CATALOGUE_CREATE_EVENT_MENU_NAME = QObject::tr("New Event...");
29 29
30 30 const auto DEFAULT_EVENT_NAME = QObject::tr("Event");
31 31 const auto DEFAULT_CATALOGUE_NAME = QObject::tr("Catalogue");
32 32
33 33 struct CatalogueActionManager::CatalogueActionManagerPrivate {
34 34
35 35 CatalogueExplorer *m_CatalogueExplorer = nullptr;
36 36 QVector<std::shared_ptr<SelectionZoneAction> > m_CreateInCatalogueActions;
37 37
38 38 CatalogueActionManagerPrivate(CatalogueExplorer *catalogueExplorer)
39 39 : m_CatalogueExplorer(catalogueExplorer)
40 40 {
41 41 }
42 42
43 43 void createEventFromZones(const QString &eventName,
44 44 const QVector<VisualizationSelectionZoneItem *> &zones,
45 45 const std::shared_ptr<DBCatalogue> &catalogue = nullptr)
46 46 {
47 47 auto event = std::make_shared<DBEvent>();
48 48 event->setName(eventName);
49 49
50 50 std::list<DBEventProduct> productList;
51 51 for (auto zone : zones) {
52 52 auto graph = zone->parentGraphWidget();
53 53 for (auto var : graph->variables()) {
54 54 auto eventProduct = std::make_shared<DBEventProduct>();
55 55 eventProduct->setEvent(*event);
56 56
57 57 auto productId
58 58 = var->metadata().value(DataSourceItem::ID_DATA_KEY, "UnknownID").toString();
59 59
60 60 auto zoneRange = zone->range();
61 61 eventProduct->setTStart(zoneRange.m_TStart);
62 62 eventProduct->setTEnd(zoneRange.m_TEnd);
63 63
64 64 eventProduct->setProductId(productId);
65 65
66 66 productList.push_back(*eventProduct);
67 67 }
68 68 }
69 69
70 70 event->setEventProducts(productList);
71 71
72 72 sqpApp->catalogueController().addEvent(event);
73 73
74 74
75 75 if (catalogue) {
76 76 catalogue->addEvent(event->getUniqId());
77 77 sqpApp->catalogueController().updateCatalogue(catalogue);
78 78 m_CatalogueExplorer->sideBarWidget().setCatalogueChanges(catalogue, true);
79 79 if (m_CatalogueExplorer->eventsWidget().displayedCatalogues().contains(catalogue)) {
80 80 m_CatalogueExplorer->eventsWidget().addEvent(event);
81 81 m_CatalogueExplorer->eventsWidget().setEventChanges(event, true);
82 82 }
83 83 }
84 84 else if (m_CatalogueExplorer->eventsWidget().isAllEventsDisplayed()) {
85 85 m_CatalogueExplorer->eventsWidget().addEvent(event);
86 86 m_CatalogueExplorer->eventsWidget().setEventChanges(event, true);
87 87 }
88 88 }
89 89
90 90 SelectionZoneAction::EnableFunction createEventEnableFuntion() const
91 91 {
92 92 return [](auto zones) {
93 93
94 94 // Checks that all variables in the zones doesn't refer to the same product
95 95 QSet<QString> usedDatasource;
96 96 for (auto zone : zones) {
97 97 auto graph = zone->parentGraphWidget();
98 98 auto variables = graph->variables();
99 99
100 100 for (auto var : variables) {
101 101 auto datasourceId
102 102 = var->metadata().value(DataSourceItem::ID_DATA_KEY).toString();
103 103 if (!usedDatasource.contains(datasourceId)) {
104 104 usedDatasource.insert(datasourceId);
105 105 }
106 106 else {
107 107 return false;
108 108 }
109 109 }
110 110 }
111 111
112 112 return true;
113 113 };
114 114 }
115 115 };
116 116
117 117 CatalogueActionManager::CatalogueActionManager(CatalogueExplorer *catalogueExplorer)
118 118 : impl{spimpl::make_unique_impl<CatalogueActionManagerPrivate>(catalogueExplorer)}
119 119 {
120 120 }
121 121
122 122 void CatalogueActionManager::installSelectionZoneActions()
123 123 {
124 124 auto &actionController = sqpApp->actionsGuiController();
125 125
126 126 auto createEventAction = actionController.addSectionZoneAction(
127 127 {CATALOGUE_MENU_NAME, CATALOGUE_CREATE_EVENT_MENU_NAME}, QObject::tr("Without Catalogue"),
128 128 [this](auto zones) { impl->createEventFromZones(DEFAULT_EVENT_NAME, zones); });
129 129 createEventAction->setEnableFunction(impl->createEventEnableFuntion());
130 createEventAction->setAllowedFiltering(false);
130 131
131 132 auto createEventInNewCatalogueAction = actionController.addSectionZoneAction(
132 133 {CATALOGUE_MENU_NAME, CATALOGUE_CREATE_EVENT_MENU_NAME}, QObject::tr("In New Catalogue"),
133 134 [this](auto zones) {
134 135
135 136 auto newCatalogue = std::make_shared<DBCatalogue>();
136 137 newCatalogue->setName(DEFAULT_CATALOGUE_NAME);
137 138 sqpApp->catalogueController().addCatalogue(newCatalogue);
138 139 impl->m_CatalogueExplorer->sideBarWidget().addCatalogue(newCatalogue,
139 140 REPOSITORY_DEFAULT);
140 141
141 142 impl->createEventFromZones(DEFAULT_EVENT_NAME, zones, newCatalogue);
142 143 });
143 144 createEventInNewCatalogueAction->setEnableFunction(impl->createEventEnableFuntion());
144
145 createEventInNewCatalogueAction->setAllowedFiltering(false);
145 146
146 147 refreshCreateInCatalogueAction();
148
149 actionController.addFilterForMenu({CATALOGUE_MENU_NAME, CATALOGUE_CREATE_EVENT_MENU_NAME});
147 150 }
148 151
149 152 void CatalogueActionManager::refreshCreateInCatalogueAction()
150 153 {
151 154 auto &actionController = sqpApp->actionsGuiController();
152 155
153 156 for (auto action : impl->m_CreateInCatalogueActions) {
154 157 actionController.removeAction(action);
155 158 }
156 159 impl->m_CreateInCatalogueActions.clear();
157 160
158 161 auto allCatalogues
159 162 = impl->m_CatalogueExplorer->sideBarWidget().getCatalogues(REPOSITORY_DEFAULT);
160 163
161 164 for (auto catalogue : allCatalogues) {
162 165 auto catalogueName = catalogue->getName();
163 166 auto createEventInCatalogueAction = actionController.addSectionZoneAction(
164 167 {CATALOGUE_MENU_NAME, CATALOGUE_CREATE_EVENT_MENU_NAME},
165 168 QObject::tr("In \"").append(catalogueName).append("\""), [this, catalogue](auto zones) {
166 169 impl->createEventFromZones(DEFAULT_EVENT_NAME, zones, catalogue);
167 170 });
168 171 createEventInCatalogueAction->setEnableFunction(impl->createEventEnableFuntion());
169 172 impl->m_CreateInCatalogueActions << createEventInCatalogueAction;
170 173 }
171 174 }
@@ -1,1057 +1,1077
1 1 #include "Visualization/VisualizationGraphWidget.h"
2 2 #include "Visualization/IVisualizationWidgetVisitor.h"
3 3 #include "Visualization/VisualizationCursorItem.h"
4 4 #include "Visualization/VisualizationDefs.h"
5 5 #include "Visualization/VisualizationGraphHelper.h"
6 6 #include "Visualization/VisualizationGraphRenderingDelegate.h"
7 7 #include "Visualization/VisualizationMultiZoneSelectionDialog.h"
8 8 #include "Visualization/VisualizationSelectionZoneItem.h"
9 9 #include "Visualization/VisualizationSelectionZoneManager.h"
10 10 #include "Visualization/VisualizationWidget.h"
11 11 #include "Visualization/VisualizationZoneWidget.h"
12 12 #include "ui_VisualizationGraphWidget.h"
13 13
14 14 #include <Actions/ActionsGuiController.h>
15 #include <Actions/FilteringAction.h>
15 16 #include <Common/MimeTypesDef.h>
16 17 #include <Data/ArrayData.h>
17 18 #include <Data/IDataSeries.h>
18 19 #include <Data/SpectrogramSeries.h>
19 20 #include <DragAndDrop/DragDropGuiController.h>
20 21 #include <Settings/SqpSettingsDefs.h>
21 22 #include <SqpApplication.h>
22 23 #include <Time/TimeController.h>
23 24 #include <Variable/Variable.h>
24 25 #include <Variable/VariableController.h>
25 26
26 27 #include <unordered_map>
27 28
28 29 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
29 30
30 31 namespace {
31 32
32 33 /// Key pressed to enable drag&drop in all modes
33 34 const auto DRAG_DROP_MODIFIER = Qt::AltModifier;
34 35
35 36 /// Key pressed to enable zoom on horizontal axis
36 37 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier;
37 38
38 39 /// Key pressed to enable zoom on vertical axis
39 40 const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier;
40 41
41 42 /// Speed of a step of a wheel event for a pan, in percentage of the axis range
42 43 const auto PAN_SPEED = 5;
43 44
44 45 /// Key pressed to enable a calibration pan
45 46 const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier;
46 47
47 48 /// Key pressed to enable multi selection of selection zones
48 49 const auto MULTI_ZONE_SELECTION_MODIFIER = Qt::ControlModifier;
49 50
50 51 /// Minimum size for the zoom box, in percentage of the axis range
51 52 const auto ZOOM_BOX_MIN_SIZE = 0.8;
52 53
53 54 /// Format of the dates appearing in the label of a cursor
54 55 const auto CURSOR_LABELS_DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd\nhh:mm:ss:zzz");
55 56
56 57 } // namespace
57 58
58 59 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
59 60
60 61 explicit VisualizationGraphWidgetPrivate(const QString &name)
61 62 : m_Name{name},
62 63 m_Flags{GraphFlag::EnableAll},
63 64 m_IsCalibration{false},
64 65 m_RenderingDelegate{nullptr}
65 66 {
66 67 }
67 68
68 69 void updateData(PlottablesMap &plottables, std::shared_ptr<Variable> variable,
69 70 const SqpRange &range)
70 71 {
71 72 VisualizationGraphHelper::updateData(plottables, variable, range);
72 73
73 74 // Prevents that data has changed to update rendering
74 75 m_RenderingDelegate->onPlotUpdated();
75 76 }
76 77
77 78 QString m_Name;
78 79 // 1 variable -> n qcpplot
79 80 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
80 81 GraphFlags m_Flags;
81 82 bool m_IsCalibration;
82 83 /// Delegate used to attach rendering features to the plot
83 84 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
84 85
85 86 QCPItemRect *m_DrawingZoomRect = nullptr;
86 87 QStack<QPair<QCPRange, QCPRange> > m_ZoomStack;
87 88
88 89 std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr;
89 90 std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr;
90 91
91 92 VisualizationSelectionZoneItem *m_DrawingZone = nullptr;
92 93 VisualizationSelectionZoneItem *m_HoveredZone = nullptr;
93 94 QVector<VisualizationSelectionZoneItem *> m_SelectionZones;
94 95
95 96 bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even
96 97
97 98 bool m_VariableAutoRangeOnInit = true;
98 99
99 100 void startDrawingRect(const QPoint &pos, QCustomPlot &plot)
100 101 {
101 102 removeDrawingRect(plot);
102 103
103 104 auto axisPos = posToAxisPos(pos, plot);
104 105
105 106 m_DrawingZoomRect = new QCPItemRect{&plot};
106 107 QPen p;
107 108 p.setWidth(2);
108 109 m_DrawingZoomRect->setPen(p);
109 110
110 111 m_DrawingZoomRect->topLeft->setCoords(axisPos);
111 112 m_DrawingZoomRect->bottomRight->setCoords(axisPos);
112 113 }
113 114
114 115 void removeDrawingRect(QCustomPlot &plot)
115 116 {
116 117 if (m_DrawingZoomRect) {
117 118 plot.removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot
118 119 m_DrawingZoomRect = nullptr;
119 120 plot.replot(QCustomPlot::rpQueuedReplot);
120 121 }
121 122 }
122 123
123 124 void startDrawingZone(const QPoint &pos, VisualizationGraphWidget *graph)
124 125 {
125 126 endDrawingZone(graph);
126 127
127 128 auto axisPos = posToAxisPos(pos, graph->plot());
128 129
129 130 m_DrawingZone = new VisualizationSelectionZoneItem{&graph->plot()};
130 131 m_DrawingZone->setRange(axisPos.x(), axisPos.x());
131 132 m_DrawingZone->setEditionEnabled(false);
132 133 }
133 134
134 135 void endDrawingZone(VisualizationGraphWidget *graph)
135 136 {
136 137 if (m_DrawingZone) {
137 138 auto drawingZoneRange = m_DrawingZone->range();
138 139 if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0) {
139 140 m_DrawingZone->setEditionEnabled(true);
140 141 addSelectionZone(m_DrawingZone);
141 142 }
142 143 else {
143 144 graph->plot().removeItem(m_DrawingZone); // the item is deleted by QCustomPlot
144 145 }
145 146
146 147 graph->plot().replot(QCustomPlot::rpQueuedReplot);
147 148 m_DrawingZone = nullptr;
148 149 }
149 150 }
150 151
151 152 void setSelectionZonesEditionEnabled(bool value)
152 153 {
153 154 for (auto s : m_SelectionZones) {
154 155 s->setEditionEnabled(value);
155 156 }
156 157 }
157 158
158 159 void addSelectionZone(VisualizationSelectionZoneItem *zone) { m_SelectionZones << zone; }
159 160
160 161 VisualizationSelectionZoneItem *selectionZoneAt(const QPoint &pos,
161 162 const QCustomPlot &plot) const
162 163 {
163 164 VisualizationSelectionZoneItem *selectionZoneItemUnderCursor = nullptr;
164 165 auto minDistanceToZone = -1;
165 166 for (auto zone : m_SelectionZones) {
166 167 auto distanceToZone = zone->selectTest(pos, false);
167 168 if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone)
168 169 && distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
169 170 selectionZoneItemUnderCursor = zone;
170 171 }
171 172 }
172 173
173 174 return selectionZoneItemUnderCursor;
174 175 }
175 176
176 177 QVector<VisualizationSelectionZoneItem *> selectionZonesAt(const QPoint &pos,
177 178 const QCustomPlot &plot) const
178 179 {
179 180 QVector<VisualizationSelectionZoneItem *> zones;
180 181 for (auto zone : m_SelectionZones) {
181 182 auto distanceToZone = zone->selectTest(pos, false);
182 183 if (distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
183 184 zones << zone;
184 185 }
185 186 }
186 187
187 188 return zones;
188 189 }
189 190
190 191 void moveSelectionZoneOnTop(VisualizationSelectionZoneItem *zone, QCustomPlot &plot)
191 192 {
192 193 if (!m_SelectionZones.isEmpty() && m_SelectionZones.last() != zone) {
193 194 zone->moveToTop();
194 195 m_SelectionZones.removeAll(zone);
195 196 m_SelectionZones.append(zone);
196 197 }
197 198 }
198 199
199 200 QPointF posToAxisPos(const QPoint &pos, QCustomPlot &plot) const
200 201 {
201 202 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
202 203 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
203 204 return QPointF{axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y())};
204 205 }
205 206
206 207 bool pointIsInAxisRect(const QPointF &axisPoint, QCustomPlot &plot) const
207 208 {
208 209 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
209 210 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
210 211 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
211 212 }
212 213 };
213 214
214 215 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
215 216 : VisualizationDragWidget{parent},
216 217 ui{new Ui::VisualizationGraphWidget},
217 218 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
218 219 {
219 220 ui->setupUi(this);
220 221
221 222 // 'Close' options : widget is deleted when closed
222 223 setAttribute(Qt::WA_DeleteOnClose);
223 224
224 225 // Set qcpplot properties :
225 226 // - zoom is enabled
226 227 // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation
227 228 ui->widget->setInteractions(QCP::iRangeZoom);
228 229 ui->widget->axisRect()->setRangeDrag(Qt::Horizontal | Qt::Vertical);
229 230
230 231 // The delegate must be initialized after the ui as it uses the plot
231 232 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
232 233
233 234 // Init the cursors
234 235 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
235 236 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
236 237 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
237 238 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
238 239
239 240 connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress);
240 241 connect(ui->widget, &QCustomPlot::mouseRelease, this,
241 242 &VisualizationGraphWidget::onMouseRelease);
242 243 connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove);
243 244 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
244 245 connect(ui->widget, &QCustomPlot::mouseDoubleClick, this,
245 246 &VisualizationGraphWidget::onMouseDoubleClick);
246 247 connect(ui->widget->xAxis, static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(
247 248 &QCPAxis::rangeChanged),
248 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
249 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
249 250
250 251 // Activates menu when right clicking on the graph
251 252 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
252 253 connect(ui->widget, &QCustomPlot::customContextMenuRequested, this,
253 254 &VisualizationGraphWidget::onGraphMenuRequested);
254 255
255 256 connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
256 257 &VariableController::onRequestDataLoading);
257 258
258 259 connect(&sqpApp->variableController(), &VariableController::updateVarDisplaying, this,
259 260 &VisualizationGraphWidget::onUpdateVarDisplaying);
260 261
261 262 // Necessary for all platform since Qt::AA_EnableHighDpiScaling is enable.
262 263 plot().setPlottingHint(QCP::phFastPolylines, true);
263 264 }
264 265
265 266
266 267 VisualizationGraphWidget::~VisualizationGraphWidget()
267 268 {
268 269 delete ui;
269 270 }
270 271
271 272 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
272 273 {
273 274 auto parent = parentWidget();
274 275 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
275 276 parent = parent->parentWidget();
276 277 }
277 278
278 279 return qobject_cast<VisualizationZoneWidget *>(parent);
279 280 }
280 281
281 282 VisualizationWidget *VisualizationGraphWidget::parentVisualizationWidget() const
282 283 {
283 284 auto parent = parentWidget();
284 285 while (parent != nullptr && !qobject_cast<VisualizationWidget *>(parent)) {
285 286 parent = parent->parentWidget();
286 287 }
287 288
288 289 return qobject_cast<VisualizationWidget *>(parent);
289 290 }
290 291
291 292 void VisualizationGraphWidget::setFlags(GraphFlags flags)
292 293 {
293 294 impl->m_Flags = std::move(flags);
294 295 }
295 296
296 297 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, SqpRange range)
297 298 {
298 299 /// Lambda used to set graph's units and range according to the variable passed in parameter
299 300 auto loadRange = [this](std::shared_ptr<Variable> variable, const SqpRange &range) {
300 301 impl->m_RenderingDelegate->setAxesUnits(*variable);
301 302
302 303 this->setFlags(GraphFlag::DisableAll);
303 304 setGraphRange(range);
304 305 this->setFlags(GraphFlag::EnableAll);
305 306 emit requestDataLoading({variable}, range, false);
306 307 };
307 308
308 309 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
309 310
310 311 // Calls update of graph's range and units when the data of the variable have been initialized.
311 312 // Note: we use QueuedConnection here as the update event must be called in the UI thread
312 313 connect(variable.get(), &Variable::dataInitialized, this,
313 314 [ varW = std::weak_ptr<Variable>{variable}, range, loadRange, this ]() {
314 315 if (auto var = varW.lock()) {
315 316 // If the variable is the first added in the graph, we load its range
316 317 auto firstVariableInGraph = range == INVALID_RANGE;
317 318 auto loadedRange = graphRange();
318 319 if (impl->m_VariableAutoRangeOnInit) {
319 320 loadedRange = firstVariableInGraph ? var->range() : range;
320 321 }
321 322 loadRange(var, loadedRange);
322 323 setYRange(var);
323 324 }
324 325 },
325 326 Qt::QueuedConnection);
326 327
327 328 // Uses delegate to create the qcpplot components according to the variable
328 329 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
329 330
330 331 // Sets graph properties
331 332 impl->m_RenderingDelegate->setGraphProperties(*variable, createdPlottables);
332 333
333 334 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
334 335
335 336 // If the variable already has its data loaded, load its units and its range in the graph
336 337 if (variable->dataSeries() != nullptr) {
337 338 loadRange(variable, range);
338 339 }
339 340
340 341 emit variableAdded(variable);
341 342 }
342 343
343 344 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
344 345 {
345 346 // Each component associated to the variable :
346 347 // - is removed from qcpplot (which deletes it)
347 348 // - is no longer referenced in the map
348 349 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
349 350 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
350 351 emit variableAboutToBeRemoved(variable);
351 352
352 353 auto &plottablesMap = variableIt->second;
353 354
354 355 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
355 356 plottableIt != plottableEnd;) {
356 357 ui->widget->removePlottable(plottableIt->second);
357 358 plottableIt = plottablesMap.erase(plottableIt);
358 359 }
359 360
360 361 impl->m_VariableToPlotMultiMap.erase(variableIt);
361 362 }
362 363
363 364 // Updates graph
364 365 ui->widget->replot();
365 366 }
366 367
367 368 QList<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
368 369 {
369 370 auto variables = QList<std::shared_ptr<Variable> >{};
370 371 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
371 372 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
372 373 variables << it->first;
373 374 }
374 375
375 376 return variables;
376 377 }
377 378
378 379 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
379 380 {
380 381 if (!variable) {
381 382 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
382 383 return;
383 384 }
384 385
385 386 VisualizationGraphHelper::setYAxisRange(variable, *ui->widget);
386 387 }
387 388
388 389 SqpRange VisualizationGraphWidget::graphRange() const noexcept
389 390 {
390 391 auto graphRange = ui->widget->xAxis->range();
391 392 return SqpRange{graphRange.lower, graphRange.upper};
392 393 }
393 394
394 395 void VisualizationGraphWidget::setGraphRange(const SqpRange &range, bool calibration)
395 396 {
396 397 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange START");
397 398
398 399 if (calibration) {
399 400 impl->m_IsCalibration = true;
400 401 }
401 402
402 403 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
403 404 ui->widget->replot();
404 405
405 406 if (calibration) {
406 407 impl->m_IsCalibration = false;
407 408 }
408 409
409 410 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange END");
410 411 }
411 412
412 413 void VisualizationGraphWidget::setAutoRangeOnVariableInitialization(bool value)
413 414 {
414 415 impl->m_VariableAutoRangeOnInit = value;
415 416 }
416 417
417 418 QVector<SqpRange> VisualizationGraphWidget::selectionZoneRanges() const
418 419 {
419 420 QVector<SqpRange> ranges;
420 421 for (auto zone : impl->m_SelectionZones) {
421 422 ranges << zone->range();
422 423 }
423 424
424 425 return ranges;
425 426 }
426 427
427 428 void VisualizationGraphWidget::addSelectionZones(const QVector<SqpRange> &ranges)
428 429 {
429 430 for (const auto &range : ranges) {
430 431 // note: ownership is transfered to QCustomPlot
431 432 auto zone = new VisualizationSelectionZoneItem(&plot());
432 433 zone->setRange(range.m_TStart, range.m_TEnd);
433 434 impl->addSelectionZone(zone);
434 435 }
435 436
436 437 plot().replot(QCustomPlot::rpQueuedReplot);
437 438 }
438 439
439 440 VisualizationSelectionZoneItem *VisualizationGraphWidget::addSelectionZone(const QString &name,
440 441 const SqpRange &range)
441 442 {
442 443 // note: ownership is transfered to QCustomPlot
443 444 auto zone = new VisualizationSelectionZoneItem(&plot());
444 445 zone->setName(name);
445 446 zone->setRange(range.m_TStart, range.m_TEnd);
446 447 impl->addSelectionZone(zone);
447 448
448 449 plot().replot(QCustomPlot::rpQueuedReplot);
449 450
450 451 return zone;
451 452 }
452 453
453 454 void VisualizationGraphWidget::removeSelectionZone(VisualizationSelectionZoneItem *selectionZone)
454 455 {
455 456 parentVisualizationWidget()->selectionZoneManager().setSelected(selectionZone, false);
456 457
457 458 if (impl->m_HoveredZone == selectionZone) {
458 459 impl->m_HoveredZone = nullptr;
459 460 setCursor(Qt::ArrowCursor);
460 461 }
461 462
462 463 impl->m_SelectionZones.removeAll(selectionZone);
463 464 plot().removeItem(selectionZone);
464 465 plot().replot(QCustomPlot::rpQueuedReplot);
465 466 }
466 467
467 468 void VisualizationGraphWidget::undoZoom()
468 469 {
469 470 auto zoom = impl->m_ZoomStack.pop();
470 471 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
471 472 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
472 473
473 474 axisX->setRange(zoom.first);
474 475 axisY->setRange(zoom.second);
475 476
476 477 plot().replot(QCustomPlot::rpQueuedReplot);
477 478 }
478 479
479 480 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
480 481 {
481 482 if (visitor) {
482 483 visitor->visit(this);
483 484 }
484 485 else {
485 486 qCCritical(LOG_VisualizationGraphWidget())
486 487 << tr("Can't visit widget : the visitor is null");
487 488 }
488 489 }
489 490
490 491 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
491 492 {
492 493 auto isSpectrogram = [](const auto &variable) {
493 494 return std::dynamic_pointer_cast<SpectrogramSeries>(variable.dataSeries()) != nullptr;
494 495 };
495 496
496 497 // - A spectrogram series can't be dropped on graph with existing plottables
497 498 // - No data series can be dropped on graph with existing spectrogram series
498 499 return isSpectrogram(variable)
499 500 ? impl->m_VariableToPlotMultiMap.empty()
500 501 : std::none_of(
501 502 impl->m_VariableToPlotMultiMap.cbegin(), impl->m_VariableToPlotMultiMap.cend(),
502 503 [isSpectrogram](const auto &entry) { return isSpectrogram(*entry.first); });
503 504 }
504 505
505 506 bool VisualizationGraphWidget::contains(const Variable &variable) const
506 507 {
507 508 // Finds the variable among the keys of the map
508 509 auto variablePtr = &variable;
509 510 auto findVariable
510 511 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
511 512
512 513 auto end = impl->m_VariableToPlotMultiMap.cend();
513 514 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
514 515 return it != end;
515 516 }
516 517
517 518 QString VisualizationGraphWidget::name() const
518 519 {
519 520 return impl->m_Name;
520 521 }
521 522
522 523 QMimeData *VisualizationGraphWidget::mimeData(const QPoint &position) const
523 524 {
524 525 auto mimeData = new QMimeData;
525 526
526 527 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position, plot());
527 528 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
528 529 && selectionZoneItemUnderCursor) {
529 530 mimeData->setData(MIME_TYPE_TIME_RANGE, TimeController::mimeDataForTimeRange(
530 531 selectionZoneItemUnderCursor->range()));
531 532 mimeData->setData(MIME_TYPE_SELECTION_ZONE, TimeController::mimeDataForTimeRange(
532 533 selectionZoneItemUnderCursor->range()));
533 534 }
534 535 else {
535 536 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
536 537
537 538 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
538 539 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
539 540 }
540 541
541 542 return mimeData;
542 543 }
543 544
544 545 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint &dragPosition)
545 546 {
546 547 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition, plot());
547 548 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
548 549 && selectionZoneItemUnderCursor) {
549 550
550 551 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
551 552 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
552 553
553 554 auto zoneSize = QSizeF{qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
554 555 qAbs(zoneBottomRight.y() - zoneTopLeft.y())}
555 556 .toSize();
556 557
557 558 auto pixmap = QPixmap(zoneSize);
558 559 render(&pixmap, QPoint(), QRegion{QRect{zoneTopLeft.toPoint(), zoneSize}});
559 560
560 561 return pixmap;
561 562 }
562 563
563 564 return QPixmap();
564 565 }
565 566
566 567 bool VisualizationGraphWidget::isDragAllowed() const
567 568 {
568 569 return true;
569 570 }
570 571
571 572 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
572 573 {
573 574 if (highlighted) {
574 575 plot().setBackground(QBrush(QColor("#BBD5EE")));
575 576 }
576 577 else {
577 578 plot().setBackground(QBrush(Qt::white));
578 579 }
579 580
580 581 plot().update();
581 582 }
582 583
583 584 void VisualizationGraphWidget::addVerticalCursor(double time)
584 585 {
585 586 impl->m_VerticalCursor->setPosition(time);
586 587 impl->m_VerticalCursor->setVisible(true);
587 588
588 589 auto text
589 590 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
590 591 impl->m_VerticalCursor->setLabelText(text);
591 592 }
592 593
593 594 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
594 595 {
595 596 impl->m_VerticalCursor->setAbsolutePosition(position);
596 597 impl->m_VerticalCursor->setVisible(true);
597 598
598 599 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
599 600 auto text
600 601 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
601 602 impl->m_VerticalCursor->setLabelText(text);
602 603 }
603 604
604 605 void VisualizationGraphWidget::removeVerticalCursor()
605 606 {
606 607 impl->m_VerticalCursor->setVisible(false);
607 608 plot().replot(QCustomPlot::rpQueuedReplot);
608 609 }
609 610
610 611 void VisualizationGraphWidget::addHorizontalCursor(double value)
611 612 {
612 613 impl->m_HorizontalCursor->setPosition(value);
613 614 impl->m_HorizontalCursor->setVisible(true);
614 615 impl->m_HorizontalCursor->setLabelText(QString::number(value));
615 616 }
616 617
617 618 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
618 619 {
619 620 impl->m_HorizontalCursor->setAbsolutePosition(position);
620 621 impl->m_HorizontalCursor->setVisible(true);
621 622
622 623 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
623 624 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
624 625 }
625 626
626 627 void VisualizationGraphWidget::removeHorizontalCursor()
627 628 {
628 629 impl->m_HorizontalCursor->setVisible(false);
629 630 plot().replot(QCustomPlot::rpQueuedReplot);
630 631 }
631 632
632 633 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
633 634 {
634 635 Q_UNUSED(event);
635 636
636 637 for (auto i : impl->m_SelectionZones) {
637 638 parentVisualizationWidget()->selectionZoneManager().setSelected(i, false);
638 639 }
639 640
640 641 // Prevents that all variables will be removed from graph when it will be closed
641 642 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
642 643 emit variableAboutToBeRemoved(variableEntry.first);
643 644 }
644 645 }
645 646
646 647 void VisualizationGraphWidget::enterEvent(QEvent *event)
647 648 {
648 649 Q_UNUSED(event);
649 650 impl->m_RenderingDelegate->showGraphOverlay(true);
650 651 }
651 652
652 653 void VisualizationGraphWidget::leaveEvent(QEvent *event)
653 654 {
654 655 Q_UNUSED(event);
655 656 impl->m_RenderingDelegate->showGraphOverlay(false);
656 657
657 658 if (auto parentZone = parentZoneWidget()) {
658 659 parentZone->notifyMouseLeaveGraph(this);
659 660 }
660 661 else {
661 662 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
662 663 }
663 664
664 665 if (impl->m_HoveredZone) {
665 666 impl->m_HoveredZone->setHovered(false);
666 667 impl->m_HoveredZone = nullptr;
667 668 }
668 669 }
669 670
670 671 QCustomPlot &VisualizationGraphWidget::plot() const noexcept
671 672 {
672 673 return *ui->widget;
673 674 }
674 675
675 676 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
676 677 {
677 678 QMenu graphMenu{};
678 679
679 680 // Iterates on variables (unique keys)
680 681 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
681 682 end = impl->m_VariableToPlotMultiMap.cend();
682 683 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
683 684 // 'Remove variable' action
684 685 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
685 686 [ this, var = it->first ]() { removeVariable(var); });
686 687 }
687 688
688 689 if (!impl->m_ZoomStack.isEmpty()) {
689 690 if (!graphMenu.isEmpty()) {
690 691 graphMenu.addSeparator();
691 692 }
692 693
693 694 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
694 695 }
695 696
696 697 // Selection Zone Actions
697 698 auto selectionZoneItem = impl->selectionZoneAt(pos, plot());
698 699 if (selectionZoneItem) {
699 700 auto selectedItems = parentVisualizationWidget()->selectionZoneManager().selectedItems();
700 701 selectedItems.removeAll(selectionZoneItem);
701 702 selectedItems.prepend(selectionZoneItem); // Put the current selection zone first
702 703
703 704 auto zoneActions = sqpApp->actionsGuiController().selectionZoneActions();
704 705 if (!zoneActions.isEmpty() && !graphMenu.isEmpty()) {
705 706 graphMenu.addSeparator();
706 707 }
707 708
708 709 QHash<QString, QMenu *> subMenus;
709 710 QHash<QString, bool> subMenusEnabled;
711 QHash<QString, FilteringAction *> filteredMenu;
710 712
711 713 for (auto zoneAction : zoneActions) {
712 714
713 715 auto isEnabled = zoneAction->isEnabled(selectedItems);
714 716
715 717 auto menu = &graphMenu;
718 QString menuPath;
716 719 for (auto subMenuName : zoneAction->subMenuList()) {
717 if (!subMenus.contains(subMenuName)) {
720 menuPath += '/';
721 menuPath += subMenuName;
722
723 if (!subMenus.contains(menuPath)) {
718 724 menu = menu->addMenu(subMenuName);
719 subMenus[subMenuName] = menu;
720 subMenusEnabled[subMenuName] = isEnabled;
725 subMenus[menuPath] = menu;
726 subMenusEnabled[menuPath] = isEnabled;
721 727 }
722 728 else {
723 menu = subMenus.value(subMenuName);
729 menu = subMenus.value(menuPath);
724 730 if (isEnabled) {
725 731 // The sub menu is enabled if at least one of its actions is enabled
726 subMenusEnabled[subMenuName] = true;
732 subMenusEnabled[menuPath] = true;
727 733 }
728 734 }
729 735 }
730 736
737 FilteringAction *filterAction = nullptr;
738 if (sqpApp->actionsGuiController().isMenuFiltered(zoneAction->subMenuList())) {
739 filterAction = filteredMenu.value(menuPath);
740 if (!filterAction) {
741 filterAction = new FilteringAction{this};
742 filteredMenu[menuPath] = filterAction;
743 menu->addAction(filterAction);
744 }
745 }
746
731 747 auto action = menu->addAction(zoneAction->name());
732 748 action->setEnabled(isEnabled);
733 749 action->setShortcut(zoneAction->displayedShortcut());
734 750 QObject::connect(action, &QAction::triggered,
735 751 [zoneAction, selectedItems]() { zoneAction->execute(selectedItems); });
752
753 if (filterAction && zoneAction->isFilteringAllowed()) {
754 filterAction->addActionToFilter(action);
755 }
736 756 }
737 757
738 758 for (auto it = subMenus.cbegin(); it != subMenus.cend(); ++it) {
739 759 it.value()->setEnabled(subMenusEnabled[it.key()]);
740 760 }
741 761 }
742 762
743 763 if (!graphMenu.isEmpty()) {
744 764 graphMenu.exec(QCursor::pos());
745 765 }
746 766 }
747 767
748 768 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
749 769 {
750 770 qCDebug(LOG_VisualizationGraphWidget()) << tr("TORM: VisualizationGraphWidget::onRangeChanged")
751 771 << QThread::currentThread()->objectName() << "DoAcqui"
752 772 << impl->m_Flags.testFlag(GraphFlag::EnableAcquisition);
753 773
754 774 auto graphRange = SqpRange{t1.lower, t1.upper};
755 775 auto oldGraphRange = SqpRange{t2.lower, t2.upper};
756 776
757 777 if (impl->m_Flags.testFlag(GraphFlag::EnableAcquisition)) {
758 778 QVector<std::shared_ptr<Variable> > variableUnderGraphVector;
759 779
760 780 for (auto it = impl->m_VariableToPlotMultiMap.begin(),
761 781 end = impl->m_VariableToPlotMultiMap.end();
762 782 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
763 783 variableUnderGraphVector.push_back(it->first);
764 784 }
765 785 emit requestDataLoading(std::move(variableUnderGraphVector), graphRange,
766 786 !impl->m_IsCalibration);
767 787 }
768 788
769 789 if (impl->m_Flags.testFlag(GraphFlag::EnableSynchronization) && !impl->m_IsCalibration) {
770 790 qCDebug(LOG_VisualizationGraphWidget())
771 791 << tr("TORM: VisualizationGraphWidget::Synchronize notify !!")
772 792 << QThread::currentThread()->objectName() << graphRange << oldGraphRange;
773 793 emit synchronize(graphRange, oldGraphRange);
774 794 }
775 795
776 796 auto pos = mapFromGlobal(QCursor::pos());
777 797 auto axisPos = impl->posToAxisPos(pos, plot());
778 798 if (auto parentZone = parentZoneWidget()) {
779 799 if (impl->pointIsInAxisRect(axisPos, plot())) {
780 800 parentZone->notifyMouseMoveInGraph(pos, axisPos, this);
781 801 }
782 802 else {
783 803 parentZone->notifyMouseLeaveGraph(this);
784 804 }
785 805 }
786 806 else {
787 807 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
788 808 }
789 809
790 810 // Quits calibration
791 811 impl->m_IsCalibration = false;
792 812 }
793 813
794 814 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept
795 815 {
796 816 impl->m_RenderingDelegate->onMouseDoubleClick(event);
797 817 }
798 818
799 819 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
800 820 {
801 821 // Handles plot rendering when mouse is moving
802 822 impl->m_RenderingDelegate->onMouseMove(event);
803 823
804 824 auto axisPos = impl->posToAxisPos(event->pos(), plot());
805 825
806 826 // Zoom box and zone drawing
807 827 if (impl->m_DrawingZoomRect) {
808 828 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
809 829 }
810 830 else if (impl->m_DrawingZone) {
811 831 impl->m_DrawingZone->setEnd(axisPos.x());
812 832 }
813 833
814 834 // Cursor
815 835 if (auto parentZone = parentZoneWidget()) {
816 836 if (impl->pointIsInAxisRect(axisPos, plot())) {
817 837 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
818 838 }
819 839 else {
820 840 parentZone->notifyMouseLeaveGraph(this);
821 841 }
822 842 }
823 843 else {
824 844 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
825 845 }
826 846
827 847 // Search for the selection zone under the mouse
828 848 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
829 849 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
830 850 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) {
831 851
832 852 // Sets the appropriate cursor shape
833 853 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
834 854 setCursor(cursorShape);
835 855
836 856 // Manages the hovered zone
837 857 if (selectionZoneItemUnderCursor != impl->m_HoveredZone) {
838 858 if (impl->m_HoveredZone) {
839 859 impl->m_HoveredZone->setHovered(false);
840 860 }
841 861 selectionZoneItemUnderCursor->setHovered(true);
842 862 impl->m_HoveredZone = selectionZoneItemUnderCursor;
843 863 plot().replot(QCustomPlot::rpQueuedReplot);
844 864 }
845 865 }
846 866 else {
847 867 // There is no zone under the mouse or the interaction mode is not "selection zones"
848 868 if (impl->m_HoveredZone) {
849 869 impl->m_HoveredZone->setHovered(false);
850 870 impl->m_HoveredZone = nullptr;
851 871 }
852 872
853 873 setCursor(Qt::ArrowCursor);
854 874 }
855 875
856 876 impl->m_HasMovedMouse = true;
857 877 VisualizationDragWidget::mouseMoveEvent(event);
858 878 }
859 879
860 880 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
861 881 {
862 882 auto value = event->angleDelta().x() + event->angleDelta().y();
863 883 if (value != 0) {
864 884
865 885 auto direction = value > 0 ? 1.0 : -1.0;
866 886 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
867 887 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
868 888 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
869 889
870 890 auto zoomOrientations = QFlags<Qt::Orientation>{};
871 891 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
872 892 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
873 893
874 894 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
875 895
876 896 if (!isZoomX && !isZoomY) {
877 897 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
878 898 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
879 899
880 900 axis->setRange(axis->range() + diff);
881 901
882 902 if (plot().noAntialiasingOnDrag()) {
883 903 plot().setNotAntialiasedElements(QCP::aeAll);
884 904 }
885 905
886 906 plot().replot(QCustomPlot::rpQueuedReplot);
887 907 }
888 908 }
889 909 }
890 910
891 911 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
892 912 {
893 913 auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
894 914 auto isSelectionZoneMode
895 915 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
896 916 auto isLeftClick = event->buttons().testFlag(Qt::LeftButton);
897 917
898 918 if (!isDragDropClick && isLeftClick) {
899 919 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) {
900 920 // Starts a zoom box
901 921 impl->startDrawingRect(event->pos(), plot());
902 922 }
903 923 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr) {
904 924 // Starts a new selection zone
905 925 auto zoneAtPos = impl->selectionZoneAt(event->pos(), plot());
906 926 if (!zoneAtPos) {
907 927 impl->startDrawingZone(event->pos(), this);
908 928 }
909 929 }
910 930 }
911 931
912 932 // Allows mouse panning only in default mode
913 933 plot().setInteraction(QCP::iRangeDrag, sqpApp->plotsInteractionMode()
914 934 == SqpApplication::PlotsInteractionMode::None
915 935 && !isDragDropClick);
916 936
917 937 // Allows zone edition only in selection zone mode without drag&drop
918 938 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
919 939
920 940 // Selection / Deselection
921 941 if (isSelectionZoneMode) {
922 942 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
923 943 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
924 944
925 945
926 946 if (selectionZoneItemUnderCursor && !selectionZoneItemUnderCursor->selected()
927 947 && !isMultiSelectionClick) {
928 948 parentVisualizationWidget()->selectionZoneManager().select(
929 949 {selectionZoneItemUnderCursor});
930 950 }
931 951 else if (!selectionZoneItemUnderCursor && !isMultiSelectionClick && isLeftClick) {
932 952 parentVisualizationWidget()->selectionZoneManager().clearSelection();
933 953 }
934 954 else {
935 955 // No selection change
936 956 }
937 957
938 958 if (selectionZoneItemUnderCursor && isLeftClick) {
939 959 selectionZoneItemUnderCursor->setAssociatedEditedZones(
940 960 parentVisualizationWidget()->selectionZoneManager().selectedItems());
941 961 }
942 962 }
943 963
944 964
945 965 impl->m_HasMovedMouse = false;
946 966 VisualizationDragWidget::mousePressEvent(event);
947 967 }
948 968
949 969 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
950 970 {
951 971 if (impl->m_DrawingZoomRect) {
952 972
953 973 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
954 974 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
955 975
956 976 auto newAxisXRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().x(),
957 977 impl->m_DrawingZoomRect->bottomRight->coords().x()};
958 978
959 979 auto newAxisYRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().y(),
960 980 impl->m_DrawingZoomRect->bottomRight->coords().y()};
961 981
962 982 impl->removeDrawingRect(plot());
963 983
964 984 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
965 985 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
966 986 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
967 987 axisX->setRange(newAxisXRange);
968 988 axisY->setRange(newAxisYRange);
969 989
970 990 plot().replot(QCustomPlot::rpQueuedReplot);
971 991 }
972 992 }
973 993
974 994 impl->endDrawingZone(this);
975 995
976 996 // Selection / Deselection
977 997 auto isSelectionZoneMode
978 998 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
979 999 if (isSelectionZoneMode) {
980 1000 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
981 1001 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
982 1002 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton
983 1003 && !impl->m_HasMovedMouse) {
984 1004
985 1005 auto zonesUnderCursor = impl->selectionZonesAt(event->pos(), plot());
986 1006 if (zonesUnderCursor.count() > 1) {
987 1007 // There are multiple zones under the mouse.
988 1008 // Performs the selection with a selection dialog.
989 1009 VisualizationMultiZoneSelectionDialog dialog{this};
990 1010 dialog.setZones(zonesUnderCursor);
991 1011 dialog.move(mapToGlobal(event->pos() - QPoint(dialog.width() / 2, 20)));
992 1012 dialog.activateWindow();
993 1013 dialog.raise();
994 1014 if (dialog.exec() == QDialog::Accepted) {
995 1015 auto selection = dialog.selectedZones();
996 1016
997 1017 if (!isMultiSelectionClick) {
998 1018 parentVisualizationWidget()->selectionZoneManager().clearSelection();
999 1019 }
1000 1020
1001 1021 for (auto it = selection.cbegin(); it != selection.cend(); ++it) {
1002 1022 auto zone = it.key();
1003 1023 auto isSelected = it.value();
1004 1024 parentVisualizationWidget()->selectionZoneManager().setSelected(zone,
1005 1025 isSelected);
1006 1026
1007 1027 if (isSelected) {
1008 1028 // Puts the zone on top of the stack so it can be moved or resized
1009 1029 impl->moveSelectionZoneOnTop(zone, plot());
1010 1030 }
1011 1031 }
1012 1032 }
1013 1033 }
1014 1034 else {
1015 1035 if (!isMultiSelectionClick) {
1016 1036 parentVisualizationWidget()->selectionZoneManager().select(
1017 1037 {selectionZoneItemUnderCursor});
1018 1038 impl->moveSelectionZoneOnTop(selectionZoneItemUnderCursor, plot());
1019 1039 }
1020 1040 else {
1021 1041 parentVisualizationWidget()->selectionZoneManager().setSelected(
1022 1042 selectionZoneItemUnderCursor, !selectionZoneItemUnderCursor->selected()
1023 1043 || event->button() == Qt::RightButton);
1024 1044 }
1025 1045 }
1026 1046 }
1027 1047 else {
1028 1048 // No selection change
1029 1049 }
1030 1050 }
1031 1051 }
1032 1052
1033 1053 void VisualizationGraphWidget::onDataCacheVariableUpdated()
1034 1054 {
1035 1055 auto graphRange = ui->widget->xAxis->range();
1036 1056 auto dateTime = SqpRange{graphRange.lower, graphRange.upper};
1037 1057
1038 1058 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
1039 1059 auto variable = variableEntry.first;
1040 1060 qCDebug(LOG_VisualizationGraphWidget())
1041 1061 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
1042 1062 qCDebug(LOG_VisualizationGraphWidget())
1043 1063 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
1044 1064 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
1045 1065 impl->updateData(variableEntry.second, variable, variable->range());
1046 1066 }
1047 1067 }
1048 1068 }
1049 1069
1050 1070 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
1051 1071 const SqpRange &range)
1052 1072 {
1053 1073 auto it = impl->m_VariableToPlotMultiMap.find(variable);
1054 1074 if (it != impl->m_VariableToPlotMultiMap.end()) {
1055 1075 impl->updateData(it->second, variable, range);
1056 1076 }
1057 1077 }
General Comments 0
You need to be logged in to leave comments. Login now