""" 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)