Source code for siriushla.as_ap_sofb.graphics.orbit
"""Control the Orbit Graphic Displnay."""
from functools import partial as _part
import numpy as _np
from pyqtgraph import functions
from qtpy.QtWidgets import QWidget, QLabel, QVBoxLayout, QPushButton, \
QHBoxLayout, QGroupBox, QComboBox, QToolTip, QGridLayout
from qtpy.QtCore import Qt, Signal
from siriuspy.namesys import SiriusPVName as _PVName
from siriuspy.sofb.csdev import SOFBFactory
import siriushla.util as _util
from siriushla.widgets.windows import create_window_from_widget
from siriushla.widgets import SiriusSpectrogramView, SiriusLabel, \
SiriusConnectionSignal as _ConnSig
from .base import BaseWidget, Graph
from .correctors import CorrectorsWidget
[docs]
class OrbitWidget(BaseWidget):
"""."""
def __init__(self, parent, device, prefix='', ctrls=None, acc='SI'):
"""."""
self._chans = []
self._csorb = SOFBFactory.create(acc)
if not ctrls:
self._chans, ctrls = self.get_default_ctrls(
device, prefix, acc=acc.upper())
names = ['Line {0:d}'.format(i+1) for i in range(2)]
super().__init__(parent, device, ctrls, names, True, prefix, acc)
txt1, txt2 = 'IOC-SPassOrb', 'IOC-RefOrb'
if self.acc == 'SI':
txt1 = 'IOC-SlowOrb'
elif self.acc == 'BO':
txt1 = 'IOC-MTurnOrb'
self.updater[0].some_changed('val', txt1)
self.updater[0].some_changed('ref', txt2)
cb1 = self.findChild(QComboBox, 'ComboBox_val0')
cb2 = self.findChild(QComboBox, 'ComboBox_ref0')
it1 = cb1.findText(txt1)
it2 = cb2.findText(txt2)
cb1.setCurrentIndex(it1)
cb2.setCurrentIndex(it2)
self.add_buttons_for_images()
[docs]
def add_buttons_for_images(self):
"""."""
grpbx = QGroupBox('Other Graphs', self)
gdl = QGridLayout(grpbx)
gdl.setSpacing(2)
self.hbl_nameh.addWidget(grpbx)
btn = QPushButton('Corrs', grpbx)
gdl.addWidget(btn, 0, 0)
Window = create_window_from_widget(
CorrectorsWidget, title=self.acc + ' - Correctors')
_util.connect_window(
btn, Window, self, device=self.device,
prefix=self.prefix, acc=self.acc)
if self.isring:
btn = QPushButton('MTurn Orb', grpbx)
gdl.addWidget(btn, 0, 1)
Window = create_window_from_widget(
MultiTurnWidget, title='Multi Turn')
_util.connect_window(
btn, Window, self, sigs=self.updater[0].raw_ref_sig,
device=self.device, prefix=self.prefix, csorb=self._csorb)
btn = QPushButton('MTurn Sum', grpbx)
gdl.addWidget(btn, 0, 2)
Window = create_window_from_widget(
MultiTurnSumWidget, title='Multi Turn Sum')
_util.connect_window(
btn, Window, self, device=self.device, prefix=self.prefix,
csorb=self._csorb)
if self.acc != 'BO':
btn = QPushButton('SingPass Sum', grpbx)
gdl.addWidget(btn, 0, 3)
Window = create_window_from_widget(
SinglePassSumWidget, title='Single Pass Sum')
_util.connect_window(
btn, Window, self, device=self.device, prefix=self.prefix,
csorb=self._csorb)
[docs]
def channels(self):
"""."""
chans = super().channels()
chans.extend(self._chans)
return chans
[docs]
@staticmethod
def get_default_ctrls(device, prefix='', acc='SI'):
"""."""
pvs = ['RefOrbX-RB', 'RefOrbY-RB']
orbs = ['IOC-RefOrb', ]
if acc.upper() != 'BO':
pvs.extend(['SPassOrbX-Mon', 'SPassOrbY-Mon'])
orbs.append('IOC-SPassOrb')
if acc.upper() == 'SI':
pvs.extend(['SlowOrbX-Mon', 'SlowOrbY-Mon'])
orbs.append('IOC-SlowOrb')
if acc.upper() in {'SI', 'BO'}:
pvs.extend(['MTurnIdxOrbX-Mon', 'MTurnIdxOrbY-Mon'])
orbs.append('IOC-MTurnOrb')
chans = [_ConnSig(device.substitute(prefix=prefix, propty=pv))
for pv in pvs]
ctrls = dict()
pvs = iter(chans)
for orb in orbs:
pvi = next(pvs)
pvj = next(pvs)
ctrls[orb] = {
'x': {
'signal': pvi.new_value_signal,
'getvalue': pvi.getvalue},
'y': {
'signal': pvj.new_value_signal,
'getvalue': pvj.getvalue}}
return chans, ctrls
[docs]
class MultiTurnWidget(QWidget):
"""."""
def __init__(self, parent, sigs, device, prefix, csorb):
"""."""
super().__init__(parent)
self._csorb = csorb
self.prefix = prefix
self.device = _PVName(device)
self.devpref = self.device.substitute(prefix=prefix)
self.setObjectName(csorb.acc + 'App')
self.setupui()
self.sigs = sigs
self.fun2setref = {
'x': _part(self.setreforbits, 'x'),
'y': _part(self.setreforbits, 'y')}
[docs]
def showEvent(self, _):
"""."""
for pln, sig in self.sigs.items():
sig.connect(self.fun2setref[pln])
[docs]
def hideEvent(self, _):
"""."""
for pln, sig in self.sigs.items():
try:
sig.disconnect(self.fun2setref[pln])
except TypeError:
pass
[docs]
def setupui(self):
"""."""
hbl = QHBoxLayout(self)
self.spectx = MultiTurnSumWidget(
self, device=self.device, prefix=self.prefix,
orbtype='X', csorb=self._csorb)
self.specty = MultiTurnSumWidget(
self, device=self.device, prefix=self.prefix,
orbtype='Y', csorb=self._csorb)
hbl.addWidget(self.spectx)
hbl.addSpacing(50)
hbl.addWidget(self.specty)
[docs]
def setreforbits(self, pln, orb):
"""."""
if pln.lower() == 'x':
self.spectx.spect.setreforbit(orb)
else:
self.specty.spect.setreforbit(orb)
[docs]
class MultiTurnSumWidget(QWidget):
"""."""
ratio_sum_avg = Signal(str)
ratio_sum_std = Signal(str)
def __init__(self, parent, device, prefix='', orbtype='sum', csorb=None):
"""."""
super().__init__(parent)
self.prefix = prefix
self.device = _PVName(device)
self.devpref = self.device.substitute(prefix=prefix)
self.orbtype = orbtype.lower()
self._csorb = csorb
self.setObjectName(self.device.sec+'App')
self.multiturnidx = _ConnSig(
self.devpref.substitute(propty='MTurnIdx-SP'))
self.setupui()
[docs]
def setupui(self):
"""."""
vbl = QVBoxLayout(self)
if self.orbtype.startswith('sum'):
img_propty = 'MTurnSum-Mon'
orb_propty = 'MTurnIdxSum-Mon'
unit = 'count'
color = 'black'
elif self.orbtype.startswith('x'):
img_propty = 'MTurnOrbX-Mon'
orb_propty = 'MTurnIdxOrbX-Mon'
unit = 'm'
color = 'blue'
else:
img_propty = 'MTurnOrbY-Mon'
orb_propty = 'MTurnIdxOrbY-Mon'
unit = 'm'
color = 'red'
lbl_text = img_propty.split('-')[0]
self.spect = Spectrogram(
parent=self,
device=self.device,
prefix=self.prefix,
image_channel=self.devpref.substitute(propty=img_propty),
xaxis_channel=self.devpref.substitute(propty='BPMPosS-Mon'),
yaxis_channel=self.devpref.substitute(propty='MTurnTime-Mon'))
self.spect.new_data_sig.connect(self.update_graph)
self.spect.normalizeData = True
self.spect.yaxis.setLabel('Time', units='s')
self.spect.xaxis.setLabel('BPM Position', units='m')
self.spect.colorbar.label_format = '{:<8.1f}'
lab = QLabel(lbl_text + ' Orbit', self, alignment=Qt.AlignCenter)
lab.setStyleSheet("font-weight: bold;")
vbl.addWidget(lab)
vbl.addWidget(self.spect)
vbl.addStretch()
lab = QLabel(
'Average ' + lbl_text + ' vs Time', self, alignment=Qt.AlignCenter)
lab.setStyleSheet("font-weight: bold;")
hbl = QHBoxLayout()
hbl.addStretch()
hbl.addWidget(lab)
hbl.addStretch()
if self.orbtype.startswith('sum'):
hbl.addWidget(QLabel(' Eff [%] =', self))
ratio_avg = QLabel('000.0', self, alignment=Qt.AlignRight)
ratio_std = QLabel('000.0', self, alignment=Qt.AlignLeft)
hbl.addWidget(ratio_avg, alignment=Qt.AlignRight)
hbl.addWidget(QLabel(
'<html><head/><body><p>±</p></body></html>', self))
hbl.addWidget(ratio_std, alignment=Qt.AlignLeft)
hbl.addStretch()
self.ratio_sum_avg.connect(ratio_avg.setText)
self.ratio_sum_std.connect(ratio_std.setText)
vbl.addLayout(hbl)
graph = Graph(self)
graph.setLabel('bottom', text='Time', units='s')
graph.setLabel('left', text='Avg ' + lbl_text, units=unit)
opts = dict(
y_channel='A',
x_channel=self.devpref.substitute(propty='MTurnTime-Mon'),
name='',
color=color,
redraw_mode=2,
lineStyle=1,
lineWidth=3,
symbol='o',
symbolSize=10)
graph.addChannel(**opts)
graph.setShowLegend(False)
graph.plotItem.scene().sigMouseMoved.connect(self._show_tooltip_time)
self.curve = graph.curveAtIndex(0)
self.graph_time = graph
vbl.addWidget(graph)
if not self.orbtype.startswith('sum'):
return
vbl.addStretch()
wid = QWidget(self)
lab = QLabel(
lbl_text + ' orbit at index:', wid,
alignment=Qt.AlignRight | Qt.AlignVCenter)
lab.setStyleSheet("font-weight: bold;")
pdmlab = SiriusLabel(
wid, self.devpref.substitute(propty='MTurnIdx-RB'))
pdmlab.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
pdmlab.setStyleSheet('min-width:4em;')
wid.setLayout(QHBoxLayout())
wid.layout().addStretch()
wid.layout().addWidget(lab)
wid.layout().addWidget(pdmlab)
wid.layout().addStretch()
vbl.addWidget(wid)
graph = Graph(self)
vbl.addWidget(graph)
graph.setLabel('bottom', text='BPM Position', units='m')
graph.setLabel('left', text='Sum', units='count')
opts = dict(
y_channel=self.devpref.substitute(propty=orb_propty),
x_channel=self.devpref.substitute(propty='BPMPosS-Mon'),
name='',
color=color,
redraw_mode=2,
lineStyle=1,
lineWidth=3,
symbol='o',
symbolSize=10)
graph.addChannel(**opts)
graph.setShowLegend(False)
graph.plotItem.scene().sigMouseMoved.connect(self._show_tooltip)
self.graph = graph
[docs]
def mouseDoubleClickEvent(self, ev):
"""."""
if ev.button() != Qt.LeftButton:
return super().mouseDoubleClickEvent(ev)
graph = self.graph_time
curve = graph.curveAtIndex(0)
posx = curve.scatter.mapFromScene(ev.pos()).x()
times = curve.getData()[0]
if times is None:
return super().mouseDoubleClickEvent(ev)
ind = _np.argmin(_np.abs(times-posx))
self.multiturnidx.send_value_signal[int].emit(int(ind))
super().mouseDoubleClickEvent(ev)
def _show_tooltip(self, pos):
names = self._csorb.bpm_nicknames
posi = self._csorb.bpm_pos
unit = 'count'
graph = self.graph
curve = graph.curveAtIndex(0)
posx = curve.scatter.mapFromScene(pos).x()
if self._csorb.isring:
posx = posx % self._csorb.circum
ind = _np.argmin(_np.abs(_np.array(posi)-posx))
posy = curve.scatter.mapFromScene(pos).y()
sca, prf = functions.siScale(posy)
txt = '{0:s}, y = {1:.3f} {2:s}'.format(
names[ind], sca*posy, prf+unit)
QToolTip.showText(
graph.mapToGlobal(pos.toPoint()),
txt, graph, graph.geometry(), 500)
def _show_tooltip_time(self, pos):
graph = self.graph_time
curve = graph.curveAtIndex(0)
posx = curve.scatter.mapFromScene(pos).x()
times = curve.getData()[0]
if times is None:
return
ind = _np.argmin(_np.abs(times-posx))
txt = f'index = {ind:d}'
QToolTip.showText(
graph.mapToGlobal(pos.toPoint()),
txt, graph, graph.geometry(), 500)
[docs]
def update_graph(self, data):
"""."""
scale = 1e-6
if self.orbtype.startswith('sum'):
scale = 1
datay = scale*data.mean(axis=1)
self.curve.receiveYWaveform(datay)
if self.orbtype.startswith('sum') and datay.size > 6:
ratios = datay[-5:]/datay[0]*100
self.ratio_sum_avg.emit(f'{ratios.mean():.1f}')
self.ratio_sum_std.emit(f'{ratios.std():.1f}')
[docs]
class Spectrogram(SiriusSpectrogramView):
"""."""
new_data_sig = Signal(_np.ndarray)
def __init__(self, device, prefix='', **kwargs):
"""."""
self._reforb = None
super().__init__(**kwargs)
self.setObjectName('graph')
self.setStyleSheet('#graph {min-height: 15em; min-width: 20em;}')
self.prefix = prefix
self.device = _PVName(device)
self.devpref = self.device.substitute(prefix=prefix)
self.multiturnidx = _ConnSig(
self.devpref.substitute(propty='MTurnIdx-SP'))
[docs]
def channels(self):
"""."""
chans = super().channels()
chans.append(self.multiturnidx)
return chans
[docs]
def process_image(self, img):
"""."""
if self._reforb is not None and img.shape[1] == self._reforb.size:
img = img - self._reforb[None, :]
self.new_data_sig.emit(img)
return img
[docs]
def mouseDoubleClickEvent(self, ev):
"""."""
if ev.button() == Qt.LeftButton:
pos = self._image_item.mapFromDevice(ev.pos())
if pos.y() > 0 and pos.y() <= self._image_item.height():
self.multiturnidx.send_value_signal[int].emit(int(pos.y()))
super().mouseDoubleClickEvent(ev)
[docs]
class SinglePassSumWidget(QWidget):
"""."""
def __init__(self, parent, device, csorb, prefix=''):
"""."""
super().__init__(parent)
self.prefix = prefix
self.device = _PVName(device)
self.devpref = self.device.substitute(prefix=prefix)
self.setObjectName(csorb.acc+'App')
self._csorb = csorb
self.setupui()
[docs]
def setupui(self):
"""."""
vbl = QVBoxLayout(self)
lab = QLabel('SinglePass Sum BPMs', self, alignment=Qt.AlignCenter)
lab.setStyleSheet("font-weight: bold;")
vbl.addWidget(lab)
graph = Graph(self)
vbl.addWidget(graph)
graph.setLabel('bottom', text='BPM Position', units='m')
graph.setLabel('left', text='Sum', units='count')
opts = dict(
y_channel=self.devpref.substitute(propty='SPassSum-Mon'),
x_channel=self.devpref.substitute(propty='BPMPosS-Mon'),
name='',
color='black',
redraw_mode=2,
lineStyle=1,
lineWidth=3,
symbol='o',
symbolSize=10)
graph.addChannel(**opts)
graph.plotItem.scene().sigMouseMoved.connect(self._show_tooltip)
graph.setShowLegend(False)
self.graph = graph
def _show_tooltip(self, pos):
names = self._csorb.bpm_nicknames
posi = self._csorb.bpm_pos
unit = 'count'
graph = self.graph
curve = graph.curveAtIndex(0)
posx = curve.scatter.mapFromScene(pos).x()
if self._csorb.isring:
posx = posx % self._csorb.circum
ind = _np.argmin(_np.abs(_np.array(posi)-posx))
posy = curve.scatter.mapFromScene(pos).y()
sca, prf = functions.siScale(posy)
txt = '{0:s}, y = {1:.3f} {2:s}'.format(
names[ind], sca*posy, prf+unit)
QToolTip.showText(
graph.mapToGlobal(pos.toPoint()),
txt, graph, graph.geometry(), 500)