Completed
Push — pulsed_with_queued_connections ( dd8f54...e734dd )
by Jan
02:09
created

PredefinedMethodsTab.__init__()   A

Complexity

Conditions 1

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
1
# -*- coding: utf-8 -*-
2
3
"""
4
This file contains the QuDi main GUI for pulsed measurements.
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
from qtpy import QtGui
24
from qtpy import QtCore
25
from qtpy import QtWidgets
26
from qtpy import uic
27
import numpy as np
28
import os
29
from collections import OrderedDict
30
import pyqtgraph as pg
31
import re
32
import inspect
33
import datetime
34
35
36
from gui.guibase import GUIBase
37
from gui.colordefs import QudiPalettePale as palette
38
from gui.colordefs import QudiPalette as palettedark
39
from qtwidgets.scientific_spinbox import ScienDSpinBox, ScienSpinBox
40
#from gui.pulsed.pulse_editor import PulseEditor
41
from core.util.mutex import Mutex
42
from core.util import units
43
from gui.pulsed.pulse_editors import BlockEditor, BlockOrganizer
44
from logic.sampling_functions import SamplingFunctions
45
46
47
#FIXME: Display the Pulse
48
#FIXME: save the length in sample points (bins)
49
#FIXME: adjust the length to the bins
50
#FIXME: Later that should be able to round up the values directly within
51
52
53
class PulsedMeasurementMainWindow(QtWidgets.QMainWindow):
54
    def __init__(self):
55
        # Get the path to the *.ui file
56
        this_dir = os.path.dirname(__file__)
57
        ui_file = os.path.join(this_dir, 'ui_pulsed_maingui.ui')
58
59
        # Load it
60
        super(PulsedMeasurementMainWindow, self).__init__()
61
62
        uic.loadUi(ui_file, self)
63
        self.show()
64
65
66
class PulseAnalysisTab(QtWidgets.QWidget):
67
    def __init__(self):
68
        # Get the path to the *.ui file
69
        this_dir = os.path.dirname(__file__)
70
        ui_file = os.path.join(this_dir, 'ui_pulse_analysis.ui')
71
        # Load it
72
        super().__init__()
73
        uic.loadUi(ui_file, self)
74
75
76
class PulseGeneratorTab(QtWidgets.QWidget):
77
    def __init__(self):
78
        # Get the path to the *.ui file
79
        this_dir = os.path.dirname(__file__)
80
        ui_file = os.path.join(this_dir, 'ui_pulse_editor.ui')
81
        # Load it
82
        super().__init__()
83
        uic.loadUi(ui_file, self)
84
85
86
class PulseExtractionTab(QtWidgets.QWidget):
87
    def __init__(self):
88
        # Get the path to the *.ui file
89
        this_dir = os.path.dirname(__file__)
90
        ui_file = os.path.join(this_dir, 'ui_pulse_extraction.ui')
91
        # Load it
92
        super().__init__()
93
        uic.loadUi(ui_file, self)
94
95
96
class AnalysisSettingDialog(QtWidgets.QDialog):
97
    def __init__(self):
98
        # Get the path to the *.ui file
99
        this_dir = os.path.dirname(__file__)
100
        ui_file = os.path.join(this_dir, 'ui_pulsed_main_gui_settings_analysis.ui')
101
102
        # Load it
103
        super().__init__()
104
105
        uic.loadUi(ui_file, self)
106
107
108
class GeneratorSettingsDialog(QtWidgets.QDialog):
109
    def __init__(self):
110
        # Get the path to the *.ui file
111
        this_dir = os.path.dirname(__file__)
112
        ui_file = os.path.join(this_dir, 'ui_pulsed_main_gui_settings_block_gen.ui')
113
114
        # Load it
115
        super().__init__()
116
117
        uic.loadUi(ui_file, self)
118
119
120
class PredefinedMethodsTab(QtWidgets.QWidget):
121
    def __init__(self):
122
        # Get the path to the *.ui file
123
        this_dir = os.path.dirname(__file__)
124
        ui_file = os.path.join(this_dir, 'ui_predefined_methods.ui')
125
126
        # Load it
127
        super().__init__()
128
129
        uic.loadUi(ui_file, self)
130
131
class PredefinedMethodsConfigDialog(QtWidgets.QDialog):
132
    def __init__(self):
133
        # Get the path to the *.ui file
134
        this_dir = os.path.dirname(__file__)
135
        ui_file = os.path.join(this_dir, 'ui_predefined_methods_config.ui')
136
137
        # Load it
138
        super().__init__()
139
140
        uic.loadUi(ui_file, self)
141
142
143
class PulsedMeasurementGui(GUIBase):
144
    """ This is the main GUI Class for pulsed measurements. """
145
146
    _modclass = 'PulsedMeasurementGui'
147
    _modtype = 'gui'
148
149
    ## declare connectors
150
    _in = {'pulsedmasterlogic': 'PulsedMasterLogic',
151
           'savelogic': 'SaveLogic'}
152
153
    def __init__(self, config, **kwargs):
154
        super().__init__(config=config, **kwargs)
155
156
        self.log.info('The following configuration was found.')
157
158
        # checking for the right configuration
159
        for key in config.keys():
160
            self.log.info('{}: {}'.format(key,config[key]))
161
162
        # that variable is for testing issues and can be deleted if not needed:
163
        self._write_chunkwise = False
164
165
    def on_activate(self, e=None):
166
        """ Initialize, connect and configure the pulsed measurement GUI.
167
168
        @param object e: Fysom.event object from Fysom class.
169
                         An object created by the state machine module Fysom,
170
                         which is connected to a specific event (have a look in
171
                         the Base Class). This object contains the passed event,
172
                         the state before the event happened and the destination
173
                         of the state which should be reached after the event
174
                         had happened.
175
176
        Establish general connectivity and activate the different tabs of the
177
        GUI.
178
        """
179
        self._pulsed_master_logic = self.get_in_connector('pulsedmasterlogic')
180
        self._save_logic = self.get_in_connector('savelogic')
181
182
        self._mw = PulsedMeasurementMainWindow()
183
        self._pa = PulseAnalysisTab()
184
        self._pg = PulseGeneratorTab()
185
        self._pe = PulseExtractionTab()
186
        self._pm = PredefinedMethodsTab()
187
188
        self._mw.tabWidget.addTab(self._pa, 'Analysis')
189
        self._mw.tabWidget.addTab(self._pe, 'Pulse Extraction')
190
        self._mw.tabWidget.addTab(self._pg, 'Pulse Generator')
191
        self._mw.tabWidget.addTab(self._pm, 'Predefined Methods')
192
193
        self.setup_toolbar()
194
        self._activate_analysis_settings_ui(e)
195
        self._activate_analysis_ui(e)
196
        self.setup_extraction_ui()
197
198
        self._activate_generator_settings_ui(e)
199
        self._activate_pulse_generator_ui(e)
200
201
        self.show()
202
203
    def on_deactivate(self, e):
204
        """ Undo the Definition, configuration and initialisation of the pulsed
205
            measurement GUI.
206
207
        @param object e: Fysom.event object from Fysom class. A more detailed
208
                         explanation can be found in the method initUI.
209
210
        This deactivation disconnects all the graphic modules, which were
211
        connected in the initUI method.
212
        """
213
        self._deactivate_analysis_settings_ui(e)
214
        self._deactivate_analysis_ui(e)
215
216
        self._deactivate_generator_settings_ui(e)
217
        self._deactivate_pulse_generator_ui(e)
218
219
        self._mw.close()
220
221
    def show(self):
222
        """Make main window visible and put it above all other windows. """
223
        QtWidgets.QMainWindow.show(self._mw)
224
        self._mw.activateWindow()
225
        self._mw.raise_()
226
227
    ###########################################################################
228
    ###   Methods related to Settings for the 'Pulse Generator' tab:        ###
229
    ###########################################################################
230
    def _activate_generator_settings_ui(self, e):
231
        """ Initialize, connect and configure the pulse generator settings to be displayed in the
232
        editor.
233
234
        @param object e: Fysom.event object from Fysom class. A more detailed
235
                         explanation can be found in the method initUI.
236
        """
237
        self._gs = GeneratorSettingsDialog()
238
        self._gs.accepted.connect(self.apply_generator_settings)
239
        self._gs.rejected.connect(self.keep_former_generator_settings)
240
        self._gs.buttonBox.button(QtWidgets.QDialogButtonBox.Apply).clicked.connect(
241
            self.apply_generator_settings)
242
        # Here the names of all function to show are stored
243
        self._functions_to_show = []
244
245
        # here are all the names of the predefined methods are saved.
246
        self._predefined_methods_list = []
247
        # here all names of the chosen predefined methods are saved
248
        self._predefined_methods_to_show = []
249
        # create a config for the predefined methods:
250
        self._pm_cfg = PredefinedMethodsConfigDialog()
251
        self._pm_cfg.accepted.connect(self.apply_predefined_methods_config)
252
        self._pm_cfg.rejected.connect(self.keep_former_predefined_methods_config)
253
        self._pm_cfg.buttonBox.button(QtWidgets.QDialogButtonBox.Apply).clicked.connect(
254
            self.apply_predefined_methods_config)
255
256
        if 'predefined_methods_to_show' in self._statusVariables:
257
            self._predefined_methods_to_show = self._statusVariables['predefined_methods_to_show']
258
        if 'functions_to_show' in self._statusVariables:
259
            self._functions_to_show = self._statusVariables['functions_to_show']
260
261
        # connect the menu to the actions:
262
        self._mw.action_Settings_Block_Generation.triggered.connect(self.show_generator_settings)
263
        self._mw.action_Predefined_Methods_Config.triggered.connect(self.show_predefined_methods_config)
264
265
        self._pulsed_master_logic.sigPredefinedSequencesUpdated.connect(self.predefined_methods_changed)
266
267
        # Create function config dialog
268
        self._create_function_config()
269
        return
270
271
    def _deactivate_generator_settings_ui(self, e):
272
        """ Disconnects the configuration of the Settings for the 'Pulse Generator' Tab.
273
274
        @param object e: Fysom.event object from Fysom class. A more detailed
275
                         explanation can be found in the method initUI.
276
        """
277
        self._statusVariables['predefined_methods_to_show'] = self._predefined_methods_to_show
278
        self._statusVariables['functions_to_show'] = self._functions_to_show
279
280
        self._gs.accepted.disconnect()
281
        self._gs.rejected.disconnect()
282
        self._gs.buttonBox.button(QtWidgets.QDialogButtonBox.Apply).clicked.disconnect()
283
        self._gs.close()
284
285
        self._pm_cfg.accepted.disconnect()
286
        self._pm_cfg.rejected.disconnect()
287
        self._pm_cfg.buttonBox.button(QtWidgets.QDialogButtonBox.Apply).clicked.disconnect()
288
        self._pm_cfg.close()
289
290
291
        self._pulsed_master_logic.sigPredefinedSequencesUpdated.disconnect()
292
        self._mw.action_Settings_Block_Generation.triggered.disconnect()
293
        self._mw.action_Predefined_Methods_Config.triggered.disconnect()
294
        return
295
296
    def show_generator_settings(self):
297
        """
298
        Opens the generator settings menu.
299
        """
300
        self._gs.exec_()
301
        return
302
303
    def _create_function_config(self):
304
        # Add in the settings menu within the groupbox widget all the available math_functions,
305
        # based on the list from the Logic. Right now, the GUI objects are inserted the 'hard' way,
306
        # like it is done in the Qt-Designer.
307
        # FIXME: Make a nicer way of displaying the available functions, maybe with a Table!
308
        _encoding = QtWidgets.QApplication.UnicodeUTF8
309
        objectname = self._gs.objectName()
310
        for index, func_name in enumerate(list(SamplingFunctions().func_config)):
311
            name_label = 'func_' + str(index)
312
            setattr(self._gs, name_label, QtWidgets.QLabel(self._gs.groupBox))
313
            label = getattr(self._gs, name_label)
314
            label.setObjectName(name_label)
315
            self._gs.gridLayout_3.addWidget(label, index, 0, 1, 1)
316
            label.setText(QtWidgets.QApplication.translate(objectname, func_name, None, _encoding))
317
318
            name_checkbox = 'checkbox_' + str(index)
319
            setattr(self._gs, name_checkbox, QtWidgets.QCheckBox(self._gs.groupBox))
320
            checkbox = getattr(self._gs, name_checkbox)
321
            checkbox.setObjectName(name_checkbox)
322
            self._gs.gridLayout_3.addWidget(checkbox, index, 1, 1, 1)
323
            checkbox.setText(QtWidgets.QApplication.translate(objectname, '', None, _encoding))
324
        # Check all functions that are in the _functions_to_show list.
325
        # If no such list is present take the first 3 functions as default
326
        if len(self._functions_to_show) > 0:
327
            for func in self._functions_to_show:
328
                index = list(SamplingFunctions().func_config).index(func)
329
                name_checkbox = 'checkbox_' + str(index)
330
                checkbox = getattr(self._gs, name_checkbox)
331
                checkbox.setCheckState(QtCore.Qt.Checked)
332
        else:
333
            for index in range(3):
334
                name_checkbox = 'checkbox_' + str(index)
335
                checkbox = getattr(self._gs, name_checkbox)
336
                checkbox.setCheckState(QtCore.Qt.Checked)
337
        return
338
339
    def apply_generator_settings(self):
340
        """
341
        Write new generator settings from the gui to the file.
342
        """
343
        new_config = SamplingFunctions().func_config
344
        for index, func_name in enumerate(list(SamplingFunctions().func_config)):
345
            name_checkbox = 'checkbox_' + str(index)
346
            checkbox = getattr(self._gs, name_checkbox)
347
            if not checkbox.isChecked():
348
                name_label = 'func_' + str(index)
349
                func = getattr(self._gs, name_label)
350
                del new_config[func.text()]
351
        self._functions_to_show = list(new_config)
352
        if self.block_editor.function_config != new_config:
353
            self.block_editor.set_function_config(new_config)
354
        return
355
356
    def keep_former_generator_settings(self):
357
        """
358
        Keep the old generator settings and restores them in the gui.
359
        """
360
        old_config = self.block_editor.function_config
361
        for index, func_name in enumerate(list(SamplingFunctions().func_config)):
362
            name_checkbox = 'checkbox_' + str(index)
363
            checkbox = getattr(self._gs, name_checkbox)
364
            if func_name in old_config:
365
                checkbox.setChecked(True)
366
            else:
367
                checkbox.setChecked(False)
368
        return
369
370
    def show_predefined_methods_config(self):
371
        """ Opens the Window for the config of predefined methods."""
372
        self._pm_cfg.show()
373
        self._pm_cfg.raise_()
374
375
    def predefined_methods_changed(self, methods_dict):
376
        """
377
378
        @param methods_dict:
379
        @return:
380
        """
381
        self._predefined_methods_list = list(methods_dict)
382
        # create all GUI elements
383
        self._create_predefined_methods(methods_dict)
384
        # check all checkboxes that correspond to the methods to show in the config dialogue
385
        for method_name in self._predefined_methods_to_show:
386
            if method_name in self._predefined_methods_list:
387
                index = self._predefined_methods_list.index(method_name)
388
                checkbox = getattr(self._pm_cfg, 'checkbox_' + str(index))
389
                checkbox.setChecked(True)
390
            else:
391
                del_index = self._predefined_methods_to_show.index(method_name)
392
                del self._predefined_methods_to_show[del_index]
393
        # apply the chosen methods to the methods dialogue
394
        self.apply_predefined_methods_config()
395
        return
396
397
    def _create_predefined_methods(self, methods_dict):
398
        """
399
        Initializes the GUI elements for the predefined methods and the corresponding config
400
401
        @param methods_dict:
402
        @return:
403
        """
404
        for index, method_name in enumerate(list(methods_dict)):
405
            # create checkboxes for the config dialogue
406
            name_checkbox = 'checkbox_' + str(index)
407
            setattr(self._pm_cfg, name_checkbox, QtWidgets.QCheckBox(self._pm_cfg.scrollArea))
408
            checkbox = getattr(self._pm_cfg, name_checkbox)
409
            checkbox.setObjectName(name_checkbox)
410
            checkbox.setText(method_name)
411
            checkbox.setChecked(False)
412
            self._pm_cfg.verticalLayout.addWidget(checkbox)
413
414
            # Create the widgets for the predefined methods dialogue
415
            # Create GroupBox for the method to reside in
416
            groupBox = QtWidgets.QGroupBox(self._pm)
417
            groupBox.setAlignment(QtCore.Qt.AlignLeft)
418
            groupBox.setTitle(method_name)
419
            # Create layout within the GroupBox
420
            gridLayout = QtWidgets.QGridLayout(groupBox)
421
            # Create generate buttons
422
            gen_button = QtWidgets.QPushButton(groupBox)
423
            gen_button.setText('Generate')
424
            gen_button.setObjectName('gen_' + method_name)
425
            gen_button.clicked.connect(self.generate_predefined_clicked)
426
            sauplo_button = QtWidgets.QPushButton(groupBox)
427
            sauplo_button.setText('GenSaUpLo')
428
            sauplo_button.setObjectName('sauplo_' + method_name)
429
            sauplo_button.clicked.connect(self.generate_sauplo_predefined_clicked)
430
            # inspect current method to extract the parameters
431
            inspected = inspect.signature(methods_dict[method_name])
432
            # run through all parameters of the current method and create the widgets
433
            for param_index, param_name in enumerate(inspected.parameters):
434
                # get default value of the parameter
435
                default_val = inspected.parameters[param_name].default
436
                if default_val is inspect._empty:
437
                    self.log.error('The method "{0}" in the logic has an argument "{1}" without a '
438
                                   'default value!\nAssign a default value to that, otherwise a '
439
                                   'type estimation is not possible!\nCreation of the viewbox '
440
                                   'aborted.'.format('generate_' + method_name, param_name))
441
                    return
442
                # create a label for the parameter
443
                param_label = QtWidgets.QLabel(groupBox)
444
                param_label.setText(param_name)
445
                # create proper input widget for the parameter depending on the type of default_val
446
                if type(default_val) is bool:
447
                    input_obj = QtWidgets.QCheckBox(groupBox)
448
                    input_obj.setChecked(default_val)
449
                elif type(default_val) is float:
450
                    input_obj = ScienDSpinBox(groupBox)
451
                    input_obj.setMaximum(np.inf)
452
                    input_obj.setMinimum(-np.inf)
453
                    if 'amp' in param_name:
454
                        input_obj.setSuffix('V')
455
                    elif 'freq' in param_name:
456
                        input_obj.setSuffix('Hz')
457
                    elif 'length' in param_name or 'time' in param_name or 'period' in param_name or 'tau' in param_name:
458
                        input_obj.setSuffix('s')
459
                    input_obj.setMinimumSize(QtCore.QSize(80, 0))
460
                    input_obj.setValue(default_val)
461
                elif type(default_val) is int:
462
                    input_obj = ScienSpinBox(groupBox)
463
                    input_obj.setMaximum(2**31 - 1)
464
                    input_obj.setMinimum(-2**31 + 1)
465
                    input_obj.setValue(default_val)
466
                elif type(default_val) is str:
467
                    input_obj = QtWidgets.QLineEdit(groupBox)
468
                    input_obj.setMinimumSize(QtCore.QSize(80, 0))
469
                    input_obj.setText(default_val)
470
                else:
471
                    self.log.error('The method "{0}" in the logic has an argument "{1}" with is not'
472
                                   ' of the valid types str, float, int or bool!\nChoose one of '
473
                                   'those default values! Creation of the viewbox aborted.'
474
                                   ''.format('generate_' + method_name, param_name))
475
                gridLayout.addWidget(param_label, 0, param_index, 1, 1)
476
                gridLayout.addWidget(input_obj, 1, param_index, 1, 1)
477
                setattr(self._pm, method_name + '_param_' + str(param_index) + '_Widget', input_obj)
478
479
            gridLayout.addWidget(gen_button, 0, len(inspected.parameters), 1, 1)
480
            gridLayout.addWidget(sauplo_button, 1, len(inspected.parameters), 1, 1)
481
            # attach the GroupBox widget to the predefined methods widget.
482
            setattr(self._pm, method_name + '_GroupBox', groupBox)
483
            self._pm.verticalLayout.addWidget(groupBox)
484
        return
485
486
    def keep_former_predefined_methods_config(self):
487
        for index, name in enumerate(self._predefined_methods_list):
488
            groupbox = getattr(self._pm, name + '_GroupBox')
489
            checkbox = getattr(self._pm_cfg, 'checkbox_' + str(index))
490
            checkbox.setChecked(groupbox.isVisible())
491
        return
492
493
    def apply_predefined_methods_config(self):
494
        self._predefined_methods_to_show = []
495
        for index, name in enumerate(self._predefined_methods_list):
496
            groupbox = getattr(self._pm, name + '_GroupBox')
497
            checkbox = getattr(self._pm_cfg, 'checkbox_' + str(index))
498
            is_checked = checkbox.isChecked()
499
            groupbox.setVisible(is_checked)
500
            if is_checked:
501
                self._predefined_methods_to_show.append(name)
502
503
        self._pm.hintLabel.setVisible(len(self._predefined_methods_to_show) == 0)
504
        return
505
506
    ###########################################################################
507
    ###   Methods related to Tab 'Pulse Generator' in the Pulsed Window:    ###
508
    ###########################################################################
509
    def _activate_pulse_generator_ui(self, e):
510
        """ Initialize, connect and configure the 'Pulse Generator' Tab.
511
512
        @param object e: Fysom.event object from Fysom class. A more detailed
513
                         explanation can be found in the method initUI.
514
        """
515
        # connect signals of input widgets
516
        self._pg.gen_sample_freq_DSpinBox.editingFinished.connect(self.generator_settings_changed, QtCore.Qt.QueuedConnection)
517
        self._pg.gen_laserchannel_ComboBox.currentIndexChanged.connect(self.generator_settings_changed, QtCore.Qt.QueuedConnection)
518
        self._pg.gen_activation_config_ComboBox.currentIndexChanged.connect(self.generator_settings_changed, QtCore.Qt.QueuedConnection)
519
        # connect signals of buttons
520
        self._pg.sample_ensemble_PushButton.clicked.connect(self.sample_ensemble_clicked)
521
        # self._pg.sample_sequence_PushButton.clicked.connect(self.sample_sequence_clicked)
522
        self._pg.sauplo_ensemble_PushButton.clicked.connect(self.sauplo_ensemble_clicked)
523
524
        self._pg.block_add_last_PushButton.clicked.connect(self.block_add_last_clicked)
525
        self._pg.block_del_last_PushButton.clicked.connect(self.block_del_last_clicked)
526
        self._pg.block_add_sel_PushButton.clicked.connect(self.block_add_sel_clicked)
527
        self._pg.block_del_sel_PushButton.clicked.connect(self.block_del_sel_clicked)
528
        self._pg.block_clear_PushButton.clicked.connect(self.block_clear_clicked)
529
        self._pg.organizer_add_last_PushButton.clicked.connect(self.organizer_add_last_clicked)
530
        self._pg.organizer_del_last_PushButton.clicked.connect(self.organizer_del_last_clicked)
531
        self._pg.organizer_add_sel_PushButton.clicked.connect(self.organizer_add_sel_clicked)
532
        self._pg.organizer_del_sel_PushButton.clicked.connect(self.organizer_del_sel_clicked)
533
        self._pg.organizer_clear_PushButton.clicked.connect(self.organizer_clear_clicked)
534
535
        self._pg.curr_block_generate_PushButton.clicked.connect(self.editor_generate_block_clicked)
536
        self._pg.curr_block_del_PushButton.clicked.connect(self.editor_delete_block_clicked)
537
        self._pg.curr_block_load_PushButton.clicked.connect(self.editor_load_block_clicked)
538
        self._pg.curr_ensemble_generate_PushButton.clicked.connect(self.editor_generate_ensemble_clicked)
539
        self._pg.curr_ensemble_del_PushButton.clicked.connect(self.editor_delete_ensemble_clicked)
540
        self._pg.curr_ensemble_load_PushButton.clicked.connect(self.editor_load_ensemble_clicked)
541
542
        # connect update signals from pulsed_master_logic
543
        self._pulsed_master_logic.sigBlockEnsembleSampled.connect(self.sample_ensemble_finished)
544
        # self._pulsed_master_logic.sigSequenceSampled.connect(self.sample_sequence_finished)
545
        self._pulsed_master_logic.sigSavedPulseBlocksUpdated.connect(self.update_block_dict)
546
        self._pulsed_master_logic.sigSavedBlockEnsemblesUpdated.connect(self.update_ensemble_dict)
547
        # self._pulsed_master_logic.sigSavedSequencesUpdated.connect(self.update_sequence_list)
548
        self._pulsed_master_logic.sigGeneratorSettingsUpdated.connect(self.update_generator_settings)
549
550
        self._pulsed_master_logic.sigCurrentPulseBlockUpdated.connect(self.load_block_in_editor)
551
        self._pulsed_master_logic.sigCurrentBlockEnsembleUpdated.connect(self.load_ensemble_in_editor)
552
        # self._pulsed_master_logic.sigCurrentSequenceUpdated.connect(self.)
553
554
        self.block_organizer = BlockOrganizer(self._pg.block_organizer_TableWidget)
555
        self.block_editor = BlockEditor(self._pg.block_editor_TableWidget)
556
557
        # Apply hardware constraints to input widgets
558
        self._gen_apply_hardware_constraints()
559
560
        # Fill initial values from logic into input widgets
561
        self._pulsed_master_logic.request_generator_init_values()
562
        return
563
564
    def _deactivate_pulse_generator_ui(self, e):
565
        """ Disconnects the configuration for 'Pulse Generator Tab.
566
567
        @param object e: Fysom.event object from Fysom class. A more detailed
568
                         explanation can be found in the method initUI.
569
        """
570
        # disconnect signals of input widgets
571
        self._pg.gen_sample_freq_DSpinBox.editingFinished.disconnect()
572
        self._pg.gen_laserchannel_ComboBox.currentIndexChanged.disconnect()
573
        self._pg.gen_activation_config_ComboBox.currentIndexChanged.disconnect()
574
        # disconnect signals of buttons
575
        self._pg.sample_ensemble_PushButton.clicked.disconnect()
576
        # self._pg.sample_sequence_PushButton.clicked.disconnect()
577
        self._pg.sauplo_ensemble_PushButton.clicked.disconnect()
578
        self._pg.block_add_last_PushButton.clicked.disconnect()
579
        self._pg.block_del_last_PushButton.clicked.disconnect()
580
        self._pg.block_add_sel_PushButton.clicked.disconnect()
581
        self._pg.block_del_sel_PushButton.clicked.disconnect()
582
        self._pg.block_clear_PushButton.clicked.disconnect()
583
        self._pg.organizer_add_last_PushButton.clicked.disconnect()
584
        self._pg.organizer_del_last_PushButton.clicked.disconnect()
585
        self._pg.organizer_add_sel_PushButton.clicked.disconnect()
586
        self._pg.organizer_del_sel_PushButton.clicked.disconnect()
587
        self._pg.organizer_clear_PushButton.clicked.disconnect()
588
        self._pg.curr_block_generate_PushButton.clicked.disconnect()
589
        self._pg.curr_block_del_PushButton.clicked.disconnect()
590
        self._pg.curr_block_load_PushButton.clicked.disconnect()
591
        self._pg.curr_ensemble_generate_PushButton.clicked.disconnect()
592
        self._pg.curr_ensemble_del_PushButton.clicked.disconnect()
593
        self._pg.curr_ensemble_load_PushButton.clicked.disconnect()
594
        # disconnect update signals from pulsed_master_logic
595
        self._pulsed_master_logic.sigBlockEnsembleSampled.disconnect()
596
        # self._pulsed_master_logic.sigSequenceSampled.disconnect()
597
        self._pulsed_master_logic.sigSavedPulseBlocksUpdated.disconnect()
598
        self._pulsed_master_logic.sigSavedBlockEnsemblesUpdated.disconnect()
599
        # self._pulsed_master_logic.sigSavedSequencesUpdated.disconnect()
600
        self._pulsed_master_logic.sigGeneratorSettingsUpdated.disconnect()
601
        self._pulsed_master_logic.sigCurrentPulseBlockUpdated.disconnect()
602
        self._pulsed_master_logic.sigCurrentBlockEnsembleUpdated.disconnect()
603
        # self._pulsed_master_logic.sigCurrentSequenceUpdated.disconnect()
604
        return
605
606
    def _gen_apply_hardware_constraints(self):
607
        """
608
        Retrieve the constraints from pulser hardware and apply these constraints to the pulse
609
        generator GUI elements.
610
        """
611
        # block signals
612
        self._pg.gen_activation_config_ComboBox.blockSignals(True)
613
        self._pg.gen_sample_freq_DSpinBox.blockSignals(True)
614
        # apply constraints
615
        pulser_constr, dummy = self._pulsed_master_logic.get_hardware_constraints()
616
        self._pg.gen_activation_config_ComboBox.addItems(list(pulser_constr['activation_config']))
617
        self._pg.gen_sample_freq_DSpinBox.setMinimum(pulser_constr['sample_rate']['min'])
618
        self._pg.gen_sample_freq_DSpinBox.setMaximum(pulser_constr['sample_rate']['max'])
619
        # unblock signals
620
        self._pg.gen_activation_config_ComboBox.blockSignals(False)
621
        self._pg.gen_sample_freq_DSpinBox.blockSignals(False)
622
        return
623
624
    def generator_settings_changed(self):
625
        """
626
627
        @return:
628
        """
629
        sample_rate = self._pg.gen_sample_freq_DSpinBox.value()
630
        laser_channel = self._pg.gen_laserchannel_ComboBox.currentText()
631
        activation_config_name = self._pg.gen_activation_config_ComboBox.currentText()
632
        amplitude_dict = self._pulsed_master_logic._generator_logic.amplitude_dict
633
634
        self._pulsed_master_logic.generator_settings_changed(activation_config_name, laser_channel,
635
                                                             sample_rate, amplitude_dict)
636
        return
637
638
    def update_generator_settings(self, activation_config_name, activation_config, sample_rate,
639
                                   amplitude_dict, laser_channel):
640
        """
641
642
        @param activation_config_name:
643
        @param activation_config:
644
        @param sample_rate:
645
        @param amplitude_dict:
646
        @param laser_channel:
647
        @return:
648
        """
649
        # block signals
650
        self._pg.gen_sample_freq_DSpinBox.blockSignals(True)
651
        self._pg.gen_laserchannel_ComboBox.blockSignals(True)
652
        self._pg.gen_activation_config_ComboBox.blockSignals(True)
653
        # activation config
654
        index = self._pg.gen_activation_config_ComboBox.findText(activation_config_name)
655
        self._pg.gen_activation_config_ComboBox.setCurrentIndex(index)
656
        display_str = ''
657
        for chnl in activation_config:
658
            display_str += chnl + ' | '
659
        display_str = display_str[:-3]
660
        self._pg.gen_activation_config_LineEdit.setText(display_str)
661
        self._pg.gen_analog_channels_SpinBox.setValue(
662
            len([chnl for chnl in activation_config if 'a_ch' in chnl]))
663
        self._pg.gen_digital_channels_SpinBox.setValue(
664
            len([chnl for chnl in activation_config if 'd_ch' in chnl]))
665
        # laser channel
666
        self._pg.gen_laserchannel_ComboBox.clear()
667
        self._pg.gen_laserchannel_ComboBox.addItems(activation_config)
668
        index = self._pg.gen_laserchannel_ComboBox.findText(laser_channel)
669
        self._pg.gen_laserchannel_ComboBox.setCurrentIndex(index)
670
        # sample rate
671
        self._pg.gen_sample_freq_DSpinBox.setValue(sample_rate)
672
        # set activation config in block editor
673
        if self.block_editor.activation_config != activation_config:
674
            if self.block_editor.activation_config is None:
675
                self.block_editor.set_activation_config(activation_config)
676
                self.apply_generator_settings()
677
            else:
678
                self.block_editor.set_activation_config(activation_config)
679
        # unblock signals
680
        self._pg.gen_sample_freq_DSpinBox.blockSignals(False)
681
        self._pg.gen_laserchannel_ComboBox.blockSignals(False)
682
        self._pg.gen_activation_config_ComboBox.blockSignals(False)
683
        return
684
685
    def block_add_last_clicked(self):
686
        """
687
688
        @return:
689
        """
690
        self.block_editor.insert_rows(self._pg.block_editor_TableWidget.rowCount(), 1)
691
        return
692
693
    def block_del_last_clicked(self):
694
        """
695
696
        @return:
697
        """
698
        self.block_editor.delete_row(self._pg.block_editor_TableWidget.rowCount())
699
        return
700
701
    def block_add_sel_clicked(self):
702
        """
703
704
        @return:
705
        """
706
        index = self._pg.block_editor_TableWidget.currentRow()
707
        self.block_editor.insert_rows(index + 1, 1)
708
        return
709
710
    def block_del_sel_clicked(self):
711
        """
712
713
        @return:
714
        """
715
        index = self._pg.block_editor_TableWidget.currentRow()
716
        self.block_editor.delete_row(index)
717
        return
718
719
    def block_clear_clicked(self):
720
        """
721
722
        @return:
723
        """
724
        self.block_editor.clear_table()
725
        return
726
727
    def organizer_add_last_clicked(self):
728
        """
729
730
        @return:
731
        """
732
        self.block_organizer.insert_rows(self._pg.block_organizer_TableWidget.rowCount(), 1)
733
        return
734
735
    def organizer_del_last_clicked(self):
736
        """
737
738
        @return:
739
        """
740
        self.block_organizer.delete_row(self._pg.block_organizer_TableWidget.rowCount() - 1)
741
        return
742
743
    def organizer_add_sel_clicked(self):
744
        """
745
746
        @return:
747
        """
748
        index = self._pg.block_organizer_TableWidget.currentRow()
749
        self.block_organizer.insert_rows(index + 1, 1)
750
        return
751
752
    def organizer_del_sel_clicked(self):
753
        """
754
755
        @return:
756
        """
757
        index = self._pg.block_organizer_TableWidget.currentRow()
758
        self.block_organizer.delete_row(index)
759
        return
760
761
    def organizer_clear_clicked(self):
762
        """
763
764
        @return:
765
        """
766
        self.block_organizer.clear_table()
767
        return
768
769
    def editor_generate_block_clicked(self):
770
        name = self._pg.curr_block_name_LineEdit.text()
771
        if name == '':
772
            self.log.error('No name has been entered for the PulseBlock to be generated.')
773
            return
774
        block_object = self.block_editor.generate_block_object(name)
775
        self._pulsed_master_logic.save_pulse_block(name, block_object)
776
        return
777
778
    def editor_delete_block_clicked(self):
779
        name = self._pg.saved_blocks_ComboBox.currentText()
780
        self._pulsed_master_logic.delete_pulse_block(name)
781
        return
782
783
    def editor_load_block_clicked(self):
784
        name = self._pg.saved_blocks_ComboBox.currentText()
785
        self._pulsed_master_logic.load_pulse_block(name)
786
        return
787
788
    def editor_generate_ensemble_clicked(self):
789
        name = self._pg.curr_ensemble_name_LineEdit.text()
790
        if name == '':
791
            self.log.error('No name has been entered for the PulseBlockEnsemble to be generated.')
792
            return
793
        rotating_frame = self._pg.curr_ensemble_rot_frame_CheckBox.isChecked()
794
        ensemble_object = self.block_organizer.generate_ensemble_object(name, rotating_frame)
795
        self._pulsed_master_logic.save_block_ensemble(name, ensemble_object)
796
        return
797
798
    def editor_delete_ensemble_clicked(self):
799
        name = self._pg.saved_ensembles_ComboBox.currentText()
800
        self._pulsed_master_logic.delete_block_ensemble(name)
801
        return
802
803
    def editor_load_ensemble_clicked(self):
804
        name = self._pg.saved_ensembles_ComboBox.currentText()
805
        self._pulsed_master_logic.load_block_ensemble(name)
806
        return
807
808
    def load_block_in_editor(self, block_obj):
809
        self.block_editor.load_pulse_block(block_obj)
810
        return
811
812
    def load_ensemble_in_editor(self, ensemble_obj):
813
        self.block_organizer.load_pulse_block_ensemble(ensemble_obj)
814
        return
815
816
    def update_block_dict(self, block_dict):
817
        """
818
819
        @param block_dict:
820
        @return:
821
        """
822
        self.block_organizer.set_block_dict(block_dict)
823
        self._pg.saved_blocks_ComboBox.blockSignals(True)
824
        self._pg.saved_blocks_ComboBox.clear()
825
        self._pg.saved_blocks_ComboBox.addItems(list(block_dict))
826
        self._pg.saved_blocks_ComboBox.blockSignals(False)
827
        return
828
829
    def update_ensemble_dict(self, ensemble_dict):
830
        """
831
832
        @param ensemble_dict:
833
        @return:
834
        """
835
        # block signals
836
        self._pg.gen_ensemble_ComboBox.blockSignals(True)
837
        self._pg.saved_ensembles_ComboBox.blockSignals(True)
838
        # update gen_sequence_ComboBox items
839
        self._pg.gen_ensemble_ComboBox.clear()
840
        self._pg.gen_ensemble_ComboBox.addItems(list(ensemble_dict))
841
        self._pg.saved_ensembles_ComboBox.clear()
842
        self._pg.saved_ensembles_ComboBox.addItems(list(ensemble_dict))
843
        # unblock signals
844
        self._pg.gen_ensemble_ComboBox.blockSignals(False)
845
        self._pg.saved_ensembles_ComboBox.blockSignals(False)
846
        return
847
848
    # def update_sequence_list(self, sequence_list):
849
    #     """
850
    #
851
    #     @param sequence_list:
852
    #     @return:
853
    #     """
854
    #     # block signals
855
    #     self._pg.gen_sequence_ComboBox.blockSignals(True)
856
    #     # update gen_sequence_ComboBox items
857
    #     self._pg.gen_sequence_ComboBox.clear()
858
    #     self._pg.gen_sequence_ComboBox.addItems(sequence_list)
859
    #     # unblock signals
860
    #     self._pg.gen_sequence_ComboBox.blockSignals(False)
861
    #     return
862
863
    def sample_ensemble_clicked(self):
864
        """
865
        This method is called when the user clicks on "sample"
866
        """
867
        # Get the ensemble name from the ComboBox
868
        ensemble_name = self._pg.gen_ensemble_ComboBox.currentText()
869
        # disable button
870
        self._pg.sample_ensemble_PushButton.setEnabled(False)
871
        # Sample the ensemble via logic module
872
        self._pulsed_master_logic.sample_block_ensemble(ensemble_name, True, self._write_chunkwise)
873
        return
874
875
    def sample_ensemble_finished(self, ensemble_name):
876
        """
877
878
        @return:
879
        """
880
        # enable button
881
        self._pg.sample_ensemble_PushButton.setEnabled(True)
882
        return
883
884
    def sauplo_ensemble_clicked(self):
885
        """
886
887
        @return:
888
        """
889
        # Get the ensemble name from the ComboBox
890
        ensemble_name = self._pg.gen_ensemble_ComboBox.currentText()
891
        # disable button
892
        self._pg.sample_ensemble_PushButton.setEnabled(False)
893
        self._pg.upload_ensemble_PushButton.setEnabled(False)
894
        self._pg.load_ensemble_PushButton.setEnabled(False)
895
        # Sample the ensemble via logic module
896
        self._pulsed_master_logic.sample_block_ensemble(ensemble_name, True, self._write_chunkwise, True)
897
        return
898
899
    # def sample_sequence_clicked(self):
900
    #     """
901
    #     This method is called when the user clicks on "sample"
902
    #     """
903
    #     # Get the sequence name from the ComboBox
904
    #     sequence_name = self._pg.gen_sequence_ComboBox.currentText()
905
    #     # Sample the sequence via logic module
906
    #     self._pulsed_master_logic.sample_sequence(sequence_name, True, self._write_chunkwise)
907
    #     # disable button
908
    #     self._pg.sample_sequence_PushButton.setEnabled(False)
909
    #     return
910
911
    # def sample_sequence_finished(self, sequence_name):
912
    #     """
913
    #
914
    #     @return:
915
    #     """
916
    #     # enable button
917
    #     self._pg.sample_sequence_PushButton.setEnabled(True)
918
    #     return
919
920
    def generate_predefined_clicked(self, button_obj=None):
921
        """
922
923
        @param button_obj:
924
        @return:
925
        """
926
        if type(button_obj) is bool:
927
            button_obj = self.sender()
928
        method_name = button_obj.objectName()
929
        if method_name.startswith('gen_'):
930
            method_name = method_name[4:]
931
        elif method_name.startswith('sauplo_'):
932
            method_name = method_name[7:]
933
        else:
934
            self.log.error('Strange naming of generate buttons in predefined methods occured.')
935
            return
936
937
        # get parameters from input widgets
938
        param_index = 0
939
        param_list = []
940
        while True:
941
            if not hasattr(self._pm, method_name + '_param_' + str(param_index) + '_Widget'):
942
                break
943
944
            input_obj = getattr(self._pm, method_name + '_param_' + str(param_index) + '_Widget')
945
            if hasattr(input_obj, 'isChecked'):
946
                param_list.append(input_obj.isChecked())
947
            elif hasattr(input_obj, 'value'):
948
                param_list.append(input_obj.value())
949
            elif hasattr(input_obj, 'text'):
950
                param_list.append(input_obj.text())
951
            else:
952
                self.log.error('Not possible to get the value from the widgets, since it does not '
953
                               'have one of the possible access methods!')
954
                return
955
956
            param_index += 1
957
958
        self._pulsed_master_logic.generate_predefined_sequence(method_name, param_list)
959
        return
960
961
    def generate_sauplo_predefined_clicked(self):
962
        button_obj = self.sender()
963
        method_name = button_obj.objectName()[7:]
964
        self.generate_predefined_clicked(button_obj)
965
        # get name of the generated ensemble
966
        input_obj = getattr(self._pm, method_name + '_param_0_Widget')
967
        if not hasattr(input_obj, 'text'):
968
            self.log.error('Predefined sequence methods must have as first argument the name of '
969
                           'the asset to be generated.')
970
            return
971
        name = input_obj.text()
972
        self._pulsed_master_logic.sample_block_ensemble(name, True, False, True)
973
        return
974
975
    ###########################################################################
976
    ###        Methods related to Settings for the 'Analysis' Tab:          ###
977
    ###########################################################################
978
    #FIXME: Implement the setting for 'Analysis' tab.
979
    def _activate_analysis_settings_ui(self, e):
980
        """ Initialize, connect and configure the Settings of 'Analysis' Tab.
981
982
        @param object e: Fysom.event object from Fysom class. A more detailed
983
                         explanation can be found in the method initUI.
984
        """
985
        self._as = AnalysisSettingDialog()
986
        self._as.accepted.connect(self.update_analysis_settings)
987
        self._as.rejected.connect(self.keep_former_analysis_settings)
988
        self._as.buttonBox.button(QtWidgets.QDialogButtonBox.Apply).clicked.connect(self.update_analysis_settings)
989
990
        if 'ana_param_x_axis_name_LineEdit' in self._statusVariables:
991
            self._as.ana_param_x_axis_name_LineEdit.setText(self._statusVariables['ana_param_x_axis_name_LineEdit'])
992
        if 'ana_param_x_axis_unit_LineEdit' in self._statusVariables:
993
            self._as.ana_param_x_axis_unit_LineEdit.setText(self._statusVariables['ana_param_x_axis_unit_LineEdit'])
994
        if 'ana_param_y_axis_name_LineEdit' in self._statusVariables:
995
            self._as.ana_param_y_axis_name_LineEdit.setText(self._statusVariables['ana_param_y_axis_name_LineEdit'])
996
        if 'ana_param_y_axis_unit_LineEdit' in self._statusVariables:
997
            self._as.ana_param_y_axis_unit_LineEdit.setText(self._statusVariables['ana_param_y_axis_unit_LineEdit'])
998
        if 'ana_param_second_plot_x_axis_name_LineEdit' in self._statusVariables:
999
            self._as.ana_param_second_plot_x_axis_name_LineEdit.setText(self._statusVariables['ana_param_second_plot_x_axis_name_LineEdit'])
1000
        if 'ana_param_second_plot_x_axis_unit_LineEdit' in self._statusVariables:
1001
            self._as.ana_param_second_plot_x_axis_unit_LineEdit.setText(self._statusVariables['ana_param_second_plot_x_axis_unit_LineEdit'])
1002
        if 'ana_param_second_plot_y_axis_name_LineEdit' in self._statusVariables:
1003
            self._as.ana_param_second_plot_y_axis_name_LineEdit.setText(self._statusVariables['ana_param_second_plot_y_axis_name_LineEdit'])
1004
        if 'ana_param_second_plot_y_axis_unit_LineEdit' in self._statusVariables:
1005
            self._as.ana_param_second_plot_y_axis_unit_LineEdit.setText(self._statusVariables['ana_param_second_plot_y_axis_unit_LineEdit'])
1006
        self.update_analysis_settings()
1007
        return
1008
1009
1010
    def _deactivate_analysis_settings_ui(self, e):
1011
        """ Disconnects the configuration of the Settings for 'Analysis' Tab.
1012
1013
        @param object e: Fysom.event object from Fysom class. A more detailed
1014
                         explanation can be found in the method initUI.
1015
        """
1016
        self._statusVariables['ana_param_x_axis_name_LineEdit'] = self._as.ana_param_x_axis_name_LineEdit.text()
1017
        self._statusVariables['ana_param_x_axis_unit_LineEdit'] = self._as.ana_param_x_axis_unit_LineEdit.text()
1018
        self._statusVariables['ana_param_y_axis_name_LineEdit'] = self._as.ana_param_y_axis_name_LineEdit.text()
1019
        self._statusVariables['ana_param_y_axis_unit_LineEdit'] = self._as.ana_param_y_axis_unit_LineEdit.text()
1020
        self._statusVariables['ana_param_second_plot_x_axis_name_LineEdit'] = self._as.ana_param_second_plot_x_axis_name_LineEdit.text()
1021
        self._statusVariables['ana_param_second_plot_x_axis_unit_LineEdit'] = self._as.ana_param_second_plot_x_axis_unit_LineEdit.text()
1022
        self._statusVariables['ana_param_second_plot_y_axis_name_LineEdit'] = self._as.ana_param_second_plot_y_axis_name_LineEdit.text()
1023
        self._statusVariables['ana_param_second_plot_y_axis_unit_LineEdit'] = self._as.ana_param_second_plot_y_axis_unit_LineEdit.text()
1024
        return
1025
1026
1027
    def update_analysis_settings(self):
1028
        """ Apply the new settings """
1029
        self._pa.pulse_analysis_PlotWidget.setLabel(
1030
            axis='bottom',
1031
            text=self._as.ana_param_x_axis_name_LineEdit.text(),
1032
            units=self._as.ana_param_x_axis_unit_LineEdit.text())
1033
        self._pa.pulse_analysis_PlotWidget.setLabel(
1034
            axis='left',
1035
            text=self._as.ana_param_y_axis_name_LineEdit.text(),
1036
            units=self._as.ana_param_y_axis_unit_LineEdit.text())
1037
        self._pa.pulse_analysis_second_PlotWidget.setLabel(
1038
            axis='bottom',
1039
            text=self._as.ana_param_second_plot_x_axis_name_LineEdit.text(),
1040
            units=self._as.ana_param_second_plot_x_axis_unit_LineEdit.text())
1041
        self._pa.pulse_analysis_second_PlotWidget.setLabel(
1042
            axis='left',
1043
            text=self._as.ana_param_second_plot_y_axis_name_LineEdit.text(),
1044
            units=self._as.ana_param_second_plot_y_axis_unit_LineEdit.text())
1045
        return
1046
1047
    def keep_former_analysis_settings(self):
1048
        """ Keep the old settings """
1049
        #FIXME: Implement the behaviour
1050
        pass
1051
1052
    def show_analysis_settings(self):
1053
        """ Open the Analysis Settings Window. """
1054
        self._as.exec_()
1055
        return
1056
1057
    ###########################################################################
1058
    ###     Methods related to the Tab 'Analysis' in the Pulsed Window:     ###
1059
    ###########################################################################
1060
    def setup_toolbar(self):
1061
        # create all the needed control widgets on the fly and connect their
1062
        # actions to each other:
1063
        self._mw.pulser_on_off_PushButton = QtWidgets.QPushButton()
1064
        self._mw.pulser_on_off_PushButton.setText('Pulser ON')
1065
        self._mw.pulser_on_off_PushButton.setToolTip('Switch the device on and off.')
1066
        self._mw.pulser_on_off_PushButton.setCheckable(True)
1067
        self._mw.control_ToolBar.addWidget(self._mw.pulser_on_off_PushButton)
1068
1069
        self._mw.clear_device_PushButton = QtWidgets.QPushButton(self._mw)
1070
        self._mw.clear_device_PushButton.setText('Clear Pulser')
1071
        self._mw.clear_device_PushButton.setToolTip(
1072
            'Clear the Pulser Device Memory\nfrom all loaded files.')
1073
        self._mw.control_ToolBar.addWidget(self._mw.clear_device_PushButton)
1074
1075
        self._mw.current_loaded_asset_Label = QtWidgets.QLabel(self._mw)
1076
        sizepolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred,
1077
                                           QtWidgets.QSizePolicy.Fixed)
1078
        sizepolicy.setHorizontalStretch(0)
1079
        sizepolicy.setVerticalStretch(0)
1080
        sizepolicy.setHeightForWidth(
1081
            self._mw.current_loaded_asset_Label.sizePolicy().hasHeightForWidth())
1082
        self._mw.current_loaded_asset_Label.setSizePolicy(sizepolicy)
1083
        self._mw.current_loaded_asset_Label.setText('  No Asset Loaded')
1084
        self._mw.current_loaded_asset_Label.setToolTip('Display the currently loaded asset.')
1085
        self._mw.control_ToolBar.addWidget(self._mw.current_loaded_asset_Label)
1086
1087
        self._mw.save_tag_LineEdit = QtWidgets.QLineEdit()
1088
        self._mw.save_tag_LineEdit.setMaximumWidth(200)
1089
        self._mw.save_ToolBar.addWidget(self._mw.save_tag_LineEdit)
1090
1091
    def setup_extraction_ui(self):
1092
        self.lasertrace_image = pg.PlotDataItem(np.array(range(10)), np.zeros(10), pen=palette.c1)
1093
        self._pe.laserpulses_PlotWidget.addItem(self.lasertrace_image)
1094
        self._pe.laserpulses_PlotWidget.addItem(self.sig_start_line)
1095
        self._pe.laserpulses_PlotWidget.addItem(self.sig_end_line)
1096
        self._pe.laserpulses_PlotWidget.addItem(self.ref_start_line)
1097
        self._pe.laserpulses_PlotWidget.addItem(self.ref_end_line)
1098
        self._pe.laserpulses_PlotWidget.setLabel('bottom', 'bins')
1099
1100
    def _activate_analysis_ui(self, e):
1101
        """ Initialize, connect and configure the 'Analysis' Tab.
1102
1103
        @param object e: Fysom.event object from Fysom class. A more detailed
1104
                         explanation can be found in the method initUI.
1105
        """
1106
        # FIXME: Implement second plot
1107
        self._pa.second_plot_GroupBox.setVisible(False)
1108
        # Configure the main pulse analysis display:
1109
        self.signal_image = pg.PlotDataItem(np.array(range(10)), np.zeros(10), pen=palette.c1)
1110
        self._pa.pulse_analysis_PlotWidget.addItem(self.signal_image)
1111
        self.signal_image2 = pg.PlotDataItem(pen=palette.c3)
1112
        self._pa.pulse_analysis_PlotWidget.addItem(self.signal_image2)
1113
        self._pa.pulse_analysis_PlotWidget.showGrid(x=True, y=True, alpha=0.8)
1114
1115
        # Configure the fit of the data in the main pulse analysis display:
1116
        self.fit_image = pg.PlotDataItem(pen=palette.c2)
1117
        self._pa.pulse_analysis_PlotWidget.addItem(self.fit_image)
1118
        self._pa.fit_param_fit_func_ComboBox.clear()
1119
        self._pa.fit_param_fit_func_ComboBox.addItems(self._pulsed_master_logic.get_fit_functions())
1120
1121
        # Configure the errorbars of the data in the main pulse analysis display:
1122
        self.signal_image_error_bars = pg.ErrorBarItem(
1123
            x=np.array(range(10)),
1124
            y=np.zeros(10),
1125
            top=0.,
1126
            bottom=0.,
1127
            pen=palette.c1)
1128
        self.signal_image_error_bars2 = pg.ErrorBarItem(
1129
            x=np.array(range(10)),
1130
            y=np.zeros(10),
1131
            top=0.,
1132
            bottom=0.,
1133
            pen=palette.c3)
1134
1135
        # Configure the second pulse analysis display:
1136
        self.second_plot_image = pg.PlotDataItem(np.array(range(10)), np.zeros(10), pen=palette.c1)
1137
        self._pa.pulse_analysis_second_PlotWidget.addItem(self.second_plot_image)
1138
        self._pa.pulse_analysis_second_PlotWidget.showGrid(x=True, y=True, alpha=0.8)
1139
1140
        # Configure the lasertrace plot display:
1141
        self.sig_start_line = pg.InfiniteLine(pos=0, pen=QtGui.QPen(palette.c3), movable=True)
1142
        self.sig_start_line.setHoverPen(QtGui.QPen(palette.c2))
1143
        self.sig_end_line = pg.InfiniteLine(pos=0, pen=QtGui.QPen(palette.c3), movable=True)
1144
        self.sig_end_line.setHoverPen(QtGui.QPen(palette.c2))
1145
        self.ref_start_line = pg.InfiniteLine(pos=0, pen=QtGui.QPen(palettedark.c4), movable=True)
1146
        self.ref_start_line.setHoverPen(QtGui.QPen(palette.c4))
1147
        self.ref_end_line = pg.InfiniteLine(pos=0, pen=QtGui.QPen(palettedark.c4), movable=True)
1148
        self.ref_end_line.setHoverPen(QtGui.QPen(palette.c4))
1149
        # Configure the measuring error display:
1150
        self.measuring_error_image = pg.PlotDataItem(
1151
            np.array(range(10)),
1152
            np.zeros(10),
1153
            pen=palette.c1)
1154
        self.measuring_error_image2 = pg.PlotDataItem(
1155
            np.array(range(10)),
1156
            np.zeros(10),
1157
            pen=palette.c3)
1158
        self._pe.measuring_error_PlotWidget.addItem(self.measuring_error_image)
1159
        self._pe.measuring_error_PlotWidget.addItem(self.measuring_error_image2)
1160
        self._pe.measuring_error_PlotWidget.setLabel('left', 'measuring error', units='a.u.')
1161
        self._pe.measuring_error_PlotWidget.setLabel('bottom', 'tau', units='ns')
1162
1163
1164
        # set boundaries
1165
        self._pe.slider_conv_std_dev.setRange(1, 200)
1166
        self._pe.conv_std_dev.setRange(1, 200)
1167
1168
        # ---------------------------------------------------------------------
1169
        #                         Connect signals
1170
        # ---------------------------------------------------------------------
1171
        # connect update signals from logic
1172
        self._pulsed_master_logic.sigSignalDataUpdated.connect(self.signal_data_updated)
1173
        self._pulsed_master_logic.sigLaserDataUpdated.connect(self.laser_data_updated)
1174
        self._pulsed_master_logic.sigLaserToShowUpdated.connect(self.laser_to_show_updated)
1175
        self._pulsed_master_logic.sigElapsedTimeUpdated.connect(self.elapsed_time_updated)
1176
        self._pulsed_master_logic.sigFitUpdated.connect(self.fit_data_updated)
1177
        self._pulsed_master_logic.sigMeasurementStatusUpdated.connect(self.measurement_status_updated)
1178
        self._pulsed_master_logic.sigPulserRunningUpdated.connect(self.pulser_running_updated)
1179
        self._pulsed_master_logic.sigFastCounterSettingsUpdated.connect(self.fast_counter_settings_updated)
1180
        self._pulsed_master_logic.sigMeasurementSequenceSettingsUpdated.connect(self.measurement_sequence_settings_updated)
1181
        self._pulsed_master_logic.sigPulserSettingsUpdated.connect(self.pulse_generator_settings_updated)
1182
        self._pulsed_master_logic.sigUploadedAssetsUpdated.connect(self.update_uploaded_assets)
1183
        self._pulsed_master_logic.sigLoadedAssetUpdated.connect(self.update_loaded_asset)
1184
        self._pulsed_master_logic.sigExtMicrowaveSettingsUpdated.connect(self.microwave_settings_updated)
1185
        self._pulsed_master_logic.sigExtMicrowaveRunningUpdated.connect(self.microwave_running_updated)
1186
        self._pulsed_master_logic.sigTimerIntervalUpdated.connect(self.measurement_timer_updated)
1187
        self._pulsed_master_logic.sigAnalysisWindowsUpdated.connect(self.analysis_windows_updated)
1188
        self._pulsed_master_logic.sigAnalysisMethodUpdated.connect(self.analysis_method_updated)
1189
1190
        # connect button click signals
1191
        self._pg.upload_ensemble_PushButton.clicked.connect(self.upload_ensemble_clicked)
1192
        self._pg.load_ensemble_PushButton.clicked.connect(self.load_ensemble_clicked)
1193
        # self._pg.upload_sequence_PushButton.clicked.connect(self.upload_sequence_clicked)
1194
        # self._pg.load_sequence_PushButton.clicked.connect(self.load_sequence_clicked)
1195
        self._mw.pulser_on_off_PushButton.clicked.connect(self.pulser_on_off_clicked)
1196
        self._mw.clear_device_PushButton.clicked.connect(self.clear_pulser_clicked)
1197
        self._pa.fit_param_PushButton.clicked.connect(self.fit_clicked)
1198
1199
        # connect action trigger signals
1200
        self._mw.action_run_stop.triggered.connect(self.measurement_run_stop_clicked)
1201
        self._mw.action_continue_pause.triggered.connect(self.measurement_continue_pause_clicked)
1202
        self._mw.action_pull_data.triggered.connect(self.pull_data_clicked)
1203
        self._mw.action_save.triggered.connect(self.save_clicked)
1204
        self._mw.action_Settings_Analysis.triggered.connect(self.show_analysis_settings)
1205
1206
        # connect checkbox click signals
1207
        self._pa.ext_control_use_mw_CheckBox.stateChanged.connect(self.ext_mw_params_changed)
1208
        self._pa.ana_param_x_axis_defined_CheckBox.stateChanged.connect(self.toggle_laser_xaxis_editor)
1209
        self._pa.ana_param_laserpulse_defined_CheckBox.stateChanged.connect(self.toggle_laser_xaxis_editor)
1210
        self._pa.ana_param_alternating_CheckBox.stateChanged.connect(self.measurement_sequence_settings_changed)
1211
        self._pa.ana_param_ignore_first_CheckBox.stateChanged.connect(self.measurement_sequence_settings_changed)
1212
        self._pa.ana_param_ignore_last_CheckBox.stateChanged.connect(self.measurement_sequence_settings_changed)
1213
        self._pe.laserpulses_display_raw_CheckBox.stateChanged.connect(self.laser_to_show_changed)
1214
        self._pa.ana_param_errorbars_CheckBox.stateChanged.connect(self.toggle_error_bars)
1215
        self._pa.pulser_use_interleave_CheckBox.stateChanged.connect(self.pulse_generator_settings_changed)
1216
1217
        # connect spinbox changed signals
1218
        self._pa.ana_param_num_laser_pulse_SpinBox.editingFinished.connect(self.measurement_sequence_settings_changed)
1219
        self._pa.ana_param_record_length_SpinBox.editingFinished.connect(self.fast_counter_settings_changed)
1220
        self._pa.time_param_ana_periode_DoubleSpinBox.editingFinished.connect(self.measurement_timer_changed)
1221
        self._pa.ext_control_mw_freq_DoubleSpinBox.editingFinished.connect(self.ext_mw_params_changed)
1222
        self._pa.ext_control_mw_power_DoubleSpinBox.editingFinished.connect(self.ext_mw_params_changed)
1223
        self._pa.pulser_sample_freq_DSpinBox.editingFinished.connect(self.pulse_generator_settings_changed)
1224
        self._pa.ana_param_x_axis_start_ScienDSpinBox.editingFinished.connect(self.measurement_sequence_settings_changed)
1225
        self._pa.ana_param_x_axis_inc_ScienDSpinBox.editingFinished.connect(self.measurement_sequence_settings_changed)
1226
        self._pe.extract_param_ana_window_start_SpinBox.editingFinished.connect(self.analysis_windows_changed)
1227
        self._pe.extract_param_ana_window_width_SpinBox.editingFinished.connect(self.analysis_windows_changed)
1228
        self._pe.extract_param_ref_window_start_SpinBox.editingFinished.connect(self.analysis_windows_changed)
1229
        self._pe.extract_param_ref_window_width_SpinBox.editingFinished.connect(self.analysis_windows_changed)
1230
        self._pe.conv_std_dev.valueChanged.connect(self.conv_std_dev_changed)
1231
1232
        # connect combobox changed signals
1233
        self._pa.ana_param_fc_bins_ComboBox.currentIndexChanged.connect(self.fast_counter_settings_changed)
1234
        #self._pa.second_plot_ComboBox.currentIndexChanged.connect(self.change_second_plot)
1235
        self._pa.pulser_activation_config_ComboBox.currentIndexChanged.connect(self.pulse_generator_settings_changed)
1236
        self._pe.laserpulses_ComboBox.currentIndexChanged.connect(self.laser_to_show_changed)
1237
1238
        # connect other widgets changed signals
1239
        self.sig_start_line.sigPositionChanged.connect(self.analysis_windows_line_changed)
1240
        self.sig_end_line.sigPositionChanged.connect(self.analysis_windows_line_changed)
1241
        self.ref_start_line.sigPositionChanged.connect(self.analysis_windows_line_changed)
1242
        self.ref_end_line.sigPositionChanged.connect(self.analysis_windows_line_changed)
1243
        self._pe.slider_conv_std_dev.sliderReleased.connect(self.slider_conv_std_dev_changed)
1244
1245
        # apply hardware constraints
1246
        self._analysis_apply_hardware_constraints()
1247
1248
        # initialize values
1249
        self._pulsed_master_logic.request_measurement_init_values()
1250
        return
1251
1252
    def _deactivate_analysis_ui(self, e):
1253
        """ Disconnects the configuration for 'Analysis' Tab.
1254
1255
       @param object e: Fysom.event object from Fysom class. A more detailed
1256
                         explanation can be found in the method initUI.
1257
        """
1258
        self.measurement_run_stop_clicked(False)
1259
1260
        self._statusVariables['ana_param_x_axis_defined_CheckBox'] = self._pa.ana_param_x_axis_defined_CheckBox.isChecked()
1261
        self._statusVariables['ana_param_laserpulse_defined_CheckBox'] = self._pa.ana_param_laserpulse_defined_CheckBox.isChecked()
1262
        self._statusVariables['ana_param_ignore_first_CheckBox'] = self._pa.ana_param_ignore_first_CheckBox.isChecked()
1263
        self._statusVariables['ana_param_ignore_last_CheckBox'] = self._pa.ana_param_ignore_last_CheckBox.isChecked()
1264
        self._statusVariables['ana_param_errorbars_CheckBox'] = self._pa.ana_param_errorbars_CheckBox.isChecked()
1265
        self._statusVariables['second_plot_ComboBox_text'] = self._pa.second_plot_ComboBox.currentText()
1266
1267
        # disconnect signals
1268
        self._pulsed_master_logic.sigSignalDataUpdated.disconnect()
1269
        self._pulsed_master_logic.sigLaserDataUpdated.disconnect()
1270
        self._pulsed_master_logic.sigLaserToShowUpdated.disconnect()
1271
        self._pulsed_master_logic.sigElapsedTimeUpdated.disconnect()
1272
        self._pulsed_master_logic.sigFitUpdated.disconnect()
1273
        self._pulsed_master_logic.sigMeasurementStatusUpdated.disconnect()
1274
        self._pulsed_master_logic.sigPulserRunningUpdated.disconnect()
1275
        self._pulsed_master_logic.sigFastCounterSettingsUpdated.disconnect()
1276
        self._pulsed_master_logic.sigMeasurementSequenceSettingsUpdated.disconnect()
1277
        self._pulsed_master_logic.sigPulserSettingsUpdated.disconnect()
1278
        self._pulsed_master_logic.sigUploadedAssetsUpdated.disconnect()
1279
        self._pulsed_master_logic.sigLoadedAssetUpdated.disconnect()
1280
        self._pulsed_master_logic.sigExtMicrowaveSettingsUpdated.disconnect()
1281
        self._pulsed_master_logic.sigExtMicrowaveRunningUpdated.disconnect()
1282
        self._pulsed_master_logic.sigTimerIntervalUpdated.disconnect()
1283
        self._pulsed_master_logic.sigAnalysisWindowsUpdated.disconnect()
1284
        self._pulsed_master_logic.sigAnalysisMethodUpdated.disconnect()
1285
        self._pg.upload_ensemble_PushButton.clicked.disconnect()
1286
        self._pg.load_ensemble_PushButton.clicked.disconnect()
1287
        # self._pg.upload_sequence_PushButton.clicked.disconnect()
1288
        # self._pg.load_sequence_PushButton.clicked.disconnect()
1289
        self._mw.pulser_on_off_PushButton.clicked.disconnect()
1290
        self._mw.clear_device_PushButton.clicked.disconnect()
1291
        self._pa.fit_param_PushButton.clicked.disconnect()
1292
        self._mw.action_run_stop.triggered.disconnect()
1293
        self._mw.action_continue_pause.triggered.disconnect()
1294
        self._mw.action_pull_data.triggered.disconnect()
1295
        self._mw.action_save.triggered.disconnect()
1296
        self._mw.action_Settings_Analysis.triggered.disconnect()
1297
        self._pa.ext_control_use_mw_CheckBox.stateChanged.disconnect()
1298
        self._pa.ana_param_x_axis_defined_CheckBox.stateChanged.disconnect()
1299
        self._pa.ana_param_laserpulse_defined_CheckBox.stateChanged.disconnect()
1300
        self._pa.ana_param_alternating_CheckBox.stateChanged.disconnect()
1301
        self._pa.ana_param_ignore_first_CheckBox.stateChanged.disconnect()
1302
        self._pa.ana_param_ignore_last_CheckBox.stateChanged.disconnect()
1303
        self._pe.laserpulses_display_raw_CheckBox.stateChanged.disconnect()
1304
        self._pa.ana_param_errorbars_CheckBox.stateChanged.disconnect()
1305
        self._pa.pulser_use_interleave_CheckBox.stateChanged.disconnect()
1306
        self._pa.ana_param_num_laser_pulse_SpinBox.editingFinished.disconnect()
1307
        self._pa.ana_param_record_length_SpinBox.editingFinished.disconnect()
1308
        self._pa.time_param_ana_periode_DoubleSpinBox.editingFinished.disconnect()
1309
        self._pa.ext_control_mw_freq_DoubleSpinBox.editingFinished.disconnect()
1310
        self._pa.ext_control_mw_power_DoubleSpinBox.editingFinished.disconnect()
1311
        self._pa.pulser_sample_freq_DSpinBox.editingFinished.disconnect()
1312
        self._pa.ana_param_x_axis_start_ScienDSpinBox.editingFinished.disconnect()
1313
        self._pa.ana_param_x_axis_inc_ScienDSpinBox.editingFinished.disconnect()
1314
        self._pe.extract_param_ana_window_start_SpinBox.editingFinished.disconnect()
1315
        self._pe.extract_param_ana_window_width_SpinBox.editingFinished.disconnect()
1316
        self._pe.extract_param_ref_window_start_SpinBox.editingFinished.disconnect()
1317
        self._pe.extract_param_ref_window_width_SpinBox.editingFinished.disconnect()
1318
        self._pe.conv_std_dev.valueChanged.disconnect()
1319
        self._pa.ana_param_fc_bins_ComboBox.currentIndexChanged.disconnect()
1320
        #self._pa.second_plot_ComboBox.currentIndexChanged.disconnect()
1321
        self._pa.pulser_activation_config_ComboBox.currentIndexChanged.disconnect()
1322
        self._pe.laserpulses_ComboBox.currentIndexChanged.disconnect()
1323
        self.sig_start_line.sigPositionChanged.disconnect()
1324
        self.sig_end_line.sigPositionChanged.disconnect()
1325
        self.ref_start_line.sigPositionChanged.disconnect()
1326
        self.ref_end_line.sigPositionChanged.disconnect()
1327
        self._pe.slider_conv_std_dev.sliderReleased.disconnect()
1328
        return
1329
1330
    def _analysis_apply_hardware_constraints(self):
1331
        """
1332
        Retrieve the constraints from pulser and fast counter hardware and apply these constraints
1333
        to the analysis tab GUI elements.
1334
        """
1335
        # block signals
1336
        self._pa.pulser_activation_config_ComboBox.blockSignals(True)
1337
        self._pa.ana_param_fc_bins_ComboBox.blockSignals(True)
1338
        # apply constraints
1339
        pulser_constr, fastcounter_constr = self._pulsed_master_logic.get_hardware_constraints()
1340
        sample_min = pulser_constr['sample_rate']['min']
1341
        sample_max = pulser_constr['sample_rate']['max']
1342
        sample_step = pulser_constr['sample_rate']['step']
1343
        self._pa.pulser_sample_freq_DSpinBox.setMinimum(sample_min)
1344
        self._pa.pulser_sample_freq_DSpinBox.setMaximum(sample_max)
1345
        self._pa.pulser_sample_freq_DSpinBox.setSingleStep(sample_step)
1346
        self._pa.pulser_activation_config_ComboBox.clear()
1347
        self._pa.pulser_activation_config_ComboBox.addItems(list(pulser_constr['activation_config']))
1348
        self._pa.ana_param_fc_bins_ComboBox.clear()
1349
        for binwidth in fastcounter_constr['hardware_binwidth_list']:
1350
            self._pa.ana_param_fc_bins_ComboBox.addItem(str(binwidth))
1351
        # unblock signals
1352
        self._pa.pulser_activation_config_ComboBox.blockSignals(False)
1353
        self._pa.ana_param_fc_bins_ComboBox.blockSignals(False)
1354
        return
1355
1356
    def measurement_run_stop_clicked(self, isChecked):
1357
        """ Manages what happens if pulsed measurement is started or stopped.
1358
1359
        @param bool isChecked: start scan if that is possible
1360
        """
1361
        if isChecked:
1362
            self._pulsed_master_logic.start_measurement()
1363
        else:
1364
            self._pulsed_master_logic.stop_measurement()
1365
        return
1366
1367
    #ToDo: I think that is not really working yet. Yeap, true....
1368
    def measurement_continue_pause_clicked(self, isChecked):
1369
        """ Continues and pauses the measurement. """
1370
        if isChecked:
1371
            self._pulsed_master_logic.pause_measurement()
1372
        else:
1373
            self._pulsed_master_logic.continue_measurement()
1374
        return
1375
1376
    def measurement_status_updated(self, is_running, is_paused):
1377
        """
1378
1379
        @param is_running:
1380
        @param is_paused:
1381
        @return:
1382
        """
1383
        # block signals
1384
        self._mw.action_run_stop.blockSignals(True)
1385
        self._mw.action_continue_pause.blockSignals(True)
1386
1387
        # Enable/Disable widgets
1388
        if is_running:
1389
            self._pa.ext_control_use_mw_CheckBox.setEnabled(False)
1390
            self._pa.ext_control_mw_freq_DoubleSpinBox.setEnabled(False)
1391
            self._pa.ext_control_mw_power_DoubleSpinBox.setEnabled(False)
1392
            self._pa.pulser_sample_freq_DSpinBox.setEnabled(False)
1393
            self._pa.pulser_activation_config_ComboBox.setEnabled(False)
1394
            self._pa.ana_param_fc_bins_ComboBox.setEnabled(False)
1395
            self._pa.ana_param_laserpulse_defined_CheckBox.setEnabled(False)
1396
            self._pa.ana_param_num_laser_pulse_SpinBox.setEnabled(False)
1397
            self._pa.ana_param_record_length_SpinBox.setEnabled(False)
1398
            self._pa.ana_param_ignore_first_CheckBox.setEnabled(False)
1399
            self._pa.ana_param_ignore_last_CheckBox.setEnabled(False)
1400
            self._pa.ana_param_alternating_CheckBox.setEnabled(False)
1401
            self._pa.ana_param_x_axis_defined_CheckBox.setEnabled(False)
1402
            self._pa.ana_param_x_axis_start_ScienDSpinBox.setEnabled(False)
1403
            self._pa.ana_param_x_axis_inc_ScienDSpinBox.setEnabled(False)
1404
            self._pg.load_ensemble_PushButton.setEnabled(False)
1405
            # self._pg.load_sequence_PushButton.setEnabled(False)
1406
            self._mw.pulser_on_off_PushButton.setEnabled(False)
1407
            self._mw.action_continue_pause.setEnabled(True)
1408
            self._mw.action_pull_data.setEnabled(True)
1409
            if not self._mw.action_run_stop.isChecked():
1410
                self._mw.action_run_stop.toggle()
1411
        else:
1412
            self._pa.ext_control_use_mw_CheckBox.setEnabled(True)
1413
            self._pa.ext_control_mw_freq_DoubleSpinBox.setEnabled(True)
1414
            self._pa.ext_control_mw_power_DoubleSpinBox.setEnabled(True)
1415
            self._pa.pulser_sample_freq_DSpinBox.setEnabled(True)
1416
            self._pa.pulser_activation_config_ComboBox.setEnabled(True)
1417
            self._pa.ana_param_fc_bins_ComboBox.setEnabled(True)
1418
            self._pa.ana_param_laserpulse_defined_CheckBox.setEnabled(True)
1419
            self._pa.ana_param_num_laser_pulse_SpinBox.setEnabled(True)
1420
            self._pa.ana_param_record_length_SpinBox.setEnabled(True)
1421
            self._pa.ana_param_ignore_first_CheckBox.setEnabled(True)
1422
            self._pa.ana_param_ignore_last_CheckBox.setEnabled(True)
1423
            self._pa.ana_param_alternating_CheckBox.setEnabled(True)
1424
            self._pa.ana_param_x_axis_defined_CheckBox.setEnabled(True)
1425
            self._pa.ana_param_x_axis_start_ScienDSpinBox.setEnabled(True)
1426
            self._pa.ana_param_x_axis_inc_ScienDSpinBox.setEnabled(True)
1427
            self._pg.load_ensemble_PushButton.setEnabled(True)
1428
            # self._pg.load_sequence_PushButton.setEnabled(True)
1429
            self._mw.pulser_on_off_PushButton.setEnabled(True)
1430
            self._mw.action_continue_pause.setEnabled(False)
1431
            self._mw.action_pull_data.setEnabled(False)
1432
            if self._mw.action_run_stop.isChecked():
1433
                self._mw.action_run_stop.toggle()
1434
        if is_paused:
1435
            if not self._mw.action_continue_pause.isChecked():
1436
                self._mw.action_continue_pause.toggle()
1437
        else:
1438
            if self._mw.action_continue_pause.isChecked():
1439
                self._mw.action_continue_pause.toggle()
1440
        # unblock signals
1441
        self._mw.action_run_stop.blockSignals(False)
1442
        self._mw.action_continue_pause.blockSignals(False)
1443
        return
1444
1445
    def pull_data_clicked(self):
1446
        """ Pulls and analysis the data when the 'action_pull_data'-button is clicked. """
1447
        self._pulsed_master_logic.manually_pull_data()
1448
        return
1449
1450
    def signal_data_updated(self, x_data, y_signal_data, y2_signal_data, y_error_data, y2_error_data):
1451
        """
1452
1453
        @param x_data:
1454
        @param y_signal_data:
1455
        @param y2_signal_data:
1456
        @param y_error_data:
1457
        @param y2_error_data:
1458
        @return:
1459
        """
1460
        show_error_bars = self._pa.ana_param_errorbars_CheckBox.isChecked()
1461
        is_alternating = self._pa.ana_param_alternating_CheckBox.isChecked()
1462
        if show_error_bars:
1463
            beamwidth = np.inf
1464
            for i in range(len(x_data) - 1):
1465
                width = x_data[i + 1] - x_data[i]
1466
                width = width / 3
1467
                if width <= beamwidth:
1468
                    beamwidth = width
1469
            # create ErrorBarItems
1470
            self.signal_image_error_bars.setData(x=x_data, y=y_signal_data, top=y_error_data,
1471
                                                 bottom=y_error_data, beam=beamwidth)
1472
            if is_alternating:
1473
                self.signal_image_error_bars2.setData(x=x_data, y=y2_signal_data, top=y2_error_data,
1474
                                                      bottom=y2_error_data, beam=beamwidth)
1475
            if not self.signal_image_error_bars in self._pa.pulse_analysis_PlotWidget.items():
1476
                self._pa.pulse_analysis_PlotWidget.addItem(self.signal_image_error_bars)
1477
                if is_alternating:
1478
                    self._pa.pulse_analysis_PlotWidget.addItem(self.signal_image_error_bars2)
1479
        else:
1480
            if self.signal_image_error_bars in self._pa.pulse_analysis_PlotWidget.items():
1481
                self._pa.pulse_analysis_PlotWidget.removeItem(self.signal_image_error_bars)
1482
                if is_alternating:
1483
                    self._pa.pulse_analysis_PlotWidget.addItem(self.signal_image_error_bars2)
1484
1485
        # dealing with the actual signal
1486
        self.signal_image.setData(x=x_data, y=y_signal_data)
1487
        if is_alternating:
1488
            self.signal_image2.setData(x=x_data, y=y2_signal_data)
1489
1490
        # dealing with the error plot
1491
        self.measuring_error_image.setData(x=x_data, y=y_error_data)
1492
        if is_alternating:
1493
            self.measuring_error_image2.setData(x=x_data, y=y2_error_data)
1494
        return
1495
1496
    def save_clicked(self):
1497
        """Saves the current data"""
1498
        self._mw.action_save.setEnabled(False)
1499
        save_tag = self._mw.save_tag_LineEdit.text()
1500
        self._pulsed_master_logic.save_measurement_data(save_tag)
1501
        self._mw.action_save.setEnabled(True)
1502
        return
1503
1504
    def fit_clicked(self):
1505
        """Fits the current data"""
1506
        current_fit_function = self._pa.fit_param_fit_func_ComboBox.currentText()
1507
        self._pulsed_master_logic.do_fit(current_fit_function)
1508
        return
1509
1510
    def fit_data_updated(self, fit_function, fit_data_x, fit_data_y, param_dict, result_dict):
1511
        """
1512
1513
        @param fit_function:
1514
        @param fit_data_x:
1515
        @param fit_data_y:
1516
        @param param_dict:
1517
        @param result_dict:
1518
        @return:
1519
        """
1520
        # block signals
1521
        self._pa.fit_param_fit_func_ComboBox.blockSignals(True)
1522
        # set widgets
1523
        self._pa.fit_param_results_TextBrowser.clear()
1524
        fit_text = units.create_formatted_output(param_dict)
1525
        self._pa.fit_param_results_TextBrowser.setPlainText(fit_text)
1526
        self.fit_image.setData(x=fit_data_x, y=fit_data_y)
1527
        if fit_function == 'No Fit' and self.fit_image in self._pa.pulse_analysis_PlotWidget.items():
1528
            self._pa.pulse_analysis_PlotWidget.removeItem(self.fit_image)
1529
        elif fit_function != 'No Fit' and self.fit_image not in self._pa.pulse_analysis_PlotWidget.items():
1530
            self._pa.pulse_analysis_PlotWidget.addItem(self.fit_image)
1531
        if self._pa.fit_param_fit_func_ComboBox.currentText() != fit_function:
1532
            index = self._pa.fit_param_fit_func_ComboBox.findText(fit_function)
1533
            if index >= 0:
1534
                self._pa.fit_param_fit_func_ComboBox.setCurrentIndex(index)
1535
        # unblock signals
1536
        self._pa.fit_param_fit_func_ComboBox.blockSignals(False)
1537
        return
1538
1539
    def elapsed_time_updated(self, elapsed_time, elapsed_time_str):
1540
        """
1541
        Refreshes the elapsed time and sweeps of the measurement.
1542
1543
        @param elapsed_time:
1544
        @param elapsed_time_str:
1545
        @return:
1546
        """
1547
        # block signals
1548
        self._pa.time_param_elapsed_time_LineEdit.blockSignals(True)
1549
        # Set widgets
1550
        self._pa.time_param_elapsed_time_LineEdit.setText(elapsed_time_str)
1551
        # unblock signals
1552
        self._pa.time_param_elapsed_time_LineEdit.blockSignals(True)
1553
        return
1554
1555
    def ext_mw_params_changed(self):
1556
        """ Shows or hides input widgets which are necessary if an external mw is turned on"""
1557
        use_ext_microwave = self._pa.ext_control_use_mw_CheckBox.isChecked()
1558
        microwave_freq = self._pa.ext_control_mw_freq_DoubleSpinBox.value()
1559
        microwave_power = self._pa.ext_control_mw_power_DoubleSpinBox.value()
1560 View Code Duplication
        if use_ext_microwave:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1561
            self._pa.ext_control_mw_freq_Label.setEnabled(True)
1562
            self._pa.ext_control_mw_freq_DoubleSpinBox.setEnabled(True)
1563
            self._pa.ext_control_mw_power_Label.setEnabled(True)
1564
            self._pa.ext_control_mw_power_DoubleSpinBox.setEnabled(True)
1565
1566
            self._pa.ext_control_mw_freq_Label.setVisible(True)
1567
            self._pa.ext_control_mw_freq_DoubleSpinBox.setVisible(True)
1568
            self._pa.ext_control_mw_power_Label.setVisible(True)
1569
            self._pa.ext_control_mw_power_DoubleSpinBox.setVisible(True)
1570
        else:
1571
            self._pa.ext_control_mw_freq_Label.setEnabled(False)
1572
            self._pa.ext_control_mw_freq_DoubleSpinBox.setEnabled(False)
1573
            self._pa.ext_control_mw_power_Label.setEnabled(False)
1574
            self._pa.ext_control_mw_power_DoubleSpinBox.setEnabled(False)
1575
1576
            self._pa.ext_control_mw_freq_Label.setVisible(False)
1577
            self._pa.ext_control_mw_freq_DoubleSpinBox.setVisible(False)
1578
            self._pa.ext_control_mw_power_Label.setVisible(False)
1579
            self._pa.ext_control_mw_power_DoubleSpinBox.setVisible(False)
1580
1581
        self._pulsed_master_logic.ext_microwave_settings_changed(microwave_freq, microwave_power,
1582
                                                                 use_ext_microwave)
1583
        return
1584
1585
    def microwave_settings_updated(self, frequency, power, use_ext_microwave):
1586
        """
1587
1588
        @param frequency:
1589
        @param power:
1590
        @param use_ext_microwave:
1591
        @return:
1592
        """
1593
        # block signals
1594
        self._pa.ext_control_mw_freq_DoubleSpinBox.blockSignals(True)
1595
        self._pa.ext_control_mw_power_DoubleSpinBox.blockSignals(True)
1596
        self._pa.ext_control_use_mw_CheckBox.blockSignals(True)
1597
        # set widgets
1598
        self._pa.ext_control_mw_freq_DoubleSpinBox.setValue(frequency)
1599
        self._pa.ext_control_mw_power_DoubleSpinBox.setValue(power)
1600
        self._pa.ext_control_use_mw_CheckBox.setChecked(use_ext_microwave)
1601
        # set visibility
1602 View Code Duplication
        if use_ext_microwave:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1603
            self._pa.ext_control_mw_freq_Label.setEnabled(True)
1604
            self._pa.ext_control_mw_freq_DoubleSpinBox.setEnabled(True)
1605
            self._pa.ext_control_mw_power_Label.setEnabled(True)
1606
            self._pa.ext_control_mw_power_DoubleSpinBox.setEnabled(True)
1607
1608
            self._pa.ext_control_mw_freq_Label.setVisible(True)
1609
            self._pa.ext_control_mw_freq_DoubleSpinBox.setVisible(True)
1610
            self._pa.ext_control_mw_power_Label.setVisible(True)
1611
            self._pa.ext_control_mw_power_DoubleSpinBox.setVisible(True)
1612
        else:
1613
            self._pa.ext_control_mw_freq_Label.setEnabled(False)
1614
            self._pa.ext_control_mw_freq_DoubleSpinBox.setEnabled(False)
1615
            self._pa.ext_control_mw_power_Label.setEnabled(False)
1616
            self._pa.ext_control_mw_power_DoubleSpinBox.setEnabled(False)
1617
1618
            self._pa.ext_control_mw_freq_Label.setVisible(False)
1619
            self._pa.ext_control_mw_freq_DoubleSpinBox.setVisible(False)
1620
            self._pa.ext_control_mw_power_Label.setVisible(False)
1621
            self._pa.ext_control_mw_power_DoubleSpinBox.setVisible(False)
1622
        # unblock signals
1623
        self._pa.ext_control_mw_freq_DoubleSpinBox.blockSignals(False)
1624
        self._pa.ext_control_mw_power_DoubleSpinBox.blockSignals(False)
1625
        self._pa.ext_control_use_mw_CheckBox.blockSignals(False)
1626
        return
1627
1628
    def microwave_running_updated(self, is_running):
1629
        """
1630
1631
        @return:
1632
        """
1633
        pass
1634
1635
    def pulse_generator_settings_changed(self):
1636
        """
1637
1638
        @return:
1639
        """
1640
        # FIXME: Properly implement amplitude and interleave
1641
        sample_rate_hz = self._pa.pulser_sample_freq_DSpinBox.value()
1642
        activation_config_name = self._pa.pulser_activation_config_ComboBox.currentText()
1643
        analogue_amplitude, dummy = self._pulsed_master_logic._measurement_logic._pulse_generator_device.get_analog_level()
1644
        interleave_on = self._pa.pulser_use_interleave_CheckBox.isChecked()
1645
        self._pulsed_master_logic.pulse_generator_settings_changed(sample_rate_hz,
1646
                                                                   activation_config_name,
1647
                                                                   analogue_amplitude,
1648
                                                                   interleave_on)
1649
        return
1650
1651
    def pulse_generator_settings_updated(self, sample_rate_hz, activation_config_name,
1652
                                         activation_config, analogue_amplitude, interleave_on):
1653
        """
1654
1655
        @param sample_rate_hz:
1656
        @param activation_config_name:
1657
        @param analogue_amplitude:
1658
        @param interleave_on:
1659
        @return:
1660
        """
1661
        # block signals
1662
        self._pa.pulser_sample_freq_DSpinBox.blockSignals(True)
1663
        self._pa.pulser_activation_config_ComboBox.blockSignals(True)
1664
        self._pa.pulser_activation_config_LineEdit.blockSignals(True)
1665
        self._pa.pulser_use_interleave_CheckBox.blockSignals(True)
1666
        # Set widgets
1667
        # FIXME: Properly implement amplitude and interleave
1668
        self._pa.pulser_sample_freq_DSpinBox.setValue(sample_rate_hz)
1669
        index = self._pa.pulser_activation_config_ComboBox.findText(activation_config_name)
1670
        self._pa.pulser_activation_config_ComboBox.setCurrentIndex(index)
1671
        config_display_str = ''
1672
        for channel in activation_config:
1673
            config_display_str += channel + ' | '
1674
        config_display_str = config_display_str[:-3]
1675
        self._pa.pulser_activation_config_LineEdit.setText(config_display_str)
1676
        self._pa.pulser_use_interleave_CheckBox.setChecked(interleave_on)
1677
        # unblock signals
1678
        self._pa.pulser_sample_freq_DSpinBox.blockSignals(False)
1679
        self._pa.pulser_activation_config_ComboBox.blockSignals(False)
1680
        self._pa.pulser_activation_config_LineEdit.blockSignals(False)
1681
        self._pa.pulser_use_interleave_CheckBox.blockSignals(False)
1682
        return
1683
1684
    def fast_counter_settings_changed(self):
1685
        """
1686
1687
        @return:
1688
        """
1689
        record_length_s = self._pa.ana_param_record_length_SpinBox.value()
1690
        bin_width_s = float(self._pa.ana_param_fc_bins_ComboBox.currentText())
1691
        self._pulsed_master_logic.fast_counter_settings_changed(bin_width_s, record_length_s)
1692
        return
1693
1694
    def fast_counter_settings_updated(self, bin_width_s, record_length_s):
1695
        """
1696
1697
        @param bin_width_s:
1698
        @param record_length_s:
1699
        @return:
1700
        """
1701
        # block signals
1702
        self._pa.ana_param_record_length_SpinBox.blockSignals(True)
1703
        self._pa.ana_param_fc_bins_ComboBox.blockSignals(True)
1704
        # set widgets
1705
        self._pa.ana_param_record_length_SpinBox.setValue(record_length_s)
1706
        index = self._pa.ana_param_fc_bins_ComboBox.findText(str(bin_width_s))
1707
        self._pa.ana_param_fc_bins_ComboBox.setCurrentIndex(index)
1708
        # unblock signals
1709
        self._pa.ana_param_record_length_SpinBox.blockSignals(False)
1710
        self._pa.ana_param_fc_bins_ComboBox.blockSignals(False)
1711
        return
1712
1713
    def measurement_sequence_settings_changed(self):
1714
        """
1715
1716
        @return:
1717
        """
1718
        laser_ignore_list = []
1719
        if self._pa.ana_param_ignore_first_CheckBox.isChecked():
1720
            laser_ignore_list.append(0)
1721
        if self._pa.ana_param_ignore_last_CheckBox.isChecked():
1722
            laser_ignore_list.append(-1)
1723
        alternating = self._pa.ana_param_alternating_CheckBox.isChecked()
1724
        num_of_lasers = self._pa.ana_param_num_laser_pulse_SpinBox.value()
1725
        xaxis_start = self._pa.ana_param_x_axis_start_ScienDSpinBox.value()
1726
        xaxis_incr = self._pa.ana_param_x_axis_inc_ScienDSpinBox.value()
1727
        laser_trigger_delay = self._as.ana_param_lasertrigger_delay_ScienDSpinBox.value()
1728
        # FIXME: properly implement sequence_length_s
1729
        sequence_length_s = self._pulsed_master_logic._measurement_logic.sequence_length_s
1730
        num_of_ticks = num_of_lasers - len(laser_ignore_list)
1731
        if alternating:
1732
            num_of_ticks //= 2
1733
        measurement_ticks = np.arange(xaxis_start,
1734
                                      xaxis_start + (xaxis_incr * num_of_ticks) - (xaxis_incr / 2),
1735
                                      xaxis_incr)
1736
1737
        self._pulsed_master_logic.measurement_sequence_settings_changed(measurement_ticks,
1738
                                                                        num_of_lasers,
1739
                                                                        sequence_length_s,
1740
                                                                        laser_ignore_list,
1741
                                                                        alternating,
1742
                                                                        laser_trigger_delay)
1743
        return
1744
1745
    def measurement_sequence_settings_updated(self, measurement_ticks, number_of_lasers,
1746
                                              sequence_length_s, laser_ignore_list, alternating,
1747
                                              laser_trigger_delay):
1748
        """
1749
1750
        @param measurement_ticks:
1751
        @param number_of_lasers:
1752
        @param sequence_length_s:
1753
        @param laser_ignore_list:
1754
        @param alternating:
1755
        @param laser_trigger_delay:
1756
        @return:
1757
        """
1758
        # block signals
1759
        self._pa.ana_param_ignore_first_CheckBox.blockSignals(True)
1760
        self._pa.ana_param_ignore_last_CheckBox.blockSignals(True)
1761
        self._pa.ana_param_alternating_CheckBox.blockSignals(True)
1762
        self._pa.ana_param_num_laser_pulse_SpinBox.blockSignals(True)
1763
        self._pa.ana_param_x_axis_start_ScienDSpinBox.blockSignals(True)
1764
        self._pa.ana_param_x_axis_inc_ScienDSpinBox.blockSignals(True)
1765
        self._as.ana_param_lasertrigger_delay_ScienDSpinBox.blockSignals(True)
1766
        self._pe.laserpulses_ComboBox.blockSignals(True)
1767
        # set widgets
1768
        self._pa.ana_param_ignore_first_CheckBox.setChecked(0 in laser_ignore_list)
1769
        self._pa.ana_param_ignore_last_CheckBox.setChecked(-1 in laser_ignore_list)
1770
        self._pa.ana_param_alternating_CheckBox.setChecked(alternating)
1771
        self._pa.ana_param_num_laser_pulse_SpinBox.setValue(number_of_lasers)
1772
        self._as.ana_param_lasertrigger_delay_ScienDSpinBox.setValue(laser_trigger_delay)
1773
        self._pa.ana_param_x_axis_start_ScienDSpinBox.setValue(measurement_ticks[0])
1774
        self._pa.ana_param_x_axis_inc_ScienDSpinBox.setValue(
1775
            (measurement_ticks[-1] - measurement_ticks[0]) / (len(measurement_ticks)-1))
1776
        self._pe.laserpulses_ComboBox.addItems([str(i) for i in range(number_of_lasers+1)])
1777
        # change plots accordingly
1778
        if alternating:
1779
            if self.signal_image2 not in self._pa.pulse_analysis_PlotWidget.items():
1780
                self._pa.pulse_analysis_PlotWidget.addItem(self.signal_image2)
1781
            if self.signal_image_error_bars in self._pa.pulse_analysis_PlotWidget.items() and self.signal_image_error_bars2 not in self._pa.pulse_analysis_PlotWidget.items():
1782
                self._pa.pulse_analysis_PlotWidget.addItem(self.signal_image_error_bars2)
1783
            if self.measuring_error_image2 not in self._pe.measuring_error_PlotWidget.items():
1784
                self._pe.measuring_error_PlotWidget.addItem(self.measuring_error_image2)
1785
        else:
1786
            if self.signal_image2 in self._pa.pulse_analysis_PlotWidget.items():
1787
                self._pa.pulse_analysis_PlotWidget.removeItem(self.signal_image2)
1788
            if self.signal_image_error_bars2 in self._pa.pulse_analysis_PlotWidget.items():
1789
                self._pa.pulse_analysis_PlotWidget.removeItem(self.signal_image_error_bars2)
1790
            if self.measuring_error_image2 in self._pe.measuring_error_PlotWidget.items():
1791
                self._pe.measuring_error_PlotWidget.removeItem(self.measuring_error_image2)
1792
        # unblock signals
1793
        self._pa.ana_param_ignore_first_CheckBox.blockSignals(False)
1794
        self._pa.ana_param_ignore_last_CheckBox.blockSignals(False)
1795
        self._pa.ana_param_alternating_CheckBox.blockSignals(False)
1796
        self._pa.ana_param_num_laser_pulse_SpinBox.blockSignals(False)
1797
        self._pa.ana_param_x_axis_start_ScienDSpinBox.blockSignals(False)
1798
        self._pa.ana_param_x_axis_inc_ScienDSpinBox.blockSignals(False)
1799
        self._as.ana_param_lasertrigger_delay_ScienDSpinBox.blockSignals(False)
1800
        self._pe.laserpulses_ComboBox.blockSignals(False)
1801
        return
1802
1803
    def toggle_laser_xaxis_editor(self):
1804
        """ Shows or hides input widgets which are necessary if the x axis id defined or not."""
1805
        if self._pa.ana_param_x_axis_defined_CheckBox.isChecked():
1806
            self._pa.ana_param_x_axis_start_Label.setVisible(True)
1807
            self._pa.ana_param_x_axis_start_ScienDSpinBox.setVisible(True)
1808
            self._pa.ana_param_x_axis_inc_Label.setVisible(True)
1809
            self._pa.ana_param_x_axis_inc_ScienDSpinBox.setVisible(True)
1810
            self._pa.ana_param_x_axis_start_ScienDSpinBox.setEnabled(True)
1811
            self._pa.ana_param_x_axis_inc_ScienDSpinBox.setEnabled(True)
1812
        else:
1813
            self._pa.ana_param_x_axis_start_Label.setVisible(False)
1814
            self._pa.ana_param_x_axis_start_ScienDSpinBox.setVisible(False)
1815
            self._pa.ana_param_x_axis_inc_Label.setVisible(False)
1816
            self._pa.ana_param_x_axis_inc_ScienDSpinBox.setVisible(False)
1817
            self._pa.ana_param_x_axis_start_ScienDSpinBox.setEnabled(False)
1818
            self._pa.ana_param_x_axis_inc_ScienDSpinBox.setEnabled(False)
1819
1820
        if self._pa.ana_param_laserpulse_defined_CheckBox.isChecked():
1821
            self._pa.ana_param_num_laserpulses_Label.setVisible(True)
1822
            self._pa.ana_param_num_laser_pulse_SpinBox.setVisible(True)
1823
            self._pa.ana_param_record_length_Label.setVisible(True)
1824
            self._pa.ana_param_record_length_SpinBox.setVisible(True)
1825
            self._pa.ana_param_num_laser_pulse_SpinBox.setEnabled(True)
1826
            self._pa.ana_param_record_length_SpinBox.setEnabled(True)
1827
        else:
1828
            self._pa.ana_param_num_laserpulses_Label.setVisible(False)
1829
            self._pa.ana_param_num_laser_pulse_SpinBox.setVisible(False)
1830
            self._pa.ana_param_record_length_Label.setVisible(False)
1831
            self._pa.ana_param_record_length_SpinBox.setVisible(False)
1832
            self._pa.ana_param_num_laser_pulse_SpinBox.setEnabled(False)
1833
            self._pa.ana_param_record_length_SpinBox.setEnabled(False)
1834
        return
1835
1836
    def toggle_error_bars(self):
1837
        """
1838
1839
        @return:
1840
        """
1841
        show_bars = self._pa.ana_param_errorbars_CheckBox.isChecked()
1842
        is_alternating = self.signal_image2 in self._pa.pulse_analysis_PlotWidget.items()
1843
        if show_bars:
1844
            if self.signal_image_error_bars not in self._pa.pulse_analysis_PlotWidget.items():
1845
                self._pa.pulse_analysis_PlotWidget.addItem(self.signal_image_error_bars)
1846
            if is_alternating and self.signal_image_error_bars2 not in self._pa.pulse_analysis_PlotWidget.items():
1847
                self._pa.pulse_analysis_PlotWidget.addItem(self.signal_image_error_bars2)
1848
        else:
1849
            if self.signal_image_error_bars in self._pa.pulse_analysis_PlotWidget.items():
1850
                self._pa.pulse_analysis_PlotWidget.removeItem(self.signal_image_error_bars)
1851
            if is_alternating and self.signal_image_error_bars2 in self._pa.pulse_analysis_PlotWidget.items():
1852
                self._pa.pulse_analysis_PlotWidget.removeItem(self.signal_image_error_bars2)
1853
        return
1854
1855
    # def change_second_plot(self):
1856
    #     """ This method handles the second plot"""
1857
    #     if self._mw.second_plot_ComboBox.currentText()=='None':
1858
    #         self._mw.second_plot_GroupBox.setVisible(False)
1859
    #     else:
1860
    #         self._mw.second_plot_GroupBox.setVisible(True)
1861
    #
1862
    #         # Here FFT is seperated from the other option. The reason for that
1863
    #         # is preventing of code doubling
1864
    #         if self._mw.second_plot_ComboBox.currentText() == 'FFT':
1865
    #             fft_x, fft_y = self._pulsed_meas_logic.compute_fft()
1866
    #             self.second_plot_image.setData(fft_x, fft_y)
1867
    #             self._mw.pulse_analysis_second_PlotWidget.setLogMode(x=False, y=False)
1868
    #
1869
    #             self._mw.pulse_analysis_second_PlotWidget.setLabel(axis='bottom',
1870
    #                                                                text=self._as.ana_param_second_plot_x_axis_name_LineEdit.text(),
1871
    #                                                                units=self._as.ana_param_second_plot_x_axis_unit_LineEdit.text())
1872
    #             self._mw.pulse_analysis_second_PlotWidget.setLabel(axis='left',
1873
    #                                                                text=self._as.ana_param_second_plot_y_axis_name_LineEdit.text(),
1874
    #                                                                units=self._as.ana_param_second_plot_y_axis_unit_LineEdit.text())
1875
    #
1876
    #         else:
1877
    #             #FIXME: Is not working when there is a 0 in the values, therefore ignoring the first measurment point
1878
    #             self.second_plot_image.setData(self._pulsed_meas_logic.signal_plot_x[1:], self._pulsed_meas_logic.signal_plot_y[1:])
1879
    #
1880
    #             if self._as.ana_param_second_plot_x_axis_name_LineEdit.text()== '':
1881
    #                 self._mw.pulse_analysis_second_PlotWidget.setLabel(axis='left',
1882
    #                                                                    text=self._as.ana_param_y_axis_name_LineEdit.text(),
1883
    #                                                                    units=self._as.ana_param_y_axis_unit_LineEdit.text())
1884
    #                 self._mw.pulse_analysis_second_PlotWidget.setLabel(axis='bottom',
1885
    #                                                                    text=self._as.ana_param_x_axis_name_LineEdit.text(),
1886
    #                                                                    units=self._as.ana_param_x_axis_unit_LineEdit.text())
1887
    #
1888
    #             else:
1889
    #                 self._mw.pulse_analysis_second_PlotWidget.setLabel(axis='bottom',
1890
    #                                                                    text=self._as.ana_param_second_plot_x_axis_name_LineEdit.text(),
1891
    #                                                                    units=self._as.ana_param_second_plot_x_axis_unit_LineEdit.text())
1892
    #                 self._mw.pulse_analysis_second_PlotWidget.setLabel(axis='left',
1893
    #                                                                    text=self._as.ana_param_second_plot_y_axis_name_LineEdit.text(),
1894
    #                                                                    units=self._as.ana_param_second_plot_y_axis_unit_LineEdit.text())
1895
    #
1896
    #             if self._mw.second_plot_ComboBox.currentText() == 'unchanged data':
1897
    #                 self._mw.pulse_analysis_second_PlotWidget.setLogMode(x=False, y=False)
1898
    #
1899
    #             elif self._mw.second_plot_ComboBox.currentText() == 'Log(x)':
1900
    #                 self._mw.pulse_analysis_second_PlotWidget.setLogMode(x=True, y=False)
1901
    #
1902
    #             elif self._mw.second_plot_ComboBox.currentText() == 'Log(y)':
1903
    #                 self._mw.pulse_analysis_second_PlotWidget.setLogMode(x=False,y=True)
1904
    #
1905
    #             elif self._mw.second_plot_ComboBox.currentText() == 'Log(x)&Log(y)':
1906
    #                 self._mw.pulse_analysis_second_PlotWidget.setLogMode(x=True, y=True)
1907
1908
    def measurement_timer_changed(self):
1909
        """ This method handles the analysis timing"""
1910
        timer_interval = self._pa.time_param_ana_periode_DoubleSpinBox.value()
1911
        self._pulsed_master_logic.analysis_interval_changed(timer_interval)
1912
        return
1913
1914
    def measurement_timer_updated(self, timer_interval_s):
1915
        """
1916
1917
        @param timer_interval_s:
1918
        @return:
1919
        """
1920
        # block signals
1921
        self._pa.time_param_ana_periode_DoubleSpinBox.blockSignals(True)
1922
        # set widget
1923
        self._pa.time_param_ana_periode_DoubleSpinBox.setValue(timer_interval_s)
1924
        # unblock signals
1925
        self._pa.time_param_ana_periode_DoubleSpinBox.blockSignals(False)
1926
        return
1927
1928
    def conv_std_dev_changed(self):
1929
        """
1930
        Uodate new value of standard deviation of gaussian filter
1931
        """
1932
        # block signals
1933
        self._pe.slider_conv_std_dev.blockSignals(True)
1934
        # set widgets
1935
        std_dev = self._pe.conv_std_dev.value()
1936
        self._pe.slider_conv_std_dev.setValue(std_dev)
1937
        # unblock signals
1938
        self._pe.slider_conv_std_dev.blockSignals(False)
1939
1940
        self._pulsed_master_logic.analysis_method_changed(std_dev)
1941
        return
1942
1943
    def slider_conv_std_dev_changed(self):
1944
        """
1945
        Uodate new value of standard deviation of gaussian filter
1946
        from slider
1947
        """
1948
        # block signals
1949
        self._pe.conv_std_dev.blockSignals(True)
1950
        # set widgets
1951
        std_dev = self._pe.slider_conv_std_dev.value()
1952
        self._pe.conv_std_dev.setValue(std_dev)
1953
        # unblock signals
1954
        self._pe.conv_std_dev.blockSignals(False)
1955
1956
        self._pulsed_master_logic.analysis_method_changed(std_dev)
1957
        return
1958
1959
    def analysis_method_updated(self, gaussfilt_std_dev):
1960
        """
1961
1962
        @param gaussfilt_std_dev:
1963
        @return:
1964
        """
1965
        # block signals
1966
        self._pe.slider_conv_std_dev.blockSignals(True)
1967
        self._pe.conv_std_dev.blockSignals(True)
1968
        # set widgets
1969
        self._pe.slider_conv_std_dev.setValue(gaussfilt_std_dev)
1970
        self._pe.conv_std_dev.setValue(gaussfilt_std_dev)
1971
        # unblock signals
1972
        self._pe.slider_conv_std_dev.blockSignals(False)
1973
        self._pe.conv_std_dev.blockSignals(False)
1974
        return
1975
1976
    def analysis_windows_changed(self):
1977
        """
1978
1979
        @return:
1980
        """
1981
        # block signals
1982
        self.sig_start_line.blockSignals(True)
1983
        self.sig_end_line.blockSignals(True)
1984
        self.ref_start_line.blockSignals(True)
1985
        self.ref_end_line.blockSignals(True)
1986
        # get data
1987
        sig_start = self._pe.extract_param_ana_window_start_SpinBox.value()
1988
        sig_length = self._pe.extract_param_ana_window_width_SpinBox.value()
1989
        ref_start = self._pe.extract_param_ref_window_start_SpinBox.value()
1990
        ref_length = self._pe.extract_param_ref_window_width_SpinBox.value()
1991
        # update plots
1992
        self.sig_start_line.setValue(sig_start)
1993
        self.sig_end_line.setValue(sig_start + sig_length)
1994
        self.ref_start_line.setValue(ref_start)
1995
        self.ref_end_line.setValue(ref_start + ref_length)
1996
        # unblock signals
1997
        self.sig_start_line.blockSignals(False)
1998
        self.sig_end_line.blockSignals(False)
1999
        self.ref_start_line.blockSignals(False)
2000
        self.ref_end_line.blockSignals(False)
2001
2002
        self._pulsed_master_logic.analysis_windows_changed(sig_start, sig_length, ref_start,
2003
                                                           ref_length)
2004
        return
2005
2006
    def analysis_windows_line_changed(self):
2007
        """
2008
2009
        @return:
2010
        """
2011
        # block signals
2012
        self._pe.extract_param_ana_window_start_SpinBox.blockSignals(True)
2013
        self._pe.extract_param_ana_window_width_SpinBox.blockSignals(True)
2014
        self._pe.extract_param_ref_window_start_SpinBox.blockSignals(True)
2015
        self._pe.extract_param_ref_window_width_SpinBox.blockSignals(True)
2016
        # get data
2017
        sig_start = self.sig_start_line.value()
2018
        sig_length = self.sig_end_line.value() - sig_start
2019
        ref_start = self.ref_start_line.value()
2020
        ref_length = self.ref_end_line.value() - ref_start
2021
        # set widgets
2022
        self._pe.extract_param_ana_window_start_SpinBox.setValue(sig_start)
2023
        self._pe.extract_param_ana_window_width_SpinBox.setValue(sig_length)
2024
        self._pe.extract_param_ref_window_start_SpinBox.setValue(ref_start)
2025
        self._pe.extract_param_ref_window_width_SpinBox.setValue(ref_length)
2026
        # unblock signals
2027
        self._pe.extract_param_ana_window_start_SpinBox.blockSignals(False)
2028
        self._pe.extract_param_ana_window_width_SpinBox.blockSignals(False)
2029
        self._pe.extract_param_ref_window_start_SpinBox.blockSignals(False)
2030
        self._pe.extract_param_ref_window_width_SpinBox.blockSignals(False)
2031
        return
2032
2033
    def analysis_windows_updated(self, sig_start, sig_length, ref_start, ref_length):
2034
        """
2035
2036
        @param sig_start:
2037
        @param sig_length:
2038
        @param ref_start:
2039
        @param ref_length:
2040
        @return:
2041
        """
2042
        # block signals
2043
        self._pe.extract_param_ana_window_start_SpinBox.blockSignals(True)
2044
        self._pe.extract_param_ana_window_width_SpinBox.blockSignals(True)
2045
        self._pe.extract_param_ref_window_start_SpinBox.blockSignals(True)
2046
        self._pe.extract_param_ref_window_width_SpinBox.blockSignals(True)
2047
        # set widgets
2048
        self._pe.extract_param_ana_window_start_SpinBox.setValue(sig_start)
2049
        self._pe.extract_param_ana_window_width_SpinBox.setValue(sig_length)
2050
        self._pe.extract_param_ref_window_start_SpinBox.setValue(ref_start)
2051
        self._pe.extract_param_ref_window_width_SpinBox.setValue(ref_length)
2052
        # update plots
2053
        self.sig_start_line.setValue(sig_start)
2054
        self.sig_end_line.setValue(sig_start + sig_length)
2055
        self.ref_start_line.setValue(ref_start)
2056
        self.ref_end_line.setValue(ref_start + ref_length)
2057
        # unblock signals
2058
        self._pe.extract_param_ana_window_start_SpinBox.blockSignals(False)
2059
        self._pe.extract_param_ana_window_width_SpinBox.blockSignals(False)
2060
        self._pe.extract_param_ref_window_start_SpinBox.blockSignals(False)
2061
        self._pe.extract_param_ref_window_width_SpinBox.blockSignals(False)
2062
        return
2063
2064
    def laser_to_show_changed(self):
2065
        """
2066
2067
        @return:
2068
        """
2069
        current_laser = self._pe.laserpulses_ComboBox.currentText()
2070
        show_raw_data = self._pe.laserpulses_display_raw_CheckBox.isChecked()
2071
        if current_laser == 'sum':
2072
            show_laser_index = 0
2073
        else:
2074
            show_laser_index = int(current_laser)
2075
2076
        self._pulsed_master_logic.laser_to_show_changed(show_laser_index, show_raw_data)
2077
        return
2078
2079
    def laser_to_show_updated(self, laser_index, show_raw_data):
2080
        """
2081
2082
        @param laser_index:
2083
        @param show_raw_data:
2084
        @return:
2085
        """
2086
        # block signals
2087
        self._pe.laserpulses_ComboBox.blockSignals(True)
2088
        self._pe.laserpulses_display_raw_CheckBox.blockSignals(True)
2089
        # set widgets
2090
        self._pe.laserpulses_ComboBox.setCurrentIndex(laser_index)
2091
        self._pe.laserpulses_display_raw_CheckBox.setChecked(show_raw_data)
2092
        # unblock signals
2093
        self._pe.laserpulses_ComboBox.blockSignals(False)
2094
        self._pe.laserpulses_display_raw_CheckBox.blockSignals(False)
2095
        return
2096
2097
    def laser_data_updated(self, x_data, y_data):
2098
        """
2099
2100
        @param x_data:
2101
        @param y_data:
2102
        @return:
2103
        """
2104
        self.lasertrace_image.setData(x=x_data, y=y_data)
2105
        return
2106
2107
2108
2109
    ###########################################################################
2110
    ###         Methods related to the Tab 'Sequence Generator':            ###
2111
    ###########################################################################
2112
    def pulser_on_off_clicked(self, checked):
2113
        """ Manually switch the pulser output on/off. """
2114
        checked = self._mw.pulser_on_off_PushButton.isChecked()
2115
        if checked:
2116
            self._mw.pulser_on_off_PushButton.setText('Pulser OFF')
2117
            self._pulsed_master_logic.toggle_pulse_generator(True)
2118
        else:
2119
            self._mw.pulser_on_off_PushButton.setText('Pulser ON')
2120
            self._pulsed_master_logic.toggle_pulse_generator(False)
2121
        return
2122
2123
    def pulser_running_updated(self, is_running):
2124
        """
2125
2126
        @param is_running:
2127
        @return:
2128
        """
2129
        # block signals
2130
        self._mw.pulser_on_off_PushButton.blockSignals(True)
2131
        # set widgets
2132
        if is_running:
2133
            self._mw.pulser_on_off_PushButton.setText('Pulser OFF')
2134
            if not self._mw.pulser_on_off_PushButton.isChecked():
2135
                self._mw.pulser_on_off_PushButton.toggle()
2136
        else:
2137
            self._mw.pulser_on_off_PushButton.setText('Pulser ON')
2138
            if self._mw.pulser_on_off_PushButton.isChecked():
2139
                self._mw.pulser_on_off_PushButton.toggle()
2140
        # unblock signals
2141
        self._mw.pulser_on_off_PushButton.blockSignals(False)
2142
        return
2143
2144
    def clear_pulser_clicked(self):
2145
        """ Delete all loaded files in the device's current memory. """
2146
        self._pulsed_master_logic.clear_pulse_generator()
2147
        return
2148
2149
    def upload_ensemble_clicked(self):
2150
        """
2151
2152
        @return:
2153
        """
2154
        # Get the ensemble name from the ComboBox
2155
        ensemble_name = self._pg.gen_ensemble_ComboBox.currentText()
2156
        # Upload the ensemble via logic module
2157
        self._pulsed_master_logic.upload_asset(ensemble_name)
2158
        # disable button
2159
        self._pg.upload_ensemble_PushButton.setEnabled(False)
2160
        self._pg.load_ensemble_PushButton.setEnabled(False)
2161
        return
2162
2163
    # def upload_sequence_clicked(self):
2164
    #     """
2165
    #
2166
    #     @return:
2167
    #     """
2168
    #     # Get the sequence name from the ComboBox
2169
    #     seq_name = self._pg.gen_sequence_ComboBox.currentText()
2170
    #     # Upload the asset via logic module
2171
    #     self._pulsed_master_logic.upload_asset(seq_name)
2172
    #     # disable button
2173
    #     self._pg.upload_sequence_PushButton.setEnabled(False)
2174
    #     self._pg.load_sequence_PushButton.setEnabled(False)
2175
    #     return
2176
2177
    def update_uploaded_assets(self, asset_names_list):
2178
        """
2179
2180
        @param asset_names_list:
2181
        @return:
2182
        """
2183
        # enable buttons
2184
        # self._pg.upload_sequence_PushButton.setEnabled(True)
2185
        self._pg.upload_ensemble_PushButton.setEnabled(True)
2186
        self._pg.load_ensemble_PushButton.setEnabled(True)
2187
        # self._pg.load_sequence_PushButton.setEnabled(True)
2188
        return
2189
2190
    def load_ensemble_clicked(self):
2191
        """
2192
2193
        @return:
2194
        """
2195
        # Get the asset name to be uploaded from the ComboBox
2196
        asset_name = self._pg.gen_ensemble_ComboBox.currentText()
2197
        # Load asset into channles via logic module
2198
        self._pulsed_master_logic.load_asset_into_channels(asset_name, {}, False)
2199
        # disable button
2200
        self._pg.load_ensemble_PushButton.setEnabled(False)
2201
        return
2202
2203
    # def load_sequence_clicked(self):
2204
    #     """
2205
    #
2206
    #     @return:
2207
    #     """
2208
    #     # Get the asset name to be uploaded from the ComboBox
2209
    #     asset_name = self._pg.gen_sequence_ComboBox.currentText()
2210
    #     # Load asset into channles via logic module
2211
    #     self._pulsed_master_logic.load_asset_into_channels(asset_name, {}, False)
2212
    #     # disable button
2213
    #     self._pg.load_sequence_PushButton.setEnabled(False)
2214
    #     return
2215
2216
    def update_loaded_asset(self, asset_name, asset_type):
2217
        """ Check the current loaded asset from the logic and update the display. """
2218
        label = self._mw.current_loaded_asset_Label
2219
        if asset_name is None:
2220
            label.setText(asset_type)
2221
        elif asset_type == 'PulseBlockEnsemble' or asset_type == 'PulseSequence':
2222
            label.setText('  {0} ({1})'.format(asset_name, asset_type))
2223
        else:
2224
            label.setText('  Unknown asset type')
2225
        # enable buttons
2226
        if asset_type == 'PulseBlockEnsemble':
2227
            self._pg.load_ensemble_PushButton.setEnabled(True)
2228
        elif asset_type == 'PulseSequence':
2229
            self._pg.load_sequence_PushButton.setEnabled(True)
2230
        return
2231
2232
2233
    # def save_plots(self):
2234
    #     """ Save plot from analysis graph as a picture. """
2235
    #     timestamp = datetime.datetime.now()
2236
    #     filetag = self._mw.save_tag_LineEdit.text()
2237
    #     filepath = self._save_logic.get_path_for_module(module_name='PulsedMeasurement')
2238
    #     if len(filetag) > 0:
2239
    #         filename = os.path.join(filepath, '{}_{}_pulsed'.format(timestamp.strftime('%Y%m%d-%H%M-%S'), filetag))
2240
    #     else:
2241
    #         filename = os.path.join(filepath, '{}_pulsed'.format(timestamp.strftime('%Y%m%d-%H%M-%S')))
2242
    #
2243
    #     # print(type(self._mw.second_plot_ComboBox.currentText()), self._mw.second_plot_ComboBox.currentText())
2244
    #     # pulse plot
2245
    #     # exporter = pg.exporters.SVGExporter(self._pa.pulse_analysis_PlotWidget.plotItem.scene())
2246
    #     # exporter.export(filename+'.svg')
2247
    #     #
2248
    #     # # auxiliary plot
2249
    #     # if 'None' not in self._mw.second_plot_ComboBox.currentText():
2250
    #     #     exporter_aux = pg.exporters.SVGExporter(self._mw.pulse_analysis_second_PlotWidget.plotItem.scene())
2251
    #     #     exporter_aux.export(filename + '_aux' + '.svg')
2252
    #
2253
    #     self._pulsed_meas_logic._save_data(filetag, timestamp)
2254
2255
2256
2257