Source code for PFTL.view.main_window

"""
Main Window
===========
This is the central code for the user interface of Python for the Lab. The design of the window is specifcied in its
own .ui file, generated with Qt Designer.

"""

from pathlib import Path

import pyqtgraph as pg
from PyQt6 import uic
from PyQt6.QtCore import QTimer
from PyQt6.QtWidgets import QMainWindow

pg.setConfigOption("background", "w")
pg.setConfigOption("foreground", "k")


[docs] class MainWindow(QMainWindow): """Main Window for the user interface Parameters ---------- experiment : Experiment Experiment model, can be left empty just for testing. Should be instantiated and initialized before passing it. Attributes ---------- experiment : Experiment The experiment object plot_widget : pg.PlotWidget Widget to hold the plot plot : pg.PlotWidget.plotItem The real plot that can be updated with new data start_button : QPushButton The start button """ def __init__(self, experiment=None): super().__init__() self.experiment = experiment self.setWindowTitle("Scan Window") base_dir = Path(__file__).parent ui_file = base_dir / "GUI" / "main_window.ui" uic.loadUi(ui_file, self) # self.plot_widget = pg.PlotWidget( # title="Plotting I vs V", labels={"left": "Current", "bottom": "Voltage"} # ) pen = pg.mkPen(cosmetic=False, width=0.05, color="black") self.plot = self.plot_widget.plot([0], [0], pen=pen, title='I vs V') plot_item = self.plot_widget.getPlotItem() plot_item.setXRange(0, 3.3) plot_item.setYRange(0, 5) self.start_button.clicked.connect(self.start_scan) self.stop_button.clicked.connect(self.stop_scan) self.actionSave.triggered.connect(self.experiment.save_data) self.start_line.setText(self.experiment.config["Scan"]["start"]) self.stop_line.setText(self.experiment.config["Scan"]["stop"]) self.num_steps_line.setText(str(self.experiment.config["Scan"]["num_steps"])) self.delay_line.setText(self.experiment.config["Scan"]["delay"]) self.out_channel_line.setText( str(self.experiment.config["Scan"]["channel_out"]) ) self.in_channel_line.setText(str(self.experiment.config["Scan"]["channel_in"])) self.gui_timer = QTimer() self.plot_timer = QTimer() self.plot_timer.timeout.connect(self.update_plot) self.gui_timer.start(50) self.gui_timer.timeout.connect(self.update_gui)
[docs] def update_plot(self): """ This method is called periodically via a QTimer. It updates the plot to show what is currently available in the experiment data. If the acquisition is over, the timer is stopped (this prevents wasting computation resources updating a plot that does not change). """ self.plot.setData( self.experiment.scan_range[: self.experiment.current_scan_index].m_as("V"), self.experiment.scan_data[: self.experiment.current_scan_index].m_as("mA"), ) if not self.experiment.is_running: self.plot_timer.stop()
[docs] def start_scan(self): """ Wrapper that updates the values from the UI (start, stop, num_steps, delay, channel_in, channel_out) before starting the scan. .. Warning:: There is a bug in this code (left for students to find out and sort it). If a user changes the values on the UI and presses "start" again, the metadata will store the new values, not the proper ones. """ start = self.start_line.text() stop = self.stop_line.text() num_steps = int(self.num_steps_line.text()) delay = self.delay_line.text() channel_in = int(self.in_channel_line.text()) channel_out = int(self.out_channel_line.text()) self.experiment.config["Scan"].update( { "start": start, "stop": stop, "num_steps": num_steps, "channel_in": channel_in, "channel_out": channel_out, "delay": delay, } ) self.experiment.start_scan() self.plot_widget.setLabel('bottom', f"Port: {self.experiment.config['Scan']['channel_out']}", units="V") self.plot_widget.setLabel('left', f"Port: {self.experiment.config['Scan']['channel_in']}", units="mA") self.plot_timer.start(50)
[docs] def update_gui(self): """ It is called on a timer to display the latest values of the applied voltage and the measured voltage. """ self.out_line.setText(f"{self.experiment.voltage_out:3.2f}") self.measured_line.setText(f"{self.experiment.last_measured_value:.2f~#P}") if self.experiment.is_running: self.start_button.setEnabled(False) self.stop_button.setEnabled(True) else: self.start_button.setEnabled(True) self.stop_button.setEnabled(False)
[docs] def stop_scan(self): """ Stops the scan. It is wrapping the :func:`~PFTL.model.experiment.Experiment.stop_scan` method. The only reason to do it this way is in case stopping a scan requires more work, for example stopping timers to prevent useless updates, etc. """ self.experiment.stop_scan() print("UI: Stopping Scan")