12 #include "KDChartLeveyJenningsDiagram_p.h"
16 #include "KDChartPainterSaver_p.h"
20 #include <QFontMetrics>
22 #include <QSvgRenderer>
25 #include <KDABLibFakes>
30 LeveyJenningsDiagram::Private::Private()
34 LeveyJenningsDiagram::Private::~Private()
46 void LeveyJenningsDiagram::init()
48 d->lotChangedPosition = Qt::AlignTop;
49 d->fluidicsPackChangedPosition = Qt::AlignBottom;
50 d->sensorChangedPosition = Qt::AlignBottom;
52 d->scanLinePen = QPen(Qt::blue);
55 d->expectedMeanValue = 0.0;
56 d->expectedStandardDeviation = 0.0;
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");
66 setSelectionMode(QAbstractItemView::SingleSelection);
104 if (
d->lotChangedPosition == pos)
107 d->lotChangedPosition = pos;
116 return d->lotChangedPosition;
125 if (
d->fluidicsPackChangedPosition == pos)
128 d->fluidicsPackChangedPosition = pos;
137 return d->fluidicsPackChangedPosition;
146 if (
d->sensorChangedPosition == pos)
149 d->sensorChangedPosition = pos;
158 return d->sensorChangedPosition;
166 if (
d->fluidicsPackChanges == changes)
169 d->fluidicsPackChanges = changes;
178 return d->fluidicsPackChanges;
186 if (
d->sensorChanges == changes)
189 d->sensorChanges = changes;
198 if (
d->scanLinePen ==
pen)
201 d->scanLinePen =
pen;
210 return d->scanLinePen;
226 if (
d->icons[
symbol] == filename)
229 delete d->iconRenderer[
symbol];
242 return d->sensorChanges;
250 if (
d->expectedMeanValue == meanValue)
253 d->expectedMeanValue = meanValue;
263 return d->expectedMeanValue;
271 if (
d->expectedStandardDeviation == sd)
274 d->expectedStandardDeviation = sd;
284 return d->expectedStandardDeviation;
292 return d->calculatedMeanValue;
300 return d->calculatedStandardDeviation;
305 QAbstractItemModel *oldModel = model();
306 if (oldModel !=
nullptr) {
307 disconnect(oldModel, &QAbstractItemModel::dataChanged,
309 disconnect(oldModel, &QAbstractItemModel::rowsInserted,
311 disconnect(oldModel, &QAbstractItemModel::rowsRemoved,
313 disconnect(oldModel, &QAbstractItemModel::columnsInserted,
315 disconnect(oldModel, &QAbstractItemModel::columnsRemoved,
317 disconnect(oldModel, &QAbstractItemModel::modelReset,
319 disconnect(oldModel, &QAbstractItemModel::layoutChanged,
323 if (newModel !=
nullptr) {
324 connect(newModel, &QAbstractItemModel::dataChanged,
326 connect(newModel, &QAbstractItemModel::rowsInserted,
328 connect(newModel, &QAbstractItemModel::rowsRemoved,
330 connect(newModel, &QAbstractItemModel::columnsInserted,
332 connect(newModel, &QAbstractItemModel::columnsRemoved,
334 connect(newModel, &QAbstractItemModel::modelReset,
336 connect(newModel, &QAbstractItemModel::layoutChanged,
347 QVector<qreal> values;
349 const QAbstractItemModel &m = *model();
350 const int rowCount = m.rowCount(rootIndex());
352 for (
int row = 0; row < rowCount; ++row) {
353 const QVariant var = m.data(m.index(row, 1, rootIndex()));
356 const qreal value = var.toReal();
363 qreal sumSquares = 0.0;
364 for (qreal value : qAsConst(values)) {
366 sumSquares += value * value;
369 const int N = values.count();
371 d->calculatedMeanValue = sum / N;
372 d->calculatedStandardDeviation = sqrt((
static_cast<qreal
>(N) * sumSquares - sum * sum) / (N * (N - 1)));
384 QDate result = dt.date();
386 if (QDateTime(result, QTime()) < dt)
387 result = result.addDays(1);
395 return QDateTime(dt.date(), QTime(dt.time().hour(), 0));
401 QDateTime result(dt.date(), QTime(dt.time().hour(), 0));
404 result = result.addSecs(3600);
412 const qreal yMin =
d->expectedMeanValue - 4 *
d->expectedStandardDeviation;
413 const qreal yMax =
d->expectedMeanValue + 4 *
d->expectedStandardDeviation;
418 const QPair<QDateTime, QDateTime> range =
timeRange();
419 const qint64 minTime = range.first.toSecsSinceEpoch();
420 const qint64 maxTime = range.second.toSecsSinceEpoch();
422 const qreal xMin =
static_cast<qreal
>(minTime / 24 * 60 * 60);
423 const qreal xMax =
static_cast<qreal
>(maxTime / 24 * 60 * 60) - xMin;
425 const QPointF bottomLeft(QPointF(0, yMin));
426 const QPointF topRight(QPointF(xMax, yMax));
428 return QPair<QPointF, QPointF>(bottomLeft, topRight);
436 if (
d->timeRange != QPair<QDateTime, QDateTime>())
439 const QAbstractItemModel &m = *model();
440 const int rowCount = m.rowCount(rootIndex());
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();
445 if (begin.secsTo(end) > 86400) {
449 const QDate max =
ceilDay(end);
450 return QPair<QDateTime, QDateTime>(min.startOfDay(), max.startOfDay());
451 }
else if (begin.secsTo(end) > 3600) {
455 const QDateTime max =
ceilHour(end);
456 return QPair<QDateTime, QDateTime>(min, max);
458 return QPair<QDateTime, QDateTime>(begin, end);
479 const unsigned int minTime =
timeRange().first.toSecsSinceEpoch();
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);
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);
497 d->reverseMapper.clear();
506 QPainter *
const painter = ctx->
painter();
507 const PainterSaver p(painter);
508 if (model()->rowCount(rootIndex()) == 0 || model()->columnCount(rootIndex()) < 4)
514 const QAbstractItemModel &m = *model();
515 const int rowCount = m.rowCount(rootIndex());
517 const unsigned int minTime =
timeRange().first.toSecsSinceEpoch();
519 painter->setRenderHint(QPainter::Antialiasing,
true);
523 bool hadMissingValue =
false;
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());
533 painter->setPen(
pen(lotIndex));
535 QVariant vValue = m.data(valueIndex);
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);
542 QVariant vExpectedMean = m.data(expectedMeanIndex);
543 const qreal expectedMean = vExpectedMean.toReal();
544 QVariant vExpectedSD = m.data(expectedSDIndex);
545 const qreal expectedSD = vExpectedSD.toReal();
549 if (vValue.isNull()) {
550 hadMissingValue =
true;
552 if (!vExpectedMean.isNull() && !vExpectedSD.isNull()) {
554 value -= expectedMean;
556 value *=
d->expectedStandardDeviation;
557 value +=
d->expectedMeanValue;
561 if (prevLot == lot) {
562 const QPen
pen = painter->pen();
565 if (hadMissingValue) {
566 newPen.setDashPattern(QVector<qreal>() << 4.0 << 4.0);
569 painter->setPen(newPen);
570 painter->drawLine(prevPoint, point);
571 painter->setPen(
pen);
573 }
else if (row > 0) {
577 if (value <= d->
expectedMeanValue + 4 *
d->expectedStandardDeviation && value >=
d->expectedMeanValue - 4 *
d->expectedStandardDeviation) {
578 const QPointF location(xValue, value);
580 d->reverseMapper.addCircle(valueIndex.row(),
587 hadMissingValue =
false;
590 const QModelIndex current = selectionModel()->currentIndex();
591 if (selectionModel()->rowIntersectsSelection(lotIndex.row(), lotIndex.parent()) || current.sibling(current.row(), 0) == lotIndex) {
593 painter->setPen(
d->scanLinePen);
594 painter->drawLine(ctx->
coordinatePlane()->
translate(QPointF(xValue,
d->expectedMeanValue - 4 *
d->expectedStandardDeviation)),
596 painter->setPen(
pen);
614 QPainter *
const painter = ctx->
painter();
615 const PainterSaver ps(painter);
617 painter->translate(transPos);
619 painter->setClipping(
false);
631 QPointF(pos.x(),
d->lotChangedPosition & Qt::AlignTop ?
d->expectedMeanValue + 4 *
d->expectedStandardDeviation :
d->expectedMeanValue - 4 *
d->expectedStandardDeviation));
633 QPainter *
const painter = ctx->
painter();
634 const PainterSaver ps(painter);
635 painter->setClipping(
false);
636 painter->translate(transPos);
648 QPointF(pos.x(),
d->sensorChangedPosition & Qt::AlignTop ?
d->expectedMeanValue + 4 *
d->expectedStandardDeviation :
d->expectedMeanValue - 4 *
d->expectedStandardDeviation));
650 QPainter *
const painter = ctx->
painter();
651 const PainterSaver ps(painter);
652 painter->setClipping(
false);
653 painter->translate(transPos);
665 QPointF(pos.x(),
d->fluidicsPackChangedPosition & Qt::AlignTop ?
d->expectedMeanValue + 4 *
d->expectedStandardDeviation :
d->expectedMeanValue - 4 *
d->expectedStandardDeviation));
667 QPainter *
const painter = ctx->
painter();
668 const PainterSaver ps(painter);
669 painter->setClipping(
false);
670 painter->translate(transPos);
683 const qreal height = fm.height() / 1.2;
684 return QRectF(-height / 2.0, -height / 2.0, height, height);
692 if (
d->iconRenderer[
symbol] == 0)
693 d->iconRenderer[
symbol] =
new QSvgRenderer(
d->icons[
symbol],
this);
695 return d->iconRenderer[
symbol];
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
void setModel(QAbstractItemModel *model) override
Base class common for all coordinate planes, CartesianCoordinatePlane, PolarCoordinatePlane,...
virtual const QPointF translate(const QPointF &diagramPoint) const =0
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 setScanLinePen(const QPen &pen)
void setExpectedMeanValue(float meanValue)
void setLotChangedSymbolPosition(Qt::Alignment pos)
void setFluidicsPackChanges(const QVector< QDateTime > &changes)
void drawChanges(PaintContext *paintContext)
void setFluidicsPackChangedSymbolPosition(Qt::Alignment pos)
void setExpectedStandardDeviation(float sd)
void calculateMeanAndStandardDeviation() const
QVector< QDateTime > fluidicsPackChanges() const
float expectedStandardDeviation() const
~LeveyJenningsDiagram() override
float calculatedStandardDeviation() const
QVector< QDateTime > sensorChanges() const
float expectedMeanValue() const
QString symbol(Symbol symbol) const
Qt::Alignment sensorChangedSymbolPosition() const
Qt::Alignment fluidicsPackChangedSymbolPosition() const
float calculatedMeanValue() 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
virtual QRectF iconRect() const
QSvgRenderer * iconRenderer(Symbol symbol)
void setTimeRange(const QPair< QDateTime, QDateTime > &timeRange)
bool compare(const LeveyJenningsDiagram *other) const
Qt::Alignment lotChangedSymbolPosition() 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.
void setCoordinatePlane(AbstractCoordinatePlane *plane)
AbstractCoordinatePlane * coordinatePlane() const
QPainter * painter() const
A set of text attributes.
void setFontSize(const Measure &measure)
const QFont calculatedFont(const QObject *autoReferenceArea, KDChartEnums::MeasureOrientation autoReferenceOrientation) const
Returns the font in the size that is used at drawing time.