KD Chart 2 [rev.2.4]
|
00001 /**************************************************************************** 00002 ** Copyright (C) 2001-2012 Klaralvdalens Datakonsult AB. All rights reserved. 00003 ** 00004 ** This file is part of the KD Chart library. 00005 ** 00006 ** Licensees holding valid commercial KD Chart licenses may use this file in 00007 ** accordance with the KD Chart Commercial License Agreement provided with 00008 ** the Software. 00009 ** 00010 ** 00011 ** This file may be distributed and/or modified under the terms of the 00012 ** GNU General Public License version 2 and version 3 as published by the 00013 ** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. 00014 ** 00015 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE 00016 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 00017 ** 00018 ** Contact info@kdab.com if any conditions of this licensing are not 00019 ** clear to you. 00020 ** 00021 **********************************************************************/ 00022 00023 #include <KDChartWidget.h> 00024 #include <KDChartWidget_p.h> 00025 00026 #include <KDChartAbstractDiagram.h> 00027 #include <KDChartBarDiagram.h> 00028 #include <KDChartChart.h> 00029 #include <KDChartAbstractCoordinatePlane.h> 00030 #include <KDChartLineDiagram.h> 00031 #include <KDChartPlotter.h> 00032 #include <KDChartPieDiagram.h> 00033 #include <KDChartPolarDiagram.h> 00034 #include <KDChartRingDiagram.h> 00035 #include <KDChartLegend.h> 00036 00037 #include <QDebug> 00038 00039 #include <KDABLibFakes> 00040 00041 #define d d_func() 00042 00043 using namespace KDChart; 00044 00045 Widget::Private::Private( Widget * qq ) 00046 : q( qq ), 00047 layout( q ), 00048 m_model( q ), 00049 m_chart( q ), 00050 m_cartPlane( &m_chart ), 00051 m_polPlane( &m_chart ), 00052 usedDatasetWidth( 0 ) 00053 { 00054 KDAB_SET_OBJECT_NAME( layout ); 00055 KDAB_SET_OBJECT_NAME( m_model ); 00056 KDAB_SET_OBJECT_NAME( m_chart ); 00057 00058 layout.addWidget( &m_chart ); 00059 } 00060 00061 Widget::Private::~Private() {} 00062 00063 00076 Widget::Widget( QWidget* parent ) : 00077 QWidget(parent), _d( new Private( this ) ) 00078 { 00079 // as default we have a cartesian coordinate plane ... 00080 // ... and a line diagram 00081 setType( Line ); 00082 } 00083 00087 Widget::~Widget() 00088 { 00089 delete _d; _d = 0; 00090 } 00091 00092 void Widget::init() 00093 { 00094 } 00095 00096 void Widget::setDataset( int column, const QVector< double > & data, const QString& title ) 00097 { 00098 if ( ! checkDatasetWidth( 1 ) ) 00099 return; 00100 00101 QStandardItemModel & model = d->m_model; 00102 00103 justifyModelSize( data.size(), column + 1 ); 00104 00105 for( int i = 0; i < data.size(); ++i ) 00106 { 00107 const QModelIndex index = model.index( i, column ); 00108 model.setData( index, QVariant( data[i] ), Qt::DisplayRole ); 00109 } 00110 if ( ! title.isEmpty() ) 00111 model.setHeaderData( column, Qt::Horizontal, QVariant( title ) ); 00112 } 00113 00114 void Widget::setDataset( int column, const QVector< QPair< double, double > > & data, const QString& title ) 00115 { 00116 if ( ! checkDatasetWidth( 2 )) 00117 return; 00118 00119 QStandardItemModel & model = d->m_model; 00120 00121 justifyModelSize( data.size(), (column + 1) * 2 ); 00122 00123 for( int i = 0; i < data.size(); ++i ) 00124 { 00125 QModelIndex index = model.index( i, column * 2 ); 00126 model.setData( index, QVariant( data[i].first ), Qt::DisplayRole ); 00127 00128 index = model.index( i, column * 2 + 1 ); 00129 model.setData( index, QVariant( data[i].second ), Qt::DisplayRole ); 00130 } 00131 if ( ! title.isEmpty() ){ 00132 model.setHeaderData( column, Qt::Horizontal, QVariant( title ) ); 00133 } 00134 } 00135 00136 void Widget::setDataCell( int row, int column, double data ) 00137 { 00138 if ( ! checkDatasetWidth( 1 ) ) 00139 return; 00140 00141 QStandardItemModel & model = d->m_model; 00142 00143 justifyModelSize( row + 1, column + 1 ); 00144 00145 const QModelIndex index = model.index( row, column ); 00146 model.setData( index, QVariant( data ), Qt::DisplayRole ); 00147 } 00148 00149 void Widget::setDataCell( int row, int column, QPair< double, double > data ) 00150 { 00151 if ( ! checkDatasetWidth( 2 )) 00152 return; 00153 00154 QStandardItemModel & model = d->m_model; 00155 00156 justifyModelSize( row + 1, (column + 1) * 2 ); 00157 00158 QModelIndex index = model.index( row, column * 2 ); 00159 model.setData( index, QVariant( data.first ), Qt::DisplayRole ); 00160 00161 index = model.index( row, column * 2 + 1 ); 00162 model.setData( index, QVariant( data.second ), Qt::DisplayRole ); 00163 } 00164 00165 /* 00166 * Resets all data. 00167 */ 00168 void Widget::resetData() 00169 { 00170 d->m_model.clear(); 00171 d->usedDatasetWidth = 0; 00172 } 00173 00177 void Widget::setGlobalLeading( int left, int top, int right, int bottom ) 00178 { 00179 d->m_chart.setGlobalLeading( left, top, right, bottom ); 00180 } 00181 00185 void Widget::setGlobalLeadingLeft( int leading ) 00186 { 00187 d->m_chart.setGlobalLeadingLeft( leading ); 00188 } 00189 00193 int Widget::globalLeadingLeft() const 00194 { 00195 return d->m_chart.globalLeadingLeft(); 00196 } 00197 00201 void Widget::setGlobalLeadingTop( int leading ) 00202 { 00203 d->m_chart.setGlobalLeadingTop( leading ); 00204 } 00205 00209 int Widget::globalLeadingTop() const 00210 { 00211 return d->m_chart.globalLeadingTop(); 00212 } 00213 00217 void Widget::setGlobalLeadingRight( int leading ) 00218 { 00219 d->m_chart.setGlobalLeadingRight( leading ); 00220 } 00221 00225 int Widget::globalLeadingRight() const 00226 { 00227 return d->m_chart.globalLeadingRight(); 00228 } 00229 00233 void Widget::setGlobalLeadingBottom( int leading ) 00234 { 00235 d->m_chart.setGlobalLeadingBottom( leading ); 00236 } 00237 00241 int Widget::globalLeadingBottom() const 00242 { 00243 return d->m_chart.globalLeadingBottom(); 00244 } 00245 00249 KDChart::HeaderFooter* Widget::firstHeaderFooter() 00250 { 00251 return d->m_chart.headerFooter(); 00252 } 00253 00257 QList<KDChart::HeaderFooter*> Widget::allHeadersFooters() 00258 { 00259 return d->m_chart.headerFooters(); 00260 } 00261 00265 void Widget::addHeaderFooter( const QString& text, 00266 HeaderFooter::HeaderFooterType type, 00267 Position position) 00268 { 00269 HeaderFooter* newHeader = new HeaderFooter( &d->m_chart ); 00270 newHeader->setType( type ); 00271 newHeader->setPosition( position ); 00272 newHeader->setText( text ); 00273 d->m_chart.addHeaderFooter( newHeader ); // we need this explicit call ! 00274 } 00275 00279 void Widget::addHeaderFooter( HeaderFooter* header ) 00280 { 00281 header->setParent( &d->m_chart ); 00282 d->m_chart.addHeaderFooter( header ); // we need this explicit call ! 00283 } 00284 00285 void Widget::replaceHeaderFooter( HeaderFooter* header, HeaderFooter* oldHeader ) 00286 { 00287 header->setParent( &d->m_chart ); 00288 d->m_chart.replaceHeaderFooter( header, oldHeader ); 00289 } 00290 00291 void Widget::takeHeaderFooter( HeaderFooter* header ) 00292 { 00293 d->m_chart.takeHeaderFooter( header ); 00294 } 00295 00299 KDChart::Legend* Widget::legend() 00300 { 00301 return d->m_chart.legend(); 00302 } 00303 00307 QList<KDChart::Legend*> Widget::allLegends() 00308 { 00309 return d->m_chart.legends(); 00310 } 00311 00315 void Widget::addLegend( Position position ) 00316 { 00317 Legend* legend = new Legend( diagram(), &d->m_chart ); 00318 legend->setPosition( position ); 00319 d->m_chart.addLegend( legend ); 00320 } 00321 00325 void Widget::addLegend( Legend* legend ) 00326 { 00327 legend->setDiagram( diagram() ); 00328 legend->setParent( &d->m_chart ); 00329 d->m_chart.addLegend( legend ); 00330 } 00331 00332 void Widget::replaceLegend( Legend* legend, Legend* oldLegend ) 00333 { 00334 legend->setDiagram( diagram() ); 00335 legend->setParent( &d->m_chart ); 00336 d->m_chart.replaceLegend( legend, oldLegend ); 00337 } 00338 00339 void Widget::takeLegend( Legend* legend ) 00340 { 00341 d->m_chart.takeLegend( legend ); 00342 } 00343 00344 AbstractDiagram* Widget::diagram() 00345 { 00346 if ( coordinatePlane() == 0 ) 00347 qDebug() << "diagram(): coordinatePlane() was NULL"; 00348 00349 return coordinatePlane()->diagram(); 00350 } 00351 00352 BarDiagram* Widget::barDiagram() 00353 { 00354 return dynamic_cast<BarDiagram*>( diagram() ); 00355 } 00356 LineDiagram* Widget::lineDiagram() 00357 { 00358 return dynamic_cast<LineDiagram*>( diagram() ); 00359 } 00360 Plotter* Widget::plotter() 00361 { 00362 return dynamic_cast<Plotter*>( diagram() ); 00363 } 00364 PieDiagram* Widget::pieDiagram() 00365 { 00366 return dynamic_cast<PieDiagram*>( diagram() ); 00367 } 00368 RingDiagram* Widget::ringDiagram() 00369 { 00370 return dynamic_cast<RingDiagram*>( diagram() ); 00371 } 00372 PolarDiagram* Widget::polarDiagram() 00373 { 00374 return dynamic_cast<PolarDiagram*>( diagram() ); 00375 } 00376 00377 AbstractCoordinatePlane* Widget::coordinatePlane() 00378 { 00379 return d->m_chart.coordinatePlane(); 00380 } 00381 00382 static bool isCartesian( KDChart::Widget::ChartType type ) 00383 { 00384 return (type == KDChart::Widget::Bar) || (type == KDChart::Widget::Line); 00385 } 00386 00387 static bool isPolar( KDChart::Widget::ChartType type ) 00388 { 00389 return (type == KDChart::Widget::Pie) 00390 || (type == KDChart::Widget::Ring) 00391 || (type == KDChart::Widget::Polar); 00392 } 00393 00394 void Widget::setType( ChartType chartType, SubType chartSubType ) 00395 { 00396 AbstractDiagram* diag = 0; 00397 const ChartType oldType = type(); 00398 00399 if ( chartType != oldType ){ 00400 if( chartType != NoType ){ 00401 if ( isCartesian( chartType ) && ! isCartesian( oldType ) ) 00402 { 00403 if( coordinatePlane() == &d->m_polPlane ){ 00404 d->m_chart.takeCoordinatePlane( &d->m_polPlane ); 00405 d->m_chart.addCoordinatePlane( &d->m_cartPlane ); 00406 }else{ 00407 d->m_chart.replaceCoordinatePlane( &d->m_cartPlane ); 00408 } 00409 } 00410 else if ( isPolar( chartType ) && ! isPolar( oldType ) ) 00411 { 00412 if( coordinatePlane() == &d->m_cartPlane ){ 00413 d->m_chart.takeCoordinatePlane( &d->m_cartPlane ); 00414 d->m_chart.addCoordinatePlane( &d->m_polPlane ); 00415 }else{ 00416 d->m_chart.replaceCoordinatePlane( &d->m_polPlane ); 00417 } 00418 } 00419 } 00420 switch ( chartType ){ 00421 case Bar: 00422 diag = new BarDiagram( &d->m_chart, &d->m_cartPlane ); 00423 break; 00424 case Line: 00425 diag = new LineDiagram( &d->m_chart, &d->m_cartPlane ); 00426 break; 00427 case Plot: 00428 diag = new Plotter( &d->m_chart, &d->m_cartPlane ); 00429 break; 00430 case Pie: 00431 diag = new PieDiagram( &d->m_chart, &d->m_polPlane ); 00432 break; 00433 case Polar: 00434 diag = new PolarDiagram( &d->m_chart, &d->m_polPlane ); 00435 break; 00436 case Ring: 00437 diag = new RingDiagram( &d->m_chart, &d->m_polPlane ); 00438 break; 00439 case NoType: 00440 break; 00441 } 00442 if ( diag != NULL ){ 00443 if ( isCartesian( oldType ) && isCartesian( chartType ) ){ 00444 AbstractCartesianDiagram *oldDiag = 00445 qobject_cast<AbstractCartesianDiagram*>( coordinatePlane()->diagram() ); 00446 AbstractCartesianDiagram *newDiag = 00447 qobject_cast<AbstractCartesianDiagram*>( diag ); 00448 Q_FOREACH( CartesianAxis* axis, oldDiag->axes() ) { 00449 oldDiag->takeAxis( axis ); 00450 newDiag->addAxis ( axis ); 00451 } 00452 } 00453 diag->setModel( &d->m_model ); 00454 coordinatePlane()->replaceDiagram( diag ); 00455 00456 LegendList legends = d->m_chart.legends(); 00457 Q_FOREACH(Legend* l, legends) 00458 l->setDiagram( diag ); 00459 //checkDatasetWidth( d->usedDatasetWidth ); 00460 } 00461 //coordinatePlane()->setGridNeedsRecalculate(); 00462 } 00463 00464 if ( chartType != NoType ){ 00465 if ( chartType != oldType || chartSubType != subType() ) 00466 setSubType( chartSubType ); 00467 d->m_chart.resize( size() ); // triggering immediate update 00468 } 00469 } 00470 00471 void Widget::setSubType( SubType subType ) 00472 { 00473 BarDiagram* barDia = qobject_cast< BarDiagram* >( diagram() ); 00474 LineDiagram* lineDia = qobject_cast< LineDiagram* >( diagram() ); 00475 Plotter* plotterDia = qobject_cast< Plotter* >( diagram() ); 00476 00477 //FIXME(khz): Add the impl for these chart types - or remove them from here: 00478 // PieDiagram* pieDia = qobject_cast< PieDiagram* >( diagram() ); 00479 // PolarDiagram* polarDia = qobject_cast< PolarDiagram* >( diagram() ); 00480 // RingDiagram* ringDia = qobject_cast< RingDiagram* >( diagram() ); 00481 00482 #define SET_SUB_TYPE(DIAGRAM, SUBTYPE) \ 00483 { \ 00484 if( DIAGRAM ) \ 00485 DIAGRAM->setType( SUBTYPE ); \ 00486 } 00487 switch ( subType ) 00488 { 00489 case Normal: 00490 SET_SUB_TYPE( barDia, BarDiagram::Normal ); 00491 SET_SUB_TYPE( lineDia, LineDiagram::Normal ); 00492 SET_SUB_TYPE( plotterDia, Plotter::Normal ); 00493 break; 00494 case Stacked: 00495 SET_SUB_TYPE( barDia, BarDiagram::Stacked ); 00496 SET_SUB_TYPE( lineDia, LineDiagram::Stacked ); 00497 //SET_SUB_TYPE( plotterDia, Plotter::Stacked ); 00498 break; 00499 case Percent: 00500 SET_SUB_TYPE( barDia, BarDiagram::Percent ); 00501 SET_SUB_TYPE( lineDia, LineDiagram::Percent ); 00502 SET_SUB_TYPE( plotterDia, Plotter::Percent ); 00503 break; 00504 case Rows: 00505 SET_SUB_TYPE( barDia, BarDiagram::Rows ); 00506 break; 00507 default: 00508 Q_ASSERT_X ( false, 00509 "Widget::setSubType", "Sub-type not supported!" ); 00510 break; 00511 } 00512 // coordinatePlane()->show(); 00513 } 00514 00518 Widget::ChartType Widget::type() const 00519 { 00520 // PENDING(christoph) save the type out-of-band: 00521 AbstractDiagram * const dia = const_cast<Widget*>( this )->diagram(); 00522 if ( qobject_cast< BarDiagram* >( dia ) ) 00523 return Bar; 00524 else if ( qobject_cast< LineDiagram* >( dia ) ) 00525 return Line; 00526 else if ( qobject_cast< Plotter* >( dia ) ) 00527 return Plot; 00528 else if( qobject_cast< PieDiagram* >( dia ) ) 00529 return Pie; 00530 else if( qobject_cast< PolarDiagram* >( dia ) ) 00531 return Polar; 00532 else if( qobject_cast< RingDiagram* >( dia ) ) 00533 return Ring; 00534 else 00535 return NoType; 00536 } 00537 00538 Widget::SubType Widget::subType() const 00539 { 00540 // PENDING(christoph) save the type out-of-band: 00541 Widget::SubType retVal = Normal; 00542 00543 AbstractDiagram * const dia = const_cast<Widget*>( this )->diagram(); 00544 BarDiagram* barDia = qobject_cast< BarDiagram* >( dia ); 00545 LineDiagram* lineDia = qobject_cast< LineDiagram* >( dia ); 00546 Plotter* plotterDia = qobject_cast< Plotter* >( dia ); 00547 00548 //FIXME(khz): Add the impl for these chart types - or remove them from here: 00549 // PieDiagram* pieDia = qobject_cast< PieDiagram* >( diagram() ); 00550 // PolarDiagram* polarDia = qobject_cast< PolarDiagram* >( diagram() ); 00551 // RingDiagram* ringDia = qobject_cast< RingDiagram* >( diagram() ); 00552 00553 #define TEST_SUB_TYPE(DIAGRAM, INTERNALSUBTYPE, SUBTYPE) \ 00554 { \ 00555 if( DIAGRAM && DIAGRAM->type() == INTERNALSUBTYPE ) \ 00556 retVal = SUBTYPE; \ 00557 } 00558 const Widget::ChartType mainType = type(); 00559 switch ( mainType ) 00560 { 00561 case Bar: 00562 TEST_SUB_TYPE( barDia, BarDiagram::Normal, Normal ); 00563 TEST_SUB_TYPE( barDia, BarDiagram::Stacked, Stacked ); 00564 TEST_SUB_TYPE( barDia, BarDiagram::Percent, Percent ); 00565 TEST_SUB_TYPE( barDia, BarDiagram::Rows, Rows ); 00566 break; 00567 case Line: 00568 TEST_SUB_TYPE( lineDia, LineDiagram::Normal, Normal ); 00569 TEST_SUB_TYPE( lineDia, LineDiagram::Stacked, Stacked ); 00570 TEST_SUB_TYPE( lineDia, LineDiagram::Percent, Percent ); 00571 break; 00572 case Plot: 00573 TEST_SUB_TYPE( plotterDia, Plotter::Normal, Normal ); 00574 TEST_SUB_TYPE( plotterDia, Plotter::Percent, Percent ); 00575 break; 00576 case Pie: 00577 // no impl. yet 00578 break; 00579 case Polar: 00580 // no impl. yet 00581 break; 00582 case Ring: 00583 // no impl. yet 00584 break; 00585 default: 00586 Q_ASSERT_X ( false, 00587 "Widget::subType", "Chart type not supported!" ); 00588 break; 00589 } 00590 return retVal; 00591 } 00592 00593 00597 bool Widget::checkDatasetWidth( int width ) 00598 { 00599 if( width == diagram()->datasetDimension() ) 00600 { 00601 d->usedDatasetWidth = width; 00602 return true; 00603 } 00604 qDebug() << "The current diagram type doesn't support this data dimension."; 00605 return false; 00606 /* if ( d->usedDatasetWidth == width || d->usedDatasetWidth == 0 ) { 00607 d->usedDatasetWidth = width; 00608 diagram()->setDatasetDimension( width ); 00609 return true; 00610 } 00611 qDebug() << "It's impossible to mix up the different setDataset() methods on the same widget."; 00612 return false;*/ 00613 } 00614 00618 void Widget::justifyModelSize( int rows, int columns ) 00619 { 00620 QAbstractItemModel & model = d->m_model; 00621 const int currentRows = model.rowCount(); 00622 const int currentCols = model.columnCount(); 00623 00624 if ( currentCols < columns ) 00625 if ( ! model.insertColumns( currentCols, columns - currentCols )) 00626 qDebug() << "justifyModelSize: could not increase model size."; 00627 if ( currentRows < rows ) 00628 if ( ! model.insertRows( currentRows, rows - currentRows )) 00629 qDebug() << "justifyModelSize: could not increase model size."; 00630 00631 Q_ASSERT( model.rowCount() >= rows ); 00632 Q_ASSERT( model.columnCount() >= columns ); 00633 }