plugins.py
Go to the documentation of this file.
00001 # SimpleRobotSteeringPlugin plugin based on rqt_robot_steering. Original
00002 # copyright notice below.
00003 #
00004 # Copyright (c) 2011, Dirk Thomas, TU Darmstadt
00005 # All rights reserved.
00006 #
00007 # Redistribution and use in source and binary forms, with or without
00008 # modification, are permitted provided that the following conditions
00009 # are met:
00010 #
00011 #   * Redistributions of source code must retain the above copyright
00012 #     notice, this list of conditions and the following disclaimer.
00013 #   * Redistributions in binary form must reproduce the above
00014 #     copyright notice, this list of conditions and the following
00015 #     disclaimer in the documentation and/or other materials provided
00016 #     with the distribution.
00017 #   * Neither the name of the TU Darmstadt nor the names of its
00018 #     contributors may be used to endorse or promote products derived
00019 #     from this software without specific prior written permission.
00020 #
00021 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00022 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00023 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00024 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00025 # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00026 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00027 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00028 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00029 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00030 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00031 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00032 # POSSIBILITY OF SUCH DAMAGE.
00033 
00034 from __future__ import division
00035 
00036 import math
00037 import rospy
00038 import time
00039 
00040 from bwi_msgs.srv import QuestionDialog, QuestionDialogResponse, \
00041                          QuestionDialogRequest
00042 from functools import partial
00043 from qt_gui.plugin import Plugin
00044 from python_qt_binding.QtGui import QFont, QGridLayout, QLabel, QLineEdit, \
00045                                     QPushButton, QTextBrowser, QVBoxLayout, QWidget
00046 
00047 import os
00048 import rospkg
00049 
00050 from geometry_msgs.msg import Twist
00051 from python_qt_binding import loadUi
00052 from python_qt_binding.QtCore import Qt, QTimer, SIGNAL, Slot
00053 
00054 class QuestionDialogPlugin(Plugin):
00055 
00056     def __init__(self, context):
00057         super(QuestionDialogPlugin, self).__init__(context)
00058         # Give QObjects reasonable names
00059         self.setObjectName('QuestionDialogPlugin')
00060 
00061         font_size = rospy.get_param("~font_size", 40)
00062 
00063         # Create QWidget
00064         self._widget = QWidget()
00065         self._widget.setFont(QFont("Times", font_size, QFont.Bold))
00066         self._layout = QVBoxLayout(self._widget)
00067         self._text_browser = QTextBrowser(self._widget)
00068         self._layout.addWidget(self._text_browser)
00069         self._button_layout = QGridLayout()
00070         self._layout.addLayout(self._button_layout)
00071 
00072         # layout = QVBoxLayout(self._widget)
00073         # layout.addWidget(self.button)
00074         self._widget.setObjectName('QuestionDialogPluginUI')
00075         if context.serial_number() > 1:
00076             self._widget.setWindowTitle(self._widget.windowTitle() +
00077                                         (' (%d)' % context.serial_number()))
00078         context.add_widget(self._widget)
00079 
00080         # Setup service provider
00081         self.service = rospy.Service('question_dialog', QuestionDialog,
00082                                      self.service_callback)
00083         self.response_ready = False
00084         self.response = None
00085         self.buttons = []
00086         self.text_label = None
00087         self.text_input = None
00088 
00089         self.connect(self._widget, SIGNAL("update"), self.update)
00090         self.connect(self._widget, SIGNAL("timeout"), self.timeout)
00091 
00092     def shutdown_plugin(self):
00093         self.service.shutdown()
00094 
00095     def service_callback(self, req):
00096         self.response_ready = False
00097         self.request = req
00098         self._widget.emit(SIGNAL("update"))
00099         # Start timer against wall clock here instead of the ros clock.
00100         start_time = time.time()
00101         while not self.response_ready:
00102             if self.request != req:
00103                 # The request got preempted by a new request.
00104                 return QuestionDialogResponse(QuestionDialogRequest.PREEMPTED, "")
00105             if req.timeout != QuestionDialogRequest.NO_TIMEOUT:
00106                 current_time = time.time()
00107                 if current_time - start_time > req.timeout:
00108                     self._widget.emit(SIGNAL("timeout"))
00109                     return QuestionDialogResponse(
00110                             QuestionDialogRequest.TIMED_OUT, "")
00111             time.sleep(0.2)
00112         return self.response
00113 
00114     def update(self):
00115         self.clean()
00116         req = self.request
00117         self._text_browser.setText(req.message)
00118         if req.type == QuestionDialogRequest.DISPLAY:
00119             # All done, nothing more too see here.
00120             self.response = QuestionDialogResponse(
00121                     QuestionDialogRequest.NO_RESPONSE, "")
00122             self.response_ready = True
00123         elif req.type == QuestionDialogRequest.CHOICE_QUESTION:
00124             for index, options in enumerate(req.options):
00125                 button = QPushButton(options, self._widget)
00126                 button.clicked.connect(partial(self.handle_button, index))
00127                 row = index / 3
00128                 col = index % 3
00129                 self._button_layout.addWidget(button, row, col)
00130                 self.buttons.append(button)
00131         elif req.type == QuestionDialogRequest.TEXT_QUESTION:
00132             self.text_label = QLabel("Enter here: ", self._widget)
00133             self._button_layout.addWidget(self.text_label, 0, 0)
00134             self.text_input = QLineEdit(self._widget)
00135             self.text_input.editingFinished.connect(self.handle_text)
00136             self._button_layout.addWidget(self.text_input, 0, 1)
00137 
00138     def timeout(self):
00139         self._text_browser.setText("Oh no! The request timed out.")
00140         self.clean()
00141 
00142     def clean(self):
00143         while self._button_layout.count():
00144             item = self._button_layout.takeAt(0)
00145             item.widget().deleteLater()
00146         self.buttons = []
00147         self.text_input = None
00148         self.text_label = None
00149 
00150     def handle_button(self, index):
00151         self.response = QuestionDialogResponse(index, "")
00152         self.clean()
00153         self.response_ready = True
00154 
00155     def handle_text(self):
00156         self.response = QuestionDialogResponse(
00157             QuestionDialogRequest.TEXT_RESPONSE,
00158             self.text_input.text())
00159         self.clean()
00160         self.response_ready = True
00161 
00162     def save_settings(self, plugin_settings, instance_settings):
00163         # TODO save intrinsic configuration, usually using:
00164         # instance_settings.set_value(k, v)
00165         pass
00166 
00167     def restore_settings(self, plugin_settings, instance_settings):
00168         # TODO restore intrinsic configuration, usually using:
00169         # v = instance_settings.value(k)
00170         pass
00171 
00172     #def trigger_configuration(self):
00173         # Comment in to signal that the plugin has a way to configure
00174         # This will enable a setting button (gear icon) in each dock widget title bar
00175         # Usually used to open a modal configuration dialog
00176 
00177 class SimpleRobotSteeringPlugin(Plugin):
00178 
00179     DEFAULT_LINEAR_VELOCITY = 1.0
00180     DEFAULT_ANGULAR_VELOCITY = math.pi / 2
00181 
00182     def __init__(self, context):
00183         super(SimpleRobotSteeringPlugin, self).__init__(context)
00184         self.setObjectName('SimpleRobotSteeringPlugin')
00185 
00186         self._publisher = None
00187 
00188         self._widget = QWidget()
00189         rp = rospkg.RosPack()
00190         ui_file = os.path.join(rp.get_path('bwi_rqt_plugins'), 'resource', 'SimpleRobotSteering.ui')
00191         loadUi(ui_file, self._widget)
00192         self._widget.setObjectName('SimpleRobotSteeringUi')
00193         if context.serial_number() > 1:
00194             self._widget.setWindowTitle(self._widget.windowTitle() + (' (%d)' % context.serial_number()))
00195 
00196         self._widget.keyPressEvent = self.keyPressEvent
00197         self._widget.keyReleaseEvent = self.keyReleaseEvent
00198         context.add_widget(self._widget)
00199 
00200         self._widget.topic_line_edit.textChanged.connect(self._on_topic_changed)
00201 
00202         self.linear_vel = 0
00203         self.angular_vel = 0
00204 
00205         # After doing so, key press events seem to work ok.
00206         self._widget.w_button.setFocus()
00207 
00208         # timer to consecutively send twist messages
00209         self._update_parameter_timer = QTimer(self)
00210         self._update_parameter_timer.timeout.connect(self._on_parameter_changed)
00211         self._update_parameter_timer.start(100)
00212 
00213     @Slot(str)
00214     def _on_topic_changed(self, topic):
00215         topic = str(topic)
00216         self._unregister_publisher()
00217         try:
00218             self._publisher = rospy.Publisher(topic, Twist, queue_size=10)
00219         except TypeError:
00220             self._publisher = rospy.Publisher(topic, Twist)
00221 
00222     def keyPressEvent(self, event):
00223         if not event.isAutoRepeat():
00224             if event.key() == Qt.Key_W:
00225                 self.linear_vel = SimpleRobotSteeringPlugin.DEFAULT_LINEAR_VELOCITY
00226                 self._widget.w_button.setDown(True)
00227                 self._widget.s_button.setDown(False)
00228             elif event.key() == Qt.Key_S:
00229                 self.linear_vel = -SimpleRobotSteeringPlugin.DEFAULT_LINEAR_VELOCITY
00230                 self._widget.s_button.setDown(True)
00231                 self._widget.w_button.setDown(False)
00232             elif event.key() == Qt.Key_A:
00233                 self.angular_vel = SimpleRobotSteeringPlugin.DEFAULT_ANGULAR_VELOCITY
00234                 self._widget.a_button.setDown(True)
00235                 self._widget.d_button.setDown(False)
00236             elif event.key() == Qt.Key_D:
00237                 self.angular_vel = -SimpleRobotSteeringPlugin.DEFAULT_ANGULAR_VELOCITY
00238                 self._widget.d_button.setDown(True)
00239                 self._widget.a_button.setDown(False)
00240 
00241     def keyReleaseEvent(self, event):
00242         if not event.isAutoRepeat():
00243             if self.linear_vel > 0 and event.key() == Qt.Key_W:
00244                 self.linear_vel = 0
00245                 self._widget.w_button.setDown(False)
00246             elif self.linear_vel < 0 and event.key() == Qt.Key_S:
00247                 self.linear_vel = 0
00248                 self._widget.s_button.setDown(False)
00249             elif self.angular_vel > 0 and event.key() == Qt.Key_A:
00250                 self.angular_vel = 0
00251                 self._widget.a_button.setDown(False)
00252             elif self.angular_vel < 0 and event.key() == Qt.Key_D:
00253                 self.angular_vel = 0
00254                 self._widget.d_button.setDown(False)
00255 
00256 
00257     def _on_parameter_changed(self):
00258         self._widget.linear_speed.setText("%.2f"%self.linear_vel)
00259         self._widget.angular_speed.setText("%.2f"%self.angular_vel)
00260         self._send_twist(self.linear_vel, self.angular_vel)
00261 
00262     def _send_twist(self, x_linear, z_angular):
00263         if self._publisher is None:
00264             return
00265         twist = Twist()
00266         twist.linear.x = x_linear
00267         twist.linear.y = 0
00268         twist.linear.z = 0
00269         twist.angular.x = 0
00270         twist.angular.y = 0
00271         twist.angular.z = z_angular
00272 
00273         self._publisher.publish(twist)
00274 
00275     def _unregister_publisher(self):
00276         if self._publisher is not None:
00277             self._publisher.unregister()
00278             self._publisher = None
00279 
00280     def shutdown_plugin(self):
00281         self._update_parameter_timer.stop()
00282         self._unregister_publisher()
00283 
00284     def save_settings(self, plugin_settings, instance_settings):
00285         instance_settings.set_value('topic' , self._widget.topic_line_edit.text())
00286 
00287     def restore_settings(self, plugin_settings, instance_settings):
00288 
00289         value = instance_settings.value('topic', "/cmd_vel")
00290         value = rospy.get_param("~default_topic", value)
00291         self._widget.topic_line_edit.setText(value)


bwi_rqt_plugins
Author(s): Piyush Khandelwal
autogenerated on Thu Jun 6 2019 17:57:50