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)
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)