Ejemplo n.º 1
0
    def add_config(self, title, choices, single_choice=True):
        '''
        create a UI element for selecting options for one variation
        and put it in a scrollArea
        '''
        scroll = QScrollArea()
        group_box = QGroupBox(title)
        group_box.setFlat(True)

        layout = QVBoxLayout()
        if len(choices) > 5 and single_choice:
            combo_box = QComboBox(group_box)
            for obj in choices:
                combo_box.addItem(obj)
            layout.addWidget(combo_box)
        else:
            for obj in choices:
                if single_choice:
                    layout.addWidget(QRadioButton(obj, group_box))
                else:
                    layout.addWidget(QCheckBox(obj, group_box))
        layout.addStretch(1)

        group_box.setLayout(layout)
        scroll.setWidget(group_box)
        scroll.setWidgetResizable(True)
        return group_box, scroll
Ejemplo n.º 2
0
    def add_buttons(self, colors):
        vbx = QVBoxLayout()
        i = 1
        for color in colors:

            but = QPushButton('Color {} ({})'.format(color, i))
            but.clicked.connect(self.clicked(color))

            but_short = QShortcut(QKeySequence("Ctrl+" + str(i)), self)
            but_short.activated.connect(self.clicked(color))
            i += 1
            vbx.addWidget(but)

        vbx.addStretch(1)
        self.color_buttons.setLayout(vbx)
Ejemplo n.º 3
0
    def __init__(self):
        super(MyWidget, self).__init__()
        self.brd = CvBridge()
        self.view = RGB
        ui_file = os.path.join(
            rospkg.RosPack().get_path('balloon_color_picker'), 'resource',
            'ColorPlugin.ui')
        rospy.loginfo('uifile {}'.format(ui_file))
        # self.log_info('uifile {}'.format(ui_file))
        loadUi(ui_file, self)
        # Give QObjects reasonable names
        self.setObjectName('ColorPluginUi')
        self.uav_name = os.environ['UAV_NAME']

        self.orig_h = 920
        self.orig_w = 1080
        self.hist_hsv_orig_h = 180
        self.hist_hsv_orig_w = 256
        self.hist_lab_orig_h = 256
        self.hist_lab_orig_w = 256
        self.hist_status = HSV
        self.select_status = HIST_SELECTION
        self.crop_stat = IMG
        self.hist_mask = np.zeros([self.hist_hsv_orig_h, self.hist_hsv_orig_w])
        self.hist_mask_lab = np.zeros(
            [self.hist_lab_orig_h, self.hist_lab_orig_w])
        self.cur_hist_hs = None
        self.cur_hist_ab = None
        self.selected_count = 0
        # ROS services

        # #{ ros services

        self.sigma_caller = rospy.ServiceProxy('change_sigma', ChangeSigma)
        self.sigma_lab_caller = rospy.ServiceProxy('change_sigma_lab',
                                                   ChangeSigmaLab)
        self.caller = rospy.ServiceProxy('capture', Capture)
        self.capture_cropped_srv = rospy.ServiceProxy('capture_cropped',
                                                      CaptureCropped)
        self.get_count = rospy.ServiceProxy('get_count', GetCount)
        self.clear_count = rospy.ServiceProxy('clear_count', ClearCount)
        self.get_config = rospy.ServiceProxy('get_config', GetConfig)
        self.get_params = rospy.ServiceProxy('get_params', Params)
        self.freeze_service = rospy.ServiceProxy('freeze', Freeze)
        self.update_service = rospy.ServiceProxy('change_obd', UpdateObd)
        self.change_callback = rospy.ServiceProxy('change_callback',
                                                  ChangeCallback)
        self.capture_hist = rospy.ServiceProxy('capture_hist', CaptureHist)

        # #} end of ros services        rospy.wait_for_service('capture')

        rospy.loginfo(
            'waiting for service "get_params" from computation module \n if this is more than 5 secs check for topic remapping'
        )
        # self.log_info('waiting for service')
        rospy.loginfo('uav_name {}'.format(os.environ['UAV_NAME']))
        # self.log_info('uav_name {}'.format(os.environ['UAV_NAME']))
        rospy.wait_for_service('get_params')

        self.config_path, self.save_path, self.circled_param, self.circle_filter_param, self.circle_luv_param, self.object_detect_param, self.save_to_drone = self.set_params(
        )
        # SUBS
        # #{ ros subs

        self.balloon_sub = rospy.Subscriber(self.circled_param,
                                            RosImg,
                                            self.img_callback,
                                            queue_size=1)
        self.filter_sub = rospy.Subscriber(self.circle_filter_param,
                                           RosImg,
                                           self.filter_callback,
                                           queue_size=1)
        self.filter_luv = rospy.Subscriber(self.circle_luv_param,
                                           RosImg,
                                           self.luv_callback,
                                           queue_size=1)
        self.obj_det_sb = rospy.Subscriber(self.object_detect_param,
                                           RosImg,
                                           self.obj_det_callback,
                                           queue_size=1)
        # self.hsv = message_filters.Subscriber(self.circle_filter_param, RosImg)
        # self.luv = message_filters.Subscriber(self.circle_luv_param, RosImg)

        # self.ts = message_filters.ApproximateTimeSynchronizer([self.luv,  self.hsv], 1, 0.5)
        # self.ts.registerCallback(self.both_callback)

        # #} end of ros subs

        self.colors = self.load_config(self.config_path)
        self.add_buttons(self.colors)

        # # DEFAULT IMAGE
        # img = cv2.imread('/home/mrs/balloon_workspace/src/ros_packages/balloon_color_picker/data/blue.png')

        # cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

        # h,w,c = img.shape
        # q_img = QImage(img.data, w,h,3*w, QImage.Format_RGB888)

        # q = QPixmap.fromImage(q_img)

        #DIRECTORY
        #default
        self.directory.setText(self.save_path + "Red.yaml")
        self.color_name = "red"
        self.save_button.clicked.connect(self.save_config)

        #PLOT

        self.figure = Figure()
        self.figure_luv = Figure()
        self.canvas = FigureCanvas(self.figure)
        self.canvas_luv = FigureCanvas(self.figure_luv)
        self.canvas.setParent(self.inner)
        # self.toolbar = NavigationToolbar(self.canvas,self)
        # self.toolbar_luv = NavigationToolbar(self.canvas_luv,self)
        # self.toolbar.setParent(self.inner)
        # self.toolbar_luv.setParent(self.inner_luv)
        self.canvas_luv.setParent(self.inner_luv)
        self.inner_luv.hide()
        self.inner_luv_hist.hide()

        #SLIDER CONFIG

        # #{ slider config

        self.sigma_slider.setRange(0, 100)
        self.sigma_slider.setSingleStep(1)
        self.sigma_slider.setValue(6)

        self.sigma_slider.valueChanged.connect(self.slider_event)

        self.sigma_slider_s.setRange(0, 100)
        self.sigma_slider_s.setSingleStep(1)
        self.sigma_slider_s.setValue(6)

        self.sigma_slider_s.valueChanged.connect(self.slider_event)

        self.sigma_slider_v.setRange(0, 100)
        self.sigma_slider_v.setSingleStep(1)
        self.sigma_slider_v.setValue(80)

        self.sigma_slider_v.valueChanged.connect(self.slider_event)

        self.sigma_slider_lab.setRange(0, 100)
        self.sigma_slider_lab.setSingleStep(1)
        self.sigma_slider_lab.setValue(80)

        self.sigma_slider_lab.valueChanged.connect(self.slider_event_lab)

        self.sigma_slider_a.setRange(0, 100)
        self.sigma_slider_a.setSingleStep(1)
        self.sigma_slider_a.setValue(6)

        self.sigma_slider_a.valueChanged.connect(self.slider_event_lab)

        self.sigma_slider_b.setRange(0, 100)
        self.sigma_slider_b.setSingleStep(1)
        self.sigma_slider_b.setValue(6)

        self.sigma_slider_b.valueChanged.connect(self.slider_event_lab)

        # #} end of slider config

        # #{ font configs

        #SIGMA TEXT
        font = self.font()
        font.setPointSize(16)
        self.sigma_value.setFont(font)
        self.sigma_value_s.setFont(font)
        self.sigma_value_v.setFont(font)
        self.sigma_value_lab.setFont(font)
        self.sigma_value_a.setFont(font)
        self.sigma_value_b.setFont(font)
        #IMAGE COUNT TEXT
        self.image_count.setFont(font)
        #BOX FOR BUTTONS font
        # self.color_buttons.setFont(font)
        font.setPointSize(14)
        self.sigma_value.setFont(font)
        self.log_text.setFont(font)

        #LAB HSV TEXT
        font.setPointSize(23)
        self.label_lab.setFont(font)
        self.label_lab.hide()
        self.label_hsv.setFont(font)
        self.label_hsv.hide()

        # #} end of font configs

        # BUTTONS
        self.change.clicked.connect(self.switch_view_hsv)
        self.change_both.clicked.connect(self.switch_view_both)
        self.change_luv.clicked.connect(self.switch_view_luv)
        self.change_object.clicked.connect(self.switch_view_object_detect)
        self.capture_button.clicked.connect(self.capture)
        self.clear_button.clicked.connect(self.clear)
        self.freeze_button.clicked.connect(self.freeze)
        self.update_button.clicked.connect(self.update_obd)
        # self.wdg_img.setPixmap(q)
        # self.box_layout.addWidget(self.toolbar)
        # self.inner.box_layout.addWidget(self.canvas)
        #shortcuts

        self.short_capture = QShortcut(QKeySequence("C"), self)
        self.short_capture.activated.connect(self.capture)
        self.short_hsv = QShortcut(QKeySequence("1"), self)
        self.short_hsv.activated.connect(self.switch_view_hsv)
        self.short_lab = QShortcut(QKeySequence("2"), self)
        self.short_lab.activated.connect(self.switch_view_luv)
        self.short_object_detect = QShortcut(QKeySequence("3"), self)
        self.short_object_detect.activated.connect(
            self.switch_view_object_detect)
        self.short_object_detect_update = QShortcut(QKeySequence("U"), self)
        self.short_object_detect_update.activated.connect(self.update_obd)
        self.short_both = QShortcut(QKeySequence("4"), self)
        self.short_both.activated.connect(self.switch_view_both)
        self.short_save = QShortcut(QKeySequence("S"), self)
        self.short_save.activated.connect(self.save_config)
        self.short_clear = QShortcut(QKeySequence("N"), self)
        self.short_clear.activated.connect(self.clear)
        self.short_freeze = QShortcut(QKeySequence("F"), self)
        self.short_freeze.activated.connect(self.freeze)

        vbx = QVBoxLayout()
        but_hsv = QRadioButton()
        but_hsv.setText('HSV')
        but_hsv.setChecked(True)
        self.color_space = 'HSV'
        but_hsv.clicked.connect(self.set_colorspace_hsv)
        vbx.addWidget(but_hsv)
        but_lab = QRadioButton()
        but_lab.setText('LAB')
        but_lab.clicked.connect(self.set_colorspace_lab)
        vbx.addWidget(but_lab)
        vbx.addStretch(1)

        self.radio_buttons.setLayout(vbx)
        vbx_method = QVBoxLayout()
        but_lut = QRadioButton()
        but_lut.setText('LUT')
        but_lut.setChecked(False)
        but_lut.clicked.connect(self.set_method_lut)
        vbx_method.addWidget(but_lut)

        but_thr = QRadioButton()
        but_thr.setText('Threshold')
        but_thr.setChecked(True)
        but_thr.clicked.connect(self.set_method_thr)
        vbx_method.addWidget(but_thr)
        vbx.addStretch(1)

        self.radio_buttons_method.setLayout(vbx_method)

        self.load_method = 'THR'

        # self.mousePressEvent.connect(self.mousePressEvent)
        self.plotted = False

        self._rubber = None

        self.frozen = False
Ejemplo n.º 4
0
class EventTransmissionGUI(Plugin):
    """
    GUI to send events from User to logic state machine
    """
    def __init__(self, context):
        """
        Create Qt GUI using the event file
        """
        super(EventTransmissionGUI, self).__init__(context)
        self.setObjectName('ManualEventTriggerGUI')
        parser = ArgumentParser()

        # Add argument(s) to the parser.
        args = self._parse_args(context.argv())

        ## Create Event trigger
        self.event_trigger = RosEventTrigger(args.event_file)

        ## Parent container to store buttons, textboxes
        self._container = QTabWidget()
        ## Tab to display robot system, state machine status
        ## and command buttons without any arguments
        self._status_tab = QWidget()
        ## Tab to display positionyaw and velocityyaw commands
        ## and sliders for them
        self._additional_commands_tab = QWidget()
        # Set title of the parent container window
        self._status_tab.setWindowTitle(self.event_trigger.event_manager_name)
        ## Layout for status tab
        self._layout = QVBoxLayout()
        self._status_tab.setLayout(self._layout)

        # Create Textboxes and add to Layout
        self._layout.addWidget(QLabel('State Machine State'))
        ## Textbox to show sytem status
        self.system_status_textbox = QTextEdit()
        self.system_status_textbox.setReadOnly(True)
        self._layout.addWidget(self.system_status_textbox)

        # Define and connect buttons
        self._layout.addWidget(QLabel('Event Triggers'))
        ## Continer to store event triggering buttons
        self.button_container = QWidget()
        ## List of push buttons to trigger events
        self.push_buttons = list()
        ## Layout for the push buttons
        self.button_layout = QGridLayout()
        self.button_container.setLayout(self.button_layout)
        button_index = 0
        for event_name in self.event_trigger.event_names_list:
            self.push_buttons.append(QPushButton(event_name))
            partial_fun = partial(self.event_trigger.triggerEvent,
                                  event_name=event_name)
            self.push_buttons[-1].clicked.connect(partial_fun)
            row, col = self.get_row_col(button_index, args.grid_cols)
            self.button_layout.addWidget(self.push_buttons[-1], row, col)
            button_index += 1
        self._layout.addWidget(self.button_container)

        ## Layout for additional commands tab
        self._additional_commands_layout = QVBoxLayout()
        self._additional_commands_tab.setLayout(
            self._additional_commands_layout)
        # Create height slider
        self._additional_commands_layout.addWidget(
            QLabel('Pose Command Height (m)'))
        ## Height slider to adjust z coordinate for pose command
        ## \todo Matt: Load slider settings from param file
        self.height_slider = self.createSlider(1.0, 20.0, 2.0, 1.0)
        self._additional_commands_layout.addWidget(self.height_slider)
        ## Add button for triggering pose command
        ## Container for pose event related objects: slide etc
        ## \todo Matt: Reset slider value based on current quad height
        self.pose_command_container = QWidget()
        ## Pose command layout
        self.pose_command_layout = QGridLayout()
        self.pose_command_container.setLayout(self.pose_command_layout)
        ## x pose label to display position command from rviz to user
        self.pose_x = QLabel('x: -')
        ## y pose label to display position command from rviz to user
        self.pose_y = QLabel('y: -')
        ## z pose label to display position command from rviz to user
        self.pose_z = QLabel("z: {0:.2f}".format(self.height_slider.value()))
        self.height_slider.valueChanged.connect(
            partial(self.updateLabel,
                    header="z",
                    label=self.pose_z,
                    slider=self.height_slider))
        self.pose_command_layout.addWidget(self.pose_x, 0, 0)
        self.pose_command_layout.addWidget(self.pose_y, 0, 1)
        self.pose_command_layout.addWidget(self.pose_z, 0, 2)
        ## Button to send the pose command to state machine as poseyaw event
        self.send_pose_command_button = QPushButton("Send Pose Command")
        self.send_pose_command_button.clicked.connect(
            self.poseCommandButtonCallback)
        self.pose_command_layout.addWidget(self.send_pose_command_button, 0, 3)
        self._additional_commands_layout.addWidget(self.pose_command_container)
        ## Pose command container to store pose from Rviz and send to state
        ## machine
        self.pose_command = None
        ## Sliders for setting vx,vy,vz, yaw
        self.velocity_sliders = []
        ## Labels vx,vy,vz,yaw as an array
        self.velocity_command_labels = []
        self._additional_commands_layout.addWidget(
            QLabel('Velocity Command (m/s), Yaw (deg)'))
        for i in range(3):
            self.velocity_sliders.append(
                self.createSlider(-20.0, 20.0, 0.0, 1.0))
            self._additional_commands_layout.addWidget(
                self.velocity_sliders[i])
        # Slider for yaw
        self.velocity_sliders.append(
            self.createSlider(-180.0, 180.0, 0.0, 10.0))
        self._additional_commands_layout.addWidget(self.velocity_sliders[-1])
        ## Add button for triggering velocity yaw
        self.velocity_command_container = QWidget()
        ## Velocity command layout
        self.velocity_command_layout = QGridLayout()
        self.velocity_command_container.setLayout(self.velocity_command_layout)
        for i, axis_label in enumerate(['x', 'y', 'z']):
            # label to display velocity command from rviz to user
            self.velocity_command_labels.append(
                QLabel("v{0}: {1:.2f}".format(
                    axis_label, self.velocity_sliders[i].value() / 10.0)))
            self.velocity_sliders[i].valueChanged.connect(
                partial(self.updateLabel,
                        header="v" + str(i),
                        label=self.velocity_command_labels[i],
                        slider=self.velocity_sliders[i],
                        scale=10.0))
            self.velocity_command_layout.addWidget(
                self.velocity_command_labels[i], 0, i)
        # Label for yaw
        self.velocity_command_labels.append(
            QLabel("Yaw: {0:.2f}".format(self.velocity_sliders[-1].value())))
        self.velocity_sliders[-1].valueChanged.connect(
            partial(self.updateLabel,
                    header="Yaw",
                    label=self.velocity_command_labels[-1],
                    slider=self.velocity_sliders[-1]))
        self.velocity_command_layout.addWidget(
            self.velocity_command_labels[-1], 0, 3)
        ## Button to send the pose command to state machine as poseyaw event
        self.send_velocity_command_button = QPushButton(
            "Send Velocity Command")
        self.send_velocity_command_button.clicked.connect(
            self.velocityCommandButtonCallback)
        self.velocity_command_layout.addWidget(
            self.send_velocity_command_button, 0, 4)
        self._additional_commands_layout.addWidget(
            self.velocity_command_container)
        self._additional_commands_layout.addStretch()

        self._container.addTab(self._status_tab, "StatusBasicCommands")
        self._container.addTab(self._additional_commands_tab,
                               "AdditionalCommands")
        context.add_widget(self._container)

        # Add textboxes to update hooks from eventTrigger class
        # Define Partial callbacks
        systemStatusCallback = partial(self.updateStatus,
                                       text_box=self.system_status_textbox)
        # Connect Event Triggers
        self.event_trigger.status_signal.connect(systemStatusCallback)
        self.event_trigger.pose_command_signal.connect(
            self.poseCommandCallback)

    def createSlider(self, minimum, maximum, default_value, tick_interval):
        """
        Create a QtSlider with specified properties

        Parameters:
        @param minimum Minimum value for slider
        @param maximum Maximum value for slider
        @param default_value Initial value the slider is set to
        @param tick_inverval Integer value specifying difference between
                        successive ticks
        @return Slider value
        """
        slider = QSlider(Qt.Horizontal)
        slider.setMinimum(minimum)
        slider.setMaximum(maximum)
        slider.setValue(default_value)
        slider.setTickPosition(QSlider.TicksBelow)
        slider.setTickInterval(tick_interval)
        return slider

    def _parse_args(self, argv):
        """
        Parse extra arguments when plugin is deployed in standalone mode
        """
        parser = argparse.ArgumentParser(prog='aerial_autonomy',
                                         add_help=False)
        EventTransmissionGUI.add_arguments(parser)
        return parser.parse_args(argv)

    @staticmethod
    def add_arguments(parser):
        """
        Notify rqt_gui that this plugin can parse these extra arguments
        """
        group = parser.add_argument_group(
            'Options for aerial autonomy gui plugin')
        group.add_argument("-e",
                           "--event_file",
                           type=str,
                           default='',
                           help="Event file")
        group.add_argument("-c",
                           "--grid_cols",
                           type=int,
                           default=3,
                           help="Number of columns in grid")

    def get_row_col(self, button_index, ncols):
        """
        Automatically find the row and col to add the button
        to in a grid based on index of the button
        """
        col_index = button_index % ncols
        row_index = int((button_index - col_index) / ncols)
        return (row_index, col_index)

    def poseCommandCallback(self, pose):
        """
        Saves pose command and updates command display
        """
        self.pose_command = pose
        self.pose_x.setText("x: {0:.2f}".format(
            self.pose_command.pose.position.x))
        self.pose_y.setText("y: {0:.2f}".format(
            self.pose_command.pose.position.y))

    def poseCommandButtonCallback(self):
        """
        Publishes stored pose command after setting height from slider
        """
        if self.pose_command:
            self.pose_command.pose.position.z = self.height_slider.value()
            self.event_trigger.triggerPoseCommand(self.pose_command)
            # Reset pose command to avoid accidental triggering
            self.pose_command = None
            self.pose_x.setText('x: -')
            self.pose_y.setText('y: -')
        else:
            print "No pose command to trigger"

    def velocityCommandButtonCallback(self):
        """
        Publishes stored velocity command
        """
        velocity_command = VelocityYaw()
        velocity_command.vx = self.velocity_sliders[0].value() / 10.0
        velocity_command.vy = self.velocity_sliders[1].value() / 10.0
        velocity_command.vz = self.velocity_sliders[2].value() / 10.0
        velocity_command.yaw = self.velocity_sliders[3].value() * np.pi / 180.0
        self.event_trigger.triggerVelocityCommand(velocity_command)

    def updateLabel(self, header, label, slider, scale=1):
        """
        Updates height label based on slider value
        """
        label.setText(header + ": {0:.2f}".format(slider.value() / scale))

    def updateStatus(self, status, text_box):
        """
        Generic placeholder function to update text box
        """
        if not sip.isdeleted(text_box):
            text_box.setHtml(status)
            doc_size = text_box.document().size()
            text_box.setFixedHeight(doc_size.height() + 10)