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 }