From 3d2485aea77872aa82126cda36855326a66e9fb9 Mon Sep 17 00:00:00 2001 From: x01da Date: Tue, 5 May 2026 15:32:01 +0200 Subject: [PATCH] wip: digital twin --- .../widgets/digital_twin/calc_surfaces.py | 2 +- .../digital_twin/calculate_positions.py | 2 +- .../digital_twin/calculate_sideview.py | 2 +- .../widgets/digital_twin/digital_twin.py | 261 +++++++++++------- debye_bec/bec_widgets/widgets/qt_widgets.py | 256 ++++++++--------- .../{digital_twin => }/x01da_parameters.py | 0 6 files changed, 274 insertions(+), 249 deletions(-) rename debye_bec/bec_widgets/widgets/{digital_twin => }/x01da_parameters.py (100%) diff --git a/debye_bec/bec_widgets/widgets/digital_twin/calc_surfaces.py b/debye_bec/bec_widgets/widgets/digital_twin/calc_surfaces.py index 25a129b..f5d408a 100644 --- a/debye_bec/bec_widgets/widgets/digital_twin/calc_surfaces.py +++ b/debye_bec/bec_widgets/widgets/digital_twin/calc_surfaces.py @@ -4,7 +4,7 @@ import numpy as np from bec_lib import bec_logger os.environ["USE_XRT"] = "False" -import debye_bec.bec_widgets.widgets.digital_twin.x01da_parameters as bl +import debye_bec.bec_widgets.widgets.x01da_parameters as bl def calc_surfaces(cfg): diff --git a/debye_bec/bec_widgets/widgets/digital_twin/calculate_positions.py b/debye_bec/bec_widgets/widgets/digital_twin/calculate_positions.py index eda5698..8012baf 100644 --- a/debye_bec/bec_widgets/widgets/digital_twin/calculate_positions.py +++ b/debye_bec/bec_widgets/widgets/digital_twin/calculate_positions.py @@ -3,7 +3,7 @@ import numpy as np from bec_lib import bec_logger os.environ["USE_XRT"] = "False" -import debye_bec.bec_widgets.widgets.digital_twin.x01da_parameters as bl +import debye_bec.bec_widgets.widgets.x01da_parameters as bl logger = bec_logger.logger diff --git a/debye_bec/bec_widgets/widgets/digital_twin/calculate_sideview.py b/debye_bec/bec_widgets/widgets/digital_twin/calculate_sideview.py index 6312a09..ce97a31 100644 --- a/debye_bec/bec_widgets/widgets/digital_twin/calculate_sideview.py +++ b/debye_bec/bec_widgets/widgets/digital_twin/calculate_sideview.py @@ -1,5 +1,5 @@ import numpy as np -import debye_bec.bec_widgets.widgets.digital_twin.x01da_parameters as bl +import debye_bec.bec_widgets.widgets.x01da_parameters as bl def calc_sideview(cfg): diff --git a/debye_bec/bec_widgets/widgets/digital_twin/digital_twin.py b/debye_bec/bec_widgets/widgets/digital_twin/digital_twin.py index 1ae45a1..b3a9846 100644 --- a/debye_bec/bec_widgets/widgets/digital_twin/digital_twin.py +++ b/debye_bec/bec_widgets/widgets/digital_twin/digital_twin.py @@ -1,40 +1,54 @@ +""" +Digital Twin: Custom BEC widget to support the beamline alignment. +""" + import sys import re -import datetime import numpy as np from scipy.interpolate import UnivariateSpline +from xrt.backends.raycing.physconsts import CHeVcm, AVOGADRO from bec_lib import bec_logger + # pylint: disable=E0611 from qtpy.QtWidgets import ( - QWidget, QVBoxLayout, QHBoxLayout, QLabel, - QApplication, QLayout, QGroupBox + QWidget, + QVBoxLayout, + QHBoxLayout, + QApplication, + QLayout, ) # pylint: disable=E0611 -from qtpy.QtCore import Qt, QTimer -from qtpy.QtGui import QColor, QFont, QBrush +from qtpy.QtCore import ( + Qt, + QTimer, +) +from qtpy.QtGui import ( + QColor, + QBrush, +) import pyqtgraph as pg -from xrt.backends.raycing.physconsts import CHeVcm, AVOGADRO - from bec_widgets.utils.bec_widget import BECWidget from bec_widgets.utils.error_popups import SafeSlot -from debye_bec.bec_widgets.widgets.qt_widgets import InputNumberField, ComboBox, Group, NumberIndicator - +from debye_bec.bec_widgets.widgets.qt_widgets import ( + InputNumberField, + ComboBox, + Group, + NumberIndicator, + Mover, +) from debye_bec.bec_widgets.widgets.digital_twin.calculate_positions import calc_positions from debye_bec.bec_widgets.widgets.digital_twin.calculate_sideview import calc_sideview from debye_bec.bec_widgets.widgets.digital_twin.calc_surfaces import calc_surfaces -import debye_bec.bec_widgets.widgets.digital_twin.x01da_parameters as bl +import debye_bec.bec_widgets.widgets.x01da_parameters as bl logger = bec_logger.logger class DigitalTwin(BECWidget, QWidget): """ - A simple BEC widget with: - - Two numeric inputs (A, B) - - Two computed outputs (Sum, Product) - - A live plot that updates every second + Main widget of Digital Twin """ PLUGIN = True @@ -53,65 +67,94 @@ class DigitalTwin(BECWidget, QWidget): self.sideview_plot = SideviewPlot() self.surface_plots = SurfacePlots() self.positions = PositionsPanel() + # self.mover = MoverPanel(dev=self.dev) self.root_layout.addWidget(self.input, stretch=1, alignment=Qt.AlignTop) # type: ignore self.plot_layout.addWidget(self.sideview_plot) # type: ignore self.plot_layout.addWidget(self.surface_plots) # type: ignore self.root_layout.addWidget(self.plot_widget, stretch=1, alignment=Qt.AlignTop) # type: ignore self.root_layout.addWidget(self.positions, stretch=1, alignment=Qt.AlignTop) # type: ignore + # self.root_layout.addWidget(self.mover, stretch=1, alignment=Qt.AlignTop) # type: ignore self.setLayout(self.root_layout) self.setWindowTitle("Digital Twin") self.resize(1800, 800) - self.input.energy.value_changed_connect(self.calc_bragg_angle) - self.input.sldi_hacc.value_changed_connect(self.calc_positions) - self.input.sldi_vacc.value_changed_connect(self.calc_positions) - self.input.cm_stripe.activated_connect(self.calc_positions) - self.input.cm_pitch.value_changed_connect(self.calc_positions) - self.input.mo1_mode.activated_connect(self.calc_positions) - self.input.fm_stripe.activated_connect(self.calc_positions) - self.input.fm_pitch.value_changed_connect(self.calc_positions) - self.input.smpl.value_changed_connect(self.calc_positions) - - self.input.energy.value_changed_connect(self.calc_crit_angle) - self.input.cm_stripe.activated_connect(self.calc_crit_angle) - - self.input.mo1_xtal.activated_connect(self.calc_bragg_angle) - - self.input.mo1_mode.activated_connect(self.update_mono_mode) - - self.input.fm_stripe.activated_connect(self.calc_ideal_fm_pitch) - self.input.smpl.value_changed_connect(self.calc_ideal_fm_pitch) - - self.input.energy.value_changed_connect(self.calc_cm_reflectivity) - self.input.cm_pitch.value_changed_connect(self.calc_cm_reflectivity) - self.input.cm_stripe.activated_connect(self.calc_cm_reflectivity) - self.input.fm_pitch.value_changed_connect(self.calc_fm_reflectivity) - self.input.fm_stripe.activated_connect(self.calc_fm_reflectivity) - self.input.energy.value_changed_connect(self.calc_cm_reflectivity) - - self.input.energy.value_changed_connect(self.calc_energy_resolution) - self.input.mo1_xtal.activated_connect(self.calc_energy_resolution) + self.input.energy.value_changed_connect(self.calc_assistant) + self.input.sldi_hacc.value_changed_connect(self.calc_assistant) + self.input.sldi_vacc.value_changed_connect(self.calc_assistant) + self.input.cm_stripe.activated_connect(self.calc_assistant) + self.input.cm_pitch.value_changed_connect(self.calc_assistant) + self.input.mo1_mode.activated_connect(self.calc_assistant) + self.input.mo1_xtal.activated_connect(self.calc_assistant) + self.input.fm_stripe.activated_connect(self.calc_assistant) + self.input.fm_pitch.value_changed_connect(self.calc_assistant) + self.input.smpl.value_changed_connect(self.calc_assistant) self.bragg_angle = 0 - self.calc_bragg_angle() - self.calc_ideal_fm_pitch() - self.calc_crit_angle() - self.calc_assistant_sideview() - self.calc_reality_sideview() - self.calc_cm_reflectivity() - self.calc_fm_reflectivity() - self.calc_energy_resolution() + + # Initialize all values + self.calc_assistant(identifier='init') # Timer: update plot every 1 second self._timer = QTimer(self) self._timer.setInterval(1000) - self._timer.timeout.connect(self.calc_reality_sideview) + self._timer.timeout.connect(self.calc_reality) # TODO: Check if I need to stop the timer if the widget is closed? self._timer.start() - def calc_energy_resolution(self, *args): + @SafeSlot() + def calc_assistant(self, *args, **kwargs): + identifier = kwargs['identifier'] + match identifier: + case 'init': + self.calc_mo1_bragg_angle() + self.calc_cm_crit_pitch() + self.calc_cm_reflectivity() + self.calc_fm_reflectivity() + self.calc_cm_fm_harm_suppr() + self.calc_fm_ideal_pitch() + self.calc_mo1_energy_resolution() + case 'energy': + self.calc_mo1_bragg_angle() + self.calc_cm_crit_pitch() + self.calc_cm_reflectivity() + self.calc_fm_reflectivity() + self.calc_cm_fm_harm_suppr() + self.calc_mo1_energy_resolution() + case 'cm_stripe': + self.calc_cm_crit_pitch() + self.calc_cm_reflectivity() + self.calc_cm_fm_harm_suppr() + case 'cm_pitch': + self.calc_cm_reflectivity() + self.calc_cm_fm_harm_suppr() + case 'mo1_mode': + self.update_mo1_mode() + case 'mo1_xtal': + self.calc_mo1_bragg_angle() + self.calc_mo1_energy_resolution() + case 'fm_stripe': + self.calc_fm_reflectivity() + self.calc_cm_fm_harm_suppr() + self.calc_fm_ideal_pitch() + case 'smpl': + self.calc_fm_ideal_pitch() + self.calc_positions() + self.calc_assistant_sideview() + self.calc_assistant_surfaces() + + @SafeSlot() + def calc_reality(self): + config = self.get_reality_config() + beam = calc_sideview(config) + self.sideview_plot.data['reality']['x'] = beam['Z'] + self.sideview_plot.data['reality']['y'] = beam['Y'] + self.sideview_plot.update_curves() + surfaces = calc_surfaces(config) + self.surface_plots.update_surfaces(scene='reality', data=surfaces) + + def calc_mo1_energy_resolution(self, *args, **kwargs): xtal = self.input.mo1_xtal.currentText().translate(str.maketrans('', '', '()')) # Remove brackets from xtal name to conform with parameters index = bl.mo1.xtal.index(xtal) crystal = bl.mo1.material1[index] @@ -139,7 +182,7 @@ class DigitalTwin(BECWidget, QWidget): self.input.mo1_eres.setValue(dE) - def calc_cm_reflectivity(self, *args): + def calc_cm_reflectivity(self): index = bl.cm.surface.index(self.input.cm_stripe.currentText()) rs, rp = bl.cm.material[index].get_amplitude( self.input.energy.value(), @@ -154,11 +197,7 @@ class DigitalTwin(BECWidget, QWidget): self.input.cm_refl_harm.setValue(100 * abs(rs)**2) self.input.cm_refl_harm.setLabel(f"Reflectivity at \n{3 * self.input.energy.value():.0f} eV") - harm_suppr = (self.input.cm_refl.value() * self.input.fm_refl.value()) / (self.input.cm_refl_harm.value() * self.input.fm_refl_harm.value()) - self.input.cm_fm_harm_suppr.setValue(harm_suppr) - self.input.cm_fm_harm_suppr.setLabel(f"Total Suppression Factor at {3 * self.input.energy.value():.0f} eV") - - def calc_fm_reflectivity(self, *args): + def calc_fm_reflectivity(self): if self.input.fm_stripe.currentText() in ('Rh (toroid)', 'Pt (toroid)'): surface = bl.fm.surfaceToroid material = bl.fm.materialToroid @@ -182,6 +221,7 @@ class DigitalTwin(BECWidget, QWidget): self.input.fm_refl_harm.setValue(100 * abs(rs)**2) self.input.fm_refl_harm.setLabel(f"Reflectivity at \n{3 * self.input.energy.value():.0f} eV") + def calc_cm_fm_harm_suppr(self): harm_suppr = (self.input.cm_refl.value() * self.input.fm_refl.value()) / (self.input.cm_refl_harm.value() * self.input.fm_refl_harm.value()) self.input.cm_fm_harm_suppr.setValue(harm_suppr) self.input.cm_fm_harm_suppr.setLabel(f"Total Suppression Factor at {3 * self.input.energy.value():.0f} eV") @@ -203,8 +243,7 @@ class DigitalTwin(BECWidget, QWidget): } # logger.info(f'Config created: {config}') return config - - # TODO Needs to run in a loop in a separate thread due to the long time it takes to get the values from self.dev... + def get_reality_config(self): if abs(self.dev.mo1_trx.read()['mo1_trx']['value']) > 5: mo1_mode = 'Monochromatic' @@ -221,6 +260,7 @@ class DigitalTwin(BECWidget, QWidget): for name, low, high in zip(bl.cm.surface, bl.cm.limOptX[0], bl.cm.limOptX[1]): if low <= cm_trx <= high: cm_stripe = name + cm_pitch = -self.dev.cm_rotx.read()['cm_rotx']['value'] * 1e-3 fm_trx = -self.dev.fm_trx.read()['fm_trx']['value'] fm_stripe = None for name, low, high in zip(bl.fm.surfaceFlat, bl.fm.limOptXFlat[1], bl.fm.limOptXFlat[0]): @@ -229,16 +269,18 @@ class DigitalTwin(BECWidget, QWidget): for name, low, high in zip(bl.fm.surfaceToroid, bl.fm.limOptXToroid[1], bl.fm.limOptXToroid[0]): if low <= fm_trx <= high: fm_stripe = name + ' (toroid)' + fm_pitch = -self.dev.fm_rotx.read()['fm_rotx']['value'] * 1e-3 + fm_pitch_real = 2 * cm_pitch - fm_pitch config = { # Config in SI units! 'energy' : mo1_bragg['mo1_bragg']['value'], 'h_acc' : h_acc, 'v_acc' : v_acc, - 'cm_pitch' : -self.dev.cm_rotx.read()['cm_rotx']['value'] * 1e-3, + 'cm_pitch' : cm_pitch, 'cm_stripe' : cm_stripe, 'mo1_mode' : mo1_mode, 'mo1_xtal' : mo1_bragg['mo1_bragg_crystal_current_xtal_string']['value'], 'mo1_bragg' : mo1_bragg['mo1_bragg_angle']['value']/180*np.pi, - 'fm_pitch' : -self.dev.fm_rotx.read()['fm_rotx']['value'] * 1e-3, + 'fm_pitch' : fm_pitch_real, 'fm_stripe' : fm_stripe, 'fm_gain_height' : 1, 'smpl' : self.dev.ot_es1_trz.read()['ot_es1_trz']['value'], @@ -247,35 +289,19 @@ class DigitalTwin(BECWidget, QWidget): return config @SafeSlot() - def calc_assistant_sideview(self, *args): + def calc_assistant_sideview(self): beam = calc_sideview(self.get_assistant_config()) self.sideview_plot.data['assistant']['x'] = beam['Z'] self.sideview_plot.data['assistant']['y'] = beam['Y'] self.sideview_plot.update_curves() @SafeSlot() - def calc_reality_sideview(self): - # logger.info('Update reality plot') - beam = calc_sideview(self.get_reality_config()) - self.sideview_plot.data['reality']['x'] = beam['Z'] - self.sideview_plot.data['reality']['y'] = beam['Y'] - self.sideview_plot.update_curves() - - # TODO Move to different place - self.calc_reality_surfaces() - - @SafeSlot() - def calc_assistant_surfaces(self, *args): + def calc_assistant_surfaces(self): surfaces = calc_surfaces(self.get_assistant_config()) self.surface_plots.update_surfaces(scene='assistant', data=surfaces) @SafeSlot() - def calc_reality_surfaces(self, *args): - surfaces = calc_surfaces(self.get_reality_config()) - self.surface_plots.update_surfaces(scene='reality', data=surfaces) - - @SafeSlot() - def calc_positions(self, *args): + def calc_positions(self): out = calc_positions(self.get_assistant_config()) self.positions.sldi_gapx.setValue(out['sldi_gapx']['value']) @@ -299,12 +325,8 @@ class DigitalTwin(BECWidget, QWidget): self.positions.ot_rotx.setValue(out['ot_rotx']['value']) self.positions.ot_es1_trz.setValue(out['ot_es1_trz']['value']) - # TODO move to somewhere else! - self.calc_assistant_sideview() - self.calc_assistant_surfaces() - @SafeSlot() - def calc_bragg_angle(self, *args): + def calc_mo1_bragg_angle(self): """ Calculates bragg angle in rad """ @@ -335,17 +357,17 @@ class DigitalTwin(BECWidget, QWidget): bragg_angle_cor = (2 * cm_pitch) / np.pi * 180 self.input.mo1_bragg_angle.setValue(bragg_angle_cor) - self.calc_positions() + # self.calc_positions() @SafeSlot() - def update_mono_mode(self, *args): + def update_mo1_mode(self): if self.input.mo1_mode.currentText() in 'Monochromatic': self.input.mo1_xtal.setDisabled(False) else: self.input.mo1_xtal.setDisabled(True) @SafeSlot() - def calc_ideal_fm_pitch(self, *args): + def calc_fm_ideal_pitch(self): p = bl.fm.center[1] # posFM q = self.input.smpl.value() - bl.fm.center[1] # dist posFM to posEX f = (p * q) / (p + q) # focal length @@ -357,7 +379,7 @@ class DigitalTwin(BECWidget, QWidget): self.input.fm_pitch_ideal.setValue(-pitch * 1e3) @SafeSlot() - def calc_crit_angle(self, *args): + def calc_cm_crit_pitch(self): stripe = self.input.cm_stripe.currentText() # Config Mirror if stripe in 'Si': @@ -384,11 +406,11 @@ class InputPanel(QWidget): self._layout.setSizeConstraint(QLayout.SetFixedSize) # type: ignore # Energy - self.energy = InputNumberField('Energy [eV]', init=8979, decimals=0, single_step=100, ll=4000, hl=65000) + self.energy = InputNumberField('energy', 'Energy [eV]', init=8979, decimals=0, single_step=100, ll=4000, hl=65000) # FE Slits Acceptance - self.sldi_hacc = InputNumberField('Horizontal [± mrad]', init=0.25, decimals=2, single_step=0.01, ll=-0.1, hl=0.9) - self.sldi_vacc = InputNumberField('Vertical [± mrad]', init=0.1, decimals=2, single_step=0.01, ll=-0.1, hl=0.5) + self.sldi_hacc = InputNumberField('h_acc', 'Horizontal [± mrad]', init=0.25, decimals=2, single_step=0.01, ll=-0.1, hl=0.9) + self.sldi_vacc = InputNumberField('v_acc', 'Vertical [± mrad]', init=0.1, decimals=2, single_step=0.01, ll=-0.1, hl=0.5) self.sldi_ass_group = Group( 'FE Slits Acceptance', [ @@ -398,9 +420,9 @@ class InputPanel(QWidget): ) # Collimating mirror - self.cm_stripe = ComboBox('Stripe', ['Si', 'Rh', 'Pt']) + self.cm_stripe = ComboBox('cm_stripe', 'Stripe', ['Si', 'Rh', 'Pt']) self.cm_pitch_critical = NumberIndicator('Critical Pitch', 'mrad', decimals=3) - self.cm_pitch = InputNumberField('Pitch [mrad]', init=-2.391, decimals=3, single_step=0.01, ll=-4.6, hl=-1.2) + self.cm_pitch = InputNumberField('cm_pitch', 'Pitch [mrad]', init=-2.391, decimals=3, single_step=0.01, ll=-4.6, hl=-1.2) self.cm_refl = NumberIndicator('Reflectivity at x eV', '%', decimals=0) self.cm_refl_harm = NumberIndicator('Reflectivity at x eV', '%', decimals=0) self.cm_ass_group = Group( @@ -415,8 +437,8 @@ class InputPanel(QWidget): ) # Monochromator - self.mo1_mode = ComboBox('Mode', ['Monochromatic', 'Pinkbeam']) - self.mo1_xtal = ComboBox('Crystal', ['Si(111)', 'Si(311)']) + self.mo1_mode = ComboBox('mo1_mode', 'Mode', ['Monochromatic', 'Pinkbeam']) + self.mo1_xtal = ComboBox('mo1_xtal', 'Crystal', ['Si(111)', 'Si(311)']) self.mo1_bragg_angle = NumberIndicator('Bragg Angle', 'deg', decimals=1) self.mo1_eres = NumberIndicator('Energy Resolution', 'eV', decimals=2) self.mo1_ass_group = Group( @@ -430,9 +452,9 @@ class InputPanel(QWidget): ) # Focusing Mirror - self.fm_stripe = ComboBox('Stripe', ['Rh (toroid)', 'Rh (flat)', 'Pt (toroid)', 'Pt (flat)']) + self.fm_stripe = ComboBox('fm_stripe', 'Stripe', ['Rh (toroid)', 'Rh (flat)', 'Pt (toroid)', 'Pt (flat)']) self.fm_pitch_ideal = NumberIndicator('Ideal Pitch', 'mrad', decimals=3) - self.fm_pitch = InputNumberField('Pitch [mrad]', init=-2.391, decimals=3, single_step=0.01, ll=-10, hl=2) + self.fm_pitch = InputNumberField('fm_pitch', 'Pitch [mrad]', init=-2.391, decimals=3, single_step=0.01, ll=-10, hl=2) self.fm_refl = NumberIndicator('Reflectivity at x eV', '%', decimals=0) self.fm_refl_harm = NumberIndicator('Reflectivity at x eV', '%', decimals=0) self.fm_ass_group = Group( @@ -448,7 +470,7 @@ class InputPanel(QWidget): # Sample self.cm_fm_harm_suppr = NumberIndicator('Total Suppression Factor at x eV', '', decimals=0) - self.smpl = InputNumberField('Sample Position [mm]', init=23511, decimals=0, single_step=100, ll=23000, hl=30000) + self.smpl = InputNumberField('smpl', 'Sample Position [mm]', init=23511, decimals=0, single_step=100, ll=23000, hl=30000) # Assemble complete assitant group self.input_group = Group( @@ -597,6 +619,43 @@ class PositionsPanel(QWidget): self._layout .addWidget(self.position_group) self._layout .addStretch() +class MoverPanel(QWidget): + + def __init__(self, dev, parent=None): + super().__init__(parent) + self.dev = dev + self._layout = QVBoxLayout(self) + self._layout.setSizeConstraint(QLayout.SetFixedSize) # type: ignore + + mot = self.dev.sldi_gapx + egu = self.dev.sldi_gapx.egu() + prec = self.dev.sldi_gapx.precision + self.sldi_gapx = Mover(mot, egu, prec) + + mot = self.dev.sldi_gapy + egu = self.dev.sldi_gapy.egu() + prec = self.dev.sldi_gapy.precision + self.sldi_gapy = Mover(mot, egu, prec) + + self.sldi_mov_group = Group( + 'FE Slits', + [ + self.sldi_gapx, + self.sldi_gapy, + ] + ) + + # Assemble complete assitant group + self.mover_group = Group( + 'Mover', + [ + self.sldi_mov_group, + ] + ) + + self._layout .addWidget(self.mover_group) + self._layout .addStretch() + class SurfacePlots(QWidget): """Plot widget with two curves and legend.""" diff --git a/debye_bec/bec_widgets/widgets/qt_widgets.py b/debye_bec/bec_widgets/widgets/qt_widgets.py index d9f3d6b..3791faf 100644 --- a/debye_bec/bec_widgets/widgets/qt_widgets.py +++ b/debye_bec/bec_widgets/widgets/qt_widgets.py @@ -42,7 +42,7 @@ class Group(QGroupBox): # self.value.setText(text) class NumberIndicator(QWidget): - def __init__(self, label, unit=None, highlight=False, decimals=3): + def __init__(self, label='', unit=None, highlight=False, decimals=3): super().__init__() layout = QHBoxLayout(self) layout.setContentsMargins(10, 0, 0, 0) @@ -80,44 +80,45 @@ class NumberIndicator(QWidget): text = text + ' ' + self.unit self.val.setText(text) -class InputTextField(QWidget): - def __init__(self, topic, label): - super().__init__() - self.topic = topic - layout = QHBoxLayout(self) - layout.setContentsMargins(10, 0, 0, 0) - layout.setSpacing(0) - self.label = QLabel(label) - self.label.setFixedWidth(140) - self.label.setContentsMargins(0, 0, 10, 0) - self.label.setWordWrap(True) - layout.addWidget(self.label) - self.val = QLineEdit() - self.val.setPlaceholderText('0') - # self.val.setFixedWidth(140) - layout.addWidget(self.val) +# class InputTextField(QWidget): +# def __init__(self, topic, label): +# super().__init__() +# self.topic = topic +# layout = QHBoxLayout(self) +# layout.setContentsMargins(10, 0, 0, 0) +# layout.setSpacing(0) +# self.label = QLabel(label) +# self.label.setFixedWidth(140) +# self.label.setContentsMargins(0, 0, 10, 0) +# self.label.setWordWrap(True) +# layout.addWidget(self.label) +# self.val = QLineEdit() +# self.val.setPlaceholderText('0') +# # self.val.setFixedWidth(140) +# layout.addWidget(self.val) - def set_text(self, text): - self.val.setText(text) +# def set_text(self, text): +# self.val.setText(text) - def has_focus(self) -> bool: - return self.val.hasFocus() +# def has_focus(self) -> bool: +# return self.val.hasFocus() - def text(self) -> str: - return self.val.text() +# def text(self) -> str: +# return self.val.text() - def set_on_return(self, func): - """Connect a function to the Enter/Return key press.""" - self.val.returnPressed.connect( - partial(func, self.val, self.topic, lambda: self.val.text()) - ) +# def set_on_return(self, func): +# """Connect a function to the Enter/Return key press.""" +# self.val.returnPressed.connect( +# partial(func, self.val, self.topic, lambda: self.val.text()) +# ) class InputNumberField(QWidget): - def __init__(self, label, init=0.0, decimals=1, single_step=0.1, ll=-1e6, hl=1e6): + def __init__(self, identifier='', label='', init=0.0, decimals=1, single_step=0.1, ll=-1e6, hl=1e6): super().__init__() layout = QHBoxLayout(self) layout.setContentsMargins(10, 0, 0, 0) layout.setSpacing(0) + self.identifier = identifier self.label = QLabel(label) self.label.setFixedWidth(140) self.label.setContentsMargins(0, 0, 10, 0) @@ -143,74 +144,16 @@ class InputNumberField(QWidget): def value_changed_connect(self, func): """Connect a function to the Enter/Return key press.""" self.val.valueChanged.connect( - partial(func, self.val, lambda: self.val.value()) + partial(func, identifier=self.identifier, value_obj=self.val, value=lambda: self.val.value()) ) -# class IPAdressInputField(QWidget): -# def __init__(self, topic, label): -# super().__init__() -# self.topic = topic -# layout = QHBoxLayout(self) -# layout.setContentsMargins(10, 0, 0, 0) -# layout.setSpacing(0) -# self.label = QLabel(label) -# self.label.setFixedWidth(150) -# layout.addWidget(self.label) -# self.oct0 = QLineEdit() -# self.oct0.setPlaceholderText('0') -# self.oct0.setFixedWidth(30) -# layout.addWidget(self.oct0) -# separator1 = QLabel('.') -# layout.addWidget(separator1) -# self.oct1 = QLineEdit() -# self.oct1.setPlaceholderText('0') -# self.oct1.setFixedWidth(30) -# layout.addWidget(self.oct1) -# separator2 = QLabel('.') -# layout.addWidget(separator2) -# self.oct2 = QLineEdit() -# self.oct2.setPlaceholderText('0') -# self.oct2.setFixedWidth(30) -# layout.addWidget(self.oct2) -# separator3 = QLabel('.') -# layout.addWidget(separator3) -# self.oct3 = QLineEdit() -# self.oct3.setPlaceholderText('0') -# self.oct3.setFixedWidth(30) -# layout.addWidget(self.oct3) - -# self.oct0.editingFinished.connect(partial(self.check_octet, self.oct0)) -# self.oct1.editingFinished.connect(partial(self.check_octet, self.oct1)) -# self.oct2.editingFinished.connect(partial(self.check_octet, self.oct2)) -# self.oct3.editingFinished.connect(partial(self.check_octet, self.oct3)) - -# def check_octet(self, octet): -# if octet.text().isnumeric(): -# if int(octet.text()) < 0: -# octet.setText('0') -# if int(octet.text()) > 254: -# octet.setText('254') -# else: -# octet.setText('') - -# def get_ip(self): -# return f'{self.oct0.text()}.{self.oct1.text()}.{self.oct2.text()}.{self.oct3.text()}' - -# def set_ip(self, ip): -# octets = ip.split('.') -# if len(octets) == 4 and all(octet.isnumeric() for octet in octets): -# if all(int(octet) > 0 and int(octet) < 254 for octet in octets): -# self.oct0.setText(octets[0]) -# self.oct1.setText(octets[1]) -# self.oct2.setText(octets[2]) -# self.oct3.setText(octets[3]) - class ComboBox(QWidget): - def __init__(self, label, enums): + def __init__(self, identifier='', label='', enums=[]): super().__init__() layout = QHBoxLayout(self) layout.setContentsMargins(10, 0, 0, 0) layout.setSpacing(0) + self.identifier = identifier self.label = QLabel(label) self.label.setFixedWidth(140) self.label.setContentsMargins(0, 0, 10, 0) @@ -233,72 +176,76 @@ class ComboBox(QWidget): def activated_connect(self, func): """Connect a function to the Enter/Return key press.""" self.value.activated.connect( - partial(func, self.value, lambda: self.value.currentText()) + partial(func, identifier=self.identifier, value_obj=self.value, value=lambda: self.value.currentText()) ) def setDisabled(self, disable): self.value.setDisabled(disable) -# class LED(QWidget): -# def __init__(self, states, colors, label): -# super().__init__() -# self.states = states -# self.colors = colors -# layout = QHBoxLayout(self) -# layout.setContentsMargins(10, 0, 0, 0) -# layout.setSpacing(0) -# self.label = QLabel(label) -# self.label.setFixedWidth(150) -# layout.addWidget(self.label) -# self.led = QLabel() -# self.led.setFixedWidth(160) -# layout.addWidget(self.led) +class Mover(QWidget): + def __init__(self, dev, egu, prec): + super().__init__() + layout = QHBoxLayout(self) + layout.setContentsMargins(10, 0, 0, 0) + layout.setSpacing(0) + self.position = QLabel('-') + self.position.setFixedWidth(150) + layout.addWidget(self.position) + self.led = QLabel() + self.led.setFixedWidth(30) + self.led.setStyleSheet("background-color: 0, 0, 0; border: 1px solid black;") + layout.addWidget(self.led) + self.start = QPushButton('Move') + self.start.setStyleSheet("color: black; background-color: green;") + self.start.setFixedWidth(80) + self.stop = QPushButton('Stop') + self.stop.setStyleSheet("color: black; background-color: firebrick;") + self.stop.setFixedWidth(80) + layout.addWidget(self.start) + layout.addWidget(self.stop) + self.dev = dev + self.unit = egu + self.decimals = prec -# def apply_color(self, val): -# color = self.colors[self.states.index(val)] -# self.led.setStyleSheet(f"background-color: {color}; border: 1px solid black;") + def led_set_status(self, status): + if status in 'out': + self.led.setStyleSheet("background-color: 255, 0, 0; border: 1px solid black;") + elif status in 'moving': + self.led.setStyleSheet("background-color: 255, 255, 0; border: 1px solid black;") + elif status in 'in': + self.led.setStyleSheet("background-color: 0, 255, 0; border: 1px solid black;") -# class StartStop(QWidget): -# def __init__(self, label, label_buttons=['Start', 'Stop']): -# super().__init__() -# layout = QHBoxLayout(self) -# layout.setContentsMargins(10, 0, 0, 0) -# layout.setSpacing(0) -# self.label = QLabel(label) -# self.label.setFixedWidth(150) -# layout.addWidget(self.label) -# self.start = QPushButton(label_buttons[0]) -# self.start.setStyleSheet("color: black; background-color: green;") -# self.start.setFixedWidth(80) -# self.stop = QPushButton(label_buttons[1]) -# self.stop.setStyleSheet("color: black; background-color: firebrick;") -# self.stop.setFixedWidth(80) -# layout.addWidget(self.start) -# layout.addWidget(self.stop) + def position_setValue(self, number): + text = f'{number:.{int(self.decimals)}f}' + if self.unit is not None: + text = text + ' ' + self.unit + self.position.setText(text) -# def set_on_start(self, func): -# """Connect a function to the start button press.""" -# self.start.clicked.connect(func) + def start_clicked_connect(self, func): + """Connect a function to the start button press.""" + self.start.clicked.connect( + partial(func, dev=self.dev) + ) -# def set_on_stop(self, func): -# """Connect a function to the stop button press.""" -# self.stop.clicked.connect(func) + def stop_clicked_connect(self, func): + """Connect a function to the stop button press.""" + self.stop.clicked.connect( + partial(func, dev=self.dev) + ) -# def enable_start(self): -# self.start.setEnabled(True) -# self.start.setStyleSheet("color: black; background-color: green;") + def start_setEnabled(self, enable): + self.start.setEnabled(enable) + if enable: + self.start.setStyleSheet("color: black; background-color: green;") + else: + self.start.setStyleSheet("color: black; background-color: grey;") -# def enable_stop(self): -# self.stop.setEnabled(True) -# self.stop.setStyleSheet("color: black; background-color: firebrick;") - -# def disable_start(self): -# self.start.setEnabled(False) -# self.start.setStyleSheet("color: black; background-color: grey;") - -# def disable_stop(self): -# self.stop.setEnabled(False) -# self.stop.setStyleSheet("color: black; background-color: grey;") + def stop_setEnabled(self, enable): + self.stop.setEnabled(enable) + if enable: + self.stop.setStyleSheet("color: black; background-color: firebrick;") + else: + self.stop.setStyleSheet("color: black; background-color: grey;") # class Button(QWidget): # def __init__(self, label, label_button): @@ -328,3 +275,22 @@ class ComboBox(QWidget): # def set_button_text(self, text): # self.button.setText(text) + +# class LED(QWidget): +# def __init__(self, states, colors, label): +# super().__init__() +# self.states = states +# self.colors = colors +# layout = QHBoxLayout(self) +# layout.setContentsMargins(10, 0, 0, 0) +# layout.setSpacing(0) +# self.label = QLabel(label) +# self.label.setFixedWidth(150) +# layout.addWidget(self.label) +# self.led = QLabel() +# self.led.setFixedWidth(160) +# layout.addWidget(self.led) + +# def apply_color(self, val): +# color = self.colors[self.states.index(val)] +# self.led.setStyleSheet(f"background-color: {color}; border: 1px solid black;") \ No newline at end of file diff --git a/debye_bec/bec_widgets/widgets/digital_twin/x01da_parameters.py b/debye_bec/bec_widgets/widgets/x01da_parameters.py similarity index 100% rename from debye_bec/bec_widgets/widgets/digital_twin/x01da_parameters.py rename to debye_bec/bec_widgets/widgets/x01da_parameters.py