文章的目的为了记录使用C++ 进行QT Widget 开发学习的经历。临时学习,完成app的开发。开发流程和要点有些记忆模糊,赶紧记录,防止忘记。
相关链接:
开源 C++ QT Widget 开发(一)工程文件结构-优快云博客
开源 C++ QT Widget 开发(二)基本控件应用-优快云博客
开源 C++ QT Widget 开发(三)图表--波形显示器-优快云博客
开源 C++ QT Widget 开发(四)文件--二进制文件查看编辑-优快云博客
开源 C++ QT Widget 开发(五)通讯--串口调试-优快云博客
开源 C++ QT Widget 开发(六)通讯--TCP调试-优快云博客
开源 C++ QT Widget 开发(七)线程--多线程及通讯-优快云博客
开源 C++ QT Widget 开发(八)网络--Http文件下载-优快云博客
开源 C++ QT Widget 开发(九)图表--仪表盘-优快云博客
开源 C++ QT Widget 开发(十)IPC进程间通信--共享内存-优快云博客
开源 C++ QT Widget 开发(十一)进程间通信--Windows 窗口通信-优快云博客
开源 C++ QT Widget 开发(十二)图表--环境监测表盘-优快云博客
开源 C++ QT Widget 开发(十三)IPC通讯--本地套接字 (Local Socket)
开源 C++ QT Widget 开发(十四)多媒体--录音机
开源 C++ QT Widget 开发(十五)多媒体--音频播放
推荐链接:
开源 java android app 开发(一)开发环境的搭建-优快云博客
开源 java android app 开发(二)工程文件结构-优快云博客
开源 java android app 开发(三)GUI界面布局和常用组件-优快云博客
开源 java android app 开发(四)GUI界面重要组件-优快云博客
开源 java android app 开发(五)文件和数据库存储-优快云博客
开源 java android app 开发(六)多媒体使用-优快云博客
开源 java android app 开发(七)通讯之Tcp和Http-优快云博客
开源 java android app 开发(八)通讯之Mqtt和Ble-优快云博客
开源 java android app 开发(九)后台之线程和服务-优快云博客
开源 java android app 开发(十)广播机制-优快云博客
开源 java android app 开发(十一)调试、发布-优快云博客
开源 java android app 开发(十二)封库.aar-优快云博客
推荐链接:
开源C# .net mvc 开发(一)WEB搭建_c#部署web程序-优快云博客
开源 C# .net mvc 开发(二)网站快速搭建_c#网站开发-优快云博客
开源 C# .net mvc 开发(三)WEB内外网访问(VS发布、IIS配置网站、花生壳外网穿刺访问)_c# mvc 域名下不可訪問內網,內網下可以訪問域名-优快云博客
开源 C# .net mvc 开发(四)工程结构、页面提交以及显示_c#工程结构-优快云博客
开源 C# .net mvc 开发(五)常用代码快速开发_c# mvc开发-优快云博客
本章主要内容:使用 Qt Charts 模块,通过定时器模拟数据源,动态更新图表,实现了一个简单的波形显示器。
一、基本介绍
该程序创建了一个主窗口,包含一个可以显示两条实时曲线的图表(MyWave)和三个控制按钮(开始、停止、清除)。通过定时器模拟数据源,动态更新图表,实现了一个简单的波形显示器。
需要修改的文件有4个,如下图

三个按钮为代码中生成,不需要特别注意。mywave需要做出单独的文件创建调用,这里需要拉入Widget控件,然后右键进行提升,为mywave。
拉入Widget控件

右键进行提升

设为mywave

二、源码分析
2.1 头文件:mainwindow.h
目的:定义主窗口类 MainWindow。
关键成员:
MyWave *wave;: 指向自定义波形显示部件的指针,是应用程序的核心。
QTimer *timer;: 定时器,用于周期性地触发数据更新,模拟实时数据流。
代码如下图:

2.2 头文件:mywave.h
目的:定义自定义波形显示部件类 MyWave,继承自 QWidget。
核心组件:
QChart *m_chart;: 图表对象,是所有系列和坐标轴的容器。
QChartView *m_chartView;: 用于显示图表的视图部件。
QSplineSeries *m_series1, *m_series2;: 两条平滑曲线系列,用于存储和绘制数据点。
QValueAxis *m_axisX, *m_axisY;: X轴和Y轴,用于定义图表的坐标范围。
数据管理:
QList<qreal> m_data1, m_data2;: 两个链表,用于在内存中存储接收到的所有数据(用于回放或缩放等功能,但当前实现主要用于管理显示点数)。
int m_maxDisplayPoints = 200;: 控制图表上最大显示的数据点数,实现“滑动窗口”效果。
qreal m_xCounter = 0;: 一个不断递增的计数器,作为每个新数据点的X坐标。
公有接口 (API):
提供了一系列 set... 方法(如 setYRange, setLine1Color),这使得在 MainWindow 中配置波形显示的外观和行为非常方便,体现了良好的可定制性。
appendData(): 最重要的方法,用于向图表追加新的数据点。
clearData(): 清除所有数据。
代码如下图:

3. 源文件:mainwindow.cpp
构造函数:
初始化UI:
创建 MyWave 实例 wave 并对其进行详细配置(大小、范围、标题、颜色等)。
创建按钮,并设置了统一的样式表 (QSS) 使其美观。
使用 QVBoxLayout 和 QHBoxLayout 进行布局管理。
初始化定时器:
创建 QTimer 实例 timer。
连接定时器的 timeout() 信号到一个 Lambda 表达式,该表达式生成正弦和余弦数据(qSin, qCos)并调用 wave->appendData()。phase1 和 phase2 是静态变量,用于保持相位连续,使波形平滑变化。
代码如下图:

4. 源文件:mywave.cpp
核心方法 appendData():
将新数据 yValue1 和 yValue2 分别添加到 m_data1 和 m_data2 列表的末尾。
递增X轴计数器 m_xCounter。
滑动窗口逻辑:检查数据列表大小是否超过 m_maxDisplayPoints。如果超过,则移除列表最先加入的数据(removeFirst()),保持列表长度固定,从而实现图表只显示最新的一段数据。
调用 updateChart() 刷新显示。
核心方法 updateChart():
性能关键:这是代码中最高效的部分。它没有使用 clear() 再逐个 append(),而是先将要显示的所有点准备在 QVector<QPointF> 中,然后一次性调用 QSplineSeries::replace()。这极大地减少了绘图开销,非常适合实时数据更新。
计算当前显示的X轴起始位置 (startX)。
遍历数据列表,为每个Y值创建对应的点(X坐标为 startX + i)。
用准备好的点集替换整个系列的数据。
根据 startX 和 m_maxDisplayPoints 调整X轴的范围,使视图跟随数据滑动。
resizeEvent():
确保当 MyWave 部件大小改变时,内部的 m_chartView 也随之改变,充满整个客户区。
代码如下图:

三、所有源码
3.1 mainwindow.h文件源码
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTimer>
class MyWave;
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
MyWave *wave;
QTimer *timer;
};
#endif // MAINWINDOW_H
3.2 mywave.h文件源码
#ifndef MYWAVE_H
#define MYWAVE_H
#include <QWidget>
#include <QtCharts>
QT_CHARTS_USE_NAMESPACE
class MyWave : public QWidget
{
Q_OBJECT
public:
explicit MyWave(QWidget *parent = nullptr);
// 设置方法
void setSize(const QSize &size);
void setXRange(qreal min, qreal max);
void setYRange(qreal min, qreal max);
void setAxisTitle(const QString &xTitle, const QString &yTitle);
void setBackgroundColor(const QColor &color);
void setLine1Color(const QColor &color);
void setLine2Color(const QColor &color);
void setMaxDisplayPoints(int count);
void appendData(qreal yValue1, qreal yValue2);
void clearData();
protected:
void resizeEvent(QResizeEvent *event) override;
private:
void updateChart();
QChart *m_chart;
QChartView *m_chartView;
QSplineSeries *m_series1;
QSplineSeries *m_series2;
QValueAxis *m_axisX;
QValueAxis *m_axisY;
QList<qreal> m_data1;
QList<qreal> m_data2;
int m_maxDisplayPoints = 200;
qreal m_xCounter = 0;
};
#endif // MYWAVE_H
3.3 mainwindow.cpp文件源码
#include "mainwindow.h"
#include "mywave.h"
#include <QPushButton>
#include <QVBoxLayout>
#include <QHBoxLayout>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
// 创建波形控件
wave = new MyWave(this);
wave->setSize(QSize(600, 300));
wave->setXRange(0, 100);
wave->setYRange(-10, 10);
wave->setAxisTitle("时间", "数值");
wave->setLine1Color(Qt::blue);
wave->setLine2Color(Qt::red);
wave->setMaxDisplayPoints(100);
// 创建按钮控件
QPushButton *startButton = new QPushButton("开始", this);
QPushButton *stopButton = new QPushButton("停止", this);
QPushButton *clearButton = new QPushButton("清除", this);
// 设置按钮样式
QString buttonStyle = "QPushButton {"
" padding: 8px 16px;"
" font-size: 14px;"
" min-width: 80px;"
" border-radius: 4px;"
" background-color: #f0f0f0;"
"}"
"QPushButton:hover {"
" background-color: #e0e0e0;"
"}";
startButton->setStyleSheet(buttonStyle);
stopButton->setStyleSheet(buttonStyle);
clearButton->setStyleSheet(buttonStyle);
// 创建水平布局放置按钮
QHBoxLayout *buttonLayout = new QHBoxLayout();
buttonLayout->addWidget(startButton);
buttonLayout->addWidget(stopButton);
buttonLayout->addWidget(clearButton);
buttonLayout->addStretch();
buttonLayout->setSpacing(10);
// 创建垂直主布局
QVBoxLayout *mainLayout = new QVBoxLayout();
mainLayout->addLayout(buttonLayout);
mainLayout->addWidget(wave);
mainLayout->setContentsMargins(20, 20, 20, 20);
mainLayout->setSpacing(15);
// 设置中央部件
QWidget *centralWidget = new QWidget(this);
centralWidget->setLayout(mainLayout);
setCentralWidget(centralWidget);
this->setFixedSize(1200, 800);
// 定时器用于模拟数据
timer = new QTimer(this);
bool isRunning = false;
// 按钮信号连接
connect(startButton, &QPushButton::clicked, [=, &isRunning]() {
if (!isRunning) {
timer->start(100);
isRunning = true;
}
});
connect(stopButton, &QPushButton::clicked, [=, &isRunning]() {
timer->stop();
isRunning = false;
});
connect(clearButton, &QPushButton::clicked, [this]() {
wave->clearData();
});
connect(timer, &QTimer::timeout, [this]() {
static qreal phase1 = 0;
static qreal phase2 = 0;
wave->appendData(
qSin(phase1) * 10,
qCos(phase2) * 8
);
phase1 += 0.1;
phase2 += 0.15;
});
}
MainWindow::~MainWindow()
{
delete timer;
delete wave;
}
3.4 mywave.cpp文件源码
#include "mywave.h"
MyWave::MyWave(QWidget *parent) : QWidget(parent)
{
// 初始化图表
m_chart = new QChart();
// 初始化两条曲线
m_series1 = new QSplineSeries();
m_series2 = new QSplineSeries();
m_chart->addSeries(m_series1);
m_chart->addSeries(m_series2);
// 初始化坐标轴
m_axisX = new QValueAxis();
m_axisY = new QValueAxis();
setXRange(0, 200);
setYRange(-10, 10);
// 添加坐标轴
m_chart->addAxis(m_axisX, Qt::AlignBottom);
m_chart->addAxis(m_axisY, Qt::AlignLeft);
m_series1->attachAxis(m_axisX);
m_series1->attachAxis(m_axisY);
m_series2->attachAxis(m_axisX);
m_series2->attachAxis(m_axisY);
// 默认样式
setBackgroundColor(Qt::white);
setLine1Color(Qt::blue);
setLine2Color(Qt::red);
m_chart->legend()->setVisible(true);
m_series1->setName("曲线1");
m_series2->setName("曲线2");
m_chart->legend()->setAlignment(Qt::AlignBottom);
// 初始化视图
m_chartView = new QChartView(m_chart, this);
m_chartView->setRenderHint(QPainter::Antialiasing);
setSize(QSize(800, 400));
}
void MyWave::setSize(const QSize &size)
{
this->resize(size);
m_chartView->resize(size);
}
void MyWave::setXRange(qreal min, qreal max)
{
m_axisX->setRange(min, max);
m_axisX->setTickCount(10);
}
void MyWave::setYRange(qreal min, qreal max)
{
m_axisY->setRange(min, max);
m_axisY->setTickCount(10);
}
void MyWave::setAxisTitle(const QString &xTitle, const QString &yTitle)
{
m_axisX->setTitleText(xTitle);
m_axisY->setTitleText(yTitle);
}
void MyWave::setBackgroundColor(const QColor &color)
{
m_chart->setBackgroundBrush(QBrush(color));
m_chart->setBackgroundPen(QPen(color));
}
void MyWave::setLine1Color(const QColor &color)
{
m_series1->setPen(QPen(color, 2));
}
void MyWave::setLine2Color(const QColor &color)
{
m_series2->setPen(QPen(color, 2));
}
void MyWave::setMaxDisplayPoints(int count)
{
m_maxDisplayPoints = count;
setXRange(0, count);
}
void MyWave::appendData(qreal yValue1, qreal yValue2)
{
m_data1.append(yValue1);
m_data2.append(yValue2);
m_xCounter++;
if (m_data1.size() > m_maxDisplayPoints) {
m_data1.removeFirst();
m_data2.removeFirst();
}
updateChart();
}
void MyWave::clearData()
{
m_data1.clear();
m_data2.clear();
m_xCounter = 0;
m_series1->clear();
m_series2->clear();
updateChart();
}
void MyWave::updateChart()
{
QVector<QPointF> points1, points2;
qreal startX = qMax(0.0, m_xCounter - m_maxDisplayPoints);
for (int i = 0; i < m_data1.size(); ++i) {
points1.append(QPointF(startX + i, m_data1.at(i)));
points2.append(QPointF(startX + i, m_data2.at(i)));
}
m_series1->replace(points1);
m_series2->replace(points2);
m_axisX->setRange(startX, startX + m_maxDisplayPoints);
}
void MyWave::resizeEvent(QResizeEvent *event)
{
m_chartView->resize(event->size());
QWidget::resizeEvent(event);
}
四、演示效果


被折叠的 条评论
为什么被折叠?



