KD Chart API Documentation 3.1
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{
306 if (oldModel != nullptr) {
321 }
323 if (newModel != nullptr) {
338
340 }
341}
342
343// TODO: This is the 'easy' solution
344// evaluate whether this is enough or we need some better one or even boost here
346{
347 QVector<qreal> values;
348 // first fetch all values
349 const QAbstractItemModel &m = *model();
350 const int rowCount = m.rowCount(rootIndex());
351
352 for (int row = 0; row < rowCount; ++row) {
353 const QVariant var = m.data(m.index(row, 1, rootIndex()));
354 if (!var.isValid())
355 continue;
356 const qreal value = var.toReal();
357 if (ISNAN(value))
358 continue;
359 values << value;
360 }
361
362 qreal sum = 0.0;
363 qreal sumSquares = 0.0;
364 for (qreal value : qAsConst(values)) {
365 sum += value;
366 sumSquares += value * value;
367 }
368
369 const int N = values.count();
370
371 d->calculatedMeanValue = sum / N;
372 d->calculatedStandardDeviation = sqrt((static_cast<qreal>(N) * sumSquares - sum * sum) / (N * (N - 1)));
373}
374
375// calculates the largest QDate not greater than \a dt.
376static QDate floorDay(const QDateTime &dt)
377{
378 return dt.date();
379}
380
381// calculates the smallest QDate not less than \a dt.
382static QDate ceilDay(const QDateTime &dt)
383{
384 QDate result = dt.date();
385
386 if (QDateTime(result, QTime()) < dt)
387 result = result.addDays(1);
388
389 return result;
390}
391
392// calculates the largest QDateTime like xx:00 not greater than \a dt.
394{
395 return QDateTime(dt.date(), QTime(dt.time().hour(), 0));
396}
397
398// calculates the smallest QDateTime like xx:00 not less than \a dt.
399static QDateTime ceilHour(const QDateTime &dt)
400{
401 QDateTime result(dt.date(), QTime(dt.time().hour(), 0));
402
403 if (result < dt)
404 result = result.addSecs(3600);
405
406 return result;
407}
408
411{
412 const qreal yMin = d->expectedMeanValue - 4 * d->expectedStandardDeviation;
413 const qreal yMax = d->expectedMeanValue + 4 * d->expectedStandardDeviation;
414
415 d->setYAxisRange();
416
417 // rounded down/up to the prev/next midnight (at least that's the default)
419 const qint64 minTime = range.first.toSecsSinceEpoch();
420 const qint64 maxTime = range.second.toSecsSinceEpoch();
421
422 const qreal xMin = static_cast<qreal>(minTime / 24 * 60 * 60);
423 const qreal xMax = static_cast<qreal>(maxTime / 24 * 60 * 60) - xMin;
424
425 const QPointF bottomLeft(QPointF(0, yMin));
426 const QPointF topRight(QPointF(xMax, yMax));
427
428 return QPair<QPointF, QPointF>(bottomLeft, topRight);
429}
430
435{
436 if (d->timeRange != QPair<QDateTime, QDateTime>())
437 return d->timeRange;
438
439 const QAbstractItemModel &m = *model();
440 const int rowCount = m.rowCount(rootIndex());
441
442 const QDateTime begin = m.data(m.index(0, 3, rootIndex())).toDateTime();
443 const QDateTime end = m.data(m.index(rowCount - 1, 3, rootIndex())).toDateTime();
444
445 if (begin.secsTo(end) > 86400) {
446 // if begin to end is more than 24h
447 // round down/up to the prev/next midnight
448 const QDate min = floorDay(begin);
449 const QDate max = ceilDay(end);
451 } else if (begin.secsTo(end) > 3600) {
452 // more than 1h: rond down up to the prex/next hour
453 // if begin to end is more than 24h
454 const QDateTime min = floorHour(begin);
455 const QDateTime max = ceilHour(end);
456 return QPair<QDateTime, QDateTime>(min, max);
457 }
458 return QPair<QDateTime, QDateTime>(begin, end);
459}
460
466{
467 if (d->timeRange == timeRange)
468 return;
469
470 d->timeRange = timeRange;
471 update();
472}
473
478{
479 const unsigned int minTime = timeRange().first.toSecsSinceEpoch();
480
481 for (const QDateTime &dt : qAsConst(d->fluidicsPackChanges)) {
482 const qreal xValue = (dt.toSecsSinceEpoch() - minTime) / static_cast<qreal>(24 * 60 * 60);
483 const QPointF point(xValue, 0.0);
485 }
486
487 for (const QDateTime &dt : qAsConst(d->sensorChanges)) {
488 const qreal xValue = (dt.toSecsSinceEpoch() - minTime) / static_cast<qreal>(24 * 60 * 60);
489 const QPointF point(xValue, 0.0);
491 }
492}
493
496{
497 d->reverseMapper.clear();
498
499 // note: Not having any data model assigned is no bug
500 // but we can not draw a diagram then either.
501 if (!checkInvariants(true))
502 return;
504 return;
505
506 QPainter *const painter = ctx->painter();
507 const PainterSaver p(painter);
508 if (model()->rowCount(rootIndex()) == 0 || model()->columnCount(rootIndex()) < 4)
509 return; // nothing to paint for us
510
511 AbstractCoordinatePlane *const plane = ctx->coordinatePlane();
512 ctx->setCoordinatePlane(plane->sharedAxisMasterPlane(painter));
513
514 const QAbstractItemModel &m = *model();
515 const int rowCount = m.rowCount(rootIndex());
516
517 const unsigned int minTime = timeRange().first.toSecsSinceEpoch();
518
520
521 int prevLot = -1;
523 bool hadMissingValue = false;
524
525 for (int row = 0; row < rowCount; ++row) {
526 const QModelIndex lotIndex = m.index(row, 0, rootIndex());
527 const QModelIndex valueIndex = m.index(row, 1, rootIndex());
528 const QModelIndex okIndex = m.index(row, 2, rootIndex());
529 const QModelIndex timeIndex = m.index(row, 3, rootIndex());
530 const QModelIndex expectedMeanIndex = m.index(row, 4, rootIndex());
531 const QModelIndex expectedSDIndex = m.index(row, 5, rootIndex());
532
533 painter->setPen(pen(lotIndex));
534
536 qreal value = vValue.toReal();
537 const int lot = m.data(lotIndex).toInt();
538 const bool ok = m.data(okIndex).toBool();
539 const QDateTime time = m.data(timeIndex).toDateTime();
540 const qreal xValue = (time.toSecsSinceEpoch() - minTime) / static_cast<qreal>(24 * 60 * 60);
541
546
547 QPointF point = ctx->coordinatePlane()->translate(QPointF(xValue, value));
548
549 if (vValue.isNull()) {
550 hadMissingValue = true;
551 } else {
552 if (!vExpectedMean.isNull() && !vExpectedSD.isNull()) {
553 // this calculates the 'logical' value relative to the expected mean and SD of this point
554 value -= expectedMean;
555 value /= expectedSD;
556 value *= d->expectedStandardDeviation;
557 value += d->expectedMeanValue;
558 point = ctx->coordinatePlane()->translate(QPointF(xValue, value));
559 }
560
561 if (prevLot == lot) {
562 const QPen pen = painter->pen();
563 QPen newPen = pen;
564
565 if (hadMissingValue) {
566 newPen.setDashPattern(QVector<qreal>() << 4.0 << 4.0);
567 }
568
569 painter->setPen(newPen);
570 painter->drawLine(prevPoint, point);
571 painter->setPen(pen);
572 // d->reverseMapper.addLine( valueIndex.row(), valueIndex.column(), prevPoint, point );
573 } else if (row > 0) {
575 }
576
577 if (value <= d->expectedMeanValue + 4 * d->expectedStandardDeviation && value >= d->expectedMeanValue - 4 * d->expectedStandardDeviation) {
578 const QPointF location(xValue, value);
579 drawDataPointSymbol(ctx, location, ok);
580 d->reverseMapper.addCircle(valueIndex.row(),
581 valueIndex.column(),
582 ctx->coordinatePlane()->translate(location),
583 iconRect().size());
584 }
585 prevLot = lot;
586 prevPoint = point;
587 hadMissingValue = false;
588 }
589
590 const QModelIndex current = selectionModel()->currentIndex();
591 if (selectionModel()->rowIntersectsSelection(lotIndex.row(), lotIndex.parent()) || current.sibling(current.row(), 0) == lotIndex) {
592 const QPen pen = ctx->painter()->pen();
593 painter->setPen(d->scanLinePen);
594 painter->drawLine(ctx->coordinatePlane()->translate(QPointF(xValue, d->expectedMeanValue - 4 * d->expectedStandardDeviation)),
595 ctx->coordinatePlane()->translate(QPointF(xValue, d->expectedMeanValue + 4 * d->expectedStandardDeviation)));
596 painter->setPen(pen);
597 }
598 }
599
601
602 ctx->setCoordinatePlane(plane);
603}
604
611{
613
614 QPainter *const painter = ctx->painter();
615 const PainterSaver ps(painter);
616 const QPointF transPos = ctx->coordinatePlane()->translate(pos).toPoint();
617 painter->translate(transPos);
618
619 painter->setClipping(false);
620 iconRenderer(type)->render(painter, iconRect());
621}
622
629{
630 const QPointF transPos = ctx->coordinatePlane()->translate(
631 QPointF(pos.x(), d->lotChangedPosition & Qt::AlignTop ? d->expectedMeanValue + 4 * d->expectedStandardDeviation : d->expectedMeanValue - 4 * d->expectedStandardDeviation));
632
633 QPainter *const painter = ctx->painter();
634 const PainterSaver ps(painter);
635 painter->setClipping(false);
636 painter->translate(transPos);
637 iconRenderer(LotChanged)->render(painter, iconRect());
638}
639
646{
647 const QPointF transPos = ctx->coordinatePlane()->translate(
648 QPointF(pos.x(), d->sensorChangedPosition & Qt::AlignTop ? d->expectedMeanValue + 4 * d->expectedStandardDeviation : d->expectedMeanValue - 4 * d->expectedStandardDeviation));
649
650 QPainter *const painter = ctx->painter();
651 const PainterSaver ps(painter);
652 painter->setClipping(false);
653 painter->translate(transPos);
654 iconRenderer(SensorChanged)->render(painter, iconRect());
655}
656
663{
664 const QPointF transPos = ctx->coordinatePlane()->translate(
665 QPointF(pos.x(), d->fluidicsPackChangedPosition & Qt::AlignTop ? d->expectedMeanValue + 4 * d->expectedStandardDeviation : d->expectedMeanValue - 4 * d->expectedStandardDeviation));
666
667 QPainter *const painter = ctx->painter();
668 const PainterSaver ps(painter);
669 painter->setClipping(false);
670 painter->translate(transPos);
671 iconRenderer(FluidicsPackChanged)->render(painter, iconRect());
672}
673
686
691{
692 if (d->iconRenderer[symbol] == 0)
693 d->iconRenderer[symbol] = new QSvgRenderer(d->icons[symbol], this);
694
695 return d->iconRenderer[symbol];
696}
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)
const QPair< QPointF, QPointF > dataBoundaries() const
Return the bottom left and top right data point, that the diagram will display (unless the grid adjus...
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)
void columnsInserted(const QModelIndex &parent, int first, int last)
void columnsRemoved(const QModelIndex &parent, int first, int last)
virtual QVariant data(const QModelIndex &index, int role) const const=0
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector< int > &roles)
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const=0
void layoutChanged(const QList< QPersistentModelIndex > &parents, QAbstractItemModel::LayoutChangeHint hint)
virtual int rowCount(const QModelIndex &parent) const const=0
void rowsInserted(const QModelIndex &parent, int first, int last)
void rowsRemoved(const QModelIndex &parent, int first, int last)
QAbstractItemModel * model() const const
QModelIndex rootIndex() const const
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 Apr 26 2024 00:04:56 for KD Chart API Documentation by doxygen 1.9.8