KDChartWidget.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002 ** Copyright (C) 2001-2011 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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Defines

Klarälvdalens Datakonsult AB (KDAB)
Qt-related services and products
http://www.kdab.com/
http://www.kdab.com/products/kd-chart/