Source code for gui.widget_pi_control

"""
This widget contains the functionality of the pi_control hardware class
provided in pi_control.py. The widget can be executed as a standalone
version if a stage is connected and the correct port/ parameters are
set.

########################################
Functionalities provided in this widget:
########################################

1.  Choose between different stages from a comboBox item.
2.  Read out general information about the connected port, the axis of
    the stage and the temporal overlap position (tzero position) of the
    chosen stage.
3.  Set the velocity of the stage in mm/s and fs/s
4.  Set the position of the stage in mm and fs
5.  Reset the tzero position of the stage.
6.  Start a wavegeneration with given parameters. It is possible to set
    the coherence time in ps, the frequency in Hz, the speedupdown points
    (how slow the stage accelerates and breaks an the endpoints) and the
    overshoot factor (see documentation of pi_control.py)
"""
if __name__ == "__main__":
    # Add directories to path for imports
    import os, sys, inspect

    currentdir = os.path.dirname(
        os.path.abspath(inspect.getfile(inspect.currentframe()))
    )
    parentdir = os.path.dirname(currentdir)

    sys.path.insert(0, os.path.join(currentdir, "ui_files"))
    sys.path.insert(0, parentdir)
    sys.path.insert(0, os.path.join(parentdir, "hardware_interfaces"))

# PyQt imports
from PyQt5 import QtWidgets
from PyQt5.QtCore import QRunnable, QThreadPool

# Import RegEx input validators
from regex_validators import *

# Import Qt Threading wrapper
from qt_multithreading_wrapper import Worker

# Import of file made from UI designer
from ui_pi_control import Ui_GroupBox_picontrol as Ui_pi_control

# Import pi_control
from pi_control import PiController, PiStage
import pi_control

# Logger Settings
import logging

# logging.basicConfig(level=logging.INFO)


[docs]class WidgetPiControl(QtWidgets.QGroupBox, Ui_pi_control): def __init__(self, *args, obj=None, pi_stage: dict = {}, **kwargs): super(QtWidgets.QGroupBox, self).__init__(*args, **kwargs) self.setupUi(self) self.pi_stage = pi_stage self.threadpool = QThreadPool() # In case pi_stage is handed over with init we need to update # combobox This only relevant when running the widget directly if self.pi_stage: self.update_combobox() self.update_values() # Setting input validators ----------------------------- # Line Edits of "Wavegenerator" GroupBox self.lineEdit_pi_wave_coherence.setValidator( positive_float_input_validator ) # Assign input validator to this lineEdit self.lineEdit_pi_wave_frequency.setValidator(positive_float_input_validator) self.lineEdit_pi_wave_speed_up_down.setValidator(positive_int_input_validator) self.lineEdit_pi_overshoot_factor.setValidator(float_input_validator) # Line Edits of "Move" GroupBox self.lineEdit_pi_pos_mm.setValidator(float_input_validator) self.lineEdit_pi_pos_fs.setValidator(float_input_validator) self.lineEdit_pi_vel_mm.setValidator(positive_float_input_validator) self.lineEdit_pi_vel_fs.setValidator(positive_float_input_validator) self.lineEdit_pi_tzero_mm.setValidator(float_input_validator) # ------------------------------------------------------ # Setup signals --------------------------------------- # Set Velocity self.lineEdit_pi_vel_mm.editingFinished.connect(self.update_velocity_mm) self.lineEdit_pi_vel_fs.editingFinished.connect(self.update_velocity_fs) # Set Position self.lineEdit_pi_pos_mm.editingFinished.connect(self.update_position_mm) self.lineEdit_pi_pos_fs.editingFinished.connect(self.update_position_fs) self.pushButton_pi_reset_t_zero.clicked.connect(self.update_t_zero) self.pushButton_pi_wave.clicked.connect(self.update_wavegen) # Changing stage in ComboBox self.comboBox_pi_control.currentIndexChanged.connect(self.update_values) # ------------------------------------------------------
[docs] def wavegen(self): key = self.current_stage() if self.pi_stage[key].controller.has_wavegen: pass
[docs] def set_enabled_move(self, enabled: bool = True): self.lineEdit_pi_pos_fs.setEnabled(enabled) self.lineEdit_pi_pos_mm.setEnabled(enabled) self.lineEdit_pi_vel_fs.setEnabled(enabled) self.lineEdit_pi_vel_mm.setEnabled(enabled) self.pushButton_pi_reset_t_zero.setEnabled(enabled)
[docs] def set_enabled_wave(self, enabled: bool = True): self.lineEdit_pi_wave_coherence.setEnabled(enabled) self.lineEdit_pi_wave_frequency.setEnabled(enabled) self.lineEdit_pi_wave_speed_up_down.setEnabled(enabled) self.lineEdit_pi_overshoot_factor.setEnabled(enabled) self.pushButton_pi_wave.setEnabled(enabled)
[docs] def current_stage(self): """ Create method which holds information about which combobox item is currently selected. Returns: str: Returns key to currently active stage. """ # Create an attribute which holds information about which # combobox item is currently selected return self.comboBox_pi_control.currentText()
[docs] def update_combobox(self): # Add different stages to combo box drop down menu self.comboBox_pi_control.addItems(self.pi_stage.keys())
[docs] def update_values(self): # The if statement is necessary because some GUI features/ # elements need a hardware object (here the stages) to work # properly. Within the __init__ hardware objects such as the # stages are defaulted with None. This is because python # directly identifies that None type objects have no # further/additional attributes. And in this case we are # interested in the attributes of the pi_control class which # itself is passed as an attribute to the adc GUI (as shown in # __init__) If you still do not get it, remove the if statement # and try it out for yourself. if self.pi_stage: key = self.current_stage() # Line Edits of "Information" Group Box self.lineEdit_pi_tzero_mm.setText(str(round(self.pi_stage[key].t_zero, 3))) # Check if stage is connected via PCI board or COM port if hasattr(self.pi_stage[key].controller, "com_port"): string = "COM" + str(self.pi_stage[key].controller.com_port) self.lineEdit_pi_port.setText(string) elif hasattr(self.pi_stage[key].controller, "pci_board_number"): string = "PCI: " + str(self.pi_stage[key].controller.pci_board_number) self.lineEdit_pi_port.setText(string) self.lineEdit_pi_axis.setText(str(self.pi_stage[key].axis)) # Line Edits of "Move" GroupBox self.lineEdit_pi_pos_mm.setText(str(round(self.pi_stage[key].position, 3))) self.lineEdit_pi_pos_fs.setText( str(round(self.pi_stage[key].position_fs, 3)) ) self.lineEdit_pi_vel_mm.setText(str(round(self.pi_stage[key].velocity, 3))) self.lineEdit_pi_vel_fs.setText( str(round(self.pi_stage[key].velocity_fs, 3)) ) # Wavegeneration if self.pi_stage[key].controller.has_wavegen: # Wavegeneration is enabled if controller is able to use # wavegeneration self.set_enabled_wave() # Line Edits of "Wavegenerator" GroupBox if hasattr(self.pi_stage[key], "coherence_time"): self.lineEdit_pi_wave_coherence.setText( str(round(self.pi_stage[key].coherence_time, 3)) ) # If coherence time attribute exists the speed up # down attribute exists too self.lineEdit_pi_wave_speed_up_down.setText( str(self.pi_stage[key].speed_up_down) ) # If coherence time attribute exists the frequency # attribute exists too self.lineEdit_pi_wave_frequency.setText( str(round(self.pi_stage[key].approx_frequency, 3)) ) # If coherence time attribute exists the # overshoot_factor attribute exists too self.lineEdit_pi_overshoot_factor.setText( str(round(self.pi_stage[key].overshoot_factor, 3)) ) if self.pi_stage[key].wavegen_running: self.pushButton_pi_wave.setText("Stop Wavegeneration") # Disable Move Box when wavegen is running self.set_enabled_move(enabled=False) else: self.pushButton_pi_wave.setText("Start Wavegeneration") self.set_enabled_move(enabled=True) else: self.set_enabled_wave(enabled=False) # Reenable moving of stage if it does not have wavegen self.set_enabled_move(enabled=True) else: self.setDisabled(True)
[docs] def update_velocity_mm(self): self.work(self.lineEdit_pi_vel_mm, "velocity", "set_velocity_mm")
[docs] def update_velocity_fs(self): self.work(self.lineEdit_pi_vel_fs, "velocity_fs", "set_velocity")
[docs] def update_position_fs(self): self.work(self.lineEdit_pi_pos_fs, "position_fs", "move")
[docs] def update_position_mm(self): self.work(self.lineEdit_pi_pos_mm, "position", "move_mm")
[docs] def update_t_zero(self): self.work(self.lineEdit_pi_pos_mm, "t_zero", "set_t_zero")
[docs] def update_wavegen(self): # Define function to run in separate Thread def run(): # Get currently "opened" stage key = self.current_stage() if self.pi_stage[key].wavegen_running: self.pi_stage[key].stop_wavegen() else: coherence_time = float(self.lineEdit_pi_wave_coherence.text()) frequency = float(self.lineEdit_pi_wave_frequency.text()) speed_up_down = float(self.lineEdit_pi_wave_speed_up_down.text()) overshoot_factor = float(self.lineEdit_pi_overshoot_factor.text()) self.pi_stage[key].setup_coherence_time_scan( coherence_time, frequency, speed_up_down, overshoot_factor=overshoot_factor, ) self.pi_stage[key].start_wavegen() worker = Worker(run) worker.signals.finished.connect(self.update_values) self.threadpool.start(worker)
[docs] def work(self, qt_obj: QtWidgets, pi_attr: str, pi_method: str): def run(qt_obj: QtWidgets, pi_attr: str, pi_method: str): # Get currently "opened" stage key = self.current_stage() # Get current value current_value = getattr(self.pi_stage[key], pi_attr) # Get requested value requested_value = getattr(qt_obj, "text")() # Convert requested value to correct type requested_value = type(current_value)(requested_value) if round(current_value, 3) != requested_value: getattr(self.pi_stage[key], pi_method)(requested_value) worker = Worker(run, qt_obj, pi_attr, pi_method) worker.signals.finished.connect(self.update_values) self.threadpool.start(worker)
if __name__ == "__main__": # TESTING ----------------------------------------------------------- pi_stage = {} pi_stage["Interferometer Stage"] = None pi_stage["IR Delay Stage"] = None pi_stage["UV Delay Stage"] = None class MainWindow(QtWidgets.QMainWindow): def __init__(self, *args, obj=None, pi_stage=pi_stage, **kwargs): super(MainWindow, self).__init__(*args, **kwargs) self.setWindowTitle("Please god let this work") pi_widget = WidgetPiControl(pi_stage=pi_stage) self.setCentralWidget(pi_widget) # PiController C-843 delay_ctrl_name = "IR stage controller (C-843)" delay_ctrl = "C-843" # delay controller delay_stage_names = ["M-531.PD1"] delay_ctrl_port = 1 # ----------------- # PiStage C-843: M-531.PD1 delay_axis = "1" delay_t_0 = 155.0 # This is in mm! delay_init_vel = 10 # mm/s delay_name = "IR Stage" delay_direction = 1 # # Interferometer HeNe Settings # he_ne_wl = 632.8E-6 # unit in mm # HeNe Wavelength # # PiController C-413 # interferometer_ctrl_name = "interferometer controller (C-413)" # interferometer_ctrl = "C-413" # interferometer controller # interferometer_stage_names = ["V-522.1AA"] #? axis correct or recognized automatically? # interferometer_ctrl_port = "COM9" #----------------- # #PiStage C-413: V522.1AA # #Todo wir sollten das in der json file pump interferometer nennen (oder sowas, falls es irgendwann mal mehrere gibt) # interferometer_axis = "1" # interferometer_t_0 = 0.640068 + 300*he_ne_wl # This is in mm! # interferometer_init_vel = 1 # mm/s # interferometer_name = "Interferometer Stage" # interferometer_direct = 1 # This is correct # # PiController C-843 # delay_ctrl_name = "interferometer controller (C-413)" # delay_ctrl = "C-843" # delay_stage_names = ["M-415.PD", "M-406.6PD"] #? axis correct?????? # delay_pci_board_number = 1 # ------------------- # # PiStage C-843: M-415.PD # ir_delay_axis = "1" #-------------- # ir_delay_t0 = 136.717 # This is in mm! # ir_delay_init_vel = 15 # ir_delay_stage_name = "IR delay stage" # ir_delay_direct = 1 #? Is this correct? # # PiStage C-843: M-406.6PD # uv_delay_axis = "2" #-------------- # uv_delay_t0 = 75 # This is in mm! # uv_delay_init_vel = 5 # uv_delay_stage_name = "UV VIS delay stage" # uv_delay_direct = 1 #? Is this correct? app = QtWidgets.QApplication(sys.argv) with PiController( delay_ctrl, delay_stage_names, pci_board_number=delay_ctrl_port ) as delay_controller, PiStage( delay_controller, delay_axis, delay_t_0, delay_direction, delay_init_vel, name=delay_name, ) as delay_stage: # with pi_control.PiController(interferometer_ctrl, interferometer_stage_names, com_port=interferometer_ctrl_port, name=interferometer_ctrl_name) as interferometer_controller, \ # \ # pi_control.PiStage(interferometer_controller, interferometer_axis, interferometer_t_0, interferometer_direct, interferometer_init_vel, name=interferometer_name) as interferometer_stage, \ # \ # pi_control.PiController(delay_ctrl, delay_stage_names, pci_board_number=delay_pci_board_number, name=delay_ctrl_name) as delay_controller, \ # \ # pi_control.PiStage(delay_controller, ir_delay_axis, ir_delay_t0, ir_delay_direct, ir_delay_init_vel, name=ir_delay_stage_name) as ir_delay_stage, \ # \ # pi_control.PiStage(delay_controller, uv_delay_axis, uv_delay_t0, uv_delay_direct, uv_delay_init_vel, name=uv_delay_stage_name) as uv_delay_stage: pi_stage = {} # pi_stage["Interferometer Stage"] = interferometer_stage # pi_stage["IR Delay Stage"] = ir_delay_stage # pi_stage["UV/ VIS Delay Stage"] = uv_delay_stage pi_stage["IR Delay Stage"] = delay_stage window = MainWindow(pi_stage=pi_stage) window.show() app.exec()