【QT学习笔记】QT进阶操作功能(四)
一. 串口(QSerialPort)
1. 定义
串口(UART,通用异步收发传输器),是硬件异步串行通信接口,通过 TX 发送引脚、RX 接收引脚,按字节逐位收发数据。
Qt 中使用QSerialPort类实现串口收发,QSerialPortInfo用来查询电脑上所有可用串口。
常用于:单片机 (STM32/51) ↔ PC 上位机通信,典型硬件:CH340、PL2303 USB 转串口芯片。
2. 工作模式
1. 发送数据工作流程
- 配置串口参数:端口名、波特率、数据位、停止位、校验位。单片机和 Qt 上位机参数必须完全一致。
- 打开串口(读写模式)。
- 将待发送字符串 / 字节数组写入串口缓冲区。
- 硬件自动按位从 TX 引脚输出数据,发送给单片机。
- 发送完成后缓冲区清空,可继续下一次发送。
2. 接收数据工作流程
- 同样配置串口参数,打开串口。
- 单片机从 TX 引脚发送数据到电脑串口 RX 引脚。
- 串口收到数据,存入 Qt 内部接收缓冲区,触发
readyRead()信号。
- 在
readyRead()槽函数调用读取接口,取出缓冲区数据。
- 处理数据(显示、解析)。
3. 常用函数
常用函数
| 函数 | 作用 |
|---|---|
setPortName(QString name) | 设置串口名,Windows如COM3 |
setBaudRate(qint32 baud) | 设置波特率(9600、115200) |
setDataBits(DataBits) | 设置数据位,常用Data8(8位) |
setStopBits(StopBits) | 设置停止位,OneStop(1位) |
setParity(Parity) | 校验位,NoParity无校验 |
open(QIODevice::ReadWrite) | 打开串口,读写模式 |
close() | 关闭串口,释放资源 |
write(QByteArray) | 发送数据,写入串口 |
readAll() | 接收数据,一次性读取缓冲区全部字节 |
isOpen() | bool,判断串口是否打开 |
常用信号
| 信号 | 触发时机 |
|---|---|
readyRead() | 串口收到数据,可以读取 |
errorOccurred() | 串口出错(串口被拔出、参数错误) |
4. 代码示例
#include <QSerialPort>
#include <QSerialPortInfo>
#include <QMessageBox>
QSerialPort *serial;
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
serial = new QSerialPort(this);
// 接收数据信号绑定
connect(serial, &QSerialPort::readyRead, this, [=](){
// 读取收到的串口数据
QByteArray recvBuf = serial->readAll();
// recvBuf就是单片机上传的数据
});
// 串口错误信号
connect(serial, &QSerialPort::errorOccurred, this, [=](){
QMessageBox::warning(this,"串口错误",serial->errorString());
});
}
// 打开串口按钮槽函数
void MainWindow::on_openSerialBtn_clicked()
{
serial->setPortName("COM3"); // 修改为你的设备串口号
serial->setBaudRate(QSerialPort::Baud9600);
serial->setDataBits(QSerialPort::Data8);
serial->setStopBits(QSerialPort::OneStop);
serial->setParity(QSerialPort::NoParity);
bool ok = serial->open(QIODevice::ReadWrite);
if(!ok)
{
QMessageBox::warning(this,"失败","串口打开失败!");
}
}
// 发送按钮槽函数:Qt发送数据给单片机
void MainWindow::on_sendBtn_clicked()
{
if(serial->isOpen())
{
serial->write("hello mcu\r\n");
}
}
// 关闭串口按钮
void MainWindow::on_closeSerialBtn_clicked()
{
serial->close();
}
二. QT启动新窗口
1. 新建文件
1. 右键创建新文件

2. 选择文件类型

3. 选择界面类型

4. 设置界面名称,此时,头文件,源文件,ui文件均会创建。

5. 点击完成

6. 完成创建

2. 定义
Qt 启动新窗口,就是在当前主窗口运行期间,创建一个新的窗口类对象并让它显示出来。新窗口可以继承三类基类:
| 基类 | 特点 | 显示方式 |
|---|---|---|
QWidget | 普通窗口 | 只能 show()(非模态) |
QDialog | 对话框 | show() 非模态 或 exec() 模态 |
QMainWindow | 独立主窗口(带菜单栏/状态栏) | 只能 show() |
3. 常用函数
| 函数 | 作用 |
|---|---|
show() | 非模态显示,不阻塞代码,三类窗口都能用 |
exec() | 模态显示,阻塞代码,仅 QDialog 可用,关闭后返回结果 |
open() | 窗口级模态,只阻塞父窗口,异步不阻塞 |
close() | 关闭窗口 |
accept() | 确定关闭,exec() 返回 Accepted |
reject() | 取消关闭,exec() 返回 Rejected |
setModal(bool) | 设置模态 / 非模态 |
deleteLater() | 窗口关闭后延迟销毁对象,防止内存泄漏 |
4. 流程
① 新建窗口类:NewWin 继承 QWidget/QDialog;.pro 加入 cpp/h/ui;ui 里拖控件
② 包含头文件:主窗口 .h 里 #include "newwin.h"
③ 声明槽函数:主窗口 .h 中 private slots: void openNewWin();
④ 实现槽:new NewWin(this) 再 show()(非模态)或 exec()(模态,仅 QDialog)
⑤ 绑定触发:按钮 clicked 信号 connect 到 openNewWin()
⑥ 运行结果:点击按钮 → 事件循环显示新窗口,程序不退出

5. 示例代码
方式一:非模态(新窗口继承 QWidget,可同时操作主窗口)
// ===== 新窗口类 NewWin.h =====
#include <QWidget>
class NewWin : public QWidget {
Q_OBJECT
public:
explicit NewWin(QWidget *parent = nullptr);
};
// NewWin.cpp 构造函数里 ui->setupUi(this) 即可
// ===== 主窗口 widget.h =====
#include "newwin.h" // ② 包含头文件
...
private slots:
void openNewWin(); // ③ 声明槽
// ===== 主窗口 widget.cpp =====
// 构造函数中绑定触发(⑤)
connect(ui->openBtn, &QPushButton::clicked, this, &Widget::openNewWin);
// ④ 实现槽:非模态
void Widget::openNewWin()
{
NewWin *w = new NewWin(this); // 指定父对象,自动回收
w->show(); // 非模态,不阻塞
}
方式二:模态(新窗口继承 QDialog,弹出后主窗口不能操作)
#include "loginDialog.h"
...
void Widget::openLogin()
{
loginDialog dlg(this); // 栈对象即可(exec 会阻塞)
int ret = dlg.exec(); // 模态,代码阻塞到关闭
if(ret == QDialog::Accepted){
qDebug() << "点击确定";
}else{
qDebug() << "点击取消";
}
}
6. 注意事项
| 显示方式 | 创建方式 | 原因 |
|---|---|---|
非模态 show() | 必须 new(堆) | show() 不阻塞代码,函数一执行完就返回;如果用的是栈对象,对象立刻被销毁,窗口一闪而过 |
模态 exec() | 可以用栈对象 | exec() 会阻塞到窗口关闭才返回,函数执行期间栈对象一直存活,不会被销毁 |
三. 多线程
选择class,既会创建一个头文件,又会创建一个源文件。

1. 定义
多线程 = 一个进程内同时运行多个线程,每个线程独立执行一段代码。Qt 中主要靠继承 QThread、重写 run() 来实现子线程。
关键概念:
- 主线程(GUI 线程):负责界面显示和事件循环,所有 UI 操作都在这里
- 子线程(工作线程):负责耗时任务(网络收发、大文件读写、复杂计算)
2. 作用
- 耗时操作不卡界面:网络收发、文件读写、计算放到子线程,主窗口不会假死
- 并行执行:多个任务同时跑,提升效率
- 后台持续运行:持续监听数据、定时采集(如 socket 一直等
readyRead) - UI 线程安全:界面只归主线程改,子线程通过信号把数据传回来
3. 常用函数
| 函数 | 作用 |
|---|---|
start() | 启动线程,自动调用 run(),立即返回不阻塞 |
run() | 线程入口,子线程任务代码写在这里(必须重写) |
quit() | 退出线程事件循环(线程里有 exec() 时用) |
exit(int code) | 退出事件循环并返回退出码 |
wait() | 阻塞等待线程执行完毕 |
isRunning() | bool,线程是否在运行 |
msleep(int) | 线程内静态休眠,毫秒 |
sleep(int) | 线程内静态休眠,秒 |
4. 流程
- new 线程对象(主线程):
myThread *t = new myThread(socket); - connect 线程信号 → 主窗口槽(主线程):
connect(t, &myThread::sendToWidget, this, &Widget::threadSlot); t->start();启动线程(主线程):立即返回,run() 开始在新线程跑- run() 执行任务(子线程):重写的 run() 里放耗时代码,如持续监听 readyRead
- emit 信号发数据(子线程):收到数据后
emit sendToWidget(ba); - 槽函数更新 UI(主线程):threadSlot 执行,run 返回后发 finished 信号,线程退出
5. 示例代码
// ===== myThread.h =====
#include <QThread>
#include <QTcpSocket>
class myThread : public QThread
{
Q_OBJECT
public:
explicit myThread(QTcpSocket *s);
void run(); // 重写:子线程入口
signals:
void sendToWidget(QByteArray b); // 自定义信号:往主线程传数据
public slots:
void clientInfoSlot();
private:
QTcpSocket *socket;
};
// ===== myThread.cpp =====
#include "mythread.h"
myThread::myThread(QTcpSocket *s) // 构造函数:主线程执行
{
socket = s;
}
void myThread::run() // 子线程执行
{
connect(socket, &QTcpSocket::readyRead, this, &myThread::clientInfoSlot);
}
void myThread::clientInfoSlot() // 子线程执行
{
QByteArray ba = socket->readAll();
emit sendToWidget(ba); // 信号发回主线程
}
// ===== widget.cpp(主线程)=====
void Widget::newClientHandler()
{
QTcpSocket *socket = server->nextPendingConnection();
myThread *t = new myThread(socket); // ① new 线程对象
connect(t, &myThread::sendToWidget, this, &Widget::threadSlot); // ② 关联信号
t->start(); // ③ 启动线程
}
void Widget::threadSlot(QByteArray b) // ⑥ 主线程更新 UI
{
ui->mainLineEdit->setText(QString(b));
}
四. 自定义信号
1. 定义
用户在自己类里用 signals: 关键字声明的信号,配合 emit 发射、connect 连接槽函数,实现对象间通信。它是 Qt 信号槽机制的核心
2. 作用
- 跨线程传数据:子线程收数据 →
emit信号 → 主线程槽更新 UI - 解耦通信:发送方不关心谁接收,接收方自己
connect就行 - 一对多通知:一个信号可以连多个槽,发射一次全部触发
- 状态变化通知:数据到达、进度更新、错误发生、任务完成
3. 示例代码
// ===== myThread.h =====
#include <QThread>
#include <QTcpSocket>
class myThread : public QThread
{
Q_OBJECT
public:
explicit myThread(QTcpSocket *s);
void run();
signals:
void sendToWidget(QByteArray b); // ① 声明自定义信号(带参数)
private:
QTcpSocket *socket;
};
// ===== myThread.cpp =====
void myThread::run()
{
connect(socket, &QTcpSocket::readyRead, this, &myThread::onReadyRead);
}
void myThread::onReadyRead()
{
QByteArray ba = socket->readAll();
emit sendToWidget(ba); // ③ 发射信号,把数据带出去
}
// ===== widget.cpp =====
connect(t, &myThread::sendToWidget, this, &Widget::threadSlot); // ② 连接
void Widget::threadSlot(QByteArray b) // ⑤ 槽函数接收参数
{
ui->mainLineEdit->setText(QString(b));
}
五. 文件结构
1. 编译文件
编译时,Qt Creator 自动创建的文件。
编译时把所有生成的中间文件、exe 都扔在这里,不污染源码文件夹

2. 编译文件内容


3. 生成release文件
参考步骤:

第 1 步:编译 Release 版
- 用 Qt Creator 打开 TCPClient 项目
- 左下角(竖着的套件选择器)点一下,把
Debug切换成Release - 点绿色三角运行(或
Ctrl+Shift+B构建) - 等它编完,出现对应的release文件:


第 2 步:windeployqt 部署 DLL
打开 PowerShell,执行这两条:
cd "E:\QT_Project\Study_project\Study_files\QtOnline\build-TCPClient-Desktop_Qt_5_12_12_MinGW_64_bit-Release\release"
D:\Qt\Qt5.12.12\5.12.12\mingw73_64\bin\windeployqt.exe TCPClient.exe
出现了对应的dll文件,表明完成:

第 3 步:自测 + 打包发送
- 双击
TCPClient.exe,确认窗口正常弹出 - 把整个
release文件夹压成 zip(连 DLL 一起,不是只发 exe!) - 发给朋友 → 对方解压 → 双击 exe 就能用,不需要装 Qt
更多推荐


所有评论(0)