##// END OF EJS Templates
Fix bug time precision with catalogue
perrinel -
r1315:b20dcec1c57b
parent child
Show More
@@ -1,19 +1,23
1 1 #ifndef SCIQLOP_DATEUTILS_H
2 2 #define SCIQLOP_DATEUTILS_H
3 3
4 4 #include "CoreGlobal.h"
5 5
6 6 #include <QDateTime>
7 7
8 /// Format for datetimes
9 const auto DATETIME_FORMAT = QStringLiteral("dd/MM/yyyy \nhh:mm:ss:zzz");
10 const auto DATETIME_FORMAT_ONE_LINE = QStringLiteral("dd/MM/yyyy hh:mm:ss:zzz");
11
8 12 /**
9 13 * Utility class with methods for dates
10 14 */
11 15 struct SCIQLOP_CORE_EXPORT DateUtils {
12 16 /// Converts seconds (since epoch) to datetime. By default, the datetime is in UTC
13 17 static QDateTime dateTime(double secs, Qt::TimeSpec timeSpec = Qt::UTC) noexcept;
14 18
15 19 /// Converts datetime to seconds since epoch
16 20 static double secondsSinceEpoch(const QDateTime &dateTime) noexcept;
17 21 };
18 22
19 23 #endif // SCIQLOP_DATEUTILS_H
@@ -1,396 +1,393
1 1 #include <Variable/Variable.h>
2 2 #include <Variable/VariableController.h>
3 3 #include <Variable/VariableModel.h>
4 4
5 5 #include <Common/DateUtils.h>
6 6 #include <Common/MimeTypesDef.h>
7 7 #include <Common/StringUtils.h>
8 8
9 9 #include <Data/IDataSeries.h>
10 10
11 11 #include <DataSource/DataSourceController.h>
12 12 #include <Time/TimeController.h>
13 13
14 14 #include <QMimeData>
15 15 #include <QSize>
16 16 #include <QTimer>
17 17 #include <unordered_map>
18 18
19 19 Q_LOGGING_CATEGORY(LOG_VariableModel, "VariableModel")
20 20
21 21 namespace {
22 22
23 23 // Column indexes
24 24 const auto NAME_COLUMN = 0;
25 25 const auto TSTART_COLUMN = 1;
26 26 const auto TEND_COLUMN = 2;
27 27 const auto NBPOINTS_COLUMN = 3;
28 28 const auto UNIT_COLUMN = 4;
29 29 const auto MISSION_COLUMN = 5;
30 30 const auto PLUGIN_COLUMN = 6;
31 31 const auto NB_COLUMNS = 7;
32 32
33 33 // Column properties
34 34 const auto DEFAULT_HEIGHT = 25;
35 35 const auto DEFAULT_WIDTH = 100;
36 36
37 37 struct ColumnProperties {
38 38 ColumnProperties(const QString &name = {}, int width = DEFAULT_WIDTH,
39 39 int height = DEFAULT_HEIGHT)
40 40 : m_Name{name}, m_Width{width}, m_Height{height}
41 41 {
42 42 }
43 43
44 44 QString m_Name;
45 45 int m_Width;
46 46 int m_Height;
47 47 };
48 48
49 49 const auto COLUMN_PROPERTIES = QHash<int, ColumnProperties>{
50 50 {NAME_COLUMN, {QObject::tr("Name")}}, {TSTART_COLUMN, {QObject::tr("tStart"), 180}},
51 51 {TEND_COLUMN, {QObject::tr("tEnd"), 180}}, {NBPOINTS_COLUMN, {QObject::tr("Nb points")}},
52 52 {UNIT_COLUMN, {QObject::tr("Unit")}}, {MISSION_COLUMN, {QObject::tr("Mission")}},
53 53 {PLUGIN_COLUMN, {QObject::tr("Plugin")}}};
54 54
55 /// Format for datetimes
56 const auto DATETIME_FORMAT = QStringLiteral("dd/MM/yyyy \nhh:mm:ss:zzz");
57
58 55 QString uniqueName(const QString &defaultName,
59 56 const std::vector<std::shared_ptr<Variable> > &variables)
60 57 {
61 58 auto forbiddenNames = std::vector<QString>(variables.size());
62 59 std::transform(variables.cbegin(), variables.cend(), forbiddenNames.begin(),
63 60 [](const auto &variable) { return variable->name(); });
64 61 auto uniqueName = StringUtils::uniqueName(defaultName, forbiddenNames);
65 62 Q_ASSERT(!uniqueName.isEmpty());
66 63
67 64 return uniqueName;
68 65 }
69 66
70 67 } // namespace
71 68
72 69 struct VariableModel::VariableModelPrivate {
73 70 /// Variables created in SciQlop
74 71 std::vector<std::shared_ptr<Variable> > m_Variables;
75 72 std::unordered_map<std::shared_ptr<Variable>, double> m_VariableToProgress;
76 73 VariableController *m_VariableController;
77 74
78 75 /// Return the row index of the variable. -1 if it's not found
79 76 int indexOfVariable(Variable *variable) const noexcept;
80 77 };
81 78
82 79 VariableModel::VariableModel(VariableController *parent)
83 80 : QAbstractTableModel{parent}, impl{spimpl::make_unique_impl<VariableModelPrivate>()}
84 81 {
85 82 impl->m_VariableController = parent;
86 83 }
87 84
88 85 void VariableModel::addVariable(std::shared_ptr<Variable> variable) noexcept
89 86 {
90 87 auto insertIndex = rowCount();
91 88 beginInsertRows({}, insertIndex, insertIndex);
92 89
93 90 // Generates unique name for the variable
94 91 variable->setName(uniqueName(variable->name(), impl->m_Variables));
95 92
96 93 impl->m_Variables.push_back(variable);
97 94 connect(variable.get(), &Variable::updated, this, &VariableModel::onVariableUpdated);
98 95
99 96 endInsertRows();
100 97 }
101 98
102 99 bool VariableModel::containsVariable(std::shared_ptr<Variable> variable) const noexcept
103 100 {
104 101 auto end = impl->m_Variables.cend();
105 102 return std::find(impl->m_Variables.cbegin(), end, variable) != end;
106 103 }
107 104
108 105 std::shared_ptr<Variable> VariableModel::createVariable(const QString &name,
109 106 const QVariantHash &metadata) noexcept
110 107 {
111 108 auto variable = std::make_shared<Variable>(name, metadata);
112 109 addVariable(variable);
113 110
114 111 return variable;
115 112 }
116 113
117 114 void VariableModel::deleteVariable(std::shared_ptr<Variable> variable) noexcept
118 115 {
119 116 if (!variable) {
120 117 qCCritical(LOG_Variable()) << "Can't delete a null variable from the model";
121 118 return;
122 119 }
123 120
124 121 // Finds variable in the model
125 122 auto begin = impl->m_Variables.cbegin();
126 123 auto end = impl->m_Variables.cend();
127 124 auto it = std::find(begin, end, variable);
128 125 if (it != end) {
129 126 auto removeIndex = std::distance(begin, it);
130 127
131 128 // Deletes variable
132 129 beginRemoveRows({}, removeIndex, removeIndex);
133 130 impl->m_Variables.erase(it);
134 131 endRemoveRows();
135 132 }
136 133 else {
137 134 qCritical(LOG_VariableModel())
138 135 << tr("Can't delete variable %1 from the model: the variable is not in the model")
139 136 .arg(variable->name());
140 137 }
141 138
142 139 // Removes variable from progress map
143 140 impl->m_VariableToProgress.erase(variable);
144 141 }
145 142
146 143
147 144 std::shared_ptr<Variable> VariableModel::variable(int index) const
148 145 {
149 146 return (index >= 0u && static_cast<size_t>(index) < impl->m_Variables.size())
150 147 ? impl->m_Variables[index]
151 148 : nullptr;
152 149 }
153 150
154 151 std::vector<std::shared_ptr<Variable> > VariableModel::variables() const
155 152 {
156 153 return impl->m_Variables;
157 154 }
158 155
159 156 void VariableModel::setDataProgress(std::shared_ptr<Variable> variable, double progress)
160 157 {
161 158 if (progress > 0.0) {
162 159 impl->m_VariableToProgress[variable] = progress;
163 160 }
164 161 else {
165 162 impl->m_VariableToProgress.erase(variable);
166 163 }
167 164 auto modelIndex = createIndex(impl->indexOfVariable(variable.get()), NAME_COLUMN);
168 165
169 166 emit dataChanged(modelIndex, modelIndex);
170 167 }
171 168
172 169 int VariableModel::columnCount(const QModelIndex &parent) const
173 170 {
174 171 Q_UNUSED(parent);
175 172
176 173 return NB_COLUMNS;
177 174 }
178 175
179 176 int VariableModel::rowCount(const QModelIndex &parent) const
180 177 {
181 178 Q_UNUSED(parent);
182 179
183 180 return impl->m_Variables.size();
184 181 }
185 182
186 183 QVariant VariableModel::data(const QModelIndex &index, int role) const
187 184 {
188 185 if (!index.isValid()) {
189 186 return QVariant{};
190 187 }
191 188
192 189 if (index.row() < 0 || index.row() >= rowCount()) {
193 190 return QVariant{};
194 191 }
195 192
196 193 if (role == Qt::DisplayRole) {
197 194 if (auto variable = impl->m_Variables.at(index.row()).get()) {
198 195 switch (index.column()) {
199 196 case NAME_COLUMN:
200 197 return variable->name();
201 198 case TSTART_COLUMN: {
202 199 auto range = variable->realRange();
203 200 return range != INVALID_RANGE
204 201 ? DateUtils::dateTime(range.m_TStart).toString(DATETIME_FORMAT)
205 202 : QVariant{};
206 203 }
207 204 case TEND_COLUMN: {
208 205 auto range = variable->realRange();
209 206 return range != INVALID_RANGE
210 207 ? DateUtils::dateTime(range.m_TEnd).toString(DATETIME_FORMAT)
211 208 : QVariant{};
212 209 }
213 210 case NBPOINTS_COLUMN:
214 211 return variable->nbPoints();
215 212 case UNIT_COLUMN:
216 213 return variable->metadata().value(QStringLiteral("units"));
217 214 case MISSION_COLUMN:
218 215 return variable->metadata().value(QStringLiteral("mission"));
219 216 case PLUGIN_COLUMN:
220 217 return variable->metadata().value(QStringLiteral("plugin"));
221 218 default:
222 219 // No action
223 220 break;
224 221 }
225 222
226 223 qWarning(LOG_VariableModel())
227 224 << tr("Can't get data (unknown column %1)").arg(index.column());
228 225 }
229 226 else {
230 227 qWarning(LOG_VariableModel()) << tr("Can't get data (no variable)");
231 228 }
232 229 }
233 230 else if (role == VariableRoles::ProgressRole) {
234 231 if (auto variable = impl->m_Variables.at(index.row())) {
235 232
236 233 auto it = impl->m_VariableToProgress.find(variable);
237 234 if (it != impl->m_VariableToProgress.cend()) {
238 235 return it->second;
239 236 }
240 237 }
241 238 }
242 239
243 240 return QVariant{};
244 241 }
245 242
246 243 QVariant VariableModel::headerData(int section, Qt::Orientation orientation, int role) const
247 244 {
248 245 if (role != Qt::DisplayRole && role != Qt::SizeHintRole) {
249 246 return QVariant{};
250 247 }
251 248
252 249 if (orientation == Qt::Horizontal) {
253 250 auto propertiesIt = COLUMN_PROPERTIES.find(section);
254 251 if (propertiesIt != COLUMN_PROPERTIES.cend()) {
255 252 // Role is either DisplayRole or SizeHintRole
256 253 return (role == Qt::DisplayRole)
257 254 ? QVariant{propertiesIt->m_Name}
258 255 : QVariant{QSize{propertiesIt->m_Width, propertiesIt->m_Height}};
259 256 }
260 257 else {
261 258 qWarning(LOG_VariableModel())
262 259 << tr("Can't get header data (unknown column %1)").arg(section);
263 260 }
264 261 }
265 262
266 263 return QVariant{};
267 264 }
268 265
269 266 Qt::ItemFlags VariableModel::flags(const QModelIndex &index) const
270 267 {
271 268 return QAbstractTableModel::flags(index) | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
272 269 }
273 270
274 271 Qt::DropActions VariableModel::supportedDropActions() const
275 272 {
276 273 return Qt::CopyAction | Qt::MoveAction;
277 274 }
278 275
279 276 Qt::DropActions VariableModel::supportedDragActions() const
280 277 {
281 278 return Qt::CopyAction | Qt::MoveAction;
282 279 }
283 280
284 281 QStringList VariableModel::mimeTypes() const
285 282 {
286 283 return {MIME_TYPE_VARIABLE_LIST, MIME_TYPE_TIME_RANGE};
287 284 }
288 285
289 286 QMimeData *VariableModel::mimeData(const QModelIndexList &indexes) const
290 287 {
291 288 auto mimeData = new QMimeData;
292 289
293 290 QList<std::shared_ptr<Variable> > variableList;
294 291
295 292
296 293 SqpRange firstTimeRange;
297 294 for (const auto &index : indexes) {
298 295 if (index.column() == 0) { // only the first column
299 296 auto variable = impl->m_Variables.at(index.row());
300 297 if (variable.get() && index.isValid()) {
301 298
302 299 if (variableList.isEmpty()) {
303 300 // Gets the range of the first variable
304 301 firstTimeRange = std::move(variable->range());
305 302 }
306 303
307 304 variableList << variable;
308 305 }
309 306 }
310 307 }
311 308
312 309 auto variablesEncodedData = impl->m_VariableController->mimeDataForVariables(variableList);
313 310 mimeData->setData(MIME_TYPE_VARIABLE_LIST, variablesEncodedData);
314 311
315 312 if (variableList.count() == 1) {
316 313 // No time range MIME data if multiple variables are dragged
317 314 auto timeEncodedData = TimeController::mimeDataForTimeRange(firstTimeRange);
318 315 mimeData->setData(MIME_TYPE_TIME_RANGE, timeEncodedData);
319 316 }
320 317
321 318 return mimeData;
322 319 }
323 320
324 321 bool VariableModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row,
325 322 int column, const QModelIndex &parent) const
326 323 {
327 324 // drop of a product
328 325 return data->hasFormat(MIME_TYPE_PRODUCT_LIST)
329 326 || (data->hasFormat(MIME_TYPE_TIME_RANGE) && parent.isValid()
330 327 && !data->hasFormat(MIME_TYPE_VARIABLE_LIST));
331 328 }
332 329
333 330 bool VariableModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column,
334 331 const QModelIndex &parent)
335 332 {
336 333 auto dropDone = false;
337 334
338 335 if (data->hasFormat(MIME_TYPE_PRODUCT_LIST)) {
339 336
340 337 auto productList
341 338 = DataSourceController::productsDataForMimeData(data->data(MIME_TYPE_PRODUCT_LIST));
342 339
343 340 for (auto metaData : productList) {
344 341 emit requestVariable(metaData.toHash());
345 342 }
346 343
347 344 dropDone = true;
348 345 }
349 346 else if (data->hasFormat(MIME_TYPE_TIME_RANGE) && parent.isValid()) {
350 347 auto variable = this->variable(parent.row());
351 348 auto range = TimeController::timeRangeForMimeData(data->data(MIME_TYPE_TIME_RANGE));
352 349
353 350 emit requestVariableRangeUpdate(variable, range);
354 351
355 352 dropDone = true;
356 353 }
357 354
358 355 return dropDone;
359 356 }
360 357
361 358 void VariableModel::abortProgress(const QModelIndex &index)
362 359 {
363 360 if (auto variable = impl->m_Variables.at(index.row())) {
364 361 emit abortProgessRequested(variable);
365 362 }
366 363 }
367 364
368 365 void VariableModel::onVariableUpdated() noexcept
369 366 {
370 367 // Finds variable that has been updated in the model
371 368 if (auto updatedVariable = dynamic_cast<Variable *>(sender())) {
372 369 auto updatedVariableIndex = impl->indexOfVariable(updatedVariable);
373 370
374 371 if (updatedVariableIndex > -1) {
375 372 emit dataChanged(createIndex(updatedVariableIndex, 0),
376 373 createIndex(updatedVariableIndex, columnCount() - 1));
377 374 }
378 375 }
379 376 }
380 377
381 378 int VariableModel::VariableModelPrivate::indexOfVariable(Variable *variable) const noexcept
382 379 {
383 380 auto begin = std::cbegin(m_Variables);
384 381 auto end = std::cend(m_Variables);
385 382 auto it
386 383 = std::find_if(begin, end, [variable](const auto &var) { return var.get() == variable; });
387 384
388 385 if (it != end) {
389 386 // Gets the index of the variable in the model: we assume here that views have the same
390 387 // order as the model
391 388 return std::distance(begin, it);
392 389 }
393 390 else {
394 391 return -1;
395 392 }
396 393 }
@@ -1,478 +1,484
1 1 #include "Catalogue/CatalogueEventsModel.h"
2 2
3 3 #include <Catalogue/CatalogueController.h>
4 4 #include <Common/DateUtils.h>
5 5 #include <Common/MimeTypesDef.h>
6 6 #include <DBEvent.h>
7 7 #include <DBEventProduct.h>
8 8 #include <DBTag.h>
9 9 #include <Data/SqpRange.h>
10 10 #include <SqpApplication.h>
11 11 #include <Time/TimeController.h>
12 12
13 13 #include <list>
14 14 #include <unordered_map>
15 15
16 16 #include <QHash>
17 17 #include <QMimeData>
18 18
19 19 Q_LOGGING_CATEGORY(LOG_CatalogueEventsModel, "CatalogueEventsModel")
20 20
21 21 const auto EVENT_ITEM_TYPE = 1;
22 22 const auto EVENT_PRODUCT_ITEM_TYPE = 2;
23 23
24 24 struct CatalogueEventsModel::CatalogueEventsModelPrivate {
25 25 QVector<std::shared_ptr<DBEvent> > m_Events;
26 26 std::unordered_map<DBEvent *, QVector<std::shared_ptr<DBEventProduct> > > m_EventProducts;
27 27 QVector<std::shared_ptr<DBCatalogue> > m_SourceCatalogue;
28 28
29 29 QStringList columnNames()
30 30 {
31 31 return QStringList{tr("Event"), tr("TStart"), tr("TEnd"),
32 32 tr("Tags"), tr("Product"), tr("")};
33 33 }
34 34
35 35 QVariant sortData(int col, const std::shared_ptr<DBEvent> &event) const
36 36 {
37 37 if (col == (int)CatalogueEventsModel::Column::Validation) {
38 38 auto hasChanges = sqpApp->catalogueController().eventHasChanges(event);
39 39 return hasChanges ? true : QVariant();
40 40 }
41 41
42 42 return eventData(col, event);
43 43 }
44 44
45 45 QVariant eventData(int col, const std::shared_ptr<DBEvent> &event) const
46 46 {
47 47 switch (static_cast<Column>(col)) {
48 48 case CatalogueEventsModel::Column::Name:
49 49 return event->getName();
50 50 case CatalogueEventsModel::Column::TStart:
51 return nbEventProducts(event) > 0 ? DateUtils::dateTime(event->getTStart())
51 return nbEventProducts(event) > 0
52 ? DateUtils::dateTime(event->getTStart())
53 .toString(DATETIME_FORMAT_ONE_LINE)
52 54 : QVariant{};
53 55 case CatalogueEventsModel::Column::TEnd:
54 return nbEventProducts(event) > 0 ? DateUtils::dateTime(event->getTEnd())
56 return nbEventProducts(event) > 0
57 ? DateUtils::dateTime(event->getTEnd())
58 .toString(DATETIME_FORMAT_ONE_LINE)
55 59 : QVariant{};
56 60 case CatalogueEventsModel::Column::Product: {
57 61 auto eventProducts = event->getEventProducts();
58 62 QStringList eventProductList;
59 63 for (auto evtProduct : eventProducts) {
60 64 eventProductList << evtProduct.getProductId();
61 65 }
62 66 return eventProductList.join(";");
63 67 }
64 68 case CatalogueEventsModel::Column::Tags: {
65 69 QString tagList;
66 70 auto tags = event->getTags();
67 71 for (auto tag : tags) {
68 72 tagList += tag.getName();
69 73 tagList += ' ';
70 74 }
71 75
72 76 return tagList;
73 77 }
74 78 case CatalogueEventsModel::Column::Validation:
75 79 return QVariant();
76 80 default:
77 81 break;
78 82 }
79 83
80 84 Q_ASSERT(false);
81 85 return QStringLiteral("Unknown Data");
82 86 }
83 87
84 88 void parseEventProduct(const std::shared_ptr<DBEvent> &event)
85 89 {
86 90 for (auto product : event->getEventProducts()) {
87 91 m_EventProducts[event.get()].append(std::make_shared<DBEventProduct>(product));
88 92 }
89 93 }
90 94
91 95 int nbEventProducts(const std::shared_ptr<DBEvent> &event) const
92 96 {
93 97 auto eventProductsIt = m_EventProducts.find(event.get());
94 98 if (eventProductsIt != m_EventProducts.cend()) {
95 99 return m_EventProducts.at(event.get()).count();
96 100 }
97 101 else {
98 102 return 0;
99 103 }
100 104 }
101 105
102 106 QVariant eventProductData(int col, const std::shared_ptr<DBEventProduct> &eventProduct) const
103 107 {
104 108 switch (static_cast<Column>(col)) {
105 109 case CatalogueEventsModel::Column::Name:
106 110 return eventProduct->getProductId();
107 111 case CatalogueEventsModel::Column::TStart:
108 return DateUtils::dateTime(eventProduct->getTStart());
112 return DateUtils::dateTime(eventProduct->getTStart())
113 .toString(DATETIME_FORMAT_ONE_LINE);
109 114 case CatalogueEventsModel::Column::TEnd:
110 return DateUtils::dateTime(eventProduct->getTEnd());
115 return DateUtils::dateTime(eventProduct->getTEnd())
116 .toString(DATETIME_FORMAT_ONE_LINE);
111 117 case CatalogueEventsModel::Column::Product:
112 118 return eventProduct->getProductId();
113 119 case CatalogueEventsModel::Column::Tags:
114 120 return QString();
115 121 case CatalogueEventsModel::Column::Validation:
116 122 return QVariant();
117 123 default:
118 124 break;
119 125 }
120 126
121 127 Q_ASSERT(false);
122 128 return QStringLiteral("Unknown Data");
123 129 }
124 130
125 131 void refreshChildrenOfIndex(CatalogueEventsModel *model, const QModelIndex &index) const
126 132 {
127 133 auto childCount = model->rowCount(index);
128 134 auto colCount = model->columnCount();
129 135 emit model->dataChanged(model->index(0, 0, index),
130 136 model->index(childCount, colCount, index));
131 137 }
132 138 };
133 139
134 140 CatalogueEventsModel::CatalogueEventsModel(QObject *parent)
135 141 : QAbstractItemModel(parent), impl{spimpl::make_unique_impl<CatalogueEventsModelPrivate>()}
136 142 {
137 143 }
138 144
139 145 void CatalogueEventsModel::setSourceCatalogues(
140 146 const QVector<std::shared_ptr<DBCatalogue> > &catalogues)
141 147 {
142 148 impl->m_SourceCatalogue = catalogues;
143 149 }
144 150
145 151 void CatalogueEventsModel::setEvents(const QVector<std::shared_ptr<DBEvent> > &events)
146 152 {
147 153 beginResetModel();
148 154
149 155 impl->m_Events = events;
150 156 impl->m_EventProducts.clear();
151 157 for (auto event : events) {
152 158 impl->parseEventProduct(event);
153 159 }
154 160
155 161 endResetModel();
156 162 }
157 163
158 164 std::shared_ptr<DBEvent> CatalogueEventsModel::getEvent(const QModelIndex &index) const
159 165 {
160 166 if (itemTypeOf(index) == CatalogueEventsModel::ItemType::Event) {
161 167 return impl->m_Events.value(index.row());
162 168 }
163 169 else {
164 170 return nullptr;
165 171 }
166 172 }
167 173
168 174 std::shared_ptr<DBEvent> CatalogueEventsModel::getParentEvent(const QModelIndex &index) const
169 175 {
170 176 if (itemTypeOf(index) == CatalogueEventsModel::ItemType::EventProduct) {
171 177 return getEvent(index.parent());
172 178 }
173 179 else {
174 180 return nullptr;
175 181 }
176 182 }
177 183
178 184 std::shared_ptr<DBEventProduct>
179 185 CatalogueEventsModel::getEventProduct(const QModelIndex &index) const
180 186 {
181 187 if (itemTypeOf(index) == CatalogueEventsModel::ItemType::EventProduct) {
182 188 auto event = static_cast<DBEvent *>(index.internalPointer());
183 189 return impl->m_EventProducts.at(event).value(index.row());
184 190 }
185 191 else {
186 192 return nullptr;
187 193 }
188 194 }
189 195
190 196 void CatalogueEventsModel::addEvent(const std::shared_ptr<DBEvent> &event)
191 197 {
192 198 beginInsertRows(QModelIndex(), impl->m_Events.count(), impl->m_Events.count());
193 199 impl->m_Events.append(event);
194 200 impl->parseEventProduct(event);
195 201 endInsertRows();
196 202
197 203 // Also refreshes its children event products
198 204 auto eventIndex = index(impl->m_Events.count(), 0);
199 205 impl->refreshChildrenOfIndex(this, eventIndex);
200 206 }
201 207
202 208 void CatalogueEventsModel::removeEvent(const std::shared_ptr<DBEvent> &event)
203 209 {
204 210 auto index = impl->m_Events.indexOf(event);
205 211 if (index >= 0) {
206 212 beginRemoveRows(QModelIndex(), index, index);
207 213 impl->m_Events.removeAt(index);
208 214 impl->m_EventProducts.erase(event.get());
209 215 endRemoveRows();
210 216 }
211 217 }
212 218
213 219 QVector<std::shared_ptr<DBEvent> > CatalogueEventsModel::events() const
214 220 {
215 221 return impl->m_Events;
216 222 }
217 223
218 224 void CatalogueEventsModel::refreshEvent(const std::shared_ptr<DBEvent> &event,
219 225 bool refreshEventProducts)
220 226 {
221 227 auto eventIndex = indexOf(event);
222 228 if (eventIndex.isValid()) {
223 229
224 230 if (refreshEventProducts) {
225 231 // Reparse the associated event products
226 232
227 233 auto nbEventProducts = impl->nbEventProducts(event);
228 234 auto newNbOfEventProducts = event->getEventProducts().size();
229 235 if (newNbOfEventProducts < nbEventProducts) {
230 236 beginRemoveRows(eventIndex, newNbOfEventProducts, nbEventProducts - 1);
231 237 impl->m_EventProducts.erase(event.get());
232 238 impl->parseEventProduct(event);
233 239 endRemoveRows();
234 240 }
235 241 else if (newNbOfEventProducts > nbEventProducts) {
236 242 beginInsertRows(eventIndex, nbEventProducts, newNbOfEventProducts - 1);
237 243 impl->m_EventProducts.erase(event.get());
238 244 impl->parseEventProduct(event);
239 245 endInsertRows();
240 246 }
241 247 else { // newNbOfEventProducts == nbEventProducts
242 248 impl->m_EventProducts.erase(event.get());
243 249 impl->parseEventProduct(event);
244 250 }
245 251 }
246 252
247 253 // Refreshes the event line
248 254 auto colCount = columnCount();
249 255 emit dataChanged(eventIndex, index(eventIndex.row(), colCount));
250 256
251 257 // Also refreshes its children event products
252 258 impl->refreshChildrenOfIndex(this, eventIndex);
253 259 }
254 260 else {
255 261 qCWarning(LOG_CatalogueEventsModel()) << "refreshEvent: event not found.";
256 262 }
257 263 }
258 264
259 265 QModelIndex CatalogueEventsModel::indexOf(const std::shared_ptr<DBEvent> &event) const
260 266 {
261 267 auto row = impl->m_Events.indexOf(event);
262 268 if (row >= 0) {
263 269 return index(row, 0);
264 270 }
265 271
266 272 return QModelIndex();
267 273 }
268 274
269 275 QModelIndex CatalogueEventsModel::index(int row, int column, const QModelIndex &parent) const
270 276 {
271 277 if (!hasIndex(row, column, parent)) {
272 278 return QModelIndex();
273 279 }
274 280
275 281 switch (itemTypeOf(parent)) {
276 282 case CatalogueEventsModel::ItemType::Root:
277 283 return createIndex(row, column);
278 284 case CatalogueEventsModel::ItemType::Event: {
279 285 auto event = getEvent(parent);
280 286 return createIndex(row, column, event.get());
281 287 }
282 288 case CatalogueEventsModel::ItemType::EventProduct:
283 289 break;
284 290 default:
285 291 break;
286 292 }
287 293
288 294 return QModelIndex();
289 295 }
290 296
291 297 QModelIndex CatalogueEventsModel::parent(const QModelIndex &index) const
292 298 {
293 299 switch (itemTypeOf(index)) {
294 300 case CatalogueEventsModel::ItemType::EventProduct: {
295 301 auto parentEvent = static_cast<DBEvent *>(index.internalPointer());
296 302 auto it
297 303 = std::find_if(impl->m_Events.cbegin(), impl->m_Events.cend(),
298 304 [parentEvent](auto event) { return event.get() == parentEvent; });
299 305
300 306 if (it != impl->m_Events.cend()) {
301 307 return createIndex(it - impl->m_Events.cbegin(), 0);
302 308 }
303 309 else {
304 310 return QModelIndex();
305 311 }
306 312 }
307 313 case CatalogueEventsModel::ItemType::Root:
308 314 break;
309 315 case CatalogueEventsModel::ItemType::Event:
310 316 break;
311 317 default:
312 318 break;
313 319 }
314 320
315 321 return QModelIndex();
316 322 }
317 323
318 324 int CatalogueEventsModel::rowCount(const QModelIndex &parent) const
319 325 {
320 326 if (parent.column() > 0) {
321 327 return 0;
322 328 }
323 329
324 330 switch (itemTypeOf(parent)) {
325 331 case CatalogueEventsModel::ItemType::Root:
326 332 return impl->m_Events.count();
327 333 case CatalogueEventsModel::ItemType::Event: {
328 334 auto event = getEvent(parent);
329 335 return impl->m_EventProducts[event.get()].count();
330 336 }
331 337 case CatalogueEventsModel::ItemType::EventProduct:
332 338 break;
333 339 default:
334 340 break;
335 341 }
336 342
337 343 return 0;
338 344 }
339 345
340 346 int CatalogueEventsModel::columnCount(const QModelIndex &parent) const
341 347 {
342 348 return static_cast<int>(CatalogueEventsModel::Column::NbColumn);
343 349 }
344 350
345 351 Qt::ItemFlags CatalogueEventsModel::flags(const QModelIndex &index) const
346 352 {
347 353 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled;
348 354 }
349 355
350 356 QVariant CatalogueEventsModel::data(const QModelIndex &index, int role) const
351 357 {
352 358 if (index.isValid()) {
353 359
354 360 auto type = itemTypeOf(index);
355 361 if (type == CatalogueEventsModel::ItemType::Event) {
356 362 auto event = getEvent(index);
357 363 switch (role) {
358 364 case Qt::DisplayRole:
359 365 return impl->eventData(index.column(), event);
360 366 break;
361 367 }
362 368 }
363 369 else if (type == CatalogueEventsModel::ItemType::EventProduct) {
364 370 auto product = getEventProduct(index);
365 371 switch (role) {
366 372 case Qt::DisplayRole:
367 373 return impl->eventProductData(index.column(), product);
368 374 break;
369 375 }
370 376 }
371 377 }
372 378
373 379 return QVariant{};
374 380 }
375 381
376 382 QVariant CatalogueEventsModel::headerData(int section, Qt::Orientation orientation, int role) const
377 383 {
378 384 if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
379 385 return impl->columnNames().value(section);
380 386 }
381 387
382 388 return QVariant();
383 389 }
384 390
385 391 void CatalogueEventsModel::sort(int column, Qt::SortOrder order)
386 392 {
387 393 beginResetModel();
388 394 std::sort(impl->m_Events.begin(), impl->m_Events.end(),
389 395 [this, column, order](auto e1, auto e2) {
390 396 auto data1 = impl->sortData(column, e1);
391 397 auto data2 = impl->sortData(column, e2);
392 398
393 399 auto result = data1.toString() < data2.toString();
394 400
395 401 return order == Qt::AscendingOrder ? result : !result;
396 402 });
397 403
398 404 endResetModel();
399 405 emit modelSorted();
400 406 }
401 407
402 408 Qt::DropActions CatalogueEventsModel::supportedDragActions() const
403 409 {
404 410 return Qt::CopyAction | Qt::MoveAction;
405 411 }
406 412
407 413 QStringList CatalogueEventsModel::mimeTypes() const
408 414 {
409 415 return {MIME_TYPE_EVENT_LIST, MIME_TYPE_SOURCE_CATALOGUE_LIST, MIME_TYPE_TIME_RANGE};
410 416 }
411 417
412 418 QMimeData *CatalogueEventsModel::mimeData(const QModelIndexList &indexes) const
413 419 {
414 420 auto mimeData = new QMimeData;
415 421
416 422 bool isFirst = true;
417 423
418 424 QVector<std::shared_ptr<DBEvent> > eventList;
419 425 QVector<std::shared_ptr<DBEventProduct> > eventProductList;
420 426
421 427 SqpRange firstTimeRange;
422 428 for (const auto &index : indexes) {
423 429 if (index.column() == 0) { // only the first column
424 430
425 431 auto type = itemTypeOf(index);
426 432 if (type == ItemType::Event) {
427 433 auto event = getEvent(index);
428 434 eventList << event;
429 435
430 436 if (isFirst) {
431 437 isFirst = false;
432 438 firstTimeRange.m_TStart = event->getTStart();
433 439 firstTimeRange.m_TEnd = event->getTEnd();
434 440 }
435 441 }
436 442 else if (type == ItemType::EventProduct) {
437 443 auto product = getEventProduct(index);
438 444 eventProductList << product;
439 445
440 446 if (isFirst) {
441 447 isFirst = false;
442 448 firstTimeRange.m_TStart = product->getTStart();
443 449 firstTimeRange.m_TEnd = product->getTEnd();
444 450 }
445 451 }
446 452 }
447 453 }
448 454
449 455 if (!eventList.isEmpty() && eventProductList.isEmpty()) {
450 456 auto eventsEncodedData = sqpApp->catalogueController().mimeDataForEvents(eventList);
451 457 mimeData->setData(MIME_TYPE_EVENT_LIST, eventsEncodedData);
452 458
453 459 auto sourceCataloguesEncodedData
454 460 = sqpApp->catalogueController().mimeDataForCatalogues(impl->m_SourceCatalogue);
455 461 mimeData->setData(MIME_TYPE_SOURCE_CATALOGUE_LIST, sourceCataloguesEncodedData);
456 462 }
457 463
458 464 if (eventList.count() + eventProductList.count() == 1) {
459 465 // No time range MIME data if multiple events are dragged
460 466 auto timeEncodedData = TimeController::mimeDataForTimeRange(firstTimeRange);
461 467 mimeData->setData(MIME_TYPE_TIME_RANGE, timeEncodedData);
462 468 }
463 469
464 470 return mimeData;
465 471 }
466 472
467 473 CatalogueEventsModel::ItemType CatalogueEventsModel::itemTypeOf(const QModelIndex &index) const
468 474 {
469 475 if (!index.isValid()) {
470 476 return ItemType::Root;
471 477 }
472 478 else if (index.internalPointer() == nullptr) {
473 479 return ItemType::Event;
474 480 }
475 481 else {
476 482 return ItemType::EventProduct;
477 483 }
478 484 }
@@ -1,627 +1,632
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 <QKeyEvent>
23 23 #include <QListWidget>
24 24 #include <QMessageBox>
25 25
26 26 Q_LOGGING_CATEGORY(LOG_CatalogueEventsWidget, "CatalogueEventsWidget")
27 27
28 28 /// Fixed size of the validation column
29 29 const auto VALIDATION_COLUMN_SIZE = 35;
30 30
31 31 /// Percentage added to the range of a event when it is displayed
32 32 const auto EVENT_RANGE_MARGE = 30; // in %
33 33
34 34 struct CatalogueEventsWidget::CatalogueEventsWidgetPrivate {
35 35
36 36 CatalogueEventsModel *m_Model = nullptr;
37 37 QStringList m_ZonesForTimeMode;
38 38 QString m_ZoneForGraphMode;
39 39 QVector<std::shared_ptr<DBCatalogue> > m_DisplayedCatalogues;
40 40 bool m_AllEventDisplayed = false;
41 41 QVector<VisualizationGraphWidget *> m_CustomGraphs;
42 42
43 43 VisualizationWidget *m_VisualizationWidget = nullptr;
44 44
45 45 void setEvents(const QVector<std::shared_ptr<DBEvent> > &events, CatalogueEventsWidget *widget)
46 46 {
47 47 widget->ui->treeView->setSortingEnabled(false);
48 48 m_Model->setSourceCatalogues(m_DisplayedCatalogues);
49 49 m_Model->setEvents(events);
50 50 widget->ui->treeView->setSortingEnabled(true);
51 51
52 52 for (auto event : events) {
53 53 if (sqpApp->catalogueController().eventHasChanges(event)) {
54 54 auto index = m_Model->indexOf(event);
55 55 widget->setEventChanges(event, true);
56 56 }
57 57 }
58 58 }
59 59
60 60 void addEvent(const std::shared_ptr<DBEvent> &event, QTreeView *treeView)
61 61 {
62 62 treeView->setSortingEnabled(false);
63 63 m_Model->addEvent(event);
64 64 treeView->setSortingEnabled(true);
65 65 }
66 66
67 67 void removeEvent(const std::shared_ptr<DBEvent> &event, QTreeView *treeView)
68 68 {
69 69 treeView->setSortingEnabled(false);
70 70 m_Model->removeEvent(event);
71 71 treeView->setSortingEnabled(true);
72 72 }
73 73
74 74 QStringList getAvailableVisualizationZoneList() const
75 75 {
76 76 if (m_VisualizationWidget) {
77 77 if (auto tab = m_VisualizationWidget->currentTabWidget()) {
78 78 return tab->availableZoneWidgets();
79 79 }
80 80 }
81 81
82 82 return QStringList{};
83 83 }
84 84
85 85 QStringList selectZone(QWidget *parent, const QStringList &selectedZones,
86 86 bool allowMultiSelection, const QPoint &location)
87 87 {
88 88 auto availableZones = getAvailableVisualizationZoneList();
89 89 if (availableZones.isEmpty()) {
90 90 return QStringList{};
91 91 }
92 92
93 93 QDialog d(parent, Qt::Tool);
94 94 d.setWindowTitle("Choose a zone");
95 95 auto layout = new QVBoxLayout{&d};
96 96 layout->setContentsMargins(0, 0, 0, 0);
97 97 auto listWidget = new QListWidget{&d};
98 98 layout->addWidget(listWidget);
99 99
100 100 QSet<QListWidgetItem *> checkedItems;
101 101 for (auto zone : availableZones) {
102 102 auto item = new QListWidgetItem{zone};
103 103 item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsUserCheckable);
104 104 if (selectedZones.contains(zone)) {
105 105 item->setCheckState(Qt::Checked);
106 106 checkedItems << item;
107 107 }
108 108 else {
109 109 item->setCheckState(Qt::Unchecked);
110 110 }
111 111
112 112 listWidget->addItem(item);
113 113 }
114 114
115 115 auto buttonBox = new QDialogButtonBox{QDialogButtonBox::Ok, &d};
116 116 layout->addWidget(buttonBox);
117 117
118 118 QObject::connect(buttonBox, &QDialogButtonBox::accepted, &d, &QDialog::accept);
119 119 QObject::connect(buttonBox, &QDialogButtonBox::rejected, &d, &QDialog::reject);
120 120
121 121 QObject::connect(listWidget, &QListWidget::itemChanged,
122 122 [&checkedItems, allowMultiSelection, listWidget](auto item) {
123 123 if (item->checkState() == Qt::Checked) {
124 124 if (!allowMultiSelection) {
125 125 for (auto checkedItem : checkedItems) {
126 126 listWidget->blockSignals(true);
127 127 checkedItem->setCheckState(Qt::Unchecked);
128 128 listWidget->blockSignals(false);
129 129 }
130 130
131 131 checkedItems.clear();
132 132 }
133 133 checkedItems << item;
134 134 }
135 135 else {
136 136 checkedItems.remove(item);
137 137 }
138 138 });
139 139
140 140 QStringList result;
141 141
142 142 d.setMinimumWidth(120);
143 143 d.resize(d.minimumSizeHint());
144 144 d.move(location);
145 145 if (d.exec() == QDialog::Accepted) {
146 146 for (auto item : checkedItems) {
147 147 result += item->text();
148 148 }
149 149 }
150 150 else {
151 151 result = selectedZones;
152 152 }
153 153
154 154 return result;
155 155 }
156 156
157 157 void updateForTimeMode(QTreeView *treeView)
158 158 {
159 159 auto selectedRows = treeView->selectionModel()->selectedRows();
160 160
161 161 if (selectedRows.count() == 1) {
162 162 auto event = m_Model->getEvent(selectedRows.first());
163 163 if (event) {
164 164 if (m_VisualizationWidget) {
165 165 if (auto tab = m_VisualizationWidget->currentTabWidget()) {
166 166
167 167 for (auto zoneName : m_ZonesForTimeMode) {
168 168 if (auto zone = tab->getZoneWithName(zoneName)) {
169 169 SqpRange eventRange;
170 170 eventRange.m_TStart = event->getTStart();
171 171 eventRange.m_TEnd = event->getTEnd();
172 172 zone->setZoneRange(eventRange);
173 173 }
174 174 }
175 175 }
176 176 else {
177 177 qCWarning(LOG_CatalogueEventsWidget())
178 178 << "updateTimeZone: no tab found in the visualization";
179 179 }
180 180 }
181 181 else {
182 182 qCWarning(LOG_CatalogueEventsWidget())
183 183 << "updateTimeZone: visualization widget not found";
184 184 }
185 185 }
186 186 }
187 187 else {
188 188 qCWarning(LOG_CatalogueEventsWidget())
189 189 << "updateTimeZone: not compatible with multiple events selected";
190 190 }
191 191 }
192 192
193 193 QVector<SqpRange> getGraphRanges(const std::shared_ptr<DBEvent> &event)
194 194 {
195 195 // Retrieves the range of each product and the maximum size
196 196 QVector<SqpRange> graphRanges;
197 197 double maxDt = 0;
198 198 for (auto eventProduct : event->getEventProducts()) {
199 199 SqpRange eventRange;
200 200 eventRange.m_TStart = eventProduct.getTStart();
201 201 eventRange.m_TEnd = eventProduct.getTEnd();
202 202 graphRanges << eventRange;
203 203
204 204 auto dt = eventRange.m_TEnd - eventRange.m_TStart;
205 205 if (dt > maxDt) {
206 206 maxDt = dt;
207 207 }
208 208 }
209 209
210 210 // Adds the marge
211 211 maxDt *= (100.0 + EVENT_RANGE_MARGE) / 100.0;
212 212
213 213 // Corrects the graph ranges so that they all have the same size
214 214 QVector<SqpRange> correctedGraphRanges;
215 215 for (auto range : graphRanges) {
216 216 auto dt = range.m_TEnd - range.m_TStart;
217 217 auto diff = qAbs((maxDt - dt) / 2.0);
218 218
219 219 SqpRange correctedRange;
220 220 correctedRange.m_TStart = range.m_TStart - diff;
221 221 correctedRange.m_TEnd = range.m_TEnd + diff;
222 222
223 223 correctedGraphRanges << correctedRange;
224 224 }
225 225
226 226 return correctedGraphRanges;
227 227 }
228 228
229 229 void updateForGraphMode(CatalogueEventsWidget *catalogueEventWidget)
230 230 {
231 231 auto selectedRows = catalogueEventWidget->ui->treeView->selectionModel()->selectedRows();
232 232 if (selectedRows.count() != 1) {
233 233 qCWarning(LOG_CatalogueEventsWidget())
234 234 << "updateGraphMode: not compatible with multiple events selected";
235 235 return;
236 236 }
237 237
238 238 if (!m_VisualizationWidget) {
239 239 qCWarning(LOG_CatalogueEventsWidget())
240 240 << "updateGraphMode: visualization widget not found";
241 241 return;
242 242 }
243 243
244 244 auto event = m_Model->getEvent(selectedRows.first());
245 245 if (!event) {
246 246 // A event product is probably selected
247 247 qCInfo(LOG_CatalogueEventsWidget()) << "updateGraphMode: no events are selected";
248 248 return;
249 249 }
250 250
251 251 auto tab = m_VisualizationWidget->currentTabWidget();
252 252 if (!tab) {
253 253 qCWarning(LOG_CatalogueEventsWidget())
254 254 << "updateGraphMode: no tab found in the visualization";
255 255 return;
256 256 }
257 257
258 258 auto zone = tab->getZoneWithName(m_ZoneForGraphMode);
259 259 if (!zone) {
260 260 qCWarning(LOG_CatalogueEventsWidget()) << "updateGraphMode: zone not found";
261 261 return;
262 262 }
263 263
264 264 // Closes the previous graph and delete the asociated variables
265 265 for (auto graph : m_CustomGraphs) {
266 266 graph->close();
267 267 auto variables = graph->variables().toVector();
268 268
269 269 QMetaObject::invokeMethod(&sqpApp->variableController(), "deleteVariables",
270 270 Qt::QueuedConnection,
271 271 Q_ARG(QVector<std::shared_ptr<Variable> >, variables));
272 272 }
273 273 m_CustomGraphs.clear();
274 274
275 275 // Closes the remaining graphs inside the zone
276 276 zone->closeAllGraphs();
277 277
278 278 // Calculates the range of each graph which will be created
279 279 auto graphRange = getGraphRanges(event);
280 280
281 281 // Loops through the event products and create the graph
282 282 auto itRange = graphRange.cbegin();
283 283 for (auto eventProduct : event->getEventProducts()) {
284 284 auto productId = eventProduct.getProductId();
285 285
286 286 auto range = *itRange;
287 287 ++itRange;
288 288
289 289 SqpRange productRange;
290 290 productRange.m_TStart = eventProduct.getTStart();
291 291 productRange.m_TEnd = eventProduct.getTEnd();
292 292
293 293 auto context = new QObject{catalogueEventWidget};
294 294 QObject::connect(
295 295 &sqpApp->variableController(), &VariableController::variableAdded, context,
296 296 [this, catalogueEventWidget, zone, context, event, range, productRange,
297 297 productId](auto variable) {
298 298
299 299 if (variable->metadata().value(DataSourceItem::ID_DATA_KEY).toString()
300 300 == productId) {
301 301 auto graph = zone->createGraph(variable);
302 302 graph->setAutoRangeOnVariableInitialization(false);
303 303
304 304 auto selectionZone
305 305 = graph->addSelectionZone(event->getName(), productRange);
306 306 emit catalogueEventWidget->selectionZoneAdded(event, productId,
307 307 selectionZone);
308 308 m_CustomGraphs << graph;
309 309
310 310 graph->setGraphRange(range, true);
311 311
312 312 // Removes the graph from the graph list if it is closed manually
313 313 QObject::connect(graph, &VisualizationGraphWidget::destroyed,
314 314 [this, graph]() { m_CustomGraphs.removeAll(graph); });
315 315
316 316 delete context; // removes the connection
317 317 }
318 318 },
319 319 Qt::QueuedConnection);
320 320
321 321 QMetaObject::invokeMethod(&sqpApp->dataSourceController(),
322 322 "requestVariableFromProductIdKey", Qt::QueuedConnection,
323 323 Q_ARG(QString, productId));
324 324 }
325 325 }
326 326
327 327 void getSelectedItems(
328 328 QTreeView *treeView, QVector<std::shared_ptr<DBEvent> > &events,
329 329 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > &eventProducts)
330 330 {
331 331 for (auto rowIndex : treeView->selectionModel()->selectedRows()) {
332 332 auto itemType = m_Model->itemTypeOf(rowIndex);
333 333 if (itemType == CatalogueEventsModel::ItemType::Event) {
334 334 events << m_Model->getEvent(rowIndex);
335 335 }
336 336 else if (itemType == CatalogueEventsModel::ItemType::EventProduct) {
337 337 eventProducts << qMakePair(m_Model->getParentEvent(rowIndex),
338 338 m_Model->getEventProduct(rowIndex));
339 339 }
340 340 }
341 341 }
342 342 };
343 343
344 344 CatalogueEventsWidget::CatalogueEventsWidget(QWidget *parent)
345 345 : QWidget(parent),
346 346 ui(new Ui::CatalogueEventsWidget),
347 347 impl{spimpl::make_unique_impl<CatalogueEventsWidgetPrivate>()}
348 348 {
349 349 ui->setupUi(this);
350 350
351 351 impl->m_Model = new CatalogueEventsModel{this};
352 352 ui->treeView->setModel(impl->m_Model);
353 353
354 354 ui->treeView->setSortingEnabled(true);
355 355 ui->treeView->setDragDropMode(QAbstractItemView::DragDrop);
356 356 ui->treeView->setDragEnabled(true);
357 357
358
358 359 connect(ui->btnTime, &QToolButton::clicked, [this](auto checked) {
359 360 if (checked) {
360 361 ui->btnChart->setChecked(false);
361 362 impl->m_ZonesForTimeMode
362 363 = impl->selectZone(this, impl->m_ZonesForTimeMode, true,
363 364 this->mapToGlobal(ui->btnTime->frameGeometry().center()));
364 365
365 366 impl->updateForTimeMode(ui->treeView);
366 367 }
367 368 });
368 369
369 370 connect(ui->btnChart, &QToolButton::clicked, [this](auto checked) {
370 371 if (checked) {
371 372 ui->btnTime->setChecked(false);
372 373 impl->m_ZoneForGraphMode
373 374 = impl->selectZone(this, {impl->m_ZoneForGraphMode}, false,
374 375 this->mapToGlobal(ui->btnChart->frameGeometry().center()))
375 376 .value(0);
376 377
377 378 impl->updateForGraphMode(this);
378 379 }
379 380 });
380 381
381 382 connect(ui->btnRemove, &QToolButton::clicked, [this]() {
382 383 QVector<std::shared_ptr<DBEvent> > events;
383 384 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > eventProducts;
384 385 impl->getSelectedItems(ui->treeView, events, eventProducts);
385 386
386 387 if (!events.isEmpty() && eventProducts.isEmpty()) {
387 388
388 389 auto canRemoveEvent
389 390 = !this->isAllEventsDisplayed()
390 391 || (QMessageBox::warning(
391 392 this, tr("Remove Event(s)"),
392 393 tr("The selected event(s) will be permanently removed "
393 394 "from the repository!\nAre you sure you want to continue?"),
394 395 QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
395 396 == QMessageBox::Yes);
396 397
397 398 if (canRemoveEvent) {
398 399 for (auto event : events) {
399 400 if (this->isAllEventsDisplayed()) {
400 401 sqpApp->catalogueController().removeEvent(event);
401 402 impl->removeEvent(event, ui->treeView);
402 403 }
403 404 else {
404 405 QVector<std::shared_ptr<DBCatalogue> > modifiedCatalogues;
405 406 for (auto catalogue : this->displayedCatalogues()) {
406 407 if (catalogue->removeEvent(event->getUniqId())) {
407 408 sqpApp->catalogueController().updateCatalogue(catalogue);
408 409 modifiedCatalogues << catalogue;
409 410 }
410 411 }
411 412 if (!modifiedCatalogues.empty()) {
412 413 emit eventCataloguesModified(modifiedCatalogues);
413 414 }
414 415 }
415 416 impl->m_Model->removeEvent(event);
416 417 }
417 418
418 419
419 420 emit this->eventsRemoved(events);
420 421 }
421 422 }
422 423 });
423 424
424 425 connect(ui->treeView, &QTreeView::clicked, this, &CatalogueEventsWidget::emitSelection);
425 426 connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this,
426 427 &CatalogueEventsWidget::emitSelection);
427 428
428 429 ui->btnRemove->setEnabled(false); // Disabled by default when nothing is selected
429 430 connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, [this]() {
430 431 auto isNotMultiSelection = ui->treeView->selectionModel()->selectedRows().count() <= 1;
431 432 ui->btnChart->setEnabled(isNotMultiSelection);
432 433 ui->btnTime->setEnabled(isNotMultiSelection);
433 434
434 435 if (isNotMultiSelection && ui->btnTime->isChecked()) {
435 436 impl->updateForTimeMode(ui->treeView);
436 437 }
437 438 else if (isNotMultiSelection && ui->btnChart->isChecked()) {
438 439 impl->updateForGraphMode(this);
439 440 }
440 441
441 442 QVector<std::shared_ptr<DBEvent> > events;
442 443 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > eventProducts;
443 444 impl->getSelectedItems(ui->treeView, events, eventProducts);
444 445 ui->btnRemove->setEnabled(!events.isEmpty() && eventProducts.isEmpty());
445 446 });
446 447
447 448 ui->treeView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
448 449 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Tags,
449 450 QHeaderView::Stretch);
450 451 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Validation,
451 452 QHeaderView::Fixed);
452 453 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Name,
453 454 QHeaderView::Interactive);
454 455 ui->treeView->header()->resizeSection((int)CatalogueEventsModel::Column::Validation,
455 456 VALIDATION_COLUMN_SIZE);
457 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::TStart,
458 QHeaderView::ResizeToContents);
459 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::TEnd,
460 QHeaderView::ResizeToContents);
456 461 ui->treeView->header()->setSortIndicatorShown(true);
457 462
458 463 connect(impl->m_Model, &CatalogueEventsModel::modelSorted, [this]() {
459 464 auto allEvents = impl->m_Model->events();
460 465 for (auto event : allEvents) {
461 466 setEventChanges(event, sqpApp->catalogueController().eventHasChanges(event));
462 467 }
463 468 });
464 469
465 470 populateWithAllEvents();
466 471 }
467 472
468 473 CatalogueEventsWidget::~CatalogueEventsWidget()
469 474 {
470 475 delete ui;
471 476 }
472 477
473 478 void CatalogueEventsWidget::setVisualizationWidget(VisualizationWidget *visualization)
474 479 {
475 480 impl->m_VisualizationWidget = visualization;
476 481 }
477 482
478 483 void CatalogueEventsWidget::addEvent(const std::shared_ptr<DBEvent> &event)
479 484 {
480 485 impl->addEvent(event, ui->treeView);
481 486 }
482 487
483 488 void CatalogueEventsWidget::setEventChanges(const std::shared_ptr<DBEvent> &event, bool hasChanges)
484 489 {
485 490 impl->m_Model->refreshEvent(event);
486 491
487 492 auto eventIndex = impl->m_Model->indexOf(event);
488 493 auto validationIndex
489 494 = eventIndex.sibling(eventIndex.row(), (int)CatalogueEventsModel::Column::Validation);
490 495
491 496 if (validationIndex.isValid()) {
492 497 if (hasChanges) {
493 498 if (ui->treeView->indexWidget(validationIndex) == nullptr) {
494 499 auto widget = CatalogueExplorerHelper::buildValidationWidget(
495 500 ui->treeView,
496 501 [this, event]() {
497 502 sqpApp->catalogueController().saveEvent(event);
498 503 setEventChanges(event, false);
499 504 },
500 505 [this, event]() {
501 506 bool removed = false;
502 507 sqpApp->catalogueController().discardEvent(event, removed);
503 508 if (removed) {
504 509 impl->m_Model->removeEvent(event);
505 510 }
506 511 else {
507 512 setEventChanges(event, false);
508 513 impl->m_Model->refreshEvent(event, true);
509 514 }
510 515 emitSelection();
511 516 });
512 517 ui->treeView->setIndexWidget(validationIndex, widget);
513 518 }
514 519 }
515 520 else {
516 521 // Note: the widget is destroyed
517 522 ui->treeView->setIndexWidget(validationIndex, nullptr);
518 523 }
519 524 }
520 525 else {
521 526 qCWarning(LOG_CatalogueEventsWidget())
522 527 << "setEventChanges: the event is not displayed in the model.";
523 528 }
524 529 }
525 530
526 531 QVector<std::shared_ptr<DBCatalogue> > CatalogueEventsWidget::displayedCatalogues() const
527 532 {
528 533 return impl->m_DisplayedCatalogues;
529 534 }
530 535
531 536 bool CatalogueEventsWidget::isAllEventsDisplayed() const
532 537 {
533 538 return impl->m_AllEventDisplayed;
534 539 }
535 540
536 541 bool CatalogueEventsWidget::isEventDisplayed(const std::shared_ptr<DBEvent> &event) const
537 542 {
538 543 return impl->m_Model->indexOf(event).isValid();
539 544 }
540 545
541 546 void CatalogueEventsWidget::refreshEvent(const std::shared_ptr<DBEvent> &event)
542 547 {
543 548 impl->m_Model->refreshEvent(event, true);
544 549 }
545 550
546 551 void CatalogueEventsWidget::populateWithCatalogues(
547 552 const QVector<std::shared_ptr<DBCatalogue> > &catalogues)
548 553 {
549 554 impl->m_DisplayedCatalogues = catalogues;
550 555 impl->m_AllEventDisplayed = false;
551 556
552 557 QSet<QUuid> eventIds;
553 558 QVector<std::shared_ptr<DBEvent> > events;
554 559
555 560 for (auto catalogue : catalogues) {
556 561 auto catalogueEvents = sqpApp->catalogueController().retrieveEventsFromCatalogue(catalogue);
557 562 for (auto event : catalogueEvents) {
558 563 if (!eventIds.contains(event->getUniqId())) {
559 564 events << event;
560 565 eventIds.insert(event->getUniqId());
561 566 }
562 567 }
563 568 }
564 569
565 570 impl->setEvents(events, this);
566 571 }
567 572
568 573 void CatalogueEventsWidget::populateWithAllEvents()
569 574 {
570 575 impl->m_DisplayedCatalogues.clear();
571 576 impl->m_AllEventDisplayed = true;
572 577
573 578 auto allEvents = sqpApp->catalogueController().retrieveAllEvents();
574 579
575 580 QVector<std::shared_ptr<DBEvent> > events;
576 581 for (auto event : allEvents) {
577 582 events << event;
578 583 }
579 584
580 585 impl->setEvents(events, this);
581 586 }
582 587
583 588 void CatalogueEventsWidget::clear()
584 589 {
585 590 impl->m_DisplayedCatalogues.clear();
586 591 impl->m_AllEventDisplayed = false;
587 592 impl->setEvents({}, this);
588 593 }
589 594
590 595 void CatalogueEventsWidget::refresh()
591 596 {
592 597 if (isAllEventsDisplayed()) {
593 598 populateWithAllEvents();
594 599 }
595 600 else if (!impl->m_DisplayedCatalogues.isEmpty()) {
596 601 populateWithCatalogues(impl->m_DisplayedCatalogues);
597 602 }
598 603 }
599 604
600 605 void CatalogueEventsWidget::emitSelection()
601 606 {
602 607 QVector<std::shared_ptr<DBEvent> > events;
603 608 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > eventProducts;
604 609 impl->getSelectedItems(ui->treeView, events, eventProducts);
605 610
606 611 if (!events.isEmpty() && eventProducts.isEmpty()) {
607 612 emit eventsSelected(events);
608 613 }
609 614 else if (events.isEmpty() && !eventProducts.isEmpty()) {
610 615 emit eventProductsSelected(eventProducts);
611 616 }
612 617 else {
613 618 emit selectionCleared();
614 619 }
615 620 }
616 621
617 622
618 623 void CatalogueEventsWidget::keyPressEvent(QKeyEvent *event)
619 624 {
620 625 switch (event->key()) {
621 626 case Qt::Key_Delete: {
622 627 ui->btnRemove->click();
623 628 }
624 629 default:
625 630 break;
626 631 }
627 632 }
@@ -1,233 +1,236
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
32 ui->dateTimeEventTStart->setDisplayFormat(DATETIME_FORMAT);
33 ui->dateTimeEventTEnd->setDisplayFormat(DATETIME_FORMAT);
31 34 }
32 35
33 36 CatalogueInspectorWidget::~CatalogueInspectorWidget()
34 37 {
35 38 delete ui;
36 39 }
37 40
38 41 void CatalogueInspectorWidget::CatalogueInspectorWidgetPrivate::connectCatalogueUpdateSignals(
39 42 CatalogueInspectorWidget *inspector, Ui::CatalogueInspectorWidget *ui)
40 43 {
41 44 connect(ui->leCatalogueName, &QLineEdit::editingFinished, [ui, inspector, this]() {
42 45 if (ui->leCatalogueName->text() != m_DisplayedCatalogue->getName()) {
43 46 m_DisplayedCatalogue->setName(ui->leCatalogueName->text());
44 47 emit inspector->catalogueUpdated(m_DisplayedCatalogue);
45 48 }
46 49 });
47 50
48 51 connect(ui->leCatalogueAuthor, &QLineEdit::editingFinished, [ui, inspector, this]() {
49 52 if (ui->leCatalogueAuthor->text() != m_DisplayedCatalogue->getAuthor()) {
50 53 m_DisplayedCatalogue->setAuthor(ui->leCatalogueAuthor->text());
51 54 emit inspector->catalogueUpdated(m_DisplayedCatalogue);
52 55 }
53 56 });
54 57 }
55 58
56 59 void CatalogueInspectorWidget::CatalogueInspectorWidgetPrivate::connectEventUpdateSignals(
57 60 CatalogueInspectorWidget *inspector, Ui::CatalogueInspectorWidget *ui)
58 61 {
59 62 connect(ui->leEventName, &QLineEdit::editingFinished, [ui, inspector, this]() {
60 63 if (ui->leEventName->text() != m_DisplayedEvent->getName()) {
61 64 m_DisplayedEvent->setName(ui->leEventName->text());
62 65 emit inspector->eventUpdated(m_DisplayedEvent);
63 66 }
64 67 });
65 68
66 69 connect(ui->leEventTags, &QLineEdit::editingFinished, [ui, inspector, this]() {
67 70 auto tags = ui->leEventTags->text().split(QRegExp("\\s+"), QString::SkipEmptyParts);
68 71 std::list<QString> tagNames;
69 72 for (auto tag : tags) {
70 73 tagNames.push_back(tag);
71 74 }
72 75
73 76 if (m_DisplayedEvent->getTagsNames() != tagNames) {
74 77 m_DisplayedEvent->setTagsNames(tagNames);
75 78 emit inspector->eventUpdated(m_DisplayedEvent);
76 79 }
77 80 });
78 81
79 82 connect(ui->leEventProduct, &QLineEdit::editingFinished, [ui, inspector, this]() {
80 83 if (ui->leEventProduct->text() != m_DisplayedEventProduct->getProductId()) {
81 84 auto oldProductId = m_DisplayedEventProduct->getProductId();
82 85 m_DisplayedEventProduct->setProductId(ui->leEventProduct->text());
83 86
84 87 auto eventProducts = m_DisplayedEvent->getEventProducts();
85 88 for (auto &eventProduct : eventProducts) {
86 89 if (eventProduct.getProductId() == oldProductId) {
87 90 eventProduct.setProductId(m_DisplayedEventProduct->getProductId());
88 91 }
89 92 }
90 93 m_DisplayedEvent->setEventProducts(eventProducts);
91 94
92 95 emit inspector->eventUpdated(m_DisplayedEvent);
93 96 }
94 97 });
95 98
96 99 connect(ui->dateTimeEventTStart, &QDateTimeEdit::editingFinished, [ui, inspector, this]() {
97 100 auto time = DateUtils::secondsSinceEpoch(ui->dateTimeEventTStart->dateTime());
98 101 if (time != m_DisplayedEventProduct->getTStart()) {
99 102 m_DisplayedEventProduct->setTStart(time);
100 103
101 104 auto eventProducts = m_DisplayedEvent->getEventProducts();
102 105 for (auto &eventProduct : eventProducts) {
103 106 if (eventProduct.getProductId() == m_DisplayedEventProduct->getProductId()) {
104 107 eventProduct.setTStart(m_DisplayedEventProduct->getTStart());
105 108 }
106 109 }
107 110 m_DisplayedEvent->setEventProducts(eventProducts);
108 111
109 112 emit inspector->eventUpdated(m_DisplayedEvent);
110 113 }
111 114 });
112 115
113 116 connect(ui->dateTimeEventTEnd, &QDateTimeEdit::editingFinished, [ui, inspector, this]() {
114 117 auto time = DateUtils::secondsSinceEpoch(ui->dateTimeEventTEnd->dateTime());
115 118 if (time != m_DisplayedEventProduct->getTEnd()) {
116 119 m_DisplayedEventProduct->setTEnd(time);
117 120
118 121 auto eventProducts = m_DisplayedEvent->getEventProducts();
119 122 for (auto &eventProduct : eventProducts) {
120 123 if (eventProduct.getProductId() == m_DisplayedEventProduct->getProductId()) {
121 124 eventProduct.setTEnd(m_DisplayedEventProduct->getTEnd());
122 125 }
123 126 }
124 127 m_DisplayedEvent->setEventProducts(eventProducts);
125 128
126 129 emit inspector->eventUpdated(m_DisplayedEvent);
127 130 }
128 131 });
129 132 }
130 133
131 134 void CatalogueInspectorWidget::showPage(CatalogueInspectorWidget::Page page)
132 135 {
133 136 ui->stackedWidget->setCurrentIndex(static_cast<int>(page));
134 137 }
135 138
136 139 CatalogueInspectorWidget::Page CatalogueInspectorWidget::currentPage() const
137 140 {
138 141 return static_cast<Page>(ui->stackedWidget->currentIndex());
139 142 }
140 143
141 144 void CatalogueInspectorWidget::setEvent(const std::shared_ptr<DBEvent> &event)
142 145 {
143 146 impl->m_DisplayedEvent = event;
144 147
145 148 blockSignals(true);
146 149
147 150 showPage(Page::EventProperties);
148 151 ui->leEventName->setEnabled(true);
149 152 ui->leEventName->setText(event->getName());
150 153 ui->leEventProduct->setEnabled(false);
151 154
152 155 auto eventProducts = event->getEventProducts();
153 156 QStringList eventProductList;
154 157 for (auto evtProduct : eventProducts) {
155 158 eventProductList << evtProduct.getProductId();
156 159 }
157 160
158 161 ui->leEventProduct->setText(eventProductList.join(";"));
159 162
160 163 QString tagList;
161 164 auto tags = event->getTagsNames();
162 165 for (auto tag : tags) {
163 166 tagList += tag;
164 167 tagList += ' ';
165 168 }
166 169
167 170 ui->leEventTags->setEnabled(true);
168 171 ui->leEventTags->setText(tagList);
169 172
170 173 ui->dateTimeEventTStart->setEnabled(false);
171 174 ui->dateTimeEventTEnd->setEnabled(false);
172 175
173 176 ui->dateTimeEventTStart->setDateTime(DateUtils::dateTime(event->getTStart()));
174 177 ui->dateTimeEventTEnd->setDateTime(DateUtils::dateTime(event->getTEnd()));
175 178
176 179 blockSignals(false);
177 180 }
178 181
179 182 void CatalogueInspectorWidget::setEventProduct(const std::shared_ptr<DBEvent> &event,
180 183 const std::shared_ptr<DBEventProduct> &eventProduct)
181 184 {
182 185
183 186 impl->m_DisplayedEvent = event;
184 187 impl->m_DisplayedEventProduct = eventProduct;
185 188
186 189 blockSignals(true);
187 190
188 191 showPage(Page::EventProperties);
189 192 ui->leEventName->setEnabled(false);
190 193 ui->leEventName->setText(event->getName());
191 194 ui->leEventProduct->setEnabled(false);
192 195 ui->leEventProduct->setText(eventProduct->getProductId());
193 196
194 197 ui->leEventTags->setEnabled(false);
195 198 ui->leEventTags->clear();
196 199
197 200 ui->dateTimeEventTStart->setEnabled(true);
198 201 ui->dateTimeEventTEnd->setEnabled(true);
199 202
200 203 ui->dateTimeEventTStart->setDateTime(DateUtils::dateTime(eventProduct->getTStart()));
201 204 ui->dateTimeEventTEnd->setDateTime(DateUtils::dateTime(eventProduct->getTEnd()));
202 205
203 206 blockSignals(false);
204 207 }
205 208
206 209 void CatalogueInspectorWidget::setCatalogue(const std::shared_ptr<DBCatalogue> &catalogue)
207 210 {
208 211 impl->m_DisplayedCatalogue = catalogue;
209 212
210 213 blockSignals(true);
211 214
212 215 showPage(Page::CatalogueProperties);
213 216 ui->leCatalogueName->setText(catalogue->getName());
214 217 ui->leCatalogueAuthor->setText(catalogue->getAuthor());
215 218
216 219 blockSignals(false);
217 220 }
218 221
219 222 void CatalogueInspectorWidget::refresh()
220 223 {
221 224 switch (static_cast<Page>(ui->stackedWidget->currentIndex())) {
222 225 case Page::CatalogueProperties:
223 226 setCatalogue(impl->m_DisplayedCatalogue);
224 227 break;
225 228 case Page::EventProperties: {
226 229 auto isEventShowed = ui->leEventName->isEnabled();
227 230 setEvent(impl->m_DisplayedEvent);
228 231 if (!isEventShowed && impl->m_DisplayedEvent) {
229 232 setEventProduct(impl->m_DisplayedEvent, impl->m_DisplayedEventProduct);
230 233 }
231 234 }
232 235 }
233 236 }
@@ -1,207 +1,205
1 1 #include "Visualization/AxisRenderingUtils.h"
2 2
3 3 #include <Data/ScalarSeries.h>
4 4 #include <Data/SpectrogramSeries.h>
5 5 #include <Data/VectorSeries.h>
6 6
7 7 #include <Variable/Variable.h>
8 8
9 9 #include <Visualization/SqpColorScale.h>
10 10 #include <Visualization/qcustomplot.h>
11 11
12 12 Q_LOGGING_CATEGORY(LOG_AxisRenderingUtils, "AxisRenderingUtils")
13 13
14 14 namespace {
15 15
16 const auto DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd hh:mm:ss:zzz");
17
18 16 /// Format for datetimes on a axis
19 17 const auto DATETIME_TICKER_FORMAT = QStringLiteral("yyyy/MM/dd \nhh:mm:ss");
20 18
21 19 const auto NUMBER_FORMAT = 'g';
22 20 const auto NUMBER_PRECISION = 9;
23 21
24 22 /// Generates the appropriate ticker for an axis, depending on whether the axis displays time or
25 23 /// non-time data
26 24 QSharedPointer<QCPAxisTicker> axisTicker(bool isTimeAxis, QCPAxis::ScaleType scaleType)
27 25 {
28 26 if (isTimeAxis) {
29 27 auto dateTicker = QSharedPointer<QCPAxisTickerDateTime>::create();
30 28 dateTicker->setDateTimeFormat(DATETIME_TICKER_FORMAT);
31 29 dateTicker->setDateTimeSpec(Qt::UTC);
32 30
33 31 return dateTicker;
34 32 }
35 33 else if (scaleType == QCPAxis::stLogarithmic) {
36 34 return QSharedPointer<QCPAxisTickerLog>::create();
37 35 }
38 36 else {
39 37 // default ticker
40 38 return QSharedPointer<QCPAxisTicker>::create();
41 39 }
42 40 }
43 41
44 42 /**
45 43 * Sets properties of the axis passed as parameter
46 44 * @param axis the axis to set
47 45 * @param unit the unit to set for the axis
48 46 * @param scaleType the scale type to set for the axis
49 47 */
50 48 void setAxisProperties(QCPAxis &axis, const Unit &unit,
51 49 QCPAxis::ScaleType scaleType = QCPAxis::stLinear)
52 50 {
53 51 // label (unit name)
54 52 axis.setLabel(unit.m_Name);
55 53
56 54 // scale type
57 55 axis.setScaleType(scaleType);
58 56 if (scaleType == QCPAxis::stLogarithmic) {
59 57 // Scientific notation
60 58 axis.setNumberPrecision(0);
61 59 axis.setNumberFormat("eb");
62 60 }
63 61
64 62 // ticker (depending on the type of unit)
65 63 axis.setTicker(axisTicker(unit.m_TimeUnit, scaleType));
66 64 }
67 65
68 66 /**
69 67 * Delegate used to set axes properties
70 68 */
71 69 template <typename T, typename Enabled = void>
72 70 struct AxisSetter {
73 71 static void setProperties(QCustomPlot &, SqpColorScale &)
74 72 {
75 73 // Default implementation does nothing
76 74 qCCritical(LOG_AxisRenderingUtils()) << "Can't set axis properties: unmanaged type of data";
77 75 }
78 76
79 77 static void setUnits(T &, QCustomPlot &, SqpColorScale &)
80 78 {
81 79 // Default implementation does nothing
82 80 qCCritical(LOG_AxisRenderingUtils()) << "Can't set axis units: unmanaged type of data";
83 81 }
84 82 };
85 83
86 84 /**
87 85 * Specialization of AxisSetter for scalars and vectors
88 86 * @sa ScalarSeries
89 87 * @sa VectorSeries
90 88 */
91 89 template <typename T>
92 90 struct AxisSetter<T, typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value
93 91 or std::is_base_of<VectorSeries, T>::value> > {
94 92 static void setProperties(QCustomPlot &, SqpColorScale &)
95 93 {
96 94 // Nothing to do
97 95 }
98 96
99 97 static void setUnits(T &dataSeries, QCustomPlot &plot, SqpColorScale &)
100 98 {
101 99 dataSeries.lockRead();
102 100 auto xAxisUnit = dataSeries.xAxisUnit();
103 101 auto valuesUnit = dataSeries.valuesUnit();
104 102 dataSeries.unlock();
105 103
106 104 setAxisProperties(*plot.xAxis, xAxisUnit);
107 105 setAxisProperties(*plot.yAxis, valuesUnit);
108 106 }
109 107 };
110 108
111 109 /**
112 110 * Specialization of AxisSetter for spectrograms
113 111 * @sa SpectrogramSeries
114 112 */
115 113 template <typename T>
116 114 struct AxisSetter<T, typename std::enable_if_t<std::is_base_of<SpectrogramSeries, T>::value> > {
117 115 static void setProperties(QCustomPlot &plot, SqpColorScale &colorScale)
118 116 {
119 117 // Displays color scale in plot
120 118 plot.plotLayout()->insertRow(0);
121 119 plot.plotLayout()->addElement(0, 0, colorScale.m_Scale);
122 120 colorScale.m_Scale->setType(QCPAxis::atTop);
123 121 colorScale.m_Scale->setMinimumMargins(QMargins{0, 0, 0, 0});
124 122
125 123 // Aligns color scale with axes
126 124 auto marginGroups = plot.axisRect()->marginGroups();
127 125 for (auto it = marginGroups.begin(), end = marginGroups.end(); it != end; ++it) {
128 126 colorScale.m_Scale->setMarginGroup(it.key(), it.value());
129 127 }
130 128
131 129 // Set color scale properties
132 130 colorScale.m_AutomaticThreshold = true;
133 131 }
134 132
135 133 static void setUnits(T &dataSeries, QCustomPlot &plot, SqpColorScale &colorScale)
136 134 {
137 135 dataSeries.lockRead();
138 136 auto xAxisUnit = dataSeries.xAxisUnit();
139 137 auto yAxisUnit = dataSeries.yAxisUnit();
140 138 auto valuesUnit = dataSeries.valuesUnit();
141 139 dataSeries.unlock();
142 140
143 141 setAxisProperties(*plot.xAxis, xAxisUnit);
144 142 setAxisProperties(*plot.yAxis, yAxisUnit, QCPAxis::stLogarithmic);
145 143 setAxisProperties(*colorScale.m_Scale->axis(), valuesUnit, QCPAxis::stLogarithmic);
146 144 }
147 145 };
148 146
149 147 /**
150 148 * Default implementation of IAxisHelper, which takes data series to set axes properties
151 149 * @tparam T the data series' type
152 150 */
153 151 template <typename T>
154 152 struct AxisHelper : public IAxisHelper {
155 153 explicit AxisHelper(std::shared_ptr<T> dataSeries) : m_DataSeries{dataSeries} {}
156 154
157 155 void setProperties(QCustomPlot &plot, SqpColorScale &colorScale) override
158 156 {
159 157 AxisSetter<T>::setProperties(plot, colorScale);
160 158 }
161 159
162 160 void setUnits(QCustomPlot &plot, SqpColorScale &colorScale) override
163 161 {
164 162 if (m_DataSeries) {
165 163 AxisSetter<T>::setUnits(*m_DataSeries, plot, colorScale);
166 164 }
167 165 else {
168 166 qCCritical(LOG_AxisRenderingUtils()) << "Can't set units: inconsistency between the "
169 167 "type of data series and the type supposed";
170 168 }
171 169 }
172 170
173 171 std::shared_ptr<T> m_DataSeries;
174 172 };
175 173
176 174 } // namespace
177 175
178 176 QString formatValue(double value, const QCPAxis &axis)
179 177 {
180 178 // If the axis is a time axis, formats the value as a date
181 179 if (auto axisTicker = qSharedPointerDynamicCast<QCPAxisTickerDateTime>(axis.ticker())) {
182 180 return DateUtils::dateTime(value, axisTicker->dateTimeSpec()).toString(DATETIME_FORMAT);
183 181 }
184 182 else {
185 183 return QString::number(value, NUMBER_FORMAT, NUMBER_PRECISION);
186 184 }
187 185 }
188 186
189 187 std::unique_ptr<IAxisHelper> IAxisHelperFactory::create(const Variable &variable) noexcept
190 188 {
191 189 switch (variable.type()) {
192 190 case DataSeriesType::SCALAR:
193 191 return std::make_unique<AxisHelper<ScalarSeries> >(
194 192 std::dynamic_pointer_cast<ScalarSeries>(variable.dataSeries()));
195 193 case DataSeriesType::SPECTROGRAM:
196 194 return std::make_unique<AxisHelper<SpectrogramSeries> >(
197 195 std::dynamic_pointer_cast<SpectrogramSeries>(variable.dataSeries()));
198 196 case DataSeriesType::VECTOR:
199 197 return std::make_unique<AxisHelper<VectorSeries> >(
200 198 std::dynamic_pointer_cast<VectorSeries>(variable.dataSeries()));
201 199 default:
202 200 // Creates default helper
203 201 break;
204 202 }
205 203
206 204 return std::make_unique<AxisHelper<IDataSeries> >(nullptr);
207 205 }
@@ -1,69 +1,69
1 1 #include "Visualization/VisualizationMultiZoneSelectionDialog.h"
2 2 #include "ui_VisualizationMultiZoneSelectionDialog.h"
3 3
4 4 #include "Common/DateUtils.h"
5 5 #include "Visualization/VisualizationSelectionZoneItem.h"
6 6
7 const auto DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd hh:mm:ss");
7 const auto DATETIME_FORMAT_S = QStringLiteral("yyyy/MM/dd hh:mm:ss");
8 8
9 9 struct VisualizationMultiZoneSelectionDialog::VisualizationMultiZoneSelectionDialogPrivate {
10 10 QVector<VisualizationSelectionZoneItem *> m_Zones;
11 11 };
12 12
13 13 VisualizationMultiZoneSelectionDialog::VisualizationMultiZoneSelectionDialog(QWidget *parent)
14 14 : QDialog(parent, Qt::Tool),
15 15 ui(new Ui::VisualizationMultiZoneSelectionDialog),
16 16 impl{spimpl::make_unique_impl<VisualizationMultiZoneSelectionDialogPrivate>()}
17 17 {
18 18 ui->setupUi(this);
19 19
20 20 connect(ui->buttonBox, &QDialogButtonBox::accepted, this,
21 21 &VisualizationMultiZoneSelectionDialog::accept);
22 22 connect(ui->buttonBox, &QDialogButtonBox::rejected, this,
23 23 &VisualizationMultiZoneSelectionDialog::reject);
24 24 }
25 25
26 26 VisualizationMultiZoneSelectionDialog::~VisualizationMultiZoneSelectionDialog()
27 27 {
28 28 delete ui;
29 29 }
30 30
31 31 void VisualizationMultiZoneSelectionDialog::setZones(
32 32 const QVector<VisualizationSelectionZoneItem *> &zones)
33 33 {
34 34 impl->m_Zones = zones;
35 35
36 36 // Sorts the zones to display them in temporal order
37 37 std::sort(impl->m_Zones.begin(), impl->m_Zones.end(), [](auto zone1, auto zone2) {
38 38 return zone1->range().m_TStart < zone2->range().m_TStart;
39 39 });
40 40
41 41 // Adds the zones in the listwidget
42 42 for (auto zone : impl->m_Zones) {
43 43 auto name = zone->name();
44 44 if (!name.isEmpty()) {
45 45 name += tr(": ");
46 46 }
47 47
48 48 auto range = zone->range();
49 name += DateUtils::dateTime(range.m_TStart).toString(DATETIME_FORMAT);
49 name += DateUtils::dateTime(range.m_TStart).toString(DATETIME_FORMAT_S);
50 50 name += " - ";
51 name += DateUtils::dateTime(range.m_TEnd).toString(DATETIME_FORMAT);
51 name += DateUtils::dateTime(range.m_TEnd).toString(DATETIME_FORMAT_S);
52 52
53 53 auto item = new QListWidgetItem(name, ui->listWidget);
54 54 item->setSelected(zone->selected());
55 55 }
56 56 }
57 57
58 58 QMap<VisualizationSelectionZoneItem *, bool>
59 59 VisualizationMultiZoneSelectionDialog::selectedZones() const
60 60 {
61 61 QMap<VisualizationSelectionZoneItem *, bool> selectedZones;
62 62
63 63 for (auto i = 0; i < ui->listWidget->count(); ++i) {
64 64 auto item = ui->listWidget->item(i);
65 65 selectedZones[impl->m_Zones[i]] = item->isSelected();
66 66 }
67 67
68 68 return selectedZones;
69 69 }
@@ -1,145 +1,144
1 1 <?xml version="1.0" encoding="UTF-8"?>
2 2 <ui version="4.0">
3 3 <class>CatalogueEventsWidget</class>
4 4 <widget class="QWidget" name="CatalogueEventsWidget">
5 5 <property name="geometry">
6 6 <rect>
7 7 <x>0</x>
8 8 <y>0</y>
9 9 <width>566</width>
10 10 <height>258</height>
11 11 </rect>
12 12 </property>
13 13 <property name="windowTitle">
14 14 <string>Form</string>
15 15 </property>
16 16 <layout class="QVBoxLayout" name="verticalLayout">
17 17 <property name="leftMargin">
18 18 <number>0</number>
19 19 </property>
20 20 <property name="topMargin">
21 21 <number>0</number>
22 22 </property>
23 23 <property name="rightMargin">
24 24 <number>0</number>
25 25 </property>
26 26 <property name="bottomMargin">
27 27 <number>0</number>
28 28 </property>
29 29 <item>
30 30 <layout class="QHBoxLayout" name="horizontalLayout">
31 31 <item>
32 32 <widget class="QToolButton" name="btnAdd">
33 33 <property name="enabled">
34 34 <bool>false</bool>
35 35 </property>
36 36 <property name="text">
37 37 <string>+</string>
38 38 </property>
39 39 <property name="icon">
40 40 <iconset resource="../../resources/sqpguiresources.qrc">
41 41 <normaloff>:/icones/add.png</normaloff>:/icones/add.png</iconset>
42 42 </property>
43 43 <property name="autoRaise">
44 44 <bool>true</bool>
45 45 </property>
46 46 </widget>
47 47 </item>
48 48 <item>
49 49 <widget class="QToolButton" name="btnRemove">
50 50 <property name="text">
51 51 <string> - </string>
52 52 </property>
53 53 <property name="icon">
54 54 <iconset resource="../../resources/sqpguiresources.qrc">
55 55 <normaloff>:/icones/remove.png</normaloff>:/icones/remove.png</iconset>
56 56 </property>
57 57 <property name="autoRaise">
58 58 <bool>true</bool>
59 59 </property>
60 60 </widget>
61 61 </item>
62 62 <item>
63 63 <widget class="Line" name="line">
64 64 <property name="orientation">
65 65 <enum>Qt::Vertical</enum>
66 66 </property>
67 67 </widget>
68 68 </item>
69 69 <item>
70 70 <widget class="QToolButton" name="btnTime">
71 71 <property name="text">
72 72 <string>T</string>
73 73 </property>
74 74 <property name="icon">
75 75 <iconset resource="../../resources/sqpguiresources.qrc">
76 76 <normaloff>:/icones/time.png</normaloff>:/icones/time.png</iconset>
77 77 </property>
78 78 <property name="checkable">
79 79 <bool>true</bool>
80 80 </property>
81 81 <property name="autoRaise">
82 82 <bool>true</bool>
83 83 </property>
84 84 </widget>
85 85 </item>
86 86 <item>
87 87 <widget class="QToolButton" name="btnChart">
88 88 <property name="text">
89 89 <string>G</string>
90 90 </property>
91 91 <property name="icon">
92 92 <iconset resource="../../resources/sqpguiresources.qrc">
93 93 <normaloff>:/icones/chart.png</normaloff>:/icones/chart.png</iconset>
94 94 </property>
95 95 <property name="checkable">
96 96 <bool>true</bool>
97 97 </property>
98 98 <property name="autoRaise">
99 99 <bool>true</bool>
100 100 </property>
101 101 </widget>
102 102 </item>
103 103 <item>
104 104 <widget class="Line" name="line_2">
105 105 <property name="orientation">
106 106 <enum>Qt::Vertical</enum>
107 107 </property>
108 108 </widget>
109 109 </item>
110 110 <item>
111 111 <widget class="QLineEdit" name="lineEdit">
112 112 <property name="enabled">
113 113 <bool>false</bool>
114 114 </property>
115 115 </widget>
116 116 </item>
117 117 </layout>
118 118 </item>
119 119 <item>
120 120 <widget class="QTreeView" name="treeView">
121 121 <property name="dragEnabled">
122 122 <bool>true</bool>
123 123 </property>
124 124 <property name="dragDropMode">
125 125 <enum>QAbstractItemView::DragDrop</enum>
126 126 </property>
127 127 <property name="selectionMode">
128 128 <enum>QAbstractItemView::ExtendedSelection</enum>
129 129 </property>
130 130 <property name="selectionBehavior">
131 131 <enum>QAbstractItemView::SelectRows</enum>
132 132 </property>
133 133 <attribute name="headerStretchLastSection">
134 134 <bool>false</bool>
135 135 </attribute>
136 136 </widget>
137 137 </item>
138 138 </layout>
139 139 </widget>
140 140 <resources>
141 141 <include location="../../resources/sqpguiresources.qrc"/>
142 <include location="../../resources/sqpguiresources.qrc"/>
143 142 </resources>
144 143 <connections/>
145 144 </ui>
General Comments 0
You need to be logged in to leave comments. Login now