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'
def save_data(self): try: path = utils.select_file(start_path=self.settings.child( 'main_settings', 'base_path').value(), save=True, ext='h5') if path is not None: #init the file object with an addhoc name given by the user h5saver = H5Saver(save_type='custom') h5saver.init_file(update_h5=True, addhoc_file_path=path) #save all metadata settings_str = custom_tree.parameter_to_xml_string( self.settings) settings_str = b'<All_settings>' + settings_str settings_str += custom_tree.parameter_to_xml_string(self.detector.settings) + \ custom_tree.parameter_to_xml_string(h5saver.settings) + \ b'</All_settings>' data_group = h5saver.add_data_group( h5saver.raw_group, group_data_type='data0D', title='data from custom app', settings_as_xml=settings_str) for dat in self.raw_data: channel = h5saver.add_CH_group(data_group) data_dict = dict(data=np.array(dat), x_axis=dict(data=np.linspace( 0, len(dat) - 1, len(dat)), units='pxl')) h5saver.add_data(channel, data_dict=data_dict, scan_type='') st = 'file {:s} has been saved'.format(str(path)) self.add_log(st) self.settings.child('main_settings', 'info').setValue(st) h5saver.close_file() except Exception as e: self.add_log(getLineInfo() + str(e))
def init_h5file(self): file, curr_dir = get_new_file_name(self.settings.child('acquisition','base_path').value(), 'tttr_data') self.h5saver = H5Saver(save_type='custom') self.h5saver.init_file(update_h5=False, custom_naming=True, addhoc_file_path=os.path.join(curr_dir, f'{file}.h5'), metadata=dict(settings=custom_tree.parameter_to_xml_string(self.settings), format_name='timestamps')) self.settings.child('acquisition', 'temp_file').setValue(f'{file}.h5')
def save_ROI(self): try: data = custom_tree.parameter_to_xml_string( self.settings.child(('ROIs'))) path = select_file(ext='roi') if path != '': with open(path, 'wb') as f: f.write(data) except Exception as e: print(e)
def init_connection(self): # %% try: # create an INET, STREAMing socket self.socket = Socket( socket.socket(socket.AF_INET, socket.SOCK_STREAM)) # now connect to the web server on port 80 - the normal http port self.socket.connect((self.ipaddress, self.port)) self.cmd_signal.emit(ThreadCommand('connected')) self.socket.send_string(self.client_type) self.send_infos_xml( custom_tree.parameter_to_xml_string(self.settings)) self.cmd_signal.emit(ThreadCommand('get_axis')) self.connected = True # %% while True: try: ready_to_read, ready_to_write, in_error = \ select.select([self.socket.socket], [self.socket.socket], [self.socket.socket], 0) if len(ready_to_read) != 0: message = self.socket.get_string() # print(message) self.get_data(message) if len(in_error) != 0: self.connected = False self.cmd_signal.emit(ThreadCommand('disconnected')) QtWidgets.QApplication.processEvents() except Exception as e: try: self.cmd_signal.emit( ThreadCommand('Update_Status', [getLineInfo() + str(e), 'log'])) self.socket.send_string('Quit') self.socket.close() except: pass finally: break except ConnectionRefusedError as e: self.connected = False self.cmd_signal.emit(ThreadCommand('disconnected')) self.cmd_signal.emit( ThreadCommand('Update_Status', [getLineInfo() + str(e), 'log']))
def commit_settings(self, param): if param.name() in custom_tree.iter_children( self.settings.child(('infos')), []): grabber_socket = \ [client['socket'] for client in self.connected_clients if client['type'] == self.client_type][0] grabber_socket.send_string('set_info') path = custom_tree.get_param_path( param )[2:] # get the path of this param as a list starting at parent 'infos' grabber_socket.send_list(path) # send value data = custom_tree.parameter_to_xml_string(param) grabber_socket.send_string(data)
def commit_settings(self, param): if param.name() in custom_tree.iter_children( self.settings.child(('infos')), []): actuator_socket = [ client['socket'] for client in self.connected_clients if client['type'] == 'ACTUATOR' ][0] send_string(actuator_socket, 'set_info') path = custom_tree.get_param_path( param )[2:] #get the path of this param as a list starting at parent 'infos' send_list(actuator_socket, path) #send value data = custom_tree.parameter_to_xml_string(param) send_string(actuator_socket, data)
def set_continuous_save(self): """ Set a continous save file using the base path located file with a header-name containing date as a string. See Also -------- daq_utils.set_current_scan_path """ self.do_continuous_save = True self.logger.settings.child(('N_saved')).show() self.logger.settings.child(('N_saved')).setValue(0) settings_str = b'<All_settings>' settings_str += custom_tree.parameter_to_xml_string( self.dashboard.settings) settings_str += custom_tree.parameter_to_xml_string( self.dashboard.preset_manager.preset_params) if self.dashboard.settings.child('loaded_files', 'overshoot_file').value() != '': settings_str += custom_tree.parameter_to_xml_string( self.dashboard.overshoot_manager.overshoot_params) if self.dashboard.settings.child('loaded_files', 'roi_file').value() != '': settings_str += custom_tree.parameter_to_xml_string( self.dashboard.roi_saver.roi_presets) settings_str += custom_tree.parameter_to_xml_string(self.settings) settings_str += custom_tree.parameter_to_xml_string( self.logger.settings) settings_str += b'</All_settings>' if self.settings.child(('log_type')).value() == 'H5 File': self.logger.settings.child(('base_name')).setValue('DataLogging') self.h5saver.init_file(update_h5=True, metadata=dict(settings=settings_str)) logger.addHandler(H5LogHandler(self.h5saver)) self.h5saver.h5_file.flush() elif self.settings.child(('log_type')).value() == 'SQL DataBase': if not self.logger.settings.child('connected_db').value(): status = self.logger.connect_db() if not status: logger.critical( 'the Database is not and cannot be connnect') self.update_status( 'the Database is not and cannot be connnect') return False self.logger.add_config(settings_str) logger.addHandler(DBLogHandler(self.logger)) return True
def save_data(self): try: fname = utils.select_file(start_path=self.save_file_pathname, save=True, ext='h5') if not (not (fname)): with tables.open_file( str(fname), mode='w', title='an example h5 file name') as h5file: data_to_save = np.squeeze(np.array(self.raw_data)) #save metadata h5file.root._v_attrs[ 'settings'] = custom_tree.parameter_to_xml_string( self.settings) for ind in range(data_to_save.shape[1]): arr = h5file.create_array('/', 'data_{:d}'.format(ind), data_to_save[:, ind]) arr._v_attrs['shape'] = data_to_save.shape[0] arr._v_attrs['type'] = 'data1D' arr = h5file.create_array('/', 'data_2D', data_to_save) arr._v_attrs['shape'] = data_to_save.shape arr._v_attrs['type'] = 'data2D' logger = "logging" text_atom = tables.atom.ObjectAtom() logger_array = h5file.create_vlarray('/', logger, atom=text_atom) logger_array._v_attrs['type'] = 'list' for ind_log in range(self.logger_list.count()): txt = self.logger_list.item(ind_log).text() logger_array.append(txt) st = 'file {:s} has been saved'.format(fname) self.add_log(st) self.settings.child('min_settings', 'info').setValue(st) except Exception as e: self.add_log(str(e))
def save_metadata(self, node, type_info='dataset_info'): """ """ attr = node._v_attrs if type_info == 'dataset_info': attr['type'] = 'dataset' params = self.dataset_attributes else: attr['type'] = 'scan' params = self.scan_attributes for child in params.child((type_info)).children(): if isinstance(child.value(), QDateTime): attr[child.name()] = child.value().toString( 'dd/mm/yyyy HH:MM:ss') else: attr[child.name()] = child.value() if type_info == 'dataset_info': #save contents of given parameter object into an xml string under the attribute settings settings_str = b'<All_settings title="All Settings" type="group">' settings_str += custom_tree.parameter_to_xml_string(params) settings_str += custom_tree.parameter_to_xml_string(self.settings) if hasattr(self.shortcut_manager, 'shortcut_params'): settings_str += custom_tree.parameter_to_xml_string( self.shortcut_manager.shortcut_params) settings_str += b'</All_settings>' attr.settings = settings_str elif type_info == 'scan_info': settings_str = b'<All_settings title="All Settings" type="group">' + \ custom_tree.parameter_to_xml_string(params) + \ custom_tree.parameter_to_xml_string(self.settings) + \ custom_tree.parameter_to_xml_string(self.h5saver.settings) + b'</All_settings>' attr.settings = settings_str
def test_commands(self, get_server, qtbot): server = get_server() server.socket_types = socket_types #Combination of general messages and specific ones (depending the connected client, Grabber or Actuator server.message_list = [ "Quit", "Done", "Info", "Infos", "Info_xml", #"Send Data 0D", "Send Data 1D", "Send Data 2D", "Send Data ND", "Status", # 'x_axis', 'y_axis' ] #read_info server.read_info(None, 'random_info', 'random info value') assert 'random_info' in custom_tree.iter_children( server.settings.child(('infos')), []) assert server.settings.child( 'infos', 'random_info').value() == 'random info value' # read_infos params = [{ 'title': 'Device index:', 'name': 'device', 'type': 'int', 'value': 0, 'max': 3, 'min': 0 }, { 'title': 'Infos:', 'name': 'infos', 'type': 'str', 'value': "one_info", 'readonly': True }, { 'title': 'Line Settings:', 'name': 'line_settings', 'type': 'group', 'expanded': False, 'children': [ { 'title': 'Device index:', 'name': 'device1', 'type': 'int', 'value': 0, 'max': 3, 'min': 0 }, { 'title': 'Device index:', 'name': 'device2', 'type': 'int', 'value': 0, 'max': 3, 'min': 0 }, ] }] param = Parameter.create(name='settings', type='group', children=params) params_xml = custom_tree.parameter_to_xml_string(param) server.read_infos(None, params_xml) assert 'device' in custom_tree.iter_children( server.settings.child(('settings_client')), []) assert 'infos' in custom_tree.iter_children( server.settings.child(('settings_client')), []) assert server.settings.child('settings_client', 'infos').value() == 'one_info' assert 'line_settings' in custom_tree.iter_children( server.settings.child(('settings_client')), []) assert server.settings.child('settings_client', 'line_settings').opts['type'] == 'group' # read_info_xml one_param = param.child(('infos')) one_param.setValue('another_info') assert one_param.value() == 'another_info' path = param.childPath(one_param) path.insert(0, '') #add one to mimic correct behaviour server.read_info_xml(None, path, custom_tree.parameter_to_xml_string(one_param)) assert server.settings.child('settings_client', 'infos').value() == 'another_info'
def queue_command(self, command=ThreadCommand()): """ """ if command.command == "ini_connection": status = self.init_connection() elif command.command == "quit": try: self.socket.close() except Exception as e: pass finally: self.cmd_signal.emit(ThreadCommand('disconnected')) elif command.command == 'update_connection': self.ipaddress = command.attributes['ipaddress'] self.port = command.attributes['port'] elif command.command == 'data_ready': self.data_ready(command.attributes) elif command.command == 'send_info': path = command.attributes['path'] param = command.attributes['param'] param_xml = custom_tree.parameter_to_xml_string(param) send_string(self.socket, 'Info_xml') send_list(self.socket, path) # send value data = custom_tree.parameter_to_xml_string(param) send_string(self.socket, data) elif command.command == 'position_is': send_string(self.socket, 'position_is') send_scalar(self.socket, command.attributes[0]) elif command.command == 'move_done': send_string(self.socket, 'move_done') send_scalar(self.socket, command.attributes[0]) elif command.command == 'x_axis': send_string(self.socket, 'x_axis') x_axis = dict(label='', units='') if isinstance(command.attributes[0], np.ndarray): x_axis['data'] = command.attributes[0] elif isinstance(command.attributes[0], dict): x_axis.update(command.attributes[0].copy()) send_array(self.socket, x_axis['data']) send_string(self.socket, x_axis['label']) send_string(self.socket, x_axis['units']) elif command.command == 'y_axis': send_string(self.socket, 'y_axis') y_axis = dict(label='', units='') if isinstance(command.attributes[0], np.ndarray): y_axis['data'] = command.attributes[0] elif isinstance(command.attributes[0], dict): y_axis.update(command.attributes[0].copy()) send_array(self.socket, y_axis['data']) send_string(self.socket, y_axis['label']) send_string(self.socket, y_axis['units'])
'line_settings', 'type': 'group', 'expanded': False, 'children': [ { 'title': 'Device index:', 'name': 'device1', 'type': 'int', 'value': 0, 'max': 3, 'min': 0 }, { 'title': 'Device index:', 'name': 'device2', 'type': 'int', 'value': 0, 'max': 3, 'min': 0 }, ] }] }] param = Parameter.create(name='settings', type='group', children=params) params = custom_tree.parameter_to_xml_string(param) server.read_infos(None, params) sys.exit(app.exec_())
def create_test_file(request, qtbot): bck = H5Saver(backend=request.param) filepath = f'./data/data_test_{request.param}.h5' bck.init_file(update_h5=True, addhoc_file_path=filepath) Nx = 12 Nnavx = 5 Nnavy = 10 scan_shape = (Nnavx, Nnavy) x_axis = dict(label='this is data axis', units='no units', data=np.arange(Nx)) data1D = dict(data=np.arange(Nx) * 1.0 + 7, x_axis=x_axis) nav_x_axis = dict(label='this is nav x axis', units='x units', data=np.arange(Nnavx)) nav_y_axis = utils.Axis(label='this is nav y axis', units='y units', data=np.arange(Nnavy)) d = datetime(year=2020, month=5, day=24, hour=10, minute=52, second=55) raw_group = bck.add_group( 'Agroup', 'data', '/', metadata=dict( date_time=d, author='Seb Weber', settings='this should be xml', scan_settings=b'scan binary setting', type='scan', shape=(10, 45), pixmap_1D=b'this should be binary png in reality', pixmap_2D=b'this should be another binary png in reality')) bck.add_data(raw_group, data1D) scan_group = bck.add_scan_group( 'first scan', settings_as_xml='this should dbe xml settings') params = [{ 'title': 'Main Settings:', 'name': 'main_settings', 'expanded': False, 'type': 'group', 'children': [ { 'title': 'DAQ type:', 'name': 'DAQ_type', 'type': 'list', 'values': ['DAQ0D', 'DAQ1D', 'DAQ2D', 'DAQND'], 'readonly': True }, { 'title': 'Detector type:', 'name': 'detector_type', 'type': 'str', 'value': '', 'readonly': True }, { 'title': 'Nviewers:', 'name': 'Nviewers', 'type': 'int', 'value': 1, 'min': 1, 'default': 1, 'readonly': True }, ] }] settings = Parameter.create(name='settings', type='group', children=params) settings_xml = ctree.parameter_to_xml_string(settings) det_group = bck.add_det_group(scan_group, 'det group', settings_as_xml=settings_xml) data_group = bck.add_data_group(det_group, 'data1D') ch_group = bck.add_CH_group(data_group) data = np.arange(Nx * Nnavx * Nnavy) data = data.reshape(Nnavx, Nnavy, Nx) data_dict = dict(data=data, x_axis=x_axis) bck.add_data(ch_group, data_dict, scan_type='scan2D', scan_shape=scan_shape, add_scan_dim=False) bck.add_navigation_axis(nav_x_axis.pop('data'), scan_group, 'x_axis', metadata=nav_x_axis) bck.add_navigation_axis(nav_y_axis.pop('data'), scan_group, 'y_axis', metadata=nav_y_axis) bck.logger_array.append('log1 to check') bck.logger_array.append('log2 to check') bck.close_file() return bck
def save_data(self, export=False): try: if export: ext = 'dat' else: ext = 'h5' path = select_file(start_path=self.save_file_pathname, save=True, ext=ext) if not (not (path)): if not export: h5saver = H5Saver(save_type='detector') h5saver.init_file(update_h5=True, custom_naming=False, addhoc_file_path=path) settings_str = b'<All_settings>' + custom_tree.parameter_to_xml_string( self.settings) if self.detector is not None: settings_str += custom_tree.parameter_to_xml_string( self.detector.settings) if hasattr(self.detector.ui.viewers[0], 'roi_manager'): settings_str += custom_tree.parameter_to_xml_string( self.detector.ui.viewers[0].roi_manager. settings) settings_str += custom_tree.parameter_to_xml_string( h5saver.settings) settings_str += b'</All_settings>' det_group = h5saver.add_det_group(h5saver.raw_group, "Data", settings_str) try: self.channel_arrays = OrderedDict([]) data_dim = 'data1D' if not h5saver.is_node_in_group(det_group, data_dim): self.channel_arrays['data1D'] = OrderedDict([]) data_group = h5saver.add_data_group( det_group, data_dim) for ind_channel, data in enumerate( self.raw_data): # list of numpy arrays channel = f'CH{ind_channel:03d}' channel_group = h5saver.add_CH_group( data_group, title=channel) self.channel_arrays[data_dim][ 'parent'] = channel_group self.channel_arrays[data_dim][ channel] = h5saver.add_data( channel_group, dict(data=data, x_axis=self.viewer_freq_axis), scan_type='', enlargeable=False) h5saver.close_file() except Exception as e: logger.exception(str(e)) else: data_to_save = [self.viewer_freq_axis['data']] data_to_save.extend([dat for dat in self.raw_data]) np.savetxt(path, data_to_save, delimiter='\t') except Exception as e: logger.exception(str(e))
def test_methods(self, qtbot): server = SimpleServer(('127.0.0.1', 6341), ) server.start() params = [{ 'title': 'Device index:', 'name': 'device', 'type': 'int', 'value': 0, 'max': 3, 'min': 0 }, { 'title': 'Infos:', 'name': 'infos', 'type': 'str', 'value': "one_info", 'readonly': True }, { 'title': 'Line Settings:', 'name': 'line_settings', 'type': 'group', 'expanded': False, 'children': [ { 'name': 'someparam', 'type': 'float', 'value': 15.54, 'readonly': True }, ] }] param = Parameter.create(name='settings', type='group', children=params) client = TCPClient(ipaddress="127.0.0.1", port=6341, params_state=param.saveState(), client_type="sometype") client.cmd_signal.connect(self.get_cmd_signal) #check init method assert client.ipaddress == "127.0.0.1" assert client.port == 6341 assert client.client_type == 'sometype' client.socket = Socket( native_socket.socket(socket.AF_INET, socket.SOCK_STREAM)) client.socket.connect((client.ipaddress, client.port)) sleep(0.5) server_socket = Socket(server.do_read()[0]) client.send_data([np.array([0, 1, 2, 3]), 'item', 5.1]) assert server_socket.get_string() == 'Done' utils.check_vals_in_iterable(server_socket.get_list(), [np.array([0, 1, 2, 3]), 'item', 5.1]) client.send_infos_xml(custom_tree.parameter_to_xml_string(param)) assert server_socket.get_string() == 'Infos' assert server_socket.get_string( ) == custom_tree.parameter_to_xml_string(param).decode() client.send_info_string('someinfo', 'this is an info') assert server_socket.get_string() == 'Info' assert server_socket.get_string() == 'someinfo' assert server_socket.get_string() == 'this is an info' #test queue_command client.cmd_signal.connect(self.get_cmd_signal) client_manager = ClientObjectManager() client_manager.cmd_signal.connect(client.queue_command) with pytest.raises(Exception): client.queue_command(utils.ThreadCommand('Weird command')) #test get_data server_socket.send_string('set_info') server_socket.send_list(['line_settings', 'someparam']) server_socket.send_string( custom_tree.parameter_to_xml_string( param.child('line_settings', 'someparam'))) msg = client.socket.get_string() client.get_data(msg) assert self.command == 'set_info' utils.check_vals_in_iterable( self.attributes, [['line_settings', 'someparam'], custom_tree.parameter_to_xml_string( param.child('line_settings', 'someparam')).decode()]) server_socket.send_string('move_abs') server_socket.send_scalar(12.546) msg = client.socket.get_string() client.get_data(msg) assert self.command == 'move_abs' utils.check_vals_in_iterable(self.attributes, [12.546]) server_socket.send_string('move_rel') server_socket.send_scalar(3.2) msg = client.socket.get_string() client.get_data(msg) assert self.command == 'move_rel' utils.check_vals_in_iterable(self.attributes, [3.2]) server.stop()
def queue_command(self, command=ThreadCommand()): """ when this TCPClient object is within a thread, the corresponding module communicate with it with signal and slots from module to client: module_signal to queue_command slot from client to module: self.cmd_signal to a module slot """ if command.command == "ini_connection": status = self.init_connection() elif command.command == "quit": try: self.socket.close() except Exception as e: pass finally: self.cmd_signal.emit(ThreadCommand('disconnected')) elif command.command == 'update_connection': self.ipaddress = command.attributes['ipaddress'] self.port = command.attributes['port'] elif command.command == 'data_ready': self.data_ready(command.attributes) elif command.command == 'send_info': if self.socket is not None: path = command.attributes['path'] param = command.attributes['param'] self.socket.send_string('Info_xml') self.socket.send_list(path) # send value data = custom_tree.parameter_to_xml_string(param) self.socket.send_string(data) elif command.command == 'position_is': if self.socket is not None: self.socket.send_string('position_is') self.socket.send_scalar(command.attributes[0]) elif command.command == 'move_done': if self.socket is not None: self.socket.send_string('move_done') self.socket.send_scalar(command.attributes[0]) elif command.command == 'x_axis': if self.socket is not None: self.socket.send_string('x_axis') x_axis = dict(label='', units='') if isinstance(command.attributes[0], np.ndarray): x_axis['data'] = command.attributes[0] elif isinstance(command.attributes[0], dict): x_axis.update(command.attributes[0].copy()) self.socket.send_array(x_axis['data']) self.socket.send_string(x_axis['label']) self.socket.send_string(x_axis['units']) elif command.command == 'y_axis': if self.socket is not None: self.socket.send_string('y_axis') y_axis = dict(label='', units='') if isinstance(command.attributes[0], np.ndarray): y_axis['data'] = command.attributes[0] elif isinstance(command.attributes[0], dict): y_axis.update(command.attributes[0].copy()) self.socket.send_array(y_axis['data']) self.socket.send_string(y_axis['label']) self.socket.send_string(y_axis['units']) else: raise IOError('Unknown TCP client command')
def set_logging(self): """ """ status = self.set_continuous_save() if status: det_modules_log = self.modules_manager.detectors if det_modules_log != []: #check if the modules are initialized for module in det_modules_log: if not module.initialized_state: logger.error( f'module {module.title} is not initialized') return False #create the detectors in the chosen logger for det in det_modules_log: settings_str = b'<All_settings>' settings_str += custom_tree.parameter_to_xml_string( det.settings) for viewer in det.ui.viewers: if hasattr(viewer, 'roi_manager'): settings_str += custom_tree.parameter_to_xml_string( viewer.roi_manager.settings) settings_str += b'</All_settings>' if self.settings.child(('log_type')).value() == 'H5 File': if det.title not in self.h5saver.raw_group.children_name( ): det_group = self.h5saver.add_det_group( self.h5saver.raw_group, det.title, settings_str) self.h5saver.add_navigation_axis( np.array([ 0.0, ]), det_group, 'time_axis', enlargeable=True, title='Time axis', metadata=dict(label='Time axis', units='timestamp', nav_index=0)) elif self.settings.child( ('log_type')).value() == 'SQL DataBase': self.logger.add_detectors( [dict(name=det.title, xml_settings=settings_str)]) self.ui.start_button.setEnabled(True) self.ui.stop_button.setEnabled(True) return True else: self.update_status( 'Cannot start logging... No detectors selected') self.ui.start_button.setEnabled(False) self.ui.stop_button.setEnabled(True) return False else: self.update_status('Cannot start logging... check connections') self.ui.start_button.setEnabled(False) self.ui.stop_button.setEnabled(True) return False