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_pixel(self): self.work(self.lineEdit_fp_pixel, "current_pixel", "set_pixel")
[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_()