charttheme.cpp
398 lines
| 11.5 KiB
| text/x-c
|
CppLexer
/ src / charttheme.cpp
Jani Honkonen
|
r794 | /**************************************************************************** | ||
** | ||||
** Copyright (C) 2012 Digia Plc | ||||
** All rights reserved. | ||||
** For any questions to Digia, please use contact form at http://qt.digia.com | ||||
** | ||||
** This file is part of the Qt Commercial Charts Add-on. | ||||
** | ||||
** $QT_BEGIN_LICENSE$ | ||||
** Licensees holding valid Qt Commercial licenses may use this file in | ||||
** accordance with the Qt Commercial License Agreement provided with the | ||||
** Software or, alternatively, in accordance with the terms contained in | ||||
** a written agreement between you and Digia. | ||||
** | ||||
** If you have questions regarding the use of this file, please use | ||||
** contact form at http://qt.digia.com | ||||
** $QT_END_LICENSE$ | ||||
** | ||||
****************************************************************************/ | ||||
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 | ||
Tero Ahola
|
r853 | #include "chartthemesystem_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" | ||||
Tero Ahola
|
r757 | #include "chartthemehighcontrast_p.h" | ||
#include "chartthemeblueicy_p.h" | ||||
Tero Ahola
|
r125 | |||
Michal Klocek
|
r143 | QTCOMMERCIALCHART_BEGIN_NAMESPACE | ||
Tero Ahola
|
r614 | ChartTheme::ChartTheme(QChart::ChartTheme id) : | ||
Tero Ahola
|
r853 | m_masterFont(QFont("arial", 14)), | ||
Tero Ahola
|
r717 | m_labelFont(QFont("arial", 10)), | ||
Tero Ahola
|
r614 | 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), | ||||
Michal Klocek
|
r943 | m_gridLinePen(QPen(QRgb(0x000000))), | ||
m_force(false) | ||||
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(); | ||||
Tero Ahola
|
r757 | case QChart::ChartThemeHighContrast: | ||
return new ChartThemeHighContrast(); | ||||
case QChart::ChartThemeBlueIcy: | ||||
return new ChartThemeBlueIcy(); | ||||
Jani Honkonen
|
r494 | default: | ||
Tero Ahola
|
r853 | return new ChartThemeSystem(); | ||
Michal Klocek
|
r143 | } | ||
} | ||||
Michal Klocek
|
r943 | void ChartTheme::decorate(QChart* chart) | ||
Michal Klocek
|
r143 | { | ||
Michal Klocek
|
r645 | QBrush brush; | ||
Michal Klocek
|
r943 | if(brush == chart->backgroundBrush() || m_force) | ||
Tero Ahola
|
r757 | chart->setBackgroundBrush(m_chartBackgroundGradient); | ||
Michal Klocek
|
r645 | chart->setTitleFont(m_masterFont); | ||
chart->setTitleBrush(m_titleBrush); | ||||
Michal Klocek
|
r143 | } | ||
Michal Klocek
|
r943 | void ChartTheme::decorate(QLegend* legend) | ||
sauimone
|
r540 | { | ||
Michal Klocek
|
r645 | QPen pen; | ||
QBrush brush; | ||||
Michal Klocek
|
r943 | if (pen == legend->pen() || m_force){ | ||
sauimone
|
r783 | legend->setPen(Qt::NoPen); | ||
Michal Klocek
|
r645 | } | ||
Michal Klocek
|
r943 | if (brush == legend->brush() || m_force) { | ||
Michal Klocek
|
r645 | legend->setBrush(m_chartBackgroundGradient); | ||
} | ||||
sauimone
|
r540 | } | ||
Michal Klocek
|
r943 | void ChartTheme::decorate(QAreaSeries* series, int index) | ||
Michal Klocek
|
r421 | { | ||
Michal Klocek
|
r644 | QPen pen; | ||
QBrush brush; | ||||
Michal Klocek
|
r421 | |||
Michal Klocek
|
r943 | if (pen == series->pen() || m_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
|
r943 | if (brush == series->brush() || m_force) { | ||
Michal Klocek
|
r644 | QBrush brush(m_seriesColors.at(index % m_seriesColors.size())); | ||
series->setBrush(brush); | ||||
} | ||||
Michal Klocek
|
r421 | } | ||
Michal Klocek
|
r943 | void ChartTheme::decorate(QLineSeries* series,int index) | ||
Tero Ahola
|
r103 | { | ||
Michal Klocek
|
r644 | QPen pen; | ||
Michal Klocek
|
r943 | if(pen == series->pen() || m_force ){ | ||
Michal Klocek
|
r644 | pen.setColor(m_seriesColors.at(index%m_seriesColors.size())); | ||
pen.setWidthF(2); | ||||
series->setPen(pen); | ||||
} | ||||
Tero Ahola
|
r103 | } | ||
Michal Klocek
|
r943 | void ChartTheme::decorate(QBarSeries* series, int index) | ||
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 | } | ||
Michal Klocek
|
r943 | if (brush == sets.at(i)->brush() || m_force ) | ||
Tero Ahola
|
r661 | 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) | ||
sauimone
|
r820 | sets.at(i)->setLabelBrush(colorAt(m_seriesGradients.at(index % m_seriesGradients.size()), 1)); | ||
Tero Ahola
|
r653 | else | ||
sauimone
|
r820 | sets.at(i)->setLabelBrush(colorAt(m_seriesGradients.at(index % m_seriesGradients.size()), 0)); | ||
Michal Klocek
|
r645 | |||
Michal Klocek
|
r943 | if (pen == sets.at(i)->pen() || m_force) { | ||
Tero Ahola
|
r653 | 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
|
r943 | void ChartTheme::decorate(QScatterSeries* series, int index) | ||
Tero Ahola
|
r182 | { | ||
Michal Klocek
|
r644 | QPen pen; | ||
QBrush brush; | ||||
Michal Klocek
|
r943 | if (pen == series->pen() || m_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
|
r943 | if (brush == series->brush() || m_force) { | ||
Michal Klocek
|
r644 | QBrush brush(m_seriesColors.at(index % m_seriesColors.size())); | ||
series->setBrush(brush); | ||||
} | ||||
Tero Ahola
|
r182 | } | ||
Michal Klocek
|
r943 | void ChartTheme::decorate(QPieSeries* series, int index) | ||
Jani Honkonen
|
r163 | { | ||
Jani Honkonen
|
r818 | |||
Tero Ahola
|
r507 | for (int i(0); i < series->slices().count(); i++) { | ||
Jani Honkonen
|
r691 | |||
QColor penColor = colorAt(m_seriesGradients.at(index % m_seriesGradients.size()), 0.0); | ||||
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(); | ||
Jani Honkonen
|
r691 | QColor brushColor = colorAt(m_seriesGradients.at(index % m_seriesGradients.size()), pos); | ||
Jani Honkonen
|
r818 | QPieSlice *s = series->slices().at(i); | ||
Jani Honkonen
|
r822 | PieSliceData data = PieSliceData::data(s); | ||
Jani Honkonen
|
r691 | |||
Michal Klocek
|
r943 | if (data.m_slicePen.isThemed() || m_force) { | ||
Jani Honkonen
|
r691 | data.m_slicePen = penColor; | ||
data.m_slicePen.setThemed(true); | ||||
} | ||||
Michal Klocek
|
r943 | if (data.m_sliceBrush.isThemed() || m_force) { | ||
Jani Honkonen
|
r691 | data.m_sliceBrush = brushColor; | ||
data.m_sliceBrush.setThemed(true); | ||||
} | ||||
Michal Klocek
|
r943 | if (data.m_labelPen.isThemed() || m_force) { | ||
Jani Honkonen
|
r714 | data.m_labelPen = QPen(m_titleBrush.color()); | ||
data.m_labelPen.setThemed(true); | ||||
} | ||||
Michal Klocek
|
r943 | if (data.m_labelFont.isThemed() || m_force) { | ||
Tero Ahola
|
r717 | data.m_labelFont = m_labelFont; | ||
Jani Honkonen
|
r714 | data.m_labelFont.setThemed(true); | ||
} | ||||
Jani Honkonen
|
r822 | if (PieSliceData::data(s) != data) { | ||
PieSliceData::data(s) = data; | ||||
emit PieSliceData::data(s).emitChangedSignal(s); | ||||
Michal Klocek
|
r645 | } | ||
Jani Honkonen
|
r163 | } | ||
} | ||||
Michal Klocek
|
r943 | void ChartTheme::decorate(QSplineSeries* series, int index) | ||
Marek Rosa
|
r401 | { | ||
Michal Klocek
|
r644 | QPen pen; | ||
Michal Klocek
|
r943 | if(pen == series->pen() || m_force){ | ||
Michal Klocek
|
r644 | pen.setColor(m_seriesColors.at(index%m_seriesColors.size())); | ||
pen.setWidthF(2); | ||||
series->setPen(pen); | ||||
} | ||||
Marek Rosa
|
r401 | } | ||
Michal Klocek
|
r943 | void ChartTheme::decorate(QChartAxis* axis,bool axisX) | ||
Tero Ahola
|
r548 | { | ||
Michal Klocek
|
r645 | QPen pen; | ||
QBrush brush; | ||||
QFont font; | ||||
Tero Ahola
|
r548 | if (axis->isAxisVisible()) { | ||
Michal Klocek
|
r645 | |||
Michal Klocek
|
r943 | if(brush == axis->labelsBrush() || m_force){ | ||
Michal Klocek
|
r645 | axis->setLabelsBrush(m_axisLabelBrush); | ||
} | ||||
Michal Klocek
|
r943 | if(pen == axis->labelsPen() || m_force){ | ||
Michal Klocek
|
r645 | axis->setLabelsPen(Qt::NoPen); // NoPen for performance reasons | ||
} | ||||
Michal Klocek
|
r943 | if (axis->shadesVisible() || m_force) { | ||
Michal Klocek
|
r645 | |||
Michal Klocek
|
r943 | if(brush == axis->shadesBrush() || m_force){ | ||
Michal Klocek
|
r645 | axis->setShadesBrush(m_backgroundShadesBrush); | ||
} | ||||
Michal Klocek
|
r943 | if(pen == axis->shadesPen() || m_force){ | ||
Michal Klocek
|
r645 | axis->setShadesPen(m_backgroundShadesPen); | ||
} | ||||
Michal Klocek
|
r943 | if( m_force && (m_backgroundShades == BackgroundShadesBoth | ||
Michal Klocek
|
r645 | || (m_backgroundShades == BackgroundShadesVertical && axisX) | ||
|| (m_backgroundShades == BackgroundShadesHorizontal && !axisX))){ | ||||
axis->setShadesVisible(true); | ||||
} | ||||
} | ||||
Michal Klocek
|
r943 | if(pen == axis->axisPen() || m_force){ | ||
Michal Klocek
|
r645 | axis->setAxisPen(m_axisLinePen); | ||
} | ||||
Michal Klocek
|
r943 | if(pen == axis->gridLinePen() || m_force){ | ||
Michal Klocek
|
r645 | axis->setGridLinePen(m_gridLinePen); | ||
} | ||||
Michal Klocek
|
r943 | if(font == axis->labelsFont() || m_force){ | ||
Tero Ahola
|
r717 | axis->setLabelsFont(m_labelFont); | ||
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); | ||||
} | ||||
Michal Klocek
|
r943 | void ChartTheme::setForced(bool enabled) | ||
{ | ||||
m_force=enabled; | ||||
} | ||||
Tero Ahola
|
r103 | QTCOMMERCIALCHART_END_NAMESPACE | ||