##// END OF EJS Templates
Drop of variable, graph and zones on the time widget
trabillard -
r878:e439cb403ff9
parent child
Show More
@@ -1,99 +1,102
1 1 #ifndef SCIQLOP_DATASOURCECONTROLLER_H
2 2 #define SCIQLOP_DATASOURCECONTROLLER_H
3 3
4 4 #include "CoreGlobal.h"
5 5
6 6 #include <QLoggingCategory>
7 7 #include <QObject>
8 8 #include <QUuid>
9 9
10 10 #include <Common/spimpl.h>
11 11
12 12 Q_DECLARE_LOGGING_CATEGORY(LOG_DataSourceController)
13 13
14 14 class DataSourceItem;
15 15 class IDataProvider;
16 16
17 17 /**
18 18 * @brief The DataSourceController class aims to make the link between SciQlop and its plugins. This
19 19 * is the intermediate class that SciQlop has to use in the way to connect a data source. Please
20 20 * first use register method to initialize a plugin specified by its metadata name (JSON plugin
21 21 * source) then others specifics method will be able to access it. You can load a data source driver
22 22 * plugin then create a data source.
23 23 */
24 24 class SCIQLOP_CORE_EXPORT DataSourceController : public QObject {
25 25 Q_OBJECT
26 26 public:
27 27 explicit DataSourceController(QObject *parent = 0);
28 28 virtual ~DataSourceController();
29 29
30 30 /**
31 31 * Registers a data source. The method delivers a unique id that can be used afterwards to
32 32 * access to the data source properties (structure, connection parameters, data provider, etc.)
33 33 * @param dataSourceName the name of the data source
34 34 * @return the unique id with which the data source has been registered
35 35 */
36 36 QUuid registerDataSource(const QString &dataSourceName) noexcept;
37 37
38 38 /**
39 39 * Sets the structure of a data source. The controller takes ownership of the structure.
40 40 * @param dataSourceUid the unique id with which the data source has been registered into the
41 41 * controller. If it is invalid, the method has no effect.
42 42 * @param dataSourceItem the structure of the data source. It must be not null to be registered
43 43 * @sa registerDataSource()
44 44 */
45 45 void setDataSourceItem(const QUuid &dataSourceUid,
46 46 std::unique_ptr<DataSourceItem> dataSourceItem) noexcept;
47 47
48 48 /**
49 49 * Sets the data provider used to retrieve data from of a data source. The controller takes
50 50 * ownership of the provider.
51 51 * @param dataSourceUid the unique id with which the data source has been registered into the
52 52 * controller. If it is invalid, the method has no effect.
53 53 * @param dataProvider the provider of the data source
54 54 * @sa registerDataSource()
55 55 */
56 56 void setDataProvider(const QUuid &dataSourceUid,
57 57 std::unique_ptr<IDataProvider> dataProvider) noexcept;
58 58
59 59 /**
60 60 * Loads an item (product) as a variable in SciQlop
61 61 * @param dataSourceUid the unique id of the data source containing the item. It is used to get
62 62 * the data provider associated to the data source, and pass it to for the variable creation
63 63 * @param productItem the item to load
64 64 */
65 65 void loadProductItem(const QUuid &dataSourceUid, const DataSourceItem &productItem) noexcept;
66 66
67 QByteArray mimeDataForProductsData(const QVariantList &productsData) const;
68 QVariantList productsDataForMimeData(const QByteArray &mimeData) const;
67 /// Returns the MIME data associated to a list of product meta data
68 static QByteArray mimeDataForProductsData(const QVariantList &productsData);
69
70 /// Returns the list of meta data contained in a MIME data
71 static QVariantList productsDataForMimeData(const QByteArray &mimeData);
69 72
70 73 public slots:
71 74 /// Manage init/end of the controller
72 75 void initialize();
73 76 void finalize();
74 77
75 78 void requestVariable(const QVariantHash &productData);
76 79
77 80 signals:
78 81 /// Signal emitted when a structure has been set for a data source
79 82 void dataSourceItemSet(DataSourceItem *dataSourceItem);
80 83
81 84 /**
82 85 * Signal emitted when a variable creation is asked for a product
83 86 * @param variableName the name of the variable
84 87 * @param variableMetadata the metadata of the variable
85 88 * @param variableProvider the provider that will be used to retrieve the data of the variable
86 89 * (can be null)
87 90 */
88 91 void variableCreationRequested(const QString &variableName,
89 92 const QVariantHash &variableMetadata,
90 93 std::shared_ptr<IDataProvider> variableProvider);
91 94
92 95 private:
93 96 void waitForFinish();
94 97
95 98 class DataSourceControllerPrivate;
96 99 spimpl::unique_impl_ptr<DataSourceControllerPrivate> impl;
97 100 };
98 101
99 102 #endif // SCIQLOP_DATASOURCECONTROLLER_H
@@ -1,42 +1,48
1 1 #ifndef SCIQLOP_TIMECONTROLLER_H
2 2 #define SCIQLOP_TIMECONTROLLER_H
3 3
4 4 #include "CoreGlobal.h"
5 5
6 6 #include <Data/SqpRange.h>
7 7
8 8 #include <QLoggingCategory>
9 9 #include <QObject>
10 10
11 11 #include <Common/spimpl.h>
12 12
13 13
14 14 Q_DECLARE_LOGGING_CATEGORY(LOG_TimeController)
15 15
16 16 /**
17 17 * @brief The TimeController class aims to handle the Time parameters notification in SciQlop.
18 18 */
19 19 class SCIQLOP_CORE_EXPORT TimeController : public QObject {
20 20 Q_OBJECT
21 21 public:
22 22 explicit TimeController(QObject *parent = 0);
23 23
24 24 SqpRange dateTime() const noexcept;
25 25
26 /// Returns the MIME data associated to a time range
27 static QByteArray mimeDataForTimeRange(const SqpRange &timeRange);
28
29 /// Returns the time range contained in a MIME data
30 static SqpRange timeRangeForMimeData(const QByteArray &mimeData);
31
26 32 signals:
27 33 /// Signal emitted to notify that time parameters has beed updated
28 34 void timeUpdated(SqpRange time);
29 35
30 36 public slots:
31 37 /// Slot called when a new dateTime has been defined.
32 38 void onTimeToUpdate(SqpRange dateTime);
33 39
34 40 /// Slot called when the dateTime has to be notified. Call timeUpdated signal
35 41 void onTimeNotify();
36 42
37 43 private:
38 44 class TimeControllerPrivate;
39 45 spimpl::unique_impl_ptr<TimeControllerPrivate> impl;
40 46 };
41 47
42 48 #endif // SCIQLOP_TIMECONTROLLER_H
@@ -1,132 +1,135
1 1 #ifndef SCIQLOP_VARIABLECONTROLLER_H
2 2 #define SCIQLOP_VARIABLECONTROLLER_H
3 3
4 4 #include "CoreGlobal.h"
5 5
6 6 #include <Data/AcquisitionDataPacket.h>
7 7 #include <Data/SqpRange.h>
8 8
9 9 #include <QLoggingCategory>
10 10 #include <QObject>
11 11 #include <QUuid>
12 12
13 13 #include <Common/spimpl.h>
14 14
15 15 class IDataProvider;
16 16 class QItemSelectionModel;
17 17 class TimeController;
18 18 class Variable;
19 19 class VariableModel;
20 20
21 21 Q_DECLARE_LOGGING_CATEGORY(LOG_VariableController)
22 22
23 23
24 24 /**
25 25 * Possible types of zoom operation
26 26 */
27 27 enum class AcquisitionZoomType { ZoomOut, ZoomIn, PanRight, PanLeft, Unknown };
28 28
29 29
30 30 /**
31 31 * @brief The VariableController class aims to handle the variables in SciQlop.
32 32 */
33 33 class SCIQLOP_CORE_EXPORT VariableController : public QObject {
34 34 Q_OBJECT
35 35 public:
36 36 explicit VariableController(QObject *parent = 0);
37 37 virtual ~VariableController();
38 38
39 39 VariableModel *variableModel() noexcept;
40 40 QItemSelectionModel *variableSelectionModel() noexcept;
41 41
42 42 void setTimeController(TimeController *timeController) noexcept;
43 43
44 44 /**
45 45 * Clones the variable passed in parameter and adds the duplicate to the controller
46 46 * @param variable the variable to duplicate
47 47 * @return the duplicate created, nullptr if the variable couldn't be created
48 48 */
49 49 std::shared_ptr<Variable> cloneVariable(std::shared_ptr<Variable> variable) noexcept;
50 50
51 51 /**
52 52 * Deletes from the controller the variable passed in parameter.
53 53 *
54 54 * Delete a variable includes:
55 55 * - the deletion of the various references to the variable in SciQlop
56 56 * - the deletion of the model variable
57 57 * - the deletion of the provider associated with the variable
58 58 * - removing the cache associated with the variable
59 59 *
60 60 * @param variable the variable to delete from the controller.
61 61 */
62 62 void deleteVariable(std::shared_ptr<Variable> variable) noexcept;
63 63
64 64 /**
65 65 * Deletes from the controller the variables passed in parameter.
66 66 * @param variables the variables to delete from the controller.
67 67 * @sa deleteVariable()
68 68 */
69 69 void deleteVariables(const QVector<std::shared_ptr<Variable> > &variables) noexcept;
70 70
71 /// Returns the MIME data associated to a list of variables
71 72 QByteArray mimeDataForVariables(const QList<std::shared_ptr<Variable> > &variables) const;
73
74 /// Returns the list of variables contained in a MIME data
72 75 QList<std::shared_ptr<Variable> > variablesForMimeData(const QByteArray &mimeData) const;
73 76
74 77 static AcquisitionZoomType getZoomType(const SqpRange &range, const SqpRange &oldRange);
75 78 signals:
76 79 /// Signal emitted when a variable is about to be deleted from the controller
77 80 void variableAboutToBeDeleted(std::shared_ptr<Variable> variable);
78 81
79 82 /// Signal emitted when a data acquisition is requested on a range for a variable
80 83 void rangeChanged(std::shared_ptr<Variable> variable, const SqpRange &range);
81 84
82 85 /// Signal emitted when a sub range of the cacheRange of the variable can be displayed
83 86 void updateVarDisplaying(std::shared_ptr<Variable> variable, const SqpRange &range);
84 87
85 88 public slots:
86 89 /// Request the data loading of the variable whithin range
87 90 void onRequestDataLoading(QVector<std::shared_ptr<Variable> > variables, const SqpRange &range,
88 91 bool synchronise);
89 92 /**
90 93 * Creates a new variable and adds it to the model
91 94 * @param name the name of the new variable
92 95 * @param metadata the metadata of the new variable
93 96 * @param provider the data provider for the new variable
94 97 * @return the pointer to the new variable or nullptr if the creation failed
95 98 */
96 99 std::shared_ptr<Variable> createVariable(const QString &name, const QVariantHash &metadata,
97 100 std::shared_ptr<IDataProvider> provider) noexcept;
98 101
99 102 /// Update the temporal parameters of every selected variable to dateTime
100 103 void onDateTimeOnSelection(const SqpRange &dateTime);
101 104
102 105
103 106 void onDataProvided(QUuid vIdentifier, const SqpRange &rangeRequested,
104 107 const SqpRange &cacheRangeRequested,
105 108 QVector<AcquisitionDataPacket> dataAcquired);
106 109
107 110 void onVariableRetrieveDataInProgress(QUuid identifier, double progress);
108 111
109 112 /// Cancel the current request for the variable
110 113 void onAbortProgressRequested(std::shared_ptr<Variable> variable);
111 114 void onAbortAcquisitionRequested(QUuid vIdentifier);
112 115
113 116 // synchronization group methods
114 117 void onAddSynchronizationGroupId(QUuid synchronizationGroupId);
115 118 void onRemoveSynchronizationGroupId(QUuid synchronizationGroupId);
116 119 void onAddSynchronized(std::shared_ptr<Variable> variable, QUuid synchronizationGroupId);
117 120
118 121 /// Desynchronizes the variable of the group whose identifier is passed in parameter
119 122 /// @remarks the method does nothing if the variable is not part of the group
120 123 void desynchronize(std::shared_ptr<Variable> variable, QUuid synchronizationGroupId);
121 124
122 125 void initialize();
123 126 void finalize();
124 127
125 128 private:
126 129 void waitForFinish();
127 130
128 131 class VariableControllerPrivate;
129 132 spimpl::unique_impl_ptr<VariableControllerPrivate> impl;
130 133 };
131 134
132 135 #endif // SCIQLOP_VARIABLECONTROLLER_H
@@ -1,192 +1,192
1 1 #include "DataSource/DataSourceController.h"
2 2 #include "DataSource/DataSourceItem.h"
3 3
4 4 #include <Data/IDataProvider.h>
5 5
6 6 #include <QMutex>
7 7 #include <QThread>
8 8
9 9 #include <QDataStream>
10 10 #include <QDir>
11 11 #include <QStandardPaths>
12 12
13 13 Q_LOGGING_CATEGORY(LOG_DataSourceController, "DataSourceController")
14 14
15 15 namespace {
16 16
17 17 /**
18 18 * Builds the metadata of the variable that will be generated from the loading of an item
19 19 * @param dataSourceItem the data source item from which to generate the metadata
20 20 * @return the metadata of the variable
21 21 */
22 22 QVariantHash variableMetadata(const DataSourceItem &dataSourceItem)
23 23 {
24 24 // Variable metadata contains...
25 25
26 26 // ... all metadata of the item
27 27 auto result = dataSourceItem.data();
28 28
29 29 // ... and the name of the plugin, recovered from root item
30 30 result.insert(QStringLiteral("plugin"), dataSourceItem.rootItem().name());
31 31
32 32 return result;
33 33 }
34 34
35 35 } // namespace
36 36
37 37 class DataSourceController::DataSourceControllerPrivate {
38 38 public:
39 39 QMutex m_WorkingMutex;
40 40 /// Data sources registered
41 41 QHash<QUuid, QString> m_DataSources;
42 42 /// Data sources structures
43 43 std::map<QUuid, std::unique_ptr<DataSourceItem> > m_DataSourceItems;
44 44 /// Data providers registered
45 45 /// @remarks Data providers are stored as shared_ptr as they can be sent to a variable and
46 46 /// continue to live without necessarily the data source controller
47 47 std::map<QUuid, std::shared_ptr<IDataProvider> > m_DataProviders;
48 48
49 49 // Search for the first datasource item matching the specified data
50 50 DataSourceItem *findDataSourceItem(const QVariantHash &data)
51 51 {
52 52 DataSourceItem *sourceItem = nullptr;
53 53 for (const auto &item : m_DataSourceItems) {
54 54 sourceItem = item.second->findItem(data, true);
55 55 if (sourceItem) {
56 56 break;
57 57 }
58 58 }
59 59
60 60 return sourceItem;
61 61 }
62 62 };
63 63
64 64 DataSourceController::DataSourceController(QObject *parent)
65 65 : impl{spimpl::make_unique_impl<DataSourceControllerPrivate>()}
66 66 {
67 67 qCDebug(LOG_DataSourceController()) << tr("DataSourceController construction")
68 68 << QThread::currentThread();
69 69 }
70 70
71 71 DataSourceController::~DataSourceController()
72 72 {
73 73 qCDebug(LOG_DataSourceController()) << tr("DataSourceController destruction")
74 74 << QThread::currentThread();
75 75 this->waitForFinish();
76 76 }
77 77
78 78 QUuid DataSourceController::registerDataSource(const QString &dataSourceName) noexcept
79 79 {
80 80 auto dataSourceUid = QUuid::createUuid();
81 81 impl->m_DataSources.insert(dataSourceUid, dataSourceName);
82 82
83 83 return dataSourceUid;
84 84 }
85 85
86 86 void DataSourceController::setDataSourceItem(
87 87 const QUuid &dataSourceUid, std::unique_ptr<DataSourceItem> dataSourceItem) noexcept
88 88 {
89 89 if (!dataSourceItem) {
90 90 qCWarning(LOG_DataSourceController())
91 91 << tr("Data source item can't be registered (null item)");
92 92 return;
93 93 }
94 94
95 95 if (impl->m_DataSources.contains(dataSourceUid)) {
96 96 // The data provider is implicitly converted to a shared_ptr
97 97 impl->m_DataSourceItems.insert(std::make_pair(dataSourceUid, std::move(dataSourceItem)));
98 98
99 99 // Retrieves the data source item to emit the signal with it
100 100 auto it = impl->m_DataSourceItems.find(dataSourceUid);
101 101 if (it != impl->m_DataSourceItems.end()) {
102 102 emit dataSourceItemSet(it->second.get());
103 103 }
104 104 }
105 105 else {
106 106 qCWarning(LOG_DataSourceController()) << tr("Can't set data source item for uid %1 : no "
107 107 "data source has been registered with the uid")
108 108 .arg(dataSourceUid.toString());
109 109 }
110 110 }
111 111
112 112 void DataSourceController::setDataProvider(const QUuid &dataSourceUid,
113 113 std::unique_ptr<IDataProvider> dataProvider) noexcept
114 114 {
115 115 if (impl->m_DataSources.contains(dataSourceUid)) {
116 116 impl->m_DataProviders.insert(std::make_pair(dataSourceUid, std::move(dataProvider)));
117 117 }
118 118 else {
119 119 qCWarning(LOG_DataSourceController()) << tr("Can't set data provider for uid %1 : no data "
120 120 "source has been registered with the uid")
121 121 .arg(dataSourceUid.toString());
122 122 }
123 123 }
124 124
125 125 void DataSourceController::loadProductItem(const QUuid &dataSourceUid,
126 126 const DataSourceItem &productItem) noexcept
127 127 {
128 128 if (productItem.type() == DataSourceItemType::PRODUCT
129 129 || productItem.type() == DataSourceItemType::COMPONENT) {
130 130 /// Retrieves the data provider of the data source (if any)
131 131 auto it = impl->m_DataProviders.find(dataSourceUid);
132 132 auto dataProvider = (it != impl->m_DataProviders.end()) ? it->second : nullptr;
133 133
134 134 emit variableCreationRequested(productItem.name(), variableMetadata(productItem),
135 135 dataProvider);
136 136 }
137 137 else {
138 138 qCWarning(LOG_DataSourceController()) << tr("Can't load an item that is not a product");
139 139 }
140 140 }
141 141
142 QByteArray DataSourceController::mimeDataForProductsData(const QVariantList &productsData) const
142 QByteArray DataSourceController::mimeDataForProductsData(const QVariantList &productsData)
143 143 {
144 144 QByteArray encodedData;
145 145 QDataStream stream{&encodedData, QIODevice::WriteOnly};
146 146
147 147 stream << productsData;
148 148
149 149 return encodedData;
150 150 }
151 151
152 QVariantList DataSourceController::productsDataForMimeData(const QByteArray &mimeData) const
152 QVariantList DataSourceController::productsDataForMimeData(const QByteArray &mimeData)
153 153 {
154 154 QDataStream stream{mimeData};
155 155
156 156 QVariantList productList;
157 157 stream >> productList;
158 158
159 159 return productList;
160 160 }
161 161
162 162 void DataSourceController::initialize()
163 163 {
164 164 qCDebug(LOG_DataSourceController()) << tr("DataSourceController init")
165 165 << QThread::currentThread();
166 166 impl->m_WorkingMutex.lock();
167 167 qCDebug(LOG_DataSourceController()) << tr("DataSourceController init END");
168 168 }
169 169
170 170 void DataSourceController::finalize()
171 171 {
172 172 impl->m_WorkingMutex.unlock();
173 173 }
174 174
175 175 void DataSourceController::requestVariable(const QVariantHash &productData)
176 176 {
177 177 auto sourceItem = impl->findDataSourceItem(productData);
178 178
179 179 if (sourceItem) {
180 180 auto sourceName = sourceItem->rootItem().name();
181 181 auto sourceId = impl->m_DataSources.key(sourceName);
182 182 loadProductItem(sourceId, *sourceItem);
183 183 }
184 184 else {
185 185 qCWarning(LOG_DataSourceController()) << tr("requestVariable, product data not found");
186 186 }
187 187 }
188 188
189 189 void DataSourceController::waitForFinish()
190 190 {
191 191 QMutexLocker locker{&impl->m_WorkingMutex};
192 192 }
@@ -1,29 +1,51
1 1 #include "Time/TimeController.h"
2 2
3 #include <QDataStream>
4
3 5 Q_LOGGING_CATEGORY(LOG_TimeController, "TimeController")
4 6
5 7 struct TimeController::TimeControllerPrivate {
6 8
7 9 SqpRange m_DateTime;
8 10 };
9 11
10 12 TimeController::TimeController(QObject *parent)
11 13 : QObject{parent}, impl{spimpl::make_unique_impl<TimeControllerPrivate>()}
12 14 {
13 15 qCDebug(LOG_TimeController()) << tr("TimeController construction");
14 16 }
15 17
16 18 SqpRange TimeController::dateTime() const noexcept
17 19 {
18 20 return impl->m_DateTime;
19 21 }
20 22
23 QByteArray TimeController::mimeDataForTimeRange(const SqpRange &timeRange)
24 {
25 QByteArray encodedData;
26 QDataStream stream{&encodedData, QIODevice::WriteOnly};
27
28 stream << timeRange.m_TStart << timeRange.m_TEnd;
29
30 return encodedData;
31 }
32
33 SqpRange TimeController::timeRangeForMimeData(const QByteArray &mimeData)
34 {
35 QDataStream stream{mimeData};
36
37 SqpRange timeRange;
38 stream >> timeRange.m_TStart >> timeRange.m_TEnd;
39
40 return timeRange;
41 }
42
21 43 void TimeController::onTimeToUpdate(SqpRange dateTime)
22 44 {
23 45 impl->m_DateTime = dateTime;
24 46 }
25 47
26 48 void TimeController::onTimeNotify()
27 49 {
28 50 emit timeUpdated(impl->m_DateTime);
29 51 }
@@ -1,368 +1,383
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 #include <QDataStream>
11 #include <DataSource/DataSourceController.h>
12 #include <Time/TimeController.h>
13
12 14 #include <QMimeData>
13 15 #include <QSize>
14 16 #include <unordered_map>
15 17
16 18 Q_LOGGING_CATEGORY(LOG_VariableModel, "VariableModel")
17 19
18 20 namespace {
19 21
20 22 // Column indexes
21 23 const auto NAME_COLUMN = 0;
22 24 const auto TSTART_COLUMN = 1;
23 25 const auto TEND_COLUMN = 2;
24 26 const auto NBPOINTS_COLUMN = 3;
25 27 const auto UNIT_COLUMN = 4;
26 28 const auto MISSION_COLUMN = 5;
27 29 const auto PLUGIN_COLUMN = 6;
28 30 const auto NB_COLUMNS = 7;
29 31
30 32 // Column properties
31 33 const auto DEFAULT_HEIGHT = 25;
32 34 const auto DEFAULT_WIDTH = 100;
33 35
34 36 struct ColumnProperties {
35 37 ColumnProperties(const QString &name = {}, int width = DEFAULT_WIDTH,
36 38 int height = DEFAULT_HEIGHT)
37 39 : m_Name{name}, m_Width{width}, m_Height{height}
38 40 {
39 41 }
40 42
41 43 QString m_Name;
42 44 int m_Width;
43 45 int m_Height;
44 46 };
45 47
46 48 const auto COLUMN_PROPERTIES = QHash<int, ColumnProperties>{
47 49 {NAME_COLUMN, {QObject::tr("Name")}}, {TSTART_COLUMN, {QObject::tr("tStart"), 180}},
48 50 {TEND_COLUMN, {QObject::tr("tEnd"), 180}}, {NBPOINTS_COLUMN, {QObject::tr("Nb points")}},
49 51 {UNIT_COLUMN, {QObject::tr("Unit")}}, {MISSION_COLUMN, {QObject::tr("Mission")}},
50 52 {PLUGIN_COLUMN, {QObject::tr("Plugin")}}};
51 53
52 54 /// Format for datetimes
53 55 const auto DATETIME_FORMAT = QStringLiteral("dd/MM/yyyy \nhh:mm:ss:zzz");
54 56
55 57 QString uniqueName(const QString &defaultName,
56 58 const std::vector<std::shared_ptr<Variable> > &variables)
57 59 {
58 60 auto forbiddenNames = std::vector<QString>(variables.size());
59 61 std::transform(variables.cbegin(), variables.cend(), forbiddenNames.begin(),
60 62 [](const auto &variable) { return variable->name(); });
61 63 auto uniqueName = StringUtils::uniqueName(defaultName, forbiddenNames);
62 64 Q_ASSERT(!uniqueName.isEmpty());
63 65
64 66 return uniqueName;
65 67 }
66 68
67 69 } // namespace
68 70
69 71 struct VariableModel::VariableModelPrivate {
70 72 /// Variables created in SciQlop
71 73 std::vector<std::shared_ptr<Variable> > m_Variables;
72 74 std::unordered_map<std::shared_ptr<Variable>, double> m_VariableToProgress;
73 75 VariableController *m_VariableController;
74 76
75 77 /// Return the row index of the variable. -1 if it's not found
76 78 int indexOfVariable(Variable *variable) const noexcept;
77 79 };
78 80
79 81 VariableModel::VariableModel(VariableController *parent)
80 82 : QAbstractTableModel{parent}, impl{spimpl::make_unique_impl<VariableModelPrivate>()}
81 83 {
82 84 impl->m_VariableController = parent;
83 85 }
84 86
85 87 void VariableModel::addVariable(std::shared_ptr<Variable> variable) noexcept
86 88 {
87 89 auto insertIndex = rowCount();
88 90 beginInsertRows({}, insertIndex, insertIndex);
89 91
90 92 // Generates unique name for the variable
91 93 variable->setName(uniqueName(variable->name(), impl->m_Variables));
92 94
93 95 impl->m_Variables.push_back(variable);
94 96 connect(variable.get(), &Variable::updated, this, &VariableModel::onVariableUpdated);
95 97
96 98 endInsertRows();
97 99 }
98 100
99 101 bool VariableModel::containsVariable(std::shared_ptr<Variable> variable) const noexcept
100 102 {
101 103 auto end = impl->m_Variables.cend();
102 104 return std::find(impl->m_Variables.cbegin(), end, variable) != end;
103 105 }
104 106
105 107 std::shared_ptr<Variable> VariableModel::createVariable(const QString &name,
106 108 const QVariantHash &metadata) noexcept
107 109 {
108 110 auto variable = std::make_shared<Variable>(name, metadata);
109 111 addVariable(variable);
110 112
111 113 return variable;
112 114 }
113 115
114 116 void VariableModel::deleteVariable(std::shared_ptr<Variable> variable) noexcept
115 117 {
116 118 if (!variable) {
117 119 qCCritical(LOG_Variable()) << "Can't delete a null variable from the model";
118 120 return;
119 121 }
120 122
121 123 // Finds variable in the model
122 124 auto begin = impl->m_Variables.cbegin();
123 125 auto end = impl->m_Variables.cend();
124 126 auto it = std::find(begin, end, variable);
125 127 if (it != end) {
126 128 auto removeIndex = std::distance(begin, it);
127 129
128 130 // Deletes variable
129 131 beginRemoveRows({}, removeIndex, removeIndex);
130 132 impl->m_Variables.erase(it);
131 133 endRemoveRows();
132 134 }
133 135 else {
134 136 qCritical(LOG_VariableModel())
135 137 << tr("Can't delete variable %1 from the model: the variable is not in the model")
136 138 .arg(variable->name());
137 139 }
138 140
139 141 // Removes variable from progress map
140 142 impl->m_VariableToProgress.erase(variable);
141 143 }
142 144
143 145
144 146 std::shared_ptr<Variable> VariableModel::variable(int index) const
145 147 {
146 148 return (index >= 0 && index < impl->m_Variables.size()) ? impl->m_Variables[index] : nullptr;
147 149 }
148 150
149 151 std::vector<std::shared_ptr<Variable> > VariableModel::variables() const
150 152 {
151 153 return impl->m_Variables;
152 154 }
153 155
154 156 void VariableModel::setDataProgress(std::shared_ptr<Variable> variable, double progress)
155 157 {
156 158 if (progress > 0.0) {
157 159 impl->m_VariableToProgress[variable] = progress;
158 160 }
159 161 else {
160 162 impl->m_VariableToProgress.erase(variable);
161 163 }
162 164 auto modelIndex = createIndex(impl->indexOfVariable(variable.get()), NAME_COLUMN);
163 165
164 166 emit dataChanged(modelIndex, modelIndex);
165 167 }
166 168
167 169 int VariableModel::columnCount(const QModelIndex &parent) const
168 170 {
169 171 Q_UNUSED(parent);
170 172
171 173 return NB_COLUMNS;
172 174 }
173 175
174 176 int VariableModel::rowCount(const QModelIndex &parent) const
175 177 {
176 178 Q_UNUSED(parent);
177 179
178 180 return impl->m_Variables.size();
179 181 }
180 182
181 183 QVariant VariableModel::data(const QModelIndex &index, int role) const
182 184 {
183 185 if (!index.isValid()) {
184 186 return QVariant{};
185 187 }
186 188
187 189 if (index.row() < 0 || index.row() >= rowCount()) {
188 190 return QVariant{};
189 191 }
190 192
191 193 if (role == Qt::DisplayRole) {
192 194 if (auto variable = impl->m_Variables.at(index.row()).get()) {
193 195 switch (index.column()) {
194 196 case NAME_COLUMN:
195 197 return variable->name();
196 198 case TSTART_COLUMN: {
197 199 auto range = variable->realRange();
198 200 return range != INVALID_RANGE
199 201 ? DateUtils::dateTime(range.m_TStart).toString(DATETIME_FORMAT)
200 202 : QVariant{};
201 203 }
202 204 case TEND_COLUMN: {
203 205 auto range = variable->realRange();
204 206 return range != INVALID_RANGE
205 207 ? DateUtils::dateTime(range.m_TEnd).toString(DATETIME_FORMAT)
206 208 : QVariant{};
207 209 }
208 210 case NBPOINTS_COLUMN:
209 211 return variable->nbPoints();
210 212 case UNIT_COLUMN:
211 213 return variable->metadata().value(QStringLiteral("units"));
212 214 case MISSION_COLUMN:
213 215 return variable->metadata().value(QStringLiteral("mission"));
214 216 case PLUGIN_COLUMN:
215 217 return variable->metadata().value(QStringLiteral("plugin"));
216 218 default:
217 219 // No action
218 220 break;
219 221 }
220 222
221 223 qWarning(LOG_VariableModel())
222 224 << tr("Can't get data (unknown column %1)").arg(index.column());
223 225 }
224 226 else {
225 227 qWarning(LOG_VariableModel()) << tr("Can't get data (no variable)");
226 228 }
227 229 }
228 230 else if (role == VariableRoles::ProgressRole) {
229 231 if (auto variable = impl->m_Variables.at(index.row())) {
230 232
231 233 auto it = impl->m_VariableToProgress.find(variable);
232 234 if (it != impl->m_VariableToProgress.cend()) {
233 235 return it->second;
234 236 }
235 237 }
236 238 }
237 239
238 240 return QVariant{};
239 241 }
240 242
241 243 QVariant VariableModel::headerData(int section, Qt::Orientation orientation, int role) const
242 244 {
243 245 if (role != Qt::DisplayRole && role != Qt::SizeHintRole) {
244 246 return QVariant{};
245 247 }
246 248
247 249 if (orientation == Qt::Horizontal) {
248 250 auto propertiesIt = COLUMN_PROPERTIES.find(section);
249 251 if (propertiesIt != COLUMN_PROPERTIES.cend()) {
250 252 // Role is either DisplayRole or SizeHintRole
251 253 return (role == Qt::DisplayRole)
252 254 ? QVariant{propertiesIt->m_Name}
253 255 : QVariant{QSize{propertiesIt->m_Width, propertiesIt->m_Height}};
254 256 }
255 257 else {
256 258 qWarning(LOG_VariableModel())
257 259 << tr("Can't get header data (unknown column %1)").arg(section);
258 260 }
259 261 }
260 262
261 263 return QVariant{};
262 264 }
263 265
264 266 Qt::ItemFlags VariableModel::flags(const QModelIndex &index) const
265 267 {
266 268 return QAbstractTableModel::flags(index) | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
267 269 }
268 270
269 271 Qt::DropActions VariableModel::supportedDropActions() const
270 272 {
271 273 return Qt::CopyAction | Qt::MoveAction;
272 274 }
273 275
274 276 Qt::DropActions VariableModel::supportedDragActions() const
275 277 {
276 278 return Qt::CopyAction | Qt::MoveAction;
277 279 }
278 280
279 281 QStringList VariableModel::mimeTypes() const
280 282 {
281 return {MIME_TYPE_VARIABLE_LIST};
283 return {MIME_TYPE_VARIABLE_LIST, MIME_TYPE_TIME_RANGE};
282 284 }
283 285
284 286 QMimeData *VariableModel::mimeData(const QModelIndexList &indexes) const
285 287 {
286 288 auto mimeData = new QMimeData;
287 289
288 290 QList<std::shared_ptr<Variable> > variableList;
289 291
292
293 SqpRange firstTimeRange;
290 294 for (const auto &index : indexes) {
291 295 if (index.column() == 0) { // only the first column
292 296 auto variable = impl->m_Variables.at(index.row());
293 297 if (variable.get() && index.isValid()) {
298
299 if (variableList.isEmpty()) {
300 // Gets the range of the first variable
301 firstTimeRange = std::move(variable->range());
302 }
303
294 304 variableList << variable;
295 305 }
296 306 }
297 307 }
298 308
299 auto encodedData = impl->m_VariableController->mimeDataForVariables(variableList);
300 mimeData->setData(MIME_TYPE_VARIABLE_LIST, encodedData);
309 auto variablesEncodedData = impl->m_VariableController->mimeDataForVariables(variableList);
310 mimeData->setData(MIME_TYPE_VARIABLE_LIST, variablesEncodedData);
311
312 if (variableList.count() == 1) {
313 // No time range MIME data if multiple variables are dragged
314 auto timeEncodedData = TimeController::mimeDataForTimeRange(firstTimeRange);
315 mimeData->setData(MIME_TYPE_TIME_RANGE, timeEncodedData);
316 }
301 317
302 318 return mimeData;
303 319 }
304 320
305 321 bool VariableModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row,
306 322 int column, const QModelIndex &parent) const
307 323 {
308 324 // drop of a product
309 325 return data->hasFormat(MIME_TYPE_PRODUCT_LIST);
310 326 }
311 327
312 328 bool VariableModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column,
313 329 const QModelIndex &parent)
314 330 {
315 331 auto dropDone = false;
316 332
317 333 if (data->hasFormat(MIME_TYPE_PRODUCT_LIST)) {
318 QDataStream stream(data->data(MIME_TYPE_PRODUCT_LIST));
319 334
320 QVariantList productList;
321 stream >> productList;
335 auto productList
336 = DataSourceController::productsDataForMimeData(data->data(MIME_TYPE_PRODUCT_LIST));
322 337
323 338 for (auto metaData : productList) {
324 339 emit requestVariable(metaData.toHash());
325 340 }
326 341
327 342 dropDone = true;
328 343 }
329 344
330 345 return dropDone;
331 346 }
332 347
333 348 void VariableModel::abortProgress(const QModelIndex &index)
334 349 {
335 350 if (auto variable = impl->m_Variables.at(index.row())) {
336 351 emit abortProgessRequested(variable);
337 352 }
338 353 }
339 354
340 355 void VariableModel::onVariableUpdated() noexcept
341 356 {
342 357 // Finds variable that has been updated in the model
343 358 if (auto updatedVariable = dynamic_cast<Variable *>(sender())) {
344 359 auto updatedVariableIndex = impl->indexOfVariable(updatedVariable);
345 360
346 361 if (updatedVariableIndex > -1) {
347 362 emit dataChanged(createIndex(updatedVariableIndex, 0),
348 363 createIndex(updatedVariableIndex, columnCount() - 1));
349 364 }
350 365 }
351 366 }
352 367
353 368 int VariableModel::VariableModelPrivate::indexOfVariable(Variable *variable) const noexcept
354 369 {
355 370 auto begin = std::cbegin(m_Variables);
356 371 auto end = std::cend(m_Variables);
357 372 auto it
358 373 = std::find_if(begin, end, [variable](const auto &var) { return var.get() == variable; });
359 374
360 375 if (it != end) {
361 376 // Gets the index of the variable in the model: we assume here that views have the same
362 377 // order as the model
363 378 return std::distance(begin, it);
364 379 }
365 380 else {
366 381 return -1;
367 382 }
368 383 }
@@ -1,32 +1,39
1 1 #ifndef SCIQLOP_TIMEWIDGET_H
2 2 #define SCIQLOP_TIMEWIDGET_H
3 3
4 4 #include <QWidget>
5 5
6 6 #include <Data/SqpRange.h>
7 7
8 8 namespace Ui {
9 9 class TimeWidget;
10 10 } // Ui
11 11
12 12 class TimeWidget : public QWidget {
13 13 Q_OBJECT
14 14
15 15 public:
16 16 explicit TimeWidget(QWidget *parent = 0);
17 17 virtual ~TimeWidget();
18 18
19 void setTimeRange(SqpRange time);
20
19 21 signals:
20 22 /// Signal emitted when the time parameters has beed updated
21 23 void timeUpdated(SqpRange time);
22 24
23 25 public slots:
24 26 /// slot called when time parameters update has ben requested
25 27 void onTimeUpdateRequested();
26 28
29 protected:
30 void dragEnterEvent(QDragEnterEvent *event) override;
31 void dragLeaveEvent(QDragLeaveEvent *event) override;
32 void dropEvent(QDropEvent *event) override;
33
27 34
28 35 private:
29 36 Ui::TimeWidget *ui;
30 37 };
31 38
32 39 #endif // SCIQLOP_ SQPSIDEPANE_H
@@ -1,50 +1,96
1 1 #include "TimeWidget/TimeWidget.h"
2 2 #include "ui_TimeWidget.h"
3 3
4 4 #include <Common/DateUtils.h>
5 #include <Common/MimeTypesDef.h>
6
5 7 #include <SqpApplication.h>
6 8 #include <Time/TimeController.h>
7 9
10 #include <QDragEnterEvent>
11 #include <QDropEvent>
12 #include <QMimeData>
13
8 14 TimeWidget::TimeWidget(QWidget *parent) : QWidget{parent}, ui{new Ui::TimeWidget}
9 15 {
10 16 ui->setupUi(this);
11 17
12 18 ui->applyToolButton->setIcon(sqpApp->style()->standardIcon(QStyle::SP_DialogApplyButton));
13 19
14 20 // Connection
15 21 connect(ui->startDateTimeEdit, &QDateTimeEdit::dateTimeChanged, this,
16 22 &TimeWidget::onTimeUpdateRequested);
17 23
18 24 connect(ui->endDateTimeEdit, &QDateTimeEdit::dateTimeChanged, this,
19 25 &TimeWidget::onTimeUpdateRequested);
20 26
21 27
22 28 connect(ui->applyToolButton, &QToolButton::clicked, &sqpApp->timeController(),
23 29 &TimeController::onTimeNotify);
24 30
25 31 // Initialisation
26 32 auto endDateTime = QDateTime::currentDateTimeUtc();
27 33 auto startDateTime = endDateTime.addSecs(-3600); // one hour before
28 34
29 35 ui->startDateTimeEdit->setDateTime(startDateTime);
30 36 ui->endDateTimeEdit->setDateTime(endDateTime);
31 37
32 38 auto dateTime = SqpRange{DateUtils::secondsSinceEpoch(startDateTime),
33 39 DateUtils::secondsSinceEpoch(endDateTime)};
34 40
35 41 sqpApp->timeController().onTimeToUpdate(dateTime);
36 42 }
37 43
38 44
39 45 TimeWidget::~TimeWidget()
40 46 {
41 47 delete ui;
42 48 }
43 49
50 void TimeWidget::setTimeRange(SqpRange time)
51 {
52 auto startDateTime = DateUtils::dateTime(time.m_TStart);
53 auto endDateTime = DateUtils::dateTime(time.m_TEnd);
54
55 ui->startDateTimeEdit->setDateTime(startDateTime);
56 ui->endDateTimeEdit->setDateTime(endDateTime);
57 }
58
44 59 void TimeWidget::onTimeUpdateRequested()
45 60 {
46 61 auto dateTime = SqpRange{DateUtils::secondsSinceEpoch(ui->startDateTimeEdit->dateTime()),
47 62 DateUtils::secondsSinceEpoch(ui->endDateTimeEdit->dateTime())};
48 63
49 64 emit timeUpdated(std::move(dateTime));
50 65 }
66
67 void TimeWidget::dragEnterEvent(QDragEnterEvent *event)
68 {
69 if (event->mimeData()->hasFormat(MIME_TYPE_TIME_RANGE)) {
70 event->acceptProposedAction();
71 setStyleSheet("QDateTimeEdit{background-color: #BBD5EE; border:2px solid #2A7FD4}");
72 }
73 else {
74 event->ignore();
75 }
76 }
77
78 void TimeWidget::dragLeaveEvent(QDragLeaveEvent *event)
79 {
80 setStyleSheet(QString());
81 }
82
83 void TimeWidget::dropEvent(QDropEvent *event)
84 {
85 if (event->mimeData()->hasFormat(MIME_TYPE_TIME_RANGE)) {
86 auto mimeData = event->mimeData()->data(MIME_TYPE_TIME_RANGE);
87 auto timeRange = TimeController::timeRangeForMimeData(mimeData);
88
89 setTimeRange(timeRange);
90 }
91 else {
92 event->ignore();
93 }
94
95 setStyleSheet(QString());
96 }
@@ -1,397 +1,401
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 <Common/MimeTypesDef.h>
10 10 #include <Data/ArrayData.h>
11 11 #include <Data/IDataSeries.h>
12 12 #include <DragDropHelper.h>
13 13 #include <Settings/SqpSettingsDefs.h>
14 14 #include <SqpApplication.h>
15 #include <Time/TimeController.h>
15 16 #include <Variable/Variable.h>
16 17 #include <Variable/VariableController.h>
17 18
18 19 #include <unordered_map>
19 20
20 21 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
21 22
22 23 namespace {
23 24
24 25 /// Key pressed to enable zoom on horizontal axis
25 26 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::NoModifier;
26 27
27 28 /// Key pressed to enable zoom on vertical axis
28 29 const auto VERTICAL_ZOOM_MODIFIER = Qt::ControlModifier;
29 30
30 31 } // namespace
31 32
32 33 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
33 34
34 35 explicit VisualizationGraphWidgetPrivate(const QString &name)
35 36 : m_Name{name},
36 37 m_DoAcquisition{true},
37 38 m_IsCalibration{false},
38 39 m_RenderingDelegate{nullptr}
39 40 {
40 41 }
41 42
42 43 QString m_Name;
43 44 // 1 variable -> n qcpplot
44 45 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
45 46 bool m_DoAcquisition;
46 47 bool m_IsCalibration;
47 48 QCPItemTracer *m_TextTracer;
48 49 /// Delegate used to attach rendering features to the plot
49 50 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
50 51 };
51 52
52 53 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
53 54 : VisualizationDragWidget{parent},
54 55 ui{new Ui::VisualizationGraphWidget},
55 56 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
56 57 {
57 58 ui->setupUi(this);
58 59
59 60 // 'Close' options : widget is deleted when closed
60 61 setAttribute(Qt::WA_DeleteOnClose);
61 62
62 63 // Set qcpplot properties :
63 64 // - Drag (on x-axis) and zoom are enabled
64 65 // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation
65 66 ui->widget->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectItems);
66 67 ui->widget->axisRect()->setRangeDrag(Qt::Horizontal);
67 68
68 69 // The delegate must be initialized after the ui as it uses the plot
69 70 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
70 71
71 72 connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress);
72 73 connect(ui->widget, &QCustomPlot::mouseRelease, this,
73 74 &VisualizationGraphWidget::onMouseRelease);
74 75 connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove);
75 76 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
76 77 connect(ui->widget->xAxis, static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(
77 78 &QCPAxis::rangeChanged),
78 79 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
79 80
80 81 // Activates menu when right clicking on the graph
81 82 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
82 83 connect(ui->widget, &QCustomPlot::customContextMenuRequested, this,
83 84 &VisualizationGraphWidget::onGraphMenuRequested);
84 85
85 86 connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
86 87 &VariableController::onRequestDataLoading);
87 88
88 89 connect(&sqpApp->variableController(), &VariableController::updateVarDisplaying, this,
89 90 &VisualizationGraphWidget::onUpdateVarDisplaying);
90 91 }
91 92
92 93
93 94 VisualizationGraphWidget::~VisualizationGraphWidget()
94 95 {
95 96 delete ui;
96 97 }
97 98
98 99 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
99 100 {
100 101 auto parent = parentWidget();
101 102 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
102 103 parent = parent->parentWidget();
103 104 }
104 105
105 106 return qobject_cast<VisualizationZoneWidget *>(parent);
106 107 }
107 108
108 109 void VisualizationGraphWidget::enableAcquisition(bool enable)
109 110 {
110 111 impl->m_DoAcquisition = enable;
111 112 }
112 113
113 114 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, SqpRange range)
114 115 {
115 116 // Uses delegate to create the qcpplot components according to the variable
116 117 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
117 118 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
118 119
119 120 // Set axes properties according to the units of the data series
120 121 /// @todo : for the moment, no control is performed on the axes: the units and the tickers
121 122 /// are fixed for the default x-axis and y-axis of the plot, and according to the new graph
122 123 auto xAxisUnit = Unit{};
123 124 auto valuesUnit = Unit{};
124 125
125 126 if (auto dataSeries = variable->dataSeries()) {
126 127 dataSeries->lockRead();
127 128 xAxisUnit = dataSeries->xAxisUnit();
128 129 valuesUnit = dataSeries->valuesUnit();
129 130 dataSeries->unlock();
130 131 }
131 132 impl->m_RenderingDelegate->setAxesProperties(xAxisUnit, valuesUnit);
132 133
133 134 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
134 135
135 136 this->enableAcquisition(false);
136 137 this->setGraphRange(range);
137 138 this->enableAcquisition(true);
138 139
139 140 emit requestDataLoading(QVector<std::shared_ptr<Variable> >() << variable, range, false);
140 141
141 142 emit variableAdded(variable);
142 143 }
143 144
144 145 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
145 146 {
146 147 // Each component associated to the variable :
147 148 // - is removed from qcpplot (which deletes it)
148 149 // - is no longer referenced in the map
149 150 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
150 151 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
151 152 emit variableAboutToBeRemoved(variable);
152 153
153 154 auto &plottablesMap = variableIt->second;
154 155
155 156 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
156 157 plottableIt != plottableEnd;) {
157 158 ui->widget->removePlottable(plottableIt->second);
158 159 plottableIt = plottablesMap.erase(plottableIt);
159 160 }
160 161
161 162 impl->m_VariableToPlotMultiMap.erase(variableIt);
162 163 }
163 164
164 165 // Updates graph
165 166 ui->widget->replot();
166 167 }
167 168
168 169 QList<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
169 170 {
170 171 auto variables = QList<std::shared_ptr<Variable> >{};
171 172 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
172 173 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
173 174 variables << it->first;
174 175 }
175 176
176 177 return variables;
177 178 }
178 179
179 180 void VisualizationGraphWidget::setYRange(const SqpRange &range)
180 181 {
181 182 ui->widget->yAxis->setRange(range.m_TStart, range.m_TEnd);
182 183 }
183 184
184 185 SqpRange VisualizationGraphWidget::graphRange() const noexcept
185 186 {
186 187 auto graphRange = ui->widget->xAxis->range();
187 188 return SqpRange{graphRange.lower, graphRange.upper};
188 189 }
189 190
190 191 void VisualizationGraphWidget::setGraphRange(const SqpRange &range)
191 192 {
192 193 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange START");
193 194 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
194 195 ui->widget->replot();
195 196 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange END");
196 197 }
197 198
198 199 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
199 200 {
200 201 if (visitor) {
201 202 visitor->visit(this);
202 203 }
203 204 else {
204 205 qCCritical(LOG_VisualizationGraphWidget())
205 206 << tr("Can't visit widget : the visitor is null");
206 207 }
207 208 }
208 209
209 210 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
210 211 {
211 212 /// @todo : for the moment, a graph can always accomodate a variable
212 213 Q_UNUSED(variable);
213 214 return true;
214 215 }
215 216
216 217 bool VisualizationGraphWidget::contains(const Variable &variable) const
217 218 {
218 219 // Finds the variable among the keys of the map
219 220 auto variablePtr = &variable;
220 221 auto findVariable
221 222 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
222 223
223 224 auto end = impl->m_VariableToPlotMultiMap.cend();
224 225 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
225 226 return it != end;
226 227 }
227 228
228 229 QString VisualizationGraphWidget::name() const
229 230 {
230 231 return impl->m_Name;
231 232 }
232 233
233 234 QMimeData *VisualizationGraphWidget::mimeData() const
234 235 {
235 236 auto mimeData = new QMimeData;
236 237 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
237 238
239 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
240 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
241
238 242 return mimeData;
239 243 }
240 244
241 245 bool VisualizationGraphWidget::isDragAllowed() const
242 246 {
243 247 return true;
244 248 }
245 249
246 250 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
247 251 {
248 252 if (highlighted) {
249 253 plot().setBackground(QBrush(QColor("#BBD5EE")));
250 254 }
251 255 else {
252 256 plot().setBackground(QBrush(Qt::white));
253 257 }
254 258
255 259 plot().update();
256 260 }
257 261
258 262 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
259 263 {
260 264 Q_UNUSED(event);
261 265
262 266 // Prevents that all variables will be removed from graph when it will be closed
263 267 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
264 268 emit variableAboutToBeRemoved(variableEntry.first);
265 269 }
266 270 }
267 271
268 272 void VisualizationGraphWidget::enterEvent(QEvent *event)
269 273 {
270 274 Q_UNUSED(event);
271 275 impl->m_RenderingDelegate->showGraphOverlay(true);
272 276 }
273 277
274 278 void VisualizationGraphWidget::leaveEvent(QEvent *event)
275 279 {
276 280 Q_UNUSED(event);
277 281 impl->m_RenderingDelegate->showGraphOverlay(false);
278 282 }
279 283
280 284 QCustomPlot &VisualizationGraphWidget::plot() noexcept
281 285 {
282 286 return *ui->widget;
283 287 }
284 288
285 289 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
286 290 {
287 291 QMenu graphMenu{};
288 292
289 293 // Iterates on variables (unique keys)
290 294 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
291 295 end = impl->m_VariableToPlotMultiMap.cend();
292 296 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
293 297 // 'Remove variable' action
294 298 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
295 299 [ this, var = it->first ]() { removeVariable(var); });
296 300 }
297 301
298 302 if (!graphMenu.isEmpty()) {
299 303 graphMenu.exec(QCursor::pos());
300 304 }
301 305 }
302 306
303 307 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
304 308 {
305 309 qCDebug(LOG_VisualizationGraphWidget()) << tr("TORM: VisualizationGraphWidget::onRangeChanged")
306 310 << QThread::currentThread()->objectName() << "DoAcqui"
307 311 << impl->m_DoAcquisition;
308 312
309 313 auto graphRange = SqpRange{t1.lower, t1.upper};
310 314 auto oldGraphRange = SqpRange{t2.lower, t2.upper};
311 315
312 316 if (impl->m_DoAcquisition) {
313 317 QVector<std::shared_ptr<Variable> > variableUnderGraphVector;
314 318
315 319 for (auto it = impl->m_VariableToPlotMultiMap.begin(),
316 320 end = impl->m_VariableToPlotMultiMap.end();
317 321 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
318 322 variableUnderGraphVector.push_back(it->first);
319 323 }
320 324 emit requestDataLoading(std::move(variableUnderGraphVector), graphRange,
321 325 !impl->m_IsCalibration);
322 326
323 327 if (!impl->m_IsCalibration) {
324 328 qCDebug(LOG_VisualizationGraphWidget())
325 329 << tr("TORM: VisualizationGraphWidget::Synchronize notify !!")
326 330 << QThread::currentThread()->objectName() << graphRange << oldGraphRange;
327 331 emit synchronize(graphRange, oldGraphRange);
328 332 }
329 333 }
330 334 }
331 335
332 336 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
333 337 {
334 338 // Handles plot rendering when mouse is moving
335 339 impl->m_RenderingDelegate->onMouseMove(event);
336 340
337 341 VisualizationDragWidget::mouseMoveEvent(event);
338 342 }
339 343
340 344 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
341 345 {
342 346 auto zoomOrientations = QFlags<Qt::Orientation>{};
343 347
344 348 // Lambda that enables a zoom orientation if the key modifier related to this orientation
345 349 // has
346 350 // been pressed
347 351 auto enableOrientation
348 352 = [&zoomOrientations, event](const auto &orientation, const auto &modifier) {
349 353 auto orientationEnabled = event->modifiers().testFlag(modifier);
350 354 zoomOrientations.setFlag(orientation, orientationEnabled);
351 355 };
352 356 enableOrientation(Qt::Vertical, VERTICAL_ZOOM_MODIFIER);
353 357 enableOrientation(Qt::Horizontal, HORIZONTAL_ZOOM_MODIFIER);
354 358
355 359 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
356 360 }
357 361
358 362 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
359 363 {
360 364 impl->m_IsCalibration = event->modifiers().testFlag(Qt::ControlModifier);
361 365
362 366 plot().setInteraction(QCP::iRangeDrag, !event->modifiers().testFlag(Qt::AltModifier));
363 367
364 368 VisualizationDragWidget::mousePressEvent(event);
365 369 }
366 370
367 371 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
368 372 {
369 373 impl->m_IsCalibration = false;
370 374 }
371 375
372 376 void VisualizationGraphWidget::onDataCacheVariableUpdated()
373 377 {
374 378 auto graphRange = ui->widget->xAxis->range();
375 379 auto dateTime = SqpRange{graphRange.lower, graphRange.upper};
376 380
377 381 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
378 382 auto variable = variableEntry.first;
379 383 qCDebug(LOG_VisualizationGraphWidget())
380 384 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
381 385 qCDebug(LOG_VisualizationGraphWidget())
382 386 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
383 387 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
384 388 VisualizationGraphHelper::updateData(variableEntry.second, variable->dataSeries(),
385 389 variable->range());
386 390 }
387 391 }
388 392 }
389 393
390 394 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
391 395 const SqpRange &range)
392 396 {
393 397 auto it = impl->m_VariableToPlotMultiMap.find(variable);
394 398 if (it != impl->m_VariableToPlotMultiMap.end()) {
395 399 VisualizationGraphHelper::updateData(it->second, variable->dataSeries(), range);
396 400 }
397 401 }
@@ -1,497 +1,512
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 "Visualization/VisualizationWidget.h"
7 7 #include "ui_VisualizationZoneWidget.h"
8 8
9 9 #include "Common/MimeTypesDef.h"
10 10 #include "Common/VisualizationDef.h"
11 11
12 12 #include <Data/SqpRange.h>
13 #include <Time/TimeController.h>
13 14 #include <Variable/Variable.h>
14 15 #include <Variable/VariableController.h>
15 16
16 17 #include <Visualization/operations/FindVariableOperation.h>
17 18
18 19 #include <DragDropHelper.h>
19 20 #include <QUuid>
20 21 #include <SqpApplication.h>
21 22 #include <cmath>
22 23
23 24 #include <QLayout>
24 25
25 26 Q_LOGGING_CATEGORY(LOG_VisualizationZoneWidget, "VisualizationZoneWidget")
26 27
27 28 namespace {
28 29
29 30
30 31 /// Generates a default name for a new graph, according to the number of graphs already displayed in
31 32 /// the zone
32 33 QString defaultGraphName(const QLayout &layout)
33 34 {
34 35 auto count = 0;
35 36 for (auto i = 0; i < layout.count(); ++i) {
36 37 if (dynamic_cast<VisualizationGraphWidget *>(layout.itemAt(i)->widget())) {
37 38 count++;
38 39 }
39 40 }
40 41
41 42 return QObject::tr("Graph %1").arg(count + 1);
42 43 }
43 44
44 45 /**
45 46 * Applies a function to all graphs of the zone represented by its layout
46 47 * @param layout the layout that contains graphs
47 48 * @param fun the function to apply to each graph
48 49 */
49 50 template <typename Fun>
50 51 void processGraphs(QLayout &layout, Fun fun)
51 52 {
52 53 for (auto i = 0; i < layout.count(); ++i) {
53 54 if (auto item = layout.itemAt(i)) {
54 55 if (auto visualizationGraphWidget
55 56 = dynamic_cast<VisualizationGraphWidget *>(item->widget())) {
56 57 fun(*visualizationGraphWidget);
57 58 }
58 59 }
59 60 }
60 61 }
61 62
62 63 } // namespace
63 64
64 65 struct VisualizationZoneWidget::VisualizationZoneWidgetPrivate {
65 66
66 67 explicit VisualizationZoneWidgetPrivate()
67 68 : m_SynchronisationGroupId{QUuid::createUuid()},
68 69 m_Synchronizer{std::make_unique<QCustomPlotSynchronizer>()}
69 70 {
70 71 }
71 72 QUuid m_SynchronisationGroupId;
72 73 std::unique_ptr<IGraphSynchronizer> m_Synchronizer;
73 74
75 // Returns the first graph in the zone or nullptr if there is no graph inside
76 VisualizationGraphWidget *firstGraph(const VisualizationZoneWidget *zoneWidget) const
77 {
78 VisualizationGraphWidget *firstGraph = nullptr;
79 auto layout = zoneWidget->ui->dragDropContainer->layout();
80 if (layout->count() > 0) {
81 if (auto visualizationGraphWidget
82 = qobject_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
83 firstGraph = visualizationGraphWidget;
84 }
85 }
86
87 return firstGraph;
88 }
89
74 90 void dropGraph(int index, VisualizationZoneWidget *zoneWidget);
75 91 void dropVariables(const QList<std::shared_ptr<Variable> > &variables, int index,
76 92 VisualizationZoneWidget *zoneWidget);
77 93 };
78 94
79 95 VisualizationZoneWidget::VisualizationZoneWidget(const QString &name, QWidget *parent)
80 96 : VisualizationDragWidget{parent},
81 97 ui{new Ui::VisualizationZoneWidget},
82 98 impl{spimpl::make_unique_impl<VisualizationZoneWidgetPrivate>()}
83 99 {
84 100 ui->setupUi(this);
85 101
86 102 ui->zoneNameLabel->setText(name);
87 103
88 104 ui->dragDropContainer->setAcceptedMimeTypes({MIME_TYPE_GRAPH, MIME_TYPE_VARIABLE_LIST});
89 105 ui->dragDropContainer->setMergeAllowedMimeTypes({MIME_TYPE_VARIABLE_LIST});
90 106 ui->dragDropContainer->setAcceptMimeDataFunction([this](auto mimeData) {
91 107 return sqpApp->dragDropHelper().checkMimeDataForVisualization(mimeData,
92 108 ui->dragDropContainer);
93 109 });
94 110
95 111 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredInContainer, this,
96 112 &VisualizationZoneWidget::dropMimeData);
97 113 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredOnWidget, this,
98 114 &VisualizationZoneWidget::dropMimeDataOnGraph);
99 115
100 116 // 'Close' options : widget is deleted when closed
101 117 setAttribute(Qt::WA_DeleteOnClose);
102 118 connect(ui->closeButton, &QToolButton::clicked, this, &VisualizationZoneWidget::close);
103 119 ui->closeButton->setIcon(sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton));
104 120
105 121 // Synchronisation id
106 122 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronizationGroupId",
107 123 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
108 124 }
109 125
110 126 VisualizationZoneWidget::~VisualizationZoneWidget()
111 127 {
112 128 delete ui;
113 129 }
114 130
115 131 void VisualizationZoneWidget::addGraph(VisualizationGraphWidget *graphWidget)
116 132 {
117 133 // Synchronize new graph with others in the zone
118 134 impl->m_Synchronizer->addGraph(*graphWidget);
119 135
120 136 ui->dragDropContainer->addDragWidget(graphWidget);
121 137 }
122 138
123 139 void VisualizationZoneWidget::insertGraph(int index, VisualizationGraphWidget *graphWidget)
124 140 {
125 141 // Synchronize new graph with others in the zone
126 142 impl->m_Synchronizer->addGraph(*graphWidget);
127 143
128 144 ui->dragDropContainer->insertDragWidget(index, graphWidget);
129 145 }
130 146
131 147 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable)
132 148 {
133 149 return createGraph(variable, -1);
134 150 }
135 151
136 152 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable,
137 153 int index)
138 154 {
139 155 auto graphWidget
140 156 = new VisualizationGraphWidget{defaultGraphName(*ui->dragDropContainer->layout()), this};
141 157
142 158
143 159 // Set graph properties
144 160 graphWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
145 161 graphWidget->setMinimumHeight(GRAPH_MINIMUM_HEIGHT);
146 162
147 163
148 164 // Lambda to synchronize zone widget
149 165 auto synchronizeZoneWidget = [this, graphWidget](const SqpRange &graphRange,
150 166 const SqpRange &oldGraphRange) {
151 167
152 168 auto zoomType = VariableController::getZoomType(graphRange, oldGraphRange);
153 169 auto frameLayout = ui->dragDropContainer->layout();
154 170 for (auto i = 0; i < frameLayout->count(); ++i) {
155 171 auto graphChild
156 172 = dynamic_cast<VisualizationGraphWidget *>(frameLayout->itemAt(i)->widget());
157 173 if (graphChild && (graphChild != graphWidget)) {
158 174
159 175 auto graphChildRange = graphChild->graphRange();
160 176 switch (zoomType) {
161 177 case AcquisitionZoomType::ZoomIn: {
162 178 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
163 179 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
164 180 graphChildRange.m_TStart += deltaLeft;
165 181 graphChildRange.m_TEnd -= deltaRight;
166 182 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomIn");
167 183 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
168 184 << deltaLeft;
169 185 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
170 186 << deltaRight;
171 187 qCDebug(LOG_VisualizationZoneWidget())
172 188 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
173 189
174 190 break;
175 191 }
176 192
177 193 case AcquisitionZoomType::ZoomOut: {
178 194 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomOut");
179 195 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
180 196 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
181 197 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
182 198 << deltaLeft;
183 199 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
184 200 << deltaRight;
185 201 qCDebug(LOG_VisualizationZoneWidget())
186 202 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
187 203 graphChildRange.m_TStart -= deltaLeft;
188 204 graphChildRange.m_TEnd += deltaRight;
189 205 break;
190 206 }
191 207 case AcquisitionZoomType::PanRight: {
192 208 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: PanRight");
193 209 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
194 210 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
195 211 graphChildRange.m_TStart += deltaLeft;
196 212 graphChildRange.m_TEnd += deltaRight;
197 213 qCDebug(LOG_VisualizationZoneWidget())
198 214 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
199 215 break;
200 216 }
201 217 case AcquisitionZoomType::PanLeft: {
202 218 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: PanLeft");
203 219 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
204 220 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
205 221 graphChildRange.m_TStart -= deltaLeft;
206 222 graphChildRange.m_TEnd -= deltaRight;
207 223 break;
208 224 }
209 225 case AcquisitionZoomType::Unknown: {
210 226 qCDebug(LOG_VisualizationZoneWidget())
211 227 << tr("Impossible to synchronize: zoom type unknown");
212 228 break;
213 229 }
214 230 default:
215 231 qCCritical(LOG_VisualizationZoneWidget())
216 232 << tr("Impossible to synchronize: zoom type not take into account");
217 233 // No action
218 234 break;
219 235 }
220 236 graphChild->enableAcquisition(false);
221 237 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: Range before: ")
222 238 << graphChild->graphRange();
223 239 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: Range after : ")
224 240 << graphChildRange;
225 241 qCDebug(LOG_VisualizationZoneWidget())
226 242 << tr("TORM: child dt") << graphChildRange.m_TEnd - graphChildRange.m_TStart;
227 243 graphChild->setGraphRange(graphChildRange);
228 244 graphChild->enableAcquisition(true);
229 245 }
230 246 }
231 247 };
232 248
233 249 // connection for synchronization
234 250 connect(graphWidget, &VisualizationGraphWidget::synchronize, synchronizeZoneWidget);
235 251 connect(graphWidget, &VisualizationGraphWidget::variableAdded, this,
236 252 &VisualizationZoneWidget::onVariableAdded);
237 253 connect(graphWidget, &VisualizationGraphWidget::variableAboutToBeRemoved, this,
238 254 &VisualizationZoneWidget::onVariableAboutToBeRemoved);
239 255
240 256 auto range = SqpRange{};
241
242 // Apply visitor to graph children
243 auto layout = ui->dragDropContainer->layout();
244 if (layout->count() > 0) {
257 if (auto firstGraph = impl->firstGraph(this)) {
245 258 // Case of a new graph in a existant zone
246 if (auto visualizationGraphWidget
247 = dynamic_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
248 range = visualizationGraphWidget->graphRange();
249 }
259 range = firstGraph->graphRange();
250 260 }
251 261 else {
252 262 // Case of a new graph as the first of the zone
253 263 range = variable->range();
254 264 }
255 265
256 266 this->insertGraph(index, graphWidget);
257 267
258 268 graphWidget->addVariable(variable, range);
259 269
260 270 // get y using variable range
261 271 if (auto dataSeries = variable->dataSeries()) {
262 272 dataSeries->lockRead();
263 273 auto valuesBounds
264 274 = dataSeries->valuesBounds(variable->range().m_TStart, variable->range().m_TEnd);
265 275 auto end = dataSeries->cend();
266 276 if (valuesBounds.first != end && valuesBounds.second != end) {
267 277 auto rangeValue = [](const auto &value) { return std::isnan(value) ? 0. : value; };
268 278
269 279 auto minValue = rangeValue(valuesBounds.first->minValue());
270 280 auto maxValue = rangeValue(valuesBounds.second->maxValue());
271 281
272 282 graphWidget->setYRange(SqpRange{minValue, maxValue});
273 283 }
274 284 dataSeries->unlock();
275 285 }
276 286
277 287 return graphWidget;
278 288 }
279 289
280 290 VisualizationGraphWidget *
281 291 VisualizationZoneWidget::createGraph(const QList<std::shared_ptr<Variable> > variables, int index)
282 292 {
283 293 if (variables.isEmpty()) {
284 294 return nullptr;
285 295 }
286 296
287 297 auto graphWidget = createGraph(variables.first(), index);
288 298 for (auto variableIt = variables.cbegin() + 1; variableIt != variables.cend(); ++variableIt) {
289 299 graphWidget->addVariable(*variableIt, graphWidget->graphRange());
290 300 }
291 301
292 302 return graphWidget;
293 303 }
294 304
295 305 void VisualizationZoneWidget::accept(IVisualizationWidgetVisitor *visitor)
296 306 {
297 307 if (visitor) {
298 308 visitor->visitEnter(this);
299 309
300 310 // Apply visitor to graph children: widgets different from graphs are not visited (no
301 311 // action)
302 312 processGraphs(
303 313 *ui->dragDropContainer->layout(),
304 314 [visitor](VisualizationGraphWidget &graphWidget) { graphWidget.accept(visitor); });
305 315
306 316 visitor->visitLeave(this);
307 317 }
308 318 else {
309 319 qCCritical(LOG_VisualizationZoneWidget()) << tr("Can't visit widget : the visitor is null");
310 320 }
311 321 }
312 322
313 323 bool VisualizationZoneWidget::canDrop(const Variable &variable) const
314 324 {
315 325 // A tab can always accomodate a variable
316 326 Q_UNUSED(variable);
317 327 return true;
318 328 }
319 329
320 330 bool VisualizationZoneWidget::contains(const Variable &variable) const
321 331 {
322 332 Q_UNUSED(variable);
323 333 return false;
324 334 }
325 335
326 336 QString VisualizationZoneWidget::name() const
327 337 {
328 338 return ui->zoneNameLabel->text();
329 339 }
330 340
331 341 QMimeData *VisualizationZoneWidget::mimeData() const
332 342 {
333 343 auto mimeData = new QMimeData;
334 344 mimeData->setData(MIME_TYPE_ZONE, QByteArray{});
335 345
346 if (const auto firstGraph = impl->firstGraph(this)) {
347 auto timeRangeData = TimeController::mimeDataForTimeRange(firstGraph->graphRange());
348 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
349 }
350
336 351 return mimeData;
337 352 }
338 353
339 354 bool VisualizationZoneWidget::isDragAllowed() const
340 355 {
341 356 return true;
342 357 }
343 358
344 359 void VisualizationZoneWidget::closeEvent(QCloseEvent *event)
345 360 {
346 361 // Closes graphs in the zone
347 362 processGraphs(*ui->dragDropContainer->layout(),
348 363 [](VisualizationGraphWidget &graphWidget) { graphWidget.close(); });
349 364
350 365 // Delete synchronization group from variable controller
351 366 QMetaObject::invokeMethod(&sqpApp->variableController(), "onRemoveSynchronizationGroupId",
352 367 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
353 368
354 369 QWidget::closeEvent(event);
355 370 }
356 371
357 372 void VisualizationZoneWidget::onVariableAdded(std::shared_ptr<Variable> variable)
358 373 {
359 374 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronized",
360 375 Qt::QueuedConnection, Q_ARG(std::shared_ptr<Variable>, variable),
361 376 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
362 377 }
363 378
364 379 void VisualizationZoneWidget::onVariableAboutToBeRemoved(std::shared_ptr<Variable> variable)
365 380 {
366 381 QMetaObject::invokeMethod(&sqpApp->variableController(), "desynchronize", Qt::QueuedConnection,
367 382 Q_ARG(std::shared_ptr<Variable>, variable),
368 383 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
369 384 }
370 385
371 386 void VisualizationZoneWidget::dropMimeData(int index, const QMimeData *mimeData)
372 387 {
373 388 if (mimeData->hasFormat(MIME_TYPE_GRAPH)) {
374 389 impl->dropGraph(index, this);
375 390 }
376 391 else if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
377 392 auto variables = sqpApp->variableController().variablesForMimeData(
378 393 mimeData->data(MIME_TYPE_VARIABLE_LIST));
379 394 impl->dropVariables(variables, index, this);
380 395 }
381 396 else {
382 397 qCWarning(LOG_VisualizationZoneWidget())
383 398 << tr("VisualizationZoneWidget::dropMimeData, unknown MIME data received.");
384 399 }
385 400 }
386 401
387 402 void VisualizationZoneWidget::dropMimeDataOnGraph(VisualizationDragWidget *dragWidget,
388 403 const QMimeData *mimeData)
389 404 {
390 405 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(dragWidget);
391 406 if (!graphWidget) {
392 407 qCWarning(LOG_VisualizationZoneWidget())
393 408 << tr("VisualizationZoneWidget::dropMimeDataOnGraph, dropping in an unknown widget, "
394 409 "drop aborted");
395 410 Q_ASSERT(false);
396 411 return;
397 412 }
398 413
399 414 if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
400 415 auto variables = sqpApp->variableController().variablesForMimeData(
401 416 mimeData->data(MIME_TYPE_VARIABLE_LIST));
402 417 for (const auto &var : variables) {
403 418 graphWidget->addVariable(var, graphWidget->graphRange());
404 419 }
405 420 }
406 421 else {
407 422 qCWarning(LOG_VisualizationZoneWidget())
408 423 << tr("VisualizationZoneWidget::dropMimeDataOnGraph, unknown MIME data received.");
409 424 }
410 425 }
411 426
412 427 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropGraph(
413 428 int index, VisualizationZoneWidget *zoneWidget)
414 429 {
415 430 auto &helper = sqpApp->dragDropHelper();
416 431
417 432 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(helper.getCurrentDragWidget());
418 433 if (!graphWidget) {
419 434 qCWarning(LOG_VisualizationZoneWidget())
420 435 << tr("VisualizationZoneWidget::dropGraph, drop aborted, the dropped graph is not "
421 436 "found or invalid.");
422 437 Q_ASSERT(false);
423 438 return;
424 439 }
425 440
426 441 auto parentDragDropContainer
427 442 = qobject_cast<VisualizationDragDropContainer *>(graphWidget->parentWidget());
428 443 if (!parentDragDropContainer) {
429 444 qCWarning(LOG_VisualizationZoneWidget())
430 445 << tr("VisualizationZoneWidget::dropGraph, drop aborted, the parent container of "
431 446 "the dropped graph is not found.");
432 447 Q_ASSERT(false);
433 448 return;
434 449 }
435 450
436 451 const auto &variables = graphWidget->variables();
437 452
438 453 if (parentDragDropContainer != zoneWidget->ui->dragDropContainer && !variables.isEmpty()) {
439 454 // The drop didn't occur in the same zone
440 455
441 456 // Abort the requests for the variables (if any)
442 457 // Commented, because it's not sure if it's needed or not
443 458 // for (const auto& var : variables)
444 459 //{
445 460 // sqpApp->variableController().onAbortProgressRequested(var);
446 461 //}
447 462
448 463 auto previousParentZoneWidget = graphWidget->parentZoneWidget();
449 464 auto nbGraph = parentDragDropContainer->countDragWidget();
450 465 if (nbGraph == 1) {
451 466 // This is the only graph in the previous zone, close the zone
452 467 previousParentZoneWidget->close();
453 468 }
454 469 else {
455 470 // Close the graph
456 471 graphWidget->close();
457 472 }
458 473
459 474 // Creates the new graph in the zone
460 475 zoneWidget->createGraph(variables, index);
461 476 }
462 477 else {
463 478 // The drop occurred in the same zone or the graph is empty
464 479 // Simple move of the graph, no variable operation associated
465 480 parentDragDropContainer->layout()->removeWidget(graphWidget);
466 481
467 482 if (variables.isEmpty() && parentDragDropContainer != zoneWidget->ui->dragDropContainer) {
468 483 // The graph is empty and dropped in a different zone.
469 484 // Take the range of the first graph in the zone (if existing).
470 485 auto layout = zoneWidget->ui->dragDropContainer->layout();
471 486 if (layout->count() > 0) {
472 487 if (auto visualizationGraphWidget
473 488 = qobject_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
474 489 graphWidget->setGraphRange(visualizationGraphWidget->graphRange());
475 490 }
476 491 }
477 492 }
478 493
479 494 zoneWidget->ui->dragDropContainer->insertDragWidget(index, graphWidget);
480 495 }
481 496 }
482 497
483 498 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropVariables(
484 499 const QList<std::shared_ptr<Variable> > &variables, int index,
485 500 VisualizationZoneWidget *zoneWidget)
486 501 {
487 502 // Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and
488 503 // compatible variable here
489 504 if (variables.count() > 1) {
490 505 qCWarning(LOG_VisualizationZoneWidget())
491 506 << tr("VisualizationZoneWidget::dropVariables, dropping multiple variables, operation "
492 507 "aborted.");
493 508 return;
494 509 }
495 510
496 511 zoneWidget->createGraph(variables, index);
497 512 }
@@ -1,98 +1,104
1 1 <?xml version="1.0" encoding="UTF-8"?>
2 2 <ui version="4.0">
3 3 <class>TimeWidget</class>
4 4 <widget class="QWidget" name="TimeWidget">
5 5 <property name="geometry">
6 6 <rect>
7 7 <x>0</x>
8 8 <y>0</y>
9 9 <width>716</width>
10 10 <height>48</height>
11 11 </rect>
12 12 </property>
13 13 <property name="sizePolicy">
14 14 <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
15 15 <horstretch>0</horstretch>
16 16 <verstretch>0</verstretch>
17 17 </sizepolicy>
18 18 </property>
19 <property name="acceptDrops">
20 <bool>true</bool>
21 </property>
19 22 <property name="windowTitle">
20 23 <string>Form</string>
21 24 </property>
25 <property name="styleSheet">
26 <string notr="true">b</string>
27 </property>
22 28 <layout class="QHBoxLayout" name="horizontalLayout_2">
23 29 <item>
24 30 <widget class="QLabel" name="label">
25 31 <property name="sizePolicy">
26 32 <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
27 33 <horstretch>0</horstretch>
28 34 <verstretch>0</verstretch>
29 35 </sizepolicy>
30 36 </property>
31 37 <property name="text">
32 38 <string>TStart :</string>
33 39 </property>
34 40 </widget>
35 41 </item>
36 42 <item>
37 43 <widget class="QDateTimeEdit" name="startDateTimeEdit">
38 44 <property name="sizePolicy">
39 45 <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
40 46 <horstretch>0</horstretch>
41 47 <verstretch>0</verstretch>
42 48 </sizepolicy>
43 49 </property>
44 50 <property name="displayFormat">
45 51 <string>dd/MM/yyyy HH:mm:ss:zzz</string>
46 52 </property>
47 53 <property name="calendarPopup">
48 54 <bool>true</bool>
49 55 </property>
50 56 <property name="timeSpec">
51 57 <enum>Qt::UTC</enum>
52 58 </property>
53 59 </widget>
54 60 </item>
55 61 <item>
56 62 <widget class="QLabel" name="label_2">
57 63 <property name="sizePolicy">
58 64 <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
59 65 <horstretch>0</horstretch>
60 66 <verstretch>0</verstretch>
61 67 </sizepolicy>
62 68 </property>
63 69 <property name="text">
64 70 <string>TEnd :</string>
65 71 </property>
66 72 </widget>
67 73 </item>
68 74 <item>
69 75 <widget class="QDateTimeEdit" name="endDateTimeEdit">
70 76 <property name="sizePolicy">
71 77 <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
72 78 <horstretch>0</horstretch>
73 79 <verstretch>0</verstretch>
74 80 </sizepolicy>
75 81 </property>
76 82 <property name="displayFormat">
77 83 <string>dd/MM/yyyy HH:mm:ss:zzz</string>
78 84 </property>
79 85 <property name="calendarPopup">
80 86 <bool>true</bool>
81 87 </property>
82 88 <property name="timeSpec">
83 89 <enum>Qt::UTC</enum>
84 90 </property>
85 91 </widget>
86 92 </item>
87 93 <item>
88 94 <widget class="QToolButton" name="applyToolButton">
89 95 <property name="text">
90 96 <string>...</string>
91 97 </property>
92 98 </widget>
93 99 </item>
94 100 </layout>
95 101 </widget>
96 102 <resources/>
97 103 <connections/>
98 104 </ui>
General Comments 0
You need to be logged in to leave comments. Login now