From e7907c494f7f78831c2e2d523f7eb43e25ee77c0 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Thu, 7 Jan 2021 14:38:37 +0100 Subject: 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 --- stats/barseries.cpp | 31 ++++----- stats/barseries.h | 16 ++--- stats/boxseries.cpp | 21 +++--- stats/boxseries.h | 5 +- stats/informationbox.cpp | 7 +- stats/informationbox.h | 7 +- stats/legend.cpp | 12 ++-- stats/legend.h | 4 +- stats/pieseries.cpp | 21 +++--- stats/pieseries.h | 6 +- stats/qml/statsview.qml | 8 --- stats/qml/statsview.qrc | 5 -- stats/scatterseries.cpp | 13 ++-- stats/scatterseries.h | 5 +- stats/statsaxis.cpp | 60 +++++++++-------- stats/statsaxis.h | 24 +++---- stats/statscolors.h | 1 + stats/statsgrid.cpp | 9 +-- stats/statsgrid.h | 8 +-- stats/statshelper.h | 25 +++++++ stats/statsseries.cpp | 4 +- stats/statsseries.h | 16 ++--- stats/statsview.cpp | 172 +++++++++++++++++++++++------------------------ stats/statsview.h | 45 ++++++------- 24 files changed, 267 insertions(+), 258 deletions(-) delete mode 100644 stats/qml/statsview.qml delete mode 100644 stats/qml/statsview.qrc create mode 100644 stats/statshelper.h (limited to 'stats') diff --git a/stats/barseries.cpp b/stats/barseries.cpp index 5727745a0..986aa7e0d 100644 --- a/stats/barseries.cpp +++ b/stats/barseries.cpp @@ -2,6 +2,7 @@ #include "barseries.h" #include "informationbox.h" #include "statscolors.h" +#include "statshelper.h" #include "statstranslations.h" #include "zvalues.h" @@ -27,19 +28,19 @@ bool BarSeries::Index::operator==(const Index &i2) const return std::tie(bar, subitem) == std::tie(i2.bar, i2.subitem); } -BarSeries::BarSeries(QtCharts::QChart *chart, StatsAxis *xAxis, StatsAxis *yAxis, +BarSeries::BarSeries(QGraphicsScene *scene, StatsAxis *xAxis, StatsAxis *yAxis, bool horizontal, bool stacked, const QString &categoryName, const StatsVariable *valueVariable, std::vector valueBinNames) : - StatsSeries(chart, xAxis, yAxis), + StatsSeries(scene, xAxis, yAxis), horizontal(horizontal), stacked(stacked), categoryName(categoryName), valueVariable(valueVariable), valueBinNames(std::move(valueBinNames)) { } -BarSeries::BarSeries(QtCharts::QChart *chart, StatsAxis *xAxis, StatsAxis *yAxis, +BarSeries::BarSeries(QGraphicsScene *scene, StatsAxis *xAxis, StatsAxis *yAxis, bool horizontal, const QString &categoryName, const std::vector &items) : - BarSeries(chart, xAxis, yAxis, horizontal, false, categoryName, nullptr, std::vector()) + BarSeries(scene, xAxis, yAxis, horizontal, false, categoryName, nullptr, std::vector()) { for (const CountItem &item: items) { StatsOperationResults res; @@ -50,10 +51,10 @@ BarSeries::BarSeries(QtCharts::QChart *chart, StatsAxis *xAxis, StatsAxis *yAxis } } -BarSeries::BarSeries(QtCharts::QChart *chart, StatsAxis *xAxis, StatsAxis *yAxis, +BarSeries::BarSeries(QGraphicsScene *scene, StatsAxis *xAxis, StatsAxis *yAxis, bool horizontal, const QString &categoryName, const StatsVariable *valueVariable, const std::vector &items) : - BarSeries(chart, xAxis, yAxis, horizontal, false, categoryName, valueVariable, std::vector()) + BarSeries(scene, xAxis, yAxis, horizontal, false, categoryName, valueVariable, std::vector()) { for (const ValueItem &item: items) { add_item(item.lowerBound, item.upperBound, makeSubItems(item.value, item.label), @@ -61,11 +62,11 @@ BarSeries::BarSeries(QtCharts::QChart *chart, StatsAxis *xAxis, StatsAxis *yAxis } } -BarSeries::BarSeries(QtCharts::QChart *chart, StatsAxis *xAxis, StatsAxis *yAxis, +BarSeries::BarSeries(QGraphicsScene *scene, StatsAxis *xAxis, StatsAxis *yAxis, bool horizontal, bool stacked, const QString &categoryName, const StatsVariable *valueVariable, std::vector valueBinNames, const std::vector &items) : - BarSeries(chart, xAxis, yAxis, horizontal, stacked, categoryName, valueVariable, std::move(valueBinNames)) + BarSeries(scene, xAxis, yAxis, horizontal, stacked, categoryName, valueVariable, std::move(valueBinNames)) { for (const MultiItem &item: items) { StatsOperationResults res; @@ -85,12 +86,12 @@ BarSeries::~BarSeries() { } -BarSeries::BarLabel::BarLabel(QtCharts::QChart *chart, const std::vector &labels, int bin_nr, int binCount) : +BarSeries::BarLabel::BarLabel(QGraphicsScene *scene, const std::vector &labels, int bin_nr, int binCount) : totalWidth(0.0), totalHeight(0.0), isOutside(false) { items.reserve(labels.size()); for (const QString &label: labels) { - items.emplace_back(new QGraphicsSimpleTextItem(chart)); + items.emplace_back(createItem(scene)); items.back()->setText(label); items.back()->setZValue(ZValues::seriesLabels); QRectF rect = items.back()->boundingRect(); @@ -175,7 +176,7 @@ void BarSeries::BarLabel::updatePosition(bool horizontal, bool center, const QRe highlight(false, bin_nr, binCount); } -BarSeries::Item::Item(QtCharts::QChart *chart, BarSeries *series, double lowerBound, double upperBound, +BarSeries::Item::Item(QGraphicsScene *scene, BarSeries *series, double lowerBound, double upperBound, std::vector subitemsIn, const QString &binName, const StatsOperationResults &res, int total, bool horizontal, bool stacked, int binCount) : @@ -264,9 +265,9 @@ std::vector BarSeries::makeSubItems(const std::vector 0.0) { - res.push_back({ std::make_unique(chart), {}, from, from + v, bin_nr }); + res.push_back({ createItemPtr(scene), {}, from, from + v, bin_nr }); if (!label.empty()) - res.back().label = std::make_unique(chart, label, bin_nr, binCount()); + res.back().label = std::make_unique(scene, label, bin_nr, binCount()); } if (stacked) from += v; @@ -292,7 +293,7 @@ void BarSeries::add_item(double lowerBound, double upperBound, std::vector(scene); information->setText(makeInfo(item, highlighted.subitem), pos); } else { information.reset(); diff --git a/stats/barseries.h b/stats/barseries.h index 114df540a..299998bfb 100644 --- a/stats/barseries.h +++ b/stats/barseries.h @@ -12,9 +12,7 @@ #include #include -namespace QtCharts { - class QAbstractAxis; -} +class QGraphicsScene; class InformationBox; class StatsVariable; @@ -49,13 +47,13 @@ public: // Note: this expects that all items are added with increasing pos // and that no bar is inside another bar, i.e. lowerBound and upperBound // are ordered identically. - BarSeries(QtCharts::QChart *chart, StatsAxis *xAxis, StatsAxis *yAxis, + BarSeries(QGraphicsScene *scene, StatsAxis *xAxis, StatsAxis *yAxis, bool horizontal, const QString &categoryName, const std::vector &items); - BarSeries(QtCharts::QChart *chart, StatsAxis *xAxis, StatsAxis *yAxis, + BarSeries(QGraphicsScene *scene, StatsAxis *xAxis, StatsAxis *yAxis, bool horizontal, const QString &categoryName, const StatsVariable *valueVariable, const std::vector &items); - BarSeries(QtCharts::QChart *chart, StatsAxis *xAxis, StatsAxis *yAxis, + BarSeries(QGraphicsScene *scene, StatsAxis *xAxis, StatsAxis *yAxis, bool horizontal, bool stacked, const QString &categoryName, const StatsVariable *valueVariable, std::vector valueBinNames, const std::vector &items); @@ -65,7 +63,7 @@ public: bool hover(QPointF pos) override; void unhighlight() override; private: - BarSeries(QtCharts::QChart *chart, StatsAxis *xAxis, StatsAxis *yAxis, + BarSeries(QGraphicsScene *scene, StatsAxis *xAxis, StatsAxis *yAxis, bool horizontal, bool stacked, const QString &categoryName, const StatsVariable *valueVariable, std::vector valueBinNames); @@ -85,7 +83,7 @@ private: std::vector> items; double totalWidth, totalHeight; // Size of the item bool isOutside; // Is shown outside of bar - BarLabel(QtCharts::QChart *chart, const std::vector &labels, int bin_nr, int binCount); + BarLabel(QGraphicsScene *scene, const std::vector &labels, int bin_nr, int binCount); void setVisible(bool visible); void updatePosition(bool horizontal, bool center, const QRectF &rect, int bin_nr, int binCount); void highlight(bool highlight, int bin_nr, int binCount); @@ -109,7 +107,7 @@ private: const QString binName; StatsOperationResults res; int total; - Item(QtCharts::QChart *chart, BarSeries *series, double lowerBound, double upperBound, + Item(QGraphicsScene *scene, BarSeries *series, double lowerBound, double upperBound, std::vector subitems, const QString &binName, const StatsOperationResults &res, int total, bool horizontal, bool stacked, int binCount); diff --git a/stats/boxseries.cpp b/stats/boxseries.cpp index 805e8cc8e..c684eff8b 100644 --- a/stats/boxseries.cpp +++ b/stats/boxseries.cpp @@ -2,6 +2,7 @@ #include "boxseries.h" #include "informationbox.h" #include "statscolors.h" +#include "statshelper.h" #include "statstranslations.h" #include "zvalues.h" @@ -12,9 +13,9 @@ static const double boxWidth = 0.8; // 1.0 = full width of category static const int boxBorderWidth = 2; -BoxSeries::BoxSeries(QtCharts::QChart *chart, StatsAxis *xAxis, StatsAxis *yAxis, +BoxSeries::BoxSeries(QGraphicsScene *scene, StatsAxis *xAxis, StatsAxis *yAxis, const QString &variable, const QString &unit, int decimals) : - StatsSeries(chart, xAxis, yAxis), + StatsSeries(scene, xAxis, yAxis), variable(variable), unit(unit), decimals(decimals), highlighted(-1) { } @@ -23,12 +24,8 @@ BoxSeries::~BoxSeries() { } -BoxSeries::Item::Item(QtCharts::QChart *chart, BoxSeries *series, double lowerBound, double upperBound, +BoxSeries::Item::Item(QGraphicsScene *scene, BoxSeries *series, double lowerBound, double upperBound, const StatsQuartiles &q, const QString &binName) : - box(chart), - topWhisker(chart), bottomWhisker(chart), - topBar(chart), bottomBar(chart), - center(chart), lowerBound(lowerBound), upperBound(upperBound), q(q), binName(binName) { @@ -38,6 +35,12 @@ BoxSeries::Item::Item(QtCharts::QChart *chart, BoxSeries *series, double lowerBo topBar.setZValue(ZValues::series); bottomBar.setZValue(ZValues::series); center.setZValue(ZValues::series); + scene->addItem(&box); + scene->addItem(&topWhisker); + scene->addItem(&bottomWhisker); + scene->addItem(&topBar); + scene->addItem(&bottomBar); + scene->addItem(¢er); highlight(false); updatePosition(series); } @@ -89,7 +92,7 @@ void BoxSeries::Item::updatePosition(BoxSeries *series) void BoxSeries::append(double lowerBound, double upperBound, const StatsQuartiles &q, const QString &binName) { - items.emplace_back(new Item(chart, this, lowerBound, upperBound, q, binName)); + items.emplace_back(new Item(scene, this, lowerBound, upperBound, q, binName)); } void BoxSeries::updatePositions() @@ -147,7 +150,7 @@ bool BoxSeries::hover(QPointF pos) Item &item = *items[highlighted]; item.highlight(true); if (!information) - information.reset(new InformationBox(chart)); + information = createItemPtr(scene); information->setText(formatInformation(item), pos); } else { information.reset(); diff --git a/stats/boxseries.h b/stats/boxseries.h index 43b177619..175ad771e 100644 --- a/stats/boxseries.h +++ b/stats/boxseries.h @@ -14,10 +14,11 @@ #include class InformationBox; +class QGraphicsScene; class BoxSeries : public StatsSeries { public: - BoxSeries(QtCharts::QChart *chart, StatsAxis *xAxis, StatsAxis *yAxis, + BoxSeries(QGraphicsScene *scene, StatsAxis *xAxis, StatsAxis *yAxis, const QString &variable, const QString &unit, int decimals); ~BoxSeries(); @@ -44,7 +45,7 @@ private: double lowerBound, upperBound; StatsQuartiles q; QString binName; - Item(QtCharts::QChart *chart, BoxSeries *series, double lowerBound, double upperBound, const StatsQuartiles &q, const QString &binName); + Item(QGraphicsScene *scene, BoxSeries *series, double lowerBound, double upperBound, const StatsQuartiles &q, const QString &binName); void updatePosition(BoxSeries *series); void highlight(bool highlight); }; diff --git a/stats/informationbox.cpp b/stats/informationbox.cpp index 727b6fe83..c11104333 100644 --- a/stats/informationbox.cpp +++ b/stats/informationbox.cpp @@ -4,6 +4,7 @@ #include #include +#include static const QColor informationBorderColor(Qt::black); static const QColor informationColor(0xff, 0xff, 0x00, 192); // Note: fourth argument is opacity @@ -11,7 +12,7 @@ static const int informationBorder = 2; static const double informationBorderRadius = 4.0; // Radius of rounded corners static const int distanceFromPointer = 10; // Distance to place box from mouse pointer or scatter item -InformationBox::InformationBox(QtCharts::QChart *chart) : RoundRectItem(informationBorderRadius, chart), chart(chart) +InformationBox::InformationBox() : RoundRectItem(informationBorderRadius, nullptr) { setPen(QPen(informationBorderColor, informationBorder)); setBrush(informationColor); @@ -37,7 +38,7 @@ void InformationBox::setText(const std::vector &text, QPointF pos) void InformationBox::setPos(QPointF pos) { - QRectF plotArea = chart->plotArea(); + QRectF plotArea = scene()->sceneRect(); double x = pos.x() + distanceFromPointer; if (x + width >= plotArea.right()) { @@ -79,6 +80,6 @@ void InformationBox::addLine(const QString &s) int InformationBox::recommendedMaxLines() const { QFontMetrics fm(font); - int maxHeight = static_cast(chart->plotArea().height()); + int maxHeight = static_cast(scene()->sceneRect().height()); return maxHeight * 2 / fm.height() / 3; } diff --git a/stats/informationbox.h b/stats/informationbox.h index 0c71447aa..741df537f 100644 --- a/stats/informationbox.h +++ b/stats/informationbox.h @@ -10,19 +10,16 @@ #include #include -namespace QtCharts { - class QChart; -} struct dive; +class QGraphicsScene; // Information window showing data of highlighted dive struct InformationBox : RoundRectItem { - InformationBox(QtCharts::QChart *chart); + InformationBox(); void setText(const std::vector &text, QPointF pos); void setPos(QPointF pos); int recommendedMaxLines() const; private: - QtCharts::QChart *chart; QFont font; // For future specialization. double width, height; void addLine(const QString &s); diff --git a/stats/legend.cpp b/stats/legend.cpp index 99340a8f9..27607fb51 100644 --- a/stats/legend.cpp +++ b/stats/legend.cpp @@ -4,8 +4,8 @@ #include "zvalues.h" #include +#include #include -#include #include static const double legendBorderSize = 2.0; @@ -16,8 +16,8 @@ static const double legendInternalBorderSize = 2.0; static const QColor legendColor(0x00, 0x8e, 0xcc, 192); // Note: fourth argument is opacity static const QColor legendBorderColor(Qt::black); -Legend::Legend(QGraphicsWidget *chart, const std::vector &names) : - RoundRectItem(legendBoxBorderRadius, chart), chart(chart), +Legend::Legend(const std::vector &names) : + RoundRectItem(legendBoxBorderRadius), displayedItems(0), width(0.0), height(0.0) { setZValue(ZValues::legend); @@ -40,8 +40,6 @@ Legend::Legend(QGraphicsWidget *chart, const std::vector &names) : } setPen(QPen(legendBorderColor, legendBorderSize)); setBrush(QBrush(legendColor)); - - resize(); // Draw initial legend } Legend::Entry::Entry(const QString &name, int idx, int numBins, QGraphicsItem *parent) : @@ -70,7 +68,7 @@ void Legend::resize() if (entries.empty()) return hide(); - QSizeF size = chart->size(); + QSizeF size = scene()->sceneRect().size(); // Silly heuristics: make the legend at most half as high and half as wide as the chart. // Not sure if that makes sense - this might need some optimization. @@ -110,7 +108,7 @@ void Legend::updatePosition() if (displayedItems <= 0) return hide(); // For now, place the legend in the top right corner. - QPointF pos(chart->size().width() - width - 10.0, 10.0); + QPointF pos(scene()->sceneRect().width() - width - 10.0, 10.0); setRect(QRectF(pos, QSizeF(width, height))); for (int i = 0; i < displayedItems; ++i) { QPointF itemPos = pos + entries[i].pos; diff --git a/stats/legend.h b/stats/legend.h index 2baf6f67b..c643a41f3 100644 --- a/stats/legend.h +++ b/stats/legend.h @@ -8,11 +8,12 @@ #include #include +class QGraphicsScene; class QGraphicsSceneMouseEvent; class Legend : public RoundRectItem { public: - Legend(QGraphicsWidget *chart, const std::vector &names); + Legend(const std::vector &names); void hover(QPointF pos); void resize(); // called when the chart size changes. private: @@ -24,7 +25,6 @@ private: double width; Entry(const QString &name, int idx, int numBins, QGraphicsItem *parent); }; - QGraphicsWidget *chart; int displayedItems; double width; double height; diff --git a/stats/pieseries.cpp b/stats/pieseries.cpp index 108046b23..ac43b6d3a 100644 --- a/stats/pieseries.cpp +++ b/stats/pieseries.cpp @@ -2,6 +2,7 @@ #include "pieseries.h" #include "informationbox.h" #include "statscolors.h" +#include "statshelper.h" #include "statstranslations.h" #include "zvalues.h" @@ -16,9 +17,9 @@ static const double pieBorderWidth = 1.0; static const double innerLabelRadius = 0.75; // 1.0 = at outer border of pie static const double outerLabelRadius = 1.01; // 1.0 = at outer border of pie -PieSeries::Item::Item(QtCharts::QChart *chart, const QString &name, int from, int count, int totalCount, +PieSeries::Item::Item(QGraphicsScene *scene, const QString &name, int from, int count, int totalCount, int bin_nr, int numBins, bool labels) : - item(new QGraphicsEllipseItem(chart)), + item(createItemPtr(scene)), name(name), count(count) { @@ -38,10 +39,10 @@ PieSeries::Item::Item(QtCharts::QChart *chart, const QString &name, int from, in if (labels) { double percentage = count * 100.0 / totalCount; QString innerLabelText = QStringLiteral("%1\%").arg(loc.toString(percentage, 'f', 1)); - innerLabel.reset(new QGraphicsSimpleTextItem(innerLabelText, chart)); + innerLabel = createItemPtr(scene, innerLabelText); innerLabel->setZValue(ZValues::seriesLabels); - outerLabel.reset(new QGraphicsSimpleTextItem(name, chart)); + outerLabel = createItemPtr(scene, name); outerLabel->setBrush(QBrush(darkLabelColor)); outerLabel->setZValue(ZValues::seriesLabels); } @@ -85,9 +86,9 @@ void PieSeries::Item::highlight(int bin_nr, bool highlight, int numBins) } } -PieSeries::PieSeries(QtCharts::QChart *chart, StatsAxis *xAxis, StatsAxis *yAxis, const QString &categoryName, +PieSeries::PieSeries(QGraphicsScene *scene, StatsAxis *xAxis, StatsAxis *yAxis, const QString &categoryName, const std::vector> &data, bool keepOrder, bool labels) : - StatsSeries(chart, xAxis, yAxis), + StatsSeries(scene, xAxis, yAxis), categoryName(categoryName), highlighted(-1) { @@ -147,7 +148,7 @@ PieSeries::PieSeries(QtCharts::QChart *chart, StatsAxis *xAxis, StatsAxis *yAxis int act = 0; for (auto it2 = sorted.begin(); it2 != it; ++it2) { int count = data[*it2].second; - items.emplace_back(chart, data[*it2].first, act, count, totalCount, (int)items.size(), numBins, labels); + items.emplace_back(scene, data[*it2].first, act, count, totalCount, (int)items.size(), numBins, labels); act += count; } @@ -157,7 +158,7 @@ PieSeries::PieSeries(QtCharts::QChart *chart, StatsAxis *xAxis, StatsAxis *yAxis for (auto it2 = it; it2 != sorted.end(); ++it2) other.push_back({ data[*it2].first, data[*it2].second }); QString name = StatsTranslations::tr("other (%1 items)").arg(other.size()); - items.emplace_back(chart, name, act, totalCount - act, totalCount, (int)items.size(), numBins, labels); + items.emplace_back(scene, name, act, totalCount - act, totalCount, (int)items.size(), numBins, labels); } } @@ -167,7 +168,7 @@ PieSeries::~PieSeries() void PieSeries::updatePositions() { - QRectF plotRect = chart->plotArea(); + QRectF plotRect = scene->sceneRect(); center = plotRect.center(); radius = std::min(plotRect.width(), plotRect.height()) * pieSize / 2.0; QRectF rect(center.x() - radius, center.y() - radius, 2.0 * radius, 2.0 * radius); @@ -246,7 +247,7 @@ bool PieSeries::hover(QPointF pos) if (highlighted >= 0 && highlighted < (int)items.size()) { items[highlighted].highlight(highlighted, true, (int)items.size()); if (!information) - information.reset(new InformationBox(chart)); + information = createItemPtr(scene); information->setText(makeInfo(highlighted), pos); } else { information.reset(); diff --git a/stats/pieseries.h b/stats/pieseries.h index 242cfa781..c7e2af319 100644 --- a/stats/pieseries.h +++ b/stats/pieseries.h @@ -11,14 +11,16 @@ class InformationBox; class QGraphicsEllipseItem; +class QGraphicsScene; class QGraphicsSimpleTextItem; +class QRectF; class PieSeries : public StatsSeries { public: // The pie series is initialized with (name, count) pairs. // If keepOrder is false, bins will be sorted by size, otherwise the sorting // of the shown bins will be retained. Small bins are omitted for clarity. - PieSeries(QtCharts::QChart *chart, StatsAxis *xAxis, StatsAxis *yAxis, const QString &categoryName, + PieSeries(QGraphicsScene *scene, StatsAxis *xAxis, StatsAxis *yAxis, const QString &categoryName, const std::vector> &data, bool keepOrder, bool labels); ~PieSeries(); @@ -42,7 +44,7 @@ private: double angleTo; // In fraction of total int count; QPointF innerLabelPos, outerLabelPos; // With respect to a (-1, -1)-(1, 1) rectangle. - Item(QtCharts::QChart *chart, const QString &name, int from, int count, int totalCount, + Item(QGraphicsScene *scene, const QString &name, int from, int count, int totalCount, int bin_nr, int numBins, bool labels); void updatePositions(const QRectF &rect, const QPointF ¢er, double radius); void highlight(int bin_nr, bool highlight, int numBins); diff --git a/stats/qml/statsview.qml b/stats/qml/statsview.qml deleted file mode 100644 index 24f1fe9d3..000000000 --- a/stats/qml/statsview.qml +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -import QtQuick 2.0 -import QtCharts 2.0 - -ChartView { - antialiasing: true - localizeNumbers: true -} diff --git a/stats/qml/statsview.qrc b/stats/qml/statsview.qrc deleted file mode 100644 index aeb65167e..000000000 --- a/stats/qml/statsview.qrc +++ /dev/null @@ -1,5 +0,0 @@ - - - statsview.qml - - diff --git a/stats/scatterseries.cpp b/stats/scatterseries.cpp index de7a8a3bd..67687de72 100644 --- a/stats/scatterseries.cpp +++ b/stats/scatterseries.cpp @@ -2,6 +2,7 @@ #include "scatterseries.h" #include "informationbox.h" #include "statscolors.h" +#include "statshelper.h" #include "statstranslations.h" #include "statsvariables.h" #include "zvalues.h" @@ -16,9 +17,9 @@ static const int scatterItemDiameter = 10; static const int scatterItemBorder = 1; -ScatterSeries::ScatterSeries(QtCharts::QChart *chart, StatsAxis *xAxis, StatsAxis *yAxis, +ScatterSeries::ScatterSeries(QGraphicsScene *scene, StatsAxis *xAxis, StatsAxis *yAxis, const StatsVariable &varX, const StatsVariable &varY) : - StatsSeries(chart, xAxis, yAxis), + StatsSeries(scene, xAxis, yAxis), varX(varX), varY(varY) { } @@ -58,8 +59,8 @@ static const QPixmap &scatterPixmap(bool highlight) return highlight ? *scatterPixmapHighlightedPtr : *scatterPixmapPtr; } -ScatterSeries::Item::Item(QtCharts::QChart *chart, ScatterSeries *series, dive *d, double pos, double value) : - item(new QGraphicsPixmapItem(scatterPixmap(false), chart)), +ScatterSeries::Item::Item(QGraphicsScene *scene, ScatterSeries *series, dive *d, double pos, double value) : + item(createItemPtr(scene, scatterPixmap(false))), d(d), pos(pos), value(value) @@ -82,7 +83,7 @@ void ScatterSeries::Item::highlight(bool highlight) void ScatterSeries::append(dive *d, double pos, double value) { - items.emplace_back(chart, this, d, pos, value); + items.emplace_back(scene, this, d, pos, value); } void ScatterSeries::updatePositions() @@ -173,7 +174,7 @@ bool ScatterSeries::hover(QPointF pos) return false; } else { if (!information) - information.reset(new InformationBox(chart)); + information = createItemPtr(scene); std::vector text; text.reserve(highlighted.size() * 5); diff --git a/stats/scatterseries.h b/stats/scatterseries.h index 212a8e4ea..ec25369ae 100644 --- a/stats/scatterseries.h +++ b/stats/scatterseries.h @@ -11,13 +11,14 @@ #include class QGraphicsPixmapItem; +class QGraphicsScene; class InformationBox; struct StatsVariable; struct dive; class ScatterSeries : public StatsSeries { public: - ScatterSeries(QtCharts::QChart *chart, StatsAxis *xAxis, StatsAxis *yAxis, + ScatterSeries(QGraphicsScene *scene, StatsAxis *xAxis, StatsAxis *yAxis, const StatsVariable &varX, const StatsVariable &varY); ~ScatterSeries(); @@ -36,7 +37,7 @@ private: std::unique_ptr item; dive *d; double pos, value; - Item(QtCharts::QChart *chart, ScatterSeries *series, dive *d, double pos, double value); + Item(QGraphicsScene *scene, ScatterSeries *series, dive *d, double pos, double value); void updatePosition(ScatterSeries *series); void highlight(bool highlight); }; diff --git a/stats/statsaxis.cpp b/stats/statsaxis.cpp index 019a16256..4d70488da 100644 --- a/stats/statsaxis.cpp +++ b/stats/statsaxis.cpp @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include "statsaxis.h" #include "statscolors.h" +#include "statshelper.h" #include "statstranslations.h" #include "statsvariables.h" #include "zvalues.h" @@ -23,9 +24,8 @@ static const double axisLabelSpaceVertical = 2.0; // Space between axis or ticks static const double axisTitleSpaceHorizontal = 2.0; // Space between labels and title static const double axisTitleSpaceVertical = 2.0; // Space between labels and title -StatsAxis::StatsAxis(QtCharts::QChart *chart, const QString &titleIn, bool horizontal, bool labelsBetweenTicks) : - QGraphicsLineItem(chart), - chart(chart), horizontal(horizontal), labelsBetweenTicks(labelsBetweenTicks), +StatsAxis::StatsAxis(const QString &titleIn, bool horizontal, bool labelsBetweenTicks) : + horizontal(horizontal), labelsBetweenTicks(labelsBetweenTicks), size(1.0), zeroOnScreen(0.0), min(0.0), max(1.0), labelWidth(0.0) { // use a Light version of the application fond for both labels and title @@ -35,7 +35,7 @@ StatsAxis::StatsAxis(QtCharts::QChart *chart, const QString &titleIn, bool horiz setPen(QPen(axisColor, axisWidth)); setZValue(ZValues::axes); if (!titleIn.isEmpty()) { - title = std::make_unique(titleIn, chart); + title.reset(new QGraphicsSimpleTextItem(titleIn, this)); title->setFont(titleFont); title->setBrush(darkLabelColor); if (!horizontal) @@ -114,8 +114,8 @@ double StatsAxis::height() const (labelsBetweenTicks ? 0.0 : axisTickSizeHorizontal); } -StatsAxis::Label::Label(const QString &name, double pos, QtCharts::QChart *chart, const QFont &font) : - label(new QGraphicsSimpleTextItem(name, chart)), +StatsAxis::Label::Label(const QString &name, double pos, QGraphicsScene *scene, const QFont &font) : + label(createItem(scene, name)), pos(pos) { label->setBrush(QBrush(darkLabelColor)); @@ -125,11 +125,11 @@ StatsAxis::Label::Label(const QString &name, double pos, QtCharts::QChart *chart void StatsAxis::addLabel(const QString &label, double pos) { - labels.emplace_back(label, pos, chart, labelFont); + labels.emplace_back(label, pos, scene(), labelFont); } -StatsAxis::Tick::Tick(double pos, QtCharts::QChart *chart) : - item(new QGraphicsLineItem(chart)), +StatsAxis::Tick::Tick(double pos, QGraphicsScene *scene) : + item(createItemPtr(scene)), pos(pos) { item->setPen(QPen(axisColor, axisTickWidth)); @@ -138,7 +138,7 @@ StatsAxis::Tick::Tick(double pos, QtCharts::QChart *chart) : void StatsAxis::addTick(double pos) { - ticks.emplace_back(pos, chart); + ticks.emplace_back(pos, scene()); } std::vector StatsAxis::ticksPositions() const @@ -220,8 +220,8 @@ void StatsAxis::setPos(QPointF pos) } } -ValueAxis::ValueAxis(QtCharts::QChart *chart, const QString &title, double min, double max, int decimals, bool horizontal) : - StatsAxis(chart, title, horizontal, false), +ValueAxis::ValueAxis(const QString &title, double min, double max, int decimals, bool horizontal) : + StatsAxis(title, horizontal, false), min(min), max(max), decimals(decimals) { } @@ -275,8 +275,8 @@ void ValueAxis::updateLabels() } } -CountAxis::CountAxis(QtCharts::QChart *chart, const QString &title, int count, bool horizontal) : - ValueAxis(chart, title, 0.0, (double)count, 0, horizontal), +CountAxis::CountAxis(const QString &title, int count, bool horizontal) : + ValueAxis(title, 0.0, (double)count, 0, horizontal), count(count) { } @@ -328,27 +328,31 @@ void CountAxis::updateLabels() } } -CategoryAxis::CategoryAxis(QtCharts::QChart *chart, const QString &title, const std::vector &labelsIn, bool horizontal) : - StatsAxis(chart, title, horizontal, true) +CategoryAxis::CategoryAxis(const QString &title, const std::vector &labels, bool horizontal) : + StatsAxis(title, horizontal, true), + labelsText(labels) { - labels.reserve(labelsIn.size()); - ticks.reserve(labelsIn.size() + 1); + setRange(-0.5, static_cast(labels.size()) + 0.5); +} + +void CategoryAxis::updateLabels() +{ + // TODO: paint ellipses if space too small + labels.clear(); + ticks.clear(); + labels.reserve(labelsText.size()); + ticks.reserve(labelsText.size() + 1); double pos = 0.0; addTick(-0.5); - for (const QString &s: labelsIn) { + for (const QString &s: labelsText) { addLabel(s, pos); addTick(pos + 0.5); pos += 1.0; } - setRange(-0.5, static_cast(labelsIn.size()) - 0.5); -} - -void CategoryAxis::updateLabels() -{ } -HistogramAxis::HistogramAxis(QtCharts::QChart *chart, const QString &title, std::vector bins, bool horizontal) : - StatsAxis(chart, title, horizontal, false), +HistogramAxis::HistogramAxis(const QString &title, std::vector bins, bool horizontal) : + StatsAxis(title, horizontal, false), bin_values(std::move(bins)) { if (bin_values.size() < 2) // Less than two makes no sense -> there must be at least one category @@ -545,7 +549,7 @@ static std::vector timeRangeToBins(double from, double to) return res; } -DateAxis::DateAxis(QtCharts::QChart *chart, const QString &title, double from, double to, bool horizontal) : - HistogramAxis(chart, title, timeRangeToBins(from, to), horizontal) +DateAxis::DateAxis(const QString &title, double from, double to, bool horizontal) : + HistogramAxis(title, timeRangeToBins(from, to), horizontal) { } diff --git a/stats/statsaxis.h b/stats/statsaxis.h index d9fdd6b41..dbcfb6fbd 100644 --- a/stats/statsaxis.h +++ b/stats/statsaxis.h @@ -11,11 +11,9 @@ #include #include -namespace QtCharts { - class QChart; -} +class QGraphicsScene; -class StatsAxis : QGraphicsLineItem { +class StatsAxis : public QGraphicsLineItem { public: virtual ~StatsAxis(); // Returns minimum and maximum of shown range, not of data points. @@ -34,13 +32,12 @@ public: std::vector ticksPositions() const; // Positions in screen coordinates protected: - StatsAxis(QtCharts::QChart *chart, const QString &title, bool horizontal, bool labelsBetweenTicks); - QtCharts::QChart *chart; + StatsAxis(const QString &title, bool horizontal, bool labelsBetweenTicks); struct Label { std::unique_ptr label; double pos; - Label(const QString &name, double pos, QtCharts::QChart *chart, const QFont &font); + Label(const QString &name, double pos, QGraphicsScene *scene, const QFont &font); }; std::vector