Пример #1
0
class SummaryWidget(QWidget):
    """General widget for controlling a power supply."""
    def __init__(self, name, visible_props, parent=None):
        """Build UI with dclink name."""
        super().__init__(parent)
        self._name = PVName(name)
        self._psmodel = PSSearch.conv_psname_2_psmodel(name)
        self._pstype = PSSearch.conv_psname_2_pstype(name)
        self.visible_props = sort_propties(visible_props)
        self.filled_widgets = set()
        self._prefixed_name = self._name.substitute(prefix=VACA_PREFIX)

        self._analog_name = get_analog_name(self._name)
        self._strength_name = get_strength_name(self._name)
        self._is_pulsed = IsPulsed.match(self._name)
        self._is_linac = IsLinac.match(self._name)
        self._li_has_not_strength = LIQuadHasNotStrength.match(self._name)
        self._is_fofb = FastCorrector.match(self._name)
        self._is_dclink = IsDCLink.match(self._name)
        self._is_regatron = self._psmodel == 'REGATRON_DCLink'
        self._is_reg_slave = self._pstype == 'as-dclink-regatron-slave'

        self._bbb_name = ''
        self._udc_name = ''
        if not self._is_pulsed and not self._is_linac and \
                not self._is_regatron and not self._is_fofb:
            self._bbb_name = PSSearch.conv_psname_2_bbbname(self._name)
            self._udc_name = PSSearch.conv_psname_2_udc(self._name)
        self._has_opmode = not self._is_linac and not self._is_pulsed\
            and not self._is_fofb
        self._has_ctrlmode = not self._is_regatron and not self._is_linac\
            and not self._is_fofb
        self._has_pwrstate = not self._is_reg_slave
        self._has_reset = not self._is_linac and not self._is_fofb\
            and not self._is_reg_slave
        self._has_ctrlloop = not self._is_linac and not self._is_pulsed\
            and not self._is_regatron
        self._has_parmupdt = not self._is_linac and not self._is_regatron\
            and not self._is_fofb
        self._has_wfmupdt = self._has_parmupdt and not self._is_dclink
        self._has_analsp = not self._is_reg_slave
        self._has_analrb = not self._is_regatron
        self._has_analmon = not self._is_fofb
        self._has_strength = bool(self._strength_name
                                  and not self._li_has_not_strength)
        self._has_strength_mon = self._has_strength and not self._is_fofb
        self._has_trim = HasTrim.match(self._name)

        self._create_pvs()
        self._setup_ui()

    @property
    def devname(self):
        """PS name."""
        return self._name

    @property
    def bbbname(self):
        """BBB name."""
        return self._bbb_name

    @property
    def udcname(self):
        """UDC name."""
        return self._udc_name

    def _setup_ui(self):
        """Setups widget UI."""
        lay = QHBoxLayout()
        lay.setContentsMargins(0, 0, 0, 0)
        lay.setSpacing(10)
        self._widgets_dict = dict()

        self.detail_wid = self._build_widget(name='detail', orientation='v')
        self._widgets_dict['detail'] = self.detail_wid
        lay.addWidget(self.detail_wid)

        if self._bbb_name:
            self.bbb_wid = self._build_widget(name='bbb', orientation='v')
            self._widgets_dict['bbb'] = self.bbb_wid
            lay.addWidget(self.bbb_wid)

        if self._udc_name:
            self.udc_wid = self._build_widget(name='udc', orientation='v')
            self._widgets_dict['udc'] = self.udc_wid
            lay.addWidget(self.udc_wid)

        if self._has_opmode:
            self.opmode_wid = self._build_widget(name='opmode',
                                                 orientation='v')
            self._widgets_dict['opmode'] = self.opmode_wid
            lay.addWidget(self.opmode_wid)

        if self._has_ctrlmode:
            self.ctrlmode_wid = self._build_widget(name='ctrlmode',
                                                   orientation='v')
            self._widgets_dict['ctrlmode'] = self.ctrlmode_wid
            lay.addWidget(self.ctrlmode_wid)

        self.state_wid = self._build_widget(name='state')
        self._widgets_dict['state'] = self.state_wid
        lay.addWidget(self.state_wid)

        if self._is_pulsed:
            self.pulse_wid = self._build_widget(name='pulse')
            self._widgets_dict['pulse'] = self.pulse_wid
            lay.addWidget(self.pulse_wid)

        self.intlk_wid = self._build_widget(name='intlk')
        self._widgets_dict['intlk'] = self.intlk_wid
        lay.addWidget(self.intlk_wid)

        if self._is_linac:
            self.conn_wid = self._build_widget(name='conn')
            self._widgets_dict['conn'] = self.conn_wid
            lay.addWidget(self.conn_wid)

        if self._has_reset:
            self.reset_wid = self._build_widget(name='reset')
            self._widgets_dict['reset'] = self.reset_wid
            lay.addWidget(self.reset_wid)

        if self._has_ctrlloop:
            self.ctrlloop_wid = self._build_widget(name='ctrlloop')
            self._widgets_dict['ctrlloop'] = self.ctrlloop_wid
            lay.addWidget(self.ctrlloop_wid)

        if self._has_wfmupdt:
            self.wfmupdate_wid = self._build_widget(name='wfmupdate')
            self._widgets_dict['wfmupdate'] = self.wfmupdate_wid
            lay.addWidget(self.wfmupdate_wid)

        if self._has_parmupdt:
            self.updparms_wid = self._build_widget(name='updparms')
            self._widgets_dict['updparms'] = self.updparms_wid
            lay.addWidget(self.updparms_wid)

        self.setpoint_wid = self._build_widget(name='setpoint',
                                               orientation='v')
        self._widgets_dict['setpoint'] = self.setpoint_wid
        lay.addWidget(self.setpoint_wid)

        if self._has_analrb:
            self.readback_wid = self._build_widget(name='readback',
                                                   orientation='v')
            self._widgets_dict['readback'] = self.readback_wid
            lay.addWidget(self.readback_wid)

        if self._has_analmon:
            self.monitor_wid = self._build_widget(name='monitor',
                                                  orientation='v')
            self._widgets_dict['monitor'] = self.monitor_wid
            lay.addWidget(self.monitor_wid)

        if self._has_strength:
            self.strength_sp_wid = self._build_widget(name='strength_sp',
                                                      orientation='v')
            self._widgets_dict['strength_sp'] = self.strength_sp_wid
            lay.addWidget(self.strength_sp_wid)

            self.strength_rb_wid = self._build_widget(name='strength_rb',
                                                      orientation='v')
            self._widgets_dict['strength_rb'] = self.strength_rb_wid
            lay.addWidget(self.strength_rb_wid)

        if self._has_strength_mon:
            self.strength_mon_wid = self._build_widget(name='strength_mon',
                                                       orientation='v')
            self._widgets_dict['strength_mon'] = self.strength_mon_wid
            lay.addWidget(self.strength_mon_wid)

        if self._has_trim:
            self.trim_wid = self._build_widget(name='trim', orientation='v')
            self._widgets_dict['trim'] = self.trim_wid
            lay.addWidget(self.trim_wid)

        _widths = get_prop2width(self._name)
        for name, widget in self._widgets_dict.items():
            width = _widths[name]
            widget.setStyleSheet('#' + name + '{min-width:' + str(width) +
                                 'em;'
                                 'max-width:' + str(width) + 'em;}')
            widget.setSizePolicy(QSzPlcy.Fixed, QSzPlcy.Fixed)
            widget.setVisible(name in self.visible_props)

        self.setStyleSheet("""
            PyDMStateButton{
                min-width: 2.5em; max-width: 2.5em;
                min-height: 1.5em; max-height: 1.5em;
            }
            QLed{
                min-width: 1.5em; max-width: 1.5em;
                min-height: 1.5em; max-height: 1.5em;
            }
            QLabel{
                min-height: 1.5em; max-height: 1.5em;
                qproperty-alignment: AlignCenter;
            }
        """)

        lay.addStretch()
        self.setLayout(lay)

        for prop in self.visible_props:
            self.fillWidget(prop)

    def _create_pvs(self):
        if self._has_opmode:
            if self._is_reg_slave:
                self._opmode_sts = self._prefixed_name.substitute(
                    propty='ModState-Mon')
            elif self._is_regatron:
                self._opmode_sts = self._prefixed_name.substitute(
                    propty='OpMode-Sts')
            else:
                self._opmode_sel = self._prefixed_name.substitute(
                    propty='OpMode-Sel')
                self._opmode_sts = self._prefixed_name.substitute(
                    propty='OpMode-Sts')

        if self._has_ctrlmode:
            self._ctrlmode_sts = self._prefixed_name.substitute(
                propty='CtrlMode-Mon')

        if self._has_pwrstate:
            self._pwrstate_sel = self._prefixed_name.substitute(
                propty='PwrState-Sel')
            self._pwrstate_sts = self._prefixed_name.substitute(
                propty='PwrState-Sts')

        if self._is_pulsed:
            self._pulse_sel = self._prefixed_name.substitute(
                propty='Pulse-Sel')
            self._pulse_sts = self._prefixed_name.substitute(
                propty='Pulse-Sts')

        # interlock
        if self._is_pulsed:
            self._intlk = list()
            for i in range(1, 8):
                self._intlk.append(
                    self._prefixed_name.substitute(propty='Intlk' + str(i) +
                                                   '-Mon'))
            if 'Sept' not in self._name.dev:
                self._intlk.append(
                    self._prefixed_name.substitute(propty='Intlk8-Mon'))
        elif self._is_linac:
            self._intlk = self._prefixed_name.substitute(
                propty='StatusIntlk-Mon')
        elif self._is_regatron:
            if not self._is_reg_slave:
                self._generr = self._prefixed_name.substitute(
                    propty='GenIntlk-Mon')
                self._genwrn = self._prefixed_name.substitute(
                    propty='GenWarn-Mon')
        elif self._is_fofb:
            self._intlk = [
                self._prefixed_name.substitute(
                    propty='PSAmpOverCurrFlagL-Sts'),
                self._prefixed_name.substitute(
                    propty='PSAmpOverCurrFlagR-Sts'),
                self._prefixed_name.substitute(
                    propty='PSAmpOverTempFlagL-Sts'),
                self._prefixed_name.substitute(
                    propty='PSAmpOverTempFlagR-Sts'),
            ]
        else:
            self._soft_intlk = self._prefixed_name.substitute(
                propty='IntlkSoft-Mon')
            self._hard_intlk = self._prefixed_name.substitute(
                propty='IntlkHard-Mon')

        if self._is_linac:
            self._conn = self._prefixed_name.substitute(propty='Connected-Mon')

        if self._has_reset:
            self._reset_intlk = self._prefixed_name.substitute(
                propty='Reset-Cmd')

        if self._has_ctrlloop:
            self._ctrlloop_sel = self._prefixed_name.substitute(
                propty='CtrlLoop-Sel')
            self._ctrlloop_sts = self._prefixed_name.substitute(
                propty='CtrlLoop-Sts')

        if self._has_wfmupdt:
            self._wfmupdate_sel = self._prefixed_name.substitute(
                propty='WfmUpdateAuto-Sel')
            self._wfmupdate_sts = self._prefixed_name.substitute(
                propty='WfmUpdateAuto-Sts')

        if self._has_parmupdt:
            self._updparms_cmd = self._prefixed_name.substitute(
                propty='ParamUpdate-Cmd')

        # analog control
        sp = self._analog_name
        if self._has_analsp:
            self._analog_sp = self._prefixed_name.substitute(
                propty='{}-SP'.format(sp))
        if self._has_analrb:
            self._analog_rb = self._prefixed_name.substitute(
                propty='{}-RB'.format(sp))
        if self._has_analmon:
            if self._is_reg_slave:
                self._analog_mon = self._prefixed_name.substitute(
                    propty='ModOutVolt-Mon')
            else:
                self._analog_mon = self._prefixed_name.substitute(
                    propty='{}-Mon'.format(sp))

        # strength
        if self._has_strength:
            st = self._strength_name
            self._strength_sp = self._prefixed_name.substitute(
                propty='{}-SP'.format(st))
            self._strength_rb = self._prefixed_name.substitute(
                propty='{}-RB'.format(st))
        if self._has_strength_mon:
            self._strength_mon = self._prefixed_name.substitute(
                propty='{}-Mon'.format(st))

    def _build_widget(self, name='', orientation='h'):
        widget = QWidget(self)
        widget.setObjectName(name)
        if orientation == 'h':
            lay = QHBoxLayout(widget)
        else:
            lay = QVBoxLayout(widget)
        lay.setSpacing(0)
        lay.setContentsMargins(0, 3, 0, 3)
        return widget

    def fillWidget(self, name):
        if name in self.filled_widgets:
            return
        if name == 'detail':
            self.detail_bt = QPushButton(self)
            if self._name.dev == 'DCLink':
                self.detail_bt.setIcon(qta.icon('fa5s.list-ul'))
                self.detail_bt.setToolTip(self._name)
            else:
                self.detail_bt.setText(self._name)
            self.detail_wid.layout().addWidget(self.detail_bt)
        elif name == 'bbb' and self._bbb_name:
            self.bbb_lb = QLabel(self._bbb_name, self)
            self.bbb_wid.layout().addWidget(self.bbb_lb)
        elif name == 'udc' and self._udc_name:
            self.udc_lb = QLabel(self._udc_name, self)
            self.udc_wid.layout().addWidget(self.udc_lb)
        elif name == 'opmode' and self._has_opmode:
            opmode_list = list()
            if 'Voltage' not in self._analog_name:
                self.opmode_cb = SiriusEnumComboBox(self, self._opmode_sel)
                opmode_list.append(self.opmode_cb)
            self.opmode_lb = PyDMLabel(self, self._opmode_sts)
            opmode_list.append(self.opmode_lb)
            for wid in opmode_list:
                self.opmode_wid.layout().addWidget(wid)
        elif name == 'ctrlmode' and self._has_ctrlmode:
            self.ctrlmode_lb = PyDMLabel(self, self._ctrlmode_sts)
            self.ctrlmode_wid.layout().addWidget(self.ctrlmode_lb)
        elif name == 'state' and self._has_pwrstate:
            self.state_bt = PyDMStateButton(self, self._pwrstate_sel)
            self.state_led = SiriusLedState(self, self._pwrstate_sts)
            self.state_wid.layout().addWidget(self.state_bt)
            self.state_wid.layout().addWidget(self.state_led)
        elif name == 'pulse' and self._is_pulsed:
            self.pulse_bt = PyDMStateButton(self, self._pulse_sel)
            self.pulse_led = SiriusLedState(self, self._pulse_sts)
            self.pulse_wid.layout().addWidget(self.pulse_bt)
            self.pulse_wid.layout().addWidget(self.pulse_led)
        elif name == 'intlk':
            if self._is_pulsed:
                self.intlk_led = PyDMLedMultiChannel(
                    self, channels2values={ch: 1
                                           for ch in self._intlk})
                self.intlk_wid.layout().addWidget(self.intlk_led)
            elif self._is_linac:
                if IsLinacSpect.match(self.devname):
                    self.intlk_led = LISpectIntlkLed(self)
                else:
                    self.intlk_led = PyDMLedMultiChannel(self,
                                                         channels2values={
                                                             self._intlk: {
                                                                 'value':
                                                                 _PS_LI_INTLK,
                                                                 'comp': 'lt'
                                                             }
                                                         })
                self.intlk_wid.layout().addWidget(self.intlk_led)
            elif self._is_regatron:
                if not self._is_reg_slave:
                    self.generr_led = SiriusLedAlert(self, self._generr)
                    self.genwrn_led = SiriusLedAlert(self, self._genwrn)
                    self.intlk_wid.layout().addWidget(self.generr_led)
                    self.intlk_wid.layout().addWidget(self.genwrn_led)
            elif self._is_fofb:
                self.intlk_led = PyDMLedMultiChannel(
                    self, channels2values={ch: 1
                                           for ch in self._intlk})
                self.intlk_wid.layout().addWidget(self.intlk_led)
            else:
                self.soft_intlk_led = SiriusLedAlert(self, self._soft_intlk)
                self.hard_intlk_led = SiriusLedAlert(self, self._hard_intlk)
                self.intlk_wid.layout().addWidget(self.soft_intlk_led)
                self.intlk_wid.layout().addWidget(self.hard_intlk_led)
        elif name == 'conn' and self._is_linac:
            self.conn_led = PyDMLedMultiChannel(
                self, channels2values={self._conn: 0})
            self.conn_wid.layout().addWidget(self.conn_led)
        elif name == 'reset' and self._has_reset:
            self.reset_bt = PyDMPushButton(parent=self,
                                           init_channel=self._reset_intlk,
                                           pressValue=1)
            self.reset_bt.setIcon(qta.icon('fa5s.sync'))
            self.reset_bt.setObjectName('reset_bt')
            self.reset_bt.setStyleSheet(
                '#reset_bt{min-width:25px; max-width:25px; icon-size:20px;}')
            self.reset_wid.layout().addWidget(self.reset_bt)
        elif name == 'ctrlloop' and self._has_ctrlloop:
            if self._is_fofb:
                self.ctrlloop_bt = PyDMStateButton(self, self._ctrlloop_sel)
            else:
                self.ctrlloop_bt = PyDMStateButton(self,
                                                   self._ctrlloop_sel,
                                                   invert=True)
            self.ctrlloop_lb = PyDMLabel(self, self._ctrlloop_sts)
            self.ctrlloop_wid.layout().addWidget(self.ctrlloop_bt)
            self.ctrlloop_wid.layout().addWidget(self.ctrlloop_lb)
        elif name == 'wfmupdate' and self._has_wfmupdt:
            self.wfmupdate_bt = PyDMStateButton(self, self._wfmupdate_sel)
            self.wfmupdate_led = SiriusLedState(self, self._wfmupdate_sts)
            self.wfmupdate_wid.layout().addWidget(self.wfmupdate_bt)
            self.wfmupdate_wid.layout().addWidget(self.wfmupdate_led)
        elif name == 'updparms' and self._has_parmupdt:
            self.updparms_bt = PyDMPushButton(parent=self,
                                              init_channel=self._updparms_cmd,
                                              pressValue=1)
            self.updparms_bt.setIcon(qta.icon('fa5s.redo-alt'))
            self.updparms_bt.setObjectName('updparms_bt')
            self.updparms_bt.setStyleSheet(
                '#updparms_bt{min-width:25px;max-width:25px;icon-size:20px;}')
            self.updparms_wid.layout().addWidget(self.updparms_bt)
        elif name == 'setpoint' and self._has_analsp:
            self.setpoint = PyDMSpinboxScrollbar(self, self._analog_sp)
            if self._is_fofb:
                self.setpoint.spinbox.precisionFromPV = False
                self.setpoint.spinbox.precision = 6
            self.setpoint_wid.layout().addWidget(self.setpoint)
        elif name == 'readback' and self._has_analrb:
            self.readback = PyDMLabel(self, self._analog_rb)
            if self._is_fofb:
                self.readback.precisionFromPV = False
                self.readback.precision = 6
            self.readback_wid.layout().addWidget(self.readback)
        elif name == 'monitor' and self._has_analmon:
            self.monitor = PyDMLabel(self, self._analog_mon)
            self.monitor_wid.layout().addWidget(self.monitor)
        elif name == 'strength_sp' and self._has_strength:
            self.strength_sp_le = PyDMSpinboxScrollbar(self, self._strength_sp)
            self.strength_sp_wid.layout().addWidget(self.strength_sp_le)
        elif name == 'strength_rb' and self._has_strength:
            self.strength_rb_lb = PyDMLabel(parent=self,
                                            init_channel=self._strength_rb)
            self.strength_rb_lb.showUnits = True
            self.strength_rb_wid.layout().addWidget(self.strength_rb_lb)
        elif name == 'strength_mon' and self._has_strength_mon:
            self.strength_mon_lb = PyDMLabel(parent=self,
                                             init_channel=self._strength_mon)
            self.strength_mon_lb.showUnits = True
            self.strength_mon_wid.layout().addWidget(self.strength_mon_lb)
        elif name == 'trim' and self._has_trim:
            self.trim_bt = QPushButton(qta.icon('fa5s.angle-right'), '', self)
            self.trim_wid.layout().addWidget(self.trim_bt)
        self.filled_widgets.add(name)

    def get_detail_button(self):
        """Return psname button."""
        return self.detail_bt

    def get_trim_button(self):
        """Return trim button."""
        if self._has_trim:
            return self.trim_bt
        return None

    def set_opmode_slowref(self):
        """Set power supply OpMode to SlowRef."""
        if hasattr(self, 'opmode_cb'):
            if self.opmode_cb.isEnabled():
                index = self.opmode_cb.findText('SlowRef')
                self.opmode_cb.internal_combo_box_activated_int(index)

    def turn_on(self):
        """Turn power supply on."""
        if hasattr(self, 'state_bt'):
            if self.state_bt.isEnabled():
                if not self.state_bt.value:
                    self.state_bt.send_value()

    def turn_off(self):
        """Turn power supply off."""
        if hasattr(self, 'state_bt'):
            if self.state_bt.isEnabled():
                if self.state_bt.value:
                    self.state_bt.send_value()

    def ctrlloop_close(self):
        """Close control loop."""
        if hasattr(self, 'ctrlloop_bt'):
            if self.ctrlloop_bt.isEnabled():
                if not self.ctrlloop_bt.value:
                    self.ctrlloop_bt.send_value()

    def ctrlloop_open(self):
        """Open control loop."""
        if hasattr(self, 'ctrlloop_bt'):
            if self.ctrlloop_bt.isEnabled():
                if self.ctrlloop_bt.value:
                    self.ctrlloop_bt.send_value()

    def pulse_on(self):
        """Turn power supply on."""
        if hasattr(self, 'pulse_bt'):
            if self.pulse_bt.isEnabled():
                if not self.pulse_bt.value:
                    self.pulse_bt.send_value()

    def pulse_off(self):
        """Turn power supply off."""
        if hasattr(self, 'pulse_bt'):
            if self.pulse_bt.isEnabled():
                if self.pulse_bt.value:
                    self.pulse_bt.send_value()

    def wfmupdate_on(self):
        """Enable WfmUpdateAuto."""
        if hasattr(self, 'wfmupdate_bt'):
            if self.wfmupdate_bt.isEnabled():
                if not self.wfmupdate_bt.value:
                    self.wfmupdate_bt.send_value()

    def wfmupdate_off(self):
        """Disable WfmUpdateAuto."""
        if hasattr(self, 'wfmupdate_bt'):
            if self.wfmupdate_bt.isEnabled():
                if self.wfmupdate_bt.value:
                    self.wfmupdate_bt.send_value()

    def reset(self):
        """Reset power supply."""
        if hasattr(self, 'reset_bt'):
            if self.reset_bt.isEnabled():
                self.reset_bt.sendValue()

    def update_params(self):
        """Update power supply parameters."""
        if hasattr(self, 'updparms_bt'):
            if self.updparms_bt.isEnabled():
                self.updparms_bt.sendValue()
Пример #2
0
class PosAngCorr(SiriusMainWindow):
    """Main Class."""

    def __init__(self, parent=None, prefix='', tl=None):
        """Class construc."""
        super(PosAngCorr, self).__init__(parent)
        if not prefix:
            self._prefix = _VACA_PREFIX
        else:
            self._prefix = prefix
        self._tl = tl.upper()
        base_name = _PVName('TL-Glob:AP-PosAng')
        self.posang_prefix = base_name.substitute(
            prefix=self._prefix, sec=self._tl)
        self.setObjectName(self._tl+'App')
        self.setWindowTitle(self._tl + ' Position and Angle Correction Window')

        if self._tl == 'TS':
            self._is_chsept = False
            ch3_pv = _PV(self.posang_prefix.substitute(propty='CH3-Cte'),
                         connection_timeout=1)
            if not ch3_pv.wait_for_connection():
                self._is_chsept = True

        if tl == 'ts':
            corr_h = (Const.TS_CORRH_POSANG_CHSEPT if self._is_chsept
                      else Const.TS_CORRH_POSANG_SEPTSEPT)
            corr_v = Const.TS_CORRV_POSANG
        elif tl == 'tb':
            corr_h = Const.TB_CORRH_POSANG
            corr_v = Const.TB_CORRV_POSANG

        self.corrs = dict()
        self.corrs['CH1'] = _PVName(corr_h[0])
        self.corrs['CH2'] = _PVName(corr_h[1])
        if len(corr_h) == 3:
            self.corrs['CH3'] = _PVName(corr_h[2])
        self.corrs['CV1'] = _PVName(corr_v[0])
        self.corrs['CV2'] = _PVName(corr_v[1])
        if len(corr_v) == 4:
            self.corrs['CV3'] = _PVName(corr_v[2])
            self.corrs['CV4'] = _PVName(corr_v[3])

        self._just_need_update = False
        self._update_ref_action = False
        self._my_input_widgets = list()
        self._setupUi()
        self.setFocus(True)
        self.setFocusPolicy(Qt.StrongFocus)

        self._ask_message = QMessageBox(self)
        self._ask_message.setWindowTitle('Message')
        self._ask_message.setText(
            'The '+self._tl+' PosAng IOC indicates reference needs to '
            'be updated! Do you want to update the reference?')
        self._ask_message.setStandardButtons(QMessageBox.No | QMessageBox.Yes)
        self._ask_message.setDefaultButton(QMessageBox.No)
        self.app = QApplication.instance()
        self.app.focusChanged.connect(self._spinbox_onfocus)

    def _setupUi(self):
        cwt = QWidget(self)
        self.setCentralWidget(cwt)

        # label
        lab = QLabel(
            '<h3>'+self._tl+' Position and Angle Correction</h3>', cwt)
        lab.setStyleSheet("""
            min-height:1.55em; max-height: 1.55em;
            qproperty-alignment: 'AlignVCenter | AlignRight';
            background-color: qlineargradient(spread:pad, x1:1, y1:0.0227273,
                              x2:0, y2:0, stop:0 rgba(173, 190, 207, 255),
                              stop:1 rgba(213, 213, 213, 255));""")

        # apply button
        self.pb_updateref = PyDMPushButton(
            self, 'Update Reference', pressValue=1,
            init_channel=self.posang_prefix.substitute(
                propty='SetNewRefKick-Cmd'))
        self.pb_updateref.setStyleSheet(
            'min-height: 2.4em; max-height: 2.4em;')
        self.led_needrefupdt = SiriusLedAlert(
            self, self.posang_prefix.substitute(propty='NeedRefUpdate-Mon'))
        self.ch_needrefupdt = SiriusConnectionSignal(
            self.posang_prefix.substitute(propty='NeedRefUpdate-Mon'))
        self.ch_needrefupdt.new_value_signal[int].connect(
            self._handle_need_update_ref_led)
        self.led_needrefupdt.setStyleSheet(
            'QLed{min-width: 1.29em; max-width: 1.29em;}')
        box_ref = QHBoxLayout()
        box_ref.setContentsMargins(0, 0, 0, 0)
        box_ref.addWidget(self.pb_updateref)
        box_ref.addWidget(self.led_needrefupdt)

        # delta setters
        self.hgbox = QGroupBox('Horizontal', self)
        self.hgbox.setLayout(self._setupDeltaControlLayout('x'))

        self.vgbox = QGroupBox('Vertical', self)
        self.vgbox.setLayout(self._setupDeltaControlLayout('y'))

        # correctors
        self.corrgbox = QGroupBox('Correctors', self)
        self.corrgbox.setLayout(self._setupCorrectorsLayout())

        # status
        self.statgbox = QGroupBox('Correction Status', self)
        self.statgbox.setLayout(self._setupStatusLayout())

        glay = QGridLayout(cwt)
        glay.setHorizontalSpacing(12)
        glay.setVerticalSpacing(12)
        glay.addWidget(lab, 0, 0, 1, 2)
        glay.addLayout(box_ref, 1, 0, 1, 2)
        glay.addWidget(self.hgbox, 2, 0)
        glay.addWidget(self.vgbox, 2, 1)
        glay.addWidget(self.corrgbox, 3, 0, 1, 2)
        glay.addWidget(self.statgbox, 4, 0, 1, 2)

        # menu
        act_settings = self.menuBar().addAction('Settings')
        _hlautil.connect_window(act_settings, CorrParamsDetailWindow,
                                parent=self, tl=self._tl, prefix=self._prefix)

        # stlesheet
        self.setStyleSheet("""
            PyDMSpinbox{
                min-width: 5em; max-width: 5em;
            }
            PyDMLabel, PyDMSpinboxScrollbar{
                min-width: 6em; max-width: 6em;
            }
            QPushButton{
                min-width: 8em;
            }
            QLabel{
                min-height: 1.35em;
                qproperty-alignment: AlignCenter;
            }
        """)

    def _setupDeltaControlLayout(self, axis=''):
        # pos
        label_pos = QLabel("<h4>Δ"+axis+"</h4>", self)
        sb_deltapos = PyDMSpinbox(self, self.posang_prefix.substitute(
            propty='DeltaPos'+axis.upper()+'-SP'))
        sb_deltapos.step_exponent = -2
        sb_deltapos.update_step_size()
        sb_deltapos.showStepExponent = False
        lb_deltapos = PyDMLabel(self, self.posang_prefix.substitute(
            propty='DeltaPos'+axis.upper()+'-RB'))
        lb_deltapos.showUnits = True
        self._my_input_widgets.append(sb_deltapos)
        # ang
        label_ang = QLabel("<h4>Δ"+axis+"'</h4>", self)
        sb_deltaang = PyDMSpinbox(self, self.posang_prefix.substitute(
            propty='DeltaAng'+axis.upper()+'-SP'))
        sb_deltaang.step_exponent = -2
        sb_deltaang.update_step_size()
        sb_deltaang.showStepExponent = False
        lb_deltaang = PyDMLabel(self, self.posang_prefix.substitute(
            propty='DeltaAng'+axis.upper()+'-RB'))
        lb_deltaang.showUnits = True
        self._my_input_widgets.append(sb_deltaang)

        lay = QGridLayout()
        lay.setVerticalSpacing(12)
        lay.setHorizontalSpacing(12)
        lay.addItem(
            QSpacerItem(10, 0, QSzPlcy.Expanding, QSzPlcy.Ignored), 0, 0)
        lay.addWidget(label_pos, 0, 1)
        lay.addWidget(sb_deltapos, 0, 2)
        lay.addWidget(lb_deltapos, 0, 3)
        lay.addWidget(label_ang, 1, 1)
        lay.addWidget(sb_deltaang, 1, 2)
        lay.addWidget(lb_deltaang, 1, 3)
        lay.addItem(
            QSpacerItem(10, 0, QSzPlcy.Expanding, QSzPlcy.Ignored), 0, 4)
        return lay

    def _setupCorrectorsLayout(self):
        lay = QGridLayout()
        lay.setVerticalSpacing(9)
        lay.setHorizontalSpacing(9)

        label_kicksp = QLabel('<h4>Kick-SP</h4>', self)
        label_kickrb = QLabel('<h4>Kick-RB</h4>', self)
        label_kickref = QLabel('<h4>RefKick-Mon</h4>', self)
        lay.addWidget(label_kicksp, 0, 2)
        lay.addWidget(label_kickrb, 0, 3)
        lay.addWidget(label_kickref, 0, 4)

        idx = 1
        for corrid, corr in self.corrs.items():
            pbt = QPushButton(qta.icon('fa5s.list-ul'), '', self)
            pbt.setObjectName('pbt')
            pbt.setStyleSheet("""
                #pbt{
                    min-width:25px; max-width:25px;
                    min-height:25px; max-height:25px;
                    icon-size:20px;}
                """)
            if corr.dis == 'PU':
                _hlautil.connect_window(
                    pbt, _PUDetailWindow, self, devname=corr)
            else:
                _hlautil.connect_window(
                    pbt, _PSDetailWindow, self, psname=corr)
            lb_name = QLabel(corr, self)
            le_sp = PyDMSpinboxScrollbar(
                self, corr.substitute(prefix=self._prefix, propty='Kick-SP'))
            le_sp.spinbox.setAlignment(Qt.AlignCenter)
            le_sp.scrollbar.limitsFromPV = True
            lb_rb = PyDMLabel(self, corr.substitute(
                prefix=self._prefix, propty='Kick-RB'))
            lb_ref = PyDMLabel(self, self.posang_prefix.substitute(
                propty='RefKick'+corrid+'-Mon'))

            lay.addWidget(pbt, idx, 0, alignment=Qt.AlignTop)
            lay.addWidget(
                lb_name, idx, 1, alignment=Qt.AlignLeft | Qt.AlignTop)
            lay.addWidget(le_sp, idx, 2, alignment=Qt.AlignTop)
            lay.addWidget(lb_rb, idx, 3, alignment=Qt.AlignTop)
            lay.addWidget(lb_ref, idx, 4, alignment=Qt.AlignTop)
            idx += 1

        if self._tl == 'TB':
            pref = self._prefix + ('-' if self._prefix else '')
            lay.addItem(QSpacerItem(0, 8, QSzPlcy.Ignored, QSzPlcy.Fixed))

            label_voltsp = QLabel('<h4>Amplitude-SP</h4>', self)
            label_voltrb = QLabel('<h4>Amplitude-RB</h4>', self)
            lay.addWidget(label_voltsp, idx+2, 2)
            lay.addWidget(label_voltrb, idx+2, 3)

            lb_kly2_name = QLabel('Klystron 2', self)
            le_kly2_sp = PyDMSpinboxScrollbar(
                self, pref+'LA-RF:LLRF:KLY2:SET_AMP')
            le_kly2_sp.spinbox.precisionFromPV = False
            le_kly2_sp.spinbox.precision = 2
            le_kly2_sp.spinbox.setAlignment(Qt.AlignCenter)
            le_kly2_sp.scrollbar.limitsFromPV = True
            lb_kly2_rb = PyDMLabel(self, pref+'LA-RF:LLRF:KLY2:GET_AMP')
            lb_kly2_rb.precisionFromPV = False
            lb_kly2_rb.precision = 2
            lay.addWidget(lb_kly2_name, idx+3, 1,
                          alignment=Qt.AlignLeft | Qt.AlignTop)
            lay.addWidget(le_kly2_sp, idx+3, 2, alignment=Qt.AlignTop)
            lay.addWidget(lb_kly2_rb, idx+3, 3, alignment=Qt.AlignTop)
            self._kckr_name = _PVName('BO-01D:PU-InjKckr')
        else:
            self._kckr_name = _PVName('SI-01SA:PU-InjNLKckr')

        label_voltsp = QLabel('<h4>Voltage-SP</h4>', self)
        label_voltrb = QLabel('<h4>Voltage-RB</h4>', self)
        lay.addWidget(label_voltsp, idx+4, 2)
        lay.addWidget(label_voltrb, idx+4, 3)

        lay.addItem(QSpacerItem(0, 8, QSzPlcy.Ignored, QSzPlcy.Fixed))
        pb_kckr = QPushButton(qta.icon('fa5s.list-ul'), '', self)
        pb_kckr.setObjectName('pb')
        pb_kckr.setStyleSheet("""
            #pb{
                min-width:25px; max-width:25px;
                min-height:25px; max-height:25px;
                icon-size:20px;}
            """)
        lb_kckr_name = QLabel(self._kckr_name, self)
        _hlautil.connect_window(
            pb_kckr, _PUDetailWindow, self, devname=self._kckr_name)
        lb_kckr_sp = PyDMSpinboxScrollbar(
            self, self._kckr_name.substitute(
                prefix=self._prefix, propty='Voltage-SP'))
        lb_kckr_sp.scrollbar.limitsFromPV = True
        lb_kckr_rb = PyDMLabel(self, self._kckr_name.substitute(
            prefix=self._prefix, propty='Voltage-RB'))
        lay.addWidget(pb_kckr, idx+5, 0, alignment=Qt.AlignTop)
        lay.addWidget(
            lb_kckr_name, idx+5, 1, alignment=Qt.AlignLeft | Qt.AlignTop)
        lay.addWidget(lb_kckr_sp, idx+5, 2, alignment=Qt.AlignTop)
        lay.addWidget(lb_kckr_rb, idx+5, 3, alignment=Qt.AlignTop)
        return lay

    def _setupStatusLayout(self):
        self.log = PyDMLogLabel(
            self, self.posang_prefix.substitute(propty='Log-Mon'))
        self.lb_sts0 = QLabel(Const.STATUSLABELS[0], self)
        self.led_sts0 = SiriusLedAlert(
            self, self.posang_prefix.substitute(propty='Status-Mon'), bit=0)
        self.lb_sts1 = QLabel(Const.STATUSLABELS[1], self)
        self.led_sts1 = SiriusLedAlert(
            self, self.posang_prefix.substitute(propty='Status-Mon'), bit=1)
        self.lb_sts2 = QLabel(Const.STATUSLABELS[2], self)
        self.led_sts2 = SiriusLedAlert(
            self, self.posang_prefix.substitute(propty='Status-Mon'), bit=2)
        self.lb_sts3 = QLabel(Const.STATUSLABELS[3], self)
        self.led_sts3 = SiriusLedAlert(
            self, self.posang_prefix.substitute(propty='Status-Mon'), bit=3)
        self.pb_config = PyDMPushButton(
            self, label='Config Correctors', pressValue=1,
            init_channel=self.posang_prefix.substitute(propty='ConfigPS-Cmd'))

        lay = QGridLayout()
        lay.setVerticalSpacing(12)
        lay.setHorizontalSpacing(12)
        lay.addWidget(self.log, 0, 0, 6, 1)
        lay.addWidget(self.lb_sts0, 1, 2)
        lay.addWidget(self.led_sts0, 1, 1)
        lay.addWidget(self.lb_sts1, 2, 2)
        lay.addWidget(self.led_sts1, 2, 1)
        lay.addWidget(self.lb_sts2, 3, 2)
        lay.addWidget(self.led_sts2, 3, 1)
        lay.addWidget(self.lb_sts3, 4, 2)
        lay.addWidget(self.led_sts3, 4, 1)
        lay.addWidget(self.pb_config, 5, 1, 1, 2)

        if self._tl == 'TS':
            self.led_corrtype = PyDMLedMultiChannel(
                self, {self.posang_prefix.substitute(
                        propty='CH1-Cte'): self.corrs['CH1']})
            self.lb_corrtype = QLabel(
                'Control ' + ('CH-Sept' if self._is_chsept else 'Sept-Sept'))
            lay.addWidget(self.led_corrtype, 0, 1)
            lay.addWidget(self.lb_corrtype, 0, 2)
        return lay

    def _set_correctors_channels(self, corrs):
        self.centralwidget.pushButton_CH1.setText(corrs[0])
        _hlautil.connect_window(
            self.centralwidget.pushButton_CH1, _PSDetailWindow, self,
            psname=corrs[0])
        self.centralwidget.PyDMLabel_KickRBCH1.channel = (
            corrs[0].substitute(prefix=self._prefix, propty='Kick-RB'))

        self.centralwidget.pushButton_CH2.setText(corrs[1])
        if corrs[1].dis == 'PU':
            _hlautil.connect_window(
                self.centralwidget.pushButton_CH2, _PUDetailWindow, self,
                devname=corrs[1])
        else:
            _hlautil.connect_window(
                self.centralwidget.pushButton_CH2, _PSDetailWindow, self,
                psname=corrs[1])
        self.centralwidget.PyDMLabel_KickRBCH2.channel = (
            corrs[1].substitute(prefix=self._prefix, propty='Kick-RB'))

        self.centralwidget.pushButton_CV1.setText(corrs[2])
        _hlautil.connect_window(
            self.centralwidget.pushButton_CV1, _PSDetailWindow, self,
            psname=corrs[2])
        self.centralwidget.PyDMLabel_KickRBCV1.channel = (
            corrs[2].substitute(prefix=self._prefix, propty='Kick-RB'))

        self.centralwidget.pushButton_CV2.setText(corrs[3])
        _hlautil.connect_window(
            self.centralwidget.pushButton_CV2, _PSDetailWindow, self,
            psname=corrs[3])
        self.centralwidget.PyDMLabel_KickRBCV2.channel = (
            corrs[3].substitute(prefix=self._prefix, propty='Kick-RB'))

    def _handle_need_update_ref_led(self, value):
        self._just_need_update = bool(value)

    def _spinbox_onfocus(self, old_focus, new_focus):
        if not self._update_ref_action and not self._just_need_update:
            return

        if self.led_needrefupdt.value != 0:
            if new_focus in self._my_input_widgets and self._just_need_update:
                ans = self._ask_message.exec_()
                if ans == QMessageBox.No:
                    self._update_ref_action = False
                else:
                    self._update_ref_action = True
                    self.pb_updateref.sendValue()
                self._just_need_update = False