def __init__(self, map_topic='/map', paths=None, polygons=None): super(NavViewWidget, self).__init__() if paths is None: paths = [ '/move_base/NavFn/plan', '/move_base/TrajectoryPlannerROS/local_plan' ] if polygons is None: polygons = ['/move_base/local_costmap/robot_footprint'] self._layout = QVBoxLayout() self._button_layout = QHBoxLayout() self.setAcceptDrops(True) self.setWindowTitle('Navigation Viewer') self.paths = paths self.polygons = polygons self.map_topic = map_topic self._tf = tf.TransformListener() self._set_pose = QPushButton('Set Pose') self._set_goal = QPushButton('Set Goal') self._button_layout.addWidget(self._set_pose) self._button_layout.addWidget(self._set_goal) self._layout.addLayout(self._button_layout) self._nav_view = None self.setLayout(self._layout)
def __init__(self, parent=None): """Create a new, empty DataPlot This will raise a RuntimeError if none of the supported plotting backends can be found """ super(DataPlot, self).__init__(parent) self._plot_index = 0 self._color_index = 0 self._markers_on = False self._autoscroll = True self._autoscale_x = True self._autoscale_y = DataPlot.SCALE_ALL # the backend widget that we're trying to hide/abstract self._data_plot_widget = None self._curves = {} self._vline = None self._redraw.connect(self._do_redraw) self._layout = QHBoxLayout() self.setLayout(self._layout) enabled_plot_types = [pt for pt in self.plot_types if pt['enabled']] if not enabled_plot_types: version_info = ' and PySide > 1.1.0' if QT_BINDING == 'pyside' else '' raise RuntimeError( 'No usable plot type found. Install at least one of: PyQtGraph, MatPlotLib (at least 1.1.0%s) or Python-Qwt5.' % version_info) self._switch_data_plot_widget(self._plot_index) self.show()
def __init__(self, reconf, node_name): """ :type reconf: dynamic_reconfigure.client :type node_name: str """ group_desc = reconf.get_group_descriptions() logging.debug('ParamClientWidget.group_desc=%s', group_desc) super(ParamClientWidget, self).__init__(ParamUpdater(reconf), group_desc, node_name) # Save and load buttons self.button_widget = QWidget(self) self.button_header = QHBoxLayout(self.button_widget) self.button_header.setContentsMargins(QMargins(0, 0, 0, 0)) self.load_button = QPushButton() self.save_button = QPushButton() self.load_button.setIcon(QIcon.fromTheme('document-open')) self.save_button.setIcon(QIcon.fromTheme('document-save')) self.load_button.clicked[bool].connect(self._handle_load_clicked) self.save_button.clicked[bool].connect(self._handle_save_clicked) self.button_header.addWidget(self.save_button) self.button_header.addWidget(self.load_button) self.setMinimumWidth(150) self.reconf = reconf self.updater.start() self.reconf.config_callback = self.config_callback self._node_grn = node_name
def __init__(self, aircraft_id): super(ChecklistWindow, self).__init__() # Set properties of the window self.setWindowTitle("BTO and BPO Checklist") self.resize(500, 700) self.move(200,100) self.checklist_state = 0 # Relative path for the default BPO and BTO checklist BPO_checklist_file = os.path.join(rospkg.RosPack().get_path('yonah_rqt'), 'src/yonah_rqt', 'BPO_checklist.csv') BTO_checklist_file = os.path.join(rospkg.RosPack().get_path('yonah_rqt'), 'src/yonah_rqt', 'BTO_checklist.csv') # Check whether checklist is present, if not print a error message to terminal try: # If checklist is present, parse it and pass it to its respective variable self.BPO_checklist = self.excel_parser(BPO_checklist_file) self.BTO_checklist = self.excel_parser(BTO_checklist_file) except: rospy.logerr("Checklist files are missing or named wrongly. Please follow the original directory and naming") exit() # Create the layout self.main_layout = QVBoxLayout() self.buttons_layout = QHBoxLayout() self.tree_widget_layout = QHBoxLayout() # Create the widgets self.create_widget() self.has_message_opened = 0 # Add the widgets into the layouts self.main_layout.addLayout(self.tree_widget_layout) self.main_layout.addLayout(self.buttons_layout) self.setLayout(self.main_layout)
def __init__(self, check_state_changed_callback, topic_name, parent=None, is_topic = False): super(TreeWidgetItem, self).__init__(parent) self._check_state_changed_callback = check_state_changed_callback self._topic_name = topic_name self.setCheckState(CHECK_COLUMN, Qt.Unchecked) self._is_topic = is_topic self._slider = QSlider(Qt.Horizontal) self._hbox = QHBoxLayout() self._min_label = QLabel("min") self._max_label = QLabel("max") self._hbox.addWidget(self._min_label) self._hbox.addWidget(self._slider) self._hbox.addWidget(self._max_label) tree = self.treeWidget() widget = QWidget() widget.setLayout(self._hbox) # tree.setItemWidget(self, 3, widget) self.setTextAlignment(self._column_names.index("min"), Qt.AlignRight | Qt.AlignVCenter) self.setTextAlignment(self._column_names.index("max"), Qt.AlignLeft | Qt.AlignVCenter) self.setTextAlignment(self._column_names.index("value"), Qt.AlignHCenter | Qt.AlignVCenter) self.setTextAlignment(self._column_names.index("type"), Qt.AlignHCenter | Qt.AlignVCenter) self.setFlags(Qt.ItemIsEditable | Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsUserCheckable ) self._slider.valueChanged.connect(self.sliderValueChanged)
def __init__(self, map_topic='/map', paths=['/move_base/NavFn/plan', '/move_base/TrajectoryPlannerROS/local_plan'], polygons=['/move_base/local_costmap/robot_footprint']): super(NavViewWidget, self).__init__() self._layout = QVBoxLayout() self._button_layout = QHBoxLayout() self.setAcceptDrops(True) self.setWindowTitle('Navigation Viewer') self.paths = paths self.polygons = polygons self.map = map_topic self._tf = tf.TransformListener() self._nav_view = NavView(map_topic, paths, polygons, tf = self._tf, parent = self) self._set_pose = QPushButton('Set Pose') self._set_pose.clicked.connect(self._nav_view.pose_mode) self._set_goal = QPushButton('Set Goal') self._set_goal.clicked.connect(self._nav_view.goal_mode) self._button_layout.addWidget(self._set_pose) self._button_layout.addWidget(self._set_goal) self._layout.addLayout(self._button_layout) self._layout.addWidget(self._nav_view) self.setLayout(self._layout)
def __init__(self, parent=None, current_values=None): super(BlacklistDialog, self).__init__(parent) self.setWindowTitle("Blacklist") vbox = QVBoxLayout() self.setLayout(vbox) self._blacklist = Blacklist() if isinstance(current_values, list): for val in current_values: self._blacklist.append(val) vbox.addWidget(self._blacklist) controls_layout = QHBoxLayout() add_button = QPushButton(icon=QIcon.fromTheme('list-add')) rem_button = QPushButton(icon=QIcon.fromTheme('list-remove')) ok_button = QPushButton("Ok") cancel_button = QPushButton("Cancel") add_button.clicked.connect(self._add_item) rem_button.clicked.connect(self._remove_item) ok_button.clicked.connect(self.accept) cancel_button.clicked.connect(self.reject) controls_layout.addWidget(add_button) controls_layout.addWidget(rem_button) controls_layout.addStretch(0) controls_layout.addWidget(ok_button) controls_layout.addWidget(cancel_button) vbox.addLayout(controls_layout)
def __init__(self): super(ServiceTabbedButtonGeneralWidget, self).__init__() self._tab_settings = None if rospy.has_param("~tabbed_layout"): tabbed_layout = rospy.get_param("~tabbed_layout") self._tab_list = [] if not 'tab_list' in tabbed_layout: self.showError("Cannot find tab_list in %s" % (tabbed_layout)) return tab_list = tabbed_layout['tab_list'] for tb in tab_list: if tb in tabbed_layout: param_settings = tabbed_layout[tb] settings = {} ## if 'type' in param_settings: settings['type'] = param_settings['type'] ## if not 'name' in param_settings: settings['name'] = tb else: settings['name'] = param_settings['name'] ## if 'yaml_file' in param_settings: settings['yaml_file'] = param_settings['yaml_file'] else: self.showError("Cannot find yaml_file in %s" % (tb)) settings = None ## if 'namespace' in param_settings: settings['namespace'] = param_settings['namespace'] if settings: self._tab_list.append(settings) else: self.showError("Cannot find key %s in %s" % (tb, tabbed_layout)) else: self.showError("Cannot find rosparam ~tabbed_layout") return if len(self._tab_list) == 0: self.showError("there is no valid param in ~tabbed_layout") return qtab = QTabWidget() for tb in self._tab_list: wg = ServiceButtonGeneralWidget_in_tab(tb) qtab.addTab(wg, tb['name']) #self.setWindowTitle('Tab Layout') hbox = QHBoxLayout() hbox.addWidget(qtab) self.setLayout(hbox) self.show()
def __init__(self, updater, config, nodename): """ :param config: :type config: Dictionary? defined in dynamic_reconfigure.client.Client :type nodename: str """ super(GroupWidget, self).__init__() self.state = config['state'] self.param_name = config['name'] self._toplevel_treenode_name = nodename # TODO: .ui file needs to be back into usage in later phase. # ui_file = os.path.join(rp.get_path('rqt_reconfigure'), # 'resource', 'singlenode_parameditor.ui') # loadUi(ui_file, self) verticalLayout = QVBoxLayout(self) verticalLayout.setContentsMargins(QMargins(0, 0, 0, 0)) _widget_nodeheader = QWidget() _h_layout_nodeheader = QHBoxLayout(_widget_nodeheader) _h_layout_nodeheader.setContentsMargins(QMargins(0, 0, 0, 0)) self.nodename_qlabel = QLabel(self) font = QFont('Trebuchet MS, Bold') font.setUnderline(True) font.setBold(True) # Button to close a node. _icon_disable_node = QIcon.fromTheme('window-close') _bt_disable_node = QPushButton(_icon_disable_node, '', self) _bt_disable_node.setToolTip('Hide this node') _bt_disable_node_size = QSize(36, 24) _bt_disable_node.setFixedSize(_bt_disable_node_size) _bt_disable_node.pressed.connect(self._node_disable_bt_clicked) _h_layout_nodeheader.addWidget(self.nodename_qlabel) _h_layout_nodeheader.addWidget(_bt_disable_node) self.nodename_qlabel.setAlignment(Qt.AlignCenter) font.setPointSize(10) self.nodename_qlabel.setFont(font) grid_widget = QWidget(self) self.grid = QFormLayout(grid_widget) verticalLayout.addWidget(_widget_nodeheader) verticalLayout.addWidget(grid_widget, 1) # Again, these UI operation above needs to happen in .ui file. self.tab_bar = None # Every group can have one tab bar self.tab_bar_shown = False self.updater = updater self.editor_widgets = [] self._param_names = [] self._create_node_widgets(config) logging.debug('Groups node name={}'.format(nodename)) self.nodename_qlabel.setText(nodename)
def __init__(self, active_aircrafts): super(WaypointWindow, self).__init__() self.setWindowTitle("Ground Control Station") self.adjustSize() self.move(200, 100) self.waypoint_plaintext_dict = {} self.main_layout = QHBoxLayout() self.progressbar_layout = QVBoxLayout() self.progressbar_layout.setAlignment(Qt.AlignTop) # Why we separate this is so that we can refresh the layout self.create_layout(active_aircrafts) self.main_layout.addLayout(self.progressbar_layout) self.setLayout(self.main_layout)
def __init__(self, reconf, node_name): """ :type reconf: dynamic_reconfigure.client :type node_name: str """ group_desc = reconf.get_group_descriptions() rospy.logdebug('DynreconfClientWidget.group_desc=%s', group_desc) super(DynreconfClientWidget, self).__init__(ParamUpdater(reconf), group_desc, node_name) # Save and load buttons self.button_widget = QWidget(self) self.button_header = QHBoxLayout(self.button_widget) self.button_header.setContentsMargins(QMargins(0, 0, 0, 0)) self.load_button = QPushButton() self.save_button = QPushButton() self.load_button.setIcon(QIcon.fromTheme('document-open')) self.save_button.setIcon(QIcon.fromTheme('document-save')) self.load_button.clicked[bool].connect(self._handle_load_clicked) self.save_button.clicked[bool].connect(self._handle_save_clicked) self.button_header.addWidget(self.save_button) self.button_header.addWidget(self.load_button) self.setMinimumWidth(150) self.reconf = reconf self.updater.start() self.reconf.config_callback = self.config_callback self._node_grn = node_name
def beginGui(self,obj): self.parent = QScrollArea() self.frame = QFrame(self.parent) if obj.layout == "vertical": self.tl = QVBoxLayout() else: self.tl = QHBoxLayout() self.__increase_nesting_level(self.frame,self.tl)
def add_widgets(self): """ Add groups of widgets to _main_widget. Supports group labels. This method can be reimplemented in order to customize appearances. """ widgets = self.get_widgets() self._widgets = [ ] # stores widgets which may need to be shut down when done for group in widgets: # Check for group label if isinstance(group[0], str): grouplabel, v = group box = QGroupBox(grouplabel) box.setContentsMargins(0, 18, 0, 0) # LTRB # Apply the center-label directive only for single-icon groups if len(group[1]) == 1: box.setAlignment(Qt.AlignHCenter) else: box = QGroupBox() box.setContentsMargins(0, 0, 0, 0) # LTRB v = group # Add widgets to QGroupBox layout = QHBoxLayout() layout.setSpacing(0) layout.setContentsMargins(0, 0, 0, 0) # LTRB for i in v: try: try: i.setIconSize( self.max_icon_size) # without this, icons are tiny except AttributeError as e: # triggers with battery which uses a QLabel instead of a QToolButton-based widget pass layout.addWidget(i) self._widgets.append(i) except: raise Exception( "All widgets must be a subclass of QWidget!") layout.activate() box.setLayout(layout) self._main_widget.addWidget(box) self._main_widget.addSeparator()
def beginGroup(self,obj): parent,layout = self.__get_immediate_parent() panel = QGroupBox(obj.name,parent) if obj.layout == "grid": l = QGridLayout() elif obj.layout == "vertical": l = QVBoxLayout() else: l = QHBoxLayout() self.__increase_nesting_level(panel, l)
def __init__(self, bagFiles, parent=None): super(CompareDataTab, self).__init__() self.parent = parent # attributes self.bagFiles = bagFiles self.plotData = ([], [], 0.0, 0.0) self.plotInfo = { 'label': '', 'y_label': '', } # widgets self.operationSelector = OperationSelectorWidget(self) self.thresholdSetter = ThresholdSetter(self) self.waitMessageBox = QMessageBox(self) self.waitMessageBox.setIcon(QMessageBox.Information) self.waitMessageBox.setWindowTitle('Computation') # layout: layout = QHBoxLayout() layout.addWidget(self.operationSelector) layout.addWidget(self.thresholdSetter) self.setLayout(layout)
def __init__(self, parent=None): """Create a new, empty DataPlot """ super(DataPlot, self).__init__(parent) # initialize miscellaneous class variables self._color_index = 0 self._scrollwidth = 10 self._dropwidth = 60 # drop data this much older than our window's lower bound # determine whether or not to scroll through the window (in X) self._autoscroll = True # initialize limits # @TODO move static Y values elsewhere? Kinda breaks modularity... self._x_limits = [0, 1.0] self._static_ylimits = [-0.5, 9.5] # initialize currently tracked data ({topic: {goal_id: [timestamp, id]}}) self._curves = {} # initialize various input signals and their callbacks and widgets self._redraw.connect(self._do_redraw) self._layout = QHBoxLayout() self.setLayout(self._layout) # initialize backend data processor (based on matplotlib) self._data_plot_widget = MatDataPlot(self) self._data_plot_widget.limits_changed.connect(self.limits_changed) self._add_curve.connect(self._data_plot_widget.add_curve) self._layout.addWidget(self._data_plot_widget) # update axes self._data_plot_widget.set_xlim(self._x_limits) self._data_plot_widget.set_ylim(self._static_ylimits) self.redraw() # display current data self.show()
def _create_cell_push_button(self, text, clicked_cb, icon=None): widget = QWidget() button = QPushButton() button.setText(text) if icon: button.setIcon(icon) button.clicked[bool].connect(clicked_cb) hlayout = QHBoxLayout(widget) hlayout.addWidget(button) hlayout.setAlignment(Qt.AlignCenter) hlayout.setContentsMargins(0, 0, 0, 0) widget.setLayout(hlayout) return widget
def add_widgets(self): """ Add groups of widgets to _main_widget. Supports group labels. This method can be reimplemented in order to customize appearances. """ widgets = self.get_widgets() self._widgets = [] # stores widgets which may need to be shut down when done for group in widgets: # Check for group label if isinstance(group[0], str): grouplabel, v = group box = QGroupBox(grouplabel) box.setContentsMargins(0, 18, 0, 0) # LTRB # Apply the center-label directive only for single-icon groups if len(group[1]) == 1: box.setAlignment(Qt.AlignHCenter) else: box = QGroupBox() box.setContentsMargins(0, 0, 0, 0) # LTRB v = group # Add widgets to QGroupBox layout = QHBoxLayout() layout.setSpacing(0) layout.setContentsMargins(0, 0, 0, 0) # LTRB for i in v: try: try: i.setIconSize(self.max_icon_size) # without this, icons are tiny except AttributeError as e: # triggers with battery which uses a QLabel instead of a QToolButton-based widget pass layout.addWidget(i) self._widgets.append(i) except: raise Exception("All widgets must be a subclass of QWidget!") layout.activate() box.setLayout(layout) self._main_widget.addWidget(box) self._main_widget.addSeparator()
def create_gait(self, name, gait, selections): gait_group_box = QGroupBox() gait_group_box.setObjectName('Gait') gait_group_box.setLayout(QHBoxLayout()) gait_group_box.setTitle(name) image = QLabel() image.setStyleSheet( 'background: url(' + gait['image'] + ') no-repeat center center 100px 100px;') image.setFixedSize(64, 80) gait_group_box.layout().addWidget(image) for subgait_name, subgait in gait['subgaits'].iteritems(): subgait_group_box = self.create_subgait(subgait_name, subgait, selections) gait_group_box.layout().addWidget(subgait_group_box) return gait_group_box
def __init__(self, parent=None): """Create a new, empty DataPlot This will raise a RuntimeError if none of the supported plotting backends can be found """ super(DataPlot, self).__init__(parent) self._plot_index = 0 self._color_index = 0 self._markers_on = False self._autoscroll = True self._autoscale_x = True self._autoscale_y = DataPlot.SCALE_ALL # the backend widget that we're trying to hide/abstract self._data_plot_widget = None self._curves = {} self._vline = None self._redraw.connect(self._do_redraw) self._layout = QHBoxLayout() self.setLayout(self._layout) enabled_plot_types = [pt for pt in self.plot_types if pt["enabled"]] if not enabled_plot_types: if qVersion().startswith("4."): version_info = "1.1.0" else: # minimum matplotlib version for Qt 5 version_info = "1.4.0" if QT_BINDING == "pyside": version_info += " and PySide %s" % ("> 1.1.0" if qVersion().startswith("4.") else ">= 2.0.0") raise RuntimeError( "No usable plot type found. Install at least one of: PyQtGraph, MatPlotLib (at least %s) or Python-Qwt5." % version_info ) self._switch_data_plot_widget(self._plot_index) self.show()
def create_progressbar(self, aircraft_no): waypoint_layout = QVBoxLayout() waypoint_layout.setContentsMargins(0, 10, 0, 10) waypoint_header_layout = QHBoxLayout( ) # waypoint_header_layout will be nested inside waypoint_layout with the progress bar beneath it aircraft_label = QLabel('Aircraft ' + str(aircraft_no)) self.waypoint_plaintext_dict['aircraft' + str(aircraft_no)] = QPlainTextEdit() self.waypoint_plaintext_dict.get('aircraft' + str(aircraft_no)).setMaximumHeight(40) self.waypoint_plaintext_dict.get('aircraft' + str(aircraft_no)).setReadOnly(True) self.waypoint_plaintext_dict[ 'aircraftlink' + str(aircraft_no)] = QPlainTextEdit("Telegram") self.waypoint_plaintext_dict.get('aircraftlink' + str(aircraft_no)).setMaximumHeight(40) self.waypoint_plaintext_dict.get('aircraftlink' + str(aircraft_no)).setMaximumWidth(100) self.waypoint_plaintext_dict.get('aircraftlink' + str(aircraft_no)).setReadOnly(True) self.waypoint_plaintext_dict['progress_bar_aircraft' + str(aircraft_no)] = QProgressBar() waypoint_header_layout.addWidget(aircraft_label) waypoint_header_layout.addWidget( self.waypoint_plaintext_dict['aircraft' + str(aircraft_no)]) waypoint_header_layout.addWidget( self.waypoint_plaintext_dict['aircraftlink' + str(aircraft_no)]) waypoint_layout.addLayout(waypoint_header_layout) waypoint_layout.addWidget( self.waypoint_plaintext_dict['progress_bar_aircraft' + str(aircraft_no)]) self.progressbar_layout.addLayout(waypoint_layout)
def __init__(self, bagFiles, parent=None): super(CompareDataTab, self).__init__() self.parent = parent # attributes self.bagFiles = bagFiles # widgets self.operationSelector = OperationSelectorWidget(self) self.thresholdSetter = ThresholdSetter(self) # layout: layout = QHBoxLayout() layout.addWidget(self.operationSelector) layout.addWidget(self.thresholdSetter) self.setLayout(layout)
def __init__(self, btnText): super(BagSelector, self).__init__() # init the elements self.bagEdit = QLineEdit() self.bagBtn = QPushButton(btnText) # connect the signals to the slots self.bagEdit.textChanged.connect(self.pathChanged) self.bagBtn.clicked.connect(self.btnClicked) self.fileName = "" # layout layout = QHBoxLayout() layout.addWidget(self.bagEdit) layout.addWidget(self.bagBtn) self.setLayout(layout)
class DataPlot(QWidget): """A widget for displaying a plot of data The DataPlot widget displays a plot, on one of several plotting backends, depending on which backend(s) are available at runtime. It currently supports PyQtGraph, MatPlot and QwtPlot backends. The DataPlot widget manages the plot backend internally, and can save and restore the internal state using `save_settings` and `restore_settings` functions. Currently, the user MUST call `restore_settings` before using the widget, to cause the creation of the enclosed plotting widget. """ # plot types in order of priority plot_types = [ { 'title': 'PyQtGraph', 'widget_class': PyQtGraphDataPlot, 'description': 'Based on PyQtGraph\n- installer: http://luke.campagnola.me/code/pyqtgraph\n', 'enabled': PyQtGraphDataPlot is not None, }, { 'title': 'MatPlot', 'widget_class': MatDataPlot, 'description': 'Based on MatPlotLib\n- needs most CPU\n- needs matplotlib >= 1.1.0\n- if using ' 'PySide: PySide > 1.1.0\n', 'enabled': MatDataPlot is not None, }, { 'title': 'QwtPlot', 'widget_class': QwtDataPlot, 'description': 'Based on QwtPlot\n- does not use timestamps\n- uses least CPU\n- needs Python ' 'Qwt bindings\n', 'enabled': QwtDataPlot is not None, }, ] # pre-defined colors: RED = (255, 0, 0) GREEN = (0, 255, 0) BLUE = (0, 0, 255) SCALE_ALL = 1 SCALE_VISIBLE = 2 SCALE_EXTEND = 4 _colors = [ Qt.blue, Qt.red, Qt.cyan, Qt.magenta, Qt.green, Qt.darkYellow, Qt.black, Qt.darkCyan, Qt.darkRed, Qt.gray ] limits_changed = Signal() _redraw = Signal() _add_curve = Signal(str, str, 'QColor', bool, bool) def __init__(self, parent=None): """Create a new, empty DataPlot This will raise a RuntimeError if none of the supported plotting backends can be found """ super(DataPlot, self).__init__(parent) self.x_width = 5.0 self._plot_index = 0 self._color_index = 0 self._markers_on = False self._autoscroll = True self._autoscale_x = False self._autoscale_y = DataPlot.SCALE_ALL # the backend widget that we're trying to hide/abstract self._data_plot_widget = None self._curves = {} self._vline = None self._redraw.connect(self._do_redraw) self._layout = QHBoxLayout() self.setLayout(self._layout) enabled_plot_types = [pt for pt in self.plot_types if pt['enabled']] if not enabled_plot_types: if qVersion().startswith('4.'): version_info = '1.1.0' else: # minimum matplotlib version for Qt 5 version_info = '1.4.0' if QT_BINDING == 'pyside': version_info += ' and PySide %s' % \ ('> 1.1.0' if qVersion().startswith('4.') else '>= 2.0.0') raise RuntimeError( 'No usable plot type found. Install at least one of: PyQtGraph, MatPlotLib ' '(at least %s) or Python-Qwt5.' % version_info) self._switch_data_plot_widget(self._plot_index) self.show() def set_x_width(self, width): self.x_width = width def _switch_data_plot_widget(self, plot_index, markers_on=False): """Internal method for activating a plotting backend by index""" # check if selected plot type is available if not self.plot_types[plot_index]['enabled']: # find other available plot type for index, plot_type in enumerate(self.plot_types): if plot_type['enabled']: plot_index = index break self._plot_index = plot_index self._markers_on = markers_on selected_plot = self.plot_types[plot_index] print("Selected plot: {}".format(selected_plot['title'])) if self._data_plot_widget: x_limits = self.get_xlim() y_limits = self.get_ylim() self._layout.removeWidget(self._data_plot_widget) self._data_plot_widget.close() self._data_plot_widget = None else: x_limits = [0.0, 10.0] y_limits = [-0.001, 0.001] self._data_plot_widget = selected_plot['widget_class'](self) self._data_plot_widget.limits_changed.connect(self.limits_changed) self._add_curve.connect(self._data_plot_widget.add_curve) self._layout.addWidget(self._data_plot_widget) # restore old data for curve_id in self._curves: curve = self._curves[curve_id] self._data_plot_widget.add_curve(curve_id, curve['name'], curve['color'], markers_on) if self._vline: self.vline(*self._vline) self.set_xlim(x_limits) self.set_ylim(y_limits) self.redraw() def _switch_plot_markers(self, markers_on): self._markers_on = markers_on self._data_plot_widget._color_index = 0 for curve_id in self._curves: self._data_plot_widget.remove_curve(curve_id) curve = self._curves[curve_id] self._data_plot_widget.add_curve(curve_id, curve['name'], curve['color'], markers_on, curve['dashed']) self.redraw() # interface out to the managing GUI component: get title, save, restore, # etc def getTitle(self): """get the title of the current plotting backend""" return self.plot_types[self._plot_index]['title'] def save_settings(self, plugin_settings, instance_settings): """Save the settings associated with this widget Currently, this is just the plot type, but may include more useful data in the future""" instance_settings.set_value('plot_type', self._plot_index) xlim = self.get_xlim() ylim = self.get_ylim() # convert limits to normal arrays of floats; some backends return numpy # arrays xlim = [float(x) for x in xlim] ylim = [float(y) for y in ylim] instance_settings.set_value('x_limits', pack(xlim)) instance_settings.set_value('y_limits', pack(ylim)) def restore_settings(self, plugin_settings, instance_settings): """Restore the settings for this widget Currently, this just restores the plot type.""" self._switch_data_plot_widget( int(instance_settings.value('plot_type', 0))) xlim = unpack(instance_settings.value('x_limits', [])) ylim = unpack(instance_settings.value('y_limits', [])) if xlim: # convert limits to an array of floats; they're often lists of # strings try: xlim = [float(x) for x in xlim] self.set_xlim(xlim) except: qWarning("Failed to restore X limits") if ylim: try: ylim = [float(y) for y in ylim] self.set_ylim(ylim) except: qWarning("Failed to restore Y limits") def doSettingsDialog(self): """Present the user with a dialog for choosing the plot backend This displays a SimpleSettingsDialog asking the user to choose a plot type, gets the result, and updates the plot type as necessary This method is blocking""" marker_settings = [{ 'title': 'Show Plot Markers', 'description': 'Warning: Displaying markers in rqt_plot may cause\n \t high cpu load, ' 'especially using PyQtGraph\n', 'enabled': True, }] if self._markers_on: selected_checkboxes = [0] else: selected_checkboxes = [] dialog = SimpleSettingsDialog(title='Plot Options') dialog.add_exclusive_option_group(title='Plot Type', options=self.plot_types, selected_index=self._plot_index) dialog.add_checkbox_group(title='Plot Markers', options=marker_settings, selected_indexes=selected_checkboxes) [plot_type, checkboxes] = dialog.get_settings() if plot_type is not None and \ plot_type['selected_index'] is not None and \ self._plot_index != plot_type['selected_index']: self._switch_data_plot_widget(plot_type['selected_index'], 0 in checkboxes['selected_indexes']) else: if checkboxes is not None and self._markers_on != ( 0 in checkboxes['selected_indexes']): self._switch_plot_markers(0 in checkboxes['selected_indexes']) # interface out to the managing DATA component: load data, update data, # etc def autoscroll(self, enabled=True): """Enable or disable autoscrolling of the plot""" self._autoscroll = enabled def redraw(self): self._redraw.emit() def _do_redraw(self): """Redraw the underlying plot This causes the underlying plot to be redrawn. This is usually used after adding or updating the plot data""" if self._data_plot_widget: self._merged_autoscale() for curve_id in self._curves: curve = self._curves[curve_id] try: self._data_plot_widget.set_values(curve_id, curve['x'], curve['y']) except KeyError: # skip curve which has been removed in the mean time pass self._data_plot_widget.redraw() def _get_curve(self, curve_id): if curve_id in self._curves: return self._curves[curve_id] else: raise DataPlotException("No curve named %s in this DataPlot" % (curve_id)) def add_curve(self, curve_id, curve_name, data_x, data_y, curve_color=None, dashed=False): """Add a new, named curve to this plot Add a curve named `curve_name` to the plot, with initial data series `data_x` and `data_y`. Future references to this curve should use the provided `curve_id` Note that the plot is not redraw automatically; call `redraw()` to make any changes visible to the user. """ if curve_color is None: curve_color = QColor(self._colors[self._color_index % len(self._colors)]) self._color_index += 1 self._curves[curve_id] = { 'x': numpy.array(data_x), 'y': numpy.array(data_y), 'name': curve_name, 'color': curve_color, 'dashed': dashed } if self._data_plot_widget: self._add_curve.emit(curve_id, curve_name, curve_color, self._markers_on, dashed) def remove_curve(self, curve_id): """Remove the specified curve from this plot""" # TODO: do on UI thread with signals if curve_id in self._curves: del self._curves[curve_id] if self._data_plot_widget: self._data_plot_widget.remove_curve(curve_id) def update_values(self, curve_id, values_x, values_y, sort_data=True): """Append new data to an existing curve `values_x` and `values_y` will be appended to the existing data for `curve_id` Note that the plot is not redraw automatically; call `redraw()` to make any changes visible to the user. If `sort_data` is set to False, values won't be sorted by `values_x` order. """ curve = self._get_curve(curve_id) curve['x'] = numpy.append(curve['x'], values_x) curve['y'] = numpy.append(curve['y'], values_y) if sort_data: # sort resulting data, so we can slice it later sort_order = curve['x'].argsort() curve['x'] = curve['x'][sort_order] curve['y'] = curve['y'][sort_order] def clear_values(self, curve_id=None): """Clear the values for the specified curve, or all curves This will erase the data series associaed with `curve_id`, or all curves if `curve_id` is not present or is None Note that the plot is not redraw automatically; call `redraw()` to make any changes visible to the user. """ # clear internal curve representation if curve_id: curve = self._get_curve(curve_id) curve['x'] = numpy.array([]) curve['y'] = numpy.array([]) else: for curve_id in self._curves: self._curves[curve_id]['x'] = numpy.array([]) self._curves[curve_id]['y'] = numpy.array([]) def vline(self, x, color=RED): """Draw a vertical line on the plot Draw a line a position X, with the given color @param x: position of the vertical line to draw @param color: optional parameter specifying the color, as tuple of RGB values from 0 to 255 """ self._vline = (x, color) if self._data_plot_widget: self._data_plot_widget.vline(x, color) # autoscaling methods def set_autoscale(self, x=None, y=None): """Change autoscaling of plot axes if a parameter is not passed, the autoscaling setting for that axis is not changed @param x: enable or disable autoscaling for X @param y: set autoscaling mode for Y """ if x is not None: self._autoscale_x = x if y is not None: self._autoscale_y = y # autoscaling: adjusting the plot bounds fit the data # autoscrollig: move the plot X window to show the most recent data # # what order do we do these adjustments in? # * assuming the various stages are enabled: # * autoscale X to bring all data into view # * else, autoscale X to determine which data we're looking at # * autoscale Y to fit the data we're viewing # # * autoscaling of Y might have several modes: # * scale Y to fit the entire dataset # * scale Y to fit the current view # * increase the Y scale to fit the current view # # TODO: incrmenetal autoscaling: only update the autoscaling bounds # when new data is added def _merged_autoscale(self): x_limit = [numpy.inf, -numpy.inf] if self._autoscale_x: for curve_id in self._curves: curve = self._curves[curve_id] if len(curve['x']) > 0: x_limit[0] = min(x_limit[0], curve['x'].min()) x_limit[1] = max(x_limit[1], curve['x'].max()) elif self._autoscroll: # get current width of plot x_limit = self.get_xlim() # x_width = x_limit[1] - x_limit[0] x_width = self.x_width # reset the upper x_limit so that we ignore the previous position x_limit[1] = -numpy.inf # get largest X value for curve_id in self._curves: curve = self._curves[curve_id] if len(curve['x']) > 0: x_limit[1] = max(x_limit[1], curve['x'].max()) # set lower limit based on width x_limit[0] = x_limit[1] - x_width else: # don't modify limit, or get it from plot x_limit = self.get_xlim() # set sane limits if our limits are infinite if numpy.isinf(x_limit[0]): x_limit[0] = 0.0 if numpy.isinf(x_limit[1]): x_limit[1] = 1.0 y_limit = [numpy.inf, -numpy.inf] if self._autoscale_y: # if we're extending the y limits, initialize them with the # current limits if self._autoscale_y & DataPlot.SCALE_EXTEND: y_limit = self.get_ylim() for curve_id in self._curves: curve = self._curves[curve_id] start_index = 0 end_index = len(curve['x']) # if we're scaling based on the visible window, find the # start and end indicies of our window if self._autoscale_y & DataPlot.SCALE_VISIBLE: # indexof x_limit[0] in curves['x'] start_index = curve['x'].searchsorted(x_limit[0]) # indexof x_limit[1] in curves['x'] end_index = curve['x'].searchsorted(x_limit[1]) # region here is cheap because it is a numpy view and not a # copy of the underlying data region = curve['y'][start_index:end_index] if len(region) > 0: y_limit[0] = min(y_limit[0], region.min()) y_limit[1] = max(y_limit[1], region.max()) # TODO: compute padding around new min and max values # ONLY consider data for new values; not # existing limits, or we'll add padding on top of old # padding in SCALE_EXTEND mode # # pad the min/max # TODO: invert this padding in get_ylim # ymin = limits[0] # ymax = limits[1] # delta = ymax - ymin if ymax != ymin else 0.1 # ymin -= .05 * delta # ymax += .05 * delta else: y_limit = self.get_ylim() # set sane limits if our limits are infinite if numpy.isinf(y_limit[0]): y_limit[0] = 0.0 if numpy.isinf(y_limit[1]): y_limit[1] = 1.0 self.set_xlim(x_limit) self.set_ylim(y_limit) def get_xlim(self): """get X limits""" if self._data_plot_widget: return self._data_plot_widget.get_xlim() else: qWarning("No plot widget; returning default X limits") return [0.0, 1.0] def set_xlim(self, limits): """set X limits""" if self._data_plot_widget: self._data_plot_widget.set_xlim(limits) else: qWarning("No plot widget; can't set X limits") def get_ylim(self): """get Y limits""" if self._data_plot_widget: return self._data_plot_widget.get_ylim() else: qWarning("No plot widget; returning default Y limits") return [0.0, 10.0] def set_ylim(self, limits): """set Y limits""" if self._data_plot_widget: self._data_plot_widget.set_ylim(limits) else: qWarning("No plot widget; can't set Y limits")
def __init__(self, parent=None): super(VisualizerWidget, self).__init__(parent) self.setWindowTitle('Graph Profiler Visualizer') vbox = QVBoxLayout() self.setLayout(vbox) toolbar_layout = QHBoxLayout() refresh_button = QPushButton() refresh_button.setIcon(QIcon.fromTheme('view-refresh')) auto_refresh_checkbox = QCheckBox("Auto Refresh") hide_disconnected_topics = QCheckBox("Hide Disconnected Topics") topic_blacklist_button = QPushButton("Topic Blacklist") node_blacklist_button = QPushButton("Node Blacklist") refresh_button.clicked.connect(self._refresh) topic_blacklist_button.clicked.connect(self._edit_topic_blacklist) node_blacklist_button.clicked.connect(self._edit_node_blacklist) auto_refresh_checkbox.setCheckState(2) auto_refresh_checkbox.stateChanged.connect(self._autorefresh_changed) hide_disconnected_topics.setCheckState(2) hide_disconnected_topics.stateChanged.connect( self._hidedisconnectedtopics_changed) toolbar_layout.addWidget(refresh_button) toolbar_layout.addWidget(auto_refresh_checkbox) toolbar_layout.addStretch(0) toolbar_layout.addWidget(hide_disconnected_topics) toolbar_layout.addWidget(topic_blacklist_button) toolbar_layout.addWidget(node_blacklist_button) vbox.addLayout(toolbar_layout) # Initialize the Visualizer self._view = qt_view.QtView() self._adapter = rosprofiler_adapter.ROSProfileAdapter(self._view) self._adapter.set_topic_quiet_list(TOPIC_BLACKLIST) self._adapter.set_node_quiet_list(NODE_BLACKLIST) vbox.addWidget(self._view)
class NavViewWidget(QWidget): def __init__(self, map_topic='/map', paths=['/move_base/NavFn/plan', '/move_base/TrajectoryPlannerROS/local_plan'], polygons=['/move_base/local_costmap/robot_footprint']): super(NavViewWidget, self).__init__() self._layout = QVBoxLayout() self._button_layout = QHBoxLayout() self.setAcceptDrops(True) self.setWindowTitle('Navigation Viewer') self.paths = paths self.polygons = polygons self.map = map_topic self._tf = tf.TransformListener() self._nav_view = NavView(map_topic, paths, polygons, tf = self._tf, parent = self) self._set_pose = QPushButton('Set Pose') self._set_pose.clicked.connect(self._nav_view.pose_mode) self._set_goal = QPushButton('Set Goal') self._set_goal.clicked.connect(self._nav_view.goal_mode) self._button_layout.addWidget(self._set_pose) self._button_layout.addWidget(self._set_goal) self._layout.addLayout(self._button_layout) self._layout.addWidget(self._nav_view) self.setLayout(self._layout) def dragEnterEvent(self, e): if not e.mimeData().hasText(): if not hasattr(e.source(), 'selectedItems') or len(e.source().selectedItems()) == 0: qWarning('NavView.dragEnterEvent(): not hasattr(event.source(), selectedItems) or len(event.source().selectedItems()) == 0') return item = e.source().selectedItems()[0] topic_name = item.data(0, Qt.UserRole) if topic_name == None: qWarning('NavView.dragEnterEvent(): not hasattr(item, ros_topic_name_)') return else: topic_name = str(e.mimeData().text()) if accepted_topic(topic_name): e.accept() e.acceptProposedAction() def dropEvent(self, e): if e.mimeData().hasText(): topic_name = str(e.mimeData().text()) else: droped_item = e.source().selectedItems()[0] topic_name = str(droped_item.data(0, Qt.UserRole)) topic_type, array = get_field_type(topic_name) if not array: if topic_type is OccupancyGrid: self.map = topic_name # Swap out the nav view for one with the new topics self._nav_view.close() self._nav_view = NavView(self.map, self.paths, self.polygons, self._tf, self) self._layout.addWidget(self._nav_view) elif topic_type is Path: self.paths.append(topic_name) self._nav_view.add_path(topic_name) elif topic_type is PolygonStamped: self.polygons.append(topic_name) self._nav_view.add_polygon(topic_name) def save_settings(self, plugin_settings, instance_settings): self._nav_view.save_settings(plugin_settings, instance_settings) def restore_settings(self, plugin_settings, instance_settings): self._nav_view.restore_settings(plugin_settings, instance_settings)
class ParamClientWidget(GroupWidget): """ A wrapper of dynamic_reconfigure.client instance. Represents a widget where users can view and modify ROS params. """ def __init__(self, reconf, node_name): """ :type reconf: dynamic_reconfigure.client :type node_name: str """ group_desc = reconf.get_group_descriptions() logging.debug('ParamClientWidget.group_desc=%s', group_desc) super(ParamClientWidget, self).__init__(ParamUpdater(reconf), group_desc, node_name) # Save and load buttons self.button_widget = QWidget(self) self.button_header = QHBoxLayout(self.button_widget) self.button_header.setContentsMargins(QMargins(0, 0, 0, 0)) self.load_button = QPushButton() self.save_button = QPushButton() self.load_button.setIcon(QIcon.fromTheme('document-open')) self.save_button.setIcon(QIcon.fromTheme('document-save')) self.load_button.clicked[bool].connect(self._handle_load_clicked) self.save_button.clicked[bool].connect(self._handle_save_clicked) self.button_header.addWidget(self.save_button) self.button_header.addWidget(self.load_button) self.setMinimumWidth(150) self.reconf = reconf self.updater.start() self.reconf.config_callback = self.config_callback self._node_grn = node_name def get_node_grn(self): return self._node_grn def config_callback(self, config): # TODO: Think about replacing callback architecture with signals. if config: # TODO: should use config.keys but this method doesnt exist names = [name for name, v in config.items()] # v isn't used but necessary to get key and put it into dict. # logging.debug('config_callback name={} v={}'.format(name, v)) for widget in self.editor_widgets: if isinstance(widget, EditorWidget): if widget.param_name in names: logging.debug('EDITOR widget.param_name=%s', widget.param_name) widget.update_value(config[widget.param_name]) elif isinstance(widget, GroupWidget): cfg = find_cfg(config, widget.param_name) logging.debug('GROUP widget.param_name=%s', widget.param_name) widget.update_group(cfg) def _handle_load_clicked(self): filename = QFileDialog.getOpenFileName( self, self.tr('Load from File'), '.', self.tr('YAML file {.yaml} (*.yaml)')) if filename[0] != '': self.load_param(filename[0]) def _handle_save_clicked(self): filename = QFileDialog.getSaveFileName( self, self.tr('Save parameters to file...'), '.', self.tr('YAML files {.yaml} (*.yaml)')) if filename[0] != '': self.save_param(filename[0]) def save_param(self, filename): configuration = self.reconf.get_configuration() if configuration is not None: with open(filename, 'w') as f: yaml.dump(configuration, f) def load_param(self, filename): with open(filename, 'r') as f: configuration = {} for doc in yaml.load_all(f.read()): configuration.update(doc) try: self.reconf.update_configuration(configuration) except ServiceException as e: logging.warn( "Call for reconfiguration wasn't successful because: %s", e.message) except DynamicReconfigureParameterException as e: logging.warn("Reconfiguration wasn't successful because: %s", e.message) except DynamicReconfigureCallbackException as e: logging.warn("Reconfiguration wasn't successful because: %s", e.message) def close(self): self.reconf.close() self.updater.stop() for w in self.editor_widgets: w.close() self.deleteLater() def filter_param(self, filter_key): # TODO impl pass
def setupButtons(self, yaml_file): """ Parse yaml file and setup Buttons. Format of the yaml file should be: - name: 'button name' (required) image: 'path to image for icon' (optional) image_size: 'width and height of icon' (optional) service: 'service' (required) column: 'column index' (optional, defaults to 0) """ self.buttons = [] with open(yaml_file) as f: yaml_data = yaml.load(f) # lookup colum direction direction = 'vertical' for d in yaml_data: if d.has_key('direction'): if d['direction'] == 'horizontal': direction = 'horizontal' else: # d['direction'] == 'vertical': direction = 'vertical' yaml_data.remove(d) break # lookup column num column_indices = [d['column'] for d in yaml_data] max_column_index = max(*column_indices) if direction == 'vertical': self.layout = QHBoxLayout() self.layout_boxes = [ QVBoxLayout() for i in range(max_column_index + 1) ] else: # direction == 'horizontal' self.layout = QVBoxLayout() self.layout_boxes = [ QHBoxLayout() for i in range(max_column_index + 1) ] self.button_groups = [ QGroupBox() for i in range(max_column_index + 1) ] for button_data in yaml_data: # check if all the field is available if not button_data.has_key("name"): self.showError("name field is missed in yaml") raise Exception("name field is missed in yaml") if not button_data.has_key("service"): self.showError("service field is missed in yaml") raise Exception("service field is missed in yaml") if self.button_type == "push": button = QToolButton() else: # self.button_type == "radio": button = QRadioButton() button.setSizePolicy( QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)) if button_data.has_key("image"): image_file = get_filename( button_data["image"])[len("file://"):] if os.path.exists(image_file): icon = QtGui.QIcon(image_file) button.setIcon(icon) if button_data.has_key("image_size"): button.setIconSize( QSize(button_data["image_size"][0], button_data["image_size"][1])) else: button.setIconSize(QSize(100, 100)) if button_data.has_key("name"): name = button_data['name'] button.setText(name) if button_data.has_key('service_type'): if button_data['service_type'] == 'Trigger': service_type = Trigger elif button_data['service_type'] == 'Empty': service_type = Empty elif button_data['service_type'] == 'SetBool': service_type = SetBool else: raise Exception("Unsupported service type: {}".format( button_data['service_type'])) else: service_type = Empty if service_type == SetBool: button.setCheckable(True) button.clicked.connect( self.buttonCallback(button_data['service'], service_type, button)) if self.button_type == "push": button.setToolButtonStyle( QtCore.Qt.ToolButtonTextUnderIcon) if ((self.button_type == "radio" or service_type == SetBool) and ("default_value" in button_data and button_data['default_value'])): button.setChecked(True) self.layout_boxes[button_data['column']].addWidget(button) self.buttons.append(button) for i in range(len(self.button_groups)): self.button_groups[i].setLayout(self.layout_boxes[i]) for group in self.button_groups: self.layout.addWidget(group) self.setLayout(self.layout)
def add_item_to_conversation_view(self, msg, type=0): label_msg = QLabel(msg) label_msg.setWordWrap(True) label_msg.setStyleSheet('font-size:10pt;') inner_text_layout = QHBoxLayout() horizonalSpacer1 = QSpacerItem(0, 0, QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) if type == 0: inner_text_layout.addWidget(label_msg) inner_text_layout.addItem(horizonalSpacer1) elif type == 1: inner_text_layout.addItem(horizonalSpacer1) inner_text_layout.addWidget(label_msg) inner_layout = QVBoxLayout() time_msg = QLabel(str(time.asctime(time.localtime(time.time())))) time_msg.setStyleSheet('font-size:8pt;') inner_layout.addItem(inner_text_layout) inner_layout.addWidget(time_msg) inner_layout.setSizeConstraint(QLayout.SetFixedSize) outer_layout = QHBoxLayout() horizonalSpacer2 = QSpacerItem(0, 0, QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) if type == 0: label_msg.setStyleSheet( 'background: #e5e5ea; padding: 6px; border-radius: 8px;') time_msg.setAlignment(Qt.AlignLeft) outer_layout.addItem(inner_layout) outer_layout.addItem(horizonalSpacer2) elif type == 1: label_msg.setStyleSheet( 'background: #1d86f4; padding: 6px; border-radius: 8px; color:#fff;' ) time_msg.setAlignment(Qt.AlignRight) outer_layout.addItem(horizonalSpacer2) outer_layout.addItem(inner_layout) outer_layout.setSizeConstraint(QLayout.SetMinimumSize) widget = QWidget() widget.setLayout(outer_layout) widget.resize(widget.sizeHint()) item = QListWidgetItem() item.setSizeHint(widget.sizeHint()) self._widget.listWidget.addItem(item) self._widget.listWidget.setItemWidget(item, widget) self._widget.listWidget.scrollToBottom()
class DataPlot(QWidget): """A widget for displaying a plot of data The DataPlot widget displays a plot, on one of several plotting backends, depending on which backend(s) are available at runtime. It currently supports PyQtGraph, MatPlot and QwtPlot backends. The DataPlot widget manages the plot backend internally, and can save and restore the internal state using `save_settings` and `restore_settings` functions. Currently, the user MUST call `restore_settings` before using the widget, to cause the creation of the enclosed plotting widget. """ # plot types in order of priority plot_types = [ { 'title': 'PyQtGraph', 'widget_class': PyQtGraphDataPlot, 'description': 'Based on PyQtGraph\n- installer: http://luke.campagnola.me/code/pyqtgraph\n', 'enabled': PyQtGraphDataPlot is not None, }, { 'title': 'MatPlot', 'widget_class': MatDataPlot, 'description': 'Based on MatPlotLib\n- needs most CPU\n- needs matplotlib >= 1.1.0\n- if using PySide: PySide > 1.1.0\n', 'enabled': MatDataPlot is not None, }, { 'title': 'QwtPlot', 'widget_class': QwtDataPlot, 'description': 'Based on QwtPlot\n- does not use timestamps\n- uses least CPU\n- needs Python Qwt bindings\n', 'enabled': QwtDataPlot is not None, }, ] # pre-defined colors: RED=(255, 0, 0) GREEN=(0, 255, 0) BLUE=(0, 0, 255) SCALE_ALL=1 SCALE_VISIBLE=2 SCALE_EXTEND=4 _colors = [Qt.blue, Qt.red, Qt.cyan, Qt.magenta, Qt.green, Qt.darkYellow, Qt.black, Qt.darkCyan, Qt.darkRed, Qt.gray] limits_changed = Signal() _redraw = Signal() _add_curve = Signal(str, str, 'QColor', bool) def __init__(self, parent=None): """Create a new, empty DataPlot This will raise a RuntimeError if none of the supported plotting backends can be found """ super(DataPlot, self).__init__(parent) self._plot_index = 0 self._color_index = 0 self._markers_on = False self._autoscroll = True self._autoscale_x = True self._autoscale_y = DataPlot.SCALE_ALL # the backend widget that we're trying to hide/abstract self._data_plot_widget = None self._curves = {} self._vline = None self._redraw.connect(self._do_redraw) self._layout = QHBoxLayout() self.setLayout(self._layout) enabled_plot_types = [pt for pt in self.plot_types if pt['enabled']] if not enabled_plot_types: if qVersion().startswith('4.'): version_info = '1.1.0' else: # minimum matplotlib version for Qt 5 version_info = '1.4.0' if QT_BINDING == 'pyside': version_info += ' and PySide %s' % \ ('> 1.1.0' if qVersion().startswith('4.') else '>= 2.0.0') raise RuntimeError('No usable plot type found. Install at least one of: PyQtGraph, MatPlotLib (at least %s) or Python-Qwt5.' % version_info) self._switch_data_plot_widget(self._plot_index) self.show() def _switch_data_plot_widget(self, plot_index, markers_on=False): """Internal method for activating a plotting backend by index""" # check if selected plot type is available if not self.plot_types[plot_index]['enabled']: # find other available plot type for index, plot_type in enumerate(self.plot_types): if plot_type['enabled']: plot_index = index break self._plot_index = plot_index self._markers_on = markers_on selected_plot = self.plot_types[plot_index] if self._data_plot_widget: x_limits = self.get_xlim() y_limits = self.get_ylim() self._layout.removeWidget(self._data_plot_widget) self._data_plot_widget.close() self._data_plot_widget = None else: x_limits = [0.0, 10.0] y_limits = [-0.001, 0.001] self._data_plot_widget = selected_plot['widget_class'](self) self._data_plot_widget.limits_changed.connect(self.limits_changed) self._add_curve.connect(self._data_plot_widget.add_curve) self._layout.addWidget(self._data_plot_widget) # restore old data for curve_id in self._curves: curve = self._curves[curve_id] self._data_plot_widget.add_curve(curve_id, curve['name'], curve['color'], markers_on) if self._vline: self.vline(*self._vline) self.set_xlim(x_limits) self.set_ylim(y_limits) self.redraw() def _switch_plot_markers(self, markers_on): self._markers_on = markers_on self._data_plot_widget._color_index = 0 for curve_id in self._curves: self._data_plot_widget.remove_curve(curve_id) curve = self._curves[curve_id] self._data_plot_widget.add_curve(curve_id, curve['name'], curve['color'], markers_on) self.redraw() # interface out to the managing GUI component: get title, save, restore, # etc def getTitle(self): """get the title of the current plotting backend""" return self.plot_types[self._plot_index]['title'] def save_settings(self, plugin_settings, instance_settings): """Save the settings associated with this widget Currently, this is just the plot type, but may include more useful data in the future""" instance_settings.set_value('plot_type', self._plot_index) xlim = self.get_xlim() ylim = self.get_ylim() # convert limits to normal arrays of floats; some backends return numpy # arrays xlim = [float(x) for x in xlim] ylim = [float(y) for y in ylim] instance_settings.set_value('x_limits', pack(xlim)) instance_settings.set_value('y_limits', pack(ylim)) def restore_settings(self, plugin_settings, instance_settings): """Restore the settings for this widget Currently, this just restores the plot type.""" self._switch_data_plot_widget(int(instance_settings.value('plot_type', 0))) xlim = unpack(instance_settings.value('x_limits', [])) ylim = unpack(instance_settings.value('y_limits', [])) if xlim: # convert limits to an array of floats; they're often lists of # strings try: xlim = [float(x) for x in xlim] self.set_xlim(xlim) except: qWarning("Failed to restore X limits") if ylim: try: ylim = [float(y) for y in ylim] self.set_ylim(ylim) except: qWarning("Failed to restore Y limits") def doSettingsDialog(self): """Present the user with a dialog for choosing the plot backend This displays a SimpleSettingsDialog asking the user to choose a plot type, gets the result, and updates the plot type as necessary This method is blocking""" marker_settings = [ { 'title': 'Show Plot Markers', 'description': 'Warning: Displaying markers in rqt_plot may cause\n \t high cpu load, especially using PyQtGraph\n', 'enabled': True, }] if self._markers_on: selected_checkboxes = [0] else: selected_checkboxes = [] dialog = SimpleSettingsDialog(title='Plot Options') dialog.add_exclusive_option_group(title='Plot Type', options=self.plot_types, selected_index=self._plot_index) dialog.add_checkbox_group(title='Plot Markers', options=marker_settings, selected_indexes=selected_checkboxes) [plot_type, checkboxes] = dialog.get_settings() if plot_type is not None and plot_type['selected_index'] is not None and self._plot_index != plot_type['selected_index']: self._switch_data_plot_widget(plot_type['selected_index'], 0 in checkboxes['selected_indexes']) else: if checkboxes is not None and self._markers_on != (0 in checkboxes['selected_indexes']): self._switch_plot_markers(0 in checkboxes['selected_indexes']) # interface out to the managing DATA component: load data, update data, # etc def autoscroll(self, enabled=True): """Enable or disable autoscrolling of the plot""" self._autoscroll = enabled def redraw(self): self._redraw.emit() def _do_redraw(self): """Redraw the underlying plot This causes the underlying plot to be redrawn. This is usually used after adding or updating the plot data""" if self._data_plot_widget: self._merged_autoscale() for curve_id in self._curves: curve = self._curves[curve_id] self._data_plot_widget.set_values(curve_id, curve['x'], curve['y']) self._data_plot_widget.redraw() def _get_curve(self, curve_id): if curve_id in self._curves: return self._curves[curve_id] else: raise DataPlotException("No curve named %s in this DataPlot" % ( curve_id) ) def add_curve(self, curve_id, curve_name, data_x, data_y): """Add a new, named curve to this plot Add a curve named `curve_name` to the plot, with initial data series `data_x` and `data_y`. Future references to this curve should use the provided `curve_id` Note that the plot is not redraw automatically; call `redraw()` to make any changes visible to the user. """ curve_color = QColor(self._colors[self._color_index % len(self._colors)]) self._color_index += 1 self._curves[curve_id] = { 'x': numpy.array(data_x), 'y': numpy.array(data_y), 'name': curve_name, 'color': curve_color} if self._data_plot_widget: self._add_curve.emit(curve_id, curve_name, curve_color, self._markers_on) def remove_curve(self, curve_id): """Remove the specified curve from this plot""" # TODO: do on UI thread with signals if curve_id in self._curves: del self._curves[curve_id] if self._data_plot_widget: self._data_plot_widget.remove_curve(curve_id) def update_values(self, curve_id, values_x, values_y): """Append new data to an existing curve `values_x` and `values_y` will be appended to the existing data for `curve_id` Note that the plot is not redraw automatically; call `redraw()` to make any changes visible to the user. """ curve = self._get_curve(curve_id) curve['x'] = numpy.append(curve['x'], values_x) curve['y'] = numpy.append(curve['y'], values_y) # sort resulting data, so we can slice it later sort_order = curve['x'].argsort() curve['x'] = curve['x'][sort_order] curve['y'] = curve['y'][sort_order] def clear_values(self, curve_id=None): """Clear the values for the specified curve, or all curves This will erase the data series associaed with `curve_id`, or all curves if `curve_id` is not present or is None Note that the plot is not redraw automatically; call `redraw()` to make any changes visible to the user. """ # clear internal curve representation if curve_id: curve = self._get_curve(curve_id) curve['x'] = numpy.array([]) curve['y'] = numpy.array([]) else: for curve_id in self._curves: self._curves[curve_id]['x'] = numpy.array([]) self._curves[curve_id]['y'] = numpy.array([]) def vline(self, x, color=RED): """Draw a vertical line on the plot Draw a line a position X, with the given color @param x: position of the vertical line to draw @param color: optional parameter specifying the color, as tuple of RGB values from 0 to 255 """ self._vline = (x, color) if self._data_plot_widget: self._data_plot_widget.vline(x, color) # autoscaling methods def set_autoscale(self, x=None, y=None): """Change autoscaling of plot axes if a parameter is not passed, the autoscaling setting for that axis is not changed @param x: enable or disable autoscaling for X @param y: set autoscaling mode for Y """ if x is not None: self._autoscale_x = x if y is not None: self._autoscale_y = y # autoscaling: adjusting the plot bounds fit the data # autoscrollig: move the plot X window to show the most recent data # # what order do we do these adjustments in? # * assuming the various stages are enabled: # * autoscale X to bring all data into view # * else, autoscale X to determine which data we're looking at # * autoscale Y to fit the data we're viewing # # * autoscaling of Y might have several modes: # * scale Y to fit the entire dataset # * scale Y to fit the current view # * increase the Y scale to fit the current view # # TODO: incrmenetal autoscaling: only update the autoscaling bounds # when new data is added def _merged_autoscale(self): x_limit = [numpy.inf, -numpy.inf] if self._autoscale_x: for curve_id in self._curves: curve = self._curves[curve_id] if len(curve['x']) > 0: x_limit[0] = min(x_limit[0], curve['x'].min()) x_limit[1] = max(x_limit[1], curve['x'].max()) elif self._autoscroll: # get current width of plot x_limit = self.get_xlim() x_width = x_limit[1] - x_limit[0] # reset the upper x_limit so that we ignore the previous position x_limit[1] = -numpy.inf # get largest X value for curve_id in self._curves: curve = self._curves[curve_id] if len(curve['x']) > 0: x_limit[1] = max(x_limit[1], curve['x'].max()) # set lower limit based on width x_limit[0] = x_limit[1] - x_width else: # don't modify limit, or get it from plot x_limit = self.get_xlim() # set sane limits if our limits are infinite if numpy.isinf(x_limit[0]): x_limit[0] = 0.0 if numpy.isinf(x_limit[1]): x_limit[1] = 1.0 y_limit = [numpy.inf, -numpy.inf] if self._autoscale_y: # if we're extending the y limits, initialize them with the # current limits if self._autoscale_y & DataPlot.SCALE_EXTEND: y_limit = self.get_ylim() for curve_id in self._curves: curve = self._curves[curve_id] start_index = 0 end_index = len(curve['x']) # if we're scaling based on the visible window, find the # start and end indicies of our window if self._autoscale_y & DataPlot.SCALE_VISIBLE: # indexof x_limit[0] in curves['x'] start_index = curve['x'].searchsorted(x_limit[0]) # indexof x_limit[1] in curves['x'] end_index = curve['x'].searchsorted(x_limit[1]) # region here is cheap because it is a numpy view and not a # copy of the underlying data region = curve['y'][start_index:end_index] if len(region) > 0: y_limit[0] = min(y_limit[0], region.min()) y_limit[1] = max(y_limit[1], region.max()) # TODO: compute padding around new min and max values # ONLY consider data for new values; not # existing limits, or we'll add padding on top of old # padding in SCALE_EXTEND mode # # pad the min/max # TODO: invert this padding in get_ylim #ymin = limits[0] #ymax = limits[1] #delta = ymax - ymin if ymax != ymin else 0.1 #ymin -= .05 * delta #ymax += .05 * delta else: y_limit = self.get_ylim() # set sane limits if our limits are infinite if numpy.isinf(y_limit[0]): y_limit[0] = 0.0 if numpy.isinf(y_limit[1]): y_limit[1] = 1.0 self.set_xlim(x_limit) self.set_ylim(y_limit) def get_xlim(self): """get X limits""" if self._data_plot_widget: return self._data_plot_widget.get_xlim() else: qWarning("No plot widget; returning default X limits") return [0.0, 1.0] def set_xlim(self, limits): """set X limits""" if self._data_plot_widget: self._data_plot_widget.set_xlim(limits) else: qWarning("No plot widget; can't set X limits") def get_ylim(self): """get Y limits""" if self._data_plot_widget: return self._data_plot_widget.get_ylim() else: qWarning("No plot widget; returning default Y limits") return [0.0, 10.0] def set_ylim(self, limits): """set Y limits""" if self._data_plot_widget: self._data_plot_widget.set_ylim(limits) else: qWarning("No plot widget; can't set Y limits")
class BagParser(QWidget): def __init__(self, bag_files, listtopics, duration): super(BagParser, self).__init__() # window title self.setWindowTitle("Making csv file") # size of window self.resize(960, 720) #self.showFullScreen() #self.setWindowState(Qt.WindowMaximized) # print listtopics # print E.get_general_features_options() # print E.get_specific_features_options() self.topics_items = dict() self.topics_items["0"] = listtopics self.topics_items["1"] = E.get_general_features_options() self.topics_items["2"] = E.get_specific_features_options() print self.topics_items #path to bag file self.bag_files = bag_files self.selected_bag_topics = [] self.selected_specific_features = [] self.selected_general_features = [] self.items_list_topics = [] self.area = QScrollArea(self) self.areagen = QScrollArea(self) self.areaspec = QScrollArea(self) self.main_widget = QWidget(self.area) self.main_widget1 = QWidget(self.areagen) self.main_widget2 = QWidget(self.areaspec) self.ok_button = QPushButton("Export To CSV", self) #self.ok_button.setFixedSize(150, 30) self.ok_button.clicked.connect(self.onButtonClicked) self.clear_button = QPushButton("Clear Selection", self) # self.clear_button.resize(self.clear_button.sizeHint()) self.clear_button.clicked.connect(self.onClearClicked) self.choose_button = QPushButton("Get Last Export Choose", self) self.choose_button.clicked.connect(self.onButtonChooseCliked) self.ok_button.setEnabled(False) self.label1 = QLabel("Select topic from bag(s)", self) self.label1.setAlignment(Qt.AlignCenter) self.label2 = QLabel("Statistics Features", self) self.label2.setAlignment(Qt.AlignCenter) self.label3 = QLabel("Specific Features", self) self.label3.setAlignment(Qt.AlignCenter) self.duration = duration self.label5 = QLabel("Duration Time: " + str("%.1f" % duration), self) self.label5.setAlignment(Qt.AlignCenter) self.main_vlayout = QVBoxLayout(self) # self.main_vlayout = QGridLayout(self) self.main_vlayout.addWidget(self.label1) self.main_vlayout.addWidget(self.area) self.main_vlayout.addWidget(self.label2) self.main_vlayout.addWidget(self.areagen) self.main_vlayout.addWidget(self.label3) self.main_vlayout.addWidget(self.areaspec) self.label4 = QLabel("Window time", self) self.label4.setAlignment(Qt.AlignCenter) # self.main_vlayout.addWidget(self.label4) self.window = QLineEdit(self) # self.main_vlayout.addWidget(self.window) self.window.setText("1") self.windows_time_3 = QHBoxLayout(self) self.windows_time_3.addWidget(self.label4) self.windows_time_3.addWidget(self.window) self.windows_time_3.addWidget(self.label5) self.main_vlayout.addLayout(self.windows_time_3) # self.window = QLineEdit(self) # self.window.setText("1") # self.box = QVBoxLayout() # self.box.addStretch(1) # self.box.addWidget(self.clear_button) # self.box.addWidget(self.choose_button) # self.box.addWidget(self.label4) # self.box.addWidget(self.window) # self.box.addWidget(self.label5) # self.box.addWidget(self.ok_button) #self.main_vlayout.addWidget(self.from_nodes_button) # self.main_vlayout.addLayout(self.box) self.two_buttons = QHBoxLayout(self) self.two_buttons.addWidget(self.choose_button) self.two_buttons.addWidget(self.clear_button) self.main_vlayout.addLayout(self.two_buttons) self.main_vlayout.addWidget(self.ok_button) self.setLayout(self.main_vlayout) self.selection_vlayout = QVBoxLayout(self) self.item_all = MyQCheckBox("All", self, self.selection_vlayout, None) self.item_all.stateChanged.connect( lambda x: self.updateList(x, self.item_all, None)) self.selection_vlayout.addWidget(self.item_all) topic_data_list = listtopics topic_data_list.sort() for topic in topic_data_list: self.addCheckBox(topic, self.selection_vlayout, self.selected_bag_topics) self.selection_vlayout1 = QVBoxLayout(self) self.item_all1 = MyQCheckBox("All", self, self.selection_vlayout1, None) self.item_all1.stateChanged.connect( lambda x: self.updateList(x, self.item_all1, None)) self.selection_vlayout1.addWidget(self.item_all1) topic_data_list1 = E.get_general_features_options() topic_data_list1.sort() for topic in topic_data_list1: self.addCheckBox(topic, self.selection_vlayout1, self.selected_general_features) self.selection_vlayout2 = QVBoxLayout(self) self.item_all2 = MyQCheckBox("All", self, self.selection_vlayout2, None) self.item_all2.stateChanged.connect( lambda x: self.updateList(x, self.item_all2, None)) self.selection_vlayout2.addWidget(self.item_all2) topic_data_list2 = E.get_specific_features_options() topic_data_list2.sort() for topic in topic_data_list2: self.addCheckBox(topic, self.selection_vlayout2, self.selected_specific_features) self.main_widget.setLayout(self.selection_vlayout) self.main_widget1.setLayout(self.selection_vlayout1) self.main_widget2.setLayout(self.selection_vlayout2) self.area.setWidget(self.main_widget) self.areagen.setWidget(self.main_widget1) self.areaspec.setWidget(self.main_widget2) self.show() def onClearClicked(self): self.clearTopicCheckState() def clearTopicCheckState(self): for item in self.items_list_topics: item.setCheckState(False) self.item_all.setCheckState(False) self.item_all1.setCheckState(False) self.item_all2.setCheckState(False) def addCheckBox(self, topic, selection_vlayout, selected_list): item = MyQCheckBox(topic, self, selection_vlayout, selected_list) item.stateChanged.connect(lambda x: self.updateList(x, item, topic)) self.items_list_topics.append(item) selection_vlayout.addWidget(item) def changeTopicCheckState(self, topic, state): for item in self.items_list_topics: if item.text() == topic: item.setCheckState(state) return def updateList(self, state, item_clicked, topic=None, force_update_state=False): if topic is None: # The "All" checkbox was checked / unchecked # print "if topic is None" if state == Qt.Checked: self.item_all.setTristate(False) for item in self.items_list_topics: if item.checkState() == Qt.Unchecked and \ item.selection_vlayout == item_clicked.selection_vlayout: item.setCheckState(Qt.Checked) elif state == Qt.Unchecked: self.item_all.setTristate(False) for item in self.items_list_topics: if item.checkState() == Qt.Checked and \ item.selection_vlayout == item_clicked.selection_vlayout: item.setCheckState(Qt.Unchecked) else: # print "else:" if state == Qt.Checked: item_clicked.selected_list.append(topic) print item_clicked.selected_list else: item_clicked.selected_list.remove(topic) #if self.item_all.checkState() == Qt.Checked: # self.item_all.setCheckState(Qt.PartiallyChecked) if self.selected_specific_features != []: if self.selected_general_features != [] and self.selected_bag_topics != []: self.ok_button.setEnabled(True) elif self.selected_general_features == [] and self.selected_bag_topics == []: self.ok_button.setEnabled(True) else: self.ok_button.setEnabled(False) else: if self.selected_general_features != [] and self.selected_bag_topics != []: self.ok_button.setEnabled(True) else: self.ok_button.setEnabled(False) # if self.selected_bag_topics != []: # if self.selected_specific_features == [] and self.selected_general_features == []: # self.ok_button.setEnabled(False) # else: # self.ok_button.setEnabled(True) # # elif self.selected_specific_features != [] and self.selected_general_features == []: # # self.ok_button.setEnabled(True) # # elif self.selected_specific_features == [] and self.selected_general_features != []: # # self.ok_button.setEnabled(True) # else: # self.ok_button.setEnabled(False) # if self.selected_specific_features != []: # if self.selected_bag_topics == [] and self.selected_general_features == []: # self.ok_button.setEnabled(True) # elif self.selected_bag_topics != [] and self.selected_general_features != []: # self.ok_button.setEnabled(True) # else: # self.ok_button.setEnabled(False) # else: # if self.selected_bag_topics == [] or self.selected_general_features == []: # self.ok_button.setEnabled(False) # else: # self.ok_button.setEnabled(True) def onButtonChooseCliked(self): for checkbox in self.items_list_topics: checkbox.setCheckState(Qt.Unchecked) with open(get_path() + "logger.log", 'r') as f: topics = f.read().splitlines() for checkbox in self.items_list_topics: if checkbox.text() in topics: checkbox.setCheckState(Qt.Checked) def get_current_opened_directory(self, filepath): import os direc = "/" if os.path.isfile(filepath): with open(filepath, 'r') as f: direc = f.read() return direc def onButtonClicked(self): import inspect, os filepath = os.path.dirname( os.path.abspath(inspect.getfile( inspect.currentframe()))) + "/log/save_csv.log" current_directory = self.get_current_opened_directory(filepath) window = self.window.text() try: val = float(window) except ValueError: QMessageBox.about(self, "Error in Window Time", "That's not a number!") return if val >= self.duration: QMessageBox.about( self, "Error in Window Time", "time need to be smaller than: " + str(self.duration)) return # filename = QFileDialog.getSaveFileName(self, self.tr('csv File'), current_directory, self.tr('csv (*.csv)')) saved_dir = str( QFileDialog.getExistingDirectory(self, "Select Directory", current_directory)) # if filename[0] != '': # with open(filepath, "w") as f: # f.write(filename[0]) if saved_dir != '': with open(filepath, "w") as f: f.write(saved_dir) topics = self.selected_bag_topics specific_features_selection = self.selected_specific_features general_features_selection = self.selected_general_features with open(get_path() + 'logger.log', "w") as f: for topic in topics: f.write(topic + "\n") for topic1 in specific_features_selection: f.write(topic1 + "\n") for topic2 in general_features_selection: f.write(topic2 + "\n") ef = E.ExtractFeatures(topics, float(window), specific_features_selection, general_features_selection) counter = 0 for bag_file in self.bag_files: df = ef.generate_features(bag_file) if len(self.bag_files) == 1: counter = -1 # temp = filename + "/" + # temp = get_corrent_file_name(filename[0], ".csv", counter) csv_path = generate_csv_from_bag(saved_dir, bag_file) # temp = "%s_%s%s" % (filename[0],counter,".csv") E.write_to_csv(csv_path, df) counter = counter + 1 QMessageBox.about(self, "csv export", "csv was exported successfuly")
def __init__(self, title, jsp, num_rows=0): super(JointStatePublisherGui, self).__init__() font = QFont("Helvetica", 9, QFont.Bold) self.hlayout = QHBoxLayout(self) vlayout = QVBoxLayout() glayout = QGridLayout() right_l_lauout = QVBoxLayout() self.listVeiw = QListWidget() self.checkbox = [] self.value_line_edit = [] self.sliders = [] self.positions = [] self.progressbars = [] self.value_last = [] speed_max = enviromnt_conf['joint_speed'] slider_max = speed_max * 1000 position_max = enviromnt_conf['joint_max_position'] progress_max = position_max * 1000 #create joints widget for i in range(0, num_rows): if config[i][0]: g_in_g = QGridLayout() checkbox = QCheckBox(config[i][1]) checkbox.setFont(font) self.checkbox.append(checkbox) value_line_edit = QLineEdit() value_line_edit.setFont(font) value_line_edit.setText("0.0") self.value_line_edit.append(value_line_edit) display_lable = QLabel() display_lable.setFont(font) display_lable.setText("Position:") position_label = QLabel() position_label.setFont(font) position_label.setText("0.0") self.positions.append(position_label) position_progress_bar = QProgressBar() position_progress_bar.setMaximum(progress_max) position_progress_bar.setMinimum(-progress_max) position_progress_bar.setValue(0) self.progressbars.append(position_progress_bar) slider = QSlider() slider.setMaximum(slider_max) slider.setMinimum(-slider_max) slider.setOrientation(Qt.Horizontal) slider.valueChanged.connect(self.slider_value_changed) self.sliders.append(slider) g_in_g.addWidget(checkbox, 0, 0) g_in_g.addWidget(value_line_edit, 0, 1) g_in_g.addWidget(display_lable, 0, 2) g_in_g.addWidget(position_label, 0, 3) g_in_g.addWidget(slider, 1, 0, 1, 2) g_in_g.addWidget(position_progress_bar, 1, 2, 1, 2) glayout.addLayout(g_in_g, i, 0) #create v layout self.import_Btn = QPushButton('Import') self.import_Btn.setFont(font) self.import_Btn.clicked.connect(self.import_Btn_clecked) self.export_Btn = QPushButton('Export') self.export_Btn.setFont(font) self.export_Btn.clicked.connect(self.export_Btn_clicked) self.start_Btn = QPushButton("Start") self.start_Btn.setFont(font) self.start_Btn.clicked.connect(self.start_Btn_clicked) self.reset_Btn = QPushButton('Reset') self.reset_Btn.setFont(font) self.reset_Btn.clicked.connect(self.reset_Btn_clicked) self.record_Btn = QPushButton('Record') self.record_Btn.setFont(font) self.record_Btn.clicked.connect(self.record_Btn_clicked) self.replay_Btn = QPushButton('Repaly') self.replay_Btn.setFont(font) self.replay_Btn.clicked.connect(self.replay_Btn_clicked) self.delete_Btn = QPushButton("Delete") self.delete_Btn.setFont(font) self.delete_Btn.clicked.connect(self.delete_Btn_clicked) self.debug_Btn = QPushButton("Debug") self.debug_Btn.setFont(font) self.debug_Btn.clicked.connect(self.debug_Btn_clicked) vlayout.addWidget(self.import_Btn) vlayout.addWidget(self.export_Btn) vlayout.addWidget(self.start_Btn) vlayout.addWidget(self.reset_Btn) vlayout.addWidget(self.record_Btn) vlayout.addWidget(self.delete_Btn) vlayout.addWidget(self.replay_Btn) vlayout.addWidget(self.debug_Btn) self.master_url = QLineEdit("http://192.168.0.91:11311") self.master_url.setFont(font) self.master_ip = QLineEdit("192.168.0.91") self.master_ip.setFont(font) self.listVeiw.clicked.connect(self.listVeiw_clicked) self.listVeiw.currentRowChanged.connect( self.listVeiw_itemSelectionChanged) self.description = QTextEdit("") self.description.setFont(font) #self.description.setGeometry(0,100,100,500) right_l_lauout.addWidget(self.master_url) right_l_lauout.addWidget(self.master_ip) right_l_lauout.addWidget(self.listVeiw) right_l_lauout.addWidget(self.description) right_l_lauout.setStretch(0, 1) right_l_lauout.setStretch(1, 1) right_l_lauout.setStretch(2, 3) right_l_lauout.setStretch(3, 1) self.num_rows = len(self.checkbox) self.hlayout.addLayout(glayout) self.hlayout.addLayout(vlayout) self.hlayout.addLayout(right_l_lauout) self.setLayout(self.hlayout) self.callback_start = None self.callback_pause = None self.callback_record = None self.callback_reset = None self.callback_replay = None self.callback_replay_stop = None self.callback_delete = None self.callback_debug = None self.callback_import = None self.callback_export = None self.callback_list_clicked = None self.listVeiw_isClicked = False self.listVeiw_current_item = 0 self.listVeiw_len = 0 self.f = QFileDialog()
class JointStatePublisherGui(QWidget): def __init__(self, title, jsp, num_rows=0): super(JointStatePublisherGui, self).__init__() font = QFont("Helvetica", 9, QFont.Bold) self.hlayout = QHBoxLayout(self) vlayout = QVBoxLayout() glayout = QGridLayout() right_l_lauout = QVBoxLayout() self.listVeiw = QListWidget() self.checkbox = [] self.value_line_edit = [] self.sliders = [] self.positions = [] self.progressbars = [] self.value_last = [] speed_max = enviromnt_conf['joint_speed'] slider_max = speed_max * 1000 position_max = enviromnt_conf['joint_max_position'] progress_max = position_max * 1000 #create joints widget for i in range(0, num_rows): if config[i][0]: g_in_g = QGridLayout() checkbox = QCheckBox(config[i][1]) checkbox.setFont(font) self.checkbox.append(checkbox) value_line_edit = QLineEdit() value_line_edit.setFont(font) value_line_edit.setText("0.0") self.value_line_edit.append(value_line_edit) display_lable = QLabel() display_lable.setFont(font) display_lable.setText("Position:") position_label = QLabel() position_label.setFont(font) position_label.setText("0.0") self.positions.append(position_label) position_progress_bar = QProgressBar() position_progress_bar.setMaximum(progress_max) position_progress_bar.setMinimum(-progress_max) position_progress_bar.setValue(0) self.progressbars.append(position_progress_bar) slider = QSlider() slider.setMaximum(slider_max) slider.setMinimum(-slider_max) slider.setOrientation(Qt.Horizontal) slider.valueChanged.connect(self.slider_value_changed) self.sliders.append(slider) g_in_g.addWidget(checkbox, 0, 0) g_in_g.addWidget(value_line_edit, 0, 1) g_in_g.addWidget(display_lable, 0, 2) g_in_g.addWidget(position_label, 0, 3) g_in_g.addWidget(slider, 1, 0, 1, 2) g_in_g.addWidget(position_progress_bar, 1, 2, 1, 2) glayout.addLayout(g_in_g, i, 0) #create v layout self.import_Btn = QPushButton('Import') self.import_Btn.setFont(font) self.import_Btn.clicked.connect(self.import_Btn_clecked) self.export_Btn = QPushButton('Export') self.export_Btn.setFont(font) self.export_Btn.clicked.connect(self.export_Btn_clicked) self.start_Btn = QPushButton("Start") self.start_Btn.setFont(font) self.start_Btn.clicked.connect(self.start_Btn_clicked) self.reset_Btn = QPushButton('Reset') self.reset_Btn.setFont(font) self.reset_Btn.clicked.connect(self.reset_Btn_clicked) self.record_Btn = QPushButton('Record') self.record_Btn.setFont(font) self.record_Btn.clicked.connect(self.record_Btn_clicked) self.replay_Btn = QPushButton('Repaly') self.replay_Btn.setFont(font) self.replay_Btn.clicked.connect(self.replay_Btn_clicked) self.delete_Btn = QPushButton("Delete") self.delete_Btn.setFont(font) self.delete_Btn.clicked.connect(self.delete_Btn_clicked) self.debug_Btn = QPushButton("Debug") self.debug_Btn.setFont(font) self.debug_Btn.clicked.connect(self.debug_Btn_clicked) vlayout.addWidget(self.import_Btn) vlayout.addWidget(self.export_Btn) vlayout.addWidget(self.start_Btn) vlayout.addWidget(self.reset_Btn) vlayout.addWidget(self.record_Btn) vlayout.addWidget(self.delete_Btn) vlayout.addWidget(self.replay_Btn) vlayout.addWidget(self.debug_Btn) self.master_url = QLineEdit("http://192.168.0.91:11311") self.master_url.setFont(font) self.master_ip = QLineEdit("192.168.0.91") self.master_ip.setFont(font) self.listVeiw.clicked.connect(self.listVeiw_clicked) self.listVeiw.currentRowChanged.connect( self.listVeiw_itemSelectionChanged) self.description = QTextEdit("") self.description.setFont(font) #self.description.setGeometry(0,100,100,500) right_l_lauout.addWidget(self.master_url) right_l_lauout.addWidget(self.master_ip) right_l_lauout.addWidget(self.listVeiw) right_l_lauout.addWidget(self.description) right_l_lauout.setStretch(0, 1) right_l_lauout.setStretch(1, 1) right_l_lauout.setStretch(2, 3) right_l_lauout.setStretch(3, 1) self.num_rows = len(self.checkbox) self.hlayout.addLayout(glayout) self.hlayout.addLayout(vlayout) self.hlayout.addLayout(right_l_lauout) self.setLayout(self.hlayout) self.callback_start = None self.callback_pause = None self.callback_record = None self.callback_reset = None self.callback_replay = None self.callback_replay_stop = None self.callback_delete = None self.callback_debug = None self.callback_import = None self.callback_export = None self.callback_list_clicked = None self.listVeiw_isClicked = False self.listVeiw_current_item = 0 self.listVeiw_len = 0 self.f = QFileDialog() def who_data_changed(self): for i in range(0, self.num_rows): value_last = self.value_line_edit[i].text() value_last = float(value_last) * 1000 value = self.sliders[i].value() if value != value_last: return i def change_line_edit(self, change_index): value = self.sliders[change_index].value() value = float(value) / 1000 value = str(value) self.value_line_edit[change_index].setText(value) def change_position_edit(self, change_index): value = self.progressbars[change_index].value() value += self.sliders[change_index].value() self.progressbars[change_index].setValue(value) self.positions[change_index].setText(str(float(value) / 1000)) def reset_speed(self, change_index): self.sliders[change_index].setValue(0) def reset_speed_all(self): for i in range(0, self.num_rows): self.reset_speed(i) def set_speed(self, index, data): self.sliders[index].setValue(data) def import_Btn_clecked(self): self.file_path = self.f.getOpenFileName(caption='Import excel data', directory='', filter='*.xlsx', initialFilter='') self.file_path = self.file_path[0] if self.callback_import: self.callback_import() pass def export_Btn_clicked(self): self.file_path = self.f.getSaveFileName(caption='Save as excel data', directory='', filter='*.xlsx', initialFilter='') self.file_path = self.file_path[0] if self.callback_export: self.callback_export() pass def set_callback_start(self, func): self.callback_start = func def start_Btn_clicked(self): if self.start_Btn.text() == "Start": if self.callback_start: self.callback_start() self.start_Btn.setText("Pause") else: if self.callback_pause: self.callback_pause() self.start_Btn.setText("Start") def reset_Btn_clicked(self): self.reset_speed_all() self.reset_postion_all() if self.callback_reset: self.callback_reset() def replay_Btn_clicked(self): if self.replay_Btn.text() == "Replay": self.replay_Btn.setText("Stop") if self.callback_replay: self.callback_replay() else: self.replay_Btn.setText("Replay") if self.callback_replay_stop: self.callback_replay_stop() def debug_Btn_clicked(self): if self.callback_debug: self.callback_debug() def record_Btn_clicked(self): self.set_postion() self.listVeiw_len += 1 if self.callback_record: self.callback_record() self.reset_speed_all() def listVeiw_itemSelectionChanged(self, index): self.listVeiw_isClicked = True self.listVeiw_current_item = index if self.callback_list_clicked: self.callback_list_clicked() def listVeiw_clicked(self, index): #print "index", index.row() if self.listVeiw_current_item != index.row(): description_text = self.description.toPlainText() #self.listVeiw.item(self.listVeiw_current_item).setData(1,"123") #print self.listVeiw.item(self.listVeiw_current_item).data self.listVeiw_isClicked = True self.listVeiw_current_item = index.row() if self.callback_list_clicked: self.callback_list_clicked() def update_listView(self): print "update", self.listVeiw_len for i in range(0, self.listVeiw_len): view = self.listVeiw.item(i) view.setText(str(i)) def get_listVeiw_current_item(self): return self.listVeiw_current_item def delete_Btn_clicked(self): if self.listVeiw_isClicked: print self.listVeiw_current_item self.listVeiw.removeItemWidget( self.listVeiw.takeItem(self.listVeiw_current_item)) self.listVeiw_len -= 1 if self.listVeiw_current_item != self.listVeiw_len: self.update_listView() if self.callback_delete: self.callback_delete() if self.listVeiw_current_item == 0: self.listVeiw_current_item = 0 else: self.listVeiw_current_item -= 1 self.listVeiw.setCurrentRow(self.listVeiw_current_item) if self.listVeiw_len == 0: self.listVeiw_isClicked = False def listView_add_item(self, index): self.listVeiw.addItem(str(index)) def listView_inset_item(self, index, label): self.listVeiw.insertItem(index, str(label)) def slider_value_changed(self, data): change_index = self.who_data_changed() self.change_line_edit(change_index) def get_speed(self): speed = [] for i in range(0, self.num_rows): value = self.sliders[i].value() value = float(value) / 1000 speed.append(value) return speed def get_position(self): position = [] for i in range(0, self.num_rows): value = self.progressbars[i].value() value += self.sliders[i].value() value = float(value) / 1000 position.append(value) return position def set_postion(self): for i in range(0, self.num_rows): self.change_position_edit(i) def set_positions(self, data): print len(data) for i in range(0, len(data)): self.progressbars[i].setValue(data[i] * 1000) self.positions[i].setText(str(data[i])) def reset_position(self, change_index): value = 0 self.progressbars[change_index].setValue(value) self.positions[change_index].setText(str(float(value) / 1000)) def reset_postion_all(self): for i in range(0, self.num_rows): self.reset_position(i)
def __init__(self, updater, config, nodename): ''' :param config: :type config: Dictionary? defined in dynamic_reconfigure.client.Client :type nodename: str ''' super(GroupWidget, self).__init__() self.state = config['state'] self.param_name = config['name'] self._toplevel_treenode_name = nodename # TODO: .ui file needs to be back into usage in later phase. # ui_file = os.path.join(rp.get_path('rqt_reconfigure'), # 'resource', 'singlenode_parameditor.ui') # loadUi(ui_file, self) verticalLayout = QVBoxLayout(self) verticalLayout.setContentsMargins(QMargins(0, 0, 0, 0)) _widget_nodeheader = QWidget() _h_layout_nodeheader = QHBoxLayout(_widget_nodeheader) _h_layout_nodeheader.setContentsMargins(QMargins(0, 0, 0, 0)) self.nodename_qlabel = QLabel(self) font = QFont('Trebuchet MS, Bold') font.setUnderline(True) font.setBold(True) # Button to close a node. _icon_disable_node = QIcon.fromTheme('window-close') _bt_disable_node = QPushButton(_icon_disable_node, '', self) _bt_disable_node.setToolTip('Hide this node') _bt_disable_node_size = QSize(36, 24) _bt_disable_node.setFixedSize(_bt_disable_node_size) _bt_disable_node.pressed.connect(self._node_disable_bt_clicked) _h_layout_nodeheader.addWidget(self.nodename_qlabel) _h_layout_nodeheader.addWidget(_bt_disable_node) self.nodename_qlabel.setAlignment(Qt.AlignCenter) font.setPointSize(10) self.nodename_qlabel.setFont(font) grid_widget = QWidget(self) self.grid = QFormLayout(grid_widget) verticalLayout.addWidget(_widget_nodeheader) verticalLayout.addWidget(grid_widget, 1) # Again, these UI operation above needs to happen in .ui file. self.tab_bar = None # Every group can have one tab bar self.tab_bar_shown = False self.updater = updater self.editor_widgets = [] self._param_names = [] self._create_node_widgets(config) rospy.logdebug('Groups node name={}'.format(nodename)) self.nodename_qlabel.setText(nodename)
def __init__(self, title, jsp, num_rows=0): super(JointStatePublisherGui, self).__init__() self.jsp = jsp self.joint_map = {} self.vlayout = QVBoxLayout(self) self.gridlayout = QGridLayout() font = QFont("Helvetica", 9, QFont.Bold) ### Generate sliders ### sliders = [] for name in self.jsp.joint_list: if name not in self.jsp.free_joints: continue joint = self.jsp.free_joints[name] if joint['min'] == joint['max']: continue joint_layout = QVBoxLayout() row_layout = QHBoxLayout() label = QLabel(name) label.setFont(font) row_layout.addWidget(label) display = QLineEdit("0.00") display.setAlignment(Qt.AlignRight) display.setFont(font) display.setReadOnly(True) row_layout.addWidget(display) joint_layout.addLayout(row_layout) slider = QSlider(Qt.Horizontal) slider.setFont(font) slider.setRange(0, RANGE) slider.setValue(RANGE/2) joint_layout.addWidget(slider) self.joint_map[name] = {'slidervalue': 0, 'display': display, 'slider': slider, 'joint': joint} # Connect to the signal provided by QSignal slider.valueChanged.connect(self.onValueChanged) sliders.append(joint_layout) # Determine number of rows to be used in grid self.num_rows = num_rows # if desired num of rows wasn't set, default behaviour is a vertical layout if self.num_rows == 0: self.num_rows = len(sliders) # equals VBoxLayout # Generate positions in grid and place sliders there self.positions = self.generate_grid_positions(len(sliders), self.num_rows) for item, pos in zip(sliders, self.positions): self.gridlayout.addLayout(item, *pos) # Set zero positions read from parameters self.center() # Synchronize slider and displayed value self.sliderUpdate(None) # Set up a signal for updating the sliders based on external joint info self.sliderUpdateTrigger.connect(self.updateSliders) self.vlayout.addLayout(self.gridlayout) # Buttons for randomizing and centering sliders and # Spinbox for on-the-fly selecting number of rows self.randbutton = QPushButton('Randomize', self) self.randbutton.clicked.connect(self.randomize_event) self.vlayout.addWidget(self.randbutton) self.ctrbutton = QPushButton('Center', self) self.ctrbutton.clicked.connect(self.center_event) self.vlayout.addWidget(self.ctrbutton) self.maxrowsupdown = QSpinBox() self.maxrowsupdown.setMinimum(1) self.maxrowsupdown.setMaximum(len(sliders)) self.maxrowsupdown.setValue(self.num_rows) self.maxrowsupdown.lineEdit().setReadOnly(True) # don't edit it by hand to avoid weird resizing of window self.maxrowsupdown.valueChanged.connect(self.reorggrid_event) self.vlayout.addWidget(self.maxrowsupdown)
def __init__(self, bag_files, listtopics, duration): super(BagParser, self).__init__() # window title self.setWindowTitle("Making csv file") # size of window self.resize(960, 720) #self.showFullScreen() #self.setWindowState(Qt.WindowMaximized) # print listtopics # print E.get_general_features_options() # print E.get_specific_features_options() self.topics_items = dict() self.topics_items["0"] = listtopics self.topics_items["1"] = E.get_general_features_options() self.topics_items["2"] = E.get_specific_features_options() print self.topics_items #path to bag file self.bag_files = bag_files self.selected_bag_topics = [] self.selected_specific_features = [] self.selected_general_features = [] self.items_list_topics = [] self.area = QScrollArea(self) self.areagen = QScrollArea(self) self.areaspec = QScrollArea(self) self.main_widget = QWidget(self.area) self.main_widget1 = QWidget(self.areagen) self.main_widget2 = QWidget(self.areaspec) self.ok_button = QPushButton("Export To CSV", self) #self.ok_button.setFixedSize(150, 30) self.ok_button.clicked.connect(self.onButtonClicked) self.clear_button = QPushButton("Clear Selection", self) # self.clear_button.resize(self.clear_button.sizeHint()) self.clear_button.clicked.connect(self.onClearClicked) self.choose_button = QPushButton("Get Last Export Choose", self) self.choose_button.clicked.connect(self.onButtonChooseCliked) self.ok_button.setEnabled(False) self.label1 = QLabel("Select topic from bag(s)", self) self.label1.setAlignment(Qt.AlignCenter) self.label2 = QLabel("Statistics Features", self) self.label2.setAlignment(Qt.AlignCenter) self.label3 = QLabel("Specific Features", self) self.label3.setAlignment(Qt.AlignCenter) self.duration = duration self.label5 = QLabel("Duration Time: " + str("%.1f" % duration), self) self.label5.setAlignment(Qt.AlignCenter) self.main_vlayout = QVBoxLayout(self) # self.main_vlayout = QGridLayout(self) self.main_vlayout.addWidget(self.label1) self.main_vlayout.addWidget(self.area) self.main_vlayout.addWidget(self.label2) self.main_vlayout.addWidget(self.areagen) self.main_vlayout.addWidget(self.label3) self.main_vlayout.addWidget(self.areaspec) self.label4 = QLabel("Window time", self) self.label4.setAlignment(Qt.AlignCenter) # self.main_vlayout.addWidget(self.label4) self.window = QLineEdit(self) # self.main_vlayout.addWidget(self.window) self.window.setText("1") self.windows_time_3 = QHBoxLayout(self) self.windows_time_3.addWidget(self.label4) self.windows_time_3.addWidget(self.window) self.windows_time_3.addWidget(self.label5) self.main_vlayout.addLayout(self.windows_time_3) # self.window = QLineEdit(self) # self.window.setText("1") # self.box = QVBoxLayout() # self.box.addStretch(1) # self.box.addWidget(self.clear_button) # self.box.addWidget(self.choose_button) # self.box.addWidget(self.label4) # self.box.addWidget(self.window) # self.box.addWidget(self.label5) # self.box.addWidget(self.ok_button) #self.main_vlayout.addWidget(self.from_nodes_button) # self.main_vlayout.addLayout(self.box) self.two_buttons = QHBoxLayout(self) self.two_buttons.addWidget(self.choose_button) self.two_buttons.addWidget(self.clear_button) self.main_vlayout.addLayout(self.two_buttons) self.main_vlayout.addWidget(self.ok_button) self.setLayout(self.main_vlayout) self.selection_vlayout = QVBoxLayout(self) self.item_all = MyQCheckBox("All", self, self.selection_vlayout, None) self.item_all.stateChanged.connect( lambda x: self.updateList(x, self.item_all, None)) self.selection_vlayout.addWidget(self.item_all) topic_data_list = listtopics topic_data_list.sort() for topic in topic_data_list: self.addCheckBox(topic, self.selection_vlayout, self.selected_bag_topics) self.selection_vlayout1 = QVBoxLayout(self) self.item_all1 = MyQCheckBox("All", self, self.selection_vlayout1, None) self.item_all1.stateChanged.connect( lambda x: self.updateList(x, self.item_all1, None)) self.selection_vlayout1.addWidget(self.item_all1) topic_data_list1 = E.get_general_features_options() topic_data_list1.sort() for topic in topic_data_list1: self.addCheckBox(topic, self.selection_vlayout1, self.selected_general_features) self.selection_vlayout2 = QVBoxLayout(self) self.item_all2 = MyQCheckBox("All", self, self.selection_vlayout2, None) self.item_all2.stateChanged.connect( lambda x: self.updateList(x, self.item_all2, None)) self.selection_vlayout2.addWidget(self.item_all2) topic_data_list2 = E.get_specific_features_options() topic_data_list2.sort() for topic in topic_data_list2: self.addCheckBox(topic, self.selection_vlayout2, self.selected_specific_features) self.main_widget.setLayout(self.selection_vlayout) self.main_widget1.setLayout(self.selection_vlayout1) self.main_widget2.setLayout(self.selection_vlayout2) self.area.setWidget(self.main_widget) self.areagen.setWidget(self.main_widget1) self.areaspec.setWidget(self.main_widget2) self.show()
def __init__(self, context, node=None): """ This class is intended to be called by rqt plugin framework class. Currently (12/12/2012) the whole widget is splitted into 2 panes: one on left allows you to choose the node(s) you work on. Right side pane lets you work with the parameters associated with the node(s) you select on the left. (12/27/2012) Despite the pkg name is changed to rqt_reconfigure to reflect the available functionality, file & class names remain 'param', expecting all the parameters will become handle-able. """ super(ParamWidget, self).__init__() self.setObjectName(self._TITLE_PLUGIN) self.setWindowTitle(self._TITLE_PLUGIN) rp = rospkg.RosPack() #TODO: .ui file needs to replace the GUI components declaration # below. For unknown reason, referring to another .ui files # from a .ui that is used in this class failed. So for now, # I decided not use .ui in this class. # If someone can tackle this I'd appreciate. _hlayout_top = QHBoxLayout(self) _hlayout_top.setContentsMargins(QMargins(0, 0, 0, 0)) self._splitter = QSplitter(self) _hlayout_top.addWidget(self._splitter) _vlayout_nodesel_widget = QWidget() _vlayout_nodesel_side = QVBoxLayout() _hlayout_filter_widget = QWidget(self) _hlayout_filter = QHBoxLayout() self._text_filter = TextFilter() self.filter_lineedit = TextFilterWidget(self._text_filter, rp) self.filterkey_label = QLabel("&Filter key:") self.filterkey_label.setBuddy(self.filter_lineedit) _hlayout_filter.addWidget(self.filterkey_label) _hlayout_filter.addWidget(self.filter_lineedit) _hlayout_filter_widget.setLayout(_hlayout_filter) self._nodesel_widget = NodeSelectorWidget(self, rp, self.sig_sysmsg) _vlayout_nodesel_side.addWidget(_hlayout_filter_widget) _vlayout_nodesel_side.addWidget(self._nodesel_widget) _vlayout_nodesel_side.setSpacing(1) _vlayout_nodesel_widget.setLayout(_vlayout_nodesel_side) reconf_widget = ParameditWidget(rp) self._splitter.insertWidget(0, _vlayout_nodesel_widget) self._splitter.insertWidget(1, reconf_widget) # 1st column, _vlayout_nodesel_widget, to minimize width. # 2nd col to keep the possible max width. self._splitter.setStretchFactor(0, 0) self._splitter.setStretchFactor(1, 1) # Signal from paramedit widget to node selector widget. reconf_widget.sig_node_disabled_selected.connect( self._nodesel_widget.node_deselected) # Pass name of node to editor widget self._nodesel_widget.sig_node_selected.connect( reconf_widget.show_reconf) if not node: title = self._TITLE_PLUGIN else: title = self._TITLE_PLUGIN + ' %s' % node self.setObjectName(title) #Connect filter signal-slots. self._text_filter.filter_changed_signal.connect( self._filter_key_changed) # Open any clients indicated from command line self.sig_selected.connect(self._nodesel_widget.node_selected) for rn in [rospy.resolve_name(c) for c in context.argv()]: if rn in self._nodesel_widget.get_paramitems(): self.sig_selected.emit(rn) else: rospy.logwarn('Could not find a dynamic reconfigure client named \'%s\'', str(rn))
def __init__(self, title, jsp, num_rows=0): super(JointStatePublisherGui, self).__init__() self.jsp = jsp self.joint_map = {} self.vlayout = QVBoxLayout(self) self.scrollable = QWidget() self.gridlayout = QGridLayout() self.scroll = QScrollArea() self.scroll.setWidgetResizable(True) font = QFont("Helvetica", 9, QFont.Bold) ### Generate sliders ### sliders = [] for name in self.jsp.joint_list: if name not in self.jsp.free_joints: continue joint = self.jsp.free_joints[name] if joint['min'] == joint['max']: continue joint_layout = QVBoxLayout() row_layout = QHBoxLayout() label = QLabel(name) label.setFont(font) row_layout.addWidget(label) display = QLineEdit("0.00") display.setAlignment(Qt.AlignRight) display.setFont(font) display.setReadOnly(True) row_layout.addWidget(display) joint_layout.addLayout(row_layout) slider = QSlider(Qt.Horizontal) slider.setFont(font) slider.setRange(0, RANGE) slider.setValue(RANGE / 2) joint_layout.addWidget(slider) self.joint_map[name] = { 'slidervalue': 0, 'display': display, 'slider': slider, 'joint': joint } # Connect to the signal provided by QSignal slider.valueChanged.connect( lambda event, name=name: self.onValueChangedOne(name)) sliders.append(joint_layout) # Determine number of rows to be used in grid self.num_rows = num_rows # if desired num of rows wasn't set, default behaviour is a vertical layout if self.num_rows == 0: self.num_rows = len(sliders) # equals VBoxLayout # Generate positions in grid and place sliders there self.positions = self.generate_grid_positions(len(sliders), self.num_rows) for item, pos in zip(sliders, self.positions): self.gridlayout.addLayout(item, *pos) # Set zero positions read from parameters self.center() # Synchronize slider and displayed value self.sliderUpdate(None) # Set up a signal for updating the sliders based on external joint info self.sliderUpdateTrigger.connect(self.updateSliders) self.scrollable.setLayout(self.gridlayout) self.scroll.setWidget(self.scrollable) self.vlayout.addWidget(self.scroll) # Buttons for randomizing and centering sliders and # Spinbox for on-the-fly selecting number of rows self.randbutton = QPushButton('Randomize', self) self.randbutton.clicked.connect(self.randomize_event) self.vlayout.addWidget(self.randbutton) self.ctrbutton = QPushButton('Center', self) self.ctrbutton.clicked.connect(self.center_event) self.vlayout.addWidget(self.ctrbutton) self.maxrowsupdown = QSpinBox() self.maxrowsupdown.setMinimum(1) self.maxrowsupdown.setMaximum(len(sliders)) self.maxrowsupdown.setValue(self.num_rows) self.maxrowsupdown.valueChanged.connect(self.reorggrid_event) self.vlayout.addWidget(self.maxrowsupdown) self.setLayout(self.vlayout)
class DynreconfClientWidget(GroupWidget): """ A wrapper of dynamic_reconfigure.client instance. Represents a widget where users can view and modify ROS params. """ def __init__(self, reconf, node_name): """ :type reconf: dynamic_reconfigure.client :type node_name: str """ group_desc = reconf.get_group_descriptions() rospy.logdebug('DynreconfClientWidget.group_desc=%s', group_desc) super(DynreconfClientWidget, self).__init__(ParamUpdater(reconf), group_desc, node_name) # Save and load buttons self.button_widget = QWidget(self) self.button_header = QHBoxLayout(self.button_widget) self.button_header.setContentsMargins(QMargins(0, 0, 0, 0)) self.load_button = QPushButton() self.save_button = QPushButton() self.load_button.setIcon(QIcon.fromTheme('document-open')) self.save_button.setIcon(QIcon.fromTheme('document-save')) self.load_button.clicked[bool].connect(self._handle_load_clicked) self.save_button.clicked[bool].connect(self._handle_save_clicked) self.button_header.addWidget(self.save_button) self.button_header.addWidget(self.load_button) self.setMinimumWidth(150) self.reconf = reconf self.updater.start() self.reconf.config_callback = self.config_callback self._node_grn = node_name def get_node_grn(self): return self._node_grn def config_callback(self, config): #TODO: Think about replacing callback architecture with signals. if config: # TODO: should use config.keys but this method doesnt exist names = [name for name, v in config.items()] # v isn't used but necessary to get key and put it into dict. rospy.logdebug('config_callback name={} v={}'.format(name, v)) for widget in self.editor_widgets: if isinstance(widget, EditorWidget): if widget.param_name in names: rospy.logdebug('EDITOR widget.param_name=%s', widget.param_name) widget.update_value(config[widget.param_name]) elif isinstance(widget, GroupWidget): cfg = find_cfg(config, widget.param_name) rospy.logdebug('GROUP widget.param_name=%s', widget.param_name) widget.update_group(cfg) def _handle_load_clicked(self): filename = QFileDialog.getOpenFileName( self, self.tr('Load from File'), '.', self.tr('YAML file {.yaml} (*.yaml)')) if filename[0] != '': self.load_param(filename[0]) def _handle_save_clicked(self): filename = QFileDialog.getSaveFileName( self, self.tr('Save parameters to file...'), '.', self.tr('YAML files {.yaml} (*.yaml)')) if filename[0] != '': self.save_param(filename[0]) def save_param(self, filename): configuration = self.reconf.get_configuration() if configuration is not None: with file(filename, 'w') as f: yaml.dump(configuration, f) def load_param(self, filename): with file(filename, 'r') as f: configuration = {} for doc in yaml.load_all(f.read()): configuration.update(doc) self.reconf.update_configuration(configuration) def close(self): self.reconf.close() self.updater.stop() for w in self.editor_widgets: w.close() self.deleteLater() def filter_param(self, filter_key): #TODO impl pass