Пример #1
0
    def __init__(self, text):
        self.item = QListWidgetItem()

        item = LayoutWidget(QHBoxLayout)
        label = Label(text, style=styles.default_font_bold)
        _, self.button, _ = item.addWidgets(
            [Spacer(width=5 * config.wr),
             DeleteButton(), label.text])

        item.layout.addStretch()
        item.layout.setSizeConstraint(QLayout.SetFixedSize)
        item.layout.setSpacing(5)
        item.layout.setContentsMargins(5, 10, 5, 5)

        self.widget = item.widget
        self.item.setSizeHint(self.widget.sizeHint())
Пример #2
0
    def __init__(self, name, width, height, layout=None, parent=None, tabbed=False):
        super(Window, self).__init__(parent)
        self.title = name
        self.width = width*config.wr
        self.height = height*config.hr
        logger.debug('Creating %s window [width:%.2f, height:%.2f]' % (
            name, self.width, self.height))
        self.parent = parent

        self.setWindowTitle(self.title)

        if tabbed:
            self.tabs = QTabWidget()
        else:
            self.window = LayoutWidget(layout)
            self.window.layout.setContentsMargins(
                5*config.wr, 5*config.hr, 5*config.wr, 5*config.hr)
            self.window.layout.setSpacing(5*config.wr)

        if tabbed:
            self.tabs.setStyleSheet(styles.tab_style)
            self.setCentralWidget(self.tabs)
        else:
            self.setCentralWidget(self.window.widget)
        self.resize(self.width, self.height)
Пример #3
0
    def initUI(self):
        fit_config = LayoutWidget(QVBoxLayout)

        # List of row options
        self.curve_option, self.fit_option, self.fit_from, self.fit_to, self.set_bounds, _ = fit_config.addWidgets(
            [
                DropDown('Data:', data_manager.get_growth_data_labels()),
                DropDown('Fit:', config.fit_options),
                TextEntry('From:', default=config.fit_from),
                TextEntry('To:', default=config.fit_to),
                CheckBox('Set parameter bounds',
                         change_action=self.render_bounds),
                Button("Fit", clicked=self.fit)
            ])

        self.window.addWidget(fit_config.widget)

        self.bound_input = LayoutWidget(QVBoxLayout)
        self.param_bounds = self.bound_input.addWidget(ParameterBounds("p"))

        self.window.addWidget(self.bound_input.widget)
        self.bound_input.hide()
Пример #4
0
    def initUI(self):
        self.test_option = self.window.addWidget(
            DropDown('Test:', config.test_options, change_action=self.render_test))

        self.data_input = LayoutWidget(QVBoxLayout)
        self.data_option1, self.data_option2 = self.data_input.addWidgets([
            DropDown('Sample 1:', data_manager.get_growth_data_labels()),
            DropDown('Sample 2:', data_manager.get_growth_data_labels())])
        self.window.addWidget(self.data_input.widget)

        self.test_measurement = self.window.addWidget(
            DropDown('Measurement:', config.measurement_options, change_action=self.render_measurement))

        self.measurement = LayoutWidget(QVBoxLayout)
        self.window.addWidget(self.measurement.widget)
        self.measurement.hide()

        self.window.addWidget(Button("Test", clicked=self.test))

        self.statistic, self.pvalue = self.window.addWidgets([
            RoundLabel("Statistic = "), RoundLabel("P value = ")])

        self.window.addWidget(Spacer())
Пример #5
0
    def __init__(self, text, parent=None):
        self.item = QListWidgetItem()
        self.type = text

        # Horizontal box layout
        layout = LayoutWidget(QHBoxLayout)

        spacer = layout.addWidget(QWidget())
        spacer.setFixedWidth(10 * config.wr)

        # Add a delete button
        layout.addWidget(DeleteButton(clicked=parent.remove_item))

        # Add a label with row type
        if (text == 'profile'):
            layout.addWidget(RoundLabel('Profile'))

        if (text == 'reactor'):
            layout.addWidget(RoundLabel('Reactor'))

        # Add other options based on type
        # Gradient needs a start and end measurement point in Y
        if (text == 'gradient'):
            self.data, self.grad_from, self.grad_to = layout.addWidgets([
                DropDown('Gradient of:', data_manager.get_growth_variables()),
                TextEntry('Between:', default=-1, placeholder='Y = '),
                TextEntry('And:', default=-1, placeholder='Y = ')
            ])

        # Time to needs a Y point to reach
        if (text == 'time to'):
            self.data, self.time_to = layout.addWidgets([
                DropDown('Time for:', data_manager.get_growth_variables()),
                TextEntry('To reach:', default=-1, placeholder='Y = ')
            ])

        # Average of a condition needs condition and start and end time
        if (text == 'average of condition'):
            self.condition, self.start_t, self.end_t = layout.addWidgets([
                DropDown('Average of:',
                         data_manager.get_condition_variables()),
                TextEntry('Between:', default=-1, placeholder=config.xvar),
                TextEntry('And:', default=-1, placeholder=config.xvar)
            ])

        # Condition at time needs condition and time
        if (text == 'condition at time'):
            self.condition, self.time = layout.addWidgets([
                DropDown('Value of:', data_manager.get_condition_variables()),
                TextEntry('At:', default=-1, placeholder=config.xvar)
            ])

        # Value of fit parameter needs fit and parameter
        if (text == 'fit parameter'):
            model = get_model(config.fit_options[0], '', '')
            self.fit, self.data, self.param, self.fit_from, self.fit_to, self.show_error = layout.addWidgets(
                [
                    DropDown('Fit:',
                             config.fit_options,
                             change_action=self.update_param_list),
                    DropDown('Data:', data_manager.get_growth_variables()),
                    DropDown('Parameter:', model.params),
                    TextEntry('From:', default=-1, placeholder=config.xvar),
                    TextEntry('To:', default=-1, placeholder=config.xvar),
                    CheckBox("Show error")
                ])

        # Pad out the row
        layout.layout.addStretch()
        layout.layout.setSpacing(5)
        layout.layout.setContentsMargins(10, 10, 50, 5)

        self.widget = layout.widget
        self.item.setSizeHint(self.widget.sizeHint())
Пример #6
0
    def __init__(self, text, index, parent=None):
        self.item = QListWidgetItem()
        list_item = LayoutWidget(QVBoxLayout, margin=0, spacing=0)

        data = LayoutWidget(QHBoxLayout)
        show_box = QCheckBox()
        show_box.setChecked(data_manager.get_condition_file(index).visible)
        show_box.clicked.connect(parent.set_condition_visibility)
        data_label = Label(text, shadow=False, style=styles.default_font_bold)

        data.addWidgets([
            DeleteButton(clicked=parent.remove_condition_item), show_box,
            data_label.text
        ])
        data.layout.addStretch()
        data.layout.setSizeConstraint(QLayout.SetFixedSize)
        list_item.addWidget(data.widget)

        for j in range(1, data_manager.num_condition_replicates(index), 1):
            replicate = LayoutWidget(QHBoxLayout)
            replicate_label = Label(
                data_manager.condition_data.replicate_files[index][j].label,
                shadow=False,
                style=styles.small_font)
            replicate.addWidgets([
                QLabel('-'),
                DeleteButton(clicked=(lambda bound_j=j: lambda: parent.
                                      remove_condition_replicate(bound_j))()),
                replicate_label.text
            ])
            replicate.layout.addStretch()
            replicate.layout.setSizeConstraint(QLayout.SetFixedSize)
            list_item.addWidget(replicate)

        list_item.layout.addStretch()
        list_item.layout.setSizeConstraint(QLayout.SetFixedSize)

        self.widget = list_item.widget
        self.item.setSizeHint(self.widget.sizeHint())
Пример #7
0
class TestWindow(Window):

    def __init__(self, parent=None):
        super(TestWindow, self).__init__(
            'Statistical test', 200, 150, QVBoxLayout, parent)
        self.initUI()

    def initUI(self):
        self.test_option = self.window.addWidget(
            DropDown('Test:', config.test_options, change_action=self.render_test))

        self.data_input = LayoutWidget(QVBoxLayout)
        self.data_option1, self.data_option2 = self.data_input.addWidgets([
            DropDown('Sample 1:', data_manager.get_growth_data_labels()),
            DropDown('Sample 2:', data_manager.get_growth_data_labels())])
        self.window.addWidget(self.data_input.widget)

        self.test_measurement = self.window.addWidget(
            DropDown('Measurement:', config.measurement_options, change_action=self.render_measurement))

        self.measurement = LayoutWidget(QVBoxLayout)
        self.window.addWidget(self.measurement.widget)
        self.measurement.hide()

        self.window.addWidget(Button("Test", clicked=self.test))

        self.statistic, self.pvalue = self.window.addWidgets([
            RoundLabel("Statistic = "), RoundLabel("P value = ")])

        self.window.addWidget(Spacer())

    @error_wrapper
    def render_test(self):
        option = self.test_option.currentText()
        if option == 'T-test':
            self.data_input.show()
        else:
            self.data_input.hide()

    @error_wrapper
    def render_measurement(self):
        self.measurement.clear()
        option = self.test_measurement.currentText()
        if option == 'gradient':
            self.signal, self.grad_from, self.grad_to = self.measurement.addWidgets([
                DropDown('Gradient of:', data_manager.get_growth_variables()),
                TextEntry('Between:', placeholder='Y = '),
                TextEntry('And:', placeholder='Y = ')])
        elif option == 'time to':
            self.signal, self.time_to = self.measurement.addWidgets([
                DropDown('Time for:', data_manager.get_growth_variables()),
                TextEntry('To reach:', placeholder='Y = ')])
        elif option == 'fit parameter':
            model = get_model(config.fit_options[0])
            self.fit, self.signal, self.param, self.fit_from, self.fit_to = self.measurement.addWidgets([
                DropDown('Fit:', config.fit_options,
                         change_action=self.update_param_list),
                DropDown('Of:', data_manager.get_growth_variables()),
                DropDown('Parameter:', model.params),
                TextEntry('From:', placeholder=config.xvar),
                TextEntry('To:', placeholder=config.xvar)])
        else:
            self.measurement.hide()
            return
        self.measurement.show()
        return

    def update_param_list(self, fit_name):
        self.param.clear()
        model = get_model(fit_name, "", "")
        self.param.addItems(model.params)

    # Add the fit info to the configuration
    @error_wrapper
    def test(self):
        config.do_fit = True
        test_option = self.test_option.currentText()
        measurement_option = self.test_measurement.currentText()
        # Calculate value of all replicates individually
        measurements = []
        for i, dat in enumerate(data_manager.get_growth_data_files()):
            if test_option == 'T-test':
                data1 = self.data_option1.currentText(error=True)
                data2 = self.data_option2.currentText(error=True)
                if data1 == data2:
                    raise RuntimeError('Samples must be different')
                if dat.label != data1 and dat.label != data2:
                    continue
            signal = self.signal.currentText()
            if measurement_option == 'gradient':
                grad_from = self.grad_from.get_float(error=True)
                grad_to = self.grad_to.get_float(error=True)
                measurements.append(data_manager.get_replicate_gradients(
                    i, signal, grad_from, grad_to))
            elif measurement_option == 'time to':
                time_to = self.time_to.get_float(error=True)
                measurements.append(
                    data_manager.get_replicate_time_to(i, signal, time_to))
            elif measurement_option == 'fit parameter':
                fit_name = self.fit.currentText(error=True)
                fit_from = self.fit_from.get_float(error=True)
                fit_to = self.fit_to.get_float(error=True)
                fit_param = self.param.currentText(error=True)
                measurements.append(data_manager.get_replicate_fits(
                    i, signal, fit_name, fit_from, fit_to, fit_param))
            else:
                raise RuntimeError('Unknown measurement')
        # Calculate test result
        statistic, pvalue = -1, -1
        if test_option == 'T-test':
            if len(measurements) != 2:
                raise RuntimeError('Unable to find data for t-test')
            statistic, pvalue = ttest_ind(measurements[0], measurements[1])
        if test_option == 'ANOVA':
            statistic, pvalue = f_oneway(*measurements)
        # Display test result
        self.statistic.setText('Statistic = ' + str(statistic))
        self.pvalue.setText('P value = ' + str(pvalue))
Пример #8
0
    def initUI(self):
        self.setGeometry(self.left, self.top, self.width, self.height)

        # Configuration tab
        corr_config = LayoutWidget(QVBoxLayout, margin=5, spacing=0)

        # X axis selection = condition variable
        # Dropdown of condition variables OR take from main plot
        self.condition = corr_config.addWidget(
            DropDown('X-axis: Average of',
                     data_manager.get_condition_variables()))

        # Y axis selection = growth related measurement
        # Dropdown of y variables (OD/CD) OR take from main plot
        self.data = corr_config.addWidget(
            DropDown('Y-axis:', data_manager.get_growth_variables()))

        # Choice of fit and fit parameter
        fit_option = LayoutWidget(QHBoxLayout)
        model = get_model(config.fit_options[0])
        self.fit, self.param = fit_option.addWidgets([
            DropDown('Fit:',
                     config.fit_options,
                     change_action=self.update_param_list),
            DropDown('Parameter:', model.params)
        ])
        corr_config.addWidget(fit_option.widget)

        range_option = LayoutWidget(QHBoxLayout)
        self.start_t, self.end_t = range_option.addWidgets([
            TextEntry('Between:', default=-1, placeholder=config.xvar),
            TextEntry('And:', default=-1, placeholder=config.xvar)
        ])
        corr_config.addWidget(range_option.widget)

        self.figure_title = corr_config.addWidget(TextEntry('Figure title:'))
        title_option = LayoutWidget(QHBoxLayout)
        self.x_title, self.y_title = title_option.addWidgets(
            [TextEntry('X-axis title:'),
             TextEntry('Y-axis title:')])
        corr_config.addWidget(title_option.widget)

        options = LayoutWidget(QHBoxLayout)
        self.label, self.calc_correlation = options.addWidgets(
            [CheckBox('Label'),
             CheckBox('Calculate correlation')])
        corr_config.addWidget(options.widget)

        # Plot button
        corr_config.addWidget(Button('Plot', clicked=self.update_plot))

        self.tabs.addTab(corr_config.widget, 'Configuration')

        # Plot tab
        plot_view = LayoutWidget(QGridLayout, margin=5, spacing=10)

        # Plot
        self.plot = plot_view.addWidget(
            CorrelationCanvas(self,
                              width=10 * config.wr,
                              height=4 * config.hr,
                              dpi=100 * config.wr), 0, 0, 4, 4)
        shadow = QGraphicsDropShadowEffect(blurRadius=10 * config.wr,
                                           xOffset=3 * config.wr,
                                           yOffset=3 * config.hr)
        self.plot.setGraphicsEffect(shadow)
        self.plot.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)

        # Save button
        plot_view.addWidget(Button('Save', clicked=self.save_plot), 4, 0, 1, 4)

        self.tabs.addTab(plot_view.widget, 'Plot')
Пример #9
0
class FitWindow(Window):
    def __init__(self, parent=None):
        super(FitWindow, self).__init__('Fit Curve', 200, 150, QHBoxLayout,
                                        parent)
        self.rows = []
        self.bounds = []
        self.initUI()

    def initUI(self):
        fit_config = LayoutWidget(QVBoxLayout)

        # List of row options
        self.curve_option, self.fit_option, self.fit_from, self.fit_to, self.set_bounds, _ = fit_config.addWidgets(
            [
                DropDown('Data:', data_manager.get_growth_data_labels()),
                DropDown('Fit:', config.fit_options),
                TextEntry('From:', default=config.fit_from),
                TextEntry('To:', default=config.fit_to),
                CheckBox('Set parameter bounds',
                         change_action=self.render_bounds),
                Button("Fit", clicked=self.fit)
            ])

        self.window.addWidget(fit_config.widget)

        self.bound_input = LayoutWidget(QVBoxLayout)
        self.param_bounds = self.bound_input.addWidget(ParameterBounds("p"))

        self.window.addWidget(self.bound_input.widget)
        self.bound_input.hide()

    @error_wrapper
    def render_bounds(self):
        self.bounds = []
        self.bound_input.clear()
        if self.set_bounds.isChecked():
            self.resize(self.width * 3, self.height)
            model = get_model(self.fit_option.currentText(), '', '')
            for i, param in enumerate(model.params):
                self.bounds.append(ParameterBounds(param))
                self.bound_input.addWidget(self.bounds[i])
            self.bound_input.addWidget(Spacer())
            self.bound_input.show()
        else:
            self.bound_input.hide()
            self.resize(self.width, self.height)

    # Add the fit info to the configuration
    @error_wrapper
    def fit(self):
        config.do_fit = True
        config.fit_curve = self.curve_option.currentText()
        config.fit_type = self.fit_option.currentText()
        logger.debug('Fitting %s with %s' %
                     (config.fit_curve, config.fit_type))
        config.fit_from = self.fit_from.get_float()
        config.fit_to = self.fit_to.get_float()
        config.fit_start = []
        config.fit_min = []
        config.fit_max = []
        for bound in self.bounds:
            config.fit_start.append(bound.get_start())
            config.fit_min.append(bound.get_min())
            config.fit_max.append(bound.get_max())
        if len(config.fit_start) == 0:
            config.fit_start = None
            config.fit_min = None
            config.fit_max = None
        self.parent.update_plot()
        self.close()
Пример #10
0
    def initUI(self):

        self.setWindowTitle(self.title)
        self.setGeometry(self.left, self.top, self.width, self.height)
        wr = config.wr
        hr = config.wr

        tabs = QTabWidget()
        tabs.setStyleSheet(styles.tab_style)

        # ---------------------------------------------------------------------
        #                           PLOTTING TAB
        # ---------------------------------------------------------------------

        # Main plotting window
        splitter = QSplitter()

        plot_view = LayoutWidget(QGridLayout, margin=5, spacing=10)

        # Main plot window (row, column, row extent, column extent)
        self.plot = plot_view.addWidget(
            PlotCanvas(self, width=10 * wr, height=4 * hr, dpi=100 * wr), 0, 1,
            5, 5)
        shadow = QGraphicsDropShadowEffect(blurRadius=10 * wr,
                                           xOffset=3 * wr,
                                           yOffset=3 * hr)
        self.plot.setGraphicsEffect(shadow)
        self.plot.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)

        toolbar = plot_view.addWidget(QToolBar("Tools", self), 0, 0, 5, 1)
        toolbar.setOrientation(Qt.Vertical)
        toolbar.setStyleSheet(styles.toolbar_style)

        toolbar_icons = [
            ":measure.svg", ":fit.svg", ":table.svg", ":correlations.svg",
            ":template.svg", ":normal.svg"
        ]
        toolbar_labels = [
            '&Measure', '&Fit', '&To Table', '&Correlations',
            '&Download Template', '&Statisitical Tests'
        ]
        toolbar_actions = [
            self.toggle_cursor, self.fit_curve, self.create_table,
            self.open_correlation, self.download_template, self.open_tests
        ]
        for i, icon in enumerate(toolbar_icons):
            toolbar_action = QAction(QIcon(icon), toolbar_labels[i], self)
            toolbar_action.triggered.connect(toolbar_actions[i])
            toolbar.addAction(toolbar_action)

        splitter.addWidget(plot_view.widget)

        data_entry = LayoutWidget(QVBoxLayout, margin=5, spacing=10)
        self.data_button, self.data_list, _, self.condition_data_list, _, self.calibration_file, _ = data_entry.addWidgets(
            [
                Button('Add Data',
                       tooltip='Import data for plotting',
                       clicked=self.open_data_files),
                List(scroll=True),
                TopLabel('Condition Data:'),
                List(scroll=True),
                Button('Add Calibration Curve',
                       tooltip='Set OD to CD conversion from file',
                       clicked=self.open_calibration_file),
                DelLabel('', clicked=self.remove_calibration_file),
                BigButton('Plot!',
                          tooltip='Plot the data!',
                          clicked=self.update_plot)
            ])

        self.data_button.setContextMenuPolicy(Qt.CustomContextMenu)
        self.data_button.customContextMenuRequested.connect(
            self.on_context_menu)

        self.clear_menu = QMenu(self)
        self.clear_action = QAction('Clear all', self)
        self.clear_menu.addAction(self.clear_action)

        self.calibration_file.setFixedHeight(40 * hr)

        splitter.addWidget(data_entry.widget)

        tabs.addTab(splitter, 'Plotting')

        # ---------------------------------------------------------------------
        #                           OPTIONS TABS
        # ---------------------------------------------------------------------

        # --------------- AXIS CONFIGURATION

        # Axis configuration
        axis_options = LayoutWidget(QVBoxLayout, style=styles.white_background)
        axis_h = LayoutWidget(QHBoxLayout)

        self.figure_title = axis_options.addWidget(TextEntry('Figure title:'))

        x_options = LayoutWidget(QVBoxLayout)
        x_options.addWidget(TopLabel('X (time):'))

        # X axis config
        x_form = Form()
        self.xaxis_dropdown, self.xaxis_name, self.xaxis_unit, self.xaxis_min, self.xaxis_max, self.xaxis_log = x_form.addRows(
            [
                DropDown('Variable:', config.xaxis_units, index=2),
                TextEntry('Label:'),
                TextEntry('Unit name:', tooltip='Enter "none" for no units'),
                TextEntry('Range min:', default=config.xmin),
                TextEntry('Range max:', default=config.xmax),
                CheckBox('Log scale')
            ],
            padding=[False, False, False, False, False, True])

        x_options.addWidget(x_form.widget)
        axis_h.addWidget(x_options.widget)

        y_options = LayoutWidget(QVBoxLayout)
        y_options.addWidget(TopLabel('Y (growth):'))
        # Y axis config
        y_form = Form()
        self.yaxis_dropdown, self.yaxis_name, self.yaxis_unit, self.yaxis_min, self.yaxis_max = y_form.addRows(
            [
                DropDown('Variable:', []),
                TextEntry('Label:'),
                TextEntry('Unit name:', tooltip='Enter "none" for no units'),
                TextEntry('Range min:', default=config.ymin),
                TextEntry('Range max:', default=config.ymax)
            ])

        # Y axis log scale
        ylog_hbox = LayoutWidget(QHBoxLayout)
        ylog_hbox.layout.setSpacing(15 * wr)
        ylog_hbox.layout.setContentsMargins(0, 0, 1 * wr, 1 * hr)
        self.yaxis_log, self.yaxis_normlog = ylog_hbox.addWidgets(
            [CheckBox('Log scale'),
             CheckBox('ln(Y/Y0)')])
        y_form.addRow(ylog_hbox.widget, pad=True)

        y_options.addWidget(y_form.widget)
        axis_h.addWidget(Spacer())
        axis_h.addWidget(y_options.widget)

        z_options = LayoutWidget(QVBoxLayout)
        z_options.addWidget(TopLabel('Y2 (conditions):'))
        # Condition Y axis drop down menu
        z_form = Form()
        self.condition_yaxis_dropdown, self.condition_yaxis_name, self.condition_yaxis_unit, self.condition_yaxis_min, self.condition_yaxis_max, self.condition_yaxis_log = z_form.addRows(
            [
                DropDown('Variable:', []),
                TextEntry('Label:'),
                TextEntry('Unit name:', tooltip='Enter "none" for no units'),
                TextEntry('Range min:', default=config.condition_ymin),
                TextEntry('Range max:', default=config.condition_ymax),
                CheckBox('Log scale')
            ],
            padding=[False, False, False, False, False, True])

        z_options.addWidget(z_form.widget)
        axis_h.addWidget(Spacer())
        axis_h.addWidget(z_options.widget)
        axis_options.addWidget(axis_h.widget)
        axis_options.addWidget(Spacer())

        tabs.addTab(axis_options.widget, 'Axes')

        # --------------- DATA CONFIGURATION

        # Data configuration options
        data_options = LayoutWidget(QHBoxLayout, style=styles.white_background)
        data_form = Form()

        self.smooth_data, self.align_data, self.y_alignment, self.initial_y, self.growth_average, self.condition_average, self.show_events = data_form.addRows(
            [
                CheckBox('Data smoothing off/on',
                         tooltip='Apply Savitzky-Golay to noisy data'),
                CheckBox('Alignment at time = 0 on/off',
                         tooltip='Start growth curves at 0 time'),
                TextEntry('Align at Y:',
                          default=config.y_alignment,
                          tooltip='Align all growth curves at given Y value'),
                TextEntry('Set initial Y:',
                          default=config.initial_y,
                          tooltip='Start growth curves at a given Y value'),
                TextEntry('Growth data time average:',
                          default=config.growth_average,
                          tooltip='Average over a given time window'),
                TextEntry('Condition data time average:',
                          default=config.condition_average,
                          tooltip='Average over a given time window'),
                CheckBox('Show events off/on')
            ],
            padding=[True, True, False, False, False, False, True])

        data_options.addWidget(data_form.widget)

        # Remove any obvious outliers from the growth data
        outlier_options = LayoutWidget(QVBoxLayout)
        outlier_options.addWidget(TopLabel('Data outliers:'))
        outlier_form = Form()
        self.auto_remove, self.remove_above, self.remove_below = outlier_form.addRows(
            [
                CheckBox('Auto-remove outliers off/on'),
                TextEntry('Remove above:', default=config.remove_above),
                TextEntry('Remove below:', default=config.remove_below)
            ],
            padding=[True, False, False])
        outlier_options.addWidget(outlier_form.widget)
        outlier_options.addWidget(Spacer())

        data_options.addWidget(outlier_options.widget)
        data_options.addWidget(Spacer())

        tabs.addTab(data_options.widget, 'Data')

        # --------------- LEGEND CONFIGURATION

        # Legend configuration options
        legend_options = LayoutWidget(QHBoxLayout,
                                      style=styles.white_background)

        # Legend on/off checkbox
        growth_options = LayoutWidget(QVBoxLayout)
        growth_options.addWidget(TopLabel('Growth Legend:'))
        growth_form = Form()
        self.legend_toggle, self.legend_names, self.legend_title, self.extra_info, self.only_extra = growth_form.addRows(
            [
                CheckBox('Legend on'),
                DropDown(
                    'Labels:', [],
                    tooltip='Edit names by changing text and pressing return',
                    edit=True),
                TextEntry(
                    'Heading:',
                    tooltip='Show extra information from the file in the legend'
                ),
                DropDown('Extra text:', config.info_options),
                CheckBox('Remove labels')
            ],
            padding=[True, False, False, False, True])

        growth_options.addWidget(growth_form.widget)
        growth_options.addWidget(Spacer())
        legend_options.addWidget(growth_options.widget)

        # Condition legend configuration
        condition_options = LayoutWidget(QVBoxLayout)
        condition_options.addWidget(TopLabel('Condition legend:'))
        condition_form = Form()
        self.condition_legend_toggle, self.condition_legend_names, self.condition_legend_title, self.condition_extra_info, self.condition_only_extra = condition_form.addRows(
            [
                CheckBox('Legend on'),
                DropDown(
                    'Labels:', [],
                    tooltip='Edit names by changing text and pressing return',
                    edit=True),
                TextEntry('Heading:'),
                DropDown(
                    'Extra text:',
                    config.info_options,
                    tooltip='Show extra information from the file in the legend'
                ),
                CheckBox('Remove labels')
            ],
            padding=[True, False, False, False, True])

        condition_options.addWidget(condition_form.widget)
        condition_options.addWidget(Spacer())
        legend_options.addWidget(condition_options.widget)
        legend_options.addWidget(Spacer())

        tabs.addTab(legend_options.widget, 'Legend')

        # --------------- STYLE CONFIGURATION

        # Style configuration
        style_options = LayoutWidget(QHBoxLayout,
                                     style=styles.white_background)

        # Plot style dropdown menu
        style_form = Form(align=True)
        self.style_dropdown, self.font_dropdown, self.axis_colour, self.grid_toggle = style_form.addRows(
            [
                DropDown('Style:', config.style_options),
                DropDown('Font style:', config.font_options),
                TextEntry('Condition axis color:'),
                CheckBox('Grid on/off')
            ],
            padding=[False, False, False, True])

        style_options.addWidget(style_form.widget)

        # Sized
        style_numeric_form = Form(align=True)
        self.title_size, self.legend_size, self.label_size, self.line_width, self.marker_size, self.capsize, self.save_dpi = style_numeric_form.addRows(
            [
                SpinBox('Title font size:',
                        start=config.title_size,
                        min_val=0,
                        max_val=100),
                SpinBox('Legend font size:',
                        start=config.legend_size,
                        min_val=0,
                        max_val=100),
                SpinBox('Label font size:',
                        start=config.label_size,
                        min_val=0,
                        max_val=100),
                SpinBox('Line width:',
                        start=config.line_width,
                        min_val=0,
                        max_val=20),
                SpinBox('Marker size:',
                        start=config.marker_size,
                        min_val=0,
                        max_val=20),
                SpinBox('Error cap size:',
                        start=config.capsize,
                        min_val=0,
                        max_val=20),
                SpinBox('Saved figure DPI:',
                        start=config.save_dpi,
                        min_val=10,
                        max_val=2000)
            ])

        style_options.addWidget(style_numeric_form.widget)

        tabs.addTab(style_options.widget, 'Style')

        # --------------- STATS CONFIGURATION

        # Stats configuration
        stats_form = Form(align=True, style=styles.white_background)
        self.std_err, self.sig_figs, self.show_fit_text, self.show_fit_result, self.show_fit_errors = stats_form.addRows(
            [
                RadioButton(
                    'Standard deviation',
                    'Standard error',
                    tooltip=
                    'Show standard deviation or the standard error on the mean in plots and measurements'
                ),
                SpinBox('Significant figures:',
                        start=config.sig_figs,
                        min_val=0,
                        max_val=20),
                CheckBox(
                    'Show fit model text',
                    tooltip='Checked = display equation for fitted model\n'
                    'Unchecked = don'
                    't display equation'),
                CheckBox(
                    'Show fit parameters',
                    tooltip='Checked = show fitted values of model parameters\n'
                    'Unchecked = don'
                    't show fit parameters'),
                CheckBox(
                    'Show fit errors',
                    tooltip='Checked = show uncertainties on fit parameters\n'
                    'Unchecked = don'
                    't show uncertainties')
            ],
            padding=[True, False, True, True, True])

        tabs.addTab(stats_form.widget, 'Stats')

        # --------------- ADVANCED CONFIGURATION

        # Advanced configuration
        advanced_options = LayoutWidget(QHBoxLayout,
                                        style=styles.white_background)

        sg_form = Form(align=True, style=styles.white_background)
        _, self.sg_window_size, self.sg_order, self.sg_deriv, self.sg_rate = sg_form.addRows(
            [
                TopLabel('Savitsky-Golay smoothing:'),
                TextEntry('Window size', default=config.sg_window_size),
                TextEntry('Order of polynomial', default=config.sg_order),
                TextEntry('Order of derivative', default=config.sg_deriv),
                TextEntry('Sample spacing', default=config.sg_rate)
            ])

        adv_outlier_form = Form(align=True, style=styles.white_background)
        self.outlier_threshold = adv_outlier_form.addRow(
            TextEntry('Auto outlier threshold',
                      default=config.outlier_threshold))

        advanced_options.addWidget(sg_form.widget)
        advanced_options.addWidget(adv_outlier_form.widget)
        tabs.addTab(advanced_options.widget, 'Advanced')

        # ----------------------------------
        self.setCentralWidget(tabs)
        self.show()