459 lines
17 KiB
Python
459 lines
17 KiB
Python
""" Gui header for SwissFEL
|
|
"""
|
|
from datetime import datetime
|
|
import random
|
|
|
|
from qtpy.QtCore import Qt, QTimer
|
|
from qtpy.QtGui import QFont
|
|
from qtpy.QtWidgets import (QApplication, QGridLayout, QGroupBox, QHBoxLayout,
|
|
QLabel, QRadioButton, QWidget)
|
|
from caqtwidgets.pvwidgets import CAQLabel, QHLine, QVLine
|
|
|
|
from pyqtacc.bdbase.enumkind import UserMode
|
|
|
|
class GUIHeader(QWidget):
|
|
""" GUI Header Class
|
|
"""
|
|
def __init__(self, parent, user_mode=UserMode.OPERATION, extended=True):
|
|
super(GUIHeader, self).__init__()
|
|
self.parent = parent
|
|
self.appname = parent.appname
|
|
self.title = parent.title
|
|
self.settings = parent.settings
|
|
self.user_mode = user_mode
|
|
self.current_user_mode_name = user_mode.name
|
|
self.extended = extended
|
|
self.cafe = parent.cafe
|
|
self.cyca = parent.cyca
|
|
|
|
self.input_parameters = parent.input_parameters
|
|
self.input_labels = parent.input_labels
|
|
self.expert_parameters = parent.expert_parameters
|
|
self.expert_labels = parent.expert_labels
|
|
|
|
self.aramis_status = None
|
|
self.athos_status = None
|
|
self.porthos_status = None
|
|
self.show_porthos = False
|
|
self.porthos_text = "Honest and extrovert!" \
|
|
if random.randint(1, 10) > 9 else "Arriving 2032"
|
|
|
|
self.font_gui = QFont("sans serif")
|
|
self.font_gui.setPointSize(11)
|
|
self.font_pts10 = QFont("sans serif")
|
|
self.font_pts10.setPointSize(10)
|
|
self.widget_height = self.settings.data["StyleGuide"]["widgetHeight"]
|
|
self.extra_height = self.settings.data["StyleGuide"]["extraGroupHeight"]
|
|
self.machine_color = self.settings.data['Machine']['color']
|
|
self.aramis_color = self.settings.data["Aramis"]["color"]
|
|
self.athos_color = self.settings.data["Athos"]["color"]
|
|
self.porthos_color = self.settings.data["Porthos"]["color"]
|
|
self.aramis_grad = self.settings.data['Aramis']['qlingrad']
|
|
self.athos_grad = self.settings.data['Athos']['qlingrad']
|
|
self.porthos_grad = self.settings.data['Porthos']['qlingrad']
|
|
self.machine_grad = self.settings.data['Machine']['qlingrad']
|
|
|
|
beamline = ["Aramis", "Athos", "Porthos"]
|
|
pvlist = []
|
|
for item in beamline:
|
|
if item not in self.settings.data:
|
|
continue
|
|
try:
|
|
pvlist.append(self.settings.data[item]["laser"])
|
|
pvlist.append(self.settings.data[item]["freq"])
|
|
pvlist.append(self.settings.data[item]["beamStatus"])
|
|
pvlist.append(self.settings.data[item]["energy"])
|
|
pvlist.append(self.settings.data[item]["charge"])
|
|
pvlist.append(self.settings.data[item]["photonEnergy"])
|
|
except KeyError as error:
|
|
print("KeyError in guiheader.py __init__", str(error))
|
|
|
|
for pv in self.settings.data["OpMsg"].values():
|
|
pvlist.append(pv)
|
|
|
|
|
|
self.cafe.openPrepare()
|
|
self.cafe.open(pvlist)
|
|
self.cafe.openNowAndWait(0.4)
|
|
|
|
#self.cafe.printDisconnected()
|
|
|
|
self.station_width = 200 #default
|
|
self.station_height = 100
|
|
self.hor_layout = QHBoxLayout()
|
|
self.hor_layout.addWidget(self.status_widget())
|
|
self.hor_layout.addWidget(self.aramis_widget())
|
|
self.hor_layout.addWidget(self.athos_widget())
|
|
yr_diff = abs(2032 - datetime.now().year)
|
|
show_porthos = False
|
|
try:
|
|
if self.settings.data["showPorthos"]:
|
|
self.show_porthos = True
|
|
else:
|
|
if random.randint(2, 12) > yr_diff:
|
|
self.show_porthos = True
|
|
except KeyError as error:
|
|
print("KeyError in guiheader.py initialization:", str(error))
|
|
if random.randint(2, 12) > yr_diff:
|
|
self.show_porthos = True
|
|
if self.show_porthos:
|
|
self.hor_layout.addWidget(self.porthos_widget())
|
|
self.hor_layout.setSpacing(10)
|
|
self.hor_layout.setAlignment(Qt.AlignLeft)
|
|
self.hor_layout.setContentsMargins(5, 0, 5, 0)
|
|
|
|
self.header_wgt = QGroupBox()
|
|
self.header_wgt.setObjectName(self.user_mode.name)
|
|
self.header_wgt.setLayout(self.hor_layout)
|
|
self.header_wgt.setFixedHeight(110)
|
|
title = "SwissFEL {0}".format(self.user_mode.name)
|
|
if self.title:
|
|
title += ": {0}".format(self.title)
|
|
|
|
self.header_wgt.setTitle(title)
|
|
|
|
self.target_beamline = None
|
|
|
|
self.timer = QTimer()
|
|
self.timer.timeout.connect(self.blink_target_beamline)
|
|
self.timer.start(500)
|
|
self.timer_count = 0
|
|
|
|
def blink_target_beamline(self):
|
|
self.timer_count += 1
|
|
if self.timer_count < 2:
|
|
return
|
|
if self.timer_count % 2 == 0:
|
|
self.reset_beamline()
|
|
else:
|
|
self.set_target_beamline(target=self.target_beamline, reset=False)
|
|
if self.timer_count > 4:
|
|
self.timer.stop()
|
|
QApplication.processEvents()
|
|
|
|
def set_target_beamline(self, target, reset=True):
|
|
""" Select beamline target and modify color scheme accordinly
|
|
"""
|
|
if not target:
|
|
return
|
|
if target == "Aramis":
|
|
self.aramis_status.setObjectName("TARGET")
|
|
self.aramis_status.setTitle("Target: Aramis")
|
|
self.aramis_status.style().polish(self.aramis_status)
|
|
elif target == "Athos":
|
|
self.athos_status.setObjectName("TARGET")
|
|
self.athos_status.setTitle("Target: Athos")
|
|
self.athos_status.style().polish(self.athos_status)
|
|
elif target == "Porthos" and self.porthos_status:
|
|
if self.porthos_status:
|
|
self.porthos_status.setObjectName("TARGET")
|
|
self.porthos_status.setTitle("Target: Porthos")
|
|
self.porthos_status.style().polish(self.porthos_status)
|
|
|
|
if reset:
|
|
self.reset_beamline()
|
|
self.target_beamline = target
|
|
|
|
def reset_beamline(self):
|
|
""" Reset QGroupBox for previous target to original colors
|
|
"""
|
|
if self.target_beamline == "Aramis":
|
|
self.reset_aramis()
|
|
elif self.target_beamline == "Athos":
|
|
self.reset_athos()
|
|
elif self.target_beamline == "Porthos":
|
|
self.reset_porthos()
|
|
|
|
def reset_aramis(self):
|
|
""" Reset Aramis QGroupBox color
|
|
"""
|
|
self.aramis_status.setObjectName("ARAMIS")
|
|
self.aramis_status.setTitle("Aramis")
|
|
self.aramis_status.style().polish(self.aramis_status)
|
|
|
|
def reset_athos(self):
|
|
""" Reset Athos QGroupBox color
|
|
"""
|
|
self.athos_status.setObjectName("ATHOS")
|
|
self.athos_status.setTitle("Athos")
|
|
self.athos_status.style().polish(self.athos_status)
|
|
|
|
def reset_porthos(self):
|
|
""" Reset Porthos QGroupBox color
|
|
"""
|
|
if self.porthos_status:
|
|
self.porthos_status.setObjectName("PORTHOS")
|
|
self.porthos_status.setTitle("Porthos")
|
|
self.porthos_status.style().polish(self.porthos_status)
|
|
|
|
def reset_operation_mode(self):
|
|
""" Reset header colors to application operation mode
|
|
"""
|
|
self.change_operation_mode(user_mode=self.user_mode)
|
|
|
|
def change_operation_mode(self, user_mode=UserMode.OPERATION):
|
|
""" Different operation modes have different color schemes
|
|
"""
|
|
title_name = user_mode.name
|
|
dry_run_tags = ['Emittance', 'Dry']
|
|
if any([x in self.header_wgt.title() for x in dry_run_tags]):
|
|
title_name = 'DRY RUN' if user_mode.name == 'SIMULATION' \
|
|
else user_mode.name
|
|
self.header_wgt.setObjectName(user_mode.name)
|
|
self.header_wgt.setTitle(self.header_wgt.title().replace(
|
|
self.current_user_mode_name, title_name))
|
|
self.header_wgt.style().polish(self.header_wgt)
|
|
self.current_user_mode_name = title_name
|
|
|
|
|
|
def beamline_widget(self, beamline="Aramis"):
|
|
""" QGroupBox template for beamlines
|
|
"""
|
|
station = QGroupBox()
|
|
station.setObjectName(beamline.upper())
|
|
station.setAlignment(Qt.AlignHCenter | Qt.AlignTop)
|
|
station.setFlat(False)
|
|
station.setTitle(beamline)
|
|
laser = CAQLabel(self, pv_name=self.settings.data[beamline]["laser"])
|
|
laser.setFixedHeight(self.widget_height)
|
|
freq = CAQLabel(self, pv_name=self.settings.data[beamline]["freq"],
|
|
show_units=True)
|
|
freq.setAlignment(Qt.AlignRight)
|
|
freq.setFixedHeight(self.widget_height)
|
|
freq.setFixedWidth(84)
|
|
beamstatus = CAQLabel(
|
|
self, pv_name=self.settings.data[beamline]["beamStatus"],
|
|
color_mode="alarm")
|
|
beamstatus.setFixedHeight(self.widget_height)
|
|
|
|
grid_layout = QGridLayout()
|
|
grid_layout.addWidget(laser, 0, 0, 1, 1, Qt.AlignCenter)
|
|
grid_layout.addWidget(freq, 0, 1, 1, 1, Qt.AlignCenter)
|
|
grid_layout.addWidget(beamstatus, 1, 0, 1, 2, Qt.AlignCenter)
|
|
|
|
if self.extended:
|
|
grid_layout.addWidget(QVLine(), 0, 2, 2, 1)
|
|
filtered_energy = CAQLabel(
|
|
self, pv_name=self.settings.data[beamline]["energy"],
|
|
show_units=True)
|
|
filtered_energy.setFixedHeight(self.widget_height)
|
|
charge = CAQLabel(
|
|
self, pv_name=self.settings.data[beamline]["charge"],
|
|
show_units=True, notify_freq_hz=2)
|
|
charge.setFixedHeight(self.widget_height)
|
|
charge.setFixedWidth(78)
|
|
photon_energy = CAQLabel(
|
|
self, pv_name=self.settings.data[beamline]["photonEnergy"],
|
|
show_units=False, suffix='\u00B5J')
|
|
photon_energy.setFixedHeight(self.widget_height)
|
|
photon_energy.setFixedWidth(66) #So aramis/Athos are the same
|
|
|
|
grid_layout.addWidget(filtered_energy, 0, 3, 1, 2, Qt.AlignCenter)
|
|
grid_layout.addWidget(charge, 1, 3, 1, 1, Qt.AlignCenter)
|
|
grid_layout.addWidget(photon_energy, 1, 4, 1, 1, Qt.AlignCenter)
|
|
|
|
grid_layout.setVerticalSpacing(0)
|
|
grid_layout.setHorizontalSpacing(0)
|
|
grid_layout.setContentsMargins(0, 10, 0, 0)
|
|
#print(grid_layout.getContentsMargins())
|
|
|
|
station.setLayout(grid_layout)
|
|
self.station_height = laser.height() + freq.height() + self.extra_height
|
|
station.setFixedHeight(self.station_height)
|
|
self.station_width = laser.width() + freq.width() + 10
|
|
if self.extended:
|
|
self.station_width += (charge.width() + photon_energy.width())
|
|
station.setFixedWidth(self.station_width)
|
|
station.setAlignment(Qt.AlignCenter)
|
|
return station
|
|
|
|
def aramis_widget(self):
|
|
""" QGroupBox encompassing main Aramis parameters
|
|
"""
|
|
self.aramis_status = self.beamline_widget(beamline="Aramis")
|
|
return self.aramis_status
|
|
|
|
def athos_widget(self):
|
|
""" QGroupBox encompassing main Athos parameters
|
|
"""
|
|
self.athos_status = self.beamline_widget(beamline="Athos")
|
|
return self.athos_status
|
|
|
|
def porthos_widget(self):
|
|
""" QGroupBox encompassing main Porthos parameters
|
|
"""
|
|
#porthos = self.beamline_widget(beamline="Porthos")
|
|
station = QGroupBox()
|
|
station.setObjectName("Porthos".upper())
|
|
station.setAlignment(Qt.AlignHCenter | Qt.AlignTop)
|
|
station.setFlat(False)
|
|
station.setTitle("Porthos")
|
|
grid_layout = QGridLayout()
|
|
qlabel = QLabel(self.porthos_text)
|
|
qlabel.setFont(self.font_pts10)
|
|
qlabel.setAlignment(Qt.AlignCenter)
|
|
grid_layout.addWidget(qlabel, 0, 0)
|
|
station.setLayout(grid_layout)
|
|
station.setFixedWidth(self.station_width)
|
|
station.setFixedHeight(self.station_height)
|
|
self.porthos_status = station
|
|
return self.porthos_status
|
|
|
|
def status_widget(self):
|
|
""" QGroupBox encompassing machine status info
|
|
"""
|
|
station = QGroupBox()
|
|
station.setObjectName("MACHINE")
|
|
station.setAlignment(Qt.AlignHCenter | Qt.AlignTop)
|
|
station.setFlat(False)
|
|
station.setTitle("Status")
|
|
date = CAQLabel(self, pv_name=self.settings.data["OpMsg"]["pvDate1"])
|
|
date.setFixedHeight(self.widget_height)
|
|
eventno = CAQLabel(
|
|
self, pv_name=self.settings.data["OpMsg"]["pvEventNo"],
|
|
notify_freq_hz=10)
|
|
eventno.setFixedHeight(self.widget_height)
|
|
eventno.setAlignment(Qt.AlignCenter)
|
|
message = CAQLabel(
|
|
self, pv_name=self.settings.data["OpMsg"]["pvMsg1"])
|
|
message.setFixedHeight(self.widget_height)
|
|
message.setFixedWidth(eventno.width() + date.width() + 2)
|
|
|
|
grid_layout = QGridLayout()
|
|
grid_layout.addWidget(date, 0, 0, 1, 1, Qt.AlignCenter)
|
|
grid_layout.addWidget(eventno, 0, 1, 1, 1, Qt.AlignCenter)
|
|
grid_layout.addWidget(message, 1, 0, 1, 2, Qt.AlignCenter)
|
|
grid_layout.setVerticalSpacing(0)
|
|
grid_layout.setHorizontalSpacing(0)
|
|
grid_layout.setContentsMargins(0, 10, 0, 0)
|
|
|
|
station.setLayout(grid_layout)
|
|
station.setFixedHeight(eventno.height() + date.height() +
|
|
self.extra_height)
|
|
station.setFixedWidth(eventno.width() + date.width() + 10)
|
|
self.machine_status = station
|
|
return self.machine_status
|
|
|
|
def get_location_color(self, title: str):
|
|
"""Determines text color for the various location points
|
|
|
|
Args:
|
|
title: String indicating location point for which the text
|
|
color is to be determined
|
|
|
|
Returns:
|
|
string in html color
|
|
"""
|
|
text_color = self.machine_color
|
|
aramis_matches = ["AR", "LINAC"]
|
|
|
|
if any([x in title for x in aramis_matches]):
|
|
text_color = self.aramis_color
|
|
# if "AR" in title:
|
|
# text_color = self.aramis_color
|
|
# elif "LINAC" in title:
|
|
# text_color = self.aramis_color
|
|
elif "AT" in title:
|
|
text_color = self.athos_color
|
|
elif "PO" in title:
|
|
text_color = self.porthos_color
|
|
return text_color
|
|
|
|
|
|
def get_location_gradient_color(self, title: str):
|
|
"""Determines bg gradient color for the various location points
|
|
|
|
Args:
|
|
title: String indicating location point for which the bg
|
|
gradient color is to be determined
|
|
|
|
Returns:
|
|
string in html color
|
|
"""
|
|
text_color = self.machine_grad
|
|
|
|
aramis_matches = ["AR", "LINAC"]
|
|
|
|
if any([x in title for x in aramis_matches]):
|
|
text_color = self.aramis_grad
|
|
# if "AR" in title:
|
|
# text_color = self.aramis_grad
|
|
# elif "LINAC" in title:
|
|
# text_color = self.aramis_grad
|
|
elif "AT" in title:
|
|
text_color = self.athos_grad
|
|
elif "PO" in title:
|
|
text_color = self.porthos_grad
|
|
return text_color
|
|
|
|
def operator_group_header(self):
|
|
return self.radio_target_beamline()
|
|
|
|
def radio_target_beamline(self):
|
|
widget = QWidget()
|
|
layout = QGridLayout()
|
|
layout.setContentsMargins(0, 0, 0, 0)
|
|
self.setLayout(layout)
|
|
target_list = ["Aramis", "Athos"]
|
|
self.radiobutton = [QRadioButton("Aramis"), QRadioButton("Athos")]
|
|
try:
|
|
if self.show_porthos:
|
|
target_list.append("Porthos")
|
|
self.radiobutton.append(QRadioButton("Porthos"))
|
|
except KeyError as error:
|
|
print("KeyError in guiheader.py, def radio_target_beamline:",
|
|
str(error))
|
|
|
|
color_list = [self.aramis_color, self.athos_color, self.porthos_color]
|
|
|
|
width_list = [70, 64, 84]
|
|
#width_list = [80, 74, 92] #pts 11
|
|
layout.addWidget(QHLine(), 0, 0, 1, 3)
|
|
|
|
for i, (radio, target, color, width) in enumerate(
|
|
zip(self.radiobutton, target_list, color_list, width_list)):
|
|
#radio = QRadioButton(target)
|
|
radio.setFont(self.font_pts10)
|
|
radio.setStyleSheet("color : {0};".format(color))
|
|
radio.target = target
|
|
radio.toggled.connect(self.on_target_clicked)
|
|
layout.addWidget(radio, 1, i)
|
|
radio.setFixedWidth(width)
|
|
|
|
try:
|
|
target = self.settings.data["Parameters"]["undulator"]["data"][
|
|
"value"]
|
|
except KeyError as error:
|
|
print("KeyError in guiheader.py, def radio_target_beamline:",
|
|
str(error))
|
|
target = target_list[0]
|
|
try:
|
|
idx = target_list.index(target)
|
|
except ValueError as error:
|
|
print("ValueError in guiheader.py, def radio_target_beamline:",
|
|
str(error))
|
|
idx = 0
|
|
|
|
self.radiobutton[idx].setChecked(True)
|
|
self.radiobutton[idx].toggled.emit(True)
|
|
|
|
layout.addWidget(QHLine(), 2, 0, 1, 3)
|
|
|
|
widget.setLayout(layout)
|
|
|
|
return widget
|
|
|
|
def on_target_clicked(self):
|
|
radio_button = self.sender()
|
|
if radio_button.isChecked():
|
|
print("Target is {0}".format(radio_button.target))
|
|
self.set_target_beamline(radio_button.target)
|
|
radio_button.setText(radio_button.target.upper())
|
|
self.timer.start(500)
|
|
self.timer_count = 0
|
|
self.parent.input_parameters['undulator'] = radio_button.target
|
|
|
|
for radio in self.radiobutton:
|
|
if not radio.isChecked():
|
|
radio.setText(radio.target)
|