All depends on what you want of course.
But here's an example.
Note: this example demonstrates how to draw a series of bars in a chart like style using a custom widget.
This, however, will not scale and is very very inefficient.
To create a better charting system, make full use of the model view design.
Datapoints should not contain any more information than their actual values. In the example below, it also contains view parameters.
Take for example the width of a bar. I added this parameter to the datapoint. I'm sure you can imagine another kind of chart that doesn't use the width to draw a datapoint.
You might think, why not add it to the widget itself? That too is not a good idea because then it would be a lot of work to add new kinds of charts.
Better is to make use of some kind of delegate.
Your datapoints contain the usefull point information.
The view handles the drawing.
How things are drawn is the task of this delegate. You can have a barchart delegate, a piechart delegate etc...
But, to keep things simple, here's an inefficient way to draw barcharts.
screenshot.PNG
datapoint.h
#ifndef DATAPOINT_H
#define DATAPOINT_H
#include <QtGlobal>
#include <QColor>
#include <QList>
#include <QMetaType>
class DataPoint
{
public:
DataPoint();
DataPoint(qreal x, qreal Y);
void setX(qreal x);
void setY(qreal y);
void setColor
(const QColor &color
);
void setWidth(int width);
qreal x() const;
qreal y() const;
int width() const;
private:
qreal m_x;
qreal m_y;
int m_width;
};
typedef QList<DataPoint*> DataPointList;
#endif // DATAPOINT_H
#ifndef DATAPOINT_H
#define DATAPOINT_H
#include <QtGlobal>
#include <QColor>
#include <QList>
#include <QMetaType>
class DataPoint
{
public:
DataPoint();
DataPoint(qreal x, qreal Y);
void setX(qreal x);
void setY(qreal y);
void setColor(const QColor &color);
void setWidth(int width);
qreal x() const;
qreal y() const;
QColor color() const;
int width() const;
private:
qreal m_x;
qreal m_y;
QColor m_color;
int m_width;
};
typedef QList<DataPoint*> DataPointList;
#endif // DATAPOINT_H
To copy to clipboard, switch view to plain text mode
datapoint.cpp
#include "datapoint.h"
DataPoint::DataPoint() :
m_x(0),
m_y(0),
m_color
(QColor(qRgb
(200,
200,
200))),
m_width(10)
{
}
DataPoint::DataPoint(qreal x, qreal y) :
m_x(x),
m_y(y),
m_color
(QColor(qRgb
(200,
200,
200))),
m_width(10)
{
}
void DataPoint::setX(qreal x)
{
m_x = x;
}
void DataPoint::setY(qreal y)
{
m_y = y;
}
void DataPoint
::setColor(const QColor &color
) {
m_color = color;
}
void DataPoint::setWidth(int width)
{
m_width = width;
}
qreal DataPoint::x() const
{
return m_x;
}
qreal DataPoint::y() const
{
return m_y;
}
QColor DataPoint
::color() const {
return m_color;
}
int DataPoint::width() const
{
return m_width;
}
#include "datapoint.h"
DataPoint::DataPoint() :
m_x(0),
m_y(0),
m_color(QColor(qRgb(200, 200, 200))),
m_width(10)
{
}
DataPoint::DataPoint(qreal x, qreal y) :
m_x(x),
m_y(y),
m_color(QColor(qRgb(200, 200, 200))),
m_width(10)
{
}
void DataPoint::setX(qreal x)
{
m_x = x;
}
void DataPoint::setY(qreal y)
{
m_y = y;
}
void DataPoint::setColor(const QColor &color)
{
m_color = color;
}
void DataPoint::setWidth(int width)
{
m_width = width;
}
qreal DataPoint::x() const
{
return m_x;
}
qreal DataPoint::y() const
{
return m_y;
}
QColor DataPoint::color() const
{
return m_color;
}
int DataPoint::width() const
{
return m_width;
}
To copy to clipboard, switch view to plain text mode
barchart.h
#ifndef BARCHART_H
#define BARCHART_H
#include <QWidget>
#include <QPaintEvent>
#include <QtGlobal>
#include "datapoint.h"
{
Q_OBJECT
public:
explicit BarChart
(QWidget *parent
= 0);
void setDataPointList(const DataPointList &pointList);
protected:
signals:
public slots:
private:
DataPointList m_dataPointList;
qreal maximumHeight();
qreal maximumWidth();
qreal yAxisWidth();
qreal xAxisHeight();
qreal spaceBetweenBars;
qreal chartOuterMargin;
qreal dataPointHeight(qreal y);
void drawEmptyBarChart
(QPainter *painter
);
};
#endif // BARCHART_H
#ifndef BARCHART_H
#define BARCHART_H
#include <QWidget>
#include <QPaintEvent>
#include <QtGlobal>
#include "datapoint.h"
class BarChart : public QWidget
{
Q_OBJECT
public:
explicit BarChart(QWidget *parent = 0);
void setDataPointList(const DataPointList &pointList);
protected:
void paintEvent(QPaintEvent *event);
signals:
public slots:
private:
DataPointList m_dataPointList;
qreal maximumHeight();
qreal maximumWidth();
qreal yAxisWidth();
qreal xAxisHeight();
qreal spaceBetweenBars;
qreal chartOuterMargin;
qreal dataPointHeight(qreal y);
void drawEmptyBarChart(QPainter *painter);
void drawYAxis(QPainter *painter);
void drawXAxis(QPainter *painter);
void drawDataPoints(QPainter *painter);
};
#endif // BARCHART_H
To copy to clipboard, switch view to plain text mode
barchart.cpp
#include "barchart.h"
#include <QPainter>
#include <QFontMetricsF>
#include <QPointF>
#include <QPen>
#include <QBrush>
BarChart
::BarChart(QWidget *parent
) :{
spaceBetweenBars = 5;
chartOuterMargin = 10;
}
void BarChart::setDataPointList(const DataPointList &pointList)
{
m_dataPointList = pointList;
update();
}
{
if (m_dataPointList.isEmpty())
drawEmptyBarChart(&painter);
else {
drawXAxis(&painter);
drawYAxis(&painter);
drawDataPoints(&painter);
}
}
qreal BarChart::maximumHeight()
{
qreal height = 0;
foreach(DataPoint *point, m_dataPointList) {
if (point->y() > height)
height = point->y();
}
return height;
}
qreal BarChart::maximumWidth()
{
qreal width = 0;
foreach(DataPoint *point, m_dataPointList) {
width += point->width() + spaceBetweenBars;
}
return width;
}
qreal BarChart::yAxisWidth()
{
qreal textWidth = fm.width(maximumValue);
return textWidth + 5;
}
qreal BarChart::xAxisHeight()
{
qreal textHeight = fm.height();
return textHeight + 5;
}
qreal BarChart::dataPointHeight(qreal y)
{
qreal textHeight = fm.height();
return y * ((height() - (2 * (chartOuterMargin + (textHeight / 2))) - xAxisHeight()) / maximumHeight());
}
void BarChart
::drawEmptyBarChart(QPainter *painter
) {
QString text
("No datapoints available!");
qreal centreX = width() / 2;
qreal centreY = height() / 2;
qreal textWidth = fontMetrics.width(text);
qreal textHeight = fontMetrics.height();
qreal textX = centreX - (textWidth / 2);
qreal textY = centreY - (textHeight / 2);
painter
->drawText
(QPointF(textX, textY
), text
);
}
void BarChart
::drawYAxis(QPainter *painter
) {
qreal textWidth = fm.width(maximumValue);
qreal textHeight = fm.height();
qreal yAxisX = chartOuterMargin + textWidth + 5;
qreal yAxisY = chartOuterMargin + (textHeight / 2);
qreal yAxisHeight = height() - yAxisY - xAxisHeight();
painter->save();
painter->setPen(yAxisPen);
painter
->drawLine
(QPointF(yAxisX, yAxisY
),
QPointF(yAxisX, yAxisHeight
));
painter
->drawLine
(QPointF(yAxisX
- 3, yAxisY
),
QPointF(yAxisX, yAxisY
));
painter
->drawLine
(QPointF(yAxisX
- 3, yAxisHeight
),
QPointF(yAxisX, yAxisHeight
));
painter
->drawText
(QPointF(chartOuterMargin, chartOuterMargin
+ textHeight
), maximumValue
);
painter
->drawText
(QPointF(chartOuterMargin, height
() - chartOuterMargin
- xAxisHeight
()),
"0");
painter->restore();
}
void BarChart
::drawXAxis(QPainter *painter
) {
qreal textHeight = fm.height();
qreal xAxisX = chartOuterMargin + yAxisWidth();
qreal xAxisY = height() - xAxisHeight() - chartOuterMargin - (textHeight/2);
qreal xAxisWidth = xAxisX + maximumWidth() + spaceBetweenBars;
painter->save();
painter->setPen(xAxisPen);
painter
->drawLine
(QPointF(xAxisX, xAxisY
),
QPointF(xAxisWidth, xAxisY
));
qreal xAxisMark = xAxisX + spaceBetweenBars; // Keep some space between the first bar and the Y axis
foreach(DataPoint *point, m_dataPointList) {
xAxisMark += point->width()/2;
painter
->drawLine
(QPointF(xAxisMark, xAxisY
),
QPointF(xAxisMark, xAxisY
+ 3));
qreal markTextWidth
= fm.
width(QString::number(point
->x
()));
qreal markTextHeight = fm.height();
painter
->drawText
(QPointF(xAxisMark
- (markTextWidth
/2), xAxisY
+ markTextHeight
+ 5),
QString::number(point
->x
()));
xAxisMark += (point->width() / 2) + spaceBetweenBars;
}
painter->restore();
}
void BarChart
::drawDataPoints(QPainter *painter
) {
qreal dataPointCentre = chartOuterMargin + yAxisWidth() + spaceBetweenBars;
qreal textHeight = fm.height();
foreach(DataPoint *point, m_dataPointList) {
QBrush dataPointBrush
(point
->color
());
dataPointCentre += point->width()/2;
painter->save();
painter->setBrush(dataPointBrush);
painter->setPen(dataPointPen);
qreal dpHeight = dataPointHeight(point->y());
painter
->drawRect
(QRectF(dataPointCentre
- (point
->width
()/2), height
() - xAxisHeight
() - chartOuterMargin
- (textHeight
/2), point
->width
(),
-dpHeight
));
painter->restore();
dataPointCentre += (point->width() / 2) + spaceBetweenBars;
}
}
#include "barchart.h"
#include <QPainter>
#include <QFontMetricsF>
#include <QPointF>
#include <QPen>
#include <QBrush>
BarChart::BarChart(QWidget *parent) :
QWidget(parent)
{
spaceBetweenBars = 5;
chartOuterMargin = 10;
}
void BarChart::setDataPointList(const DataPointList &pointList)
{
m_dataPointList = pointList;
update();
}
void BarChart::paintEvent(QPaintEvent *event)
{
QWidget::paintEvent(event);
QPainter painter(this);
if (m_dataPointList.isEmpty())
drawEmptyBarChart(&painter);
else {
drawXAxis(&painter);
drawYAxis(&painter);
drawDataPoints(&painter);
}
}
qreal BarChart::maximumHeight()
{
qreal height = 0;
foreach(DataPoint *point, m_dataPointList) {
if (point->y() > height)
height = point->y();
}
return height;
}
qreal BarChart::maximumWidth()
{
qreal width = 0;
foreach(DataPoint *point, m_dataPointList) {
width += point->width() + spaceBetweenBars;
}
return width;
}
qreal BarChart::yAxisWidth()
{
QString maximumValue = QString::number(maximumHeight());
QFontMetrics fm(font());
qreal textWidth = fm.width(maximumValue);
return textWidth + 5;
}
qreal BarChart::xAxisHeight()
{
QFontMetrics fm(font());
qreal textHeight = fm.height();
return textHeight + 5;
}
qreal BarChart::dataPointHeight(qreal y)
{
QFontMetrics fm(font());
qreal textHeight = fm.height();
return y * ((height() - (2 * (chartOuterMargin + (textHeight / 2))) - xAxisHeight()) / maximumHeight());
}
void BarChart::drawEmptyBarChart(QPainter *painter)
{
QString text("No datapoints available!");
qreal centreX = width() / 2;
qreal centreY = height() / 2;
QFontMetricsF fontMetrics(font());
qreal textWidth = fontMetrics.width(text);
qreal textHeight = fontMetrics.height();
qreal textX = centreX - (textWidth / 2);
qreal textY = centreY - (textHeight / 2);
painter->drawText(QPointF(textX, textY), text);
}
void BarChart::drawYAxis(QPainter *painter)
{
QPen yAxisPen(QColor(qRgb(0, 0, 0)));
QString maximumValue = QString::number(maximumHeight());
QFontMetrics fm(font());
qreal textWidth = fm.width(maximumValue);
qreal textHeight = fm.height();
qreal yAxisX = chartOuterMargin + textWidth + 5;
qreal yAxisY = chartOuterMargin + (textHeight / 2);
qreal yAxisHeight = height() - yAxisY - xAxisHeight();
painter->save();
painter->setPen(yAxisPen);
painter->drawLine(QPointF(yAxisX, yAxisY), QPointF(yAxisX, yAxisHeight));
painter->drawLine(QPointF(yAxisX - 3, yAxisY), QPointF(yAxisX, yAxisY));
painter->drawLine(QPointF(yAxisX - 3, yAxisHeight), QPointF(yAxisX, yAxisHeight));
painter->drawText(QPointF(chartOuterMargin, chartOuterMargin + textHeight), maximumValue);
painter->drawText(QPointF(chartOuterMargin, height() - chartOuterMargin - xAxisHeight()), "0");
painter->restore();
}
void BarChart::drawXAxis(QPainter *painter)
{
QPen xAxisPen(QColor(qRgb(0, 0, 0)));
QFontMetrics fm(font());
qreal textHeight = fm.height();
qreal xAxisX = chartOuterMargin + yAxisWidth();
qreal xAxisY = height() - xAxisHeight() - chartOuterMargin - (textHeight/2);
qreal xAxisWidth = xAxisX + maximumWidth() + spaceBetweenBars;
painter->save();
painter->setPen(xAxisPen);
painter->drawLine(QPointF(xAxisX, xAxisY), QPointF(xAxisWidth, xAxisY));
qreal xAxisMark = xAxisX + spaceBetweenBars; // Keep some space between the first bar and the Y axis
foreach(DataPoint *point, m_dataPointList) {
xAxisMark += point->width()/2;
painter->drawLine(QPointF(xAxisMark, xAxisY), QPointF(xAxisMark, xAxisY + 3));
QFontMetrics fm(font());
qreal markTextWidth = fm.width(QString::number(point->x()));
qreal markTextHeight = fm.height();
painter->drawText(QPointF(xAxisMark - (markTextWidth/2), xAxisY + markTextHeight + 5), QString::number(point->x()));
xAxisMark += (point->width() / 2) + spaceBetweenBars;
}
painter->restore();
}
void BarChart::drawDataPoints(QPainter *painter)
{
qreal dataPointCentre = chartOuterMargin + yAxisWidth() + spaceBetweenBars;
QFontMetrics fm(font());
qreal textHeight = fm.height();
foreach(DataPoint *point, m_dataPointList) {
QBrush dataPointBrush(point->color());
QPen dataPointPen(QColor(qRgb(0, 0, 0)));
dataPointCentre += point->width()/2;
painter->save();
painter->setBrush(dataPointBrush);
painter->setPen(dataPointPen);
qreal dpHeight = dataPointHeight(point->y());
painter->drawRect(QRectF(dataPointCentre - (point->width()/2), height() - xAxisHeight() - chartOuterMargin - (textHeight/2), point->width(), -dpHeight));
painter->restore();
dataPointCentre += (point->width() / 2) + spaceBetweenBars;
}
}
To copy to clipboard, switch view to plain text mode
Usage:
dataPoints.append(new DataPoint(1, 1));
dataPoints.append(new DataPoint(2, 2));
DataPoint *colorPoint = new DataPoint(3, 3);
colorPoint
->setColor
(QColor(qRgb
(255,
0,
0)));
dataPoints.append(colorPoint);
dataPoints.append(new DataPoint(4, 4));
DataPoint *widthPoint = new DataPoint(5, 5);
widthPoint->setWidth(20);
dataPoints.append(widthPoint);
dataPoints.append(new DataPoint(6, 6));
barChart = new BarChart;
//...
barChart->setDataPointList(dataPoints);
dataPoints.append(new DataPoint(1, 1));
dataPoints.append(new DataPoint(2, 2));
DataPoint *colorPoint = new DataPoint(3, 3);
colorPoint->setColor(QColor(qRgb(255, 0, 0)));
dataPoints.append(colorPoint);
dataPoints.append(new DataPoint(4, 4));
DataPoint *widthPoint = new DataPoint(5, 5);
widthPoint->setWidth(20);
dataPoints.append(widthPoint);
dataPoints.append(new DataPoint(6, 6));
barChart = new BarChart;
//...
barChart->setDataPointList(dataPoints);
To copy to clipboard, switch view to plain text mode
Bookmarks