我是 ROS 2 的新手,我正在尝试使用 RQT 插件创建一个简单的 GUI。为此,我遵循本指南中描述的流程:
https://robobe.github.io/blog/ROS2/rqt/custom_plugin/step1/
请注意,我使用的是 ros2 iron 和 Python 3.10.12,操作系统是 Linux Mint。
目前项目结构如下:
~/ros2_custom_rqt_plugin $ tree
.
└── src
└── rqt_mypkg
├── LICENSE
├── package.xml
├── plugin.xml
├── resource
│ └── rqt_mypkg
├── rqt_mypkg
│ ├── __init__.py
│ └── my_module.py
├── setup.cfg
├── setup.py
└── test
├── test_copyright.py
├── test_flake8.py
└── test_pep257.py
5 directories, 11 files
在 my_module.py 文件中,我有以下内容:
#!/usr/bin/env python3
# File: rqt_mypkg/rqt_mypkg/my_module.py
from rqt_gui_py.plugin import Plugin
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton, QLabel
class MyPlugin(Plugin):
def __init__(self, context):
super(MyPlugin, self).__init__(context)
self.setObjectName('MyPlugin')
# GUI initialization code
self._widget = MyWidget()
self._widget.setObjectName('UniqueWidget')
self.setWidget(self._widget)
class MyWidget(QWidget):
def __init__(self):
super(MyWidget, self).__init__()
# Create a layout
layout = QVBoxLayout(self)
# Create a button
self.button = QPushButton('Click Me!', self)
self.button.clicked.connect(self.on_button_click)
# Create a label for text display
self.label = QLabel('Hello, World!', self)
# Add the button and label to the layout
layout.addWidget(self.button)
layout.addWidget(self.label)
def on_button_click(self):
self.label.setText('Button Clicked!')
在此代码中,MyPlugin 类应该继承自 rqt_gui_py.Plugin 并创建 MyWidget 类的实例。
在 package.xml 文件中,我有:
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>rqt_mypkg</name>
<version>0.0.0</version>
<description>My RQT plugin</description>
<maintainer email="[email protected]">Sam</maintainer>
<license>Apache License 2.0</license>
<depend>rclpy</depend>
<depend>rqt_gui</depend>
<depend>rqt_gui_py</depend>
<test_depend>ament_copyright</test_depend>
<test_depend>ament_flake8</test_depend>
<test_depend>ament_pep257</test_depend>
<test_depend>python3-pytest</test_depend>
<export>
<build_type>ament_python</build_type>
<rqt_gui plugin="${prefix}/plugin.xml"/>
</export>
</package>
与链接指南保持一致,我在
<rqt_gui plugin="${prefix}/plugin.xml"/>
标签内添加了 <export>
行。
在plugin.xml中我有:
<library path="src">
<class name="My Plugin" type="rqt_mypkg.my_module.MyPlugin" base_class_type="rqt_gui_py::Plugin">
<description>
An example Python GUI plugin to create a great user interface.
</description>
<qtgui>
<group>
<label>Visualization</label>
</group>
<label>My first Python Plugin</label>
<icon type="theme">system-help</icon>
<statustip>Great user interface to provide real value.</statustip>
</qtgui>
</class>
</library>
此代码与链接指南中提供的示例完全相同。
在 setup.py 中我有:
from setuptools import find_packages, setup
package_name = 'rqt_mypkg'
setup(
name=package_name,
version='0.0.0',
packages=find_packages(exclude=['test']),
data_files=[
('share/ament_index/resource_index/packages',
['resource/' + package_name]),
('share/' + package_name, ['package.xml', 'plugin.xml']),
],
install_requires=['setuptools'],
zip_safe=True,
maintainer='sam',
maintainer_email='[email protected]',
description='TODO: Package description',
license='Apache-2.0',
tests_require=['pytest'],
entry_points={
'console_scripts': [
],
},
)
值得注意的是,我修改了 data_files 部分以包含plugin.xml
为了构建项目并加载插件,我尝试执行以下命令:
~/ros2_custom_rqt_plugin $ colcon build
~/ros2_custom_rqt_plugin $ source install/setup.bash
~/ros2_custom_rqt_plugin $ rqt --force-discover --standalone rqt_mypkg
该项目似乎构建没有问题,但是当我尝试加载插件时,我得到以下信息:
~/ros2_custom_rqt_plugin $ rqt --force-discover --standalone rqt_mypkg
PluginManager._load_plugin() could not load plugin "rqt_mypkg/My Plugin":
Traceback (most recent call last):
File "/opt/ros/iron/lib/python3.10/site-packages/qt_gui/plugin_handler.py", line 102, in load
self._load()
File "/opt/ros/iron/lib/python3.10/site-packages/qt_gui/plugin_handler_direct.py", line 55, in _load
self._plugin = self._plugin_provider.load(self._instance_id.plugin_id, self._context)
File "/opt/ros/iron/lib/python3.10/site-packages/qt_gui/composite_plugin_provider.py", line 72, in load
instance = plugin_provider.load(plugin_id, plugin_context)
File "/opt/ros/iron/lib/python3.10/site-packages/qt_gui/composite_plugin_provider.py", line 72, in load
instance = plugin_provider.load(plugin_id, plugin_context)
File "/opt/ros/iron/lib/python3.10/site-packages/rqt_gui_py/ros_py_plugin_provider.py", line 69, in load
return super(RosPyPluginProvider, self).load(plugin_id, ros_plugin_context)
File "/opt/ros/iron/lib/python3.10/site-packages/qt_gui/composite_plugin_provider.py", line 72, in load
instance = plugin_provider.load(plugin_id, plugin_context)
File "/opt/ros/iron/lib/python3.10/site-packages/rqt_gui/ros_plugin_provider.py", line 107, in load
return class_ref(plugin_context)
File "/home/sam/ros2_custom_rqt_plugin/install/rqt_mypkg/lib/python3.10/site-packages/rqt_mypkg/my_module.py", line 16, in __init__
self.setWidget(self._widget)
AttributeError: 'MyPlugin' object has no attribute 'setWidget'
Failed to delete datawriter, at ./src/publisher.cpp:45 during '__function__'
Warning: class_loader.ClassLoader: SEVERE WARNING!!! Attempting to unload library while objects created by this loader exist in the heap! You should delete your objects before attempting to unload the library or destroying the ClassLoader. The library will NOT be unloaded.
at line 127 in ./src/class_loader.cpp
从这条消息来看,由于属性错误,插件无法加载:
AttributeError: 'MyPlugin' object has no attribute 'setWidget'
我原以为setWidget属性是rqt_gui_py.plugin的一部分,所以MyPlugin类应该能够访问它。我可以进行哪些更改才能正确加载插件?
更新:我可以通过将以下内容放入 my_modules.py 来使插件正常工作
#!/usr/bin/env python3
# File: rqt_mypkg/rqt_mypkg/my_module.py
from rqt_gui_py.plugin import Plugin
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton, QLabel
class MyPlugin(Plugin):
def __init__(self, context):
super(MyPlugin, self).__init__(context)
self.setObjectName('MyPlugin')
# GUI initialization code
self._widget = MyWidget()
print("MyWidget instance created")
self._widget.show()
class MyWidget(QWidget):
def __init__(self):
super(MyWidget, self).__init__()
print("MyWidget constructor called...")
self.setStyleSheet("background-color: white;")
# Create a layout
layout = QVBoxLayout(self)
# Create a button
self.button = QPushButton('Click Me!', self)
self.button.clicked.connect(self.on_button_click)
# Create a label for text display
self.label = QLabel('Hello, World!', self)
# Add the button and label to the layout
layout.addWidget(self.button)
layout.addWidget(self.label)
# Set the layout for the widget
self.setLayout(layout)
def on_button_click(self):
self.label.setText('Button Clicked!')