예제 #1
0
    def motor_controller(self):
        '''
        Sets up the GUI in the middle of the Screen to control the motors. 
        Uses self._motorValues to determine which motors are present.
        '''
        i = 0
        for k, v in sorted(self._currentGoals.items()):
            group = QGroupBox()
            slider = QSlider(Qt.Horizontal)
            slider.setTickInterval(1)
            slider.setMinimum(-181)
            slider.setMaximum(181)
            slider.valueChanged.connect(self.slider_update)
            self._sliders[k] = slider

            textfield = QLineEdit()
            textfield.setText('0')
            textfield.textEdited.connect(self.textfield_update)
            self._textFields[k] = textfield

            label = QLabel()
            label.setText(k)

            layout = QVBoxLayout()
            layout.addWidget(label)
            layout.addWidget(self._sliders[k])
            layout.addWidget(self._textFields[k])
            group.setLayout(layout)
            self._widget.motorControlLayout.addWidget(group, i / 5, i % 5)
            i = i+1
예제 #2
0
class BoxGroup(GroupWidget):
    def __init__(self, updater, config, nodename):
        super(BoxGroup, self).__init__(updater, config, nodename)

        self.box = QGroupBox(self.param_name)
        self.box.setLayout(self.grid)

    def display(self, grid):
        grid.addRow(self.box)
예제 #3
0
class BoxGroup(GroupWidget):
    def __init__(self, updater, config):
        super(BoxGroup, self).__init__(updater, config)

        self.box = QGroupBox(self.name)
        self.box.setLayout(self.grid)

    def display(self, grid, row):
        grid.addWidget(self.box, row, 0, 1, -1)
예제 #4
0
class BoxGroup(GroupWidget):
    def __init__(self, updater, config, nodename):
        super(BoxGroup, self).__init__(updater, config, nodename)

        self.box = QGroupBox(self.param_name)
        self.box.setLayout(self.grid)

    def display(self, grid):
        grid.addRow(self.box)
예제 #5
0
    def create_subgait(self, name, subgait, version_selection):
        subgait_group_box = QGroupBox()
        subgait_group_box.setLayout(QGridLayout())
        subgait_group_box.setObjectName('Subgait')
        subgait_group_box.setTitle(name)
        try:
            version_name = version_selection[name]
        except TypeError:
            version_name = None
        except KeyError:
            version_name = None
        dropdown = self.create_dropdown(subgait, version_name)

        subgait_group_box.layout().addWidget(dropdown, 0, 0)
        return subgait_group_box
예제 #6
0
 def beginGroup(self,obj):
     parent,layout = self.__get_immediate_parent()
     panel = QGroupBox(obj.name,parent)
     if obj.layout == "grid":
         l = QGridLayout()
     elif obj.layout == "vertical":
         l = QVBoxLayout()
     else:
         l = QHBoxLayout()
     
     self.__increase_nesting_level(panel, l)
예제 #7
0
    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()
예제 #8
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
예제 #9
0
    def __init__(self, title, items, parent):
        super(ListDialog, self).__init__(parent)

        self._layout = QVBoxLayout()
        self._view_box = QGroupBox("Optional topics")
        self._view_box_layout = QVBoxLayout(self._view_box)
        self._list_widget = QListWidget()
        self._view_box_layout.addWidget(self._list_widget)
        self._button_box = QDialogButtonBox(QDialogButtonBox.Save | QDialogButtonBox.Close)
        self._button_box.accepted.connect(self.save)
        self._button_box.rejected.connect(self.reject)

        self.setWindowTitle(title)

        self.create_list_widget(items)

        self._layout.addWidget(self._view_box)
        self._layout.addWidget(self._button_box)
        self.setLayout(self._layout)

        # createConnections
        self._list_widget.itemChanged.connect(self.highlight_checked)

        self.selected_items = []
예제 #10
0
 def setupButtons(self, yaml_file):
     """
     Parse yaml file and setup Buttons. Format of the yaml file should be:
     - name: 'button name' (required)
       image: 'path to image for icon' (optional)
       image_size: 'width and height of icon' (optional)
       service: 'service' (required)
       column: 'column index' (optional, defaults to 0)
     """
     self.buttons = []
     with open(yaml_file) as f:
         yaml_data = yaml.load(f)
         # lookup colum direction
         direction = 'vertical'
         for d in yaml_data:
             if d.has_key('direction'):
                 if d['direction'] == 'horizontal':
                     direction = 'horizontal'
                 else:  # d['direction'] == 'vertical':
                     direction = 'vertical'
                 yaml_data.remove(d)
                 break
         # lookup column num
         column_indices = [d['column'] for d in yaml_data]
         max_column_index = max(*column_indices)
         if direction == 'vertical':
             self.layout = QHBoxLayout()
             self.layout_boxes = [
                 QVBoxLayout() for i in range(max_column_index + 1)
             ]
         else:  # direction == 'horizontal'
             self.layout = QVBoxLayout()
             self.layout_boxes = [
                 QHBoxLayout() for i in range(max_column_index + 1)
             ]
         self.button_groups = [
             QGroupBox() for i in range(max_column_index + 1)
         ]
         for button_data in yaml_data:
             # check if all the field is available
             if not button_data.has_key("name"):
                 self.showError("name field is missed in yaml")
                 raise Exception("name field is missed in yaml")
             if not button_data.has_key("service"):
                 self.showError("service field is missed in yaml")
                 raise Exception("service field is missed in yaml")
             if self.button_type == "push":
                 button = QToolButton()
             else:  # self.button_type == "radio":
                 button = QRadioButton()
             button.setSizePolicy(
                 QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred))
             if button_data.has_key("image"):
                 image_file = get_filename(
                     button_data["image"])[len("file://"):]
                 if os.path.exists(image_file):
                     icon = QtGui.QIcon(image_file)
                     button.setIcon(icon)
                     if button_data.has_key("image_size"):
                         button.setIconSize(
                             QSize(button_data["image_size"][0],
                                   button_data["image_size"][1]))
                     else:
                         button.setIconSize(QSize(100, 100))
             if button_data.has_key("name"):
                 name = button_data['name']
                 button.setText(name)
             if button_data.has_key('service_type'):
                 if button_data['service_type'] == 'Trigger':
                     service_type = Trigger
                 elif button_data['service_type'] == 'Empty':
                     service_type = Empty
                 elif button_data['service_type'] == 'SetBool':
                     service_type = SetBool
                 else:
                     raise Exception("Unsupported service type: {}".format(
                         button_data['service_type']))
             else:
                 service_type = Empty
             if service_type == SetBool:
                 button.setCheckable(True)
             button.clicked.connect(
                 self.buttonCallback(button_data['service'], service_type,
                                     button))
             if self.button_type == "push":
                 button.setToolButtonStyle(
                     QtCore.Qt.ToolButtonTextUnderIcon)
             if ((self.button_type == "radio" or service_type == SetBool)
                     and ("default_value" in button_data
                          and button_data['default_value'])):
                 button.setChecked(True)
             self.layout_boxes[button_data['column']].addWidget(button)
             self.buttons.append(button)
         for i in range(len(self.button_groups)):
             self.button_groups[i].setLayout(self.layout_boxes[i])
         for group in self.button_groups:
             self.layout.addWidget(group)
         self.setLayout(self.layout)
예제 #11
0
    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()
예제 #12
0
class BenchmarkConfig(object):
    def __init__(self, config, config_path, benchmark_name,
                 benchmark_result_type):
        self.config = config
        self.config_path = config_path
        self.benchmark_name = benchmark_name
        self.result_type = benchmark_result_type
        self.variation_widgets = {}
        self.result_widgets = {}
        self.bagfile_name = None
        self.notes_widget = None

    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)

    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

    def get_current_selections(self):
        '''
        read current selections from the config UI elements
        and return it as a dictionary
        '''
        variations = self.config['variations'].keys()
        trial_config = {}
        for var in variations:
            widget = self.variation_widgets[var]
            selections = []

            for child in widget.children():
                if isinstance(child, QRadioButton) or isinstance(
                        child, QCheckBox):
                    if child.isChecked():
                        selections.append(child.text())
                elif isinstance(child, QComboBox):
                    selections.append(child.currentText())

            trial_config[var] = selections
        return trial_config

    def apply_selections(self, trial_config):
        '''
        apply selections specified in trial_config to config UI elements
        '''
        variations = self.config['variations'].keys()
        for var in variations:
            widget = self.variation_widgets[var]
            selections = trial_config[var]
            if len(selections) == 0:
                return
            for child in widget.children():
                if isinstance(child, QRadioButton) or isinstance(
                        child, QCheckBox):
                    if child.text() in selections:
                        child.setChecked(True)
                    else:
                        child.setChecked(False)
                elif isinstance(child, QComboBox):
                    child.setCurrentText(selections[0])

    def clear_selections(self):
        '''
        remove all selections in config UI elements
        (except radio buttons since they cannot be unselected)
        '''
        variations = self.config['variations'].keys()
        for var in variations:
            widget = self.variation_widgets[var]
            for child in widget.children():
                if isinstance(child, QRadioButton) or isinstance(
                        child, QCheckBox):
                    child.setChecked(False)
                elif isinstance(child, QComboBox):
                    child.setCurrentIndex(0)

    def generate(self):
        '''
        Generate a set of selections for all config elements
        '''
        trial_config = {}
        variations = self.config['variations'].keys()
        for var in variations:
            single_choice = True
            if var in self.config['multiple choice variation']:
                single_choice = False

            selected_indices = []
            if single_choice:
                choices = list(range(len(self.config['variations'][var])))
                probabilities = None
                if var in self.config['selection likelihood'].keys():
                    probabilities = self.config['selection likelihood'][var]
                choice = np.random.choice(choices, p=probabilities)
                selected_indices.append(choice)
            else:
                num_choices = len(self.config['variations'][var])
                # TODO: should this be configurable?
                min_selections = 2
                max_selections = 5
                num_selections = random.randint(min_selections, max_selections)
                for idx in range(num_selections):
                    choice = random.randrange(0, num_choices)
                    selected_indices.append(choice)
            selected_items = np.array(
                self.config['variations'][var])[selected_indices].tolist()
            trial_config[var] = selected_items
        return trial_config

    def show_results(self, msg, timeout, stopped):
        pass

    def clear_results(self):
        '''
        clear results elements
        '''
        self.bagfile_name = None
        results = self.config['results'].keys()
        for res in results:
            if isinstance(self.config['results'][res], list):
                single_choice = True
                if var in self.config['multiple choice result']:
                    single_choice = False
                if not single_choice:
                    widget = self.result_widgets[res]
                    for child in widget.children():
                        if isinstance(child, QRadioButton) or isinstance(
                                child, QCheckBox):
                            child.setChecked(False)
                        elif isinstance(child, QComboBox):
                            child.setCurrentIndex(0)
            else:
                self.result_widgets[res].setText('')
        self.notes_widget.clear()

    def get_trial_result_dict(self, msg, current_trial_name, current_team_name,
                              timeout, stopped, elapsed_time):
        '''
        return dictionary with all result fields for this benchmark
        '''
        results = {}
        results['trial_id'] = current_trial_name
        results['team_name'] = current_team_name
        results['bagfile'] = self.bagfile_name
        results['duration'] = elapsed_time
        results['timeout'] = timeout
        results['stopped'] = stopped
        results['config'] = self.get_current_selections()
        results['notes'] = self.notes_widget.toPlainText()
        if not stopped and not timeout:
            results['results'] = self.get_result_dict_from_msg(msg)
        else:
            results['results'] = {}
        return results

    def get_result_dict_from_msg(self, msg):
        '''
        Return benchmark-specific results in the form of a dictionary
        Must be overridden by subclasses.
        '''
        pass

    def get_task_info_for_robot(self):
        '''
        return config information which is required for the robot to perform the task
        e.g. object to be detected, grasp pose, navigation poses etc.
        '''
        return {}

    def get_timeout(self):
        return self.config["timeout"]

    def set_bagfile_name(self, name):
        self.bagfile_name = name

    def get_bagfile_name(self):
        return self.bagfile_name

    def lock_config(self):
        self.config_group_box.setChecked(False)

    def unlock_config(self):
        self.config_group_box.setChecked(True)
예제 #13
0
    def __init__(self, updater, config, nodename):
        super(BoxGroup, self).__init__(updater, config, nodename)

        self.box = QGroupBox(self.param_name)
        self.box.setLayout(self.grid)
예제 #14
0
    def create_gait(self, name, gait, selections):
        gait_group_box = QGroupBox()
        gait_group_box.setObjectName('Gait')
        gait_group_box.setLayout(QHBoxLayout())

        gait_group_box.setTitle(name)
        image = QLabel()
        image.setStyleSheet(
            'background: url(' + gait['image'] + ') no-repeat center center 100px 100px;')
        image.setFixedSize(64, 80)
        gait_group_box.layout().addWidget(image)
        for subgait_name, subgait in gait['subgaits'].iteritems():
            subgait_group_box = self.create_subgait(subgait_name, subgait, selections)
            gait_group_box.layout().addWidget(subgait_group_box)

        return gait_group_box
예제 #15
0
    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)
예제 #16
0
    def __init__(self, updater, config, nodename):
        super(BoxGroup, self).__init__(updater, config, nodename)

        self.box = QGroupBox(self.param_name)
        self.box.setLayout(self.grid)
예제 #17
0
    def setup(self):
        layout = QGridLayout()

        # Sidebar
        self.benchmark_combo_box = QComboBox()
        for key in self.config['benchmarks'].keys():
            self.benchmark_combo_box.addItem(
                self.config['benchmarks'][key]['name'])
        self.benchmark_combo_box.currentIndexChanged.connect(
            self._handle_benchmark_selected)
        self.benchmark_combo_box.setMaximumWidth(SIDEBAR_WIDTH)
        self.set_current_benchmark()

        self.team_combo_box = QComboBox()
        for key in self.config['teams']:
            self.team_combo_box.addItem(key)
        self.team_combo_box.setMaximumWidth(SIDEBAR_WIDTH)
        self.test_communication_button = QPushButton('Test communication')
        self.test_communication_button.clicked.connect(self._handle_test_comm)

        self.trial_list_widget = QListWidget()
        self.trial_list_widget.currentItemChanged.connect(
            self._handle_trial_change)
        self.trial_list_widget.setMaximumWidth(SIDEBAR_WIDTH)

        sidebar_layout = QVBoxLayout()
        sidebar_layout.addWidget(QLabel("Team"))
        sidebar_layout.addWidget(self.team_combo_box)
        sidebar_layout.addWidget(self.test_communication_button)
        sidebar_layout.addWidget(QLabel("Benchmark"))
        sidebar_layout.addWidget(self.benchmark_combo_box)
        sidebar_layout.addWidget(QLabel("Trial"))
        sidebar_layout.addWidget(self.trial_list_widget)

        self.generate_button = QPushButton('Generate')
        self.generate_button.clicked.connect(self._handle_generate)

        self.delete_button = QPushButton('Delete')
        self.delete_button.clicked.connect(self._handle_delete)

        self.save_trials_button = QPushButton('Save')
        self.save_trials_button.clicked.connect(self._handle_save_trial_config)

        self.lock_button = QPushButton('Lock')
        if self.config_locked:
            self.lock_button.setText('Unlock')
        self.lock_button.clicked.connect(self._handle_lock)

        sidebar_button_layout = QGridLayout()
        sidebar_button_layout.addWidget(self.generate_button, 0, 0)
        sidebar_button_layout.addWidget(self.delete_button, 0, 1)
        sidebar_button_layout.addWidget(self.save_trials_button, 1, 0)
        sidebar_button_layout.addWidget(self.lock_button, 1, 1)
        sidebar_layout.addLayout(sidebar_button_layout)

        layout.addLayout(sidebar_layout, 0, 0)

        # Status box
        self.status = QPlainTextEdit()
        self.status.setReadOnly(True)
        self.status.setMaximumHeight(200)

        # row 1, col 0, rowspan 1, colspan 2
        layout.addWidget(self.status, 1, 0, 1, 2)

        # trial config and results
        trial_layout = QVBoxLayout()
        self.trial_config_layout = QHBoxLayout()
        self.trial_results_layout = QVBoxLayout()
        self.setup_trial_config()

        # benchmark trial controls
        benchmark_controls_group_box = QGroupBox('Controls')
        benchmark_controls_layout = QHBoxLayout()
        self.start_trial_button = QPushButton('Start')
        self.start_trial_button.clicked.connect(self._handle_start_trial)
        self.stop_trial_button = QPushButton('Stop')
        self.stop_trial_button.clicked.connect(self._handle_stop_trial)
        self.prev_trial_button = QPushButton('Previous')
        self.prev_trial_button.clicked.connect(self._handle_prev_trial)
        self.next_trial_button = QPushButton('Next')
        self.next_trial_button.clicked.connect(self._handle_next_trial)
        self.start_continuous_recording_button = QPushButton(
            'Start continuous recording')
        self.start_continuous_recording_button.clicked.connect(
            self._handle_continuous_recording)

        self.timer_field = QLabel()
        font = QFont("Arial", 20, QFont.Bold)
        self.timer_field.setFont(font)
        self.timer_field.setAutoFillBackground(True)
        benchmark_controls_layout.addWidget(self.start_trial_button)
        benchmark_controls_layout.addWidget(self.stop_trial_button)
        benchmark_controls_layout.addWidget(self.prev_trial_button)
        benchmark_controls_layout.addWidget(self.next_trial_button)
        benchmark_controls_layout.addWidget(
            self.start_continuous_recording_button)
        benchmark_controls_layout.addWidget(self.timer_field)
        benchmark_controls_group_box.setLayout(benchmark_controls_layout)

        trial_layout.addLayout(self.trial_config_layout)
        trial_layout.addWidget(benchmark_controls_group_box)
        trial_layout.addLayout(self.trial_results_layout)
        self.save_results_button = QPushButton('Save results')
        self.save_results_button.clicked.connect(self._handle_save_result)
        trial_layout.addWidget(self.save_results_button)

        layout.addLayout(trial_layout, 0, 1)

        self.setLayout(layout)
        self.show()

        self.show_env_var('ROS_MASTER_URI')
        self.show_env_var('ROS_IP')
        self.show_env_var('ROS_HOSTNAME')