''' Diagnostic Interface of the LINAC's Screen'''
import os as _os
import numpy as _np
from qtpy.QtCore import Qt
from qtpy.QtGui import QPixmap
from qtpy.QtWidgets import QGroupBox, QHBoxLayout, QVBoxLayout, \
QWidget, QLabel, QGridLayout, QStackedWidget, \
QSizePolicy, QPushButton
from pydm.widgets import PyDMPushButton, PyDMImageView, PyDMLineEdit, \
enum_button
import qtawesome as qta
from .. import util as _util
from ..widgets import SiriusMainWindow, PyDMLedMultiChannel, \
SiriusWaveformPlot, SiriusLabel, SiriusSpinbox, SiriusConnectionSignal
from .util import DEVICES, SCREENS_PANEL, SCREENS_INFO, HEADER, \
GRAPH, SCREEN
from .motorBtn import MotorBtn
[docs]
class GeneralFunctions():
''' Class with the functions used in both Screens Windows'''
[docs]
def getPvName(self, device, pv_name):
''' Build PV name '''
return self.device_name + ':' + device + ":" + pv_name
[docs]
def setBasicInfo(self, device, label, pv_name):
''' Build one basic information Component '''
bi_hlay = QHBoxLayout()
bi_hlay.addWidget(
QLabel(label),
alignment=Qt.AlignCenter)
if label in ['Limit Mode', 'Motor Code', 'Counter', 'Sigma', 'Center']:
wid_type = 'label'
elif label == "Coefficient":
wid_type = 'spinBox'
elif label == "Centroid Threshold":
wid_type = 'lineEdit'
else:
wid_type = 'led'
bi_hlay.addWidget(
self.setWidgetType(wid_type, device, pv_name))
return bi_hlay
[docs]
def setRBVObj(self, device, channel, label, pv_prefix):
''' Build formatted RBV Component'''
rbv_hlay = QHBoxLayout()
title = QLabel(label)
title.setMinimumWidth(75)
title.setAlignment(Qt.AlignVCenter | Qt.AlignRight)
rbv_hlay.addWidget(title)
for item in range(1, -1, -1):
pv_name = pv_prefix + channel[item]
if 'RBV' not in pv_name:
if label in ['Gain', 'Exposure']:
wid_type = 'lineEdit'
else:
wid_type = 'spinBox'
else:
wid_type = 'label'
widget = self.setWidgetType(wid_type, device, pv_name)
widget.setMaximumWidth(75)
if item != 0:
rbv_hlay.addWidget(
widget,
alignment=Qt.AlignRight)
if 'MaxSize' in channel[0]:
sep_label = QLabel("X")
sep_label.setFixedWidth(10)
rbv_hlay.addWidget(
sep_label,
Qt.AlignCenter)
else:
rbv_hlay.addWidget(
widget, alignment=Qt.AlignLeft)
return rbv_hlay
[docs]
def setSingleScrn(self, device, screen):
''' Build a single screen Component '''
group = QGroupBox()
ss_vlay = QVBoxLayout()
image_wid = PyDMImageView(
image_channel=self.getPvName(device, screen['data']),
width_channel=self.getPvName(device, screen['width']))
image_wid.readingOrder = image_wid.ReadingOrder.Clike
ss_vlay.addWidget(image_wid, 5)
if 'IMG' not in screen['data']:
ss_vlay.addLayout(self.setScrnInfo(device), 1)
group.setTitle(screen['title'] + " " + device)
group.setLayout(ss_vlay)
return group
[docs]
class LiBeamProfile(SiriusMainWindow, GeneralFunctions):
''' Linac Profile Screen '''
def __init__(self, prefix='', parent=None):
'''Contain all the graphic interface data'''
super().__init__(parent)
self.prefix = prefix + ('-' if prefix else '')
self.setObjectName('LIApp')
color = _util.get_appropriate_color('LI')
self.device_name = 'LA-BI'
self.window_title = "Linac Screen View"
self.setWindowIcon(qta.icon('mdi.camera-metering-center', color=color))
self.setWindowTitle(self.window_title)
self.image_container = QLabel()
self.pixmap = QPixmap(_os.path.join(
_os.path.abspath(_os.path.dirname(__file__)), "linac.png"))
self.selected_device = ''
self.stack_screens = QStackedWidget()
self.stack_screen = QStackedWidget()
self._setupUi()
[docs]
def radioBtnClick(self, value):
''' Action on radio button change '''
self.selected_device = DEVICES[value]
device_index = DEVICES.index(self.selected_device)
self.stack_screens.setCurrentIndex(device_index)
self.stack_screen.setCurrentIndex(device_index)
[docs]
def selectionItem(self, channel):
'''Build a selection widget'''
selector = enum_button.PyDMEnumButton(
init_channel=self.prefix + self.device_name+":"+channel)
self.active_screen = SiriusConnectionSignal(
self.prefix + self.device_name+":"+channel)
self.active_screen.new_value_signal[int].connect(
self.radioBtnClick)
selector.widgetType = 1
return selector
[docs]
def setPanelInfo(self, device, layout, row):
''' Build the information of one screen in screen panel '''
pv_list = SCREENS_PANEL.get('content')
pv_name = 'MOTOR' + ":"
count = 0
count += 1
for item in range(0, len(pv_list)):
if item == 0:
wid_type = 'label'
else:
wid_type = 'led'
layout.addWidget(
self.setWidgetType(
wid_type, device, pv_name + pv_list[item]),
row, count,
alignment=Qt.AlignCenter)
count += 1
layout.addWidget(
self.setWidgetType('led', device, pv_name + pv_list[0], value=1),
row, count,
alignment=Qt.AlignCenter)
return layout
[docs]
def setMotorsConfig(self):
''' Build the Motor Control Buttons'''
mc_hlay = QHBoxLayout()
for label, channel in HEADER.items():
mc_hlay.addWidget(
self.setWidgetType('pushBtn', "PRF:MOTOR", channel, label),
alignment=Qt.AlignCenter)
return mc_hlay
[docs]
def setScrnPanel(self):
''' Build the Screens Panel Component'''
group = QGroupBox()
am_glay = QGridLayout()
am_glay = self.setScrnHeader(am_glay)
row = 1
am_glay.addWidget(
self.selectionItem('PRF:OPI'),
row, 0, 5, 1,
alignment=Qt.AlignCenter)
for device in DEVICES:
am_glay = self.setPanelInfo(
device, am_glay, row)
row += 1
am_glay.addLayout(
self.setMotorsConfig(),
row, 0, 1, 4)
group.setTitle(SCREENS_PANEL.get('title'))
group.setLayout(am_glay)
return group
[docs]
def getScrnSelBtns(self, device, label):
''' Get the button label for the position of the screen '''
if isinstance(label, dict):
if device in label.keys():
return label[device]
return label["GEN"]
return label
[docs]
def setScrnSelection(self, selection_info, device):
''' Build the screen position selection menu '''
widget = QGroupBox()
ms_vlay = QVBoxLayout()
ms_vlay.addWidget(
SiriusLabel(
init_channel=self.getPvName(
device, 'MOTOR:' + selection_info.get('selected'))),
alignment=Qt.AlignCenter)
for pv_name, label in selection_info.get('content').items():
label = self.getScrnSelBtns(device, label)
if label:
ms_vlay.addWidget(
PyDMPushButton(
init_channel=self.getPvName(
device, 'MOTOR:' + pv_name),
label=label, pressValue=0))
widget.setLayout(ms_vlay)
widget.setTitle("Position")
return widget
[docs]
def setZeroOpt(self, item, device):
''' Build Zero Operation Component'''
zo_hlay = QHBoxLayout()
zo_hlay.addWidget(
QLabel(item["title"]),
alignment=Qt.AlignCenter)
count = 0
for label, pv_name in item["content"].items():
widget = self.setWidgetType(
'pushBtn', device, "MOTOR:" + pv_name, label)
if count == 1:
widget.setStyleSheet("background-color:#ffff00;")
zo_hlay.addWidget(
widget,
alignment=Qt.AlignCenter)
count += 1
return zo_hlay
[docs]
def setLight(self, device, pv_list, title):
''' Build Light Component '''
lo_hlay = QHBoxLayout()
lo_hlay.addWidget(
QLabel(title))
lo_hlay.addWidget(
self.setWidgetType('motorBtn', device, "MOTOR:" + pv_list[1]))
lo_hlay.addWidget(
self.setWidgetType('led', device, "MOTOR:" + pv_list[0]))
return lo_hlay
[docs]
def setSingleScrnInfo(self, device):
''' Build selected screen information '''
group = QGroupBox()
sm_glay = QGridLayout()
group.setTitle(SCREENS_INFO.get('title') + ' ' + device)
group.setLayout(sm_glay)
pos = [0, 0]
for label, channel in SCREENS_INFO.get('content').items():
sm_glay.addLayout(
self.setBasicInfo(device, label, 'MOTOR:' + channel),
pos[0], pos[1], 2, 2)
pos[0] += 2
sm_glay.addLayout(
self.setZeroOpt(
SCREENS_INFO.get('special_content')[1], device),
pos[0], pos[1], 1, 3)
btn = QPushButton("ROI View")
_util.connect_window(
btn, ROIViewWindow,
parent=self)
sm_glay.addWidget(btn, pos[0]+1, 0, 1, 3)
pos[0] = 0
sm_glay.addWidget(
self.setScrnSelection(
SCREENS_INFO.get('special_content')[0], device),
pos[0], pos[1] + 2, 10, 1)
return group
[docs]
def getStackItem(self, stack_type, device):
''' Get one stack item '''
if stack_type == 0:
return self.setSingleScrnInfo(device)
elif stack_type == 1:
return self.setSingleScrn(device, SCREEN['Screen_Camera'])
[docs]
def saveStack(self, stack, stack_type):
''' Save the stack for future item changes '''
if stack_type == 0:
stack.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Expanding)
self.stack_screens = stack
elif stack_type == 1:
self.stack_screen = stack
[docs]
def buildStacks(self, stack_type):
''' Build all the stack groups '''
stack = QStackedWidget()
for device in DEVICES:
stack.addWidget(
self.getStackItem(stack_type, device))
self.saveStack(stack, stack_type)
return stack
[docs]
def setScrnInfo(self, device):
''' Build the screen information Component'''
si_glay = QGridLayout()
counter = [0, 0]
for title, item in SCREEN['info'].items():
if title in ['Gain', 'Exposure']:
si_glay.addLayout(
self.setRBVObj(device, item, title, 'CAM:'),
counter[0], counter[1],
alignment=Qt.AlignHCenter)
elif title == 'LED':
si_glay.addLayout(
self.setLight(device, item, title),
counter[0], counter[1],
alignment=Qt.AlignHCenter)
elif item == "RESET.PROC":
si_glay.addWidget(
self.setWidgetType(
'pushBtn', device, 'CAM:' + item, title),
counter[0], counter[1],
alignment=Qt.AlignHCenter)
else:
si_glay.addLayout(
self.setBasicInfo(device, title, item),
counter[0], counter[1],
alignment=Qt.AlignHCenter)
counter[0] += 1
if counter[0] >= 2:
si_glay.setColumnStretch(counter[1], 30)
counter[1] += 1
counter[0] = 0
return si_glay
[docs]
def imageViewer(self):
''' Build the image'''
self.image_container.setPixmap(self.pixmap)
self.image_container.setScaledContents(True)
self.image_container.setSizePolicy(
QSizePolicy.Ignored, QSizePolicy.Ignored)
return self.image_container
def _setupUi(self):
'''Build the graphic interface'''
wid = QWidget(self)
if_glay = QGridLayout()
if_glay.addLayout(self.header(), 0, 0, 1, 10)
if_glay.addWidget(self.imageViewer(), 1, 1, 4, 10)
if_glay.addWidget(self.setScrnPanel(), 1, 0, 7, 1)
if_glay.addWidget(self.buildStacks(0), 8, 0, 7, 1)
if_glay.addWidget(self.buildStacks(1), 5, 1, 10, 10)
wid.setLayout(if_glay)
self.setCentralWidget(wid)
[docs]
class ROIViewWindow(SiriusMainWindow, GeneralFunctions):
"""Show the ROI View Window."""
def __init__(self, parent=None, prefix=''):
"""Init."""
super().__init__(parent)
self.device_name = 'LA-BI'
self.setObjectName('LIApp')
self.setWindowTitle('ROI View')
self.active_screen = SiriusConnectionSignal(
prefix + self.device_name+":PRF:OPI")
self.active_screen.new_value_signal[int].connect(
self.radioBtnClick)
self.selected_device = 0
self.stack_graphs = QStackedWidget()
self.stack_screen = QStackedWidget()
self.connect_sign = dict()
self.curves = dict()
self.roi_screens = dict()
self._setupUi()
[docs]
def setGraphInfo(self, device, graph_info):
''' Build the basic graph information '''
wid = QWidget()
gi_hlay = QHBoxLayout()
wid.setLayout(gi_hlay)
gi_hlay.addStretch()
for label, channel in graph_info.items():
gi_hlay.addLayout(
self.setBasicInfo(device, label, channel))
gi_hlay.addStretch()
return wid
[docs]
def setRoiInfo(self, device, roi_data, title):
''' Build the ROI information '''
group = QGroupBox()
ri_glay = QGridLayout()
counter = [0, 0]
col_span = 2
for label, channel in roi_data.items():
ri_glay.addLayout(
self.setRBVObj(device, channel, label, 'ROI:'),
counter[0], counter[1], 1, col_span)
if counter[0] == 2:
counter[0] = 0
counter[1] = 1
counter[0] += 1
col_span = 1
group.setLayout(ri_glay)
group.setTitle(title)
return group
[docs]
def radioBtnClick(self, value):
''' Action on radio button change '''
self.selected_device = DEVICES[value]
device_index = DEVICES.index(self.selected_device)
self.stack_graphs.setCurrentIndex(device_index)
self.stack_screen.setCurrentIndex(device_index)
[docs]
def rotate_chart(self, x_array):
''' Rotate X and Y Axis in the Chart '''
pvname = self.sender().address
device = pvname.split(':')[1]
curvename = 'data' if 'Gauss:Data' in pvname else 'centroid'
curve = self.curves[device][curvename]
size = len(x_array)
curve.receiveXWaveform(x_array)
curve.receiveYWaveform(_np.linspace(0, size, num=size))
[docs]
def setGraph(self, device, graph_data, orientation='V'):
'''Build a graph widget'''
self.connect_sign[device] = dict()
self.curves[device] = dict()
graph_plot = SiriusWaveformPlot(background="#ffffff")
pvname = self.getPvName(device, graph_data['channel']['centroid']) \
if orientation != 'V' else 'Fake'
graph_plot.addChannel(
y_channel=pvname,
color="#ff8b98",
lineWidth=1,
symbol='o',
symbolSize=5)
pvname = self.getPvName(device, graph_data['channel']['data']) \
if orientation != 'V' else 'Fake'
graph_plot.addChannel(
y_channel=pvname,
color="#ff0000",
lineWidth=1,
symbol='o',
symbolSize=5)
if orientation == 'V':
self.curves[device]['centroid'] = graph_plot.curveAtIndex(0)
self.connect_sign[device]['centroid'] = SiriusConnectionSignal(
self.getPvName(
device, graph_data['channel']['centroid']))
self.connect_sign[device]['centroid'].new_value_signal[_np.ndarray].connect(
self.rotate_chart)
self.curves[device]['data'] = graph_plot.curveAtIndex(1)
self.connect_sign[device]['data'] = SiriusConnectionSignal(
self.getPvName(
device, graph_data['channel']['data']))
self.connect_sign[device]['data'].new_value_signal[_np.ndarray].connect(
self.rotate_chart)
graph_plot.setMaximumWidth(400)
else:
graph_plot.setMaximumHeight(300)
return graph_plot
[docs]
def scrnAndProj(self, device):
''' Display ROI Screen, the projection charts
and the projection information '''
group = QGroupBox()
lay = QGridLayout()
group.setLayout(lay)
self.roi_screens[device] = self.setSingleScrn(
device, SCREEN['Screen_ROI'])
lay.addWidget(
self.roi_screens[device], 0, 0, 3, 3)
lay.addWidget(
self.setGraph(device, GRAPH["Horizontal"], 'H'),
3, 0, 1, 3)
lay.addWidget(
self.setGraph(device, GRAPH["Vertical"], 'V'),
0, 3, 1, 1)
info_group = QGroupBox()
lay_info = QVBoxLayout()
info_group.setLayout(lay_info)
info_group.setTitle("Projections Info")
for title in ["Horizontal", "Vertical"]:
lay_info.addWidget(
QLabel(title), 1, alignment=Qt.AlignHCenter)
lay_info.addWidget(
self.setGraphInfo(device, GRAPH[title]['info']),
1, alignment=Qt.AlignTop)
lay.addWidget(info_group, 3, 3, 1, 1)
return group
[docs]
def getStackItem(self, stack_type, device):
''' Get one stack item '''
if stack_type == 0:
return self.scrnAndProj(device)
elif stack_type == 1:
return self.setRoiInfo(device, GRAPH["ROI"], "ROI")
[docs]
def saveStack(self, stack, stack_type):
''' Save the stack for future item changes '''
if stack_type == 0:
self.stack_screen = stack
elif stack_type == 1:
self.stack_graphs = stack
[docs]
def buildStacks(self, stack_type):
''' Build all the stack groups '''
stack = QStackedWidget()
for device in DEVICES:
stack.addWidget(
self.getStackItem(stack_type, device))
self.saveStack(stack, stack_type)
return stack
def _setupUi(self):
"""Build the UI."""
wid = QWidget(self)
self.setCentralWidget(wid)
lay = QGridLayout()
wid.setLayout(lay)
lay.addWidget(self.buildStacks(0))
lay.addWidget(self.buildStacks(1))