KD Chart 2  [rev.2.5]
KDChartRadarDiagram.cpp
Go to the documentation of this file.
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 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 "KDChartRadarDiagram.h"
00024 #include "KDChartRadarDiagram_p.h"
00025 
00026 #include <QPainter>
00027 #include "KDChartAttributesModel.h"
00028 #include "KDChartPaintContext.h"
00029 #include "KDChartPainterSaver_p.h"
00030 #include "KDChartDataValueAttributes.h"
00031 
00032 #include <KDABLibFakes>
00033 
00034 using namespace KDChart;
00035 
00036 RadarDiagram::Private::Private() :
00037     closeDatasets( false ),
00038     reverseData( false ),
00039     fillAlpha( 0.0 )
00040 {
00041 }
00042 
00043 RadarDiagram::Private::~Private() {}
00044 
00045 #define d d_func()
00046 
00047 RadarDiagram::RadarDiagram( QWidget* parent, RadarCoordinatePlane* plane ) :
00048     AbstractPolarDiagram( new Private( ), parent, plane )
00049 {
00050     //init();
00051 }
00052 
00053 RadarDiagram::~RadarDiagram()
00054 {
00055 
00056 }
00057 
00058 void RadarDiagram::init()
00059 {
00060 }
00061 
00062 
00066 RadarDiagram * RadarDiagram::clone() const
00067 {
00068     RadarDiagram* newDiagram = new RadarDiagram( new Private( *d ) );
00069     // This needs to be copied after the fact
00070     newDiagram->d->closeDatasets = d->closeDatasets;
00071     return newDiagram;
00072 }
00073 
00074 const QPair<QPointF, QPointF> RadarDiagram::calculateDataBoundaries () const
00075 {
00076     if ( !checkInvariants(true) ) return QPair<QPointF, QPointF>( QPointF( 0, 0 ), QPointF( 0, 0 ) );
00077     const int rowCount = model()->rowCount(rootIndex());
00078     const int colCount = model()->columnCount(rootIndex());
00079     qreal xMin = 0.0;
00080     qreal xMax = colCount;
00081     qreal yMin = 0, yMax = 0;
00082     for ( int iCol=0; iCol<colCount; ++iCol ) {
00083         for ( int iRow=0; iRow< rowCount; ++iRow ) {
00084             qreal value = model()->data( model()->index( iRow, iCol, rootIndex() ) ).toReal(); // checked
00085             yMax = qMax( yMax, value );
00086             yMin = qMin( yMin, value );
00087         }
00088     }
00089     QPointF bottomLeft ( QPointF( xMin, yMin ) );
00090     QPointF topRight ( QPointF( xMax, yMax ) );
00091     return QPair<QPointF, QPointF> ( bottomLeft,  topRight );
00092 }
00093 
00094 
00095 
00096 void RadarDiagram::paintEvent ( QPaintEvent*)
00097 {
00098     QPainter painter ( viewport() );
00099     PaintContext ctx;
00100     ctx.setPainter ( &painter );
00101     ctx.setRectangle( QRectF ( 0, 0, width(), height() ) );
00102     paint ( &ctx );
00103 }
00104 
00105 void RadarDiagram::paint( PaintContext* ctx )
00106 {
00107     qreal dummy1, dummy2;
00108     paint( ctx, true,  dummy1, dummy2 );
00109     paint( ctx, false, dummy1, dummy2 );
00110 }
00111 
00112 static qreal fitFontSizeToGeometry( const QString& text, const QFont& font, const QRectF& geometry, const TextAttributes& ta )
00113 {
00114     QFont f = font;
00115     const qreal origResult = f.pointSizeF();
00116     qreal result = origResult;
00117     const QSizeF mySize = geometry.size();
00118     if( mySize.isNull() )
00119         return result;
00120 
00121     const QString t = text;
00122     QFontMetrics fm( f );
00123     while( true )
00124     {
00125         const QSizeF textSize = rotatedRect( fm.boundingRect( t ), ta.rotation() ).normalized().size();
00126 
00127         if( textSize.height() <= mySize.height() && textSize.width() <= mySize.width() )
00128             return result;
00129 
00130         result -= 0.5;
00131         if( result <= 0.0 )
00132             return origResult;
00133         f.setPointSizeF( result );
00134         fm = QFontMetrics( f );
00135     }
00136 }
00137 
00138 static QPointF scaleToRealPosition( const QPointF& origin, const QRectF& sourceRect, const QRectF& destRect, const AbstractCoordinatePlane& plane )
00139 {
00140     QPointF result = plane.translate( origin );
00141     result -= sourceRect.topLeft();
00142     result.setX( result.x() / sourceRect.width() * destRect.width() );
00143     result.setY( result.y() / sourceRect.height() * destRect.height() );
00144     result += destRect.topLeft();
00145     return result;
00146 }
00147 
00148 void RadarDiagram::setReverseData( bool val )
00149 {
00150     d->reverseData = val;
00151 }
00152 bool RadarDiagram::reverseData()
00153 {
00154     return d->reverseData;
00155 }
00156 
00157 // local structure to remember the settings of a polygon inclusive the used color and pen.
00158 struct Polygon {
00159     QPolygonF polygon;
00160     QBrush brush;
00161     QPen pen;
00162     Polygon(const QPolygonF &polygon, const QBrush &brush, const QPen &pen) : polygon(polygon), brush(brush), pen(pen) {}
00163 };
00164 
00165 void RadarDiagram::paint( PaintContext* ctx,
00166                           bool calculateListAndReturnScale,
00167                           qreal& newZoomX, qreal& newZoomY )
00168 {
00169     // note: Not having any data model assigned is no bug
00170     //       but we can not draw a diagram then either.
00171     if ( !checkInvariants(true) )
00172         return;
00173     d->reverseMapper.clear();
00174 
00175     const int rowCount = model()->rowCount( rootIndex() );
00176     const int colCount = model()->columnCount( rootIndex() );
00177 
00178     int iRow, iCol;
00179 
00180     const qreal min = dataBoundaries().first.y();
00181     const qreal r = qAbs( min ) + dataBoundaries().second.y();
00182     const qreal step = ( r - qAbs( min ) ) / ( numberOfGridRings() );
00183     
00184     RadarCoordinatePlane* plane = dynamic_cast<RadarCoordinatePlane*>(ctx->coordinatePlane());
00185     TextAttributes ta = plane->textAttributes();
00186     QRectF fontRect = ctx->rectangle();    
00187     fontRect.setSize( QSizeF( fontRect.width(), step / 2.0 ) );
00188     const qreal labelFontSize = fitFontSizeToGeometry( QString::fromLatin1( "TestXYWQgqy" ), ta.font(), fontRect, ta );
00189     QFont labelFont = ta.font();
00190     ctx->painter()->setPen( ta.pen() );
00191     labelFont.setPointSizeF( labelFontSize );
00192     const QFontMetricsF metric( labelFont );
00193     const qreal labelHeight = metric.height();
00194     QPointF offset;
00195     QRectF destRect = ctx->rectangle();    
00196     if ( ta.isVisible() )
00197     {        
00198         destRect.setY( destRect.y() + 2 * labelHeight );
00199         destRect.setHeight( destRect.height() - 4 * labelHeight );
00200     }
00201     
00202     if( calculateListAndReturnScale ){
00203         ctx->painter()->save();
00204         // Check if all of the data value texts / data comments will fit
00205         // into the available space:
00206         d->labelPaintCache.clear();
00207         ctx->painter()->save();
00208         for ( iCol=0; iCol < colCount; ++iCol ) {
00209             for ( iRow=0; iRow < rowCount; ++iRow ) {
00210                 QModelIndex index = model()->index( iRow, iCol, rootIndex() ); // checked
00211                 const qreal value = model()->data( index ).toReal();
00212                 QPointF point = scaleToRealPosition( QPointF( value, iRow ), ctx->rectangle(), destRect, *ctx->coordinatePlane() );
00213                 d->addLabel( &d->labelPaintCache, index, 0, PositionPoints( point ),
00214                              Position::Center, Position::Center, value );
00215             }
00216         }
00217         ctx->painter()->restore();
00218         const qreal oldZoomX = coordinatePlane()->zoomFactorX();
00219         const qreal oldZoomY = coordinatePlane()->zoomFactorY();
00220         newZoomX = oldZoomX;
00221         newZoomY = oldZoomY;
00222         if( d->labelPaintCache.paintReplay.count() ) {
00223             QRectF txtRectF;
00224             d->paintDataValueTextsAndMarkers( ctx, d->labelPaintCache, true, true, &txtRectF );
00225             const QRect txtRect = txtRectF.toRect();
00226             const QRect curRect = coordinatePlane()->geometry();
00227             const qreal gapX = qMin( txtRect.left() - curRect.left(), curRect.right()  - txtRect.right() );
00228             const qreal gapY = qMin( txtRect.top()  - curRect.top(),  curRect.bottom() - txtRect.bottom() );
00229             newZoomX = oldZoomX;
00230             newZoomY = oldZoomY;
00231             if( gapX < 0.0 )
00232                 newZoomX *= 1.0 + (gapX-1.0) / curRect.width();
00233             if( gapY < 0.0 )
00234                 newZoomY *= 1.0 + (gapY-1.0) / curRect.height();
00235         }
00236         ctx->painter()->restore();
00237 
00238     }else{
00239         // Iterate through data sets and create a list of polygons out of them.
00240         QList<Polygon> polygons;
00241         for ( iCol=0; iCol < colCount; ++iCol ) {
00242             //TODO(khz): As of yet RadarDiagram can not show per-segment line attributes
00243             //           but it draws every polyline in one go - using one color.
00244             //           This needs to be enhanced to allow for cell-specific settings
00245             //           in the same way as LineDiagram does it.
00246             QPolygonF polygon;
00247             QPointF point0;
00248             for ( iRow=0; iRow < rowCount; ++iRow ) {
00249                 QModelIndex index = model()->index( iRow, iCol, rootIndex() ); // checked
00250                 const qreal value = model()->data( index ).toReal();
00251                 QPointF point = scaleToRealPosition( QPointF( value, d->reverseData ? ( rowCount - iRow ) : iRow ), ctx->rectangle(), destRect, *ctx->coordinatePlane() );
00252                 polygon.append( point );
00253                 if( ! iRow )
00254                     point0= point;
00255             }
00256             if( closeDatasets() && rowCount )
00257                 polygon.append( point0 );
00258 
00259             QBrush brush = qVariantValue<QBrush>( d->datasetAttrs( iCol, KDChart::DatasetBrushRole ) );
00260             QPen p( model()->headerData( iCol, Qt::Horizontal, KDChart::DatasetPenRole ).value< QPen >() );
00261             if ( p.style() != Qt::NoPen )
00262             {
00263                 polygons.append( Polygon(polygon, brush, PrintingParameters::scalePen( p )) );
00264             }
00265         }
00266 
00267         // first fill the areas with the brush-color and the defined alpha-value.
00268         if (d->fillAlpha > 0.0) {
00269             Q_FOREACH(const Polygon& p, polygons) {
00270                 PainterSaver painterSaver( ctx->painter() );
00271                 ctx->painter()->setRenderHint ( QPainter::Antialiasing );
00272                 QBrush br = p.brush;
00273                 QColor c = br.color();
00274                 c.setAlphaF(d->fillAlpha);
00275                 br.setColor(c);
00276                 ctx->painter()->setBrush( br );
00277                 ctx->painter()->setPen( p.pen );
00278                 ctx->painter()->drawPolygon( p.polygon );
00279             }
00280         }
00281 
00282         // then draw the poly-lines.
00283         Q_FOREACH(const Polygon& p, polygons) {
00284             PainterSaver painterSaver( ctx->painter() );
00285             ctx->painter()->setRenderHint ( QPainter::Antialiasing );
00286             ctx->painter()->setBrush( p.brush );
00287             ctx->painter()->setPen( p.pen );
00288             ctx->painter()->drawPolyline( p.polygon );
00289         }
00290 
00291         d->paintDataValueTextsAndMarkers( ctx, d->labelPaintCache, true );
00292     }
00293 }
00294 
00295 void RadarDiagram::resize ( const QSizeF& )
00296 {
00297 }
00298 
00299 /*virtual*/
00300 qreal RadarDiagram::valueTotals () const
00301 {
00302     return model()->rowCount(rootIndex());
00303 }
00304 
00305 /*virtual*/
00306 qreal RadarDiagram::numberOfValuesPerDataset() const
00307 {
00308     return model() ? model()->rowCount(rootIndex()) : 0.0;
00309 }
00310 
00311 /*virtual*/
00312 qreal RadarDiagram::numberOfGridRings() const
00313 {
00314     return 5; // FIXME
00315 }
00316 
00317 void RadarDiagram::setCloseDatasets( bool closeDatasets )
00318 {
00319     d->closeDatasets = closeDatasets;
00320 }
00321 
00322 bool RadarDiagram::closeDatasets() const
00323 {
00324     return d->closeDatasets;
00325 }
00326 
00327 qreal RadarDiagram::fillAlpha() const
00328 {
00329     return d->fillAlpha;
00330 }
00331 
00332 void RadarDiagram::setFillAlpha(qreal alphaF)
00333 {
00334     d->fillAlpha = alphaF;
00335 }
00336 
00337 void RadarDiagram::resizeEvent ( QResizeEvent*)
00338 {
00339 }
00340 
00341 
 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/