qchart.cpp
414 lines
| 12.2 KiB
| text/x-c
|
CppLexer
/ src / qchart.cpp
Michal Klocek
|
r12 | #include "qchart.h" | ||
Michal Klocek
|
r21 | #include "qchartseries.h" | ||
Tero Ahola
|
r42 | #include "qscatterseries.h" | ||
#include "qscatterseries_p.h" | ||||
Tero Ahola
|
r51 | #include "qpieseries.h" | ||
Tero Ahola
|
r103 | #include "qpieseries_p.h" | ||
Michal Klocek
|
r45 | #include "qxychartseries.h" | ||
Michal Klocek
|
r85 | #include "qchartaxis.h" | ||
sauimone
|
r56 | #include "barchartseries.h" | ||
#include "bargroup.h" | ||||
sauimone
|
r95 | #include "stackedbarchartseries.h" | ||
#include "stackedbargroup.h" | ||||
sauimone
|
r101 | #include "percentbarchartseries.h" | ||
#include "percentbargroup.h" | ||||
Tero Ahola
|
r103 | #include "charttheme_p.h" | ||
Tero Ahola
|
r108 | #include "chartitem_p.h" | ||
sauimone
|
r56 | |||
Michal Klocek
|
r21 | #include "xylinechartitem_p.h" | ||
Michal Klocek
|
r67 | #include "plotdomain_p.h" | ||
#include "axisitem_p.h" | ||||
Tero Ahola
|
r42 | #include <QGraphicsScene> | ||
Michal Klocek
|
r115 | #include <QGraphicsSceneResizeEvent> | ||
Michal Klocek
|
r21 | #include <QDebug> | ||
Michal Klocek
|
r12 | |||
Tero Ahola
|
r30 | QTCOMMERCIALCHART_BEGIN_NAMESPACE | ||
Michal Klocek
|
r12 | |||
Michal Klocek
|
r115 | QChart::QChart(QGraphicsItem *parent, Qt::WindowFlags wFlags) : QGraphicsWidget(parent,wFlags), | ||
Michal Klocek
|
r87 | m_backgroundItem(0), | ||
m_titleItem(0), | ||||
Tero Ahola
|
r110 | m_axisXItem(new AxisItem(AxisItem::X_AXIS, this)), | ||
Tero Ahola
|
r64 | m_plotDataIndex(0), | ||
Tero Ahola
|
r103 | m_marginSize(0), | ||
Tero Ahola
|
r110 | m_chartTheme(new ChartTheme(this)) | ||
Michal Klocek
|
r12 | { | ||
Tero Ahola
|
r64 | // TODO: the default theme? | ||
Tero Ahola
|
r91 | setTheme(QChart::ChartThemeDefault); | ||
Michal Klocek
|
r85 | |||
Michal Klocek
|
r67 | PlotDomain domain; | ||
Tero Ahola
|
r103 | m_plotDomainList << domain; | ||
Michal Klocek
|
r87 | m_axisYItem << new AxisItem(AxisItem::Y_AXIS,this); | ||
Tero Ahola
|
r108 | m_chartItems << m_axisXItem; | ||
m_chartItems << m_axisYItem.at(0); | ||||
Michal Klocek
|
r12 | } | ||
QChart::~QChart(){} | ||||
Michal Klocek
|
r21 | void QChart::addSeries(QChartSeries* series) | ||
Michal Klocek
|
r12 | { | ||
Tero Ahola
|
r48 | // TODO: we should check the series not already added | ||
Tero Ahola
|
r120 | Q_ASSERT(series); | ||
Q_ASSERT(series->type() != QChartSeries::SeriesTypeInvalid); | ||||
Michal Klocek
|
r45 | |||
Tero Ahola
|
r75 | m_chartSeries << series; | ||
Michal Klocek
|
r21 | |||
Tero Ahola
|
r111 | m_plotDataIndex = 0 ; | ||
m_plotDomainList.resize(1); | ||||
PlotDomain& domain = m_plotDomainList[m_plotDataIndex]; | ||||
Michal Klocek
|
r21 | switch(series->type()) | ||
{ | ||||
Tero Ahola
|
r30 | case QChartSeries::SeriesTypeLine: { | ||
Michal Klocek
|
r47 | |||
QXYChartSeries* xyseries = static_cast<QXYChartSeries*>(series); | ||||
Tero Ahola
|
r108 | for (int i = 0 ; i < xyseries->count() ; i++) { | ||
Michal Klocek
|
r47 | qreal x = xyseries->x(i); | ||
qreal y = xyseries->y(i); | ||||
domain.m_minX = qMin(domain.m_minX,x); | ||||
domain.m_minY = qMin(domain.m_minY,y); | ||||
domain.m_maxX = qMax(domain.m_maxX,x); | ||||
domain.m_maxY = qMax(domain.m_maxY,y); | ||||
} | ||||
XYLineChartItem* item = new XYLineChartItem(xyseries,this); | ||||
Michal Klocek
|
r67 | |||
Tero Ahola
|
r108 | m_chartItems << item; | ||
// TODO: | ||||
//m_chartTheme->addObserver(xyseries); | ||||
Michal Klocek
|
r67 | |||
Michal Klocek
|
r21 | break; | ||
Michal Klocek
|
r12 | } | ||
sauimone
|
r56 | case QChartSeries::SeriesTypeBar: { | ||
qDebug() << "barSeries added"; | ||||
BarChartSeries* barSeries = static_cast<BarChartSeries*>(series); | ||||
sauimone
|
r78 | BarGroup* barGroup = new BarGroup(*barSeries,this); | ||
sauimone
|
r82 | |||
// Add some fugly colors for 5 fist series... | ||||
barGroup->addColor(QColor(255,0,0,128)); | ||||
barGroup->addColor(QColor(255,255,0,128)); | ||||
barGroup->addColor(QColor(0,255,0,128)); | ||||
barGroup->addColor(QColor(0,0,255,128)); | ||||
barGroup->addColor(QColor(255,128,0,128)); | ||||
Tero Ahola
|
r108 | m_chartItems << barGroup; | ||
sauimone
|
r78 | childItems().append(barGroup); | ||
sauimone
|
r107 | |||
qreal x = barSeries->countColumns(); | ||||
qreal y = barSeries->max(); | ||||
domain.m_minX = qMin(domain.m_minX,x); | ||||
domain.m_minY = qMin(domain.m_minY,y); | ||||
domain.m_maxX = qMax(domain.m_maxX,x); | ||||
domain.m_maxY = qMax(domain.m_maxY,y); | ||||
sauimone
|
r119 | m_axisXItem->setVisible(false); | ||
sauimone
|
r56 | break; | ||
} | ||||
sauimone
|
r95 | case QChartSeries::SeriesTypeStackedBar: { | ||
qDebug() << "barSeries added"; | ||||
StackedBarChartSeries* stackedBarSeries = static_cast<StackedBarChartSeries*>(series); | ||||
StackedBarGroup* stackedBarGroup = new StackedBarGroup(*stackedBarSeries,this); | ||||
// Add some fugly colors for 5 fist series... | ||||
stackedBarGroup->addColor(QColor(255,0,0,128)); | ||||
stackedBarGroup->addColor(QColor(255,255,0,128)); | ||||
stackedBarGroup->addColor(QColor(0,255,0,128)); | ||||
stackedBarGroup->addColor(QColor(0,0,255,128)); | ||||
stackedBarGroup->addColor(QColor(255,128,0,128)); | ||||
Tero Ahola
|
r108 | m_chartItems << stackedBarGroup; | ||
sauimone
|
r95 | childItems().append(stackedBarGroup); | ||
sauimone
|
r107 | |||
qreal x = stackedBarSeries->countColumns(); | ||||
qreal y = stackedBarSeries->maxColumnSum(); | ||||
domain.m_minX = qMin(domain.m_minX,x); | ||||
domain.m_minY = qMin(domain.m_minY,y); | ||||
domain.m_maxX = qMax(domain.m_maxX,x); | ||||
domain.m_maxY = qMax(domain.m_maxY,y); | ||||
sauimone
|
r119 | m_axisXItem->setVisible(false); | ||
sauimone
|
r95 | break; | ||
} | ||||
sauimone
|
r101 | case QChartSeries::SeriesTypePercentBar: { | ||
qDebug() << "barSeries added"; | ||||
PercentBarChartSeries* percentBarSeries = static_cast<PercentBarChartSeries*>(series); | ||||
PercentBarGroup* percentBarGroup = new PercentBarGroup(*percentBarSeries,this); | ||||
// Add some fugly colors for 5 fist series... | ||||
percentBarGroup->addColor(QColor(255,0,0,128)); | ||||
percentBarGroup->addColor(QColor(255,255,0,128)); | ||||
percentBarGroup->addColor(QColor(0,255,0,128)); | ||||
percentBarGroup->addColor(QColor(0,0,255,128)); | ||||
percentBarGroup->addColor(QColor(255,128,0,128)); | ||||
Tero Ahola
|
r108 | m_chartItems << percentBarGroup; | ||
sauimone
|
r101 | childItems().append(percentBarGroup); | ||
sauimone
|
r107 | |||
qreal x = percentBarSeries->countColumns(); | ||||
domain.m_minX = qMin(domain.m_minX,x); | ||||
domain.m_minY = 0; | ||||
domain.m_maxX = qMax(domain.m_maxX,x); | ||||
domain.m_maxY = 100; | ||||
sauimone
|
r119 | m_axisXItem->setVisible(false); | ||
sauimone
|
r101 | break; | ||
} | ||||
Tero Ahola
|
r42 | case QChartSeries::SeriesTypeScatter: { | ||
Tero Ahola
|
r65 | QScatterSeries *scatterSeries = qobject_cast<QScatterSeries *>(series); | ||
Tero Ahola
|
r108 | scatterSeries->d->m_theme = m_chartTheme->themeForSeries(); | ||
Tero Ahola
|
r75 | scatterSeries->d->setParentItem(this); | ||
Tero Ahola
|
r111 | scatterSeries->d->m_boundingRect = m_rect.adjusted(margin(),margin(), -margin(), -margin()); | ||
Tero Ahola
|
r108 | m_chartItems << scatterSeries->d; | ||
m_chartTheme->addObserver(scatterSeries->d); | ||||
Tero Ahola
|
r111 | |||
foreach (qreal x, scatterSeries->d->m_x) { | ||||
domain.m_minX = qMin(domain.m_minX, x); | ||||
domain.m_maxX = qMax(domain.m_maxX, x); | ||||
} | ||||
foreach (qreal y, scatterSeries->d->m_y) { | ||||
domain.m_minY = qMin(domain.m_minY, y); | ||||
domain.m_maxY = qMax(domain.m_maxY, y); | ||||
} | ||||
Tero Ahola
|
r75 | break; | ||
Tero Ahola
|
r65 | } | ||
Tero Ahola
|
r51 | case QChartSeries::SeriesTypePie: { | ||
Tero Ahola
|
r65 | QPieSeries *pieSeries = qobject_cast<QPieSeries *>(series); | ||
Tero Ahola
|
r108 | pieSeries->d->setParentItem(this); | ||
m_chartItems << pieSeries->d; | ||||
pieSeries->d->m_chartTheme = m_chartTheme; | ||||
m_chartTheme->addObserver(pieSeries->d); | ||||
Tero Ahola
|
r75 | break; | ||
Tero Ahola
|
r65 | } | ||
Tero Ahola
|
r120 | default: | ||
break; | ||||
Tero Ahola
|
r65 | } | ||
Tero Ahola
|
r108 | |||
// Update all the items to match the new visible area of the chart | ||||
foreach(ChartItem* i, m_chartItems) | ||||
i->setPlotDomain(m_plotDomainList.at(m_plotDataIndex)); | ||||
Tero Ahola
|
r65 | } | ||
QChartSeries* QChart::createSeries(QChartSeries::QChartSeriesType type) | ||||
{ | ||||
// TODO: support also other types; not only scatter and pie | ||||
QChartSeries *series(0); | ||||
switch (type) { | ||||
case QChartSeries::SeriesTypeLine: { | ||||
series = QXYChartSeries::create(); | ||||
break; | ||||
} | ||||
case QChartSeries::SeriesTypeBar: { | ||||
series = new BarChartSeries(this); | ||||
break; | ||||
} | ||||
sauimone
|
r95 | case QChartSeries::SeriesTypeStackedBar: { | ||
series = new StackedBarChartSeries(this); | ||||
break; | ||||
} | ||||
sauimone
|
r101 | case QChartSeries::SeriesTypePercentBar: { | ||
series = new PercentBarChartSeries(this); | ||||
break; | ||||
} | ||||
Tero Ahola
|
r65 | case QChartSeries::SeriesTypeScatter: { | ||
series = new QScatterSeries(this); | ||||
break; | ||||
} | ||||
case QChartSeries::SeriesTypePie: { | ||||
series = new QPieSeries(this); | ||||
break; | ||||
Tero Ahola
|
r42 | } | ||
default: | ||||
Tero Ahola
|
r61 | Q_ASSERT(false); | ||
Tero Ahola
|
r42 | break; | ||
} | ||||
Tero Ahola
|
r65 | addSeries(series); | ||
return series; | ||||
Tero Ahola
|
r42 | } | ||
Tero Ahola
|
r48 | |||
Michal Klocek
|
r86 | void QChart::setBackground(const QColor& startColor, const QColor& endColor, GradientOrientation orientation) | ||
Michal Klocek
|
r69 | { | ||
Michal Klocek
|
r86 | |||
Michal Klocek
|
r87 | if(!m_backgroundItem){ | ||
m_backgroundItem = new QGraphicsRectItem(this); | ||||
m_backgroundItem->setZValue(-1); | ||||
Michal Klocek
|
r86 | } | ||
m_bacgroundOrinetation = orientation; | ||||
Tero Ahola
|
r103 | m_backgroundGradient.setColorAt(0.0, startColor); | ||
m_backgroundGradient.setColorAt(1.0, endColor); | ||||
Michal Klocek
|
r86 | m_backgroundGradient.setStart(0,0); | ||
if(orientation == VerticalGradientOrientation){ | ||||
m_backgroundGradient.setFinalStop(0,m_rect.height()); | ||||
}else{ | ||||
m_backgroundGradient.setFinalStop(m_rect.width(),0); | ||||
} | ||||
Michal Klocek
|
r87 | m_backgroundItem->setBrush(m_backgroundGradient); | ||
m_backgroundItem->setPen(Qt::NoPen); | ||||
m_backgroundItem->update(); | ||||
Michal Klocek
|
r69 | } | ||
Michal Klocek
|
r87 | void QChart::setTitle(const QString& title,const QFont& font) | ||
Michal Klocek
|
r69 | { | ||
Michal Klocek
|
r87 | if(!m_titleItem) m_titleItem = new QGraphicsTextItem(this); | ||
m_titleItem->setPlainText(title); | ||||
m_titleItem->setFont(font); | ||||
Michal Klocek
|
r69 | } | ||
Michal Klocek
|
r28 | int QChart::margin() const | ||
{ | ||||
Michal Klocek
|
r53 | return m_marginSize; | ||
Michal Klocek
|
r28 | } | ||
Michal Klocek
|
r12 | void QChart::setMargin(int margin) | ||
{ | ||||
Michal Klocek
|
r53 | m_marginSize = margin; | ||
Michal Klocek
|
r12 | } | ||
Tero Ahola
|
r75 | void QChart::setTheme(QChart::ChartThemeId theme) | ||
Tero Ahola
|
r64 | { | ||
Tero Ahola
|
r108 | m_chartTheme->setTheme(theme); | ||
setBackground(m_chartTheme->d->m_gradientStartColor, | ||||
m_chartTheme->d->m_gradientEndColor, | ||||
m_bacgroundOrinetation); | ||||
// TODO: Move the controlling of the series presentations into private implementation of the | ||||
// series instead of QChart controlling themes for each | ||||
// In other words, the following should be used when creating xy series: | ||||
// m_chartTheme->addObserver(xyseries) | ||||
foreach (QChartSeries *series, m_chartSeries) { | ||||
if (series->type() == QChartSeries::SeriesTypeLine) { | ||||
QXYChartSeries *xyseries = static_cast<QXYChartSeries *>(series); | ||||
SeriesTheme seriesTheme = m_chartTheme->themeForSeries(); | ||||
xyseries->setPen(seriesTheme.linePen); | ||||
} | ||||
Tero Ahola
|
r75 | } | ||
Tero Ahola
|
r108 | |||
update(); | ||||
Tero Ahola
|
r64 | } | ||
Tero Ahola
|
r120 | QChart::ChartThemeId QChart::theme() | ||
{ | ||||
return (QChart::ChartThemeId) m_chartTheme->d->m_currentTheme; | ||||
} | ||||
Michal Klocek
|
r115 | void QChart::zoomInToRect(const QRectF& rectangle) | ||
Michal Klocek
|
r67 | { | ||
if(!rectangle.isValid()) return; | ||||
qreal margin = this->margin(); | ||||
Michal Klocek
|
r115 | QRectF rect = rectangle.normalized(); | ||
Michal Klocek
|
r67 | rect.translate(-margin, -margin); | ||
PlotDomain& oldDomain = m_plotDomainList[m_plotDataIndex]; | ||||
Michal Klocek
|
r76 | PlotDomain domain = oldDomain.subDomain(rect,m_rect.width() - 2 * margin,m_rect.height() - 2 * margin); | ||
Michal Klocek
|
r67 | |||
m_plotDomainList.resize(m_plotDataIndex + 1); | ||||
Michal Klocek
|
r76 | m_plotDomainList<<domain; | ||
Michal Klocek
|
r67 | m_plotDataIndex++; | ||
Tero Ahola
|
r108 | foreach (ChartItem* item, m_chartItems) | ||
item->setPlotDomain(m_plotDomainList[m_plotDataIndex]); | ||||
Michal Klocek
|
r67 | update(); | ||
} | ||||
void QChart::zoomIn() | ||||
{ | ||||
if (m_plotDataIndex < m_plotDomainList.count() - 1) { | ||||
m_plotDataIndex++; | ||||
Tero Ahola
|
r108 | foreach (ChartItem* item, m_chartItems) | ||
Michal Klocek
|
r67 | item->setPlotDomain(m_plotDomainList[m_plotDataIndex]); | ||
update(); | ||||
Tero Ahola
|
r93 | } else { | ||
Michal Klocek
|
r115 | QRectF rect = m_rect.adjusted(margin(),margin(), -margin(), -margin()); | ||
Michal Klocek
|
r67 | rect.setWidth(rect.width()/2); | ||
rect.setHeight(rect.height()/2); | ||||
rect.moveCenter(m_rect.center()); | ||||
zoomInToRect(rect); | ||||
} | ||||
} | ||||
void QChart::zoomOut() | ||||
{ | ||||
if (m_plotDataIndex > 0) { | ||||
m_plotDataIndex--; | ||||
Tero Ahola
|
r108 | foreach (ChartItem* item, m_chartItems) | ||
Michal Klocek
|
r67 | item->setPlotDomain(m_plotDomainList[m_plotDataIndex]); | ||
update(); | ||||
} | ||||
} | ||||
Tero Ahola
|
r93 | void QChart::zoomReset() | ||
{ | ||||
if (m_plotDataIndex > 0) { | ||||
m_plotDataIndex = 0; | ||||
Tero Ahola
|
r108 | foreach (ChartItem* item, m_chartItems) | ||
Tero Ahola
|
r93 | item->setPlotDomain(m_plotDomainList[m_plotDataIndex]); | ||
update(); | ||||
} | ||||
} | ||||
Michal Klocek
|
r85 | void QChart::setAxisX(const QChartAxis& axis) | ||
{ | ||||
Michal Klocek
|
r87 | setAxis(m_axisXItem,axis); | ||
Michal Klocek
|
r85 | } | ||
void QChart::setAxisY(const QChartAxis& axis) | ||||
{ | ||||
Michal Klocek
|
r87 | setAxis(m_axisYItem.at(0),axis); | ||
Michal Klocek
|
r85 | } | ||
void QChart::setAxisY(const QList<QChartAxis>& axis) | ||||
{ | ||||
//TODO not implemented | ||||
} | ||||
void QChart::setAxis(AxisItem *item, const QChartAxis& axis) | ||||
{ | ||||
item->setVisible(axis.isAxisVisible()); | ||||
} | ||||
Michal Klocek
|
r115 | void QChart::resizeEvent(QGraphicsSceneResizeEvent *event) | ||
{ | ||||
m_rect = QRectF(QPoint(0,0),event->newSize()); | ||||
QRectF rect = m_rect.adjusted(margin(),margin(), -margin(), -margin()); | ||||
// recalculate title position | ||||
if (m_titleItem) { | ||||
QPointF center = m_rect.center() -m_titleItem->boundingRect().center(); | ||||
m_titleItem->setPos(center.x(),m_rect.top()/2 + margin()/2); | ||||
} | ||||
//recalculate background gradient | ||||
if (m_backgroundItem) { | ||||
m_backgroundItem->setRect(rect); | ||||
if (m_bacgroundOrinetation == HorizonatlGradientOrientation) | ||||
m_backgroundGradient.setFinalStop(m_backgroundItem->rect().width(), 0); | ||||
else | ||||
m_backgroundGradient.setFinalStop(0, m_backgroundItem->rect().height()); | ||||
m_backgroundItem->setBrush(m_backgroundGradient); | ||||
} | ||||
// resize and reposition childs | ||||
foreach (ChartItem *item, m_chartItems) { | ||||
item->setPos(rect.topLeft()); | ||||
item->setSize(rect.size()); | ||||
} | ||||
Michal Klocek
|
r117 | QGraphicsWidget::resizeEvent(event); | ||
Michal Klocek
|
r115 | update(); | ||
} | ||||
Tero Ahola
|
r64 | #include "moc_qchart.cpp" | ||
Tero Ahola
|
r48 | |||
Tero Ahola
|
r30 | QTCOMMERCIALCHART_END_NAMESPACE | ||