Exemple #1
0
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("&nbsp;", " ");
      }
      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();
  }
}
Exemple #2
0
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);
}