File indexing completed on 2024-12-15 04:02:31
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 "KChartLeveyJenningsAxis.h" 0010 #include "KChartLeveyJenningsAxis_p.h" 0011 0012 #include <QDateTime> 0013 #include <QPainter> 0014 0015 #include "KChartPaintContext.h" 0016 #include "KChartChart.h" 0017 #include "KChartAbstractCartesianDiagram.h" 0018 #include "KChartAbstractGrid.h" 0019 #include "KChartPainterSaver_p.h" 0020 #include "KChartLayoutItems.h" 0021 #include "KChartPrintingParameters.h" 0022 #include "KChartMath_p.h" 0023 0024 using namespace KChart; 0025 0026 #define d (d_func()) 0027 0028 LeveyJenningsAxis::LeveyJenningsAxis ( LeveyJenningsDiagram* diagram ) 0029 : CartesianAxis ( new Private( diagram, this ), diagram ) 0030 { 0031 init(); 0032 } 0033 0034 LeveyJenningsAxis::~LeveyJenningsAxis () 0035 { 0036 // when we remove the first axis it will unregister itself and 0037 // propagate the next one to the primary, thus the while loop 0038 while ( d->mDiagram ) { 0039 LeveyJenningsDiagram *cd = qobject_cast< LeveyJenningsDiagram* >( d->mDiagram ); 0040 cd->takeAxis( this ); 0041 } 0042 for ( AbstractDiagram *diagram : qAsConst(d->secondaryDiagrams) ) { 0043 LeveyJenningsDiagram *cd = qobject_cast< LeveyJenningsDiagram* >( diagram ); 0044 cd->takeAxis( this ); 0045 } 0046 } 0047 0048 void LeveyJenningsAxis::init () 0049 { 0050 setType( LeveyJenningsGridAttributes::Expected ); 0051 setDateFormat( Qt::TextDate ); 0052 const QStringList labels = QStringList() << tr( "-3sd" ) << tr( "-2sd" ) << tr( "mean" ) 0053 << tr( "+2sd" ) << tr( "+3sd" ); 0054 0055 setLabels( labels ); 0056 } 0057 0058 LeveyJenningsGridAttributes::GridType LeveyJenningsAxis::type() const 0059 { 0060 return d->type; 0061 } 0062 0063 void LeveyJenningsAxis::setType( LeveyJenningsGridAttributes::GridType type ) 0064 { 0065 if ( type != d->type ) 0066 { 0067 TextAttributes ta = textAttributes(); 0068 QPen pen = ta.pen(); 0069 QColor color = type == LeveyJenningsGridAttributes::Expected ? Qt::black : Qt::blue; 0070 if ( qobject_cast< const LeveyJenningsDiagram* >( d->diagram() ) && 0071 qobject_cast< const LeveyJenningsCoordinatePlane* >( d->diagram()->coordinatePlane() ) ) 0072 { 0073 color = qobject_cast< const LeveyJenningsCoordinatePlane* >( d->diagram()->coordinatePlane() )->gridAttributes().gridPen( type ).color(); 0074 } 0075 pen.setColor( color ); 0076 ta.setPen( pen ); 0077 setTextAttributes( ta ); 0078 } 0079 d->type = type; 0080 } 0081 0082 Qt::DateFormat LeveyJenningsAxis::dateFormat() const 0083 { 0084 return d->format; 0085 } 0086 0087 void LeveyJenningsAxis::setDateFormat(Qt::DateFormat format) 0088 { 0089 d->format = format; 0090 } 0091 0092 bool LeveyJenningsAxis::compare( const LeveyJenningsAxis* other ) const 0093 { 0094 if ( other == this ) return true; 0095 if ( ! other ) { 0096 //qDebug() << "CartesianAxis::compare() cannot compare to Null pointer"; 0097 return false; 0098 } 0099 return ( static_cast<const CartesianAxis*>(this)->compare( other ) ) && 0100 ( type() == other->type() ); 0101 } 0102 0103 void LeveyJenningsAxis::paintCtx( PaintContext* context ) 0104 { 0105 0106 Q_ASSERT_X ( d->diagram(), "LeveyJenningsAxis::paint", 0107 "Function call not allowed: The axis is not assigned to any diagram." ); 0108 0109 LeveyJenningsCoordinatePlane* plane = dynamic_cast<LeveyJenningsCoordinatePlane*>(context->coordinatePlane()); 0110 Q_ASSERT_X ( plane, "LeveyJenningsAxis::paint", 0111 "Bad function call: PaintContext::coodinatePlane() NOT a levey jennings plane." ); 0112 Q_UNUSED(plane); 0113 // note: Not having any data model assigned is no bug 0114 // but we can not draw an axis then either. 0115 if ( ! d->diagram()->model() ) 0116 return; 0117 0118 if ( isOrdinate() ) 0119 paintAsOrdinate( context ); 0120 else 0121 paintAsAbscissa( context ); 0122 } 0123 0124 void LeveyJenningsAxis::paintAsOrdinate( PaintContext* context ) 0125 { 0126 const LeveyJenningsDiagram* const diag = dynamic_cast< const LeveyJenningsDiagram* >( d->diagram() ); 0127 0128 Q_ASSERT( isOrdinate() ); 0129 LeveyJenningsCoordinatePlane* plane = dynamic_cast<LeveyJenningsCoordinatePlane*>(context->coordinatePlane()); 0130 0131 const qreal meanValue = type() == LeveyJenningsGridAttributes::Expected ? diag->expectedMeanValue() 0132 : diag->calculatedMeanValue(); 0133 const qreal standardDeviation = type() == LeveyJenningsGridAttributes::Expected ? diag->expectedStandardDeviation() 0134 : diag->calculatedStandardDeviation(); 0135 const TextAttributes labelTA = textAttributes(); 0136 const bool drawLabels = labelTA.isVisible(); 0137 0138 // nothing to draw, since we've no ticks 0139 if ( !drawLabels ) 0140 return; 0141 0142 const QObject* referenceArea = plane->parent(); 0143 0144 const QVector< qreal > values = QVector< qreal >() << ( meanValue - 3 * standardDeviation ) 0145 << ( meanValue - 2 * standardDeviation ) 0146 << ( meanValue ) 0147 << ( meanValue + 2 * standardDeviation ) 0148 << ( meanValue + 3 * standardDeviation ); 0149 0150 Q_ASSERT_X( values.count() <= labels().count(), "LeveyJenningsAxis::paintAsOrdinate", "Need to have at least 5 labels" ); 0151 0152 TextLayoutItem labelItem( tr( "mean" ), 0153 labelTA, 0154 referenceArea, 0155 KChartEnums::MeasureOrientationMinimum, 0156 Qt::AlignLeft ); 0157 0158 QPainter* const painter = context->painter(); 0159 const PainterSaver ps( painter ); 0160 painter->setRenderHint( QPainter::Antialiasing, true ); 0161 painter->setClipping( false ); 0162 0163 painter->setPen ( PrintingParameters::scalePen( labelTA.pen() ) ); // perhaps we want to add a setter method later? 0164 0165 for ( int i = 0; i < values.count(); ++i ) 0166 { 0167 const QPointF labelPos = plane->translate( QPointF( 0.0, values.at( i ) ) ); 0168 const QString label = customizedLabel( labels().at( i ) ); 0169 labelItem.setText( label ); 0170 const QSize size = labelItem.sizeHint(); 0171 const float xPos = position() == Left ? geometry().right() - size.width() : geometry().left(); 0172 labelItem.setGeometry( QRectF( QPointF( xPos, labelPos.y() - size.height() / 2.0 ), size ).toRect() ); 0173 0174 // don't draw labels which aren't in the valid range (might happen for calculated SDs) 0175 if ( values.at( i ) > diag->expectedMeanValue() + 4 * diag->expectedStandardDeviation() ) 0176 continue; 0177 0178 if ( values.at( i ) < diag->expectedMeanValue() - 4 * diag->expectedStandardDeviation() ) 0179 continue; 0180 0181 labelItem.paint( painter ); 0182 } 0183 } 0184 0185 void LeveyJenningsAxis::paintAsAbscissa( PaintContext* context ) 0186 { 0187 Q_ASSERT( isAbscissa() ); 0188 0189 // this triggers drawing of the ticks 0190 setLabels( QStringList() << QString::fromLatin1( " " ) ); 0191 CartesianAxis::paintCtx( context ); 0192 0193 const LeveyJenningsDiagram* const diag = dynamic_cast< const LeveyJenningsDiagram* >( d->diagram() ); 0194 LeveyJenningsCoordinatePlane* plane = dynamic_cast<LeveyJenningsCoordinatePlane*>(context->coordinatePlane()); 0195 0196 const QObject* referenceArea = plane->parent(); 0197 const TextAttributes labelTA = textAttributes(); 0198 0199 const bool drawLabels = labelTA.isVisible(); 0200 0201 if ( !drawLabels ) 0202 return; 0203 0204 0205 const QPair< QDateTime, QDateTime > range = diag->timeRange(); 0206 0207 QPainter* const painter = context->painter(); 0208 const PainterSaver ps( painter ); 0209 painter->setRenderHint( QPainter::Antialiasing, true ); 0210 painter->setClipping( false ); 0211 0212 0213 TextLayoutItem labelItem( range.first.date().toString( dateFormat() ), 0214 labelTA, 0215 referenceArea, 0216 KChartEnums::MeasureOrientationMinimum, 0217 Qt::AlignLeft ); 0218 QSize origSize = labelItem.sizeHint(); 0219 if ( range.first.secsTo( range.second ) < 86400 ) 0220 labelItem = TextLayoutItem( range.first.toString( dateFormat() ), 0221 labelTA, 0222 referenceArea, 0223 KChartEnums::MeasureOrientationMinimum, 0224 Qt::AlignLeft ); 0225 QSize size = labelItem.sizeHint(); 0226 0227 float yPos = position() == Bottom ? geometry().bottom() - size.height() : geometry().top(); 0228 labelItem.setGeometry( QRectF( QPointF( geometry().left() - origSize.width() / 2.0, yPos ), size ).toRect() ); 0229 labelItem.paint( painter ); 0230 0231 0232 TextLayoutItem labelItem2( range.second.date().toString( dateFormat() ), 0233 labelTA, 0234 referenceArea, 0235 KChartEnums::MeasureOrientationMinimum, 0236 Qt::AlignLeft ); 0237 origSize = labelItem2.sizeHint(); 0238 if ( range.first.secsTo( range.second ) < 86400 ) 0239 labelItem2 = TextLayoutItem( range.second.toString( dateFormat() ), 0240 labelTA, 0241 referenceArea, 0242 KChartEnums::MeasureOrientationMinimum, 0243 Qt::AlignLeft ); 0244 size = labelItem2.sizeHint(); 0245 yPos = position() == Bottom ? geometry().bottom() - size.height() : geometry().top(); 0246 labelItem2.setGeometry( QRectF( QPointF( geometry().right() - size.width() + origSize.width() / 2.0, yPos ), size ).toRect() ); 0247 labelItem2.paint( painter ); 0248 }