2021-01-01 21:43:21 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
#ifndef STATS_VIEW_H
|
|
|
|
#define STATS_VIEW_H
|
|
|
|
|
|
|
|
#include "statsstate.h"
|
|
|
|
#include <memory>
|
2021-01-04 20:41:30 +00:00
|
|
|
#include <QFont>
|
statistics: convert chart to QQuickItem
It turns out that the wrong base class was used for the chart.
QQuickWidget can only be used on desktop, not in a mobile UI.
Therefore, turn this into a QQuickItem and move the container
QQuickWidget into desktop-only code.
Currently, this code is insane: The chart is rendered onto a
QGraphicsScene (as it was before), which is then rendered into
a QImage, which is transformed into a QSGTexture, which is then
projected onto the device. This is performed on every mouse
move event, since these events in general change the position
of the info-box.
The plan is to slowly convert elements such as the info-box into
QQuickItems. Browsing the QtQuick documentation, this will
not be much fun.
Also note that the rendering currently tears, flickers and has
antialiasing artifacts, most likely owing to integer (QImage)
to floating point (QGraphicsScene, QQuickItem) conversion
problems. The data flow is
QGraphicsScene (float) -> QImage (int) -> QQuickItem (float).
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-07 13:38:37 +00:00
|
|
|
#include <QGraphicsScene>
|
|
|
|
#include <QImage>
|
|
|
|
#include <QPainter>
|
|
|
|
#include <QQuickItem>
|
2021-01-09 23:12:41 +00:00
|
|
|
#include <QGraphicsPolygonItem>
|
2021-01-01 21:43:21 +00:00
|
|
|
|
|
|
|
struct dive;
|
|
|
|
struct StatsBinner;
|
|
|
|
struct StatsBin;
|
|
|
|
struct StatsState;
|
|
|
|
struct StatsVariable;
|
|
|
|
|
|
|
|
class QGraphicsLineItem;
|
2021-01-04 20:41:30 +00:00
|
|
|
class QGraphicsSimpleTextItem;
|
2021-01-01 21:43:21 +00:00
|
|
|
class StatsSeries;
|
|
|
|
class CategoryAxis;
|
2021-01-12 14:20:05 +00:00
|
|
|
class ChartItem;
|
2021-01-01 21:43:21 +00:00
|
|
|
class CountAxis;
|
|
|
|
class HistogramAxis;
|
|
|
|
class StatsAxis;
|
2021-01-05 12:51:39 +00:00
|
|
|
class StatsGrid;
|
2021-01-01 21:43:21 +00:00
|
|
|
class Legend;
|
statistics: convert chart to QQuickItem
It turns out that the wrong base class was used for the chart.
QQuickWidget can only be used on desktop, not in a mobile UI.
Therefore, turn this into a QQuickItem and move the container
QQuickWidget into desktop-only code.
Currently, this code is insane: The chart is rendered onto a
QGraphicsScene (as it was before), which is then rendered into
a QImage, which is transformed into a QSGTexture, which is then
projected onto the device. This is performed on every mouse
move event, since these events in general change the position
of the info-box.
The plan is to slowly convert elements such as the info-box into
QQuickItems. Browsing the QtQuick documentation, this will
not be much fun.
Also note that the rendering currently tears, flickers and has
antialiasing artifacts, most likely owing to integer (QImage)
to floating point (QGraphicsScene, QQuickItem) conversion
problems. The data flow is
QGraphicsScene (float) -> QImage (int) -> QQuickItem (float).
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-07 13:38:37 +00:00
|
|
|
class QSGTexture;
|
2021-01-13 15:19:27 +00:00
|
|
|
class RootNode; // Internal implementation detail
|
2021-01-01 21:43:21 +00:00
|
|
|
|
|
|
|
enum class ChartSubType : int;
|
2021-01-13 15:19:27 +00:00
|
|
|
enum class ChartZValue : int;
|
2021-01-01 21:43:21 +00:00
|
|
|
enum class StatsOperation : int;
|
|
|
|
|
2021-01-12 18:39:25 +00:00
|
|
|
struct regression_data {
|
|
|
|
double a,b;
|
|
|
|
double res2, r2, sx2, xavg;
|
|
|
|
int n;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
statistics: convert chart to QQuickItem
It turns out that the wrong base class was used for the chart.
QQuickWidget can only be used on desktop, not in a mobile UI.
Therefore, turn this into a QQuickItem and move the container
QQuickWidget into desktop-only code.
Currently, this code is insane: The chart is rendered onto a
QGraphicsScene (as it was before), which is then rendered into
a QImage, which is transformed into a QSGTexture, which is then
projected onto the device. This is performed on every mouse
move event, since these events in general change the position
of the info-box.
The plan is to slowly convert elements such as the info-box into
QQuickItems. Browsing the QtQuick documentation, this will
not be much fun.
Also note that the rendering currently tears, flickers and has
antialiasing artifacts, most likely owing to integer (QImage)
to floating point (QGraphicsScene, QQuickItem) conversion
problems. The data flow is
QGraphicsScene (float) -> QImage (int) -> QQuickItem (float).
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-07 13:38:37 +00:00
|
|
|
class StatsView : public QQuickItem {
|
2021-01-01 21:43:21 +00:00
|
|
|
Q_OBJECT
|
|
|
|
public:
|
statistics: convert chart to QQuickItem
It turns out that the wrong base class was used for the chart.
QQuickWidget can only be used on desktop, not in a mobile UI.
Therefore, turn this into a QQuickItem and move the container
QQuickWidget into desktop-only code.
Currently, this code is insane: The chart is rendered onto a
QGraphicsScene (as it was before), which is then rendered into
a QImage, which is transformed into a QSGTexture, which is then
projected onto the device. This is performed on every mouse
move event, since these events in general change the position
of the info-box.
The plan is to slowly convert elements such as the info-box into
QQuickItems. Browsing the QtQuick documentation, this will
not be much fun.
Also note that the rendering currently tears, flickers and has
antialiasing artifacts, most likely owing to integer (QImage)
to floating point (QGraphicsScene, QQuickItem) conversion
problems. The data flow is
QGraphicsScene (float) -> QImage (int) -> QQuickItem (float).
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-07 13:38:37 +00:00
|
|
|
StatsView();
|
|
|
|
StatsView(QQuickItem *parent);
|
2021-01-01 21:43:21 +00:00
|
|
|
~StatsView();
|
|
|
|
|
|
|
|
void plot(const StatsState &state);
|
2021-01-12 14:20:05 +00:00
|
|
|
QQuickWindow *w() const; // Make window available to items
|
|
|
|
QSizeF size() const;
|
2021-01-13 15:19:27 +00:00
|
|
|
void addQSGNode(QSGNode *node, ChartZValue z); // Must only be called in render thread!
|
|
|
|
void registerChartItem(ChartItem *item);
|
2021-01-12 14:20:05 +00:00
|
|
|
void unregisterChartItem(const ChartItem *item);
|
2021-01-13 14:17:54 +00:00
|
|
|
template <typename T, class... Args>
|
|
|
|
std::unique_ptr<T> createChartItem(Args&&... args);
|
|
|
|
|
2021-01-01 21:43:21 +00:00
|
|
|
private slots:
|
|
|
|
void replotIfVisible();
|
|
|
|
private:
|
statistics: convert chart to QQuickItem
It turns out that the wrong base class was used for the chart.
QQuickWidget can only be used on desktop, not in a mobile UI.
Therefore, turn this into a QQuickItem and move the container
QQuickWidget into desktop-only code.
Currently, this code is insane: The chart is rendered onto a
QGraphicsScene (as it was before), which is then rendered into
a QImage, which is transformed into a QSGTexture, which is then
projected onto the device. This is performed on every mouse
move event, since these events in general change the position
of the info-box.
The plan is to slowly convert elements such as the info-box into
QQuickItems. Browsing the QtQuick documentation, this will
not be much fun.
Also note that the rendering currently tears, flickers and has
antialiasing artifacts, most likely owing to integer (QImage)
to floating point (QGraphicsScene, QQuickItem) conversion
problems. The data flow is
QGraphicsScene (float) -> QImage (int) -> QQuickItem (float).
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-07 13:38:37 +00:00
|
|
|
// QtQuick related things
|
|
|
|
QRectF plotRect;
|
|
|
|
QSGNode *updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *updatePaintNodeData) override;
|
|
|
|
std::unique_ptr<QImage> img;
|
|
|
|
std::unique_ptr<QPainter> painter;
|
|
|
|
QGraphicsScene scene;
|
|
|
|
std::unique_ptr<QSGTexture> texture;
|
|
|
|
|
|
|
|
void plotAreaChanged(const QSizeF &size);
|
2021-01-01 21:43:21 +00:00
|
|
|
void reset(); // clears all series and axes
|
2021-01-05 11:11:46 +00:00
|
|
|
void setAxes(StatsAxis *x, StatsAxis *y);
|
2021-01-01 21:43:21 +00:00
|
|
|
void plotBarChart(const std::vector<dive *> &dives,
|
|
|
|
ChartSubType subType,
|
|
|
|
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner,
|
|
|
|
const StatsVariable *valueVariable, const StatsBinner *valueBinner, bool labels, bool legend);
|
|
|
|
void plotValueChart(const std::vector<dive *> &dives,
|
|
|
|
ChartSubType subType,
|
|
|
|
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner,
|
|
|
|
const StatsVariable *valueVariable, StatsOperation valueAxisOperation, bool labels);
|
|
|
|
void plotDiscreteCountChart(const std::vector<dive *> &dives,
|
|
|
|
ChartSubType subType,
|
|
|
|
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner, bool labels);
|
|
|
|
void plotPieChart(const std::vector<dive *> &dives,
|
|
|
|
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner, bool labels, bool legend);
|
|
|
|
void plotDiscreteBoxChart(const std::vector<dive *> &dives,
|
|
|
|
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner, const StatsVariable *valueVariable);
|
|
|
|
void plotDiscreteScatter(const std::vector<dive *> &dives,
|
|
|
|
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner,
|
|
|
|
const StatsVariable *valueVariable, bool quartiles);
|
|
|
|
void plotHistogramCountChart(const std::vector<dive *> &dives,
|
|
|
|
ChartSubType subType,
|
|
|
|
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner,
|
|
|
|
bool labels, bool showMedian, bool showMean);
|
|
|
|
void plotHistogramValueChart(const std::vector<dive *> &dives,
|
|
|
|
ChartSubType subType,
|
|
|
|
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner,
|
|
|
|
const StatsVariable *valueVariable, StatsOperation valueAxisOperation, bool labels);
|
|
|
|
void plotHistogramStackedChart(const std::vector<dive *> &dives,
|
|
|
|
ChartSubType subType,
|
|
|
|
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner,
|
|
|
|
const StatsVariable *valueVariable, const StatsBinner *valueBinner, bool labels, bool legend);
|
|
|
|
void plotHistogramBoxChart(const std::vector<dive *> &dives,
|
|
|
|
const StatsVariable *categoryVariable, const StatsBinner *categoryBinner, const StatsVariable *valueVariable);
|
|
|
|
void plotScatter(const std::vector<dive *> &dives, const StatsVariable *categoryVariable, const StatsVariable *valueVariable);
|
|
|
|
void setTitle(const QString &);
|
2021-01-04 20:41:30 +00:00
|
|
|
void updateTitlePos(); // After resizing, set title to correct position
|
|
|
|
void plotChart();
|
2021-01-01 21:43:21 +00:00
|
|
|
|
|
|
|
template <typename T, class... Args>
|
|
|
|
T *createSeries(Args&&... args);
|
|
|
|
|
|
|
|
template <typename T, class... Args>
|
|
|
|
T *createAxis(const QString &title, Args&&... args);
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
CategoryAxis *createCategoryAxis(const QString &title, const StatsBinner &binner,
|
|
|
|
const std::vector<T> &bins, bool isHorizontal);
|
|
|
|
template<typename T>
|
|
|
|
HistogramAxis *createHistogramAxis(const QString &title, const StatsBinner &binner,
|
|
|
|
const std::vector<T> &bins, bool isHorizontal);
|
|
|
|
CountAxis *createCountAxis(int maxVal, bool isHorizontal);
|
|
|
|
|
|
|
|
// Helper functions to add feature to the chart
|
|
|
|
void addLineMarker(double pos, double low, double high, const QPen &pen, bool isHorizontal);
|
|
|
|
|
|
|
|
// A short line used to mark quartiles
|
|
|
|
struct QuartileMarker {
|
|
|
|
std::unique_ptr<QGraphicsLineItem> item;
|
2021-01-05 11:11:46 +00:00
|
|
|
StatsAxis *xAxis, *yAxis;
|
2021-01-01 21:43:21 +00:00
|
|
|
double pos, value;
|
statistics: convert chart to QQuickItem
It turns out that the wrong base class was used for the chart.
QQuickWidget can only be used on desktop, not in a mobile UI.
Therefore, turn this into a QQuickItem and move the container
QQuickWidget into desktop-only code.
Currently, this code is insane: The chart is rendered onto a
QGraphicsScene (as it was before), which is then rendered into
a QImage, which is transformed into a QSGTexture, which is then
projected onto the device. This is performed on every mouse
move event, since these events in general change the position
of the info-box.
The plan is to slowly convert elements such as the info-box into
QQuickItems. Browsing the QtQuick documentation, this will
not be much fun.
Also note that the rendering currently tears, flickers and has
antialiasing artifacts, most likely owing to integer (QImage)
to floating point (QGraphicsScene, QQuickItem) conversion
problems. The data flow is
QGraphicsScene (float) -> QImage (int) -> QQuickItem (float).
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-07 13:38:37 +00:00
|
|
|
QuartileMarker(double pos, double value, QGraphicsScene *scene, StatsAxis *xAxis, StatsAxis *yAxis);
|
2021-01-01 21:43:21 +00:00
|
|
|
void updatePosition();
|
|
|
|
};
|
|
|
|
|
2021-01-06 13:17:51 +00:00
|
|
|
// A regression line
|
|
|
|
struct RegressionLine {
|
2021-01-09 23:12:41 +00:00
|
|
|
std::unique_ptr<QGraphicsPolygonItem> item;
|
2021-01-12 20:27:16 +00:00
|
|
|
std::unique_ptr<QGraphicsPolygonItem> central;
|
2021-01-05 11:11:46 +00:00
|
|
|
StatsAxis *xAxis, *yAxis;
|
2021-01-12 18:39:25 +00:00
|
|
|
const struct regression_data reg;
|
2021-01-01 21:43:21 +00:00
|
|
|
void updatePosition();
|
2021-01-12 18:39:25 +00:00
|
|
|
RegressionLine(const struct regression_data reg, QBrush brush, QGraphicsScene *scene, StatsAxis *xAxis, StatsAxis *yAxis);
|
2021-01-06 13:17:51 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// A line marking median or mean in histograms
|
|
|
|
struct HistogramMarker {
|
|
|
|
std::unique_ptr<QGraphicsLineItem> item;
|
|
|
|
StatsAxis *xAxis, *yAxis;
|
|
|
|
double val;
|
|
|
|
bool horizontal;
|
|
|
|
void updatePosition();
|
statistics: convert chart to QQuickItem
It turns out that the wrong base class was used for the chart.
QQuickWidget can only be used on desktop, not in a mobile UI.
Therefore, turn this into a QQuickItem and move the container
QQuickWidget into desktop-only code.
Currently, this code is insane: The chart is rendered onto a
QGraphicsScene (as it was before), which is then rendered into
a QImage, which is transformed into a QSGTexture, which is then
projected onto the device. This is performed on every mouse
move event, since these events in general change the position
of the info-box.
The plan is to slowly convert elements such as the info-box into
QQuickItems. Browsing the QtQuick documentation, this will
not be much fun.
Also note that the rendering currently tears, flickers and has
antialiasing artifacts, most likely owing to integer (QImage)
to floating point (QGraphicsScene, QQuickItem) conversion
problems. The data flow is
QGraphicsScene (float) -> QImage (int) -> QQuickItem (float).
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-07 13:38:37 +00:00
|
|
|
HistogramMarker(double val, bool horizontal, QPen pen, QGraphicsScene *scene, StatsAxis *xAxis, StatsAxis *yAxis);
|
2021-01-01 21:43:21 +00:00
|
|
|
};
|
|
|
|
|
2021-01-12 18:39:25 +00:00
|
|
|
void addLinearRegression(const struct regression_data reg, StatsAxis *xAxis, StatsAxis *yAxis);
|
2021-01-06 13:17:51 +00:00
|
|
|
void addHistogramMarker(double pos, const QPen &pen, bool isHorizontal, StatsAxis *xAxis, StatsAxis *yAxis);
|
2021-01-01 21:43:21 +00:00
|
|
|
|
|
|
|
StatsState state;
|
2021-01-04 20:41:30 +00:00
|
|
|
QFont titleFont;
|
2021-01-01 21:43:21 +00:00
|
|
|
std::vector<std::unique_ptr<StatsAxis>> axes;
|
2021-01-05 12:51:39 +00:00
|
|
|
std::unique_ptr<StatsGrid> grid;
|
2021-01-01 21:43:21 +00:00
|
|
|
std::vector<std::unique_ptr<StatsSeries>> series;
|
|
|
|
std::unique_ptr<Legend> legend;
|
|
|
|
std::vector<QuartileMarker> quartileMarkers;
|
2021-01-06 13:17:51 +00:00
|
|
|
std::vector<RegressionLine> regressionLines;
|
|
|
|
std::vector<HistogramMarker> histogramMarkers;
|
2021-01-04 20:41:30 +00:00
|
|
|
std::unique_ptr<QGraphicsSimpleTextItem> title;
|
2021-01-01 21:43:21 +00:00
|
|
|
StatsSeries *highlightedSeries;
|
2021-01-05 11:11:46 +00:00
|
|
|
StatsAxis *xAxis, *yAxis;
|
2021-01-13 12:23:41 +00:00
|
|
|
Legend *draggedItem;
|
|
|
|
QPointF dragStartMouse, dragStartItem;
|
2021-01-01 21:43:21 +00:00
|
|
|
|
statistics: convert chart to QQuickItem
It turns out that the wrong base class was used for the chart.
QQuickWidget can only be used on desktop, not in a mobile UI.
Therefore, turn this into a QQuickItem and move the container
QQuickWidget into desktop-only code.
Currently, this code is insane: The chart is rendered onto a
QGraphicsScene (as it was before), which is then rendered into
a QImage, which is transformed into a QSGTexture, which is then
projected onto the device. This is performed on every mouse
move event, since these events in general change the position
of the info-box.
The plan is to slowly convert elements such as the info-box into
QQuickItems. Browsing the QtQuick documentation, this will
not be much fun.
Also note that the rendering currently tears, flickers and has
antialiasing artifacts, most likely owing to integer (QImage)
to floating point (QGraphicsScene, QQuickItem) conversion
problems. The data flow is
QGraphicsScene (float) -> QImage (int) -> QQuickItem (float).
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-07 13:38:37 +00:00
|
|
|
void hoverEnterEvent(QHoverEvent *event) override;
|
|
|
|
void hoverMoveEvent(QHoverEvent *event) override;
|
2021-01-13 12:23:41 +00:00
|
|
|
void mousePressEvent(QMouseEvent *event) override;
|
|
|
|
void mouseReleaseEvent(QMouseEvent *event) override;
|
|
|
|
void mouseMoveEvent(QMouseEvent *event) override;
|
2021-01-13 15:19:27 +00:00
|
|
|
RootNode *rootNode;
|
2021-01-01 21:43:21 +00:00
|
|
|
};
|
|
|
|
|
2021-01-13 14:17:54 +00:00
|
|
|
// This implementation detail must be known to users of the class.
|
|
|
|
// Perhaps move it into a statsview_impl.h file.
|
|
|
|
template <typename T, class... Args>
|
|
|
|
std::unique_ptr<T> StatsView::createChartItem(Args&&... args)
|
|
|
|
{
|
|
|
|
std::unique_ptr<T> res(new T(*this, std::forward<Args>(args)...));
|
2021-01-13 15:19:27 +00:00
|
|
|
registerChartItem(res.get());
|
2021-01-13 14:17:54 +00:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2021-01-01 21:43:21 +00:00
|
|
|
#endif
|