mirror of
https://github.com/fralx/LimeReport.git
synced 2024-12-24 00:33:02 +03:00
Add grid chart
This commit is contained in:
parent
b0f45f55a4
commit
fdee26a2b8
110
limereport/items/charts/lrgridlineschart.cpp
Normal file
110
limereport/items/charts/lrgridlineschart.cpp
Normal file
@ -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<qreal> &xAxisValues = series->data()->xAxisValues();
|
||||||
|
const QList<qreal> &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();
|
||||||
|
}
|
||||||
|
}
|
17
limereport/items/charts/lrgridlineschart.h
Normal file
17
limereport/items/charts/lrgridlineschart.h
Normal file
@ -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
|
@ -13,6 +13,7 @@
|
|||||||
#include "charts/lrverticalbarchart.h"
|
#include "charts/lrverticalbarchart.h"
|
||||||
#include "charts/lrhorizontalbarchart.h"
|
#include "charts/lrhorizontalbarchart.h"
|
||||||
#include "charts/lrlineschart.h"
|
#include "charts/lrlineschart.h"
|
||||||
|
#include "charts/lrgridlineschart.h"
|
||||||
|
|
||||||
namespace{
|
namespace{
|
||||||
|
|
||||||
@ -341,6 +342,10 @@ void ChartItem::setChartType(const ChartType &chartType)
|
|||||||
break;
|
break;
|
||||||
case Lines:
|
case Lines:
|
||||||
m_chart = new LinesChart(this);
|
m_chart = new LinesChart(this);
|
||||||
|
break;
|
||||||
|
case GridLines:
|
||||||
|
m_chart = new GridLinesChart(this);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
notify("chartType",oldValue,m_chartType);
|
notify("chartType",oldValue,m_chartType);
|
||||||
update();
|
update();
|
||||||
@ -612,8 +617,11 @@ void AbstractSeriesChart::updateMinAndMaxValues()
|
|||||||
maxYValue = std::max(maxYValue, value);
|
maxYValue = std::max(maxYValue, value);
|
||||||
}
|
}
|
||||||
if (series->data()->xAxisValues().isEmpty()) {
|
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);
|
minXValue = std::min(0.0, minXValue);
|
||||||
maxXValue = std::max((qreal)valuesCount(), maxXValue);
|
maxXValue = std::max(valuesCount, maxXValue);
|
||||||
} else {
|
} else {
|
||||||
for (qreal value : series->data()->xAxisValues()){
|
for (qreal value : series->data()->xAxisValues()){
|
||||||
minXValue = std::min(value, minXValue);
|
minXValue = std::min(value, minXValue);
|
||||||
@ -785,7 +793,7 @@ void AbstractSeriesChart::paintVerticalGrid(QPainter *painter, QRectF gridRect)
|
|||||||
const qreal y = vStep * i;
|
const qreal y = vStep * i;
|
||||||
painter->drawText(QRectF(gridRect.bottomLeft()-QPointF(textPositionOffset,y+halfFontHeight),
|
painter->drawText(QRectF(gridRect.bottomLeft()-QPointF(textPositionOffset,y+halfFontHeight),
|
||||||
QSizeF(valuesHMargin,fontHeight)),
|
QSizeF(valuesHMargin,fontHeight)),
|
||||||
verticalLabel(i, yAxisData.step(), yAxisData.rangeMin()),
|
axisLabel(i, yAxisData),
|
||||||
verticalTextOption);
|
verticalTextOption);
|
||||||
painter->drawLine(gridRect.bottomLeft()-QPointF(-valuesHMargin,y),
|
painter->drawLine(gridRect.bottomLeft()-QPointF(-valuesHMargin,y),
|
||||||
gridRect.bottomRight()-QPointF(0,y));
|
gridRect.bottomRight()-QPointF(0,y));
|
||||||
@ -794,6 +802,65 @@ void AbstractSeriesChart::paintVerticalGrid(QPainter *painter, QRectF gridRect)
|
|||||||
painter->setRenderHint(QPainter::Antialiasing,true);
|
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)
|
void AbstractSeriesChart::drawSegment(QPainter *painter, QPoint startPoint, QPoint endPoint, QColor color)
|
||||||
{
|
{
|
||||||
int radius = m_chartItem->seriesLineWidth();
|
int radius = m_chartItem->seriesLineWidth();
|
||||||
@ -819,7 +886,7 @@ qreal AbstractSeriesChart::valuesHMargin(QPainter *painter)
|
|||||||
const int yAxisLineCount = yAxisData.segmentCount() + 1;
|
const int yAxisLineCount = yAxisData.segmentCount() + 1;
|
||||||
|
|
||||||
for (int i = 0 ; i < yAxisLineCount ; i++) {
|
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);
|
max = std::max(max, (qreal)painter->fontMetrics().boundingRect(label).width()+offset);
|
||||||
}
|
}
|
||||||
return max;
|
return max;
|
||||||
@ -869,14 +936,22 @@ QFont AbstractSeriesChart::adaptValuesFont(qreal width, QFont font)
|
|||||||
return tmpFont;
|
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) {
|
if (std::floor(step) == step) {
|
||||||
return QString::number(value);
|
return QString::number(value);
|
||||||
}
|
}
|
||||||
// For float round numbers to small precision
|
// 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)
|
void AbstractBarChart::paintChartLegend(QPainter *painter, QRectF legendRect)
|
||||||
|
@ -104,13 +104,14 @@ protected:
|
|||||||
virtual void paintHorizontalLabels(QPainter *painter, QRectF labelsRect);
|
virtual void paintHorizontalLabels(QPainter *painter, QRectF labelsRect);
|
||||||
virtual void paintVerticalLabels(QPainter *painter, QRectF labelsRect);
|
virtual void paintVerticalLabels(QPainter *painter, QRectF labelsRect);
|
||||||
virtual void paintHorizontalGrid(QPainter *painter, QRectF gridRect);
|
virtual void paintHorizontalGrid(QPainter *painter, QRectF gridRect);
|
||||||
|
virtual void paintGrid(QPainter *painter, QRectF gridRect);
|
||||||
virtual void paintVerticalGrid(QPainter *painter, QRectF gridRect);
|
virtual void paintVerticalGrid(QPainter *painter, QRectF gridRect);
|
||||||
virtual void drawSegment(QPainter *painter, QPoint startPoint, QPoint endPoint, QColor color);
|
virtual void drawSegment(QPainter *painter, QPoint startPoint, QPoint endPoint, QColor color);
|
||||||
virtual qreal valuesHMargin(QPainter *painter);
|
virtual qreal valuesHMargin(QPainter *painter);
|
||||||
virtual qreal valuesVMargin(QPainter *painter);
|
virtual qreal valuesVMargin(QPainter *painter);
|
||||||
virtual QFont adaptLabelsFont(QRectF rect, QFont font);
|
virtual QFont adaptLabelsFont(QRectF rect, QFont font);
|
||||||
virtual QFont adaptValuesFont(qreal width, 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:
|
private:
|
||||||
AxisData m_yAxisData, m_xAxisData;
|
AxisData m_yAxisData, m_xAxisData;
|
||||||
@ -148,8 +149,9 @@ class ChartItem : public LimeReport::ItemDesignIntf
|
|||||||
public:
|
public:
|
||||||
|
|
||||||
enum LegendAlign{LegendAlignTop,LegendAlignCenter,LegendAlignBottom};
|
enum LegendAlign{LegendAlignTop,LegendAlignCenter,LegendAlignBottom};
|
||||||
|
enum LegendStyle{LegendPoints, LegendLines};
|
||||||
enum TitleAlign{TitleAlignLeft, TitleAlignCenter, TitleAlignRight};
|
enum TitleAlign{TitleAlignLeft, TitleAlignCenter, TitleAlignRight};
|
||||||
enum ChartType{Pie, VerticalBar, HorizontalBar, Lines};
|
enum ChartType{Pie, VerticalBar, HorizontalBar, Lines, GridLines};
|
||||||
#if QT_VERSION >= 0x050500
|
#if QT_VERSION >= 0x050500
|
||||||
Q_ENUM(LegendAlign)
|
Q_ENUM(LegendAlign)
|
||||||
Q_ENUM(TitleAlign)
|
Q_ENUM(TitleAlign)
|
||||||
|
@ -49,6 +49,7 @@ SOURCES += \
|
|||||||
$$REPORT_PATH/items/lrchartitemeditor.cpp \
|
$$REPORT_PATH/items/lrchartitemeditor.cpp \
|
||||||
$$REPORT_PATH/items/charts/lrhorizontalbarchart.cpp \
|
$$REPORT_PATH/items/charts/lrhorizontalbarchart.cpp \
|
||||||
$$REPORT_PATH/items/charts/lrlineschart.cpp \
|
$$REPORT_PATH/items/charts/lrlineschart.cpp \
|
||||||
|
$$REPORT_PATH/items/charts/lrgridlineschart.cpp \
|
||||||
$$REPORT_PATH/items/charts/lrpiechart.cpp \
|
$$REPORT_PATH/items/charts/lrpiechart.cpp \
|
||||||
$$REPORT_PATH/items/charts/lrverticalbarchart.cpp \
|
$$REPORT_PATH/items/charts/lrverticalbarchart.cpp \
|
||||||
$$REPORT_PATH/lrbanddesignintf.cpp \
|
$$REPORT_PATH/lrbanddesignintf.cpp \
|
||||||
@ -132,6 +133,7 @@ HEADERS += \
|
|||||||
$$REPORT_PATH/items/lrchartitemeditor.h \
|
$$REPORT_PATH/items/lrchartitemeditor.h \
|
||||||
$$REPORT_PATH/items/charts/lrhorizontalbarchart.h \
|
$$REPORT_PATH/items/charts/lrhorizontalbarchart.h \
|
||||||
$$REPORT_PATH/items/charts/lrlineschart.h \
|
$$REPORT_PATH/items/charts/lrlineschart.h \
|
||||||
|
$$REPORT_PATH/items/charts/lrgridlineschart.h \
|
||||||
$$REPORT_PATH/items/charts/lrpiechart.h \
|
$$REPORT_PATH/items/charts/lrpiechart.h \
|
||||||
$$REPORT_PATH/items/charts/lrverticalbarchart.h \
|
$$REPORT_PATH/items/charts/lrverticalbarchart.h \
|
||||||
$$REPORT_PATH/lrbanddesignintf.h \
|
$$REPORT_PATH/lrbanddesignintf.h \
|
||||||
|
Loading…
Reference in New Issue
Block a user