Files
sf/guiheader.py
2024-02-08 14:16:00 +01:00

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)