charttheme.cpp
310 lines
| 9.4 KiB
| text/x-c
|
CppLexer
/ src / charttheme.cpp
Tero Ahola
|
r103 | #include "charttheme_p.h" | ||
#include "qchart.h" | ||||
sauimone
|
r540 | #include "qlegend.h" | ||
Michal Klocek
|
r176 | #include "qchartaxis.h" | ||
Jani Honkonen
|
r324 | #include <QTime> | ||
Tero Ahola
|
r103 | |||
Michal Klocek
|
r143 | //series | ||
sauimone
|
r214 | #include "qbarset.h" | ||
sauimone
|
r338 | #include "qbarseries.h" | ||
#include "qstackedbarseries.h" | ||||
#include "qpercentbarseries.h" | ||||
Michal Klocek
|
r349 | #include "qlineseries.h" | ||
Michal Klocek
|
r421 | #include "qareaseries.h" | ||
Tero Ahola
|
r182 | #include "qscatterseries.h" | ||
Jani Honkonen
|
r163 | #include "qpieseries.h" | ||
Jani Honkonen
|
r203 | #include "qpieslice.h" | ||
Marek Rosa
|
r401 | #include "qsplineseries.h" | ||
Jani Honkonen
|
r163 | |||
Michal Klocek
|
r143 | //items | ||
#include "axisitem_p.h" | ||||
sauimone
|
r381 | #include "barpresenter_p.h" | ||
#include "stackedbarpresenter_p.h" | ||||
#include "percentbarpresenter_p.h" | ||||
Michal Klocek
|
r145 | #include "linechartitem_p.h" | ||
Michal Klocek
|
r421 | #include "areachartitem_p.h" | ||
Michal Klocek
|
r470 | #include "scatterchartitem_p.h" | ||
Jani Honkonen
|
r568 | #include "piechartitem_p.h" | ||
Marek Rosa
|
r460 | #include "splinechartitem_p.h" | ||
Tero Ahola
|
r103 | |||
Michal Klocek
|
r143 | //themes | ||
Jani Honkonen
|
r494 | #include "chartthemedefault_p.h" | ||
Michal Klocek
|
r143 | #include "chartthemevanilla_p.h" | ||
#include "chartthemeicy_p.h" | ||||
#include "chartthemegrayscale_p.h" | ||||
#include "chartthemescientific_p.h" | ||||
Tero Ahola
|
r581 | #include "chartthemebluecerulean_p.h" | ||
Tero Ahola
|
r103 | |||
Tero Ahola
|
r125 | |||
Michal Klocek
|
r143 | QTCOMMERCIALCHART_BEGIN_NAMESPACE | ||
Michal Klocek
|
r153 | ChartTheme::ChartTheme(QChart::ChartTheme id) | ||
Michal Klocek
|
r143 | { | ||
Michal Klocek
|
r153 | m_id = id; | ||
Jani Honkonen
|
r324 | qsrand(QTime(0,0,0).secsTo(QTime::currentTime())); | ||
Michal Klocek
|
r143 | } | ||
Michal Klocek
|
r153 | ChartTheme* ChartTheme::createTheme(QChart::ChartTheme theme) | ||
Michal Klocek
|
r143 | { | ||
switch(theme) { | ||||
case QChart::ChartThemeVanilla: | ||||
return new ChartThemeVanilla(); | ||||
case QChart::ChartThemeIcy: | ||||
return new ChartThemeIcy(); | ||||
case QChart::ChartThemeGrayscale: | ||||
return new ChartThemeGrayscale(); | ||||
case QChart::ChartThemeScientific: | ||||
return new ChartThemeScientific(); | ||||
Tero Ahola
|
r581 | case QChart::ChartThemeBlueCerulean: | ||
return new ChartThemeBlueCerulean(); | ||||
Jani Honkonen
|
r494 | default: | ||
return new ChartThemeDefault(); | ||||
Michal Klocek
|
r143 | } | ||
} | ||||
void ChartTheme::decorate(QChart* chart) | ||||
{ | ||||
Tero Ahola
|
r581 | if (m_backgroundShades == BackgroundShadesNone) | ||
chart->setChartBackgroundBrush(m_backgroundGradient); | ||||
else | ||||
chart->setChartBackgroundBrush(Qt::NoBrush); | ||||
Tero Ahola
|
r549 | chart->setChartTitleFont(m_masterFont); | ||
Michal Klocek
|
r143 | } | ||
sauimone
|
r540 | void ChartTheme::decorate(QLegend* legend) | ||
{ | ||||
Tero Ahola
|
r550 | legend->setBackgroundBrush(m_backgroundGradient); | ||
sauimone
|
r540 | } | ||
Michal Klocek
|
r562 | void ChartTheme::decorate(QAreaSeries* series, int index) | ||
Michal Klocek
|
r421 | { | ||
QPen pen; | ||||
QBrush brush; | ||||
Michal Klocek
|
r560 | if (pen == series->pen()){ | ||
Tero Ahola
|
r538 | pen.setColor(colorAt(m_seriesGradients.at(index % m_seriesGradients.size()), 1.0)); | ||
Michal Klocek
|
r421 | pen.setWidthF(2); | ||
Michal Klocek
|
r557 | series->setPen(pen); | ||
Michal Klocek
|
r421 | } | ||
Michal Klocek
|
r560 | if (brush == series->brush()) { | ||
Tero Ahola
|
r538 | QBrush brush(m_seriesColors.at(index % m_seriesColors.size())); | ||
Michal Klocek
|
r557 | series->setBrush(brush); | ||
Michal Klocek
|
r421 | } | ||
} | ||||
Michal Klocek
|
r562 | void ChartTheme::decorate(QLineSeries* series,int index) | ||
Tero Ahola
|
r103 | { | ||
Michal Klocek
|
r152 | QPen pen; | ||
Michal Klocek
|
r560 | if(pen == series->pen()){ | ||
Michal Klocek
|
r544 | pen.setColor(m_seriesColors.at(index%m_seriesColors.size())); | ||
pen.setWidthF(2); | ||||
series->setPen(pen); | ||||
Michal Klocek
|
r152 | } | ||
Tero Ahola
|
r103 | } | ||
Tero Ahola
|
r538 | void ChartTheme::decorate(BarPresenter* item, QBarSeries* series,int index) | ||
Tero Ahola
|
r108 | { | ||
sauimone
|
r357 | QList<QBarSet*> sets = series->barSets(); | ||
sauimone
|
r509 | for (int i=0; i<sets.count(); i++) { | ||
Tero Ahola
|
r523 | qreal pos = 0.5; | ||
if (sets.count() > 1) | ||||
pos = (qreal) i / (qreal) (sets.count() - 1); | ||||
Tero Ahola
|
r538 | QColor c = colorAt(m_seriesGradients.at(index % m_seriesGradients.size()), pos); | ||
sauimone
|
r509 | sets.at(i)->setBrush(QBrush(c)); | ||
sauimone
|
r512 | |||
// Pick label color as far as possible from bar color (within gradient). | ||||
// 0.3 is magic number that was picked as value that gave enough contrast with icy theme gradient :) | ||||
// TODO: better picking of label color? | ||||
if (pos < 0.3) { | ||||
Tero Ahola
|
r538 | c = colorAt(m_seriesGradients.at(index % m_seriesGradients.size()), 1); | ||
sauimone
|
r512 | } else { | ||
Tero Ahola
|
r538 | c = colorAt(m_seriesGradients.at(index % m_seriesGradients.size()), 0); | ||
sauimone
|
r512 | } | ||
sets.at(i)->setFloatingValuePen(QPen(c)); | ||||
sauimone
|
r164 | } | ||
Tero Ahola
|
r108 | } | ||
Tero Ahola
|
r538 | void ChartTheme::decorate(StackedBarPresenter* item, QStackedBarSeries* series,int index) | ||
Tero Ahola
|
r103 | { | ||
sauimone
|
r357 | QList<QBarSet*> sets = series->barSets(); | ||
sauimone
|
r509 | for (int i=0; i<sets.count(); i++) { | ||
Tero Ahola
|
r523 | qreal pos = 0.5; | ||
if (sets.count() > 1) | ||||
pos = (qreal) i / (qreal) (sets.count() - 1); | ||||
Tero Ahola
|
r538 | QColor c = colorAt(m_seriesGradients.at(index % m_seriesGradients.size()), pos); | ||
sauimone
|
r509 | sets.at(i)->setBrush(QBrush(c)); | ||
sauimone
|
r512 | |||
if (pos < 0.3) { | ||||
Tero Ahola
|
r538 | c = colorAt(m_seriesGradients.at(index % m_seriesGradients.size()), 1); | ||
sauimone
|
r512 | } else { | ||
Tero Ahola
|
r538 | c = colorAt(m_seriesGradients.at(index % m_seriesGradients.size()), 0); | ||
sauimone
|
r512 | } | ||
sets.at(i)->setFloatingValuePen(QPen(c)); | ||||
sauimone
|
r164 | } | ||
Tero Ahola
|
r103 | } | ||
Tero Ahola
|
r538 | void ChartTheme::decorate(PercentBarPresenter* item, QPercentBarSeries* series,int index) | ||
Michal Klocek
|
r143 | { | ||
sauimone
|
r357 | QList<QBarSet*> sets = series->barSets(); | ||
sauimone
|
r509 | for (int i=0; i<sets.count(); i++) { | ||
Tero Ahola
|
r523 | qreal pos = 0.5; | ||
if (sets.count() > 1) | ||||
pos = (qreal) i / (qreal) (sets.count() - 1); | ||||
Tero Ahola
|
r538 | QColor c = colorAt(m_seriesGradients.at(index % m_seriesGradients.size()), pos); | ||
sauimone
|
r509 | sets.at(i)->setBrush(QBrush(c)); | ||
sauimone
|
r512 | |||
if (pos < 0.3) { | ||||
Tero Ahola
|
r538 | c = colorAt(m_seriesGradients.at(index % m_seriesGradients.size()), 1); | ||
sauimone
|
r512 | } else { | ||
Tero Ahola
|
r538 | c = colorAt(m_seriesGradients.at(index % m_seriesGradients.size()), 0); | ||
sauimone
|
r512 | } | ||
sets.at(i)->setFloatingValuePen(QPen(c)); | ||||
sauimone
|
r164 | } | ||
Michal Klocek
|
r143 | } | ||
Tero Ahola
|
r103 | |||
Michal Klocek
|
r562 | void ChartTheme::decorate(QScatterSeries* series, int index) | ||
Tero Ahola
|
r182 | { | ||
Michal Klocek
|
r541 | QPen pen; | ||
QBrush brush; | ||||
if (pen == series->pen()) { | ||||
Michal Klocek
|
r543 | pen.setColor(colorAt(m_seriesGradients.at(index % m_seriesGradients.size()), 1.0)); | ||
Michal Klocek
|
r541 | pen.setWidthF(2); | ||
series->setPen(pen); | ||||
} | ||||
Tero Ahola
|
r195 | |||
Michal Klocek
|
r541 | if (brush == series->brush()) { | ||
Michal Klocek
|
r543 | QBrush brush(m_seriesColors.at(index % m_seriesColors.size())); | ||
Michal Klocek
|
r541 | series->setBrush(brush); | ||
} | ||||
Tero Ahola
|
r182 | } | ||
Jani Honkonen
|
r568 | void ChartTheme::decorate(PieChartItem* item, QPieSeries* series, int index) | ||
Jani Honkonen
|
r163 | { | ||
Tero Ahola
|
r507 | // Get color for a slice from a gradient linearly, beginning from the start of the gradient | ||
for (int i(0); i < series->slices().count(); i++) { | ||||
qreal pos = (qreal) i / (qreal) series->count(); | ||||
Tero Ahola
|
r538 | QColor penColor = colorAt(m_seriesGradients.at(index % m_seriesGradients.size()), 0.1); | ||
Tero Ahola
|
r510 | series->slices().at(i)->setSlicePen(penColor); | ||
Tero Ahola
|
r538 | QColor brushColor = colorAt(m_seriesGradients.at(index % m_seriesGradients.size()), pos); | ||
Tero Ahola
|
r510 | series->slices().at(i)->setSliceBrush(brushColor); | ||
Jani Honkonen
|
r163 | } | ||
} | ||||
Michal Klocek
|
r562 | void ChartTheme::decorate(QSplineSeries* series, int index) | ||
Marek Rosa
|
r401 | { | ||
Marek Rosa
|
r434 | QPen pen; | ||
Michal Klocek
|
r562 | if(pen == series->pen()){ | ||
Tero Ahola
|
r538 | pen.setColor(m_seriesColors.at(index%m_seriesColors.size())); | ||
Michal Klocek
|
r562 | pen.setWidthF(2); | ||
series->setPen(pen); | ||||
Marek Rosa
|
r434 | } | ||
Marek Rosa
|
r401 | } | ||
Michal Klocek
|
r562 | void ChartTheme::decorate(QChartAxis* axis,bool axisX) | ||
Tero Ahola
|
r548 | { | ||
if (axis->isAxisVisible()) { | ||||
axis->setLabelsBrush(m_axisLabelBrush); | ||||
axis->setLabelsPen(m_axisLabelPen); | ||||
// TODO: check the axis type (x or y) should define whether to show the shades or not | ||||
if (m_backgroundShades == BackgroundShadesBoth | ||||
Michal Klocek
|
r562 | || (m_backgroundShades == BackgroundShadesVertical && axisX) | ||
|| (m_backgroundShades == BackgroundShadesHorizontal && !axisX)) { | ||||
Tero Ahola
|
r548 | axis->setShadesPen(m_backgroundShadesPen); | ||
axis->setShadesBrush(m_backgroundShadesBrush); | ||||
} else { | ||||
// The shades not supposed to be shown for this axis, clear possible brush and pen | ||||
axis->setShadesPen(Qt::NoPen); | ||||
axis->setShadesBrush(Qt::NoBrush); | ||||
} | ||||
axis->setAxisPen(m_axisLinePen); | ||||
axis->setGridLinePen(m_gridLinePen); | ||||
axis->setLabelsFont(m_masterFont); | ||||
} | ||||
} | ||||
Tero Ahola
|
r507 | void ChartTheme::generateSeriesGradients() | ||
{ | ||||
// Generate gradients in HSV color space | ||||
foreach (QColor color, m_seriesColors) { | ||||
QLinearGradient g; | ||||
qreal h = color.hsvHueF(); | ||||
qreal s = color.hsvSaturationF(); | ||||
// TODO: tune the algorithm to give nice results with most base colors defined in | ||||
// most themes. The rest of the gradients we can define manually in theme specific | ||||
// implementation. | ||||
QColor start = color; | ||||
start.setHsvF(h, 0.05, 0.95); | ||||
g.setColorAt(0.0, start); | ||||
g.setColorAt(0.5, color); | ||||
QColor end = color; | ||||
end.setHsvF(h, s, 0.25); | ||||
g.setColorAt(1.0, end); | ||||
m_seriesGradients << g; | ||||
} | ||||
} | ||||
Jani Honkonen
|
r494 | QColor ChartTheme::colorAt(const QColor &start, const QColor &end, qreal pos) | ||
{ | ||||
Q_ASSERT(pos >=0.0 && pos <= 1.0); | ||||
qreal r = start.redF() + ((end.redF() - start.redF()) * pos); | ||||
qreal g = start.greenF() + ((end.greenF() - start.greenF()) * pos); | ||||
qreal b = start.blueF() + ((end.blueF() - start.blueF()) * pos); | ||||
QColor c; | ||||
c.setRgbF(r, g, b); | ||||
return c; | ||||
} | ||||
QColor ChartTheme::colorAt(const QGradient &gradient, qreal pos) | ||||
{ | ||||
Q_ASSERT(pos >=0 && pos <= 1.0); | ||||
// another possibility: | ||||
// http://stackoverflow.com/questions/3306786/get-intermediate-color-from-a-gradient | ||||
QGradientStops stops = gradient.stops(); | ||||
int count = stops.count(); | ||||
// find previous stop relative to position | ||||
QGradientStop prev = stops.first(); | ||||
for (int i=0; i<count; i++) { | ||||
QGradientStop stop = stops.at(i); | ||||
if (pos > stop.first) | ||||
prev = stop; | ||||
// given position is actually a stop position? | ||||
if (pos == stop.first) { | ||||
//qDebug() << "stop color" << pos; | ||||
return stop.second; | ||||
} | ||||
} | ||||
// find next stop relative to position | ||||
QGradientStop next = stops.last(); | ||||
for (int i=count-1; i>=0; i--) { | ||||
QGradientStop stop = stops.at(i); | ||||
if (pos < stop.first) | ||||
next = stop; | ||||
} | ||||
//qDebug() << "prev" << prev.first << "pos" << pos << "next" << next.first; | ||||
qreal range = next.first - prev.first; | ||||
qreal posDelta = pos - prev.first; | ||||
qreal relativePos = posDelta / range; | ||||
//qDebug() << "range" << range << "posDelta" << posDelta << "relativePos" << relativePos; | ||||
return colorAt(prev.second, next.second, relativePos); | ||||
} | ||||
Tero Ahola
|
r103 | QTCOMMERCIALCHART_END_NAMESPACE | ||