##// END OF EJS Templates
Link between selection zone item and event
trabillard -
r1347:12c6415397ca
parent child
Show More
@@ -1,58 +1,64
1 1 #ifndef SCIQLOP_CATALOGUEEVENTSWIDGET_H
2 2 #define SCIQLOP_CATALOGUEEVENTSWIDGET_H
3 3
4 4 #include <Common/spimpl.h>
5 5 #include <QLoggingCategory>
6 6 #include <QWidget>
7 7
8 8 class DBCatalogue;
9 9 class DBEvent;
10 10 class DBEventProduct;
11 11 class VisualizationWidget;
12 class VisualizationSelectionZoneItem;
12 13
13 14 namespace Ui {
14 15 class CatalogueEventsWidget;
15 16 }
16 17
17 18 Q_DECLARE_LOGGING_CATEGORY(LOG_CatalogueEventsWidget)
18 19
19 20 class CatalogueEventsWidget : public QWidget {
20 21 Q_OBJECT
21 22
22 23 signals:
23 24 void eventsSelected(const QVector<std::shared_ptr<DBEvent> > &event);
25 void eventsRemoved(const QVector<std::shared_ptr<DBEvent> > &event);
24 26 void eventProductsSelected(
25 27 const QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > >
26 28 &eventproducts);
27 29 void selectionCleared();
30 void selectionZoneAdded(const std::shared_ptr<DBEvent> &event, const QString &productId,
31 VisualizationSelectionZoneItem *selectionZone);
28 32
29 33 public:
30 34 explicit CatalogueEventsWidget(QWidget *parent = 0);
31 35 virtual ~CatalogueEventsWidget();
32 36
33 37 void setVisualizationWidget(VisualizationWidget *visualization);
34 38
35 39 void addEvent(const std::shared_ptr<DBEvent> &event);
36 40 void setEventChanges(const std::shared_ptr<DBEvent> &event, bool hasChanges);
37 41
38 42 QVector<std::shared_ptr<DBCatalogue> > displayedCatalogues() const;
39 43 bool isAllEventsDisplayed() const;
40 44 bool isEventDisplayed(const std::shared_ptr<DBEvent> &event) const;
41 45
46 void refreshEvent(const std::shared_ptr<DBEvent> &event);
47
42 48 public slots:
43 49 void populateWithCatalogues(const QVector<std::shared_ptr<DBCatalogue> > &catalogues);
44 50 void populateWithAllEvents();
45 51 void clear();
46 52 void refresh();
47 53
48 54 private:
49 55 Ui::CatalogueEventsWidget *ui;
50 56
51 57 class CatalogueEventsWidgetPrivate;
52 58 spimpl::unique_impl_ptr<CatalogueEventsWidgetPrivate> impl;
53 59
54 60 private slots:
55 61 void emitSelection();
56 62 };
57 63
58 64 #endif // SCIQLOP_CATALOGUEEVENTSWIDGET_H
@@ -1,49 +1,51
1 1 #ifndef SCIQLOP_CATALOGUEINSPECTORWIDGET_H
2 2 #define SCIQLOP_CATALOGUEINSPECTORWIDGET_H
3 3
4 4 #include <Common/spimpl.h>
5 5 #include <QWidget>
6 6 #include <memory>
7 7
8 8 namespace Ui {
9 9 class CatalogueInspectorWidget;
10 10 }
11 11
12 12 class DBCatalogue;
13 13 class DBEvent;
14 14 class DBEventProduct;
15 15
16 16 class CatalogueInspectorWidget : public QWidget {
17 17 Q_OBJECT
18 18
19 19 signals:
20 20 void catalogueUpdated(const std::shared_ptr<DBCatalogue> &catalogue);
21 21 void eventUpdated(const std::shared_ptr<DBEvent> &event);
22 22 void eventProductUpdated(const std::shared_ptr<DBEvent> &event,
23 23 const std::shared_ptr<DBEventProduct> &eventProduct);
24 24
25 25 public:
26 26 explicit CatalogueInspectorWidget(QWidget *parent = 0);
27 27 virtual ~CatalogueInspectorWidget();
28 28
29 29 /// Enum matching the pages inside the stacked widget
30 30 enum class Page { Empty, CatalogueProperties, EventProperties };
31 31
32 32 Page currentPage() const;
33 33
34 34 void setEvent(const std::shared_ptr<DBEvent> &event);
35 35 void setEventProduct(const std::shared_ptr<DBEvent> &event,
36 36 const std::shared_ptr<DBEventProduct> &eventProduct);
37 37 void setCatalogue(const std::shared_ptr<DBCatalogue> &catalogue);
38 38
39 void refresh();
40
39 41 public slots:
40 42 void showPage(Page page);
41 43
42 44 private:
43 45 Ui::CatalogueInspectorWidget *ui;
44 46
45 47 class CatalogueInspectorWidgetPrivate;
46 48 spimpl::unique_impl_ptr<CatalogueInspectorWidgetPrivate> impl;
47 49 };
48 50
49 51 #endif // SCIQLOP_CATALOGUEINSPECTORWIDGET_H
@@ -1,156 +1,158
1 1 #ifndef SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
2 2 #define SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
3 3
4 4 #include "Visualization/IVisualizationWidget.h"
5 5 #include "Visualization/VisualizationDragWidget.h"
6 6
7 7 #include <QLoggingCategory>
8 8 #include <QWidget>
9 9
10 10 #include <memory>
11 11
12 12 #include <Common/spimpl.h>
13 13
14 14 Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationGraphWidget)
15 15
16 16 class QCPRange;
17 17 class QCustomPlot;
18 18 class SqpRange;
19 19 class Variable;
20 20 class VisualizationWidget;
21 21 class VisualizationZoneWidget;
22 22 class VisualizationSelectionZoneItem;
23 23
24 24 namespace Ui {
25 25 class VisualizationGraphWidget;
26 26 } // namespace Ui
27 27
28 28 /// Defines options that can be associated with the graph
29 29 enum GraphFlag {
30 30 DisableAll = 0x0, ///< Disables acquisition and synchronization
31 31 EnableAcquisition = 0x1, ///< When this flag is set, the change of the graph's range leads to
32 32 /// the acquisition of data
33 33 EnableSynchronization = 0x2, ///< When this flag is set, the change of the graph's range causes
34 34 /// the call to the synchronization of the graphs contained in the
35 35 /// same zone of this graph
36 36 EnableAll = ~DisableAll ///< Enables acquisition and synchronization
37 37 };
38 38
39 39 Q_DECLARE_FLAGS(GraphFlags, GraphFlag)
40 40 Q_DECLARE_OPERATORS_FOR_FLAGS(GraphFlags)
41 41
42 42 class VisualizationGraphWidget : public VisualizationDragWidget, public IVisualizationWidget {
43 43 Q_OBJECT
44 44
45 45 friend class QCustomPlotSynchronizer;
46 46 friend class VisualizationGraphRenderingDelegate;
47 47
48 48 public:
49 49 explicit VisualizationGraphWidget(const QString &name = {}, QWidget *parent = 0);
50 50 virtual ~VisualizationGraphWidget();
51 51
52 52 /// Returns the VisualizationZoneWidget which contains the graph or nullptr
53 53 VisualizationZoneWidget *parentZoneWidget() const noexcept;
54 54
55 55 /// Returns the main VisualizationWidget which contains the graph or nullptr
56 56 VisualizationWidget *parentVisualizationWidget() const;
57 57
58 58 /// Sets graph options
59 59 void setFlags(GraphFlags flags);
60 60
61 61 void addVariable(std::shared_ptr<Variable> variable, SqpRange range);
62 62
63 63 /// Removes a variable from the graph
64 64 void removeVariable(std::shared_ptr<Variable> variable) noexcept;
65 65
66 66 /// Returns the list of all variables used in the graph
67 67 QList<std::shared_ptr<Variable> > variables() const;
68 68
69 69 /// Sets the y-axis range based on the data of a variable
70 70 void setYRange(std::shared_ptr<Variable> variable);
71 71 SqpRange graphRange() const noexcept;
72 72 void setGraphRange(const SqpRange &range, bool calibration = false);
73 73 void setAutoRangeOnVariableInitialization(bool value);
74 74
75 75 // Zones
76 76 /// Returns the ranges of all the selection zones on the graph
77 77 QVector<SqpRange> selectionZoneRanges() const;
78 78 /// Adds new selection zones in the graph
79 79 void addSelectionZones(const QVector<SqpRange> &ranges);
80 /// Adds a new selection zone in the graph
81 VisualizationSelectionZoneItem *addSelectionZone(const QString &name, const SqpRange &range);
80 82 /// Removes the specified selection zone
81 83 void removeSelectionZone(VisualizationSelectionZoneItem *selectionZone);
82 84
83 85 /// Undo the last zoom done with a zoom box
84 86 void undoZoom();
85 87
86 88 // IVisualizationWidget interface
87 89 void accept(IVisualizationWidgetVisitor *visitor) override;
88 90 bool canDrop(const Variable &variable) const override;
89 91 bool contains(const Variable &variable) const override;
90 92 QString name() const override;
91 93
92 94 // VisualisationDragWidget
93 95 QMimeData *mimeData(const QPoint &position) const override;
94 96 QPixmap customDragPixmap(const QPoint &dragPosition) override;
95 97 bool isDragAllowed() const override;
96 98 void highlightForMerge(bool highlighted) override;
97 99
98 100 // Cursors
99 101 /// Adds or moves the vertical cursor at the specified value on the x-axis
100 102 void addVerticalCursor(double time);
101 103 /// Adds or moves the vertical cursor at the specified value on the x-axis
102 104 void addVerticalCursorAtViewportPosition(double position);
103 105 void removeVerticalCursor();
104 106 /// Adds or moves the vertical cursor at the specified value on the y-axis
105 107 void addHorizontalCursor(double value);
106 108 /// Adds or moves the vertical cursor at the specified value on the y-axis
107 109 void addHorizontalCursorAtViewportPosition(double position);
108 110 void removeHorizontalCursor();
109 111
110 112 signals:
111 113 void synchronize(const SqpRange &range, const SqpRange &oldRange);
112 114 void requestDataLoading(QVector<std::shared_ptr<Variable> > variable, const SqpRange &range,
113 115 bool synchronise);
114 116
115 117 /// Signal emitted when the variable is about to be removed from the graph
116 118 void variableAboutToBeRemoved(std::shared_ptr<Variable> var);
117 119 /// Signal emitted when the variable has been added to the graph
118 120 void variableAdded(std::shared_ptr<Variable> var);
119 121
120 122 protected:
121 123 void closeEvent(QCloseEvent *event) override;
122 124 void enterEvent(QEvent *event) override;
123 125 void leaveEvent(QEvent *event) override;
124 126
125 127 QCustomPlot &plot() const noexcept;
126 128
127 129 private:
128 130 Ui::VisualizationGraphWidget *ui;
129 131
130 132 class VisualizationGraphWidgetPrivate;
131 133 spimpl::unique_impl_ptr<VisualizationGraphWidgetPrivate> impl;
132 134
133 135 private slots:
134 136 /// Slot called when right clicking on the graph (displays a menu)
135 137 void onGraphMenuRequested(const QPoint &pos) noexcept;
136 138
137 139 /// Rescale the X axe to range parameter
138 140 void onRangeChanged(const QCPRange &t1, const QCPRange &t2);
139 141
140 142 /// Slot called when a mouse double click was made
141 143 void onMouseDoubleClick(QMouseEvent *event) noexcept;
142 144 /// Slot called when a mouse move was made
143 145 void onMouseMove(QMouseEvent *event) noexcept;
144 146 /// Slot called when a mouse wheel was made, to perform some processing before the zoom is done
145 147 void onMouseWheel(QWheelEvent *event) noexcept;
146 148 /// Slot called when a mouse press was made, to activate the calibration of a graph
147 149 void onMousePress(QMouseEvent *event) noexcept;
148 150 /// Slot called when a mouse release was made, to deactivate the calibration of a graph
149 151 void onMouseRelease(QMouseEvent *event) noexcept;
150 152
151 153 void onDataCacheVariableUpdated();
152 154
153 155 void onUpdateVarDisplaying(std::shared_ptr<Variable> variable, const SqpRange &range);
154 156 };
155 157
156 158 #endif // SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
@@ -1,68 +1,73
1 1 #ifndef SCIQLOP_VISUALIZATIONSELECTIONZONEITEM_H
2 2 #define SCIQLOP_VISUALIZATIONSELECTIONZONEITEM_H
3 3
4 4 #include <Common/spimpl.h>
5 5 #include <Data/SqpRange.h>
6 6 #include <Visualization/qcustomplot.h>
7 7
8 8 class VisualizationGraphWidget;
9 9
10 10 class VisualizationSelectionZoneItem : public QCPItemRect {
11 Q_OBJECT
12
13 signals:
14 /// Signal emitted when the zone range is edited manually
15 void rangeEdited(const SqpRange &range);
11 16
12 17 public:
13 18 VisualizationSelectionZoneItem(QCustomPlot *plot);
14 19 virtual ~VisualizationSelectionZoneItem();
15 20
16 21 VisualizationGraphWidget *parentGraphWidget() const noexcept;
17 22
18 23 void setName(const QString &name);
19 24 QString name() const;
20 25
21 26 SqpRange range() const;
22 27 void setRange(double tstart, double tend);
23 28 void setStart(double tstart);
24 29 void setEnd(double tend);
25 30
26 31 void setColor(const QColor &color);
27 32
28 33 void setEditionEnabled(bool value);
29 34 bool isEditionEnabled() const;
30 35
31 36 /// Moves the item at the top of its QCPLayer. It will then receive the mouse events if multiple
32 37 /// items are stacked on top of each others.
33 38 void moveToTop();
34 39
35 40 Qt::CursorShape curshorShapeForPosition(const QPoint &position) const;
36 41 void setHovered(bool value);
37 42
38 43 /// Sets the zones which should be moved or reisized together with this zone
39 44 void setAssociatedEditedZones(const QVector<VisualizationSelectionZoneItem *> &associatedZones);
40 45
41 46 /// Align the specified zones with this one, vertically with the left border
42 47 bool alignZonesVerticallyOnLeft(const QVector<VisualizationSelectionZoneItem *> &zonesToAlign,
43 48 bool allowResize);
44 49 /// Align the specified zones with this one, vertically with the right border
45 50 bool alignZonesVerticallyOnRight(const QVector<VisualizationSelectionZoneItem *> &zonesToAlign,
46 51 bool allowResize);
47 52 /// Align the specified zones with this one, temporally with the left border
48 53 bool alignZonesTemporallyOnLeft(const QVector<VisualizationSelectionZoneItem *> &zonesToAlign,
49 54 bool allowResize);
50 55 /// Align the specified zones with this one, temporally with the right border
51 56 bool alignZonesTemporallyOnRight(const QVector<VisualizationSelectionZoneItem *> &zonesToAlign,
52 57 bool allowResize);
53 58
54 59 protected:
55 60 void mousePressEvent(QMouseEvent *event, const QVariant &details) override;
56 61 void mouseMoveEvent(QMouseEvent *event, const QPointF &startPos) override;
57 62 void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos) override;
58 63
59 64 void resizeLeft(double pixelDiff);
60 65 void resizeRight(double pixelDiff);
61 66 void move(double pixelDiff);
62 67
63 68 private:
64 69 class VisualizationSelectionZoneItemPrivate;
65 70 spimpl::unique_impl_ptr<VisualizationSelectionZoneItemPrivate> impl;
66 71 };
67 72
68 73 #endif // SCIQLOP_VISUALIZATIONSELECTIONZONEITEM_H
@@ -1,147 +1,148
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 'include/Visualization/VisualizationSelectionZoneItem.h',
23 24 'include/Actions/SelectionZoneAction.h',
24 25 'include/Visualization/VisualizationMultiZoneSelectionDialog.h',
25 26 'include/Catalogue/CatalogueExplorer.h',
26 27 'include/Catalogue/CatalogueEventsWidget.h',
27 28 'include/Catalogue/CatalogueSideBarWidget.h',
28 29 'include/Catalogue/CatalogueInspectorWidget.h',
29 30 'include/Catalogue/CatalogueEventsModel.h',
30 31 'include/Catalogue/CreateEventDialog.h',
31 32 'include/Catalogue/CatalogueTreeModel.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 'ui/Catalogue/CreateEventDialog.ui'
54 55 ]
55 56
56 57 gui_qresources = ['resources/sqpguiresources.qrc']
57 58
58 59 rcc_gen = generator(rcc,
59 60 output : 'qrc_@BASENAME@.cpp',
60 61 arguments : [
61 62 '--output',
62 63 '@OUTPUT@',
63 64 '@INPUT@',
64 65 '@EXTRA_ARGS@'])
65 66
66 67 rcc_files = rcc_gen.process(gui_qresources, extra_args : ['-name', 'sqpguiresources'])
67 68
68 69 gui_moc_files = qt5.preprocess(moc_headers : gui_moc_headers,
69 70 ui_files : gui_ui_files)
70 71
71 72 gui_sources = [
72 73 'src/SqpApplication.cpp',
73 74 'src/DragAndDrop/DragDropGuiController.cpp',
74 75 'src/DragAndDrop/DragDropScroller.cpp',
75 76 'src/DragAndDrop/DragDropTabSwitcher.cpp',
76 77 'src/Common/ColorUtils.cpp',
77 78 'src/Common/VisualizationDef.cpp',
78 79 'src/DataSource/DataSourceTreeWidgetItem.cpp',
79 80 'src/DataSource/DataSourceTreeWidgetHelper.cpp',
80 81 'src/DataSource/DataSourceWidget.cpp',
81 82 'src/DataSource/DataSourceTreeWidget.cpp',
82 83 'src/Settings/SqpSettingsDialog.cpp',
83 84 'src/Settings/SqpSettingsGeneralWidget.cpp',
84 85 'src/SidePane/SqpSidePane.cpp',
85 86 'src/TimeWidget/TimeWidget.cpp',
86 87 'src/Variable/VariableInspectorWidget.cpp',
87 88 'src/Variable/VariableInspectorTableView.cpp',
88 89 'src/Variable/VariableMenuHeaderWidget.cpp',
89 90 'src/Variable/RenameVariableDialog.cpp',
90 91 'src/Visualization/VisualizationGraphHelper.cpp',
91 92 'src/Visualization/VisualizationGraphRenderingDelegate.cpp',
92 93 'src/Visualization/VisualizationGraphWidget.cpp',
93 94 'src/Visualization/VisualizationTabWidget.cpp',
94 95 'src/Visualization/VisualizationWidget.cpp',
95 96 'src/Visualization/VisualizationZoneWidget.cpp',
96 97 'src/Visualization/qcustomplot.cpp',
97 98 'src/Visualization/QCustomPlotSynchronizer.cpp',
98 99 'src/Visualization/operations/FindVariableOperation.cpp',
99 100 'src/Visualization/operations/GenerateVariableMenuOperation.cpp',
100 101 'src/Visualization/operations/MenuBuilder.cpp',
101 102 'src/Visualization/operations/RemoveVariableOperation.cpp',
102 103 'src/Visualization/operations/RescaleAxeOperation.cpp',
103 104 'src/Visualization/VisualizationDragDropContainer.cpp',
104 105 'src/Visualization/VisualizationDragWidget.cpp',
105 106 'src/Visualization/AxisRenderingUtils.cpp',
106 107 'src/Visualization/PlottablesRenderingUtils.cpp',
107 108 'src/Visualization/MacScrollBarStyle.cpp',
108 109 'src/Visualization/VisualizationCursorItem.cpp',
109 110 'src/Visualization/ColorScaleEditor.cpp',
110 111 'src/Visualization/SqpColorScale.cpp',
111 112 'src/Visualization/QCPColorMapIterator.cpp',
112 113 'src/Visualization/VisualizationSelectionZoneItem.cpp',
113 114 'src/Visualization/VisualizationSelectionZoneManager.cpp',
114 115 'src/Actions/SelectionZoneAction.cpp',
115 116 'src/Actions/ActionsGuiController.cpp',
116 117 'src/Visualization/VisualizationActionManager.cpp',
117 118 'src/Visualization/VisualizationMultiZoneSelectionDialog.cpp',
118 119 'src/Catalogue/CatalogueExplorer.cpp',
119 120 'src/Catalogue/CatalogueEventsWidget.cpp',
120 121 'src/Catalogue/CatalogueSideBarWidget.cpp',
121 122 'src/Catalogue/CatalogueInspectorWidget.cpp',
122 123 'src/Catalogue/CatalogueTreeItems/CatalogueAbstractTreeItem.cpp',
123 124 'src/Catalogue/CatalogueTreeItems/CatalogueTreeItem.cpp',
124 125 'src/Catalogue/CatalogueTreeItems/CatalogueTextTreeItem.cpp',
125 126 'src/Catalogue/CatalogueEventsModel.cpp',
126 127 'src/Catalogue/CatalogueExplorerHelper.cpp',
127 128 'src/Catalogue/CatalogueActionManager.cpp',
128 129 'src/Catalogue/CreateEventDialog.cpp',
129 130 'src/Catalogue/CatalogueTreeModel.cpp'
130 131 ]
131 132
132 133 gui_inc = include_directories(['include'])
133 134
134 135 sciqlop_gui_lib = library('sciqlopgui',
135 136 gui_sources,
136 137 gui_moc_files,
137 138 rcc_files,
138 139 include_directories : [gui_inc],
139 140 dependencies : [ qt5printsupport, qt5gui, qt5widgets, qt5svg, sciqlop_core, catalogueapi_dep],
140 141 install : true
141 142 )
142 143
143 144 sciqlop_gui = declare_dependency(link_with : sciqlop_gui_lib,
144 145 include_directories : gui_inc,
145 146 dependencies : [qt5printsupport, qt5gui, qt5widgets, qt5svg, sciqlop_core, catalogueapi_dep])
146 147
147 148
@@ -1,579 +1,590
1 1 #include "Catalogue/CatalogueEventsWidget.h"
2 2 #include "ui_CatalogueEventsWidget.h"
3 3
4 4 #include <Catalogue/CatalogueController.h>
5 5 #include <Catalogue/CatalogueEventsModel.h>
6 6 #include <Catalogue/CatalogueExplorerHelper.h>
7 7 #include <CatalogueDao.h>
8 8 #include <DBCatalogue.h>
9 9 #include <DBEventProduct.h>
10 10 #include <DataSource/DataSourceController.h>
11 11 #include <DataSource/DataSourceItem.h>
12 12 #include <SqpApplication.h>
13 13 #include <Variable/Variable.h>
14 14 #include <Variable/VariableController.h>
15 15 #include <Visualization/VisualizationGraphWidget.h>
16 16 #include <Visualization/VisualizationTabWidget.h>
17 17 #include <Visualization/VisualizationWidget.h>
18 18 #include <Visualization/VisualizationZoneWidget.h>
19 19
20 20 #include <QDialog>
21 21 #include <QDialogButtonBox>
22 22 #include <QListWidget>
23 23 #include <QMessageBox>
24 24
25 25 Q_LOGGING_CATEGORY(LOG_CatalogueEventsWidget, "CatalogueEventsWidget")
26 26
27 27 /// Fixed size of the validation column
28 28 const auto VALIDATION_COLUMN_SIZE = 35;
29 29
30 30 /// Percentage added to the range of a event when it is displayed
31 31 const auto EVENT_RANGE_MARGE = 30; // in %
32 32
33 33 struct CatalogueEventsWidget::CatalogueEventsWidgetPrivate {
34 34
35 35 CatalogueEventsModel *m_Model = nullptr;
36 36 QStringList m_ZonesForTimeMode;
37 37 QString m_ZoneForGraphMode;
38 38 QVector<std::shared_ptr<DBCatalogue> > m_DisplayedCatalogues;
39 39 bool m_AllEventDisplayed = false;
40 40 QVector<VisualizationGraphWidget *> m_CustomGraphs;
41 41
42 42 VisualizationWidget *m_VisualizationWidget = nullptr;
43 43
44 44 void setEvents(const QVector<std::shared_ptr<DBEvent> > &events, CatalogueEventsWidget *widget)
45 45 {
46 46 widget->ui->treeView->setSortingEnabled(false);
47 47 m_Model->setEvents(events);
48 48 widget->ui->treeView->setSortingEnabled(true);
49 49
50 50 for (auto event : events) {
51 51 if (sqpApp->catalogueController().eventHasChanges(event)) {
52 52 auto index = m_Model->indexOf(event);
53 53 widget->setEventChanges(event, true);
54 54 }
55 55 }
56 56 }
57 57
58 58 void addEvent(const std::shared_ptr<DBEvent> &event, QTreeView *treeView)
59 59 {
60 60 treeView->setSortingEnabled(false);
61 61 m_Model->addEvent(event);
62 62 treeView->setSortingEnabled(true);
63 63 }
64 64
65 65 void removeEvent(const std::shared_ptr<DBEvent> &event, QTreeView *treeView)
66 66 {
67 67 treeView->setSortingEnabled(false);
68 68 m_Model->removeEvent(event);
69 69 treeView->setSortingEnabled(true);
70 70 }
71 71
72 72 QStringList getAvailableVisualizationZoneList() const
73 73 {
74 74 if (m_VisualizationWidget) {
75 75 if (auto tab = m_VisualizationWidget->currentTabWidget()) {
76 76 return tab->availableZoneWidgets();
77 77 }
78 78 }
79 79
80 80 return QStringList{};
81 81 }
82 82
83 83 QStringList selectZone(QWidget *parent, const QStringList &selectedZones,
84 84 bool allowMultiSelection, const QPoint &location)
85 85 {
86 86 auto availableZones = getAvailableVisualizationZoneList();
87 87 if (availableZones.isEmpty()) {
88 88 return QStringList{};
89 89 }
90 90
91 91 QDialog d(parent, Qt::Tool);
92 92 d.setWindowTitle("Choose a zone");
93 93 auto layout = new QVBoxLayout{&d};
94 94 layout->setContentsMargins(0, 0, 0, 0);
95 95 auto listWidget = new QListWidget{&d};
96 96 layout->addWidget(listWidget);
97 97
98 98 QSet<QListWidgetItem *> checkedItems;
99 99 for (auto zone : availableZones) {
100 100 auto item = new QListWidgetItem{zone};
101 101 item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsUserCheckable);
102 102 if (selectedZones.contains(zone)) {
103 103 item->setCheckState(Qt::Checked);
104 104 checkedItems << item;
105 105 }
106 106 else {
107 107 item->setCheckState(Qt::Unchecked);
108 108 }
109 109
110 110 listWidget->addItem(item);
111 111 }
112 112
113 113 auto buttonBox = new QDialogButtonBox{QDialogButtonBox::Ok, &d};
114 114 layout->addWidget(buttonBox);
115 115
116 116 QObject::connect(buttonBox, &QDialogButtonBox::accepted, &d, &QDialog::accept);
117 117 QObject::connect(buttonBox, &QDialogButtonBox::rejected, &d, &QDialog::reject);
118 118
119 119 QObject::connect(listWidget, &QListWidget::itemChanged,
120 120 [&checkedItems, allowMultiSelection, listWidget](auto item) {
121 121 if (item->checkState() == Qt::Checked) {
122 122 if (!allowMultiSelection) {
123 123 for (auto checkedItem : checkedItems) {
124 124 listWidget->blockSignals(true);
125 125 checkedItem->setCheckState(Qt::Unchecked);
126 126 listWidget->blockSignals(false);
127 127 }
128 128
129 129 checkedItems.clear();
130 130 }
131 131 checkedItems << item;
132 132 }
133 133 else {
134 134 checkedItems.remove(item);
135 135 }
136 136 });
137 137
138 138 QStringList result;
139 139
140 140 d.setMinimumWidth(120);
141 141 d.resize(d.minimumSizeHint());
142 142 d.move(location);
143 143 if (d.exec() == QDialog::Accepted) {
144 144 for (auto item : checkedItems) {
145 145 result += item->text();
146 146 }
147 147 }
148 148 else {
149 149 result = selectedZones;
150 150 }
151 151
152 152 return result;
153 153 }
154 154
155 155 void updateForTimeMode(QTreeView *treeView)
156 156 {
157 157 auto selectedRows = treeView->selectionModel()->selectedRows();
158 158
159 159 if (selectedRows.count() == 1) {
160 160 auto event = m_Model->getEvent(selectedRows.first());
161 161 if (event) {
162 162 if (m_VisualizationWidget) {
163 163 if (auto tab = m_VisualizationWidget->currentTabWidget()) {
164 164
165 165 for (auto zoneName : m_ZonesForTimeMode) {
166 166 if (auto zone = tab->getZoneWithName(zoneName)) {
167 167 SqpRange eventRange;
168 168 eventRange.m_TStart = event->getTStart();
169 169 eventRange.m_TEnd = event->getTEnd();
170 170 zone->setZoneRange(eventRange);
171 171 }
172 172 }
173 173 }
174 174 else {
175 175 qCWarning(LOG_CatalogueEventsWidget())
176 176 << "updateTimeZone: no tab found in the visualization";
177 177 }
178 178 }
179 179 else {
180 180 qCWarning(LOG_CatalogueEventsWidget())
181 181 << "updateTimeZone: visualization widget not found";
182 182 }
183 183 }
184 184 }
185 185 else {
186 186 qCWarning(LOG_CatalogueEventsWidget())
187 187 << "updateTimeZone: not compatible with multiple events selected";
188 188 }
189 189 }
190 190
191 191 QVector<SqpRange> getGraphRanges(const std::shared_ptr<DBEvent> &event)
192 192 {
193 193 // Retrieves the range of each product and the maximum size
194 194 QVector<SqpRange> graphRanges;
195 195 double maxDt = 0;
196 196 for (auto eventProduct : event->getEventProducts()) {
197 197 SqpRange eventRange;
198 198 eventRange.m_TStart = eventProduct.getTStart();
199 199 eventRange.m_TEnd = eventProduct.getTEnd();
200 200 graphRanges << eventRange;
201 201
202 202 auto dt = eventRange.m_TEnd - eventRange.m_TStart;
203 203 if (dt > maxDt) {
204 204 maxDt = dt;
205 205 }
206 206 }
207 207
208 208 // Adds the marge
209 209 maxDt *= (100.0 + EVENT_RANGE_MARGE) / 100.0;
210 210
211 211 // Corrects the graph ranges so that they all have the same size
212 212 QVector<SqpRange> correctedGraphRanges;
213 213 for (auto range : graphRanges) {
214 214 auto dt = range.m_TEnd - range.m_TStart;
215 215 auto diff = qAbs((maxDt - dt) / 2.0);
216 216
217 217 SqpRange correctedRange;
218 218 correctedRange.m_TStart = range.m_TStart - diff;
219 219 correctedRange.m_TEnd = range.m_TEnd + diff;
220 220
221 221 correctedGraphRanges << correctedRange;
222 222 }
223 223
224 224 return correctedGraphRanges;
225 225 }
226 226
227 void updateForGraphMode(QTreeView *treeView)
227 void updateForGraphMode(CatalogueEventsWidget *catalogueEventWidget)
228 228 {
229 auto selectedRows = treeView->selectionModel()->selectedRows();
229 auto selectedRows = catalogueEventWidget->ui->treeView->selectionModel()->selectedRows();
230 230 if (selectedRows.count() != 1) {
231 231 qCWarning(LOG_CatalogueEventsWidget())
232 232 << "updateGraphMode: not compatible with multiple events selected";
233 233 return;
234 234 }
235 235
236 236 if (!m_VisualizationWidget) {
237 237 qCWarning(LOG_CatalogueEventsWidget())
238 238 << "updateGraphMode: visualization widget not found";
239 239 return;
240 240 }
241 241
242 242 auto event = m_Model->getEvent(selectedRows.first());
243 243 if (!event) {
244 244 // A event product is probably selected
245 245 qCInfo(LOG_CatalogueEventsWidget()) << "updateGraphMode: no events are selected";
246 246 return;
247 247 }
248 248
249 249 auto tab = m_VisualizationWidget->currentTabWidget();
250 250 if (!tab) {
251 251 qCWarning(LOG_CatalogueEventsWidget())
252 252 << "updateGraphMode: no tab found in the visualization";
253 253 return;
254 254 }
255 255
256 256 auto zone = tab->getZoneWithName(m_ZoneForGraphMode);
257 257 if (!zone) {
258 258 qCWarning(LOG_CatalogueEventsWidget()) << "updateGraphMode: zone not found";
259 259 return;
260 260 }
261 261
262 262 // Close the previous graph and delete the asociated variables
263 263 for (auto graph : m_CustomGraphs) {
264 264 graph->close();
265 265 auto variables = graph->variables().toVector();
266 266
267 267 QMetaObject::invokeMethod(&sqpApp->variableController(), "deleteVariables",
268 268 Qt::QueuedConnection,
269 269 Q_ARG(QVector<std::shared_ptr<Variable> >, variables));
270 270 }
271 271 m_CustomGraphs.clear();
272 272
273 273 // Calculates the range of each graph which will be created
274 274 auto graphRange = getGraphRanges(event);
275 275
276 276 // Loops through the event products and create the graph
277 277 auto itRange = graphRange.cbegin();
278 278 for (auto eventProduct : event->getEventProducts()) {
279 279 auto productId = eventProduct.getProductId();
280 280
281 281 auto range = *itRange;
282 282 ++itRange;
283 283
284 284 SqpRange productRange;
285 285 productRange.m_TStart = eventProduct.getTStart();
286 286 productRange.m_TEnd = eventProduct.getTEnd();
287 287
288 auto context = new QObject{treeView};
288 auto context = new QObject{catalogueEventWidget};
289 289 QObject::connect(
290 290 &sqpApp->variableController(), &VariableController::variableAdded, context,
291 [this, zone, context, range, productRange, productId](auto variable) {
291 [this, catalogueEventWidget, zone, context, event, range, productRange,
292 productId](auto variable) {
292 293
293 294 if (variable->metadata().value(DataSourceItem::ID_DATA_KEY).toString()
294 295 == productId) {
295 296 auto graph = zone->createGraph(variable);
296 297 graph->setAutoRangeOnVariableInitialization(false);
297 298
298 graph->addSelectionZones({productRange});
299 auto selectionZone
300 = graph->addSelectionZone(event->getName(), productRange);
301 emit catalogueEventWidget->selectionZoneAdded(event, productId,
302 selectionZone);
299 303 m_CustomGraphs << graph;
300 304
301 305 graph->setGraphRange(range, true);
302 306
303 307 // Removes the graph from the graph list if it is closed manually
304 308 QObject::connect(graph, &VisualizationGraphWidget::destroyed,
305 309 [this, graph]() { m_CustomGraphs.removeAll(graph); });
306 310
307 311 delete context; // removes the connection
308 312 }
309 313 },
310 314 Qt::QueuedConnection);
311 315
312 316 QMetaObject::invokeMethod(&sqpApp->dataSourceController(),
313 317 "requestVariableFromProductIdKey", Qt::QueuedConnection,
314 318 Q_ARG(QString, productId));
315 319 }
316 320 }
317 321
318 322 void getSelectedItems(
319 323 QTreeView *treeView, QVector<std::shared_ptr<DBEvent> > &events,
320 324 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > &eventProducts)
321 325 {
322 326 for (auto rowIndex : treeView->selectionModel()->selectedRows()) {
323 327 auto itemType = m_Model->itemTypeOf(rowIndex);
324 328 if (itemType == CatalogueEventsModel::ItemType::Event) {
325 329 events << m_Model->getEvent(rowIndex);
326 330 }
327 331 else if (itemType == CatalogueEventsModel::ItemType::EventProduct) {
328 332 eventProducts << qMakePair(m_Model->getParentEvent(rowIndex),
329 333 m_Model->getEventProduct(rowIndex));
330 334 }
331 335 }
332 336 }
333 337 };
334 338
335 339 CatalogueEventsWidget::CatalogueEventsWidget(QWidget *parent)
336 340 : QWidget(parent),
337 341 ui(new Ui::CatalogueEventsWidget),
338 342 impl{spimpl::make_unique_impl<CatalogueEventsWidgetPrivate>()}
339 343 {
340 344 ui->setupUi(this);
341 345
342 346 impl->m_Model = new CatalogueEventsModel{this};
343 347 ui->treeView->setModel(impl->m_Model);
344 348
345 349 ui->treeView->setSortingEnabled(true);
346 350 ui->treeView->setDragDropMode(QAbstractItemView::DragDrop);
347 351 ui->treeView->setDragEnabled(true);
348 352
349 353 connect(ui->btnTime, &QToolButton::clicked, [this](auto checked) {
350 354 if (checked) {
351 355 ui->btnChart->setChecked(false);
352 356 impl->m_ZonesForTimeMode
353 357 = impl->selectZone(this, impl->m_ZonesForTimeMode, true,
354 358 this->mapToGlobal(ui->btnTime->frameGeometry().center()));
355 359
356 360 impl->updateForTimeMode(ui->treeView);
357 361 }
358 362 });
359 363
360 364 connect(ui->btnChart, &QToolButton::clicked, [this](auto checked) {
361 365 if (checked) {
362 366 ui->btnTime->setChecked(false);
363 367 impl->m_ZoneForGraphMode
364 368 = impl->selectZone(this, {impl->m_ZoneForGraphMode}, false,
365 369 this->mapToGlobal(ui->btnChart->frameGeometry().center()))
366 370 .value(0);
367 371
368 impl->updateForGraphMode(ui->treeView);
372 impl->updateForGraphMode(this);
369 373 }
370 374 });
371 375
372 376 connect(ui->btnRemove, &QToolButton::clicked, [this]() {
373 377 QVector<std::shared_ptr<DBEvent> > events;
374 378 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > eventProducts;
375 379 impl->getSelectedItems(ui->treeView, events, eventProducts);
376 380
377 381 if (!events.isEmpty() && eventProducts.isEmpty()) {
378 382
379 383 if (QMessageBox::warning(this, tr("Remove Event(s)"),
380 384 tr("The selected event(s) will be permanently removed "
381 385 "from the repository!\nAre you sure you want to continue?"),
382 386 QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
383 387 == QMessageBox::Yes) {
384 388
385 389 for (auto event : events) {
386 390 sqpApp->catalogueController().removeEvent(event);
387 391 impl->removeEvent(event, ui->treeView);
388 392 }
393
394 emit this->eventsRemoved(events);
389 395 }
390 396 }
391 397 });
392 398
393 399 connect(ui->treeView, &QTreeView::clicked, this, &CatalogueEventsWidget::emitSelection);
394 400 connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this,
395 401 &CatalogueEventsWidget::emitSelection);
396 402
397 403 ui->btnRemove->setEnabled(false); // Disabled by default when nothing is selected
398 404 connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, [this]() {
399 405 auto isNotMultiSelection = ui->treeView->selectionModel()->selectedRows().count() <= 1;
400 406 ui->btnChart->setEnabled(isNotMultiSelection);
401 407 ui->btnTime->setEnabled(isNotMultiSelection);
402 408
403 409 if (isNotMultiSelection && ui->btnTime->isChecked()) {
404 410 impl->updateForTimeMode(ui->treeView);
405 411 }
406 412 else if (isNotMultiSelection && ui->btnChart->isChecked()) {
407 impl->updateForGraphMode(ui->treeView);
413 impl->updateForGraphMode(this);
408 414 }
409 415
410 416 QVector<std::shared_ptr<DBEvent> > events;
411 417 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > eventProducts;
412 418 impl->getSelectedItems(ui->treeView, events, eventProducts);
413 419 ui->btnRemove->setEnabled(!events.isEmpty() && eventProducts.isEmpty());
414 420 });
415 421
416 422 ui->treeView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
417 423 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Tags,
418 424 QHeaderView::Stretch);
419 425 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Validation,
420 426 QHeaderView::Fixed);
421 427 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Name,
422 428 QHeaderView::Interactive);
423 429 ui->treeView->header()->resizeSection((int)CatalogueEventsModel::Column::Validation,
424 430 VALIDATION_COLUMN_SIZE);
425 431 ui->treeView->header()->setSortIndicatorShown(true);
426 432
427 433 connect(impl->m_Model, &CatalogueEventsModel::modelSorted, [this]() {
428 434 auto allEvents = impl->m_Model->events();
429 435 for (auto event : allEvents) {
430 436 setEventChanges(event, sqpApp->catalogueController().eventHasChanges(event));
431 437 }
432 438 });
433 439
434 440 populateWithAllEvents();
435 441 }
436 442
437 443 CatalogueEventsWidget::~CatalogueEventsWidget()
438 444 {
439 445 delete ui;
440 446 }
441 447
442 448 void CatalogueEventsWidget::setVisualizationWidget(VisualizationWidget *visualization)
443 449 {
444 450 impl->m_VisualizationWidget = visualization;
445 451 }
446 452
447 453 void CatalogueEventsWidget::addEvent(const std::shared_ptr<DBEvent> &event)
448 454 {
449 455 impl->addEvent(event, ui->treeView);
450 456 }
451 457
452 458 void CatalogueEventsWidget::setEventChanges(const std::shared_ptr<DBEvent> &event, bool hasChanges)
453 459 {
454 460 impl->m_Model->refreshEvent(event);
455 461
456 462 auto eventIndex = impl->m_Model->indexOf(event);
457 463 auto validationIndex
458 464 = eventIndex.sibling(eventIndex.row(), (int)CatalogueEventsModel::Column::Validation);
459 465
460 466 if (validationIndex.isValid()) {
461 467 if (hasChanges) {
462 468 if (ui->treeView->indexWidget(validationIndex) == nullptr) {
463 469 auto widget = CatalogueExplorerHelper::buildValidationWidget(
464 470 ui->treeView,
465 471 [this, event]() {
466 472 sqpApp->catalogueController().saveEvent(event);
467 473 setEventChanges(event, false);
468 474 },
469 475 [this, event]() {
470 476 bool removed = false;
471 477 sqpApp->catalogueController().discardEvent(event, removed);
472 478 if (removed) {
473 479 impl->m_Model->removeEvent(event);
474 480 }
475 481 else {
476 482 setEventChanges(event, false);
477 483 impl->m_Model->refreshEvent(event, true);
478 484 }
479 485 emitSelection();
480 486 });
481 487 ui->treeView->setIndexWidget(validationIndex, widget);
482 488 }
483 489 }
484 490 else {
485 491 // Note: the widget is destroyed
486 492 ui->treeView->setIndexWidget(validationIndex, nullptr);
487 493 }
488 494 }
489 495 else {
490 496 qCWarning(LOG_CatalogueEventsWidget())
491 497 << "setEventChanges: the event is not displayed in the model.";
492 498 }
493 499 }
494 500
495 501 QVector<std::shared_ptr<DBCatalogue> > CatalogueEventsWidget::displayedCatalogues() const
496 502 {
497 503 return impl->m_DisplayedCatalogues;
498 504 }
499 505
500 506 bool CatalogueEventsWidget::isAllEventsDisplayed() const
501 507 {
502 508 return impl->m_AllEventDisplayed;
503 509 }
504 510
505 511 bool CatalogueEventsWidget::isEventDisplayed(const std::shared_ptr<DBEvent> &event) const
506 512 {
507 513 return impl->m_Model->indexOf(event).isValid();
508 514 }
509 515
516 void CatalogueEventsWidget::refreshEvent(const std::shared_ptr<DBEvent> &event)
517 {
518 impl->m_Model->refreshEvent(event, true);
519 }
520
510 521 void CatalogueEventsWidget::populateWithCatalogues(
511 522 const QVector<std::shared_ptr<DBCatalogue> > &catalogues)
512 523 {
513 524 impl->m_DisplayedCatalogues = catalogues;
514 525 impl->m_AllEventDisplayed = false;
515 526
516 527 QSet<QUuid> eventIds;
517 528 QVector<std::shared_ptr<DBEvent> > events;
518 529
519 530 for (auto catalogue : catalogues) {
520 531 auto catalogueEvents = sqpApp->catalogueController().retrieveEventsFromCatalogue(catalogue);
521 532 for (auto event : catalogueEvents) {
522 533 if (!eventIds.contains(event->getUniqId())) {
523 534 events << event;
524 535 eventIds.insert(event->getUniqId());
525 536 }
526 537 }
527 538 }
528 539
529 540 impl->setEvents(events, this);
530 541 }
531 542
532 543 void CatalogueEventsWidget::populateWithAllEvents()
533 544 {
534 545 impl->m_DisplayedCatalogues.clear();
535 546 impl->m_AllEventDisplayed = true;
536 547
537 548 auto allEvents = sqpApp->catalogueController().retrieveAllEvents();
538 549
539 550 QVector<std::shared_ptr<DBEvent> > events;
540 551 for (auto event : allEvents) {
541 552 events << event;
542 553 }
543 554
544 555 impl->setEvents(events, this);
545 556 }
546 557
547 558 void CatalogueEventsWidget::clear()
548 559 {
549 560 impl->m_DisplayedCatalogues.clear();
550 561 impl->m_AllEventDisplayed = false;
551 562 impl->setEvents({}, this);
552 563 }
553 564
554 565 void CatalogueEventsWidget::refresh()
555 566 {
556 567 if (isAllEventsDisplayed()) {
557 568 populateWithAllEvents();
558 569 }
559 570 else if (!impl->m_DisplayedCatalogues.isEmpty()) {
560 571 populateWithCatalogues(impl->m_DisplayedCatalogues);
561 572 }
562 573 }
563 574
564 575 void CatalogueEventsWidget::emitSelection()
565 576 {
566 577 QVector<std::shared_ptr<DBEvent> > events;
567 578 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > eventProducts;
568 579 impl->getSelectedItems(ui->treeView, events, eventProducts);
569 580
570 581 if (!events.isEmpty() && eventProducts.isEmpty()) {
571 582 emit eventsSelected(events);
572 583 }
573 584 else if (events.isEmpty() && !eventProducts.isEmpty()) {
574 585 emit eventProductsSelected(eventProducts);
575 586 }
576 587 else {
577 588 emit selectionCleared();
578 589 }
579 590 }
@@ -1,126 +1,171
1 1 #include "Catalogue/CatalogueExplorer.h"
2 2 #include "ui_CatalogueExplorer.h"
3 3
4 4 #include <Catalogue/CatalogueActionManager.h>
5 5 #include <Catalogue/CatalogueController.h>
6 6 #include <SqpApplication.h>
7 #include <Visualization/VisualizationGraphWidget.h>
8 #include <Visualization/VisualizationSelectionZoneItem.h>
7 9 #include <Visualization/VisualizationWidget.h>
8 10
9 11 #include <DBCatalogue.h>
10 12 #include <DBEvent.h>
13 #include <DBEventProduct.h>
14
15 #include <unordered_map>
11 16
12 17 struct CatalogueExplorer::CatalogueExplorerPrivate {
13 18 CatalogueActionManager m_ActionManager;
19 std::unordered_map<std::shared_ptr<DBEvent>, QVector<VisualizationSelectionZoneItem *> >
20 m_SelectionZonesPerEvents;
14 21
15 22 CatalogueExplorerPrivate(CatalogueExplorer *catalogueExplorer)
16 23 : m_ActionManager(catalogueExplorer)
17 24 {
18 25 }
19 26 };
20 27
21 28 CatalogueExplorer::CatalogueExplorer(QWidget *parent)
22 29 : QDialog(parent, Qt::Dialog | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint),
23 30 ui(new Ui::CatalogueExplorer),
24 31 impl{spimpl::make_unique_impl<CatalogueExplorerPrivate>(this)}
25 32 {
26 33 ui->setupUi(this);
27 34
28 35 impl->m_ActionManager.installSelectionZoneActions();
29 36
37 // Updates events and inspector when something is selected in the catalogue widget
30 38 connect(ui->catalogues, &CatalogueSideBarWidget::catalogueSelected, [this](auto catalogues) {
31 39 if (catalogues.count() == 1) {
32 40 ui->inspector->setCatalogue(catalogues.first());
33 41 }
34 42 else {
35 43 ui->inspector->showPage(CatalogueInspectorWidget::Page::Empty);
36 44 }
37 45
38 46 ui->events->populateWithCatalogues(catalogues);
39 47 });
40 48
41 49 connect(ui->catalogues, &CatalogueSideBarWidget::databaseSelected, [this](auto databases) {
42 50 ui->inspector->showPage(CatalogueInspectorWidget::Page::Empty);
43 51 });
44 52
45 53 connect(ui->catalogues, &CatalogueSideBarWidget::trashSelected, [this]() {
46 54 ui->inspector->showPage(CatalogueInspectorWidget::Page::Empty);
47 55 ui->events->clear();
48 56 });
49 57
50 58 connect(ui->catalogues, &CatalogueSideBarWidget::allEventsSelected, [this]() {
51 59 ui->inspector->showPage(CatalogueInspectorWidget::Page::Empty);
52 60 ui->events->populateWithAllEvents();
53 61 });
54 62
55 63 connect(ui->catalogues, &CatalogueSideBarWidget::databaseSelected, [this](auto databaseList) {
56 64 QVector<std::shared_ptr<DBCatalogue> > catalogueList;
57 65 for (auto database : databaseList) {
58 66 catalogueList.append(ui->catalogues->getCatalogues(database));
59 67 }
60 68 ui->events->populateWithCatalogues(catalogueList);
61 69 ui->inspector->showPage(CatalogueInspectorWidget::Page::Empty);
62 70 });
63 71
64 72 connect(ui->catalogues, &CatalogueSideBarWidget::selectionCleared, [this]() {
65 73 ui->inspector->showPage(CatalogueInspectorWidget::Page::Empty);
66 74 ui->events->clear();
67 75 });
68 76
77 // Updates the inspectot when something is selected in the events
69 78 connect(ui->events, &CatalogueEventsWidget::eventsSelected, [this](auto events) {
70 79 if (events.count() == 1) {
71 80 ui->inspector->setEvent(events.first());
72 81 }
73 82 else {
74 83 ui->inspector->showPage(CatalogueInspectorWidget::Page::Empty);
75 84 }
76 85 });
77 86
78 87 connect(ui->events, &CatalogueEventsWidget::eventProductsSelected, [this](auto eventProducts) {
79 88 if (eventProducts.count() == 1) {
80 89 ui->inspector->setEventProduct(eventProducts.first().first,
81 90 eventProducts.first().second);
82 91 }
83 92 else {
84 93 ui->inspector->showPage(CatalogueInspectorWidget::Page::Empty);
85 94 }
86 95 });
87 96
88 97 connect(ui->events, &CatalogueEventsWidget::selectionCleared,
89 98 [this]() { ui->inspector->showPage(CatalogueInspectorWidget::Page::Empty); });
90 99
100 // Manage Selection Zones associated to events
101 connect(ui->events, &CatalogueEventsWidget::selectionZoneAdded,
102 [this](auto event, auto productId, auto zone) {
103 impl->m_SelectionZonesPerEvents[event] << zone;
104 connect(zone, &VisualizationSelectionZoneItem::rangeEdited,
105 [event, productId, this](auto range) {
106 auto productList = event->getEventProducts();
107 for (auto &product : productList) {
108 if (product.getProductId() == productId) {
109 product.setTStart(range.m_TStart);
110 product.setTEnd(range.m_TEnd);
111 }
112 }
113 event->setEventProducts(productList);
114 sqpApp->catalogueController().updateEvent(event);
115 ui->events->refreshEvent(event);
116 ui->events->setEventChanges(event, true);
117 ui->inspector->refresh();
118 });
119 });
120
121 connect(ui->events, &CatalogueEventsWidget::eventsRemoved, [this](auto events) {
122 for (auto event : events) {
123 auto associatedSelectionZonesIt = impl->m_SelectionZonesPerEvents.find(event);
124 if (associatedSelectionZonesIt != impl->m_SelectionZonesPerEvents.cend()) {
125 for (auto selectionZone : associatedSelectionZonesIt->second) {
126 auto parentGraph = selectionZone->parentGraphWidget();
127 parentGraph->removeSelectionZone(selectionZone);
128 }
129
130 impl->m_SelectionZonesPerEvents.erase(event);
131 }
132 }
133 });
134
135 // Updates changes from the inspector
91 136 connect(ui->inspector, &CatalogueInspectorWidget::catalogueUpdated, [this](auto catalogue) {
92 137 sqpApp->catalogueController().updateCatalogue(catalogue);
93 138 ui->catalogues->setCatalogueChanges(catalogue, true);
94 139 });
95 140
96 141 connect(ui->inspector, &CatalogueInspectorWidget::eventUpdated, [this](auto event) {
97 142 sqpApp->catalogueController().updateEvent(event);
98 143 ui->events->setEventChanges(event, true);
99 144 });
100 145
101 146 connect(ui->inspector, &CatalogueInspectorWidget::eventProductUpdated,
102 147 [this](auto event, auto eventProduct) {
103 148 sqpApp->catalogueController().updateEventProduct(eventProduct);
104 149 ui->events->setEventChanges(event, true);
105 150 });
106 151 }
107 152
108 153 CatalogueExplorer::~CatalogueExplorer()
109 154 {
110 155 delete ui;
111 156 }
112 157
113 158 void CatalogueExplorer::setVisualizationWidget(VisualizationWidget *visualization)
114 159 {
115 160 ui->events->setVisualizationWidget(visualization);
116 161 }
117 162
118 163 CatalogueEventsWidget &CatalogueExplorer::eventsWidget() const
119 164 {
120 165 return *ui->events;
121 166 }
122 167
123 168 CatalogueSideBarWidget &CatalogueExplorer::sideBarWidget() const
124 169 {
125 170 return *ui->catalogues;
126 171 }
@@ -1,211 +1,227
1 1 #include "Catalogue/CatalogueInspectorWidget.h"
2 2 #include "ui_CatalogueInspectorWidget.h"
3 3
4 4 #include <Common/DateUtils.h>
5 5 #include <DBCatalogue.h>
6 6 #include <DBEvent.h>
7 7 #include <DBEventProduct.h>
8 8 #include <DBTag.h>
9 9
10 10 struct CatalogueInspectorWidget::CatalogueInspectorWidgetPrivate {
11 11 std::shared_ptr<DBCatalogue> m_DisplayedCatalogue = nullptr;
12 12 std::shared_ptr<DBEvent> m_DisplayedEvent = nullptr;
13 13 std::shared_ptr<DBEventProduct> m_DisplayedEventProduct = nullptr;
14 14
15 15 void connectCatalogueUpdateSignals(CatalogueInspectorWidget *inspector,
16 16 Ui::CatalogueInspectorWidget *ui);
17 17 void connectEventUpdateSignals(CatalogueInspectorWidget *inspector,
18 18 Ui::CatalogueInspectorWidget *ui);
19 19 };
20 20
21 21 CatalogueInspectorWidget::CatalogueInspectorWidget(QWidget *parent)
22 22 : QWidget(parent),
23 23 ui(new Ui::CatalogueInspectorWidget),
24 24 impl{spimpl::make_unique_impl<CatalogueInspectorWidgetPrivate>()}
25 25 {
26 26 ui->setupUi(this);
27 27 showPage(Page::Empty);
28 28
29 29 impl->connectCatalogueUpdateSignals(this, ui);
30 30 impl->connectEventUpdateSignals(this, ui);
31 31 }
32 32
33 33 CatalogueInspectorWidget::~CatalogueInspectorWidget()
34 34 {
35 35 delete ui;
36 36 }
37 37
38 38 void CatalogueInspectorWidget::CatalogueInspectorWidgetPrivate::connectCatalogueUpdateSignals(
39 39 CatalogueInspectorWidget *inspector, Ui::CatalogueInspectorWidget *ui)
40 40 {
41 41 connect(ui->leCatalogueName, &QLineEdit::editingFinished, [ui, inspector, this]() {
42 42 if (ui->leCatalogueName->text() != m_DisplayedCatalogue->getName()) {
43 43 m_DisplayedCatalogue->setName(ui->leCatalogueName->text());
44 44 emit inspector->catalogueUpdated(m_DisplayedCatalogue);
45 45 }
46 46 });
47 47
48 48 connect(ui->leCatalogueAuthor, &QLineEdit::editingFinished, [ui, inspector, this]() {
49 49 if (ui->leCatalogueAuthor->text() != m_DisplayedCatalogue->getAuthor()) {
50 50 m_DisplayedCatalogue->setAuthor(ui->leCatalogueAuthor->text());
51 51 emit inspector->catalogueUpdated(m_DisplayedCatalogue);
52 52 }
53 53 });
54 54 }
55 55
56 56 void CatalogueInspectorWidget::CatalogueInspectorWidgetPrivate::connectEventUpdateSignals(
57 57 CatalogueInspectorWidget *inspector, Ui::CatalogueInspectorWidget *ui)
58 58 {
59 59 connect(ui->leEventName, &QLineEdit::editingFinished, [ui, inspector, this]() {
60 60 if (ui->leEventName->text() != m_DisplayedEvent->getName()) {
61 61 m_DisplayedEvent->setName(ui->leEventName->text());
62 62 emit inspector->eventUpdated(m_DisplayedEvent);
63 63 }
64 64 });
65 65
66 66 connect(ui->leEventTags, &QLineEdit::editingFinished, [ui, inspector, this]() {
67 67 auto tags = ui->leEventTags->text().split(QRegExp("\\s+"), QString::SkipEmptyParts);
68 68 std::list<QString> tagNames;
69 69 for (auto tag : tags) {
70 70 tagNames.push_back(tag);
71 71 }
72 72
73 73 if (m_DisplayedEvent->getTagsNames() != tagNames) {
74 74 m_DisplayedEvent->setTagsNames(tagNames);
75 75 emit inspector->eventUpdated(m_DisplayedEvent);
76 76 }
77 77 });
78 78
79 79 connect(ui->leEventProduct, &QLineEdit::editingFinished, [ui, inspector, this]() {
80 80 if (ui->leEventProduct->text() != m_DisplayedEventProduct->getProductId()) {
81 81 auto oldProductId = m_DisplayedEventProduct->getProductId();
82 82 m_DisplayedEventProduct->setProductId(ui->leEventProduct->text());
83 83
84 84 auto eventProducts = m_DisplayedEvent->getEventProducts();
85 85 for (auto &eventProduct : eventProducts) {
86 86 if (eventProduct.getProductId() == oldProductId) {
87 87 eventProduct.setProductId(m_DisplayedEventProduct->getProductId());
88 88 }
89 89 }
90 90 m_DisplayedEvent->setEventProducts(eventProducts);
91 91
92 92 emit inspector->eventUpdated(m_DisplayedEvent);
93 93 }
94 94 });
95 95
96 96 connect(ui->dateTimeEventTStart, &QDateTimeEdit::editingFinished, [ui, inspector, this]() {
97 97 auto time = DateUtils::secondsSinceEpoch(ui->dateTimeEventTStart->dateTime());
98 98 if (time != m_DisplayedEventProduct->getTStart()) {
99 99 m_DisplayedEventProduct->setTStart(time);
100 100
101 101 auto eventProducts = m_DisplayedEvent->getEventProducts();
102 102 for (auto &eventProduct : eventProducts) {
103 103 if (eventProduct.getProductId() == m_DisplayedEventProduct->getProductId()) {
104 104 eventProduct.setTStart(m_DisplayedEventProduct->getTStart());
105 105 }
106 106 }
107 107 m_DisplayedEvent->setEventProducts(eventProducts);
108 108
109 109 emit inspector->eventUpdated(m_DisplayedEvent);
110 110 }
111 111 });
112 112
113 113 connect(ui->dateTimeEventTEnd, &QDateTimeEdit::editingFinished, [ui, inspector, this]() {
114 114 auto time = DateUtils::secondsSinceEpoch(ui->dateTimeEventTEnd->dateTime());
115 115 if (time != m_DisplayedEventProduct->getTEnd()) {
116 116 m_DisplayedEventProduct->setTEnd(time);
117 117
118 118 auto eventProducts = m_DisplayedEvent->getEventProducts();
119 119 for (auto &eventProduct : eventProducts) {
120 120 if (eventProduct.getProductId() == m_DisplayedEventProduct->getProductId()) {
121 121 eventProduct.setTEnd(m_DisplayedEventProduct->getTEnd());
122 122 }
123 123 }
124 124 m_DisplayedEvent->setEventProducts(eventProducts);
125 125
126 126 emit inspector->eventUpdated(m_DisplayedEvent);
127 127 }
128 128 });
129 129 }
130 130
131 131 void CatalogueInspectorWidget::showPage(CatalogueInspectorWidget::Page page)
132 132 {
133 133 ui->stackedWidget->setCurrentIndex(static_cast<int>(page));
134 134 }
135 135
136 136 CatalogueInspectorWidget::Page CatalogueInspectorWidget::currentPage() const
137 137 {
138 138 return static_cast<Page>(ui->stackedWidget->currentIndex());
139 139 }
140 140
141 141 void CatalogueInspectorWidget::setEvent(const std::shared_ptr<DBEvent> &event)
142 142 {
143 143 impl->m_DisplayedEvent = event;
144 144
145 145 blockSignals(true);
146 146
147 147 showPage(Page::EventProperties);
148 148 ui->leEventName->setEnabled(true);
149 149 ui->leEventName->setText(event->getName());
150 150 ui->leEventProduct->setEnabled(false);
151 151 ui->leEventProduct->setText(
152 152 QString::number(event->getEventProducts().size()).append(" product(s)"));
153 153
154 154 QString tagList;
155 155 auto tags = event->getTagsNames();
156 156 for (auto tag : tags) {
157 157 tagList += tag;
158 158 tagList += ' ';
159 159 }
160 160
161 161 ui->leEventTags->setEnabled(true);
162 162 ui->leEventTags->setText(tagList);
163 163
164 164 ui->dateTimeEventTStart->setEnabled(false);
165 165 ui->dateTimeEventTEnd->setEnabled(false);
166 166
167 167 ui->dateTimeEventTStart->setDateTime(DateUtils::dateTime(event->getTStart()));
168 168 ui->dateTimeEventTEnd->setDateTime(DateUtils::dateTime(event->getTEnd()));
169 169
170 170 blockSignals(false);
171 171 }
172 172
173 173 void CatalogueInspectorWidget::setEventProduct(const std::shared_ptr<DBEvent> &event,
174 174 const std::shared_ptr<DBEventProduct> &eventProduct)
175 175 {
176 176
177 177 impl->m_DisplayedEvent = event;
178 178 impl->m_DisplayedEventProduct = eventProduct;
179 179
180 180 blockSignals(true);
181 181
182 182 showPage(Page::EventProperties);
183 183 ui->leEventName->setEnabled(false);
184 184 ui->leEventName->setText(event->getName());
185 185 ui->leEventProduct->setEnabled(false);
186 186 ui->leEventProduct->setText(eventProduct->getProductId());
187 187
188 188 ui->leEventTags->setEnabled(false);
189 189 ui->leEventTags->clear();
190 190
191 191 ui->dateTimeEventTStart->setEnabled(true);
192 192 ui->dateTimeEventTEnd->setEnabled(true);
193 193
194 194 ui->dateTimeEventTStart->setDateTime(DateUtils::dateTime(eventProduct->getTStart()));
195 195 ui->dateTimeEventTEnd->setDateTime(DateUtils::dateTime(eventProduct->getTEnd()));
196 196
197 197 blockSignals(false);
198 198 }
199 199
200 200 void CatalogueInspectorWidget::setCatalogue(const std::shared_ptr<DBCatalogue> &catalogue)
201 201 {
202 202 impl->m_DisplayedCatalogue = catalogue;
203 203
204 204 blockSignals(true);
205 205
206 206 showPage(Page::CatalogueProperties);
207 207 ui->leCatalogueName->setText(catalogue->getName());
208 208 ui->leCatalogueAuthor->setText(catalogue->getAuthor());
209 209
210 210 blockSignals(false);
211 211 }
212
213 void CatalogueInspectorWidget::refresh()
214 {
215 switch (static_cast<Page>(ui->stackedWidget->currentIndex())) {
216 case Page::CatalogueProperties:
217 setCatalogue(impl->m_DisplayedCatalogue);
218 break;
219 case Page::EventProperties: {
220 auto isEventShowed = ui->leEventName->isEnabled();
221 setEvent(impl->m_DisplayedEvent);
222 if (!isEventShowed && impl->m_DisplayedEvent) {
223 setEventProduct(impl->m_DisplayedEvent, impl->m_DisplayedEventProduct);
224 }
225 }
226 }
227 }
@@ -1,1041 +1,1055
1 1 #include "Visualization/VisualizationGraphWidget.h"
2 2 #include "Visualization/IVisualizationWidgetVisitor.h"
3 3 #include "Visualization/VisualizationCursorItem.h"
4 4 #include "Visualization/VisualizationDefs.h"
5 5 #include "Visualization/VisualizationGraphHelper.h"
6 6 #include "Visualization/VisualizationGraphRenderingDelegate.h"
7 7 #include "Visualization/VisualizationMultiZoneSelectionDialog.h"
8 8 #include "Visualization/VisualizationSelectionZoneItem.h"
9 9 #include "Visualization/VisualizationSelectionZoneManager.h"
10 10 #include "Visualization/VisualizationWidget.h"
11 11 #include "Visualization/VisualizationZoneWidget.h"
12 12 #include "ui_VisualizationGraphWidget.h"
13 13
14 14 #include <Actions/ActionsGuiController.h>
15 15 #include <Common/MimeTypesDef.h>
16 16 #include <Data/ArrayData.h>
17 17 #include <Data/IDataSeries.h>
18 18 #include <Data/SpectrogramSeries.h>
19 19 #include <DragAndDrop/DragDropGuiController.h>
20 20 #include <Settings/SqpSettingsDefs.h>
21 21 #include <SqpApplication.h>
22 22 #include <Time/TimeController.h>
23 23 #include <Variable/Variable.h>
24 24 #include <Variable/VariableController.h>
25 25
26 26 #include <unordered_map>
27 27
28 28 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
29 29
30 30 namespace {
31 31
32 32 /// Key pressed to enable drag&drop in all modes
33 33 const auto DRAG_DROP_MODIFIER = Qt::AltModifier;
34 34
35 35 /// Key pressed to enable zoom on horizontal axis
36 36 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier;
37 37
38 38 /// Key pressed to enable zoom on vertical axis
39 39 const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier;
40 40
41 41 /// Speed of a step of a wheel event for a pan, in percentage of the axis range
42 42 const auto PAN_SPEED = 5;
43 43
44 44 /// Key pressed to enable a calibration pan
45 45 const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier;
46 46
47 47 /// Key pressed to enable multi selection of selection zones
48 48 const auto MULTI_ZONE_SELECTION_MODIFIER = Qt::ControlModifier;
49 49
50 50 /// Minimum size for the zoom box, in percentage of the axis range
51 51 const auto ZOOM_BOX_MIN_SIZE = 0.8;
52 52
53 53 /// Format of the dates appearing in the label of a cursor
54 54 const auto CURSOR_LABELS_DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd\nhh:mm:ss:zzz");
55 55
56 56 } // namespace
57 57
58 58 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
59 59
60 60 explicit VisualizationGraphWidgetPrivate(const QString &name)
61 61 : m_Name{name},
62 62 m_Flags{GraphFlag::EnableAll},
63 63 m_IsCalibration{false},
64 64 m_RenderingDelegate{nullptr}
65 65 {
66 66 }
67 67
68 68 void updateData(PlottablesMap &plottables, std::shared_ptr<Variable> variable,
69 69 const SqpRange &range)
70 70 {
71 71 VisualizationGraphHelper::updateData(plottables, variable, range);
72 72
73 73 // Prevents that data has changed to update rendering
74 74 m_RenderingDelegate->onPlotUpdated();
75 75 }
76 76
77 77 QString m_Name;
78 78 // 1 variable -> n qcpplot
79 79 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
80 80 GraphFlags m_Flags;
81 81 bool m_IsCalibration;
82 82 /// Delegate used to attach rendering features to the plot
83 83 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
84 84
85 85 QCPItemRect *m_DrawingZoomRect = nullptr;
86 86 QStack<QPair<QCPRange, QCPRange> > m_ZoomStack;
87 87
88 88 std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr;
89 89 std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr;
90 90
91 91 VisualizationSelectionZoneItem *m_DrawingZone = nullptr;
92 92 VisualizationSelectionZoneItem *m_HoveredZone = nullptr;
93 93 QVector<VisualizationSelectionZoneItem *> m_SelectionZones;
94 94
95 95 bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even
96 96
97 97 bool m_VariableAutoRangeOnInit = true;
98 98
99 99 void startDrawingRect(const QPoint &pos, QCustomPlot &plot)
100 100 {
101 101 removeDrawingRect(plot);
102 102
103 103 auto axisPos = posToAxisPos(pos, plot);
104 104
105 105 m_DrawingZoomRect = new QCPItemRect{&plot};
106 106 QPen p;
107 107 p.setWidth(2);
108 108 m_DrawingZoomRect->setPen(p);
109 109
110 110 m_DrawingZoomRect->topLeft->setCoords(axisPos);
111 111 m_DrawingZoomRect->bottomRight->setCoords(axisPos);
112 112 }
113 113
114 114 void removeDrawingRect(QCustomPlot &plot)
115 115 {
116 116 if (m_DrawingZoomRect) {
117 117 plot.removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot
118 118 m_DrawingZoomRect = nullptr;
119 119 plot.replot(QCustomPlot::rpQueuedReplot);
120 120 }
121 121 }
122 122
123 123 void startDrawingZone(const QPoint &pos, VisualizationGraphWidget *graph)
124 124 {
125 125 endDrawingZone(graph);
126 126
127 127 auto axisPos = posToAxisPos(pos, graph->plot());
128 128
129 129 m_DrawingZone = new VisualizationSelectionZoneItem{&graph->plot()};
130 130 m_DrawingZone->setRange(axisPos.x(), axisPos.x());
131 131 m_DrawingZone->setEditionEnabled(false);
132 132 }
133 133
134 134 void endDrawingZone(VisualizationGraphWidget *graph)
135 135 {
136 136 if (m_DrawingZone) {
137 137 auto drawingZoneRange = m_DrawingZone->range();
138 138 if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0) {
139 139 m_DrawingZone->setEditionEnabled(true);
140 140 addSelectionZone(m_DrawingZone);
141 141 }
142 142 else {
143 143 graph->plot().removeItem(m_DrawingZone); // the item is deleted by QCustomPlot
144 144 }
145 145
146 146 graph->plot().replot(QCustomPlot::rpQueuedReplot);
147 147 m_DrawingZone = nullptr;
148 148 }
149 149 }
150 150
151 151 void setSelectionZonesEditionEnabled(bool value)
152 152 {
153 153 for (auto s : m_SelectionZones) {
154 154 s->setEditionEnabled(value);
155 155 }
156 156 }
157 157
158 158 void addSelectionZone(VisualizationSelectionZoneItem *zone) { m_SelectionZones << zone; }
159 159
160 160 VisualizationSelectionZoneItem *selectionZoneAt(const QPoint &pos,
161 161 const QCustomPlot &plot) const
162 162 {
163 163 VisualizationSelectionZoneItem *selectionZoneItemUnderCursor = nullptr;
164 164 auto minDistanceToZone = -1;
165 165 for (auto zone : m_SelectionZones) {
166 166 auto distanceToZone = zone->selectTest(pos, false);
167 167 if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone)
168 168 && distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
169 169 selectionZoneItemUnderCursor = zone;
170 170 }
171 171 }
172 172
173 173 return selectionZoneItemUnderCursor;
174 174 }
175 175
176 176 QVector<VisualizationSelectionZoneItem *> selectionZonesAt(const QPoint &pos,
177 177 const QCustomPlot &plot) const
178 178 {
179 179 QVector<VisualizationSelectionZoneItem *> zones;
180 180 for (auto zone : m_SelectionZones) {
181 181 auto distanceToZone = zone->selectTest(pos, false);
182 182 if (distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
183 183 zones << zone;
184 184 }
185 185 }
186 186
187 187 return zones;
188 188 }
189 189
190 190 void moveSelectionZoneOnTop(VisualizationSelectionZoneItem *zone, QCustomPlot &plot)
191 191 {
192 192 if (!m_SelectionZones.isEmpty() && m_SelectionZones.last() != zone) {
193 193 zone->moveToTop();
194 194 m_SelectionZones.removeAll(zone);
195 195 m_SelectionZones.append(zone);
196 196 }
197 197 }
198 198
199 199 QPointF posToAxisPos(const QPoint &pos, QCustomPlot &plot) const
200 200 {
201 201 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
202 202 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
203 203 return QPointF{axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y())};
204 204 }
205 205
206 206 bool pointIsInAxisRect(const QPointF &axisPoint, QCustomPlot &plot) const
207 207 {
208 208 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
209 209 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
210 210 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
211 211 }
212 212 };
213 213
214 214 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
215 215 : VisualizationDragWidget{parent},
216 216 ui{new Ui::VisualizationGraphWidget},
217 217 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
218 218 {
219 219 ui->setupUi(this);
220 220
221 221 // 'Close' options : widget is deleted when closed
222 222 setAttribute(Qt::WA_DeleteOnClose);
223 223
224 224 // Set qcpplot properties :
225 225 // - zoom is enabled
226 226 // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation
227 227 ui->widget->setInteractions(QCP::iRangeZoom);
228 228 ui->widget->axisRect()->setRangeDrag(Qt::Horizontal | Qt::Vertical);
229 229
230 230 // The delegate must be initialized after the ui as it uses the plot
231 231 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
232 232
233 233 // Init the cursors
234 234 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
235 235 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
236 236 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
237 237 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
238 238
239 239 connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress);
240 240 connect(ui->widget, &QCustomPlot::mouseRelease, this,
241 241 &VisualizationGraphWidget::onMouseRelease);
242 242 connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove);
243 243 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
244 244 connect(ui->widget, &QCustomPlot::mouseDoubleClick, this,
245 245 &VisualizationGraphWidget::onMouseDoubleClick);
246 246 connect(ui->widget->xAxis, static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(
247 247 &QCPAxis::rangeChanged),
248 248 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
249 249
250 250 // Activates menu when right clicking on the graph
251 251 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
252 252 connect(ui->widget, &QCustomPlot::customContextMenuRequested, this,
253 253 &VisualizationGraphWidget::onGraphMenuRequested);
254 254
255 255 connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
256 256 &VariableController::onRequestDataLoading);
257 257
258 258 connect(&sqpApp->variableController(), &VariableController::updateVarDisplaying, this,
259 259 &VisualizationGraphWidget::onUpdateVarDisplaying);
260 260
261 261 #ifdef Q_OS_MAC
262 262 plot().setPlottingHint(QCP::phFastPolylines, true);
263 263 #endif
264 264 }
265 265
266 266
267 267 VisualizationGraphWidget::~VisualizationGraphWidget()
268 268 {
269 269 delete ui;
270 270 }
271 271
272 272 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
273 273 {
274 274 auto parent = parentWidget();
275 275 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
276 276 parent = parent->parentWidget();
277 277 }
278 278
279 279 return qobject_cast<VisualizationZoneWidget *>(parent);
280 280 }
281 281
282 282 VisualizationWidget *VisualizationGraphWidget::parentVisualizationWidget() const
283 283 {
284 284 auto parent = parentWidget();
285 285 while (parent != nullptr && !qobject_cast<VisualizationWidget *>(parent)) {
286 286 parent = parent->parentWidget();
287 287 }
288 288
289 289 return qobject_cast<VisualizationWidget *>(parent);
290 290 }
291 291
292 292 void VisualizationGraphWidget::setFlags(GraphFlags flags)
293 293 {
294 294 impl->m_Flags = std::move(flags);
295 295 }
296 296
297 297 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, SqpRange range)
298 298 {
299 299 /// Lambda used to set graph's units and range according to the variable passed in parameter
300 300 auto loadRange = [this](std::shared_ptr<Variable> variable, const SqpRange &range) {
301 301 impl->m_RenderingDelegate->setAxesUnits(*variable);
302 302
303 303 this->setFlags(GraphFlag::DisableAll);
304 304 setGraphRange(range);
305 305 this->setFlags(GraphFlag::EnableAll);
306 306
307 307 emit requestDataLoading({variable}, range, false);
308 308 };
309 309
310 310 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
311 311
312 312 // Calls update of graph's range and units when the data of the variable have been initialized.
313 313 // Note: we use QueuedConnection here as the update event must be called in the UI thread
314 314 connect(variable.get(), &Variable::dataInitialized, this,
315 315 [ varW = std::weak_ptr<Variable>{variable}, range, loadRange, this ]() {
316 316 if (auto var = varW.lock()) {
317 317 // If the variable is the first added in the graph, we load its range
318 318 auto firstVariableInGraph = range == INVALID_RANGE;
319 319 auto loadedRange = graphRange();
320 320 if (impl->m_VariableAutoRangeOnInit) {
321 321 loadedRange = firstVariableInGraph ? var->range() : range;
322 322 }
323 323 loadRange(var, loadedRange);
324 324 setYRange(var);
325 325 }
326 326 },
327 327 Qt::QueuedConnection);
328 328
329 329 // Uses delegate to create the qcpplot components according to the variable
330 330 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
331 331
332 332 // Sets graph properties
333 333 impl->m_RenderingDelegate->setGraphProperties(*variable, createdPlottables);
334 334
335 335 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
336 336
337 337 // If the variable already has its data loaded, load its units and its range in the graph
338 338 if (variable->dataSeries() != nullptr) {
339 339 loadRange(variable, range);
340 340 }
341 341
342 342 emit variableAdded(variable);
343 343 }
344 344
345 345 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
346 346 {
347 347 // Each component associated to the variable :
348 348 // - is removed from qcpplot (which deletes it)
349 349 // - is no longer referenced in the map
350 350 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
351 351 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
352 352 emit variableAboutToBeRemoved(variable);
353 353
354 354 auto &plottablesMap = variableIt->second;
355 355
356 356 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
357 357 plottableIt != plottableEnd;) {
358 358 ui->widget->removePlottable(plottableIt->second);
359 359 plottableIt = plottablesMap.erase(plottableIt);
360 360 }
361 361
362 362 impl->m_VariableToPlotMultiMap.erase(variableIt);
363 363 }
364 364
365 365 // Updates graph
366 366 ui->widget->replot();
367 367 }
368 368
369 369 QList<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
370 370 {
371 371 auto variables = QList<std::shared_ptr<Variable> >{};
372 372 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
373 373 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
374 374 variables << it->first;
375 375 }
376 376
377 377 return variables;
378 378 }
379 379
380 380 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
381 381 {
382 382 if (!variable) {
383 383 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
384 384 return;
385 385 }
386 386
387 387 VisualizationGraphHelper::setYAxisRange(variable, *ui->widget);
388 388 }
389 389
390 390 SqpRange VisualizationGraphWidget::graphRange() const noexcept
391 391 {
392 392 auto graphRange = ui->widget->xAxis->range();
393 393 return SqpRange{graphRange.lower, graphRange.upper};
394 394 }
395 395
396 396 void VisualizationGraphWidget::setGraphRange(const SqpRange &range, bool calibration)
397 397 {
398 398 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange START");
399 399
400 400 if (calibration) {
401 401 impl->m_IsCalibration = true;
402 402 }
403 403
404 404 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
405 405 ui->widget->replot();
406 406
407 407 if (calibration) {
408 408 impl->m_IsCalibration = false;
409 409 }
410 410
411 411 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange END");
412 412 }
413 413
414 414 void VisualizationGraphWidget::setAutoRangeOnVariableInitialization(bool value)
415 415 {
416 416 impl->m_VariableAutoRangeOnInit = value;
417 417 }
418 418
419 419 QVector<SqpRange> VisualizationGraphWidget::selectionZoneRanges() const
420 420 {
421 421 QVector<SqpRange> ranges;
422 422 for (auto zone : impl->m_SelectionZones) {
423 423 ranges << zone->range();
424 424 }
425 425
426 426 return ranges;
427 427 }
428 428
429 429 void VisualizationGraphWidget::addSelectionZones(const QVector<SqpRange> &ranges)
430 430 {
431 431 for (const auto &range : ranges) {
432 432 // note: ownership is transfered to QCustomPlot
433 433 auto zone = new VisualizationSelectionZoneItem(&plot());
434 434 zone->setRange(range.m_TStart, range.m_TEnd);
435 435 impl->addSelectionZone(zone);
436 436 }
437 437
438 438 plot().replot(QCustomPlot::rpQueuedReplot);
439 439 }
440 440
441 VisualizationSelectionZoneItem *VisualizationGraphWidget::addSelectionZone(const QString &name,
442 const SqpRange &range)
443 {
444 // note: ownership is transfered to QCustomPlot
445 auto zone = new VisualizationSelectionZoneItem(&plot());
446 zone->setName(name);
447 zone->setRange(range.m_TStart, range.m_TEnd);
448 impl->addSelectionZone(zone);
449
450 plot().replot(QCustomPlot::rpQueuedReplot);
451
452 return zone;
453 }
454
441 455 void VisualizationGraphWidget::removeSelectionZone(VisualizationSelectionZoneItem *selectionZone)
442 456 {
443 457 parentVisualizationWidget()->selectionZoneManager().setSelected(selectionZone, false);
444 458
445 459 if (impl->m_HoveredZone == selectionZone) {
446 460 impl->m_HoveredZone = nullptr;
447 461 setCursor(Qt::ArrowCursor);
448 462 }
449 463
450 464 impl->m_SelectionZones.removeAll(selectionZone);
451 465 plot().removeItem(selectionZone);
452 466 plot().replot(QCustomPlot::rpQueuedReplot);
453 467 }
454 468
455 469 void VisualizationGraphWidget::undoZoom()
456 470 {
457 471 auto zoom = impl->m_ZoomStack.pop();
458 472 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
459 473 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
460 474
461 475 axisX->setRange(zoom.first);
462 476 axisY->setRange(zoom.second);
463 477
464 478 plot().replot(QCustomPlot::rpQueuedReplot);
465 479 }
466 480
467 481 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
468 482 {
469 483 if (visitor) {
470 484 visitor->visit(this);
471 485 }
472 486 else {
473 487 qCCritical(LOG_VisualizationGraphWidget())
474 488 << tr("Can't visit widget : the visitor is null");
475 489 }
476 490 }
477 491
478 492 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
479 493 {
480 494 auto isSpectrogram = [](const auto &variable) {
481 495 return std::dynamic_pointer_cast<SpectrogramSeries>(variable.dataSeries()) != nullptr;
482 496 };
483 497
484 498 // - A spectrogram series can't be dropped on graph with existing plottables
485 499 // - No data series can be dropped on graph with existing spectrogram series
486 500 return isSpectrogram(variable)
487 501 ? impl->m_VariableToPlotMultiMap.empty()
488 502 : std::none_of(
489 503 impl->m_VariableToPlotMultiMap.cbegin(), impl->m_VariableToPlotMultiMap.cend(),
490 504 [isSpectrogram](const auto &entry) { return isSpectrogram(*entry.first); });
491 505 }
492 506
493 507 bool VisualizationGraphWidget::contains(const Variable &variable) const
494 508 {
495 509 // Finds the variable among the keys of the map
496 510 auto variablePtr = &variable;
497 511 auto findVariable
498 512 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
499 513
500 514 auto end = impl->m_VariableToPlotMultiMap.cend();
501 515 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
502 516 return it != end;
503 517 }
504 518
505 519 QString VisualizationGraphWidget::name() const
506 520 {
507 521 return impl->m_Name;
508 522 }
509 523
510 524 QMimeData *VisualizationGraphWidget::mimeData(const QPoint &position) const
511 525 {
512 526 auto mimeData = new QMimeData;
513 527
514 528 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position, plot());
515 529 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
516 530 && selectionZoneItemUnderCursor) {
517 531 mimeData->setData(MIME_TYPE_TIME_RANGE, TimeController::mimeDataForTimeRange(
518 532 selectionZoneItemUnderCursor->range()));
519 533 mimeData->setData(MIME_TYPE_SELECTION_ZONE, TimeController::mimeDataForTimeRange(
520 534 selectionZoneItemUnderCursor->range()));
521 535 }
522 536 else {
523 537 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
524 538
525 539 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
526 540 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
527 541 }
528 542
529 543 return mimeData;
530 544 }
531 545
532 546 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint &dragPosition)
533 547 {
534 548 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition, plot());
535 549 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
536 550 && selectionZoneItemUnderCursor) {
537 551
538 552 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
539 553 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
540 554
541 555 auto zoneSize = QSizeF{qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
542 556 qAbs(zoneBottomRight.y() - zoneTopLeft.y())}
543 557 .toSize();
544 558
545 559 auto pixmap = QPixmap(zoneSize);
546 560 render(&pixmap, QPoint(), QRegion{QRect{zoneTopLeft.toPoint(), zoneSize}});
547 561
548 562 return pixmap;
549 563 }
550 564
551 565 return QPixmap();
552 566 }
553 567
554 568 bool VisualizationGraphWidget::isDragAllowed() const
555 569 {
556 570 return true;
557 571 }
558 572
559 573 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
560 574 {
561 575 if (highlighted) {
562 576 plot().setBackground(QBrush(QColor("#BBD5EE")));
563 577 }
564 578 else {
565 579 plot().setBackground(QBrush(Qt::white));
566 580 }
567 581
568 582 plot().update();
569 583 }
570 584
571 585 void VisualizationGraphWidget::addVerticalCursor(double time)
572 586 {
573 587 impl->m_VerticalCursor->setPosition(time);
574 588 impl->m_VerticalCursor->setVisible(true);
575 589
576 590 auto text
577 591 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
578 592 impl->m_VerticalCursor->setLabelText(text);
579 593 }
580 594
581 595 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
582 596 {
583 597 impl->m_VerticalCursor->setAbsolutePosition(position);
584 598 impl->m_VerticalCursor->setVisible(true);
585 599
586 600 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
587 601 auto text
588 602 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
589 603 impl->m_VerticalCursor->setLabelText(text);
590 604 }
591 605
592 606 void VisualizationGraphWidget::removeVerticalCursor()
593 607 {
594 608 impl->m_VerticalCursor->setVisible(false);
595 609 plot().replot(QCustomPlot::rpQueuedReplot);
596 610 }
597 611
598 612 void VisualizationGraphWidget::addHorizontalCursor(double value)
599 613 {
600 614 impl->m_HorizontalCursor->setPosition(value);
601 615 impl->m_HorizontalCursor->setVisible(true);
602 616 impl->m_HorizontalCursor->setLabelText(QString::number(value));
603 617 }
604 618
605 619 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
606 620 {
607 621 impl->m_HorizontalCursor->setAbsolutePosition(position);
608 622 impl->m_HorizontalCursor->setVisible(true);
609 623
610 624 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
611 625 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
612 626 }
613 627
614 628 void VisualizationGraphWidget::removeHorizontalCursor()
615 629 {
616 630 impl->m_HorizontalCursor->setVisible(false);
617 631 plot().replot(QCustomPlot::rpQueuedReplot);
618 632 }
619 633
620 634 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
621 635 {
622 636 Q_UNUSED(event);
623 637
624 638 // Prevents that all variables will be removed from graph when it will be closed
625 639 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
626 640 emit variableAboutToBeRemoved(variableEntry.first);
627 641 }
628 642 }
629 643
630 644 void VisualizationGraphWidget::enterEvent(QEvent *event)
631 645 {
632 646 Q_UNUSED(event);
633 647 impl->m_RenderingDelegate->showGraphOverlay(true);
634 648 }
635 649
636 650 void VisualizationGraphWidget::leaveEvent(QEvent *event)
637 651 {
638 652 Q_UNUSED(event);
639 653 impl->m_RenderingDelegate->showGraphOverlay(false);
640 654
641 655 if (auto parentZone = parentZoneWidget()) {
642 656 parentZone->notifyMouseLeaveGraph(this);
643 657 }
644 658 else {
645 659 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
646 660 }
647 661
648 662 if (impl->m_HoveredZone) {
649 663 impl->m_HoveredZone->setHovered(false);
650 664 impl->m_HoveredZone = nullptr;
651 665 }
652 666 }
653 667
654 668 QCustomPlot &VisualizationGraphWidget::plot() const noexcept
655 669 {
656 670 return *ui->widget;
657 671 }
658 672
659 673 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
660 674 {
661 675 QMenu graphMenu{};
662 676
663 677 // Iterates on variables (unique keys)
664 678 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
665 679 end = impl->m_VariableToPlotMultiMap.cend();
666 680 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
667 681 // 'Remove variable' action
668 682 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
669 683 [ this, var = it->first ]() { removeVariable(var); });
670 684 }
671 685
672 686 if (!impl->m_ZoomStack.isEmpty()) {
673 687 if (!graphMenu.isEmpty()) {
674 688 graphMenu.addSeparator();
675 689 }
676 690
677 691 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
678 692 }
679 693
680 694 // Selection Zone Actions
681 695 auto selectionZoneItem = impl->selectionZoneAt(pos, plot());
682 696 if (selectionZoneItem) {
683 697 auto selectedItems = parentVisualizationWidget()->selectionZoneManager().selectedItems();
684 698 selectedItems.removeAll(selectionZoneItem);
685 699 selectedItems.prepend(selectionZoneItem); // Put the current selection zone first
686 700
687 701 auto zoneActions = sqpApp->actionsGuiController().selectionZoneActions();
688 702 if (!zoneActions.isEmpty() && !graphMenu.isEmpty()) {
689 703 graphMenu.addSeparator();
690 704 }
691 705
692 706 QHash<QString, QMenu *> subMenus;
693 707 QHash<QString, bool> subMenusEnabled;
694 708
695 709 for (auto zoneAction : zoneActions) {
696 710
697 711 auto isEnabled = zoneAction->isEnabled(selectedItems);
698 712
699 713 auto menu = &graphMenu;
700 714 for (auto subMenuName : zoneAction->subMenuList()) {
701 715 if (!subMenus.contains(subMenuName)) {
702 716 menu = menu->addMenu(subMenuName);
703 717 subMenus[subMenuName] = menu;
704 718 subMenusEnabled[subMenuName] = isEnabled;
705 719 }
706 720 else {
707 721 menu = subMenus.value(subMenuName);
708 722 if (isEnabled) {
709 723 // The sub menu is enabled if at least one of its actions is enabled
710 724 subMenusEnabled[subMenuName] = true;
711 725 }
712 726 }
713 727 }
714 728
715 729 auto action = menu->addAction(zoneAction->name());
716 730 action->setEnabled(isEnabled);
717 731 action->setShortcut(zoneAction->displayedShortcut());
718 732 QObject::connect(action, &QAction::triggered,
719 733 [zoneAction, selectedItems]() { zoneAction->execute(selectedItems); });
720 734 }
721 735
722 736 for (auto it = subMenus.cbegin(); it != subMenus.cend(); ++it) {
723 737 it.value()->setEnabled(subMenusEnabled[it.key()]);
724 738 }
725 739 }
726 740
727 741 if (!graphMenu.isEmpty()) {
728 742 graphMenu.exec(QCursor::pos());
729 743 }
730 744 }
731 745
732 746 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
733 747 {
734 748 qCDebug(LOG_VisualizationGraphWidget()) << tr("TORM: VisualizationGraphWidget::onRangeChanged")
735 749 << QThread::currentThread()->objectName() << "DoAcqui"
736 750 << impl->m_Flags.testFlag(GraphFlag::EnableAcquisition);
737 751
738 752 auto graphRange = SqpRange{t1.lower, t1.upper};
739 753 auto oldGraphRange = SqpRange{t2.lower, t2.upper};
740 754
741 755 if (impl->m_Flags.testFlag(GraphFlag::EnableAcquisition)) {
742 756 QVector<std::shared_ptr<Variable> > variableUnderGraphVector;
743 757
744 758 for (auto it = impl->m_VariableToPlotMultiMap.begin(),
745 759 end = impl->m_VariableToPlotMultiMap.end();
746 760 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
747 761 variableUnderGraphVector.push_back(it->first);
748 762 }
749 763 emit requestDataLoading(std::move(variableUnderGraphVector), graphRange,
750 764 !impl->m_IsCalibration);
751 765 }
752 766
753 767 if (impl->m_Flags.testFlag(GraphFlag::EnableSynchronization) && !impl->m_IsCalibration) {
754 768 qCDebug(LOG_VisualizationGraphWidget())
755 769 << tr("TORM: VisualizationGraphWidget::Synchronize notify !!")
756 770 << QThread::currentThread()->objectName() << graphRange << oldGraphRange;
757 771 emit synchronize(graphRange, oldGraphRange);
758 772 }
759 773
760 774 auto pos = mapFromGlobal(QCursor::pos());
761 775 auto axisPos = impl->posToAxisPos(pos, plot());
762 776 if (auto parentZone = parentZoneWidget()) {
763 777 if (impl->pointIsInAxisRect(axisPos, plot())) {
764 778 parentZone->notifyMouseMoveInGraph(pos, axisPos, this);
765 779 }
766 780 else {
767 781 parentZone->notifyMouseLeaveGraph(this);
768 782 }
769 783 }
770 784 else {
771 785 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
772 786 }
773 787
774 788 // Quits calibration
775 789 impl->m_IsCalibration = false;
776 790 }
777 791
778 792 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept
779 793 {
780 794 impl->m_RenderingDelegate->onMouseDoubleClick(event);
781 795 }
782 796
783 797 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
784 798 {
785 799 // Handles plot rendering when mouse is moving
786 800 impl->m_RenderingDelegate->onMouseMove(event);
787 801
788 802 auto axisPos = impl->posToAxisPos(event->pos(), plot());
789 803
790 804 // Zoom box and zone drawing
791 805 if (impl->m_DrawingZoomRect) {
792 806 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
793 807 }
794 808 else if (impl->m_DrawingZone) {
795 809 impl->m_DrawingZone->setEnd(axisPos.x());
796 810 }
797 811
798 812 // Cursor
799 813 if (auto parentZone = parentZoneWidget()) {
800 814 if (impl->pointIsInAxisRect(axisPos, plot())) {
801 815 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
802 816 }
803 817 else {
804 818 parentZone->notifyMouseLeaveGraph(this);
805 819 }
806 820 }
807 821 else {
808 822 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
809 823 }
810 824
811 825 // Search for the selection zone under the mouse
812 826 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
813 827 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
814 828 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) {
815 829
816 830 // Sets the appropriate cursor shape
817 831 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
818 832 setCursor(cursorShape);
819 833
820 834 // Manages the hovered zone
821 835 if (selectionZoneItemUnderCursor != impl->m_HoveredZone) {
822 836 if (impl->m_HoveredZone) {
823 837 impl->m_HoveredZone->setHovered(false);
824 838 }
825 839 selectionZoneItemUnderCursor->setHovered(true);
826 840 impl->m_HoveredZone = selectionZoneItemUnderCursor;
827 841 plot().replot(QCustomPlot::rpQueuedReplot);
828 842 }
829 843 }
830 844 else {
831 845 // There is no zone under the mouse or the interaction mode is not "selection zones"
832 846 if (impl->m_HoveredZone) {
833 847 impl->m_HoveredZone->setHovered(false);
834 848 impl->m_HoveredZone = nullptr;
835 849 }
836 850
837 851 setCursor(Qt::ArrowCursor);
838 852 }
839 853
840 854 impl->m_HasMovedMouse = true;
841 855 VisualizationDragWidget::mouseMoveEvent(event);
842 856 }
843 857
844 858 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
845 859 {
846 860 auto value = event->angleDelta().x() + event->angleDelta().y();
847 861 if (value != 0) {
848 862
849 863 auto direction = value > 0 ? 1.0 : -1.0;
850 864 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
851 865 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
852 866 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
853 867
854 868 auto zoomOrientations = QFlags<Qt::Orientation>{};
855 869 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
856 870 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
857 871
858 872 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
859 873
860 874 if (!isZoomX && !isZoomY) {
861 875 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
862 876 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
863 877
864 878 axis->setRange(axis->range() + diff);
865 879
866 880 if (plot().noAntialiasingOnDrag()) {
867 881 plot().setNotAntialiasedElements(QCP::aeAll);
868 882 }
869 883
870 884 plot().replot(QCustomPlot::rpQueuedReplot);
871 885 }
872 886 }
873 887 }
874 888
875 889 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
876 890 {
877 891 auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
878 892 auto isSelectionZoneMode
879 893 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
880 894 auto isLeftClick = event->buttons().testFlag(Qt::LeftButton);
881 895
882 896 if (!isDragDropClick && isLeftClick) {
883 897 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) {
884 898 // Starts a zoom box
885 899 impl->startDrawingRect(event->pos(), plot());
886 900 }
887 901 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr) {
888 902 // Starts a new selection zone
889 903 auto zoneAtPos = impl->selectionZoneAt(event->pos(), plot());
890 904 if (!zoneAtPos) {
891 905 impl->startDrawingZone(event->pos(), this);
892 906 }
893 907 }
894 908 }
895 909
896 910 // Allows mouse panning only in default mode
897 911 plot().setInteraction(QCP::iRangeDrag, sqpApp->plotsInteractionMode()
898 912 == SqpApplication::PlotsInteractionMode::None
899 913 && !isDragDropClick);
900 914
901 915 // Allows zone edition only in selection zone mode without drag&drop
902 916 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
903 917
904 918 // Selection / Deselection
905 919 if (isSelectionZoneMode) {
906 920 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
907 921 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
908 922
909 923
910 924 if (selectionZoneItemUnderCursor && !selectionZoneItemUnderCursor->selected()
911 925 && !isMultiSelectionClick) {
912 926 parentVisualizationWidget()->selectionZoneManager().select(
913 927 {selectionZoneItemUnderCursor});
914 928 }
915 929 else if (!selectionZoneItemUnderCursor && !isMultiSelectionClick && isLeftClick) {
916 930 parentVisualizationWidget()->selectionZoneManager().clearSelection();
917 931 }
918 932 else {
919 933 // No selection change
920 934 }
921 935
922 936 if (selectionZoneItemUnderCursor && isLeftClick) {
923 937 selectionZoneItemUnderCursor->setAssociatedEditedZones(
924 938 parentVisualizationWidget()->selectionZoneManager().selectedItems());
925 939 }
926 940 }
927 941
928 942
929 943 impl->m_HasMovedMouse = false;
930 944 VisualizationDragWidget::mousePressEvent(event);
931 945 }
932 946
933 947 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
934 948 {
935 949 if (impl->m_DrawingZoomRect) {
936 950
937 951 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
938 952 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
939 953
940 954 auto newAxisXRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().x(),
941 955 impl->m_DrawingZoomRect->bottomRight->coords().x()};
942 956
943 957 auto newAxisYRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().y(),
944 958 impl->m_DrawingZoomRect->bottomRight->coords().y()};
945 959
946 960 impl->removeDrawingRect(plot());
947 961
948 962 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
949 963 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
950 964 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
951 965 axisX->setRange(newAxisXRange);
952 966 axisY->setRange(newAxisYRange);
953 967
954 968 plot().replot(QCustomPlot::rpQueuedReplot);
955 969 }
956 970 }
957 971
958 972 impl->endDrawingZone(this);
959 973
960 974 // Selection / Deselection
961 975 auto isSelectionZoneMode
962 976 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
963 977 if (isSelectionZoneMode) {
964 978 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
965 979 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
966 980 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton
967 981 && !impl->m_HasMovedMouse) {
968 982
969 983 auto zonesUnderCursor = impl->selectionZonesAt(event->pos(), plot());
970 984 if (zonesUnderCursor.count() > 1) {
971 985 // There are multiple zones under the mouse.
972 986 // Performs the selection with a selection dialog.
973 987 VisualizationMultiZoneSelectionDialog dialog{this};
974 988 dialog.setZones(zonesUnderCursor);
975 989 dialog.move(mapToGlobal(event->pos() - QPoint(dialog.width() / 2, 20)));
976 990 dialog.activateWindow();
977 991 dialog.raise();
978 992 if (dialog.exec() == QDialog::Accepted) {
979 993 auto selection = dialog.selectedZones();
980 994
981 995 if (!isMultiSelectionClick) {
982 996 parentVisualizationWidget()->selectionZoneManager().clearSelection();
983 997 }
984 998
985 999 for (auto it = selection.cbegin(); it != selection.cend(); ++it) {
986 1000 auto zone = it.key();
987 1001 auto isSelected = it.value();
988 1002 parentVisualizationWidget()->selectionZoneManager().setSelected(zone,
989 1003 isSelected);
990 1004
991 1005 if (isSelected) {
992 1006 // Puts the zone on top of the stack so it can be moved or resized
993 1007 impl->moveSelectionZoneOnTop(zone, plot());
994 1008 }
995 1009 }
996 1010 }
997 1011 }
998 1012 else {
999 1013 if (!isMultiSelectionClick) {
1000 1014 parentVisualizationWidget()->selectionZoneManager().select(
1001 1015 {selectionZoneItemUnderCursor});
1002 1016 impl->moveSelectionZoneOnTop(selectionZoneItemUnderCursor, plot());
1003 1017 }
1004 1018 else {
1005 1019 parentVisualizationWidget()->selectionZoneManager().setSelected(
1006 1020 selectionZoneItemUnderCursor, !selectionZoneItemUnderCursor->selected()
1007 1021 || event->button() == Qt::RightButton);
1008 1022 }
1009 1023 }
1010 1024 }
1011 1025 else {
1012 1026 // No selection change
1013 1027 }
1014 1028 }
1015 1029 }
1016 1030
1017 1031 void VisualizationGraphWidget::onDataCacheVariableUpdated()
1018 1032 {
1019 1033 auto graphRange = ui->widget->xAxis->range();
1020 1034 auto dateTime = SqpRange{graphRange.lower, graphRange.upper};
1021 1035
1022 1036 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
1023 1037 auto variable = variableEntry.first;
1024 1038 qCDebug(LOG_VisualizationGraphWidget())
1025 1039 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
1026 1040 qCDebug(LOG_VisualizationGraphWidget())
1027 1041 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
1028 1042 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
1029 1043 impl->updateData(variableEntry.second, variable, variable->range());
1030 1044 }
1031 1045 }
1032 1046 }
1033 1047
1034 1048 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
1035 1049 const SqpRange &range)
1036 1050 {
1037 1051 auto it = impl->m_VariableToPlotMultiMap.find(variable);
1038 1052 if (it != impl->m_VariableToPlotMultiMap.end()) {
1039 1053 impl->updateData(it->second, variable, range);
1040 1054 }
1041 1055 }
@@ -1,440 +1,443
1 1 #include "Visualization/VisualizationSelectionZoneItem.h"
2 2 #include "Visualization/VisualizationGraphWidget.h"
3 3 #include "Visualization/VisualizationSelectionZoneManager.h"
4 4 #include "Visualization/VisualizationWidget.h"
5 5
6 6 const QString &DEFAULT_COLOR = QStringLiteral("#E79D41");
7 7
8 8 struct VisualizationSelectionZoneItem::VisualizationSelectionZoneItemPrivate {
9 9
10 10 QCustomPlot *m_Plot;
11 11 double m_T1 = 0;
12 12 double m_T2 = 0;
13 13 QColor m_Color;
14 14
15 15 bool m_IsEditionEnabled = true;
16 16 double m_MovedOrinalT1 = 0;
17 17 double m_MovedOrinalT2 = 0;
18 18
19 19 QCPItemStraightLine *m_LeftLine;
20 20 QCPItemStraightLine *m_RightLine;
21 21 QCPItemText *m_NameLabelItem = nullptr;
22 22
23 23 enum class EditionMode { NoEdition, ResizeLeft, ResizeRight, Move };
24 24 EditionMode m_CurrentEditionMode;
25 25
26 26 QVector<VisualizationSelectionZoneItem *> m_AssociatedEditedZones;
27 27
28 28 VisualizationSelectionZoneItemPrivate(QCustomPlot *plot)
29 29 : m_Plot(plot), m_Color(Qt::blue), m_CurrentEditionMode(EditionMode::NoEdition)
30 30 {
31 31 }
32 32
33 33 void updatePosition(VisualizationSelectionZoneItem *item)
34 34 {
35 35 item->topLeft->setCoords(m_T1, 0);
36 36 item->bottomRight->setCoords(m_T2, 1);
37 37 }
38 38
39 39 EditionMode getEditionMode(const QPoint &pos, const VisualizationSelectionZoneItem *zoneItem)
40 40 {
41 41 auto distanceLeft = m_LeftLine->selectTest(pos, false);
42 42 auto distanceRight = m_RightLine->selectTest(pos, false);
43 43 auto distance = zoneItem->selectTest(pos, false);
44 44
45 45 if (distanceRight <= distance) {
46 46 return VisualizationSelectionZoneItemPrivate::EditionMode::ResizeRight;
47 47 }
48 48 else if (distanceLeft <= distance) {
49 49 return VisualizationSelectionZoneItemPrivate::EditionMode::ResizeLeft;
50 50 }
51 51
52 52 return VisualizationSelectionZoneItemPrivate::EditionMode::Move;
53 53 }
54 54
55 55 double pixelSizeToAxisXSize(double pixels)
56 56 {
57 57 auto axis = m_Plot->axisRect()->axis(QCPAxis::atBottom);
58 58 return axis->pixelToCoord(pixels) - axis->pixelToCoord(0);
59 59 }
60 60
61 61 bool alignZones(VisualizationSelectionZoneItem *referenceZone,
62 62 const QVector<VisualizationSelectionZoneItem *> &zonesToAlign, bool alignOnLeft,
63 63 bool allowResize, bool vertically)
64 64 {
65 65 auto result = false;
66 66
67 67 auto referenceTime
68 68 = alignOnLeft ? referenceZone->range().m_TStart : referenceZone->range().m_TEnd;
69 69
70 70 auto referenceBottomAxis = m_Plot->axisRect()->axis(QCPAxis::atBottom);
71 71 auto referenceVerticalPosition = referenceBottomAxis->coordToPixel(referenceTime);
72 72
73 73 for (auto otherZone : zonesToAlign) {
74 74
75 75 auto otherZoneRange = otherZone->range();
76 76 auto newZoneStart = otherZoneRange.m_TStart;
77 77 auto newZoneEnd = otherZoneRange.m_TEnd;
78 78
79 79 auto alignedTime = referenceTime;
80 80 if (vertically) {
81 81 auto otherZoneAxis = otherZone->parentPlot()->axisRect()->axis(QCPAxis::atBottom);
82 82 alignedTime = otherZoneAxis->pixelToCoord(referenceVerticalPosition);
83 83 }
84 84
85 85 if (alignOnLeft) {
86 86 newZoneStart = alignedTime;
87 87 if (!allowResize) {
88 88 newZoneEnd = alignedTime + (otherZoneRange.m_TEnd - otherZoneRange.m_TStart);
89 89 }
90 90 }
91 91 else { // align on right
92 92 newZoneEnd = alignedTime;
93 93 if (!allowResize) {
94 94 newZoneStart = alignedTime - (otherZoneRange.m_TEnd - otherZoneRange.m_TStart);
95 95 }
96 96 }
97 97
98 98 if (newZoneStart < newZoneEnd) {
99 99 result = true;
100 100 otherZone->setRange(newZoneStart, newZoneEnd);
101 101 otherZone->parentPlot()->replot();
102 102 }
103 103 }
104 104
105 105 return result;
106 106 }
107 107 };
108 108
109 109 VisualizationSelectionZoneItem::VisualizationSelectionZoneItem(QCustomPlot *plot)
110 110 : QCPItemRect(plot),
111 111 impl{spimpl::make_unique_impl<VisualizationSelectionZoneItemPrivate>(plot)}
112 112 {
113 113 topLeft->setTypeX(QCPItemPosition::ptPlotCoords);
114 114 topLeft->setTypeY(QCPItemPosition::ptAxisRectRatio);
115 115 bottomRight->setTypeX(QCPItemPosition::ptPlotCoords);
116 116 bottomRight->setTypeY(QCPItemPosition::ptAxisRectRatio);
117 117 setSelectable(false);
118 118
119 119 impl->m_RightLine = new QCPItemStraightLine(plot);
120 120 impl->m_RightLine->point1->setParentAnchor(topRight);
121 121 impl->m_RightLine->point2->setParentAnchor(bottomRight);
122 122 impl->m_RightLine->point1->setTypeX(QCPItemPosition::ptAbsolute);
123 123 impl->m_RightLine->point1->setTypeY(QCPItemPosition::ptAbsolute);
124 124 impl->m_RightLine->point2->setTypeX(QCPItemPosition::ptAbsolute);
125 125 impl->m_RightLine->point2->setTypeY(QCPItemPosition::ptAbsolute);
126 126 impl->m_RightLine->setSelectable(false);
127 127
128 128 impl->m_LeftLine = new QCPItemStraightLine(plot);
129 129 impl->m_LeftLine->point1->setParentAnchor(topLeft);
130 130 impl->m_LeftLine->point2->setParentAnchor(bottomLeft);
131 131 impl->m_LeftLine->point1->setTypeX(QCPItemPosition::ptAbsolute);
132 132 impl->m_LeftLine->point1->setTypeY(QCPItemPosition::ptAbsolute);
133 133 impl->m_LeftLine->point2->setTypeX(QCPItemPosition::ptAbsolute);
134 134 impl->m_LeftLine->point2->setTypeY(QCPItemPosition::ptAbsolute);
135 135 impl->m_LeftLine->setSelectable(false);
136 136
137 137 connect(this, &VisualizationSelectionZoneItem::selectionChanged, impl->m_RightLine,
138 138 &QCPItemStraightLine::setSelected);
139 139 connect(this, &VisualizationSelectionZoneItem::selectionChanged, impl->m_LeftLine,
140 140 &QCPItemStraightLine::setSelected);
141 141
142 142 setColor(QColor(DEFAULT_COLOR));
143 143 }
144 144
145 145 VisualizationSelectionZoneItem::~VisualizationSelectionZoneItem()
146 146 {
147 147 }
148 148
149 149 VisualizationGraphWidget *VisualizationSelectionZoneItem::parentGraphWidget() const noexcept
150 150 {
151 151 auto parent = impl->m_Plot->parentWidget();
152 152 while (parent != nullptr && !qobject_cast<VisualizationGraphWidget *>(parent)) {
153 153 parent = parent->parentWidget();
154 154 }
155 155
156 156 return qobject_cast<VisualizationGraphWidget *>(parent);
157 157 }
158 158
159 159 void VisualizationSelectionZoneItem::setName(const QString &name)
160 160 {
161 161 if (name.isEmpty() && impl->m_NameLabelItem) {
162 162 impl->m_Plot->removeItem(impl->m_NameLabelItem);
163 163 impl->m_NameLabelItem = nullptr;
164 164 }
165 165 else if (!impl->m_NameLabelItem) {
166 166 impl->m_NameLabelItem = new QCPItemText(impl->m_Plot);
167 167 impl->m_NameLabelItem->setText(name);
168 168 impl->m_NameLabelItem->setPositionAlignment(Qt::AlignHCenter | Qt::AlignTop);
169 169 impl->m_NameLabelItem->setColor(impl->m_Color);
170 170 impl->m_NameLabelItem->position->setParentAnchor(top);
171 171 }
172 172 }
173 173
174 174 QString VisualizationSelectionZoneItem::name() const
175 175 {
176 176 if (!impl->m_NameLabelItem) {
177 177 return QString();
178 178 }
179 179
180 180 return impl->m_NameLabelItem->text();
181 181 }
182 182
183 183 SqpRange VisualizationSelectionZoneItem::range() const
184 184 {
185 185 SqpRange range;
186 186 range.m_TStart = impl->m_T1 <= impl->m_T2 ? impl->m_T1 : impl->m_T2;
187 187 range.m_TEnd = impl->m_T1 > impl->m_T2 ? impl->m_T1 : impl->m_T2;
188 188 return range;
189 189 }
190 190
191 191 void VisualizationSelectionZoneItem::setRange(double tstart, double tend)
192 192 {
193 193 impl->m_T1 = tstart;
194 194 impl->m_T2 = tend;
195 195 impl->updatePosition(this);
196 196 }
197 197
198 198 void VisualizationSelectionZoneItem::setStart(double tstart)
199 199 {
200 200 impl->m_T1 = tstart;
201 201 impl->updatePosition(this);
202 202 }
203 203
204 204 void VisualizationSelectionZoneItem::setEnd(double tend)
205 205 {
206 206 impl->m_T2 = tend;
207 207 impl->updatePosition(this);
208 208 }
209 209
210 210 void VisualizationSelectionZoneItem::setColor(const QColor &color)
211 211 {
212 212 impl->m_Color = color;
213 213
214 214 auto brushColor = color;
215 215 brushColor.setAlpha(80);
216 216 setBrush(QBrush(brushColor));
217 217 setPen(QPen(Qt::NoPen));
218 218
219 219 auto selectedBrushColor = brushColor;
220 220 selectedBrushColor.setAlpha(150);
221 221 setSelectedBrush(QBrush(selectedBrushColor));
222 222 setSelectedPen(QPen(Qt::NoPen));
223 223
224 224 auto linePen = QPen(color);
225 225 linePen.setStyle(Qt::SolidLine);
226 226 linePen.setWidth(4);
227 227
228 228 auto selectedLinePen = linePen;
229 229 selectedLinePen.setColor(color.darker(120));
230 230 selectedLinePen.setWidth(4);
231 231
232 232 impl->m_LeftLine->setPen(linePen);
233 233 impl->m_RightLine->setPen(linePen);
234 234
235 235 impl->m_LeftLine->setSelectedPen(selectedLinePen);
236 236 impl->m_RightLine->setSelectedPen(selectedLinePen);
237 237 }
238 238
239 239 void VisualizationSelectionZoneItem::setEditionEnabled(bool value)
240 240 {
241 241 impl->m_IsEditionEnabled = value;
242 242 setSelectable(value);
243 243 if (!value) {
244 244 setSelected(false);
245 245 impl->m_CurrentEditionMode = VisualizationSelectionZoneItemPrivate::EditionMode::NoEdition;
246 246 }
247 247 }
248 248
249 249 bool VisualizationSelectionZoneItem::isEditionEnabled() const
250 250 {
251 251 return impl->m_IsEditionEnabled;
252 252 }
253 253
254 254 void VisualizationSelectionZoneItem::moveToTop()
255 255 {
256 256 moveToLayer(layer(), false);
257 257 }
258 258
259 259 Qt::CursorShape
260 260 VisualizationSelectionZoneItem::curshorShapeForPosition(const QPoint &position) const
261 261 {
262 262 auto mode = impl->m_CurrentEditionMode
263 263 == VisualizationSelectionZoneItemPrivate::EditionMode::NoEdition
264 264 ? impl->getEditionMode(position, this)
265 265 : impl->m_CurrentEditionMode;
266 266 switch (mode) {
267 267 case VisualizationSelectionZoneItemPrivate::EditionMode::Move:
268 268 return Qt::SizeAllCursor;
269 269 case VisualizationSelectionZoneItemPrivate::EditionMode::ResizeLeft:
270 270 case VisualizationSelectionZoneItemPrivate::EditionMode::ResizeRight: // fallthrough
271 271 return Qt::SizeHorCursor;
272 272 default:
273 273 return Qt::ArrowCursor;
274 274 }
275 275 }
276 276
277 277 void VisualizationSelectionZoneItem::setHovered(bool value)
278 278 {
279 279 if (value) {
280 280 auto linePen = impl->m_LeftLine->pen();
281 281 linePen.setStyle(Qt::DotLine);
282 282 linePen.setWidth(3);
283 283
284 284 auto selectedLinePen = impl->m_LeftLine->selectedPen();
285 285 ;
286 286 selectedLinePen.setStyle(Qt::DotLine);
287 287 selectedLinePen.setWidth(3);
288 288
289 289 impl->m_LeftLine->setPen(linePen);
290 290 impl->m_RightLine->setPen(linePen);
291 291
292 292 impl->m_LeftLine->setSelectedPen(selectedLinePen);
293 293 impl->m_RightLine->setSelectedPen(selectedLinePen);
294 294 }
295 295 else {
296 296 setColor(impl->m_Color);
297 297 }
298 298 }
299 299
300 300 void VisualizationSelectionZoneItem::setAssociatedEditedZones(
301 301 const QVector<VisualizationSelectionZoneItem *> &associatedZones)
302 302 {
303 303 impl->m_AssociatedEditedZones = associatedZones;
304 304 impl->m_AssociatedEditedZones.removeAll(this);
305 305 }
306 306
307 307 bool VisualizationSelectionZoneItem::alignZonesVerticallyOnLeft(
308 308 const QVector<VisualizationSelectionZoneItem *> &zonesToAlign, bool allowResize)
309 309 {
310 310 return impl->alignZones(this, zonesToAlign, true, allowResize, true);
311 311 }
312 312
313 313 bool VisualizationSelectionZoneItem::alignZonesVerticallyOnRight(
314 314 const QVector<VisualizationSelectionZoneItem *> &zonesToAlign, bool allowResize)
315 315 {
316 316 return impl->alignZones(this, zonesToAlign, false, allowResize, true);
317 317 }
318 318
319 319 bool VisualizationSelectionZoneItem::alignZonesTemporallyOnLeft(
320 320 const QVector<VisualizationSelectionZoneItem *> &zonesToAlign, bool allowResize)
321 321 {
322 322 return impl->alignZones(this, zonesToAlign, true, allowResize, false);
323 323 }
324 324
325 325 bool VisualizationSelectionZoneItem::alignZonesTemporallyOnRight(
326 326 const QVector<VisualizationSelectionZoneItem *> &zonesToAlign, bool allowResize)
327 327 {
328 328 return impl->alignZones(this, zonesToAlign, false, allowResize, false);
329 329 }
330 330
331 331 void VisualizationSelectionZoneItem::mousePressEvent(QMouseEvent *event, const QVariant &details)
332 332 {
333 333 Q_UNUSED(details);
334 334
335 335 if (isEditionEnabled() && event->button() == Qt::LeftButton) {
336 336 impl->m_CurrentEditionMode = impl->getEditionMode(event->pos(), this);
337 337
338 338 impl->m_MovedOrinalT1 = impl->m_T1;
339 339 impl->m_MovedOrinalT2 = impl->m_T2;
340 340 for (auto associatedZone : impl->m_AssociatedEditedZones) {
341 341 associatedZone->impl->m_MovedOrinalT1 = associatedZone->impl->m_T1;
342 342 associatedZone->impl->m_MovedOrinalT2 = associatedZone->impl->m_T2;
343 343 }
344 344 }
345 345 else {
346 346 impl->m_CurrentEditionMode = VisualizationSelectionZoneItemPrivate::EditionMode::NoEdition;
347 347 event->ignore();
348 348 }
349 349 }
350 350
351 351 void VisualizationSelectionZoneItem::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos)
352 352 {
353 353 if (isEditionEnabled()) {
354 354 if (!selected()) {
355 355 // Force the item to be selected during the edition
356 356 parentGraphWidget()->parentVisualizationWidget()->selectionZoneManager().setSelected(
357 357 this, true);
358 358 }
359 359
360 360 auto axis = impl->m_Plot->axisRect()->axis(QCPAxis::atBottom);
361 361 auto pixelDiff = event->pos().x() - startPos.x();
362 362 auto diff = impl->pixelSizeToAxisXSize(pixelDiff);
363 363
364 364 switch (impl->m_CurrentEditionMode) {
365 365 case VisualizationSelectionZoneItemPrivate::EditionMode::Move:
366 366 setRange(impl->m_MovedOrinalT1 + diff, impl->m_MovedOrinalT2 + diff);
367 367 for (auto associatedZone : impl->m_AssociatedEditedZones) {
368 368 associatedZone->move(pixelDiff);
369 369 }
370 370 break;
371 371 case VisualizationSelectionZoneItemPrivate::EditionMode::ResizeLeft:
372 372 setStart(impl->m_MovedOrinalT1 + diff);
373 373 for (auto associatedZone : impl->m_AssociatedEditedZones) {
374 374 impl->m_MovedOrinalT1 < impl->m_MovedOrinalT2
375 375 ? associatedZone->resizeLeft(pixelDiff)
376 376 : associatedZone->resizeRight(pixelDiff);
377 377 }
378 378 break;
379 379 case VisualizationSelectionZoneItemPrivate::EditionMode::ResizeRight:
380 380 setEnd(impl->m_MovedOrinalT2 + diff);
381 381 for (auto associatedZone : impl->m_AssociatedEditedZones) {
382 382 impl->m_MovedOrinalT1 < impl->m_MovedOrinalT2
383 383 ? associatedZone->resizeRight(pixelDiff)
384 384 : associatedZone->resizeLeft(pixelDiff);
385 385 }
386 386 break;
387 387 default:
388 388 break;
389 389 }
390 390
391 emit rangeEdited(range());
392
391 393 for (auto associatedZone : impl->m_AssociatedEditedZones) {
392 394 associatedZone->parentPlot()->replot();
395 emit associatedZone->rangeEdited(associatedZone->range());
393 396 }
394 397 }
395 398 else {
396 399 event->ignore();
397 400 }
398 401 }
399 402
400 403 void VisualizationSelectionZoneItem::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)
401 404 {
402 405 Q_UNUSED(startPos);
403 406
404 407 if (isEditionEnabled()) {
405 408 impl->m_CurrentEditionMode = VisualizationSelectionZoneItemPrivate::EditionMode::NoEdition;
406 409 }
407 410 else {
408 411 event->ignore();
409 412 }
410 413
411 414 impl->m_AssociatedEditedZones.clear();
412 415 }
413 416
414 417 void VisualizationSelectionZoneItem::resizeLeft(double pixelDiff)
415 418 {
416 419 auto diff = impl->pixelSizeToAxisXSize(pixelDiff);
417 420 if (impl->m_MovedOrinalT1 <= impl->m_MovedOrinalT2) {
418 421 setStart(impl->m_MovedOrinalT1 + diff);
419 422 }
420 423 else {
421 424 setEnd(impl->m_MovedOrinalT2 + diff);
422 425 }
423 426 }
424 427
425 428 void VisualizationSelectionZoneItem::resizeRight(double pixelDiff)
426 429 {
427 430 auto diff = impl->pixelSizeToAxisXSize(pixelDiff);
428 431 if (impl->m_MovedOrinalT1 > impl->m_MovedOrinalT2) {
429 432 setStart(impl->m_MovedOrinalT1 + diff);
430 433 }
431 434 else {
432 435 setEnd(impl->m_MovedOrinalT2 + diff);
433 436 }
434 437 }
435 438
436 439 void VisualizationSelectionZoneItem::move(double pixelDiff)
437 440 {
438 441 auto diff = impl->pixelSizeToAxisXSize(pixelDiff);
439 442 setRange(impl->m_MovedOrinalT1 + diff, impl->m_MovedOrinalT2 + diff);
440 443 }
General Comments 0
You need to be logged in to leave comments. Login now