chart.cpp 4.76 KB
Newer Older
Volker Krause's avatar
Volker Krause committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*
    Copyright (C) 2016 Volker Krause <vkrause@kde.org>

    This program is free software; you can redistribute it and/or modify it
    under the terms of the GNU Library General Public License as published by
    the Free Software Foundation; either version 2 of the License, or (at your
    option) any later version.

    This program is distributed in the hope that it will be useful, but WITHOUT
    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
    License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "chart.h"
19

20
#include <model/categoryaggregationmodel.h>
21
#include <model/numericaggregationmodel.h>
22
#include <model/timeaggregationmodel.h>
Volker Krause's avatar
Volker Krause committed
23

24
#include <QtCharts/QAreaSeries>
25
26
#include <QtCharts/QBarCategoryAxis>
#include <QtCharts/QBoxPlotSeries>
Volker Krause's avatar
Volker Krause committed
27
28
29
30
#include <QtCharts/QChart>
#include <QtCharts/QDateTimeAxis>
#include <QtCharts/QLineSeries>
#include <QtCharts/QValueAxis>
31
#include <QtCharts/QVBoxPlotModelMapper>
Volker Krause's avatar
Volker Krause committed
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <QtCharts/QVXYModelMapper>

#include <QAbstractItemModel>
#include <QApplication>
#include <QDateTime>

#include <numeric>

using namespace UserFeedback::Analyzer;
using namespace QtCharts;

Chart::Chart(QObject *parent) :
    QObject(parent),
    m_chart(new QChart),
    m_xAxis(new QDateTimeAxis(this)),
47
    m_boxXAxis(new QBarCategoryAxis(this)),
Volker Krause's avatar
Volker Krause committed
48
49
50
51
52
53
    m_yAxis(new QValueAxis(this))
{
    m_chart->setTheme(qApp->palette().color(QPalette::Window).lightnessF() < 0.25 ? QChart::ChartThemeDark : QChart::ChartThemeLight);

    m_xAxis->setFormat(QStringLiteral("yyyy-MM-dd")); // TODO, follow aggregation mode
    m_chart->addAxis(m_xAxis, Qt::AlignBottom);
54
    m_chart->addAxis(m_boxXAxis, Qt::AlignBottom);
Volker Krause's avatar
Volker Krause committed
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
    m_yAxis->setTickCount(5);
    m_yAxis->setMinorTickCount(4);
    m_chart->addAxis(m_yAxis, Qt::AlignLeft);
}

Chart::~Chart() = default;

QChart* Chart::chart() const
{
    return m_chart.get();
}

void Chart::setModel(QAbstractItemModel* model)
{
    if (m_model)
        disconnect(m_model, &QAbstractItemModel::modelReset, this, &Chart::modelReset);

72
    m_model = model;
73
74
    if (!model) {
        m_chart->removeAllSeries();
Volker Krause's avatar
Volker Krause committed
75
        return;
76
    }
Volker Krause's avatar
Volker Krause committed
77
78
79
80
81
82
83
84
85
86

    connect(m_model, &QAbstractItemModel::modelReset, this, &Chart::modelReset);
    modelReset();
}

void Chart::modelReset()
{
    Q_ASSERT(m_model);
    m_chart->removeAllSeries();

Volker Krause's avatar
Volker Krause committed
87
88
89
90
    const auto colCount = m_model->columnCount();
    if (m_model->rowCount() <= 0 || colCount <= 1)
        return;

91
92
93
    if (qobject_cast<NumericAggregationModel*>(m_model)) {
        auto series = new QBoxPlotSeries(this);
        series->setName(tr("TODO"));
Volker Krause's avatar
Volker Krause committed
94

95
        auto mapper = new QVBoxPlotModelMapper(series);
Volker Krause's avatar
Volker Krause committed
96
        mapper->setModel(m_model);
97
98
        mapper->setFirstBoxSetColumn(1);
        mapper->setLastBoxSetColumn(5);
Volker Krause's avatar
Volker Krause committed
99
100
101
        mapper->setSeries(series);
        m_chart->addSeries(series);

102
        series->attachAxis(m_boxXAxis);
Volker Krause's avatar
Volker Krause committed
103
        series->attachAxis(m_yAxis);
104
105
106
107
108
109
110
111
112
113

        QStringList l;
        for (int i = 0; i < m_model->rowCount(); ++i) {
            l.push_back(m_model->index(i, 0).data(TimeAggregationModel::DateTimeRole).toDateTime().toString(QStringLiteral("yyyy-MM-dd")));
        }

        m_boxXAxis->setCategories(l);
        m_boxXAxis->show();
        m_xAxis->hide();
    } else {
114
        QLineSeries *prevSeries = nullptr;
115
        for (int i = 1; i < colCount; ++i) {
116
            auto series = new QLineSeries;
117
118
119
120
121
122
123
124

            auto mapper = new QVXYModelMapper(series);
            mapper->setModel(m_model);
            mapper->setXColumn(0);
            mapper->setYColumn(i);
            mapper->setFirstRow(0);
            mapper->setSeries(series);

125
            auto areaSeries = new QAreaSeries(series, prevSeries);
126
            series->setParent(areaSeries); // otherwise series isn't deleted by removeAllSeries!
127
128
129
130
131
132
133
            areaSeries->setName(m_model->headerData(i, Qt::Horizontal).toString().toHtmlEscaped());
            m_chart->addSeries(areaSeries);

            areaSeries->attachAxis(m_xAxis);
            areaSeries->attachAxis(m_yAxis);

            prevSeries = series;
134
135
136
137
        }

        m_boxXAxis->hide();
        m_xAxis->show();
Volker Krause's avatar
Volker Krause committed
138
    }
Volker Krause's avatar
Volker Krause committed
139
140
141
142
143
144
145
146
147
148
149
150
151

    // auto-scale axes
    if (const auto rcount = m_model->rowCount()) {
        const auto beginDt = m_model->index(0, 0).data(TimeAggregationModel::DateTimeRole).toDateTime();
        const auto endDt = m_model->index(rcount - 1, 0).data(TimeAggregationModel::DateTimeRole).toDateTime();
        m_xAxis->setRange(beginDt, endDt);
        m_xAxis->setTickCount(std::min(rcount, 12));

        const auto max = m_model->index(0, 0).data(TimeAggregationModel::MaximumValueRole).toInt();
        m_yAxis->setRange(0, max);
        m_yAxis->applyNiceNumbers();
    }
}