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

This commit is contained in:
x01da
2026-04-30 08:11:33 +02:00
parent 282756288f
commit 274bb9154c
4 changed files with 170 additions and 211 deletions
@@ -7,7 +7,7 @@ import debye_bec.bec_widgets.widgets.digital_twin.x01da_parameters as bl
logger = bec_logger.logger
def calculate_positions(cfg):
def calc_positions(cfg):
pos = {}
@@ -56,7 +56,7 @@ def calculate_positions(cfg):
# See raytracing script or here: bragg = np.asin(rm.ch / (2.*cfg['dSpacing']*cfg['energyCCM'])) - aCrystal.get_dtheta_symmetric_Bragg(cfg['energyCCM'])
if cfg['mo_mode'] == 'Monochromatic':
# Add 2x CM pitch to the bragg angle
bragg = ((2 * cfg['cm_pitch']) + cfg['mo_bragg'][1]) / np.pi * 180
bragg = ((2 * cfg['cm_pitch']) + cfg['mo_bragg']) / np.pi * 180
elif cfg['mo_mode'] == 'Pinkbeam':
# Align xtal surfaces parallel to beam
bragg = (2 * cfg['cm_pitch']) / np.pi * 180
@@ -65,12 +65,12 @@ def calculate_positions(cfg):
pos['mo1_bragg_angle'] = {'value': bragg} # Bragg angle in deg
# TRY, Height
l = bl.mo1.xtalGap[0]/np.sin(cfg['mo_bragg'][1])
yhor = l*np.cos(2.*(cfg['mo_bragg'][1]+cfg['cm_pitch']))
l = bl.mo1.xtalGap[0]/np.sin(cfg['mo_bragg'])
yhor = l*np.cos(2.*(cfg['mo_bragg']+cfg['cm_pitch']))
yver = yhor*np.tan(2.*cfg['cm_pitch'])
if cfg['mo_mode'] == 'Monochromatic':
beamOffsetCCM = l*np.sin(2.*(cfg['mo_bragg'][1]+cfg['cm_pitch']))-yver # Resultat ist korrekt!
beamOffsetCCM = l*np.sin(2.*(cfg['mo_bragg']+cfg['cm_pitch']))-yver # Resultat ist korrekt!
elif cfg['mo_mode'] == 'Pinkbeam':
beamOffsetCCM = 0
else:
@@ -84,22 +84,22 @@ def calculate_positions(cfg):
# calculate height of center of first crystal surface
f = bl.mo1.rotOffset # rotation offset, mm
logger.info(f'f = {f}')
# logger.info(f'f = {f}')
d = bl.mo1.heightOffset # xtal height offset, mm
logger.info(f'd = {d}')
c = d*csc(cfg['mo_bragg'][1])-f*cot(cfg['mo_bragg'][1])
logger.info(f'c = {c}')
# logger.info(f'd = {d}')
c = d*csc(cfg['mo_bragg'])-f*cot(cfg['mo_bragg'])
# logger.info(f'c = {c}')
# Calculate height of center of rotation
b = np.sqrt(d**2*csc(cfg['mo_bragg'][1])**2-2*d*f*cot(cfg['mo_bragg'][1])*csc(cfg['mo_bragg'][1])+f**2*cot(cfg['mo_bragg'][1])**2+f**2)
logger.info(f'b = {b}')
h = np.cos(np.pi/2-np.arctan(f/c)-cfg['mo_bragg'][1]-2*cfg['cm_pitch'])*b
logger.info(f'h = {h}')
b = np.sqrt(d**2*csc(cfg['mo_bragg'])**2-2*d*f*cot(cfg['mo_bragg'])*csc(cfg['mo_bragg'])+f**2*cot(cfg['mo_bragg'])**2+f**2)
# logger.info(f'b = {b}')
h = np.cos(np.pi/2-np.arctan(f/c)-cfg['mo_bragg']-2*cfg['cm_pitch'])*b
# logger.info(f'h = {h}')
h2 = ((bl.mo1.center[1] - bl.cm.center[1])-np.sqrt(b**2-h**2))*np.tan(2*cfg['cm_pitch'])
logger.info(f'mo1 = {bl.mo1.center[1]}')
logger.info(f'cm = {bl.cm.center[1]}')
logger.info(f'pitch = {cfg["cm_pitch"]}')
logger.info(f'h2 = {h2}')
# logger.info(f'mo1 = {bl.mo1.center[1]}')
# logger.info(f'cm = {bl.cm.center[1]}')
# logger.info(f'pitch = {cfg["cm_pitch"]}')
# logger.info(f'h2 = {h2}')
#TODO Mono height not exactly the same as in raytracing
heightCCM1real = h + h2 # per design, the height should not change if the pitch of the CM is not changed!
# heightCCM1real = heightCCM1real - 30 # Zero position of stage is at 1430 mm from ground.
@@ -112,12 +112,15 @@ def calculate_positions(cfg):
pos['mo1_try'] = {'value': heightCCM1real}
# TRX, Crystal selection
try:
xtal = cfg['mo_xtal'].translate(str.maketrans('', '', '()')) # Remove brackets from xtal name to conform with parameters
index = bl.mo1.xtal.index(xtal)
except:
raise ValueError(f"Requested xtal {xtal} not found in parameters!")
pos['mo1_trx'] = {'value': bl.mo1.xtalOffsetX[index]}
if cfg['mo_mode'] == 'Monochromatic':
try:
xtal = cfg['mo_xtal'].translate(str.maketrans('', '', '()')) # Remove brackets from xtal name to conform with parameters
index = bl.mo1.xtal.index(xtal)
except:
raise ValueError(f"Requested xtal {xtal} not found in parameters!")
pos['mo1_trx'] = {'value': bl.mo1.xtalOffsetX[index]}
else:
pos['mo1_trx'] = {'value': 0}
#TODO move to mono, calc for beam Z-movement between crystal surfaces
@@ -131,9 +134,9 @@ def calculate_positions(cfg):
## Beam Monitor 1
d = bl.opBM1.center[1] - bl.cm.center[1] - dz
logger.info(f'distance: {d}')
logger.info(f'cm pitch: {cfg["cm_pitch"]}')
logger.info(f'mono offset: {beamOffsetCCM}')
# logger.info(f'distance: {d}')
# logger.info(f'cm pitch: {cfg["cm_pitch"]}')
# logger.info(f'mono offset: {beamOffsetCCM}')
bm1_beam_height = d * np.tan(2 * cfg['cm_pitch']) + beamOffsetCCM
pos['bm1_try'] = {'value': bm1_beam_height}
@@ -1,4 +1,5 @@
import sys
import os
import datetime
import numpy as np
from bec_lib import bec_logger
@@ -17,7 +18,10 @@ 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.digital_twin.calculate_positions import calculate_positions
from debye_bec.bec_widgets.widgets.digital_twin.calculate_positions import calc_positions
from xrt.backends.raycing.physconsts import CHeVcm, AVOGADRO
import debye_bec.bec_widgets.widgets.digital_twin.x01da_parameters as bl
logger = bec_logger.logger
@@ -45,32 +49,44 @@ class DigitalTwin(BECWidget, QWidget):
self.plot_widget = PlotWidget(title='Plot title', chart_data = [])
self.input = InputPanel()
self.positions_panel = PositionsPanel()
self.positions = PositionsPanel()
self.root_layout.addWidget(self.plot_widget, stretch=3)
self.root_layout.addWidget(self.input, stretch=1, alignment=Qt.AlignTop)
self.root_layout.addWidget(self.positions_panel, stretch=1, alignment=Qt.AlignTop)
self.root_layout.addWidget(self.positions, stretch=1, alignment=Qt.AlignTop)
self.setLayout(self.root_layout)
self.setWindowTitle("Digital Twin")
self.resize(600, 500)
self.input.energy.value_changed_connect(self.calculate_positions)
self.input.sldi_hacc.value_changed_connect(self.calculate_positions)
self.input.sldi_vacc.value_changed_connect(self.calculate_positions)
self.input.cm_stripe.activated_connect(self.calculate_positions)
self.input.cm_pitch.value_changed_connect(self.calculate_positions)
self.input.mo1_mode.activated_connect(self.calculate_positions)
self.input.mo1_xtal.activated_connect(self.calculate_positions)
self.input.fm_stripe.activated_connect(self.calculate_positions)
self.input.fm_pitch.value_changed_connect(self.calculate_positions)
self.input.smpl.value_changed_connect(self.calculate_positions)
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.bragg_angle = 0
self.calc_bragg_angle()
self.calc_ideal_fm_pitch()
self.calc_crit_angle()
@SafeSlot()
def calculate_positions(self, field, qt_obj, number, *args):
logger.info(f'Got field {field} and number {qt_obj} and number {number} and args {args}')
def calc_positions(self, *args):
# logger.info(f'Got field {field} and number {qt_obj} and number {number} and args {args}')
config = { # Config in SI units!
'energy' : self.input.energy.value(),
@@ -80,7 +96,7 @@ class DigitalTwin(BECWidget, QWidget):
'cm_stripe' : self.input.cm_stripe.currentText(),
'mo_mode' : self.input.mo1_mode.currentText(),
'mo_xtal' : self.input.mo1_xtal.currentText(),
'mo_bragg' : [12.725, 12.725],
'mo_bragg' : self.bragg_angle,
'fm_pitch' : -self.input.fm_pitch.value() * 1e-3,
'fm_stripe' : self.input.fm_stripe.currentText(),
'fm_gain_height' : 1,
@@ -88,10 +104,53 @@ class DigitalTwin(BECWidget, QWidget):
}
logger.info(f'Config created: {config}')
out = calc_positions(config)
logger.info(f'Got positions: {out}')
positions = calculate_positions(config)
self.positions.sldi_gapx.setValue(out['sldi_gapx']['value'])
self.positions.sldi_gapy.setValue(out['sldi_gapy']['value'])
self.positions.cm_trx.setValue(out['cm_trx']['value'])
self.positions.cm_try.setValue(out['cm_try']['value'])
self.positions.cm_bnd.setValue(out['cm_bnd_radius']['value'])
self.positions.cm_rotx.setValue(out['cm_rotx']['value'])
self.positions.mo1_bragg_angle.setValue(out['mo1_bragg_angle']['value'])
self.positions.mo1_trx.setValue(out['mo1_trx']['value'])
self.positions.mo1_try.setValue(out['mo1_try']['value'])
self.positions.sl1_centery.setValue(out['sl1_centery']['value'])
self.positions.bm1_try.setValue(out['bm1_try']['value'])
self.positions.fm_trx.setValue(out['fm_trx']['value'])
self.positions.fm_try.setValue(out['fm_try']['value'])
self.positions.fm_bnd.setValue(out['fm_bnd_radius']['value'])
self.positions.fm_rotx.setValue(out['fm_rotx']['value'])
self.positions.sl2_centery.setValue(out['sl2_centery']['value'])
self.positions.bm2_try.setValue(out['bm2_try']['value'])
self.positions.ot_try.setValue(out['ot_try']['value'])
self.positions.ot_rotx.setValue(out['ot_rotx']['value'])
self.positions.ot_es1_trz.setValue(out['ot_es1_trz']['value'])
logger.info(f'Got positions: {positions}')
@SafeSlot()
def calc_bragg_angle(self, *args):
"""
Calculates bragg angle in rad
"""
xtal = self.input.mo1_xtal.currentText()
if xtal in 'Si(111)':
d_spacing = self.dev.mo1_bragg.crystal.d_spacing_si111.get()
elif xtal in 'Si(311)':
d_spacing = self.dev.mo1_bragg.crystal.d_spacing_si311.get()
else:
raise Exception(f'Invalid xtal selection: {xtal}')
H = 6.62606957E-34
E = 1.602176634E-19
C = 299792458
wl = C * H / (E * self.input.energy.value())
val = wl / (2 * d_spacing * 1e-10)
self.bragg_angle = 0
if val > -1 and val < 1:
self.bragg_angle = np.asin(val)
self.calc_positions()
@SafeSlot()
def update_mono_mode(self, *args):
@@ -101,141 +160,36 @@ class DigitalTwin(BECWidget, QWidget):
else:
self.input.mo1_xtal.setDisabled(True)
# self.init_ui()
# self._recalculate() # populate outputs on startup
# Timer: update plot every 1 second
# self._timer = QTimer(self)
# self._timer.setInterval(1000)
# self._timer.timeout.connect(self._tick)
# self._timer.start()
# ------------------------------------------------------------------ UI ---
# def init_ui(self):
# self.spin_a = InputNumberField('Acceptance 1')
# self.spin_b = InputNumberField('Acceptance 2')
# self.input_group = Group(
# 'Inputs',
# [
# self.spin_a,
# self.spin_b,
# ]
# )
# self.root_layout.addWidget(self.input_group)
# self.root_layout.addStretch()
# root = QVBoxLayout(self)
# # --- Inputs ---
# input_group = QGroupBox("Inputs")
# input_layout = QHBoxLayout(input_group)
# self._spin_a = QLineEdit()
# self._spin_a.setPlaceholderText('0')
# self._spin_a.setText('0')
# # self._spin_a.setRange(-1e6, 1e6)
# # self._spin_a.setDecimals(3)
# # self._spin_a.setValue(1.0)
# # self._spin_a.setSingleStep(0.1)
# self._spin_b = QLineEdit()
# self._spin_b.setPlaceholderText('0')
# self._spin_b.setText('0')
# # self._spin_b.setRange(-1e6, 1e6)
# # self._spin_b.setDecimals(3)
# # self._spin_b.setValue(2.0)
# # self._spin_b.setSingleStep(0.1)
# self._spin_c = QLineEdit()
# self._spin_c.setPlaceholderText('0')
# self._spin_c.setText('10')
# input_layout.addWidget(QLabel("A:"))
# input_layout.addWidget(self._spin_a)
# input_layout.addWidget(QLabel("B:"))
# input_layout.addWidget(self._spin_b)
# input_layout.addWidget(QLabel("C:"))
# input_layout.addWidget(self._spin_c)
# root.addWidget(input_group)
# # --- Outputs ---
# output_group = QGroupBox("Outputs")
# output_layout = QHBoxLayout(output_group)
# self._label_sum = QLabel("Sum: —")
# self._label_product = QLabel("Product: —")
# output_layout.addWidget(self._label_sum)
# output_layout.addWidget(self._label_product)
# root.addWidget(output_group)
# # --- Plot ---
# plot_group = QGroupBox("Live History (updates every 1 s)")
# plot_layout = QVBoxLayout(plot_group)
# self._plot_widget = pg.PlotWidget()
# self._plot_widget.setBackground("w")
# self._plot_widget.addLegend()
# self._plot_widget.setLabel("left", "Value")
# self._plot_widget.setLabel("bottom", "Tick")
# self._curve_sum = self._plot_widget.plot(
# pen=pg.mkPen("b", width=2), name="Sum"
# )
# self._curve_product = self._plot_widget.plot(
# pen=pg.mkPen("r", width=2), name="Product"
# )
# plot_layout.addWidget(self._plot_widget)
# plot_group.setLayout(plot_layout)
# root.addWidget(plot_group)
# self.setLayout(root)
# self.setWindowTitle("BEC Calculator Widget")
# self.resize(600, 500)
# # Connect inputs → recalculate
# self._spin_a.editingFinished .connect(self._recalculate)
# self._spin_b.editingFinished .connect(self._recalculate)
# ---------------------------------------------------------- Logic ---
# @SafeSlot()
# def _recalculate(self):
# # logger.info(var)
# a = float(self._spin_a.text())
# b = float(self._spin_b.text())
# s = a + b
# p = a * b
# self._label_sum.setText(f"Sum: {s:.4f}")
# self._label_product.setText(f"Product: {p:.4f}")
# self._current_sum = s
# self._current_product = p
# @SafeSlot()
# def _tick(self):
# """Called every second: record current outputs and refresh plot."""
# self._history.append((self._t, self._current_sum, self._current_product))
# self._t += 1
# ticks = [h[0] for h in self._history]
# sums = [h[1] for h in self._history]
# products = [h[2] for h in self._history]
# self._curve_sum.setData(ticks, sums)
# self._curve_product.setData(ticks, products)
# # --------------------------------------------------- RPC interface ---
# def set_a(self, value: float):
# """Set input A remotely from the BEC CLI."""
# self._spin_a.setValue(value)
# def set_b(self, value: float):
# """Set input B remotely from the BEC CLI."""
# self._spin_b.setValue(value)
@SafeSlot()
def calc_ideal_fm_pitch(self, *args):
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
pitch = 0
if 'Rh' in self.input.fm_stripe.currentText():
pitch = np.arcsin(bl.fm.r[0]/(2*f))# ideal pitch for FM
if 'Pt' in self.input.fm_stripe.currentText():
pitch = np.arcsin(bl.fm.r[1]/(2*f)) # ideal pitch for FM
self.input.fm_pitch_ideal.setValue(-pitch * 1e3)
@SafeSlot()
def calc_crit_angle(self, *args):
stripe = self.input.cm_stripe.currentText()
# Config Mirror
if stripe in 'Si':
stripe = bl.stripeSi
elif stripe in 'Pt':
stripe = bl.stripePt
elif stripe in 'Rh':
stripe = bl.stripeRh
else:
raise Exception(f'Stripe {stripe} not found in beamline parameters!')
w = CHeVcm/100/self.input.energy.value() # convert energy [eV] to wavelength [m]
# Calculate critical angle for mirror
f1 = stripe.elements[0].Z + np.real(stripe.elements[0].get_f1f2(self.input.energy.value()))
numberDensity = stripe.rho*1e3*AVOGADRO/(stripe.elements[0].mass/1e3)
criticalAngle = np.sqrt(numberDensity*2.8179e-15*w**2*f1/np.pi)
self.input.cm_pitch_critical.setValue(-criticalAngle * 1e3)
class InputPanel(QWidget):
"""Right-side control panel: input field, indicator, send, recording."""
@@ -262,7 +216,7 @@ class InputPanel(QWidget):
# Collimating mirror
self.cm_stripe = ComboBox('Stripe', ['Si', 'Rh', 'Pt'])
self.cm_pitch_critical = NumberIndicator('Critical Pitch', 'mrad', decimals=3)
self.cm_pitch = InputNumberField('Pitch [mrad]', init=-3, decimals=3, single_step=0.01, ll=-4.6, hl=-1.2)
self.cm_pitch = InputNumberField('Pitch [mrad]', init=-2.391, decimals=3, single_step=0.01, ll=-4.6, hl=-1.2)
self.cm_ass_group = Group(
'Collimating Mirror',
[
@@ -286,7 +240,7 @@ class InputPanel(QWidget):
# Focusing Mirror
self.fm_stripe = ComboBox('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=-3, decimals=3, single_step=0.01, ll=-10, hl=2)
self.fm_pitch = InputNumberField('Pitch [mrad]', init=-2.391, decimals=3, single_step=0.01, ll=-10, hl=2)
self.fm_ass_group = Group(
'Focusing Mirror',
[
@@ -297,7 +251,7 @@ class InputPanel(QWidget):
)
# Sample
self.smpl = InputNumberField('Sample Position [mm]')
self.smpl = InputNumberField('Sample Position [mm]', init=23511, decimals=0, single_step=100, ll=23000, hl=30000)
# Assemble complete assitant group
self.input_group = Group(
@@ -337,8 +291,8 @@ class PositionsPanel(QWidget):
# Collimating mirror
self.cm_trx = NumberIndicator('TRX', 'mm', decimals=1)
self.cm_try = NumberIndicator('TRY', 'mm', decimals=3)
self.cm_bnd = NumberIndicator('BENDER', 'mm', decimals=2)
self.cm_rotx = NumberIndicator('PITCH', 'mm', decimals=3)
self.cm_bnd = NumberIndicator('BENDER', 'km', decimals=2)
self.cm_rotx = NumberIndicator('PITCH', 'mrad', decimals=3)
self.cm_pos_group = Group(
'Collimating Mirror',
[
@@ -350,7 +304,7 @@ class PositionsPanel(QWidget):
)
# Monochromator
self.mo1_bragg_angle = NumberIndicator('Bragg Angle', 'mm', decimals=3)
self.mo1_bragg_angle = NumberIndicator('Bragg Angle', 'deg', decimals=3)
self.mo1_trx = NumberIndicator('TRX', 'mm', decimals=1)
self.mo1_try = NumberIndicator('TRY', 'mm', decimals=3)
self.mo1_pos_group = Group(
@@ -383,8 +337,8 @@ class PositionsPanel(QWidget):
# Focusing Mirror
self.fm_trx = NumberIndicator('TRX', 'mm', decimals=1)
self.fm_try = NumberIndicator('TRY', 'mm', decimals=3)
self.fm_bnd = NumberIndicator('BENDER', 'mm', decimals=2)
self.fm_rotx = NumberIndicator('PITCH', 'mm', decimals=3)
self.fm_bnd = NumberIndicator('BENDER', 'km', decimals=2)
self.fm_rotx = NumberIndicator('PITCH', 'mrad', decimals=3)
self.fm_pos_group = Group(
'Focusing Mirror',
[
@@ -414,15 +368,15 @@ class PositionsPanel(QWidget):
)
# Optical Table
self.es_try = NumberIndicator('TRY', 'mm', decimals=3)
self.es_rotx = NumberIndicator('ROTX', 'mm', decimals=3)
self.es1_trz = NumberIndicator('ES1 TRZ', 'mm', decimals=0)
self.es_pos_group = Group(
self.ot_try = NumberIndicator('TRY', 'mm', decimals=0)
self.ot_rotx = NumberIndicator('ROTX', 'mrad', decimals=3)
self.ot_es1_trz = NumberIndicator('ES1 TRZ', 'mm', decimals=0)
self.ot_pos_group = Group(
'Optical Table',
[
self.es_try,
self.es_rotx,
self.es1_trz,
self.ot_try,
self.ot_rotx,
self.ot_es1_trz,
]
)
@@ -438,7 +392,7 @@ class PositionsPanel(QWidget):
self.fm_pos_group,
self.sl2_pos_group,
self.bm2_pos_group,
self.es_pos_group,
self.ot_pos_group,
]
)
@@ -8,16 +8,18 @@ import os
import numpy as np
from collections import namedtuple
if os.environ.get("USE_XRT", "True").lower() in ("1", "true", "yes"):
import xrt.backends.raycing.materials as rm # type: ignore
else:
class _DummyClass:
def __init__(self, *args, **kwargs):
pass
class _DummyMaterials:
Material = _DummyClass
CrystalSi = _DummyClass
rm = _DummyMaterials()
import xrt.backends.raycing.materials as rm
# if os.environ.get("USE_XRT", "True").lower() in ("1", "true", "yes"):
# import xrt.backends.raycing.materials as rm # type: ignore
# else:
# class _DummyClass:
# def __init__(self, *args, **kwargs):
# pass
# class _DummyMaterials:
# Material = _DummyClass
# CrystalSi = _DummyClass
# rm = _DummyMaterials()
# XRT definitions
filterBeryl = rm.Material('Be', rho=1.85, kind='plate') # pyright: ignore[reportArgumentType]
+7 -7
View File
@@ -50,7 +50,7 @@ class NumberIndicator(QWidget):
self.label.setFixedWidth(150)
layout.addWidget(self.label)
self.val = QLabel('-')
self.val.setFixedWidth(160)
# self.val.setFixedWidth(140)
layout.addWidget(self.val)
self.unit = unit
self.highlight = highlight
@@ -68,7 +68,7 @@ class NumberIndicator(QWidget):
def setValue(self, number):
self.number = number
text = f'{number:.{self.decimals}f}'
text = f'{number:.{int(self.decimals)}f}'
if self.unit is not None:
text = text + ' ' + self.unit
self.val.setText(text)
@@ -85,7 +85,7 @@ class InputTextField(QWidget):
layout.addWidget(self.label)
self.val = QLineEdit()
self.val.setPlaceholderText('0')
self.val.setFixedWidth(160)
# self.val.setFixedWidth(140)
layout.addWidget(self.val)
def set_text(self, text):
@@ -104,7 +104,7 @@ class InputTextField(QWidget):
)
class InputNumberField(QWidget):
def __init__(self, label, init=0, decimals=1, single_step=0.1, ll=-1e6, hl=1e6):
def __init__(self, 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)
@@ -113,11 +113,11 @@ class InputNumberField(QWidget):
self.label.setFixedWidth(150)
layout.addWidget(self.label)
self.val = QDoubleSpinBox()
self.val.setValue(init)
self.val.setRange(ll, hl)
self.val.setDecimals(decimals)
self.val.setSingleStep(single_step)
self.val.setFixedWidth(160)
self.val.setValue(init)
# self.val.setFixedWidth(140)
layout.addWidget(self.val)
def set_number(self, number):
@@ -204,7 +204,7 @@ class ComboBox(QWidget):
self.label.setFixedWidth(150)
layout.addWidget(self.label)
self.value = QComboBox()
self.value.setFixedWidth(160)
# self.value.setFixedWidth(140)
for entry in enums:
self.value.addItem(entry)
layout.addWidget(self.value)