##// END OF EJS Templates
Merge pull request 196 from SCIQLOP-Initialisation develop...
perrinel -
r495:f81e8612a847 merge
parent child
Show More
@@ -0,0 +1,19
1 #ifndef SCIQLOP_DATEUTILS_H
2 #define SCIQLOP_DATEUTILS_H
3
4 #include "CoreGlobal.h"
5
6 #include <QDateTime>
7
8 /**
9 * Utility class with methods for dates
10 */
11 struct SCIQLOP_CORE_EXPORT DateUtils {
12 /// Converts seconds (since epoch) to datetime. By default, the datetime is in UTC
13 static QDateTime dateTime(double secs, Qt::TimeSpec timeSpec = Qt::UTC) noexcept;
14
15 /// Converts datetime to seconds since epoch
16 static double secondsSinceEpoch(const QDateTime &dateTime) noexcept;
17 };
18
19 #endif // SCIQLOP_DATEUTILS_H
@@ -0,0 +1,13
1 #include "Common/DateUtils.h"
2
3 QDateTime DateUtils::dateTime(double secs, Qt::TimeSpec timeSpec) noexcept
4 {
5 // Uses msecs to be Qt 4 compatible
6 return QDateTime::fromMSecsSinceEpoch(secs * 1000., timeSpec);
7 }
8
9 double DateUtils::secondsSinceEpoch(const QDateTime &dateTime) noexcept
10 {
11 // Uses msecs to be Qt 4 compatible
12 return dateTime.toMSecsSinceEpoch() / 1000.;
13 }
@@ -0,0 +1,20
1 #ifndef SCIQLOP_VISUALIZATIONGRAPHRENDERINGDELEGATE_H
2 #define SCIQLOP_VISUALIZATIONGRAPHRENDERINGDELEGATE_H
3
4 #include <Common/spimpl.h>
5
6 class QCustomPlot;
7 class QMouseEvent;
8
9 class VisualizationGraphRenderingDelegate {
10 public:
11 explicit VisualizationGraphRenderingDelegate(QCustomPlot &plot);
12
13 void onMouseMove(QMouseEvent *event) noexcept;
14
15 private:
16 class VisualizationGraphRenderingDelegatePrivate;
17 spimpl::unique_impl_ptr<VisualizationGraphRenderingDelegatePrivate> impl;
18 };
19
20 #endif // SCIQLOP_VISUALIZATIONGRAPHRENDERINGDELEGATE_H
@@ -0,0 +1,123
1 #include "Visualization/VisualizationGraphRenderingDelegate.h"
2 #include "Visualization/qcustomplot.h"
3
4 #include <Common/DateUtils.h>
5
6 namespace {
7
8 const auto DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd hh:mm:ss:zzz");
9
10 const auto TEXT_TRACER_FORMAT = QStringLiteral("key: %1\nvalue: %2");
11
12 /// Timeout after which a tracer is displayed
13 const auto TRACER_TIMEOUT = 500;
14
15 /// Formats a data value according to the axis on which it is present
16 QString formatValue(double value, const QCPAxis &axis)
17 {
18 // If the axis is a time axis, formats the value as a date
19 if (auto axisTicker = qSharedPointerDynamicCast<QCPAxisTickerDateTime>(axis.ticker())) {
20 return DateUtils::dateTime(value, axisTicker->dateTimeSpec()).toString(DATETIME_FORMAT);
21 }
22 else {
23 return QString::number(value);
24 }
25 }
26
27 void initPointTracerStyle(QCPItemTracer &tracer) noexcept
28 {
29 tracer.setInterpolating(false);
30 tracer.setStyle(QCPItemTracer::tsPlus);
31 tracer.setPen(QPen(Qt::black));
32 tracer.setBrush(Qt::black);
33 tracer.setSize(10);
34 }
35
36 void initTextTracerStyle(QCPItemText &tracer) noexcept
37 {
38 tracer.setPen(QPen{Qt::gray});
39 tracer.setBrush(Qt::white);
40 tracer.setPadding(QMargins{6, 6, 6, 6});
41 tracer.setPositionAlignment(Qt::AlignTop | Qt::AlignLeft);
42 tracer.setTextAlignment(Qt::AlignLeft);
43 }
44
45 } // namespace
46
47 struct VisualizationGraphRenderingDelegate::VisualizationGraphRenderingDelegatePrivate {
48 explicit VisualizationGraphRenderingDelegatePrivate(QCustomPlot &plot)
49 : m_Plot{plot},
50 m_PointTracer{new QCPItemTracer{&plot}},
51 m_TextTracer{new QCPItemText{&plot}},
52 m_TracerTimer{}
53 {
54 initPointTracerStyle(*m_PointTracer);
55 initTextTracerStyle(*m_TextTracer);
56
57 m_TracerTimer.setInterval(TRACER_TIMEOUT);
58 m_TracerTimer.setSingleShot(true);
59 }
60
61 QCustomPlot &m_Plot;
62 QCPItemTracer *m_PointTracer;
63 QCPItemText *m_TextTracer;
64 QTimer m_TracerTimer;
65 };
66
67 VisualizationGraphRenderingDelegate::VisualizationGraphRenderingDelegate(QCustomPlot &plot)
68 : impl{spimpl::make_unique_impl<VisualizationGraphRenderingDelegatePrivate>(plot)}
69 {
70 }
71
72 void VisualizationGraphRenderingDelegate::onMouseMove(QMouseEvent *event) noexcept
73 {
74 // Cancels pending refresh
75 impl->m_TracerTimer.disconnect();
76
77 auto showTracers = [ eventPos = event->pos(), this ]()
78 {
79 // Lambda function to display a tracer
80 auto displayTracer = [this](auto &tracer) {
81 // Tracer is set on top of the plot's main layer
82 tracer.setLayer(impl->m_Plot.layer("main"));
83 tracer.setVisible(true);
84 impl->m_Plot.replot();
85 };
86
87 // Reinits tracers
88 impl->m_PointTracer->setGraph(nullptr);
89 impl->m_PointTracer->setVisible(false);
90 impl->m_TextTracer->setVisible(false);
91 impl->m_Plot.replot();
92
93 // Gets the graph under the mouse position
94 if (auto graph = qobject_cast<QCPGraph *>(impl->m_Plot.plottableAt(eventPos))) {
95 auto mouseKey = graph->keyAxis()->pixelToCoord(eventPos.x());
96 auto graphData = graph->data();
97
98 // Gets the closest data point to the mouse
99 auto graphDataIt = graphData->findBegin(mouseKey);
100 if (graphDataIt != graphData->constEnd()) {
101 auto key = formatValue(graphDataIt->key, *graph->keyAxis());
102 auto value = formatValue(graphDataIt->value, *graph->valueAxis());
103 impl->m_TextTracer->setText(TEXT_TRACER_FORMAT.arg(key, value));
104
105 // Displays point tracer
106 impl->m_PointTracer->setGraph(graph);
107 impl->m_PointTracer->setGraphKey(graphDataIt->key);
108 displayTracer(*impl->m_PointTracer);
109
110 // Displays text tracer
111 auto tracerPosition = impl->m_TextTracer->position;
112 tracerPosition->setAxes(graph->keyAxis(), graph->valueAxis());
113 tracerPosition->setCoords(impl->m_PointTracer->position->key(),
114 impl->m_PointTracer->position->value());
115 displayTracer(*impl->m_TextTracer);
116 }
117 }
118 };
119
120 // Starts the timer to display tracers at timeout
121 QObject::connect(&impl->m_TracerTimer, &QTimer::timeout, showTracers);
122 impl->m_TracerTimer.start();
123 }
@@ -36,7 +36,6
36 36
37 37 #include <QAction>
38 38 #include <QDate>
39 #include <QDateTime>
40 39 #include <QDir>
41 40 #include <QFileDialog>
42 41 #include <QToolBar>
@@ -23,7 +23,7 struct Unit {
23 23 inline bool operator!=(const Unit &other) const { return !(*this == other); }
24 24
25 25 QString m_Name; ///< Unit name
26 bool m_TimeUnit; ///< The unit is a unit of time
26 bool m_TimeUnit; ///< The unit is a unit of time (UTC)
27 27 };
28 28
29 29 /**
@@ -3,18 +3,18
3 3
4 4 #include <QObject>
5 5
6 #include <QDateTime>
7 6 #include <QDebug>
8 7
8 #include <Common/DateUtils.h>
9 9 #include <Common/MetaTypes.h>
10 10
11 11 /**
12 12 * @brief The SqpDateTime struct holds the information of time parameters
13 13 */
14 14 struct SqpDateTime {
15 /// Start time
15 /// Start time (UTC)
16 16 double m_TStart;
17 /// End time
17 /// End time (UTC)
18 18 double m_TEnd;
19 19
20 20 bool contains(const SqpDateTime &dateTime) const noexcept
@@ -30,10 +30,9 struct SqpDateTime {
30 30
31 31 inline QDebug operator<<(QDebug d, SqpDateTime obj)
32 32 {
33 auto tendDateTimeStart = QDateTime::fromMSecsSinceEpoch(obj.m_TStart * 1000);
34 auto tendDateTimeEnd = QDateTime::fromMSecsSinceEpoch(obj.m_TEnd * 1000);
33 auto tendDateTimeStart = DateUtils::dateTime(obj.m_TStart);
34 auto tendDateTimeEnd = DateUtils::dateTime(obj.m_TEnd);
35 35
36 // QDebug << "ts: " << tendDateTimeStart << " te: " << tendDateTimeEnd;
37 36 d << "ts: " << tendDateTimeStart << " te: " << tendDateTimeEnd;
38 37 return d;
39 38 }
@@ -8,7 +8,6
8 8 #include <Data/IDataSeries.h>
9 9 #include <Time/TimeController.h>
10 10
11 #include <QDateTime>
12 11 #include <QMutex>
13 12 #include <QThread>
14 13 #include <QUuid>
@@ -1,9 +1,10
1 1 #include <Variable/Variable.h>
2 2 #include <Variable/VariableModel.h>
3 3
4 #include <Common/DateUtils.h>
5
4 6 #include <Data/IDataSeries.h>
5 7
6 #include <QDateTime>
7 8 #include <QSize>
8 9 #include <unordered_map>
9 10
@@ -150,8 +151,8 QVariant VariableModel::data(const QModelIndex &index, int role) const
150 151 if (role == Qt::DisplayRole) {
151 152 if (auto variable = impl->m_Variables.at(index.row()).get()) {
152 153 /// Lambda function that builds the variant to return for a time value
153 auto dateTimeVariant = [](double time) {
154 auto dateTime = QDateTime::fromMSecsSinceEpoch(time * 1000.);
154 auto dateTimeVariant = [](double secs) {
155 auto dateTime = DateUtils::dateTime(secs);
155 156 return dateTime.toString(DATETIME_FORMAT);
156 157 };
157 158
@@ -69,6 +69,8 private slots:
69 69 /// Rescale the X axe to range parameter
70 70 void onRangeChanged(const QCPRange &t1, const QCPRange &t2);
71 71
72 /// Slot called when a mouse move was made
73 void onMouseMove(QMouseEvent *event) noexcept;
72 74 /// Slot called when a mouse wheel was made, to perform some processing before the zoom is done
73 75 void onMouseWheel(QWheelEvent *event) noexcept;
74 76 /// Slot called when a mouse press was made, to activate the calibration of a graph
@@ -1,6 +1,7
1 1 #include "TimeWidget/TimeWidget.h"
2 2 #include "ui_TimeWidget.h"
3 3
4 #include <Common/DateUtils.h>
4 5 #include <SqpApplication.h>
5 6 #include <Time/TimeController.h>
6 7
@@ -22,13 +23,15 TimeWidget::TimeWidget(QWidget *parent) : QWidget{parent}, ui{new Ui::TimeWidget
22 23 &TimeController::onTimeNotify);
23 24
24 25 // Initialisation
25 ui->startDateTimeEdit->setDateTime(
26 QDateTime::currentDateTime().addSecs(-3600)); // one hour berefore
27 ui->endDateTimeEdit->setDateTime(QDateTime::currentDateTime());
26 auto endDateTime = QDateTime::currentDateTimeUtc();
27 auto startDateTime = endDateTime.addSecs(-3600); // one hour before
28
29 ui->startDateTimeEdit->setDateTime(startDateTime);
30 ui->endDateTimeEdit->setDateTime(endDateTime);
31
32 auto dateTime = SqpDateTime{DateUtils::secondsSinceEpoch(startDateTime),
33 DateUtils::secondsSinceEpoch(endDateTime)};
28 34
29 auto dateTime
30 = SqpDateTime{QDateTime::currentDateTime().addSecs(-3600).toMSecsSinceEpoch() / 1000.0,
31 QDateTime::currentDateTime().toMSecsSinceEpoch() / 1000.0};
32 35 sqpApp->timeController().onTimeToUpdate(dateTime);
33 36 }
34 37
@@ -40,9 +43,8 TimeWidget::~TimeWidget()
40 43
41 44 void TimeWidget::onTimeUpdateRequested()
42 45 {
43 auto dateTime = SqpDateTime{
44 static_cast<double>(ui->startDateTimeEdit->dateTime().toMSecsSinceEpoch() / 1000.),
45 static_cast<double>(ui->endDateTimeEdit->dateTime().toMSecsSinceEpoch()) / 1000.};
46 auto dateTime = SqpDateTime{DateUtils::secondsSinceEpoch(ui->startDateTimeEdit->dateTime()),
47 DateUtils::secondsSinceEpoch(ui->endDateTimeEdit->dateTime())};
46 48
47 49 emit timeUpdated(std::move(dateTime));
48 50 }
@@ -25,6 +25,7 QSharedPointer<QCPAxisTicker> axisTicker(bool isTimeAxis)
25 25 if (isTimeAxis) {
26 26 auto dateTicker = QSharedPointer<QCPAxisTickerDateTime>::create();
27 27 dateTicker->setDateTimeFormat(DATETIME_TICKER_FORMAT);
28 dateTicker->setDateTimeSpec(Qt::UTC);
28 29
29 30 return dateTicker;
30 31 }
@@ -1,6 +1,7
1 1 #include "Visualization/VisualizationGraphWidget.h"
2 2 #include "Visualization/IVisualizationWidgetVisitor.h"
3 3 #include "Visualization/VisualizationGraphHelper.h"
4 #include "Visualization/VisualizationGraphRenderingDelegate.h"
4 5 #include "ui_VisualizationGraphWidget.h"
5 6
6 7 #include <Data/ArrayData.h>
@@ -33,17 +34,21 double toleranceValue(const QString &key, double defaultValue) noexcept
33 34
34 35 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
35 36
36 explicit VisualizationGraphWidgetPrivate() : m_DoSynchronize{true}, m_IsCalibration{false} {}
37
37 explicit VisualizationGraphWidgetPrivate()
38 : m_DoSynchronize{true}, m_IsCalibration{false}, m_RenderingDelegate{nullptr}
39 {
40 }
38 41
39 42 // Return the operation when range changed
40 43 VisualizationGraphWidgetZoomType getZoomType(const QCPRange &t1, const QCPRange &t2);
41 44
42 45 // 1 variable -> n qcpplot
43 46 std::multimap<std::shared_ptr<Variable>, QCPAbstractPlottable *> m_VariableToPlotMultiMap;
44
45 47 bool m_DoSynchronize;
46 48 bool m_IsCalibration;
49 QCPItemTracer *m_TextTracer;
50 /// Delegate used to attach rendering features to the plot
51 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
47 52 };
48 53
49 54 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
@@ -53,6 +58,9 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget
53 58 {
54 59 ui->setupUi(this);
55 60
61 // The delegate must be initialized after the ui as it uses the plot
62 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*ui->widget);
63
56 64 ui->graphNameLabel->setText(name);
57 65
58 66 // 'Close' options : widget is deleted when closed
@@ -65,9 +73,11 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget
65 73 // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation
66 74 ui->widget->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom);
67 75 ui->widget->axisRect()->setRangeDrag(Qt::Horizontal);
76
68 77 connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress);
69 78 connect(ui->widget, &QCustomPlot::mouseRelease, this,
70 79 &VisualizationGraphWidget::onMouseRelease);
80 connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove);
71 81 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
72 82 connect(ui->widget->xAxis, static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(
73 83 &QCPAxis::rangeChanged),
@@ -329,6 +339,12 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange
329 339 }
330 340 }
331 341
342 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
343 {
344 // Handles plot rendering when mouse is moving
345 impl->m_RenderingDelegate->onMouseMove(event);
346 }
347
332 348 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
333 349 {
334 350 auto zoomOrientations = QFlags<Qt::Orientation>{};
@@ -47,6 +47,9
47 47 <property name="calendarPopup">
48 48 <bool>true</bool>
49 49 </property>
50 <property name="timeSpec">
51 <enum>Qt::UTC</enum>
52 </property>
50 53 </widget>
51 54 </item>
52 55 <item>
@@ -76,6 +79,9
76 79 <property name="calendarPopup">
77 80 <bool>true</bool>
78 81 </property>
82 <property name="timeSpec">
83 <enum>Qt::UTC</enum>
84 </property>
79 85 </widget>
80 86 </item>
81 87 <item>
@@ -2,6 +2,7
2 2 #include "AmdaDefs.h"
3 3 #include "AmdaResultParser.h"
4 4
5 #include <Common/DateUtils.h>
5 6 #include <Data/DataProviderParameters.h>
6 7 #include <Network/NetworkController.h>
7 8 #include <SqpApplication.h>
@@ -31,7 +32,7 const auto AMDA_TIME_FORMAT = QStringLiteral("yyyy-MM-ddThh:mm:ss");
31 32 /// Formats a time to a date that can be passed in URL
32 33 QString dateFormat(double sqpDateTime) noexcept
33 34 {
34 auto dateTime = QDateTime::fromMSecsSinceEpoch(sqpDateTime * 1000.);
35 auto dateTime = DateUtils::dateTime(sqpDateTime);
35 36 return dateTime.toString(AMDA_TIME_FORMAT);
36 37 }
37 38
@@ -1,5 +1,6
1 1 #include "AmdaResultParser.h"
2 2
3 #include <Common/DateUtils.h>
3 4 #include <Data/ScalarSeries.h>
4 5
5 6 #include <QDateTime>
@@ -33,10 +34,17 const auto UNIT_REGEX = QRegularExpression{QStringLiteral("-\\s*Units\\s*:\\s*(.
33 34 double doubleDate(const QString &stringDate) noexcept
34 35 {
35 36 auto dateTime = QDateTime::fromString(stringDate, DATE_FORMAT);
36 return dateTime.isValid() ? (dateTime.toMSecsSinceEpoch() / 1000.)
37 dateTime.setTimeSpec(Qt::UTC);
38 return dateTime.isValid() ? DateUtils::secondsSinceEpoch(dateTime)
37 39 : std::numeric_limits<double>::quiet_NaN();
38 40 }
39 41
42 /// Checks if a line is a comment line
43 bool isCommentLine(const QString &line)
44 {
45 return line.startsWith("#");
46 }
47
40 48 /**
41 49 * Reads stream to retrieve x-axis unit
42 50 * @param stream the stream to read
@@ -47,20 +55,16 Unit readXAxisUnit(QTextStream &stream)
47 55 {
48 56 QString line{};
49 57
50 if (stream.readLineInto(&line)) {
58 // Searches unit in the comment lines
59 while (stream.readLineInto(&line) && isCommentLine(line)) {
51 60 auto match = UNIT_REGEX.match(line);
52 61 if (match.hasMatch()) {
53 62 return Unit{match.captured(1), true};
54 63 }
55 else {
56 qCWarning(LOG_AmdaResultParser())
57 << QObject::tr("Can't read unit: invalid line %1").arg(line);
58 }
59 }
60 else {
61 qCWarning(LOG_AmdaResultParser()) << QObject::tr("Can't read unit: end of file");
62 64 }
63 65
66 qCWarning(LOG_AmdaResultParser()) << QObject::tr("The unit could not be found in the file");
67
64 68 // Error cases
65 69 return Unit{{}, true};
66 70 }
@@ -76,32 +80,36 QPair<QVector<double>, QVector<double> > readResults(QTextStream &stream)
76 80 auto valuesData = QVector<double>{};
77 81
78 82 QString line{};
83
79 84 while (stream.readLineInto(&line)) {
80 auto lineData = line.split(RESULT_LINE_SEPARATOR, QString::SkipEmptyParts);
81 if (lineData.size() == 2) {
82 // X : the data is converted from date to double (in secs)
83 auto x = doubleDate(lineData.at(0));
84
85 // Value
86 bool valueOk;
87 auto value = lineData.at(1).toDouble(&valueOk);
88
89 // Adds result only if x and value are valid
90 if (!std::isnan(x) && !std::isnan(value) && valueOk) {
91 xData.push_back(x);
92 valuesData.push_back(value);
85 // Ignore comment lines
86 if (!isCommentLine(line)) {
87 auto lineData = line.split(RESULT_LINE_SEPARATOR, QString::SkipEmptyParts);
88 if (lineData.size() == 2) {
89 // X : the data is converted from date to double (in secs)
90 auto x = doubleDate(lineData.at(0));
91
92 // Value
93 bool valueOk;
94 auto value = lineData.at(1).toDouble(&valueOk);
95
96 // Adds result only if x and value are valid
97 if (!std::isnan(x) && !std::isnan(value) && valueOk) {
98 xData.push_back(x);
99 valuesData.push_back(value);
100 }
101 else {
102 qCWarning(LOG_AmdaResultParser())
103 << QObject::tr(
104 "Can't retrieve results from line %1: x and/or value are invalid")
105 .arg(line);
106 }
93 107 }
94 108 else {
95 109 qCWarning(LOG_AmdaResultParser())
96 << QObject::tr(
97 "Can't retrieve results from line %1: x and/or value are invalid")
98 .arg(line);
110 << QObject::tr("Can't retrieve results from line %1: invalid line").arg(line);
99 111 }
100 112 }
101 else {
102 qCWarning(LOG_AmdaResultParser())
103 << QObject::tr("Can't retrieve results from line %1: invalid line").arg(line);
104 }
105 113 }
106 114
107 115 return qMakePair(std::move(xData), std::move(valuesData));
@@ -131,13 +139,12 std::shared_ptr<IDataSeries> AmdaResultParser::readTxt(const QString &filePath)
131 139 return nullptr;
132 140 }
133 141
134 // Ignore comments lines
135 stream.readLine();
136
137 142 // Reads x-axis unit
143 stream.seek(0); // returns to the beginning of the file
138 144 auto xAxisUnit = readXAxisUnit(stream);
139 145
140 146 // Reads results
147 stream.seek(0); // returns to the beginning of the file
141 148 auto results = readResults(stream);
142 149
143 150 return std::make_shared<ScalarSeries>(std::move(results.first), std::move(results.second),
@@ -99,7 +99,7 void TestAmdaResultParser::testReadTxt_data()
99 99 // ////////// //
100 100
101 101 auto dateTime = [](int year, int month, int day, int hours, int minutes, int seconds) {
102 return QDateTime{{year, month, day}, {hours, minutes, seconds}};
102 return QDateTime{{year, month, day}, {hours, minutes, seconds}, Qt::UTC};
103 103 };
104 104
105 105 // Valid file
@@ -5,7 +5,6
5 5
6 6 #include <cmath>
7 7
8 #include <QDateTime>
9 8 #include <QFuture>
10 9 #include <QThread>
11 10 #include <QtConcurrent/QtConcurrent>
General Comments 0
You need to be logged in to leave comments. Login now