KChart

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

KDE's Doxygen guidelines are available online.