本文介绍如何用QT6.4如何建立图表效果图,并给出源代码 及 演示exe。
在网上找了几个demo来试了一下,要么不能编译,要么有各种bug,要么制作很粗糙。于是我自己重新开发代码,并尽量简洁易懂好用。下面代码在Windows10 X64下编译运行。
欢迎学习交流开发。
效果图如下,演示exe在 QT6.4图表demo,可以快速学习使用-桌面系统文档类资源-CSDN文库
#ifndef CHARTWIDGET_H
#define CHARTWIDGET_H
#include <QWidget>
#include <QTimer>
#include <QChartView>
#include <QtCharts>
#include <QPieSeries>
#include <QPieSlice>
#include <QValueAxis>
#include <QLineSeries>
#include <QtCharts/QChartView>
QT_BEGIN_NAMESPACE
namespace Ui { class ChartWidget; }
QT_END_NAMESPACE
class DealThread;
class ChartWidget : public QWidget
{
Q_OBJECT
public:
ChartWidget(QWidget *parent = nullptr);
~ChartWidget();
void createpieSewies();
void createBarChart();
void createAreaChart();
void createLineChart();
void createScatterChart();
private:
// 用于模拟生成实时数据的定时器
QTimer* mTimer;
// 图表对象
QChart* mChart;
// 横纵坐标轴对象
QValueAxis *mAxisX, *mAxisY;
// 曲线图对象
QLineSeries* mLineSeries;
// 横纵坐标最大显示范围
const int AXIS_MAX_X = 40, AXIS_MAX_Y = 12;
// 用来记录数据点数
int mPointCount = 0;
private:
QPieSeries* mPieSeries;
QChart* mPieChart;
QChartView* mPieChartView;
QPieSeries* mPieSeries2;
QChartView* mPieChartView2;
private:
QBarSet* mBarSet[5];
QBarSeries* mSeries;
QChart* mBarChart;
QBarCategoryAxis* mAxis;
QChartView* mBarChartView;
QChartView* mAreaChartView;
QAreaSeries* mAreaSeries;
QLineSeries* mUpperSeries, *mLowerSeries;
QChartView* mLineChartView;
QChartView* mScatterChartView;
QVector<QLineSeries*> mLineSeriesVec;
QVector<QScatterSeries*> mAcatterSeriesVec;
private:
private slots:
void slotBtnClear();
void slotBtnStartAndStop();
void slotTimeout();
void keyPressEvent(QKeyEvent *event);
void closeEvent(QCloseEvent *event);
private:
Ui::ChartWidget *ui;
};
#endif
#include "ChartWidget.h"
#include "DealThread.h"
#include "ui_ChartWidget.h"
ChartWidget::ChartWidget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::ChartWidget)
{
ui->setupUi(this);
// 创建定时器
mTimer = new QTimer(this);
mTimer->setSingleShot(false);
QObject::connect(mTimer, SIGNAL(timeout()), this, SLOT(slotTimeout()));
QObject::connect(ui->btnClear, SIGNAL(clicked(bool)), this, SLOT(slotBtnClear()));
QObject::connect(ui->btnStartAndStop, SIGNAL(clicked(bool)), this, SLOT(slotBtnStartAndStop()));
// 创建横纵坐标轴并设置显示范围
mAxisX = new QValueAxis();
mAxisY = new QValueAxis();
mAxisX->setTitleText("X-时间");
mAxisY->setTitleText("Y-数值");
mAxisX->setMin(0);
mAxisX->setMax(AXIS_MAX_X);
mAxisY->setMin(0);
mAxisY->setMax(AXIS_MAX_Y);
mLineSeries = new QLineSeries(); // 创建曲线绘制画布
mLineSeries->setPointsVisible(true); // 设置数据点可见
mLineSeries->setName("动态数字连续曲线"); // 设置图例名称
mChart = new QChart(); // 创建图表对象
mChart->addAxis(mAxisX, Qt::AlignBottom); // 将X轴添加到图表上
mChart->addAxis(mAxisY, Qt::AlignLeft); // 将Y轴添加到图表上
mChart->addSeries(mLineSeries); // 将曲线画布添加到图表上
mChart->setAnimationOptions(QChart::SeriesAnimations); // 启用动画模式,能使曲线绘制显示过渡的更平滑
mChart->setAnimationDuration(400); // 动画持续时间间隔,要小于新增节点的时间 (否则导致现有节点变动位置为新位置)
mChart->setBackgroundPen(QPen(QColor(33,88,155)));
mChart->setBackgroundBrush(QBrush(QColor(0xF7F8CC)));
mLineSeries->attachAxis(mAxisX); // 曲线对象关联上X轴,此步骤必须在mChart->addSeries之后
mLineSeries->attachAxis(mAxisY); // 曲线对象关联上Y轴,此步骤必须在mChart->addSeries之后
ui->widgetView->setChart(mChart); // 将图表对象设置到widgetView上进行显示
ui->widgetView->setRenderHint(QPainter::Antialiasing); // 启用抗锯齿效果
createpieSewies();
createBarChart();
createAreaChart();
createLineChart();
createScatterChart();
slotBtnStartAndStop();// 初始就点击开始计时器
ChartWidget::~ChartWidget()
{
mPieChart->removeAllSeries();
ui->hLayout2->removeWidget(mPieChartView);
mPieSeries->deleteLater();
mPieChart->deleteLater();
mPieChartView->deleteLater();
mPieSeries2->deleteLater();
mPieChartView2->deleteLater();
for(int i=0;i<5;i++)
mSeries->remove(mBarSet[i]);
mBarChart->removeAllSeries();
ui->hLayout2->removeWidget(mBarChartView);
for(int i=0;i<5;i++)
mBarSet[i]->deleteLater();
mSeries->deleteLater();
mBarChart->deleteLater();
mAxis->deleteLater();
mBarChartView->deleteLater();
mAreaChartView->deleteLater();
mAreaSeries->deleteLater();
mUpperSeries->deleteLater();
mLowerSeries->deleteLater();
mLineChartView->deleteLater();
mScatterChartView->deleteLater();
foreach (QLineSeries* m , mLineSeriesVec)
m->deleteLater();
foreach (QScatterSeries* m , mAcatterSeriesVec)
m->deleteLater();
delete ui;
delete mTimer;
delete mAxisX;
delete mAxisY;
delete mLineSeries;
delete mChart;
delete mThread;
}
// 创建饼状图
void ChartWidget::createpieSewies()
{
// 饼状图
mPieSeries = new QPieSeries();
// 中间圆与大圆的比例,可以没有中间圆
mPieSeries->setHoleSize(0.3);
// 扇形及数据 0
QPieSlice* pieSlice0 = mPieSeries->append("学者",25);//扇形占整个圆的百分比
pieSlice0->setLabelVisible(); // 设置标签可见,缺省不可见
pieSlice0->setColor(QColor(255,0,0,255)); // 启用主题前设置无效
pieSlice0->setLabelColor(QColor(0x4cb9cf));
pieSlice0->setBorderColor(QColor(0x4cb9cf));
// 扇形及数据 1
QPieSlice* pieSlice1 = mPieSeries->append("民工",30);
pieSlice1->setLabelVisible(); // 设置标签可见,缺省不可见
pieSlice1->setColor(QColor(0x53b666)); // 启用主题前设置无效
pieSlice1->setLabelColor(QColor(0x53b666));
pieSlice1->setBorderColor(QColor(0x53b666));
// 扇形及数据 2
QPieSlice* pieSlice2 = mPieSeries->append("白领",45);
pieSlice2->setLabelVisible(); // 设置标签可见,缺省不可见
pieSlice2->setColor(QColor(0x2f89cf)); // 启用主题前设置无效
pieSlice2->setLabelColor(QColor(0x2f89cf));
pieSlice2->setBorderColor(QColor(0x2f89cf));
// 图表视图
mPieChart = new QChart();
mPieChart->setTitle("人员分布");
mPieChart->addSeries(mPieSeries);
mPieChart->setAnimationOptions(QChart::SeriesAnimations);
mPieChart->legend()->setAlignment(Qt::AlignBottom);
mPieChart->legend()->setBackgroundVisible(false);
// mPieChart->legend()->setFont(QFont("黑体", 8)) ; // 图例字体
// mPieChart->setTitleBrush(QColor(0x808396));
// mPieChart->legend()->setLabelColor(QColor(0x808396));
mPieChart->setTheme(QChart::ChartThemeBlueCerulean);
pieSlice0->setColor(QColor(255,0,0,255)); // 启用主题前设置无效
pieSlice1->setColor(QColor(97,81,233,255)); // 启用主题前设置无效
pieSlice2->setColor(QColor(0,255,0,255)); // 启用主题前设置无效
mPieChartView = new QChartView(mPieChart);
mPieChartView->setRenderHint(QPainter::Antialiasing);
mPieChartView->setRenderHint(QPainter::NonCosmeticBrushPatterns);
ui->hLayout2->addWidget(mPieChartView);
{
//Creating donutchart
mPieSeries2=new QPieSeries();
mPieSeries2->setHoleSize(0.0);//圆孔大小
QPieSlice *slice0 = mPieSeries2->append("学者 10%",10);
slice0->setColor(QColor(97,81,233,255)); // 启用主题前设置无效
//slice0->setLabelVisible(); // 设置标签可见,缺省不可见
QPieSlice *slice1=mPieSeries2->append("躺平 15%",15); // 单个切片
slice1->setExploded(); // 使该切片突出
slice1->setLabelVisible(); // 显示切片的标签
slice1->setColor(QColor(255,0,0,255)); // 启用主题前设置无效
QPieSlice *slice2 = mPieSeries2->append("白领 30%",30);
slice2->setLabelVisible(); // 设置标签可见,缺省不可见
QPieSlice *slice3 = mPieSeries2->append("民工 35%", 40); // 以圆心为原点,以y轴为起始轴,圆环的每一小部分按照编程顺序顺时针排列。
slice3->setColor(QColor(0,200,0,255)); // 启用主题前设置无效
slice3->setLabelVisible(); // 设置标签可见,缺省不可见
mPieChartView2=new QChartView();
mPieChartView2->setRenderHint(QPainter::Antialiasing); // 启用抗锯齿
mPieChartView2->chart()->setTitle("人员分布");
mPieChartView2->chart()->addSeries(mPieSeries2);
mPieChartView2->chart()->legend()->setAlignment(Qt::AlignBottom); // 图例放在底部
mPieChartView2->chart()->setTheme(QChart::ChartThemeBlueIcy); // 主题颜色设置为天然色
//chartView->chart()->legend()->setFont(QFont("Arial",7)); // 图例字体宋体7号字
slice0->setColor(QColor(97,81,233,255)); // 启用主题前设置无效
slice1->setColor(QColor(255,0,0,255)); // 启用主题前设置无效
slice3->setColor(QColor(0,200,0,255)); // 启用主题前设置无效
ui->hLayout2->addWidget(mPieChartView2);
}
}
// 创建柱状图
void ChartWidget::createBarChart()
{
mBarSet[0] = new QBarSet("产量");
mBarSet[1] = new QBarSet("废料");
mBarSet[2] = new QBarSet("销量");
mBarSet[3] = new QBarSet("利润");
mBarSet[4] = new QBarSet("产能");
*mBarSet[0] << 2 << 12 << 3 << 4 << 7 << 5; // 产量 6个月份的值
*mBarSet[1] << 6 << 3 << 13 << 4 << 4 << 8; // 废料 6个月份的值
*mBarSet[2] << 3 << 4 << 8 << 14 << 3 << 4; // 销量 6个月份的值
*mBarSet[3] << 4 << 11 << 7 << 3 << 3 << 6; // 利润 6个月份的值
*mBarSet[4] << 8 << 8 << 6 << 12 << 4 << 9; // 产能 6个月份的值
mSeries = new QBarSeries();
for(int i=0;i<5;i++)
mSeries->append(mBarSet[i]);
mBarChart = new QChart();
mBarChart->addSeries(mSeries);
mBarChart->setTitle("工厂经营状态图表");
mBarChart->setAnimationOptions(QChart::SeriesAnimations);
mBarChart->setTheme(QChart::ChartThemeBlueCerulean);
QStringList categories;
categories << "一月" << "二月" << "三月" << "四月" << "五月" << "六月";
mAxis = new QBarCategoryAxis();
mAxis->append(categories);
mBarChart->createDefaultAxes(); // 创建默认的左侧的坐标轴(根据 QBarSet 设置的值)
mBarChart->setAxisX(mAxis, mSeries); // 设置坐标轴
mBarChart->legend()->setVisible(true); // 设置图例为显示状态
mBarChart->legend()->setAlignment(Qt::AlignBottom); // 设置图例的显示位置在底部
mBarChartView = new QChartView(mBarChart);
mBarChartView->setRenderHint(QPainter::Antialiasing);
ui->hLayout2->addWidget(mBarChartView);
}
// 创建面积图
void ChartWidget::createAreaChart()
{
// 创建折线图系列对象,一块面积由上下两条折线合围
mUpperSeries = new QLineSeries();
mLowerSeries = new QLineSeries();
*mUpperSeries << QPointF(1, 5) << QPointF(3, 7) << QPointF(7, 6) << QPointF(9, 7)
<< QPointF(12,6) << QPointF(16,7) << QPointF(18,5);
*mLowerSeries << QPointF(1, 3) << QPointF(3, 4) << QPointF(7, 3) << QPointF(8, 2)
<< QPointF(12,3) << QPointF(16,4) << QPointF(18,3);
//mLowerSeries->append(8, 6 + rand() % 6); // 也可以这样添加数据
// 创建面积对象
mAreaSeries = new QAreaSeries(mUpperSeries, mLowerSeries); // 一块面积由上下两条折线合围
mAreaSeries->setName("稻田面积");
QPen pen(0x059605);
pen.setWidth(3);
mAreaSeries->setPen(pen);
// 设置边线和面积内部颜色
QLinearGradient gradient(QPointF(0, 0), QPointF(0, 1));
gradient.setColorAt(0.0, 0x3cc63c);
gradient.setColorAt(1.0, 0x26f626);
gradient.setCoordinateMode(QGradient::ObjectBoundingMode);
mAreaSeries->setBrush(gradient);
mAreaChartView = new QChartView();
mAreaChartView->setRenderHint(QPainter::Antialiasing);
// 创建QChart对象
QChart *chart = mAreaChartView->chart();
chart->addSeries(mAreaSeries);
chart->setTitle("面积图");
chart->createDefaultAxes();
chart->axes(Qt::Horizontal).first()->setRange(0, 20);// 自定义坐标范围
chart->axes(Qt::Vertical).first()->setRange(0, 10);
//chart->createDefaultAxes();// 根据数据 创建默认坐标轴
chart->setTheme(QChart::ChartThemeBlueCerulean);
ui->hLayout3->addWidget(mAreaChartView);
}
// 创建折线图
void ChartWidget::createLineChart()
{
mLineChartView = new QChartView();
mLineChartView->setRenderHint(QPainter::Antialiasing);
// 创建QChart对象
QChart *chart = mLineChartView->chart();
chart->setTitle( "折线图");// 设置标题
chart->setTheme(QChart::ChartThemeBlueCerulean);
// 添加图表值
for(int i = 0; i < 2; i++) // 几条折线
{
// 创建折线图系列对象
QLineSeries* series = new QLineSeries(chart);
mLineSeriesVec.push_back(series);
series->setName(!i?"产量":"质量");
for(int k = 0; k < 6; k++)
series->append(k, k * (i+1) + rand() % 6);
chart->addSeries(series);// 将series添加到QChart对象中
}
chart->createDefaultAxes();// 根据数据 创建默认坐标轴
ui->hLayout3->addWidget(mLineChartView);
}
// 创建散点图
void ChartWidget::createScatterChart()
{
mScatterChartView = new QChartView();
mScatterChartView->setRenderHint(QPainter::Antialiasing);
// 创建QChart对象
QChart *chart = mScatterChartView->chart();
chart->setTitle( "散点图");// 设置标题
chart->setTheme(QChart::ChartThemeBlueCerulean);
// 添加图表值
for(int i = 0; i < 2; i++) // 几条散点线
{
// 创建曲线图系列对象
QScatterSeries* series = new QScatterSeries(chart);
mAcatterSeriesVec.push_back(series);
series->setName(!i?"苹果":"香蕉");
if(i)series->setMarkerShape(QScatterSeries::MarkerShape::MarkerShapeStar);
if(i)series->setColor(Qt::green);
series->setBorderColor(Qt::transparent);
series->setPen(QColor(Qt::transparent));
for(int k = 0; k < 6; k++)
series->append(k , k * i * 4 + rand() % 8);
chart->addSeries(series);// 将series添加到QChart对象中
}
chart->createDefaultAxes();// 根据数据 创建默认坐标轴
ui->hLayout3->addWidget(mScatterChartView);
}
void ChartWidget::slotBtnClear()
{
mLineSeries->clear();
mChart->axisX()->setMin(0);
mChart->axisX()->setMax(AXIS_MAX_X);
mPointCount = 0;
//给子线程发信号和参数
emit sengMsgToThread("hello to you");
}
void ChartWidget::slotBtnStartAndStop()
{
if(mTimer->isActive())
{
mTimer->stop();
ui->btnStartAndStop->setText("启动500ms定时器");
}else
{
mTimer->start(500);
ui->btnStartAndStop->setText("停止500ms定时器");
}
}
void ChartWidget::slotTimeout()
{
//连续生成动态数据 基于上一个节点数值计算
static int ap=0,lastY=AXIS_MAX_Y/2;
int y = lastY+((rand()%2)?1:-1)*(rand()%3+1);
while(y>AXIS_MAX_Y-2 || y<2)
{
y = lastY+((rand()%2)?1:-1)*(rand()%3+1);
}
lastY=y;
// 增加节点 更新显示(随机生成AXIS_MAX_Y以内的一个数)
mLineSeries->append(QPointF(mPointCount, y));
if(mPointCount >= AXIS_MAX_X)
{
mChart->axisX()->setMin(mPointCount - AXIS_MAX_X);
mChart->axisX()->setMax(mPointCount); // 更新X轴范围
if(mPointCount > AXIS_MAX_X) mLineSeries->remove(0);
}
mPointCount++;
}
void ChartWidget::keyPressEvent(QKeyEvent *event)
{
switch (event->key())
{
case Qt::Key_Escape: // 按下的为Esc键
qApp->quit();
break;
default:
QWidget::keyPressEvent(event);
}
}
void ChartWidget::closeEvent(QCloseEvent *event)
{
event->accept();
}
ChartWidget.ui 上的布局代码
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ChartWidget</class>
<widget class="QWidget" name="ChartWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>802</width>
<height>758</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="windowTitle">
<string>Charts</string>
</property>
<widget class="QWidget" name="verticalLayoutWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>801</width>
<height>741</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item>
<layout class="QHBoxLayout" name="hLayout0">
<item>
<widget class="QPushButton" name="btnClear">
<property name="minimumSize">
<size>
<width>0</width>
<height>30</height>
</size>
</property>
<property name="text">
<string>清空曲线</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnStartAndStop">
<property name="minimumSize">
<size>
<width>0</width>
<height>30</height>
</size>
</property>
<property name="text">
<string>启动500ms定时器</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="vLayout1">
<item>
<widget class="QChartView" name="widgetView" native="true"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="hLayout2"/>
</item>
<item>
<layout class="QHBoxLayout" name="hLayout3"/>
</item>
</layout>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>QChartView</class>
<extends>QWidget</extends>
<header location="global">qchartview.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>