From a3ffc08d4973cf1315af91f11f2a870c34e8b4d6 Mon Sep 17 00:00:00 2001 From: Emil Sawicki Date: Tue, 25 Jan 2022 19:13:00 +0100 Subject: [PATCH] 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");