class QInteractionsChooser(QMainWindow): # pyqt signals are always defined as class attributes signal_interactions_updated = Signal() def __init__(self, parent, title, application, rocon_master_index="", rocon_master_name="", rocon_master_uri='localhost', host_name='localhost'): super(QInteractionsChooser, self).__init__(parent) self.rocon_master_index = rocon_master_index self.rocon_master_uri = rocon_master_uri self.rocon_master_name = rocon_master_name self.host_name = host_name self.cur_selected_interaction = None self.cur_selected_role = 0 self.interactions = {} self.interactive_client = InteractiveClient(stop_interaction_postexec_fn=self.interactions_updated_relay) self.application = application rospack = rospkg.RosPack() icon_file = os.path.join(rospack.get_path('rocon_icons'), 'icons', 'rocon_logo.png') self.application.setWindowIcon(QIcon(icon_file)) self.interactions_widget = QWidget() self.roles_widget = QWidget() path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../ui/interactions_list.ui") loadUi(path, self.interactions_widget) path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../ui/role_list.ui") loadUi(path, self.roles_widget) # role list widget self.roles_widget.role_list_widget.setIconSize(QSize(50, 50)) self.roles_widget.role_list_widget.itemDoubleClicked.connect(self._switch_to_interactions_list) self.roles_widget.back_btn.pressed.connect(self._switch_to_master_chooser) self.roles_widget.stop_all_interactions_button.pressed.connect(self._stop_all_interactions) self.roles_widget.refresh_btn.pressed.connect(self._refresh_role_list) self.roles_widget.closeEvent = self._close_event # interactions list widget self.interactions_widget.interactions_list_widget.setIconSize(QSize(50, 50)) self.interactions_widget.interactions_list_widget.itemDoubleClicked.connect(self._start_interaction) self.interactions_widget.back_btn.pressed.connect(self._switch_to_role_list) self.interactions_widget.interactions_list_widget.itemClicked.connect(self._display_interaction_info) # rocon master item click event self.interactions_widget.stop_interactions_button.pressed.connect(self._stop_interaction) self.interactions_widget.stop_interactions_button.setDisabled(True) self.interactions_widget.closeEvent = self._close_event # signals self.signal_interactions_updated.connect(self._refresh_interactions_list, Qt.QueuedConnection) self.signal_interactions_updated.connect(self._set_stop_interactions_button, Qt.QueuedConnection) # create a few directories for caching icons and ... utils.setup_home_dirs() # connect to the ros master (result, message) = self.interactive_client._connect(self.rocon_master_name, self.rocon_master_uri, self.host_name) if not result: QMessageBox.warning(self, 'Connection Failed', "%s." % message.capitalize(), QMessageBox.Ok) self._switch_to_master_chooser() return role_list = self._refresh_role_list() # Ugly Hack : our window manager is not graying out the button when an interaction closes itself down and the appropriate # callback (_set_stop_interactions_button) is fired. It does otherwise though so it looks like the window manager # is getting confused when the original program doesn't have the focus. # # Taking control of it ourselves works... self.interactions_widget.stop_interactions_button.setStyleSheet("QPushButton:disabled { color: gray }") # show interactions list if there's no choice amongst roles, otherwise show the role list if len(role_list) == 1: self.cur_selected_role = role_list[0] self.interactive_client.select_role(self.cur_selected_role) self.interactions_widget.show() self._refresh_interactions_list() else: self.roles_widget.show() def shutdown(self): """ Public method to enable shutdown of the script - this function is primarily for shutting down the interactions chooser from external signals (e.g. CTRL-C on the command line). """ self.interactive_client.shutdown() def _close_event(self, event): """ Re-implementation of close event handlers for the interaction chooser's children (i.e. role and interactions list widgets). """ console.logdebug("Interactions Chooser : remocon shutting down.") self.shutdown() ###################################### # Roles List Widget ###################################### def _switch_to_interactions_list(self, Item): """ Take the selected role and switch to an interactions view of that role. """ console.logdebug("Interactions Chooser : switching to the interactions list") self.cur_selected_role = str(Item.text()) self.interactive_client.select_role(self.cur_selected_role) self.interactions_widget.show() self.interactions_widget.move(self.roles_widget.pos()) self.roles_widget.hide() self._refresh_interactions_list() def _switch_to_master_chooser(self): console.logdebug("Interactions Chooser : switching back to the master chooser") self.shutdown() os.execv(QMasterChooser.rocon_remocon_script, ['', self.host_name]) def _refresh_role_list(self): self.roles_widget.role_list_widget.clear() role_list = self.interactive_client.get_role_list() #set list widget item (reverse order because we push them on the top) for role in reversed(role_list): self.roles_widget.role_list_widget.insertItem(0, role) #setting the list font font = self.roles_widget.role_list_widget.item(0).font() font.setPointSize(13) self.roles_widget.role_list_widget.item(0).setFont(font) return role_list def _stop_all_interactions(self): console.logdebug("Interactions Chooser : stopping all running interactions") self.interactive_client.stop_all_interactions() self.roles_widget.stop_all_interactions_button.setEnabled(False) ###################################### # Interactions List Widget ###################################### def _switch_to_role_list(self): console.logdebug("Interactions Chooser : switching to the role list") # show the roles widget if self.interactive_client.has_running_interactions(): self.roles_widget.stop_all_interactions_button.setEnabled(True) else: self.roles_widget.stop_all_interactions_button.setEnabled(False) self.roles_widget.show() self.roles_widget.move(self.interactions_widget.pos()) # show the interactions widget self.interactions_widget.stop_interactions_button.setEnabled(False) self.interactions_widget.hide() def _display_interaction_info(self, Item): """ Display the currently selected interaction's information. Triggered when single-clicking on it in the interactions list view. """ list_widget = Item.listWidget() cur_index = list_widget.count() - list_widget.currentRow() - 1 for k in self.interactions.values(): if(k.index == cur_index): self.cur_selected_interaction = k break self.interactions_widget.app_info.clear() info_text = "<html>" info_text += "<p>-------------------------------------------</p>" web_interaction = web_interactions.parse(self.cur_selected_interaction.name) name = self.cur_selected_interaction.name if web_interaction is None else web_interaction.url info_text += "<p><b>name: </b>" + name + "</p>" info_text += "<p><b> ---------------------</b>" + "</p>" info_text += "<p><b>compatibility: </b>" + self.cur_selected_interaction.compatibility + "</p>" info_text += "<p><b>display name: </b>" + self.cur_selected_interaction.display_name + "</p>" info_text += "<p><b>description: </b>" + self.cur_selected_interaction.description + "</p>" info_text += "<p><b>namespace: </b>" + self.cur_selected_interaction.namespace + "</p>" info_text += "<p><b>max: </b>" + str(self.cur_selected_interaction.max) + "</p>" info_text += "<p><b> ---------------------</b>" + "</p>" info_text += "<p><b>remappings: </b>" + str(self.cur_selected_interaction.remappings) + "</p>" info_text += "<p><b>parameters: </b>" + str(self.cur_selected_interaction.parameters) + "</p>" info_text += "</html>" self.interactions_widget.app_info.appendHtml(info_text) self._set_stop_interactions_button() ###################################### # Gui Updates/Refreshes ###################################### def interactions_updated_relay(self): """ Called by the underlying interactive client whenever the gui needs to be updated with fresh information. Using this relay saves us from having to embed qt functions in the underlying class but makes sure we signal across threads so the gui can update things in its own thread. Currently this only handles updates caused by termination of an interaction. If we wished to handle additional situations, we should use an argument here indicating what kind of interaction update occurred. """ self.signal_interactions_updated.emit() # this connects to: # - self._refresh_interactions_list() # - self._set_stop_interactions_button() def _refresh_interactions_list(self): """ This just does a complete redraw of the interactions with the currently selected role. It's a bit brute force doing this every time the interactions' 'state' changes, but this suffices for now. :param str role_name: role to request list of interactions for. """ console.logdebug("Interactions Chooser : refreshing the interactions list") self.interactions = self.interactive_client.interactions(self.cur_selected_role) self.interactions_widget.interactions_list_widget.clear() index = 0 for interaction in self.interactions.values(): interaction.index = index index = index + 1 self.interactions_widget.interactions_list_widget.insertItem(0, interaction.display_name) # is it a currently running pairing if self.interactive_client.pairing == interaction.hash: self.interactions_widget.interactions_list_widget.item(0).setBackground(QColor(100, 100, 150)) #setting the list font font = self.interactions_widget.interactions_list_widget.item(0).font() font.setPointSize(13) self.interactions_widget.interactions_list_widget.item(0).setFont(font) #setting the icon icon = interaction.icon if icon == "unknown.png": icon = QIcon(self.icon_paths['unknown']) self.interactions_widget.interactions_list_widget.item(0).setIcon(icon) elif len(icon): icon = QIcon(os.path.join(utils.get_icon_cache_home(), icon)) self.interactions_widget.interactions_list_widget.item(0).setIcon(icon) else: console.logdebug("%s : No icon" % str(self.rocon_master_name)) def _set_stop_interactions_button(self): ''' Disable or enable the stop button depending on whether the selected interaction has any currently launched processes, ''' if not self.interactions: console.logwarn("No interactions") return if self.cur_selected_interaction.launch_list: console.logdebug("Interactions Chooser : enabling stop interactions button [%s]" % self.cur_selected_interaction.display_name) self.interactions_widget.stop_interactions_button.setEnabled(True) else: console.logdebug("Interactions Chooser : disabling stop interactions button [%s]" % self.cur_selected_interaction.display_name) self.interactions_widget.stop_interactions_button.setEnabled(False) ###################################### # Start/Stop Interactions ###################################### def _start_interaction(self): console.logdebug("Interactions Chooser : starting interaction [%s]" % str(self.cur_selected_interaction.name)) (result, message) = self.interactive_client.start_interaction(self.cur_selected_role, self.cur_selected_interaction.hash) if result: if self.cur_selected_interaction.is_paired_type(): self._refresh_interactions_list() # make sure the highlight is working self.interactions_widget.stop_interactions_button.setDisabled(False) else: QMessageBox.warning(self, 'Start Interaction Failed', "%s." % message.capitalize(), QMessageBox.Ok) console.logwarn("Interactions Chooser : start interaction failed [%s]" % message) def _stop_interaction(self): console.logdebug("Interactions Chooser : stopping interaction %s " % str(self.cur_selected_interaction.name)) (result, message) = self.interactive_client.stop_interaction(self.cur_selected_interaction.hash) if result: if self.cur_selected_interaction.is_paired_type(): self._refresh_interactions_list() # make sure the highlight is disabled self._set_stop_interactions_button() #self.interactions_widget.stop_interactions_button.setDisabled(True) else: QMessageBox.warning(self, 'Stop Interaction Failed', "%s." % message.capitalize(), QMessageBox.Ok) console.logwarn("Interactions Chooser : stop interaction failed [%s]" % message)
def hide(self): self.stop() QWidget.hide(self)
class QInteractionsChooser(QMainWindow): def __init__(self, interactive_client_interface=None): self.binded_function = {} self.cur_selected_role = '' self.cur_selected_interaction = None self.interactions = {} self.interactive_client_interface = interactive_client_interface self.interactions_widget = QWidget() # load ui rospack = rospkg.RosPack() path = os.path.join(rospack.get_path('rocon_remocon'), 'ui', 'interactions_list.ui') loadUi(path, self.interactions_widget) # interactions list widget self.interactions_widget.interactions_list_widget.setIconSize( QSize(50, 50)) self.interactions_widget.interactions_list_widget.itemDoubleClicked.connect( self._start_interaction) self.interactions_widget.back_btn.pressed.connect(self._back) self.interactions_widget.interactions_list_widget.itemClicked.connect( self._display_interaction_info) # rocon master item click event self.interactions_widget.stop_interactions_button.pressed.connect( self._stop_interaction) self.interactions_widget.stop_interactions_button.setDisabled(True) self.interactions_widget.closeEvent = self._close_event console.logdebug('init QInteractionsChooser') def _back(self): if 'back' in self.binded_function.keys( ) and self.binded_function['back'] is not None: self.binded_function['back']() else: console.logdebug( "Interactions Chooser : None binded functione: shutdown") def _close_event(self, event): """ Re-implementation of close event handlers for the interaction chooser's children (i.e. role and interactions list widgets). """ console.logdebug("Interactions Chooser : remocon shutting down.") if 'shutdown' in self.binded_function.keys( ) and self.binded_function['shutdown'] is not None: self.binded_function['shutdown']() else: console.logdebug( "Interactions Chooser : None binded functione: shutdown") ###################################### # Interactions List Widget ###################################### def _display_interaction_info(self, Item): """ Display the currently selected interaction's information. Triggered when single-clicking on it in the interactions list view. """ list_widget = Item.listWidget() cur_index = list_widget.count() - list_widget.currentRow() - 1 for k in self.interactions.values(): if (k.index == cur_index): self.cur_selected_interaction = k break self.interactions_widget.app_info.clear() info_text = "<html>" info_text += "<p>-------------------------------------------</p>" web_interaction = web_interactions.parse( self.cur_selected_interaction.name) name = self.cur_selected_interaction.name if web_interaction is None else web_interaction.url info_text += "<p><b>name: </b>" + name + "</p>" info_text += "<p><b> ---------------------</b>" + "</p>" info_text += "<p><b>compatibility: </b>" + self.cur_selected_interaction.compatibility + "</p>" info_text += "<p><b>display name: </b>" + self.cur_selected_interaction.display_name + "</p>" info_text += "<p><b>description: </b>" + self.cur_selected_interaction.description + "</p>" info_text += "<p><b>namespace: </b>" + self.cur_selected_interaction.namespace + "</p>" info_text += "<p><b>max: </b>" + str( self.cur_selected_interaction.max) + "</p>" info_text += "<p><b> ---------------------</b>" + "</p>" info_text += "<p><b>remappings: </b>" + str( self.cur_selected_interaction.remappings) + "</p>" info_text += "<p><b>parameters: </b>" + str( self.cur_selected_interaction.parameters) + "</p>" info_text += "</html>" self.interactions_widget.app_info.appendHtml(info_text) self._set_stop_interactions_button() ###################################### # Gui Updates/Refreshes ###################################### def _set_stop_interactions_button(self): """ Disable or enable the stop button depending on whether the selected interaction has any currently launched processes, """ if not self.interactions: console.logwarn("No interactions") return if self.cur_selected_interaction.launch_list: console.logdebug( "Interactions Chooser : enabling stop interactions button [%s]" % self.cur_selected_interaction.display_name) self.interactions_widget.stop_interactions_button.setEnabled(True) else: console.logdebug( "Interactions Chooser : disabling stop interactions button [%s]" % self.cur_selected_interaction.display_name) self.interactions_widget.stop_interactions_button.setEnabled(False) ###################################### # Start/Stop Interactions ###################################### def _start_interaction(self): """ Start selected interactions when user hits start button and does doubleclicking interaction item. The interactions can be launched in duplicate. """ console.logdebug("Interactions Chooser : starting interaction [%s]" % str(self.cur_selected_interaction.name)) (result, message) = self.interactive_client_interface.start_interaction( self.cur_selected_role, self.cur_selected_interaction.hash) if result: if self.cur_selected_interaction.is_paired_type(): self.refresh_interactions_list( ) # make sure the highlight is working self.interactions_widget.stop_interactions_button.setDisabled( False) else: QMessageBox.warning(self.interactions_widget, 'Start Interaction Failed', "%s." % message.capitalize(), QMessageBox.Ok) console.logwarn( "Interactions Chooser : start interaction failed [%s]" % message) def _stop_interaction(self): """ Stop running interactions when user hits `stop` or 'all stop interactions button` button. If no interactions is running, buttons are disabled. """ console.logdebug("Interactions Chooser : stopping interaction %s " % str(self.cur_selected_interaction.name)) (result, message) = self.interactive_client_interface.stop_interaction( self.cur_selected_interaction.hash) if result: if self.cur_selected_interaction.is_paired_type(): self.refresh_interactions_list( ) # make sure the highlight is disabled self._set_stop_interactions_button() # self.interactions_widget.stop_interactions_button.setDisabled(True) else: QMessageBox.warning(self.interactions_widget, 'Stop Interaction Failed', "%s." % message.capitalize(), QMessageBox.Ok) console.logwarn( "Interactions Chooser : stop interaction failed [%s]" % message) def bind_function(self, name, function_handle): """ Binding external function to map with ui button """ self.binded_function[name] = function_handle def show(self, pos=None): """ Showing the interactions chooser """ self.interactions_widget.show() if pos is not None: self.interactions_widget.move(pos) if self.interactive_client_interface.has_running_interactions(): self.interactions_widget.stop_interactions_button.setEnabled(True) else: self.interactions_widget.stop_interactions_button.setEnabled(False) def hide(self): """ Hiding the interactions chooser """ self.interactions_widget.hide() def select_role(self, role): """ Take the selected role to get a list of interaction. :param role: role name from role chooser. :type role: string """ self.cur_selected_role = role self.interactive_client_interface.select_role(self.cur_selected_role) self.refresh_interactions_list() def refresh_interactions_list(self): """ This just does a complete redraw of the interactions with the currently selected role. It's a bit brute force doing this every time the interactions' 'state' changes, but this suffices for now. """ console.logdebug( "Interactions Chooser : refreshing the interactions list") self.interactions = self.interactive_client_interface.interactions( self.cur_selected_role) self.interactions_widget.interactions_list_widget.clear() index = 0 for interaction in self.interactions.values(): interaction.index = index index = index + 1 self.interactions_widget.interactions_list_widget.insertItem( 0, interaction.display_name) # is it a currently running pairing if self.interactive_client_interface.pairing == interaction.hash: self.interactions_widget.interactions_list_widget.item( 0).setBackground(QColor(100, 100, 150)) # setting the list font font = self.interactions_widget.interactions_list_widget.item( 0).font() font.setPointSize(13) self.interactions_widget.interactions_list_widget.item(0).setFont( font) # setting the icon icon = interaction.icon if icon == "unknown.png": icon = QIcon(self.icon_paths['unknown']) self.interactions_widget.interactions_list_widget.item( 0).setIcon(icon) elif len(icon): icon = QIcon(os.path.join(utils.get_icon_cache_home(), icon)) self.interactions_widget.interactions_list_widget.item( 0).setIcon(icon) else: console.logdebug("%s : No icon" % str(self.rocon_master_name))
class QRoleChooser(): # pyqt signals are always defined as class attributes # signal_interactions_updated = Signal() def __init__(self, interactive_client_interface=None, with_rqt=False): self.interactive_client_interface = interactive_client_interface self.with_rqt = with_rqt self.binded_function = {} self.role_list = [] self.cur_selected_role = '' self.roles_widget = QWidget() # load ui rospack = rospkg.RosPack() path = os.path.join(rospack.get_path('rocon_remocon'), 'ui', 'role_list.ui') loadUi(path, self.roles_widget) # connect ui event role list widget self.roles_widget.role_list_widget.setIconSize(QSize(50, 50)) self.roles_widget.role_list_widget.itemDoubleClicked.connect(self._select_role) self.roles_widget.refresh_btn.pressed.connect(self.refresh_role_list) self.roles_widget.back_btn.pressed.connect(self._back) self.roles_widget.stop_all_interactions_button.pressed.connect(self._stop_all_interactions) self.roles_widget.closeEvent = self._close_event self._init() def _init(self): """ Initialization of role chooser. It it launced with rqt, the back button is disabled. Viewer of interactions chooser is launched at once when the role list has one role. """ if self.with_rqt: self.roles_widget.back_btn.setEnabled(False) self.refresh_role_list() if len(self.role_list) == 1: self.cur_selected_role = self.role_list[0] self.interactive_client_interface.select_role(self.cur_selected_role) def _back(self): """ Public method to enable shutdown of the script - this function is primarily for shutting down the Role chooser from external signals (e.g. CTRL-C on the command line). """ console.logdebug("Role Chooser : Back") if 'back' in self.binded_function.keys() and self.binded_function['back'] is not None: self.binded_function['back']() def _close_event(self, event): """ Re-implementation of close event handlers for the interaction chooser's children (i.e. role and interactions list widgets). """ console.logdebug("Role Chooser : Role Chooser shutting down.") self._back() def _select_role(self, item): """ Take the selected role to switch interactions viewer as it. :param item: qt list widget item of selected role. The user does double click on item wanted to launch :type item: python_qt_binding.QtGui.QListWidgetItem """ console.logdebug("Role Chooser : switching to the interactions list") self.cur_selected_role = str(item.text()) if 'select_role' in self.binded_function.keys() and self.binded_function['select_role'] is not None: self.binded_function['select_role']() def _stop_all_interactions(self): """ Stopping all running interactions. If no interactions is running, stop interactions button is disables. """ console.logdebug("Role Chooser : stopping all running interactions") self.interactive_client_interface.stop_all_interactions() self.roles_widget.stop_all_interactions_button.setEnabled(False) def bind_function(self, name, function_handle): """ Binding external function to map with ui button """ self.binded_function[name] = function_handle def show(self, pos=None): """ Showing the role chooser with rereshing role list """ self.roles_widget.show() if pos is not None: self.roles_widget.move(pos) self.refresh_role_list() def hide(self): """ Hiding the role chooser to show other widget """ self.roles_widget.hide() def pos(self): """ Postion of role chooser :return: xy position on desktop :rtype: python_qt_binding.QtCore.QPoint """ return self.roles_widget.pos() def refresh_role_list(self): """ Update a list of roles. define status of all interaction stop button as checking running interactions. """ if self.interactive_client_interface.has_running_interactions(): self.roles_widget.stop_all_interactions_button.setEnabled(True) else: self.roles_widget.stop_all_interactions_button.setEnabled(False) self.roles_widget.role_list_widget.clear() self.role_list = self.interactive_client_interface.get_role_list() # set list widget item (reverse order because we push them on the top) for role in reversed(self.role_list): self.roles_widget.role_list_widget.insertItem(0, role) # setting the list font font = self.roles_widget.role_list_widget.item(0).font() font.setPointSize(13) self.roles_widget.role_list_widget.item(0).setFont(font)
class QMasterChooser(QMainWindow): rocon_remocon_script = utils.find_rocon_remocon_script('rocon_remocon') rocon_remocon_sub_script = utils.find_rocon_remocon_script( 'rocon_remocon_sub') def __init__(self, parent, title, application): self._context = parent self.application = application super(QMasterChooser, self).__init__() utils.setup_home_dirs() self._init_widget() self._init_interface() # start application self._widget_main.show() self._widget_main.activateWindow() # give it the focus self._widget_main.raise_() # make sure it is on top self._update_rocon_master_list() def __del__(self): console.loginfo("RemoconMain: Destroy") def _init_host_configuration(self): self.host_name = "localhost" self.master_uri = "http://%s:11311" % (self.host_name) self.env_host_name = os.getenv("ROS_HOSTNAME") self.env_master_uri = os.getenv("ROS_MASTER_URI") if self.env_host_name: self.host_name = self.env_host_name if self.env_master_uri == None: self.env_master_uri = "http://%s:11311" % (self.host_name) elif self.env_master_uri: self.master_uri = self.env_master_uri def _init_icon_paths(self): self.icon_paths = {} try: self.icon_paths[ 'unknown'] = rocon_python_utils.ros.find_resource_from_string( 'rocon_icons/unknown', extension='png') except (rospkg.ResourceNotFound, ValueError): console.logerror( "Remocon : couldn't find icons on the ros package path (install rocon_icons and rocon_bubble_icons" ) sys.exit(1) def _init_widget(self): self._init_icon_paths() self.setObjectName('Remocon') self._widget_main = QWidget() rospack = rospkg.RosPack() path = os.path.join(rospack.get_path('rocon_remocon'), 'ui', 'remocon.ui') loadUi(path, self._widget_main) # main widget self._widget_main.list_widget.setIconSize(QSize(50, 50)) self._widget_main.list_widget.itemDoubleClicked.connect( self._connect_rocon_master) # list item double click event self._widget_main.list_widget.itemClicked.connect( self._select_rocon_master) # list item double click event self._widget_main.add_concert_btn.pressed.connect( self._set_add_rocon_master) # add button event self._widget_main.delete_btn.pressed.connect( self._delete_rocon_master) # delete button event self._widget_main.delete_all_btn.pressed.connect( self._delete_all_rocon_masters) # delete all button event self._widget_main.refresh_btn.pressed.connect( self._refresh_all_rocon_master_list) # refresh all button event self._widget_main.list_info_widget.clear() def _init_interface(self): self._init_host_configuration() self.rocon_masters = RoconMasters() self._connect_dlg = None self.cur_selected_rocon_master = None self._is_init = True def _delete_all_rocon_masters(self): self.rocon_masters.clear() self._update_rocon_master_list() self._widget_main.list_info_widget.clear() def _delete_rocon_master(self): if self.cur_selected_rocon_master in self.rocon_masters.keys(): self.rocon_masters.delete(self.cur_selected_rocon_master) self._update_rocon_master_list() self._widget_main.list_info_widget.clear() def _add_rocon_master(self, uri_text_widget, host_name_text_widget): rocon_master = self.rocon_masters.add( uri_text_widget.toPlainText(), host_name_text_widget.toPlainText()) self._refresh_rocon_master(rocon_master) def _set_add_rocon_master(self): if self._connect_dlg: console.logdebug("Dialog is live!!") self._connect_dlg.done(0) self._connect_dlg = self._create_add_rocon_master_dialog() self._connect_dlg.setVisible(True) self._connect_dlg.finished.connect(self._destroy_connect_dlg) def _refresh_rocon_master(self, rocon_master): rocon_master.check() self._widget_main.list_info_widget.clear() self._update_rocon_master_list() def _refresh_all_rocon_master_list(self): self.rocon_masters.check() self._widget_main.list_info_widget.clear() self._update_rocon_master_list() def _update_rocon_master_list(self): self._widget_main.list_widget.clear() for rocon_master in self.rocon_masters.values(): self._add_rocon_master_list_item(rocon_master) self.rocon_masters.dump() def _add_rocon_master_list_item(self, rocon_master): rocon_master.current_row = str(self._widget_main.list_widget.count()) display_name = str(rocon_master.name) + "\n" + "[" + str( rocon_master.uri) + "]" self._widget_main.list_widget.insertItem( self._widget_main.list_widget.count(), display_name) # setting the list font font = self._widget_main.list_widget.item( self._widget_main.list_widget.count() - 1).font() font.setPointSize(13) self._widget_main.list_widget.item( self._widget_main.list_widget.count() - 1).setFont(font) # setToolTip rocon_master_info = "" rocon_master_info += "rocon_master_index: " + str( rocon_master.index) + "\n" rocon_master_info += "rocon_master_name: " + str( rocon_master.name) + "\n" rocon_master_info += "master_uri: " + str(rocon_master.uri) + "\n" rocon_master_info += "host_name: " + str( rocon_master.host_name) + "\n" rocon_master_info += "description: " + str(rocon_master.description) self._widget_main.list_widget.item( self._widget_main.list_widget.count() - 1).setToolTip(rocon_master_info) # set icon if rocon_master.icon == "unknown.png": icon = QIcon(self.icon_paths['unknown']) self._widget_main.list_widget.item( self._widget_main.list_widget.count() - 1).setIcon(icon) elif len(rocon_master.icon): icon = QIcon( os.path.join(utils.get_icon_cache_home(), rocon_master.icon)) self._widget_main.list_widget.item( self._widget_main.list_widget.count() - 1).setIcon(icon) else: console.logdebug("%s : No icon" % rocon_master.name) def _select_rocon_master(self, Item): list_widget = Item.listWidget() for k in self.rocon_masters.values(): if k.current_row == str(list_widget.currentRow()): self.cur_selected_rocon_master = k.index break self._widget_main.list_info_widget.clear() info_text = "<html>" info_text += "<p>-------------------------------------------</p>" info_text += "<p><b>name: </b>" + str( self.rocon_masters[self.cur_selected_rocon_master].name) + "</p>" info_text += "<p><b>master_uri: </b>" + str( self.rocon_masters[self.cur_selected_rocon_master].uri) + "</p>" info_text += "<p><b>host_name: </b>" + str(self.rocon_masters[ self.cur_selected_rocon_master].host_name) + "</p>" info_text += "<p><b>description: </b>" + str(self.rocon_masters[ self.cur_selected_rocon_master].description) + "</p>" info_text += "<p>-------------------------------------------</p>" info_text += "</html>" self._widget_main.list_info_widget.appendHtml(info_text) def _destroy_connect_dlg(self): self._connect_dlg = None def _connect_rocon_master(self): rocon_master_name = str( self.rocon_masters[self.cur_selected_rocon_master].name) rocon_master_uri = str( self.rocon_masters[self.cur_selected_rocon_master].uri) rocon_master_host_name = str( self.rocon_masters[self.cur_selected_rocon_master].host_name) rocon_master_index = str(self.cur_selected_rocon_master) self.rocon_masters[rocon_master_index].check() # Todo this use of flags is spanky if self.rocon_masters[rocon_master_index].flag == '0': QMessageBox.warning( self, 'Rocon Master Connection Error', "Could not find a rocon master at %s" % self.rocon_masters[rocon_master_index].uri, QMessageBox.Ok) return if self.rocon_masters[rocon_master_index].flag == '1': QMessageBox.warning( self, 'Rocon Master Communication Error', "Found a rocon master at %s but cannot communicate with it (are ROS_IP/ROS_MASTER_URI correctly configured locally and remotely?)" % self.rocon_masters[rocon_master_index].uri, QMessageBox.Ok) return self._widget_main.hide() arguments = ["", rocon_master_uri, rocon_master_host_name] os.execv(QMasterChooser.rocon_remocon_sub_script, arguments) console.logdebug("Spawning: %s with args %s" % (QMasterChooser.rocon_remocon_sub_script, arguments)) def _create_add_rocon_master_dialog(self): # dialog connect_dlg = QDialog(self._widget_main) connect_dlg.setWindowTitle("Add Ros Master") connect_dlg.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Ignored) connect_dlg.setMinimumSize(350, 0) # dlg_rect = self._connect_dlg.geometry() # dialog layout ver_layout = QVBoxLayout(connect_dlg) ver_layout.setContentsMargins(9, 9, 9, 9) # param layout text_grid_sub_widget = QWidget() text_grid_layout = QGridLayout(text_grid_sub_widget) text_grid_layout.setColumnStretch(1, 0) text_grid_layout.setRowStretch(2, 0) # param 1 title_widget1 = QLabel("MASTER_URI: ") context_widget1 = QTextEdit() context_widget1.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Ignored) context_widget1.setMinimumSize(0, 30) context_widget1.append(self.master_uri) # param 2 title_widget2 = QLabel("HOST_NAME: ") context_widget2 = QTextEdit() context_widget2.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Ignored) context_widget2.setMinimumSize(0, 30) context_widget2.append(self.host_name) # add param text_grid_layout.addWidget(title_widget1) text_grid_layout.addWidget(context_widget1) text_grid_layout.addWidget(title_widget2) text_grid_layout.addWidget(context_widget2) # add param layout ver_layout.addWidget(text_grid_sub_widget) # button layout button_hor_sub_widget = QWidget() button_hor_layout = QHBoxLayout(button_hor_sub_widget) uri_text_widget = context_widget1 host_name_text_widget = context_widget2 # button btn_call = QPushButton("Add") btn_cancel = QPushButton("Cancel") btn_call.clicked.connect(lambda: connect_dlg.done(0)) btn_call.clicked.connect(lambda: self._add_rocon_master( uri_text_widget, host_name_text_widget)) btn_cancel.clicked.connect(lambda: connect_dlg.done(0)) # add button button_hor_layout.addWidget(btn_call) button_hor_layout.addWidget(btn_cancel) # add button layout ver_layout.addWidget(button_hor_sub_widget) return connect_dlg
class ControllerManager(Plugin): """ Graphical frontend for managing ros_control controllers. """ _cm_update_freq = 1 # Hz def __init__(self, context): super(ControllerManager, self).__init__(context) self.setObjectName('ControllerManager') # Create QWidget and extend it with all the attributes and children # from the UI file self._widget = QWidget() rp = rospkg.RosPack() ui_file = os.path.join(rp.get_path('rqt_controller_manager'), 'resource', 'controller_manager.ui') loadUi(ui_file, self._widget) self._widget.setObjectName('ControllerManagerUi') # Pop-up that displays controller information self._popup_widget = QWidget() ui_file = os.path.join(rp.get_path('rqt_controller_manager'), 'resource', 'controller_info.ui') loadUi(ui_file, self._popup_widget) self._popup_widget.setObjectName('ControllerInfoUi') # Show _widget.windowTitle on left-top of each plugin (when # it's set in _widget). This is useful when you open multiple # plugins at 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) # Initialize members self._cm_ns = [] # Namespace of the selected controller manager self._controllers = [] # State of each controller self._table_model = None self._controller_lister = None # Controller manager service proxies self._load_srv = None self._unload_srv = None self._switch_srv = None # Controller state icons rospack = rospkg.RosPack() path = rospack.get_path('rqt_controller_manager') self._icons = {'running': QIcon(path + '/resource/led_green.png'), 'stopped': QIcon(path + '/resource/led_red.png'), 'uninitialized': QIcon(path + '/resource/led_off.png')} # Controllers display table_view = self._widget.table_view table_view.setContextMenuPolicy(Qt.CustomContextMenu) table_view.customContextMenuRequested.connect(self._on_ctrl_menu) table_view.doubleClicked.connect(self._on_ctrl_info) header = table_view.horizontalHeader() header.setResizeMode(QHeaderView.ResizeToContents) header.setContextMenuPolicy(Qt.CustomContextMenu) header.customContextMenuRequested.connect(self._on_header_menu) # Timer for controller manager updates self._list_cm = ControllerManagerLister() self._update_cm_list_timer = QTimer(self) self._update_cm_list_timer.setInterval(1000.0 / self._cm_update_freq) self._update_cm_list_timer.timeout.connect(self._update_cm_list) self._update_cm_list_timer.start() # Timer for running controller updates self._update_ctrl_list_timer = QTimer(self) self._update_ctrl_list_timer.setInterval(1000.0 / self._cm_update_freq) self._update_ctrl_list_timer.timeout.connect(self._update_controllers) self._update_ctrl_list_timer.start() # Signal connections w = self._widget w.cm_combo.currentIndexChanged[str].connect(self._on_cm_change) def shutdown_plugin(self): self._update_cm_list_timer.stop() self._update_ctrl_list_timer.stop() self._popup_widget.hide() def save_settings(self, plugin_settings, instance_settings): instance_settings.set_value('cm_ns', self._cm_ns) def restore_settings(self, plugin_settings, instance_settings): # Restore last session's controller_manager, if present self._update_cm_list() cm_ns = instance_settings.value('cm_ns') cm_combo = self._widget.cm_combo cm_list = [cm_combo.itemText(i) for i in range(cm_combo.count())] try: idx = cm_list.index(cm_ns) cm_combo.setCurrentIndex(idx) except (ValueError): pass # def trigger_configuration(self): # Comment in to signal that the plugin has a way to configure # This will enable a setting button (gear icon) in each dock widget # title bar # Usually used to open a modal configuration dialog def _update_cm_list(self): update_combo(self._widget.cm_combo, self._list_cm()) def _on_cm_change(self, cm_ns): self._cm_ns = cm_ns # Setup services for communicating with the selected controller manager self._set_cm_services(cm_ns) # Controller lister for the selected controller manager if cm_ns: self._controller_lister = ControllerLister(cm_ns) self._update_controllers() else: self._controller_lister = None def _set_cm_services(self, cm_ns): if cm_ns: # NOTE: Persistent services are used for performance reasons. # If the controller manager dies, we detect it and disconnect from # it anyway load_srv_name = _append_ns(cm_ns, 'load_controller') self._load_srv = rospy.ServiceProxy(load_srv_name, LoadController, persistent=True) unload_srv_name = _append_ns(cm_ns, 'unload_controller') self._unload_srv = rospy.ServiceProxy(unload_srv_name, UnloadController, persistent=True) switch_srv_name = _append_ns(cm_ns, 'switch_controller') self._switch_srv = rospy.ServiceProxy(switch_srv_name, SwitchController, persistent=True) else: self._load_srv = None self._unload_srv = None self._switch_srv = None def _update_controllers(self): # Find controllers associated to the selected controller manager controllers = self._list_controllers() # Update controller display, if necessary if self._controllers != controllers: self._controllers = controllers self._show_controllers() # NOTE: Model is recomputed from scratch def _list_controllers(self): """ @return List of controllers associated to a controller manager namespace. Contains both stopped/running controllers, as returned by the C{list_controllers} service, plus uninitialized controllers with configurations loaded in the parameter server. @rtype [str] """ if not self._cm_ns: return [] # Add loaded controllers first controllers = self._controller_lister() # Append potential controller configs found in the parameter server all_ctrls_ns = _resolve_controllers_ns(self._cm_ns) for name in get_rosparam_controller_names(all_ctrls_ns): add_ctrl = not any(name == ctrl.name for ctrl in controllers) if add_ctrl: type_str = _rosparam_controller_type(all_ctrls_ns, name) uninit_ctrl = ControllerState(name=name, type=type_str, state='uninitialized') controllers.append(uninit_ctrl) return controllers def _show_controllers(self): table_view = self._widget.table_view self._table_model = ControllerTable(self._controllers, self._icons) table_view.setModel(self._table_model) def _on_ctrl_menu(self, pos): # Get data of selected controller row = self._widget.table_view.rowAt(pos.y()) if row < 0: return # Cursor is not under a valid item ctrl = self._controllers[row] # Show context menu menu = QMenu(self._widget.table_view) if ctrl.state == 'running': action_stop = menu.addAction(self._icons['stopped'], 'Stop') action_kill = menu.addAction(self._icons['uninitialized'], 'Stop and Unload') elif ctrl.state == 'stopped': action_start = menu.addAction(self._icons['running'], 'Start') action_unload = menu.addAction(self._icons['uninitialized'], 'Unload') elif ctrl.state == 'uninitialized': action_load = menu.addAction(self._icons['stopped'], 'Load') action_spawn = menu.addAction(self._icons['running'], 'Load and Start') action = menu.exec_(self._widget.table_view.mapToGlobal(pos)) # Evaluate user action if ctrl.state == 'running': if action is action_stop: self._stop_controller(ctrl.name) elif action is action_kill: self._stop_controller(ctrl.name) self._unload_controller(ctrl.name) elif ctrl.state == 'stopped': if action is action_start: self._start_controller(ctrl.name) elif action is action_unload: self._unload_controller(ctrl.name) elif ctrl.state == 'uninitialized': if action is action_load: self._load_controller(ctrl.name) if action is action_spawn: self._load_controller(ctrl.name) self._start_controller(ctrl.name) def _on_ctrl_info(self, index): popup = self._popup_widget ctrl = self._controllers[index.row()] popup.ctrl_name.setText(ctrl.name) popup.ctrl_type.setText(ctrl.type) res_model = QStandardItemModel() model_root = QStandardItem('Claimed Resources') res_model.appendRow(model_root) for hw_res in ctrl.claimed_resources: hw_iface_item = QStandardItem(hw_res.hardware_interface) model_root.appendRow(hw_iface_item) for res in hw_res.resources: res_item = QStandardItem(res) hw_iface_item.appendRow(res_item) popup.resource_tree.setModel(res_model) popup.resource_tree.setItemDelegate(FontDelegate(popup.resource_tree)) popup.resource_tree.expandAll() popup.move(QCursor.pos()) popup.show() def _on_header_menu(self, pos): header = self._widget.table_view.horizontalHeader() # Show context menu menu = QMenu(self._widget.table_view) action_toggle_auto_resize = menu.addAction('Toggle Auto-Resize') action = menu.exec_(header.mapToGlobal(pos)) # Evaluate user action if action is action_toggle_auto_resize: if header.resizeMode(0) == QHeaderView.ResizeToContents: header.setResizeMode(QHeaderView.Interactive) else: header.setResizeMode(QHeaderView.ResizeToContents) def _load_controller(self, name): self._load_srv.call(LoadControllerRequest(name=name)) def _unload_controller(self, name): self._unload_srv.call(UnloadControllerRequest(name=name)) def _start_controller(self, name): strict = SwitchControllerRequest.STRICT req = SwitchControllerRequest(start_controllers=[name], stop_controllers=[], strictness=strict) self._switch_srv.call(req) def _stop_controller(self, name): strict = SwitchControllerRequest.STRICT req = SwitchControllerRequest(start_controllers=[], stop_controllers=[name], strictness=strict) self._switch_srv.call(req)
class ControllerManager(Plugin): """ Graphical frontend for managing ros_control controllers. """ _cm_update_freq = 1 # Hz def __init__(self, context): super(ControllerManager, self).__init__(context) self.setObjectName('ControllerManager') # Create QWidget and extend it with all the attributes and children # from the UI file self._widget = QWidget() rp = rospkg.RosPack() ui_file = os.path.join(rp.get_path('rqt_controller_manager'), 'resource', 'controller_manager.ui') loadUi(ui_file, self._widget) self._widget.setObjectName('ControllerManagerUi') # Pop-up that displays controller information self._popup_widget = QWidget() ui_file = os.path.join(rp.get_path('rqt_controller_manager'), 'resource', 'controller_info.ui') loadUi(ui_file, self._popup_widget) self._popup_widget.setObjectName('ControllerInfoUi') # Show _widget.windowTitle on left-top of each plugin (when # it's set in _widget). This is useful when you open multiple # plugins at 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) # Initialize members self._cm_ns = [] # Namespace of the selected controller manager self._controllers = [] # State of each controller self._table_model = None self._controller_lister = None # Controller manager service proxies self._load_srv = None self._unload_srv = None self._switch_srv = None # Controller state icons rospack = rospkg.RosPack() path = rospack.get_path('rqt_controller_manager') self._icons = { 'running': QIcon(path + '/resource/led_green.png'), 'stopped': QIcon(path + '/resource/led_red.png'), 'uninitialized': QIcon(path + '/resource/led_off.png') } # Controllers display table_view = self._widget.table_view table_view.setContextMenuPolicy(Qt.CustomContextMenu) table_view.customContextMenuRequested.connect(self._on_ctrl_menu) table_view.doubleClicked.connect(self._on_ctrl_info) header = table_view.horizontalHeader() header.setResizeMode(QHeaderView.ResizeToContents) header.setContextMenuPolicy(Qt.CustomContextMenu) header.customContextMenuRequested.connect(self._on_header_menu) # Timer for controller manager updates self._list_cm = ControllerManagerLister() self._update_cm_list_timer = QTimer(self) self._update_cm_list_timer.setInterval(1000.0 / self._cm_update_freq) self._update_cm_list_timer.timeout.connect(self._update_cm_list) self._update_cm_list_timer.start() # Timer for running controller updates self._update_ctrl_list_timer = QTimer(self) self._update_ctrl_list_timer.setInterval(1000.0 / self._cm_update_freq) self._update_ctrl_list_timer.timeout.connect(self._update_controllers) self._update_ctrl_list_timer.start() # Signal connections w = self._widget w.cm_combo.currentIndexChanged[str].connect(self._on_cm_change) def shutdown_plugin(self): self._update_cm_list_timer.stop() self._update_ctrl_list_timer.stop() self._popup_widget.hide() def save_settings(self, plugin_settings, instance_settings): instance_settings.set_value('cm_ns', self._cm_ns) def restore_settings(self, plugin_settings, instance_settings): # Restore last session's controller_manager, if present self._update_cm_list() cm_ns = instance_settings.value('cm_ns') cm_combo = self._widget.cm_combo cm_list = [cm_combo.itemText(i) for i in range(cm_combo.count())] try: idx = cm_list.index(cm_ns) cm_combo.setCurrentIndex(idx) except (ValueError): pass # def trigger_configuration(self): # Comment in to signal that the plugin has a way to configure # This will enable a setting button (gear icon) in each dock widget # title bar # Usually used to open a modal configuration dialog def _update_cm_list(self): update_combo(self._widget.cm_combo, self._list_cm()) def _on_cm_change(self, cm_ns): self._cm_ns = cm_ns # Setup services for communicating with the selected controller manager self._set_cm_services(cm_ns) # Controller lister for the selected controller manager if cm_ns: self._controller_lister = ControllerLister(cm_ns) self._update_controllers() else: self._controller_lister = None def _set_cm_services(self, cm_ns): if cm_ns: # NOTE: Persistent services are used for performance reasons. # If the controller manager dies, we detect it and disconnect from # it anyway load_srv_name = _append_ns(cm_ns, 'load_controller') self._load_srv = rospy.ServiceProxy(load_srv_name, LoadController, persistent=True) unload_srv_name = _append_ns(cm_ns, 'unload_controller') self._unload_srv = rospy.ServiceProxy(unload_srv_name, UnloadController, persistent=True) switch_srv_name = _append_ns(cm_ns, 'switch_controller') self._switch_srv = rospy.ServiceProxy(switch_srv_name, SwitchController, persistent=True) else: self._load_srv = None self._unload_srv = None self._switch_srv = None def _update_controllers(self): # Find controllers associated to the selected controller manager controllers = self._list_controllers() # Update controller display, if necessary if self._controllers != controllers: self._controllers = controllers self._show_controllers() # NOTE: Model is recomputed from scratch def _list_controllers(self): """ @return List of controllers associated to a controller manager namespace. Contains both stopped/running controllers, as returned by the C{list_controllers} service, plus uninitialized controllers with configurations loaded in the parameter server. @rtype [str] """ if not self._cm_ns: return [] # Add loaded controllers first controllers = self._controller_lister() # Append potential controller configs found in the parameter server all_ctrls_ns = _resolve_controllers_ns(self._cm_ns) for name in get_rosparam_controller_names(all_ctrls_ns): add_ctrl = not any(name == ctrl.name for ctrl in controllers) if add_ctrl: type_str = _rosparam_controller_type(all_ctrls_ns, name) uninit_ctrl = ControllerState(name=name, type=type_str, state='uninitialized') controllers.append(uninit_ctrl) return controllers def _show_controllers(self): table_view = self._widget.table_view self._table_model = ControllerTable(self._controllers, self._icons) table_view.setModel(self._table_model) def _on_ctrl_menu(self, pos): # Get data of selected controller row = self._widget.table_view.rowAt(pos.y()) if row < 0: return # Cursor is not under a valid item ctrl = self._controllers[row] # Show context menu menu = QMenu(self._widget.table_view) if ctrl.state == 'running': action_stop = menu.addAction(self._icons['stopped'], 'Stop') action_kill = menu.addAction(self._icons['uninitialized'], 'Stop and Unload') elif ctrl.state == 'stopped': action_start = menu.addAction(self._icons['running'], 'Start') action_unload = menu.addAction(self._icons['uninitialized'], 'Unload') elif ctrl.state == 'uninitialized': action_load = menu.addAction(self._icons['stopped'], 'Load') action_spawn = menu.addAction(self._icons['running'], 'Load and Start') action = menu.exec_(self._widget.table_view.mapToGlobal(pos)) # Evaluate user action if ctrl.state == 'running': if action is action_stop: self._stop_controller(ctrl.name) elif action is action_kill: self._stop_controller(ctrl.name) self._unload_controller(ctrl.name) elif ctrl.state == 'stopped': if action is action_start: self._start_controller(ctrl.name) elif action is action_unload: self._unload_controller(ctrl.name) elif ctrl.state == 'uninitialized': if action is action_load: self._load_controller(ctrl.name) if action is action_spawn: self._load_controller(ctrl.name) self._start_controller(ctrl.name) def _on_ctrl_info(self, index): popup = self._popup_widget ctrl = self._controllers[index.row()] popup.ctrl_name.setText(ctrl.name) popup.ctrl_type.setText(ctrl.type) res_model = QStandardItemModel() model_root = QStandardItem('Claimed Resources') res_model.appendRow(model_root) for hw_res in ctrl.claimed_resources: hw_iface_item = QStandardItem(hw_res.hardware_interface) model_root.appendRow(hw_iface_item) for res in hw_res.resources: res_item = QStandardItem(res) hw_iface_item.appendRow(res_item) popup.resource_tree.setModel(res_model) popup.resource_tree.setItemDelegate(FontDelegate(popup.resource_tree)) popup.resource_tree.expandAll() popup.move(QCursor.pos()) popup.show() def _on_header_menu(self, pos): header = self._widget.table_view.horizontalHeader() # Show context menu menu = QMenu(self._widget.table_view) action_toggle_auto_resize = menu.addAction('Toggle Auto-Resize') action = menu.exec_(header.mapToGlobal(pos)) # Evaluate user action if action is action_toggle_auto_resize: if header.resizeMode(0) == QHeaderView.ResizeToContents: header.setResizeMode(QHeaderView.Interactive) else: header.setResizeMode(QHeaderView.ResizeToContents) def _load_controller(self, name): self._load_srv.call(LoadControllerRequest(name=name)) def _unload_controller(self, name): self._unload_srv.call(UnloadControllerRequest(name=name)) def _start_controller(self, name): strict = SwitchControllerRequest.STRICT req = SwitchControllerRequest(start_controllers=[name], stop_controllers=[], strictness=strict) self._switch_srv.call(req) def _stop_controller(self, name): strict = SwitchControllerRequest.STRICT req = SwitchControllerRequest(start_controllers=[], stop_controllers=[name], strictness=strict) self._switch_srv.call(req)