void KReportChartView::drawPivotChart(const PivotGrid &grid, const MyMoneyReport &config, int numberColumns, const QStringList& columnHeadings, const QList<ERowType>& rowTypeList, const QStringList& columnTypeHeaderList) { //set the number of columns setNumColumns(numberColumns); //set skipZero m_skipZero = config.isSkippingZero(); //remove existing headers while (headerFooters().count() > 0) { HeaderFooter* delHeader = headerFooters().at(0); takeHeaderFooter(delHeader); delete delHeader; } //make sure the model is clear m_model.removeColumns(0, m_model.columnCount()); m_model.removeRows(0, m_model.rowCount()); //set the new header HeaderFooter* header = new HeaderFooter(this); header->setText(config.name()); header->setType(HeaderFooter::Header); header->setPosition(Position::North); TextAttributes headerTextAttr(header->textAttributes()); headerTextAttr.setPen(m_foregroundBrush.color()); header->setTextAttributes(headerTextAttr); addHeaderFooter(header); // whether to limit the chart to use series totals only. Used for reports which only // show one dimension (pie). setSeriesTotals(false); // whether series (rows) are accounts (true) or months (false). This causes a lot // of complexity in the charts. The problem is that circular reports work best with // an account in a COLUMN, while line/bar prefer it in a ROW. setAccountSeries(true); switch (config.chartType()) { case MyMoneyReport::eChartNone: case MyMoneyReport::eChartEnd: case MyMoneyReport::eChartLine: { KDChart::LineDiagram* diagram = new KDChart::LineDiagram; if (config.isSkippingZero()) { LineAttributes attributes = diagram->lineAttributes(); attributes.setMissingValuesPolicy(LineAttributes::MissingValuesAreBridged); diagram->setLineAttributes(attributes); } CartesianCoordinatePlane* cartesianPlane = new CartesianCoordinatePlane; replaceCoordinatePlane(cartesianPlane); coordinatePlane()->replaceDiagram(diagram); break; } case MyMoneyReport::eChartBar: { KDChart::BarDiagram* diagram = new KDChart::BarDiagram; CartesianCoordinatePlane* cartesianPlane = new CartesianCoordinatePlane; replaceCoordinatePlane(cartesianPlane); coordinatePlane()->replaceDiagram(diagram); break; } case MyMoneyReport::eChartStackedBar: { KDChart::BarDiagram* diagram = new KDChart::BarDiagram; CartesianCoordinatePlane* cartesianPlane = new CartesianCoordinatePlane; replaceCoordinatePlane(cartesianPlane); diagram->setType(BarDiagram::Stacked); coordinatePlane()->replaceDiagram(diagram); break; } case MyMoneyReport::eChartPie: { KDChart::PieDiagram* diagram = new KDChart::PieDiagram; PolarCoordinatePlane* polarPlane = new PolarCoordinatePlane; replaceCoordinatePlane(polarPlane); coordinatePlane()->replaceDiagram(diagram); setAccountSeries(false); setSeriesTotals(true); break; } case MyMoneyReport::eChartRing: { KDChart::RingDiagram* diagram = new KDChart::RingDiagram; PolarCoordinatePlane* polarPlane = new PolarCoordinatePlane; replaceCoordinatePlane(polarPlane); polarPlane->replaceDiagram(diagram); //chartView.params()->setRelativeRingThickness( true ); setAccountSeries(false); break; } } //get the diagram for later use AbstractDiagram* planeDiagram = coordinatePlane()->diagram(); //set grid attributes GridAttributes gridAttr(coordinatePlane()->globalGridAttributes()); gridAttr.setGridVisible(config.isChartGridLines()); coordinatePlane()->setGlobalGridAttributes(gridAttr); //the palette - we set it here because it is a property of the diagram switch (KMyMoneySettings::chartsPalette()) { case 0: planeDiagram->useDefaultColors(); break; case 1: planeDiagram->useRainbowColors(); break; case 2: default: planeDiagram->useSubduedColors(); break; } //the legend will be used later Legend* legend = new Legend(planeDiagram, this); legend->setTitleText(i18nc("Chart legend title", "Legend")); //set up the axes for cartesian diagrams if (config.chartType() == MyMoneyReport::eChartLine || config.chartType() == MyMoneyReport::eChartBar || config.chartType() == MyMoneyReport::eChartStackedBar) { //set x axis CartesianAxis *xAxis = new CartesianAxis(); xAxis->setPosition(CartesianAxis::Bottom); xAxis->setTitleText(i18n("Time")); TextAttributes xAxisTitleTextAttr(xAxis->titleTextAttributes()); xAxisTitleTextAttr.setMinimalFontSize(KGlobalSettings::generalFont().pointSize()); xAxisTitleTextAttr.setPen(m_foregroundBrush.color()); xAxis->setTitleTextAttributes(xAxisTitleTextAttr); TextAttributes xAxisTextAttr(xAxis->textAttributes()); xAxisTextAttr.setPen(m_foregroundBrush.color()); xAxis->setTextAttributes(xAxisTextAttr); RulerAttributes xAxisRulerAttr(xAxis->rulerAttributes()); xAxisRulerAttr.setTickMarkPen(m_foregroundBrush.color()); xAxisRulerAttr.setShowRulerLine(true); xAxis->setRulerAttributes(xAxisRulerAttr); // Set up X axis labels (ie "abscissa" to use the technical term) QStringList abscissaNames; if (accountSeries()) { // if not, we will set these up while putting in the chart values. int column = 1; while (column < numColumns()) { abscissaNames += QString(columnHeadings[column++]).replace(" ", " "); } xAxis->setLabels(abscissaNames); } //set y axis KBalanceAxis *yAxis = new KBalanceAxis(); yAxis->setPosition(CartesianAxis::Left); // TODO // if the chart shows prices and no balance // the axis title should be 'Price' if (config.isIncludingPrice()) { yAxis->setTitleText(i18n("Price")); } else { yAxis->setTitleText(i18n("Balance")); } TextAttributes yAxisTitleTextAttr(yAxis->titleTextAttributes()); yAxisTitleTextAttr.setMinimalFontSize(KGlobalSettings::generalFont().pointSize()); yAxisTitleTextAttr.setPen(m_foregroundBrush.color()); yAxis->setTitleTextAttributes(yAxisTitleTextAttr); TextAttributes yAxisTextAttr(yAxis->textAttributes()); yAxisTextAttr.setPen(m_foregroundBrush.color()); yAxis->setTextAttributes(yAxisTextAttr); RulerAttributes yAxisRulerAttr(yAxis->rulerAttributes()); yAxisRulerAttr.setTickMarkPen(m_foregroundBrush.color()); yAxisRulerAttr.setShowRulerLine(true); yAxis->setRulerAttributes(yAxisRulerAttr); //add the axes to the corresponding diagram if (config.chartType() == MyMoneyReport::eChartLine) { KDChart::LineDiagram* lineDiagram = qobject_cast<LineDiagram*>(planeDiagram); lineDiagram->addAxis(xAxis); lineDiagram->addAxis(yAxis); } else if (config.chartType() == MyMoneyReport::eChartBar || config.chartType() == MyMoneyReport::eChartStackedBar) { KDChart::BarDiagram* barDiagram = qobject_cast<BarDiagram*>(planeDiagram); barDiagram->addAxis(xAxis); barDiagram->addAxis(yAxis); } } switch (config.detailLevel()) { case MyMoneyReport::eDetailNone: case MyMoneyReport::eDetailEnd: case MyMoneyReport::eDetailAll: { int rowNum = 0; // iterate over outer groups PivotGrid::const_iterator it_outergroup = grid.begin(); while (it_outergroup != grid.end()) { // iterate over inner groups PivotOuterGroup::const_iterator it_innergroup = (*it_outergroup).begin(); while (it_innergroup != (*it_outergroup).end()) { // // Rows // QString innergroupdata; PivotInnerGroup::const_iterator it_row = (*it_innergroup).begin(); while (it_row != (*it_innergroup).end()) { //Do not include investments accounts in the chart because they are merely container of stock and other accounts if (it_row.key().accountType() != MyMoneyAccount::Investment) { //iterate row types for (int i = 0; i < rowTypeList.size(); ++i) { //skip the budget difference rowset if (rowTypeList[i] != eBudgetDiff) { QString legendText; //only show the column type in the header if there is more than one type if (rowTypeList.size() > 1) { legendText = QString(columnTypeHeaderList[i] + " - " + it_row.key().name()); } else { legendText = QString(it_row.key().name()); } //set the cell value and tooltip rowNum = drawPivotRowSet(rowNum, it_row.value(), rowTypeList[i], legendText, 1, numColumns()); //set the legend text legend->setText(rowNum - 1, legendText); } } } ++it_row; } ++it_innergroup; } ++it_outergroup; } } break; case MyMoneyReport::eDetailTop: { int rowNum = 0; // iterate over outer groups PivotGrid::const_iterator it_outergroup = grid.begin(); while (it_outergroup != grid.end()) { // iterate over inner groups PivotOuterGroup::const_iterator it_innergroup = (*it_outergroup).begin(); while (it_innergroup != (*it_outergroup).end()) { //iterate row types for (int i = 0; i < rowTypeList.size(); ++i) { //skip the budget difference rowset if (rowTypeList[i] != eBudgetDiff) { QString legendText; //only show the column type in the header if there is more than one type if (rowTypeList.size() > 1) { legendText = QString(columnTypeHeaderList[i] + " - " + it_innergroup.key()); } else { legendText = QString(it_innergroup.key()); } //set the cell value and tooltip rowNum = drawPivotRowSet(rowNum, (*it_innergroup).m_total, rowTypeList[i], legendText, 1, numColumns()); //set the legend text legend->setText(rowNum - 1, legendText); } } ++it_innergroup; } ++it_outergroup; } } break; case MyMoneyReport::eDetailGroup: { int rowNum = 0; // iterate over outer groups PivotGrid::const_iterator it_outergroup = grid.begin(); while (it_outergroup != grid.end()) { //iterate row types for (int i = 0; i < rowTypeList.size(); ++i) { //skip the budget difference rowset if (rowTypeList[i] != eBudgetDiff) { QString legendText; //only show the column type in the header if there is more than one type if (rowTypeList.size() > 1) { legendText = QString(columnTypeHeaderList[i] + " - " + it_outergroup.key()); } else { legendText = QString(it_outergroup.key()); } //set the cell value and tooltip rowNum = drawPivotRowSet(rowNum, (*it_outergroup).m_total, rowTypeList[i], legendText, 1, numColumns()); //set the legend legend->setText(rowNum - 1, legendText); } } ++it_outergroup; } //if selected, show totals too if (config.isShowingRowTotals()) { //iterate row types for (int i = 0; i < rowTypeList.size(); ++i) { //skip the budget difference rowset if (rowTypeList[i] != eBudgetDiff) { QString legendText; //only show the column type in the header if there is more than one type if (rowTypeList.size() > 1) { legendText = QString(columnTypeHeaderList[i] + " - " + i18nc("Total balance", "Total")); } else { legendText = QString(i18nc("Total balance", "Total")); } //set the cell value rowNum = drawPivotRowSet(rowNum, grid.m_total, rowTypeList[i], legendText, 1, numColumns()); //set the legend legend->setText(rowNum - 1, legendText); } } } } break; case MyMoneyReport::eDetailTotal: { int rowNum = 0; //iterate row types for (int i = 0; i < rowTypeList.size(); ++i) { //skip the budget difference rowset if (rowTypeList[i] != eBudgetDiff) { QString legendText; //only show the column type in the header if there is more than one type if (rowTypeList.size() > 1) { legendText = QString(columnTypeHeaderList[i] + " - " + i18nc("Total balance", "Total")); } else { legendText = QString(i18nc("Total balance", "Total")); } if (config.isMixedTime() && (rowTypeList[i] == eActual || rowTypeList[i] == eForecast)) { if (rowTypeList[i] == eActual) { rowNum = drawPivotRowSet(rowNum, grid.m_total, rowTypeList[i], legendText, 1, config.currentDateColumn()); } else if (rowTypeList[i] == eForecast) { rowNum = drawPivotRowSet(rowNum, grid.m_total, rowTypeList[i], legendText, config.currentDateColumn(), numColumns()); } else { rowNum = drawPivotRowSet(rowNum, grid.m_total, rowTypeList[i], legendText, 1, numColumns()); } } else { //set cell value rowNum = drawPivotRowSet(rowNum, grid.m_total, rowTypeList[i], legendText, 1, numColumns()); } //set legend text legend->setText(rowNum - 1, legendText); } } } break; } //assign model to the diagram planeDiagram->setModel(&m_model); //set the legend basic attributes //this is done after adding the legend because the values are overridden when adding the legend to the chart for (uint i = static_cast<uint>(KMyMoneyGlobalSettings::maximumLegendItems()); i < legend->datasetCount(); ++i) { legend->setDatasetHidden(i, true); } legend->setTitleText(i18nc("Chart lines legend", "Legend")); legend->setUseAutomaticMarkerSize(false); FrameAttributes legendFrameAttr(legend->frameAttributes()); legendFrameAttr.setPen(m_foregroundBrush.color()); // leave some space between the content and the frame legendFrameAttr.setPadding(2); legend->setFrameAttributes(legendFrameAttr); legend->setPosition(Position::East); legend->setTextAlignment(Qt::AlignLeft); legend->setLegendStyle(KDChart::Legend::MarkersAndLines); replaceLegend(legend); // set the text attributes after calling replaceLegend() otherwise fon sizes will get overwritten qreal generalFontSize = KGlobalSettings::generalFont().pointSizeF(); if (generalFontSize == -1) generalFontSize = 8; // this is a fallback if the fontsize was specified in pixels TextAttributes legendTextAttr(legend->textAttributes()); legendTextAttr.setPen(m_foregroundBrush.color()); legendTextAttr.setFontSize(KDChart::Measure(generalFontSize, KDChartEnums::MeasureCalculationModeAbsolute)); legend->setTextAttributes(legendTextAttr); TextAttributes legendTitleTextAttr(legend->titleTextAttributes()); legendTitleTextAttr.setPen(m_foregroundBrush.color()); legendTitleTextAttr.setFontSize(KDChart::Measure(generalFontSize + 4, KDChartEnums::MeasureCalculationModeAbsolute)); legend->setTitleTextAttributes(legendTitleTextAttr); //this sets the line width only for line diagrams setLineWidth(config.chartLineWidth()); //set data value attributes //make sure to show only the required number of fractional digits on the labels of the graph DataValueAttributes dataValueAttr(planeDiagram->dataValueAttributes()); MarkerAttributes markerAttr(dataValueAttr.markerAttributes()); markerAttr.setVisible(true); markerAttr.setMarkerStyle(MarkerAttributes::MarkerCircle); markerAttr.setMarkerSize(QSize(8, 8)); dataValueAttr.setMarkerAttributes(markerAttr); TextAttributes dataValueTextAttr(dataValueAttr.textAttributes()); dataValueTextAttr.setPen(m_foregroundBrush.color()); dataValueAttr.setTextAttributes(dataValueTextAttr); dataValueAttr.setVisible(config.isChartDataLabels()); dataValueAttr.setDecimalDigits(MyMoneyMoney::denomToPrec(MyMoneyFile::instance()->baseCurrency().smallestAccountFraction())); planeDiagram->setDataValueAttributes(dataValueAttr); planeDiagram->setAllowOverlappingDataValueTexts(true); if (qMin(static_cast<uint>(KMyMoneyGlobalSettings::maximumLegendItems()), legend->datasetCount()) < 2) { // the legend is needed only if at least two data sets are rendered removeLegend(); } }
MainWindow::MainWindow( QWidget* parent ) : QWidget( parent ) { setupUi( this ); m_chartLayout = new QHBoxLayout( chartFrame ); m_chart = new KDChart::Chart(); m_chartLayout->addWidget( m_chart ); // add a small left-side leading because we use a coloured Y axis background m_chart->setGlobalLeadingLeft( 1 ); initializeDataModel(); // register our own serializer for saving / loading the Y axis: KDChart::registerElementSerializer< AdjustedCartesianAxisSerializer, AdjustedCartesianAxis >( 0 ); // will be un-registered in ~Mainwindow() // Set up the diagrams KDChart::LineDiagram* lines = new KDChart::LineDiagram(); lines->setModel( m_model ); // increase the line width for ( int iRow = 0; iRow < lines->model()->rowCount(); ++iRow ) { QPen pen( lines->pen( iRow ) ); pen.setWidth(2); pen.setColor( pen.color().dark(133) ); lines->setPen( iRow, pen ); } // Assign some axes KDChart::CartesianAxis *xAxis = new KDChart::CartesianAxis( lines ); AdjustedCartesianAxis *yAxis = new AdjustedCartesianAxis( lines ); yAxis->setBounds(29.9, 31.0); xAxis->setPosition ( KDChart::CartesianAxis::Bottom ); yAxis->setPosition ( KDChart::CartesianAxis::Left ); yAxis->setTitleText ( "an axis class with customized labels" ); lines->addAxis( xAxis ); lines->addAxis( yAxis ); KDChart::CartesianCoordinatePlane* plane = static_cast< KDChart::CartesianCoordinatePlane * >(m_chart->coordinatePlane()); plane->replaceDiagram( lines ); KDChart::HeaderFooter* headerFooter = new KDChart::HeaderFooter( m_chart ); m_chart->addHeaderFooter( headerFooter ); headerFooter->setText( "Line diagram using a custom axis class" ); KDChart::TextAttributes textAttrs( headerFooter->textAttributes() ); textAttrs.setPen( QPen( Qt::red ) ); headerFooter->setTextAttributes( textAttrs ); headerFooter->setType( KDChart::HeaderFooter::Header ); headerFooter->setPosition( KDChart::Position::North ); // assign some bg colors KDChart::BackgroundAttributes ba = yAxis->backgroundAttributes(); ba.setVisible(true); ba.setBrush(QBrush(QColor(255,255,200))); yAxis->setBackgroundAttributes(ba); }