@@ -0,0 +1,30 | |||
|
1 | #ifndef SCIQLOP_GRAPHPLOTTABLESFACTORY_H | |
|
2 | #define SCIQLOP_GRAPHPLOTTABLESFACTORY_H | |
|
3 | ||
|
4 | #include <QLoggingCategory> | |
|
5 | #include <QVector> | |
|
6 | ||
|
7 | Q_DECLARE_LOGGING_CATEGORY(LOG_GraphPlottablesFactory) | |
|
8 | ||
|
9 | class QCPAbstractPlottable; | |
|
10 | class QCustomPlot; | |
|
11 | class Variable; | |
|
12 | ||
|
13 | /** | |
|
14 | * @brief The GraphPlottablesFactory class aims to create the QCustomPlot components relative to a | |
|
15 | * variable, depending on the data series of this variable | |
|
16 | */ | |
|
17 | struct GraphPlottablesFactory { | |
|
18 | /** | |
|
19 | * Creates (if possible) the QCustomPlot components relative to the variable passed in | |
|
20 | * parameter, and adds these to the plot passed in parameter. | |
|
21 | * @param variable the variable for which to create the components | |
|
22 | * @param plot the plot in which to add the created components. It takes ownership of these | |
|
23 | * components. | |
|
24 | * @return the list of the components created | |
|
25 | */ | |
|
26 | static QVector<QCPAbstractPlottable *> create(const Variable *variable, | |
|
27 | QCustomPlot &plot) noexcept; | |
|
28 | }; | |
|
29 | ||
|
30 | #endif // SCIQLOP_GRAPHPLOTTABLESFACTORY_H |
@@ -0,0 +1,91 | |||
|
1 | #include "Visualization/GraphPlottablesFactory.h" | |
|
2 | #include "Visualization/qcustomplot.h" | |
|
3 | ||
|
4 | #include <Data/ScalarSeries.h> | |
|
5 | ||
|
6 | #include <Variable/Variable.h> | |
|
7 | ||
|
8 | Q_LOGGING_CATEGORY(LOG_GraphPlottablesFactory, "GraphPlottablesFactory") | |
|
9 | ||
|
10 | namespace { | |
|
11 | ||
|
12 | /// Format for datetimes on a axis | |
|
13 | const auto DATETIME_TICKER_FORMAT = QStringLiteral("yyyy/MM/dd \nhh:mm:ss"); | |
|
14 | ||
|
15 | /// Generates the appropriate ticker for an axis, depending on whether the axis displays time or | |
|
16 | /// non-time data | |
|
17 | QSharedPointer<QCPAxisTicker> axisTicker(bool isTimeAxis) | |
|
18 | { | |
|
19 | if (isTimeAxis) { | |
|
20 | auto dateTicker = QSharedPointer<QCPAxisTickerDateTime>::create(); | |
|
21 | dateTicker->setDateTimeFormat(DATETIME_TICKER_FORMAT); | |
|
22 | ||
|
23 | return dateTicker; | |
|
24 | } | |
|
25 | else { | |
|
26 | // default ticker | |
|
27 | return QSharedPointer<QCPAxisTicker>::create(); | |
|
28 | } | |
|
29 | } | |
|
30 | ||
|
31 | QCPAbstractPlottable *createScalarSeriesComponent(ScalarSeries &scalarSeries, QCustomPlot &plot) | |
|
32 | { | |
|
33 | auto component = plot.addGraph(); | |
|
34 | ||
|
35 | if (component) { | |
|
36 | // Graph data | |
|
37 | component->setData(scalarSeries.xAxisData()->data(), scalarSeries.valuesData()->data(), | |
|
38 | true); | |
|
39 | ||
|
40 | // Axes properties | |
|
41 | /// @todo : for the moment, no control is performed on the axes: the units and the tickers | |
|
42 | /// are fixed for the default x-axis and y-axis of the plot, and according to the new graph | |
|
43 | ||
|
44 | auto setAxisProperties = [](auto axis, const auto &unit) { | |
|
45 | // label (unit name) | |
|
46 | axis->setLabel(unit.m_Name); | |
|
47 | ||
|
48 | // ticker (depending on the type of unit) | |
|
49 | axis->setTicker(axisTicker(unit.m_TimeUnit)); | |
|
50 | }; | |
|
51 | setAxisProperties(plot.xAxis, scalarSeries.xAxisUnit()); | |
|
52 | setAxisProperties(plot.yAxis, scalarSeries.valuesUnit()); | |
|
53 | ||
|
54 | // Display all data | |
|
55 | component->rescaleAxes(); | |
|
56 | ||
|
57 | plot.replot(); | |
|
58 | } | |
|
59 | else { | |
|
60 | qCDebug(LOG_GraphPlottablesFactory()) | |
|
61 | << QObject::tr("Can't create graph for the scalar series"); | |
|
62 | } | |
|
63 | ||
|
64 | return component; | |
|
65 | } | |
|
66 | ||
|
67 | } // namespace | |
|
68 | ||
|
69 | QVector<QCPAbstractPlottable *> GraphPlottablesFactory::create(const Variable *variable, | |
|
70 | QCustomPlot &plot) noexcept | |
|
71 | { | |
|
72 | auto result = QVector<QCPAbstractPlottable *>{}; | |
|
73 | ||
|
74 | if (variable) { | |
|
75 | // Gets the data series of the variable to call the creation of the right components | |
|
76 | // according to its type | |
|
77 | if (auto scalarSeries = dynamic_cast<ScalarSeries *>(variable->dataSeries())) { | |
|
78 | result.append(createScalarSeriesComponent(*scalarSeries, plot)); | |
|
79 | } | |
|
80 | else { | |
|
81 | qCDebug(LOG_GraphPlottablesFactory()) | |
|
82 | << QObject::tr("Can't create graph plottables : unmanaged data series type"); | |
|
83 | } | |
|
84 | } | |
|
85 | else { | |
|
86 | qCDebug(LOG_GraphPlottablesFactory()) | |
|
87 | << QObject::tr("Can't create graph plottables : the variable is null"); | |
|
88 | } | |
|
89 | ||
|
90 | return result; | |
|
91 | } |
@@ -27,6 +27,8 | |||
|
27 | 27 | #include <SidePane/SqpSidePane.h> |
|
28 | 28 | #include <SqpApplication.h> |
|
29 | 29 | #include <TimeWidget/TimeWidget.h> |
|
30 | #include <Variable/Variable.h> | |
|
31 | #include <Visualization/VisualizationController.h> | |
|
30 | 32 | |
|
31 | 33 | #include <QAction> |
|
32 | 34 | #include <QDate> |
@@ -172,6 +174,10 MainWindow::MainWindow(QWidget *parent) | |||
|
172 | 174 | connect(&sqpApp->dataSourceController(), SIGNAL(dataSourceItemSet(DataSourceItem *)), |
|
173 | 175 | m_Ui->dataSourceWidget, SLOT(addDataSource(DataSourceItem *))); |
|
174 | 176 | |
|
177 | qRegisterMetaType<std::shared_ptr<Variable> >(); | |
|
178 | connect(&sqpApp->visualizationController(), SIGNAL(variableCreated(std::shared_ptr<Variable>)), | |
|
179 | m_Ui->view, SLOT(displayVariable(std::shared_ptr<Variable>))); | |
|
180 | ||
|
175 | 181 | /* QLopGUI::registerMenuBar(menuBar()); |
|
176 | 182 | this->setWindowIcon(QIcon(":/sciqlopLOGO.svg")); |
|
177 | 183 | this->m_progressWidget = new QWidget(); |
@@ -21,30 +21,30 public: | |||
|
21 | 21 | std::shared_ptr<ArrayData<1> > xAxisData() override { return m_XAxisData; } |
|
22 | 22 | |
|
23 | 23 | /// @sa IDataSeries::xAxisUnit() |
|
24 |
|
|
|
24 | Unit xAxisUnit() const override { return m_XAxisUnit; } | |
|
25 | 25 | |
|
26 | 26 | /// @return the values dataset |
|
27 | 27 | std::shared_ptr<ArrayData<Dim> > valuesData() const { return m_ValuesData; } |
|
28 | 28 | |
|
29 | 29 | /// @sa IDataSeries::valuesUnit() |
|
30 |
|
|
|
30 | Unit valuesUnit() const override { return m_ValuesUnit; } | |
|
31 | 31 | |
|
32 | 32 | protected: |
|
33 | 33 | /// Protected ctor (DataSeries is abstract) |
|
34 |
explicit DataSeries(std::shared_ptr<ArrayData<1> > xAxisData, |
|
|
35 |
std::shared_ptr<ArrayData<Dim> > valuesData, |
|
|
34 | explicit DataSeries(std::shared_ptr<ArrayData<1> > xAxisData, Unit xAxisUnit, | |
|
35 | std::shared_ptr<ArrayData<Dim> > valuesData, Unit valuesUnit) | |
|
36 | 36 | : m_XAxisData{xAxisData}, |
|
37 | m_XAxisUnit{xAxisUnit}, | |
|
37 | m_XAxisUnit{std::move(xAxisUnit)}, | |
|
38 | 38 | m_ValuesData{valuesData}, |
|
39 | m_ValuesUnit{valuesUnit} | |
|
39 | m_ValuesUnit{std::move(valuesUnit)} | |
|
40 | 40 | { |
|
41 | 41 | } |
|
42 | 42 | |
|
43 | 43 | private: |
|
44 | 44 | std::shared_ptr<ArrayData<1> > m_XAxisData; |
|
45 |
|
|
|
45 | Unit m_XAxisUnit; | |
|
46 | 46 | std::shared_ptr<ArrayData<Dim> > m_ValuesData; |
|
47 |
|
|
|
47 | Unit m_ValuesUnit; | |
|
48 | 48 | }; |
|
49 | 49 | |
|
50 | 50 | #endif // SCIQLOP_DATASERIES_H |
@@ -8,6 +8,16 | |||
|
8 | 8 | template <int Dim> |
|
9 | 9 | class ArrayData; |
|
10 | 10 | |
|
11 | struct Unit { | |
|
12 | explicit Unit(const QString &name = {}, bool timeUnit = false) | |
|
13 | : m_Name{name}, m_TimeUnit{timeUnit} | |
|
14 | { | |
|
15 | } | |
|
16 | ||
|
17 | QString m_Name; ///< Unit name | |
|
18 | bool m_TimeUnit; ///< The unit is a unit of time | |
|
19 | }; | |
|
20 | ||
|
11 | 21 | /** |
|
12 | 22 | * @brief The IDataSeries aims to declare a data series. |
|
13 | 23 | * |
@@ -29,9 +39,9 public: | |||
|
29 | 39 | /// Returns the x-axis dataset |
|
30 | 40 | virtual std::shared_ptr<ArrayData<1> > xAxisData() = 0; |
|
31 | 41 | |
|
32 |
virtual |
|
|
42 | virtual Unit xAxisUnit() const = 0; | |
|
33 | 43 | |
|
34 |
virtual |
|
|
44 | virtual Unit valuesUnit() const = 0; | |
|
35 | 45 | }; |
|
36 | 46 | |
|
37 | 47 | #endif // SCIQLOP_IDATASERIES_H |
@@ -14,7 +14,7 public: | |||
|
14 | 14 | * @param xAxisUnit x-axis unit |
|
15 | 15 | * @param valuesUnit values unit |
|
16 | 16 | */ |
|
17 |
explicit ScalarSeries(int size, |
|
|
17 | explicit ScalarSeries(int size, Unit xAxisUnit, Unit valuesUnit); | |
|
18 | 18 | |
|
19 | 19 | /** |
|
20 | 20 | * Sets data for a specific index. The index has to be valid to be effective |
@@ -21,6 +21,9 public: | |||
|
21 | 21 | |
|
22 | 22 | void addDataSeries(std::unique_ptr<IDataSeries> dataSeries) noexcept; |
|
23 | 23 | |
|
24 | /// @return the data of the variable, nullptr if there is no data | |
|
25 | IDataSeries *dataSeries() const noexcept; | |
|
26 | ||
|
24 | 27 | private: |
|
25 | 28 | class VariablePrivate; |
|
26 | 29 | spimpl::unique_impl_ptr<VariablePrivate> impl; |
@@ -25,10 +25,11 public: | |||
|
25 | 25 | explicit VisualizationController(QObject *parent = 0); |
|
26 | 26 | virtual ~VisualizationController(); |
|
27 | 27 | |
|
28 | public slots: | |
|
29 |
/// S |
|
|
30 |
void |
|
|
28 | signals: | |
|
29 | /// Signal emitted when a variable has been created in SciQlop | |
|
30 | void variableCreated(std::shared_ptr<Variable> variable); | |
|
31 | 31 | |
|
32 | public slots: | |
|
32 | 33 | /// Manage init/end of the controller |
|
33 | 34 | void initialize(); |
|
34 | 35 | void finalize(); |
@@ -1,8 +1,8 | |||
|
1 | 1 | #include <Data/ScalarSeries.h> |
|
2 | 2 | |
|
3 |
ScalarSeries::ScalarSeries(int size, |
|
|
4 | : DataSeries{std::make_shared<ArrayData<1> >(size), xAxisUnit, | |
|
5 | std::make_shared<ArrayData<1> >(size), valuesUnit} | |
|
3 | ScalarSeries::ScalarSeries(int size, Unit xAxisUnit, Unit valuesUnit) | |
|
4 | : DataSeries{std::make_shared<ArrayData<1> >(size), std::move(xAxisUnit), | |
|
5 | std::make_shared<ArrayData<1> >(size), std::move(valuesUnit)} | |
|
6 | 6 | { |
|
7 | 7 | } |
|
8 | 8 |
@@ -41,3 +41,8 void Variable::addDataSeries(std::unique_ptr<IDataSeries> dataSeries) noexcept | |||
|
41 | 41 | } |
|
42 | 42 | /// @todo : else, merge the two data series (if possible) |
|
43 | 43 | } |
|
44 | ||
|
45 | IDataSeries *Variable::dataSeries() const noexcept | |
|
46 | { | |
|
47 | return impl->m_DataSeries.get(); | |
|
48 | } |
@@ -29,12 +29,6 VisualizationController::~VisualizationController() | |||
|
29 | 29 | this->waitForFinish(); |
|
30 | 30 | } |
|
31 | 31 | |
|
32 | void VisualizationController::onVariableCreated(std::shared_ptr<Variable> variable) noexcept | |
|
33 | { | |
|
34 | /// @todo ALX : make new graph for the variable | |
|
35 | qCDebug(LOG_VisualizationController()) << "new variable to display"; | |
|
36 | } | |
|
37 | ||
|
38 | 32 | void VisualizationController::initialize() |
|
39 | 33 | { |
|
40 | 34 | qCDebug(LOG_VisualizationController()) << tr("VisualizationController init") |
@@ -34,6 +34,10 private: | |||
|
34 | 34 | |
|
35 | 35 | class VisualizationGraphWidgetPrivate; |
|
36 | 36 | spimpl::unique_impl_ptr<VisualizationGraphWidgetPrivate> impl; |
|
37 | ||
|
38 | private slots: | |
|
39 | /// Slot called when a mouse wheel was made, to perform some processing before the zoom is done | |
|
40 | void onMouseWheel(QWheelEvent *event) noexcept; | |
|
37 | 41 | }; |
|
38 | 42 | |
|
39 | 43 | #endif // SCIQLOP_VISUALIZATIONGRAPHWIDGET_H |
@@ -6,6 +6,7 | |||
|
6 | 6 | #include <QLoggingCategory> |
|
7 | 7 | #include <QWidget> |
|
8 | 8 | |
|
9 | class Variable; | |
|
9 | 10 | class VisualizationTabWidget; |
|
10 | 11 | |
|
11 | 12 | Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationWidget) |
@@ -35,6 +36,15 public: | |||
|
35 | 36 | void close() override; |
|
36 | 37 | QString name() const; |
|
37 | 38 | |
|
39 | public slots: | |
|
40 | /** | |
|
41 | * Displays a variable in a new graph of a new zone of the current tab | |
|
42 | * @param variable the variable to display | |
|
43 | * @todo this is a temporary method that will be replaced by own actions for each type of | |
|
44 | * visualization widget | |
|
45 | */ | |
|
46 | void displayVariable(std::shared_ptr<Variable> variable) noexcept; | |
|
47 | ||
|
38 | 48 | private: |
|
39 | 49 | Ui::VisualizationWidget *ui; |
|
40 | 50 | }; |
@@ -31,7 +31,7 public: | |||
|
31 | 31 | qRegisterMetaType<std::shared_ptr<Variable> >(); |
|
32 | 32 | connect(m_VariableController.get(), SIGNAL(variableCreated(std::shared_ptr<Variable>)), |
|
33 | 33 | m_VisualizationController.get(), |
|
34 |
SL |
|
|
34 | SIGNAL(variableCreated(std::shared_ptr<Variable>))); | |
|
35 | 35 | |
|
36 | 36 | m_DataSourceController->moveToThread(&m_DataSourceControllerThread); |
|
37 | 37 | m_VariableController->moveToThread(&m_VariableControllerThread); |
@@ -1,15 +1,25 | |||
|
1 | 1 | #include "Visualization/VisualizationGraphWidget.h" |
|
2 | #include "Visualization/GraphPlottablesFactory.h" | |
|
2 | 3 | #include "ui_VisualizationGraphWidget.h" |
|
3 | 4 | |
|
4 | 5 | #include <Variable/Variable.h> |
|
5 | 6 | |
|
6 | 7 | #include <unordered_map> |
|
7 | 8 | |
|
9 | namespace { | |
|
10 | ||
|
11 | /// Key pressed to enable zoom on horizontal axis | |
|
12 | const auto HORIZONTAL_ZOOM_MODIFIER = Qt::NoModifier; | |
|
13 | ||
|
14 | /// Key pressed to enable zoom on vertical axis | |
|
15 | const auto VERTICAL_ZOOM_MODIFIER = Qt::ControlModifier; | |
|
16 | ||
|
17 | } // namespace | |
|
18 | ||
|
8 | 19 | struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate { |
|
9 | 20 | |
|
10 | 21 | // 1 variable -> n qcpplot |
|
11 |
std::unordered_map<std::shared_ptr<Variable>, |
|
|
12 | m_VariableToPlotMap; | |
|
22 | std::unordered_map<std::shared_ptr<Variable>, QCPAbstractPlottable *> m_VariableToPlotMap; | |
|
13 | 23 | }; |
|
14 | 24 | |
|
15 | 25 | VisualizationGraphWidget::VisualizationGraphWidget(QWidget *parent) |
@@ -18,6 +28,13 VisualizationGraphWidget::VisualizationGraphWidget(QWidget *parent) | |||
|
18 | 28 | impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>()} |
|
19 | 29 | { |
|
20 | 30 | ui->setupUi(this); |
|
31 | ||
|
32 | // Set qcpplot properties : | |
|
33 | // - Drag (on x-axis) and zoom are enabled | |
|
34 | // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation | |
|
35 | ui->widget->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom); | |
|
36 | ui->widget->axisRect()->setRangeDrag(Qt::Horizontal); | |
|
37 | connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel); | |
|
21 | 38 | } |
|
22 | 39 | |
|
23 | 40 | VisualizationGraphWidget::~VisualizationGraphWidget() |
@@ -27,7 +44,12 VisualizationGraphWidget::~VisualizationGraphWidget() | |||
|
27 | 44 | |
|
28 | 45 | void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable) |
|
29 | 46 | { |
|
30 | // todo: first check is variable contains data then check how many plot have to be created | |
|
47 | // Uses delegate to create the qcpplot components according to the variable | |
|
48 | auto createdPlottables = GraphPlottablesFactory::create(variable.get(), *ui->widget); | |
|
49 | ||
|
50 | for (auto createdPlottable : qAsConst(createdPlottables)) { | |
|
51 | impl->m_VariableToPlotMap.insert({variable, createdPlottable}); | |
|
52 | } | |
|
31 | 53 | } |
|
32 | 54 | |
|
33 | 55 | void VisualizationGraphWidget::accept(IVisualizationWidget *visitor) |
@@ -45,3 +67,20 QString VisualizationGraphWidget::name() const | |||
|
45 | 67 | { |
|
46 | 68 | return QStringLiteral("MainView"); |
|
47 | 69 | } |
|
70 | ||
|
71 | void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept | |
|
72 | { | |
|
73 | auto zoomOrientations = QFlags<Qt::Orientation>{}; | |
|
74 | ||
|
75 | // Lambda that enables a zoom orientation if the key modifier related to this orientation has | |
|
76 | // been pressed | |
|
77 | auto enableOrientation | |
|
78 | = [&zoomOrientations, event](const auto &orientation, const auto &modifier) { | |
|
79 | auto orientationEnabled = event->modifiers().testFlag(modifier); | |
|
80 | zoomOrientations.setFlag(orientation, orientationEnabled); | |
|
81 | }; | |
|
82 | enableOrientation(Qt::Vertical, VERTICAL_ZOOM_MODIFIER); | |
|
83 | enableOrientation(Qt::Horizontal, HORIZONTAL_ZOOM_MODIFIER); | |
|
84 | ||
|
85 | ui->widget->axisRect()->setRangeZoom(zoomOrientations); | |
|
86 | } |
@@ -1,5 +1,7 | |||
|
1 | 1 | #include "Visualization/VisualizationWidget.h" |
|
2 | #include "Visualization/VisualizationGraphWidget.h" | |
|
2 | 3 | #include "Visualization/VisualizationTabWidget.h" |
|
4 | #include "Visualization/VisualizationZoneWidget.h" | |
|
3 | 5 | #include "Visualization/qcustomplot.h" |
|
4 | 6 | |
|
5 | 7 | #include "ui_VisualizationWidget.h" |
@@ -78,3 +80,26 QString VisualizationWidget::name() const | |||
|
78 | 80 | { |
|
79 | 81 | return QStringLiteral("MainView"); |
|
80 | 82 | } |
|
83 | ||
|
84 | void VisualizationWidget::displayVariable(std::shared_ptr<Variable> variable) noexcept | |
|
85 | { | |
|
86 | if (auto currentTab = dynamic_cast<VisualizationTabWidget *>(ui->tabWidget->currentWidget())) { | |
|
87 | if (auto newZone = currentTab->createZone()) { | |
|
88 | if (auto newGraph = newZone->createGraph()) { | |
|
89 | newGraph->addVariable(variable); | |
|
90 | } | |
|
91 | else { | |
|
92 | qCDebug(LOG_VisualizationWidget()) | |
|
93 | << tr("Can't display the variable : can't create the graph"); | |
|
94 | } | |
|
95 | } | |
|
96 | else { | |
|
97 | qCDebug(LOG_VisualizationWidget()) | |
|
98 | << tr("Can't display the variable : can't create a new zone in the current tab"); | |
|
99 | } | |
|
100 | } | |
|
101 | else { | |
|
102 | qCDebug(LOG_VisualizationWidget()) | |
|
103 | << tr("Can't display the variable : there is no current tab"); | |
|
104 | } | |
|
105 | } |
@@ -19,7 +19,7 CosinusProvider::retrieveData(const DataProviderParameters ¶meters) const | |||
|
19 | 19 | |
|
20 | 20 | // Generates scalar series containing cosinus values (one value per second) |
|
21 | 21 | auto scalarSeries |
|
22 |
= std::make_unique<ScalarSeries>(end - start, QStringLiteral("t"), |
|
|
22 | = std::make_unique<ScalarSeries>(end - start, Unit{QStringLiteral("t"), true}, Unit{}); | |
|
23 | 23 | |
|
24 | 24 | auto dataIndex = 0; |
|
25 | 25 | for (auto time = start; time < end; ++time, ++dataIndex) { |
General Comments 0
You need to be logged in to leave comments.
Login now