Example #1
0
class QDPlotterGui(GUIBase):
    """ GUI  for displaying up to 3 custom plots.
    The plots are held in tabified DockWidgets and can either be manipulated in the logic
    or by corresponding parameter DockWidgets.

    Example config for copy-paste:

    qdplotter:
        module.Class: 'qdplotter.qdplotter_gui.QDPlotterGui'
        pen_color_list: [[100, 100, 100], 'c', 'm', 'g']
        connect:
            qdplot_logic: 'qdplotlogic'
    """

    sigPlotParametersChanged = QtCore.Signal(int, dict)
    sigAutoRangeClicked = QtCore.Signal(int, bool, bool)
    sigDoFit = QtCore.Signal(str, int)
    sigRemovePlotClicked = QtCore.Signal(int)

    # declare connectors
    qdplot_logic = Connector(interface='QDPlotLogic')
    # declare config options
    _pen_color_list = ConfigOption(name='pen_color_list',
                                   default=['b', 'y', 'm', 'g'])
    # declare status variables
    widget_alignment = StatusVar(name='widget_alignment', default='tabbed')

    _allowed_colors = {'b', 'g', 'r', 'c', 'm', 'y', 'k', 'w'}

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._plot_logic = None
        self._mw = None
        self._fsd = None

        self._plot_dockwidgets = list()
        self._pen_colors = list()
        self._plot_curves = list()
        self._fit_curves = list()
        self._pg_signal_proxys = list()

    def on_activate(self):
        """ Definition and initialisation of the GUI.
        """
        self._plot_logic = self.qdplot_logic()

        if not isinstance(self._pen_color_list,
                          (list, tuple)) or len(self._pen_color_list) < 1:
            self.log.warning(
                'The ConfigOption pen_color_list needs to be a list of strings but was "{0}".'
                ' Will use the following pen colors as default: {1}.'
                ''.format(self._pen_color_list, ['b', 'y', 'm', 'g']))
            self._pen_color_list = ['b', 'y', 'm', 'g']
        else:
            self._pen_color_list = list(self._pen_color_list)

        for index, color in enumerate(self._pen_color_list):
            if (isinstance(color, (list, tuple)) and len(color) == 3) or \
                    (isinstance(color, str) and color in self._allowed_colors):
                pass
            else:
                self.log.warning(
                    'The color was "{0}" but needs to be from this list: {1} '
                    'or a 3 element tuple with values from 0 to 255 for RGB.'
                    ' Setting color to "b".'.format(color,
                                                    self._allowed_colors))
                self._pen_color_list[index] = 'b'

        # Use the inherited class 'QDPlotMainWindow' to create the GUI window
        self._mw = QDPlotMainWindow()

        # Fit settings dialogs
        self._fsd = FitSettingsDialog(self._plot_logic.fit_container)
        self._fsd.applySettings()
        self._mw.fit_settings_Action.triggered.connect(self._fsd.show)

        # Connect the main window restore view actions
        self._mw.restore_tabbed_view_Action.triggered.connect(
            self.restore_tabbed_view)
        self._mw.restore_side_by_side_view_Action.triggered.connect(
            self.restore_side_by_side_view)
        self._mw.restore_arc_view_Action.triggered.connect(
            self.restore_arc_view)
        self._mw.save_all_Action.triggered.connect(self.save_all_clicked)

        # Initialize dock widgets
        self._plot_dockwidgets = list()
        self._pen_colors = list()
        self._plot_curves = list()
        self._fit_curves = list()
        self._pg_signal_proxys = list()
        self.update_number_of_plots(self._plot_logic.number_of_plots)
        # Update all plot parameters and data from logic
        for index, _ in enumerate(self._plot_dockwidgets):
            self.update_data(index)
            self.update_fit_data(index)
            self.update_plot_parameters(index)
        self.restore_view()

        # Connect signal to logic
        self.sigPlotParametersChanged.connect(
            self._plot_logic.update_plot_parameters,
            QtCore.Qt.QueuedConnection)
        self.sigAutoRangeClicked.connect(self._plot_logic.update_auto_range,
                                         QtCore.Qt.QueuedConnection)
        self.sigDoFit.connect(self._plot_logic.do_fit,
                              QtCore.Qt.QueuedConnection)
        self.sigRemovePlotClicked.connect(self._plot_logic.remove_plot,
                                          QtCore.Qt.QueuedConnection)
        self._mw.new_plot_Action.triggered.connect(self._plot_logic.add_plot,
                                                   QtCore.Qt.QueuedConnection)
        # Connect signals from logic
        self._plot_logic.sigPlotDataUpdated.connect(self.update_data,
                                                    QtCore.Qt.QueuedConnection)
        self._plot_logic.sigPlotParamsUpdated.connect(
            self.update_plot_parameters, QtCore.Qt.QueuedConnection)
        self._plot_logic.sigPlotNumberChanged.connect(
            self.update_number_of_plots, QtCore.Qt.QueuedConnection)
        self._plot_logic.sigFitUpdated.connect(self.update_fit_data,
                                               QtCore.Qt.QueuedConnection)

        self.show()

    def show(self):
        """ Make window visible and put it above all other windows. """
        self._mw.show()
        self._mw.activateWindow()
        self._mw.raise_()

    def on_deactivate(self):
        """ Deactivate the module """
        # disconnect fit
        self._mw.fit_settings_Action.triggered.disconnect()

        self._mw.restore_tabbed_view_Action.triggered.disconnect()
        self._mw.restore_side_by_side_view_Action.triggered.disconnect()
        self._mw.restore_arc_view_Action.triggered.disconnect()
        self._mw.save_all_Action.triggered.disconnect()

        # Disconnect signal to logic
        self.sigPlotParametersChanged.disconnect()
        self.sigAutoRangeClicked.disconnect()
        self.sigDoFit.disconnect()
        self.sigRemovePlotClicked.disconnect()
        self._mw.new_plot_Action.triggered.disconnect()
        # Disconnect signals from logic
        self._plot_logic.sigPlotDataUpdated.disconnect(self.update_data)
        self._plot_logic.sigPlotParamsUpdated.disconnect(
            self.update_plot_parameters)
        self._plot_logic.sigPlotNumberChanged.disconnect(
            self.update_number_of_plots)
        self._plot_logic.sigFitUpdated.disconnect(self.update_fit_data)

        # disconnect GUI elements
        self.update_number_of_plots(0)

        self._fsd.sigFitsUpdated.disconnect()
        self._mw.close()

    @QtCore.Slot(int)
    def update_number_of_plots(self, count):
        """ Adjust number of QDockWidgets to current number of plots. Does NO initialization of the
        contents.

        @param int count: Number of plots to display.
        """
        # Remove dock widgets if plot count decreased
        while count < len(self._plot_dockwidgets):
            index = len(self._plot_dockwidgets) - 1
            self._disconnect_plot_signals(index)
            self._plot_dockwidgets[-1].setParent(None)
            del self._plot_curves[-1]
            del self._fit_curves[-1]
            del self._pen_colors[-1]
            del self._plot_dockwidgets[-1]
            del self._pg_signal_proxys[-1]
        # Add dock widgets if plot count increased
        while count > len(self._plot_dockwidgets):
            index = len(self._plot_dockwidgets)
            dockwidget = PlotDockWidget('Plot {0:d}'.format(index + 1),
                                        self._mw)
            dockwidget.widget().fit_comboBox.setFitFunctions(
                self._fsd.currentFits)
            dockwidget.widget().show_fit_checkBox.setChecked(False)
            dockwidget.widget().show_controls_checkBox.setChecked(False)
            dockwidget.widget().fit_groupBox.setVisible(False)
            dockwidget.widget().controls_groupBox.setVisible(False)
            self._plot_dockwidgets.append(dockwidget)
            self._pen_colors.append(cycle(self._pen_color_list))
            self._plot_curves.append(list())
            self._fit_curves.append(list())
            self._pg_signal_proxys.append([None, None])
            self._connect_plot_signals(index)
            self.restore_view()

    def _connect_plot_signals(self, index):
        dockwidget = self._plot_dockwidgets[index].widget()
        self._fsd.sigFitsUpdated.connect(
            dockwidget.fit_comboBox.setFitFunctions)
        dockwidget.fit_pushButton.clicked.connect(
            functools.partial(self.fit_clicked, index))

        x_lim_callback = functools.partial(self.x_limits_changed, index)
        dockwidget.x_lower_limit_DoubleSpinBox.valueChanged.connect(
            x_lim_callback)
        dockwidget.x_upper_limit_DoubleSpinBox.valueChanged.connect(
            x_lim_callback)
        y_lim_callback = functools.partial(self.y_limits_changed, index)
        dockwidget.y_lower_limit_DoubleSpinBox.valueChanged.connect(
            y_lim_callback)
        dockwidget.y_upper_limit_DoubleSpinBox.valueChanged.connect(
            y_lim_callback)

        dockwidget.x_label_lineEdit.editingFinished.connect(
            functools.partial(self.x_label_changed, index))
        dockwidget.x_unit_lineEdit.editingFinished.connect(
            functools.partial(self.x_unit_changed, index))
        dockwidget.y_label_lineEdit.editingFinished.connect(
            functools.partial(self.y_label_changed, index))
        dockwidget.y_unit_lineEdit.editingFinished.connect(
            functools.partial(self.y_unit_changed, index))

        dockwidget.x_auto_PushButton.clicked.connect(
            functools.partial(self.x_auto_range_clicked, index))
        dockwidget.y_auto_PushButton.clicked.connect(
            functools.partial(self.y_auto_range_clicked, index))
        dockwidget.save_pushButton.clicked.connect(
            functools.partial(self.save_clicked, index))
        dockwidget.remove_pushButton.clicked.connect(
            functools.partial(self.remove_clicked, index))
        self._pg_signal_proxys[index][0] = SignalProxy(
            dockwidget.plot_PlotWidget.sigXRangeChanged,
            delay=0.2,
            slot=lambda args: self._pyqtgraph_x_limits_changed(index, args[1]))
        self._pg_signal_proxys[index][1] = SignalProxy(
            dockwidget.plot_PlotWidget.sigYRangeChanged,
            delay=0.2,
            slot=lambda args: self._pyqtgraph_y_limits_changed(index, args[1]))

    def _disconnect_plot_signals(self, index):
        dockwidget = self._plot_dockwidgets[index].widget()
        self._fsd.sigFitsUpdated.disconnect(
            dockwidget.fit_comboBox.setFitFunctions)
        dockwidget.fit_pushButton.clicked.disconnect()

        dockwidget.x_lower_limit_DoubleSpinBox.valueChanged.disconnect()
        dockwidget.x_upper_limit_DoubleSpinBox.valueChanged.disconnect()
        dockwidget.y_lower_limit_DoubleSpinBox.valueChanged.disconnect()
        dockwidget.y_upper_limit_DoubleSpinBox.valueChanged.disconnect()

        dockwidget.x_label_lineEdit.editingFinished.disconnect()
        dockwidget.x_unit_lineEdit.editingFinished.disconnect()
        dockwidget.y_label_lineEdit.editingFinished.disconnect()
        dockwidget.y_unit_lineEdit.editingFinished.disconnect()

        dockwidget.x_auto_PushButton.clicked.disconnect()
        dockwidget.y_auto_PushButton.clicked.disconnect()
        dockwidget.save_pushButton.clicked.disconnect()
        dockwidget.remove_pushButton.clicked.disconnect()
        for sig_proxy in self._pg_signal_proxys[index]:
            sig_proxy.sigDelayed.disconnect()
            sig_proxy.disconnect()

    @property
    def pen_color_list(self):
        return self._pen_color_list.copy()

    @pen_color_list.setter
    def pen_color_list(self, value):
        if not isinstance(value, (list, tuple)) or len(value) < 1:
            self.log.warning(
                'The parameter pen_color_list needs to be a list of strings but was "{0}".'
                ' Will use the following old pen colors: {1}.'
                ''.format(value, self._pen_color_list))
            return
        for index, color in enumerate(self._pen_color_list):
            if (isinstance(color, (list, tuple)) and len(color) == 3) or \
                    (isinstance(color, str) and color in self._allowed_colors):
                pass
            else:
                self.log.warning(
                    'The color was "{0}" but needs to be from this list: {1} '
                    'or a 3 element tuple with values from 0 to 255 for RGB.'
                    ''.format(color, self._allowed_colors))
                return
        else:
            self._pen_color_list = list(value)

    def restore_side_by_side_view(self):
        """ Restore the arrangement of DockWidgets to the default """
        self.restore_view(alignment='side_by_side')

    def restore_arc_view(self):
        """ Restore the arrangement of DockWidgets to the default """
        self.restore_view(alignment='arc')

    def restore_tabbed_view(self):
        """ Restore the arrangement of DockWidgets to the default """
        self.restore_view(alignment='tabbed')

    @QtCore.Slot()
    def restore_view(self, alignment=None):
        """ Restore the arrangement of DockWidgets to the default """

        if alignment is None:
            alignment = self.widget_alignment
        if alignment not in ('side_by_side', 'arc', 'tabbed'):
            alignment = 'tabbed'
        self.widget_alignment = alignment

        self._mw.setDockNestingEnabled(True)
        self._mw.centralwidget.setVisible(False)

        for i, dockwidget in enumerate(self._plot_dockwidgets):
            dockwidget.show()
            dockwidget.setFloating(False)
            dockwidget.widget().show_fit_checkBox.setChecked(False)
            dockwidget.widget().show_controls_checkBox.setChecked(False)
            if alignment == 'tabbed':
                self._mw.addDockWidget(QtCore.Qt.TopDockWidgetArea, dockwidget)
                if i > 0:
                    self._mw.tabifyDockWidget(self._plot_dockwidgets[0],
                                              dockwidget)
            elif alignment == 'arc':
                mod = i % 3
                if mod == 0:
                    self._mw.addDockWidget(QtCore.Qt.TopDockWidgetArea,
                                           dockwidget)
                    if i > 2:
                        self._mw.tabifyDockWidget(self._plot_dockwidgets[0],
                                                  dockwidget)
                elif mod == 1:
                    self._mw.addDockWidget(QtCore.Qt.BottomDockWidgetArea,
                                           dockwidget)
                    if i > 2:
                        self._mw.tabifyDockWidget(self._plot_dockwidgets[1],
                                                  dockwidget)
                elif mod == 2:
                    self._mw.addDockWidget(QtCore.Qt.BottomDockWidgetArea,
                                           dockwidget)
                    if i > 2:
                        self._mw.tabifyDockWidget(self._plot_dockwidgets[2],
                                                  dockwidget)
            elif alignment == 'side_by_side':
                self._mw.addDockWidget(QtCore.Qt.TopDockWidgetArea, dockwidget)
        if alignment == 'arc':
            if len(self._plot_dockwidgets) > 2:
                self._mw.resizeDocks(
                    [self._plot_dockwidgets[1], self._plot_dockwidgets[2]],
                    [1, 1], QtCore.Qt.Horizontal)
        elif alignment == 'side_by_side':
            self._mw.resizeDocks(self._plot_dockwidgets,
                                 [1] * len(self._plot_dockwidgets),
                                 QtCore.Qt.Horizontal)

    @QtCore.Slot(int, list, list, bool)
    def update_data(self,
                    plot_index,
                    x_data=None,
                    y_data=None,
                    clear_old=None):
        """ Function creates empty plots, grabs the data and sends it to them. """
        if not (0 <= plot_index < len(self._plot_dockwidgets)):
            self.log.warning(
                'Tried to update plot with invalid index {0:d}'.format(
                    plot_index))
            return

        if x_data is None:
            x_data = self._plot_logic.get_x_data(plot_index)
        if y_data is None:
            y_data = self._plot_logic.get_y_data(plot_index)
        if clear_old is None:
            clear_old = self._plot_logic.clear_old_data(plot_index)

        dockwidget = self._plot_dockwidgets[plot_index].widget()
        if clear_old:
            dockwidget.plot_PlotWidget.clear()
            self._pen_colors[plot_index] = cycle(self._pen_color_list)

        self._plot_curves[plot_index] = list()
        self._fit_curves[plot_index] = list()

        for line, xd in enumerate(x_data):
            yd = y_data[line]
            pen_color = next(self._pen_colors[plot_index])
            self._plot_curves[plot_index].append(
                dockwidget.plot_PlotWidget.plot(
                    pen=mkColor(pen_color),
                    symbol='d',
                    symbolSize=6,
                    symbolBrush=mkColor(pen_color)))
            self._plot_curves[plot_index][-1].setData(x=xd, y=yd)
            self._fit_curves[plot_index].append(
                dockwidget.plot_PlotWidget.plot())
            self._fit_curves[plot_index][-1].setPen('r')

    @QtCore.Slot(int)
    @QtCore.Slot(int, dict)
    def update_plot_parameters(self, plot_index, params=None):
        """ Function updated limits, labels and units in the plot and parameter widgets. """
        if not (0 <= plot_index < len(self._plot_dockwidgets)):
            self.log.warning(
                'Tried to update plot with invalid index {0:d}'.format(
                    plot_index))
            return

        dockwidget = self._plot_dockwidgets[plot_index].widget()
        if params is None:
            params = dict()
            params['x_label'] = self._plot_logic.get_x_label(plot_index)
            params['y_label'] = self._plot_logic.get_y_label(plot_index)
            params['x_unit'] = self._plot_logic.get_x_unit(plot_index)
            params['y_unit'] = self._plot_logic.get_y_unit(plot_index)
            params['x_limits'] = self._plot_logic.get_x_limits(plot_index)
            params['y_limits'] = self._plot_logic.get_y_limits(plot_index)

        if 'x_label' in params or 'x_unit' in params:
            label = params.get('x_label', None)
            unit = params.get('x_unit', None)
            if label is None:
                label = self._plot_logic.get_x_label(plot_index)
            if unit is None:
                unit = self._plot_logic.get_x_unit(plot_index)
            dockwidget.plot_PlotWidget.setLabel('bottom', label, units=unit)
            dockwidget.x_label_lineEdit.blockSignals(True)
            dockwidget.x_unit_lineEdit.blockSignals(True)
            dockwidget.x_label_lineEdit.setText(label)
            dockwidget.x_unit_lineEdit.setText(unit)
            dockwidget.x_label_lineEdit.blockSignals(False)
            dockwidget.x_unit_lineEdit.blockSignals(False)
        if 'y_label' in params or 'y_unit' in params:
            label = params.get('y_label', None)
            unit = params.get('y_unit', None)
            if label is None:
                label = self._plot_logic.get_y_label(plot_index)
            if unit is None:
                unit = self._plot_logic.get_y_unit(plot_index)
            dockwidget.plot_PlotWidget.setLabel('left', label, units=unit)
            dockwidget.y_label_lineEdit.blockSignals(True)
            dockwidget.y_unit_lineEdit.blockSignals(True)
            dockwidget.y_label_lineEdit.setText(label)
            dockwidget.y_unit_lineEdit.setText(unit)
            dockwidget.y_label_lineEdit.blockSignals(False)
            dockwidget.y_unit_lineEdit.blockSignals(False)
        if 'x_limits' in params:
            limits = params['x_limits']
            self._pg_signal_proxys[plot_index][0].block = True
            dockwidget.plot_PlotWidget.setXRange(*limits, padding=0)
            self._pg_signal_proxys[plot_index][0].block = False
            dockwidget.x_lower_limit_DoubleSpinBox.blockSignals(True)
            dockwidget.x_upper_limit_DoubleSpinBox.blockSignals(True)
            dockwidget.x_lower_limit_DoubleSpinBox.setValue(limits[0])
            dockwidget.x_upper_limit_DoubleSpinBox.setValue(limits[1])
            dockwidget.x_lower_limit_DoubleSpinBox.blockSignals(False)
            dockwidget.x_upper_limit_DoubleSpinBox.blockSignals(False)
        if 'y_limits' in params:
            limits = params['y_limits']
            self._pg_signal_proxys[plot_index][1].block = True
            dockwidget.plot_PlotWidget.setYRange(*limits, padding=0)
            self._pg_signal_proxys[plot_index][1].block = False
            dockwidget.y_lower_limit_DoubleSpinBox.blockSignals(True)
            dockwidget.y_upper_limit_DoubleSpinBox.blockSignals(True)
            dockwidget.y_lower_limit_DoubleSpinBox.setValue(limits[0])
            dockwidget.y_upper_limit_DoubleSpinBox.setValue(limits[1])
            dockwidget.y_lower_limit_DoubleSpinBox.blockSignals(False)
            dockwidget.y_upper_limit_DoubleSpinBox.blockSignals(False)

    def save_clicked(self, plot_index):
        """ Handling the save button to save the data into a file. """
        self._flush_pg_proxy(plot_index)
        self._plot_logic.save_data(plot_index=plot_index)

    def save_all_clicked(self):
        """ Handling the save button to save the data into a file. """
        for plot_index, _ in enumerate(self._plot_dockwidgets):
            self.save_clicked(plot_index)

    def remove_clicked(self, plot_index):
        self._flush_pg_proxy(plot_index)
        self.sigRemovePlotClicked.emit(plot_index)

    def x_auto_range_clicked(self, plot_index):
        """ Set the parameter_1_x_limits to the min/max of the data values """
        self.sigAutoRangeClicked.emit(plot_index, True, False)

    def y_auto_range_clicked(self, plot_index):
        """ Set the parameter_1_y_limits to the min/max of the data values """
        self.sigAutoRangeClicked.emit(plot_index, False, True)

    def x_limits_changed(self, plot_index):
        """ Handling the change of the parameter_1_x_limits. """
        dockwidget = self._plot_dockwidgets[plot_index].widget()
        self.sigPlotParametersChanged.emit(
            plot_index, {
                'x_limits': [
                    dockwidget.x_lower_limit_DoubleSpinBox.value(),
                    dockwidget.x_upper_limit_DoubleSpinBox.value()
                ]
            })

    def y_limits_changed(self, plot_index):
        """ Handling the change of the parameter_1_y_limits. """
        dockwidget = self._plot_dockwidgets[plot_index].widget()
        self.sigPlotParametersChanged.emit(
            plot_index, {
                'y_limits': [
                    dockwidget.y_lower_limit_DoubleSpinBox.value(),
                    dockwidget.y_upper_limit_DoubleSpinBox.value()
                ]
            })

    def x_label_changed(self, plot_index):
        """ Set the x-label """
        dockwidget = self._plot_dockwidgets[plot_index].widget()
        self.sigPlotParametersChanged.emit(
            plot_index, {'x_label': dockwidget.x_label_lineEdit.text()})

    def y_label_changed(self, plot_index):
        """ Set the y-label and the uni of plot 1 """
        dockwidget = self._plot_dockwidgets[plot_index].widget()
        self.sigPlotParametersChanged.emit(
            plot_index, {'y_label': dockwidget.y_label_lineEdit.text()})

    def x_unit_changed(self, plot_index):
        """ Set the x-label """
        dockwidget = self._plot_dockwidgets[plot_index].widget()
        self.sigPlotParametersChanged.emit(
            plot_index, {'x_unit': dockwidget.x_unit_lineEdit.text()})

    def y_unit_changed(self, plot_index):
        """ Set the y-label and the uni of plot 1 """
        dockwidget = self._plot_dockwidgets[plot_index].widget()
        self.sigPlotParametersChanged.emit(
            plot_index, {'y_unit': dockwidget.y_unit_lineEdit.text()})

    def fit_clicked(self, plot_index=0):
        """ Triggers the fit to be done. Attention, this runs in the GUI thread. """
        current_fit_method = self._plot_dockwidgets[plot_index].widget(
        ).fit_comboBox.getCurrentFit()[0]
        self.sigDoFit.emit(current_fit_method, plot_index)

    @QtCore.Slot(int, np.ndarray, str, str)
    def update_fit_data(self,
                        plot_index,
                        fit_data=None,
                        formatted_fitresult=None,
                        fit_method=None):
        """ Function that handles the fit results received from the logic via a signal.

        @param int plot_index: index of the plot the fit was performed for in the range for 0 to 2
        @param 3-dimensional np.ndarray fit_data: the fit data in a 2-d array for each data set
        @param str formatted_fitresult: string containing the parameters already formatted
        @param str fit_method: the fit_method used
        """
        dockwidget = self._plot_dockwidgets[plot_index].widget()

        if fit_data is None or formatted_fitresult is None or fit_method is None:
            fit_data, formatted_fitresult, fit_method = self._plot_logic.get_fit_data(
                plot_index)

        if not fit_method:
            fit_method = 'No Fit'

        dockwidget.fit_comboBox.blockSignals(True)

        dockwidget.show_fit_checkBox.setChecked(True)
        dockwidget.fit_textBrowser.clear()
        dockwidget.fit_comboBox.setCurrentFit(fit_method)
        if fit_method == 'No Fit':
            for index, curve in enumerate(self._fit_curves[plot_index]):
                if curve in dockwidget.plot_PlotWidget.items():
                    dockwidget.plot_PlotWidget.removeItem(curve)
        else:
            dockwidget.fit_textBrowser.setPlainText(formatted_fitresult)
            for index, curve in enumerate(self._fit_curves[plot_index]):
                if curve not in dockwidget.plot_PlotWidget.items():
                    dockwidget.plot_PlotWidget.addItem(curve)
                curve.setData(x=fit_data[index][0], y=fit_data[index][1])

        dockwidget.fit_comboBox.blockSignals(False)

    def _pyqtgraph_x_limits_changed(self, plot_index, limits):
        plot_item = self._plot_dockwidgets[plot_index].widget(
        ).plot_PlotWidget.getPlotItem()
        if plot_item.ctrl.logXCheck.isChecked(
        ) or plot_item.ctrl.fftCheck.isChecked():
            return
        self.sigPlotParametersChanged.emit(plot_index, {'x_limits': limits})

    def _pyqtgraph_y_limits_changed(self, plot_index, limits):
        plot_item = self._plot_dockwidgets[plot_index].widget(
        ).plot_PlotWidget.getPlotItem()
        if plot_item.ctrl.logYCheck.isChecked(
        ) or plot_item.ctrl.fftCheck.isChecked():
            return
        self.sigPlotParametersChanged.emit(plot_index, {'y_limits': limits})

    def _flush_pg_proxy(self, plot_index):
        x_proxy, y_proxy = self._pg_signal_proxys[plot_index]
        x_proxy.flush()
        y_proxy.flush()
Example #2
0
class WavemeterLogGui(GUIBase):
    _modclass = 'WavemeterLogGui'
    _modtype = 'gui'

    ## declare connectors
    wavemeterloggerlogic1 = Connector(interface='WavemeterLoggerLogic')
    savelogic = Connector(interface='SaveLogic')

    sigStartCounter = QtCore.Signal()
    sigStopCounter = QtCore.Signal()
    sigFitChanged = QtCore.Signal(str)
    sigDoFit = QtCore.Signal()

    def __init__(self, config, **kwargs):
        super().__init__(config=config, **kwargs)

        self.log.debug('The following configuration was found.')

        # checking for the right configuration
        for key in config.keys():
            self.log.info('{0}: {1}'.format(key, config[key]))

    def on_activate(self):
        """ Definition and initialisation of the GUI.
        """

        self._wm_logger_logic = self.get_connector('wavemeterloggerlogic1')
        self._save_logic = self.get_connector('savelogic')

        # setting up the window
        self._mw = WavemeterLogWindow()

        ## giving the plots names allows us to link their axes together
        self._pw = self._mw.plotWidget  # pg.PlotWidget(name='Counter1')
        self._plot_item = self._pw.plotItem

        ## create a new ViewBox, link the right axis to its coordinate system
        self._right_axis = pg.ViewBox()
        self._plot_item.showAxis('right')
        self._plot_item.scene().addItem(self._right_axis)
        self._plot_item.getAxis('right').linkToView(self._right_axis)
        self._right_axis.setXLink(self._plot_item)

        ## create a new ViewBox, link the right axis to its coordinate system
        self._top_axis = pg.ViewBox()
        self._plot_item.showAxis('top')
        self._plot_item.scene().addItem(self._top_axis)
        self._plot_item.getAxis('top').linkToView(self._top_axis)
        self._top_axis.setYLink(self._plot_item)
        self._top_axis.invertX(b=True)

        # handle resizing of any of the elements
        self._update_plot_views()
        self._plot_item.vb.sigResized.connect(self._update_plot_views)

        self._pw.setLabel('left', 'Fluorescence', units='counts/s')
        self._pw.setLabel('right', 'Number of Points', units='#')
        self._pw.setLabel('bottom', 'Wavelength', units='nm')
        self._pw.setLabel('top', 'Relative Frequency', units='Hz')

        self._mw.actionStop_resume_scan.triggered.connect(
            self.stop_resume_clicked)
        self._mw.actionSave_histogram.triggered.connect(self.save_clicked)
        self._mw.actionStart_scan.triggered.connect(self.start_clicked)
        self._mw.actionAuto_range.triggered.connect(self.set_auto_range)

        # defining the parameters to edit
        self._mw.binSpinBox.setValue(self._wm_logger_logic.get_bins())
        self._mw.binSpinBox.editingFinished.connect(self.recalculate_histogram)

        self._mw.minDoubleSpinBox.setValue(
            self._wm_logger_logic.get_min_wavelength())
        self._mw.minDoubleSpinBox.editingFinished.connect(
            self.recalculate_histogram)

        self._mw.maxDoubleSpinBox.setValue(
            self._wm_logger_logic.get_max_wavelength())
        self._mw.maxDoubleSpinBox.editingFinished.connect(
            self.recalculate_histogram)

        self._mw.show()

        ## Create an empty plot curve to be filled later, set its pen
        self.curve_data_points = pg.PlotDataItem(pen=pg.mkPen(palette.c1),
                                                 symbol=None)

        self.curve_nm_counts = pg.PlotDataItem(pen=pg.mkPen(
            palette.c2, style=QtCore.Qt.DotLine),
                                               symbol=None)

        self.curve_hz_counts = pg.PlotDataItem(pen=pg.mkPen(
            palette.c6, style=QtCore.Qt.DotLine),
                                               symbol=None)

        self.curve_envelope = pg.PlotDataItem(pen=pg.mkPen(
            palette.c3, style=QtCore.Qt.DotLine),
                                              symbol=None)

        self.curve_fit = pg.PlotDataItem(pen=pg.mkPen(palette.c2, width=3),
                                         symbol=None)

        self._pw.addItem(self.curve_data_points)
        self._pw.addItem(self.curve_envelope)
        self._right_axis.addItem(self.curve_nm_counts)
        self._top_axis.addItem(self.curve_hz_counts)

        # scatter plot for time series
        self._spw = self._mw.scatterPlotWidget
        self._spi = self._spw.plotItem
        self._spw.setLabel('bottom', 'Wavelength', units='nm')
        self._spw.setLabel('left', 'Time', units='s')
        self._scatterplot = pg.ScatterPlotItem(size=10,
                                               pen=pg.mkPen(None),
                                               brush=pg.mkBrush(
                                                   255, 255, 255, 20))
        self._spw.addItem(self._scatterplot)
        self._spw.setXLink(self._plot_item)
        self._wm_logger_logic.sig_new_data_point.connect(self.add_data_point)

        self._wm_logger_logic.sig_data_updated.connect(self.updateData)

        # fit settings
        self._fsd = FitSettingsDialog(self._wm_logger_logic.fc)
        self._fsd.sigFitsUpdated.connect(
            self._mw.fit_methods_ComboBox.setFitFunctions)
        self._fsd.applySettings()

        self._mw.actionFit_settings.triggered.connect(self._fsd.show)
        self._mw.do_fit_PushButton.clicked.connect(self.doFit)
        self.sigDoFit.connect(self._wm_logger_logic.do_fit)
        self.sigFitChanged.connect(self._wm_logger_logic.fc.set_current_fit)
        self._wm_logger_logic.sig_fit_updated.connect(self.updateFit)

    def on_deactivate(self):
        """ Deactivate the module properly.
        """
        self._mw.close()

    def show(self):
        """ Make window visible and put it above all other windows.
        """
        QtWidgets.QMainWindow.show(self._mw)
        self._mw.activateWindow()
        self._mw.raise_()

    def updateData(self):
        """ The function that grabs the data and sends it to the plot.
        """
        self._mw.wavelengthLabel.setText('{0:,.6f} nm '.format(
            self._wm_logger_logic.current_wavelength))
        self._mw.autoMinLabel.setText('Minimum: {0:3.6f} (nm)   '.format(
            self._wm_logger_logic.intern_xmin))
        self._mw.autoMaxLabel.setText('Maximum: {0:3.6f} (nm)   '.format(
            self._wm_logger_logic.intern_xmax))

        x_axis = self._wm_logger_logic.histogram_axis
        x_axis_hz = (3.0e17 / (x_axis) - 6.0e17 /
                     (self._wm_logger_logic.get_max_wavelength() +
                      self._wm_logger_logic.get_min_wavelength()))

        plotdata = np.array(self._wm_logger_logic.counts_with_wavelength)
        if len(plotdata.shape) > 1 and plotdata.shape[1] == 3:
            self.curve_data_points.setData(plotdata[:, 2:0:-1])

        self.curve_nm_counts.setData(x=x_axis,
                                     y=self._wm_logger_logic.histogram)
        self.curve_hz_counts.setData(x=x_axis_hz,
                                     y=self._wm_logger_logic.histogram)
        self.curve_envelope.setData(x=x_axis,
                                    y=self._wm_logger_logic.envelope_histogram)

    @QtCore.Slot()
    def doFit(self):
        self.sigFitChanged.emit(
            self._mw.fit_methods_ComboBox.getCurrentFit()[0])
        self.sigDoFit.emit()

    @QtCore.Slot()
    def updateFit(self):
        """ Do the configured fit and show it in the plot """
        fit_name = self._wm_logger_logic.fc.current_fit
        fit_result = self._wm_logger_logic.fc.current_fit_result
        fit_param = self._wm_logger_logic.fc.current_fit_param

        if fit_result is not None:
            # display results as formatted text
            self._mw.fit_results_DisplayWidget.clear()
            try:
                formated_results = units.create_formatted_output(
                    fit_result.result_str_dict)
            except:
                formated_results = 'this fit does not return formatted results'
            self._mw.fit_results_DisplayWidget.setPlainText(formated_results)

        if fit_name is not None:
            self._mw.fit_methods_ComboBox.setCurrentFit(fit_name)

        # check which fit method is used and show the curve in the plot accordingly
        if fit_name != 'No Fit':
            self.curve_fit.setData(x=self._wm_logger_logic.wlog_fit_x,
                                   y=self._wm_logger_logic.wlog_fit_y)

            if self.curve_fit not in self._mw.plotWidget.listDataItems():
                self._mw.plotWidget.addItem(self.curve_fit)
        else:
            if self.curve_fit in self._mw.plotWidget.listDataItems():
                self._mw.plotWidget.removeItem(self.curve_fit)

    def add_data_point(self, point):
        if len(point) >= 3:
            spts = [{
                'pos': (point[0], point[1]),
                'size': 5,
                'brush': pg.intColor(point[2] / 100, 255)
            }]
            self._scatterplot.addPoints(spts)

    def stop_resume_clicked(self):
        """ Handling the Start button to stop and restart the counter.
        """
        # If running, then we stop the measurement and enable inputs again
        if self._wm_logger_logic.module_state() == 'running':
            self._mw.actionStop_resume_scan.setText('Resume')
            self._wm_logger_logic.stop_scanning()
            self._mw.actionStop_resume_scan.setEnabled(True)
            self._mw.actionStart_scan.setEnabled(True)
            self._mw.binSpinBox.setEnabled(True)
        # Otherwise, we start a measurement and disable some inputs.
        else:
            self._mw.actionStop_resume_scan.setText('Stop')
            self._wm_logger_logic.start_scanning(resume=True)
            self._mw.actionStart_scan.setEnabled(False)
            self._mw.binSpinBox.setEnabled(False)

    def start_clicked(self):
        """ Handling resume of the scanning without resetting the data.
        """
        if self._wm_logger_logic.module_state() == 'idle':
            self._scatterplot.clear()
            self._wm_logger_logic.start_scanning()

            # Enable the stop button once a scan starts.
            self._mw.actionStop_resume_scan.setText('Stop')
            self._mw.actionStop_resume_scan.setEnabled(True)
            self._mw.actionStart_scan.setEnabled(False)
            self._mw.binSpinBox.setEnabled(False)
            self.recalculate_histogram()
        else:
            self.log.error('Cannot scan, since a scan is alredy running.')

    def save_clicked(self):
        """ Handling the save button to save the data into a file.
        """

        timestamp = datetime.datetime.now()
        filepath = self._save_logic.get_path_for_module(
            module_name='WavemeterLogger')
        filename = os.path.join(
            filepath,
            timestamp.strftime('%Y%m%d-%H%M-%S_wavemeter_log_thumbnail'))

        exporter = pg.exporters.SVGExporter(self._pw.plotItem)
        exporter.export(filename + '.svg')

        self._wm_logger_logic.save_data(timestamp=timestamp)

    def recalculate_histogram(self):
        self._wm_logger_logic.recalculate_histogram(
            bins=self._mw.binSpinBox.value(),
            xmin=self._mw.minDoubleSpinBox.value(),
            xmax=self._mw.maxDoubleSpinBox.value())

    def set_auto_range(self):
        self._mw.minDoubleSpinBox.setValue(self._wm_logger_logic.intern_xmin)
        self._mw.maxDoubleSpinBox.setValue(self._wm_logger_logic.intern_xmax)
        self.recalculate_histogram()

    ## Handle view resizing
    def _update_plot_views(self):
        ## view has resized; update auxiliary views to match
        self._right_axis.setGeometry(self._plot_item.vb.sceneBoundingRect())
        self._top_axis.setGeometry(self._plot_item.vb.sceneBoundingRect())

        ## need to re-update linked axes since this was called
        ## incorrectly while views had different shapes.
        ## (probably this should be handled in ViewBox.resizeEvent)
        self._right_axis.linkedViewChanged(self._plot_item.vb,
                                           self._right_axis.XAxis)
        self._top_axis.linkedViewChanged(self._plot_item.vb,
                                         self._top_axis.YAxis)
Example #3
0
class ODMRGui(GUIBase):
    """
    This is the GUI Class for ODMR measurements
    """

    _modclass = 'ODMRGui'
    _modtype = 'gui'

    # declare connectors
    _connectors = {'odmrlogic1': 'ODMRLogic',
           'savelogic': 'SaveLogic'}

    sigStartOdmrScan = QtCore.Signal()
    sigStopOdmrScan = QtCore.Signal()
    sigContinueOdmrScan = QtCore.Signal()
    sigClearData = QtCore.Signal()
    sigCwMwOn = QtCore.Signal()
    sigMwOff = QtCore.Signal()
    sigMwPowerChanged = QtCore.Signal(float)
    sigMwCwParamsChanged = QtCore.Signal(float, float)
    sigMwSweepParamsChanged = QtCore.Signal(float, float, float, float)
    sigClockFreqChanged = QtCore.Signal(float)
    sigFitChanged = QtCore.Signal(str)
    sigNumberOfLinesChanged = QtCore.Signal(int)
    sigRuntimeChanged = QtCore.Signal(float)
    sigDoFit = QtCore.Signal(str)
    sigSaveMeasurement = QtCore.Signal(str, list, list)

    def __init__(self, config, **kwargs):
        super().__init__(config=config, **kwargs)

        self.log.info('The following configuration was found.')

        # checking for the right configuration
        for key in config.keys():
            self.log.info('{0}: {1}'.format(key, config[key]))

    def on_activate(self):
        """ Definition, configuration and initialisation of the ODMR GUI.

        This init connects all the graphic modules, which were created in the
        *.ui file and configures the event handling between the modules.
        """

        self._odmr_logic = self.get_connector('odmrlogic1')

        # Use the inherited class 'Ui_ODMRGuiUI' to create now the GUI element:
        self._mw = ODMRMainWindow()
        self._sd = ODMRSettingDialog()

        # Create a QSettings object for the mainwindow and store the actual GUI layout
        self.mwsettings = QtCore.QSettings("QUDI", "ODMR")
        self.mwsettings.setValue("geometry", self._mw.saveGeometry())
        self.mwsettings.setValue("windowState", self._mw.saveState())

        # Get hardware constraints to set limits for input widgets
        constraints = self._odmr_logic.get_hw_constraints()

        # Adjust range of scientific spinboxes above what is possible in Qt Designer
        self._mw.cw_frequency_DoubleSpinBox.setMaximum(constraints.max_frequency)
        self._mw.cw_frequency_DoubleSpinBox.setMinimum(constraints.min_frequency)
        self._mw.start_freq_DoubleSpinBox.setMaximum(constraints.max_frequency)
        self._mw.start_freq_DoubleSpinBox.setMinimum(constraints.min_frequency)
        self._mw.step_freq_DoubleSpinBox.setMaximum(100e9)
        self._mw.step_freq_DoubleSpinBox.setOpts(minStep=1.0)  # set the minimal step to 1Hz
        self._mw.stop_freq_DoubleSpinBox.setMaximum(constraints.max_frequency)
        self._mw.stop_freq_DoubleSpinBox.setMinimum(constraints.min_frequency)
        self._mw.cw_power_DoubleSpinBox.setMaximum(constraints.max_power)
        self._mw.cw_power_DoubleSpinBox.setMinimum(constraints.min_power)
        self._mw.cw_power_DoubleSpinBox.setOpts(minStep=0.1)
        self._mw.sweep_power_DoubleSpinBox.setMaximum(constraints.max_power)
        self._mw.sweep_power_DoubleSpinBox.setMinimum(constraints.min_power)
        self._mw.sweep_power_DoubleSpinBox.setOpts(minStep=0.1)

        # Add save file tag input box
        self._mw.save_tag_LineEdit = QtWidgets.QLineEdit(self._mw)
        self._mw.save_tag_LineEdit.setMaximumWidth(500)
        self._mw.save_tag_LineEdit.setMinimumWidth(200)
        self._mw.save_tag_LineEdit.setToolTip('Enter a nametag which will be\n'
                                              'added to the filename.')
        self._mw.save_ToolBar.addWidget(self._mw.save_tag_LineEdit)

        # add a clear button to clear the ODMR plots:
        self._mw.clear_odmr_PushButton = QtWidgets.QPushButton(self._mw)
        self._mw.clear_odmr_PushButton.setText('Clear ODMR')
        self._mw.clear_odmr_PushButton.setToolTip('Clear the data of the\n'
                                                  'current ODMR measurements.')
        self._mw.clear_odmr_PushButton.setEnabled(False)
        self._mw.toolBar.addWidget(self._mw.clear_odmr_PushButton)

        # Get the image from the logic
        self.odmr_matrix_image = pg.ImageItem(self._odmr_logic.odmr_plot_xy.transpose())
        self.odmr_matrix_image.setRect(QtCore.QRectF(
                self._odmr_logic.mw_start,
                0,
                self._odmr_logic.mw_stop - self._odmr_logic.mw_start,
                self._odmr_logic.number_of_lines
            ))

        self.odmr_image = pg.PlotDataItem(self._odmr_logic.odmr_plot_x,
                                          self._odmr_logic.odmr_plot_y,
                                          pen=pg.mkPen(palette.c1, style=QtCore.Qt.DotLine),
                                          symbol='o',
                                          symbolPen=palette.c1,
                                          symbolBrush=palette.c1,
                                          symbolSize=7)

        self.odmr_fit_image = pg.PlotDataItem(self._odmr_logic.odmr_fit_x,
                                              self._odmr_logic.odmr_fit_y,
                                              pen=pg.mkPen(palette.c2))

        # Add the display item to the xy and xz ViewWidget, which was defined in the UI file.
        self._mw.odmr_PlotWidget.addItem(self.odmr_image)
        self._mw.odmr_PlotWidget.setLabel(axis='left', text='Counts', units='Counts/s')
        self._mw.odmr_PlotWidget.setLabel(axis='bottom', text='Frequency', units='Hz')
        self._mw.odmr_PlotWidget.showGrid(x=True, y=True, alpha=0.8)

        self._mw.odmr_matrix_PlotWidget.addItem(self.odmr_matrix_image)
        self._mw.odmr_matrix_PlotWidget.setLabel(axis='left', text='Matrix Lines', units='#')
        self._mw.odmr_matrix_PlotWidget.setLabel(axis='bottom', text='Frequency', units='Hz')

        # Get the colorscales at set LUT
        my_colors = ColorScaleInferno()
        self.odmr_matrix_image.setLookupTable(my_colors.lut)

        ########################################################################
        #                  Configuration of the Colorbar                       #
        ########################################################################
        self.odmr_cb = ColorBar(my_colors.cmap_normed, 100, 0, 100000)

        # adding colorbar to ViewWidget
        self._mw.odmr_cb_PlotWidget.addItem(self.odmr_cb)
        self._mw.odmr_cb_PlotWidget.hideAxis('bottom')
        self._mw.odmr_cb_PlotWidget.hideAxis('left')
        self._mw.odmr_cb_PlotWidget.setLabel('right', 'Fluorescence', units='counts/s')

        ########################################################################
        #          Configuration of the various display Widgets                #
        ########################################################################
        # Take the default values from logic:
        self._mw.cw_frequency_DoubleSpinBox.setValue(self._odmr_logic.cw_mw_frequency)
        self._mw.start_freq_DoubleSpinBox.setValue(self._odmr_logic.mw_start)
        self._mw.stop_freq_DoubleSpinBox.setValue(self._odmr_logic.mw_stop)
        self._mw.step_freq_DoubleSpinBox.setValue(self._odmr_logic.mw_step)
        self._mw.cw_power_DoubleSpinBox.setValue(self._odmr_logic.cw_mw_power)
        self._mw.sweep_power_DoubleSpinBox.setValue(self._odmr_logic.sweep_mw_power)

        self._mw.runtime_DoubleSpinBox.setValue(self._odmr_logic.run_time)
        self._mw.elapsed_time_DisplayWidget.display(int(np.rint(self._odmr_logic.elapsed_time)))
        self._mw.elapsed_sweeps_DisplayWidget.display(self._odmr_logic.elapsed_sweeps)

        self._sd.matrix_lines_SpinBox.setValue(self._odmr_logic.number_of_lines)
        self._sd.clock_frequency_DoubleSpinBox.setValue(self._odmr_logic.clock_frequency)

        # fit settings
        self._fsd = FitSettingsDialog(self._odmr_logic.fc)
        self._fsd.sigFitsUpdated.connect(self._mw.fit_methods_ComboBox.setFitFunctions)
        self._fsd.applySettings()
        self._mw.action_FitSettings.triggered.connect(self._fsd.show)

        ########################################################################
        #                       Connect signals                                #
        ########################################################################
        # Internal user input changed signals
        self._mw.cw_frequency_DoubleSpinBox.editingFinished.connect(self.change_cw_params)
        self._mw.start_freq_DoubleSpinBox.editingFinished.connect(self.change_sweep_params)
        self._mw.step_freq_DoubleSpinBox.editingFinished.connect(self.change_sweep_params)
        self._mw.stop_freq_DoubleSpinBox.editingFinished.connect(self.change_sweep_params)
        self._mw.sweep_power_DoubleSpinBox.editingFinished.connect(self.change_sweep_params)
        self._mw.cw_power_DoubleSpinBox.editingFinished.connect(self.change_cw_params)
        self._mw.runtime_DoubleSpinBox.editingFinished.connect(self.change_runtime)
        self._mw.odmr_cb_max_DoubleSpinBox.valueChanged.connect(self.colorscale_changed)
        self._mw.odmr_cb_min_DoubleSpinBox.valueChanged.connect(self.colorscale_changed)
        self._mw.odmr_cb_high_percentile_DoubleSpinBox.valueChanged.connect(self.colorscale_changed)
        self._mw.odmr_cb_low_percentile_DoubleSpinBox.valueChanged.connect(self.colorscale_changed)
        # Internal trigger signals
        self._mw.odmr_cb_manual_RadioButton.clicked.connect(self.colorscale_changed)
        self._mw.odmr_cb_centiles_RadioButton.clicked.connect(self.colorscale_changed)
        self._mw.clear_odmr_PushButton.clicked.connect(self.clear_odmr_data)
        self._mw.action_run_stop.triggered.connect(self.run_stop_odmr)
        self._mw.action_resume_odmr.triggered.connect(self.resume_odmr)
        self._mw.action_toggle_cw.triggered.connect(self.toggle_cw_mode)
        self._mw.action_Save.triggered.connect(self.save_data)
        self._mw.action_RestoreDefault.triggered.connect(self.restore_defaultview)
        self._mw.do_fit_PushButton.clicked.connect(self.do_fit)

        # Control/values-changed signals to logic
        self.sigCwMwOn.connect(self._odmr_logic.mw_cw_on, QtCore.Qt.QueuedConnection)
        self.sigMwOff.connect(self._odmr_logic.mw_off, QtCore.Qt.QueuedConnection)
        self.sigClearData.connect(self._odmr_logic.clear_odmr_data, QtCore.Qt.QueuedConnection)
        self.sigStartOdmrScan.connect(self._odmr_logic.start_odmr_scan, QtCore.Qt.QueuedConnection)
        self.sigStopOdmrScan.connect(self._odmr_logic.stop_odmr_scan, QtCore.Qt.QueuedConnection)
        self.sigContinueOdmrScan.connect(self._odmr_logic.continue_odmr_scan,
                                         QtCore.Qt.QueuedConnection)
        self.sigDoFit.connect(self._odmr_logic.do_fit, QtCore.Qt.QueuedConnection)
        self.sigMwCwParamsChanged.connect(self._odmr_logic.set_cw_parameters,
                                          QtCore.Qt.QueuedConnection)
        self.sigMwSweepParamsChanged.connect(self._odmr_logic.set_sweep_parameters,
                                             QtCore.Qt.QueuedConnection)
        self.sigRuntimeChanged.connect(self._odmr_logic.set_runtime, QtCore.Qt.QueuedConnection)
        self.sigNumberOfLinesChanged.connect(self._odmr_logic.set_matrix_line_number,
                                             QtCore.Qt.QueuedConnection)
        self.sigSaveMeasurement.connect(self._odmr_logic.save_odmr_data, QtCore.Qt.QueuedConnection)

        # Update signals coming from logic:
        self._odmr_logic.sigParameterUpdated.connect(self.update_parameter,
                                                     QtCore.Qt.QueuedConnection)
        self._odmr_logic.sigOutputStateUpdated.connect(self.update_status,
                                                       QtCore.Qt.QueuedConnection)
        self._odmr_logic.sigOdmrPlotsUpdated.connect(self.update_plots, QtCore.Qt.QueuedConnection)
        self._odmr_logic.sigOdmrFitUpdated.connect(self.update_fit, QtCore.Qt.QueuedConnection)
        self._odmr_logic.sigOdmrElapsedTimeUpdated.connect(self.update_elapsedtime,
                                                           QtCore.Qt.QueuedConnection)

        # connect settings signals
        self._mw.action_Settings.triggered.connect(self._menu_settings)
        self._sd.accepted.connect(self.update_settings)
        self._sd.rejected.connect(self.reject_settings)
        self._sd.buttonBox.button(QtWidgets.QDialogButtonBox.Apply).clicked.connect(
            self.update_settings)
        self.reject_settings()

        # Show the Main ODMR GUI:
        self._show()

    def on_deactivate(self):
        """ Reverse steps of activation

        @return int: error code (0:OK, -1:error)
        """
        # Disconnect signals
        self._sd.buttonBox.button(QtWidgets.QDialogButtonBox.Apply).clicked.disconnect()
        self._sd.accepted.disconnect()
        self._sd.rejected.disconnect()
        self._mw.action_Settings.triggered.disconnect()
        self._odmr_logic.sigParameterUpdated.disconnect()
        self._odmr_logic.sigOutputStateUpdated.disconnect()
        self._odmr_logic.sigOdmrPlotsUpdated.disconnect()
        self._odmr_logic.sigOdmrFitUpdated.disconnect()
        self._odmr_logic.sigOdmrElapsedTimeUpdated.disconnect()
        self.sigCwMwOn.disconnect()
        self.sigMwOff.disconnect()
        self.sigClearData.disconnect()
        self.sigStartOdmrScan.disconnect()
        self.sigStopOdmrScan.disconnect()
        self.sigContinueOdmrScan.disconnect()
        self.sigDoFit.disconnect()
        self.sigMwCwParamsChanged.disconnect()
        self.sigMwSweepParamsChanged.disconnect()
        self.sigRuntimeChanged.disconnect()
        self.sigNumberOfLinesChanged.disconnect()
        self.sigSaveMeasurement.disconnect()
        self._mw.odmr_cb_manual_RadioButton.clicked.disconnect()
        self._mw.odmr_cb_centiles_RadioButton.clicked.disconnect()
        self._mw.clear_odmr_PushButton.clicked.disconnect()
        self._mw.action_run_stop.triggered.disconnect()
        self._mw.action_resume_odmr.triggered.disconnect()
        self._mw.action_Save.triggered.disconnect()
        self._mw.action_toggle_cw.triggered.disconnect()
        self._mw.action_RestoreDefault.triggered.disconnect()
        self._mw.do_fit_PushButton.clicked.disconnect()
        self._mw.cw_frequency_DoubleSpinBox.editingFinished.disconnect()
        self._mw.start_freq_DoubleSpinBox.editingFinished.disconnect()
        self._mw.step_freq_DoubleSpinBox.editingFinished.disconnect()
        self._mw.stop_freq_DoubleSpinBox.editingFinished.disconnect()
        self._mw.cw_power_DoubleSpinBox.editingFinished.disconnect()
        self._mw.sweep_power_DoubleSpinBox.editingFinished.disconnect()
        self._mw.runtime_DoubleSpinBox.editingFinished.disconnect()
        self._mw.odmr_cb_max_DoubleSpinBox.valueChanged.disconnect()
        self._mw.odmr_cb_min_DoubleSpinBox.valueChanged.disconnect()
        self._mw.odmr_cb_high_percentile_DoubleSpinBox.valueChanged.disconnect()
        self._mw.odmr_cb_low_percentile_DoubleSpinBox.valueChanged.disconnect()
        self._fsd.sigFitsUpdated.disconnect()
        self._mw.action_FitSettings.triggered.disconnect()
        self._mw.close()
        return 0

    def _show(self):
        """Make window visible and put it above all other windows. """
        self._mw.show()
        self._mw.activateWindow()
        self._mw.raise_()
        return

    def _menu_settings(self):
        """ Open the settings menu """
        self._sd.exec_()

    def run_stop_odmr(self, is_checked):
        """ Manages what happens if odmr scan is started/stopped. """
        if is_checked:
            # change the axes appearance according to input values:
            self._mw.action_run_stop.setEnabled(False)
            self._mw.action_resume_odmr.setEnabled(False)
            self._mw.action_toggle_cw.setEnabled(False)
            self._mw.odmr_PlotWidget.removeItem(self.odmr_fit_image)
            self._mw.cw_power_DoubleSpinBox.setEnabled(False)
            self._mw.sweep_power_DoubleSpinBox.setEnabled(False)
            self._mw.cw_frequency_DoubleSpinBox.setEnabled(False)
            self._mw.start_freq_DoubleSpinBox.setEnabled(False)
            self._mw.step_freq_DoubleSpinBox.setEnabled(False)
            self._mw.stop_freq_DoubleSpinBox.setEnabled(False)
            self._mw.runtime_DoubleSpinBox.setEnabled(False)
            self._sd.clock_frequency_DoubleSpinBox.setEnabled(False)
            self.sigStartOdmrScan.emit()
        else:
            self._mw.action_run_stop.setEnabled(False)
            self._mw.action_resume_odmr.setEnabled(False)
            self._mw.action_toggle_cw.setEnabled(False)
            self.sigStopOdmrScan.emit()
        return

    def resume_odmr(self, is_checked):
        if is_checked:
            self._mw.action_run_stop.setEnabled(False)
            self._mw.action_resume_odmr.setEnabled(False)
            self._mw.action_toggle_cw.setEnabled(False)
            self._mw.cw_power_DoubleSpinBox.setEnabled(False)
            self._mw.sweep_power_DoubleSpinBox.setEnabled(False)
            self._mw.cw_frequency_DoubleSpinBox.setEnabled(False)
            self._mw.start_freq_DoubleSpinBox.setEnabled(False)
            self._mw.step_freq_DoubleSpinBox.setEnabled(False)
            self._mw.stop_freq_DoubleSpinBox.setEnabled(False)
            self._mw.runtime_DoubleSpinBox.setEnabled(False)
            self._sd.clock_frequency_DoubleSpinBox.setEnabled(False)
            self.sigContinueOdmrScan.emit()
        else:
            self._mw.action_run_stop.setEnabled(False)
            self._mw.action_resume_odmr.setEnabled(False)
            self._mw.action_toggle_cw.setEnabled(False)
            self.sigStopOdmrScan.emit()
        return

    def toggle_cw_mode(self, is_checked):
        """ Starts or stops CW microwave output if no measurement is running. """
        if is_checked:
            self._mw.action_run_stop.setEnabled(False)
            self._mw.action_resume_odmr.setEnabled(False)
            self._mw.action_toggle_cw.setEnabled(False)
            self._mw.cw_power_DoubleSpinBox.setEnabled(False)
            self._mw.cw_frequency_DoubleSpinBox.setEnabled(False)
            self.sigCwMwOn.emit()
        else:
            self._mw.action_toggle_cw.setEnabled(False)
            self.sigMwOff.emit()
        return

    def update_status(self, mw_mode, is_running):
        """ 
        Update the display for a change in the microwave status (mode and output).

        @param str mw_mode: is the microwave output active?
        @param bool is_running: is the microwave output active?
        """
        # Block signals from firing
        self._mw.action_run_stop.blockSignals(True)
        self._mw.action_resume_odmr.blockSignals(True)
        self._mw.action_toggle_cw.blockSignals(True)

        # Update measurement status (activate/deactivate widgets/actions)
        if is_running:
            self._mw.action_resume_odmr.setEnabled(False)
            self._mw.cw_power_DoubleSpinBox.setEnabled(False)
            self._mw.cw_frequency_DoubleSpinBox.setEnabled(False)
            if mw_mode != 'cw':
                self._mw.clear_odmr_PushButton.setEnabled(True)
                self._mw.action_run_stop.setEnabled(True)
                self._mw.action_toggle_cw.setEnabled(False)
                self._mw.start_freq_DoubleSpinBox.setEnabled(False)
                self._mw.step_freq_DoubleSpinBox.setEnabled(False)
                self._mw.stop_freq_DoubleSpinBox.setEnabled(False)
                self._mw.sweep_power_DoubleSpinBox.setEnabled(False)
                self._mw.runtime_DoubleSpinBox.setEnabled(False)
                self._sd.clock_frequency_DoubleSpinBox.setEnabled(False)
                self._mw.action_run_stop.setChecked(True)
                self._mw.action_resume_odmr.setChecked(True)
                self._mw.action_toggle_cw.setChecked(False)
            else:
                self._mw.clear_odmr_PushButton.setEnabled(False)
                self._mw.action_run_stop.setEnabled(False)
                self._mw.action_toggle_cw.setEnabled(True)
                self._mw.start_freq_DoubleSpinBox.setEnabled(True)
                self._mw.step_freq_DoubleSpinBox.setEnabled(True)
                self._mw.stop_freq_DoubleSpinBox.setEnabled(True)
                self._mw.sweep_power_DoubleSpinBox.setEnabled(True)
                self._mw.runtime_DoubleSpinBox.setEnabled(True)
                self._sd.clock_frequency_DoubleSpinBox.setEnabled(True)
                self._mw.action_run_stop.setChecked(False)
                self._mw.action_resume_odmr.setChecked(False)
                self._mw.action_toggle_cw.setChecked(True)
        else:
            self._mw.action_resume_odmr.setEnabled(True)
            self._mw.cw_power_DoubleSpinBox.setEnabled(True)
            self._mw.sweep_power_DoubleSpinBox.setEnabled(True)
            self._mw.cw_frequency_DoubleSpinBox.setEnabled(True)
            self._mw.clear_odmr_PushButton.setEnabled(False)
            self._mw.action_run_stop.setEnabled(True)
            self._mw.action_toggle_cw.setEnabled(True)
            self._mw.start_freq_DoubleSpinBox.setEnabled(True)
            self._mw.step_freq_DoubleSpinBox.setEnabled(True)
            self._mw.stop_freq_DoubleSpinBox.setEnabled(True)
            self._mw.runtime_DoubleSpinBox.setEnabled(True)
            self._sd.clock_frequency_DoubleSpinBox.setEnabled(True)
            self._mw.action_run_stop.setChecked(False)
            self._mw.action_resume_odmr.setChecked(False)
            self._mw.action_toggle_cw.setChecked(False)

        # Unblock signal firing
        self._mw.action_run_stop.blockSignals(False)
        self._mw.action_resume_odmr.blockSignals(False)
        self._mw.action_toggle_cw.blockSignals(False)
        return

    def clear_odmr_data(self):
        """ Clear the ODMR data. """
        self.sigClearData.emit()
        return

    def update_plots(self, odmr_data_x, odmr_data_y, odmr_matrix):
        """ Refresh the plot widgets with new data. """
        # Update mean signal plot
        self.odmr_image.setData(odmr_data_x, odmr_data_y)
        # Update raw data matrix plot
        cb_range = self.get_matrix_cb_range()
        self.update_colorbar(cb_range)
        self.odmr_matrix_image.setRect(QtCore.QRectF(odmr_data_x[0],
                                                     0,
                                                     np.abs(odmr_data_x[-1] - odmr_data_x[0]),
                                                     odmr_matrix.shape[0]))
        self.odmr_matrix_image.setImage(image=odmr_matrix.transpose(),
                                        levels=(cb_range[0], cb_range[1]))

        return

    def colorscale_changed(self):
        """
        Updates the range of the displayed colorscale in both the colorbar and the matrix plot.
        """
        cb_range = self.get_matrix_cb_range()
        self.update_colorbar(cb_range)
        matrix_image = self.odmr_matrix_image.image
        self.odmr_matrix_image.setImage(image=matrix_image, levels=(cb_range[0], cb_range[1]))
        return

    def update_colorbar(self, cb_range):
        """ 
        Update the colorbar to a new range.
        
        @param list cb_range: List or tuple containing the min and max values for the cb range
        """
        self.odmr_cb.refresh_colorbar(cb_range[0], cb_range[1])
        return

    def get_matrix_cb_range(self):
        """ 
        Determines the cb_min and cb_max values for the matrix plot
        """
        matrix_image = self.odmr_matrix_image.image

        # If "Manual" is checked or the image is empty (all zeros), then take manual cb range.
        # Otherwise, calculate cb range from percentiles.
        if self._mw.odmr_cb_manual_RadioButton.isChecked() or np.max(matrix_image) < 0.1:
            cb_min = self._mw.odmr_cb_min_DoubleSpinBox.value()
            cb_max = self._mw.odmr_cb_max_DoubleSpinBox.value()
        else:
            # Exclude any zeros (which are typically due to unfinished scan)
            matrix_image_nonzero = matrix_image[np.nonzero(matrix_image)]

            # Read centile range
            low_centile = self._mw.odmr_cb_low_percentile_DoubleSpinBox.value()
            high_centile = self._mw.odmr_cb_high_percentile_DoubleSpinBox.value()

            cb_min = np.percentile(matrix_image_nonzero, low_centile)
            cb_max = np.percentile(matrix_image_nonzero, high_centile)

        cb_range = [cb_min, cb_max]
        return cb_range

    def restore_defaultview(self):
        self._mw.restoreGeometry(self.mwsettings.value("geometry", ""))
        self._mw.restoreState(self.mwsettings.value("windowState", ""))

    def update_elapsedtime(self, elapsed_time, scanned_lines):
        """ Updates current elapsed measurement time and completed frequency sweeps """
        self._mw.elapsed_time_DisplayWidget.display(int(np.rint(elapsed_time)))
        self._mw.elapsed_sweeps_DisplayWidget.display(scanned_lines)
        return

    def update_settings(self):
        """ Write the new settings from the gui to the file. """
        number_of_lines = self._sd.matrix_lines_SpinBox.value()
        clock_frequency = self._sd.clock_frequency_DoubleSpinBox.value()
        self.sigClockFreqChanged.emit(clock_frequency)
        self.sigNumberOfLinesChanged.emit(number_of_lines)
        return

    def reject_settings(self):
        """ Keep the old settings and restores the old settings in the gui. """
        self._sd.matrix_lines_SpinBox.setValue(self._odmr_logic.number_of_lines)
        self._sd.clock_frequency_DoubleSpinBox.setValue(self._odmr_logic.clock_frequency)
        return

    def do_fit(self):
        fit_function  = self._mw.fit_methods_ComboBox.getCurrentFit()[0]
        self.sigDoFit.emit(fit_function)
        return

    def update_fit(self, x_data, y_data, result_str_dict, current_fit):
        """ Update the shown fit. """
        if current_fit != 'No Fit':
            # display results as formatted text
            self._mw.odmr_fit_results_DisplayWidget.clear()
            try:
                formated_results = units.create_formatted_output(result_str_dict)
            except:
                formated_results = 'this fit does not return formatted results'
            self._mw.odmr_fit_results_DisplayWidget.setPlainText(formated_results)

        self._mw.fit_methods_ComboBox.blockSignals(True)
        self._mw.fit_methods_ComboBox.setCurrentFit(current_fit)
        self._mw.fit_methods_ComboBox.blockSignals(False)

        # check which Fit method is used and remove or add again the
        # odmr_fit_image, check also whether a odmr_fit_image already exists.
        if current_fit != 'No Fit':
            self.odmr_fit_image.setData(x=x_data, y=y_data)
            if self.odmr_fit_image not in self._mw.odmr_PlotWidget.listDataItems():
                self._mw.odmr_PlotWidget.addItem(self.odmr_fit_image)
        else:
            if self.odmr_fit_image in self._mw.odmr_PlotWidget.listDataItems():
                self._mw.odmr_PlotWidget.removeItem(self.odmr_fit_image)

        self._mw.odmr_PlotWidget.getViewBox().updateAutoRange()
        return

    def update_parameter(self, param_dict):
        """ Update the parameter display in the GUI.

        @param param_dict:
        @return:

        Any change event from the logic should call this update function.
        The update will block the GUI signals from emitting a change back to the
        logic.
        """
        param = param_dict.get('mw_power')
        if param is not None:
            self._mw.power_DoubleSpinBox.blockSignals(True)
            self._mw.power_DoubleSpinBox.setValue(param)
            self._mw.power_DoubleSpinBox.blockSignals(False)

        param = param_dict.get('mw_frequency')
        if param is not None:
            self._mw.frequency_DoubleSpinBox.blockSignals(True)
            self._mw.frequency_DoubleSpinBox.setValue(param)
            self._mw.frequency_DoubleSpinBox.blockSignals(False)

        param = param_dict.get('mw_start')
        if param is not None:
            self._mw.start_freq_DoubleSpinBox.blockSignals(True)
            self._mw.start_freq_DoubleSpinBox.setValue(param)
            self._mw.start_freq_DoubleSpinBox.blockSignals(False)

        param = param_dict.get('mw_step')
        if param is not None:
            self._mw.step_freq_DoubleSpinBox.blockSignals(True)
            self._mw.step_freq_DoubleSpinBox.setValue(param)
            self._mw.step_freq_DoubleSpinBox.blockSignals(False)

        param = param_dict.get('mw_stop')
        if param is not None:
            self._mw.stop_freq_DoubleSpinBox.blockSignals(True)
            self._mw.stop_freq_DoubleSpinBox.setValue(param)
            self._mw.stop_freq_DoubleSpinBox.blockSignals(False)

        param = param_dict.get('run_time')
        if param is not None:
            self._mw.runtime_DoubleSpinBox.blockSignals(True)
            self._mw.runtime_DoubleSpinBox.setValue(param)
            self._mw.runtime_DoubleSpinBox.blockSignals(False)

        param = param_dict.get('number_of_lines')
        if param is not None:
            self._sd.matrix_lines_SpinBox.blockSignals(True)
            self._sd.matrix_lines_SpinBox.setValue(param)
            self._sd.matrix_lines_SpinBox.blockSignals(False)

        param = param_dict.get('clock_frequency')
        if param is not None:
            self._sd.clock_frequency_DoubleSpinBox.blockSignals(True)
            self._sd.clock_frequency_DoubleSpinBox.setValue(param)
            self._sd.clock_frequency_DoubleSpinBox.blockSignals(False)
        return

    ############################################################################
    #                           Change Methods                                 #
    ############################################################################

    def change_cw_params(self):
        """ Change CW frequency and power of microwave source """
        frequency = self._mw.cw_frequency_DoubleSpinBox.value()
        power = self._mw.cw_power_DoubleSpinBox.value()
        self.sigMwCwParamsChanged.emit(frequency, power)
        return

    def change_sweep_params(self):
        """ Change start, stop and step frequency of frequency sweep """
        start = self._mw.start_freq_DoubleSpinBox.value()
        stop = self._mw.stop_freq_DoubleSpinBox.value()
        step = self._mw.step_freq_DoubleSpinBox.value()
        power = self._mw.sweep_power_DoubleSpinBox.value()
        self.sigMwSweepParamsChanged.emit(start, stop, step, power)
        return

    def change_runtime(self):
        """ Change time after which microwave sweep is stopped """
        runtime = self._mw.runtime_DoubleSpinBox.value()
        self.sigRuntimeChanged.emit(runtime)
        return

    def save_data(self):
        """ Save the sum plot, the scan marix plot and the scan data """
        filetag = self._mw.save_tag_LineEdit.text()
        cb_range = self.get_matrix_cb_range()

        # Percentile range is None, unless the percentile scaling is selected in GUI.
        pcile_range = None
        if self._mw.odmr_cb_centiles_RadioButton.isChecked():
            low_centile = self._mw.odmr_cb_low_percentile_DoubleSpinBox.value()
            high_centile = self._mw.odmr_cb_high_percentile_DoubleSpinBox.value()
            pcile_range = [low_centile, high_centile]

        self.sigSaveMeasurement.emit(filetag, cb_range, pcile_range)
        return
Example #4
0
class ODMRGui(GUIBase):
    """
    This is the GUI Class for ODMR measurements
    """

    # declare connectors
    odmrlogic1 = Connector(interface='ODMRAWGLogic')
    savelogic = Connector(interface='SaveLogic')

    sigStartOdmrScan = QtCore.Signal()
    sigStopOdmrScan = QtCore.Signal()
    sigContinueOdmrScan = QtCore.Signal()
    sigClearData = QtCore.Signal()
    sigCwMwOn = QtCore.Signal()
    sigMwOff = QtCore.Signal()
    sigMwPowerChanged = QtCore.Signal(float)
    sigMwCwParamsChanged = QtCore.Signal(float, float)
    sigMwSweepParamsChanged = QtCore.Signal(float, float, float, float)
    sigClockFreqChanged = QtCore.Signal(float)
    sigOversamplingChanged = QtCore.Signal(int)
    sigLockInChanged = QtCore.Signal(bool)
    sigFitChanged = QtCore.Signal(str)
    sigNumberOfLinesChanged = QtCore.Signal(int)
    sigRuntimeChanged = QtCore.Signal(float)
    sigDoFit = QtCore.Signal(str, object, object, int)
    sigSaveMeasurement = QtCore.Signal(str, list, list)
    sigAverageLinesChanged = QtCore.Signal(int)

    def __init__(self, config, **kwargs):
        super().__init__(config=config, **kwargs)

    def on_activate(self):
        """ Definition, configuration and initialisation of the ODMR GUI.

        This init connects all the graphic modules, which were created in the
        *.ui file and configures the event handling between the modules.
        """

        self._odmr_logic = self.odmrlogic1()

        # Use the inherited class 'Ui_ODMRGuiUI' to create now the GUI element:
        self._mw = ODMRMainWindow()
        self._sd = ODMRSettingDialog()

        # Create a QSettings object for the mainwindow and store the actual GUI layout
        self.mwsettings = QtCore.QSettings("QUDI", "ODMR")
        self.mwsettings.setValue("geometry", self._mw.saveGeometry())
        self.mwsettings.setValue("windowState", self._mw.saveState())

        # Get hardware constraints to set limits for input widgets
        constraints = self._odmr_logic.get_hw_constraints()

        # Adjust range of scientific spinboxes above what is possible in Qt Designer
        self._mw.cw_frequency_DoubleSpinBox.setMaximum(
            constraints.max_frequency)
        self._mw.cw_frequency_DoubleSpinBox.setMinimum(
            constraints.min_frequency)
        self._mw.start_freq_DoubleSpinBox.setMaximum(constraints.max_frequency)
        self._mw.start_freq_DoubleSpinBox.setMinimum(constraints.min_frequency)
        self._mw.step_freq_DoubleSpinBox.setMaximum(100e9)
        self._mw.stop_freq_DoubleSpinBox.setMaximum(constraints.max_frequency)
        self._mw.stop_freq_DoubleSpinBox.setMinimum(constraints.min_frequency)
        self._mw.cw_power_DoubleSpinBox.setMaximum(constraints.max_power)
        self._mw.cw_power_DoubleSpinBox.setMinimum(constraints.min_power)
        self._mw.sweep_power_DoubleSpinBox.setMaximum(constraints.max_power)
        self._mw.sweep_power_DoubleSpinBox.setMinimum(constraints.min_power)

        # Add save file tag input box
        self._mw.save_tag_LineEdit = QtWidgets.QLineEdit(self._mw)
        self._mw.save_tag_LineEdit.setMaximumWidth(500)
        self._mw.save_tag_LineEdit.setMinimumWidth(200)
        self._mw.save_tag_LineEdit.setToolTip('Enter a nametag which will be\n'
                                              'added to the filename.')
        self._mw.save_ToolBar.addWidget(self._mw.save_tag_LineEdit)

        # add a clear button to clear the ODMR plots:
        self._mw.clear_odmr_PushButton = QtWidgets.QPushButton(self._mw)
        self._mw.clear_odmr_PushButton.setText('Clear ODMR')
        self._mw.clear_odmr_PushButton.setToolTip('Clear the data of the\n'
                                                  'current ODMR measurements.')
        self._mw.clear_odmr_PushButton.setEnabled(False)
        self._mw.toolBar.addWidget(self._mw.clear_odmr_PushButton)

        # Set up and connect channel combobox
        self.display_channel = 0
        odmr_channels = self._odmr_logic.get_odmr_channels()
        for n, ch in enumerate(odmr_channels):
            self._mw.odmr_channel_ComboBox.addItem(str(ch), n)

        self._mw.odmr_channel_ComboBox.activated.connect(self.update_channel)

        # Get the image from the logic
        self.odmr_matrix_image = pg.ImageItem(
            self._odmr_logic.odmr_plot_xy[:, self.display_channel],
            axisOrder='row-major')
        self.odmr_matrix_image.setRect(
            QtCore.QRectF(self._odmr_logic.mw_start, 0,
                          self._odmr_logic.mw_stop - self._odmr_logic.mw_start,
                          self._odmr_logic.number_of_lines))

        self.odmr_image = pg.PlotDataItem(
            self._odmr_logic.odmr_plot_x,
            self._odmr_logic.odmr_plot_y[self.display_channel],
            pen=pg.mkPen(palette.c1, style=QtCore.Qt.DotLine),
            symbol='o',
            symbolPen=palette.c1,
            symbolBrush=palette.c1,
            symbolSize=7)

        self.odmr_fit_image = pg.PlotDataItem(self._odmr_logic.odmr_fit_x,
                                              self._odmr_logic.odmr_fit_y,
                                              pen=pg.mkPen(palette.c2))

        # Add the display item to the xy and xz ViewWidget, which was defined in the UI file.
        self._mw.odmr_PlotWidget.addItem(self.odmr_image)
        self._mw.odmr_PlotWidget.setLabel(axis='left',
                                          text='Counts',
                                          units='Counts/s')
        self._mw.odmr_PlotWidget.setLabel(axis='bottom',
                                          text='Frequency',
                                          units='Hz')
        self._mw.odmr_PlotWidget.showGrid(x=True, y=True, alpha=0.8)

        self._mw.odmr_matrix_PlotWidget.addItem(self.odmr_matrix_image)
        self._mw.odmr_matrix_PlotWidget.setLabel(axis='left',
                                                 text='Matrix Lines',
                                                 units='#')
        self._mw.odmr_matrix_PlotWidget.setLabel(axis='bottom',
                                                 text='Frequency',
                                                 units='Hz')

        # Get the colorscales at set LUT
        my_colors = ColorScaleInferno()
        self.odmr_matrix_image.setLookupTable(my_colors.lut)

        ########################################################################
        #                  Configuration of the Colorbar                       #
        ########################################################################
        self.odmr_cb = ColorBar(my_colors.cmap_normed, 100, 0, 100000)

        # adding colorbar to ViewWidget
        self._mw.odmr_cb_PlotWidget.addItem(self.odmr_cb)
        self._mw.odmr_cb_PlotWidget.hideAxis('bottom')
        self._mw.odmr_cb_PlotWidget.hideAxis('left')
        self._mw.odmr_cb_PlotWidget.setLabel('right',
                                             'Fluorescence',
                                             units='counts/s')

        ########################################################################
        #          Configuration of the various display Widgets                #
        ########################################################################
        # Take the default values from logic:
        self._mw.cw_frequency_DoubleSpinBox.setValue(
            self._odmr_logic.cw_mw_frequency)
        self._mw.start_freq_DoubleSpinBox.setValue(self._odmr_logic.mw_start)
        self._mw.stop_freq_DoubleSpinBox.setValue(self._odmr_logic.mw_stop)
        self._mw.step_freq_DoubleSpinBox.setValue(self._odmr_logic.mw_step)
        self._mw.cw_power_DoubleSpinBox.setValue(self._odmr_logic.cw_mw_power)
        self._mw.sweep_power_DoubleSpinBox.setValue(
            self._odmr_logic.sweep_mw_power)

        self._mw.runtime_DoubleSpinBox.setValue(self._odmr_logic.run_time)
        self._mw.elapsed_time_DisplayWidget.display(
            int(np.rint(self._odmr_logic.elapsed_time)))
        self._mw.elapsed_sweeps_DisplayWidget.display(
            self._odmr_logic.elapsed_sweeps)
        self._mw.average_level_SpinBox.setValue(
            self._odmr_logic.lines_to_average)

        self._sd.matrix_lines_SpinBox.setValue(
            self._odmr_logic.number_of_lines)
        self._sd.clock_frequency_DoubleSpinBox.setValue(
            self._odmr_logic.clock_frequency)
        self._sd.oversampling_SpinBox.setValue(self._odmr_logic.oversampling)
        self._sd.lock_in_CheckBox.setChecked(self._odmr_logic.lock_in)

        # fit settings
        self._fsd = FitSettingsDialog(self._odmr_logic.fc)
        self._fsd.sigFitsUpdated.connect(
            self._mw.fit_methods_ComboBox.setFitFunctions)
        self._fsd.applySettings()
        self._mw.action_FitSettings.triggered.connect(self._fsd.show)

        ########################################################################
        #                       Connect signals                                #
        ########################################################################
        # Internal user input changed signals
        self._mw.cw_frequency_DoubleSpinBox.editingFinished.connect(
            self.change_cw_params)
        self._mw.start_freq_DoubleSpinBox.editingFinished.connect(
            self.change_sweep_params)
        self._mw.step_freq_DoubleSpinBox.editingFinished.connect(
            self.change_sweep_params)
        self._mw.stop_freq_DoubleSpinBox.editingFinished.connect(
            self.change_sweep_params)
        self._mw.sweep_power_DoubleSpinBox.editingFinished.connect(
            self.change_sweep_params)
        self._mw.cw_power_DoubleSpinBox.editingFinished.connect(
            self.change_cw_params)
        self._mw.runtime_DoubleSpinBox.editingFinished.connect(
            self.change_runtime)
        self._mw.odmr_cb_max_DoubleSpinBox.valueChanged.connect(
            self.colorscale_changed)
        self._mw.odmr_cb_min_DoubleSpinBox.valueChanged.connect(
            self.colorscale_changed)
        self._mw.odmr_cb_high_percentile_DoubleSpinBox.valueChanged.connect(
            self.colorscale_changed)
        self._mw.odmr_cb_low_percentile_DoubleSpinBox.valueChanged.connect(
            self.colorscale_changed)
        self._mw.average_level_SpinBox.valueChanged.connect(
            self.average_level_changed)
        # Internal trigger signals
        self._mw.odmr_cb_manual_RadioButton.clicked.connect(
            self.colorscale_changed)
        self._mw.odmr_cb_centiles_RadioButton.clicked.connect(
            self.colorscale_changed)
        self._mw.clear_odmr_PushButton.clicked.connect(self.clear_odmr_data)
        self._mw.action_run_stop.triggered.connect(self.run_stop_odmr)
        self._mw.action_resume_odmr.triggered.connect(self.resume_odmr)
        self._mw.action_toggle_cw.triggered.connect(self.toggle_cw_mode)
        self._mw.action_Save.triggered.connect(self.save_data)
        self._mw.action_RestoreDefault.triggered.connect(
            self.restore_defaultview)
        self._mw.do_fit_PushButton.clicked.connect(self.do_fit)

        # Control/values-changed signals to logic
        self.sigCwMwOn.connect(self._odmr_logic.mw_cw_on,
                               QtCore.Qt.QueuedConnection)
        self.sigMwOff.connect(self._odmr_logic.mw_off,
                              QtCore.Qt.QueuedConnection)
        self.sigClearData.connect(self._odmr_logic.clear_odmr_data,
                                  QtCore.Qt.QueuedConnection)
        self.sigStartOdmrScan.connect(self._odmr_logic.start_odmr_scan,
                                      QtCore.Qt.QueuedConnection)
        self.sigStopOdmrScan.connect(self._odmr_logic.stop_odmr_scan,
                                     QtCore.Qt.QueuedConnection)
        self.sigContinueOdmrScan.connect(self._odmr_logic.continue_odmr_scan,
                                         QtCore.Qt.QueuedConnection)
        self.sigDoFit.connect(self._odmr_logic.do_fit,
                              QtCore.Qt.QueuedConnection)
        self.sigMwCwParamsChanged.connect(self._odmr_logic.set_cw_parameters,
                                          QtCore.Qt.QueuedConnection)
        self.sigMwSweepParamsChanged.connect(
            self._odmr_logic.set_sweep_parameters, QtCore.Qt.QueuedConnection)
        self.sigRuntimeChanged.connect(self._odmr_logic.set_runtime,
                                       QtCore.Qt.QueuedConnection)
        self.sigNumberOfLinesChanged.connect(
            self._odmr_logic.set_matrix_line_number,
            QtCore.Qt.QueuedConnection)
        self.sigClockFreqChanged.connect(self._odmr_logic.set_clock_frequency,
                                         QtCore.Qt.QueuedConnection)
        self.sigOversamplingChanged.connect(self._odmr_logic.set_oversampling,
                                            QtCore.Qt.QueuedConnection)
        self.sigLockInChanged.connect(self._odmr_logic.set_lock_in,
                                      QtCore.Qt.QueuedConnection)
        self.sigSaveMeasurement.connect(self._odmr_logic.save_odmr_data,
                                        QtCore.Qt.QueuedConnection)
        self.sigAverageLinesChanged.connect(
            self._odmr_logic.set_average_length, QtCore.Qt.QueuedConnection)

        # Update signals coming from logic:
        self._odmr_logic.sigParameterUpdated.connect(
            self.update_parameter, QtCore.Qt.QueuedConnection)
        self._odmr_logic.sigOutputStateUpdated.connect(
            self.update_status, QtCore.Qt.QueuedConnection)
        self._odmr_logic.sigOdmrPlotsUpdated.connect(
            self.update_plots, QtCore.Qt.QueuedConnection)
        self._odmr_logic.sigOdmrFitUpdated.connect(self.update_fit,
                                                   QtCore.Qt.QueuedConnection)
        self._odmr_logic.sigOdmrElapsedTimeUpdated.connect(
            self.update_elapsedtime, QtCore.Qt.QueuedConnection)

        # connect settings signals
        self._mw.action_Settings.triggered.connect(self._menu_settings)
        self._sd.accepted.connect(self.update_settings)
        self._sd.rejected.connect(self.reject_settings)
        self._sd.buttonBox.button(
            QtWidgets.QDialogButtonBox.Apply).clicked.connect(
                self.update_settings)
        self.reject_settings()

        # Show the Main ODMR GUI:
        self.show()

    def on_deactivate(self):
        """ Reverse steps of activation

        @return int: error code (0:OK, -1:error)
        """
        # Disconnect signals
        self._sd.buttonBox.button(
            QtWidgets.QDialogButtonBox.Apply).clicked.disconnect()
        self._sd.accepted.disconnect()
        self._sd.rejected.disconnect()
        self._mw.action_Settings.triggered.disconnect()
        self._odmr_logic.sigParameterUpdated.disconnect()
        self._odmr_logic.sigOutputStateUpdated.disconnect()
        self._odmr_logic.sigOdmrPlotsUpdated.disconnect()
        self._odmr_logic.sigOdmrFitUpdated.disconnect()
        self._odmr_logic.sigOdmrElapsedTimeUpdated.disconnect()
        self.sigCwMwOn.disconnect()
        self.sigMwOff.disconnect()
        self.sigClearData.disconnect()
        self.sigStartOdmrScan.disconnect()
        self.sigStopOdmrScan.disconnect()
        self.sigContinueOdmrScan.disconnect()
        self.sigDoFit.disconnect()
        self.sigMwCwParamsChanged.disconnect()
        self.sigMwSweepParamsChanged.disconnect()
        self.sigRuntimeChanged.disconnect()
        self.sigNumberOfLinesChanged.disconnect()
        self.sigClockFreqChanged.disconnect()
        self.sigOversamplingChanged.disconnect()
        self.sigLockInChanged.disconnect()
        self.sigSaveMeasurement.disconnect()
        self.sigAverageLinesChanged.disconnect()
        self._mw.odmr_cb_manual_RadioButton.clicked.disconnect()
        self._mw.odmr_cb_centiles_RadioButton.clicked.disconnect()
        self._mw.clear_odmr_PushButton.clicked.disconnect()
        self._mw.action_run_stop.triggered.disconnect()
        self._mw.action_resume_odmr.triggered.disconnect()
        self._mw.action_Save.triggered.disconnect()
        self._mw.action_toggle_cw.triggered.disconnect()
        self._mw.action_RestoreDefault.triggered.disconnect()
        self._mw.do_fit_PushButton.clicked.disconnect()
        self._mw.cw_frequency_DoubleSpinBox.editingFinished.disconnect()
        self._mw.start_freq_DoubleSpinBox.editingFinished.disconnect()
        self._mw.step_freq_DoubleSpinBox.editingFinished.disconnect()
        self._mw.stop_freq_DoubleSpinBox.editingFinished.disconnect()
        self._mw.cw_power_DoubleSpinBox.editingFinished.disconnect()
        self._mw.sweep_power_DoubleSpinBox.editingFinished.disconnect()
        self._mw.runtime_DoubleSpinBox.editingFinished.disconnect()
        self._mw.odmr_cb_max_DoubleSpinBox.valueChanged.disconnect()
        self._mw.odmr_cb_min_DoubleSpinBox.valueChanged.disconnect()
        self._mw.odmr_cb_high_percentile_DoubleSpinBox.valueChanged.disconnect(
        )
        self._mw.odmr_cb_low_percentile_DoubleSpinBox.valueChanged.disconnect()
        self._mw.average_level_SpinBox.valueChanged.disconnect()
        self._fsd.sigFitsUpdated.disconnect()
        self._mw.action_FitSettings.triggered.disconnect()
        self._mw.close()
        return 0

    def show(self):
        """Make window visible and put it above all other windows. """
        self._mw.show()
        self._mw.activateWindow()
        self._mw.raise_()

    def _menu_settings(self):
        """ Open the settings menu """
        self._sd.exec_()

    def run_stop_odmr(self, is_checked):
        """ Manages what happens if odmr scan is started/stopped. """
        if is_checked:
            # change the axes appearance according to input values:
            self._mw.action_run_stop.setEnabled(False)
            self._mw.action_resume_odmr.setEnabled(False)
            self._mw.action_toggle_cw.setEnabled(False)
            self._mw.odmr_PlotWidget.removeItem(self.odmr_fit_image)
            self._mw.cw_power_DoubleSpinBox.setEnabled(False)
            self._mw.sweep_power_DoubleSpinBox.setEnabled(False)
            self._mw.cw_frequency_DoubleSpinBox.setEnabled(False)
            self._mw.start_freq_DoubleSpinBox.setEnabled(False)
            self._mw.step_freq_DoubleSpinBox.setEnabled(False)
            self._mw.stop_freq_DoubleSpinBox.setEnabled(False)
            self._mw.runtime_DoubleSpinBox.setEnabled(False)
            self._sd.clock_frequency_DoubleSpinBox.setEnabled(False)
            self._sd.oversampling_SpinBox.setEnabled(False)
            self._sd.lock_in_CheckBox.setEnabled(False)
            self.sigStartOdmrScan.emit()
        else:
            self._mw.action_run_stop.setEnabled(False)
            self._mw.action_resume_odmr.setEnabled(False)
            self._mw.action_toggle_cw.setEnabled(False)
            self.sigStopOdmrScan.emit()
        return

    def resume_odmr(self, is_checked):
        if is_checked:
            self._mw.action_run_stop.setEnabled(False)
            self._mw.action_resume_odmr.setEnabled(False)
            self._mw.action_toggle_cw.setEnabled(False)
            self._mw.cw_power_DoubleSpinBox.setEnabled(False)
            self._mw.sweep_power_DoubleSpinBox.setEnabled(False)
            self._mw.cw_frequency_DoubleSpinBox.setEnabled(False)
            self._mw.start_freq_DoubleSpinBox.setEnabled(False)
            self._mw.step_freq_DoubleSpinBox.setEnabled(False)
            self._mw.stop_freq_DoubleSpinBox.setEnabled(False)
            self._mw.runtime_DoubleSpinBox.setEnabled(False)
            self._sd.clock_frequency_DoubleSpinBox.setEnabled(False)
            self._sd.oversampling_SpinBox.setEnabled(False)
            self._sd.lock_in_CheckBox.setEnabled(False)
            self.sigContinueOdmrScan.emit()
        else:
            self._mw.action_run_stop.setEnabled(False)
            self._mw.action_resume_odmr.setEnabled(False)
            self._mw.action_toggle_cw.setEnabled(False)
            self.sigStopOdmrScan.emit()
        return

    def toggle_cw_mode(self, is_checked):
        """ Starts or stops CW microwave output if no measurement is running. """
        if is_checked:
            self._mw.action_run_stop.setEnabled(False)
            self._mw.action_resume_odmr.setEnabled(False)
            self._mw.action_toggle_cw.setEnabled(False)
            self._mw.cw_power_DoubleSpinBox.setEnabled(False)
            self._mw.cw_frequency_DoubleSpinBox.setEnabled(False)
            self.sigCwMwOn.emit()
        else:
            self._mw.action_toggle_cw.setEnabled(False)
            self.sigMwOff.emit()
        return

    def update_status(self, mw_mode, is_running):
        """
        Update the display for a change in the microwave status (mode and output).

        @param str mw_mode: is the microwave output active?
        @param bool is_running: is the microwave output active?
        """
        # Block signals from firing
        self._mw.action_run_stop.blockSignals(True)
        self._mw.action_resume_odmr.blockSignals(True)
        self._mw.action_toggle_cw.blockSignals(True)

        # Update measurement status (activate/deactivate widgets/actions)
        if is_running:
            self._mw.action_resume_odmr.setEnabled(False)
            self._mw.cw_power_DoubleSpinBox.setEnabled(False)
            self._mw.cw_frequency_DoubleSpinBox.setEnabled(False)
            if mw_mode != 'cw':
                self._mw.clear_odmr_PushButton.setEnabled(True)
                self._mw.action_run_stop.setEnabled(True)
                self._mw.action_toggle_cw.setEnabled(False)
                self._mw.start_freq_DoubleSpinBox.setEnabled(False)
                self._mw.step_freq_DoubleSpinBox.setEnabled(False)
                self._mw.stop_freq_DoubleSpinBox.setEnabled(False)
                self._mw.sweep_power_DoubleSpinBox.setEnabled(False)
                self._mw.runtime_DoubleSpinBox.setEnabled(False)
                self._sd.clock_frequency_DoubleSpinBox.setEnabled(False)
                self._sd.oversampling_SpinBox.setEnabled(False)
                self._sd.lock_in_CheckBox.setEnabled(False)
                self._mw.action_run_stop.setChecked(True)
                self._mw.action_resume_odmr.setChecked(True)
                self._mw.action_toggle_cw.setChecked(False)
            else:
                self._mw.clear_odmr_PushButton.setEnabled(False)
                self._mw.action_run_stop.setEnabled(False)
                self._mw.action_toggle_cw.setEnabled(True)
                self._mw.start_freq_DoubleSpinBox.setEnabled(True)
                self._mw.step_freq_DoubleSpinBox.setEnabled(True)
                self._mw.stop_freq_DoubleSpinBox.setEnabled(True)
                self._mw.sweep_power_DoubleSpinBox.setEnabled(True)
                self._mw.runtime_DoubleSpinBox.setEnabled(True)
                self._sd.clock_frequency_DoubleSpinBox.setEnabled(True)
                self._sd.oversampling_SpinBox.setEnabled(True)
                self._sd.lock_in_CheckBox.setEnabled(True)
                self._mw.action_run_stop.setChecked(False)
                self._mw.action_resume_odmr.setChecked(False)
                self._mw.action_toggle_cw.setChecked(True)
        else:
            self._mw.action_resume_odmr.setEnabled(True)
            self._mw.cw_power_DoubleSpinBox.setEnabled(True)
            self._mw.sweep_power_DoubleSpinBox.setEnabled(True)
            self._mw.cw_frequency_DoubleSpinBox.setEnabled(True)
            self._mw.clear_odmr_PushButton.setEnabled(False)
            self._mw.action_run_stop.setEnabled(True)
            self._mw.action_toggle_cw.setEnabled(True)
            self._mw.start_freq_DoubleSpinBox.setEnabled(True)
            self._mw.step_freq_DoubleSpinBox.setEnabled(True)
            self._mw.stop_freq_DoubleSpinBox.setEnabled(True)
            self._mw.runtime_DoubleSpinBox.setEnabled(True)
            self._sd.clock_frequency_DoubleSpinBox.setEnabled(True)
            self._sd.oversampling_SpinBox.setEnabled(True)
            self._sd.lock_in_CheckBox.setEnabled(True)
            self._mw.action_run_stop.setChecked(False)
            self._mw.action_resume_odmr.setChecked(False)
            self._mw.action_toggle_cw.setChecked(False)

        # Unblock signal firing
        self._mw.action_run_stop.blockSignals(False)
        self._mw.action_resume_odmr.blockSignals(False)
        self._mw.action_toggle_cw.blockSignals(False)
        return

    def clear_odmr_data(self):
        """ Clear the ODMR data. """
        self.sigClearData.emit()
        return

    def update_plots(self, odmr_data_x, odmr_data_y, odmr_matrix):
        """ Refresh the plot widgets with new data. """
        # Update mean signal plot
        self.odmr_image.setData(odmr_data_x, odmr_data_y[self.display_channel])
        # Update raw data matrix plot
        cb_range = self.get_matrix_cb_range()
        self.update_colorbar(cb_range)
        self.odmr_matrix_image.setRect(
            QtCore.QRectF(odmr_data_x[0], 0,
                          np.abs(odmr_data_x[-1] - odmr_data_x[0]),
                          odmr_matrix.shape[0]))
        self.odmr_matrix_image.setImage(
            image=odmr_matrix[:, self.display_channel],
            axisOrder='row-major',
            levels=(cb_range[0], cb_range[1]))

    def update_channel(self, index):
        self.display_channel = int(
            self._mw.odmr_channel_ComboBox.itemData(index, QtCore.Qt.UserRole))
        self.update_plots(self._odmr_logic.odmr_plot_x,
                          self._odmr_logic.odmr_plot_y,
                          self._odmr_logic.odmr_plot_xy)

    def average_level_changed(self):
        """
        Sends to lines to average to the logic
        """
        self.sigAverageLinesChanged.emit(
            self._mw.average_level_SpinBox.value())
        return

    def colorscale_changed(self):
        """
        Updates the range of the displayed colorscale in both the colorbar and the matrix plot.
        """
        cb_range = self.get_matrix_cb_range()
        self.update_colorbar(cb_range)
        matrix_image = self.odmr_matrix_image.image
        self.odmr_matrix_image.setImage(image=matrix_image,
                                        levels=(cb_range[0], cb_range[1]))
        return

    def update_colorbar(self, cb_range):
        """
        Update the colorbar to a new range.

        @param list cb_range: List or tuple containing the min and max values for the cb range
        """
        self.odmr_cb.refresh_colorbar(cb_range[0], cb_range[1])
        return

    def get_matrix_cb_range(self):
        """
        Determines the cb_min and cb_max values for the matrix plot
        """
        matrix_image = self.odmr_matrix_image.image

        # If "Manual" is checked or the image is empty (all zeros), then take manual cb range.
        # Otherwise, calculate cb range from percentiles.
        if self._mw.odmr_cb_manual_RadioButton.isChecked(
        ) or np.count_nonzero(matrix_image) < 1:
            cb_min = self._mw.odmr_cb_min_DoubleSpinBox.value()
            cb_max = self._mw.odmr_cb_max_DoubleSpinBox.value()
        else:
            # Exclude any zeros (which are typically due to unfinished scan)
            matrix_image_nonzero = matrix_image[np.nonzero(matrix_image)]

            # Read centile range
            low_centile = self._mw.odmr_cb_low_percentile_DoubleSpinBox.value()
            high_centile = self._mw.odmr_cb_high_percentile_DoubleSpinBox.value(
            )

            cb_min = np.percentile(matrix_image_nonzero, low_centile)
            cb_max = np.percentile(matrix_image_nonzero, high_centile)

        cb_range = [cb_min, cb_max]
        return cb_range

    def restore_defaultview(self):
        self._mw.restoreGeometry(self.mwsettings.value("geometry", ""))
        self._mw.restoreState(self.mwsettings.value("windowState", ""))

    def update_elapsedtime(self, elapsed_time, scanned_lines):
        """ Updates current elapsed measurement time and completed frequency sweeps """
        self._mw.elapsed_time_DisplayWidget.display(int(np.rint(elapsed_time)))
        self._mw.elapsed_sweeps_DisplayWidget.display(scanned_lines)
        return

    def update_settings(self):
        """ Write the new settings from the gui to the file. """
        number_of_lines = self._sd.matrix_lines_SpinBox.value()
        clock_frequency = self._sd.clock_frequency_DoubleSpinBox.value()
        oversampling = self._sd.oversampling_SpinBox.value()
        lock_in = self._sd.lock_in_CheckBox.isChecked()
        self.sigOversamplingChanged.emit(oversampling)
        self.sigLockInChanged.emit(lock_in)
        self.sigClockFreqChanged.emit(clock_frequency)
        self.sigNumberOfLinesChanged.emit(number_of_lines)
        return

    def reject_settings(self):
        """ Keep the old settings and restores the old settings in the gui. """
        self._sd.matrix_lines_SpinBox.setValue(
            self._odmr_logic.number_of_lines)
        self._sd.clock_frequency_DoubleSpinBox.setValue(
            self._odmr_logic.clock_frequency)
        self._sd.oversampling_SpinBox.setValue(self._odmr_logic.oversampling)
        self._sd.lock_in_CheckBox.setChecked(self._odmr_logic.lock_in)
        return

    def do_fit(self):
        fit_function = self._mw.fit_methods_ComboBox.getCurrentFit()[0]
        self.sigDoFit.emit(fit_function, None, None,
                           self._mw.odmr_channel_ComboBox.currentIndex())
        return

    def update_fit(self, x_data, y_data, result_str_dict, current_fit):
        """ Update the shown fit. """
        if current_fit != 'No Fit':
            # display results as formatted text
            self._mw.odmr_fit_results_DisplayWidget.clear()
            try:
                formated_results = units.create_formatted_output(
                    result_str_dict)
            except:
                formated_results = 'this fit does not return formatted results'
            self._mw.odmr_fit_results_DisplayWidget.setPlainText(
                formated_results)

        self._mw.fit_methods_ComboBox.blockSignals(True)
        self._mw.fit_methods_ComboBox.setCurrentFit(current_fit)
        self._mw.fit_methods_ComboBox.blockSignals(False)

        # check which Fit method is used and remove or add again the
        # odmr_fit_image, check also whether a odmr_fit_image already exists.
        if current_fit != 'No Fit':
            self.odmr_fit_image.setData(x=x_data, y=y_data)
            if self.odmr_fit_image not in self._mw.odmr_PlotWidget.listDataItems(
            ):
                self._mw.odmr_PlotWidget.addItem(self.odmr_fit_image)
        else:
            if self.odmr_fit_image in self._mw.odmr_PlotWidget.listDataItems():
                self._mw.odmr_PlotWidget.removeItem(self.odmr_fit_image)

        self._mw.odmr_PlotWidget.getViewBox().updateAutoRange()
        return

    def update_parameter(self, param_dict):
        """ Update the parameter display in the GUI.

        @param param_dict:
        @return:

        Any change event from the logic should call this update function.
        The update will block the GUI signals from emitting a change back to the
        logic.
        """
        param = param_dict.get('sweep_mw_power')
        if param is not None:
            self._mw.sweep_power_DoubleSpinBox.blockSignals(True)
            self._mw.sweep_power_DoubleSpinBox.setValue(param)
            self._mw.sweep_power_DoubleSpinBox.blockSignals(False)

        param = param_dict.get('mw_start')
        if param is not None:
            self._mw.start_freq_DoubleSpinBox.blockSignals(True)
            self._mw.start_freq_DoubleSpinBox.setValue(param)
            self._mw.start_freq_DoubleSpinBox.blockSignals(False)

        param = param_dict.get('mw_step')
        if param is not None:
            self._mw.step_freq_DoubleSpinBox.blockSignals(True)
            self._mw.step_freq_DoubleSpinBox.setValue(param)
            self._mw.step_freq_DoubleSpinBox.blockSignals(False)

        param = param_dict.get('mw_stop')
        if param is not None:
            self._mw.stop_freq_DoubleSpinBox.blockSignals(True)
            self._mw.stop_freq_DoubleSpinBox.setValue(param)
            self._mw.stop_freq_DoubleSpinBox.blockSignals(False)

        param = param_dict.get('run_time')
        if param is not None:
            self._mw.runtime_DoubleSpinBox.blockSignals(True)
            self._mw.runtime_DoubleSpinBox.setValue(param)
            self._mw.runtime_DoubleSpinBox.blockSignals(False)

        param = param_dict.get('number_of_lines')
        if param is not None:
            self._sd.matrix_lines_SpinBox.blockSignals(True)
            self._sd.matrix_lines_SpinBox.setValue(param)
            self._sd.matrix_lines_SpinBox.blockSignals(False)

        param = param_dict.get('clock_frequency')
        if param is not None:
            self._sd.clock_frequency_DoubleSpinBox.blockSignals(True)
            self._sd.clock_frequency_DoubleSpinBox.setValue(param)
            self._sd.clock_frequency_DoubleSpinBox.blockSignals(False)

        param = param_dict.get('oversampling')
        if param is not None:
            self._sd.oversampling_SpinBox.blockSignals(True)
            self._sd.oversampling_SpinBox.setValue(param)
            self._sd.oversampling_SpinBox.blockSignals(False)

        param = param_dict.get('lock_in')
        if param is not None:
            self._sd.lock_in_CheckBox.blockSignals(True)
            self._sd.lock_in_CheckBox.setChecked(param)
            self._sd.lock_in_CheckBox.blockSignals(False)

        param = param_dict.get('cw_mw_frequency')
        if param is not None:
            self._mw.cw_frequency_DoubleSpinBox.blockSignals(True)
            self._mw.cw_frequency_DoubleSpinBox.setValue(param)
            self._mw.cw_frequency_DoubleSpinBox.blockSignals(False)

        param = param_dict.get('cw_mw_power')
        if param is not None:
            self._mw.cw_power_DoubleSpinBox.blockSignals(True)
            self._mw.cw_power_DoubleSpinBox.setValue(param)
            self._mw.cw_power_DoubleSpinBox.blockSignals(False)

        param = param_dict.get('average_length')
        if param is not None:
            self._mw.average_level_SpinBox.blockSignals(True)
            self._mw.average_level_SpinBox.setValue(param)
            self._mw.average_level_SpinBox.blockSignals(False)
        return

    ############################################################################
    #                           Change Methods                                 #
    ############################################################################

    def change_cw_params(self):
        """ Change CW frequency and power of microwave source """
        frequency = self._mw.cw_frequency_DoubleSpinBox.value()
        power = self._mw.cw_power_DoubleSpinBox.value()
        self.sigMwCwParamsChanged.emit(frequency, power)
        return

    def change_sweep_params(self):
        """ Change start, stop and step frequency of frequency sweep """
        start = self._mw.start_freq_DoubleSpinBox.value()
        stop = self._mw.stop_freq_DoubleSpinBox.value()
        step = self._mw.step_freq_DoubleSpinBox.value()
        power = self._mw.sweep_power_DoubleSpinBox.value()
        self.sigMwSweepParamsChanged.emit(start, stop, step, power)
        return

    def change_runtime(self):
        """ Change time after which microwave sweep is stopped """
        runtime = self._mw.runtime_DoubleSpinBox.value()
        self.sigRuntimeChanged.emit(runtime)
        return

    def save_data(self):
        """ Save the sum plot, the scan marix plot and the scan data """
        filetag = self._mw.save_tag_LineEdit.text()
        cb_range = self.get_matrix_cb_range()

        # Percentile range is None, unless the percentile scaling is selected in GUI.
        pcile_range = None
        if self._mw.odmr_cb_centiles_RadioButton.isChecked():
            low_centile = self._mw.odmr_cb_low_percentile_DoubleSpinBox.value()
            high_centile = self._mw.odmr_cb_high_percentile_DoubleSpinBox.value(
            )
            pcile_range = [low_centile, high_centile]

        self.sigSaveMeasurement.emit(filetag, cb_range, pcile_range)
        return
class SpectrometerGui(GUIBase):
    _modclass = 'SpectrometerGui'
    _modtype = 'gui'

    # declare connectors
    spectrumlogic = Connector(interface='SpectrumLogic')

    def __init__(self, config, **kwargs):
        super().__init__(config=config, **kwargs)

    def on_activate(self):
        """ Definition and initialisation of the GUI.
        """

        self._spectrum_logic = self.spectrumlogic()

        # setting up the window
        self._mw = SpectrometerWindow()

        self._mw.stop_diff_spec_Action.setEnabled(False)
        self._mw.resume_diff_spec_Action.setEnabled(False)
        self._mw.correct_background_Action.setChecked(
            self._spectrum_logic.background_correction)

        # giving the plots names allows us to link their axes together
        self._pw = self._mw.plotWidget  # pg.PlotWidget(name='Counter1')
        self._plot_item = self._pw.plotItem

        # create a new ViewBox, link the right axis to its coordinate system
        self._right_axis = pg.ViewBox()
        self._plot_item.showAxis('right')
        self._plot_item.scene().addItem(self._right_axis)
        self._plot_item.getAxis('right').linkToView(self._right_axis)
        self._right_axis.setXLink(self._plot_item)

        # create a new ViewBox, link the right axis to its coordinate system
        self._top_axis = pg.ViewBox()
        self._plot_item.showAxis('top')
        self._plot_item.scene().addItem(self._top_axis)
        self._plot_item.getAxis('top').linkToView(self._top_axis)
        self._top_axis.setYLink(self._plot_item)
        self._top_axis.invertX(b=True)

        # handle resizing of any of the elements

        self._pw.setLabel('left', 'Fluorescence', units='counts/s')
        self._pw.setLabel('right', 'Number of Points', units='#')
        self._pw.setLabel('bottom', 'Wavelength', units='m')
        self._pw.setLabel('top', 'Relative Frequency', units='Hz')

        # Create an empty plot curve to be filled later, set its pen
        self._curve1 = self._pw.plot()
        self._curve1.setPen(palette.c1, width=2)

        self._curve2 = self._pw.plot()
        self._curve2.setPen(palette.c2, width=2)

        self.update_data()

        # Connect singals
        self._mw.rec_single_spectrum_Action.triggered.connect(
            self.record_single_spectrum)
        self._mw.start_diff_spec_Action.triggered.connect(
            self.start_differential_measurement)
        self._mw.stop_diff_spec_Action.triggered.connect(
            self.stop_differential_measurement)
        self._mw.resume_diff_spec_Action.triggered.connect(
            self.resume_differential_measurement)

        self._mw.save_spectrum_Action.triggered.connect(
            self.save_spectrum_data)
        self._mw.correct_background_Action.triggered.connect(
            self.correct_background)
        self._mw.acquire_background_Action.triggered.connect(
            self.acquire_background)
        self._mw.save_background_Action.triggered.connect(
            self.save_background_data)

        self._mw.restore_default_view_Action.triggered.connect(
            self.restore_default_view)

        self._spectrum_logic.sig_specdata_updated.connect(self.update_data)
        self._spectrum_logic.spectrum_fit_updated_Signal.connect(
            self.update_fit)
        self._spectrum_logic.fit_domain_updated_Signal.connect(
            self.update_fit_domain)

        self._mw.show()

        self._save_PNG = True

        # Internal user input changed signals
        self._mw.fit_domain_min_doubleSpinBox.valueChanged.connect(
            self.set_fit_domain)
        self._mw.fit_domain_max_doubleSpinBox.valueChanged.connect(
            self.set_fit_domain)

        # Internal trigger signals
        self._mw.do_fit_PushButton.clicked.connect(self.do_fit)
        self._mw.fit_domain_all_data_pushButton.clicked.connect(
            self.reset_fit_domain_all_data)

        # fit settings
        self._fsd = FitSettingsDialog(self._spectrum_logic.fc)
        self._fsd.sigFitsUpdated.connect(
            self._mw.fit_methods_ComboBox.setFitFunctions)
        self._fsd.applySettings()
        self._mw.action_FitSettings.triggered.connect(self._fsd.show)

    def on_deactivate(self):
        """ Deinitialisation performed during deactivation of the module.
        """
        # disconnect signals
        self._fsd.sigFitsUpdated.disconnect()

        self._mw.close()

    def show(self):
        """Make window visible and put it above all other windows.
        """
        QtWidgets.QMainWindow.show(self._mw)
        self._mw.activateWindow()
        self._mw.raise_()

    def update_data(self):
        """ The function that grabs the data and sends it to the plot.
        """
        data = self._spectrum_logic.spectrum_data

        # erase previous fit line
        self._curve2.setData(x=[], y=[])

        # draw new data
        self._curve1.setData(x=data[0, :], y=data[1, :])

    def update_fit(self, fit_data, result_str_dict, current_fit):
        """ Update the drawn fit curve and displayed fit results.
        """
        if current_fit != 'No Fit':
            # display results as formatted text
            self._mw.spectrum_fit_results_DisplayWidget.clear()
            try:
                formated_results = units.create_formatted_output(
                    result_str_dict)
            except:
                formated_results = 'this fit does not return formatted results'
            self._mw.spectrum_fit_results_DisplayWidget.setPlainText(
                formated_results)

            # redraw the fit curve in the GUI plot.
            self._curve2.setData(x=fit_data[0, :], y=fit_data[1, :])

    def record_single_spectrum(self):
        """ Handle resume of the scanning without resetting the data.
        """
        self._spectrum_logic.get_single_spectrum()

    def start_differential_measurement(self):

        # Change enabling of GUI actions
        self._mw.stop_diff_spec_Action.setEnabled(True)
        self._mw.start_diff_spec_Action.setEnabled(False)
        self._mw.rec_single_spectrum_Action.setEnabled(False)
        self._mw.resume_diff_spec_Action.setEnabled(False)

        self._spectrum_logic.start_differential_spectrum()

    def stop_differential_measurement(self):
        self._spectrum_logic.stop_differential_spectrum()

        # Change enabling of GUI actions
        self._mw.stop_diff_spec_Action.setEnabled(False)
        self._mw.start_diff_spec_Action.setEnabled(True)
        self._mw.rec_single_spectrum_Action.setEnabled(True)
        self._mw.resume_diff_spec_Action.setEnabled(True)

    def resume_differential_measurement(self):
        self._spectrum_logic.resume_differential_spectrum()

        # Change enabling of GUI actions
        self._mw.stop_diff_spec_Action.setEnabled(True)
        self._mw.start_diff_spec_Action.setEnabled(False)
        self._mw.rec_single_spectrum_Action.setEnabled(False)
        self._mw.resume_diff_spec_Action.setEnabled(False)

    def save_spectrum_data(self):
        self._spectrum_logic.save_spectrum_data()

    def correct_background(self):
        self._spectrum_logic.background_correction = self._mw.correct_background_Action.isChecked(
        )

    def acquire_background(self):
        self._spectrum_logic.get_single_spectrum(background=True)

    def save_background_data(self):
        self._spectrum_logic.save_spectrum_data(background=True)

    def do_fit(self):
        """ Command spectrum logic to do the fit with the chosen fit function.
        """
        fit_function = self._mw.fit_methods_ComboBox.getCurrentFit()[0]
        self._spectrum_logic.do_fit(fit_function)

    def set_fit_domain(self):
        """ Set the fit domain in the spectrum logic to values given by the GUI spinboxes.
        """
        lambda_min = self._mw.fit_domain_min_doubleSpinBox.value()
        lambda_max = self._mw.fit_domain_max_doubleSpinBox.value()

        new_fit_domain = np.array([lambda_min, lambda_max])

        self._spectrum_logic.set_fit_domain(new_fit_domain)

    def reset_fit_domain_all_data(self):
        """ Reset the fit domain to match the full data set.
        """
        self._spectrum_logic.set_fit_domain()

    def update_fit_domain(self, domain):
        """ Update the displayed fit domain to new values (set elsewhere).
        """
        self._mw.fit_domain_min_doubleSpinBox.setValue(domain[0])
        self._mw.fit_domain_max_doubleSpinBox.setValue(domain[1])

    def restore_default_view(self):
        """ Restore the arrangement of DockWidgets to the default
        """
        # Show any hidden dock widgets
        self._mw.spectrum_fit_dockWidget.show()

        # re-dock any floating dock widgets
        self._mw.spectrum_fit_dockWidget.setFloating(False)

        # Arrange docks widgets
        self._mw.addDockWidget(
            QtCore.Qt.DockWidgetArea(QtCore.Qt.TopDockWidgetArea),
            self._mw.spectrum_fit_dockWidget)

        # Set the toolbar to its initial top area
        self._mw.addToolBar(QtCore.Qt.TopToolBarArea, self._mw.measure_ToolBar)
        self._mw.addToolBar(QtCore.Qt.TopToolBarArea,
                            self._mw.background_ToolBar)
        self._mw.addToolBar(QtCore.Qt.TopToolBarArea,
                            self._mw.differential_ToolBar)
        return 0
Example #6
0
class M2ScannerGUI(GUIBase):
    _modclass = 'M2LaserGui'
    _modtype = 'gui'

    sigStartCounter = QtCore.Signal()
    sigStopCounter = QtCore.Signal()

    sigFitChanged = QtCore.Signal(str)
    sigDoFit = QtCore.Signal()

    # declare connectors
    laserlogic = Connector(interface='M2LaserLogic')

    def __init__(self, config, **kwargs):
        super().__init__(config=config, **kwargs)

    def on_activate(self):
        """ Definition and initialisation of the GUI.
        """

        #Connect to laser_logic
        self._laser_logic = self.laserlogic()

        # setting up the window
        self._mw = M2ControllerWindow()

        ###################
        # Connect Signals
        ###################

        self._mw.run_scan_Action.triggered.connect(
            self.start_clicked)  # start_clicked then triggers sigStartCounter
        self._mw.save_scan_Action.triggered.connect(
            self.save_spectrum_data
        )  # start_clicked then triggers sigStartCounter
        self._mw.actionSave_as.triggered.connect(self.change_filepath)
        #      self._mw.restore_default_view_Action.triggered.connect(self.restore_default_view)

        # FROM countergui.py
        # Connect signals for counter
        self.sigStartCounter.connect(self._laser_logic.startCount)
        self.sigStopCounter.connect(self._laser_logic.stopCount)
        self._laser_logic.sigScanComplete.connect(
            self.scanComplete)  #handle the end of scans

        # Handling signals from the logic
        #   signals during terascan
        self._laser_logic.sigCounterUpdated.connect(self.update_data)
        #   update wavelength reading and status reading only
        self._laser_logic.sigUpdate.connect(self.updateGui)

        ####################
        #set up GUI
        ####################

        self._mw.scanType_comboBox.setInsertPolicy = 6  # InsertAlphabetically
        self._mw.scanType_comboBox.addItem("Fine")
        self._mw.scanType_comboBox.addItem("Medium")

        self._mw.scanRate_comboBox.setInsertPolicy = 6  #InsertAlphabetically
        for x in range(0, 14):
            self._mw.scanRate_comboBox.addItem(str(x))

        #####################
        # Connecting user interactions
        ########################

        self._mw.scanType_comboBox.currentIndexChanged.connect(
            self.update_calculated_scan_params)
        self._mw.scanRate_comboBox.currentIndexChanged.connect(
            self.update_calculated_scan_params)
        self._mw.startWvln_doubleSpinBox.valueChanged.connect(
            self.update_calculated_scan_params)
        self._mw.stopWvln_doubleSpinBox.valueChanged.connect(
            self.update_calculated_scan_params)
        self._mw.numScans_spinBox.valueChanged.connect(
            self.update_calculated_scan_params)

        self._mw.plotPoints_checkBox.stateChanged.connect(
            self.update_points_checkbox)
        self._mw.replot_pushButton.clicked.connect(self.replot_pressed)

        #below from countergui.py
        self._pw = self._mw.plotWidget
        self._plot_item = self._pw.plotItem

        # create a new ViewBox, link the right axis to its coordinate system
        self._right_axis = pg.ViewBox()
        self._plot_item.showAxis('right')
        self._plot_item.scene().addItem(self._right_axis)
        self._plot_item.getAxis('right').linkToView(self._right_axis)
        self._right_axis.setXLink(self._plot_item)

        # create a new ViewBox, link the right axis to its coordinate system
        self._top_axis = pg.ViewBox()
        self._plot_item.showAxis('top')
        self._plot_item.scene().addItem(self._top_axis)
        self._plot_item.getAxis('top').linkToView(self._top_axis)
        self._top_axis.setYLink(self._plot_item)
        self._top_axis.invertX(b=True)

        self._pw.setLabel('left', 'Count Rate', units='counts/s')
        ##self._pw.setLabel('right', 'Counts', units='#') #Tget rid of or implement
        self._pw.setLabel('bottom', 'Wavelength', units='nm')
        ##self._pw.setLabel('top', 'Relative Frequency', units='Hz') #TODO implement

        # Create an empty plot curve to be filled later, set its pen
        self._curve1 = self._pw.plot()
        self._curve1.setPen(palette.c1, width=2)

        #self._curve1 = pg.PlotDataItem(
        #        pen=pg.mkPen(palette.c3, style=QtCore.Qt.DotLine),
        #        symbol='s',
        #        symbolPen=palette.c3,
        #        symbolBrush=palette.c3,
        #        symbolSize=5)
        self._pw.addItem(self._curve1)

        #initialize starting calculated scanning parameters
        self.update_calculated_scan_params()  # initialize

        #
        self._mw.wvlnRead_disp.setText("Laser Connected")
        self._mw.status_disp.setText("idle")

        #show the main gui
        self._mw.show()

        # fit settings #just added!
        self._fsd = FitSettingsDialog(self._laser_logic.fc)
        self._fsd.sigFitsUpdated.connect(
            self._mw.fit_methods_ComboBox.setFitFunctions)
        self._fsd.applySettings()

        self._mw.action_FitSettings.triggered.connect(self._fsd.show)
        self._mw.do_fit_PushButton.clicked.connect(self.doFit)
        self.sigDoFit.connect(self._laser_logic.do_fit)
        self.sigFitChanged.connect(self._laser_logic.fc.set_current_fit)
        self._laser_logic.sig_fit_updated.connect(self.updateFit)  #TODO

        self.curve_fit = pg.PlotDataItem(pen=pg.mkPen(palette.c2, width=3),
                                         symbol=None)

    def on_deactivate(self):
        """ Deinitialisation performed during deactivation of the module.
        """
        # disconnect signals #I think this will be unnecessary given that only the gui,
        #not hardware or logic modules will deactivate when deactivate button is pressed
        #(modeled after previously existing Laser module)
        #        self._fsd.sigFitsUpdated.disconnect()

        print('in gui trying to deactivate')
        self._mw.close()

        #if a terascan is running, stop the terascan before deactivating
        if self._laser_logic.module_state() == 'locked':
            print('Terascan is running. Trying to stop now!')
            self._mw.run_scan_Action.setText('Start counter')
            self.sigStopCounter.emit()

            startWvln, stopWvln, scantype, scanrate, numScans = self.get_scan_info(
            )
            self._laser_logic._laser.stop_terascan(scantype, True)

    def show(self):
        """Make window visible and put it above all other windows.
        """
        QtWidgets.QMainWindow.show(self._mw)
        self._mw.activateWindow()
        self._mw.raise_()

    def update_data(self):
        """ The function that grabs the terascan count data and sends it to the plot.
        """
        #        print('update_data called in gui')
        ################ Adapted from spectrometer gui
        data = self._laser_logic.countdata

        #Don't plot initialization 0's
        mask = (data == 0).all(0)
        start_idx = np.argmax(~mask)
        data = data[:, start_idx:]

        # draw new data
        if data.shape[1] > 0:
            self._curve1.setData(x=data[0, :], y=data[1, :])
#       print('updatedata finished in gui')

    def save_spectrum_data(self):
        self._laser_logic.save_spectrum_data()

    def get_scan_info(self):
        #        print('get_scan_info called in gui')
        finerates = [
            20, 10, 5, 2, 1, .5, .2, .1, .05, .02, .01, .005, .002, .001
        ]  # in GHz/s
        mediumrates = [100, 50, 20, 15, 10, 5, 2, 1]  # in GHz/s

        typebox = self._mw.scanType_comboBox.currentText()
        if typebox == 'Fine':
            scanrate = finerates[int(
                self._mw.scanRate_comboBox.currentText())] * 10**9  # in Hz/s
        else:
            indx = int(self._mw.scanRate_comboBox.currentText())
            #handle if index is too high for medium scan
            if indx >= len(mediumrates):
                error_dialog = QtWidgets.QErrorMessage()
                error_dialog.showMessage(
                    'ERROR: index given for medium rates is too high')
                error_dialog.exec()
                self._mw.scanRate_comboBox.setCurrentIndex(7)
                return
                #alternatively (Todo?) change number of scanRate options based on whether we are on Fine or Medium
            else:
                scanrate = mediumrates[int(self._mw.scanRate_comboBox.
                                           currentText())] * 10**9  # in Hz/s

        startwvln = self._mw.startWvln_doubleSpinBox.value() * 10**-9  # in m
        stopwvln = self._mw.stopWvln_doubleSpinBox.value() * 10**-9  #in m

        numscans = self._mw.numScans_spinBox.value()

        return startwvln, stopwvln, typebox.lower(), scanrate, numscans

    def update_calculated_scan_params(self):
        speed_c = 299792458  #speed of light in m/s

        try:
            startWvln, stopWvln, scantype, scanrate, numScans = self.get_scan_info(
            )
        except:
            return  #error handling occurs inside get_scan_info

        midWvln = (stopWvln + startWvln) / 2  #in m
        rangeWvln = (stopWvln - startWvln)  #in m

        startFreq = speed_c / stopWvln  #in Hz
        stopFreq = speed_c / startWvln  #in Hz
        midFreq = (stopFreq + startFreq) / 2  #in Hz
        rangeFreq = stopFreq - startFreq  #in Hz
        scanrate_wvln = scanrate * speed_c / (midFreq**2)  #in m/s

        self._mw.calcDwellTime_disp.setText("0.2 sec \n{0:.4f} pm".format(
            0.2 * scanrate_wvln * 10**12))
        #Scan resolution is 0.2 sec  (based on manually testing, with print
        #and time.time() statements in countloop). May be different on a different computer

        self._mw.calcScanRes_disp.setText(
            "{0:.3f} GHz/s \n{1:.3f} pm/s".format(scanrate * 10**-9,
                                                  scanrate_wvln * 10**12))

        totaltime = numScans * rangeFreq / scanrate
        secs = totaltime % 60
        mins = totaltime // 60
        hrs = mins // 60
        days = hrs // 24

        if days > 0:
            timestr = "{0:.0f} days, {1:.0f} hrs, {2:.0f} min, {3:.0f} sec".format(
                days, hrs, mins, secs)
        elif hrs > 0:
            timestr = "{0:.0f} hrs, {1:.0f} min, {2:.0f} sec".format(
                hrs, mins, secs)
        elif mins > 0:
            timestr = "{0:.0f} min, {1:.0f} sec".format(mins, secs)
        else:
            timestr = "{0:.0f} sec".format(secs)

        self._mw.calcTotalTime_disp.setText(timestr)

    @QtCore.Slot()
    def updateGui(self):
        """ Update labels, the plot and button states with new data. """
        self._mw.wvlnRead_disp.setText("{0:.5f}".format(
            self._laser_logic.current_wavelength))
        self._mw.status_disp.setText(self._laser_logic.current_state)

    def start_clicked(
        self
    ):  #todo: move the logic elements of this function to the logic module
        """ Handling the Start button to stop and restart the counter.
        """
        #        print('start_clicked called in gui')
        if self._laser_logic.module_state() == 'locked':

            print('STOP TERASCAN')

            #   Disable/enable buttons as appropriate, update gui
            self._mw.replot_pushButton.setEnabled(True)
            self._mw.run_scan_Action.setEnabled(False)

            self._mw.status_disp.setText(
                'stopping scan')  #is not being seen.? fixme
            self._laser_logic.current_state = 'stopping scan'  #is not being seen fixme

            self._mw.run_scan_Action.setText(
                'Start counter'
            )  #this also isn't working as far as I can tell fixme

            #   Stop the counter
            self.sigStopCounter.emit()

            #   Enable the "start/stop scan" button Todo maybe wait for signal before enable?
            self._mw.run_scan_Action.setEnabled(True)
        else:
            print('START TERASCAN')

            #   Disable/enable buttons as appropriate, update gui
            #self._laser_logic.current_state = 'starting scan'
            self._mw.status_disp.setText('starting scan')

            self._mw.replot_pushButton.setEnabled(False)
            self._mw.run_scan_Action.setEnabled(False)
            self._mw.run_scan_Action.setText(
                'Stop counter')  #not sure if this is working fixme

            #   Grab terascan parameters
            startWvln, stopWvln, scantype, scanrate, numScans = self.get_scan_info(
            )

            #   More update gui
            self._mw.scanNumber_label.setText(
                str(self._laser_logic.repetition_count))
            self._mw.scanNumber_label.setText("Scan {0:d} of {1:d}".format(
                self._laser_logic.repetition_count + 1, numScans))

            #   Check for input parameter errors. E.G., stop_wavelength should be less than start_wavelength
            if startWvln >= stopWvln:
                error_dialog = QtWidgets.QErrorMessage()
                error_dialog.showMessage(
                    'ERROR: start wavelength must be less than stop wavelength'
                )
                error_dialog.exec()
                return self._laser_logic.module_state()

            #   save terascan parameters to laser module
            self._laser_logic.scanParams = {
                "scanbounds": (startWvln, stopWvln),
                "scantype": scantype,
                "scanrate": scanrate,
                "numScans": numScans
            }

            #   Start the counter
            self.sigStartCounter.emit()

            #   Enable clicking of "start/stop" button
            self._mw.run_scan_Action.setEnabled(True)

#        print('start_clicked finished in gui')
        return self._laser_logic.module_state()

    def update_points_checkbox(self):
        #Change display style of counts plot
        #check if locked?
        if not self._mw.plotPoints_checkBox.isChecked():
            self._curve1.setPen(palette.c1, width=2)
            self._curve1.setSymbol(None)
        else:
            self._curve1.setPen(palette.c3, style=QtCore.Qt.DotLine)
            self._curve1.setSymbol('s')
            self._curve1.setSymbolBrush(palette.c3)
            self._curve1.setSymbolSize(5)
            self._curve1.setSymbolPen(palette.c3)
        #self._pw.addItem(self._curve1)

    def replot_pressed(self):
        #Replot counts to be ordered by wavelength
        if self._laser_logic.module_state() == 'locked':
            pass  #Button should be disabled when module_state is locked, so this should never happen anyway
        else:
            self._laser_logic.order_data()
            self.update_data()
        ### self._mw.replot_pushButton.setDefault(False) #Isn't working? Supposed to reset button style TODO fix

    def scanComplete(self):
        #Handle the end of scans
        startWvln, stopWvln, scantype, scanrate, numScans = self.get_scan_info(
        )

        self._mw.wvlnRead_disp.setText("Scan Completed")
        self._mw.status_disp.setText("idle")

        if numScans != 1:
            # don't automatically save if it's just one scan
            #save scan. Note: save_spectrum_data increases repetition_count by 1
            self._laser_logic.save_spectrum_data()

        if numScans == self._laser_logic.repetition_count or self._laser_logic.repetition_count == 0:
            # Final scan has been completed.

            self._laser_logic.repetition_count = 0
            self._mw.replot_pushButton.setEnabled(True)

            self._laser_logic.module_state.unlock()

        else:
            # Advance to next scan

            self._mw.scanNumber_label.setText("Scan {0:d} of {1:d}".format(
                self._laser_logic.repetition_count + 1, numScans))

            self._laser_logic.module_state.unlock()
            self.sigStartCounter.emit()  #clears out data, etc.

    def change_filepath(self):
        save_dialog = QtWidgets.QFileDialog()
        self._laser_logic.filepath = save_dialog.getExistingDirectory()
        print('Saving in ' + self._laser_logic.filepath)
        self.save_spectrum_data()

    @QtCore.Slot()
    def doFit(self):
        self.sigFitChanged.emit(
            self._mw.fit_methods_ComboBox.getCurrentFit()[0])
        self.sigDoFit.emit()

    @QtCore.Slot()
    def updateFit(self):
        """ Do the configured fit and show it in the plot """
        fit_name = self._laser_logic.fc.current_fit
        fit_result = self._laser_logic.fc.current_fit_result
        fit_param = self._laser_logic.fc.current_fit_param

        if fit_result is not None:
            # display results as formatted text
            self._mw.fit_results_DisplayWidget.clear()
            try:
                formated_results = units.create_formatted_output(
                    fit_result.result_str_dict)
            except:
                formated_results = 'this fit does not return formatted results'
            self._mw.fit_results_DisplayWidget.setPlainText(formated_results)

        if fit_name is not None:
            self._mw.fit_methods_ComboBox.setCurrentFit(fit_name)

        # check which fit method is used and show the curve in the plot accordingly
        if fit_name != 'No Fit':
            self.curve_fit.setData(x=self._laser_logic.wlog_fit_x,
                                   y=self._laser_logic.wlog_fit_y)

            if self.curve_fit not in self._mw.plotWidget.listDataItems():
                self._mw.plotWidget.addItem(self.curve_fit)
        else:
            if self.curve_fit in self._mw.plotWidget.listDataItems():
                self._mw.plotWidget.removeItem(self.curve_fit)


#https://doc.qt.io/qt-5/signalsandslots.html
#When a signal is emitted, the slots connected to it are usually executed immediately, just like a normal
# function call. When this happens, the signals and slots mechanism is totally independent of any GUI event loop.
# Execution of the code following the emit statement will occur once all slots have returned. The situation
# is slightly different when using queued connections; in such a case, the code following the emit keyword
# will continue immediately, and the slots will be executed later.

#Seems wrong??? sigStartQueryLoop is connected NOT through a queued connection and the function doesn't wait
#for the slots to complete. "slot returning" must just mean that the slot has begun executing???
Example #7
0
class WavemeterLogGui(GUIBase):
    _modclass = 'WavemeterLogGui'
    _modtype = 'gui'

    ## declare connectors
    wavemeterloggerlogic1 = Connector(interface='WavemeterLoggerLogic')
    savelogic = Connector(interface='SaveLogic')

    sigStartCounter = QtCore.Signal()
    sigStopCounter = QtCore.Signal()
    sigFitChanged = QtCore.Signal(str)
    sigDoFit = QtCore.Signal()

    def __init__(self, config, **kwargs):
        super().__init__(config=config, **kwargs)

        self.log.debug('The following configuration was found.')

        # checking for the right configuration
        for key in config.keys():
            self.log.info('{0}: {1}'.format(key,config[key]))

    def on_activate(self):
        """ Definition and initialisation of the GUI.
        """

        self._wm_logger_logic = self.wavemeterloggerlogic1()
        self._save_logic = self.savelogic()

        # setting up the window
        self._mw = WavemeterLogWindow()

        ## giving the plots names allows us to link their axes together
        self._pw = self._mw.plotWidget  # pg.PlotWidget(name='Counter1')
        self._plot_item = self._pw.plotItem

        ## create a new ViewBox, link the right axis to its coordinate system
        self._right_axis = pg.ViewBox()
        self._plot_item.showAxis('right')
        self._plot_item.scene().addItem(self._right_axis)
        self._plot_item.getAxis('right').linkToView(self._right_axis)
        self._right_axis.setXLink(self._plot_item)

        ## create a new ViewBox, link the right axis to its coordinate system
        self._top_axis = pg.ViewBox()
        self._plot_item.showAxis('top')
        self._plot_item.scene().addItem(self._top_axis)
        self._plot_item.getAxis('top').linkToView(self._top_axis)
        self._top_axis.setYLink(self._plot_item)
        self._top_axis.invertX(b=True)

        # handle resizing of any of the elements
        self._update_plot_views()
        self._plot_item.vb.sigResized.connect(self._update_plot_views)

        self._pw.setLabel('left', 'Fluorescence', units='counts/s')
        self._pw.setLabel('right', 'Number of Points', units='#')
        self._pw.setLabel('bottom', 'Wavelength', units='nm')
        self._pw.setLabel('top', 'Relative Frequency', units='Hz')

        self._mw.actionStop_resume_scan.triggered.connect(self.stop_resume_clicked)
        self._mw.actionSave_histogram.triggered.connect(self.save_clicked)
        self._mw.actionStart_scan.triggered.connect(self.start_clicked)
        self._mw.actionAuto_range.triggered.connect(self.set_auto_range)

        # defining the parameters to edit
        self._mw.binSpinBox.setValue(self._wm_logger_logic.get_bins())
        self._mw.binSpinBox.editingFinished.connect(self.recalculate_histogram)

        self._mw.minDoubleSpinBox.setValue(self._wm_logger_logic.get_min_wavelength())
        self._mw.minDoubleSpinBox.editingFinished.connect(self.recalculate_histogram)

        self._mw.maxDoubleSpinBox.setValue(self._wm_logger_logic.get_max_wavelength())
        self._mw.maxDoubleSpinBox.editingFinished.connect(self.recalculate_histogram)

        self._mw.show()

        ## Create an empty plot curve to be filled later, set its pen
        self.curve_data_points = pg.PlotDataItem(
            pen=pg.mkPen(palette.c1),
            symbol=None
            )

        self.curve_nm_counts = pg.PlotDataItem(
            pen=pg.mkPen(palette.c2, style=QtCore.Qt.DotLine),
            symbol=None
            )

        self.curve_hz_counts = pg.PlotDataItem(
            pen=pg.mkPen(palette.c6, style=QtCore.Qt.DotLine),
            symbol=None
            )

        self.curve_envelope = pg.PlotDataItem(
            pen=pg.mkPen(palette.c3, style=QtCore.Qt.DotLine),
            symbol=None
            )

        self.curve_fit = pg.PlotDataItem(
            pen=pg.mkPen(palette.c2, width=3),
            symbol=None
            )

        self._pw.addItem(self.curve_data_points)
        self._pw.addItem(self.curve_envelope)
        self._right_axis.addItem(self.curve_nm_counts)
        self._top_axis.addItem(self.curve_hz_counts)

        # scatter plot for time series
        self._spw = self._mw.scatterPlotWidget
        self._spi = self._spw.plotItem
        self._spw.setLabel('bottom', 'Wavelength', units='nm')
        self._spw.setLabel('left', 'Time', units='s')
        self._scatterplot =  pg.ScatterPlotItem(size=10, pen=pg.mkPen(None), brush=pg.mkBrush(255, 255, 255, 20))
        self._spw.addItem(self._scatterplot)
        self._spw.setXLink(self._plot_item)
        self._wm_logger_logic.sig_new_data_point.connect(self.add_data_point)

        self._wm_logger_logic.sig_data_updated.connect(self.updateData)

        # fit settings
        self._fsd = FitSettingsDialog(self._wm_logger_logic.fc)
        self._fsd.sigFitsUpdated.connect(self._mw.fit_methods_ComboBox.setFitFunctions)
        self._fsd.applySettings()

        self._mw.actionFit_settings.triggered.connect(self._fsd.show)
        self._mw.do_fit_PushButton.clicked.connect(self.doFit)
        self.sigDoFit.connect(self._wm_logger_logic.do_fit)
        self.sigFitChanged.connect(self._wm_logger_logic.fc.set_current_fit)
        self._wm_logger_logic.sig_fit_updated.connect(self.updateFit)

    def on_deactivate(self):
        """ Deactivate the module properly.
        """
        self._mw.close()

    def show(self):
        """ Make window visible and put it above all other windows.
        """
        QtWidgets.QMainWindow.show(self._mw)
        self._mw.activateWindow()
        self._mw.raise_()

    def updateData(self):
        """ The function that grabs the data and sends it to the plot.
        """
        self._mw.wavelengthLabel.setText('{0:,.6f} nm '.format(self._wm_logger_logic.current_wavelength))
        self._mw.autoMinLabel.setText('Minimum: {0:3.6f} (nm)   '.format(self._wm_logger_logic.intern_xmin))
        self._mw.autoMaxLabel.setText('Maximum: {0:3.6f} (nm)   '.format(self._wm_logger_logic.intern_xmax))

        x_axis = self._wm_logger_logic.histogram_axis
        x_axis_hz = (
                3.0e17 / x_axis
                - 6.0e17 / (self._wm_logger_logic.get_max_wavelength() + self._wm_logger_logic.get_min_wavelength())
            )

        plotdata = np.array(self._wm_logger_logic.counts_with_wavelength)
        if len(plotdata.shape) > 1 and plotdata.shape[1] == 3:
            self.curve_data_points.setData(plotdata[:, 2:0:-1])

        self.curve_nm_counts.setData(x=x_axis, y=self._wm_logger_logic.histogram)
        self.curve_hz_counts.setData(x=x_axis_hz, y=self._wm_logger_logic.histogram)
        self.curve_envelope.setData(x=x_axis, y=self._wm_logger_logic.envelope_histogram)

    @QtCore.Slot()
    def doFit(self):
        self.sigFitChanged.emit(self._mw.fit_methods_ComboBox.getCurrentFit()[0])
        self.sigDoFit.emit()

    @QtCore.Slot()
    def updateFit(self):
        """ Do the configured fit and show it in the plot """
        fit_name = self._wm_logger_logic.fc.current_fit
        fit_result = self._wm_logger_logic.fc.current_fit_result
        fit_param = self._wm_logger_logic.fc.current_fit_param

        if fit_result is not None:
            # display results as formatted text
            self._mw.fit_results_DisplayWidget.clear()
            try:
                formated_results = units.create_formatted_output(fit_result.result_str_dict)
            except:
                formated_results = 'this fit does not return formatted results'
            self._mw.fit_results_DisplayWidget.setPlainText(formated_results)

        if fit_name is not None:
            self._mw.fit_methods_ComboBox.setCurrentFit(fit_name)

        # check which fit method is used and show the curve in the plot accordingly
        if fit_name != 'No Fit':
            self.curve_fit.setData(
                x=self._wm_logger_logic.wlog_fit_x,
                y=self._wm_logger_logic.wlog_fit_y)

            if self.curve_fit not in self._mw.plotWidget.listDataItems():
                self._mw.plotWidget.addItem(self.curve_fit)
        else:
            if self.curve_fit in self._mw.plotWidget.listDataItems():
                self._mw.plotWidget.removeItem(self.curve_fit)

    def add_data_point(self, point):
        if len(point) >= 3:
            spts = [{'pos': (point[0], point[1]), 'size': 5, 'brush':pg.intColor(point[2]/100, 255)}]
            self._scatterplot.addPoints(spts)

    def stop_resume_clicked(self):
        """ Handling the Start button to stop and restart the counter.
        """
        # If running, then we stop the measurement and enable inputs again
        if self._wm_logger_logic.module_state() == 'running':
            self._mw.actionStop_resume_scan.setText('Resume')
            self._wm_logger_logic.stop_scanning()
            self._mw.actionStop_resume_scan.setEnabled(True)
            self._mw.actionStart_scan.setEnabled(True)
            self._mw.binSpinBox.setEnabled(True)
        # Otherwise, we start a measurement and disable some inputs.
        else:
            self._mw.actionStop_resume_scan.setText('Stop')
            self._wm_logger_logic.start_scanning(resume=True)
            self._mw.actionStart_scan.setEnabled(False)
            self._mw.binSpinBox.setEnabled(False)

    def start_clicked(self):
        """ Handling resume of the scanning without resetting the data.
        """
        if self._wm_logger_logic.module_state() == 'idle':
            self._scatterplot.clear()
            self._wm_logger_logic.start_scanning()

            # Enable the stop button once a scan starts.
            self._mw.actionStop_resume_scan.setText('Stop')
            self._mw.actionStop_resume_scan.setEnabled(True)
            self._mw.actionStart_scan.setEnabled(False)
            self._mw.binSpinBox.setEnabled(False)
            self.recalculate_histogram()
        else:
            self.log.error('Cannot scan, since a scan is alredy running.')

    def save_clicked(self):
        """ Handling the save button to save the data into a file.
        """

        timestamp = datetime.datetime.now()
        filepath = self._save_logic.get_path_for_module(module_name='WavemeterLogger')
        filename = os.path.join(filepath, timestamp.strftime('%Y%m%d-%H%M-%S_wavemeter_log_thumbnail'))

        exporter = pg.exporters.SVGExporter(self._pw.plotItem)
        exporter.export(filename+'.svg')


        self._wm_logger_logic.save_data(timestamp=timestamp)

    def recalculate_histogram(self):
        self._wm_logger_logic.recalculate_histogram(
            bins=self._mw.binSpinBox.value(),
            xmin=self._mw.minDoubleSpinBox.value(),
            xmax=self._mw.maxDoubleSpinBox.value()
        )

    def set_auto_range(self):
        self._mw.minDoubleSpinBox.setValue(self._wm_logger_logic.intern_xmin)
        self._mw.maxDoubleSpinBox.setValue(self._wm_logger_logic.intern_xmax)
        self.recalculate_histogram()

    ## Handle view resizing
    def _update_plot_views(self):
        ## view has resized; update auxiliary views to match
        self._right_axis.setGeometry(self._plot_item.vb.sceneBoundingRect())
        self._top_axis.setGeometry(self._plot_item.vb.sceneBoundingRect())

        ## need to re-update linked axes since this was called
        ## incorrectly while views had different shapes.
        ## (probably this should be handled in ViewBox.resizeEvent)
        self._right_axis.linkedViewChanged(self._plot_item.vb, self._right_axis.XAxis)
        self._top_axis.linkedViewChanged(self._plot_item.vb, self._top_axis.YAxis)
Example #8
0
class ODMRGui(GUIBase):
    """
    This is the GUI Class for ODMR measurements
    """

    _modclass = 'ODMRGui'
    _modtype = 'gui'

    # declare connectors
    _connectors = {'odmrlogic1': 'ODMRLogic', 'savelogic': 'SaveLogic'}

    sigStartODMRScan = QtCore.Signal()
    sigStopODMRScan = QtCore.Signal()
    sigContinueODMRScan = QtCore.Signal()
    sigClearPlots = QtCore.Signal()
    sigMWOn = QtCore.Signal()
    sigMWOff = QtCore.Signal()
    sigMWPowerChanged = QtCore.Signal(float)
    sigMWFreqChanged = QtCore.Signal(float)
    sigFitChanged = QtCore.Signal(str)
    sigDoFit = QtCore.Signal()

    def __init__(self, config, **kwargs):
        super().__init__(config=config, **kwargs)

        self.log.info('The following configuration was found.')

        # checking for the right configuration
        for key in config.keys():
            self.log.info('{0}: {1}'.format(key, config[key]))

    def on_activate(self):
        """ Definition, configuration and initialisation of the ODMR GUI.

        This init connects all the graphic modules, which were created in the
        *.ui file and configures the event handling between the modules.
        """

        self._odmr_logic = self.get_connector('odmrlogic1')
        self._save_logic = self.get_connector('savelogic')

        # Use the inherited class 'Ui_ODMRGuiUI' to create now the
        # GUI element:
        self._mw = ODMRMainWindow()
        self._sd = ODMRSettingDialog()

        # Create a QSettings object for the mainwindow and store the actual GUI layout
        self.mwsettings = QtCore.QSettings("QUDI", "ODMR")
        self.mwsettings.setValue("geometry", self._mw.saveGeometry())
        self.mwsettings.setValue("windowState", self._mw.saveState())

        # Adjust range of scientific spinboxes above what is possible in Qt Designer
        self._mw.frequency_DoubleSpinBox.setMaximum(
            self._odmr_logic.limits.max_frequency)
        self._mw.frequency_DoubleSpinBox.setMinimum(
            self._odmr_logic.limits.min_frequency)
        self._mw.start_freq_DoubleSpinBox.setMaximum(
            self._odmr_logic.limits.max_frequency)
        self._mw.start_freq_DoubleSpinBox.setMinimum(
            self._odmr_logic.limits.min_frequency)
        self._mw.step_freq_DoubleSpinBox.setMaximum(100e9)
        self._mw.stop_freq_DoubleSpinBox.setMaximum(
            self._odmr_logic.limits.max_frequency)
        self._mw.stop_freq_DoubleSpinBox.setMinimum(
            self._odmr_logic.limits.min_frequency)
        self._mw.power_DoubleSpinBox.setMaximum(
            self._odmr_logic.limits.max_power)
        self._mw.power_DoubleSpinBox.setMinimum(
            self._odmr_logic.limits.min_power)

        # connect the parameter update events:
        self._odmr_logic.sigParameterChanged.connect(self.update_parameter)

        # Add save file tag input box
        self._mw.save_tag_LineEdit = QtWidgets.QLineEdit(self._mw)
        self._mw.save_tag_LineEdit.setMaximumWidth(200)
        self._mw.save_tag_LineEdit.setToolTip(
            'Enter a nametag which will be\nadded to the filename.')
        self._mw.save_ToolBar.addWidget(self._mw.save_tag_LineEdit)

        # add a clear button to clear the ODMR plots:
        self._mw.clear_odmr_PushButton = QtWidgets.QPushButton(self._mw)
        self._mw.clear_odmr_PushButton.setText('Clear ODMR')
        self._mw.clear_odmr_PushButton.setToolTip(
            'Clear the plots of the\ncurrent ODMR measurements.')
        self._mw.clear_odmr_PushButton.setEnabled(False)
        self._mw.save_ToolBar.addWidget(self._mw.clear_odmr_PushButton)
        self.sigClearPlots.connect(self._odmr_logic.clear_odmr_plots)

        # Get the image from the logic
        self.odmr_matrix_image = pg.ImageItem(
            self._odmr_logic.ODMR_plot_xy.transpose())
        self.odmr_matrix_image.setRect(
            QtCore.QRectF(self._odmr_logic.mw_start, 0,
                          self._odmr_logic.mw_stop - self._odmr_logic.mw_start,
                          self._odmr_logic.number_of_lines))

        self.odmr_image = pg.PlotDataItem(self._odmr_logic.ODMR_plot_x,
                                          self._odmr_logic.ODMR_plot_y,
                                          pen=pg.mkPen(
                                              palette.c1,
                                              style=QtCore.Qt.DotLine),
                                          symbol='o',
                                          symbolPen=palette.c1,
                                          symbolBrush=palette.c1,
                                          symbolSize=7)

        self.odmr_fit_image = pg.PlotDataItem(self._odmr_logic.ODMR_fit_x,
                                              self._odmr_logic.ODMR_fit_y,
                                              pen=pg.mkPen(palette.c2))

        # Add the display item to the xy and xz VieWidget, which was defined in
        # the UI file.
        self._mw.odmr_PlotWidget.addItem(self.odmr_image)
        self._mw.odmr_PlotWidget.setLabel(axis='left',
                                          text='Counts',
                                          units='Counts/s')
        self._mw.odmr_PlotWidget.setLabel(axis='bottom',
                                          text='Frequency',
                                          units='Hz')

        self._mw.odmr_matrix_PlotWidget.addItem(self.odmr_matrix_image)
        self._mw.odmr_matrix_PlotWidget.setLabel(axis='left',
                                                 text='Matrix Lines',
                                                 units='#')
        self._mw.odmr_matrix_PlotWidget.setLabel(axis='bottom',
                                                 text='Frequency',
                                                 units='Hz')

        self._mw.odmr_PlotWidget.showGrid(x=True, y=True, alpha=0.8)

        # Get the colorscales at set LUT
        my_colors = ColorScaleInferno()
        self.odmr_matrix_image.setLookupTable(my_colors.lut)

        # Configuration of the microwave mode comboWidget
        self._mw.mode_ComboBox.addItem('Off')
        self._mw.mode_ComboBox.addItem('CW')

        ########################################################################
        #                  Configuration of the Colorbar                       #
        ########################################################################

        self.odmr_cb = ColorBar(my_colors.cmap_normed, 100, 0, 100000)

        # adding colorbar to ViewWidget
        self._mw.odmr_cb_PlotWidget.addItem(self.odmr_cb)
        self._mw.odmr_cb_PlotWidget.hideAxis('bottom')
        self._mw.odmr_cb_PlotWidget.hideAxis('left')
        self._mw.odmr_cb_PlotWidget.setLabel('right',
                                             'Fluorescence',
                                             units='counts/s')

        # Connect the buttons and inputs for the odmr colorbar
        self._mw.odmr_cb_manual_RadioButton.clicked.connect(
            self.refresh_matrix)
        self._mw.odmr_cb_centiles_RadioButton.clicked.connect(
            self.refresh_matrix)

        ########################################################################
        #          Configuration of the various display Widgets                #
        ########################################################################

        # Take the default values from logic:
        self._mw.frequency_DoubleSpinBox.setValue(
            self._odmr_logic.mw_frequency)
        self._mw.start_freq_DoubleSpinBox.setValue(self._odmr_logic.mw_start)

        self._mw.step_freq_DoubleSpinBox.setValue(self._odmr_logic.mw_step)
        self._mw.step_freq_DoubleSpinBox.setOpts(
            minStep=1.0)  # set the minimal step to 1Hz.

        self._mw.stop_freq_DoubleSpinBox.setValue(self._odmr_logic.mw_stop)

        self._mw.power_DoubleSpinBox.setValue(self._odmr_logic.mw_power)
        self._mw.power_DoubleSpinBox.setOpts(minStep=0.1)

        self._mw.runtime_DoubleSpinBox.setValue(self._odmr_logic.run_time)
        self._mw.elapsed_time_DisplayWidget.display(
            int(self._odmr_logic.elapsed_time))

        self._sd.matrix_lines_SpinBox.setValue(
            self._odmr_logic.number_of_lines)
        self._sd.clock_frequency_DoubleSpinBox.setValue(
            self._odmr_logic._clock_frequency)

        # fit settings
        self._fsd = FitSettingsDialog(self._odmr_logic.fc)
        self._fsd.sigFitsUpdated.connect(
            self._mw.fit_methods_ComboBox.setFitFunctions)
        self._fsd.applySettings()

        self._mw.action_FitSettings.triggered.connect(self._fsd.show)
        self.sigDoFit.connect(self._odmr_logic.do_fit)
        self.sigFitChanged.connect(self._odmr_logic.fc.set_current_fit)
        self._odmr_logic.sigOdmrFitUpdated.connect(self.update_fit_display)

        # Update the inputed/displayed numbers if return key is hit:

        # If the attribute setKeyboardTracking is set in a SpinBox or
        # DoubleSpinBox the valueChanged method will actually hold on the signal
        #  until the return key is pressed which is pretty useful ;)

        # self._mw.frequency_DoubleSpinBox.setKeyboardTracking(False)

        # Update the inputed/displayed numbers if the cursor has left the field:

        self._mw.frequency_DoubleSpinBox.editingFinished.connect(
            self.change_frequency)
        self._mw.start_freq_DoubleSpinBox.editingFinished.connect(
            self.change_start_freq)
        self._mw.step_freq_DoubleSpinBox.editingFinished.connect(
            self.change_step_freq)
        self._mw.stop_freq_DoubleSpinBox.editingFinished.connect(
            self.change_stop_freq)
        self._mw.power_DoubleSpinBox.editingFinished.connect(self.change_power)
        self._mw.runtime_DoubleSpinBox.editingFinished.connect(
            self.change_runtime)

        self._mw.odmr_cb_max_DoubleSpinBox.valueChanged.connect(
            self.refresh_matrix)
        self._mw.odmr_cb_min_DoubleSpinBox.valueChanged.connect(
            self.refresh_matrix)
        self._mw.odmr_cb_high_percentile_DoubleSpinBox.valueChanged.connect(
            self.refresh_matrix)
        self._mw.odmr_cb_low_percentile_DoubleSpinBox.valueChanged.connect(
            self.refresh_matrix)

        ########################################################################
        #                       Connect signals                                #
        ########################################################################

        # Connect the RadioButtons and connect to the events if they are clicked:
        self._mw.action_run_stop.triggered.connect(self.run_stop)
        self._mw.action_resume_odmr.triggered.connect(self.resume_odmr)
        self._mw.action_Save.triggered.connect(self.save_data)
        self._mw.action_RestoreDefault.triggered.connect(
            self.restore_defaultview)
        self.sigStartODMRScan.connect(self._odmr_logic.start_odmr_scan)
        self.sigStopODMRScan.connect(self._odmr_logic.stop_odmr_scan)
        self.sigContinueODMRScan.connect(self._odmr_logic.continue_odmr_scan)

        self.sigMWOn.connect(self._odmr_logic.MW_on)
        self.sigMWOff.connect(self._odmr_logic.MW_off)
        self.sigMWFreqChanged.connect(self._odmr_logic.set_frequency)
        self.sigMWPowerChanged.connect(self._odmr_logic.set_power)

        # react on an axis change in the logic by adapting the display:
        self._odmr_logic.sigODMRMatrixAxesChanged.connect(
            self.update_matrix_axes)

        # connect the clear button:
        self._mw.clear_odmr_PushButton.clicked.connect(
            self.clear_odmr_plots_clicked)

        self._odmr_logic.sigOdmrPlotUpdated.connect(self.refresh_plot)
        self._odmr_logic.sigOdmrMatrixUpdated.connect(self.refresh_matrix)
        self._odmr_logic.sigOdmrElapsedTimeChanged.connect(
            self.refresh_elapsedtime)
        # connect settings signals
        self._mw.action_Settings.triggered.connect(self.menu_settings)
        self._sd.accepted.connect(self.update_settings)
        self._sd.rejected.connect(self.reject_settings)
        self._sd.buttonBox.button(
            QtWidgets.QDialogButtonBox.Apply).clicked.connect(
                self.update_settings)
        self.reject_settings()
        # Connect stop odmr
        self._odmr_logic.sigOdmrStarted.connect(self.odmr_started)
        self._odmr_logic.sigOdmrStopped.connect(self.odmr_stopped)
        # Combo Widget
        self._mw.mode_ComboBox.activated[str].connect(self.mw_stop)
        # Push Buttons
        self._mw.do_fit_PushButton.clicked.connect(self.do_fit)

        # let the gui react on the signals from the GUI
        self._odmr_logic.sigMicrowaveCWModeChanged.connect(
            self.update_cw_display)
        self._odmr_logic.sigMicrowaveListModeChanged.connect(
            self.update_run_stop_display)

        # Show the Main ODMR GUI:
        self._mw.show()

    def on_deactivate(self):
        """ Reverse steps of activation

        @return int: error code (0:OK, -1:error)
        """
        self._mw.close()
        return 0

    def show(self):
        """Make window visible and put it above all other windows. """
        QtWidgets.QMainWindow.show(self._mw)
        self._mw.activateWindow()
        self._mw.raise_()

    def run_stop(self, is_checked):
        """ Manages what happens if odmr scan is started/stopped. """
        if is_checked:
            # change the axes appearance according to input values:
            self.sigStopODMRScan.emit()
            self.sigStartODMRScan.emit()
            self._mw.action_run_stop.setEnabled(False)
            self._mw.action_resume_odmr.setEnabled(False)
            self._mw.odmr_PlotWidget.removeItem(self.odmr_fit_image)

            # during scan, enable the clear plot possibility.
            self._mw.clear_odmr_PushButton.setEnabled(True)
        else:
            self.sigStopODMRScan.emit()
            self._mw.action_run_stop.setEnabled(False)
            self._mw.action_resume_odmr.setEnabled(False)
            # Disable the clear functionality since that is not needed if no
            # scan is running:
            self._mw.clear_odmr_PushButton.setEnabled(False)

    def update_cw_display(self, cw_on):
        """ Update the display for the cw state of the microwave.

        @param bool cw_on: for True the mw on display will be shown, otherwise
                           mw off will be displayed.
        """
        if cw_on:
            # # prevent any triggering, which results from changing the state of
            # # the combobox:
            # self._mw.mode_ComboBox.blockSignals(True)
            text = 'CW'
        else:
            text = 'Off'

        index = self._mw.mode_ComboBox.findText(text,
                                                QtCore.Qt.MatchFixedString)
        if index >= 0:
            self._mw.mode_ComboBox.setCurrentIndex(index)
        else:
            self.log.warning(
                'No proper state to display was found in the combobox!')

    def update_run_stop_display(self, run_odmr):
        """ Update the display for the odmr measurement.

        @param bool run_odmr: True indicates that the measurement is running and
                              False that it is stopped.
        """
        if run_odmr:
            self._mw.action_resume_odmr.setEnabled(False)
            self._mw.clear_odmr_PushButton.setEnabled(True)
        else:
            self._mw.action_resume_odmr.setEnabled(True)
            self._mw.clear_odmr_PushButton.setEnabled(False)

    def resume_odmr(self, is_checked):
        if is_checked:
            self.sigStopODMRScan.emit()
            self.sigContinueODMRScan.emit()
            self._mw.action_run_stop.setChecked(True)
            self._mw.action_run_stop.setEnabled(False)
            self._mw.action_resume_odmr.setEnabled(False)

            # during scan, enable the clear plot possibility.
            self._mw.clear_odmr_PushButton.setEnabled(True)
        else:
            self.sigStopODMRScan.emit()
            self._mw.action_run_stop.setChecked(False)
            self._mw.action_run_stop.setEnabled(True)
            self._mw.action_resume_odmr.setEnabled(True)
            # Disable the clear functionality since that is not needed if no scan is running:
            self._mw.clear_odmr_PushButton.setEnabled(False)

    def odmr_started(self):
        """ Switch the run/stop button to stop after receiving an odmsStarted
                    signal """
        if not self._mw.action_run_stop.isChecked():
            self._mw.action_run_stop.blockSignals(True)
            self._mw.action_run_stop.setChecked(True)
            self._mw.action_run_stop.blockSignals(False)
        self._mw.action_run_stop.setEnabled(True)

        if not self._mw.action_resume_odmr.isChecked():
            self._mw.action_resume_odmr.blockSignals(True)
            self._mw.action_resume_odmr.setChecked(True)
            self._mw.action_resume_odmr.blockSignals(False)
        self._mw.action_resume_odmr.setEnabled(False)

    def odmr_stopped(self):
        """ Switch the run/stop button to stop after receiving an odmr_stoped
            signal """
        self._mw.action_run_stop.setChecked(False)
        self._mw.action_resume_odmr.setChecked(False)
        self._mw.action_run_stop.setEnabled(True)
        self._mw.action_resume_odmr.setEnabled(True)

    def clear_odmr_plots_clicked(self):
        """ Clear the ODMR plots. """
        self.sigClearPlots.emit()

    def menu_settings(self):
        """ Open the settings menu """
        self._sd.exec_()

    def refresh_plot(self):
        """ Refresh the xy-plot image """
        self.odmr_image.setData(self._odmr_logic.ODMR_plot_x,
                                self._odmr_logic.ODMR_plot_y)

    def refresh_matrix(self):
        """ Refresh the xy-matrix image """
        odmr_image_data = self._odmr_logic.ODMR_plot_xy.transpose()

        cb_range = self.get_matrix_cb_range()

        # Now update image with new color scale, and update colorbar
        self.odmr_matrix_image.setImage(image=odmr_image_data,
                                        levels=(cb_range[0], cb_range[1]))
        self.refresh_odmr_colorbar()

    def update_matrix_axes(self):
        """ Adjust the x and y axes in the image according to the input. """

        self.odmr_matrix_image.setRect(
            QtCore.QRectF(self._odmr_logic.mw_start, 0,
                          self._odmr_logic.mw_stop - self._odmr_logic.mw_start,
                          self._odmr_logic.number_of_lines))

    def refresh_odmr_colorbar(self):
        """ Update the colorbar to a new scaling.

        Calls the refresh method from colorbar.
        """
        cb_range = self.get_matrix_cb_range()
        self.odmr_cb.refresh_colorbar(cb_range[0], cb_range[1])

        self._mw.odmr_cb_PlotWidget.update(
        )  # TODO: Is this necessary?  It is not in refresh_xy_colorbar in confocal gui

    def get_matrix_cb_range(self):
        """ Determines the cb_min and cb_max values for the matrix plot
        """
        matrix_image = self.odmr_matrix_image.image

        # If "Manual" is checked or the image is empty (all zeros), then take manual cb range.
        if self._mw.odmr_cb_manual_RadioButton.isChecked() or np.max(
                matrix_image) == 0.0:
            cb_min = self._mw.odmr_cb_min_DoubleSpinBox.value()
            cb_max = self._mw.odmr_cb_max_DoubleSpinBox.value()

        # Otherwise, calculate cb range from percentiles.
        else:
            # Exclude any zeros (which are typically due to unfinished scan)
            matrix_image_nonzero = matrix_image[np.nonzero(matrix_image)]

            # Read centile range
            low_centile = self._mw.odmr_cb_low_percentile_DoubleSpinBox.value()
            high_centile = self._mw.odmr_cb_high_percentile_DoubleSpinBox.value(
            )

            cb_min = np.percentile(matrix_image_nonzero, low_centile)
            cb_max = np.percentile(matrix_image_nonzero, high_centile)

        cb_range = [cb_min, cb_max]
        return cb_range

    def refresh_elapsedtime(self):
        """ Show current elapsed measurement time """
        self._mw.elapsed_time_DisplayWidget.display(
            int(self._odmr_logic.elapsed_time))

    def update_settings(self):
        """ Write the new settings from the gui to the file. """
        self._odmr_logic.number_of_lines = self._sd.matrix_lines_SpinBox.value(
        )
        self._odmr_logic.set_clock_frequency(
            self._sd.clock_frequency_DoubleSpinBox.value())
        self._odmr_logic.saveRawData = self._sd.save_raw_data_CheckBox.isChecked(
        )

    def reject_settings(self):
        """ Keep the old settings and restores the old settings in the gui. """
        self._sd.matrix_lines_SpinBox.setValue(
            self._odmr_logic.number_of_lines)
        self._sd.clock_frequency_DoubleSpinBox.setValue(
            self._odmr_logic._clock_frequency)
        self._sd.save_raw_data_CheckBox.setChecked(
            self._odmr_logic.saveRawData)

    def do_fit(self):
        self.sigFitChanged.emit(
            self._mw.fit_methods_ComboBox.getCurrentFit()[0])
        self.sigDoFit.emit()

    def update_fit_display(self):
        """ Do the configured fit and show it in the sum plot """
        fit_name = self._odmr_logic.fc.current_fit
        fit_result = self._odmr_logic.fc.current_fit_result
        fit_param = self._odmr_logic.fc.current_fit_param

        if fit_result is not None:
            # display results as formatted text
            self._mw.odmr_fit_results_DisplayWidget.clear()
            try:
                formated_results = units.create_formatted_output(
                    fit_result.result_str_dict)
            except:
                formated_results = 'this fit does not return formatted results'
            self._mw.odmr_fit_results_DisplayWidget.setPlainText(
                formated_results)

        if fit_name is not None:
            self._mw.fit_methods_ComboBox.setCurrentFit(fit_name)

        # check which Fit method is used and remove or add again the
        # odmr_fit_image, check also whether a odmr_fit_image already exists.
        if fit_name != 'No Fit':
            self.odmr_fit_image.setData(x=self._odmr_logic.ODMR_fit_x,
                                        y=self._odmr_logic.ODMR_fit_y)
            if self.odmr_fit_image not in self._mw.odmr_PlotWidget.listDataItems(
            ):
                self._mw.odmr_PlotWidget.addItem(self.odmr_fit_image)
        else:
            if self.odmr_fit_image in self._mw.odmr_PlotWidget.listDataItems():
                self._mw.odmr_PlotWidget.removeItem(self.odmr_fit_image)

        self._mw.odmr_PlotWidget.getViewBox().updateAutoRange()

    def update_parameter(self, param_dict=None):
        """ Update the parameter display in the GUI.

        @param param_dict:
        @return:

        Any change event from the logic should call this update function.
        The update will block the GUI signals from emiting a change back to the
        logic.
        """

        if param_dict is None:
            return

        param = param_dict.get('mw_power')
        if param is not None:
            self._mw.power_DoubleSpinBox.blockSignals(True)
            self._mw.power_DoubleSpinBox.setValue(param)
            self._mw.power_DoubleSpinBox.blockSignals(False)

        param = param_dict.get('mw_frequency')
        if param is not None:
            self._mw.frequency_DoubleSpinBox.blockSignals(True)
            self._mw.frequency_DoubleSpinBox.setValue(param)
            self._mw.frequency_DoubleSpinBox.blockSignals(False)

        param = param_dict.get('mw_start')
        if param is not None:
            self._mw.start_freq_DoubleSpinBox.blockSignals(True)
            self._mw.start_freq_DoubleSpinBox.setValue(param)
            self._mw.start_freq_DoubleSpinBox.blockSignals(False)

        param = param_dict.get('mw_step')
        if param is not None:
            self._mw.step_freq_DoubleSpinBox.blockSignals(True)
            self._mw.step_freq_DoubleSpinBox.setValue(param)
            self._mw.step_freq_DoubleSpinBox.blockSignals(False)

        param = param_dict.get('mw_stop')
        if param is not None:
            self._mw.stop_freq_DoubleSpinBox.blockSignals(True)
            self._mw.stop_freq_DoubleSpinBox.setValue(param)
            self._mw.stop_freq_DoubleSpinBox.blockSignals(False)

        param = param_dict.get('run_time')
        if param is not None:
            self._mw.runtime_DoubleSpinBox.blockSignals(True)
            self._mw.runtime_DoubleSpinBox.setValue(param)
            self._mw.runtime_DoubleSpinBox.blockSignals(False)

        param = param_dict.get('number_of_lines')
        if param is not None:
            self._sd.matrix_lines_SpinBox.blockSignals(True)
            self._sd.matrix_lines_SpinBox.setValue(param)
            self._sd.matrix_lines_SpinBox.blockSignals(False)

        param = param_dict.get('clock_frequency')
        if param is not None:
            self._sd.clock_frequency_DoubleSpinBox.blockSignals(True)
            self._sd.clock_frequency_DoubleSpinBox.setValue(param)
            self._sd.clock_frequency_DoubleSpinBox.blockSignals(False)

    def mw_stop(self, txt):
        """ Stop frequency sweep and change to CW of off"""
        if txt == 'Off':
            self.sigMWOff.emit()
        if txt == 'CW':
            self.change_frequency()
            self.change_power()
            self.sigMWOn.emit()

    ############################################################################
    #                           Change Methods                                 #
    ############################################################################

    def change_frequency(self):
        """ Change CW frequency of microwave source """
        frequency = self._mw.frequency_DoubleSpinBox.value()
        self.sigMWFreqChanged.emit(frequency)

    def change_start_freq(self):
        """ Change start frequency of frequency sweep """
        self._odmr_logic.mw_start = self._mw.start_freq_DoubleSpinBox.value()

    def change_step_freq(self):
        """ Change step size in which frequency is changed """
        self._odmr_logic.mw_step = self._mw.step_freq_DoubleSpinBox.value()

    def change_stop_freq(self):
        """ Change end of frequency sweep """
        self._odmr_logic.mw_stop = self._mw.stop_freq_DoubleSpinBox.value()

    def change_power(self):
        """ Change microwave power """
        power = self._mw.power_DoubleSpinBox.value()
        self.sigMWPowerChanged.emit(power)

    def change_runtime(self):
        """ Change time after which microwave sweep is stopped """
        self._odmr_logic.run_time = self._mw.runtime_DoubleSpinBox.value()

    def save_data(self):
        """ Save the sum plot, the scan marix plot and the scan data """
        filetag = self._mw.save_tag_LineEdit.text()
        cb_range = self.get_matrix_cb_range()

        # Percentile range is None, unless the percentile scaling is selected in GUI.
        pcile_range = None
        if self._mw.odmr_cb_centiles_RadioButton.isChecked():
            low_centile = self._mw.odmr_cb_low_percentile_DoubleSpinBox.value()
            high_centile = self._mw.odmr_cb_high_percentile_DoubleSpinBox.value(
            )
            pcile_range = [low_centile, high_centile]

        self._odmr_logic.save_ODMR_Data(filetag,
                                        colorscale_range=cb_range,
                                        percentile_range=pcile_range)

    def restore_defaultview(self):
        self._mw.restoreGeometry(self.mwsettings.value("geometry", ""))
        self._mw.restoreState(self.mwsettings.value("windowState", ""))
Example #9
0
class MagnetControlGui(GUIBase):
    """ Class for the alignment of the magnetic field.
    """
    _modclass = 'MagnetControlGui'
    _modtype = 'gui'

    # declare connectors
    magnetlogic1 = Connector(interface='MagnetController')
    savelogic = Connector(interface='SaveLogic')

    # signals
    sigFitXChanged = QtCore.Signal(str)
    sigDoXFit = QtCore.Signal(str)
    sigFitYChanged = QtCore.Signal(str)
    sigDoYFit = QtCore.Signal(str)

    def __init__(self, config, **kwargs):
        super().__init__(config=config, **kwargs)

    def on_activate(self):
        """ Initializes all needed UI files and establishes the connectors.

        This method executes the all the inits for the differnt GUIs and passes
        the event argument from fysom to the methods.
        """

        # # Getting an access to all connectors:
        self._magnet_logic = self.get_connector('magnetlogic1')
        self._save_logic = self.get_connector('savelogic')
        self._hardware_state = True

        self.initMainUI()  # initialize the main GUI

        # Get the image from the logic
        self.imageX = pg.PlotDataItem(self._magnet_logic.fluor_plot_x,
                                      self._magnet_logic.fluor_plot_y,
                                      pen=pg.mkPen(palette.c1,
                                                   style=QtCore.Qt.DotLine),
                                      symbol='o',
                                      symbolPen=palette.c1,
                                      symbolBrush=palette.c1,
                                      symbolSize=7)

        self.imageY = pg.PlotDataItem(self._magnet_logic.yfluor_plot_x,
                                      self._magnet_logic.yfluor_plot_y,
                                      pen=pg.mkPen(palette.c1,
                                                   style=QtCore.Qt.DotLine),
                                      symbol='o',
                                      symbolPen=palette.c1,
                                      symbolBrush=palette.c1,
                                      symbolSize=7)

        self.fluor_x_fit_image = pg.PlotDataItem(
            self._magnet_logic.x_scan_fit_x,
            self._magnet_logic.x_scan_fit_y,
            pen=pg.mkPen(palette.c2))
        self.fluor_y_fit_image = pg.PlotDataItem(
            self._magnet_logic.y_scan_fit_x,
            self._magnet_logic.y_scan_fit_y,
            pen=pg.mkPen(palette.c2))

        # Add the display item to the X_scan and Y_scan, which were defined in the UI file.
        self._mw.X_scan.addItem(self.imageX)
        self._mw.X_scan.setLabel(axis='left', text='Counts', units='Counts/s')
        self._mw.X_scan.setLabel(axis='bottom', text='X axis', units='mm')
        self._mw.X_scan.showGrid(x=True, y=True, alpha=0.8)

        self._mw.Y_scan.addItem(self.imageY)
        self._mw.Y_scan.setLabel(axis='left', text='Counts', units='Counts/s')
        self._mw.Y_scan.setLabel(axis='bottom', text='X axis', units='mm')
        self._mw.Y_scan.showGrid(x=True, y=True, alpha=0.8)

        ########################################################################
        #          Configuration of the various display Widgets                #
        ########################################################################
        # Take the default values from logic:
        self._mw.x_start.setValue(self._magnet_logic.x_start)
        self._mw.x_end.setValue(self._magnet_logic.x_end)
        self._mw.step_x.setValue(self._magnet_logic.step_x)

        self._mw.y_start.setValue(self._magnet_logic.y_start)
        self._mw.y_end.setValue(self._magnet_logic.y_end)
        self._mw.step_y.setValue(self._magnet_logic.step_y)

        self._mw.acq_time.setValue(
            self._magnet_logic.fluorescence_integration_time)
        self._mw.N_AF_points.setValue(self._magnet_logic.N_AF_points)

        self._mw.set_x_pos.setValue(self._magnet_logic.set_x_pos)
        self._mw.set_y_pos.setValue(self._magnet_logic.set_y_pos)
        self._mw.set_z_pos.setValue(self._magnet_logic.set_z_pos)

        # fit settings
        self._fsd = FitSettingsDialog(self._magnet_logic.fc)
        self._fsd.sigFitsUpdated.connect(
            self._mw.fit_methods_ComboBox.setFitFunctions)
        self._fsd.sigFitsUpdated.connect(
            self._mw.fit_methods_ComboBox_2.setFitFunctions)
        self._fsd.applySettings()
        self._mw.action_FitSettings.triggered.connect(self._fsd.show)

        ########################################################################
        #                       Connect signals                                #
        ########################################################################
        # Internal user input changed signals
        self._mw.x_start.editingFinished.connect(self.change_x_start)
        self._mw.x_end.editingFinished.connect(self.change_x_end)
        self._mw.step_x.editingFinished.connect(self.change_step_x)

        self._mw.y_start.editingFinished.connect(self.change_y_start)
        self._mw.y_end.editingFinished.connect(self.change_y_end)
        self._mw.step_y.editingFinished.connect(self.change_step_y)

        self._mw.acq_time.editingFinished.connect(self.change_acq_time)
        self._mw.N_AF_points.editingFinished.connect(self.change_N_AF_points)

        self._mw.set_x_pos.editingFinished.connect(self.change_set_x_pos)
        self._mw.set_y_pos.editingFinished.connect(self.change_set_y_pos)
        self._mw.set_z_pos.editingFinished.connect(self.change_set_z_pos)

        self._mw.do_x_fit_PushButton.clicked.connect(self.do_x_fit)
        self.sigDoXFit.connect(self._magnet_logic.do_x_fit,
                               QtCore.Qt.QueuedConnection)
        self._magnet_logic.sigFitXUpdated.connect(self.update_x_fit,
                                                  QtCore.Qt.QueuedConnection)

        self._mw.do_y_fit_PushButton.clicked.connect(self.do_y_fit)
        self.sigDoYFit.connect(self._magnet_logic.do_y_fit,
                               QtCore.Qt.QueuedConnection)
        self._magnet_logic.sigFitYUpdated.connect(self.update_y_fit,
                                                  QtCore.Qt.QueuedConnection)

        #################################################################
        #                           Actions                             #
        #################################################################
        # Connect the scan actions to the events if they are clicked. Connect
        # also the adjustment of the displayed windows.
        self._mw.action_stop_scanning.triggered.connect(self.ready_clicked)

        self._scan_x_start_proxy = pg.SignalProxy(
            self._mw.action_scan_x_start.triggered,
            delay=0.1,
            slot=self.x_scan_clicked)

        self._scan_y_start_proxy = pg.SignalProxy(
            self._mw.action_scan_y_start.triggered,
            delay=0.1,
            slot=self.y_scan_clicked)

        self._magnet_logic.sigPlotXUpdated.connect(self.update_x_plot,
                                                   QtCore.Qt.QueuedConnection)
        self._magnet_logic.sigPlotYUpdated.connect(self.update_y_plot,
                                                   QtCore.Qt.QueuedConnection)
        self._magnet_logic.sigPositionUpdated.connect(
            self.get_current_position)

        self._magnet_logic.signal_stop_scanning.connect(self.ready_clicked)

        self._mw.currposbutton.clicked.connect(self.get_current_position)
        self._mw.appl_pos_butt.clicked.connect(self.apply_position)
        self._mw.Set_pos_button.clicked.connect(self.set_position)

        self.get_current_position()

        return

    def initMainUI(self):
        """ Definition, configuration and initialisation of the confocal GUI.

        This init connects all the graphic modules, which were created in the
        *.ui file and configures the event handling between the modules.
        Moreover it sets default values.
        """
        self._mw = MagnetControlMainWindow()

    def on_deactivate(self):
        """ Reverse steps of activation

        @return int: error code (0:OK, -1:error)
        """
        self._mw.close()
        return 0

    def show(self):
        """Make main window visible and put it above all other windows. """
        # Show the Main Magnet Control GUI:
        self._mw.show()
        self._mw.activateWindow()
        self._mw.raise_()

        ############################################################################
        #                           Change Methods                                 #
        ############################################################################

    def change_x_start(self):
        """ Update the starting position along x axis in the logic according to the GUI.
        """
        self._magnet_logic.x_start = self._mw.x_start.value()
        return

    def change_x_end(self):
        """ Update the final position along x axis in the logic according to the GUI.
        """
        self._magnet_logic.x_end = self._mw.x_end.value()
        return

    def change_step_x(self):
        """ Update step along x axis in the logic according to the GUI.
        """
        self._magnet_logic.step_x = self._mw.step_x.value()
        if self._magnet_logic.x_start != 0 and self._magnet_logic.x_end != 0:
            self._mw.n_x_points.setValue(
                len(
                    np.arange(self._mw.x_start.value(), self._mw.x_end.value(),
                              self._mw.step_x.value())))
            self._magnet_logic.n_x_points = self._mw.n_x_points.value()
        return

    def change_y_start(self):
        """ Update the xy resolution in the logic according to the GUI.
        """
        self._magnet_logic.y_start = self._mw.y_start.value()
        return

    def change_y_end(self):
        """ Update the xy resolution in the logic according to the GUI.
        """
        self._magnet_logic.y_end = self._mw.y_end.value()
        return

    def change_step_y(self):
        """ Update the xy resolution in the logic according to the GUI.
        """
        self._magnet_logic.step_y = self._mw.step_y.value()
        if self._magnet_logic.y_start != 0 and self._magnet_logic.y_end != 0:
            self._mw.n_y_points.setValue(
                len(
                    np.arange(self._mw.y_start.value(), self._mw.y_end.value(),
                              self._mw.step_y.value())))
            self._magnet_logic.n_y_points = self._mw.n_y_points.value()
        return

    def change_acq_time(self):
        """ Update the xy resolution in the logic according to the GUI.
        """
        self._magnet_logic.fluorescence_integration_time = self._mw.acq_time.value(
        )
        return

    def change_N_AF_points(self):
        """ Update the xy resolution in the logic according to the GUI.
        """
        self._magnet_logic.N_AF_points = self._mw.N_AF_points.value()
        return

    def change_set_x_pos(self):
        """ Update the starting position along x axis in the logic according to the GUI.
        """
        self._magnet_logic.set_x_pos = self._mw.set_x_pos.value()

        return

    def change_set_y_pos(self):
        """ Update the final position along x axis in the logic according to the GUI.
        """
        self._magnet_logic.set_y_pos = self._mw.set_y_pos.value()
        return

    def change_set_z_pos(self):
        """ Update step along x axis in the logic according to the GUI.
        """
        self._magnet_logic.set_z_pos = self._mw.set_z_pos.value()
        return

    def get_current_position(self):
        """ Update current actuators position in the logic according to the GUI.
                """
        self._magnet_logic.get_current_position()
        time.sleep(0.1)
        self._mw.curr_x_pos.setValue(self._magnet_logic.curr_x_pos)
        self._mw.curr_y_pos.setValue(self._magnet_logic.curr_y_pos)
        self._mw.curr_z_pos.setValue(self._magnet_logic.curr_z_pos)

        return

    def apply_position(self):
        """ Update current actuators position in the logic according to the GUI.
                """

        self._mw.set_x_pos.setValue(self._mw.curr_x_pos.value())
        self._mw.set_y_pos.setValue(self._mw.curr_y_pos.value())
        self._mw.set_z_pos.setValue(self._mw.curr_z_pos.value())

        self._magnet_logic.set_x_pos = self._mw.set_x_pos.value()
        self._magnet_logic.set_y_pos = self._mw.set_y_pos.value()
        self._magnet_logic.set_z_pos = self._mw.set_z_pos.value()
        return

    def set_position(self):

        self._magnet_logic.set_position()
        return

    def x_scan_clicked(self):
        """ Manages what happens if the xy scan is started. """
        self.disable_scan_actions()
        self._magnet_logic.start_x_scanning(tag='gui')

    def y_scan_clicked(self):
        """ Manages what happens if the xy scan is started. """
        self.disable_scan_actions()
        self._magnet_logic.start_y_scanning(tag='gui')

    def disable_scan_actions(self):
        """ Disables the buttons for scanning.
        """
        # Ensable the stop scanning button
        self._mw.action_stop_scanning.setEnabled(True)

        # Disable the start scan buttons
        self._mw.action_scan_x_start.setEnabled(False)
        self._mw.action_scan_y_start.setEnabled(False)

        self._mw.set_x_pos.setEnabled(False)
        self._mw.set_y_pos.setEnabled(False)
        self._mw.set_z_pos.setEnabled(False)

        self._mw.acq_time.setEnabled(False)

        self._mw.x_start.setEnabled(False)
        self._mw.x_end.setEnabled(False)
        self._mw.step_x.setEnabled(False)

        self._mw.y_start.setEnabled(False)
        self._mw.y_end.setEnabled(False)
        self._mw.step_y.setEnabled(False)

        self._mw.N_AF_points.setEnabled(False)

    def enable_scan_actions(self):
        """ Disables the buttons for scanning.
        """
        # Disable the start scan buttons
        self._mw.action_scan_x_start.setEnabled(True)
        self._mw.action_scan_y_start.setEnabled(True)

        self._mw.set_x_pos.setEnabled(True)
        self._mw.set_y_pos.setEnabled(True)
        self._mw.set_z_pos.setEnabled(True)

        self._mw.acq_time.setEnabled(True)

        self._mw.x_start.setEnabled(True)
        self._mw.x_end.setEnabled(True)
        self._mw.step_x.setEnabled(True)

        self._mw.y_start.setEnabled(True)
        self._mw.y_end.setEnabled(True)
        self._mw.step_y.setEnabled(True)

        self._mw.N_AF_points.setEnabled(True)

    def ready_clicked(self):
        """ Stopp the scan if the state has switched to ready. """
        if self._magnet_logic.module_state() == 'idle':
            self._magnet_logic.stopRequested = True
            self.enable_scan_actions()

    def update_x_plot(self, data_x, data_y):
        """ Refresh the plot widgets with new data. """
        # Update mean signal plot
        self.imageX.setData(data_x, data_y)

    def update_y_plot(self, data_x, data_y):
        """ Refresh the plot widgets with new data. """
        # Update mean signal plot
        self.imageY.setData(data_x, data_y)

    def do_x_fit(self):
        fit_function = self._mw.fit_methods_ComboBox.getCurrentFit()[0]
        self.sigDoXFit.emit(fit_function)
        return

    def do_y_fit(self):
        fit_function = self._mw.fit_methods_ComboBox.getCurrentFit()[0]
        self.sigDoYFit.emit(fit_function)
        return

    def update_x_fit(self, x_data, y_data, result_str_dict, current_fit):
        """ Update the shown fit. """
        if current_fit != 'No Fit':
            # display results as formatted text
            self._mw.x_fit_result.clear()
            try:
                formated_results = units.create_formatted_output(
                    result_str_dict)
            except:
                formated_results = 'this fit does not return formatted results'
            self._mw.x_fit_result.setPlainText(formated_results)

        self._mw.fit_methods_ComboBox.blockSignals(True)
        self._mw.fit_methods_ComboBox.setCurrentFit(current_fit)
        self._mw.fit_methods_ComboBox.blockSignals(False)

        # check which Fit method is used and remove or add again the
        # odmr_fit_image, check also whether a odmr_fit_image already exists.
        if current_fit != 'No Fit':
            self.fluor_x_fit_image.setData(x=x_data, y=y_data)
            if self.fluor_x_fit_image not in self._mw.X_scan.listDataItems():
                self._mw.X_scan.addItem(self.fluor_x_fit_image)
        else:
            if self.fluor_x_fit_image in self._mw.X_scan.listDataItems():
                self._mw.X_scan.removeItem(self.fluor_x_fit_image)

        self._mw.X_scan.getViewBox().updateAutoRange()
        return

    def update_y_fit(self, x_data, y_data, result_str_dict, current_fit):
        """ Update the shown fit. """
        if current_fit != 'No Fit':
            # display results as formatted text
            self._mw.y_fit_result.clear()
            try:
                formated_results = units.create_formatted_output(
                    result_str_dict)
            except:
                formated_results = 'this fit does not return formatted results'
            self._mw.y_fit_result.setPlainText(formated_results)

        self._mw.fit_methods_ComboBox.blockSignals(True)
        self._mw.fit_methods_ComboBox.setCurrentFit(current_fit)
        self._mw.fit_methods_ComboBox.blockSignals(False)

        # check which Fit method is used and remove or add again the
        # odmr_fit_image, check also whether a odmr_fit_image already exists.
        if current_fit != 'No Fit':
            self.fluor_y_fit_image.setData(x=x_data, y=y_data)
            if self.fluor_y_fit_image not in self._mw.Y_scan.listDataItems():
                self._mw.Y_scan.addItem(self.fluor_y_fit_image)
        else:
            if self.fluor_y_fit_image in self._mw.Y_scan.listDataItems():
                self._mw.Y_scan.removeItem(self.fluor_y_fit_image)

        self._mw.Y_scan.getViewBox().updateAutoRange()
        return

    def save_data(self):
        """ Save the sum plot, the scan marix plot and the scan data """
        filetag = self._mw.save_tag_LineEdit.text()
        cb_range = self.get_matrix_cb_range()

        # Percentile range is None, unless the percentile scaling is selected in GUI.
        pcile_range = None
        if self._mw.odmr_cb_centiles_RadioButton.isChecked():
            low_centile = self._mw.odmr_cb_low_percentile_DoubleSpinBox.value()
            high_centile = self._mw.odmr_cb_high_percentile_DoubleSpinBox.value(
            )
            pcile_range = [low_centile, high_centile]

        self.sigSaveMeasurement.emit(filetag, cb_range, pcile_range)
        return
Example #10
0
class NoiseSpectrumGui(GUIBase):
    """ Class for the alignment of the magnetic field.
    """
    _modclass = 'MagnetControlGui'
    _modtype = 'gui'

    # declare connectors
    fitlogic = Connector(interface='FitLogic')
    savelogic = Connector(interface='SaveLogic')

    fc = StatusVar('fits', None)

    def __init__(self, config, **kwargs):
        super().__init__(config=config, **kwargs)

    def on_activate(self):
        """ Initializes all needed UI files and establishes the connectors.

        This method executes the all the inits for the differnt GUIs and passes
        the event argument from fysom to the methods.
        """

        # # Getting an access to all connectors:
        self._save_logic = self.get_connector('savelogic')
        self._fit_logic = self.get_connector('fitlogic')

        self.initMainUI()  # initialize the main GUI

        self.ex = App()
        self.initAppUI()

        self._mw.actionOpen.triggered.connect(self.open_file)

        self.time = np.zeros(1)
        self.counts1 = np.zeros(1)
        self.error1 = np.zeros(1)
        self.counts2 = np.zeros(1)
        self.error2 = np.zeros(1)

        self._mw.sequence_order.editingFinished.connect(
            self.redraw_normalized_plot)
        self._mw.contrast.editingFinished.connect(self.redraw_normalized_plot)

        self._mw.n_FFT.setMaximum(3.0e+06)
        self._mw.n_FFT.setValue(2.0e+06)

        self._mw.calculate_filter_function.clicked.connect(
            self._filter_function_button_fired)
        self._mw.calculate_ns.clicked.connect(
            self._calculate_noise_spectrum_button_fired)

        self.fit_x = np.array([0, 1])
        self.fit_y = np.array([0, 1])

        self.fit_image = pg.PlotDataItem(self.fit_x,
                                         self.fit_y,
                                         pen=pg.mkPen(palette.c3))

        # fit settings
        self._fsd = FitSettingsDialog(self.fc)
        self._fsd.sigFitsUpdated.connect(
            self._mw.fit_methods_ComboBox.setFitFunctions)
        self._fsd.applySettings()
        self._mw.actionFit_Settings.triggered.connect(self._fsd.show)

        self._mw.do_x_fit_PushButton.clicked.connect(self.do_x_fit)

        if 'fits' in self._statusVariables and isinstance(
                self._statusVariables['fits'], dict):
            self.fc.load_from_dict(self._statusVariables['fits'])
        return

    def initMainUI(self):
        """ Definition, configuration and initialisation of the confocal GUI.

        This init connects all the graphic modules, which were created in the
        *.ui file and configures the event handling between the modules.
        Moreover it sets default values.
        """
        self._mw = NVdepthMainWindow()

    def initAppUI(self):
        self.ex.setWindowTitle(self.ex.title)
        self.ex.setGeometry(self.ex.left, self.ex.top, self.ex.width,
                            self.ex.height)
        # self.openFileNamesDialog()
        # self.saveFileDialog()
        #

    def openFileNameDialog(self):
        options = QFileDialog.Options()
        options |= QFileDialog.DontUseNativeDialog
        fileName, _ = QFileDialog.getOpenFileName(
            self.ex,
            "QFileDialog.getOpenFileName()",
            "",
            "All Files (*.dat);;Python Files (*.txt)",
            options=options)

        if fileName:
            print(fileName)
        return str(fileName)

    def on_deactivate(self):
        """ Reverse steps of activation

        @return int: error code (0:OK, -1:error)
        """
        self._mw.close()
        return 0

    def show(self):
        """Make main window visible and put it above all other windows. """
        # Show the Main Magnet Control GUI:
        self._mw.show()
        self._mw.activateWindow()
        self._mw.raise_()

    def open_file(self):
        self.myfile = self.openFileNameDialog()
        self.ex.show()
        self.ex.exec_()

        self.show_data()

    def show_data(self):

        f = open(self.myfile, 'r')
        lines = f.readlines()
        result = []
        for x in lines:
            result.append(x.split('#')[0])
        f.close()

        a = [x for x in result if x != '']
        self.time = np.zeros(len(a))
        self.counts1 = np.zeros(len(a))
        self.error1 = np.zeros(len(a))
        self.counts2 = np.zeros(len(a))
        self.error2 = np.zeros(len(a))

        self._mw.data_plot.clear()
        self._mw.processeddataplot.clear()

        for i in range(len(a)):
            self.time[i] = np.asarray(a[i].split(), dtype=np.float32)[0]
            self.counts1[i] = np.asarray(a[i].split(), dtype=np.float32)[1]
            self.error1[i] = np.asarray(a[i].split(), dtype=np.float32)[3]
            self.counts2[i] = np.asarray(a[i].split(), dtype=np.float32)[2]
            self.error2[i] = np.asarray(a[i].split(), dtype=np.float32)[4]

        self.data_image1 = pg.PlotDataItem(self.time,
                                           self.counts1,
                                           pen=pg.mkPen(
                                               palette.c1,
                                               style=QtCore.Qt.DotLine),
                                           symbol='o',
                                           symbolPen=palette.c1,
                                           symbolBrush=palette.c1,
                                           symbolSize=7)

        self._mw.data_plot.addItem(self.data_image1)
        self.data_image2 = pg.PlotDataItem(self.time,
                                           self.counts2,
                                           pen=pg.mkPen(
                                               palette.c3,
                                               style=QtCore.Qt.DotLine),
                                           symbol='o',
                                           symbolPen=palette.c3,
                                           symbolBrush=palette.c3,
                                           symbolSize=7)

        self._mw.data_plot.addItem(self.data_image2)
        self._mw.data_plot.setLabel(axis='left',
                                    text='Counts',
                                    units='Counts/s')
        self._mw.data_plot.setLabel(axis='bottom', text='time', units='s')
        self._mw.data_plot.showGrid(x=True, y=True, alpha=0.8)

        self.baseline = np.sum(self.counts2 + self.counts1) / len(
            self.counts2) / 2
        C0_up = self.baseline / (1 - 0.01 * self._mw.contrast.value() / 2)
        C0_down = C0_up * (1 - 0.01 * self._mw.contrast.value())
        counts = self.counts2 - self.counts1

        self.T = self.time * 8 * self._mw.sequence_order.value()

        self.normalized_counts = (counts) / (C0_up - C0_down)

        self.normalized_image = pg.PlotDataItem(
            self.time,  #self.T,
            self.normalized_counts,
            pen=pg.mkPen(palette.c2, style=QtCore.Qt.DotLine),
            symbol='o',
            symbolPen=palette.c2,
            symbolBrush=palette.c2,
            symbolSize=7)

        self._mw.processeddataplot.addItem(self.normalized_image)
        self._mw.processeddataplot.setLabel(axis='left',
                                            text='Counts',
                                            units='Counts/s')
        self._mw.processeddataplot.setLabel(axis='bottom',
                                            text='time',
                                            units='s')
        self._mw.processeddataplot.showGrid(x=True, y=True, alpha=0.8)

    def _calculate_noise_spectrum_button_fired(self):

        self._mw.spin_noise_plot.clear()

        S = -np.log(self.normalized_counts) / self.T
        # self.S = np.concatenate((self.S, S), axis=0)

        frequency = 1e+6 * 1e-9 * 0.5e+3 / self.time  # (in Hz)

        # self.frequency = np.concatenate((self.frequency, frequency), axis=0)

        self.noise_spectrum_image = pg.PlotDataItem(
            frequency * 1e-6,
            S,
            pen=pg.mkPen(palette.c5, style=QtCore.Qt.DotLine),
            symbol='o',
            symbolPen=palette.c5,
            symbolBrush=palette.c5,
            symbolSize=7)

        self._mw.spin_noise_plot.addItem(self.noise_spectrum_image)
        self._mw.spin_noise_plot.setLabel(axis='left',
                                          text='Intensity',
                                          units='arb.u.')
        self._mw.spin_noise_plot.setLabel(axis='bottom',
                                          text='Frequency',
                                          units='MHz')
        self._mw.spin_noise_plot.showGrid(x=True, y=True, alpha=0.8)

    def redraw_normalized_plot(self):
        self._mw.processeddataplot.clear()

        self.baseline = np.sum(self.counts2 + self.counts1) / len(
            self.counts2) / 2
        C0_up = self.baseline / (1 - 0.01 * self._mw.contrast.value() / 2)
        C0_down = C0_up * (1 - 0.01 * self._mw.contrast.value())
        counts = self.counts2 - self.counts1

        self.T = self.time * 8 * self._mw.sequence_order.value()

        self.normalized_counts = (counts) / (C0_up - C0_down)

        self.normalized_image = pg.PlotDataItem(
            self.time,  #self.T * 1.0e-6,
            self.normalized_counts,
            pen=pg.mkPen(palette.c2, style=QtCore.Qt.DotLine),
            symbol='o',
            symbolPen=palette.c2,
            symbolBrush=palette.c2,
            symbolSize=7)

        self._mw.processeddataplot.addItem(self.normalized_image)
        self._mw.processeddataplot.setLabel(axis='left',
                                            text='Counts',
                                            units='Counts/s')
        self._mw.processeddataplot.setLabel(axis='bottom',
                                            text='time',
                                            units='us')
        self._mw.processeddataplot.showGrid(x=True, y=True, alpha=0.8)
        return

    def _filter_function(self, tau):
        # generate filter function
        dt = 1e-9
        n = int(tau / dt)

        v = np.zeros(8 * self._mw.sequence_order.value() * n)

        T = np.linspace(0,
                        dt * n * 8 * self._mw.sequence_order.value(),
                        num=8 * self._mw.sequence_order.value() * n)
        v[:n // 2] = 1
        k = n / 2 + 1
        for j in range(8 * self._mw.sequence_order.value() - 1):
            v[(n // 2 + j * n):(n // 2 + j * n + n)] = (-1)**(j + 1)
            k = k + 1
        v[8 * self._mw.sequence_order.value() * n - n // 2:8 *
          self._mw.sequence_order.value() * n] = np.ones((n // 2, ),
                                                         dtype=np.int)
        return T, v

    def _fourier_transform(self, tau):
        T, v = self._filter_function(tau)

        g = int(self._mw.n_FFT.value())

        signalFFT = np.fft.fft(v, g)

        yf = (np.abs(signalFFT)**
              2) * (1e-9) / (8 * self._mw.sequence_order.value())
        xf = np.fft.fftfreq(g, 1e-9)

        self.FFy = yf[0:g]  # take only real part
        self.FFx = xf[0:g]

        f1 = (1 / (2 * self.time[0])) * 1.1  # bigger
        f2 = (1 / (2 * self.time[-1])) * 0.5  # smaller

        yf1 = self.FFy[np.where(self.FFx <= f1)]
        xf1 = self.FFx[np.where(self.FFx <= f1)]

        self.FFy = self.FFy[np.where(xf1 >= f2)]
        self.FFx = self.FFx[np.where(xf1 >= f2)]
        return

    def _filter_function_button_fired(self):
        self._fourier_transform(self.time[self._mw.N_tau.value()])

        self._mw.filter_function.clear()

        self.filter_function_image = pg.PlotDataItem(
            self.FFx * 1e-6,
            self.FFy,
            pen=pg.mkPen(palette.c4, style=QtCore.Qt.DotLine),
            symbol='o',
            symbolPen=palette.c4,
            symbolBrush=palette.c4,
            symbolSize=7)

        self._mw.filter_function.addItem(self.filter_function_image)
        self._mw.filter_function.setLabel(axis='left',
                                          text='Intensity',
                                          units='arb.u.')
        self._mw.filter_function.setLabel(axis='bottom',
                                          text='Frequency',
                                          units='MHz')
        self._mw.filter_function.showGrid(x=True, y=True, alpha=0.8)
        return

    @fc.constructor
    def sv_set_fits(self, val):
        # Setup fit container
        fc = self.fitlogic().make_fit_container('processed', '1d')
        fc.set_units(['s', 'c/s'])
        if isinstance(val, dict) and len(val) > 0:
            fc.load_from_dict(val)
        else:
            d1 = OrderedDict()
            d1['Gaussian peak'] = {
                'fit_function': 'gaussian',
                'estimator': 'peak'
            }

            d1['Lorentzian peak'] = {
                'fit_function': 'lorentzian',
                'estimator': 'peak'
            }
            d1['Two Lorentzian dips'] = {
                'fit_function': 'lorentziandouble',
                'estimator': 'dip'
            }
            d1['N14'] = {
                'fit_function': 'lorentziantriple',
                'estimator': 'N14'
            }
            d1['N15'] = {
                'fit_function': 'lorentziandouble',
                'estimator': 'N15'
            }

            default_fits = OrderedDict()
            default_fits['1d'] = d1['Lorentzian peak']

            fc.load_from_dict(default_fits)
        return fc

    @fc.representer
    def sv_get_fits(self, val):
        """ save configured fits """
        if len(val.fit_list) > 0:
            return val.save_to_dict()
        else:
            return None

    def get_fit_x_functions(self):
        """ Return the hardware constraints/limits
        @return list(str): list of fit function names
        """
        return list(self.fc.fit_list)

    def do_x_fit(self, fit_function=None, x_data=None, y_data=None):
        """
        Execute the currently configured fit on the measurement data. Optionally on passed data
        """
        fit_function = self.get_fit_x_functions()[0]
        if (x_data is None) or (y_data is None):
            x_data = self.time
            y_data = self.normalized_counts

        if fit_function is not None and isinstance(fit_function, str):
            if fit_function in self.get_fit_x_functions():
                self.fc.set_current_fit(fit_function)
            else:
                self.fc.set_current_fit('No Fit')
                if fit_function != 'No Fit':
                    self.log.warning(
                        'Fit function "{0}" not available in ODMRLogic fit container.'
                        ''.format(fit_function))
        self.fit_x, self.fit_y, result = self.fc.do_fit(x_data, y_data)
        print(result)

        if result is None:
            result_str_dict = {}
        else:
            result_str_dict = result.result_str_dict
        self.update_x_fit(self.fit_x, self.fit_y, result_str_dict,
                          self.fc.current_fit)
        return

    def update_x_fit(self, x_data, y_data, result_str_dict, current_fit):
        """ Update the shown fit. """

        if current_fit != 'No Fit':
            # display results as formatted text
            self._mw.x_fit_result.clear()
            try:
                formated_results = units.create_formatted_output(
                    result_str_dict)
            except:
                formated_results = 'this fit does not return formatted results'
            self._mw.x_fit_result.setPlainText(formated_results)

        self._mw.fit_methods_ComboBox.blockSignals(True)
        self._mw.fit_methods_ComboBox.setCurrentFit(current_fit)
        self._mw.fit_methods_ComboBox.blockSignals(False)

        # check which Fit method is used and remove or add again the
        # odmr_fit_image, check also whether a odmr_fit_image already exists.
        if current_fit != 'No Fit':
            self.fit_image.setData(x=x_data, y=y_data)
            if self.fit_image not in self._mw.processeddataplot.listDataItems(
            ):
                self._mw.processeddataplot.addItem(self.fit_image)
        else:
            if self.fit_image in self._mw.processeddataplot.listDataItems():
                self._mw.processeddataplot.removeItem(self.fit_image)

        # self._mw.X_scan.getViewBox().updateAutoRange()
        return