Code indexing in gitaly is broken and leads to code not being visible to the user. We work on the issue with highest priority.

Skip to content
Snippets Groups Projects

Add alignment gui 1d v1

Merged appel_c requested to merge feat/add_alignment_gui_1d_v1 into main
Compare and Show latest version
1 file
+ 277
1
Compare changes
  • Side-by-side
  • Inline
@@ -80,7 +80,283 @@ class Alignment1D(BECWidget, QWidget):
super().__init__(client=client, gui_id=gui_id)
QWidget.__init__(self, parent)
self.get_bec_shortcuts()
self.ui_file = "Alignment_v3.ui"
self.ui_file = "alignment_1d.ui"
self.ui = None
self.progress_bar = None
self._dap_summary = None
self.arrow_item = None
self.init_ui()
def init_ui(self):
"""Initialise the UI from QT Designer file"""
current_path = os.path.dirname(__file__)
self.ui = UILoader(self).loader(os.path.join(current_path, self.ui_file))
layout = QVBoxLayout(self)
layout.addWidget(self.ui)
self.setLayout(layout)
# Customize the plotting widget
self._customise_bec_waveform_widget()
# Setup arrow item
self._setup_arrow_item()
# Setup Scan Control
self._setup_scan_control()
# Setup progress bar
self._setup_progress_bar()
# Setup filters for comboboxes
self._setup_motor_combobox()
self._setup_signal_combobox()
# Add actions buttons
self._add_action_buttons()
# Hook scaninfo updates
self.bec_dispatcher.connect_slot(self._scan_status_callback, MessageEndpoints.scan_status())
@Slot(dict, dict)
def _scan_status_callback(self, content: dict, metadata: dict) -> None:
"""This slot allows to enable/disable the UI critical components when a scan is running"""
if content["status"] in ["running", "open"]:
self.motion_is_active.emit(True)
self._enable_ui(False)
elif content["status"] in ["aborted", "halted", "closed"]:
self.motion_is_active.emit(False)
self._enable_ui(True)
def _enable_ui(self, enable: bool) -> None:
"""Enable or disable the UI components"""
# Device selection comboboxes
self.ui.device_combobox.setEnabled(enable)
self.ui.device_combobox_2.setEnabled(enable)
self.ui.dap_combo_box.setEnabled(enable)
# Scan button
self.ui.scan_button.setEnabled(enable)
# Positioner control line
# pylint: disable=protected-access
self.ui.positioner_control_line._toogle_enable_buttons(enable)
# Send report to scilog
self.ui.button_send_summary_scilog.setEnabled(enable)
# Disable move to buttons in LMFitDialog
self.ui.findChild(LMFitDialog).set_enable_move_to_buttons(enable)
def _add_action_buttons(self) -> None:
"""Add action buttons for the Action Control"""
fit_dialog = self.ui.findChild(LMFitDialog)
fit_dialog.update_activated_button_list(["center", "center1", "center2"])
fit_dialog.move_to_position.connect(self.move_to_center)
@Slot()
def move_to_center(self, pos: float) -> None:
"""Move the selected motor to the center"""
motor = self.ui.device_combobox.currentText()
self.dev[motor].move(float(pos), relative=False)
def _customise_bec_waveform_widget(self) -> None:
"""Customise the BEC Waveform Widget, i.e. hide toolbar buttons except roi selection"""
waveform = self.ui.findChild(BECWaveformWidget, "bec_waveform_widget")
for button in waveform.toolbar.widgets.values():
if getattr(button, "action", None) is not None:
button.action.setVisible(False)
waveform.toolbar.widgets["roi_select"].action.setVisible(True)
toggle_switch = self.ui.findChild(ToggleSwitch, "toggle_switch")
scan_control = self.ui.scan_control
waveform.toolbar.populate_toolbar(
{
"label": WidgetAction(label="Enable DAP ROI", widget=toggle_switch),
"scan_control": WidgetAction(widget=scan_control),
},
waveform,
)
def _setup_arrow_item(self) -> None:
"""Setup the arrow item"""
waveform = self.ui.findChild(BECWaveformWidget, "bec_waveform_widget")
self.arrow_item = BECArrowItem(parent=self, plot_item=waveform.waveform.plot_item)
self.arrow_item.add_to_plot()
positioner_line = self.ui.findChild(PositionerControlLine)
positioner_line.position_update.connect(self._update_arrow_item_position)
@Slot(float)
def _update_arrow_item_position(self, x_pos: float) -> None:
"""Update the position of the arrow item"""
motor_name = self.ui.device_combobox.currentText()
curve_label = (
f"{self.ui.device_combobox_2.currentText()}-{self.ui.device_combobox_2.currentText()}"
)
curves = self.ui.findChild(BECWaveformWidget, "bec_waveform_widget").waveform._curves_data
# Get the x and y data of the curve -> numpy array
if len(curves["scan_segment"]) == 0:
y_pos = 0
else:
x_data = curves["scan_segment"][curve_label].curve.xData
y_data = curves["scan_segment"][curve_label].curve.yData
if len(x_data) > 0:
closest_index = (np.abs(x_data - x_pos)).argmin()
y_pos = y_data[closest_index]
else:
y_pos = 0
self.arrow_item.set_position((x_pos, y_pos))
def _hook_arrow_item(self) -> None:
"""Hook the arrow item to updates from the position"""
self.ui.device_combobox.currentTextChanged.connect()
self.bec_dispatcher.connect_slot()
def _setup_motor_combobox(self) -> None:
"""Setup motor selection"""
motors = [name for name in self.dev if isinstance(self.dev.get(name), Positioner)]
self.ui.device_combobox.setCurrentText(motors[0])
self.ui.device_combobox.set_device_filter("Positioner")
def _setup_signal_combobox(self) -> None:
"""Setup signal selection"""
# TODO - here should also be Devices right?
signals = [name for name in self.dev if isinstance(self.dev.get(name), Signal)]
self.ui.device_combobox_2.setCurrentText(signals[0])
self.ui.device_combobox_2.set_device_filter("Signal")
def _setup_scan_control(self) -> None:
"""Setup scan control, connect spin and check boxes to the scan_control widget"""
device_line_edit = self.ui.scan_control.arg_box.findChild(DeviceLineEdit)
self.ui.device_combobox.currentTextChanged.connect(device_line_edit.setText)
spin_boxes = self.ui.scan_control.arg_box.findChildren(QDoubleSpinBox)
start = self.ui.findChild(QDoubleSpinBox, "linescan_start")
start.valueChanged.connect(spin_boxes[0].setValue)
stop = self.ui.findChild(QDoubleSpinBox, "linescan_stop")
stop.valueChanged.connect(spin_boxes[1].setValue)
step = self.ui.findChild(QSpinBox, "linescan_step")
step.valueChanged.connect(
self.ui.scan_control.kwarg_boxes[0].findChildren(QSpinBox)[0].setValue
)
exp_time = self.ui.findChild(QDoubleSpinBox, "linescan_exp_time")
exp_time.valueChanged.connect(
self.ui.scan_control.kwarg_boxes[1].findChildren(QDoubleSpinBox)[0].setValue
)
relative = self.ui.findChild(QCheckBox, "linescan_relative")
relative.toggled.connect(
self.ui.scan_control.kwarg_boxes[0].findChildren(QCheckBox)[0].setChecked
)
def _setup_progress_bar(self) -> None:
"""Setup progress bar"""
self.progress_bar = self.ui.findChild(BECProgressBar, "bec_progress_bar")
self.progress_bar.set_value(0)
self.ui.bec_waveform_widget.new_scan.connect(self._reset_progress_bar)
self.bec_dispatcher.connect_slot(
self._update_progress_bar, MessageEndpoints.scan_progress()
)
@Slot()
def _reset_progress_bar(self) -> None:
"""Reset the progress bar"""
self.progress_bar.set_value(0)
self.progress_bar.set_minimum(0)
@Slot(dict, dict)
def _update_progress_bar(self, content: dict, metadata: dict) -> None:
"""Hook to update the progress bar
Args:
content: Content of the scan progress message.
metadata: Metadata of the message.
"""
if content["max_value"] == 0:
self.progress_bar.set_value(0)
return
self.progress_bar.set_maximum(content["max_value"])
self.progress_bar.set_value(content["value"])
if __name__ == "__main__":
import sys
from qtpy.QtWidgets import QApplication
app = QApplication(sys.argv)
window = Alignment1D()
window.show()
sys.exit(app.exec_())
import os
from typing import Optional
import numpy as np
import pyqtgraph as pg
from bec_lib.device import Positioner, Signal
from bec_lib.endpoints import MessageEndpoints
from qtpy.QtCore import QObject
from qtpy.QtCore import Signal as pyqtSignal
from qtpy.QtWidgets import QCheckBox, QDoubleSpinBox, QSpinBox, QVBoxLayout, QWidget
from bec_widgets.qt_utils.error_popups import SafeSlot as Slot
from bec_widgets.qt_utils.toolbar import WidgetAction
from bec_widgets.utils import UILoader
from bec_widgets.utils.bec_widget import BECWidget
from bec_widgets.utils.colors import get_accent_colors
from bec_widgets.widgets.bec_progressbar.bec_progressbar import BECProgressBar
from bec_widgets.widgets.device_line_edit.device_line_edit import DeviceLineEdit
from bec_widgets.widgets.lmfit_dialog.lmfit_dialog import LMFitDialog
from bec_widgets.widgets.positioner_box.positioner_control_line import PositionerControlLine
from bec_widgets.widgets.toggle.toggle import ToggleSwitch
from bec_widgets.widgets.waveform.waveform_widget import BECWaveformWidget
class BECArrowItem(QObject):
position_changed = pyqtSignal(tuple[float, float])
def __init__(self, plot_item: pg.PlotItem = None, parent=None):
super().__init__(parent)
self.arrow_item = pg.ArrowItem()
self._accent_colors = get_accent_colors()
self.arrow_item.setStyle(
angle=-90,
pen=pg.mkPen(self._accent_colors.emergency, width=1),
brush=pg.mkBrush(self._accent_colors.highlight),
headLen=25,
)
self.plot_item = plot_item
@Slot(tuple)
def set_position(self, pos: tuple[float, float]):
"""Set the position of the arrow item"""
pos_x = pos[0]
pos_y = pos[1]
self.arrow_item.setPos(pos_x, pos_y)
@Slot(float)
def set_angle(self, angle: float):
"""Set the angle of the arrow item"""
self.arrow_item.setStyle(angle=angle)
def add_to_plot(self):
self.plot_item.addItem(self.arrow_item)
def remove_from_plot(self):
self.plot_item.removeItem(self.arrow_item)
def cleanup(self):
self.remove_from_plot()
self.deleteLater()
class Alignment1D(BECWidget, QWidget):
"""GUI for 1D alignment"""
motion_is_active = pyqtSignal(bool)
def __init__(
self, parent: Optional[QWidget] = None, client=None, gui_id: Optional[str] = None
) -> None:
"""Initialise the widget
Args:
parent: Parent widget.
config: Configuration of the widget.
client: BEC client object.
gui_id: GUI ID.
"""
super().__init__(client=client, gui_id=gui_id)
QWidget.__init__(self, parent)
self.get_bec_shortcuts()
self.ui_file = "alignment_1d.ui"
self.ui = None
self.progress_bar = None
self._dap_summary = None
Loading