##// END OF EJS Templates
MR for linux compilation
perrinel -
r848:89fbdd3d4a7b
parent child
Show More
@@ -1,230 +1,230
1 1 #include "DragDropHelper.h"
2 2 #include "SqpApplication.h"
3 3 #include "Visualization/VisualizationDragWidget.h"
4 4
5 5 #include <QDir>
6 6 #include <QDragEnterEvent>
7 7 #include <QDragMoveEvent>
8 8 #include <QScrollArea>
9 9 #include <QScrollBar>
10 10 #include <QTimer>
11 11 #include <QVBoxLayout>
12 12
13 13 const QString DragDropHelper::MIME_TYPE_GRAPH = "scqlop/graph";
14 14 const QString DragDropHelper::MIME_TYPE_ZONE = "scqlop/zone";
15 15
16 16 const int SCROLL_SPEED = 5;
17 17 const int SCROLL_ZONE_SIZE = 50;
18 18
19 19 struct DragDropScroller::DragDropScrollerPrivate {
20 20
21 21 QList<QScrollArea *> m_ScrollAreas;
22 22 QScrollArea *m_CurrentScrollArea = nullptr;
23 23 std::unique_ptr<QTimer> m_Timer = nullptr;
24 24
25 25
26 26 enum class ScrollDirection { up, down, unknown };
27 27 ScrollDirection m_Direction = ScrollDirection::unknown;
28 28
29 29 explicit DragDropScrollerPrivate() : m_Timer{std::make_unique<QTimer>()}
30 30 {
31 31 m_Timer->setInterval(0);
32 32 }
33 33 };
34 34
35 35 DragDropScroller::DragDropScroller(QObject *parent)
36 36 : QObject{parent}, impl{spimpl::make_unique_impl<DragDropScrollerPrivate>()}
37 37 {
38 38 connect(impl->m_Timer.get(), &QTimer::timeout, this, &DragDropScroller::onTimer);
39 39 }
40 40
41 41 void DragDropScroller::addScrollArea(QScrollArea *scrollArea)
42 42 {
43 43 impl->m_ScrollAreas << scrollArea;
44 44 scrollArea->viewport()->setAcceptDrops(true);
45 45 }
46 46
47 47 void DragDropScroller::removeScrollArea(QScrollArea *scrollArea)
48 48 {
49 49 impl->m_ScrollAreas.removeAll(scrollArea);
50 50 scrollArea->viewport()->setAcceptDrops(false);
51 51 }
52 52
53 53 bool DragDropScroller::eventFilter(QObject *obj, QEvent *event)
54 54 {
55 55 if (event->type() == QEvent::DragMove) {
56 56 auto w = static_cast<QWidget *>(obj);
57 57
58 58 if (impl->m_CurrentScrollArea && impl->m_CurrentScrollArea->isAncestorOf(w)) {
59 59 auto moveEvent = static_cast<QDragMoveEvent *>(event);
60 60
61 61 auto pos = moveEvent->pos();
62 62 if (impl->m_CurrentScrollArea->viewport() != w) {
63 63 auto globalPos = w->mapToGlobal(moveEvent->pos());
64 64 pos = impl->m_CurrentScrollArea->viewport()->mapFromGlobal(globalPos);
65 65 }
66 66
67 67 auto isInTopZone = pos.y() > impl->m_CurrentScrollArea->viewport()->size().height()
68 68 - SCROLL_ZONE_SIZE;
69 69 auto isInBottomZone = pos.y() < SCROLL_ZONE_SIZE;
70 70
71 71 if (!isInTopZone && !isInBottomZone) {
72 72 impl->m_Direction = DragDropScrollerPrivate::ScrollDirection::unknown;
73 73 impl->m_Timer->stop();
74 74 }
75 75 else if (!impl->m_Timer->isActive()) {
76 76 impl->m_Direction = isInTopZone ? DragDropScrollerPrivate::ScrollDirection::up
77 77 : DragDropScrollerPrivate::ScrollDirection::down;
78 78 impl->m_Timer->start();
79 79 }
80 80 }
81 81 }
82 82 else if (event->type() == QEvent::DragEnter) {
83 83 auto w = static_cast<QWidget *>(obj);
84 84
85 85 for (auto scrollArea : impl->m_ScrollAreas) {
86 86 if (impl->m_CurrentScrollArea != scrollArea && scrollArea->isAncestorOf(w)) {
87 87 auto enterEvent = static_cast<QDragEnterEvent *>(event);
88 88 enterEvent->acceptProposedAction();
89 89 enterEvent->setDropAction(Qt::IgnoreAction);
90 90 impl->m_CurrentScrollArea = scrollArea;
91 91 break;
92 92 }
93 93 }
94 94 }
95 95 else if (event->type() == QEvent::DragLeave) {
96 auto w = static_cast<QWidget *>(obj);
97 96 if (impl->m_CurrentScrollArea) {
98 97 if (!QRect(QPoint(), impl->m_CurrentScrollArea->size())
99 98 .contains(impl->m_CurrentScrollArea->mapFromGlobal(QCursor::pos()))) {
100 99 impl->m_CurrentScrollArea = nullptr;
101 100 impl->m_Direction = DragDropScrollerPrivate::ScrollDirection::unknown;
102 101 impl->m_Timer->stop();
103 102 }
104 103 }
105 104 }
106 105 else if (event->type() == QEvent::Drop) {
107 auto w = static_cast<QWidget *>(obj);
108 106 if (impl->m_CurrentScrollArea) {
109 107 impl->m_CurrentScrollArea = nullptr;
110 108 impl->m_Direction = DragDropScrollerPrivate::ScrollDirection::unknown;
111 109 impl->m_Timer->stop();
112 110 }
113 111 }
114 112
115 113 return false;
116 114 }
117 115
118 116 void DragDropScroller::onTimer()
119 117 {
120 118 if (impl->m_CurrentScrollArea) {
121 119 auto mvt = 0;
122 120 switch (impl->m_Direction) {
123 121 case DragDropScrollerPrivate::ScrollDirection::up:
124 122 mvt = SCROLL_SPEED;
125 123 break;
126 124 case DragDropScrollerPrivate::ScrollDirection::down:
127 125 mvt = -SCROLL_SPEED;
128 126 break;
129 127 default:
130 128 break;
131 129 }
132 130
133 131 impl->m_CurrentScrollArea->verticalScrollBar()->setValue(
134 132 impl->m_CurrentScrollArea->verticalScrollBar()->value() + mvt);
135 133 }
136 134 }
137 135
138 136 struct DragDropHelper::DragDropHelperPrivate {
139 137
140 138 VisualizationDragWidget *m_CurrentDragWidget = nullptr;
141 139 std::unique_ptr<QWidget> m_PlaceHolder = nullptr;
142 140 std::unique_ptr<DragDropScroller> m_DragDropScroller = nullptr;
143 141 QString m_ImageTempUrl; // Temporary file for image url generated by the drag & drop. Not using
144 142 // QTemporaryFile to have a name which is not generated.
145 143
146 144 explicit DragDropHelperPrivate()
147 145 : m_PlaceHolder{std::make_unique<QWidget>()},
148 146 m_DragDropScroller{std::make_unique<DragDropScroller>()}
149 147 {
150 148 m_PlaceHolder->setStyleSheet("background-color: #BBD5EE; border:2px solid #2A7FD4");
151 149 sqpApp->installEventFilter(m_DragDropScroller.get());
152 150
153 151
154 152 m_ImageTempUrl = QDir::temp().absoluteFilePath("Scqlop_graph.png");
155 153 }
156 154
157 155 void preparePlaceHolder() const
158 156 {
159 157 if (m_CurrentDragWidget) {
160 158 m_PlaceHolder->setMinimumSize(m_CurrentDragWidget->size());
161 159 m_PlaceHolder->setSizePolicy(m_CurrentDragWidget->sizePolicy());
162 160 }
163 161 else {
164 162 m_PlaceHolder->setMinimumSize(200, 200);
165 163 }
166 164 }
167 165 };
168 166
169 167
170 DragDropHelper::DragDropHelper() : impl{spimpl::make_unique_impl<DragDropHelperPrivate>()} {}
168 DragDropHelper::DragDropHelper() : impl{spimpl::make_unique_impl<DragDropHelperPrivate>()}
169 {
170 }
171 171
172 172 DragDropHelper::~DragDropHelper()
173 173 {
174 174 QFile::remove(impl->m_ImageTempUrl);
175 175 }
176 176
177 177 void DragDropHelper::setCurrentDragWidget(VisualizationDragWidget *dragWidget)
178 178 {
179 179 impl->m_CurrentDragWidget = dragWidget;
180 180 }
181 181
182 182 VisualizationDragWidget *DragDropHelper::getCurrentDragWidget() const
183 183 {
184 184 return impl->m_CurrentDragWidget;
185 185 }
186 186
187 187
188 188 QWidget &DragDropHelper::placeHolder() const
189 189 {
190 190 return *impl->m_PlaceHolder;
191 191 }
192 192
193 193 void DragDropHelper::insertPlaceHolder(QVBoxLayout *layout, int index)
194 194 {
195 195 removePlaceHolder();
196 196 impl->preparePlaceHolder();
197 197 layout->insertWidget(index, impl->m_PlaceHolder.get());
198 198 impl->m_PlaceHolder->show();
199 199 }
200 200
201 201 void DragDropHelper::removePlaceHolder()
202 202 {
203 203 auto parentWidget = impl->m_PlaceHolder->parentWidget();
204 204 if (parentWidget) {
205 205 parentWidget->layout()->removeWidget(impl->m_PlaceHolder.get());
206 206 impl->m_PlaceHolder->setParent(nullptr);
207 207 impl->m_PlaceHolder->hide();
208 208 }
209 209 }
210 210
211 211 bool DragDropHelper::isPlaceHolderSet() const
212 212 {
213 213 return impl->m_PlaceHolder->parentWidget();
214 214 }
215 215
216 216 void DragDropHelper::addDragDropScrollArea(QScrollArea *scrollArea)
217 217 {
218 218 impl->m_DragDropScroller->addScrollArea(scrollArea);
219 219 }
220 220
221 221 void DragDropHelper::removeDragDropScrollArea(QScrollArea *scrollArea)
222 222 {
223 223 impl->m_DragDropScroller->removeScrollArea(scrollArea);
224 224 }
225 225
226 226 QUrl DragDropHelper::imageTemporaryUrl(const QImage &image) const
227 227 {
228 228 image.save(impl->m_ImageTempUrl);
229 229 return QUrl::fromLocalFile(impl->m_ImageTempUrl);
230 230 }
@@ -1,155 +1,159
1 1 #include "SqpApplication.h"
2 2
3 3 #include <Data/IDataProvider.h>
4 4 #include <DataSource/DataSourceController.h>
5 5 #include <DragDropHelper.h>
6 6 #include <Network/NetworkController.h>
7 7 #include <QThread>
8 8 #include <Time/TimeController.h>
9 9 #include <Variable/Variable.h>
10 10 #include <Variable/VariableController.h>
11 11 #include <Visualization/VisualizationController.h>
12 12
13 13 Q_LOGGING_CATEGORY(LOG_SqpApplication, "SqpApplication")
14 14
15 15 class SqpApplication::SqpApplicationPrivate {
16 16 public:
17 17 SqpApplicationPrivate()
18 18 : m_DataSourceController{std::make_unique<DataSourceController>()},
19 19 m_NetworkController{std::make_unique<NetworkController>()},
20 20 m_TimeController{std::make_unique<TimeController>()},
21 21 m_VariableController{std::make_unique<VariableController>()},
22 22 m_VisualizationController{std::make_unique<VisualizationController>()},
23 23 m_DragDropHelper{std::make_unique<DragDropHelper>()}
24 24 {
25 25 // /////////////////////////////// //
26 26 // Connections between controllers //
27 27 // /////////////////////////////// //
28 28
29 29 // VariableController <-> DataSourceController
30 30 connect(m_DataSourceController.get(),
31 31 SIGNAL(variableCreationRequested(const QString &, const QVariantHash &,
32 32 std::shared_ptr<IDataProvider>)),
33 33 m_VariableController.get(),
34 34 SLOT(createVariable(const QString &, const QVariantHash &,
35 35 std::shared_ptr<IDataProvider>)));
36 36
37 37 // VariableController <-> VisualizationController
38 38 connect(m_VariableController.get(),
39 39 SIGNAL(variableAboutToBeDeleted(std::shared_ptr<Variable>)),
40 40 m_VisualizationController.get(),
41 41 SIGNAL(variableAboutToBeDeleted(std::shared_ptr<Variable>)), Qt::DirectConnection);
42 42
43 43 connect(m_VariableController.get(),
44 44 SIGNAL(rangeChanged(std::shared_ptr<Variable>, const SqpRange &)),
45 45 m_VisualizationController.get(),
46 46 SIGNAL(rangeChanged(std::shared_ptr<Variable>, const SqpRange &)));
47 47
48 48
49 49 m_DataSourceController->moveToThread(&m_DataSourceControllerThread);
50 50 m_DataSourceControllerThread.setObjectName("DataSourceControllerThread");
51 51 m_NetworkController->moveToThread(&m_NetworkControllerThread);
52 52 m_NetworkControllerThread.setObjectName("NetworkControllerThread");
53 53 m_VariableController->moveToThread(&m_VariableControllerThread);
54 54 m_VariableControllerThread.setObjectName("VariableControllerThread");
55 55 m_VisualizationController->moveToThread(&m_VisualizationControllerThread);
56 56 m_VisualizationControllerThread.setObjectName("VsualizationControllerThread");
57 57
58 58
59 59 // Additionnal init
60 60 m_VariableController->setTimeController(m_TimeController.get());
61 61 }
62 62
63 63 virtual ~SqpApplicationPrivate()
64 64 {
65 65 m_DataSourceControllerThread.quit();
66 66 m_DataSourceControllerThread.wait();
67 67
68 68 m_NetworkControllerThread.quit();
69 69 m_NetworkControllerThread.wait();
70 70
71 71 m_VariableControllerThread.quit();
72 72 m_VariableControllerThread.wait();
73 73
74 74 m_VisualizationControllerThread.quit();
75 75 m_VisualizationControllerThread.wait();
76 76 }
77 77
78 78 std::unique_ptr<DataSourceController> m_DataSourceController;
79 79 std::unique_ptr<VariableController> m_VariableController;
80 80 std::unique_ptr<TimeController> m_TimeController;
81 81 std::unique_ptr<NetworkController> m_NetworkController;
82 82 std::unique_ptr<VisualizationController> m_VisualizationController;
83 83 QThread m_DataSourceControllerThread;
84 84 QThread m_NetworkControllerThread;
85 85 QThread m_VariableControllerThread;
86 86 QThread m_VisualizationControllerThread;
87 87
88 88 std::unique_ptr<DragDropHelper> m_DragDropHelper;
89 89 };
90 90
91 91
92 92 SqpApplication::SqpApplication(int &argc, char **argv)
93 93 : QApplication{argc, argv}, impl{spimpl::make_unique_impl<SqpApplicationPrivate>()}
94 94 {
95 95 qCDebug(LOG_SqpApplication()) << tr("SqpApplication construction") << QThread::currentThread();
96 96
97 97 connect(&impl->m_DataSourceControllerThread, &QThread::started,
98 98 impl->m_DataSourceController.get(), &DataSourceController::initialize);
99 99 connect(&impl->m_DataSourceControllerThread, &QThread::finished,
100 100 impl->m_DataSourceController.get(), &DataSourceController::finalize);
101 101
102 102 connect(&impl->m_NetworkControllerThread, &QThread::started, impl->m_NetworkController.get(),
103 103 &NetworkController::initialize);
104 104 connect(&impl->m_NetworkControllerThread, &QThread::finished, impl->m_NetworkController.get(),
105 105 &NetworkController::finalize);
106 106
107 107 connect(&impl->m_VariableControllerThread, &QThread::started, impl->m_VariableController.get(),
108 108 &VariableController::initialize);
109 109 connect(&impl->m_VariableControllerThread, &QThread::finished, impl->m_VariableController.get(),
110 110 &VariableController::finalize);
111 111
112 112 connect(&impl->m_VisualizationControllerThread, &QThread::started,
113 113 impl->m_VisualizationController.get(), &VisualizationController::initialize);
114 114 connect(&impl->m_VisualizationControllerThread, &QThread::finished,
115 115 impl->m_VisualizationController.get(), &VisualizationController::finalize);
116 116
117 117 impl->m_DataSourceControllerThread.start();
118 118 impl->m_NetworkControllerThread.start();
119 119 impl->m_VariableControllerThread.start();
120 120 impl->m_VisualizationControllerThread.start();
121 121 }
122 122
123 SqpApplication::~SqpApplication() {}
123 SqpApplication::~SqpApplication()
124 {
125 }
124 126
125 void SqpApplication::initialize() {}
127 void SqpApplication::initialize()
128 {
129 }
126 130
127 131 DataSourceController &SqpApplication::dataSourceController() noexcept
128 132 {
129 133 return *impl->m_DataSourceController;
130 134 }
131 135
132 136 NetworkController &SqpApplication::networkController() noexcept
133 137 {
134 138 return *impl->m_NetworkController;
135 139 }
136 140
137 141 TimeController &SqpApplication::timeController() noexcept
138 142 {
139 143 return *impl->m_TimeController;
140 144 }
141 145
142 146 VariableController &SqpApplication::variableController() noexcept
143 147 {
144 148 return *impl->m_VariableController;
145 149 }
146 150
147 151 VisualizationController &SqpApplication::visualizationController() noexcept
148 152 {
149 153 return *impl->m_VisualizationController;
150 154 }
151 155
152 156 DragDropHelper &SqpApplication::dragDropHelper() noexcept
153 157 {
154 158 return *impl->m_DragDropHelper;
155 159 }
@@ -1,301 +1,302
1 1 #include "Visualization/VisualizationDragDropContainer.h"
2 2 #include "DragDropHelper.h"
3 3 #include "SqpApplication.h"
4 4 #include "Visualization/VisualizationDragWidget.h"
5 5
6 6 #include <QDrag>
7 7 #include <QDragEnterEvent>
8 8 #include <QVBoxLayout>
9 9
10 #include <cmath>
10 11 #include <memory>
11 12
12 13 struct VisualizationDragDropContainer::VisualizationDragDropContainerPrivate {
13 14
14 15 QVBoxLayout *m_Layout;
15 16 QStringList m_AcceptedMimeTypes;
16 17 QStringList m_MergeAllowedMimeTypes;
17 18
18 19 explicit VisualizationDragDropContainerPrivate(QWidget *widget)
19 20 {
20 21 m_Layout = new QVBoxLayout(widget);
21 22 m_Layout->setContentsMargins(0, 0, 0, 0);
22 23 }
23 24
24 25 bool acceptMimeData(const QMimeData *data) const
25 26 {
26 27 for (const auto &type : m_AcceptedMimeTypes) {
27 28 if (data->hasFormat(type)) {
28 29 return true;
29 30 }
30 31 }
31 32
32 33 return false;
33 34 }
34 35
35 36 bool allowMergeMimeData(const QMimeData *data) const
36 37 {
37 38 for (const auto &type : m_MergeAllowedMimeTypes) {
38 39 if (data->hasFormat(type)) {
39 40 return true;
40 41 }
41 42 }
42 43
43 44 return false;
44 45 }
45 46
46 47 bool hasPlaceHolder() const
47 48 {
48 49 return sqpApp->dragDropHelper().placeHolder().parentWidget() == m_Layout->parentWidget();
49 50 }
50 51
51 52 VisualizationDragWidget *getChildDragWidgetAt(QWidget *parent, const QPoint &pos) const
52 53 {
53 54 VisualizationDragWidget *dragWidget = nullptr;
54 55
55 56 for (auto child : parent->children()) {
56 57 auto widget = qobject_cast<VisualizationDragWidget *>(child);
57 58 if (widget && widget->isVisible()) {
58 59 if (widget->frameGeometry().contains(pos)) {
59 60 dragWidget = widget;
60 61 break;
61 62 }
62 63 }
63 64 }
64 65
65 66 return dragWidget;
66 67 }
67 68
68 69 bool cursorIsInContainer(QWidget *container) const
69 70 {
70 71 auto adustNum = 18; // to be safe, in case of scrollbar on the side
71 72 auto containerRect = QRect(QPoint(), container->contentsRect().size())
72 73 .adjusted(adustNum, adustNum, -adustNum, -adustNum);
73 74 qDebug() << containerRect << container->mapFromGlobal(QCursor::pos());
74 75 return containerRect.contains(container->mapFromGlobal(QCursor::pos()));
75 76 }
76 77 };
77 78
78 79 VisualizationDragDropContainer::VisualizationDragDropContainer(QWidget *parent)
79 80 : QWidget{parent},
80 81 impl{spimpl::make_unique_impl<VisualizationDragDropContainerPrivate>(this)}
81 82 {
82 83 setAcceptDrops(true);
83 84 }
84 85
85 86 void VisualizationDragDropContainer::addDragWidget(VisualizationDragWidget *dragWidget)
86 87 {
87 88 impl->m_Layout->addWidget(dragWidget);
88 89 disconnect(dragWidget, &VisualizationDragWidget::dragDetected, nullptr, nullptr);
89 90 connect(dragWidget, &VisualizationDragWidget::dragDetected, this,
90 91 &VisualizationDragDropContainer::startDrag);
91 92 }
92 93
93 94 void VisualizationDragDropContainer::insertDragWidget(int index,
94 95 VisualizationDragWidget *dragWidget)
95 96 {
96 97 impl->m_Layout->insertWidget(index, dragWidget);
97 98 disconnect(dragWidget, &VisualizationDragWidget::dragDetected, nullptr, nullptr);
98 99 connect(dragWidget, &VisualizationDragWidget::dragDetected, this,
99 100 &VisualizationDragDropContainer::startDrag);
100 101 }
101 102
102 103 void VisualizationDragDropContainer::setAcceptedMimeTypes(const QStringList &mimeTypes)
103 104 {
104 105 impl->m_AcceptedMimeTypes = mimeTypes;
105 106 }
106 107
107 108 void VisualizationDragDropContainer::setMergeAllowedMimeTypes(const QStringList &mimeTypes)
108 109 {
109 110 impl->m_MergeAllowedMimeTypes = mimeTypes;
110 111 }
111 112
112 113 int VisualizationDragDropContainer::countDragWidget() const
113 114 {
114 115 auto nbGraph = 0;
115 116 for (auto child : children()) {
116 117 auto widget = qobject_cast<VisualizationDragWidget *>(child);
117 118 if (widget) {
118 119 nbGraph += 1;
119 120 }
120 121 }
121 122
122 123 return nbGraph;
123 124 }
124 125
125 126 void VisualizationDragDropContainer::startDrag(VisualizationDragWidget *dragWidget,
126 127 const QPoint &dragPosition)
127 128 {
128 129 auto &helper = sqpApp->dragDropHelper();
129 130
130 131 // Note: The management of the drag object is done by Qt
131 132 auto *drag = new QDrag{dragWidget};
132 133 drag->setHotSpot(dragPosition);
133 134
134 135 auto mimeData = dragWidget->mimeData();
135 136 drag->setMimeData(mimeData);
136 137
137 138 auto pixmap = QPixmap(dragWidget->size());
138 139 dragWidget->render(&pixmap);
139 140 drag->setPixmap(pixmap);
140 141
141 142 auto image = pixmap.toImage();
142 143 mimeData->setImageData(image);
143 144 mimeData->setUrls({helper.imageTemporaryUrl(image)});
144 145
145 146 if (impl->m_Layout->indexOf(dragWidget) >= 0) {
146 147 helper.setCurrentDragWidget(dragWidget);
147 148
148 149 if (impl->cursorIsInContainer(this)) {
149 150 auto dragWidgetIndex = impl->m_Layout->indexOf(dragWidget);
150 151 helper.insertPlaceHolder(impl->m_Layout, dragWidgetIndex);
151 152 dragWidget->setVisible(false);
152 153 }
153 154 }
154 155
155 156 // Note: The exec() is blocking on windows but not on linux and macOS
156 157 drag->exec(Qt::MoveAction | Qt::CopyAction);
157 158 }
158 159
159 160 void VisualizationDragDropContainer::dragEnterEvent(QDragEnterEvent *event)
160 161 {
161 162 if (impl->acceptMimeData(event->mimeData())) {
162 163 event->acceptProposedAction();
163 164
164 165 auto &helper = sqpApp->dragDropHelper();
165 166
166 167 if (!impl->hasPlaceHolder()) {
167 168 auto dragWidget = helper.getCurrentDragWidget();
168 169 auto parentWidget
169 170 = qobject_cast<VisualizationDragDropContainer *>(dragWidget->parentWidget());
170 171 if (parentWidget) {
171 172 dragWidget->setVisible(false);
172 173 }
173 174
174 175 auto dragWidgetHovered = impl->getChildDragWidgetAt(this, event->pos());
175 176
176 177 if (dragWidgetHovered) {
177 178 auto hoveredWidgetIndex = impl->m_Layout->indexOf(dragWidgetHovered);
178 179 auto dragWidgetIndex = impl->m_Layout->indexOf(helper.getCurrentDragWidget());
179 180 if (dragWidgetIndex >= 0 && dragWidgetIndex <= hoveredWidgetIndex) {
180 181 hoveredWidgetIndex
181 182 += 1; // Correction of the index if the drop occurs in the same container
182 183 }
183 184
184 185 helper.insertPlaceHolder(impl->m_Layout, hoveredWidgetIndex);
185 186 }
186 187 else {
187 188 helper.insertPlaceHolder(impl->m_Layout, 0);
188 189 }
189 190 }
190 191 }
191 192 else
192 193 event->ignore();
193 194
194 195 QWidget::dragEnterEvent(event);
195 196 }
196 197
197 198 void VisualizationDragDropContainer::dragLeaveEvent(QDragLeaveEvent *event)
198 199 {
199 200 Q_UNUSED(event);
200 201
201 202 auto &helper = sqpApp->dragDropHelper();
202 203
203 204 if (!impl->cursorIsInContainer(this)) {
204 205 helper.removePlaceHolder();
205 206
206 207 bool isInternal = true;
207 208 if (isInternal) {
208 209 // Only if the drag is strated from the visualization
209 210 // Show the drag widget at its original place
210 211 // So the drag widget doesn't stay hidden if the drop occurs outside the visualization
211 212 // drop zone (It is not possible to catch a drop event outside of the application)
212 213
213 214 auto dragWidget = sqpApp->dragDropHelper().getCurrentDragWidget();
214 215 if (dragWidget) {
215 216 dragWidget->setVisible(true);
216 217 }
217 218 }
218 219 }
219 220
220 221 QWidget::dragLeaveEvent(event);
221 222 }
222 223
223 224 void VisualizationDragDropContainer::dragMoveEvent(QDragMoveEvent *event)
224 225 {
225 226 if (impl->acceptMimeData(event->mimeData())) {
226 227 auto dragWidgetHovered = impl->getChildDragWidgetAt(this, event->pos());
227 228 if (dragWidgetHovered) {
228 229 auto canMerge = impl->allowMergeMimeData(event->mimeData());
229 230
230 231 auto nbDragWidget = countDragWidget();
231 232 if (nbDragWidget > 0) {
232 233 auto graphHeight = size().height() / nbDragWidget;
233 234 auto dropIndex = floor(event->pos().y() / graphHeight);
234 235 auto zoneSize = qMin(graphHeight / 3.0, 150.0);
235 236
236 237 auto isOnTop = event->pos().y() < dropIndex * graphHeight + zoneSize;
237 238 auto isOnBottom = event->pos().y() > (dropIndex + 1) * graphHeight - zoneSize;
238 239
239 240 auto &helper = sqpApp->dragDropHelper();
240 241 auto placeHolderIndex = impl->m_Layout->indexOf(&(helper.placeHolder()));
241 242
242 243 if (isOnTop || isOnBottom) {
243 244 if (isOnBottom) {
244 245 dropIndex += 1;
245 246 }
246 247
247 248 auto dragWidgetIndex = impl->m_Layout->indexOf(helper.getCurrentDragWidget());
248 249 if (dragWidgetIndex >= 0 && dragWidgetIndex <= dropIndex) {
249 250 dropIndex += 1; // Correction of the index if the drop occurs in the same
250 251 // container
251 252 }
252 253
253 254 if (dropIndex != placeHolderIndex) {
254 255 helper.insertPlaceHolder(impl->m_Layout, dropIndex);
255 256 }
256 257 }
257 258 else if (canMerge) {
258 259 // drop on the middle -> merge
259 260 if (impl->hasPlaceHolder()) {
260 261 helper.removePlaceHolder();
261 262 }
262 263 }
263 264 }
264 265 }
265 266 }
266 267 else
267 268 event->ignore();
268 269
269 270 QWidget::dragMoveEvent(event);
270 271 }
271 272
272 273 void VisualizationDragDropContainer::dropEvent(QDropEvent *event)
273 274 {
274 275 if (impl->acceptMimeData(event->mimeData())) {
275 276 auto dragWidget = sqpApp->dragDropHelper().getCurrentDragWidget();
276 277 if (impl->hasPlaceHolder() && dragWidget) {
277 278 auto &helper = sqpApp->dragDropHelper();
278 279
279 280 auto droppedIndex = impl->m_Layout->indexOf(&helper.placeHolder());
280 281
281 282 auto dragWidgetIndex = impl->m_Layout->indexOf(dragWidget);
282 283 if (dragWidgetIndex >= 0 && dragWidgetIndex < droppedIndex) {
283 284 droppedIndex
284 285 -= 1; // Correction of the index if the drop occurs in the same container
285 286 }
286 287
287 288 dragWidget->setVisible(true);
288 289 dragWidget->setStyleSheet("");
289 290
290 291 event->acceptProposedAction();
291 292
292 293 helper.removePlaceHolder();
293 294
294 295 emit dropOccured(droppedIndex, event->mimeData());
295 296 }
296 297 }
297 298 else
298 299 event->ignore();
299 300
300 301 QWidget::dropEvent(event);
301 302 }
@@ -1,385 +1,384
1 1 #include "Visualization/VisualizationGraphWidget.h"
2 2 #include "Visualization/IVisualizationWidgetVisitor.h"
3 3 #include "Visualization/VisualizationDefs.h"
4 4 #include "Visualization/VisualizationGraphHelper.h"
5 5 #include "Visualization/VisualizationGraphRenderingDelegate.h"
6 6 #include "Visualization/VisualizationZoneWidget.h"
7 7 #include "ui_VisualizationGraphWidget.h"
8 8
9 9 #include <Data/ArrayData.h>
10 10 #include <Data/IDataSeries.h>
11 11 #include <DragDropHelper.h>
12 12 #include <Settings/SqpSettingsDefs.h>
13 13 #include <SqpApplication.h>
14 14 #include <Variable/Variable.h>
15 15 #include <Variable/VariableController.h>
16 16
17 17 #include <unordered_map>
18 18
19 19 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
20 20
21 21 namespace {
22 22
23 23 /// Key pressed to enable zoom on horizontal axis
24 24 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::NoModifier;
25 25
26 26 /// Key pressed to enable zoom on vertical axis
27 27 const auto VERTICAL_ZOOM_MODIFIER = Qt::ControlModifier;
28 28
29 29 } // namespace
30 30
31 31 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
32 32
33 33 explicit VisualizationGraphWidgetPrivate(const QString &name)
34 34 : m_Name{name},
35 35 m_DoAcquisition{true},
36 36 m_IsCalibration{false},
37 37 m_RenderingDelegate{nullptr}
38 38 {
39 39 }
40 40
41 41 QString m_Name;
42 42 // 1 variable -> n qcpplot
43 43 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
44 44 bool m_DoAcquisition;
45 45 bool m_IsCalibration;
46 46 QCPItemTracer *m_TextTracer;
47 47 /// Delegate used to attach rendering features to the plot
48 48 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
49 49 };
50 50
51 51 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
52 52 : VisualizationDragWidget{parent},
53 53 ui{new Ui::VisualizationGraphWidget},
54 54 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
55 55 {
56 56 ui->setupUi(this);
57 57
58 58 // 'Close' options : widget is deleted when closed
59 59 setAttribute(Qt::WA_DeleteOnClose);
60 60
61 61 // Set qcpplot properties :
62 62 // - Drag (on x-axis) and zoom are enabled
63 63 // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation
64 64 ui->widget->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectItems);
65 65 ui->widget->axisRect()->setRangeDrag(Qt::Horizontal);
66 66
67 67 // The delegate must be initialized after the ui as it uses the plot
68 68 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
69 69
70 70 connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress);
71 71 connect(ui->widget, &QCustomPlot::mouseRelease, this,
72 72 &VisualizationGraphWidget::onMouseRelease);
73 73 connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove);
74 74 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
75 connect(
76 ui->widget->xAxis,
77 static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(&QCPAxis::rangeChanged),
78 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
75 connect(ui->widget->xAxis, static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(
76 &QCPAxis::rangeChanged),
77 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
79 78
80 79 // Activates menu when right clicking on the graph
81 80 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
82 81 connect(ui->widget, &QCustomPlot::customContextMenuRequested, this,
83 82 &VisualizationGraphWidget::onGraphMenuRequested);
84 83
85 84 connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
86 85 &VariableController::onRequestDataLoading);
87 86
88 87 connect(&sqpApp->variableController(), &VariableController::updateVarDisplaying, this,
89 88 &VisualizationGraphWidget::onUpdateVarDisplaying);
90 89 }
91 90
92 91
93 92 VisualizationGraphWidget::~VisualizationGraphWidget()
94 93 {
95 94 delete ui;
96 95 }
97 96
98 97 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
99 98 {
100 99 auto parent = parentWidget();
101 100 do {
102 101 parent = parent->parentWidget();
103 102 } while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent));
104 103
105 104 return qobject_cast<VisualizationZoneWidget *>(parent);
106 105 }
107 106
108 107 void VisualizationGraphWidget::enableAcquisition(bool enable)
109 108 {
110 109 impl->m_DoAcquisition = enable;
111 110 }
112 111
113 112 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, SqpRange range)
114 113 {
115 114 // Uses delegate to create the qcpplot components according to the variable
116 115 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
117 116 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
118 117
119 118 // Set axes properties according to the units of the data series
120 119 /// @todo : for the moment, no control is performed on the axes: the units and the tickers
121 120 /// are fixed for the default x-axis and y-axis of the plot, and according to the new graph
122 121 auto xAxisUnit = Unit{};
123 122 auto valuesUnit = Unit{};
124 123
125 124 if (auto dataSeries = variable->dataSeries()) {
126 125 dataSeries->lockRead();
127 126 xAxisUnit = dataSeries->xAxisUnit();
128 127 valuesUnit = dataSeries->valuesUnit();
129 128 dataSeries->unlock();
130 129 }
131 130 impl->m_RenderingDelegate->setAxesProperties(xAxisUnit, valuesUnit);
132 131
133 132 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
134 133
135 134 this->enableAcquisition(false);
136 135 this->setGraphRange(range);
137 136 this->enableAcquisition(true);
138 137
139 138 emit requestDataLoading(QVector<std::shared_ptr<Variable> >() << variable, range, false);
140 139
141 140 emit variableAdded(variable);
142 141 }
143 142
144 143 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
145 144 {
146 145 // Each component associated to the variable :
147 146 // - is removed from qcpplot (which deletes it)
148 147 // - is no longer referenced in the map
149 148 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
150 149 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
151 150 emit variableAboutToBeRemoved(variable);
152 151
153 152 auto &plottablesMap = variableIt->second;
154 153
155 154 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
156 155 plottableIt != plottableEnd;) {
157 156 ui->widget->removePlottable(plottableIt->second);
158 157 plottableIt = plottablesMap.erase(plottableIt);
159 158 }
160 159
161 160 impl->m_VariableToPlotMultiMap.erase(variableIt);
162 161 }
163 162
164 163 // Updates graph
165 164 ui->widget->replot();
166 165 }
167 166
168 167 QList<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
169 168 {
170 169 auto variables = QList<std::shared_ptr<Variable> >{};
171 170 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
172 171 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
173 172 variables << it->first;
174 173 }
175 174
176 175 return variables;
177 176 }
178 177
179 178 void VisualizationGraphWidget::setYRange(const SqpRange &range)
180 179 {
181 180 ui->widget->yAxis->setRange(range.m_TStart, range.m_TEnd);
182 181 }
183 182
184 183 SqpRange VisualizationGraphWidget::graphRange() const noexcept
185 184 {
186 185 auto graphRange = ui->widget->xAxis->range();
187 186 return SqpRange{graphRange.lower, graphRange.upper};
188 187 }
189 188
190 189 void VisualizationGraphWidget::setGraphRange(const SqpRange &range)
191 190 {
192 191 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange START");
193 192 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
194 193 ui->widget->replot();
195 194 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange END");
196 195 }
197 196
198 197 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
199 198 {
200 199 if (visitor) {
201 200 visitor->visit(this);
202 201 }
203 202 else {
204 203 qCCritical(LOG_VisualizationGraphWidget())
205 204 << tr("Can't visit widget : the visitor is null");
206 205 }
207 206 }
208 207
209 208 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
210 209 {
211 210 /// @todo : for the moment, a graph can always accomodate a variable
212 211 Q_UNUSED(variable);
213 212 return true;
214 213 }
215 214
216 215 bool VisualizationGraphWidget::contains(const Variable &variable) const
217 216 {
218 217 // Finds the variable among the keys of the map
219 218 auto variablePtr = &variable;
220 219 auto findVariable
221 220 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
222 221
223 222 auto end = impl->m_VariableToPlotMultiMap.cend();
224 223 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
225 224 return it != end;
226 225 }
227 226
228 227 QString VisualizationGraphWidget::name() const
229 228 {
230 229 return impl->m_Name;
231 230 }
232 231
233 232 QMimeData *VisualizationGraphWidget::mimeData() const
234 233 {
235 234 auto *mimeData = new QMimeData;
236 235 mimeData->setData(DragDropHelper::MIME_TYPE_GRAPH, QByteArray());
237 236
238 237 return mimeData;
239 238 }
240 239
241 240 bool VisualizationGraphWidget::isDragAllowed() const
242 241 {
243 242 return true;
244 243 }
245 244
246 245 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
247 246 {
248 247 Q_UNUSED(event);
249 248
250 249 // Prevents that all variables will be removed from graph when it will be closed
251 250 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
252 251 emit variableAboutToBeRemoved(variableEntry.first);
253 252 }
254 253 }
255 254
256 255 void VisualizationGraphWidget::enterEvent(QEvent *event)
257 256 {
258 257 Q_UNUSED(event);
259 258 impl->m_RenderingDelegate->showGraphOverlay(true);
260 259 }
261 260
262 261 void VisualizationGraphWidget::leaveEvent(QEvent *event)
263 262 {
264 263 Q_UNUSED(event);
265 264 impl->m_RenderingDelegate->showGraphOverlay(false);
266 265 }
267 266
268 267 QCustomPlot &VisualizationGraphWidget::plot() noexcept
269 268 {
270 269 return *ui->widget;
271 270 }
272 271
273 272 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
274 273 {
275 274 QMenu graphMenu{};
276 275
277 276 // Iterates on variables (unique keys)
278 277 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
279 278 end = impl->m_VariableToPlotMultiMap.cend();
280 279 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
281 280 // 'Remove variable' action
282 281 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
283 282 [ this, var = it->first ]() { removeVariable(var); });
284 283 }
285 284
286 285 if (!graphMenu.isEmpty()) {
287 286 graphMenu.exec(QCursor::pos());
288 287 }
289 288 }
290 289
291 290 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
292 291 {
293 qCDebug(LOG_VisualizationGraphWidget())
294 << tr("TORM: VisualizationGraphWidget::onRangeChanged")
295 << QThread::currentThread()->objectName() << "DoAcqui" << impl->m_DoAcquisition;
292 qCDebug(LOG_VisualizationGraphWidget()) << tr("TORM: VisualizationGraphWidget::onRangeChanged")
293 << QThread::currentThread()->objectName() << "DoAcqui"
294 << impl->m_DoAcquisition;
296 295
297 296 auto graphRange = SqpRange{t1.lower, t1.upper};
298 297 auto oldGraphRange = SqpRange{t2.lower, t2.upper};
299 298
300 299 if (impl->m_DoAcquisition) {
301 300 QVector<std::shared_ptr<Variable> > variableUnderGraphVector;
302 301
303 302 for (auto it = impl->m_VariableToPlotMultiMap.begin(),
304 303 end = impl->m_VariableToPlotMultiMap.end();
305 304 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
306 305 variableUnderGraphVector.push_back(it->first);
307 306 }
308 307 emit requestDataLoading(std::move(variableUnderGraphVector), graphRange,
309 308 !impl->m_IsCalibration);
310 309
311 310 if (!impl->m_IsCalibration) {
312 311 qCDebug(LOG_VisualizationGraphWidget())
313 312 << tr("TORM: VisualizationGraphWidget::Synchronize notify !!")
314 313 << QThread::currentThread()->objectName() << graphRange << oldGraphRange;
315 314 emit synchronize(graphRange, oldGraphRange);
316 315 }
317 316 }
318 317 }
319 318
320 319 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
321 320 {
322 321 // Handles plot rendering when mouse is moving
323 322 impl->m_RenderingDelegate->onMouseMove(event);
324 323
325 324 VisualizationDragWidget::mouseMoveEvent(event);
326 325 }
327 326
328 327 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
329 328 {
330 329 auto zoomOrientations = QFlags<Qt::Orientation>{};
331 330
332 331 // Lambda that enables a zoom orientation if the key modifier related to this orientation
333 332 // has
334 333 // been pressed
335 334 auto enableOrientation
336 335 = [&zoomOrientations, event](const auto &orientation, const auto &modifier) {
337 336 auto orientationEnabled = event->modifiers().testFlag(modifier);
338 337 zoomOrientations.setFlag(orientation, orientationEnabled);
339 338 };
340 339 enableOrientation(Qt::Vertical, VERTICAL_ZOOM_MODIFIER);
341 340 enableOrientation(Qt::Horizontal, HORIZONTAL_ZOOM_MODIFIER);
342 341
343 342 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
344 343 }
345 344
346 345 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
347 346 {
348 347 impl->m_IsCalibration = event->modifiers().testFlag(Qt::ControlModifier);
349 348
350 349 plot().setInteraction(QCP::iRangeDrag, !event->modifiers().testFlag(Qt::AltModifier));
351 350
352 351 VisualizationDragWidget::mousePressEvent(event);
353 352 }
354 353
355 354 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
356 355 {
357 356 impl->m_IsCalibration = false;
358 357 }
359 358
360 359 void VisualizationGraphWidget::onDataCacheVariableUpdated()
361 360 {
362 361 auto graphRange = ui->widget->xAxis->range();
363 362 auto dateTime = SqpRange{graphRange.lower, graphRange.upper};
364 363
365 364 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
366 365 auto variable = variableEntry.first;
367 366 qCDebug(LOG_VisualizationGraphWidget())
368 367 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
369 368 qCDebug(LOG_VisualizationGraphWidget())
370 369 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
371 370 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
372 371 VisualizationGraphHelper::updateData(variableEntry.second, variable->dataSeries(),
373 372 variable->range());
374 373 }
375 374 }
376 375 }
377 376
378 377 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
379 378 const SqpRange &range)
380 379 {
381 380 auto it = impl->m_VariableToPlotMultiMap.find(variable);
382 381 if (it != impl->m_VariableToPlotMultiMap.end()) {
383 382 VisualizationGraphHelper::updateData(it->second, variable->dataSeries(), range);
384 383 }
385 384 }
@@ -1,410 +1,410
1 1 #include "Visualization/VisualizationZoneWidget.h"
2 2
3 3 #include "Visualization/IVisualizationWidgetVisitor.h"
4 4 #include "Visualization/QCustomPlotSynchronizer.h"
5 5 #include "Visualization/VisualizationGraphWidget.h"
6 6 #include "ui_VisualizationZoneWidget.h"
7 7
8 8 #include <Data/SqpRange.h>
9 9 #include <Variable/Variable.h>
10 10 #include <Variable/VariableController.h>
11 11
12 12 #include <DragDropHelper.h>
13 13 #include <QUuid>
14 14 #include <SqpApplication.h>
15 15 #include <cmath>
16 16
17 17 #include <QLayout>
18 18
19 19 Q_LOGGING_CATEGORY(LOG_VisualizationZoneWidget, "VisualizationZoneWidget")
20 20
21 21 namespace {
22 22
23 23 /// Minimum height for graph added in zones (in pixels)
24 24 const auto GRAPH_MINIMUM_HEIGHT = 300;
25 25
26 26 /// Generates a default name for a new graph, according to the number of graphs already displayed in
27 27 /// the zone
28 28 QString defaultGraphName(const QLayout &layout)
29 29 {
30 30 auto count = 0;
31 31 for (auto i = 0; i < layout.count(); ++i) {
32 32 if (dynamic_cast<VisualizationGraphWidget *>(layout.itemAt(i)->widget())) {
33 33 count++;
34 34 }
35 35 }
36 36
37 37 return QObject::tr("Graph %1").arg(count + 1);
38 38 }
39 39
40 40 /**
41 41 * Applies a function to all graphs of the zone represented by its layout
42 42 * @param layout the layout that contains graphs
43 43 * @param fun the function to apply to each graph
44 44 */
45 45 template <typename Fun>
46 46 void processGraphs(QLayout &layout, Fun fun)
47 47 {
48 48 for (auto i = 0; i < layout.count(); ++i) {
49 49 if (auto item = layout.itemAt(i)) {
50 50 if (auto visualizationGraphWidget
51 51 = dynamic_cast<VisualizationGraphWidget *>(item->widget())) {
52 52 fun(*visualizationGraphWidget);
53 53 }
54 54 }
55 55 }
56 56 }
57 57
58 58 } // namespace
59 59
60 60 struct VisualizationZoneWidget::VisualizationZoneWidgetPrivate {
61 61
62 62 explicit VisualizationZoneWidgetPrivate()
63 63 : m_SynchronisationGroupId{QUuid::createUuid()},
64 64 m_Synchronizer{std::make_unique<QCustomPlotSynchronizer>()}
65 65 {
66 66 }
67 67 QUuid m_SynchronisationGroupId;
68 68 std::unique_ptr<IGraphSynchronizer> m_Synchronizer;
69 69 };
70 70
71 71 VisualizationZoneWidget::VisualizationZoneWidget(const QString &name, QWidget *parent)
72 72 : VisualizationDragWidget{parent},
73 73 ui{new Ui::VisualizationZoneWidget},
74 74 impl{spimpl::make_unique_impl<VisualizationZoneWidgetPrivate>()}
75 75 {
76 76 ui->setupUi(this);
77 77
78 78 ui->zoneNameLabel->setText(name);
79 79
80 80 ui->dragDropContainer->setAcceptedMimeTypes({DragDropHelper::MIME_TYPE_GRAPH});
81 81 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccured, this,
82 82 &VisualizationZoneWidget::dropMimeData);
83 83
84 84 // 'Close' options : widget is deleted when closed
85 85 setAttribute(Qt::WA_DeleteOnClose);
86 86 connect(ui->closeButton, &QToolButton::clicked, this, &VisualizationZoneWidget::close);
87 87 ui->closeButton->setIcon(sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton));
88 88
89 89 // Synchronisation id
90 90 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronizationGroupId",
91 91 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
92 92 }
93 93
94 94 VisualizationZoneWidget::~VisualizationZoneWidget()
95 95 {
96 96 delete ui;
97 97 }
98 98
99 99 void VisualizationZoneWidget::addGraph(VisualizationGraphWidget *graphWidget)
100 100 {
101 101 // Synchronize new graph with others in the zone
102 102 impl->m_Synchronizer->addGraph(*graphWidget);
103 103
104 104 ui->dragDropContainer->addDragWidget(graphWidget);
105 105 }
106 106
107 107 void VisualizationZoneWidget::insertGraph(int index, VisualizationGraphWidget *graphWidget)
108 108 {
109 109 // Synchronize new graph with others in the zone
110 110 impl->m_Synchronizer->addGraph(*graphWidget);
111 111
112 112 ui->dragDropContainer->insertDragWidget(index, graphWidget);
113 113 }
114 114
115 115 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable)
116 116 {
117 117 return createGraph(variable, -1);
118 118 }
119 119
120 120 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable,
121 121 int index)
122 122 {
123 123 auto graphWidget
124 124 = new VisualizationGraphWidget{defaultGraphName(*ui->dragDropContainer->layout()), this};
125 125
126 126
127 127 // Set graph properties
128 128 graphWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
129 129 graphWidget->setMinimumHeight(GRAPH_MINIMUM_HEIGHT);
130 130
131 131
132 132 // Lambda to synchronize zone widget
133 133 auto synchronizeZoneWidget = [this, graphWidget](const SqpRange &graphRange,
134 134 const SqpRange &oldGraphRange) {
135 135
136 136 auto zoomType = VariableController::getZoomType(graphRange, oldGraphRange);
137 137 auto frameLayout = ui->dragDropContainer->layout();
138 138 for (auto i = 0; i < frameLayout->count(); ++i) {
139 139 auto graphChild
140 140 = dynamic_cast<VisualizationGraphWidget *>(frameLayout->itemAt(i)->widget());
141 141 if (graphChild && (graphChild != graphWidget)) {
142 142
143 143 auto graphChildRange = graphChild->graphRange();
144 144 switch (zoomType) {
145 145 case AcquisitionZoomType::ZoomIn: {
146 146 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
147 147 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
148 148 graphChildRange.m_TStart += deltaLeft;
149 149 graphChildRange.m_TEnd -= deltaRight;
150 150 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomIn");
151 qCDebug(LOG_VisualizationZoneWidget())
152 << tr("TORM: deltaLeft") << deltaLeft;
153 qCDebug(LOG_VisualizationZoneWidget())
154 << tr("TORM: deltaRight") << deltaRight;
151 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
152 << deltaLeft;
153 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
154 << deltaRight;
155 155 qCDebug(LOG_VisualizationZoneWidget())
156 156 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
157 157
158 158 break;
159 159 }
160 160
161 161 case AcquisitionZoomType::ZoomOut: {
162 162 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomOut");
163 163 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
164 164 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
165 qCDebug(LOG_VisualizationZoneWidget())
166 << tr("TORM: deltaLeft") << deltaLeft;
167 qCDebug(LOG_VisualizationZoneWidget())
168 << tr("TORM: deltaRight") << deltaRight;
165 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
166 << deltaLeft;
167 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
168 << deltaRight;
169 169 qCDebug(LOG_VisualizationZoneWidget())
170 170 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
171 171 graphChildRange.m_TStart -= deltaLeft;
172 172 graphChildRange.m_TEnd += deltaRight;
173 173 break;
174 174 }
175 175 case AcquisitionZoomType::PanRight: {
176 176 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: PanRight");
177 177 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
178 178 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
179 179 graphChildRange.m_TStart += deltaLeft;
180 180 graphChildRange.m_TEnd += deltaRight;
181 181 qCDebug(LOG_VisualizationZoneWidget())
182 182 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
183 183 break;
184 184 }
185 185 case AcquisitionZoomType::PanLeft: {
186 186 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: PanLeft");
187 187 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
188 188 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
189 189 graphChildRange.m_TStart -= deltaLeft;
190 190 graphChildRange.m_TEnd -= deltaRight;
191 191 break;
192 192 }
193 193 case AcquisitionZoomType::Unknown: {
194 194 qCDebug(LOG_VisualizationZoneWidget())
195 195 << tr("Impossible to synchronize: zoom type unknown");
196 196 break;
197 197 }
198 198 default:
199 199 qCCritical(LOG_VisualizationZoneWidget())
200 200 << tr("Impossible to synchronize: zoom type not take into account");
201 201 // No action
202 202 break;
203 203 }
204 204 graphChild->enableAcquisition(false);
205 qCDebug(LOG_VisualizationZoneWidget())
206 << tr("TORM: Range before: ") << graphChild->graphRange();
207 qCDebug(LOG_VisualizationZoneWidget())
208 << tr("TORM: Range after : ") << graphChildRange;
205 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: Range before: ")
206 << graphChild->graphRange();
207 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: Range after : ")
208 << graphChildRange;
209 209 qCDebug(LOG_VisualizationZoneWidget())
210 210 << tr("TORM: child dt") << graphChildRange.m_TEnd - graphChildRange.m_TStart;
211 211 graphChild->setGraphRange(graphChildRange);
212 212 graphChild->enableAcquisition(true);
213 213 }
214 214 }
215 215 };
216 216
217 217 // connection for synchronization
218 218 connect(graphWidget, &VisualizationGraphWidget::synchronize, synchronizeZoneWidget);
219 219 connect(graphWidget, &VisualizationGraphWidget::variableAdded, this,
220 220 &VisualizationZoneWidget::onVariableAdded);
221 221 connect(graphWidget, &VisualizationGraphWidget::variableAboutToBeRemoved, this,
222 222 &VisualizationZoneWidget::onVariableAboutToBeRemoved);
223 223
224 224 auto range = SqpRange{};
225 225
226 226 // Apply visitor to graph children
227 227 auto layout = ui->dragDropContainer->layout();
228 228 if (layout->count() > 0) {
229 229 // Case of a new graph in a existant zone
230 230 if (auto visualizationGraphWidget
231 231 = dynamic_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
232 232 range = visualizationGraphWidget->graphRange();
233 233 }
234 234 }
235 235 else {
236 236 // Case of a new graph as the first of the zone
237 237 range = variable->range();
238 238 }
239 239
240 240 this->insertGraph(index, graphWidget);
241 241
242 242 graphWidget->addVariable(variable, range);
243 243
244 244 // get y using variable range
245 245 if (auto dataSeries = variable->dataSeries()) {
246 246 dataSeries->lockRead();
247 247 auto valuesBounds
248 248 = dataSeries->valuesBounds(variable->range().m_TStart, variable->range().m_TEnd);
249 249 auto end = dataSeries->cend();
250 250 if (valuesBounds.first != end && valuesBounds.second != end) {
251 251 auto rangeValue = [](const auto &value) { return std::isnan(value) ? 0. : value; };
252 252
253 253 auto minValue = rangeValue(valuesBounds.first->minValue());
254 254 auto maxValue = rangeValue(valuesBounds.second->maxValue());
255 255
256 256 graphWidget->setYRange(SqpRange{minValue, maxValue});
257 257 }
258 258 dataSeries->unlock();
259 259 }
260 260
261 261 return graphWidget;
262 262 }
263 263
264 264 VisualizationGraphWidget *
265 265 VisualizationZoneWidget::createGraph(const QList<std::shared_ptr<Variable> > variables, int index)
266 266 {
267 267 if (variables.isEmpty()) {
268 268 return nullptr;
269 269 }
270 270
271 271 auto graphWidget = createGraph(variables.first(), index);
272 272 for (auto variableIt = variables.cbegin() + 1; variableIt != variables.cend(); ++variableIt) {
273 273 graphWidget->addVariable(*variableIt, graphWidget->graphRange());
274 274 }
275 275
276 276 return graphWidget;
277 277 }
278 278
279 279 void VisualizationZoneWidget::accept(IVisualizationWidgetVisitor *visitor)
280 280 {
281 281 if (visitor) {
282 282 visitor->visitEnter(this);
283 283
284 284 // Apply visitor to graph children: widgets different from graphs are not visited (no
285 285 // action)
286 286 processGraphs(
287 287 *ui->dragDropContainer->layout(),
288 288 [visitor](VisualizationGraphWidget &graphWidget) { graphWidget.accept(visitor); });
289 289
290 290 visitor->visitLeave(this);
291 291 }
292 292 else {
293 293 qCCritical(LOG_VisualizationZoneWidget()) << tr("Can't visit widget : the visitor is null");
294 294 }
295 295 }
296 296
297 297 bool VisualizationZoneWidget::canDrop(const Variable &variable) const
298 298 {
299 299 // A tab can always accomodate a variable
300 300 Q_UNUSED(variable);
301 301 return true;
302 302 }
303 303
304 304 bool VisualizationZoneWidget::contains(const Variable &variable) const
305 305 {
306 306 Q_UNUSED(variable);
307 307 return false;
308 308 }
309 309
310 310 QString VisualizationZoneWidget::name() const
311 311 {
312 312 return ui->zoneNameLabel->text();
313 313 }
314 314
315 315 QMimeData *VisualizationZoneWidget::mimeData() const
316 316 {
317 317 auto *mimeData = new QMimeData;
318 318 mimeData->setData(DragDropHelper::MIME_TYPE_ZONE, QByteArray());
319 319
320 320 return mimeData;
321 321 }
322 322
323 323 bool VisualizationZoneWidget::isDragAllowed() const
324 324 {
325 325 return true;
326 326 }
327 327
328 328 void VisualizationZoneWidget::closeEvent(QCloseEvent *event)
329 329 {
330 330 // Closes graphs in the zone
331 331 processGraphs(*ui->dragDropContainer->layout(),
332 332 [](VisualizationGraphWidget &graphWidget) { graphWidget.close(); });
333 333
334 334 // Delete synchronization group from variable controller
335 335 QMetaObject::invokeMethod(&sqpApp->variableController(), "onRemoveSynchronizationGroupId",
336 336 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
337 337
338 338 QWidget::closeEvent(event);
339 339 }
340 340
341 341 void VisualizationZoneWidget::onVariableAdded(std::shared_ptr<Variable> variable)
342 342 {
343 343 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronized",
344 344 Qt::QueuedConnection, Q_ARG(std::shared_ptr<Variable>, variable),
345 345 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
346 346 }
347 347
348 348 void VisualizationZoneWidget::onVariableAboutToBeRemoved(std::shared_ptr<Variable> variable)
349 349 {
350 350 QMetaObject::invokeMethod(&sqpApp->variableController(), "desynchronize", Qt::QueuedConnection,
351 351 Q_ARG(std::shared_ptr<Variable>, variable),
352 352 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
353 353 }
354 354
355 355 void VisualizationZoneWidget::dropMimeData(int index, const QMimeData *mimeData)
356 356 {
357 357 auto &helper = sqpApp->dragDropHelper();
358 358 if (mimeData->hasFormat(DragDropHelper::MIME_TYPE_GRAPH)) {
359 359 auto graphWidget = static_cast<VisualizationGraphWidget *>(helper.getCurrentDragWidget());
360 360 auto parentDragDropContainer
361 361 = qobject_cast<VisualizationDragDropContainer *>(graphWidget->parentWidget());
362 362 Q_ASSERT(parentDragDropContainer);
363 363
364 364 const auto &variables = graphWidget->variables();
365 365
366 366 if (parentDragDropContainer != ui->dragDropContainer && !variables.isEmpty()) {
367 367 // The drop didn't occur in the same zone
368 368
369 369 // Abort the requests for the variables (if any)
370 370 // Commented, because it's not sure if it's needed or not
371 371 // for (const auto& var : variables)
372 372 //{
373 373 // sqpApp->variableController().onAbortProgressRequested(var);
374 374 //}
375 375
376 376 auto previousParentZoneWidget = graphWidget->parentZoneWidget();
377 377 auto nbGraph = parentDragDropContainer->countDragWidget();
378 378 if (nbGraph == 1) {
379 379 // This is the only graph in the previous zone, close the zone
380 380 previousParentZoneWidget->close();
381 381 }
382 382 else {
383 383 // Close the graph
384 384 graphWidget->close();
385 385 }
386 386
387 387 // Creates the new graph in the zone
388 388 createGraph(variables, index);
389 389 }
390 390 else {
391 391 // The drop occurred in the same zone or the graph is empty
392 392 // Simple move of the graph, no variable operation associated
393 393 parentDragDropContainer->layout()->removeWidget(graphWidget);
394 394
395 395 if (variables.isEmpty() && parentDragDropContainer != ui->dragDropContainer) {
396 396 // The graph is empty and dropped in a different zone.
397 397 // Take the range of the first graph in the zone (if existing).
398 398 auto layout = ui->dragDropContainer->layout();
399 399 if (layout->count() > 0) {
400 400 if (auto visualizationGraphWidget
401 401 = qobject_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
402 402 graphWidget->setGraphRange(visualizationGraphWidget->graphRange());
403 403 }
404 404 }
405 405 }
406 406
407 407 ui->dragDropContainer->insertDragWidget(index, graphWidget);
408 408 }
409 409 }
410 410 }
General Comments 6
Approved

Status change > Approved

You need to be logged in to leave comments. Login now