##// END OF EJS Templates
Drop of product in variables
trabillard -
r870:e6294435151a
parent child
Show More
@@ -1,97 +1,99
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 67 QByteArray mimeDataForProductsData(const QVariantList &productsData) const;
68 68 QVariantList productsDataForMimeData(const QByteArray &mimeData) const;
69 69
70 70 public slots:
71 71 /// Manage init/end of the controller
72 72 void initialize();
73 73 void finalize();
74 74
75 void requestVariable(const QVariantHash &productData);
76
75 77 signals:
76 78 /// Signal emitted when a structure has been set for a data source
77 79 void dataSourceItemSet(DataSourceItem *dataSourceItem);
78 80
79 81 /**
80 82 * Signal emitted when a variable creation is asked for a product
81 83 * @param variableName the name of the variable
82 84 * @param variableMetadata the metadata of the variable
83 85 * @param variableProvider the provider that will be used to retrieve the data of the variable
84 86 * (can be null)
85 87 */
86 88 void variableCreationRequested(const QString &variableName,
87 89 const QVariantHash &variableMetadata,
88 90 std::shared_ptr<IDataProvider> variableProvider);
89 91
90 92 private:
91 93 void waitForFinish();
92 94
93 95 class DataSourceControllerPrivate;
94 96 spimpl::unique_impl_ptr<DataSourceControllerPrivate> impl;
95 97 };
96 98
97 99 #endif // SCIQLOP_DATASOURCECONTROLLER_H
@@ -1,102 +1,110
1 1 #ifndef SCIQLOP_DATASOURCEITEM_H
2 2 #define SCIQLOP_DATASOURCEITEM_H
3 3
4 4 #include "CoreGlobal.h"
5 5
6 6 #include <Common/spimpl.h>
7 7
8 8 #include <QVariant>
9 9 #include <QVector>
10 10
11 11 class DataSourceItemAction;
12 12
13 13 /**
14 14 * Possible types of an item
15 15 */
16 16 enum class DataSourceItemType { NODE, PRODUCT, COMPONENT };
17 17
18 18 /**
19 19 * @brief The DataSourceItem class aims to represent a structure element of a data source.
20 20 * A data source has a tree structure that is made up of a main DataSourceItem object (root)
21 21 * containing other DataSourceItem objects (children).
22 22 * For each DataSourceItem can be associated a set of data representing it.
23 23 */
24 24 class SCIQLOP_CORE_EXPORT DataSourceItem {
25 25 public:
26 26 /// Key associated with the name of the item
27 27 static const QString NAME_DATA_KEY;
28 28
29 29 explicit DataSourceItem(DataSourceItemType type, const QString &name);
30 30 explicit DataSourceItem(DataSourceItemType type, QVariantHash data = {});
31 31
32 32 /// @return the actions of the item as a vector
33 33 QVector<DataSourceItemAction *> actions() const noexcept;
34 34
35 35 /**
36 36 * Adds an action to the item. The item takes ownership of the action, and the action is
37 37 * automatically associated to the item
38 38 * @param action the action to add
39 39 */
40 40 void addAction(std::unique_ptr<DataSourceItemAction> action) noexcept;
41 41
42 42 /**
43 43 * Adds a child to the item. The item takes ownership of the child.
44 44 * @param child the child to add
45 45 */
46 46 void appendChild(std::unique_ptr<DataSourceItem> child) noexcept;
47 47
48 48 /**
49 49 * Returns the item's child associated to an index
50 50 * @param childIndex the index to search
51 51 * @return a pointer to the child if index is valid, nullptr otherwise
52 52 */
53 53 DataSourceItem *child(int childIndex) const noexcept;
54 54
55 55 int childCount() const noexcept;
56 56
57 57 /**
58 58 * Get the data associated to a key
59 59 * @param key the key to search
60 60 * @return the data found if key is valid, default QVariant otherwise
61 61 */
62 62 QVariant data(const QString &key) const noexcept;
63 63
64 64 /// Gets all data
65 65 QVariantHash data() const noexcept;
66 66
67 67 bool isRoot() const noexcept;
68 68
69 69 QString name() const noexcept;
70 70
71 71 /**
72 72 * Get the item's parent
73 73 * @return a pointer to the parent if it exists, nullptr if the item is a root
74 74 */
75 75 DataSourceItem *parentItem() const noexcept;
76 76
77 77 /**
78 78 * Gets the item's root
79 79 * @return the top parent, the item itself if it's the root item
80 80 */
81 81 const DataSourceItem &rootItem() const noexcept;
82 82
83 83 /**
84 84 * Sets or appends a value to a key
85 85 * @param key the key
86 86 * @param value the value
87 87 * @param append if true, the value is added to the values already existing for the key,
88 88 * otherwise it replaces the existing values
89 89 */
90 90 void setData(const QString &key, const QVariant &value, bool append = false) noexcept;
91 91
92 92 DataSourceItemType type() const noexcept;
93 93
94 /**
95 * @brief Searches the first child matching the specified data.
96 * @param data The data to search.
97 * @param recursive So the search recursively.
98 * @return the item matching the data or nullptr if it was not found.
99 */
100 DataSourceItem *findItem(const QVariantHash &data, bool recursive);
101
94 102 bool operator==(const DataSourceItem &other);
95 103 bool operator!=(const DataSourceItem &other);
96 104
97 105 private:
98 106 class DataSourceItemPrivate;
99 107 spimpl::unique_impl_ptr<DataSourceItemPrivate> impl;
100 108 };
101 109
102 110 #endif // SCIQLOP_DATASOURCEITEMMODEL_H
@@ -1,109 +1,110
1 1 #ifndef SCIQLOP_VARIABLEMODEL_H
2 2 #define SCIQLOP_VARIABLEMODEL_H
3 3
4 4 #include "CoreGlobal.h"
5 5
6 6 #include <Data/SqpRange.h>
7 7
8 8 #include <QAbstractTableModel>
9 9 #include <QLoggingCategory>
10 10
11 11 #include <Common/MetaTypes.h>
12 12 #include <Common/spimpl.h>
13 13
14 14 Q_DECLARE_LOGGING_CATEGORY(LOG_VariableModel)
15 15
16 16 enum VariableRoles { ProgressRole = Qt::UserRole };
17 17
18 18
19 19 class IDataSeries;
20 20 class Variable;
21 21 class VariableController;
22 22
23 23 /**
24 24 * @brief The VariableModel class aims to hold the variables that have been created in SciQlop
25 25 */
26 26 class SCIQLOP_CORE_EXPORT VariableModel : public QAbstractTableModel {
27 27 Q_OBJECT
28 28 public:
29 29 explicit VariableModel(VariableController *parent = nullptr);
30 30
31 31 /**
32 32 * Adds an existing variable in the model.
33 33 * @param variable the variable to add.
34 34 * @remarks the variable's name is modified to avoid name duplicates
35 35 * @remarks this method does nothing if the variable already exists in the model
36 36 */
37 37 void addVariable(std::shared_ptr<Variable> variable) noexcept;
38 38
39 39 /**
40 40 * Checks that a variable is contained in the model
41 41 * @param variable the variable to check
42 42 * @return true if the variable is in the model, false otherwise
43 43 */
44 44 bool containsVariable(std::shared_ptr<Variable> variable) const noexcept;
45 45
46 46 /**
47 47 * Creates a new variable in the model
48 48 * @param name the name of the new variable
49 49 * @param metadata the metadata associated to the new variable
50 50 * @return the pointer to the new variable
51 51 */
52 52 std::shared_ptr<Variable> createVariable(const QString &name,
53 53 const QVariantHash &metadata) noexcept;
54 54
55 55 /**
56 56 * Deletes a variable from the model, if it exists
57 57 * @param variable the variable to delete
58 58 */
59 59 void deleteVariable(std::shared_ptr<Variable> variable) noexcept;
60 60
61 61
62 62 std::shared_ptr<Variable> variable(int index) const;
63 63 std::vector<std::shared_ptr<Variable> > variables() const;
64 64
65 65 void setDataProgress(std::shared_ptr<Variable> variable, double progress);
66 66
67 67
68 68 // /////////////////////////// //
69 69 // QAbstractTableModel methods //
70 70 // /////////////////////////// //
71 71
72 72 virtual int columnCount(const QModelIndex &parent = QModelIndex{}) const override;
73 73 virtual int rowCount(const QModelIndex &parent = QModelIndex{}) const override;
74 74 virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
75 75 virtual QVariant headerData(int section, Qt::Orientation orientation,
76 76 int role = Qt::DisplayRole) const override;
77 77 virtual Qt::ItemFlags flags(const QModelIndex &index) const override;
78 78
79 79 // ///////////////// //
80 80 // Drag&Drop methods //
81 81 // ///////////////// //
82 82
83 83 virtual Qt::DropActions supportedDropActions() const override;
84 84 virtual Qt::DropActions supportedDragActions() const override;
85 85 virtual QStringList mimeTypes() const override;
86 86 virtual QMimeData *mimeData(const QModelIndexList &indexes) const override;
87 87 virtual bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column,
88 88 const QModelIndex &parent) const override;
89 89 virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column,
90 90 const QModelIndex &parent) override;
91 91
92 92 void abortProgress(const QModelIndex &index);
93 93
94 94 signals:
95 95 void abortProgessRequested(std::shared_ptr<Variable> variable);
96 void requestVariable(const QVariantHash &productData);
96 97
97 98 private:
98 99 class VariableModelPrivate;
99 100 spimpl::unique_impl_ptr<VariableModelPrivate> impl;
100 101
101 102 private slots:
102 103 /// Slot called when data of a variable has been updated
103 104 void onVariableUpdated() noexcept;
104 105 };
105 106
106 107 // Registers QVector<int> metatype so it can be used in VariableModel::dataChanged() signal
107 108 SCIQLOP_REGISTER_META_TYPE(QVECTOR_INT_REGISTRY, QVector<int>)
108 109
109 110 #endif // SCIQLOP_VARIABLEMODEL_H
@@ -1,163 +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 #include <QDataStream>
9 10 #include <QDir>
10 11 #include <QStandardPaths>
11 12
12 13 Q_LOGGING_CATEGORY(LOG_DataSourceController, "DataSourceController")
13 14
14 15 namespace {
15 16
16 17 /**
17 18 * Builds the metadata of the variable that will be generated from the loading of an item
18 19 * @param dataSourceItem the data source item from which to generate the metadata
19 20 * @return the metadata of the variable
20 21 */
21 22 QVariantHash variableMetadata(const DataSourceItem &dataSourceItem)
22 23 {
23 24 // Variable metadata contains...
24 25
25 26 // ... all metadata of the item
26 27 auto result = dataSourceItem.data();
27 28
28 29 // ... and the name of the plugin, recovered from root item
29 30 result.insert(QStringLiteral("plugin"), dataSourceItem.rootItem().name());
30 31
31 32 return result;
32 33 }
33 34
34 35 } // namespace
35 36
36 37 class DataSourceController::DataSourceControllerPrivate {
37 38 public:
38 39 QMutex m_WorkingMutex;
39 40 /// Data sources registered
40 41 QHash<QUuid, QString> m_DataSources;
41 42 /// Data sources structures
42 43 std::map<QUuid, std::unique_ptr<DataSourceItem> > m_DataSourceItems;
43 44 /// Data providers registered
44 45 /// @remarks Data providers are stored as shared_ptr as they can be sent to a variable and
45 46 /// continue to live without necessarily the data source controller
46 47 std::map<QUuid, std::shared_ptr<IDataProvider> > m_DataProviders;
48
49 // Search for the first datasource item matching the specified data
50 DataSourceItem *findDataSourceItem(const QVariantHash &data)
51 {
52 DataSourceItem *sourceItem = nullptr;
53 for (const auto &item : m_DataSourceItems) {
54 sourceItem = item.second->findItem(data, true);
55 if (sourceItem) {
56 break;
57 }
58 }
59
60 return sourceItem;
61 }
47 62 };
48 63
49 64 DataSourceController::DataSourceController(QObject *parent)
50 65 : impl{spimpl::make_unique_impl<DataSourceControllerPrivate>()}
51 66 {
52 67 qCDebug(LOG_DataSourceController()) << tr("DataSourceController construction")
53 68 << QThread::currentThread();
54 69 }
55 70
56 71 DataSourceController::~DataSourceController()
57 72 {
58 73 qCDebug(LOG_DataSourceController()) << tr("DataSourceController destruction")
59 74 << QThread::currentThread();
60 75 this->waitForFinish();
61 76 }
62 77
63 78 QUuid DataSourceController::registerDataSource(const QString &dataSourceName) noexcept
64 79 {
65 80 auto dataSourceUid = QUuid::createUuid();
66 81 impl->m_DataSources.insert(dataSourceUid, dataSourceName);
67 82
68 83 return dataSourceUid;
69 84 }
70 85
71 86 void DataSourceController::setDataSourceItem(
72 87 const QUuid &dataSourceUid, std::unique_ptr<DataSourceItem> dataSourceItem) noexcept
73 88 {
74 89 if (!dataSourceItem) {
75 90 qCWarning(LOG_DataSourceController())
76 91 << tr("Data source item can't be registered (null item)");
77 92 return;
78 93 }
79 94
80 95 if (impl->m_DataSources.contains(dataSourceUid)) {
81 96 // The data provider is implicitly converted to a shared_ptr
82 97 impl->m_DataSourceItems.insert(std::make_pair(dataSourceUid, std::move(dataSourceItem)));
83 98
84 99 // Retrieves the data source item to emit the signal with it
85 100 auto it = impl->m_DataSourceItems.find(dataSourceUid);
86 101 if (it != impl->m_DataSourceItems.end()) {
87 102 emit dataSourceItemSet(it->second.get());
88 103 }
89 104 }
90 105 else {
91 106 qCWarning(LOG_DataSourceController()) << tr("Can't set data source item for uid %1 : no "
92 107 "data source has been registered with the uid")
93 108 .arg(dataSourceUid.toString());
94 109 }
95 110 }
96 111
97 112 void DataSourceController::setDataProvider(const QUuid &dataSourceUid,
98 113 std::unique_ptr<IDataProvider> dataProvider) noexcept
99 114 {
100 115 if (impl->m_DataSources.contains(dataSourceUid)) {
101 116 impl->m_DataProviders.insert(std::make_pair(dataSourceUid, std::move(dataProvider)));
102 117 }
103 118 else {
104 119 qCWarning(LOG_DataSourceController()) << tr("Can't set data provider for uid %1 : no data "
105 120 "source has been registered with the uid")
106 121 .arg(dataSourceUid.toString());
107 122 }
108 123 }
109 124
110 125 void DataSourceController::loadProductItem(const QUuid &dataSourceUid,
111 126 const DataSourceItem &productItem) noexcept
112 127 {
113 128 if (productItem.type() == DataSourceItemType::PRODUCT
114 129 || productItem.type() == DataSourceItemType::COMPONENT) {
115 130 /// Retrieves the data provider of the data source (if any)
116 131 auto it = impl->m_DataProviders.find(dataSourceUid);
117 132 auto dataProvider = (it != impl->m_DataProviders.end()) ? it->second : nullptr;
118 133
119 134 emit variableCreationRequested(productItem.name(), variableMetadata(productItem),
120 135 dataProvider);
121 136 }
122 137 else {
123 138 qCWarning(LOG_DataSourceController()) << tr("Can't load an item that is not a product");
124 139 }
125 140 }
126 141
127 142 QByteArray DataSourceController::mimeDataForProductsData(const QVariantList &productsData) const
128 143 {
129 144 QByteArray encodedData;
130 145 QDataStream stream{&encodedData, QIODevice::WriteOnly};
131 146
132 147 stream << productsData;
133 148
134 149 return encodedData;
135 150 }
136 151
137 152 QVariantList DataSourceController::productsDataForMimeData(const QByteArray &mimeData) const
138 153 {
139 154 QDataStream stream{mimeData};
140 155
141 156 QVariantList productList;
142 157 stream >> productList;
143 158
144 159 return productList;
145 160 }
146 161
147 162 void DataSourceController::initialize()
148 163 {
149 164 qCDebug(LOG_DataSourceController()) << tr("DataSourceController init")
150 165 << QThread::currentThread();
151 166 impl->m_WorkingMutex.lock();
152 167 qCDebug(LOG_DataSourceController()) << tr("DataSourceController init END");
153 168 }
154 169
155 170 void DataSourceController::finalize()
156 171 {
157 172 impl->m_WorkingMutex.unlock();
158 173 }
159 174
175 void DataSourceController::requestVariable(const QVariantHash &productData)
176 {
177 DataSourceItem *sourceItem = impl->findDataSourceItem(productData);
178
179 if (sourceItem) {
180 auto sourceName = sourceItem->rootItem().name();
181 auto sourceId = impl->m_DataSources.key(sourceName);
182 loadProductItem(sourceId, *sourceItem);
183 }
184 else {
185 qCWarning(LOG_DataSourceController()) << tr("requestVariable, product data not found");
186 }
187 }
188
160 189 void DataSourceController::waitForFinish()
161 190 {
162 191 QMutexLocker locker{&impl->m_WorkingMutex};
163 192 }
@@ -1,145 +1,163
1 1 #include <DataSource/DataSourceItem.h>
2 2 #include <DataSource/DataSourceItemAction.h>
3 3
4 4 #include <QVector>
5 5
6 6 const QString DataSourceItem::NAME_DATA_KEY = QStringLiteral("name");
7 7
8 8 struct DataSourceItem::DataSourceItemPrivate {
9 9 explicit DataSourceItemPrivate(DataSourceItemType type, QVariantHash data)
10 10 : m_Parent{nullptr}, m_Children{}, m_Type{type}, m_Data{std::move(data)}, m_Actions{}
11 11 {
12 12 }
13 13
14 14 DataSourceItem *m_Parent;
15 15 std::vector<std::unique_ptr<DataSourceItem> > m_Children;
16 16 DataSourceItemType m_Type;
17 17 QVariantHash m_Data;
18 18 std::vector<std::unique_ptr<DataSourceItemAction> > m_Actions;
19 19 };
20 20
21 21 DataSourceItem::DataSourceItem(DataSourceItemType type, const QString &name)
22 22 : DataSourceItem{type, QVariantHash{{NAME_DATA_KEY, name}}}
23 23 {
24 24 }
25 25
26 26 DataSourceItem::DataSourceItem(DataSourceItemType type, QVariantHash data)
27 27 : impl{spimpl::make_unique_impl<DataSourceItemPrivate>(type, std::move(data))}
28 28 {
29 29 }
30 30
31 31 QVector<DataSourceItemAction *> DataSourceItem::actions() const noexcept
32 32 {
33 33 auto result = QVector<DataSourceItemAction *>{};
34 34
35 35 std::transform(std::cbegin(impl->m_Actions), std::cend(impl->m_Actions),
36 36 std::back_inserter(result), [](const auto &action) { return action.get(); });
37 37
38 38 return result;
39 39 }
40 40
41 41 void DataSourceItem::addAction(std::unique_ptr<DataSourceItemAction> action) noexcept
42 42 {
43 43 action->setDataSourceItem(this);
44 44 impl->m_Actions.push_back(std::move(action));
45 45 }
46 46
47 47 void DataSourceItem::appendChild(std::unique_ptr<DataSourceItem> child) noexcept
48 48 {
49 49 child->impl->m_Parent = this;
50 50 impl->m_Children.push_back(std::move(child));
51 51 }
52 52
53 53 DataSourceItem *DataSourceItem::child(int childIndex) const noexcept
54 54 {
55 55 if (childIndex < 0 || childIndex >= childCount()) {
56 56 return nullptr;
57 57 }
58 58 else {
59 59 return impl->m_Children.at(childIndex).get();
60 60 }
61 61 }
62 62
63 63 int DataSourceItem::childCount() const noexcept
64 64 {
65 65 return impl->m_Children.size();
66 66 }
67 67
68 68 QVariant DataSourceItem::data(const QString &key) const noexcept
69 69 {
70 70 return impl->m_Data.value(key);
71 71 }
72 72
73 73 QVariantHash DataSourceItem::data() const noexcept
74 74 {
75 75 return impl->m_Data;
76 76 }
77 77
78 78 bool DataSourceItem::isRoot() const noexcept
79 79 {
80 80 return impl->m_Parent == nullptr;
81 81 }
82 82
83 83 QString DataSourceItem::name() const noexcept
84 84 {
85 85 return data(NAME_DATA_KEY).toString();
86 86 }
87 87
88 88 DataSourceItem *DataSourceItem::parentItem() const noexcept
89 89 {
90 90 return impl->m_Parent;
91 91 }
92 92
93 93 const DataSourceItem &DataSourceItem::rootItem() const noexcept
94 94 {
95 95 return isRoot() ? *this : parentItem()->rootItem();
96 96 }
97 97
98 98 void DataSourceItem::setData(const QString &key, const QVariant &value, bool append) noexcept
99 99 {
100 100 auto it = impl->m_Data.constFind(key);
101 101 if (append && it != impl->m_Data.constEnd()) {
102 102 // Case of an existing value to which we want to add to the new value
103 103 if (it->canConvert<QVariantList>()) {
104 104 auto variantList = it->value<QVariantList>();
105 105 variantList.append(value);
106 106
107 107 impl->m_Data.insert(key, variantList);
108 108 }
109 109 else {
110 110 impl->m_Data.insert(key, QVariantList{*it, value});
111 111 }
112 112 }
113 113 else {
114 114 // Other cases :
115 115 // - new value in map OR
116 116 // - replacement of an existing value (not appending)
117 117 impl->m_Data.insert(key, value);
118 118 }
119 119 }
120 120
121 121 DataSourceItemType DataSourceItem::type() const noexcept
122 122 {
123 123 return impl->m_Type;
124 124 }
125 125
126 DataSourceItem *DataSourceItem::findItem(const QVariantHash &data, bool recursive)
127 {
128 for (const auto &child : impl->m_Children) {
129 if (child->impl->m_Data == data) {
130 return child.get();
131 }
132
133 if (recursive) {
134 auto foundItem = child->findItem(data, true);
135 if (foundItem) {
136 return foundItem;
137 }
138 }
139 }
140
141 return nullptr;
142 }
143
126 144 bool DataSourceItem::operator==(const DataSourceItem &other)
127 145 {
128 146 // Compares items' attributes
129 147 if (std::tie(impl->m_Type, impl->m_Data) == std::tie(other.impl->m_Type, other.impl->m_Data)) {
130 148 // Compares contents of items' children
131 149 return std::equal(std::cbegin(impl->m_Children), std::cend(impl->m_Children),
132 150 std::cbegin(other.impl->m_Children),
133 151 [](const auto &itemChild, const auto &otherChild) {
134 152 return *itemChild == *otherChild;
135 153 });
136 154 }
137 155 else {
138 156 return false;
139 157 }
140 158 }
141 159
142 160 bool DataSourceItem::operator!=(const DataSourceItem &other)
143 161 {
144 162 return !(*this == other);
145 163 }
@@ -1,351 +1,368
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 12 #include <QMimeData>
12 13 #include <QSize>
13 14 #include <unordered_map>
14 15
15 16 Q_LOGGING_CATEGORY(LOG_VariableModel, "VariableModel")
16 17
17 18 namespace {
18 19
19 20 // Column indexes
20 21 const auto NAME_COLUMN = 0;
21 22 const auto TSTART_COLUMN = 1;
22 23 const auto TEND_COLUMN = 2;
23 24 const auto NBPOINTS_COLUMN = 3;
24 25 const auto UNIT_COLUMN = 4;
25 26 const auto MISSION_COLUMN = 5;
26 27 const auto PLUGIN_COLUMN = 6;
27 28 const auto NB_COLUMNS = 7;
28 29
29 30 // Column properties
30 31 const auto DEFAULT_HEIGHT = 25;
31 32 const auto DEFAULT_WIDTH = 100;
32 33
33 34 struct ColumnProperties {
34 35 ColumnProperties(const QString &name = {}, int width = DEFAULT_WIDTH,
35 36 int height = DEFAULT_HEIGHT)
36 37 : m_Name{name}, m_Width{width}, m_Height{height}
37 38 {
38 39 }
39 40
40 41 QString m_Name;
41 42 int m_Width;
42 43 int m_Height;
43 44 };
44 45
45 46 const auto COLUMN_PROPERTIES = QHash<int, ColumnProperties>{
46 47 {NAME_COLUMN, {QObject::tr("Name")}}, {TSTART_COLUMN, {QObject::tr("tStart"), 180}},
47 48 {TEND_COLUMN, {QObject::tr("tEnd"), 180}}, {NBPOINTS_COLUMN, {QObject::tr("Nb points")}},
48 49 {UNIT_COLUMN, {QObject::tr("Unit")}}, {MISSION_COLUMN, {QObject::tr("Mission")}},
49 50 {PLUGIN_COLUMN, {QObject::tr("Plugin")}}};
50 51
51 52 /// Format for datetimes
52 53 const auto DATETIME_FORMAT = QStringLiteral("dd/MM/yyyy \nhh:mm:ss:zzz");
53 54
54 55 QString uniqueName(const QString &defaultName,
55 56 const std::vector<std::shared_ptr<Variable> > &variables)
56 57 {
57 58 auto forbiddenNames = std::vector<QString>(variables.size());
58 59 std::transform(variables.cbegin(), variables.cend(), forbiddenNames.begin(),
59 60 [](const auto &variable) { return variable->name(); });
60 61 auto uniqueName = StringUtils::uniqueName(defaultName, forbiddenNames);
61 62 Q_ASSERT(!uniqueName.isEmpty());
62 63
63 64 return uniqueName;
64 65 }
65 66
66 67 } // namespace
67 68
68 69 struct VariableModel::VariableModelPrivate {
69 70 /// Variables created in SciQlop
70 71 std::vector<std::shared_ptr<Variable> > m_Variables;
71 72 std::unordered_map<std::shared_ptr<Variable>, double> m_VariableToProgress;
72 73 VariableController *m_VariableController;
73 74
74 75 /// Return the row index of the variable. -1 if it's not found
75 76 int indexOfVariable(Variable *variable) const noexcept;
76 77 };
77 78
78 79 VariableModel::VariableModel(VariableController *parent)
79 80 : QAbstractTableModel{parent}, impl{spimpl::make_unique_impl<VariableModelPrivate>()}
80 81 {
81 82 impl->m_VariableController = parent;
82 83 }
83 84
84 85 void VariableModel::addVariable(std::shared_ptr<Variable> variable) noexcept
85 86 {
86 87 auto insertIndex = rowCount();
87 88 beginInsertRows({}, insertIndex, insertIndex);
88 89
89 90 // Generates unique name for the variable
90 91 variable->setName(uniqueName(variable->name(), impl->m_Variables));
91 92
92 93 impl->m_Variables.push_back(variable);
93 94 connect(variable.get(), &Variable::updated, this, &VariableModel::onVariableUpdated);
94 95
95 96 endInsertRows();
96 97 }
97 98
98 99 bool VariableModel::containsVariable(std::shared_ptr<Variable> variable) const noexcept
99 100 {
100 101 auto end = impl->m_Variables.cend();
101 102 return std::find(impl->m_Variables.cbegin(), end, variable) != end;
102 103 }
103 104
104 105 std::shared_ptr<Variable> VariableModel::createVariable(const QString &name,
105 106 const QVariantHash &metadata) noexcept
106 107 {
107 108 auto variable = std::make_shared<Variable>(name, metadata);
108 109 addVariable(variable);
109 110
110 111 return variable;
111 112 }
112 113
113 114 void VariableModel::deleteVariable(std::shared_ptr<Variable> variable) noexcept
114 115 {
115 116 if (!variable) {
116 117 qCCritical(LOG_Variable()) << "Can't delete a null variable from the model";
117 118 return;
118 119 }
119 120
120 121 // Finds variable in the model
121 122 auto begin = impl->m_Variables.cbegin();
122 123 auto end = impl->m_Variables.cend();
123 124 auto it = std::find(begin, end, variable);
124 125 if (it != end) {
125 126 auto removeIndex = std::distance(begin, it);
126 127
127 128 // Deletes variable
128 129 beginRemoveRows({}, removeIndex, removeIndex);
129 130 impl->m_Variables.erase(it);
130 131 endRemoveRows();
131 132 }
132 133 else {
133 134 qCritical(LOG_VariableModel())
134 135 << tr("Can't delete variable %1 from the model: the variable is not in the model")
135 136 .arg(variable->name());
136 137 }
137 138
138 139 // Removes variable from progress map
139 140 impl->m_VariableToProgress.erase(variable);
140 141 }
141 142
142 143
143 144 std::shared_ptr<Variable> VariableModel::variable(int index) const
144 145 {
145 146 return (index >= 0 && index < impl->m_Variables.size()) ? impl->m_Variables[index] : nullptr;
146 147 }
147 148
148 149 std::vector<std::shared_ptr<Variable> > VariableModel::variables() const
149 150 {
150 151 return impl->m_Variables;
151 152 }
152 153
153 154 void VariableModel::setDataProgress(std::shared_ptr<Variable> variable, double progress)
154 155 {
155 156 if (progress > 0.0) {
156 157 impl->m_VariableToProgress[variable] = progress;
157 158 }
158 159 else {
159 160 impl->m_VariableToProgress.erase(variable);
160 161 }
161 162 auto modelIndex = createIndex(impl->indexOfVariable(variable.get()), NAME_COLUMN);
162 163
163 164 emit dataChanged(modelIndex, modelIndex);
164 165 }
165 166
166 167 int VariableModel::columnCount(const QModelIndex &parent) const
167 168 {
168 169 Q_UNUSED(parent);
169 170
170 171 return NB_COLUMNS;
171 172 }
172 173
173 174 int VariableModel::rowCount(const QModelIndex &parent) const
174 175 {
175 176 Q_UNUSED(parent);
176 177
177 178 return impl->m_Variables.size();
178 179 }
179 180
180 181 QVariant VariableModel::data(const QModelIndex &index, int role) const
181 182 {
182 183 if (!index.isValid()) {
183 184 return QVariant{};
184 185 }
185 186
186 187 if (index.row() < 0 || index.row() >= rowCount()) {
187 188 return QVariant{};
188 189 }
189 190
190 191 if (role == Qt::DisplayRole) {
191 192 if (auto variable = impl->m_Variables.at(index.row()).get()) {
192 193 switch (index.column()) {
193 194 case NAME_COLUMN:
194 195 return variable->name();
195 196 case TSTART_COLUMN: {
196 197 auto range = variable->realRange();
197 198 return range != INVALID_RANGE
198 199 ? DateUtils::dateTime(range.m_TStart).toString(DATETIME_FORMAT)
199 200 : QVariant{};
200 201 }
201 202 case TEND_COLUMN: {
202 203 auto range = variable->realRange();
203 204 return range != INVALID_RANGE
204 205 ? DateUtils::dateTime(range.m_TEnd).toString(DATETIME_FORMAT)
205 206 : QVariant{};
206 207 }
207 208 case NBPOINTS_COLUMN:
208 209 return variable->nbPoints();
209 210 case UNIT_COLUMN:
210 211 return variable->metadata().value(QStringLiteral("units"));
211 212 case MISSION_COLUMN:
212 213 return variable->metadata().value(QStringLiteral("mission"));
213 214 case PLUGIN_COLUMN:
214 215 return variable->metadata().value(QStringLiteral("plugin"));
215 216 default:
216 217 // No action
217 218 break;
218 219 }
219 220
220 221 qWarning(LOG_VariableModel())
221 222 << tr("Can't get data (unknown column %1)").arg(index.column());
222 223 }
223 224 else {
224 225 qWarning(LOG_VariableModel()) << tr("Can't get data (no variable)");
225 226 }
226 227 }
227 228 else if (role == VariableRoles::ProgressRole) {
228 229 if (auto variable = impl->m_Variables.at(index.row())) {
229 230
230 231 auto it = impl->m_VariableToProgress.find(variable);
231 232 if (it != impl->m_VariableToProgress.cend()) {
232 233 return it->second;
233 234 }
234 235 }
235 236 }
236 237
237 238 return QVariant{};
238 239 }
239 240
240 241 QVariant VariableModel::headerData(int section, Qt::Orientation orientation, int role) const
241 242 {
242 243 if (role != Qt::DisplayRole && role != Qt::SizeHintRole) {
243 244 return QVariant{};
244 245 }
245 246
246 247 if (orientation == Qt::Horizontal) {
247 248 auto propertiesIt = COLUMN_PROPERTIES.find(section);
248 249 if (propertiesIt != COLUMN_PROPERTIES.cend()) {
249 250 // Role is either DisplayRole or SizeHintRole
250 251 return (role == Qt::DisplayRole)
251 252 ? QVariant{propertiesIt->m_Name}
252 253 : QVariant{QSize{propertiesIt->m_Width, propertiesIt->m_Height}};
253 254 }
254 255 else {
255 256 qWarning(LOG_VariableModel())
256 257 << tr("Can't get header data (unknown column %1)").arg(section);
257 258 }
258 259 }
259 260
260 261 return QVariant{};
261 262 }
262 263
263 264 Qt::ItemFlags VariableModel::flags(const QModelIndex &index) const
264 265 {
265 266 return QAbstractTableModel::flags(index) | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
266 267 }
267 268
268 269 Qt::DropActions VariableModel::supportedDropActions() const
269 270 {
270 return Qt::MoveAction;
271 return Qt::CopyAction | Qt::MoveAction;
271 272 }
272 273
273 274 Qt::DropActions VariableModel::supportedDragActions() const
274 275 {
275 276 return Qt::CopyAction | Qt::MoveAction;
276 277 }
277 278
278 279 QStringList VariableModel::mimeTypes() const
279 280 {
280 281 return {MIME_TYPE_VARIABLE_LIST};
281 282 }
282 283
283 284 QMimeData *VariableModel::mimeData(const QModelIndexList &indexes) const
284 285 {
285 286 auto mimeData = new QMimeData;
286 287
287 288 QList<std::shared_ptr<Variable> > variableList;
288 289
289 290 for (const auto &index : indexes) {
290 291 if (index.column() == 0) { // only the first column
291 292 auto variable = impl->m_Variables.at(index.row());
292 293 if (variable.get() && index.isValid()) {
293 294 variableList << variable;
294 295 }
295 296 }
296 297 }
297 298
298 299 auto encodedData = impl->m_VariableController->mimeDataForVariables(variableList);
299 300 mimeData->setData(MIME_TYPE_VARIABLE_LIST, encodedData);
300 301
301 302 return mimeData;
302 303 }
303 304
304 305 bool VariableModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row,
305 306 int column, const QModelIndex &parent) const
306 307 {
307 return false;
308 // drop of a product
309 return data->hasFormat(MIME_TYPE_PRODUCT_LIST);
308 310 }
309 311
310 312 bool VariableModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column,
311 313 const QModelIndex &parent)
312 314 {
313 return false;
315 bool dropDone = false;
316
317 if (data->hasFormat(MIME_TYPE_PRODUCT_LIST)) {
318 QDataStream stream(data->data(MIME_TYPE_PRODUCT_LIST));
319
320 QVariantList productList;
321 stream >> productList;
322
323 for (auto metaData : productList) {
324 emit requestVariable(metaData.toHash());
325 }
326
327 dropDone = true;
328 }
329
330 return dropDone;
314 331 }
315 332
316 333 void VariableModel::abortProgress(const QModelIndex &index)
317 334 {
318 335 if (auto variable = impl->m_Variables.at(index.row())) {
319 336 emit abortProgessRequested(variable);
320 337 }
321 338 }
322 339
323 340 void VariableModel::onVariableUpdated() noexcept
324 341 {
325 342 // Finds variable that has been updated in the model
326 343 if (auto updatedVariable = dynamic_cast<Variable *>(sender())) {
327 344 auto updatedVariableIndex = impl->indexOfVariable(updatedVariable);
328 345
329 346 if (updatedVariableIndex > -1) {
330 347 emit dataChanged(createIndex(updatedVariableIndex, 0),
331 348 createIndex(updatedVariableIndex, columnCount() - 1));
332 349 }
333 350 }
334 351 }
335 352
336 353 int VariableModel::VariableModelPrivate::indexOfVariable(Variable *variable) const noexcept
337 354 {
338 355 auto begin = std::cbegin(m_Variables);
339 356 auto end = std::cend(m_Variables);
340 357 auto it
341 358 = std::find_if(begin, end, [variable](const auto &var) { return var.get() == variable; });
342 359
343 360 if (it != end) {
344 361 // Gets the index of the variable in the model: we assume here that views have the same
345 362 // order as the model
346 363 return std::distance(begin, it);
347 364 }
348 365 else {
349 366 return -1;
350 367 }
351 368 }
@@ -1,176 +1,185
1 1 #include <DataSource/DataSourceItem.h>
2 2 #include <DataSource/DataSourceItemAction.h>
3 3 #include <DataSource/DataSourceTreeWidgetItem.h>
4 4
5 5 #include <QAction>
6 6
7 7 Q_LOGGING_CATEGORY(LOG_DataSourceTreeWidgetItem, "DataSourceTreeWidgetItem")
8 8
9 9 namespace {
10 10
11 11 // Column indexes
12 12 const auto NAME_COLUMN = 0;
13 13
14 14 QIcon itemIcon(const DataSourceItem *dataSource)
15 15 {
16 16 if (dataSource) {
17 17 auto dataSourceType = dataSource->type();
18 18 switch (dataSourceType) {
19 19 case DataSourceItemType::NODE: {
20 20 return dataSource->isRoot() ? QIcon{":/icones/dataSourceRoot.png"}
21 21 : QIcon{":/icones/dataSourceNode.png"};
22 22 }
23 23 case DataSourceItemType::PRODUCT:
24 24 return QIcon{":/icones/dataSourceProduct.png"};
25 25 case DataSourceItemType::COMPONENT:
26 26 return QIcon{":/icones/dataSourceComponent.png"};
27 27 default:
28 28 // No action
29 29 break;
30 30 }
31 31
32 32 qCWarning(LOG_DataSourceTreeWidgetItem())
33 33 << QObject::tr("Can't set data source icon : unknown data source type");
34 34 }
35 35 else {
36 36 qCCritical(LOG_DataSourceTreeWidgetItem())
37 37 << QObject::tr("Can't set data source icon : the data source is null");
38 38 }
39 39
40 40 // Default cases
41 41 return QIcon{};
42 42 }
43 43
44 44 /// @return the tooltip text for a variant. The text depends on whether the data is a simple variant
45 45 /// or a list of variants
46 46 QString tooltipValue(const QVariant &variant) noexcept
47 47 {
48 48 // If the variant is a list of variants, the text of the tooltip is of the form: {val1, val2,
49 49 // ...}
50 50 if (variant.canConvert<QVariantList>()) {
51 51 auto valueString = QStringLiteral("{");
52 52
53 53 auto variantList = variant.value<QVariantList>();
54 54 for (auto it = variantList.cbegin(), end = variantList.cend(); it != end; ++it) {
55 55 valueString.append(it->toString());
56 56
57 57 if (std::distance(it, end) != 1) {
58 58 valueString.append(", ");
59 59 }
60 60 }
61 61
62 62 valueString.append(QStringLiteral("}"));
63 63
64 64 return valueString;
65 65 }
66 66 else {
67 67 return variant.toString();
68 68 }
69 69 }
70 70
71 71 QString itemTooltip(const DataSourceItem *dataSource) noexcept
72 72 {
73 73 // The tooltip displays all item's data
74 74 if (dataSource) {
75 75 auto result = QString{};
76 76
77 77 const auto &data = dataSource->data();
78 78 for (auto it = data.cbegin(), end = data.cend(); it != end; ++it) {
79 79 result.append(QString{"<b>%1:</b> %2<br/>"}.arg(it.key(), tooltipValue(it.value())));
80 80 }
81 81
82 82 return result;
83 83 }
84 84 else {
85 85 qCCritical(LOG_DataSourceTreeWidgetItem())
86 86 << QObject::tr("Can't set data source tooltip : the data source is null");
87 87
88 88 return QString{};
89 89 }
90 90 }
91 91
92 92 } // namespace
93 93
94 94 struct DataSourceTreeWidgetItem::DataSourceTreeWidgetItemPrivate {
95 95 explicit DataSourceTreeWidgetItemPrivate(const DataSourceItem *data) : m_Data{data} {}
96 96
97 97 /// Model used to retrieve data source information
98 98 const DataSourceItem *m_Data;
99 99 /// Actions associated to the item. The parent of the item (QTreeWidget) takes the ownership of
100 100 /// the actions
101 101 QList<QAction *> m_Actions;
102 102 };
103 103
104 104 DataSourceTreeWidgetItem::DataSourceTreeWidgetItem(const DataSourceItem *data, int type)
105 105 : DataSourceTreeWidgetItem{nullptr, data, type}
106 106 {
107 107 }
108 108
109 109 DataSourceTreeWidgetItem::DataSourceTreeWidgetItem(QTreeWidget *parent, const DataSourceItem *data,
110 110 int type)
111 111 : QTreeWidgetItem{parent, type},
112 112 impl{spimpl::make_unique_impl<DataSourceTreeWidgetItemPrivate>(data)}
113 113 {
114 114 // Sets the icon and the tooltip depending on the data source
115 115 setIcon(0, itemIcon(impl->m_Data));
116 116 setToolTip(0, itemTooltip(impl->m_Data));
117 117
118 118 // Generates tree actions based on the item actions
119 119 auto createTreeAction = [this, &parent](const auto &itemAction) {
120 120 auto treeAction = new QAction{itemAction->name(), parent};
121 121
122 122 // Executes item action when tree action is triggered
123 123 QObject::connect(treeAction, &QAction::triggered, itemAction,
124 124 &DataSourceItemAction::execute);
125 125
126 126 return treeAction;
127 127 };
128 128
129 129 auto itemActions = impl->m_Data->actions();
130 130 std::transform(std::cbegin(itemActions), std::cend(itemActions),
131 131 std::back_inserter(impl->m_Actions), createTreeAction);
132
133 // Sets the flags of the items
134 auto flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
135 if (data->type() == DataSourceItemType::COMPONENT
136 || data->type() == DataSourceItemType::PRODUCT) {
137 flags |= Qt::ItemIsDragEnabled;
138 }
139
140 setFlags(flags);
132 141 }
133 142
134 143 const DataSourceItem *DataSourceTreeWidgetItem::data() const
135 144 {
136 145 return impl->m_Data;
137 146 }
138 147
139 148 QVariant DataSourceTreeWidgetItem::data(int column, int role) const
140 149 {
141 150 if (role == Qt::DisplayRole) {
142 151 if (impl->m_Data) {
143 152 switch (column) {
144 153 case NAME_COLUMN:
145 154 return impl->m_Data->name();
146 155 default:
147 156 // No action
148 157 break;
149 158 }
150 159
151 160 qCWarning(LOG_DataSourceTreeWidgetItem())
152 161 << QObject::tr("Can't get data (unknown column %1)").arg(column);
153 162 }
154 163 else {
155 164 qCCritical(LOG_DataSourceTreeWidgetItem()) << QObject::tr("Can't get data (null item)");
156 165 }
157 166
158 167 return QVariant{};
159 168 }
160 169 else {
161 170 return QTreeWidgetItem::data(column, role);
162 171 }
163 172 }
164 173
165 174 void DataSourceTreeWidgetItem::setData(int column, int role, const QVariant &value)
166 175 {
167 176 // Data can't be changed by edition
168 177 if (role != Qt::EditRole) {
169 178 QTreeWidgetItem::setData(column, role, value);
170 179 }
171 180 }
172 181
173 182 QList<QAction *> DataSourceTreeWidgetItem::actions() const noexcept
174 183 {
175 184 return impl->m_Actions;
176 185 }
@@ -1,159 +1,163
1 1 #include "SqpApplication.h"
2 2
3 3 #include <Data/IDataProvider.h>
4 4 #include <DataSource/DataSourceController.h>
5 5 #include <DragDropHelper.h>
6 6 #include <Network/NetworkController.h>
7 7 #include <QThread>
8 8 #include <Time/TimeController.h>
9 9 #include <Variable/Variable.h>
10 10 #include <Variable/VariableController.h>
11 #include <Variable/VariableModel.h>
11 12 #include <Visualization/VisualizationController.h>
12 13
13 14 Q_LOGGING_CATEGORY(LOG_SqpApplication, "SqpApplication")
14 15
15 16 class SqpApplication::SqpApplicationPrivate {
16 17 public:
17 18 SqpApplicationPrivate()
18 19 : m_DataSourceController{std::make_unique<DataSourceController>()},
19 20 m_NetworkController{std::make_unique<NetworkController>()},
20 21 m_TimeController{std::make_unique<TimeController>()},
21 22 m_VariableController{std::make_unique<VariableController>()},
22 23 m_VisualizationController{std::make_unique<VisualizationController>()},
23 24 m_DragDropHelper{std::make_unique<DragDropHelper>()}
24 25 {
25 26 // /////////////////////////////// //
26 27 // Connections between controllers //
27 28 // /////////////////////////////// //
28 29
29 30 // VariableController <-> DataSourceController
30 31 connect(m_DataSourceController.get(),
31 32 SIGNAL(variableCreationRequested(const QString &, const QVariantHash &,
32 33 std::shared_ptr<IDataProvider>)),
33 34 m_VariableController.get(),
34 35 SLOT(createVariable(const QString &, const QVariantHash &,
35 36 std::shared_ptr<IDataProvider>)));
36 37
38 connect(m_VariableController->variableModel(), &VariableModel::requestVariable,
39 m_DataSourceController.get(), &DataSourceController::requestVariable);
40
37 41 // VariableController <-> VisualizationController
38 42 connect(m_VariableController.get(),
39 43 SIGNAL(variableAboutToBeDeleted(std::shared_ptr<Variable>)),
40 44 m_VisualizationController.get(),
41 45 SIGNAL(variableAboutToBeDeleted(std::shared_ptr<Variable>)), Qt::DirectConnection);
42 46
43 47 connect(m_VariableController.get(),
44 48 SIGNAL(rangeChanged(std::shared_ptr<Variable>, const SqpRange &)),
45 49 m_VisualizationController.get(),
46 50 SIGNAL(rangeChanged(std::shared_ptr<Variable>, const SqpRange &)));
47 51
48 52
49 53 m_DataSourceController->moveToThread(&m_DataSourceControllerThread);
50 54 m_DataSourceControllerThread.setObjectName("DataSourceControllerThread");
51 55 m_NetworkController->moveToThread(&m_NetworkControllerThread);
52 56 m_NetworkControllerThread.setObjectName("NetworkControllerThread");
53 57 m_VariableController->moveToThread(&m_VariableControllerThread);
54 58 m_VariableControllerThread.setObjectName("VariableControllerThread");
55 59 m_VisualizationController->moveToThread(&m_VisualizationControllerThread);
56 60 m_VisualizationControllerThread.setObjectName("VsualizationControllerThread");
57 61
58 62
59 63 // Additionnal init
60 64 m_VariableController->setTimeController(m_TimeController.get());
61 65 }
62 66
63 67 virtual ~SqpApplicationPrivate()
64 68 {
65 69 m_DataSourceControllerThread.quit();
66 70 m_DataSourceControllerThread.wait();
67 71
68 72 m_NetworkControllerThread.quit();
69 73 m_NetworkControllerThread.wait();
70 74
71 75 m_VariableControllerThread.quit();
72 76 m_VariableControllerThread.wait();
73 77
74 78 m_VisualizationControllerThread.quit();
75 79 m_VisualizationControllerThread.wait();
76 80 }
77 81
78 82 std::unique_ptr<DataSourceController> m_DataSourceController;
79 83 std::unique_ptr<VariableController> m_VariableController;
80 84 std::unique_ptr<TimeController> m_TimeController;
81 85 std::unique_ptr<NetworkController> m_NetworkController;
82 86 std::unique_ptr<VisualizationController> m_VisualizationController;
83 87 QThread m_DataSourceControllerThread;
84 88 QThread m_NetworkControllerThread;
85 89 QThread m_VariableControllerThread;
86 90 QThread m_VisualizationControllerThread;
87 91
88 92 std::unique_ptr<DragDropHelper> m_DragDropHelper;
89 93 };
90 94
91 95
92 96 SqpApplication::SqpApplication(int &argc, char **argv)
93 97 : QApplication{argc, argv}, impl{spimpl::make_unique_impl<SqpApplicationPrivate>()}
94 98 {
95 99 qCDebug(LOG_SqpApplication()) << tr("SqpApplication construction") << QThread::currentThread();
96 100
97 101 connect(&impl->m_DataSourceControllerThread, &QThread::started,
98 102 impl->m_DataSourceController.get(), &DataSourceController::initialize);
99 103 connect(&impl->m_DataSourceControllerThread, &QThread::finished,
100 104 impl->m_DataSourceController.get(), &DataSourceController::finalize);
101 105
102 106 connect(&impl->m_NetworkControllerThread, &QThread::started, impl->m_NetworkController.get(),
103 107 &NetworkController::initialize);
104 108 connect(&impl->m_NetworkControllerThread, &QThread::finished, impl->m_NetworkController.get(),
105 109 &NetworkController::finalize);
106 110
107 111 connect(&impl->m_VariableControllerThread, &QThread::started, impl->m_VariableController.get(),
108 112 &VariableController::initialize);
109 113 connect(&impl->m_VariableControllerThread, &QThread::finished, impl->m_VariableController.get(),
110 114 &VariableController::finalize);
111 115
112 116 connect(&impl->m_VisualizationControllerThread, &QThread::started,
113 117 impl->m_VisualizationController.get(), &VisualizationController::initialize);
114 118 connect(&impl->m_VisualizationControllerThread, &QThread::finished,
115 119 impl->m_VisualizationController.get(), &VisualizationController::finalize);
116 120
117 121 impl->m_DataSourceControllerThread.start();
118 122 impl->m_NetworkControllerThread.start();
119 123 impl->m_VariableControllerThread.start();
120 124 impl->m_VisualizationControllerThread.start();
121 125 }
122 126
123 127 SqpApplication::~SqpApplication()
124 128 {
125 129 }
126 130
127 131 void SqpApplication::initialize()
128 132 {
129 133 }
130 134
131 135 DataSourceController &SqpApplication::dataSourceController() noexcept
132 136 {
133 137 return *impl->m_DataSourceController;
134 138 }
135 139
136 140 NetworkController &SqpApplication::networkController() noexcept
137 141 {
138 142 return *impl->m_NetworkController;
139 143 }
140 144
141 145 TimeController &SqpApplication::timeController() noexcept
142 146 {
143 147 return *impl->m_TimeController;
144 148 }
145 149
146 150 VariableController &SqpApplication::variableController() noexcept
147 151 {
148 152 return *impl->m_VariableController;
149 153 }
150 154
151 155 VisualizationController &SqpApplication::visualizationController() noexcept
152 156 {
153 157 return *impl->m_VisualizationController;
154 158 }
155 159
156 160 DragDropHelper &SqpApplication::dragDropHelper() noexcept
157 161 {
158 162 return *impl->m_DragDropHelper;
159 163 }
@@ -1,299 +1,300
1 1 #include "Visualization/VisualizationTabWidget.h"
2 2 #include "Visualization/IVisualizationWidgetVisitor.h"
3 3 #include "ui_VisualizationTabWidget.h"
4 4
5 5 #include "Visualization/VisualizationGraphWidget.h"
6 6 #include "Visualization/VisualizationZoneWidget.h"
7 7
8 8 #include "Variable/VariableController.h"
9 9
10 10 #include "Common/MimeTypesDef.h"
11 11
12 12 #include "DragDropHelper.h"
13 13 #include "SqpApplication.h"
14 14
15 15 Q_LOGGING_CATEGORY(LOG_VisualizationTabWidget, "VisualizationTabWidget")
16 16
17 17 namespace {
18 18
19 19 /// Generates a default name for a new zone, according to the number of zones already displayed in
20 20 /// the tab
21 21 QString defaultZoneName(const QLayout &layout)
22 22 {
23 23 auto count = 0;
24 24 for (auto i = 0; i < layout.count(); ++i) {
25 25 if (dynamic_cast<VisualizationZoneWidget *>(layout.itemAt(i)->widget())) {
26 26 count++;
27 27 }
28 28 }
29 29
30 30 return QObject::tr("Zone %1").arg(count + 1);
31 31 }
32 32
33 33 /**
34 34 * Applies a function to all zones of the tab represented by its layout
35 35 * @param layout the layout that contains zones
36 36 * @param fun the function to apply to each zone
37 37 */
38 38 template <typename Fun>
39 39 void processZones(QLayout &layout, Fun fun)
40 40 {
41 41 for (auto i = 0; i < layout.count(); ++i) {
42 42 if (auto item = layout.itemAt(i)) {
43 43 if (auto visualizationZoneWidget
44 44 = dynamic_cast<VisualizationZoneWidget *>(item->widget())) {
45 45 fun(*visualizationZoneWidget);
46 46 }
47 47 }
48 48 }
49 49 }
50 50
51 51 } // namespace
52 52
53 53 struct VisualizationTabWidget::VisualizationTabWidgetPrivate {
54 54 explicit VisualizationTabWidgetPrivate(const QString &name) : m_Name{name} {}
55 55
56 56 QString m_Name;
57 57
58 58 void dropGraph(int index, VisualizationTabWidget *tabWidget);
59 59 void dropZone(int index, VisualizationTabWidget *tabWidget);
60 60 void dropVariables(const QList<std::shared_ptr<Variable> > &variables, int index,
61 61 VisualizationTabWidget *tabWidget);
62 62 };
63 63
64 64 VisualizationTabWidget::VisualizationTabWidget(const QString &name, QWidget *parent)
65 65 : QWidget{parent},
66 66 ui{new Ui::VisualizationTabWidget},
67 67 impl{spimpl::make_unique_impl<VisualizationTabWidgetPrivate>(name)}
68 68 {
69 69 ui->setupUi(this);
70 70
71 71 ui->dragDropContainer->setAcceptedMimeTypes(
72 72 {MIME_TYPE_GRAPH, MIME_TYPE_ZONE, MIME_TYPE_VARIABLE_LIST});
73 73 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccured, this,
74 74 &VisualizationTabWidget::dropMimeData);
75 75 ui->dragDropContainer->setAcceptMimeDataFunction([this](auto mimeData) {
76 76 return sqpApp->dragDropHelper().checkMimeDataForVisualization(mimeData,
77 77 ui->dragDropContainer);
78 78 });
79 79 sqpApp->dragDropHelper().addDragDropScrollArea(ui->scrollArea);
80 80
81 81 // Widget is deleted when closed
82 82 setAttribute(Qt::WA_DeleteOnClose);
83 83 }
84 84
85 85 VisualizationTabWidget::~VisualizationTabWidget()
86 86 {
87 87 sqpApp->dragDropHelper().removeDragDropScrollArea(ui->scrollArea);
88 88 delete ui;
89 89 }
90 90
91 91 void VisualizationTabWidget::addZone(VisualizationZoneWidget *zoneWidget)
92 92 {
93 93 ui->dragDropContainer->addDragWidget(zoneWidget);
94 94 }
95 95
96 96 void VisualizationTabWidget::insertZone(int index, VisualizationZoneWidget *zoneWidget)
97 97 {
98 98 ui->dragDropContainer->insertDragWidget(index, zoneWidget);
99 99 }
100 100
101 101 VisualizationZoneWidget *VisualizationTabWidget::createZone(std::shared_ptr<Variable> variable)
102 102 {
103 103 return createZone({variable}, -1);
104 104 }
105 105
106 106 VisualizationZoneWidget *
107 107 VisualizationTabWidget::createZone(const QList<std::shared_ptr<Variable> > &variables, int index)
108 108 {
109 109 auto zoneWidget = createEmptyZone(index);
110 110
111 111 // Creates a new graph into the zone
112 112 zoneWidget->createGraph(variables, index);
113 113
114 114 return zoneWidget;
115 115 }
116 116
117 117 VisualizationZoneWidget *VisualizationTabWidget::createEmptyZone(int index)
118 118 {
119 119 auto zoneWidget
120 120 = new VisualizationZoneWidget{defaultZoneName(*ui->dragDropContainer->layout()), this};
121 121 this->insertZone(index, zoneWidget);
122 122
123 123 return zoneWidget;
124 124 }
125 125
126 126 void VisualizationTabWidget::accept(IVisualizationWidgetVisitor *visitor)
127 127 {
128 128 if (visitor) {
129 129 visitor->visitEnter(this);
130 130
131 131 // Apply visitor to zone children: widgets different from zones are not visited (no action)
132 132 processZones(tabLayout(), [visitor](VisualizationZoneWidget &zoneWidget) {
133 133 zoneWidget.accept(visitor);
134 134 });
135 135
136 136 visitor->visitLeave(this);
137 137 }
138 138 else {
139 139 qCCritical(LOG_VisualizationTabWidget()) << tr("Can't visit widget : the visitor is null");
140 140 }
141 141 }
142 142
143 143 bool VisualizationTabWidget::canDrop(const Variable &variable) const
144 144 {
145 145 // A tab can always accomodate a variable
146 146 Q_UNUSED(variable);
147 147 return true;
148 148 }
149 149
150 150 bool VisualizationTabWidget::contains(const Variable &variable) const
151 151 {
152 152 Q_UNUSED(variable);
153 153 return false;
154 154 }
155 155
156 156 QString VisualizationTabWidget::name() const
157 157 {
158 158 return impl->m_Name;
159 159 }
160 160
161 161 void VisualizationTabWidget::closeEvent(QCloseEvent *event)
162 162 {
163 163 // Closes zones in the tab
164 164 processZones(tabLayout(), [](VisualizationZoneWidget &zoneWidget) { zoneWidget.close(); });
165 165
166 166 QWidget::closeEvent(event);
167 167 }
168 168
169 169 QLayout &VisualizationTabWidget::tabLayout() const noexcept
170 170 {
171 171 return *ui->dragDropContainer->layout();
172 172 }
173 173
174 174 void VisualizationTabWidget::dropMimeData(int index, const QMimeData *mimeData)
175 175 {
176 176 if (mimeData->hasFormat(MIME_TYPE_GRAPH)) {
177 177 impl->dropGraph(index, this);
178 178 }
179 179 else if (mimeData->hasFormat(MIME_TYPE_ZONE)) {
180 180 impl->dropZone(index, this);
181 181 }
182 182 else if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
183 183 auto variables = sqpApp->variableController().variablesForMimeData(
184 184 mimeData->data(MIME_TYPE_VARIABLE_LIST));
185 185 impl->dropVariables(variables, index, this);
186 186 }
187 187 else {
188 188 qCWarning(LOG_VisualizationZoneWidget())
189 189 << tr("VisualizationTabWidget::dropMimeData, unknown MIME data received.");
190 190 }
191 191 }
192 192
193 193 void VisualizationTabWidget::VisualizationTabWidgetPrivate::dropGraph(
194 194 int index, VisualizationTabWidget *tabWidget)
195 195 {
196 196 auto &helper = sqpApp->dragDropHelper();
197 197
198 198 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(helper.getCurrentDragWidget());
199 199 if (!graphWidget) {
200 200 qCWarning(LOG_VisualizationZoneWidget())
201 201 << tr("VisualizationTabWidget::dropGraph, drop aborted, the dropped graph is not "
202 202 "found or invalid.");
203 203 Q_ASSERT(false);
204 204 return;
205 205 }
206 206
207 207 auto parentDragDropContainer
208 208 = qobject_cast<VisualizationDragDropContainer *>(graphWidget->parentWidget());
209 209 if (!parentDragDropContainer) {
210 210 qCWarning(LOG_VisualizationZoneWidget())
211 211 << tr("VisualizationTabWidget::dropGraph, drop aborted, the parent container of "
212 212 "the dropped graph is not found.");
213 213 Q_ASSERT(false);
214 214 return;
215 215 }
216 216
217 217 auto nbGraph = parentDragDropContainer->countDragWidget();
218 218
219 219 const auto &variables = graphWidget->variables();
220 220
221 221 if (!variables.isEmpty()) {
222 222 // Abort the requests for the variables (if any)
223 223 // Commented, because it's not sure if it's needed or not
224 224 // for (const auto& var : variables)
225 225 //{
226 226 // sqpApp->variableController().onAbortProgressRequested(var);
227 227 //}
228 228
229 229 if (nbGraph == 1) {
230 230 // This is the only graph in the previous zone, close the zone
231 231 graphWidget->parentZoneWidget()->close();
232 232 }
233 233 else {
234 234 // Close the graph
235 235 graphWidget->close();
236 236 }
237 237
238 238 tabWidget->createZone(variables, index);
239 239 }
240 240 else {
241 241 // The graph is empty, create an empty zone and move the graph inside
242 242
243 243 auto parentZoneWidget = graphWidget->parentZoneWidget();
244 244
245 245 parentDragDropContainer->layout()->removeWidget(graphWidget);
246 246
247 247 auto zoneWidget = tabWidget->createEmptyZone(index);
248 248 zoneWidget->addGraph(graphWidget);
249 249
250 250 // Close the old zone if it was the only graph inside
251 251 if (nbGraph == 1) {
252 252 parentZoneWidget->close();
253 253 }
254 254 }
255 255 }
256 256
257 257 void VisualizationTabWidget::VisualizationTabWidgetPrivate::dropZone(
258 258 int index, VisualizationTabWidget *tabWidget)
259 259 {
260 260 auto &helper = sqpApp->dragDropHelper();
261 261
262 262 auto zoneWidget = qobject_cast<VisualizationZoneWidget *>(helper.getCurrentDragWidget());
263 263 if (!zoneWidget) {
264 264 qCWarning(LOG_VisualizationZoneWidget())
265 265 << tr("VisualizationTabWidget::dropZone, drop aborted, the dropped zone is not "
266 266 "found or invalid.");
267 267 Q_ASSERT(false);
268 268 return;
269 269 }
270 270
271 271 auto parentDragDropContainer
272 272 = qobject_cast<VisualizationDragDropContainer *>(zoneWidget->parentWidget());
273 273 if (!parentDragDropContainer) {
274 274 qCWarning(LOG_VisualizationZoneWidget())
275 275 << tr("VisualizationTabWidget::dropZone, drop aborted, the parent container of "
276 276 "the dropped zone is not found.");
277 277 Q_ASSERT(false);
278 278 return;
279 279 }
280 280
281 281 // Simple move of the zone, no variable operation associated
282 282 parentDragDropContainer->layout()->removeWidget(zoneWidget);
283 283 tabWidget->ui->dragDropContainer->insertDragWidget(index, zoneWidget);
284 284 }
285 285
286 286 void VisualizationTabWidget::VisualizationTabWidgetPrivate::dropVariables(
287 287 const QList<std::shared_ptr<Variable> > &variables, int index,
288 288 VisualizationTabWidget *tabWidget)
289 289 {
290 290 // Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and
291 291 // compatible variable here
292 292 if (variables.count() > 1) {
293 293 qCWarning(LOG_VisualizationZoneWidget())
294 294 << tr("VisualizationTabWidget::dropVariables, dropping multiple variables, operation "
295 295 "aborted.");
296 296 return;
297 }
297 298
298 299 tabWidget->createZone(variables, index);
299 300 }
@@ -1,468 +1,468
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 13 #include <Variable/Variable.h>
14 14 #include <Variable/VariableController.h>
15 15
16 16 #include <Visualization/operations/FindVariableOperation.h>
17 17
18 18 #include <DragDropHelper.h>
19 19 #include <QUuid>
20 20 #include <SqpApplication.h>
21 21 #include <cmath>
22 22
23 23 #include <QLayout>
24 24
25 25 Q_LOGGING_CATEGORY(LOG_VisualizationZoneWidget, "VisualizationZoneWidget")
26 26
27 27 namespace {
28 28
29 29
30 30 /// Generates a default name for a new graph, according to the number of graphs already displayed in
31 31 /// the zone
32 32 QString defaultGraphName(const QLayout &layout)
33 33 {
34 34 auto count = 0;
35 35 for (auto i = 0; i < layout.count(); ++i) {
36 36 if (dynamic_cast<VisualizationGraphWidget *>(layout.itemAt(i)->widget())) {
37 37 count++;
38 38 }
39 39 }
40 40
41 41 return QObject::tr("Graph %1").arg(count + 1);
42 42 }
43 43
44 44 /**
45 45 * Applies a function to all graphs of the zone represented by its layout
46 46 * @param layout the layout that contains graphs
47 47 * @param fun the function to apply to each graph
48 48 */
49 49 template <typename Fun>
50 50 void processGraphs(QLayout &layout, Fun fun)
51 51 {
52 52 for (auto i = 0; i < layout.count(); ++i) {
53 53 if (auto item = layout.itemAt(i)) {
54 54 if (auto visualizationGraphWidget
55 55 = dynamic_cast<VisualizationGraphWidget *>(item->widget())) {
56 56 fun(*visualizationGraphWidget);
57 57 }
58 58 }
59 59 }
60 60 }
61 61
62 62 } // namespace
63 63
64 64 struct VisualizationZoneWidget::VisualizationZoneWidgetPrivate {
65 65
66 66 explicit VisualizationZoneWidgetPrivate()
67 67 : m_SynchronisationGroupId{QUuid::createUuid()},
68 68 m_Synchronizer{std::make_unique<QCustomPlotSynchronizer>()}
69 69 {
70 70 }
71 71 QUuid m_SynchronisationGroupId;
72 72 std::unique_ptr<IGraphSynchronizer> m_Synchronizer;
73 73
74 74 void dropGraph(int index, VisualizationZoneWidget *zoneWidget);
75 75 void dropVariables(const QList<std::shared_ptr<Variable> > &variables, int index,
76 76 VisualizationZoneWidget *zoneWidget);
77 77 };
78 78
79 79 VisualizationZoneWidget::VisualizationZoneWidget(const QString &name, QWidget *parent)
80 80 : VisualizationDragWidget{parent},
81 81 ui{new Ui::VisualizationZoneWidget},
82 82 impl{spimpl::make_unique_impl<VisualizationZoneWidgetPrivate>()}
83 83 {
84 84 ui->setupUi(this);
85 85
86 86 ui->zoneNameLabel->setText(name);
87 87
88 88 ui->dragDropContainer->setAcceptedMimeTypes({MIME_TYPE_GRAPH, MIME_TYPE_VARIABLE_LIST});
89 89 ui->dragDropContainer->setAcceptMimeDataFunction([this](auto mimeData) {
90 90 return sqpApp->dragDropHelper().checkMimeDataForVisualization(mimeData,
91 91 ui->dragDropContainer);
92 92 });
93 93 connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccured, this,
94 94 &VisualizationZoneWidget::dropMimeData);
95 95
96 96 // 'Close' options : widget is deleted when closed
97 97 setAttribute(Qt::WA_DeleteOnClose);
98 98 connect(ui->closeButton, &QToolButton::clicked, this, &VisualizationZoneWidget::close);
99 99 ui->closeButton->setIcon(sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton));
100 100
101 101 // Synchronisation id
102 102 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronizationGroupId",
103 103 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
104 104 }
105 105
106 106 VisualizationZoneWidget::~VisualizationZoneWidget()
107 107 {
108 108 delete ui;
109 109 }
110 110
111 111 void VisualizationZoneWidget::addGraph(VisualizationGraphWidget *graphWidget)
112 112 {
113 113 // Synchronize new graph with others in the zone
114 114 impl->m_Synchronizer->addGraph(*graphWidget);
115 115
116 116 ui->dragDropContainer->addDragWidget(graphWidget);
117 117 }
118 118
119 119 void VisualizationZoneWidget::insertGraph(int index, VisualizationGraphWidget *graphWidget)
120 120 {
121 121 // Synchronize new graph with others in the zone
122 122 impl->m_Synchronizer->addGraph(*graphWidget);
123 123
124 124 ui->dragDropContainer->insertDragWidget(index, graphWidget);
125 125 }
126 126
127 127 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable)
128 128 {
129 129 return createGraph(variable, -1);
130 130 }
131 131
132 132 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable,
133 133 int index)
134 134 {
135 135 auto graphWidget
136 136 = new VisualizationGraphWidget{defaultGraphName(*ui->dragDropContainer->layout()), this};
137 137
138 138
139 139 // Set graph properties
140 140 graphWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
141 141 graphWidget->setMinimumHeight(GRAPH_MINIMUM_HEIGHT);
142 142
143 143
144 144 // Lambda to synchronize zone widget
145 145 auto synchronizeZoneWidget = [this, graphWidget](const SqpRange &graphRange,
146 146 const SqpRange &oldGraphRange) {
147 147
148 148 auto zoomType = VariableController::getZoomType(graphRange, oldGraphRange);
149 149 auto frameLayout = ui->dragDropContainer->layout();
150 150 for (auto i = 0; i < frameLayout->count(); ++i) {
151 151 auto graphChild
152 152 = dynamic_cast<VisualizationGraphWidget *>(frameLayout->itemAt(i)->widget());
153 153 if (graphChild && (graphChild != graphWidget)) {
154 154
155 155 auto graphChildRange = graphChild->graphRange();
156 156 switch (zoomType) {
157 157 case AcquisitionZoomType::ZoomIn: {
158 158 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
159 159 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
160 160 graphChildRange.m_TStart += deltaLeft;
161 161 graphChildRange.m_TEnd -= deltaRight;
162 162 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomIn");
163 163 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
164 164 << deltaLeft;
165 165 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
166 166 << deltaRight;
167 167 qCDebug(LOG_VisualizationZoneWidget())
168 168 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
169 169
170 170 break;
171 171 }
172 172
173 173 case AcquisitionZoomType::ZoomOut: {
174 174 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomOut");
175 175 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
176 176 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
177 177 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
178 178 << deltaLeft;
179 179 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
180 180 << deltaRight;
181 181 qCDebug(LOG_VisualizationZoneWidget())
182 182 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
183 183 graphChildRange.m_TStart -= deltaLeft;
184 184 graphChildRange.m_TEnd += deltaRight;
185 185 break;
186 186 }
187 187 case AcquisitionZoomType::PanRight: {
188 188 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: PanRight");
189 189 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
190 190 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
191 191 graphChildRange.m_TStart += deltaLeft;
192 192 graphChildRange.m_TEnd += deltaRight;
193 193 qCDebug(LOG_VisualizationZoneWidget())
194 194 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
195 195 break;
196 196 }
197 197 case AcquisitionZoomType::PanLeft: {
198 198 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: PanLeft");
199 199 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
200 200 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
201 201 graphChildRange.m_TStart -= deltaLeft;
202 202 graphChildRange.m_TEnd -= deltaRight;
203 203 break;
204 204 }
205 205 case AcquisitionZoomType::Unknown: {
206 206 qCDebug(LOG_VisualizationZoneWidget())
207 207 << tr("Impossible to synchronize: zoom type unknown");
208 208 break;
209 209 }
210 210 default:
211 211 qCCritical(LOG_VisualizationZoneWidget())
212 212 << tr("Impossible to synchronize: zoom type not take into account");
213 213 // No action
214 214 break;
215 215 }
216 216 graphChild->enableAcquisition(false);
217 217 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: Range before: ")
218 218 << graphChild->graphRange();
219 219 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: Range after : ")
220 220 << graphChildRange;
221 221 qCDebug(LOG_VisualizationZoneWidget())
222 222 << tr("TORM: child dt") << graphChildRange.m_TEnd - graphChildRange.m_TStart;
223 223 graphChild->setGraphRange(graphChildRange);
224 224 graphChild->enableAcquisition(true);
225 225 }
226 226 }
227 227 };
228 228
229 229 // connection for synchronization
230 230 connect(graphWidget, &VisualizationGraphWidget::synchronize, synchronizeZoneWidget);
231 231 connect(graphWidget, &VisualizationGraphWidget::variableAdded, this,
232 232 &VisualizationZoneWidget::onVariableAdded);
233 233 connect(graphWidget, &VisualizationGraphWidget::variableAboutToBeRemoved, this,
234 234 &VisualizationZoneWidget::onVariableAboutToBeRemoved);
235 235
236 236 auto range = SqpRange{};
237 237
238 238 // Apply visitor to graph children
239 239 auto layout = ui->dragDropContainer->layout();
240 240 if (layout->count() > 0) {
241 241 // Case of a new graph in a existant zone
242 242 if (auto visualizationGraphWidget
243 243 = dynamic_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
244 244 range = visualizationGraphWidget->graphRange();
245 245 }
246 246 }
247 247 else {
248 248 // Case of a new graph as the first of the zone
249 249 range = variable->range();
250 250 }
251 251
252 252 this->insertGraph(index, graphWidget);
253 253
254 254 graphWidget->addVariable(variable, range);
255 255
256 256 // get y using variable range
257 257 if (auto dataSeries = variable->dataSeries()) {
258 258 dataSeries->lockRead();
259 259 auto valuesBounds
260 260 = dataSeries->valuesBounds(variable->range().m_TStart, variable->range().m_TEnd);
261 261 auto end = dataSeries->cend();
262 262 if (valuesBounds.first != end && valuesBounds.second != end) {
263 263 auto rangeValue = [](const auto &value) { return std::isnan(value) ? 0. : value; };
264 264
265 265 auto minValue = rangeValue(valuesBounds.first->minValue());
266 266 auto maxValue = rangeValue(valuesBounds.second->maxValue());
267 267
268 268 graphWidget->setYRange(SqpRange{minValue, maxValue});
269 269 }
270 270 dataSeries->unlock();
271 271 }
272 272
273 273 return graphWidget;
274 274 }
275 275
276 276 VisualizationGraphWidget *
277 277 VisualizationZoneWidget::createGraph(const QList<std::shared_ptr<Variable> > variables, int index)
278 278 {
279 279 if (variables.isEmpty()) {
280 280 return nullptr;
281 281 }
282 282
283 283 auto graphWidget = createGraph(variables.first(), index);
284 284 for (auto variableIt = variables.cbegin() + 1; variableIt != variables.cend(); ++variableIt) {
285 285 graphWidget->addVariable(*variableIt, graphWidget->graphRange());
286 286 }
287 287
288 288 return graphWidget;
289 289 }
290 290
291 291 void VisualizationZoneWidget::accept(IVisualizationWidgetVisitor *visitor)
292 292 {
293 293 if (visitor) {
294 294 visitor->visitEnter(this);
295 295
296 296 // Apply visitor to graph children: widgets different from graphs are not visited (no
297 297 // action)
298 298 processGraphs(
299 299 *ui->dragDropContainer->layout(),
300 300 [visitor](VisualizationGraphWidget &graphWidget) { graphWidget.accept(visitor); });
301 301
302 302 visitor->visitLeave(this);
303 303 }
304 304 else {
305 305 qCCritical(LOG_VisualizationZoneWidget()) << tr("Can't visit widget : the visitor is null");
306 306 }
307 307 }
308 308
309 309 bool VisualizationZoneWidget::canDrop(const Variable &variable) const
310 310 {
311 311 // A tab can always accomodate a variable
312 312 Q_UNUSED(variable);
313 313 return true;
314 314 }
315 315
316 316 bool VisualizationZoneWidget::contains(const Variable &variable) const
317 317 {
318 318 Q_UNUSED(variable);
319 319 return false;
320 320 }
321 321
322 322 QString VisualizationZoneWidget::name() const
323 323 {
324 324 return ui->zoneNameLabel->text();
325 325 }
326 326
327 327 QMimeData *VisualizationZoneWidget::mimeData() const
328 328 {
329 329 auto mimeData = new QMimeData;
330 330 mimeData->setData(MIME_TYPE_ZONE, QByteArray{});
331 331
332 332 return mimeData;
333 333 }
334 334
335 335 bool VisualizationZoneWidget::isDragAllowed() const
336 336 {
337 337 return true;
338 338 }
339 339
340 340 void VisualizationZoneWidget::closeEvent(QCloseEvent *event)
341 341 {
342 342 // Closes graphs in the zone
343 343 processGraphs(*ui->dragDropContainer->layout(),
344 344 [](VisualizationGraphWidget &graphWidget) { graphWidget.close(); });
345 345
346 346 // Delete synchronization group from variable controller
347 347 QMetaObject::invokeMethod(&sqpApp->variableController(), "onRemoveSynchronizationGroupId",
348 348 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
349 349
350 350 QWidget::closeEvent(event);
351 351 }
352 352
353 353 void VisualizationZoneWidget::onVariableAdded(std::shared_ptr<Variable> variable)
354 354 {
355 355 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronized",
356 356 Qt::QueuedConnection, Q_ARG(std::shared_ptr<Variable>, variable),
357 357 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
358 358 }
359 359
360 360 void VisualizationZoneWidget::onVariableAboutToBeRemoved(std::shared_ptr<Variable> variable)
361 361 {
362 362 QMetaObject::invokeMethod(&sqpApp->variableController(), "desynchronize", Qt::QueuedConnection,
363 363 Q_ARG(std::shared_ptr<Variable>, variable),
364 364 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
365 365 }
366 366
367 367 void VisualizationZoneWidget::dropMimeData(int index, const QMimeData *mimeData)
368 368 {
369 369 if (mimeData->hasFormat(MIME_TYPE_GRAPH)) {
370 370 impl->dropGraph(index, this);
371 371 }
372 372 else if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
373 373 auto variables = sqpApp->variableController().variablesForMimeData(
374 374 mimeData->data(MIME_TYPE_VARIABLE_LIST));
375 375 impl->dropVariables(variables, index, this);
376 376 }
377 377 else {
378 378 qCWarning(LOG_VisualizationZoneWidget())
379 379 << tr("VisualizationZoneWidget::dropMimeData, unknown MIME data received.");
380 380 }
381 381 }
382 382
383 383 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropGraph(
384 384 int index, VisualizationZoneWidget *zoneWidget)
385 385 {
386 386 auto &helper = sqpApp->dragDropHelper();
387 387
388 388 auto graphWidget = qobject_cast<VisualizationGraphWidget *>(helper.getCurrentDragWidget());
389 389 if (!graphWidget) {
390 390 qCWarning(LOG_VisualizationZoneWidget())
391 391 << tr("VisualizationZoneWidget::dropGraph, drop aborted, the dropped graph is not "
392 392 "found or invalid.");
393 393 Q_ASSERT(false);
394 394 return;
395 395 }
396 396
397 397 auto parentDragDropContainer
398 398 = qobject_cast<VisualizationDragDropContainer *>(graphWidget->parentWidget());
399 399 if (!parentDragDropContainer) {
400 400 qCWarning(LOG_VisualizationZoneWidget())
401 401 << tr("VisualizationZoneWidget::dropGraph, drop aborted, the parent container of "
402 402 "the dropped graph is not found.");
403 403 Q_ASSERT(false);
404 404 return;
405 405 }
406 406
407 407 const auto &variables = graphWidget->variables();
408 408
409 409 if (parentDragDropContainer != zoneWidget->ui->dragDropContainer && !variables.isEmpty()) {
410 410 // The drop didn't occur in the same zone
411 411
412 412 // Abort the requests for the variables (if any)
413 413 // Commented, because it's not sure if it's needed or not
414 414 // for (const auto& var : variables)
415 415 //{
416 416 // sqpApp->variableController().onAbortProgressRequested(var);
417 417 //}
418 418
419 419 auto previousParentZoneWidget = graphWidget->parentZoneWidget();
420 420 auto nbGraph = parentDragDropContainer->countDragWidget();
421 421 if (nbGraph == 1) {
422 422 // This is the only graph in the previous zone, close the zone
423 423 previousParentZoneWidget->close();
424 424 }
425 425 else {
426 426 // Close the graph
427 427 graphWidget->close();
428 428 }
429 429
430 430 // Creates the new graph in the zone
431 431 zoneWidget->createGraph(variables, index);
432 432 }
433 433 else {
434 434 // The drop occurred in the same zone or the graph is empty
435 435 // Simple move of the graph, no variable operation associated
436 436 parentDragDropContainer->layout()->removeWidget(graphWidget);
437 437
438 438 if (variables.isEmpty() && parentDragDropContainer != zoneWidget->ui->dragDropContainer) {
439 439 // The graph is empty and dropped in a different zone.
440 440 // Take the range of the first graph in the zone (if existing).
441 441 auto layout = zoneWidget->ui->dragDropContainer->layout();
442 442 if (layout->count() > 0) {
443 443 if (auto visualizationGraphWidget
444 444 = qobject_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
445 445 graphWidget->setGraphRange(visualizationGraphWidget->graphRange());
446 446 }
447 447 }
448 448 }
449 449
450 450 zoneWidget->ui->dragDropContainer->insertDragWidget(index, graphWidget);
451 451 }
452 452 }
453 453
454 454 void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropVariables(
455 455 const QList<std::shared_ptr<Variable> > &variables, int index,
456 456 VisualizationZoneWidget *zoneWidget)
457 457 {
458 458 // Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and
459 459 // compatible variable here
460 460 if (variables.count() > 1) {
461 461 qCWarning(LOG_VisualizationZoneWidget())
462 462 << tr("VisualizationZoneWidget::dropVariables, dropping multiple variables, operation "
463 463 "aborted.");
464 464 return;
465 465 }
466 466
467 zoneWidget->createGraph({variable}, index);
467 zoneWidget->createGraph(variables, index);
468 468 }
@@ -1,37 +1,40
1 1 <?xml version="1.0" encoding="UTF-8"?>
2 2 <ui version="4.0">
3 3 <class>VariableInspectorWidget</class>
4 4 <widget class="QWidget" name="VariableInspectorWidget">
5 5 <property name="geometry">
6 6 <rect>
7 7 <x>0</x>
8 8 <y>0</y>
9 9 <width>400</width>
10 10 <height>300</height>
11 11 </rect>
12 12 </property>
13 13 <property name="windowTitle">
14 14 <string>Variables</string>
15 15 </property>
16 16 <layout class="QGridLayout" name="gridLayout">
17 17 <item row="0" column="0">
18 18 <widget class="QTableView" name="tableView">
19 <property name="acceptDrops">
20 <bool>true</bool>
21 </property>
19 22 <property name="dragEnabled">
20 23 <bool>true</bool>
21 24 </property>
22 25 <property name="dragDropMode">
23 26 <enum>QAbstractItemView::DragDrop</enum>
24 27 </property>
25 28 <property name="sortingEnabled">
26 29 <bool>true</bool>
27 30 </property>
28 31 <attribute name="horizontalHeaderStretchLastSection">
29 32 <bool>true</bool>
30 33 </attribute>
31 34 </widget>
32 35 </item>
33 36 </layout>
34 37 </widget>
35 38 <resources/>
36 39 <connections/>
37 40 </ui>
General Comments 0
You need to be logged in to leave comments. Login now