Source code for gui.widget_fabry_perot
"""
This widget contains the functionality of the fabry_perot hardware class
provided in fabry_perot.py. The widget can be executed as a standalone
version.
########################################
Functionalities provided in this widget:
########################################
1. Set the wavelength in nm to which the Fabry Perot should be tuned
2. Set the wavenumber in 1/cm to which the Fabry Perot should be tuned
3. Set the Pixel to which the Fabry Perot should be tuned
4. Display the voltage which is currently applied to the Fabry Perot.
This is also needed to tune the Fabry Perot/ to reset the voltage to
a proper value so that the algorithm works
5. If the Fabry Perot is tuned in the main software, this widget
provides a plot (see code and comments for further explanation)
"""
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"))
from PyQt5 import QtWidgets, QtCore
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_fabry_perot import Ui_GroupBox_fabry_perot as Ui_fabry_perot
# Import fabry perot
from fabry_perot import FabryPerot
import numpy as np
import pyqtgraph as pg
# Logger settings
import logging
# logging.basicConfig(level=logging.DEBUG)
[docs]class WidgetFabryPerot(QtWidgets.QGroupBox, Ui_fabry_perot):
def __init__(
self, *args, obj=None, fabry_perot=None, widget_pyqtgraph=None, **kwargs
):
super(QtWidgets.QGroupBox, self).__init__(*args, **kwargs)
self.setupUi(self)
self.fabry_perot = fabry_perot
self.widget_pyqtgraph = widget_pyqtgraph
self.threadpool = QThreadPool()
# Set maximum number of threads to 10
self.threadpool.setMaxThreadCount(10)
# Load values
self.update_values()
# Setting input validators-----------------------------
self.lineEdit_fp_wavenumber.setValidator(
positive_float_input_validator
) # Assign input validator to this lineEdit
self.lineEdit_fp_wavelength.setValidator(positive_float_input_validator)
self.lineEdit_fp_pixel.setValidator(positive_int_with_0_input_validator)
# Setup signals
self.lineEdit_fp_wavenumber.returnPressed.connect(self.update_wavenumber)
self.lineEdit_fp_wavelength.returnPressed.connect(self.update_wavelength)
self.lineEdit_fp_pixel.returnPressed.connect(self.update_pixel)
self.lineEdit_fp_voltage.returnPressed.connect(self.update_voltage)
[docs] def update_values(self):
if not self.fabry_perot == None:
self.lineEdit_fp_wavenumber.setText(
str(round(self.fabry_perot.current_wavenumber, 3))
)
self.lineEdit_fp_wavelength.setText(
str(round(self.fabry_perot.current_wavelength, 3))
)
self.lineEdit_fp_pixel.setText(str(self.fabry_perot.current_pixel))
self.lineEdit_fp_voltage.setText(
str(round(self.fabry_perot.analog_voltage_controller.voltage, 3))
)
else:
self.setDisabled(True)
[docs] def work(self, qt_obj: QtWidgets, fp_attr: str, fp_method: str):
def run(qt_obj: QtWidgets, fp_attr: str, fp_method: str):
# Get current value
current_value = getattr(self.fabry_perot, fp_attr)
# Get requested value
requested_value = getattr(qt_obj, "text")()
# Convert requested value to correct type
requested_value = type(current_value)(requested_value)
# Call method / Execute
getattr(self.fabry_perot, fp_method)(requested_value)
def plot():
# Create reference to line that is going to be plotted
plot_ref = None
# Set color of line
probe_pen = pg.mkPen(color="#1f77b4", width=2.5, style=QtCore.Qt.SolidLine)
while True:
data = self.fabry_perot.queue.get()
if type(data) == str:
if data == "stop":
break
# Linearize pixel response
data = self.fabry_perot.pixel_linearisation.linearize(
data[self.fabry_perot.adc.probe_pixel_idx]
)
# Calculate the mean probe intensity for each probe
# pixel.
data = np.average(data, axis=-1)
if plot_ref:
# Update plot
plot_ref.setData(x=self.fabry_perot.spectrometer.wn_axis, y=data)
else:
# Plot for the first time
plot_ref = self.widget_pyqtgraph.plots["intensities"].plot(
x=self.fabry_perot.spectrometer.wn_axis,
y=data,
name="Average intensities on probe array",
pen=probe_pen,
)
# --- Initialize plot ---
# This can not be put into the plot function directly because
# the plot function is threaded and this causes timer issues
# when modifying base structure of the pyqtgraph widget
# Clear old plots
self.widget_pyqtgraph.remove_plots()
# Setup plot that displays intensities
self.widget_pyqtgraph.plots[
"intensities"
] = self.widget_pyqtgraph.graphics_layout.addPlot(row=0, col=0, rowspan=2)
self.widget_pyqtgraph.plots["intensities"].setTitle("Average intensities")
self.widget_pyqtgraph.plots["intensities"].setLabel(
"bottom", "wavenumber [cm<sup>-1</sup>]"
)
self.widget_pyqtgraph.plots["intensities"].setLabel("left", "intensity [a.u.]")
self.widget_pyqtgraph.set_style(self.widget_pyqtgraph.plots["intensities"])
# --- ----
worker = Worker(run, qt_obj, fp_attr, fp_method)
plot_worker = Worker(plot)
# Deactivate GUI
worker.signals.started.connect(lambda: self.setDisabled(True))
# Reactivate GUI
worker.signals.finished.connect(lambda: self.setEnabled(True))
# Update values
worker.signals.finished.connect(self.update_values)
# Start worker once plotting is ready
plot_worker.signals.started.connect(lambda: self.threadpool.start(worker))
self.threadpool.start(plot_worker)
[docs] def update_wavenumber(self):
self.work(self.lineEdit_fp_wavenumber, "current_wavenumber", "set_wavenumber")
[docs] def update_wavelength(self):
self.work(self.lineEdit_fp_wavelength, "current_wavelength", "set_wavelength")
[docs] def update_voltage(self):
# This method cannot use the general work method because it
# needs to access the analog voltage controller
def run(self):
# Get current value
current_value = self.fabry_perot.analog_voltage_controller.voltage
# Get requested value
requested_value = self.lineEdit_fp_voltage.text()
# Convert requested value to correct type
requested_value = type(current_value)(requested_value)
if round(current_value, 3) != requested_value:
# Call method / Execute
self.fabry_perot.analog_voltage_controller.set_voltage(requested_value)
worker = Worker(run, self)
# Deactivate GUI
worker.signals.started.connect(lambda: self.setDisabled(True))
# Reactivate GUI
worker.signals.finished.connect(lambda: self.setEnabled(True))
# Update values
worker.signals.finished.connect(self.update_values)
self.threadpool.start(worker)
if __name__ == "__main__":
# TESTING -----------------------------------------------------------
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, *args, obj=None, fabry_perot=None, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
self.setWindowTitle("Please god let this work")
widget = WidgetFabryPerot(fabry_perot=fabry_perot)
self.setCentralWidget(widget)
app = QtWidgets.QApplication(sys.argv)
# with ADC("Dev2", "./hardware_config_files/H-Lab analog input configuration.json", 30*3000, "PFI4", 3000, name="NI_DIO") as adc:
window = MainWindow()
window.show()
app.exec_()