KD Chart 2  [rev.2.7]
KDChartWidget.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 ** Copyright (C) 2001-2020 Klaralvdalens Datakonsult AB. All rights reserved.
3 **
4 ** This file is part of the KD Chart library.
5 **
6 ** Licensees holding valid commercial KD Chart licenses may use this file in
7 ** accordance with the KD Chart Commercial License Agreement provided with
8 ** the Software.
9 **
10 **
11 ** This file may be distributed and/or modified under the terms of the
12 ** GNU General Public License version 2 and version 3 as published by the
13 ** Free Software Foundation and appearing in the file LICENSE.GPL.txt included.
14 **
15 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
16 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
17 **
18 ** Contact info@kdab.com if any conditions of this licensing are not
19 ** clear to you.
20 **
21 **********************************************************************/
22 
23 #include <KDChartWidget.h>
24 #include <KDChartWidget_p.h>
25 
26 #include <KDChartAbstractDiagram.h>
27 #include <KDChartBarDiagram.h>
28 #include <KDChartChart.h>
30 #include <KDChartLineDiagram.h>
31 #include <KDChartPlotter.h>
32 #include <KDChartPieDiagram.h>
33 #include <KDChartPolarDiagram.h>
34 #include <KDChartRingDiagram.h>
35 #include <KDChartLegend.h>
36 
37 #include <QDebug>
38 
39 #include <KDABLibFakes>
40 
41 #define d d_func()
42 
43 using namespace KDChart;
44 
45 Widget::Private::Private( Widget * qq )
46  : q( qq ),
47  layout( q ),
48  m_model( q ),
49  m_chart( q ),
50  m_cartPlane( &m_chart ),
51  m_polPlane( &m_chart ),
52  usedDatasetWidth( 0 )
53 {
54  KDAB_SET_OBJECT_NAME( layout );
55  KDAB_SET_OBJECT_NAME( m_model );
56  KDAB_SET_OBJECT_NAME( m_chart );
57 
58  layout.addWidget( &m_chart );
59 }
60 
61 Widget::Private::~Private() {}
62 
63 
76 Widget::Widget( QWidget* parent ) :
77  QWidget(parent), _d( new Private( this ) )
78 {
79  // as default we have a cartesian coordinate plane ...
80  // ... and a line diagram
81  setType( Line );
82 }
83 
88 {
89  delete _d; _d = 0;
90 }
91 
92 void Widget::init()
93 {
94 }
95 
96 void Widget::setDataset( int column, const QVector< qreal > & data, const QString& title )
97 {
98  if ( ! checkDatasetWidth( 1 ) )
99  return;
100 
101  QStandardItemModel & model = d->m_model;
102 
103  justifyModelSize( data.size(), column + 1 );
104 
105  for ( int i = 0; i < data.size(); ++i )
106  {
107  const QModelIndex index = model.index( i, column );
108  model.setData( index, QVariant( data[i] ), Qt::DisplayRole );
109  }
110  if ( ! title.isEmpty() )
111  model.setHeaderData( column, Qt::Horizontal, QVariant( title ) );
112 }
113 
114 void Widget::setDataset( int column, const QVector< QPair< qreal, qreal > > & data, const QString& title )
115 {
116  if ( ! checkDatasetWidth( 2 ))
117  return;
118 
119  QStandardItemModel & model = d->m_model;
120 
121  justifyModelSize( data.size(), (column + 1) * 2 );
122 
123  for ( int i = 0; i < data.size(); ++i )
124  {
125  QModelIndex index = model.index( i, column * 2 );
126  model.setData( index, QVariant( data[i].first ), Qt::DisplayRole );
127 
128  index = model.index( i, column * 2 + 1 );
129  model.setData( index, QVariant( data[i].second ), Qt::DisplayRole );
130  }
131  if ( ! title.isEmpty() ) {
132  model.setHeaderData( column, Qt::Horizontal, QVariant( title ) );
133  }
134 }
135 
136 void Widget::setDataCell( int row, int column, qreal data )
137 {
138  if ( ! checkDatasetWidth( 1 ) )
139  return;
140 
141  QStandardItemModel & model = d->m_model;
142 
143  justifyModelSize( row + 1, column + 1 );
144 
145  const QModelIndex index = model.index( row, column );
146  model.setData( index, QVariant( data ), Qt::DisplayRole );
147 }
148 
149 void Widget::setDataCell( int row, int column, QPair< qreal, qreal > data )
150 {
151  if ( ! checkDatasetWidth( 2 ))
152  return;
153 
154  QStandardItemModel & model = d->m_model;
155 
156  justifyModelSize( row + 1, (column + 1) * 2 );
157 
158  QModelIndex index = model.index( row, column * 2 );
159  model.setData( index, QVariant( data.first ), Qt::DisplayRole );
160 
161  index = model.index( row, column * 2 + 1 );
162  model.setData( index, QVariant( data.second ), Qt::DisplayRole );
163 }
164 
165 /*
166  * Resets all data.
167  */
169 {
170  d->m_model.clear();
171  d->usedDatasetWidth = 0;
172 }
173 
177 void Widget::setGlobalLeading( int left, int top, int right, int bottom )
178 {
179  d->m_chart.setGlobalLeading( left, top, right, bottom );
180 }
181 
185 void Widget::setGlobalLeadingLeft( int leading )
186 {
187  d->m_chart.setGlobalLeadingLeft( leading );
188 }
189 
194 {
195  return d->m_chart.globalLeadingLeft();
196 }
197 
201 void Widget::setGlobalLeadingTop( int leading )
202 {
203  d->m_chart.setGlobalLeadingTop( leading );
204 }
205 
210 {
211  return d->m_chart.globalLeadingTop();
212 }
213 
217 void Widget::setGlobalLeadingRight( int leading )
218 {
219  d->m_chart.setGlobalLeadingRight( leading );
220 }
221 
226 {
227  return d->m_chart.globalLeadingRight();
228 }
229 
234 {
235  d->m_chart.setGlobalLeadingBottom( leading );
236 }
237 
242 {
243  return d->m_chart.globalLeadingBottom();
244 }
245 
250 {
251  return d->m_chart.headerFooter();
252 }
253 
258 {
259  return d->m_chart.headerFooters();
260 }
261 
265 void Widget::addHeaderFooter( const QString& text,
267  Position position)
268 {
269  HeaderFooter* newHeader = new HeaderFooter( &d->m_chart );
270  newHeader->setType( type );
271  newHeader->setPosition( position );
272  newHeader->setText( text );
273  d->m_chart.addHeaderFooter( newHeader ); // we need this explicit call !
274 }
275 
280 {
281  header->setParent( &d->m_chart );
282  d->m_chart.addHeaderFooter( header ); // we need this explicit call !
283 }
284 
286 {
287  header->setParent( &d->m_chart );
288  d->m_chart.replaceHeaderFooter( header, oldHeader );
289 }
290 
292 {
293  d->m_chart.takeHeaderFooter( header );
294 }
295 
300 {
301  return d->m_chart.legend();
302 }
303 
308 {
309  return d->m_chart.legends();
310 }
311 
315 void Widget::addLegend( Position position )
316 {
317  Legend* legend = new Legend( diagram(), &d->m_chart );
318  legend->setPosition( position );
319  d->m_chart.addLegend( legend );
320 }
321 
326 {
327  legend->setDiagram( diagram() );
328  legend->setParent( &d->m_chart );
329  d->m_chart.addLegend( legend );
330 }
331 
333 {
334  legend->setDiagram( diagram() );
335  legend->setParent( &d->m_chart );
336  d->m_chart.replaceLegend( legend, oldLegend );
337 }
338 
340 {
341  d->m_chart.takeLegend( legend );
342 }
343 
345 {
346  if ( coordinatePlane() == 0 )
347  qDebug() << "diagram(): coordinatePlane() was NULL";
348 
349  return coordinatePlane()->diagram();
350 }
351 
353 {
354  return dynamic_cast<BarDiagram*>( diagram() );
355 }
357 {
358  return dynamic_cast<LineDiagram*>( diagram() );
359 }
361 {
362  return dynamic_cast<Plotter*>( diagram() );
363 }
365 {
366  return dynamic_cast<PieDiagram*>( diagram() );
367 }
369 {
370  return dynamic_cast<RingDiagram*>( diagram() );
371 }
373 {
374  return dynamic_cast<PolarDiagram*>( diagram() );
375 }
376 
378 {
379  return d->m_chart.coordinatePlane();
380 }
381 
383 {
384  return (type == KDChart::Widget::Bar) || (type == KDChart::Widget::Line);
385 }
386 
388 {
389  return (type == KDChart::Widget::Pie)
390  || (type == KDChart::Widget::Ring)
391  || (type == KDChart::Widget::Polar);
392 }
393 
394 void Widget::setType( ChartType chartType, SubType chartSubType )
395 {
396  AbstractDiagram* diag = 0;
397  const ChartType oldType = type();
398 
399  if ( chartType != oldType ) {
400  if ( chartType != NoType ) {
401  if ( isCartesian( chartType ) && ! isCartesian( oldType ) )
402  {
403  if ( coordinatePlane() == &d->m_polPlane ) {
404  d->m_chart.takeCoordinatePlane( &d->m_polPlane );
405  d->m_chart.addCoordinatePlane( &d->m_cartPlane );
406  } else {
407  d->m_chart.replaceCoordinatePlane( &d->m_cartPlane );
408  }
409  }
410  else if ( isPolar( chartType ) && ! isPolar( oldType ) )
411  {
412  if ( coordinatePlane() == &d->m_cartPlane ) {
413  d->m_chart.takeCoordinatePlane( &d->m_cartPlane );
414  d->m_chart.addCoordinatePlane( &d->m_polPlane );
415  } else {
416  d->m_chart.replaceCoordinatePlane( &d->m_polPlane );
417  }
418  }
419  }
420  switch ( chartType ) {
421  case Bar:
422  diag = new BarDiagram( &d->m_chart, &d->m_cartPlane );
423  break;
424  case Line:
425  diag = new LineDiagram( &d->m_chart, &d->m_cartPlane );
426  break;
427  case Plot:
428  diag = new Plotter( &d->m_chart, &d->m_cartPlane );
429  break;
430  case Pie:
431  diag = new PieDiagram( &d->m_chart, &d->m_polPlane );
432  break;
433  case Polar:
434  diag = new PolarDiagram( &d->m_chart, &d->m_polPlane );
435  break;
436  case Ring:
437  diag = new RingDiagram( &d->m_chart, &d->m_polPlane );
438  break;
439  case NoType:
440  break;
441  }
442  if ( diag != NULL ) {
443  if ( isCartesian( oldType ) && isCartesian( chartType ) ) {
444  AbstractCartesianDiagram *oldDiag =
445  qobject_cast<AbstractCartesianDiagram*>( coordinatePlane()->diagram() );
446  AbstractCartesianDiagram *newDiag =
447  qobject_cast<AbstractCartesianDiagram*>( diag );
448  Q_FOREACH( CartesianAxis* axis, oldDiag->axes() ) {
449  oldDiag->takeAxis( axis );
450  newDiag->addAxis ( axis );
451  }
452  }
453 
454  Q_FOREACH( Legend* l, d->m_chart.legends() ) {
455  l->setDiagram( diag );
456  }
457 
458  diag->setModel( &d->m_model );
459  coordinatePlane()->replaceDiagram( diag );
460 
461  //checkDatasetWidth( d->usedDatasetWidth );
462  }
463  //coordinatePlane()->setGridNeedsRecalculate();
464  }
465 
466  if ( chartType != NoType ) {
467  if ( chartType != oldType || chartSubType != subType() )
468  setSubType( chartSubType );
469  d->m_chart.resize( size() ); // triggering immediate update
470  }
471 }
472 
473 template< class DiagramType, class Subtype >
474 void setSubtype( AbstractDiagram *_dia, Subtype st)
475 {
476  if ( DiagramType *dia = qobject_cast< DiagramType * >( _dia ) ) {
477  dia->setType( st );
478  }
479 }
480 
482 {
483  // ### at least PieDiagram, PolarDiagram and RingDiagram are unhandled here
484 
485  AbstractDiagram *dia = diagram();
486  switch ( subType ) {
487  case Normal:
488  setSubtype< BarDiagram >( dia, BarDiagram::Normal );
489  setSubtype< LineDiagram >( dia, LineDiagram::Normal );
490  setSubtype< Plotter >( dia, Plotter::Normal );
491  break;
492  case Stacked:
493  setSubtype< BarDiagram >( dia, BarDiagram::Stacked );
494  setSubtype< LineDiagram >( dia, LineDiagram::Stacked );
495  // setSubtype< Plotter >( dia, Plotter::Stacked );
496  break;
497  case Percent:
498  setSubtype< BarDiagram >( dia, BarDiagram::Percent );
499  setSubtype< LineDiagram >( dia, LineDiagram::Percent );
500  setSubtype< Plotter >( dia, Plotter::Percent );
501  break;
502  case Rows:
503  setSubtype< BarDiagram >( dia, BarDiagram::Rows );
504  break;
505  default:
506  Q_ASSERT_X ( false, "Widget::setSubType", "Sub-type not supported!" );
507  break;
508  }
509 }
510 
515 {
516  // PENDING(christoph) save the type out-of-band:
517  AbstractDiagram * const dia = const_cast<Widget*>( this )->diagram();
518  if ( qobject_cast< BarDiagram* >( dia ) )
519  return Bar;
520  else if ( qobject_cast< LineDiagram* >( dia ) )
521  return Line;
522  else if ( qobject_cast< Plotter* >( dia ) )
523  return Plot;
524  else if ( qobject_cast< PieDiagram* >( dia ) )
525  return Pie;
526  else if ( qobject_cast< PolarDiagram* >( dia ) )
527  return Polar;
528  else if ( qobject_cast< RingDiagram* >( dia ) )
529  return Ring;
530  else
531  return NoType;
532 }
533 
535 {
536  // PENDING(christoph) save the type out-of-band:
537  Widget::SubType retVal = Normal;
538 
539  AbstractDiagram * const dia = const_cast<Widget*>( this )->diagram();
540  BarDiagram* barDia = qobject_cast< BarDiagram* >( dia );
541  LineDiagram* lineDia = qobject_cast< LineDiagram* >( dia );
542  Plotter* plotterDia = qobject_cast< Plotter* >( dia );
543 
544 //FIXME(khz): Add the impl for these chart types - or remove them from here:
545 // PieDiagram* pieDia = qobject_cast< PieDiagram* >( diagram() );
546 // PolarDiagram* polarDia = qobject_cast< PolarDiagram* >( diagram() );
547 // RingDiagram* ringDia = qobject_cast< RingDiagram* >( diagram() );
548 
549 #define TEST_SUB_TYPE(DIAGRAM, INTERNALSUBTYPE, SUBTYPE) \
550 { \
551  if ( DIAGRAM && DIAGRAM->type() == INTERNALSUBTYPE ) \
552  retVal = SUBTYPE; \
553 }
554  const Widget::ChartType mainType = type();
555  switch ( mainType )
556  {
557  case Bar:
561  TEST_SUB_TYPE( barDia, BarDiagram::Rows, Rows );
562  break;
563  case Line:
567  break;
568  case Plot:
569  TEST_SUB_TYPE( plotterDia, Plotter::Normal, Normal );
570  TEST_SUB_TYPE( plotterDia, Plotter::Percent, Percent );
571  break;
572  case Pie:
573  // no impl. yet
574  break;
575  case Polar:
576  // no impl. yet
577  break;
578  case Ring:
579  // no impl. yet
580  break;
581  default:
582  Q_ASSERT_X ( false,
583  "Widget::subType", "Chart type not supported!" );
584  break;
585  }
586  return retVal;
587 }
588 
589 
593 bool Widget::checkDatasetWidth( int width )
594 {
595  if ( width == diagram()->datasetDimension() )
596  {
597  d->usedDatasetWidth = width;
598  return true;
599  }
600  qDebug() << "The current diagram type doesn't support this data dimension.";
601  return false;
602 /* if ( d->usedDatasetWidth == width || d->usedDatasetWidth == 0 ) {
603  d->usedDatasetWidth = width;
604  diagram()->setDatasetDimension( width );
605  return true;
606  }
607  qDebug() << "It's impossible to mix up the different setDataset() methods on the same widget.";
608  return false;*/
609 }
610 
614 void Widget::justifyModelSize( int rows, int columns )
615 {
616  QAbstractItemModel & model = d->m_model;
617  const int currentRows = model.rowCount();
618  const int currentCols = model.columnCount();
619 
620  if ( currentCols < columns )
621  if ( ! model.insertColumns( currentCols, columns - currentCols ))
622  qDebug() << "justifyModelSize: could not increase model size.";
623  if ( currentRows < rows )
624  if ( ! model.insertRows( currentRows, rows - currentRows ))
625  qDebug() << "justifyModelSize: could not increase model size.";
626 
627  Q_ASSERT( model.rowCount() >= rows );
628  Q_ASSERT( model.columnCount() >= columns );
629 }
Base class common for all coordinate planes, CartesianCoordinatePlane, PolarCoordinatePlane, TernaryCoordinatePlane.
SubType
Sub type values, matching the values defines for the respective Diagram classes.
void setDataCell(int row, int column, qreal data)
Sets the Y value data for a given cell.
void setDiagram(KDChart::AbstractDiagram *newDiagram)
A convenience method doing the same as replaceDiagram( newDiagram, 0 );.
void resetData()
Resets all data.
void setPosition(Position position)
Specify the position of a non-floating legend.
RingDiagram * ringDiagram()
If the current diagram is a RingDiagram, it is returnd; otherwise 0 is returned.
int globalLeadingLeft() const
Returns the left leading (border).
#define TEST_SUB_TYPE(DIAGRAM, INTERNALSUBTYPE, SUBTYPE)
HeaderFooter * firstHeaderFooter()
Returns the first of all headers.
PolarDiagram defines a common polar diagram.
static bool isPolar(KDChart::Widget::ChartType type)
BarDiagram defines a common bar diagram.
void setSubtype(AbstractDiagram *_dia, Subtype st)
Plotter defines a diagram type plotting two-dimensional data.
void replaceLegend(Legend *legend, Legend *oldLegend=0)
void setGlobalLeading(int left, int top, int right, int bottom)
Sets all global leadings (borders).
PieDiagram * pieDiagram()
If the current diagram is a Plotter, it is returnd; otherwise 0 is returned.
virtual void replaceDiagram(AbstractDiagram *diagram, AbstractDiagram *oldDiagram=0)
Replaces the old diagram, or appends the diagram, it there is none yet.
AbstractDiagram defines the interface for diagram classes.
void setText(const QString &text)
int globalLeadingTop() const
Returns the top leading (border).
AbstractDiagram * diagram()
Returns a pointer to the current diagram.
The class for cartesian axes.
#define d
~Widget() override
Destructor.
void takeHeaderFooter(HeaderFooter *header)
Remove the header (or footer, resp.) from the widget, without deleting it.
int globalLeadingRight() const
Returns the right leading (border).
QList< HeaderFooter * > allHeadersFooters()
Returns a list with all headers.
Legend defines the interface for the legend drawing class.
Definition: KDChartLegend.h:55
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 setGlobalLeadingTop(int leading)
Sets the top leading (border).
void setGlobalLeadingRight(int leading)
Sets the right leading (border).
LineDiagram defines a common line diagram.
virtual KDChart::CartesianAxisList axes() const
BarDiagram * barDiagram()
If the current diagram is a BarDiagram, it is returnd; otherwise 0 is returned.
#define KDAB_SET_OBJECT_NAME(x)
Definition: KDChartGlobal.h:45
SubType subType() const
Returns the sub-type of the chart.
void setModel(QAbstractItemModel *model) override
Associate a model with the diagram.
Base class for diagrams based on a cartesian coordianate system.
static bool isCartesian(KDChart::Widget::ChartType type)
AbstractCoordinatePlane * coordinatePlane()
Returns a pointer to the current coordinate plane.
LineDiagram * lineDiagram()
If the current diagram is a LineDiagram, it is returnd; otherwise 0 is returned.
Legend * legend()
Returns the first of all legends.
The KD Chart widget for usage without Model/View.
Definition: KDChartWidget.h:60
void addHeaderFooter(const QString &text, HeaderFooter::HeaderFooterType type, Position position)
Adds a new header/footer with the given text to the position.
int globalLeadingBottom() const
Returns the bottom leading (border).
Defines a position, using compass terminology.
void takeLegend(Legend *legend)
void replaceHeaderFooter(HeaderFooter *header, HeaderFooter *oldHeader=0)
Replaces the old header (or footer, resp.), or appends the new header or footer, it there is none yet...
ChartType type() const
Returns the type of the chart.
PieDiagram defines a common pie diagram.
Plotter * plotter()
If the current diagram is a LineDiagram, it is returnd; otherwise 0 is returned.
Class only listed here to document inheritance of some KDChart classes.
void addLegend(Position position)
Adds an empty legend on the given position.
void setSubType(SubType subType)
Sets the type of the chart without changing the main type.
void setType(ChartType chartType, SubType subType=Normal)
Sets the type of the chart.
virtual void takeAxis(CartesianAxis *axis)
Removes the axis from the diagram, without deleting it.
RingDiagram defines a common ring diagram.
PolarDiagram * polarDiagram()
If the current diagram is a PolarDiagram, it is returnd; otherwise 0 is returned. ...
QList< Legend * > allLegends()
Returns a list with all legends.
void setGlobalLeadingBottom(int leading)
Sets the bottom leading (border).
void setGlobalLeadingLeft(int leading)
Sets the left leading (border).

Klarälvdalens Datakonsult AB (KDAB)
"The Qt, C++ and OpenGL Experts"
https://www.kdab.com/

https://www.kdab.com/development-resources/qt-tools/kd-chart/