From a3ffc08d4973cf1315af91f11f2a870c34e8b4d6 Mon Sep 17 00:00:00 2001 From: Emil Sawicki Date: Tue, 25 Jan 2022 19:13:00 +0100 Subject: [PATCH 01/11] Add x axis --- limereport/items/charts/lrlineschart.cpp | 17 ++++-- limereport/items/charts/lrlineschart.h | 2 +- limereport/items/lrchartitem.cpp | 61 ++++++++++++++++++- limereport/items/lrchartitem.h | 20 +++++- .../objectinspector/lrobjectitemmodel.cpp | 1 + 5 files changed, 91 insertions(+), 10 deletions(-) diff --git a/limereport/items/charts/lrlineschart.cpp b/limereport/items/charts/lrlineschart.cpp index 7bd6c60..5919ee7 100644 --- a/limereport/items/charts/lrlineschart.cpp +++ b/limereport/items/charts/lrlineschart.cpp @@ -68,17 +68,22 @@ void LinesChart::drawDesignMode(QPainter* painter, qreal hStep, qreal vStep, qre } } -qreal LinesChart::calculateValueYPos(qreal, qreal max, qreal value, qreal delta, qreal height) +qreal LinesChart::calculatePos(const AxisData &data, qreal value, qreal rectSize) const { - return (max - value) / delta * height; + if (data.reverseDirection() && data.rangeMin() >= 0) { + // Not flipping for minimum less than 0 because lower number is at the bottom. + return (1 - (data.rangeMax() - value) / data.delta()) * rectSize; + } else { + return (data.rangeMax() - value) / data.delta() * rectSize; + } } void LinesChart::paintSeries(QPainter *painter, SeriesItem *series, QRectF barsRect) { const AxisData &yAxisData = this->yAxisData(); - const qreal delta = yAxisData.delta(); - const qreal hStep = barsRect.width() / valuesCount(); + const qreal xAxisDiff = std::max(1.0, maxXValue() - minXValue()); + const qreal hStep = barsRect.width() / xAxisDiff; const qreal topMargin = barsRect.top(); QPen pen(series->color()); @@ -91,11 +96,11 @@ void LinesChart::paintSeries(QPainter *painter, SeriesItem *series, QRectF barsR qreal lastXValue = barsRect.left() + hStep/2; if (!values.isEmpty()) { // Calculate first point position on plot before loop - lastYValue = calculateValueYPos(yAxisData.rangeMin(), yAxisData.rangeMax(), values.first(), delta, barsRect.height()); + lastYValue = calculatePos(yAxisData, values.first(), barsRect.height()); } for (int i = 0; i < values.count()-1; ++i ){ const qreal startY = lastYValue; - const qreal endY = calculateValueYPos(yAxisData.rangeMin(), yAxisData.rangeMax(), values.at(i+1), delta, barsRect.height()); + const qreal endY = calculatePos(yAxisData, values.at(i+1), barsRect.height()); // Record last used Y position to only calculate new one lastYValue = endY; diff --git a/limereport/items/charts/lrlineschart.h b/limereport/items/charts/lrlineschart.h index d37cb2c..d9325fc 100644 --- a/limereport/items/charts/lrlineschart.h +++ b/limereport/items/charts/lrlineschart.h @@ -10,7 +10,7 @@ public: void paintChart(QPainter *painter, QRectF chartRect); protected: void drawDesignMode(QPainter *painter, qreal hStep, qreal vStep, qreal topShift, QRectF barsRect); - qreal calculateValueYPos(qreal min, qreal max, qreal value, qreal delta, qreal height); + qreal calculatePos(const AxisData &data, qreal value, qreal rectSize) const; void paintSeries(QPainter *painter, SeriesItem *series, QRectF barsRect); private: diff --git a/limereport/items/lrchartitem.cpp b/limereport/items/lrchartitem.cpp index 6868109..90db91b 100644 --- a/limereport/items/lrchartitem.cpp +++ b/limereport/items/lrchartitem.cpp @@ -79,6 +79,16 @@ void SeriesItem::setLabelsColumn(const QString &labelsColumn) m_labelsColumn = labelsColumn; } +QString SeriesItem::xAxisColumn() const +{ + return m_xAxisColumn; +} + +void SeriesItem::setXAxisColumn(const QString &xAxisColumn) +{ + m_xAxisColumn = xAxisColumn; +} + SeriesItem *SeriesItem::clone() { SeriesItem* result = new SeriesItem(); @@ -98,6 +108,8 @@ void SeriesItem::fillSeriesData(IDataSource *dataSource) while(!dataSource->eof()){ if (!m_labelsColumn.isEmpty()) m_data.labels().append(dataSource->data(m_labelsColumn).toString()); + if (!m_xAxisColumn.isEmpty()) + m_data.xAxisValues().append(dataSource->data(m_xAxisColumn).toDouble()); m_data.values().append(dataSource->data(m_valuesColumn).toDouble()); m_data.colors().append((currentColorIndex<32)?color_map[currentColorIndex]:generateColor()); dataSource->next(); @@ -130,7 +142,8 @@ ChartItem::ChartItem(QObject *owner, QGraphicsItem *parent) : ItemDesignIntf(xmlTag, owner, parent), m_legendBorder(true), m_legendAlign(LegendAlignCenter), m_titleAlign(TitleAlignCenter), m_chartType(Pie), m_labelsField(""), m_isEmpty(true), - m_showLegend(true), m_drawPoints(true), m_seriesLineWidth(4) + m_showLegend(true), m_drawPoints(true), m_seriesLineWidth(4), + m_horizontalAxisOnTop(false), m_legendStyle(LegendPoints) { m_labels<<"First"<<"Second"<<"Thrid"; m_chart = new PieChart(this); @@ -235,6 +248,7 @@ void ChartItem::updateItemSize(DataSourceManager *dataManager, RenderPass , int foreach (SeriesItem* series, m_series) { if (series->isEmpty()){ series->setLabelsColumn(m_labelsField); + series->setXAxisColumn(m_xAxisField); series->fillSeriesData(ds); } } @@ -464,6 +478,31 @@ void ChartItem::setSeriesLineWidth(int newSeriesLineWidth) m_seriesLineWidth = newSeriesLineWidth; } +QString ChartItem::xAxisField() const +{ + return m_xAxisField; +} + +void ChartItem::setXAxisField(const QString &xAxisField) +{ + m_xAxisField = xAxisField; +} + +bool ChartItem::horizontalAxisOnTop() const +{ + return m_horizontalAxisOnTop; +} + +void ChartItem::setHorizontalAxisOnTop(bool horizontalAxisOnTop) +{ + if (m_horizontalAxisOnTop != horizontalAxisOnTop){ + m_horizontalAxisOnTop = horizontalAxisOnTop; + notify("horizontalAxisOnTop", !m_horizontalAxisOnTop, m_horizontalAxisOnTop); + update(); + } + m_horizontalAxisOnTop = horizontalAxisOnTop; +} + AbstractChart::AbstractChart(ChartItem *chartItem) :m_chartItem(chartItem) { @@ -550,20 +589,40 @@ qreal AbstractSeriesChart::minValue() AxisData AbstractSeriesChart::yAxisData() { return m_yAxisData; +qreal AbstractSeriesChart::minXValue() +{ + return m_chartItem->xAxisData()->minValue(); +} + } void AbstractSeriesChart::updateMinAndMaxValues() { qreal maxYValue = 0; qreal minYValue = 0; + qreal maxXValue = 0; + qreal minXValue = 0; if (m_chartItem->itemMode() == DesignMode) { maxYValue = 40; + maxXValue = 40; } else { for (SeriesItem* series : m_chartItem->series()){ for (qreal value : series->data()->values()){ minYValue = std::min(minYValue, value); maxYValue = std::max(maxYValue, value); } + if (series->data()->xAxisValues().isEmpty()) { + // Grid plot starts from 0 on x axis so x range must be decresed by 1 + const bool startingFromZero = m_chartItem->chartType() == ChartItem::GridLines; + const qreal valuesCount = this->valuesCount() - (startingFromZero ? 1 : 0); + minXValue = std::min(0.0, minXValue); + maxXValue = std::max(valuesCount, maxXValue); + } else { + for (qreal value : series->data()->xAxisValues()){ + minXValue = std::min(value, minXValue); + maxXValue = std::max(value, maxXValue); + } + } } } diff --git a/limereport/items/lrchartitem.h b/limereport/items/lrchartitem.h index 7e1ecac..c088dce 100644 --- a/limereport/items/lrchartitem.h +++ b/limereport/items/lrchartitem.h @@ -16,11 +16,12 @@ class SeriesItemData : public QObject{ Q_OBJECT public: QList& values(){ return m_values;} + QList& xAxisValues(){ return m_xAxisValues;} QList& labels(){ return m_labels;} QList& colors() { return m_colors;} void clear(){ m_values.clear(); m_labels.clear(); m_colors.clear(); } private: - QList m_values; + QList m_values, m_xAxisValues; QList m_labels; QList m_colors; }; @@ -30,6 +31,7 @@ class SeriesItem : public QObject{ Q_PROPERTY(QString name READ name WRITE setName) Q_PROPERTY(QString valuesColumn READ valuesColumn WRITE setValuesColumn) Q_PROPERTY(QString labelsColumn READ labelsColumn WRITE setLabelsColumn) + Q_PROPERTY(QString xAxisColumn READ xAxisColumn WRITE setXAxisColumn) Q_PROPERTY(QColor color READ color WRITE setColor) Q_PROPERTY(SeriesItemPreferredType preferredType READ preferredType WRITE setPreferredType) public: @@ -46,6 +48,8 @@ public: void setValuesColumn(const QString &valuesColumn); QString labelsColumn() const; void setLabelsColumn(const QString &labelsColumn); + QString xAxisColumn() const; + void setXAxisColumn(const QString &xAxisColumn); SeriesItem* clone(); void fillSeriesData(IDataSource* dataSource); SeriesItemData* data(){ return &m_data;} @@ -58,6 +62,7 @@ private: QString m_name; QString m_valuesColumn; QString m_labelsColumn; + QString m_xAxisColumn; SeriesItemData m_data; QColor m_color; SeriesItemPreferredType m_preferredType; @@ -86,7 +91,8 @@ public: protected: qreal maxValue(); qreal minValue(); - AxisData yAxisData(); + qreal maxXValue(); + qreal minXValue(); void updateMinAndMaxValues(); int valuesCount(); int seriesCount(); @@ -136,6 +142,8 @@ class ChartItem : public LimeReport::ItemDesignIntf //linesChart Q_PROPERTY(bool drawPoints READ drawPoints WRITE setDrawPoints) Q_PROPERTY(int seriesLineWidth READ seriesLineWidth WRITE setSeriesLineWidth) + Q_PROPERTY(bool horizontalAxisOnTop READ horizontalAxisOnTop WRITE setHorizontalAxisOnTop) + Q_PROPERTY(QString xAxisField READ xAxisField WRITE setXAxisField) friend class AbstractChart; public: @@ -194,6 +202,12 @@ public: int seriesLineWidth() const; void setSeriesLineWidth(int newSeriesLineWidth); + QString xAxisField() const; + void setXAxisField(const QString &xAxisField); + + bool horizontalAxisOnTop() const; + void setHorizontalAxisOnTop(bool horizontalAxisOnTop); + protected: void paintChartTitle(QPainter* painter, QRectF titleRect); virtual BaseDesignIntf* createSameTypeItem(QObject *owner, QGraphicsItem *parent); @@ -222,6 +236,8 @@ private: bool m_showLegend; bool m_drawPoints; int m_seriesLineWidth; + QString m_xAxisField; + bool m_horizontalAxisOnTop; }; } //namespace LimeReport #endif // LRCHARTITEM_H diff --git a/limereport/objectinspector/lrobjectitemmodel.cpp b/limereport/objectinspector/lrobjectitemmodel.cpp index 50da3f0..cb5c2b6 100644 --- a/limereport/objectinspector/lrobjectitemmodel.cpp +++ b/limereport/objectinspector/lrobjectitemmodel.cpp @@ -144,6 +144,7 @@ void QObjectPropertyModel::translatePropertyName() tr("chartType"); tr("drawLegendBorder"); tr("labelsField"); + tr("xAxisField"); tr("legendAlign"); tr("series"); tr("titleAlign"); From b0f45f55a4830da90d61483e35b8ef538bf98023 Mon Sep 17 00:00:00 2001 From: Emil Sawicki Date: Tue, 25 Jan 2022 19:28:32 +0100 Subject: [PATCH 02/11] Fix compilation --- limereport/items/charts/lrlineschart.cpp | 3 ++- limereport/items/lrchartitem.cpp | 15 +++++++-------- limereport/items/lrchartitem.h | 6 +++--- limereport/lraxisdata.cpp | 10 ++++++++++ limereport/lraxisdata.h | 6 +++++- 5 files changed, 27 insertions(+), 13 deletions(-) diff --git a/limereport/items/charts/lrlineschart.cpp b/limereport/items/charts/lrlineschart.cpp index 5919ee7..01da6b0 100644 --- a/limereport/items/charts/lrlineschart.cpp +++ b/limereport/items/charts/lrlineschart.cpp @@ -81,8 +81,9 @@ qreal LinesChart::calculatePos(const AxisData &data, qreal value, qreal rectSize void LinesChart::paintSeries(QPainter *painter, SeriesItem *series, QRectF barsRect) { const AxisData &yAxisData = this->yAxisData(); + const AxisData &xAxisData = this->xAxisData(); - const qreal xAxisDiff = std::max(1.0, maxXValue() - minXValue()); + const qreal xAxisDiff = std::max(1.0, xAxisData.maxValue() - xAxisData.minValue()); const qreal hStep = barsRect.width() / xAxisDiff; const qreal topMargin = barsRect.top(); diff --git a/limereport/items/lrchartitem.cpp b/limereport/items/lrchartitem.cpp index 90db91b..0130c67 100644 --- a/limereport/items/lrchartitem.cpp +++ b/limereport/items/lrchartitem.cpp @@ -143,7 +143,7 @@ ChartItem::ChartItem(QObject *owner, QGraphicsItem *parent) m_legendAlign(LegendAlignCenter), m_titleAlign(TitleAlignCenter), m_chartType(Pie), m_labelsField(""), m_isEmpty(true), m_showLegend(true), m_drawPoints(true), m_seriesLineWidth(4), - m_horizontalAxisOnTop(false), m_legendStyle(LegendPoints) + m_horizontalAxisOnTop(false) { m_labels<<"First"<<"Second"<<"Thrid"; m_chart = new PieChart(this); @@ -589,11 +589,11 @@ qreal AbstractSeriesChart::minValue() AxisData AbstractSeriesChart::yAxisData() { return m_yAxisData; -qreal AbstractSeriesChart::minXValue() -{ - return m_chartItem->xAxisData()->minValue(); } +AxisData AbstractSeriesChart::xAxisData() +{ + return m_xAxisData; } void AbstractSeriesChart::updateMinAndMaxValues() @@ -612,11 +612,8 @@ void AbstractSeriesChart::updateMinAndMaxValues() maxYValue = std::max(maxYValue, value); } if (series->data()->xAxisValues().isEmpty()) { - // Grid plot starts from 0 on x axis so x range must be decresed by 1 - const bool startingFromZero = m_chartItem->chartType() == ChartItem::GridLines; - const qreal valuesCount = this->valuesCount() - (startingFromZero ? 1 : 0); minXValue = std::min(0.0, minXValue); - maxXValue = std::max(valuesCount, maxXValue); + maxXValue = std::max((qreal)valuesCount(), maxXValue); } else { for (qreal value : series->data()->xAxisValues()){ minXValue = std::min(value, minXValue); @@ -627,6 +624,8 @@ void AbstractSeriesChart::updateMinAndMaxValues() } m_yAxisData = AxisData(minYValue, maxYValue); + m_xAxisData = AxisData(minXValue, maxXValue); + m_xAxisData.setReverseDirection(true); } qreal AbstractSeriesChart::hPadding(QRectF chartRect) diff --git a/limereport/items/lrchartitem.h b/limereport/items/lrchartitem.h index c088dce..7a20767 100644 --- a/limereport/items/lrchartitem.h +++ b/limereport/items/lrchartitem.h @@ -89,10 +89,10 @@ class AbstractSeriesChart: public AbstractChart{ public: AbstractSeriesChart(ChartItem* chartItem); protected: + AxisData yAxisData(); + AxisData xAxisData(); qreal maxValue(); qreal minValue(); - qreal maxXValue(); - qreal minXValue(); void updateMinAndMaxValues(); int valuesCount(); int seriesCount(); @@ -113,7 +113,7 @@ protected: virtual QString verticalLabel(int i, qreal step, qreal min); private: - AxisData m_yAxisData; + AxisData m_yAxisData, m_xAxisData; qreal m_designValues [9]; }; diff --git a/limereport/lraxisdata.cpp b/limereport/lraxisdata.cpp index fbe7f2a..34ef9e9 100644 --- a/limereport/lraxisdata.cpp +++ b/limereport/lraxisdata.cpp @@ -53,6 +53,16 @@ qreal AxisData::delta() const return m_delta; } +bool AxisData::reverseDirection() const +{ + return m_reverseDirection; +} + +void AxisData::setReverseDirection(bool reverseDirection) +{ + m_reverseDirection = reverseDirection; +} + void AxisData::calculateValuesAboveMax(qreal minValue, qreal maxValue, int segments) { const int delta = maxValue - minValue; diff --git a/limereport/lraxisdata.h b/limereport/lraxisdata.h index 84a40c7..f69d72d 100644 --- a/limereport/lraxisdata.h +++ b/limereport/lraxisdata.h @@ -20,9 +20,12 @@ public: qreal step() const; qreal delta() const; + + bool reverseDirection() const; + void setReverseDirection(bool reverseDirection); + private: void calculateValuesAboveMax(qreal minValue, qreal maxValue, int segments); - qreal calculateNiceNum(qreal range, bool round); qreal m_rangeMin; qreal m_rangeMax; @@ -31,6 +34,7 @@ private: qreal m_step; qreal m_delta; int m_segmentCount; + bool m_reverseDirection; }; }; From fdee26a2b8fbb99912b58bebf072bfc2b01b1868 Mon Sep 17 00:00:00 2001 From: Emil Sawicki Date: Tue, 25 Jan 2022 19:46:14 +0100 Subject: [PATCH 03/11] Add grid chart --- limereport/items/charts/lrgridlineschart.cpp | 110 +++++++++++++++++++ limereport/items/charts/lrgridlineschart.h | 17 +++ limereport/items/lrchartitem.cpp | 87 ++++++++++++++- limereport/items/lrchartitem.h | 6 +- limereport/limereport.pri | 2 + 5 files changed, 214 insertions(+), 8 deletions(-) create mode 100644 limereport/items/charts/lrgridlineschart.cpp create mode 100644 limereport/items/charts/lrgridlineschart.h diff --git a/limereport/items/charts/lrgridlineschart.cpp b/limereport/items/charts/lrgridlineschart.cpp new file mode 100644 index 0000000..f96ab1b --- /dev/null +++ b/limereport/items/charts/lrgridlineschart.cpp @@ -0,0 +1,110 @@ +#include "lrgridlineschart.h" + +namespace LimeReport { +void GridLinesChart::paintChart(QPainter *painter, QRectF chartRect) +{ + updateMinAndMaxValues(); + + const qreal valuesHMargin = this->valuesHMargin(painter); + const qreal valuesVMargin = this->valuesVMargin(painter); + + const qreal hPadding = this->hPadding(chartRect); + const qreal vPadding = this->vPadding(chartRect); + + QRectF calcRect = horizontalLabelsRect( + painter, + chartRect.adjusted( + hPadding * 2 + valuesHMargin, + chartRect.height() - (painter->fontMetrics().height() + vPadding*2), + -(hPadding * 2), + -vPadding + ) + ); + const qreal barsShift = calcRect.height(); + const qreal topOffset = painter->fontMetrics().height() * (m_chartItem->horizontalAxisOnTop() ? 1 : -1); + const QRectF gridRect = chartRect.adjusted( + hPadding, + vPadding + valuesVMargin + topOffset, + -hPadding * 3, + -(vPadding + barsShift) + ); + + paintGrid(painter, gridRect); + + paintSerialLines( + painter, + gridRect.adjusted(hPadding + valuesHMargin, 0, 0, 0) + ); + paintHorizontalLabels(painter, calcRect); +} + +void GridLinesChart::paintSerialLines(QPainter* painter, QRectF barsRect) +{ + if (valuesCount() == 0) return; + + painter->save(); + painter->setRenderHint(QPainter::Antialiasing,true); + + const AxisData &yAxisData = this->yAxisData(); + const qreal delta = yAxisData.delta(); + + if (m_chartItem->itemMode() == DesignMode){ + const qreal hStep = barsRect.width() / valuesCount(); + const qreal vStep = barsRect.height() / delta; + const qreal topShift = (delta - (maxValue() - minValue())) * vStep + barsRect.top(); + drawDesignMode(painter, hStep, vStep, topShift, barsRect); + painter->restore(); + return; + } + + const AxisData &xAxisData = this->xAxisData(); + const qreal hStep = barsRect.width() / (xAxisData.rangeMax() - xAxisData.rangeMin()); + + qreal leftMargin = 0; + const qreal topMargin = barsRect.top(); + + for (SeriesItem* series : m_chartItem->series()) { + QPen pen(series->color()); + pen.setWidth(m_chartItem->seriesLineWidth()); + painter->setPen(pen); + + const QList &xAxisValues = series->data()->xAxisValues(); + const QList &values = series->data()->values(); + const int xAxisValuesSize = xAxisValues.size(); + qreal lastXPos = 0; + qreal lastYPos = 0; + if (!values.isEmpty()) { + // Calculate first point position on plot before loop + lastYPos = calculatePos(yAxisData, values.first(), barsRect.height()); + } + if (xAxisValues.isEmpty()) { + leftMargin = barsRect.left(); + } else { + leftMargin = barsRect.left(); + lastXPos = calculatePos(xAxisData, xAxisValues.first(), barsRect.width()); + } + for (int i = 0; i < values.count() - 1; ++i ) { + const qreal startY = lastYPos; + const qreal endY = calculatePos(yAxisData, values.at(i+1), barsRect.height()); + // Record last used Y position to only calculate new one + lastYPos = endY; + + qreal startX = lastXPos; + qreal endX = 0; + if (i + 1 < xAxisValuesSize) { + endX = calculatePos(xAxisData, xAxisValues.at(i+1), barsRect.width()); + } else { + endX = startX + hStep; + } + // Record last used X position to only calculate new one + lastXPos = endX; + + QPoint startPoint = QPoint(startX + leftMargin, startY + topMargin); + QPoint endPoint = QPoint(endX + leftMargin, endY + topMargin); + drawSegment(painter, startPoint, endPoint, series->color()); + } + } + + painter->restore(); +} +} diff --git a/limereport/items/charts/lrgridlineschart.h b/limereport/items/charts/lrgridlineschart.h new file mode 100644 index 0000000..a95a07b --- /dev/null +++ b/limereport/items/charts/lrgridlineschart.h @@ -0,0 +1,17 @@ +#ifndef GRIDLINESCHART_H +#define GRIDLINESCHART_H + +#include "lrlineschart.h" + +namespace LimeReport { +class GridLinesChart : public LinesChart{ +public: + GridLinesChart(ChartItem* chartItem):LinesChart(chartItem){} + void paintChart(QPainter *painter, QRectF chartRect); + +private: + void paintSerialLines(QPainter *painter, QRectF barsRect); +}; +} + +#endif // GRIDLINESCHART_H diff --git a/limereport/items/lrchartitem.cpp b/limereport/items/lrchartitem.cpp index 0130c67..defaea9 100644 --- a/limereport/items/lrchartitem.cpp +++ b/limereport/items/lrchartitem.cpp @@ -13,6 +13,7 @@ #include "charts/lrverticalbarchart.h" #include "charts/lrhorizontalbarchart.h" #include "charts/lrlineschart.h" +#include "charts/lrgridlineschart.h" namespace{ @@ -341,6 +342,10 @@ void ChartItem::setChartType(const ChartType &chartType) break; case Lines: m_chart = new LinesChart(this); + break; + case GridLines: + m_chart = new GridLinesChart(this); + break; } notify("chartType",oldValue,m_chartType); update(); @@ -612,8 +617,11 @@ void AbstractSeriesChart::updateMinAndMaxValues() maxYValue = std::max(maxYValue, value); } if (series->data()->xAxisValues().isEmpty()) { + // Grid plot starts from 0 on x axis so x range must be decresed by 1 + const bool startingFromZero = m_chartItem->chartType() == ChartItem::GridLines; + const qreal valuesCount = this->valuesCount() - (startingFromZero ? 1 : 0); minXValue = std::min(0.0, minXValue); - maxXValue = std::max((qreal)valuesCount(), maxXValue); + maxXValue = std::max(valuesCount, maxXValue); } else { for (qreal value : series->data()->xAxisValues()){ minXValue = std::min(value, minXValue); @@ -785,7 +793,7 @@ void AbstractSeriesChart::paintVerticalGrid(QPainter *painter, QRectF gridRect) const qreal y = vStep * i; painter->drawText(QRectF(gridRect.bottomLeft()-QPointF(textPositionOffset,y+halfFontHeight), QSizeF(valuesHMargin,fontHeight)), - verticalLabel(i, yAxisData.step(), yAxisData.rangeMin()), + axisLabel(i, yAxisData), verticalTextOption); painter->drawLine(gridRect.bottomLeft()-QPointF(-valuesHMargin,y), gridRect.bottomRight()-QPointF(0,y)); @@ -794,6 +802,65 @@ void AbstractSeriesChart::paintVerticalGrid(QPainter *painter, QRectF gridRect) painter->setRenderHint(QPainter::Antialiasing,true); } +void AbstractSeriesChart::paintGrid(QPainter *painter, QRectF gridRect) +{ + painter->save(); + + const AxisData &yAxisData = this->yAxisData(); + const AxisData &xAxisData = this->xAxisData(); + + painter->setRenderHint(QPainter::Antialiasing,false); + + const int xAxisSegmentCount = xAxisData.segmentCount(); + const int xAxisLineCount = xAxisSegmentCount + 1; + const int yAxisSegmentCount = yAxisData.segmentCount(); + const int yAxisLineCount = yAxisSegmentCount + 1; + + const qreal gridOffset = hPadding(gridRect); + const int fontHeight = painter->fontMetrics().height(); + const int halfFontHeight = fontHeight / 2; + const qreal valuesHMargin = this->valuesHMargin(painter); + const qreal vStep = gridRect.height() / yAxisSegmentCount; + const qreal hStep = (gridRect.width() - valuesHMargin - gridOffset) / xAxisSegmentCount; + + // Vertical axis lines + const QTextOption verticalTextOption(Qt::AlignRight); + for (int i = 0 ; i < yAxisLineCount ; i++ ) { + const qreal y = vStep * i; + painter->drawText(QRectF(gridRect.bottomLeft()-QPointF(halfFontHeight, y + halfFontHeight), + QSizeF(valuesHMargin,fontHeight)), + axisLabel(i, yAxisData), + verticalTextOption); + painter->drawLine(gridRect.bottomLeft()-QPointF(-valuesHMargin, y), + gridRect.bottomRight()-QPointF(0, y)); + } + + // Horizontal axis lines + for (int i = 0 ; i < xAxisLineCount ; i++) { + const qreal x = gridRect.left() + hStep * i + valuesHMargin + gridOffset; + const bool drawFullLine = i == 0 || i == xAxisSegmentCount; + const QString text = QString::number(xAxisData.rangeMin() + i * xAxisData.step()); + + if (m_chartItem->horizontalAxisOnTop()) { + painter->drawLine(x, gridRect.top() - gridOffset, + x, (drawFullLine ? gridRect.bottom() : gridRect.top())); + painter->drawText(QRectF(x - painter->fontMetrics().width(text) / 2, + gridRect.top() - (fontHeight + gridOffset), + hStep, fontHeight), + text); + } else { + painter->drawLine(x, gridRect.bottom() + gridOffset, + x, (drawFullLine ? gridRect.top() : gridRect.bottom())); + painter->drawText(QRectF(x - painter->fontMetrics().width(text) / 2, + gridRect.bottom() + halfFontHeight + gridOffset, + hStep, fontHeight), + text); + } + } + + painter->restore(); +} + void AbstractSeriesChart::drawSegment(QPainter *painter, QPoint startPoint, QPoint endPoint, QColor color) { int radius = m_chartItem->seriesLineWidth(); @@ -819,7 +886,7 @@ qreal AbstractSeriesChart::valuesHMargin(QPainter *painter) const int yAxisLineCount = yAxisData.segmentCount() + 1; for (int i = 0 ; i < yAxisLineCount ; i++) { - const QString label = verticalLabel(i, yAxisData.step(), yAxisData.rangeMin()); + const QString label = axisLabel(i, yAxisData); max = std::max(max, (qreal)painter->fontMetrics().boundingRect(label).width()+offset); } return max; @@ -869,14 +936,22 @@ QFont AbstractSeriesChart::adaptValuesFont(qreal width, QFont font) return tmpFont; } -QString AbstractSeriesChart::verticalLabel(int i, qreal step, qreal min) +QString AbstractSeriesChart::axisLabel(int i, const AxisData &axisData) { - qreal value = min + i * step; + const qreal min = axisData.rangeMin(); + const qreal step = axisData.step(); + qreal value = 0; + // For values below 0, axis is already reversed (value closer to 0 is higher) + if (axisData.reverseDirection() && min >= 0) { + value = min + (axisData.segmentCount() - i) * step; + } else { + value = min + i * step; + } if (std::floor(step) == step) { return QString::number(value); } // For float round numbers to small precision - return QString::number(value, 'g', 2); + return QString::number(round(value * 100.0) / 100.0); } void AbstractBarChart::paintChartLegend(QPainter *painter, QRectF legendRect) diff --git a/limereport/items/lrchartitem.h b/limereport/items/lrchartitem.h index 7a20767..1efc827 100644 --- a/limereport/items/lrchartitem.h +++ b/limereport/items/lrchartitem.h @@ -104,13 +104,14 @@ protected: virtual void paintHorizontalLabels(QPainter *painter, QRectF labelsRect); virtual void paintVerticalLabels(QPainter *painter, QRectF labelsRect); virtual void paintHorizontalGrid(QPainter *painter, QRectF gridRect); + virtual void paintGrid(QPainter *painter, QRectF gridRect); virtual void paintVerticalGrid(QPainter *painter, QRectF gridRect); virtual void drawSegment(QPainter *painter, QPoint startPoint, QPoint endPoint, QColor color); virtual qreal valuesHMargin(QPainter *painter); virtual qreal valuesVMargin(QPainter *painter); virtual QFont adaptLabelsFont(QRectF rect, QFont font); virtual QFont adaptValuesFont(qreal width, QFont font); - virtual QString verticalLabel(int i, qreal step, qreal min); + virtual QString axisLabel(int i, const AxisData &axisData); private: AxisData m_yAxisData, m_xAxisData; @@ -148,8 +149,9 @@ class ChartItem : public LimeReport::ItemDesignIntf public: enum LegendAlign{LegendAlignTop,LegendAlignCenter,LegendAlignBottom}; + enum LegendStyle{LegendPoints, LegendLines}; enum TitleAlign{TitleAlignLeft, TitleAlignCenter, TitleAlignRight}; - enum ChartType{Pie, VerticalBar, HorizontalBar, Lines}; + enum ChartType{Pie, VerticalBar, HorizontalBar, Lines, GridLines}; #if QT_VERSION >= 0x050500 Q_ENUM(LegendAlign) Q_ENUM(TitleAlign) diff --git a/limereport/limereport.pri b/limereport/limereport.pri index e6862a9..4ed78f6 100644 --- a/limereport/limereport.pri +++ b/limereport/limereport.pri @@ -49,6 +49,7 @@ SOURCES += \ $$REPORT_PATH/items/lrchartitemeditor.cpp \ $$REPORT_PATH/items/charts/lrhorizontalbarchart.cpp \ $$REPORT_PATH/items/charts/lrlineschart.cpp \ + $$REPORT_PATH/items/charts/lrgridlineschart.cpp \ $$REPORT_PATH/items/charts/lrpiechart.cpp \ $$REPORT_PATH/items/charts/lrverticalbarchart.cpp \ $$REPORT_PATH/lrbanddesignintf.cpp \ @@ -132,6 +133,7 @@ HEADERS += \ $$REPORT_PATH/items/lrchartitemeditor.h \ $$REPORT_PATH/items/charts/lrhorizontalbarchart.h \ $$REPORT_PATH/items/charts/lrlineschart.h \ + $$REPORT_PATH/items/charts/lrgridlineschart.h \ $$REPORT_PATH/items/charts/lrpiechart.h \ $$REPORT_PATH/items/charts/lrverticalbarchart.h \ $$REPORT_PATH/lrbanddesignintf.h \ From 5dac77083f5027ad0bdb587a9dd30e7c0e69a703 Mon Sep 17 00:00:00 2001 From: Emil Sawicki Date: Tue, 25 Jan 2022 20:05:18 +0100 Subject: [PATCH 04/11] Get axis label using method --- limereport/items/lrchartitem.cpp | 6 +++--- limereport/items/lrchartitem.h | 3 +++ limereport/items/lrchartitemeditor.cpp | 2 ++ 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/limereport/items/lrchartitem.cpp b/limereport/items/lrchartitem.cpp index defaea9..2936901 100644 --- a/limereport/items/lrchartitem.cpp +++ b/limereport/items/lrchartitem.cpp @@ -752,7 +752,6 @@ void AbstractSeriesChart::paintHorizontalGrid(QPainter *painter, QRectF gridRect painter->save(); const AxisData &yAxisData = this->yAxisData(); - const qreal delta = yAxisData.delta(); const int segmentCount = yAxisData.segmentCount(); const int lineCount = segmentCount + 1; @@ -765,7 +764,7 @@ void AbstractSeriesChart::paintHorizontalGrid(QPainter *painter, QRectF gridRect for (int i = 0 ; i < lineCount ; i++ ) { painter->drawText(QRectF(gridRect.left() + 4 + hStep * i, gridRect.bottom() - painter->fontMetrics().height(), hStep, painter->fontMetrics().height()), - QString::number(minValue() + i * delta / segmentCount)); + axisLabel(i, yAxisData)); painter->drawLine( gridRect.left()+hStep*i, gridRect.bottom(), gridRect.left()+hStep*i, gridRect.top()); @@ -791,6 +790,7 @@ void AbstractSeriesChart::paintVerticalGrid(QPainter *painter, QRectF gridRect) const QTextOption verticalTextOption(Qt::AlignRight); for (int i = 0 ; i < lineCount ; i++ ) { const qreal y = vStep * i; + // TODO_ES handle horizontalAxisOnTop painter->drawText(QRectF(gridRect.bottomLeft()-QPointF(textPositionOffset,y+halfFontHeight), QSizeF(valuesHMargin,fontHeight)), axisLabel(i, yAxisData), @@ -839,7 +839,7 @@ void AbstractSeriesChart::paintGrid(QPainter *painter, QRectF gridRect) for (int i = 0 ; i < xAxisLineCount ; i++) { const qreal x = gridRect.left() + hStep * i + valuesHMargin + gridOffset; const bool drawFullLine = i == 0 || i == xAxisSegmentCount; - const QString text = QString::number(xAxisData.rangeMin() + i * xAxisData.step()); + const QString text = axisLabel(i, xAxisData); if (m_chartItem->horizontalAxisOnTop()) { painter->drawLine(x, gridRect.top() - gridOffset, diff --git a/limereport/items/lrchartitem.h b/limereport/items/lrchartitem.h index 1efc827..ff855c4 100644 --- a/limereport/items/lrchartitem.h +++ b/limereport/items/lrchartitem.h @@ -145,6 +145,9 @@ class ChartItem : public LimeReport::ItemDesignIntf Q_PROPERTY(int seriesLineWidth READ seriesLineWidth WRITE setSeriesLineWidth) Q_PROPERTY(bool horizontalAxisOnTop READ horizontalAxisOnTop WRITE setHorizontalAxisOnTop) Q_PROPERTY(QString xAxisField READ xAxisField WRITE setXAxisField) + + //girdChart + // TODO_ES add grid property for showing lines inside friend class AbstractChart; public: diff --git a/limereport/items/lrchartitemeditor.cpp b/limereport/items/lrchartitemeditor.cpp index 31a84ec..e51ed22 100644 --- a/limereport/items/lrchartitemeditor.cpp +++ b/limereport/items/lrchartitemeditor.cpp @@ -23,6 +23,8 @@ ChartItemEditor::ChartItemEditor(LimeReport::ChartItem *item, LimeReport::PageDe readSetting(); init(); connect(m_colorButton, SIGNAL(clicked(bool)), this, SLOT(slotChangeSeriesColor())); + + // TODO_ES add field in editor for x axis field, like for labels } ChartItemEditor::~ChartItemEditor() From d0830a7a3705d1bfc38d689de88318d789436b56 Mon Sep 17 00:00:00 2001 From: Emil Sawicki Date: Thu, 27 Jan 2022 19:20:44 +0100 Subject: [PATCH 05/11] Handle horizontal bars --- .../items/charts/lrhorizontalbarchart.cpp | 24 +++++++++++++------ limereport/items/lrchartitem.cpp | 19 +++++++++------ 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/limereport/items/charts/lrhorizontalbarchart.cpp b/limereport/items/charts/lrhorizontalbarchart.cpp index 17858fe..eeb04ac 100644 --- a/limereport/items/charts/lrhorizontalbarchart.cpp +++ b/limereport/items/charts/lrhorizontalbarchart.cpp @@ -20,7 +20,9 @@ void HorizontalBarChart::paintChart(QPainter *painter, QRectF chartRect) paintHorizontalGrid(painter, chartRect.adjusted( hPadding(chartRect) + barsShift, vPadding(chartRect), - -(hPadding(chartRect)),-vPadding(chartRect))); + -(hPadding(chartRect)), + -vPadding(chartRect))); + paintHorizontalBars(painter, chartRect.adjusted( hPadding(chartRect) + barsShift, vPadding(chartRect) * 2, @@ -36,25 +38,33 @@ void HorizontalBarChart::paintHorizontalBars(QPainter *painter, QRectF barsRect) painter->save(); painter->setRenderHint(QPainter::Antialiasing,false); + const AxisData &yAxisData = this->yAxisData(); const qreal delta = yAxisData.delta(); - qreal vStep = (barsRect.height()-painter->fontMetrics().height()) / valuesCount() / seriesCount(); + const qreal verticalOffset = painter->fontMetrics().height(); + qreal vStep = (barsRect.height() - verticalOffset) / valuesCount() / seriesCount(); qreal hStep = (barsRect.width()-painter->fontMetrics().boundingRect(QString::number(maxValue())).width()) / delta; if (!m_chartItem->series().isEmpty() && (m_chartItem->itemMode() != DesignMode)){ - int curSeries = 0; + qreal curVOffset = barsRect.top(); + if (m_chartItem->horizontalAxisOnTop()) { + curVOffset += verticalOffset; + } foreach (SeriesItem* series, m_chartItem->series()) { - qreal curVOffset = curSeries*vStep+barsRect.top(); painter->setBrush(series->color()); + qreal y = curVOffset; foreach (qreal value, series->data()->values()) { - painter->drawRect(QRectF((-minValue()*hStep)+barsRect.left(), curVOffset, value*hStep, vStep)); - curVOffset+=vStep*seriesCount(); + painter->drawRect(QRectF((-minValue()*hStep)+barsRect.left(), y, value*hStep, vStep)); + y+=vStep*seriesCount(); } - curSeries++; + curVOffset += vStep; } } else { qreal curVOffset = barsRect.top(); + if (m_chartItem->horizontalAxisOnTop()) { + curVOffset += verticalOffset; + } int curColor = 0; for (int i=0; i<9; ++i){ if (curColor==3) curColor=0; diff --git a/limereport/items/lrchartitem.cpp b/limereport/items/lrchartitem.cpp index 2936901..54df5c8 100644 --- a/limereport/items/lrchartitem.cpp +++ b/limereport/items/lrchartitem.cpp @@ -633,7 +633,6 @@ void AbstractSeriesChart::updateMinAndMaxValues() m_yAxisData = AxisData(minYValue, maxYValue); m_xAxisData = AxisData(minXValue, maxXValue); - m_xAxisData.setReverseDirection(true); } qreal AbstractSeriesChart::hPadding(QRectF chartRect) @@ -761,13 +760,18 @@ void AbstractSeriesChart::paintHorizontalGrid(QPainter *painter, QRectF gridRect painter->setFont(adaptValuesFont(hStep-4, painter->font())); + QPointF textPos; + if (m_chartItem->horizontalAxisOnTop()) { + textPos.setY(gridRect.top()); + } else { + textPos.setY(gridRect.bottom() - painter->fontMetrics().height()); + } for (int i = 0 ; i < lineCount ; i++ ) { - painter->drawText(QRectF(gridRect.left() + 4 + hStep * i, gridRect.bottom() - painter->fontMetrics().height(), - hStep, painter->fontMetrics().height()), + const qreal x = gridRect.left() + hStep * i; + textPos.setX(x + 4); + painter->drawText(QRectF(textPos, QSizeF(hStep, painter->fontMetrics().height())), axisLabel(i, yAxisData)); - painter->drawLine( gridRect.left()+hStep*i, gridRect.bottom(), - gridRect.left()+hStep*i, gridRect.top()); - + painter->drawLine(x, gridRect.bottom(), x, gridRect.top()); } painter->restore(); } @@ -790,7 +794,6 @@ void AbstractSeriesChart::paintVerticalGrid(QPainter *painter, QRectF gridRect) const QTextOption verticalTextOption(Qt::AlignRight); for (int i = 0 ; i < lineCount ; i++ ) { const qreal y = vStep * i; - // TODO_ES handle horizontalAxisOnTop painter->drawText(QRectF(gridRect.bottomLeft()-QPointF(textPositionOffset,y+halfFontHeight), QSizeF(valuesHMargin,fontHeight)), axisLabel(i, yAxisData), @@ -823,6 +826,8 @@ void AbstractSeriesChart::paintGrid(QPainter *painter, QRectF gridRect) const qreal vStep = gridRect.height() / yAxisSegmentCount; const qreal hStep = (gridRect.width() - valuesHMargin - gridOffset) / xAxisSegmentCount; + painter->setFont(adaptValuesFont(hStep-4, painter->font())); + // Vertical axis lines const QTextOption verticalTextOption(Qt::AlignRight); for (int i = 0 ; i < yAxisLineCount ; i++ ) { From 036913780084801ed854ad728b620f0ef8f68f54 Mon Sep 17 00:00:00 2001 From: Emil Sawicki Date: Thu, 27 Jan 2022 19:51:40 +0100 Subject: [PATCH 06/11] Add property to draw lines in grid --- limereport/items/lrchartitem.cpp | 35 ++++++++++++++++++++++++++++---- limereport/items/lrchartitem.h | 20 +++++++++++++++--- 2 files changed, 48 insertions(+), 7 deletions(-) diff --git a/limereport/items/lrchartitem.cpp b/limereport/items/lrchartitem.cpp index 54df5c8..3dbeafa 100644 --- a/limereport/items/lrchartitem.cpp +++ b/limereport/items/lrchartitem.cpp @@ -144,7 +144,7 @@ ChartItem::ChartItem(QObject *owner, QGraphicsItem *parent) m_legendAlign(LegendAlignCenter), m_titleAlign(TitleAlignCenter), m_chartType(Pie), m_labelsField(""), m_isEmpty(true), m_showLegend(true), m_drawPoints(true), m_seriesLineWidth(4), - m_horizontalAxisOnTop(false) + m_horizontalAxisOnTop(false), m_gridChartLines(AllLines) { m_labels<<"First"<<"Second"<<"Thrid"; m_chart = new PieChart(this); @@ -508,6 +508,25 @@ void ChartItem::setHorizontalAxisOnTop(bool horizontalAxisOnTop) m_horizontalAxisOnTop = horizontalAxisOnTop; } +ChartItem::GridChartLines ChartItem::gridChartLines() const +{ + return m_gridChartLines; +} + +void ChartItem::setGridChartLines(GridChartLines flags) +{ + if (m_gridChartLines == flags) { + return; + } + GridChartLines oldValue = m_gridChartLines; + m_gridChartLines = flags; + if (isLoading()) { + return; + } + update(rect()); + notify("gridChartLines",QVariant(oldValue),QVariant(flags)); +} + AbstractChart::AbstractChart(ChartItem *chartItem) :m_chartItem(chartItem) { @@ -832,18 +851,25 @@ void AbstractSeriesChart::paintGrid(QPainter *painter, QRectF gridRect) const QTextOption verticalTextOption(Qt::AlignRight); for (int i = 0 ; i < yAxisLineCount ; i++ ) { const qreal y = vStep * i; + const bool drawFullLine = m_chartItem->gridChartLines() & ChartItem::HorizontalLine + || i == 0 || i == xAxisSegmentCount; painter->drawText(QRectF(gridRect.bottomLeft()-QPointF(halfFontHeight, y + halfFontHeight), QSizeF(valuesHMargin,fontHeight)), axisLabel(i, yAxisData), verticalTextOption); - painter->drawLine(gridRect.bottomLeft()-QPointF(-valuesHMargin, y), - gridRect.bottomRight()-QPointF(0, y)); + + QPointF lineEndPos = gridRect.bottomRight() - QPointF(0, y); + if (!drawFullLine) { + lineEndPos.setX(gridRect.left() + valuesHMargin + gridOffset); + } + painter->drawLine(gridRect.bottomLeft() - QPointF(-valuesHMargin, y), lineEndPos); } // Horizontal axis lines for (int i = 0 ; i < xAxisLineCount ; i++) { const qreal x = gridRect.left() + hStep * i + valuesHMargin + gridOffset; - const bool drawFullLine = i == 0 || i == xAxisSegmentCount; + const bool drawFullLine = m_chartItem->gridChartLines() & ChartItem::VerticalLine + || i == 0 || i == xAxisSegmentCount; const QString text = axisLabel(i, xAxisData); if (m_chartItem->horizontalAxisOnTop()) { @@ -1035,4 +1061,5 @@ QRectF AbstractBarChart::horizontalLabelsRect(QPainter *painter, QRectF labelsRe else return labelsRect.adjusted(0, (labelsRect.height() - maxWidth), 0, 0); } + } // namespace LimeReport diff --git a/limereport/items/lrchartitem.h b/limereport/items/lrchartitem.h index ff855c4..e524fb2 100644 --- a/limereport/items/lrchartitem.h +++ b/limereport/items/lrchartitem.h @@ -144,10 +144,11 @@ class ChartItem : public LimeReport::ItemDesignIntf Q_PROPERTY(bool drawPoints READ drawPoints WRITE setDrawPoints) Q_PROPERTY(int seriesLineWidth READ seriesLineWidth WRITE setSeriesLineWidth) Q_PROPERTY(bool horizontalAxisOnTop READ horizontalAxisOnTop WRITE setHorizontalAxisOnTop) - Q_PROPERTY(QString xAxisField READ xAxisField WRITE setXAxisField) - //girdChart - // TODO_ES add grid property for showing lines inside + //gridChart + Q_FLAGS(GridChartLines) + Q_PROPERTY(QString xAxisField READ xAxisField WRITE setXAxisField) + Q_PROPERTY(GridChartLines gridChartLines READ gridChartLines WRITE setGridChartLines) friend class AbstractChart; public: @@ -155,15 +156,24 @@ public: enum LegendStyle{LegendPoints, LegendLines}; enum TitleAlign{TitleAlignLeft, TitleAlignCenter, TitleAlignRight}; enum ChartType{Pie, VerticalBar, HorizontalBar, Lines, GridLines}; + enum LineType { + NoLine = 0, + HorizontalLine = 1, + VerticalLine = 2, + AllLines = 3 + }; #if QT_VERSION >= 0x050500 Q_ENUM(LegendAlign) Q_ENUM(TitleAlign) Q_ENUM(ChartType) + Q_ENUM(LineType) #else Q_ENUMS(LegendAlign) Q_ENUMS(TitleAlign) Q_ENUMS(ChartType) + Q_ENUMS(LineType) #endif + Q_DECLARE_FLAGS(GridChartLines, LineType) ChartItem(QObject* owner, QGraphicsItem* parent); ~ChartItem(); @@ -213,6 +223,9 @@ public: bool horizontalAxisOnTop() const; void setHorizontalAxisOnTop(bool horizontalAxisOnTop); + GridChartLines gridChartLines() const; + void setGridChartLines(GridChartLines flags); + protected: void paintChartTitle(QPainter* painter, QRectF titleRect); virtual BaseDesignIntf* createSameTypeItem(QObject *owner, QGraphicsItem *parent); @@ -243,6 +256,7 @@ private: int m_seriesLineWidth; QString m_xAxisField; bool m_horizontalAxisOnTop; + GridChartLines m_gridChartLines; }; } //namespace LimeReport #endif // LRCHARTITEM_H From 7a4b3646e3c53a792e1430a5023e4f8e413c7f52 Mon Sep 17 00:00:00 2001 From: Emil Sawicki Date: Thu, 27 Jan 2022 19:57:53 +0100 Subject: [PATCH 07/11] Update code --- limereport/items/charts/lrlineschart.cpp | 7 +------ limereport/lraxisdata.cpp | 10 ---------- limereport/lraxisdata.h | 4 ---- limereport/objectinspector/lrobjectitemmodel.cpp | 2 ++ 4 files changed, 3 insertions(+), 20 deletions(-) diff --git a/limereport/items/charts/lrlineschart.cpp b/limereport/items/charts/lrlineschart.cpp index 01da6b0..36aabc6 100644 --- a/limereport/items/charts/lrlineschart.cpp +++ b/limereport/items/charts/lrlineschart.cpp @@ -70,12 +70,7 @@ void LinesChart::drawDesignMode(QPainter* painter, qreal hStep, qreal vStep, qre qreal LinesChart::calculatePos(const AxisData &data, qreal value, qreal rectSize) const { - if (data.reverseDirection() && data.rangeMin() >= 0) { - // Not flipping for minimum less than 0 because lower number is at the bottom. - return (1 - (data.rangeMax() - value) / data.delta()) * rectSize; - } else { - return (data.rangeMax() - value) / data.delta() * rectSize; - } + return (data.rangeMax() - value) / data.delta() * rectSize; } void LinesChart::paintSeries(QPainter *painter, SeriesItem *series, QRectF barsRect) diff --git a/limereport/lraxisdata.cpp b/limereport/lraxisdata.cpp index 34ef9e9..fbe7f2a 100644 --- a/limereport/lraxisdata.cpp +++ b/limereport/lraxisdata.cpp @@ -53,16 +53,6 @@ qreal AxisData::delta() const return m_delta; } -bool AxisData::reverseDirection() const -{ - return m_reverseDirection; -} - -void AxisData::setReverseDirection(bool reverseDirection) -{ - m_reverseDirection = reverseDirection; -} - void AxisData::calculateValuesAboveMax(qreal minValue, qreal maxValue, int segments) { const int delta = maxValue - minValue; diff --git a/limereport/lraxisdata.h b/limereport/lraxisdata.h index f69d72d..820eeb1 100644 --- a/limereport/lraxisdata.h +++ b/limereport/lraxisdata.h @@ -21,9 +21,6 @@ public: qreal delta() const; - bool reverseDirection() const; - void setReverseDirection(bool reverseDirection); - private: void calculateValuesAboveMax(qreal minValue, qreal maxValue, int segments); @@ -34,7 +31,6 @@ private: qreal m_step; qreal m_delta; int m_segmentCount; - bool m_reverseDirection; }; }; diff --git a/limereport/objectinspector/lrobjectitemmodel.cpp b/limereport/objectinspector/lrobjectitemmodel.cpp index cb5c2b6..122a921 100644 --- a/limereport/objectinspector/lrobjectitemmodel.cpp +++ b/limereport/objectinspector/lrobjectitemmodel.cpp @@ -171,6 +171,8 @@ void QObjectPropertyModel::translatePropertyName() tr("removeGap"); tr("dropPrinterMargins"); tr("notPrintIfEmpty"); + tr("gridChartLines"); + tr("horizontalAxisOnTop"); tr("mixWithPriorPage"); } From 2452e37334f1c4e73d9bb62a38893ca989f1ae8c Mon Sep 17 00:00:00 2001 From: Emil Sawicki Date: Fri, 28 Jan 2022 07:05:55 +0100 Subject: [PATCH 08/11] Add x axis field combobox to chart editor --- limereport/items/lrchartitem.cpp | 8 +------- limereport/items/lrchartitemeditor.cpp | 11 +++++++++-- limereport/items/lrchartitemeditor.h | 2 ++ limereport/items/lrchartitemeditor.ui | 22 ++++++++++++++++++---- 4 files changed, 30 insertions(+), 13 deletions(-) diff --git a/limereport/items/lrchartitem.cpp b/limereport/items/lrchartitem.cpp index 3dbeafa..2bec388 100644 --- a/limereport/items/lrchartitem.cpp +++ b/limereport/items/lrchartitem.cpp @@ -971,13 +971,7 @@ QString AbstractSeriesChart::axisLabel(int i, const AxisData &axisData) { const qreal min = axisData.rangeMin(); const qreal step = axisData.step(); - qreal value = 0; - // For values below 0, axis is already reversed (value closer to 0 is higher) - if (axisData.reverseDirection() && min >= 0) { - value = min + (axisData.segmentCount() - i) * step; - } else { - value = min + i * step; - } + qreal value = min + i * step; if (std::floor(step) == step) { return QString::number(value); } diff --git a/limereport/items/lrchartitemeditor.cpp b/limereport/items/lrchartitemeditor.cpp index e51ed22..c609c46 100644 --- a/limereport/items/lrchartitemeditor.cpp +++ b/limereport/items/lrchartitemeditor.cpp @@ -23,8 +23,6 @@ ChartItemEditor::ChartItemEditor(LimeReport::ChartItem *item, LimeReport::PageDe readSetting(); init(); connect(m_colorButton, SIGNAL(clicked(bool)), this, SLOT(slotChangeSeriesColor())); - - // TODO_ES add field in editor for x axis field, like for labels } ChartItemEditor::~ChartItemEditor() @@ -108,6 +106,7 @@ void ChartItemEditor::init() for (int i=0;icolumnCount();++i){ ui->valuesFieldComboBox->addItem(ds->columnNameByIndex(i)); ui->labelsFieldComboBox->addItem(ds->columnNameByIndex(i)); + ui->xAxisFieldComboBox->addItem(ds->columnNameByIndex(i)); } } } @@ -122,8 +121,10 @@ void ChartItemEditor::init() #if QT_VERSION < 0x050000 ui->labelsFieldComboBox->setCurrentIndex(ui->labelsFieldComboBox->findText( m_charItem->labelsField())); + ui->xAxisFieldComboBox->setCurrentIndex(ui->xAxisFieldComboBox->findText( m_charItem->xAxisField())); #else ui->labelsFieldComboBox->setCurrentText(m_charItem->labelsField()); + ui->xAxisFieldComboBox->setCurrentText(m_charItem->xAxisField()); #endif if (!m_charItem->series().isEmpty()){ enableSeriesEditor(); @@ -289,3 +290,9 @@ void ChartItemEditor::on_seriesTypeComboBox_currentIndexChanged(const QString &a currentSeries()->setPreferredType(static_cast(enumerator.keysToValue(arg1.toLatin1()))); } } + +void ChartItemEditor::on_xAxisFieldComboBox_currentTextChanged(const QString &arg1) +{ + if (!m_initing) + m_charItem->setXAxisField(arg1); +} \ No newline at end of file diff --git a/limereport/items/lrchartitemeditor.h b/limereport/items/lrchartitemeditor.h index f24123f..64e0549 100644 --- a/limereport/items/lrchartitemeditor.h +++ b/limereport/items/lrchartitemeditor.h @@ -41,6 +41,8 @@ private slots: void slotChangeSeriesColor(); void on_seriesTypeComboBox_currentIndexChanged(const QString &arg1); + void on_xAxisFieldComboBox_currentTextChanged(const QString &arg1); + private: void readSetting(); void writeSetting(); diff --git a/limereport/items/lrchartitemeditor.ui b/limereport/items/lrchartitemeditor.ui index a745777..20a5f4b 100644 --- a/limereport/items/lrchartitemeditor.ui +++ b/limereport/items/lrchartitemeditor.ui @@ -144,16 +144,30 @@ - - + + + + + true + + + + Labels field - - + + + + X data field + + + + + true From 25dff69679a293e6715367e01c078684294f1bc3 Mon Sep 17 00:00:00 2001 From: Emil Sawicki Date: Sat, 29 Jan 2022 08:57:50 +0100 Subject: [PATCH 09/11] Fix drawing vertical lines in grid chart --- limereport/items/charts/lrgridlineschart.cpp | 11 ++++++++-- limereport/items/lrchartitem.cpp | 21 +++++++++++--------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/limereport/items/charts/lrgridlineschart.cpp b/limereport/items/charts/lrgridlineschart.cpp index f96ab1b..3ed300f 100644 --- a/limereport/items/charts/lrgridlineschart.cpp +++ b/limereport/items/charts/lrgridlineschart.cpp @@ -21,14 +21,21 @@ void GridLinesChart::paintChart(QPainter *painter, QRectF chartRect) ) ); const qreal barsShift = calcRect.height(); - const qreal topOffset = painter->fontMetrics().height() * (m_chartItem->horizontalAxisOnTop() ? 1 : -1); - const QRectF gridRect = chartRect.adjusted( + const qreal topOffset = painter->fontMetrics().height(); + QRectF gridRect = chartRect.adjusted( hPadding, vPadding + valuesVMargin + topOffset, -hPadding * 3, -(vPadding + barsShift) ); + if (!m_chartItem->horizontalAxisOnTop()) { + // Draw labels above the grid + const qreal height = calcRect.height(); + calcRect.setBottom(gridRect.top()); + calcRect.setTop(calcRect.bottom() - height); + } + paintGrid(painter, gridRect); paintSerialLines( diff --git a/limereport/items/lrchartitem.cpp b/limereport/items/lrchartitem.cpp index 2bec388..b97259d 100644 --- a/limereport/items/lrchartitem.cpp +++ b/limereport/items/lrchartitem.cpp @@ -838,12 +838,15 @@ void AbstractSeriesChart::paintGrid(QPainter *painter, QRectF gridRect) const int yAxisSegmentCount = yAxisData.segmentCount(); const int yAxisLineCount = yAxisSegmentCount + 1; - const qreal gridOffset = hPadding(gridRect); const int fontHeight = painter->fontMetrics().height(); const int halfFontHeight = fontHeight / 2; + const QSizeF gridOffset = QSizeF(hPadding(gridRect), vPadding(gridRect)); const qreal valuesHMargin = this->valuesHMargin(painter); const qreal vStep = gridRect.height() / yAxisSegmentCount; - const qreal hStep = (gridRect.width() - valuesHMargin - gridOffset) / xAxisSegmentCount; + const qreal hStep = (gridRect.width() - valuesHMargin - gridOffset.width()) / xAxisSegmentCount; + const qreal textPositionHOffset = valuesHMargin * 0.2; + + // TODO_ES horizontal axis labels doesn't adapt font painter->setFont(adaptValuesFont(hStep-4, painter->font())); @@ -853,37 +856,37 @@ void AbstractSeriesChart::paintGrid(QPainter *painter, QRectF gridRect) const qreal y = vStep * i; const bool drawFullLine = m_chartItem->gridChartLines() & ChartItem::HorizontalLine || i == 0 || i == xAxisSegmentCount; - painter->drawText(QRectF(gridRect.bottomLeft()-QPointF(halfFontHeight, y + halfFontHeight), + painter->drawText(QRectF(gridRect.bottomLeft()-QPointF(textPositionHOffset, y + halfFontHeight), QSizeF(valuesHMargin,fontHeight)), axisLabel(i, yAxisData), verticalTextOption); QPointF lineEndPos = gridRect.bottomRight() - QPointF(0, y); if (!drawFullLine) { - lineEndPos.setX(gridRect.left() + valuesHMargin + gridOffset); + lineEndPos.setX(gridRect.left() + valuesHMargin + gridOffset.width()); } painter->drawLine(gridRect.bottomLeft() - QPointF(-valuesHMargin, y), lineEndPos); } // Horizontal axis lines for (int i = 0 ; i < xAxisLineCount ; i++) { - const qreal x = gridRect.left() + hStep * i + valuesHMargin + gridOffset; + const qreal x = gridRect.left() + hStep * i + valuesHMargin + gridOffset.width(); const bool drawFullLine = m_chartItem->gridChartLines() & ChartItem::VerticalLine || i == 0 || i == xAxisSegmentCount; const QString text = axisLabel(i, xAxisData); if (m_chartItem->horizontalAxisOnTop()) { - painter->drawLine(x, gridRect.top() - gridOffset, + painter->drawLine(x, gridRect.top() - gridOffset.height(), x, (drawFullLine ? gridRect.bottom() : gridRect.top())); painter->drawText(QRectF(x - painter->fontMetrics().width(text) / 2, - gridRect.top() - (fontHeight + gridOffset), + gridRect.top() - (fontHeight + gridOffset.height()), hStep, fontHeight), text); } else { - painter->drawLine(x, gridRect.bottom() + gridOffset, + painter->drawLine(x, gridRect.bottom() + gridOffset.height(), x, (drawFullLine ? gridRect.top() : gridRect.bottom())); painter->drawText(QRectF(x - painter->fontMetrics().width(text) / 2, - gridRect.bottom() + halfFontHeight + gridOffset, + gridRect.bottom() + halfFontHeight * 0 + gridOffset.height(), hStep, fontHeight), text); } From 74c0f52369bb3efc9958d8d5a2b3d8f916b46653 Mon Sep 17 00:00:00 2001 From: Emil Sawicki Date: Sun, 30 Jan 2022 17:02:24 +0100 Subject: [PATCH 10/11] Update font calculation --- limereport/items/charts/lrgridlineschart.cpp | 29 ++++++++++++-------- limereport/items/lrchartitem.cpp | 23 ++++++++-------- limereport/items/lrchartitem.h | 2 +- 3 files changed, 30 insertions(+), 24 deletions(-) diff --git a/limereport/items/charts/lrgridlineschart.cpp b/limereport/items/charts/lrgridlineschart.cpp index 3ed300f..6ce6411 100644 --- a/limereport/items/charts/lrgridlineschart.cpp +++ b/limereport/items/charts/lrgridlineschart.cpp @@ -5,12 +5,26 @@ void GridLinesChart::paintChart(QPainter *painter, QRectF chartRect) { updateMinAndMaxValues(); - const qreal valuesHMargin = this->valuesHMargin(painter); - const qreal valuesVMargin = this->valuesVMargin(painter); - const qreal hPadding = this->hPadding(chartRect); const qreal vPadding = this->vPadding(chartRect); + const qreal valuesVMargin = this->valuesVMargin(painter); + const qreal topOffset = painter->fontMetrics().height(); + + QRectF gridRect = chartRect.adjusted( + hPadding, + vPadding + valuesVMargin + topOffset, + -hPadding * 3, + -vPadding + ); + + // Adapt font for horizontal axis + painter->setFont(adaptFont((gridRect.width() - this->valuesHMargin(painter)) / xAxisData().segmentCount() * 0.8, + painter->font(), + xAxisData())); + + const qreal valuesHMargin = this->valuesHMargin(painter); + QRectF calcRect = horizontalLabelsRect( painter, chartRect.adjusted( @@ -20,14 +34,7 @@ void GridLinesChart::paintChart(QPainter *painter, QRectF chartRect) -vPadding ) ); - const qreal barsShift = calcRect.height(); - const qreal topOffset = painter->fontMetrics().height(); - QRectF gridRect = chartRect.adjusted( - hPadding, - vPadding + valuesVMargin + topOffset, - -hPadding * 3, - -(vPadding + barsShift) - ); + gridRect.adjust(0, 0, 0, -calcRect.height()); if (!m_chartItem->horizontalAxisOnTop()) { // Draw labels above the grid diff --git a/limereport/items/lrchartitem.cpp b/limereport/items/lrchartitem.cpp index b97259d..401b92f 100644 --- a/limereport/items/lrchartitem.cpp +++ b/limereport/items/lrchartitem.cpp @@ -777,7 +777,7 @@ void AbstractSeriesChart::paintHorizontalGrid(QPainter *painter, QRectF gridRect painter->setRenderHint(QPainter::Antialiasing,false); qreal hStep = (gridRect.width() - painter->fontMetrics().boundingRect(QString::number(maxValue())).width()) / segmentCount; - painter->setFont(adaptValuesFont(hStep-4, painter->font())); + painter->setFont(adaptFont(hStep-4, painter->font(), yAxisData)); QPointF textPos; if (m_chartItem->horizontalAxisOnTop()) { @@ -846,10 +846,6 @@ void AbstractSeriesChart::paintGrid(QPainter *painter, QRectF gridRect) const qreal hStep = (gridRect.width() - valuesHMargin - gridOffset.width()) / xAxisSegmentCount; const qreal textPositionHOffset = valuesHMargin * 0.2; - // TODO_ES horizontal axis labels doesn't adapt font - - painter->setFont(adaptValuesFont(hStep-4, painter->font())); - // Vertical axis lines const QTextOption verticalTextOption(Qt::AlignRight); for (int i = 0 ; i < yAxisLineCount ; i++ ) { @@ -956,16 +952,19 @@ QFont AbstractSeriesChart::adaptLabelsFont(QRectF rect, QFont font) return tmpFont; } -QFont AbstractSeriesChart::adaptValuesFont(qreal width, QFont font) +QFont AbstractSeriesChart::adaptFont(qreal width, QFont font, const AxisData &axisData) { - QString strValue = QString::number(maxValue()); QFont tmpFont = font; + const int axisLineCount = axisData.segmentCount() + 1; QScopedPointer fm(new QFontMetricsF(tmpFont)); - qreal curWidth = fm->boundingRect(strValue).width(); - while (curWidth > width && tmpFont.pixelSize() > 1){ - tmpFont.setPixelSize(tmpFont.pixelSize() - 1); - fm.reset(new QFontMetricsF(tmpFont)); - curWidth = fm->boundingRect(strValue).width(); + for (int i = 0 ; i < axisLineCount ; i++) { + QString strValue = axisLabel(i, axisData); + qreal curWidth = fm->boundingRect(strValue).width(); + while (curWidth > width && tmpFont.pixelSize() > 1){ + tmpFont.setPixelSize(tmpFont.pixelSize() - 1); + fm.reset(new QFontMetricsF(tmpFont)); + curWidth = fm->boundingRect(strValue).width(); + } } return tmpFont; } diff --git a/limereport/items/lrchartitem.h b/limereport/items/lrchartitem.h index e524fb2..a2f40a8 100644 --- a/limereport/items/lrchartitem.h +++ b/limereport/items/lrchartitem.h @@ -110,7 +110,7 @@ protected: virtual qreal valuesHMargin(QPainter *painter); virtual qreal valuesVMargin(QPainter *painter); virtual QFont adaptLabelsFont(QRectF rect, QFont font); - virtual QFont adaptValuesFont(qreal width, QFont font); + virtual QFont adaptFont(qreal width, QFont font, const AxisData &axisData); virtual QString axisLabel(int i, const AxisData &axisData); private: From df0e4d8393addbb736d8deb500fc621203614114 Mon Sep 17 00:00:00 2001 From: Emil Sawicki Date: Sun, 30 Jan 2022 17:46:04 +0100 Subject: [PATCH 11/11] Fix vertical axis labels padding --- limereport/items/charts/lrgridlineschart.cpp | 30 ++++++-------------- limereport/items/lrchartitem.cpp | 2 +- 2 files changed, 10 insertions(+), 22 deletions(-) diff --git a/limereport/items/charts/lrgridlineschart.cpp b/limereport/items/charts/lrgridlineschart.cpp index 6ce6411..5c2f3bc 100644 --- a/limereport/items/charts/lrgridlineschart.cpp +++ b/limereport/items/charts/lrgridlineschart.cpp @@ -9,15 +9,19 @@ void GridLinesChart::paintChart(QPainter *painter, QRectF chartRect) const qreal vPadding = this->vPadding(chartRect); const qreal valuesVMargin = this->valuesVMargin(painter); - const qreal topOffset = painter->fontMetrics().height(); QRectF gridRect = chartRect.adjusted( hPadding, - vPadding + valuesVMargin + topOffset, + vPadding + valuesVMargin * 2, -hPadding * 3, - -vPadding + -vPadding * 3 ); + if (!m_chartItem->horizontalAxisOnTop()) { + // If horizontal axis is on the bottom, move grid a little up + gridRect.adjust(0, -valuesVMargin, 0 , -valuesVMargin); + } + // Adapt font for horizontal axis painter->setFont(adaptFont((gridRect.width() - this->valuesHMargin(painter)) / xAxisData().segmentCount() * 0.8, painter->font(), @@ -25,23 +29,8 @@ void GridLinesChart::paintChart(QPainter *painter, QRectF chartRect) const qreal valuesHMargin = this->valuesHMargin(painter); - QRectF calcRect = horizontalLabelsRect( - painter, - chartRect.adjusted( - hPadding * 2 + valuesHMargin, - chartRect.height() - (painter->fontMetrics().height() + vPadding*2), - -(hPadding * 2), - -vPadding - ) - ); - gridRect.adjust(0, 0, 0, -calcRect.height()); - - if (!m_chartItem->horizontalAxisOnTop()) { - // Draw labels above the grid - const qreal height = calcRect.height(); - calcRect.setBottom(gridRect.top()); - calcRect.setTop(calcRect.bottom() - height); - } + // Adjust vertical axis labels padding + gridRect.adjust(valuesHMargin * 0.2, 0, 0, 0); paintGrid(painter, gridRect); @@ -49,7 +38,6 @@ void GridLinesChart::paintChart(QPainter *painter, QRectF chartRect) painter, gridRect.adjusted(hPadding + valuesHMargin, 0, 0, 0) ); - paintHorizontalLabels(painter, calcRect); } void GridLinesChart::paintSerialLines(QPainter* painter, QRectF barsRect) diff --git a/limereport/items/lrchartitem.cpp b/limereport/items/lrchartitem.cpp index 401b92f..565b3cf 100644 --- a/limereport/items/lrchartitem.cpp +++ b/limereport/items/lrchartitem.cpp @@ -844,7 +844,7 @@ void AbstractSeriesChart::paintGrid(QPainter *painter, QRectF gridRect) const qreal valuesHMargin = this->valuesHMargin(painter); const qreal vStep = gridRect.height() / yAxisSegmentCount; const qreal hStep = (gridRect.width() - valuesHMargin - gridOffset.width()) / xAxisSegmentCount; - const qreal textPositionHOffset = valuesHMargin * 0.2; + const qreal textPositionHOffset = valuesHMargin * 0.1; // Vertical axis lines const QTextOption verticalTextOption(Qt::AlignRight);