Source code for siriushla.as_ap_sofb.ioc_control.main
"""Define Controllers for the orbits displayed in the graphic."""
import numpy as _np
from qtpy.QtWidgets import QLabel, QGroupBox, QPushButton, QFormLayout, \
QVBoxLayout, QHBoxLayout, QGridLayout, QWidget, QTabWidget, QSpacerItem
from qtpy.QtCore import Qt
import qtawesome as qta
from pydm.widgets import PyDMPushButton
from siriuspy.namesys import SiriusPVName as _PVName
from ...widgets import SiriusConnectionSignal as _ConnSig, SiriusLabel, \
SiriusLedAlert, SiriusSpinbox, PyDMStateButton, SiriusLedState
from ...widgets.windows import create_window_from_widget
from ... import util as _util
from .status import StatusWidget
from .kicks_config import KicksConfigWidget
from .orbit_acquisition import AcqControlWidget
from .respmat import RespMatWidget
from .base import BaseWidget, BaseCombo
[docs]
class SOFBControl(BaseWidget):
"""."""
def __init__(self, parent, device, ctrls, prefix='', acc='SI'):
"""."""
super().__init__(parent, device, prefix=prefix, acc=acc)
self.ctrls = ctrls
self.setupui()
[docs]
def setupui(self):
"""."""
vbl = QVBoxLayout(self)
tabw = QTabWidget(self)
tabw.setObjectName(self.acc+'Tab')
vbl.addWidget(tabw)
main_wid = self.get_main_widget(tabw)
tabw.addTab(main_wid, 'Main')
wid = AcqControlWidget(
tabw, self.device, prefix=self.prefix, acc=self.acc)
tabw.addTab(wid, 'Trig. Acq. Control')
[docs]
def get_main_widget(self, parent):
"""."""
main_wid = QWidget(parent)
vbl = QVBoxLayout()
main_wid.setLayout(vbl)
tabw = QGroupBox('Orbit', main_wid)
tabw.setLayout(QHBoxLayout())
orb_wid = self.get_orbit_widget(tabw)
tabw.layout().addWidget(orb_wid)
corr_wid = self.get_correction_widget(main_wid)
mat_wid = RespMatWidget(main_wid, self.device, self.prefix, self.acc)
vbl.addWidget(tabw)
vbl.addStretch()
vbl.addWidget(corr_wid)
vbl.addStretch()
vbl.addWidget(mat_wid)
return main_wid
[docs]
def get_orbit_widget(self, parent):
"""."""
rules = (
'[{"name": "EnblRule", "property": "Enable", ' +
'"expression": "not ch[0]", "channels": [{"channel": "' +
self.devpref.substitute(propty='LoopState-Sts') +
'", "trigger": true}]}]')
orb_wid = QWidget(parent)
orb_wid.setObjectName('grp')
orb_wid.setStyleSheet('#grp{min-height: 11em; max-height: 15em;}')
orb_wid.setLayout(QGridLayout())
conf = PyDMPushButton(
orb_wid, pressValue=1,
init_channel=self.devpref.substitute(propty='TrigAcqConfig-Cmd'))
conf.rules = rules
conf.setToolTip('Refresh Configurations')
conf.setIcon(qta.icon('fa5s.sync'))
conf.setObjectName('conf')
conf.setStyleSheet(
'#conf{min-width:25px; max-width:25px; icon-size:20px;}')
sts = QPushButton('', orb_wid)
sts.setIcon(qta.icon('fa5s.list-ul'))
sts.setToolTip('Open Detailed Status View')
sts.setObjectName('sts')
sts.setStyleSheet(
'#sts{min-width:25px; max-width:25px; icon-size:20px;}')
icon = qta.icon(
'fa5s.hammer', color=_util.get_appropriate_color(self.acc))
window = create_window_from_widget(
StatusWidget, title='Orbit Status', icon=icon)
_util.connect_window(
sts, window, orb_wid, device=self.device,
prefix=self.prefix, acc=self.acc, is_orb=True)
pdm_led = SiriusLedAlert(
orb_wid, self.devpref.substitute(propty='OrbStatus-Mon'))
lbl = QLabel('Status:', orb_wid)
hbl = QHBoxLayout()
hbl.setSpacing(9)
hbl.addStretch()
hbl.addWidget(lbl)
hbl.addWidget(pdm_led)
hbl.addWidget(sts)
hbl.addWidget(conf)
orb_wid.layout().addLayout(hbl, 0, 0, 1, 2)
if self.acc == 'SI':
lbl = QLabel('SOFB Mode', orb_wid)
wid = self.create_pair_sel(orb_wid, 'SOFBMode', rules=rules)
orb_wid.layout().addWidget(lbl, 1, 0, alignment=Qt.AlignVCenter)
orb_wid.layout().addWidget(wid, 1, 1)
orb_wid.layout().setRowStretch(2, 3)
lbl = QLabel('RefOrb:', orb_wid)
combo = RefControl(
self, self.device, self.ctrls, prefix=self.prefix, acc=self.acc)
combo.rules = rules
lbl2 = QLabel('', orb_wid)
combo.configname.connect(lbl2.setText)
vbl_ref = QVBoxLayout()
vbl_ref.addWidget(combo)
vbl_ref.addWidget(lbl2)
orb_wid.layout().addWidget(lbl, 3, 0, alignment=Qt.AlignVCenter)
orb_wid.layout().addLayout(vbl_ref, 3, 1)
lbl = QLabel('Num. Pts.', orb_wid)
stp = SiriusSpinbox(
orb_wid, self.devpref.substitute(propty='SmoothNrPts-SP'))
rdb = SiriusLabel(
orb_wid, self.devpref.substitute(propty='SmoothNrPts-RB'))
rdb.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
slsh = QLabel('/', orb_wid, alignment=Qt.AlignCenter)
slsh.setStyleSheet('min-width:0.7em; max-width:0.7em;')
cnt = SiriusLabel(
orb_wid, self.devpref.substitute(propty='BufferCount-Mon'))
cnt.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
cnt.setToolTip('Current Buffer Size')
rst = PyDMPushButton(
orb_wid, pressValue=1,
init_channel=self.devpref.substitute(propty='SmoothReset-Cmd'))
rst.setToolTip('Reset Buffer')
rst.setIcon(qta.icon('mdi.delete-empty'))
rst.setObjectName('rst')
rst.setStyleSheet(
'#rst{min-width:25px; max-width:25px; icon-size:20px;}')
hbl = QHBoxLayout()
hbl.addWidget(stp)
hbl.addWidget(cnt)
hbl.addWidget(slsh)
hbl.addWidget(rdb)
hbl.addWidget(rst)
orb_wid.layout().addWidget(lbl, 4, 0, alignment=Qt.AlignVCenter)
orb_wid.layout().addLayout(hbl, 4, 1)
lbl = QLabel('Smooth Method', orb_wid, alignment=Qt.AlignCenter)
wid = self.create_pair_sel(orb_wid, 'SmoothMethod', rules=rules)
orb_wid.layout().addWidget(lbl, 5, 0, alignment=Qt.AlignVCenter)
orb_wid.layout().addWidget(wid, 5, 1)
orb_wid.layout().setColumnStretch(1, 2)
return orb_wid
[docs]
def get_correction_widget(self, parent):
"""."""
corr_wid = QGroupBox('Correction', parent)
lay = QVBoxLayout(corr_wid)
lay.setContentsMargins(0, 6, 0, 6)
if self.acc == 'SI':
lbl = QLabel('Auto Correction State:', corr_wid)
wid = self.create_pair_butled(corr_wid, 'LoopState')
hbl = QHBoxLayout()
hbl.setContentsMargins(6, 6, 6, 0)
hbl.addWidget(lbl)
hbl.addWidget(wid)
lay.addLayout(hbl)
corr_tab = QTabWidget(corr_wid)
corr_tab.setObjectName(self.acc+'Tab')
lay.addWidget(corr_tab)
if self.acc == 'SI':
auto_wid = self.get_auto_correction_widget(corr_tab)
corr_tab.addTab(auto_wid, 'Loop')
man_wid = self.get_manual_correction_widget(corr_tab)
corr_tab.addTab(man_wid, 'Manual')
kicks_wid = KicksConfigWidget(
parent, self.device, prefix=self.prefix, acc=self.acc)
corr_tab.addTab(kicks_wid, 'Kicks')
if self.acc == 'SI':
fofb_wid = self.get_fofb_widget(corr_tab)
corr_tab.addTab(fofb_wid, 'FOFB')
if self.acc != 'BO':
hbl = kicks_wid.get_status_widget(corr_wid)
hbl.setContentsMargins(6, 0, 6, 6)
lay.addLayout(hbl)
return corr_wid
[docs]
def get_fofb_widget(self, parent):
"""."""
wid = QWidget(parent)
wid.setObjectName('grp')
gdl = QGridLayout(wid)
gdl.setAlignment(Qt.AlignTop)
headers = [
'Description', 'Setpoint', 'Status', 'Monitor', '%']
for col, text in enumerate(headers):
lbl = QLabel(text, wid, alignment=Qt.AlignHCenter)
lbl.setStyleSheet('QLabel{max-height: 1.2em;}')
gdl.addWidget(lbl, 0, col+1)
props = [
'FOFBDownloadKicks', 'FOFBUpdateRefOrb',
'FOFBNullSpaceProj', 'FOFBZeroDistortionAtBPMs']
desc = [
'Download Kicks', 'Update RefOrb',
'Project in Kernel', 'Zero Distortion']
vislist = list()
for i, (prop, des) in enumerate(zip(props, desc)):
lbl = QLabel(des, wid)
spt = PyDMStateButton(
wid, self.devpref.substitute(propty=prop+'-Sel'))
rdb = SiriusLedState(
wid, self.devpref.substitute(propty=prop+'-Sts'))
mon = SiriusLedState(
wid, self.devpref.substitute(propty=prop+'-Mon'))
wids = [lbl, spt, rdb, mon]
gdl.addWidget(lbl, i+1, 0, 1, 2)
gdl.addWidget(spt, i+1, 2)
gdl.addWidget(rdb, i+1, 3)
gdl.addWidget(mon, i+1, 4)
if prop in ['FOFBDownloadKicks', 'FOFBUpdateRefOrb']:
sbp = SiriusSpinbox(
wid, self.devpref.substitute(propty=prop+'Perc-SP'))
rbp = SiriusLabel(
wid, self.devpref.substitute(propty=prop+'Perc-RB'))
rbp.showUnits = False
gdl2 = QGridLayout()
gdl2.setContentsMargins(0, 0, 0, 0)
gdl2.addWidget(sbp, 0, 0)
gdl2.addWidget(rbp, 1, 0)
gdl.addLayout(gdl2, i+1, 5)
wids.extend([sbp, rbp])
if prop != 'FOFBDownloadKicks':
vislist.extend(wids)
for w in wids:
w.setVisible(False)
btmore = QPushButton(qta.icon('fa5s.angle-down'), '', self)
btmore.setToolTip('Show advanced SOFB <-> FOFB interaction options')
btmore.setStyleSheet(
'QPushButton{max-height:0.8em; max-width:1.6em;}')
btmore.clicked.connect(self._handle_fofb_options_vis)
btmore.visItems = vislist
btmore.setFlat(True)
gdl.addWidget(btmore, 0, 0, alignment=Qt.AlignLeft)
return wid
[docs]
def get_manual_correction_widget(self, parent):
"""."""
man_wid = QWidget(parent)
man_wid.setObjectName('grp')
gdl = QGridLayout(man_wid)
gdl.setSpacing(9)
calc = PyDMPushButton(
man_wid, '', pressValue=1,
init_channel=self.devpref.substitute(propty='CalcDelta-Cmd'))
calc.setIcon(qta.icon('mdi.calculator-variant'))
calc.setToolTip('Calculate Kicks')
calc.setObjectName('button')
calc.setStyleSheet('#button {\
min-height: 45px; min-width: 45px;\
max-height: 45px; max-width: 45px;\
icon-size: 40px;}')
rules = (
'[{"name": "EnblRule", "property": "Enable", ' +
'"expression": "not ch[0]", "channels": [{"channel": "' +
self.devpref.substitute(propty='LoopState-Sts') +
'", "trigger": true}]}]')
calc.rules = rules
if self.acc == 'BO':
gdl.addWidget(calc, 1, 1)
gdl.setColumnStretch(0, 2)
gdl.setColumnStretch(2, 2)
gdl.setRowStretch(0, 2)
gdl.setRowStretch(2, 2)
return man_wid
rules = ''
if self.isring:
exp = 'not ch[0]'
ch = '{"channel": "' + self.devpref.substitute(
propty='LoopState-Sts') + '", "trigger": true},'
rules = (
'[{"name": "EnblRule", "property": "Enable", ' +
'"expression": "'+exp+'", "channels": ['+ch +
'", "trigger": true}]}]')
lst = [
('All', self._csorb.ApplyDelta.All),
('CH', self._csorb.ApplyDelta.CH),
('CV', self._csorb.ApplyDelta.CV)]
if self.acc in {'SI', 'BO'}:
lst.append(('RF', self._csorb.ApplyDelta.RF))
btns = dict()
for itm, val in lst:
btn = PyDMPushButton(
man_wid, ' '+itm, pressValue=val,
init_channel=self.devpref.substitute(propty='ApplyDelta-Cmd'))
btn.rules = rules
btn.setIcon(qta.icon('fa5s.hammer'))
btn.setToolTip('Apply ' + itm)
btn.setObjectName('button')
btn.setStyleSheet('#button {\
min-height: 25px; min-width: 45px;\
max-height: 25px;\
icon-size: 20px;}')
if self.acc == 'BO':
btn.setVisible(False)
btns[itm] = btn
gdl.addWidget(calc, 0, 0, 2, 1)
gdl.addWidget(btns['CH'], 0, 1)
gdl.addWidget(btns['CV'], 0, 2)
if self.acc in {'SI', 'BO'}:
gdl.addWidget(btns['RF'], 0, 3)
gdl.addWidget(btns['All'], 1, 1, 1, 3)
else:
gdl.addWidget(btns['All'], 1, 1, 1, 2)
gdl.setColumnMinimumWidth(0, 60)
grpbx = QWidget(man_wid)
grpbx.setObjectName('gbx')
if self.acc in {'SI', 'BO'}:
planes = ('CH', 'CV', 'RF')
gdl.addWidget(grpbx, 2, 0, 1, 4)
else:
planes = ('CH', 'CV')
gdl.addWidget(grpbx, 2, 0, 1, 3)
fbl = QFormLayout(grpbx)
for pln in planes:
lbl = QLabel(pln+' [%] ', grpbx)
lbl.setObjectName('lbl')
lbl.setStyleSheet('#lbl{min-height:1em;}')
wid = self.create_pair(grpbx, 'ManCorrGain'+pln)
wid.setObjectName('wid')
wid.setStyleSheet('#wid{min-height:1.2em;}')
if self.acc == 'BO':
lbl.setVisible(False)
wid.setVisible(False)
fbl.addRow(lbl, wid)
vlay = QVBoxLayout()
vlay.addStretch()
gdl.addLayout(vlay, 3, 0)
return man_wid
[docs]
def get_auto_correction_widget(self, parent):
"""."""
auto_wid = QWidget(parent)
vbl2 = QVBoxLayout(auto_wid)
tabw = QTabWidget(auto_wid)
tabw.setObjectName(self.acc+'Tab')
tabw.setStyleSheet("""
#{0}Tab::pane {{
border-left: 2px solid gray;
border-bottom: 2px solid gray;
border-right: 2px solid gray;
}}""".format(self.acc))
vbl2.addWidget(tabw)
# Add Main Tab
gpbx = QWidget(tabw)
gpbx_lay = QVBoxLayout(gpbx)
tabw.addTab(gpbx, 'Main')
fbl = QFormLayout()
fbl.setHorizontalSpacing(9)
gpbx_lay.addLayout(fbl)
lbl = QLabel('Freq. [Hz]', gpbx)
wid = self.create_pair(gpbx, 'LoopFreq')
fbl.addRow(lbl, wid)
lbl = QLabel('Max. Orb. Distortion [um]', gpbx)
wid = self.create_pair(gpbx, 'LoopMaxOrbDistortion')
fbl.addRow(lbl, wid)
wid = QWidget(gpbx)
wid.setLayout(QHBoxLayout())
pbtn = QPushButton('Loop Performance', wid)
pbtn.setIcon(qta.icon('mdi.poll'))
wid.layout().addStretch()
wid.layout().addWidget(pbtn)
icon = qta.icon(
'fa5s.hammer', color=_util.get_appropriate_color(self.acc))
wind = create_window_from_widget(
PerformanceWidget, title='Loop Performance', icon=icon)
_util.connect_window(
pbtn, wind, self, device=self.device, prefix=self.prefix)
fbl.addRow(wid)
# Add PID Tab
gpbx = QWidget(tabw)
gpbx_lay = QGridLayout(gpbx)
gpbx_lay.setSpacing(1)
tabw.addTab(gpbx, 'PID')
gpbx_lay.addWidget(QLabel('CH', gpbx), 1, 0)
gpbx_lay.addWidget(QLabel('CV', gpbx), 2, 0)
tmpl = 'LoopPID{:s}{:s}'
pairs = []
for i, k in enumerate(('Kp', 'Ki', 'Kd'), 1):
gpbx_lay.addWidget(
QLabel(k, gpbx), 0, i, alignment=Qt.AlignCenter)
pair = self.create_pair(wid, tmpl.format(k, 'CH'), is_vert=True)
pairs.append(pair)
gpbx_lay.addWidget(pair, 1, i)
pair = self.create_pair(wid, tmpl.format(k, 'CV'), is_vert=True)
pairs.append(pair)
gpbx_lay.addWidget(pair, 2, i)
if self.acc in {'SI', 'BO'}:
pair = self.create_pair(
wid, tmpl.format(k, 'RF'), is_vert=True)
pairs.append(pair)
gpbx_lay.addWidget(pair, 3, i)
if self.acc in {'SI', 'BO'}:
gpbx_lay.addWidget(QLabel('RF', gpbx), 3, 0)
pbc = QPushButton('SP')
pbc.setStyleSheet('max-width:2.2em;')
gpbx_lay.addWidget(pbc, 0, 0)
pbc.setCheckable(True)
pbc.setChecked(False)
pbc.toggled.connect(
lambda x: pbc.setText('RB' if x else 'SP'))
for pair in pairs:
pair.rb_wid.setVisible(False)
pbc.toggled.connect(pair.rb_wid.setVisible)
pbc.toggled.connect(pair.sp_wid.setHidden)
gpbx_lay.setRowStretch(4, 2)
return auto_wid
def _handle_fofb_options_vis(self):
btn = self.sender()
tooltip = btn.toolTip()
vis = 'Hide' in tooltip
if vis:
tooltip = tooltip.replace('Hide', 'Show')
else:
tooltip = tooltip.replace('Show', 'Hide')
iconname = 'fa5s.angle-down' if vis else 'fa5s.angle-up'
btn.setIcon(qta.icon(iconname))
btn.setToolTip(tooltip)
for item in btn.visItems:
item.setVisible(not vis)
self.adjustSize()
[docs]
class RefControl(BaseCombo):
"""."""
def __init__(self, parent, device, ctrls, prefix='', acc='SI'):
"""."""
setpoint = dict()
readback = dict()
basename = _PVName(device).substitute(prefix=prefix)
setpoint['x'] = _ConnSig(basename.substitute(propty='RefOrbX-SP'))
setpoint['y'] = _ConnSig(basename.substitute(propty='RefOrbY-SP'))
readback['x'] = _ConnSig(basename.substitute(propty='RefOrbX-RB'))
readback['y'] = _ConnSig(basename.substitute(propty='RefOrbY-RB'))
super().__init__(parent, ctrls, setpoint, readback, acc)
def _selection_changed(self, text):
sigs = dict()
if text.lower().startswith('bba_orb'):
data = self._client.get_config_value('bba_orb')
for pln in ('x', 'y'):
self.orbits[pln] = _np.array(data[pln])
self.setpoint[pln].send_value_signal[_np.ndarray].emit(
self.orbits[pln])
elif text.lower().startswith('ref_orb'):
data = self._client.get_config_value('ref_orb')
for pln in ('x', 'y'):
self.orbits[pln] = _np.array(data[pln])
self.setpoint[pln].send_value_signal[_np.ndarray].emit(
self.orbits[pln])
super()._selection_changed(text, sigs)
[docs]
def setup_ui(self):
"""."""
if self.acc == 'SI':
super().setup_ui(['bba_orb', 'ref_orb'])
else:
super().setup_ui()
[docs]
class PerformanceWidget(QWidget):
"""."""
def __init__(self, parent, device, prefix=''):
"""."""
super().__init__(parent)
self.prefix = prefix
self.device = _PVName(device)
self.setObjectName(self.device.sec+'App')
self._setupui()
def _setupui(self):
def lamb(x):
return self.device.substitute(prefix=self.prefix, propty=x)
lbl_main = QLabel(
'<h3>Loop Performance</h3>', self, alignment=Qt.AlignCenter)
lbl_prnt = QLabel(
'Number of Iter. Between Updates', self, alignment=Qt.AlignCenter)
spb = SiriusSpinbox(self, lamb('LoopPrintEveryNumIters-SP'))
ldrb = SiriusLabel(self, lamb('LoopPrintEveryNumIters-RB'))
slsh = QLabel('/', self, alignment=Qt.AlignCenter)
slsh.setStyleSheet('min-width:0.7em; max-width:0.7em;')
ldmon = SiriusLabel(self, lamb('LoopNumIters-Mon'))
lbl_tout = QLabel(
'SlowOrb\nTimeouts', self, alignment=Qt.AlignCenter)
ld_tout = SiriusLabel(self, lamb('SlowOrbTimeoutCnt-Mon'))
ld_tout.showUnits = True
ld_tout.setAlignment(Qt.AlignCenter)
lbl_rate = QLabel(
'Effective\nRate', self, alignment=Qt.AlignCenter)
ld_rate = SiriusLabel(self, lamb('LoopEffectiveRate-Mon'))
ld_rate.showUnits = True
lay_niter = QGridLayout()
lay_niter.addWidget(lbl_prnt, 0, 0, 1, 4, alignment=Qt.AlignCenter)
lay_niter.addWidget(spb, 1, 0)
lay_niter.addWidget(ldrb, 1, 1)
lay_niter.addWidget(slsh, 1, 2)
lay_niter.addWidget(ldmon, 1, 3)
lay_niter.addWidget(lbl_tout, 0, 4, alignment=Qt.AlignCenter)
lay_niter.addWidget(ld_tout, 1, 4)
lay_niter.addWidget(lbl_rate, 0, 5, alignment=Qt.AlignCenter)
lay_niter.addWidget(ld_rate, 1, 5)
lbl_iters = QLabel('Iterations [%]:', self)
lbl_ok = QLabel('OK', self, alignment=Qt.AlignCenter)
lbl_tout = QLabel('Timeout', self, alignment=Qt.AlignCenter)
lbl_diff = QLabel('Diff', self, alignment=Qt.AlignCenter)
ld_ok = SiriusLabel(self, lamb('LoopPerfItersOk-Mon'))
ld_tout = SiriusLabel(self, lamb('LoopPerfItersTOut-Mon'))
ld_diff = SiriusLabel(self, lamb('LoopPerfItersDiff-Mon'))
lay_iters = QGridLayout()
lay_iters.addWidget(lbl_iters, 1, 0)
lay_iters.addWidget(lbl_ok, 0, 1)
lay_iters.addWidget(lbl_tout, 0, 2)
lay_iters.addWidget(lbl_diff, 0, 3)
lay_iters.addWidget(ld_ok, 1, 1, alignment=Qt.AlignCenter)
lay_iters.addWidget(ld_tout, 1, 2, alignment=Qt.AlignCenter)
lay_iters.addWidget(ld_diff, 1, 3, alignment=Qt.AlignCenter)
lbl_psd = QLabel('# of PSs with Diffs:', self)
lbl_avg = QLabel('AVG', self, alignment=Qt.AlignCenter)
lbl_std = QLabel('STD', self, alignment=Qt.AlignCenter)
lbl_max = QLabel('MAX', self, alignment=Qt.AlignCenter)
ld_avg = SiriusLabel(self, lamb('LoopPerfDiffNrPSAvg-Mon'))
ld_std = SiriusLabel(self, lamb('LoopPerfDiffNrPSStd-Mon'))
ld_max = SiriusLabel(self, lamb('LoopPerfDiffNrPSMax-Mon'))
lay_psd = QGridLayout()
lay_psd.addWidget(lbl_psd, 1, 0)
lay_psd.addWidget(lbl_avg, 0, 1)
lay_psd.addWidget(lbl_std, 0, 2)
lay_psd.addWidget(lbl_max, 0, 3)
lay_psd.addWidget(ld_avg, 1, 1, alignment=Qt.AlignCenter)
lay_psd.addWidget(ld_std, 1, 2, alignment=Qt.AlignCenter)
lay_psd.addWidget(ld_max, 1, 3, alignment=Qt.AlignCenter)
lbl_tim = QLabel('Duration Statistics', self, alignment=Qt.AlignCenter)
lbl_unt = QLabel('[ms]', self, alignment=Qt.AlignCenter)
lbl_avg = QLabel('AVG', self, alignment=Qt.AlignCenter)
lbl_std = QLabel('STD', self, alignment=Qt.AlignCenter)
lbl_min = QLabel('MIN', self, alignment=Qt.AlignCenter)
lbl_max = QLabel('MAX', self, alignment=Qt.AlignCenter)
lbl_avg.setStyleSheet('min-width: 5em;')
lbl_std.setStyleSheet('min-width: 5em;')
lbl_min.setStyleSheet('min-width: 5em;')
lbl_max.setStyleSheet('min-width: 5em;')
lay_tim = QGridLayout()
lay_tim.addWidget(lbl_tim, 0, 0, 1, 5)
lay_tim.addWidget(lbl_unt, 1, 0)
lay_tim.addWidget(lbl_avg, 1, 1)
lay_tim.addWidget(lbl_std, 1, 2)
lay_tim.addWidget(lbl_min, 1, 3)
lay_tim.addWidget(lbl_max, 1, 4)
labs = [
'Get Orbit', 'Calculate', 'Process', 'Apply', 'Total']
nms = ['GetO', 'Calc', 'Proc', 'App', 'Tot']
for i, (nm, lab) in enumerate(zip(nms, labs)):
lbl = QLabel(lab, self)
lay_tim.addWidget(lbl, i+2, 0)
for j, ld in enumerate(['Avg', 'Std', 'Min', 'Max']):
ld_ = SiriusLabel(
self, lamb(f'LoopPerfTim{nm:s}{ld:s}-Mon'))
lay_tim.addWidget(ld_, i+2, j+1, alignment=Qt.AlignCenter)
lay = QGridLayout(self)
lay.addLayout(lay_niter, 0, 0)
lay.addItem(QSpacerItem(1, 20), 1, 0)
lay.addWidget(lbl_main, 2, 0)
lay.addItem(QSpacerItem(1, 20), 3, 0)
lay.addLayout(lay_iters, 4, 0)
lay.addItem(QSpacerItem(1, 20), 5, 0)
lay.addLayout(lay_psd, 6, 0)
lay.addItem(QSpacerItem(1, 20), 7, 0)
lay.addLayout(lay_tim, 8, 0)
lay.setRowStretch(1, 2)
lay.setRowStretch(3, 2)
lay.setRowStretch(5, 2)
lay.setRowStretch(7, 2)