##// END OF EJS Templates
Merge pull request 371 from SciQLop-fork develop...
trabillard -
r1055:184d761e8d0c merge
parent child
Show More
@@ -0,0 +1,15
1 #ifndef SCIQLOP_DATASOURCEITEMMERGEHELPER_H
2 #define SCIQLOP_DATASOURCEITEMMERGEHELPER_H
3
4 class DataSourceItem;
5
6 /**
7 * @brief The DataSourceItemMergeHelper struct is used to merge two data source items
8 * @sa DataSourceItem::merge()
9 */
10 struct DataSourceItemMergeHelper {
11 /// Merges source item into dest item
12 static void merge(const DataSourceItem &source, DataSourceItem &dest);
13 };
14
15 #endif // SCIQLOP_DATASOURCEITEMMERGEHELPER_H
@@ -0,0 +1,55
1 #include "DataSource/DataSourceItemMergeHelper.h"
2
3 #include <DataSource/DataSourceItem.h>
4
5 namespace {
6
7 /**
8 * Finds in a tree an item similar to the item passed in parameter
9 * @param item the item for which to find a similar item
10 * @param root the root item of the tree
11 * @return the similar item if found, nullptr otherwise
12 */
13 DataSourceItem *findSimilarItem(const DataSourceItem &item, const DataSourceItem &root)
14 {
15 // An item is considered similar to the another item if:
16 // - the items are both nodes AND
17 // - the names of the items are identical
18
19 if (item.type() != DataSourceItemType::NODE) {
20 return nullptr;
21 }
22
23 DataSourceItem *result{nullptr};
24 bool found{false};
25 for (auto i = 0, count = root.childCount(); i < count && !found; ++i) {
26 auto child = root.child(i);
27
28 found = child->type() == DataSourceItemType::NODE
29 && QString::compare(child->name(), item.name(), Qt::CaseInsensitive) == 0;
30 if (found) {
31 result = child;
32 }
33 }
34
35 return result;
36 }
37
38 } // namespace
39
40 void DataSourceItemMergeHelper::merge(const DataSourceItem &source, DataSourceItem &dest)
41 {
42 // Checks if the source item can be merged into the destination item (i.e. there is a child item
43 // similar to the source item)
44 if (auto subItem = findSimilarItem(source, dest)) {
45 // If there is an item similar to the source item, applies the merge recursively
46 for (auto i = 0, count = source.childCount(); i < count; ++i) {
47 merge(*source.child(i), *subItem);
48 }
49 }
50 else {
51 // If no item is similar to the source item, the item is copied as the child of the
52 // destination item
53 dest.appendChild(source.clone());
54 }
55 }
@@ -0,0 +1,73
1 #include "DataSourceItemBuilder.h"
2
3 DataSourceItemBuilder &DataSourceItemBuilder::root(const QString &name)
4 {
5 m_Root = std::make_shared<DataSourceItem>(DataSourceItemType::NODE, name);
6 m_Items.push(m_Root.get());
7 return *this;
8 }
9
10 DataSourceItemBuilder &DataSourceItemBuilder::root(QVariantHash data)
11 {
12 m_Root = std::make_shared<DataSourceItem>(DataSourceItemType::NODE, data);
13 m_Items.push(m_Root.get());
14 return *this;
15 }
16
17 DataSourceItemBuilder &DataSourceItemBuilder::node(const QString &name)
18 {
19 return append(DataSourceItemType::NODE, name);
20 }
21
22 DataSourceItemBuilder &DataSourceItemBuilder::node(QVariantHash data)
23 {
24 return append(DataSourceItemType::NODE, std::move(data));
25 }
26
27 DataSourceItemBuilder &DataSourceItemBuilder::product(const QString &name)
28 {
29 return append(DataSourceItemType::PRODUCT, name);
30 }
31
32 DataSourceItemBuilder &DataSourceItemBuilder::product(QVariantHash data)
33 {
34 return append(DataSourceItemType::PRODUCT, std::move(data));
35 }
36
37 DataSourceItemBuilder &DataSourceItemBuilder::component(const QString &name)
38 {
39 return append(DataSourceItemType::COMPONENT, name);
40 }
41
42 DataSourceItemBuilder &DataSourceItemBuilder::component(QVariantHash data)
43 {
44 return append(DataSourceItemType::COMPONENT, std::move(data));
45 }
46
47 DataSourceItemBuilder &DataSourceItemBuilder::end()
48 {
49 m_Items.pop();
50 return *this;
51 }
52
53 std::shared_ptr<DataSourceItem> DataSourceItemBuilder::build()
54 {
55 return m_Root;
56 }
57
58 DataSourceItemBuilder &DataSourceItemBuilder::append(DataSourceItemType type, const QString &name)
59 {
60 append(type, QVariantHash{{DataSourceItem::NAME_DATA_KEY, name}});
61 return *this;
62 }
63
64 DataSourceItemBuilder &DataSourceItemBuilder::append(DataSourceItemType type, QVariantHash data)
65 {
66 auto parentItem = m_Items.top();
67
68 auto insertIndex = parentItem->childCount();
69 parentItem->appendChild(std::make_unique<DataSourceItem>(type, std::move(data)));
70
71 m_Items.push(parentItem->child(insertIndex));
72 return *this;
73 }
@@ -0,0 +1,45
1 #ifndef SCIQLOP_DATASOURCEITEMBUILDER_H
2 #define SCIQLOP_DATASOURCEITEMBUILDER_H
3
4 #include <DataSource/DataSourceItem.h>
5
6 #include <memory>
7 #include <stack>
8
9 /**
10 * @brief The DataSourceItemBuilder class aims to facilitate the creation of a DataSourceItem for unit tests
11 * @sa DataSourceItem
12 */
13 class DataSourceItemBuilder {
14 public:
15 /// Inits root item
16 DataSourceItemBuilder & root(const QString &name);
17 DataSourceItemBuilder & root(QVariantHash data);
18
19 /// Adds node into the current item
20 DataSourceItemBuilder & node(const QString &name);
21 DataSourceItemBuilder & node(QVariantHash data);
22
23 /// Adds product into the current item
24 DataSourceItemBuilder & product(const QString &name);
25 DataSourceItemBuilder & product(QVariantHash data);
26
27 /// Adds component into the current item
28 DataSourceItemBuilder & component(const QString &name);
29 DataSourceItemBuilder & component(QVariantHash data);
30
31 /// Closes the build of the current item
32 DataSourceItemBuilder& end();
33
34 /// Creates the DataSourceItem
35 std::shared_ptr<DataSourceItem> build();
36
37 private:
38 DataSourceItemBuilder& append(DataSourceItemType type, const QString &name);
39 DataSourceItemBuilder& append(DataSourceItemType type, QVariantHash data);
40
41 std::shared_ptr<DataSourceItem> m_Root{nullptr};
42 std::stack<DataSourceItem*> m_Items;
43 };
44
45 #endif // SCIQLOP_DATASOURCEITEMBUILDER_H
@@ -0,0 +1,207
1 #include <DataSource/DataSourceItem.h>
2
3 #include "DataSourceItemBuilder.h"
4
5 #include <QObject>
6 #include <QtTest>
7
8 #include <iostream>
9
10 namespace {
11
12 void printItem(std::ostream &out, const DataSourceItem &item, int level = 0)
13 {
14 for (auto i = 0; i < level; ++i) {
15 out << " ";
16 }
17
18 out << item.name().toStdString() << "\n";
19
20 for (auto i = 0, count = item.childCount(); i < count; ++i) {
21 printItem(out, *item.child(i), level + 1);
22 }
23 }
24
25 std::ostream &operator<<(std::ostream &out, const DataSourceItem &item)
26 {
27 printItem(out, item, 0);
28 return out;
29 }
30
31 } // namespace
32
33 Q_DECLARE_METATYPE(std::shared_ptr<DataSourceItem>)
34
35 class TestDataSourceItem : public QObject {
36 Q_OBJECT
37 private slots:
38 void testMerge_data();
39 void testMerge();
40 };
41
42 void TestDataSourceItem::testMerge_data()
43 {
44 QTest::addColumn<std::shared_ptr<DataSourceItem> >("source");
45 QTest::addColumn<std::shared_ptr<DataSourceItem> >("dest");
46 QTest::addColumn<std::shared_ptr<DataSourceItem> >("expectedResult");
47
48 QTest::newRow("merge (basic case)") << DataSourceItemBuilder{}
49 .root("A2")
50 .node("- B2")
51 .product("-- P2")
52 .end() // P2
53 .end() // B2
54 .end() // A2
55 .build()
56 << DataSourceItemBuilder{}
57 .root("A1")
58 .node("- B1")
59 .product("-- P1")
60 .end() // P1
61 .end() // B1
62 .end() // A1
63 .build()
64 << DataSourceItemBuilder{}
65 .root("A1")
66 .node("- B1")
67 .product("-- P1")
68 .end() // P1
69 .end() // B1
70 .node("- B2")
71 .product("-- P2")
72 .end() // P2
73 .end() // B2
74 .end() // A1
75 .build();
76
77 QTest::newRow("merge (some of the source and destination trees are identical)")
78 << DataSourceItemBuilder{}
79 .root("A2")
80 .node("- B1")
81 .node("-- C1")
82 .product("--- P2")
83 .end() // P2
84 .end() // C1
85 .node("-- C2")
86 .end() // C2
87 .end() // B1
88 .end() // A2
89 .build()
90 << DataSourceItemBuilder{}
91 .root("A1")
92 .node("- B1")
93 .node("-- C1")
94 .product("--- P1")
95 .end() // P1
96 .end() // C1
97 .end() // B1
98 .end() // A1
99 .build()
100 << DataSourceItemBuilder{}
101 .root("A1")
102 .node("- B1")
103 .node("-- C1")
104 .product("--- P1")
105 .end() // P1
106 .product("--- P2")
107 .end() // P2
108 .end() // C1
109 .node("-- C2")
110 .end() // C2
111 .end() // B1
112 .end() // A1
113 .build();
114
115 QTest::newRow("merge (products with the same name and tree are kept)")
116 << DataSourceItemBuilder{}
117 .root("A2")
118 .node("- B1")
119 .node("-- C1")
120 .product({{"name", "--- P1"}, {"from", "source"}})
121 .end() // P1
122 .end() // C1
123 .end() // B1
124 .end() // A2
125 .build()
126 << DataSourceItemBuilder{}
127 .root("A1")
128 .node("- B1")
129 .node("-- C1")
130 .product({{"name", "--- P1"}, {"from", "dest"}})
131 .end() // P1
132 .end() // C1
133 .end() // B1
134 .end() // A1
135 .build()
136 << DataSourceItemBuilder{}
137 .root("A1")
138 .node("- B1")
139 .node("-- C1")
140 .product({{"name", "--- P1"}, {"from", "dest"}})
141 .end() // P1 (dest)
142 .product({{"name", "--- P1"}, {"from", "source"}})
143 .end() // P1 (source)
144 .end() // C1
145 .end() // B1
146 .end() // A1
147 .build();
148
149 QTest::newRow("merge (for same nodes, metadata of dest node are kept)")
150 << DataSourceItemBuilder{}
151 .root("A2")
152 .node("- B1")
153 .node({{"name", "-- C1"}, {"from", "source"}})
154 .product("--- P2")
155 .end() // P1
156 .end() // C1
157 .end() // B1
158 .end() // A2
159 .build()
160 << DataSourceItemBuilder{}
161 .root("A1")
162 .node("- B1")
163 .node({{"name", "-- C1"}, {"from", "dest"}})
164 .product("--- P1")
165 .end() // P1
166 .end() // C1
167 .end() // B1
168 .end() // A1
169 .build()
170 << DataSourceItemBuilder{}
171 .root("A1")
172 .node("- B1")
173 .node({{"name", "-- C1"}, {"from", "dest"}})
174 .product("--- P1")
175 .end() // P1
176 .product("--- P2")
177 .end() // P2
178 .end() // C1 (dest)
179 .end() // B1
180 .end() // A1
181 .build();
182 }
183
184 void TestDataSourceItem::testMerge()
185 {
186 QFETCH(std::shared_ptr<DataSourceItem>, source);
187 QFETCH(std::shared_ptr<DataSourceItem>, dest);
188 QFETCH(std::shared_ptr<DataSourceItem>, expectedResult);
189
190 // Uncomment to print trees
191 // std::cout << "source: \n" << *source << "\n";
192 // std::cout << "dest: \n" << *dest << "\n";
193
194 // Merges source in dest (not taking source root)
195 for (auto i = 0, count = source->childCount(); i < count; ++i) {
196 dest->merge(*source->child(i));
197 }
198
199 // Uncomment to print trees
200 // std::cout << "dest after merge: \n" << *dest << "\n";
201
202 // Checks merge result
203 QVERIFY(*dest == *expectedResult);
204 }
205
206 QTEST_MAIN(TestDataSourceItem)
207 #include "TestDataSourceItem.moc"
@@ -1,164 +1,170
1 <?xml version="1.0" encoding="UTF-8"?>
1 <?xml version="1.0" encoding="UTF-8"?>
2 <ui version="4.0">
2 <ui version="4.0">
3 <class>MainWindow</class>
3 <class>MainWindow</class>
4 <widget class="QMainWindow" name="MainWindow">
4 <widget class="QMainWindow" name="MainWindow">
5 <property name="geometry">
5 <property name="geometry">
6 <rect>
6 <rect>
7 <x>0</x>
7 <x>0</x>
8 <y>0</y>
8 <y>0</y>
9 <width>800</width>
9 <width>800</width>
10 <height>600</height>
10 <height>600</height>
11 </rect>
11 </rect>
12 </property>
12 </property>
13 <property name="windowTitle">
13 <property name="windowTitle">
14 <string>SciQlop v0.0.1</string>
14 <string>SciQlop v0.0.1</string>
15 </property>
15 </property>
16 <property name="dockNestingEnabled">
16 <property name="dockNestingEnabled">
17 <bool>true</bool>
17 <bool>true</bool>
18 </property>
18 </property>
19 <widget class="QWidget" name="centralWidget">
19 <widget class="QWidget" name="centralWidget">
20 <property name="enabled">
20 <property name="enabled">
21 <bool>true</bool>
21 <bool>true</bool>
22 </property>
22 </property>
23 <property name="sizePolicy">
23 <property name="sizePolicy">
24 <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
24 <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
25 <horstretch>0</horstretch>
25 <horstretch>0</horstretch>
26 <verstretch>0</verstretch>
26 <verstretch>0</verstretch>
27 </sizepolicy>
27 </sizepolicy>
28 </property>
28 </property>
29 <property name="maximumSize">
29 <property name="maximumSize">
30 <size>
30 <size>
31 <width>16777215</width>
31 <width>16777215</width>
32 <height>16777215</height>
32 <height>16777215</height>
33 </size>
33 </size>
34 </property>
34 </property>
35 <layout class="QHBoxLayout" name="horizontalLayout">
35 <layout class="QHBoxLayout" name="horizontalLayout">
36 <property name="spacing">
36 <property name="spacing">
37 <number>0</number>
37 <number>0</number>
38 </property>
38 </property>
39 <property name="leftMargin">
39 <property name="leftMargin">
40 <number>0</number>
40 <number>0</number>
41 </property>
41 </property>
42 <property name="topMargin">
42 <property name="topMargin">
43 <number>0</number>
43 <number>0</number>
44 </property>
44 </property>
45 <property name="rightMargin">
45 <property name="rightMargin">
46 <number>0</number>
46 <number>0</number>
47 </property>
47 </property>
48 <property name="bottomMargin">
48 <property name="bottomMargin">
49 <number>0</number>
49 <number>0</number>
50 </property>
50 </property>
51 <item>
51 <item>
52 <widget class="QSplitter" name="splitter">
52 <widget class="QSplitter" name="splitter">
53 <property name="orientation">
53 <property name="orientation">
54 <enum>Qt::Horizontal</enum>
54 <enum>Qt::Horizontal</enum>
55 </property>
55 </property>
56 <widget class="QWidget" name="leftMainInspectorWidget" native="true">
56 <widget class="QWidget" name="leftMainInspectorWidget" native="true">
57 <layout class="QVBoxLayout" name="verticalLayout">
57 <layout class="QVBoxLayout" name="verticalLayout">
58 <property name="spacing">
58 <property name="spacing">
59 <number>0</number>
59 <number>0</number>
60 </property>
60 </property>
61 <property name="leftMargin">
61 <property name="leftMargin">
62 <number>0</number>
62 <number>0</number>
63 </property>
63 </property>
64 <property name="topMargin">
64 <property name="topMargin">
65 <number>0</number>
65 <number>0</number>
66 </property>
66 </property>
67 <property name="rightMargin">
67 <property name="rightMargin">
68 <number>0</number>
68 <number>0</number>
69 </property>
69 </property>
70 <property name="bottomMargin">
70 <property name="bottomMargin">
71 <number>0</number>
71 <number>0</number>
72 </property>
72 </property>
73 <item>
73 <item>
74 <widget class="DataSourceWidget" name="dataSourceWidget" native="true"/>
74 <widget class="DataSourceWidget" name="dataSourceWidget" native="true"/>
75 </item>
75 </item>
76 <item>
76 <item>
77 <widget class="QWidget" name="dateTimeWidget" native="true"/>
77 <widget class="QWidget" name="dateTimeWidget" native="true"/>
78 </item>
78 </item>
79 <item>
79 <item>
80 <widget class="VariableInspectorWidget" name="variableInspectorWidget" native="true"/>
80 <widget class="VariableInspectorWidget" name="variableInspectorWidget" native="true"/>
81 </item>
81 </item>
82 </layout>
82 </layout>
83 </widget>
83 </widget>
84 <widget class="SqpSidePane" name="leftInspectorSidePane" native="true"/>
84 <widget class="SqpSidePane" name="leftInspectorSidePane" native="true"/>
85 <widget class="VisualizationWidget" name="view" native="true">
85 <widget class="VisualizationWidget" name="view" native="true">
86 <property name="sizePolicy">
86 <property name="sizePolicy">
87 <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
87 <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
88 <horstretch>0</horstretch>
88 <horstretch>0</horstretch>
89 <verstretch>0</verstretch>
89 <verstretch>0</verstretch>
90 </sizepolicy>
90 </sizepolicy>
91 </property>
91 </property>
92 </widget>
92 </widget>
93 <widget class="SqpSidePane" name="rightInspectorSidePane" native="true"/>
93 <widget class="SqpSidePane" name="rightInspectorSidePane" native="true"/>
94 <widget class="QWidget" name="rightMainInspectorWidget" native="true">
94 <widget class="QWidget" name="rightMainInspectorWidget" native="true">
95 <layout class="QVBoxLayout" name="verticalLayout_3">
95 <layout class="QVBoxLayout" name="verticalLayout_3">
96 <property name="spacing">
96 <property name="spacing">
97 <number>0</number>
97 <number>0</number>
98 </property>
98 </property>
99 <property name="leftMargin">
99 <property name="leftMargin">
100 <number>0</number>
100 <number>0</number>
101 </property>
101 </property>
102 <property name="topMargin">
102 <property name="topMargin">
103 <number>0</number>
103 <number>0</number>
104 </property>
104 </property>
105 <property name="rightMargin">
105 <property name="rightMargin">
106 <number>0</number>
106 <number>0</number>
107 </property>
107 </property>
108 <property name="bottomMargin">
108 <property name="bottomMargin">
109 <number>0</number>
109 <number>0</number>
110 </property>
110 </property>
111 <item>
111 <item>
112 <widget class="QWidget" name="commonPropertyInspectorWidget" native="true"/>
112 <widget class="QWidget" name="commonPropertyInspectorWidget" native="true"/>
113 </item>
113 </item>
114 <item>
114 <item>
115 <widget class="DataSourceWidget" name="catalogWidget" native="true"/>
115 <widget class="QTreeWidget" name="catalogWidget">
116 <column>
117 <property name="text">
118 <string notr="true">Name</string>
119 </property>
120 </column>
121 </widget>
116 </item>
122 </item>
117 </layout>
123 </layout>
118 </widget>
124 </widget>
119 </widget>
125 </widget>
120 </item>
126 </item>
121 </layout>
127 </layout>
122 </widget>
128 </widget>
123 <widget class="QMenuBar" name="menuBar">
129 <widget class="QMenuBar" name="menuBar">
124 <property name="geometry">
130 <property name="geometry">
125 <rect>
131 <rect>
126 <x>0</x>
132 <x>0</x>
127 <y>0</y>
133 <y>0</y>
128 <width>800</width>
134 <width>800</width>
129 <height>28</height>
135 <height>28</height>
130 </rect>
136 </rect>
131 </property>
137 </property>
132 </widget>
138 </widget>
133 <widget class="QStatusBar" name="statusBar"/>
139 <widget class="QStatusBar" name="statusBar"/>
134 </widget>
140 </widget>
135 <layoutdefault spacing="6" margin="11"/>
141 <layoutdefault spacing="6" margin="11"/>
136 <customwidgets>
142 <customwidgets>
137 <customwidget>
143 <customwidget>
138 <class>VisualizationWidget</class>
144 <class>VisualizationWidget</class>
139 <extends>QWidget</extends>
145 <extends>QWidget</extends>
140 <header location="global">Visualization/VisualizationWidget.h</header>
146 <header location="global">Visualization/VisualizationWidget.h</header>
141 <container>1</container>
147 <container>1</container>
142 </customwidget>
148 </customwidget>
143 <customwidget>
149 <customwidget>
144 <class>SqpSidePane</class>
150 <class>SqpSidePane</class>
145 <extends>QWidget</extends>
151 <extends>QWidget</extends>
146 <header location="global">SidePane/SqpSidePane.h</header>
152 <header location="global">SidePane/SqpSidePane.h</header>
147 <container>1</container>
153 <container>1</container>
148 </customwidget>
154 </customwidget>
149 <customwidget>
155 <customwidget>
150 <class>DataSourceWidget</class>
156 <class>DataSourceWidget</class>
151 <extends>QWidget</extends>
157 <extends>QWidget</extends>
152 <header location="global">DataSource/DataSourceWidget.h</header>
158 <header location="global">DataSource/DataSourceWidget.h</header>
153 <container>1</container>
159 <container>1</container>
154 </customwidget>
160 </customwidget>
155 <customwidget>
161 <customwidget>
156 <class>VariableInspectorWidget</class>
162 <class>VariableInspectorWidget</class>
157 <extends>QWidget</extends>
163 <extends>QWidget</extends>
158 <header location="global">Variable/VariableInspectorWidget.h</header>
164 <header location="global">Variable/VariableInspectorWidget.h</header>
159 <container>1</container>
165 <container>1</container>
160 </customwidget>
166 </customwidget>
161 </customwidgets>
167 </customwidgets>
162 <resources/>
168 <resources/>
163 <connections/>
169 <connections/>
164 </ui>
170 </ui>
@@ -1,110 +1,154
1 #ifndef SCIQLOP_DATASOURCEITEM_H
1 #ifndef SCIQLOP_DATASOURCEITEM_H
2 #define SCIQLOP_DATASOURCEITEM_H
2 #define SCIQLOP_DATASOURCEITEM_H
3
3
4 #include "CoreGlobal.h"
4 #include "CoreGlobal.h"
5
5
6 #include <Common/spimpl.h>
6 #include <Common/spimpl.h>
7
7
8 #include <QVariant>
8 #include <QVariant>
9 #include <QVector>
9 #include <QVector>
10
10
11 class DataSourceItemAction;
11 class DataSourceItemAction;
12
12
13 /**
13 /**
14 * Possible types of an item
14 * Possible types of an item
15 */
15 */
16 enum class DataSourceItemType { NODE, PRODUCT, COMPONENT };
16 enum class DataSourceItemType { NODE, PRODUCT, COMPONENT };
17
17
18 /**
18 /**
19 * @brief The DataSourceItem class aims to represent a structure element of a data source.
19 * @brief The DataSourceItem class aims to represent a structure element of a data source.
20 * A data source has a tree structure that is made up of a main DataSourceItem object (root)
20 * A data source has a tree structure that is made up of a main DataSourceItem object (root)
21 * containing other DataSourceItem objects (children).
21 * containing other DataSourceItem objects (children).
22 * For each DataSourceItem can be associated a set of data representing it.
22 * For each DataSourceItem can be associated a set of data representing it.
23 */
23 */
24 class SCIQLOP_CORE_EXPORT DataSourceItem {
24 class SCIQLOP_CORE_EXPORT DataSourceItem {
25 public:
25 public:
26 /// Key associated with the name of the item
26 /// Key associated with the name of the item
27 static const QString NAME_DATA_KEY;
27 static const QString NAME_DATA_KEY;
28 /// Key associated with the plugin of the item
29 static const QString PLUGIN_DATA_KEY;
28
30
29 explicit DataSourceItem(DataSourceItemType type, const QString &name);
31 explicit DataSourceItem(DataSourceItemType type, const QString &name);
30 explicit DataSourceItem(DataSourceItemType type, QVariantHash data = {});
32 explicit DataSourceItem(DataSourceItemType type, QVariantHash data = {});
31
33
34 std::unique_ptr<DataSourceItem> clone() const;
35
32 /// @return the actions of the item as a vector
36 /// @return the actions of the item as a vector
33 QVector<DataSourceItemAction *> actions() const noexcept;
37 QVector<DataSourceItemAction *> actions() const noexcept;
34
38
35 /**
39 /**
36 * Adds an action to the item. The item takes ownership of the action, and the action is
40 * Adds an action to the item. The item takes ownership of the action, and the action is
37 * automatically associated to the item
41 * automatically associated to the item
38 * @param action the action to add
42 * @param action the action to add
39 */
43 */
40 void addAction(std::unique_ptr<DataSourceItemAction> action) noexcept;
44 void addAction(std::unique_ptr<DataSourceItemAction> action) noexcept;
41
45
42 /**
46 /**
43 * Adds a child to the item. The item takes ownership of the child.
47 * Adds a child to the item. The item takes ownership of the child.
44 * @param child the child to add
48 * @param child the child to add
45 */
49 */
46 void appendChild(std::unique_ptr<DataSourceItem> child) noexcept;
50 void appendChild(std::unique_ptr<DataSourceItem> child) noexcept;
47
51
48 /**
52 /**
49 * Returns the item's child associated to an index
53 * Returns the item's child associated to an index
50 * @param childIndex the index to search
54 * @param childIndex the index to search
51 * @return a pointer to the child if index is valid, nullptr otherwise
55 * @return a pointer to the child if index is valid, nullptr otherwise
52 */
56 */
53 DataSourceItem *child(int childIndex) const noexcept;
57 DataSourceItem *child(int childIndex) const noexcept;
54
58
55 int childCount() const noexcept;
59 int childCount() const noexcept;
56
60
57 /**
61 /**
58 * Get the data associated to a key
62 * Get the data associated to a key
59 * @param key the key to search
63 * @param key the key to search
60 * @return the data found if key is valid, default QVariant otherwise
64 * @return the data found if key is valid, default QVariant otherwise
61 */
65 */
62 QVariant data(const QString &key) const noexcept;
66 QVariant data(const QString &key) const noexcept;
63
67
64 /// Gets all data
68 /// Gets all data
65 QVariantHash data() const noexcept;
69 QVariantHash data() const noexcept;
66
70
71 /**
72 * Merge in the item the source item passed as parameter.
73 *
74 * The merge is done by adding as child of the item the complete tree represented by the source
75 * item. If a part of the tree already exists in the item (based on the name of the nodes), it
76 * is merged by completing the existing tree by items "leaves" (products, components or nodes
77 * with no child).
78 *
79 * For example, with item representing the tree:
80 * R (root node)
81 * - N1 (node)
82 * -- N11 (node)
83 * --- P1 (product)
84 * --- P2 (product)
85 * - N2 (node)
86 *
87 * and the source item representing the tree:
88 * N1 (root node)
89 * - N11 (node)
90 * -- P3 (product)
91 * - N12 (node)
92 *
93 * The leaves of the source item to merge into the item are N1/N11/P3 and N1/N12 => we therefore
94 * have the following merge result:
95 * R
96 * - N1
97 * -- N11
98 * --- P1
99 * --- P2
100 * --- P3 (added leaf)
101 * -- N12 (added leaf)
102 *
103 * @param item the source item
104 * @remarks No control is performed on products or components that are merged into the same tree
105 * part (two products or components may have the same name)
106 * @remarks the merge is made by copy (source item is not changed and still exists after the
107 * operation)
108 */
109 void merge(const DataSourceItem &item);
110
67 bool isRoot() const noexcept;
111 bool isRoot() const noexcept;
68
112
69 QString name() const noexcept;
113 QString name() const noexcept;
70
114
71 /**
115 /**
72 * Get the item's parent
116 * Get the item's parent
73 * @return a pointer to the parent if it exists, nullptr if the item is a root
117 * @return a pointer to the parent if it exists, nullptr if the item is a root
74 */
118 */
75 DataSourceItem *parentItem() const noexcept;
119 DataSourceItem *parentItem() const noexcept;
76
120
77 /**
121 /**
78 * Gets the item's root
122 * Gets the item's root
79 * @return the top parent, the item itself if it's the root item
123 * @return the top parent, the item itself if it's the root item
80 */
124 */
81 const DataSourceItem &rootItem() const noexcept;
125 const DataSourceItem &rootItem() const noexcept;
82
126
83 /**
127 /**
84 * Sets or appends a value to a key
128 * Sets or appends a value to a key
85 * @param key the key
129 * @param key the key
86 * @param value the value
130 * @param value the value
87 * @param append if true, the value is added to the values already existing for the key,
131 * @param append if true, the value is added to the values already existing for the key,
88 * otherwise it replaces the existing values
132 * otherwise it replaces the existing values
89 */
133 */
90 void setData(const QString &key, const QVariant &value, bool append = false) noexcept;
134 void setData(const QString &key, const QVariant &value, bool append = false) noexcept;
91
135
92 DataSourceItemType type() const noexcept;
136 DataSourceItemType type() const noexcept;
93
137
94 /**
138 /**
95 * @brief Searches the first child matching the specified data.
139 * @brief Searches the first child matching the specified data.
96 * @param data The data to search.
140 * @param data The data to search.
97 * @param recursive So the search recursively.
141 * @param recursive So the search recursively.
98 * @return the item matching the data or nullptr if it was not found.
142 * @return the item matching the data or nullptr if it was not found.
99 */
143 */
100 DataSourceItem *findItem(const QVariantHash &data, bool recursive);
144 DataSourceItem *findItem(const QVariantHash &data, bool recursive);
101
145
102 bool operator==(const DataSourceItem &other);
146 bool operator==(const DataSourceItem &other);
103 bool operator!=(const DataSourceItem &other);
147 bool operator!=(const DataSourceItem &other);
104
148
105 private:
149 private:
106 class DataSourceItemPrivate;
150 class DataSourceItemPrivate;
107 spimpl::unique_impl_ptr<DataSourceItemPrivate> impl;
151 spimpl::unique_impl_ptr<DataSourceItemPrivate> impl;
108 };
152 };
109
153
110 #endif // SCIQLOP_DATASOURCEITEMMODEL_H
154 #endif // SCIQLOP_DATASOURCEITEMMODEL_H
@@ -1,52 +1,54
1 #ifndef SCIQLOP_DATASOURCEITEMACTION_H
1 #ifndef SCIQLOP_DATASOURCEITEMACTION_H
2 #define SCIQLOP_DATASOURCEITEMACTION_H
2 #define SCIQLOP_DATASOURCEITEMACTION_H
3
3
4 #include "CoreGlobal.h"
4 #include "CoreGlobal.h"
5
5
6 #include <Common/spimpl.h>
6 #include <Common/spimpl.h>
7
7
8 #include <QLoggingCategory>
8 #include <QLoggingCategory>
9 #include <QObject>
9 #include <QObject>
10
10
11 #include <functional>
11 #include <functional>
12
12
13 Q_DECLARE_LOGGING_CATEGORY(LOG_DataSourceItemAction)
13 Q_DECLARE_LOGGING_CATEGORY(LOG_DataSourceItemAction)
14
14
15 class DataSourceItem;
15 class DataSourceItem;
16
16
17 /**
17 /**
18 * @brief The DataSourceItemAction class represents an action on a data source item.
18 * @brief The DataSourceItemAction class represents an action on a data source item.
19 *
19 *
20 * An action is a function that will be executed when the slot execute() is called.
20 * An action is a function that will be executed when the slot execute() is called.
21 */
21 */
22 class SCIQLOP_CORE_EXPORT DataSourceItemAction : public QObject {
22 class SCIQLOP_CORE_EXPORT DataSourceItemAction : public QObject {
23
23
24 Q_OBJECT
24 Q_OBJECT
25
25
26 public:
26 public:
27 /// Signature of the function associated to the action
27 /// Signature of the function associated to the action
28 using ExecuteFunction = std::function<void(DataSourceItem &dataSourceItem)>;
28 using ExecuteFunction = std::function<void(DataSourceItem &dataSourceItem)>;
29
29
30 /**
30 /**
31 * Ctor
31 * Ctor
32 * @param name the name of the action
32 * @param name the name of the action
33 * @param fun the function that will be called when the action is executed
33 * @param fun the function that will be called when the action is executed
34 * @sa execute()
34 * @sa execute()
35 */
35 */
36 explicit DataSourceItemAction(const QString &name, ExecuteFunction fun);
36 explicit DataSourceItemAction(const QString &name, ExecuteFunction fun);
37
37
38 std::unique_ptr<DataSourceItemAction> clone() const;
39
38 QString name() const noexcept;
40 QString name() const noexcept;
39
41
40 /// Sets the data source item concerned by the action
42 /// Sets the data source item concerned by the action
41 void setDataSourceItem(DataSourceItem *dataSourceItem) noexcept;
43 void setDataSourceItem(DataSourceItem *dataSourceItem) noexcept;
42
44
43 public slots:
45 public slots:
44 /// Executes the action
46 /// Executes the action
45 void execute();
47 void execute();
46
48
47 private:
49 private:
48 class DataSourceItemActionPrivate;
50 class DataSourceItemActionPrivate;
49 spimpl::unique_impl_ptr<DataSourceItemActionPrivate> impl;
51 spimpl::unique_impl_ptr<DataSourceItemActionPrivate> impl;
50 };
52 };
51
53
52 #endif // SCIQLOP_DATASOURCEITEMACTION_H
54 #endif // SCIQLOP_DATASOURCEITEMACTION_H
@@ -1,65 +1,66
1
1
2 core_moc_headers = [
2 core_moc_headers = [
3 'include/Data/IDataProvider.h',
3 'include/Data/IDataProvider.h',
4 'include/DataSource/DataSourceController.h',
4 'include/DataSource/DataSourceController.h',
5 'include/DataSource/DataSourceItemAction.h',
5 'include/DataSource/DataSourceItemAction.h',
6 'include/Network/NetworkController.h',
6 'include/Network/NetworkController.h',
7 'include/Time/TimeController.h',
7 'include/Time/TimeController.h',
8 'include/Variable/Variable.h',
8 'include/Variable/Variable.h',
9 'include/Variable/VariableCacheController.h',
9 'include/Variable/VariableCacheController.h',
10 'include/Variable/VariableController.h',
10 'include/Variable/VariableController.h',
11 'include/Variable/VariableAcquisitionWorker.h',
11 'include/Variable/VariableAcquisitionWorker.h',
12 'include/Variable/VariableSynchronizationGroup.h',
12 'include/Variable/VariableSynchronizationGroup.h',
13 'include/Variable/VariableModel.h',
13 'include/Variable/VariableModel.h',
14 'include/Visualization/VisualizationController.h'
14 'include/Visualization/VisualizationController.h'
15 ]
15 ]
16
16
17
17
18 core_moc_files = qt5.preprocess(moc_headers : core_moc_headers)
18 core_moc_files = qt5.preprocess(moc_headers : core_moc_headers)
19
19
20 core_sources = [
20 core_sources = [
21 'src/Common/DateUtils.cpp',
21 'src/Common/DateUtils.cpp',
22 'src/Common/StringUtils.cpp',
22 'src/Common/StringUtils.cpp',
23 'src/Common/MimeTypesDef.cpp',
23 'src/Common/MimeTypesDef.cpp',
24 'src/Data/ScalarSeries.cpp',
24 'src/Data/ScalarSeries.cpp',
25 'src/Data/SpectrogramSeries.cpp',
25 'src/Data/SpectrogramSeries.cpp',
26 'src/Data/DataSeriesIterator.cpp',
26 'src/Data/DataSeriesIterator.cpp',
27 'src/Data/ArrayDataIterator.cpp',
27 'src/Data/ArrayDataIterator.cpp',
28 'src/Data/VectorSeries.cpp',
28 'src/Data/VectorSeries.cpp',
29 'src/Data/OptionalAxis.cpp',
29 'src/Data/OptionalAxis.cpp',
30 'src/Data/DataSeriesUtils.cpp',
30 'src/Data/DataSeriesUtils.cpp',
31 'src/DataSource/DataSourceController.cpp',
31 'src/DataSource/DataSourceController.cpp',
32 'src/DataSource/DataSourceItem.cpp',
32 'src/DataSource/DataSourceItem.cpp',
33 'src/DataSource/DataSourceItemAction.cpp',
33 'src/DataSource/DataSourceItemAction.cpp',
34 'src/DataSource/DataSourceItemMergeHelper.cpp',
34 'src/Network/NetworkController.cpp',
35 'src/Network/NetworkController.cpp',
35 'src/Plugin/PluginManager.cpp',
36 'src/Plugin/PluginManager.cpp',
36 'src/Settings/SqpSettingsDefs.cpp',
37 'src/Settings/SqpSettingsDefs.cpp',
37 'src/Time/TimeController.cpp',
38 'src/Time/TimeController.cpp',
38 'src/Variable/Variable.cpp',
39 'src/Variable/Variable.cpp',
39 'src/Variable/VariableCacheController.cpp',
40 'src/Variable/VariableCacheController.cpp',
40 'src/Variable/VariableController.cpp',
41 'src/Variable/VariableController.cpp',
41 'src/Variable/VariableAcquisitionWorker.cpp',
42 'src/Variable/VariableAcquisitionWorker.cpp',
42 'src/Variable/VariableSynchronizationGroup.cpp',
43 'src/Variable/VariableSynchronizationGroup.cpp',
43 'src/Variable/VariableModel.cpp',
44 'src/Variable/VariableModel.cpp',
44 'src/Visualization/VisualizationController.cpp'
45 'src/Visualization/VisualizationController.cpp'
45 ]
46 ]
46
47
47 core_inc = include_directories(['include', '../plugin/include'])
48 core_inc = include_directories(['include', '../plugin/include'])
48
49
49 sciqlop_core_lib = library('sciqlopcore',
50 sciqlop_core_lib = library('sciqlopcore',
50 core_sources,
51 core_sources,
51 core_moc_files,
52 core_moc_files,
52 cpp_args : '-DCORE_LIB',
53 cpp_args : '-DCORE_LIB',
53 include_directories : core_inc,
54 include_directories : core_inc,
54 dependencies : [qt5core, qt5network],
55 dependencies : [qt5core, qt5network],
55 install : true
56 install : true
56 )
57 )
57
58
58
59
59 sciqlop_core = declare_dependency(link_with : sciqlop_core_lib,
60 sciqlop_core = declare_dependency(link_with : sciqlop_core_lib,
60 include_directories : core_inc,
61 include_directories : core_inc,
61 dependencies : [qt5core, qt5network])
62 dependencies : [qt5core, qt5network])
62
63
63
64
64 subdir('tests')
65 subdir('tests')
65
66
@@ -1,192 +1,169
1 #include "DataSource/DataSourceController.h"
1 #include "DataSource/DataSourceController.h"
2 #include "DataSource/DataSourceItem.h"
2 #include "DataSource/DataSourceItem.h"
3
3
4 #include <Data/IDataProvider.h>
4 #include <Data/IDataProvider.h>
5
5
6 #include <QMutex>
6 #include <QMutex>
7 #include <QThread>
7 #include <QThread>
8
8
9 #include <QDataStream>
9 #include <QDataStream>
10 #include <QDir>
10 #include <QDir>
11 #include <QStandardPaths>
11 #include <QStandardPaths>
12
12
13 Q_LOGGING_CATEGORY(LOG_DataSourceController, "DataSourceController")
13 Q_LOGGING_CATEGORY(LOG_DataSourceController, "DataSourceController")
14
14
15 namespace {
16
17 /**
18 * Builds the metadata of the variable that will be generated from the loading of an item
19 * @param dataSourceItem the data source item from which to generate the metadata
20 * @return the metadata of the variable
21 */
22 QVariantHash variableMetadata(const DataSourceItem &dataSourceItem)
23 {
24 // Variable metadata contains...
25
26 // ... all metadata of the item
27 auto result = dataSourceItem.data();
28
29 // ... and the name of the plugin, recovered from root item
30 result.insert(QStringLiteral("plugin"), dataSourceItem.rootItem().name());
31
32 return result;
33 }
34
35 } // namespace
36
37 class DataSourceController::DataSourceControllerPrivate {
15 class DataSourceController::DataSourceControllerPrivate {
38 public:
16 public:
39 QMutex m_WorkingMutex;
17 QMutex m_WorkingMutex;
40 /// Data sources registered
18 /// Data sources registered
41 QHash<QUuid, QString> m_DataSources;
19 QHash<QUuid, QString> m_DataSources;
42 /// Data sources structures
20 /// Data sources structures
43 std::map<QUuid, std::unique_ptr<DataSourceItem> > m_DataSourceItems;
21 std::map<QUuid, std::unique_ptr<DataSourceItem> > m_DataSourceItems;
44 /// Data providers registered
22 /// Data providers registered
45 /// @remarks Data providers are stored as shared_ptr as they can be sent to a variable and
23 /// @remarks Data providers are stored as shared_ptr as they can be sent to a variable and
46 /// continue to live without necessarily the data source controller
24 /// continue to live without necessarily the data source controller
47 std::map<QUuid, std::shared_ptr<IDataProvider> > m_DataProviders;
25 std::map<QUuid, std::shared_ptr<IDataProvider> > m_DataProviders;
48
26
49 // Search for the first datasource item matching the specified data
27 // Search for the first datasource item matching the specified data
50 DataSourceItem *findDataSourceItem(const QVariantHash &data)
28 DataSourceItem *findDataSourceItem(const QVariantHash &data)
51 {
29 {
52 DataSourceItem *sourceItem = nullptr;
30 DataSourceItem *sourceItem = nullptr;
53 for (const auto &item : m_DataSourceItems) {
31 for (const auto &item : m_DataSourceItems) {
54 sourceItem = item.second->findItem(data, true);
32 sourceItem = item.second->findItem(data, true);
55 if (sourceItem) {
33 if (sourceItem) {
56 break;
34 break;
57 }
35 }
58 }
36 }
59
37
60 return sourceItem;
38 return sourceItem;
61 }
39 }
62 };
40 };
63
41
64 DataSourceController::DataSourceController(QObject *parent)
42 DataSourceController::DataSourceController(QObject *parent)
65 : impl{spimpl::make_unique_impl<DataSourceControllerPrivate>()}
43 : impl{spimpl::make_unique_impl<DataSourceControllerPrivate>()}
66 {
44 {
67 qCDebug(LOG_DataSourceController()) << tr("DataSourceController construction")
45 qCDebug(LOG_DataSourceController()) << tr("DataSourceController construction")
68 << QThread::currentThread();
46 << QThread::currentThread();
69 }
47 }
70
48
71 DataSourceController::~DataSourceController()
49 DataSourceController::~DataSourceController()
72 {
50 {
73 qCDebug(LOG_DataSourceController()) << tr("DataSourceController destruction")
51 qCDebug(LOG_DataSourceController()) << tr("DataSourceController destruction")
74 << QThread::currentThread();
52 << QThread::currentThread();
75 this->waitForFinish();
53 this->waitForFinish();
76 }
54 }
77
55
78 QUuid DataSourceController::registerDataSource(const QString &dataSourceName) noexcept
56 QUuid DataSourceController::registerDataSource(const QString &dataSourceName) noexcept
79 {
57 {
80 auto dataSourceUid = QUuid::createUuid();
58 auto dataSourceUid = QUuid::createUuid();
81 impl->m_DataSources.insert(dataSourceUid, dataSourceName);
59 impl->m_DataSources.insert(dataSourceUid, dataSourceName);
82
60
83 return dataSourceUid;
61 return dataSourceUid;
84 }
62 }
85
63
86 void DataSourceController::setDataSourceItem(
64 void DataSourceController::setDataSourceItem(
87 const QUuid &dataSourceUid, std::unique_ptr<DataSourceItem> dataSourceItem) noexcept
65 const QUuid &dataSourceUid, std::unique_ptr<DataSourceItem> dataSourceItem) noexcept
88 {
66 {
89 if (!dataSourceItem) {
67 if (!dataSourceItem) {
90 qCWarning(LOG_DataSourceController())
68 qCWarning(LOG_DataSourceController())
91 << tr("Data source item can't be registered (null item)");
69 << tr("Data source item can't be registered (null item)");
92 return;
70 return;
93 }
71 }
94
72
95 if (impl->m_DataSources.contains(dataSourceUid)) {
73 if (impl->m_DataSources.contains(dataSourceUid)) {
96 // The data provider is implicitly converted to a shared_ptr
74 // The data provider is implicitly converted to a shared_ptr
97 impl->m_DataSourceItems.insert(std::make_pair(dataSourceUid, std::move(dataSourceItem)));
75 impl->m_DataSourceItems.insert(std::make_pair(dataSourceUid, std::move(dataSourceItem)));
98
76
99 // Retrieves the data source item to emit the signal with it
77 // Retrieves the data source item to emit the signal with it
100 auto it = impl->m_DataSourceItems.find(dataSourceUid);
78 auto it = impl->m_DataSourceItems.find(dataSourceUid);
101 if (it != impl->m_DataSourceItems.end()) {
79 if (it != impl->m_DataSourceItems.end()) {
102 emit dataSourceItemSet(it->second.get());
80 emit dataSourceItemSet(it->second.get());
103 }
81 }
104 }
82 }
105 else {
83 else {
106 qCWarning(LOG_DataSourceController()) << tr("Can't set data source item for uid %1 : no "
84 qCWarning(LOG_DataSourceController()) << tr("Can't set data source item for uid %1 : no "
107 "data source has been registered with the uid")
85 "data source has been registered with the uid")
108 .arg(dataSourceUid.toString());
86 .arg(dataSourceUid.toString());
109 }
87 }
110 }
88 }
111
89
112 void DataSourceController::setDataProvider(const QUuid &dataSourceUid,
90 void DataSourceController::setDataProvider(const QUuid &dataSourceUid,
113 std::unique_ptr<IDataProvider> dataProvider) noexcept
91 std::unique_ptr<IDataProvider> dataProvider) noexcept
114 {
92 {
115 if (impl->m_DataSources.contains(dataSourceUid)) {
93 if (impl->m_DataSources.contains(dataSourceUid)) {
116 impl->m_DataProviders.insert(std::make_pair(dataSourceUid, std::move(dataProvider)));
94 impl->m_DataProviders.insert(std::make_pair(dataSourceUid, std::move(dataProvider)));
117 }
95 }
118 else {
96 else {
119 qCWarning(LOG_DataSourceController()) << tr("Can't set data provider for uid %1 : no data "
97 qCWarning(LOG_DataSourceController()) << tr("Can't set data provider for uid %1 : no data "
120 "source has been registered with the uid")
98 "source has been registered with the uid")
121 .arg(dataSourceUid.toString());
99 .arg(dataSourceUid.toString());
122 }
100 }
123 }
101 }
124
102
125 void DataSourceController::loadProductItem(const QUuid &dataSourceUid,
103 void DataSourceController::loadProductItem(const QUuid &dataSourceUid,
126 const DataSourceItem &productItem) noexcept
104 const DataSourceItem &productItem) noexcept
127 {
105 {
128 if (productItem.type() == DataSourceItemType::PRODUCT
106 if (productItem.type() == DataSourceItemType::PRODUCT
129 || productItem.type() == DataSourceItemType::COMPONENT) {
107 || productItem.type() == DataSourceItemType::COMPONENT) {
130 /// Retrieves the data provider of the data source (if any)
108 /// Retrieves the data provider of the data source (if any)
131 auto it = impl->m_DataProviders.find(dataSourceUid);
109 auto it = impl->m_DataProviders.find(dataSourceUid);
132 auto dataProvider = (it != impl->m_DataProviders.end()) ? it->second : nullptr;
110 auto dataProvider = (it != impl->m_DataProviders.end()) ? it->second : nullptr;
133
111
134 emit variableCreationRequested(productItem.name(), variableMetadata(productItem),
112 emit variableCreationRequested(productItem.name(), productItem.data(), dataProvider);
135 dataProvider);
136 }
113 }
137 else {
114 else {
138 qCWarning(LOG_DataSourceController()) << tr("Can't load an item that is not a product");
115 qCWarning(LOG_DataSourceController()) << tr("Can't load an item that is not a product");
139 }
116 }
140 }
117 }
141
118
142 QByteArray DataSourceController::mimeDataForProductsData(const QVariantList &productsData)
119 QByteArray DataSourceController::mimeDataForProductsData(const QVariantList &productsData)
143 {
120 {
144 QByteArray encodedData;
121 QByteArray encodedData;
145 QDataStream stream{&encodedData, QIODevice::WriteOnly};
122 QDataStream stream{&encodedData, QIODevice::WriteOnly};
146
123
147 stream << productsData;
124 stream << productsData;
148
125
149 return encodedData;
126 return encodedData;
150 }
127 }
151
128
152 QVariantList DataSourceController::productsDataForMimeData(const QByteArray &mimeData)
129 QVariantList DataSourceController::productsDataForMimeData(const QByteArray &mimeData)
153 {
130 {
154 QDataStream stream{mimeData};
131 QDataStream stream{mimeData};
155
132
156 QVariantList productList;
133 QVariantList productList;
157 stream >> productList;
134 stream >> productList;
158
135
159 return productList;
136 return productList;
160 }
137 }
161
138
162 void DataSourceController::initialize()
139 void DataSourceController::initialize()
163 {
140 {
164 qCDebug(LOG_DataSourceController()) << tr("DataSourceController init")
141 qCDebug(LOG_DataSourceController()) << tr("DataSourceController init")
165 << QThread::currentThread();
142 << QThread::currentThread();
166 impl->m_WorkingMutex.lock();
143 impl->m_WorkingMutex.lock();
167 qCDebug(LOG_DataSourceController()) << tr("DataSourceController init END");
144 qCDebug(LOG_DataSourceController()) << tr("DataSourceController init END");
168 }
145 }
169
146
170 void DataSourceController::finalize()
147 void DataSourceController::finalize()
171 {
148 {
172 impl->m_WorkingMutex.unlock();
149 impl->m_WorkingMutex.unlock();
173 }
150 }
174
151
175 void DataSourceController::requestVariable(const QVariantHash &productData)
152 void DataSourceController::requestVariable(const QVariantHash &productData)
176 {
153 {
177 auto sourceItem = impl->findDataSourceItem(productData);
154 auto sourceItem = impl->findDataSourceItem(productData);
178
155
179 if (sourceItem) {
156 if (sourceItem) {
180 auto sourceName = sourceItem->rootItem().name();
157 auto sourceName = sourceItem->rootItem().name();
181 auto sourceId = impl->m_DataSources.key(sourceName);
158 auto sourceId = impl->m_DataSources.key(sourceName);
182 loadProductItem(sourceId, *sourceItem);
159 loadProductItem(sourceId, *sourceItem);
183 }
160 }
184 else {
161 else {
185 qCWarning(LOG_DataSourceController()) << tr("requestVariable, product data not found");
162 qCWarning(LOG_DataSourceController()) << tr("requestVariable, product data not found");
186 }
163 }
187 }
164 }
188
165
189 void DataSourceController::waitForFinish()
166 void DataSourceController::waitForFinish()
190 {
167 {
191 QMutexLocker locker{&impl->m_WorkingMutex};
168 QMutexLocker locker{&impl->m_WorkingMutex};
192 }
169 }
@@ -1,162 +1,186
1 #include <DataSource/DataSourceItem.h>
1 #include <DataSource/DataSourceItem.h>
2 #include <DataSource/DataSourceItemAction.h>
2 #include <DataSource/DataSourceItemAction.h>
3 #include <DataSource/DataSourceItemMergeHelper.h>
3
4
4 #include <QVector>
5 #include <QVector>
5
6
6 const QString DataSourceItem::NAME_DATA_KEY = QStringLiteral("name");
7 const QString DataSourceItem::NAME_DATA_KEY = QStringLiteral("name");
8 const QString DataSourceItem::PLUGIN_DATA_KEY = QStringLiteral("plugin");
7
9
8 struct DataSourceItem::DataSourceItemPrivate {
10 struct DataSourceItem::DataSourceItemPrivate {
9 explicit DataSourceItemPrivate(DataSourceItemType type, QVariantHash data)
11 explicit DataSourceItemPrivate(DataSourceItemType type, QVariantHash data)
10 : m_Parent{nullptr}, m_Children{}, m_Type{type}, m_Data{std::move(data)}, m_Actions{}
12 : m_Parent{nullptr}, m_Children{}, m_Type{type}, m_Data{std::move(data)}, m_Actions{}
11 {
13 {
12 }
14 }
13
15
14 DataSourceItem *m_Parent;
16 DataSourceItem *m_Parent;
15 std::vector<std::unique_ptr<DataSourceItem> > m_Children;
17 std::vector<std::unique_ptr<DataSourceItem> > m_Children;
16 DataSourceItemType m_Type;
18 DataSourceItemType m_Type;
17 QVariantHash m_Data;
19 QVariantHash m_Data;
18 std::vector<std::unique_ptr<DataSourceItemAction> > m_Actions;
20 std::vector<std::unique_ptr<DataSourceItemAction> > m_Actions;
19 };
21 };
20
22
21 DataSourceItem::DataSourceItem(DataSourceItemType type, const QString &name)
23 DataSourceItem::DataSourceItem(DataSourceItemType type, const QString &name)
22 : DataSourceItem{type, QVariantHash{{NAME_DATA_KEY, name}}}
24 : DataSourceItem{type, QVariantHash{{NAME_DATA_KEY, name}}}
23 {
25 {
24 }
26 }
25
27
26 DataSourceItem::DataSourceItem(DataSourceItemType type, QVariantHash data)
28 DataSourceItem::DataSourceItem(DataSourceItemType type, QVariantHash data)
27 : impl{spimpl::make_unique_impl<DataSourceItemPrivate>(type, std::move(data))}
29 : impl{spimpl::make_unique_impl<DataSourceItemPrivate>(type, std::move(data))}
28 {
30 {
29 }
31 }
30
32
33 std::unique_ptr<DataSourceItem> DataSourceItem::clone() const
34 {
35 auto result = std::make_unique<DataSourceItem>(impl->m_Type, impl->m_Data);
36
37 // Clones children
38 for (const auto &child : impl->m_Children) {
39 result->appendChild(std::move(child->clone()));
40 }
41
42 // Clones actions
43 for (const auto &action : impl->m_Actions) {
44 result->addAction(std::move(action->clone()));
45 }
46
47 return result;
48 }
49
31 QVector<DataSourceItemAction *> DataSourceItem::actions() const noexcept
50 QVector<DataSourceItemAction *> DataSourceItem::actions() const noexcept
32 {
51 {
33 auto result = QVector<DataSourceItemAction *>{};
52 auto result = QVector<DataSourceItemAction *>{};
34
53
35 std::transform(std::cbegin(impl->m_Actions), std::cend(impl->m_Actions),
54 std::transform(std::cbegin(impl->m_Actions), std::cend(impl->m_Actions),
36 std::back_inserter(result), [](const auto &action) { return action.get(); });
55 std::back_inserter(result), [](const auto &action) { return action.get(); });
37
56
38 return result;
57 return result;
39 }
58 }
40
59
41 void DataSourceItem::addAction(std::unique_ptr<DataSourceItemAction> action) noexcept
60 void DataSourceItem::addAction(std::unique_ptr<DataSourceItemAction> action) noexcept
42 {
61 {
43 action->setDataSourceItem(this);
62 action->setDataSourceItem(this);
44 impl->m_Actions.push_back(std::move(action));
63 impl->m_Actions.push_back(std::move(action));
45 }
64 }
46
65
47 void DataSourceItem::appendChild(std::unique_ptr<DataSourceItem> child) noexcept
66 void DataSourceItem::appendChild(std::unique_ptr<DataSourceItem> child) noexcept
48 {
67 {
49 child->impl->m_Parent = this;
68 child->impl->m_Parent = this;
50 impl->m_Children.push_back(std::move(child));
69 impl->m_Children.push_back(std::move(child));
51 }
70 }
52
71
53 DataSourceItem *DataSourceItem::child(int childIndex) const noexcept
72 DataSourceItem *DataSourceItem::child(int childIndex) const noexcept
54 {
73 {
55 if (childIndex < 0 || childIndex >= childCount()) {
74 if (childIndex < 0 || childIndex >= childCount()) {
56 return nullptr;
75 return nullptr;
57 }
76 }
58 else {
77 else {
59 return impl->m_Children.at(childIndex).get();
78 return impl->m_Children.at(childIndex).get();
60 }
79 }
61 }
80 }
62
81
63 int DataSourceItem::childCount() const noexcept
82 int DataSourceItem::childCount() const noexcept
64 {
83 {
65 return impl->m_Children.size();
84 return impl->m_Children.size();
66 }
85 }
67
86
68 QVariant DataSourceItem::data(const QString &key) const noexcept
87 QVariant DataSourceItem::data(const QString &key) const noexcept
69 {
88 {
70 return impl->m_Data.value(key);
89 return impl->m_Data.value(key);
71 }
90 }
72
91
73 QVariantHash DataSourceItem::data() const noexcept
92 QVariantHash DataSourceItem::data() const noexcept
74 {
93 {
75 return impl->m_Data;
94 return impl->m_Data;
76 }
95 }
77
96
97 void DataSourceItem::merge(const DataSourceItem &item)
98 {
99 DataSourceItemMergeHelper::merge(item, *this);
100 }
101
78 bool DataSourceItem::isRoot() const noexcept
102 bool DataSourceItem::isRoot() const noexcept
79 {
103 {
80 return impl->m_Parent == nullptr;
104 return impl->m_Parent == nullptr;
81 }
105 }
82
106
83 QString DataSourceItem::name() const noexcept
107 QString DataSourceItem::name() const noexcept
84 {
108 {
85 return data(NAME_DATA_KEY).toString();
109 return data(NAME_DATA_KEY).toString();
86 }
110 }
87
111
88 DataSourceItem *DataSourceItem::parentItem() const noexcept
112 DataSourceItem *DataSourceItem::parentItem() const noexcept
89 {
113 {
90 return impl->m_Parent;
114 return impl->m_Parent;
91 }
115 }
92
116
93 const DataSourceItem &DataSourceItem::rootItem() const noexcept
117 const DataSourceItem &DataSourceItem::rootItem() const noexcept
94 {
118 {
95 return isRoot() ? *this : parentItem()->rootItem();
119 return isRoot() ? *this : parentItem()->rootItem();
96 }
120 }
97
121
98 void DataSourceItem::setData(const QString &key, const QVariant &value, bool append) noexcept
122 void DataSourceItem::setData(const QString &key, const QVariant &value, bool append) noexcept
99 {
123 {
100 auto it = impl->m_Data.constFind(key);
124 auto it = impl->m_Data.constFind(key);
101 if (append && it != impl->m_Data.constEnd()) {
125 if (append && it != impl->m_Data.constEnd()) {
102 // Case of an existing value to which we want to add to the new value
126 // Case of an existing value to which we want to add to the new value
103 if (it->canConvert<QVariantList>()) {
127 if (it->canConvert<QVariantList>()) {
104 auto variantList = it->value<QVariantList>();
128 auto variantList = it->value<QVariantList>();
105 variantList.append(value);
129 variantList.append(value);
106
130
107 impl->m_Data.insert(key, variantList);
131 impl->m_Data.insert(key, variantList);
108 }
132 }
109 else {
133 else {
110 impl->m_Data.insert(key, QVariantList{*it, value});
134 impl->m_Data.insert(key, QVariantList{*it, value});
111 }
135 }
112 }
136 }
113 else {
137 else {
114 // Other cases :
138 // Other cases :
115 // - new value in map OR
139 // - new value in map OR
116 // - replacement of an existing value (not appending)
140 // - replacement of an existing value (not appending)
117 impl->m_Data.insert(key, value);
141 impl->m_Data.insert(key, value);
118 }
142 }
119 }
143 }
120
144
121 DataSourceItemType DataSourceItem::type() const noexcept
145 DataSourceItemType DataSourceItem::type() const noexcept
122 {
146 {
123 return impl->m_Type;
147 return impl->m_Type;
124 }
148 }
125
149
126 DataSourceItem *DataSourceItem::findItem(const QVariantHash &data, bool recursive)
150 DataSourceItem *DataSourceItem::findItem(const QVariantHash &data, bool recursive)
127 {
151 {
128 for (const auto &child : impl->m_Children) {
152 for (const auto &child : impl->m_Children) {
129 if (child->impl->m_Data == data) {
153 if (child->impl->m_Data == data) {
130 return child.get();
154 return child.get();
131 }
155 }
132
156
133 if (recursive) {
157 if (recursive) {
134 if (auto foundItem = child->findItem(data, true)) {
158 if (auto foundItem = child->findItem(data, true)) {
135 return foundItem;
159 return foundItem;
136 }
160 }
137 }
161 }
138 }
162 }
139
163
140 return nullptr;
164 return nullptr;
141 }
165 }
142
166
143 bool DataSourceItem::operator==(const DataSourceItem &other)
167 bool DataSourceItem::operator==(const DataSourceItem &other)
144 {
168 {
145 // Compares items' attributes
169 // Compares items' attributes
146 if (std::tie(impl->m_Type, impl->m_Data) == std::tie(other.impl->m_Type, other.impl->m_Data)) {
170 if (std::tie(impl->m_Type, impl->m_Data) == std::tie(other.impl->m_Type, other.impl->m_Data)) {
147 // Compares contents of items' children
171 // Compares contents of items' children
148 return std::equal(std::cbegin(impl->m_Children), std::cend(impl->m_Children),
172 return std::equal(std::cbegin(impl->m_Children), std::cend(impl->m_Children),
149 std::cbegin(other.impl->m_Children),
173 std::cbegin(other.impl->m_Children), std::cend(other.impl->m_Children),
150 [](const auto &itemChild, const auto &otherChild) {
174 [](const auto &itemChild, const auto &otherChild) {
151 return *itemChild == *otherChild;
175 return *itemChild == *otherChild;
152 });
176 });
153 }
177 }
154 else {
178 else {
155 return false;
179 return false;
156 }
180 }
157 }
181 }
158
182
159 bool DataSourceItem::operator!=(const DataSourceItem &other)
183 bool DataSourceItem::operator!=(const DataSourceItem &other)
160 {
184 {
161 return !(*this == other);
185 return !(*this == other);
162 }
186 }
@@ -1,44 +1,49
1 #include <DataSource/DataSourceItemAction.h>
1 #include <DataSource/DataSourceItemAction.h>
2
2
3 #include <functional>
3 #include <functional>
4
4
5 Q_LOGGING_CATEGORY(LOG_DataSourceItemAction, "DataSourceItemAction")
5 Q_LOGGING_CATEGORY(LOG_DataSourceItemAction, "DataSourceItemAction")
6
6
7 struct DataSourceItemAction::DataSourceItemActionPrivate {
7 struct DataSourceItemAction::DataSourceItemActionPrivate {
8 explicit DataSourceItemActionPrivate(const QString &name,
8 explicit DataSourceItemActionPrivate(const QString &name,
9 DataSourceItemAction::ExecuteFunction fun)
9 DataSourceItemAction::ExecuteFunction fun)
10 : m_Name{name}, m_Fun{std::move(fun)}, m_DataSourceItem{nullptr}
10 : m_Name{name}, m_Fun{std::move(fun)}, m_DataSourceItem{nullptr}
11 {
11 {
12 }
12 }
13
13
14 QString m_Name;
14 QString m_Name;
15 DataSourceItemAction::ExecuteFunction m_Fun;
15 DataSourceItemAction::ExecuteFunction m_Fun;
16 /// Item associated to the action (can be null, in which case the action will not be executed)
16 /// Item associated to the action (can be null, in which case the action will not be executed)
17 DataSourceItem *m_DataSourceItem;
17 DataSourceItem *m_DataSourceItem;
18 };
18 };
19
19
20 DataSourceItemAction::DataSourceItemAction(const QString &name, ExecuteFunction fun)
20 DataSourceItemAction::DataSourceItemAction(const QString &name, ExecuteFunction fun)
21 : impl{spimpl::make_unique_impl<DataSourceItemActionPrivate>(name, std::move(fun))}
21 : impl{spimpl::make_unique_impl<DataSourceItemActionPrivate>(name, std::move(fun))}
22 {
22 {
23 }
23 }
24
24
25 std::unique_ptr<DataSourceItemAction> DataSourceItemAction::clone() const
26 {
27 return std::make_unique<DataSourceItemAction>(impl->m_Name, impl->m_Fun);
28 }
29
25 QString DataSourceItemAction::name() const noexcept
30 QString DataSourceItemAction::name() const noexcept
26 {
31 {
27 return impl->m_Name;
32 return impl->m_Name;
28 }
33 }
29
34
30 void DataSourceItemAction::setDataSourceItem(DataSourceItem *dataSourceItem) noexcept
35 void DataSourceItemAction::setDataSourceItem(DataSourceItem *dataSourceItem) noexcept
31 {
36 {
32 impl->m_DataSourceItem = dataSourceItem;
37 impl->m_DataSourceItem = dataSourceItem;
33 }
38 }
34
39
35 void DataSourceItemAction::execute()
40 void DataSourceItemAction::execute()
36 {
41 {
37 if (impl->m_DataSourceItem) {
42 if (impl->m_DataSourceItem) {
38 impl->m_Fun(*impl->m_DataSourceItem);
43 impl->m_Fun(*impl->m_DataSourceItem);
39 }
44 }
40 else {
45 else {
41 qCDebug(LOG_DataSourceItemAction())
46 qCDebug(LOG_DataSourceItemAction())
42 << tr("Can't execute action : no item has been associated");
47 << tr("Can't execute action : no item has been associated");
43 }
48 }
44 }
49 }
@@ -1,33 +1,36
1
1
2
2
3 tests = [
3 tests = [
4 [['Common/TestStringUtils.cpp'],'test_string_utils','StringUtils test'],
4 [['Common/TestStringUtils.cpp'],'test_string_utils','StringUtils test'],
5 [['Data/TestScalarSeries.cpp'],'test_scalar','ScalarSeries test'],
5 [['Data/TestScalarSeries.cpp'],'test_scalar','ScalarSeries test'],
6 [['Data/TestSpectrogramSeries.cpp'],'test_spectrogram','SpectrogramSeries test'],
6 [['Data/TestSpectrogramSeries.cpp'],'test_spectrogram','SpectrogramSeries test'],
7 [['Data/TestVectorSeries.cpp'],'test_vector','VectorSeries test'],
7 [['Data/TestVectorSeries.cpp'],'test_vector','VectorSeries test'],
8 [['Data/TestOneDimArrayData.cpp'],'test_1d','One Dim Array test'],
8 [['Data/TestOneDimArrayData.cpp'],'test_1d','One Dim Array test'],
9 [['Data/TestOptionalAxis.cpp'],'test_optional_axis','OptionalAxis test'],
9 [['Data/TestOptionalAxis.cpp'],'test_optional_axis','OptionalAxis test'],
10 [['Data/TestTwoDimArrayData.cpp'],'test_2d','Two Dim Array test'],
10 [['Data/TestTwoDimArrayData.cpp'],'test_2d','Two Dim Array test'],
11 [['Data/TestDataSeriesUtils.cpp'],'test_dataseries_util','Data series utils test'],
11 [['Data/TestDataSeriesUtils.cpp'],'test_dataseries_util','Data series utils test'],
12 [['DataSource/TestDataSourceController.cpp'],'test_data_source','DataSourceController test'],
12 [['DataSource/TestDataSourceController.cpp'],'test_data_source','DataSourceController test'],
13 [['DataSource/TestDataSourceItem.cpp'],'test_data_source_item','DataSourceItem test'],
13 [['Variable/TestVariableCacheController.cpp'],'test_variable_cache','VariableCacheController test'],
14 [['Variable/TestVariableCacheController.cpp'],'test_variable_cache','VariableCacheController test'],
14 [['Variable/TestVariable.cpp'],'test_variable','Variable test'],
15 [['Variable/TestVariable.cpp'],'test_variable','Variable test'],
15 [['Variable/TestVariableSync.cpp'],'test_variable_sync','Variable synchronization test']
16 [['Variable/TestVariableSync.cpp'],'test_variable_sync','Variable synchronization test']
16 ]
17 ]
17
18
18 amdatest_sources = [
19 amdatest_sources = [
19 'Data/DataSeriesBuilders.h',
20 'Data/DataSeriesBuilders.h',
20 'Data/DataSeriesBuilders.cpp',
21 'Data/DataSeriesBuilders.cpp',
21 'Data/DataSeriesTestsUtils.h',
22 'Data/DataSeriesTestsUtils.h',
22 'Data/DataSeriesTestsUtils.cpp'
23 'Data/DataSeriesTestsUtils.cpp',
24 'DataSource/DataSourceItemBuilder.h',
25 'DataSource/DataSourceItemBuilder.cpp'
23 ]
26 ]
24
27
25 foreach unit_test : tests
28 foreach unit_test : tests
26 test_moc_files = qt5.preprocess(moc_sources : unit_test[0])
29 test_moc_files = qt5.preprocess(moc_sources : unit_test[0])
27 test_exe = executable(unit_test[1],unit_test[0] , test_moc_files,
30 test_exe = executable(unit_test[1],unit_test[0] , test_moc_files,
28 dependencies : [sciqlop_core, qt5test],
31 dependencies : [sciqlop_core, qt5test],
29 cpp_args : ['-DCORE_TESTS_RESOURCES_DIR="'+meson.current_source_dir()+'/../tests-resources"'],
32 cpp_args : ['-DCORE_TESTS_RESOURCES_DIR="'+meson.current_source_dir()+'/../tests-resources"'],
30 sources : [amdatest_sources])
33 sources : [amdatest_sources])
31 test(unit_test[2], test_exe, args: ['-teamcity', '-o', '@0@.teamcity.txt'.format(unit_test[1])])
34 test(unit_test[2], test_exe, args: ['-teamcity', '-o', '@0@.teamcity.txt'.format(unit_test[1])])
32 endforeach
35 endforeach
33
36
@@ -1,42 +1,47
1 #ifndef SCIQLOP_DATASOURCEWIDGET_H
1 #ifndef SCIQLOP_DATASOURCEWIDGET_H
2 #define SCIQLOP_DATASOURCEWIDGET_H
2 #define SCIQLOP_DATASOURCEWIDGET_H
3
3
4 #include <QWidget>
4 #include <QWidget>
5
5
6 #include <memory>
7
6 namespace Ui {
8 namespace Ui {
7 class DataSourceWidget;
9 class DataSourceWidget;
8 } // Ui
10 } // Ui
9
11
10 class DataSourceItem;
12 class DataSourceItem;
11
13
12 /**
14 /**
13 * @brief The DataSourceWidget handles the graphical representation (as a tree) of the data sources
15 * @brief The DataSourceWidget handles the graphical representation (as a tree) of the data sources
14 * attached to SciQlop.
16 * attached to SciQlop.
15 */
17 */
16 class DataSourceWidget : public QWidget {
18 class DataSourceWidget : public QWidget {
17 Q_OBJECT
19 Q_OBJECT
18
20
19 public:
21 public:
20 explicit DataSourceWidget(QWidget *parent = 0);
22 explicit DataSourceWidget(QWidget *parent = 0);
21 virtual ~DataSourceWidget() noexcept;
23 virtual ~DataSourceWidget() noexcept;
22
24
23 public slots:
25 public slots:
24 /**
26 /**
25 * Adds a data source. An item associated to the data source is created and then added to the
27 * Adds a data source. An item associated to the data source is created and then added to the
26 * representation tree
28 * representation tree
27 * @param dataSource the data source to add. The pointer has to be not null
29 * @param dataSource the data source to add. The pointer has to be not null
28 */
30 */
29 void addDataSource(DataSourceItem *dataSource) noexcept;
31 void addDataSource(DataSourceItem *dataSource) noexcept;
30
32
31 private:
33 private:
34 void updateTreeWidget() noexcept;
35
32 Ui::DataSourceWidget *ui;
36 Ui::DataSourceWidget *ui;
37 std::unique_ptr<DataSourceItem> m_Root;
33
38
34 private slots:
39 private slots:
35 /// Slot called when the filtering text has changed
40 /// Slot called when the filtering text has changed
36 void filterChanged(const QString &text) noexcept;
41 void filterChanged(const QString &text) noexcept;
37
42
38 /// Slot called when right clicking on an item in the tree (displays a menu)
43 /// Slot called when right clicking on an item in the tree (displays a menu)
39 void onTreeMenuRequested(const QPoint &pos) noexcept;
44 void onTreeMenuRequested(const QPoint &pos) noexcept;
40 };
45 };
41
46
42 #endif // SCIQLOP_DATASOURCEWIDGET_H
47 #endif // SCIQLOP_DATASOURCEWIDGET_H
@@ -1,185 +1,225
1 #include <DataSource/DataSourceItem.h>
1 #include <DataSource/DataSourceItem.h>
2 #include <DataSource/DataSourceItemAction.h>
2 #include <DataSource/DataSourceItemAction.h>
3 #include <DataSource/DataSourceTreeWidgetItem.h>
3 #include <DataSource/DataSourceTreeWidgetItem.h>
4
4
5 #include <QAction>
5 #include <QAction>
6
6
7 Q_LOGGING_CATEGORY(LOG_DataSourceTreeWidgetItem, "DataSourceTreeWidgetItem")
7 Q_LOGGING_CATEGORY(LOG_DataSourceTreeWidgetItem, "DataSourceTreeWidgetItem")
8
8
9 namespace {
9 namespace {
10
10
11 // Column indexes
11 // Column indexes
12 const auto NAME_COLUMN = 0;
12 const auto NAME_COLUMN = 0;
13
13
14 /**
15 * Generates the full name of an item.
16 *
17 * The full name of an item is its name possibly suffixed by the name of its plugin, in case there
18 * are items of the same name in its relatives
19 * @param item the item for which to generate the complete name
20 * @return the complete name of the item
21 */
22 QString completeName(const DataSourceItem &item)
23 {
24 auto name = item.name();
25
26 if (item.type() == DataSourceItemType::NODE) {
27 return name;
28 }
29
30 auto parentItem = item.parentItem();
31 if (!parentItem) {
32 return name;
33 }
34
35 // Finds in item's relatives items that have the same name
36 bool foundSameName = false;
37 for (auto i = 0, count = parentItem->childCount(); i < count && !foundSameName; ++i) {
38 auto child = parentItem->child(i);
39 foundSameName = child != &item
40 && QString::compare(child->name(), item.name(), Qt::CaseInsensitive) == 0;
41 }
42
43 // If the name of the item is not unique, it is completed by the plugin suffix
44 return foundSameName
45 ? QString{"%1 (%2)"}.arg(name, item.data(DataSourceItem::PLUGIN_DATA_KEY).toString())
46 : name;
47 }
48
14 QIcon itemIcon(const DataSourceItem *dataSource)
49 QIcon itemIcon(const DataSourceItem *dataSource)
15 {
50 {
16 if (dataSource) {
51 if (dataSource) {
17 auto dataSourceType = dataSource->type();
52 auto dataSourceType = dataSource->type();
18 switch (dataSourceType) {
53 switch (dataSourceType) {
19 case DataSourceItemType::NODE: {
54 case DataSourceItemType::NODE: {
20 return dataSource->isRoot() ? QIcon{":/icones/dataSourceRoot.png"}
55 return dataSource->isRoot() ? QIcon{":/icones/dataSourceRoot.png"}
21 : QIcon{":/icones/dataSourceNode.png"};
56 : QIcon{":/icones/dataSourceNode.png"};
22 }
57 }
23 case DataSourceItemType::PRODUCT:
58 case DataSourceItemType::PRODUCT:
24 return QIcon{":/icones/dataSourceProduct.png"};
59 return QIcon{":/icones/dataSourceProduct.png"};
25 case DataSourceItemType::COMPONENT:
60 case DataSourceItemType::COMPONENT:
26 return QIcon{":/icones/dataSourceComponent.png"};
61 return QIcon{":/icones/dataSourceComponent.png"};
27 default:
62 default:
28 // No action
63 // No action
29 break;
64 break;
30 }
65 }
31
66
32 qCWarning(LOG_DataSourceTreeWidgetItem())
67 qCWarning(LOG_DataSourceTreeWidgetItem())
33 << QObject::tr("Can't set data source icon : unknown data source type");
68 << QObject::tr("Can't set data source icon : unknown data source type");
34 }
69 }
35 else {
70 else {
36 qCCritical(LOG_DataSourceTreeWidgetItem())
71 qCCritical(LOG_DataSourceTreeWidgetItem())
37 << QObject::tr("Can't set data source icon : the data source is null");
72 << QObject::tr("Can't set data source icon : the data source is null");
38 }
73 }
39
74
40 // Default cases
75 // Default cases
41 return QIcon{};
76 return QIcon{};
42 }
77 }
43
78
44 /// @return the tooltip text for a variant. The text depends on whether the data is a simple variant
79 /// @return the tooltip text for a variant. The text depends on whether the data is a simple variant
45 /// or a list of variants
80 /// or a list of variants
46 QString tooltipValue(const QVariant &variant) noexcept
81 QString tooltipValue(const QVariant &variant) noexcept
47 {
82 {
48 // If the variant is a list of variants, the text of the tooltip is of the form: {val1, val2,
83 // If the variant is a list of variants, the text of the tooltip is of the form: {val1, val2,
49 // ...}
84 // ...}
50 if (variant.canConvert<QVariantList>()) {
85 if (variant.canConvert<QVariantList>()) {
51 auto valueString = QStringLiteral("{");
86 auto valueString = QStringLiteral("{");
52
87
53 auto variantList = variant.value<QVariantList>();
88 auto variantList = variant.value<QVariantList>();
54 for (auto it = variantList.cbegin(), end = variantList.cend(); it != end; ++it) {
89 for (auto it = variantList.cbegin(), end = variantList.cend(); it != end; ++it) {
55 valueString.append(it->toString());
90 valueString.append(it->toString());
56
91
57 if (std::distance(it, end) != 1) {
92 if (std::distance(it, end) != 1) {
58 valueString.append(", ");
93 valueString.append(", ");
59 }
94 }
60 }
95 }
61
96
62 valueString.append(QStringLiteral("}"));
97 valueString.append(QStringLiteral("}"));
63
98
64 return valueString;
99 return valueString;
65 }
100 }
66 else {
101 else {
67 return variant.toString();
102 return variant.toString();
68 }
103 }
69 }
104 }
70
105
71 QString itemTooltip(const DataSourceItem *dataSource) noexcept
106 QString itemTooltip(const DataSourceItem *dataSource) noexcept
72 {
107 {
73 // The tooltip displays all item's data
108 // The tooltip displays all item's data
74 if (dataSource) {
109 if (dataSource) {
75 auto result = QString{};
110 auto result = QString{};
76
111
77 const auto &data = dataSource->data();
112 const auto &data = dataSource->data();
78 for (auto it = data.cbegin(), end = data.cend(); it != end; ++it) {
113 for (auto it = data.cbegin(), end = data.cend(); it != end; ++it) {
79 result.append(QString{"<b>%1:</b> %2<br/>"}.arg(it.key(), tooltipValue(it.value())));
114 result.append(QString{"<b>%1:</b> %2<br/>"}.arg(it.key(), tooltipValue(it.value())));
80 }
115 }
81
116
82 return result;
117 return result;
83 }
118 }
84 else {
119 else {
85 qCCritical(LOG_DataSourceTreeWidgetItem())
120 qCCritical(LOG_DataSourceTreeWidgetItem())
86 << QObject::tr("Can't set data source tooltip : the data source is null");
121 << QObject::tr("Can't set data source tooltip : the data source is null");
87
122
88 return QString{};
123 return QString{};
89 }
124 }
90 }
125 }
91
126
92 } // namespace
127 } // namespace
93
128
94 struct DataSourceTreeWidgetItem::DataSourceTreeWidgetItemPrivate {
129 struct DataSourceTreeWidgetItem::DataSourceTreeWidgetItemPrivate {
95 explicit DataSourceTreeWidgetItemPrivate(const DataSourceItem *data) : m_Data{data} {}
130 explicit DataSourceTreeWidgetItemPrivate(const DataSourceItem *data)
131 : m_Data{data}, m_Name{completeName(*m_Data)}
132 {
133 }
96
134
97 /// Model used to retrieve data source information
135 /// Model used to retrieve data source information
98 const DataSourceItem *m_Data;
136 const DataSourceItem *m_Data;
137 /// Name displayed
138 QString m_Name;
99 /// Actions associated to the item. The parent of the item (QTreeWidget) takes the ownership of
139 /// Actions associated to the item. The parent of the item (QTreeWidget) takes the ownership of
100 /// the actions
140 /// the actions
101 QList<QAction *> m_Actions;
141 QList<QAction *> m_Actions;
102 };
142 };
103
143
104 DataSourceTreeWidgetItem::DataSourceTreeWidgetItem(const DataSourceItem *data, int type)
144 DataSourceTreeWidgetItem::DataSourceTreeWidgetItem(const DataSourceItem *data, int type)
105 : DataSourceTreeWidgetItem{nullptr, data, type}
145 : DataSourceTreeWidgetItem{nullptr, data, type}
106 {
146 {
107 }
147 }
108
148
109 DataSourceTreeWidgetItem::DataSourceTreeWidgetItem(QTreeWidget *parent, const DataSourceItem *data,
149 DataSourceTreeWidgetItem::DataSourceTreeWidgetItem(QTreeWidget *parent, const DataSourceItem *data,
110 int type)
150 int type)
111 : QTreeWidgetItem{parent, type},
151 : QTreeWidgetItem{parent, type},
112 impl{spimpl::make_unique_impl<DataSourceTreeWidgetItemPrivate>(data)}
152 impl{spimpl::make_unique_impl<DataSourceTreeWidgetItemPrivate>(data)}
113 {
153 {
114 // Sets the icon and the tooltip depending on the data source
154 // Sets the icon and the tooltip depending on the data source
115 setIcon(0, itemIcon(impl->m_Data));
155 setIcon(0, itemIcon(impl->m_Data));
116 setToolTip(0, itemTooltip(impl->m_Data));
156 setToolTip(0, itemTooltip(impl->m_Data));
117
157
118 // Generates tree actions based on the item actions
158 // Generates tree actions based on the item actions
119 auto createTreeAction = [this, &parent](const auto &itemAction) {
159 auto createTreeAction = [this, &parent](const auto &itemAction) {
120 auto treeAction = new QAction{itemAction->name(), parent};
160 auto treeAction = new QAction{itemAction->name(), parent};
121
161
122 // Executes item action when tree action is triggered
162 // Executes item action when tree action is triggered
123 QObject::connect(treeAction, &QAction::triggered, itemAction,
163 QObject::connect(treeAction, &QAction::triggered, itemAction,
124 &DataSourceItemAction::execute);
164 &DataSourceItemAction::execute);
125
165
126 return treeAction;
166 return treeAction;
127 };
167 };
128
168
129 auto itemActions = impl->m_Data->actions();
169 auto itemActions = impl->m_Data->actions();
130 std::transform(std::cbegin(itemActions), std::cend(itemActions),
170 std::transform(std::cbegin(itemActions), std::cend(itemActions),
131 std::back_inserter(impl->m_Actions), createTreeAction);
171 std::back_inserter(impl->m_Actions), createTreeAction);
132
172
133 // Sets the flags of the items
173 // Sets the flags of the items
134 auto flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
174 auto flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
135 if (data->type() == DataSourceItemType::COMPONENT
175 if (data->type() == DataSourceItemType::COMPONENT
136 || data->type() == DataSourceItemType::PRODUCT) {
176 || data->type() == DataSourceItemType::PRODUCT) {
137 flags |= Qt::ItemIsDragEnabled;
177 flags |= Qt::ItemIsDragEnabled;
138 }
178 }
139
179
140 setFlags(flags);
180 setFlags(flags);
141 }
181 }
142
182
143 const DataSourceItem *DataSourceTreeWidgetItem::data() const
183 const DataSourceItem *DataSourceTreeWidgetItem::data() const
144 {
184 {
145 return impl->m_Data;
185 return impl->m_Data;
146 }
186 }
147
187
148 QVariant DataSourceTreeWidgetItem::data(int column, int role) const
188 QVariant DataSourceTreeWidgetItem::data(int column, int role) const
149 {
189 {
150 if (role == Qt::DisplayRole) {
190 if (role == Qt::DisplayRole) {
151 if (impl->m_Data) {
191 if (impl->m_Data) {
152 switch (column) {
192 switch (column) {
153 case NAME_COLUMN:
193 case NAME_COLUMN:
154 return impl->m_Data->name();
194 return impl->m_Name;
155 default:
195 default:
156 // No action
196 // No action
157 break;
197 break;
158 }
198 }
159
199
160 qCWarning(LOG_DataSourceTreeWidgetItem())
200 qCWarning(LOG_DataSourceTreeWidgetItem())
161 << QObject::tr("Can't get data (unknown column %1)").arg(column);
201 << QObject::tr("Can't get data (unknown column %1)").arg(column);
162 }
202 }
163 else {
203 else {
164 qCCritical(LOG_DataSourceTreeWidgetItem()) << QObject::tr("Can't get data (null item)");
204 qCCritical(LOG_DataSourceTreeWidgetItem()) << QObject::tr("Can't get data (null item)");
165 }
205 }
166
206
167 return QVariant{};
207 return QVariant{};
168 }
208 }
169 else {
209 else {
170 return QTreeWidgetItem::data(column, role);
210 return QTreeWidgetItem::data(column, role);
171 }
211 }
172 }
212 }
173
213
174 void DataSourceTreeWidgetItem::setData(int column, int role, const QVariant &value)
214 void DataSourceTreeWidgetItem::setData(int column, int role, const QVariant &value)
175 {
215 {
176 // Data can't be changed by edition
216 // Data can't be changed by edition
177 if (role != Qt::EditRole) {
217 if (role != Qt::EditRole) {
178 QTreeWidgetItem::setData(column, role, value);
218 QTreeWidgetItem::setData(column, role, value);
179 }
219 }
180 }
220 }
181
221
182 QList<QAction *> DataSourceTreeWidgetItem::actions() const noexcept
222 QList<QAction *> DataSourceTreeWidgetItem::actions() const noexcept
183 {
223 {
184 return impl->m_Actions;
224 return impl->m_Actions;
185 }
225 }
@@ -1,100 +1,123
1 #include <DataSource/DataSourceWidget.h>
1 #include <DataSource/DataSourceWidget.h>
2
2
3 #include <ui_DataSourceWidget.h>
3 #include <ui_DataSourceWidget.h>
4
4
5 #include <DataSource/DataSourceItem.h>
5 #include <DataSource/DataSourceItem.h>
6 #include <DataSource/DataSourceTreeWidgetHelper.h>
6 #include <DataSource/DataSourceTreeWidgetHelper.h>
7 #include <DataSource/DataSourceTreeWidgetItem.h>
7 #include <DataSource/DataSourceTreeWidgetItem.h>
8
8
9 #include <QMenu>
9 #include <QMenu>
10
10
11 namespace {
11 namespace {
12
12
13 /// Number of columns displayed in the tree
13 /// Number of columns displayed in the tree
14 const auto TREE_NB_COLUMNS = 1;
14 const auto TREE_NB_COLUMNS = 1;
15
15
16 /// Header labels for the tree
16 /// Header labels for the tree
17 const auto TREE_HEADER_LABELS = QStringList{QObject::tr("Name")};
17 const auto TREE_HEADER_LABELS = QStringList{QObject::tr("Name")};
18
18
19 /**
19 /**
20 * Creates the item associated to a data source
20 * Creates the item associated to a data source
21 * @param dataSource the data source for which to create the item
21 * @param dataSource the data source for which to create the item
22 * @return the new item
22 * @return the new item
23 */
23 */
24 DataSourceTreeWidgetItem *createTreeWidgetItem(DataSourceItem *dataSource)
24 DataSourceTreeWidgetItem *createTreeWidgetItem(DataSourceItem *dataSource)
25 {
25 {
26 // Creates item for the data source
26 // Creates item for the data source
27 auto item = new DataSourceTreeWidgetItem{dataSource};
27 auto item = new DataSourceTreeWidgetItem{dataSource};
28
28
29 // Generates items for the children of the data source
29 // Generates items for the children of the data source
30 for (auto i = 0; i < dataSource->childCount(); ++i) {
30 for (auto i = 0; i < dataSource->childCount(); ++i) {
31 item->addChild(createTreeWidgetItem(dataSource->child(i)));
31 item->addChild(createTreeWidgetItem(dataSource->child(i)));
32 }
32 }
33
33
34 return item;
34 return item;
35 }
35 }
36
36
37 } // namespace
37 } // namespace
38
38
39 DataSourceWidget::DataSourceWidget(QWidget *parent) : QWidget{parent}, ui{new Ui::DataSourceWidget}
39 DataSourceWidget::DataSourceWidget(QWidget *parent)
40 : QWidget{parent},
41 ui{new Ui::DataSourceWidget},
42 m_Root{
43 std::make_unique<DataSourceItem>(DataSourceItemType::NODE, QStringLiteral("Sources"))}
40 {
44 {
41 ui->setupUi(this);
45 ui->setupUi(this);
42
46
43 // Set tree properties
47 // Set tree properties
44 ui->treeWidget->setColumnCount(TREE_NB_COLUMNS);
48 ui->treeWidget->setColumnCount(TREE_NB_COLUMNS);
45 ui->treeWidget->setHeaderLabels(TREE_HEADER_LABELS);
49 ui->treeWidget->setHeaderLabels(TREE_HEADER_LABELS);
46 ui->treeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
50 ui->treeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
47
51
48 // Connection to show a menu when right clicking on the tree
52 // Connection to show a menu when right clicking on the tree
49 connect(ui->treeWidget, &QTreeWidget::customContextMenuRequested, this,
53 connect(ui->treeWidget, &QTreeWidget::customContextMenuRequested, this,
50 &DataSourceWidget::onTreeMenuRequested);
54 &DataSourceWidget::onTreeMenuRequested);
51
55
52 // Connection to filter tree
56 // Connection to filter tree
53 connect(ui->filterLineEdit, &QLineEdit::textChanged, this, &DataSourceWidget::filterChanged);
57 connect(ui->filterLineEdit, &QLineEdit::textChanged, this, &DataSourceWidget::filterChanged);
58
59 // First init
60 updateTreeWidget();
54 }
61 }
55
62
56 DataSourceWidget::~DataSourceWidget() noexcept
63 DataSourceWidget::~DataSourceWidget() noexcept
57 {
64 {
58 delete ui;
65 delete ui;
59 }
66 }
60
67
61 void DataSourceWidget::addDataSource(DataSourceItem *dataSource) noexcept
68 void DataSourceWidget::addDataSource(DataSourceItem *dataSource) noexcept
62 {
69 {
63 // Creates the item associated to the source and adds it to the tree widget. The tree widget
70 // Merges the data source (without taking its root)
64 // takes the ownership of the item
65 if (dataSource) {
71 if (dataSource) {
66 ui->treeWidget->addTopLevelItem(createTreeWidgetItem(dataSource));
72 for (auto i = 0, count = dataSource->childCount(); i < count; ++i) {
73 m_Root->merge(*dataSource->child(i));
74 }
75
76 updateTreeWidget();
67 }
77 }
68 }
78 }
69
79
80 void DataSourceWidget::updateTreeWidget() noexcept
81 {
82 ui->treeWidget->clear();
83
84 auto rootItem = createTreeWidgetItem(m_Root.get());
85 ui->treeWidget->addTopLevelItem(rootItem);
86 rootItem->setExpanded(true);
87
88 // Sorts tree
89 ui->treeWidget->setSortingEnabled(true);
90 ui->treeWidget->sortByColumn(0, Qt::AscendingOrder);
91 }
92
70 void DataSourceWidget::filterChanged(const QString &text) noexcept
93 void DataSourceWidget::filterChanged(const QString &text) noexcept
71 {
94 {
72 auto validateItem = [&text](const DataSourceTreeWidgetItem &item) {
95 auto validateItem = [&text](const DataSourceTreeWidgetItem &item) {
73 auto regExp = QRegExp{text, Qt::CaseInsensitive, QRegExp::Wildcard};
96 auto regExp = QRegExp{text, Qt::CaseInsensitive, QRegExp::Wildcard};
74
97
75 // An item is valid if any of its metadata validates the text filter
98 // An item is valid if any of its metadata validates the text filter
76 auto itemMetadata = item.data()->data();
99 auto itemMetadata = item.data()->data();
77 auto itemMetadataEnd = itemMetadata.cend();
100 auto itemMetadataEnd = itemMetadata.cend();
78 auto acceptFilter
101 auto acceptFilter
79 = [&regExp](const auto &variant) { return variant.toString().contains(regExp); };
102 = [&regExp](const auto &variant) { return variant.toString().contains(regExp); };
80
103
81 return std::find_if(itemMetadata.cbegin(), itemMetadataEnd, acceptFilter)
104 return std::find_if(itemMetadata.cbegin(), itemMetadataEnd, acceptFilter)
82 != itemMetadataEnd;
105 != itemMetadataEnd;
83 };
106 };
84
107
85 // Applies filter on tree widget
108 // Applies filter on tree widget
86 DataSourceTreeWidgetHelper::filter(*ui->treeWidget, validateItem);
109 DataSourceTreeWidgetHelper::filter(*ui->treeWidget, validateItem);
87 }
110 }
88
111
89 void DataSourceWidget::onTreeMenuRequested(const QPoint &pos) noexcept
112 void DataSourceWidget::onTreeMenuRequested(const QPoint &pos) noexcept
90 {
113 {
91 // Retrieves the selected item in the tree, and build the menu from its actions
114 // Retrieves the selected item in the tree, and build the menu from its actions
92 if (auto selectedItem = dynamic_cast<DataSourceTreeWidgetItem *>(ui->treeWidget->itemAt(pos))) {
115 if (auto selectedItem = dynamic_cast<DataSourceTreeWidgetItem *>(ui->treeWidget->itemAt(pos))) {
93 QMenu treeMenu{};
116 QMenu treeMenu{};
94 treeMenu.addActions(selectedItem->actions());
117 treeMenu.addActions(selectedItem->actions());
95
118
96 if (!treeMenu.isEmpty()) {
119 if (!treeMenu.isEmpty()) {
97 treeMenu.exec(QCursor::pos());
120 treeMenu.exec(QCursor::pos());
98 }
121 }
99 }
122 }
100 }
123 }
@@ -1,331 +1,331
1 #include "Visualization/VisualizationGraphRenderingDelegate.h"
1 #include "Visualization/VisualizationGraphRenderingDelegate.h"
2 #include "Visualization/AxisRenderingUtils.h"
2 #include "Visualization/AxisRenderingUtils.h"
3 #include "Visualization/ColorScaleEditor.h"
3 #include "Visualization/ColorScaleEditor.h"
4 #include "Visualization/PlottablesRenderingUtils.h"
4 #include "Visualization/PlottablesRenderingUtils.h"
5 #include "Visualization/SqpColorScale.h"
5 #include "Visualization/SqpColorScale.h"
6 #include "Visualization/VisualizationGraphWidget.h"
6 #include "Visualization/VisualizationGraphWidget.h"
7 #include "Visualization/qcustomplot.h"
7 #include "Visualization/qcustomplot.h"
8
8
9 #include <Common/DateUtils.h>
9 #include <Common/DateUtils.h>
10
10
11 #include <Data/IDataSeries.h>
11 #include <Data/IDataSeries.h>
12
12
13 #include <SqpApplication.h>
13 #include <SqpApplication.h>
14
14
15 namespace {
15 namespace {
16
16
17 /// Name of the axes layer in QCustomPlot
17 /// Name of the axes layer in QCustomPlot
18 const auto AXES_LAYER = QStringLiteral("axes");
18 const auto AXES_LAYER = QStringLiteral("axes");
19
19
20 /// Icon used to show x-axis properties
20 /// Icon used to show x-axis properties
21 const auto HIDE_AXIS_ICON_PATH = QStringLiteral(":/icones/down.png");
21 const auto HIDE_AXIS_ICON_PATH = QStringLiteral(":/icones/down.png");
22
22
23 /// Name of the overlay layer in QCustomPlot
23 /// Name of the overlay layer in QCustomPlot
24 const auto OVERLAY_LAYER = QStringLiteral("overlay");
24 const auto OVERLAY_LAYER = QStringLiteral("overlay");
25
25
26 /// Pixmap used to show x-axis properties
26 /// Pixmap used to show x-axis properties
27 const auto SHOW_AXIS_ICON_PATH = QStringLiteral(":/icones/up.png");
27 const auto SHOW_AXIS_ICON_PATH = QStringLiteral(":/icones/up.png");
28
28
29 /// Tooltip format for graphs
29 /// Tooltip format for graphs
30 const auto GRAPH_TOOLTIP_FORMAT = QStringLiteral("key: %1\nvalue: %2");
30 const auto GRAPH_TOOLTIP_FORMAT = QStringLiteral("key: %1\nvalue: %2");
31
31
32 /// Tooltip format for colormaps
32 /// Tooltip format for colormaps
33 const auto COLORMAP_TOOLTIP_FORMAT = QStringLiteral("x: %1\ny: %2\nvalue: %3");
33 const auto COLORMAP_TOOLTIP_FORMAT = QStringLiteral("x: %1\ny: %2\nvalue: %3");
34
34
35 /// Offset used to shift the tooltip of the mouse
35 /// Offset used to shift the tooltip of the mouse
36 const auto TOOLTIP_OFFSET = QPoint{20, 20};
36 const auto TOOLTIP_OFFSET = QPoint{20, 20};
37
37
38 /// Tooltip display rectangle (the tooltip is hidden when the mouse leaves this rectangle)
38 /// Tooltip display rectangle (the tooltip is hidden when the mouse leaves this rectangle)
39 const auto TOOLTIP_RECT = QRect{10, 10, 10, 10};
39 const auto TOOLTIP_RECT = QRect{10, 10, 10, 10};
40
40
41 /// Timeout after which the tooltip is displayed
41 /// Timeout after which the tooltip is displayed
42 const auto TOOLTIP_TIMEOUT = 500;
42 const auto TOOLTIP_TIMEOUT = 500;
43
43
44 void initPointTracerStyle(QCPItemTracer &tracer) noexcept
44 void initPointTracerStyle(QCPItemTracer &tracer) noexcept
45 {
45 {
46 tracer.setInterpolating(false);
46 tracer.setInterpolating(false);
47 tracer.setStyle(QCPItemTracer::tsCircle);
47 tracer.setStyle(QCPItemTracer::tsCircle);
48 tracer.setSize(3);
48 tracer.setSize(3);
49 tracer.setPen(QPen(Qt::black));
49 tracer.setPen(QPen(Qt::black));
50 tracer.setBrush(Qt::black);
50 tracer.setBrush(Qt::black);
51 }
51 }
52
52
53 QPixmap pixmap(const QString &iconPath) noexcept
53 QPixmap pixmap(const QString &iconPath) noexcept
54 {
54 {
55 return QIcon{iconPath}.pixmap(QSize{16, 16});
55 return QIcon{iconPath}.pixmap(QSize{16, 16});
56 }
56 }
57
57
58 void initClosePixmapStyle(QCPItemPixmap &pixmap) noexcept
58 void initClosePixmapStyle(QCPItemPixmap &pixmap) noexcept
59 {
59 {
60 // Icon
60 // Icon
61 pixmap.setPixmap(
61 pixmap.setPixmap(
62 sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton).pixmap(QSize{16, 16}));
62 sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton).pixmap(QSize{16, 16}));
63
63
64 // Position
64 // Position
65 pixmap.topLeft->setType(QCPItemPosition::ptAxisRectRatio);
65 pixmap.topLeft->setType(QCPItemPosition::ptAxisRectRatio);
66 pixmap.topLeft->setCoords(1, 0);
66 pixmap.topLeft->setCoords(1, 0);
67 pixmap.setClipToAxisRect(false);
67 pixmap.setClipToAxisRect(false);
68
68
69 // Can be selected
69 // Can be selected
70 pixmap.setSelectable(true);
70 pixmap.setSelectable(true);
71 }
71 }
72
72
73 void initXAxisPixmapStyle(QCPItemPixmap &itemPixmap) noexcept
73 void initXAxisPixmapStyle(QCPItemPixmap &itemPixmap) noexcept
74 {
74 {
75 // Icon
75 // Icon
76 itemPixmap.setPixmap(pixmap(HIDE_AXIS_ICON_PATH));
76 itemPixmap.setPixmap(pixmap(HIDE_AXIS_ICON_PATH));
77
77
78 // Position
78 // Position
79 itemPixmap.topLeft->setType(QCPItemPosition::ptAxisRectRatio);
79 itemPixmap.topLeft->setType(QCPItemPosition::ptAxisRectRatio);
80 itemPixmap.topLeft->setCoords(0, 1);
80 itemPixmap.topLeft->setCoords(0, 1);
81 itemPixmap.setClipToAxisRect(false);
81 itemPixmap.setClipToAxisRect(false);
82
82
83 // Can be selected
83 // Can be selected
84 itemPixmap.setSelectable(true);
84 itemPixmap.setSelectable(true);
85 }
85 }
86
86
87 void initTitleTextStyle(QCPItemText &text) noexcept
87 void initTitleTextStyle(QCPItemText &text) noexcept
88 {
88 {
89 // Font and background styles
89 // Font and background styles
90 text.setColor(Qt::gray);
90 text.setColor(Qt::gray);
91 text.setBrush(Qt::white);
91 text.setBrush(Qt::white);
92
92
93 // Position
93 // Position
94 text.setPositionAlignment(Qt::AlignTop | Qt::AlignLeft);
94 text.setPositionAlignment(Qt::AlignTop | Qt::AlignLeft);
95 text.position->setType(QCPItemPosition::ptAxisRectRatio);
95 text.position->setType(QCPItemPosition::ptAxisRectRatio);
96 text.position->setCoords(0.5, 0);
96 text.position->setCoords(0.5, 0);
97 }
97 }
98
98
99 /**
99 /**
100 * Returns the cell index (x or y) of a colormap according to the coordinate passed in parameter.
100 * Returns the cell index (x or y) of a colormap according to the coordinate passed in parameter.
101 * This method handles the fact that a colormap axis can be logarithmic or linear.
101 * This method handles the fact that a colormap axis can be logarithmic or linear.
102 * @param colormap the colormap for which to calculate the index
102 * @param colormap the colormap for which to calculate the index
103 * @param coord the coord to convert to cell index
103 * @param coord the coord to convert to cell index
104 * @param xCoord calculates the x index if true, calculates y index if false
104 * @param xCoord calculates the x index if true, calculates y index if false
105 * @return the cell index
105 * @return the cell index
106 */
106 */
107 int colorMapCellIndex(const QCPColorMap &colormap, double coord, bool xCoord)
107 int colorMapCellIndex(const QCPColorMap &colormap, double coord, bool xCoord)
108 {
108 {
109 // Determines the axis of the colormap according to xCoord, and whether it is logarithmic or not
109 // Determines the axis of the colormap according to xCoord, and whether it is logarithmic or not
110 auto isLogarithmic = (xCoord ? colormap.keyAxis() : colormap.valueAxis())->scaleType()
110 auto isLogarithmic = (xCoord ? colormap.keyAxis() : colormap.valueAxis())->scaleType()
111 == QCPAxis::stLogarithmic;
111 == QCPAxis::stLogarithmic;
112
112
113 if (isLogarithmic) {
113 if (isLogarithmic) {
114 // For a logarithmic axis we can't use the conversion method of colormap, so we calculate
114 // For a logarithmic axis we can't use the conversion method of colormap, so we calculate
115 // the index manually based on the position of the coordinate on the axis
115 // the index manually based on the position of the coordinate on the axis
116
116
117 // Gets the axis range and the number of values between range bounds to calculate the step
117 // Gets the axis range and the number of values between range bounds to calculate the step
118 // between each value of the range
118 // between each value of the range
119 auto range = xCoord ? colormap.data()->keyRange() : colormap.data()->valueRange();
119 auto range = xCoord ? colormap.data()->keyRange() : colormap.data()->valueRange();
120 auto nbValues = (xCoord ? colormap.data()->keySize() : colormap.data()->valueSize()) - 1;
120 auto nbValues = (xCoord ? colormap.data()->keySize() : colormap.data()->valueSize()) - 1;
121 auto valueStep
121 auto valueStep
122 = (std::log10(range.upper) - std::log10(range.lower)) / static_cast<double>(nbValues);
122 = (std::log10(range.upper) - std::log10(range.lower)) / static_cast<double>(nbValues);
123
123
124 // According to the coord position, calculates the closest index in the range
124 // According to the coord position, calculates the closest index in the range
125 return std::round((std::log10(coord) - std::log10(range.lower)) / valueStep);
125 return std::round((std::log10(coord) - std::log10(range.lower)) / valueStep);
126 }
126 }
127 else {
127 else {
128 // For a linear axis, we use the conversion method of colormap
128 // For a linear axis, we use the conversion method of colormap
129 int index;
129 int index;
130 if (xCoord) {
130 if (xCoord) {
131 colormap.data()->coordToCell(coord, 0., &index, nullptr);
131 colormap.data()->coordToCell(coord, 0., &index, nullptr);
132 }
132 }
133 else {
133 else {
134 colormap.data()->coordToCell(0., coord, nullptr, &index);
134 colormap.data()->coordToCell(0., coord, nullptr, &index);
135 }
135 }
136
136
137 return index;
137 return index;
138 }
138 }
139 }
139 }
140
140
141 } // namespace
141 } // namespace
142
142
143 struct VisualizationGraphRenderingDelegate::VisualizationGraphRenderingDelegatePrivate {
143 struct VisualizationGraphRenderingDelegate::VisualizationGraphRenderingDelegatePrivate {
144 explicit VisualizationGraphRenderingDelegatePrivate(VisualizationGraphWidget &graphWidget)
144 explicit VisualizationGraphRenderingDelegatePrivate(VisualizationGraphWidget &graphWidget)
145 : m_Plot{graphWidget.plot()},
145 : m_Plot{graphWidget.plot()},
146 m_PointTracer{new QCPItemTracer{&m_Plot}},
146 m_PointTracer{new QCPItemTracer{&m_Plot}},
147 m_TracerTimer{},
147 m_TracerTimer{},
148 m_ClosePixmap{new QCPItemPixmap{&m_Plot}},
148 m_ClosePixmap{new QCPItemPixmap{&m_Plot}},
149 m_TitleText{new QCPItemText{&m_Plot}},
149 m_TitleText{new QCPItemText{&m_Plot}},
150 m_XAxisPixmap{new QCPItemPixmap{&m_Plot}},
150 m_XAxisPixmap{new QCPItemPixmap{&m_Plot}},
151 m_ShowXAxis{true},
151 m_ShowXAxis{true},
152 m_XAxisLabel{},
152 m_XAxisLabel{},
153 m_ColorScale{SqpColorScale{m_Plot}}
153 m_ColorScale{SqpColorScale{m_Plot}}
154 {
154 {
155 initPointTracerStyle(*m_PointTracer);
155 initPointTracerStyle(*m_PointTracer);
156
156
157 m_TracerTimer.setInterval(TOOLTIP_TIMEOUT);
157 m_TracerTimer.setInterval(TOOLTIP_TIMEOUT);
158 m_TracerTimer.setSingleShot(true);
158 m_TracerTimer.setSingleShot(true);
159
159
160 // Inits "close button" in plot overlay
160 // Inits "close button" in plot overlay
161 m_ClosePixmap->setLayer(OVERLAY_LAYER);
161 m_ClosePixmap->setLayer(OVERLAY_LAYER);
162 initClosePixmapStyle(*m_ClosePixmap);
162 initClosePixmapStyle(*m_ClosePixmap);
163
163
164 // Connects pixmap selection to graph widget closing
164 // Connects pixmap selection to graph widget closing
165 QObject::connect(m_ClosePixmap, &QCPItemPixmap::selectionChanged,
165 QObject::connect(m_ClosePixmap, &QCPItemPixmap::selectionChanged,
166 [&graphWidget](bool selected) {
166 [&graphWidget](bool selected) {
167 if (selected) {
167 if (selected) {
168 graphWidget.close();
168 graphWidget.close();
169 }
169 }
170 });
170 });
171
171
172 // Inits graph name in plot overlay
172 // Inits graph name in plot overlay
173 m_TitleText->setLayer(OVERLAY_LAYER);
173 m_TitleText->setLayer(OVERLAY_LAYER);
174 m_TitleText->setText(graphWidget.name());
174 m_TitleText->setText(graphWidget.name());
175 initTitleTextStyle(*m_TitleText);
175 initTitleTextStyle(*m_TitleText);
176
176
177 // Inits "show x-axis button" in plot overlay
177 // Inits "show x-axis button" in plot overlay
178 m_XAxisPixmap->setLayer(OVERLAY_LAYER);
178 m_XAxisPixmap->setLayer(OVERLAY_LAYER);
179 initXAxisPixmapStyle(*m_XAxisPixmap);
179 initXAxisPixmapStyle(*m_XAxisPixmap);
180
180
181 // Connects pixmap selection to graph x-axis showing/hiding
181 // Connects pixmap selection to graph x-axis showing/hiding
182 QObject::connect(m_XAxisPixmap, &QCPItemPixmap::selectionChanged, [this]() {
182 QObject::connect(m_XAxisPixmap, &QCPItemPixmap::selectionChanged, [this]() {
183 if (m_XAxisPixmap->selected()) {
183 if (m_XAxisPixmap->selected()) {
184 // Changes the selection state and refreshes the x-axis
184 // Changes the selection state and refreshes the x-axis
185 m_ShowXAxis = !m_ShowXAxis;
185 m_ShowXAxis = !m_ShowXAxis;
186 updateXAxisState();
186 updateXAxisState();
187 m_Plot.layer(AXES_LAYER)->replot();
187 m_Plot.layer(AXES_LAYER)->replot();
188
188
189 // Deselects the x-axis pixmap and updates icon
189 // Deselects the x-axis pixmap and updates icon
190 m_XAxisPixmap->setSelected(false);
190 m_XAxisPixmap->setSelected(false);
191 m_XAxisPixmap->setPixmap(
191 m_XAxisPixmap->setPixmap(
192 pixmap(m_ShowXAxis ? HIDE_AXIS_ICON_PATH : SHOW_AXIS_ICON_PATH));
192 pixmap(m_ShowXAxis ? HIDE_AXIS_ICON_PATH : SHOW_AXIS_ICON_PATH));
193 m_Plot.layer(OVERLAY_LAYER)->replot();
193 m_Plot.layer(OVERLAY_LAYER)->replot();
194 }
194 }
195 });
195 });
196 }
196 }
197
197
198 /// Updates state of x-axis according to the current selection of x-axis pixmap
198 /// Updates state of x-axis according to the current selection of x-axis pixmap
199 /// @remarks the method doesn't call plot refresh
199 /// @remarks the method doesn't call plot refresh
200 void updateXAxisState() noexcept
200 void updateXAxisState() noexcept
201 {
201 {
202 m_Plot.xAxis->setTickLabels(m_ShowXAxis);
202 m_Plot.xAxis->setTickLabels(m_ShowXAxis);
203 m_Plot.xAxis->setLabel(m_ShowXAxis ? m_XAxisLabel : QString{});
203 m_Plot.xAxis->setLabel(m_ShowXAxis ? m_XAxisLabel : QString{});
204 }
204 }
205
205
206 QCustomPlot &m_Plot;
206 QCustomPlot &m_Plot;
207 QCPItemTracer *m_PointTracer;
207 QCPItemTracer *m_PointTracer;
208 QTimer m_TracerTimer;
208 QTimer m_TracerTimer;
209 QCPItemPixmap *m_ClosePixmap; /// Graph's close button
209 QCPItemPixmap *m_ClosePixmap; /// Graph's close button
210 QCPItemText *m_TitleText; /// Graph's title
210 QCPItemText *m_TitleText; /// Graph's title
211 QCPItemPixmap *m_XAxisPixmap;
211 QCPItemPixmap *m_XAxisPixmap;
212 bool m_ShowXAxis; /// X-axis properties are shown or hidden
212 bool m_ShowXAxis; /// X-axis properties are shown or hidden
213 QString m_XAxisLabel;
213 QString m_XAxisLabel;
214 SqpColorScale m_ColorScale; /// Color scale used for some types of graphs (as spectrograms)
214 SqpColorScale m_ColorScale; /// Color scale used for some types of graphs (as spectrograms)
215 };
215 };
216
216
217 VisualizationGraphRenderingDelegate::VisualizationGraphRenderingDelegate(
217 VisualizationGraphRenderingDelegate::VisualizationGraphRenderingDelegate(
218 VisualizationGraphWidget &graphWidget)
218 VisualizationGraphWidget &graphWidget)
219 : impl{spimpl::make_unique_impl<VisualizationGraphRenderingDelegatePrivate>(graphWidget)}
219 : impl{spimpl::make_unique_impl<VisualizationGraphRenderingDelegatePrivate>(graphWidget)}
220 {
220 {
221 }
221 }
222
222
223 void VisualizationGraphRenderingDelegate::onMouseDoubleClick(QMouseEvent *event) noexcept
223 void VisualizationGraphRenderingDelegate::onMouseDoubleClick(QMouseEvent *event) noexcept
224 {
224 {
225 // Opens color scale editor if color scale is double clicked
225 // Opens color scale editor if color scale is double clicked
226 auto colorScale = dynamic_cast<QCPColorScale *>(impl->m_Plot.layoutElementAt(event->pos()));
226 auto colorScale = static_cast<QCPColorScale *>(impl->m_Plot.layoutElementAt(event->pos()));
227 if (impl->m_ColorScale.m_Scale == colorScale) {
227 if (impl->m_ColorScale.m_Scale == colorScale) {
228 if (ColorScaleEditor{impl->m_ColorScale}.exec() == QDialog::Accepted) {
228 if (ColorScaleEditor{impl->m_ColorScale}.exec() == QDialog::Accepted) {
229 impl->m_Plot.replot();
229 impl->m_Plot.replot();
230 }
230 }
231 }
231 }
232 }
232 }
233
233
234 void VisualizationGraphRenderingDelegate::onMouseMove(QMouseEvent *event) noexcept
234 void VisualizationGraphRenderingDelegate::onMouseMove(QMouseEvent *event) noexcept
235 {
235 {
236 // Cancels pending refresh
236 // Cancels pending refresh
237 impl->m_TracerTimer.disconnect();
237 impl->m_TracerTimer.disconnect();
238
238
239 // Reinits tracers
239 // Reinits tracers
240 impl->m_PointTracer->setGraph(nullptr);
240 impl->m_PointTracer->setGraph(nullptr);
241 impl->m_PointTracer->setVisible(false);
241 impl->m_PointTracer->setVisible(false);
242 impl->m_Plot.replot();
242 impl->m_Plot.replot();
243
243
244 QString tooltip{};
244 QString tooltip{};
245
245
246 // Gets the graph under the mouse position
246 // Gets the graph under the mouse position
247 auto eventPos = event->pos();
247 auto eventPos = event->pos();
248 if (auto graph = qobject_cast<QCPGraph *>(impl->m_Plot.plottableAt(eventPos))) {
248 if (auto graph = qobject_cast<QCPGraph *>(impl->m_Plot.plottableAt(eventPos))) {
249 auto mouseKey = graph->keyAxis()->pixelToCoord(eventPos.x());
249 auto mouseKey = graph->keyAxis()->pixelToCoord(eventPos.x());
250 auto graphData = graph->data();
250 auto graphData = graph->data();
251
251
252 // Gets the closest data point to the mouse
252 // Gets the closest data point to the mouse
253 auto graphDataIt = graphData->findBegin(mouseKey);
253 auto graphDataIt = graphData->findBegin(mouseKey);
254 if (graphDataIt != graphData->constEnd()) {
254 if (graphDataIt != graphData->constEnd()) {
255 // Sets tooltip
255 // Sets tooltip
256 auto key = formatValue(graphDataIt->key, *graph->keyAxis());
256 auto key = formatValue(graphDataIt->key, *graph->keyAxis());
257 auto value = formatValue(graphDataIt->value, *graph->valueAxis());
257 auto value = formatValue(graphDataIt->value, *graph->valueAxis());
258 tooltip = GRAPH_TOOLTIP_FORMAT.arg(key, value);
258 tooltip = GRAPH_TOOLTIP_FORMAT.arg(key, value);
259
259
260 // Displays point tracer
260 // Displays point tracer
261 impl->m_PointTracer->setGraph(graph);
261 impl->m_PointTracer->setGraph(graph);
262 impl->m_PointTracer->setGraphKey(graphDataIt->key);
262 impl->m_PointTracer->setGraphKey(graphDataIt->key);
263 impl->m_PointTracer->setLayer(
263 impl->m_PointTracer->setLayer(
264 impl->m_Plot.layer("main")); // Tracer is set on top of the plot's main layer
264 impl->m_Plot.layer("main")); // Tracer is set on top of the plot's main layer
265 impl->m_PointTracer->setVisible(true);
265 impl->m_PointTracer->setVisible(true);
266 impl->m_Plot.replot();
266 impl->m_Plot.replot();
267 }
267 }
268 }
268 }
269 else if (auto colorMap = qobject_cast<QCPColorMap *>(impl->m_Plot.plottableAt(eventPos))) {
269 else if (auto colorMap = qobject_cast<QCPColorMap *>(impl->m_Plot.plottableAt(eventPos))) {
270 // Gets x and y coords
270 // Gets x and y coords
271 auto x = colorMap->keyAxis()->pixelToCoord(eventPos.x());
271 auto x = colorMap->keyAxis()->pixelToCoord(eventPos.x());
272 auto y = colorMap->valueAxis()->pixelToCoord(eventPos.y());
272 auto y = colorMap->valueAxis()->pixelToCoord(eventPos.y());
273
273
274 // Calculates x and y cell indexes, and retrieves the underlying value
274 // Calculates x and y cell indexes, and retrieves the underlying value
275 auto xCellIndex = colorMapCellIndex(*colorMap, x, true);
275 auto xCellIndex = colorMapCellIndex(*colorMap, x, true);
276 auto yCellIndex = colorMapCellIndex(*colorMap, y, false);
276 auto yCellIndex = colorMapCellIndex(*colorMap, y, false);
277 auto value = colorMap->data()->cell(xCellIndex, yCellIndex);
277 auto value = colorMap->data()->cell(xCellIndex, yCellIndex);
278
278
279 // Sets tooltips
279 // Sets tooltips
280 tooltip = COLORMAP_TOOLTIP_FORMAT.arg(formatValue(x, *colorMap->keyAxis()),
280 tooltip = COLORMAP_TOOLTIP_FORMAT.arg(formatValue(x, *colorMap->keyAxis()),
281 formatValue(y, *colorMap->valueAxis()),
281 formatValue(y, *colorMap->valueAxis()),
282 formatValue(value, *colorMap->colorScale()->axis()));
282 formatValue(value, *colorMap->colorScale()->axis()));
283 }
283 }
284
284
285 if (!tooltip.isEmpty()) {
285 if (!tooltip.isEmpty()) {
286 // Starts timer to show tooltip after timeout
286 // Starts timer to show tooltip after timeout
287 auto showTooltip = [tooltip, eventPos, this]() {
287 auto showTooltip = [tooltip, eventPos, this]() {
288 QToolTip::showText(impl->m_Plot.mapToGlobal(eventPos) + TOOLTIP_OFFSET, tooltip,
288 QToolTip::showText(impl->m_Plot.mapToGlobal(eventPos) + TOOLTIP_OFFSET, tooltip,
289 &impl->m_Plot, TOOLTIP_RECT);
289 &impl->m_Plot, TOOLTIP_RECT);
290 };
290 };
291
291
292 QObject::connect(&impl->m_TracerTimer, &QTimer::timeout, showTooltip);
292 QObject::connect(&impl->m_TracerTimer, &QTimer::timeout, showTooltip);
293 impl->m_TracerTimer.start();
293 impl->m_TracerTimer.start();
294 }
294 }
295 }
295 }
296
296
297 void VisualizationGraphRenderingDelegate::onPlotUpdated() noexcept
297 void VisualizationGraphRenderingDelegate::onPlotUpdated() noexcept
298 {
298 {
299 // Updates color scale bounds
299 // Updates color scale bounds
300 impl->m_ColorScale.updateDataRange();
300 impl->m_ColorScale.updateDataRange();
301 impl->m_Plot.replot();
301 impl->m_Plot.replot();
302 }
302 }
303
303
304 void VisualizationGraphRenderingDelegate::setAxesProperties(
304 void VisualizationGraphRenderingDelegate::setAxesProperties(
305 std::shared_ptr<IDataSeries> dataSeries) noexcept
305 std::shared_ptr<IDataSeries> dataSeries) noexcept
306 {
306 {
307 // Stores x-axis label to be able to retrieve it when x-axis pixmap is unselected
307 // Stores x-axis label to be able to retrieve it when x-axis pixmap is unselected
308 impl->m_XAxisLabel = dataSeries->xAxisUnit().m_Name;
308 impl->m_XAxisLabel = dataSeries->xAxisUnit().m_Name;
309
309
310 auto axisHelper = IAxisHelperFactory::create(dataSeries);
310 auto axisHelper = IAxisHelperFactory::create(dataSeries);
311 axisHelper->setProperties(impl->m_Plot, impl->m_ColorScale);
311 axisHelper->setProperties(impl->m_Plot, impl->m_ColorScale);
312
312
313 // Updates x-axis state
313 // Updates x-axis state
314 impl->updateXAxisState();
314 impl->updateXAxisState();
315
315
316 impl->m_Plot.layer(AXES_LAYER)->replot();
316 impl->m_Plot.layer(AXES_LAYER)->replot();
317 }
317 }
318
318
319 void VisualizationGraphRenderingDelegate::setPlottablesProperties(
319 void VisualizationGraphRenderingDelegate::setPlottablesProperties(
320 std::shared_ptr<IDataSeries> dataSeries, PlottablesMap &plottables) noexcept
320 std::shared_ptr<IDataSeries> dataSeries, PlottablesMap &plottables) noexcept
321 {
321 {
322 auto plottablesHelper = IPlottablesHelperFactory::create(dataSeries);
322 auto plottablesHelper = IPlottablesHelperFactory::create(dataSeries);
323 plottablesHelper->setProperties(plottables);
323 plottablesHelper->setProperties(plottables);
324 }
324 }
325
325
326 void VisualizationGraphRenderingDelegate::showGraphOverlay(bool show) noexcept
326 void VisualizationGraphRenderingDelegate::showGraphOverlay(bool show) noexcept
327 {
327 {
328 auto overlay = impl->m_Plot.layer(OVERLAY_LAYER);
328 auto overlay = impl->m_Plot.layer(OVERLAY_LAYER);
329 overlay->setVisible(show);
329 overlay->setVisible(show);
330 overlay->replot();
330 overlay->replot();
331 }
331 }
@@ -1,74 +1,77
1 #include "AmdaPlugin.h"
1 #include "AmdaPlugin.h"
2 #include "AmdaDefs.h"
2 #include "AmdaDefs.h"
3 #include "AmdaParser.h"
3 #include "AmdaParser.h"
4 #include "AmdaProvider.h"
4 #include "AmdaProvider.h"
5
5
6 #include <DataSource/DataSourceController.h>
6 #include <DataSource/DataSourceController.h>
7 #include <DataSource/DataSourceItem.h>
7 #include <DataSource/DataSourceItem.h>
8 #include <DataSource/DataSourceItemAction.h>
8 #include <DataSource/DataSourceItemAction.h>
9
9
10 #include <SqpApplication.h>
10 #include <SqpApplication.h>
11
11
12 Q_LOGGING_CATEGORY(LOG_AmdaPlugin, "AmdaPlugin")
12 Q_LOGGING_CATEGORY(LOG_AmdaPlugin, "AmdaPlugin")
13
13
14 namespace {
14 namespace {
15
15
16 /// Name of the data source
16 /// Name of the data source
17 const auto DATA_SOURCE_NAME = QStringLiteral("AMDA");
17 const auto DATA_SOURCE_NAME = QStringLiteral("AMDA");
18
18
19 /// Path of the file used to generate the data source item for AMDA
19 /// Path of the file used to generate the data source item for AMDA
20 const auto JSON_FILE_PATH = QStringLiteral(":/samples/AmdaSampleV3.json");
20 const auto JSON_FILE_PATH = QStringLiteral(":/samples/AmdaSampleV3.json");
21
21
22 void associateActions(DataSourceItem &item, const QUuid &dataSourceUid)
22 void associateActions(DataSourceItem &item, const QUuid &dataSourceUid)
23 {
23 {
24 auto addLoadAction = [&item, dataSourceUid](const QString &label) {
24 auto addLoadAction = [&item, dataSourceUid](const QString &label) {
25 item.addAction(
25 item.addAction(
26 std::make_unique<DataSourceItemAction>(label, [dataSourceUid](DataSourceItem &item) {
26 std::make_unique<DataSourceItemAction>(label, [dataSourceUid](DataSourceItem &item) {
27 if (auto app = sqpApp) {
27 if (auto app = sqpApp) {
28 app->dataSourceController().loadProductItem(dataSourceUid, item);
28 app->dataSourceController().loadProductItem(dataSourceUid, item);
29 }
29 }
30 }));
30 }));
31 };
31 };
32
32
33 const auto itemType = item.type();
33 const auto itemType = item.type();
34 if (itemType == DataSourceItemType::PRODUCT) {
34 if (itemType == DataSourceItemType::PRODUCT || itemType == DataSourceItemType::COMPONENT) {
35 addLoadAction(QObject::tr("Load %1 product").arg(item.name()));
35 // Adds plugin name to item metadata
36 }
36 item.setData(DataSourceItem::PLUGIN_DATA_KEY, DATA_SOURCE_NAME);
37 else if (itemType == DataSourceItemType::COMPONENT) {
37
38 addLoadAction(QObject::tr("Load %1 component").arg(item.name()));
38 // Adds load action
39 auto actionLabel = QObject::tr(
40 itemType == DataSourceItemType::PRODUCT ? "Load %1 product" : "Load %1 component");
41 addLoadAction(actionLabel.arg(item.name()));
39 }
42 }
40
43
41 auto count = item.childCount();
44 auto count = item.childCount();
42 for (auto i = 0; i < count; ++i) {
45 for (auto i = 0; i < count; ++i) {
43 if (auto child = item.child(i)) {
46 if (auto child = item.child(i)) {
44 associateActions(*child, dataSourceUid);
47 associateActions(*child, dataSourceUid);
45 }
48 }
46 }
49 }
47 }
50 }
48
51
49 } // namespace
52 } // namespace
50
53
51 void AmdaPlugin::initialize()
54 void AmdaPlugin::initialize()
52 {
55 {
53 if (auto app = sqpApp) {
56 if (auto app = sqpApp) {
54 // Registers to the data source controller
57 // Registers to the data source controller
55 auto &dataSourceController = app->dataSourceController();
58 auto &dataSourceController = app->dataSourceController();
56 auto dataSourceUid = dataSourceController.registerDataSource(DATA_SOURCE_NAME);
59 auto dataSourceUid = dataSourceController.registerDataSource(DATA_SOURCE_NAME);
57
60
58 // Sets data source tree
61 // Sets data source tree
59 if (auto dataSourceItem = AmdaParser::readJson(JSON_FILE_PATH)) {
62 if (auto dataSourceItem = AmdaParser::readJson(JSON_FILE_PATH)) {
60 associateActions(*dataSourceItem, dataSourceUid);
63 associateActions(*dataSourceItem, dataSourceUid);
61
64
62 dataSourceController.setDataSourceItem(dataSourceUid, std::move(dataSourceItem));
65 dataSourceController.setDataSourceItem(dataSourceUid, std::move(dataSourceItem));
63 }
66 }
64 else {
67 else {
65 qCCritical(LOG_AmdaPlugin()) << tr("No data source item could be generated for AMDA");
68 qCCritical(LOG_AmdaPlugin()) << tr("No data source item could be generated for AMDA");
66 }
69 }
67
70
68 // Sets data provider
71 // Sets data provider
69 dataSourceController.setDataProvider(dataSourceUid, std::make_unique<AmdaProvider>());
72 dataSourceController.setDataProvider(dataSourceUid, std::make_unique<AmdaProvider>());
70 }
73 }
71 else {
74 else {
72 qCWarning(LOG_AmdaPlugin()) << tr("Can't access to SciQlop application");
75 qCWarning(LOG_AmdaPlugin()) << tr("Can't access to SciQlop application");
73 }
76 }
74 }
77 }
@@ -1,114 +1,118
1 #include "MockPlugin.h"
1 #include "MockPlugin.h"
2 #include "CosinusProvider.h"
2 #include "CosinusProvider.h"
3 #include "MockDefs.h"
3 #include "MockDefs.h"
4
4
5 #include <DataSource/DataSourceController.h>
5 #include <DataSource/DataSourceController.h>
6 #include <DataSource/DataSourceItem.h>
6 #include <DataSource/DataSourceItem.h>
7 #include <DataSource/DataSourceItemAction.h>
7 #include <DataSource/DataSourceItemAction.h>
8
8
9 #include <SqpApplication.h>
9 #include <SqpApplication.h>
10
10
11 Q_LOGGING_CATEGORY(LOG_MockPlugin, "MockPlugin")
11 Q_LOGGING_CATEGORY(LOG_MockPlugin, "MockPlugin")
12
12
13 namespace {
13 namespace {
14
14
15 /// Name of the data source
15 /// Name of the data source
16 const auto DATA_SOURCE_NAME = QStringLiteral("MMS");
16 const auto DATA_SOURCE_NAME = QStringLiteral("MMS");
17
17
18 /// Creates the data provider relative to the plugin
18 /// Creates the data provider relative to the plugin
19 std::unique_ptr<IDataProvider> createDataProvider() noexcept
19 std::unique_ptr<IDataProvider> createDataProvider() noexcept
20 {
20 {
21 return std::make_unique<CosinusProvider>();
21 return std::make_unique<CosinusProvider>();
22 }
22 }
23
23
24 std::unique_ptr<DataSourceItem> createProductItem(const QVariantHash &data,
24 std::unique_ptr<DataSourceItem> createProductItem(const QVariantHash &data,
25 const QUuid &dataSourceUid)
25 const QUuid &dataSourceUid)
26 {
26 {
27 auto result = std::make_unique<DataSourceItem>(DataSourceItemType::PRODUCT, data);
27 auto result = std::make_unique<DataSourceItem>(DataSourceItemType::PRODUCT, data);
28
29 // Adds plugin name to product metadata
30 result->setData(DataSourceItem::PLUGIN_DATA_KEY, DATA_SOURCE_NAME);
31
28 auto productName = data.value(DataSourceItem::NAME_DATA_KEY).toString();
32 auto productName = data.value(DataSourceItem::NAME_DATA_KEY).toString();
29
33
30 // Add action to load product from DataSourceController
34 // Add action to load product from DataSourceController
31 result->addAction(std::make_unique<DataSourceItemAction>(
35 result->addAction(std::make_unique<DataSourceItemAction>(
32 QObject::tr("Load %1 product").arg(productName),
36 QObject::tr("Load %1 product").arg(productName),
33 [productName, dataSourceUid](DataSourceItem &item) {
37 [productName, dataSourceUid](DataSourceItem &item) {
34 if (auto app = sqpApp) {
38 if (auto app = sqpApp) {
35 app->dataSourceController().loadProductItem(dataSourceUid, item);
39 app->dataSourceController().loadProductItem(dataSourceUid, item);
36 }
40 }
37 }));
41 }));
38
42
39 return result;
43 return result;
40 }
44 }
41
45
42 /// Creates the data source item relative to the plugin
46 /// Creates the data source item relative to the plugin
43 std::unique_ptr<DataSourceItem> createDataSourceItem(const QUuid &dataSourceUid) noexcept
47 std::unique_ptr<DataSourceItem> createDataSourceItem(const QUuid &dataSourceUid) noexcept
44 {
48 {
45 // Magnetic field products
49 // Magnetic field products
46 auto magneticFieldFolder = std::make_unique<DataSourceItem>(DataSourceItemType::NODE,
50 auto magneticFieldFolder = std::make_unique<DataSourceItem>(DataSourceItemType::NODE,
47 QStringLiteral("Magnetic field"));
51 QStringLiteral("_Magnetic field"));
48 magneticFieldFolder->appendChild(
52 magneticFieldFolder->appendChild(
49 createProductItem({{DataSourceItem::NAME_DATA_KEY, QStringLiteral("Scalar 10 Hz")},
53 createProductItem({{DataSourceItem::NAME_DATA_KEY, QStringLiteral("Scalar 10 Hz")},
50 {COSINUS_TYPE_KEY, "scalar"},
54 {COSINUS_TYPE_KEY, "scalar"},
51 {COSINUS_FREQUENCY_KEY, 10.}},
55 {COSINUS_FREQUENCY_KEY, 10.}},
52 dataSourceUid));
56 dataSourceUid));
53 magneticFieldFolder->appendChild(
57 magneticFieldFolder->appendChild(
54 createProductItem({{DataSourceItem::NAME_DATA_KEY, QStringLiteral("Scalar 60 Hz")},
58 createProductItem({{DataSourceItem::NAME_DATA_KEY, QStringLiteral("Scalar 60 Hz")},
55 {COSINUS_TYPE_KEY, "scalar"},
59 {COSINUS_TYPE_KEY, "scalar"},
56 {COSINUS_FREQUENCY_KEY, 60.}},
60 {COSINUS_FREQUENCY_KEY, 60.}},
57 dataSourceUid));
61 dataSourceUid));
58 magneticFieldFolder->appendChild(
62 magneticFieldFolder->appendChild(
59 createProductItem({{DataSourceItem::NAME_DATA_KEY, QStringLiteral("Scalar 100 Hz")},
63 createProductItem({{DataSourceItem::NAME_DATA_KEY, QStringLiteral("Scalar 100 Hz")},
60 {COSINUS_TYPE_KEY, "scalar"},
64 {COSINUS_TYPE_KEY, "scalar"},
61 {COSINUS_FREQUENCY_KEY, 100.}},
65 {COSINUS_FREQUENCY_KEY, 100.}},
62 dataSourceUid));
66 dataSourceUid));
63 magneticFieldFolder->appendChild(
67 magneticFieldFolder->appendChild(
64 createProductItem({{DataSourceItem::NAME_DATA_KEY, QStringLiteral("Vector 10 Hz")},
68 createProductItem({{DataSourceItem::NAME_DATA_KEY, QStringLiteral("Vector 10 Hz")},
65 {COSINUS_TYPE_KEY, "vector"},
69 {COSINUS_TYPE_KEY, "vector"},
66 {COSINUS_FREQUENCY_KEY, 10.}},
70 {COSINUS_FREQUENCY_KEY, 10.}},
67 dataSourceUid));
71 dataSourceUid));
68 magneticFieldFolder->appendChild(
72 magneticFieldFolder->appendChild(
69 createProductItem({{DataSourceItem::NAME_DATA_KEY, QStringLiteral("Vector 60 Hz")},
73 createProductItem({{DataSourceItem::NAME_DATA_KEY, QStringLiteral("Vector 60 Hz")},
70 {COSINUS_TYPE_KEY, "vector"},
74 {COSINUS_TYPE_KEY, "vector"},
71 {COSINUS_FREQUENCY_KEY, 60.}},
75 {COSINUS_FREQUENCY_KEY, 60.}},
72 dataSourceUid));
76 dataSourceUid));
73 magneticFieldFolder->appendChild(
77 magneticFieldFolder->appendChild(
74 createProductItem({{DataSourceItem::NAME_DATA_KEY, QStringLiteral("Vector 100 Hz")},
78 createProductItem({{DataSourceItem::NAME_DATA_KEY, QStringLiteral("Vector 100 Hz")},
75 {COSINUS_TYPE_KEY, "vector"},
79 {COSINUS_TYPE_KEY, "vector"},
76 {COSINUS_FREQUENCY_KEY, 100.}},
80 {COSINUS_FREQUENCY_KEY, 100.}},
77 dataSourceUid));
81 dataSourceUid));
78 magneticFieldFolder->appendChild(
82 magneticFieldFolder->appendChild(
79 createProductItem({{DataSourceItem::NAME_DATA_KEY, QStringLiteral("Spectrogram 1 Hz")},
83 createProductItem({{DataSourceItem::NAME_DATA_KEY, QStringLiteral("Spectrogram 1 Hz")},
80 {COSINUS_TYPE_KEY, "spectrogram"},
84 {COSINUS_TYPE_KEY, "spectrogram"},
81 {COSINUS_FREQUENCY_KEY, 1.}},
85 {COSINUS_FREQUENCY_KEY, 1.}},
82 dataSourceUid));
86 dataSourceUid));
83
87
84 // Electric field products
88 // Electric field products
85 auto electricFieldFolder = std::make_unique<DataSourceItem>(DataSourceItemType::NODE,
89 auto electricFieldFolder = std::make_unique<DataSourceItem>(DataSourceItemType::NODE,
86 QStringLiteral("Electric field"));
90 QStringLiteral("_Electric field"));
87
91
88 // Root
92 // Root
89 auto root = std::make_unique<DataSourceItem>(DataSourceItemType::NODE, DATA_SOURCE_NAME);
93 auto root = std::make_unique<DataSourceItem>(DataSourceItemType::NODE, DATA_SOURCE_NAME);
90 root->appendChild(std::move(magneticFieldFolder));
94 root->appendChild(std::move(magneticFieldFolder));
91 root->appendChild(std::move(electricFieldFolder));
95 root->appendChild(std::move(electricFieldFolder));
92
96
93 return root;
97 return root;
94 }
98 }
95
99
96 } // namespace
100 } // namespace
97
101
98 void MockPlugin::initialize()
102 void MockPlugin::initialize()
99 {
103 {
100 if (auto app = sqpApp) {
104 if (auto app = sqpApp) {
101 // Registers to the data source controller
105 // Registers to the data source controller
102 auto &dataSourceController = app->dataSourceController();
106 auto &dataSourceController = app->dataSourceController();
103 auto dataSourceUid = dataSourceController.registerDataSource(DATA_SOURCE_NAME);
107 auto dataSourceUid = dataSourceController.registerDataSource(DATA_SOURCE_NAME);
104
108
105 // Sets data source tree
109 // Sets data source tree
106 dataSourceController.setDataSourceItem(dataSourceUid, createDataSourceItem(dataSourceUid));
110 dataSourceController.setDataSourceItem(dataSourceUid, createDataSourceItem(dataSourceUid));
107
111
108 // Sets data provider
112 // Sets data provider
109 dataSourceController.setDataProvider(dataSourceUid, createDataProvider());
113 dataSourceController.setDataProvider(dataSourceUid, createDataProvider());
110 }
114 }
111 else {
115 else {
112 qCWarning(LOG_MockPlugin()) << tr("Can't access to SciQlop application");
116 qCWarning(LOG_MockPlugin()) << tr("Can't access to SciQlop application");
113 }
117 }
114 }
118 }
General Comments 0
You need to be logged in to leave comments. Login now