charttheme.cpp
365 lines
| 10.5 KiB
| text/x-c
|
CppLexer
/ src / charttheme.cpp
Tero Ahola
|
r103 | #include "charttheme_p.h" | ||
#include "qchart.h" | ||||
Tero Ahola
|
r584 | #include "qchartview.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
|
r666 | #include "barchartitem_p.h" | ||
#include "stackedbarchartitem_p.h" | ||||
#include "percentbarchartitem_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" | ||
Tero Ahola
|
r651 | #include "chartthemelight_p.h" | ||
#include "chartthemebluecerulean_p.h" | ||||
#include "chartthemedark_p.h" | ||||
#include "chartthemebrownsand_p.h" | ||||
#include "chartthemebluencs_p.h" | ||||
Michal Klocek
|
r143 | #include "chartthemevanilla_p.h" | ||
#include "chartthemeicy_p.h" | ||||
#include "chartthemegrayscale_p.h" | ||||
#include "chartthemescientific_p.h" | ||||
Tero Ahola
|
r125 | |||
Michal Klocek
|
r143 | QTCOMMERCIALCHART_BEGIN_NAMESPACE | ||
Tero Ahola
|
r614 | ChartTheme::ChartTheme(QChart::ChartTheme id) : | ||
m_masterFont(QFont()), | ||||
m_titleBrush(QColor(QRgb(0x000000))), | ||||
m_axisLinePen(QPen(QRgb(0x000000))), | ||||
m_axisLabelBrush(QColor(QRgb(0x000000))), | ||||
m_backgroundShadesPen(Qt::NoPen), | ||||
m_backgroundShadesBrush(Qt::NoBrush), | ||||
m_backgroundShades(BackgroundShadesNone), | ||||
m_gridLinePen(QPen(QRgb(0x000000))) | ||||
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) { | ||||
Tero Ahola
|
r651 | case QChart::ChartThemeLight: | ||
return new ChartThemeLight(); | ||||
case QChart::ChartThemeBlueCerulean: | ||||
return new ChartThemeBlueCerulean(); | ||||
case QChart::ChartThemeDark: | ||||
return new ChartThemeDark(); | ||||
case QChart::ChartThemeBrownSand: | ||||
return new ChartThemeBrownSand(); | ||||
case QChart::ChartThemeBlueNcs: | ||||
return new ChartThemeBlueNcs(); | ||||
Michal Klocek
|
r143 | case QChart::ChartThemeVanilla: | ||
return new ChartThemeVanilla(); | ||||
case QChart::ChartThemeIcy: | ||||
return new ChartThemeIcy(); | ||||
case QChart::ChartThemeGrayscale: | ||||
return new ChartThemeGrayscale(); | ||||
case QChart::ChartThemeScientific: | ||||
return new ChartThemeScientific(); | ||||
Jani Honkonen
|
r494 | default: | ||
return new ChartThemeDefault(); | ||||
Michal Klocek
|
r143 | } | ||
} | ||||
Michal Klocek
|
r645 | void ChartTheme::decorate(QChart* chart,bool force) | ||
Michal Klocek
|
r143 | { | ||
Michal Klocek
|
r645 | QPen pen; | ||
QBrush brush; | ||||
if(brush == chart->backgroundBrush() || force) | ||||
{ | ||||
if (m_backgroundShades == BackgroundShadesNone) { | ||||
chart->setBackgroundBrush(m_chartBackgroundGradient); | ||||
} | ||||
else { | ||||
chart->setBackgroundBrush(Qt::NoBrush); | ||||
} | ||||
Tero Ahola
|
r584 | } | ||
Michal Klocek
|
r645 | chart->setTitleFont(m_masterFont); | ||
chart->setTitleBrush(m_titleBrush); | ||||
Michal Klocek
|
r143 | } | ||
Michal Klocek
|
r645 | void ChartTheme::decorate(QLegend* legend,bool force) | ||
sauimone
|
r540 | { | ||
Michal Klocek
|
r645 | QPen pen; | ||
QBrush brush; | ||||
if (pen == legend->pen() || force){ | ||||
//TODO:: legend->setPen(); | ||||
} | ||||
if (brush == legend->brush() || force) { | ||||
legend->setBrush(m_chartBackgroundGradient); | ||||
} | ||||
sauimone
|
r540 | } | ||
Michal Klocek
|
r645 | void ChartTheme::decorate(QAreaSeries* series, int index,bool force) | ||
Michal Klocek
|
r421 | { | ||
Michal Klocek
|
r644 | QPen pen; | ||
QBrush brush; | ||||
Michal Klocek
|
r421 | |||
Michal Klocek
|
r645 | if (pen == series->pen() || force){ | ||
Tero Ahola
|
r653 | pen.setColor(colorAt(m_seriesGradients.at(index % m_seriesGradients.size()), 0.0)); | ||
Michal Klocek
|
r644 | pen.setWidthF(2); | ||
series->setPen(pen); | ||||
} | ||||
Michal Klocek
|
r645 | if (brush == series->brush() || force) { | ||
Michal Klocek
|
r644 | QBrush brush(m_seriesColors.at(index % m_seriesColors.size())); | ||
series->setBrush(brush); | ||||
} | ||||
Michal Klocek
|
r421 | } | ||
Michal Klocek
|
r645 | void ChartTheme::decorate(QLineSeries* series,int index,bool force) | ||
Tero Ahola
|
r103 | { | ||
Michal Klocek
|
r644 | QPen pen; | ||
Michal Klocek
|
r645 | if(pen == series->pen() || force ){ | ||
Michal Klocek
|
r644 | pen.setColor(m_seriesColors.at(index%m_seriesColors.size())); | ||
pen.setWidthF(2); | ||||
series->setPen(pen); | ||||
} | ||||
Tero Ahola
|
r103 | } | ||
Tero Ahola
|
r653 | void ChartTheme::decorate(QBarSeries* series, int index, bool force) | ||
Tero Ahola
|
r108 | { | ||
Michal Klocek
|
r645 | QBrush brush; | ||
Tero Ahola
|
r653 | QPen pen; | ||
sauimone
|
r357 | QList<QBarSet*> sets = series->barSets(); | ||
Michal Klocek
|
r645 | |||
Tero Ahola
|
r661 | qreal takeAtPos = 0.5; | ||
qreal step = 0.2; | ||||
if (sets.count() > 1 ) { | ||||
step = 1.0 / (qreal) sets.count(); | ||||
if (sets.count() % m_seriesGradients.count()) | ||||
step *= m_seriesGradients.count(); | ||||
else | ||||
step *= (m_seriesGradients.count() - 1); | ||||
} | ||||
Michal Klocek
|
r645 | |||
Tero Ahola
|
r661 | for (int i(0); i < sets.count(); i++) { | ||
int colorIndex = (index + i) % m_seriesGradients.count(); | ||||
if (i > 0 && i % m_seriesGradients.count() == 0) { | ||||
// There is no dedicated base color for each sets, generate more colors | ||||
takeAtPos += step; | ||||
if (takeAtPos == 1.0) | ||||
takeAtPos += step; | ||||
takeAtPos -= (int) takeAtPos; | ||||
sauimone
|
r512 | } | ||
Tero Ahola
|
r661 | qDebug() << "pos:" << takeAtPos; | ||
if (brush == sets.at(i)->brush() || force ) | ||||
sets.at(i)->setBrush(colorAt(m_seriesGradients.at(colorIndex), takeAtPos)); | ||||
Tero Ahola
|
r103 | |||
Tero Ahola
|
r653 | // Pick label color from the opposite end of the gradient. | ||
// 0.3 as a boundary seems to work well. | ||||
Tero Ahola
|
r661 | if (takeAtPos < 0.3) | ||
Tero Ahola
|
r653 | sets.at(i)->setFloatingValuePen(colorAt(m_seriesGradients.at(index % m_seriesGradients.size()), 1)); | ||
else | ||||
sets.at(i)->setFloatingValuePen(colorAt(m_seriesGradients.at(index % m_seriesGradients.size()), 0)); | ||||
Michal Klocek
|
r645 | |||
Tero Ahola
|
r653 | if (pen == sets.at(i)->pen() || force) { | ||
QColor c = colorAt(m_seriesGradients.at(index % m_seriesGradients.size()), 0.0); | ||||
sets.at(i)->setPen(c); | ||||
sauimone
|
r512 | } | ||
sauimone
|
r164 | } | ||
Michal Klocek
|
r143 | } | ||
Tero Ahola
|
r103 | |||
Michal Klocek
|
r645 | void ChartTheme::decorate(QScatterSeries* series, int index,bool force) | ||
Tero Ahola
|
r182 | { | ||
Michal Klocek
|
r644 | QPen pen; | ||
QBrush brush; | ||||
Michal Klocek
|
r645 | if (pen == series->pen() || force) { | ||
Tero Ahola
|
r653 | pen.setColor(colorAt(m_seriesGradients.at(index % m_seriesGradients.size()), 0.0)); | ||
Michal Klocek
|
r644 | pen.setWidthF(2); | ||
series->setPen(pen); | ||||
} | ||||
Michal Klocek
|
r645 | if (brush == series->brush() || force) { | ||
Michal Klocek
|
r644 | QBrush brush(m_seriesColors.at(index % m_seriesColors.size())); | ||
series->setBrush(brush); | ||||
} | ||||
Tero Ahola
|
r182 | } | ||
Michal Klocek
|
r645 | void ChartTheme::decorate(QPieSeries* series, int index, bool force) | ||
Jani Honkonen
|
r163 | { | ||
Michal Klocek
|
r645 | QPen pen; | ||
QBrush brush; | ||||
Tero Ahola
|
r507 | for (int i(0); i < series->slices().count(); i++) { | ||
Tero Ahola
|
r653 | if (pen == series->slices().at(i)->slicePen() || force) { | ||
QColor penColor = colorAt(m_seriesGradients.at(index % m_seriesGradients.size()), 0.0); | ||||
series->slices().at(i)->setSlicePen(penColor); | ||||
Michal Klocek
|
r645 | } | ||
Tero Ahola
|
r653 | |||
// Get color for a slice from a gradient linearly, beginning from the start of the gradient | ||||
Tero Ahola
|
r661 | qreal pos = (qreal) (i + 1) / (qreal) series->count(); | ||
Tero Ahola
|
r653 | if (brush == series->slices().at(i)->sliceBrush() || force) { | ||
QColor brushColor = colorAt(m_seriesGradients.at(index % m_seriesGradients.size()), pos); | ||||
series->slices().at(i)->setSliceBrush(brushColor); | ||||
Michal Klocek
|
r645 | } | ||
Jani Honkonen
|
r163 | } | ||
} | ||||
Michal Klocek
|
r645 | void ChartTheme::decorate(QSplineSeries* series, int index, bool force) | ||
Marek Rosa
|
r401 | { | ||
Michal Klocek
|
r644 | QPen pen; | ||
Michal Klocek
|
r645 | if(pen == series->pen() || force){ | ||
Michal Klocek
|
r644 | pen.setColor(m_seriesColors.at(index%m_seriesColors.size())); | ||
pen.setWidthF(2); | ||||
series->setPen(pen); | ||||
} | ||||
Marek Rosa
|
r401 | } | ||
Michal Klocek
|
r645 | void ChartTheme::decorate(QChartAxis* axis,bool axisX, bool force) | ||
Tero Ahola
|
r548 | { | ||
Michal Klocek
|
r645 | QPen pen; | ||
QBrush brush; | ||||
QFont font; | ||||
Tero Ahola
|
r548 | if (axis->isAxisVisible()) { | ||
Michal Klocek
|
r645 | |||
if(brush == axis->labelsBrush() || force){ | ||||
axis->setLabelsBrush(m_axisLabelBrush); | ||||
} | ||||
if(pen == axis->labelsPen() || force){ | ||||
axis->setLabelsPen(Qt::NoPen); // NoPen for performance reasons | ||||
} | ||||
if (axis->shadesVisible() || force) { | ||||
if(brush == axis->shadesBrush() || force){ | ||||
axis->setShadesBrush(m_backgroundShadesBrush); | ||||
} | ||||
if(pen == axis->shadesPen() || force){ | ||||
axis->setShadesPen(m_backgroundShadesPen); | ||||
} | ||||
if(force && (m_backgroundShades == BackgroundShadesBoth | ||||
|| (m_backgroundShades == BackgroundShadesVertical && axisX) | ||||
|| (m_backgroundShades == BackgroundShadesHorizontal && !axisX))){ | ||||
axis->setShadesVisible(true); | ||||
} | ||||
} | ||||
if(pen == axis->axisPen() || force){ | ||||
axis->setAxisPen(m_axisLinePen); | ||||
} | ||||
if(pen == axis->gridLinePen() || force){ | ||||
axis->setGridLinePen(m_gridLinePen); | ||||
} | ||||
if(font == axis->labelsFont() || force){ | ||||
axis->setLabelsFont(m_masterFont); | ||||
Tero Ahola
|
r548 | } | ||
} | ||||
} | ||||
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; | ||||
Tero Ahola
|
r656 | start.setHsvF(h, 0.0, 1.0); | ||
Tero Ahola
|
r507 | 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 | ||