File indexing completed on 2024-05-12 04:20:15

0001 /**
0002  * SPDX-FileCopyrightText: 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved.
0003  *
0004  * This file is part of the KD Chart library.
0005  *
0006  * SPDX-License-Identifier: GPL-2.0-or-later
0007  */
0008 
0009 #include "mainwindow.h"
0010 #include "framewidget.h"
0011 
0012 #include <KChartChart>
0013 #include <KChartAbstractCoordinatePlane>
0014 #include <KChartLineDiagram>
0015 #include <KChartLineAttributes>
0016 #include <KChartTextAttributes>
0017 #include <KChartDataValueAttributes>
0018 #include <KChartThreeDLineAttributes>
0019 #include <KChartMarkerAttributes>
0020 #include <KChartFrameAttributes>
0021 #include <KChartBackgroundAttributes>
0022 #include <KChartLegend>
0023 
0024 #include <QDebug>
0025 #include <QPainter>
0026 #include <QTime>
0027 #include <QElapsedTimer>
0028 
0029 using namespace KChart;
0030 
0031 static QPixmap drawIntoPixmap( const QSize& size, KChart::Chart* chart )
0032 {
0033     QPixmap pix( size );
0034     pix.fill( Qt::white );
0035     QPainter painter( &pix );
0036     chart->paint( &painter, QRect( 0, 0, size.width(), size.height() ) );
0037     return pix;
0038 }
0039 
0040 // When set, this example uses FrameWidget which uses Chart::paint to paint itself.
0041 // When not set, this example uses a Chart widget directly.
0042 #define USE_FRAME_WIDGET 1
0043 
0044 
0045 MainWindow::MainWindow( QWidget* parent ) :
0046     QWidget( parent )
0047 {
0048 
0049     setupUi( this );
0050 
0051     connect( lineTypeCB, SIGNAL(currentIndexChanged(QString)), this, SLOT(setLineType(QString)) );
0052     connect( paintLegendCB, SIGNAL(toggled(bool)), this, SLOT(setLegendVisible(bool)) );
0053     connect( paintValuesCB, SIGNAL(toggled(bool)), this, SLOT(setValuesVisible(bool)) );
0054     connect( paintMarkersCB, SIGNAL(toggled(bool)), this, SLOT(setMarkersVisible(bool)) );
0055     connect( markersStyleCB, SIGNAL(currentIndexChanged(int)), this, SLOT(updateMarkers()) );
0056     connect( markersWidthSB, SIGNAL(valueChanged(int)), this, SLOT(updateMarkersHeight()) );
0057     connect( markersHeightSB, SIGNAL(valueChanged(int)), this, SLOT(updateMarkersWidth()) );
0058     connect( displayAreasCB, SIGNAL(toggled(bool)), this, SLOT(updateAreas(bool)) );
0059     connect( transparencySB, SIGNAL(valueChanged(int)), this, SLOT(updateAreasTransparency()) );
0060     connect( zoomFactorSB, SIGNAL(valueChanged(double)), this, SLOT(setZoomFactor(qreal)) );
0061     connect( hSBar, SIGNAL(valueChanged(int)), this, SLOT(setHPos(int)) );
0062     connect( vSBar, SIGNAL(valueChanged(int)), this, SLOT(setVPos(int)) );
0063     connect( savePB, SIGNAL(clicked()), this, SLOT(saveChart()) );
0064 
0065     QHBoxLayout* chartLayout = new QHBoxLayout( chartFrame );
0066 #ifdef USE_FRAME_WIDGET
0067     FrameWidget* chartFrameWidget = new FrameWidget();
0068     chartLayout->addWidget( chartFrameWidget );
0069 #endif
0070     hSBar->setVisible( false );
0071     vSBar->setVisible( false );
0072 
0073     m_model.loadFromCSV( ":/empty" );
0074 
0075     // Set up the diagram
0076     m_lines = new LineDiagram();
0077 
0078     m_lines->setModel( &m_model );
0079 
0080     CartesianAxis *xAxis = new CartesianAxis( m_lines );
0081     CartesianAxis *yAxis = new CartesianAxis ( m_lines );
0082     CartesianAxis *axisTop = new CartesianAxis ( m_lines );
0083     CartesianAxis *axisRight = new CartesianAxis ( m_lines );
0084     xAxis->setPosition ( KChart::CartesianAxis::Bottom );
0085     yAxis->setPosition ( KChart::CartesianAxis::Left );
0086     axisTop->setPosition( KChart::CartesianAxis::Top );
0087     axisRight->setPosition( KChart::CartesianAxis::Right );
0088 
0089     m_lines->addAxis( xAxis );
0090     m_lines->addAxis( yAxis );
0091     m_lines->addAxis( axisTop );
0092     m_lines->addAxis( axisRight );
0093 
0094     m_chart = new Chart();
0095     //m_chart->setGlobalLeading(10,10,10,10); // by default there is no leading
0096 
0097 #ifdef USE_FRAME_WIDGET
0098     chartFrameWidget->setChart( m_chart );
0099     // make sure, we re-draw after changing one of the chart's properties
0100     connect( m_chart,          SIGNAL(propertiesChanged()),
0101              chartFrameWidget, SLOT(update()) ) ;
0102 #else
0103     chartLayout->addWidget( m_chart );
0104 #endif
0105 
0106     m_chart->coordinatePlane()->replaceDiagram( m_lines );
0107 
0108     for ( int iColumn = 0; iColumn<m_lines->model()->columnCount(); ++iColumn ) {
0109         QPen pen(m_lines->pen( iColumn ));
0110         pen.setWidth(4);
0111         m_lines->setPen( iColumn, pen );
0112     }
0113 
0114     FrameAttributes faChart( m_chart->frameAttributes() );
0115     faChart.setVisible( true );
0116     faChart.setPen( QPen(QColor(0x60,0x60,0xb0), 8) );
0117     m_chart->setFrameAttributes( faChart );
0118 
0119     BackgroundAttributes baChart( m_chart->backgroundAttributes() );
0120     baChart.setVisible( true );
0121     baChart.setBrush( QColor(0xd0,0xd0,0xff) );
0122     m_chart->setBackgroundAttributes( baChart );
0123 
0124     // Set up the legend
0125     m_legend = new Legend( m_lines, m_chart );
0126 
0127     m_legend->setPosition( Position::South );
0128     m_legend->setAlignment( Qt::AlignRight );
0129     m_legend->setShowLines( false );
0130     m_legend->setTitleText( QStringLiteral( "Legend" ) );
0131     m_legend->setOrientation( Qt::Horizontal );
0132 
0133     // setting the legend frame and background to the same color:
0134     const QColor legendColor(0xff,0xe0,0x80);
0135     FrameAttributes faLegend( m_legend->frameAttributes() );
0136     faLegend.setVisible( true );
0137     faLegend.setPen( QPen(legendColor, 1) );
0138     m_legend->setFrameAttributes( faLegend );
0139 
0140     BackgroundAttributes baLegend( m_legend->backgroundAttributes() );
0141     baLegend.setVisible( true );
0142     baLegend.setBrush( legendColor );
0143     m_legend->setBackgroundAttributes( baLegend );
0144 
0145     m_chart->addLegend( m_legend );
0146 
0147     // for illustration we paint the same chart at different sizes:
0148     QSize size1 = QSize( 200, 200 );
0149     QSize size2 = QSize( 800, 800 );
0150     m_pix1 = drawIntoPixmap( size1, m_chart );
0151     m_pix2 = drawIntoPixmap( size2, m_chart );
0152     m_pix2 = m_pix2.scaled( size1 );
0153 
0154     m_smallChart1 = new QLabel( this );
0155     m_smallChart1->setWindowTitle( "200x200" );
0156     m_smallChart1->setPixmap( m_pix1 );
0157     m_smallChart1->setFixedSize( m_pix1.size() );
0158     m_smallChart1->move( width() - m_pix1.width()*2,
0159                          height()/2 - m_pix1.height()-5 );
0160     m_smallChart1->show();
0161 
0162     m_smallChart2 = new QLabel( this );
0163     m_smallChart2->setWindowTitle( "800x800 scaled down" );
0164     m_smallChart2->setPixmap( m_pix2 );
0165     m_smallChart2->setFixedSize( m_pix2.size() );
0166     m_smallChart2->move( width() - m_pix2.width()*2,
0167                          height()/2 + 5 );
0168     m_smallChart2->show();
0169 
0170     faChart.setPen( QPen(QColor(0xb0,0xb0,0xff), 8) );
0171     m_chart->setFrameAttributes( faChart );
0172 
0173     // initialize attributes; this is necessary because we need to enable data value attributes before
0174     // any of them (e.g. only markers) can be displayed. but if we enable data value attributes, a default
0175     // data value text is included, even if we only wanted to set markers. so we enable DVA and then
0176     // individually disable the parts we don't want.
0177     setValuesVisible( false );
0178     setMarkersVisible( false );
0179 }
0180 
0181 void MainWindow::updateData(QString data)
0182 {
0183     QElapsedTimer t;
0184     t.start();
0185 
0186     m_model.loadFromCSV( data );
0187 
0188     qDebug("Time for loading data %s: %lld ms", data.toLatin1().constData(), t.elapsed());
0189     t.restart();
0190 
0191     QSize size1 = QSize( 200, 200 );
0192     QSize size2 = QSize( 800, 800 );
0193     m_pix1 = drawIntoPixmap( size1, m_chart );
0194     m_pix2 = drawIntoPixmap( size2, m_chart );
0195 
0196     qDebug("Time for drawing pixmap %s: %lld ms", data.toLatin1().constData(), t.elapsed());
0197     t.restart();
0198 
0199     m_lines->setModel( &m_model );
0200 
0201     qDebug("Time for setting model %s: %lld ms", data.toLatin1().constData(), t.elapsed());
0202     t.restart();
0203 
0204     m_smallChart1->setPixmap( m_pix1 );
0205     m_smallChart2->setPixmap( m_pix2 );
0206 
0207     m_smallChart1->show();
0208     m_smallChart2->show();
0209 
0210     qDebug("Time for setting pixmap %s: %lld ms", data.toLatin1().constData(), t.elapsed());
0211     t.restart();
0212 
0213 }
0214 
0215 void MainWindow::setLineType( const QString & text )
0216 {
0217     if ( text == "Normal" )
0218         m_lines->setType( LineDiagram::Normal );
0219     else if ( text == "Stacked" )
0220         m_lines->setType( LineDiagram::Stacked );
0221     else if ( text == "Percent" )
0222         m_lines->setType( LineDiagram::Percent );
0223     else
0224         qWarning (" Does not match any type");
0225 }
0226 
0227 void MainWindow::setLegendVisible(bool visible )
0228 {
0229     KChart::Legend* legend = m_chart->legend();
0230     if ( visible != ( legend != nullptr ) ) {
0231         if ( visible )
0232             m_chart->addLegend( m_legend );
0233         else
0234             m_chart->takeLegend( legend );
0235     }
0236 }
0237 
0238 void MainWindow::setValuesVisible(bool visible )
0239 {
0240     const int colCount = m_lines->model()->columnCount();
0241     for ( int iColumn = 0; iColumn<colCount; ++iColumn ) {
0242         DataValueAttributes a = m_lines->dataValueAttributes( iColumn );
0243         a.setVisible( true );
0244 
0245         TextAttributes ta = a.textAttributes();
0246         ta.setRotation( 0 );
0247         ta.setFont( QFont( "Comic", 10 ) );
0248         ta.setPen( m_lines->brush( iColumn ).color() );
0249         ta.setVisible( visible );
0250 
0251         a.setTextAttributes( ta );
0252         m_lines->setDataValueAttributes( iColumn, a);
0253     }
0254 }
0255 
0256 
0257 void MainWindow::setMarkersVisible( bool visible )
0258 {
0259     paintMarkers( visible, QSize() );
0260 }
0261 
0262 void MainWindow::updateMarkers()
0263 {
0264     setMarkersVisible( paintMarkersCB->isChecked() );
0265 }
0266 
0267 void MainWindow::updateMarkersHeight()
0268 {
0269     markersHeightSB->setValue( markersWidthSB->value() );
0270     updateMarkers();
0271 }
0272 
0273 void MainWindow::updateMarkersWidth()
0274 {
0275     markersWidthSB->setValue( markersHeightSB->value() );
0276     updateMarkers();
0277 }
0278 
0279 void MainWindow::updateAreas( bool visible )
0280 {
0281     const int colCount = m_lines->model()->columnCount();
0282     for ( int iColumn = 0; iColumn<colCount; ++iColumn ) {
0283         LineAttributes la( m_lines->lineAttributes( iColumn ) );
0284         la.setDisplayArea( visible );
0285         if ( visible  )
0286             la.setTransparency( transparencySB->value() );
0287         m_lines->setLineAttributes( iColumn,  la );
0288     }
0289 }
0290 
0291 void MainWindow::updateAreasTransparency()
0292 {
0293     if ( !displayAreasCB->isChecked() )
0294         displayAreasCB->setChecked( true );
0295     else
0296         updateAreas( true );
0297 }
0298 
0299 void MainWindow::setZoomFactor( qreal factor )
0300 {
0301     const bool isZoomedIn = factor > 1.0f;
0302     hSBar->setVisible( isZoomedIn );
0303     vSBar->setVisible( isZoomedIn );
0304     if ( !isZoomedIn ) {
0305         hSBar->setValue( 500 );
0306         vSBar->setValue( 500 );
0307     }
0308     m_chart->coordinatePlane()->setZoomFactorX( factor );
0309     m_chart->coordinatePlane()->setZoomFactorY( factor );
0310 }
0311 
0312 void MainWindow::setHPos( int hPos )
0313 {
0314     m_chart->coordinatePlane()->setZoomCenter( QPointF(hPos/1000.0, vSBar->value()/1000.0) );
0315     m_chart->update();
0316 }
0317 
0318 void MainWindow::setVPos( int vPos )
0319 {
0320     m_chart->coordinatePlane()->setZoomCenter( QPointF( hSBar->value()/1000.0, vPos/1000.0) );
0321 }
0322 
0323 // since DataValue markers have no relative sizing mode we need to scale them for printing
0324 void MainWindow::paintMarkers( bool checked, const QSize& printSize )
0325 {
0326     MarkerAttributes::MarkerStylesMap map;
0327     map.insert( 0, MarkerAttributes::MarkerSquare );
0328     map.insert( 1, MarkerAttributes::MarkerCircle );
0329     map.insert( 2, MarkerAttributes::MarkerRing );
0330     map.insert( 3, MarkerAttributes::MarkerCross );
0331     map.insert( 4, MarkerAttributes::MarkerDiamond );
0332 
0333     // next: Specify column- / cell-specific attributes!
0334     const int colCount = m_lines->model()->columnCount();
0335     for ( int iColumn = 0; iColumn<colCount; ++iColumn ) {
0336         DataValueAttributes dva = m_lines->dataValueAttributes( iColumn );
0337         dva.setVisible( true );
0338         MarkerAttributes ma( dva.markerAttributes() );
0339 
0340     switch ( markersStyleCB->currentIndex() ) {
0341         case 0:
0342                    ma.setMarkerStyle( MarkerAttributes::MarkerSquare );
0343                    break;
0344         case 1:
0345                    // Column-specific attributes
0346                    ma.setMarkerStyle( map.value( iColumn ) );
0347                    break;
0348         case 2:
0349                    ma.setMarkerStyle( MarkerAttributes::MarkerCircle );
0350                    break;
0351         case 3:
0352                    ma.setMarkerStyle( MarkerAttributes::MarkerDiamond );
0353                    break;
0354         case 4:
0355                    ma.setMarkerStyle( MarkerAttributes::Marker1Pixel );
0356                    break;
0357         case 5:
0358                    ma.setMarkerStyle( MarkerAttributes::Marker4Pixels );
0359                    break;
0360         case 6:
0361                    ma.setMarkerStyle( MarkerAttributes::MarkerRing );
0362                    break;
0363         case 7:
0364                    ma.setMarkerStyle( MarkerAttributes::MarkerCross );
0365                    break;
0366         case 8:
0367                    ma.setMarkerStyle( MarkerAttributes::MarkerFastCross );
0368                    break;
0369         default:
0370             Q_ASSERT( false );
0371     }
0372     ma.setVisible( checked );
0373 
0374         qreal factorWidth = printSize.isValid() ? ( printSize.width() / m_chart->rect().width() ) : 1.0f;
0375         qreal factorHeight = printSize.isValid() ? ( printSize.height() / m_chart->rect().height() ) : 1.0f;
0376         ma.setMarkerSize( QSize( markersWidthSB->value() * factorWidth, markersHeightSB->value() * factorHeight ) );
0377 
0378         dva.setMarkerAttributes( ma );
0379         m_lines->setDataValueAttributes( iColumn, dva );
0380 
0381     // make a special one for certain values
0382     DataValueAttributes yellowAttributes( dva );
0383     MarkerAttributes yellowMarker( yellowAttributes.markerAttributes() );
0384     yellowMarker.setMarkerColor( Qt::yellow );
0385     yellowAttributes.setMarkerAttributes( yellowMarker );
0386 
0387     const int rowCount = m_lines->model()->rowCount();
0388         for ( int j=0; j< rowCount; ++j ) {
0389             QModelIndex index = m_lines->model()->index( j, iColumn, QModelIndex() );
0390             QBrush brush = m_lines->model()->headerData( iColumn, Qt::Vertical, DatasetBrushRole ).value<QBrush>();
0391             qreal value = m_lines->model()->data( index ).toReal();
0392             /* Set a specific color - marker for a specific value */
0393             if ( value == 13 ) {
0394                 m_lines->setDataValueAttributes( index, yellowAttributes );
0395             }
0396         }
0397     }
0398 }
0399 
0400 void MainWindow::saveChart()
0401 {
0402     qDebug() << "Painting into PNG";
0403     QPixmap qpix(600,600);
0404     QPainter painter(&qpix);
0405     painter.setBrush(Qt::white);
0406     painter.fillRect( 0, 0, 600, 600, Qt::white);
0407     m_chart->paint( &painter,
0408                     QRect(100, 100, 400, 400) );
0409     painter.end();
0410     qpix.save("kchart-demo.png", "PNG");
0411     qDebug() << "Painting into PNG - done";
0412 }
0413 
0414 void MainWindow::resizeEvent ( QResizeEvent * )
0415 {
0416     m_smallChart1->move( width() - m_pix1.width()*2,
0417                          height()/2 - m_pix1.height()-5 );
0418     m_smallChart2->move( width() - m_pix2.width()*2,
0419                          height()/2 + 5 );
0420 }
0421 
0422