controller_manager_gui.py
Go to the documentation of this file.
00001 import python_qt_binding.QtCore as QtCore
00002 import python_qt_binding.QtGui as QtGui
00003 from python_qt_binding.QtCore import Qt, QTimer, Signal, Slot
00004 from python_qt_binding.QtGui import QHeaderView, QIcon, QMenu, QTreeWidgetItem, QWidget
00005 
00006 from qt_gui.plugin import Plugin
00007 
00008 import rospy
00009 import rosservice
00010 import rosparam
00011 from rqt_py_common.plugin_container_widget import PluginContainerWidget
00012 from controller_manager_msgs.srv import *
00013 
00014 class ControllerManagerGUI(Plugin):
00015 
00016     def __init__(self, context):
00017         super(ControllerManagerGUI, self).__init__(context)
00018         self.setObjectName('controller_manager_gui')
00019 
00020         self.widget = ControllerManagerWidget(self)
00021 
00022         self.main_widget = PluginContainerWidget(self.widget, True, False)
00023 
00024         self.widget.start()
00025         if context.serial_number() > 1:
00026             self._widget.setWindowTitle(self.widget.windowTitle() + (' (%d)' % context.serial_number()))
00027         context.add_widget(self.widget)
00028 
00029     def shutdown_plugin(self):
00030         self.widget.shutdown_plugin()
00031 
00032 class ControllerManagerWidget(QWidget):
00033 
00034     _column_names = ['name', 'state', 'type', 'hw_iface', 'resources']
00035     _column_names_pretty = ['Controller Name', 'State', 'Type', 'HW Interface', 'Claimed Resources']
00036 
00037     sig_sysmsg = Signal(str)
00038     def __init__(self, plugin):
00039         super(ControllerManagerWidget, self).__init__()
00040 
00041         self._plugin = plugin
00042         self.setWindowTitle('Controller Manager')
00043 
00044         # create layouts
00045         vlayout_outer = QtGui.QVBoxLayout(self)
00046         vlayout_outer.setObjectName('vert_layout_outer')
00047         hlayout_top = QtGui.QHBoxLayout(self)
00048         hlayout_top.setObjectName('hori_layout_top')
00049         vlayout_outer.addLayout(hlayout_top)
00050 
00051         # create top bar
00052         # controller manager namespace combo box & label
00053         cm_ns_label = QtGui.QLabel(self)
00054         cm_ns_label.setObjectName('cm_ns_label')
00055         cm_ns_label.setText('CM Namespace:')
00056         fixed_policy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed)
00057         cm_ns_label.setSizePolicy(fixed_policy)
00058         hlayout_top.addWidget(cm_ns_label)
00059         cm_namespace_combo = QtGui.QComboBox(self)
00060         cm_namespace_combo.setObjectName('cm_namespace_combo')
00061         hlayout_top.addWidget(cm_namespace_combo)
00062         self.cm_namespace_combo = cm_namespace_combo
00063 
00064         # load controller combo box & label
00065         load_ctrl_label = QtGui.QLabel(self)
00066         load_ctrl_label.setObjectName('load_ctrl_label')
00067         load_ctrl_label.setText('Load Controller:')
00068         fixed_policy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed)
00069         load_ctrl_label.setSizePolicy(fixed_policy)
00070         hlayout_top.addWidget(load_ctrl_label)
00071         load_ctrl_combo = QtGui.QComboBox(self)
00072         load_ctrl_combo.setObjectName('load_ctrl_combo')
00073         load_ctrl_size_policy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed)
00074         load_ctrl_combo.setSizePolicy(load_ctrl_size_policy)
00075         hlayout_top.addWidget(load_ctrl_combo)
00076         self.load_ctrl_combo = load_ctrl_combo
00077 
00078         # load control button
00079         load_ctrl_button = QtGui.QPushButton(self)
00080         load_ctrl_button.setObjectName('load_ctrl_button')
00081         button_size_policy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
00082         button_size_policy.setHorizontalStretch(0)
00083         button_size_policy.setVerticalStretch(0)
00084         button_size_policy.setHeightForWidth(load_ctrl_button.sizePolicy().hasHeightForWidth())
00085         load_ctrl_button.setSizePolicy(button_size_policy)
00086         load_ctrl_button.setBaseSize(QtCore.QSize(30, 30))
00087         load_ctrl_button.setIcon(QIcon.fromTheme('list-add'))
00088         load_ctrl_button.setIconSize(QtCore.QSize(20,20))
00089         load_ctrl_button.clicked.connect(self.load_cur_ctrl)
00090         hlayout_top.addWidget(load_ctrl_button)
00091 
00092         # start control button
00093         start_ctrl_button = QtGui.QPushButton(self)
00094         start_ctrl_button.setObjectName('start_ctrl_button')
00095         button_size_policy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
00096         button_size_policy.setHorizontalStretch(0)
00097         button_size_policy.setVerticalStretch(0)
00098         button_size_policy.setHeightForWidth(start_ctrl_button.sizePolicy().hasHeightForWidth())
00099         start_ctrl_button.setSizePolicy(button_size_policy)
00100         start_ctrl_button.setBaseSize(QtCore.QSize(30, 30))
00101         start_ctrl_button.setIcon(QIcon.fromTheme('media-playback-start'))
00102         start_ctrl_button.setIconSize(QtCore.QSize(20,20))
00103         start_ctrl_button.clicked.connect(self.start_cur_ctrl)
00104         hlayout_top.addWidget(start_ctrl_button)
00105 
00106         # create tree/list widget
00107         ctrl_list_tree_widget = QtGui.QTreeWidget(self)
00108         ctrl_list_tree_widget.setObjectName('ctrl_list_tree_widget')
00109         self.ctrl_list_tree_widget = ctrl_list_tree_widget
00110         ctrl_list_tree_widget.setColumnCount(len(self._column_names))
00111         ctrl_list_tree_widget.setHeaderLabels(self._column_names_pretty)
00112         ctrl_list_tree_widget.sortByColumn(0, Qt.AscendingOrder)
00113         ctrl_list_tree_widget.setContextMenuPolicy(Qt.CustomContextMenu)
00114         ctrl_list_tree_widget.customContextMenuRequested.connect(
00115                             self.on_ctrl_list_tree_widget_customContextMenuRequested)
00116         vlayout_outer.addWidget(ctrl_list_tree_widget)
00117 
00118         header = self.ctrl_list_tree_widget.header()
00119         header.setResizeMode(QHeaderView.ResizeToContents)
00120         header.customContextMenuRequested.connect(
00121                             self.handle_header_view_customContextMenuRequested)
00122         header.setContextMenuPolicy(Qt.CustomContextMenu)
00123 
00124         self._ctrlers = {}
00125         self._column_index = {}
00126         for column_name in self._column_names:
00127             self._column_index[column_name] = len(self._column_index)
00128 
00129         # controller manager services
00130         self.list_types = {}
00131         self.list_ctrlers = {}
00132         self.load_ctrler = {}
00133         self.unload_ctrler = {}
00134         self.switch_ctrlers = {}
00135         self.ctrlman_ns_cur = ''
00136         self.loadable_params = {}
00137 
00138         # init and start update timer
00139         self._timer_refresh_ctrlers = QTimer(self)
00140         self._timer_refresh_ctrlers.timeout.connect(self._refresh_ctrlers_cb)
00141 
00142     def controller_manager_connect(self, ctrlman_ns):
00143         self.list_types[ctrlman_ns] = rospy.ServiceProxy(
00144                 ctrlman_ns + '/controller_manager/list_controller_types', ListControllerTypes)
00145         self.list_ctrlers[ctrlman_ns] = rospy.ServiceProxy(
00146                 ctrlman_ns + '/controller_manager/list_controllers', ListControllers)
00147         self.load_ctrler[ctrlman_ns] = rospy.ServiceProxy(
00148                 ctrlman_ns + '/controller_manager/load_controller', LoadController)
00149         self.unload_ctrler[ctrlman_ns] = rospy.ServiceProxy(
00150                 ctrlman_ns + '/controller_manager/unload_controller', UnloadController)
00151         self.switch_ctrlers[ctrlman_ns] = rospy.ServiceProxy(
00152                 ctrlman_ns + '/controller_manager/switch_controller', SwitchController)
00153         self.cm_namespace_combo.addItem(ctrlman_ns)
00154 
00155     def controller_manager_disconnect(self, ctrlman_ns):
00156         self.list_types[ctrlman_ns].close()
00157         del self.list_types[ctrlman_ns]
00158         self.list_ctrlers[ctrlman_ns].close()
00159         del self.list_ctrlers[ctrlman_ns]
00160         self.load_ctrler[ctrlman_ns].close()
00161         del self.load_ctrler[ctrlman_ns]
00162         self.unload_ctrler[ctrlman_ns].close()
00163         del self.unload_ctrler[ctrlman_ns]
00164         self.switch_ctrlers[ctrlman_ns].close()
00165         del self.switch_ctrlers[ctrlman_ns]
00166         combo_ind = self.cm_namespace_combo.findText(ctrlman_ns)
00167         self.cm_namespace_combo.removeItem(combo_ind)
00168 
00169     def _refresh_ctrlers_cb(self):
00170         try:
00171             # refresh the list of controller managers we can find
00172             srv_list = rosservice.get_service_list()
00173             ctrlman_ns_list_cur = []
00174             for srv_name in srv_list:
00175                 if 'controller_manager/list_controllers' in srv_name:
00176                     srv_type = rosservice.get_service_type(srv_name)
00177                     if srv_type == 'controller_manager_msgs/ListControllers':
00178                         ctrlman_ns = srv_name.split('/controller_manager/list_controllers')[0]
00179                         if ctrlman_ns == '':
00180                             ctrlman_ns = '/'
00181                         # ctrlman_ns is a Controller Manager namespace
00182                         if ctrlman_ns not in self.list_ctrlers:
00183                             # we haven't connected to it yet, create the service proxies
00184                             self.controller_manager_connect(ctrlman_ns)
00185                         ctrlman_ns_list_cur.append(ctrlman_ns)
00186 
00187             # remove every controller manager which isn't up anymore
00188             for ctrlman_ns_old in self.list_ctrlers.keys():
00189                 if ctrlman_ns_old not in ctrlman_ns_list_cur:
00190                     self.controller_manager_disconnect(ctrlman_ns_old)
00191 
00192             # refresh the controller list for the current controller manager
00193             self.refresh_loadable_ctrlers()
00194             self.refresh_ctrlers()
00195         except Exception as e:
00196             self.sig_sysmsg.emit(e.message)
00197 
00198     def remove_ctrler_from_list(self, ctrler_name):
00199         item = self._ctrlers[ctrler_name]['item']
00200         index = self.ctrl_list_tree_widget.indexOfTopLevelItem(item)
00201         self.ctrl_list_tree_widget.takeTopLevelItem(index)
00202         del self._ctrlers[ctrler_name]
00203 
00204     def remove_loadable_from_list(self, load_text):
00205         load_ctrl_ind = self.load_ctrl_combo.findText(load_text)
00206         self.load_ctrl_combo.removeItem(load_ctrl_ind)
00207         del self.loadable_params[load_text]
00208 
00209     def refresh_loadable_ctrlers(self):
00210         if self.cm_namespace_combo.count() == 0:
00211             # no controller managers found so there are no loadable controllers
00212             # remove old loadables
00213             for old_loadable_text in self.loadable_params.keys():
00214                 self.remove_loadable_from_list(old_loadable_text)
00215             return
00216 
00217         ctrlman_ns = self.cm_namespace_combo.currentText()
00218 
00219         if self.ctrlman_ns_cur != ctrlman_ns:
00220             # new controller manager selected
00221             # remove old loadables from list from last CM
00222             for old_loadable_text in self.loadable_params.keys():
00223                 self.remove_loadable_from_list(old_loadable_text)
00224 
00225         rospy.wait_for_service(ctrlman_ns + '/controller_manager/list_controller_types', 0.2)
00226         try:
00227             resp = self.list_types[ctrlman_ns].call(ListControllerTypesRequest())
00228         except rospy.ServiceException as e:
00229             # TODO: display warning somehow
00230             return 
00231         ctrler_types = resp.types
00232         loadable_params_cur = []
00233         all_params = rosparam.list_params('/')
00234         # for every parameter
00235         for pname in all_params:
00236             # remove the controller manager namespace
00237             if ctrlman_ns == '/':
00238                 pname_sub = pname
00239             else:
00240                 pname_sub = pname[len(ctrlman_ns):]
00241             psplit = pname_sub.split('/')
00242             if len(psplit) > 2 and psplit[2] == 'type':
00243                 loadable_type = rosparam.get_param(pname)
00244                 if loadable_type in ctrler_types:
00245                     load_text = pname[:-5] + '  -  ' + loadable_type
00246                     loadable_params_cur.append(load_text)
00247                     if load_text not in self.loadable_params:
00248                         self.loadable_params[load_text] = psplit[1]
00249                         self.load_ctrl_combo.addItem(load_text)
00250 
00251         # remove loadable parameters no longer in the parameter server
00252         for load_text_old in self.loadable_params.keys():
00253             if load_text_old not in loadable_params_cur:
00254                 self.remove_loadable_from_list(load_text_old)
00255 
00256     @Slot()
00257     def refresh_ctrlers(self):
00258         if self.cm_namespace_combo.count() == 0:
00259             # no controller managers found so there are no controllers to update
00260             # remove old controllers
00261             for old_ctrler_name in self._ctrlers.keys():
00262                 self.remove_ctrler_from_list(old_ctrler_name)
00263             return
00264 
00265         ctrlman_ns = self.cm_namespace_combo.currentText()
00266 
00267         if self.ctrlman_ns_cur != ctrlman_ns:
00268             # new controller manager selected
00269 
00270             # remove old controllers from list from last CM
00271             for old_ctrler_name in self._ctrlers.keys():
00272                 self.remove_ctrler_from_list(old_ctrler_name)
00273             self.ctrlman_ns_cur = ctrlman_ns
00274 
00275         rospy.wait_for_service(ctrlman_ns + '/controller_manager/list_controllers', 0.2)
00276         try:
00277             resp = self.list_ctrlers[ctrlman_ns].call(ListControllersRequest())
00278         except rospy.ServiceException as e:
00279             # TODO: display warning somehow
00280             return 
00281 
00282         controller_list = resp.controller
00283         new_ctrlers = {}
00284         for c in controller_list:
00285             if c.name not in self._ctrlers:
00286                 # new controller
00287                 item = QTreeWidgetItem(self.ctrl_list_tree_widget)
00288                 item.setData(0, Qt.UserRole, c.name)
00289                 ctrler = {'item' : item,
00290                           'state' : c.state,
00291                           'type' : c.type,
00292                           'hw_iface' : c.hardware_interface,
00293                           'resources' : "[" + ", ".join(c.resources) + "]"}
00294                 ctrler['item'].setText(self._column_index['name'], c.name)
00295                 update_type = True
00296                 update_state = True
00297             else:
00298                 # controller already in list
00299                 ctrler = self._ctrlers[c.name]
00300                 update_type = False
00301                 update_state = False
00302                 if ctrler['type'] != c.type or ctrler['hw_iface'] != c.hardware_interface:
00303                     # total controller change
00304                     ctrler['state'] = c.state
00305                     ctrler['type'] = c.type
00306                     ctrler['hw_iface'] = c.hardware_interface
00307                     ctrler['resources'] = "[" + ", ".join(c.resources) + "]"
00308                     update_type = True
00309                 if ctrler['state'] != c.state:
00310                     # state change
00311                     ctrler['state'] = c.state
00312                     update_state = True
00313 
00314             # update entries if needed
00315             if update_type:
00316                 ctrler['item'].setText(self._column_index['type'], ctrler['type'])
00317                 ctrler['item'].setText(self._column_index['hw_iface'], ctrler['hw_iface'])
00318                 ctrler['item'].setText(self._column_index['resources'], ctrler['resources'])
00319             if update_state or update_type:
00320                 ctrler['item'].setText(self._column_index['state'], ctrler['state'])
00321             new_ctrlers[c.name] = ctrler
00322 
00323         # remove missing controllers
00324         for old_ctrler_name in self._ctrlers.keys():
00325             if old_ctrler_name not in new_ctrlers:
00326                 self.remove_ctrler_from_list(old_ctrler_name)
00327                 
00328         # update current controllers
00329         self._ctrlers = new_ctrlers
00330 
00331     def start(self):
00332         self._timer_refresh_ctrlers.start(1000)
00333 
00334     @Slot('QPoint')
00335     def handle_header_view_customContextMenuRequested(self, pos):
00336         header = self.ctrl_list_tree_widget.header()
00337 
00338         # show context menu
00339         menu = QMenu(self)
00340         action_toggle_auto_resize = menu.addAction('Toggle Auto-Resize')
00341         action = menu.exec_(header.mapToGlobal(pos))
00342 
00343         # evaluate user action
00344         if action is action_toggle_auto_resize:
00345             if header.resizeMode(0) == QHeaderView.ResizeToContents:
00346                 header.setResizeMode(QHeaderView.Interactive)
00347             else:
00348                 header.setResizeMode(QHeaderView.ResizeToContents)
00349 
00350     def load_cur_ctrl(self):
00351         ctrlman_ns = self.cm_namespace_combo.currentText()
00352         load_text = self.load_ctrl_combo.currentText()
00353         load_param = self.loadable_params[load_text]
00354         self.load_controller(ctrlman_ns, load_param)
00355 
00356     def start_cur_ctrl(self):
00357         ctrlman_ns = self.cm_namespace_combo.currentText()
00358         load_text = self.load_ctrl_combo.currentText()
00359         load_param = self.loadable_params[load_text]
00360         self.load_controller(ctrlman_ns, load_param)
00361         self.start_stop_controller(ctrlman_ns, load_param, True)
00362 
00363     @Slot('QPoint')
00364     def on_ctrl_list_tree_widget_customContextMenuRequested(self, pos):
00365         ctrlman_ns = self.cm_namespace_combo.currentText()
00366         item = self.ctrl_list_tree_widget.itemAt(pos)
00367         if item is None:
00368             return
00369         ctrl_name = item.data(0, Qt.UserRole)
00370         ctrl_state = self._ctrlers[ctrl_name]['state']
00371 
00372         # show context menu
00373         menu = QMenu(self)
00374         if ctrl_state == 'running':
00375             action_stop = menu.addAction(
00376                     QIcon.fromTheme('media-playback-stop'), 'Stop Controller')
00377             action_kill = menu.addAction(
00378                     QIcon.fromTheme('list-remove'), 'Stop and Unload Controller')
00379         elif ctrl_state == 'stopped':
00380             action_start = menu.addAction(
00381                     QIcon.fromTheme('media-playback-start'), 'Start Controller')
00382             action_unload = menu.addAction(
00383                     QIcon.fromTheme('list-remove'), 'Unload Controller')
00384 
00385         action = menu.exec_(self.ctrl_list_tree_widget.mapToGlobal(pos))
00386 
00387         # evaluate user action
00388         if ctrl_state == 'running':
00389             if action is action_stop:
00390                 self.start_stop_controller(ctrlman_ns, ctrl_name, False)
00391             elif action is action_kill:
00392                 self.start_stop_controller(ctrlman_ns, ctrl_name, False)
00393                 self.unload_controller(ctrlman_ns, ctrl_name)
00394         elif ctrl_state == 'stopped':
00395             if action is action_start:
00396                 self.start_stop_controller(ctrlman_ns, ctrl_name, True)
00397             elif action is action_unload:
00398                 self.unload_controller(ctrlman_ns, ctrl_name)
00399 
00400     def load_controller(self, ctrlman_ns, name):
00401         rospy.wait_for_service(ctrlman_ns + '/controller_manager/load_controller', 0.2)
00402         try:
00403             resp = self.load_ctrler[ctrlman_ns].call(LoadControllerRequest(name))
00404             if resp.ok == 1:
00405                 # successful
00406                 return 0
00407             else:
00408                 # failure
00409                 return 1
00410         except rospy.ServiceException as e:
00411             # exception
00412             return 2
00413 
00414     def unload_controller(self, ctrlman_ns, name):
00415         rospy.wait_for_service(ctrlman_ns + '/controller_manager/unload_controller', 0.2)
00416         try:
00417             resp = self.unload_ctrler[ctrlman_ns].call(UnloadControllerRequest(name))
00418             if resp.ok == 1:
00419                 # successful
00420                 return 0
00421             else:
00422                 # failure
00423                 return 1
00424         except rospy.ServiceException as e:
00425             # exception
00426             return 2
00427 
00428     def start_stop_controller(self, ctrlman_ns, name, is_start):
00429         rospy.wait_for_service(ctrlman_ns + '/controller_manager/switch_controller', 0.2)
00430         start = []
00431         stop = []
00432         strictness = SwitchControllerRequest.STRICT
00433         if is_start:
00434             start = [name]
00435         else:
00436             stop = [name]
00437         try:
00438             resp = self.switch_ctrlers[ctrlman_ns].call(
00439                         SwitchControllerRequest(start, stop, strictness))
00440             if resp.ok == 1:
00441                 # successful
00442                 return 0
00443             else:
00444                 # failure
00445                 return 1
00446         except rospy.ServiceException as e:
00447             # exception
00448             return 2
00449 
00450     def shutdown_plugin(self):
00451         for ctrlman_ns in self.list_ctrlers.keys():
00452             self.controller_manager_disconnect(ctrlman_ns)
00453         self._timer_refresh_ctrlers.stop()
00454 


rqt_controller_manager
Author(s): Kelsey Hawkins
autogenerated on Fri Aug 28 2015 12:36:31