summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt2
-rw-r--r--backend-shared/roundrectitem.cpp4
-rw-r--r--backend-shared/roundrectitem.h1
-rw-r--r--desktop-widgets/qml/statsview2.qml6
-rw-r--r--desktop-widgets/qml/statsview2.qrc (renamed from stats/qml/statsview.qrc)2
-rw-r--r--desktop-widgets/statswidget.cpp17
-rw-r--r--desktop-widgets/statswidget.h2
-rw-r--r--desktop-widgets/statswidget.ui10
-rw-r--r--stats/barseries.cpp31
-rw-r--r--stats/barseries.h16
-rw-r--r--stats/boxseries.cpp21
-rw-r--r--stats/boxseries.h5
-rw-r--r--stats/informationbox.cpp7
-rw-r--r--stats/informationbox.h7
-rw-r--r--stats/legend.cpp12
-rw-r--r--stats/legend.h4
-rw-r--r--stats/pieseries.cpp21
-rw-r--r--stats/pieseries.h6
-rw-r--r--stats/qml/statsview.qml8
-rw-r--r--stats/scatterseries.cpp13
-rw-r--r--stats/scatterseries.h5
-rw-r--r--stats/statsaxis.cpp60
-rw-r--r--stats/statsaxis.h24
-rw-r--r--stats/statscolors.h1
-rw-r--r--stats/statsgrid.cpp9
-rw-r--r--stats/statsgrid.h8
-rw-r--r--stats/statshelper.h25
-rw-r--r--stats/statsseries.cpp4
-rw-r--r--stats/statsseries.h16
-rw-r--r--stats/statsview.cpp172
-rw-r--r--stats/statsview.h45
-rw-r--r--subsurface-helper.cpp2
32 files changed, 298 insertions, 268 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d25313e96..0fa9032ec 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -301,7 +301,7 @@ endif()
#set up the subsurface_link_libraries variable
set(SUBSURFACE_LINK_LIBRARIES ${SUBSURFACE_LINK_LIBRARIES} ${LIBDIVECOMPUTER_LIBRARIES} ${LIBGIT2_LIBRARIES} ${LIBUSB_LIBRARIES} ${LIBMTP_LIBRARIES})
if (NOT SUBSURFACE_TARGET_EXECUTABLE MATCHES "DownloaderExecutable")
- qt5_add_resources(SUBSURFACE_RESOURCES subsurface.qrc map-widget/qml/map-widget.qrc stats/qml/statsview.qrc)
+ qt5_add_resources(SUBSURFACE_RESOURCES subsurface.qrc map-widget/qml/map-widget.qrc desktop-widgets/qml/statsview2.qrc)
endif()
# hack to build successfully on LGTM
diff --git a/backend-shared/roundrectitem.cpp b/backend-shared/roundrectitem.cpp
index 2dbfd7b03..52200b017 100644
--- a/backend-shared/roundrectitem.cpp
+++ b/backend-shared/roundrectitem.cpp
@@ -7,6 +7,10 @@ RoundRectItem::RoundRectItem(double radius, QGraphicsItem *parent) : QGraphicsRe
{
}
+RoundRectItem::RoundRectItem(double radius) : RoundRectItem(radius, nullptr)
+{
+}
+
void RoundRectItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *)
{
painter->save();
diff --git a/backend-shared/roundrectitem.h b/backend-shared/roundrectitem.h
index d3f58782f..b8cb3f89b 100644
--- a/backend-shared/roundrectitem.h
+++ b/backend-shared/roundrectitem.h
@@ -7,6 +7,7 @@
class RoundRectItem : public QGraphicsRectItem {
public:
RoundRectItem(double radius, QGraphicsItem *parent);
+ RoundRectItem(double radius);
private:
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
double radius;
diff --git a/desktop-widgets/qml/statsview2.qml b/desktop-widgets/qml/statsview2.qml
new file mode 100644
index 000000000..ccad7fd1e
--- /dev/null
+++ b/desktop-widgets/qml/statsview2.qml
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
+import QtQuick 2.0
+import org.subsurfacedivelog.mobile 1.0
+
+StatsView {
+}
diff --git a/stats/qml/statsview.qrc b/desktop-widgets/qml/statsview2.qrc
index aeb65167e..d54f6c6b9 100644
--- a/stats/qml/statsview.qrc
+++ b/desktop-widgets/qml/statsview2.qrc
@@ -1,5 +1,5 @@
<RCC>
<qresource prefix="/qml">
- <file>statsview.qml</file>
+ <file>statsview2.qml</file>
</qresource>
</RCC>
diff --git a/desktop-widgets/statswidget.cpp b/desktop-widgets/statswidget.cpp
index 01fd16eb1..e0090395f 100644
--- a/desktop-widgets/statswidget.cpp
+++ b/desktop-widgets/statswidget.cpp
@@ -70,6 +70,7 @@ QSize ChartItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QMod
return size;
}
+static const QUrl urlStatsView = QUrl(QStringLiteral("qrc:/qml/statsview2.qml"));
StatsWidget::StatsWidget(QWidget *parent) : QWidget(parent)
{
ui.setupUi(this);
@@ -83,6 +84,13 @@ StatsWidget::StatsWidget(QWidget *parent) : QWidget(parent)
connect(ui.var1Binner, QOverload<int>::of(&QComboBox::activated), this, &StatsWidget::var1BinnerChanged);
connect(ui.var2Binner, QOverload<int>::of(&QComboBox::activated), this, &StatsWidget::var2BinnerChanged);
connect(ui.var2Operation, QOverload<int>::of(&QComboBox::activated), this, &StatsWidget::var2OperationChanged);
+
+ ui.stats->setSource(urlStatsView);
+ ui.stats->setResizeMode(QQuickWidget::SizeRootObjectToView);
+ QQuickItem *root = ui.stats->rootObject();
+ view = qobject_cast<StatsView *>(root);
+ if (!view)
+ qWarning("Oops. The root of the StatsView is not a StatsView.");
}
// Initialize QComboBox with list of variables
@@ -96,7 +104,7 @@ static void setVariableList(QComboBox *combo, const StatsState::VariableList &li
}
// Initialize QComboBox and QLabel of binners. Hide if there are no binners.
-static void setBinList(QLabel *label, QComboBox *combo, const StatsState::BinnerList &list)
+static void setBinList(QComboBox *combo, const StatsState::BinnerList &list)
{
combo->clear();
combo->setEnabled(!list.binners.empty());
@@ -114,8 +122,8 @@ void StatsWidget::updateUi()
int pos = charts.update(uiState.charts);
ui.chartType->setCurrentIndex(pos);
ui.chartType->setItemDelegate(new ChartItemDelegate);
- setBinList(ui.var1BinnerLabel, ui.var1Binner, uiState.binners1);
- setBinList(ui.var2BinnerLabel, ui.var2Binner, uiState.binners2);
+ setBinList(ui.var1Binner, uiState.binners1);
+ setBinList(ui.var2Binner, uiState.binners2);
setVariableList(ui.var2Operation, uiState.operations2);
// Add checkboxes for additional features
@@ -129,7 +137,8 @@ void StatsWidget::updateUi()
ui.features->addWidget(check);
}
- ui.stats->plot(state);
+ if (view)
+ view->plot(state);
}
void StatsWidget::closeStats()
diff --git a/desktop-widgets/statswidget.h b/desktop-widgets/statswidget.h
index 40e6d106c..b85d89730 100644
--- a/desktop-widgets/statswidget.h
+++ b/desktop-widgets/statswidget.h
@@ -9,6 +9,7 @@
#include <memory>
class QCheckBox;
+class StatsView;
class StatsWidget : public QWidget {
Q_OBJECT
@@ -27,6 +28,7 @@ slots:
private:
Ui::StatsWidget ui;
StatsState state;
+ StatsView *view;
void updateUi();
std::vector<std::unique_ptr<QCheckBox>> features;
diff --git a/desktop-widgets/statswidget.ui b/desktop-widgets/statswidget.ui
index 684b0fa58..a20fb4a8f 100644
--- a/desktop-widgets/statswidget.ui
+++ b/desktop-widgets/statswidget.ui
@@ -105,7 +105,7 @@
</layout>
</item>
<item>
- <widget class="StatsView" name="stats">
+ <widget class="QQuickWidget" name="stats">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
@@ -116,14 +116,6 @@
</item>
</layout>
</widget>
- <customwidgets>
- <customwidget>
- <class>StatsView</class>
- <extends>QQuickWidget</extends>
- <header>stats/statsview.h</header>
- <container>1</container>
- </customwidget>
- </customwidgets>
<resources>
<include location="../subsurface.qrc"/>
</resources>
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<QString> 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<CountItem> &items) :
- BarSeries(chart, xAxis, yAxis, horizontal, false, categoryName, nullptr, std::vector<QString>())
+ BarSeries(scene, xAxis, yAxis, horizontal, false, categoryName, nullptr, std::vector<QString>())
{
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<ValueItem> &items) :
- BarSeries(chart, xAxis, yAxis, horizontal, false, categoryName, valueVariable, std::vector<QString>())
+ BarSeries(scene, xAxis, yAxis, horizontal, false, categoryName, valueVariable, std::vector<QString>())
{
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<QString> valueBinNames,
const std::vector<MultiItem> &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<QString> &labels, int bin_nr, int binCount) :
+BarSeries::BarLabel::BarLabel(QGraphicsScene *scene, const std::vector<QString> &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<QGraphicsSimpleTextItem>(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<SubItem> subitemsIn,
const QString &binName, const StatsOperationResults &res, int total,
bool horizontal, bool stacked, int binCount) :
@@ -264,9 +265,9 @@ std::vector<BarSeries::SubItem> BarSeries::makeSubItems(const std::vector<std::p
int bin_nr = 0;
for (auto &[v, label]: values) {
if (v > 0.0) {
- res.push_back({ std::make_unique<QGraphicsRectItem>(chart), {}, from, from + v, bin_nr });
+ res.push_back({ createItemPtr<QGraphicsRectItem>(scene), {}, from, from + v, bin_nr });
if (!label.empty())
- res.back().label = std::make_unique<BarLabel>(chart, label, bin_nr, binCount());
+ res.back().label = std::make_unique<BarLabel>(scene, label, bin_nr, binCount());
}
if (stacked)
from += v;
@@ -292,7 +293,7 @@ void BarSeries::add_item(double lowerBound, double upperBound, std::vector<SubIt
// Don't add empty items, as that messes with the "find item under mouse" routine.
if (subitems.empty())
return;
- items.emplace_back(chart, this, lowerBound, upperBound, std::move(subitems), binName, res,
+ items.emplace_back(scene, this, lowerBound, upperBound, std::move(subitems), binName, res,
total, horizontal, stacked, binCount());
}
@@ -403,7 +404,7 @@ bool BarSeries::hover(QPointF pos)
Item &item = items[highlighted.bar];
item.highlight(index.subitem, true, binCount());
if (!information)
- information.reset(new InformationBox(chart));
+ information = createItemPtr<InformationBox>(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 <vector>
#include <QGraphicsRectItem>
-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<CountItem> &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<ValueItem> &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<QString> valueBinNames,
const std::vector<MultiItem> &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<QString> valueBinNames);
@@ -85,7 +83,7 @@ private:
std::vector<std::unique_ptr<QGraphicsSimpleTextItem>> items;
double totalWidth, totalHeight; // Size of the item
bool isOutside; // Is shown outside of bar
- BarLabel(QtCharts::QChart *chart, const std::vector<QString> &labels, int bin_nr, int binCount);
+ BarLabel(QGraphicsScene *scene, const std::vector<QString> &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<SubItem> 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(&center);
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<InformationBox>(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 <QGraphicsRectItem>
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 <QChart>
#include <QFontMetrics>
+#include <QGraphicsScene>
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<QString> &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<int>(chart->plotArea().height());
+ int maxHeight = static_cast<int>(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 <memory>
#include <QFont>
-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<QString> &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 <QFontMetrics>
+#include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent>
-#include <QGraphicsWidget>
#include <QPen>
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<QString> &names) :
- RoundRectItem(legendBoxBorderRadius, chart), chart(chart),
+Legend::Legend(const std::vector<QString> &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<QString> &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 <memory>
#include <vector>
+class QGraphicsScene;
class QGraphicsSceneMouseEvent;
class Legend : public RoundRectItem {
public:
- Legend(QGraphicsWidget *chart, const std::vector<QString> &names);
+ Legend(const std::vector<QString> &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<QGraphicsEllipseItem>(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<QGraphicsSimpleTextItem>(scene, innerLabelText);
innerLabel->setZValue(ZValues::seriesLabels);
- outerLabel.reset(new QGraphicsSimpleTextItem(name, chart));
+ outerLabel = createItemPtr<QGraphicsSimpleTextItem>(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<std::pair<QString, int>> &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<InformationBox>(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<std::pair<QString, int>> &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 &center, 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/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<QGraphicsPixmapItem>(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<InformationBox>(scene);
std::vector<QString> 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 <QGraphicsRectItem>
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<QGraphicsPixmapItem> 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<QGraphicsSimpleTextItem>(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<QGraphicsSimpleTextItem>(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<QGraphicsLineItem>(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<double> 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<QString> &labelsIn, bool horizontal) :
- StatsAxis(chart, title, horizontal, true)
+CategoryAxis::CategoryAxis(const QString &title, const std::vector<QString> &labels, bool horizontal) :
+ StatsAxis(title, horizontal, true),
+ labelsText(labels)
{
- labels.reserve(labelsIn.size());
- ticks.reserve(labelsIn.size() + 1);
+ setRange(-0.5, static_cast<double>(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<double>(labelsIn.size()) - 0.5);
-}
-
-void CategoryAxis::updateLabels()
-{
}
-HistogramAxis::HistogramAxis(QtCharts::QChart *chart, const QString &title, std::vector<HistogramAxisEntry> bins, bool horizontal) :
- StatsAxis(chart, title, horizontal, false),
+HistogramAxis::HistogramAxis(const QString &title, std::vector<HistogramAxisEntry> 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<HistogramAxisEntry> 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 <QGraphicsLineItem>
#include <QValueAxis>
-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<double> 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<QGraphicsSimpleTextItem> 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<Label> labels;
void addLabel(const QString &label, double pos);
@@ -49,7 +46,7 @@ protected:
struct Tick {
std::unique_ptr<QGraphicsLineItem> item;
double pos;
- Tick(double pos, QtCharts::QChart *chart);
+ Tick(double pos, QGraphicsScene *scene);
};
std::vector<Tick> ticks;
void addTick(double pos);
@@ -70,7 +67,7 @@ private:
class ValueAxis : public StatsAxis {
public:
- ValueAxis(QtCharts::QChart *chart, const QString &title, double min, double max, int decimals, bool horizontal);
+ ValueAxis(const QString &title, double min, double max, int decimals, bool horizontal);
private:
double min, max;
int decimals;
@@ -79,7 +76,7 @@ private:
class CountAxis : public ValueAxis {
public:
- CountAxis(QtCharts::QChart *chart, const QString &title, int count, bool horizontal);
+ CountAxis(const QString &title, int count, bool horizontal);
private:
int count;
void updateLabels() override;
@@ -87,8 +84,9 @@ private:
class CategoryAxis : public StatsAxis {
public:
- CategoryAxis(QtCharts::QChart *chart, const QString &title, const std::vector<QString> &labels, bool horizontal);
+ CategoryAxis(const QString &title, const std::vector<QString> &labels, bool horizontal);
private:
+ std::vector<QString> labelsText;
void updateLabels();
};
@@ -100,7 +98,7 @@ struct HistogramAxisEntry {
class HistogramAxis : public StatsAxis {
public:
- HistogramAxis(QtCharts::QChart *chart, const QString &title, std::vector<HistogramAxisEntry> bin_values, bool horizontal);
+ HistogramAxis(const QString &title, std::vector<HistogramAxisEntry> bin_values, bool horizontal);
private:
void updateLabels() override;
std::vector<HistogramAxisEntry> bin_values;
@@ -109,7 +107,7 @@ private:
class DateAxis : public HistogramAxis {
public:
- DateAxis(QtCharts::QChart *chart, const QString &title, double from, double to, bool horizontal);
+ DateAxis(const QString &title, double from, double to, bool horizontal);
};
#endif
diff --git a/stats/statscolors.h b/stats/statscolors.h
index e9775e2a6..050b8a3ab 100644
--- a/stats/statscolors.h
+++ b/stats/statscolors.h
@@ -5,6 +5,7 @@
#include <QColor>
+inline const QColor backgroundColor(Qt::white);
inline const QColor fillColor(0x44, 0x76, 0xaa);
inline const QColor borderColor(0x66, 0xb2, 0xff);
inline const QColor highlightedColor(Qt::yellow);
diff --git a/stats/statsgrid.cpp b/stats/statsgrid.cpp
index fe99b1762..11b05882b 100644
--- a/stats/statsgrid.cpp
+++ b/stats/statsgrid.cpp
@@ -2,6 +2,7 @@
#include "statsgrid.h"
#include "statsaxis.h"
#include "statscolors.h"
+#include "statshelper.h"
#include "zvalues.h"
#include <QChart>
@@ -10,8 +11,8 @@
static const double gridWidth = 1.0;
static const Qt::PenStyle gridStyle = Qt::SolidLine;
-StatsGrid::StatsGrid(QtCharts::QChart *chart, const StatsAxis &xAxis, const StatsAxis &yAxis)
- : chart(chart), xAxis(xAxis), yAxis(yAxis)
+StatsGrid::StatsGrid(QGraphicsScene *scene, const StatsAxis &xAxis, const StatsAxis &yAxis)
+ : scene(scene), xAxis(xAxis), yAxis(yAxis)
{
}
@@ -24,12 +25,12 @@ void StatsGrid::updatePositions()
return;
for (double x: xtics) {
- lines.emplace_back(new QGraphicsLineItem(x, ytics.front(), x, ytics.back(), chart));
+ lines.emplace_back(createItem<QGraphicsLineItem>(scene, x, ytics.front(), x, ytics.back()));
lines.back()->setPen(QPen(gridColor, gridWidth, gridStyle));
lines.back()->setZValue(ZValues::grid);
}
for (double y: ytics) {
- lines.emplace_back(new QGraphicsLineItem(xtics.front(), y, xtics.back(), y, chart));
+ lines.emplace_back(createItem<QGraphicsLineItem>(scene, xtics.front(), y, xtics.back(), y));
lines.back()->setPen(QPen(gridColor, gridWidth, gridStyle));
lines.back()->setZValue(ZValues::grid);
}
diff --git a/stats/statsgrid.h b/stats/statsgrid.h
index 0fdb2a188..47b48b3ac 100644
--- a/stats/statsgrid.h
+++ b/stats/statsgrid.h
@@ -7,16 +7,14 @@
#include <QGraphicsLineItem>
class StatsAxis;
-namespace QtCharts {
- class QChart;
-};
+class QGraphicsScene;
class StatsGrid {
public:
- StatsGrid(QtCharts::QChart *chart, const StatsAxis &xAxis, const StatsAxis &yAxis);
+ StatsGrid(QGraphicsScene *scene, const StatsAxis &xAxis, const StatsAxis &yAxis);
void updatePositions();
private:
- QtCharts::QChart *chart;
+ QGraphicsScene *scene;
const StatsAxis &xAxis, &yAxis;
std::vector<std::unique_ptr<QGraphicsLineItem>> lines;
};
diff --git a/stats/statshelper.h b/stats/statshelper.h
new file mode 100644
index 000000000..0ea39763d
--- /dev/null
+++ b/stats/statshelper.h
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0
+// Helper functions to render the stats. Currently only
+// contains a small template to create scene-items. This
+// is for historical reasons to ease transition from QtCharts
+// and might be removed.
+#ifndef STATSHELPER_H
+
+#include <memory>
+#include <QGraphicsScene>
+
+template <typename T, class... Args>
+T *createItem(QGraphicsScene *scene, Args&&... args)
+{
+ T *res = new T(std::forward<Args>(args)...);
+ scene->addItem(res);
+ return res;
+}
+
+template <typename T, class... Args>
+std::unique_ptr<T> createItemPtr(QGraphicsScene *scene, Args&&... args)
+{
+ return std::unique_ptr<T>(createItem<T>(scene, std::forward<Args>(args)...));
+}
+
+#endif
diff --git a/stats/statsseries.cpp b/stats/statsseries.cpp
index 5996508a4..2b7a5adea 100644
--- a/stats/statsseries.cpp
+++ b/stats/statsseries.cpp
@@ -2,8 +2,8 @@
#include "statsseries.h"
#include "statsaxis.h"
-StatsSeries::StatsSeries(QtCharts::QChart *chart, StatsAxis *xAxis, StatsAxis *yAxis) :
- chart(chart), xAxis(xAxis), yAxis(yAxis)
+StatsSeries::StatsSeries(QGraphicsScene *scene, StatsAxis *xAxis, StatsAxis *yAxis) :
+ scene(scene), xAxis(xAxis), yAxis(yAxis)
{
}
diff --git a/stats/statsseries.h b/stats/statsseries.h
index 5c1833c8a..2494569e6 100644
--- a/stats/statsseries.h
+++ b/stats/statsseries.h
@@ -4,26 +4,20 @@
#ifndef STATS_SERIES_H
#define STATS_SERIES_H
-#include <QScatterSeries>
+#include <QPointF>
-namespace QtCharts {
- class QChart;
-}
+class QGraphicsScene;
class StatsAxis;
-// We derive from a proper scatter series to get access to the map-to
-// and map-from coordinates calls. But we don't use any of its functionality.
-// This should be removed in due course.
-
-class StatsSeries : public QtCharts::QScatterSeries {
+class StatsSeries {
public:
- StatsSeries(QtCharts::QChart *chart, StatsAxis *xAxis, StatsAxis *yAxis);
+ StatsSeries(QGraphicsScene *scene, StatsAxis *xAxis, StatsAxis *yAxis);
virtual ~StatsSeries();
virtual void updatePositions() = 0; // Called if chart geometry changes.
virtual bool hover(QPointF pos) = 0; // Called on mouse movement. Return true if an item of this series is highlighted.
virtual void unhighlight() = 0; // Unhighlight any highlighted item.
protected:
- QtCharts::QChart *chart;
+ QGraphicsScene *scene;
StatsAxis *xAxis, *yAxis; // May be zero for charts without axes (pie charts).
QPointF toScreen(QPointF p);
};
diff --git a/stats/statsview.cpp b/stats/statsview.cpp
index f42296fc2..1533583ba 100644
--- a/stats/statsview.cpp
+++ b/stats/statsview.cpp
@@ -6,7 +6,9 @@
#include "pieseries.h"
#include "scatterseries.h"
#include "statsaxis.h"
+#include "statscolors.h"
#include "statsgrid.h"
+#include "statshelper.h"
#include "statsstate.h"
#include "statstranslations.h"
#include "statsvariables.h"
@@ -15,12 +17,35 @@
#include "core/subsurface-qt/divelistnotifier.h"
#include <cmath>
-#include <QQuickItem>
-#include <QAbstractSeries>
-#include <QChart>
+#include <QGraphicsScene>
#include <QGraphicsSceneHoverEvent>
#include <QGraphicsSimpleTextItem>
-#include <QLocale>
+#include <QQuickItem>
+#include <QQuickWindow>
+#include <QSGImageNode>
+#include <QSGTexture>
+
+QSGNode *StatsView::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *)
+{
+ // The QtQuick drawing interface is utterly bizzare with a distinct 1980ies-style memory management.
+ // This is just a copy of what is found in Qt's documentation.
+ QSGImageNode *n = static_cast<QSGImageNode *>(oldNode);
+ if (!n)
+ n = window()->createImageNode();
+
+ QRectF rect = boundingRect();
+ if (plotRect != rect) {
+ plotRect = rect;
+ plotAreaChanged(plotRect.size());
+ }
+
+ img->fill(backgroundColor);
+ scene.render(painter.get());
+ texture.reset(window()->createTextureFromImage(*img, QQuickWindow::TextureIsOpaque));
+ n->setTexture(texture.get());
+ n->setRect(rect);
+ return n;
+}
// Constants that control the graph layouts
static const QColor quartileMarkerColor(Qt::red);
@@ -28,76 +53,45 @@ static const double quartileMarkerSize = 15.0;
static const double sceneBorder = 5.0; // Border between scene edges and statitistics view
static const double titleBorder = 2.0; // Border between title and chart
-static const QUrl urlStatsView = QUrl(QStringLiteral("qrc:/qml/statsview.qml"));
-
-// We use QtQuick's ChartView so that we can show the statistics on mobile.
-// However, accessing the ChartView from C++ is maliciously cumbersome and
-// the full QChart interface is not exported. Fortunately, the interface
-// leaks the QChart object: We can create a dummy-series and access the chart
-// object via the chart() accessor function. By creating a "PieSeries", the
-// ChartView does not automatically add axes.
-static QtCharts::QChart *getChart(QQuickItem *item)
-{
- QtCharts::QAbstractSeries *abstract_series;
- if (!item)
- return nullptr;
- if (!QMetaObject::invokeMethod(item, "createSeries", Qt::AutoConnection,
- Q_RETURN_ARG(QtCharts::QAbstractSeries *, abstract_series),
- Q_ARG(int, QtCharts::QAbstractSeries::SeriesTypePie),
- Q_ARG(QString, QString()))) {
- qWarning("Couldn't call createSeries()");
- return nullptr;
- }
- QtCharts::QChart *res = abstract_series->chart();
- res->removeSeries(abstract_series);
- delete abstract_series;
- return res;
-}
-
-bool StatsView::EventFilter::eventFilter(QObject *o, QEvent *event)
-{
- if (event->type() == QEvent::GraphicsSceneHoverMove) {
- QGraphicsSceneHoverEvent *hover = static_cast<QGraphicsSceneHoverEvent *>(event);
- view->hover(hover->pos());
- return true;
- }
- return QObject::eventFilter(o, event);
-}
-
-StatsView::StatsView(QWidget *parent) : QQuickWidget(parent),
+StatsView::StatsView(QQuickItem *parent) : QQuickItem(parent),
highlightedSeries(nullptr),
xAxis(nullptr),
- yAxis(nullptr),
- eventFilter(this)
+ yAxis(nullptr)
{
- setResizeMode(QQuickWidget::SizeRootObjectToView);
- // if we get a failure to load the QML file (e.g., when the QtCharts QML modules aren't found)
- // the chart will be null
- setSource(urlStatsView);
- chart = getChart(rootObject());
- if (chart) {
- connect(chart, &QtCharts::QChart::plotAreaChanged, this, &StatsView::plotAreaChanged);
- connect(&diveListNotifier, &DiveListNotifier::numShownChanged, this, &StatsView::replotIfVisible);
-
- chart->installEventFilter(&eventFilter);
- chart->setAcceptHoverEvents(true);
- chart->legend()->setVisible(false);
- }
+ setFlag(ItemHasContents, true);
+
+ connect(&diveListNotifier, &DiveListNotifier::numShownChanged, this, &StatsView::replotIfVisible);
+
+ setAcceptHoverEvents(true);
QFont font;
titleFont = QFont(font.family(), font.pointSize(), QFont::Light); // Make configurable
}
+StatsView::StatsView() : StatsView(nullptr)
+{
+}
+
StatsView::~StatsView()
{
}
-void StatsView::plotAreaChanged(const QRectF &r)
+void StatsView::plotAreaChanged(const QSizeF &s)
{
- double left = r.x() + sceneBorder;
- double top = r.y() + sceneBorder;
- double right = r.right() - sceneBorder;
- double bottom = r.bottom() - sceneBorder;
+ // Make sure that image is at least one pixel wide / high, otherwise
+ // the painter starts acting up.
+ int w = std::max(1, static_cast<int>(floor(s.width())));
+ int h = std::max(1, static_cast<int>(floor(s.height())));
+ scene.setSceneRect(QRectF(0, 0, static_cast<double>(w), static_cast<double>(h)));
+ painter.reset();
+ img.reset(new QImage(w, h, QImage::Format_RGB32));
+ painter.reset(new QPainter(img.get()));
+ painter->setRenderHint(QPainter::Antialiasing);
+
+ double left = sceneBorder;
+ double top = sceneBorder;
+ double right = s.width() - sceneBorder;
+ double bottom = s.height() - sceneBorder;
const double minSize = 30.0;
if (title)
@@ -140,8 +134,13 @@ void StatsView::replotIfVisible()
plot(state);
}
-void StatsView::hover(QPointF pos)
+void StatsView::hoverEnterEvent(QHoverEvent *)
{
+}
+
+void StatsView::hoverMoveEvent(QHoverEvent *event)
+{
+ QPointF pos(event->pos());
for (auto &series: series) {
if (series->hover(pos)) {
if (series.get() != highlightedSeries) {
@@ -149,7 +148,7 @@ void StatsView::hover(QPointF pos)
highlightedSeries->unhighlight();
highlightedSeries = series.get();
}
- return;
+ return update();
}
}
@@ -157,13 +156,14 @@ void StatsView::hover(QPointF pos)
if (highlightedSeries) {
highlightedSeries->unhighlight();
highlightedSeries = nullptr;
+ update();
}
}
template <typename T, class... Args>
T *StatsView::createSeries(Args&&... args)
{
- T *res = new T(chart, xAxis, yAxis, std::forward<Args>(args)...);
+ T *res = new T(&scene, xAxis, yAxis, std::forward<Args>(args)...);
series.emplace_back(res);
series.back()->updatePositions();
return res;
@@ -175,7 +175,7 @@ void StatsView::setTitle(const QString &s)
title.reset();
return;
}
- title = std::make_unique<QGraphicsSimpleTextItem>(s, chart);
+ title = createItemPtr<QGraphicsSimpleTextItem>(&scene, s);
title->setFont(titleFont);
}
@@ -183,7 +183,7 @@ void StatsView::updateTitlePos()
{
if (!title)
return;
- QRectF rect = chart->plotArea();
+ QRectF rect = scene.sceneRect();
title->setPos(sceneBorder + (rect.width() - title->boundingRect().width()) / 2.0,
sceneBorder);
}
@@ -191,7 +191,7 @@ void StatsView::updateTitlePos()
template <typename T, class... Args>
T *StatsView::createAxis(const QString &title, Args&&... args)
{
- T *res = new T(chart, title, std::forward<Args>(args)...);
+ T *res = createItem<T>(&scene, title, std::forward<Args>(args)...);
axes.emplace_back(res);
return res;
}
@@ -201,13 +201,11 @@ void StatsView::setAxes(StatsAxis *x, StatsAxis *y)
xAxis = x;
yAxis = y;
if (x && y)
- grid = std::make_unique<StatsGrid>(chart, *x, *y);
+ grid = std::make_unique<StatsGrid>(&scene, *x, *y);
}
void StatsView::reset()
{
- if (!chart)
- return;
highlightedSeries = nullptr;
xAxis = yAxis = nullptr;
legend.reset();
@@ -215,7 +213,6 @@ void StatsView::reset()
quartileMarkers.clear();
regressionLines.clear();
histogramMarkers.clear();
- chart->removeAllSeries();
grid.reset();
axes.clear();
title.reset();
@@ -225,12 +222,13 @@ void StatsView::plot(const StatsState &stateIn)
{
state = stateIn;
plotChart();
- plotAreaChanged(chart->plotArea());
+ plotAreaChanged(scene.sceneRect().size());
+ update();
}
void StatsView::plotChart()
{
- if (!chart || !state.var1)
+ if (!state.var1)
return;
reset();
@@ -402,7 +400,7 @@ void StatsView::plotBarChart(const std::vector<dive *> &dives,
// Paint legend first, because the bin-names will be moved away from.
if (showLegend)
- legend = std::make_unique<Legend>(chart, data.vbinNames);
+ legend = createItemPtr<Legend>(&scene, data.vbinNames);
std::vector<BarSeries::MultiItem> items;
items.reserve(data.hbin_counts.size());
@@ -619,7 +617,7 @@ void StatsView::plotPieChart(const std::vector<dive *> &dives,
PieSeries *series = createSeries<PieSeries>(categoryVariable->name(), data, keepOrder, labels);
if (showLegend)
- legend = std::make_unique<Legend>(chart, series->binNames());
+ legend = createItemPtr<Legend>(&scene, series->binNames());
}
void StatsView::plotDiscreteBoxChart(const std::vector<dive *> &dives,
@@ -689,17 +687,17 @@ void StatsView::plotDiscreteScatter(const std::vector<dive *> &dives,
if (quartiles) {
StatsQuartiles quartiles = StatsVariable::quartiles(array);
if (quartiles.isValid()) {
- quartileMarkers.emplace_back(x, quartiles.q1, chart, catAxis, valAxis);
- quartileMarkers.emplace_back(x, quartiles.q2, chart, catAxis, valAxis);
- quartileMarkers.emplace_back(x, quartiles.q3, chart, catAxis, valAxis);
+ quartileMarkers.emplace_back(x, quartiles.q1, &scene, catAxis, valAxis);
+ quartileMarkers.emplace_back(x, quartiles.q2, &scene, catAxis, valAxis);
+ quartileMarkers.emplace_back(x, quartiles.q3, &scene, catAxis, valAxis);
}
}
x += 1.0;
}
}
-StatsView::QuartileMarker::QuartileMarker(double pos, double value, QtCharts::QChart *chart, StatsAxis *xAxis, StatsAxis *yAxis) :
- item(new QGraphicsLineItem(chart)),
+StatsView::QuartileMarker::QuartileMarker(double pos, double value, QGraphicsScene *scene, StatsAxis *xAxis, StatsAxis *yAxis) :
+ item(createItemPtr<QGraphicsLineItem>(scene)),
xAxis(xAxis), yAxis(yAxis),
pos(pos),
value(value)
@@ -719,8 +717,8 @@ void StatsView::QuartileMarker::updatePosition()
x + quartileMarkerSize / 2.0, y);
}
-StatsView::RegressionLine::RegressionLine(double a, double b, QPen pen, QtCharts::QChart *chart, StatsAxis *xAxis, StatsAxis *yAxis) :
- item(new QGraphicsLineItem(chart)),
+StatsView::RegressionLine::RegressionLine(double a, double b, QPen pen, QGraphicsScene *scene, StatsAxis *xAxis, StatsAxis *yAxis) :
+ item(createItemPtr<QGraphicsLineItem>(scene)),
xAxis(xAxis), yAxis(yAxis),
a(a), b(b)
{
@@ -751,8 +749,8 @@ void StatsView::RegressionLine::updatePosition()
xAxis->toScreen(maxX), yAxis->toScreen(a * maxX + b));
}
-StatsView::HistogramMarker::HistogramMarker(double val, bool horizontal, QPen pen, QtCharts::QChart *chart, StatsAxis *xAxis, StatsAxis *yAxis) :
- item(new QGraphicsLineItem(chart)),
+StatsView::HistogramMarker::HistogramMarker(double val, bool horizontal, QPen pen, QGraphicsScene *scene, StatsAxis *xAxis, StatsAxis *yAxis) :
+ item(createItemPtr<QGraphicsLineItem>(scene)),
xAxis(xAxis), yAxis(yAxis),
val(val), horizontal(horizontal)
{
@@ -777,12 +775,12 @@ void StatsView::HistogramMarker::updatePosition()
void StatsView::addHistogramMarker(double pos, const QPen &pen, bool isHorizontal, StatsAxis *xAxis, StatsAxis *yAxis)
{
- histogramMarkers.emplace_back(pos, isHorizontal, pen, chart, xAxis, yAxis);
+ histogramMarkers.emplace_back(pos, isHorizontal, pen, &scene, xAxis, yAxis);
}
void StatsView::addLinearRegression(double a, double b, double minX, double maxX, double minY, double maxY, StatsAxis *xAxis, StatsAxis *yAxis)
{
- regressionLines.emplace_back(a, b, QPen(Qt::red), chart, xAxis, yAxis);
+ regressionLines.emplace_back(a, b, QPen(Qt::red), &scene, xAxis, yAxis);
}
// Yikes, we get our data in different kinds of (bin, value) pairs.
@@ -942,7 +940,7 @@ void StatsView::plotHistogramStackedChart(const std::vector<dive *> &dives,
BarPlotData data(categoryBins, *valueBinner);
if (showLegend)
- legend = std::make_unique<Legend>(chart, data.vbinNames);
+ legend = createItemPtr<Legend>(&scene, data.vbinNames);
CountAxis *valAxis = createCountAxis(data.maxCategoryCount, isHorizontal);
diff --git a/stats/statsview.h b/stats/statsview.h
index fd8ec1e2c..fbf51d46e 100644
--- a/stats/statsview.h
+++ b/stats/statsview.h
@@ -5,7 +5,10 @@
#include "statsstate.h"
#include <memory>
#include <QFont>
-#include <QQuickWidget>
+#include <QGraphicsScene>
+#include <QImage>
+#include <QPainter>
+#include <QQuickItem>
struct dive;
struct StatsBinner;
@@ -13,10 +16,6 @@ struct StatsBin;
struct StatsState;
struct StatsVariable;
-namespace QtCharts {
- class QAbstractSeries;
- class QChart;
-}
class QGraphicsLineItem;
class QGraphicsSimpleTextItem;
class StatsSeries;
@@ -26,21 +25,31 @@ class HistogramAxis;
class StatsAxis;
class StatsGrid;
class Legend;
+class QSGTexture;
enum class ChartSubType : int;
enum class StatsOperation : int;
-class StatsView : public QQuickWidget {
+class StatsView : public QQuickItem {
Q_OBJECT
public:
- StatsView(QWidget *parent = NULL);
+ StatsView();
+ StatsView(QQuickItem *parent);
~StatsView();
void plot(const StatsState &state);
private slots:
- void plotAreaChanged(const QRectF &plotArea);
void replotIfVisible();
private:
+ // 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);
void reset(); // clears all series and axes
void setAxes(StatsAxis *x, StatsAxis *y);
void plotBarChart(const std::vector<dive *> &dives,
@@ -102,7 +111,7 @@ private:
std::unique_ptr<QGraphicsLineItem> item;
StatsAxis *xAxis, *yAxis;
double pos, value;
- QuartileMarker(double pos, double value, QtCharts::QChart *chart, StatsAxis *xAxis, StatsAxis *yAxis);
+ QuartileMarker(double pos, double value, QGraphicsScene *scene, StatsAxis *xAxis, StatsAxis *yAxis);
void updatePosition();
};
@@ -112,7 +121,7 @@ private:
StatsAxis *xAxis, *yAxis;
double a, b; // y = ax + b
void updatePosition();
- RegressionLine(double a, double b, QPen pen, QtCharts::QChart *chart, StatsAxis *xAxis, StatsAxis *yAxis);
+ RegressionLine(double a, double b, QPen pen, QGraphicsScene *scene, StatsAxis *xAxis, StatsAxis *yAxis);
};
// A line marking median or mean in histograms
@@ -122,14 +131,13 @@ private:
double val;
bool horizontal;
void updatePosition();
- HistogramMarker(double val, bool horizontal, QPen pen, QtCharts::QChart *chart, StatsAxis *xAxis, StatsAxis *yAxis);
+ HistogramMarker(double val, bool horizontal, QPen pen, QGraphicsScene *scene, StatsAxis *xAxis, StatsAxis *yAxis);
};
void addLinearRegression(double a, double b, double minX, double maxX, double minY, double maxY, StatsAxis *xAxis, StatsAxis *yAxis);
void addHistogramMarker(double pos, const QPen &pen, bool isHorizontal, StatsAxis *xAxis, StatsAxis *yAxis);
StatsState state;
- QtCharts::QChart *chart;
QFont titleFont;
std::vector<std::unique_ptr<StatsAxis>> axes;
std::unique_ptr<StatsGrid> grid;
@@ -142,17 +150,8 @@ private:
StatsSeries *highlightedSeries;
StatsAxis *xAxis, *yAxis;
- // This is unfortunate: we can't derive from QChart, because the chart is allocated by QML.
- // Therefore, we have to listen to hover events using an events-filter.
- // Probably we should try to get rid of the QML ChartView.
- struct EventFilter : public QObject {
- StatsView *view;
- EventFilter(StatsView *view) : view(view) {}
- private:
- bool eventFilter(QObject *o, QEvent *event);
- } eventFilter;
- friend EventFilter;
- void hover(QPointF pos);
+ void hoverEnterEvent(QHoverEvent *event) override;
+ void hoverMoveEvent(QHoverEvent *event) override;
};
#endif
diff --git a/subsurface-helper.cpp b/subsurface-helper.cpp
index 7120405dc..45cadcfa1 100644
--- a/subsurface-helper.cpp
+++ b/subsurface-helper.cpp
@@ -4,6 +4,7 @@
#include "map-widget/qmlmapwidgethelper.h"
#include "qt-models/maplocationmodel.h"
+#include "stats/statsview.h"
#include "core/qt-gui.h"
#include "core/settings/qPref.h"
#include "core/ssrf.h"
@@ -213,4 +214,5 @@ static void register_qml_types(QQmlEngine *engine)
register_qml_type<MapWidgetHelper>("MapWidgetHelper");
register_qml_type<MapLocationModel>("MapLocationModel");
+ register_qml_type<StatsView>("StatsView");
}