KChart

KChartPlotter.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 "KChartPlotter.h"
10#include "KChartPlotter_p.h"
11
12#include "KChartAbstractGrid.h"
13#include "KChartPainterSaver_p.h"
14#include "KChartMath_p.h"
15
16#include "KChartNormalPlotter_p.h"
17#include "KChartPercentPlotter_p.h"
18#include "KChartStackedPlotter_p.h"
19
20using namespace KChart;
21
22Plotter::Private::Private()
23 : implementor( nullptr )
24 , normalPlotter( nullptr )
25 , percentPlotter( nullptr )
26 , stackedPlotter( nullptr )
27{
28}
29
30Plotter::Private::~Private()
31{
32 delete normalPlotter;
33 delete percentPlotter;
34 delete stackedPlotter;
35}
36
37
38#define d d_func()
39
40
41Plotter::Plotter( QWidget* parent, CartesianCoordinatePlane* plane ) :
42 AbstractCartesianDiagram( new Private(), parent, plane )
43{
44 init();
45}
46
47void Plotter::init()
48{
49 d->diagram = this;
50 d->normalPlotter = new NormalPlotter( this );
51 d->percentPlotter = new PercentPlotter( this );
52 d->stackedPlotter = new StackedPlotter( this );
53 d->implementor = d->normalPlotter;
54 QObject* test = d->implementor->plotterPrivate();
55 connect( this, SIGNAL(boundariesChanged()), test, SLOT(changedProperties()) );
56 // The signal is connected to the superclass's slot at this point because the connection happened
57 // in its constructor when "its type was not Plotter yet".
59 this, SLOT(connectAttributesModel(KChart::AttributesModel*)) );
61 this, SLOT(connectAttributesModel(KChart::AttributesModel*)) );
62 setDatasetDimensionInternal( 2 );
63}
64
65Plotter::~Plotter()
66{
67}
68
70{
71 Plotter* newDiagram = new Plotter( new Private( *d ) );
72 newDiagram->setType( type() );
73 return newDiagram;
74}
75
76bool Plotter::compare( const Plotter* other ) const
77{
78 if ( other == this )
79 return true;
80 if ( other == nullptr )
81 return false;
82 return // compare the base class
83 ( static_cast< const AbstractCartesianDiagram* >( this )->compare( other ) ) &&
84 // compare own properties
85 ( type() == other->type() );
86}
87
88void Plotter::connectAttributesModel( AttributesModel* newModel )
89{
90 // Order of setting the AttributesModel in compressor and diagram is very important due to slot
91 // invocation order. Refer to the longer comment in
92 // AbstractCartesianDiagram::connectAttributesModel() for details.
93
94 if ( useDataCompression() == Plotter::NONE )
95 {
96 d->plotterCompressor.setModel( nullptr );
97 AbstractCartesianDiagram::connectAttributesModel( newModel );
98 }
99 else
100 {
101 d->compressor.setModel( nullptr );
102 if ( attributesModel() != d->plotterCompressor.model() )
103 {
104 d->plotterCompressor.setModel( attributesModel() );
105 connect( &d->plotterCompressor, SIGNAL(boundariesChanged()), this, SLOT(setDataBoundariesDirty()) );
106 if ( useDataCompression() != Plotter::SLOPE )
107 {
108 connect( coordinatePlane(), SIGNAL(internal_geometryChanged(QRect,QRect)),
109 this, SLOT(setDataBoundariesDirty()) );
110 connect( coordinatePlane(), SIGNAL(geometryChanged(QRect,QRect)),
111 this, SLOT(setDataBoundariesDirty()) );
112 calcMergeRadius();
113 }
114 }
115 }
116}
117
118Plotter::CompressionMode Plotter::useDataCompression() const
119{
120 return d->implementor->useCompression();
121}
122
123void Plotter::setUseDataCompression( Plotter::CompressionMode value )
124{
125 if ( useDataCompression() != value )
126 {
127 d->implementor->setUseCompression( value );
128 if ( useDataCompression() != Plotter::NONE )
129 {
130 d->compressor.setModel( nullptr );
131 if ( attributesModel() != d->plotterCompressor.model() )
132 d->plotterCompressor.setModel( attributesModel() );
133 }
134 }
135}
136
137qreal Plotter::maxSlopeChange() const
138{
139 return d->plotterCompressor.maxSlopeChange();
140}
141
142void Plotter::setMaxSlopeChange( qreal value )
143{
144 d->plotterCompressor.setMaxSlopeChange( value );
145}
146
147qreal Plotter::mergeRadiusPercentage() const
148{
149 return d->mergeRadiusPercentage;
150}
151
152void Plotter::setMergeRadiusPercentage( qreal value )
153{
154 if ( d->mergeRadiusPercentage != value )
155 {
156 d->mergeRadiusPercentage = value;
157 //d->plotterCompressor.setMergeRadiusPercentage( value );
158 //update();
159 }
160}
161
162void Plotter::setType( const PlotType type )
163{
164 if ( d->implementor->type() == type ) {
165 return;
166 }
167 if ( datasetDimension() != 2 ) {
168 Q_ASSERT_X ( false, "setType()",
169 "This line chart type can only be used with two-dimensional data." );
170 return;
171 }
172 switch ( type ) {
173 case Normal:
174 d->implementor = d->normalPlotter;
175 break;
176 case Percent:
177 d->implementor = d->percentPlotter;
178 break;
179 case Stacked:
180 d->implementor = d->stackedPlotter;
181 break;
182 default:
183 Q_ASSERT_X( false, "Plotter::setType", "unknown plotter subtype" );
184 }
185 bool connection = connect( this, SIGNAL(boundariesChanged()),
186 d->implementor->plotterPrivate(), SLOT(changedProperties()) );
187 Q_ASSERT( connection );
188 Q_UNUSED( connection );
189
190 // d->lineType = type;
191 Q_ASSERT( d->implementor->type() == type );
192
193 setDataBoundariesDirty();
194 Q_EMIT layoutChanged( this );
196}
197
198Plotter::PlotType Plotter::type() const
199{
200 return d->implementor->type();
201}
202
204{
205 d->attributesModel->setModelData( QVariant::fromValue( la ), LineAttributesRole );
207}
208
209void Plotter::setLineAttributes( int column, const LineAttributes& la )
210{
211 d->setDatasetAttrs( column, QVariant::fromValue( la ), LineAttributesRole );
213}
214
216{
217 d->resetDatasetAttrs( column, LineAttributesRole );
219}
220
222{
223 d->attributesModel->setData( d->attributesModel->mapFromSource( index ),
224 QVariant::fromValue( la ), LineAttributesRole );
226}
227
229{
230 d->attributesModel->resetData(
231 d->attributesModel->mapFromSource(index), LineAttributesRole );
233}
234
236{
237 return d->attributesModel->data( KChart::LineAttributesRole ).value<LineAttributes>();
238}
239
241{
242 const QVariant attrs( d->datasetAttrs( column, LineAttributesRole ) );
243 if ( attrs.isValid() )
244 return attrs.value<LineAttributes>();
245 return lineAttributes();
246}
247
249{
250 return d->attributesModel->data(
251 d->attributesModel->mapFromSource( index ), KChart::LineAttributesRole ).value<LineAttributes>();
252}
253
255{
256 setDataBoundariesDirty();
257 d->attributesModel->setModelData( QVariant::fromValue( la ), ThreeDLineAttributesRole );
259}
260
262{
263 setDataBoundariesDirty();
264 d->setDatasetAttrs( column, QVariant::fromValue( la ), ThreeDLineAttributesRole );
266}
267
269{
270 setDataBoundariesDirty();
271 d->attributesModel->setData( d->attributesModel->mapFromSource( index ), QVariant::fromValue( la ),
272 ThreeDLineAttributesRole );
274}
275
277{
278 return d->attributesModel->data( KChart::ThreeDLineAttributesRole ).value<ThreeDLineAttributes>();
279}
280
282{
283 const QVariant attrs( d->datasetAttrs( column, ThreeDLineAttributesRole ) );
284 if ( attrs.isValid() ) {
285 return attrs.value<ThreeDLineAttributes>();
286 }
287 return threeDLineAttributes();
288}
289
291{
292 return d->attributesModel->data(
293 d->attributesModel->mapFromSource( index ), KChart::ThreeDLineAttributesRole ).value<ThreeDLineAttributes>();
294}
295
296qreal Plotter::threeDItemDepth( const QModelIndex & index ) const
297{
298 return threeDLineAttributes( index ).validDepth();
299}
300
301qreal Plotter::threeDItemDepth( int column ) const
302{
303 return threeDLineAttributes( column ).validDepth();
304}
305
307{
308 d->attributesModel->setData( d->attributesModel->mapFromSource( index ),
309 QVariant::fromValue( va ), KChart::ValueTrackerAttributesRole );
311}
312
314{
315 return d->attributesModel->data(
316 d->attributesModel->mapFromSource( index ), KChart::ValueTrackerAttributesRole ).value<ValueTrackerAttributes>();
317}
318
319void Plotter::resizeEvent ( QResizeEvent* )
320{
321}
322
323const QPair< QPointF, QPointF > Plotter::calculateDataBoundaries() const
324{
325 if ( !checkInvariants( true ) )
326 return QPair< QPointF, QPointF >( QPointF( 0, 0 ), QPointF( 0, 0 ) );
327
328 // note: calculateDataBoundaries() is ignoring the hidden flags.
329 // That's not a bug but a feature: Hiding data does not mean removing them.
330 // For totally removing data from KD Chart's view people can use e.g. a proxy model ...
331
332 // calculate boundaries for different line types Normal - Stacked - Percent - Default Normal
333 return d->implementor->calculateDataBoundaries();
334}
335
336
337void Plotter::paintEvent ( QPaintEvent*)
338{
339 QPainter painter ( viewport() );
340 PaintContext ctx;
341 ctx.setPainter ( &painter );
342 ctx.setRectangle ( QRectF ( 0, 0, width(), height() ) );
343 paint ( &ctx );
344}
345
347{
348 // note: Not having any data model assigned is no bug
349 // but we can not draw a diagram then either.
350 if ( !checkInvariants( true ) ) return;
351
352 AbstractCoordinatePlane* const plane = ctx->coordinatePlane();
353 if ( ! plane ) return;
354 d->setCompressorResolution( size(), plane );
355
357
358 const PainterSaver p( ctx->painter() );
359 if ( model()->rowCount( rootIndex() ) == 0 || model()->columnCount( rootIndex() ) == 0 )
360 return; // nothing to paint for us
361
362 ctx->setCoordinatePlane( plane->sharedAxisMasterPlane( ctx->painter() ) );
363
364 // paint different line types Normal - Stacked - Percent - Default Normal
365 d->implementor->paint( ctx );
366
367 ctx->setCoordinatePlane( plane );
368}
369
370void Plotter::resize ( const QSizeF& size )
371{
372 d->setCompressorResolution( size, coordinatePlane() );
373 if ( useDataCompression() == Plotter::BOTH || useDataCompression() == Plotter::DISTANCE )
374 {
375 d->plotterCompressor.cleanCache();
376 calcMergeRadius();
377 }
378 setDataBoundariesDirty();
380}
381
382void Plotter::setDataBoundariesDirty()
383{
384 AbstractCartesianDiagram::setDataBoundariesDirty();
385 if ( useDataCompression() == Plotter::DISTANCE || useDataCompression() == Plotter::BOTH )
386 {
387 calcMergeRadius();
388 //d->plotterCompressor.setMergeRadiusPercentage( d->mergeRadiusPercentage );
389 }
390}
391
392void Plotter::calcMergeRadius()
393{
395 Q_ASSERT( plane );
396 //Q_ASSERT( plane->translate( plane->translateBack( plane->visibleDiagramArea().topLeft() ) ) == plane->visibleDiagramArea().topLeft() );
397 QRectF range = plane->visibleDataRange();
398 //qDebug() << range;
399 const qreal radius = std::sqrt( ( range.x() + range.width() ) * ( range.y() + range.height() ) );
400 //qDebug() << radius;
401 //qDebug() << radius * d->mergeRadiusPercentage;
402 //qDebug() << d->mergeRadiusPercentage;
403 d->plotterCompressor.setMergeRadius( radius * d->mergeRadiusPercentage );
404}
405
406#if defined(Q_COMPILER_MANGLES_RETURN_TYPE)
407const
408#endif
409int Plotter::numberOfAbscissaSegments () const
410{
411 return d->attributesModel->rowCount( attributesModelRootIndex() );
412}
413
414#if defined(Q_COMPILER_MANGLES_RETURN_TYPE)
415const
416#endif
417int Plotter::numberOfOrdinateSegments () const
418{
419 return d->attributesModel->columnCount( attributesModelRootIndex() );
420}
Base class for diagrams based on a cartesian coordianate system.
bool compare(const AbstractCartesianDiagram *other) const
Returns true if both diagrams have the same settings.
Base class common for all coordinate planes, CartesianCoordinatePlane, PolarCoordinatePlane,...
virtual AttributesModel * attributesModel() const
Returns the AttributesModel, that is used by this diagram.
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 attributesModelAboutToChange(KChart::AttributesModel *newModel, KChart::AttributesModel *oldModel)
This signal is emitted just before the new attributes model is connected internally.
void layoutChanged(KChart::AbstractDiagram *)
Diagrams are supposed to emit this signal, when the layout of one of their element changes.
AbstractCoordinatePlane * coordinatePlane() const
The coordinate plane associated with the diagram.
int datasetDimension() const
The dataset dimension of a diagram determines how many value dimensions it expects each datapoint to ...
void boundariesChanged()
Emitted upon change of a data boundary.
static bool isBoundariesValid(const QRectF &r)
Checks whether both coordinates of r are valid according to isValueValid.
A proxy model used for decorating data with attributes.
QRectF visibleDataRange() const
Returns the currently visible data range.
Set of attributes for changing the appearance of line charts.
Stores information about painting diagrams.
Plotter defines a diagram type plotting two-dimensional data.
void setThreeDLineAttributes(const ThreeDLineAttributes &la)
Sets the global 3D line attributes to la.
bool compare(const Plotter *other) const
Returns true if both diagrams have the same settings.
void setLineAttributes(const LineAttributes &la)
Sets the global line attributes to la.
void setType(const PlotType type)
Sets the plotter's type to type.
void resize(const QSizeF &area) override
Called by the widget's sizeEvent.
virtual Plotter * clone() const
Creates an exact copy of this diagram.
ThreeDLineAttributes threeDLineAttributes() const
PlotType type() const
void paint(PaintContext *paintContext) override
Draw the diagram contents to the rectangle and painter, that are passed in as part of the paint conte...
void resetLineAttributes(int column)
Resets the line attributes of data set column.
LineAttributes lineAttributes() const
const QPair< QPointF, QPointF > calculateDataBoundaries() const override
\reimpl
qreal threeDItemDepth(const QModelIndex &index) const override
ValueTrackerAttributes valueTrackerAttributes(const QModelIndex &index) const
Returns the value tracker attributes of the model index index.
void setValueTrackerAttributes(const QModelIndex &index, const ValueTrackerAttributes &va)
Sets the value tracker attributes of the model index index to va.
Cell-specific attributes regarding value tracking.
void init(KXmlGuiWindow *window, KGameDifficulty *difficulty=nullptr)
KGuiItem test()
QAbstractItemModel * model() const const
QModelIndex rootIndex() const const
QWidget * viewport() const const
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
qreal height() const const
qreal width() const const
qreal x() const const
qreal y() const const
QVariant fromValue(T &&value)
bool isValid() const const
T value() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Oct 4 2024 12:04:16 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.