QT线程的派生
Q_OBJECTpublic:/********/signals:private:// 实际上未在 run 中使用,通过信号槽更新 UI设计点实现方式优点硬件访问隔离通过 RPC 与后端服务器通信前端不直接操作硬件,解耦,便于调试和远程部署不阻塞 UI温湿度读取放在独立QThread中界面保持流畅,不会因网络或硬件延迟卡顿线程安全更新 UI使用信号槽(队列连接)Qt 自动处理跨线程 UI 更新,无
下面我将通过实例段代码中关于 DHT11 温湿度传感器 的全部运用,包括:RPC 通信机制、多线程设计、UI 交互以及启动/停止的控制流程,说明线程运用。
一、整体架构
程序通过 RPC(远程过程调用) 与后端服务器通信,后端服务器负责实际驱动 DHT11 硬件读取温湿度。
前端是一个 Qt 界面,包含一个“温湿度”按钮(Temp_HumiButton)、两个显示标签(Humi_label 显示湿度,Temp_label 显示温度)。
核心组件:
-
DHT11Thread:继承自QThread,在子线程中循环读取温湿度。 -
rpc_dht11_read:RPC 客户端函数,向服务器发送 JSON 请求,并解析返回的温湿度数据。 -
MainWindow::on_Temp_HumiButton_clicked:按钮槽函数,用于启动或停止读取线程,并建立信号槽连接。
二、RPC 客户端实现(rpc_dht11_read)
int rpc_dht11_read(char *humi, char *temp)
功能:通过 TCP socket 向 RPC 服务器发送一个 JSON 格式的方法调用,获取 DHT11 的湿度(humi)和温度(temp)。
参数:两个输出指针,用于返回读取到的整数值(湿度为百分比,温度为摄氏度)。
执行步骤:
-
构造 JSON 请求
sprintf(buf, "{\"method\": \"dht11_read\", \"params\": [0], \"id\": \"2\" }");-
method:调用的远程方法名dht11_read。 -
params:参数数组,这里传入 0(占位,无实际意义)。 -
id:请求标识(这里固定为 "2")。
-
-
通过全局 socket
g_SocketClient发送请求
使用send发送 JSON 字符串,并检查是否全部发送成功。 -
读取服务器响应
-
循环调用
read,跳过仅包含\r或\n的空行。 -
读取到的有效响应存入
buf。
-
-
解析 JSON 响应
-
使用
cJSON_Parse解析 JSON。 -
获取
result数组,其第 0 个元素是湿度,第 1 个元素是温度。 -
将整数值赋给
*humi和*temp。 -
释放 cJSON 对象。
-
-
返回值:成功返回 0,失败返回 -1。
注意:
g_SocketClient需要通过RPC_Client_Init()提前初始化并连接服务器(代码片段中未展示调用,但一般在main函数中执行)。
三、DHT11 线程类(DHT11Thread)
类定义 MainWindows.h
class DHT11Thread : public QThread {
Q_OBJECT
public:
void run() override;
void Setlabels(QLabel *labelHumi, QLabel *labelTemp); /********/
signals:
void updateHumi(QString humi);
void updateTemp(QString temp);
private:
QLabel *LabelHumi; // 实际上未在 run 中使用,通过信号槽更新 UI
QLabel *LabelTemp;
};
1. run() 函数实现zz
void DHT11Thread::run() {
char humi, temp;
char buf[50];
while(!isInterruptionRequested()) {
if (0 == rpc_dht11_read(&humi, &temp)) {
sprintf(buf, "%d%%", humi);
emit updateHumi(QString(buf)); //发送线程读到的rpc请求
sprintf(buf, "%d", temp);
emit updateTemp(QString(buf)); //发射更新后的数据
msleep(1000); // 成功读取后等待 1 秒
}
// 无论成功或失败,都等待 1 秒,并在此过程中可响应中断请求
for (int i = 0; i < 10 && !isInterruptionRequested(); ++i)
msleep(100); // 分 10 次共睡眠 1 秒,提高中断响应速度
//此1秒间如果刚好有中断请求
}
}
关键点:
-
循环检查
isInterruptionRequested(),支持外部请求终止线程。 -
调用
rpc_dht11_read读取温湿度。若成功:-
格式化湿度为
"XX%",温度格式化为"XX"。 -
通过
emit updateHumi和updateTemp发送信号。
-
-
若读取失败,不发送信号,但仍会进入下方的睡眠等待。
-
睡眠采用 分片睡眠(10×100ms),而不是直接
msleep(1000),这样当主线程调用requestInterruption()时,线程可以更快地检查到中断请求并退出,提高响应速度。
2. Setlabels 方法
cpp
void DHT11Thread::Setlabels(QLabel *labelHumi, QLabel *labelTemp) {
this->LabelHumi = labelHumi;
this->LabelTemp = labelTemp;
}
-
此方法将 UI 上的标签指针保存到线程对象中。
-
但在
run()中并未使用这两个指针,因为更新 UI 是通过信号槽机制完成的(信号连接到标签的setText)。 -
所以
Setlabels实际上可以省略,保留它可能是历史遗留或为了其他未展示的用途。 -
线程通过信号发送真实数据,主线程接受数据
四、主窗口(MainWindow)中的 DHT11 交互
1. 按钮槽函数 on_Temp_HumiButton_clicked
void MainWindow::on_Temp_HumiButton_clicked()
{
static int status = 1; // 1: 启动,0: 停止
if(status)
{
thread = new DHT11Thread();
thread->Setlabels(ui->Humi_label, ui->Temp_label);
connect(thread, &DHT11Thread::updateHumi, ui->Humi_label, &QLabel::setText);
//将线程的 updateHumi信号连接到 ui->Humi_label的 setText槽。更新QT界面
connect(thread, &DHT11Thread::updateTemp, ui->Temp_label, &QLabel::setText);
thread->start();
}
else
{
if (thread)
{
thread->requestInterruption(); // 请求线程中断
thread->quit(); // 退出线程事件循环(对于 QThread 非必须,但安全)
thread->wait(); // 等待线程真正结束
delete thread;
thread = nullptr;
}
}
status = !status;
}
启动流程:
-
status静态变量记录当前状态(1=未启动,0=已启动),每次点击切换。 -
创建
DHT11Thread对象。 -
调用
Setlabels(虽然后续未用到,但保留)。 -
将线程的
updateHumi信号连接到ui->Humi_label的setText槽。 -
将线程的
updateTemp信号连接到ui->Temp_label的setText槽。 -
启动线程
thread->start(),即执行run()函数。
停止流程:
-
调用
thread->requestInterruption(),设置中断标志。 -
thread->quit()退出线程的事件循环(本线程没有事件循环,但调用无害)。 -
thread->wait()阻塞等待线程结束,确保资源安全释放。 -
删除线程对象并置空。
2. UI 标签
-
ui->Humi_label:显示湿度,格式为"XX%"。 -
ui->Temp_label:显示温度,格式为"XX"(单位隐含为摄氏度)。
五、完整的工作时序
-
程序启动:
main函数中调用RPC_Client_Init()连接到 RPC 服务器(代码未给出,但必须存在)。 -
用户点击“温湿度”按钮:
-
创建
DHT11Thread,连接信号槽,启动线程。
-
-
线程循环:
-
每秒钟调用一次
rpc_dht11_read。 -
若成功,发射
updateHumi和updateTemp信号。 -
主线程(Qt 事件循环)接收到信号后,自动更新 UI 标签的文本。
-
-
用户再次点击按钮:
-
请求线程中断,等待线程退出,销毁线程对象。
-
停止读取。
-
六、关键设计要点总结
| 设计点 | 实现方式 | 优点 |
|---|---|---|
| 硬件访问隔离 | 通过 RPC 与后端服务器通信 | 前端不直接操作硬件,解耦,便于调试和远程部署 |
| 不阻塞 UI | 温湿度读取放在独立 QThread 中 |
界面保持流畅,不会因网络或硬件延迟卡顿 |
| 线程安全更新 UI | 使用信号槽(队列连接) | Qt 自动处理跨线程 UI 更新,无需手动加锁 |
| 线程优雅停止 | requestInterruption() + 分片睡眠 |
能够快速响应停止请求,避免 msleep(1000) 长时间阻塞 |
| 错误处理 | 读取失败时不发送信号,继续循环 | 不会因单次失败导致线程退出,增加鲁棒性 |
| JSON RPC 通信 | 简单文本协议,使用 cJSON 解析 | 易于实现,与语言无关,便于与不同后端集成 |
七、注意事项
-
RPC 全局 socket:
g_SocketClient在多线程中未加锁,但只有一个线程(DHT11Thread)会调用rpc_dht11_read,且主线程中其他 RPC 调用(LED、VRV、PRV)也在主线程,实际上不会有并发冲突,但严格来说如果多个线程同时使用 socket 需要保护。 -
RPC 响应解析:
rpc_dht11_read中假定响应中的result是数组且包含两个整数,未做充分错误检查,可以增强健壮性。 -
单位显示:温度没有显示单位(℃),可以在标签中加上或格式化时追加。
-
初始状态:按钮上的文字没有变化(未显示“启动/停止”),用户体验可改进。
八、总结
这段代码完整演示了在 Qt 应用中如何:
-
使用 RPC 客户端 远程读取 DHT11 传感器。
-
利用 QThread 实现后台周期性数据采集。
-
通过 信号槽 安全地更新 UI 控件。
-
实现线程的 优雅启动与停止。
整个设计清晰地将硬件访问、业务逻辑和界面显示分离,适用于嵌入式 Linux 或物联网设备上的图形界面开发。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)