KChart

KChartWidget.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 <KChartWidget.h>
10#include <KChartWidget_p.h>
11
12#include <KChartAbstractDiagram.h>
13#include <KChartBarDiagram.h>
14#include <KChartChart.h>
15#include <KChartAbstractCoordinatePlane.h>
16#include <KChartLineDiagram.h>
17#include <KChartPlotter.h>
18#include <KChartPieDiagram.h>
19#include <KChartPolarDiagram.h>
20#include <KChartRingDiagram.h>
21#include <KChartLegend.h>
22#include "KChartMath_p.h"
23
24#include <QDebug>
25
26#define d d_func()
27
28using namespace KChart;
29
30Widget::Private::Private( Widget * qq )
31 : q( qq ),
32 layout( q ),
33 m_model( q ),
34 m_chart( q ),
35 m_cartPlane( &m_chart ),
36 m_polPlane( &m_chart ),
37 usedDatasetWidth( 0 )
38{
39 KDAB_SET_OBJECT_NAME( layout );
40 KDAB_SET_OBJECT_NAME( m_model );
41 KDAB_SET_OBJECT_NAME( m_chart );
42
43 layout.addWidget( &m_chart );
44}
45
46Widget::Private::~Private() {}
47
48
49
51 QWidget(parent), _d( new Private( this ) )
52{
53 // as default we have a cartesian coordinate plane ...
54 // ... and a line diagram
55 setType( Line );
56}
57
59{
60 delete _d; _d = nullptr;
61}
62
63void Widget::init()
64{
65}
66
67void Widget::setDataset( int column, const QVector< qreal > & data, const QString& title )
68{
69 if ( ! checkDatasetWidth( 1 ) )
70 return;
71
72 QStandardItemModel & model = d->m_model;
73
74 justifyModelSize( data.size(), column + 1 );
75
76 for ( int i = 0; i < data.size(); ++i )
77 {
78 const QModelIndex index = model.index( i, column );
79 model.setData( index, QVariant( data[i] ), Qt::DisplayRole );
80 }
81 if ( ! title.isEmpty() )
82 model.setHeaderData( column, Qt::Horizontal, QVariant( title ) );
83}
84
85void Widget::setDataset( int column, const QVector< QPair< qreal, qreal > > & data, const QString& title )
86{
87 if ( ! checkDatasetWidth( 2 ))
88 return;
89
90 QStandardItemModel & model = d->m_model;
91
92 justifyModelSize( data.size(), (column + 1) * 2 );
93
94 for ( int i = 0; i < data.size(); ++i )
95 {
96 QModelIndex index = model.index( i, column * 2 );
97 model.setData( index, QVariant( data[i].first ), Qt::DisplayRole );
98
99 index = model.index( i, column * 2 + 1 );
100 model.setData( index, QVariant( data[i].second ), Qt::DisplayRole );
101 }
102 if ( ! title.isEmpty() ) {
103 model.setHeaderData( column, Qt::Horizontal, QVariant( title ) );
104 }
105}
106
107void Widget::setDataCell( int row, int column, qreal data )
108{
109 if ( ! checkDatasetWidth( 1 ) )
110 return;
111
112 QStandardItemModel & model = d->m_model;
113
114 justifyModelSize( row + 1, column + 1 );
115
116 const QModelIndex index = model.index( row, column );
117 model.setData( index, QVariant( data ), Qt::DisplayRole );
118}
119
120void Widget::setDataCell( int row, int column, QPair< qreal, qreal > data )
121{
122 if ( ! checkDatasetWidth( 2 ))
123 return;
124
125 QStandardItemModel & model = d->m_model;
126
127 justifyModelSize( row + 1, (column + 1) * 2 );
128
129 QModelIndex index = model.index( row, column * 2 );
130 model.setData( index, QVariant( data.first ), Qt::DisplayRole );
131
132 index = model.index( row, column * 2 + 1 );
133 model.setData( index, QVariant( data.second ), Qt::DisplayRole );
134}
135
136/*
137 * Resets all data.
138 */
140{
141 d->m_model.clear();
142 d->usedDatasetWidth = 0;
143}
144
145void Widget::setGlobalLeading( int left, int top, int right, int bottom )
146{
147 d->m_chart.setGlobalLeading( left, top, right, bottom );
148}
149
151{
152 d->m_chart.setGlobalLeadingLeft( leading );
153}
154
156{
157 return d->m_chart.globalLeadingLeft();
158}
159
161{
162 d->m_chart.setGlobalLeadingTop( leading );
163}
164
166{
167 return d->m_chart.globalLeadingTop();
168}
169
171{
172 d->m_chart.setGlobalLeadingRight( leading );
173}
174
176{
177 return d->m_chart.globalLeadingRight();
178}
179
181{
182 d->m_chart.setGlobalLeadingBottom( leading );
183}
184
186{
187 return d->m_chart.globalLeadingBottom();
188}
189
191{
192 return d->m_chart.headerFooter();
193}
194
196{
197 return d->m_chart.headerFooters();
198}
199
201 HeaderFooter::HeaderFooterType type,
202 Position position)
203{
204 HeaderFooter* newHeader = new HeaderFooter( &d->m_chart );
205 newHeader->setType( type );
206 newHeader->setPosition( position );
207 newHeader->setText( text );
208 d->m_chart.addHeaderFooter( newHeader ); // we need this explicit call !
209}
210
212{
213 header->setParent( &d->m_chart );
214 d->m_chart.addHeaderFooter( header ); // we need this explicit call !
215}
216
218{
219 header->setParent( &d->m_chart );
220 d->m_chart.replaceHeaderFooter( header, oldHeader );
221}
222
224{
225 d->m_chart.takeHeaderFooter( header );
226}
227
229{
230 return d->m_chart.legend();
231}
232
234{
235 return d->m_chart.legends();
236}
237
239{
240 Legend* legend = new Legend( diagram(), &d->m_chart );
241 legend->setPosition( position );
242 d->m_chart.addLegend( legend );
243}
244
246{
248 legend->setParent( &d->m_chart );
249 d->m_chart.addLegend( legend );
250}
251
252void Widget::replaceLegend( Legend* legend, Legend* oldLegend )
253{
255 legend->setParent( &d->m_chart );
256 d->m_chart.replaceLegend( legend, oldLegend );
257}
258
259void Widget::takeLegend( Legend* legend )
260{
261 d->m_chart.takeLegend( legend );
262}
263
265{
266 if ( coordinatePlane() == nullptr )
267 qDebug() << "diagram(): coordinatePlane() was NULL";
268
269 return coordinatePlane()->diagram();
270}
271
273{
274 return dynamic_cast<BarDiagram*>( diagram() );
275}
277{
278 return dynamic_cast<LineDiagram*>( diagram() );
279}
281{
282 return dynamic_cast<Plotter*>( diagram() );
283}
285{
286 return dynamic_cast<PieDiagram*>( diagram() );
287}
289{
290 return dynamic_cast<RingDiagram*>( diagram() );
291}
293{
294 return dynamic_cast<PolarDiagram*>( diagram() );
295}
296
298{
299 return d->m_chart.coordinatePlane();
300}
301
302static bool isCartesian( KChart::Widget::ChartType type )
303{
304 return (type == KChart::Widget::Bar) || (type == KChart::Widget::Line);
305}
306
307static bool isPolar( KChart::Widget::ChartType type )
308{
309 return (type == KChart::Widget::Pie)
310 || (type == KChart::Widget::Ring)
311 || (type == KChart::Widget::Polar);
312}
313
314void Widget::setType( ChartType chartType, SubType chartSubType )
315{
316 AbstractDiagram* diag = nullptr;
317 const ChartType oldType = type();
318
319 if ( chartType != oldType ) {
320 if ( chartType != NoType ) {
321 if ( isCartesian( chartType ) && ! isCartesian( oldType ) )
322 {
323 if ( coordinatePlane() == &d->m_polPlane ) {
324 d->m_chart.takeCoordinatePlane( &d->m_polPlane );
325 d->m_chart.addCoordinatePlane( &d->m_cartPlane );
326 } else {
327 d->m_chart.replaceCoordinatePlane( &d->m_cartPlane );
328 }
329 }
330 else if ( isPolar( chartType ) && ! isPolar( oldType ) )
331 {
332 if ( coordinatePlane() == &d->m_cartPlane ) {
333 d->m_chart.takeCoordinatePlane( &d->m_cartPlane );
334 d->m_chart.addCoordinatePlane( &d->m_polPlane );
335 } else {
336 d->m_chart.replaceCoordinatePlane( &d->m_polPlane );
337 }
338 }
339 }
340 switch ( chartType ) {
341 case Bar:
342 diag = new BarDiagram( &d->m_chart, &d->m_cartPlane );
343 break;
344 case Line:
345 diag = new LineDiagram( &d->m_chart, &d->m_cartPlane );
346 break;
347 case Plot:
348 diag = new Plotter( &d->m_chart, &d->m_cartPlane );
349 break;
350 case Pie:
351 diag = new PieDiagram( &d->m_chart, &d->m_polPlane );
352 break;
353 case Polar:
354 diag = new PolarDiagram( &d->m_chart, &d->m_polPlane );
355 break;
356 case Ring:
357 diag = new RingDiagram( &d->m_chart, &d->m_polPlane );
358 break;
359 case NoType:
360 break;
361 }
362 if ( diag != nullptr ) {
363 if ( isCartesian( oldType ) && isCartesian( chartType ) ) {
368 const auto axes = oldDiag->axes();
369 for ( CartesianAxis* axis : axes ) {
370 oldDiag->takeAxis( axis );
371 newDiag->addAxis ( axis );
372 }
373 }
374
375 const auto legends = d->m_chart.legends();
376 for ( Legend* l : legends ) {
377 l->setDiagram( diag );
378 }
379
380 diag->setModel( &d->m_model );
382
383 //checkDatasetWidth( d->usedDatasetWidth );
384 }
385 //coordinatePlane()->setGridNeedsRecalculate();
386 }
387
388 if ( chartType != NoType ) {
389 if ( chartType != oldType || chartSubType != subType() )
391 d->m_chart.resize( size() ); // triggering immediate update
392 }
393}
394
395template< class DiagramType, class Subtype >
396void setSubtype( AbstractDiagram *_dia, Subtype st)
397{
398 if ( DiagramType *dia = qobject_cast< DiagramType * >( _dia ) ) {
399 dia->setType( st );
400 }
401}
402
404{
405 // ### at least PieDiagram, PolarDiagram and RingDiagram are unhandled here
406
408 switch ( subType ) {
409 case Normal:
410 setSubtype< BarDiagram >( dia, BarDiagram::Normal );
411 setSubtype< LineDiagram >( dia, LineDiagram::Normal );
412 setSubtype< Plotter >( dia, Plotter::Normal );
413 break;
414 case Stacked:
415 setSubtype< BarDiagram >( dia, BarDiagram::Stacked );
416 setSubtype< LineDiagram >( dia, LineDiagram::Stacked );
417 // setSubtype< Plotter >( dia, Plotter::Stacked );
418 break;
419 case Percent:
420 setSubtype< BarDiagram >( dia, BarDiagram::Percent );
421 setSubtype< LineDiagram >( dia, LineDiagram::Percent );
422 setSubtype< Plotter >( dia, Plotter::Percent );
423 break;
424 case Rows:
426 break;
427 default:
428 Q_ASSERT_X ( false, "Widget::setSubType", "Sub-type not supported!" );
429 break;
430 }
431}
432
433Widget::ChartType Widget::type() const
434{
435 // PENDING(christoph) save the type out-of-band:
436 AbstractDiagram * const dia = const_cast<Widget*>( this )->diagram();
438 return Bar;
439 else if ( qobject_cast< LineDiagram* >( dia ) )
440 return Line;
441 else if ( qobject_cast< Plotter* >( dia ) )
442 return Plot;
443 else if ( qobject_cast< PieDiagram* >( dia ) )
444 return Pie;
446 return Polar;
447 else if ( qobject_cast< RingDiagram* >( dia ) )
448 return Ring;
449 else
450 return NoType;
451}
452
454{
455 // PENDING(christoph) save the type out-of-band:
456 Widget::SubType retVal = Normal;
457
458 AbstractDiagram * const dia = const_cast<Widget*>( this )->diagram();
462
463//FIXME(khz): Add the impl for these chart types - or remove them from here:
464// PieDiagram* pieDia = qobject_cast< PieDiagram* >( diagram() );
465// PolarDiagram* polarDia = qobject_cast< PolarDiagram* >( diagram() );
466// RingDiagram* ringDia = qobject_cast< RingDiagram* >( diagram() );
467
468#define TEST_SUB_TYPE(DIAGRAM, INTERNALSUBTYPE, SUBTYPE) \
469{ \
470 if ( DIAGRAM && DIAGRAM->type() == INTERNALSUBTYPE ) \
471 retVal = SUBTYPE; \
472}
473 const Widget::ChartType mainType = type();
474 switch ( mainType )
475 {
476 case Bar:
477 TEST_SUB_TYPE( barDia, BarDiagram::Normal, Normal );
478 TEST_SUB_TYPE( barDia, BarDiagram::Stacked, Stacked );
479 TEST_SUB_TYPE( barDia, BarDiagram::Percent, Percent );
480 TEST_SUB_TYPE( barDia, BarDiagram::Rows, Rows );
481 break;
482 case Line:
483 TEST_SUB_TYPE( lineDia, LineDiagram::Normal, Normal );
484 TEST_SUB_TYPE( lineDia, LineDiagram::Stacked, Stacked );
485 TEST_SUB_TYPE( lineDia, LineDiagram::Percent, Percent );
486 break;
487 case Plot:
488 TEST_SUB_TYPE( plotterDia, Plotter::Normal, Normal );
489 TEST_SUB_TYPE( plotterDia, Plotter::Percent, Percent );
490 break;
491 case Pie:
492 // no impl. yet
493 break;
494 case Polar:
495 // no impl. yet
496 break;
497 case Ring:
498 // no impl. yet
499 break;
500 default:
501 Q_ASSERT_X ( false,
502 "Widget::subType", "Chart type not supported!" );
503 break;
504 }
505 return retVal;
506}
507
508
509bool Widget::checkDatasetWidth( int width )
510{
511 if ( width == diagram()->datasetDimension() )
512 {
513 d->usedDatasetWidth = width;
514 return true;
515 }
516 qDebug() << "The current diagram type doesn't support this data dimension.";
517 return false;
518/* if ( d->usedDatasetWidth == width || d->usedDatasetWidth == 0 ) {
519 d->usedDatasetWidth = width;
520 diagram()->setDatasetDimension( width );
521 return true;
522 }
523 qDebug() << "It's impossible to mix up the different setDataset() methods on the same widget.";
524 return false;*/
525}
526
527void Widget::justifyModelSize( int rows, int columns )
528{
529 QAbstractItemModel & model = d->m_model;
530 const int currentRows = model.rowCount();
531 const int currentCols = model.columnCount();
532
533 if ( currentCols < columns )
534 if ( ! model.insertColumns( currentCols, columns - currentCols ))
535 qDebug() << "justifyModelSize: could not increase model size.";
536 if ( currentRows < rows )
537 if ( ! model.insertRows( currentRows, rows - currentRows ))
538 qDebug() << "justifyModelSize: could not increase model size.";
539
540 Q_ASSERT( model.rowCount() >= rows );
541 Q_ASSERT( model.columnCount() >= columns );
542}
Base class for diagrams based on a cartesian coordianate system.
Base class common for all coordinate planes, CartesianCoordinatePlane, PolarCoordinatePlane,...
virtual void replaceDiagram(AbstractDiagram *diagram, AbstractDiagram *oldDiagram=nullptr)
Replaces the old diagram, or appends the diagram, it there is none yet.
AbstractDiagram defines the interface for diagram classes.
BarDiagram defines a common bar diagram.
The class for cartesian axes.
A header or footer displaying text above or below charts.
Legend defines the interface for the legend drawing class.
void setPosition(Position position)
Specify the position of a non-floating legend.
void setDiagram(KChart::AbstractDiagram *newDiagram)
A convenience method doing the same as replaceDiagram( newDiagram, 0 );.
LineDiagram defines a common line diagram.
PieDiagram defines a common pie diagram.
Plotter defines a diagram type plotting two-dimensional data.
PolarDiagram defines a common polar diagram.
Defines a position, using compass terminology.
RingDiagram defines a common ring diagram.
The KChart widget for usage without Interwiev.
void replaceHeaderFooter(HeaderFooter *header, HeaderFooter *oldHeader=nullptr)
Replaces the old header (or footer, resp.), or appends the new header or footer, it there is none yet...
QList< HeaderFooter * > allHeadersFooters()
Returns a list with all headers.
BarDiagram * barDiagram()
If the current diagram is a BarDiagram, it is returned; otherwise 0 is returned.
AbstractDiagram * diagram()
Returns a pointer to the current diagram.
void setGlobalLeading(int left, int top, int right, int bottom)
Sets all global leadings (borders).
int globalLeadingBottom() const
Returns the bottom leading (border).
int globalLeadingLeft() const
Returns the left leading (border).
Plotter * plotter()
If the current diagram is a LineDiagram, it is returned; otherwise 0 is returned.
SubType subType() const
Returns the sub-type of the chart.
void takeHeaderFooter(HeaderFooter *header)
Remove the header (or footer, resp.) from the widget, without deleting it.
PieDiagram * pieDiagram()
If the current diagram is a Plotter, it is returned; otherwise 0 is returned.
AbstractCoordinatePlane * coordinatePlane()
Returns a pointer to the current coordinate plane.
void setGlobalLeadingRight(int leading)
Sets the right leading (border).
void resetData()
Resets all data.
LineDiagram * lineDiagram()
If the current diagram is a LineDiagram, it is returned; otherwise 0 is returned.
SubType
Sub type values, matching the values defines for the respective Diagram classes.
void setGlobalLeadingBottom(int leading)
Sets the bottom leading (border).
QList< Legend * > allLegends()
Returns a list with all legends.
HeaderFooter * firstHeaderFooter()
Returns the first of all headers.
RingDiagram * ringDiagram()
If the current diagram is a RingDiagram, it is returned; otherwise 0 is returned.
void addHeaderFooter(const QString &text, HeaderFooter::HeaderFooterType type, Position position)
Adds a new header/footer with the given text to the position.
PolarDiagram * polarDiagram()
If the current diagram is a PolarDiagram, it is returned; otherwise 0 is returned.
void setSubType(KChart::Widget::SubType subType)
Sets the type of the chart without changing the main type.
Widget(QWidget *parent=nullptr)
Standard Qt-style Constructor.
void setType(KChart::Widget::ChartType chartType, KChart::Widget::SubType subType=Normal)
Sets the type of the chart.
Legend * legend()
Returns the first of all legends.
ChartType type() const
Returns the type of the chart.
void setDataset(int column, const QVector< qreal > &data, const QString &title=QString())
Sets the data in the given column using a QVector of qreal for the Y values.
void setDataCell(int row, int column, qreal data)
Sets the Y value data for a given cell.
int globalLeadingRight() const
Returns the right leading (border).
~Widget() override
Destructor.
int globalLeadingTop() const
Returns the top leading (border).
void setGlobalLeadingTop(int leading)
Sets the top leading (border).
void setGlobalLeadingLeft(int leading)
Sets the left leading (border).
void addLegend(Position position)
Adds an empty legend on the given position.
virtual int columnCount(const QModelIndex &parent) const const=0
virtual bool insertColumns(int column, int count, const QModelIndex &parent)
virtual bool insertRows(int row, int count, const QModelIndex &parent)
virtual int rowCount(const QModelIndex &parent) const const=0
qsizetype size() const const
T qobject_cast(QObject *object)
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const override
virtual bool setData(const QModelIndex &index, const QVariant &value, int role) override
virtual bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role) override
bool isEmpty() const const
DisplayRole
Horizontal
void setParent(QWidget *parent)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:14:24 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.