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