在进行Ubuntu下PyQt5与Ros结合的项目开发时考虑到ros处理回调以及循环等可能会导致GUI界面假死,所以打算将Ros的回调处理逻辑和GUI界面的更新放在两个线程,GUI当然是主线程,ros是子线程。我这里是使用ROS订阅手柄,测试代码,发现了一些问题。
1、当Ros的订阅和发布代码不在主线程
#!/usr/bin/env python3
# coding=utf-8
import rospy
from sensor_msgs.msg import Joy
from PyQt5.QtCore import pyqtSignal, QObject, QThread
from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel, QVBoxLayout, QWidget
import threading
import time
# 自定义的ROS数据读取线程
class JoyReaderThread(QThread):
# 定义信号,用于传递手柄数据
joy_data_received = pyqtSignal(dict)
def __init__(self):
super().__init__()
# 初始化ROS节点和订阅者(这些操作需要在单独的线程中完成,以避免阻塞主线程)
self.ros_initialized = False
self.sub = None
def run(self):
# 在线程中初始化ROS节点和订阅者
rospy.init_node('joy_reader', anonymous=True)
self.sub = rospy.Subscriber('/joy', Joy, self.callback)
self.ros_initialized = True
rospy.spin()
# ROS的spin循环,或者你可以使用一个循环加sleep来模拟(不推荐,但为了简单起见)
rate = rospy.Rate(10) # 10 Hz
while not rospy.is_shutdown():
# 这里通常不需要做任何事情,因为回调会自动被调用
# 但为了保持线程活跃,我们可以使用rate.sleep()
rate.sleep()
def callback(self, data):
# 将Joy消息转换为字典
joy_dict = {
'axes': {f'axis_{i}': data.axes[i] for i in range(6)},
'buttons': {f'button_{i}': data.buttons[i] for i in range(14)}
}
# 发送信号,准备更新界面(信号发射需要在创建该信号的线程中完成)
self.joy_data_received.emit(joy_dict)
# 主窗口类
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.joy_reader_thread = JoyReaderThread()
self.joy_reader_thread.joy_data_received.connect(self.update_ui)
self.initUI