def handle_header_view_customContextMenuRequested(self, pos): # create context menu menu = QMenu(self) action_toggle_auto_resize = menu.addAction('Auto-Resize') action_toggle_auto_resize.setCheckable(True) auto_resize_flag = ( self.header().resizeMode(0) == QHeaderView.ResizeToContents) action_toggle_auto_resize.setChecked(auto_resize_flag) action_toggle_sorting = menu.addAction('Sorting') action_toggle_sorting.setCheckable(True) action_toggle_sorting.setChecked(self.isSortingEnabled()) # show menu action = menu.exec_(self.header().mapToGlobal(pos)) # evaluate user action if action is action_toggle_auto_resize: if auto_resize_flag: self.header().setResizeMode(QHeaderView.Interactive) else: self.header().setResizeMode(QHeaderView.ResizeToContents) elif action is action_toggle_sorting: self.setSortingEnabled(not self.isSortingEnabled())
def _add_exclude_filter(self, filter_index=False): """ :param filter_index: if false then this function shows a QMenu to allow the user to choose a type of message filter. ''bool'' OR :param filter_index: the index of the filter to be added, ''int'' :return: if a filter was added then the index is returned, ''int'' OR :return: if no filter was added then None is returned, ''NoneType'' """ if filter_index is False: filter_index = -1 filter_select_menu = QMenu() for index in self._filter_factory_order: # flattens the _exclude filters list and only adds the item if it doesn't # already exist if index in ['message', 'location'] or \ not self.filter_factory[index][1] in \ [type(item) for sublist in self._exclude_filters for item in sublist]: filter_select_menu.addAction(self.filter_factory[index][0]) action = filter_select_menu.exec_(QCursor.pos()) if action is None: return None for index in self._filter_factory_order: if self.filter_factory[index][0] == action.text(): filter_index = index if filter_index == -1: return None index = len(self._exclude_filters) newfilter = self.filter_factory[filter_index][1]() if len(self.filter_factory[filter_index]) >= 4: newwidget = self.filter_factory[filter_index][2]( newfilter, self._rospack, self.filter_factory[filter_index][3]) else: newwidget = self.filter_factory[filter_index][2](newfilter, self._rospack) # pack the new filter tuple onto the filter list self._exclude_filters.append( (newfilter, FilterWrapperWidget(newwidget, self.filter_factory[filter_index][0]), filter_index)) self._proxy_model.add_exclude_filter(newfilter) newfilter.filter_changed_signal.connect( self._proxy_model.handle_exclude_filters_changed) self._exclude_filters[index][1].delete_button.clicked.connect( self._delete_exclude_filter) self._model.rowsInserted.connect( self._exclude_filters[index][1].repopulate) # place the widget in the proper location self.exclude_table.insertRow(index) self.exclude_table.setCellWidget(index, 0, self._exclude_filters[index][1]) self.exclude_table.resizeColumnsToContents() self.exclude_table.resizeRowsToContents() newfilter.filter_changed_signal.emit() return index
def handle_header_view_customContextMenuRequested(self, pos): # create context menu menu = QMenu(self) action_toggle_auto_resize = menu.addAction('Auto-Resize') action_toggle_auto_resize.setCheckable(True) auto_resize_flag = (self.header().resizeMode(0) == QHeaderView.ResizeToContents) action_toggle_auto_resize.setChecked(auto_resize_flag) action_toggle_sorting = menu.addAction('Sorting') action_toggle_sorting.setCheckable(True) action_toggle_sorting.setChecked(self.isSortingEnabled()) # show menu action = menu.exec_(self.header().mapToGlobal(pos)) # evaluate user action if action is action_toggle_auto_resize: if auto_resize_flag: self.header().setResizeMode(QHeaderView.Interactive) else: self.header().setResizeMode(QHeaderView.ResizeToContents) elif action is action_toggle_sorting: self.setSortingEnabled(not self.isSortingEnabled())
def _gl_view_mouseReleaseEvent(self, event): if event.button() == Qt.RightButton: menu = QMenu(self._gl_view) action = QAction(self._gl_view.tr("Reset view"), self._gl_view) menu.addAction(action) action.triggered.connect(self._set_default_view) menu.exec_(self._gl_view.mapToGlobal(event.pos()))
def _define_menu(self): self._menu = QMenu(self._gl_view) layer_menu = QMenu('Layers', self._gl_view) for layer in self._mapDrawer.get_layers(): layer_menu.addAction(layer.menu_action) self._menu.addMenu(layer_menu) self._menu.addSeparator() self._view2dAction = QAction(self._gl_view.tr("2D View"), self._gl_view, triggered=self._set_default_view) self._menu.addAction(self._view2dAction) self._view3dAction = QAction(self._gl_view.tr("3D View"), self._gl_view, triggered=self._set_3d_view) self._menu.addAction(self._view3dAction) self._menu.addSeparator() #self._rotateSubAction = QAction(self._gl_view.tr("Rotate with Sub"), self, checkable=True, # triggered=self._rotate_with_sub) #self._menu.addAction(self._rotateSubAction) self._lockOnSubAction = QAction(self._gl_view.tr("Lock on Sub"), self, checkable=True, triggered=self._lock_on_sub) self._menu.addAction(self._lockOnSubAction) self._menu.addSeparator() self._menu.addAction(QAction('Reset Path', self, triggered=self._mapDrawer.reset_path)) setLocationAction = QAction('Set Location Target', self._gl_view, triggered=self._set_location_action) self._menu.addAction(setLocationAction)
def _rightclick_menu(self, event): """ :type event: QEvent """ # QTreeview.selectedIndexes() returns 0 when no node is selected. # This can happen when after booting no left-click has been made yet # (ie. looks like right-click doesn't count). These lines are the # workaround for that problem. selected = self._messages_tree.selectedIndexes() if len(selected) == 0: return menu = QMenu() text_action = QAction(self.tr('View Text'), menu) menu.addAction(text_action) raw_action = QAction(self.tr('View Raw'), menu) menu.addAction(raw_action) action = menu.exec_(event.globalPos()) if action == raw_action or action == text_action: rospy.logdebug('_rightclick_menu selected={}'.format(selected)) selected_type = selected[1].data() if selected_type[-2:] == '[]': selected_type = selected_type[:-2] browsetext = None try: if (self._mode == rosmsg.MODE_MSG or self._mode == rosaction.MODE_ACTION): browsetext = rosmsg.get_msg_text(selected_type, action == raw_action) elif self._mode == rosmsg.MODE_SRV: browsetext = rosmsg.get_srv_text(selected_type, action == raw_action) else: raise except rosmsg.ROSMsgException, e: QMessageBox.warning( self, self.tr('Warning'), self.tr('The selected item component ' + 'does not have text to view.')) if browsetext is not None: self._browsers.append( TextBrowseDialog(browsetext, self._rospack)) self._browsers[-1].show()
def _handle_column_right_click(self, pos): menu = QMenu(self) hide = menu.addAction('Hide Column') showall = menu.addAction('Show all columns') # Don't allow hiding the last column if self.table_view.horizontalHeader().count() - self.table_view.horizontalHeader().hiddenSectionCount() == 1: hide.setEnabled(False) ac = menu.exec_(self.table_view.horizontalHeader().mapToGlobal(pos)) if ac == hide: column = self.table_view.horizontalHeader().logicalIndexAt(pos.x()) self.table_view.horizontalHeader().hideSection(column) elif ac == showall: for i in range(self.table_view.horizontalHeader().count()): self.table_view.horizontalHeader().showSection(i)
def _rightclick_menu(self, event): """ :type event: QEvent """ # QTreeview.selectedIndexes() returns 0 when no node is selected. # This can happen when after booting no left-click has been made yet # (ie. looks like right-click doesn't count). These lines are the # workaround for that problem. selected = self._messages_tree.selectedIndexes() if len(selected) == 0: return menu = QMenu() text_action = QAction(self.tr('View Text'), menu) menu.addAction(text_action) raw_action = QAction(self.tr('View Raw'), menu) menu.addAction(raw_action) action = menu.exec_(event.globalPos()) if action == raw_action or action == text_action: rospy.logdebug('_rightclick_menu selected={}'.format(selected)) selected_type = selected[1].data() if selected_type[-2:] == '[]': selected_type = selected_type[:-2] browsetext = None try: if (self._mode == rosmsg.MODE_MSG or self._mode == rosaction.MODE_ACTION): browsetext = rosmsg.get_msg_text(selected_type, action == raw_action) elif self._mode == rosmsg.MODE_SRV: browsetext = rosmsg.get_srv_text(selected_type, action == raw_action) else: raise except rosmsg.ROSMsgException, e: QMessageBox.warning(self, self.tr('Warning'), self.tr('The selected item component ' + 'does not have text to view.')) if browsetext is not None: self._browsers.append(TextBrowseDialog(browsetext, self._rospack)) self._browsers[-1].show()
def _add_exclude_filter(self, filter_index=False): """ :param filter_index: if false then this function shows a QMenu to allow the user to choose a type of message filter. ''bool'' OR :param filter_index: the index of the filter to be added, ''int'' :return: if a filter was added then the index is returned, ''int'' OR :return: if no filter was added then None is returned, ''NoneType'' """ if filter_index is False: filter_index = -1 filter_select_menu = QMenu() for index in self._filter_factory_order: # flattens the _exclude filters list and only adds the item if it doesn't already exist if index in ['message', 'location'] or not self.filter_factory[index][1] in [type(item) for sublist in self._exclude_filters for item in sublist]: filter_select_menu.addAction(self.filter_factory[index][0]) action = filter_select_menu.exec_(QCursor.pos()) if action is None: return None for index in self._filter_factory_order: if self.filter_factory[index][0] == action.text(): filter_index = index if filter_index == -1: return None index = len(self._exclude_filters) newfilter = self.filter_factory[filter_index][1]() if len(self.filter_factory[filter_index]) >= 4: newwidget = self.filter_factory[filter_index][2](newfilter, self._rospack, self.filter_factory[filter_index][3]) else: newwidget = self.filter_factory[filter_index][2](newfilter, self._rospack) # pack the new filter tuple onto the filter list self._exclude_filters.append((newfilter, FilterWrapperWidget(newwidget, self.filter_factory[filter_index][0]), filter_index)) self._proxy_model.add_exclude_filter(newfilter) newfilter.filter_changed_signal.connect(self._proxy_model.handle_exclude_filters_changed) self._exclude_filters[index][1].delete_button.clicked.connect(self._delete_exclude_filter) self._model.rowsInserted.connect(self._exclude_filters[index][1].repopulate) # place the widget in the proper location self.exclude_table.insertRow(index) self.exclude_table.setCellWidget(index, 0, self._exclude_filters[index][1]) self.exclude_table.resizeColumnsToContents() self.exclude_table.resizeRowsToContents() newfilter.filter_changed_signal.emit() return index
def _show_context_menu(self, item, global_pos): if item is None: return # show context menu menu = QMenu(self) action_item_expand = menu.addAction(QIcon.fromTheme('zoom-in'), "Expand All Children") action_item_collapse = menu.addAction(QIcon.fromTheme('zoom-out'), "Collapse All Children") action = menu.exec_(global_pos) # evaluate user action if action in (action_item_expand, action_item_collapse): expanded = (action is action_item_expand) def recursive_set_expanded(item): item.setExpanded(expanded) for index in range(item.childCount()): recursive_set_expanded(item.child(index)) recursive_set_expanded(item)
def on_topics_tree_widget_customContextMenuRequested(self, pos): item = self.topics_tree_widget.itemAt(pos) if item is None: return # show context menu menu = QMenu(self) action_item_expand = menu.addAction(QIcon.fromTheme('zoom-in'), 'Expand All Children') action_item_collapse = menu.addAction(QIcon.fromTheme('zoom-out'), 'Collapse All Children') action = menu.exec_(self.topics_tree_widget.mapToGlobal(pos)) # evaluate user action if action in (action_item_expand, action_item_collapse): expanded = (action is action_item_expand) def recursive_set_expanded(item): item.setExpanded(expanded) for index in range(item.childCount()): recursive_set_expanded(item.child(index)) recursive_set_expanded(item)
def show_context_menu(self, point): menu = QMenu(self) waypoint_action = menu.addAction("Add Waypoint") replace_action = menu.addAction("Replace all waypoints") land_action = menu.addAction("Land here") clear_action = menu.addAction("Clear all waypoints") choice = menu.exec_(self.mapToGlobal(point)) clickX = point.x() clickY = point.y() lon = GoogleMapPlotter.pix_to_rel_lon(self.GMP.west, clickX, self.GMP.zoom) lat = GoogleMapPlotter.pix_to_rel_lat(self.GMP.north, clickY, self.GMP.zoom) n, e, d = PPPub.getNED(lat, lon) if choice == waypoint_action: self.add_waypoint(n, e, d) elif choice == replace_action: self.replace_waypoints(n, e, d) elif choice == clear_action: self.clear_waypoints() elif choice == land_action: self.add_land_waypoint([n, e, 0])
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 again') action_unload = menu.addAction(self._icons['uninitialized'], 'Unload') elif ctrl.state == 'initialized': 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' or ctrl.state == 'initialized': 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 handle_header_view_customContextMenuRequested(self, pos): header = self.topics_tree_widget.header() # show context menu menu = QMenu(self) 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 _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.setSectionResizeMode(QHeaderView.Interactive) else: header.setSectionResizeMode(QHeaderView.ResizeToContents)
def eventFilter(self, obj, event): if event.type() in self._event_callbacks: ret_val = self._event_callbacks[event.type()](obj, event) if ret_val is not None: return ret_val if event.type() == event.ContextMenu and obj == self.title_label: menu = QMenu(self) rename_action = menu.addAction(self.tr('Rename dock widget')) action = menu.exec_(self.mapToGlobal(event.pos())) if action == rename_action: self.title_label.hide() self.title_edit.setText(self.title_label.text()) self.title_edit.show() self.title_edit.setFocus() return True return QObject.eventFilter(self, obj, event)
class MenuDashWidget(IconToolButton): """ A widget which displays a pop-up menu when clicked :param name: The name to give this widget. :type name: str :param icon: The icon to display in this widgets button. :type icon: str """ def __init__(self, name, icons=None, clicked_icons=None, icon_paths=[]): if icons == None: icons = [['bg-grey.svg', 'ic-motors.svg']] super(MenuDashWidget, self).__init__(name, icons=icons, suppress_overlays=True, icon_paths=icon_paths) self.setStyleSheet( 'QToolButton::menu-indicator {image: url(none.jpg);} QToolButton {border: none;}' ) self.setPopupMode(QToolButton.InstantPopup) self.update_state(0) self.pressed.disconnect(self._pressed) self.released.disconnect(self._released) self._menu = QMenu() self._menu.aboutToHide.connect(self._released) self._menu.aboutToShow.connect(self._pressed) self.setMenu(self._menu) def add_separator(self): return self._menu.addSeparator() def add_action(self, name, callback): """ Add an action to the menu, and return the newly created action. :param name: The name of the action. :type name: str :param callback: Function to be called when this item is pressed. :type callback: callable """ return self._menu.addAction(name, callback)
class MenuDashWidget(IconToolButton): """ A widget which displays a pop-up menu when clicked :param name: The name to give this widget. :type name: str :param icon: The icon to display in this widgets button. :type icon: str """ def __init__(self, name, icons=None, clicked_icons=None, icon_paths=[]): if icons == None: icons = [['bg-grey.svg', 'ic-motors.svg']] super(MenuDashWidget, self).__init__(name, icons=icons, suppress_overlays=True, icon_paths=icon_paths) self.setStyleSheet('QToolButton::menu-indicator {image: url(none.jpg);} QToolButton {border: none;}') self.setPopupMode(QToolButton.InstantPopup) self.update_state(0) self.pressed.disconnect(self._pressed) self.released.disconnect(self._released) self._menu = QMenu() self._menu.aboutToHide.connect(self._released) self._menu.aboutToShow.connect(self._pressed) self.setMenu(self._menu) def add_separator(self): return self._menu.addSeparator() def add_action(self, name, callback): """ Add an action to the menu, and return the newly created action. :param name: The name of the action. :type name: str :param callback: Function to be called when this item is pressed. :type callback: callable """ return self._menu.addAction(name, callback)
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)
class VisionMainWidget(QWidget): def __init__(self): super(VisionMainWidget, self).__init__() try: rospy.wait_for_service('/provider_vision/get_information_list', timeout=2) rospy.wait_for_service('/provider_vision/get_filterchain_from_execution', timeout=2) rospy.wait_for_service('/provider_vision/get_media_from_execution', timeout=2) rospy.wait_for_service('/provider_vision/execute_cmd', timeout=2) except rospy.ROSException: rospy.loginfo('Services unavailable') ui_file = os.path.join(rospkg.RosPack().get_path('rqt_vision'), 'resource', 'mainwidget.ui') loadUi(ui_file, self) self.setWindowTitle('Vision UI') self._current_execution = None self._current_execution_subscriber = None self._current_execution_subscriber_result = None self._is_recording = False self._video_writer = None self._cv_image = None self._widget = None self.bridge = CvBridge() self.connect(self, SIGNAL("result_change(QString)"), self._handle_result) ##### Service self._srv_get_information_list = rospy.ServiceProxy('/provider_vision/get_information_list', get_information_list) self._srv_get_filterchain_from_execution = rospy.ServiceProxy('/provider_vision/get_filterchain_from_execution', get_filterchain_from_execution) self._srv_get_media_from_execution = rospy.ServiceProxy('/provider_vision/get_media_from_execution', get_media_from_execution) self._srv_execute_cmd = rospy.ServiceProxy('/provider_vision/execute_cmd', execute_cmd) ### self.image_frame_mouse_release_event_original = self.imageFrame.mouseReleaseEvent self.imageFrame.mouseReleaseEvent = self.image_frame_mouse_release_event self.refresh_button.clicked[bool].connect(self.fill_execution_list) self.current_execution.currentIndexChanged[int].connect(self.current_execution_index_changed) self.image_paint_event_original = self.imageFrame.paintEvent self.imageFrame.paintEvent = self.image_paint_event self.fill_execution_list() self._define_menu() def _define_menu(self): self._menu = QMenu(self.imageFrame) configureAction = QAction("Configure ...", self.imageFrame, triggered=self.configure_filterchain_action) self._menu.addAction(configureAction) self._menu.addSeparator() self._recordAction = QAction("Record ...", self.imageFrame, triggered=self.record_execution_action) self._menu.addAction(self._recordAction) self._stop_recordAction = QAction("Stop Record", self.imageFrame, triggered=self.stop_record_execution_action) self._stop_recordAction.setEnabled(False) self._menu.addAction(self._stop_recordAction) self._menu.addSeparator() deleteAction = QAction(self.imageFrame.tr("Delete this execution"), self.imageFrame, triggered=self.delete_current_execution) self._menu.addAction(deleteAction) def fill_execution_list(self): self._refresh_clean() self._current_execution = None self.current_execution.clear() execution_string = self._srv_get_information_list(1) execution_list = execution_string.list.split(';') if len(execution_list) == 0: return self._current_execution = execution_list[0] for execution in execution_list: if len(execution) > 0: self.current_execution.addItem(execution) def current_execution_index_changed(self, index): self._refresh_clean() if self._current_execution_subscriber is not None: self._current_execution_subscriber.unregister() self._current_execution_subscriber_result.unregister() new_execution = self.current_execution.itemText(index) self._current_execution = new_execution self._filterchain = self._srv_get_filterchain_from_execution(self._current_execution) self._current_execution_subscriber = rospy.Subscriber('/provider_vision/' + new_execution + '_image', SensorImage, self.current_execution_callback) self._current_execution_subscriber_result = rospy.Subscriber('/provider_vision/' + new_execution + '_result', VisionTarget, self.current_execution_result_callback) def _refresh_clean(self): if self._current_execution_subscriber is not None: self._current_execution_subscriber.unregister() self._current_execution_subscriber_result.unregister() self.result_text.setText('') self._cv_image = None self.imageFrame.update() def current_execution_callback(self, img): try: cv_image = self.bridge.imgmsg_to_cv2(img, desired_encoding="rgb8") height, width, channel = cv_image.shape bytesPerLine = 3 * width self._cv_image = QImage(cv_image.data, width, height, bytesPerLine, QImage.Format_RGB888) if self._is_recording: if self._video_writer is None: four_cc = cv2.cv.CV_FOURCC(*'HFYU') self._video_writer = cv2.VideoWriter(self._recording_file_name, four_cc, float(15), (width,height)) temp = cv2.cvtColor(cv_image,cv2.cv.CV_BGR2RGB) self._video_writer.write(temp) except CvBridgeError as e: print(e) self.imageFrame.update() def image_paint_event(self, data): self.image_paint_event_original(data) if self._cv_image is None: return; img = self._cv_image painter = QPainter(self.imageFrame) painter.drawImage(data.rect(), img) painter.end() def current_execution_result_callback(self, visionTarget): result = 'X:{}, Y:{}, width:{:.2f}, height:{:.2f}, angle:{:.2f}, desc_1:{}, desc_2:{}'.format(visionTarget.x, visionTarget.y, visionTarget.width, visionTarget.height, visionTarget.angle, visionTarget.desc_1, visionTarget.desc_2) self.emit(SIGNAL("result_change(QString)"), result) def _handle_result(self,result): self.result_text.setText(result) def image_frame_mouse_release_event(self,event): if event.button() == Qt.RightButton: self._menu.exec_(self.imageFrame.mapToGlobal(event.pos())) def delete_current_execution(self): if self._current_execution is None : return media = self._srv_get_media_from_execution(self._current_execution) self._srv_execute_cmd(self._current_execution,self._filterchain.list,media.list,2) self.fill_execution_list() def configure_filterchain_action(self): self._widget = ConfigureFilterchainWidget(self._current_execution, self._filterchain) self._widget.show() def shutdown_plugin(self): if self._current_execution_subscriber is not None: self._current_execution_subscriber.unregister() self._current_execution_subscriber_result.unregister() if self._widget is not None: self._widget.close() def record_execution_action(self): Tk().withdraw() filename = asksaveasfilename(defaultextension='.avi') self._recording_file_name = filename if len(self._recording_file_name) > 0: self._is_recording = True self._recordAction.setEnabled(False) self._stop_recordAction.setEnabled(True) def stop_record_execution_action(self): self._is_recording = False time.sleep(0.1) self._video_writer.release() self._video_writer = None self._recordAction.setEnabled(True) self._stop_recordAction.setEnabled(False)
class PlotWidget(QWidget): _interval = 500 def __init__(self, initial_topics=None, start_paused=False): super(PlotWidget, self).__init__() self.setObjectName('PlotWidget') self._initial_topics = initial_topics rp = rospkg.RosPack() ui_file = '/home/mohammad/Documents/CMU/RED/catkin_ws/src/rqt_safeguard/resource/plot.ui' loadUi(ui_file, self) self.subscribe_topic_button.setIcon(QIcon.fromTheme('list-add')) self.remove_topic_button.setIcon(QIcon.fromTheme('list-remove')) self.pause_button.setIcon(QIcon.fromTheme('media-playback-pause')) # self.clear_button.setIcon(QIcon.fromTheme('edit-clear')) # self.data_plot = None self.subscribe_topic_button.setEnabled(False) if start_paused: self.pause_button.setChecked(True) self._topic_completer = TopicCompleter(self.topic_edit) self._topic_completer.update_topics() self.topic_edit.setCompleter(self._topic_completer) self._start_time = rospy.get_time() self._rosdata = {} self._remove_topic_menu = QMenu() self.color_box.setStyleSheet("background-color: red;") # init and start update timer for plot self._update_timer = QTimer(self) self._update_timer.timeout.connect(self.update) # def switch_data_plot_widget(self, data_plot): # self.enable_timer(enabled=False) # self.data_plot_layout.removeWidget(self.data_plot) # if self.data_plot is not None: # self.data_plot.close() # self.data_plot = data_plot # self.data_plot_layout.addWidget(self.data_plot) # self.data_plot.autoscroll(self.autoscroll_checkbox.isChecked()) # # setup drag 'n drop # self.data_plot.dropEvent = self.dropEvent # self.data_plot.dragEnterEvent = self.dragEnterEvent # if self._initial_topics: # for topic_name in self._initial_topics: # self.add_topic(topic_name) # self._initial_topics = None # else: # for topic_name, rosdata in self._rosdata.items(): # data_x, data_y = rosdata.next() # self.data_plot.add_curve(topic_name, topic_name, data_x, data_y) # self._subscribed_topics_changed() @Slot('QDragEnterEvent*') def dragEnterEvent(self, event): # get topic name if not event.mimeData().hasText(): if not hasattr(event.source(), 'selectedItems') or len( event.source().selectedItems()) == 0: qWarning( 'Plot.dragEnterEvent(): not hasattr(event.source(), selectedItems) or len(event.source().selectedItems()) == 0' ) return item = event.source().selectedItems()[0] topic_name = item.data(0, Qt.UserRole) if topic_name == None: qWarning( 'Plot.dragEnterEvent(): not hasattr(item, ros_topic_name_)' ) return else: topic_name = str(event.mimeData().text()) # check for plottable field type plottable, message = is_plottable(topic_name) if plottable: event.acceptProposedAction() else: qWarning('Plot.dragEnterEvent(): rejecting: "%s"' % (message)) @Slot('QDropEvent*') def dropEvent(self, event): if event.mimeData().hasText(): topic_name = str(event.mimeData().text()) else: droped_item = event.source().selectedItems()[0] topic_name = str(droped_item.data(0, Qt.UserRole)) self.add_topic(topic_name) @Slot(str) def on_topic_edit_textChanged(self, topic_name): # on empty topic name, update topics if topic_name in ('', '/'): self._topic_completer.update_topics() plottable, message = is_plottable(topic_name) self.subscribe_topic_button.setEnabled(plottable) self.subscribe_topic_button.setToolTip(message) @Slot() def on_topic_edit_returnPressed(self): if self.subscribe_topic_button.isEnabled(): self.add_topic(str(self.topic_edit.text())) @Slot() def on_subscribe_topic_button_clicked(self): self.add_topic(str(self.topic_edit.text())) @Slot(bool) def on_pause_button_clicked(self, checked): self.enable_timer(not checked) print("pause") @Slot(bool) def on_autoscroll_checkbox_clicked(self, checked): # self.data_plot.autoscroll(checked) if checked: # self.data_plot.redraw() print("checked is clicked") # @Slot() # def on_clear_button_clicked(self): # print("Clear Button is clicked") # #self.clear_plot() def update(self): print("update") for topic_name, rosdata in self._rosdata.items(): decision, oriX = rosdata.next_dx() if oriX: self.oriX.setText(str(round(oriX, 2))) oriY, oriZ = rosdata.next_yz() if oriY: self.oriY.setText(str(round(oriY, 2))) if oriZ: self.oriZ.setText(str(round(oriZ, 2))) placX, placY = rosdata.next_pxy() if placX: self.placX.setText(str(round(100 * placX, 2))) if placY: self.placY.setText(str(round(100 * placY, 2))) placZ = rosdata.next_pz() if placZ: self.placZ.setText(str(round(100 * placZ, 2))) if decision: print(decision) self.color_box.setStyleSheet( "background-color: red; color: white; font: bold; font-size: 96px;" ) self.color_box.setText("Not Safe") else: print(decision) self.color_box.setStyleSheet( "background-color: green; color: white; font: bold; font-size: 96px;" ) self.color_box.setText("Safe") # if self.data_plot is not None: # needs_redraw = False # for topic_name, rosdata in self._rosdata.items(): # try: # data_x, data_y = rosdata.next() # if data_x or data_y: # self.data_plot.update_values(topic_name, data_x, data_y) # needs_redraw = True # except RosPlotException as e: # qWarning('PlotWidget.update_plot(): error in rosplot: %s' % e) # if needs_redraw: # self.data_plot.redraw() def _subscribed_topics_changed(self): self._update_remove_topic_menu() if not self.pause_button.isChecked(): # if pause button is not pressed, enable timer based on subscribed topics self.enable_timer() print("if not self.pause_button.isChecked(): IN WIDGET PY") #self.data_plot.redraw() def _update_remove_topic_menu(self): def make_remove_topic_function(x): return lambda: self.remove_topic(x) self._remove_topic_menu.clear() for topic_name in sorted(self._rosdata.keys()): action = QAction(topic_name, self._remove_topic_menu) action.triggered.connect(make_remove_topic_function(topic_name)) self._remove_topic_menu.addAction(action) if len(self._rosdata) > 1: all_action = QAction('All', self._remove_topic_menu) all_action.triggered.connect(self.clean_up_subscribers) self._remove_topic_menu.addAction(all_action) self.remove_topic_button.setMenu(self._remove_topic_menu) def add_topic(self, topic_name): self.enable_timer() topics_changed = False for topic_name in get_plot_fields(topic_name)[0]: if topic_name in self._rosdata: qWarning( 'PlotWidget.add_topic(): topic already subscribed: %s' % topic_name) continue self._rosdata[topic_name] = ROSData(topic_name, self._start_time) if self._rosdata[topic_name].error is not None: qWarning(str(self._rosdata[topic_name].error)) del self._rosdata[topic_name] else: # data_x, data_y = self._rosdata[topic_name].next() INJA TAGHIR BEDE UPDATE KON MAGHADIR RO #self.data_plot.add_curve(topic_name, topic_name, data_x, data_y) topics_changed = True if topics_changed: self._subscribed_topics_changed() def remove_topic(self, topic_name): self._rosdata[topic_name].close() del self._rosdata[topic_name] # self.data_plot.remove_curve(topic_name) self._subscribed_topics_changed() # def clear_plot(self): # for topic_name, _ in self._rosdata.items(): # self.data_plot.clear_values(topic_name) # self.data_plot.redraw() def clean_up_subscribers(self): for topic_name, rosdata in self._rosdata.items(): rosdata.close() # self.data_plot.remove_curve(topic_name) self._rosdata = {} self._subscribed_topics_changed() def enable_timer(self, enabled=True): if enabled: self._update_timer.start(self._interval) else: self._update_timer.stop() def alaki(self): print("alaki")
class PlotWidget(QWidget): _redraw_interval = 40 list_of_elements_in_plot = [] monitoring_topic_overview_list = [] def __init__(self, initial_topics=None, start_paused=False): super(PlotWidget, self).__init__() self.setObjectName('PlotWidget') self._initial_topics = initial_topics rp = rospkg.RosPack() ui_file = os.path.join(rp.get_path('monitoring_rqt_plot'), 'resource', 'plot.ui') loadUi(ui_file, self) self.subscribe_topic_button.setIcon(QIcon.fromTheme('list-add')) self.remove_topic_button.setIcon(QIcon.fromTheme('list-remove')) self.pause_button.setIcon(QIcon.fromTheme('media-playback-pause')) self.clear_button.setIcon(QIcon.fromTheme('edit-clear')) self.data_plot = None self.subscribe_topic_button.setEnabled(True) if start_paused: self.pause_button.setChecked(True) self._start_time = rospy.get_time() self._rosdata = {} self._remove_topic_menu = QMenu() # init and start update timer for plot self._update_plot_timer = QTimer(self) self._update_plot_timer.timeout.connect(self.update_plot) rospy.Subscriber("/monitoring/gui", Gui, self.monitoring_topic_overview_list_manager) def monitoring_topic_overview_list_manager(self, msg): for element in msg.infos: if not element.name in self.monitoring_topic_overview_list: self.monitoring_topic_overview_list.append(element.name) def switch_data_plot_widget(self, data_plot): self.enable_timer(enabled=False) self.data_plot_layout.removeWidget(self.data_plot) if self.data_plot is not None: self.data_plot.close() self.data_plot = data_plot self.data_plot_layout.addWidget(self.data_plot) self.data_plot.autoscroll(self.autoscroll_checkbox.isChecked()) self._subscribed_topics_changed() @Slot() def on_refresh_list_button_clicked(self): self.listWidget.clear() for element in self.monitoring_topic_overview_list: self.listWidget.addItem(element) @Slot() def on_subscribe_topic_button_clicked(self): node_value_name = self.listWidget.currentItem().text() if node_value_name not in self.list_of_elements_in_plot: self.add_topic(node_value_name) @Slot(bool) def on_pause_button_clicked(self, checked): self.enable_timer(not checked) @Slot(bool) def on_autoscroll_checkbox_clicked(self, checked): self.data_plot.autoscroll(checked) if checked: self.data_plot.redraw() @Slot() def on_clear_button_clicked(self): self.clear_plot() def update_plot(self): if self.data_plot is not None: needs_redraw = False for topic_name, rosdata in self._rosdata.items(): try: data_x, data_y = rosdata.next() if data_x or data_y: rospy.logout(data_x) rospy.logout(data_y) self.data_plot.update_values(topic_name, data_x, data_y) needs_redraw = True except RosPlotException as e: qWarning('PlotWidget.update_plot(): error in rosplot: %s' % e) if needs_redraw: self.data_plot.redraw() def _subscribed_topics_changed(self): self._update_remove_topic_menu() rospy.logout("_subscribed_topics_changed") if not self.pause_button.isChecked(): rospy.logout("enable timer") # if pause button is not pressed, enable timer based on subscribed topics self.enable_timer(self._rosdata) self.data_plot.redraw() def _update_remove_topic_menu(self): def make_remove_topic_function(x): return lambda: self.remove_topic(x) self._remove_topic_menu.clear() for topic_name in sorted(self._rosdata.keys()): node_value_name = topic_name action = QAction(node_value_name, self._remove_topic_menu) action.triggered.connect(make_remove_topic_function(topic_name)) self._remove_topic_menu.addAction(action) if len(self._rosdata) > 1: all_action = QAction('All', self._remove_topic_menu) all_action.triggered.connect(self.clean_up_subscribers) self._remove_topic_menu.addAction(all_action) self.remove_topic_button.setMenu(self._remove_topic_menu) def add_topic(self, node_value_name): topics_changed = False rospy.logwarn("new value with name: %s", node_value_name) self.list_of_elements_in_plot.append(node_value_name) self._rosdata[node_value_name] = ROSData(node_value_name, self._start_time) data_x, data_y = self._rosdata[node_value_name].next() self.data_plot.add_curve(node_value_name, node_value_name, data_x, data_y) topics_changed = True if topics_changed: self._subscribed_topics_changed() def remove_topic(self, topic_name): rospy.logout("remove topic") self._rosdata[topic_name].close() del self._rosdata[topic_name] self.data_plot.remove_curve(topic_name) rospy.logout("remove " + str(topic_name)) if topic_name in self.list_of_elements_in_plot: self.list_of_elements_in_plot.remove(topic_name) self._subscribed_topics_changed() def clear_plot(self): rospy.logout("clear plot") for topic_name, _ in self._rosdata.items(): self.data_plot.clear_values(topic_name) self.data_plot.redraw() def clean_up_subscribers(self): rospy.logout("clean up subscribers") for topic_name, rosdata in self._rosdata.items(): rosdata.close() self.data_plot.remove_curve(topic_name) if topic_name in self.list_of_elements_in_plot: print topic_name self.list_of_elements_in_plot.remove(topic_name) self._rosdata = {} self._subscribed_topics_changed() def enable_timer(self, enabled=True): if enabled: rospy.logout("Timer enabled") self._update_plot_timer.start(self._redraw_interval) else: rospy.logout("Timer stopped") self._update_plot_timer.stop()
class EditorWidget(QWidget): ''' This class is abstract -- its child classes should be instantiated. There exist two kinds of "update" methods: - _update_paramserver for Parameter Server. - update_value for the value displayed on GUI. ''' def __init__(self, updater, config): ''' @param updater: A class that extends threading.Thread. @type updater: rqt_reconfigure.param_updater.ParamUpdater ''' super(EditorWidget, self).__init__() self._updater = updater self.param_name = config['name'] self.param_default = config['default'] self.param_description = config['description'] self.old_value = None self.cmenu = QMenu() self.cmenu.addAction(self.tr('Set to Default')).triggered.connect( self._set_to_default) def _update_paramserver(self, value): ''' Update the value on Parameter Server. ''' if value != self.old_value: self.update_configuration(value) self.old_value = value def update_value(self, value): ''' To be implemented in subclass, but still used. Update the value that's displayed on the arbitrary GUI component based on user's input. This method is not called from the GUI thread, so any changes to QObjects will need to be done through a signal. ''' self.old_value = value def update_configuration(self, value): self._updater.update({self.param_name: value}) def display(self, grid): ''' Should be overridden in subclass. :type grid: QFormLayout ''' self._paramname_label.setText(self.param_name) # label_paramname = QLabel(self.param_name) # label_paramname.setWordWrap(True) self._paramname_label.setMinimumWidth(100) grid.addRow(self._paramname_label, self) self.setToolTip(self.param_description) self._paramname_label.setToolTip(self.param_description) self._paramname_label.contextMenuEvent = self.contextMenuEvent def close(self): ''' Should be overridden in subclass. ''' pass def _set_to_default(self): self._update_paramserver(self.param_default) def contextMenuEvent(self, e): self.cmenu.exec_(e.globalPos())
def _rightclick_menu(self, event): """ Dynamically builds the rightclick menu based on the unique column data from the passed in datamodel and then launches it modally :param event: the mouse event object, ''QMouseEvent'' """ severities = {} for severity, label in Message.SEVERITY_LABELS.items(): if severity in self._model.get_unique_severities(): severities[severity] = label nodes = sorted(self._model.get_unique_nodes()) topics = sorted(self._model.get_unique_topics()) # menutext entries turned into menutext = [] menutext.append([ self.tr('Exclude'), [[self.tr('Severity'), severities], [self.tr('Node'), nodes], [self.tr('Topic'), topics], [self.tr('Selected Message(s)')]] ]) menutext.append([ self.tr('Highlight'), [[self.tr('Severity'), severities], [self.tr('Node'), nodes], [self.tr('Topic'), topics], [self.tr('Selected Message(s)')]] ]) menutext.append([self.tr('Copy Selected')]) menutext.append([self.tr('Browse Selected')]) menu = QMenu() submenus = [] subsubmenus = [] for item in menutext: if len(item) > 1: submenus.append(QMenu(item[0], menu)) for subitem in item[1]: if len(subitem) > 1: subsubmenus.append(QMenu(subitem[0], submenus[-1])) if isinstance(subitem[1], dict): for key in sorted(subitem[1].keys()): action = subsubmenus[-1].addAction( subitem[1][key]) action.setData(key) else: for subsubitem in subitem[1]: subsubmenus[-1].addAction(subsubitem) submenus[-1].addMenu(subsubmenus[-1]) else: submenus[-1].addAction(subitem[0]) menu.addMenu(submenus[-1]) else: menu.addAction(item[0]) action = menu.exec_(event.globalPos()) if action is None or action == 0: return elif action.text() == self.tr('Browse Selected'): self._show_browsers() elif action.text() == self.tr('Copy Selected'): rowlist = [] for current in self.table_view.selectionModel().selectedIndexes(): rowlist.append(self._proxy_model.mapToSource(current).row()) copytext = self._model.get_selected_text(rowlist) if copytext is not None: clipboard = QApplication.clipboard() clipboard.setText(copytext) elif action.text() == self.tr('Selected Message(s)'): if action.parentWidget().title() == self.tr('Highlight'): self._process_highlight_exclude_filter(action.text(), 'Message', False) elif action.parentWidget().title() == self.tr('Exclude'): self._process_highlight_exclude_filter(action.text(), 'Message', True) else: raise RuntimeError( "Menu format corruption in ConsoleWidget._rightclick_menu()" ) else: # This processes the dynamic list entries (severity, node and topic) try: roottitle = action.parentWidget().parentWidget().title() except: raise RuntimeError( "Menu format corruption in ConsoleWidget._rightclick_menu()" ) if roottitle == self.tr('Highlight'): self._process_highlight_exclude_filter( action.text(), action.parentWidget().title(), False) elif roottitle == self.tr('Exclude'): self._process_highlight_exclude_filter( action.text(), action.parentWidget().title(), True) else: raise RuntimeError( "Unknown Root Action %s selected in ConsoleWidget._rightclick_menu()" % roottitle)
class NavigationMapWidget(QWidget): def __init__(self, plugin): super(NavigationMapWidget, self).__init__() rp = rospkg.RosPack() try: rospy.wait_for_service('/proc_control/set_global_target', timeout=2) except rospy.ROSException: False ui_file = os.path.join(rp.get_path('rqt_navigation_map'), 'resource', 'mainWidget.ui') loadUi(ui_file, self) self._plugin = plugin self._topic_name = None self._odom_subscriber = None # create GL view self._gl_view = GLWidget() self._gl_view.unsetCursor() self._mapDrawer = MapDrawer(self) self._position = (0.0, 0.0, 0.0) self._mapDrawer.set_position(self._position) self._orientation = quaternion_about_axis(45.0, (0.0, 0.0, 1.0)) self._mapDrawer.set_orientation(self._orientation,45) # add GL view to widget layout self.layout().addWidget(self._gl_view) self._rotate_with_sub_activated = False self._lock_on_sub_activated = False self._yaw = 0 self._odom_subscriber = rospy.Subscriber('/proc_navigation/odom', Odometry, self._odom_callback) self.position_target_subscriber = rospy.Subscriber('/proc_control/current_target', PositionTarget, self._position_target_callback) self.set_global_target = rospy.ServiceProxy('/proc_control/set_global_target', SetPositionTarget) self._define_menu() def _define_menu(self): self._menu = QMenu(self._gl_view) layer_menu = QMenu('Layers', self._gl_view) for layer in self._mapDrawer.get_layers(): layer_menu.addAction(layer.menu_action) self._menu.addMenu(layer_menu) self._menu.addSeparator() self._view2dAction = QAction(self._gl_view.tr("2D View"), self._gl_view, triggered=self._set_default_view) self._menu.addAction(self._view2dAction) self._view3dAction = QAction(self._gl_view.tr("3D View"), self._gl_view, triggered=self._set_3d_view) self._menu.addAction(self._view3dAction) self._menu.addSeparator() #self._rotateSubAction = QAction(self._gl_view.tr("Rotate with Sub"), self, checkable=True, # triggered=self._rotate_with_sub) #self._menu.addAction(self._rotateSubAction) self._lockOnSubAction = QAction(self._gl_view.tr("Lock on Sub"), self, checkable=True, triggered=self._lock_on_sub) self._menu.addAction(self._lockOnSubAction) self._menu.addSeparator() self._menu.addAction(QAction('Reset Path', self, triggered=self._mapDrawer.reset_path)) setLocationAction = QAction('Set Location Target', self._gl_view, triggered=self._set_location_action) self._menu.addAction(setLocationAction) def _position_target_callback(self, target): self._mapDrawer.drawTarget(target.X, target.Y, target.Z) def _odom_callback(self, odom_data): vehicle_position_x = odom_data.pose.pose.position.x vehicle_position_y = odom_data.pose.pose.position.y vehicle_position_z = odom_data.pose.pose.position.z self._position = (vehicle_position_x, vehicle_position_y, vehicle_position_z) self._mapDrawer.set_position(self._position) self._yaw = odom_data.pose.pose.orientation.z self._orientation = quaternion_about_axis(math.radians(self._yaw), (0.0, 0.0, 1.0)) self._mapDrawer.set_orientation(self._orientation,self._yaw) def save_settings(self, plugin_settings, instance_settings): self._mapDrawer.save_settings(plugin_settings,instance_settings) view_matrix_string = repr(self._gl_view.get_view_matrix()) instance_settings.set_value('view_matrix', view_matrix_string) instance_settings.set_value('lock_on_sub_activated', str(self._lock_on_sub_activated)) def restore_settings(self, plugin_settings, instance_settings): self._mapDrawer.restore_settings(plugin_settings,instance_settings) view_matrix_string = instance_settings.value('view_matrix') try: view_matrix = eval(view_matrix_string) except Exception: view_matrix = None if view_matrix is not None: self._gl_view.set_view_matrix(view_matrix) else: self._set_default_view() lock_on_sub = instance_settings.value('lock_on_sub_activated') == 'True' if lock_on_sub is None: print 'Nothing stored for lock_on_sub' else: self._lock_on_sub(lock_on_sub) self._lockOnSubAction.setChecked(lock_on_sub) #rotate_with_sub = instance_settings.value('rotate_with_sub_activated') == 'True' #if rotate_with_sub is None: # print 'Nothing stored for lock_on_sub' #else: # self._rotate_with_sub(rotate_with_sub) # self._rotateSubAction.setChecked(rotate_with_sub) def _set_default_view(self): self._gl_view.makeCurrent() self._gl_view.reset_view() self._gl_view.translate((0, 0, -800)) def _set_3d_view(self): self._gl_view.makeCurrent() self._gl_view.reset_view() self._gl_view.rotate((0, 0, 1), 0) self._gl_view.rotate((1, 0, 0), -75) self._gl_view.translate((-100, -100, -500)) def _rotate_with_sub(self, checked): self._rotate_with_sub_activated = checked self._mapDrawer.set_rotate_with_sub_activated(checked) def _lock_on_sub(self, checked): self._lock_on_sub_activated = checked self._mapDrawer.set_lock_on_sub_activated(checked) self._view2dAction.setEnabled(not checked) self._view3dAction.setEnabled(not checked) def _gl_view_mouseReleaseEvent(self, event): if event.button() == Qt.RightButton: self._event_3dPoint = event self._menu.exec_(self._gl_view.mapToGlobal(event.pos())) def _set_location_action(self): x_cursor = self._event_3dPoint.pos().x() y_cursor = self._gl_view.height() - self._event_3dPoint.pos().y() x, y, z = self._gl_view.unproject_mouse_on_scene(QPoint(x_cursor, y_cursor)) position_x = x / self._mapDrawer.resolution_meters position_y = y / self._mapDrawer.resolution_meters position_z = z / self._mapDrawer.resolution_meters self._mapDrawer.drawTarget(position_x, position_y, position_z) rospy.loginfo('Set Target selected at (%.2f, %.2f)', position_x, position_y) self.set_global_target(X=position_x, Y=position_y, Z=position_z, ROLL=0, PITCH=0, YAW=self._yaw) def shutdown_plugin(self): print 'Shutting down'
class EditorWidget(QWidget): ''' This class is abstract -- its child classes should be instantiated. There exist two kinds of "update" methods: - _update_paramserver for Parameter Server. - update_value for the value displayed on GUI. ''' def __init__(self, updater, config): ''' @param updater: A class that extends threading.Thread. @type updater: rqt_reconfigure.param_updater.ParamUpdater ''' super(EditorWidget, self).__init__() self._updater = updater self.param_name = config['name'] self.param_default = config['default'] self.param_description = config['description'] self.old_value = None self.cmenu = QMenu() self.cmenu.addAction(self.tr('Set to Default')).triggered.connect(self._set_to_default) def _update_paramserver(self, value): ''' Update the value on Parameter Server. ''' if value != self.old_value: self.update_configuration(value) self.old_value = value def update_value(self, value): ''' To be implemented in subclass, but still used. Update the value that's displayed on the arbitrary GUI component based on user's input. This method is not called from the GUI thread, so any changes to QObjects will need to be done through a signal. ''' self.old_value = value def update_configuration(self, value): self._updater.update({self.param_name: value}) def display(self, grid): ''' Should be overridden in subclass. :type grid: QFormLayout ''' self._paramname_label.setText(self.param_name) # label_paramname = QLabel(self.param_name) # label_paramname.setWordWrap(True) self._paramname_label.setMinimumWidth(100) grid.addRow(self._paramname_label, self) self.setToolTip(self.param_description) self._paramname_label.setToolTip(self.param_description) self._paramname_label.contextMenuEvent = self.contextMenuEvent def close(self): ''' Should be overridden in subclass. ''' pass def _set_to_default(self): self._update_paramserver(self.param_default) def contextMenuEvent(self, e): self.cmenu.exec_(e.globalPos())
class PlotWidget(QWidget): _redraw_interval = 40 def __init__(self, initial_topics=None, start_paused=False): super(PlotWidget, self).__init__() self.setObjectName('PlotWidget') # Available ros topics for plotting # keys are i=InitSub, s=StateSub, r=RCSub, p=PathSub, w=WaypointSub, # o=ObstacleSub, g=GPSDataSub, ci=ConInSub, cc=ConComSub self.message_dict = { 'Course angle vs. Commanded': [('cc', 'chi_c'), ('s', 'chi')], 'Roll angle vs. Commanded': [('ci', 'phi_c'), ('s', 'phi')], 'Pitch angle vs. Commanded': [('s', 'theta'), ('ci', 'theta_c')], 'Airspeed vs. Commanded': [('s', 'Va'), ('cc', 'Va_c')] } # # Available ros topics for plotting # self.message_dict = { # 'Course angle (rad)':'/state/chi', # 'Course angle commanded (rad)':'/controller_commands/chi_c', # 'Airspeed (m/s)':'/state/Va', # 'Angle of attack (rad)':'/state/alpha', # 'Slide slip angle (rad)':'/state/beta', # 'Roll angle (rad)':'/state/phi', # 'Pitch angle (rad)':'/state/theta', # 'Yaw angle (rad)':'/state/psi', # 'Body frame rollrate (rad/s)':'/state/p', # 'Body frame pitchrate (rad/s)':'/state/q', # 'Body frame yawrate (rad/s)':'/state/r', # 'Groundspeed (m/s)':'/state/Vg' # } #self._initial_topics = initial_topics rp = rospkg.RosPack() ui_file = os.path.join(PWD, 'resources', 'plot.ui') loadUi(ui_file, self) self.pause_button.setIcon(QIcon.fromTheme('media-playback-pause')) self.clear_button.setIcon(QIcon.fromTheme('edit-clear')) self.data_plot = None if start_paused: self.pause_button.setChecked(True) self._start_time = rospy.get_time() self._rosdata = {} self._current_key = '' self._current_topics = [] self._remove_topic_menu = QMenu() self._msgs.clear() self._msgs.addItems(self.message_dict.keys()) self._msgs.currentIndexChanged[str].connect( self._draw_graph) # <<<<<<< start here (also modify the dict) # init and start update timer for plot self._update_plot_timer = QTimer(self) self._update_plot_timer.timeout.connect(self.update_plot) def switch_data_plot_widget(self, data_plot): self.enable_timer(enabled=False) self.data_plot_layout.removeWidget(self.data_plot) if self.data_plot is not None: self.data_plot.close() self.data_plot = data_plot self.data_plot_layout.addWidget(self.data_plot) self.data_plot.autoscroll(True) # setup drag 'n drop self.data_plot.dropEvent = self.dropEvent self.data_plot.dragEnterEvent = self.dragEnterEvent ''' if self._initial_topics: for topic_name in self._initial_topics: self.add_topic(topic_name) self._initial_topics = None else: for topic_name, rosdata in self._rosdata.items(): data_x, data_y = rosdata.next() self.data_plot.add_curve(topic_name, topic_name, data_x, data_y) self._subscribed_topics_changed() ''' def _draw_graph(self): #print 'entering _draw_graph' # NOT GONNA WORRY ABOUT THIS! # plottable, message = is_plottable(self.message_dict[self._msgs.currentText()]) # <<<<<< will feed this a list if self._current_key and not (self._msgs.currentText() == self._current_key): for topic_tuple in self._current_topics: #print 'removing %s from plot' % topic # ------------------------ self.remove_topic(topic_tuple[0], topic_tuple[1]) self._current_topics = [] self._current_topics = [] self._current_key = self._msgs.currentText() #print 'current key:', self._current_key for topic_tuple in self.message_dict[self._current_key]: #topic = get_topic(topic_tuple) #print 'topic:', topic #if not (topic is None): self._current_topics.append(topic_tuple) #print 'adding %s to plot' % topic # -------------------------- self.add_topic(topic_tuple[0], topic_tuple[1]) self._subscribed_topics_changed() @Slot(bool) def on_pause_button_clicked(self, checked): self.enable_timer(not checked) @Slot() def on_clear_button_clicked(self): self.clear_plot() def update_plot(self): if self.data_plot is not None: needs_redraw = False for topic_name, rosdata in self._rosdata.items(): try: data_x, data_y = rosdata.next() if data_x or data_y: self.data_plot.update_values(topic_name, data_x, data_y) needs_redraw = True except RosPlotException as e: qWarning('PlotWidget.update_plot(): error in rosplot: %s' % e) if needs_redraw: self.data_plot.redraw() def _subscribed_topics_changed(self): #self._update_remove_topic_menu() # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< if not self.pause_button.isChecked(): # if pause button is not pressed, enable timer based on subscribed topics self.enable_timer(self._rosdata) self.data_plot.redraw() def _update_remove_topic_menu(self): def make_remove_topic_function(x): return lambda: self.remove_topic(x) self._remove_topic_menu.clear() for topic_name in sorted(self._rosdata.keys()): action = QAction(topic_name, self._remove_topic_menu) action.triggered.connect(make_remove_topic_function(topic_name)) self._remove_topic_menu.addAction(action) if len(self._rosdata) > 1: all_action = QAction('All', self._remove_topic_menu) all_action.triggered.connect(self.clean_up_subscribers) self._remove_topic_menu.addAction(all_action) def add_topic(self, topic_code, topic_item): topics_changed = False topic_name = topic_code + '/' + topic_item #for topic_name in get_plot_fields(topic_name)[0]: # <<<<<<<< this is what allows for multiple topics if topic_name in self._rosdata: qWarning('PlotWidget.add_topic(): topic already subscribed: %s' % topic_name) #continue return self._rosdata[topic_name] = ROSData(topic_code, topic_item, self._start_time) if self._rosdata[topic_name].error is not None: qWarning(str(self._rosdata[topic_name].error)) del self._rosdata[topic_name] else: data_x, data_y = self._rosdata[topic_name].next() self.data_plot.add_curve(topic_name, topic_name, data_x, data_y) #print self._rosdata.items() # ------------------------------------------ topics_changed = True #if topics_changed: # self._subscribed_topics_changed() def remove_topic(self, topic_code, topic_item): topic_name = topic_code + '/' + topic_item self._rosdata[topic_name].close() del self._rosdata[topic_name] self.data_plot.remove_curve(topic_name) self._subscribed_topics_changed() def clear_plot(self): for topic_name, _ in self._rosdata.items(): self.data_plot.clear_values(topic_name) self.data_plot.redraw() def clean_up_subscribers(self): for topic_name, rosdata in self._rosdata.items(): rosdata.close() self.data_plot.remove_curve(topic_name) self._rosdata = {} self._subscribed_topics_changed() def enable_timer(self, enabled=True): if enabled: self._update_plot_timer.start(self._redraw_interval) else: self._update_plot_timer.stop()
def __init__(self, timeline, event, menu_topic): super(TimelinePopupMenu, self).__init__() self.parent = timeline self.timeline = timeline if menu_topic is not None: self.setTitle(menu_topic) self._menu_topic = menu_topic else: self._menu_topic = None self._reset_timeline = self.addAction('Reset Timeline') self._play_all = self.addAction('Play All Messages') self._play_all.setCheckable(True) self._play_all.setChecked(self.timeline.play_all) self.addSeparator() self._renderers = self.timeline._timeline_frame.get_renderers() self._thumbnail_actions = [] # create thumbnail menu items if menu_topic is None: submenu = self.addMenu('Thumbnails...') self._thumbnail_show_action = submenu.addAction('Show All') self._thumbnail_hide_action = submenu.addAction('Hide All') submenu.addSeparator() for topic, renderer in self._renderers: self._thumbnail_actions.append(submenu.addAction(topic)) self._thumbnail_actions[-1].setCheckable(True) self._thumbnail_actions[-1].setChecked(self.timeline._timeline_frame.is_renderer_active(topic)) else: self._thumbnail_show_action = None self._thumbnail_hide_action = None for topic, renderer in self._renderers: if menu_topic == topic: self._thumbnail_actions.append(self.addAction("Thumbnail")) self._thumbnail_actions[-1].setCheckable(True) self._thumbnail_actions[-1].setChecked(self.timeline._timeline_frame.is_renderer_active(topic)) # create view menu items self._topic_actions = [] self._type_actions = [] if menu_topic is None: self._topics = self.timeline._timeline_frame.topics view_topics_menu = self.addMenu('View (by Topic)') for topic in self._topics: datatype = self.timeline.get_datatype(topic) # View... / topic topic_menu = QMenu(topic, self) viewer_types = self.timeline._timeline_frame.get_viewer_types(datatype) # View... / topic / Viewer for viewer_type in viewer_types: tempaction = topic_menu.addAction(viewer_type.name) tempaction.setData(viewer_type) self._topic_actions.append(tempaction) view_topics_menu.addMenu(topic_menu) view_type_menu = self.addMenu('View (by Type)') self._topics_by_type = self.timeline._timeline_frame._topics_by_datatype for datatype in self._topics_by_type: # View... / datatype datatype_menu = QMenu(datatype, self) datatype_topics = self._topics_by_type[datatype] viewer_types = self.timeline._timeline_frame.get_viewer_types(datatype) for topic in [t for t in self._topics if t in datatype_topics]: # use timeline ordering topic_menu = QMenu(topic, datatype_menu) # View... / datatype / topic / Viewer for viewer_type in viewer_types: tempaction = topic_menu.addAction(viewer_type.name) tempaction.setData(viewer_type) self._topic_actions.append(tempaction) datatype_menu.addMenu(topic_menu) view_type_menu.addMenu(datatype_menu) else: view_menu = self.addMenu("View") datatype = self.timeline.get_datatype(menu_topic) viewer_types = self.timeline._timeline_frame.get_viewer_types(datatype) for viewer_type in viewer_types: tempaction = view_menu.addAction(viewer_type.name) tempaction.setData(viewer_type) self._topic_actions.append(tempaction) self.addSeparator() # create publish menu items self._publish_actions = [] if menu_topic is None: submenu = self.addMenu('Publish...') self._publish_all = submenu.addAction('Publish All') self._publish_none = submenu.addAction('Publish None') submenu.addSeparator() for topic in self._topics: self._publish_actions.append(submenu.addAction(topic)) self._publish_actions[-1].setCheckable(True) self._publish_actions[-1].setChecked(self.timeline.is_publishing(topic)) else: self._publish_actions.append(self.addAction("Publish")) self._publish_actions[-1].setCheckable(True) self._publish_actions[-1].setChecked(self.timeline.is_publishing(menu_topic)) self._publish_all = None self._publish_none = None action = self.exec_(event.globalPos()) if action is not None and action != 0: self.process(action)
class Plot3DWidget(QWidget): _redraw_interval = 40 def __init__(self, initial_topics=None, start_paused=False, buffer_length=100, use_poly=True, no_legend=False): super(Plot3DWidget, self).__init__() self.setObjectName('Plot3DWidget') self._buffer_length = buffer_length self._initial_topics = initial_topics rp = rospkg.RosPack() ui_file = os.path.join(rp.get_path('jsk_rqt_plugins'), 'resource', 'plot3d.ui') loadUi(ui_file, self) self.subscribe_topic_button.setIcon(QIcon.fromTheme('add')) self.remove_topic_button.setIcon(QIcon.fromTheme('remove')) self.pause_button.setIcon(QIcon.fromTheme('media-playback-pause')) self.clear_button.setIcon(QIcon.fromTheme('edit-clear')) self.data_plot = MatDataPlot3D(self, self._buffer_length, use_poly, no_legend) self.data_plot_layout.addWidget(self.data_plot) self.data_plot.autoscroll(self.autoscroll_checkbox.isChecked()) self.data_plot.dropEvent = self.dropEvent self.data_plot.dragEnterEvent = self.dragEnterEvent self.subscribe_topic_button.setEnabled(False) if start_paused: self.pause_button.setChecked(True) self._topic_completer = TopicCompleter(self.topic_edit) self._topic_completer.update_topics() self.topic_edit.setCompleter(self._topic_completer) self._start_time = rospy.get_time() self._rosdata = {} self._remove_topic_menu = QMenu() # init and start update timer for plot self._update_plot_timer = QTimer(self) self._update_plot_timer.timeout.connect(self.update_plot) if self._initial_topics: for topic_name in self._initial_topics: self.add_topic(topic_name) self._initial_topics = None @Slot('QDragEnterEvent*') def dragEnterEvent(self, event): # get topic name if not event.mimeData().hasText(): if not hasattr(event.source(), 'selectedItems') or \ len(event.source().selectedItems()) == 0: qWarning( 'Plot.dragEnterEvent(): not hasattr(event.source(), selectedItems) or len(event.source().selectedItems()) == 0' ) # NOQA return item = event.source().selectedItems()[0] topic_name = item.data(0, Qt.UserRole) if topic_name == None: qWarning( 'Plot.dragEnterEvent(): not hasattr(item, ros_topic_name_)' ) # NOQA return else: topic_name = str(event.mimeData().text()) # check for numeric field type is_numeric, is_array, message = is_slot_numeric(topic_name) if is_numeric and not is_array: event.acceptProposedAction() else: qWarning('Plot.dragEnterEvent(): rejecting: "%s"' % (message)) @Slot('QDropEvent*') def dropEvent(self, event): if event.mimeData().hasText(): topic_name = str(event.mimeData().text()) else: droped_item = event.source().selectedItems()[0] topic_name = str(droped_item.data(0, Qt.UserRole)) self.add_topic(topic_name) @Slot(str) def on_topic_edit_textChanged(self, topic_name): # on empty topic name, update topics if topic_name in ('', '/'): self._topic_completer.update_topics() is_numeric, is_array, message = is_slot_numeric(topic_name) self.subscribe_topic_button.setEnabled(is_numeric and not is_array) self.subscribe_topic_button.setToolTip(message) @Slot() def on_topic_edit_returnPressed(self): if self.subscribe_topic_button.isEnabled(): self.add_topic(str(self.topic_edit.text())) @Slot() def on_subscribe_topic_button_clicked(self): self.add_topic(str(self.topic_edit.text())) @Slot(bool) def on_pause_button_clicked(self, checked): self.enable_timer(not checked) @Slot(bool) def on_autoscroll_checkbox_clicked(self, checked): self.data_plot.autoscroll(checked) @Slot() def on_clear_button_clicked(self): self.clean_up_subscribers() def update_plot(self): if self.data_plot is not None: needs_redraw = False for topic_name, rosdata in self._rosdata.items(): try: data_x, data_y = rosdata.next() if data_x or data_y: self.data_plot.update_values(topic_name, data_x, data_y) needs_redraw = True except RosPlotException as e: qWarning('PlotWidget.update_plot(): error in rosplot: %s' % e) if needs_redraw: self.data_plot.redraw() def _subscribed_topics_changed(self): self._update_remove_topic_menu() if not self.pause_button.isChecked(): # if pause button is not pressed, # enable timer based on subscribed topics self.enable_timer(self._rosdata) def _update_remove_topic_menu(self): def make_remove_topic_function(x): return lambda: self.remove_topic(x) self._remove_topic_menu.clear() for topic_name in sorted(self._rosdata.keys()): action = QAction(topic_name, self._remove_topic_menu) action.triggered.connect(make_remove_topic_function(topic_name)) self._remove_topic_menu.addAction(action) self.remove_topic_button.setMenu(self._remove_topic_menu) def add_topic(self, topic_name): if topic_name in self._rosdata: qWarning('PlotWidget.add_topic(): topic already subscribed: %s' % topic_name) # NOQA return self._rosdata[topic_name] = ROSData(topic_name, self._start_time) if self._rosdata[topic_name].error is not None: qWarning(str(self._rosdata[topic_name].error)) del self._rosdata[topic_name] else: data_x, data_y = self._rosdata[topic_name].next() self.data_plot.add_curve(topic_name, topic_name, data_x, data_y) self._subscribed_topics_changed() def remove_topic(self, topic_name): self._rosdata[topic_name].close() del self._rosdata[topic_name] self.data_plot.remove_curve(topic_name) self._subscribed_topics_changed() def clean_up_subscribers(self): for topic_name, rosdata in self._rosdata.items(): rosdata.close() self.data_plot.remove_curve(topic_name) self._rosdata = {} self._subscribed_topics_changed() def enable_timer(self, enabled=True): if enabled: self._update_plot_timer.start(self._redraw_interval) else: self._update_plot_timer.stop()
def _rightclick_menu(self, event): """ :type event: QEvent """ # QTreeview.selectedIndexes() returns 0 when no node is selected. # This can happen when after booting no left-click has been made yet # (ie. looks like right-click doesn't count). These lines are the # workaround for that problem. selected = self._messages_tree.selectedIndexes() if len(selected) == 0: return menu = QMenu() text_action = QAction(self.tr('View Text'), menu) menu.addAction(text_action) remove_action = QAction(self.tr('Remove message'), menu) menu.addAction(remove_action) action = menu.exec_(event.globalPos()) if action == text_action: self._logger.debug('_rightclick_menu selected={}'.format(selected)) selected_type = selected[1].data() # We strip any array information for loading the python classes selected_type_bare = selected_type if selected_type_bare.find('[') >= 0: selected_type_bare = selected_type_bare[:selected_type_bare. find('[')] # We use the number of '/' to determine of the selected type is a msg, action, srv, # or primitive type. # NOTE (mlautman - 2/4/19) this heuristic seems brittle and should be removed selected_type_bare_tokens_len = len(selected_type_bare.split('/')) # We only want the base class so we transform eg. pkg1/my_srv/Request -> pkg1/my_srv if selected_type_bare_tokens_len > 2: selected_type_bare = "/".join( selected_type_bare.split('/')[:2]) browsetext = None # If the type does not have '/'s then we treat it as a primitive type if selected_type_bare_tokens_len == 1: browsetext = selected_type # if the type has a single '/' then we treat it as a msg type elif selected_type_bare_tokens_len == 2: msg_class = get_message_class(selected_type_bare) browsetext = get_message_text_from_class(msg_class) # If the type has two '/'s then we treat it as a srv or action type elif selected_type_bare_tokens_len == 3: if self._mode == message_helpers.SRV_MODE: msg_class = get_service_class(selected_type_bare) browsetext = get_service_text_from_class(msg_class) elif self._mode == message_helpers.ACTION_MODE: msg_class = get_action_class(selected_type_bare) browsetext = get_action_text_from_class(msg_class) else: self._logger.warn("Unrecognized value for self._mode: {} " "for selected_type: {}".format( self._mode, selected_type)) else: self._logger.warn( "Invalid selected_type: {}".format(selected_type)) if browsetext is not None: self._browsers.append(TextBrowseDialog(browsetext)) self._browsers[-1].show() if action == remove_action: self._messages_tree.model().removeRow(selected[0].row())
def _rightclick_menu(self, event): """ Dynamically builds the rightclick menu based on the unique column data from the passed in datamodel and then launches it modally :param event: the mouse event object, ''QMouseEvent'' """ severities = {} for severity, label in Message.SEVERITY_LABELS.items(): if severity in self._model.get_unique_severities(): severities[severity] = label nodes = sorted(self._model.get_unique_nodes()) topics = sorted(self._model.get_unique_topics()) # menutext entries turned into menutext = [] menutext.append([self.tr('Exclude'), [[self.tr('Severity'), severities], [self.tr('Node'), nodes], [self.tr('Topic'), topics], [self.tr('Selected Message(s)')]]]) menutext.append([self.tr('Highlight'), [[self.tr('Severity'), severities], [self.tr('Node'), nodes], [self.tr('Topic'), topics], [self.tr('Selected Message(s)')]]]) menutext.append([self.tr('Copy Selected')]) menutext.append([self.tr('Browse Selected')]) menu = QMenu() submenus = [] subsubmenus = [] for item in menutext: if len(item) > 1: submenus.append(QMenu(item[0], menu)) for subitem in item[1]: if len(subitem) > 1: subsubmenus.append(QMenu(subitem[0], submenus[-1])) if isinstance(subitem[1], dict): for key in sorted(subitem[1].keys()): action = subsubmenus[-1].addAction(subitem[1][key]) action.setData(key) else: for subsubitem in subitem[1]: subsubmenus[-1].addAction(subsubitem) submenus[-1].addMenu(subsubmenus[-1]) else: submenus[-1].addAction(subitem[0]) menu.addMenu(submenus[-1]) else: menu.addAction(item[0]) action = menu.exec_(event.globalPos()) if action is None or action == 0: return elif action.text() == self.tr('Browse Selected'): self._show_browsers() elif action.text() == self.tr('Copy Selected'): rowlist = [] for current in self.table_view.selectionModel().selectedIndexes(): rowlist.append(self._proxy_model.mapToSource(current).row()) copytext = self._model.get_selected_text(rowlist) if copytext is not None: clipboard = QApplication.clipboard() clipboard.setText(copytext) elif action.text() == self.tr('Selected Message(s)'): if action.parentWidget().title() == self.tr('Highlight'): self._process_highlight_exclude_filter(action.text(), 'Message', False) elif action.parentWidget().title() == self.tr('Exclude'): self._process_highlight_exclude_filter(action.text(), 'Message', True) else: raise RuntimeError("Menu format corruption in ConsoleWidget._rightclick_menu()") else: # This processes the dynamic list entries (severity, node and topic) try: roottitle = action.parentWidget().parentWidget().title() except: raise RuntimeError("Menu format corruption in ConsoleWidget._rightclick_menu()") if roottitle == self.tr('Highlight'): self._process_highlight_exclude_filter(action.text(), action.parentWidget().title(), False) elif roottitle == self.tr('Exclude'): self._process_highlight_exclude_filter(action.text(), action.parentWidget().title(), True) else: raise RuntimeError("Unknown Root Action %s selected in ConsoleWidget._rightclick_menu()" % roottitle)
def __init__(self, timeline, event, menu_topic): super(TimelinePopupMenu, self).__init__() self.parent = timeline self.timeline = timeline if menu_topic is not None: self.setTitle(menu_topic) self._menu_topic = menu_topic else: self._menu_topic = None self._reset_timeline = self.addAction('Reset Timeline') self._play_all = self.addAction('Play All Messages') self._play_all.setCheckable(True) self._play_all.setChecked(self.timeline.play_all) self.addSeparator() self._renderers = self.timeline._timeline_frame.get_renderers() self._thumbnail_actions = [] # create thumbnail menu items if menu_topic is None: submenu = self.addMenu('Thumbnails...') self._thumbnail_show_action = submenu.addAction('Show All') self._thumbnail_hide_action = submenu.addAction('Hide All') submenu.addSeparator() for topic, renderer in self._renderers: self._thumbnail_actions.append(submenu.addAction(topic)) self._thumbnail_actions[-1].setCheckable(True) self._thumbnail_actions[-1].setChecked( self.timeline._timeline_frame.is_renderer_active(topic)) else: self._thumbnail_show_action = None self._thumbnail_hide_action = None for topic, renderer in self._renderers: if menu_topic == topic: self._thumbnail_actions.append(self.addAction("Thumbnail")) self._thumbnail_actions[-1].setCheckable(True) self._thumbnail_actions[-1].setChecked( self.timeline._timeline_frame.is_renderer_active( topic)) # create view menu items self._topic_actions = [] self._type_actions = [] if menu_topic is None: self._topics = self.timeline._timeline_frame.topics view_topics_menu = self.addMenu('View (by Topic)') for topic in self._topics: datatype = self.timeline.get_datatype(topic) # View... / topic topic_menu = QMenu(topic, self) viewer_types = self.timeline._timeline_frame.get_viewer_types( datatype) # View... / topic / Viewer for viewer_type in viewer_types: tempaction = topic_menu.addAction(viewer_type.name) tempaction.setData(viewer_type) self._topic_actions.append(tempaction) view_topics_menu.addMenu(topic_menu) view_type_menu = self.addMenu('View (by Type)') self._topics_by_type = self.timeline._timeline_frame._topics_by_datatype for datatype in self._topics_by_type: # View... / datatype datatype_menu = QMenu(datatype, self) datatype_topics = self._topics_by_type[datatype] viewer_types = self.timeline._timeline_frame.get_viewer_types( datatype) for topic in [t for t in self._topics if t in datatype_topics ]: # use timeline ordering topic_menu = QMenu(topic, datatype_menu) # View... / datatype / topic / Viewer for viewer_type in viewer_types: tempaction = topic_menu.addAction(viewer_type.name) tempaction.setData(viewer_type) self._topic_actions.append(tempaction) datatype_menu.addMenu(topic_menu) view_type_menu.addMenu(datatype_menu) else: view_menu = self.addMenu("View") datatype = self.timeline.get_datatype(menu_topic) viewer_types = self.timeline._timeline_frame.get_viewer_types( datatype) for viewer_type in viewer_types: tempaction = view_menu.addAction(viewer_type.name) tempaction.setData(viewer_type) self._topic_actions.append(tempaction) self.addSeparator() # create publish menu items self._publish_actions = [] if menu_topic is None: submenu = self.addMenu('Publish...') self._publish_all = submenu.addAction('Publish All') self._publish_none = submenu.addAction('Publish None') submenu.addSeparator() for topic in self._topics: self._publish_actions.append(submenu.addAction(topic)) self._publish_actions[-1].setCheckable(True) self._publish_actions[-1].setChecked( self.timeline.is_publishing(topic)) else: self._publish_actions.append(self.addAction("Publish")) self._publish_actions[-1].setCheckable(True) self._publish_actions[-1].setChecked( self.timeline.is_publishing(menu_topic)) self._publish_all = None self._publish_none = None action = self.exec_(event.globalPos()) if action is not None and action != 0: self.process(action)
class PlotWidget(QWidget): _redraw_interval = 40 def __init__(self, node, initial_topics=None, start_paused=False): super(PlotWidget, self).__init__() self.setObjectName('PlotWidget') self._node = node self._initial_topics = initial_topics _, package_path = get_resource('packages', 'rqt_plot') ui_file = os.path.join(package_path, 'share', 'rqt_plot', 'resource', 'plot.ui') loadUi(ui_file, self) self.subscribe_topic_button.setIcon(QIcon.fromTheme('list-add')) self.remove_topic_button.setIcon(QIcon.fromTheme('list-remove')) self.pause_button.setIcon(QIcon.fromTheme('media-playback-pause')) self.clear_button.setIcon(QIcon.fromTheme('edit-clear')) self.data_plot = None self.subscribe_topic_button.setEnabled(False) if start_paused: self.pause_button.setChecked(True) self._topic_completer = TopicCompleter(self.topic_edit) self._topic_completer.update_topics(node) self.topic_edit.setCompleter(self._topic_completer) self._start_time = time.time() self._rosdata = {} self._remove_topic_menu = QMenu() # init and start update timer for plot self._update_plot_timer = QTimer(self) self._update_plot_timer.timeout.connect(self.update_plot) def switch_data_plot_widget(self, data_plot): self.enable_timer(enabled=False) self.data_plot_layout.removeWidget(self.data_plot) if self.data_plot is not None: self.data_plot.close() self.data_plot = data_plot self.data_plot_layout.addWidget(self.data_plot) self.data_plot.autoscroll(self.autoscroll_checkbox.isChecked()) # setup drag 'n drop self.data_plot.dropEvent = self.dropEvent self.data_plot.dragEnterEvent = self.dragEnterEvent if self._initial_topics: for topic_name in self._initial_topics: self.add_topic(topic_name) self._initial_topics = None else: for topic_name, rosdata in self._rosdata.items(): data_x, data_y = rosdata.next() self.data_plot.add_curve(topic_name, topic_name, data_x, data_y) self._subscribed_topics_changed() @Slot('QDragEnterEvent*') def dragEnterEvent(self, event): # get topic name if not event.mimeData().hasText(): if not hasattr(event.source(), 'selectedItems') or \ len(event.source().selectedItems()) == 0: qWarning( 'Plot.dragEnterEvent(): not hasattr(event.source(), selectedItems) or ' 'len(event.source().selectedItems()) == 0') return item = event.source().selectedItems()[0] topic_name = item.data(0, Qt.UserRole) if topic_name == None: qWarning( 'Plot.dragEnterEvent(): not hasattr(item, ros_topic_name_)' ) return else: topic_name = str(event.mimeData().text()) # check for plottable field type plottable, message = is_plottable(self._node, topic_name) if plottable: event.acceptProposedAction() else: qWarning('Plot.dragEnterEvent(): rejecting: "%s"' % (message)) @Slot('QDropEvent*') def dropEvent(self, event): if event.mimeData().hasText(): topic_name = str(event.mimeData().text()) else: droped_item = event.source().selectedItems()[0] topic_name = str(droped_item.data(0, Qt.UserRole)) self.add_topic(topic_name) @Slot(str) def on_topic_edit_textChanged(self, topic_name): # on empty topic name, update topics if topic_name in ('', '/'): self._topic_completer.update_topics(self._node) plottable, message = is_plottable(self._node, topic_name) self.subscribe_topic_button.setEnabled(plottable) self.subscribe_topic_button.setToolTip(message) @Slot() def on_topic_edit_returnPressed(self): if self.subscribe_topic_button.isEnabled(): self.add_topic(str(self.topic_edit.text())) @Slot() def on_subscribe_topic_button_clicked(self): self.add_topic(str(self.topic_edit.text())) @Slot(bool) def on_pause_button_clicked(self, checked): self.enable_timer(not checked) @Slot(bool) def on_autoscroll_checkbox_clicked(self, checked): self.data_plot.autoscroll(checked) if checked: self.data_plot.redraw() @Slot() def on_clear_button_clicked(self): self.clear_plot() def update_plot(self): if self.data_plot is not None: needs_redraw = False for topic_name, rosdata in self._rosdata.items(): try: data_x, data_y = rosdata.next() if data_x or data_y: self.data_plot.update_values(topic_name, data_x, data_y) needs_redraw = True except RosPlotException as e: qWarning('PlotWidget.update_plot(): error in rosplot: %s' % e) if needs_redraw: self.data_plot.redraw() def _subscribed_topics_changed(self): self._update_remove_topic_menu() if not self.pause_button.isChecked(): # if pause button is not pressed, enable timer based on subscribed topics self.enable_timer(self._rosdata) self.data_plot.redraw() def _update_remove_topic_menu(self): def make_remove_topic_function(x): return lambda: self.remove_topic(x) self._remove_topic_menu.clear() for topic_name in sorted(self._rosdata.keys()): action = QAction(topic_name, self._remove_topic_menu) action.triggered.connect(make_remove_topic_function(topic_name)) self._remove_topic_menu.addAction(action) if len(self._rosdata) > 1: all_action = QAction('All', self._remove_topic_menu) all_action.triggered.connect(self.clean_up_subscribers) self._remove_topic_menu.addAction(all_action) self.remove_topic_button.setMenu(self._remove_topic_menu) def add_topic(self, topic_name): topics_changed = False for topic_name in get_plot_fields(self._node, topic_name)[0]: if topic_name in self._rosdata: qWarning( 'PlotWidget.add_topic(): topic already subscribed: %s' % topic_name) continue self._rosdata[topic_name] = ROSData(self._node, topic_name, self._start_time) if self._rosdata[topic_name].error is not None: qWarning(str(self._rosdata[topic_name].error)) del self._rosdata[topic_name] else: data_x, data_y = self._rosdata[topic_name].next() self.data_plot.add_curve(topic_name, topic_name, data_x, data_y) topics_changed = True if topics_changed: self._subscribed_topics_changed() def remove_topic(self, topic_name): self._rosdata[topic_name].close() del self._rosdata[topic_name] self.data_plot.remove_curve(topic_name) self._subscribed_topics_changed() def clear_plot(self): for topic_name, _ in self._rosdata.items(): self.data_plot.clear_values(topic_name) self.data_plot.redraw() def clean_up_subscribers(self): for topic_name, rosdata in self._rosdata.items(): rosdata.close() self.data_plot.remove_curve(topic_name) self._rosdata = {} self._subscribed_topics_changed() def enable_timer(self, enabled=True): if enabled: self._update_plot_timer.start(self._redraw_interval) else: self._update_plot_timer.stop()
class PlotWidget(QWidget): _redraw_interval = 40 def __init__(self, initial_topics=None, start_paused=False): super(PlotWidget, self).__init__() self.setObjectName('PlotWidget') self._initial_topics = initial_topics rp = rospkg.RosPack() ui_file = os.path.join(rp.get_path('rqt_plot'), 'resource', 'plot.ui') loadUi(ui_file, self) self.subscribe_topic_button.setIcon(QIcon.fromTheme('list-add')) self.remove_topic_button.setIcon(QIcon.fromTheme('list-remove')) self.pause_button.setIcon(QIcon.fromTheme('media-playback-pause')) self.clear_button.setIcon(QIcon.fromTheme('edit-clear')) self.data_plot = None self.subscribe_topic_button.setEnabled(False) if start_paused: self.pause_button.setChecked(True) self._topic_completer = TopicCompleter(self.topic_edit) self._topic_completer.update_topics() self.topic_edit.setCompleter(self._topic_completer) self._start_time = rospy.get_time() self._rosdata = {} self._remove_topic_menu = QMenu() # init and start update timer for plot self._update_plot_timer = QTimer(self) self._update_plot_timer.timeout.connect(self.update_plot) def switch_data_plot_widget(self, data_plot): self.enable_timer(enabled=False) self.data_plot_layout.removeWidget(self.data_plot) if self.data_plot is not None: self.data_plot.close() self.data_plot = data_plot self.data_plot_layout.addWidget(self.data_plot) self.data_plot.autoscroll(self.autoscroll_checkbox.isChecked()) # setup drag 'n drop self.data_plot.dropEvent = self.dropEvent self.data_plot.dragEnterEvent = self.dragEnterEvent if self._initial_topics: for topic_name in self._initial_topics: self.add_topic(topic_name) self._initial_topics = None else: for topic_name, rosdata in self._rosdata.items(): data_x, data_y = rosdata.next() self.data_plot.add_curve(topic_name, topic_name, data_x, data_y) self._subscribed_topics_changed() @Slot('QDragEnterEvent*') def dragEnterEvent(self, event): # get topic name if not event.mimeData().hasText(): if not hasattr(event.source(), 'selectedItems') or len(event.source().selectedItems()) == 0: qWarning('Plot.dragEnterEvent(): not hasattr(event.source(), selectedItems) or len(event.source().selectedItems()) == 0') return item = event.source().selectedItems()[0] topic_name = item.data(0, Qt.UserRole) if topic_name == None: qWarning('Plot.dragEnterEvent(): not hasattr(item, ros_topic_name_)') return else: topic_name = str(event.mimeData().text()) # check for plottable field type plottable, message = is_plottable(topic_name) if plottable: event.acceptProposedAction() else: qWarning('Plot.dragEnterEvent(): rejecting: "%s"' % (message)) @Slot('QDropEvent*') def dropEvent(self, event): if event.mimeData().hasText(): topic_name = str(event.mimeData().text()) else: droped_item = event.source().selectedItems()[0] topic_name = str(droped_item.data(0, Qt.UserRole)) self.add_topic(topic_name) @Slot(str) def on_topic_edit_textChanged(self, topic_name): # on empty topic name, update topics if topic_name in ('', '/'): self._topic_completer.update_topics() plottable, message = is_plottable(topic_name) self.subscribe_topic_button.setEnabled(plottable) self.subscribe_topic_button.setToolTip(message) @Slot() def on_topic_edit_returnPressed(self): if self.subscribe_topic_button.isEnabled(): self.add_topic(str(self.topic_edit.text())) @Slot() def on_subscribe_topic_button_clicked(self): self.add_topic(str(self.topic_edit.text())) @Slot(bool) def on_pause_button_clicked(self, checked): self.enable_timer(not checked) @Slot(bool) def on_autoscroll_checkbox_clicked(self, checked): self.data_plot.autoscroll(checked) if checked: self.data_plot.redraw() @Slot() def on_clear_button_clicked(self): self.clear_plot() def update_plot(self): if self.data_plot is not None: needs_redraw = False for topic_name, rosdata in self._rosdata.items(): try: data_x, data_y = rosdata.next() if data_x or data_y: self.data_plot.update_values(topic_name, data_x, data_y) needs_redraw = True except RosPlotException as e: qWarning('PlotWidget.update_plot(): error in rosplot: %s' % e) if needs_redraw: self.data_plot.redraw() def _subscribed_topics_changed(self): self._update_remove_topic_menu() if not self.pause_button.isChecked(): # if pause button is not pressed, enable timer based on subscribed topics self.enable_timer(self._rosdata) self.data_plot.redraw() def _update_remove_topic_menu(self): def make_remove_topic_function(x): return lambda: self.remove_topic(x) self._remove_topic_menu.clear() for topic_name in sorted(self._rosdata.keys()): action = QAction(topic_name, self._remove_topic_menu) action.triggered.connect(make_remove_topic_function(topic_name)) self._remove_topic_menu.addAction(action) if len(self._rosdata) > 1: all_action = QAction('All', self._remove_topic_menu) all_action.triggered.connect(self.clean_up_subscribers) self._remove_topic_menu.addAction(all_action) self.remove_topic_button.setMenu(self._remove_topic_menu) def add_topic(self, topic_name): topics_changed = False for topic_name in get_plot_fields(topic_name)[0]: if topic_name in self._rosdata: qWarning('PlotWidget.add_topic(): topic already subscribed: %s' % topic_name) continue self._rosdata[topic_name] = ROSData(topic_name, self._start_time) if self._rosdata[topic_name].error is not None: qWarning(str(self._rosdata[topic_name].error)) del self._rosdata[topic_name] else: data_x, data_y = self._rosdata[topic_name].next() self.data_plot.add_curve(topic_name, topic_name, data_x, data_y) topics_changed = True if topics_changed: self._subscribed_topics_changed() def remove_topic(self, topic_name): self._rosdata[topic_name].close() del self._rosdata[topic_name] self.data_plot.remove_curve(topic_name) self._subscribed_topics_changed() def clear_plot(self): for topic_name, _ in self._rosdata.items(): self.data_plot.clear_values(topic_name) self.data_plot.redraw() def clean_up_subscribers(self): for topic_name, rosdata in self._rosdata.items(): rosdata.close() self.data_plot.remove_curve(topic_name) self._rosdata = {} self._subscribed_topics_changed() def enable_timer(self, enabled=True): if enabled: self._update_plot_timer.start(self._redraw_interval) else: self._update_plot_timer.stop()