## For instruction on writing tutorials ## http://www.ros.org/wiki/WritingTutorials #################################### ##FILL ME IN #################################### ## for a custom note with links: ## note = This tutorial assumes you are comfortable with using roscpp, and have gone through the [[ROS/Tutorials/CreatingPackage]], [[ROS/Tutorials/WritingPublisherSubscriber(c++)]] and [[pluginlib/Tutorials/Writing and Using a Simple Plugin]] ## for the canned note of "This tutorial assumes that you have completed the previous tutorials:" just add the links ## note.0= ## descriptive title for the tutorial ## title = Writing a C++ Plugin ## multi-line description to be displayed in search ## description = Shows how to write a plugin for [[rqt]] in C++. ## the next tutorial description (optional) ## next = ## links to next tutorial (optional) ## next.0.link= [[rqt/Tutorials/Using .ui file in rqt plugin]] ## next.1.link= [[rqt/Tutorials/Create rqt plugin using an existing Qt based tool]] ## what level user is this tutorial for ## level= IntermediateCategory ## keywords = rqt tutorial #################################### <> <> == Intro == This tutorial will show you how to write a C++ plugin to integrate a custom user interface into rqt. You're expected to come from [[rqt/Tutorials/Create your new rqt plugin|Create your new rqt plugin]] tutorial. Complete example of all files in a package is available on [[https://github.com/lucasw/rqt_mypkg/tree/master/rqt_example_cpp|github]]. If you are using QtCreator you can generate the skeleton code (described below) for the plugin [[https://github.com/martonmiklos/rqt_plugin_wizard_for_qtcreator|with this rqt plugin wizard]]. Particularly in `CMakeLists.txt` you'll see `C++` specifics. Refer to existing plugins that are written in `C++` [[https://github.com/ros-visualization/rqt_common_plugins/tree/groovy-devel/rqt_image_view|rqt_image_view]], [[https://github.com/ros-visualization/rqt_robot_plugins/tree/groovy-devel/rqt_rviz|rqt_rviz]] to get a better idea. == Create plugin description file == Basically the same step described in [[rqt/Tutorials/Create your new rqt plugin#Create_plugin.xml_file|previous tutorial]]. Some unique cases: * Replace `rqt_gui_py` with `rqt_gui_cpp` * `C++` binding specific note for xml attribute: `/library/class@name` . The name of the plugin that is exported via the `PLUGINLIB_DECLARE_CLASS` macro. == The code == Create the src/my_namespace/my_plugin.h file and paste the following inside it: {{{#!cplusplus block=header #ifndef my_namespace__my_plugin_H #define my_namespace__my_plugin_H #include #include #include namespace my_namespace { class MyPlugin : public rqt_gui_cpp::Plugin { Q_OBJECT public: MyPlugin(); virtual void initPlugin(qt_gui_cpp::PluginContext& context); virtual void shutdownPlugin(); virtual void saveSettings(qt_gui_cpp::Settings& plugin_settings, qt_gui_cpp::Settings& instance_settings) const; virtual void restoreSettings(const qt_gui_cpp::Settings& plugin_settings, const qt_gui_cpp::Settings& instance_settings); // Comment in to signal that the plugin has a way to configure it //bool hasConfiguration() const; //void triggerConfiguration(); private: Ui::MyPluginWidget ui_; QWidget* widget_; }; } // namespace #endif // my_namespace__my_plugin_H }}} Create the src/my_namespace/my_plugin.cpp file and paste the following inside it: {{{#!cplusplus block=cppcode #include "my_plugin.h" #include #include namespace my_namespace { MyPlugin::MyPlugin() : rqt_gui_cpp::Plugin() , widget_(0) { // Constructor is called first before initPlugin function, needless to say. // give QObjects reasonable names setObjectName("MyPlugin"); } void MyPlugin::initPlugin(qt_gui_cpp::PluginContext& context) { // access standalone command line arguments QStringList argv = context.argv(); // create QWidget widget_ = new QWidget(); // extend the widget with all attributes and children from UI file ui_.setupUi(widget_); // add widget to the user interface context.addWidget(widget_); } void MyPlugin::shutdownPlugin() { // TODO unregister all publishers here } void MyPlugin::saveSettings(qt_gui_cpp::Settings& plugin_settings, qt_gui_cpp::Settings& instance_settings) const { // TODO save intrinsic configuration, usually using: // instance_settings.setValue(k, v) } void MyPlugin::restoreSettings(const qt_gui_cpp::Settings& plugin_settings, const qt_gui_cpp::Settings& instance_settings) { // TODO restore intrinsic configuration, usually using: // v = instance_settings.value(k) } /*bool hasConfiguration() const { return true; } void triggerConfiguration() { // Usually used to open a dialog to offer the user a set of configuration }*/ } // namespace PLUGINLIB_DECLARE_CLASS(my_namespace, MyPlugin, my_namespace::MyPlugin, rqt_gui_cpp::Plugin) }}} This simple plugin only adds a single widget using `addWidget`. But a plugin can contribute multiple widgets by calling that method for each widget. The method can be called at any time to dynamically contribute more widgets. Internally each widget is embedded into a new QDockWidget which itself is added to the QMainWindow. === Using a UI file === The example uses an Qt Designer UI file to describe the widget tree. Instead of hand-coding the widget tree the Qt meta object compiler generates a header file at compile time which includes all the widgets based on the description from the file. For more detail, see [[rqt/Tutorials/Using .ui file in rqt plugin]] == Special note for using roscpp with rqt == The plugin should not call `init_node` as this is performed by `rqt_gui_cpp`. The plugin can use any [[roscpp]]-specific functionality (like Publishers, Subscribers, Parameters). Just make sure to stop timers and publishers, unsubscribe from Topics etc in the shutdown_plugin method. A C++ plugin inherits from [[nodelet|Nodelet]] and therefore gains the same advantages (i.e. enables exchanging boost>::shared_pointer). Due to restrictions in Qt, you cannot manipulate Qt widgets directly within ROS callbacks, because they are running in a different thread. In the ROS callback you can: * emit a Qt signal (which will bridge across the threads) and manipulate the widgets in the receiving slot OR * only operate on non-widget entities like `QAbstractItemModels` ## AUTOGENERATED DO NOT DELETE ## TutorialCategory ## FILL IN THE STACK TUTORIAL CATEGORY HERE