File indexing completed on 2024-06-09 04:18:28
0001 /* 0002 * SPDX-FileCopyrightText: 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. 0003 * 0004 * This file is part of the KD Chart library. 0005 * 0006 * SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #include "KChartLeveyJenningsDiagram.h" 0010 #include "KChartLeveyJenningsDiagram_p.h" 0011 0012 #include "KChartChart.h" 0013 #include "KChartTextAttributes.h" 0014 #include "KChartAbstractGrid.h" 0015 #include "KChartPainterSaver_p.h" 0016 0017 #include <QDateTime> 0018 #include <QFontMetrics> 0019 #include <QPainter> 0020 #include <QSvgRenderer> 0021 #include <QVector> 0022 0023 using namespace KChart; 0024 using namespace std; 0025 0026 LeveyJenningsDiagram::Private::Private() 0027 { 0028 } 0029 0030 LeveyJenningsDiagram::Private::~Private() {} 0031 0032 0033 #define d d_func() 0034 0035 0036 LeveyJenningsDiagram::LeveyJenningsDiagram( QWidget* parent, LeveyJenningsCoordinatePlane* plane ) 0037 : LineDiagram( new Private(), parent, plane ) 0038 { 0039 init(); 0040 } 0041 0042 void LeveyJenningsDiagram::init() 0043 { 0044 d->lotChangedPosition = Qt::AlignTop; 0045 d->fluidicsPackChangedPosition = Qt::AlignBottom; 0046 d->sensorChangedPosition = Qt::AlignBottom; 0047 0048 d->scanLinePen = QPen( Qt::blue ); 0049 setPen( d->scanLinePen ); 0050 0051 d->expectedMeanValue = 0.0; 0052 d->expectedStandardDeviation = 0.0; 0053 0054 d->diagram = this; 0055 0056 d->icons[ LotChanged ] = QString::fromLatin1( ":/KDE/kchart/LeveyJennings/karo_black.svg" ); 0057 d->icons[ SensorChanged ] = QString::fromLatin1( ":/KDE/kchart/LeveyJennings/karo_red.svg" ); 0058 d->icons[ FluidicsPackChanged ] = QString::fromLatin1( ":/KDE/kchart/LeveyJennings/karo_blue.svg" ); 0059 d->icons[ OkDataPoint ] = QString::fromLatin1( ":/KDE/kchart/LeveyJennings/circle_blue.svg" ); 0060 d->icons[ NotOkDataPoint ] = QString::fromLatin1( ":/KDE/kchart/LeveyJennings/circle_blue_red.svg" ); 0061 0062 setSelectionMode( QAbstractItemView::SingleSelection ); 0063 } 0064 0065 LeveyJenningsDiagram::~LeveyJenningsDiagram() 0066 { 0067 } 0068 0069 LineDiagram * LeveyJenningsDiagram::clone() const 0070 { 0071 LeveyJenningsDiagram* newDiagram = new LeveyJenningsDiagram( new Private( *d ) ); 0072 return newDiagram; 0073 } 0074 0075 bool LeveyJenningsDiagram::compare( const LeveyJenningsDiagram* other ) const 0076 { 0077 if ( other == this ) return true; 0078 if ( ! other ) { 0079 return false; 0080 } 0081 /* 0082 qDebug() <<"\n LineDiagram::compare():"; 0083 // compare own properties 0084 qDebug() << (type() == other->type()); 0085 */ 0086 return // compare the base class 0087 ( static_cast<const LineDiagram*>(this)->compare( other ) ); 0088 } 0089 0090 void LeveyJenningsDiagram::setLotChangedSymbolPosition( Qt::Alignment pos ) 0091 { 0092 if ( d->lotChangedPosition == pos ) 0093 return; 0094 0095 d->lotChangedPosition = pos; 0096 update(); 0097 } 0098 0099 Qt::Alignment LeveyJenningsDiagram::lotChangedSymbolPosition() const 0100 { 0101 return d->lotChangedPosition; 0102 } 0103 0104 void LeveyJenningsDiagram::setFluidicsPackChangedSymbolPosition( Qt::Alignment pos ) 0105 { 0106 if ( d->fluidicsPackChangedPosition == pos ) 0107 return; 0108 0109 d->fluidicsPackChangedPosition = pos; 0110 update(); 0111 } 0112 0113 Qt::Alignment LeveyJenningsDiagram::fluidicsPackChangedSymbolPosition() const 0114 { 0115 return d->fluidicsPackChangedPosition; 0116 } 0117 0118 void LeveyJenningsDiagram::setSensorChangedSymbolPosition( Qt::Alignment pos ) 0119 { 0120 if ( d->sensorChangedPosition == pos ) 0121 return; 0122 0123 d->sensorChangedPosition = pos; 0124 update(); 0125 } 0126 0127 Qt::Alignment LeveyJenningsDiagram::sensorChangedSymbolPosition() const 0128 { 0129 return d->sensorChangedPosition; 0130 } 0131 0132 void LeveyJenningsDiagram::setFluidicsPackChanges( const QVector< QDateTime >& changes ) 0133 { 0134 if ( d->fluidicsPackChanges == changes ) 0135 return; 0136 0137 d->fluidicsPackChanges = changes; 0138 update(); 0139 } 0140 0141 QVector< QDateTime > LeveyJenningsDiagram::fluidicsPackChanges() const 0142 { 0143 return d->fluidicsPackChanges; 0144 } 0145 0146 void LeveyJenningsDiagram::setSensorChanges( const QVector< QDateTime >& changes ) 0147 { 0148 if ( d->sensorChanges == changes ) 0149 return; 0150 0151 d->sensorChanges = changes; 0152 update(); 0153 } 0154 0155 void LeveyJenningsDiagram::setScanLinePen( const QPen& pen ) 0156 { 0157 if ( d->scanLinePen == pen ) 0158 return; 0159 0160 d->scanLinePen = pen; 0161 update(); 0162 } 0163 0164 QPen LeveyJenningsDiagram::scanLinePen() const 0165 { 0166 return d->scanLinePen; 0167 } 0168 0169 QString LeveyJenningsDiagram::symbol( Symbol symbol ) const 0170 { 0171 return d->icons[ symbol ]; 0172 } 0173 0174 void LeveyJenningsDiagram::setSymbol( Symbol symbol, const QString& filename ) 0175 { 0176 if ( d->icons[ symbol ] == filename ) 0177 return; 0178 0179 delete d->iconRenderer[ symbol ]; 0180 d->iconRenderer[ symbol ] = nullptr; 0181 0182 d->icons[ symbol ] = filename; 0183 0184 update(); 0185 } 0186 0187 QVector< QDateTime > LeveyJenningsDiagram::sensorChanges() const 0188 { 0189 return d->sensorChanges; 0190 } 0191 0192 void LeveyJenningsDiagram::setExpectedMeanValue( float meanValue ) 0193 { 0194 if ( d->expectedMeanValue == meanValue ) 0195 return; 0196 0197 d->expectedMeanValue = meanValue; 0198 d->setYAxisRange(); 0199 update(); 0200 } 0201 0202 float LeveyJenningsDiagram::expectedMeanValue() const 0203 { 0204 return d->expectedMeanValue; 0205 } 0206 0207 void LeveyJenningsDiagram::setExpectedStandardDeviation( float sd ) 0208 { 0209 if ( d->expectedStandardDeviation == sd ) 0210 return; 0211 0212 d->expectedStandardDeviation = sd; 0213 d->setYAxisRange(); 0214 update(); 0215 } 0216 0217 float LeveyJenningsDiagram::expectedStandardDeviation() const 0218 { 0219 return d->expectedStandardDeviation; 0220 } 0221 0222 float LeveyJenningsDiagram::calculatedMeanValue() const 0223 { 0224 return d->calculatedMeanValue; 0225 } 0226 0227 float LeveyJenningsDiagram::calculatedStandardDeviation() const 0228 { 0229 return d->calculatedStandardDeviation; 0230 } 0231 0232 void LeveyJenningsDiagram::setModel( QAbstractItemModel* model ) 0233 { 0234 if ( this->model() != nullptr ) 0235 { 0236 disconnect( this->model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)), 0237 this, SLOT(calculateMeanAndStandardDeviation()) ); 0238 disconnect( this->model(), SIGNAL(rowsInserted(QModelIndex,int,int)), 0239 this, SLOT(calculateMeanAndStandardDeviation()) ); 0240 disconnect( this->model(), SIGNAL(rowsRemoved(QModelIndex,int,int)), 0241 this, SLOT(calculateMeanAndStandardDeviation()) ); 0242 disconnect( this->model(), SIGNAL(columnsInserted(QModelIndex,int,int)), 0243 this, SLOT(calculateMeanAndStandardDeviation()) ); 0244 disconnect( this->model(), SIGNAL(columnsRemoved(QModelIndex,int,int)), 0245 this, SLOT(calculateMeanAndStandardDeviation()) ); 0246 disconnect( this->model(), SIGNAL(modelReset()), 0247 this, SLOT(calculateMeanAndStandardDeviation()) ); 0248 disconnect( this->model(), SIGNAL(layoutChanged()), 0249 this, SLOT(calculateMeanAndStandardDeviation()) ); 0250 } 0251 LineDiagram::setModel( model ); 0252 if ( this->model() != nullptr ) 0253 { 0254 connect( this->model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)), 0255 this, SLOT(calculateMeanAndStandardDeviation()) ); 0256 connect( this->model(), SIGNAL(rowsInserted(QModelIndex,int,int)), 0257 this, SLOT(calculateMeanAndStandardDeviation()) ); 0258 connect( this->model(), SIGNAL(rowsRemoved(QModelIndex,int,int)), 0259 this, SLOT(calculateMeanAndStandardDeviation()) ); 0260 connect( this->model(), SIGNAL(columnsInserted(QModelIndex,int,int)), 0261 this, SLOT(calculateMeanAndStandardDeviation()) ); 0262 connect( this->model(), SIGNAL(columnsRemoved(QModelIndex,int,int)), 0263 this, SLOT(calculateMeanAndStandardDeviation()) ); 0264 connect( this->model(), SIGNAL(modelReset()), 0265 this, SLOT(calculateMeanAndStandardDeviation()) ); 0266 connect( this->model(), SIGNAL(layoutChanged()), 0267 this, SLOT(calculateMeanAndStandardDeviation()) ); 0268 0269 calculateMeanAndStandardDeviation(); 0270 } 0271 } 0272 0273 // TODO: This is the 'easy' solution 0274 // evaluate whether this is enough or we need some better one or even boost here 0275 void LeveyJenningsDiagram::calculateMeanAndStandardDeviation() const 0276 { 0277 QVector< qreal > values; 0278 // first fetch all values 0279 const QAbstractItemModel& m = *model(); 0280 const int rowCount = m.rowCount( rootIndex() ); 0281 0282 for ( int row = 0; row < rowCount; ++row ) 0283 { 0284 const QVariant var = m.data( m.index( row, 1, rootIndex() ) ); 0285 if ( !var.isValid() ) 0286 continue; 0287 const qreal value = var.toReal(); 0288 if ( ISNAN( value ) ) 0289 continue; 0290 values << value; 0291 } 0292 0293 qreal sum = 0.0; 0294 qreal sumSquares = 0.0; 0295 for ( qreal value : qAsConst(values) ) 0296 { 0297 sum += value; 0298 sumSquares += value * value; 0299 } 0300 0301 const int N = values.count(); 0302 0303 d->calculatedMeanValue = sum / N; 0304 d->calculatedStandardDeviation = sqrt( ( static_cast< qreal >( N ) * sumSquares - sum * sum ) / ( N * ( N - 1 ) ) ); 0305 } 0306 0307 // calculates the largest QDate not greater than \a dt. 0308 static QDate floorDay( const QDateTime& dt ) 0309 { 0310 return dt.date(); 0311 } 0312 0313 // calculates the smallest QDate not less than \a dt. 0314 static QDate ceilDay( const QDateTime& dt ) 0315 { 0316 QDate result = dt.date(); 0317 0318 if ( QDateTime( result, QTime() ) < dt ) 0319 result = result.addDays( 1 ); 0320 0321 return result; 0322 } 0323 0324 // calculates the largest QDateTime like xx:00 not greater than \a dt. 0325 static QDateTime floorHour( const QDateTime& dt ) 0326 { 0327 return QDateTime( dt.date(), QTime( dt.time().hour(), 0 ) ); 0328 } 0329 0330 // calculates the smallest QDateTime like xx:00 not less than \a dt. 0331 static QDateTime ceilHour( const QDateTime& dt ) 0332 { 0333 QDateTime result( dt.date(), QTime( dt.time().hour(), 0 ) ); 0334 0335 if ( result < dt ) 0336 result = result.addSecs( 3600 ); 0337 0338 return result; 0339 } 0340 0341 const QPair<QPointF, QPointF> LeveyJenningsDiagram::calculateDataBoundaries() const 0342 { 0343 const qreal yMin = d->expectedMeanValue - 4 * d->expectedStandardDeviation; 0344 const qreal yMax = d->expectedMeanValue + 4 * d->expectedStandardDeviation; 0345 0346 d->setYAxisRange(); 0347 0348 // rounded down/up to the prev/next midnight (at least that's the default) 0349 const QPair< QDateTime, QDateTime > range = timeRange(); 0350 const unsigned int minTime = range.first.toSecsSinceEpoch(); 0351 const unsigned int maxTime = range.second.toSecsSinceEpoch(); 0352 0353 const qreal xMin = minTime / static_cast< qreal >( 24 * 60 * 60 ); 0354 const qreal xMax = maxTime / static_cast< qreal >( 24 * 60 * 60 ) - xMin; 0355 0356 const QPointF bottomLeft( QPointF( 0, yMin ) ); 0357 const QPointF topRight( QPointF( xMax, yMax ) ); 0358 0359 return QPair< QPointF, QPointF >( bottomLeft, topRight ); 0360 } 0361 0362 QPair< QDateTime, QDateTime > LeveyJenningsDiagram::timeRange() const 0363 { 0364 if ( d->timeRange != QPair< QDateTime, QDateTime >() ) 0365 return d->timeRange; 0366 0367 const QAbstractItemModel& m = *model(); 0368 const int rowCount = m.rowCount( rootIndex() ); 0369 0370 const QDateTime begin = m.data( m.index( 0, 3, rootIndex() ) ).toDateTime(); 0371 const QDateTime end = m.data( m.index( rowCount - 1, 3, rootIndex() ) ).toDateTime(); 0372 0373 if ( begin.secsTo( end ) > 86400 ) 0374 { 0375 // if begin to end is more than 24h 0376 // round down/up to the prev/next midnight 0377 const QDate min = floorDay( begin ); 0378 const QDate max = ceilDay( end ); 0379 return QPair< QDateTime, QDateTime >( QDateTime( min, QTime() ), QDateTime( max, QTime() ) ); 0380 } 0381 else if ( begin.secsTo( end ) > 3600 ) 0382 { 0383 // more than 1h: rond down up to the prex/next hour 0384 // if begin to end is more than 24h 0385 const QDateTime min = floorHour( begin ); 0386 const QDateTime max = ceilHour( end ); 0387 return QPair< QDateTime, QDateTime >( min, max ); 0388 } 0389 return QPair< QDateTime, QDateTime >( begin, end ); 0390 } 0391 0392 void LeveyJenningsDiagram::setTimeRange( const QPair< QDateTime, QDateTime >& timeRange ) 0393 { 0394 if ( d->timeRange == timeRange ) 0395 return; 0396 0397 d->timeRange = timeRange; 0398 update(); 0399 } 0400 0401 void LeveyJenningsDiagram::drawChanges( PaintContext* ctx ) 0402 { 0403 const unsigned int minTime = timeRange().first.toSecsSinceEpoch(); 0404 0405 for ( const QDateTime& dt : qAsConst(d->fluidicsPackChanges) ) 0406 { 0407 const qreal xValue = ( dt.toSecsSinceEpoch() - minTime ) / static_cast< qreal >( 24 * 60 * 60 ); 0408 const QPointF point( xValue, 0.0 ); 0409 drawFluidicsPackChangedSymbol( ctx, point ); 0410 } 0411 0412 for ( const QDateTime& dt : qAsConst(d->sensorChanges) ) 0413 { 0414 const qreal xValue = ( dt.toSecsSinceEpoch() - minTime ) / static_cast< qreal >( 24 * 60 * 60 ); 0415 const QPointF point( xValue, 0.0 ); 0416 drawSensorChangedSymbol( ctx, point ); 0417 } 0418 } 0419 0420 void LeveyJenningsDiagram::paint( PaintContext* ctx ) 0421 { 0422 d->reverseMapper.clear(); 0423 0424 // note: Not having any data model assigned is no bug 0425 // but we can not draw a diagram then either. 0426 if ( !checkInvariants( true ) ) return; 0427 if ( !AbstractGrid::isBoundariesValid(dataBoundaries()) ) return; 0428 0429 QPainter* const painter = ctx->painter(); 0430 const PainterSaver p( painter ); 0431 if ( model()->rowCount( rootIndex() ) == 0 || model()->columnCount( rootIndex() ) < 4 ) 0432 return; // nothing to paint for us 0433 0434 AbstractCoordinatePlane* const plane = ctx->coordinatePlane(); 0435 ctx->setCoordinatePlane( plane->sharedAxisMasterPlane( painter ) ); 0436 0437 const QAbstractItemModel& m = *model(); 0438 const int rowCount = m.rowCount( rootIndex() ); 0439 0440 const unsigned int minTime = timeRange().first.toSecsSinceEpoch(); 0441 0442 painter->setRenderHint( QPainter::Antialiasing, true ); 0443 0444 int prevLot = -1; 0445 QPointF prevPoint; 0446 bool hadMissingValue = false; 0447 0448 for ( int row = 0; row < rowCount; ++row ) 0449 { 0450 const QModelIndex lotIndex = m.index( row, 0, rootIndex() ); 0451 const QModelIndex valueIndex = m.index( row, 1, rootIndex() ); 0452 const QModelIndex okIndex = m.index( row, 2, rootIndex() ); 0453 const QModelIndex timeIndex = m.index( row, 3, rootIndex() ); 0454 const QModelIndex expectedMeanIndex = m.index( row, 4, rootIndex() ); 0455 const QModelIndex expectedSDIndex = m.index( row, 5, rootIndex() ); 0456 0457 painter->setPen( pen( lotIndex ) ); 0458 0459 QVariant vValue = m.data( valueIndex ); 0460 qreal value = vValue.toReal(); 0461 const int lot = m.data( lotIndex ).toInt(); 0462 const bool ok = m.data( okIndex ).toBool(); 0463 const QDateTime time = m.data( timeIndex ).toDateTime(); 0464 const qreal xValue = ( time.toSecsSinceEpoch() - minTime ) / static_cast< qreal >( 24 * 60 * 60 ); 0465 0466 QVariant vExpectedMean = m.data( expectedMeanIndex ); 0467 const qreal expectedMean = vExpectedMean.toReal(); 0468 QVariant vExpectedSD = m.data( expectedSDIndex ); 0469 const qreal expectedSD = vExpectedSD.toReal(); 0470 0471 QPointF point = ctx->coordinatePlane()->translate( QPointF( xValue, value ) ); 0472 0473 if ( vValue.isNull() ) 0474 { 0475 hadMissingValue = true; 0476 } 0477 else 0478 { 0479 if ( !vExpectedMean.isNull() && !vExpectedSD.isNull() ) 0480 { 0481 // this calculates the 'logical' value relative to the expected mean and SD of this point 0482 value -= expectedMean; 0483 value /= expectedSD; 0484 value *= d->expectedStandardDeviation; 0485 value += d->expectedMeanValue; 0486 point = ctx->coordinatePlane()->translate( QPointF( xValue, value ) ); 0487 } 0488 0489 if ( prevLot == lot ) 0490 { 0491 const QPen pen = painter->pen(); 0492 QPen newPen = pen; 0493 0494 if ( hadMissingValue ) 0495 { 0496 newPen.setDashPattern( QVector< qreal >() << 4.0 << 4.0 ); 0497 } 0498 0499 painter->setPen( newPen ); 0500 painter->drawLine( prevPoint, point ); 0501 painter->setPen( pen ); 0502 // d->reverseMapper.addLine( valueIndex.row(), valueIndex.column(), prevPoint, point ); 0503 } 0504 else if ( row > 0 ) 0505 { 0506 drawLotChangeSymbol( ctx, QPointF( xValue, value ) ); 0507 } 0508 0509 if ( value <= d->expectedMeanValue + 4 * d->expectedStandardDeviation && 0510 value >= d->expectedMeanValue - 4 * d->expectedStandardDeviation ) 0511 { 0512 const QPointF location( xValue, value ); 0513 drawDataPointSymbol( ctx, location, ok ); 0514 d->reverseMapper.addCircle( valueIndex.row(), 0515 valueIndex.column(), 0516 ctx->coordinatePlane()->translate( location ), 0517 iconRect().size() ); 0518 } 0519 prevLot = lot; 0520 prevPoint = point; 0521 hadMissingValue = false; 0522 } 0523 0524 const QModelIndex current = selectionModel()->currentIndex(); 0525 if ( selectionModel()->rowIntersectsSelection( lotIndex.row(), lotIndex.parent() ) || current.sibling( current.row(), 0 ) == lotIndex ) 0526 { 0527 const QPen pen = ctx->painter()->pen(); 0528 painter->setPen( d->scanLinePen ); 0529 painter->drawLine( ctx->coordinatePlane()->translate( QPointF( xValue, d->expectedMeanValue - 4 * 0530 d->expectedStandardDeviation ) ), 0531 ctx->coordinatePlane()->translate( QPointF( xValue, d->expectedMeanValue + 4 * 0532 d->expectedStandardDeviation ) ) ); 0533 painter->setPen( pen ); 0534 } 0535 } 0536 0537 drawChanges( ctx ); 0538 0539 ctx->setCoordinatePlane( plane ); 0540 } 0541 0542 void LeveyJenningsDiagram::drawDataPointSymbol( PaintContext* ctx, const QPointF& pos, bool ok ) 0543 { 0544 const Symbol type = ok ? OkDataPoint : NotOkDataPoint; 0545 0546 QPainter* const painter = ctx->painter(); 0547 const PainterSaver ps( painter ); 0548 const QPointF transPos = ctx->coordinatePlane()->translate( pos ).toPoint(); 0549 painter->translate( transPos ); 0550 0551 painter->setClipping( false ); 0552 iconRenderer( type )->render( painter, iconRect() ); 0553 } 0554 0555 void LeveyJenningsDiagram::drawLotChangeSymbol( PaintContext* ctx, const QPointF& pos ) 0556 { 0557 const QPointF transPos = ctx->coordinatePlane()->translate( 0558 QPointF( pos.x(), d->lotChangedPosition & Qt::AlignTop ? d->expectedMeanValue + 0559 4 * d->expectedStandardDeviation 0560 : d->expectedMeanValue - 0561 4 * d->expectedStandardDeviation ) ); 0562 0563 0564 QPainter* const painter = ctx->painter(); 0565 const PainterSaver ps( painter ); 0566 painter->setClipping( false ); 0567 painter->translate( transPos ); 0568 iconRenderer( LotChanged )->render( painter, iconRect() ); 0569 } 0570 0571 void LeveyJenningsDiagram::drawSensorChangedSymbol( PaintContext* ctx, const QPointF& pos ) 0572 { 0573 const QPointF transPos = ctx->coordinatePlane()->translate( 0574 QPointF( pos.x(), d->sensorChangedPosition & Qt::AlignTop ? d->expectedMeanValue + 0575 4 * d->expectedStandardDeviation 0576 : d->expectedMeanValue - 0577 4 * d->expectedStandardDeviation ) ); 0578 0579 QPainter* const painter = ctx->painter(); 0580 const PainterSaver ps( painter ); 0581 painter->setClipping( false ); 0582 painter->translate( transPos ); 0583 iconRenderer( SensorChanged )->render( painter, iconRect() ); 0584 } 0585 0586 void LeveyJenningsDiagram::drawFluidicsPackChangedSymbol( PaintContext* ctx, const QPointF& pos ) 0587 { 0588 const QPointF transPos = ctx->coordinatePlane()->translate( 0589 QPointF( pos.x(), d->fluidicsPackChangedPosition & Qt::AlignTop ? d->expectedMeanValue + 0590 4 * d->expectedStandardDeviation 0591 : d->expectedMeanValue - 0592 4 * d->expectedStandardDeviation ) ); 0593 0594 QPainter* const painter = ctx->painter(); 0595 const PainterSaver ps( painter ); 0596 painter->setClipping( false ); 0597 painter->translate( transPos ); 0598 iconRenderer( FluidicsPackChanged )->render( painter, iconRect() ); 0599 } 0600 0601 QRectF LeveyJenningsDiagram::iconRect() const 0602 { 0603 const Measure m( 12.5, KChartEnums::MeasureCalculationModeAuto, KChartEnums::MeasureOrientationAuto ); 0604 TextAttributes test; 0605 test.setFontSize( m ); 0606 const QFontMetrics fm( test.calculatedFont( coordinatePlane()->parent(), KChartEnums::MeasureOrientationAuto ) ); 0607 const qreal height = fm.height() / 1.2; 0608 return QRectF( -height / 2.0, -height / 2.0, height, height ); 0609 } 0610 0611 QSvgRenderer* LeveyJenningsDiagram::iconRenderer( Symbol symbol ) 0612 { 0613 if ( d->iconRenderer[ symbol ] == nullptr ) 0614 d->iconRenderer[ symbol ] = new QSvgRenderer( d->icons[ symbol ], this ); 0615 0616 return d->iconRenderer[ symbol ]; 0617 }