KD Chart 2  [rev.2.7]
KDChartAbstractDiagram.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 "KDChartAbstractDiagram.h"
24 #include "KDChartAbstractDiagram_p.h"
25 
26 #include <QPainter>
27 #include <QDebug>
28 #include <QApplication>
29 #include <QAbstractProxyModel>
30 #include <QSizeF>
31 
33 #include "KDChartChart.h"
35 #include "KDChartTextAttributes.h"
39 #include "KDChartPainterSaver_p.h"
40 
41 #include <KDABLibFakes>
42 
43 #include <limits>
44 
45 using namespace KDChart;
46 
47 #define d d_func()
48 
50  : QAbstractItemView ( parent ), _d( new Private() )
51 {
52  _d->init( plane );
53  init();
54 }
55 
57 {
58  emit aboutToBeDestroyed();
59  delete _d;
60 }
61 
62 void AbstractDiagram::init()
63 {
64  _d->diagram = this;
65  d->reverseMapper.setDiagram( this );
66 }
67 
68 
69 bool AbstractDiagram::compare( const AbstractDiagram* other ) const
70 {
71  if ( other == this ) return true;
72  if ( !other ) {
73  return false;
74  }
75  return // compare QAbstractScrollArea properties
76  (horizontalScrollBarPolicy() == other->horizontalScrollBarPolicy()) &&
77  (verticalScrollBarPolicy() == other->verticalScrollBarPolicy()) &&
78  // compare QFrame properties
79  (frameShadow() == other->frameShadow()) &&
80  (frameShape() == other->frameShape()) &&
81 // frameWidth is a read-only property defined by the style, it should not be in here:
82  // (frameWidth() == other->frameWidth()) &&
83  (lineWidth() == other->lineWidth()) &&
84  (midLineWidth() == other->midLineWidth()) &&
85  // compare QAbstractItemView properties
86  (alternatingRowColors() == other->alternatingRowColors()) &&
87  (hasAutoScroll() == other->hasAutoScroll()) &&
88 #if QT_VERSION > 0x040199
89  (dragDropMode() == other->dragDropMode()) &&
90  (dragDropOverwriteMode() == other->dragDropOverwriteMode()) &&
91  (horizontalScrollMode() == other->horizontalScrollMode ()) &&
92  (verticalScrollMode() == other->verticalScrollMode()) &&
93 #endif
94  (dragEnabled() == other->dragEnabled()) &&
95  (editTriggers() == other->editTriggers()) &&
96  (iconSize() == other->iconSize()) &&
97  (selectionBehavior() == other->selectionBehavior()) &&
98  (selectionMode() == other->selectionMode()) &&
99  (showDropIndicator() == other->showDropIndicator()) &&
100  (tabKeyNavigation() == other->tabKeyNavigation()) &&
101  (textElideMode() == other->textElideMode()) &&
102  // compare all of the properties stored in the attributes model
103  attributesModel()->compare( other->attributesModel() ) &&
104  // compare own properties
105  (rootIndex().column() == other->rootIndex().column()) &&
106  (rootIndex().row() == other->rootIndex().row()) &&
108  (antiAliasing() == other->antiAliasing()) &&
109  (percentMode() == other->percentMode()) &&
110  (datasetDimension() == other->datasetDimension());
111 }
112 
114 {
115  return d->plane;
116 }
117 
119 {
120  if ( d->databoundariesDirty ) {
121  d->databoundaries = calculateDataBoundaries ();
122  d->databoundariesDirty = false;
123  }
124  return d->databoundaries;
125 }
126 
128 {
129  d->databoundariesDirty = true;
130  update();
131 }
132 
133 void AbstractDiagram::setModel( QAbstractItemModel * newModel )
134 {
135  if ( newModel == model() ) {
136  return;
137  }
138 
139  AttributesModel* amodel = new PrivateAttributesModel( newModel, this );
140  amodel->initFrom( d->attributesModel );
141  d->setAttributesModel(amodel);
142 
143  QAbstractItemView::setModel( newModel );
144 
145  scheduleDelayedItemsLayout();
147  emit modelsChanged();
148 }
149 
150 void AbstractDiagram::setSelectionModel( QItemSelectionModel* newSelectionModel )
151 {
152  if ( selectionModel() )
153  {
154  disconnect( selectionModel(), SIGNAL( currentChanged( QModelIndex, QModelIndex ) ), this, SIGNAL( modelsChanged() ) );
155  disconnect( selectionModel(), SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), this, SIGNAL( modelsChanged() ) );
156  }
157  QAbstractItemView::setSelectionModel( newSelectionModel );
158  if ( selectionModel() )
159  {
160  connect( selectionModel(), SIGNAL( currentChanged( QModelIndex, QModelIndex ) ), this, SIGNAL( modelsChanged() ) );
161  connect( selectionModel(), SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), this, SIGNAL( modelsChanged() ) );
162  }
163  emit modelsChanged();
164 }
165 
174 {
175  if ( amodel->sourceModel() != model() ) {
176  qWarning("KDChart::AbstractDiagram::setAttributesModel() failed: "
177  "Trying to set an attributesmodel which works on a different "
178  "model than the diagram.");
179  return;
180  }
181  if ( qobject_cast<PrivateAttributesModel*>(amodel) ) {
182  qWarning("KDChart::AbstractDiagram::setAttributesModel() failed: "
183  "Trying to set an attributesmodel that is private to another diagram.");
184  return;
185  }
186 
187  d->setAttributesModel( amodel );
188  scheduleDelayedItemsLayout();
190  emit modelsChanged();
191 }
192 
194 {
195  return d->usesExternalAttributesModel();
196 }
197 
199 {
200  return d->attributesModel;
201 }
202 
203 QModelIndex AbstractDiagram::conditionallyMapFromSource( const QModelIndex & index ) const
204 {
205  Q_ASSERT( !index.isValid() || index.model() == attributesModel() || index.model() == attributesModel()->sourceModel() );
206  return index.model() == attributesModel() ? index : attributesModel()->mapFromSource( index );
207 }
208 
210 void AbstractDiagram::setRootIndex ( const QModelIndex& idx )
211 {
212  QAbstractItemView::setRootIndex( idx );
213  setAttributesModelRootIndex( d->attributesModel->mapFromSource( idx ) );
214 }
215 
217 void AbstractDiagram::setAttributesModelRootIndex( const QModelIndex& idx )
218 {
219  d->attributesModelRootIndex = idx;
221  scheduleDelayedItemsLayout();
222 }
223 
227 {
228  if ( !d->attributesModelRootIndex.isValid() )
229  d->attributesModelRootIndex = d->attributesModel->mapFromSource( rootIndex() );
230  return d->attributesModelRootIndex;
231 }
232 
234 {
235  d->plane = parent;
236 }
237 
239 {
240  if ( d->plane ) {
241  d->plane->layoutDiagrams();
242  update();
243  }
244  QAbstractItemView::doItemsLayout();
245 }
246 
247 #if QT_VERSION < 0x050000
248 void AbstractDiagram::dataChanged( const QModelIndex &topLeft,
249  const QModelIndex &bottomRight )
250 #else
251 void AbstractDiagram::dataChanged( const QModelIndex &topLeft,
252  const QModelIndex &bottomRight,
253  const QVector<int> & )
254 #endif
255 {
256  Q_UNUSED( topLeft );
257  Q_UNUSED( bottomRight );
258  // We are still too dumb to do intelligent updates...
260  scheduleDelayedItemsLayout();
261 }
262 
263 
264 void AbstractDiagram::setHidden( const QModelIndex & index, bool hidden )
265 {
266  d->attributesModel->setData(
267  conditionallyMapFromSource( index ),
268  qVariantFromValue( hidden ),
269  DataHiddenRole );
270  emit dataHidden();
271 }
272 
273 void AbstractDiagram::setHidden( int dataset, bool hidden )
274 {
275  d->setDatasetAttrs( dataset, qVariantFromValue( hidden ), DataHiddenRole );
276  emit dataHidden();
277 }
278 
279 void AbstractDiagram::setHidden( bool hidden )
280 {
281  d->attributesModel->setModelData( qVariantFromValue( hidden ), DataHiddenRole );
282  emit dataHidden();
283 }
284 
286 {
287  return attributesModel()->modelData( DataHiddenRole ).value< bool >();
288 }
289 
290 bool AbstractDiagram::isHidden( int dataset ) const
291 {
292  const QVariant boolFlag( d->datasetAttrs( dataset, DataHiddenRole ) );
293  if ( boolFlag.isValid() )
294  return boolFlag.value< bool >();
295  return isHidden();
296 }
297 
298 bool AbstractDiagram::isHidden( const QModelIndex & index ) const
299 {
300  const QVariant boolFlag( attributesModel()->data( conditionallyMapFromSource( index ),
301  DataHiddenRole ) );
302  if ( boolFlag.isValid() ) {
303  return boolFlag.value< bool >();
304  }
305  int dataset = index.column() / d->datasetDimension;
306  return isHidden( dataset );
307 }
308 
309 
310 void AbstractDiagram::setDataValueAttributes( const QModelIndex & index,
311  const DataValueAttributes & a )
312 {
313  d->attributesModel->setData( conditionallyMapFromSource( index ), qVariantFromValue( a ),
315  emit propertiesChanged();
316 }
317 
318 
320 {
321  d->setDatasetAttrs( dataset, qVariantFromValue( a ), DataValueLabelAttributesRole );
322  emit propertiesChanged();
323 }
324 
326 {
328 }
329 
331 {
332  /*
333  The following did not work!
334  (khz, 2008-01-25)
335  If there was some attrs specified for the 0-th cells of a dataset,
336  then this logic would return the cell's settings instead of the header settings:
337 
338  return qVariantValue<DataValueAttributes>(
339  attributesModel()->data( attributesModel()->mapFromSource(columnToIndex( column )),
340  KDChart::DataValueLabelAttributesRole ) );
341  */
342 
343  const QVariant headerAttrs(
344  d->datasetAttrs( dataset, KDChart::DataValueLabelAttributesRole ) );
345  if ( headerAttrs.isValid() )
346  return headerAttrs.value< DataValueAttributes >();
347  return dataValueAttributes();
348 }
349 
351 {
352  return attributesModel()->data(
353  conditionallyMapFromSource( index ),
355 }
356 
358 {
359  d->attributesModel->setModelData( qVariantFromValue( a ), DataValueLabelAttributesRole );
360  emit propertiesChanged();
361 }
362 
364 {
366  attrs.setShowOverlappingDataLabels( allow );
367  setDataValueAttributes( attrs );
368  d->allowOverlappingDataValueTexts = allow;
369  emit propertiesChanged();
370 }
371 
373 {
374  return d->allowOverlappingDataValueTexts;
375 }
376 
378 {
379  d->antiAliasing = enabled;
380  emit propertiesChanged();
381 }
382 
384 {
385  return d->antiAliasing;
386 }
387 
388 void AbstractDiagram::setPercentMode ( bool percent )
389 {
390  d->percent = percent;
391  emit propertiesChanged();
392 }
393 
395 {
396  return d->percent;
397 }
398 
399 
400 void AbstractDiagram::paintDataValueText( QPainter* painter,
401  const QModelIndex& index,
402  const QPointF& pos,
403  qreal value )
404 {
405  d->paintDataValueText( painter, index, pos, value );
406 }
407 
408 
409 void AbstractDiagram::paintDataValueTexts( QPainter* painter )
410 {
411  if ( !checkInvariants() ) {
412  return;
413  }
414 
415  d->forgetAlreadyPaintedDataValues();
416  const int rowCount = model()->rowCount( rootIndex() );
417  const int columnCount = model()->columnCount( rootIndex() );
418  for ( int column = 0; column < columnCount; column += datasetDimension() ) {
419  for ( int row = 0; row < rowCount; ++row ) {
420  QModelIndex index = model()->index( row, column, rootIndex() ); // checked
421  qreal x;
422  qreal y;
423  if ( datasetDimension() == 1 ) {
424  x = row;
425  y = index.data().toReal();
426  } else {
427  x = index.data().toReal();
428  y = model()->index( row, column + 1, rootIndex() ).data().toReal();
429  }
430  paintDataValueText( painter, index, coordinatePlane()->translate( QPointF( x, y ) ), y );
431  }
432  }
433 }
434 
435 
436 void AbstractDiagram::paintMarker( QPainter* painter,
437  const DataValueAttributes& a,
438  const QModelIndex& index,
439  const QPointF& pos )
440 {
441  if ( !checkInvariants() || !a.isVisible() ) return;
442  const MarkerAttributes ma = a.markerAttributes();
443  if ( !ma.isVisible() ) return;
444 
445  const PainterSaver painterSaver( painter );
446  // the size of the marker - unscaled
447  const QSizeF maSize( ma.markerSize().width() / painter->matrix().m11(),
448  ma.markerSize().height() / painter->matrix().m22() );
449  QBrush indexBrush( brush( index ) );
450  QPen indexPen( ma.pen() );
451  if ( ma.markerColor().isValid() )
452  indexBrush.setColor( ma.markerColor() );
453 
454  paintMarker( painter, ma, indexBrush, indexPen, pos, maSize );
455 
456  // workaround: BC cannot be changed, otherwise we would pass the
457  // index down to next-lower paintMarker function. So far, we
458  // basically save a circle of radius maSize at pos in the
459  // reverseMapper. This means that ^^^ this version of paintMarker
460  // needs to be called to reverse-map the marker.
461  d->reverseMapper.addCircle( index.row(), index.column(), pos, 2 * maSize );
462 }
463 
464 void AbstractDiagram::paintMarker( QPainter* painter,
465  const QModelIndex& index,
466  const QPointF& pos )
467 {
468  if ( !checkInvariants() ) return;
469  paintMarker( painter, dataValueAttributes( index ), index, pos );
470 }
471 
472 void AbstractDiagram::paintMarker( QPainter* painter,
473  const MarkerAttributes& markerAttributes,
474  const QBrush& brush,
475  const QPen& pen,
476  const QPointF& pos,
477  const QSizeF& maSize )
478 {
479  const QPen oldPen( painter->pen() );
480  // Pen is used to paint 4Pixels - 1 Pixel - Ring and FastCross types.
481  // make sure to use the brush color - see above in those cases.
482  const bool isFourPixels = (markerAttributes.markerStyle() == MarkerAttributes::Marker4Pixels);
483  if ( isFourPixels || (markerAttributes.markerStyle() == MarkerAttributes::Marker1Pixel) ) {
484  // for high-performance point charts with tiny point markers:
485  painter->setPen( PrintingParameters::scalePen( QPen( brush.color().light() ) ) );
486  if ( isFourPixels ) {
487  const qreal x = pos.x();
488  const qreal y = pos.y();
489  painter->drawLine( QPointF(x-1.0,y-1.0),
490  QPointF(x+1.0,y-1.0) );
491  painter->drawLine( QPointF(x-1.0,y),
492  QPointF(x+1.0,y) );
493  painter->drawLine( QPointF(x-1.0,y+1.0),
494  QPointF(x+1.0,y+1.0) );
495  }
496  painter->drawPoint( pos );
497  } else {
498  const PainterSaver painterSaver( painter );
499  QPen painterPen( pen );
500  painter->setPen( PrintingParameters::scalePen( painterPen ) );
501  painter->setBrush( brush );
502  painter->setRenderHint ( QPainter::Antialiasing );
503  painter->translate( pos );
504  switch ( markerAttributes.markerStyle() ) {
506  {
507  if ( markerAttributes.threeD() ) {
508  QRadialGradient grad;
509  grad.setCoordinateMode( QGradient::ObjectBoundingMode );
510  QColor drawColor = brush.color();
511  grad.setCenter( 0.5, 0.5 );
512  grad.setRadius( 1.0 );
513  grad.setFocalPoint( 0.35, 0.35 );
514  grad.setColorAt( 0.00, drawColor.lighter( 150 ) );
515  grad.setColorAt( 0.20, drawColor );
516  grad.setColorAt( 0.50, drawColor.darker( 150 ) );
517  grad.setColorAt( 0.75, drawColor.darker( 200 ) );
518  grad.setColorAt( 0.95, drawColor.darker( 250 ) );
519  grad.setColorAt( 1.00, drawColor.darker( 200 ) );
520  QBrush newBrush( grad );
521  newBrush.setMatrix( brush.matrix() );
522  painter->setBrush( newBrush );
523  }
524  painter->drawEllipse( QRectF( 0 - maSize.height()/2, 0 - maSize.width()/2,
525  maSize.height(), maSize.width()) );
526  }
527  break;
529  {
530  QRectF rect( 0 - maSize.width()/2, 0 - maSize.height()/2,
531  maSize.width(), maSize.height() );
532  painter->drawRect( rect );
533  break;
534  }
536  {
537  QVector <QPointF > diamondPoints;
538  QPointF top, left, bottom, right;
539  top = QPointF( 0, 0 - maSize.height()/2 );
540  left = QPointF( 0 - maSize.width()/2, 0 );
541  bottom = QPointF( 0, maSize.height()/2 );
542  right = QPointF( maSize.width()/2, 0 );
543  diamondPoints << top << left << bottom << right;
544  painter->drawPolygon( diamondPoints );
545  break;
546  }
547  // both handled on top of the method:
550  break;
552  {
553  painter->setBrush( Qt::NoBrush );
554  painter->setPen( PrintingParameters::scalePen( QPen( brush.color() ) ) );
555  painter->drawEllipse( QRectF( 0 - maSize.height()/2, 0 - maSize.width()/2,
556  maSize.height(), maSize.width()) );
557  break;
558  }
560  {
561  // Note: Markers can have outline,
562  // so just drawing two rects is NOT the solution here!
563  const qreal w02 = maSize.width() * 0.2;
564  const qreal w05 = maSize.width() * 0.5;
565  const qreal h02 = maSize.height()* 0.2;
566  const qreal h05 = maSize.height()* 0.5;
567  QVector <QPointF > crossPoints;
568  QPointF p[12];
569  p[ 0] = QPointF( -w02, -h05 );
570  p[ 1] = QPointF( w02, -h05 );
571  p[ 2] = QPointF( w02, -h02 );
572  p[ 3] = QPointF( w05, -h02 );
573  p[ 4] = QPointF( w05, h02 );
574  p[ 5] = QPointF( w02, h02 );
575  p[ 6] = QPointF( w02, h05 );
576  p[ 7] = QPointF( -w02, h05 );
577  p[ 8] = QPointF( -w02, h02 );
578  p[ 9] = QPointF( -w05, h02 );
579  p[10] = QPointF( -w05, -h02 );
580  p[11] = QPointF( -w02, -h02 );
581  for ( int i=0; i<12; ++i )
582  crossPoints << p[i];
583  crossPoints << p[0];
584  painter->drawPolygon( crossPoints );
585  break;
586  }
588  {
589  QPointF left, right, top, bottom;
590  left = QPointF( -maSize.width()/2, 0 );
591  right = QPointF( maSize.width()/2, 0 );
592  top = QPointF( 0, -maSize.height()/2 );
593  bottom= QPointF( 0, maSize.height()/2 );
594  painter->setPen( PrintingParameters::scalePen( QPen( brush.color() ) ) );
595  painter->drawLine( left, right );
596  painter->drawLine( top, bottom );
597  break;
598  }
600  break;
602  {
603  QPainterPath path = markerAttributes.customMarkerPath();
604  const QRectF pathBoundingRect = path.boundingRect();
605  const qreal xScaling = maSize.height() / pathBoundingRect.height();
606  const qreal yScaling = maSize.width() / pathBoundingRect.width();
607  const qreal scaling = qMin( xScaling, yScaling );
608  painter->scale( scaling, scaling );
609  painter->setPen( PrintingParameters::scalePen( QPen( brush.color() ) ) );
610  painter->drawPath(path);
611  break;
612  }
613  default:
614  Q_ASSERT_X ( false, "paintMarkers()",
615  "Type item does not match a defined Marker Type." );
616  }
617  }
618  painter->setPen( oldPen );
619 }
620 
621 void AbstractDiagram::paintMarkers( QPainter* painter )
622 {
623  if ( !checkInvariants() ) {
624  return;
625  }
626 
627  const int rowCount = model()->rowCount( rootIndex() );
628  const int columnCount = model()->columnCount( rootIndex() );
629  for ( int column = 0; column < columnCount; column += datasetDimension() ) {
630  for ( int row = 0; row < rowCount; ++row ) {
631  QModelIndex index = model()->index( row, column, rootIndex() ); // checked
632  qreal x;
633  qreal y;
634  if ( datasetDimension() == 1 ) {
635  x = row;
636  y = index.data().toReal();
637  } else {
638  x = index.data().toReal();
639  y = model()->index( row, column + 1, rootIndex() ).data().toReal();
640  }
641  paintMarker( painter, index, coordinatePlane()->translate( QPointF( x, y ) ) );
642  }
643  }
644 }
645 
646 
647 void AbstractDiagram::setPen( const QModelIndex& index, const QPen& pen )
648 {
650  conditionallyMapFromSource( index ),
651  qVariantFromValue( pen ), DatasetPenRole );
652  emit propertiesChanged();
653 }
654 
655 void AbstractDiagram::setPen( const QPen& pen )
656 {
658  qVariantFromValue( pen ), DatasetPenRole );
659  emit propertiesChanged();
660 }
661 
662 void AbstractDiagram::setPen( int dataset, const QPen& pen )
663 {
664  d->setDatasetAttrs( dataset, qVariantFromValue( pen ), DatasetPenRole );
665  emit propertiesChanged();
666 }
667 
669 {
670  return attributesModel()->data( DatasetPenRole ).value< QPen >();
671 }
672 
673 QPen AbstractDiagram::pen( int dataset ) const
674 {
675  const QVariant penSettings( d->datasetAttrs( dataset, DatasetPenRole ) );
676  if ( penSettings.isValid() )
677  return penSettings.value< QPen >();
678  return pen();
679 }
680 
681 QPen AbstractDiagram::pen( const QModelIndex& index ) const
682 {
683  return attributesModel()->data(
684  conditionallyMapFromSource( index ),
685  DatasetPenRole ).value< QPen >();
686 }
687 
688 void AbstractDiagram::setBrush( const QModelIndex& index, const QBrush& brush )
689 {
691  conditionallyMapFromSource( index ),
692  qVariantFromValue( brush ), DatasetBrushRole );
693  emit propertiesChanged();
694 }
695 
696 void AbstractDiagram::setBrush( const QBrush& brush )
697 {
699  qVariantFromValue( brush ), DatasetBrushRole );
700  emit propertiesChanged();
701 }
702 
703 void AbstractDiagram::setBrush( int dataset, const QBrush& brush )
704 {
705  d->setDatasetAttrs( dataset, qVariantFromValue( brush ), DatasetBrushRole );
706  emit propertiesChanged();
707 }
708 
710 {
711  return attributesModel()->data( DatasetBrushRole ).value< QBrush >();
712 }
713 
714 QBrush AbstractDiagram::brush( int dataset ) const
715 {
716  const QVariant brushSettings( d->datasetAttrs( dataset, DatasetBrushRole ) );
717  if ( brushSettings.isValid() )
718  return brushSettings.value< QBrush >();
719  return brush();
720 }
721 
722 QBrush AbstractDiagram::brush( const QModelIndex& index ) const
723 {
724  return
725  attributesModel()->data( conditionallyMapFromSource( index ), DatasetBrushRole ).value< QBrush >();
726 }
727 
734 void AbstractDiagram::setUnitPrefix( const QString& prefix, int column, Qt::Orientation orientation )
735 {
736  d->unitPrefixMap[ column ][ orientation ]= prefix;
737 }
738 
744 void AbstractDiagram::setUnitPrefix( const QString& prefix, Qt::Orientation orientation )
745 {
746  d->unitPrefix[ orientation ] = prefix;
747 }
748 
755 void AbstractDiagram::setUnitSuffix( const QString& suffix, int column, Qt::Orientation orientation )
756 {
757  d->unitSuffixMap[ column ][ orientation ]= suffix;
758 }
759 
765 void AbstractDiagram::setUnitSuffix( const QString& suffix, Qt::Orientation orientation )
766 {
767  d->unitSuffix[ orientation ] = suffix;
768 }
769 
777 QString AbstractDiagram::unitPrefix( int column, Qt::Orientation orientation, bool fallback ) const
778 {
779  if ( !fallback || d->unitPrefixMap[ column ].contains( orientation ) )
780  return d->unitPrefixMap[ column ][ orientation ];
781  return d->unitPrefix[ orientation ];
782 }
783 
788 QString AbstractDiagram::unitPrefix( Qt::Orientation orientation ) const
789 {
790  return d->unitPrefix[ orientation ];
791 }
792 
800 QString AbstractDiagram::unitSuffix( int column, Qt::Orientation orientation, bool fallback ) const
801 {
802  if ( !fallback || d->unitSuffixMap[ column ].contains( orientation ) )
803  return d->unitSuffixMap[ column ][ orientation ];
804  return d->unitSuffix[ orientation ];
805 }
806 
811 QString AbstractDiagram::unitSuffix( Qt::Orientation orientation ) const
812 {
813  return d->unitSuffix[ orientation ];
814 }
815 
816 // implement QAbstractItemView:
817 QRect AbstractDiagram::visualRect( const QModelIndex &index ) const
818 {
819  return d->reverseMapper.boundingRect( index.row(), index.column() ).toRect();
820 }
821 
822 void AbstractDiagram::scrollTo(const QModelIndex &, ScrollHint )
823 {}
824 
825 // indexAt ... down below
826 
827 QModelIndex AbstractDiagram::moveCursor(CursorAction, Qt::KeyboardModifiers )
828 { return QModelIndex(); }
829 
831 { return 0; }
832 
834 { return 0; }
835 
836 bool AbstractDiagram::isIndexHidden(const QModelIndex &) const
837 { return true; }
838 
839 void AbstractDiagram::setSelection(const QRect& rect , QItemSelectionModel::SelectionFlags command )
840 {
841  const QModelIndexList indexes = d->indexesIn( rect );
842  QItemSelection selection;
843  KDAB_FOREACH( const QModelIndex& index, indexes )
844  {
845  selection.append( QItemSelectionRange( index ) );
846  }
847  selectionModel()->select( selection, command );
848 }
849 
850 QRegion AbstractDiagram::visualRegionForSelection(const QItemSelection &selection) const
851 {
852  QPolygonF polygon;
853  KDAB_FOREACH( const QModelIndex& index, selection.indexes() )
854  {
855  polygon << d->reverseMapper.polygon(index.row(), index.column());
856  }
857  return polygon.isEmpty() ? QRegion() : QRegion( polygon.toPolygon() );
858 }
859 
860 QRegion AbstractDiagram::visualRegion(const QModelIndex &index) const
861 {
862  QPolygonF polygon = d->reverseMapper.polygon(index.row(), index.column());
863  return polygon.isEmpty() ? QRegion() : QRegion( polygon.toPolygon() );
864 }
865 
867 {
868  d->attributesModel->setPaletteType( AttributesModel::PaletteTypeDefault );
869 }
870 
872 {
873  d->attributesModel->setPaletteType( AttributesModel::PaletteTypeSubdued );
874 }
875 
877 {
878  d->attributesModel->setPaletteType( AttributesModel::PaletteTypeRainbow );
879 }
880 
882 {
883  QStringList ret;
884  if ( model() ) {
885  //qDebug() << "AbstractDiagram::itemRowLabels(): " << attributesModel()->rowCount(attributesModelRootIndex()) << "entries";
886  const int rowCount = attributesModel()->rowCount(attributesModelRootIndex());
887  for ( int i = 0; i < rowCount; ++i ) {
888  //qDebug() << "item row label: " << attributesModel()->headerData( i, Qt::Vertical, Qt::DisplayRole ).toString();
889  ret << unitPrefix( i, Qt::Horizontal, true ) +
890  attributesModel()->headerData( i, Qt::Vertical, Qt::DisplayRole ).toString() +
891  unitSuffix( i, Qt::Horizontal, true );
892  }
893  }
894  return ret;
895 }
896 
898 {
899  QStringList ret;
900  if ( !model() ) {
901  return ret;
902  }
903  const int datasetCount = d->datasetCount();
904  for ( int i = 0; i < datasetCount; ++i ) {
905  ret << d->datasetAttrs( i, Qt::DisplayRole ).toString();
906  }
907  return ret;
908 }
909 
911 {
912  QList<QBrush> ret;
913  if ( !model() ) {
914  return ret;
915  }
916  const int datasetCount = d->datasetCount();
917  for ( int i = 0; i < datasetCount; ++i ) {
918  ret << brush( i );
919  }
920  return ret;
921 }
922 
924 {
925  QList<QPen> ret;
926  if ( !model() ) {
927  return ret;
928  }
929  const int datasetCount = d->datasetCount();
930  for ( int i = 0; i < datasetCount; ++i ) {
931  ret << pen( i );
932  }
933  return ret;
934 }
935 
937 {
939  if ( !model() ) {
940  return ret;
941  }
942  const int datasetCount = d->datasetCount();
943  for ( int i = 0; i < datasetCount; ++i ) {
945  }
946  return ret;
947 }
948 
949 bool AbstractDiagram::checkInvariants( bool justReturnTheStatus ) const
950 {
951  if ( ! justReturnTheStatus ) {
952  Q_ASSERT_X ( model(), "AbstractDiagram::checkInvariants()",
953  "There is no usable model set, for the diagram." );
954 
955  Q_ASSERT_X ( coordinatePlane(), "AbstractDiagram::checkInvariants()",
956  "There is no usable coordinate plane set, for the diagram." );
957  }
958  return model() && coordinatePlane();
959 }
960 
962 {
963  return d->datasetDimension;
964 }
965 
967 {
968  Q_UNUSED( dimension );
969  qDebug() << "Setting the dataset dimension using AbstractDiagram::setDatasetDimension is "
970  "obsolete. Use the specific diagram types instead.";
971 }
972 
974 {
975  Q_ASSERT( dimension != 0 );
976  if ( d->datasetDimension == dimension ) {
977  return;
978  }
979  d->datasetDimension = dimension;
980  d->attributesModel->setDatasetDimension( dimension );
982  emit layoutChanged( this );
983 }
984 
985 qreal AbstractDiagram::valueForCell( int row, int column ) const
986 {
987  if ( !d->attributesModel->hasIndex( row, column, attributesModelRootIndex() ) ) {
988  qWarning() << "AbstractDiagram::valueForCell(): Requesting value for invalid index!";
989  return std::numeric_limits<qreal>::quiet_NaN();
990  }
991  return d->attributesModel->data(
992  d->attributesModel->index( row, column, attributesModelRootIndex() ) ).toReal(); // checked
993 }
994 
996 {
997  if ( d->plane ) {
998  d->plane->update();
999  }
1000 }
1001 
1002 QModelIndex AbstractDiagram::indexAt( const QPoint& point ) const
1003 {
1004  return d->indexAt( point );
1005 }
1006 
1007 QModelIndexList AbstractDiagram::indexesAt( const QPoint& point ) const
1008 {
1009  return d->indexesAt( point );
1010 }
1011 
1012 QModelIndexList AbstractDiagram::indexesIn( const QRect& rect ) const
1013 {
1014  return d->indexesIn( rect );
1015 }
void useRainbowColors()
Set the palette to be used, for painting datasets to the rainbow palette.
DataValueAttributes dataValueAttributes() const
Retrieve the DataValueAttributes specified globally.
Base class common for all coordinate planes, CartesianCoordinatePlane, PolarCoordinatePlane, TernaryCoordinatePlane.
QModelIndexList indexesAt(const QPoint &point) const
This method is added alongside with indexAt from QAIM, since in kdchart multiple indexes can be displ...
void dataHidden()
This signal is emitted, when the hidden status of at least one data cell was (un)set.
void useDefaultColors()
Set the palette to be used, for painting datasets to the default palette.
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::DisplayRole) override
[reimplemented]
MarkerAttributes markerAttributes() const
virtual bool checkInvariants(bool justReturnTheStatus=false) const
Diagram attributes dealing with data value labels.
int rowCount(const QModelIndex &) const override
[reimplemented]
void setSelectionModel(QItemSelectionModel *selectionModel) override
Associate a seleection model with the diagrom.
virtual bool usesExternalAttributesModel() const
Returns whether the diagram is using its own built-in attributes model or an attributes model that wa...
Declaring the class KDChart::DataValueAttributes.
QRegion visualRegionForSelection(const QItemSelection &selection) const override
[reimplemented]
void useSubduedColors()
Set the palette to be used, for painting datasets to the subdued palette.
void setPercentMode(bool percent)
Deprecated method that turns the percent mode of this diagram on or off.
void setHidden(const QModelIndex &index, bool hidden)
Hide (or unhide, resp.) a data cell.
QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override
qreal valueForCell(int row, int column) const
Helper method, retrieving the data value (DisplayRole) for a given row and column.
virtual AttributesModel * attributesModel() const
Returns the AttributesModel, that is used by this diagram.
AbstractCoordinatePlane * coordinatePlane() const
The coordinate plane associated with the diagram.
QStringList datasetLabels() const
The set of dataset labels currently displayed, for use in legends, etc.
void scrollTo(const QModelIndex &index, ScrollHint hint=EnsureVisible) override
[reimplemented]
QVariant modelData(int role) const
void setDatasetDimensionInternal(int dimension)
void setBrush(const QModelIndex &index, const QBrush &brush)
Set the brush to be used, for painting the datapoint at the given index.
QString unitSuffix(int column, Qt::Orientation orientation, bool fallback=false) const
Retrieves the axis unit suffix for a specific column.
QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const override
[reimplemented]
void setAllowOverlappingDataValueTexts(bool allow)
Set whether data value labels are allowed to overlap.
void setRootIndex(const QModelIndex &idx) override
Set the root index in the model, where the diagram starts referencing data for display.
QString unitPrefix(int column, Qt::Orientation orientation, bool fallback=false) const
Retrieves the axis unit prefix for a specific column.
AbstractDiagram defines the interface for diagram classes.
QVariant data(int role) const
Returns the data that were specified at global level, or the default data, or QVariant().
QBrush brush() const
Retrieve the brush to be used for painting datapoints globally.
QPainterPath customMarkerPath() const
void setPen(const QModelIndex &index, const QPen &pen)
Set the pen to be used, for painting the datapoint at the given index.
void setDatasetDimension(int dimension)
const QPair< QPointF, QPointF > dataBoundaries() const
Return the bottom left and top right data point, that the diagram will display (unless the grid adjus...
void aboutToBeDestroyed()
This signal is emitted when this diagram is being destroyed, but before all the data, i.e.
void setAntiAliasing(bool enabled)
Set whether anti-aliasing is to be used while rendering this diagram.
Class only listed here to document inheritance of some KDChart classes.
virtual void paintMarker(QPainter *painter, const MarkerAttributes &markerAttributes, const QBrush &brush, const QPen &, const QPointF &point, const QSizeF &size)
bool isIndexHidden(const QModelIndex &index) const override
[reimplemented]
bool compare(const AttributesModel *other) const
Returns true if both, all of the attributes set, and the palette set is equal in both of the Attribut...
QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) override
[reimplemented]
void propertiesChanged()
Emitted upon change of a property of the Diagram.
bool percentMode() const
Returns whether this diagram is drawn in percent mode.
A proxy model used for decorating data with attributes.
QList< QBrush > datasetBrushes() const
The set of dataset brushes currently used, for use in legends, etc.
#define d
virtual void setAttributesModel(AttributesModel *model)
Associate an AttributesModel with this diagram.
virtual void setCoordinatePlane(AbstractCoordinatePlane *plane)
Set the coordinate plane associated with the diagram.
virtual void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
[reimplemented]
QList< QPen > datasetPens() const
The set of dataset pens currently used, for use in legends, etc.
void setAttributesModelRootIndex(const QModelIndex &)
void setModel(QAbstractItemModel *model) override
Associate a model with the diagram.
int horizontalOffset() const override
[reimplemented]
void doItemsLayout() override
[reimplemented]
void setUnitSuffix(const QString &suffix, int column, Qt::Orientation orientation)
Set the unit prefix to be used on axes for one specific column.
int verticalOffset() const override
[reimplemented]
virtual void paintMarkers(QPainter *painter)
QPointF translate(const TernaryPoint &point)
void setUnitPrefix(const QString &prefix, int column, Qt::Orientation orientation)
Set the unit prefix to be used on axes for one specific column.
bool compare(const AbstractDiagram *other) const
Returns true if both diagrams have the same settings.
void paintDataValueText(QPainter *painter, const QModelIndex &index, const QPointF &pos, qreal value)
A set of attributes controlling the appearance of data set markers.
QPen pen() const
Retrieve the pen to be used for painting datapoints globally.
void setShowOverlappingDataLabels(bool showOverlappingDataLabels)
Set whether data value texts overlapping other data value texts of the same diagram should be drawn...
void setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command) override
[reimplemented]
void layoutChanged(AbstractDiagram *)
Diagrams are supposed to emit this signal, when the layout of one of their element changes...
QRect visualRect(const QModelIndex &index) const override
[reimplemented]
QStringList itemRowLabels() const
The set of item row labels currently displayed, for use in Abscissa axes, etc.
QModelIndex attributesModelRootIndex() const
virtual QRegion visualRegion(const QModelIndex &index) const
void initFrom(const AttributesModel *other)
Copies the internal data (maps and palette) of another AttributesModel* into this one...
virtual void paintDataValueTexts(QPainter *painter)
static QPen scalePen(const QPen &pen)
bool isHidden() const
Retrieve the hidden status specified globally.
QList< MarkerAttributes > datasetMarkers() const
The set of dataset markers currently used, for use in legends, etc.
virtual const QPair< QPointF, QPointF > calculateDataBoundaries() const =0
QModelIndexList indexesIn(const QRect &rect) const
Class only listed here to document inheritance of some KDChart classes.
bool setModelData(const QVariant value, int role)
void setDataValueAttributes(const QModelIndex &index, const DataValueAttributes &a)
Set the DataValueAttributes for the given index.
int datasetDimension() const
The dataset dimension of a diagram determines how many value dimensions it expects each datapoint to ...
AbstractDiagram(Private *p, QWidget *parent, AbstractCoordinatePlane *plane)
QModelIndex indexAt(const QPoint &point) const override
[reimplemented]
Internally used class just adding a special constructor used by AbstractDiagram.
void modelsChanged()
This signal is emitted when either the model or the AttributesModel is replaced.

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/