You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PyQt5 GUI每秒更新多标签文本30次后冻结问题咨询

问题描述

我用PyQt5编写了一段代码,用于订阅一个30Hz(每秒发布30条消息)的ROS话题。当用户从下拉框选择gaze话题时,会触发create_subscriber_gaze_information函数创建ROS订阅者,其回调函数show_gaze_information会每秒30次更新GUI中的多个标签文本。但程序运行约10秒后,整个GUI会出现冻结现象。

相关代码

下拉框信号连接

# 当下拉框值变化时,创建gaze信息的ROS订阅者
ui.gaze_info_topic_name_value.activated.connect(create_subscriber_gaze_information)

创建订阅者函数

def create_subscriber_gaze_information():

    # 取消订阅之前的话题
    if ui.gaze_info_topic_name_value.ros_subs is not None:
        ui.gaze_info_topic_name_value.ros_subs.unregister()

    # 创建新订阅者
    ui.gaze_info_topic_name_value.ros_subs = rospy.Subscriber(
                                                        name=ui.gaze_info_topic_name_value.currentText(), 
                                                        data_class=gazesense_msgs.msg.TrackedUserArray, 
                                                        callback=show_gaze_information,
                                                        queue_size=1)

更新标签回调函数

def show_gaze_information(gaze_msg):
   
    if ui.groupBox_gaze.is_lock == True:
        return

    ui.groupBox_gaze.is_lock = True

    for person in gaze_msg.people:
        
        #################################
        # 头部信息
        #################################
        # 设置头部平移X值
        ui.label_head_translation_x_value.setText("{}".format(person.tracking_info.head_pose.transform.translation.x))
        # 设置头部平移Y值
        ui.label_head_translation_y_value.setText("{}".format(person.tracking_info.head_pose.transform.translation.y))
        # 设置头部平移Z值
        ui.label_head_translation_z_value.setText("{}".format(person.tracking_info.head_pose.transform.translation.z))
        # 设置头部旋转X值
        ui.label_head_rotation_x_value.setText("{}".format(person.tracking_info.head_pose.transform.rotation.x))
        # 设置头部旋转Y值
        ui.label_head_rotation_y_value.setText("{}".format(person.tracking_info.head_pose.transform.rotation.y))
        # 设置头部旋转Z值
        ui.label_head_rotation_z_value.setText("{}".format(person.tracking_info.head_pose.transform.rotation.z))
        # 设置头部旋转W值
        ui.label_head_rotation_w_value.setText("{}".format(person.tracking_info.head_pose.transform.rotation.w))
        # 设置是否丢失跟踪
        ui.label_head_is_lost_value.setText("{}".format(person.tracking_info.head_pose.is_lost))
        #################################
        # 左眼信息
        #################################
        # 设置左眼原点X值
        ui.label_left_eye_orgin_x_value.setText("{}".format(person.tracking_info.gaze.left_eye_ray.origin.x))
        # 设置左眼原点Y值
        ui.label_left_eye_orgin_y_value.setText("{}".format(person.tracking_info.gaze.left_eye_ray.origin.y))
        # 设置左眼原点Z值
        ui.label_left_eye_orgin_z_value.setText("{}".format(person.tracking_info.gaze.left_eye_ray.origin.z))
        # 设置左眼方向X值
        ui.label_left_eye_dir_x_value.setText("{}".format(person.tracking_info.gaze.left_eye_ray.direction.x))
        # 设置左眼方向Y值
        ui.label_left_eye_dir_y_value.setText("{}".format(person.tracking_info.gaze.left_eye_ray.direction.y))
        # 设置左眼方向Z值
        ui.label_left_eye_dir_z_value.setText("{}".format(person.tracking_info.gaze.left_eye_ray.direction.z))
        # 设置左眼置信度
        ui.label_left_eye_conf_value.setText("{}".format(person.tracking_info.gaze.confidence_left.value))
        # 设置左眼是否丢失跟踪
        ui.label_left_eye_is_lost_value.setText("{}".format(person.tracking_info.gaze.is_lost))
 
    ui.groupBox_gaze.is_lock = False
问题原因

ROS订阅者的回调函数运行在ROS后台线程中,而PyQt的GUI操作必须在**主线程(UI线程)**中执行。直接在回调里更新UI组件会引发线程冲突,大量跨线程的UI更新操作会堵塞事件队列,最终导致GUI冻结。

另外,30Hz的更新频率本身较高,即使加了锁,也只能防止同一回调的并发执行,无法解决跨线程操作UI的核心问题。

解决方案

使用PyQt的pyqtSignal实现线程间通信,将ROS回调中获取的数据发送到主线程,由主线程安全更新UI:

步骤1:定义信号类(或在主窗口类中添加信号)

from PyQt5.QtCore import pyqtSignal, QObject

# 定义信号类用于传递ROS数据
class RosDataSignals(QObject):
    gaze_data_updated = pyqtSignal(object)  # 传递解析后的人员数据列表

# 在主窗口初始化时创建信号实例并连接到UI更新函数
self.ros_signals = RosDataSignals()
self.ros_signals.gaze_data_updated.connect(self.update_gaze_ui)

步骤2:修改ROS回调函数,仅处理数据并发送信号

def show_gaze_information(gaze_msg):
    # 只传递数据,不操作UI
    self.ros_signals.gaze_data_updated.emit(gaze_msg.people)

步骤3:在主线程实现UI更新函数

该函数运行在主线程,可安全更新UI组件:

def update_gaze_ui(people_list):
    if ui.groupBox_gaze.is_lock:
        return

    ui.groupBox_gaze.is_lock = True

    for person in people_list:
        # 更新头部信息
        ui.label_head_translation_x_value.setText(f"{person.tracking_info.head_pose.transform.translation.x}")
        ui.label_head_translation_y_value.setText(f"{person.tracking_info.head_pose.transform.translation.y}")
        ui.label_head_translation_z_value.setText(f"{person.tracking_info.head_pose.transform.translation.z}")
        ui.label_head_rotation_x_value.setText(f"{person.tracking_info.head_pose.transform.rotation.x}")
        ui.label_head_rotation_y_value.setText(f"{person.tracking_info.head_pose.transform.rotation.y}")
        ui.label_head_rotation_z_value.setText(f"{person.tracking_info.head_pose.transform.rotation.z}")
        ui.label_head_rotation_w_value.setText(f"{person.tracking_info.head_pose.transform.rotation.w}")
        ui.label_head_is_lost_value.setText(f"{person.tracking_info.head_pose.is_lost}")
        
        # 更新左眼信息
        ui.label_left_eye_orgin_x_value.setText(f"{person.tracking_info.gaze.left_eye_ray.origin.x}")
        ui.label_left_eye_orgin_y_value.setText(f"{person.tracking_info.gaze.left_eye_ray.origin.y}")
        ui.label_left_eye_orgin_z_value.setText(f"{person.tracking_info.gaze.left_eye_ray.origin.z}")
        ui.label_left_eye_dir_x_value.setText(f"{person.tracking_info.gaze.left_eye_ray.direction.x}")
        ui.label_left_eye_dir_y_value.setText(f"{person.tracking_info.gaze.left_eye_ray.direction.y}")
        ui.label_left_eye_dir_z_value.setText(f"{person.tracking_info.gaze.left_eye_ray.direction.z}")
        ui.label_left_eye_conf_value.setText(f"{person.tracking_info.gaze.confidence_left.value}")
        ui.label_left_eye_is_lost_value.setText(f"{person.tracking_info.gaze.is_lost}")

    ui.groupBox_gaze.is_lock = False

额外优化建议

  • 调整ROS订阅者的queue_size:当前设置为1,30Hz的话题可适当调大(比如5),减少消息丢失同时降低回调触发压力。
  • 降低UI更新频率:若无需30Hz的更新速度,可在主线程设置定时器(如每30ms更新一次),仅取最新消息数据,减少UI刷新次数。

内容的提问来源于stack exchange,提问作者CVDE

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 20:12:14