/rqt_mypkg

rqtプラグインのチュートリアルをやってみた。

Primary LanguagePython

Rqt Plugin Tutorial Example

rqtプラグインを作ってみようと思ってチュートリアルを参考にやったことまとめ。

作り方

空のpkgを作る

ここではrqt_mypkgというpluginを作る

$ cd <catkin_ws>
$ catkin_create_pkg rqt_mypkg rospy rqt_gui rqt_gui_py

package.xmlを変更する

$ cd <catkin_ws>/rqt_mypkg
$ emacs package.xml

package.xmlexportタグに以下のように追記する。

<package>
	:
	<!-- all the existing tags -->
	<export>
		<rqt_gui plugin="${prefix}/plugin.xml" />
	</export>
</package>

ちなみに、build_dependタグはPythonを使っているなら消してもいいよ。

plugin.xmlを作成する

$ cd <catkin_ws>/rqt_mypkg
$ emacs plugin.xml

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>Group</label>
      </group>
      <group>
	<label>Subgroup</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>

大体のタグは見たら分かると思う。(詳しい説明はref.[1]の4.3.1節を参照)
ちなみにGroupタグとSubgroupタグは名前は何でも良くて、これはpluginをrqtのプルダウンメニューから選択して起動するときのフォルダ分け。

Pluginのコードを書く

PluginはC++でも書けるみたいだけど、特別な理由が無い限り、Pythonで書くことがおすすめされている。特別な理由というのはC++でしかlibraryにアクセスする方法が無いだとか、Pythonよりも快適(たぶん性能)なとき。ちなみにこれはPythonを想定している。

$ cd <catkin_ws>/rqt_mypkg
$ mkdir -p src/rqt_mypkg
$ cd src/rqt_mypkg
$ touch __init__.py

__init__.pyの中身は空で問題無い。何で必要かとかは調べる。
次にスクリプト本体を書く。
さっきの__init__.pyと同じディレクトリで

$ emacs my_module.py

my_module.pyに以下をコピペする。

import os
import rospy
import rospkg

from qt_gui.plugin import Plugin
from python_qt_binding import loadUi
from python_qt_binding.QtGui import QWidget

class MyPlugin(Plugin):

    def __init__(self, context):
        super(MyPlugin, self).__init__(context)
        # Give QObjects reasonable names
        self.setObjectName('MyPlugin')

        # Process standalone plugin command-line arguments
        from argparse import ArgumentParser
        parser = ArgumentParser()
        # Add argument(s) to the parser
        parser.add_argument("-q", "--quiet", action="store_true",
                            dest="quiet",
                            help="Put plugin in silent mode")
        args, unknowns = parser.parse_known_args(context.argv())
        if not args.quiet:
            print 'arguments: ', args
            print 'unknowns: ', unknowns

        # Create QWidget
        self._widget = QWidget()
        # Get path to UI file which should be in the "resource" folder of this package
        ui_file = os.path.join(rospkg.RosPack().get_path('rqt_mypkg'), 'resource', 'MyPlugin.ui')
        # Extend the widget with all atrributes and children from UI File
        loadUi(ui_file, self._widget)
        # Give QObjects reasonable names
        self._widget.setObjectName('MyPluginUi')
        # Show _widget.windowTitle on left-top of each plugin(when it's set in _widget).
        # This is useful when you open multiple plugins aat once. Also if you open multiple
        # instances of your plugin at once, these lines add number to make it easy to
        # tell from pane to pane.
        if context.serial_number() > 1:
            self._widget.setWindowTitle(self._widget.windowTitle() + (' %d' % context.serial_number()))
        # Add widget to the user interface
        context.add_widget(self._widget)

    def shutdown_plugin(self):
        # TODO unregister all publishers here
        pass

    def save_settings(self, plugin_settings, instance_settings):
        # TODO save intrinsic configuration, usually using:
        # instance_settings.get_value(k, v)
        pass

    def restore_settings(self, pluign_settings, instance_settings):
        # TODO restore intrinsic configuration, usually using:
        # v = instance_settings.value(k)
        pass

OKボタンとCancelボタンがあってそれぞれクリックしたらコマンドラインにprintが実行される。 ちなみに上記のコードはref.[2]にある。
このリポジトリにあるコードはチュートリアルのコードに若干の追記がしてある。

UIを用意する

$ cd <catkin_ws>/rqt_mypkg
$ mkdir resource

[WIP]

コマンドラインから

$ designer

と入力してQt Designerを起動する。
適当にデザインする。
参考:http://densan-labs.net/tech/qt/chapter5.html
保存する。
出来上がったファイルの4行目あたりのclass="hogehoge"を以下のように変更する。

<widget class="QWidget" name="B3m">

resourceディレクトリの中にMyPlugin.uiを用意する。適当なpluginから持ってきてやればとりあえずは起動まで出来る。 例えば
https://github.com/ros-visualization/rqt_common_plugins/blob/efc7c7e30533c598869943fb400a5744c7d30881/rqt_service_caller/resource/ServiceCaller.ui
とか。

setup.pyを用意する

rqt_pluginを実行する方法はいくつかある。例えば、rosrun hogehoge hugahugahogehoge、rqtのメニューから起動するなど。しかし、どのような方法で実行したいにしてもパスが通ってないと実行できない。

pkg直下にsetup.pyを用意する。

$ cd <catkin_ws>/rqt_mypkg
$ emacs setup.py

以下の内容をコピペする。

# -*- coding: utf-8 -*-
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)

setup.pyについてはref.[3]を参照すること。

CMakeLists.txtを修正する

catkin_create_pkgを実行した際に作成されたCMakeLists.txtの中身を少し、修正する。

  • まず、20行目のcatkin_python_setup()のコメントアウトを外す。

  • 次に、151行目あたりのinstall(hogehoge)のコメントアウトを外す。

以下のようになる。

## Mark executable scripts (Python etc.) for installation
## in contrast to setup.py, you can choose the destination
install(PROGRAMS
  scripts/rqt_mypkg
  DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)

上記でscripts/rqt_mypkgとなっているのは問題ないのかな…

catkin_makeする

$ cd <catkin_ws>
$ catkin_make
$ source devel/setup.bash[zsh]

実行してみる

$ rqt --standalone rqt_mypkg

もし、次のようなエラーが出れば

> qt_gui_main() found no plugin matching "rqt_mypkg"
$ rqt --force-discover

を実行して、メニューから呼び出す。 ref[4]によれば、rqtは起動を速くするために前回の起動から24時間以内ならキャッシュしたpluginリストを見に行くらしい。なので、見つからないということがおこる(要出典)。

ビルドエラーがなければ、rqtでプラグインを実行できる。
多分、MyPlugin.uiが無いと言ってエクセプションするけど、どこかのpluginから持ってくればよい。

Reference

[1] http://wiki.ros.org/rqt/Tutorials/Create%20your%20new%20rqt%20plugin
[2] http://wiki.ros.org/rqt/Tutorials/Writing%20a%20Python%20Plugin
[3] http://docs.ros.org/groovy/api/catkin/html/user_guide/setup_dot_py.html
[4] ros-visualization/rqt#90