Note: This tutorial assumes you are comfortable with using rospy, and have gone through the ROS/Tutorials/CreatingPackage and ROS/Tutorials/WritingPublisherSubscriber(python). Also assumes you already have QWidget-based GUI that you want to integrate into ROS by using rqt. |
Please ask about problems and questions regarding this tutorial on answers.ros.org. Don't forget to include in your question the link to this page, the versions of your OS & ROS, and also add appropriate tags. |
Create your rqt plugin package
Description: Entry point for creating your rqt plugin either in python or C++.Tutorial Level: BEGINNER
Next Tutorial: rqt/Tutorials/Writing a Python Plugin rqt/Tutorials/Writing a C++ Plugin
Contents
Intro
This tutorial will show you how to create a plugin to integrate your custom user interface into ROS' GUI framework rqt.
When you want to run your code after writing it, refer to rqt/UserGuide about how to run and so on.
A complete set of all files in this tutorial using python is available ongithub. These rqt plugins are also useful because they bring up than an empty widget. For example rqt_bag.
Refer to the Usability Resources for design guidelines.
Prerequisite & assumption
- Have QWidget-based GUI that you want to integrate into ROS by using rqt.
Assumes ROS groovy or later, and catkin as a buildsystem by default.
For fuerte, this thread might be of your help.
- This tutorial is written based on Ubuntu 12.10 (initiated on 3/12/2013)
Examples in this page use python. With C++, replace rqt_gui_py with rqt_gui_cpp
Steps to create rqt plugin pkg
Create an Empty Package
Before getting started, let's create an empty package to called rqt_mypkg, somewhere in your package path.:
roscreate-pkg rqt_mypkg rospy rqt_gui rqt_gui_py
catkin_create_pkg rqt_mypkg rospy rqt_gui rqt_gui_py
Modify package.xml
Add export tag
So your plugin can be discovered, you must declare the plugin in either package.xml for catkin, or manifest.xml for rosbuild:
1 <package>
2 :
3 <!-- all the existing tags -->
4 <export>
5 <rqt_gui plugin="${prefix}/plugin.xml"/>
6 </export>
7 :
8 </package>
Complete `package.xml` example.
Remove build_depend (Optional)
If you're writing your plugin in python that doesn't require building, you can omit build_depend tags.
Create plugin.xml file
Then you create the referenced file plugin.xml with additional meta information regarding the plugin:
1 <library path="src">
2 <class name="My Plugin" type="rqt_mypkg.my_module.MyPlugin" base_class_type="rqt_gui_py::Plugin">
3 <description>
4 An example Python GUI plugin to create a great user interface.
5 </description>
6 <qtgui>
7 <!-- optional grouping...
8 <group>
9 <label>Group</label>
10 </group>
11 <group>
12 <label>Subgroup</label>
13 </group>
14 -->
15 <label>My first Python Plugin</label>
16 <icon type="theme">system-help</icon>
17 <statustip>Great user interface to provide real value.</statustip>
18 </qtgui>
19 </class>
20 </library>
Consider the available plugins and their grouping when deciding where to place your plugin.
Attributes of library element in plugin.xml
Usually you can just copy the example and modify wherever you feel necessary to get the plugin to work. Here are some explanations about xml attributes of the library element for those who need to know more.
/library@path
- The package-relative path which gets added to sys.path.
/library/class@name
- The name of the plugin, which must be unique within a package.
/library/class@type
- The concatenated name of the package, module and class, which is used for the import statement. (Form: package.module.class)
/library/class@base_class_type
For Python plugins which use the rospy client library the value is rqt_gui_py::Plugin.
/library/description
- The description of the plugin.
/library/qtgui
- This tag contains additional optional information about the package. If none are provided, the name of the plugin is used as the label for the plugin in the menu and the description is displayed as a status tip.
/library/qtgui/group (optional, multiple)
Enables grouping of plugins. Group tags can contain a label, icon and statustip tag. The groups form a hierarchy of menus where the plugin is added as the leaf. The groups of different plugins are merged based on their label (icons and statustip may be overridden by other plugins when they are defined differently).
/library/qtgui/label
- Overrides the label with which the plugin appears in the menu.
/library/qtgui/icon
- Defines the icon that should be shown beside the label (depending on the type of attribute).
/library/qtgui/icon@type
file (default): the icon value is a package-relative path to an image.
theme: the icon value names an icon defined by the Icon Naming Specification.
resource: the icon value names a Qt resource.
/library/qtgui/statustip
- Overrides the status tip that is shown when hovering over the plugin label.
Write a plugin code
Writing code is explained on separate pages for python | C++ respectively.
Coding rule for rqt
Mostly follow the general ROS coding style guide C++ | Python
- List dependency, import in an alphabetical order
rqt in python defines some rules
Also, python in rqt defines documenting rules here.
Choice of programming language in rqt
Mainly because of the ease of maitainance, many rqt plugins are written in python, and it is strongly recommended for new plugins to be written in python. C++ is completely acceptable in rqt. Only if:
you need the extra performance of C++ or want to access libraries only available in C++ (i.e. rqt_image_view)
you are far more comfortable with C++ than Python
You can find out in which language the existing rqt plugins are written at rqt/Plugins page. Go to the page of each plugin and find its source repository where you can look at the source code.
Install & Run your plugin
See the Running rqt section for how to run your plugin.
With catkin, no matter which method in the link above you want to run your plugin, you need to install it via CMake which puts the script into a package-specific folder which is not on the PATH.
Add macros to your setup.py (reference). For example, after adding the line the section that contains it might look like :
from distutils.core import setup from catkin_pkg.python_setup import generate_distutils_setup d = generate_distutils_setup( packages=['rqt_mypkg'], package_dir={'': 'src'}, ) setup(**d)
Also make sure in your CMakeLists.txt, to uncomment a line:
catkin_python_setup()
Add install macro that puts the script into a location where it is rosrun-able is declared. For example:
install(PROGRAMS scripts/rqt_mypkg DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} )
Add the following lines to call the resource and plugin.xml
install(DIRECTORY resource DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} ) install(FILES plugin.xml DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} )
These scripts can be run by:
$ rosrun rqt_mypkg rqt_mypkg
Detail for rosbuild is TBD. Ask maintainers if you need to know immediately, or feel free to modify this wiki once you figure it out..
A very short description how to install your plugin in fuerte (because catkin does not work very well in fuerte) can be found at: http://answers.ros.org/question/65679/install-rqt-plugin-with-rosbuild/?answer=65773#post-id-65773
If your plugin does not show up under the Plugins menu when you launch rqt, you may need to run:
rqt --force-discover
Option
Do not forget to add enough info into package.xml, just as with every ROS package.
Unit testing rqt plugins
Needless to say, making & maintaining unit test codes is strongly recommended, but not required.
Unit test codes can be best stored under /test folder on the root directory of a package.
Area covered by unit testing can only be business & application logic. There seems to be no convenient way to do unit test for visual components (ref).
If you have better idea, please open a discussion in rqt community. This is very interesting topic.
For integrated test, using rosunit is highly recommended.
To run your plugin directly
You can add your rqt plugin to the system PATH so that you can run it on-the-fly without using other tools such as rosrun, rqt_gui and so on. It is not, however, recommended to put it to PATH in order to keep system common space cleaner. Only if you dare to do so, there's a way.
Running custom rqt plugins directly is NOT recommended (discussion 1, 2, 3). Do this only when you're really in need.
Add 1 line:
scripts=['%RELATIVE_PATH_TOYOUR_EXECUTABLE%']
to your setup.py (reference). For example after adding the line the section that contains it might look like :
d = generate_distutils_setup( packages=['rqt_mypkg'], package_dir={'': 'src'}, scripts=['scripts/rqt_mypkg'] )
Once you're done, run:
$ cd %TOPDIR_YOUR_CATKIN_WS% $ catkin_make
This will yield a relay script to %TOPDIR_YOUR_CATKIN_WS%devel/bin, which you can call if you're already sourced %TOPDIR_YOUR_CATKIN_WS%devel/setup.bash% (or similar, as you wish).
This is not fully supported with rosbuild.
Practices to follow on making rqt plugins
- Same as general GUI development, you should pay attention to from which thread you're updating GUI.
Although nodes that are instantiated from rqt plugins run as different thread in the same process, callback function that is given to node handler (NodeHandle in C++ / rospy.Subscriber (for example)) runs in the main thread, which enables to update GUI from there.
Apply common GUI software architecture (eg. MVC)
For the same reason, you should ideally separate Plugin class (rqt_gui_cpp::Plugin or rqt_gui_py.plugin.Plugin) and your widgets' implementation.