class DataTaker: def __init__(self, logger=None, client_tuples=None, config=None, config_name=None): self.log = LogHandler(logger) self.dataset = None # Instantiate GUI window self.gui = Window(gui_template='data_taker', host=get_ip()) # Configure list of experiments self.gui.config.setText(config_name) self.config = config self.exp_path = self.config['exp_path'] if self.exp_path is None: self.exp_path = os.getcwd() sys.path.insert(1, self.exp_path) self.update_experiment_list() # Configure list of clients self.clients = {} # Configure list of missing clients self.missing_clients = {} # Setup Autosave # First check whether autosave is specified in config file if 'auto_save' in self.config: if self.config['auto_save']: self.gui.autosave.setChecked(True) # Retrieve Clients for client_entry in self.config['servers']: client_type = client_entry['type'] client_config = client_entry['config'] client = find_client(clients=client_tuples, settings=client_config, client_type=client_type, client_config=client_config, logger=self.log) if (client == None): self.missing_clients[f"{client_type}_{client_config}"] = [ client_type, client_config ] else: self.clients[f"{client_type}_{client_config}"] = client for client_name, client_obj in self.clients.items(): client_item = QtWidgets.QListWidgetItem(client_name) client_item.setToolTip(str(client_obj)) self.gui.clients.addItem(client_item) for client_name, client_config in self.missing_clients.items(): client_item = QtWidgets.QListWidgetItem(client_name) client_item.setForeground(Qt.gray) self.gui.clients.addItem(client_item) self.log.error("Datataker missing client: " + client_name) # Configure button clicks self.gui.configure.clicked.connect(self.configure) self.gui.run.clicked.connect(self.run) self.gui.save.clicked.connect(self.save) self.gui.clearData.clicked.connect(self.clear_data) self.gui.load_config.clicked.connect(self.reload_config) self.gui.showMaximized() self.gui.apply_stylesheet() def update_experiment_list(self): """ Updates list of experiments """ self.gui.exp.clear() for filename in os.listdir(self.exp_path): if filename.endswith('.py'): self.gui.exp.addItem(filename[:-3]) self.gui.exp.itemClicked.connect(self.display_experiment) def display_experiment(self, item): """ Displays the currently clicked experiment in the text browser :param item: (QlistWidgetItem) with label of name of experiment to display """ with open(os.path.join(self.exp_path, f'{item.text()}.py'), 'r') as exp_file: exp_content = exp_file.read() self.gui.exp_preview.setText(exp_content) self.gui.exp_preview.setStyleSheet('font: 10pt "Consolas"; ' 'color: rgb(255, 255, 255); ' 'background-color: rgb(0, 0, 0);') self.log.update_metadata(experiment_file=exp_content) def configure(self): """ Configures the currently selected experiment + dataset """ # If the experiment is running, do nothing try: if self.experiment_thread.isRunning(): self.log.warn('Did not configure experiment, since it ' 'is still in progress') return except: pass # Load the config self.reload_config() # Set all experiments to normal state and highlight configured expt for item_no in range(self.gui.exp.count()): self.gui.exp.item(item_no).setBackground( QtGui.QBrush(QtGui.QColor('black'))) self.gui.exp.currentItem().setBackground( QtGui.QBrush(QtGui.QColor('darkRed'))) exp_name = self.gui.exp.currentItem().text() self.module = importlib.import_module(exp_name) self.module = importlib.reload(self.module) # Clear graph area and set up new or cleaned up dataset for index in reversed(range(self.gui.graph_layout.count())): try: self.gui.graph_layout.itemAt(index).widget().deleteLater() except AttributeError: try: self.gui.graph_layout.itemAt(index).layout().deleteLater() except AttributeError: pass self.gui.windows = {} # If we're not setting up a new measurement type, just clear the data # We are reading in the required base-dataset by looking at the define_dataset() as defined in the experiment script. try: classname = self.module.define_dataset() except AttributeError: error_msg = "No 'define_dataset' method found in experiment script." self.log.error( "No 'define_dataset' method found in experiment script.") return try: self.dataset = getattr(datasets, classname)(gui=self.gui, log=self.log, config=self.config) except AttributeError: error_msg = f"Dataset name {classname} as provided in 'define_dataset' method in experiment script is not valid." self.log.error(error_msg) return # Run any pre-experiment configuration try: self.module.configure(dataset=self.dataset, **self.clients) except AttributeError: pass self.experiment = self.module.experiment self.log.info(f'Experiment {exp_name} configured') self.gui.exp_preview.setStyleSheet( 'font: 10pt "Consolas"; ' 'color: rgb(255, 255, 255); ' 'background-color: rgb(50, 50, 50);') def clear_data(self): """ Clears all data from curves""" self.log.info("Clearing data") self.dataset.clear_all_data() def run(self): """ Runs/stops the experiment """ # Run experiment if self.gui.run.text() == 'Run': self.gui.run.setStyleSheet('background-color: red') self.gui.run.setText('Stop') self.log.info('Experiment started') # Run update thread self.update_thread = UpdateThread( autosave=self.gui.autosave.isChecked(), save_time=self.gui.autosave_interval.value()) self.update_thread.data_updated.connect(self.dataset.update) self.update_thread.save_flag.connect(self.save) self.gui.autosave.toggled.connect( self.update_thread.update_autosave) self.gui.autosave_interval.valueChanged.connect( self.update_thread.update_autosave_interval) ''' # Step 2: Create a QThread object self.thread = QThread() # Step 3: Create a worker object self.worker = Worker() # Step 4: Move worker to the thread self.worker.moveToThread(self.thread) # Step 5: Connect signals and slots self.thread.started.connect(self.worker.run) self.worker.finished.connect(self.thread.quit) self.worker.finished.connect(self.worker.deleteLater) self.thread.finished.connect(self.thread.deleteLater) self.worker.progress.connect(self.reportProgress) # Step 6: Start the thread self.thread.start() # Final resets self.longRunningBtn.setEnabled(False) self.thread.finished.connect( lambda: self.longRunningBtn.setEnabled(True) ) self.thread.finished.connect( lambda: self.stepLabel.setText("Long-Running Step: 0") ) ''' self.experiment_thread = ExperimentThread(self.experiment, dataset=self.dataset, gui=self.gui, **self.clients) self.experiment_thread.status_flag.connect( self.dataset.interpret_status) self.experiment_thread.finished.connect(self.stop) self.log.update_metadata( exp_start_time=datetime.now().strftime('%d/%m/%Y %H:%M:%S:%f')) # Stop experiment else: self.experiment_thread.running = False def stop(self): """ Stops the experiment""" self.gui.run.setStyleSheet('background-color: green') self.gui.run.setText('Run') self.log.info('Experiment stopped') self.update_thread.running = False self.log.update_metadata( exp_stop_time=datetime.now().strftime('%d/%m/%Y %H:%M:%S:%f')) # Autosave if relevant if self.gui.autosave.isChecked(): self.save() def save(self): """ Saves data """ self.log.update_metadata(notes=self.gui.notes.toPlainText()) filename = self.gui.save_name.text() directory = self.config['save_path'] self.dataset.save(filename=filename, directory=directory, date_dir=True) save_metadata(self.log, filename, directory, True) self.log.info('Data saved') def reload_config(self): """ Loads a new config file """ self.config = load_script_config(script='data_taker', config=self.gui.config.text(), logger=self.log)
class DataTaker: def __init__(self, logger=None, client_tuples=None, config=None, config_name=None): self.log = LogHandler(logger) self.dataset = None # Instantiate GUI window self.gui = Window(gui_template='data_taker', host=get_ip()) # Configure list of experiments self.gui.config.setText(config_name) self.config = config self.exp_path = self.config['exp_path'] if self.exp_path is None: self.exp_path = os.getcwd() sys.path.insert(1, self.exp_path) self.update_experiment_list() # Configure list of clients self.clients = {} # Retrieve Clients for client_entry in self.config['servers']: client_type = client_entry['type'] client_config = client_entry['config'] client = find_client(clients=client_tuples, settings=client_config, client_type=client_type, client_config=client_config, logger=self.log) self.clients[f"{client_type}_{client_config}"] = client for client_name, client_obj in self.clients.items(): client_item = QtWidgets.QListWidgetItem(client_name) client_item.setToolTip(str(client_obj)) self.gui.clients.addItem(client_item) # Configure dataset menu for name, obj in inspect.getmembers(datasets): if inspect.isclass(obj) and issubclass(obj, datasets.Dataset): self.gui.dataset.addItem(name) # Configure button clicks self.gui.configure.clicked.connect(self.configure) self.gui.run.clicked.connect(self.run) self.gui.save.clicked.connect(self.save) self.gui.load_config.clicked.connect(self.reload_config) self.gui.showMaximized() self.gui.apply_stylesheet() def update_experiment_list(self): """ Updates list of experiments """ self.gui.exp.clear() for filename in os.listdir(self.exp_path): if filename.endswith('.py'): self.gui.exp.addItem(filename[:-3]) self.gui.exp.itemClicked.connect(self.display_experiment) def display_experiment(self, item): """ Displays the currently clicked experiment in the text browser :param item: (QlistWidgetItem) with label of name of experiment to display """ with open(os.path.join(self.exp_path, f'{item.text()}.py'), 'r') as exp_file: exp_content = exp_file.read() self.gui.exp_preview.setText(exp_content) self.gui.exp_preview.setStyleSheet('font: 10pt "Consolas"; ' 'color: rgb(255, 255, 255); ' 'background-color: rgb(0, 0, 0);') self.log.update_metadata(experiment_file=exp_content) def configure(self): """ Configures the currently selected experiment + dataset """ # If the experiment is running, do nothing try: if self.experiment_thread.isRunning(): self.log.warn('Did not configure experiment, since it ' 'is still in progress') return except: pass # Load the config self.reload_config() # Set all experiments to normal state and highlight configured expt for item_no in range(self.gui.exp.count()): self.gui.exp.item(item_no).setBackground( QtGui.QBrush(QtGui.QColor('black'))) self.gui.exp.currentItem().setBackground( QtGui.QBrush(QtGui.QColor('darkRed'))) exp_name = self.gui.exp.currentItem().text() self.module = importlib.import_module(exp_name) self.module = importlib.reload(self.module) # Clear graph area and set up new or cleaned up dataset for index in reversed(range(self.gui.graph_layout.count())): try: self.gui.graph_layout.itemAt(index).widget().deleteLater() except AttributeError: try: self.gui.graph_layout.itemAt(index).layout().deleteLater() except AttributeError: pass self.gui.windows = {} # If we're not setting up a new measurement type, just clear the data self.dataset = getattr(datasets, self.gui.dataset.currentText())( gui=self.gui, log=self.log, config=self.config) # Run any pre-experiment configuration try: self.module.configure(dataset=self.dataset, **self.clients) except AttributeError: pass self.experiment = self.module.experiment self.log.info(f'Experiment {exp_name} configured') self.gui.exp_preview.setStyleSheet( 'font: 10pt "Consolas"; ' 'color: rgb(255, 255, 255); ' 'background-color: rgb(50, 50, 50);') def run(self): """ Runs/stops the experiment """ # Run experiment if self.gui.run.text() == 'Run': self.gui.run.setStyleSheet('background-color: red') self.gui.run.setText('Stop') self.log.info('Experiment started') # Run update thread self.update_thread = UpdateThread( autosave=self.gui.autosave.isChecked(), save_time=self.gui.autosave_interval.value()) self.update_thread.data_updated.connect(self.dataset.update) self.update_thread.save_flag.connect(self.save) self.gui.autosave.toggled.connect( self.update_thread.update_autosave) self.gui.autosave_interval.valueChanged.connect( self.update_thread.update_autosave_interval) self.experiment_thread = ExperimentThread(self.experiment, dataset=self.dataset, gui=self.gui, **self.clients) self.experiment_thread.status_flag.connect( self.dataset.interpret_status) self.experiment_thread.finished.connect(self.stop) self.log.update_metadata( exp_start_time=datetime.now().strftime('%d/%m/%Y %H:%M:%S:%f')) # Stop experiment else: self.experiment_thread.running = False def stop(self): """ Stops the experiment""" self.gui.run.setStyleSheet('background-color: green') self.gui.run.setText('Run') self.log.info('Experiment stopped') self.update_thread.running = False self.log.update_metadata( exp_stop_time=datetime.now().strftime('%d/%m/%Y %H:%M:%S:%f')) # Autosave if relevant if self.gui.autosave.isChecked(): self.save() def save(self): """ Saves data """ self.log.update_metadata(notes=self.gui.notes.toPlainText()) filename = self.gui.save_name.text() directory = self.config['save_path'] self.dataset.save(filename=filename, directory=directory, date_dir=True) save_metadata(self.log, filename, directory, True) self.log.info('Data saved') def reload_config(self): """ Loads a new config file """ self.config = load_script_config(script='data_taker', config=self.gui.config.text(), logger=self.log)