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, 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): 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 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()
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 __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)
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 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 __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, 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)
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)
class NavViewWidget(QWidget): 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 new_nav_view(self): if self._nav_view: self._nav_view.close() self._nav_view = NavView(self.map_topic, self.paths, self.polygons, tf_listener=self._tf, parent=self) self._set_pose.clicked.connect(self._nav_view.pose_mode) self._set_goal.clicked.connect(self._nav_view.goal_mode) self._layout.addWidget(self._nav_view) 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 is None: qWarning( 'NavView.dragEnterEvent(): not hasattr(item, ros_topic_name_)' ) return else: topic_name = str(e.mimeData().text()) if accepted_topic(topic_name): e.acceptProposedAction() def dropEvent(self, e): if e.mimeData().hasText(): topic_name = str(e.mimeData().text()) else: dropped_item = e.source().selectedItems()[0] topic_name = str(dropped_item.data(0, Qt.UserRole)) topic_type, array = get_field_type(topic_name) if not array: if topic_type is OccupancyGrid: self.map_topic = topic_name # Swap out the nav view for one with the new topics self.new_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): instance_settings.set_value("map_topic", self.map_topic) instance_settings.set_value("paths", self.paths) instance_settings.set_value("polygons", self.polygons) def restore_settings(self, plugin_settings, instance_settings): try: self.map_topic = instance_settings.value("map_topic", "/map") except Exception: pass try: self.paths = instance_settings.value("paths", []) except Exception: pass try: self.polygons = instance_settings.value("polygons", []) except Exception: pass self.new_nav_view() def trigger_configuration(self): """ Callback when the configuration button is clicked """ changed = False map_topics = sorted(rostopic.find_by_type('nav_msgs/OccupancyGrid')) try: index = map_topics.index(self.map_topic) except ValueError: index = 0 map_topic, ok = QInputDialog.getItem(self, "Select map topic name", "Topic name", map_topics, index) if ok: if map_topic != self.map_topic: changed = True self.map_topic = map_topic # Paths path_topics = sorted(rostopic.find_by_type('nav_msgs/Path')) path_topics = [(topic, topic in self.paths) for topic in path_topics] dialog = ListDialog("Select path topic(s)", path_topics, self) paths, ok = dialog.exec_() if ok: if not paths: changed = True diff = set(paths).symmetric_difference(set(self.paths)) if diff: self.paths = paths changed = True # Polygons polygon_topics = sorted( rostopic.find_by_type('geometry_msgs/PolygonStamped')) polygon_topics = [(topic, topic in self.polygons) for topic in polygon_topics] dialog = ListDialog("Select polygon topic(s)", polygon_topics, self) polygons, ok = dialog.exec_() if ok: if not polygons: changed = True diff = set(polygons).symmetric_difference(set(self.polygons)) if diff: self.polygons = polygons changed = True if changed: rospy.logdebug( "New configuration is different, creating a new nav_view") self.new_nav_view()
def init_sliders(self): sliderbox = self._widget.findChild(QLayout,'Sliders') firstCol = QVBoxLayout() graph_button=QPushButton() graph_button.setCheckable(True) graph_button.setText("Graph Off") graph_button.toggle() graph_button.clicked.connect(self.set_graph_state) self.graph_button = graph_button reset_button=QPushButton() reset_button.setCheckable(False) reset_button.setText("Reset") reset_button.clicked.connect(self.set_reset) self.graph_button = graph_button self.reset_button = reset_button firstCol.addWidget(graph_button) firstCol.addWidget(reset_button) firstCol.addWidget(graph_button) firstCol.setAlignment(graph_button,Qt.AlignVCenter) zero_button=QPushButton() zero_button.setCheckable(False) zero_button.setText("Set All Zero") zero_button.clicked.connect(self.set_pressure_zero) self.zero_button = zero_button firstCol.addWidget(zero_button) firstCol.setAlignment(zero_button,Qt.AlignVCenter) transition_box = QVBoxLayout() label = QLabel() label.setAlignment(Qt.AlignCenter) label.setText("Transition Time") spinbox = QDoubleSpinBox() spinbox.setMinimum(0) spinbox.setMaximum(10) spinbox.setValue(self.settings['transitions']) spinbox.setDecimals(1) spinbox.setSingleStep(0.1) spinbox.setSuffix(" sec") spinbox.valueChanged.connect(self.set_transition_value) transition_box.addWidget(label) transition_box.addWidget(spinbox) transition_box.setAlignment(label,Qt.AlignBottom) transition_box.setAlignment(spinbox,Qt.AlignTop) firstCol.addLayout(transition_box) self.sliders = [] sliderbox.addLayout(firstCol) all_rows_layout = QVBoxLayout() g_idx = 0 for row in self.settings['gui_config']: num_groups_row = len(row) row_layout = QHBoxLayout() for gr_idx, s_group in enumerate(row): g_channels = s_group['channels'] g_layout = s_group['layout'] if 'horiz' in g_layout: group_layout = QHBoxLayout() else: group_layout = QVBoxLayout() control_group = {'sliders': [], 'on_off': None} label = QLabel() label.setText("Group. %d"%(g_idx+1)) label.setAlignment(Qt.AlignCenter) group_layout.addWidget(label) for c_idx, s_idx in enumerate(g_channels): idx = s_idx*1 slider_group={'slider':None, 'number':None} layout_cluster = QVBoxLayout() labelfirst = QLabel() labelfirst.setAlignment(Qt.AlignCenter) labellast = QLabel() labellast.setAlignment(Qt.AlignCenter) layout = QVBoxLayout() if 'diff' in g_layout and c_idx == 0: sublayout=QHBoxLayout() layout.setAlignment(Qt.AlignVCenter) slider = QSlider(Qt.Horizontal) slider.setMinimum(-100) slider.setMaximum(100) slider.setValue(0) slider.setTickPosition(QSlider.TicksRight) slider.setTickInterval(20) spinbox = QDoubleSpinBox() spinbox.setMinimum(-10) spinbox.setMaximum(10) spinbox.setValue(0) spinbox.setDecimals(1) spinbox.setSingleStep(0.1) labellast.setText("%0.1f"%(10)) # These are flipped becasue of order labelfirst.setText("%0.1f"%(-10)) max_label = labellast min_label = labelfirst else: layout.setAlignment(Qt.AlignHCenter) slider = QSlider(Qt.Vertical) slider.setMinimum(self.settings['min_pressure'][idx]*10.0) slider.setMaximum(self.settings['max_pressure'][idx]*10.0) slider.setValue(0) slider.setTickPosition(QSlider.TicksRight) slider.setTickInterval(20) spinbox = QDoubleSpinBox() spinbox.setMinimum(self.settings['min_pressure'][idx]) spinbox.setMaximum(self.settings['max_pressure'][idx]) spinbox.setValue(0) spinbox.setDecimals(1) spinbox.setSingleStep(0.1) labelfirst.setText("%0.1f"%(self.settings['max_pressure'][idx])) labellast.setText("%0.1f"%(self.settings['min_pressure'][idx])) max_label = labelfirst min_label = labellast cb_function_number = lambda value, g_idx=g_idx, s_idx=c_idx, slider=False: self.send_slider_value(g_idx,s_idx,value,slider) cb_function_slider = lambda value, g_idx=g_idx, s_idx=c_idx, slider=True: self.send_slider_value(g_idx,s_idx,value,slider) slider.valueChanged.connect(cb_function_slider) spinbox.valueChanged.connect(cb_function_number) if 'diff' in g_layout and c_idx == 0: sublayout.addWidget(labelfirst) sublayout.addWidget(slider) sublayout.addWidget(labellast) layout.addWidget(spinbox) layout.addLayout(sublayout) else: layout.addWidget(labelfirst) layout.addWidget(slider) layout.addWidget(labellast) layout.addWidget(spinbox) layout.setAlignment(slider, Qt.AlignHCenter) layout.setAlignment(spinbox, Qt.AlignHCenter) layout_cluster.addLayout(layout) slider_group['slider'] = slider slider_group['number'] = spinbox slider_group['max_label'] = max_label slider_group['min_label'] = min_label control_group['sliders'].append(slider_group) group_layout.addLayout(layout_cluster) on_button=QPushButton() on_button.setCheckable(True) on_button.setText("Off") if self.settings['channel_states'][idx]: on_button.toggle() on_button.setText("On") on_button.clicked.connect(lambda state, g_idx=g_idx: self.send_channel_state(g_idx,state)) group_layout.addWidget(on_button) row_layout.addLayout(group_layout) row_layout.addSpacing(20) control_group['on_off'] = on_button self.sliders.append(control_group) g_idx+=1 all_rows_layout.addLayout(row_layout) sliderbox.addLayout(all_rows_layout)
def __init__(self, context): #Start client -rosbridge self.client = roslibpy.Ros(host='localhost', port=5803) self.client.run() super(Dashboard, self).__init__(context) # Give QObjects reasonable names self.setObjectName('Dashboard') # Process standalone plugin command-line arguments from argparse import ArgumentParser parser = ArgumentParser() # Add argument(s) to the parser. parser.add_argument("-q", "--quiet", action="store_true", dest="quiet", help="Put plugin in silent mode") args, unknowns = parser.parse_known_args(context.argv()) if not args.quiet: print 'arguments: ', args print 'unknowns: ', unknowns # Create QWidget self._widget = QWidget() # Get path to UI file which should be in the "resource" folder of this package ui_file = os.path.join(rospkg.RosPack().get_path('rqt_dashboard'), 'resource', 'Dashboard.ui') # Extend the widget with all attributes and children from UI file loadUi(ui_file, self._widget) # Give QObjects reasonable names self._widget.setObjectName('DashboardUi') # Set up signal-slot connections self._widget.set_imu_angle_button.clicked.connect(self.setImuAngle) self._widget.imu_angle.valueChanged.connect(self.imuAngleChanged) self._widget.auto_wall_dist_button.clicked.connect(self.setAutoWallDist) self._widget.auto_wall_dist.valueChanged.connect(self.autoWallDistChanged) self._widget.ball_reset_button.clicked.connect(self.resetBallCount) self._widget.ball_reset_count.valueChanged.connect(self.resetBallChanged) # Add buttons for auto modes v_layout = self._widget.auto_mode_v_layout #vertical layout storing the buttons self.auto_mode_button_group = QButtonGroup(self._widget) # needs to be a member variable so the publisher can access it to see which auto mode was selected # Search for auto_mode config items for i in range(1,100): # loop will exit when can't find the next auto mode, so really only a while loop needed, but exiting at 100 will prevent infinite looping if rospy.has_param("/auto/auto_mode_" + str(i)): auto_sequence = rospy.get_param("/auto/auto_mode_" + str(i)) new_auto_mode = QWidget() new_h_layout = QHBoxLayout() new_h_layout.setContentsMargins(0,0,0,0) new_button = QRadioButton("Mode " + str(i)) new_button.setStyleSheet("font-weight: bold") self.auto_mode_button_group.addButton(new_button, i) #second arg is the button's id new_h_layout.addWidget( new_button ) new_h_layout.addWidget( QLabel(", ".join(auto_sequence)) ) hSpacer = QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Minimum) new_h_layout.addItem(hSpacer) new_auto_mode.setLayout( new_h_layout ) v_layout.addWidget(new_auto_mode) else: print(str(i-1) + " auto modes found.") # if no auto modes found, inform the user with a label if (i-1) == 0: v_layout.addWidget( QLabel("No auto modes found") ) break #break out of for loop searching for auto modes # auto state stuff self.autoState = 0 self.displayAutoState() #display initial auto state # publish thread publish_thread = Thread(target=self.publish_thread) #args=(self,)) publish_thread.start() # number balls display self.zero_balls = QPixmap(":/images/0_balls.png") self.one_ball = QPixmap(":/images/1_ball.png") self.two_balls = QPixmap(":/images/2_balls.png") self.three_balls = QPixmap(":/images/3_balls.png") self.four_balls = QPixmap(":/images/4_balls.png") self.five_balls = QPixmap(":/images/5_balls.png") self.more_than_five_balls = QPixmap(":/images/more_than_5_balls.png") self.n_balls = -1 #don't know n balls at first #in range stuff self.shooter_in_range = False self.turret_in_range = False self.in_range_pixmap = QPixmap(":/images/GreenTarget.png") self.not_in_range_pixmap = QPixmap(":/images/RedTarget.png") self._widget.in_range_display.setPixmap(self.not_in_range_pixmap) # Show _widget.windowTitle on left-top of each plugin (when # it's set in _widget). This is useful when you open multiple # plugins at once. Also if you open multiple instances of your # plugin at once, these lines add number to make it easy to # tell from pane to pane. if context.serial_number() > 1: self._widget.setWindowTitle(self._widget.windowTitle() + (' (%d)' % context.serial_number())) # Add widget to the user interface context.add_widget(self._widget) #initialize subscribers last, so that any callbacks they execute won't interfere with init auto_state_listener = roslibpy.Topic(self.client, '/auto/auto_state', 'behavior_actions/AutoState') self.auto_state_sub = auto_state_listener.subscribe(self.autoStateCallback) n_balls_listener = roslibpy.Topic(self.client,'/num_powercells','std_msgs/UInt8') self.n_balls_sub = n_balls_listener.subscribe(self.nBallsCallback) shooter_in_range_listener = roslibpy.Topic(self.client, '/shooter/shooter_in_range', std_msgs.msg.Bool) self.shooter_in_range_sub = shooter_in_range_listener.subscribe(self.shooterInRangeCallback) turret_in_range_listener = roslibpy.Topic(self.client, '/align_to_shoot/turret_in_range', std_msgs.msg.Bool) self.turret_in_range_sub = turret_in_range_listener.subscribe(self.turretInRangeCallback) self.autoStateSignal.connect(self.autoStateSlot) self.nBallsSignal.connect(self.nBallsSlot) self.shooterInRangeSignal.connect(self.shooterInRangeSlot) self.turretInRangeSignal.connect(self.turretInRangeSlot)
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))
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)
def __init__(self, context): super(SpectrogramPlugin, self).__init__(context) self.setObjectName('Spectrogram') sns.set(style="whitegrid", palette="bright", color_codes=True) self._widget = QWidget() layout = QVBoxLayout() self._widget.setLayout(layout) layout_ = QHBoxLayout() self.lbl_topic = QLabel('Topic:') layout_.addWidget(self.lbl_topic) self.le_topic = QLineEdit() layout_.addWidget(self.le_topic) self.apply_topic = QPushButton("Apply") self.apply_topic.clicked.connect(self.apply_clicked_topic) layout_.addWidget(self.apply_topic) layout.addLayout(layout_) layout_ = QHBoxLayout() self.lbl_lcf = QLabel('Low-cut Freq.[Hz]:') layout_.addWidget(self.lbl_lcf) self.spb_lcf = QSpinBox() self.spb_lcf.setRange(0, 50) self.spb_lcf.setValue(0) layout_.addWidget(self.spb_lcf) self.apply_lcf = QPushButton("Apply") self.apply_lcf.clicked.connect(self.apply_clicked_lcf) layout_.addWidget(self.apply_lcf) layout.addLayout(layout_) layout_ = QHBoxLayout() self.lbl_hcf = QLabel('High-cut Freq.[Hz]:') layout_.addWidget(self.lbl_hcf) self.spb_hcf = QSpinBox() self.spb_hcf.setRange(50, self.vib_freq / 2) self.spb_hcf.setValue(self.vib_freq / 2) layout_.addWidget(self.spb_hcf) self.apply_hcf = QPushButton("Apply") self.apply_hcf.clicked.connect(self.apply_clicked_hcf) layout_.addWidget(self.apply_hcf) layout.addLayout(layout_) #self.fig, self.axes = plt.subplots(2, 1, sharex=True) self.fig = plt.figure() self.ax = self.fig.add_subplot(1, 1, 1) self.canvas = FigureCanvas(self.fig) self.fig.tight_layout() layout.addWidget(self.canvas) context.add_widget(self._widget) self.update_signal.connect(self.update_spectrogram) self.subscriber_signal.connect(self.update_subscriber) self.subscriber_signal.emit('spectrum')
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")
class TreeWidgetItem(QTreeWidgetItem): _column_names = ['topic', 'type', 'min', 'value', 'max', 'checkbox'] def flags(self, index): print "checking flags" if index == 1: return QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled else: return QtCore.Qt.ItemIsEnabled 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) @Slot(int) def sliderValueChanged(self, value): self.setText(2, str(value)) print "value changed to %d" % value pass # print(topic_name) def setData(self, column, role, value): if role == Qt.CheckStateRole: state = self.checkState(column) # if state: # item.setAutoFillBackground # if state: # super(TreeWidgetItem, self).setData(column, Qt.UserRole, self._selected_items.index(self._topic_name)) super(TreeWidgetItem, self).setData(column, role, value) if role == Qt.CheckStateRole and state != self.checkState(column): self._check_state_changed_callback(self._topic_name)
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 TopicSelection(QWidget): recordSettingsSelected = Signal(bool, list, dict) def __init__(self): super(TopicSelection, self).__init__() master = rosgraph.Master('rqt_bag_recorder') self.setWindowTitle("Record a Simulation") self.resize(650, 720) pre = TS.get_time_series_pre_feature_options() glob = TS.get_global_time_series_features_options() # print pre # print glob all_topics = S.get_topics_options() keys = all_topics.keys() # print all_topics.keys()[0] self.plp_filename = "" self.group_selected_items = dict() self.group_areas = dict() self.group_main_widget = dict() self.group_selection_vlayout = dict() self.group_item_all = dict() # self.main_vlayout = QVBoxLayout(self) self.main_vlayout = QVBoxLayout(self) self.group_label = dict() self.selected_topics = [] self.items_list = [] for group_name in all_topics: self.group_selected_items[group_name] = [] self.group_areas[group_name] = QScrollArea(self) self.group_main_widget[group_name] = QWidget( self.group_areas[group_name]) self.group_label[group_name] = QLabel(group_name, self) self.group_label[group_name].setAlignment(Qt.AlignCenter) self.main_vlayout.addWidget(self.group_label[group_name]) self.main_vlayout.addWidget(self.group_areas[group_name]) self.group_selection_vlayout[group_name] = QVBoxLayout(self) self.group_item_all[group_name] = MyQCheckBox( "All", self, self.group_selection_vlayout[group_name], None) self.MakeCheckBoxList(self.group_selection_vlayout[group_name], self.group_selected_items[group_name], all_topics[group_name], self.group_item_all[group_name]) self.group_main_widget[group_name].setLayout( self.group_selection_vlayout[group_name]) self.group_areas[group_name].setWidget( self.group_main_widget[group_name]) self.label1 = QLabel("Scenarios", self) self.label1.setAlignment(Qt.AlignCenter) self.main_vlayout.addWidget(self.label1) scanarios = S.get_scenarios_options() self.scanarios_answer = scanarios self.map_answer = dict() # print scanarios keys1 = scanarios.keys() # print keys1[0] # print scanarios[keys1[0]]["name"] # print scanarios[keys1[0]]["params"] # # for item in scanarios[keys1[0]]["params"]: # print item self.radio_items = dict() self.number_group = QButtonGroup(self) for id_radio in scanarios: self.radio_items[id_radio] = QRadioButton( scanarios[id_radio]["name"]) self.number_group.addButton(self.radio_items[id_radio]) self.main_vlayout.addWidget(self.radio_items[id_radio]) self.radio_items[id_radio].setChecked(False) self.radio_items[id_radio].clicked.connect( partial(self.callConsult, scanarios[id_radio]["params"], id_radio)) self.select_path = QLineEdit() self.save_path = QLineEdit() self.select_path.setEnabled(False) self.save_path.setEnabled(False) self.plp_button = QPushButton("Select PLP Python File...", self) self.plp_button.clicked.connect(self.onPlpClicked) self.two_buttons1 = QHBoxLayout(self) self.two_buttons1.addWidget(self.plp_button) self.two_buttons1.addWidget(self.select_path) self.main_vlayout.addLayout(self.two_buttons1) # self.label = QLabel("live Topics", self) # self.label.setAlignment(Qt.AlignCenter) # # self.main_vlayout.addWidget(self.label) # self.area = QScrollArea(self) # self.main_widget = QWidget(self.area) self.ok_button = QPushButton("Record", self) self.ok_button.clicked.connect(self.onButtonClicked) self.ok_button.setEnabled(False) self.choose_button = QPushButton("Get Last Export Choose", self) self.choose_button.clicked.connect(self.onButtonChooseCliked) self.clear_button = QPushButton("Clear Selection", self) self.clear_button.clicked.connect(self.onClearClicked) self.choose_clear_buttons = QHBoxLayout(self) self.choose_clear_buttons.addWidget(self.choose_button) self.choose_clear_buttons.addWidget(self.clear_button) self.main_vlayout.addLayout(self.choose_clear_buttons) # self.main_vlayout.addRow(self.choose_button, self.clear_button) # self.from_nodes_button = QPushButton("From Nodes", self) # self.from_nodes_button.clicked.connect(self.onFromNodesButtonClicked) # self.main_vlayout.addWidget(self.area) # self.main_vlayout.addWidget(self.choose_button) self.main_vlayout.addWidget(self.ok_button) # self.main_vlayout.addWidget(self.from_nodes_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_list4 = map(lambda l: l[0], master.getPublishedTopics('')) # topic_data_list4.sort() # for topic in topic_data_list4: # self.addCheckBox(topic, self.selection_vlayout, self.selected_topics) # self.main_widget.setLayout(self.selection_vlayout) # self.area.setWidget(self.main_widget) # print S.get_scenarios_options() self.show() def onClearClicked(self): self.clearTopicCheckState() def clearTopicCheckState(self): for item in self.items_list: item.setCheckState(False) for item in self.group_item_all.values(): item.setCheckState(False) def onButtonChooseCliked(self): for checkbox in self.items_list: checkbox.setCheckState(Qt.Unchecked) with open(get_path() + "logger_topic.log", 'r') as f: topics = f.read().splitlines() for checkbox in self.items_list: if checkbox.text() in topics: checkbox.setCheckState(Qt.Checked) def callConsult(self, params, id_radio): self.input_dialog = inputDialog(params, id_radio) self.input_dialog.ParamsSelected.connect(self.params_answer) # item, ok = QInputDialog.getItem(self, "select parameter", # "list of parameters", params, 0, False) # if ok and item: # self.gettext(item) def params_answer(self, params, label_items, id_radio): if id_radio == 0: # print "------" + str(id_radio) self.number_group.setExclusive(False) # print self.radio_items for item in self.radio_items: # print self.radio_items[item] self.radio_items[item].setChecked(False) self.number_group.setExclusive(True) self.enable_record() else: # print id_radio # print params # print params.values() a = {} # print label_items for item, name in zip(params, label_items): # value = params[item].text() a[name] = item.text().encode("utf-8") # print a self.map_answer = {"id": id_radio, "params": a} self.enable_record() return self.map_answer # for item in params.values(): # print item.text() # pass # def gettext(self, item): # text, ok = QInputDialog.getText(self, 'Text Input Dialog', item) # # if ok: # print self.multipleReplace(str(text), item) # # # def multipleReplace(self, text, item): # for key in self.scanarios_answer: # # # print self.scanarios_answer[key]["params"] # for id, par in enumerate(self.scanarios_answer[key]["params"]): # if par == item: # self.scanarios_answer[key]["params"][id] = text # for id_radio in self.radio_items.keys(): # if self.radio_items[id_radio].isChecked(): # self.map_answer = {"id" : id_radio, "params" : self.scanarios_answer[id_radio]["params"]} # return self.map_answer def MakeCheckBoxList(self, selection_vlayout, selected, topics_Keys, item_all): item_all.stateChanged.connect( lambda x: self.updateList(x, item_all, None)) selection_vlayout.addWidget(item_all) topic_data_list = topics_Keys topic_data_list.sort() for topic in topic_data_list: self.addCheckBox(topic, selection_vlayout, selected) def get_item_by_name(self, item_name): for item in self.items_linputDialogist: if item.text() == item_name: return item return None 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.append(item) selection_vlayout.addWidget(item) def changeTopicCheckState(self, topic, state): for item in self.items_list: if item.text() == topic: item.setCheckState(state) return # def updateList(self, state, topic=None, force_update_state=False): # if topic is None: # The "All" checkbox was checked / unchecked # if state == Qt.Checked: # self.item_all.setTristate(False) # for item in self.items_list: # if item.checkState() == Qt.Unchecked: # item.setCheckState(Qt.Checked) # elif state == Qt.Unchecked: # self.item_all.setTristate(False) # for item in self.items_list: # if item.checkState() == Qt.Checked: # item.setCheckState(Qt.Unchecked) # else: # if state == Qt.Checked: # self.selected_topics.append(topic) # else: # self.selected_topics.remove(topic) # if self.item_all.checkState() == Qt.Checked: # self.item_all.setCheckState(Qt.PartiallyChecked) # # if self.selected_topics == []: # self.ok_button.setEnabled(False) # else: # self.ok_button.setEnabled(True) def updateList(self, state, item_clicked, topic=None, force_update_state=False): if type(item_clicked) is str: item_clicked = self.get_item_by_name(item_clicked) if item_clicked is None: return 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: 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: if item.checkState() == Qt.Checked and \ item.selection_vlayout == item_clicked.selection_vlayout: item.setCheckState(Qt.Unchecked) 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) self.enable_record() def enable_record(self): temp_selected_topics = [] for item in self.group_selected_items.values(): if item: for i in item: temp_selected_topics.append(i) flag = False for item in self.radio_items: if self.radio_items[item].isChecked(): flag = True break # print self.radio_items[item] # flag = reduce(lambda acc, curr: acc or , self.radio_items, False) # print "------+++++-" + str(flag) if len(temp_selected_topics) > 0 or len(self.selected_topics) > 0: self.ok_button.setEnabled(flag) else: self.ok_button.setEnabled(False) def onButtonClicked(self): # print self.group_selected_items for item in self.group_selected_items.values(): if item: for i in item: self.selected_topics.append(i) topics = self.selected_topics with open(get_path() + 'logger_topic.log', "w") as f: for topic in topics: f.write(topic + "\n") self.close() # TBD - runing plp on shell on itself # if self.plp_filename != "": # from .plp import Plp # Plp(self.plp_filename) self.recordSettingsSelected.emit(False, self.selected_topics, self.map_answer) def get_current_opened_directory(self, filepath): import os direc = "/" if os.path.isfile(filepath): with open(filepath, 'r') as f: pathes = f.read() direc = pathes.rsplit('/', 1)[0] return direc def onPlpClicked(self): import inspect, os filepath = os.path.dirname( os.path.abspath(inspect.getfile( inspect.currentframe()))) + "/log/save_plp.log" current_directory = self.get_current_opened_directory(filepath) fd = QFileDialog(self) wc = "Csv files {.py} (*.py)" # print current_directory filename, filter = fd.getOpenFileNamesAndFilter( filter=wc, initialFilter=('*.py'), directory=current_directory) if len(filename): self.plp_filename = filename[0] with open(filepath, "w") as f: f.write(self.plp_filename) self.select_path.setText(self.plp_filename) # print self.plp_filename[0] def onFromNodesButtonClicked(self): self.node_selection = NodeSelection(self) def getTopicsByName(self, name): arr = S.get_topics_options() return arr[name]
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)
class HandEyeCalibration(Plugin): PLUGIN_TITLE = ' Intel OTC Robotics: Hand-Eye Calibration' def __init__(self, context): super(HandEyeCalibration, self).__init__(context) self.context = context self.node = context.node self.widget = QWidget() self.widget.setObjectName(self.PLUGIN_TITLE) self.widget.setWindowTitle(self.PLUGIN_TITLE) # Data self.Tsamples = [] # Toolbar _, path_pkg = get_resource('packages', 'handeye_dashboard') print("{}".format(path_pkg)) self.snapshot_action = QAction(QIcon.fromTheme('camera-photo'), 'Take a snapshot', self.widget) path = path_pkg + '/share/handeye_dashboard/images/capture.png' self.calibrate_action = QAction(QIcon(QPixmap.fromImage(QImage(path))), 'Get the camera/robot transform', self.widget) self.clear_action = QAction(QIcon.fromTheme('edit-clear'), 'Clear the record data.', self.widget) path = path_pkg + '/share/handeye_dashboard/images/UR5.png' self.execut_action = QAction(QIcon(QPixmap.fromImage(QImage(path))), 'EStart the publishing the TF.', self.widget) self.toolbar = QToolBar() self.toolbar.addAction(self.snapshot_action) self.toolbar.addAction(self.calibrate_action) self.toolbar.addAction(self.clear_action) self.toolbar.addAction(self.execut_action) # Toolbar0 self.l0 = QLabel(self.widget) self.l0.setText("Camera-Mount-Type: ") self.l0.setFixedWidth(150) self.l0.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) self.combobox = QComboBox(self.widget) self.combobox.addItem('attached on robot') self.combobox.addItem('fixed beside robot') self.toolbar0 = QToolBar() self.toolbar0.addWidget(self.l0) self.toolbar0.addWidget(self.combobox) # Toolbar1 self.l1 = QLabel(self.widget) self.l1.setText("Camera-Frame: ") self.l1.setFixedWidth(150) self.l1.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) self.camera_frame = QLineEdit(self.widget) self.camera_frame.setText("camera_link") self.toolbar1 = QToolBar() self.toolbar1.addWidget(self.l1) self.toolbar1.addWidget(self.camera_frame) # Toolbar2 self.l2 = QLabel(self.widget) self.l2.setText("Object-Frame: ") self.l2.setFixedWidth(150) self.l2.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) self.object_frame = QLineEdit(self.widget) self.object_frame.setText("calib_board") self.toolbar2 = QToolBar() self.toolbar2.addWidget(self.l2) self.toolbar2.addWidget(self.object_frame) # Toolbar3 self.l3 = QLabel(self.widget) self.l3.setText("Robot-Base-Frame: ") self.l3.setFixedWidth(150) self.l3.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) self.base_frame = QLineEdit(self.widget) self.base_frame.setText("base") self.toolbar3 = QToolBar() self.toolbar3.addWidget(self.l3) self.toolbar3.addWidget(self.base_frame) # Toolbar4 self.l4 = QLabel(self.widget) self.l4.setText("End-Effector-Frame: ") self.l4.setFixedWidth(150) self.l4.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) self.endeffector_frame = QLineEdit(self.widget) self.endeffector_frame.setText("tool0") self.toolbar4 = QToolBar() self.toolbar4.addWidget(self.l4) self.toolbar4.addWidget(self.endeffector_frame) # Toolbar5 self.l5 = QLabel(self.widget) self.l5.setText("Sample-Number: ") self.l5.setFixedWidth(150) self.l5.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) self.le5 = QLineEdit(self.widget) self.le5.setValidator(QIntValidator()) self.le5.setText('10') self.le5.setReadOnly(True) self.toolbar5 = QToolBar() self.toolbar5.addWidget(self.l5) self.toolbar5.addWidget(self.le5) # TreeView self.treeview = QTreeView() self.treeview.setAlternatingRowColors(True) self.model = QStandardItemModel(self.treeview) self.treeview.setModel(self.model) self.treeview.setHeaderHidden(True) # TextEdit self.textedit = QTextEdit(self.widget) self.textedit.setReadOnly(True) # Layout self.layout = QVBoxLayout() self.layout.addWidget(self.toolbar0) self.layout.addWidget(self.toolbar1) self.layout.addWidget(self.toolbar2) self.layout.addWidget(self.toolbar3) self.layout.addWidget(self.toolbar4) self.layout.addWidget(self.toolbar5) self.layout.addWidget(self.toolbar) self.layoutH = QHBoxLayout() self.layoutH.addWidget(self.treeview) self.layoutH.addWidget(self.textedit) self.layout.addLayout(self.layoutH) self.widget.setLayout(self.layout) # Add the widget to the user interface if context.serial_number() > 1: self.widget.setWindowTitle(self.widget.windowTitle() + (' (%d)' % context.serial_number())) context.add_widget(self.widget) # Make the connections self.snapshot_action.triggered.connect(self.take_snapshot) self.calibrate_action.triggered.connect(self.calibration) self.clear_action.triggered.connect(self.clear) self.execut_action.triggered.connect(self.execution) # Package path self.path_pkg = path_pkg # Set up TF self.cli = self.node.create_client(HandeyeTF, 'handeye_tf_service') while not self.cli.wait_for_service(timeout_sec=1.0): self.node.get_logger().info( 'service not available, waiting again...') self.req = HandeyeTF.Request() def clear(self): # >>> Clear the recorded samples self.textedit.append('Clearing the recorded data ...') self.textedit.clear() self.Tsamples = [] self.model.clear() def get_tf_transform(self, frame_id, child_frame_id): self.req.transform.header.frame_id = frame_id self.req.transform.child_frame_id = child_frame_id self.req.publish.data = False future = self.cli.call_async(self.req) rclpy.spin_until_future_complete(self.node, future) transform = TransformStamped() try: result = future.result() except Exception as e: self.node.get_logger().info('Service call failed %r' % (e, )) else: transform = result.tf_lookup_result return transform def publish_tf_transform(self, transform_to_publish): self.req.publish.data = True self.req.transform = transform_to_publish future = self.cli.call_async(self.req) rclpy.spin_until_future_complete(self.node, future) try: future.result() except Exception as e: self.node.get_logger().info('Service call failed %r' % (e, )) else: self.node.get_logger().info( 'Send the camera-robot transform :\n\tfrom `{}` to `{}`.'. format(self.req.transform.header.frame_id, self.req.transform.child_frame_id)) def take_snapshot(self): # >>> Take the snapshot self.textedit.append('Taking snapshot ...') # Get the transform from `tool0` to `base_link` T = self.get_tf_transform(self.base_frame.text(), self.endeffector_frame.text()) bTe = np.zeros((4, 4)) q = [ T.transform.rotation.w, T.transform.rotation.x, T.transform.rotation.y, T.transform.rotation.z ] bTe = br.quaternion.to_transform(q) bTe[:3, 3] = np.array([ T.transform.translation.x, T.transform.translation.y, T.transform.translation.z ]) self.textedit.append('Lookup transform: from `{}` to `{}`.'.format( self.base_frame.text(), self.endeffector_frame.text())) self.node.get_logger().info(bcolors.OKGREEN + 'bTe:' + bcolors.ENDC + '\n{}'.format(bTe)) # Get the transform from `calib_board` to `camera_link` T = self.get_tf_transform(self.camera_frame.text(), self.object_frame.text()) cTo = np.zeros((4, 4)) q = [ T.transform.rotation.w, T.transform.rotation.x, T.transform.rotation.y, T.transform.rotation.z ] cTo = br.quaternion.to_transform(q) cTo[:3, 3] = np.array([ T.transform.translation.x, T.transform.translation.y, T.transform.translation.z ]) self.textedit.append('Lookup transform: from `{}` to `{}`.'.format( self.camera_frame.text(), self.object_frame.text())) self.node.get_logger().info(bcolors.OKGREEN + 'cTo:' + bcolors.ENDC + '\n{}'.format(cTo)) parent = QStandardItem('Snapshot {}'.format(len(self.Tsamples))) child_1 = QStandardItem('bTe:\n{}\n{}\n{}\n{}'.format( bTe[0, :], bTe[1, :], bTe[2, :], bTe[3, :])) child_2 = QStandardItem('cTo:\n{}\n{}\n{}\n{}'.format( cTo[0, :], cTo[1, :], cTo[2, :], cTo[3, :])) parent.appendRow(child_1) parent.appendRow(child_2) self.model.appendRow(parent) self.Tsamples.append((bTe, cTo)) self.le5.setText(str(len(self.Tsamples))) def calibration(self): # >>> Compute the calibration self.textedit.append('Making the calibration ...') if len(self.Tsamples) == 0: self.textedit.append( 'No transform recorded, please take snapshots.') return # save samples to `dataset.json` file save_samples_to_file(self.Tsamples) import handeye if self.combobox.currentIndex() == 0: solver_cri = handeye.calibrator.HandEyeCalibrator(setup='Moving') if self.combobox.currentIndex() == 1: solver_cri = handeye.calibrator.HandEyeCalibrator(setup='Fixed') for sample in self.Tsamples: solver_cri.add_sample(sample[0], sample[1]) try: bTc = solver_cri.solve(method=handeye.solver.Daniilidis1999) # save the calibration result to 'camera-robot.json' file file_output = '/tmp/' + 'camera-robot.json' with open(file_output, 'w') as f: json.dump(bTc.tolist(), f) except Exception: self.textedit.append("Failed to solve the hand-eye calibration.") def execution(self): # >>> Publish the camera-robot transform self.textedit.append('Publishing the camera TF ...') file_input = '/tmp/' + 'camera-robot.json' with open(file_input, 'r') as f: datastore = json.load(f) to_frame = self.camera_frame.text() if self.combobox.currentIndex() == 0: from_frame = self.endeffector_frame.text() if self.combobox.currentIndex() == 1: from_frame = self.base_frame.text() bTc = np.array(datastore) static_transformStamped = TransformStamped() static_transformStamped.header.stamp = ROSClock().now().to_msg() static_transformStamped.header.frame_id = from_frame static_transformStamped.child_frame_id = to_frame static_transformStamped.transform.translation.x = bTc[0, 3] static_transformStamped.transform.translation.y = bTc[1, 3] static_transformStamped.transform.translation.z = bTc[2, 3] q = br.transform.to_quaternion(bTc) static_transformStamped.transform.rotation.x = q[1] static_transformStamped.transform.rotation.y = q[2] static_transformStamped.transform.rotation.z = q[3] static_transformStamped.transform.rotation.w = q[0] self.publish_tf_transform(static_transformStamped) output_string = "camera-robot pose:\n" output_string += " Translation: [{}, {}, {}]\n".format( bTc[0, 3], bTc[1, 3], bTc[2, 3]) output_string += " Rotation: in Quaternion [{}, {}, {}, {}]".format( q[0], q[1], q[2], q[3]) file_path = '/tmp/' + 'camera-robot.txt' with open(file_path, 'w') as f: f.write(output_string) def shutdown_plugin(self): """ Unregister subscribers when the plugin shutdown """ pass def save_settings(self, plugin_settings, instance_settings): # Nothing to be done here pass def restore_settings(self, plugin_settings, instance_settings): # Nothing to be done here pass
def __init__(self, context, node_name): """ Initializaze things. :type node_name: str """ super(ParamClientWidget, self).__init__() self._node_grn = node_name self._toplevel_treenode_name = node_name self._editor_widgets = {} self._param_client = create_param_client( context.node, node_name, self._handle_param_event ) 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)) nodename_qlabel = QLabel(self) font = QFont('Trebuchet MS, Bold') font.setUnderline(True) font.setBold(True) font.setPointSize(10) nodename_qlabel.setFont(font) nodename_qlabel.setAlignment(Qt.AlignCenter) nodename_qlabel.setText(node_name) h_layout_nodeheader.addWidget(nodename_qlabel) # 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(bt_disable_node) 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. param_names = self._param_client.list_parameters() self.add_editor_widgets( self._param_client.get_parameters(param_names), self._param_client.describe_parameters(param_names) ) # Save and load buttons button_widget = QWidget(self) button_header = QHBoxLayout(button_widget) button_header.setContentsMargins(QMargins(0, 0, 0, 0)) load_button = QPushButton() save_button = QPushButton() load_button.setIcon(QIcon.fromTheme('document-open')) save_button.setIcon(QIcon.fromTheme('document-save')) load_button.clicked[bool].connect(self._handle_load_clicked) save_button.clicked[bool].connect(self._handle_save_clicked) button_header.addWidget(save_button) button_header.addWidget(load_button) self.setMinimumWidth(150)
def __init__(self, content): super(SliderPublisher, self).__init__() content = content.replace('\t', ' ') # get message types self.publishers = {} self.values = {} pkgs = [] # to keep track of key ordering in the yaml file order = [] old = [] for topic, info in yaml.load(content).items(): pkg, msg = info['type'].split('/') pkgs.append(__import__(pkg, globals(), locals(), ['msg'])) self.publishers[topic] = Publisher(topic, getattr(pkgs[-1].msg, msg), info) order.append((topic, [])) for key in info: self.values[key] = info[key] order[-1][1].append((content.find(' ' + key + ':'), key)) old.append((content.find(' ' + key + ':'), key)) for bound in ['min', 'max']: self.values[key][bound] = float(self.values[key][bound]) self.values[key]['val'] = 0 order[-1][1].sort() order.sort(key=lambda x: x[1][0][0]) # build sliders - thanks joint_state_publisher sliderUpdateTrigger = Signal() self.vlayout = QVBoxLayout(self) self.gridlayout = QGridLayout() font = QFont("Helvetica", 9, QFont.Bold) topic_font = QFont("Helvetica", 10, QFont.Bold) sliders = [] self.key_map = {} y = 0 for topic, keys in order: topic_layout = QVBoxLayout() label = QLabel(topic) label.setFont(topic_font) topic_layout.addWidget(label) self.gridlayout.addLayout(topic_layout, *(y, 0)) y += 1 for idx, key in keys: key_layout = QVBoxLayout() row_layout = QHBoxLayout() label = QLabel(key) 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) key_layout.addLayout(row_layout) slider = QSlider(Qt.Horizontal) slider.setFont(font) slider.setRange(0, RANGE) slider.setValue(RANGE / 2) key_layout.addWidget(slider) self.key_map[key] = { 'slidervalue': 0, 'display': display, 'slider': slider } slider.valueChanged.connect(self.onValueChanged) self.gridlayout.addLayout(key_layout, *(y, 0)) y += 1 #sliders.append(key_layout) # Generate positions in grid and place sliders there #self.positions = [(y,0) for y in range(len(sliders))] #for item, pos in zip(sliders, self.positions): # self.gridlayout.addLayout(item, *pos) self.vlayout.addLayout(self.gridlayout) self.ctrbutton = QPushButton('Center', self) self.ctrbutton.clicked.connect(self.center) self.vlayout.addWidget(self.ctrbutton) self.center(1)
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))
class ChecklistWindow(QWidget): 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) # Create the main layout of widget def create_widget(self): # Create tree structure self.create_tree() # Declare buttons and connect each of them to a function self.load_button = QPushButton('Load') self.ok_button = QPushButton('OK') self.cancel_button = QPushButton('Cancel') self.load_button.pressed.connect(self.load_clicked) self.ok_button.pressed.connect(self.ok_clicked) self.cancel_button.pressed.connect(self.cancel_clicked) # Add buttons into the layout self.buttons_layout.addWidget(self.load_button) self.buttons_layout.addWidget(self.cancel_button) self.buttons_layout.addWidget(self.ok_button) self.tree_widget_layout.addWidget(self.tree_widget) # Create the tree layout of widget inside of the main layout def create_tree(self): # Set up the main tree widget self.tree_widget = QTreeWidget() self.tree_widget.setColumnCount(2) self.tree_widget.setColumnWidth(0, 250) self.tree_widget.setHeaderLabels(['Parts', 'Status']) self.item = QTreeWidgetItem() # Create the BPO section self.BPO_header = QTreeWidgetItem(self.tree_widget) self.BPO_header.setFlags(self.BPO_header.flags() | Qt.ItemIsTristate | Qt.ItemIsUserCheckable) self.BPO_header.setText(0, 'BPO Checklist') self.BPO_header.setExpanded(True) self.create_item(self.BPO_header, self.BPO_checklist) # Adds the list of items into the section # Create the BTO section self.BTO_header = QTreeWidgetItem(self.tree_widget) self.BTO_header.setFlags(self.BTO_header.flags() | Qt.ItemIsTristate | Qt.ItemIsUserCheckable) self.BTO_header.setText(0, 'BTO Checklist') self.BTO_header.setExpanded(True) self.create_item(self.BTO_header, self.BTO_checklist) # Adds the list of items into the section # Populate the tree layout with items def create_item(self, parent, list): section_header = [] # List of the section headers for i in range (len(list)): if (list[i][1] == '' and list[i][0] != ''): section_header.append(list[i][0]) k = 0 # Iterate through the different sections for j in range (len(section_header)): # Child refers to the sections (mechanical, avionics, etc) child = QTreeWidgetItem(parent) child.setFlags(child.flags() | Qt.ItemIsTristate | Qt.ItemIsUserCheckable) child.setText(0, section_header[j]) while k < len(list): if list[k][0] in section_header: # When the while loop encounters the first title, continue the loop if (list[k][0] == section_header[0]): k += 1 continue # When the while loop encounters the next titles, break the loop so that the value of j increases k += 1 break # when the list contains empty cells, skip the cell and continue elif list[k][0] == '': k += 1 continue # Add the list items to the treewidgetitem # Grandchild refers to the items under each section (wing nuts, tail nuts, etc) grandchild = QTreeWidgetItem(child) grandchild.setText(0, list[k][0]) grandchild.setText(1, list[k][1]) grandchild.setCheckState(0, Qt.Unchecked) # Set all checkbox to its unchecked state k += 1 # Read the excel sheet and parse it as an array def excel_parser(self, file_name): with open(file_name, 'r') as file: checklist = [] reader = csv.reader(file) for row in reader: checklist.append(row) return checklist # Determines what happens when load button is clicked def load_clicked(self): # Use QFileDialog to open the system's file browser filenames = QFileDialog.getOpenFileNames( self, self.tr('Load from Files'), '.', self.tr('csv files {.csv} (*.csv)')) # Iterate through the file names selected for filename in filenames[0]: # If the file names has the word BPO or BTO in it, remove current widget, add the loaded one if (filename.find('BPO') != -1): self.BPO_checklist = self.excel_parser(filename) self.remove_widget() self.create_widget() elif (filename.find('BTO') != -1): self.BTO_checklist = self.excel_parser(filename) self.remove_widget() self.create_widget() else: rospy.logerr('rqt: Checklist name must contain BPO or BTO') self.close() # Close all the main_layout def remove_widget(self): self.main_layout.removeWidget(self.tree_widget) self.tree_widget.deleteLater() self.buttons_layout.removeWidget(self.ok_button) self.buttons_layout.removeWidget(self.cancel_button) self.buttons_layout.removeWidget(self.load_button) self.ok_button.deleteLater() self.cancel_button.deleteLater() self.load_button.deleteLater() # Declare what will happen when ok button is clicked def ok_clicked(self): if self.BPO_header.checkState(0) != 2 or self.BTO_header.checkState(0) != 2: # User clicks ok without checking all self.dialog_window("Some items in the checklist are still unchecked", "Do you still want to continue?", True) else: self.checklist_state = 1 self.close() # Declare what will happen when cancel button is clicked def cancel_clicked(self): if self.BPO_header.checkState(0) != 0 or self.BTO_header.checkState(0) != 0: # User clicks cancel with some boxes checked self.dialog_window('Some of your items are checked. Cancelling will uncheck all your items', 'Do you still want to continue?', False) else: self.BTO_header.setCheckState(0, Qt.Unchecked) self.BPO_header.setCheckState(0, Qt.Unchecked) self.close() # Create a pop up window when user pre-emptively cancelled or clicked ok without completing the checklist def dialog_window(self, message, detail, check): self.message = QMessageBox() self.has_message_opened = 1 self.message.setIcon(QMessageBox.Warning) self.message.setText(message) self.message.setInformativeText(detail) self.message.setWindowTitle("Items are unchecked") self.message.setStandardButtons(QMessageBox.Yes | QMessageBox.No) self.message.show() if check == True: # Check == True means it is from ok_clicked self.message.buttonClicked.connect(self.message_action) else: self.message.buttonClicked.connect(self.message_action_uncheck) # Determines what happens after dialog_window pops up from ok_button def message_action(self, i): if i.text() == '&Yes': self.checklist_state = 1 self.close() else: self.message.close() # Determines what happens after dialog_window pops up from cancel_button def message_action_uncheck(self, i): self.response = i.text() if self.response == '&Yes': self.checklist_state = 1 self.BTO_header.setCheckState(0, Qt.Unchecked) self.BPO_header.setCheckState(0, Qt.Unchecked) self.close() else: self.message.close() # Shutdown function def shutdown(self): self.close() if self.has_message_opened == 1: self.message.close()
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 refresh_topics(self): """ refresh tree view items """ # # update the list of subsystems # topic_list = rospy.get_published_topics() if topic_list is None: rospy.logerr( 'Not even a single published topic found. Check network configuration' ) return # start new topic dict new_subsystems = {} for topic_name, topic_type in topic_list: name_split = topic_name.split('/') # print name_split if (len(name_split) == 3) and (name_split[0] == '') and ( name_split[2] == 'diag') and (topic_type == "diagnostic_msgs/DiagnosticArray"): subsystem_name = name_split[1] # if topic is new if subsystem_name not in self._subsystems: # create new TopicInfo topic_info = TopicInfo(topic_name, topic_type) new_subsystems[subsystem_name] = topic_info topic_info.start_monitoring() else: # if topic has been seen before, copy it to new dict and # remove it from the old one new_subsystems[subsystem_name] = self._subsystems[ subsystem_name] del self._subsystems[subsystem_name] # remove unused subsystems while True: repeat = False for s in self._subsystems: if not s in new_subsystems: del self._subsystems[s] repeat = True break if not repeat: break # switch to new topic dict self._subsystems = new_subsystems # print self._subsystems.keys() # # update each subsystem # new_widgets = {} for subsystem_name in self._subsystems: msg = self._subsystems[subsystem_name].last_message if (msg != None) and (len(msg.status) == 2) and \ msg.status[0].name == 'components' and msg.status[1].name == 'diagnostics': name_split = subsystem_name.split('/') if not subsystem_name in self.all_subsystems: self.all_subsystems[subsystem_name] = SubsystemWidget( self._plugin, subsystem_name) if not subsystem_name in self._widgets: new_widgets[subsystem_name] = self.all_subsystems[ subsystem_name] # self.verticalLayout.addWidget(new_widgets[subsystem_name]) else: new_widgets[subsystem_name] = self._widgets[subsystem_name] # del self._widgets[subsystem_name] # for value in msg.status[1].values: # if value.key == 'master_component': # new_widgets[subsystem_name].setStateName(value.value, '') # break # remove unused subsystems # while True: # repeat = False # for s in self._widgets: # if not s in new_widgets: # del self._widgets[s] # repeat = True # break # if not repeat: # break self._widgets = new_widgets # print self._widgets.keys() structure_changed = self.checkStructureChange() if structure_changed: self.structure_changed = True if self.structure_changed: allInitialized = True for subsystem_name in self._widgets: if not self._widgets[subsystem_name].isInitialized(): allInitialized = False break if allInitialized: # remove all widgets from layouts # and remove all layouts for i in reversed(range(len(self.levels_layouts))): layout = self.levels_layouts[i] for i in reversed(range(layout.count())): # The new widget is deleted when its parent is deleted. layout.itemAt(i).widget().setParent(None) self.verticalLayout.removeItem(layout) del layout self.levels_layouts = [] levels = self.generateStructure() for l in levels: hbox = QHBoxLayout() for w in l: hbox.addWidget(self._widgets[w]) self._widgets[w].show() self.levels_layouts.append(hbox) self.verticalLayout.addLayout(hbox) # for # TODO self.structure_changed = False while True: repeat = False for s in self.all_subsystems: if not s in self._widgets: del self.all_subsystems[s] repeat = True break if not repeat: break for subsystem_name in self._widgets: self._widgets[subsystem_name].update_subsystem( self._subsystems[subsystem_name].last_message)
class HistorySelection(QWidget): def __init__(self): super(HistorySelection, self).__init__() self.setWindowTitle("Extracting Window Features") self.resize(550, 440) per_title, pre = TS.get_time_series_pre_feature_options() glob_title, glob = TS.get_global_time_series_features_options() self.history_items = dict() self.history_items[per_title] = pre self.history_items[glob_title] = glob self.saved_dir = [] self.group_selected_items = dict() self.group_areas = dict() self.group_main_widget = dict() self.group_selection_vlayout = dict() self.group_item_all = dict() self.main_vlayout = QVBoxLayout(self) self.group_label = dict() self.items_list = [] self.selected_topics = [] self.files = [] # layout = QFormLayout() self.select_path = QLineEdit() self.save_path = QLineEdit() self.select_path.setEnabled(False) self.save_path.setEnabled(False) self.ok_button = QPushButton("Select CSV...", self) self.ok_button.clicked.connect(self.onButtonClicked) self.two_buttons1 = QHBoxLayout(self) self.two_buttons1.addWidget(self.ok_button) self.two_buttons1.addWidget(self.select_path) self.main_vlayout.addLayout(self.two_buttons1) # self.main_vlayout.addRow(self.ok_button, self.client_answers) # self.main_vlayout.addWidget(self.ok_button) # self.main_vlayout.addWidget(self.ok_button) for group_name in self.history_items: self.group_selected_items[group_name] = [] self.group_areas[group_name] = QScrollArea(self) self.group_main_widget[group_name] = QWidget( self.group_areas[group_name]) self.group_label[group_name] = QLabel(group_name, self) self.group_label[group_name].setAlignment(Qt.AlignCenter) self.main_vlayout.addWidget(self.group_label[group_name]) self.main_vlayout.addWidget(self.group_areas[group_name]) self.group_selection_vlayout[group_name] = QVBoxLayout(self) self.group_item_all[group_name] = MyQCheckBox( "All", self, self.group_selection_vlayout[group_name], None) self.MakeCheckBoxList(self.group_selection_vlayout[group_name], self.group_selected_items[group_name], self.history_items[group_name], self.group_item_all[group_name]) self.group_main_widget[group_name].setLayout( self.group_selection_vlayout[group_name]) self.group_areas[group_name].setWidget( self.group_main_widget[group_name]) self.clear_button = QPushButton("Clear Selection", self) self.clear_button.clicked.connect(self.onClearClicked) self.choose_button = QPushButton("Get Last Export Choose", self) self.choose_button.clicked.connect(self.onButtonChooseCliked) # self.main_vlayout.addWidget(self.choose_button) 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("3") self.label5 = QLabel("Duration Time:", self) self.label5.setAlignment(Qt.AlignCenter) self.label6 = QLabel("Step:", self) self.label6.setAlignment(Qt.AlignCenter) self.step = QLineEdit(self) self.step.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.step_2 = QHBoxLayout(self) self.step_2.addWidget(self.label6) self.step_2.addWidget(self.step) self.main_vlayout.addLayout(self.step_2) # self.main_vlayout.addRow(self.label4, self.window) 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.addRow(self.clear_button, self.choose_button) # self.label5 = QLabel("Load CSV", self) # self.label5.setAlignment(Qt.AlignCenter) # # self.main_vlayout.addWidget(self.label5) # self.label6 = QLabel("output", self) # self.label6.setAlignment(Qt.AlignCenter) # # self.main_vlayout.addWidget(self.label6) self.save_button = QPushButton("Save CSV...", self) self.save_button.clicked.connect(self.onSaveClicked) self.submit_button = QPushButton("Sumbit", self) self.submit_button.clicked.connect(self.onSumbitClicked) # self.main_vlayout.addWidget(self.save_button) self.two_buttons2 = QHBoxLayout(self) self.two_buttons2.addWidget(self.save_button) self.two_buttons2.addWidget(self.save_path) self.main_vlayout.addLayout(self.two_buttons2) # self.main_vlayout.addRow(self.save_button, self.client_answers1) self.main_vlayout.addWidget(self.submit_button) self.show() def get_min_rows_csv(self, csv_files): import pandas as pd dfs = map(lambda x: pd.read_csv(x, header=0), csv_files) number = dfs[0].shape[0] number = reduce(lambda acc, curr: min(acc, curr.shape[0]), dfs, number) self.label5.setText("Duration Time: %s" % number) def onClearClicked(self): self.clearTopicCheckState() def clearTopicCheckState(self): for item in self.items_list: item.setCheckState(False) for item in self.group_item_all.values(): item.setCheckState(False) def MakeCheckBoxList(self, selection_vlayout, selected, topics_Keys, item_all): item_all.stateChanged.connect( lambda x: self.updateList(x, item_all, None)) selection_vlayout.addWidget(item_all) topic_data_list = topics_Keys topic_data_list.sort() for topic in topic_data_list: self.addCheckBox(topic, selection_vlayout, selected) 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.append(item) selection_vlayout.addWidget(item) def updateList(self, state, item_clicked, topic=None, force_update_state=False): if type(item_clicked) is str: item_clicked = self.get_item_by_name(item_clicked) if item_clicked is None: return 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: 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: if item.checkState() == Qt.Checked and \ item.selection_vlayout == item_clicked.selection_vlayout: item.setCheckState(Qt.Unchecked) 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) 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/select_history.log" fd = QFileDialog(self) wc = "Csv files {.csv} (*.csv)" self.files = [] current_directory = self.get_current_opened_directory(filepath) tmp_pathes, filter = fd.getOpenFileNamesAndFilter( filter=wc, initialFilter=('*.csv'), directory=current_directory) for path in tmp_pathes: self.files.append(path.encode("utf-8")) # print self.files if len(self.files) != 0: self.get_min_rows_csv(tmp_pathes) self.select_path.setText(self.files[0]) with open(filepath, "w") as f: f.write(self.files[0]) # handler = logging.FileHandler(filepath, mode='w') # logger_topic.addHandler(handler) # logger_topic.info(self.files[0]) else: self.select_path.setText("") def check_int(self, number, condition, title, message_body): try: # print type(number) val = int(number) if val <= condition: QMessageBox.about(self, title, "The number should > %s" % condition) return False except ValueError: QMessageBox.about(self, title, message_body) return False return True def check_choose(self): flag = True temp = [] for item in self.group_selected_items.values(): if item: for i in item: temp.append(i) if len(temp) == 0: QMessageBox.about(self, "Features", "One feature at least should be chosen") flag = False return flag def check_files_amount(self): flag = True if len(self.files) <= 0: QMessageBox.about(self, "Load CSV", "One file at least should be chosen") flag = False return flag def check_validation(self): flag = self.check_files_amount() flag = flag & self.check_choose() flag = flag & self.check_int(self.window.text(), 2, "Error in Window Time", "That's not a number!") flag = flag & self.check_int(self.step.text(), 0, "Error in Step", "That's not a integer!") # TODO selected topic not empty if self.saved_dir == []: QMessageBox.about(self, "Save CSV", "Select path for saving") flag = False return flag def onSumbitClicked(self): if not self.check_validation(): return self.selected_topics = [] for item in self.group_selected_items.values(): if item: for i in item: self.selected_topics.append(i) # Defined Logging # handler = logging.FileHandler('/var/tmp/logger_history.log', mode='a') # logger_topic.addHandler(handler) topics = self.selected_topics with open(get_path() + 'logger_history.log', "w") as f: for topic in topics: f.write(topic + "\n") self.createTimeSeriesFeatures(self.files, self.saved_dir, int(self.window.text()), self.group_selected_items, int(self.step.text())) def onSaveClicked(self): import inspect, os filepath = os.path.dirname( os.path.abspath(inspect.getfile( inspect.currentframe()))) + "/log/save_history.log" # print self.files # print self.window.text() # print self.group_selected_items.values() # print self.group_selected_items current_directory = self.get_current_opened_directory(filepath) # print current_directory # self._to_save_filename = QFileDialog.getSaveFileName(self, self.tr('csv File'), current_directory, # self.tr('csv (*.csv)')) self.saved_dir = str( QFileDialog.getExistingDirectory(self, "Select Directory", current_directory)) # if self._to_save_filename[0] != "": # with open(filepath, "w") as f: # f.write(self._to_save_filename[0]) if self.saved_dir != "": with open(filepath, "w") as f: f.write(self.saved_dir) # handler = logging.FileHandler(filepath, mode='w') # logger_topic.addHandler(handler) # print self._to_save_filename[0] # logger_topic.info() # self.save_path.setText(get_corrent_file_name(self._to_save_filename[0], ".csv")) self.save_path.setText(self.saved_dir) def createTimeSeriesFeatures(self, files, saved_dir, window, group_selected_items, step): import TimeSeriesFeatures as TS # saved_dir = saved_dir[0].encode('utf-8') for input_path in files: output_path = generate_csv_from_bag(saved_dir, input_path) print "++++++++++++++++++++++++++ ", output_path print "in = %s out = %s " % (input_path, output_path) print "step = %s" % step ts = TS.TimeSeries(input_path, output_path, window, group_selected_items, step) ts.generate_time_series_features() QMessageBox.about(self, "csv save", "csv was saved successfuly") def onButtonChooseCliked(self): for checkbox in self.items_list: checkbox.setCheckState(Qt.Unchecked) with open(get_path() + "logger_history.log", 'r') as f: topics = f.read().splitlines() for checkbox in self.items_list: if checkbox.text() in topics: checkbox.setCheckState(Qt.Checked)
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
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")
class DataPlot(QWidget): """A widget for displaying a plot of data The DataPlot widget displays a plot on one a MatPlot backend. 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. """ # pre-defined colors: RED = (255, 0, 0) GREEN = (0, 255, 0) BLUE = (0, 0, 255) _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, float, 'QColor', bool) 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() # interface out to the managing GUI component: get title, save, restore, # etc def getTitle(self): """get the title of the current plotting backend""" return "MatPlot" def save_settings(self, plugin_settings, instance_settings): pass def restore_settings(self, plugin_settings, instance_settings): pass # 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): """ Trigger a redraw. @TODO investigate why this is a signal; is anyone using it externally? """ 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""" self._merged_autoscale() for topic_value in self._curves.values(): for goal_value in topic_value["goals"].values(): # here we check if the given curve should be dropped (i.e. if we have no data within our visible range for it) if len(goal_value['x']) != 0 and numpy.max(goal_value['x']) < ( self._x_limits[0] - self._dropwidth): # delete this curve self._data_plot_widget.remove_curve(goal_value["name"]) else: # update self._data_plot_widget.set_values(goal_value["name"], goal_value['x'], goal_value['y']) self._data_plot_widget.redraw() def add_topic(self, topic_name, data): """Add a new topic to this plot. Note that the plot is not redraw automatically; call `redraw()` to make any changes visible to the user. """ topic_color = QColor(self._colors[self._color_index % len(self._colors)]) self._color_index += 1 # initialize topic object self._curves[topic_name] = { "goals": {}, "name": topic_name, "color": topic_color } self.update_values(topic_name, data) def remove_topic(self, topic_name): """Remove the specified curve from this plot""" if topic_name in self._curves: # remove all associated goal curves for goal_id in self._curves[topic_name]["goals"].keys(): self._data_plot_widget.remove_curve(goal_id) # delete this topic object self._curves.pop(topic_name) def update_values(self, topic_name, values, sort_data=True): """Append new data to an existing topic 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. """ # ignore if we aren't tracking this topic if topic_name in self._curves: # iterate through all the new goals and data for goal_id, data in values.items(): # parse data new_x, new_y, time_sent = [list(v) for v in zip(*data)] time_sent = max(time_sent) # for each goal in the new values we need to check if it's new and therefore # warrants adding a new curve if goal_id not in self._curves[topic_name]["goals"]: self._curves[topic_name]["goals"][goal_id] = { "x": numpy.array([]), "y": numpy.array([]), "name": goal_id, "sent": time_sent, "color": self._curves[topic_name]["color"] } # update QT with knowledge of this new curve self._add_curve.emit(topic_name, goal_id, time_sent, self._curves[topic_name]["color"], True) # check if this new curve has a newer X data point than we've previously seen # we use this to update our upper bound curve_x_max = max(new_x) if curve_x_max > self._x_limits[1]: self._x_limits[1] = curve_x_max # add new data to the given goal id's curve self._curves[topic_name]["goals"][goal_id]['x'] = numpy.append( self._curves[topic_name]["goals"][goal_id]['x'], new_x) self._curves[topic_name]["goals"][goal_id]['y'] = numpy.append( self._curves[topic_name]["goals"][goal_id]['y'], new_y) if sort_data: # sort resulting data, so we can slice it later sort_order = self._curves[topic_name]["goals"][goal_id][ 'x'].argsort() self._curves[topic_name]["goals"][goal_id][ 'x'] = self._curves[topic_name]["goals"][goal_id]['x'][ sort_order] self._curves[topic_name]["goals"][goal_id][ 'y'] = self._curves[topic_name]["goals"][goal_id]['y'][ sort_order] def clear_values(self): """Clear the values for all curves Note that the plot is not redrawn automatically; call `redraw()` to make any changes visible to the user. """ # clear internal curve representation for topic_values in self._curves.values(): for goal_values in topic_values["goals"].values(): goal_values["x"] = numpy.array([]) goal_values["y"] = numpy.array([]) self._x_limits = [0, 1.0] def _merged_autoscale(self): """ Update the X axes to include the latest data. If `autoscroll`, we keep the last `self.scrollwidth` seconds of data in the field of view. If not, we keep all data in the field of view. """ if self._autoscroll: # set xlimit lower bound to fit our window self._x_limits[0] = max(0, self._x_limits[1] - self._scrollwidth) # update underlying axes object's limits self._data_plot_widget.set_xlim(self._x_limits)
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)
def setup(self, config_layout, results_layout, config_locked): ''' set up the layout for selecting options from the different benchmark variations ''' config_group_box_layout = QHBoxLayout() self.config_group_box = QGroupBox('Configuration') self.config_group_box.setCheckable(True) if (config_locked): self.config_group_box.setChecked(False) variations = self.config['variations'].keys() for var in variations: single_choice = True if var in self.config['multiple choice variation']: single_choice = False if isinstance(self.config['variations'][var], list): self.variation_widgets[var], scroll = self.add_config( var, self.config['variations'][var], single_choice=single_choice) config_group_box_layout.addWidget(scroll) else: path = os.path.join(self.config_path, self.config['variations'][var]) if os.path.exists(path): with open(path) as f: options = f.readlines() options = [x.strip() for x in options] self.variation_widgets[var], scroll = self.add_config( var, options, single_choice=single_choice) config_group_box_layout.addWidget(scroll) self.config['variations'][var] = options else: QMessageBox.critical(None, "Error", "File %s does not exist" % path) self.config_group_box.setLayout(config_group_box_layout) config_layout.addWidget(self.config_group_box) results_group_box = QGroupBox('Results') results_group_box_layout = QHBoxLayout() results = self.config['results'].keys() for res in results: single_choice = True if var in self.config['multiple choice result']: single_choice = False if isinstance(self.config['results'][res], list): self.result_widgets[res], scroll = self.add_config( res, self.config['results'][res], single_choice=single_choice) results_group_box_layout.addWidget(scroll) else: if self.config['results'][res] == 'text_field': widget = QWidget() text_field = QLineEdit() text_field.setReadOnly(True) txtlayout = QHBoxLayout() txtlayout.addWidget(QLabel(res)) txtlayout.addWidget(text_field) widget.setLayout(txtlayout) self.result_widgets[res] = text_field results_group_box_layout.addWidget(widget) results_group_box.setLayout(results_group_box_layout) results_layout.addWidget(results_group_box) self.notes_widget = QPlainTextEdit() self.notes_widget.setMaximumHeight(100) self.notes_widget.setPlaceholderText('Enter notes about the result...') results_layout.addWidget(self.notes_widget)