在ubuntu20.04系统下,实现ros2 与pyqt5的联合

最近在负责项目中pyqt界面的制作,但网上关于ros2与pyqt5的中文资料很少,基本都是ros1 ,qt的博客和文章,通过不断的碰壁+摸索,终于实现了以下功能:

1. qt界面中启动节点

2. qt界面通过获取pid进程名关闭节点

3. qt界面通过定时器输出文本

重新写了一个简单的例子,代码如下:

launch.py

from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
    action_1 = Node(
    			package='village_li',
            	 executable='li4_node',
            	 name='li4_node',
               )
    return LaunchDescription([
        action_1
    ])

li4.py:

import rclpy  #ros2 接口库
from rclpy.node import Node #ros2 节点类
from std_msgs.msg import String  #字符串消息类型


class PublisherNode(Node):
	def __init__(self, name):
		super().__init__(name)   # 父类初始化
		self.pub = self.create_publisher(String, "cha", 10) #创建发布者对象(消息类型,话题名(密码),长度)
		self.i = 0
		self.timer = self.create_timer(0.5, self.timer_callback)
		
	def timer_callback(self):
		msg = String()        #创建消息对象
		msg.data = '%d' %(self.i) #实例化消息对象
		self.pub.publish(msg) #发布信息
		self.get_logger().info('Publishing: "%s"' % msg.data)  #输出日志
		self.i +=1
def main(args=None):
	rclpy.init(args=args)  # 初始化python接口函数
	node = PublisherNode("li4") #创建发布节点
	rclpy.spin(node)
	node.destroy_node()
	rclpy.shutdown()

 wang2.py:

from lib2to3.pytree import generate_matches
from numpy import tile
import rclpy
import sys
import time
from rclpy.node import Node
from std_msgs.msg import String
from python_qt_binding import loadUi
import numpy as np
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import *
from PyQt5.QtGui import * 
from PyQt5.QtCore import *
import threading
from PyQt5.QtWidgets import QApplication,QWidget,QTextEdit,QVBoxLayout,QPushButton
import sys
'''
QtGui:多种基本图形功能的类,窗口,事件处理
QtWidgets:包含了一整套ui元素组件,用于建立符合系统分割的classic界面
QtCore:包含核心的非gui功能,涉及到time,文件,目录,数据类型,文本,连接,mime,线程或者进程等对象
'''
#需要杀死的进程
kill_name = ["li4_node"]

class SubscriberNode(Node):  #订阅者节点创建
    
    def __init__(self, name):
        super().__init__(name)
        self.sub=self.create_subscription(String, "cha", self.listener_callback, 20)
        self.data = 'None'
        self.data_len = 0

    def listener_callback(self, msg):
   
        self.data = msg.data
        self.data_len = len(self.data)
        


class TextEditDemo(QWidget):
    def __init__(self,parent=None):
        super(TextEditDemo, self).__init__(parent)
        self.setWindowTitle('QTextEdit 例子')

        #定义窗口的初始大小
        self.resize(300,270)
        #创建多行文本框
        self.textEdit=QTextEdit()
        self.textEdit.ensureCursorVisible()        # 确保光标是在必要时滚动文本编辑可见
        self.textEdit.setLineWrapColumnOrWidth(800)# 指定行宽
        self.textEdit.setLineWrapMode(QTextEdit.FixedPixelWidth) #设置QTextEdit按行显示,一条信息只显示一行
        self.textEdit.setFixedWidth(400)           # 文本输出界面宽
        self.textEdit.setFixedHeight(200)          # 文本输出界面高
        self.textEdit.move(30, 50)
        #创建三个按钮
        self.btnPress0=QPushButton('打开节点')
        self.btnPress1=QPushButton('显示文本')
        self.btnPress2=QPushButton('关闭文本')
        self.btnPress3=QPushButton('关闭节点')

        #实例化垂直布局
        layout=QVBoxLayout()
        #相关控件添加到垂直布局中
        layout.addWidget(self.textEdit)
        layout.addWidget(self.btnPress0)           # 启动发布者节点 
        layout.addWidget(self.btnPress1)           # 打印文本
        layout.addWidget(self.btnPress2)           # 结束打印(但不会关掉节点)
        layout.addWidget(self.btnPress3)           # 关掉节点
        

        #设置布局
        self.setLayout(layout)

        self.timer = QTimer(self)                 # 创建timer实例对象

        #将按钮的点击信号与相关的槽函数进行绑定,点击即触发
        self.btnPress0.clicked.connect(self.btnPress0_clicked)
        self.btnPress1.clicked.connect(self.btnPress1_clicked)
        self.btnPress2.clicked.connect(self.btnPress2_clicked)
        self.btnPress3.clicked.connect(self.btnPress3_clicked)

        #在界面主线程中开启守护线程启动节点
        self.node = None
        t_ = threading.Thread(target=self.thread_ros, args=())
        t_.setDaemon(True)                        # 主线程崩掉,守护线程可以自己shutdown
        t_.start()

    def thread_ros(self):
        rclpy.init(args=None)                     # 初始化python接口函数
        self.node = SubscriberNode("wang2")
        rclpy.spin(self.node)
        self.node.destroy_node()
        rclpy.shutdown()

    def btnPress0_clicked(self):
        ROS_PROGRAM = QProcess(self)
        self.textEdit.setPlainText("launching")
        program = 'ros2 launch ./src/village_li/village_li/launch.py'
        ROS_PROGRAM.start(program)

    def btnPress1_clicked(self):
        #以文本的形式输出到多行文本框
        self.setup_ui()                           # 打开定时器
        self.textEdit.setPlainText("开始打印:")

    def btnPress2_clicked(self):
        #以文本的形式输出到多行文本框
        self.timer.stop()                         # 关闭定时器
        self.textEdit.setPlainText("已经清零了!")

    def btnPress3_clicked(self):                  # 关闭节点

        global kill_name
        
        self.kill_pid(kill_name)
    
    def kill_pid(self,name):
        '''
        作用:根据进程名获取进程pid,然后杀死进程
        '''
        pids = psutil.process_iter()

        for pid in pids:
            for i in range(0,len(name)):
                if(pid.name() == name[i]):
                    os.kill((pid.pid), signal.SIGTERM)

    def setup_ui(self):
        self.timer = QTimer()                     # 点击一次就初始化一次
        self.timer.start(500)                     # 设置计时间隔并启动;单位毫秒
        self.timer.timeout.connect(self.operate)  # 每次计时到时间时发出信号
        

    def operate(self):

        self.textEdit.append(self.node.data + '\t' + str(self.node.data_len))               

        self.textEdit.moveCursor(QTextCursor.End)# 确保光标是在必要时滚动文本编辑可见


def main(args=None):

    app=QApplication(sys.argv)
    win=TextEditDemo()
    win.show()
    sys.exit(app.exec_())



    

    

    
    
    


    
    
    
    


    

setup.py

from setuptools import setup

package_name = 'village_li'

setup(
    name=package_name,
    version='0.0.0',
    packages=[package_name],
    data_files=[
        ('share/ament_index/resource_index/packages',
            ['resource/' + package_name]),
        ('share/' + package_name, ['package.xml']),
    ],
    install_requires=['setuptools'],
    zip_safe=True,
    maintainer='wxy',
    maintainer_email='wxy@todo.todo',
    description='TODO: Package description',
    license='TODO: License declaration',
    tests_require=['pytest'],
    entry_points={
        'console_scripts': [
        
            "li4_node = village_li.li4:main",   #village_li:代表文件夹的名字 li4:代表文件的名字
            "wang2_node = village_li.wang2:main",

            
        ],
    },

)

  • 0
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值