Completed
Push — pulsed_with_queued_connections ( 6b1460...30fbbf )
by
unknown
02:58
created

PulsedMeasurementGui._update_current_pulse_block_ensemble()   F

Complexity

Conditions 30

Size

Total Lines 87

Duplication

Lines 87
Ratio 100 %

Importance

Changes 0
Metric Value
cc 30
dl 87
loc 87
rs 2.1523
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like PulsedMeasurementGui._update_current_pulse_block_ensemble() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

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 core.util.mutex import Mutex
40
from core.util import units
41
42
from logic.pulse_objects import Pulse_Block_Element
43
from logic.pulse_objects import Pulse_Block
44
from logic.pulse_objects import Pulse_Block_Ensemble
45
from logic.pulse_objects import Pulse_Sequence
46
47
from .spinbox_delegate import SpinBoxDelegate
48
from .doublespinbox_delegate import DoubleSpinBoxDelegate
49
from .combobox_delegate import ComboBoxDelegate
50
from .checkbox_delegate import CheckBoxDelegate
51
52
#FIXME: Display the Pulse
53
#FIXME: save the length in sample points (bins)
54
#FIXME: adjust the length to the bins
55
#FIXME: Later that should be able to round up the values directly within
56
#       the entering in the dspinbox for a consistent display of the
57
#       sequence length.
58
59
# =============================================================================
60
#                       Define some delegate classes.
61
# =============================================================================
62
#
63
# A general idea, which functions are customizable for our purpose it is worth
64
# to read the documentation for the QItemDelegate Class:
65
# http://pyqt.sourceforge.net/Docs/PyQt4/qitemdelegate.html
66
#
67
# If you want to delegate a row or a column of a QTableWidget, then you have
68
# at least to declare the constructors and the modification function for the
69
# displayed data (which you see in the table) and the saved data (which is
70
# handeled by the model class of the table). That means your delegate should
71
# at least contain the functions:
72
#       - createEditor
73
#       - setEditor
74
#       - updateEditorGeometry
75
#       - setModelData
76
#
77
# I.e. when editing data in an item view, editors are created and displayed by
78
# a delegate.
79
#
80
# Use the QStyledItemDelegate class instead of QItemDelegate, since the first
81
# one provides extended possibilities of painting the windows and can be
82
# changed by Qt style sheets.
83
# Since the delegate is a subclass of QItemDelegate or QStyledItemDelegate, the
84
# data it retrieves from the model is displayed in a default style, and we do
85
# not need to provide a custom paintEvent().
86
# We use QStyledItemDelegate as our base class, so that we benefit from the
87
# default delegate implementation. We could also have used
88
# QAbstractItemDelegate, if we had wanted to start completely from scratch.
89
#
90
# Examples how to create e.g. of SpinBoxdelegate in native Qt:
91
# http://qt.developpez.com/doc/4.7/itemviews-spinboxdelegate/
92
# and a similar python implementation:
93
# https://github.com/PySide/Examples/blob/master/examples/itemviews/spinboxdelegate.py
94
95
# ==============================================================================
96
#                Explanation of the usage of QTableWidget
97
# ==============================================================================
98
99
# In general a table consist out of an object for viewing the data and out of an
100
# object where the data are saved. For viewing the data, there is the general
101
# QWidgetView class and for holding/storing the data you have to define a model.
102
# The model ensures to hold your data in the proper data type and give the
103
# possibility to separate the data from the display.
104
# The QTableWidget class is a specialized class to handle user input into an
105
# table. In order to handle the data, it contains already a model (due to that
106
# you can e.g. easily add rows and columns and modify the content of each cell).
107
# Therefore the model of a QTableWidget is a privite attribute and cannot be
108
# changed externally. If you want to define a custom model for QTableWidget you
109
# have to start from a QTableView and construct you own data handling in the
110
# model.
111
# Since QTableWidget has all the (nice and) needed requirements for us, a
112
# custom definition of QTableView with a Model is not needed.
113
114
115
116
class PulsedMeasurementMainWindow(QtWidgets.QMainWindow):
117
    def __init__(self):
118
        # Get the path to the *.ui file
119
        this_dir = os.path.dirname(__file__)
120
        ui_file = os.path.join(this_dir, 'ui_pulsed_maingui.ui')
121
122
        # Load it
123
        super(PulsedMeasurementMainWindow, self).__init__()
124
125
        uic.loadUi(ui_file, self)
126
        self.show()
127
128
class BlockSettingDialog(QtWidgets.QDialog):
129
    def __init__(self):
130
        # Get the path to the *.ui file
131
        this_dir = os.path.dirname(__file__)
132
        ui_file = os.path.join(this_dir, 'ui_pulsed_main_gui_settings_block_gen.ui')
133
134
        # Load it
135
        super(BlockSettingDialog, self).__init__()
136
137
        uic.loadUi(ui_file, self)
138
139
class AnalysisSettingDialog(QtWidgets.QDialog):
140
    def __init__(self):
141
        # Get the path to the *.ui file
142
        this_dir = os.path.dirname(__file__)
143
        ui_file = os.path.join(this_dir, 'ui-pulsed-main-gui-settings-analysis.ui')
144
145
        # Load it
146
        super(AnalysisSettingDialog, self).__init__()
147
148
        uic.loadUi(ui_file, self)
149
150
class PredefinedMethodsDialog(QtWidgets.QDialog):
151
    def __init__(self):
152
        # Get the path to the *.ui file
153
        this_dir = os.path.dirname(__file__)
154
        ui_file = os.path.join(this_dir, 'ui_predefined_methods.ui')
155
156
        # Load it
157
        super(PredefinedMethodsDialog, self).__init__()
158
159
        uic.loadUi(ui_file, self)
160
161
class PredefinedMethodsConfigDialog(QtWidgets.QDialog):
162
    def __init__(self):
163
        # Get the path to the *.ui file
164
        this_dir = os.path.dirname(__file__)
165
        ui_file = os.path.join(this_dir, 'ui-predefined_methods_config.ui')
166
167
        # Load it
168
        super(PredefinedMethodsConfigDialog, self).__init__()
169
170
        uic.loadUi(ui_file, self)
171
172
class PulsedMeasurementGui(GUIBase):
173
    """ This is the main GUI Class for pulsed measurements. """
174
175
    _modclass = 'PulsedMeasurementGui'
176
    _modtype = 'gui'
177
178
    sigUploadToDevice = QtCore.Signal(str)
179
    sigLoadToChannel = QtCore.Signal(str)
180
    sigSampleEnsemble = QtCore.Signal(str, bool, bool)
181
182
    ## declare connectors
183
    _in = {'sequencegeneratorlogic': 'SequenceGeneratorLogic',
184
            'savelogic': 'SaveLogic',
185
            'pulsedmeasurementlogic': 'PulsedMeasurementLogic'
186
            }
187
188
    def __init__(self, config, **kwargs):
189
        super().__init__(config=config, **kwargs)
190
191
        self.log.info('The following configuration was found.')
192
193
        # checking for the right configuration
194
        for key in config.keys():
195
            self.log.info('{0}: {1}'.format(key,config[key]))
196
197
        #locking for thread safety
198
        self.threadlock = Mutex()
199
200
        # that variable is for testing issues and can be deleted if not needed:
201
        self._write_chunkwise = False
202
203
    def on_activate(self, e=None):
204
        """ Initialize, connect and configure the pulsed measurement GUI.
205
206
        @param object e: Fysom.event object from Fysom class.
207
                         An object created by the state machine module Fysom,
208
                         which is connected to a specific event (have a look in
209
                         the Base Class). This object contains the passed event,
210
                         the state before the event happened and the destination
211
                         of the state which should be reached after the event
212
                         had happened.
213
214
        Establish general connectivity and activate the different tabs of the
215
        GUI.
216
        """
217
218
        self._pulsed_meas_logic  = self.get_in_connector('pulsedmeasurementlogic')
219
        self._seq_gen_logic = self.get_in_connector('sequencegeneratorlogic')
220
        self._save_logic = self.get_in_connector('savelogic')
221
222
        self._mw = PulsedMeasurementMainWindow()
223
224
225
        # each tab/section has its own activation methods:
226
        self._activate_analysis_settings_ui(e)
227
        self._activate_analysis_ui(e)
228
229
        self._activate_pulse_generator_settings_ui(e)
230
        self._activate_pulse_generator_ui(e)
231
232
        self._activate_sequence_settings_ui(e)
233
        self._activate_sequence_generator_ui(e)
234
235
        self._activate_pulse_extraction_settings_ui(e)
236
        self._activate_pulse_extraction_ui(e)
237
238
        self.show()
239
240
241
    def on_deactivate(self, e):
242
        """ Undo the Definition, configuration and initialisation of the pulsed
243
            measurement GUI.
244
245
        @param object e: Fysom.event object from Fysom class. A more detailed
246
                         explanation can be found in the method initUI.
247
248
        This deactivation disconnects all the graphic modules, which were
249
        connected in the initUI method.
250
        """
251
252
        # each tab/section has its own deactivation methods:
253
        self._deactivate_analysis_settings_ui(e)
254
        self._deactivate_analysis_ui(e)
255
256
        self._deactivate_pulse_generator_settings_ui(e)
257
        self._deactivate_pulse_generator_ui(e)
258
259
        self._deactivate_sequence_settings_ui(e)
260
        self._deactivate_sequence_generator_ui(e)
261
262
        self._deactivate_pulse_extraction_settings_ui(e)
263
        self._deactivate_pulse_extraction_ui(e)
264
265
266
        self._mw.close()
267
268
    def show(self):
269
        """Make main window visible and put it above all other windows. """
270
271
        QtWidgets.QMainWindow.show(self._mw)
272
        self._mw.activateWindow()
273
        self._mw.raise_()
274
275
276
277
278
    ###########################################################################
279
    ###     Methods related to Settings for the 'Pulse Generator' Tab:      ###
280
    ###########################################################################
281
282
283 View Code Duplication
    def _activate_pulse_generator_settings_ui(self, e):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
284
        """ Initialize, connect and configure the Settings for the
285
            'Pulse Generator' Tab.
286
287
        @param object e: Fysom.event object from Fysom class. A more detailed
288
                         explanation can be found in the method initUI.
289
        """
290
291
        self._bs = BlockSettingDialog() # initialize the block settings
292
        self._bs.accepted.connect(self.apply_block_settings)
293
        self._bs.rejected.connect(self.keep_former_block_settings)
294
        self._bs.buttonBox.button(QtWidgets.QDialogButtonBox.Apply).clicked.connect(self.apply_block_settings)
295
296
        # # load in the possible channel configurations into the config
297
        # pulser_constr = self.get_hardware_constraints()
298
        # activation_config = self._seq_gen_logic.activation_config
299
        # self._bs.activation_config_ComboBox.clear()
300
        # self._bs.activation_config_ComboBox.addItems(list(pulser_constr['activation_config']))
301
        # # set ComboBox index to init value of logic
302
        # for index, config_name in enumerate(pulser_constr['activation_config']):
303
        #     if pulser_constr['activation_config'][config_name] == activation_config:
304
        #         self._bs.activation_config_ComboBox.setCurrentIndex(index)
305
        #         break
306
        #
307
        # self._bs.activation_config_ComboBox.currentIndexChanged.connect(self._update_channel_display)
308
309
        self._bs.use_interleave_CheckBox.setChecked(self._pulsed_meas_logic.get_interleave())
310
        self._bs.use_interleave_CheckBox.stateChanged.connect(self._interleave_changed)
311
312
        # create the Predefined methods Dialog
313
        self._pm = PredefinedMethodsDialog()
314
        self._predefined_methods_list = []  # here are all the names saved of
315
                                            # the created predefined methods.
316
317
        # create a config for the predefined methods:
318
        self._pm_cfg = PredefinedMethodsConfigDialog()
319
320
        # Add in the settings menu within the groupbox widget all the available
321
        # math_functions, based on the list from the Logic. Right now, the GUI
322
        # objects are inserted the 'hard' way, like it is done in the
323
        # Qt-Designer.
324
325
        # FIXME: Make a nicer way of displaying the available functions, maybe
326
        #        with a Table!
327
328
        _encoding = QtWidgets.QApplication.UnicodeUTF8
329
        objectname = self._bs.objectName()
330
        for index, func_name in enumerate(self.get_func_config_list()):
331
332
            name_label = 'func_'+ str(index)
333
            setattr(self._bs, name_label, QtWidgets.QLabel(self._bs.groupBox))
334
            label = getattr(self._bs, name_label)
335
            label.setObjectName(name_label)
336
            self._bs.gridLayout_3.addWidget(label, index, 0, 1, 1)
337
            label.setText(QtWidgets.QApplication.translate(objectname, func_name, None, _encoding))
338
339
            name_checkbox = 'checkbox_'+ str(index)
340
            setattr(self._bs, name_checkbox, QtWidgets.QCheckBox(self._bs.groupBox))
341
            checkbox = getattr(self._bs, name_checkbox)
342
            checkbox.setObjectName(name_checkbox)
343
            self._bs.gridLayout_3.addWidget(checkbox, index, 1, 1, 1)
344
            checkbox.setText(QtWidgets.QApplication.translate(objectname, '', None, _encoding))
345
346
        # make the first 4 Functions as default.
347
        # FIXME: the default functions, must be passed as a config
348
349
        for index in range(4):
350
            name_checkbox = 'checkbox_'+ str(index)
351
            checkbox = getattr(self._bs, name_checkbox)
352
            checkbox.setCheckState(QtCore.Qt.Checked)
353
354
    def _deactivate_pulse_generator_settings_ui(self, e):
355
        """ Disconnects the configuration of the Settings for the
356
            'Pulse Generator' Tab.
357
358
        @param object e: Fysom.event object from Fysom class. A more detailed
359
                         explanation can be found in the method initUI.
360
        """
361
        self._bs.accepted.disconnect()
362
        self._bs.rejected.disconnect()
363
        self._bs.buttonBox.button(QtWidgets.QDialogButtonBox.Apply).clicked.disconnect()
364
        self._bs.close()
365
366
    def _interleave_changed(self, state):
367
        """ React on a Interleave state change.
368
369
        @param int state: 0 for False and 1 or 2 for True.
370
        """
371
        self._seq_gen_logic.set_interleave(bool(state))
372
373
    def show_block_settings(self):
374
        """ Opens the block settings menue. """
375
        self._bs.exec_()
376
377
    def show_predefined_methods(self):
378
        """ Opens the predefined methods Window."""
379
        self._pm.show()
380
        self._pm.raise_()
381
382
    def show_predefined_methods_config(self):
383
        """ Opens the Window for the config of predefined methods."""
384
        self._pm_cfg.show()
385
        self._pm_cfg.raise_()
386
387
    def keep_former_predefined_methods(self):
388
389
        for method_name in self._predefined_methods_list:
390
            groupbox = self._get_ref_groupbox_predefined_methods(method_name)
391
            checkbox = self._get_ref_checkbox_predefined_methods_config(method_name)
392
393
            checkbox.setChecked(groupbox.isVisible())
394
395
    def update_predefined_methods(self):
396
397
        for method_name in self._predefined_methods_list:
398
            groupbox = self._get_ref_groupbox_predefined_methods(method_name)
399
            checkbox = self._get_ref_checkbox_predefined_methods_config(method_name)
400
401
            groupbox.setVisible(checkbox.isChecked())
402
403
404
    def apply_block_settings(self):
405
        """ Write new block settings from the gui to the file. """
406
        if self._bs.use_saupload_CheckBox.isChecked():
407
            self._set_visibility_saupload_button_pulse_gen(state=True)
408
        else:
409
            self._set_visibility_saupload_button_pulse_gen(state=False)
410
411
412
    def keep_former_block_settings(self):
413
        """ Keep the old block settings and restores them in the gui. """
414
        if self._mw.sample_ensemble_PushButton.isHidden():
415
            self._bs.use_saupload_CheckBox.setChecked(True)
416
        else:
417
            self._bs.use_saupload_CheckBox.setChecked(False)
418
419
    def _set_visibility_saupload_button_pulse_gen(self, state):
420
        """ Set whether the sample Uplaod and load Buttons should be visible or not
421
422
        @param bool state:
423
        @return:
424
        """
425
        #FIXME: Implement that functionality
426
        pass
427
428
    def get_current_function_list(self):
429
        """ Retrieve the functions, which are chosen by the user.
430
431
        @return: list[] with strings of the used functions. Names are based on
432
                 the passed func_config dict from the logic. Depending on the
433
                 settings, a current function list is generated.
434
        """
435
        current_functions = []
436
437
        for index, func_name in enumerate(self.get_func_config_list()):
438
            name_checkbox = 'checkbox_'+ str(index)
439
            checkbox = getattr(self._bs, name_checkbox)
440
            if checkbox.isChecked():
441
                name_label = 'func_'+ str(index)
442
                func = getattr(self._bs, name_label)
443
                current_functions.append(func.text())
444
445
        return current_functions
446
447
    ###########################################################################
448
    ###   Methods related to Tab 'Pulse Generator' in the Pulsed Window:    ###
449
    ###########################################################################
450
451
    def _activate_pulse_generator_ui(self, e):
452
        """ Initialize, connect and configure the 'Pulse Generator' Tab.
453
454
        @param object e: Fysom.event object from Fysom class. A more detailed
455
                         explanation can be found in the method initUI.
456
        """
457
        # connect the signal for a change of the generator parameters
458
        self._mw.gen_sample_freq_DSpinBox.editingFinished.connect(
459
            self.generator_sample_rate_changed)
460
        self._mw.gen_laserchannel_ComboBox.currentIndexChanged.connect(
461
            self.generator_laser_channel_changed)
462
        self._mw.gen_activation_config_ComboBox.currentIndexChanged.connect(
463
            self.generator_activation_config_changed)
464
465
        # connect signal for file upload and loading of pulser device
466
        self.sigSampleEnsemble.connect(self._seq_gen_logic.sample_pulse_block_ensemble)
467
        self.sigUploadToDevice.connect(self._pulsed_meas_logic.upload_asset)
468
        self.sigLoadToChannel.connect(self._pulsed_meas_logic.load_asset)
469
470
        # set them to maximum or minimum
471
        self._mw.curr_block_bins_SpinBox.setMaximum(2**31 -1)
472
        self._mw.curr_block_laserpulses_SpinBox.setMaximum(2**31 -1)
473
        self._mw.curr_ensemble_bins_SpinBox.setMaximum(2**31 -1)
474
        self._mw.curr_ensemble_length_DSpinBox.setMaximum(np.inf)
475
476
        # connect the signals for the block editor:
477
        self._mw.block_add_last_PushButton.clicked.connect(self.block_editor_add_row_after_last)
478
        self._mw.block_del_last_PushButton.clicked.connect(self.block_editor_delete_row_last)
479
        self._mw.block_add_sel_PushButton.clicked.connect(self.block_editor_add_row_before_selected)
480
        self._mw.block_del_sel_PushButton.clicked.connect(self.block_editor_delete_row_selected)
481
        self._mw.block_clear_PushButton.clicked.connect(self.block_editor_clear_table)
482
483
        self._mw.curr_block_load_PushButton.clicked.connect(self.load_pulse_block_clicked)
484
        self._mw.curr_block_del_PushButton.clicked.connect(self.delete_pulse_block_clicked)
485
486
        # connect the signals for the block organizer:
487
        self._mw.organizer_add_last_PushButton.clicked.connect(self.block_organizer_add_row_after_last)
488
        self._mw.organizer_del_last_PushButton.clicked.connect(self.block_organizer_delete_row_last)
489
        self._mw.organizer_add_sel_PushButton.clicked.connect(self.block_organizer_add_row_before_selected)
490
        self._mw.organizer_del_sel_PushButton.clicked.connect(self.block_organizer_delete_row_selected)
491
        self._mw.organizer_clear_PushButton.clicked.connect(self.block_organizer_clear_table)
492
493
        self._mw.curr_ensemble_load_PushButton.clicked.connect(self.load_pulse_block_ensemble_clicked)
494
        self._mw.curr_ensemble_del_PushButton.clicked.connect(self.delete_pulse_block_ensemble_clicked)
495
496
        # connect the signals for the "Upload on device" section
497
        self._mw.sample_ensemble_PushButton.clicked.connect(self.sample_ensemble_clicked)
498
        self._mw.upload_to_device_PushButton.clicked.connect(self.upload_to_device_clicked)
499
        self._mw.load_channel_PushButton.clicked.connect(self.load_into_channel_clicked)
500
501
        # connect the menu to the actions:
502
        self._mw.action_Settings_Block_Generation.triggered.connect(self.show_block_settings)
503
        self._mw.actionOpen_Predefined_Methods.triggered.connect(self.show_predefined_methods)
504
        self._mw.actionConfigure_Predefined_Methods.triggered.connect(self.show_predefined_methods_config)
505
506
        # emit a trigger event when for all mouse click and keyboard click events:
507
        self._mw.block_editor_TableWidget.setEditTriggers(QtWidgets.QAbstractItemView.AllEditTriggers)
508
        self._mw.block_organizer_TableWidget.setEditTriggers(QtWidgets.QAbstractItemView.AllEditTriggers)
509
        # self._mw.seq_editor_TableWidget.setEditTriggers(QtWidgets.QAbstractItemView.AllEditTriggers)
510
511
        # connect update signals of the sequence_generator_logic
512
        self._seq_gen_logic.signal_block_list_updated.connect(self.update_block_list)
513
        self._seq_gen_logic.signal_ensemble_list_updated.connect(self.update_ensemble_list)
514
        self._seq_gen_logic.sigSampleEnsembleComplete.connect(self.sample_ensemble_finished)
515
516
        # connect update signals of the pulsed_measurement_logic
517
        self._pulsed_meas_logic.sigUploadAssetComplete.connect(self.upload_to_device_finished)
518
        self._pulsed_meas_logic.sigLoadAssetComplete.connect(self.load_into_channel_finished)
519
520
        # Definition of this parameter. See fore more explanation in file
521
        # sampling_functions.py
522
        length_def = {'unit': 's', 'init_val': 0.0, 'min': 0.0, 'max': np.inf,
523
                      'view_stepsize': 1e-9, 'dec': 8, 'unit_prefix': 'n', 'type': float}
524
        rep_def = {'unit': '#', 'init_val': 0, 'min': 0, 'max': (2 ** 31 - 1),
525
                   'view_stepsize': 1, 'dec': 0, 'unit_prefix': '', 'type': int}
526
        bool_def = {'unit': 'bool', 'init_val': 0, 'min': 0, 'max': 1,
527
                    'view_stepsize': 1, 'dec': 0, 'unit_prefix': '', 'type': bool}
528
        # make a parameter constraint dict for the additional parameters of the
529
        # Pulse_Block_Ensemble objects:
530
        self._add_pbe_param = OrderedDict()
531
        self._add_pbe_param['length'] = length_def
532
        self._add_pbe_param['increment'] = length_def
533
        self._add_pbe_param['use as tick?'] = bool_def
534
        # make a parameter constraint dict for the additional parameters of the
535
        # Pulse_Block objects:
536
        self._add_pb_param = OrderedDict()
537
        self._add_pb_param['repetition'] = rep_def
538
539
        # create all the needed control widgets on the fly and connect their
540
        # actions to each other:
541
        self._create_control_for_predefined_methods()
542
        self._create_pulser_on_off_buttons()
543
        # self._create_radiobuttons_for_channels()
544
        self._create_pushbutton_clear_device()
545
        self._create_current_asset_QLabel()
546
        # filename tag input widget
547
        self._create_save_tag_input()
548
549
        self.keep_former_block_settings()
550
551
        self._set_block_editor_columns()
552
        self._set_organizer_columns()
553
554
        # connect all the needed signal to methods:
555
        self._mw.curr_block_generate_PushButton.clicked.connect(self.generate_pulse_block_clicked)
556
        self._mw.curr_ensemble_generate_PushButton.clicked.connect(self.generate_pulse_block_ensemble_clicked)
557
        self._mw.block_editor_TableWidget.itemChanged.connect(self._update_current_pulse_block)
558
559
        self._mw.block_organizer_TableWidget.itemChanged.connect(self._update_current_pulse_block_ensemble)
560
        self._mw.pulser_on_PushButton.clicked.connect(self.pulser_on_clicked)
561
        self._mw.pulser_off_PushButton.clicked.connect(self.pulser_off_clicked)
562
        self._mw.clear_device_PushButton.clicked.connect(self.clear_device_clicked)
563
564
        # the loaded asset will be updated in the GUI:
565
        self._pulsed_meas_logic.sigLoadedAssetUpdated.connect(self.update_loaded_asset)
566
567
        # initialize the lists of available blocks, ensembles and sequences
568
        self.update_block_list()
569
        self.update_ensemble_list()
570
571
        # connect the actions of the Config for Predefined methods:
572
        self._pm_cfg.accepted.connect(self.update_predefined_methods)
573
        self._pm_cfg.rejected.connect(self.keep_former_predefined_methods)
574
        self._pm_cfg.buttonBox.button(QtWidgets.QDialogButtonBox.Apply).clicked.connect(self.update_predefined_methods)
575
576
        # set the chosen predefined method to be visible:
577
        for predefined_method in self._predefined_methods_list:
578
            if predefined_method in self._statusVariables:
579
                checkbox = self._get_ref_checkbox_predefined_methods_config(predefined_method)
580
                checkbox.setChecked(self._statusVariables[predefined_method])
581
582
        self.update_predefined_methods()
583
584
        # Apply hardware constraints to input widgets
585
        self._gen_apply_hardware_constraints()
586
587
        # Fill initial values from logic into input widgets
588
        self._init_generator_values()
589
590
        # Modified by me
591
        # self._mw.init_block_TableWidget.viewport().setAttribute(QtCore.Qt.WA_Hover)
592
        # self._mw.repeat_block_TableWidget.viewport().setAttribute(QtCore.Qt.WA_Hover)
593
594
    def _deactivate_pulse_generator_ui(self, e):
595
        """ Disconnects the configuration for 'Pulse Generator Tab.
596
597
        @param object e: Fysom.event object from Fysom class. A more detailed
598
                         explanation can be found in the method initUI.
599
        """
600
        #FIXME: implement a proper deactivation for that.
601
        self._pm.close()
602
        self._pm_cfg.close()
603
604
        # save which predefined method should be visible:
605
        for predefined_method in self._predefined_methods_list:
606
            checkbox = self._get_ref_checkbox_predefined_methods_config(predefined_method)
607
            self._statusVariables[predefined_method] = checkbox.isChecked()
608
609
    def _create_save_tag_input(self):
610
        """ Add save file tag input box. """
611
        self._mw.save_tag_LineEdit = QtWidgets.QLineEdit()
612
        self._mw.save_tag_LineEdit.setMaximumWidth(200)
613
        self._mw.save_ToolBar.addWidget(self._mw.save_tag_LineEdit)
614
615
    def _create_pulser_on_off_buttons(self):
616
        """ Create Buttons for Pulser on and Pulser Off and add to toolbar. """
617
618
        self._mw.pulser_on_PushButton =  QtWidgets.QPushButton(self._mw)
619
        self._mw.pulser_on_PushButton.setText('Pulser On')
620
        self._mw.pulser_on_PushButton.setToolTip('Switch on the device.\n'
621
                                                 'The channels, which will be activated\n'
622
                                                 'are displayed on the right.')
623
        self._mw.control_ToolBar.addWidget(self._mw.pulser_on_PushButton)
624
625
626
        self._mw.pulser_off_PushButton = QtWidgets.QPushButton(self._mw)
627
        self._mw.pulser_off_PushButton.setText('Pulser Off')
628
        self._mw.pulser_off_PushButton.setToolTip('Switch off the device.\n'
629
                                                  'The channels, which will be deactivated\n'
630
                                                  'are displayed on the right.')
631
        self._mw.control_ToolBar.addWidget(self._mw.pulser_off_PushButton)
632
633
    def _create_pushbutton_clear_device(self):
634
        """ Create the  Clear Button to clear the device. """
635
636
        self._mw.clear_device_PushButton = QtWidgets.QPushButton(self._mw)
637
        self._mw.clear_device_PushButton.setText('Clear Pulser')
638
        self._mw.clear_device_PushButton.setToolTip('Clear the Pulser Device Memory\n'
639
                                                    'from all loaded files.')
640
        self._mw.control_ToolBar.addWidget(self._mw.clear_device_PushButton)
641
642 View Code Duplication
    def _gen_apply_hardware_constraints(self):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
643
        """
644
        Retrieve the constraints from pulser hardware and apply these constraints to the pulse
645
        generator GUI elements.
646
        """
647
        pulser_constr = self._pulsed_meas_logic.get_pulser_constraints()
648
        sample_min = pulser_constr['sample_rate']['min'] / 1e6
649
        sample_max = pulser_constr['sample_rate']['max'] / 1e6
650
        sample_step = pulser_constr['sample_rate']['step'] / 1e6
651
652
        self._mw.gen_sample_freq_DSpinBox.setMinimum(sample_min)
653
        self._mw.gen_sample_freq_DSpinBox.setMaximum(sample_max)
654
        self._mw.gen_sample_freq_DSpinBox.setSingleStep(sample_step)
655
        self._mw.gen_sample_freq_DSpinBox.setDecimals( (np.log10(sample_step)* -1) )
656
657
        # configure the sequence generator logic to use the hardware compatible file formats
658
        self._seq_gen_logic.waveform_format = pulser_constr['waveform_format']
659
        self._seq_gen_logic.sequence_format = pulser_constr['sequence_format']
660
661 View Code Duplication
    def _init_generator_values(self):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
662
        """
663
        This method will retrieve the initial values from the sequence_generator_logic and
664
        initializes all input GUI elements with these values.
665
        """
666
        # get init values from logic
667
        sample_rate = self._seq_gen_logic.sample_rate
668
        laser_channel = self._seq_gen_logic.laser_channel
669
        activation_config = self._seq_gen_logic.activation_config
670
        # get hardware constraints
671
        avail_activation_configs = self._pulsed_meas_logic.get_pulser_constraints()['activation_config']
672
        # init GUI elements
673
        # set sample rate
674
        self._mw.gen_sample_freq_DSpinBox.setValue(sample_rate/1e6)
675
        self.generator_sample_rate_changed()
676
        # set activation_config. This will also update the laser channel and number of channels
677
        # from the logic.
678
        self._mw.gen_activation_config_ComboBox.blockSignals(True)
679
        self._mw.gen_activation_config_ComboBox.clear()
680
        self._mw.gen_activation_config_ComboBox.addItems(list(avail_activation_configs))
681
        found_config = False
682
        for config in avail_activation_configs:
683
            if avail_activation_configs[config] == activation_config:
684
                index = self._mw.gen_activation_config_ComboBox.findText(config)
685
                self._mw.gen_activation_config_ComboBox.setCurrentIndex(index)
686
                found_config = True
687
                break
688
        if not found_config:
689
            self._mw.gen_activation_config_ComboBox.setCurrentIndex(0)
690
        self._mw.gen_activation_config_ComboBox.blockSignals(False)
691
        self.generator_activation_config_changed()
692
693
    def _create_current_asset_QLabel(self):
694
        """ Creaate a QLabel Display for the currently loaded asset for the toolbar. """
695
        self._mw.current_loaded_asset_Label = self._create_QLabel(self._mw, '  No Asset Loaded')
696
        self._mw.current_loaded_asset_Label.setToolTip('Display the currently loaded asset.')
697
        self._mw.control_ToolBar.addWidget(self._mw.current_loaded_asset_Label)
698
699
    def update_loaded_asset(self):
700
        """ Check the current loaded asset from the logic and update the display. """
701
702
        if self._mw.current_loaded_asset_Label is None:
703
            self._create_current_asset_QLabel()
704
        label = self._mw.current_loaded_asset_Label
705
        asset_name = self._pulsed_meas_logic.loaded_asset_name
706
        if asset_name is None:
707
            asset = None
708
        else:
709
            asset = self._seq_gen_logic.get_saved_asset(asset_name)
710
            if asset is None:
711
                self._pulsed_meas_logic.clear_pulser()
712
713
        if asset is None:
714
            label.setText('  No asset loaded')
715
        elif type(asset).__name__ is 'Pulse_Block_Ensemble':
716
            label.setText('  {0} (Pulse_Block_Ensemble)'.format(asset.name))
717
        elif type(asset).__name__ is 'Pulse_Sequence':
718
            label.setText('  {0} (Pulse_Sequence)'.format(asset.name))
719
        else:
720
            label.setText('  Unknown asset type')
721
722
    def pulser_on_clicked(self):
723
        """ Switch on the pulser output. """
724
        self._pulsed_meas_logic.pulse_generator_on()
725
726
    def pulser_off_clicked(self):
727
        """ Switch off the pulser output. """
728
        self._pulsed_meas_logic.pulse_generator_off()
729
730
    def get_func_config_list(self):
731
        """ Retrieve the possible math functions as a list of strings.
732
733
        @return: list[] with string entries as function names.
734
        """
735
        return list(self._seq_gen_logic.func_config)
736
737
    def get_current_pulse_block_list(self):
738
        """ Retrieve the available Pulse_Block objects from the logic.
739
740
        @return: list[] with strings descriping the available Pulse_Block
741
                        objects.
742
        """
743
        return self._seq_gen_logic.saved_pulse_blocks
744
745
    def get_current_ensemble_list(self):
746
        """ Retrieve the available Pulse_Block_Ensemble objects from the logic.
747
748
        @return: list[] with strings descriping the available Pulse_Block_Ensemble objects.
749
        """
750
        return self._seq_gen_logic.saved_pulse_block_ensembles
751
752 View Code Duplication
    def generator_sample_rate_changed(self):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
753
        """
754
        Is called whenever the sample rate for the sequence generation has changed in the GUI
755
        """
756
        self._mw.gen_sample_freq_DSpinBox.blockSignals(True)
757
        sample_rate = self._mw.gen_sample_freq_DSpinBox.value()*1e6
758
        actual_sample_rate = self._seq_gen_logic.set_sample_rate(sample_rate)
759
        self._mw.gen_sample_freq_DSpinBox.setValue(actual_sample_rate/1e6)
760
        self._mw.gen_sample_freq_DSpinBox.blockSignals(False)
761
        self._update_current_pulse_block()
762
        self._update_current_pulse_block_ensemble()
763
        # self._update_current_pulse_sequence()
764
765 View Code Duplication
    def generator_laser_channel_changed(self):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
766
        """
767
        Is called whenever the laser channel for the sequence generation has changed in the GUI
768
        """
769
        self._mw.gen_laserchannel_ComboBox.blockSignals(True)
770
        laser_channel = self._mw.gen_laserchannel_ComboBox.currentText()
771
        actual_laser_channel = self._seq_gen_logic.set_laser_channel(laser_channel)
772
        index = self._mw.gen_laserchannel_ComboBox.findText(actual_laser_channel)
773
        self._mw.gen_laserchannel_ComboBox.setCurrentIndex(index)
774
        self._mw.gen_laserchannel_ComboBox.blockSignals(False)
775
        self._update_current_pulse_block()
776
        self._update_current_pulse_block_ensemble()
777
778 View Code Duplication
    def generator_activation_config_changed(self):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
779
        """
780
        Is called whenever the channel config for the sequence generation has changed in the GUI
781
        """
782
        self._mw.block_editor_TableWidget.blockSignals(True)
783
        # retreive GUI inputs
784
        new_config_name = self._mw.gen_activation_config_ComboBox.currentText()
785
        new_channel_config = self._pulsed_meas_logic.get_pulser_constraints()['activation_config'][new_config_name]
786
        # set chosen config in sequence generator logic
787
        self._seq_gen_logic.set_activation_config(new_channel_config)
788
        # set display new config alongside with number of channels
789
        display_str = ''
790
        for chnl in new_channel_config:
791
            display_str += chnl + ' | '
792
        display_str = display_str[:-3]
793
        self._mw.gen_activation_config_LineEdit.setText(display_str)
794
        self._mw.gen_analog_channels_SpinBox.setValue(self._seq_gen_logic.analog_channels)
795
        self._mw.gen_digital_channels_SpinBox.setValue(self._seq_gen_logic.digital_channels)
796
        # and update the laser channel combobx
797
        self._mw.gen_laserchannel_ComboBox.blockSignals(True)
798
        self._mw.gen_laserchannel_ComboBox.clear()
799
        self._mw.gen_laserchannel_ComboBox.addItems(new_channel_config)
800
        # set the laser channel in the ComboBox
801
        laser_channel = self._seq_gen_logic.laser_channel
802
        index = self._mw.gen_laserchannel_ComboBox.findText(laser_channel)
803
        self._mw.gen_laserchannel_ComboBox.setCurrentIndex(index)
804
        self._mw.gen_laserchannel_ComboBox.blockSignals(False)
805
806
        # reshape block editor table
807
        self._set_block_editor_columns()
808
809
        self._mw.block_editor_TableWidget.blockSignals(False)
810
811
        self._update_current_pulse_block()
812
        self._update_current_pulse_block_ensemble()
813
814
    def sample_ensemble_clicked(self):
815
        """
816
        This method is called when the user clicks on "sample"
817
        """
818
        # disable the "sample ensemble" button until the sampling process is finished.
819
        if self._mw.sample_ensemble_PushButton.isEnabled():
820
            self._mw.sample_ensemble_PushButton.setEnabled(False)
821
        # Also disable the "upload" and "load" buttons to prevent loading of a previously sampled
822
        # file.
823
        if self._mw.upload_to_device_PushButton.isEnabled():
824
            self._mw.upload_to_device_PushButton.setEnabled(False)
825
        if self._mw.load_channel_PushButton.isEnabled():
826
            self._mw.load_channel_PushButton.setEnabled(False)
827
        # Get the ensemble name to be uploaded from the ComboBox
828
        ensemble_name = self._mw.upload_ensemble_ComboBox.currentText()
829
        # Sample the ensemble via logic module
830
        self.sigSampleEnsemble.emit(ensemble_name, True, self._write_chunkwise)
831
        return
832
833
    def sample_ensemble_finished(self):
834
        """
835
        Reenables the "sample ensemble" button once the sampling process is finished.
836
        """
837
        if not self._mw.sample_ensemble_PushButton.isEnabled():
838
            self._mw.sample_ensemble_PushButton.setEnabled(True)
839
        if not self._mw.upload_to_device_PushButton.isEnabled():
840
            self._mw.upload_to_device_PushButton.setEnabled(True)
841
        if not self._mw.load_channel_PushButton.isEnabled():
842
            self._mw.load_channel_PushButton.setEnabled(True)
843
        return
844
845
    def upload_to_device_clicked(self):
846
        """
847
        This method is called when the user clicks on "upload to device"
848
        """
849
        # disable the "upload to device" button until the upload process is finished.
850
        if self._mw.upload_to_device_PushButton.isEnabled():
851
            self._mw.upload_to_device_PushButton.setEnabled(False)
852
        # Get the ensemble name to be uploaded from the ComboBox
853
        ensemble_name = self._mw.upload_ensemble_ComboBox.currentText()
854
        # Upload the ensemble waveform via logic module.
855
        self.sigUploadToDevice.emit(ensemble_name)
856
        return
857
858
    def upload_to_device_finished(self):
859
        """
860
        Reenables the "upload to device" button once the upload process is finished.
861
        """
862
        if not self._mw.upload_to_device_PushButton.isEnabled():
863
            self._mw.upload_to_device_PushButton.setEnabled(True)
864
        return
865
866
    def load_into_channel_clicked(self):
867
        """
868
        This method is called when the user clicks on "load to channel"
869
        """
870
        # disable the "load to channel" button until the load process is finished.
871
        if self._mw.load_channel_PushButton.isEnabled():
872
            self._mw.load_channel_PushButton.setEnabled(False)
873
874
        # Get the asset name to be uploaded from the ComboBox
875
        asset_name = self._mw.upload_ensemble_ComboBox.currentText()
876
877
        # FIXME: Implement a proper GUI element (upload center) to manually assign assets to channels
878
        # Right now the default is chosen to invoke channel assignment from the Ensemble/Sequence object
879
880
        # stop the pulser hardware output if it is running
881
        self._pulsed_meas_logic.pulse_generator_off()
882
883
        # configure pulser with the same settings that were chosen during ensemble generation.
884
        # This information is stored in the ensemble object.
885
        asset_obj = self._seq_gen_logic.get_saved_asset(asset_name)
886
887
        # Set proper activation config
888
        activation_config = asset_obj.activation_config
889
        config_name = None
890
        avail_configs = self._pulsed_meas_logic.get_pulser_constraints()['activation_config']
891
        for config in avail_configs:
892
            if activation_config == avail_configs[config]:
893
                config_name = config
894
                break
895
        if config_name != self._mw.pulser_activation_config_ComboBox.currentText():
896
            index = self._mw.pulser_activation_config_ComboBox.findText(config_name)
897
            self._mw.pulser_activation_config_ComboBox.setCurrentIndex(index)
898
899
        # Set proper sample rate
900
        if self._pulsed_meas_logic.sample_rate != asset_obj.sample_rate:
901
            self._mw.pulser_sample_freq_DSpinBox.setValue(asset_obj.sample_rate/1e6)
902
            self.pulser_sample_rate_changed()
903
904
905
        self._pulsed_meas_logic.set_num_of_lasers(asset_obj.number_of_lasers)
906
        self._pulsed_meas_logic.sequence_length_s = asset_obj.length_bins / asset_obj.sample_rate
907
908
        # Load asset into channles via logic module
909
        self.sigLoadToChannel.emit(asset_name)
910
        return
911
912
    def load_into_channel_finished(self):
913
        """
914
        Reenables the "load to channel" button once the load process is finished.
915
        """
916
        if not self._mw.load_channel_PushButton.isEnabled():
917
            self._mw.load_channel_PushButton.setEnabled(True)
918
        return
919
920
    def update_block_list(self):
921
        """
922
        This method is called upon signal_block_list_updated emit of the sequence_generator_logic.
923
        Updates all ComboBoxes showing generated blocks.
924
        """
925
        # updated list of all generated blocks
926
        new_list = self._seq_gen_logic.saved_pulse_blocks
927
        # update saved_blocks_ComboBox items
928
        self._mw.saved_blocks_ComboBox.clear()
929
        self._mw.saved_blocks_ComboBox.addItems(new_list)
930
        return
931
932 View Code Duplication
    def update_ensemble_list(self):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
933
        """
934
        This method is called upon signal_ensemble_list_updated emit of the sequence_generator_logic.
935
        Updates all ComboBoxes showing generated block_ensembles.
936
        """
937
        # updated list of all generated ensembles
938
        new_list = self._seq_gen_logic.saved_pulse_block_ensembles
939
        # update upload_ensemble_ComboBox items
940
        self._mw.upload_ensemble_ComboBox.clear()
941
        self._mw.upload_ensemble_ComboBox.addItems(new_list)
942
        # update saved_ensembles_ComboBox items
943
        self._mw.saved_ensembles_ComboBox.clear()
944
        self._mw.saved_ensembles_ComboBox.addItems(new_list)
945
        # Set active index of the ComboBoxes to the currently shown/last created ensemble
946
        if self._seq_gen_logic.current_ensemble is not None:
947
            # get last generated and currently shown ensemble name from logic
948
            current_ensemble_name = self._seq_gen_logic.current_ensemble.name
949
            # identify the corresponding index within the ComboBox
950
            index_to_set = self._mw.upload_ensemble_ComboBox.findText(current_ensemble_name)
951
            # Set index inside the ComboBox
952
            self._mw.upload_ensemble_ComboBox.setCurrentIndex(index_to_set)
953
            self._mw.saved_ensembles_ComboBox.setCurrentIndex(index_to_set)
954
        else:
955
            # set the current ensemble in the logic and all ComboBoxes to the currently
956
            # shown ensemble in the upload_ensemble_ComboBox.
957
            current_ensemble_name = self._mw.upload_ensemble_ComboBox.currentText()
958
            index_to_set = self._mw.saved_ensembles_ComboBox.findText(current_ensemble_name)
959
            self._mw.saved_ensembles_ComboBox.setCurrentIndex(index_to_set)
960
            self.load_pulse_block_ensemble_clicked()
961
        return
962
963
    def clear_device_clicked(self):
964
        """ Delete all loaded files in the device's current memory. """
965
        self._pulsed_meas_logic.clear_pulser()
966
967 View Code Duplication
    def generate_pulse_block_object(self, pb_name, block_matrix):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
968
        """ Generates from an given table block_matrix a block_object.
969
970
        @param pb_name: string, Name of the created Pulse_Block Object
971
        @param block_matrix: structured np.array, matrix, in which the
972
                             construction plan for Pulse_Block_Element objects
973
                             are displayed as rows.
974
975
        Three internal dict where used, to get all the needed information about
976
        how parameters, functions are defined (_add_pbe_param,func_config and
977
        _unit_prefix).
978
        The dict cfg_param_pbe (configuration parameter declaration dict for
979
        Pulse_Block_Element) stores how the objects are appearing in the GUI.
980
        This dict enables the proper access to the desired element in the GUI.
981
        """
982
983
        # list of all the pulse block element objects
984
        pbe_obj_list = [None] * len(block_matrix)
985
986
        # seperate digital and analogue channels
987
        activation_config = self._seq_gen_logic.activation_config
988
        analog_chnl_names = [chnl for chnl in activation_config if 'a_ch' in chnl]
989
        digital_chnl_names = [chnl for chnl in activation_config if 'd_ch' in chnl]
990
991
        for row_index, row in enumerate(block_matrix):
992
            # check how length is displayed and convert it to bins:
993
            length_time = row[self._cfg_param_pbe['length']]
994
            init_length_bins = int(np.round(length_time * self._seq_gen_logic.sample_rate))
995
996
            # check how increment is displayed and convert it to bins:
997
            increment_time = row[self._cfg_param_pbe['increment']]
998
            increment_bins = int(np.round(increment_time * self._seq_gen_logic.sample_rate))
999
1000
            # get the dict with all possible functions and their parameters:
1001
            func_dict = self._seq_gen_logic.func_config
1002
1003
            # get the proper pulse_functions and its parameters:
1004
            pulse_function = [None] * self._seq_gen_logic.analog_channels
1005
            parameter_list = [None] * self._seq_gen_logic.analog_channels
1006
            for num in range(self._seq_gen_logic.analog_channels):
1007
                # get the number of the analogue channel according to the channel activation_config
1008
                a_chnl_number = analog_chnl_names[num].split('ch')[-1]
1009
                pulse_function[num] = row[self._cfg_param_pbe['function_' + a_chnl_number]].decode(
1010
                    'UTF-8')
1011
1012
                # search for this function in the dictionary and get all the
1013
                # parameter with their names in list:
1014
                param_dict = func_dict[pulse_function[num]]
1015
                parameters = {}
1016
                for entry in list(param_dict):
1017
                    # Obtain how the value is displayed in the table:
1018
                    param_value = row[self._cfg_param_pbe[entry + '_' + a_chnl_number]]
1019
                    parameters[entry] = param_value
1020
                parameter_list[num] = parameters
1021
1022
            digital_high = [None] * self._seq_gen_logic.digital_channels
1023
            for num in range(self._seq_gen_logic.digital_channels):
1024
                # get the number of the digital channel according to the channel activation_config
1025
                d_chnl_number = digital_chnl_names[num].split('ch')[-1]
1026
                digital_high[num] = bool(row[self._cfg_param_pbe['digital_' + d_chnl_number]])
1027
1028
            use_as_tick = bool(row[self._cfg_param_pbe['use']])
1029
1030
            # create here actually the object with all the obtained information:
1031
            pbe_obj_list[row_index] = Pulse_Block_Element(init_length_bins=init_length_bins,
1032
                                                          increment_bins=increment_bins,
1033
                                                          pulse_function=pulse_function,
1034
                                                          digital_high=digital_high,
1035
                                                          parameters=parameter_list,
1036
                                                          use_as_tick=use_as_tick)
1037
1038
        pb_obj = Pulse_Block(pb_name, pbe_obj_list)
1039
        # save block
1040
        self._seq_gen_logic.save_block(pb_name, pb_obj)
1041
1042 View Code Duplication
    def generate_pulse_block_ensemble_object(self, ensemble_name, ensemble_matrix, rotating_frame=True):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1043
        """
1044
        Generates from an given table ensemble_matrix a ensemble object.
1045
1046
        @param str ensemble_name: Name of the created Pulse_Block_Ensemble object
1047
        @param np.array ensemble_matrix: structured 2D np.array, matrix, in which the construction
1048
                                         plan for Pulse_Block objects are displayed as rows.
1049
        @param str laser_channel: the channel controlling the laser
1050
        @param bool rotating_frame: optional, whether the phase preservation is mentained
1051
                                    throughout the sequence.
1052
1053
        The dict cfg_param_pb (configuration parameter declaration dict for Pulse_Block) stores how
1054
        the objects are related to each other in a sequencial way. That relationship is used in the
1055
        GUI, where the parameters appear in columns.
1056
        This dict enables the proper access to the desired element in the GUI.
1057
        """
1058
1059
        # list of all the pulse block element objects
1060
        pb_obj_list = [None] * len(ensemble_matrix)
1061
1062
        for row_index, row in enumerate(ensemble_matrix):
1063
            pulse_block_name = row[self._cfg_param_pb['pulse_block']].decode('UTF-8')
1064
            pulse_block_reps = row[self._cfg_param_pb['repetition']]
1065
            # Fetch previously saved block object
1066
            block = self._seq_gen_logic.get_pulse_block(pulse_block_name)
1067
            # Append block object along with repetitions to the block list
1068
            pb_obj_list[row_index] = (block, pulse_block_reps)
1069
1070
        # Create the Pulse_Block_Ensemble object
1071
        pulse_block_ensemble = Pulse_Block_Ensemble(name=ensemble_name, block_list=pb_obj_list,
1072
                                                    activation_config=self._seq_gen_logic.activation_config,
1073
                                                    sample_rate=self._seq_gen_logic.sample_rate,
1074
                                                    laser_channel=self._seq_gen_logic.laser_channel,
1075
                                                    rotating_frame=rotating_frame)
1076
        # save ensemble
1077
        self._seq_gen_logic.save_ensemble(ensemble_name, pulse_block_ensemble)
1078
1079
    # -------------------------------------------------------------------------
1080
    #           Methods for the Pulse Block Editor
1081
    # -------------------------------------------------------------------------
1082
1083 View Code Duplication
    def _update_current_pulse_block(self):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1084
        """ Update the current Pulse Block Info in the display. """
1085
        length = 0.0 # in ns
1086
        bin_length = 0
1087
        col_ind = self._cfg_param_pbe['length']
1088
1089
        laser_channel = self._seq_gen_logic.laser_channel
1090
        num_laser_ch = 0
1091
1092
        # Simple search routine:
1093
        if 'a' in laser_channel:
1094
            # extract with regular expression module the number from the
1095
            # string:
1096
            num = re.findall('\d+', laser_channel)
1097
            laser_column = self._cfg_param_pbe['function_'+str(num[0])]
1098
        elif 'd' in laser_channel:
1099
            num = re.findall('\d+', laser_channel)
1100
            laser_column = self._cfg_param_pbe['digital_'+str(num[0])]
1101
        else:
1102
            return
1103
1104
        # This bool is to prevent two consecutive laser on states to be counted as two laser pulses.
1105
        laser_on = False
1106
        # Iterate over the editor rows
1107
        for row_ind in range(self._mw.block_editor_TableWidget.rowCount()):
1108
            curr_length = self.get_element_in_block_table(row_ind, col_ind)
1109
            curr_bin_length = int(np.round(curr_length*(self._seq_gen_logic.sample_rate)))
1110
            length += curr_length
1111
            bin_length += curr_bin_length
1112
1113
            laser_val =self.get_element_in_block_table(row_ind, laser_column)
1114
            if laser_val in ('DC', 2):
1115
                if not laser_on:
1116
                    num_laser_ch += 1
1117
                    laser_on = True
1118
            else:
1119
                laser_on = False
1120
1121
        #FIXME: The display unit will be later on set in the settings, so that
1122
        #       one can choose which units are suiting the best. For now on it
1123
        #       will be fixed to microns.
1124
1125
        self._mw.curr_block_length_DSpinBox.setValue(length*1e6) # in microns
1126
        self._mw.curr_block_bins_SpinBox.setValue(bin_length)
1127
        self._mw.curr_block_laserpulses_SpinBox.setValue(num_laser_ch)
1128
1129
    def _determine_needed_parameters(self):
1130
        """ Determine the maximal number of needed parameters for desired functions.
1131
1132
        @return ('<biggest_func_name>, number_of_parameters)
1133
        """
1134
1135
        # FIXME: Reimplement this function such that it will return the
1136
        #       parameters of all needed functions and not take only the
1137
        #       parameters of the biggest function. Then the return should be
1138
        #       not the biggest function, but a set of all the needed
1139
        #       parameters which is obtained from get_func_config()!
1140
1141
1142
        curr_func_list = self.get_current_function_list()
1143
        complete_func_config = self._seq_gen_logic.func_config
1144
1145
        num_max_param = 0
1146
        biggest_func = ''
1147
1148
        for func in curr_func_list:
1149
            if num_max_param < len(complete_func_config[func]):
1150
                num_max_param = len(complete_func_config[func])
1151
                biggest_func = func
1152
1153
        return (num_max_param, biggest_func)
1154
1155
    # -------------------------------------------------------------------------
1156
    #           Methods for the Pulse Block Organizer
1157
    # -------------------------------------------------------------------------
1158
1159 View Code Duplication
    def _update_current_pulse_block_ensemble(self):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1160
        length_mu = 0.0  # in microseconds
1161
        length_bin = 0
1162
        num_laser_pulses = 0
1163
        filesize_bytes = 0
1164
        pulse_block_col = self._cfg_param_pb['pulse_block']
1165
1166
        reps_col = self._cfg_param_pb['repetition']
1167
1168
        if len(self._seq_gen_logic.saved_pulse_blocks) > 0:
1169
            for row_ind in range(self._mw.block_organizer_TableWidget.rowCount()):
1170
                pulse_block_name = self.get_element_in_organizer_table(row_ind, pulse_block_col)
1171
1172
                block_obj = self._seq_gen_logic.get_pulse_block(pulse_block_name)
1173
1174
                reps = self.get_element_in_organizer_table(row_ind, reps_col)
1175
1176
                # Calculate the length via the gaussian summation formula:
1177
                length_bin = int(length_bin + block_obj.init_length_bins * (reps + 1) +
1178
                             ((reps + 1) * ((reps + 1) + 1) / 2) * block_obj.increment_bins)
1179
1180
                # Calculate the number of laser pulses
1181
                num_laser_pulses_block = 0
1182
                if self._seq_gen_logic.laser_channel is None:
1183
                    num_laser_pulses_block = 0
1184
                elif 'd_ch' in self._seq_gen_logic.laser_channel:
1185
                    # determine the laser channel index for the corresponding channel
1186
                    digital_chnl_list = [chnl for chnl in self._seq_gen_logic.activation_config if
1187
                                         'd_ch' in chnl]
1188
                    laser_index = digital_chnl_list.index(self._seq_gen_logic.laser_channel)
1189
                    # Iterate through the elements and count laser on state changes
1190
                    # (no double counting)
1191
                    laser_on = False
1192
                    for elem in block_obj.element_list:
1193
                        if laser_index >= len(elem.digital_high):
1194
                            break
1195
                        if elem.digital_high[laser_index] and not laser_on:
1196
                            num_laser_pulses_block += 1
1197
                            laser_on = True
1198
                        elif not elem.digital_high[laser_index]:
1199
                            laser_on = False
1200
                elif 'a_ch' in self._seq_gen_logic.laser_channel:
1201
                    # determine the laser channel index for the corresponding channel
1202
                    analog_chnl_list = [chnl for chnl in self._seq_gen_logic.activation_config if
1203
                                        'a_ch' in chnl]
1204
                    laser_index = analog_chnl_list.index(self._seq_gen_logic.laser_channel)
1205
                    # Iterate through the elements and count laser on state changes
1206
                    # (no double counting)
1207
                    laser_on = False
1208
                    for elem in block_obj.element_list:
1209
                        if laser_index >= len(elem.pulse_function):
1210
                            break
1211
                        if elem.pulse_function[laser_index] == 'DC' and not laser_on:
1212
                            num_laser_pulses_block += 1
1213
                            laser_on = True
1214
                        elif elem.pulse_function[laser_index] != 'DC':
1215
                            laser_on = False
1216
                num_laser_pulses += num_laser_pulses_block*(reps+1)
1217
1218
1219
            length_mu = (length_bin / self._seq_gen_logic.sample_rate) * 1e6  # in microns
1220
1221
        # get file format to determine the file size in bytes.
1222
        # This is just an estimate since it does not include file headers etc..
1223
        # FIXME: This is just a crude first try to implement this. Improvement required.
1224
        file_format = self._pulsed_meas_logic.get_pulser_constraints()['waveform_format']
1225
        if file_format == 'wfm':
1226
            num_ana_chnl = self._seq_gen_logic.analog_channels
1227
            filesize_bytes = num_ana_chnl * 5 * length_bin
1228
        elif file_format == 'wfmx':
1229
            chnl_config = self._seq_gen_logic.activation_config
1230
            analogue_chnl_num = [int(chnl.split('ch')[1]) for chnl in chnl_config if 'a_ch' in chnl]
1231
            digital_chnl_num= [int(chnl.split('ch')[1]) for chnl in chnl_config if 'd_ch' in chnl]
1232
            for ana_chnl in analogue_chnl_num:
1233
                if (ana_chnl*2-1) in digital_chnl_num or (ana_chnl*2) in digital_chnl_num:
1234
                    filesize_bytes += 5 * length_bin
1235
                else:
1236
                    filesize_bytes += 4 * length_bin
1237
        elif file_format == 'fpga':
1238
            filesize_bytes = length_bin
1239
        else:
1240
            filesize_bytes = 0
1241
1242
        self._mw.curr_ensemble_size_DSpinBox.setValue(filesize_bytes/(1024**2))
1243
        self._mw.curr_ensemble_length_DSpinBox.setValue(length_mu)
1244
        self._mw.curr_ensemble_bins_SpinBox.setValue(length_bin)
1245
        self._mw.curr_ensemble_laserpulses_SpinBox.setValue(num_laser_pulses)
1246
1247 View Code Duplication
    def block_editor_add_row_before_selected(self, insert_rows=1):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1248
        """ Add row before selected element. """
1249
1250
        self._mw.block_editor_TableWidget.blockSignals(True)
1251
1252
        selected_row = self._mw.block_editor_TableWidget.currentRow()
1253
1254
        # the signal passes a boolean value, which overwrites the insert_rows
1255
        # parameter. Check that here and use the actual default value:
1256
        if type(insert_rows) is bool:
1257
            insert_rows = 1
1258
1259
        for rows in range(insert_rows):
1260
            self._mw.block_editor_TableWidget.insertRow(selected_row)
1261
        self.initialize_cells_block_editor(start_row=selected_row,
1262
                                           stop_row=selected_row + insert_rows)
1263
1264
        self._mw.block_editor_TableWidget.blockSignals(False)
1265
1266 View Code Duplication
    def block_editor_add_row_after_last(self, insert_rows=1):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1267
        """ Add row after last row in the block editor. """
1268
1269
        self._mw.block_editor_TableWidget.blockSignals(True)
1270
1271
        # the signal passes a boolean value, which overwrites the insert_rows
1272
        # parameter. Check that here and use the actual default value:
1273
        if type(insert_rows) is bool:
1274
            insert_rows = 1
1275
1276
        number_of_rows = self._mw.block_editor_TableWidget.rowCount()
1277
1278
        self._mw.block_editor_TableWidget.setRowCount(
1279
            number_of_rows + insert_rows)
1280
        self.initialize_cells_block_editor(start_row=number_of_rows,
1281
                                           stop_row=number_of_rows + insert_rows)
1282
1283
        self._mw.block_editor_TableWidget.blockSignals(False)
1284
1285
    def block_editor_delete_row_selected(self):
1286
        """ Delete row of selected element. """
1287
1288
        # get the row number of the selected item(s). That will return the
1289
        # lowest selected row
1290
        row_to_remove = self._mw.block_editor_TableWidget.currentRow()
1291
        self._mw.block_editor_TableWidget.removeRow(row_to_remove)
1292
1293
    def block_editor_delete_row_last(self):
1294
        """ Delete the last row in the block editor. """
1295
1296
        number_of_rows = self._mw.block_editor_TableWidget.rowCount()
1297
        # remember, the row index is started to count from 0 and not from 1,
1298
        # therefore one has to reduce the value by 1:
1299
        self._mw.block_editor_TableWidget.removeRow(number_of_rows - 1)
1300
1301
    def block_editor_clear_table(self):
1302
        """ Delete all rows in the block editor table. """
1303
1304
        self._mw.block_editor_TableWidget.blockSignals(True)
1305
1306
        self._mw.block_editor_TableWidget.setRowCount(1)
1307
        self._mw.block_editor_TableWidget.clearContents()
1308
1309
        self.initialize_cells_block_editor(start_row=0)
1310
        self._mw.block_editor_TableWidget.blockSignals(False)
1311
    #
1312
    # def load_pulse_block_ensemble_clicked(self, ensemble_name=None):
1313
    #     """ Loads the current selected Pulse_Block_Ensemble object from the
1314
    #         logic into the editor or a specified object with name ensemble_name.
1315
    #
1316
    #     @param str ensemble_name: optional, name of the Pulse_Block_Element
1317
    #                               object, which should be loaded in the GUI
1318
    #                               Block Organizer. If no name passed, the
1319
    #                               current Pulse_Block_Ensemble from the Logic is
1320
    #                               taken to be loaded.
1321
    #
1322
    #     Unfortuanetly this method needs to know how Pulse_Block_Ensemble objects
1323
    #     are looking like and cannot be that general.
1324
    #     """
1325
    #
1326
    #     # NOTE: This method will be connected to the CLICK event of a
1327
    #     #       QPushButton, which passes as an optional argument as a bool
1328
    #     #       value depending on the checked state of the QPushButton. The
1329
    #     #       passed boolean value has to be handled in addition!
1330
    #
1331
    #     if (ensemble_name is not None) and (type(ensemble_name) is not bool):
1332
    #         current_ensemble_name = ensemble_name
1333
    #     else:
1334
    #         current_ensemble_name = self._mw.saved_ensembles_ComboBox.currentText()
1335
    #
1336
    #     # get the ensemble object and set as current ensemble
1337
    #     ensemble = self._seq_gen_logic.get_pulse_block_ensemble(current_ensemble_name,
1338
    #                                                 set_as_current_ensemble=True)
1339
    #
1340
    #     # Check whether an ensemble is found, otherwise there will be None:
1341
    #     if ensemble is None:
1342
    #         return
1343
    #
1344
    #     # set the activation_config to the one defined in the loaded ensemble
1345
    #     avail_configs = self._pulsed_meas_logic.get_pulser_constraints()['activation_config']
1346
    #     current_activation_config = self._seq_gen_logic.activation_config
1347
    #     activation_config_to_set = ensemble.activation_config
1348
    #     config_name_to_set = None
1349
    #     if current_activation_config != activation_config_to_set:
1350
    #         for config in avail_configs:
1351
    #             if activation_config_to_set == avail_configs[config]:
1352
    #                 config_name_to_set = config
1353
    #                 break
1354
    #         if config_name_to_set is not None:
1355
    #             index = self._mw.gen_activation_config_ComboBox.findText(config_name_to_set)
1356
    #             self._mw.gen_activation_config_ComboBox.setCurrentIndex(index)
1357
    #         self.log.info('Current generator channel activation config '
1358
    #                 'did not match the activation config of the '
1359
    #                 'Pulse_Block_Ensemble to load. Changed config to "{0}".'
1360
    #                 ''.format(config_name_to_set))
1361
    #
1362
    #     # set the sample rate to the one defined in the loaded ensemble
1363
    #     current_sample_rate = self._seq_gen_logic.sample_rate
1364
    #     sample_rate_to_set = ensemble.sample_rate
1365
    #     if current_sample_rate != sample_rate_to_set:
1366
    #         self._mw.gen_sample_freq_DSpinBox.setValue(sample_rate_to_set/1e6)
1367
    #         self.generator_sample_rate_changed()
1368
    #         self.log.info('Current generator sample rate did not match the '
1369
    #                 'sample rate of the Pulse_Block_Ensemble to load. '
1370
    #                 'Changed the sample rate to {0}Hz.'
1371
    #                 ''.format(sample_rate_to_set))
1372
    #
1373
    #     # set the laser channel to the one defined in the loaded ensemble
1374
    #     current_laser_channel = self._seq_gen_logic.laser_channel
1375
    #     laser_channel_to_set = ensemble.laser_channel
1376
    #     if current_laser_channel != laser_channel_to_set and laser_channel_to_set is not None:
1377
    #         index = self._mw.gen_laserchannel_ComboBox.findText(laser_channel_to_set)
1378
    #         self._mw.gen_laserchannel_ComboBox.setCurrentIndex(index)
1379
    #         self.log.info('Current generator laser channel did not match the '
1380
    #                 'laser channel of the Pulse_Block_Ensemble to load. '
1381
    #                 'Changed the laser channel to "{0}".'
1382
    #                 ''.format(laser_channel_to_set))
1383
    #
1384
    #     self.block_organizer_clear_table()  # clear the block organizer table
1385
    #     rows = len(ensemble.block_list)  # get amout of rows needed for display
1386
    #
1387
    #     # add as many rows as there are blocks in the ensemble
1388
    #     # minus 1 because a single row is already present after clear
1389
    #     self.block_organizer_add_row_after_last(rows - 1)
1390
    #
1391
    #     # This dictionary has the information which column number describes
1392
    #     # which object, it is a configuration dict between GUI and logic
1393
    #     organizer_config_dict = self._cfg_param_pb
1394
    #
1395
    #     # run through all blocks in the block_elements block_list to fill in the
1396
    #     # row informations
1397
    #     for row_index, (pulse_block, repetitions) in enumerate(ensemble.block_list):
1398
    #         column = organizer_config_dict['pulse_block']
1399
    #         self.set_element_in_organizer_table(row_index, column, pulse_block.name)
1400
    #
1401
    #         column = organizer_config_dict['repetition']
1402
    #         self.set_element_in_organizer_table(row_index, column, int(repetitions))
1403
    #
1404
    #     # set the ensemble name LineEdit to the current ensemble
1405
    #     self._mw.curr_ensemble_name_LineEdit.setText(current_ensemble_name)
1406
1407
1408
1409
1410
    # def block_organizer_add_row_before_selected(self,insert_rows=1):
1411
    #     """ Add row before selected element. """
1412
    #     self._mw.block_organizer_TableWidget.blockSignals(True)
1413
    #     selected_row = self._mw.block_organizer_TableWidget.currentRow()
1414
    #
1415
    #     # the signal passes a boolean value, which overwrites the insert_rows
1416
    #     # parameter. Check that here and use the actual default value:
1417
    #     if type(insert_rows) is bool:
1418
    #         insert_rows = 1
1419
    #
1420
    #     for rows in range(insert_rows):
1421
    #         self._mw.block_organizer_TableWidget.insertRow(selected_row)
1422
    #
1423
    #     self.initialize_cells_block_organizer(start_row=selected_row)
1424
    #     self._mw.block_organizer_TableWidget.blockSignals(False)
1425
    #     self._update_current_pulse_block_ensemble()
1426
    #
1427
    #
1428
    # def block_organizer_add_row_after_last(self, insert_rows=1):
1429
    #     """ Add row after last row in the block editor. """
1430
    #     self._mw.block_organizer_TableWidget.blockSignals(True)
1431
    #
1432
    #     # the signal of a QPushButton passes an optional boolean value to this
1433
    #     # method, which overwrites the insert_rows parameter. Check that here
1434
    #     # and use the actual default value:
1435
    #     if type(insert_rows) is bool:
1436
    #         insert_rows = 1
1437
    #
1438
    #     number_of_rows = self._mw.block_organizer_TableWidget.rowCount()
1439
    #     self._mw.block_organizer_TableWidget.setRowCount(number_of_rows+insert_rows)
1440
    #
1441
    #     self.initialize_cells_block_organizer(start_row=number_of_rows,
1442
    #                                           stop_row=number_of_rows + insert_rows)
1443
    #
1444
    #     self._mw.block_organizer_TableWidget.blockSignals(False)
1445
    #     self._update_current_pulse_block_ensemble()
1446
    #
1447
    # def block_organizer_delete_row_selected(self):
1448
    #     """ Delete row of selected element. """
1449
    #
1450
    #     # get the row number of the selected item(s). That will return the
1451
    #     # lowest selected row
1452
    #     row_to_remove = self._mw.block_organizer_TableWidget.currentRow()
1453
    #     self._mw.block_organizer_TableWidget.removeRow(row_to_remove)
1454
    #     self._update_current_pulse_block_ensemble()
1455
    #
1456
    # def block_organizer_delete_row_last(self):
1457
    #     """ Delete the last row in the block editor. """
1458
    #
1459
    #     number_of_rows = self._mw.block_organizer_TableWidget.rowCount()
1460
    #     # remember, the row index is started to count from 0 and not from 1,
1461
    #     # therefore one has to reduce the value by 1:
1462
    #     self._mw.block_organizer_TableWidget.removeRow(number_of_rows-1)
1463
    #     self._update_current_pulse_block_ensemble()
1464
1465
    # def block_organizer_clear_table(self):
1466
    #     """ Delete all rows in the block editor table. """
1467
    #
1468
    #
1469
    #     self._mw.block_organizer_TableWidget.blockSignals(True)
1470
    #     self._mw.block_organizer_TableWidget.setRowCount(1)
1471
    #     self._mw.block_organizer_TableWidget.clearContents()
1472
    #     self.initialize_cells_block_organizer(start_row=0)
1473
    #     self._mw.block_organizer_TableWidget.blockSignals(False)
1474
    #     self._update_current_pulse_block_ensemble()
1475
1476
    def delete_pulse_block_ensemble_clicked(self):
1477
        """
1478
        Actions to perform when the delete button in the block organizer is clicked
1479
        """
1480
        name = self._mw.saved_ensembles_ComboBox.currentText()
1481
        self._seq_gen_logic.delete_ensemble(name)
1482
        self.update_ensemble_list()
1483
        return
1484
1485
1486
    def generate_pulse_block_ensemble_clicked(self):
1487
        """ Generate a Pulse_Block_ensemble object."""
1488
1489
        objectname = self._mw.curr_ensemble_name_LineEdit.text()
1490
        if objectname == '':
1491
            self.log.warning('No Name for Pulse_Block_Ensemble specified. '
1492
                        'Generation aborted!')
1493
            return
1494
        rotating_frame =  self._mw.curr_ensemble_rot_frame_CheckBox.isChecked()
1495
        self.generate_pulse_block_ensemble_object(objectname, self.get_organizer_table(),
1496
                                                  rotating_frame)
1497
1498 View Code Duplication
    def set_cfg_param_pbe(self):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1499
        """ Set the parameter configuration of the Pulse_Block_Elements
1500
        according to the current table configuration and updates the dict in
1501
        the logic.
1502
        """
1503
1504
        cfg_param_pbe = OrderedDict()
1505
        for column in range(self._mw.block_editor_TableWidget.columnCount()):
1506
            text = self._mw.block_editor_TableWidget.horizontalHeaderItem(column).text()
1507
            split_text = text.split()
1508
            if 'DCh' in split_text[0]:
1509
                cfg_param_pbe['digital_' + split_text[0][3]] = column
1510
            elif 'ACh' in split_text[0]:
1511
                cfg_param_pbe[split_text[1] + '_' + split_text[0][3]] = column
1512
            else:
1513
                cfg_param_pbe[split_text[0]] = column
1514
1515
        self._cfg_param_pbe = cfg_param_pbe
1516
1517
    # def set_cfg_param_pb(self):
1518
    #     """ Set the parameter configuration of the Pulse_Block according to the
1519
    #     current table configuration and updates the dict in the logic.
1520
    #     """
1521
    #
1522
    #     cfg_param_pb = OrderedDict()
1523
    #
1524
    #     for column in range(self._mw.block_organizer_TableWidget.columnCount()):
1525
    #         text = self._mw.block_organizer_TableWidget.horizontalHeaderItem(column).text()
1526
    #         # split_text = text.split()
1527
    #         if 'Pulse Block' in text:
1528
    #             cfg_param_pb['pulse_block'] = column
1529
    #         elif 'length' in text:
1530
    #             cfg_param_pb['length'] = column
1531
    #         elif 'repetition' in text:
1532
    #             cfg_param_pb['repetition'] = column
1533
    #         else:
1534
    #             print('text:',text)
1535
    #             raise NotImplementedError
1536
    #     self._cfg_param_pb = cfg_param_pb
1537
    #
1538
    #
1539
    #
1540
    #
1541
    # def initialize_cells_block_organizer(self, start_row, stop_row=None,
1542
    #                                      start_col=None, stop_col=None):
1543
    #     """ Initialize the desired cells in the block organizer table.
1544
    #
1545
    #     @param start_row: int, index of the row, where the initialization
1546
    #                       should start
1547
    #     @param stop_row: int, optional, index of the row, where the
1548
    #                      initalization should end.
1549
    #     @param start_col: int, optional, index of the column where the
1550
    #                       initialization should start
1551
    #     @param stop_col: int, optional, index of the column, where the
1552
    #                      initalization should end.
1553
    #
1554
    #     With this function it is possible to reinitialize specific elements or
1555
    #     part of a row or even the whole row. If start_row is set to 0 the whole
1556
    #     row is going to be initialzed to the default value.
1557
    #     """
1558
    #
1559
    #     if stop_row is None:
1560
    #         stop_row = start_row +1
1561
    #
1562
    #     if start_col is None:
1563
    #         start_col = 0
1564
    #
1565
    #     if stop_col is None:
1566
    #         stop_col = self._mw.block_organizer_TableWidget.columnCount()
1567
    #
1568
    #     for col_num in range(start_col, stop_col):
1569
    #
1570
    #         for row_num in range(start_row,stop_row):
1571
    #             # get the model, here are the data stored:
1572
    #             model = self._mw.block_organizer_TableWidget.model()
1573
    #             # get the corresponding index of the current element:
1574
    #             index = model.index(row_num, col_num)
1575
    #             # get the initial values of the delegate class which was
1576
    #             # uses for this column:
1577
    #             ini_values = self._mw.block_organizer_TableWidget.itemDelegateForColumn(col_num).get_initial_value()
1578
    #             # set initial values:
1579
    #             model.setData(index, ini_values[0], ini_values[1])
1580
1581
    def _add_config_for_predefined_methods(self, parent, name):
1582
        """ Create the Config Elements for altering the Predefined Methods
1583
            display.
1584
        """
1585
        # one has to know that all the checkbox control elements are attached
1586
        # to the widget verticalLayout, accessible via self._pm_cfg.verticalLayout
1587
1588
        checkbox = self._create_QCheckBox(parent, default_val=True)
1589
1590
        checkbox.setText(name)
1591
        setattr(self._pm_cfg, name +'_CheckBox', checkbox)
1592
        self._pm_cfg.verticalLayout.addWidget(checkbox)
1593
1594
    def _get_ref_checkbox_predefined_methods_config(self, name):
1595
        """ Retrieve the reference to the CheckBox with the name of the predefined method
1596
1597
        @param str name: the name of the predefined method
1598
1599
        @return QtGui.QCheckBox: reference to the CheckBox widget.
1600
        """
1601
1602
        return getattr(self._pm_cfg, name+'_CheckBox')
1603
1604 View Code Duplication
    def _create_control_for_predefined_methods(self):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1605
        """ Create the Control Elements in the Predefined Windows, depending
1606
            on the methods of the logic.
1607
1608
        The following procedure was chosen:
1609
            1. At first the method is inspected and all the parameters are
1610
              investigated. Depending on the type of the default value of the
1611
              keyword, a ControlBox (CheckBox, DoubleSpinBox, ...) is created.laserchannel_ComboBox
1612
                _<method_name>_generate()
1613
                which are connected to the generate button and passes all the
1614
                parameters to the method in the logic.
1615
            3. An additional method is created as
1616
                _<method_name>_generate_upload()
1617
                which generates and uploads the current values to the device.
1618
        """
1619
        method_list = self._seq_gen_logic.predefined_method_list
1620
1621
        for method in method_list:
1622
            inspected = inspect.signature(method)
1623
1624
1625
            gridLayout = QtWidgets.QGridLayout()
1626
            groupBox = QtWidgets.QGroupBox(self._pm)
1627
1628
            obj_list = []
1629
1630
            # go through the parameter list and check the type of the default
1631
            # parameter
1632
            for index, param_name in enumerate(inspected.parameters):
1633
1634
                label_obj = self._create_QLabel(groupBox, param_name)
1635
1636
                default_value = inspected.parameters[param_name].default
1637
1638
                if default_value is inspect._empty:
1639
                    self.log.error('The method "{0}" in the logic has an '
1640
                                'argument "{1}" without a default value!\n'
1641
                                'Assign a default value to that, otherwise a '
1642
                                'type estimation is not possible!\n'
1643
                                'Creation of the viewbox '
1644
                                'aborted.'.format(method.__name__, param_name))
1645
                    return
1646
1647
                if type(default_value) is bool:
1648
                    view_obj = self._create_QCheckBox(groupBox, default_value)
1649
                elif type(default_value) is float:
1650
                    view_obj = self._create_QDoubleSpinBox(groupBox, default_value)
1651
                elif type(default_value) is int:
1652
                    view_obj = self._create_QSpinBox(groupBox, default_value)
1653
                elif type(default_value) is str:
1654
                    view_obj = self._create_QLineEdit(groupBox, default_value)
1655
                else:
1656
                    self.log.error('The method "{0}" in the logic has an '
1657
                            'argument "{1}" with is not of the valid types'
1658
                            'str, float int or bool!\n'
1659
                            'Choose one of those default values! Creation '
1660
                            'of the viewbox aborted.'.format(
1661
                                method.__name__, param_name))
1662
1663
                obj_list.append(view_obj)
1664
                gridLayout.addWidget(label_obj, 0, index, 1, 1)
1665
                gridLayout.addWidget(view_obj, 1, index, 1, 1)
1666
1667
            gen_button = self._create_QPushButton(groupBox, 'Generate')
1668
            # Create with the function builder a method, which will be
1669
            # connected to the click event of the pushbutton generate:
1670
            func_name = '_'+ method.__name__ + '_generate'
1671
            setattr(self, func_name, self._function_builder_generate(func_name, obj_list, method) )
1672
            gen_func = getattr(self, func_name)
1673
            gen_button.clicked.connect(gen_func)
1674
1675
1676
            gen_upload_button = self._create_QPushButton(groupBox, 'Gen. & Upload')
1677
            # Create with the function builder a method, which will be
1678
            # connected to the click event of the pushbutton generate & upload:
1679
            func_name = '_'+ method.__name__ + '_generate_upload'
1680
            setattr(self, func_name, self._function_builder_generate_upload(func_name, gen_func) )
1681
            gen_upload_func = getattr(self, func_name)
1682
            gen_upload_button.clicked.connect(gen_upload_func)
1683
1684
            # position the buttons in the groupbox:
1685
            pos = len(inspected.parameters)
1686
            gridLayout.addWidget(gen_button, 0, pos, 1, 1)
1687
            gridLayout.addWidget(gen_upload_button, 1, pos, 1, 1)
1688
1689
            horizontalLayout = QtWidgets.QHBoxLayout(groupBox)
1690
1691
            horizontalLayout.addLayout(gridLayout)
1692
1693
            groupBox.setTitle(method.__name__.replace('_',' '))
1694
1695
            # attach the GroupBox widget to the predefined methods widget.
1696
            setattr(self._pm, method.__name__+'_GroupBox', groupBox)
1697
1698
            # Since a Scroll Widget is used, you need you pass the
1699
            # scrollAreaWidgetContents as the parent widget.
1700
            self._add_config_for_predefined_methods(self._pm_cfg.scrollAreaWidgetContents, method.__name__)
1701
1702
            # add the name of the predefined method to a local list to keep
1703
            # track of the method:
1704
            self._predefined_methods_list.append(method.__name__)
1705
1706
            self._pm.verticalLayout.addWidget(groupBox)
1707
1708
    def _get_ref_groupbox_predefined_methods(self, name):
1709
        """ Retrieve the reference to the GroupBox with the name of the predefined method
1710
1711
        @param str name: the name of the predefined method
1712
1713
        @return QtGui.QGroupBox: reference to the groupbox widget containing all
1714
                                 elements for the predefined methods.
1715
        """
1716
1717
        return getattr(self._pm, name+'_GroupBox')
1718
1719
1720
    def _create_QLabel(self, parent, label_name):
1721
        """ Helper method for _create_control_for_predefined_methods.
1722
1723
        @param parent: The parent QWidget, which should own that object
1724
        @param str label_name: the display name for the QLabel Widget.
1725
1726
        @return QtGui.QLabel: a predefined label for the GUI.
1727
        """
1728
1729
        label = QtWidgets.QLabel(parent)
1730
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
1731
        sizePolicy.setHorizontalStretch(0)
1732
        sizePolicy.setVerticalStretch(0)
1733
        sizePolicy.setHeightForWidth(label.sizePolicy().hasHeightForWidth())
1734
        label.setSizePolicy(sizePolicy)
1735 View Code Duplication
        label.setText(label_name)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1736
        return label
1737
1738
    def _create_QDoubleSpinBox(self, parent, default_val=0.0):
1739
        """ Helper method for _create_control_for_predefined_methods.
1740
1741
        @param parent: The parent QWidget, which should own that object
1742
        @param float default_val: a default value for the QDoubleSpinBox.
1743
1744
        @return QtGui.QDoubleSpinBox: a predefined QDoubleSpinBox for the GUI.
1745
        """
1746
1747
        doublespinbox = QtWidgets.QDoubleSpinBox(parent)
1748
        doublespinbox.setMaximum(np.inf)
1749
        doublespinbox.setMinimum(-np.inf)
1750
1751
        # set a size for vertivcal an horizontal dimensions
1752
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
1753
        sizePolicy.setHorizontalStretch(0)
1754
        sizePolicy.setVerticalStretch(0)
1755
        sizePolicy.setHeightForWidth(doublespinbox.sizePolicy().hasHeightForWidth())
1756
1757
        doublespinbox.setMinimumSize(QtCore.QSize(80, 0))
1758
        doublespinbox.setValue(default_val)
1759
        return doublespinbox
1760
1761
    def _create_QSpinBox(self, parent, default_val=0):
1762
        """ Helper method for _create_control_for_predefined_methods.
1763
1764
        @param parent: The parent QWidget, which should own that object
1765
        @param int default_val: a default value for the QSpinBox.
1766
1767
        @return QtGui.QSpinBox: a predefined QSpinBox for the GUI.
1768
        """
1769
1770
        spinBox = QtWidgets.QSpinBox(parent)
1771
        spinBox.setMaximum(2**31 -1)
1772
        spinBox.setMinimum(-2**31 +1)
1773
        spinBox.setValue(default_val)
1774
        return spinBox
1775
1776
    def _create_QCheckBox(self, parent, default_val=False):
1777
        """ Helper method for _create_control_for_predefined_methods.
1778
1779
        @param parent: The parent QWidget, which should own that object
1780
        @param bool default_val: a default value for the QCheckBox.
1781
1782
        @return QtGui.QCheckBox: a predefined QCheckBox for the GUI.
1783
        """
1784
1785
        checkBox = QtWidgets.QCheckBox(parent)
1786
        checkBox.setChecked(default_val)
1787
        return checkBox
1788
1789
    def _create_QLineEdit(self, parent, default_val=''):
1790
        """ Helper method for _create_control_for_predefined_methods.
1791
1792
        @param parent: The parent QWidget, which should own that object
1793
        @param str default_val: a default value for the QLineEdit.
1794
1795
        @return QtGui.QLineEdit: a predefined QLineEdit for the GUI.
1796
        """
1797
1798
        lineedit = QtWidgets.QLineEdit(parent)
1799
        lineedit.setText(default_val)
1800
1801
        # set a size for vertivcal an horizontal dimensions
1802 View Code Duplication
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1803
        sizePolicy.setHorizontalStretch(0)
1804
        sizePolicy.setVerticalStretch(0)
1805
        sizePolicy.setHeightForWidth(lineedit.sizePolicy().hasHeightForWidth())
1806
1807
        lineedit.setMinimumSize(QtCore.QSize(80, 0))
1808
1809
        lineedit.setSizePolicy(sizePolicy)
1810
        return lineedit
1811
1812
    def _create_QPushButton(self, parent, text='Generate'):
1813
        """ Helper method for _create_control_for_predefined_methods.
1814
1815
        @param parent: The parent QWidget, which should own that object
1816
        @param str text: a display text for the QPushButton.
1817
1818
        @return QtGui.QPushButton: a predefined QPushButton for the GUI.
1819
        """
1820
1821 View Code Duplication
        pushbutton = QtWidgets.QPushButton(parent)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1822
        pushbutton.setText(text)
1823
        return pushbutton
1824
1825
    def _function_builder_generate(self, func_name, obj_list, ref_logic_gen ):
1826
        """ Create a function/method which is called by the generate button.
1827
1828
        @param str func_name: name of the function, which will be append to self
1829
        @param list obj_list: list of objects, which where the value will be
1830
                              retrieved
1831
        @param method ref_logic_gen: reference to method in logic
1832
1833
        @return: a function, which can be called with func_name
1834
        """
1835
1836
        def func_dummy_name():
1837
            object_list = obj_list
1838
1839
            ensemble_name = ''
1840
            parameters = [None]*len(object_list)
1841
            for index, obj in enumerate(object_list):
1842
                if hasattr(obj,'isChecked'):
1843
                    parameters[index] = obj.isChecked()
1844
                elif hasattr(obj,'value'):
1845
                    parameters[index] = obj.value()
1846
                elif hasattr(obj,'text'):
1847
1848
                    parameters[index] = obj.text()
1849
                    ensemble_name = obj.text()
1850
                else:
1851
                    self.log.error('Not possible to get the value from the '
1852
                            'viewbox, since it does not have one of the'
1853
                            'possible access methods!')
1854
1855
            # the * operator unpacks the list
1856
            ref_logic_gen(*parameters)
1857
            return ensemble_name
1858
1859
        # assign now a new name to that function, this name will be used to
1860
        # bound the function as attribute to the main object.
1861
        func_dummy_name.__name__ = func_name
1862
        return func_dummy_name
1863
1864
    def _function_builder_generate_upload(self, func_name, ref_gen_func):
1865
        """ Create a function/method which is called by the generate and upload button
1866
1867
        @param str func_name: name of the function, which will be append to self
1868
        @param str ensemble_name: name of the pulse_block_ensemble which will
1869
                                  be uploaded.
1870
        @param ref_gen_func: reference to the generate function, which calls
1871
                             the logic method
1872
1873
        @return: a function, which can be called with func_name
1874
        """
1875
1876
        def func_dummy_name():
1877
            ensemble_name = ref_gen_func()
1878
1879
            index = self._mw.upload_ensemble_ComboBox.findText(ensemble_name, QtCore.Qt.MatchFixedString)
1880
            self._mw.upload_ensemble_ComboBox.setCurrentIndex(index)
1881
            self.sample_ensemble_clicked()
1882
            self.upload_to_device_clicked()
1883
1884
        # assign now a new name to that function, this name will be used to
1885
        # bound the function as attribute to the main object.
1886
        func_dummy_name.__name__ = func_name
1887
        return func_dummy_name
1888
1889
1890
1891
    ###########################################################################
1892
    ###        Methods related to Settings for the 'Analysis' Tab:          ###
1893
    ###########################################################################
1894
1895
    #FIXME: Implement the setting for 'Analysis' tab.
1896 View Code Duplication
    def _activate_analysis_settings_ui(self, e):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1897
        """ Initialize, connect and configure the Settings of 'Analysis' Tab.
1898
1899
        @param object e: Fysom.event object from Fysom class. A more detailed
1900
                         explanation can be found in the method initUI.
1901
        """
1902
        self._as = AnalysisSettingDialog()
1903
        self._as.accepted.connect(self.update_analysis_settings)
1904
        self._as.rejected.connect(self.keep_former_analysis_settings)
1905
        self._as.buttonBox.button(QtWidgets.QDialogButtonBox.Apply).clicked.connect(self.update_analysis_settings)
1906
1907
        if 'ana_param_x_axis_name_LineEdit' in self._statusVariables:
1908
            self._as.ana_param_x_axis_name_LineEdit.setText(self._statusVariables['ana_param_x_axis_name_LineEdit'])
1909
        if 'ana_param_x_axis_unit_LineEdit' in self._statusVariables:
1910
            self._as.ana_param_x_axis_unit_LineEdit.setText(self._statusVariables['ana_param_x_axis_unit_LineEdit'])
1911
        if 'ana_param_y_axis_name_LineEdit' in self._statusVariables:
1912
            self._as.ana_param_y_axis_name_LineEdit.setText(self._statusVariables['ana_param_y_axis_name_LineEdit'])
1913
        if 'ana_param_y_axis_unit_LineEdit' in self._statusVariables:
1914
            self._as.ana_param_y_axis_unit_LineEdit.setText(self._statusVariables['ana_param_y_axis_unit_LineEdit'])
1915
        if 'ana_param_second_plot_x_axis_name_LineEdit' in self._statusVariables:
1916
            self._as.ana_param_second_plot_x_axis_name_LineEdit.setText(self._statusVariables['ana_param_second_plot_x_axis_name_LineEdit'])
1917
        if 'ana_param_second_plot_x_axis_unit_LineEdit' in self._statusVariables:
1918
            self._as.ana_param_second_plot_x_axis_unit_LineEdit.setText(self._statusVariables['ana_param_second_plot_x_axis_unit_LineEdit'])
1919
        if 'ana_param_second_plot_y_axis_name_LineEdit' in self._statusVariables:
1920
            self._as.ana_param_second_plot_y_axis_name_LineEdit.setText(self._statusVariables['ana_param_second_plot_y_axis_name_LineEdit'])
1921
        if 'ana_param_second_plot_y_axis_unit_LineEdit' in self._statusVariables:
1922
            self._as.ana_param_second_plot_y_axis_unit_LineEdit.setText(self._statusVariables['ana_param_second_plot_y_axis_unit_LineEdit'])
1923
1924
        self._as.ana_param_lasertrigger_delay_ScienDSpinBox.setValue(self._pulsed_meas_logic.laser_trigger_delay_s)
1925
        # configure a bit the laser trigger delay spinbox:
1926
        #self._as.ana_param_lasertrigger_delay_ScienDSpinBox.setSingleStep(10)  # in ns
1927
        self.update_analysis_settings()
1928
1929
1930 View Code Duplication
    def _deactivate_analysis_settings_ui(self, e):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1931
        """ Disconnects the configuration of the Settings for 'Analysis' Tab.
1932
1933
        @param object e: Fysom.event object from Fysom class. A more detailed
1934
                         explanation can be found in the method initUI.
1935
        """
1936
        self._statusVariables['ana_param_x_axis_name_LineEdit'] = self._as.ana_param_x_axis_name_LineEdit.text()
1937
        self._statusVariables['ana_param_x_axis_unit_LineEdit'] = self._as.ana_param_x_axis_unit_LineEdit.text()
1938
        self._statusVariables['ana_param_y_axis_name_LineEdit'] = self._as.ana_param_y_axis_name_LineEdit.text()
1939
        self._statusVariables['ana_param_y_axis_unit_LineEdit'] = self._as.ana_param_y_axis_unit_LineEdit.text()
1940
        self._statusVariables['ana_param_second_plot_x_axis_name_LineEdit'] = self._as.ana_param_second_plot_x_axis_name_LineEdit.text()
1941
        self._statusVariables['ana_param_second_plot_x_axis_unit_LineEdit'] = self._as.ana_param_second_plot_x_axis_unit_LineEdit.text()
1942
        self._statusVariables['ana_param_second_plot_y_axis_name_LineEdit'] = self._as.ana_param_second_plot_y_axis_name_LineEdit.text()
1943
        self._statusVariables['ana_param_second_plot_y_axis_unit_LineEdit'] = self._as.ana_param_second_plot_y_axis_unit_LineEdit.text()
1944
1945
1946
    def update_analysis_settings(self):
1947
        """ Apply the new settings """
1948
        self._mw.pulse_analysis_PlotWidget.setLabel(axis='bottom',
1949
            text=self._as.ana_param_x_axis_name_LineEdit.text(),
1950
            units=self._as.ana_param_x_axis_unit_LineEdit.text())
1951
        self._mw.pulse_analysis_PlotWidget.setLabel(axis='left',
1952
            text=self._as.ana_param_y_axis_name_LineEdit.text(),
1953
            units=self._as.ana_param_y_axis_unit_LineEdit.text())
1954
        self._mw.pulse_analysis_second_PlotWidget.setLabel(axis='bottom',
1955
            text=self._as.ana_param_second_plot_x_axis_name_LineEdit.text(),
1956
            units=self._as.ana_param_second_plot_x_axis_unit_LineEdit.text())
1957
        self._mw.pulse_analysis_second_PlotWidget.setLabel(axis='left',
1958
            text=self._as.ana_param_second_plot_y_axis_name_LineEdit.text(),
1959
            units=self._as.ana_param_second_plot_y_axis_unit_LineEdit.text())
1960
        lasertrig_delay = self._as.ana_param_lasertrigger_delay_ScienDSpinBox.value()
1961
        self._pulsed_meas_logic.set_laser_trigger_delay(lasertrig_delay)
1962
        pass
1963
1964
    def keep_former_analysis_settings(self):
1965 View Code Duplication
        """ Keep the old settings """
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1966
        #FIXME: Implement the behaviour
1967
        self._as.ana_param_lasertrigger_delay_ScienDSpinBox.setValue(
1968
            self._pulsed_meas_logic.laser_trigger_delay_s)
1969
1970
    def show_analysis_settings(self):
1971
        """ Open the Analysis Settings Window. """
1972
        self._as.exec_()
1973
1974
1975
    ###########################################################################
1976
    ###     Methods related to the Tab 'Analysis' in the Pulsed Window:     ###
1977
    ###########################################################################
1978
1979
    def _activate_analysis_ui(self, e):
1980
        """ Initialize, connect and configure the 'Analysis' Tab.
1981
1982
        @param object e: Fysom.event object from Fysom class. A more detailed
1983 View Code Duplication
                         explanation can be found in the method initUI.
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1984
        """
1985
        # Configure the main pulse analysis display:
1986
        self.signal_image = pg.PlotDataItem(self._pulsed_meas_logic.signal_plot_x,
1987
                                            self._pulsed_meas_logic.signal_plot_y,
1988
                                            pen=palette.c1)
1989
        self._mw.pulse_analysis_PlotWidget.addItem(self.signal_image)
1990
        self._mw.pulse_analysis_PlotWidget.showGrid(x=True, y=True, alpha=0.8)
1991
        if self._pulsed_meas_logic.alternating:
1992
            self.signal_image2 = pg.PlotDataItem(self._pulsed_meas_logic.signal_plot_x,
1993
                                                 self._pulsed_meas_logic.signal_plot_y2,
1994
                                                 pen=palette.c3)
1995
            self._mw.pulse_analysis_PlotWidget.addItem(self.signal_image2)
1996
1997
        # Configure the fit of the data in the main pulse analysis display:
1998
        self.fit_image = pg.PlotDataItem(pen=palette.c2)
1999
        self._mw.pulse_analysis_PlotWidget.addItem(self.fit_image)
2000
2001
        # Configure the errorbars of the data in the main pulse analysis display:
2002
        self.signal_image_error_bars=pg.ErrorBarItem(x=self._pulsed_meas_logic.signal_plot_x,
2003
                                                     y=self._pulsed_meas_logic.signal_plot_y,
2004
                                                     top=self._pulsed_meas_logic.measuring_error_plot_y,
2005
                                                     bottom=self._pulsed_meas_logic.measuring_error_plot_y,
2006
                                                     pen=palette.c1)
2007
2008
        # Configure the second pulse analysis display:
2009
        self.second_plot_image = pg.PlotDataItem(self._pulsed_meas_logic.signal_plot_x,
2010
                                                 self._pulsed_meas_logic.signal_plot_y,
2011
                                                 pen=palette.c1)
2012
        self._mw.pulse_analysis_second_PlotWidget.addItem(self.second_plot_image)
2013
        self._mw.pulse_analysis_second_PlotWidget.showGrid(x=True, y=True, alpha=0.8)
2014
2015
        #FIXME: Is currently needed for the errorbars, but there has to be a better solution
2016
        self.errorbars_present = False
2017
2018
        # that must be one out of the dict units.get_unit_prefix_dict()
2019
        self._freq_prefix = 'M'
2020
        self._time_prefix = 'n'
2021
2022
        # apply hardware constraints
2023
        self._analysis_apply_hardware_constraints()
2024
        # Initialize External Control GroupBox from logic and saved status variables
2025
        self._init_external_control()
2026
        # Initialize Analysis Parameter GroupBox from logic and saved status variables
2027
        self._init_analysis_parameter()
2028
        # Initialize Fit Parameter GroupBox from logic and saved status variables
2029
        self._init_fit_parameter()
2030
2031
        # ---------------------------------------------------------------------
2032
        #                         Connect signals
2033
        # ---------------------------------------------------------------------
2034
        self._mw.action_run_stop.triggered.connect(self.run_stop_clicked)
2035
        self._mw.action_continue_pause.triggered.connect(self.continue_pause_clicked)
2036
        self._mw.action_pull_data.triggered.connect(self.pull_data_clicked)
2037
        self._mw.action_save.triggered.connect(self.save_clicked)
2038
2039
        self._pulsed_meas_logic.signal_time_updated.connect(self.refresh_elapsed_time)
2040
        self._pulsed_meas_logic.sigPulseAnalysisUpdated.connect(self.refresh_signal_plot)
2041
        self._pulsed_meas_logic.sigMeasuringErrorUpdated.connect(self.refresh_measuring_error_plot)
2042
2043
        self._mw.action_Settings_Analysis.triggered.connect(self.show_analysis_settings)
2044
2045
        # Connect the CheckBoxes
2046
        # anaylsis tab
2047
        self._mw.ext_control_use_mw_CheckBox.stateChanged.connect(self.toggle_external_mw_source_editor)
2048
        self._mw.ana_param_x_axis_defined_CheckBox.stateChanged.connect(self.toggle_laser_xaxis_editor)
2049
        self._mw.ana_param_laserpulse_defined_CheckBox.stateChanged.connect(self.toggle_laser_xaxis_editor)
2050
        self._mw.ana_param_alternating_CheckBox.stateChanged.connect(self.analysis_alternating_changed)
2051
2052
        # Connect InputWidgets to events
2053
        self._mw.ana_param_num_laser_pulse_SpinBox.editingFinished.connect(self.num_of_lasers_changed)
2054
        self._mw.ana_param_laser_length_SpinBox.editingFinished.connect(self.laser_length_changed)
2055
        self._mw.time_param_ana_periode_DoubleSpinBox.editingFinished.connect(self.analysis_timing_changed)
2056
        self._mw.ana_param_fc_bins_ComboBox.currentIndexChanged.connect(self.analysis_fc_binning_changed)
2057
        self._mw.second_plot_ComboBox.currentIndexChanged.connect(self.change_second_plot)
2058
2059
        # connect the fit stuff:
2060
        self._mw.fit_param_PushButton.clicked.connect(self.fit_clicked)
2061
        self._pulsed_meas_logic.sigFitUpdated.connect(self.refresh_signal_plot_fit)
2062
2063
        self._mw.ext_control_mw_freq_DoubleSpinBox.editingFinished.connect(self.ext_mw_params_changed)
2064
        self._mw.ext_control_mw_power_DoubleSpinBox.editingFinished.connect(self.ext_mw_params_changed)
2065
2066
        self._mw.pulser_sample_freq_DSpinBox.editingFinished.connect(self.pulser_sample_rate_changed)
2067
        self._mw.pulser_activation_config_ComboBox.currentIndexChanged.connect(self.pulser_activation_config_changed)
2068
2069
        self._mw.ana_param_x_axis_start_ScienDSpinBox.editingFinished.connect(self.analysis_xaxis_changed)
2070
        self._mw.ana_param_x_axis_inc_ScienDSpinBox.editingFinished.connect(self.analysis_xaxis_changed)
2071
2072
    def _deactivate_analysis_ui(self, e):
2073
        """ Disconnects the configuration for 'Analysis' Tab.
2074
2075
       @param object e: Fysom.event object from Fysom class. A more detailed
2076
                         explanation can be found in the method initUI.
2077
        """
2078
        self.run_stop_clicked(False)
2079
2080
        self._statusVariables['ana_param_x_axis_defined_CheckBox'] = self._mw.ana_param_x_axis_defined_CheckBox.isChecked()
2081
        self._statusVariables['ana_param_laserpulse_defined_CheckBox'] = self._mw.ana_param_laserpulse_defined_CheckBox.isChecked()
2082
        self._statusVariables['ana_param_ignore_first_CheckBox'] = self._mw.ana_param_ignore_first_CheckBox.isChecked()
2083
        self._statusVariables['ana_param_ignore_last_CheckBox'] = self._mw.ana_param_ignore_last_CheckBox.isChecked()
2084
        self._statusVariables['ana_param_errorbars_CheckBox'] = self._mw.ana_param_errorbars_CheckBox.isChecked()
2085
        self._statusVariables['second_plot_ComboBox_text'] = self._mw.second_plot_ComboBox.currentText()
2086
2087
2088
        # disconnect signals
2089
        # self._pulsed_meas_logic.sigPulseAnalysisUpdated.disconnect()
2090
        # self._mw.ana_param_num_laser_pulse_SpinBox.editingFinished.disconnect()
2091
2092
    def _analysis_apply_hardware_constraints(self):
2093 View Code Duplication
        """
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
2094
        Retrieve the constraints from pulser and fast counter hardware and apply these constraints
2095
        to the analysis tab GUI elements.
2096
        """
2097
        pulser_constr = self._pulsed_meas_logic.get_pulser_constraints()
2098
        sample_min = pulser_constr['sample_rate']['min'] / 1e6
2099
        sample_max = pulser_constr['sample_rate']['max'] / 1e6
2100
        sample_step = pulser_constr['sample_rate']['step'] / 1e6
2101
2102
        self._mw.pulser_sample_freq_DSpinBox.setMinimum(sample_min)
2103
        self._mw.pulser_sample_freq_DSpinBox.setMaximum(sample_max)
2104
        self._mw.pulser_sample_freq_DSpinBox.setSingleStep(sample_step)
2105
        self._mw.pulser_sample_freq_DSpinBox.setDecimals((np.log10(sample_step) * -1))
2106
2107
    def _init_analysis_parameter(self):
2108
        """
2109
        This method initializes the input parameters in the Analysis Parameter GroupBox
2110
        """
2111
        # Get the possible binwidth setting from the hardware constraints in order to keep the full
2112
        # precision (which is not needed in the display) a reference variable self._binwidth_list
2113
        # will be created where the values are stored with the absolute given presicion:
2114
        self._binwidth_list = self._pulsed_meas_logic.get_fastcounter_constraints()[
2115
            'hardware_binwidth_list']
2116
        binwidth_str_list = []
2117
        for entry in self._binwidth_list:
2118
            binwidth_str_list.append(str(round(entry, 12)))
2119
        self._mw.ana_param_fc_bins_ComboBox.addItems(binwidth_str_list)
2120
        index = self._binwidth_list.index(self._pulsed_meas_logic.fast_counter_binwidth)
2121
        self._mw.ana_param_fc_bins_ComboBox.setCurrentIndex(index)
2122
2123
        # define num laserpulses checkbox
2124
        if 'ana_param_laserpulse_defined_CheckBox' in self._statusVariables:
2125
            self._mw.ana_param_laserpulse_defined_CheckBox.setChecked(
2126
                self._statusVariables['ana_param_laserpulse_defined_CheckBox'])
2127
        else:
2128
            self._mw.ana_param_laserpulse_defined_CheckBox.setChecked(True)
2129
2130
        # number of laser pulses
2131
        self._mw.ana_param_num_laser_pulse_SpinBox.setValue(
2132
            self._pulsed_meas_logic.number_of_lasers)
2133
2134
        # Laser pulse length
2135
        self._mw.ana_param_laser_length_SpinBox.setValue(self._pulsed_meas_logic.laser_length_s*1e9)
2136
2137
        # ignore and alternating checkboxes
2138
        self._mw.ana_param_alternating_CheckBox.setChecked(self._pulsed_meas_logic.alternating)
2139
2140
        if 'ana_param_ignore_first_CheckBox' in self._statusVariables:
2141
            self._mw.ana_param_ignore_first_CheckBox.setChecked(
2142
                self._statusVariables['ana_param_ignore_first_CheckBox'])
2143
        else:
2144
            self._mw.ana_param_ignore_first_CheckBox.setChecked(False)
2145
        if 'ana_param_ignore_last_CheckBox' in self._statusVariables:
2146
            self._mw.ana_param_ignore_last_CheckBox.setChecked(
2147
                self._statusVariables['ana_param_ignore_last_CheckBox'])
2148
        else:
2149
            self._mw.ana_param_ignore_last_CheckBox.setChecked(False)
2150
2151
        # define own x-axis checkbox
2152
        if 'ana_param_x_axis_defined_CheckBox' in self._statusVariables:
2153
            self._mw.ana_param_x_axis_defined_CheckBox.setChecked(
2154
                self._statusVariables['ana_param_x_axis_defined_CheckBox'])
2155
        else:
2156
            self._mw.ana_param_x_axis_defined_CheckBox.setChecked(True)
2157
2158
        # measurement ticks
2159
        ticks_list = self._pulsed_meas_logic.measurement_ticks_list
2160
        if ticks_list is not None and len(ticks_list) > 1:
2161
            xaxis_start = ticks_list[0]
2162
            xaxis_incr = ticks_list[1] - ticks_list[0]
2163
        else:
2164
            xaxis_start = 1e-9
2165
            xaxis_incr = 1e-9
2166
        self._mw.ana_param_x_axis_start_ScienDSpinBox.setValue(xaxis_start)
2167
        self._mw.ana_param_x_axis_inc_ScienDSpinBox.setValue(xaxis_incr)
2168
2169
        # Show second plot ComboBox
2170
        second_plot_list = ['None', 'unchanged data', 'FFT', 'Log(x)', 'Log(y)', 'Log(x)&Log(y)']
2171
        self._mw.second_plot_ComboBox.addItems(second_plot_list)
2172
        if 'second_plot_ComboBox_text' in self._statusVariables:
2173
            index = second_plot_list.index(self._statusVariables['second_plot_ComboBox_text'])
2174
            self._mw.second_plot_ComboBox.setCurrentIndex(index)
2175
2176
        # Error bars CheckBox
2177
        if 'ana_param_errorbars_CheckBox' in self._statusVariables:
2178
            self._mw.ana_param_errorbars_CheckBox.setChecked(
2179
                self._statusVariables['ana_param_errorbars_CheckBox'])
2180
        else:
2181
            self._mw.ana_param_errorbars_CheckBox.setChecked(False)
2182
2183
        # Analysis period
2184
        self._mw.time_param_ana_periode_DoubleSpinBox.setValue(
2185
            self._pulsed_meas_logic.timer_interval)
2186
2187
        # Let the initial values change GUI elements accordingly
2188
        self.toggle_laser_xaxis_editor()
2189
2190
        self.change_second_plot()
2191
2192
        self._mw.laserpulses_ComboBox.clear()
2193
        self._mw.laserpulses_ComboBox.addItem('sum')
2194
        new_num_of_lasers = self._mw.ana_param_num_laser_pulse_SpinBox.value()
2195
        for ii in range(new_num_of_lasers):
2196
            self._mw.laserpulses_ComboBox.addItem(str(1 + ii))
2197
2198
    def _init_fit_parameter(self):
2199
        """
2200
        This method initializes the input parameters in the Fit Parameter GroupBox
2201
        """
2202
        # Fit ComboBox
2203
        fit_functions = self._pulsed_meas_logic.get_fit_functions()
2204
        self._mw.fit_param_fit_func_ComboBox.addItems(fit_functions)
2205
2206
    def _init_external_control(self):
2207
        """
2208
        This method initializes the input parameters in the External Control GroupBox
2209
        """
2210
        # external MW CheckBox
2211
        self._mw.ext_control_use_mw_CheckBox.setChecked(self._pulsed_meas_logic.use_ext_microwave)
2212
        # MW freq
2213
        self._mw.ext_control_mw_freq_DoubleSpinBox.setValue(self._pulsed_meas_logic.microwave_freq)
2214
        # MW power
2215
        self._mw.ext_control_mw_power_DoubleSpinBox.setValue(
2216
            self._pulsed_meas_logic.microwave_power)
2217
        self.toggle_external_mw_source_editor()
2218
2219
        # Channel config ComboBox
2220
        avail_configs = self._pulsed_meas_logic.get_pulser_constraints()['activation_config']
2221
        self._mw.pulser_activation_config_ComboBox.addItems(list(avail_configs))
2222
        config_name_to_set = self._pulsed_meas_logic.current_channel_config_name
2223
        if config_name_to_set is not None and config_name_to_set in avail_configs.keys():
2224
            index = self._mw.pulser_activation_config_ComboBox.findText(config_name_to_set)
2225
            self._mw.pulser_activation_config_ComboBox.setCurrentIndex(index)
2226
            display_str = ''
2227
            for chnl in avail_configs[config_name_to_set]:
2228
                display_str += chnl + ' | '
2229
            display_str = display_str[:-3]
2230
            self._mw.pulser_activation_config_LineEdit.setText(display_str)
2231
2232
        # Sample rate
2233
        self._mw.pulser_sample_freq_DSpinBox.setValue(self._pulsed_meas_logic.sample_rate/1e6)
2234
        self.pulser_sample_rate_changed()
2235
2236
    def run_stop_clicked(self, isChecked):
2237
        """ Manages what happens if pulsed measurement is started or stopped.
2238
2239
        @param bool enabled: start scan if that is possible
2240
        """
2241
2242
        #Firstly stop any scan that might be in progress
2243
        self._pulsed_meas_logic.stop_pulsed_measurement()
2244
2245
        if isChecked:
2246
            # get currently loaded asset for the parameters
2247
            asset_name = self._pulsed_meas_logic.loaded_asset_name
2248
            asset_obj = self._seq_gen_logic.get_saved_asset(asset_name)
2249
            if not self._mw.ana_param_laserpulse_defined_CheckBox.isChecked() or \
2250
                not self._mw.ana_param_x_axis_defined_CheckBox.isChecked():
2251
                if asset_obj is None:
2252
                    self.log.error('Error while trying to run pulsed '
2253
                            'measurement. No asset is loaded onto the '
2254
                            'pulse generator. Aborting run.')
2255
                    return
2256
            # infer number of laser pulses from the currently loaded asset if needed.
2257
            # If they have been manually set in the GUI the changes are already in the logic.
2258
            if not self._mw.ana_param_laserpulse_defined_CheckBox.isChecked():
2259
                num_laser_pulses = asset_obj.number_of_lasers
2260
                self._mw.ana_param_num_laser_pulse_SpinBox.setValue(num_laser_pulses)
2261
                self.num_of_lasers_changed()
2262
                laser_length = asset_obj.laser_length_bins/self._pulsed_meas_logic.sample_rate
2263
                self._mw.ana_param_laser_length_SpinBox.setValue(laser_length*1e9)
2264
                self.laser_length_changed()
2265
2266
            # infer x axis measurement ticks from the currently loaded asset if needed.
2267
            # If they have been manually set in the GUI the changes are already in the logic.
2268
            if not self._mw.ana_param_x_axis_defined_CheckBox.isChecked():
2269
                if asset_obj.measurement_ticks_list is not None:
2270
                    meas_ticks_list = asset_obj.measurement_ticks_list/ asset_obj.sample_rate
2271
                else:
2272
                    self.log.error('Error while trying to run pulsed '
2273
                            'measurement. No measurement ticks defined '
2274
                            'in asset. Aborting run.')
2275
                    return
2276
                self._pulsed_meas_logic.set_measurement_ticks_list(meas_ticks_list)
2277
2278
            #Todo: Should all be set by the logic itself during load of a new sequence
2279
            self._mw.time_param_expected_dur_DoubleSpinBox.setValue(self._pulsed_meas_logic.sequence_length_s*1e3) #computed expected duration in ms
2280
2281
            # Enable and disable buttons
2282
            self._mw.ext_control_use_mw_CheckBox.setEnabled(False)
2283
            self._mw.ext_control_mw_freq_DoubleSpinBox.setEnabled(False)
2284
            self._mw.ext_control_mw_power_DoubleSpinBox.setEnabled(False)
2285
            self._mw.pulser_sample_freq_DSpinBox.setEnabled(False)
2286
            self._mw.pulser_activation_config_ComboBox.setEnabled(False)
2287
            self._mw.ana_param_fc_bins_ComboBox.setEnabled(False)
2288
            self._mw.ana_param_laserpulse_defined_CheckBox.setEnabled(False)
2289
            self._mw.ana_param_num_laser_pulse_SpinBox.setEnabled(False)
2290
            self._mw.ana_param_laser_length_SpinBox.setEnabled(False)
2291
            self._mw.ana_param_ignore_first_CheckBox.setEnabled(False)
2292
            self._mw.ana_param_ignore_last_CheckBox.setEnabled(False)
2293
            self._mw.ana_param_alternating_CheckBox.setEnabled(False)
2294
            self._mw.ana_param_x_axis_defined_CheckBox.setEnabled(False)
2295
            self._mw.ana_param_x_axis_start_ScienDSpinBox.setEnabled(False)
2296
            self._mw.ana_param_x_axis_inc_ScienDSpinBox.setEnabled(False)
2297
            self._mw.action_pull_data.setEnabled(True)
2298
2299
2300
            self._pulsed_meas_logic.start_pulsed_measurement()
2301
            self._mw.action_continue_pause.setEnabled(True)
2302
2303
2304
            if not self._mw.action_continue_pause.isChecked():
2305
                self._mw.action_continue_pause.toggle()
2306
        else:
2307
            #Enables and disables buttons
2308
            self._mw.ext_control_use_mw_CheckBox.setEnabled(True)
2309
            self._mw.ext_control_mw_freq_DoubleSpinBox.setEnabled(True)
2310
            self._mw.ext_control_mw_power_DoubleSpinBox.setEnabled(True)
2311
            self._mw.pulser_sample_freq_DSpinBox.setEnabled(True)
2312
            self._mw.pulser_activation_config_ComboBox.setEnabled(True)
2313
            self._mw.ana_param_fc_bins_ComboBox.setEnabled(True)
2314
            self._mw.ana_param_laserpulse_defined_CheckBox.setEnabled(True)
2315
            self._mw.ana_param_num_laser_pulse_SpinBox.setEnabled(True)
2316
            self._mw.ana_param_laser_length_SpinBox.setEnabled(True)
2317
            self._mw.ana_param_ignore_first_CheckBox.setEnabled(True)
2318
            self._mw.ana_param_ignore_last_CheckBox.setEnabled(True)
2319
            self._mw.ana_param_alternating_CheckBox.setEnabled(True)
2320
            self._mw.ana_param_x_axis_defined_CheckBox.setEnabled(True)
2321
            self._mw.ana_param_x_axis_start_ScienDSpinBox.setEnabled(True)
2322
            self._mw.ana_param_x_axis_inc_ScienDSpinBox.setEnabled(True)
2323
            self._mw.action_pull_data.setEnabled(False)
2324
            self._mw.action_continue_pause.setEnabled(False)
2325
2326
2327
    #ToDo: I think that is not really working yet. Yeap, true....
2328
    def continue_pause_clicked(self, isChecked):
2329
        """ Continues and pauses the measurement. """
2330
        if isChecked:
2331
            #self._mw.action_continue_pause.toggle()
2332
            self._mw.action_run_stop.setChecked(True)
2333
            #self._pulsed_meas_logic.continue_pulsed_measurement()
2334
        else:
2335
            #self._mw.action_continue_pause.toggle
2336
            #self._pulsed_meas_logic.pause_pulsed_measurement()
2337
            self._mw.action_run_stop.setChecked(False)
2338
2339
    def pull_data_clicked(self):
2340
        """ Pulls and analysis the data when the 'action_pull_data'-button is clicked. """
2341
        self._pulsed_meas_logic.manually_pull_data()
2342
        return
2343
2344
    def save_clicked(self):
2345
        """Saves the current data"""
2346
        self.save_plots()
2347
        # FIXME: Also save the data from pulsed_measurement_logic
2348
2349
    def fit_clicked(self):
2350
        """Fits the current data"""
2351
        self._mw.fit_param_results_TextBrowser.clear()
2352
        current_fit_function = self._mw.fit_param_fit_func_ComboBox.currentText()
2353
        fit_x, fit_y, fit_param, fit_result = self._pulsed_meas_logic.do_fit(current_fit_function)
2354
2355
        fit_text = units.create_formatted_output(fit_param)
2356
2357
        self.fit_image.setData(x=fit_x, y=fit_y)
2358
        self._mw.fit_param_results_TextBrowser.setPlainText(fit_text)
2359
        return
2360
2361
    def refresh_signal_plot(self):
2362
        """ Refreshes the xy-plot image """
2363
        # dealing with the error bars
2364
        if self._mw.ana_param_errorbars_CheckBox.isChecked():
2365
            beamwidth = np.inf
2366
            for i in range(len(self._pulsed_meas_logic.measurement_ticks_list) - 1):
2367
                width = self._pulsed_meas_logic.measurement_ticks_list[i + 1] - \
2368
                        self._pulsed_meas_logic.measurement_ticks_list[i]
2369
                width = width / 3
2370
                if width <= beamwidth:
2371
                    beamwidth = width
2372
            # create ErrorBarItem
2373
            self.signal_image_error_bars.setData(x=self._pulsed_meas_logic.signal_plot_x,
2374
                                                 y=self._pulsed_meas_logic.signal_plot_y,
2375
                                                 top=self._pulsed_meas_logic.measuring_error,
2376
                                                 bottom=self._pulsed_meas_logic.measuring_error,
2377
                                                 beam=beamwidth)
2378
            if not self.errorbars_present:
2379
                self._mw.pulse_analysis_PlotWidget.addItem(self.signal_image_error_bars)
2380
                self.errorbars_present = True
2381
        else:
2382
            if self.errorbars_present:
2383
                self._mw.pulse_analysis_PlotWidget.removeItem(self.signal_image_error_bars)
2384
                self.errorbars_present = False
2385
2386
2387
        # dealing with the actual signal
2388
        self.signal_image.setData(x=self._pulsed_meas_logic.signal_plot_x,
2389
                                  y=self._pulsed_meas_logic.signal_plot_y)
2390
        if self._pulsed_meas_logic.alternating:
2391
            self.signal_image2.setData(x=self._pulsed_meas_logic.signal_plot_x,
2392
                                       y=self._pulsed_meas_logic.signal_plot_y2)
2393
        self.change_second_plot()
2394
        return
2395
2396
    def refresh_signal_plot_fit(self):
2397
        """ Refreshes the xy-plot fit image """
2398
2399
        self.fit_image.setData(x=self._pulsed_meas_logic.signal_plot_x_fit,
2400
                               y=self._pulsed_meas_logic.signal_plot_y_fit)
2401
2402
2403
    def refresh_measuring_error_plot(self):
2404
        self.measuring_error_image.setData(x=self._pulsed_meas_logic.signal_plot_x,
2405
                                           y=self._pulsed_meas_logic.measuring_error*1000)
2406
2407
    def refresh_elapsed_time(self):
2408
        """ Refreshes the elapsed time and sweeps of the measurement. """
2409
        self._mw.time_param_elapsed_time_LineEdit.setText(self._pulsed_meas_logic.elapsed_time_str)
2410
2411
2412
        #FIXME: That is not a clean way! What if there is no waveform defined,
2413
        #       so that expected duration is actually zero??!! Handle that for
2414
        #       now in checking the parameter for zero, and if so, then using
2415
        #       just 1.0 instead.
2416
        if np.isclose(self._mw.time_param_expected_dur_DoubleSpinBox.value()/1e3,0):
2417
            expected_time = 1.0
2418
        else:
2419
            expected_time = self._mw.time_param_expected_dur_DoubleSpinBox.value()
2420
        self._mw.time_param_elapsed_sweep_ScienSpinBox.setValue(self._pulsed_meas_logic.elapsed_time/(expected_time/1e3))
2421
2422
    def toggle_external_mw_source_editor(self):
2423
        """ Shows or hides input widgets which are necessary if an external mw is turned on"""
2424
        if not self._mw.ext_control_use_mw_CheckBox.isChecked():
2425
2426
            self._mw.ext_control_mw_freq_Label.setVisible(False)
2427
            self._mw.ext_control_mw_freq_DoubleSpinBox.setVisible(False)
2428
            self._mw.ext_control_mw_power_Label.setVisible(False)
2429
            self._mw.ext_control_mw_power_DoubleSpinBox.setVisible(False)
2430
        else:
2431
            self._mw.ext_control_mw_freq_Label.setVisible(True)
2432
            self._mw.ext_control_mw_freq_DoubleSpinBox.setVisible(True)
2433
            self._mw.ext_control_mw_power_Label.setVisible(True)
2434
            self._mw.ext_control_mw_power_DoubleSpinBox.setVisible(True)
2435
        return
2436
2437
    def toggle_laser_xaxis_editor(self):
2438
        """ Shows or hides input widgets which are necessary if the x axis id defined or not."""
2439
2440
        if self._mw.ana_param_x_axis_defined_CheckBox.isChecked():
2441
            self._mw.ana_param_x_axis_start_Label.setVisible(True)
2442
            self._mw.ana_param_x_axis_start_ScienDSpinBox.setVisible(True)
2443
            self._mw.ana_param_x_axis_inc_Label.setVisible(True)
2444
            self._mw.ana_param_x_axis_inc_ScienDSpinBox.setVisible(True)
2445
        else:
2446
            self._mw.ana_param_x_axis_start_Label.setVisible(False)
2447
            self._mw.ana_param_x_axis_start_ScienDSpinBox.setVisible(False)
2448
            self._mw.ana_param_x_axis_inc_Label.setVisible(False)
2449
            self._mw.ana_param_x_axis_inc_ScienDSpinBox.setVisible(False)
2450
2451
        if self._mw.ana_param_laserpulse_defined_CheckBox.isChecked():
2452
            self._mw.ana_param_num_laserpulses_Label.setVisible(True)
2453
            self._mw.ana_param_num_laser_pulse_SpinBox.setVisible(True)
2454
            if self._pulsed_meas_logic.fast_counter_gated:
2455
                self._mw.ana_param_laser_length_Label.setVisible(True)
2456
                self._mw.ana_param_laser_length_SpinBox.setVisible(True)
2457
            else:
2458
                self._mw.ana_param_laser_length_Label.setVisible(False)
2459
                self._mw.ana_param_laser_length_SpinBox.setVisible(False)
2460
        else:
2461
            self._mw.ana_param_num_laserpulses_Label.setVisible(False)
2462
            self._mw.ana_param_num_laser_pulse_SpinBox.setVisible(False)
2463
            self._mw.ana_param_laser_length_Label.setVisible(False)
2464
            self._mw.ana_param_laser_length_SpinBox.setVisible(False)
2465
2466
    def change_second_plot(self):
2467
        """ This method handles the second plot"""
2468
        if self._mw.second_plot_ComboBox.currentText()=='None':
2469
            self._mw.second_plot_GroupBox.setVisible(False)
2470
        else:
2471
            self._mw.second_plot_GroupBox.setVisible(True)
2472
2473
            # Here FFT is seperated from the other option. The reason for that
2474
            # is preventing of code doubling
2475
            if self._mw.second_plot_ComboBox.currentText() == 'FFT':
2476
                fft_x, fft_y = self._pulsed_meas_logic.compute_fft()
2477
                self.second_plot_image.setData(fft_x, fft_y)
2478
                self._mw.pulse_analysis_second_PlotWidget.setLogMode(x=False, y=False)
2479
2480
                self._mw.pulse_analysis_second_PlotWidget.setLabel(axis='bottom',
2481
                                                                   text=self._as.ana_param_second_plot_x_axis_name_LineEdit.text(),
2482
                                                                   units=self._as.ana_param_second_plot_x_axis_unit_LineEdit.text())
2483
                self._mw.pulse_analysis_second_PlotWidget.setLabel(axis='left',
2484
                                                                   text=self._as.ana_param_second_plot_y_axis_name_LineEdit.text(),
2485
                                                                   units=self._as.ana_param_second_plot_y_axis_unit_LineEdit.text())
2486
2487
            else:
2488
                #FIXME: Is not working when there is a 0 in the values, therefore ignoring the first measurment point
2489
                self.second_plot_image.setData(self._pulsed_meas_logic.signal_plot_x[1:], self._pulsed_meas_logic.signal_plot_y[1:])
2490
2491
                if self._as.ana_param_second_plot_x_axis_name_LineEdit.text()== '':
2492
                    self._mw.pulse_analysis_second_PlotWidget.setLabel(axis='left',
2493
                                                                       text=self._as.ana_param_y_axis_name_LineEdit.text(),
2494
                                                                       units=self._as.ana_param_y_axis_unit_LineEdit.text())
2495
                    self._mw.pulse_analysis_second_PlotWidget.setLabel(axis='bottom',
2496
                                                                       text=self._as.ana_param_x_axis_name_LineEdit.text(),
2497
                                                                       units=self._as.ana_param_x_axis_unit_LineEdit.text())
2498
2499
                else:
2500
                    self._mw.pulse_analysis_second_PlotWidget.setLabel(axis='bottom',
2501
                                                                       text=self._as.ana_param_second_plot_x_axis_name_LineEdit.text(),
2502
                                                                       units=self._as.ana_param_second_plot_x_axis_unit_LineEdit.text())
2503
                    self._mw.pulse_analysis_second_PlotWidget.setLabel(axis='left',
2504
                                                                       text=self._as.ana_param_second_plot_y_axis_name_LineEdit.text(),
2505
                                                                       units=self._as.ana_param_second_plot_y_axis_unit_LineEdit.text())
2506
2507
                if self._mw.second_plot_ComboBox.currentText() == 'unchanged data':
2508
                    self._mw.pulse_analysis_second_PlotWidget.setLogMode(x=False, y=False)
2509
2510
                elif self._mw.second_plot_ComboBox.currentText() == 'Log(x)':
2511
                    self._mw.pulse_analysis_second_PlotWidget.setLogMode(x=True, y=False)
2512
2513
                elif self._mw.second_plot_ComboBox.currentText() == 'Log(y)':
2514
                    self._mw.pulse_analysis_second_PlotWidget.setLogMode(x=False,y=True)
2515
2516
                elif self._mw.second_plot_ComboBox.currentText() == 'Log(x)&Log(y)':
2517
                    self._mw.pulse_analysis_second_PlotWidget.setLogMode(x=True, y=True)
2518
2519
    def analysis_timing_changed(self):
2520
        """ This method handles the analysis timing"""
2521
        timer_interval = self._mw.time_param_ana_periode_DoubleSpinBox.value()
2522
        self._pulsed_meas_logic.set_timer_interval(timer_interval)
2523
2524
    def analysis_alternating_changed(self):
2525
        """
2526
        Is called whenever the "alternating" CheckBox is clicked
2527
        """
2528
        alternating = self._mw.ana_param_alternating_CheckBox.isChecked()
2529
        # add/remove data set in plot widget
2530
        if alternating and not self._pulsed_meas_logic.alternating:
2531
            self.signal_image2 = pg.PlotDataItem(self._pulsed_meas_logic.signal_plot_x,
2532
                                                 self._pulsed_meas_logic.signal_plot_y2,
2533
                                                 pen=palette.c3)
2534
            self._mw.pulse_analysis_PlotWidget.addItem(self.signal_image2)
2535
        if not alternating and self._pulsed_meas_logic.alternating:
2536
            self._mw.pulse_analysis_PlotWidget.removeItem(self.signal_image2)
2537
        # Set flag in logic
2538
        self._pulsed_meas_logic.alternating = alternating
2539
        # recalculate measurement ticks
2540
        self.analysis_xaxis_changed()
2541
        return
2542
2543
    def analysis_fc_binning_changed(self):
2544
        """
2545
        If a new binning value is selected, apply the change to the logic.
2546
        """
2547
        index = self._mw.ana_param_fc_bins_ComboBox.currentIndex()
2548
        fc_binning = self._binwidth_list[index]
2549
        self._pulsed_meas_logic.set_fc_binning(fc_binning)
2550
        return
2551
2552
    def analysis_xaxis_changed(self):
2553
        """
2554
        Gets called whenever the user alters manually the x axis start and increment
2555
        for the pulsed measurement.
2556
        """
2557
        xaxis_start = self._mw.ana_param_x_axis_start_ScienDSpinBox.value()
2558
        xaxis_incr = self._mw.ana_param_x_axis_inc_ScienDSpinBox.value()
2559
        if self._pulsed_meas_logic.alternating:
2560
            num_of_lasers = self._pulsed_meas_logic.number_of_lasers//2
2561
        else:
2562
            num_of_lasers = self._pulsed_meas_logic.number_of_lasers
2563
        xaxis_ticks_list = np.linspace(xaxis_start, xaxis_start+(xaxis_incr*(num_of_lasers-1)), num_of_lasers)
2564
        self._pulsed_meas_logic.set_measurement_ticks_list(xaxis_ticks_list)
2565
2566
    def ext_mw_params_changed(self):
2567
        """
2568
        Gets called whenever the parameters for the external MW source are altered,
2569
        i.e. frequency and/or power
2570
        """
2571
        freq = self._mw.ext_control_mw_freq_DoubleSpinBox.value()
2572
        power = self._mw.ext_control_mw_power_DoubleSpinBox.value()
2573
        self._pulsed_meas_logic.set_microwave_params(frequency=freq, power=power)
2574
2575
    def pulser_activation_config_changed(self):
2576
        """
2577
        Is called whenever the activation config is changed in the Analysis tab.
2578
        This is actually the activation config that controls the hardware.
2579
        """
2580
        # retreive GUI inputs
2581
        new_config_name = self._mw.pulser_activation_config_ComboBox.currentText()
2582
        new_channel_config = self._pulsed_meas_logic.get_pulser_constraints()['activation_config'][
2583
            new_config_name]
2584
        # set chosen config in pulsed measurement logic
2585
        self._pulsed_meas_logic.set_activation_config(new_config_name)
2586
        # set display new config alongside with number of channels
2587
        display_str = ''
2588
        for chnl in new_channel_config:
2589
            display_str += chnl + ' | '
2590
        display_str = display_str[:-3]
2591
        self._mw.pulser_activation_config_LineEdit.setText(display_str)
2592
2593
2594
    def pulser_sample_rate_changed(self):
2595
        """
2596
        Is called whenever the sample rate is changed in the Analysis tab.
2597
        This is actually the sample rate that is set in the hardware.
2598
        """
2599
        self._mw.pulser_sample_freq_DSpinBox.blockSignals(True)
2600
        sample_rate = self._mw.pulser_sample_freq_DSpinBox.value()*1e6
2601
        actual_sample_rate = self._pulsed_meas_logic.set_sample_rate(sample_rate)
2602
        self._mw.pulser_sample_freq_DSpinBox.setValue(actual_sample_rate/1e6)
2603
        self._mw.pulser_sample_freq_DSpinBox.blockSignals(False)
2604
2605
2606
    ###########################################################################
2607
    ###   Methods related to Settings for the 'Sequence Generator' Tab:     ###
2608
    ###########################################################################
2609
2610
    #FIXME: Implement the setting for 'Sequence Generator' tab.
2611
2612
    def _activate_sequence_settings_ui(self, e):
2613
        """ Initialize, connect and configure the Settings of the
2614
        'Sequence Generator' Tab.
2615
2616
        @param object e: Fysom.event object from Fysom class. A more detailed
2617
                         explanation can be found in the method initUI.
2618
        """
2619
2620
        pass
2621
2622
    def _deactivate_sequence_settings_ui(self, e):
2623
        """ Disconnects the configuration of the Settings for the
2624
        'Sequence Generator' Tab.
2625
2626
        @param object e: Fysom.event object from Fysom class. A more detailed
2627
                         explanation can be found in the method initUI.
2628
        """
2629
2630
        pass
2631
2632
2633
    ###########################################################################
2634
    ###         Methods related to the Tab 'Sequence Generator':            ###
2635
    ###########################################################################
2636
2637
    #FIXME: Implement the 'Sequence Generator' tab.
2638
2639
    def _activate_sequence_generator_ui(self, e):
2640
        """ Initialize, connect and configure the 'Sequence Generator' Tab.
2641
2642
        @param object e: Fysom.event object from Fysom class. A more detailed
2643
                         explanation can be found in the method initUI.
2644
        """
2645
2646
        # set viewboxes of the sequence length to proper minimum and top maximum
2647
        self._mw.curr_seq_bins_SpinBox.setMinimum(0)
2648
        self._mw.curr_seq_bins_SpinBox.setMaximum(2 ** 31 - 1)
2649
        self._mw.curr_seq_length_DSpinBox.setMinimum(0)
2650
        self._mw.curr_seq_length_DSpinBox.setMaximum(np.inf)
2651
        # self._mw.curr_seq_laserpulses_SpinBox.setMinimum(0)
2652
        # self._mw.curr_seq_laserpulses_SpinBox.setMaximum(2 ** 31 - 1)
2653
2654
        # check for sequencer mode and then hide the tab.
2655
        if not self._pulsed_meas_logic.has_sequence_mode():
2656
            # save the tab for later usage if needed in the instance variable:
2657
            self._seq_editor_tab_Widget = self._mw.tabWidget.widget(2)
2658
            self._mw.tabWidget.removeTab(2)
2659
2660
            # with that command the saved tab can be again attached to the Tab Widget
2661
            # self._mw.tabWidget.insertTab(2, self._seq_editor_tab_Widget ,'Sequence Editor')
2662
2663
        # make together with the hardware a proper dictionary for the sequence parameter:
2664
        self._seq_param = self._create_seq_param()
2665
        # create the table according to the passed values from the logic:
2666
        self._set_sequence_editor_columns()
2667
        self.update_sequence_list()
2668
2669
        # connect the signals for the block editor:
2670
        self._mw.seq_add_last_PushButton.clicked.connect(self.sequence_editor_add_row_after_last)
2671
        self._mw.seq_del_last_PushButton.clicked.connect(self.sequence_editor_delete_row_last)
2672
        self._mw.seq_add_sel_PushButton.clicked.connect(self.sequence_editor_add_row_before_selected)
2673
        self._mw.seq_del_sel_PushButton.clicked.connect(self.sequence_editor_delete_row_selected)
2674
        self._mw.seq_clear_PushButton.clicked.connect(self.sequence_editor_clear_table)
2675
2676
        self._mw.seq_editor_TableWidget.itemChanged.connect(self._update_current_pulse_sequence)
2677
2678
        # connect the buttons in the current sequence section:
2679
        self._mw.curr_seq_generate_PushButton.clicked.connect(self.generate_pulse_sequence_clicked)
2680
        self._mw.curr_seq_del_PushButton.clicked.connect(self.delete_pulse_sequence_clicked)
2681
        self._mw.curr_seq_load_PushButton.clicked.connect(self.load_pulse_sequence_clicked)
2682
2683
        # connect the buttons in the upload section
2684
        self._mw.upload_sample_seq_PushButton.clicked.connect(self.sample_sequence_clicked)
2685
        self._mw.upload_load_seq_to_channel_PushButton.clicked.connect(self.load_seq_into_channel_clicked)
2686
        self._mw.upload_seq_to_device_PushButton.clicked.connect(self.upload_seq_to_device_clicked)
2687
2688
        self._seq_gen_logic.signal_sequence_list_updated.connect(self.update_sequence_list)
2689
2690
    def _deactivate_sequence_generator_ui(self, e):
2691
        """ Disconnects the configuration for 'Sequence Generator' Tab.
2692
2693
        @param object e: Fysom.event object from Fysom class. A more detailed
2694
                         explanation can be found in the method initUI.
2695
        """
2696
        pass
2697
2698
    def _create_seq_param(self):
2699
        """ Create a dictionary for sequence parameters.
2700
2701
        @return dict: the parameter dictionary for the sequence mode
2702
2703
        Based on the information from the hardware, the logic will create an rather abstract
2704
        configuration dictionary, so that the GUI has no problems to build from that the proper
2705
        viewwidgets.
2706
        """
2707
2708
        # predefined definition dicts:
2709
        float_def = {'unit': 's', 'init_val': 0.0, 'min': 0.0, 'max': np.inf,
2710
                     'view_stepsize': 1e-9, 'dec': 8, 'unit_prefix': 'n', 'type': float}
2711
2712
        int_def = {'unit': '#', 'init_val': 0, 'min': 0, 'max': (2 ** 31 - 1),
2713
                   'view_stepsize': 1, 'dec': 0, 'unit_prefix': '', 'type': int}
2714
2715
        bool_def = {'unit': 'bool', 'init_val': 0, 'min': 0, 'max': 1,
2716
                    'view_stepsize': 1, 'dec': 0, 'unit_prefix': '', 'type': bool}
2717
2718
        seq_param_hardware = self._pulsed_meas_logic.get_pulser_constraints()['sequence_param']
2719
        seq_param = OrderedDict()
2720
2721
        # What follows now is a converion algorithm, which takes one of the valid above definition
2722
        # dicts. Then the keywords, which are given by the contraints are replaced with their
2723
        # proper value from the constraints. Furthermore an bool entry has to be converted to an
2724
        # integer expression (True=1, False=0). Then the parameter definition is appended to the
2725
        # sequence configuration parameters
2726
2727
        for entry in seq_param_hardware:
2728
            param = {}
2729
2730
            # check the type of the sequence parameter:
2731
            if type(seq_param_hardware[entry]['min']) == bool:
2732
                dict_def = bool_def
2733
            elif type(seq_param_hardware[entry]['min']) == int:
2734
                dict_def = int_def
2735
            elif type(seq_param_hardware[entry]['min']) == float:
2736
                dict_def = float_def
2737
            else:
2738
                self.log.error('The configuration dict for sequence '
2739
                        'parameter could not be created, since the keyword '
2740
                        '"min" in the parameter {0} does not correspond to '
2741
                        'type of "bool", "int" nor "float" but has a type '
2742
                        '{1}. Cannot handle that, therefore this parameter '
2743
                        'is neglected.'.format(
2744
                            entry, type(seq_param_hardware[entry]['min'])))
2745
                dict_def = {}
2746
2747
            # go through the dict_def and replace all given entries by the sequence parameter
2748
            # constraints from the hardware.
2749
            for element in dict_def:
2750
2751
                if element == 'view_stepsize':
2752
                    param['view_stepsize'] = seq_param_hardware[entry]['step']
2753
                elif element == 'init_value':
2754
                    # convert an bool value into an integer value:
2755
                    if type(element) is bool:
2756
                        param[element] = int(seq_param_hardware[entry]['min'])
2757
                    else:
2758
                        param[element] = seq_param_hardware[entry]['min']
2759
                elif element in seq_param_hardware[entry]:
2760
                    # convert an bool value into an integer value:
2761
                    if type(seq_param_hardware[entry][element]) is bool:
2762
                        param[element] = int(seq_param_hardware[entry][element])
2763
                    else:
2764
                        param[element] = seq_param_hardware[entry][element]
2765
                else:
2766
                    param[element] = dict_def[element]
2767
2768
            seq_param[entry] = param
2769
2770
        return seq_param
2771
2772
    def set_cfg_param_seq(self):
2773
        """
2774
        Set the parameter configuration of the Pulse_Sequence according to the current table
2775
        configuration and updates the dict in the logic.
2776
        """
2777
        cfg_param_seq = OrderedDict()
2778
2779
        for column in range(self._mw.seq_editor_TableWidget.columnCount()):
2780
            # keep in mind that the underscore was deleted for nicer representation during creation
2781
            text = self._mw.seq_editor_TableWidget.horizontalHeaderItem(column).text().replace(' ','_')
2782
            # split_text = text.split()
2783
            cfg_param_seq[text] = column
2784
2785
        self._cfg_param_seq = cfg_param_seq
2786
2787
2788
    def _set_sequence_editor_columns(self):
2789
        """ Depending on the sequence parameters a table witll be created. """
2790
2791
        seq_param = self._seq_param
2792
2793
        # Erase the delegate from the column, pass a None reference:
2794
        for column in range(self._mw.seq_editor_TableWidget.columnCount()):
2795
            self._mw.seq_editor_TableWidget.setItemDelegateForColumn(column, None)
2796
2797
        # clear the number of columns:
2798
        self._mw.seq_editor_TableWidget.setColumnCount(0)
2799
2800
        # set the count to the desired length:
2801
        self._mw.seq_editor_TableWidget.setColumnCount(len(seq_param)+1)
2802
2803
        column = 0
2804
        # set the name for the column:
2805
        self._mw.seq_editor_TableWidget.setHorizontalHeaderItem(column, QtWidgets.QTableWidgetItem())
2806
        self._mw.seq_editor_TableWidget.horizontalHeaderItem(column).setText('ensemble')
2807
        self._mw.seq_editor_TableWidget.setColumnWidth(column, 100)
2808
2809
        # give the delegated object the reference to the method:
2810
        item_dict = {}
2811
        item_dict['get_list_method'] = self.get_current_ensemble_list
2812
2813
        comboDelegate = ComboBoxDelegate(self._mw.seq_editor_TableWidget, item_dict)
2814
        self._mw.seq_editor_TableWidget.setItemDelegateForColumn(column, comboDelegate)
2815
2816
        # the first element was the ensemble combobox.
2817
        column = 1
2818
        for seq_param_name in seq_param:
2819
2820
            param = seq_param[seq_param_name]
2821
2822
            # self._mw.seq_editor_TableWidget.insertColumn(column)
2823
            self._mw.seq_editor_TableWidget.setHorizontalHeaderItem(column, QtWidgets.QTableWidgetItem())
2824
            header_name = seq_param_name.replace('_',' ')
2825
            self._mw.seq_editor_TableWidget.horizontalHeaderItem(column).setText(header_name)
2826
            self._mw.seq_editor_TableWidget.setColumnWidth(column, 80)
2827
2828
            # choose the proper delegate function:
2829
            if param['type'] == bool:
2830
                item_dict = param
2831
                delegate = CheckBoxDelegate(self._mw.seq_editor_TableWidget, item_dict)
2832
            elif param['type'] == int:
2833
                item_dict = param
2834
                delegate = SpinBoxDelegate(self._mw.seq_editor_TableWidget, item_dict)
2835
            elif param['type'] == float:
2836
                item_dict = param
2837
                delegate = DoubleSpinBoxDelegate(self._mw.seq_editor_TableWidget, item_dict)
2838
2839
            self._mw.seq_editor_TableWidget.setItemDelegateForColumn(column, delegate)
2840
2841
            column += 1
2842
2843
        # at the end, initialize all the cells with the proper value:
2844
        self.initialize_cells_sequence_editor(start_row=0,
2845
                                              stop_row=self._mw.seq_editor_TableWidget.rowCount())
2846
2847
        self.set_cfg_param_seq()
2848
        self._update_current_pulse_sequence()
2849
2850 View Code Duplication
    def initialize_cells_sequence_editor(self, start_row, stop_row=None,
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
2851
                                         start_col=None, stop_col=None):
2852
        """ Initialize the desired cells in the pulse sequence table.
2853
2854
        @param int start_row: index of the row, where the initialization should start
2855
        @param int stop_row: optional, index of the row, where the initalization should end
2856
        @param int start_col: optional, index of the column where the initialization should start
2857
        @param int stop_col: optional, index of the column, where the initalization should end.
2858
2859
        With this function it is possible to reinitialize specific elements or part of a row or
2860
        even the whole row. If start_row is set to 0 the whole row is going to be initialzed to the
2861
        default value.
2862
        """
2863
2864
        if stop_row is None:
2865
            stop_row = start_row + 1
2866
2867
        if start_col is None:
2868
            start_col = 0
2869
2870
        if stop_col is None:
2871
            stop_col = self._mw.seq_editor_TableWidget.columnCount()
2872
2873
        for col_num in range(start_col, stop_col):
2874
2875
            for row_num in range(start_row, stop_row):
2876
                # get the model, here are the data stored:
2877
                model = self._mw.seq_editor_TableWidget.model()
2878
                # get the corresponding index of the current element:
2879
                index = model.index(row_num, col_num)
2880
                # get the initial values of the delegate class which was
2881
                # uses for this column:
2882
                ini_values = self._mw.seq_editor_TableWidget.itemDelegateForColumn(col_num).get_initial_value()
2883
                # set initial values:
2884
                model.setData(index, ini_values[0], ini_values[1])
2885
2886
2887
    def sequence_editor_add_row_before_selected(self, insert_rows=1):
2888
        """ Add row before selected element. """
2889
2890
        self._mw.seq_editor_TableWidget.blockSignals(True)
2891
2892
        selected_row = self._mw.seq_editor_TableWidget.currentRow()
2893
2894
        # the signal passes a boolean value, which overwrites the insert_rows
2895
        # parameter. Check that here and use the actual default value:
2896
        if type(insert_rows) is bool:
2897
            insert_rows = 1
2898
2899
        for rows in range(insert_rows):
2900
            self._mw.seq_editor_TableWidget.insertRow(selected_row)
2901
        self.initialize_cells_sequence_editor(start_row=selected_row,
2902
                                              stop_row=selected_row + insert_rows)
2903
2904
        self._mw.seq_editor_TableWidget.blockSignals(False)
2905
        self._update_current_pulse_sequence()
2906
2907 View Code Duplication
    def sequence_editor_add_row_after_last(self, insert_rows=1):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
2908
        """ Add row after last row in the sequence editor. """
2909
2910
        self._mw.seq_editor_TableWidget.blockSignals(True)
2911
2912
        # the signal passes a boolean value, which overwrites the insert_rows
2913
        # parameter. Check that here and use the actual default value:
2914
        if type(insert_rows) is bool:
2915
            insert_rows = 1
2916
2917
        number_of_rows = self._mw.seq_editor_TableWidget.rowCount()
2918
2919
        self._mw.seq_editor_TableWidget.setRowCount(
2920
            number_of_rows + insert_rows)
2921
        self.initialize_cells_sequence_editor(start_row=number_of_rows,
2922
                                              stop_row=number_of_rows + insert_rows)
2923
2924
        self._mw.seq_editor_TableWidget.blockSignals(False)
2925
        self._update_current_pulse_sequence()
2926
2927
    def sequence_editor_delete_row_selected(self):
2928
        """ Delete row of selected element. """
2929
2930
        # get the row number of the selected item(s). That will return the
2931
        # lowest selected row
2932
        row_to_remove = self._mw.seq_editor_TableWidget.currentRow()
2933
        self._mw.seq_editor_TableWidget.removeRow(row_to_remove)
2934
        self._update_current_pulse_sequence()
2935
2936
    def sequence_editor_delete_row_last(self):
2937
        """ Delete the last row in the sequence editor. """
2938
2939
        number_of_rows = self._mw.seq_editor_TableWidget.rowCount()
2940
        # remember, the row index is started to count from 0 and not from 1,
2941
        # therefore one has to reduce the value by 1:
2942
        self._mw.seq_editor_TableWidget.removeRow(number_of_rows - 1)
2943
        self._update_current_pulse_sequence()
2944
2945
    def sequence_editor_clear_table(self):
2946
        """ Delete all rows in the sequence editor table. """
2947
2948
        self._mw.seq_editor_TableWidget.blockSignals(True)
2949
2950
        self._mw.seq_editor_TableWidget.setRowCount(1)
2951
        self._mw.seq_editor_TableWidget.clearContents()
2952
2953
        self.initialize_cells_sequence_editor(start_row=0)
2954
        self._mw.seq_editor_TableWidget.blockSignals(False)
2955
        self._update_current_pulse_sequence()
2956
2957
2958
    # load, delete, generate and update functionality for pulse sequence:
2959
2960
    def load_pulse_sequence_clicked(self, sequence_name=None):
2961
        """ Loads the current selected Pulse_Sequence object from the logic into the editor or a
2962
            specified object with name sequence_name.
2963
2964
        @param str sequence_name: optional, name of a Pulse_Sequence object, which should be loaded
2965
                                  into the GUI Sequence Editor. If no name passed, the current
2966
                                  Pulse_Sequence from the Logic is taken to be loaded.
2967
2968
        Unfortuanetly this method needs to know how Pulse_Sequence objects are looking like and
2969
        cannot be that general, since it will load an object and a table, where the data have to be
2970
        converted.
2971
        """
2972
2973
        # NOTE: This method will be connected to the CLICK event of a QPushButton, which passes an
2974
        #       optional argument as a bool value depending on the checked state of the
2975
        #       QPushButton (that is called an overloaded routine). The passed boolean value has to
2976
        #       be handled in addition!
2977
2978
        if (sequence_name is not None) and (type(sequence_name) is not bool):
2979
            current_sequence_name = sequence_name
2980
        else:
2981
            current_sequence_name = self._mw.saved_seq_ComboBox.currentText()
2982
2983
        # get the ensemble object and set as current ensemble
2984
        seq_obj = self._seq_gen_logic.get_pulse_sequence(current_sequence_name,
2985
                                                         set_as_current_sequence=True)
2986
2987
        # Check whether an sequence is found, otherwise there will be None:
2988
        if seq_obj is None:
2989
            return
2990
2991
        self.sequence_editor_clear_table()  # clear the block organizer table
2992
        rows = len(seq_obj.ensemble_param_list)  # get amout of rows needed for display
2993
2994
        # add as many rows as there are blocks in the sequence minus 1 because a single row is
2995
        # already present after clear
2996
        self.sequence_editor_add_row_after_last(rows - 1)
2997
2998
        # This dictionary has the information which column number describes which object, it is a
2999
        # configuration dict between GUI and logic.
3000
        seq_config_dict = self._cfg_param_seq
3001
3002
        # run through all blocks in the block_elements block_list to fill in the
3003
        # row informations
3004
        for row_index, (pulse_ensemble, seq_param) in enumerate(seq_obj.ensemble_param_list):
3005
3006
            column = seq_config_dict['ensemble']
3007
            self.set_element_in_sequence_table(row_index, column, pulse_ensemble.name)
3008
3009
            # boa... int is not equal to np.int32 that has to handled!
3010
            for entry in seq_param:
3011
                column = seq_config_dict[entry]
3012
                if type(seq_param[entry]) is np.int32:
3013
                    self.set_element_in_sequence_table(row_index, column, int(seq_param[entry]))
3014
                elif type(seq_param[entry]) is np.float32:
3015
                    self.set_element_in_sequence_table(row_index, column, float(seq_param[entry]))
3016
                else:
3017
                    self.set_element_in_sequence_table(row_index, column, seq_param[entry])
3018
        # set the ensemble name LineEdit to the current ensemble
3019
        self._mw.curr_seq_name_LineEdit.setText(current_sequence_name)
3020
        pass
3021
3022
    def delete_pulse_sequence_clicked(self):
3023
        """
3024
        Actions to perform when the delete button in the sequence editor is clicked
3025
        """
3026
        name = self._mw.saved_seq_ComboBox.currentText()
3027
        self._seq_gen_logic.delete_sequence(name)
3028
        self.update_sequence_list()
3029
        return
3030
3031
3032
    def generate_pulse_sequence_clicked(self):
3033
        """ Generate a Pulse_Sequence object."""
3034
        objectname = self._mw.curr_seq_name_LineEdit.text()
3035
        if objectname == '':
3036
            self.log.warning('No Name for Pulse_Sequence specified. '
3037
                    'Generation aborted!')
3038
            return
3039
        rotating_frame = self._mw.curr_seq_rot_frame_CheckBox.isChecked()
3040
3041
        self.generate_pulse_sequence_object(objectname, self.get_sequence_table(), rotating_frame)
3042
3043
    def generate_pulse_sequence_object(self, sequence_name, sequence_matrix, rotating_frame=True):
3044
        """ Generates a Pulse_Sequence object out of the corresponding editor table/matrix.
3045
3046
        @param str sequence_name: name of the created Pulse_Sequence object
3047
        @param np.array sequence_matrix: structured 2D np.array, matrix, in which the construction
3048
                                         plan for Pulse_Block_Ensemble objects are displayed as
3049
                                         rows.
3050
        @param bool rotating_frame: optional, whether the phase preservation is mentained
3051
                                    throughout the sequence.
3052
3053
        Creates a collection of Pulse_Block_Ensemble objects.
3054
        """
3055
        # list of all the Pulse_Block_Ensemble objects and their parameters
3056
        ensemble_param_list = [None] * len(sequence_matrix)
3057
3058
        for row_index, row in enumerate(sequence_matrix):
3059
            # the ensemble entry must be always (!) present, therefore this entry in the
3060
            # configuration dict for the sequence parameter are taken for granted. Get from the
3061
            # cfg_param_seq the relative situation to the other parameters (which is in the table
3062
            # the column number)
3063
            column_index = self._cfg_param_seq['ensemble']
3064
            pulse_block_ensemble_name = row[column_index].decode('UTF-8')
3065
3066
            # the rest must be obtained together with the actual sequence configuration parameter
3067
            # dict cfg_param_seq and the hardware constraints:
3068
            seq_param_hardware = self._pulsed_meas_logic.get_pulser_constraints()['sequence_param']
3069
3070
            # here the actual configuration will be save:
3071
            seq_param = dict()
3072
3073
            for param in seq_param_hardware:
3074
                # get the the relative situation to the other parameters (which is in the table
3075
                # the column number):
3076
                column_index = self._cfg_param_seq[param]
3077
                # save in the sequenc parameter dict:
3078
                seq_param[param] = row[column_index]
3079
3080
            # small and simple search routine, which tries to extract a repetition parameter
3081
            # (but the presence of such parameter is not assumed!):
3082
            # All the sequence parameter keywords are string identifiers.
3083
            for param in seq_param:
3084
                if 'reps' in param.lower() or 'repetition' in param.lower():
3085
                    pulse_block_ensemble_reps = seq_param[param]
3086
                    break
3087
                else:
3088
                    pulse_block_ensemble_reps = 0
3089
3090
            # get the reference on the Pulse_Block_Ensemble object:
3091
            pulse_block_ensemble = self._seq_gen_logic.get_pulse_block_ensemble(
3092
                pulse_block_ensemble_name)
3093
3094
            # save in the list the object and sequence parameter
3095
            ensemble_param_list[row_index] = (pulse_block_ensemble, seq_param)
3096
3097
        pulse_sequence = Pulse_Sequence(name=sequence_name, ensemble_param_list=ensemble_param_list,
3098
                                        rotating_frame=rotating_frame)
3099
        # save sequence
3100
        self._seq_gen_logic.save_sequence(sequence_name, pulse_sequence)
3101
3102
3103
    def update_sequence_list(self):
3104
        """  Called upon signal_block_list_updated emit of the sequence_generator_logic.
3105
3106
        Updates all ComboBoxes showing generated blocks.
3107
        """
3108
        # # updated list of all generated blocks
3109
        new_list = self._seq_gen_logic.saved_pulse_sequences
3110
        # update saved_blocks_ComboBox items
3111
        self._mw.saved_seq_ComboBox.clear()
3112
        self._mw.saved_seq_ComboBox.addItems(new_list)
3113
3114
        self._mw.upload_seq_ComboBox.clear()
3115
        self._mw.upload_seq_ComboBox.addItems(new_list)
3116
3117
        # Set active index of the ComboBoxes to the currently shown/last created sequence
3118
        if self._seq_gen_logic.current_sequence is not None:
3119
3120
            # get last generated and currently shown ensemble name from logic
3121
            current_sequence_name = self._seq_gen_logic.current_sequence.name
3122
            # identify the corresponding index within the ComboBox
3123
            index_to_set = self._mw.upload_seq_ComboBox.findText(current_sequence_name)
3124
3125
            self._mw.saved_seq_ComboBox.setCurrentIndex(index_to_set)
3126
            self._mw.upload_seq_ComboBox.setCurrentIndex(index_to_set)
3127
        else:
3128
            # set the current sequence in the logic and all ComboBoxes to the currently
3129
            # shown sequence in the upload_seq_ComboBox.
3130
3131
            current_sequence_name = self._mw.upload_seq_ComboBox.currentText()
3132
            index_to_set = self._mw.saved_seq_ComboBox.findText(current_sequence_name)
3133
            self._mw.saved_seq_ComboBox.setCurrentIndex(index_to_set)
3134
            self.load_pulse_sequence_clicked()
3135
        return
3136
3137
    def _update_current_pulse_sequence(self):
3138
        """ Update the current Pulse Sequence Info in the display. """
3139
3140
        length_milli = 0.0  # in milliseconds
3141
        length_bin = 0
3142
        # num_laser_pulses = 0
3143
3144
3145
        pulse_block_col = self._cfg_param_seq['ensemble']
3146
3147
        reps_col = self._cfg_param_seq.get('reps')
3148
3149
        if len(self._seq_gen_logic.saved_pulse_block_ensembles) > 0:
3150
            for row_ind in range(self._mw.seq_editor_TableWidget.rowCount()):
3151
                pulse_block_ensemble_name = self.get_element_in_sequence_table(row_ind, pulse_block_col)
3152
3153
                ensemble_obj = self._seq_gen_logic.get_pulse_block_ensemble(pulse_block_ensemble_name)
3154
3155
                if reps_col is None:
3156
                    reps = 0
3157
                else:
3158
                    reps = self.get_element_in_sequence_table(row_ind, reps_col)
3159
3160
                # Calculate the length via the gaussian summation formula:
3161
                length_bin = int(length_bin + ensemble_obj.length_bins * (reps + 1) )
3162
3163
                # num_laser_pulses = num_laser_pulses + block_obj.number_of_lasers * (reps + 1)
3164
3165
            length_milli = (length_bin / self._seq_gen_logic.sample_rate) * 1e3  # in milliseconds
3166
3167
        self._mw.curr_seq_length_DSpinBox.setValue(length_milli)
3168
        self._mw.curr_seq_bins_SpinBox.setValue(length_bin)
3169
        # self._mw.curr_ensemble_laserpulses_SpinBox.setValue(num_laser_pulses)
3170
        return
3171
3172
3173
    # Sample, Upload and Load functionality for Pulse Sequence
3174
3175
    def sample_sequence_clicked(self):
3176
        """
3177
        This method is called when the user clicks on "sample"
3178
        """
3179
        # Get the ensemble name to be uploaded from the ComboBox
3180
        sequence_name = self._mw.upload_seq_ComboBox.currentText()
3181
3182
        # Sample the ensemble via logic module
3183
3184
        self._seq_gen_logic.sample_pulse_sequence(sequence_name, write_to_file=True,
3185
                                                  chunkwise=self._write_chunkwise)
3186
        return
3187
3188
    def upload_seq_to_device_clicked(self):
3189
        """
3190
        This method is called when the user clicks on "upload to device"
3191
        """
3192
3193
        # Get the asset name to be uploaded from the ComboBox
3194
        seq_name = self._mw.upload_seq_ComboBox.currentText()
3195
        print(seq_name)
3196
3197
        # Upload the asset via logic module
3198
        self._pulsed_meas_logic.upload_asset(seq_name)
3199
        return
3200
3201
    def load_seq_into_channel_clicked(self):
3202
        """
3203
        This method is called when the user clicks on "load to channel"
3204
        """
3205
        # Get the asset name to be uploaded from the ComboBox
3206
        asset_name = self._mw.upload_seq_ComboBox.currentText()
3207
3208
        # Check out on which channel it should be uploaded:
3209
        # FIXME: Implement a proper GUI element (upload center) to manually assign assets to channels
3210
        # Right now the default is chosen to invoke channel assignment from the Ensemble/Sequence object
3211
        load_dict = {}
3212
3213
        # Load asset into channles via logic module
3214
        self._pulsed_meas_logic.load_asset(asset_name, load_dict)
3215
        return
3216
3217
    def get_element_in_sequence_table(self, row, column):
3218
        """ Simplified wrapper function to get the data from a specific cell in the pulse sequence
3219
            table.
3220
3221
        @param int row: row index
3222
        @param int column: column index
3223
3224
        @return: the value of the corresponding cell, which can be a string, a float or an integer.
3225
                 Remember that the checkbox state unchecked corresponds to 0 and check to 2. That
3226
                 is Qt convention.
3227
3228
        Note that the order of the arguments in this function (first row index
3229
        and then column index) was taken from the Qt convention.
3230
        """
3231
3232
        tab = self._mw.seq_editor_TableWidget
3233
3234
        # Get from the corresponding delegate the data access model
3235
        access = tab.itemDelegateForColumn(column).model_data_access
3236
        data = tab.model().index(row, column).data(access)
3237
3238
        # check whether the value has to be normalized to SI values.
3239
        if hasattr(tab.itemDelegateForColumn(column), 'get_unit_prefix'):
3240
            unit_prefix = tab.itemDelegateForColumn(column).get_unit_prefix()
3241
            # access the method defined in base for unit prefix:
3242
            return data * units.get_unit_prefix_dict()[unit_prefix]
3243
3244
        return data
3245
3246
    def set_element_in_sequence_table(self, row, column, value):
3247
        """ Simplified wrapper function to set the data to a specific cell in the pulse sequence
3248
            table.
3249
3250
        @param int row: row index
3251
        @param int column: column index
3252
3253
        Note that the order of the arguments in this function (first row index and then column
3254
        index) was taken from the Qt convention.
3255
        A type check will be performed for the passed value argument. If the type does not
3256
        correspond to the delegate, then the value will not be changed. You have to ensure that!
3257
        """
3258
3259
        tab = self._mw.seq_editor_TableWidget
3260
        model = tab.model()
3261
        access = tab.itemDelegateForColumn(column).model_data_access
3262
        data = tab.model().index(row, column).data(access)
3263
3264
        if type(data) == type(value):
3265
            model.setData(model.index(row, column), value, access)
3266
        else:
3267
            self.log.warning('The cell ({0},{1}) in pulse sequence table '
3268
                    'could not be assigned with the value="{2}", since the '
3269
                    'type "{3}" of the cell of the delegated column differs '
3270
                    'from the type "{4}" of the value!\n'
3271
                    'Previous value will be kept.'.format(
3272
                        row, column, value, type(data), type(value)))
3273
        return
3274
3275
    def get_sequence_table(self):
3276
        """ Convert sequence table data to numpy array.
3277
3278
        @return: np.array[rows][columns] which has a structure, i.e. strings
3279
                 integer and float values are represented by this array.
3280
        """
3281
3282
        tab = self._mw.seq_editor_TableWidget
3283
3284
        # create a structure for the output numpy array:
3285
        structure = ''
3286
        for column in range(tab.columnCount()):
3287
            elem = self.get_element_in_sequence_table(0, column)
3288
            if type(elem) is str:
3289
                structure = structure + '|S20, '
3290
            elif type(elem) is int:
3291
                structure = structure + '|int, '
3292
            elif type(elem) is float:
3293
                structure = structure + '|float, '
3294
            else:
3295
                self.log.error('Type definition not found in the sequence '
3296
                        'table.'
3297
                        '\nType is neither a string, integer or float. '
3298
                        'Include that type in the get_sequence_table method!')
3299
3300
        # remove the last two elements since these are a comma and a space:
3301
        structure = structure[:-2]
3302
        table = np.zeros(tab.rowCount(), dtype=structure)
3303
3304
        # fill the return table:
3305
        for column in range(tab.columnCount()):
3306
            for row in range(tab.rowCount()):
3307
                table[row][column] = self.get_element_in_sequence_table(row, column)
3308
3309
        return table
3310
3311
3312
    ###########################################################################
3313
    ###    Methods related to Settings for the 'Pulse Extraction' Tab:      ###
3314
    ###########################################################################
3315
3316
    #FIXME: Implement the setting for 'Pulse Extraction' tab.
3317
3318
    def _activate_pulse_extraction_settings_ui(self, e):
3319
        """ Initialize, connect and configure the Settings of the
3320
        'Sequence Generator' Tab.
3321
3322
        @param object e: Fysom.event object from Fysom class. A more detailed
3323
                         explanation can be found in the method initUI.
3324
        """
3325
        pass
3326
3327
    def _deactivate_pulse_extraction_settings_ui(self, e):
3328
        """ Disconnects the configuration of the Settings for the
3329
        'Sequence Generator' Tab.
3330
3331
        @param object e: Fysom.event object from Fysom class. A more detailed
3332
                         explanation can be found in the method initUI.
3333
        """
3334
3335
        pass
3336
3337
3338
    ###########################################################################
3339
    ###          Methods related to the Tab 'Pulse Extraction':             ###
3340
    ###########################################################################
3341
3342
3343
    def _activate_pulse_extraction_ui(self, e):
3344
        """ Initialize, connect and configure the 'Pulse Extraction' Tab.
3345
3346
        @param object e: Fysom.event object from Fysom class. A more detailed
3347
                         explanation can be found in the method initUI.
3348
        """
3349
3350
        # Configure all objects for laserpulses_PlotWidget and also itself:
3351
3352
        # Adjust settings for the moveable lines in the pulses plot:
3353
        self.sig_start_line = pg.InfiniteLine(pos=0, pen=QtGui.QPen(palette.c3), movable=True)
3354
        self.sig_start_line.setHoverPen(QtGui.QPen(palette.c2))
3355
        self.sig_end_line = pg.InfiniteLine(pos=0, pen=QtGui.QPen(palette.c3), movable=True)
3356
        self.sig_end_line.setHoverPen(QtGui.QPen(palette.c2))
3357
        self.ref_start_line = pg.InfiniteLine(pos=0, pen=QtGui.QPen(palettedark.c4), movable=True)
3358
        self.ref_start_line.setHoverPen(QtGui.QPen(palette.c4))
3359
        self.ref_end_line = pg.InfiniteLine(pos=0, pen=QtGui.QPen(palettedark.c4), movable=True)
3360
        self.ref_end_line.setHoverPen(QtGui.QPen(palette.c4))
3361
3362
        # the actual data:
3363
        self.lasertrace_image = pg.PlotDataItem(self._pulsed_meas_logic.laser_plot_x,
3364
                                                self._pulsed_meas_logic.laser_plot_y,
3365
                                                pen=palette.c1)
3366
3367
        self._mw.laserpulses_PlotWidget.addItem(self.lasertrace_image)
3368
        self._mw.laserpulses_PlotWidget.addItem(self.sig_start_line)
3369
        self._mw.laserpulses_PlotWidget.addItem(self.sig_end_line)
3370
        self._mw.laserpulses_PlotWidget.addItem(self.ref_start_line)
3371
        self._mw.laserpulses_PlotWidget.addItem(self.ref_end_line)
3372
3373
        #self._mw.laserpulses_PlotWidget.setLabel('bottom', 'tau', units='s')
3374
        self._mw.laserpulses_PlotWidget.setLabel('bottom', 'bins')
3375
3376
        # Configure all objects for measuring_error_PlotWidget and also itself:
3377
        self.measuring_error_image = pg.PlotDataItem(self._pulsed_meas_logic.measuring_error_plot_x,
3378
                                                     self._pulsed_meas_logic.measuring_error_plot_y*1000,
3379
                                                     pen=palette.c1)
3380
        self._mw.measuring_error_PlotWidget.addItem(self.measuring_error_image)
3381
        self._mw.measuring_error_PlotWidget.setLabel('left', 'measuring error', units='a.u.')
3382
        self._mw.measuring_error_PlotWidget.setLabel('bottom', 'tau', units='ns')
3383
3384
        self._mw.extract_param_ana_window_start_SpinBox.setValue(self._pulsed_meas_logic.signal_start_bin)
3385
        self._mw.extract_param_ana_window_width_SpinBox.setValue(self._pulsed_meas_logic.signal_width_bin)
3386
        self._mw.extract_param_ref_window_start_SpinBox.setValue(self._pulsed_meas_logic.norm_start_bin)
3387
        self._mw.extract_param_ref_window_width_SpinBox.setValue(self._pulsed_meas_logic.norm_width_bin)
3388
3389
        # Display laser pulses, connect change of viewboxes and change of lines:
3390
        self._mw.extract_param_ana_window_start_SpinBox.valueChanged.connect(self.analysis_window_values_changed)
3391
        self._mw.extract_param_ana_window_width_SpinBox.valueChanged.connect(self.analysis_window_values_changed)
3392
        self._mw.extract_param_ref_window_start_SpinBox.valueChanged.connect(self.analysis_window_values_changed)
3393
        self._mw.extract_param_ref_window_width_SpinBox.valueChanged.connect(self.analysis_window_values_changed)
3394
        self.analysis_window_values_changed()   # run it to apply changes.
3395
3396 View Code Duplication
        self.sig_start_line.sigPositionChanged.connect(self.analysis_window_sig_line_start_changed)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
3397
        self.sig_end_line.sigPositionChanged.connect(self.analysis_window_sig_line_stop_changed)
3398
        self.ref_start_line.sigPositionChanged.connect(self.analysis_window_ref_line_start_changed)
3399
        self.ref_end_line.sigPositionChanged.connect(self.analysis_window_ref_line_stop_changed)
3400
3401
        self._mw.laserpulses_ComboBox.currentIndexChanged.connect(self.refresh_laser_pulses_display)
3402
        self._mw.laserpulses_display_raw_CheckBox.stateChanged.connect(self.refresh_laser_pulses_display)
3403
3404
        self._pulsed_meas_logic.sigSinglePulsesUpdated.connect(self.refresh_laser_pulses_display)
3405
        self._pulsed_meas_logic.sigPulseAnalysisUpdated.connect(self.refresh_laser_pulses_display)
3406
3407
        # Setting standard deviation of gaussian convolution
3408
        self._mw.conv_std_dev.setRange(self._pulsed_meas_logic.conv_std_dev_range_min, self._pulsed_meas_logic.conv_std_dev_range_max)
3409
        self._mw.conv_std_dev.setValue(self._pulsed_meas_logic.conv_std_dev)
3410
        self._mw.conv_std_dev.valueChanged.connect(self.conv_std_dev_changed)
3411
3412
        self._mw.slider_conv_std_dev.setRange(self._pulsed_meas_logic.conv_std_dev_range_min, self._pulsed_meas_logic.conv_std_dev_range_max)
3413
        self._mw.slider_conv_std_dev.sliderMoved.connect(self.slider_conv_std_dev_changed)
3414
3415
    def _deactivate_pulse_extraction_ui(self, e):
3416
        """ Disconnects the configuration for 'Pulse Extraction' Tab.
3417
3418
        @param object e: Fysom.event object from Fysom class. A more detailed
3419
                         explanation can be found in the method initUI.
3420
        """
3421
3422
    def num_of_lasers_changed(self):
3423
        """
3424
        Handle what happens if number of laser pulses changes.
3425
        """
3426
        self._mw.laserpulses_ComboBox.blockSignals(True)
3427
3428
        self._mw.laserpulses_ComboBox.clear()
3429
        self._mw.laserpulses_ComboBox.addItem('sum')
3430
        new_num_of_lasers = self._mw.ana_param_num_laser_pulse_SpinBox.value()
3431
        for ii in range(new_num_of_lasers):
3432
            self._mw.laserpulses_ComboBox.addItem(str(1+ii))
3433
3434
        self._mw.laserpulses_ComboBox.blockSignals(False)
3435
3436
        if self._mw.ana_param_laserpulse_defined_CheckBox.isChecked():
3437
            self._pulsed_meas_logic.set_num_of_lasers(new_num_of_lasers)
3438
3439
        self.analysis_xaxis_changed()
3440
3441
    def laser_length_changed(self):
3442
        """
3443
        Handle what happens if length of laser pulses change.
3444
        """
3445
        new_laser_length = self._mw.ana_param_laser_length_SpinBox.value()/1e9
3446
        self._pulsed_meas_logic.set_laser_length(new_laser_length)
3447
3448
    def conv_std_dev_changed(self):
3449
        """
3450
        Uodate new value of standard deviation of gaussian filter
3451
        """
3452
3453
        std_dev = self._mw.conv_std_dev.value()
3454
        self._mw.slider_conv_std_dev.setValue(std_dev)
3455
        self._pulsed_meas_logic.conv_std_dev = std_dev
3456
3457
    def slider_conv_std_dev_changed(self):
3458
        """
3459
        Uodate new value of standard deviation of gaussian filter
3460
        from slider
3461
        """
3462
3463
        std_dev = self._mw.slider_conv_std_dev.value()
3464
        self._mw.conv_std_dev.setValue(std_dev)
3465
        self._pulsed_meas_logic.conv_std_dev = std_dev
3466
3467
    def analysis_window_values_changed(self):
3468
        """ If the boarders or the lines are changed update the other parameters
3469
        """
3470
3471
        sig_start = self._mw.extract_param_ana_window_start_SpinBox.value()
3472
        sig_length = self._mw.extract_param_ana_window_width_SpinBox.value()
3473
        ref_start = self._mw.extract_param_ref_window_start_SpinBox.value()
3474
        ref_length = self._mw.extract_param_ref_window_width_SpinBox.value()
3475
3476
        self.sig_start_line.setValue(sig_start)
3477
        self.sig_end_line.setValue(sig_start+sig_length)
3478
3479
        self.ref_start_line.setValue(ref_start)
3480
        self.ref_end_line.setValue(ref_start+ref_length)
3481
3482
        self._pulsed_meas_logic.signal_start_bin = sig_start
3483
        self._pulsed_meas_logic.signal_width_bin = sig_length
3484
        self._pulsed_meas_logic.norm_start_bin = ref_start
3485
        self._pulsed_meas_logic.norm_width_bin = ref_length
3486
3487
    def analysis_window_sig_line_start_changed(self):
3488
        """ React when the start signal line get moved by the user. """
3489
        sig_start = self.sig_start_line.value()
3490
        self._mw.extract_param_ana_window_start_SpinBox.setValue(sig_start)
3491
        self._pulsed_meas_logic.signal_start_bin = int(sig_start)
3492
3493
    def analysis_window_sig_line_stop_changed(self):
3494
        """ React when the stop signal line get moved by the user. """
3495 View Code Duplication
        sig_start = self.sig_start_line.value()
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
3496
        sig_length = self.sig_end_line.value() - sig_start
3497
        self._mw.extract_param_ana_window_width_SpinBox.setValue(sig_length)
3498
        self._pulsed_meas_logic.signal_width_bin = int(sig_length)
3499
3500
    def analysis_window_ref_line_start_changed(self):
3501
        """ React when the reference start line get moved by the user. """
3502
        ref_start = self.ref_start_line.value()
3503
        self._mw.extract_param_ref_window_start_SpinBox.setValue(ref_start)
3504
        self._pulsed_meas_logic.norm_start_bin = int(ref_start)
3505
3506
    def analysis_window_ref_line_stop_changed(self):
3507
        """ React when the reference stop line get moved by the user. """
3508
        ref_start = self.ref_start_line.value()
3509
        ref_length = self.ref_end_line.value()-ref_start
3510
        self._mw.extract_param_ref_window_width_SpinBox.setValue(ref_length)
3511
        self._pulsed_meas_logic.norm_width_bin = int(ref_length)
3512
3513
    def refresh_laser_pulses_display(self):
3514
        """ Refresh the extracted laser pulse display. """
3515 View Code Duplication
        current_laser = self._mw.laserpulses_ComboBox.currentText()
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
3516
3517
        if current_laser == 'sum':
3518
            show_laser_num = 0
3519
        else:
3520
            show_laser_num = int(current_laser)
3521
3522
        if self._mw.laserpulses_display_raw_CheckBox.isChecked():
3523
            self._pulsed_meas_logic.raw_laser_pulse = True
3524
        else:
3525
            self._pulsed_meas_logic.raw_laser_pulse = False
3526
3527
        x_data, y_data = self._pulsed_meas_logic.get_laserpulse(show_laser_num)
3528
3529
        self.lasertrace_image.setData(x=x_data, y=y_data)
3530
3531
    def save_plots(self):
3532
        """ Save plot from analysis graph as a picture. """
3533
        timestamp = datetime.datetime.now()
3534
        filetag = self._mw.save_tag_LineEdit.text()
3535
        filepath = self._save_logic.get_path_for_module(module_name='PulsedMeasurement')
3536
        if len(filetag) > 0:
3537
            filename = os.path.join(filepath, '{0}_{1}_pulsed'.format(timestamp.strftime('%Y%m%d-%H%M-%S'), filetag))
3538
        else:
3539
            filename = os.path.join(filepath, '{0}_pulsed'.format(timestamp.strftime('%Y%m%d-%H%M-%S')))
3540
3541
        # print(type(self._mw.second_plot_ComboBox.currentText()), self._mw.second_plot_ComboBox.currentText())
3542
        # pulse plot
3543
        # exporter = pg.exporters.SVGExporter(self._mw.pulse_analysis_PlotWidget.plotItem.scene())
3544
        # exporter.export(filename+'.svg')
3545
        #
3546
        # # auxiliary plot
3547
        # if 'None' not in self._mw.second_plot_ComboBox.currentText():
3548
        #     exporter_aux = pg.exporters.SVGExporter(self._mw.pulse_analysis_second_PlotWidget.plotItem.scene())
3549
        #     exporter_aux.export(filename + '_aux' + '.svg')
3550
3551
        self._pulsed_meas_logic._save_data(filetag, timestamp)
3552
3553
3554
3555