KChart

KChartAbstractDiagram.cpp
1/*
2 * SPDX-FileCopyrightText: 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved.
3 *
4 * This file is part of the KD Chart library.
5 *
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 */
8
9#include "KChartAbstractDiagram.h"
10#include "KChartAbstractDiagram_p.h"
11
12#include <QPainter>
13#include <QPainterPath>
14#include <QDebug>
15#include <QApplication>
16#include <QAbstractProxyModel>
17#include <QSizeF>
18#include <QPainterPath>
19
20#include "KChartAbstractCoordinatePlane.h"
21#include "KChartChart.h"
23#include "KChartTextAttributes.h"
24#include "KChartAbstractThreeDAttributes.h"
25#include "KChartThreeDLineAttributes.h"
26#include "KChartPainterSaver_p.h"
27
28#include <limits>
29
30using namespace KChart;
31
32#define d d_func()
33
34AbstractDiagram::AbstractDiagram ( QWidget* parent, AbstractCoordinatePlane* plane )
35 : QAbstractItemView ( parent ), _d( new Private() )
36{
37 _d->init( plane );
38 init();
39}
40
41AbstractDiagram::~AbstractDiagram()
42{
44 delete _d;
45}
46
47void AbstractDiagram::init()
48{
49 _d->diagram = this;
50 d->reverseMapper.setDiagram( this );
51}
52
53
54bool AbstractDiagram::compare( const AbstractDiagram* other ) const
55{
56 if ( other == this ) return true;
57 if ( !other ) {
58 return false;
59 }
60 return // compare QAbstractScrollArea properties
63 // compare QFrame properties
64 (frameShadow() == other->frameShadow()) &&
65 (frameShape() == other->frameShape()) &&
66// frameWidth is a read-only property defined by the style, it should not be in here:
67 // (frameWidth() == other->frameWidth()) &&
68 (lineWidth() == other->lineWidth()) &&
69 (midLineWidth() == other->midLineWidth()) &&
70 // compare QAbstractItemView properties
72 (hasAutoScroll() == other->hasAutoScroll()) &&
73 (dragDropMode() == other->dragDropMode()) &&
76 (verticalScrollMode() == other->verticalScrollMode()) &&
77 (dragEnabled() == other->dragEnabled()) &&
78 (editTriggers() == other->editTriggers()) &&
79 (iconSize() == other->iconSize()) &&
80 (selectionBehavior() == other->selectionBehavior()) &&
81 (selectionMode() == other->selectionMode()) &&
82 (showDropIndicator() == other->showDropIndicator()) &&
83 (tabKeyNavigation() == other->tabKeyNavigation()) &&
84 (textElideMode() == other->textElideMode()) &&
85 // compare all of the properties stored in the attributes model
87 // compare own properties
88 (rootIndex().column() == other->rootIndex().column()) &&
89 (rootIndex().row() == other->rootIndex().row()) &&
91 (antiAliasing() == other->antiAliasing()) &&
92 (percentMode() == other->percentMode()) &&
93 (datasetDimension() == other->datasetDimension());
94}
95
97{
98 return d->plane;
99}
100
101const QPair<QPointF, QPointF> AbstractDiagram::dataBoundaries () const
102{
103 if ( d->databoundariesDirty ) {
104 d->databoundaries = calculateDataBoundaries ();
105 d->databoundariesDirty = false;
106 }
107 return d->databoundaries;
108}
109
110void AbstractDiagram::setDataBoundariesDirty() const
111{
112 d->databoundariesDirty = true;
113 update();
114}
115
117{
118 d->diagramSize = size;
120}
121
123{
124 if ( newModel == model() ) {
125 return;
126 }
127
128 AttributesModel* amodel = new PrivateAttributesModel( newModel, this );
129 amodel->initFrom( d->attributesModel );
130 d->setAttributesModel(amodel);
131
132 QAbstractItemView::setModel( newModel );
133
135 setDataBoundariesDirty();
137}
138
140{
141 if ( selectionModel() )
142 {
145 }
146 QAbstractItemView::setSelectionModel( newSelectionModel );
147 if ( selectionModel() )
148 {
151 }
153}
154
155/* Sets an external AttributesModel on this diagram. By default, a diagram has it's
156 own internal set of attributes, but an external one can be set. This can be used to
157 share attributes between several diagrams. The diagram does not take ownership of the
158 attributesmodel.
159
160 @param amodel The AttributesModel to use for this diagram.
161*/
163{
164 if ( amodel->sourceModel() != model() ) {
165 qWarning("KChart::AbstractDiagram::setAttributesModel() failed: "
166 "Trying to set an attributesmodel which works on a different "
167 "model than the diagram.");
168 return;
169 }
171 qWarning("KChart::AbstractDiagram::setAttributesModel() failed: "
172 "Trying to set an attributesmodel that is private to another diagram.");
173 return;
174 }
175
176 d->setAttributesModel( amodel );
178 setDataBoundariesDirty();
180}
181
183{
184 return d->usesExternalAttributesModel();
185}
186
188{
189 return d->attributesModel;
190}
191
192QModelIndex AbstractDiagram::conditionallyMapFromSource( const QModelIndex & index ) const
193{
194 Q_ASSERT( !index.isValid() || index.model() == attributesModel() || index.model() == attributesModel()->sourceModel() );
195 return index.model() == attributesModel() ? index : attributesModel()->mapFromSource( index );
196}
197
198/* \reimpl */
200{
202 setAttributesModelRootIndex( d->attributesModel->mapFromSource( idx ) );
203}
204
206{
207 d->attributesModelRootIndex = idx;
208 setDataBoundariesDirty();
210}
211
213{
214 if ( !d->attributesModelRootIndex.isValid() )
215 d->attributesModelRootIndex = d->attributesModel->mapFromSource( rootIndex() );
216 return d->attributesModelRootIndex;
217}
218
223
225{
226 if ( d->plane ) {
227 d->plane->layoutDiagrams();
228 update();
229 }
230 QAbstractItemView::doItemsLayout();
231}
232
234 const QModelIndex &bottomRight,
235 const QVector<int> & )
236{
237 Q_UNUSED( topLeft );
238 Q_UNUSED( bottomRight );
239 // We are still too dumb to do intelligent updates...
240 setDataBoundariesDirty();
242}
243
244
245void AbstractDiagram::setHidden( const QModelIndex & index, bool hidden )
246{
247 d->attributesModel->setData(
248 conditionallyMapFromSource( index ),
249 QVariant::fromValue( hidden ),
250 DataHiddenRole );
252}
253
254void AbstractDiagram::setHidden( int dataset, bool hidden )
255{
256 d->setDatasetAttrs( dataset, QVariant::fromValue( hidden ), DataHiddenRole );
258}
259
260void AbstractDiagram::setHidden( bool hidden )
261{
262 d->attributesModel->setModelData( QVariant::fromValue( hidden ), DataHiddenRole );
264}
265
267{
268 return attributesModel()->modelData( DataHiddenRole ).value< bool >();
269}
270
271bool AbstractDiagram::isHidden( int dataset ) const
272{
273 const QVariant boolFlag( d->datasetAttrs( dataset, DataHiddenRole ) );
274 if ( boolFlag.isValid() )
275 return boolFlag.value< bool >();
276 return isHidden();
277}
278
279bool AbstractDiagram::isHidden( const QModelIndex & index ) const
280{
281 const QVariant boolFlag( attributesModel()->data( conditionallyMapFromSource( index ),
282 DataHiddenRole ) );
283 if ( boolFlag.isValid() ) {
284 return boolFlag.value< bool >();
285 }
286 int dataset = index.column() / d->datasetDimension;
287 return isHidden( dataset );
288}
289
290
292 const DataValueAttributes & a )
293{
294 d->attributesModel->setData( conditionallyMapFromSource( index ), QVariant::fromValue( a ),
295 DataValueLabelAttributesRole );
297}
298
299
301{
302 d->setDatasetAttrs( dataset, QVariant::fromValue( a ), DataValueLabelAttributesRole );
304}
305
307{
308 return attributesModel()->modelData( KChart::DataValueLabelAttributesRole ).value< DataValueAttributes >();
309}
310
312{
313 /*
314 The following did not work!
315 (khz, 2008-01-25)
316 If there was some attrs specified for the 0-th cells of a dataset,
317 then this logic would return the cell's settings instead of the header settings:
318
319 return qVariantValue<DataValueAttributes>(
320 attributesModel()->data( attributesModel()->mapFromSource(columnToIndex( column )),
321 KChart::DataValueLabelAttributesRole ) );
322 */
323
324 const QVariant headerAttrs(
325 d->datasetAttrs( dataset, KChart::DataValueLabelAttributesRole ) );
326 if ( headerAttrs.isValid() )
327 return headerAttrs.value< DataValueAttributes >();
328 return dataValueAttributes();
329}
330
332{
333 return attributesModel()->data(
334 conditionallyMapFromSource( index ),
335 KChart::DataValueLabelAttributesRole ).value< DataValueAttributes >();
336}
337
339{
340 d->attributesModel->setModelData( QVariant::fromValue( a ), DataValueLabelAttributesRole );
342}
343
345{
347 attrs.setShowOverlappingDataLabels( allow );
348 setDataValueAttributes( attrs );
349 d->allowOverlappingDataValueTexts = allow;
351}
352
354{
355 return d->allowOverlappingDataValueTexts;
356}
357
359{
360 d->antiAliasing = enabled;
362}
363
365{
366 return d->antiAliasing;
367}
368
370{
371 d->percent = percent;
373}
374
376{
377 return d->percent;
378}
379
380
381void AbstractDiagram::paintDataValueText( QPainter* painter,
382 const QModelIndex& index,
383 const QPointF& pos,
384 qreal value )
385{
386 d->paintDataValueText( painter, index, pos, value );
387}
388
389
391{
392 if ( !checkInvariants() ) {
393 return;
394 }
395
396 d->forgetAlreadyPaintedDataValues();
397 const int rowCount = model()->rowCount( rootIndex() );
398 const int columnCount = model()->columnCount( rootIndex() );
399 for ( int column = 0; column < columnCount; column += datasetDimension() ) {
400 for ( int row = 0; row < rowCount; ++row ) {
401 QModelIndex index = model()->index( row, column, rootIndex() ); // checked
402 qreal x;
403 qreal y;
404 if ( datasetDimension() == 1 ) {
405 x = row;
406 y = index.data().toReal();
407 } else {
408 x = index.data().toReal();
409 y = model()->index( row, column + 1, rootIndex() ).data().toReal();
410 }
411 paintDataValueText( painter, index, coordinatePlane()->translate( QPointF( x, y ) ), y );
412 }
413 }
414}
415
416
417void AbstractDiagram::paintMarker( QPainter* painter,
418 const DataValueAttributes& a,
419 const QModelIndex& index,
420 const QPointF& pos )
421{
422 if ( !checkInvariants() || !a.isVisible() ) return;
423 const MarkerAttributes ma = a.markerAttributes();
424 if ( !ma.isVisible() ) return;
425
426 const PainterSaver painterSaver( painter );
427
428 QSizeF maSize = ma.markerSize();
429 const qreal diagramWidth = d->diagramSize.width();
430 const qreal diagramHeight = d->diagramSize.height();
431
432 switch( ma.markerSizeMode() ) {
434 // Unscaled, i.e. without the painter's "zoom"
435 maSize.rwidth() /= painter->transform().m11();
436 maSize.rheight() /= painter->transform().m22();
437 break;
439 // Keep maSize as is. It is specified directly in pixels and desired
440 // to be effected by the painter's "zoom".
441 break;
443 maSize *= qMin( diagramWidth, diagramHeight );
444 break;
445 }
446
447 QBrush indexBrush( brush( index ) );
448 QPen indexPen( ma.pen() );
449 if ( ma.markerColor().isValid() )
450 indexBrush.setColor( ma.markerColor() );
451
452 paintMarker( painter, ma, indexBrush, indexPen, pos, maSize );
453
454 // workaround: BC cannot be changed, otherwise we would pass the
455 // index down to next-lower paintMarker function. So far, we
456 // basically save a circle of radius maSize at pos in the
457 // reverseMapper. This means that ^^^ this version of paintMarker
458 // needs to be called to reverse-map the marker.
459 if ((ma.markerStyle() == MarkerAttributes::Marker4Pixels) || (ma.markerStyle() == MarkerAttributes::Marker1Pixel)) {
460 auto rect = QRectF({}, maSize);
462 d->reverseMapper.addRect( index.row(), index.column(), rect );
463 } else {
464 d->reverseMapper.addCircle( index.row(), index.column(), pos, 2 * maSize );
465 }
466}
467
468void AbstractDiagram::paintMarker( QPainter* painter,
469 const QModelIndex& index,
470 const QPointF& pos )
471{
472 if ( !checkInvariants() ) return;
473 paintMarker( painter, dataValueAttributes( index ), index, pos );
474}
475
476void AbstractDiagram::paintMarker( QPainter* painter,
477 const MarkerAttributes& markerAttributes,
478 const QBrush& brush,
479 const QPen& pen,
480 const QPointF& pos,
481 const QSizeF& maSize )
482{
483 const QPen oldPen( painter->pen() );
484 // Pen is used to paint 4Pixels - 1 Pixel - Ring and FastCross types.
485 // make sure to use the brush color - see above in those cases.
486 const bool isFourPixels = (markerAttributes.markerStyle() == MarkerAttributes::Marker4Pixels);
487 if ( isFourPixels || (markerAttributes.markerStyle() == MarkerAttributes::Marker1Pixel) ) {
488 // for high-performance point charts with tiny point markers:
489 painter->setPen( PrintingParameters::scalePen( QPen( brush.color().lighter() ) ) );
490 if ( isFourPixels ) {
491 const qreal x = pos.x();
492 const qreal y = pos.y();
493 painter->drawLine( QPointF(x-1.0,y-1.0),
494 QPointF(x+1.0,y-1.0) );
495 painter->drawLine( QPointF(x-1.0,y),
496 QPointF(x+1.0,y) );
497 painter->drawLine( QPointF(x-1.0,y+1.0),
498 QPointF(x+1.0,y+1.0) );
499 }
500 painter->drawPoint( pos );
501 } else {
502 const PainterSaver painterSaver( painter );
503 QPen painterPen( pen );
504 painter->setPen( PrintingParameters::scalePen( painterPen ) );
505 painter->setBrush( brush );
507 painter->translate( pos );
508 switch ( markerAttributes.markerStyle() ) {
509 case MarkerAttributes::MarkerCircle:
510 {
511 if ( markerAttributes.threeD() ) {
512 QRadialGradient grad;
514 QColor drawColor = brush.color();
515 grad.setCenter( 0.5, 0.5 );
516 grad.setRadius( 1.0 );
517 grad.setFocalPoint( 0.35, 0.35 );
518 grad.setColorAt( 0.00, drawColor.lighter( 150 ) );
519 grad.setColorAt( 0.20, drawColor );
520 grad.setColorAt( 0.50, drawColor.darker( 150 ) );
521 grad.setColorAt( 0.75, drawColor.darker( 200 ) );
522 grad.setColorAt( 0.95, drawColor.darker( 250 ) );
523 grad.setColorAt( 1.00, drawColor.darker( 200 ) );
524 QBrush newBrush( grad );
525 newBrush.setTransform( brush.transform() );
526 painter->setBrush( newBrush );
527 }
528 painter->drawEllipse( QRectF( 0 - maSize.height()/2, 0 - maSize.width()/2,
529 maSize.height(), maSize.width()) );
530 }
531 break;
532 case MarkerAttributes::MarkerSquare:
533 {
534 QRectF rect( 0 - maSize.width()/2, 0 - maSize.height()/2,
535 maSize.width(), maSize.height() );
536 painter->drawRect( rect );
537 break;
538 }
539 case MarkerAttributes::MarkerDiamond:
540 {
541 QVector <QPointF > diamondPoints;
542 QPointF top, left, bottom, right;
543 top = QPointF( 0, 0 - maSize.height()/2 );
544 left = QPointF( 0 - maSize.width()/2, 0 );
545 bottom = QPointF( 0, maSize.height()/2 );
546 right = QPointF( maSize.width()/2, 0 );
547 diamondPoints << top << left << bottom << right;
548 painter->drawPolygon( diamondPoints );
549 break;
550 }
551 // both handled on top of the method:
552 case MarkerAttributes::Marker1Pixel:
553 case MarkerAttributes::Marker4Pixels:
554 break;
555 case MarkerAttributes::MarkerRing:
556 {
557 painter->setBrush( Qt::NoBrush );
558 painter->setPen( PrintingParameters::scalePen( QPen( brush.color() ) ) );
559 painter->drawEllipse( QRectF( 0 - maSize.height()/2, 0 - maSize.width()/2,
560 maSize.height(), maSize.width()) );
561 break;
562 }
563 case MarkerAttributes::MarkerCross:
564 {
565 // Note: Markers can have outline,
566 // so just drawing two rects is NOT the solution here!
567 const qreal w02 = maSize.width() * 0.2;
568 const qreal w05 = maSize.width() * 0.5;
569 const qreal h02 = maSize.height()* 0.2;
570 const qreal h05 = maSize.height()* 0.5;
571 QVector <QPointF > crossPoints;
572 QPointF p[12];
573 p[ 0] = QPointF( -w02, -h05 );
574 p[ 1] = QPointF( w02, -h05 );
575 p[ 2] = QPointF( w02, -h02 );
576 p[ 3] = QPointF( w05, -h02 );
577 p[ 4] = QPointF( w05, h02 );
578 p[ 5] = QPointF( w02, h02 );
579 p[ 6] = QPointF( w02, h05 );
580 p[ 7] = QPointF( -w02, h05 );
581 p[ 8] = QPointF( -w02, h02 );
582 p[ 9] = QPointF( -w05, h02 );
583 p[10] = QPointF( -w05, -h02 );
584 p[11] = QPointF( -w02, -h02 );
585 for ( int i=0; i<12; ++i )
586 crossPoints << p[i];
587 crossPoints << p[0];
588 painter->drawPolygon( crossPoints );
589 break;
590 }
591 case MarkerAttributes::MarkerFastCross:
592 {
593 QPointF left, right, top, bottom;
594 left = QPointF( -maSize.width()/2, 0 );
595 right = QPointF( maSize.width()/2, 0 );
596 top = QPointF( 0, -maSize.height()/2 );
597 bottom= QPointF( 0, maSize.height()/2 );
598 painter->setPen( PrintingParameters::scalePen( QPen( brush.color() ) ) );
599 painter->drawLine( left, right );
600 painter->drawLine( top, bottom );
601 break;
602 }
603 case MarkerAttributes::MarkerArrowDown:
604 {
605 QVector <QPointF > arrowPoints;
606 QPointF topLeft, topRight, bottom;
607 topLeft = QPointF( 0 - maSize.width()/2, 0 - maSize.height()/2 );
608 topRight = QPointF( maSize.width()/2, 0 - maSize.height()/2 );
609 bottom = QPointF( 0, maSize.height()/2 );
610 arrowPoints << topLeft << bottom << topRight;
611 painter->drawPolygon( arrowPoints );
612 break;
613 }
614 case MarkerAttributes::MarkerArrowUp:
615 {
616 QVector <QPointF > arrowPoints;
617 QPointF top, bottomLeft, bottomRight;
618 top = QPointF( 0, 0 - maSize.height()/2 );
619 bottomLeft = QPointF( 0 - maSize.width()/2, maSize.height()/2 );
620 bottomRight = QPointF( maSize.width()/2, maSize.height()/2 );
621 arrowPoints << top << bottomLeft << bottomRight;
622 painter->drawPolygon( arrowPoints );
623 break;
624 }
625 case MarkerAttributes::MarkerArrowRight:
626 {
627 QVector <QPointF > arrowPoints;
628 QPointF right, topLeft, bottomLeft;
629 right = QPointF( maSize.width()/2, 0 );
630 topLeft = QPointF( 0 - maSize.width()/2, 0 - maSize.height()/2 );
631 bottomLeft = QPointF( 0 - maSize.width()/2, maSize.height()/2 );
632 arrowPoints << topLeft << bottomLeft << right;
633 painter->drawPolygon( arrowPoints );
634 break;
635 }
636 case MarkerAttributes::MarkerArrowLeft:
637 {
638 QVector <QPointF > arrowPoints;
639 QPointF left, topRight, bottomRight;
640 left = QPointF( 0 - maSize.width()/2, 0 );
641 topRight = QPointF( maSize.width()/2, 0 - maSize.height()/2 );
642 bottomRight = QPointF( maSize.width()/2, maSize.height()/2 );
643 arrowPoints << left << bottomRight << topRight;
644 painter->drawPolygon( arrowPoints );
645 break;
646 }
647 case MarkerAttributes::MarkerBowTie:
648 case MarkerAttributes::MarkerHourGlass:
649 {
650 QVector <QPointF > points;
651 QPointF topLeft, topRight, bottomLeft, bottomRight;
652 topLeft = QPointF( 0 - maSize.width()/2, 0 - maSize.height()/2);
653 topRight = QPointF( maSize.width()/2, 0 - maSize.height()/2 );
654 bottomLeft = QPointF( 0 - maSize.width()/2, maSize.height()/2 );
655 bottomRight = QPointF( maSize.width()/2, maSize.height()/2 );
656 if ( markerAttributes.markerStyle() == MarkerAttributes::MarkerBowTie)
657 points << topLeft << bottomLeft << topRight << bottomRight;
658 else
659 points << topLeft << bottomRight << bottomLeft << topRight;
660 painter->drawPolygon( points );
661 break;
662 }
663 case MarkerAttributes::MarkerStar:
664 {
665 const qreal w01 = maSize.width() * 0.1;
666 const qreal w05 = maSize.width() * 0.5;
667 const qreal h01 = maSize.height() * 0.1;
668 const qreal h05 = maSize.height() * 0.5;
669 QVector <QPointF > points;
670 QPointF p1 = QPointF( 0, -h05 );
671 QPointF p2 = QPointF( -w01, -h01 );
672 QPointF p3 = QPointF( -w05, 0 );
673 QPointF p4 = QPointF( -w01, h01 );
674 QPointF p5 = QPointF( 0, h05 );
675 QPointF p6 = QPointF( w01, h01 );
676 QPointF p7 = QPointF( w05, 0 );
677 QPointF p8 = QPointF( w01, -h01 );
678 points << p1 << p2 << p3 << p4 << p5 << p6 << p7 << p8;
679 painter->drawPolygon( points );
680 break;
681 }
682 case MarkerAttributes::MarkerX:
683 {
684 const qreal w01 = maSize.width() * 0.1;
685 const qreal w04 = maSize.width() * 0.4;
686 const qreal w05 = maSize.width() * 0.5;
687 const qreal h01 = maSize.height() * 0.1;
688 const qreal h04 = maSize.height() * 0.4;
689 const qreal h05 = maSize.height() * 0.5;
690 QVector <QPointF > crossPoints;
691 QPointF p1 = QPointF( -w04, -h05 );
692 QPointF p2 = QPointF( -w05, -h04 );
693 QPointF p3 = QPointF( -w01, 0 );
694 QPointF p4 = QPointF( -w05, h04 );
695 QPointF p5 = QPointF( -w04, h05 );
696 QPointF p6 = QPointF( 0, h01 );
697 QPointF p7 = QPointF( w04, h05 );
698 QPointF p8 = QPointF( w05, h04 );
699 QPointF p9 = QPointF( w01, 0 );
700 QPointF p10 = QPointF( w05, -h04 );
701 QPointF p11 = QPointF( w04, -h05 );
702 QPointF p12 = QPointF( 0, -h01 );
703 crossPoints << p1 << p2 << p3 << p4 << p5 << p6
704 << p7 << p8 << p9 << p10 << p11 << p12;
705 painter->drawPolygon( crossPoints );
706 break;
707 }
708 case MarkerAttributes::MarkerAsterisk:
709 {
710 // Note: Markers can have outline,
711 // so just drawing three lines is NOT the solution here!
712 // The idea that we use is to draw 3 lines anyway, but convert their
713 // outlines to QPainterPaths which are then united and filled.
714 const qreal w04 = maSize.width() * 0.4;
715 const qreal h02 = maSize.height() * 0.2;
716 const qreal h05 = maSize.height() * 0.5;
717 //QVector <QPointF > crossPoints;
718 QPointF p1 = QPointF( 0, -h05 );
719 QPointF p2 = QPointF( -w04, -h02 );
720 QPointF p3 = QPointF( -w04, h02 );
721 QPointF p4 = QPointF( 0, h05 );
722 QPointF p5 = QPointF( w04, h02 );
723 QPointF p6 = QPointF( w04, -h02 );
724 QPen pen = painter->pen();
725 QPainterPathStroker stroker;
726 stroker.setWidth( pen.widthF() );
727 stroker.setCapStyle( pen.capStyle() );
728
730 QPainterPath dummyPath;
731 dummyPath.moveTo( p1 );
732 dummyPath.lineTo( p4 );
733 path = stroker.createStroke( dummyPath );
734
735 dummyPath = QPainterPath();
736 dummyPath.moveTo( p2 );
737 dummyPath.lineTo( p5 );
738 path = path.united( stroker.createStroke( dummyPath ) );
739
740 dummyPath = QPainterPath();
741 dummyPath.moveTo( p3 );
742 dummyPath.lineTo( p6 );
743 path = path.united( stroker.createStroke( dummyPath ) );
744
745 painter->drawPath( path );
746 break;
747 }
748 case MarkerAttributes::MarkerHorizontalBar:
749 {
750 const qreal w05 = maSize.width() * 0.5;
751 const qreal h02 = maSize.height()* 0.2;
752 QVector <QPointF > points;
753 QPointF p1 = QPointF( -w05, -h02 );
754 QPointF p2 = QPointF( -w05, h02 );
755 QPointF p3 = QPointF( w05, h02 );
756 QPointF p4 = QPointF( w05, -h02 );
757 points << p1 << p2 << p3 << p4;
758 painter->drawPolygon( points );
759 break;
760 }
761 case MarkerAttributes::MarkerVerticalBar:
762 {
763 const qreal w02 = maSize.width() * 0.2;
764 const qreal h05 = maSize.height()* 0.5;
765 QVector <QPointF > points;
766 QPointF p1 = QPointF( -w02, -h05 );
767 QPointF p2 = QPointF( -w02, h05 );
768 QPointF p3 = QPointF( w02, h05 );
769 QPointF p4 = QPointF( w02, -h05 );
770 points << p1 << p2 << p3 << p4;
771 painter->drawPolygon( points );
772 break;
773 }
774 case MarkerAttributes::NoMarker:
775 break;
776 case MarkerAttributes::PainterPathMarker:
777 {
778 QPainterPath path = markerAttributes.customMarkerPath();
779 const QRectF pathBoundingRect = path.boundingRect();
780 const qreal xScaling = maSize.height() / pathBoundingRect.height();
781 const qreal yScaling = maSize.width() / pathBoundingRect.width();
782 const qreal scaling = qMin( xScaling, yScaling );
783 painter->scale( scaling, scaling );
784 painter->setPen( PrintingParameters::scalePen( QPen( brush.color() ) ) );
785 painter->drawPath(path);
786 break;
787 }
788 default:
789 Q_ASSERT_X ( false, "paintMarkers()",
790 "Type item does not match a defined Marker Type." );
791 }
792 }
793 painter->setPen( oldPen );
794}
795
797{
798 if ( !checkInvariants() ) {
799 return;
800 }
801
802 const int rowCount = model()->rowCount( rootIndex() );
803 const int columnCount = model()->columnCount( rootIndex() );
804 for ( int column = 0; column < columnCount; column += datasetDimension() ) {
805 for ( int row = 0; row < rowCount; ++row ) {
806 QModelIndex index = model()->index( row, column, rootIndex() ); // checked
807 qreal x;
808 qreal y;
809 if ( datasetDimension() == 1 ) {
810 x = row;
811 y = index.data().toReal();
812 } else {
813 x = index.data().toReal();
814 y = model()->index( row, column + 1, rootIndex() ).data().toReal();
815 }
816 paintMarker( painter, index, coordinatePlane()->translate( QPointF( x, y ) ) );
817 }
818 }
819}
820
821
822void AbstractDiagram::setPen( const QModelIndex& index, const QPen& pen )
823{
825 conditionallyMapFromSource( index ),
826 QVariant::fromValue( pen ), DatasetPenRole );
828}
829
831{
832 attributesModel()->setModelData(
833 QVariant::fromValue( pen ), DatasetPenRole );
835}
836
837void AbstractDiagram::setPen( int dataset, const QPen& pen )
838{
839 d->setDatasetAttrs( dataset, QVariant::fromValue( pen ), DatasetPenRole );
841}
842
844{
845 return attributesModel()->data( DatasetPenRole ).value< QPen >();
846}
847
848QPen AbstractDiagram::pen( int dataset ) const
849{
850 const QVariant penSettings( d->datasetAttrs( dataset, DatasetPenRole ) );
851 if ( penSettings.isValid() )
852 return penSettings.value< QPen >();
853 return pen();
854}
855
857{
858 return attributesModel()->data(
859 conditionallyMapFromSource( index ),
860 DatasetPenRole ).value< QPen >();
861}
862
863void AbstractDiagram::setBrush( const QModelIndex& index, const QBrush& brush )
864{
866 conditionallyMapFromSource( index ),
867 QVariant::fromValue( brush ), DatasetBrushRole );
869}
870
872{
873 attributesModel()->setModelData(
874 QVariant::fromValue( brush ), DatasetBrushRole );
876}
877
878void AbstractDiagram::setBrush( int dataset, const QBrush& brush )
879{
880 d->setDatasetAttrs( dataset, QVariant::fromValue( brush ), DatasetBrushRole );
882}
883
885{
886 return attributesModel()->data( DatasetBrushRole ).value< QBrush >();
887}
888
889QBrush AbstractDiagram::brush( int dataset ) const
890{
891 const QVariant brushSettings( d->datasetAttrs( dataset, DatasetBrushRole ) );
892 if ( brushSettings.isValid() )
893 return brushSettings.value< QBrush >();
894 return brush();
895}
896
898{
899 return
900 attributesModel()->data( conditionallyMapFromSource( index ), DatasetBrushRole ).value< QBrush >();
901}
902
903/*
904 * Sets the unit prefix for one value
905 * @param prefix the prefix to be set
906 * @param column the value using that prefix
907 * @param orientation the orientantion of the axis to set
908 */
909void AbstractDiagram::setUnitPrefix( const QString& prefix, int column, Qt::Orientation orientation )
910{
911 d->unitPrefixMap[ column ][ orientation ]= prefix;
912}
913
914/*
915 * Sets the unit prefix for all values
916 * @param prefix the prefix to be set
917 * @param orientation the orientantion of the axis to set
918 */
920{
921 d->unitPrefix[ orientation ] = prefix;
922}
923
924/*
925 * Sets the unit suffix for one value
926 * @param suffix the suffix to be set
927 * @param column the value using that suffix
928 * @param orientation the orientantion of the axis to set
929 */
930void AbstractDiagram::setUnitSuffix( const QString& suffix, int column, Qt::Orientation orientation )
931{
932 d->unitSuffixMap[ column ][ orientation ]= suffix;
933}
934
935/*
936 * Sets the unit suffix for all values
937 * @param suffix the suffix to be set
938 * @param orientation the orientantion of the axis to set
939 */
941{
942 d->unitSuffix[ orientation ] = suffix;
943}
944
945/*
946 * Returns the unit prefix for a special value
947 * @param column the value which's prefix is requested
948 * @param orientation the orientation of the axis
949 * @param fallback if true, the global prefix is return when no specific one is set for that value
950 * @return the unit prefix
951 */
952QString AbstractDiagram::unitPrefix( int column, Qt::Orientation orientation, bool fallback ) const
953{
954 if ( !fallback || d->unitPrefixMap[ column ].contains( orientation ) )
955 return d->unitPrefixMap[ column ][ orientation ];
956 return d->unitPrefix[ orientation ];
957}
958
959/* Returns the global unit prefix
960 * @param orientation the orientation of the axis
961 * @return the unit prefix
962 */
964{
965 return d->unitPrefix[ orientation ];
966}
967
968/*
969 * Returns the unit suffix for a special value
970 * @param column the value which's suffix is requested
971 * @param orientation the orientation of the axis
972 * @param fallback if true, the global suffix is return when no specific one is set for that value
973 * @return the unit suffix
974 */
975QString AbstractDiagram::unitSuffix( int column, Qt::Orientation orientation, bool fallback ) const
976{
977 if ( !fallback || d->unitSuffixMap[ column ].contains( orientation ) )
978 return d->unitSuffixMap[ column ][ orientation ];
979 return d->unitSuffix[ orientation ];
980}
981
982/* Returns the global unit suffix
983 * @param orientation the orientation of the axis
984 * @return the unit suffix
985 */
987{
988 return d->unitSuffix[ orientation ];
989}
990
991// implement QAbstractItemView:
993{
994 return d->reverseMapper.boundingRect( index.row(), index.column() ).toRect();
995}
996
999
1000// indexAt ... down below
1001
1004
1006{ return 0; }
1007
1009{ return 0; }
1010
1012{ return true; }
1013
1015{
1016 const QModelIndexList indexes = d->indexesIn( rect );
1017 QItemSelection selection;
1018 for ( const QModelIndex& index : indexes )
1019 {
1020 selection.append( QItemSelectionRange( index ) );
1021 }
1022 selectionModel()->select( selection, command );
1023}
1024
1026{
1027 QPolygonF polygon;
1028 const auto indexes = selection.indexes();
1029 polygon.reserve(indexes.count());
1030 for ( const QModelIndex& index : indexes )
1031 {
1032 polygon << d->reverseMapper.polygon(index.row(), index.column());
1033 }
1034 return polygon.isEmpty() ? QRegion() : QRegion( polygon.toPolygon() );
1035}
1036
1037QRegion AbstractDiagram::visualRegion(const QModelIndex &index) const
1038{
1039 QPolygonF polygon = d->reverseMapper.polygon(index.row(), index.column());
1040 return polygon.isEmpty() ? QRegion() : QRegion( polygon.toPolygon() );
1041}
1042
1044{
1045 d->attributesModel->setPaletteType( AttributesModel::PaletteTypeDefault );
1046}
1047
1049{
1050 d->attributesModel->setPaletteType( AttributesModel::PaletteTypeSubdued );
1051}
1052
1054{
1055 d->attributesModel->setPaletteType( AttributesModel::PaletteTypeRainbow );
1056}
1057
1059{
1060 QStringList ret;
1061 if ( model() ) {
1062 //qDebug() << "AbstractDiagram::itemRowLabels(): " << attributesModel()->rowCount(attributesModelRootIndex()) << "entries";
1063 const int rowCount = attributesModel()->rowCount(attributesModelRootIndex());
1064 for ( int i = 0; i < rowCount; ++i ) {
1065 //qDebug() << "item row label: " << attributesModel()->headerData( i, Qt::Vertical, Qt::DisplayRole ).toString();
1066 ret << unitPrefix( i, Qt::Horizontal, true ) +
1068 unitSuffix( i, Qt::Horizontal, true );
1069 }
1070 }
1071 return ret;
1072}
1073
1075{
1076 QStringList ret;
1077 if ( !model() ) {
1078 return ret;
1079 }
1080 const int datasetCount = d->datasetCount();
1081 for ( int i = 0; i < datasetCount; ++i ) {
1082 ret << d->datasetAttrs( i, Qt::DisplayRole ).toString();
1083 }
1084 return ret;
1085}
1086
1088{
1089 QList<QBrush> ret;
1090 if ( !model() ) {
1091 return ret;
1092 }
1093 const int datasetCount = d->datasetCount();
1094 for ( int i = 0; i < datasetCount; ++i ) {
1095 ret << brush( i );
1096 }
1097 return ret;
1098}
1099
1101{
1102 QList<QPen> ret;
1103 if ( !model() ) {
1104 return ret;
1105 }
1106 const int datasetCount = d->datasetCount();
1107 for ( int i = 0; i < datasetCount; ++i ) {
1108 ret << pen( i );
1109 }
1110 return ret;
1111}
1112
1114{
1116 if ( !model() ) {
1117 return ret;
1118 }
1119 const int datasetCount = d->datasetCount();
1120 for ( int i = 0; i < datasetCount; ++i ) {
1122 }
1123 return ret;
1124}
1125
1126bool AbstractDiagram::checkInvariants( bool justReturnTheStatus ) const
1127{
1128 if ( ! justReturnTheStatus ) {
1129 Q_ASSERT_X ( model(), "AbstractDiagram::checkInvariants()",
1130 "There is no usable model set, for the diagram." );
1131
1132 Q_ASSERT_X ( coordinatePlane(), "AbstractDiagram::checkInvariants()",
1133 "There is no usable coordinate plane set, for the diagram." );
1134 }
1135 return model() && coordinatePlane();
1136}
1137
1139{
1140 return d->datasetDimension;
1141}
1142
1144{
1145 Q_UNUSED( dimension );
1146 qDebug() << "Setting the dataset dimension using AbstractDiagram::setDatasetDimension is "
1147 "obsolete. Use the specific diagram types instead.";
1148}
1149
1150void AbstractDiagram::setDatasetDimensionInternal( int dimension )
1151{
1152 Q_ASSERT( dimension != 0 );
1153 if ( d->datasetDimension == dimension ) {
1154 return;
1155 }
1156 d->datasetDimension = dimension;
1157 d->attributesModel->setDatasetDimension( dimension );
1158 setDataBoundariesDirty();
1159 Q_EMIT layoutChanged( this );
1160}
1161
1162qreal AbstractDiagram::valueForCell( int row, int column ) const
1163{
1164 if ( !d->attributesModel->hasIndex( row, column, attributesModelRootIndex() ) ) {
1165 qWarning() << "AbstractDiagram::valueForCell(): Requesting value for invalid index!";
1166 return std::numeric_limits<qreal>::quiet_NaN();
1167 }
1168 return d->attributesModel->data(
1169 d->attributesModel->index( row, column, attributesModelRootIndex() ) ).toReal(); // checked
1170}
1171
1172void AbstractDiagram::update() const
1173{
1174 if ( d->plane ) {
1175 d->plane->update();
1176 }
1177}
1178
1180{
1181 return d->indexAt( point );
1182}
1183
1184QModelIndexList AbstractDiagram::indexesAt( const QPoint& point ) const
1185{
1186 return d->indexesAt( point );
1187}
1188
1189QModelIndexList AbstractDiagram::indexesIn( const QRect& rect ) const
1190{
1191 return d->indexesIn( rect );
1192}
Declaring the class KChart::DataValueAttributes.
Base class common for all coordinate planes, CartesianCoordinatePlane, PolarCoordinatePlane,...
AbstractDiagram defines the interface for diagram classes.
void setModel(QAbstractItemModel *model) override
Associate a model with the diagram.
void setDataValueAttributes(const QModelIndex &index, const DataValueAttributes &a)
Set the DataValueAttributes for the given index.
virtual void paintDataValueTexts(QPainter *painter)
virtual AttributesModel * attributesModel() const
Returns the AttributesModel, that is used by this diagram.
bool compare(const AbstractDiagram *other) const
Returns true if both diagrams have the same settings.
void setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command) override
\reimpl
void modelsChanged()
This signal is emitted when either the model or the AttributesModel is replaced.
int verticalOffset() const override
\reimpl
void setUnitPrefix(const QString &prefix, int column, Qt::Orientation orientation)
Set the unit prefix to be used on axes for one specific column.
void setPen(const QModelIndex &index, const QPen &pen)
Set the pen to be used, for painting the datapoint at the given index.
int horizontalOffset() const override
\reimpl
void aboutToBeDestroyed()
This signal is emitted when this diagram is being destroyed, but before all the data,...
virtual void resize(const QSizeF &area)
Called by the widget's sizeEvent.
QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) override
\reimpl
DataValueAttributes dataValueAttributes() const
Retrieve the DataValueAttributes specified globally.
void setAntiAliasing(bool enabled)
Set whether anti-aliasing is to be used while rendering this diagram.
QStringList datasetLabels() const
The set of dataset labels currently displayed, for use in legends, etc.
QRect visualRect(const QModelIndex &index) const override
\reimpl
QModelIndex attributesModelRootIndex() const
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 propertiesChanged()
Emitted upon change of a property of the Diagram.
void scrollTo(const QModelIndex &index, ScrollHint hint=EnsureVisible) override
\reimpl
QPen pen() const
Retrieve the pen to be used for painting datapoints globally.
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector< int > &roles=QVector< int >()) override
\reimpl
void layoutChanged(KChart::AbstractDiagram *)
Diagrams are supposed to emit this signal, when the layout of one of their element changes.
QList< QBrush > datasetBrushes() const
The set of dataset brushes currently used, for use in legends, etc.
virtual void setAttributesModel(AttributesModel *model)
Associate an AttributesModel with this diagram.
qreal valueForCell(int row, int column) const
Helper method, retrieving the data value (DisplayRole) for a given row and column.
QString unitPrefix(int column, Qt::Orientation orientation, bool fallback=false) const
Retrieves the axis unit prefix for a specific column.
AbstractCoordinatePlane * coordinatePlane() const
The coordinate plane associated with the diagram.
void setHidden(const QModelIndex &index, bool hidden)
Hide (or unhide, resp.) a data cell.
void useDefaultColors()
Set the palette to be used, for painting datasets to the default palette.
bool isIndexHidden(const QModelIndex &index) const override
\reimpl
QList< QPen > datasetPens() const
The set of dataset pens currently used, for use in legends, etc.
void setUnitSuffix(const QString &suffix, int column, Qt::Orientation orientation)
Set the unit prefix to be used on axes for one specific column.
void setBrush(const QModelIndex &index, const QBrush &brush)
Set the brush to be used, for painting the datapoint at the given index.
void setPercentMode(bool percent)
Deprecated method that turns the percent mode of this diagram on or off.
void setAllowOverlappingDataValueTexts(bool allow)
Set whether data value labels are allowed to overlap.
QStringList itemRowLabels() const
The set of item row labels currently displayed, for use in Abscissa axes, etc.
bool percentMode() const
Returns whether this diagram is drawn in percent mode.
void setSelectionModel(QItemSelectionModel *selectionModel) override
Associate a seleection model with the diagrom.
void setDatasetDimension(int dimension)
virtual bool usesExternalAttributesModel() const
Returns whether the diagram is using its own built-in attributes model or an attributes model that wa...
QBrush brush() const
Retrieve the brush to be used for painting datapoints globally.
int datasetDimension() const
The dataset dimension of a diagram determines how many value dimensions it expects each datapoint to ...
virtual void setCoordinatePlane(AbstractCoordinatePlane *plane)
Set the coordinate plane associated with the diagram.
QModelIndex indexAt(const QPoint &point) const override
\reimpl
void dataHidden()
This signal is emitted, when the hidden status of at least one data cell was (un)set.
QModelIndexList indexesAt(const QPoint &point) const
This method is added alongside with indexAt from QAIM, since in KChart multiple indexes can be displa...
void setRootIndex(const QModelIndex &idx) override
Set the root index in the model, where the diagram starts referencing data for display.
void useSubduedColors()
Set the palette to be used, for painting datasets to the subdued palette.
virtual void paintMarkers(QPainter *painter)
QList< MarkerAttributes > datasetMarkers() const
The set of dataset markers currently used, for use in legends, etc.
void doItemsLayout() override
\reimpl
QString unitSuffix(int column, Qt::Orientation orientation, bool fallback=false) const
Retrieves the axis unit suffix for a specific column.
bool isHidden() const
Retrieve the hidden status specified globally.
void setAttributesModelRootIndex(const QModelIndex &)
void useRainbowColors()
Set the palette to be used, for painting datasets to the rainbow palette.
QRegion visualRegionForSelection(const QItemSelection &selection) const override
\reimpl
QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override
A proxy model used for decorating data with attributes.
QVariant data(int role) const
Returns the data that were specified at global level, or the default data, or QVariant().
int rowCount(const QModelIndex &) const override
\reimpl
void initFrom(const AttributesModel *other)
Copies the internal data (maps and palette) of another AttributesModel* into this one.
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...
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::DisplayRole) override
\reimpl
QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const override
\reimpl
Diagram attributes dealing with data value labels.
MarkerAttributes markerAttributes() const
void setShowOverlappingDataLabels(bool showOverlappingDataLabels)
Set whether data value texts overlapping other data value texts of the same diagram should be drawn.
A set of attributes controlling the appearance of data set markers.
@ AbsoluteSize
the marker size is directly specified in pixels
@ RelativeToDiagramWidthHeightMin
the marker size is relative to the diagram's min(width, height)
@ AbsoluteSizeScaled
the marker size is specified in pixels, but scaled by the painter's zoom level
Internally used class just adding a special constructor used by AbstractDiagram.
QString path(const QString &relativePath)
QCA_EXPORT void init()
virtual int columnCount(const QModelIndex &parent) const const=0
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const=0
virtual int rowCount(const QModelIndex &parent) const const=0
bool hasAutoScroll() const const
virtual void currentChanged(const QModelIndex &current, const QModelIndex &previous)
QAbstractItemModel * model() const const
QModelIndex rootIndex() const const
void scheduleDelayedItemsLayout()
virtual void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
QItemSelectionModel * selectionModel() const const
virtual void setModel(QAbstractItemModel *model)
virtual void setRootIndex(const QModelIndex &index)
virtual void setSelectionModel(QItemSelectionModel *selectionModel)
const QColor & color() const const
QTransform transform() const const
QColor darker(int factor) const const
bool isValid() const const
QColor lighter(int factor) const const
void setColorAt(qreal position, const QColor &color)
void setCoordinateMode(CoordinateMode mode)
QModelIndexList indexes() const const
virtual void select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command)
void append(QList< T > &&value)
bool isEmpty() const const
void reserve(qsizetype size)
int column() const const
QVariant data(int role) const const
bool isValid() const const
const QAbstractItemModel * model() const const
int row() const const
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
QObject * parent() const const
T qobject_cast(QObject *object)
void drawEllipse(const QPoint &center, int rx, int ry)
void drawLine(const QLine &line)
void drawPath(const QPainterPath &path)
void drawPoint(const QPoint &position)
void drawPolygon(const QPoint *points, int pointCount, Qt::FillRule fillRule)
void drawRect(const QRect &rectangle)
const QPen & pen() const const
void scale(qreal sx, qreal sy)
void setBrush(Qt::BrushStyle style)
void setPen(Qt::PenStyle style)
void setRenderHint(RenderHint hint, bool on)
const QTransform & transform() const const
void translate(const QPoint &offset)
void lineTo(const QPointF &endPoint)
void moveTo(const QPointF &point)
QPainterPath createStroke(const QPainterPath &path) const const
void setCapStyle(Qt::PenCapStyle style)
void setWidth(qreal width)
Qt::PenCapStyle capStyle() const const
qreal widthF() const const
QPolygon toPolygon() const const
void setCenter(const QPointF &center)
void setFocalPoint(const QPointF &focalPoint)
void setRadius(qreal radius)
qreal height() const const
void moveCenter(const QPointF &position)
qreal width() const const
qreal height() const const
qreal & rheight()
qreal & rwidth()
qreal width() const const
DisplayRole
typedef KeyboardModifiers
Orientation
QTextStream & left(QTextStream &stream)
QTextStream & right(QTextStream &stream)
qreal m11() const const
qreal m22() const const
QVariant fromValue(T &&value)
bool isValid() const const
qreal toReal(bool *ok) const const
QString toString() const const
T value() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Dec 21 2024 17:02:46 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.