##// END OF EJS Templates
Add Catalogue methods
perrinel -
r1159:4c27015bd9c6
parent child
Show More
@@ -1,89 +1,89
1 /*------------------------------------------------------------------------------
1 /*------------------------------------------------------------------------------
2 -- This file is a part of the QLop Software
2 -- This file is a part of the QLop Software
3 -- Copyright (C) 2015, Plasma Physics Laboratory - CNRS
3 -- Copyright (C) 2015, Plasma Physics Laboratory - CNRS
4 --
4 --
5 -- This program is free software; you can redistribute it and/or modify
5 -- This program is free software; you can redistribute it and/or modify
6 -- it under the terms of the GNU General Public License as published by
6 -- it under the terms of the GNU General Public License as published by
7 -- the Free Software Foundation; either version 2 of the License, or
7 -- the Free Software Foundation; either version 2 of the License, or
8 -- (at your option) any later version.
8 -- (at your option) any later version.
9 --
9 --
10 -- This program is distributed in the hope that it will be useful,
10 -- This program is distributed in the hope that it will be useful,
11 -- but WITHOUT ANY WARRANTY; without even the implied warranty of
11 -- but WITHOUT ANY WARRANTY; without even the implied warranty of
12 -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 -- GNU General Public License for more details.
13 -- GNU General Public License for more details.
14 --
14 --
15 -- You should have received a copy of the GNU General Public License
15 -- You should have received a copy of the GNU General Public License
16 -- along with this program; if not, write to the Free Software
16 -- along with this program; if not, write to the Free Software
17 -- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 -- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 -------------------------------------------------------------------------------*/
18 -------------------------------------------------------------------------------*/
19 /*-- Author : Alexis Jeandet
19 /*-- Author : Alexis Jeandet
20 -- Mail : alexis.jeandet@member.fsf.org
20 -- Mail : alexis.jeandet@member.fsf.org
21 ----------------------------------------------------------------------------*/
21 ----------------------------------------------------------------------------*/
22 #include "MainWindow.h"
22 #include "MainWindow.h"
23 #include <QProcessEnvironment>
23 #include <QProcessEnvironment>
24 #include <QThread>
24 #include <QThread>
25 #include <SqpApplication.h>
25 #include <SqpApplication.h>
26 #include <qglobal.h>
26 #include <qglobal.h>
27
27
28 #include <QtPlugin>
29 #include <Plugin/PluginManager.h>
28 #include <Plugin/PluginManager.h>
30 #include <QDir>
29 #include <QDir>
30 #include <QtPlugin>
31
31
32 #include <QLoggingCategory>
32 #include <QLoggingCategory>
33
33
34 Q_LOGGING_CATEGORY(LOG_Main, "Main")
34 Q_LOGGING_CATEGORY(LOG_Main, "Main")
35
35
36 namespace {
36 namespace {
37
37
38 const auto PLUGIN_DIRECTORY_NAME = QStringLiteral("plugins");
38 const auto PLUGIN_DIRECTORY_NAME = QStringLiteral("plugins");
39
39
40
40
41 } // namespace
41 } // namespace
42
42
43 int main(int argc, char *argv[])
43 int main(int argc, char *argv[])
44 {
44 {
45 #ifdef QT_STATICPLUGIN
45 #ifdef QT_STATICPLUGIN
46 Q_IMPORT_PLUGIN(MockPlugin)
46 Q_IMPORT_PLUGIN(MockPlugin)
47 Q_IMPORT_PLUGIN(AmdaPlugin)
47 Q_IMPORT_PLUGIN(AmdaPlugin)
48 Q_INIT_RESOURCE(amdaresources);
48 Q_INIT_RESOURCE(amdaresources);
49 #endif
49 #endif
50 Q_INIT_RESOURCE(sqpguiresources);
50 Q_INIT_RESOURCE(sqpguiresources);
51
51
52 SqpApplication a{argc, argv};
52 SqpApplication a{argc, argv};
53 SqpApplication::setOrganizationName("LPP");
53 SqpApplication::setOrganizationName("LPP");
54 SqpApplication::setOrganizationDomain("lpp.fr");
54 SqpApplication::setOrganizationDomain("lpp.fr");
55 SqpApplication::setApplicationName("SciQLop");
55 SqpApplication::setApplicationName("SciQLop");
56 MainWindow w;
56 MainWindow w;
57 w.show();
57 w.show();
58
58
59 // Loads plugins
59 // Loads plugins
60 auto pluginDir = QDir{a.applicationDirPath()};
60 auto pluginDir = QDir{a.applicationDirPath()};
61 auto pluginLookupPath = {
61 auto pluginLookupPath = {
62 a.applicationDirPath(),
62 a.applicationDirPath(),
63 a.applicationDirPath() + "/" + PLUGIN_DIRECTORY_NAME,
63 a.applicationDirPath() + "/" + PLUGIN_DIRECTORY_NAME,
64 a.applicationDirPath() + "/../lib64/SciQlop",
64 a.applicationDirPath() + "/../lib64/SciQlop",
65 a.applicationDirPath() + "/../lib64/sciqlop",
65 a.applicationDirPath() + "/../lib64/sciqlop",
66 a.applicationDirPath() + "/../lib/SciQlop",
66 a.applicationDirPath() + "/../lib/SciQlop",
67 a.applicationDirPath() + "/../lib/sciqlop",
67 a.applicationDirPath() + "/../lib/sciqlop",
68 a.applicationDirPath() + "/../plugins",
68 a.applicationDirPath() + "/../plugins",
69 };
69 };
70
70
71 #if _WIN32 || _WIN64
71 #if _WIN32 || _WIN64
72 pluginDir.mkdir(PLUGIN_DIRECTORY_NAME);
72 pluginDir.mkdir(PLUGIN_DIRECTORY_NAME);
73 pluginDir.cd(PLUGIN_DIRECTORY_NAME);
73 pluginDir.cd(PLUGIN_DIRECTORY_NAME);
74 #endif
74 #endif
75
75
76 PluginManager pluginManager{};
76 PluginManager pluginManager{};
77
77
78 for (auto &&path : pluginLookupPath) {
78 for (auto &&path : pluginLookupPath) {
79 QDir directory{path};
79 QDir directory{path};
80 if (directory.exists()) {
80 if (directory.exists()) {
81 qCDebug(LOG_Main())
81 qCDebug(LOG_Main())
82 << QObject::tr("Plugin directory: %1").arg(directory.absolutePath());
82 << QObject::tr("Plugin directory: %1").arg(directory.absolutePath());
83 pluginManager.loadPlugins(directory);
83 pluginManager.loadPlugins(directory);
84 }
84 }
85 }
85 }
86 pluginManager.loadStaticPlugins();
86 pluginManager.loadStaticPlugins();
87
87
88 return a.exec();
88 return a.exec();
89 }
89 }
@@ -1,69 +1,74
1 #ifndef SCIQLOP_CATALOGUECONTROLLER_H
1 #ifndef SCIQLOP_CATALOGUECONTROLLER_H
2 #define SCIQLOP_CATALOGUECONTROLLER_H
2 #define SCIQLOP_CATALOGUECONTROLLER_H
3
3
4 #include "CoreGlobal.h"
4 #include "CoreGlobal.h"
5
5
6 #include <Data/SqpRange.h>
6 #include <Data/SqpRange.h>
7
7
8 #include <QLoggingCategory>
8 #include <QLoggingCategory>
9 #include <QObject>
9 #include <QObject>
10 #include <QUuid>
10 #include <QUuid>
11
11
12 #include <Common/spimpl.h>
12 #include <Common/spimpl.h>
13
13
14 #include <memory>
14 #include <memory>
15
15
16 class DBCatalogue;
16 class DBCatalogue;
17 class DBEvent;
17 class DBEvent;
18
18
19 Q_DECLARE_LOGGING_CATEGORY(LOG_CatalogueController)
19 Q_DECLARE_LOGGING_CATEGORY(LOG_CatalogueController)
20
20
21 class DataSourceItem;
21 class DataSourceItem;
22 class Variable;
22 class Variable;
23
23
24 /**
24 /**
25 * @brief The CatalogueController class aims to handle catalogues and event using the CatalogueAPI
25 * @brief The CatalogueController class aims to handle catalogues and event using the CatalogueAPI
26 * library.
26 * library.
27 */
27 */
28 class SCIQLOP_CORE_EXPORT CatalogueController : public QObject {
28 class SCIQLOP_CORE_EXPORT CatalogueController : public QObject {
29 Q_OBJECT
29 Q_OBJECT
30 public:
30 public:
31 explicit CatalogueController(QObject *parent = 0);
31 explicit CatalogueController(QObject *parent = 0);
32 virtual ~CatalogueController();
32 virtual ~CatalogueController();
33
33
34 // DB
34 // DB
35 // QStringList getRepositories() const;
35 QStringList getRepositories() const;
36 void addDB(const QString &dbPath);
36 void addDB(const QString &dbPath);
37 void saveDB(const QString &destinationPath, const QString &repository);
37 void saveDB(const QString &destinationPath, const QString &repository);
38
38
39 // Event
39 // Event
40 // bool createEvent(const QString &name);
40 /// retrieveEvents with empty repository retrieve them from the default repository
41 std::list<std::shared_ptr<DBEvent> > retrieveEvents(const QString &repository) const;
41 std::list<std::shared_ptr<DBEvent> > retrieveEvents(const QString &repository) const;
42 std::list<std::shared_ptr<DBEvent> > retrieveAllEvents() const;
42 std::list<std::shared_ptr<DBEvent> > retrieveAllEvents() const;
43 std::list<std::shared_ptr<DBEvent> >
43 std::list<std::shared_ptr<DBEvent> >
44 retrieveEventsFromCatalogue(std::shared_ptr<DBCatalogue> catalogue) const;
44 retrieveEventsFromCatalogue(std::shared_ptr<DBCatalogue> catalogue) const;
45 // void updateEvent(std::shared_ptr<DBEvent> event);
45 void addEvent(std::shared_ptr<DBEvent> event);
46 void updateEvent(std::shared_ptr<DBEvent> event);
47 void removeEvent(std::shared_ptr<DBEvent> event);
46 // void trashEvent(std::shared_ptr<DBEvent> event);
48 // void trashEvent(std::shared_ptr<DBEvent> event);
47 // void removeEvent(std::shared_ptr<DBEvent> event);
48 // void restore(QUuid eventId);
49 // void restore(QUuid eventId);
49 // void saveEvent(std::shared_ptr<DBEvent> event);
50 void saveEvent(std::shared_ptr<DBEvent> event);
50
51
51 // Catalogue
52 // Catalogue
52 // bool createCatalogue(const QString &name, QVector<QUuid> eventList);
53 // bool createCatalogue(const QString &name, QVector<QUuid> eventList);
53 std::list<std::shared_ptr<DBCatalogue> > getCatalogues(const QString &repository) const;
54 /// retrieveEvents with empty repository retrieve them from the default repository
54 // void removeEvent(QUuid catalogueId, const QString &repository);
55 std::list<std::shared_ptr<DBCatalogue> > retrieveCatalogues(const QString &repository) const;
55 // void saveCatalogue(std::shared_ptr<DBEvent> event);
56 void updateCatalogue(std::shared_ptr<DBCatalogue> catalogue);
57 void removeCatalogue(std::shared_ptr<DBCatalogue> catalogue);
58 void saveCatalogue(std::shared_ptr<DBCatalogue> catalogue);
59
60 void saveAll();
56
61
57 public slots:
62 public slots:
58 /// Manage init/end of the controller
63 /// Manage init/end of the controller
59 void initialize();
64 void initialize();
60 void finalize();
65 void finalize();
61
66
62 private:
67 private:
63 void waitForFinish();
68 void waitForFinish();
64
69
65 class CatalogueControllerPrivate;
70 class CatalogueControllerPrivate;
66 spimpl::unique_impl_ptr<CatalogueControllerPrivate> impl;
71 spimpl::unique_impl_ptr<CatalogueControllerPrivate> impl;
67 };
72 };
68
73
69 #endif // SCIQLOP_CATALOGUECONTROLLER_H
74 #endif // SCIQLOP_CATALOGUECONTROLLER_H
@@ -1,161 +1,287
1 #include <Catalogue/CatalogueController.h>
1 #include <Catalogue/CatalogueController.h>
2
2
3 #include <Variable/Variable.h>
3 #include <Variable/Variable.h>
4
4
5 #include <CatalogueDao.h>
5 #include <CatalogueDao.h>
6
6
7 #include <ComparaisonPredicate.h>
7 #include <ComparaisonPredicate.h>
8 #include <CompoundPredicate.h>
8 #include <CompoundPredicate.h>
9 #include <DBCatalogue.h>
9 #include <DBCatalogue.h>
10 #include <DBEvent.h>
10 #include <DBEvent.h>
11 #include <DBEventProduct.h>
11 #include <DBTag.h>
12 #include <DBTag.h>
12 #include <IRequestPredicate.h>
13 #include <IRequestPredicate.h>
13
14
14 #include <QMutex>
15 #include <QMutex>
15 #include <QThread>
16 #include <QThread>
16
17
17 #include <QDir>
18 #include <QDir>
18 #include <QStandardPaths>
19 #include <QStandardPaths>
19
20
20 Q_LOGGING_CATEGORY(LOG_CatalogueController, "CatalogueController")
21 Q_LOGGING_CATEGORY(LOG_CatalogueController, "CatalogueController")
21
22
22 namespace {
23 namespace {
23
24
24 static QString REPOSITORY_WORK_SUFFIX = QString{"Work"};
25 static QString REPOSITORY_WORK_SUFFIX = QString{"work"};
25
26 static QString REPOSITORY_TRASH_SUFFIX = QString{"trash"};
26 }
27 }
27
28
28 class CatalogueController::CatalogueControllerPrivate {
29 class CatalogueController::CatalogueControllerPrivate {
30
29 public:
31 public:
32 explicit CatalogueControllerPrivate(CatalogueController *parent) : m_Q{parent} {}
33
30 QMutex m_WorkingMutex;
34 QMutex m_WorkingMutex;
31 CatalogueDao m_CatalogueDao;
35 CatalogueDao m_CatalogueDao;
32
36
33 std::list<QString> m_RepositoryList;
37 QStringList m_RepositoryList;
38 CatalogueController *m_Q;
39
40 void copyDBtoDB(const QString &dbFrom, const QString &dbTo);
41 QString toWorkRepository(QString repository);
42 QString toSyncRepository(QString repository);
34 };
43 };
35
44
36 CatalogueController::CatalogueController(QObject *parent)
45 CatalogueController::CatalogueController(QObject *parent)
37 : impl{spimpl::make_unique_impl<CatalogueControllerPrivate>()}
46 : impl{spimpl::make_unique_impl<CatalogueControllerPrivate>(this)}
38 {
47 {
39 qCDebug(LOG_CatalogueController()) << tr("CatalogueController construction")
48 qCDebug(LOG_CatalogueController()) << tr("CatalogueController construction")
40 << QThread::currentThread();
49 << QThread::currentThread();
41 }
50 }
42
51
43 CatalogueController::~CatalogueController()
52 CatalogueController::~CatalogueController()
44 {
53 {
45 qCDebug(LOG_CatalogueController()) << tr("CatalogueController destruction")
54 qCDebug(LOG_CatalogueController()) << tr("CatalogueController destruction")
46 << QThread::currentThread();
55 << QThread::currentThread();
47 this->waitForFinish();
56 this->waitForFinish();
48 }
57 }
49
58
59 QStringList CatalogueController::getRepositories() const
60 {
61 return impl->m_RepositoryList;
62 }
63
50 void CatalogueController::addDB(const QString &dbPath)
64 void CatalogueController::addDB(const QString &dbPath)
51 {
65 {
52 QDir dbDir(dbPath);
66 QDir dbDir(dbPath);
53 if (dbDir.exists()) {
67 if (dbDir.exists()) {
54 auto dirName = dbDir.dirName();
68 auto dirName = dbDir.dirName();
55
69
56 if (std::find(impl->m_RepositoryList.cbegin(), impl->m_RepositoryList.cend(), dirName)
70 if (std::find(impl->m_RepositoryList.cbegin(), impl->m_RepositoryList.cend(), dirName)
57 != impl->m_RepositoryList.cend()) {
71 != impl->m_RepositoryList.cend()) {
58 qCCritical(LOG_CatalogueController())
72 qCCritical(LOG_CatalogueController())
59 << tr("Impossible to addDB that is already loaded");
73 << tr("Impossible to addDB that is already loaded");
60 }
74 }
61
75
62 if (!impl->m_CatalogueDao.addDB(dbPath, dirName)) {
76 if (!impl->m_CatalogueDao.addDB(dbPath, dirName)) {
63 qCCritical(LOG_CatalogueController())
77 qCCritical(LOG_CatalogueController())
64 << tr("Impossible to addDB %1 from %2 ").arg(dirName, dbPath);
78 << tr("Impossible to addDB %1 from %2 ").arg(dirName, dbPath);
65 }
79 }
66 else {
80 else {
67 impl->m_RepositoryList.push_back(dirName);
81 impl->m_RepositoryList << dirName;
82 impl->copyDBtoDB(dirName, impl->toWorkRepository(dirName));
68 }
83 }
69 }
84 }
70 else {
85 else {
71 qCCritical(LOG_CatalogueController()) << tr("Impossible to addDB that not exists: ")
86 qCCritical(LOG_CatalogueController()) << tr("Impossible to addDB that not exists: ")
72 << dbPath;
87 << dbPath;
73 }
88 }
74 }
89 }
75
90
76 void CatalogueController::saveDB(const QString &destinationPath, const QString &repository)
91 void CatalogueController::saveDB(const QString &destinationPath, const QString &repository)
77 {
92 {
78 if (!impl->m_CatalogueDao.saveDB(destinationPath, repository)) {
93 if (!impl->m_CatalogueDao.saveDB(destinationPath, repository)) {
79 qCCritical(LOG_CatalogueController())
94 qCCritical(LOG_CatalogueController())
80 << tr("Impossible to saveDB %1 from %2 ").arg(repository, destinationPath);
95 << tr("Impossible to saveDB %1 from %2 ").arg(repository, destinationPath);
81 }
96 }
82 }
97 }
83
98
84 std::list<std::shared_ptr<DBEvent> >
99 std::list<std::shared_ptr<DBEvent> >
85 CatalogueController::retrieveEvents(const QString &repository) const
100 CatalogueController::retrieveEvents(const QString &repository) const
86 {
101 {
102 QString dbDireName = repository.isEmpty() ? REPOSITORY_DEFAULT : repository;
103
87 auto eventsShared = std::list<std::shared_ptr<DBEvent> >{};
104 auto eventsShared = std::list<std::shared_ptr<DBEvent> >{};
88 auto events = impl->m_CatalogueDao.getEvents(repository);
105 auto events = impl->m_CatalogueDao.getEvents(impl->toWorkRepository(dbDireName));
89 for (auto event : events) {
106 for (auto event : events) {
90 eventsShared.push_back(std::make_shared<DBEvent>(event));
107 eventsShared.push_back(std::make_shared<DBEvent>(event));
91 }
108 }
92 return eventsShared;
109 return eventsShared;
93 }
110 }
94
111
95 std::list<std::shared_ptr<DBEvent> > CatalogueController::retrieveAllEvents() const
112 std::list<std::shared_ptr<DBEvent> > CatalogueController::retrieveAllEvents() const
96 {
113 {
97 auto eventsShared = std::list<std::shared_ptr<DBEvent> >{};
114 auto eventsShared = std::list<std::shared_ptr<DBEvent> >{};
98 for (auto repository : impl->m_RepositoryList) {
115 for (auto repository : impl->m_RepositoryList) {
99 eventsShared.splice(eventsShared.end(), retrieveEvents(repository));
116 eventsShared.splice(eventsShared.end(), retrieveEvents(repository));
100 }
117 }
101
118
102 return eventsShared;
119 return eventsShared;
103 }
120 }
104
121
105 std::list<std::shared_ptr<DBEvent> >
122 std::list<std::shared_ptr<DBEvent> >
106 CatalogueController::retrieveEventsFromCatalogue(std::shared_ptr<DBCatalogue> catalogue) const
123 CatalogueController::retrieveEventsFromCatalogue(std::shared_ptr<DBCatalogue> catalogue) const
107 {
124 {
108 auto eventsShared = std::list<std::shared_ptr<DBEvent> >{};
125 auto eventsShared = std::list<std::shared_ptr<DBEvent> >{};
109 auto events = impl->m_CatalogueDao.getCatalogueEvents(*catalogue);
126 auto events = impl->m_CatalogueDao.getCatalogueEvents(*catalogue);
110 for (auto event : events) {
127 for (auto event : events) {
111 eventsShared.push_back(std::make_shared<DBEvent>(event));
128 eventsShared.push_back(std::make_shared<DBEvent>(event));
112 }
129 }
113 return eventsShared;
130 return eventsShared;
114 }
131 }
115
132
133 void CatalogueController::updateEvent(std::shared_ptr<DBEvent> event)
134 {
135 event->setRepository(impl->toSyncRepository(event->getRepository()));
136
137 impl->m_CatalogueDao.updateEvent(*event);
138 }
139
140 void CatalogueController::removeEvent(std::shared_ptr<DBEvent> event)
141 {
142 // Remove it from both repository and repository_work
143 event->setRepository(impl->toWorkRepository(event->getRepository()));
144 impl->m_CatalogueDao.removeEvent(*event);
145 event->setRepository(impl->toSyncRepository(event->getRepository()));
146 impl->m_CatalogueDao.removeEvent(*event);
147 }
148
149 void CatalogueController::addEvent(std::shared_ptr<DBEvent> event)
150 {
151 event->setRepository(impl->toSyncRepository(event->getRepository()));
152
153 impl->m_CatalogueDao.addEvent(*event);
154
155 // Call update is necessary at the creation of add Event if it has some tags or some event
156 // products
157 if (!event->getEventProducts().empty() || !event->getTags().empty()) {
158 impl->m_CatalogueDao.updateEvent(*event);
159 }
160 }
161
162 void CatalogueController::saveEvent(std::shared_ptr<DBEvent> event)
163 {
164 impl->m_CatalogueDao.moveEvent(*event, impl->toSyncRepository(event->getRepository()), true);
165 }
166
116 std::list<std::shared_ptr<DBCatalogue> >
167 std::list<std::shared_ptr<DBCatalogue> >
117 CatalogueController::getCatalogues(const QString &repository) const
168 CatalogueController::retrieveCatalogues(const QString &repository) const
118 {
169 {
170 QString dbDireName = repository.isEmpty() ? REPOSITORY_DEFAULT : repository;
171
119 auto cataloguesShared = std::list<std::shared_ptr<DBCatalogue> >{};
172 auto cataloguesShared = std::list<std::shared_ptr<DBCatalogue> >{};
120 auto catalogues = impl->m_CatalogueDao.getCatalogues(repository);
173 auto catalogues = impl->m_CatalogueDao.getCatalogues(impl->toWorkRepository(dbDireName));
121 for (auto catalogue : catalogues) {
174 for (auto catalogue : catalogues) {
122 cataloguesShared.push_back(std::make_shared<DBCatalogue>(catalogue));
175 cataloguesShared.push_back(std::make_shared<DBCatalogue>(catalogue));
123 }
176 }
124 return cataloguesShared;
177 return cataloguesShared;
125 }
178 }
126
179
180 void CatalogueController::updateCatalogue(std::shared_ptr<DBCatalogue> catalogue)
181 {
182 catalogue->setRepository(impl->toSyncRepository(catalogue->getRepository()));
183
184 impl->m_CatalogueDao.updateCatalogue(*catalogue);
185 }
186
187 void CatalogueController::removeCatalogue(std::shared_ptr<DBCatalogue> catalogue)
188 {
189 // Remove it from both repository and repository_work
190 catalogue->setRepository(impl->toWorkRepository(catalogue->getRepository()));
191 impl->m_CatalogueDao.removeCatalogue(*catalogue);
192 catalogue->setRepository(impl->toSyncRepository(catalogue->getRepository()));
193 impl->m_CatalogueDao.removeCatalogue(*catalogue);
194 }
195
196 void CatalogueController::saveCatalogue(std::shared_ptr<DBCatalogue> catalogue)
197 {
198 impl->m_CatalogueDao.moveCatalogue(*catalogue,
199 impl->toSyncRepository(catalogue->getRepository()), true);
200 }
201
202 void CatalogueController::saveAll()
203 {
204 for (auto repository : impl->m_RepositoryList) {
205 // Save Event
206 auto events = this->retrieveEvents(repository);
207 for (auto event : events) {
208 this->saveEvent(event);
209 }
210
211 // Save Catalogue
212 auto catalogues = this->retrieveCatalogues(repository);
213 for (auto catalogue : catalogues) {
214 this->saveCatalogue(catalogue);
215 }
216 }
217 }
218
127 void CatalogueController::initialize()
219 void CatalogueController::initialize()
128 {
220 {
129 qCDebug(LOG_CatalogueController()) << tr("CatalogueController init")
221 qCDebug(LOG_CatalogueController()) << tr("CatalogueController init")
130 << QThread::currentThread();
222 << QThread::currentThread();
131 impl->m_WorkingMutex.lock();
223 impl->m_WorkingMutex.lock();
132 impl->m_CatalogueDao.initialize();
224 impl->m_CatalogueDao.initialize();
133 auto defaultRepositoryLocation
225 auto defaultRepositoryLocation
134 = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
226 = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
135
227
136 QDir defaultRepositoryLocationDir;
228 QDir defaultRepositoryLocationDir;
137 if (defaultRepositoryLocationDir.mkpath(defaultRepositoryLocation)) {
229 if (defaultRepositoryLocationDir.mkpath(defaultRepositoryLocation)) {
138 defaultRepositoryLocationDir.cd(defaultRepositoryLocation);
230 defaultRepositoryLocationDir.cd(defaultRepositoryLocation);
139 auto defaultRepository = defaultRepositoryLocationDir.absoluteFilePath(REPOSITORY_DEFAULT);
231 auto defaultRepository = defaultRepositoryLocationDir.absoluteFilePath(REPOSITORY_DEFAULT);
140 qCInfo(LOG_CatalogueController())
232 qCInfo(LOG_CatalogueController()) << tr("Persistant data loading from: ")
141 << tr("Persistant data loading from: ") << defaultRepository;
233 << defaultRepository;
142 this->addDB(defaultRepository);
234 this->addDB(defaultRepository);
143 }
235 }
144 else {
236 else {
145 qCWarning(LOG_CatalogueController())
237 qCWarning(LOG_CatalogueController())
146 << tr("Cannot load the persistent default repository from ")
238 << tr("Cannot load the persistent default repository from ")
147 << defaultRepositoryLocation;
239 << defaultRepositoryLocation;
148 }
240 }
149
241
150 qCDebug(LOG_CatalogueController()) << tr("CatalogueController init END");
242 qCDebug(LOG_CatalogueController()) << tr("CatalogueController init END");
151 }
243 }
152
244
153 void CatalogueController::finalize()
245 void CatalogueController::finalize()
154 {
246 {
155 impl->m_WorkingMutex.unlock();
247 impl->m_WorkingMutex.unlock();
156 }
248 }
157
249
158 void CatalogueController::waitForFinish()
250 void CatalogueController::waitForFinish()
159 {
251 {
160 QMutexLocker locker{&impl->m_WorkingMutex};
252 QMutexLocker locker{&impl->m_WorkingMutex};
161 }
253 }
254
255 void CatalogueController::CatalogueControllerPrivate::copyDBtoDB(const QString &dbFrom,
256 const QString &dbTo)
257 {
258 auto catalogues = m_Q->retrieveCatalogues(dbFrom);
259 auto events = m_Q->retrieveEvents(dbFrom);
260
261 for (auto catalogue : catalogues) {
262 m_CatalogueDao.copyCatalogue(*catalogue, dbTo, true);
263 }
264
265 for (auto event : events) {
266 m_CatalogueDao.copyEvent(*event, dbTo, true);
267 }
268 }
269
270 QString CatalogueController::CatalogueControllerPrivate::toWorkRepository(QString repository)
271 {
272 auto syncRepository = toSyncRepository(repository);
273
274 return QString("%1_%2").arg(syncRepository, REPOSITORY_WORK_SUFFIX);
275 }
276
277 QString CatalogueController::CatalogueControllerPrivate::toSyncRepository(QString repository)
278 {
279 auto syncRepository = repository;
280 if (repository.endsWith(REPOSITORY_WORK_SUFFIX)) {
281 syncRepository.remove(REPOSITORY_WORK_SUFFIX);
282 }
283 else if (repository.endsWith(REPOSITORY_TRASH_SUFFIX)) {
284 syncRepository.remove(REPOSITORY_TRASH_SUFFIX);
285 }
286 return syncRepository;
287 }
@@ -1,138 +1,137
1 #include <Plugin/PluginManager.h>
1 #include <Plugin/PluginManager.h>
2
2
3 #include <Plugin/IPlugin.h>
3 #include <Plugin/IPlugin.h>
4
4
5 #include <QDir>
5 #include <QDir>
6 #include <QLibrary>
6 #include <QLibrary>
7 #include <QPluginLoader>
7 #include <QPluginLoader>
8
8
9 Q_LOGGING_CATEGORY(LOG_PluginManager, "PluginManager")
9 Q_LOGGING_CATEGORY(LOG_PluginManager, "PluginManager")
10
10
11 namespace {
11 namespace {
12
12
13 /// Key for retrieving metadata of the plugin
13 /// Key for retrieving metadata of the plugin
14 const auto PLUGIN_METADATA_KEY = QStringLiteral("MetaData");
14 const auto PLUGIN_METADATA_KEY = QStringLiteral("MetaData");
15
15
16 /// Key for retrieving the name of the plugin in its metadata
16 /// Key for retrieving the name of the plugin in its metadata
17 const auto PLUGIN_NAME_KEY = QStringLiteral("name");
17 const auto PLUGIN_NAME_KEY = QStringLiteral("name");
18
18
19 /// Helper to state the plugin loading operation
19 /// Helper to state the plugin loading operation
20 struct LoadPluginState {
20 struct LoadPluginState {
21 explicit LoadPluginState(const QString &pluginPath)
21 explicit LoadPluginState(const QString &pluginPath)
22 : m_PluginPath{pluginPath}, m_Valid{true}, m_ErrorMessage{}
22 : m_PluginPath{pluginPath}, m_Valid{true}, m_ErrorMessage{}
23 {
23 {
24 }
24 }
25
25
26 void log() const
26 void log() const
27 {
27 {
28 if (m_Valid) {
28 if (m_Valid) {
29 qCDebug(LOG_PluginManager())
29 qCDebug(LOG_PluginManager())
30 << QObject::tr("File '%1' has been loaded as a plugin").arg(m_PluginPath);
30 << QObject::tr("File '%1' has been loaded as a plugin").arg(m_PluginPath);
31 }
31 }
32 else {
32 else {
33 qCWarning(LOG_PluginManager())
33 qCWarning(LOG_PluginManager())
34 << QObject::tr("File '%1' can't be loaded as a plugin: %2")
34 << QObject::tr("File '%1' can't be loaded as a plugin: %2")
35 .arg(m_PluginPath)
35 .arg(m_PluginPath)
36 .arg(m_ErrorMessage);
36 .arg(m_ErrorMessage);
37 }
37 }
38 }
38 }
39
39
40 void setError(const QString &errorMessage)
40 void setError(const QString &errorMessage)
41 {
41 {
42 m_Valid = false;
42 m_Valid = false;
43 m_ErrorMessage = errorMessage;
43 m_ErrorMessage = errorMessage;
44 }
44 }
45
45
46 QString m_PluginPath;
46 QString m_PluginPath;
47 bool m_Valid;
47 bool m_Valid;
48 QString m_ErrorMessage;
48 QString m_ErrorMessage;
49 };
49 };
50
50
51 } // namespace
51 } // namespace
52
52
53 struct PluginManager::PluginManagerPrivate {
53 struct PluginManager::PluginManagerPrivate {
54 /**
54 /**
55 * Loads a single plugin into SciQlop. The method has no effect if the plugin is malformed (e.g.
55 * Loads a single plugin into SciQlop. The method has no effect if the plugin is malformed (e.g.
56 * wrong library type, missing metadata, etc.)
56 * wrong library type, missing metadata, etc.)
57 * @param pluginPath the path to the plugin library.
57 * @param pluginPath the path to the plugin library.
58 */
58 */
59 void loadPlugin(const QString &pluginPath)
59 void loadPlugin(const QString &pluginPath)
60 {
60 {
61 qCDebug(LOG_PluginManager())
61 qCDebug(LOG_PluginManager())
62 << QObject::tr("Attempting to load file '%1' as a plugin").arg(pluginPath);
62 << QObject::tr("Attempting to load file '%1' as a plugin").arg(pluginPath);
63
63
64 LoadPluginState loadState{pluginPath};
64 LoadPluginState loadState{pluginPath};
65
65
66 if (QLibrary::isLibrary(pluginPath)) {
66 if (QLibrary::isLibrary(pluginPath)) {
67 QPluginLoader pluginLoader{pluginPath};
67 QPluginLoader pluginLoader{pluginPath};
68
68
69 // Retrieving the plugin name to check if it can be loaded (i.e. no plugin with the same
69 // Retrieving the plugin name to check if it can be loaded (i.e. no plugin with the same
70 // name has been registered yet)
70 // name has been registered yet)
71 auto metadata = pluginLoader.metaData().value(PLUGIN_METADATA_KEY).toObject();
71 auto metadata = pluginLoader.metaData().value(PLUGIN_METADATA_KEY).toObject();
72 auto pluginName = metadata.value(PLUGIN_NAME_KEY).toString();
72 auto pluginName = metadata.value(PLUGIN_NAME_KEY).toString();
73
73
74 if (pluginName.isEmpty()) {
74 if (pluginName.isEmpty()) {
75 loadState.setError(QObject::tr("empty file name"));
75 loadState.setError(QObject::tr("empty file name"));
76 }
76 }
77 else if (m_RegisteredPlugins.contains(pluginName)) {
77 else if (m_RegisteredPlugins.contains(pluginName)) {
78 loadState.setError(QObject::tr("name '%1' already registered").arg(pluginName));
78 loadState.setError(QObject::tr("name '%1' already registered").arg(pluginName));
79 }
79 }
80 else {
80 else {
81 if (auto pluginInstance = qobject_cast<IPlugin *>(pluginLoader.instance())) {
81 if (auto pluginInstance = qobject_cast<IPlugin *>(pluginLoader.instance())) {
82 pluginInstance->initialize();
82 pluginInstance->initialize();
83 m_RegisteredPlugins.insert(pluginName, pluginPath);
83 m_RegisteredPlugins.insert(pluginName, pluginPath);
84 }
84 }
85 else {
85 else {
86 loadState.setError(QObject::tr("the file is not a Sciqlop plugin"));
86 loadState.setError(QObject::tr("the file is not a Sciqlop plugin"));
87 }
87 }
88 }
88 }
89 }
89 }
90 else {
90 else {
91 loadState.setError(QObject::tr("the file is not a library"));
91 loadState.setError(QObject::tr("the file is not a library"));
92 }
92 }
93
93
94 // Log loading result
94 // Log loading result
95 loadState.log();
95 loadState.log();
96 }
96 }
97
97
98 void loadStaticPlugins()
98 void loadStaticPlugins()
99 {
99 {
100 for (QObject *plugin : QPluginLoader::staticInstances())
100 for (QObject *plugin : QPluginLoader::staticInstances()) {
101 {
102 qobject_cast<IPlugin *>(plugin)->initialize();
101 qobject_cast<IPlugin *>(plugin)->initialize();
103 m_RegisteredPlugins.insert(plugin->metaObject()->className(), "StaticPlugin");
102 m_RegisteredPlugins.insert(plugin->metaObject()->className(), "StaticPlugin");
104 }
103 }
105 }
104 }
106
105
107 /// Registered plugins (key: plugin name, value: plugin path)
106 /// Registered plugins (key: plugin name, value: plugin path)
108 QHash<QString, QString> m_RegisteredPlugins;
107 QHash<QString, QString> m_RegisteredPlugins;
109 };
108 };
110
109
111 PluginManager::PluginManager() : impl{spimpl::make_unique_impl<PluginManagerPrivate>()}
110 PluginManager::PluginManager() : impl{spimpl::make_unique_impl<PluginManagerPrivate>()}
112 {
111 {
113 }
112 }
114
113
115 void PluginManager::loadPlugins(const QDir &pluginDir)
114 void PluginManager::loadPlugins(const QDir &pluginDir)
116 {
115 {
117 // Load plugins
116 // Load plugins
118 auto pluginInfoList
117 auto pluginInfoList
119 = pluginDir.entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name);
118 = pluginDir.entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name);
120 for (auto entryInfo : qAsConst(pluginInfoList)) {
119 for (auto entryInfo : qAsConst(pluginInfoList)) {
121 if (entryInfo.isDir()) {
120 if (entryInfo.isDir()) {
122 this->loadPlugins(QDir{entryInfo.absoluteFilePath()});
121 this->loadPlugins(QDir{entryInfo.absoluteFilePath()});
123 }
122 }
124 else if (QLibrary::isLibrary(entryInfo.absoluteFilePath())) {
123 else if (QLibrary::isLibrary(entryInfo.absoluteFilePath())) {
125 impl->loadPlugin(entryInfo.absoluteFilePath());
124 impl->loadPlugin(entryInfo.absoluteFilePath());
126 }
125 }
127 }
126 }
128 }
127 }
129
128
130 void PluginManager::loadStaticPlugins()
129 void PluginManager::loadStaticPlugins()
131 {
130 {
132 impl->loadStaticPlugins();
131 impl->loadStaticPlugins();
133 }
132 }
134
133
135 int PluginManager::nbPluginsLoaded() const noexcept
134 int PluginManager::nbPluginsLoaded() const noexcept
136 {
135 {
137 return impl->m_RegisteredPlugins.size();
136 return impl->m_RegisteredPlugins.size();
138 }
137 }
@@ -1,206 +1,206
1 #include <Data/DataSeriesUtils.h>
1 #include <Data/DataSeriesUtils.h>
2
2
3 #include <QObject>
3 #include <QObject>
4 #include <QtTest>
4 #include <QtTest>
5
5
6 namespace {
6 namespace {
7
7
8 /// Path for resources
8 /// Path for resources
9 const auto TESTS_RESOURCES_PATH
9 const auto TESTS_RESOURCES_PATH
10 = QFileInfo{QString{CORE_TESTS_RESOURCES_DIR}, "TestDataSeriesUtils"}.absoluteFilePath();
10 = QFileInfo{QString{CORE_TESTS_RESOURCES_DIR}, "TestDataSeriesUtils"}.absoluteFilePath();
11
11
12 QString inputFilePath(const QString &inputFileName)
12 QString inputFilePath(const QString &inputFileName)
13 {
13 {
14 return QFileInfo{TESTS_RESOURCES_PATH, inputFileName}.absoluteFilePath();
14 return QFileInfo{TESTS_RESOURCES_PATH, inputFileName}.absoluteFilePath();
15 }
15 }
16
16
17 } // namespace
17 } // namespace
18
18
19 class TestDataSeriesUtils : public QObject {
19 class TestDataSeriesUtils : public QObject {
20 Q_OBJECT
20 Q_OBJECT
21
21
22 private slots:
22 private slots:
23 /// Tests @sa DataSeriesUtils::thresholds() method
23 /// Tests @sa DataSeriesUtils::thresholds() method
24 void testThresholds_data();
24 void testThresholds_data();
25 void testThresholds();
25 void testThresholds();
26
26
27 /// Tests @sa DataSeriesUtils::fillDataHoles() method
27 /// Tests @sa DataSeriesUtils::fillDataHoles() method
28 void testFillDataHoles_data();
28 void testFillDataHoles_data();
29 void testFillDataHoles();
29 void testFillDataHoles();
30 };
30 };
31
31
32 void TestDataSeriesUtils::testFillDataHoles_data()
32 void TestDataSeriesUtils::testFillDataHoles_data()
33 {
33 {
34 QTest::addColumn<std::vector<double> >("xAxisData");
34 QTest::addColumn<std::vector<double> >("xAxisData");
35 QTest::addColumn<std::vector<double> >("valuesData");
35 QTest::addColumn<std::vector<double> >("valuesData");
36 QTest::addColumn<double>("resolution");
36 QTest::addColumn<double>("resolution");
37 QTest::addColumn<double>("fillValue");
37 QTest::addColumn<double>("fillValue");
38 QTest::addColumn<double>("minBound");
38 QTest::addColumn<double>("minBound");
39 QTest::addColumn<double>("maxBound");
39 QTest::addColumn<double>("maxBound");
40 QTest::addColumn<std::vector<double> >(
40 QTest::addColumn<std::vector<double> >(
41 "expectedXAxisData"); // expected x-axis data after filling holes
41 "expectedXAxisData"); // expected x-axis data after filling holes
42 QTest::addColumn<std::vector<double> >(
42 QTest::addColumn<std::vector<double> >(
43 "expectedValuesData"); // expected values data after filling holes
43 "expectedValuesData"); // expected values data after filling holes
44
44
45 auto nan = std::numeric_limits<double>::quiet_NaN();
45 auto nan = std::numeric_limits<double>::quiet_NaN();
46
46
47 QTest::newRow("fillDataHoles (basic case)")
47 QTest::newRow("fillDataHoles (basic case)")
48 << std::vector<double>{0., 1., 5., 7., 14.} << std::vector<double>{0., 1., 2., 3., 4.} << 2.
48 << std::vector<double>{0., 1., 5., 7., 14.} << std::vector<double>{0., 1., 2., 3., 4.} << 2.
49 << nan << nan << nan << std::vector<double>{0., 1., 3., 5., 7., 9., 11., 13., 14.}
49 << nan << nan << nan << std::vector<double>{0., 1., 3., 5., 7., 9., 11., 13., 14.}
50 << std::vector<double>{0., 1., nan, 2., 3., nan, nan, nan, 4.};
50 << std::vector<double>{0., 1., nan, 2., 3., nan, nan, nan, 4.};
51
51
52 QTest::newRow("fillDataHoles (change nb components)")
52 QTest::newRow("fillDataHoles (change nb components)")
53 << std::vector<double>{0., 1., 5., 7., 14.}
53 << std::vector<double>{0., 1., 5., 7., 14.}
54 << std::vector<double>{0., 1., 2., 3., 4., 5., 6., 7., 8., 9.} << 2. << nan << nan << nan
54 << std::vector<double>{0., 1., 2., 3., 4., 5., 6., 7., 8., 9.} << 2. << nan << nan << nan
55 << std::vector<double>{0., 1., 3., 5., 7., 9., 11., 13., 14.}
55 << std::vector<double>{0., 1., 3., 5., 7., 9., 11., 13., 14.}
56 << std::vector<double>{0., 1., 2., 3., nan, nan, 4., 5., 6.,
56 << std::vector<double>{0., 1., 2., 3., nan, nan, 4., 5., 6.,
57 7., nan, nan, nan, nan, nan, nan, 8., 9.};
57 7., nan, nan, nan, nan, nan, nan, 8., 9.};
58
58
59 QTest::newRow("fillDataHoles (change resolution)")
59 QTest::newRow("fillDataHoles (change resolution)")
60 << std::vector<double>{0., 1., 5., 7., 14.} << std::vector<double>{0., 1., 2., 3., 4.}
60 << std::vector<double>{0., 1., 5., 7., 14.} << std::vector<double>{0., 1., 2., 3., 4.}
61 << 1.5 << nan << nan << nan
61 << 1.5 << nan << nan << nan
62 << std::vector<double>{0., 1., 2.5, 4., 5., 6.5, 7., 8.5, 10., 11.5, 13., 14.}
62 << std::vector<double>{0., 1., 2.5, 4., 5., 6.5, 7., 8.5, 10., 11.5, 13., 14.}
63 << std::vector<double>{0., 1., nan, nan, 2., nan, 3., nan, nan, nan, nan, 4.};
63 << std::vector<double>{0., 1., nan, nan, 2., nan, 3., nan, nan, nan, nan, 4.};
64
64
65 QTest::newRow("fillDataHoles (with no data (no changes made))")
65 QTest::newRow("fillDataHoles (with no data (no changes made))")
66 << std::vector<double>{} << std::vector<double>{} << 2. << nan << nan << nan
66 << std::vector<double>{} << std::vector<double>{} << 2. << nan << nan << nan
67 << std::vector<double>{} << std::vector<double>{};
67 << std::vector<double>{} << std::vector<double>{};
68
68
69 QTest::newRow("fillDataHoles (with no resolution (no changes made))")
69 QTest::newRow("fillDataHoles (with no resolution (no changes made))")
70 << std::vector<double>{0., 1., 5., 7., 14.} << std::vector<double>{0., 1., 2., 3., 4.} << 0.
70 << std::vector<double>{0., 1., 5., 7., 14.} << std::vector<double>{0., 1., 2., 3., 4.} << 0.
71 << nan << nan << nan << std::vector<double>{0., 1., 5., 7., 14.}
71 << nan << nan << nan << std::vector<double>{0., 1., 5., 7., 14.}
72 << std::vector<double>{0., 1., 2., 3., 4.};
72 << std::vector<double>{0., 1., 2., 3., 4.};
73
73
74 QTest::newRow("fillDataHoles (change fill value)")
74 QTest::newRow("fillDataHoles (change fill value)")
75 << std::vector<double>{0., 1., 5., 7., 14.} << std::vector<double>{0., 1., 2., 3., 4.} << 2.
75 << std::vector<double>{0., 1., 5., 7., 14.} << std::vector<double>{0., 1., 2., 3., 4.} << 2.
76 << -1. << nan << nan << std::vector<double>{0., 1., 3., 5., 7., 9., 11., 13., 14.}
76 << -1. << nan << nan << std::vector<double>{0., 1., 3., 5., 7., 9., 11., 13., 14.}
77 << std::vector<double>{0., 1., -1., 2., 3., -1., -1., -1., 4.};
77 << std::vector<double>{0., 1., -1., 2., 3., -1., -1., -1., 4.};
78
78
79 QTest::newRow("fillDataHoles (add data holes to the beginning)")
79 QTest::newRow("fillDataHoles (add data holes to the beginning)")
80 << std::vector<double>{5., 7., 9., 11., 13.} << std::vector<double>{0., 1., 2., 3., 4.}
80 << std::vector<double>{5., 7., 9., 11., 13.} << std::vector<double>{0., 1., 2., 3., 4.}
81 << 2. << nan << 0. << nan << std::vector<double>{1., 3., 5., 7., 9., 11., 13.}
81 << 2. << nan << 0. << nan << std::vector<double>{1., 3., 5., 7., 9., 11., 13.}
82 << std::vector<double>{nan, nan, 0., 1., 2., 3., 4.};
82 << std::vector<double>{nan, nan, 0., 1., 2., 3., 4.};
83
83
84 QTest::newRow("fillDataHoles (add data holes to the end)")
84 QTest::newRow("fillDataHoles (add data holes to the end)")
85 << std::vector<double>{5., 7., 9., 11., 13.} << std::vector<double>{0., 1., 2., 3., 4.}
85 << std::vector<double>{5., 7., 9., 11., 13.} << std::vector<double>{0., 1., 2., 3., 4.}
86 << 2. << nan << nan << 21. << std::vector<double>{5., 7., 9., 11., 13., 15., 17., 19., 21.}
86 << 2. << nan << nan << 21. << std::vector<double>{5., 7., 9., 11., 13., 15., 17., 19., 21.}
87 << std::vector<double>{0., 1., 2., 3., 4., nan, nan, nan, nan};
87 << std::vector<double>{0., 1., 2., 3., 4., nan, nan, nan, nan};
88
88
89 QTest::newRow("fillDataHoles (invalid min/max bounds (no changes made))")
89 QTest::newRow("fillDataHoles (invalid min/max bounds (no changes made))")
90 << std::vector<double>{5., 7., 9., 11., 13.} << std::vector<double>{0., 1., 2., 3., 4.}
90 << std::vector<double>{5., 7., 9., 11., 13.} << std::vector<double>{0., 1., 2., 3., 4.}
91 << 2. << nan << 8. << 13. << std::vector<double>{5., 7., 9., 11., 13.}
91 << 2. << nan << 8. << 13. << std::vector<double>{5., 7., 9., 11., 13.}
92 << std::vector<double>{0., 1., 2., 3., 4.};
92 << std::vector<double>{0., 1., 2., 3., 4.};
93 }
93 }
94
94
95 void TestDataSeriesUtils::testFillDataHoles()
95 void TestDataSeriesUtils::testFillDataHoles()
96 {
96 {
97 QFETCH(std::vector<double>, xAxisData);
97 QFETCH(std::vector<double>, xAxisData);
98 QFETCH(std::vector<double>, valuesData);
98 QFETCH(std::vector<double>, valuesData);
99 QFETCH(double, resolution);
99 QFETCH(double, resolution);
100 QFETCH(double, fillValue);
100 QFETCH(double, fillValue);
101 QFETCH(double, minBound);
101 QFETCH(double, minBound);
102 QFETCH(double, maxBound);
102 QFETCH(double, maxBound);
103
103
104 QFETCH(std::vector<double>, expectedXAxisData);
104 QFETCH(std::vector<double>, expectedXAxisData);
105 QFETCH(std::vector<double>, expectedValuesData);
105 QFETCH(std::vector<double>, expectedValuesData);
106
106
107 // Executes method (xAxisData and valuesData are modified)
107 // Executes method (xAxisData and valuesData are modified)
108 DataSeriesUtils::fillDataHoles(xAxisData, valuesData, resolution, fillValue, minBound,
108 DataSeriesUtils::fillDataHoles(xAxisData, valuesData, resolution, fillValue, minBound,
109 maxBound);
109 maxBound);
110
110
111 // Checks results
111 // Checks results
112 auto equal = [](const auto &data, const auto &expectedData) {
112 auto equal = [](const auto &data, const auto &expectedData) {
113 // Compares with NaN values
113 // Compares with NaN values
114 return std::equal(data.begin(), data.end(), expectedData.begin(), expectedData.end(),
114 return std::equal(data.begin(), data.end(), expectedData.begin(), expectedData.end(),
115 [](const auto &val, const auto &expectedVal) {
115 [](const auto &val, const auto &expectedVal) {
116 return (std::isnan(val) && std::isnan(expectedVal))
116 return (std::isnan(val) && std::isnan(expectedVal))
117 || val == expectedVal;
117 || val == expectedVal;
118 });
118 });
119 };
119 };
120 QVERIFY(equal(xAxisData, expectedXAxisData));
120 QVERIFY(equal(xAxisData, expectedXAxisData));
121 QVERIFY(equal(valuesData, expectedValuesData));
121 QVERIFY(equal(valuesData, expectedValuesData));
122 }
122 }
123
123
124 namespace {
124 namespace {
125
125
126 const auto LINE_SEP = QRegularExpression{QStringLiteral("\\s+")};
126 const auto LINE_SEP = QRegularExpression{QStringLiteral("\\s+")};
127
127
128 std::vector<double> fromFile(const QString &filePath)
128 std::vector<double> fromFile(const QString &filePath)
129 {
129 {
130 QFile file{filePath};
130 QFile file{filePath};
131
131
132 if (!file.open(QFile::ReadOnly | QIODevice::Text)) {
132 if (!file.open(QFile::ReadOnly | QIODevice::Text)) {
133 return {};
133 return {};
134 }
134 }
135
135
136 std::vector<double> result{};
136 std::vector<double> result{};
137
137
138 QTextStream stream{&file};
138 QTextStream stream{&file};
139 QString line{};
139 QString line{};
140
140
141 while (stream.readLineInto(&line)) {
141 while (stream.readLineInto(&line)) {
142 auto lineData = line.split(LINE_SEP, QString::SkipEmptyParts);
142 auto lineData = line.split(LINE_SEP, QString::SkipEmptyParts);
143
143
144 for (auto data : lineData) {
144 for (auto data : lineData) {
145 bool valueOk;
145 bool valueOk;
146 auto value = data.toDouble(&valueOk);
146 auto value = data.toDouble(&valueOk);
147
147
148 result.push_back(valueOk ? value : std::numeric_limits<double>::quiet_NaN());
148 result.push_back(valueOk ? value : std::numeric_limits<double>::quiet_NaN());
149 }
149 }
150 }
150 }
151
151
152 return result;
152 return result;
153 }
153 }
154
154
155 } // namespace
155 } // namespace
156
156
157 void TestDataSeriesUtils::testThresholds_data()
157 void TestDataSeriesUtils::testThresholds_data()
158 {
158 {
159 QTest::addColumn<std::vector<double> >("input");
159 QTest::addColumn<std::vector<double> >("input");
160 QTest::addColumn<bool>("logarithmic");
160 QTest::addColumn<bool>("logarithmic");
161 QTest::addColumn<double>("expectedMinThreshold");
161 QTest::addColumn<double>("expectedMinThreshold");
162 QTest::addColumn<double>("expectedMaxThreshold");
162 QTest::addColumn<double>("expectedMaxThreshold");
163
163
164 auto nan = std::numeric_limits<double>::quiet_NaN();
164 auto nan = std::numeric_limits<double>::quiet_NaN();
165
165
166 QTest::newRow("thresholds (basic case)")
166 QTest::newRow("thresholds (basic case)")
167 << std::vector<double>{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.} << false << 1. << 10.;
167 << std::vector<double>{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.} << false << 1. << 10.;
168
168
169 QTest::newRow("thresholds (with nan values)")
169 QTest::newRow("thresholds (with nan values)")
170 << std::vector<double>{nan, 2., 3., 4., 5., 6., 7., 8., 9., nan} << false << 2. << 9.;
170 << std::vector<double>{nan, 2., 3., 4., 5., 6., 7., 8., 9., nan} << false << 2. << 9.;
171
171
172 QTest::newRow("thresholds (case with low values and aberrant value)")
172 QTest::newRow("thresholds (case with low values and aberrant value)")
173 << std::vector<double>{1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 2.,
173 << std::vector<double>{1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 2.,
174 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 3., 3.,
174 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 3., 3.,
175 3., 3., 3., 3., 3., 3., 3., 3., 3., 3., 3., 3., 3., 100.}
175 3., 3., 3., 3., 3., 3., 3., 3., 3., 3., 3., 3., 3., 100.}
176 << false << 1. << 47.073;
176 << false << 1. << 47.073;
177
177
178 QTest::newRow("thresholds (empty data)") << std::vector<double>{} << false << nan << nan;
178 QTest::newRow("thresholds (empty data)") << std::vector<double>{} << false << nan << nan;
179 QTest::newRow("thresholds (only nan values)")
179 QTest::newRow("thresholds (only nan values)") << std::vector<double>{nan, nan, nan, nan, nan}
180 << std::vector<double>{nan, nan, nan, nan, nan} << false << nan << nan;
180 << false << nan << nan;
181
181
182 QTest::newRow("thresholds (from file with logarithmic scale)")
182 QTest::newRow("thresholds (from file with logarithmic scale)")
183 << fromFile(inputFilePath("TestThresholds.txt")) << true << 832.005 << 17655064.730;
183 << fromFile(inputFilePath("TestThresholds.txt")) << true << 832.005 << 17655064.730;
184 }
184 }
185
185
186 void TestDataSeriesUtils::testThresholds()
186 void TestDataSeriesUtils::testThresholds()
187 {
187 {
188 QFETCH(std::vector<double>, input);
188 QFETCH(std::vector<double>, input);
189 QFETCH(bool, logarithmic);
189 QFETCH(bool, logarithmic);
190 QFETCH(double, expectedMinThreshold);
190 QFETCH(double, expectedMinThreshold);
191 QFETCH(double, expectedMaxThreshold);
191 QFETCH(double, expectedMaxThreshold);
192
192
193 double minThreshold, maxThreshold;
193 double minThreshold, maxThreshold;
194 std::tie(minThreshold, maxThreshold)
194 std::tie(minThreshold, maxThreshold)
195 = DataSeriesUtils::thresholds(input.begin(), input.end(), logarithmic);
195 = DataSeriesUtils::thresholds(input.begin(), input.end(), logarithmic);
196
196
197 auto compareWithNaN = [](const auto &v1, const auto &v2) {
197 auto compareWithNaN = [](const auto &v1, const auto &v2) {
198 return (std::isnan(v1) && std::isnan(v2)) || std::abs(v1 - v2) < 1e-3;
198 return (std::isnan(v1) && std::isnan(v2)) || std::abs(v1 - v2) < 1e-3;
199 };
199 };
200
200
201 QVERIFY(compareWithNaN(minThreshold, expectedMinThreshold));
201 QVERIFY(compareWithNaN(minThreshold, expectedMinThreshold));
202 QVERIFY(compareWithNaN(maxThreshold, expectedMaxThreshold));
202 QVERIFY(compareWithNaN(maxThreshold, expectedMaxThreshold));
203 }
203 }
204
204
205 QTEST_MAIN(TestDataSeriesUtils)
205 QTEST_MAIN(TestDataSeriesUtils)
206 #include "TestDataSeriesUtils.moc"
206 #include "TestDataSeriesUtils.moc"
@@ -1,247 +1,247
1 #include "Catalogue/CatalogueSideBarWidget.h"
1 #include "Catalogue/CatalogueSideBarWidget.h"
2 #include "ui_CatalogueSideBarWidget.h"
2 #include "ui_CatalogueSideBarWidget.h"
3 #include <SqpApplication.h>
3 #include <SqpApplication.h>
4
4
5 #include <Catalogue/CatalogueController.h>
5 #include <Catalogue/CatalogueController.h>
6 #include <Catalogue/CatalogueTreeWidgetItem.h>
6 #include <Catalogue/CatalogueTreeWidgetItem.h>
7 #include <CatalogueDao.h>
7 #include <CatalogueDao.h>
8 #include <ComparaisonPredicate.h>
8 #include <ComparaisonPredicate.h>
9 #include <DBCatalogue.h>
9 #include <DBCatalogue.h>
10
10
11 #include <QMenu>
11 #include <QMenu>
12
12
13 Q_LOGGING_CATEGORY(LOG_CatalogueSideBarWidget, "CatalogueSideBarWidget")
13 Q_LOGGING_CATEGORY(LOG_CatalogueSideBarWidget, "CatalogueSideBarWidget")
14
14
15
15
16 constexpr auto ALL_EVENT_ITEM_TYPE = QTreeWidgetItem::UserType;
16 constexpr auto ALL_EVENT_ITEM_TYPE = QTreeWidgetItem::UserType;
17 constexpr auto TRASH_ITEM_TYPE = QTreeWidgetItem::UserType + 1;
17 constexpr auto TRASH_ITEM_TYPE = QTreeWidgetItem::UserType + 1;
18 constexpr auto CATALOGUE_ITEM_TYPE = QTreeWidgetItem::UserType + 2;
18 constexpr auto CATALOGUE_ITEM_TYPE = QTreeWidgetItem::UserType + 2;
19 constexpr auto DATABASE_ITEM_TYPE = QTreeWidgetItem::UserType + 3;
19 constexpr auto DATABASE_ITEM_TYPE = QTreeWidgetItem::UserType + 3;
20
20
21
21
22 struct CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate {
22 struct CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate {
23
23
24 void configureTreeWidget(QTreeWidget *treeWidget);
24 void configureTreeWidget(QTreeWidget *treeWidget);
25 QTreeWidgetItem *addDatabaseItem(const QString &name, QTreeWidget *treeWidget);
25 QTreeWidgetItem *addDatabaseItem(const QString &name, QTreeWidget *treeWidget);
26 QTreeWidgetItem *getDatabaseItem(const QString &name, QTreeWidget *treeWidget);
26 QTreeWidgetItem *getDatabaseItem(const QString &name, QTreeWidget *treeWidget);
27 void addCatalogueItem(const std::shared_ptr<DBCatalogue> &catalogue,
27 void addCatalogueItem(const std::shared_ptr<DBCatalogue> &catalogue,
28 QTreeWidgetItem *parentDatabaseItem);
28 QTreeWidgetItem *parentDatabaseItem);
29
29
30 CatalogueTreeWidgetItem *getCatalogueItem(const std::shared_ptr<DBCatalogue> &catalogue,
30 CatalogueTreeWidgetItem *getCatalogueItem(const std::shared_ptr<DBCatalogue> &catalogue,
31 QTreeWidget *treeWidget) const;
31 QTreeWidget *treeWidget) const;
32 };
32 };
33
33
34 CatalogueSideBarWidget::CatalogueSideBarWidget(QWidget *parent)
34 CatalogueSideBarWidget::CatalogueSideBarWidget(QWidget *parent)
35 : QWidget(parent),
35 : QWidget(parent),
36 ui(new Ui::CatalogueSideBarWidget),
36 ui(new Ui::CatalogueSideBarWidget),
37 impl{spimpl::make_unique_impl<CatalogueSideBarWidgetPrivate>()}
37 impl{spimpl::make_unique_impl<CatalogueSideBarWidgetPrivate>()}
38 {
38 {
39 ui->setupUi(this);
39 ui->setupUi(this);
40 impl->configureTreeWidget(ui->treeWidget);
40 impl->configureTreeWidget(ui->treeWidget);
41
41
42 ui->treeWidget->setColumnCount(2);
42 ui->treeWidget->setColumnCount(2);
43 ui->treeWidget->header()->setStretchLastSection(false);
43 ui->treeWidget->header()->setStretchLastSection(false);
44 ui->treeWidget->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
44 ui->treeWidget->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
45 ui->treeWidget->header()->setSectionResizeMode(0, QHeaderView::Stretch);
45 ui->treeWidget->header()->setSectionResizeMode(0, QHeaderView::Stretch);
46
46
47 auto emitSelection = [this]() {
47 auto emitSelection = [this]() {
48
48
49 auto selectedItems = ui->treeWidget->selectedItems();
49 auto selectedItems = ui->treeWidget->selectedItems();
50 if (selectedItems.isEmpty()) {
50 if (selectedItems.isEmpty()) {
51 emit this->selectionCleared();
51 emit this->selectionCleared();
52 }
52 }
53 else {
53 else {
54 QVector<std::shared_ptr<DBCatalogue> > catalogues;
54 QVector<std::shared_ptr<DBCatalogue> > catalogues;
55 QStringList databases;
55 QStringList databases;
56 int selectionType = selectedItems.first()->type();
56 int selectionType = selectedItems.first()->type();
57
57
58 for (auto item : ui->treeWidget->selectedItems()) {
58 for (auto item : ui->treeWidget->selectedItems()) {
59 if (item->type() == selectionType) {
59 if (item->type() == selectionType) {
60 switch (selectionType) {
60 switch (selectionType) {
61 case CATALOGUE_ITEM_TYPE:
61 case CATALOGUE_ITEM_TYPE:
62 catalogues.append(
62 catalogues.append(
63 static_cast<CatalogueTreeWidgetItem *>(item)->catalogue());
63 static_cast<CatalogueTreeWidgetItem *>(item)->catalogue());
64 break;
64 break;
65 case DATABASE_ITEM_TYPE:
65 case DATABASE_ITEM_TYPE:
66 selectionType = DATABASE_ITEM_TYPE;
66 selectionType = DATABASE_ITEM_TYPE;
67 databases.append(item->text(0));
67 databases.append(item->text(0));
68 case ALL_EVENT_ITEM_TYPE: // fallthrough
68 case ALL_EVENT_ITEM_TYPE: // fallthrough
69 case TRASH_ITEM_TYPE: // fallthrough
69 case TRASH_ITEM_TYPE: // fallthrough
70 default:
70 default:
71 break;
71 break;
72 }
72 }
73 }
73 }
74 else {
74 else {
75 // Incoherent multi selection
75 // Incoherent multi selection
76 selectionType = -1;
76 selectionType = -1;
77 break;
77 break;
78 }
78 }
79 }
79 }
80
80
81 switch (selectionType) {
81 switch (selectionType) {
82 case CATALOGUE_ITEM_TYPE:
82 case CATALOGUE_ITEM_TYPE:
83 emit this->catalogueSelected(catalogues);
83 emit this->catalogueSelected(catalogues);
84 break;
84 break;
85 case DATABASE_ITEM_TYPE:
85 case DATABASE_ITEM_TYPE:
86 emit this->databaseSelected(databases);
86 emit this->databaseSelected(databases);
87 break;
87 break;
88 case ALL_EVENT_ITEM_TYPE:
88 case ALL_EVENT_ITEM_TYPE:
89 emit this->allEventsSelected();
89 emit this->allEventsSelected();
90 break;
90 break;
91 case TRASH_ITEM_TYPE:
91 case TRASH_ITEM_TYPE:
92 emit this->trashSelected();
92 emit this->trashSelected();
93 break;
93 break;
94 default:
94 default:
95 emit this->selectionCleared();
95 emit this->selectionCleared();
96 break;
96 break;
97 }
97 }
98 }
98 }
99
99
100
100
101 };
101 };
102
102
103 connect(ui->treeWidget, &QTreeWidget::itemClicked, emitSelection);
103 connect(ui->treeWidget, &QTreeWidget::itemClicked, emitSelection);
104 connect(ui->treeWidget, &QTreeWidget::currentItemChanged, emitSelection);
104 connect(ui->treeWidget, &QTreeWidget::currentItemChanged, emitSelection);
105 connect(ui->treeWidget, &QTreeWidget::itemChanged,
105 connect(ui->treeWidget, &QTreeWidget::itemChanged,
106 [emitSelection, this](auto item, auto column) {
106 [emitSelection, this](auto item, auto column) {
107 auto selectedItems = ui->treeWidget->selectedItems();
107 auto selectedItems = ui->treeWidget->selectedItems();
108 qDebug() << "ITEM CHANGED" << column;
108 qDebug() << "ITEM CHANGED" << column;
109 if (selectedItems.contains(item) && column == 0) {
109 if (selectedItems.contains(item) && column == 0) {
110 emitSelection();
110 emitSelection();
111 }
111 }
112 });
112 });
113
113
114 ui->treeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
114 ui->treeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
115 connect(ui->treeWidget, &QTreeWidget::customContextMenuRequested, this,
115 connect(ui->treeWidget, &QTreeWidget::customContextMenuRequested, this,
116 &CatalogueSideBarWidget::onContextMenuRequested);
116 &CatalogueSideBarWidget::onContextMenuRequested);
117 }
117 }
118
118
119 CatalogueSideBarWidget::~CatalogueSideBarWidget()
119 CatalogueSideBarWidget::~CatalogueSideBarWidget()
120 {
120 {
121 delete ui;
121 delete ui;
122 }
122 }
123
123
124 void CatalogueSideBarWidget::setCatalogueChanges(const std::shared_ptr<DBCatalogue> &catalogue,
124 void CatalogueSideBarWidget::setCatalogueChanges(const std::shared_ptr<DBCatalogue> &catalogue,
125 bool hasChanges)
125 bool hasChanges)
126 {
126 {
127 if (auto catalogueItem = impl->getCatalogueItem(catalogue, ui->treeWidget)) {
127 if (auto catalogueItem = impl->getCatalogueItem(catalogue, ui->treeWidget)) {
128 catalogueItem->setHasChanges(hasChanges);
128 catalogueItem->setHasChanges(hasChanges);
129 catalogueItem->refresh();
129 catalogueItem->refresh();
130 }
130 }
131 }
131 }
132
132
133 void CatalogueSideBarWidget::onContextMenuRequested(const QPoint &pos)
133 void CatalogueSideBarWidget::onContextMenuRequested(const QPoint &pos)
134 {
134 {
135 QMenu menu{this};
135 QMenu menu{this};
136
136
137 auto currentItem = ui->treeWidget->currentItem();
137 auto currentItem = ui->treeWidget->currentItem();
138 switch (currentItem->type()) {
138 switch (currentItem->type()) {
139 case CATALOGUE_ITEM_TYPE:
139 case CATALOGUE_ITEM_TYPE:
140 menu.addAction("Rename",
140 menu.addAction("Rename",
141 [this, currentItem]() { ui->treeWidget->editItem(currentItem); });
141 [this, currentItem]() { ui->treeWidget->editItem(currentItem); });
142 break;
142 break;
143 case DATABASE_ITEM_TYPE:
143 case DATABASE_ITEM_TYPE:
144 break;
144 break;
145 case ALL_EVENT_ITEM_TYPE:
145 case ALL_EVENT_ITEM_TYPE:
146 break;
146 break;
147 case TRASH_ITEM_TYPE:
147 case TRASH_ITEM_TYPE:
148 menu.addAction("Empty Trash", []() {
148 menu.addAction("Empty Trash", []() {
149 // TODO
149 // TODO
150 });
150 });
151 break;
151 break;
152 default:
152 default:
153 break;
153 break;
154 }
154 }
155
155
156 if (!menu.isEmpty()) {
156 if (!menu.isEmpty()) {
157 menu.exec(ui->treeWidget->mapToGlobal(pos));
157 menu.exec(ui->treeWidget->mapToGlobal(pos));
158 }
158 }
159 }
159 }
160
160
161 void CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::configureTreeWidget(
161 void CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::configureTreeWidget(
162 QTreeWidget *treeWidget)
162 QTreeWidget *treeWidget)
163 {
163 {
164 auto allEventsItem = new QTreeWidgetItem{{"All Events"}, ALL_EVENT_ITEM_TYPE};
164 auto allEventsItem = new QTreeWidgetItem{{"All Events"}, ALL_EVENT_ITEM_TYPE};
165 allEventsItem->setIcon(0, QIcon(":/icones/allEvents.png"));
165 allEventsItem->setIcon(0, QIcon(":/icones/allEvents.png"));
166 treeWidget->addTopLevelItem(allEventsItem);
166 treeWidget->addTopLevelItem(allEventsItem);
167
167
168 auto trashItem = new QTreeWidgetItem{{"Trash"}, TRASH_ITEM_TYPE};
168 auto trashItem = new QTreeWidgetItem{{"Trash"}, TRASH_ITEM_TYPE};
169 trashItem->setIcon(0, QIcon(":/icones/trash.png"));
169 trashItem->setIcon(0, QIcon(":/icones/trash.png"));
170 treeWidget->addTopLevelItem(trashItem);
170 treeWidget->addTopLevelItem(trashItem);
171
171
172 auto separator = new QFrame{treeWidget};
172 auto separator = new QFrame{treeWidget};
173 separator->setFrameShape(QFrame::HLine);
173 separator->setFrameShape(QFrame::HLine);
174 auto separatorItem = new QTreeWidgetItem{};
174 auto separatorItem = new QTreeWidgetItem{};
175 separatorItem->setFlags(Qt::NoItemFlags);
175 separatorItem->setFlags(Qt::NoItemFlags);
176 treeWidget->addTopLevelItem(separatorItem);
176 treeWidget->addTopLevelItem(separatorItem);
177 treeWidget->setItemWidget(separatorItem, 0, separator);
177 treeWidget->setItemWidget(separatorItem, 0, separator);
178
178
179 auto db = addDatabaseItem("Default", treeWidget);
179 auto db = addDatabaseItem("Default", treeWidget);
180
180
181 auto catalogues = sqpApp->catalogueController().getCatalogues("Default");
181 auto catalogues = sqpApp->catalogueController().retrieveCatalogues("Default");
182 for (auto catalogue : catalogues) {
182 for (auto catalogue : catalogues) {
183 addCatalogueItem(catalogue, db);
183 addCatalogueItem(catalogue, db);
184 }
184 }
185
185
186 treeWidget->expandAll();
186 treeWidget->expandAll();
187 }
187 }
188
188
189 QTreeWidgetItem *
189 QTreeWidgetItem *
190 CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::addDatabaseItem(const QString &name,
190 CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::addDatabaseItem(const QString &name,
191 QTreeWidget *treeWidget)
191 QTreeWidget *treeWidget)
192 {
192 {
193 auto databaseItem = new QTreeWidgetItem{{name}, DATABASE_ITEM_TYPE};
193 auto databaseItem = new QTreeWidgetItem{{name}, DATABASE_ITEM_TYPE};
194 databaseItem->setIcon(0, QIcon{":/icones/database.png"});
194 databaseItem->setIcon(0, QIcon{":/icones/database.png"});
195 treeWidget->addTopLevelItem(databaseItem);
195 treeWidget->addTopLevelItem(databaseItem);
196
196
197 return databaseItem;
197 return databaseItem;
198 }
198 }
199
199
200 QTreeWidgetItem *
200 QTreeWidgetItem *
201 CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::getDatabaseItem(const QString &name,
201 CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::getDatabaseItem(const QString &name,
202 QTreeWidget *treeWidget)
202 QTreeWidget *treeWidget)
203 {
203 {
204 for (auto i = 0; i < treeWidget->topLevelItemCount(); ++i) {
204 for (auto i = 0; i < treeWidget->topLevelItemCount(); ++i) {
205 auto item = treeWidget->topLevelItem(i);
205 auto item = treeWidget->topLevelItem(i);
206 if (item->type() == DATABASE_ITEM_TYPE && item->text(0) == name) {
206 if (item->type() == DATABASE_ITEM_TYPE && item->text(0) == name) {
207 return item;
207 return item;
208 }
208 }
209 }
209 }
210
210
211 return nullptr;
211 return nullptr;
212 }
212 }
213
213
214 void CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::addCatalogueItem(
214 void CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::addCatalogueItem(
215 const std::shared_ptr<DBCatalogue> &catalogue, QTreeWidgetItem *parentDatabaseItem)
215 const std::shared_ptr<DBCatalogue> &catalogue, QTreeWidgetItem *parentDatabaseItem)
216 {
216 {
217 auto catalogueItem = new CatalogueTreeWidgetItem{catalogue, CATALOGUE_ITEM_TYPE};
217 auto catalogueItem = new CatalogueTreeWidgetItem{catalogue, CATALOGUE_ITEM_TYPE};
218 catalogueItem->setIcon(0, QIcon{":/icones/catalogue.png"});
218 catalogueItem->setIcon(0, QIcon{":/icones/catalogue.png"});
219 parentDatabaseItem->addChild(catalogueItem);
219 parentDatabaseItem->addChild(catalogueItem);
220 }
220 }
221
221
222 CatalogueTreeWidgetItem *CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::getCatalogueItem(
222 CatalogueTreeWidgetItem *CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::getCatalogueItem(
223 const std::shared_ptr<DBCatalogue> &catalogue, QTreeWidget *treeWidget) const
223 const std::shared_ptr<DBCatalogue> &catalogue, QTreeWidget *treeWidget) const
224 {
224 {
225 for (auto i = 0; i < treeWidget->topLevelItemCount(); ++i) {
225 for (auto i = 0; i < treeWidget->topLevelItemCount(); ++i) {
226 auto item = treeWidget->topLevelItem(i);
226 auto item = treeWidget->topLevelItem(i);
227 if (item->type() == DATABASE_ITEM_TYPE) {
227 if (item->type() == DATABASE_ITEM_TYPE) {
228 for (auto j = 0; j < item->childCount(); ++j) {
228 for (auto j = 0; j < item->childCount(); ++j) {
229 auto childItem = item->child(j);
229 auto childItem = item->child(j);
230 if (childItem->type() == CATALOGUE_ITEM_TYPE) {
230 if (childItem->type() == CATALOGUE_ITEM_TYPE) {
231 auto catalogueItem = static_cast<CatalogueTreeWidgetItem *>(childItem);
231 auto catalogueItem = static_cast<CatalogueTreeWidgetItem *>(childItem);
232 if (catalogueItem->catalogue() == catalogue) {
232 if (catalogueItem->catalogue() == catalogue) {
233 return catalogueItem;
233 return catalogueItem;
234 }
234 }
235 }
235 }
236 else {
236 else {
237 qCWarning(LOG_CatalogueSideBarWidget()) << "getCatalogueItem: Invalid tree "
237 qCWarning(LOG_CatalogueSideBarWidget()) << "getCatalogueItem: Invalid tree "
238 "structure. A database item should "
238 "structure. A database item should "
239 "only contain catalogues.";
239 "only contain catalogues.";
240 Q_ASSERT(false);
240 Q_ASSERT(false);
241 }
241 }
242 }
242 }
243 }
243 }
244 }
244 }
245
245
246 return nullptr;
246 return nullptr;
247 }
247 }
@@ -1,109 +1,111
1 #include "Visualization/VisualizationActionManager.h"
1 #include "Visualization/VisualizationActionManager.h"
2 #include "Visualization/VisualizationGraphWidget.h"
2 #include "Visualization/VisualizationGraphWidget.h"
3 #include "Visualization/VisualizationSelectionZoneItem.h"
3 #include "Visualization/VisualizationSelectionZoneItem.h"
4
4
5 #include <Actions/ActionsGuiController.h>
5 #include <Actions/ActionsGuiController.h>
6 #include <SqpApplication.h>
6 #include <SqpApplication.h>
7
7
8 VisualizationActionManager::VisualizationActionManager() {}
8 VisualizationActionManager::VisualizationActionManager()
9 {
10 }
9
11
10 void VisualizationActionManager::installSelectionZoneActions()
12 void VisualizationActionManager::installSelectionZoneActions()
11 {
13 {
12 auto &actionController = sqpApp->actionsGuiController();
14 auto &actionController = sqpApp->actionsGuiController();
13
15
14 auto removeZonesAction
16 auto removeZonesAction
15 = actionController.addSectionZoneAction("Remove Selected Zone(s)", [](auto zones) {
17 = actionController.addSectionZoneAction("Remove Selected Zone(s)", [](auto zones) {
16 for (auto selectionZone : zones) {
18 for (auto selectionZone : zones) {
17 if (auto graph = selectionZone->parentGraphWidget()) {
19 if (auto graph = selectionZone->parentGraphWidget()) {
18 graph->removeSelectionZone(selectionZone);
20 graph->removeSelectionZone(selectionZone);
19 }
21 }
20 }
22 }
21 });
23 });
22 removeZonesAction->setDisplayedShortcut(QKeySequence::Delete);
24 removeZonesAction->setDisplayedShortcut(QKeySequence::Delete);
23
25
24 auto alignEnableFuntion = [](auto items) { return items.count() > 1; };
26 auto alignEnableFuntion = [](auto items) { return items.count() > 1; };
25
27
26 // Vertical alignment actions
28 // Vertical alignment actions
27 auto alignLeftAction = actionController.addSectionZoneAction(
29 auto alignLeftAction = actionController.addSectionZoneAction(
28 QStringList{"Align Vertically"}, "Left", [](auto zones) {
30 QStringList{"Align Vertically"}, "Left", [](auto zones) {
29 Q_ASSERT(zones.count() > 1);
31 Q_ASSERT(zones.count() > 1);
30 auto ref = zones.takeFirst();
32 auto ref = zones.takeFirst();
31 ref->alignZonesVerticallyOnLeft(zones, false);
33 ref->alignZonesVerticallyOnLeft(zones, false);
32 });
34 });
33 alignLeftAction->setEnableFunction(alignEnableFuntion);
35 alignLeftAction->setEnableFunction(alignEnableFuntion);
34
36
35 auto alignLeftBorderAction = actionController.addSectionZoneAction(
37 auto alignLeftBorderAction = actionController.addSectionZoneAction(
36 QStringList{"Align Vertically"}, "Left Borders", [](auto zones) {
38 QStringList{"Align Vertically"}, "Left Borders", [](auto zones) {
37 Q_ASSERT(zones.count() > 1);
39 Q_ASSERT(zones.count() > 1);
38 auto ref = zones.takeFirst();
40 auto ref = zones.takeFirst();
39 ref->alignZonesVerticallyOnLeft(zones, true);
41 ref->alignZonesVerticallyOnLeft(zones, true);
40 });
42 });
41 alignLeftBorderAction->setEnableFunction(alignEnableFuntion);
43 alignLeftBorderAction->setEnableFunction(alignEnableFuntion);
42
44
43 auto alignRightAction = actionController.addSectionZoneAction(
45 auto alignRightAction = actionController.addSectionZoneAction(
44 QStringList{"Align Vertically"}, "Right", [](auto zones) {
46 QStringList{"Align Vertically"}, "Right", [](auto zones) {
45 Q_ASSERT(zones.count() > 1);
47 Q_ASSERT(zones.count() > 1);
46 auto ref = zones.takeFirst();
48 auto ref = zones.takeFirst();
47 ref->alignZonesVerticallyOnRight(zones, false);
49 ref->alignZonesVerticallyOnRight(zones, false);
48 });
50 });
49 alignRightAction->setEnableFunction(alignEnableFuntion);
51 alignRightAction->setEnableFunction(alignEnableFuntion);
50
52
51 auto alignRightBorderAction = actionController.addSectionZoneAction(
53 auto alignRightBorderAction = actionController.addSectionZoneAction(
52 QStringList{"Align Vertically"}, "Right Borders", [](auto zones) {
54 QStringList{"Align Vertically"}, "Right Borders", [](auto zones) {
53 Q_ASSERT(zones.count() > 1);
55 Q_ASSERT(zones.count() > 1);
54 auto ref = zones.takeFirst();
56 auto ref = zones.takeFirst();
55 ref->alignZonesVerticallyOnRight(zones, true);
57 ref->alignZonesVerticallyOnRight(zones, true);
56 });
58 });
57 alignRightBorderAction->setEnableFunction(alignEnableFuntion);
59 alignRightBorderAction->setEnableFunction(alignEnableFuntion);
58
60
59 auto alignLeftAndRightAction = actionController.addSectionZoneAction(
61 auto alignLeftAndRightAction = actionController.addSectionZoneAction(
60 QStringList{"Align Vertically"}, "Left and Right", [](auto zones) {
62 QStringList{"Align Vertically"}, "Left and Right", [](auto zones) {
61 Q_ASSERT(zones.count() > 1);
63 Q_ASSERT(zones.count() > 1);
62 auto ref = zones.takeFirst();
64 auto ref = zones.takeFirst();
63 ref->alignZonesVerticallyOnLeft(zones, false);
65 ref->alignZonesVerticallyOnLeft(zones, false);
64 ref->alignZonesVerticallyOnRight(zones, true);
66 ref->alignZonesVerticallyOnRight(zones, true);
65 });
67 });
66 alignLeftAndRightAction->setEnableFunction(alignEnableFuntion);
68 alignLeftAndRightAction->setEnableFunction(alignEnableFuntion);
67
69
68 // Temporal alignment actions
70 // Temporal alignment actions
69 auto alignLeftTemporallyAction = actionController.addSectionZoneAction(
71 auto alignLeftTemporallyAction = actionController.addSectionZoneAction(
70 QStringList{"Align Temporally"}, "Left", [](auto zones) {
72 QStringList{"Align Temporally"}, "Left", [](auto zones) {
71 Q_ASSERT(zones.count() > 1);
73 Q_ASSERT(zones.count() > 1);
72 auto ref = zones.takeFirst();
74 auto ref = zones.takeFirst();
73 ref->alignZonesTemporallyOnLeft(zones, false);
75 ref->alignZonesTemporallyOnLeft(zones, false);
74 });
76 });
75 alignLeftTemporallyAction->setEnableFunction(alignEnableFuntion);
77 alignLeftTemporallyAction->setEnableFunction(alignEnableFuntion);
76
78
77 auto alignLeftBorderTemporallyAction = actionController.addSectionZoneAction(
79 auto alignLeftBorderTemporallyAction = actionController.addSectionZoneAction(
78 QStringList{"Align Temporally"}, "Left Borders", [](auto zones) {
80 QStringList{"Align Temporally"}, "Left Borders", [](auto zones) {
79 Q_ASSERT(zones.count() > 1);
81 Q_ASSERT(zones.count() > 1);
80 auto ref = zones.takeFirst();
82 auto ref = zones.takeFirst();
81 ref->alignZonesTemporallyOnLeft(zones, true);
83 ref->alignZonesTemporallyOnLeft(zones, true);
82 });
84 });
83 alignLeftBorderTemporallyAction->setEnableFunction(alignEnableFuntion);
85 alignLeftBorderTemporallyAction->setEnableFunction(alignEnableFuntion);
84
86
85 auto alignRightTemporallyAction = actionController.addSectionZoneAction(
87 auto alignRightTemporallyAction = actionController.addSectionZoneAction(
86 QStringList{"Align Temporally"}, "Right", [](auto zones) {
88 QStringList{"Align Temporally"}, "Right", [](auto zones) {
87 Q_ASSERT(zones.count() > 1);
89 Q_ASSERT(zones.count() > 1);
88 auto ref = zones.takeFirst();
90 auto ref = zones.takeFirst();
89 ref->alignZonesTemporallyOnRight(zones, false);
91 ref->alignZonesTemporallyOnRight(zones, false);
90 });
92 });
91 alignRightTemporallyAction->setEnableFunction(alignEnableFuntion);
93 alignRightTemporallyAction->setEnableFunction(alignEnableFuntion);
92
94
93 auto alignRightBorderTemporallyAction = actionController.addSectionZoneAction(
95 auto alignRightBorderTemporallyAction = actionController.addSectionZoneAction(
94 QStringList{"Align Temporally"}, "Right Borders", [](auto zones) {
96 QStringList{"Align Temporally"}, "Right Borders", [](auto zones) {
95 Q_ASSERT(zones.count() > 1);
97 Q_ASSERT(zones.count() > 1);
96 auto ref = zones.takeFirst();
98 auto ref = zones.takeFirst();
97 ref->alignZonesTemporallyOnRight(zones, true);
99 ref->alignZonesTemporallyOnRight(zones, true);
98 });
100 });
99 alignRightBorderTemporallyAction->setEnableFunction(alignEnableFuntion);
101 alignRightBorderTemporallyAction->setEnableFunction(alignEnableFuntion);
100
102
101 auto alignLeftAndRightTemporallyAction = actionController.addSectionZoneAction(
103 auto alignLeftAndRightTemporallyAction = actionController.addSectionZoneAction(
102 QStringList{"Align Temporally"}, "Left and Right", [](auto zones) {
104 QStringList{"Align Temporally"}, "Left and Right", [](auto zones) {
103 Q_ASSERT(zones.count() > 1);
105 Q_ASSERT(zones.count() > 1);
104 auto ref = zones.takeFirst();
106 auto ref = zones.takeFirst();
105 ref->alignZonesTemporallyOnLeft(zones, false);
107 ref->alignZonesTemporallyOnLeft(zones, false);
106 ref->alignZonesTemporallyOnRight(zones, true);
108 ref->alignZonesTemporallyOnRight(zones, true);
107 });
109 });
108 alignLeftAndRightTemporallyAction->setEnableFunction(alignEnableFuntion);
110 alignLeftAndRightTemporallyAction->setEnableFunction(alignEnableFuntion);
109 }
111 }
@@ -1,1004 +1,1003
1 #include "Visualization/VisualizationGraphWidget.h"
1 #include "Visualization/VisualizationGraphWidget.h"
2 #include "Visualization/IVisualizationWidgetVisitor.h"
2 #include "Visualization/IVisualizationWidgetVisitor.h"
3 #include "Visualization/VisualizationCursorItem.h"
3 #include "Visualization/VisualizationCursorItem.h"
4 #include "Visualization/VisualizationDefs.h"
4 #include "Visualization/VisualizationDefs.h"
5 #include "Visualization/VisualizationGraphHelper.h"
5 #include "Visualization/VisualizationGraphHelper.h"
6 #include "Visualization/VisualizationGraphRenderingDelegate.h"
6 #include "Visualization/VisualizationGraphRenderingDelegate.h"
7 #include "Visualization/VisualizationMultiZoneSelectionDialog.h"
7 #include "Visualization/VisualizationMultiZoneSelectionDialog.h"
8 #include "Visualization/VisualizationSelectionZoneItem.h"
8 #include "Visualization/VisualizationSelectionZoneItem.h"
9 #include "Visualization/VisualizationSelectionZoneManager.h"
9 #include "Visualization/VisualizationSelectionZoneManager.h"
10 #include "Visualization/VisualizationWidget.h"
10 #include "Visualization/VisualizationWidget.h"
11 #include "Visualization/VisualizationZoneWidget.h"
11 #include "Visualization/VisualizationZoneWidget.h"
12 #include "ui_VisualizationGraphWidget.h"
12 #include "ui_VisualizationGraphWidget.h"
13
13
14 #include <Actions/ActionsGuiController.h>
14 #include <Actions/ActionsGuiController.h>
15 #include <Common/MimeTypesDef.h>
15 #include <Common/MimeTypesDef.h>
16 #include <Data/ArrayData.h>
16 #include <Data/ArrayData.h>
17 #include <Data/IDataSeries.h>
17 #include <Data/IDataSeries.h>
18 #include <Data/SpectrogramSeries.h>
18 #include <Data/SpectrogramSeries.h>
19 #include <DragAndDrop/DragDropGuiController.h>
19 #include <DragAndDrop/DragDropGuiController.h>
20 #include <Settings/SqpSettingsDefs.h>
20 #include <Settings/SqpSettingsDefs.h>
21 #include <SqpApplication.h>
21 #include <SqpApplication.h>
22 #include <Time/TimeController.h>
22 #include <Time/TimeController.h>
23 #include <Variable/Variable.h>
23 #include <Variable/Variable.h>
24 #include <Variable/VariableController.h>
24 #include <Variable/VariableController.h>
25
25
26 #include <unordered_map>
26 #include <unordered_map>
27
27
28 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
28 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
29
29
30 namespace {
30 namespace {
31
31
32 /// Key pressed to enable drag&drop in all modes
32 /// Key pressed to enable drag&drop in all modes
33 const auto DRAG_DROP_MODIFIER = Qt::AltModifier;
33 const auto DRAG_DROP_MODIFIER = Qt::AltModifier;
34
34
35 /// Key pressed to enable zoom on horizontal axis
35 /// Key pressed to enable zoom on horizontal axis
36 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier;
36 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier;
37
37
38 /// Key pressed to enable zoom on vertical axis
38 /// Key pressed to enable zoom on vertical axis
39 const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier;
39 const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier;
40
40
41 /// Speed of a step of a wheel event for a pan, in percentage of the axis range
41 /// Speed of a step of a wheel event for a pan, in percentage of the axis range
42 const auto PAN_SPEED = 5;
42 const auto PAN_SPEED = 5;
43
43
44 /// Key pressed to enable a calibration pan
44 /// Key pressed to enable a calibration pan
45 const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier;
45 const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier;
46
46
47 /// Key pressed to enable multi selection of selection zones
47 /// Key pressed to enable multi selection of selection zones
48 const auto MULTI_ZONE_SELECTION_MODIFIER = Qt::ControlModifier;
48 const auto MULTI_ZONE_SELECTION_MODIFIER = Qt::ControlModifier;
49
49
50 /// Minimum size for the zoom box, in percentage of the axis range
50 /// Minimum size for the zoom box, in percentage of the axis range
51 const auto ZOOM_BOX_MIN_SIZE = 0.8;
51 const auto ZOOM_BOX_MIN_SIZE = 0.8;
52
52
53 /// Format of the dates appearing in the label of a cursor
53 /// Format of the dates appearing in the label of a cursor
54 const auto CURSOR_LABELS_DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd\nhh:mm:ss:zzz");
54 const auto CURSOR_LABELS_DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd\nhh:mm:ss:zzz");
55
55
56 } // namespace
56 } // namespace
57
57
58 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
58 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
59
59
60 explicit VisualizationGraphWidgetPrivate(const QString &name)
60 explicit VisualizationGraphWidgetPrivate(const QString &name)
61 : m_Name{name},
61 : m_Name{name},
62 m_DoAcquisition{true},
62 m_DoAcquisition{true},
63 m_IsCalibration{false},
63 m_IsCalibration{false},
64 m_RenderingDelegate{nullptr}
64 m_RenderingDelegate{nullptr}
65 {
65 {
66 }
66 }
67
67
68 void updateData(PlottablesMap &plottables, std::shared_ptr<IDataSeries> dataSeries,
68 void updateData(PlottablesMap &plottables, std::shared_ptr<IDataSeries> dataSeries,
69 const SqpRange &range)
69 const SqpRange &range)
70 {
70 {
71 VisualizationGraphHelper::updateData(plottables, dataSeries, range);
71 VisualizationGraphHelper::updateData(plottables, dataSeries, range);
72
72
73 // Prevents that data has changed to update rendering
73 // Prevents that data has changed to update rendering
74 m_RenderingDelegate->onPlotUpdated();
74 m_RenderingDelegate->onPlotUpdated();
75 }
75 }
76
76
77 QString m_Name;
77 QString m_Name;
78 // 1 variable -> n qcpplot
78 // 1 variable -> n qcpplot
79 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
79 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
80 bool m_DoAcquisition;
80 bool m_DoAcquisition;
81 bool m_IsCalibration;
81 bool m_IsCalibration;
82 /// Delegate used to attach rendering features to the plot
82 /// Delegate used to attach rendering features to the plot
83 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
83 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
84
84
85 QCPItemRect *m_DrawingZoomRect = nullptr;
85 QCPItemRect *m_DrawingZoomRect = nullptr;
86 QStack<QPair<QCPRange, QCPRange> > m_ZoomStack;
86 QStack<QPair<QCPRange, QCPRange> > m_ZoomStack;
87
87
88 std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr;
88 std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr;
89 std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr;
89 std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr;
90
90
91 VisualizationSelectionZoneItem *m_DrawingZone = nullptr;
91 VisualizationSelectionZoneItem *m_DrawingZone = nullptr;
92 VisualizationSelectionZoneItem *m_HoveredZone = nullptr;
92 VisualizationSelectionZoneItem *m_HoveredZone = nullptr;
93 QVector<VisualizationSelectionZoneItem *> m_SelectionZones;
93 QVector<VisualizationSelectionZoneItem *> m_SelectionZones;
94
94
95 bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even
95 bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even
96
96
97 void startDrawingRect(const QPoint &pos, QCustomPlot &plot)
97 void startDrawingRect(const QPoint &pos, QCustomPlot &plot)
98 {
98 {
99 removeDrawingRect(plot);
99 removeDrawingRect(plot);
100
100
101 auto axisPos = posToAxisPos(pos, plot);
101 auto axisPos = posToAxisPos(pos, plot);
102
102
103 m_DrawingZoomRect = new QCPItemRect{&plot};
103 m_DrawingZoomRect = new QCPItemRect{&plot};
104 QPen p;
104 QPen p;
105 p.setWidth(2);
105 p.setWidth(2);
106 m_DrawingZoomRect->setPen(p);
106 m_DrawingZoomRect->setPen(p);
107
107
108 m_DrawingZoomRect->topLeft->setCoords(axisPos);
108 m_DrawingZoomRect->topLeft->setCoords(axisPos);
109 m_DrawingZoomRect->bottomRight->setCoords(axisPos);
109 m_DrawingZoomRect->bottomRight->setCoords(axisPos);
110 }
110 }
111
111
112 void removeDrawingRect(QCustomPlot &plot)
112 void removeDrawingRect(QCustomPlot &plot)
113 {
113 {
114 if (m_DrawingZoomRect) {
114 if (m_DrawingZoomRect) {
115 plot.removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot
115 plot.removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot
116 m_DrawingZoomRect = nullptr;
116 m_DrawingZoomRect = nullptr;
117 plot.replot(QCustomPlot::rpQueuedReplot);
117 plot.replot(QCustomPlot::rpQueuedReplot);
118 }
118 }
119 }
119 }
120
120
121 void startDrawingZone(const QPoint &pos, VisualizationGraphWidget *graph)
121 void startDrawingZone(const QPoint &pos, VisualizationGraphWidget *graph)
122 {
122 {
123 endDrawingZone(graph);
123 endDrawingZone(graph);
124
124
125 auto axisPos = posToAxisPos(pos, graph->plot());
125 auto axisPos = posToAxisPos(pos, graph->plot());
126
126
127 m_DrawingZone = new VisualizationSelectionZoneItem{&graph->plot()};
127 m_DrawingZone = new VisualizationSelectionZoneItem{&graph->plot()};
128 m_DrawingZone->setRange(axisPos.x(), axisPos.x());
128 m_DrawingZone->setRange(axisPos.x(), axisPos.x());
129 m_DrawingZone->setEditionEnabled(false);
129 m_DrawingZone->setEditionEnabled(false);
130 }
130 }
131
131
132 void endDrawingZone(VisualizationGraphWidget *graph)
132 void endDrawingZone(VisualizationGraphWidget *graph)
133 {
133 {
134 if (m_DrawingZone) {
134 if (m_DrawingZone) {
135 auto drawingZoneRange = m_DrawingZone->range();
135 auto drawingZoneRange = m_DrawingZone->range();
136 if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0) {
136 if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0) {
137 m_DrawingZone->setEditionEnabled(true);
137 m_DrawingZone->setEditionEnabled(true);
138 addSelectionZone(m_DrawingZone);
138 addSelectionZone(m_DrawingZone);
139 }
139 }
140 else {
140 else {
141 graph->plot().removeItem(m_DrawingZone); // the item is deleted by QCustomPlot
141 graph->plot().removeItem(m_DrawingZone); // the item is deleted by QCustomPlot
142 }
142 }
143
143
144 graph->plot().replot(QCustomPlot::rpQueuedReplot);
144 graph->plot().replot(QCustomPlot::rpQueuedReplot);
145 m_DrawingZone = nullptr;
145 m_DrawingZone = nullptr;
146 }
146 }
147 }
147 }
148
148
149 void setSelectionZonesEditionEnabled(bool value)
149 void setSelectionZonesEditionEnabled(bool value)
150 {
150 {
151 for (auto s : m_SelectionZones) {
151 for (auto s : m_SelectionZones) {
152 s->setEditionEnabled(value);
152 s->setEditionEnabled(value);
153 }
153 }
154 }
154 }
155
155
156 void addSelectionZone(VisualizationSelectionZoneItem *zone) { m_SelectionZones << zone; }
156 void addSelectionZone(VisualizationSelectionZoneItem *zone) { m_SelectionZones << zone; }
157
157
158 VisualizationSelectionZoneItem *selectionZoneAt(const QPoint &pos,
158 VisualizationSelectionZoneItem *selectionZoneAt(const QPoint &pos,
159 const QCustomPlot &plot) const
159 const QCustomPlot &plot) const
160 {
160 {
161 VisualizationSelectionZoneItem *selectionZoneItemUnderCursor = nullptr;
161 VisualizationSelectionZoneItem *selectionZoneItemUnderCursor = nullptr;
162 auto minDistanceToZone = -1;
162 auto minDistanceToZone = -1;
163 for (auto zone : m_SelectionZones) {
163 for (auto zone : m_SelectionZones) {
164 auto distanceToZone = zone->selectTest(pos, false);
164 auto distanceToZone = zone->selectTest(pos, false);
165 if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone)
165 if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone)
166 && distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
166 && distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
167 selectionZoneItemUnderCursor = zone;
167 selectionZoneItemUnderCursor = zone;
168 }
168 }
169 }
169 }
170
170
171 return selectionZoneItemUnderCursor;
171 return selectionZoneItemUnderCursor;
172 }
172 }
173
173
174 QVector<VisualizationSelectionZoneItem *> selectionZonesAt(const QPoint &pos,
174 QVector<VisualizationSelectionZoneItem *> selectionZonesAt(const QPoint &pos,
175 const QCustomPlot &plot) const
175 const QCustomPlot &plot) const
176 {
176 {
177 QVector<VisualizationSelectionZoneItem *> zones;
177 QVector<VisualizationSelectionZoneItem *> zones;
178 for (auto zone : m_SelectionZones) {
178 for (auto zone : m_SelectionZones) {
179 auto distanceToZone = zone->selectTest(pos, false);
179 auto distanceToZone = zone->selectTest(pos, false);
180 if (distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
180 if (distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
181 zones << zone;
181 zones << zone;
182 }
182 }
183 }
183 }
184
184
185 return zones;
185 return zones;
186 }
186 }
187
187
188 void moveSelectionZoneOnTop(VisualizationSelectionZoneItem *zone, QCustomPlot &plot)
188 void moveSelectionZoneOnTop(VisualizationSelectionZoneItem *zone, QCustomPlot &plot)
189 {
189 {
190 if (!m_SelectionZones.isEmpty() && m_SelectionZones.last() != zone) {
190 if (!m_SelectionZones.isEmpty() && m_SelectionZones.last() != zone) {
191 zone->moveToTop();
191 zone->moveToTop();
192 m_SelectionZones.removeAll(zone);
192 m_SelectionZones.removeAll(zone);
193 m_SelectionZones.append(zone);
193 m_SelectionZones.append(zone);
194 }
194 }
195 }
195 }
196
196
197 QPointF posToAxisPos(const QPoint &pos, QCustomPlot &plot) const
197 QPointF posToAxisPos(const QPoint &pos, QCustomPlot &plot) const
198 {
198 {
199 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
199 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
200 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
200 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
201 return QPointF{axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y())};
201 return QPointF{axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y())};
202 }
202 }
203
203
204 bool pointIsInAxisRect(const QPointF &axisPoint, QCustomPlot &plot) const
204 bool pointIsInAxisRect(const QPointF &axisPoint, QCustomPlot &plot) const
205 {
205 {
206 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
206 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
207 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
207 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
208 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
208 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
209 }
209 }
210 };
210 };
211
211
212 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
212 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
213 : VisualizationDragWidget{parent},
213 : VisualizationDragWidget{parent},
214 ui{new Ui::VisualizationGraphWidget},
214 ui{new Ui::VisualizationGraphWidget},
215 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
215 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
216 {
216 {
217 ui->setupUi(this);
217 ui->setupUi(this);
218
218
219 // 'Close' options : widget is deleted when closed
219 // 'Close' options : widget is deleted when closed
220 setAttribute(Qt::WA_DeleteOnClose);
220 setAttribute(Qt::WA_DeleteOnClose);
221
221
222 // Set qcpplot properties :
222 // Set qcpplot properties :
223 // - zoom is enabled
223 // - zoom is enabled
224 // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation
224 // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation
225 ui->widget->setInteractions(QCP::iRangeZoom);
225 ui->widget->setInteractions(QCP::iRangeZoom);
226 ui->widget->axisRect()->setRangeDrag(Qt::Horizontal | Qt::Vertical);
226 ui->widget->axisRect()->setRangeDrag(Qt::Horizontal | Qt::Vertical);
227
227
228 // The delegate must be initialized after the ui as it uses the plot
228 // The delegate must be initialized after the ui as it uses the plot
229 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
229 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
230
230
231 // Init the cursors
231 // Init the cursors
232 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
232 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
233 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
233 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
234 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
234 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
235 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
235 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
236
236
237 connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress);
237 connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress);
238 connect(ui->widget, &QCustomPlot::mouseRelease, this,
238 connect(ui->widget, &QCustomPlot::mouseRelease, this,
239 &VisualizationGraphWidget::onMouseRelease);
239 &VisualizationGraphWidget::onMouseRelease);
240 connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove);
240 connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove);
241 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
241 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
242 connect(ui->widget, &QCustomPlot::mouseDoubleClick, this,
242 connect(ui->widget, &QCustomPlot::mouseDoubleClick, this,
243 &VisualizationGraphWidget::onMouseDoubleClick);
243 &VisualizationGraphWidget::onMouseDoubleClick);
244 connect(
244 connect(ui->widget->xAxis, static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(
245 ui->widget->xAxis,
245 &QCPAxis::rangeChanged),
246 static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(&QCPAxis::rangeChanged),
246 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
247 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
248
247
249 // Activates menu when right clicking on the graph
248 // Activates menu when right clicking on the graph
250 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
249 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
251 connect(ui->widget, &QCustomPlot::customContextMenuRequested, this,
250 connect(ui->widget, &QCustomPlot::customContextMenuRequested, this,
252 &VisualizationGraphWidget::onGraphMenuRequested);
251 &VisualizationGraphWidget::onGraphMenuRequested);
253
252
254 connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
253 connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
255 &VariableController::onRequestDataLoading);
254 &VariableController::onRequestDataLoading);
256
255
257 connect(&sqpApp->variableController(), &VariableController::updateVarDisplaying, this,
256 connect(&sqpApp->variableController(), &VariableController::updateVarDisplaying, this,
258 &VisualizationGraphWidget::onUpdateVarDisplaying);
257 &VisualizationGraphWidget::onUpdateVarDisplaying);
259
258
260 #ifdef Q_OS_MAC
259 #ifdef Q_OS_MAC
261 plot().setPlottingHint(QCP::phFastPolylines, true);
260 plot().setPlottingHint(QCP::phFastPolylines, true);
262 #endif
261 #endif
263 }
262 }
264
263
265
264
266 VisualizationGraphWidget::~VisualizationGraphWidget()
265 VisualizationGraphWidget::~VisualizationGraphWidget()
267 {
266 {
268 delete ui;
267 delete ui;
269 }
268 }
270
269
271 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
270 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
272 {
271 {
273 auto parent = parentWidget();
272 auto parent = parentWidget();
274 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
273 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
275 parent = parent->parentWidget();
274 parent = parent->parentWidget();
276 }
275 }
277
276
278 return qobject_cast<VisualizationZoneWidget *>(parent);
277 return qobject_cast<VisualizationZoneWidget *>(parent);
279 }
278 }
280
279
281 VisualizationWidget *VisualizationGraphWidget::parentVisualizationWidget() const
280 VisualizationWidget *VisualizationGraphWidget::parentVisualizationWidget() const
282 {
281 {
283 auto parent = parentWidget();
282 auto parent = parentWidget();
284 while (parent != nullptr && !qobject_cast<VisualizationWidget *>(parent)) {
283 while (parent != nullptr && !qobject_cast<VisualizationWidget *>(parent)) {
285 parent = parent->parentWidget();
284 parent = parent->parentWidget();
286 }
285 }
287
286
288 return qobject_cast<VisualizationWidget *>(parent);
287 return qobject_cast<VisualizationWidget *>(parent);
289 }
288 }
290
289
291 void VisualizationGraphWidget::enableAcquisition(bool enable)
290 void VisualizationGraphWidget::enableAcquisition(bool enable)
292 {
291 {
293 impl->m_DoAcquisition = enable;
292 impl->m_DoAcquisition = enable;
294 }
293 }
295
294
296 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, SqpRange range)
295 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, SqpRange range)
297 {
296 {
298 // Uses delegate to create the qcpplot components according to the variable
297 // Uses delegate to create the qcpplot components according to the variable
299 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
298 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
300
299
301 if (auto dataSeries = variable->dataSeries()) {
300 if (auto dataSeries = variable->dataSeries()) {
302 // Set axes properties according to the units of the data series
301 // Set axes properties according to the units of the data series
303 impl->m_RenderingDelegate->setAxesProperties(dataSeries);
302 impl->m_RenderingDelegate->setAxesProperties(dataSeries);
304
303
305 // Sets rendering properties for the new plottables
304 // Sets rendering properties for the new plottables
306 // Warning: this method must be called after setAxesProperties(), as it can access to some
305 // Warning: this method must be called after setAxesProperties(), as it can access to some
307 // axes properties that have to be initialized
306 // axes properties that have to be initialized
308 impl->m_RenderingDelegate->setPlottablesProperties(dataSeries, createdPlottables);
307 impl->m_RenderingDelegate->setPlottablesProperties(dataSeries, createdPlottables);
309 }
308 }
310
309
311 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
310 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
312
311
313 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
312 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
314
313
315 this->enableAcquisition(false);
314 this->enableAcquisition(false);
316 this->setGraphRange(range);
315 this->setGraphRange(range);
317 this->enableAcquisition(true);
316 this->enableAcquisition(true);
318
317
319 emit requestDataLoading(QVector<std::shared_ptr<Variable> >() << variable, range, false);
318 emit requestDataLoading(QVector<std::shared_ptr<Variable> >() << variable, range, false);
320
319
321 emit variableAdded(variable);
320 emit variableAdded(variable);
322 }
321 }
323
322
324 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
323 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
325 {
324 {
326 // Each component associated to the variable :
325 // Each component associated to the variable :
327 // - is removed from qcpplot (which deletes it)
326 // - is removed from qcpplot (which deletes it)
328 // - is no longer referenced in the map
327 // - is no longer referenced in the map
329 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
328 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
330 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
329 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
331 emit variableAboutToBeRemoved(variable);
330 emit variableAboutToBeRemoved(variable);
332
331
333 auto &plottablesMap = variableIt->second;
332 auto &plottablesMap = variableIt->second;
334
333
335 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
334 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
336 plottableIt != plottableEnd;) {
335 plottableIt != plottableEnd;) {
337 ui->widget->removePlottable(plottableIt->second);
336 ui->widget->removePlottable(plottableIt->second);
338 plottableIt = plottablesMap.erase(plottableIt);
337 plottableIt = plottablesMap.erase(plottableIt);
339 }
338 }
340
339
341 impl->m_VariableToPlotMultiMap.erase(variableIt);
340 impl->m_VariableToPlotMultiMap.erase(variableIt);
342 }
341 }
343
342
344 // Updates graph
343 // Updates graph
345 ui->widget->replot();
344 ui->widget->replot();
346 }
345 }
347
346
348 QList<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
347 QList<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
349 {
348 {
350 auto variables = QList<std::shared_ptr<Variable> >{};
349 auto variables = QList<std::shared_ptr<Variable> >{};
351 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
350 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
352 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
351 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
353 variables << it->first;
352 variables << it->first;
354 }
353 }
355
354
356 return variables;
355 return variables;
357 }
356 }
358
357
359 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
358 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
360 {
359 {
361 if (!variable) {
360 if (!variable) {
362 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
361 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
363 return;
362 return;
364 }
363 }
365
364
366 VisualizationGraphHelper::setYAxisRange(variable, *ui->widget);
365 VisualizationGraphHelper::setYAxisRange(variable, *ui->widget);
367 }
366 }
368
367
369 SqpRange VisualizationGraphWidget::graphRange() const noexcept
368 SqpRange VisualizationGraphWidget::graphRange() const noexcept
370 {
369 {
371 auto graphRange = ui->widget->xAxis->range();
370 auto graphRange = ui->widget->xAxis->range();
372 return SqpRange{graphRange.lower, graphRange.upper};
371 return SqpRange{graphRange.lower, graphRange.upper};
373 }
372 }
374
373
375 void VisualizationGraphWidget::setGraphRange(const SqpRange &range)
374 void VisualizationGraphWidget::setGraphRange(const SqpRange &range)
376 {
375 {
377 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange START");
376 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange START");
378 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
377 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
379 ui->widget->replot();
378 ui->widget->replot();
380 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange END");
379 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange END");
381 }
380 }
382
381
383 QVector<SqpRange> VisualizationGraphWidget::selectionZoneRanges() const
382 QVector<SqpRange> VisualizationGraphWidget::selectionZoneRanges() const
384 {
383 {
385 QVector<SqpRange> ranges;
384 QVector<SqpRange> ranges;
386 for (auto zone : impl->m_SelectionZones) {
385 for (auto zone : impl->m_SelectionZones) {
387 ranges << zone->range();
386 ranges << zone->range();
388 }
387 }
389
388
390 return ranges;
389 return ranges;
391 }
390 }
392
391
393 void VisualizationGraphWidget::addSelectionZones(const QVector<SqpRange> &ranges)
392 void VisualizationGraphWidget::addSelectionZones(const QVector<SqpRange> &ranges)
394 {
393 {
395 for (const auto &range : ranges) {
394 for (const auto &range : ranges) {
396 // note: ownership is transfered to QCustomPlot
395 // note: ownership is transfered to QCustomPlot
397 auto zone = new VisualizationSelectionZoneItem(&plot());
396 auto zone = new VisualizationSelectionZoneItem(&plot());
398 zone->setRange(range.m_TStart, range.m_TEnd);
397 zone->setRange(range.m_TStart, range.m_TEnd);
399 impl->addSelectionZone(zone);
398 impl->addSelectionZone(zone);
400 }
399 }
401
400
402 plot().replot(QCustomPlot::rpQueuedReplot);
401 plot().replot(QCustomPlot::rpQueuedReplot);
403 }
402 }
404
403
405 void VisualizationGraphWidget::removeSelectionZone(VisualizationSelectionZoneItem *selectionZone)
404 void VisualizationGraphWidget::removeSelectionZone(VisualizationSelectionZoneItem *selectionZone)
406 {
405 {
407 parentVisualizationWidget()->selectionZoneManager().setSelected(selectionZone, false);
406 parentVisualizationWidget()->selectionZoneManager().setSelected(selectionZone, false);
408
407
409 if (impl->m_HoveredZone == selectionZone) {
408 if (impl->m_HoveredZone == selectionZone) {
410 impl->m_HoveredZone = nullptr;
409 impl->m_HoveredZone = nullptr;
411 setCursor(Qt::ArrowCursor);
410 setCursor(Qt::ArrowCursor);
412 }
411 }
413
412
414 impl->m_SelectionZones.removeAll(selectionZone);
413 impl->m_SelectionZones.removeAll(selectionZone);
415 plot().removeItem(selectionZone);
414 plot().removeItem(selectionZone);
416 plot().replot(QCustomPlot::rpQueuedReplot);
415 plot().replot(QCustomPlot::rpQueuedReplot);
417 }
416 }
418
417
419 void VisualizationGraphWidget::undoZoom()
418 void VisualizationGraphWidget::undoZoom()
420 {
419 {
421 auto zoom = impl->m_ZoomStack.pop();
420 auto zoom = impl->m_ZoomStack.pop();
422 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
421 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
423 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
422 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
424
423
425 axisX->setRange(zoom.first);
424 axisX->setRange(zoom.first);
426 axisY->setRange(zoom.second);
425 axisY->setRange(zoom.second);
427
426
428 plot().replot(QCustomPlot::rpQueuedReplot);
427 plot().replot(QCustomPlot::rpQueuedReplot);
429 }
428 }
430
429
431 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
430 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
432 {
431 {
433 if (visitor) {
432 if (visitor) {
434 visitor->visit(this);
433 visitor->visit(this);
435 }
434 }
436 else {
435 else {
437 qCCritical(LOG_VisualizationGraphWidget())
436 qCCritical(LOG_VisualizationGraphWidget())
438 << tr("Can't visit widget : the visitor is null");
437 << tr("Can't visit widget : the visitor is null");
439 }
438 }
440 }
439 }
441
440
442 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
441 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
443 {
442 {
444 auto isSpectrogram = [](const auto &variable) {
443 auto isSpectrogram = [](const auto &variable) {
445 return std::dynamic_pointer_cast<SpectrogramSeries>(variable.dataSeries()) != nullptr;
444 return std::dynamic_pointer_cast<SpectrogramSeries>(variable.dataSeries()) != nullptr;
446 };
445 };
447
446
448 // - A spectrogram series can't be dropped on graph with existing plottables
447 // - A spectrogram series can't be dropped on graph with existing plottables
449 // - No data series can be dropped on graph with existing spectrogram series
448 // - No data series can be dropped on graph with existing spectrogram series
450 return isSpectrogram(variable)
449 return isSpectrogram(variable)
451 ? impl->m_VariableToPlotMultiMap.empty()
450 ? impl->m_VariableToPlotMultiMap.empty()
452 : std::none_of(
451 : std::none_of(
453 impl->m_VariableToPlotMultiMap.cbegin(), impl->m_VariableToPlotMultiMap.cend(),
452 impl->m_VariableToPlotMultiMap.cbegin(), impl->m_VariableToPlotMultiMap.cend(),
454 [isSpectrogram](const auto &entry) { return isSpectrogram(*entry.first); });
453 [isSpectrogram](const auto &entry) { return isSpectrogram(*entry.first); });
455 }
454 }
456
455
457 bool VisualizationGraphWidget::contains(const Variable &variable) const
456 bool VisualizationGraphWidget::contains(const Variable &variable) const
458 {
457 {
459 // Finds the variable among the keys of the map
458 // Finds the variable among the keys of the map
460 auto variablePtr = &variable;
459 auto variablePtr = &variable;
461 auto findVariable
460 auto findVariable
462 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
461 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
463
462
464 auto end = impl->m_VariableToPlotMultiMap.cend();
463 auto end = impl->m_VariableToPlotMultiMap.cend();
465 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
464 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
466 return it != end;
465 return it != end;
467 }
466 }
468
467
469 QString VisualizationGraphWidget::name() const
468 QString VisualizationGraphWidget::name() const
470 {
469 {
471 return impl->m_Name;
470 return impl->m_Name;
472 }
471 }
473
472
474 QMimeData *VisualizationGraphWidget::mimeData(const QPoint &position) const
473 QMimeData *VisualizationGraphWidget::mimeData(const QPoint &position) const
475 {
474 {
476 auto mimeData = new QMimeData;
475 auto mimeData = new QMimeData;
477
476
478 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position, plot());
477 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position, plot());
479 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
478 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
480 && selectionZoneItemUnderCursor) {
479 && selectionZoneItemUnderCursor) {
481 mimeData->setData(MIME_TYPE_TIME_RANGE, TimeController::mimeDataForTimeRange(
480 mimeData->setData(MIME_TYPE_TIME_RANGE, TimeController::mimeDataForTimeRange(
482 selectionZoneItemUnderCursor->range()));
481 selectionZoneItemUnderCursor->range()));
483 mimeData->setData(MIME_TYPE_SELECTION_ZONE, TimeController::mimeDataForTimeRange(
482 mimeData->setData(MIME_TYPE_SELECTION_ZONE, TimeController::mimeDataForTimeRange(
484 selectionZoneItemUnderCursor->range()));
483 selectionZoneItemUnderCursor->range()));
485 }
484 }
486 else {
485 else {
487 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
486 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
488
487
489 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
488 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
490 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
489 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
491 }
490 }
492
491
493 return mimeData;
492 return mimeData;
494 }
493 }
495
494
496 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint &dragPosition)
495 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint &dragPosition)
497 {
496 {
498 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition, plot());
497 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition, plot());
499 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
498 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
500 && selectionZoneItemUnderCursor) {
499 && selectionZoneItemUnderCursor) {
501
500
502 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
501 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
503 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
502 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
504
503
505 auto zoneSize = QSizeF{qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
504 auto zoneSize = QSizeF{qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
506 qAbs(zoneBottomRight.y() - zoneTopLeft.y())}
505 qAbs(zoneBottomRight.y() - zoneTopLeft.y())}
507 .toSize();
506 .toSize();
508
507
509 auto pixmap = QPixmap(zoneSize);
508 auto pixmap = QPixmap(zoneSize);
510 render(&pixmap, QPoint(), QRegion{QRect{zoneTopLeft.toPoint(), zoneSize}});
509 render(&pixmap, QPoint(), QRegion{QRect{zoneTopLeft.toPoint(), zoneSize}});
511
510
512 return pixmap;
511 return pixmap;
513 }
512 }
514
513
515 return QPixmap();
514 return QPixmap();
516 }
515 }
517
516
518 bool VisualizationGraphWidget::isDragAllowed() const
517 bool VisualizationGraphWidget::isDragAllowed() const
519 {
518 {
520 return true;
519 return true;
521 }
520 }
522
521
523 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
522 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
524 {
523 {
525 if (highlighted) {
524 if (highlighted) {
526 plot().setBackground(QBrush(QColor("#BBD5EE")));
525 plot().setBackground(QBrush(QColor("#BBD5EE")));
527 }
526 }
528 else {
527 else {
529 plot().setBackground(QBrush(Qt::white));
528 plot().setBackground(QBrush(Qt::white));
530 }
529 }
531
530
532 plot().update();
531 plot().update();
533 }
532 }
534
533
535 void VisualizationGraphWidget::addVerticalCursor(double time)
534 void VisualizationGraphWidget::addVerticalCursor(double time)
536 {
535 {
537 impl->m_VerticalCursor->setPosition(time);
536 impl->m_VerticalCursor->setPosition(time);
538 impl->m_VerticalCursor->setVisible(true);
537 impl->m_VerticalCursor->setVisible(true);
539
538
540 auto text
539 auto text
541 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
540 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
542 impl->m_VerticalCursor->setLabelText(text);
541 impl->m_VerticalCursor->setLabelText(text);
543 }
542 }
544
543
545 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
544 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
546 {
545 {
547 impl->m_VerticalCursor->setAbsolutePosition(position);
546 impl->m_VerticalCursor->setAbsolutePosition(position);
548 impl->m_VerticalCursor->setVisible(true);
547 impl->m_VerticalCursor->setVisible(true);
549
548
550 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
549 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
551 auto text
550 auto text
552 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
551 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
553 impl->m_VerticalCursor->setLabelText(text);
552 impl->m_VerticalCursor->setLabelText(text);
554 }
553 }
555
554
556 void VisualizationGraphWidget::removeVerticalCursor()
555 void VisualizationGraphWidget::removeVerticalCursor()
557 {
556 {
558 impl->m_VerticalCursor->setVisible(false);
557 impl->m_VerticalCursor->setVisible(false);
559 plot().replot(QCustomPlot::rpQueuedReplot);
558 plot().replot(QCustomPlot::rpQueuedReplot);
560 }
559 }
561
560
562 void VisualizationGraphWidget::addHorizontalCursor(double value)
561 void VisualizationGraphWidget::addHorizontalCursor(double value)
563 {
562 {
564 impl->m_HorizontalCursor->setPosition(value);
563 impl->m_HorizontalCursor->setPosition(value);
565 impl->m_HorizontalCursor->setVisible(true);
564 impl->m_HorizontalCursor->setVisible(true);
566 impl->m_HorizontalCursor->setLabelText(QString::number(value));
565 impl->m_HorizontalCursor->setLabelText(QString::number(value));
567 }
566 }
568
567
569 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
568 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
570 {
569 {
571 impl->m_HorizontalCursor->setAbsolutePosition(position);
570 impl->m_HorizontalCursor->setAbsolutePosition(position);
572 impl->m_HorizontalCursor->setVisible(true);
571 impl->m_HorizontalCursor->setVisible(true);
573
572
574 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
573 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
575 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
574 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
576 }
575 }
577
576
578 void VisualizationGraphWidget::removeHorizontalCursor()
577 void VisualizationGraphWidget::removeHorizontalCursor()
579 {
578 {
580 impl->m_HorizontalCursor->setVisible(false);
579 impl->m_HorizontalCursor->setVisible(false);
581 plot().replot(QCustomPlot::rpQueuedReplot);
580 plot().replot(QCustomPlot::rpQueuedReplot);
582 }
581 }
583
582
584 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
583 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
585 {
584 {
586 Q_UNUSED(event);
585 Q_UNUSED(event);
587
586
588 // Prevents that all variables will be removed from graph when it will be closed
587 // Prevents that all variables will be removed from graph when it will be closed
589 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
588 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
590 emit variableAboutToBeRemoved(variableEntry.first);
589 emit variableAboutToBeRemoved(variableEntry.first);
591 }
590 }
592 }
591 }
593
592
594 void VisualizationGraphWidget::enterEvent(QEvent *event)
593 void VisualizationGraphWidget::enterEvent(QEvent *event)
595 {
594 {
596 Q_UNUSED(event);
595 Q_UNUSED(event);
597 impl->m_RenderingDelegate->showGraphOverlay(true);
596 impl->m_RenderingDelegate->showGraphOverlay(true);
598 }
597 }
599
598
600 void VisualizationGraphWidget::leaveEvent(QEvent *event)
599 void VisualizationGraphWidget::leaveEvent(QEvent *event)
601 {
600 {
602 Q_UNUSED(event);
601 Q_UNUSED(event);
603 impl->m_RenderingDelegate->showGraphOverlay(false);
602 impl->m_RenderingDelegate->showGraphOverlay(false);
604
603
605 if (auto parentZone = parentZoneWidget()) {
604 if (auto parentZone = parentZoneWidget()) {
606 parentZone->notifyMouseLeaveGraph(this);
605 parentZone->notifyMouseLeaveGraph(this);
607 }
606 }
608 else {
607 else {
609 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
608 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
610 }
609 }
611
610
612 if (impl->m_HoveredZone) {
611 if (impl->m_HoveredZone) {
613 impl->m_HoveredZone->setHovered(false);
612 impl->m_HoveredZone->setHovered(false);
614 impl->m_HoveredZone = nullptr;
613 impl->m_HoveredZone = nullptr;
615 }
614 }
616 }
615 }
617
616
618 QCustomPlot &VisualizationGraphWidget::plot() const noexcept
617 QCustomPlot &VisualizationGraphWidget::plot() const noexcept
619 {
618 {
620 return *ui->widget;
619 return *ui->widget;
621 }
620 }
622
621
623 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
622 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
624 {
623 {
625 QMenu graphMenu{};
624 QMenu graphMenu{};
626
625
627 // Iterates on variables (unique keys)
626 // Iterates on variables (unique keys)
628 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
627 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
629 end = impl->m_VariableToPlotMultiMap.cend();
628 end = impl->m_VariableToPlotMultiMap.cend();
630 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
629 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
631 // 'Remove variable' action
630 // 'Remove variable' action
632 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
631 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
633 [ this, var = it->first ]() { removeVariable(var); });
632 [ this, var = it->first ]() { removeVariable(var); });
634 }
633 }
635
634
636 if (!impl->m_ZoomStack.isEmpty()) {
635 if (!impl->m_ZoomStack.isEmpty()) {
637 if (!graphMenu.isEmpty()) {
636 if (!graphMenu.isEmpty()) {
638 graphMenu.addSeparator();
637 graphMenu.addSeparator();
639 }
638 }
640
639
641 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
640 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
642 }
641 }
643
642
644 // Selection Zone Actions
643 // Selection Zone Actions
645 auto selectionZoneItem = impl->selectionZoneAt(pos, plot());
644 auto selectionZoneItem = impl->selectionZoneAt(pos, plot());
646 if (selectionZoneItem) {
645 if (selectionZoneItem) {
647 auto selectedItems = parentVisualizationWidget()->selectionZoneManager().selectedItems();
646 auto selectedItems = parentVisualizationWidget()->selectionZoneManager().selectedItems();
648 selectedItems.removeAll(selectionZoneItem);
647 selectedItems.removeAll(selectionZoneItem);
649 selectedItems.prepend(selectionZoneItem); // Put the current selection zone first
648 selectedItems.prepend(selectionZoneItem); // Put the current selection zone first
650
649
651 auto zoneActions = sqpApp->actionsGuiController().selectionZoneActions();
650 auto zoneActions = sqpApp->actionsGuiController().selectionZoneActions();
652 if (!zoneActions.isEmpty() && !graphMenu.isEmpty()) {
651 if (!zoneActions.isEmpty() && !graphMenu.isEmpty()) {
653 graphMenu.addSeparator();
652 graphMenu.addSeparator();
654 }
653 }
655
654
656 QHash<QString, QMenu *> subMenus;
655 QHash<QString, QMenu *> subMenus;
657 QHash<QString, bool> subMenusEnabled;
656 QHash<QString, bool> subMenusEnabled;
658
657
659 for (auto zoneAction : zoneActions) {
658 for (auto zoneAction : zoneActions) {
660
659
661 auto isEnabled = zoneAction->isEnabled(selectedItems);
660 auto isEnabled = zoneAction->isEnabled(selectedItems);
662
661
663 auto menu = &graphMenu;
662 auto menu = &graphMenu;
664 for (auto subMenuName : zoneAction->subMenuList()) {
663 for (auto subMenuName : zoneAction->subMenuList()) {
665 if (!subMenus.contains(subMenuName)) {
664 if (!subMenus.contains(subMenuName)) {
666 menu = menu->addMenu(subMenuName);
665 menu = menu->addMenu(subMenuName);
667 subMenus[subMenuName] = menu;
666 subMenus[subMenuName] = menu;
668 subMenusEnabled[subMenuName] = isEnabled;
667 subMenusEnabled[subMenuName] = isEnabled;
669 }
668 }
670 else {
669 else {
671 menu = subMenus.value(subMenuName);
670 menu = subMenus.value(subMenuName);
672 if (isEnabled) {
671 if (isEnabled) {
673 // The sub menu is enabled if at least one of its actions is enabled
672 // The sub menu is enabled if at least one of its actions is enabled
674 subMenusEnabled[subMenuName] = true;
673 subMenusEnabled[subMenuName] = true;
675 }
674 }
676 }
675 }
677 }
676 }
678
677
679 auto action = menu->addAction(zoneAction->name());
678 auto action = menu->addAction(zoneAction->name());
680 action->setEnabled(isEnabled);
679 action->setEnabled(isEnabled);
681 action->setShortcut(zoneAction->displayedShortcut());
680 action->setShortcut(zoneAction->displayedShortcut());
682 QObject::connect(action, &QAction::triggered,
681 QObject::connect(action, &QAction::triggered,
683 [zoneAction, selectedItems]() { zoneAction->execute(selectedItems); });
682 [zoneAction, selectedItems]() { zoneAction->execute(selectedItems); });
684 }
683 }
685
684
686 for (auto it = subMenus.cbegin(); it != subMenus.cend(); ++it) {
685 for (auto it = subMenus.cbegin(); it != subMenus.cend(); ++it) {
687 it.value()->setEnabled(subMenusEnabled[it.key()]);
686 it.value()->setEnabled(subMenusEnabled[it.key()]);
688 }
687 }
689 }
688 }
690
689
691 if (!graphMenu.isEmpty()) {
690 if (!graphMenu.isEmpty()) {
692 graphMenu.exec(QCursor::pos());
691 graphMenu.exec(QCursor::pos());
693 }
692 }
694 }
693 }
695
694
696 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
695 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
697 {
696 {
698 qCDebug(LOG_VisualizationGraphWidget())
697 qCDebug(LOG_VisualizationGraphWidget()) << tr("TORM: VisualizationGraphWidget::onRangeChanged")
699 << tr("TORM: VisualizationGraphWidget::onRangeChanged")
698 << QThread::currentThread()->objectName() << "DoAcqui"
700 << QThread::currentThread()->objectName() << "DoAcqui" << impl->m_DoAcquisition;
699 << impl->m_DoAcquisition;
701
700
702 auto graphRange = SqpRange{t1.lower, t1.upper};
701 auto graphRange = SqpRange{t1.lower, t1.upper};
703 auto oldGraphRange = SqpRange{t2.lower, t2.upper};
702 auto oldGraphRange = SqpRange{t2.lower, t2.upper};
704
703
705 if (impl->m_DoAcquisition) {
704 if (impl->m_DoAcquisition) {
706 QVector<std::shared_ptr<Variable> > variableUnderGraphVector;
705 QVector<std::shared_ptr<Variable> > variableUnderGraphVector;
707
706
708 for (auto it = impl->m_VariableToPlotMultiMap.begin(),
707 for (auto it = impl->m_VariableToPlotMultiMap.begin(),
709 end = impl->m_VariableToPlotMultiMap.end();
708 end = impl->m_VariableToPlotMultiMap.end();
710 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
709 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
711 variableUnderGraphVector.push_back(it->first);
710 variableUnderGraphVector.push_back(it->first);
712 }
711 }
713 emit requestDataLoading(std::move(variableUnderGraphVector), graphRange,
712 emit requestDataLoading(std::move(variableUnderGraphVector), graphRange,
714 !impl->m_IsCalibration);
713 !impl->m_IsCalibration);
715
714
716 if (!impl->m_IsCalibration) {
715 if (!impl->m_IsCalibration) {
717 qCDebug(LOG_VisualizationGraphWidget())
716 qCDebug(LOG_VisualizationGraphWidget())
718 << tr("TORM: VisualizationGraphWidget::Synchronize notify !!")
717 << tr("TORM: VisualizationGraphWidget::Synchronize notify !!")
719 << QThread::currentThread()->objectName() << graphRange << oldGraphRange;
718 << QThread::currentThread()->objectName() << graphRange << oldGraphRange;
720 emit synchronize(graphRange, oldGraphRange);
719 emit synchronize(graphRange, oldGraphRange);
721 }
720 }
722 }
721 }
723
722
724 auto pos = mapFromGlobal(QCursor::pos());
723 auto pos = mapFromGlobal(QCursor::pos());
725 auto axisPos = impl->posToAxisPos(pos, plot());
724 auto axisPos = impl->posToAxisPos(pos, plot());
726 if (auto parentZone = parentZoneWidget()) {
725 if (auto parentZone = parentZoneWidget()) {
727 if (impl->pointIsInAxisRect(axisPos, plot())) {
726 if (impl->pointIsInAxisRect(axisPos, plot())) {
728 parentZone->notifyMouseMoveInGraph(pos, axisPos, this);
727 parentZone->notifyMouseMoveInGraph(pos, axisPos, this);
729 }
728 }
730 else {
729 else {
731 parentZone->notifyMouseLeaveGraph(this);
730 parentZone->notifyMouseLeaveGraph(this);
732 }
731 }
733 }
732 }
734 else {
733 else {
735 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
734 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
736 }
735 }
737 }
736 }
738
737
739 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept
738 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept
740 {
739 {
741 impl->m_RenderingDelegate->onMouseDoubleClick(event);
740 impl->m_RenderingDelegate->onMouseDoubleClick(event);
742 }
741 }
743
742
744 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
743 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
745 {
744 {
746 // Handles plot rendering when mouse is moving
745 // Handles plot rendering when mouse is moving
747 impl->m_RenderingDelegate->onMouseMove(event);
746 impl->m_RenderingDelegate->onMouseMove(event);
748
747
749 auto axisPos = impl->posToAxisPos(event->pos(), plot());
748 auto axisPos = impl->posToAxisPos(event->pos(), plot());
750
749
751 // Zoom box and zone drawing
750 // Zoom box and zone drawing
752 if (impl->m_DrawingZoomRect) {
751 if (impl->m_DrawingZoomRect) {
753 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
752 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
754 }
753 }
755 else if (impl->m_DrawingZone) {
754 else if (impl->m_DrawingZone) {
756 impl->m_DrawingZone->setEnd(axisPos.x());
755 impl->m_DrawingZone->setEnd(axisPos.x());
757 }
756 }
758
757
759 // Cursor
758 // Cursor
760 if (auto parentZone = parentZoneWidget()) {
759 if (auto parentZone = parentZoneWidget()) {
761 if (impl->pointIsInAxisRect(axisPos, plot())) {
760 if (impl->pointIsInAxisRect(axisPos, plot())) {
762 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
761 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
763 }
762 }
764 else {
763 else {
765 parentZone->notifyMouseLeaveGraph(this);
764 parentZone->notifyMouseLeaveGraph(this);
766 }
765 }
767 }
766 }
768 else {
767 else {
769 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
768 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
770 }
769 }
771
770
772 // Search for the selection zone under the mouse
771 // Search for the selection zone under the mouse
773 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
772 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
774 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
773 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
775 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) {
774 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) {
776
775
777 // Sets the appropriate cursor shape
776 // Sets the appropriate cursor shape
778 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
777 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
779 setCursor(cursorShape);
778 setCursor(cursorShape);
780
779
781 // Manages the hovered zone
780 // Manages the hovered zone
782 if (selectionZoneItemUnderCursor != impl->m_HoveredZone) {
781 if (selectionZoneItemUnderCursor != impl->m_HoveredZone) {
783 if (impl->m_HoveredZone) {
782 if (impl->m_HoveredZone) {
784 impl->m_HoveredZone->setHovered(false);
783 impl->m_HoveredZone->setHovered(false);
785 }
784 }
786 selectionZoneItemUnderCursor->setHovered(true);
785 selectionZoneItemUnderCursor->setHovered(true);
787 impl->m_HoveredZone = selectionZoneItemUnderCursor;
786 impl->m_HoveredZone = selectionZoneItemUnderCursor;
788 plot().replot(QCustomPlot::rpQueuedReplot);
787 plot().replot(QCustomPlot::rpQueuedReplot);
789 }
788 }
790 }
789 }
791 else {
790 else {
792 // There is no zone under the mouse or the interaction mode is not "selection zones"
791 // There is no zone under the mouse or the interaction mode is not "selection zones"
793 if (impl->m_HoveredZone) {
792 if (impl->m_HoveredZone) {
794 impl->m_HoveredZone->setHovered(false);
793 impl->m_HoveredZone->setHovered(false);
795 impl->m_HoveredZone = nullptr;
794 impl->m_HoveredZone = nullptr;
796 }
795 }
797
796
798 setCursor(Qt::ArrowCursor);
797 setCursor(Qt::ArrowCursor);
799 }
798 }
800
799
801 impl->m_HasMovedMouse = true;
800 impl->m_HasMovedMouse = true;
802 VisualizationDragWidget::mouseMoveEvent(event);
801 VisualizationDragWidget::mouseMoveEvent(event);
803 }
802 }
804
803
805 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
804 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
806 {
805 {
807 auto value = event->angleDelta().x() + event->angleDelta().y();
806 auto value = event->angleDelta().x() + event->angleDelta().y();
808 if (value != 0) {
807 if (value != 0) {
809
808
810 auto direction = value > 0 ? 1.0 : -1.0;
809 auto direction = value > 0 ? 1.0 : -1.0;
811 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
810 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
812 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
811 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
813 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
812 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
814
813
815 auto zoomOrientations = QFlags<Qt::Orientation>{};
814 auto zoomOrientations = QFlags<Qt::Orientation>{};
816 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
815 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
817 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
816 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
818
817
819 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
818 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
820
819
821 if (!isZoomX && !isZoomY) {
820 if (!isZoomX && !isZoomY) {
822 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
821 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
823 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
822 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
824
823
825 axis->setRange(axis->range() + diff);
824 axis->setRange(axis->range() + diff);
826
825
827 if (plot().noAntialiasingOnDrag()) {
826 if (plot().noAntialiasingOnDrag()) {
828 plot().setNotAntialiasedElements(QCP::aeAll);
827 plot().setNotAntialiasedElements(QCP::aeAll);
829 }
828 }
830
829
831 plot().replot(QCustomPlot::rpQueuedReplot);
830 plot().replot(QCustomPlot::rpQueuedReplot);
832 }
831 }
833 }
832 }
834 }
833 }
835
834
836 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
835 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
837 {
836 {
838 auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
837 auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
839 auto isSelectionZoneMode
838 auto isSelectionZoneMode
840 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
839 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
841 auto isLeftClick = event->buttons().testFlag(Qt::LeftButton);
840 auto isLeftClick = event->buttons().testFlag(Qt::LeftButton);
842
841
843 if (!isDragDropClick && isLeftClick) {
842 if (!isDragDropClick && isLeftClick) {
844 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) {
843 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) {
845 // Starts a zoom box
844 // Starts a zoom box
846 impl->startDrawingRect(event->pos(), plot());
845 impl->startDrawingRect(event->pos(), plot());
847 }
846 }
848 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr) {
847 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr) {
849 // Starts a new selection zone
848 // Starts a new selection zone
850 auto zoneAtPos = impl->selectionZoneAt(event->pos(), plot());
849 auto zoneAtPos = impl->selectionZoneAt(event->pos(), plot());
851 if (!zoneAtPos) {
850 if (!zoneAtPos) {
852 impl->startDrawingZone(event->pos(), this);
851 impl->startDrawingZone(event->pos(), this);
853 }
852 }
854 }
853 }
855 }
854 }
856
855
857 // Allows mouse panning only in default mode
856 // Allows mouse panning only in default mode
858 plot().setInteraction(QCP::iRangeDrag, sqpApp->plotsInteractionMode()
857 plot().setInteraction(QCP::iRangeDrag, sqpApp->plotsInteractionMode()
859 == SqpApplication::PlotsInteractionMode::None
858 == SqpApplication::PlotsInteractionMode::None
860 && !isDragDropClick);
859 && !isDragDropClick);
861
860
862 // Allows zone edition only in selection zone mode without drag&drop
861 // Allows zone edition only in selection zone mode without drag&drop
863 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
862 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
864
863
865 // Selection / Deselection
864 // Selection / Deselection
866 if (isSelectionZoneMode) {
865 if (isSelectionZoneMode) {
867 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
866 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
868 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
867 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
869
868
870
869
871 if (selectionZoneItemUnderCursor && !selectionZoneItemUnderCursor->selected()
870 if (selectionZoneItemUnderCursor && !selectionZoneItemUnderCursor->selected()
872 && !isMultiSelectionClick) {
871 && !isMultiSelectionClick) {
873 parentVisualizationWidget()->selectionZoneManager().select(
872 parentVisualizationWidget()->selectionZoneManager().select(
874 {selectionZoneItemUnderCursor});
873 {selectionZoneItemUnderCursor});
875 }
874 }
876 else if (!selectionZoneItemUnderCursor && !isMultiSelectionClick && isLeftClick) {
875 else if (!selectionZoneItemUnderCursor && !isMultiSelectionClick && isLeftClick) {
877 parentVisualizationWidget()->selectionZoneManager().clearSelection();
876 parentVisualizationWidget()->selectionZoneManager().clearSelection();
878 }
877 }
879 else {
878 else {
880 // No selection change
879 // No selection change
881 }
880 }
882
881
883 if (selectionZoneItemUnderCursor && isLeftClick) {
882 if (selectionZoneItemUnderCursor && isLeftClick) {
884 selectionZoneItemUnderCursor->setAssociatedEditedZones(
883 selectionZoneItemUnderCursor->setAssociatedEditedZones(
885 parentVisualizationWidget()->selectionZoneManager().selectedItems());
884 parentVisualizationWidget()->selectionZoneManager().selectedItems());
886 }
885 }
887 }
886 }
888
887
889
888
890 impl->m_HasMovedMouse = false;
889 impl->m_HasMovedMouse = false;
891 VisualizationDragWidget::mousePressEvent(event);
890 VisualizationDragWidget::mousePressEvent(event);
892 }
891 }
893
892
894 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
893 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
895 {
894 {
896 if (impl->m_DrawingZoomRect) {
895 if (impl->m_DrawingZoomRect) {
897
896
898 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
897 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
899 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
898 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
900
899
901 auto newAxisXRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().x(),
900 auto newAxisXRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().x(),
902 impl->m_DrawingZoomRect->bottomRight->coords().x()};
901 impl->m_DrawingZoomRect->bottomRight->coords().x()};
903
902
904 auto newAxisYRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().y(),
903 auto newAxisYRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().y(),
905 impl->m_DrawingZoomRect->bottomRight->coords().y()};
904 impl->m_DrawingZoomRect->bottomRight->coords().y()};
906
905
907 impl->removeDrawingRect(plot());
906 impl->removeDrawingRect(plot());
908
907
909 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
908 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
910 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
909 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
911 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
910 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
912 axisX->setRange(newAxisXRange);
911 axisX->setRange(newAxisXRange);
913 axisY->setRange(newAxisYRange);
912 axisY->setRange(newAxisYRange);
914
913
915 plot().replot(QCustomPlot::rpQueuedReplot);
914 plot().replot(QCustomPlot::rpQueuedReplot);
916 }
915 }
917 }
916 }
918
917
919 impl->endDrawingZone(this);
918 impl->endDrawingZone(this);
920
919
921 impl->m_IsCalibration = false;
920 impl->m_IsCalibration = false;
922
921
923 // Selection / Deselection
922 // Selection / Deselection
924 auto isSelectionZoneMode
923 auto isSelectionZoneMode
925 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
924 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
926 if (isSelectionZoneMode) {
925 if (isSelectionZoneMode) {
927 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
926 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
928 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
927 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
929 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton
928 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton
930 && !impl->m_HasMovedMouse) {
929 && !impl->m_HasMovedMouse) {
931
930
932 auto zonesUnderCursor = impl->selectionZonesAt(event->pos(), plot());
931 auto zonesUnderCursor = impl->selectionZonesAt(event->pos(), plot());
933 if (zonesUnderCursor.count() > 1) {
932 if (zonesUnderCursor.count() > 1) {
934 // There are multiple zones under the mouse.
933 // There are multiple zones under the mouse.
935 // Performs the selection with a selection dialog.
934 // Performs the selection with a selection dialog.
936 VisualizationMultiZoneSelectionDialog dialog{this};
935 VisualizationMultiZoneSelectionDialog dialog{this};
937 dialog.setZones(zonesUnderCursor);
936 dialog.setZones(zonesUnderCursor);
938 dialog.move(mapToGlobal(event->pos() - QPoint(dialog.width() / 2, 20)));
937 dialog.move(mapToGlobal(event->pos() - QPoint(dialog.width() / 2, 20)));
939 dialog.activateWindow();
938 dialog.activateWindow();
940 dialog.raise();
939 dialog.raise();
941 if (dialog.exec() == QDialog::Accepted) {
940 if (dialog.exec() == QDialog::Accepted) {
942 auto selection = dialog.selectedZones();
941 auto selection = dialog.selectedZones();
943
942
944 if (!isMultiSelectionClick) {
943 if (!isMultiSelectionClick) {
945 parentVisualizationWidget()->selectionZoneManager().clearSelection();
944 parentVisualizationWidget()->selectionZoneManager().clearSelection();
946 }
945 }
947
946
948 for (auto it = selection.cbegin(); it != selection.cend(); ++it) {
947 for (auto it = selection.cbegin(); it != selection.cend(); ++it) {
949 auto zone = it.key();
948 auto zone = it.key();
950 auto isSelected = it.value();
949 auto isSelected = it.value();
951 parentVisualizationWidget()->selectionZoneManager().setSelected(zone,
950 parentVisualizationWidget()->selectionZoneManager().setSelected(zone,
952 isSelected);
951 isSelected);
953
952
954 if (isSelected) {
953 if (isSelected) {
955 // Puts the zone on top of the stack so it can be moved or resized
954 // Puts the zone on top of the stack so it can be moved or resized
956 impl->moveSelectionZoneOnTop(zone, plot());
955 impl->moveSelectionZoneOnTop(zone, plot());
957 }
956 }
958 }
957 }
959 }
958 }
960 }
959 }
961 else {
960 else {
962 if (!isMultiSelectionClick) {
961 if (!isMultiSelectionClick) {
963 parentVisualizationWidget()->selectionZoneManager().select(
962 parentVisualizationWidget()->selectionZoneManager().select(
964 {selectionZoneItemUnderCursor});
963 {selectionZoneItemUnderCursor});
965 impl->moveSelectionZoneOnTop(selectionZoneItemUnderCursor, plot());
964 impl->moveSelectionZoneOnTop(selectionZoneItemUnderCursor, plot());
966 }
965 }
967 else {
966 else {
968 parentVisualizationWidget()->selectionZoneManager().setSelected(
967 parentVisualizationWidget()->selectionZoneManager().setSelected(
969 selectionZoneItemUnderCursor, !selectionZoneItemUnderCursor->selected()
968 selectionZoneItemUnderCursor, !selectionZoneItemUnderCursor->selected()
970 || event->button() == Qt::RightButton);
969 || event->button() == Qt::RightButton);
971 }
970 }
972 }
971 }
973 }
972 }
974 else {
973 else {
975 // No selection change
974 // No selection change
976 }
975 }
977 }
976 }
978 }
977 }
979
978
980 void VisualizationGraphWidget::onDataCacheVariableUpdated()
979 void VisualizationGraphWidget::onDataCacheVariableUpdated()
981 {
980 {
982 auto graphRange = ui->widget->xAxis->range();
981 auto graphRange = ui->widget->xAxis->range();
983 auto dateTime = SqpRange{graphRange.lower, graphRange.upper};
982 auto dateTime = SqpRange{graphRange.lower, graphRange.upper};
984
983
985 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
984 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
986 auto variable = variableEntry.first;
985 auto variable = variableEntry.first;
987 qCDebug(LOG_VisualizationGraphWidget())
986 qCDebug(LOG_VisualizationGraphWidget())
988 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
987 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
989 qCDebug(LOG_VisualizationGraphWidget())
988 qCDebug(LOG_VisualizationGraphWidget())
990 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
989 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
991 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
990 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
992 impl->updateData(variableEntry.second, variable->dataSeries(), variable->range());
991 impl->updateData(variableEntry.second, variable->dataSeries(), variable->range());
993 }
992 }
994 }
993 }
995 }
994 }
996
995
997 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
996 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
998 const SqpRange &range)
997 const SqpRange &range)
999 {
998 {
1000 auto it = impl->m_VariableToPlotMultiMap.find(variable);
999 auto it = impl->m_VariableToPlotMultiMap.find(variable);
1001 if (it != impl->m_VariableToPlotMultiMap.end()) {
1000 if (it != impl->m_VariableToPlotMultiMap.end()) {
1002 impl->updateData(it->second, variable->dataSeries(), range);
1001 impl->updateData(it->second, variable->dataSeries(), range);
1003 }
1002 }
1004 }
1003 }
@@ -1,438 +1,440
1 #include "Visualization/VisualizationSelectionZoneItem.h"
1 #include "Visualization/VisualizationSelectionZoneItem.h"
2 #include "Visualization/VisualizationGraphWidget.h"
2 #include "Visualization/VisualizationGraphWidget.h"
3 #include "Visualization/VisualizationSelectionZoneManager.h"
3 #include "Visualization/VisualizationSelectionZoneManager.h"
4 #include "Visualization/VisualizationWidget.h"
4 #include "Visualization/VisualizationWidget.h"
5
5
6 const QString &DEFAULT_COLOR = QStringLiteral("#E79D41");
6 const QString &DEFAULT_COLOR = QStringLiteral("#E79D41");
7
7
8 struct VisualizationSelectionZoneItem::VisualizationSelectionZoneItemPrivate {
8 struct VisualizationSelectionZoneItem::VisualizationSelectionZoneItemPrivate {
9
9
10 QCustomPlot *m_Plot;
10 QCustomPlot *m_Plot;
11 double m_T1 = 0;
11 double m_T1 = 0;
12 double m_T2 = 0;
12 double m_T2 = 0;
13 QColor m_Color;
13 QColor m_Color;
14
14
15 bool m_IsEditionEnabled = true;
15 bool m_IsEditionEnabled = true;
16 double m_MovedOrinalT1 = 0;
16 double m_MovedOrinalT1 = 0;
17 double m_MovedOrinalT2 = 0;
17 double m_MovedOrinalT2 = 0;
18
18
19 QCPItemStraightLine *m_LeftLine;
19 QCPItemStraightLine *m_LeftLine;
20 QCPItemStraightLine *m_RightLine;
20 QCPItemStraightLine *m_RightLine;
21 QCPItemText *m_NameLabelItem = nullptr;
21 QCPItemText *m_NameLabelItem = nullptr;
22
22
23 enum class EditionMode { NoEdition, ResizeLeft, ResizeRight, Move };
23 enum class EditionMode { NoEdition, ResizeLeft, ResizeRight, Move };
24 EditionMode m_CurrentEditionMode;
24 EditionMode m_CurrentEditionMode;
25
25
26 QVector<VisualizationSelectionZoneItem *> m_AssociatedEditedZones;
26 QVector<VisualizationSelectionZoneItem *> m_AssociatedEditedZones;
27
27
28 VisualizationSelectionZoneItemPrivate(QCustomPlot *plot)
28 VisualizationSelectionZoneItemPrivate(QCustomPlot *plot)
29 : m_Plot(plot), m_Color(Qt::blue), m_CurrentEditionMode(EditionMode::NoEdition)
29 : m_Plot(plot), m_Color(Qt::blue), m_CurrentEditionMode(EditionMode::NoEdition)
30 {
30 {
31 }
31 }
32
32
33 void updatePosition(VisualizationSelectionZoneItem *item)
33 void updatePosition(VisualizationSelectionZoneItem *item)
34 {
34 {
35 item->topLeft->setCoords(m_T1, 0);
35 item->topLeft->setCoords(m_T1, 0);
36 item->bottomRight->setCoords(m_T2, 1);
36 item->bottomRight->setCoords(m_T2, 1);
37 }
37 }
38
38
39 EditionMode getEditionMode(const QPoint &pos, const VisualizationSelectionZoneItem *zoneItem)
39 EditionMode getEditionMode(const QPoint &pos, const VisualizationSelectionZoneItem *zoneItem)
40 {
40 {
41 auto distanceLeft = m_LeftLine->selectTest(pos, false);
41 auto distanceLeft = m_LeftLine->selectTest(pos, false);
42 auto distanceRight = m_RightLine->selectTest(pos, false);
42 auto distanceRight = m_RightLine->selectTest(pos, false);
43 auto distance = zoneItem->selectTest(pos, false);
43 auto distance = zoneItem->selectTest(pos, false);
44
44
45 if (distanceRight <= distance) {
45 if (distanceRight <= distance) {
46 return VisualizationSelectionZoneItemPrivate::EditionMode::ResizeRight;
46 return VisualizationSelectionZoneItemPrivate::EditionMode::ResizeRight;
47 }
47 }
48 else if (distanceLeft <= distance) {
48 else if (distanceLeft <= distance) {
49 return VisualizationSelectionZoneItemPrivate::EditionMode::ResizeLeft;
49 return VisualizationSelectionZoneItemPrivate::EditionMode::ResizeLeft;
50 }
50 }
51
51
52 return VisualizationSelectionZoneItemPrivate::EditionMode::Move;
52 return VisualizationSelectionZoneItemPrivate::EditionMode::Move;
53 }
53 }
54
54
55 double pixelSizeToAxisXSize(double pixels)
55 double pixelSizeToAxisXSize(double pixels)
56 {
56 {
57 auto axis = m_Plot->axisRect()->axis(QCPAxis::atBottom);
57 auto axis = m_Plot->axisRect()->axis(QCPAxis::atBottom);
58 return axis->pixelToCoord(pixels) - axis->pixelToCoord(0);
58 return axis->pixelToCoord(pixels) - axis->pixelToCoord(0);
59 }
59 }
60
60
61 bool alignZones(VisualizationSelectionZoneItem *referenceZone,
61 bool alignZones(VisualizationSelectionZoneItem *referenceZone,
62 const QVector<VisualizationSelectionZoneItem *> &zonesToAlign, bool alignOnLeft,
62 const QVector<VisualizationSelectionZoneItem *> &zonesToAlign, bool alignOnLeft,
63 bool allowResize, bool vertically)
63 bool allowResize, bool vertically)
64 {
64 {
65 auto result = false;
65 auto result = false;
66
66
67 auto referenceTime
67 auto referenceTime
68 = alignOnLeft ? referenceZone->range().m_TStart : referenceZone->range().m_TEnd;
68 = alignOnLeft ? referenceZone->range().m_TStart : referenceZone->range().m_TEnd;
69
69
70 auto referenceBottomAxis = m_Plot->axisRect()->axis(QCPAxis::atBottom);
70 auto referenceBottomAxis = m_Plot->axisRect()->axis(QCPAxis::atBottom);
71 auto referenceVerticalPosition = referenceBottomAxis->coordToPixel(referenceTime);
71 auto referenceVerticalPosition = referenceBottomAxis->coordToPixel(referenceTime);
72
72
73 for (auto otherZone : zonesToAlign) {
73 for (auto otherZone : zonesToAlign) {
74
74
75 auto otherZoneRange = otherZone->range();
75 auto otherZoneRange = otherZone->range();
76 auto newZoneStart = otherZoneRange.m_TStart;
76 auto newZoneStart = otherZoneRange.m_TStart;
77 auto newZoneEnd = otherZoneRange.m_TEnd;
77 auto newZoneEnd = otherZoneRange.m_TEnd;
78
78
79 auto alignedTime = referenceTime;
79 auto alignedTime = referenceTime;
80 if (vertically) {
80 if (vertically) {
81 auto otherZoneAxis = otherZone->parentPlot()->axisRect()->axis(QCPAxis::atBottom);
81 auto otherZoneAxis = otherZone->parentPlot()->axisRect()->axis(QCPAxis::atBottom);
82 alignedTime = otherZoneAxis->pixelToCoord(referenceVerticalPosition);
82 alignedTime = otherZoneAxis->pixelToCoord(referenceVerticalPosition);
83 }
83 }
84
84
85 if (alignOnLeft) {
85 if (alignOnLeft) {
86 newZoneStart = alignedTime;
86 newZoneStart = alignedTime;
87 if (!allowResize) {
87 if (!allowResize) {
88 newZoneEnd = alignedTime + (otherZoneRange.m_TEnd - otherZoneRange.m_TStart);
88 newZoneEnd = alignedTime + (otherZoneRange.m_TEnd - otherZoneRange.m_TStart);
89 }
89 }
90 }
90 }
91 else { // align on right
91 else { // align on right
92 newZoneEnd = alignedTime;
92 newZoneEnd = alignedTime;
93 if (!allowResize) {
93 if (!allowResize) {
94 newZoneStart = alignedTime - (otherZoneRange.m_TEnd - otherZoneRange.m_TStart);
94 newZoneStart = alignedTime - (otherZoneRange.m_TEnd - otherZoneRange.m_TStart);
95 }
95 }
96 }
96 }
97
97
98 if (newZoneStart < newZoneEnd) {
98 if (newZoneStart < newZoneEnd) {
99 result = true;
99 result = true;
100 otherZone->setRange(newZoneStart, newZoneEnd);
100 otherZone->setRange(newZoneStart, newZoneEnd);
101 otherZone->parentPlot()->replot();
101 otherZone->parentPlot()->replot();
102 }
102 }
103 }
103 }
104
104
105 return result;
105 return result;
106 }
106 }
107 };
107 };
108
108
109 VisualizationSelectionZoneItem::VisualizationSelectionZoneItem(QCustomPlot *plot)
109 VisualizationSelectionZoneItem::VisualizationSelectionZoneItem(QCustomPlot *plot)
110 : QCPItemRect(plot),
110 : QCPItemRect(plot),
111 impl{spimpl::make_unique_impl<VisualizationSelectionZoneItemPrivate>(plot)}
111 impl{spimpl::make_unique_impl<VisualizationSelectionZoneItemPrivate>(plot)}
112 {
112 {
113 topLeft->setTypeX(QCPItemPosition::ptPlotCoords);
113 topLeft->setTypeX(QCPItemPosition::ptPlotCoords);
114 topLeft->setTypeY(QCPItemPosition::ptAxisRectRatio);
114 topLeft->setTypeY(QCPItemPosition::ptAxisRectRatio);
115 bottomRight->setTypeX(QCPItemPosition::ptPlotCoords);
115 bottomRight->setTypeX(QCPItemPosition::ptPlotCoords);
116 bottomRight->setTypeY(QCPItemPosition::ptAxisRectRatio);
116 bottomRight->setTypeY(QCPItemPosition::ptAxisRectRatio);
117 setSelectable(false);
117 setSelectable(false);
118
118
119 impl->m_RightLine = new QCPItemStraightLine(plot);
119 impl->m_RightLine = new QCPItemStraightLine(plot);
120 impl->m_RightLine->point1->setParentAnchor(topRight);
120 impl->m_RightLine->point1->setParentAnchor(topRight);
121 impl->m_RightLine->point2->setParentAnchor(bottomRight);
121 impl->m_RightLine->point2->setParentAnchor(bottomRight);
122 impl->m_RightLine->point1->setTypeX(QCPItemPosition::ptAbsolute);
122 impl->m_RightLine->point1->setTypeX(QCPItemPosition::ptAbsolute);
123 impl->m_RightLine->point1->setTypeY(QCPItemPosition::ptAbsolute);
123 impl->m_RightLine->point1->setTypeY(QCPItemPosition::ptAbsolute);
124 impl->m_RightLine->point2->setTypeX(QCPItemPosition::ptAbsolute);
124 impl->m_RightLine->point2->setTypeX(QCPItemPosition::ptAbsolute);
125 impl->m_RightLine->point2->setTypeY(QCPItemPosition::ptAbsolute);
125 impl->m_RightLine->point2->setTypeY(QCPItemPosition::ptAbsolute);
126 impl->m_RightLine->setSelectable(false);
126 impl->m_RightLine->setSelectable(false);
127
127
128 impl->m_LeftLine = new QCPItemStraightLine(plot);
128 impl->m_LeftLine = new QCPItemStraightLine(plot);
129 impl->m_LeftLine->point1->setParentAnchor(topLeft);
129 impl->m_LeftLine->point1->setParentAnchor(topLeft);
130 impl->m_LeftLine->point2->setParentAnchor(bottomLeft);
130 impl->m_LeftLine->point2->setParentAnchor(bottomLeft);
131 impl->m_LeftLine->point1->setTypeX(QCPItemPosition::ptAbsolute);
131 impl->m_LeftLine->point1->setTypeX(QCPItemPosition::ptAbsolute);
132 impl->m_LeftLine->point1->setTypeY(QCPItemPosition::ptAbsolute);
132 impl->m_LeftLine->point1->setTypeY(QCPItemPosition::ptAbsolute);
133 impl->m_LeftLine->point2->setTypeX(QCPItemPosition::ptAbsolute);
133 impl->m_LeftLine->point2->setTypeX(QCPItemPosition::ptAbsolute);
134 impl->m_LeftLine->point2->setTypeY(QCPItemPosition::ptAbsolute);
134 impl->m_LeftLine->point2->setTypeY(QCPItemPosition::ptAbsolute);
135 impl->m_LeftLine->setSelectable(false);
135 impl->m_LeftLine->setSelectable(false);
136
136
137 connect(this, &VisualizationSelectionZoneItem::selectionChanged, impl->m_RightLine,
137 connect(this, &VisualizationSelectionZoneItem::selectionChanged, impl->m_RightLine,
138 &QCPItemStraightLine::setSelected);
138 &QCPItemStraightLine::setSelected);
139 connect(this, &VisualizationSelectionZoneItem::selectionChanged, impl->m_LeftLine,
139 connect(this, &VisualizationSelectionZoneItem::selectionChanged, impl->m_LeftLine,
140 &QCPItemStraightLine::setSelected);
140 &QCPItemStraightLine::setSelected);
141
141
142 setColor(QColor(DEFAULT_COLOR));
142 setColor(QColor(DEFAULT_COLOR));
143 }
143 }
144
144
145 VisualizationSelectionZoneItem::~VisualizationSelectionZoneItem() {}
145 VisualizationSelectionZoneItem::~VisualizationSelectionZoneItem()
146 {
147 }
146
148
147 VisualizationGraphWidget *VisualizationSelectionZoneItem::parentGraphWidget() const noexcept
149 VisualizationGraphWidget *VisualizationSelectionZoneItem::parentGraphWidget() const noexcept
148 {
150 {
149 auto parent = impl->m_Plot->parentWidget();
151 auto parent = impl->m_Plot->parentWidget();
150 while (parent != nullptr && !qobject_cast<VisualizationGraphWidget *>(parent)) {
152 while (parent != nullptr && !qobject_cast<VisualizationGraphWidget *>(parent)) {
151 parent = parent->parentWidget();
153 parent = parent->parentWidget();
152 }
154 }
153
155
154 return qobject_cast<VisualizationGraphWidget *>(parent);
156 return qobject_cast<VisualizationGraphWidget *>(parent);
155 }
157 }
156
158
157 void VisualizationSelectionZoneItem::setName(const QString &name)
159 void VisualizationSelectionZoneItem::setName(const QString &name)
158 {
160 {
159 if (name.isEmpty() && impl->m_NameLabelItem) {
161 if (name.isEmpty() && impl->m_NameLabelItem) {
160 impl->m_Plot->removeItem(impl->m_NameLabelItem);
162 impl->m_Plot->removeItem(impl->m_NameLabelItem);
161 impl->m_NameLabelItem = nullptr;
163 impl->m_NameLabelItem = nullptr;
162 }
164 }
163 else if (!impl->m_NameLabelItem) {
165 else if (!impl->m_NameLabelItem) {
164 impl->m_NameLabelItem = new QCPItemText(impl->m_Plot);
166 impl->m_NameLabelItem = new QCPItemText(impl->m_Plot);
165 impl->m_NameLabelItem->setText(name);
167 impl->m_NameLabelItem->setText(name);
166 impl->m_NameLabelItem->setPositionAlignment(Qt::AlignHCenter | Qt::AlignTop);
168 impl->m_NameLabelItem->setPositionAlignment(Qt::AlignHCenter | Qt::AlignTop);
167 impl->m_NameLabelItem->setColor(impl->m_Color);
169 impl->m_NameLabelItem->setColor(impl->m_Color);
168 impl->m_NameLabelItem->position->setParentAnchor(top);
170 impl->m_NameLabelItem->position->setParentAnchor(top);
169 }
171 }
170 }
172 }
171
173
172 QString VisualizationSelectionZoneItem::name() const
174 QString VisualizationSelectionZoneItem::name() const
173 {
175 {
174 if (!impl->m_NameLabelItem) {
176 if (!impl->m_NameLabelItem) {
175 return QString();
177 return QString();
176 }
178 }
177
179
178 return impl->m_NameLabelItem->text();
180 return impl->m_NameLabelItem->text();
179 }
181 }
180
182
181 SqpRange VisualizationSelectionZoneItem::range() const
183 SqpRange VisualizationSelectionZoneItem::range() const
182 {
184 {
183 SqpRange range;
185 SqpRange range;
184 range.m_TStart = impl->m_T1 <= impl->m_T2 ? impl->m_T1 : impl->m_T2;
186 range.m_TStart = impl->m_T1 <= impl->m_T2 ? impl->m_T1 : impl->m_T2;
185 range.m_TEnd = impl->m_T1 > impl->m_T2 ? impl->m_T1 : impl->m_T2;
187 range.m_TEnd = impl->m_T1 > impl->m_T2 ? impl->m_T1 : impl->m_T2;
186 return range;
188 return range;
187 }
189 }
188
190
189 void VisualizationSelectionZoneItem::setRange(double tstart, double tend)
191 void VisualizationSelectionZoneItem::setRange(double tstart, double tend)
190 {
192 {
191 impl->m_T1 = tstart;
193 impl->m_T1 = tstart;
192 impl->m_T2 = tend;
194 impl->m_T2 = tend;
193 impl->updatePosition(this);
195 impl->updatePosition(this);
194 }
196 }
195
197
196 void VisualizationSelectionZoneItem::setStart(double tstart)
198 void VisualizationSelectionZoneItem::setStart(double tstart)
197 {
199 {
198 impl->m_T1 = tstart;
200 impl->m_T1 = tstart;
199 impl->updatePosition(this);
201 impl->updatePosition(this);
200 }
202 }
201
203
202 void VisualizationSelectionZoneItem::setEnd(double tend)
204 void VisualizationSelectionZoneItem::setEnd(double tend)
203 {
205 {
204 impl->m_T2 = tend;
206 impl->m_T2 = tend;
205 impl->updatePosition(this);
207 impl->updatePosition(this);
206 }
208 }
207
209
208 void VisualizationSelectionZoneItem::setColor(const QColor &color)
210 void VisualizationSelectionZoneItem::setColor(const QColor &color)
209 {
211 {
210 impl->m_Color = color;
212 impl->m_Color = color;
211
213
212 auto brushColor = color;
214 auto brushColor = color;
213 brushColor.setAlpha(80);
215 brushColor.setAlpha(80);
214 setBrush(QBrush(brushColor));
216 setBrush(QBrush(brushColor));
215 setPen(QPen(Qt::NoPen));
217 setPen(QPen(Qt::NoPen));
216
218
217 auto selectedBrushColor = brushColor;
219 auto selectedBrushColor = brushColor;
218 selectedBrushColor.setAlpha(150);
220 selectedBrushColor.setAlpha(150);
219 setSelectedBrush(QBrush(selectedBrushColor));
221 setSelectedBrush(QBrush(selectedBrushColor));
220 setSelectedPen(QPen(Qt::NoPen));
222 setSelectedPen(QPen(Qt::NoPen));
221
223
222 auto linePen = QPen(color);
224 auto linePen = QPen(color);
223 linePen.setStyle(Qt::SolidLine);
225 linePen.setStyle(Qt::SolidLine);
224 linePen.setWidth(4);
226 linePen.setWidth(4);
225
227
226 auto selectedLinePen = linePen;
228 auto selectedLinePen = linePen;
227 selectedLinePen.setColor(color.darker(120));
229 selectedLinePen.setColor(color.darker(120));
228 selectedLinePen.setWidth(4);
230 selectedLinePen.setWidth(4);
229
231
230 impl->m_LeftLine->setPen(linePen);
232 impl->m_LeftLine->setPen(linePen);
231 impl->m_RightLine->setPen(linePen);
233 impl->m_RightLine->setPen(linePen);
232
234
233 impl->m_LeftLine->setSelectedPen(selectedLinePen);
235 impl->m_LeftLine->setSelectedPen(selectedLinePen);
234 impl->m_RightLine->setSelectedPen(selectedLinePen);
236 impl->m_RightLine->setSelectedPen(selectedLinePen);
235 }
237 }
236
238
237 void VisualizationSelectionZoneItem::setEditionEnabled(bool value)
239 void VisualizationSelectionZoneItem::setEditionEnabled(bool value)
238 {
240 {
239 impl->m_IsEditionEnabled = value;
241 impl->m_IsEditionEnabled = value;
240 setSelectable(value);
242 setSelectable(value);
241 if (!value) {
243 if (!value) {
242 setSelected(false);
244 setSelected(false);
243 impl->m_CurrentEditionMode = VisualizationSelectionZoneItemPrivate::EditionMode::NoEdition;
245 impl->m_CurrentEditionMode = VisualizationSelectionZoneItemPrivate::EditionMode::NoEdition;
244 }
246 }
245 }
247 }
246
248
247 bool VisualizationSelectionZoneItem::isEditionEnabled() const
249 bool VisualizationSelectionZoneItem::isEditionEnabled() const
248 {
250 {
249 return impl->m_IsEditionEnabled;
251 return impl->m_IsEditionEnabled;
250 }
252 }
251
253
252 void VisualizationSelectionZoneItem::moveToTop()
254 void VisualizationSelectionZoneItem::moveToTop()
253 {
255 {
254 moveToLayer(layer(), false);
256 moveToLayer(layer(), false);
255 }
257 }
256
258
257 Qt::CursorShape
259 Qt::CursorShape
258 VisualizationSelectionZoneItem::curshorShapeForPosition(const QPoint &position) const
260 VisualizationSelectionZoneItem::curshorShapeForPosition(const QPoint &position) const
259 {
261 {
260 auto mode = impl->m_CurrentEditionMode
262 auto mode = impl->m_CurrentEditionMode
261 == VisualizationSelectionZoneItemPrivate::EditionMode::NoEdition
263 == VisualizationSelectionZoneItemPrivate::EditionMode::NoEdition
262 ? impl->getEditionMode(position, this)
264 ? impl->getEditionMode(position, this)
263 : impl->m_CurrentEditionMode;
265 : impl->m_CurrentEditionMode;
264 switch (mode) {
266 switch (mode) {
265 case VisualizationSelectionZoneItemPrivate::EditionMode::Move:
267 case VisualizationSelectionZoneItemPrivate::EditionMode::Move:
266 return Qt::SizeAllCursor;
268 return Qt::SizeAllCursor;
267 case VisualizationSelectionZoneItemPrivate::EditionMode::ResizeLeft:
269 case VisualizationSelectionZoneItemPrivate::EditionMode::ResizeLeft:
268 case VisualizationSelectionZoneItemPrivate::EditionMode::ResizeRight: // fallthrough
270 case VisualizationSelectionZoneItemPrivate::EditionMode::ResizeRight: // fallthrough
269 return Qt::SizeHorCursor;
271 return Qt::SizeHorCursor;
270 default:
272 default:
271 return Qt::ArrowCursor;
273 return Qt::ArrowCursor;
272 }
274 }
273 }
275 }
274
276
275 void VisualizationSelectionZoneItem::setHovered(bool value)
277 void VisualizationSelectionZoneItem::setHovered(bool value)
276 {
278 {
277 if (value) {
279 if (value) {
278 auto linePen = impl->m_LeftLine->pen();
280 auto linePen = impl->m_LeftLine->pen();
279 linePen.setStyle(Qt::DotLine);
281 linePen.setStyle(Qt::DotLine);
280 linePen.setWidth(3);
282 linePen.setWidth(3);
281
283
282 auto selectedLinePen = impl->m_LeftLine->selectedPen();
284 auto selectedLinePen = impl->m_LeftLine->selectedPen();
283 ;
285 ;
284 selectedLinePen.setStyle(Qt::DotLine);
286 selectedLinePen.setStyle(Qt::DotLine);
285 selectedLinePen.setWidth(3);
287 selectedLinePen.setWidth(3);
286
288
287 impl->m_LeftLine->setPen(linePen);
289 impl->m_LeftLine->setPen(linePen);
288 impl->m_RightLine->setPen(linePen);
290 impl->m_RightLine->setPen(linePen);
289
291
290 impl->m_LeftLine->setSelectedPen(selectedLinePen);
292 impl->m_LeftLine->setSelectedPen(selectedLinePen);
291 impl->m_RightLine->setSelectedPen(selectedLinePen);
293 impl->m_RightLine->setSelectedPen(selectedLinePen);
292 }
294 }
293 else {
295 else {
294 setColor(impl->m_Color);
296 setColor(impl->m_Color);
295 }
297 }
296 }
298 }
297
299
298 void VisualizationSelectionZoneItem::setAssociatedEditedZones(
300 void VisualizationSelectionZoneItem::setAssociatedEditedZones(
299 const QVector<VisualizationSelectionZoneItem *> &associatedZones)
301 const QVector<VisualizationSelectionZoneItem *> &associatedZones)
300 {
302 {
301 impl->m_AssociatedEditedZones = associatedZones;
303 impl->m_AssociatedEditedZones = associatedZones;
302 impl->m_AssociatedEditedZones.removeAll(this);
304 impl->m_AssociatedEditedZones.removeAll(this);
303 }
305 }
304
306
305 bool VisualizationSelectionZoneItem::alignZonesVerticallyOnLeft(
307 bool VisualizationSelectionZoneItem::alignZonesVerticallyOnLeft(
306 const QVector<VisualizationSelectionZoneItem *> &zonesToAlign, bool allowResize)
308 const QVector<VisualizationSelectionZoneItem *> &zonesToAlign, bool allowResize)
307 {
309 {
308 return impl->alignZones(this, zonesToAlign, true, allowResize, true);
310 return impl->alignZones(this, zonesToAlign, true, allowResize, true);
309 }
311 }
310
312
311 bool VisualizationSelectionZoneItem::alignZonesVerticallyOnRight(
313 bool VisualizationSelectionZoneItem::alignZonesVerticallyOnRight(
312 const QVector<VisualizationSelectionZoneItem *> &zonesToAlign, bool allowResize)
314 const QVector<VisualizationSelectionZoneItem *> &zonesToAlign, bool allowResize)
313 {
315 {
314 return impl->alignZones(this, zonesToAlign, false, allowResize, true);
316 return impl->alignZones(this, zonesToAlign, false, allowResize, true);
315 }
317 }
316
318
317 bool VisualizationSelectionZoneItem::alignZonesTemporallyOnLeft(
319 bool VisualizationSelectionZoneItem::alignZonesTemporallyOnLeft(
318 const QVector<VisualizationSelectionZoneItem *> &zonesToAlign, bool allowResize)
320 const QVector<VisualizationSelectionZoneItem *> &zonesToAlign, bool allowResize)
319 {
321 {
320 return impl->alignZones(this, zonesToAlign, true, allowResize, false);
322 return impl->alignZones(this, zonesToAlign, true, allowResize, false);
321 }
323 }
322
324
323 bool VisualizationSelectionZoneItem::alignZonesTemporallyOnRight(
325 bool VisualizationSelectionZoneItem::alignZonesTemporallyOnRight(
324 const QVector<VisualizationSelectionZoneItem *> &zonesToAlign, bool allowResize)
326 const QVector<VisualizationSelectionZoneItem *> &zonesToAlign, bool allowResize)
325 {
327 {
326 return impl->alignZones(this, zonesToAlign, false, allowResize, false);
328 return impl->alignZones(this, zonesToAlign, false, allowResize, false);
327 }
329 }
328
330
329 void VisualizationSelectionZoneItem::mousePressEvent(QMouseEvent *event, const QVariant &details)
331 void VisualizationSelectionZoneItem::mousePressEvent(QMouseEvent *event, const QVariant &details)
330 {
332 {
331 Q_UNUSED(details);
333 Q_UNUSED(details);
332
334
333 if (isEditionEnabled() && event->button() == Qt::LeftButton) {
335 if (isEditionEnabled() && event->button() == Qt::LeftButton) {
334 impl->m_CurrentEditionMode = impl->getEditionMode(event->pos(), this);
336 impl->m_CurrentEditionMode = impl->getEditionMode(event->pos(), this);
335
337
336 impl->m_MovedOrinalT1 = impl->m_T1;
338 impl->m_MovedOrinalT1 = impl->m_T1;
337 impl->m_MovedOrinalT2 = impl->m_T2;
339 impl->m_MovedOrinalT2 = impl->m_T2;
338 for (auto associatedZone : impl->m_AssociatedEditedZones) {
340 for (auto associatedZone : impl->m_AssociatedEditedZones) {
339 associatedZone->impl->m_MovedOrinalT1 = associatedZone->impl->m_T1;
341 associatedZone->impl->m_MovedOrinalT1 = associatedZone->impl->m_T1;
340 associatedZone->impl->m_MovedOrinalT2 = associatedZone->impl->m_T2;
342 associatedZone->impl->m_MovedOrinalT2 = associatedZone->impl->m_T2;
341 }
343 }
342 }
344 }
343 else {
345 else {
344 impl->m_CurrentEditionMode = VisualizationSelectionZoneItemPrivate::EditionMode::NoEdition;
346 impl->m_CurrentEditionMode = VisualizationSelectionZoneItemPrivate::EditionMode::NoEdition;
345 event->ignore();
347 event->ignore();
346 }
348 }
347 }
349 }
348
350
349 void VisualizationSelectionZoneItem::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos)
351 void VisualizationSelectionZoneItem::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos)
350 {
352 {
351 if (isEditionEnabled()) {
353 if (isEditionEnabled()) {
352 if (!selected()) {
354 if (!selected()) {
353 // Force the item to be selected during the edition
355 // Force the item to be selected during the edition
354 parentGraphWidget()->parentVisualizationWidget()->selectionZoneManager().setSelected(
356 parentGraphWidget()->parentVisualizationWidget()->selectionZoneManager().setSelected(
355 this, true);
357 this, true);
356 }
358 }
357
359
358 auto axis = impl->m_Plot->axisRect()->axis(QCPAxis::atBottom);
360 auto axis = impl->m_Plot->axisRect()->axis(QCPAxis::atBottom);
359 auto pixelDiff = event->pos().x() - startPos.x();
361 auto pixelDiff = event->pos().x() - startPos.x();
360 auto diff = impl->pixelSizeToAxisXSize(pixelDiff);
362 auto diff = impl->pixelSizeToAxisXSize(pixelDiff);
361
363
362 switch (impl->m_CurrentEditionMode) {
364 switch (impl->m_CurrentEditionMode) {
363 case VisualizationSelectionZoneItemPrivate::EditionMode::Move:
365 case VisualizationSelectionZoneItemPrivate::EditionMode::Move:
364 setRange(impl->m_MovedOrinalT1 + diff, impl->m_MovedOrinalT2 + diff);
366 setRange(impl->m_MovedOrinalT1 + diff, impl->m_MovedOrinalT2 + diff);
365 for (auto associatedZone : impl->m_AssociatedEditedZones) {
367 for (auto associatedZone : impl->m_AssociatedEditedZones) {
366 associatedZone->move(pixelDiff);
368 associatedZone->move(pixelDiff);
367 }
369 }
368 break;
370 break;
369 case VisualizationSelectionZoneItemPrivate::EditionMode::ResizeLeft:
371 case VisualizationSelectionZoneItemPrivate::EditionMode::ResizeLeft:
370 setStart(impl->m_MovedOrinalT1 + diff);
372 setStart(impl->m_MovedOrinalT1 + diff);
371 for (auto associatedZone : impl->m_AssociatedEditedZones) {
373 for (auto associatedZone : impl->m_AssociatedEditedZones) {
372 impl->m_MovedOrinalT1 < impl->m_MovedOrinalT2
374 impl->m_MovedOrinalT1 < impl->m_MovedOrinalT2
373 ? associatedZone->resizeLeft(pixelDiff)
375 ? associatedZone->resizeLeft(pixelDiff)
374 : associatedZone->resizeRight(pixelDiff);
376 : associatedZone->resizeRight(pixelDiff);
375 }
377 }
376 break;
378 break;
377 case VisualizationSelectionZoneItemPrivate::EditionMode::ResizeRight:
379 case VisualizationSelectionZoneItemPrivate::EditionMode::ResizeRight:
378 setEnd(impl->m_MovedOrinalT2 + diff);
380 setEnd(impl->m_MovedOrinalT2 + diff);
379 for (auto associatedZone : impl->m_AssociatedEditedZones) {
381 for (auto associatedZone : impl->m_AssociatedEditedZones) {
380 impl->m_MovedOrinalT1 < impl->m_MovedOrinalT2
382 impl->m_MovedOrinalT1 < impl->m_MovedOrinalT2
381 ? associatedZone->resizeRight(pixelDiff)
383 ? associatedZone->resizeRight(pixelDiff)
382 : associatedZone->resizeLeft(pixelDiff);
384 : associatedZone->resizeLeft(pixelDiff);
383 }
385 }
384 break;
386 break;
385 default:
387 default:
386 break;
388 break;
387 }
389 }
388
390
389 for (auto associatedZone : impl->m_AssociatedEditedZones) {
391 for (auto associatedZone : impl->m_AssociatedEditedZones) {
390 associatedZone->parentPlot()->replot();
392 associatedZone->parentPlot()->replot();
391 }
393 }
392 }
394 }
393 else {
395 else {
394 event->ignore();
396 event->ignore();
395 }
397 }
396 }
398 }
397
399
398 void VisualizationSelectionZoneItem::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)
400 void VisualizationSelectionZoneItem::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)
399 {
401 {
400 Q_UNUSED(startPos);
402 Q_UNUSED(startPos);
401
403
402 if (isEditionEnabled()) {
404 if (isEditionEnabled()) {
403 impl->m_CurrentEditionMode = VisualizationSelectionZoneItemPrivate::EditionMode::NoEdition;
405 impl->m_CurrentEditionMode = VisualizationSelectionZoneItemPrivate::EditionMode::NoEdition;
404 }
406 }
405 else {
407 else {
406 event->ignore();
408 event->ignore();
407 }
409 }
408
410
409 impl->m_AssociatedEditedZones.clear();
411 impl->m_AssociatedEditedZones.clear();
410 }
412 }
411
413
412 void VisualizationSelectionZoneItem::resizeLeft(double pixelDiff)
414 void VisualizationSelectionZoneItem::resizeLeft(double pixelDiff)
413 {
415 {
414 auto diff = impl->pixelSizeToAxisXSize(pixelDiff);
416 auto diff = impl->pixelSizeToAxisXSize(pixelDiff);
415 if (impl->m_MovedOrinalT1 <= impl->m_MovedOrinalT2) {
417 if (impl->m_MovedOrinalT1 <= impl->m_MovedOrinalT2) {
416 setStart(impl->m_MovedOrinalT1 + diff);
418 setStart(impl->m_MovedOrinalT1 + diff);
417 }
419 }
418 else {
420 else {
419 setEnd(impl->m_MovedOrinalT2 + diff);
421 setEnd(impl->m_MovedOrinalT2 + diff);
420 }
422 }
421 }
423 }
422
424
423 void VisualizationSelectionZoneItem::resizeRight(double pixelDiff)
425 void VisualizationSelectionZoneItem::resizeRight(double pixelDiff)
424 {
426 {
425 auto diff = impl->pixelSizeToAxisXSize(pixelDiff);
427 auto diff = impl->pixelSizeToAxisXSize(pixelDiff);
426 if (impl->m_MovedOrinalT1 > impl->m_MovedOrinalT2) {
428 if (impl->m_MovedOrinalT1 > impl->m_MovedOrinalT2) {
427 setStart(impl->m_MovedOrinalT1 + diff);
429 setStart(impl->m_MovedOrinalT1 + diff);
428 }
430 }
429 else {
431 else {
430 setEnd(impl->m_MovedOrinalT2 + diff);
432 setEnd(impl->m_MovedOrinalT2 + diff);
431 }
433 }
432 }
434 }
433
435
434 void VisualizationSelectionZoneItem::move(double pixelDiff)
436 void VisualizationSelectionZoneItem::move(double pixelDiff)
435 {
437 {
436 auto diff = impl->pixelSizeToAxisXSize(pixelDiff);
438 auto diff = impl->pixelSizeToAxisXSize(pixelDiff);
437 setRange(impl->m_MovedOrinalT1 + diff, impl->m_MovedOrinalT2 + diff);
439 setRange(impl->m_MovedOrinalT1 + diff, impl->m_MovedOrinalT2 + diff);
438 }
440 }
@@ -1,579 +1,579
1 #include "AmdaResultParser.h"
1 #include "AmdaResultParser.h"
2
2
3 #include <Data/ScalarSeries.h>
3 #include <Data/ScalarSeries.h>
4 #include <Data/SpectrogramSeries.h>
4 #include <Data/SpectrogramSeries.h>
5 #include <Data/VectorSeries.h>
5 #include <Data/VectorSeries.h>
6
6
7 #include <QObject>
7 #include <QObject>
8 #include <QtTest>
8 #include <QtTest>
9
9
10 namespace {
10 namespace {
11
11
12 /// Path for the tests
12 /// Path for the tests
13 const auto TESTS_RESOURCES_PATH
13 const auto TESTS_RESOURCES_PATH
14 = QFileInfo{QString{AMDA_TESTS_RESOURCES_DIR}, "TestAmdaResultParser"}.absoluteFilePath();
14 = QFileInfo{QString{AMDA_TESTS_RESOURCES_DIR}, "TestAmdaResultParser"}.absoluteFilePath();
15
15
16 QDateTime dateTime(int year, int month, int day, int hours, int minutes, int seconds)
16 QDateTime dateTime(int year, int month, int day, int hours, int minutes, int seconds)
17 {
17 {
18 return QDateTime{{year, month, day}, {hours, minutes, seconds}, Qt::UTC};
18 return QDateTime{{year, month, day}, {hours, minutes, seconds}, Qt::UTC};
19 }
19 }
20
20
21 QString inputFilePath(const QString &inputFileName)
21 QString inputFilePath(const QString &inputFileName)
22 {
22 {
23 return QFileInfo{TESTS_RESOURCES_PATH, inputFileName}.absoluteFilePath();
23 return QFileInfo{TESTS_RESOURCES_PATH, inputFileName}.absoluteFilePath();
24 }
24 }
25
25
26 template <typename T>
26 template <typename T>
27 struct ExpectedResults {
27 struct ExpectedResults {
28
28
29 ExpectedResults &setParsingOK(bool parsingOK)
29 ExpectedResults &setParsingOK(bool parsingOK)
30 {
30 {
31 m_ParsingOK = parsingOK;
31 m_ParsingOK = parsingOK;
32 return *this;
32 return *this;
33 }
33 }
34
34
35 ExpectedResults &setXAxisUnit(Unit xAxisUnit)
35 ExpectedResults &setXAxisUnit(Unit xAxisUnit)
36 {
36 {
37 m_XAxisUnit = std::move(xAxisUnit);
37 m_XAxisUnit = std::move(xAxisUnit);
38 return *this;
38 return *this;
39 }
39 }
40
40
41 ExpectedResults &setXAxisData(const QVector<QDateTime> &xAxisData)
41 ExpectedResults &setXAxisData(const QVector<QDateTime> &xAxisData)
42 {
42 {
43 m_XAxisData.clear();
43 m_XAxisData.clear();
44
44
45 // Converts QVector<QDateTime> to QVector<double>
45 // Converts QVector<QDateTime> to QVector<double>
46 std::transform(xAxisData.cbegin(), xAxisData.cend(), std::back_inserter(m_XAxisData),
46 std::transform(xAxisData.cbegin(), xAxisData.cend(), std::back_inserter(m_XAxisData),
47 [](const auto &dateTime) { return dateTime.toMSecsSinceEpoch() / 1000.; });
47 [](const auto &dateTime) { return dateTime.toMSecsSinceEpoch() / 1000.; });
48
48
49 return *this;
49 return *this;
50 }
50 }
51
51
52 ExpectedResults &setValuesUnit(Unit valuesUnit)
52 ExpectedResults &setValuesUnit(Unit valuesUnit)
53 {
53 {
54 m_ValuesUnit = std::move(valuesUnit);
54 m_ValuesUnit = std::move(valuesUnit);
55 return *this;
55 return *this;
56 }
56 }
57
57
58 ExpectedResults &setValuesData(QVector<double> valuesData)
58 ExpectedResults &setValuesData(QVector<double> valuesData)
59 {
59 {
60 m_ValuesData.clear();
60 m_ValuesData.clear();
61 m_ValuesData.push_back(std::move(valuesData));
61 m_ValuesData.push_back(std::move(valuesData));
62 return *this;
62 return *this;
63 }
63 }
64
64
65 ExpectedResults &setValuesData(QVector<QVector<double> > valuesData)
65 ExpectedResults &setValuesData(QVector<QVector<double> > valuesData)
66 {
66 {
67 m_ValuesData = std::move(valuesData);
67 m_ValuesData = std::move(valuesData);
68 return *this;
68 return *this;
69 }
69 }
70
70
71 ExpectedResults &setYAxisEnabled(bool yAxisEnabled)
71 ExpectedResults &setYAxisEnabled(bool yAxisEnabled)
72 {
72 {
73 m_YAxisEnabled = yAxisEnabled;
73 m_YAxisEnabled = yAxisEnabled;
74 return *this;
74 return *this;
75 }
75 }
76
76
77 ExpectedResults &setYAxisUnit(Unit yAxisUnit)
77 ExpectedResults &setYAxisUnit(Unit yAxisUnit)
78 {
78 {
79 m_YAxisUnit = std::move(yAxisUnit);
79 m_YAxisUnit = std::move(yAxisUnit);
80 return *this;
80 return *this;
81 }
81 }
82
82
83 ExpectedResults &setYAxisData(QVector<double> yAxisData)
83 ExpectedResults &setYAxisData(QVector<double> yAxisData)
84 {
84 {
85 m_YAxisData = std::move(yAxisData);
85 m_YAxisData = std::move(yAxisData);
86 return *this;
86 return *this;
87 }
87 }
88
88
89 /**
89 /**
90 * Validates a DataSeries compared to the expected results
90 * Validates a DataSeries compared to the expected results
91 * @param results the DataSeries to validate
91 * @param results the DataSeries to validate
92 */
92 */
93 void validate(std::shared_ptr<IDataSeries> results)
93 void validate(std::shared_ptr<IDataSeries> results)
94 {
94 {
95 if (m_ParsingOK) {
95 if (m_ParsingOK) {
96 auto dataSeries = dynamic_cast<T *>(results.get());
96 auto dataSeries = dynamic_cast<T *>(results.get());
97 if (dataSeries == nullptr) {
97 if (dataSeries == nullptr) {
98
98
99 // No unit detected, parsink ok but data is nullptr
99 // No unit detected, parsink ok but data is nullptr
100 // TODO, improve the test to verify that the data is null
100 // TODO, improve the test to verify that the data is null
101 return;
101 return;
102 }
102 }
103
103
104 // Checks units
104 // Checks units
105 QVERIFY(dataSeries->xAxisUnit() == m_XAxisUnit);
105 QVERIFY(dataSeries->xAxisUnit() == m_XAxisUnit);
106 QVERIFY(dataSeries->valuesUnit() == m_ValuesUnit);
106 QVERIFY(dataSeries->valuesUnit() == m_ValuesUnit);
107
107
108 auto verifyRange = [dataSeries](const auto &expectedData, const auto &equalFun) {
108 auto verifyRange = [dataSeries](const auto &expectedData, const auto &equalFun) {
109 QVERIFY(std::equal(dataSeries->cbegin(), dataSeries->cend(), expectedData.cbegin(),
109 QVERIFY(std::equal(dataSeries->cbegin(), dataSeries->cend(), expectedData.cbegin(),
110 expectedData.cend(),
110 expectedData.cend(),
111 [&equalFun](const auto &dataSeriesIt, const auto &expectedX) {
111 [&equalFun](const auto &dataSeriesIt, const auto &expectedX) {
112 return equalFun(dataSeriesIt, expectedX);
112 return equalFun(dataSeriesIt, expectedX);
113 }));
113 }));
114 };
114 };
115
115
116 // Checks x-axis data
116 // Checks x-axis data
117 verifyRange(m_XAxisData, [](const auto &seriesIt, const auto &value) {
117 verifyRange(m_XAxisData, [](const auto &seriesIt, const auto &value) {
118 return seriesIt.x() == value;
118 return seriesIt.x() == value;
119 });
119 });
120
120
121 // Checks values data of each component
121 // Checks values data of each component
122 for (auto i = 0; i < m_ValuesData.size(); ++i) {
122 for (auto i = 0; i < m_ValuesData.size(); ++i) {
123 verifyRange(m_ValuesData.at(i), [i](const auto &seriesIt, const auto &value) {
123 verifyRange(m_ValuesData.at(i), [i](const auto &seriesIt, const auto &value) {
124 auto itValue = seriesIt.value(i);
124 auto itValue = seriesIt.value(i);
125 return (std::isnan(itValue) && std::isnan(value)) || seriesIt.value(i) == value;
125 return (std::isnan(itValue) && std::isnan(value)) || seriesIt.value(i) == value;
126 });
126 });
127 }
127 }
128
128
129 // Checks y-axis (if defined)
129 // Checks y-axis (if defined)
130 auto yAxis = dataSeries->yAxis();
130 auto yAxis = dataSeries->yAxis();
131 QCOMPARE(yAxis.isDefined(), m_YAxisEnabled);
131 QCOMPARE(yAxis.isDefined(), m_YAxisEnabled);
132
132
133 if (m_YAxisEnabled) {
133 if (m_YAxisEnabled) {
134 // Unit
134 // Unit
135 QCOMPARE(yAxis.unit(), m_YAxisUnit);
135 QCOMPARE(yAxis.unit(), m_YAxisUnit);
136
136
137 // Data
137 // Data
138 QVERIFY(std::equal(yAxis.cbegin(), yAxis.cend(), m_YAxisData.cbegin(),
138 QVERIFY(std::equal(yAxis.cbegin(), yAxis.cend(), m_YAxisData.cbegin(),
139 m_YAxisData.cend(), [](const auto &it, const auto &expectedVal) {
139 m_YAxisData.cend(), [](const auto &it, const auto &expectedVal) {
140 return it.first() == expectedVal;
140 return it.first() == expectedVal;
141 }));
141 }));
142 }
142 }
143 }
143 }
144 else {
144 else {
145 QVERIFY(results == nullptr);
145 QVERIFY(results == nullptr);
146 }
146 }
147 }
147 }
148
148
149 // Parsing was successfully completed
149 // Parsing was successfully completed
150 bool m_ParsingOK{false};
150 bool m_ParsingOK{false};
151 // Expected x-axis unit
151 // Expected x-axis unit
152 Unit m_XAxisUnit{};
152 Unit m_XAxisUnit{};
153 // Expected x-axis data
153 // Expected x-axis data
154 QVector<double> m_XAxisData{};
154 QVector<double> m_XAxisData{};
155 // Expected values unit
155 // Expected values unit
156 Unit m_ValuesUnit{};
156 Unit m_ValuesUnit{};
157 // Expected values data
157 // Expected values data
158 QVector<QVector<double> > m_ValuesData{};
158 QVector<QVector<double> > m_ValuesData{};
159 // Expected data series has y-axis
159 // Expected data series has y-axis
160 bool m_YAxisEnabled{false};
160 bool m_YAxisEnabled{false};
161 // Expected y-axis unit (if axis defined)
161 // Expected y-axis unit (if axis defined)
162 Unit m_YAxisUnit{};
162 Unit m_YAxisUnit{};
163 // Expected y-axis data (if axis defined)
163 // Expected y-axis data (if axis defined)
164 QVector<double> m_YAxisData{};
164 QVector<double> m_YAxisData{};
165 };
165 };
166
166
167 } // namespace
167 } // namespace
168
168
169 Q_DECLARE_METATYPE(ExpectedResults<ScalarSeries>)
169 Q_DECLARE_METATYPE(ExpectedResults<ScalarSeries>)
170 Q_DECLARE_METATYPE(ExpectedResults<SpectrogramSeries>)
170 Q_DECLARE_METATYPE(ExpectedResults<SpectrogramSeries>)
171 Q_DECLARE_METATYPE(ExpectedResults<VectorSeries>)
171 Q_DECLARE_METATYPE(ExpectedResults<VectorSeries>)
172
172
173 class TestAmdaResultParser : public QObject {
173 class TestAmdaResultParser : public QObject {
174 Q_OBJECT
174 Q_OBJECT
175 private:
175 private:
176 template <typename T>
176 template <typename T>
177 void testReadDataStructure()
177 void testReadDataStructure()
178 {
178 {
179 // ////////////// //
179 // ////////////// //
180 // Test structure //
180 // Test structure //
181 // ////////////// //
181 // ////////////// //
182
182
183 // Name of TXT file to read
183 // Name of TXT file to read
184 QTest::addColumn<QString>("inputFileName");
184 QTest::addColumn<QString>("inputFileName");
185 // Expected results
185 // Expected results
186 QTest::addColumn<ExpectedResults<T> >("expectedResults");
186 QTest::addColumn<ExpectedResults<T> >("expectedResults");
187 }
187 }
188
188
189 template <typename T>
189 template <typename T>
190 void testRead(AmdaResultParser::ValueType valueType)
190 void testRead(AmdaResultParser::ValueType valueType)
191 {
191 {
192 QFETCH(QString, inputFileName);
192 QFETCH(QString, inputFileName);
193 QFETCH(ExpectedResults<T>, expectedResults);
193 QFETCH(ExpectedResults<T>, expectedResults);
194
194
195 // Parses file
195 // Parses file
196 auto filePath = inputFilePath(inputFileName);
196 auto filePath = inputFilePath(inputFileName);
197 auto results = AmdaResultParser::readTxt(filePath, valueType);
197 auto results = AmdaResultParser::readTxt(filePath, valueType);
198
198
199 // ///////////////// //
199 // ///////////////// //
200 // Validates results //
200 // Validates results //
201 // ///////////////// //
201 // ///////////////// //
202 expectedResults.validate(results);
202 expectedResults.validate(results);
203 }
203 }
204
204
205 private slots:
205 private slots:
206 /// Input test data
206 /// Input test data
207 /// @sa testReadScalarTxt()
207 /// @sa testReadScalarTxt()
208 void testReadScalarTxt_data();
208 void testReadScalarTxt_data();
209
209
210 /// Tests parsing scalar series of a TXT file
210 /// Tests parsing scalar series of a TXT file
211 void testReadScalarTxt();
211 void testReadScalarTxt();
212
212
213 /// Input test data
213 /// Input test data
214 /// @sa testReadSpectrogramTxt()
214 /// @sa testReadSpectrogramTxt()
215 void testReadSpectrogramTxt_data();
215 void testReadSpectrogramTxt_data();
216
216
217 /// Tests parsing spectrogram series of a TXT file
217 /// Tests parsing spectrogram series of a TXT file
218 void testReadSpectrogramTxt();
218 void testReadSpectrogramTxt();
219
219
220 /// Input test data
220 /// Input test data
221 /// @sa testReadVectorTxt()
221 /// @sa testReadVectorTxt()
222 void testReadVectorTxt_data();
222 void testReadVectorTxt_data();
223
223
224 /// Tests parsing vector series of a TXT file
224 /// Tests parsing vector series of a TXT file
225 void testReadVectorTxt();
225 void testReadVectorTxt();
226 };
226 };
227
227
228 void TestAmdaResultParser::testReadScalarTxt_data()
228 void TestAmdaResultParser::testReadScalarTxt_data()
229 {
229 {
230 testReadDataStructure<ScalarSeries>();
230 testReadDataStructure<ScalarSeries>();
231
231
232 // ////////// //
232 // ////////// //
233 // Test cases //
233 // Test cases //
234 // ////////// //
234 // ////////// //
235
235
236 // Valid files
236 // Valid files
237 QTest::newRow("Valid file")
237 QTest::newRow("Valid file")
238 << QStringLiteral("ValidScalar1.txt")
238 << QStringLiteral("ValidScalar1.txt")
239 << ExpectedResults<ScalarSeries>{}
239 << ExpectedResults<ScalarSeries>{}
240 .setParsingOK(true)
240 .setParsingOK(true)
241 .setXAxisUnit(Unit{"nT", true})
241 .setXAxisUnit(Unit{"nT", true})
242 .setXAxisData({dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
242 .setXAxisData({dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
243 dateTime(2013, 9, 23, 9, 2, 30), dateTime(2013, 9, 23, 9, 3, 30),
243 dateTime(2013, 9, 23, 9, 2, 30), dateTime(2013, 9, 23, 9, 3, 30),
244 dateTime(2013, 9, 23, 9, 4, 30), dateTime(2013, 9, 23, 9, 5, 30),
244 dateTime(2013, 9, 23, 9, 4, 30), dateTime(2013, 9, 23, 9, 5, 30),
245 dateTime(2013, 9, 23, 9, 6, 30), dateTime(2013, 9, 23, 9, 7, 30),
245 dateTime(2013, 9, 23, 9, 6, 30), dateTime(2013, 9, 23, 9, 7, 30),
246 dateTime(2013, 9, 23, 9, 8, 30), dateTime(2013, 9, 23, 9, 9, 30)})
246 dateTime(2013, 9, 23, 9, 8, 30), dateTime(2013, 9, 23, 9, 9, 30)})
247 .setValuesData({-2.83950, -2.71850, -2.52150, -2.57633, -2.58050, -2.48325, -2.63025,
247 .setValuesData({-2.83950, -2.71850, -2.52150, -2.57633, -2.58050, -2.48325, -2.63025,
248 -2.55800, -2.43250, -2.42200});
248 -2.55800, -2.43250, -2.42200});
249
249
250 QTest::newRow("Valid file (value of first line is invalid but it is converted to NaN")
250 QTest::newRow("Valid file (value of first line is invalid but it is converted to NaN")
251 << QStringLiteral("WrongValue.txt")
251 << QStringLiteral("WrongValue.txt")
252 << ExpectedResults<ScalarSeries>{}
252 << ExpectedResults<ScalarSeries>{}
253 .setParsingOK(true)
253 .setParsingOK(true)
254 .setXAxisUnit(Unit{"nT", true})
254 .setXAxisUnit(Unit{"nT", true})
255 .setXAxisData({dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
255 .setXAxisData({dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
256 dateTime(2013, 9, 23, 9, 2, 30)})
256 dateTime(2013, 9, 23, 9, 2, 30)})
257 .setValuesData({std::numeric_limits<double>::quiet_NaN(), -2.71850, -2.52150});
257 .setValuesData({std::numeric_limits<double>::quiet_NaN(), -2.71850, -2.52150});
258
258
259 QTest::newRow("Valid file that contains NaN values")
259 QTest::newRow("Valid file that contains NaN values")
260 << QStringLiteral("NaNValue.txt")
260 << QStringLiteral("NaNValue.txt")
261 << ExpectedResults<ScalarSeries>{}
261 << ExpectedResults<ScalarSeries>{}
262 .setParsingOK(true)
262 .setParsingOK(true)
263 .setXAxisUnit(Unit{("nT"), true})
263 .setXAxisUnit(Unit{("nT"), true})
264 .setXAxisData({dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
264 .setXAxisData({dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
265 dateTime(2013, 9, 23, 9, 2, 30)})
265 dateTime(2013, 9, 23, 9, 2, 30)})
266 .setValuesData({std::numeric_limits<double>::quiet_NaN(), -2.71850, -2.52150});
266 .setValuesData({std::numeric_limits<double>::quiet_NaN(), -2.71850, -2.52150});
267
267
268 // Valid files but with some invalid lines (wrong unit, wrong values, etc.)
268 // Valid files but with some invalid lines (wrong unit, wrong values, etc.)
269 QTest::newRow("No unit file")
269 QTest::newRow("No unit file")
270 << QStringLiteral("NoUnit.txt")
270 << QStringLiteral("NoUnit.txt")
271 << ExpectedResults<ScalarSeries>{}.setParsingOK(true).setXAxisUnit(Unit{"", true});
271 << ExpectedResults<ScalarSeries>{}.setParsingOK(true).setXAxisUnit(Unit{"", true});
272
272
273 QTest::newRow("Wrong unit file")
273 QTest::newRow("Wrong unit file")
274 << QStringLiteral("WrongUnit.txt")
274 << QStringLiteral("WrongUnit.txt")
275 << ExpectedResults<ScalarSeries>{}
275 << ExpectedResults<ScalarSeries>{}
276 .setParsingOK(true)
276 .setParsingOK(true)
277 .setXAxisUnit(Unit{"", true})
277 .setXAxisUnit(Unit{"", true})
278 .setXAxisData({dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
278 .setXAxisData({dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
279 dateTime(2013, 9, 23, 9, 2, 30)})
279 dateTime(2013, 9, 23, 9, 2, 30)})
280 .setValuesData({-2.83950, -2.71850, -2.52150});
280 .setValuesData({-2.83950, -2.71850, -2.52150});
281
281
282 QTest::newRow("Wrong results file (date of first line is invalid")
282 QTest::newRow("Wrong results file (date of first line is invalid")
283 << QStringLiteral("WrongDate.txt")
283 << QStringLiteral("WrongDate.txt")
284 << ExpectedResults<ScalarSeries>{}
284 << ExpectedResults<ScalarSeries>{}
285 .setParsingOK(true)
285 .setParsingOK(true)
286 .setXAxisUnit(Unit{"nT", true})
286 .setXAxisUnit(Unit{"nT", true})
287 .setXAxisData({dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)})
287 .setXAxisData({dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)})
288 .setValuesData({-2.71850, -2.52150});
288 .setValuesData({-2.71850, -2.52150});
289
289
290 QTest::newRow("Wrong results file (too many values for first line")
290 QTest::newRow("Wrong results file (too many values for first line")
291 << QStringLiteral("TooManyValues.txt")
291 << QStringLiteral("TooManyValues.txt")
292 << ExpectedResults<ScalarSeries>{}
292 << ExpectedResults<ScalarSeries>{}
293 .setParsingOK(true)
293 .setParsingOK(true)
294 .setXAxisUnit(Unit{"nT", true})
294 .setXAxisUnit(Unit{"nT", true})
295 .setXAxisData({dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)})
295 .setXAxisData({dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)})
296 .setValuesData({-2.71850, -2.52150});
296 .setValuesData({-2.71850, -2.52150});
297
297
298 QTest::newRow("Wrong results file (x of first line is NaN")
298 QTest::newRow("Wrong results file (x of first line is NaN")
299 << QStringLiteral("NaNX.txt")
299 << QStringLiteral("NaNX.txt")
300 << ExpectedResults<ScalarSeries>{}
300 << ExpectedResults<ScalarSeries>{}
301 .setParsingOK(true)
301 .setParsingOK(true)
302 .setXAxisUnit(Unit{"nT", true})
302 .setXAxisUnit(Unit{"nT", true})
303 .setXAxisData({dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)})
303 .setXAxisData({dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)})
304 .setValuesData({-2.71850, -2.52150});
304 .setValuesData({-2.71850, -2.52150});
305
305
306 QTest::newRow("Invalid file type (vector)")
306 QTest::newRow("Invalid file type (vector)")
307 << QStringLiteral("ValidVector1.txt")
307 << QStringLiteral("ValidVector1.txt")
308 << ExpectedResults<ScalarSeries>{}.setParsingOK(true).setXAxisUnit(Unit{"nT", true});
308 << ExpectedResults<ScalarSeries>{}.setParsingOK(true).setXAxisUnit(Unit{"nT", true});
309
309
310 // Invalid files
310 // Invalid files
311 QTest::newRow("Invalid file (unexisting file)")
311 QTest::newRow("Invalid file (unexisting file)")
312 << QStringLiteral("UnexistingFile.txt")
312 << QStringLiteral("UnexistingFile.txt")
313 << ExpectedResults<ScalarSeries>{}.setParsingOK(false);
313 << ExpectedResults<ScalarSeries>{}.setParsingOK(false);
314
314
315 QTest::newRow("Invalid file (file not found on server)")
315 QTest::newRow("Invalid file (file not found on server)")
316 << QStringLiteral("FileNotFound.txt")
316 << QStringLiteral("FileNotFound.txt")
317 << ExpectedResults<ScalarSeries>{}.setParsingOK(false);
317 << ExpectedResults<ScalarSeries>{}.setParsingOK(false);
318 }
318 }
319
319
320 void TestAmdaResultParser::testReadScalarTxt()
320 void TestAmdaResultParser::testReadScalarTxt()
321 {
321 {
322 testRead<ScalarSeries>(AmdaResultParser::ValueType::SCALAR);
322 testRead<ScalarSeries>(AmdaResultParser::ValueType::SCALAR);
323 }
323 }
324
324
325 void TestAmdaResultParser::testReadSpectrogramTxt_data()
325 void TestAmdaResultParser::testReadSpectrogramTxt_data()
326 {
326 {
327 testReadDataStructure<SpectrogramSeries>();
327 testReadDataStructure<SpectrogramSeries>();
328
328
329 // ////////// //
329 // ////////// //
330 // Test cases //
330 // Test cases //
331 // ////////// //
331 // ////////// //
332
332
333 // Valid files
333 // Valid files
334 QTest::newRow("Valid file (three bands)")
334 QTest::newRow("Valid file (three bands)")
335 << QStringLiteral("spectro/ValidSpectrogram1.txt")
335 << QStringLiteral("spectro/ValidSpectrogram1.txt")
336 << ExpectedResults<SpectrogramSeries>{}
336 << ExpectedResults<SpectrogramSeries>{}
337 .setParsingOK(true)
337 .setParsingOK(true)
338 .setXAxisUnit(Unit{"t", true})
338 .setXAxisUnit(Unit{"t", true})
339 .setXAxisData({dateTime(2012, 11, 6, 9, 14, 35), dateTime(2012, 11, 6, 9, 16, 10),
339 .setXAxisData({dateTime(2012, 11, 6, 9, 14, 35), dateTime(2012, 11, 6, 9, 16, 10),
340 dateTime(2012, 11, 6, 9, 17, 45), dateTime(2012, 11, 6, 9, 19, 20),
340 dateTime(2012, 11, 6, 9, 17, 45), dateTime(2012, 11, 6, 9, 19, 20),
341 dateTime(2012, 11, 6, 9, 20, 55)})
341 dateTime(2012, 11, 6, 9, 20, 55)})
342 .setYAxisEnabled(true)
342 .setYAxisEnabled(true)
343 .setYAxisUnit(Unit{"eV"})
343 .setYAxisUnit(Unit{"eV"})
344 .setYAxisData({5.75, 7.6, 10.05}) // middle of the intervals of each band
344 .setYAxisData({5.75, 7.6, 10.05}) // middle of the intervals of each band
345 .setValuesUnit(Unit{"eV/(cm^2-s-sr-eV)"})
345 .setValuesUnit(Unit{"eV/(cm^2-s-sr-eV)"})
346 .setValuesData(QVector<QVector<double> >{
346 .setValuesData(QVector<QVector<double> >{
347 {16313.780, 12631.465, 8223.368, 27595.301, 12820.613},
347 {16313.780, 12631.465, 8223.368, 27595.301, 12820.613},
348 {15405.838, 11957.925, 15026.249, 25617.533, 11179.109},
348 {15405.838, 11957.925, 15026.249, 25617.533, 11179.109},
349 {8946.475, 18133.158, 10875.621, 24051.619, 19283.221}});
349 {8946.475, 18133.158, 10875.621, 24051.619, 19283.221}});
350
350
351 auto fourBandsResult
351 auto fourBandsResult
352 = ExpectedResults<SpectrogramSeries>{}
352 = ExpectedResults<SpectrogramSeries>{}
353 .setParsingOK(true)
353 .setParsingOK(true)
354 .setXAxisUnit(Unit{"t", true})
354 .setXAxisUnit(Unit{"t", true})
355 .setXAxisData({dateTime(2012, 11, 6, 9, 14, 35), dateTime(2012, 11, 6, 9, 16, 10),
355 .setXAxisData({dateTime(2012, 11, 6, 9, 14, 35), dateTime(2012, 11, 6, 9, 16, 10),
356 dateTime(2012, 11, 6, 9, 17, 45), dateTime(2012, 11, 6, 9, 19, 20),
356 dateTime(2012, 11, 6, 9, 17, 45), dateTime(2012, 11, 6, 9, 19, 20),
357 dateTime(2012, 11, 6, 9, 20, 55)})
357 dateTime(2012, 11, 6, 9, 20, 55)})
358 .setYAxisEnabled(true)
358 .setYAxisEnabled(true)
359 .setYAxisUnit(Unit{"eV"})
359 .setYAxisUnit(Unit{"eV"})
360 .setYAxisData({5.75, 7.6, 10.05, 13.}) // middle of the intervals of each band
360 .setYAxisData({5.75, 7.6, 10.05, 13.}) // middle of the intervals of each band
361 .setValuesUnit(Unit{"eV/(cm^2-s-sr-eV)"})
361 .setValuesUnit(Unit{"eV/(cm^2-s-sr-eV)"})
362 .setValuesData(QVector<QVector<double> >{
362 .setValuesData(QVector<QVector<double> >{
363 {16313.780, 12631.465, 8223.368, 27595.301, 12820.613},
363 {16313.780, 12631.465, 8223.368, 27595.301, 12820.613},
364 {15405.838, 11957.925, 15026.249, 25617.533, 11179.109},
364 {15405.838, 11957.925, 15026.249, 25617.533, 11179.109},
365 {8946.475, 18133.158, 10875.621, 24051.619, 19283.221},
365 {8946.475, 18133.158, 10875.621, 24051.619, 19283.221},
366 {20907.664, 32076.725, 13008.381, 13142.759, 23226.998}});
366 {20907.664, 32076.725, 13008.381, 13142.759, 23226.998}});
367
367
368 QTest::newRow("Valid file (four bands)")
368 QTest::newRow("Valid file (four bands)") << QStringLiteral("spectro/ValidSpectrogram2.txt")
369 << QStringLiteral("spectro/ValidSpectrogram2.txt") << fourBandsResult;
369 << fourBandsResult;
370 QTest::newRow("Valid file (four unsorted bands)")
370 QTest::newRow("Valid file (four unsorted bands)")
371 << QStringLiteral("spectro/ValidSpectrogram3.txt")
371 << QStringLiteral("spectro/ValidSpectrogram3.txt")
372 << fourBandsResult; // Bands and values are sorted
372 << fourBandsResult; // Bands and values are sorted
373
373
374 auto nan = std::numeric_limits<double>::quiet_NaN();
374 auto nan = std::numeric_limits<double>::quiet_NaN();
375
375
376 auto nanValuesResult
376 auto nanValuesResult
377 = ExpectedResults<SpectrogramSeries>{}
377 = ExpectedResults<SpectrogramSeries>{}
378 .setParsingOK(true)
378 .setParsingOK(true)
379 .setXAxisUnit(Unit{"t", true})
379 .setXAxisUnit(Unit{"t", true})
380 .setXAxisData({dateTime(2012, 11, 6, 9, 14, 35), dateTime(2012, 11, 6, 9, 16, 10),
380 .setXAxisData({dateTime(2012, 11, 6, 9, 14, 35), dateTime(2012, 11, 6, 9, 16, 10),
381 dateTime(2012, 11, 6, 9, 17, 45), dateTime(2012, 11, 6, 9, 19, 20),
381 dateTime(2012, 11, 6, 9, 17, 45), dateTime(2012, 11, 6, 9, 19, 20),
382 dateTime(2012, 11, 6, 9, 20, 55)})
382 dateTime(2012, 11, 6, 9, 20, 55)})
383 .setYAxisEnabled(true)
383 .setYAxisEnabled(true)
384 .setYAxisUnit(Unit{"eV"})
384 .setYAxisUnit(Unit{"eV"})
385 .setYAxisData({5.75, 7.6, 10.05, 13.}) // middle of the intervals of each band
385 .setYAxisData({5.75, 7.6, 10.05, 13.}) // middle of the intervals of each band
386 .setValuesUnit(Unit{"eV/(cm^2-s-sr-eV)"})
386 .setValuesUnit(Unit{"eV/(cm^2-s-sr-eV)"})
387 .setValuesData(
387 .setValuesData(
388 QVector<QVector<double> >{{nan, 12631.465, 8223.368, 27595.301, 12820.613},
388 QVector<QVector<double> >{{nan, 12631.465, 8223.368, 27595.301, 12820.613},
389 {15405.838, nan, nan, 25617.533, 11179.109},
389 {15405.838, nan, nan, 25617.533, 11179.109},
390 {8946.475, 18133.158, 10875.621, 24051.619, 19283.221},
390 {8946.475, 18133.158, 10875.621, 24051.619, 19283.221},
391 {nan, nan, nan, nan, nan}});
391 {nan, nan, nan, nan, nan}});
392
392
393 QTest::newRow("Valid file (containing NaN values)")
393 QTest::newRow("Valid file (containing NaN values)")
394 << QStringLiteral("spectro/ValidSpectrogramNaNValues.txt") << nanValuesResult;
394 << QStringLiteral("spectro/ValidSpectrogramNaNValues.txt") << nanValuesResult;
395 QTest::newRow("Valid file (containing fill values)")
395 QTest::newRow("Valid file (containing fill values)")
396 << QStringLiteral("spectro/ValidSpectrogramFillValues.txt")
396 << QStringLiteral("spectro/ValidSpectrogramFillValues.txt")
397 << nanValuesResult; // Fill values are replaced by NaN values in the data series
397 << nanValuesResult; // Fill values are replaced by NaN values in the data series
398
398
399 QTest::newRow("Valid file (containing data holes, resolution = 3 minutes)")
399 QTest::newRow("Valid file (containing data holes, resolution = 3 minutes)")
400 << QStringLiteral("spectro/ValidSpectrogramDataHoles.txt")
400 << QStringLiteral("spectro/ValidSpectrogramDataHoles.txt")
401 << ExpectedResults<SpectrogramSeries>{}
401 << ExpectedResults<SpectrogramSeries>{}
402 .setParsingOK(true)
402 .setParsingOK(true)
403 .setXAxisUnit(Unit{"t", true})
403 .setXAxisUnit(Unit{"t", true})
404 .setXAxisData({dateTime(2011, 12, 10, 12, 10, 54), //
404 .setXAxisData({dateTime(2011, 12, 10, 12, 10, 54), //
405 dateTime(2011, 12, 10, 12, 13, 54), // Data hole
405 dateTime(2011, 12, 10, 12, 13, 54), // Data hole
406 dateTime(2011, 12, 10, 12, 16, 54), // Data hole
406 dateTime(2011, 12, 10, 12, 16, 54), // Data hole
407 dateTime(2011, 12, 10, 12, 17, 23), //
407 dateTime(2011, 12, 10, 12, 17, 23), //
408 dateTime(2011, 12, 10, 12, 20, 23), // Data hole
408 dateTime(2011, 12, 10, 12, 20, 23), // Data hole
409 dateTime(2011, 12, 10, 12, 23, 23), // Data hole
409 dateTime(2011, 12, 10, 12, 23, 23), // Data hole
410 dateTime(2011, 12, 10, 12, 23, 51), //
410 dateTime(2011, 12, 10, 12, 23, 51), //
411 dateTime(2011, 12, 10, 12, 26, 51), // Data hole
411 dateTime(2011, 12, 10, 12, 26, 51), // Data hole
412 dateTime(2011, 12, 10, 12, 29, 51), // Data hole
412 dateTime(2011, 12, 10, 12, 29, 51), // Data hole
413 dateTime(2011, 12, 10, 12, 30, 19), //
413 dateTime(2011, 12, 10, 12, 30, 19), //
414 dateTime(2011, 12, 10, 12, 33, 19), // Data hole
414 dateTime(2011, 12, 10, 12, 33, 19), // Data hole
415 dateTime(2011, 12, 10, 12, 35, 04), //
415 dateTime(2011, 12, 10, 12, 35, 04), //
416 dateTime(2011, 12, 10, 12, 36, 41), //
416 dateTime(2011, 12, 10, 12, 36, 41), //
417 dateTime(2011, 12, 10, 12, 38, 18), //
417 dateTime(2011, 12, 10, 12, 38, 18), //
418 dateTime(2011, 12, 10, 12, 39, 55)})
418 dateTime(2011, 12, 10, 12, 39, 55)})
419 .setYAxisEnabled(true)
419 .setYAxisEnabled(true)
420 .setYAxisUnit(Unit{"eV"})
420 .setYAxisUnit(Unit{"eV"})
421 .setYAxisData({16485.85, 20996.1}) // middle of the intervals of each band
421 .setYAxisData({16485.85, 20996.1}) // middle of the intervals of each band
422 .setValuesUnit(Unit{"eV/(cm^2-s-sr-eV)"})
422 .setValuesUnit(Unit{"eV/(cm^2-s-sr-eV)"})
423 .setValuesData(QVector<QVector<double> >{{2577578.000, //
423 .setValuesData(QVector<QVector<double> >{{2577578.000, //
424 nan, // Data hole
424 nan, // Data hole
425 nan, // Data hole
425 nan, // Data hole
426 2314121.500, //
426 2314121.500, //
427 nan, // Data hole
427 nan, // Data hole
428 nan, // Data hole
428 nan, // Data hole
429 2063608.750, //
429 2063608.750, //
430 nan, // Data hole
430 nan, // Data hole
431 nan, // Data hole
431 nan, // Data hole
432 2234525.500, //
432 2234525.500, //
433 nan, // Data hole
433 nan, // Data hole
434 1670215.250, //
434 1670215.250, //
435 1689243.250, //
435 1689243.250, //
436 1654617.125, //
436 1654617.125, //
437 1504983.750},
437 1504983.750},
438 {2336016.000, //
438 {2336016.000, //
439 nan, // Data hole
439 nan, // Data hole
440 nan, // Data hole
440 nan, // Data hole
441 1712093.125, //
441 1712093.125, //
442 nan, // Data hole
442 nan, // Data hole
443 nan, // Data hole
443 nan, // Data hole
444 1614491.625, //
444 1614491.625, //
445 nan, // Data hole
445 nan, // Data hole
446 nan, // Data hole
446 nan, // Data hole
447 1764516.500, //
447 1764516.500, //
448 nan, // Data hole
448 nan, // Data hole
449 1688078.500, //
449 1688078.500, //
450 1743183.500, //
450 1743183.500, //
451 1733603.250, //
451 1733603.250, //
452 1708356.500}});
452 1708356.500}});
453
453
454 QTest::newRow(
454 QTest::newRow(
455 "Valid file (containing data holes at the beginning and the end, resolution = 4 minutes)")
455 "Valid file (containing data holes at the beginning and the end, resolution = 4 minutes)")
456 << QStringLiteral("spectro/ValidSpectrogramDataHoles2.txt")
456 << QStringLiteral("spectro/ValidSpectrogramDataHoles2.txt")
457 << ExpectedResults<SpectrogramSeries>{}
457 << ExpectedResults<SpectrogramSeries>{}
458 .setParsingOK(true)
458 .setParsingOK(true)
459 .setXAxisUnit(Unit{"t", true})
459 .setXAxisUnit(Unit{"t", true})
460 .setXAxisData({
460 .setXAxisData({
461 dateTime(2011, 12, 10, 12, 2, 54), // Data hole
461 dateTime(2011, 12, 10, 12, 2, 54), // Data hole
462 dateTime(2011, 12, 10, 12, 6, 54), // Data hole
462 dateTime(2011, 12, 10, 12, 6, 54), // Data hole
463 dateTime(2011, 12, 10, 12, 10, 54), //
463 dateTime(2011, 12, 10, 12, 10, 54), //
464 dateTime(2011, 12, 10, 12, 14, 54), // Data hole
464 dateTime(2011, 12, 10, 12, 14, 54), // Data hole
465 dateTime(2011, 12, 10, 12, 17, 23), //
465 dateTime(2011, 12, 10, 12, 17, 23), //
466 dateTime(2011, 12, 10, 12, 21, 23), // Data hole
466 dateTime(2011, 12, 10, 12, 21, 23), // Data hole
467 dateTime(2011, 12, 10, 12, 23, 51), //
467 dateTime(2011, 12, 10, 12, 23, 51), //
468 dateTime(2011, 12, 10, 12, 27, 51), // Data hole
468 dateTime(2011, 12, 10, 12, 27, 51), // Data hole
469 dateTime(2011, 12, 10, 12, 30, 19), //
469 dateTime(2011, 12, 10, 12, 30, 19), //
470 dateTime(2011, 12, 10, 12, 34, 19), // Data hole
470 dateTime(2011, 12, 10, 12, 34, 19), // Data hole
471 dateTime(2011, 12, 10, 12, 35, 04), //
471 dateTime(2011, 12, 10, 12, 35, 04), //
472 dateTime(2011, 12, 10, 12, 36, 41), //
472 dateTime(2011, 12, 10, 12, 36, 41), //
473 dateTime(2011, 12, 10, 12, 38, 18), //
473 dateTime(2011, 12, 10, 12, 38, 18), //
474 dateTime(2011, 12, 10, 12, 39, 55),
474 dateTime(2011, 12, 10, 12, 39, 55),
475 dateTime(2011, 12, 10, 12, 43, 55), // Data hole
475 dateTime(2011, 12, 10, 12, 43, 55), // Data hole
476 dateTime(2011, 12, 10, 12, 47, 55), // Data hole
476 dateTime(2011, 12, 10, 12, 47, 55), // Data hole
477 dateTime(2011, 12, 10, 12, 51, 55), // Data hole
477 dateTime(2011, 12, 10, 12, 51, 55), // Data hole
478 dateTime(2011, 12, 10, 12, 55, 55), // Data hole
478 dateTime(2011, 12, 10, 12, 55, 55), // Data hole
479 dateTime(2011, 12, 10, 12, 59, 55) // Data hole
479 dateTime(2011, 12, 10, 12, 59, 55) // Data hole
480 })
480 })
481 .setYAxisEnabled(true)
481 .setYAxisEnabled(true)
482 .setYAxisUnit(Unit{"eV"})
482 .setYAxisUnit(Unit{"eV"})
483 .setYAxisData({16485.85, 20996.1}) // middle of the intervals of each band
483 .setYAxisData({16485.85, 20996.1}) // middle of the intervals of each band
484 .setValuesUnit(Unit{"eV/(cm^2-s-sr-eV)"})
484 .setValuesUnit(Unit{"eV/(cm^2-s-sr-eV)"})
485 .setValuesData(QVector<QVector<double> >{{
485 .setValuesData(QVector<QVector<double> >{{
486 nan, // Data hole
486 nan, // Data hole
487 nan, // Data hole
487 nan, // Data hole
488 2577578.000, //
488 2577578.000, //
489 nan, // Data hole
489 nan, // Data hole
490 2314121.500, //
490 2314121.500, //
491 nan, // Data hole
491 nan, // Data hole
492 2063608.750, //
492 2063608.750, //
493 nan, // Data hole
493 nan, // Data hole
494 2234525.500, //
494 2234525.500, //
495 nan, // Data hole
495 nan, // Data hole
496 1670215.250, //
496 1670215.250, //
497 1689243.250, //
497 1689243.250, //
498 1654617.125, //
498 1654617.125, //
499 1504983.750, //
499 1504983.750, //
500 nan, // Data hole
500 nan, // Data hole
501 nan, // Data hole
501 nan, // Data hole
502 nan, // Data hole
502 nan, // Data hole
503 nan, // Data hole
503 nan, // Data hole
504 nan // Data hole
504 nan // Data hole
505 },
505 },
506 {
506 {
507 nan, // Data hole
507 nan, // Data hole
508 nan, // Data hole
508 nan, // Data hole
509 2336016.000, //
509 2336016.000, //
510 nan, // Data hole
510 nan, // Data hole
511 1712093.125, //
511 1712093.125, //
512 nan, // Data hole
512 nan, // Data hole
513 1614491.625, //
513 1614491.625, //
514 nan, // Data hole
514 nan, // Data hole
515 1764516.500, //
515 1764516.500, //
516 nan, // Data hole
516 nan, // Data hole
517 1688078.500, //
517 1688078.500, //
518 1743183.500, //
518 1743183.500, //
519 1733603.250, //
519 1733603.250, //
520 1708356.500, //
520 1708356.500, //
521 nan, // Data hole
521 nan, // Data hole
522 nan, // Data hole
522 nan, // Data hole
523 nan, // Data hole
523 nan, // Data hole
524 nan, // Data hole
524 nan, // Data hole
525 nan // Data hole
525 nan // Data hole
526 }});
526 }});
527
527
528 // Invalid files
528 // Invalid files
529 QTest::newRow("Invalid file (inconsistent bands)")
529 QTest::newRow("Invalid file (inconsistent bands)")
530 << QStringLiteral("spectro/InvalidSpectrogramWrongBands.txt")
530 << QStringLiteral("spectro/InvalidSpectrogramWrongBands.txt")
531 << ExpectedResults<SpectrogramSeries>{}.setParsingOK(false);
531 << ExpectedResults<SpectrogramSeries>{}.setParsingOK(false);
532 }
532 }
533
533
534 void TestAmdaResultParser::testReadSpectrogramTxt()
534 void TestAmdaResultParser::testReadSpectrogramTxt()
535 {
535 {
536 testRead<SpectrogramSeries>(AmdaResultParser::ValueType::SPECTROGRAM);
536 testRead<SpectrogramSeries>(AmdaResultParser::ValueType::SPECTROGRAM);
537 }
537 }
538
538
539 void TestAmdaResultParser::testReadVectorTxt_data()
539 void TestAmdaResultParser::testReadVectorTxt_data()
540 {
540 {
541 testReadDataStructure<VectorSeries>();
541 testReadDataStructure<VectorSeries>();
542
542
543 // ////////// //
543 // ////////// //
544 // Test cases //
544 // Test cases //
545 // ////////// //
545 // ////////// //
546
546
547 // Valid files
547 // Valid files
548 QTest::newRow("Valid file")
548 QTest::newRow("Valid file")
549 << QStringLiteral("ValidVector1.txt")
549 << QStringLiteral("ValidVector1.txt")
550 << ExpectedResults<VectorSeries>{}
550 << ExpectedResults<VectorSeries>{}
551 .setParsingOK(true)
551 .setParsingOK(true)
552 .setXAxisUnit(Unit{"nT", true})
552 .setXAxisUnit(Unit{"nT", true})
553 .setXAxisData({dateTime(2013, 7, 2, 9, 13, 50), dateTime(2013, 7, 2, 9, 14, 6),
553 .setXAxisData({dateTime(2013, 7, 2, 9, 13, 50), dateTime(2013, 7, 2, 9, 14, 6),
554 dateTime(2013, 7, 2, 9, 14, 22), dateTime(2013, 7, 2, 9, 14, 38),
554 dateTime(2013, 7, 2, 9, 14, 22), dateTime(2013, 7, 2, 9, 14, 38),
555 dateTime(2013, 7, 2, 9, 14, 54), dateTime(2013, 7, 2, 9, 15, 10),
555 dateTime(2013, 7, 2, 9, 14, 54), dateTime(2013, 7, 2, 9, 15, 10),
556 dateTime(2013, 7, 2, 9, 15, 26), dateTime(2013, 7, 2, 9, 15, 42),
556 dateTime(2013, 7, 2, 9, 15, 26), dateTime(2013, 7, 2, 9, 15, 42),
557 dateTime(2013, 7, 2, 9, 15, 58), dateTime(2013, 7, 2, 9, 16, 14)})
557 dateTime(2013, 7, 2, 9, 15, 58), dateTime(2013, 7, 2, 9, 16, 14)})
558 .setValuesData(
558 .setValuesData(
559 {{-0.332, -1.011, -1.457, -1.293, -1.217, -1.443, -1.278, -1.202, -1.22, -1.259},
559 {{-0.332, -1.011, -1.457, -1.293, -1.217, -1.443, -1.278, -1.202, -1.22, -1.259},
560 {3.206, 2.999, 2.785, 2.736, 2.612, 2.564, 2.892, 2.862, 2.859, 2.764},
560 {3.206, 2.999, 2.785, 2.736, 2.612, 2.564, 2.892, 2.862, 2.859, 2.764},
561 {0.058, 0.496, 1.018, 1.485, 1.662, 1.505, 1.168, 1.244, 1.15, 1.358}});
561 {0.058, 0.496, 1.018, 1.485, 1.662, 1.505, 1.168, 1.244, 1.15, 1.358}});
562
562
563 // Valid files but with some invalid lines (wrong unit, wrong values, etc.)
563 // Valid files but with some invalid lines (wrong unit, wrong values, etc.)
564 QTest::newRow("Invalid file type (scalar)")
564 QTest::newRow("Invalid file type (scalar)")
565 << QStringLiteral("ValidScalar1.txt")
565 << QStringLiteral("ValidScalar1.txt")
566 << ExpectedResults<VectorSeries>{}
566 << ExpectedResults<VectorSeries>{}
567 .setParsingOK(true)
567 .setParsingOK(true)
568 .setXAxisUnit(Unit{"nT", true})
568 .setXAxisUnit(Unit{"nT", true})
569 .setXAxisData({})
569 .setXAxisData({})
570 .setValuesData(QVector<QVector<double> >{{}, {}, {}});
570 .setValuesData(QVector<QVector<double> >{{}, {}, {}});
571 }
571 }
572
572
573 void TestAmdaResultParser::testReadVectorTxt()
573 void TestAmdaResultParser::testReadVectorTxt()
574 {
574 {
575 testRead<VectorSeries>(AmdaResultParser::ValueType::VECTOR);
575 testRead<VectorSeries>(AmdaResultParser::ValueType::VECTOR);
576 }
576 }
577
577
578 QTEST_MAIN(TestAmdaResultParser)
578 QTEST_MAIN(TestAmdaResultParser)
579 #include "TestAmdaResultParser.moc"
579 #include "TestAmdaResultParser.moc"
General Comments 0
You need to be logged in to leave comments. Login now