Completed
Push — master ( ae4288...40b1cc )
by
unknown
11s
created

GatedCounterGui.update_analysis_results()   A

Complexity

Conditions 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
c 1
b 0
f 0
dl 0
loc 6
rs 9.4285
1
# -*- coding: utf-8 -*-
2
3
"""
4
This file contains the GUI for control of a Gated Counter.
5
6
Qudi is free software: you can redistribute it and/or modify
7
it under the terms of the GNU General Public License as published by
8
the Free Software Foundation, either version 3 of the License, or
9
(at your option) any later version.
10
11
Qudi is distributed in the hope that it will be useful,
12
but WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
GNU General Public License for more details.
15
16
You should have received a copy of the GNU General Public License
17
along with Qudi. If not, see <http://www.gnu.org/licenses/>.
18
19
Copyright (c) the Qudi Developers. See the COPYRIGHT.txt file at the
20
top-level directory of this distribution and at <https://github.com/Ulm-IQO/qudi/>
21
"""
22
23
import numpy as np
24
import os
25
import pyqtgraph as pg
26
27
from core.module import Connector
28
from core.util import units
29
from gui.guibase import GUIBase
30
from gui.colordefs import QudiPalettePale as palette
31
from gui.colordefs import QudiPalette as palettedark
32
from qtpy import QtCore
33
from qtpy import QtWidgets
34
from qtpy import uic
35
36
37
class GatedCounterMainWindow(QtWidgets.QMainWindow):
38
    """ Create the Main Window based on the *.ui file. """
39
40
    def __init__(self):
41
        # Get the path to the *.ui file
42
        this_dir = os.path.dirname(__file__)
43
        ui_file = os.path.join(this_dir, 'ui_gated_counter_gui.ui')
44
45
        # Load it
46
        super(GatedCounterMainWindow, self).__init__()
47
        uic.loadUi(ui_file, self)
48
        self.show()
49
50
class GatedCounterGui(GUIBase):
51
    """ Main GUI for the Gated Counting. """
52
53
    _modclass = 'GatedCounterGui'
54
    _modtype = 'gui'
55
56
    ## declare connectors
57
    gatedcounterlogic1 = Connector(interface='GatedCounterLogic')
58
    traceanalysislogic1 = Connector(interface='TraceAnalysisLogic')
59
60
61
    sigStartGatedCounter = QtCore.Signal()
62
    sigStopGatedCounter = QtCore.Signal()
63
64
    def __init__(self, config, **kwargs):
65
        super().__init__(config=config, **kwargs)
66
67
        self.log.debug('The following configuration was found.')
68
69
        # checking for the right configuration
70
        for key in config.keys():
71
            self.log.info('{0}: {1}'.format(key,config[key]))
72
73
    def on_activate(self, e=None):
74
        """ Definition and initialisation of the GUI.
75
76
        @param object e: Fysom.event object from Fysom class.
77
                         An object created by the state machine module Fysom,
78
                         which is connected to a specific event (have a look in
79
                         the Base Class). This object contains the passed event,
80
                         the state before the event happened and the destination
81
                         of the state which should be reached after the event
82
                         had happened.
83
        """
84
85
        self._counter_logic = self.gatedcounterlogic1()
86
        self._trace_analysis = self.traceanalysislogic1()
87
88
        self._mw = GatedCounterMainWindow()
89
        self._mw.centralwidget.hide()
90
        self._mw.setDockNestingEnabled(True)
91
        self.set_default_view_main_window()
92
93
        self._gp = self._mw.gated_count_trace_PlotWidget
94
        self._gp.setLabel('left', 'Counts', units='counts/s')
95
        self._gp.setLabel('bottom', 'Number of Gates', units='#')
96
97
        # Create an empty plot curve to be filled later, set its pen
98
        self._trace1 = self._gp.plot()
99
        self._trace1.setPen(palette.c1)
100
101
        self._hp = self._mw.histogram_PlotWidget
102
        self._hp.setLabel('left', 'Occurrences', units='#')
103
        self._hp.setLabel('bottom', 'Counts', units='counts/s')
104
105
        self._histoplot1 = pg.PlotCurveItem()
106
        self._histoplot1.setPen(palette.c1)
107
        self._hp.addItem(self._histoplot1)
108
109
110
        # Configure the fit of the data in the main pulse analysis display:
111
        self._fit_image = pg.PlotCurveItem()
112
        self._hp.addItem(self._fit_image)
113
114
        # setting the x axis length correctly
115
        self._gp.setXRange(0, self._counter_logic.get_count_length())
116
        self._mw.hist_bins_SpinBox.setRange(1, self._counter_logic.get_count_length())
117
118
        # set up the slider with the values of the logic:
119
        self._mw.hist_bins_Slider.setRange(1,self._counter_logic.get_count_length())
120
        self._mw.hist_bins_Slider.setSingleStep(1)
121
122
        # set the counting mode in the logic:
123
        self._counter_logic.set_counting_mode('FINITE_GATED')
124
125
        # Setting default parameters
126
        self._mw.count_length_SpinBox.setValue(self._counter_logic.get_count_length())
127
        self._mw.count_per_readout_SpinBox.setValue(self._counter_logic.get_counting_samples())
128
129
        # Connecting user interactions
130
        # set at first the action buttons in the tab
131
        self._mw.start_counter_Action.triggered.connect(self.start_clicked)
132
        self._mw.stop_counter_Action.triggered.connect(self.stop_clicked)
133
        self._mw.save_measurement_Action.triggered.connect(self.save_clicked)
134
        self._mw.actionRestore_Default.triggered.connect(self.set_default_view_main_window)
135
136
        # that is also the default value of the histogram method in logic
137
        # important: the set of a value should not trigger a redrawn of the
138
        # current empty histogram, which is at the start of the program.
139
        self._mw.hist_bins_SpinBox.setValue(50)
140
        # connect now a reaction on a change of the various input widgets:
141
        self._mw.count_length_SpinBox.editingFinished.connect(self.count_length_changed)
142
        self._mw.count_per_readout_SpinBox.editingFinished.connect(self.count_per_readout_changed)
143
        self._mw.hist_bins_Slider.valueChanged.connect(self.num_bins_changed)
144
        self._mw.hist_bins_SpinBox.valueChanged.connect(self.num_bins_changed)
145
        self._trace_analysis.sigHistogramUpdated.connect(self.update_histogram)
146
147
148
        # starting the physical measurement:
149
        self.sigStartGatedCounter.connect(self._counter_logic.startCount)
150
        self.sigStopGatedCounter.connect(self._counter_logic.stopCount)
151
152
        # connect to signals in the logic:
153
        self._counter_logic.sigCounterUpdated.connect(self.update_trace)
154
        self._counter_logic.sigGatedCounterFinished.connect(self.reset_toolbar_display)
155
156
        # configuration of the combo widget
157
        fit_functions = self._trace_analysis.get_fit_functions()
158
        self._mw.fit_methods_ComboBox.addItems(fit_functions)
159
160
        # Push buttons
161
        self._mw.fit_PushButton.clicked.connect(self.fit_clicked)
162
163
        # Connect analysis result update
164
        self._trace_analysis.sigAnalysisResultsUpdated.connect(self.update_analysis_results)
165
166
167
    def on_deactivate(self):
168
        """ Deinitialisation performed during deactivation of the module.
169
        """
170
        self._mw.close()
171
172
    def show(self):
173
        """ Make main window visible and put it above all other windows. """
174
175
        self._mw.show()
176
        self._mw.activateWindow()
177
        self._mw.raise_()
178 View Code Duplication
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
179
    def set_default_view_main_window(self):
180
        """ Restore the default view and arrangement of the DockWidgets. """
181
182
        self._mw.control_param_DockWidget.setFloating(False)
183
        self._mw.count_trace_DockWidget.setFloating(False)
184
        self._mw.histogram_DockWidget.setFloating(False)
185
186
        # QtCore.Qt.LeftDockWidgetArea        0x1
187
        # QtCore.Qt.RightDockWidgetArea       0x2
188
        # QtCore.Qt.TopDockWidgetArea         0x4
189
        # QtCore.Qt.BottomDockWidgetArea      0x8
190
        # QtCore.Qt.AllDockWidgetAreas        DockWidgetArea_Mask
191
        # QtCore.Qt.NoDockWidgetArea          0
192
193
        self._mw.addDockWidget(QtCore.Qt.DockWidgetArea(4), self._mw.control_param_DockWidget)
194
        self._mw.addDockWidget(QtCore.Qt.DockWidgetArea(8), self._mw.count_trace_DockWidget)
195
        self._mw.addDockWidget(QtCore.Qt.DockWidgetArea(8), self._mw.histogram_DockWidget)
196
197
    def start_clicked(self):
198
        """ Handling the Start button to stop and restart the counter. """
199
200
        if self._counter_logic.module_state() != 'locked':
201
            self.sigStartGatedCounter.emit()
202
            self._mw.start_counter_Action.setEnabled(False)
203
            self._mw.stop_counter_Action.setEnabled(True)
204
205
    def stop_clicked(self):
206
        """ Handling the Stop button to stop and restart the counter. """
207
208
        if self._counter_logic.module_state() == 'locked':
209
            self.sigStopGatedCounter.emit()
210
            self.reset_toolbar_display()
211
212
    def reset_toolbar_display(self):
213
        """ Run this method after finishing the counting to get initial status
214
        """
215
216
        self._mw.start_counter_Action.setEnabled(True)
217
        self._mw.stop_counter_Action.setEnabled(False)
218
219
    def save_clicked(self):
220
        """ Trigger the save routine in the logic.
221
            Pass also the chosen filename.
222
        """
223
224
        file_desc = self._mw.filetag_LineEdit.text()
225
        if file_desc == '':
226
            file_desc = 'gated_counter'
227
228
        trace_file_desc = file_desc + '_trace'
229
        self._counter_logic.save_current_count_trace(name_tag=trace_file_desc)
230
231
        # histo_file_desc = file_desc + '_histogram'
232
        # self._trace_analysis.save_histogram(file_desc=histo_file_desc)
233
234
    def count_length_changed(self):
235
        """ Handle the change of the count_length and send it to the measurement.
236
        """
237
238
        self._counter_logic.set_count_length(self._mw.count_length_SpinBox.value())
239
        self._gp.setXRange(0, self._counter_logic.get_count_length())
240
        self._mw.hist_bins_Slider.setRange(1, self._counter_logic.get_count_length())
241
        self._mw.hist_bins_SpinBox.setRange(1, self._counter_logic.get_count_length())
242
243
    def count_per_readout_changed(self):
244
        """ Handling the change of the oversampling and sending it to the measurement.
245
        """
246
        self._counter_logic.set_counting_samples(samples=self._mw.count_per_readout_SpinBox.value())
247
248
    def update_trace(self):
249
        """ The function that grabs the data and sends it to the plot. """
250
251
        if self._counter_logic.module_state() == 'locked':
252
            self._trace1.setData(x=np.arange(0, self._counter_logic.get_count_length()),
253
                                 y=self._counter_logic.countdata[0] )
254
255
    def update_histogram(self):
256
        """ Update procedure for the histogram to display the new data. """
257
258
        self._histoplot1.setData(x=self._trace_analysis.hist_data[0],
259
                                 y=self._trace_analysis.hist_data[1],
260
                                 stepMode=True, fillLevel=0,
261
                                 brush=palettedark.c1)
262
263
    def num_bins_changed(self, num_bins):
264
        """
265
        @param int num_bins: Number of bins to be set in the trace.
266
267
        This method is executed by both events, the valueChanged of the SpinBox
268
        and value changed in the Slider. Until now, there appears no infinite
269
        signal loop. It that occur one day, than this method has to be split
270
        in two seperate methods.
271
        """
272
273
        self._trace_analysis.set_num_bins_histogram(num_bins)
274
        self._mw.hist_bins_SpinBox.setValue(num_bins)
275
        self._mw.hist_bins_Slider.setValue(num_bins)
276
277
278
    def fit_clicked(self):
279
        """ Do the configured fit and show it in the sum plot """
280
        self._mw.fit_param_TextEdit.clear()
281
282
        current_fit_function = self._mw.fit_methods_ComboBox.currentText()
283
284
        fit_x, fit_y, fit_param_dict, fit_result = self._trace_analysis.do_fit(fit_function=current_fit_function)
285
286
        self._fit_image.setData(x=fit_x, y=fit_y, pen=pg.mkPen(palette.c2, width=2))
287
288
        if len(fit_param_dict) == 0:
289
            fit_result = 'No Fit parameter passed.'
290
291
        else:
292
            fit_result = units.create_formatted_output(fit_param_dict)
293
        self._mw.fit_param_TextEdit.setPlainText(fit_result)
294
295
        return
296
297
298
    def update_analysis_results(self):
299
        """ Update the spin flip probability and the fidelities. """
300
301
        self._mw.spin_flip_prob_DSpinBox.setValue(self._trace_analysis.spin_flip_prob*100)
302
        self._mw.fidelity_left_DSpinBox.setValue(self._trace_analysis.fidelity_left*100)
303
        self._mw.fidelity_right_DSpinBox.setValue(self._trace_analysis.fidelity_right*100)
304