KD Chart API Documentation  3.1
KDChartWidget.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** This file is part of the KD Chart library.
4 **
5 ** SPDX-FileCopyrightText: 2001 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
6 **
7 ** SPDX-License-Identifier: MIT
8 **
9 ****************************************************************************/
10 
11 #include <KDChartWidget.h>
12 #include <KDChartWidget_p.h>
13 
15 #include <KDChartAbstractDiagram.h>
16 #include <KDChartBarDiagram.h>
17 #include <KDChartChart.h>
18 #include <KDChartLegend.h>
19 #include <KDChartLineDiagram.h>
20 #include <KDChartPieDiagram.h>
21 #include <KDChartPlotter.h>
22 #include <KDChartPolarDiagram.h>
23 #include <KDChartRingDiagram.h>
24 
25 #include <QDebug>
26 
27 #include <KDABLibFakes>
28 
29 #define d d_func()
30 
31 using namespace KDChart;
32 
33 Widget::Private::Private(Widget *qq)
34  : q(qq)
35  , layout(q)
36  , m_model(q)
37  , m_chart(q)
38  , m_cartPlane(&m_chart)
39  , m_polPlane(&m_chart)
40  , usedDatasetWidth(0)
41 {
42  KDAB_SET_OBJECT_NAME(layout);
43  KDAB_SET_OBJECT_NAME(m_model);
44  KDAB_SET_OBJECT_NAME(m_chart);
45 
46  layout.addWidget(&m_chart);
47 }
48 
49 Widget::Private::~Private()
50 {
51 }
52 
65 Widget::Widget(QWidget *parent)
66  : QWidget(parent)
67  , _d(new Private(this))
68 {
69  // as default we have a cartesian coordinate plane ...
70  // ... and a line diagram
71  setType(Line);
72 }
73 
78 {
79  delete _d;
80  _d = nullptr;
81 }
82 
83 void Widget::init()
84 {
85 }
86 
87 void Widget::setDataset(int column, const QVector<qreal> &data, const QString &title)
88 {
89  if (!checkDatasetWidth(1))
90  return;
91 
92  QStandardItemModel &model = d->m_model;
93 
94  justifyModelSize(data.size(), column + 1);
95 
96  for (int i = 0; i < data.size(); ++i) {
97  const QModelIndex index = model.index(i, column);
98  model.setData(index, QVariant(data[i]), Qt::DisplayRole);
99  }
100  if (!title.isEmpty())
101  model.setHeaderData(column, Qt::Horizontal, QVariant(title));
102 }
103 
104 void Widget::setDataset(int column, const QVector<QPair<qreal, qreal>> &data, const QString &title)
105 {
106  if (!checkDatasetWidth(2))
107  return;
108 
109  QStandardItemModel &model = d->m_model;
110 
111  justifyModelSize(data.size(), (column + 1) * 2);
112 
113  for (int i = 0; i < data.size(); ++i) {
114  QModelIndex index = model.index(i, column * 2);
115  model.setData(index, QVariant(data[i].first), Qt::DisplayRole);
116 
117  index = model.index(i, column * 2 + 1);
118  model.setData(index, QVariant(data[i].second), Qt::DisplayRole);
119  }
120  if (!title.isEmpty()) {
121  model.setHeaderData(column, Qt::Horizontal, QVariant(title));
122  }
123 }
124 
125 void Widget::setDataCell(int row, int column, qreal data)
126 {
127  if (!checkDatasetWidth(1))
128  return;
129 
130  QStandardItemModel &model = d->m_model;
131 
132  justifyModelSize(row + 1, column + 1);
133 
134  const QModelIndex index = model.index(row, column);
135  model.setData(index, QVariant(data), Qt::DisplayRole);
136 }
137 
138 void Widget::setDataCell(int row, int column, QPair<qreal, qreal> data)
139 {
140  if (!checkDatasetWidth(2))
141  return;
142 
143  QStandardItemModel &model = d->m_model;
144 
145  justifyModelSize(row + 1, (column + 1) * 2);
146 
147  QModelIndex index = model.index(row, column * 2);
148  model.setData(index, QVariant(data.first), Qt::DisplayRole);
149 
150  index = model.index(row, column * 2 + 1);
151  model.setData(index, QVariant(data.second), Qt::DisplayRole);
152 }
153 
154 /*
155  * Resets all data.
156  */
158 {
159  d->m_model.clear();
160  d->usedDatasetWidth = 0;
161 }
162 
166 void Widget::setGlobalLeading(int left, int top, int right, int bottom)
167 {
168  d->m_chart.setGlobalLeading(left, top, right, bottom);
169 }
170 
175 {
176  d->m_chart.setGlobalLeadingLeft(leading);
177 }
178 
183 {
184  return d->m_chart.globalLeadingLeft();
185 }
186 
191 {
192  d->m_chart.setGlobalLeadingTop(leading);
193 }
194 
199 {
200  return d->m_chart.globalLeadingTop();
201 }
202 
207 {
208  d->m_chart.setGlobalLeadingRight(leading);
209 }
210 
215 {
216  return d->m_chart.globalLeadingRight();
217 }
218 
223 {
224  d->m_chart.setGlobalLeadingBottom(leading);
225 }
226 
231 {
232  return d->m_chart.globalLeadingBottom();
233 }
234 
239 {
240  return d->m_chart.headerFooter();
241 }
242 
246 QList<KDChart::HeaderFooter *> Widget::allHeadersFooters()
247 {
248  return d->m_chart.headerFooters();
249 }
250 
254 void Widget::addHeaderFooter(const QString &text,
256  Position position)
257 {
258  auto *newHeader = new HeaderFooter(&d->m_chart);
259  newHeader->setType(type);
260  newHeader->setPosition(position);
261  newHeader->setText(text);
262  d->m_chart.addHeaderFooter(newHeader); // we need this explicit call !
263 }
264 
269 {
270  header->setParent(&d->m_chart);
271  d->m_chart.addHeaderFooter(header); // we need this explicit call !
272 }
273 
275 {
276  header->setParent(&d->m_chart);
277  d->m_chart.replaceHeaderFooter(header, oldHeader);
278 }
279 
281 {
282  d->m_chart.takeHeaderFooter(header);
283 }
284 
289 {
290  return d->m_chart.legend();
291 }
292 
296 QList<KDChart::Legend *> Widget::allLegends()
297 {
298  return d->m_chart.legends();
299 }
300 
305 {
306  auto *legend = new Legend(diagram(), &d->m_chart);
307  legend->setPosition(position);
308  d->m_chart.addLegend(legend);
309 }
310 
315 {
317  legend->setParent(&d->m_chart);
318  d->m_chart.addLegend(legend);
319 }
320 
321 void Widget::replaceLegend(Legend *legend, Legend *oldLegend)
322 {
324  legend->setParent(&d->m_chart);
325  d->m_chart.replaceLegend(legend, oldLegend);
326 }
327 
329 {
330  d->m_chart.takeLegend(legend);
331 }
332 
334 {
335  if (coordinatePlane() == nullptr)
336  qDebug() << "diagram(): coordinatePlane() was NULL";
337 
338  return coordinatePlane()->diagram();
339 }
340 
342 {
343  return dynamic_cast<BarDiagram *>(diagram());
344 }
346 {
347  return dynamic_cast<LineDiagram *>(diagram());
348 }
350 {
351  return dynamic_cast<Plotter *>(diagram());
352 }
354 {
355  return dynamic_cast<PieDiagram *>(diagram());
356 }
358 {
359  return dynamic_cast<RingDiagram *>(diagram());
360 }
362 {
363  return dynamic_cast<PolarDiagram *>(diagram());
364 }
365 
367 {
368  return d->m_chart.coordinatePlane();
369 }
370 
372 {
373  return (type == KDChart::Widget::Bar) || (type == KDChart::Widget::Line);
374 }
375 
377 {
378  return (type == KDChart::Widget::Pie)
379  || (type == KDChart::Widget::Ring)
380  || (type == KDChart::Widget::Polar);
381 }
382 
383 void Widget::setType(ChartType chartType, SubType chartSubType)
384 {
385  AbstractDiagram *diag = nullptr;
386  const ChartType oldType = type();
387 
388  if (chartType != oldType) {
389  if (chartType != NoType) {
390  if (isCartesian(chartType) && !isCartesian(oldType)) {
391  if (coordinatePlane() == &d->m_polPlane) {
392  d->m_chart.takeCoordinatePlane(&d->m_polPlane);
393  d->m_chart.addCoordinatePlane(&d->m_cartPlane);
394  } else {
395  d->m_chart.replaceCoordinatePlane(&d->m_cartPlane);
396  }
397  } else if (isPolar(chartType) && !isPolar(oldType)) {
398  if (coordinatePlane() == &d->m_cartPlane) {
399  d->m_chart.takeCoordinatePlane(&d->m_cartPlane);
400  d->m_chart.addCoordinatePlane(&d->m_polPlane);
401  } else {
402  d->m_chart.replaceCoordinatePlane(&d->m_polPlane);
403  }
404  }
405  }
406  switch (chartType) {
407  case Bar:
408  diag = new BarDiagram(&d->m_chart, &d->m_cartPlane);
409  break;
410  case Line:
411  diag = new LineDiagram(&d->m_chart, &d->m_cartPlane);
412  break;
413  case Plot:
414  diag = new Plotter(&d->m_chart, &d->m_cartPlane);
415  break;
416  case Pie:
417  diag = new PieDiagram(&d->m_chart, &d->m_polPlane);
418  break;
419  case Polar:
420  diag = new PolarDiagram(&d->m_chart, &d->m_polPlane);
421  break;
422  case Ring:
423  diag = new RingDiagram(&d->m_chart, &d->m_polPlane);
424  break;
425  case NoType:
426  break;
427  }
428  if (diag != nullptr) {
429  if (isCartesian(oldType) && isCartesian(chartType)) {
430  auto *oldDiag =
431  qobject_cast<AbstractCartesianDiagram *>(coordinatePlane()->diagram());
432  auto *newDiag =
433  qobject_cast<AbstractCartesianDiagram *>(diag);
434  const auto constAxes = oldDiag->axes();
435  for (CartesianAxis *axis : constAxes) {
436  oldDiag->takeAxis(axis);
437  newDiag->addAxis(axis);
438  }
439  }
440 
441  const auto constLegends = d->m_chart.legends();
442  for (Legend *l : constLegends) {
443  l->setDiagram(diag);
444  }
445 
446  diag->setModel(&d->m_model);
448 
449  // checkDatasetWidth( d->usedDatasetWidth );
450  }
451  // coordinatePlane()->setGridNeedsRecalculate();
452  }
453 
454  if (chartType != NoType) {
455  if (chartType != oldType || chartSubType != subType())
456  setSubType(chartSubType);
457  d->m_chart.resize(size()); // triggering immediate update
458  }
459 }
460 
461 template<class DiagramType, class Subtype>
462 void setSubtype(AbstractDiagram *_dia, Subtype st)
463 {
464  if (auto *dia = qobject_cast<DiagramType *>(_dia)) {
465  dia->setType(st);
466  }
467 }
468 
470 {
471  // ### at least PieDiagram, PolarDiagram and RingDiagram are unhandled here
472 
473  AbstractDiagram *dia = diagram();
474  switch (subType) {
475  case Normal:
476  setSubtype<BarDiagram>(dia, BarDiagram::Normal);
477  setSubtype<LineDiagram>(dia, LineDiagram::Normal);
478  setSubtype<Plotter>(dia, Plotter::Normal);
479  break;
480  case Stacked:
481  setSubtype<BarDiagram>(dia, BarDiagram::Stacked);
482  setSubtype<LineDiagram>(dia, LineDiagram::Stacked);
483  // setSubtype< Plotter >( dia, Plotter::Stacked );
484  break;
485  case Percent:
486  setSubtype<BarDiagram>(dia, BarDiagram::Percent);
487  setSubtype<LineDiagram>(dia, LineDiagram::Percent);
488  setSubtype<Plotter>(dia, Plotter::Percent);
489  break;
490  case Rows:
491  setSubtype<BarDiagram>(dia, BarDiagram::Rows);
492  break;
493  default:
494  Q_ASSERT_X(false, "Widget::setSubType", "Sub-type not supported!");
495  break;
496  }
497 }
498 
503 {
504  // PENDING(christoph) save the type out-of-band:
505  AbstractDiagram *const dia = const_cast<Widget *>(this)->diagram();
506  if (qobject_cast<BarDiagram *>(dia))
507  return Bar;
508  else if (qobject_cast<LineDiagram *>(dia))
509  return Line;
510  else if (qobject_cast<Plotter *>(dia))
511  return Plot;
512  else if (qobject_cast<PieDiagram *>(dia))
513  return Pie;
514  else if (qobject_cast<PolarDiagram *>(dia))
515  return Polar;
516  else if (qobject_cast<RingDiagram *>(dia))
517  return Ring;
518  else
519  return NoType;
520 }
521 
523 {
524  // PENDING(christoph) save the type out-of-band:
525  Widget::SubType retVal = Normal;
526 
527  AbstractDiagram *const dia = const_cast<Widget *>(this)->diagram();
528  auto *barDia = qobject_cast<BarDiagram *>(dia);
529  auto *lineDia = qobject_cast<LineDiagram *>(dia);
530  auto *plotterDia = qobject_cast<Plotter *>(dia);
531 
532  // FIXME(khz): Add the impl for these chart types - or remove them from here:
533  // PieDiagram* pieDia = qobject_cast< PieDiagram* >( diagram() );
534  // PolarDiagram* polarDia = qobject_cast< PolarDiagram* >( diagram() );
535  // RingDiagram* ringDia = qobject_cast< RingDiagram* >( diagram() );
536 
537 #define TEST_SUB_TYPE(DIAGRAM, INTERNALSUBTYPE, SUBTYPE) \
538  { \
539  if (DIAGRAM && DIAGRAM->type() == INTERNALSUBTYPE) \
540  retVal = SUBTYPE; \
541  }
542  const Widget::ChartType mainType = type();
543  switch (mainType) {
544  case Bar:
548  TEST_SUB_TYPE(barDia, BarDiagram::Rows, Rows);
549  break;
550  case Line:
554  break;
555  case Plot:
556  TEST_SUB_TYPE(plotterDia, Plotter::Normal, Normal);
557  TEST_SUB_TYPE(plotterDia, Plotter::Percent, Percent);
558  break;
559  case Pie:
560  // no impl. yet
561  break;
562  case Polar:
563  // no impl. yet
564  break;
565  case Ring:
566  // no impl. yet
567  break;
568  default:
569  Q_ASSERT_X(false,
570  "Widget::subType", "Chart type not supported!");
571  break;
572  }
573  return retVal;
574 }
575 
579 bool Widget::checkDatasetWidth(int width)
580 {
581  if (width == diagram()->datasetDimension()) {
582  d->usedDatasetWidth = width;
583  return true;
584  }
585  qDebug() << "The current diagram type doesn't support this data dimension.";
586  return false;
587  /* if ( d->usedDatasetWidth == width || d->usedDatasetWidth == 0 ) {
588  d->usedDatasetWidth = width;
589  diagram()->setDatasetDimension( width );
590  return true;
591  }
592  qDebug() << "It's impossible to mix up the different setDataset() methods on the same widget.";
593  return false;*/
594 }
595 
599 void Widget::justifyModelSize(int rows, int columns)
600 {
601  QAbstractItemModel &model = d->m_model;
602  const int currentRows = model.rowCount();
603  const int currentCols = model.columnCount();
604 
605  if (currentCols < columns)
606  if (!model.insertColumns(currentCols, columns - currentCols))
607  qDebug() << "justifyModelSize: could not increase model size.";
608  if (currentRows < rows)
609  if (!model.insertRows(currentRows, rows - currentRows))
610  qDebug() << "justifyModelSize: could not increase model size.";
611 
612  Q_ASSERT(model.rowCount() >= rows);
613  Q_ASSERT(model.columnCount() >= columns);
614 }
#define KDAB_SET_OBJECT_NAME(x)
Definition: KDChartGlobal.h:35
#define d
void setSubtype(AbstractDiagram *_dia, Subtype st)
static bool isCartesian(KDChart::Widget::ChartType type)
#define TEST_SUB_TYPE(DIAGRAM, INTERNALSUBTYPE, SUBTYPE)
static bool isPolar(KDChart::Widget::ChartType type)
Base class common for all coordinate planes, CartesianCoordinatePlane, PolarCoordinatePlane,...
virtual void replaceDiagram(AbstractDiagram *diagram, AbstractDiagram *oldDiagram=nullptr)
AbstractDiagram defines the interface for diagram classes.
void setModel(QAbstractItemModel *model) override
BarDiagram defines a common bar diagram.
A header or footer displaying text above or below charts.
void setParent(QObject *parent)
Legend defines the interface for the legend drawing class.
Definition: KDChartLegend.h:44
void setDiagram(KDChart::AbstractDiagram *newDiagram)
A convenience method doing the same as replaceDiagram( newDiagram, 0 );.
void setPosition(Position position)
Specify the position of a non-floating legend.
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 KD Chart widget for usage without Model/View.
Definition: KDChartWidget.h:45
void replaceHeaderFooter(HeaderFooter *header, HeaderFooter *oldHeader=nullptr)
void setSubType(SubType subType)
Sets the type of the chart without changing the main type.
BarDiagram * barDiagram()
AbstractDiagram * diagram()
void setGlobalLeading(int left, int top, int right, int bottom)
int globalLeadingBottom() const
int globalLeadingLeft() const
Plotter * plotter()
SubType subType() const
void takeHeaderFooter(HeaderFooter *header)
PieDiagram * pieDiagram()
AbstractCoordinatePlane * coordinatePlane()
void setGlobalLeadingRight(int leading)
LineDiagram * lineDiagram()
void replaceLegend(Legend *legend, Legend *oldLegend=nullptr)
void setGlobalLeadingBottom(int leading)
RingDiagram * ringDiagram()
HeaderFooter * firstHeaderFooter()
void addHeaderFooter(const QString &text, HeaderFooter::HeaderFooterType type, Position position)
QList< Legend * > allLegends()
QList< HeaderFooter * > allHeadersFooters()
PolarDiagram * polarDiagram()
void setType(ChartType chartType, SubType subType=Normal)
void takeLegend(Legend *legend)
ChartType type() const
void setDataset(int column, const QVector< qreal > &data, const QString &title=QString())
void setDataCell(int row, int column, qreal data)
int globalLeadingRight() const
~Widget() override
int globalLeadingTop() const
void setGlobalLeadingTop(int leading)
void setGlobalLeadingLeft(int leading)
void addLegend(Position position)

© 2001 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/
Generated by doxygen 1.9.1