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

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