Qt-QChart实现折线图

一、介绍场景

动态查看数据变化,或者了解数据发展趋势,让数据可以形象直观展现出来,这里推荐使用折线图的方式展现,本文抛砖引玉,简单实现一个实例,效果图如下:

二、实现步骤

1、charts组件

(1)、这里用来绘制图表,引入QT中的charts组件;首先要确保安装了组件QT Charts;在pro文件中添加如下代码

cpp 复制代码
QT       +=  charts

(2)、这里主要用的类为QChart为图表绘制画布; QChartView为图表绘制相框,其位于QChart之上;QDateTimeAxis为时间坐标轴;QValueAxis为数值坐标轴;QLineSeries为折线数据线;

2、核心代码

widget.h

cpp 复制代码
#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

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

相关推荐
用户805533698032 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner2 天前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz7 天前
QML Hello World 入门示例
qt
xcyxiner10 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner10 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner11 天前
DicomViewer (添加模型类)3
qt
xcyxiner12 天前
DicomViewer (目录调整) 2
qt
xcyxiner12 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
桥田智能14 天前
桥田智能 QT-650S:面向白车身焊装的 800kg 重载快换解决方案
开发语言·qt·系统架构
森G14 天前
75、服务器源码解析---------云视频服务项目
linux·服务器·网络·c++·qt