Completed
Push — pulsed_with_queued_connections ( 8830a7...cddfb2 )
by
unknown
03:09
created

MagnetGui.update_magnet_settings()   B

Complexity

Conditions 5

Size

Total Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
c 1
b 0
f 0
dl 0
loc 25
rs 8.0894
1
# -*- coding: utf-8 -*-
2
3
"""
4
This file contains the GUI for magnet control.
5
6
QuDi is free software: you can redistribute it and/or modify
7
it under the terms of the GNU General Public License as published by
8
the Free Software Foundation, either version 3 of the License, or
9
(at your option) any later version.
10
11
QuDi is distributed in the hope that it will be useful,
12
but WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
GNU General Public License for more details.
15
16
You should have received a copy of the GNU General Public License
17
along with QuDi. If not, see <http://www.gnu.org/licenses/>.
18
19
Copyright (c) the Qudi Developers. See the COPYRIGHT.txt file at the
20
top-level directory of this distribution and at <https://github.com/Ulm-IQO/qudi/>
21
"""
22
23
import os
24
import numpy as np
25
import pyqtgraph as pg
26
from qtpy import QtCore
27
from qtpy import QtWidgets
28
from qtpy import uic
29
import datetime
30
31
from gui.guibase import GUIBase
32
from gui.guiutils import ColorBar
33
from gui.colordefs import ColorScaleInferno
34
from core.util.units import get_unit_prefix_dict
35
from tools.scientific_spinbox import ScienSpinBox
36
from tools.scientific_spinbox import ScienDSpinBox
37
import pyqtgraph.exporters
38
39
40
class MagnetMainWindow(QtWidgets.QMainWindow):
41
    """ Create the Main Window based on the *.ui file. """
42
43
    def __init__(self):
44
        # Get the path to the *.ui file
45
        this_dir = os.path.dirname(__file__)
46
        ui_file = os.path.join(this_dir, 'ui_magnet_gui.ui')
47
48
        # Load it
49
        super(MagnetMainWindow, self).__init__()
50
        uic.loadUi(ui_file, self)
51
        self.show()
52
53
class MagnetSettingsWindow(QtWidgets.QDialog):
54
    def __init__(self):
55
        # Get the path to the *.ui file
56
        this_dir = os.path.dirname(__file__)
57
        ui_file = os.path.join(this_dir, 'ui_magnet_settings.ui')
58
59
        # Load it
60
        super(MagnetSettingsWindow, self).__init__()
61
62
        uic.loadUi(ui_file, self)
63
64
65
class MagnetGui(GUIBase):
66
    """ Main GUI for the magnet. """
67
68
    _modclass = 'MagnetGui'
69
    _modtype = 'gui'
70
71
    ## declare connectors
72
    _in = {'magnetlogic1': 'MagnetLogic',
73
           'savelogic': 'SaveLogic'}
74
75
    def __init__(self, config, **kwargs):
76
        super().__init__(config=config, **kwargs)
77
78
        self.log.info('The following configuration was found.')
79
80
        # checking for the right configuration
81
        for key in config.keys():
82
            self.log.info('{0}: {1}'.format(key,config[key]))
83
84
        self._continue_2d_fluorescence_alignment = False
85
86
87
    def on_activate(self, e=None):
88
        """ Definition and initialisation of the GUI.
89
90
        @param object e: Fysom.event object from Fysom class.
91
                         An object created by the state machine module Fysom,
92
                         which is connected to a specific event (have a look in
93
                         the Base Class). This object contains the passed event,
94
                         the state before the event happened and the destination
95
                         of the state which should be reached after the event
96
                         had happened.
97
        """
98
        self._magnet_logic = self.connector['in']['magnetlogic1']['object']
99
        self._save_logic = self.connector['in']['savelogic']['object']
100
101
        self._mw = MagnetMainWindow()
102
103
        config = self.getConfiguration()
104
105
        # create all the needed control elements. They will manage the
106
        # connection with each other themselves. Note some buttons are also
107
        # connected within these functions because they have to be placed at
108
        # first in the GUI Layout, otherwise the signals will not react.
109
        self._create_axis_pos_disp()
110
        self._create_move_rel_control()
111
        self._create_move_abs_control()
112
113
        self._create_meas_type_RadioButtons()
114
115
        # Configuring the dock widgets
116
        # Use the class 'MagnetMainWindow' to create the GUI window
117
118
        axis_list = list(self._magnet_logic.get_hardware_constraints())
119
        self._mw.align_2d_axes0_name_ComboBox.clear()
120
        self._mw.align_2d_axes0_name_ComboBox.addItems(axis_list)
121
122
        self._mw.align_2d_axes1_name_ComboBox.clear()
123
        self._mw.align_2d_axes1_name_ComboBox.addItems(axis_list)
124
125
        # Setup dock widgets
126
        self._mw.centralwidget.hide()
127
        self._mw.setDockNestingEnabled(True)
128
       # self._mw.tabifyDockWidget(self._mw.curr_pos_DockWidget, self._mw.move_rel_DockWidget)
129
       # self._mw.tabifyDockWidget(self._mw.curr_pos_DockWidget, self._mw.move_abs_DockWidget)
130
       # self._mw.addDockWidget(QtCore.Qt.DockWidgetArea(1), self._mw.curr_pos_DockWidget)
131
       # self._mw.addDockWidget(QtCore.Qt.DockWidgetArea(2), self._mw.move_rel_DockWidget)
132
       # self._mw.addDockWidget(QtCore.Qt.DockWidgetArea(3), self._mw.move_abs_DockWidget)
133
        self.set_default_view_main_window()
134
135
        # After a movement command, the device should not block the program, at
136
        # least on the hardware level. That meant that the dll (or whatever
137
        # protocol is used to access the hardware) can receive a command during
138
        # an ongoing action. That is of course controller specific, but in
139
        # general should it should be possible (unless the controller was
140
        # written by someone who has no clue what he is doing). Eventually with
141
        # that you have the possibility of stopping an ongoing movement!
142
        self._interactive_mode = True
143
        self._activate_magnet_settings(e)
144
145
        # connect the actions of the toolbar:
146
        self._mw.magnet_settings_Action.triggered.connect(self.open_magnet_settings)
147
        self._mw.default_view_Action.triggered.connect(self.set_default_view_main_window)
148
149
        self.update_pos()
150
        self._magnet_logic.sigPosChanged.connect(self.update_pos)
151
152
        # Connect alignment GUI elements:
153
154
        self._magnet_logic.sigMeasurementFinished.connect(self._change_display_to_stop_2d_alignment)
155
156
157
158
        self._mw.align_2d_axes0_name_ComboBox.currentIndexChanged.connect(self._update_limits_axis0)
159
        self._mw.align_2d_axes1_name_ComboBox.currentIndexChanged.connect(self._update_limits_axis1)
160
        self._mw.align_2d_axis0_set_vel_CheckBox.stateChanged.connect(self._set_vel_display_axis0)
161
        self._mw.align_2d_axis1_set_vel_CheckBox.stateChanged.connect(self._set_vel_display_axis1)
162
163
164
        self._mw.alignment_2d_cb_min_centiles_DSpinBox.valueChanged.connect(self._update_2d_graph_data)
165
        self._mw.alignment_2d_cb_max_centiles_DSpinBox.valueChanged.connect(self._update_2d_graph_data)
166
        self._mw.alignment_2d_cb_low_centiles_DSpinBox.valueChanged.connect(self._update_2d_graph_data)
167
        self._mw.alignment_2d_cb_high_centiles_DSpinBox.valueChanged.connect(self._update_2d_graph_data)
168
169
        self._update_limits_axis0()
170
        self._update_limits_axis1()
171
        self._set_vel_display_axis0()
172
        self._set_vel_display_axis1()
173
174
        self._2d_alignment_ImageItem = pg.ImageItem(self._magnet_logic.get_2d_data_matrix())
175
        axis0, axis1 = self._magnet_logic.get_2d_axis_arrays()
176
        self._2d_alignment_ImageItem.setRect(QtCore.QRectF(axis0[0],
177
                                                           axis1[0],
178
                                                           axis0[-1]-axis0[0],
179
                                                           axis1[-1]-axis1[0],))
180
181
        self._mw.alignment_2d_GraphicsView.addItem(self._2d_alignment_ImageItem)
182
183
        # Get the colorscales at set LUT
184
        my_colors = ColorScaleInferno()
185
186
        self._2d_alignment_ImageItem.setLookupTable(my_colors.lut)
187
188
189
190
        # Configuration of Colorbar:
191
        self._2d_alignment_cb = ColorBar(my_colors.cmap_normed, 100, 0, 100000)
192
193
        self._mw.alignment_2d_cb_GraphicsView.addItem(self._2d_alignment_cb)
194
        self._mw.alignment_2d_cb_GraphicsView.hideAxis('bottom')
195
        self._mw.alignment_2d_cb_GraphicsView.hideAxis('left')
196
197
        self._mw.alignment_2d_cb_GraphicsView.addItem(self._2d_alignment_cb)
198
199
        if 'alignment_2d_cb_GraphicsView_text' in self._statusVariables:
200
            textlabel = self._statusVariables['alignment_2d_cb_GraphicsView_text']
201
202
        else:
203
            textlabel = 'Fluorescence'
204
205
        if 'alignment_2d_cb_GraphicsView_units' in self._statusVariables:
206
            units = self._statusVariables['alignment_2d_cb_GraphicsView_units']
207
        else:
208
            units = 'counts/s'
209
210
        self._mw.alignment_2d_cb_GraphicsView.setLabel('right', textlabel, units=units)
211
212
        #FIXME: save that in the logic
213
        if 'align_2d_axes0_range_DSpinBox' in self._statusVariables:
214
            self._mw.align_2d_axes0_range_DSpinBox.setValue(self._statusVariables['align_2d_axes0_range_DSpinBox'])
215
        if 'align_2d_axes0_step_DSpinBox' in self._statusVariables:
216
            self._mw.align_2d_axes0_step_DSpinBox.setValue(self._statusVariables['align_2d_axes0_step_DSpinBox'])
217
        if 'align_2d_axes0_vel_DSpinBox' in self._statusVariables:
218
            self._mw.align_2d_axes0_vel_DSpinBox.setValue(self._statusVariables['align_2d_axes0_vel_DSpinBox'])
219
        if 'align_2d_axes1_range_DSpinBox' in self._statusVariables:
220
            self._mw.align_2d_axes1_range_DSpinBox.setValue(self._statusVariables['align_2d_axes1_range_DSpinBox'])
221
        if 'align_2d_axes1_step_DSpinBox' in self._statusVariables:
222
            self._mw.align_2d_axes1_step_DSpinBox.setValue(self._statusVariables['align_2d_axes1_step_DSpinBox'])
223
        if 'align_2d_axes1_vel_DSpinBox' in self._statusVariables:
224
            self._mw.align_2d_axes1_vel_DSpinBox.setValue(self._statusVariables['align_2d_axes1_vel_DSpinBox'])
225
226
        #FIXME: that should be actually set in the logic
227
        if 'measurement_type' in self._statusVariables:
228
            self.measurement_type = self._statusVariables['measurement_type']
229
        else:
230
            self.measurement_type = 'fluorescence'
231
232
        self._magnet_logic.sig2DAxisChanged.connect(self._update_2d_graph_axis)
233
        self._magnet_logic.sig2DMatrixChanged.connect(self._update_2d_graph_data)
234
235
        # Connect the buttons and inputs for the odmr colorbar
236
        self._mw.alignment_2d_manual_RadioButton.clicked.connect(self._update_2d_graph_data)
237
        self._mw.alignment_2d_centiles_RadioButton.clicked.connect(self._update_2d_graph_data)
238
239
        self._update_2d_graph_data()
240
        self._update_2d_graph_cb()
241
242
243
        # Add save file tag input box
244
        self._mw.alignment_2d_nametag_LineEdit = QtWidgets.QLineEdit(self._mw)
245
        self._mw.alignment_2d_nametag_LineEdit.setMaximumWidth(200)
246
        self._mw.alignment_2d_nametag_LineEdit.setToolTip('Enter a nametag which will be\n'
247
                                                          'added to the filename.')
248
249
        self._mw.save_ToolBar.addWidget(self._mw.alignment_2d_nametag_LineEdit)
250
        self._mw.save_Action.triggered.connect(self.save_2d_plots_and_data)
251
252
        self._mw.run_stop_2d_alignment_Action.triggered.connect(self.run_stop_2d_alignment)
253
        self._mw.continue_2d_alignment_Action.triggered.connect(self.continue_stop_2d_alignment)
254
255
        # connect the signals:
256
        # --------------------
257
258
        # for fluorescence alignment:
259
        self._mw.align_2d_fluorescence_optimize_CheckBox.stateChanged.connect(self.optimize_pos_changed)
260
261
262
        # for odmr alignment:
263
        self._mw.meas_type_fluorescence_RadioButton.toggled.connect(self.set_measurement_type)
264
        self._mw.meas_type_odmr_RadioButton.toggled.connect(self.set_measurement_type)
265
        self._mw.meas_type_nuclear_spin_RadioButton.toggled.connect(self.set_measurement_type)
266
        self.set_measurement_type()
267
268
        # for odmr alignment:
269
        self._mw.align_2d_odmr_low_fit_func_ComboBox.clear()
270
        self._mw.align_2d_odmr_low_fit_func_ComboBox.addItems(self._magnet_logic.odmr_2d_low_fitfunction_list)
271
        self._mw.align_2d_odmr_low_fit_func_ComboBox.setCurrentIndex(1)
272
        self._mw.align_2d_odmr_low_center_freq_DSpinBox.setValue(self._magnet_logic.odmr_2d_low_center_freq)
273
        self._mw.align_2d_odmr_low_range_freq_DSpinBox.setValue(self._magnet_logic.odmr_2d_low_range_freq)
274
        self._mw.align_2d_odmr_low_step_freq_DSpinBox.setValue(self._magnet_logic.odmr_2d_low_step_freq)
275
        self._mw.align_2d_odmr_low_power_DSpinBox.setValue(self._magnet_logic.odmr_2d_low_power)
276
        self._mw.align_2d_odmr_low_runtime_DSpinBox.setValue(self._magnet_logic.odmr_2d_low_runtime)
277
278
        self._mw.align_2d_odmr_high_fit_func_ComboBox.clear()
279
        self._mw.align_2d_odmr_high_fit_func_ComboBox.addItems(self._magnet_logic.odmr_2d_high_fitfunction_list)
280
        self._mw.align_2d_odmr_high_fit_func_ComboBox.setCurrentIndex(1)
281
        self._mw.align_2d_odmr_high_center_freq_DSpinBox.setValue(self._magnet_logic.odmr_2d_high_center_freq)
282
        self._mw.align_2d_odmr_high_range_freq_DSpinBox.setValue(self._magnet_logic.odmr_2d_high_range_freq)
283
        self._mw.align_2d_odmr_high_step_freq_DSpinBox.setValue(self._magnet_logic.odmr_2d_high_step_freq)
284
        self._mw.align_2d_odmr_high_power_DSpinBox.setValue(self._magnet_logic.odmr_2d_high_power)
285
        self._mw.align_2d_odmr_high_runtime_DSpinBox.setValue(self._magnet_logic.odmr_2d_high_runtime)
286
287
        self._mw.align_2d_odmr_save_after_measure_CheckBox.setChecked(self._magnet_logic.odmr_2d_save_after_measure)
288
289
        self._mw.odmr_2d_single_trans_CheckBox.stateChanged.connect(self._odmr_single_trans_alignment_changed)
290
291
        # peak shift for odmr:
292
        self._mw.align_2d_axes0_shift_DSpinBox.setValue(self._magnet_logic.odmr_2d_peak_axis0_move_ratio/1e12)
293
        self._mw.align_2d_axes1_shift_DSpinBox.setValue(self._magnet_logic.odmr_2d_peak_axis1_move_ratio/1e12)
294
295
296
297
        # for single shot alignment of a nuclear spin:
298
        self._mw.align_2d_nuclear_rabi_periode_DSpinBox.setValue(self._magnet_logic.nuclear_2d_rabi_periode)
299
        self._mw.align_2d_nuclear_mw_freq_DSpinBox.setValue(self._magnet_logic.nuclear_2d_mw_freq)
300
        self._mw.align_2d_nuclear_mw_channel_SpinBox.setValue(self._magnet_logic.nuclear_2d_mw_channel)
301
        self._mw.align_2d_nuclear_mw_power_DSpinBox.setValue(self._magnet_logic.nuclear_2d_mw_power)
302
        self._mw.align_2d_nuclear_laser_time_DSpinBox.setValue(self._magnet_logic.nuclear_2d_laser_time)
303
        self._mw.align_2d_nuclear_laser_channel_SpinBox.setValue(self._magnet_logic.nuclear_2d_laser_channel)
304
        self._mw.align_2d_nuclear_detect_channel_SpinBox.setValue(self._magnet_logic.nuclear_2d_detect_channel)
305
        self._mw.align_2d_nuclear_idle_time_DSpinBox.setValue(self._magnet_logic.nuclear_2d_idle_time)
306
        self._mw.align_2d_nuclear_reps_within_ssr_SpinBox.setValue(self._magnet_logic.nuclear_2d_reps_within_ssr)
307
        self._mw.align_2d_nuclear_num_of_ssr_SpinBox.setValue(self._magnet_logic.nuclear_2d_num_ssr)
308
309
    def _activate_magnet_settings(self, e):
310
        """ Activate magnet settings.
311
312
        @param object e: Fysom.event object from Fysom class. A more detailed
313
                         explanation can be found in the method initUI.
314
        """
315
        self._ms = MagnetSettingsWindow()
316
        # default config is normal_mode
317
        self._ms.normal_mode_checkBox.setChecked(True)
318
        self._ms.z_mode_checkBox.setChecked(False)
319
        # make sure the buttons are exclusively checked
320
        self._ms.normal_mode_checkBox.stateChanged.connect(self.trig_wrapper_normal_mode)
321
        self._ms.z_mode_checkBox.stateChanged.connect(self.trig_wrapper_z_mode)
322
323
        #self._ms.z_mode_checkBox.stateChanged.connect(self._ms.normal_mode_checkBox.toggle)
324
        self._ms.accepted.connect(self.update_magnet_settings)
325
        self._ms.rejected.connect(self.keep_former_magnet_settings)
326
        self._ms.ButtonBox.button(QtWidgets.QDialogButtonBox.Apply).clicked.connect(self.update_magnet_settings)
327
328
        self.keep_former_magnet_settings()
329
330
    def trig_wrapper_normal_mode(self):
331
        if not self._ms.normal_mode_checkBox.isChecked() and not self._ms.z_mode_checkBox.isChecked():
332
            self._ms.z_mode_checkBox.toggle()
333
        elif self._ms.normal_mode_checkBox.isChecked() and self._ms.z_mode_checkBox.isChecked():
334
            self._ms.z_mode_checkBox.toggle()
335
336
    def trig_wrapper_z_mode(self):
337
        if not self._ms.normal_mode_checkBox.isChecked() and not self._ms.z_mode_checkBox.isChecked():
338
            self._ms.normal_mode_checkBox.toggle()
339
        elif self._ms.normal_mode_checkBox.isChecked() and self._ms.z_mode_checkBox.isChecked():
340
            self._ms.normal_mode_checkBox.toggle()
341
342
343
    def on_deactivate(self, e=None):
344
        """ Deactivate the module properly.
345
346
        @param object e: Fysom.event object from Fysom class. A more detailed
347
                         explanation can be found in the method initUI.
348
        """
349
        self._statusVariables['measurement_type'] = self.measurement_type
350
        self._statusVariables['alignment_2d_cb_GraphicsView_text'] =  self._mw.alignment_2d_cb_GraphicsView.plotItem.axes['right']['item'].labelText
351
        self._statusVariables['alignment_2d_cb_GraphicsView_units'] =  self._mw.alignment_2d_cb_GraphicsView.plotItem.axes['right']['item'].labelUnits
352
353
        #FIXME: save that in the logic
354
        self._statusVariables['align_2d_axes0_range_DSpinBox'] = self._mw.align_2d_axes0_range_DSpinBox.value()
355
        self._statusVariables['align_2d_axes0_step_DSpinBox'] = self._mw.align_2d_axes0_step_DSpinBox.value()
356
        self._statusVariables['align_2d_axes0_vel_DSpinBox'] = self._mw.align_2d_axes0_vel_DSpinBox.value()
357
        self._statusVariables['align_2d_axes1_range_DSpinBox'] = self._mw.align_2d_axes1_range_DSpinBox.value()
358
        self._statusVariables['align_2d_axes1_step_DSpinBox'] = self._mw.align_2d_axes1_step_DSpinBox.value()
359
        self._statusVariables['align_2d_axes1_vel_DSpinBox'] = self._mw.align_2d_axes1_vel_DSpinBox.value()
360
361
        self._mw.close()
362
363
    def show(self):
364
        """Make window visible and put it above all other windows. """
365
        QtWidgets.QMainWindow.show(self._mw)
366
        self._mw.activateWindow()
367
        self._mw.raise_()
368
369
370
    def set_default_view_main_window(self):
371
        """ Establish the default dock Widget configuration. """
372
373
        # connect all widgets to the main Window
374
        self._mw.curr_pos_DockWidget.setFloating(False)
375
        self._mw.move_rel_DockWidget.setFloating(False)
376
        self._mw.move_abs_DockWidget.setFloating(False)
377
        self._mw.alignment_DockWidget.setFloating(False)
378
379
        # QtCore.Qt.LeftDockWidgetArea        0x1
380
        # QtCore.Qt.RightDockWidgetArea       0x2
381
        # QtCore.Qt.TopDockWidgetArea         0x4
382
        # QtCore.Qt.BottomDockWidgetArea      0x8
383
        # QtCore.Qt.AllDockWidgetAreas        DockWidgetArea_Mask
384
        # QtCore.Qt.NoDockWidgetArea          0
385
386
        # align the widget
387
        self._mw.addDockWidget(QtCore.Qt.DockWidgetArea(1),
388
                               self._mw.curr_pos_DockWidget)
389
        self._mw.addDockWidget(QtCore.Qt.DockWidgetArea(1),
390
                               self._mw.move_rel_DockWidget)
391
        self._mw.addDockWidget(QtCore.Qt.DockWidgetArea(1),
392
                               self._mw.move_abs_DockWidget)
393
394
        self._mw.addDockWidget(QtCore.Qt.DockWidgetArea(2),
395
                               self._mw.alignment_DockWidget)
396
397
    def open_magnet_settings(self):
398
        """ This method opens the settings menu. """
399
        self._ms.exec_()
400
401
    def update_magnet_settings(self):
402
        """ Apply the set configuration in the Settings Window. """
403
404
        if self._ms.interactive_mode_CheckBox.isChecked():
405
            self._interactive_mode = True
406
        else:
407
            self._interactive_mode = False
408
409
        if self._ms.interactive_mode_CheckBox.isChecked():
410
            self._interactive_mode = True
411
        else:
412
            self._interactive_mode = False
413
        if self._ms.z_mode_checkBox.isChecked():
414
            self._z_mode = True
415
            self._magnet_logic._magnet_device.mode = 'z_mode'
416
        else:
417
            self._z_mode = False
418
            self._magnet_logic._magnet_device.mode = 'normal_mode'
419
420
        if self._ms.normal_mode_checkBox.isChecked():
421
            self._normal_mode = True
422
            self._magnet_logic._magnet_device.mode = 'normal_mode'
423
        else:
424
            self._normal_mode = False
425
            self._magnet_logic._magnet_device.mode = 'z_mode'
426
427
    def keep_former_magnet_settings(self):
428
429
        self._ms.interactive_mode_CheckBox.setChecked(self._interactive_mode)
430
431
    def _create_meas_type_RadioButtons(self):
432
        """ Create the measurement Buttons for the desired measurements:
433
434
        @return:
435
        """
436
437
        self._mw.alignment_2d_ButtonGroup = QtWidgets.QButtonGroup(self._mw)
438
439
        self._mw.meas_type_fluorescence_RadioButton = QtWidgets.QRadioButton(parent=self._mw)
440
        self._mw.alignment_2d_ButtonGroup.addButton(self._mw.meas_type_fluorescence_RadioButton)
441
        self._mw.alignment_2d_ToolBar.addWidget(self._mw.meas_type_fluorescence_RadioButton)
442
        self._mw.meas_type_fluorescence_RadioButton.setText('Fluorescence')
443
444
        self._mw.meas_type_odmr_RadioButton = QtWidgets.QRadioButton(parent=self._mw)
445
        self._mw.alignment_2d_ButtonGroup.addButton(self._mw.meas_type_odmr_RadioButton)
446
        self._mw.alignment_2d_ToolBar.addWidget(self._mw.meas_type_odmr_RadioButton)
447
        self._mw.meas_type_odmr_RadioButton.setText('ODMR')
448
449
        self._mw.meas_type_nuclear_spin_RadioButton = QtWidgets.QRadioButton(parent=self._mw)
450
        self._mw.alignment_2d_ButtonGroup.addButton(self._mw.meas_type_nuclear_spin_RadioButton)
451
        self._mw.alignment_2d_ToolBar.addWidget(self._mw.meas_type_nuclear_spin_RadioButton)
452
        self._mw.meas_type_nuclear_spin_RadioButton.setText('Nuclear Spin')
453
454
        self._mw.meas_type_fluorescence_RadioButton.setChecked(True)
455
456
457
    def _create_axis_pos_disp(self):
458
        """ Create the axis position display.
459
460
        The generic variable name for a created QLable is:
461
            curr_pos_axis{0}_Label
462
        The generic variable name for a created ScienDSpinBox is:
463
            curr_pos_axis{0}_ScienDSpinBox
464
        where in {0} the name of the axis will be inserted.
465
466
        DO NOT CALL THESE VARIABLES DIRECTLY! USE THE DEDICATED METHOD INSTEAD!
467
        Use the method get_ref_curr_pos_ScienDSpinBox with the appropriated
468
        label, otherwise you will break the generality.
469
        """
470
471
        constraints = self._magnet_logic.get_hardware_constraints()
472
473
        # set the parameters in the curr_pos_DockWidget:
474
        for index, axis_label in enumerate(constraints):
475
476
            # Set the QLabel according to the grid
477
            # this is the name prototype for the label of current position display
478
            label_var_name = 'curr_pos_axis{0}_Label'.format(axis_label)
479
            setattr(self._mw, label_var_name, QtWidgets.QLabel(self._mw.curr_pos_DockWidgetContents))
480
            label_var = getattr(self._mw, label_var_name)
481
            label_var.setObjectName(label_var_name)
482
            label_var.setText('{0}'.format(axis_label))
483
            self._mw.curr_pos_GridLayout.addWidget(label_var, index, 0, 1, 1)
484
485
            # Set the ScienDSpinBox according to the grid
486
            # this is the name prototype for the current position display
487
            dspinbox_ref_name = 'curr_pos_axis{0}_ScienDSpinBox'.format(axis_label)
488
489
            setattr(self._mw, dspinbox_ref_name, ScienDSpinBox(parent=self._mw.curr_pos_DockWidgetContents))
490
            dspinbox_ref = getattr(self._mw, dspinbox_ref_name)
491
            dspinbox_ref.setObjectName(dspinbox_ref_name)
492
            dspinbox_ref.setReadOnly(True)
493
            dspinbox_ref.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)
494
            dspinbox_ref.setMaximum(np.inf)
495
            dspinbox_ref.setMinimum(-np.inf)
496
497
            # in the ScienDSpinBox the decimals are actually the number of
498
            # significant digits, therefore set them here by default:
499
            dspinbox_ref.setDecimals(5)
500
            dspinbox_ref.setOpts(minStep=constraints[axis_label]['pos_step'])
501
            dspinbox_ref.setSingleStep(0.001)
502
            dspinbox_ref.setSuffix(constraints[axis_label]['unit'])
503
504
            self._mw.curr_pos_GridLayout.addWidget(dspinbox_ref, index, 1, 1, 1)
505
506
        extension = len(constraints)
507
        self._mw.curr_pos_GridLayout.addWidget(self._mw.curr_pos_get_pos_PushButton, 0, 2, extension, 1)
508
        self._mw.curr_pos_GridLayout.addWidget(self._mw.curr_pos_stop_PushButton, 0, 3, extension, 1)
509
        self._mw.curr_pos_get_pos_PushButton.clicked.connect(self.update_pos)
510
        self._mw.curr_pos_stop_PushButton.clicked.connect(self.stop_movement)
511
512
    def _create_move_rel_control(self):
513
        """ Create all the gui elements to control a relative movement.
514
515
        The generic variable name for a created QLable is:
516
            move_rel_axis_{0}_Label
517
        The generic variable name for a created ScienDSpinBox is:
518
            move_rel_axis_{0}_ScienDSpinBox
519
        The generic variable name for a created QPushButton in negative dir is:
520
            move_rel_axis_{0}_m_PushButton
521
        The generic variable name for a created QPushButton in positive dir is:
522
            move_rel_axis_{0}_p_PushButton
523
524
        DO NOT CALL THESE VARIABLES DIRECTLY! USE THE DEDICATED METHOD INSTEAD!
525
        Use the method get_ref_move_rel_ScienDSpinBox with the appropriated
526
        label, otherwise you will break the generality.
527
        """
528
529
        constraints = self._magnet_logic.get_hardware_constraints()
530
531
        # set the axis_labels in the curr_pos_DockWidget:
532
        for index, axis_label in enumerate(constraints):
533
534
            label_var_name = 'move_rel_axis_{0}_Label'.format(axis_label)
535
            setattr(self._mw, label_var_name, QtWidgets.QLabel(self._mw.move_rel_DockWidgetContents))
536
            label_var = getattr(self._mw, label_var_name) # get the reference
537
            label_var.setObjectName(label_var_name) # set axis_label for the label
538
            label_var.setText('{0}'.format(axis_label))
539
            # add the label to the grid:
540
            self._mw.move_rel_GridLayout.addWidget(label_var, index, 0, 1, 1)
541
542
            # Set the ScienDSpinBox according to the grid
543
            # this is the name prototype for the relative movement display
544
            dspinbox_ref_name = 'move_rel_axis_{0}_ScienDSpinBox'.format(axis_label)
545
            setattr(self._mw, dspinbox_ref_name, ScienDSpinBox(parent=self._mw.move_rel_DockWidgetContents))
546
            dspinbox_ref = getattr(self._mw, dspinbox_ref_name)
547
            dspinbox_ref.setObjectName(dspinbox_ref_name)
548
#            dspinbox_ref.setButtonSymbols(QtGui.QAbstractSpinBox.NoButtons)
549
550
            dspinbox_ref.setMaximum(constraints[axis_label]['pos_max'])
551
            dspinbox_ref.setMinimum(constraints[axis_label]['pos_min'])
552
553
            # in the ScienDSpinBox the decimals are actually the number of
554
            # significant digits, therefore set them here by default:
555
            dspinbox_ref.setDecimals(5)
556
            dspinbox_ref.setOpts(minStep=constraints[axis_label]['pos_step'])
557
            dspinbox_ref.setSingleStep(0.001)
558
            dspinbox_ref.setSuffix(constraints[axis_label]['unit'])
559
560
            self._mw.move_rel_GridLayout.addWidget(dspinbox_ref, index, 1, 1, 1)
561
562
563
            # this is the name prototype for the relative movement minus button
564
            func_name = 'move_rel_axis_{0}_m'.format(axis_label)
565
            # create a method and assign it as attribute:
566
            setattr(self, func_name, self._function_builder_move_rel(func_name,axis_label,-1) )
567
            move_rel_m_ref =  getattr(self, func_name)  # get the reference
568
569
            # the change of the PushButton is connected to the previous method.
570
            button_var_name = 'move_rel_axis_{0}_m_PushButton'.format(axis_label)
571
            setattr(self._mw, button_var_name, QtWidgets.QPushButton(self._mw.move_rel_DockWidgetContents))
572
            button_var = getattr(self._mw, button_var_name)
573
            button_var.setObjectName(button_var_name)
574
            button_var.setText('-')
575
            button_var.clicked.connect(move_rel_m_ref, type=QtCore.Qt.QueuedConnection)
576
            self._mw.move_rel_GridLayout.addWidget(button_var, index, 2, 1, 1)
577
578
            # this is the name prototype for the relative movement plus button
579
            func_name = 'move_rel_axis_{0}_p'.format(axis_label)
580
            setattr(self, func_name, self._function_builder_move_rel(func_name,axis_label,1) )
581
            move_rel_p_ref = getattr(self, func_name)
582
583
            # the change of the PushButton is connected to the previous method.
584
            button_var_name = 'move_rel_axis_{0}_p_PushButton'.format(axis_label)
585
            setattr(self._mw, button_var_name, QtWidgets.QPushButton(self._mw.move_rel_DockWidgetContents))
586
            button_var = getattr(self._mw, button_var_name)
587
            button_var.setObjectName(button_var_name)
588
            button_var.setText('+')
589
            button_var.clicked.connect(move_rel_p_ref, type=QtCore.Qt.QueuedConnection)
590
            self._mw.move_rel_GridLayout.addWidget(button_var, index, 3, 1, 1)
591
592
    def _create_move_abs_control(self):
593
        """ Create all the GUI elements to control a relative movement.
594
595
        The generic variable name for a created QLable is:
596
            move_abs_axis_{0}_Label
597
        The generic variable name for a created QLable is:
598
            move_abs_axis_{0}_Slider
599
        The generic variable name for a created ScienDSpinBox is:
600
            move_abs_axis_{0}_ScienDSpinBox
601
        The generic variable name for a created QPushButton for move is:
602
            move_abs_PushButton
603
604
        These methods should not be called:
605
        The generic variable name for a update method for the ScienDSpinBox:
606
            _update_move_abs_{0}_dspinbox
607
        The generic variable name for a update method for the QSlider:
608
            _update_move_abs_{0}_slider
609
610
        DO NOT CALL THESE VARIABLES DIRECTLY! USE THE DEDICATED METHOD INSTEAD!
611
        Use the method get_ref_move_abs_ScienDSpinBox with the appropriated
612
        label, otherwise you will break the generality.
613
        """
614
615
        constraints = self._magnet_logic.get_hardware_constraints()
616
617
        for index, axis_label in enumerate(constraints):
618
619
            label_var_name = 'move_abs_axis_{0}_Label'.format(axis_label)
620
            setattr(self._mw, label_var_name, QtWidgets.QLabel(self._mw.move_abs_DockWidgetContents))
621
            label_var = getattr(self._mw, label_var_name) # get the reference
622
            # set axis_label for the label:
623
            label_var.setObjectName(label_var_name)
624
            label_var.setText(axis_label)
625
626
            # make the steps of the splider as a multiple of 10
627
            # smallest_step_slider = 10**int(np.log10(constraints[axis_label]['pos_step']) -1)
628
            smallest_step_slider = constraints[axis_label]['pos_step']
629
630
            # add the label to the grid:
631
            self._mw.move_abs_GridLayout.addWidget(label_var, index, 0, 1, 1)
632
633
            # Set the ScienDSpinBox according to the grid
634
            # this is the name prototype for the relative movement display
635
            slider_obj_name = 'move_abs_axis_{0}_Slider'.format(axis_label)
636
            setattr(self._mw, slider_obj_name, QtWidgets.QSlider(self._mw.move_abs_DockWidgetContents))
637
            slider_obj = getattr(self._mw, slider_obj_name)
638
            slider_obj.setObjectName(slider_obj_name)
639
            slider_obj.setOrientation(QtCore.Qt.Horizontal)
640
#            dspinbox_ref.setButtonSymbols(QtGui.QAbstractSpinBox.NoButtons)
641
642
            max_val = abs(constraints[axis_label]['pos_max'] - constraints[axis_label]['pos_min'])
643
644
            # set the step size of the slider to a fixed resolution, that
645
            # prevents really ugly rounding error behaviours in display.
646
            # Set precision to nanometer scale, which is actually never reached.
647
            max_steps = int(max_val/smallest_step_slider)
648
649
650
            slider_obj.setMaximum(max_steps)
651
            slider_obj.setMinimum(0)
652
            #TODO: set the decimals also from the constraints!
653
#            slider_obj.setDecimals(3)
654
            slider_obj.setSingleStep(1)
655
            # slider_obj.setEnabled(False)
656
657
            self._mw.move_abs_GridLayout.addWidget(slider_obj, index, 1, 1, 1)
658
659
            # Set the ScienDSpinBox according to the grid
660
            # this is the name prototype for the relative movement display
661
            dspinbox_ref_name = 'move_abs_axis_{0}_ScienDSpinBox'.format(axis_label)
662
            setattr(self._mw, dspinbox_ref_name, ScienDSpinBox(parent=self._mw.move_abs_DockWidgetContents))
663
            dspinbox_ref = getattr(self._mw, dspinbox_ref_name)
664
            dspinbox_ref.setObjectName(dspinbox_ref_name)
665
#            dspinbox_ref.setButtonSymbols(QtGui.QAbstractSpinBox.NoButtons)
666
667
            dspinbox_ref.setMaximum(constraints[axis_label]['pos_max'])
668
            dspinbox_ref.setMinimum(constraints[axis_label]['pos_min'])
669
670
            # in the ScienDSpinBox the decimals are actually the number of
671
            # significant digits, therefore set them here by default:
672
            dspinbox_ref.setDecimals(5)
673
            dspinbox_ref.setOpts(minStep=constraints[axis_label]['pos_step'])
674
            dspinbox_ref.setSingleStep(0.001)
675
            dspinbox_ref.setSuffix(constraints[axis_label]['unit'])
676
677
            # set the horizontal size to 100 pixel:
678
            dspinbox_ref.setMaximumSize(QtCore.QSize(80, 16777215))
679
680
            self._mw.move_abs_GridLayout.addWidget(dspinbox_ref, index, 2, 1, 1)
681
682
            # build a function to change the dspinbox value and connect a
683
            # slidermove event to it:
684
            func_name = '_update_move_abs_{0}_dspinbox'.format(axis_label)
685
            setattr(self, func_name, self._function_builder_update_viewbox(func_name, axis_label, dspinbox_ref))
686
            update_func_dspinbox_ref = getattr(self, func_name)
687
            slider_obj.valueChanged.connect(update_func_dspinbox_ref)
688
689
            # build a function to change the slider value and connect a
690
            # spinbox value change event to it:
691
            func_name = '_update_move_abs_{0}_slider'.format(axis_label)
692
            setattr(self, func_name, self._function_builder_update_slider(func_name, axis_label, slider_obj))
693
            update_func_slider_ref = getattr(self, func_name)
694
            # dspinbox_ref.valueChanged.connect(update_func_slider_ref)
695
696
            # the editingFinished idea has to be implemented properly at first:
697
            dspinbox_ref.editingFinished.connect(update_func_slider_ref)
698
699
        extension = len(constraints)
700
        self._mw.move_abs_GridLayout.addWidget(self._mw.move_abs_PushButton, 0, 3, extension, 1)
701
        self._mw.move_abs_PushButton.clicked.connect(self.move_abs)
702
703
    def _function_builder_move_rel(self, func_name, axis_label, direction):
704
        """ Create a function/method, which gots executed for pressing move_rel.
705
706
        @param str func_name: name how the function should be called.
707
        @param str axis_label: label of the axis you want to create a control
708
                               function for.
709
        @param int direction: either 1 or -1 depending on the relative movement.
710
        @return: function with name func_name
711
712
        A routine to construct a method on the fly and attach it as attribute
713
        to the object, so that it can be used or so that other signals can be
714
        connected to it. That means the return value is already fixed for a
715
        function name.
716
        """
717
718
        def func_dummy_name():
719
            self.move_rel(axis_label, direction)
720
721
        func_dummy_name.__name__ = func_name
722
        return func_dummy_name
723
724
        # create the signals for the push buttons and connect them to the move
725
        # rel method in the Logic
726
727
    def _function_builder_update_viewbox(self, func_name, axis_label,
728
                                         ref_dspinbox):
729
        """ Create a function/method, which gots executed for pressing move_rel.
730
731
        @param str func_name: name how the function should be called.
732
        @param str axis_label: label of the axis you want to create a control
733
                               function for.
734
        @param object ref_dspinbox: a reference to the dspinbox object, which
735
                                    will actually apply the changed within the
736
                                    created method.
737
738
        @return: function with name func_name
739
740
        A routine to construct a method on the fly and attach it as attribute
741
        to the object, so that it can be used or so that other signals can be
742
        connected to it. The connection of a signal to this method must appear
743
        outside of the present function.
744
        """
745
746
        def func_dummy_name(slider_val):
747
            """
748
            @param int slider_val: The current value of the slider, will be an
749
                                   integer value between
750
                                       [0,(pos_max - pos_min)/pos_step]
751
                                   of the corresponding axis label.
752
                                   Now convert this value back to a viewbox
753
                                   value like:
754
                                       pos_min + slider_step*pos_step
755
            """
756
757
            constraints = self._magnet_logic.get_hardware_constraints()
758
            # set the resolution of the slider to nanometer precision, that is
759
            # better for the display behaviour. In the end, that will just make
760
            # everything smoother but not actually affect the displayed number:
761
762
            # max_step_slider = 10**int(np.log10(constraints[axis_label]['pos_step']) -1)
763
            max_step_slider = constraints[axis_label]['pos_step']
764
765
            actual_pos = (constraints[axis_label]['pos_min'] + slider_val * max_step_slider)
766
            ref_dspinbox.setValue(actual_pos)
767
768
        func_dummy_name.__name__ = func_name
769
        return func_dummy_name
770
771
    def _function_builder_update_slider(self, func_name, axis_label, ref_slider):
772
        """ Create a function/method, which gots executed for pressing move_rel.
773
774
        Create a function/method, which gots executed for pressing move_rel.
775
776
        @param str func_name: name how the function should be called.
777
        @param str axis_label: label of the axis you want to create a control
778
                               function for.
779
        @param object ref_slider: a reference to the slider object, which
780
                                  will actually apply the changed within the
781
                                  created method.
782
783
        @return: function with name func_name
784
785
        A routine to construct a method on the fly and attach it as attribute
786
        to the object, so that it can be used or so that other signals can be
787
        connected to it. The connection of a signal to this method must appear
788
        outside of the present function.
789
        """
790
791
        def func_dummy_name():
792
            """
793
            @param int slider_step: The current value of the slider, will be an
794
                                    integer value between
795
                                        [0,(pos_max - pos_min)/pos_step]
796
                                    of the corresponding axis label.
797
                                    Now convert this value back to a viewbox
798
                                    value like:
799
                                        pos_min + slider_step*pos_step
800
            """
801
802
            dspinbox_obj = self.get_ref_move_abs_ScienDSpinBox(axis_label)
803
            viewbox_val = dspinbox_obj.value()
804
805
            constraints = self._magnet_logic.get_hardware_constraints()
806
            # set the resolution of the slider to nanometer precision, that is
807
            # better for the display behaviour. In the end, that will just make
808
            # everything smoother but not actually affect the displayed number:
809
810
            # max_step_slider = 10**int(np.log10(constraints[axis_label]['pos_step']) -1)
811
            max_step_slider = constraints[axis_label]['pos_step']
812
813
            slider_val = abs(viewbox_val - constraints[axis_label]['pos_min'])/max_step_slider
814
            ref_slider.setValue(slider_val)
815
816
        func_dummy_name.__name__ = func_name
817
        return func_dummy_name
818
819
        # create the signals for the push buttons and connect them to the move
820
        # rel method in the Logic
821
822
    def move_rel(self, axis_label, direction):
823
        """ Move relative by the axis with given label an direction.
824
825
        @param str axis_label: tells which axis should move.
826
        @param int direction: either 1 or -1 depending on the relative movement.
827
828
        That method get called from methods, which are created on the fly at
829
        runtime during the activation of that module (basically from the
830
        methods with the generic name move_rel_axis_{0}_p or
831
        move_rel_axis_{0}_m with the appropriate label).
832
        """
833
        constraints = self._magnet_logic.get_hardware_constraints()
834
        dspinbox = self.get_ref_move_rel_ScienDSpinBox(axis_label)
835
836
        movement = dspinbox.value() * direction
837
838
        self._magnet_logic.move_rel({axis_label: movement})
839
        # if self._interactive_mode:
840
        #     self.update_pos()
841
842
    def move_abs(self, param_dict=None):
843
        """ Perform an absolute movement.
844
845
        @param param_dict: with {<axis_label>:<position>}, can of course
846
                           contain many entries of the same kind.
847
848
        Basically all the axis can be controlled at the same time.
849
        """
850
851
        if (param_dict is not None) and (type(param_dict) is not bool):
852
            self._magnet_logic.move_abs(param_dict)
853
        else:
854
            constraints = self._magnet_logic.get_hardware_constraints()
855
856
            # create the move_abs dict
857
            move_abs = {}
858
            for label in constraints:
859
                move_abs[label] = self.get_ref_move_abs_ScienDSpinBox(label).value()
860
861
            self._magnet_logic.move_abs(move_abs)
862
863
        # if self._interactive_mode:
864
        #     self.update_pos()
865
866
867
    def get_ref_curr_pos_ScienDSpinBox(self, label):
868
        """ Get the reference to the double spin box for the passed label. """
869
870
        dspinbox_name = 'curr_pos_axis{0}_ScienDSpinBox'.format(label)
871
        dspinbox_ref = getattr(self._mw, dspinbox_name)
872
        return dspinbox_ref
873
874
    def get_ref_move_rel_ScienDSpinBox(self, label):
875
        """ Get the reference to the double spin box for the passed label. """
876
877
        dspinbox_name = 'move_rel_axis_{0}_ScienDSpinBox'.format(label)
878
        dspinbox_ref = getattr(self._mw, dspinbox_name)
879
        return dspinbox_ref
880
881
    def get_ref_move_abs_ScienDSpinBox(self, label):
882
        """ Get the reference to the double spin box for the passed label. """
883
884
        dspinbox_name = 'move_abs_axis_{0}_ScienDSpinBox'.format(label)
885
        dspinbox_ref = getattr(self._mw, dspinbox_name)
886
        return dspinbox_ref
887
888
    def get_ref_move_abs_Slider(self, label):
889
        """ Get the reference to the slider for the passed label. """
890
891
        slider_name = 'move_abs_axis_{0}_Slider'.format(label)
892
        slider_ref = getattr(self._mw, slider_name)
893
        return slider_ref
894
895
    def optimize_pos_changed(self):
896
        """ Set whether postition should be optimized at each point. """
897
898
        state = self._mw.align_2d_fluorescence_optimize_CheckBox.isChecked()
899
        self._magnet_logic.set_optimize_pos(state)
900
901
    def stop_movement(self):
902
        """ Invokes an immediate stop of the hardware.
903
904
        MAKE SURE THAT THE HARDWARE CAN BE CALLED DURING AN ACTION!
905
        If the parameter _interactive_mode is set to False no stop can be done
906
        since the device would anyway not respond to a method call.
907
        """
908
909
        if self._interactive_mode:
910
            self._magnet_logic.stop_movement()
911
        else:
912
            self.log.warning('Movement cannot be stopped during a movement '
913
                    'anyway! Set the interactive mode to True in the Magnet '
914
                    'Settings! Otherwise this method is useless.')
915
916
    def update_pos(self, param_list=None):
917
        """ Update the current position.
918
919
        @param list param_list: optional, if specific positions needed to be
920
                                updated.
921
922
        If no value is passed, the current position is retrieved from the
923
        logic and the display is changed.
924
        """
925
        constraints = self._magnet_logic.get_hardware_constraints()
926
        curr_pos =  self._magnet_logic.get_pos()
927
928
        if (param_list is not None) and (type(param_list) is not bool):
929
            param_list = list(param_list)
930
            # param_list =list(param_list) # convert for safety to a list
931
            curr_pos =  self._magnet_logic.get_pos(param_list)
932
933
        for axis_label in curr_pos:
934
            # update the values of the current position viewboxes:
935
            dspinbox_pos_ref = self.get_ref_curr_pos_ScienDSpinBox(axis_label)
936
937
            dspinbox_pos_ref.setValue(curr_pos[axis_label])
938
939
            # update the values also of the absolute movement display:
940
            dspinbox_move_abs_ref = self.get_ref_move_abs_ScienDSpinBox(axis_label)
941
            dspinbox_move_abs_ref.setValue(curr_pos[axis_label])
942
943
944
    def run_stop_2d_alignment(self, is_checked):
945
        """ Manage what happens if 2d magnet scan is started/stopped
946
947
        @param bool is_checked: state if the current scan, True = started,
948
                                False = stopped
949
        """
950
951
        if is_checked:
952
            self.start_2d_alignment_clicked()
953
954
        else:
955
            self.abort_2d_alignment_clicked()
956
957
    def _change_display_to_stop_2d_alignment(self):
958
        """ Changes every display component back to the stopped state. """
959
960
        self._mw.run_stop_2d_alignment_Action.blockSignals(True)
961
        self._mw.run_stop_2d_alignment_Action.setChecked(False)
962
963
        self._mw.continue_2d_alignment_Action.blockSignals(True)
964
        self._mw.continue_2d_alignment_Action.setChecked(False)
965
966
        self._mw.run_stop_2d_alignment_Action.blockSignals(False)
967
        self._mw.continue_2d_alignment_Action.blockSignals(False)
968
969
    def start_2d_alignment_clicked(self):
970
        """ Start the 2d alignment. """
971
972
        if self.measurement_type == '2d_fluorescence':
973
            self._magnet_logic.curr_alignment_method = self.measurement_type
974
975
            self._magnet_logic.fluorescence_integration_time = self._mw.align_2d_fluorescence_integrationtime_DSpinBox.value()
976
977
            self._mw.alignment_2d_cb_GraphicsView.setLabel('right', 'Fluorescence', units='c/s')
978
979
        elif self.measurement_type == '2d_odmr':
980
            self._magnet_logic.curr_alignment_method = self.measurement_type
981
982
            self._magnet_logic.odmr_2d_low_center_freq = self._mw.align_2d_odmr_low_center_freq_DSpinBox.value()
983
            self._magnet_logic.odmr_2d_low_range_freq = self._mw.align_2d_odmr_low_range_freq_DSpinBox.value()
984
            self._magnet_logic.odmr_2d_low_step_freq = self._mw.align_2d_odmr_low_step_freq_DSpinBox.value()
985
            self._magnet_logic.odmr_2d_low_power = self._mw.align_2d_odmr_low_power_DSpinBox.value()
986
            self._magnet_logic.odmr_2d_low_runtime  = self._mw.align_2d_odmr_low_runtime_DSpinBox.value()
987
            self._magnet_logic.odmr_2d_low_fitfunction = self._mw.align_2d_odmr_low_fit_func_ComboBox.currentText()
988
989
            self._magnet_logic.odmr_2d_high_center_freq = self._mw.align_2d_odmr_high_center_freq_DSpinBox.value()
990
            self._magnet_logic.odmr_2d_high_range_freq = self._mw.align_2d_odmr_high_range_freq_DSpinBox.value()
991
            self._magnet_logic.odmr_2d_high_step_freq = self._mw.align_2d_odmr_high_step_freq_DSpinBox.value()
992
            self._magnet_logic.odmr_2d_high_power = self._mw.align_2d_odmr_high_power_DSpinBox.value()
993
            self._magnet_logic.odmr_2d_high_runtime = self._mw.align_2d_odmr_high_runtime_DSpinBox.value()
994
            self._magnet_logic.odmr_2d_high_fitfunction = self._mw.align_2d_odmr_high_fit_func_ComboBox.currentText()
995
996
            self._magnet_logic.odmr_2d_peak_axis0_move_ratio = self._mw.align_2d_axes0_shift_DSpinBox.value()*1e12
997
            self._magnet_logic.odmr_2d_peak_axis1_move_ratio = self._mw.align_2d_axes1_shift_DSpinBox.value()*1e12
998
999
            self._magnet_logic.odmr_2d_single_trans = self._mw.odmr_2d_single_trans_CheckBox.isChecked()
1000
1001
            if self._mw.odmr_2d_single_trans_CheckBox.isChecked():
1002
                self._mw.alignment_2d_cb_GraphicsView.setLabel('right', 'ODMR transition contrast', units='%')
1003
            else:
1004
                self._mw.alignment_2d_cb_GraphicsView.setLabel('right', 'Half ODMR splitting', units='Hz')
1005
1006
        elif self.measurement_type == '2d_nuclear':
1007
            self._magnet_logic.curr_alignment_method = self.measurement_type
1008
1009
            # ODMR stuff:
1010
            self._magnet_logic.odmr_2d_low_center_freq = self._mw.align_2d_odmr_low_center_freq_DSpinBox.value()*1e6
1011
            self._magnet_logic.odmr_2d_low_step_freq = self._mw.align_2d_odmr_low_step_freq_DSpinBox.value()*1e6
1012
            self._magnet_logic.odmr_2d_low_range_freq = self._mw.align_2d_odmr_low_range_freq_DSpinBox.value()*1e6
1013
            self._magnet_logic.odmr_2d_low_power = self._mw.align_2d_odmr_low_power_DSpinBox.value()
1014
            self._magnet_logic.odmr_2d_low_runtime  = self._mw.align_2d_odmr_low_runtime_DSpinBox.value()
1015
            self._magnet_logic.odmr_2d_low_fitfunction = self._mw.align_2d_odmr_low_fit_func_ComboBox.currentText()
1016
1017
            self._magnet_logic.odmr_2d_peak_axis0_move_ratio = self._mw.align_2d_axes0_shift_DSpinBox.value()*1e12
1018
            self._magnet_logic.odmr_2d_peak_axis1_move_ratio = self._mw.align_2d_axes1_shift_DSpinBox.value()*1e12
1019
1020
            self._magnet_logic.odmr_2d_single_trans = self._mw.odmr_2d_single_trans_CheckBox.isChecked()
1021
1022
            # nuclear ops:
1023
            self._magnet_logic.nuclear_2d_rabi_periode = self._mw.align_2d_nuclear_rabi_periode_DSpinBox.value()*1e-9
1024
            self._magnet_logic.nuclear_2d_mw_freq = self._mw.align_2d_nuclear_mw_freq_DSpinBox.value()*1e6
1025
            self._magnet_logic.nuclear_2d_mw_channel = self._mw.align_2d_nuclear_mw_channel_SpinBox.value()
1026
            self._magnet_logic.nuclear_2d_mw_power = self._mw.align_2d_nuclear_mw_power_DSpinBox.value()
1027
            self._magnet_logic.nuclear_2d_laser_time = self._mw.align_2d_nuclear_laser_time_DSpinBox.value()
1028
            self._magnet_logic.nuclear_2d_laser_channel = self._mw.align_2d_nuclear_laser_channel_SpinBox.value()
1029
            self._magnet_logic.nuclear_2d_detect_channel = self._mw.align_2d_nuclear_detect_channel_SpinBox.value()
1030
            self._magnet_logic.nuclear_2d_idle_time = self._mw.align_2d_nuclear_idle_time_DSpinBox.value()
1031
            self._magnet_logic.nuclear_2d_reps_within_ssr = self._mw.align_2d_nuclear_reps_within_ssr_SpinBox.value()
1032
            self._magnet_logic.nuclear_2d_num_ssr = self._mw.align_2d_nuclear_num_of_ssr_SpinBox.value()
1033
1034
            self._mw.alignment_2d_cb_GraphicsView.setLabel('right', 'Single shot readout fidelity', units='%')
1035
1036
1037
        constraints = self._magnet_logic.get_hardware_constraints()
1038
1039
        axis0_name = self._mw.align_2d_axes0_name_ComboBox.currentText()
1040
        axis0_range = self._mw.align_2d_axes0_range_DSpinBox.value()
1041
        axis0_step = self._mw.align_2d_axes0_step_DSpinBox.value()
1042
1043
        axis1_name = self._mw.align_2d_axes1_name_ComboBox.currentText()
1044
        axis1_range = self._mw.align_2d_axes1_range_DSpinBox.value()
1045
        axis1_step = self._mw.align_2d_axes1_step_DSpinBox.value()
1046
1047
        if axis0_name == axis1_name:
1048
            self.log.error('Fluorescence Alignment cannot be started since the '
1049
                        'same axis with name "{0}" was chosen for axis0 and '
1050
                        'axis1!\n'
1051
                        'Alignment will not be started. Change the '
1052
                        'settings!'.format(axis0_name))
1053
            return
1054
1055
        if self._mw.align_2d_axis0_set_vel_CheckBox.isChecked():
1056
            axis0_vel = self._mw.align_2d_axes0_vel_DSpinBox.value()
1057
        else:
1058
            axis0_vel = None
1059 View Code Duplication
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1060
        if self._mw.align_2d_axis1_set_vel_CheckBox.isChecked():
1061
            axis1_vel = self._mw.align_2d_axes1_vel_DSpinBox.value()
1062
        else:
1063
            axis1_vel = None
1064
1065
        self._magnet_logic.start_2d_alignment(axis0_name=axis0_name, axis0_range=axis0_range,
1066
                                              axis0_step=axis0_step, axis1_name=axis1_name,
1067
                                              axis1_range=axis1_range,axis1_step=axis1_step,
1068
                                              axis0_vel=axis0_vel, axis1_vel=axis1_vel,
1069
                                              continue_meas=self._continue_2d_fluorescence_alignment)
1070
1071
        self._continue_2d_fluorescence_alignment = False
1072
1073
    def continue_stop_2d_alignment(self, is_checked):
1074
        """ Manage what happens if 2d magnet scan is continued/stopped
1075
1076
        @param bool is_checked: state if the current scan, True = continue,
1077
                                False = stopped
1078
        """
1079
1080
        if is_checked:
1081
            self.continue_2d_alignment_clicked()
1082
        else:
1083
            self.abort_2d_alignment_clicked()
1084
1085
1086
    def continue_2d_alignment_clicked(self):
1087
1088 View Code Duplication
        self._continue_2d_fluorescence_alignment = True
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1089
        self.start_2d_alignment_clicked()
1090
1091
1092
    def abort_2d_alignment_clicked(self):
1093
        """ Stops the current Fluorescence alignment. """
1094
1095
        self._change_display_to_stop_2d_alignment()
1096
        self._magnet_logic.stop_alignment()
1097
1098
    def _update_limits_axis0(self):
1099
        """ Whenever a new axis name was chosen in axis0 config, the limits of the
1100
            viewboxes will be adjusted.
1101
        """
1102
1103
        constraints = self._magnet_logic.get_hardware_constraints()
1104
        axis0_name = self._mw.align_2d_axes0_name_ComboBox.currentText()
1105
1106
        # set the range constraints:
1107
        self._mw.align_2d_axes0_range_DSpinBox.setMinimum(0)
1108
        self._mw.align_2d_axes0_range_DSpinBox.setMaximum(constraints[axis0_name]['pos_max'])
1109
        self._mw.align_2d_axes0_range_DSpinBox.setSingleStep(constraints[axis0_name]['pos_step'])
1110
        # self._mw.align_2d_axes0_range_DSpinBox.setDecimals(5)
1111
        self._mw.align_2d_axes0_range_DSpinBox.setSuffix(constraints[axis0_name]['unit'])
1112
1113
        # set the step constraints:
1114
        self._mw.align_2d_axes0_step_DSpinBox.setMinimum(0)
1115
        self._mw.align_2d_axes0_step_DSpinBox.setMaximum(constraints[axis0_name]['pos_max'])
1116
        self._mw.align_2d_axes0_step_DSpinBox.setSingleStep(constraints[axis0_name]['pos_step'])
1117
        # self._mw.align_2d_axes0_step_DSpinBox.setDecimals(5)
1118
        self._mw.align_2d_axes0_step_DSpinBox.setSuffix(constraints[axis0_name]['unit'])
1119
1120
        # set the velocity constraints:
1121
        self._mw.align_2d_axes0_vel_DSpinBox.setMinimum(constraints[axis0_name]['vel_min'])
1122
        self._mw.align_2d_axes0_vel_DSpinBox.setMaximum(constraints[axis0_name]['vel_max'])
1123
        self._mw.align_2d_axes0_vel_DSpinBox.setSingleStep(constraints[axis0_name]['vel_step'])
1124
        # self._mw.align_2d_axes0_vel_DSpinBox.setDecimals(5)
1125
        self._mw.align_2d_axes0_vel_DSpinBox.setSuffix(constraints[axis0_name]['unit']+'/s')
1126
1127
    def _update_limits_axis1(self):
1128
        """ Whenever a new axis name was chosen in axis0 config, the limits of the
1129
            viewboxes will be adjusted.
1130
        """
1131
1132
        constraints = self._magnet_logic.get_hardware_constraints()
1133
        axis1_name = self._mw.align_2d_axes1_name_ComboBox.currentText()
1134
1135
        self._mw.align_2d_axes1_range_DSpinBox.setMinimum(0)
1136
        self._mw.align_2d_axes1_range_DSpinBox.setMaximum(constraints[axis1_name]['pos_max'])
1137
        self._mw.align_2d_axes1_range_DSpinBox.setSingleStep(constraints[axis1_name]['pos_step'])
1138
        # self._mw.align_2d_axes1_range_DSpinBox.setDecimals(5)
1139
        self._mw.align_2d_axes1_range_DSpinBox.setSuffix(constraints[axis1_name]['unit'])
1140
1141
        self._mw.align_2d_axes1_step_DSpinBox.setMinimum(0)
1142
        self._mw.align_2d_axes1_step_DSpinBox.setMaximum(constraints[axis1_name]['pos_max'])
1143
        self._mw.align_2d_axes1_step_DSpinBox.setSingleStep(constraints[axis1_name]['pos_step'])
1144
        # self._mw.align_2d_axes1_step_DSpinBox.setDecimals(5)
1145
        self._mw.align_2d_axes1_step_DSpinBox.setSuffix(constraints[axis1_name]['unit'])
1146
1147
        self._mw.align_2d_axes1_vel_DSpinBox.setMinimum(constraints[axis1_name]['vel_min'])
1148
        self._mw.align_2d_axes1_vel_DSpinBox.setMaximum(constraints[axis1_name]['vel_max'])
1149
        self._mw.align_2d_axes1_vel_DSpinBox.setSingleStep(constraints[axis1_name]['vel_step'])
1150
        # self._mw.align_2d_axes1_vel_DSpinBox.setDecimals(5)
1151
        self._mw.align_2d_axes1_vel_DSpinBox.setSuffix(constraints[axis1_name]['unit']+'/s')
1152
1153
    def _set_vel_display_axis0(self):
1154
        """ Set the visibility of the velocity display for axis 0. """
1155
1156
        if self._mw.align_2d_axis0_set_vel_CheckBox.isChecked():
1157
            self._mw.align_2d_axes0_vel_DSpinBox.setVisible(True)
1158
        else:
1159 View Code Duplication
            self._mw.align_2d_axes0_vel_DSpinBox.setVisible(False)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1160
1161
    def _set_vel_display_axis1(self):
1162
        """ Set the visibility of the velocity display for axis 1. """
1163
1164
        if self._mw.align_2d_axis1_set_vel_CheckBox.isChecked():
1165
            self._mw.align_2d_axes1_vel_DSpinBox.setVisible(True)
1166
        else:
1167
            self._mw.align_2d_axes1_vel_DSpinBox.setVisible(False)
1168
1169
    def _update_2d_graph_axis(self):
1170
1171
        constraints = self._magnet_logic.get_hardware_constraints()
1172
1173
        axis0_name = self._mw.align_2d_axes0_name_ComboBox.currentText()
1174
        axis0_unit = constraints[axis0_name]['unit']
1175
        axis1_name = self._mw.align_2d_axes1_name_ComboBox.currentText()
1176
        axis1_unit = constraints[axis1_name]['unit']
1177
1178
        axis0_array, axis1_array = self._magnet_logic.get_2d_axis_arrays()
1179
1180
        self._2d_alignment_ImageItem.setRect(QtCore.QRectF(axis0_array[0],
1181
                                                           axis1_array[0],
1182
                                                           axis0_array[-1]-axis0_array[0],
1183
                                                           axis1_array[-1]-axis1_array[0],))
1184
1185
        self._mw.alignment_2d_GraphicsView.setLabel('bottom', 'Absolute Position, Axis0: ' + axis0_name, units=axis0_unit)
1186
        self._mw.alignment_2d_GraphicsView.setLabel('left', 'Absolute Position, Axis1: '+ axis1_name, units=axis1_unit)
1187
1188 View Code Duplication
    def _update_2d_graph_cb(self):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1189
        """ Update the colorbar to a new scaling.
1190
1191
        That function alters the color scaling of the colorbar next to the main
1192
        picture.
1193
        """
1194
1195
        # If "Centiles" is checked, adjust colour scaling automatically to
1196
        # centiles. Otherwise, take user-defined values.
1197
1198
        if self._mw.alignment_2d_centiles_RadioButton.isChecked():
1199
1200
            low_centile = self._mw.alignment_2d_cb_low_centiles_DSpinBox.value()
1201
            high_centile = self._mw.alignment_2d_cb_high_centiles_DSpinBox.value()
1202
1203
            if np.isclose(low_centile, 0.0):
1204
                low_centile = 0.0
1205
1206
            # mask the array such that the arrays will be
1207
            masked_image = np.ma.masked_equal(self._2d_alignment_ImageItem.image, 0.0)
1208
1209
            if len(masked_image.compressed()) == 0:
1210
                cb_min = np.percentile(self._2d_alignment_ImageItem.image, low_centile)
1211
                cb_max = np.percentile(self._2d_alignment_ImageItem.image, high_centile)
1212
            else:
1213
                cb_min = np.percentile(masked_image.compressed(), low_centile)
1214
                cb_max = np.percentile(masked_image.compressed(), high_centile)
1215
1216
        else:
1217
            cb_min = self._mw.alignment_2d_cb_min_centiles_DSpinBox.value()
1218
            cb_max = self._mw.alignment_2d_cb_max_centiles_DSpinBox.value()
1219
1220
        self._2d_alignment_cb.refresh_colorbar(cb_min, cb_max)
1221
        self._mw.alignment_2d_cb_GraphicsView.update()
1222 View Code Duplication
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1223
    def _update_2d_graph_data(self):
1224
        """ Refresh the 2D-matrix image. """
1225
        matrix_data = self._magnet_logic.get_2d_data_matrix()
1226
1227
        if self._mw.alignment_2d_centiles_RadioButton.isChecked():
1228
1229
            low_centile = self._mw.alignment_2d_cb_low_centiles_DSpinBox.value()
1230
            high_centile = self._mw.alignment_2d_cb_high_centiles_DSpinBox.value()
1231
1232
            if np.isclose(low_centile, 0.0):
1233
                low_centile = 0.0
1234
1235
            # mask the array in order to mark the values which are zeros with
1236
            # True, the rest with False:
1237
            masked_image = np.ma.masked_equal(matrix_data, 0.0)
1238
1239
            # compress the 2D masked array to a 1D array where the zero values
1240
            # are excluded:
1241
            if len(masked_image.compressed()) == 0:
1242
                cb_min = np.percentile(self._2d_alignment_ImageItem.image, low_centile)
1243
                cb_max = np.percentile(self._2d_alignment_ImageItem.image, high_centile)
1244
            else:
1245
                cb_min = np.percentile(masked_image.compressed(), low_centile)
1246
                cb_max = np.percentile(masked_image.compressed(), high_centile)
1247
        else:
1248
            cb_min = self._mw.alignment_2d_cb_min_centiles_DSpinBox.value()
1249
            cb_max = self._mw.alignment_2d_cb_max_centiles_DSpinBox.value()
1250
1251
1252
        self._2d_alignment_ImageItem.setImage(image=matrix_data,
1253
                                              levels=(cb_min, cb_max))
1254
        self._update_2d_graph_axis()
1255
1256
        self._update_2d_graph_cb()
1257
1258
        # get data from logic
1259
1260
1261
    def save_2d_plots_and_data(self):
1262
        """ Save the sum plot, the scan marix plot and the scan data """
1263
        timestamp = datetime.datetime.now()
1264
        filetag = self._mw.alignment_2d_nametag_LineEdit.text()
1265
        filepath = self._save_logic.get_path_for_module(module_name='Magnet')
1266
1267
        if len(filetag) > 0:
1268
            filename = os.path.join(filepath, '{0}_{1}_Magnet'.format(timestamp.strftime('%Y%m%d-%H%M-%S'), filetag))
1269
        else:
1270
            filename = os.path.join(filepath, '{0}_Magnet'.format(timestamp.strftime('%Y%m%d-%H%M-%S'),))
1271
1272
        exporter_graph = pyqtgraph.exporters.SVGExporter(self._mw.alignment_2d_GraphicsView.plotItem.scene())
1273
        #exporter_graph = pg.exporters.ImageExporter(self._mw.odmr_PlotWidget.plotItem)
1274
        exporter_graph.export(filename  + '.svg')
1275
1276
        # self._save_logic.
1277
        self._magnet_logic.save_2d_data(filetag, timestamp)
1278
1279
    def set_measurement_type(self):
1280
        """ According to the selected Radiobox a measurement type will be chosen."""
1281
1282
        #FIXME: the measurement type should actually be set and saved in the logic
1283
1284
        if self._mw.meas_type_fluorescence_RadioButton.isChecked():
1285
            self.measurement_type = '2d_fluorescence'
1286
        elif self._mw.meas_type_odmr_RadioButton.isChecked():
1287
            self.measurement_type = '2d_odmr'
1288
        elif self._mw.meas_type_nuclear_spin_RadioButton.isChecked():
1289
            self.measurement_type = '2d_nuclear'
1290
        else:
1291
            self.log.error('No measurement type specified in Magnet GUI!')
1292
    def _odmr_single_trans_alignment_changed(self):
1293
        """ Adjust the GUI display if only one ODMR transition is used. """
1294
1295
        if self._mw.odmr_2d_single_trans_CheckBox.isChecked():
1296
            self._mw.odmr_2d_high_trans_GroupBox.setVisible(False)
1297
        else:
1298
            self._mw.odmr_2d_high_trans_GroupBox.setVisible(True)
1299