""" Gui header for PROSCAN """ from qtpy.QtCore import Qt, QTimer from qtpy.QtGui import QFont from qtpy.QtWidgets import ( QApplication, QFrame, QGridLayout, QGroupBox, QHBoxLayout, QLabel, QProxyStyle, QVBoxLayout, QWidget) from caqtwidgets.pvwidgets import CAQLabel 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.header_groups = self.settings.data['header'] self.header_status = [None] * len(self.header_groups) self.header_row = {"COMET": 1, "G1": 2, "SH": 1, "G2": 2, "O2": 1, "PIF": 0, "G3": 0, "MASTER": 1} #self.header_row = {"COMET": 0, "G1": 0, "SH": 0, "G2": 0, "O2": 0, # "PIF": 0, "G3": 0, "MASTER": 0} 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"] pvlist = [] header_colors = [] for sector in self.header_groups: pvlist.append(self.settings.data[sector]["status"]) header_colors.append(self.settings.data[sector]["colorObj"]) 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.grid_layout = QGridLayout() self.beam_current_wgt_dict = {} self.beam_current_wgt_group = {} has_hbox_layout = False self.pframe_matrix = {"POST-COMET": {}, "G1": {}, "PRE-SH": {}, "POST-SH": {}, "G2": {}, "PRE-O2": {}, "O2": {}, "PRE-PIF": {}, "PRE-G3": {}} self.op_color = self.settings.data['StyleGuide']['bgTitleOperation'] self.sim_color = self.settings.data['StyleGuide']['bgTitleSimulation'] self.pf_off_color = self.sim_color if self.settings.data['Expert'][ 'simulation']['data']['value'] else self.op_color def pframe(color="green"): qframe = QFrame() qframe.setStyleSheet("background-color: {0};".format(color)) qframe.setFixedWidth(6) qframe.setFixedHeight(6) return qframe def draw_box(nheight: int = 10, nwidth: int = 8, area: str = None): if area is None: return qw = QWidget() qgrid = QGridLayout() for i in range(0, nheight): self.pframe_matrix[area][i] = {} for j in range(0, nwidth): self.pframe_matrix[area][i][j] = {} pf = pframe(self.pf_off_color) qgrid.addWidget(pf, i, j, 1, 1, Qt.AlignCenter) self.pframe_matrix[area][i][j] = pf qgrid.setSpacing(1) qgrid.setContentsMargins(0, 0, 0, 0) qgrid.setAlignment(Qt.AlignCenter) qw.setLayout(qgrid) return qw if has_hbox_layout: for sector, pv, color in zip(self.header_groups, pvlist, header_colors): if sector == "MASTER": qframe = QFrame() qframe.setFixedWidth(200) self.hor_layout.addWidget(qframe) self.hor_layout.addWidget(self.beam_current_widget( header=sector, pv= pv, color_obj=color)) self.hor_layout.setSpacing(6) self.hor_layout.setAlignment(Qt.AlignLeft) self.hor_layout.setContentsMargins(5, 0, 5, 0) else: j = 0 _align = Qt.AlignCenter for (sector, pv, color) in ( zip(self.header_groups, pvlist, header_colors)): if sector == "MASTER": qframe = QFrame() qframe.setFixedWidth(100) self.grid_layout.addWidget(qframe, self.header_row[sector], j, 2, 1) j += 1 self.grid_layout.addWidget(self.beam_current_widget( header=sector, pv= pv, color_obj=color), self.header_row[sector], j, 2, 1) j += 1 if sector in ["PIF"]: qw = draw_box(nheight=10, nwidth=8, area="PRE-G3") self.grid_layout.addWidget(qw, 0, j, 2, 1, _align) j += 1 elif sector in ["O2"]: qw = draw_box(nheight=5, nwidth=16, area="PRE-PIF") self.grid_layout.addWidget(qw, 0, j-1, 1, 1, _align) qw = draw_box(nheight=10, nwidth=8, area="O2") self.grid_layout.addWidget(qw, 0, j, 2, 1, _align) j += 1 elif sector in ["COMET"]: qw = draw_box(nheight=10, nwidth=10, area="POST-COMET") self.grid_layout.addWidget(qw, 1, j, 2, 1, _align) j += 1 elif sector in ["SH"]: qw = draw_box(nheight=15, nwidth=10, area="POST-SH") self.grid_layout.addWidget(qw, 0, j, 3, 1, _align) j += 1 elif sector in ["G1"]: qw = draw_box(nheight=5, nwidth=16, area="G1") self.grid_layout.addWidget(qw, 1, j-1, 1, 1, _align) qw = draw_box(nheight=10, nwidth=8, area="PRE-SH") self.grid_layout.addWidget(qw, 1, j, 2, 1, _align) j += 1 elif sector in ["G2"]: qw = draw_box(nheight=10, nwidth=16, area="G2") self.grid_layout.addWidget(qw, 0, j-1, 2, 1, _align) qw = draw_box(nheight=10, nwidth=8, area="PRE-O2") self.grid_layout.addWidget(qw, 0, j, 2, 1, _align) j += 1 elif sector not in ["MASTER", "G3"]: qw = draw_box() self.grid_layout.addWidget(qw, 0, j, 2, 1, _align) j += 1 shift_type = CAQLabel(self, pv_name="PRO-HUSH:SHIFT-TYPE") self.grid_layout.addWidget(shift_type, 0, 0, 1, 2, Qt.AlignLeft) if has_hbox_layout: self.grid_layout.setSpacing(6) else: self.grid_layout.setSpacing(0) self.grid_layout.setAlignment(Qt.AlignLeft) self.grid_layout.setContentsMargins(5, 0, 5, 0) self.header_wgt = QGroupBox() self.header_wgt.setObjectName(self.user_mode.name) if has_hbox_layout: self.header_wgt.setLayout(self.hor_layout) self.header_wgt.setFixedHeight(110) else: self.header_wgt.setLayout(self.grid_layout) self.header_wgt.setFixedHeight(160) #self.header_wgt.setStyle(QProxyStyle()) title = "PROSCAN {0}".format(self.user_mode.name) if self.title: title += ": {0}".format(self.title) self.header_wgt.setTitle(title) #print(self.pframe_matrix) self.target_beamline = None self.timer = QTimer() #self.timer.timeout.connect(lambda: self.blink_target_beamline(None)) self.timer.timeout.connect(self.blink_target_beamline) self.timer.start(500) self.timer_count = 0 self.master_to_wgt = {"Gantry 1": "G1", "Gantry 2": "G2", "Gantry 3": "G3", "OPTIS2": "O2"} self.toSH("OFF") self.toG3("OFF") self.toPIF("OFF") self.toO2("OFF") self.toG2("OFF") self.toG1("OFF") def color_pframe(self, color='#cccccc'): for section in self.pframe_matrix.keys(): #print(section, flush=True) for key, value in self.pframe_matrix[section].items(): #print(key, value, "\n") for key2, pf in value.items(): #print("===>", key2, pf, "\n") ss = pf.styleSheet() aa = ss[ss.index('#'):ss.index(';')] #print(aa) if "green" in aa or self.pf_bgcolor() in aa: #print("PASS", ss, self.pf_bgcolor(), flush=True) pass else: pf.setStyleSheet("background-color:{0};".format(color)) def pf_bgcolor(self): #self.pf_off_color = self.sim_color if self.input_parameters( # 'simulation') else self.op_color return '#cccccc' def toG3(self, on_off: str = "ON"): #self.toPIF(on_off) _color = "green" if on_off=="ON" else self.pf_bgcolor() for key, pf in self.pframe_matrix["PRE-G3"][3].items(): pf.setStyleSheet("background-color:{0};".format(_color)) def toSH(self, on_off: str = "ON"): _color = "green" if on_off=="ON" else self.pf_bgcolor() for key, pf in self.pframe_matrix["POST-COMET"][3].items(): pf.setStyleSheet("background-color:{0};".format(_color)) for key, pf in self.pframe_matrix["G1"][3].items(): pf.setStyleSheet("background-color:{0};".format(_color)) for key, pf in self.pframe_matrix["PRE-SH"][3].items(): pf.setStyleSheet("background-color:{0};".format(_color)) def toCometOverlap(self, on_off: str = "ON"): _color = "green" if on_off=="ON" else self.pf_bgcolor() pf = self.pframe_matrix["POST-COMET"][3][0] pf.setStyleSheet("background-color:{0};".format(_color)) pf = self.pframe_matrix["POST-COMET"][3][1] pf.setStyleSheet("background-color:{0};".format(_color)) def toSHOverlap(self, on_off: str = "ON"): _color = "green" if on_off=="ON" else self.pf_bgcolor() pf = self.pframe_matrix["POST-COMET"][3][0] pf.setStyleSheet("background-color:{0};".format(_color)) pf = self.pframe_matrix["POST-COMET"][3][1] pf.setStyleSheet("background-color:{0};".format(_color)) pf = self.pframe_matrix["POST-SH"][8][0] pf.setStyleSheet("background-color:{0};".format(_color)) pf = self.pframe_matrix["POST-SH"][8][1] pf.setStyleSheet("background-color:{0};".format(_color)) def toPIF(self, on_off: str = "ON"): #self.toSH(on_off) _color = "green" if on_off=="ON" else self.pf_bgcolor() pf = self.pframe_matrix["POST-SH"][8][0] pf.setStyleSheet("background-color:{0};".format(_color)) for i in range(0, 5): pf = self.pframe_matrix["POST-SH"][8-i][i+1] pf.setStyleSheet("background-color:{0};".format(_color)) for i in range(6, 10): pf = self.pframe_matrix["POST-SH"][3][i] pf.setStyleSheet("background-color:{0};".format(_color)) for key, pf in self.pframe_matrix["G2"][3].items(): pf.setStyleSheet("background-color:{0};".format(_color)) for key, pf in self.pframe_matrix["PRE-O2"][3].items(): pf.setStyleSheet("background-color:{0};".format(_color)) for key, pf in self.pframe_matrix["O2"][3].items(): pf.setStyleSheet("background-color:{0};".format(_color)) for key, pf in self.pframe_matrix["PRE-PIF"][3].items(): pf.setStyleSheet("background-color:{0};".format(_color)) def toO2(self, on_off: str = "ON"): _color = "green" if on_off=="ON" else self.pf_bgcolor() #pf = self.pframe_matrix["POST-COMET"][3][0] #pf.setStyleSheet("background-color:{0};".format(_color)) pf = self.pframe_matrix["POST-SH"][8][0] pf.setStyleSheet("background-color:{0};".format(_color)) for key, pf in self.pframe_matrix["POST-SH"][8].items(): pf.setStyleSheet("background-color:{0};".format(_color)) for key, pf in self.pframe_matrix["G2"][8].items(): pf.setStyleSheet("background-color:{0};".format(_color)) for key, pf in self.pframe_matrix["PRE-O2"][8].items(): pf.setStyleSheet("background-color:{0};".format(_color)) def toG1(self, on_off: str = "ON"): _color = "green" if on_off=="ON" else self.pf_bgcolor() pf = self.pframe_matrix["POST-COMET"][3][0] pf.setStyleSheet("background-color:{0};".format(_color)) for i in range(0, 5): pf = self.pframe_matrix["POST-COMET"][3+i][i+1] pf.setStyleSheet("background-color:{0};".format(_color)) for i in range(6, 10): pf = self.pframe_matrix["POST-COMET"][8][i] pf.setStyleSheet("background-color:{0};".format(_color)) def toG2(self, on_off: str = "ON"): _color = "green" if on_off=="ON" else self.pf_bgcolor() pf = self.pframe_matrix["POST-COMET"][3][0] pf.setStyleSheet("background-color:{0};".format(_color)) pf = self.pframe_matrix["POST-SH"][8][0] pf.setStyleSheet("background-color:{0};".format(_color)) for i in range(0, 5): pf = self.pframe_matrix["POST-SH"][8+i][i+1] pf.setStyleSheet("background-color:{0};".format(_color)) for i in range(6, 10): pf = self.pframe_matrix["POST-SH"][13][i] pf.setStyleSheet("background-color:{0};".format(_color)) def blink_target_beamline(self): target = self.target_beamline if target is None: return self.target_beamline = target self.timer_count += 1 if self.timer_count < 2: return if self.timer_count % 2 == 0: self.reset_beamline() else: self.set_target_beamline(reset=False) if self.timer_count > 8: self.timer.stop() QApplication.processEvents() def set_target_beamline(self, reset=True): """ Select beamline target and modify color scheme accordinly """ print("set_target_beamline", self.target_beamline, flush=True) master = self.beam_current_wgt_group[self.target_beamline] master.setObjectName("ARAMIS") master.style().polish(master) print("set_target_beamline//", self.target_beamline, flush=True) if reset: self.reset_beamline() def reset_beamline(self): """ Reset QGroupBox for previous target to original colors """ print("reset_target_beamline", self.target_beamline, flush=True) master = self.beam_current_wgt_group[self.target_beamline] if "BEAMLINE" not in master.objectName(): master.setObjectName("BEAMLINE") master.style().polish(master) print("reset_target_beamline/", self.target_beamline, flush=True) print("reset_target_beamline//", self.target_beamline, flush=True) 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 = ['HUSH', 'Dry'] #print('title', self.header_wgt.title(), flush=True) 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 beam_current_widget(self, header: str="", pv: str ="", color_obj: str="MACHINE"): """ QGroupBox encompassing beam current info """ station = QGroupBox() station.setObjectName(color_obj.upper()) station.setAlignment(Qt.AlignHCenter | Qt.AlignTop) station.setFlat(False) if header == "SH": station.setTitle("BMA1 SH") else: station.setTitle(header) beam_current = CAQLabel(self, pv_name=pv, show_units=True) beam_current.setFixedHeight(self.widget_height) if header == "MASTER": beam_current.setFixedWidth(140) else: beam_current.setFixedWidth(100) grid_layout = QGridLayout() grid_layout.addWidget(beam_current, 0, 0, 1, 1, Qt.AlignCenter) grid_layout.setVerticalSpacing(0) grid_layout.setHorizontalSpacing(0) grid_layout.setContentsMargins(2, 9, 2, 0) station.setLayout(grid_layout) station.setFixedHeight(beam_current.height() + self.extra_height) station.setFixedWidth(beam_current.width() + 15) self.beam_current_wgt_dict[header] = beam_current self.beam_current_wgt_group[header] = station return station