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 "KDChartLeveyJenningsDiagram.h" 00024 #include "KDChartLeveyJenningsDiagram_p.h" 00025 00026 #include <QDateTime> 00027 #include <QFontMetrics> 00028 #include <QPainter> 00029 #include <QSvgRenderer> 00030 #include <QVector> 00031 00032 #include "KDChartChart.h" 00033 #include "KDChartTextAttributes.h" 00034 #include "KDChartAbstractGrid.h" 00035 00036 #include <KDABLibFakes> 00037 00038 using namespace KDChart; 00039 using namespace std; 00040 00041 LeveyJenningsDiagram::Private::Private() 00042 { 00043 } 00044 00045 LeveyJenningsDiagram::Private::~Private() {} 00046 00047 00048 #define d d_func() 00049 00050 00051 LeveyJenningsDiagram::LeveyJenningsDiagram( QWidget* parent, LeveyJenningsCoordinatePlane* plane ) 00052 : LineDiagram( new Private(), parent, plane ) 00053 { 00054 init(); 00055 } 00056 00057 void LeveyJenningsDiagram::init() 00058 { 00059 d->lotChangedPosition = Qt::AlignTop; 00060 d->fluidicsPackChangedPosition = Qt::AlignBottom; 00061 d->sensorChangedPosition = Qt::AlignBottom; 00062 00063 d->scanLinePen = QPen( Qt::blue ); 00064 setPen( d->scanLinePen ); 00065 00066 d->expectedMeanValue = 0.0; 00067 d->expectedStandardDeviation = 0.0; 00068 00069 d->diagram = this; 00070 00071 d->icons[ LotChanged ] = QString::fromLatin1( ":/KDAB/kdchart/LeveyJennings/karo_black.svg" ); 00072 d->icons[ SensorChanged ] = QString::fromLatin1( ":/KDAB/kdchart/LeveyJennings/karo_red.svg" ); 00073 d->icons[ FluidicsPackChanged ] = QString::fromLatin1( ":/KDAB/kdchart/LeveyJennings/karo_blue.svg" ); 00074 d->icons[ OkDataPoint ] = QString::fromLatin1( ":/KDAB/kdchart/LeveyJennings/circle_blue.svg" ); 00075 d->icons[ NotOkDataPoint ] = QString::fromLatin1( ":/KDAB/kdchart/LeveyJennings/circle_blue_red.svg" ); 00076 00077 setSelectionMode( QAbstractItemView::SingleSelection ); 00078 } 00079 00080 LeveyJenningsDiagram::~LeveyJenningsDiagram() 00081 { 00082 } 00083 00087 LineDiagram * LeveyJenningsDiagram::clone() const 00088 { 00089 LeveyJenningsDiagram* newDiagram = new LeveyJenningsDiagram( new Private( *d ) ); 00090 return newDiagram; 00091 } 00092 00093 bool LeveyJenningsDiagram::compare( const LeveyJenningsDiagram* other )const 00094 { 00095 if( other == this ) return true; 00096 if( ! other ){ 00097 return false; 00098 } 00099 /* 00100 qDebug() <<"\n LineDiagram::compare():"; 00101 // compare own properties 00102 qDebug() << (type() == other->type()); 00103 */ 00104 return // compare the base class 00105 ( static_cast<const LineDiagram*>(this)->compare( other ) ); 00106 } 00107 00112 void LeveyJenningsDiagram::setLotChangedSymbolPosition( Qt::Alignment pos ) 00113 { 00114 if( d->lotChangedPosition == pos ) 00115 return; 00116 00117 d->lotChangedPosition = pos; 00118 update(); 00119 } 00120 00124 Qt::Alignment LeveyJenningsDiagram::lotChangedSymbolPosition() const 00125 { 00126 return d->lotChangedPosition; 00127 } 00128 00133 void LeveyJenningsDiagram::setFluidicsPackChangedSymbolPosition( Qt::Alignment pos ) 00134 { 00135 if( d->fluidicsPackChangedPosition == pos ) 00136 return; 00137 00138 d->fluidicsPackChangedPosition = pos; 00139 update(); 00140 } 00141 00145 Qt::Alignment LeveyJenningsDiagram::fluidicsPackChangedSymbolPosition() const 00146 { 00147 return d->fluidicsPackChangedPosition; 00148 } 00149 00154 void LeveyJenningsDiagram::setSensorChangedSymbolPosition( Qt::Alignment pos ) 00155 { 00156 if( d->sensorChangedPosition == pos ) 00157 return; 00158 00159 d->sensorChangedPosition = pos; 00160 update(); 00161 } 00162 00166 Qt::Alignment LeveyJenningsDiagram::sensorChangedSymbolPosition() const 00167 { 00168 return d->sensorChangedPosition; 00169 } 00170 00174 void LeveyJenningsDiagram::setFluidicsPackChanges( const QVector< QDateTime >& changes ) 00175 { 00176 if( d->fluidicsPackChanges == changes ) 00177 return; 00178 00179 d->fluidicsPackChanges = changes; 00180 update(); 00181 } 00182 00186 QVector< QDateTime > LeveyJenningsDiagram::fluidicsPackChanges() const 00187 { 00188 return d->fluidicsPackChanges; 00189 } 00190 00194 void LeveyJenningsDiagram::setSensorChanges( const QVector< QDateTime >& changes ) 00195 { 00196 if( d->sensorChanges == changes ) 00197 return; 00198 00199 d->sensorChanges = changes; 00200 update(); 00201 } 00202 00206 void LeveyJenningsDiagram::setScanLinePen( const QPen& pen ) 00207 { 00208 if( d->scanLinePen == pen ) 00209 return; 00210 00211 d->scanLinePen = pen; 00212 update(); 00213 } 00214 00218 QPen LeveyJenningsDiagram::scanLinePen() const 00219 { 00220 return d->scanLinePen; 00221 } 00222 00226 QString LeveyJenningsDiagram::symbol( Symbol symbol ) const 00227 { 00228 return d->icons[ symbol ]; 00229 } 00230 00234 void LeveyJenningsDiagram::setSymbol( Symbol symbol, const QString& filename ) 00235 { 00236 if( d->icons[ symbol ] == filename ) 00237 return; 00238 00239 delete d->iconRenderer[ symbol ]; 00240 d->iconRenderer[ symbol ] = 0; 00241 00242 d->icons[ symbol ] = filename; 00243 00244 update(); 00245 } 00246 00250 QVector< QDateTime > LeveyJenningsDiagram::sensorChanges() const 00251 { 00252 return d->sensorChanges; 00253 } 00254 00258 void LeveyJenningsDiagram::setExpectedMeanValue( float meanValue ) 00259 { 00260 if( d->expectedMeanValue == meanValue ) 00261 return; 00262 00263 d->expectedMeanValue = meanValue; 00264 d->setYAxisRange(); 00265 update(); 00266 } 00267 00271 float LeveyJenningsDiagram::expectedMeanValue() const 00272 { 00273 return d->expectedMeanValue; 00274 } 00275 00279 void LeveyJenningsDiagram::setExpectedStandardDeviation( float sd ) 00280 { 00281 if( d->expectedStandardDeviation == sd ) 00282 return; 00283 00284 d->expectedStandardDeviation = sd; 00285 d->setYAxisRange(); 00286 update(); 00287 } 00288 00292 float LeveyJenningsDiagram::expectedStandardDeviation() const 00293 { 00294 return d->expectedStandardDeviation; 00295 } 00296 00300 float LeveyJenningsDiagram::calculatedMeanValue() const 00301 { 00302 return d->calculatedMeanValue; 00303 } 00304 00308 float LeveyJenningsDiagram::calculatedStandardDeviation() const 00309 { 00310 return d->calculatedStandardDeviation; 00311 } 00312 00313 void LeveyJenningsDiagram::setModel( QAbstractItemModel* model ) 00314 { 00315 if( this->model() != 0 ) 00316 { 00317 disconnect( this->model(), SIGNAL( dataChanged( const QModelIndex&, const QModelIndex& ) ), 00318 this, SLOT( calculateMeanAndStandardDeviation() ) ); 00319 disconnect( this->model(), SIGNAL( rowsInserted( const QModelIndex&, int, int ) ), 00320 this, SLOT( calculateMeanAndStandardDeviation() ) ); 00321 disconnect( this->model(), SIGNAL( rowsRemoved( const QModelIndex&, int, int ) ), 00322 this, SLOT( calculateMeanAndStandardDeviation() ) ); 00323 disconnect( this->model(), SIGNAL( columnsInserted( const QModelIndex&, int, int ) ), 00324 this, SLOT( calculateMeanAndStandardDeviation() ) ); 00325 disconnect( this->model(), SIGNAL( columnsRemoved( const QModelIndex&, int, int ) ), 00326 this, SLOT( calculateMeanAndStandardDeviation() ) ); 00327 disconnect( this->model(), SIGNAL( modelReset() ), 00328 this, SLOT( calculateMeanAndStandardDeviation() ) ); 00329 disconnect( this->model(), SIGNAL( layoutChanged() ), 00330 this, SLOT( calculateMeanAndStandardDeviation() ) ); 00331 } 00332 LineDiagram::setModel( model ); 00333 if( this->model() != 0 ) 00334 { 00335 connect( this->model(), SIGNAL( dataChanged( const QModelIndex&, const QModelIndex& ) ), 00336 this, SLOT( calculateMeanAndStandardDeviation() ) ); 00337 connect( this->model(), SIGNAL( rowsInserted( const QModelIndex&, int, int ) ), 00338 this, SLOT( calculateMeanAndStandardDeviation() ) ); 00339 connect( this->model(), SIGNAL( rowsRemoved( const QModelIndex&, int, int ) ), 00340 this, SLOT( calculateMeanAndStandardDeviation() ) ); 00341 connect( this->model(), SIGNAL( columnsInserted( const QModelIndex&, int, int ) ), 00342 this, SLOT( calculateMeanAndStandardDeviation() ) ); 00343 connect( this->model(), SIGNAL( columnsRemoved( const QModelIndex&, int, int ) ), 00344 this, SLOT( calculateMeanAndStandardDeviation() ) ); 00345 connect( this->model(), SIGNAL( modelReset() ), 00346 this, SLOT( calculateMeanAndStandardDeviation() ) ); 00347 connect( this->model(), SIGNAL( layoutChanged() ), 00348 this, SLOT( calculateMeanAndStandardDeviation() ) ); 00349 00350 calculateMeanAndStandardDeviation(); 00351 } 00352 } 00353 00354 // TODO: This is the 'easy' solution 00355 // evaluate whether this is enough or we need some better one or even boost here 00356 void LeveyJenningsDiagram::calculateMeanAndStandardDeviation() const 00357 { 00358 QVector< double > values; 00359 // first fetch all values 00360 const QAbstractItemModel& m = *model(); 00361 const int rowCount = m.rowCount( rootIndex() ); 00362 00363 for( int row = 0; row < rowCount; ++row ) 00364 { 00365 const QVariant var = m.data( m.index( row, 1, rootIndex() ) ); 00366 if( !var.isValid() ) 00367 continue; 00368 const double value = var.toDouble(); 00369 if( ISNAN( value ) ) 00370 continue; 00371 values << value; 00372 } 00373 00374 double sum = 0.0; 00375 double sumSquares = 0.0; 00376 KDAB_FOREACH( double value, values ) 00377 { 00378 sum += value; 00379 sumSquares += value * value; 00380 } 00381 00382 const int N = values.count(); 00383 00384 d->calculatedMeanValue = sum / N; 00385 d->calculatedStandardDeviation = sqrt( ( static_cast< double >( N ) * sumSquares - sum * sum ) / ( N * ( N - 1 ) ) ); 00386 } 00387 00388 // calculates the largest QDate not greater than \a dt. 00389 static QDate floorDay( const QDateTime& dt ) 00390 { 00391 return dt.date(); 00392 } 00393 00394 // calculates the smallest QDate not less than \a dt. 00395 static QDate ceilDay( const QDateTime& dt ) 00396 { 00397 QDate result = dt.date(); 00398 00399 if( QDateTime( result, QTime() ) < dt ) 00400 result = result.addDays( 1 ); 00401 00402 return result; 00403 } 00404 00405 // calculates the largest QDateTime like xx:00 not greater than \a dt. 00406 static QDateTime floorHour( const QDateTime& dt ) 00407 { 00408 return QDateTime( dt.date(), QTime( dt.time().hour(), 0 ) ); 00409 } 00410 00411 // calculates the smallest QDateTime like xx:00 not less than \a dt. 00412 static QDateTime ceilHour( const QDateTime& dt ) 00413 { 00414 QDateTime result( dt.date(), QTime( dt.time().hour(), 0 ) ); 00415 00416 if( result < dt ) 00417 result = result.addSecs( 3600 ); 00418 00419 return result; 00420 } 00421 00423 const QPair<QPointF, QPointF> LeveyJenningsDiagram::calculateDataBoundaries() const 00424 { 00425 const double yMin = d->expectedMeanValue - 4 * d->expectedStandardDeviation; 00426 const double yMax = d->expectedMeanValue + 4 * d->expectedStandardDeviation; 00427 00428 d->setYAxisRange(); 00429 00430 // rounded down/up to the prev/next midnight (at least that's the default) 00431 const QPair< QDateTime, QDateTime > range = timeRange(); 00432 const unsigned int minTime = range.first.toTime_t(); 00433 const unsigned int maxTime = range.second.toTime_t(); 00434 00435 const double xMin = minTime / static_cast< double >( 24 * 60 * 60 ); 00436 const double xMax = maxTime / static_cast< double >( 24 * 60 * 60 ) - xMin; 00437 00438 const QPointF bottomLeft( QPointF( 0, yMin ) ); 00439 const QPointF topRight( QPointF( xMax, yMax ) ); 00440 00441 return QPair< QPointF, QPointF >( bottomLeft, topRight ); 00442 } 00443 00447 QPair< QDateTime, QDateTime > LeveyJenningsDiagram::timeRange() const 00448 { 00449 if( d->timeRange != QPair< QDateTime, QDateTime >() ) 00450 return d->timeRange; 00451 00452 const QAbstractItemModel& m = *model(); 00453 const int rowCount = m.rowCount( rootIndex() ); 00454 00455 const QDateTime begin = m.data( m.index( 0, 3, rootIndex() ) ).toDateTime(); 00456 const QDateTime end = m.data( m.index( rowCount - 1, 3, rootIndex() ) ).toDateTime(); 00457 00458 if( begin.secsTo( end ) > 86400 ) 00459 { 00460 // if begin to end is more than 24h 00461 // round down/up to the prev/next midnight 00462 const QDate min = floorDay( begin ); 00463 const QDate max = ceilDay( end ); 00464 return QPair< QDateTime, QDateTime >( QDateTime( min ), QDateTime( max ) ); 00465 } 00466 else if( begin.secsTo( end ) > 3600 ) 00467 { 00468 // more than 1h: rond down up to the prex/next hour 00469 // if begin to end is more than 24h 00470 const QDateTime min = floorHour( begin ); 00471 const QDateTime max = ceilHour( end ); 00472 return QPair< QDateTime, QDateTime >( min, max ); 00473 } 00474 return QPair< QDateTime, QDateTime >( begin, end ); 00475 } 00476 00481 void LeveyJenningsDiagram::setTimeRange( const QPair< QDateTime, QDateTime >& timeRange ) 00482 { 00483 if( d->timeRange == timeRange ) 00484 return; 00485 00486 d->timeRange = timeRange; 00487 update(); 00488 } 00489 00493 void LeveyJenningsDiagram::drawChanges( PaintContext* ctx ) 00494 { 00495 const unsigned int minTime = timeRange().first.toTime_t(); 00496 00497 KDAB_FOREACH( const QDateTime& dt, d->fluidicsPackChanges ) 00498 { 00499 const double xValue = ( dt.toTime_t() - minTime ) / static_cast< double >( 24 * 60 * 60 ); 00500 const QPointF point( xValue, 0.0 ); 00501 drawFluidicsPackChangedSymbol( ctx, point ); 00502 } 00503 00504 KDAB_FOREACH( const QDateTime& dt, d->sensorChanges ) 00505 { 00506 const double xValue = ( dt.toTime_t() - minTime ) / static_cast< double >( 24 * 60 * 60 ); 00507 const QPointF point( xValue, 0.0 ); 00508 drawSensorChangedSymbol( ctx, point ); 00509 } 00510 } 00511 00513 void LeveyJenningsDiagram::paint( PaintContext* ctx ) 00514 { 00515 d->reverseMapper.clear(); 00516 00517 // note: Not having any data model assigned is no bug 00518 // but we can not draw a diagram then either. 00519 if ( !checkInvariants( true ) ) return; 00520 if ( !AbstractGrid::isBoundariesValid(dataBoundaries()) ) return; 00521 00522 QPainter* const painter = ctx->painter(); 00523 const PainterSaver p( painter ); 00524 if( model()->rowCount( rootIndex() ) == 0 || model()->columnCount( rootIndex() ) < 4 ) 00525 return; // nothing to paint for us 00526 00527 AbstractCoordinatePlane* const plane = ctx->coordinatePlane(); 00528 ctx->setCoordinatePlane( plane->sharedAxisMasterPlane( painter ) ); 00529 00530 const QAbstractItemModel& m = *model(); 00531 const int rowCount = m.rowCount( rootIndex() ); 00532 00533 const unsigned int minTime = timeRange().first.toTime_t(); 00534 00535 painter->setRenderHint( QPainter::Antialiasing, true ); 00536 00537 int prevLot = -1; 00538 QPointF prevPoint; 00539 bool hadMissingValue = false; 00540 00541 for( int row = 0; row < rowCount; ++row ) 00542 { 00543 const QModelIndex lotIndex = m.index( row, 0, rootIndex() ); 00544 const QModelIndex valueIndex = m.index( row, 1, rootIndex() ); 00545 const QModelIndex okIndex = m.index( row, 2, rootIndex() ); 00546 const QModelIndex timeIndex = m.index( row, 3, rootIndex() ); 00547 const QModelIndex expectedMeanIndex = m.index( row, 4, rootIndex() ); 00548 const QModelIndex expectedSDIndex = m.index( row, 5, rootIndex() ); 00549 00550 painter->setPen( pen( lotIndex ) ); 00551 00552 QVariant vValue = m.data( valueIndex ); 00553 double value = vValue.toDouble(); 00554 const int lot = m.data( lotIndex ).toInt(); 00555 const bool ok = m.data( okIndex ).toBool(); 00556 const QDateTime time = m.data( timeIndex ).toDateTime(); 00557 const double xValue = ( time.toTime_t() - minTime ) / static_cast< double >( 24 * 60 * 60 ); 00558 00559 QVariant vExpectedMean = m.data( expectedMeanIndex ); 00560 const double expectedMean = vExpectedMean.toDouble(); 00561 QVariant vExpectedSD = m.data( expectedSDIndex ); 00562 const double expectedSD = vExpectedSD.toDouble(); 00563 00564 QPointF point = ctx->coordinatePlane()->translate( QPointF( xValue, value ) ); 00565 00566 if( vValue.isNull() ) 00567 { 00568 hadMissingValue = true; 00569 } 00570 else 00571 { 00572 if( !vExpectedMean.isNull() && !vExpectedSD.isNull() ) 00573 { 00574 // this calculates the 'logical' value relative to the expected mean and SD of this point 00575 value -= expectedMean; 00576 value /= expectedSD; 00577 value *= d->expectedStandardDeviation; 00578 value += d->expectedMeanValue; 00579 point = ctx->coordinatePlane()->translate( QPointF( xValue, value ) ); 00580 } 00581 00582 if( prevLot == lot ) 00583 { 00584 const QPen pen = painter->pen(); 00585 QPen newPen = pen; 00586 00587 if( hadMissingValue ) 00588 { 00589 newPen.setDashPattern( QVector< qreal >() << 4.0 << 4.0 ); 00590 } 00591 00592 painter->setPen( newPen ); 00593 painter->drawLine( prevPoint, point ); 00594 painter->setPen( pen ); 00595 // d->reverseMapper.addLine( valueIndex.row(), valueIndex.column(), prevPoint, point ); 00596 } 00597 else if( row > 0 ) 00598 { 00599 drawLotChangeSymbol( ctx, QPointF( xValue, value ) ); 00600 } 00601 00602 if( value <= d->expectedMeanValue + 4 * d->expectedStandardDeviation && 00603 value >= d->expectedMeanValue - 4 * d->expectedStandardDeviation ) 00604 { 00605 const QPointF location( xValue, value ); 00606 drawDataPointSymbol( ctx, location, ok ); 00607 d->reverseMapper.addCircle( valueIndex.row(), 00608 valueIndex.column(), 00609 ctx->coordinatePlane()->translate( location ), 00610 iconRect().size() ); 00611 } 00612 prevLot = lot; 00613 prevPoint = point; 00614 hadMissingValue = false; 00615 } 00616 00617 const QModelIndex current = selectionModel()->currentIndex(); 00618 if( selectionModel()->rowIntersectsSelection( lotIndex.row(), lotIndex.parent() ) || current.sibling( current.row(), 0 ) == lotIndex ) 00619 { 00620 const QPen pen = ctx->painter()->pen(); 00621 painter->setPen( d->scanLinePen ); 00622 painter->drawLine( ctx->coordinatePlane()->translate( QPointF( xValue, d->expectedMeanValue - 4 * 00623 d->expectedStandardDeviation ) ), 00624 ctx->coordinatePlane()->translate( QPointF( xValue, d->expectedMeanValue + 4 * 00625 d->expectedStandardDeviation ) ) ); 00626 painter->setPen( pen ); 00627 } 00628 } 00629 00630 drawChanges( ctx ); 00631 00632 ctx->setCoordinatePlane( plane ); 00633 } 00634 00640 void LeveyJenningsDiagram::drawDataPointSymbol( PaintContext* ctx, const QPointF& pos, bool ok ) 00641 { 00642 const Symbol type = ok ? OkDataPoint : NotOkDataPoint; 00643 00644 QPainter* const painter = ctx->painter(); 00645 const PainterSaver ps( painter ); 00646 const QPointF transPos = ctx->coordinatePlane()->translate( pos ).toPoint(); 00647 painter->translate( transPos ); 00648 00649 painter->setClipping( false ); 00650 iconRenderer( type )->render( painter, iconRect() ); 00651 } 00652 00658 void LeveyJenningsDiagram::drawLotChangeSymbol( PaintContext* ctx, const QPointF& pos ) 00659 { 00660 const QPointF transPos = ctx->coordinatePlane()->translate( 00661 QPointF( pos.x(), d->lotChangedPosition & Qt::AlignTop ? d->expectedMeanValue + 00662 4 * d->expectedStandardDeviation 00663 : d->expectedMeanValue - 00664 4 * d->expectedStandardDeviation ) ); 00665 00666 00667 QPainter* const painter = ctx->painter(); 00668 const PainterSaver ps( painter ); 00669 painter->setClipping( false ); 00670 painter->translate( transPos ); 00671 iconRenderer( LotChanged )->render( painter, iconRect() ); 00672 } 00673 00679 void LeveyJenningsDiagram::drawSensorChangedSymbol( PaintContext* ctx, const QPointF& pos ) 00680 { 00681 const QPointF transPos = ctx->coordinatePlane()->translate( 00682 QPointF( pos.x(), d->sensorChangedPosition & Qt::AlignTop ? d->expectedMeanValue + 00683 4 * d->expectedStandardDeviation 00684 : d->expectedMeanValue - 00685 4 * d->expectedStandardDeviation ) ); 00686 00687 QPainter* const painter = ctx->painter(); 00688 const PainterSaver ps( painter ); 00689 painter->setClipping( false ); 00690 painter->translate( transPos ); 00691 iconRenderer( SensorChanged )->render( painter, iconRect() ); 00692 } 00693 00699 void LeveyJenningsDiagram::drawFluidicsPackChangedSymbol( PaintContext* ctx, const QPointF& pos ) 00700 { 00701 const QPointF transPos = ctx->coordinatePlane()->translate( 00702 QPointF( pos.x(), d->fluidicsPackChangedPosition & Qt::AlignTop ? d->expectedMeanValue + 00703 4 * d->expectedStandardDeviation 00704 : d->expectedMeanValue - 00705 4 * d->expectedStandardDeviation ) ); 00706 00707 QPainter* const painter = ctx->painter(); 00708 const PainterSaver ps( painter ); 00709 painter->setClipping( false ); 00710 painter->translate( transPos ); 00711 iconRenderer( FluidicsPackChanged )->render( painter, iconRect() ); 00712 } 00713 00717 QRectF LeveyJenningsDiagram::iconRect() const 00718 { 00719 const Measure m( 12.5, KDChartEnums::MeasureCalculationModeAuto, KDChartEnums::MeasureOrientationAuto ); 00720 TextAttributes test; 00721 test.setFontSize( m ); 00722 const QFontMetrics fm( test.calculatedFont( coordinatePlane()->parent(), KDChartEnums::MeasureOrientationAuto ) ); 00723 const qreal height = fm.height() / 1.2; 00724 return QRectF( -height / 2.0, -height / 2.0, height, height ); 00725 } 00726 00730 QSvgRenderer* LeveyJenningsDiagram::iconRenderer( Symbol symbol ) 00731 { 00732 if( d->iconRenderer[ symbol ] == 0 ) 00733 d->iconRenderer[ symbol ] = new QSvgRenderer( d->icons[ symbol ], this ); 00734 00735 return d->iconRenderer[ symbol ]; 00736 }