class DAQ_2DViewer_FLIM(DAQ_1DViewer_TH260): """ ==================== ================== **Atrributes** **Type** *params* dictionnary list *hardware_averaging* boolean *x_axis* 1D numpy array *ind_data* int ==================== ================== See Also -------- utility_classes.DAQ_Viewer_base """ params = DAQ_1DViewer_TH260.params + stage_params stop_scanner = pyqtSignal() start_tttr_scan = pyqtSignal() def __init__(self, parent=None, params_state=None): super(DAQ_2DViewer_FLIM, self).__init__( parent, params_state ) #initialize base class with commom attributes and methods self.settings.child( 'acquisition', 'acq_type').setOpts(limits=['Counting', 'Histo', 'T3', 'FLIM']) self.settings.child('acquisition', 'acq_type').setValue('Histo') self.stage = None self.scan_parameters = None self.x_axis = None self.y_axis = None self.Nx = 1 self.Ny = 1 self.signal_axis = None def commit_settings(self, param): if param.name() not in custom_tree.iter_children( self.settings.child(('stage_settings'))): super(DAQ_2DViewer_FLIM, self).commit_settings(param) else: if param.name() == 'time_interval': self.stage._get_read() self.stage.set_time_interval( Time(param.value(), unit='m')) # set time interval between pixels elif param.name() == 'show_navigator': self.emit_status(ThreadCommand('show_navigator')) self.emit_status(ThreadCommand('show_scanner')) param.setValue(False) elif param.name() in custom_tree.iter_children( self.settings.child('stage_settings', 'move_at'), []): pos_x = self.settings.child('stage_settings', 'move_at', 'move_at_x').value() pos_y = self.settings.child('stage_settings', 'move_at', 'move_at_y').value() self.move_at_navigator(pos_x, pos_y) def emit_data(self): """ """ try: mode = self.settings.child('acquisition', 'acq_type').value() if mode == 'Counting' or mode == 'Histo' or mode == 'T3': super(DAQ_2DViewer_FLIM, self).emit_data() elif mode == 'FLIM': self.stop_scanner.emit() self.h5saver.h5_file.flush() datas = self.process_histo_from_h5_and_correct_shifts( self.Nx, self.Ny, channel=0, marker=65) self.data_grabed_signal.emit([ DataFromPlugins(name='TH260', data=datas, dim='DataND', nav_axes=(0, 1), nav_x_axis=self.get_nav_xaxis(), nav_y_axis=self.get_nav_yaxis(), xaxis=self.get_xaxis(), external_h5=self.h5saver.h5_file) ]) self.stop() except Exception as e: self.emit_status( ThreadCommand('Update_Status', [getLineInfo() + str(e), 'log'])) def emit_data_tmp(self): """ """ try: mode = self.settings.child('acquisition', 'acq_type').value() if mode == 'Counting' or mode == 'Histo' or mode == 'T3': super(DAQ_2DViewer_FLIM, self).emit_data_tmp() elif mode == 'FLIM': self.data_grabed_signal_temp.emit([ DataFromPlugins(name='TH260', data=self.datas, dim='DataND', nav_axes=(0, 1), nav_x_axis=self.get_nav_xaxis(), nav_y_axis=self.get_nav_yaxis(), xaxis=self.get_xaxis()) ]) except Exception as e: self.emit_status( ThreadCommand('Update_Status', [getLineInfo() + str(e), 'log'])) def process_histo_from_h5_and_correct_shifts(self, Nx=1, Ny=1, channel=0, marker=65): """ Specific method to correct for drifts between various scans performed using the quick scans feature Parameters ---------- Nx Ny channel marker Returns ------- """ Nbins = self.settings.child('acquisition', 'timings', 'nbins').value() time_window = Nbins markers_array = self.h5saver.h5_file.get_node('/markers') nanotime_array = self.h5saver.h5_file.get_node('/nanotimes') datas = np.zeros((Nx, Ny, Nbins)) intensity_map_ref = np.zeros((Nx, Ny), dtype=np.int64) ind_lines = np.where(markers_array.read() == marker)[0] indexes_reading = ind_lines[::Nx * Ny][1:] Nreadings = len(indexes_reading) ind_reading = 0 ind_offset = 0 for ind in range(Nreadings): if len(ind_lines) > 2: ind_last_line = ind_lines[-1] markers_tmp = markers_array[ind_reading:ind_reading + ind_last_line] nanotimes_tmp = nanotime_array[ind_reading:ind_reading + ind_last_line] # datas array is updated within this method datas_tmp = self.extract_TTTR_histo_every_pixels( nanotimes_tmp, markers_tmp, marker=marker, Nx=Nx, Ny=Ny, Ntime=Nbins, ind_line_offset=ind_offset, channel=channel, time_window=time_window) intensity_map = np.squeeze(np.sum(datas_tmp, axis=2)) if ind == 0: intensity_map_ref = intensity_map ind_offset += len(ind_lines) - 2 ind_reading += ind_lines[-2] #correct for shifts in x or y during collections and multiple scans of the same area shift, error, diffphase = register_translation( intensity_map_ref, intensity_map, 1) datas += np.roll(datas_tmp, [int(s) for s in shift], (0, 1)) return datas def ini_detector(self, controller=None): """ See Also -------- DAQ_utils.ThreadCommand, hardware1D.DAQ_1DViewer_Picoscope.update_pico_settings """ self.status.update( edict(initialized=False, info="", x_axis=None, y_axis=None, controller=None)) try: self.status = super(DAQ_2DViewer_FLIM, self).ini_detector(controller) self.ini_stage() self.status.x_axis = self.x_axis self.status.initialized = True self.status.controller = self.controller return self.status except Exception as e: self.status.info = getLineInfo() + str(e) self.status.initialized = False return self.status @pyqtSlot(ScanParameters) def update_scanner(self, scan_parameters): self.scan_parameters = scan_parameters self.x_axis = self.scan_parameters.axes_unique[0] self.Nx = self.x_axis.size self.y_axis = self.scan_parameters.axes_unique[1] self.Ny = self.y_axis.size def get_nav_xaxis(self): """ """ return self.scan_parameters.axis_2D_1 def get_nav_yaxis(self): """ """ return self.scan_parameters.axis_2D_2 def ini_stage(self): if self.settings.child('stage_settings', 'stage_type').value() == 'PiezoConcept': self.stage = PiezoConcept() self.controller.stage = self.stage self.stage.init_communication( self.settings.child('stage_settings', 'com_port').value()) controller_id = self.stage.get_controller_infos() self.settings.child('stage_settings', 'controller_id').setValue(controller_id) self.stage.set_time_interval( Time(self.settings.child('stage_settings', 'time_interval').value(), unit='m')) #set time interval between pixels #set the TTL outputs for each displacement of first axis self.stage.set_TTL_state( 1, self.settings.child('stage_settings', 'stage_x', 'stage_x_axis').value(), 'output', dict(type='start')) self.move_abs(0, 'X') self.move_abs(0, 'Y') @pyqtSlot(float, float) def move_at_navigator(self, posx, posy): self.move_abs(posx, 'X') self.move_abs(posy, 'Y') def move_abs(self, position, axis='X'): stage = f'stage_{axis}'.lower() stage_axis = f'stage_{axis}_axis'.lower() offset_stage = f'offset_{axis}'.lower() stage_dir = f'stage_{axis}_direction'.lower() offset = self.settings.child('stage_settings', stage, offset_stage).value() if self.settings.child('stage_settings', stage, stage_dir).value() == 'Normal': posi = position + offset else: posi = -position + offset ax = self.settings.child('stage_settings', stage, stage_axis).value() pos = Position(ax, int(posi * 1000), unit='n') out = self.stage.move_axis('ABS', pos) def close(self): """ """ super(DAQ_2DViewer_FLIM, self).close() self.stage.close_communication() def set_acq_mode(self, mode='FLIM', update=False): """ herited method Change the acquisition mode (histogram for mode=='Counting' and 'Histo' or T3 for mode == 'FLIM') Parameters ---------- mode Returns ------- """ # check enabled channels labels = [ k for k in self.channels_enabled.keys() if self.channels_enabled[k]['enabled'] ] N = len(labels) if mode != self.actual_mode or update: if mode == 'Counting' or mode == 'Histo' or mode == 'T3': super(DAQ_2DViewer_FLIM, self).set_acq_mode(mode, update) elif mode == 'FLIM': self.emit_status(ThreadCommand('show_scanner')) QtWidgets.QApplication.processEvents() self.emit_status(ThreadCommand('show_navigator')) self.controller.TH260_Initialize(self.device, mode=3) # mode T3 self.controller.TH260_SetMarkerEnable(self.device, 1) self.datas = np.zeros((10, 10, 1024)) self.data_grabed_signal_temp.emit([ DataFromPlugins(name='TH260', data=self.datas, nav_axes=[0, 1], dim='DataND') ]) self.data_pointers = self.datas.ctypes.data_as( ctypes.POINTER(ctypes.c_uint32)) self.actual_mode = mode self.actual_mode = mode def grab_data(self, Naverage=1, **kwargs): """ Start new acquisition in two steps : * Initialize data: self.datas for the memory to store new data and self.data_average to store the average data * Start acquisition with the given exposure in ms, in "1d" or "2d" mode =============== =========== ============================= **Parameters** **Type** **Description** Naverage int Number of images to average =============== =========== ============================= See Also -------- DAQ_utils.ThreadCommand """ try: self.acq_done = False mode = self.settings.child('acquisition', 'acq_type').value() if mode == 'Counting' or mode == 'Histo' or mode == 'T3': super(DAQ_2DViewer_FLIM, self).grab_data(Naverage, **kwargs) elif mode == 'FLIM': self.ind_reading = 0 self.ind_offset = 0 self.do_process_tttr = False self.init_h5file() self.datas = np.zeros(( self.Nx, self.Ny, self.settings.child('acquisition', 'timings', 'nbins').value(), ), dtype=np.float64) time_acq = int( self.settings.child('acquisition', 'acq_time').value() * 1000) # in ms self.general_timer.stop() self.prepare_moves() # prepare asynchronous tttr time event reading t3_reader = T3Reader_scan(self.device, self.controller, time_acq, self.stage, self.Nchannels) self.detector_thread = QThread() t3_reader.moveToThread(self.detector_thread) t3_reader.data_signal[dict].connect(self.populate_h5) self.stop_tttr.connect(t3_reader.stop_TTTR) self.start_tttr_scan.connect(t3_reader.start_TTTR) self.detector_thread.t3_reader = t3_reader self.detector_thread.start() self.detector_thread.setPriority(QThread.HighestPriority) #start acquisition and scanner self.time_t3 = time.perf_counter() self.time_t3_rate = time.perf_counter() self.start_tttr_scan.emit() except Exception as e: self.emit_status( ThreadCommand('Update_Status', [getLineInfo() + str(e), "log"])) def prepare_moves(self): """ prepare given actuators with positions from scan_parameters Returns ------- """ self.x_axis = self.scan_parameters.axis_2D_1 self.Nx = self.x_axis.size self.y_axis = self.scan_parameters.axis_2D_2 self.Ny = self.y_axis.size positions_real = self.transform_scan_coord( self.scan_parameters.positions) # interval_time = self.settings.child('stage_settings', 'time_interval').value()/1000 # total_time = self.settings.child('acquisition', 'acq_time').value() # Ncycles = int(total_time/(interval_time*len(positions_real))) # total_time = Ncycles * interval_time*len(positions_real) # self.settings.child('acquisition', 'acq_time').setValue(total_time) # positions = [] # for ind in range(Ncycles): # positions.extend(positions_real) self.stage.set_positions_arbitrary(positions_real) self.move_at_navigator(*self.scan_parameters.positions[0][0:2]) def transform_scan_coord(self, positions): offset = self.settings.child('stage_settings', 'stage_x', 'offset_x').value() if self.settings.child('stage_settings', 'stage_x', 'stage_x_direction').value() == 'Normal': scaling_x = -1 else: scaling_x = 1 if self.settings.child('stage_settings', 'stage_y', 'stage_y_direction').value() == 'Normal': scaling_y = -1 else: scaling_y = +1 if self.settings.child('stage_settings', 'stage_x', 'stage_x_axis').value() == 'X': ind_x = 0 else: ind_x = 1 if self.settings.child('stage_settings', 'stage_y', 'stage_y_axis').value() == 'Y': ind_y = 1 else: ind_y = 0 positions_out = [] for pos in positions: pos_tmp = [(scaling_x * pos[ind_x] + offset) * 1000, (scaling_y * pos[ind_y] + offset) * 1000] positions_out.append(pos_tmp) return positions_out def stop(self): super(DAQ_2DViewer_FLIM, self).stop() self.stop_scanner.emit() try: self.move_at_navigator(0, 0) except: pass
class DAQ_1DViewer_TH260(DAQ_Viewer_base): """ See Also -------- utility_classes.DAQ_Viewer_base """ params = comon_parameters+[ {'title': 'Device index:', 'name': 'device', 'type': 'int', 'value': 0, 'max': 3, 'min': 0}, {'title': 'Infos:', 'name': 'infos', 'type': 'str', 'value': "", 'readonly': True}, {'title': 'Line Settings:', 'name': 'line_settings', 'type': 'group', 'expanded': False, 'children': [ {'title': 'Sync Settings:', 'name': 'sync_settings', 'type': 'group', 'expanded': True, 'children': [ {'title': 'ZeroX (mV):', 'name': 'zerox', 'type': 'int', 'value': -10, 'max': 0, 'min': -40}, {'title': 'Level (mV):', 'name': 'level', 'type': 'int', 'value': -50, 'max': 0, 'min': -1200}, {'title': 'Offset (ps):', 'name': 'offset', 'type': 'int', 'value': 0, 'max': 99999, 'min': -99999}, {'title': 'Divider:', 'name': 'divider', 'type': 'list', 'value': 1, 'values': [1, 2, 4, 8]}, ]}, {'title': 'CH1 Settings:', 'name': 'ch1_settings', 'type': 'group', 'expanded': True, 'children': [ {'title': 'Enabled?:', 'name': 'enabled', 'type': 'bool', 'value': True}, {'title': 'ZeroX (mV):', 'name': 'zerox', 'type': 'int', 'value': -10, 'max': 0, 'min': -40}, {'title': 'Level (mV):', 'name': 'level', 'type': 'int', 'value': -150, 'max': 0, 'min': -1200}, {'title': 'Offset (ps):', 'name': 'offset', 'type': 'int', 'value': 0, 'max': 99999, 'min': -99999}, {'title': 'Deadtime (ns):', 'name': 'deadtime', 'type': 'list', 'value': 24, 'values': [24, 44, 66, 88, 112, 135, 160, 180]}, ]}, {'title': 'CH2 Settings:', 'name': 'ch2_settings', 'type': 'group', 'expanded': True, 'children': [ {'title': 'Enabled?:', 'name': 'enabled', 'type': 'bool', 'value': False}, {'title': 'ZeroX (mV):', 'name': 'zerox', 'type': 'int', 'value': -10, 'max': 0, 'min': -40}, {'title': 'Level (mV):', 'name': 'level', 'type': 'int', 'value': -150, 'max': 0, 'min': -1200}, {'title': 'Offset (ps):', 'name': 'offset', 'type': 'int', 'value': 0, 'max': 99999, 'min': -99999}, {'title': 'Deadtime (ns):', 'name': 'deadtime', 'type': 'list', 'value': 24, 'values': [24, 44, 66, 88, 112, 135, 160, 180]}, ]}, ]}, {'title': 'Acquisition:', 'name': 'acquisition', 'type': 'group', 'expanded': True, 'children': [ {'title': 'Acq. type:', 'name': 'acq_type', 'type': 'list', 'value': 'Histo', 'values': ['Counting', 'Histo', 'T3']}, {'title': 'Base path:', 'name': 'base_path', 'type': 'browsepath', 'value': 'E:\Data', 'filetype': False, 'readonly': True, 'visible': False }, {'title': 'Temp. File:', 'name': 'temp_file', 'type': 'str', 'value': '', 'visible': False}, {'title': 'Acq. time (s):', 'name': 'acq_time', 'type': 'float', 'value': 1, 'min': 0.1, 'max': 360000}, {'title': 'Elapsed time (s):', 'name': 'elapsed_time', 'type': 'float', 'value': 0, 'min': 0, 'readonly': True}, {'title': 'Timings:', 'name': 'timings', 'type': 'group', 'expanded': True, 'children': [ {'title': 'Mode:', 'name': 'timing_mode', 'type': 'list', 'value': 'Hires', 'values': ['Hires', 'Lowres']}, {'title': 'Base Resolution (ps):', 'name': 'base_resolution', 'type': 'float', 'value': 25, 'min': 0, 'readonly': True}, {'title': 'Resolution (ns):', 'name': 'resolution', 'type': 'float', 'value': 0.2, 'min': 0}, {'title': 'Time window (s):', 'name': 'window', 'type': 'float', 'value': 100, 'min': 0, 'readonly': True, 'enabled': False, 'siPrefix': True}, {'title': 'Nbins:', 'name': 'nbins', 'type': 'list', 'value': 1024, 'values': [1024*(2**lencode) for lencode in range(6)]}, {'title': 'Offset (ns):', 'name': 'offset', 'type': 'int', 'value': 0, 'max': 100000000, 'min': 0}, ]}, {'title': 'Rates:', 'name': 'rates', 'type': 'group', 'expanded': True, 'children': [ {'title': 'Show large display?', 'name': 'large_display', 'type': 'bool', 'value': True}, {'title': 'Sync rate (cts/s):', 'name': 'syncrate', 'type': 'int', 'value': 0, 'min': 0, 'readonly': True, 'siPrefix': True}, {'title': 'CH1 rate (cts/s):', 'name': 'ch1_rate', 'type': 'int', 'value': 0, 'min': 0, 'readonly': True, 'siPrefix': True}, {'title': 'CH2 rate (cts/s):', 'name': 'ch2_rate', 'type': 'int', 'value': 0, 'min': 0, 'readonly': True, 'siPrefix': True}, {'title': 'Nrecords:', 'name': 'records', 'type': 'int', 'value': 0, 'min': 0, 'readonly': True, 'siPrefix': True}, ]}, ]}, ] hardware_averaging = False stop_tttr = pyqtSignal() def __init__(self, parent=None, params_state=None): super(DAQ_1DViewer_TH260, self).__init__(parent, params_state) #initialize base class with commom attributes and methods self.device = None self.x_axis = None self.controller = None self.datas = None #list of numpy arrays, see set_acq_mode self.data_pointers = None #list of ctypes pointers pointing to self.datas array elements, see set_acq_mode self.acq_done = False self.Nchannels = 0 self.channels_enabled = {'CH1': {'enabled': True, 'index': 0}, 'CH2': {'enabled': False, 'index': 1}} self.modes = ['Histo', 'T2', 'T3'] self.actual_mode = 'Counting' self.h5file = None self.detector_thread = None self.time_t3 = 0 self.time_t3_rate = 0 self.ind_reading = 0 self.histo_array = None def emit_log(self,string): self.emit_status(ThreadCommand('Update_Status', [string, 'log'])) def commit_settings(self, param): """ | Activate parameters changes on the hardware from parameter's name. | =============== ================================ ========================= **Parameters** **Type** **Description** *param* instance of pyqtgraph parameter The parameter to activate =============== ================================ ========================= Three profile of parameter : * **bin_x** : set binning camera from bin_x parameter's value * **bin_y** : set binning camera from bin_y parameter's value * **set_point** : Set the camera's temperature from parameter's value. """ try: if param.name() == 'acq_type': self.set_acq_mode(param.value()) self.set_get_resolution(wintype='both') if param.value() == 'Counting' or param.value() == 'Histo': self.settings.child('acquisition', 'temp_file').hide() self.settings.child('acquisition', 'base_path').hide() else: self.settings.child('acquisition', 'temp_file').show() self.settings.child('acquisition', 'base_path').show() # self.settings.child('acquisition', 'timings', 'nbins').setOpts( # limits=[128 * (2 ** lencode) for lencode in range(6)]) # self.settings.child('acquisition', 'timings', 'nbins').setValue(128) # # else: # self.settings.child('acquisition', 'timings', 'nbins').setOpts( # limits=[1024 * (2 ** lencode) for lencode in range(6)]) # self.settings.child('acquisition', 'timings', 'nbins').setValue(1024) elif param.name() == 'nbins' or param.name() == 'resolution': self.set_get_resolution(param.name()) elif param.name() == 'timing_mode': self.set_get_resolution('resolution') elif param.parent().name() == 'ch1_settings' or param.parent().name() == 'ch2_settings' or param.parent().name() == 'sync_settings': self.set_sync_channel(param) elif param.name() == 'offset' and param.parent().name() == 'timings': self.controller.TH260_SetOffset(self.device, param.value()) elif param.name() == 'large_display' and param.value(): self.emit_status(ThreadCommand('init_lcd', [dict(labels=['Syn. Rate (kcts/s)', 'CH1 rate (kcts/s)', 'CH2 Rate (kcts/s)'], Nvals=3, digits=6)])) except Exception as e: self.emit_status(ThreadCommand('Update_Status', [getLineInfo() + str(e), 'log'])) def emit_data(self): """ """ try: mode = self.settings.child('acquisition', 'acq_type').value() if mode == 'Counting': rates = [np.array(rate) for rate in self.get_rates()[1:]] self.data_grabed_signal.emit([OrderedDict(name='TH260', data=rates, type='Data0D')]) elif mode == 'Histo': channels_index = [self.channels_enabled[k]['index'] for k in self.channels_enabled.keys() if self.channels_enabled[k]['enabled']] for ind, channel in enumerate(channels_index): self.controller.TH260_GetHistogram(self.device, self.data_pointers[ind], channel=channel, clear=True) records = np.sum(np.array([np.sum(data) for data in self.datas])) self.settings.child('acquisition', 'rates', 'records').setValue(records) self.data_grabed_signal.emit([OrderedDict(name='TH260', data=self.datas, type='Data1D',)]) self.general_timer.start() elif mode == 'T3': self.data_grabed_signal.emit([OrderedDict(name='TH260', data=[self.datas], type='Data1D')]) self.general_timer.start() except Exception as e: self.emit_status(ThreadCommand('Update_Status', [getLineInfo()+ str(e), 'log'])) def emit_data_tmp(self): """ """ try: mode = self.settings.child('acquisition', 'acq_type').value() if mode == 'Counting': rates = [np.array(rate) for rate in self.get_rates()[1:]] self.data_grabed_signal_temp.emit([OrderedDict(name='TH260', data=rates, type='Data0D')]) elif mode == 'Histo': channels_index = [self.channels_enabled[k]['index'] for k in self.channels_enabled.keys() if self.channels_enabled[k]['enabled']] for ind, channel in enumerate(channels_index): self.controller.TH260_GetHistogram(self.device, self.data_pointers[ind], channel=channel, clear=False) records = np.sum(np.array([np.sum(data) for data in self.datas])) self.settings.child('acquisition', 'rates', 'records').setValue(records) self.data_grabed_signal_temp.emit([OrderedDict(name='TH260', data=self.datas, type='Data1D',)]) elif mode == 'T3': self.data_grabed_signal_temp.emit([OrderedDict(name='TH260', data=[self.datas], type='Data1D')]) except Exception as e: self.emit_status(ThreadCommand('Update_Status', [getLineInfo()+ str(e), 'log'])) def process_histo_from_h5(self, Nx=1, Ny=1, channel=0, marker=65): marker = 65 markers = self.h5file.get_node('/markers')[self.ind_reading:] nanotimes = self.h5file.get_node('/nanotimes')[self.ind_reading:] nbins = self.settings.child('acquisition', 'timings', 'nbins').value() datas = extract_TTTR_histo_every_pixels(nanotimes, markers, marker=marker, Nx=Nx, Ny=Ny, Ntime=nbins, ind_line_offset=self.ind_reading, channel=channel) self.ind_reading += nanotimes.size return datas def set_acq_mode(self, mode, update=False): """ Change the acquisition mode (histogram for mode=='Counting' and 'Histo' or T3 for mode == 'FLIM') Parameters ---------- mode Returns ------- """ #check enabled channels labels = [k for k in self.channels_enabled.keys() if self.channels_enabled[k]['enabled']] N = len(labels) if mode != self.actual_mode or update: if mode == 'Counting': self.controller.TH260_Initialize(self.device, mode=0) # histogram self.datas = [np.zeros((1,), dtype=np.uint32) for ind in range(N)] self.data_grabed_signal_temp.emit([OrderedDict(name='TH260', data=self.datas, type='Data0D', labels=labels)]) self.data_pointers = [data.ctypes.data_as(ctypes.POINTER(ctypes.c_uint32)) for data in self.datas] elif mode == 'Histo': self.controller.TH260_Initialize(self.device, mode=0) # histogram self.datas = [np.zeros((self.settings.child('acquisition', 'timings', 'nbins').value(),), dtype=np.uint32) for ind in range(N)] self.data_grabed_signal_temp.emit([OrderedDict(name='TH260', data=self.datas, type='Data1D', x_axis=self.get_xaxis(), labels=labels)]) self.data_pointers = [data.ctypes.data_as(ctypes.POINTER(ctypes.c_uint32)) for data in self.datas] elif mode == 'T3': self.controller.TH260_Initialize(self.device, mode=3) # T3 mode self.datas = [np.zeros((self.settings.child('acquisition', 'timings', 'nbins').value(),), dtype=np.uint32) for ind in range(N)] self.data_grabed_signal_temp.emit([OrderedDict(name='TH260', data=self.datas, type='Data1D', x_axis=self.get_xaxis(), labels=labels)]) self.data_pointers = [data.ctypes.data_as(ctypes.POINTER(ctypes.c_uint32)) for data in self.datas] self.actual_mode = mode def ini_channels(self): self.controller.TH260_SetSyncDiv(self.device, self.settings.child('line_settings', 'sync_settings', 'divider').value()) self.controller.TH260_SetSyncCFD(self.device, self.settings.child('line_settings', 'sync_settings', 'level').value(), self.settings.child('line_settings', 'sync_settings', 'zerox').value()) self.controller.TH260_SetSyncChannelOffset(self.device, self.settings.child('line_settings', 'sync_settings', 'offset').value()) self.controller.TH260_SetInputCFD(self.device, 0, self.settings.child('line_settings', 'ch1_settings', 'level').value(), self.settings.child('line_settings', 'ch1_settings', 'zerox').value()) self.controller.TH260_SetInputCFD(self.device, 1, self.settings.child('line_settings', 'ch2_settings', 'level').value(), self.settings.child('line_settings', 'ch2_settings', 'zerox').value()) self.controller.TH260_SetInputChannelOffset(self.device, 0, self.settings.child('line_settings', 'ch1_settings', 'offset').value()) self.controller.TH260_SetInputChannelOffset(self.device, 1, self.settings.child('line_settings', 'ch2_settings', 'offset').value()) param = self.settings.child('line_settings', 'ch1_settings', 'deadtime') code = param.opts['limits'].index(param.value()) self.controller.TH260_SetInputDeadTime(self.device, 0, code) param = self.settings.child('line_settings', 'ch2_settings', 'deadtime') code = param.opts['limits'].index(param.value()) self.controller.TH260_SetInputDeadTime(self.device, 1, code) self.Nchannels = self.controller.TH260_GetNumOfInputChannels(self.device) if self.Nchannels >= 1: self.settings.child('line_settings', 'ch2_settings').hide() self.controller.TH260_SetInputChannelEnable(self.device, channel=0, enable=self.settings.child('line_settings', 'ch1_settings', 'enabled').value()) self.channels_enabled['CH2']['enabled'] = False self.channels_enabled['CH1']['enabled'] = self.settings.child('line_settings', 'ch1_settings', 'enabled').value() if self.Nchannels >= 2: self.settings.child('line_settings', 'ch2_settings').show() self.channels_enabled['CH2']['enabled'] = self.settings.child('line_settings', 'ch2_settings', 'enabled').value() self.controller.TH260_SetInputChannelEnable(self.device, channel=1, enable=self.settings.child('line_settings', 'ch2_settings', 'enabled').value()) def ini_detector(self, controller=None): """ See Also -------- DAQ_utils.ThreadCommand, hardware1D.DAQ_1DViewer_Picoscope.update_pico_settings """ self.status.update(edict(initialized=False,info="",x_axis=None,y_axis=None,controller=None)) try: if self.settings.child(('controller_status')).value()=="Slave": if controller is None: raise Exception('no controller has been defined externally while this detector is a slave one') else: self.controller=controller else: self.device = self.settings.child(('device')).value() self.settings.child(('device')).setOpts(readonly=True) #not possible to change it once initialized self.controller = timeharp260.Th260() # open device and initialize it self.controller.TH260_OpenDevice(self.device) #set timer to update info from controller self.general_timer = QTimer() self.general_timer.setInterval(200) self.general_timer.timeout.connect(self.update_timer) #set timer to check acquisition state self.acq_timer = QTimer() self.acq_timer.setInterval(500) self.acq_timer.timeout.connect(self.check_acquisition) #init the device and memory in the selected mode self.set_acq_mode(self.settings.child('acquisition', 'acq_type').value(), update=True) model, partn, version = self.controller.TH260_GetHardwareInfo(self.device) serial = self.controller.TH260_GetSerialNumber(self.device) self.settings.child(('infos')).setValue('serial: {}, model: {}, pn: {}, version: {}'.format(serial, model, partn, version)) self.ini_channels() self.set_get_resolution(wintype='both') self.emit_status(ThreadCommand('init_lcd', [dict(labels=['CH1 rate (kcts/s)', 'CH2 Rate (kcts/s)'], Nvals=2, digits=6)])) self.general_timer.start() # Timer event fired every 200ms #%%%%%%% init axes from image self.x_axis = self.get_xaxis() self.status.x_axis = self.x_axis self.status.initialized = True self.status.controller = self.controller return self.status except Exception as e: self.status.info = getLineInfo()+ str(e) self.status.initialized = False return self.status def poll_acquisition(self): """ valid only for histogramming mode Returns ------- """ while not self.controller.TH260_CTCStatus(self.device): # elapsed_time = self.controller.TH260_GetElapsedMeasTime(self.device) # in ms # self.settings.child('acquisition', 'elapsed_time').setValue(elapsed_time / 1000) # in s QtWidgets.QApplication.processEvents() QThread.msleep(100) #self.emit_data_tmp() self.controller.TH260_StopMeas(self.device) self.emit_data() @pyqtSlot(int) def set_elapsed_time(self, elapsed_time): self.settings.child('acquisition', 'elapsed_time').setValue(elapsed_time/1000) # in s def check_acquisition(self): if not self.controller.TH260_CTCStatus(self.device): elapsed_time = self.controller.TH260_GetElapsedMeasTime(self.device) # in ms self.set_elapsed_time(elapsed_time) self.emit_data_tmp() else: self.acq_timer.stop() QtWidgets.QApplication.processEvents() # this to be sure the timer is not fired while emitting data self.controller.TH260_StopMeas(self.device) QtWidgets.QApplication.processEvents() #this to be sure the timer is not fired while emitting data self.emit_data() def get_rates(self): vals = [] sync_rate = self.controller.TH260_GetSyncRate(self.device) vals.append([sync_rate/1000]) for ind_channel in range(self.Nchannels): if self.settings.child('line_settings', 'ch{:d}_settings'.format(ind_channel+1), 'enabled').value(): rate = self.controller.TH260_GetCountRate(self.device, ind_channel) vals.append([rate/1000]) else: vals.append([0]) self.emit_rates(vals) return vals def emit_rates(self,vals): self.settings.child('acquisition', 'rates', 'syncrate').setValue(vals[0][0]*1000) for ind_channel in range(self.Nchannels): self.settings.child('acquisition', 'rates', 'ch{:d}_rate'.format(ind_channel+1)).setValue(vals[ind_channel+1][0]*1000) if self.settings.child('acquisition', 'rates', 'large_display').value(): self.emit_status(ThreadCommand('lcd', [vals[1:]])) return vals def set_sync_channel(self, param): """ Set the channel or sync settings (level, zerox, ...) Parameters ---------- param: (Parameter) either ch1_settings children, ch2_settings children or sync_settings children """ if param.parent().name() == 'sync_settings': source = 'sync' source_str = 'sync' elif param.parent().name() == 'ch1_settings': source = 0 source_str = 'CH1' elif param.parent().name() == 'ch2_settings': source = 1 source_str = 'CH2' if param.name() == 'divider': self.controller.TH260_SetSyncDiv(self.device, param.value()) elif param.name() == 'zerox' or param.name() == 'level': level = param.parent().child(('level')).value() zerox = param.parent().child(('zerox')).value() if source == 'sync': self.controller.TH260_SetSyncCFD(self.device, level, zerox) else: self.controller.TH260_SetInputCFD(self.device, source, level, zerox) elif param.name() == 'offset': if source == 'sync': self.controller.TH260_SetSyncChannelOffset(self.device,param.value()) else: self.controller.TH260_SetInputChannelOffset(self.device, source, param.value()) elif param.name() == 'enabled': self.controller.TH260_SetInputChannelEnable(self.device, source, enable=param.value()) self.channels_enabled[source_str]['enabled'] = param.value() for par in param.parent().children(): if par != param: par.setOpts(enabled=param.value()) elif param.name() == 'deadtime': code = param.opts['limits'].index(param.value()) self.controller.TH260_SetInputDeadTime(self.device, source, code) def set_get_resolution(self, wintype='resolution'): """ Set and get right values of bin time resolution number of bins and gloabl time window Parameters ---------- wintype: (str) either 'nbins' or 'resolution' or 'both' Returns ------- """ base_res, max_bin_size_code = self.controller.TH260_GetBaseResolution(self.device) # bas res in ps self.settings.child('acquisition', 'timings', 'base_resolution').setValue(base_res) resolution = self.settings.child('acquisition', 'timings', 'resolution').value() # in ns Nbins = self.settings.child('acquisition', 'timings', 'nbins').value() bin_size_code = int(np.log(resolution * 1000 / base_res)/np.log(2)) if bin_size_code < 0: bin_size_code = 0 if wintype =='resolution' or wintype =='both': if bin_size_code >= max_bin_size_code: bin_size_code = max_bin_size_code-1 #see SetBinning documentation self.controller.TH260_SetBinning(self.device, bin_size_code) resolution = 2**bin_size_code * base_res / 1000 resolution=self.controller.TH260_GetResolution(self.device)/1000 self.settings.child('acquisition', 'timings', 'resolution').setValue(resolution) if wintype =='nbins' or wintype =='both': mode = self.settings.child('acquisition', 'acq_type').value() if mode == 'Counting' or mode == 'Histo': Nbins = self.controller.TH260_SetHistoLen(self.device, int(np.log(Nbins/1024)/np.log(2))) self.settings.child('acquisition', 'timings', 'nbins').setValue(Nbins) N = len([k for k in self.channels_enabled.keys() if self.channels_enabled[k]['enabled']]) if mode == 'Counting': self.datas = [np.zeros((1,), dtype=np.uint32) for ind in range(N)] elif mode == 'Histo' or mode == 'T3': self.datas = [np.zeros((Nbins,), dtype=np.uint32) for ind in range(N)] self.get_xaxis() self.emit_x_axis() self.data_pointers = [data.ctypes.data_as(ctypes.POINTER(ctypes.c_uint32)) for data in self.datas] self.settings.child('acquisition', 'timings', 'window').setValue(Nbins*resolution/1e6) # in ms self.set_acq_mode(self.settings.child('acquisition', 'acq_type').value()) def update_timer(self): """ """ self.get_rates() warn = self.controller.TH260_GetWarnings(self.device) if warn != '': self.emit_status(ThreadCommand('Update_Status', [warn, ''])) def close(self): """ """ self.stop() QtWidgets.QApplication.processEvents() self.datas = None self.data_pointers = None self.general_timer.stop() QtWidgets.QApplication.processEvents() #QThread.msleep(1000) self.controller.TH260_CloseDevice(self.device) if self.h5file is not None: if self.h5file.isopen: self.h5file.flush() self.h5file.close() def get_xaxis(self): """ Obtain the horizontal axis of the data. Returns ------- 1D numpy array Contains a vector of integer corresponding to the horizontal camera pixels. """ if self.controller is not None: res = self.settings.child('acquisition', 'timings', 'resolution').value() Nbins = self.settings.child('acquisition', 'timings', 'nbins').value() self.x_axis = dict(data=np.linspace(0, (Nbins-1)*res, Nbins), label='Time', units='ns') else: raise(Exception('Controller not defined')) return self.x_axis def get_yaxis(self): """ Obtain the vertical axis of the image. Returns ------- 1D numpy array Contains a vector of integer corresponding to the vertical camera pixels. """ if self.controller is not None: pass else: raise(Exception('Controller not defined')) return self.y_axis def grab_data(self, Naverage=1, **kwargs): """ Start new acquisition in two steps : * Initialize data: self.datas for the memory to store new data and self.data_average to store the average data * Start acquisition with the given exposure in ms, in "1d" or "2d" mode =============== =========== ============================= **Parameters** **Type** **Description** Naverage int Number of images to average =============== =========== ============================= See Also -------- DAQ_utils.ThreadCommand """ try: self.acq_done = False mode = self.settings.child('acquisition', 'acq_type').value() if mode == 'Counting': QThread.msleep(100) #sleeps 100ms otherwise the loop is too fast self.emit_data() elif mode == 'Histo': time_acq = int(self.settings.child('acquisition', 'acq_time').value()*1000) # in ms self.controller.TH260_ClearHistMem(self.device) self.controller.TH260_StartMeas(self.device, time_acq) self.acq_timer.start() #self.poll_acquisition() elif mode == 'T3': self.ind_reading = 0 self.Nx = 1 self.Ny = 1 self.init_h5file() self.datas = np.zeros((self.settings.child('acquisition', 'timings', 'nbins').value(),), dtype=np.float64) self.init_histo_group() time_acq = int(self.settings.child('acquisition', 'acq_time').value() * 1000) # in ms self.general_timer.stop() t3_reader = T3Reader(self.device, self.controller, time_acq, self.Nchannels) self.detector_thread = QThread() t3_reader.moveToThread(self.detector_thread) t3_reader.data_signal[dict].connect(self.populate_h5) self.stop_tttr.connect(t3_reader.stop_TTTR) self.detector_thread.t3_reader = t3_reader self.detector_thread.start() self.detector_thread.setPriority(QThread.HighestPriority) self.time_t3 = time.perf_counter() self.time_t3_rate = time.perf_counter() t3_reader.start_TTTR() except Exception as e: self.emit_status(ThreadCommand('Update_Status', [getLineInfo() + str(e), "log"])) def init_histo_group(self): histo_group = self.h5file.create_group(self.h5file.root, 'histograms') self.histo_array = self.h5file.create_carray(histo_group, 'histogram', obj=self.datas, title='histogram') x_axis = self.get_xaxis() xarray = self.h5file.create_carray(histo_group, "x_axis", obj=x_axis['data'], title='x_axis') xarray.attrs['shape'] = xarray.shape xarray.attrs['type'] = 'signal_axis' xarray.attrs['data_type'] = '1D' self.histo_array._v_attrs['data_type'] = '1D' self.histo_array._v_attrs['type'] = 'histo_data' self.histo_array._v_attrs['shape'] = self.datas.shape def get_new_file_name(self): today = datetime.datetime.now() date = today.strftime('%Y%m%d') year = today.strftime('%Y') curr_dir = os.path.join(self.settings.child('acquisition', 'base_path').value(), year, date) if not os.path.isdir(curr_dir): os.mkdir(curr_dir) with os.scandir(curr_dir) as it: files = [] for entry in it: if entry.name.startswith('tttr_data') and entry.is_file(): files.append(entry.name) files.sort() if files == []: index = 0 else: index = int(os.path.splitext(files[-1])[0][-3:])+1 file = 'tttr_data_{:03d}'.format(index) return file, curr_dir def init_h5file(self): file, curr_dir = self.get_new_file_name() self.settings.child('acquisition', 'temp_file').setValue(file+'.h5') self.h5file = tables.open_file(os.path.join(curr_dir, file+'.h5'), mode='w') h5group = self.h5file.root h5group._v_attrs['settings'] = customparameter.parameter_to_xml_string(self.settings) h5group._v_attrs.type = 'detector' h5group._v_attrs['format_name'] = 'timestamps' channels_index = [self.channels_enabled[k]['index'] for k in self.channels_enabled.keys() if self.channels_enabled[k]['enabled']] self.marker_array = self.h5file.create_earray(self.h5file.root, 'markers', tables.UInt8Atom(), (0,), title='markers') self.marker_array._v_attrs['data_type'] = '1D' self.marker_array._v_attrs['type'] = 'tttr_data' self.nanotimes_array = self.h5file.create_earray(self.h5file.root, 'nanotimes', tables.UInt16Atom(), (0,), title='nanotimes') self.nanotimes_array._v_attrs['data_type'] = '1D' self.nanotimes_array._v_attrs['type'] = 'tttr_data' self.timestamp_array = self.h5file.create_earray(self.h5file.root, 'timestamps', tables.UInt64Atom(), (0,), title='timestamps') self.timestamp_array._v_attrs['data_type'] = '1D' self.timestamp_array._v_attrs['type'] = 'tttr_data' # self.raw_datas_array = self.h5file.create_earray(self.h5file.root, 'raw_data', tables.UInt64Atom(), (0,), # title='raw_data') # self.raw_datas_array._v_attrs['data_type'] = '1D' # self.raw_datas_array._v_attrs['type'] = 'tttr_data' @pyqtSlot(dict) def populate_h5(self, datas): """ Parameters ---------- datas: (dict) dict(data=self.buffer[0:nrecords], rates=rates, elapsed_time=elapsed_time) Returns ------- """ if datas['data'] != []: # self.raw_datas_array.append(datas['data']) # self.raw_datas_array._v_attrs['shape'] = self.raw_datas_array.shape detectors, timestamps, nanotimes = pqreader.process_t3records( datas['data'], time_bit=10, dtime_bit=15, ch_bit=6, special_bit=True, ovcfunc=pqreader._correct_overflow_nsync) self.timestamp_array.append(timestamps) self.timestamp_array._v_attrs['shape'] = self.timestamp_array.shape self.nanotimes_array.append(nanotimes) self.nanotimes_array._v_attrs['shape'] = self.nanotimes_array.shape self.marker_array.append(detectors) self.marker_array._v_attrs['shape'] = self.marker_array.shape self.h5file.flush() if time.perf_counter() - self.time_t3_rate > 0.5: self.emit_rates(datas['rates']) self.set_elapsed_time(datas['elapsed_time']) self.settings.child('acquisition', 'rates', 'records').setValue(self.nanotimes_array.shape[0]) self.time_t3_rate = time.perf_counter() elif time.perf_counter() - self.time_t3 > 5: self.datas += np.squeeze(self.process_histo_from_h5(Nx=self.Nx, Ny=self.Ny)) self.histo_array[:] = self.datas self.emit_data_tmp() self.time_t3 = time.perf_counter() if datas['acquisition_done']: self.datas += np.squeeze(self.process_histo_from_h5(Nx=self.Nx, Ny=self.Ny)) self.histo_array[:] = self.datas self.emit_data() def stop(self): """ stop the camera's actions. """ try: self.acq_timer.stop() QtWidgets.QApplication.processEvents() self.controller.TH260_StopMeas(self.device) QtWidgets.QApplication.processEvents() self.general_timer.start() except Exception as e: self.emit_status(ThreadCommand('Update_Status', [getLineInfo()+ str(e), "log"])) return ""
class PrettyWidget(QOpenGLWidget): started = False finished = False timer = None port_trigger = None current_index = None presenting = None paused = False delay = 0 # used when pausing cross_delay = 2 cross_color = 'green' sound = {'start': None, 'end': None} fast_tsv = None fast_i = None fast_t = None def __init__(self, parameters): super().__init__() self.P = parameters if self.P['SOUND']['PLAY']: self.sound = { 'start': QSound(str(SOUNDS_DIR / self.P['SOUND']['START'])), 'end': QSound(str(SOUNDS_DIR / self.P['SOUND']['END'])), } self.open_serial() try: port_input = Serial(self.P['COM']['INPUT']['PORT'], baudrate=self.P['COM']['INPUT']['BAUDRATE']) except SerialException: port_input = None lg.warning('could not open serial port to read input') _warn_about_ports() self.port_input = port_input self.start_serial_input() lg.info('Reading images') self.stimuli = read_stimuli(self.P) lg.info('Reading images: finished') # background color self.bg_color = QColor(self.P['BACKGROUND']) self.show() self.time = QTime() self.timer = QTimer() self.timer.setTimerType(Qt.PreciseTimer) self.timer.timeout.connect(self.check_time) self.setCursor(Qt.BlankCursor) self.setGeometry(200, 200, 1000, 1000) if self.P['FULLSCREEN']: self.showFullScreen() else: self.showNormal() self.serial(250) if self.P['DATAGLOVE']: self.open_dataglove() def open_dataglove(self): lg.info('Opening dataglove') self.glove = [] if FiveDTGlove.gloveDLL is None: # could not initialize DLL return for i in range(2): # TODO: we should use scan_USB but I get error logname = self.P['logname'] DATAGLOVE_LOG = logname.parent / (logname.stem + f'_dataglove{i}.txt') new_glove = FiveDTGlove(DATAGLOVE_LOG) try: new_glove.open(f'USB{i}'.encode()) except IOError: pass else: self.glove.append(new_glove) def open_serial(self): try: self.port_trigger = Serial( self.P['COM']['TRIGGER']['PORT'], baudrate=self.P['COM']['TRIGGER']['BAUDRATE']) except SerialException: lg.warning('could not open serial port for triggers') _warn_about_ports() def serial(self, trigger): """trigger needs to be between 0 and 255. If none, then it closes the serial port""" if trigger is None: if self.port_trigger is not None: self.port_trigger.close() else: lg.debug(f'Sending trigger {trigger:03d}') if self.port_trigger is not None: try: self.port_trigger.write(pack('>B', trigger)) except Exception: lg.warning('could not write to serial port') self.open_serial() def paintGL(self): window_rect = self.rect() rect_x = window_rect.center().x() + self.P['SCREEN']['RIGHTWARDS'] rect_y = window_rect.center().y() + self.P['SCREEN']['DOWNWARDS'] qp = QPainter() qp.begin(self) qp.fillRect(window_rect, self.bg_color) if self.paused: self.draw_text(qp, 'PAUSED') elif self.current_index is None: self.draw_text(qp, 'READY') else: if self.fast_tsv is not None: i_pixmap = where(self.fast_tsv['onset'] <= self.fast_i)[0][-1] if self.fast_i == self.fast_tsv['onset'][-1]: self.fast_tsv = None self.fast_i = None self.frameSwapped.disconnect() else: if self.fast_tsv['stim_file'][i_pixmap] is not None: current_pixmap = self.fast_tsv['stim_file'][i_pixmap] image_rect = current_pixmap.rect() size = image_rect.size().scaled( window_rect.size(), Qt.KeepAspectRatio) img_origin_x = rect_x - int(size.width() / 2) img_origin_y = rect_y - int(size.height() / 2) qp.beginNativePainting() qp.drawPixmap(img_origin_x, img_origin_y, size.width(), size.height(), current_pixmap) qp.endNativePainting() lg.debug(f'FAST IMAGE #{self.fast_i}') self.fast_i += 1 else: current_pixmap = self.stimuli['stim_file'][self.current_index] if isinstance(current_pixmap, Path): self.fast_tsv = read_fast_stimuli(current_pixmap) self.fast_i = 0 elif isinstance(current_pixmap, str): self.draw_text(qp, current_pixmap) if current_pixmap == 'END': if not self.finished: self.draw_text(qp, 'END') self.finished = True self.serial(None) if self.sound['end'] is not None: self.sound['end'].play() else: image_rect = current_pixmap.rect() size = image_rect.size().scaled(window_rect.size(), Qt.KeepAspectRatio) img_origin_x = rect_x - int(size.width() / 2) img_origin_y = rect_y - int(size.height() / 2) qp.drawPixmap(img_origin_x, img_origin_y, size.width(), size.height(), current_pixmap) self.drawText(qp) qp.end() if self.presenting is not None: # send triggers and log info right after the image was presented trial = self.stimuli[self.presenting] lg.info('Presenting ' + str(trial['trial_name'])) self.serial(trial['trial_type']) self.presenting = None if self.fast_i == 0: self.input_thread.msleep(1000) self.frameSwapped.connect(self.update) self.update() def drawText(self, qp): if not self.P['FIXATION']['ACTIVE']: return elapsed = self.time.elapsed() + self.delay if elapsed > self.cross_delay: if self.cross_color == 'green': self.cross_color = 'red' self.serial(240) else: self.cross_color = 'green' self.serial(241) self.cross_delay += random() * 5000 + 2000 color = QColor(self.cross_color) qp.setPen(color) qp.setFont(QFont('SansSerif', 50)) qp.drawText(*self.center_rect(), Qt.AlignCenter, '+') def draw_text(self, qp, text): qp.setPen(QColor(40, 40, 255)) qp.setFont(QFont('Decorative', 50)) qp.drawText(*self.center_rect(), Qt.AlignCenter, text) def center_rect(self): window_rect = self.rect() width = window_rect.width() height = window_rect.height() img_origin_x = window_rect.center().x() - int( width / 2) + self.P['SCREEN']['RIGHTWARDS'] img_origin_y = window_rect.center().y() - int( height / 2) + self.P['SCREEN']['DOWNWARDS'] return (img_origin_x, img_origin_y, width, height) def check_time(self): elapsed = self.time.elapsed() + self.delay if self.P['DATAGLOVE']: for glove in self.glove: if glove.new_data: glove_data = glove.get_sensor_raw_all() glove.f.write(datetime.now().strftime('%H:%M:%S.%f') + '\t' + '\t'.join([f'{x}' for x in glove_data]) + '\n') index_image = where((self.stimuli['onset'] * 1e3) <= elapsed)[0] if len(index_image) == len(self.stimuli): self.stop() elif len(index_image) > 0: index_image = index_image[-1] if index_image != self.current_index: self.current_index = index_image if index_image is not None: self.presenting = index_image self.update() def start(self): if self.started: return lg.warning('Starting') self.started = True self.current_index = -1 self.time.start() self.timer.start(self.P['QTIMER_INTERVAL']) if self.sound['start'] is not None: self.sound['start'].play() def start_serial_input(self): self.input_worker = SerialInputWorker() self.input_worker.port_input = self.port_input self.input_thread = QThread(parent=self) self.input_thread.started.connect(self.input_worker.start_reading) self.input_worker.signal_to_main.connect(self.read_serial_input) self.input_worker.moveToThread(self.input_thread) self.input_thread.start() self.input_thread.setPriority(QThread.LowestPriority) def read_serial_input(self, number): lg.info(f'Received input trigger {number}') if self.P['COM']['INPUT']['START_TRIGGER'] == number: self.start() self.serial(254) def stop(self): lg.info('Stopping task') if self.timer is not None: self.timer.stop() # nice wayt to stop the worker self.input_worker.running = False self.input_thread.terminate() sleep(1) app.exit(0) def pause(self): if not self.paused: self.paused = True self.delay += self.time.elapsed() self.timer.stop() self.serial(253) lg.info('Pausing the task') else: self.paused = False self.time.restart() self.serial(254) lg.info('Pause finished: restarting the task') self.timer.start(self.P['QTIMER_INTERVAL']) self.update() def keyPressEvent(self, event): if isinstance(event, QKeyEvent): if event.key() in (Qt.Key_Enter, Qt.Key_Return): self.start() elif event.key() == Qt.Key_Space: self.pause() elif event.key() == Qt.Key_Escape: lg.info('Pressed Escape') self.serial(255) self.stop() elif event.key() == Qt.Key_PageUp: self.showFullScreen() elif event.key() == Qt.Key_PageDown: self.showNormal() else: super().keyPressEvent(event) else: super().keyPressEvent(event) def mouseDoubleClickEvent(self, event): if isinstance(event, QMouseEvent): if event.pos().x() > self.rect().center().x(): if not self.started: self.start() else: self.pause() else: if self.isFullScreen(): self.showNormal() else: self.showFullScreen() else: super().mouseDoubleClickEvent(event) def closeEvent(self, event): self.stop() event.accept()
class MainWindowController(QMainWindow): __storyData: ActionBundle # Type hints for .ui objects - same name, do not change here! storyStatementList: QTableWidget comboLogLevel: QComboBox btnPlay: QPushButton btnLoadStory: QPushButton btnSaveStory: QPushButton # Signals sigStoryDataChanged = pyqtSignal() # Must NOT be in constructor ! @property def storyData(self): return self.__storyData @storyData.setter def storyData(self, bundle: ActionBundle): self.__storyData = bundle self.sigStoryDataChanged.emit() def __init__(self, *args, **kwargs): logger.debug("Initializing class %s", __class__.__name__) # Order of steps is important from here. # 1. Load UI file super(MainWindowController, self).__init__(*args, **kwargs) uic.loadUi("mainWindow.ui", self) # Loads all widgets of .ui into my instance # 2. Init priority properties # Set StoryLogHandler which is responsible for logging into a GUI-Widget self.storyLogHandler = StoryLogHandler() # Thread handling. We later need to access the worker while running, # to be able to force stop it. So preserve window lifecycle. self.storyWorker = None # Keep thread lifecycle, so we don't need to create any new as long as this window lives self.workerThread = QThread() # Bind GUI elements. self.defineConnections() # 3. Init general properties which may depend on step 2. self.storyData = ActionBundle.createNew() # 4. Init UI self.initView() def initView(self): self.setStoryStatementListLayout() self.initProgressbar(isVisible=False) self.defineComboLogLevel() self.btnPlay.setShortcut(QKeySequence("Ctrl+R")) self.btnLoadStory.setShortcut(QKeySequence("Ctrl+L")) self.btnSaveStory.setShortcut(QKeySequence("Ctrl+S")) def defineConnections(self): self.storyLogHandler.sigLoggerEmitted.connect(self.addLogText) self.sigStoryDataChanged.connect(self.setRunButtonState) self.sigStoryDataChanged.connect(self.setStoryTitle) self.sigStoryDataChanged.connect(self.setStoryStatementListData) self.btnLoadStory.clicked.connect(self.onClickedLoadStory) self.btnPlay.clicked.connect(self.onClickedRun) self.btnClearLog.clicked.connect(self.onClickedClearLog) self.comboLogLevel.currentIndexChanged.connect( self.onComboLogLevelChanged) def addStoryLogHandlerToLogger(self, level): """Add dedicated log handler to some package loggers (hence all its children, too) for routing logs into the GUI widget. NOTE: We MUST NOT change log levels of the loggers themselves here! :param level: The log level :return: None """ packagesToChange = (__name__, "storage", "story", "network", "selenium") self.storyLogHandler.setLevel(level) for package in packagesToChange: tmpLogger = clog.getLogger( package) # Side effect: sets new logger if not exists! tmpLogger.removeHandler( self.storyLogHandler) # removing only works by reference! tmpLogger.addHandler(self.storyLogHandler) def setStoryTitle(self): self.lblStoryTitle.setText(f"Loaded Story: {self.storyData.name}") def setStoryStatementListLayout(self): # Set style of story list self.storyStatementList.setShowGrid(False) self.storyStatementList.setStyleSheet( 'QTableView::item {border-right: 1px solid #d6d9dc;}') # Set the story headers headerLabels = [ "Action", "Property", "Value", "Search Data", "Search Strategy", "Result" ] self.storyStatementList.setColumnCount(len(headerLabels)) self.storyStatementList.setHorizontalHeaderLabels(headerLabels) # Fonts headerFont = QFont() headerFont.setPointSize(15) self.storyStatementList.horizontalHeader().setFont(headerFont) self.storyStatementList.verticalHeader().setFont(headerFont) # Fit horizontal sizes headerH: QHeaderView = self.storyStatementList.horizontalHeader() headerH.setSectionResizeMode(0, QHeaderView.ResizeToContents) headerH.setSectionResizeMode(1, QHeaderView.ResizeToContents) headerH.setSectionResizeMode(2, QHeaderView.Interactive) headerH.setSectionResizeMode(3, QHeaderView.Interactive) headerH.setSectionResizeMode(4, QHeaderView.ResizeToContents) headerH.setSectionResizeMode(5, QHeaderView.Stretch) def setStoryStatementListData(self): numberOfStatements = len(self.storyData.actions) self.storyStatementList.setRowCount(numberOfStatements) # Fill for row, statement in enumerate(self.storyData.actions): # Set cells # col id 0 content = QTableWidgetItem(statement.command.readable) content.setTextAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter) self.storyStatementList.setItem(row, 0, content) # col id 1, 2 content = ("", "") if isinstance(statement, TextSendable): content = (ActionKey.TEXT_TO_SEND.readable, statement.textToSend) elif isinstance(statement, Waitable): content = (ActionKey.MAX_WAIT.readable, str(statement.maxWait)) self.storyStatementList.setItem(row, 1, QTableWidgetItem(content[0])) self.storyStatementList.setItem(row, 2, QTableWidgetItem(content[1])) # col id 3, 4 content = ("", "") if isinstance(statement, Searchable): content = (statement.searchConditions.identifier, statement.searchConditions.searchStrategy.readable) self.storyStatementList.setItem(row, 3, QTableWidgetItem(content[0])) self.storyStatementList.setItem(row, 4, QTableWidgetItem(content[1])) # col id 5 is set by setStoryStatementIndicatorsToDefault() self.setStoryStatementIndicatorsToDefault() def setStoryStatementIndicatorsToDefault(self): for row, statement in enumerate(self.storyData.actions): # Vertical headers self.setStoryStatementVerticalHeader(row=row) # Results self.setStoryStatementResult(row=row, text="") def setStoryStatementVerticalHeader(self, row: int, rgb: (int, int, int) = None): if row < 0: return counterStr = str(row + 1) headerItem = QTableWidgetItem(counterStr) if rgb: r, g, b = rgb[0], rgb[1], rgb[2] brush = QBrush(QColor(r, g, b)) headerItem.setBackground(brush) self.storyStatementList.setVerticalHeaderItem(row, headerItem) def setStoryStatementResult(self, row, text: str): if row < 0: return COLUMN = 5 content = QTableWidgetItem(text) content.setTextAlignment(QtCore.Qt.AlignCenter) content.setForeground(QBrush(QColor(94, 94, 94))) self.storyStatementList.setItem(row, COLUMN, content) def defineComboLogLevel(self): self.comboLogLevel.disconnect( ) # Avoid multiple triggers while setting up self.comboLogLevel.clear() self.comboLogLevel.addItem("Debug", clog.DEBUG) self.comboLogLevel.addItem("Info", clog.INFO) self.comboLogLevel.addItem("Warning", clog.WARN) self.comboLogLevel.addItem("Error", clog.ERROR) self.comboLogLevel.addItem("Critical", clog.CRITICAL) self.comboLogLevel.currentIndexChanged.connect( self.onComboLogLevelChanged) self.comboLogLevel.setCurrentIndex(1) def addLogText(self, logText): self.logList.appendPlainText(logText) self.logList.moveCursor(QtGui.QTextCursor.End) # implies scrolling def initProgressbar(self, isVisible: bool, maximum: int = 100, showText: bool = False): if isVisible: self.progressBar.setMaximum(maximum) self.progressBar.show() else: self.progressBar.hide() self.setProgressbar(0) self.progressBar.setTextVisible(showText) def setProgressbar(self, val): self.progressBar.setValue(val) def setRunButtonState(self): savedShortcut = self.btnPlay.shortcut() # We must restore that below isEnabled = len(self.storyData.actions) > 0 self.btnPlay.setEnabled(isEnabled) if self.workerThread.isRunning(): self.btnPlay.setText("Stop") else: self.btnPlay.setText("Run") # Documentation says: Shortcut gets deleted after setText is called. Restore: self.btnPlay.setShortcut(savedShortcut) def runStory(self): actionCount = len(self.storyData.actions) if actionCount == 0: return self.initProgressbar(isVisible=True, maximum=actionCount, showText=True) # Setup storyWorker & thread self.storyWorker = StoryWorker(self.storyData, doHeadless=True) self.workerThread.started.connect(self.storyWorker.work) self.storyWorker.sigStarted.connect(self.setRunButtonState) self.storyWorker.sigStarted.connect( self.setStoryStatementIndicatorsToDefault) self.storyWorker.sigCurrentAction.connect(self.onStoryRunNextStatement) self.storyWorker.sigCompleted.connect(self.onStoryRunCompleted) # Only delete worker object inside the thread after it's been finished work. # Notice: We do NOT mark the workerThread for deletion as we will reuse it. self.storyWorker.sigCompleted.connect(self.storyWorker.deleteLater) # Move worker to thread and start working: self.storyWorker.moveToThread( self.workerThread) # Move Worker object to Thread object # Start work self.workerThread.start() self.workerThread.setPriority(QThread.HighPriority) def openFileDialog(self, dirPath: pl.Path) -> pl.Path: dirStr = "" if dirPath is not None and dirPath.is_dir(): dirStr = str(dirPath) # Setup and show dialog dlg = QFileDialog() options = dlg.HideNameFilterDetails pathStr, what = dlg.getOpenFileName( parent=self, caption="Open a story", directory=dirStr, filter="Story Files (*.json);; All Files (*)", options=options) # Transform path string of selected file if pathStr: return pl.Path(pathStr) # --------------------------------------------------------------------------------- # Callbacks # --------------------------------------------------------------------------------- def onClickedLoadStory(self, _, dao=JSONActionBundleDao()): path = self.openFileDialog(dao.connection.path.parent) if path: try: dao.connection.path = path with dao as connectedDao: self.storyData = connectedDao.loadAll() except Exception as e: logger.error("Failed to load story. %s", e, exc_info=True) def onClickedRun(self, _): if self.workerThread.isRunning(): logger.info("User stopped story execution.") self.storyWorker.stop() else: self.runStory() def onClickedClearLog(self, _): self.logList.clear() def onComboLogLevelChanged(self): currentLevel = self.comboLogLevel.currentData() self.addStoryLogHandlerToLogger(currentLevel) logger.debug("Log level changed - new level: %s", currentLevel) def onStoryRunNextStatement(self, actionProgress: int): row = actionProgress - 1 self.setStoryStatementResult(row, text="ok") self.setStoryStatementVerticalHeader(row, rgb=(26, 173, 102)) # mark green self.setProgressbar(actionProgress) def onStoryRunCompleted(self): if not self.workerThread.isRunning(): return # Stop storyWorker's thread and do cleanups logger.debug( "Work completed. Waiting for storyWorker thread to quit...") self.workerThread.quit() self.workerThread.wait() if self.workerThread.isFinished(): self.initProgressbar(isVisible=False) self.setRunButtonState() logger.debug("Worker thread did quit.\n")
class DAQ_2DViewer_FLIM(DAQ_1DViewer_TH260): """ ==================== ================== **Atrributes** **Type** *params* dictionnary list *hardware_averaging* boolean *x_axis* 1D numpy array *ind_data* int ==================== ================== See Also -------- utility_classes.DAQ_Viewer_base """ params = DAQ_1DViewer_TH260.params + stage_params stop_scanner = pyqtSignal() start_tttr_scan = pyqtSignal() def __init__(self, parent=None, params_state=None): super(DAQ_2DViewer_FLIM, self).__init__(parent, params_state) #initialize base class with commom attributes and methods self.settings.child('acquisition', 'acq_type').setOpts(limits=['Counting', 'Histo', 'T3', 'FLIM']) self.settings.child('acquisition', 'acq_type').setValue('Histo') self.stage = None self.scan_parameters = None self.x_axis = None self.y_axis = None self.Nx = 1 self.Ny = 1 self.signal_axis = None def commit_settings(self, param): if param.name() not in custom_tree.iter_children(self.settings.child(('stage_settings'))): super(DAQ_2DViewer_FLIM, self).commit_settings(param) else: if param.name() == 'time_interval': self.stage._get_read() self.stage.set_time_interval(Time(param.value(), unit='m')) # set time interval between pixels elif param.name() == 'show_navigator': self.emit_status(ThreadCommand('show_navigator')) param.setValue(False) def emit_data(self): """ """ try: mode = self.settings.child('acquisition', 'acq_type').value() if mode == 'Counting' or mode == 'Histo' or mode == 'T3': super(DAQ_2DViewer_FLIM, self).emit_data() elif mode == 'FLIM': self.stop_scanner.emit() self.data_grabed_signal.emit([OrderedDict(name='TH260', data=self.datas, type='DataND', nav_axes=(0, 1), nav_x_axis=self.get_nav_xaxis(), nav_y_axis=self.get_nav_yaxis(), xaxis=self.get_xaxis())]) self.stop() except Exception as e: self.emit_status(ThreadCommand('Update_Status', [getLineInfo() + str(e), 'log'])) def emit_data_tmp(self): """ """ try: mode = self.settings.child('acquisition', 'acq_type').value() if mode == 'Counting' or mode == 'Histo' or mode == 'T3': super(DAQ_2DViewer_FLIM, self).emit_data_tmp() elif mode == 'FLIM': self.data_grabed_signal_temp.emit([OrderedDict(name='TH260', data=self.datas, type='DataND', nav_axes=(0, 1), nav_x_axis=self.get_nav_xaxis(), nav_y_axis=self.get_nav_yaxis(), xaxis=self.get_xaxis())]) except Exception as e: self.emit_status(ThreadCommand('Update_Status', [getLineInfo() + str(e), 'log'])) def process_histo_from_h5(self, Nx=1, Ny=1, channel=0, marker=65): mode = self.settings.child('acquisition', 'acq_type').value() if mode == 'Counting' or mode == 'Histo' or mode == 'T3': datas = super(DAQ_2DViewer_FLIM, self).process_histo_from_h5(Nx, Ny, channel) return datas else: markers_array = self.h5file.get_node('/markers') ind_lines = np.squeeze(np.where(self.h5file.get_node('/markers')[self.ind_reading:] == marker)) if ind_lines.size == 0: return np.zeros_like(self.datas) # basically do nothing else: ind_last_line = ind_lines[-1] print(self.ind_reading, ind_last_line) markers = markers_array[self.ind_reading:self.ind_reading+ind_last_line] nanotimes = self.h5file.get_node('/nanotimes')[self.ind_reading:self.ind_reading+ind_last_line] nbins = self.settings.child('acquisition', 'timings', 'nbins').value() datas = extract_TTTR_histo_every_pixels(nanotimes, markers, marker=marker, Nx=Nx, Ny=Ny, Ntime=nbins, ind_line_offset=self.ind_reading, channel=channel) self.ind_reading = ind_last_line return datas def ini_detector(self, controller=None): """ See Also -------- DAQ_utils.ThreadCommand, hardware1D.DAQ_1DViewer_Picoscope.update_pico_settings """ self.status.update(edict(initialized=False, info="", x_axis=None, y_axis=None, controller=None)) try: self.status = super(DAQ_2DViewer_FLIM, self).ini_detector(controller) self.ini_stage() self.status.x_axis = self.x_axis self.status.initialized = True self.status.controller = self.controller return self.status except Exception as e: self.status.info = getLineInfo() + str(e) self.status.initialized = False return self.status @pyqtSlot(ScanParameters) def update_scanner(self, scan_parameters): self.scan_parameters = scan_parameters self.x_axis = self.scan_parameters.axis_2D_1 self.Nx = self.x_axis.size self.y_axis = self.scan_parameters.axis_2D_2 self.Ny = self.y_axis.size def get_nav_xaxis(self): """ """ return self.scan_parameters.axis_2D_1 def get_nav_yaxis(self): """ """ return self.scan_parameters.axis_2D_2 def ini_stage(self): if self.settings.child('stage_settings', 'stage_type').value() == 'PiezoConcept': self.stage = PiezoConcept() self.controller.stage = self.stage self.stage.init_communication(self.settings.child('stage_settings', 'com_port').value()) controller_id = self.stage.get_controller_infos() self.settings.child('stage_settings', 'controller_id').setValue(controller_id) self.stage.set_time_interval(Time(self.settings.child('stage_settings', 'time_interval').value(), unit='m')) #set time interval between pixels #set the TTL outputs for each displacement of first axis self.stage.set_TTL_state(1, self.settings.child('stage_settings', 'stage_x', 'stage_x_axis').value(), 'output', dict(type='start')) self.move_abs(0, 'X') self.move_abs(0, 'Y') @pyqtSlot(float, float) def move_at_navigator(self, posx, posy): self.move_abs(posx, 'X') self.move_abs(posy, 'Y') def move_abs(self, position, axis='X'): offset = self.settings.child('stage_settings', 'stage_x', 'offset_x').value() if self.settings.child('stage_settings', 'stage_x', 'stage_x_direction').value() == 'Normal': posi = position + offset else: posi = -position + offset if axis == 'X': ax = self.settings.child('stage_settings', 'stage_x', 'stage_x_axis').value() else: ax = self.settings.child('stage_settings', 'stage_y', 'stage_y_axis').value() pos = Position(ax, int(posi * 1000), unit='n') out = self.stage.move_axis('ABS', pos) def close(self): """ """ super(DAQ_2DViewer_FLIM, self).close() self.stage.close_communication() def set_acq_mode(self, mode='FLIM', update=False): """ herited method Change the acquisition mode (histogram for mode=='Counting' and 'Histo' or T3 for mode == 'FLIM') Parameters ---------- mode Returns ------- """ # check enabled channels labels = [k for k in self.channels_enabled.keys() if self.channels_enabled[k]['enabled']] N = len(labels) if mode != self.actual_mode or update: if mode == 'Counting' or mode == 'Histo' or mode == 'T3': super(DAQ_2DViewer_FLIM, self).set_acq_mode(mode, update) elif mode == 'FLIM': self.emit_status(ThreadCommand('show_scanner')) QtWidgets.QApplication.processEvents() self.emit_status(ThreadCommand('show_navigator')) self.controller.TH260_Initialize(self.device, mode=3) # mode T3 self.controller.TH260_SetMarkerEnable(self.device, 1) self.datas = np.zeros((10, 10, 1024)) self.data_grabed_signal_temp.emit([OrderedDict(name='TH260', data=self.datas, nav_axes=[0, 1], type='DataND')]) self.data_pointers = self.datas.ctypes.data_as(ctypes.POINTER(ctypes.c_uint32)) self.actual_mode = mode self.actual_mode = mode def grab_data(self, Naverage=1, **kwargs): """ Start new acquisition in two steps : * Initialize data: self.datas for the memory to store new data and self.data_average to store the average data * Start acquisition with the given exposure in ms, in "1d" or "2d" mode =============== =========== ============================= **Parameters** **Type** **Description** Naverage int Number of images to average =============== =========== ============================= See Also -------- DAQ_utils.ThreadCommand """ try: self.acq_done = False mode = self.settings.child('acquisition', 'acq_type').value() if mode == 'Counting' or mode == 'Histo' or mode == 'T3': super(DAQ_2DViewer_FLIM, self).grab_data(Naverage, **kwargs) elif mode == 'FLIM': self.ind_reading = 0 self.do_process_tttr = False self.init_h5file() self.datas = np.zeros((self.Nx, self.Ny, self.settings.child('acquisition', 'timings', 'nbins').value(),), dtype=np.float64) self.init_histo_group() time_acq = int(self.settings.child('acquisition', 'acq_time').value() * 1000) # in ms self.general_timer.stop() self.prepare_moves() # prepare asynchronous tttr time event reading t3_reader = T3Reader_scan(self.device, self.controller, time_acq, self.stage, self.Nchannels) self.detector_thread = QThread() t3_reader.moveToThread(self.detector_thread) t3_reader.data_signal[dict].connect(self.populate_h5) self.stop_tttr.connect(t3_reader.stop_TTTR) self.start_tttr_scan.connect(t3_reader.start_TTTR) self.detector_thread.t3_reader = t3_reader self.detector_thread.start() self.detector_thread.setPriority(QThread.HighestPriority) #start acquisition and scanner self.time_t3 = time.perf_counter() self.time_t3_rate = time.perf_counter() self.start_tttr_scan.emit() except Exception as e: self.emit_status(ThreadCommand('Update_Status', [getLineInfo() + str(e), "log"])) def init_histo_group(self): mode = self.settings.child('acquisition', 'acq_type').value() if mode == 'Counting' or mode == 'Histo' or mode == 'T3': super(DAQ_2DViewer_FLIM, self).init_histo_group() else: histo_group = self.h5file.create_group(self.h5file.root, 'histograms') self.histo_array = self.h5file.create_carray(histo_group, 'histogram', obj=self.datas, title='histogram') x_axis = self.get_xaxis() xarray = self.h5file.create_carray(histo_group, "x_axis", obj=x_axis['data'], title='x_axis') xarray.attrs['shape'] = xarray.shape xarray.attrs['type'] = 'signal_axis' xarray.attrs['data_type'] = '1D' navxarray = self.h5file.create_carray(self.h5file.root, 'scan_x_axis_unique', obj=self.get_nav_xaxis(), title='data') navxarray.set_attr('shape', navxarray.shape) navxarray.attrs['type'] = 'navigation_axis' navxarray.attrs['data_type'] = '1D' navyarray = self.h5file.create_carray(self.h5file.root, 'scan_y_axis_unique', obj=self.get_nav_yaxis(), title='data') navyarray.set_attr('shape', navyarray.shape) navyarray.attrs['type'] = 'navigation_axis' navyarray.attrs['data_type'] = '1D' self.histo_array._v_attrs['data_type'] = '1D' self.histo_array._v_attrs['type'] = 'histo_data' self.histo_array._v_attrs['shape'] = self.datas.shape self.histo_array._v_attrs['scan_type'] = 'Scan2D' def prepare_moves(self): """ prepare given actuators with positions from scan_parameters Returns ------- """ self.x_axis = self.scan_parameters.axis_2D_1 self.Nx = self.x_axis.size self.y_axis = self.scan_parameters.axis_2D_2 self.Ny = self.y_axis.size positions_real = self.transform_scan_coord(self.scan_parameters.positions) # interval_time = self.settings.child('stage_settings', 'time_interval').value()/1000 # total_time = self.settings.child('acquisition', 'acq_time').value() # Ncycles = int(total_time/(interval_time*len(positions_real))) # total_time = Ncycles * interval_time*len(positions_real) # self.settings.child('acquisition', 'acq_time').setValue(total_time) # positions = [] # for ind in range(Ncycles): # positions.extend(positions_real) self.stage.set_positions_arbitrary(positions_real) self.move_at_navigator(*self.scan_parameters.positions[0][0:2]) def transform_scan_coord(self, positions): offset = self.settings.child('stage_settings', 'stage_x', 'offset_x').value() if self.settings.child('stage_settings', 'stage_x', 'stage_x_direction').value() == 'Normal': scaling_x = 1 else: scaling_x = -1 if self.settings.child('stage_settings', 'stage_y', 'stage_y_direction').value() == 'Normal': scaling_y = 1 else: scaling_y = -1 if self.settings.child('stage_settings', 'stage_x', 'stage_x_axis').value() == 'X': ind_x = 0 else: ind_x = 1 if self.settings.child('stage_settings', 'stage_y', 'stage_y_axis').value() == 'Y': ind_y = 1 else: ind_y = 0 positions_out = [] for pos in positions: pos_tmp = [(scaling_x*pos[ind_x]+offset)*1000, (scaling_y*pos[ind_y]+offset)*1000] positions_out.append(pos_tmp) return positions_out def stop(self): super(DAQ_2DViewer_FLIM, self).stop() self.stop_scanner.emit() try: self.move_at_navigator(*self.scan_parameters.positions[0][0:2]) except: pass