KD Chart API Documentation 3.0
Loading...
Searching...
No Matches
KDChartLeveyJenningsDiagram.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
12#include "KDChartLeveyJenningsDiagram_p.h"
13
14#include "KDChartAbstractGrid.h"
15#include "KDChartChart.h"
16#include "KDChartPainterSaver_p.h"
18
19#include <QDateTime>
20#include <QFontMetrics>
21#include <QPainter>
22#include <QSvgRenderer>
23#include <QVector>
24
25#include <KDABLibFakes>
26
27using namespace KDChart;
28using namespace std;
29
30LeveyJenningsDiagram::Private::Private()
31{
32}
33
34LeveyJenningsDiagram::Private::~Private()
35{
36}
37
38#define d d_func()
39
41 : LineDiagram(new Private(), parent, plane)
42{
43 init();
44}
45
46void LeveyJenningsDiagram::init()
47{
48 d->lotChangedPosition = Qt::AlignTop;
49 d->fluidicsPackChangedPosition = Qt::AlignBottom;
50 d->sensorChangedPosition = Qt::AlignBottom;
51
52 d->scanLinePen = QPen(Qt::blue);
53 setPen(d->scanLinePen);
54
55 d->expectedMeanValue = 0.0;
56 d->expectedStandardDeviation = 0.0;
57
58 d->diagram = this;
59
60 d->icons[LotChanged] = QString::fromLatin1(":/KDAB/kdchart/LeveyJennings/karo_black.svg");
61 d->icons[SensorChanged] = QString::fromLatin1(":/KDAB/kdchart/LeveyJennings/karo_red.svg");
62 d->icons[FluidicsPackChanged] = QString::fromLatin1(":/KDAB/kdchart/LeveyJennings/karo_blue.svg");
63 d->icons[OkDataPoint] = QString::fromLatin1(":/KDAB/kdchart/LeveyJennings/circle_blue.svg");
64 d->icons[NotOkDataPoint] = QString::fromLatin1(":/KDAB/kdchart/LeveyJennings/circle_blue_red.svg");
65
67}
68
72
77{
78 auto *newDiagram = new LeveyJenningsDiagram(new Private(*d));
79 return newDiagram;
80}
81
83{
84 if (other == this)
85 return true;
86 if (!other) {
87 return false;
88 }
89 /*
90 qDebug() <<"\n LineDiagram::compare():";
91 // compare own properties
92 qDebug() << (type() == other->type());
93 */
94 return // compare the base class
95 (static_cast<const LineDiagram *>(this)->compare(other));
96}
97
103{
104 if (d->lotChangedPosition == pos)
105 return;
106
107 d->lotChangedPosition = pos;
108 update();
109}
110
115{
116 return d->lotChangedPosition;
117}
118
124{
125 if (d->fluidicsPackChangedPosition == pos)
126 return;
127
128 d->fluidicsPackChangedPosition = pos;
129 update();
130}
131
136{
137 return d->fluidicsPackChangedPosition;
138}
139
145{
146 if (d->sensorChangedPosition == pos)
147 return;
148
149 d->sensorChangedPosition = pos;
150 update();
151}
152
157{
158 return d->sensorChangedPosition;
159}
160
165{
166 if (d->fluidicsPackChanges == changes)
167 return;
168
169 d->fluidicsPackChanges = changes;
170 update();
171}
172
177{
178 return d->fluidicsPackChanges;
179}
180
185{
186 if (d->sensorChanges == changes)
187 return;
188
189 d->sensorChanges = changes;
190 update();
191}
192
197{
198 if (d->scanLinePen == pen)
199 return;
200
201 d->scanLinePen = pen;
202 update();
203}
204
209{
210 return d->scanLinePen;
211}
212
217{
218 return d->icons[symbol];
219}
220
224void LeveyJenningsDiagram::setSymbol(Symbol symbol, const QString &filename)
225{
226 if (d->icons[symbol] == filename)
227 return;
228
229 delete d->iconRenderer[symbol];
230 d->iconRenderer[symbol] = 0;
231
232 d->icons[symbol] = filename;
233
234 update();
235}
236
241{
242 return d->sensorChanges;
243}
244
249{
250 if (d->expectedMeanValue == meanValue)
251 return;
252
253 d->expectedMeanValue = meanValue;
254 d->setYAxisRange();
255 update();
256}
257
262{
263 return d->expectedMeanValue;
264}
265
270{
271 if (d->expectedStandardDeviation == sd)
272 return;
273
274 d->expectedStandardDeviation = sd;
275 d->setYAxisRange();
276 update();
277}
278
283{
284 return d->expectedStandardDeviation;
285}
286
291{
292 return d->calculatedMeanValue;
293}
294
299{
300 return d->calculatedStandardDeviation;
301}
302
304{
305 if (this->model() != nullptr) {
306 disconnect(this->model(), SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)),
308 disconnect(this->model(), SIGNAL(rowsInserted(const QModelIndex &, int, int)),
310 disconnect(this->model(), SIGNAL(rowsRemoved(const QModelIndex &, int, int)),
312 disconnect(this->model(), SIGNAL(columnsInserted(const QModelIndex &, int, int)),
314 disconnect(this->model(), SIGNAL(columnsRemoved(const QModelIndex &, int, int)),
316 disconnect(this->model(), SIGNAL(modelReset()),
320 }
322 if (this->model() != nullptr) {
323 connect(this->model(), SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)),
325 connect(this->model(), SIGNAL(rowsInserted(const QModelIndex &, int, int)),
327 connect(this->model(), SIGNAL(rowsRemoved(const QModelIndex &, int, int)),
329 connect(this->model(), SIGNAL(columnsInserted(const QModelIndex &, int, int)),
331 connect(this->model(), SIGNAL(columnsRemoved(const QModelIndex &, int, int)),
333 connect(this->model(), SIGNAL(modelReset()),
335 connect(this->model(), SIGNAL(layoutChanged()),
337
339 }
340}
341
342// TODO: This is the 'easy' solution
343// evaluate whether this is enough or we need some better one or even boost here
345{
346 QVector<qreal> values;
347 // first fetch all values
348 const QAbstractItemModel &m = *model();
349 const int rowCount = m.rowCount(rootIndex());
350
351 for (int row = 0; row < rowCount; ++row) {
352 const QVariant var = m.data(m.index(row, 1, rootIndex()));
353 if (!var.isValid())
354 continue;
355 const qreal value = var.toReal();
356 if (ISNAN(value))
357 continue;
358 values << value;
359 }
360
361 qreal sum = 0.0;
362 qreal sumSquares = 0.0;
363 Q_FOREACH (qreal value, values) {
364 sum += value;
365 sumSquares += value * value;
366 }
367
368 const int N = values.count();
369
370 d->calculatedMeanValue = sum / N;
371 d->calculatedStandardDeviation = sqrt((static_cast<qreal>(N) * sumSquares - sum * sum) / (N * (N - 1)));
372}
373
374// calculates the largest QDate not greater than \a dt.
375static QDate floorDay(const QDateTime &dt)
376{
377 return dt.date();
378}
379
380// calculates the smallest QDate not less than \a dt.
381static QDate ceilDay(const QDateTime &dt)
382{
383 QDate result = dt.date();
384
385 if (QDateTime(result, QTime()) < dt)
386 result = result.addDays(1);
387
388 return result;
389}
390
391// calculates the largest QDateTime like xx:00 not greater than \a dt.
393{
394 return QDateTime(dt.date(), QTime(dt.time().hour(), 0));
395}
396
397// calculates the smallest QDateTime like xx:00 not less than \a dt.
398static QDateTime ceilHour(const QDateTime &dt)
399{
400 QDateTime result(dt.date(), QTime(dt.time().hour(), 0));
401
402 if (result < dt)
403 result = result.addSecs(3600);
404
405 return result;
406}
407
410{
411 const qreal yMin = d->expectedMeanValue - 4 * d->expectedStandardDeviation;
412 const qreal yMax = d->expectedMeanValue + 4 * d->expectedStandardDeviation;
413
414 d->setYAxisRange();
415
416 // rounded down/up to the prev/next midnight (at least that's the default)
418 const qint64 minTime = range.first.toSecsSinceEpoch();
419 const qint64 maxTime = range.second.toSecsSinceEpoch();
420
421 const qreal xMin = static_cast<qreal>(minTime / 24 * 60 * 60);
422 const qreal xMax = static_cast<qreal>(maxTime / 24 * 60 * 60) - xMin;
423
424 const QPointF bottomLeft(QPointF(0, yMin));
425 const QPointF topRight(QPointF(xMax, yMax));
426
427 return QPair<QPointF, QPointF>(bottomLeft, topRight);
428}
429
434{
435 if (d->timeRange != QPair<QDateTime, QDateTime>())
436 return d->timeRange;
437
438 const QAbstractItemModel &m = *model();
439 const int rowCount = m.rowCount(rootIndex());
440
441 const QDateTime begin = m.data(m.index(0, 3, rootIndex())).toDateTime();
442 const QDateTime end = m.data(m.index(rowCount - 1, 3, rootIndex())).toDateTime();
443
444 if (begin.secsTo(end) > 86400) {
445 // if begin to end is more than 24h
446 // round down/up to the prev/next midnight
447 const QDate min = floorDay(begin);
448 const QDate max = ceilDay(end);
450 } else if (begin.secsTo(end) > 3600) {
451 // more than 1h: rond down up to the prex/next hour
452 // if begin to end is more than 24h
453 const QDateTime min = floorHour(begin);
454 const QDateTime max = ceilHour(end);
455 return QPair<QDateTime, QDateTime>(min, max);
456 }
457 return QPair<QDateTime, QDateTime>(begin, end);
458}
459
465{
466 if (d->timeRange == timeRange)
467 return;
468
469 d->timeRange = timeRange;
470 update();
471}
472
477{
478 const unsigned int minTime = timeRange().first.toSecsSinceEpoch();
479
480 Q_FOREACH (const QDateTime &dt, d->fluidicsPackChanges) {
481 const qreal xValue = (dt.toSecsSinceEpoch() - minTime) / static_cast<qreal>(24 * 60 * 60);
482 const QPointF point(xValue, 0.0);
484 }
485
486 Q_FOREACH (const QDateTime &dt, d->sensorChanges) {
487 const qreal xValue = (dt.toSecsSinceEpoch() - minTime) / static_cast<qreal>(24 * 60 * 60);
488 const QPointF point(xValue, 0.0);
490 }
491}
492
495{
496 d->reverseMapper.clear();
497
498 // note: Not having any data model assigned is no bug
499 // but we can not draw a diagram then either.
500 if (!checkInvariants(true))
501 return;
503 return;
504
505 QPainter *const painter = ctx->painter();
506 const PainterSaver p(painter);
507 if (model()->rowCount(rootIndex()) == 0 || model()->columnCount(rootIndex()) < 4)
508 return; // nothing to paint for us
509
510 AbstractCoordinatePlane *const plane = ctx->coordinatePlane();
511 ctx->setCoordinatePlane(plane->sharedAxisMasterPlane(painter));
512
513 const QAbstractItemModel &m = *model();
514 const int rowCount = m.rowCount(rootIndex());
515
516 const unsigned int minTime = timeRange().first.toSecsSinceEpoch();
517
519
520 int prevLot = -1;
522 bool hadMissingValue = false;
523
524 for (int row = 0; row < rowCount; ++row) {
525 const QModelIndex lotIndex = m.index(row, 0, rootIndex());
526 const QModelIndex valueIndex = m.index(row, 1, rootIndex());
527 const QModelIndex okIndex = m.index(row, 2, rootIndex());
528 const QModelIndex timeIndex = m.index(row, 3, rootIndex());
529 const QModelIndex expectedMeanIndex = m.index(row, 4, rootIndex());
530 const QModelIndex expectedSDIndex = m.index(row, 5, rootIndex());
531
532 painter->setPen(pen(lotIndex));
533
535 qreal value = vValue.toReal();
536 const int lot = m.data(lotIndex).toInt();
537 const bool ok = m.data(okIndex).toBool();
538 const QDateTime time = m.data(timeIndex).toDateTime();
539 const qreal xValue = (time.toSecsSinceEpoch() - minTime) / static_cast<qreal>(24 * 60 * 60);
540
545
546 QPointF point = ctx->coordinatePlane()->translate(QPointF(xValue, value));
547
548 if (vValue.isNull()) {
549 hadMissingValue = true;
550 } else {
551 if (!vExpectedMean.isNull() && !vExpectedSD.isNull()) {
552 // this calculates the 'logical' value relative to the expected mean and SD of this point
553 value -= expectedMean;
554 value /= expectedSD;
555 value *= d->expectedStandardDeviation;
556 value += d->expectedMeanValue;
557 point = ctx->coordinatePlane()->translate(QPointF(xValue, value));
558 }
559
560 if (prevLot == lot) {
561 const QPen pen = painter->pen();
562 QPen newPen = pen;
563
564 if (hadMissingValue) {
565 newPen.setDashPattern(QVector<qreal>() << 4.0 << 4.0);
566 }
567
568 painter->setPen(newPen);
569 painter->drawLine(prevPoint, point);
570 painter->setPen(pen);
571 // d->reverseMapper.addLine( valueIndex.row(), valueIndex.column(), prevPoint, point );
572 } else if (row > 0) {
574 }
575
576 if (value <= d->expectedMeanValue + 4 * d->expectedStandardDeviation && value >= d->expectedMeanValue - 4 * d->expectedStandardDeviation) {
577 const QPointF location(xValue, value);
578 drawDataPointSymbol(ctx, location, ok);
579 d->reverseMapper.addCircle(valueIndex.row(),
580 valueIndex.column(),
581 ctx->coordinatePlane()->translate(location),
582 iconRect().size());
583 }
584 prevLot = lot;
585 prevPoint = point;
586 hadMissingValue = false;
587 }
588
589 const QModelIndex current = selectionModel()->currentIndex();
590 if (selectionModel()->rowIntersectsSelection(lotIndex.row(), lotIndex.parent()) || current.sibling(current.row(), 0) == lotIndex) {
591 const QPen pen = ctx->painter()->pen();
592 painter->setPen(d->scanLinePen);
593 painter->drawLine(ctx->coordinatePlane()->translate(QPointF(xValue, d->expectedMeanValue - 4 * d->expectedStandardDeviation)),
594 ctx->coordinatePlane()->translate(QPointF(xValue, d->expectedMeanValue + 4 * d->expectedStandardDeviation)));
595 painter->setPen(pen);
596 }
597 }
598
600
601 ctx->setCoordinatePlane(plane);
602}
603
610{
612
613 QPainter *const painter = ctx->painter();
614 const PainterSaver ps(painter);
615 const QPointF transPos = ctx->coordinatePlane()->translate(pos).toPoint();
616 painter->translate(transPos);
617
618 painter->setClipping(false);
619 iconRenderer(type)->render(painter, iconRect());
620}
621
628{
629 const QPointF transPos = ctx->coordinatePlane()->translate(
630 QPointF(pos.x(), d->lotChangedPosition & Qt::AlignTop ? d->expectedMeanValue + 4 * d->expectedStandardDeviation : d->expectedMeanValue - 4 * d->expectedStandardDeviation));
631
632 QPainter *const painter = ctx->painter();
633 const PainterSaver ps(painter);
634 painter->setClipping(false);
635 painter->translate(transPos);
636 iconRenderer(LotChanged)->render(painter, iconRect());
637}
638
645{
646 const QPointF transPos = ctx->coordinatePlane()->translate(
647 QPointF(pos.x(), d->sensorChangedPosition & Qt::AlignTop ? d->expectedMeanValue + 4 * d->expectedStandardDeviation : d->expectedMeanValue - 4 * d->expectedStandardDeviation));
648
649 QPainter *const painter = ctx->painter();
650 const PainterSaver ps(painter);
651 painter->setClipping(false);
652 painter->translate(transPos);
653 iconRenderer(SensorChanged)->render(painter, iconRect());
654}
655
662{
663 const QPointF transPos = ctx->coordinatePlane()->translate(
664 QPointF(pos.x(), d->fluidicsPackChangedPosition & Qt::AlignTop ? d->expectedMeanValue + 4 * d->expectedStandardDeviation : d->expectedMeanValue - 4 * d->expectedStandardDeviation));
665
666 QPainter *const painter = ctx->painter();
667 const PainterSaver ps(painter);
668 painter->setClipping(false);
669 painter->translate(transPos);
670 iconRenderer(FluidicsPackChanged)->render(painter, iconRect());
671}
672
685
690{
691 if (d->iconRenderer[symbol] == 0)
692 d->iconRenderer[symbol] = new QSvgRenderer(d->icons[symbol], this);
693
694 return d->iconRenderer[symbol];
695}
static QDateTime floorHour(const QDateTime &dt)
static QDate floorDay(const QDateTime &dt)
static QDateTime ceilHour(const QDateTime &dt)
static QDate ceilDay(const QDateTime &dt)
@ MeasureCalculationModeAuto
Base class common for all coordinate planes, CartesianCoordinatePlane, PolarCoordinatePlane,...
virtual AbstractCoordinatePlane * sharedAxisMasterPlane(QPainter *p=nullptr)
virtual bool checkInvariants(bool justReturnTheStatus=false) const
void setPen(const QModelIndex &index, const QPen &pen)
void layoutChanged(AbstractDiagram *)
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 dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector< int > &roles=QVector< int >()) override
AbstractCoordinatePlane * coordinatePlane() const
static bool isBoundariesValid(const QRectF &r)
Levey Jennings coordinate plane This is actually nothing real more than a plain cartesian coordinate ...
LeveyDiagram defines a Levey Jennings chart.
LeveyJenningsDiagram(QWidget *parent=nullptr, LeveyJenningsCoordinatePlane *plane=nullptr)
void setModel(QAbstractItemModel *model) override
void setLotChangedSymbolPosition(Qt::Alignment pos)
void setFluidicsPackChanges(const QVector< QDateTime > &changes)
void drawChanges(PaintContext *paintContext)
void setFluidicsPackChangedSymbolPosition(Qt::Alignment pos)
QVector< QDateTime > fluidicsPackChanges() const
QVector< QDateTime > sensorChanges() const
Qt::Alignment fluidicsPackChangedSymbolPosition() const
void paint(PaintContext *paintContext) override
virtual void drawDataPointSymbol(PaintContext *paintContext, const QPointF &pos, bool ok)
virtual void drawFluidicsPackChangedSymbol(PaintContext *paintContext, const QPointF &pos)
LineDiagram * clone() const override
void setSymbol(Symbol symbol, const QString &filename)
void setSensorChangedSymbolPosition(Qt::Alignment pos)
QPair< QDateTime, QDateTime > timeRange() const
QSvgRenderer * iconRenderer(Symbol symbol)
void setTimeRange(const QPair< QDateTime, QDateTime > &timeRange)
bool compare(const LeveyJenningsDiagram *other) const
const QPair< QPointF, QPointF > calculateDataBoundaries() const override
void setSensorChanges(const QVector< QDateTime > &changes)
virtual void drawSensorChangedSymbol(PaintContext *paintContext, const QPointF &pos)
virtual void drawLotChangeSymbol(PaintContext *paintContext, const QPointF &pos)
LineDiagram defines a common line diagram.
Measure is used to specify relative and absolute sizes in KDChart, e.g. font sizes.
Stores information about painting diagrams.
A set of text attributes.
void setFontSize(const Measure &measure)
virtual QVariant data(const QModelIndex &index, int role) const const=0
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const=0
virtual int rowCount(const QModelIndex &parent) const const=0
QAbstractItemModel * model() const const
QModelIndex rootIndex() const const
virtual void rowsInserted(const QModelIndex &parent, int start, int end)
void setSelectionMode(QAbstractItemView::SelectionMode mode)
QItemSelectionModel * selectionModel() const const
virtual void setModel(QAbstractItemModel *model)
QDate addDays(qint64 ndays) const const
QDateTime startOfDay(Qt::TimeSpec spec, int offsetSeconds) const const
QDateTime addSecs(qint64 s) const const
QDate date() const const
qint64 secsTo(const QDateTime &other) const const
QTime time() const const
qint64 toSecsSinceEpoch() const const
QModelIndex currentIndex() const const
int row() const const
QModelIndex sibling(int row, int column) const const
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
QObject * parent() const const
T qobject_cast(QObject *object)
void drawLine(const QLineF &line)
const QPen & pen() const const
void setClipping(bool enable)
void setPen(const QColor &color)
void setRenderHint(QPainter::RenderHint hint, bool on)
void translate(const QPointF &offset)
void setDashPattern(const QVector< qreal > &pattern)
QPoint toPoint() const const
QString fromLatin1(const char *str, int size)
AlignTop
int hour() const const
bool toBool() const const
QDateTime toDateTime() const const
int toInt(bool *ok) const const
qreal toReal(bool *ok) const const
int count(const T &value) const const

© 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 on Fri Feb 23 2024 00:02:58 for KD Chart API Documentation by doxygen 1.9.8