Qt-QChart实现折线图
一、介绍场景
动态查看数据变化,或者了解数据发展趋势,让数据可以形象直观展现出来,这里推荐使用折线图的方式展现,本文抛砖引玉,简单实现一个实例,效果图如下:
二、实现步骤
1、charts组件
(1)、这里用来绘制图表,引入QT中的charts组件;首先要确保安装了组件QT Charts;在pro文件中添加如下代码
QT += charts
(2)、这里主要用的类为QChart为图表绘制画布; QChartView为图表绘制相框,其位于QChart之上;QDateTimeAxis为时间坐标轴;QValueAxis为数值坐标轴;QLineSeries为折线数据线;
2、核心代码
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QString>
#include <QLabel>
//#include <QtCharts/QChartGlobal>
#include <QtCharts/QChart>
#include <QtCharts/QChartView> // 图标相框
#include <QtCharts/QLineSeries> // 绘制线
#include <QtCharts/QValueAxis> // 坐标轴
#include <QtCharts/QDateTimeAxis> // 时间坐标轴
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class colorForm;
// 使用 QChart,必须包含这个名字空间,不然,就要加名字空间
// 例如:QtCharts::QChart
QT_CHARTS_USE_NAMESPACE
typedef struct tagTipsColor
{
QString strColor{""};
QString strTipsTxt{""};
tagTipsColor(){}
tagTipsColor(QString color, QString strTxt){
strColor = color;
strTipsTxt = strTxt;
}
}TAGTIPSCOLOR;
// 绘制折线数据
typedef struct LinePtProgress
{
__int64 x; // 使用word类型,记录时间,
int y; // 具体数值
QString yColor{""};
LinePtProgress(){}
LinePtProgress(__int64 inX, int inY, QString inYColor="#B3F4AB"){
x = inX;
y = inY;
yColor = inYColor;
}
}LPTPROGRESS;
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
public:
void initData();
void initUi();
protected:
void resizeEvent(QResizeEvent* event) override;
void showEvent(QShowEvent *event) override;
private:
void initChart();
// 创建仿真数据单元
void createTempDataUnit(QVector<QVector<LPTPROGRESS>>& vecArrData, QStringList& vecColor,
const int unitSize, const int size);
// 造仿真数据
void createTempData(QVector<LPTPROGRESS>& arrData, QString strColor, const int size, const int key);
// 设置颜色图例
void setColorLayout(QVector<colorForm*> vecForm);
// 绘制数据线
void drawDataLine(QVector<QVector<LPTPROGRESS>>& vecPtInfo);
// 设置样式
void loadStyleSheet();
private:
Ui::Widget *ui;
QVector<TAGTIPSCOLOR> m_arrColor;
QVector<QVector<LPTPROGRESS>> m_vecArrData;
QChart* m_chart{nullptr};
QChartView* m_chartView{nullptr};
QDateTimeAxis * m_axisX{nullptr}; // X轴
QValueAxis* m_axisY{nullptr}; // Y轴
QLabel* m_targetLabel{nullptr};
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QDateTime>
#include <QRandomGenerator>
#include <colorform.h>
#include <QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
initChart();
initData();
}
Widget::~Widget()
{
delete ui;
}
void Widget::initData()
{
m_arrColor << TAGTIPSCOLOR() << TAGTIPSCOLOR() <<TAGTIPSCOLOR();
m_arrColor[0].strColor = "#BA4CB9";
m_arrColor[0].strTipsTxt = "语文";
m_arrColor[1].strColor = "#268BFF";
m_arrColor[1].strTipsTxt = "数学";
m_arrColor[2].strColor = "#FF6B26";
m_arrColor[2].strTipsTxt = "外语";
QStringList colorStrLst;
colorStrLst.push_back("#BA4CB9");
colorStrLst.push_back("#268BFF");
colorStrLst.push_back("#FF6B26");
const int SIZE = 90;
createTempDataUnit(m_vecArrData, colorStrLst, colorStrLst.size(), SIZE);
initUi();
}
void Widget::initUi()
{
ui->label_Title->setText("语数外成绩走势图");
QVector<colorForm *> vecColorForm;
for (int i = 0; i < m_arrColor.size(); i++)
{
colorForm* pForm = new colorForm(m_arrColor[i].strColor, m_arrColor[i].strTipsTxt, this);
vecColorForm.push_back(pForm);
}
setColorLayout(vecColorForm);
drawDataLine(m_vecArrData);
}
void Widget::resizeEvent(QResizeEvent *event)
{
Q_UNUSED(event)
loadStyleSheet();
int viewX = m_chartView->x();
int viewY = m_chartView->y();
int viewW = m_chartView->width();
if(m_targetLabel)
{
m_targetLabel->move(viewX + viewW/3, viewY+20);
}
}
void Widget::showEvent(QShowEvent *event)
{
Q_UNUSED(event)
int viewX = m_chartView->x();
int viewY = m_chartView->y();
int viewW = m_chartView->width();
if(m_targetLabel)
{
m_targetLabel->move(viewX + viewW/3, viewY+20);
}
}
void Widget::initChart()
{
m_chart = new QChart();
m_chart->setBackgroundBrush(QBrush(Qt::transparent));
// 获取X轴
m_axisX = new QDateTimeAxis(this);
// 设置日期显示格式为年 - 月 - 日
m_axisX->setFormat("yyyy.MM.dd");
m_axisX->setGridLineVisible(false);// 去掉网格线
// 设置 X 轴文本颜色为红色,字体大小为 12
QFont xAxisFont = m_axisX->labelsFont();
xAxisFont.setPixelSize(12);
m_axisX->setLabelsColor(QColor(158, 158, 158));
m_axisX->setLabelsFont(xAxisFont);
m_axisY = new QValueAxis();
m_axisY->setGridLineVisible(false);// 去掉网格线
// 设置 Y 轴文本颜色,字体大小为 12
QFont yAxisFont = m_axisY->labelsFont();
yAxisFont.setPixelSize(12);
m_axisY->setLabelsColor(QColor(158, 158, 158));
m_axisY->setLabelsFont(yAxisFont);
// 将轴添加到图表中
m_chart->addAxis(m_axisX, Qt::AlignBottom);
m_chart->addAxis(m_axisY, Qt::AlignLeft);
// 设置图例的可见性
m_chart->legend()->setVisible(false);
// 设置图例的对齐方式
m_chart->legend()->setAlignment(Qt::AlignBottom);
// 创建一个QChartView对象,用于显示QChart
m_chartView = new QChartView(m_chart, this);
m_chartView->setStyleSheet("background: transparent;");
// 设置抗锯齿,使图表显示更平滑
m_chartView->setRenderHint(QPainter::Antialiasing);
ui->mainVLayout->addWidget(m_chartView, 10);
m_targetLabel = new QLabel(this);
}
void Widget::createTempDataUnit(QVector<QVector<LPTPROGRESS> > &vecArrData, QStringList& vecColor, const int unitSize, const int size)
{
for(int i = 0; i < unitSize; i++)
{
QVector<LPTPROGRESS> arrData;
QString str = vecColor[i];
createTempData(arrData, str, size, i);
vecArrData.push_back(arrData);
}
}
void Widget::createTempData(QVector<LPTPROGRESS> &arrData, QString strColor, const int size, const int key)
{
QVector<int> data;
// 先随机生成 90 个数并计算总和
for (int i = 0; i < size; ++i) {
qint64 b = QDateTime::currentMSecsSinceEpoch();
// 使用时间戳作为种子初始化随机数生成器
QRandomGenerator generator(b);
// 生成一个0到300之间的随机整数
int addNum = generator.bounded(100);
if(addNum < 60)
{
int t = addNum / 10;
addNum += (6 - t)*10;
}
data.push_back(addNum); // 生成 60 到 99 之间的随机数
}
QDateTime currentTime(QDate(2024, 10, 1), QTime(12, 0, 0)); // 每隔一分钟一个数据点
for (int i = 0; i < size; i++)
{
qint64 msecsSinceEpoch = currentTime.toMSecsSinceEpoch();
LPTPROGRESS itemData(msecsSinceEpoch, data[i], strColor);
currentTime = currentTime.addDays(1);
arrData.push_back(itemData);
}
}
void Widget::setColorLayout(QVector<colorForm *> vecForm)
{
int idx = 0;
int r = 0;
int maxCol = 0;
for (const auto& pForm:vecForm)
{
if(!pForm)
{
continue;
}
r = idx / 3;
int col = idx % 3;
if(col+3 > maxCol)
{
maxCol = col+3;
}
ui->subGLayout->addWidget(pForm, r, col+2);
++idx;
}
}
void Widget::drawDataLine(QVector<QVector<LPTPROGRESS> > &vecVecPtInfo)
{
int ptLineUnit = vecVecPtInfo.size();
if(0 == ptLineUnit)
{
qDebug() << "drawDataLine data empty";
return;
}
QLineSeries *targetseries = new QLineSeries(this);
QPen pen = targetseries->pen();
pen.setStyle(Qt::DashLine); // 设置为虚线样式
pen.setColor(Qt::green); // 设置线条颜色
pen.setWidth(2); // 设置线条宽度
targetseries->setPen(pen);
QFont font;
font.setPixelSize(12);
m_targetLabel->setFont(font);
QPalette palette = m_targetLabel->palette();
palette.setColor(QPalette::WindowText, Qt::green); // 设置文本颜色为红色
m_targetLabel->setPalette(palette);
m_targetLabel->setText("满分:100");
bool targetDraw = false;
for(const auto& vecPtInfo:vecVecPtInfo)
{
if(0 == vecPtInfo.size())
{
qDebug() << "draw unit DataLine data empty";
continue;
}
QLineSeries *addseries = new QLineSeries(this);
QPen pen1(QColor(vecPtInfo[0].yColor));
pen1.setWidth(2); // 设置折线宽度为 2 像素
addseries->setPen(pen1);
__int64 minX = vecPtInfo[0].x;
__int64 maxX = vecPtInfo[0].x;
bool hasDrawTarget = false;
for(const auto& ptInfo:vecPtInfo)
{
int pY = ptInfo.y;
addseries->append(ptInfo.x, pY);
if(targetseries && !targetDraw)
{
targetseries->append(ptInfo.x, 100);
hasDrawTarget = true;
}
if(minX > ptInfo.x)
{
minX = ptInfo.x;
}
if(maxX < ptInfo.x)
{
maxX = ptInfo.x;
}
}
if(!targetDraw)
{
targetDraw = hasDrawTarget;
}
QDateTime startDate = QDateTime::fromMSecsSinceEpoch(minX);
QDateTime endDate = QDateTime::fromMSecsSinceEpoch(maxX);
m_axisX->setRange(startDate, endDate);
m_axisY->setRange(0, 100);
// 设置y轴标签格式为整数
m_axisY->setLabelFormat("%d");
// 将Y轴分为四等份
m_axisY->setTickCount(5);
// 将X轴分为3等份
m_axisX->setTickCount(4);
m_chart->addSeries(addseries);
addseries->attachAxis(m_axisX);
addseries->attachAxis(m_axisY);
}
if(targetseries)
{
m_chart->addSeries(targetseries);
targetseries->attachAxis(m_axisX);
targetseries->attachAxis(m_axisY);
}
}
void Widget::loadStyleSheet()
{
QFile file(":/style.qss");
if (file.open(QFile::ReadOnly)) {
QTextStream stream(&file);
QString styleSheet = stream.readAll();
setStyleSheet(styleSheet);
file.close();
}
}
三、代码详情
CuiQingCheng/QtStudy - Gitee.comhttps://gitee.com/cuiqingcheng/QtStudy/tree/master/widget/demo/chartDemo