0
0
mirror of https://github.com/fralx/LimeReport.git synced 2024-12-24 00:33:02 +03:00

Merge pull request #380 from emil-sawicki9/feature/charts-x-axis

Added grid chart with x axis
This commit is contained in:
Alexander Arin 2022-02-01 00:01:08 +03:00 committed by GitHub
commit bc7666c351
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 411 additions and 45 deletions

View File

@ -0,0 +1,112 @@
#include "lrgridlineschart.h"
namespace LimeReport {
void GridLinesChart::paintChart(QPainter *painter, QRectF chartRect)
{
updateMinAndMaxValues();
const qreal hPadding = this->hPadding(chartRect);
const qreal vPadding = this->vPadding(chartRect);
const qreal valuesVMargin = this->valuesVMargin(painter);
QRectF gridRect = chartRect.adjusted(
hPadding,
vPadding + valuesVMargin * 2,
-hPadding * 3,
-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(),
xAxisData()));
const qreal valuesHMargin = this->valuesHMargin(painter);
// Adjust vertical axis labels padding
gridRect.adjust(valuesHMargin * 0.2, 0, 0, 0);
paintGrid(painter, gridRect);
paintSerialLines(
painter,
gridRect.adjusted(hPadding + valuesHMargin, 0, 0, 0)
);
}
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();
}
}

View 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

View File

@ -20,7 +20,9 @@ void HorizontalBarChart::paintChart(QPainter *painter, QRectF chartRect)
paintHorizontalGrid(painter, chartRect.adjusted( paintHorizontalGrid(painter, chartRect.adjusted(
hPadding(chartRect) + barsShift, hPadding(chartRect) + barsShift,
vPadding(chartRect), vPadding(chartRect),
-(hPadding(chartRect)),-vPadding(chartRect))); -(hPadding(chartRect)),
-vPadding(chartRect)));
paintHorizontalBars(painter, chartRect.adjusted( paintHorizontalBars(painter, chartRect.adjusted(
hPadding(chartRect) + barsShift, hPadding(chartRect) + barsShift,
vPadding(chartRect) * 2, vPadding(chartRect) * 2,
@ -36,25 +38,33 @@ void HorizontalBarChart::paintHorizontalBars(QPainter *painter, QRectF barsRect)
painter->save(); painter->save();
painter->setRenderHint(QPainter::Antialiasing,false); painter->setRenderHint(QPainter::Antialiasing,false);
const AxisData &yAxisData = this->yAxisData(); const AxisData &yAxisData = this->yAxisData();
const qreal delta = yAxisData.delta(); 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; qreal hStep = (barsRect.width()-painter->fontMetrics().boundingRect(QString::number(maxValue())).width()) / delta;
if (!m_chartItem->series().isEmpty() && (m_chartItem->itemMode() != DesignMode)){ 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()) { foreach (SeriesItem* series, m_chartItem->series()) {
qreal curVOffset = curSeries*vStep+barsRect.top();
painter->setBrush(series->color()); painter->setBrush(series->color());
qreal y = curVOffset;
foreach (qreal value, series->data()->values()) { foreach (qreal value, series->data()->values()) {
painter->drawRect(QRectF((-minValue()*hStep)+barsRect.left(), curVOffset, value*hStep, vStep)); painter->drawRect(QRectF((-minValue()*hStep)+barsRect.left(), y, value*hStep, vStep));
curVOffset+=vStep*seriesCount(); y+=vStep*seriesCount();
} }
curSeries++; curVOffset += vStep;
} }
} else { } else {
qreal curVOffset = barsRect.top(); qreal curVOffset = barsRect.top();
if (m_chartItem->horizontalAxisOnTop()) {
curVOffset += verticalOffset;
}
int curColor = 0; int curColor = 0;
for (int i=0; i<9; ++i){ for (int i=0; i<9; ++i){
if (curColor==3) curColor=0; if (curColor==3) curColor=0;

View File

@ -68,17 +68,18 @@ 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; return (data.rangeMax() - value) / data.delta() * rectSize;
} }
void LinesChart::paintSeries(QPainter *painter, SeriesItem *series, QRectF barsRect) void LinesChart::paintSeries(QPainter *painter, SeriesItem *series, QRectF barsRect)
{ {
const AxisData &yAxisData = this->yAxisData(); const AxisData &yAxisData = this->yAxisData();
const qreal delta = yAxisData.delta(); const AxisData &xAxisData = this->xAxisData();
const qreal hStep = barsRect.width() / valuesCount(); const qreal xAxisDiff = std::max(1.0, xAxisData.maxValue() - xAxisData.minValue());
const qreal hStep = barsRect.width() / xAxisDiff;
const qreal topMargin = barsRect.top(); const qreal topMargin = barsRect.top();
QPen pen(series->color()); QPen pen(series->color());
@ -91,11 +92,11 @@ void LinesChart::paintSeries(QPainter *painter, SeriesItem *series, QRectF barsR
qreal lastXValue = barsRect.left() + hStep/2; qreal lastXValue = barsRect.left() + hStep/2;
if (!values.isEmpty()) { if (!values.isEmpty()) {
// Calculate first point position on plot before loop // 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 ){ for (int i = 0; i < values.count()-1; ++i ){
const qreal startY = lastYValue; 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 // Record last used Y position to only calculate new one
lastYValue = endY; lastYValue = endY;

View File

@ -10,7 +10,7 @@ public:
void paintChart(QPainter *painter, QRectF chartRect); void paintChart(QPainter *painter, QRectF chartRect);
protected: protected:
void drawDesignMode(QPainter *painter, qreal hStep, qreal vStep, qreal topShift, QRectF barsRect); 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); void paintSeries(QPainter *painter, SeriesItem *series, QRectF barsRect);
private: private:

View File

@ -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{
@ -79,6 +80,16 @@ void SeriesItem::setLabelsColumn(const QString &labelsColumn)
m_labelsColumn = labelsColumn; m_labelsColumn = labelsColumn;
} }
QString SeriesItem::xAxisColumn() const
{
return m_xAxisColumn;
}
void SeriesItem::setXAxisColumn(const QString &xAxisColumn)
{
m_xAxisColumn = xAxisColumn;
}
SeriesItem *SeriesItem::clone() SeriesItem *SeriesItem::clone()
{ {
SeriesItem* result = new SeriesItem(); SeriesItem* result = new SeriesItem();
@ -98,6 +109,8 @@ void SeriesItem::fillSeriesData(IDataSource *dataSource)
while(!dataSource->eof()){ while(!dataSource->eof()){
if (!m_labelsColumn.isEmpty()) if (!m_labelsColumn.isEmpty())
m_data.labels().append(dataSource->data(m_labelsColumn).toString()); 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.values().append(dataSource->data(m_valuesColumn).toDouble());
m_data.colors().append((currentColorIndex<32)?color_map[currentColorIndex]:generateColor()); m_data.colors().append((currentColorIndex<32)?color_map[currentColorIndex]:generateColor());
dataSource->next(); dataSource->next();
@ -130,7 +143,8 @@ ChartItem::ChartItem(QObject *owner, QGraphicsItem *parent)
: ItemDesignIntf(xmlTag, owner, parent), m_legendBorder(true), : ItemDesignIntf(xmlTag, owner, parent), m_legendBorder(true),
m_legendAlign(LegendAlignCenter), m_titleAlign(TitleAlignCenter), m_legendAlign(LegendAlignCenter), m_titleAlign(TitleAlignCenter),
m_chartType(Pie), m_labelsField(""), m_isEmpty(true), 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_gridChartLines(AllLines)
{ {
m_labels<<"First"<<"Second"<<"Thrid"; m_labels<<"First"<<"Second"<<"Thrid";
m_chart = new PieChart(this); m_chart = new PieChart(this);
@ -235,6 +249,7 @@ void ChartItem::updateItemSize(DataSourceManager *dataManager, RenderPass , int
foreach (SeriesItem* series, m_series) { foreach (SeriesItem* series, m_series) {
if (series->isEmpty()){ if (series->isEmpty()){
series->setLabelsColumn(m_labelsField); series->setLabelsColumn(m_labelsField);
series->setXAxisColumn(m_xAxisField);
series->fillSeriesData(ds); series->fillSeriesData(ds);
} }
} }
@ -327,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();
@ -464,6 +483,50 @@ void ChartItem::setSeriesLineWidth(int newSeriesLineWidth)
m_seriesLineWidth = 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;
}
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) AbstractChart::AbstractChart(ChartItem *chartItem)
:m_chartItem(chartItem) :m_chartItem(chartItem)
{ {
@ -552,22 +615,43 @@ AxisData AbstractSeriesChart::yAxisData()
return m_yAxisData; return m_yAxisData;
} }
AxisData AbstractSeriesChart::xAxisData()
{
return m_xAxisData;
}
void AbstractSeriesChart::updateMinAndMaxValues() void AbstractSeriesChart::updateMinAndMaxValues()
{ {
qreal maxYValue = 0; qreal maxYValue = 0;
qreal minYValue = 0; qreal minYValue = 0;
qreal maxXValue = 0;
qreal minXValue = 0;
if (m_chartItem->itemMode() == DesignMode) { if (m_chartItem->itemMode() == DesignMode) {
maxYValue = 40; maxYValue = 40;
maxXValue = 40;
} else { } else {
for (SeriesItem* series : m_chartItem->series()){ for (SeriesItem* series : m_chartItem->series()){
for (qreal value : series->data()->values()){ for (qreal value : series->data()->values()){
minYValue = std::min(minYValue, value); minYValue = std::min(minYValue, value);
maxYValue = std::max(maxYValue, 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);
}
}
} }
} }
m_yAxisData = AxisData(minYValue, maxYValue); m_yAxisData = AxisData(minYValue, maxYValue);
m_xAxisData = AxisData(minXValue, maxXValue);
} }
qreal AbstractSeriesChart::hPadding(QRectF chartRect) qreal AbstractSeriesChart::hPadding(QRectF chartRect)
@ -686,7 +770,6 @@ void AbstractSeriesChart::paintHorizontalGrid(QPainter *painter, QRectF gridRect
painter->save(); painter->save();
const AxisData &yAxisData = this->yAxisData(); const AxisData &yAxisData = this->yAxisData();
const qreal delta = yAxisData.delta();
const int segmentCount = yAxisData.segmentCount(); const int segmentCount = yAxisData.segmentCount();
const int lineCount = segmentCount + 1; const int lineCount = segmentCount + 1;
@ -694,15 +777,20 @@ void AbstractSeriesChart::paintHorizontalGrid(QPainter *painter, QRectF gridRect
painter->setRenderHint(QPainter::Antialiasing,false); painter->setRenderHint(QPainter::Antialiasing,false);
qreal hStep = (gridRect.width() - painter->fontMetrics().boundingRect(QString::number(maxValue())).width()) / segmentCount; 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()) {
textPos.setY(gridRect.top());
} else {
textPos.setY(gridRect.bottom() - painter->fontMetrics().height());
}
for (int i = 0 ; i < lineCount ; i++ ) { for (int i = 0 ; i < lineCount ; i++ ) {
painter->drawText(QRectF(gridRect.left() + 4 + hStep * i, gridRect.bottom() - painter->fontMetrics().height(), const qreal x = gridRect.left() + hStep * i;
hStep, painter->fontMetrics().height()), textPos.setX(x + 4);
QString::number(minValue() + i * delta / segmentCount)); painter->drawText(QRectF(textPos, QSizeF(hStep, painter->fontMetrics().height())),
painter->drawLine( gridRect.left()+hStep*i, gridRect.bottom(), axisLabel(i, yAxisData));
gridRect.left()+hStep*i, gridRect.top()); painter->drawLine(x, gridRect.bottom(), x, gridRect.top());
} }
painter->restore(); painter->restore();
} }
@ -727,7 +815,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));
@ -736,6 +824,73 @@ 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 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.width()) / xAxisSegmentCount;
const qreal textPositionHOffset = valuesHMargin * 0.1;
// Vertical axis lines
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(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.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.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.height(),
x, (drawFullLine ? gridRect.bottom() : gridRect.top()));
painter->drawText(QRectF(x - painter->fontMetrics().width(text) / 2,
gridRect.top() - (fontHeight + gridOffset.height()),
hStep, fontHeight),
text);
} else {
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 * 0 + gridOffset.height(),
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();
@ -761,7 +916,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;
@ -797,28 +952,33 @@ QFont AbstractSeriesChart::adaptLabelsFont(QRectF rect, QFont font)
return tmpFont; 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; QFont tmpFont = font;
const int axisLineCount = axisData.segmentCount() + 1;
QScopedPointer<QFontMetricsF> fm(new QFontMetricsF(tmpFont)); QScopedPointer<QFontMetricsF> fm(new QFontMetricsF(tmpFont));
qreal curWidth = fm->boundingRect(strValue).width(); for (int i = 0 ; i < axisLineCount ; i++) {
while (curWidth > width && tmpFont.pixelSize() > 1){ QString strValue = axisLabel(i, axisData);
tmpFont.setPixelSize(tmpFont.pixelSize() - 1); qreal curWidth = fm->boundingRect(strValue).width();
fm.reset(new QFontMetricsF(tmpFont)); while (curWidth > width && tmpFont.pixelSize() > 1){
curWidth = fm->boundingRect(strValue).width(); tmpFont.setPixelSize(tmpFont.pixelSize() - 1);
fm.reset(new QFontMetricsF(tmpFont));
curWidth = fm->boundingRect(strValue).width();
}
} }
return tmpFont; return tmpFont;
} }
QString AbstractSeriesChart::verticalLabel(int i, qreal step, qreal min) QString AbstractSeriesChart::axisLabel(int i, const AxisData &axisData)
{ {
const qreal min = axisData.rangeMin();
const qreal step = axisData.step();
qreal value = min + i * step; qreal 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)
@ -897,4 +1057,5 @@ QRectF AbstractBarChart::horizontalLabelsRect(QPainter *painter, QRectF labelsRe
else else
return labelsRect.adjusted(0, (labelsRect.height() - maxWidth), 0, 0); return labelsRect.adjusted(0, (labelsRect.height() - maxWidth), 0, 0);
} }
} // namespace LimeReport } // namespace LimeReport

View File

@ -16,11 +16,12 @@ class SeriesItemData : public QObject{
Q_OBJECT Q_OBJECT
public: public:
QList<qreal>& values(){ return m_values;} QList<qreal>& values(){ return m_values;}
QList<qreal>& xAxisValues(){ return m_xAxisValues;}
QList<QString>& labels(){ return m_labels;} QList<QString>& labels(){ return m_labels;}
QList<QColor>& colors() { return m_colors;} QList<QColor>& colors() { return m_colors;}
void clear(){ m_values.clear(); m_labels.clear(); m_colors.clear(); } void clear(){ m_values.clear(); m_labels.clear(); m_colors.clear(); }
private: private:
QList<qreal> m_values; QList<qreal> m_values, m_xAxisValues;
QList<QString> m_labels; QList<QString> m_labels;
QList<QColor> m_colors; QList<QColor> m_colors;
}; };
@ -30,6 +31,7 @@ class SeriesItem : public QObject{
Q_PROPERTY(QString name READ name WRITE setName) Q_PROPERTY(QString name READ name WRITE setName)
Q_PROPERTY(QString valuesColumn READ valuesColumn WRITE setValuesColumn) Q_PROPERTY(QString valuesColumn READ valuesColumn WRITE setValuesColumn)
Q_PROPERTY(QString labelsColumn READ labelsColumn WRITE setLabelsColumn) 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(QColor color READ color WRITE setColor)
Q_PROPERTY(SeriesItemPreferredType preferredType READ preferredType WRITE setPreferredType) Q_PROPERTY(SeriesItemPreferredType preferredType READ preferredType WRITE setPreferredType)
public: public:
@ -46,6 +48,8 @@ public:
void setValuesColumn(const QString &valuesColumn); void setValuesColumn(const QString &valuesColumn);
QString labelsColumn() const; QString labelsColumn() const;
void setLabelsColumn(const QString &labelsColumn); void setLabelsColumn(const QString &labelsColumn);
QString xAxisColumn() const;
void setXAxisColumn(const QString &xAxisColumn);
SeriesItem* clone(); SeriesItem* clone();
void fillSeriesData(IDataSource* dataSource); void fillSeriesData(IDataSource* dataSource);
SeriesItemData* data(){ return &m_data;} SeriesItemData* data(){ return &m_data;}
@ -58,6 +62,7 @@ private:
QString m_name; QString m_name;
QString m_valuesColumn; QString m_valuesColumn;
QString m_labelsColumn; QString m_labelsColumn;
QString m_xAxisColumn;
SeriesItemData m_data; SeriesItemData m_data;
QColor m_color; QColor m_color;
SeriesItemPreferredType m_preferredType; SeriesItemPreferredType m_preferredType;
@ -84,9 +89,10 @@ class AbstractSeriesChart: public AbstractChart{
public: public:
AbstractSeriesChart(ChartItem* chartItem); AbstractSeriesChart(ChartItem* chartItem);
protected: protected:
AxisData yAxisData();
AxisData xAxisData();
qreal maxValue(); qreal maxValue();
qreal minValue(); qreal minValue();
AxisData yAxisData();
void updateMinAndMaxValues(); void updateMinAndMaxValues();
int valuesCount(); int valuesCount();
int seriesCount(); int seriesCount();
@ -98,16 +104,17 @@ 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 adaptFont(qreal width, QFont font, const AxisData &axisData);
virtual QString verticalLabel(int i, qreal step, qreal min); virtual QString axisLabel(int i, const AxisData &axisData);
private: private:
AxisData m_yAxisData; AxisData m_yAxisData, m_xAxisData;
qreal m_designValues [9]; qreal m_designValues [9];
}; };
@ -136,21 +143,37 @@ class ChartItem : public LimeReport::ItemDesignIntf
//linesChart //linesChart
Q_PROPERTY(bool drawPoints READ drawPoints WRITE setDrawPoints) Q_PROPERTY(bool drawPoints READ drawPoints WRITE setDrawPoints)
Q_PROPERTY(int seriesLineWidth READ seriesLineWidth WRITE setSeriesLineWidth) Q_PROPERTY(int seriesLineWidth READ seriesLineWidth WRITE setSeriesLineWidth)
Q_PROPERTY(bool horizontalAxisOnTop READ horizontalAxisOnTop WRITE setHorizontalAxisOnTop)
//gridChart
Q_FLAGS(GridChartLines)
Q_PROPERTY(QString xAxisField READ xAxisField WRITE setXAxisField)
Q_PROPERTY(GridChartLines gridChartLines READ gridChartLines WRITE setGridChartLines)
friend class AbstractChart; friend class AbstractChart;
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};
enum LineType {
NoLine = 0,
HorizontalLine = 1,
VerticalLine = 2,
AllLines = 3
};
#if QT_VERSION >= 0x050500 #if QT_VERSION >= 0x050500
Q_ENUM(LegendAlign) Q_ENUM(LegendAlign)
Q_ENUM(TitleAlign) Q_ENUM(TitleAlign)
Q_ENUM(ChartType) Q_ENUM(ChartType)
Q_ENUM(LineType)
#else #else
Q_ENUMS(LegendAlign) Q_ENUMS(LegendAlign)
Q_ENUMS(TitleAlign) Q_ENUMS(TitleAlign)
Q_ENUMS(ChartType) Q_ENUMS(ChartType)
Q_ENUMS(LineType)
#endif #endif
Q_DECLARE_FLAGS(GridChartLines, LineType)
ChartItem(QObject* owner, QGraphicsItem* parent); ChartItem(QObject* owner, QGraphicsItem* parent);
~ChartItem(); ~ChartItem();
@ -194,6 +217,15 @@ public:
int seriesLineWidth() const; int seriesLineWidth() const;
void setSeriesLineWidth(int newSeriesLineWidth); void setSeriesLineWidth(int newSeriesLineWidth);
QString xAxisField() const;
void setXAxisField(const QString &xAxisField);
bool horizontalAxisOnTop() const;
void setHorizontalAxisOnTop(bool horizontalAxisOnTop);
GridChartLines gridChartLines() const;
void setGridChartLines(GridChartLines flags);
protected: protected:
void paintChartTitle(QPainter* painter, QRectF titleRect); void paintChartTitle(QPainter* painter, QRectF titleRect);
virtual BaseDesignIntf* createSameTypeItem(QObject *owner, QGraphicsItem *parent); virtual BaseDesignIntf* createSameTypeItem(QObject *owner, QGraphicsItem *parent);
@ -222,6 +254,9 @@ private:
bool m_showLegend; bool m_showLegend;
bool m_drawPoints; bool m_drawPoints;
int m_seriesLineWidth; int m_seriesLineWidth;
QString m_xAxisField;
bool m_horizontalAxisOnTop;
GridChartLines m_gridChartLines;
}; };
} //namespace LimeReport } //namespace LimeReport
#endif // LRCHARTITEM_H #endif // LRCHARTITEM_H

View File

@ -106,6 +106,7 @@ void ChartItemEditor::init()
for (int i=0;i<ds->columnCount();++i){ for (int i=0;i<ds->columnCount();++i){
ui->valuesFieldComboBox->addItem(ds->columnNameByIndex(i)); ui->valuesFieldComboBox->addItem(ds->columnNameByIndex(i));
ui->labelsFieldComboBox->addItem(ds->columnNameByIndex(i)); ui->labelsFieldComboBox->addItem(ds->columnNameByIndex(i));
ui->xAxisFieldComboBox->addItem(ds->columnNameByIndex(i));
} }
} }
} }
@ -120,8 +121,10 @@ void ChartItemEditor::init()
#if QT_VERSION < 0x050000 #if QT_VERSION < 0x050000
ui->labelsFieldComboBox->setCurrentIndex(ui->labelsFieldComboBox->findText( m_charItem->labelsField())); ui->labelsFieldComboBox->setCurrentIndex(ui->labelsFieldComboBox->findText( m_charItem->labelsField()));
ui->xAxisFieldComboBox->setCurrentIndex(ui->xAxisFieldComboBox->findText( m_charItem->xAxisField()));
#else #else
ui->labelsFieldComboBox->setCurrentText(m_charItem->labelsField()); ui->labelsFieldComboBox->setCurrentText(m_charItem->labelsField());
ui->xAxisFieldComboBox->setCurrentText(m_charItem->xAxisField());
#endif #endif
if (!m_charItem->series().isEmpty()){ if (!m_charItem->series().isEmpty()){
enableSeriesEditor(); enableSeriesEditor();
@ -287,3 +290,9 @@ void ChartItemEditor::on_seriesTypeComboBox_currentIndexChanged(const QString &a
currentSeries()->setPreferredType(static_cast<LimeReport::SeriesItem::SeriesItemPreferredType>(enumerator.keysToValue(arg1.toLatin1()))); currentSeries()->setPreferredType(static_cast<LimeReport::SeriesItem::SeriesItemPreferredType>(enumerator.keysToValue(arg1.toLatin1())));
} }
} }
void ChartItemEditor::on_xAxisFieldComboBox_currentTextChanged(const QString &arg1)
{
if (!m_initing)
m_charItem->setXAxisField(arg1);
}

View File

@ -41,6 +41,8 @@ private slots:
void slotChangeSeriesColor(); void slotChangeSeriesColor();
void on_seriesTypeComboBox_currentIndexChanged(const QString &arg1); void on_seriesTypeComboBox_currentIndexChanged(const QString &arg1);
void on_xAxisFieldComboBox_currentTextChanged(const QString &arg1);
private: private:
void readSetting(); void readSetting();
void writeSetting(); void writeSetting();

View File

@ -144,16 +144,30 @@
</widget> </widget>
</item> </item>
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_2"> <layout class="QGridLayout" name="gridLayout">
<item> <item row="0" column="1">
<widget class="QComboBox" name="labelsFieldComboBox">
<property name="editable">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="labelsFieldLabel"> <widget class="QLabel" name="labelsFieldLabel">
<property name="text"> <property name="text">
<string>Labels field</string> <string>Labels field</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item row="1" column="0">
<widget class="QComboBox" name="labelsFieldComboBox"> <widget class="QLabel" name="xAxisFieldLabel">
<property name="text">
<string>X data field</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="xAxisFieldComboBox">
<property name="editable"> <property name="editable">
<bool>true</bool> <bool>true</bool>
</property> </property>

View File

@ -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 \

View File

@ -20,9 +20,9 @@ public:
qreal step() const; qreal step() const;
qreal delta() const; qreal delta() const;
private: private:
void calculateValuesAboveMax(qreal minValue, qreal maxValue, int segments); void calculateValuesAboveMax(qreal minValue, qreal maxValue, int segments);
qreal calculateNiceNum(qreal range, bool round);
qreal m_rangeMin; qreal m_rangeMin;
qreal m_rangeMax; qreal m_rangeMax;

View File

@ -144,6 +144,7 @@ void QObjectPropertyModel::translatePropertyName()
tr("chartType"); tr("chartType");
tr("drawLegendBorder"); tr("drawLegendBorder");
tr("labelsField"); tr("labelsField");
tr("xAxisField");
tr("legendAlign"); tr("legendAlign");
tr("series"); tr("series");
tr("titleAlign"); tr("titleAlign");
@ -170,6 +171,8 @@ void QObjectPropertyModel::translatePropertyName()
tr("removeGap"); tr("removeGap");
tr("dropPrinterMargins"); tr("dropPrinterMargins");
tr("notPrintIfEmpty"); tr("notPrintIfEmpty");
tr("gridChartLines");
tr("horizontalAxisOnTop");
tr("mixWithPriorPage"); tr("mixWithPriorPage");
} }