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_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()