wip: digital twin
CI for debye_bec / test (pull_request) Failing after 1m3s
CI for debye_bec / test (push) Failing after 1m35s

This commit is contained in:
x01da
2026-05-05 15:32:01 +02:00
parent 131d7f7f3e
commit 3d2485aea7
6 changed files with 274 additions and 249 deletions
@@ -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):
@@ -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
@@ -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):
@@ -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."""
+111 -145
View File
@@ -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;")