KD Chart 2  [rev.2.7]
KDChartCartesianAxis.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 ** Copyright (C) 2001-2020 Klaralvdalens Datakonsult AB. All rights reserved.
3 **
4 ** This file is part of the KD Chart library.
5 **
6 ** Licensees holding valid commercial KD Chart licenses may use this file in
7 ** accordance with the KD Chart Commercial License Agreement provided with
8 ** the Software.
9 **
10 **
11 ** This file may be distributed and/or modified under the terms of the
12 ** GNU General Public License version 2 and version 3 as published by the
13 ** Free Software Foundation and appearing in the file LICENSE.GPL.txt included.
14 **
15 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
16 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
17 **
18 ** Contact info@kdab.com if any conditions of this licensing are not
19 ** clear to you.
20 **
21 **********************************************************************/
22 
23 #include "KDChartCartesianAxis.h"
24 #include "KDChartCartesianAxis_p.h"
25 
26 #include <cmath>
27 
28 #include <QtDebug>
29 #include <QPainter>
30 #include <QPen>
31 #include <QBrush>
32 #include <QApplication>
33 
34 #include "KDChartPaintContext.h"
35 #include "KDChartChart.h"
37 #include "KDChartAbstractDiagram_p.h"
38 #include "KDChartAbstractGrid.h"
39 #include "KDChartPainterSaver_p.h"
40 #include "KDChartLayoutItems.h"
41 #include "KDChartBarDiagram.h"
42 #include "KDChartStockDiagram.h"
43 #include "KDChartLineDiagram.h"
45 
46 #include <KDABLibFakes>
47 
48 using namespace KDChart;
49 
50 #define d (d_func())
51 
52 static qreal slightlyLessThan( qreal r )
53 {
54  if ( r == 0.0 ) {
55  // scale down the epsilon somewhat arbitrarily
56  return r - std::numeric_limits< qreal >::epsilon() * 1e-6;
57  }
58  // scale the epsilon so that it (hopefully) changes at least the least significant bit of r
59  qreal diff = qAbs( r ) * std::numeric_limits< qreal >::epsilon() * 2.0;
60  return r - diff;
61 }
62 
63 static int numSignificantDecimalPlaces( qreal floatNumber )
64 {
65  static const int maxPlaces = 15;
66  QString sample = QString::number( floatNumber, 'f', maxPlaces ).section( QLatin1Char('.'), 1, 2 );
67  int ret = maxPlaces;
68  for ( ; ret > 0; ret-- ) {
69  if ( sample[ ret - 1 ] != QLatin1Char( '0' ) ) {
70  break;
71  }
72  }
73  return ret;
74 }
75 
76 // Feature idea: In case of numeric labels, consider limiting the possible values of majorThinningFactor
77 // to something like {1, 2, 5} * 10^n. Or even better, something that achieves round values in the
78 // remaining labels.
79 
80 TickIterator::TickIterator( CartesianAxis* a, CartesianCoordinatePlane* plane, uint majorThinningFactor,
81  bool omitLastTick )
82  : m_axis( a ),
83  m_majorThinningFactor( majorThinningFactor ),
84  m_majorLabelCount( 0 ),
85  m_type( NoTick )
86 {
87  // deal with the things that are specific to axes (like annotations), before the generic init().
88  const CartesianAxis::Private *axisPriv = CartesianAxis::Private::get( a );
89  XySwitch xy( axisPriv->isVertical() );
90  m_dimension = xy( plane->gridDimensionsList().first(), plane->gridDimensionsList().last() );
91  if ( omitLastTick ) {
92  // In bar and stock charts the last X tick is a fencepost with no associated value, which is
93  // convenient for grid painting. Here we have to manually exclude it to avoid overpainting.
94  m_dimension.end -= m_dimension.stepWidth;
95  }
96 
97  m_annotations = axisPriv->annotations;
98  m_customTicks = axisPriv->customTicksPositions;
99 
100  const qreal inf = std::numeric_limits< qreal >::infinity();
101 
102  if ( m_customTicks.count() ) {
103  qSort( m_customTicks.begin(), m_customTicks.end() );
104  m_customTickIndex = 0;
105  m_customTick = m_customTicks.at( m_customTickIndex );
106  } else {
107  m_customTickIndex = -1;
108  m_customTick = inf;
109  }
110 
111  if ( m_majorThinningFactor > 1 && hasShorterLabels() ) {
112  m_manualLabelTexts = m_axis->shortLabels();
113  } else {
114  m_manualLabelTexts = m_axis->labels();
115  }
116  m_manualLabelIndex = m_manualLabelTexts.isEmpty() ? -1 : 0;
117 
118  if ( !m_dimension.isCalculated ) {
119  // ### depending on the data, it is difficult to impossible to choose anchors (where ticks
120  // corresponding to the header labels are) on the ordinate or even the abscissa with
121  // 2-dimensional data. this should be somewhat mitigated by isCalculated only being false
122  // when header data labels should work, at least that seems to be what the code that sets up
123  // the dimensions is trying to do.
124  QStringList dataHeaderLabels;
125  AbstractDiagram* const dia = plane->diagram();
126  dataHeaderLabels = dia->itemRowLabels();
127  if ( !dataHeaderLabels.isEmpty() ) {
128  AttributesModel* model = dia->attributesModel();
129  const int anchorCount = model->rowCount( QModelIndex() );
130  if ( anchorCount == dataHeaderLabels.count() ) {
131  for ( int i = 0; i < anchorCount; i++ ) {
132  // ### ordinal number as anchor point generally only works for 1-dimensional data
133  m_dataHeaderLabels.insert( qreal( i ), dataHeaderLabels.at( i ) );
134  }
135  }
136  }
137  }
138 
139  bool hasMajorTicks = m_axis->rulerAttributes().showMajorTickMarks();
140  bool hasMinorTicks = m_axis->rulerAttributes().showMinorTickMarks();
141 
142  init( xy.isY, hasMajorTicks, hasMinorTicks, plane );
143 }
144 
146 {
147  QMap< qreal, QString > annotations;
148  Q_FOREACH( const AbstractDiagram *diagram, plane->diagrams() ) {
149  const AbstractCartesianDiagram *cd = qobject_cast< const AbstractCartesianDiagram* >( diagram );
150  if ( !cd ) {
151  continue;
152  }
153  Q_FOREACH( const CartesianAxis *axis, cd->axes() ) {
154  const CartesianAxis::Private *axisPriv = CartesianAxis::Private::get( axis );
155  if ( axisPriv->isVertical() == isY ) {
156  annotations.unite( axisPriv->annotations );
157  }
158  }
159  }
160  return annotations;
161 }
162 
163 TickIterator::TickIterator( bool isY, const DataDimension& dimension, bool useAnnotationsForTicks,
164  bool hasMajorTicks, bool hasMinorTicks, CartesianCoordinatePlane* plane )
165  : m_axis( 0 ),
166  m_dimension( dimension ),
167  m_majorThinningFactor( 1 ),
168  m_majorLabelCount( 0 ),
169  m_customTickIndex( -1 ),
170  m_manualLabelIndex( -1 ),
171  m_type( NoTick ),
172  m_customTick( std::numeric_limits< qreal >::infinity() )
173 {
174  if ( useAnnotationsForTicks ) {
175  m_annotations = allAxisAnnotations( plane, isY );
176  }
177  init( isY, hasMajorTicks, hasMinorTicks, plane );
178 }
179 
180 void TickIterator::init( bool isY, bool hasMajorTicks, bool hasMinorTicks,
181  CartesianCoordinatePlane* plane )
182 {
183  Q_ASSERT( std::numeric_limits< qreal >::has_infinity );
184 
185  m_isLogarithmic = m_dimension.calcMode == AbstractCoordinatePlane::Logarithmic;
186  // sanity check against infinite loops
187  hasMajorTicks = hasMajorTicks && ( m_dimension.stepWidth > 0 || m_isLogarithmic );
188  hasMinorTicks = hasMinorTicks && ( m_dimension.subStepWidth > 0 || m_isLogarithmic );
189 
190  XySwitch xy( isY );
191 
192  GridAttributes gridAttributes = plane->gridAttributes( xy( Qt::Horizontal, Qt::Vertical ) );
193  m_isLogarithmic = m_dimension.calcMode == AbstractCoordinatePlane::Logarithmic;
194  if ( !m_isLogarithmic ) {
195  // adjustedLowerUpperRange() is intended for use with linear scaling; specifically it would
196  // round lower bounds < 1 to 0.
197 
198  const bool fixedRange = xy( plane->autoAdjustHorizontalRangeToData(),
199  plane->autoAdjustVerticalRangeToData() ) >= 100;
200  const bool adjustLower = gridAttributes.adjustLowerBoundToGrid() && !fixedRange;
201  const bool adjustUpper = gridAttributes.adjustUpperBoundToGrid() && !fixedRange;
202  m_dimension = AbstractGrid::adjustedLowerUpperRange( m_dimension, adjustLower, adjustUpper );
203 
204  m_decimalPlaces = numSignificantDecimalPlaces( m_dimension.stepWidth );
205  } else {
206  // the number of significant decimal places for each label naturally varies with logarithmic scaling
207  m_decimalPlaces = -1;
208  }
209 
210  const qreal inf = std::numeric_limits< qreal >::infinity();
211 
212  // try to place m_position just in front of the first tick to be drawn so that operator++()
213  // can be used to find the first tick
214  if ( m_isLogarithmic ) {
215  if ( ISNAN( m_dimension.start ) || ISNAN( m_dimension.end ) ) {
216  // this can happen in a spurious paint operation before everything is set up;
217  // just bail out to avoid an infinite loop in that case.
218  m_dimension.start = 0.0;
219  m_dimension.end = 0.0;
220  m_position = inf;
221  m_majorTick = inf;
222  m_minorTick = inf;
223  } else if ( m_dimension.start >= 0 ) {
224  m_position = m_dimension.start ? pow( 10.0, floor( log10( m_dimension.start ) ) - 1.0 )
225  : 1e-6;
226  m_majorTick = hasMajorTicks ? m_position : inf;
227  m_minorTick = hasMinorTicks ? m_position * 20.0 : inf;
228  } else {
229  m_position = -pow( 10.0, ceil( log10( -m_dimension.start ) ) + 1.0 );
230  m_majorTick = hasMajorTicks ? m_position : inf;
231  m_minorTick = hasMinorTicks ? m_position * 0.09 : inf;
232  }
233  } else {
234  m_majorTick = hasMajorTicks ? m_dimension.start : inf;
235  m_minorTick = hasMinorTicks ? m_dimension.start : inf;
236  m_position = slightlyLessThan( m_dimension.start );
237  }
238 
239  ++( *this );
240 }
241 
242 bool TickIterator::areAlmostEqual( qreal r1, qreal r2 ) const
243 {
244  if ( !m_isLogarithmic ) {
245  qreal span = m_dimension.end - m_dimension.start;
246  if ( span == 0 ) {
247  // When start == end, we still want to show one tick if possible,
248  // which needs this function to perform a reasonable comparison.
249  span = qFuzzyIsNull( m_dimension.start) ? 1 : qAbs( m_dimension.start );
250  }
251  return qAbs( r2 - r1 ) < ( span ) * 1e-6;
252  } else {
253  return qAbs( r2 - r1 ) < qMax( qAbs( r1 ), qAbs( r2 ) ) * 0.01;
254  }
255 }
256 
257 bool TickIterator::isHigherPrecedence( qreal importantTick, qreal unimportantTick ) const
258 {
259  return importantTick != std::numeric_limits< qreal >::infinity() &&
260  ( importantTick <= unimportantTick || areAlmostEqual( importantTick, unimportantTick ) );
261 }
262 
263 void TickIterator::computeMajorTickLabel( int decimalPlaces )
264 {
265  if ( m_manualLabelIndex >= 0 ) {
266  m_text = m_manualLabelTexts[ m_manualLabelIndex++ ];
267  if ( m_manualLabelIndex >= m_manualLabelTexts.count() ) {
268  // manual label texts repeat if there are less label texts than ticks on an axis
269  m_manualLabelIndex = 0;
270  }
271  m_type = m_majorThinningFactor > 1 ? MajorTickManualShort : MajorTickManualLong;
272  } else {
273  // if m_axis is null, we are dealing with grid lines. grid lines never need labels.
274  if ( m_axis && ( m_majorLabelCount++ % m_majorThinningFactor ) == 0 ) {
276  m_dataHeaderLabels.lowerBound( slightlyLessThan( m_position ) );
277 
278  if ( it != m_dataHeaderLabels.constEnd() && areAlmostEqual( it.key(), m_position ) ) {
279  m_text = it.value();
280  m_type = MajorTickHeaderDataLabel;
281  } else {
282  // 'f' to avoid exponential notation for large numbers, consistent with data value text
283  if ( decimalPlaces < 0 ) {
284  decimalPlaces = numSignificantDecimalPlaces( m_position );
285  }
286  m_text = QString::number( m_position, 'f', decimalPlaces );
287  m_type = MajorTick;
288  }
289  } else {
290  m_text.clear();
291  m_type = MajorTick;
292  }
293  }
294 }
295 
296 void TickIterator::operator++()
297 {
298  if ( isAtEnd() ) {
299  return;
300  }
301  const qreal inf = std::numeric_limits< qreal >::infinity();
302 
303  // make sure to find the next tick at a value strictly greater than m_position
304 
305  if ( !m_annotations.isEmpty() ) {
306  QMap< qreal, QString >::ConstIterator it = m_annotations.upperBound( m_position );
307  if ( it != m_annotations.constEnd() ) {
308  m_position = it.key();
309  m_text = it.value();
310  m_type = CustomTick;
311  } else {
312  m_position = inf;
313  }
314  } else if ( !m_isLogarithmic && m_dimension.stepWidth * 1e6 <
315  qMax( qAbs( m_dimension.start ), qAbs( m_dimension.end ) ) ) {
316  // If the step width is too small to increase m_position at all, we get an infinite loop.
317  // This usually happens when m_dimension.start == m_dimension.end and both are very large.
318  // When start == end, the step width defaults to 1, and it doesn't scale with start or end.
319  // So currently, we bail and show no tick at all for empty ranges > 10^6, but at least we don't hang.
320  m_position = inf;
321  } else {
322  // advance the calculated ticks
323  if ( m_isLogarithmic ) {
324  while ( m_majorTick <= m_position ) {
325  m_majorTick *= m_position >= 0 ? 10 : 0.1;
326  }
327  while ( m_minorTick <= m_position ) {
328  // the next major tick position should be greater than this
329  m_minorTick += m_majorTick * ( m_position >= 0 ? 0.1 : 1.0 );
330  }
331  } else {
332  while ( m_majorTick <= m_position ) {
333  m_majorTick += m_dimension.stepWidth;
334  }
335  while ( m_minorTick <= m_position ) {
336  m_minorTick += m_dimension.subStepWidth;
337  }
338  }
339 
340  while ( m_customTickIndex >= 0 && m_customTick <= m_position ) {
341  if ( ++m_customTickIndex >= m_customTicks.count() ) {
342  m_customTickIndex = -1;
343  m_customTick = inf;
344  break;
345  }
346  m_customTick = m_customTicks.at( m_customTickIndex );
347  }
348 
349  // now see which kind of tick we'll have
350  if ( isHigherPrecedence( m_customTick, m_majorTick ) && isHigherPrecedence( m_customTick, m_minorTick ) ) {
351  m_position = m_customTick;
352  computeMajorTickLabel( -1 );
353  // override the MajorTick type here because those tick's labels are collision-tested, which we don't want
354  // for custom ticks. they may be arbitrarily close to other ticks, causing excessive label thinning.
355  if ( m_type == MajorTick ) {
356  m_type = CustomTick;
357  }
358  } else if ( isHigherPrecedence( m_majorTick, m_minorTick ) ) {
359  m_position = m_majorTick;
360  if ( m_minorTick != inf ) {
361  // realign minor to major
362  m_minorTick = m_majorTick;
363  }
364  computeMajorTickLabel( m_decimalPlaces );
365  } else if ( m_minorTick != inf ) {
366  m_position = m_minorTick;
367  m_text.clear();
368  m_type = MinorTick;
369  } else {
370  m_position = inf;
371  }
372  }
373 
374  if ( m_position > m_dimension.end || ISNAN( m_position ) ) {
375  m_position = inf; // make isAtEnd() return true
376  m_text.clear();
377  m_type = NoTick;
378  }
379 }
380 
381 CartesianAxis::CartesianAxis( AbstractCartesianDiagram* diagram )
382  : AbstractAxis ( new Private( diagram, this ), diagram )
383 {
384  init();
385 }
386 
388 {
389  // when we remove the first axis it will unregister itself and
390  // propagate the next one to the primary, thus the while loop
391  while ( d->mDiagram ) {
392  AbstractCartesianDiagram *cd = qobject_cast< AbstractCartesianDiagram* >( d->mDiagram );
393  cd->takeAxis( this );
394  }
395  KDAB_FOREACH( AbstractDiagram *diagram, d->secondaryDiagrams ) {
396  AbstractCartesianDiagram *cd = qobject_cast< AbstractCartesianDiagram* >( diagram );
397  cd->takeAxis( this );
398  }
399 }
400 
401 void CartesianAxis::init()
402 {
403  d->customTickLength = 3;
404  d->position = Bottom;
406  connect( this, SIGNAL( coordinateSystemChanged() ), SLOT( coordinateSystemChanged() ) );
407 }
408 
409 
410 bool CartesianAxis::compare( const CartesianAxis* other ) const
411 {
412  if ( other == this ) {
413  return true;
414  }
415  if ( !other ) {
416  return false;
417  }
418  return AbstractAxis::compare( other ) && ( position() == other->position() ) &&
419  ( titleText() == other->titleText() ) &&
420  ( titleTextAttributes() == other->titleTextAttributes() );
421 }
422 
423 void CartesianAxis::coordinateSystemChanged()
424 {
425  layoutPlanes();
426 }
427 
428 void CartesianAxis::setTitleText( const QString& text )
429 {
430  d->titleText = text;
432  layoutPlanes();
433 }
434 
436 {
437  return d->titleText;
438 }
439 
441 {
442  d->titleTextAttributes = a;
443  d->useDefaultTextAttributes = false;
445  layoutPlanes();
446 }
447 
449 {
452  Measure me( ta.fontSize() );
453  me.setValue( me.value() * 1.5 );
454  ta.setFontSize( me );
455  return ta;
456  }
457  return d->titleTextAttributes;
458 }
459 
461 {
462  d->useDefaultTextAttributes = true;
464  layoutPlanes();
465 }
466 
468 {
469  return d->useDefaultTextAttributes;
470 }
471 
473 {
474  if ( d->position == p ) {
475  return;
476  }
477  d->position = p;
478  // Invalidating size is not always necessary if both old and new positions are horizontal or both
479  // vertical, but in practice there could be small differences due to who-knows-what, so always adapt
480  // to the possibly new size. Changing position is expensive anyway.
482  layoutPlanes();
483 }
484 
485 #if QT_VERSION < 0x040400 || defined(Q_COMPILER_MANGLES_RETURN_TYPE)
486 const
487 #endif
489 {
490  return d->position;
491 }
492 
494 {
495  if ( ! d->diagram() || ! d->diagram()->coordinatePlane() ) {
496  return;
497  }
498  AbstractCoordinatePlane* plane = d->diagram()->coordinatePlane();
499  if ( plane ) {
500  plane->layoutPlanes();
501  }
502 }
503 
504 static bool referenceDiagramIsBarDiagram( const AbstractDiagram * diagram )
505 {
506  const AbstractCartesianDiagram * dia =
507  qobject_cast< const AbstractCartesianDiagram * >( diagram );
508  if ( dia && dia->referenceDiagram() )
509  dia = dia->referenceDiagram();
510  return qobject_cast< const BarDiagram* >( dia ) != 0;
511 }
512 
514 {
515  const AbstractCartesianDiagram * dia =
516  qobject_cast< const AbstractCartesianDiagram * >( diagram );
517  if ( dia && dia->referenceDiagram() )
518  dia = dia->referenceDiagram();
519  if ( qobject_cast< const BarDiagram* >( dia ) )
520  return true;
521  if ( qobject_cast< const StockDiagram* >( dia ) )
522  return true;
523 
524  const LineDiagram * lineDiagram = qobject_cast< const LineDiagram* >( dia );
525  return lineDiagram && lineDiagram->centerDataPoints();
526 }
527 
529 {
530  const Qt::Orientation diagramOrientation = referenceDiagramIsBarDiagram( d->diagram() ) ? ( ( BarDiagram* )( d->diagram() ) )->orientation()
531  : Qt::Vertical;
532  return diagramOrientation == Qt::Vertical ? position() == Bottom || position() == Top
533  : position() == Left || position() == Right;
534 }
535 
537 {
538  return !isAbscissa();
539 }
540 
541 void CartesianAxis::paint( QPainter* painter )
542 {
543  if ( !d->diagram() || !d->diagram()->coordinatePlane() ) {
544  return;
545  }
546  PaintContext ctx;
547  ctx.setPainter ( painter );
548  AbstractCoordinatePlane *const plane = d->diagram()->coordinatePlane();
549  ctx.setCoordinatePlane( plane );
550 
551  ctx.setRectangle( QRectF( areaGeometry() ) );
552  PainterSaver painterSaver( painter );
553 
554  // enable clipping only when required due to zoom, because it slows down painting
555  // (the alternative to clipping when zoomed in requires much more work to paint just the right area)
556  const qreal zoomFactor = d->isVertical() ? plane->zoomFactorY() : plane->zoomFactorX();
557  if ( zoomFactor > 1.0 ) {
558  painter->setClipRegion( areaGeometry().adjusted( - d->amountOfLeftOverlap - 1, - d->amountOfTopOverlap - 1,
559  d->amountOfRightOverlap + 1, d->amountOfBottomOverlap + 1 ) );
560  }
561  paintCtx( &ctx );
562 }
563 
564 const TextAttributes CartesianAxis::Private::titleTextAttributesWithAdjustedRotation() const
565 {
566  TextAttributes titleTA( titleTextAttributes );
567  int rotation = titleTA.rotation();
568  if ( position == Left || position == Right ) {
569  rotation += 270;
570  }
571  if ( rotation >= 360 ) {
572  rotation -= 360;
573  }
574  // limit the allowed values to 0, 90, 180, 270
575  rotation = ( rotation / 90 ) * 90;
576  titleTA.setRotation( rotation );
577  return titleTA;
578 }
579 
580 QString CartesianAxis::Private::customizedLabelText( const QString& text, Qt::Orientation orientation,
581  qreal value ) const
582 {
583  // ### like in the old code, using int( value ) as column number...
584  QString withUnits = diagram()->unitPrefix( int( value ), orientation, true ) +
585  text +
586  diagram()->unitSuffix( int( value ), orientation, true );
587  return axis()->customizedLabel( withUnits );
588 }
589 
590 void CartesianAxis::setTitleSpace( qreal axisTitleSpace )
591 {
592  d->axisTitleSpace = axisTitleSpace;
593 }
594 
596 {
597  return d->axisTitleSpace;
598 }
599 
600 void CartesianAxis::setTitleSize( qreal value )
601 {
602  Q_UNUSED( value )
603  // ### remove me
604 }
605 
607 {
608  // ### remove me
609  return 1.0;
610 }
611 
612 void CartesianAxis::Private::drawTitleText( QPainter* painter, CartesianCoordinatePlane* plane,
613  const QRect& geoRect ) const
614 {
615  const TextAttributes titleTA( titleTextAttributesWithAdjustedRotation() );
616  if ( titleTA.isVisible() ) {
617  TextLayoutItem titleItem( titleText, titleTA, plane->parent(), KDChartEnums::MeasureOrientationMinimum,
618  Qt::AlignHCenter | Qt::AlignVCenter );
619  QPointF point;
620  QSize size = titleItem.sizeHint();
621  switch ( position ) {
622  case Top:
623  point.setX( geoRect.left() + geoRect.width() / 2 );
624  point.setY( geoRect.top() + ( size.height() / 2 ) / axisTitleSpace );
625  size.setWidth( qMin( size.width(), axis()->geometry().width() ) );
626  break;
627  case Bottom:
628  point.setX( geoRect.left() + geoRect.width() / 2 );
629  point.setY( geoRect.bottom() - ( size.height() / 2 ) / axisTitleSpace );
630  size.setWidth( qMin( size.width(), axis()->geometry().width() ) );
631  break;
632  case Left:
633  point.setX( geoRect.left() + ( size.width() / 2 ) / axisTitleSpace );
634  point.setY( geoRect.top() + geoRect.height() / 2 );
635  size.setHeight( qMin( size.height(), axis()->geometry().height() ) );
636  break;
637  case Right:
638  point.setX( geoRect.right() - ( size.width() / 2 ) / axisTitleSpace );
639  point.setY( geoRect.top() + geoRect.height() / 2 );
640  size.setHeight( qMin( size.height(), axis()->geometry().height() ) );
641  break;
642  }
643  const PainterSaver painterSaver( painter );
644  painter->setClipping( false );
645  painter->translate( point );
646  titleItem.setGeometry( QRect( QPoint( -size.width() / 2, -size.height() / 2 ), size ) );
647  titleItem.paint( painter );
648  }
649 }
650 
651 bool CartesianAxis::Private::isVertical() const
652 {
653  return axis()->isAbscissa() == AbstractDiagram::Private::get( diagram() )->isTransposed();
654 }
655 
657 {
658  Q_ASSERT_X ( d->diagram(), "CartesianAxis::paint",
659  "Function call not allowed: The axis is not assigned to any diagram." );
660 
661  CartesianCoordinatePlane* plane = dynamic_cast<CartesianCoordinatePlane*>( context->coordinatePlane() );
662  Q_ASSERT_X ( plane, "CartesianAxis::paint",
663  "Bad function call: PaintContext::coordinatePlane() NOT a cartesian plane." );
664 
665  // note: Not having any data model assigned is no bug
666  // but we can not draw an axis then either.
667  if ( !d->diagram()->model() ) {
668  return;
669  }
670 
671  const bool centerTicks = referenceDiagramNeedsCenteredAbscissaTicks( d->diagram() ) && isAbscissa();
672 
673  XySwitch geoXy( d->isVertical() );
674 
675  QPainter* const painter = context->painter();
676 
677  // determine the position of the axis (also required for labels) and paint it
678 
679  qreal transversePosition = signalingNaN; // in data space
680  // the next one describes an additional shift in screen space; it is unfortunately required to
681  // make axis sharing work, which uses the areaGeometry() to override the position of the axis.
682  qreal transverseScreenSpaceShift = signalingNaN;
683  {
684  // determine the unadulterated position in screen space
685 
686  DataDimension dimX = plane->gridDimensionsList().first();
687  DataDimension dimY = plane->gridDimensionsList().last();
688  QPointF start( dimX.start, dimY.start );
689  QPointF end( dimX.end, dimY.end );
690  // consider this: you can turn a diagonal line into a horizontal or vertical line on any
691  // edge by changing just one of its four coordinates.
692  switch ( position() ) {
694  end.setY( dimY.start );
695  break;
696  case CartesianAxis::Top:
697  start.setY( dimY.end );
698  break;
699  case CartesianAxis::Left:
700  end.setX( dimX.start );
701  break;
703  start.setX( dimX.end );
704  break;
705  }
706 
707  transversePosition = geoXy( start.y(), start.x() );
708 
709  QPointF transStart = plane->translate( start );
710  QPointF transEnd = plane->translate( end );
711 
712  // an externally set areaGeometry() moves the axis position transversally; the shift is
713  // nonzero only when this is a shared axis
714 
715  const QRect geo = areaGeometry();
716  switch ( position() ) {
718  transverseScreenSpaceShift = geo.top() - transStart.y();
719  break;
720  case CartesianAxis::Top:
721  transverseScreenSpaceShift = geo.bottom() - transStart.y();
722  break;
723  case CartesianAxis::Left:
724  transverseScreenSpaceShift = geo.right() - transStart.x();
725  break;
727  transverseScreenSpaceShift = geo.left() - transStart.x();
728  break;
729  }
730 
731  geoXy.lvalue( transStart.ry(), transStart.rx() ) += transverseScreenSpaceShift;
732  geoXy.lvalue( transEnd.ry(), transEnd.rx() ) += transverseScreenSpaceShift;
733 
734  if ( rulerAttributes().showRulerLine() ) {
735  bool clipSaved = context->painter()->hasClipping();
736  painter->setClipping( false );
737  painter->drawLine( transStart, transEnd );
738  painter->setClipping( clipSaved );
739  }
740  }
741 
742  // paint ticks and labels
743 
744  TextAttributes labelTA = textAttributes();
745  RulerAttributes rulerAttr = rulerAttributes();
746 
747  int labelThinningFactor = 1;
748  // TODO: label thinning also when grid line distance < 4 pixels, not only when labels collide
749  TextLayoutItem *tickLabel = new TextLayoutItem( QString(), labelTA, plane->parent(),
751  TextLayoutItem *prevTickLabel = new TextLayoutItem( QString(), labelTA, plane->parent(),
753  QPointF prevTickLabelPos;
754  enum {
755  Layout = 0,
756  Painting,
757  Done
758  };
759  for ( int step = labelTA.isVisible() ? Layout : Painting; step < Done; step++ ) {
760  bool skipFirstTick = !rulerAttr.showFirstTick();
761  bool isFirstLabel = true;
762  for ( TickIterator it( this, plane, labelThinningFactor, centerTicks ); !it.isAtEnd(); ++it ) {
763  if ( skipFirstTick ) {
764  skipFirstTick = false;
765  continue;
766  }
767 
768  const qreal drawPos = it.position() + ( centerTicks ? 0.5 : 0. );
769  QPointF onAxis = plane->translate( geoXy( QPointF( drawPos, transversePosition ) ,
770  QPointF( transversePosition, drawPos ) ) );
771  geoXy.lvalue( onAxis.ry(), onAxis.rx() ) += transverseScreenSpaceShift;
772  const bool isOutwardsPositive = position() == Bottom || position() == Right;
773 
774  // paint the tick mark
775 
776  QPointF tickEnd = onAxis;
777  qreal tickLen = it.type() == TickIterator::CustomTick ?
778  d->customTickLength : tickLength( it.type() == TickIterator::MinorTick );
779  geoXy.lvalue( tickEnd.ry(), tickEnd.rx() ) += isOutwardsPositive ? tickLen : -tickLen;
780 
781  // those adjustments are required to paint the ticks exactly on the axis and of the right length
782  if ( position() == Top ) {
783  onAxis.ry() += 1;
784  tickEnd.ry() += 1;
785  } else if ( position() == Left ) {
786  tickEnd.rx() += 1;
787  }
788 
789  if ( step == Painting ) {
790  painter->save();
791  if ( rulerAttr.hasTickMarkPenAt( it.position() ) ) {
792  painter->setPen( rulerAttr.tickMarkPen( it.position() ) );
793  } else {
794  painter->setPen( it.type() == TickIterator::MinorTick ? rulerAttr.minorTickMarkPen()
795  : rulerAttr.majorTickMarkPen() );
796  }
797  painter->drawLine( onAxis, tickEnd );
798  painter->restore();
799  }
800 
801  if ( it.text().isEmpty() || !labelTA.isVisible() ) {
802  // the following code in the loop is only label painting, so skip it
803  continue;
804  }
805 
806  // paint the label
807 
808  QString text = it.text();
809  if ( it.type() == TickIterator::MajorTick ) {
810  // add unit prefixes and suffixes, then customize
811  text = d->customizedLabelText( text, geoXy( Qt::Horizontal, Qt::Vertical ), it.position() );
812  } else if ( it.type() == TickIterator::MajorTickHeaderDataLabel ) {
813  // unit prefixes and suffixes have already been added in this case - only customize
814  text = customizedLabel( text );
815  }
816 
817  tickLabel->setText( text );
818  QSizeF size = QSizeF( tickLabel->sizeHint() );
819  QPolygon labelPoly = tickLabel->boundingPolygon();
820  Q_ASSERT( labelPoly.count() == 4 );
821 
822  // for alignment, find the label polygon edge "most parallel" and closest to the axis
823 
824  int axisAngle = 0;
825  switch ( position() ) {
826  case Bottom:
827  axisAngle = 0; break;
828  case Top:
829  axisAngle = 180; break;
830  case Right:
831  axisAngle = 270; break;
832  case Left:
833  axisAngle = 90; break;
834  default:
835  Q_ASSERT( false );
836  }
837  // the left axis is not actually pointing down and the top axis not actually pointing
838  // left, but their corresponding closest edges of a rectangular unrotated label polygon are.
839 
840  int relAngle = axisAngle - labelTA.rotation() + 45;
841  if ( relAngle < 0 ) {
842  relAngle += 360;
843  }
844  int polyCorner1 = relAngle / 90;
845  QPoint p1 = labelPoly.at( polyCorner1 );
846  QPoint p2 = labelPoly.at( polyCorner1 == 3 ? 0 : ( polyCorner1 + 1 ) );
847 
848  QPointF labelPos = tickEnd;
849 
850  qreal labelMargin = rulerAttr.labelMargin();
851  if ( labelMargin < 0 ) {
852  labelMargin = QFontMetricsF( tickLabel->realFont() ).height() * 0.5;
853  }
854  labelMargin -= tickLabel->marginWidth(); // make up for the margin that's already there
855 
856  switch ( position() ) {
857  case Left:
858  labelPos += QPointF( -size.width() - labelMargin,
859  -0.45 * size.height() - 0.5 * ( p1.y() + p2.y() ) );
860  break;
861  case Right:
862  labelPos += QPointF( labelMargin,
863  -0.45 * size.height() - 0.5 * ( p1.y() + p2.y() ) );
864  break;
865  case Top:
866  labelPos += QPointF( -0.45 * size.width() - 0.5 * ( p1.x() + p2.x() ),
867  -size.height() - labelMargin );
868  break;
869  case Bottom:
870  labelPos += QPointF( -0.45 * size.width() - 0.5 * ( p1.x() + p2.x() ),
871  labelMargin );
872  break;
873  }
874 
875  tickLabel->setGeometry( QRect( labelPos.toPoint(), size.toSize() ) );
876 
877  if ( step == Painting ) {
878  tickLabel->paint( painter );
879  }
880 
881  // collision check the current label against the previous one
882 
883  // like in the old code, we don't shorten or decimate labels if they are already the
884  // manual short type, or if they are the manual long type and on the vertical axis
885  // ### they can still collide though, especially when they're rotated!
886  if ( step == Layout ) {
887  int spaceSavingRotation = geoXy( 270, 0 );
888  bool canRotate = labelTA.autoRotate() && labelTA.rotation() != spaceSavingRotation;
889  const bool canShortenLabels = !geoXy.isY && it.type() == TickIterator::MajorTickManualLong &&
890  it.hasShorterLabels();
891  bool collides = false;
892  if ( it.type() == TickIterator::MajorTick || it.type() == TickIterator::MajorTickHeaderDataLabel
893  || canShortenLabels || canRotate ) {
894  if ( isFirstLabel ) {
895  isFirstLabel = false;
896  } else {
897  collides = tickLabel->intersects( *prevTickLabel, labelPos, prevTickLabelPos );
898  qSwap( prevTickLabel, tickLabel );
899  }
900  prevTickLabelPos = labelPos;
901  }
902  if ( collides ) {
903  // to make room, we try in order: shorten, rotate, decimate
904  if ( canRotate && !canShortenLabels ) {
905  labelTA.setRotation( spaceSavingRotation );
906  // tickLabel will be reused in the next round
907  tickLabel->setTextAttributes( labelTA );
908  } else {
909  labelThinningFactor++;
910  }
911  step--; // relayout
912  break;
913  }
914  }
915  }
916  }
917  delete tickLabel;
918  tickLabel = 0;
919  delete prevTickLabel;
920  prevTickLabel = 0;
921 
922  if ( ! titleText().isEmpty() ) {
923  d->drawTitleText( painter, plane, geometry() );
924  }
925 }
926 
927 /* pure virtual in QLayoutItem */
929 {
930  return false; // if the axis exists, it has some (perhaps default) content
931 }
932 
933 /* pure virtual in QLayoutItem */
934 Qt::Orientations CartesianAxis::expandingDirections() const
935 {
936  Qt::Orientations ret;
937  switch ( position() ) {
938  case Bottom:
939  case Top:
940  ret = Qt::Horizontal;
941  break;
942  case Left:
943  case Right:
944  ret = Qt::Vertical;
945  break;
946  default:
947  Q_ASSERT( false );
948  break;
949  };
950  return ret;
951 }
952 
954 {
955  d->cachedMaximumSize = QSize();
956 }
957 
958 /* pure virtual in QLayoutItem */
960 {
961  if ( ! d->cachedMaximumSize.isValid() )
962  d->cachedMaximumSize = d->calculateMaximumSize();
963  return d->cachedMaximumSize;
964 }
965 
966 QSize CartesianAxis::Private::calculateMaximumSize() const
967 {
968  if ( !diagram() ) {
969  return QSize();
970  }
971 
972  CartesianCoordinatePlane* plane = dynamic_cast< CartesianCoordinatePlane* >( diagram()->coordinatePlane() );
973  Q_ASSERT( plane );
974  QObject* refArea = plane->parent();
975  const bool centerTicks = referenceDiagramNeedsCenteredAbscissaTicks( diagram() )
976  && axis()->isAbscissa();
977 
978  // we ignore:
979  // - label thinning (expensive, not worst case and we want worst case)
980  // - label autorotation (expensive, obscure feature(?))
981  // - axis length (it is determined by the plane / diagram / chart anyway)
982  // - the title's influence on axis length; this one might be TODO. See KDCH-863.
983 
984  XySwitch geoXy( isVertical() );
985  qreal size = 0; // this is the size transverse to the axis direction
986 
987  // the following variables describe how much the first and last label stick out over the axis
988  // area, so that the geometry of surrounding layout items can be adjusted to make room.
989  qreal startOverhang = 0.0;
990  qreal endOverhang = 0.0;
991 
992  if ( mAxis->textAttributes().isVisible() ) {
993  // these four are used just to calculate startOverhang and endOverhang
994  qreal lowestLabelPosition = signalingNaN;
995  qreal highestLabelPosition = signalingNaN;
996  qreal lowestLabelLongitudinalSize = signalingNaN;
997  qreal highestLabelLongitudinalSize = signalingNaN;
998 
999  TextLayoutItem tickLabel( QString(), mAxis->textAttributes(), refArea,
1000  KDChartEnums::MeasureOrientationMinimum, Qt::AlignLeft );
1001  const RulerAttributes rulerAttr = mAxis->rulerAttributes();
1002 
1003  bool showFirstTick = rulerAttr.showFirstTick();
1004  for ( TickIterator it( axis(), plane, 1, centerTicks ); !it.isAtEnd(); ++it ) {
1005  const qreal drawPos = it.position() + ( centerTicks ? 0.5 : 0. );
1006  if ( !showFirstTick ) {
1007  showFirstTick = true;
1008  continue;
1009  }
1010 
1011  qreal labelSizeTransverse = 0.0;
1012  qreal labelMargin = 0.0;
1013  QString text = it.text();
1014  if ( !text.isEmpty() ) {
1015  QPointF labelPosition = plane->translate( QPointF( geoXy( drawPos, (qreal)1.0 ),
1016  geoXy( (qreal)1.0, drawPos ) ) );
1017  highestLabelPosition = geoXy( labelPosition.x(), labelPosition.y() );
1018 
1019  if ( it.type() == TickIterator::MajorTick ) {
1020  // add unit prefixes and suffixes, then customize
1021  text = customizedLabelText( text, geoXy( Qt::Horizontal, Qt::Vertical ), it.position() );
1022  } else if ( it.type() == TickIterator::MajorTickHeaderDataLabel ) {
1023  // unit prefixes and suffixes have already been added in this case - only customize
1024  text = axis()->customizedLabel( text );
1025  }
1026  tickLabel.setText( text );
1027 
1028  QSize sz = tickLabel.sizeHint();
1029  highestLabelLongitudinalSize = geoXy( sz.width(), sz.height() );
1030  if ( ISNAN( lowestLabelLongitudinalSize ) ) {
1031  lowestLabelLongitudinalSize = highestLabelLongitudinalSize;
1032  lowestLabelPosition = highestLabelPosition;
1033  }
1034 
1035  labelSizeTransverse = geoXy( sz.height(), sz.width() );
1036  labelMargin = rulerAttr.labelMargin();
1037  if ( labelMargin < 0 ) {
1038  labelMargin = QFontMetricsF( tickLabel.realFont() ).height() * 0.5;
1039  }
1040  labelMargin -= tickLabel.marginWidth(); // make up for the margin that's already there
1041  }
1042  qreal tickLength = it.type() == TickIterator::CustomTick ?
1043  customTickLength : axis()->tickLength( it.type() == TickIterator::MinorTick );
1044  size = qMax( size, tickLength + labelMargin + labelSizeTransverse );
1045  }
1046 
1047  const DataDimension dimX = plane->gridDimensionsList().first();
1048  const DataDimension dimY = plane->gridDimensionsList().last();
1049 
1050  QPointF pt = plane->translate( QPointF( dimX.start, dimY.start ) );
1051  const qreal lowestPosition = geoXy( pt.x(), pt.y() );
1052  pt = plane->translate( QPointF( dimX.end, dimY.end ) );
1053  const qreal highestPosition = geoXy( pt.x(), pt.y() );
1054 
1055  // the geoXy( 1.0, -1.0 ) here is necessary because Qt's y coordinate is inverted
1056  startOverhang = qMax( 0.0, ( lowestPosition - lowestLabelPosition ) * geoXy( 1.0, -1.0 ) +
1057  lowestLabelLongitudinalSize * 0.5 );
1058  endOverhang = qMax( 0.0, ( highestLabelPosition - highestPosition ) * geoXy( 1.0, -1.0 ) +
1059  highestLabelLongitudinalSize * 0.5 );
1060  }
1061 
1062  amountOfLeftOverlap = geoXy( startOverhang, (qreal)0.0 );
1063  amountOfRightOverlap = geoXy( endOverhang, (qreal)0.0 );
1064  amountOfBottomOverlap = geoXy( (qreal)0.0, startOverhang );
1065  amountOfTopOverlap = geoXy( (qreal)0.0, endOverhang );
1066 
1067  const TextAttributes titleTA = titleTextAttributesWithAdjustedRotation();
1068  if ( titleTA.isVisible() && !axis()->titleText().isEmpty() ) {
1069  TextLayoutItem title( axis()->titleText(), titleTA, refArea, KDChartEnums::MeasureOrientationMinimum,
1070  Qt::AlignHCenter | Qt::AlignVCenter );
1071 
1072  QFontMetricsF titleFM( title.realFont(), GlobalMeasureScaling::paintDevice() );
1073  size += geoXy( titleFM.height() * 0.33, titleFM.averageCharWidth() * 0.55 ); // spacing
1074  size += geoXy( title.sizeHint().height(), title.sizeHint().width() );
1075  }
1076 
1077  // the size parallel to the axis direction is not determined by us, so we just return 1
1078  return QSize( geoXy( 1, int( size ) ), geoXy( int ( size ), 1 ) );
1079 }
1080 
1081 /* pure virtual in QLayoutItem */
1083 {
1084  return maximumSize();
1085 }
1086 
1087 /* pure virtual in QLayoutItem */
1089 {
1090  return maximumSize();
1091 }
1092 
1093 /* pure virtual in QLayoutItem */
1094 void CartesianAxis::setGeometry( const QRect& r )
1095 {
1096  if ( d->geometry != r ) {
1097  d->geometry = r;
1099  }
1100 }
1101 
1102 /* pure virtual in QLayoutItem */
1104 {
1105  return d->geometry;
1106 }
1107 
1109 {
1110  if ( d->customTickLength == value ) {
1111  return;
1112  }
1113  d->customTickLength = value;
1115  layoutPlanes();
1116 }
1117 
1119 {
1120  return d->customTickLength;
1121 }
1122 
1123 int CartesianAxis::tickLength( bool subUnitTicks ) const
1124 {
1125  const RulerAttributes& rulerAttr = rulerAttributes();
1126  return subUnitTicks ? rulerAttr.minorTickMarkLength() : rulerAttr.majorTickMarkLength();
1127 }
1128 
1130 {
1131  return d->annotations;
1132 }
1133 
1135 {
1136  if ( d->annotations == annotations )
1137  return;
1138 
1139  d->annotations = annotations;
1141  layoutPlanes();
1142 }
1143 
1145 {
1146  return d->customTicksPositions;
1147 }
1148 
1149 void CartesianAxis::setCustomTicks( const QList< qreal >& customTicksPositions )
1150 {
1151  if ( d->customTicksPositions == customTicksPositions )
1152  return;
1153 
1154  d->customTicksPositions = customTicksPositions;
1156  layoutPlanes();
1157 }
KDChart::AbstractDiagram::itemRowLabels
QStringList itemRowLabels() const
The set of item row labels currently displayed, for use in Abscissa axes, etc.
Definition: KDChartAbstractDiagram.cpp:881
KDChart::TextAttributes::autoRotate
bool autoRotate() const
Definition: KDChartTextAttributes.cpp:219
KDChart::GlobalMeasureScaling::paintDevice
static QPaintDevice * paintDevice()
Return the paint device to use for calculating font metrics.
Definition: KDChartMeasure.cpp:227
KDChart::RulerAttributes::hasTickMarkPenAt
bool hasTickMarkPenAt(qreal value) const
Definition: KDChartRulerAttributes.cpp:162
KDChart::CartesianAxis::hasDefaultTitleTextAttributes
bool hasDefaultTitleTextAttributes() const
Definition: KDChartCartesianAxis.cpp:467
KDChart::TextLayoutItem::setGeometry
void setGeometry(const QRect &r) override
pure virtual in QLayoutItem
Definition: KDChartLayoutItems.cpp:323
KDChart::TextLayoutItem::setTextAttributes
void setTextAttributes(const TextAttributes &a)
Use this to specify the text attributes to be used for this item.
Definition: KDChartLayoutItems.cpp:277
KDChart::CartesianAxis::setAnnotations
void setAnnotations(const QMap< qreal, QString > &annotations)
Sets the axis annotations to annotations.
Definition: KDChartCartesianAxis.cpp:1134
KDChart::AbstractAxis
The base class for axes.
Definition: KDChartAbstractAxis.h:58
KDChart::RulerAttributes::labelMargin
int labelMargin() const
Definition: KDChartRulerAttributes.cpp:208
KDChart::TextLayoutItem::realFont
virtual QFont realFont() const
Definition: KDChartLayoutItems.cpp:386
KDChart::CartesianAxis::layoutPlanes
virtual void layoutPlanes()
Definition: KDChartCartesianAxis.cpp:493
KDChart::CartesianAxis::isOrdinate
virtual bool isOrdinate() const
Definition: KDChartCartesianAxis.cpp:536
KDChart::CartesianAxis::tickLength
virtual int tickLength(bool subUnitTicks=false) const
Definition: KDChartCartesianAxis.cpp:1123
numSignificantDecimalPlaces
static int numSignificantDecimalPlaces(qreal floatNumber)
Definition: KDChartCartesianAxis.cpp:63
KDChart::TextLayoutItem::setText
void setText(const QString &text)
Definition: KDChartLayoutItems.cpp:244
KDChart::CartesianCoordinatePlane::autoAdjustVerticalRangeToData
unsigned int autoAdjustVerticalRangeToData() const
Returns the maximal allowed percent of the vertical space covered by the coordinate plane that may be...
Definition: KDChartCartesianCoordinatePlane.cpp:715
KDChart::CartesianAxis::expandingDirections
Qt::Orientations expandingDirections() const override
pure virtual in QLayoutItem
Definition: KDChartCartesianAxis.cpp:934
KDChart::RulerAttributes::tickMarkPen
QPen tickMarkPen() const
Definition: KDChartRulerAttributes.cpp:103
KDChart::CartesianAxis::position
virtual const Position position() const
Definition: KDChartCartesianAxis.cpp:488
QList
Definition: KDChartPosition.h:36
KDChartCartesianAxis.h
KDChart::CartesianAxis::setPosition
virtual void setPosition(Position p)
Definition: KDChartCartesianAxis.cpp:472
KDChart::CartesianCoordinatePlane::translate
const QPointF translate(const QPointF &diagramPoint) const override
Translate the given point in value space coordinates to a position in pixel space.
Definition: KDChartCartesianCoordinatePlane.cpp:456
KDChart::CartesianAxis::compare
bool compare(const CartesianAxis *other) const
Returns true if both axes have the same settings.
Definition: KDChartCartesianAxis.cpp:410
KDChart::CartesianAxis::setTitleTextAttributes
void setTitleTextAttributes(const TextAttributes &a)
Definition: KDChartCartesianAxis.cpp:440
KDChart::CartesianAxis::maximumSize
QSize maximumSize() const override
pure virtual in QLayoutItem
Definition: KDChartCartesianAxis.cpp:959
KDChart::AbstractCoordinatePlane::layoutPlanes
void layoutPlanes()
Calling layoutPlanes() on the plane triggers the global KDChart::Chart::slotLayoutPlanes()
Definition: KDChartAbstractCoordinatePlane.cpp:265
KDChart::CartesianAxis::titleSpace
qreal titleSpace() const
Definition: KDChartCartesianAxis.cpp:595
KDChart::GridAttributes
A set of attributes controlling the appearance of grids.
Definition: KDChartGridAttributes.h:40
KDChart::CartesianAxis::setCustomTickLength
void setCustomTickLength(int value)
Sets the length of custom ticks on the axis.
Definition: KDChartCartesianAxis.cpp:1108
KDChart::CartesianAxis
The class for cartesian axes.
Definition: KDChartCartesianAxis.h:43
KDChart::GridAttributes::adjustUpperBoundToGrid
bool adjustUpperBoundToGrid() const
Definition: KDChartGridAttributes.cpp:243
KDChart::RulerAttributes::minorTickMarkPen
QPen minorTickMarkPen() const
Definition: KDChartRulerAttributes.cpp:135
KDChart::CartesianAxis::setTitleText
void setTitleText(const QString &text)
Sets the optional text displayed as chart title.
Definition: KDChartCartesianAxis.cpp:428
KDChart::TextLayoutItem::paint
void paint(QPainter *) override
Definition: KDChartLayoutItems.cpp:490
KDChart::Measure::setValue
void setValue(qreal val)
Definition: KDChartMeasure.h:61
KDChart
Definition: KDChartAbstractCartesianDiagram.h:30
KDChart::PaintContext::coordinatePlane
AbstractCoordinatePlane * coordinatePlane() const
Definition: KDChartPaintContext.cpp:78
KDChart::AbstractCoordinatePlane::diagram
AbstractDiagram * diagram()
Definition: KDChartAbstractCoordinatePlane.cpp:130
KDChart::TextLayoutItem::sizeHint
QSize sizeHint() const override
pure virtual in QLayoutItem
Definition: KDChartLayoutItems.cpp:411
KDChart::PaintContext::setPainter
void setPainter(QPainter *painter)
Definition: KDChartPaintContext.cpp:73
KDChart::CartesianAxis::Top
@ Top
Definition: KDChartCartesianAxis.h:52
KDChart::AttributesModel::rowCount
int rowCount(const QModelIndex &) const override
[reimplemented]
Definition: KDChartAttributesModel.cpp:501
KDChart::CartesianAxis::titleText
QString titleText() const
Definition: KDChartCartesianAxis.cpp:435
KDChart::AbstractCoordinatePlane::zoomFactorX
virtual qreal zoomFactorX() const
Definition: KDChartAbstractCoordinatePlane.h:140
KDChartPaintContext.h
KDChart::RulerAttributes::majorTickMarkPen
QPen majorTickMarkPen() const
Definition: KDChartRulerAttributes.cpp:119
KDChart::RulerAttributes::showFirstTick
bool showFirstTick() const
Definition: KDChartRulerAttributes.cpp:250
KDChartPrintingParameters.h
KDChart::CartesianAxis::Left
@ Left
Definition: KDChartCartesianAxis.h:54
KDChart::PaintContext
Stores information about painting diagrams.
Definition: KDChartPaintContext.h:42
KDChart::TextAttributes
A set of text attributes.
Definition: KDChartTextAttributes.h:47
KDChart::CartesianAxis::resetTitleTextAttributes
void resetTitleTextAttributes()
Reset the title text attributes to the built-in default:
Definition: KDChartCartesianAxis.cpp:460
referenceDiagramIsBarDiagram
static bool referenceDiagramIsBarDiagram(const AbstractDiagram *diagram)
Definition: KDChartCartesianAxis.cpp:504
KDChart::Measure
Measure is used to specify relative and absolute sizes in KDChart, e.g.
Definition: KDChartMeasure.h:52
KDChart::CartesianAxis::customTickLength
int customTickLength() const
Returns the length of custom ticks on the axis.
Definition: KDChartCartesianAxis.cpp:1118
KDChart::AbstractCoordinatePlane::parent
Chart * parent()
Definition: KDChartAbstractCoordinatePlane.cpp:198
KDChart::RulerAttributes::minorTickMarkLength
int minorTickMarkLength() const
Definition: KDChartRulerAttributes.cpp:235
KDChart::LineDiagram
LineDiagram defines a common line diagram.
Definition: KDChartLineDiagram.h:45
KDChart::AbstractCartesianDiagram::referenceDiagram
virtual AbstractCartesianDiagram * referenceDiagram() const
Definition: KDChartAbstractCartesianDiagram.cpp:153
KDChart::TextAttributes::isVisible
bool isVisible() const
Definition: KDChartTextAttributes.cpp:123
KDChart::CartesianAxis::setGeometry
void setGeometry(const QRect &r) override
pure virtual in QLayoutItem
Definition: KDChartCartesianAxis.cpp:1094
KDChart::AbstractCoordinatePlane::zoomFactorY
virtual qreal zoomFactorY() const
Definition: KDChartAbstractCoordinatePlane.h:146
KDChart::CartesianAxis::setCustomTicks
void setCustomTicks(const QList< qreal > &ticksPostions)
Sets custom ticks on the axis.
Definition: KDChartCartesianAxis.cpp:1149
KDChart::CartesianAxis::~CartesianAxis
~CartesianAxis() override
Definition: KDChartCartesianAxis.cpp:387
KDChart::PaintContext::painter
QPainter * painter() const
Definition: KDChartPaintContext.cpp:68
KDChart::CartesianAxis::setTitleSize
void setTitleSize(qreal value)
use setTitleTextAttributes() instead
Definition: KDChartCartesianAxis.cpp:600
KDChart::CartesianAxis::paintCtx
void paintCtx(PaintContext *) override
reimpl
Definition: KDChartCartesianAxis.cpp:656
KDChart::CartesianCoordinatePlane::gridAttributes
const GridAttributes gridAttributes(Qt::Orientation orientation) const
Definition: KDChartCartesianCoordinatePlane.cpp:739
KDChart::TextLayoutItem::marginWidth
virtual int marginWidth() const
Definition: KDChartLayoutItems.cpp:447
KDChart::TextLayoutItem::intersects
virtual bool intersects(const TextLayoutItem &other, const QPointF &myPos, const QPointF &otherPos) const
Definition: KDChartLayoutItems.cpp:398
KDChart::AbstractCartesianDiagram
Base class for diagrams based on a cartesian coordianate system.
Definition: KDChartAbstractCartesianDiagram.h:42
KDChart::RulerAttributes
A set of attributes controlling the appearance of axis rulers.
Definition: KDChartRulerAttributes.h:40
KDChartAbstractCartesianDiagram.h
KDChart::RulerAttributes::majorTickMarkLength
int majorTickMarkLength() const
Definition: KDChartRulerAttributes.cpp:219
KDChart::DataDimension::end
qreal end
Definition: KDChartAbstractCoordinatePlane.h:433
KDChart::AttributesModel
A proxy model used for decorating data with attributes.
Definition: KDChartAttributesModel.h:47
KDChart::AbstractDiagram::attributesModel
virtual AttributesModel * attributesModel() const
Returns the AttributesModel, that is used by this diagram.
Definition: KDChartAbstractDiagram.cpp:198
KDChart::TextAttributes::setFontSize
void setFontSize(const Measure &measure)
Set the size of the font used for rendering text.
Definition: KDChartTextAttributes.cpp:140
KDChartLayoutItems.h
KDChart::CartesianAxis::isEmpty
bool isEmpty() const override
pure virtual in QLayoutItem
Definition: KDChartCartesianAxis.cpp:928
KDChart::AbstractCoordinatePlane::diagrams
AbstractDiagramList diagrams()
Definition: KDChartAbstractCoordinatePlane.cpp:140
KDChart::CartesianAxis::Position
Position
Definition: KDChartCartesianAxis.h:50
KDChart::DataDimension::start
qreal start
Definition: KDChartAbstractCoordinatePlane.h:432
KDChart::AbstractDiagram
AbstractDiagram defines the interface for diagram classes.
Definition: KDChartAbstractDiagram.h:51
KDChartBarDiagram.h
KDChart::CartesianAxis::minimumSize
QSize minimumSize() const override
pure virtual in QLayoutItem
Definition: KDChartCartesianAxis.cpp:1082
KDChart::CartesianAxis::sizeHint
QSize sizeHint() const override
pure virtual in QLayoutItem
Definition: KDChartCartesianAxis.cpp:1088
KDChart::LineDiagram::centerDataPoints
bool centerDataPoints() const
Definition: KDChartLineDiagram.cpp:165
KDChart::CartesianAxis::paint
void paint(QPainter *) override
reimpl
Definition: KDChartCartesianAxis.cpp:541
KDChart::GridAttributes::adjustLowerBoundToGrid
bool adjustLowerBoundToGrid() const
Definition: KDChartGridAttributes.cpp:239
KDChart::AbstractArea::areaGeometry
QRect areaGeometry() const override
Definition: KDChartAbstractArea.cpp:151
KDChart::AbstractAxis::customizedLabel
virtual const QString customizedLabel(const QString &label) const
Reimplement this method if you want to adjust axis labels before they are printed.
Definition: KDChartAbstractAxis.cpp:162
KDChart::PaintContext::setCoordinatePlane
void setCoordinatePlane(AbstractCoordinatePlane *plane)
Definition: KDChartPaintContext.cpp:83
KDChart::CartesianAxis::geometry
QRect geometry() const override
pure virtual in QLayoutItem
Definition: KDChartCartesianAxis.cpp:1103
KDChartChart.h
KDChart::AbstractCoordinatePlane::gridDimensionsList
DataDimensionsList gridDimensionsList()
Returns the dimensions used for drawing the grid lines.
Definition: KDChartAbstractCoordinatePlane.cpp:168
allAxisAnnotations
static QMap< qreal, QString > allAxisAnnotations(const AbstractCoordinatePlane *plane, bool isY)
Definition: KDChartCartesianAxis.cpp:145
KDChart::AbstractAxis::diagram
const AbstractDiagram * diagram() const
Definition: KDChartAbstractAxis.cpp:249
KDChart::TextLayoutItem
Layout item showing a text.
Definition: KDChartLayoutItems.h:100
slightlyLessThan
static qreal slightlyLessThan(qreal r)
Definition: KDChartCartesianAxis.cpp:52
KDChart::TextAttributes::fontSize
Measure fontSize() const
Definition: KDChartTextAttributes.cpp:145
KDChart::TextLayoutItem::boundingPolygon
QPolygon boundingPolygon() const
Definition: KDChartLayoutItems.cpp:392
QMap
Definition: KDChartMarkerAttributes.h:35
referenceDiagramNeedsCenteredAbscissaTicks
static bool referenceDiagramNeedsCenteredAbscissaTicks(const AbstractDiagram *diagram)
Definition: KDChartCartesianAxis.cpp:513
KDChart::CartesianAxis::annotations
QMap< qreal, QString > annotations() const
Returns the currently set axis annotations.
Definition: KDChartCartesianAxis.cpp:1129
KDChart::TextAttributes::setRotation
void setRotation(int rotation)
Set the rotation angle to use for the text.
Definition: KDChartTextAttributes.cpp:234
d
#define d
Definition: KDChartCartesianAxis.cpp:50
KDChart::CartesianAxis::isAbscissa
virtual bool isAbscissa() const
Definition: KDChartCartesianAxis.cpp:528
KDChart::PaintContext::setRectangle
void setRectangle(const QRectF &rect)
Definition: KDChartPaintContext.cpp:63
KDChart::AbstractAxis::compare
bool compare(const AbstractAxis *other) const
Returns true if both axes have the same settings.
Definition: KDChartAbstractAxis.cpp:146
KDChart::CartesianAxis::setTitleSpace
void setTitleSpace(qreal value)
Definition: KDChartCartesianAxis.cpp:590
KDChart::CartesianCoordinatePlane
Cartesian coordinate plane.
Definition: KDChartCartesianCoordinatePlane.h:40
QObject
KDChart::CartesianAxis::Right
@ Right
Definition: KDChartCartesianAxis.h:53
KDChart::AbstractAxis::rulerAttributes
RulerAttributes rulerAttributes() const
Returns the attributes to be used for painting the rulers.
Definition: KDChartAbstractAxis.cpp:209
KDChart::CartesianAxis::titleSize
qreal titleSize() const
Definition: KDChartCartesianAxis.cpp:606
KDChart::AbstractCartesianDiagram::axes
virtual KDChart::CartesianAxisList axes() const
Definition: KDChartAbstractCartesianDiagram.cpp:103
KDChart::CartesianAxis::setCachedSizeDirty
void setCachedSizeDirty() const
Definition: KDChartCartesianAxis.cpp:953
KDChart::Measure::value
qreal value() const
Definition: KDChartMeasure.h:62
KDChart::CartesianCoordinatePlane::autoAdjustHorizontalRangeToData
unsigned int autoAdjustHorizontalRangeToData() const
Returns the maximal allowed percent of the horizontal space covered by the coordinate plane that may ...
Definition: KDChartCartesianCoordinatePlane.cpp:710
KDChartStockDiagram.h
KDChartLineDiagram.h
KDChartEnums::MeasureOrientationMinimum
@ MeasureOrientationMinimum
Definition: KDChartEnums.h:293
KDChart::DataDimension
Helper class for one dimension of data, e.g.
Definition: KDChartAbstractCoordinatePlane.h:378
KDChart::BarDiagram
BarDiagram defines a common bar diagram.
Definition: KDChartBarDiagram.h:43
KDChart::CartesianAxis::titleTextAttributes
TextAttributes titleTextAttributes() const
Returns the text attributes that will be used for displaying the title text.
Definition: KDChartCartesianAxis.cpp:448
KDChart::CartesianAxis::Bottom
@ Bottom
Definition: KDChartCartesianAxis.h:51
KDChart::AbstractCoordinatePlane
Base class common for all coordinate planes, CartesianCoordinatePlane, PolarCoordinatePlane,...
Definition: KDChartAbstractCoordinatePlane.h:45
KDChart::AbstractCartesianDiagram::takeAxis
virtual void takeAxis(CartesianAxis *axis)
Removes the axis from the diagram, without deleting it.
Definition: KDChartAbstractCartesianDiagram.cpp:93
KDChart::TextAttributes::rotation
int rotation() const
Definition: KDChartTextAttributes.cpp:240
KDChart::CartesianAxis::customTicks
QList< qreal > customTicks() const
Returns the currently set custom ticks on the axis.
Definition: KDChartCartesianAxis.cpp:1144
KDChart::AbstractAxis::textAttributes
TextAttributes textAttributes() const
Returns the text attributes to be used for axis labels.
Definition: KDChartAbstractAxis.cpp:197

Klarälvdalens Datakonsult AB (KDAB)
"The Qt, C++ and OpenGL Experts"
https://www.kdab.com/

https://www.kdab.com/development-resources/qt-tools/kd-chart/