""" Base frame """ from datetime import datetime from decimal import Decimal import inspect import os import re #import matplotlib from matplotlib.backends.backend_qt5agg import ( FigureCanvasQTAgg, NavigationToolbar2QT as NavigationToolbar) #matplotlib.use('Agg') from qtpy.QtCore import QSize, Qt, Slot from qtpy.QtGui import ( QColor, QDoubleValidator, QFont, QFontMetricsF, QIcon, QIntValidator, QPainter, QPixmap) from qtpy.QtWidgets import ( QCheckBox, QComboBox, QDoubleSpinBox, QFrame, QGridLayout, QGroupBox, QHBoxLayout, QLabel, QLineEdit, QPushButton, QRadioButton, QSpinBox, QStackedWidget, QTabBar, QTabWidget, QToolButton, QVBoxLayout, QWidget) from caqtwidgets.pvwidgets import ( CAQLabel, CAQTableWidget, QMessageWidget, QHLine, QVLine) from apps4ops.bdbase.enumkind import Facility, MsgSeverity, UserMode _pymodule = os.path.basename(__file__) _appversion = "1.0.0" INPUT_PARAMETERS_HEIGHT = 200 EXPERT_PARAMETERS_HEIGHT = 200 def _line(): """Macro to return the current line number. The current line number within the file is used when reporting messages to the message logging window. Returns: int: Current line number. """ return inspect.currentframe().f_back.f_lineno class GUIFrame(QWidget): """ GUI Class """ def __init__(self, parent, has_optics=True, has_procedure="Vertical"): #super(GUIFrame, self).__init__() super().__init__() self.parent = parent self.has_optics = has_optics if isinstance(has_procedure, bool): self.has_procedure = has_procedure self.orientation_procedure = 'Vertical' else: self.has_procedure = True self.orientation_procedure = has_procedure self.appname = parent.appname self.facility = parent.facility self.settings = parent.settings 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.all_input_parameters = parent.all_input_parameters self.all_input_labels = parent.all_input_labels self.all_expert_parameters = parent.all_expert_parameters self.all_expert_labels = parent.all_expert_labels self.results_output_wgt_dict = {} self.results_output_wgt_dict_2 = {} try: self.widget_height = self.settings.data["StyleGuide"][ "widgetHeight"] except KeyError as error: print("KeyError in guiframe.py, init:", error) self.widget_height = 25 self.autopost_elog = parent.autopost_elog self.autopost_epics = parent.autopost_epics self.autopost_hdf = parent.autopost_hdf self.autopost_save = None self.expert_parameters_group = None self.operator_parameters_group = None self.output_parameters_wgt = None self.optics_group = None self.energy_stacked_widget = None self.phase_stacked_widget = None self.table_tab_wgt = None self.radiobutton = None self.multiple_figure_dict = None self.line_sender_dict = {} self.radio_stack_dict = {} self.stack_radiolist_dict = {} self.stacked_wgt_dict = {} self.current_stacked_wgt_dict = {} self.msg_severity_qcolor = parent.msg_severity_qcolor self.msg_severity = MsgSeverity self.font_gui = QFont("sans serif") self.font_gui.setPointSize(10) self.font_pts10 = QFont("sans serif") self.font_pts10.setPointSize(10) self.central_tab_widget = QTabWidget() self.measurement_wgt = QWidget() #self.measurement_layout = QHBoxLayout(self.measurement_wgt) self.measurement_layout = QGridLayout(self.measurement_wgt) self.measurement_layout.setAlignment(Qt.AlignLeft | Qt.AlignTop) self.measurement_layout.setSpacing(50) self.measurement_tab_wgt = QTabWidget(self.measurement_wgt) self.measurement_tab_wgt.setMinimumWidth(480) self.operator_wgt = QWidget() #(self.measurement_tab_wgt) self.expert_wgt = QWidget(self.measurement_tab_wgt) self.results_wgt = QWidget() self.results_layout = QHBoxLayout(self.results_wgt) self.results_tab_wgt = QTabWidget(self.results_wgt) self.results_wgt_2 = QWidget() self.results_layout_2 = QHBoxLayout(self.results_wgt_2) self.results_tab_wgt_2 = QTabWidget(self.results_wgt_2) self.slicing_group = QGroupBox("Screen: Slicing") self.good_region_group = QGroupBox("Good Region") self.canvas_fig_dict = {} self.canvas_fig_dict_2 = {} self.left_arrow_dict = {} self.right_arrow_dict = {} self.analysis_wgt = QWidget() self.abort_wgt = QWidget() self.start_wgt = QWidget() self.start_wgt_text = "Start" self.save_all_wgt = QWidget() if "GUI" in self.settings.data: if "subResultsTabTitle" in self.settings.data["GUI"]: self.results_tab_wgt_titles = self.settings.data[ "GUI"]["subResultsTabTitle"] self.sub_results_wgt = [None] * len(self.results_tab_wgt_titles) self.sub_results_layout = [None] * len(self.results_tab_wgt_titles) for i in range(0, len(self.results_tab_wgt_titles)): self.sub_results_wgt[i] = QWidget(self.results_tab_wgt) self.canvas = [None] * len(self.results_tab_wgt_titles) self.nav = [None] * len(self.results_tab_wgt_titles) self.arrow_button_widget = [None] * len(self.results_tab_wgt_titles) self.canvas_current_idx = [0] * len(self.results_tab_wgt_titles) self.init_results_tab_wgt() if "GUI2" in self.settings.data: if "subResultsTabTitle" in self.settings.data["GUI2"]: self.results_tab_wgt_titles_2 = self.settings.data[ "GUI2"]["subResultsTabTitle"] self.sub_results_wgt_2 = [None] * len(self.results_tab_wgt_titles_2) self.sub_results_layout_2 = [None] * len(self.results_tab_wgt_titles_2) for i in range(0, len(self.results_tab_wgt_titles_2)): self.sub_results_wgt_2[i] = QWidget() #self.results_tab_wgt_2) self.canvas_2 = [None] * len(self.results_tab_wgt_titles_2) self.nav_2 = [None] * len(self.results_tab_wgt_titles_2) self.arrow_button_widget_2 = [None] * len(self.results_tab_wgt_titles_2) self.canvas_current_idx_2 = [0] * len(self.results_tab_wgt_titles_2) self.init_results_tab_wgt_2() self.slice_orientation = "vertical" #horizontal/vertical for SATMA02 self.init_measurement_tab_wgt() self.log_wgt = QMessageWidget() self.log_layout = QVBoxLayout(self.log_wgt) #self.show_log_message(MsgSeverity.INFO, _pymodule, _line(), # "Application started") self.input_wgt_grid = QGridLayout() self.input_wgt_grid.setHorizontalSpacing(0) self.input_wgt_grid.setVerticalSpacing(2) self.expert_wgt_grid = QGridLayout() self.expert_wgt_grid.setHorizontalSpacing(0) self.expert_wgt_grid.setVerticalSpacing(2) if "GUITree" in self.settings.data: self.init_central_tab_widget_tree() else: self.init_central_tab_widget() self.enter_input_parameters() def init_central_tab_widget_tree(self): #_from_config(self): self.central_tab_widget.setFont(self.font_gui) _margin = 4 destination_index = [] try: idx = self.settings.data["GUI"]["destinationTreeIndex"] destination_index.append(idx) except KeyError: pass try: idx = self.settings.data["GUI2"]["destinationTreeIndex"] destination_index.append(idx) except KeyError: pass def is_destination(a, b, c): for item in destination_index: if len(item) != 3: continue if item[0] == a and item[1] == b and item[2] == c: return True return False if "GUITree" in self.settings.data: key_list = list(self.settings.data["GUITree"].keys()) self.level1_wgt = [None] * len(key_list) self.level1_tab_wgt = [None] * len(key_list) self.level2_wgt = {} self.level2_tab_wgt = {} self.level3_wgt = {} self.level3_tab_wgt = {} #qwgt2 = {} #qwgt2_tab = {} for i, title in enumerate(key_list): print(i, title, flush=True) self.level1_wgt[i] = QWidget() self.level1_wgt[i].title = title self.level1_tab_wgt[i] = QTabWidget() #self.level1_wgt[i]) self.level1_tab_wgt[i].setMinimumWidth(480) self.level1_tab_wgt[i].setFont(self.font_gui) key_list2 = list(self.settings.data["GUITree"][title]) self.level2_wgt[i] = [None] * len(key_list2) self.level2_tab_wgt[i] = [None] * len(key_list2) #qwgt2[i] = [None] * len(key_list2) #qwgt2_tab[i] = [None] * len(key_list2) self.level3_wgt[i] = [None] * len(key_list2) self.level3_tab_wgt[i] = [None] * len(key_list2) for j, title2 in enumerate(key_list2): print(j, title2, flush=True) key_list3 = (self.settings.data["GUITree"][title][title2]) is_plot = False if isinstance(key_list3, list): #if "Plots" in key_list3: # is_plot = True len_key_list3 = len(key_list3) self.level3_wgt[i][j] = [None] * len_key_list3 self.level3_tab_wgt[i][j] = [None] * len_key_list3 elif isinstance(key_list3, dict): if 'link' in key_list3: #get link link = key_list3['link'] key_list3 = (self.settings.data[link]["subResultsTabTitle"]) if isinstance(key_list3, list): is_plot = True len_key_list3 = len(key_list3) self.level3_wgt[i][j] = [None] * len_key_list3 self.level3_tab_wgt[i][j] = [None] * len_key_list3 else: len_key_list3 = 0 else: len_key_list3 = 0 else: len_key_list3 = 0 self.level2_wgt[i][j] = QWidget() #self.level1_wgt[i]) self.level2_wgt[i][j].title2 = title2 self.level2_wgt[i][j].setFont(self.font_gui) #Attaching the correct object to QTabWidget is crucuial! if not is_plot: self.level2_tab_wgt[i][j] = QTabWidget(self.level2_wgt[i][j]) else: if i == 1: print("///", i, j, flush=True) self.level2_tab_wgt[i][j] = QTabWidget() #self.results_wgt_2) else: print("//", i, j, flush=True) self.level2_tab_wgt[i][j] = QTabWidget() #self.results_wgt) self.level2_tab_wgt[i][j].setMinimumWidth(480) self.level2_tab_wgt[i][j].setFont(self.font_gui) for k in range (0, len_key_list3): print("tilt3", key_list3[k], flush=True) self.level3_tab_wgt[i][j][k] = QTabWidget() #if "Plots" in key_list3[k]: # is_plot = True #if not is_plot: title3 = key_list3[k] self.level3_wgt[i][j][k] = QWidget() #self.level2_wgt[i][j]) self.level3_wgt[i][j][k].title3 = title3 self.level3_wgt[i][j][k].setFont(self.font_gui) if is_destination(i, j, k): #is_plot = True #if i==1 and j==0 and k==1: print("LEVEL2=============>", i, j, k, title3) self.level2_tab_wgt[i][j].addTab(self.results_wgt_2, title3) else: print("LEVEL2/3=============>", i, j, k, title3) self.level2_tab_wgt[i][j].addTab(self.level3_wgt[i][j][k], title3) if not is_plot: self.level1_tab_wgt[i].addTab(self.level2_wgt[i][j], title2) else: #if i == 1 and j==0: # print("////", i, flush=True) # self.level1_tab_wgt[i].addTab(self.results_wgt_2, title2) #else: # print("//=", i, flush=True) self.level1_tab_wgt[i].addTab(self.results_wgt, title2) layout2 = QGridLayout() #self.level1_wgt[i]) layout2.addWidget(self.level2_tab_wgt[i][j]) layout2.addWidget(QFrame()) #Add to stop streching of Measurement Tab layout2.setContentsMargins(_margin, _margin, _margin, _margin) self.level2_wgt[i][j].setLayout(layout2) layout = QGridLayout() #self.level1_wgt[i]) layout.addWidget(self.level1_tab_wgt[i], 1, 0, 1, 20) self.level1_wgt[i].setLayout(layout) layout.setContentsMargins(_margin, _margin, _margin, _margin) self.central_tab_widget.addTab(self.level1_wgt[i], title) #***How to get to the widgets within the lowest level tabs*** #lo = QHBoxLayout() #lo.addWidget(QLabel('TEST')) #self.gui_frame.level2_tab_wgt[1][0].widget(0).setLayout(lo) #self.gui_frame.level3_wgt[1][0][0].setLayout(lo) #if "GUI" in self.settings.data: #self.central_tab_widget.addTab( ## self.level1_tab_wgt[1].addTab( ## self.results_wgt, self.settings.data["GUI"]["resultsTabTitle"]) self.central_tab_widget.addTab(self.log_wgt, "Log") self.operator_wgt = self.level3_wgt[0][0][0] self.expert_wgt = self.level3_wgt[0][0][1] self.measurement_tab_wgt = self.level2_tab_wgt[0][0] self.measurement_wgt = self.level2_wgt[0][0] def init_central_tab_widget(self): """ Add tabs to central widget """ self.central_tab_widget.setFont(self.font_gui) self.central_tab_widget.addTab(self.measurement_wgt, "Measurement") #if "GUITree" in self.settings.data: # tab = list(self.settings.data["GUITree"].keys())[0] # self.central_tab_widget.addTab(self.measurement_wgt, tab) if "GUI" in self.settings.data: self.central_tab_widget.addTab( self.results_wgt, self.settings.data["GUI"]["resultsTabTitle"]) self.central_tab_widget.addTab(self.log_wgt, "Log") def init_measurement_tab_wgt(self): """ Add tabs to measurement widget """ self.measurement_tab_wgt.setFont(self.font_gui) self.measurement_tab_wgt.addTab(self.operator_wgt, "Operator") self.measurement_tab_wgt.addTab(self.expert_wgt, "Expert") self.measurement_layout.addWidget(self.measurement_tab_wgt, 0, 0) def init_results_tab_wgt(self): """ Add canvas tabs for plots/results """ self.results_tab_wgt.setFont(self.font_gui) for i, (wgt, subtitle) in enumerate(zip(self.sub_results_wgt, self.results_tab_wgt_titles)): top_widget = QWidget(self.results_tab_wgt) top_layout = QVBoxLayout() self.sub_results_layout[i] = QVBoxLayout() if self.settings.data["GUI"]["resultsSeq"][i] > 1: top_layout.addWidget(self.get_arrow_button_widget(i)) wgt.setLayout(self.sub_results_layout[i]) top_layout.addWidget(wgt) #top_layout.addWidget(QLabel("HERE")) top_widget.setLayout(top_layout) self.results_tab_wgt.addTab(top_widget, subtitle) self.results_layout.addWidget(self.results_tab_wgt) def init_results_tab_wgt_2(self): """ Add canvas tabs for plots/results """ self.results_tab_wgt_2.setFont(self.font_gui) for i, (wgt, subtitle) in enumerate(zip(self.sub_results_wgt_2, self.results_tab_wgt_titles_2)): top_widget = QWidget(self.results_tab_wgt_2) top_layout = QVBoxLayout() self.sub_results_layout_2[i] = QVBoxLayout() if self.settings.data["GUI2"]["resultsSeq"][i] > 1: top_layout.addWidget(self.get_arrow_button_widget(i)) wgt.setLayout(self.sub_results_layout_2[i]) top_layout.addWidget(wgt) #top_layout.addWidget(QLabel("THERE")) top_widget.setLayout(top_layout) self.results_tab_wgt_2.addTab(top_widget, subtitle) self.results_layout_2.addWidget(self.results_tab_wgt_2) def show_log_message(self, severity=None, pymodule="", lineno=0, message="", options=None): """ Post to log window """ source = "{0}:{1:d} ".format(pymodule, lineno) #datetime.utcnow() message_to_send = datetime.now().strftime( "%Y-%m-%d %H:%M:%S.%f")[:-5] message_to_send += " " if isinstance(severity, self.msg_severity): message_to_send += severity.name elif severity in self.msg_severity_qcolor.keys(): message_to_send += str(severity) elif str(severity) == "WARNING": message_to_send += "WARN" message_to_send += (" {" + source + message + "} ") if options: for k, v in options.items(): message_to_send += (" [" + str(k) + ": " + str(v) + "]") self.log_wgt.insertItem(0, message_to_send) self.log_wgt.item(0).setFlags( Qt.ItemIsSelectable | Qt.ItemIsDragEnabled | Qt.ItemIsEnabled | Qt.NoItemFlags) self.log_wgt.item(0).setForeground( self.msg_severity_qcolor.get(severity, QColor(8, 8, 8))) #Top level groups def analysis_procedure_group(self): """ Procedure group box """ if 'V' in self.orientation_procedure.upper(): _width = 208 _height = 196 else: _width = 508 _height = 106 group_box = QGroupBox("Procedure") group_box.setObjectName("OUTER") vbox = QVBoxLayout() vbox.addLayout(self.create_analysis_wgt()) vbox.setContentsMargins(9, 19, 9, 9) vbox.setSpacing(5) vbox.setAlignment(Qt.AlignTop | Qt.AlignHCenter) group_box.setMaximumWidth(_width) group_box.setMinimumHeight(_height) group_box.setFont(self.font_gui) group_box.setAlignment(Qt.AlignTop | Qt.AlignHCenter) group_box.setLayout(vbox) return group_box def optics_groupbox(self): """ Optice set and restore group box """ self.optics_group = QGroupBox("Optics") self.optics_group.setObjectName("OUTER") self.optics_group.setMaximumWidth(208) self.optics_group.setMaximumHeight(220) self.optics_group.setFont(self.font_gui) self.optics_group.setAlignment(Qt.AlignTop | Qt.AlignHCenter) self.optics_group.setAlignment(Qt.AlignCenter) self.optics_group.setLayout(self.create_optics_layout()) self.optics_group.table = None self.optics_group.deflector = None self.optics_group.profile_monitor = None self.optics_group.quads = None self.optics_group.cycle_quads = None return self.optics_group def output_parameters_group(self, title="Results Output"): """ Generic group box for user supplied input """ self.output_parameters_wgt = QWidget() qtop = QVBoxLayout() self.output_parameters_group_box = QGroupBox(title) self.output_parameters_group_box.setObjectName("OUTER") vbox = QVBoxLayout() vbox.setContentsMargins(9, 19, 9, 9) vbox.setAlignment(Qt.AlignTop | Qt.AlignHCenter) keys = self.settings.data["Results"].keys() max_str_len = 4 for key in keys: if len(key) > max_str_len: max_str_len = len(key) for key in keys: ikey = self.settings.data["Results"][key] if ikey['flag']: _widget = ikey["data"]["widget"].upper() _text = ikey["data"]["text"] _value = str(ikey["data"]["value"]) _width = int(ikey["data"]["width"]) if _widget == "QLINEEDIT": ql = QLabel(str(_text)) _label_width = (ql.fontMetrics().averageCharWidth() * max_str_len+2) ql.setMinimumWidth(_label_width) ql.setFixedHeight(self.widget_height) ql.setFont(self.font_pts10) wgt = QLineEdit() wgt.setFixedWidth(_width) wgt.setObjectName("ReadRight") wgt.setFixedHeight(self.widget_height) wgt.setFont(self.font_pts10) wgt.setAlignment(Qt.AlignRight) self.results_output_wgt_dict[key] = wgt if "NONE" not in _value.upper(): self.results_output_wgt_dict[key].setText(_value) hbox = QHBoxLayout() hbox.setContentsMargins(0, 0, 0, 0) hbox.addWidget(ql) hbox.addWidget(wgt) hbox.setAlignment(Qt.AlignLeft) vbox.addLayout(hbox) self.output_parameters_group_box.setContentsMargins(9, 19, 9, 9) self.output_parameters_group_box.setMinimumWidth(140) self.output_parameters_group_box.setMaximumWidth(300) #self.output_parameters_group_box.setMaximumHeight(400) self.output_parameters_group_box.setFont(self.font_gui) self.output_parameters_group_box.setAlignment(Qt.AlignTop | Qt.AlignHCenter) qf = QFrame() qf.setFixedHeight(35) self.output_parameters_group_box.setLayout(vbox) qtop.addWidget(qf) qtop.addWidget(self.output_parameters_group_box) self.output_parameters_wgt.setLayout(qtop) return self.output_parameters_wgt def send_to_results_output_wgt(self, results_data): for key, value in results_data.items(): self.results_output_wgt_dict[key].setText(str(value)) def input_parameters_group(self, title="Input Parameters"): """ Generic group box for user supplied input """ group_box = QGroupBox(title) group_box.setObjectName("OUTER") hbox = QVBoxLayout() hbox.setContentsMargins(9, 19, 9, 9) hbox.setAlignment(Qt.AlignTop | Qt.AlignHCenter) group_box.setContentsMargins(0, 0, 0, 0) group_box.setMinimumWidth(200) group_box.setMaximumHeight(400) group_box.setFont(self.font_gui) group_box.setAlignment(Qt.AlignTop | Qt.AlignHCenter) group_box.setLayout(hbox) return group_box def operator_parameters_groupbox(self, title="Input Parameters"): """ Operator input parameters group box """ self.operator_parameters_group = self.input_parameters_group(title) return self.operator_parameters_group def expert_parameters_groupbox(self, title="Expert Parameters"): """ Expert input parameters group box """ self.expert_parameters_group = self.input_parameters_group(title) return self.expert_parameters_group def create_checkbox(self, top_key, key, text="", tip="", default_value=Qt.Unchecked): """ Generic check box """ def on_checkbox_change(value): self.input_parameters[key] = bool(value) #print("CB fired") #print(self.input_parameters[key]) #print(self.input_parameters) if key == "goodRegion": self.good_region_group.setEnabled(value) checkbox = QCheckBox(text) checkbox.setToolTip(tip) checkbox.setFont(self.font_gui) checkbox.stateChanged.connect(on_checkbox_change) #checkbox.setFixedHeight(30) #checkbox.setLayoutDirection(Qt.RightToLeft) checked_value = default_value if key in self.parent.input_parameters.keys(): if "data" in self.settings.data[top_key][key].keys(): if "value" in self.settings.data[top_key][key]["data"].keys(): if self.settings.data[top_key][key]["data"]["value"]: checked_value = Qt.Checked else: checked_value = Qt.Unchecked checkbox.setCheckState(checked_value) checkbox.stateChanged.emit(checked_value) self.line_sender_dict[key] = checkbox return checkbox def create_label_qdoublespinbox(self, key, label="", min_val=0, max_val=1, step=0.1, start_val=0.5, digits=1): """ Generic QDoubleSpinbox with buddy label """ def callback(value): self.input_parameters[key] = round(value, digits) widget_height = 24 suggested = "WWW" lab = QLabel(label) lab.setFixedHeight(widget_height) lab.setFont(self.font_pts10) lab.setContentsMargins(5, 0, 0, 0) wgt = QDoubleSpinBox() wgt.setObjectName("Write") wgt.setFixedHeight(widget_height) wgt.setFont(self.font_pts10) wgt.setContentsMargins(5, 0, 0, 0) decimal = Decimal(str(step)) wgt.setDecimals(abs(decimal.as_tuple().exponent)) #precision wgt.setRange(min_val, max_val) wgt.setSingleStep(step) fm = QFontMetricsF(wgt.font()) param_width = max(fm.width(str(decimal)), fm.width(suggested)) wgt.setMaximumWidth(param_width + 40) wgt.valueChanged.connect(callback) wgt.setValue(start_val) wgt.valueChanged.emit(start_val) self.line_sender_dict[key] = wgt return lab, wgt def create_label_qspinbox(self, key, label="", min_val=0, max_val=100, step=1, start_val=5): """ Generic QSpinbox with buddy label """ def cb(value): self.input_parameters[key] = value widget_height = 24 suggested = "WWW" lab = QLabel(label) lab.setFixedHeight(widget_height) lab.setFont(self.font_pts10) lab.setContentsMargins(5, 0, 0, 0) wgt = QSpinBox() wgt.setObjectName("Write") wgt.setFixedHeight(widget_height) wgt.setFont(self.font_pts10) wgt.setContentsMargins(5, 0, 0, 0) decimal = Decimal(str(step)) wgt.setRange(min_val, max_val) wgt.setSingleStep(step) fm = QFontMetricsF(wgt.font()) param_width = max(fm.width(str(decimal)), fm.width(suggested)) wgt.setMaximumWidth(param_width + 40) wgt.valueChanged.connect(cb) wgt.setValue(start_val) wgt.valueChanged.emit(start_val) self.line_sender_dict[key] = wgt return lab, wgt def create_label_qcombobox(self, key, label="", values: list = [], start_val: str =""): """ Generic QCombobox with buddy label """ def cb(new_text): self.input_parameters[key] = new_text #print( self.input_parameters) widget_height = 26 suggested = "WWW" lab = QLabel(label) lab.setFixedHeight(widget_height) lab.setFont(self.font_pts10) lab.setContentsMargins(5, 0, 0, 0) wgt = QComboBox() wgt.clear() if values: wgt.addItems(values) wgt.setObjectName("Write") wgt.setFixedHeight(widget_height) wgt.setFont(self.font_pts10) wgt.setContentsMargins(5, 0, 0, 0) wgt.currentTextChanged.connect(cb) start_idx = wgt.findText(start_val) start_idx = start_idx if start_idx > 0 else 0 wgt.setCurrentIndex(start_idx) wgt.currentTextChanged.emit(values[start_idx]) value_for_width = max(values, key=len) fm = QFontMetricsF(wgt.font()) param_width = max(fm.width(str(value_for_width)), fm.width(suggested)) wgt.setFixedWidth(param_width + 50) self.line_sender_dict[key] = wgt return lab, wgt def create_analysis_wgt( self, start_title="Start", start_action=None, start_tooltip="Start measurement and analysis procedure", abort_title="Abort", abort_action=None, abort_tooltip="Abort procedure at next break point", orientation="Vertical"): if start_action is None: start_action = self.parent.start_analysis_thread if abort_action is None: abort_action = self.parent.receive_abort_analysis self.start_wgt = self.create_start_wgt(start_title, start_action, start_tooltip) self.abort_wgt = self.create_abort_wgt(abort_title, abort_action, abort_tooltip) self.save_all_wgt = self.save_all_group() grid = QVBoxLayout() if 'V' in self.orientation_procedure.upper() else QHBoxLayout() grid.addWidget(self.start_wgt) grid.addWidget(self.abort_wgt) grid.addWidget(self.save_all_wgt) grid.setAlignment(Qt.AlignLeft | Qt.AlignTop) return grid def create_optics_layout(self): vbox = QGridLayout() self.set_optics_btn = QPushButton("Set") self.set_optics_btn.setFixedWidth(100) self.set_optics_btn.setFixedHeight(40) self.set_optics_btn.setObjectName("Controller") self.set_optics_btn.setProperty("actOnBeam", True) self.set_optics_btn.clicked.connect(self.parent.set_optics) _tip = "Set associated quadruples" self.set_optics_btn.setToolTip(_tip) self.restore_optics_btn = QPushButton("Restore") self.restore_optics_btn.setFixedWidth(100) self.restore_optics_btn.setFixedHeight(40) self.restore_optics_btn.setObjectName("Controller") self.restore_optics_btn.setProperty("actOnBeam", True) self.restore_optics_btn.clicked.connect(self.parent.restore_optics) _tip = "Restore quadruples to their pre-measurement values" self.restore_optics_btn.setToolTip(_tip) #_text = """

# {1}
{2}
#

# """.format('gray', 'Deflector', 'PM') self.target_optics = QLabel() #self.target_optics.setText(_text) self.target_optics.setFixedWidth(150) self.target_optics.setFixedHeight(40) self.target_optics.setStyleSheet("background-color:white;") _tip = "Shows target for optics settings" self.target_optics.setToolTip(_tip) vbox.addWidget(self.set_optics_btn, 0, 0, Qt.AlignCenter) vbox.addWidget(self.target_optics, 1, 0, Qt.AlignCenter) vbox.addWidget(self.restore_optics_btn, 2, 0, Qt.AlignCenter) vbox.setContentsMargins(0, 19, 0, 9) vbox.setAlignment(Qt.AlignCenter) vbox.setSpacing(8) return vbox def in_measurement_procedure(self): self.abort_wgt.setVisible(True) self.abort_wgt.setEnabled(True) self.start_wgt.setEnabled(False) self.start_wgt.setText("in progress...") self.save_all_group_box.setEnabled(False) self.operator_parameters_group.setEnabled(False) self.expert_parameters_group.setEnabled(False) if self.optics_group is not None: self.optics_group.setEnabled(False) if self.table_tab_wgt is not None: for table in self.table_wgt: table.init_value_button.setEnabled(False) table.restore_value_button.setEnabled(False) if self.output_parameters_wgt is not None: for wgt in self.results_output_wgt_dict.values(): wgt.setText("") #self.output_parameters_wg.setEnabled(False) def in_abort_procedure(self): self.abort_wgt.setEnabled(False) self.start_wgt.setEnabled(False) self.start_wgt.setText("Aborting...") def reset_procedure(self): self.abort_wgt.setVisible(False) self.abort_wgt.setEnabled(True) self.start_wgt.setEnabled(True) self.start_wgt.setText(self.start_wgt_text) self.save_all_group_box.setEnabled(True) self.operator_parameters_group.setEnabled(True) self.expert_parameters_group.setEnabled(True) if self.optics_group is not None: self.optics_group.setEnabled(True) if self.table_tab_wgt is not None: for table in self.table_wgt: table.init_value_button.setEnabled(True) table.restore_value_button.setEnabled(True) #if self.output_parameters_wgt is not None: # self.output_parameters_wgt.setEnabled(True) def in_hdf_measurement_procedure(self): self.parent.h5_groupbox.analyze_h5_widget.setEnabled(False) self.parent.h5_groupbox.analyze_h5_widget.setText("Analysing") self.start_wgt.setEnabled(False) self.start_wgt.setText("HDF analysis...") self.save_all_group_box.setEnabled(False) def hdf_reset_procedure(self): self.parent.h5_groupbox.analyze_h5_widget.setEnabled(True) self.parent.h5_groupbox.analyze_h5_widget.setText("Analyze") self.start_wgt.setEnabled(True) self.start_wgt.setText(self.start_wgt_text) self.save_all_group_box.setEnabled(True) def create_abort_wgt(self, title, action, tooltip): abort_wgt = QPushButton(title) abort_wgt.setObjectName("Abort") abort_wgt.setFixedWidth(150) abort_wgt.setFixedHeight(40) abort_wgt.setToolTip(tooltip) if action: abort_wgt.clicked.connect(action) abort_wgt.setVisible(False) sp_retain = abort_wgt.sizePolicy() sp_retain.setRetainSizeWhenHidden(True) abort_wgt.setSizePolicy(sp_retain) return abort_wgt def create_start_wgt(self, title, action, tooltip): start_wgt = QPushButton(title) start_wgt.setObjectName("Controller") start_wgt.setProperty("actOnBeam", True) start_wgt.setFixedWidth(150) start_wgt.setFixedHeight(40) start_wgt.setToolTip(tooltip) if action: start_wgt.clicked.connect(action) start_wgt.setVisible(True) return start_wgt def combine_save_icons(self): #epics=False, hdf=False, elog=False): epics = self.autopost_epics hdf = self.autopost_hdf elog = self.autopost_elog combinedIcon = QIcon() startx1 = 0 startx2 = 0 startx3 = 0 true_list = [epics, hdf, elog] if true_list.count(True) == 1: startx1 = 150 startx2 = 150 startx3 = 150 elif true_list.count(True) == 3: startx1 = 0 startx2 = 150 startx3 = 300 else: if epics: startx1 = 75 startx2 = 225 startx3 = 225 elif hdf: startx2 = 75 startx3 = 225 comboPixmap = QPixmap(420, 100) #420, 100 comboPixmap.fill(Qt.transparent) epicsImage = QPixmap(":/epics.png") hdfImage = QPixmap(":/hdfS.png") elogImage = QPixmap(":/elog.png") painter = QPainter(comboPixmap) if epics: painter.drawPixmap(startx1, 0, epicsImage, 0, -20, 100, 160) if hdf: painter.drawPixmap(startx2, 0, hdfImage, 0, -20, 100, 160) if elog: painter.drawPixmap(startx3, 0, elogImage) combinedIcon.addPixmap(comboPixmap) painter.end() return combinedIcon def post_measurement_save_button(self): # epics=True, hdf=True, elog=True): epics = self.autopost_epics #= epics hdf = self.autopost_hdf #= hdf elog = self.autopost_elog #= elog widget_height = 40 true_list = [epics, hdf, elog] widget_width = max(75, 45 * true_list.count(True)) save_data_btn = QPushButton("") save_data_btn.setIconSize(QSize(100, 38)) save_data_btn.setIcon(self.combine_save_icons()) #epics=epics, hdf=hdf, elog=elog)) save_data_btn.setFixedHeight(widget_height) save_data_btn.setObjectName("WriteData") save_data_btn.clicked.connect(self.parent.write_to_destinations) save_data_btn.setFixedHeight(widget_height) save_data_btn.setFixedWidth(widget_width) #80 for one if epics and hdf and elog: choices = "epics/hdf5/elog" elif epics and hdf: choices = "epics/hdf5" elif hdf and elog: choices = "hdf/elog" elif epics: choices = "epics" elif hdf: choices = "hdf" elif elog: choices = "elog" _tip = "Saves data to {0}".format(choices) save_data_btn.setToolTip(_tip) self.autopost_save = save_data_btn return self.autopost_save def post_measurement_group(self): group_box = QGroupBox("Post-measurement") group_box.setObjectName("OUTER") hbox = QVBoxLayout() hbox.addWidget(self.post_measurement_save_button()) hbox.setContentsMargins(9, 19, 9, 9) hbox.setAlignment(Qt.AlignTop | Qt.AlignHCenter) group_box.setContentsMargins(0, 0, 0, 0) group_box.setFixedWidth(208) group_box.setMaximumHeight(130) group_box.setFont(self.font_gui) group_box.setAlignment(Qt.AlignTop | Qt.AlignHCenter) group_box.setLayout(hbox) return group_box def save_all_group(self): self.save_all_group_box = QGroupBox("Save All") self.save_all_group_box.setObjectName("INNERCENTER") hbox = QVBoxLayout() hbox.addWidget(self.post_measurement_save_button()) hbox.setContentsMargins(1, 4, 1, 2) hbox.setAlignment(Qt.AlignTop | Qt.AlignHCenter) self.save_all_group_box.setContentsMargins(0, 0, 0, 0) self.save_all_group_box.setMaximumWidth(160) self.save_all_group_box.setFixedHeight(60) self.save_all_group_box.setFont(self.font_gui) self.save_all_group_box.setAlignment(Qt.AlignTop | Qt.AlignHCenter) self.save_all_group_box.setLayout(hbox) return self.save_all_group_box def image_parameters_group(self): group_box = QGroupBox("Image Pipeline Expert") box = QHBoxLayout() box.setSpacing(10) try: start_val_gr = self.settings.data["Expert"]["pipeline"]["data"][ "goodRegion"] start_val_gr = Qt.Checked if start_val_gr else Qt.Unchecked except KeyError as ex: start_val_gr = Qt.Unchecked box.addWidget(self.create_checkbox( top_key="Expert", key="goodRegion", text="Good region", tip="Enables good region in pipeline", default_value=start_val_gr)) try: start_val_sub = self.settings.data["Expert"]["pipeline"]["data"][ "subtractBackground"] start_val_sub = Qt.Checked if start_val_sub else Qt.Unchecked except KeyError as ex: start_val_sub = Qt.Unchecked box.addWidget(self.create_checkbox( top_key="Expert", key="subtractBackground", text="Subtract bkgrnd", tip="Enables background subtraction in pipeline", default_value=start_val_sub)) group_box.setAlignment(Qt.AlignCenter) group_box.setObjectName("INNERCENTER") self.good_region_group.setObjectName("INNER") try: start_val_grThreshold = self.settings.data["Expert"]["pipeline"][ "data"]["grThreshold"] except KeyError as ex: start_val_grThreshold = 21 lab_thresh, thresh = self.create_label_qdoublespinbox( key="grThreshold", label="Threshold:", start_val=start_val_grThreshold) try: start_val_grScale = self.settings.data["Expert"]["pipeline"][ "data"]["grScale"] except KeyError as ex: start_val_grScale = 6 lab_gfscale, gfscale = self.create_label_qspinbox( key="grScale", label="Scale:", start_val=start_val_grScale) layout = QGridLayout() layout.addWidget(lab_thresh, 0, 0) layout.addWidget(thresh, 0, 1) layout.addWidget(lab_gfscale, 1, 0) layout.addWidget(gfscale, 1, 1) self.good_region_group.setLayout(layout) self.slicing_group.setObjectName("INNER") try: start_val_nslices = self.settings.data["Expert"]["pipeline"][ "data"]["slices"] except KeyError as ex: start_val_nslices = 21 lab_slices, slices = self.create_label_qspinbox( key="slices", label="No. Slices:", start_val=start_val_nslices) try: start_val_slicing_scale = self.settings.data["Expert"]["pipeline"][ "data"]["slicingScale"] except KeyError as ex: start_val_slicing_scale = 5 lab_scale, scale = self.create_label_qspinbox( key="slicingScale", label="Scale:", start_val=start_val_slicing_scale) try: start_val_orientation = self.settings.data["Expert"]["pipeline"][ "data"]["orientation"] except KeyError as ex: start_val_orientation = "vertical" lab_orientation, self.slice_orientation = self.create_label_qcombobox( key="orientation", label="Orientation:", values=["vertical", "horizontal"], start_val=start_val_orientation) layout = QGridLayout() layout.addWidget(lab_slices, 0, 0) layout.addWidget(slices, 0, 1) layout.addWidget(lab_scale, 1, 0) layout.addWidget(scale, 1, 1) layout.addWidget(lab_orientation, 2, 0, 1, 2, Qt.AlignBottom) layout.addWidget(self.slice_orientation, 3, 0, 1, 2, Qt.AlignTop | Qt.AlignRight) self.slicing_group.setLayout(layout) layout_top = QGridLayout() layout_top.addLayout(box, 0, 0, 1, 2) layout_top.addWidget(self.good_region_group, 1, 0, 1, 1, Qt.AlignHCenter | Qt.AlignTop) layout_top.addWidget(self.slicing_group, 1, 1, 1, 1, Qt.AlignCenter | Qt.AlignTop) layout_top.setHorizontalSpacing(8) group_box.setLayout(layout_top) group_box.setFixedHeight(200) return group_box def checkbox_wgt(self, key: str = "", tooltip: str = "", object_name=None, hline="NONE"): def on_change(state): self.input_parameters[key] = bool(state) widget = QWidget() layout = QGridLayout() layout.setContentsMargins(0, 0, 0, 0) row_wgt = 0 if hline == "TOP": layout.addWidget(QHLine(), 0, 0, 1, 3) row_wgt = 1 try: text = self.settings.data["Expert"][key]["data"]["text"] except KeyError as error: print(("KeyError in guiframe.py; Unknown key {0} " + "in def checkbox_wgt: {1}").format(key, error)) text = key checkbox = QCheckBox(text) checkbox.setToolTip(tooltip) checkbox.setFont(self.font_gui) if object_name: checkbox.setObjectName(object_name) checkbox.setLayoutDirection(Qt.LeftToRight) #is default checkbox.stateChanged.connect(on_change) checked_value = Qt.Unchecked if "Expert" not in self.settings.data: return widget #if key in self.parent.input_parameters.keys(): if key in self.settings.data["Expert"].keys(): if "data" in self.settings.data["Expert"][key].keys(): if "value" in self.settings.data["Expert"][key]["data"].keys(): if self.settings.data["Expert"][key]["data"]["value"]: checked_value = Qt.Checked checkbox.setCheckState(checked_value) layout.addWidget(checkbox, row_wgt, 0) layout.addWidget(QFrame(), row_wgt, 1, 1, 2) if hline == "BOTTOM": layout.addWidget(QHLine(), row_wgt+1, 0, 1, 3) widget.setLayout(layout) return widget def checkbox_rawdata(self, hline="None"): tooltip = ("Save scanned data to separate hdf file " + "(for later reanalysis)") return self.checkbox_wgt(key="rawData", tooltip=tooltip, hline=hline) def checkbox_autoRestore(self, hline="None"): tooltip = ("Automatically restores quads to initial values before " + "new setting or exit") return self.checkbox_wgt(key="autoRestore", tooltip=tooltip, hline=hline) def checkbox_keepImages(self, hline="None"): tooltip = ("Store plots/images on disk") return self.checkbox_wgt(key="keepImages", tooltip=tooltip, hline=hline) def checkbox_autoCycle(self, hline="None"): tooltip = ("Always cycle quads when setting to new values") return self.checkbox_wgt(key="autoCycle", tooltip=tooltip, hline=hline) def checkbox_debug(self, hline="None"): tooltip = "Activates print statements in user code" self.input_parameters["debug"] = False return self.checkbox_wgt(key="debug", tooltip=tooltip, hline=hline) def checkbox_simulation(self, hline="NONE"): def on_change(state): if bool(state): self.parent.simulation = True self.parent.gui_header.change_operation_mode( user_mode=UserMode.SIMULATION) self.parent.progressbar_color \ = self.parent.progressbar_simulation else: self.parent.simulation = False self.parent.gui_header.reset_operation_mode() self.parent.progressbar_color = self.parent.progressbar_standard self.input_parameters["simulation"] = self.parent.simulation widget = QWidget() layout = QGridLayout() layout.setContentsMargins(0, 0, 0, 0) row_wgt = 0 if hline == "TOP": layout.addWidget(QHLine(), 0, 0, 1, 3) row_wgt = 1 try: text = self.settings.data["Expert"]["simulation"]["data"]["text"] except KeyError as error: print("KeyError in guiframe.py; def checkbox_simulation:", error) text = "Simulation" checkbox = QCheckBox(text) checkbox.setObjectName("Simulation") checkbox.setToolTip( "Dry-run only; does not write to EPICS Process Variables") checkbox.setFont(self.font_gui) #checkbox.setStyleSheet( #"QCheckBox::indicator::checked {width=85px; height: 85px;};") checkbox.stateChanged.connect(on_change) checked_value = Qt.Unchecked if "simulation" in self.parent.input_parameters.keys(): if "data" in self.settings.data["Expert"]["simulation"].keys(): if "value" in self.settings.data["Expert"]["simulation"][ "data"].keys(): if self.settings.data["Expert"]["simulation"]["data"][ "value"]: checked_value = Qt.Checked checkbox.setCheckState(checked_value) layout.addWidget(checkbox, row_wgt, 0) layout.addWidget(QFrame(), row_wgt, 1, 1, 2) if hline == "BOTTOM": layout.addWidget(QHLine(), row_wgt+1, 0, 1, 3) widget.setLayout(layout) return widget def get_arrow_button_widget(self, parent_idx=0): #Back/Forward Buttons START#### arrow_button_widget = QWidget() #self.plot_widget[i]) layout = QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setAlignment(Qt.AlignLeft|Qt.AlignTop) button_left = QToolButton(arrow_button_widget) button_left.setArrowType(Qt.LeftArrow) button_left.clicked.connect(self.on_toggle_left) layout.addWidget(button_left) button_right = QToolButton(arrow_button_widget) button_right.setArrowType(Qt.RightArrow) button_right.clicked.connect(self.on_toggle_right) layout.addWidget(button_right) arrow_button_widget.setLayout(layout) self.left_arrow_dict[button_left] = parent_idx self.right_arrow_dict[button_right] = parent_idx layout.setAlignment(Qt.AlignTop) arrow_button_widget.setMaximumHeight(25) return arrow_button_widget #Back/Forward Buttons Actions def on_toggle_left(self): #(self, is_clicked) #print(self.sender()) #print(self.left_arrow_dict[self.sender()]) self.next_figure(self.left_arrow_dict[self.sender()], -1) def on_toggle_right(self): #(self, is_clicked) #print(self.sender()) #print(self.right_arrow_dict[self.sender()]) self.next_figure(self.right_arrow_dict[self.sender()], 1) def next_figure(self, det=0, idx=0): if det not in self.canvas_fig_dict: return fig = self.canvas_fig_dict[det] no_plots_seq = len(self.multiple_figure_dict[fig]) self.canvas_current_idx[det] = ( self.canvas_current_idx[det] + idx) % no_plots_seq if self.canvas[det] is not None: self.sub_results_layout[det].removeWidget(self.canvas[det]) self.canvas[det].deleteLater() if self.nav[det] is not None: self.sub_results_layout[det].removeWidget(self.nav[det]) self.nav[det].deleteLater() if fig in self.multiple_figure_dict: if self.multiple_figure_dict[fig]: self.canvas[det] = FigureCanvasQTAgg(self.multiple_figure_dict[ fig][self.canvas_current_idx[det]]) self.sub_results_layout[det].addWidget(self.canvas[det]) self.sub_results_wgt[det].setLayout( self.sub_results_layout[det]) self.nav[det] = NavigationToolbar( self.canvas[det], self.sub_results_wgt[det], coordinates=False) self.nav[det].setMinimumWidth(400) self.nav[det].setMinimumHeight(50) self.nav[det].setVisible(True) self.nav[det].setStyleSheet("QToolBar { border: 0px }") def canvas_update(self, fig_data, idx=0): #place figures in a list self.parent.clear_screenshot() self.multiple_figure_dict = fig_data for i, key in enumerate(self.multiple_figure_dict.keys()): self.canvas_fig_dict[i] = key print("UPDATE", flush=True) #print(len(self.canvas), len(self.nav), len(self.sub_results_layout), # len(self.multiple_figure_dict.keys(), flush=True)) for i, (canvas, nav, wgt, layout, key) in enumerate( zip(self.canvas, self.nav, self.sub_results_wgt, self.sub_results_layout, self.multiple_figure_dict.keys())): if canvas is not None: layout.removeWidget(canvas) canvas.deleteLater() if nav is not None: nav.deleteLater() if self.multiple_figure_dict[key]: #'Figure 1']: if not isinstance(self.multiple_figure_dict[key], list): temp_list = [] temp_list.append(self.multiple_figure_dict[key]) self.multiple_figure_dict[key] = temp_list self.canvas[i] = FigureCanvasQTAgg( self.multiple_figure_dict[key][idx]) self.sub_results_layout[i].addWidget(self.canvas[i]) self.nav[i] = NavigationToolbar( self.canvas[i], self.sub_results_wgt[i], coordinates=False) self.nav[i].setMinimumWidth(400) self.nav[i].setMinimumHeight(50) #self.nav[i].setContentsMargins(0, 0, 0, 0) self.nav[i].setVisible(True) self.nav[i].setStyleSheet("QToolBar { border: 0px }") self.parent.add_screenshot(title=self.results_tab_wgt_titles[i], item=wgt) else: self.canvas[i] = None self.nav[i] = None def canvas_update_2(self, fig_data, idx=0): #place figures in a list #Only clear last result fro this canvas #self.parent.clear_screenshot() self.multiple_figure_dict_2 = fig_data for i, key in enumerate(self.multiple_figure_dict_2.keys()): self.canvas_fig_dict_2[i] = key print("UPDATE==2", flush=True) #print(len(self.canvas), len(self.nav), len(self.sub_results_layout), # len(self.multiple_figure_dict.keys(), flush=True)) for i, (canvas, nav, wgt, layout, key) in enumerate( zip(self.canvas_2, self.nav_2, self.sub_results_wgt_2, self.sub_results_layout_2, self.multiple_figure_dict_2.keys())): if canvas is not None: layout.removeWidget(canvas) canvas.deleteLater() if nav is not None: nav.deleteLater() if self.multiple_figure_dict_2[key]: #'Figure 1']: if not isinstance(self.multiple_figure_dict_2[key], list): temp_list = [] temp_list.append(self.multiple_figure_dict_2[key]) self.multiple_figure_dict_2[key] = temp_list self.canvas_2[i] = FigureCanvasQTAgg( self.multiple_figure_dict_2[key][idx]) self.sub_results_layout_2[i].addWidget(self.canvas_2[i]) self.nav_2[i] = NavigationToolbar( self.canvas_2[i], self.sub_results_wgt_2[i], coordinates=False) self.nav_2[i].setMinimumWidth(400) self.nav_2[i].setMinimumHeight(50) #self.nav[i].setContentsMargins(0, 0, 0, 0) self.nav_2[i].setVisible(True) self.nav_2[i].setStyleSheet("QToolBar { border: 0px }") self.parent.clear_screenshot(self.results_tab_wgt_titles_2[i], wgt) self.parent.add_screenshot( title=self.results_tab_wgt_titles_2[i], item=wgt) else: self.canvas_2[i] = None self.nav_2[i] = None def prepare_qlineread(self, line, value): line.setObjectName("Read") line.setFixedHeight(24) line.setText(str(value)) line.setAlignment(Qt.AlignRight | Qt.AlignBottom) line.setStyleSheet("QLabel{text-align: right}") fm = QFontMetricsF(line.font()) param_width = fm.maxWidth() * (len(str(value))*0.5) + 20 line.setMaximumWidth(param_width) def prepare_qlineedit(self, line, value, link, title, key): has_min = False has_max = False is_float = False is_int = False no_decimals = 0 try: min_val = self.settings.data[link][title][key]["data"]["min"] has_min = True if isinstance(min_val, float): is_float = True d = abs(Decimal(str(min_val)).as_tuple().exponent) no_decimals = max(no_decimals, d) elif isinstance(min_val, int): is_int = True except KeyError: pass try: max_val = self.settings.data[link][title][key]["data"]["max"] has_max = True if isinstance(max_val, float): is_float = True d = abs(Decimal(str(max_val)).as_tuple().exponent) no_decimals = max(no_decimals, d) elif isinstance(max_val, int): is_int = True except KeyError: pass validator = None #QDoubleValidate only controls the number of digits if is_float: validator = QDoubleValidator() validator.setNotation(QDoubleValidator.StandardNotation) validator.setDecimals(no_decimals) elif is_int: validator = QIntValidator() if validator: validator_str = "Valid range " if has_min: validator.setBottom(min_val) validator_str += "from {} ".format(min_val) if has_max: validator.setTop(max_val) validator_str += "to {}".format(max_val) line.setToolTip(validator_str) line.setValidator(validator) line.setObjectName("Write") line.setFixedHeight(24) line.setText(str(value)) fm = QFontMetricsF(line.font()) #param_width = fm.width(str(value)) #extra_width = 22 if len(str(value)) < 16 else 26 #line.setFixedWidth(param_width + extra_width) param_width = fm.maxWidth() * (len(str(value))*0.5) + 20 line.setMaximumWidth(param_width) return line def prepare_qspinbox(self, line, value, link, title, key): min_val = self.settings.data[link][title][key]["data"]["min"] max_val = self.settings.data[link][title][key]["data"]["max"] step = self.settings.data[link][title][key]["data"]["step"] line.setObjectName("Write") line.setFixedHeight(24) line.setRange(min_val, max_val) line.setSingleStep(step) line.setValue(value) line.setAlignment(Qt.AlignRight) #required fm = QFontMetricsF(line.font()) param_width = fm.maxWidth() * (len(str(max_val)) + 0.2) line.setMaximumWidth(param_width) return line def prepare_qdoublespinbox(self, line, value, link, title, key): min_val = self.settings.data[link][title][key]["data"]["min"] max_val = self.settings.data[link][title][key]["data"]["max"] step = self.settings.data[link][title][key]["data"]["step"] line.setObjectName("Write") line.setFixedHeight(24) decimal = Decimal(str(step)) line.setDecimals(abs(decimal.as_tuple().exponent)) #precision line.setRange(min_val, max_val) line.setSingleStep(step) line.setValue(value) line.setAlignment(Qt.AlignRight) #required fm = QFontMetricsF(line.font()) param_width = fm.maxWidth() * (len(str(max_val))) # + len(str(step))) line.setMaximumWidth(param_width) return line def prepare_qcombobox(self, line, value, link, title, key): line.clear() line.addItems(value) line.setFixedHeight(26) line.setObjectName("Write") value_for_width = max(value, key=len) fm = QFontMetricsF(line.font()) param_width = fm.maxWidth() * (len(str(value_for_width)) * 0.5) # + len(str(step))) line.setFixedWidth(param_width) return line def prepare_qcheckbox(self, line, value, link, title, key): line.setChecked(Qt.Checked if value else Qt.Unchecked) line.setMaximumWidth(320) line.setMinimumWidth(100) line.setFixedHeight(28) try: orientation = self.settings.data[link][title][key]["data"][ "orientation"] orientation = Qt.RightToLeft if "RightToLeft" in orientation \ else Qt.LeftToRight line.setLayoutDirection(orientation) except KeyError as ex: print(ex, flush=True) pass return line def prepare_qradiobutton(self, value, link, title, key): widget = QWidget() layout = QGridLayout() layout.setContentsMargins(5, 0, 5, 20) widget.setMaximumWidth(len(value)*100) target_list = value color_list = ["#894961", "teal", "darkblue"] width_list = [70, 70, 70] self.radiobutton = [None] * len(target_list) for i, name in enumerate(target_list): self.radiobutton[i] = QRadioButton(name) full_width = len(target_list) + 1 layout.addWidget(QHLine(), 0, 0, 1, full_width) layout.addWidget(QLabel( self.settings.data[link][title][key]['data']['text']), 1, 0, 1, full_width, Qt.AlignCenter) for i, (radio, target, color, width) in enumerate( zip(self.radiobutton, target_list, color_list, width_list)): radio.setFont(self.font_pts10) radio.setStyleSheet("color : {0};".format(color)) radio.val = target radio.title = title radio.key = key layout.addWidget(radio, 2, i, 1, 1, Qt.AlignCenter) layout.addWidget(QHLine(), 3, 0, 1, layout.columnCount()) layout.setSpacing(0) widget.setLayout(layout) return widget ''' line.setContentsMargins(0, 0, 0, 0) rblist = line.findChildren(QRadioButton) for rb in rblist: rb.toggled.connect(on_radiobutton_change) wgt_grid.addWidget(line, irow, 0, 1, 4, Qt.AlignLeft) _idx=0 try: if key in self.settings.data["Expert"].keys(): top_key = "Expert" elif key in self.settings.data["Parameters"].keys(): top_key = "Parameters" _choice =self.settings.data[top_key][key]['data']['default'] _values = self.settings.data[top_key][key]['data']['value'] if isinstance(_choice, (str, bytes)): _idx = _values.index(_choice) elif isinstance(_choice, int): _idx = _choice if _choice < len(_values) else 0 except KeyError: pass rblist[_idx].setChecked(True) rblist[_idx].toggled.emit(True) return line ''' def qtab_wgt(self, key, irow, wgt_grid): if key in self.settings.data["Expert"].keys(): top_key = "Expert" elif key in self.settings.data["Parameters"].keys(): top_key = "Parameters" def emboss(): line.title = title line.key = inner_key line.val = value def check_cb(new_value): sender= self.sender() self.parent.input_parameters[sender.title][sender.key] = new_value def line_cb(new_value): sender= self.sender() self.parent.input_parameters[sender.title][sender.key] = new_value def on_radiobutton_change(new_value): sender = self.sender() #print("rb callback ", new_value, sender.isChecked(), sender.val, flush=True) if sender.isChecked(): self.parent.input_parameters[sender.title][sender.key] = sender.val def on_tab_change(new_idx): sender_tab = self.sender().qtab_widget_dict[new_idx].title meas_line.setText(sender_tab) try: _color = self.settings.data[top_key][key]["data"]["color"][ new_idx] except KeyError as ex: _color = "black" except IndexError as ex: _color = "black" meas_line.setStyleSheet("color: {0}".format(_color)) for i in range (0, self.sender().count()): if i == new_idx: self.sender().tabBar().setTabTextColor(new_idx, QColor(_color)) else: self.sender().tabBar().setTabTextColor(i, QColor("black")) self.parent.input_parameters[key] = sender_tab qtab_widget = QTabWidget() qtab_widget_dict = {} itab = 0 longest_title = "short" link = self.settings.data[top_key][key]["data"]["link"] for title, tab in self.settings.data[link].items(): lo = QGridLayout() qw = QWidget() _irow = 0 _irowspan = 1 tab_height = 0 if len(title) > len(longest_title): longest_title = title self.parent.input_parameters[title] = {} for inner_key, param in tab.items(): tab_height +=32 wgt_type = param["data"]["widget"] text = param["data"]["text"] if "value" in param["data"]: value = param["data"]["value"] elif "link" in param["data"]: link_list = param["data"]["link"] print("ll", link_list, flush=True) if len(link_list) == 1: value = self.settings.data[link_list[0]] print("values", value, flush=True) else: bottom_leaf = self.settings.data[link_list[0]] for link_item in link_list[1:]: bottom_leaf = bottom_leaf[link_item] value = bottom_leaf label = None def add_label(): label = QLabel(text) label.setFixedHeight(24) label.setFont(self.font_pts10) label.setContentsMargins(5, 0, 0, 0) return label if "QSpinBox" in wgt_type: line = QSpinBox() label = add_label() emboss() self.prepare_qspinbox(line, value, link, title, inner_key) line.valueChanged.connect(line_cb) line.valueChanged.emit(value) elif "QDoubleSpinBox" in wgt_type: line = QDoubleSpinBox() label = add_label() emboss() self.prepare_qdoublespinbox(line, value, link, title, inner_key) line.valueChanged.connect(line_cb) line.valueChanged.emit(value) elif "QLineEdit" in wgt_type: line = QLineEdit() label = add_label() emboss() self.prepare_qlineedit(line, value, link, title, inner_key) line.textEdited.connect(line_cb) line.textEdited.emit(str(value)) elif "QLineRead" in wgt_type: line = QLineEdit() label = add_label() emboss() self.prepare_qlineread(line, value) line.textEdited.connect(line_cb) line.textEdited.emit(str(value)) elif "QCheckBox" in wgt_type: line = QCheckBox(text) emboss() self.prepare_qcheckbox(line, value, link, title, inner_key) line.stateChanged.connect(check_cb) line.stateChanged.emit(bool(value)) elif "QComboBox" in wgt_type: line = QComboBox() label = add_label() emboss() self.prepare_qcombobox(line, value, link, title, inner_key) line.currentTextChanged.connect(line_cb) line.setCurrentIndex(0) line.currentTextChanged.emit(value[0]) elif "QRadioButton" in wgt_type: tab_height += 32 _irowspan = 2 line = self.prepare_qradiobutton(value, link, title, inner_key) #emboss() rblist = line.findChildren(QRadioButton) for rb in rblist: rb.toggled.connect(on_radiobutton_change) if "startIdx" in param["data"]: start_idx = param["data"]["startIdx"] else: start_idx = 0 rblist[start_idx].setChecked(True) rblist[start_idx].toggled.emit(True) icol = 0 icolspan = 3 if label: lo.addWidget(label, _irow, icol, _irowspan, 1, Qt.AlignLeft | Qt.AlignVCenter) icol += 1 lo.addWidget(line, _irow, icol, _irowspan, icolspan, Qt.AlignLeft | Qt.AlignVCenter) #print(title, tab) #print(key, line, icolumn, wgt_type, text, flush=True) _irow += 1 qw.setLayout(lo) qw.setFixedHeight(tab_height) qtab_widget_dict[itab] = qw qtab_widget_dict[itab].title = title qtab_widget.addTab(qtab_widget_dict[itab], title) itab += 1 qtab_widget.qtab_widget_dict = qtab_widget_dict meas_label = QLabel() meas_label.setFixedHeight(24) meas_label.setFont(self.font_pts10) meas_label.setContentsMargins(5, 0, 0, 0) meas_label.setText(self.settings.data[top_key][key]["data"]["text"]) meas_line = QLineEdit() meas_line.setObjectName("Read") fm = QFontMetricsF(meas_line.font()) #param_width = fm.width(longest_title) #extra_width = 22 if len(str(value)) < 15 else 28 #meas_line.setFixedWidth(param_width + extra_width) param_width = fm.maxWidth() * len(longest_title) meas_line.setMaximumWidth(param_width*0.5) qtab_widget.setContentsMargins(0, 40, 0, 0) qtab_widget.currentChanged.connect(on_tab_change) _default_idx = self.settings.data[top_key][key]["data"]["value"] qtab_widget.setCurrentIndex(_default_idx) qtab_widget.currentChanged.emit(_default_idx) final_wgt = QWidget() final_lo = QGridLayout() final_lo.addWidget(meas_label, 0, 0, 1, 2, Qt.AlignLeft | Qt.AlignVCenter) final_lo.addWidget(meas_line, 0, 2, 1, 2, Qt.AlignLeft | Qt.AlignVCenter) qHLine = QHLine() qHLine.setFixedHeight(10) final_lo.addWidget(qHLine, 1, 0, 1, 4) final_lo.addWidget(qtab_widget, 2, 0, 1, 4, Qt.AlignHCenter | Qt.AlignVCenter) #final_lo.setSpacing(0) final_wgt.setLayout(final_lo) ''' wgt_grid.addWidget(meas_label, irow, 0, 1, 2, Qt.AlignLeft | Qt.AlignVCenter) wgt_grid.addWidget(meas_line, irow, 2, 1, 2, Qt.AlignLeft | Qt.AlignVCenter) qHLine = QHLine() qHLine.setFixedHeight(10) wgt_grid.addWidget(qHLine, irow+1, 0, 1, 4) wgt_grid.addWidget(qtab_widget, irow+2, 0, 1, 4, Qt.AlignHCenter | Qt.AlignVCenter) ''' return final_wgt #qtab_widget # def pv_stacked_wgt(self, pvlist, title, default_value, key, object_name): """GUI Input Widget""" stacked_widget = QStackedWidget() self.cafe.openPrepare() self.cafe.open(pvlist) self.cafe.openNowAndWait(0.4) for pv in pvlist: self.cafe.setGetActionWhenMonitorPolicy( pv, self.cyca.GET_FROM_CACHE) stack_list = [] for pv, head, val, obj in zip(pvlist, title, default_value, object_name): stack, radiobutton_list = self.pv_selector_wgt( title=head, key=key, pv=pv, manual_value=val) self.stack_radiolist_dict[stack] = radiobutton_list stack_list.append(stack) stack.setObjectName(obj) stack.style().polish(stack) stacked_widget.addWidget(stack) stacked_widget.setMaximumHeight(320) self.stacked_wgt_dict[stacked_widget] = stack_list return stacked_widget def pv_energy_stacked_wgt(self): pvlist = self.settings.data["EnergyStackedWgt"]["pvlist"] title = self.settings.data["EnergyStackedWgt"]["title"] default_value = self.settings.data["EnergyStackedWgt"]["value"] key = self.settings.data["EnergyStackedWgt"]["key"] object_name = self.settings.data["EnergyStackedWgt"]["objectName"] self.energy_stacked_widget = self.pv_stacked_wgt( pvlist, title, default_value, key, object_name) radio_list = self.stack_radiolist_dict[ self.energy_stacked_widget.currentWidget()] for radio in radio_list: if radio.isChecked(): radio.toggled.emit(True) return self.energy_stacked_widget def pv_phase_stacked_wgt(self): pvlist = self.settings.data["PhaseStackedWgt"]["pvlist"] title = self.settings.data["PhaseStackedWgt"]["title"] default_value = self.settings.data["PhaseStackedWgt"]["value"] key = self.settings.data["PhaseStackedWgt"]["key"] object_name = self.settings.data["PhaseStackedWgt"]["objectName"] self.phase_stacked_widget = self.pv_stacked_wgt( pvlist, title, default_value, key, object_name) radio_list = self.stack_radiolist_dict[ self.phase_stacked_widget.currentWidget()] for radio in radio_list: if radio.isChecked(): radio.toggled.emit(True) return self.phase_stacked_widget def pv_selector_wgt(self, title="Select", key="beamEnergy", pv=None, manual_value="", monitor=True, read=True, manual=True): group_box = QGroupBox(title) group_box.setObjectName("INNERCENTER") grid = QGridLayout() radio_buddy_text_dict = {} radiobutton_title = ["Monitor", "Update", "Manual"] radiobutton_flag = [monitor, read, manual] radiobutton_list = [None] * len(radiobutton_title) pv_local = pv monitor_pv = None def cb_pv_selector(value): value_str = radio_buddy_text_dict[self.sender()].text() #print("cb_pv_selector", value_str) #Find which group the radio button belongs to: #Find which stack the group belongs to: #Set this to the current stack? for k, v in self.stack_radiolist_dict.items(): if self.sender() in v: #get groupbox from key for k2, v2 in self.stacked_wgt_dict.items(): if k in v2: self.current_stacked_wgt_dict[k2] = pv_local #self.sender() #print("Current STACK FOUND") self.radio_stack_dict[self.sender()] = value if not value_str: print("Fired on Initialization when not connected", value) print("sender", self.sender()) print("radio_buddy_text", radio_buddy_text_dict) print("value", radio_buddy_text_dict[self.sender()].text()) self.input_parameters[key] = 0 return if hasattr(radio_buddy_text_dict[self.sender()], "pv_info"): dt = radio_buddy_text_dict[self.sender()].pv_info.dataType if dt not in [self.cyca.CY_DBR_STRING, self.cyca.CY_DBR_ENUM, self.cyca.CY_DBR_CHAR]: value_input = re.findall(r"-?\d+\.?\d*", value_str) if dt in [self.cyca.CY_DBR_FLOAT, self.cyca.CY_DBR_DOUBLE]: self.input_parameters[key] = float(value_input[0]) elif dt in [self.cyca.CY_DBR_SHORT, self.cyca.CY_DBR_LONG]: self.input_parameters[key] = int(value_input[0]) else: self.input_parameters[key] = value_str else: self.input_parameters[key] = value_str for irow, (radio, flag) in enumerate(zip(radiobutton_list, radiobutton_flag)): radio = QRadioButton("") radio.toggled.connect(cb_pv_selector) radio.setFont(self.font_pts10) radio.setFixedWidth(15) radiobutton_list[irow] = radio if flag: grid.addWidget(radio, irow, 0) if monitor: ql = QLabel(radiobutton_title[0]) ql.setFont(self.font_pts10) ql.setFixedHeight(self.widget_height) ql.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) grid.addWidget(ql, 0, 1) if manual: ql = QLabel(radiobutton_title[2]) ql.setFont(self.font_pts10) ql.setFixedHeight(self.widget_height) ql.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) grid.addWidget(ql, 2, 1) def mon_cb(handle, pvname, pvdata): if monitor_pv is not None: monitor_pv.py_monitor_callback(handle, pvname, pvdata) if pvname in self.current_stacked_wgt_dict.values() and \ radiobutton_list[0].isChecked(): # self.input_parameters[key] = pvdata.value[0] if monitor: monitor_pv = CAQLabel(self, pv_name=pv, monitor_callback=mon_cb, show_units=True) monitor_pv.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) monitor_pv.setFont(self.font_pts10) monitor_pv.setFixedHeight(self.widget_height) monitor_pv.setFixedWidth(122) grid.addWidget(monitor_pv, 0, 2, 1, 2) if read: read_pv = CAQLabel(self, pv_name=pv, pv_within_daq_group=True, color_mode="static", show_units=True) read_pv.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) #read_pv.setObjectName("Readback") #read_pv.setProperty("static", True) #read_pv.setStyleSheet('background-color: rgb(255, 255,223)') read_pv.setFont(self.font_pts10) init_value = self.cafe.getCache(pv) if init_value is not None: val = read_pv.format_display_value(init_value) read_pv.setText(str(val)) read_pv.setFixedHeight(self.widget_height) read_pv.setFixedWidth(122) grid.addWidget(read_pv, 1, 2, 1, 2) def update_read_value(): #print("sender = ", self.sender()) pvdata = self.cafe.getPV(pv) init_value = pvdata.value[0] if init_value is not None: #value_str = read_pv.format_display_value(init_value) #read_pv.setText(str(value_str)) #print(len(read_pv.text())) #val = re.findall(r'-?\d+\.?\d*', value_str) #print (val) #val[0] = val[0] #add space that we stripped off read_pv.receive_monitor_update(init_value, pvdata.status, pvdata.alarmSeverity) if radiobutton_list[1].isChecked(): radiobutton_list[1].toggled.emit(True) if read: qpb = QPushButton(radiobutton_title[1]) qpb.setObjectName("Update") qpb.pressed.connect(update_read_value) qpb.setFixedHeight(self.widget_height) qpb.setFixedWidth(60) grid.addWidget(qpb, 1, 1) qpb.pressed.emit() def update_manual_value(new_text): if radiobutton_list[2].isChecked(): radiobutton_list[2].toggled.emit(True) if manual: manual_wgt = QLineEdit(str(manual_value)) manual_wgt.setObjectName("WriteCenter") manual_wgt.textChanged.connect(update_manual_value) manual_wgt.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) manual_wgt.setFixedHeight(self.widget_height) manual_wgt.setFixedWidth(74) grid.addWidget(manual_wgt, 2, 2) ql = QLabel(self.cafe.getUnits(pv)) ql.setFont(self.font_pts10) ql.setFixedHeight(self.widget_height) ql.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) grid.addWidget(ql, 2, 3) grid.setContentsMargins(5, 10, 5, 0) #grid.setContentsMargins(9, 15, 9, 0) grid.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) grid.setVerticalSpacing(2) grid.setHorizontalSpacing(4) group_box.setContentsMargins(0, 0, 0, 0) group_box.setMaximumWidth(280) group_box.setMaximumHeight(320) group_box.setFont(self.font_pts10) group_box.setAlignment(Qt.AlignTop | Qt.AlignHCenter) group_box.setLayout(grid) if monitor: radio_buddy_text_dict[radiobutton_list[0]] = monitor_pv if read: radio_buddy_text_dict[radiobutton_list[1]] = read_pv if manual: radio_buddy_text_dict[radiobutton_list[2]] = manual_wgt idx = 0 #1 if read else 0 #print("idx", idx) #print(radio_buddy_text_dict) radiobutton_list[idx].setChecked(True) radiobutton_list[idx].toggled.emit(True) return group_box, radiobutton_list def input_wgt_logging(self, irow, wgt_grid): tooltip = "Select logging level, CRITICAL=50, DEBUG=10" value = ['CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG'] #NOTSET=0 label = QLabel("Logging level:") label.setFixedHeight(24) label.setFont(self.font_pts10) label.setContentsMargins(5, 0, 0, 0) label.setToolTip(tooltip) return self.input_wgt_qcombobox(label, "loggingLevel", value, irow, wgt_grid) def input_wgt_qcombobox(self, label, key, value, irow, wgt_grid): suggested = "WW" if key in self.settings.data["Expert"].keys(): top_key = "Expert" elif key in self.settings.data["Parameters"].keys(): top_key = "Parameters" else: top_key = "Expert" def combo_cb(new_text): self.input_parameters[key] = new_text if 'loggingLevel' in key: self.parent.logger.setLevel(new_text) line = QComboBox() line.clear() line.addItems(value) line.setFixedHeight(26) line.currentTextChanged.connect(combo_cb) line.setCurrentIndex(0) line.currentTextChanged.emit(value[0]) value_for_width = max(value, key=len) fm = QFontMetricsF(line.font()) param_width = max(fm.width(str(value_for_width)), fm.width(suggested)) line.setFixedWidth(param_width + 56) line.setObjectName("Write") if key in self.settings.data[top_key]: if "layout" in self.settings.data[top_key][key]['data']: if "VERT" in self.settings.data[top_key][key]['data'][ 'layout'].upper(): box = QVBoxLayout() label.setAlignment(Qt.AlignBottom) else: box = QHBoxLayout() label.setAlignment(Qt.AlignCenter) else: box = QVBoxLayout() label.setAlignment(Qt.AlignBottom) else: box = QHBoxLayout() label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) box.setAlignment(Qt.AlignCenter) box.addWidget(label) box.addWidget(line) if self.grid_loc: a = self.grid_loc[0] b = self.grid_loc[1] c = self.grid_loc[2] d = self.grid_loc[3] else: a = irow b = 0 c = 1 d = 3 if 'loggingLevel' not in key: wgt_grid.addLayout(box, a, b, c, d, Qt.AlignLeft | Qt.AlignVCenter) return box if 'loggingLevel' in key else line def input_wgt_qradiobutton(self, key, value, irow, wgt_grid): def on_radiobutton_change(new_value): radio_button = self.sender() if radio_button.isChecked(): self.parent.input_parameters[key] = radio_button.target #print("KEY================", key, self.parent.input_parameters[key], new_value) if key in self.settings.data["Expert"].keys(): top_key = "Expert" elif key in self.settings.data["Parameters"].keys(): top_key = "Parameters" if 'forwardLink' in self.settings.data[top_key][key]['data']: link = self.settings.data[top_key][key]['data'][ 'forwardLink'][0] for item in self.settings.data[link][radio_button.target]: value_list = self.settings.data[link][radio_button.target][ item] if item in self.line_sender_dict: if self.settings.data[top_key][item]['data'][ 'widget'] in 'QComboBox': self.line_sender_dict[item].clear() self.line_sender_dict[item].addItems(value_list) self.line_sender_dict[item].setCurrentIndex(0) self.line_sender_dict[item].currentTextChanged.emit( value_list[0]) elif self.settings.data[top_key][item]['data'][ 'widget'] in 'QLineEdit': self.line_sender_dict[item].setText(str(value_list)) line = self.radio_buttons(key=key, options=value) line.setContentsMargins(0, 0, 0, 0) rblist = line.findChildren(QRadioButton) for rb in rblist: rb.toggled.connect(on_radiobutton_change) if self.grid_loc: a = self.grid_loc[0] b = self.grid_loc[1] c = self.grid_loc[2] d = self.grid_loc[3] else: a = irow b = 0 c = 1 d = 4 wgt_grid.addWidget(line, a, b, c, d, Qt.AlignLeft) _idx=0 try: if key in self.settings.data["Expert"].keys(): top_key = "Expert" elif key in self.settings.data["Parameters"].keys(): top_key = "Parameters" _choice =self.settings.data[top_key][key]['data']['default'] _values = self.settings.data[top_key][key]['data']['value'] if isinstance(_choice, (str, bytes)): _idx = _values.index(_choice) elif isinstance(_choice, int): _idx = _choice if _choice < len(_values) else 0 except KeyError: pass rblist[_idx].setChecked(True) rblist[_idx].toggled.emit(True) return line def input_wgt_qcheckbox(self, key, value, label_text, irow, wgt_grid): def check_cb(new_value): self.parent.input_parameters[key] = bool(new_value) line = QCheckBox(label_text) line.setChecked(Qt.Checked if value else Qt.Unchecked) line.stateChanged.connect(check_cb) line.setMaximumWidth(320) line.setMinimumWidth(100) line.setFixedHeight(28) if key in self.settings.data["Expert"].keys(): top_key = "Expert" elif key in self.settings.data["Parameters"].keys(): top_key = "Parameters" col_wdt = 4 try: orientation = self.settings.data[top_key][key]["data"][ "orientation"] orientation = Qt.RightToLeft if "RightToLeft" in orientation \ else Qt.LeftToRight line.setLayoutDirection(orientation) col_wdt=3 except KeyError: pass if self.grid_loc: a = self.grid_loc[0] b = self.grid_loc[1] c = self.grid_loc[2] d = self.grid_loc[3] else: a = irow b = 0 c = 1 d = col_wdt wgt_grid.addWidget(line, a, b, c, d, Qt.AlignLeft | Qt.AlignVCenter) return line def input_wgt_qlabel(self, value, irow, wgt_grid): #Read only has no callback suggested = "WWWW" line = QLabel() line.setText(str(value)) #"{0: 3.1f}".format(value)) line.setFixedHeight(24) line.setAlignment(Qt.AlignRight | Qt.AlignBottom) line.setStyleSheet("QLabel{text-align: right}") fm = QFontMetricsF(line.font()) param_width = max(fm.width(str(value)), fm.width(suggested)) line.setMaximumWidth(param_width + 40) if self.grid_loc: a = self.grid_loc[0] b = self.grid_loc[1] c = self.grid_loc[2] d = self.grid_loc[3] else: a = irow b = 0 c = 1 d = 4 wgt_grid.addWidget(line, a, b, c, d) return line def input_wgt_qlineread(self, key, value, label, irow, wgt_grid): def line_cb(new_value): self.parent.input_parameters[key] = new_value #print("NEW VALUE FROM BASE GUI FRAME", new_value) suggested = "WW" line = QLineEdit() line.setObjectName("Read") line.setText(str(value)) #"{0: 3.1f}".format(value)) line.setFixedHeight(26) line.textChanged.connect(line_cb) #line.textEdited.connect(line_cb) line.textChanged.emit(str(value)) line.setAlignment(Qt.AlignRight | Qt.AlignBottom) line.setStyleSheet("QLabel{text-align: right}") try: w =self.settings.data["Parameters"][key]["data"]["width"] line.setFixedWidth(w) except KeyError: fm = QFontMetricsF(line.font()) param_width = max(fm.width(str(value)), fm.width(suggested)) extra_width = 22 if len(str(value)) < 16 else 26 line.setFixedWidth(param_width + extra_width) qframe_buffer = QFrame() qframe_buffer.setFixedWidth(3) if self.grid_loc: a = self.grid_loc[0] b = self.grid_loc[1] c = self.grid_loc[2] d = self.grid_loc[3] else: a = irow b = 0 c = 1 d = 1 wgt_grid.addWidget(label, a, b, c, d, Qt.AlignLeft | Qt.AlignVCenter) wgt_grid.addWidget(qframe_buffer, a, b+1, c, d, Qt.AlignLeft | Qt.AlignVCenter) wgt_grid.addWidget(line, a, b+2, c, d, Qt.AlignLeft | Qt.AlignVCenter) #wgt_grid.addWidget(label, irow, self.input_wgt_grid_column, 1, 1, # Qt.AlignLeft | Qt.AlignVCenter) #wgt_grid.addWidget(qframe_buffer, irow, self.input_wgt_grid_column+1, # 1, 1, Qt.AlignLeft | Qt.AlignVCenter) #wgt_grid.addWidget(line, irow, self.input_wgt_grid_column+2, 1, 1, # Qt.AlignLeft | Qt.AlignVCenter) return line def input_wgt_qlineedit(self, key, value, label, irow, wgt_grid): def line_cb(new_value): self.parent.input_parameters[key] = new_value line = QLineEdit() line.setObjectName("Write") line.setFixedHeight(24) line.textEdited.connect(line_cb) line.setText(str(value)) fm = QFontMetricsF(line.font()) param_width = fm.width(str(value)) extra_width = 22 if len(str(value)) < 16 else 26 line.setFixedWidth(param_width + extra_width) if self.grid_loc: a = self.grid_loc[0] b = self.grid_loc[1] c = self.grid_loc[2] d = self.grid_loc[3] else: a = irow b = 0 c = 1 d = 2 wgt_grid.addWidget(label, a, b, c, d, Qt.AlignLeft | Qt.AlignVCenter) wgt_grid.addWidget(line, a, b+2, c, d, Qt.AlignLeft | Qt.AlignVCenter) #wgt_grid.addWidget(label, irow, self.input_wgt_grid_column, 1, 2, Qt.AlignLeft | Qt.AlignVCenter) #wgt_grid.addWidget(line, irow, self.input_wgt_grid_column+2, 1, 2, Qt.AlignLeft | Qt.AlignVCenter) return line def input_wgt_qdoublespinbox(self, key, value, label, irow, wgt_grid): def line_cb(new_value): self.parent.input_parameters[key] = new_value suggested = "WWWW" line = QDoubleSpinBox() line.setObjectName("Write") line.setFixedHeight(24) line.valueChanged.connect(line_cb) line.setValue(value) if key in self.settings.data["Expert"].keys(): top_key = "Expert" elif key in self.settings.data["Parameters"].keys(): top_key = "Parameters" min_val = self.settings.data[top_key][key]["data"]["min"] max_val = self.settings.data[top_key][key]["data"]["max"] step = self.settings.data[top_key][key]["data"]["step"] try: if 'tooltip' in self.settings.data[top_key][key]["data"]: label.setToolTip(self.settings.data[top_key][key]["data"][ "tooltip"]) except KeyError: pass decimal = Decimal(str(step)) line.setDecimals(abs(decimal.as_tuple().exponent)) #precision line.setRange(min_val, max_val) line.setSingleStep(step) fm = QFontMetricsF(line.font()) param_width = max(fm.width(str(decimal)), fm.width(suggested)) line.setMaximumWidth(param_width + 40) if self.grid_loc: a = self.grid_loc[0] b = self.grid_loc[1] c = self.grid_loc[2] d = self.grid_loc[3] else: a = irow b = 0 c = 1 d = 2 wgt_grid.addWidget(label, a, b, c, d, Qt.AlignLeft | Qt.AlignVCenter) wgt_grid.addWidget(line, a, b+2, c, d, Qt.AlignLeft | Qt.AlignVCenter) #wgt_grid.addWidget(label, irow, self.input_wgt_grid_column, 1, 2, Qt.AlignLeft | Qt.AlignVCenter) #wgt_grid.addWidget(line, irow, self.input_wgt_grid_column+2, 1, 2, Qt.AlignLeft | Qt.AlignVCenter) return line def input_wgt_qspinbox(self, key, value, label, irow, wgt_grid): def line_cb(new_value): self.parent.input_parameters[key] = new_value suggested = "WWWW" line = QSpinBox() line.setObjectName("Write") line.setFixedHeight(24) line.valueChanged.connect(line_cb) line.setValue(value) if key in self.settings.data["Expert"].keys(): top_key = "Expert" elif key in self.settings.data["Parameters"].keys(): top_key = "Parameters" min_val = self.settings.data[top_key][key]["data"]["min"] max_val = self.settings.data[top_key][key]["data"]["max"] step = self.settings.data[top_key][key]["data"]["step"] line.setRange(min_val, max_val) line.setSingleStep(step) fm = QFontMetricsF(line.font()) param_width = max(fm.width(str(max_val)), fm.width(suggested)) line.setMaximumWidth(param_width + 40) if self.grid_loc: a = self.grid_loc[0] b = self.grid_loc[1] c = self.grid_loc[2] d = self.grid_loc[3] else: a = irow b = 0 c = 1 d = 2 wgt_grid.addWidget(label, a, b, c, d, Qt.AlignLeft | Qt.AlignVCenter) wgt_grid.addWidget(line, a, b+2, c, d, Qt.AlignLeft | Qt.AlignVCenter) #wgt_grid.addWidget(label, irow, self.input_wgt_grid_column, 1, 2, Qt.AlignLeft | Qt.AlignVCenter) #wgt_grid.addWidget(line, irow, self.input_wgt_grid_column+2, 1, 2, Qt.AlignLeft | Qt.AlignVCenter) return line def input_wgt_qenergystacked(self, irow, wgt_grid): qframe_top = QFrame() qframe_top.setFixedHeight(3) stacked_wgt = self.pv_energy_stacked_wgt() qframe_bottom = QFrame() qframe_bottom.setFixedHeight(3) if self.grid_loc: a = self.grid_loc[0] b = self.grid_loc[1] c = self.grid_loc[2] d = self.grid_loc[3] else: a = irow b = 0 c = 1 d = 4 wgt_grid.addWidget(qframe_top, a, b, c, d, Qt.AlignLeft | Qt.AlignVCenter) wgt_grid.addWidget(stacked_wgt, a+1, b, c+3, d, Qt.AlignLeft | Qt.AlignVCenter) wgt_grid.addWidget(qframe_bottom, a+2, b, c, d, Qt.AlignLeft | Qt.AlignVCenter) #wgt_grid.addWidget(qframe_top, irow, self.input_wgt_grid_column, 1, 4) #wgt_grid.addWidget(stacked_wgt, irow+1, # self.input_wgt_grid_column, 5, 4) #wgt_grid.addWidget(qframe_bottom, irow+2, # self.input_wgt_grid_column, 1, 4) return stacked_wgt def input_wgt_qphasestacked(self, irow, wgt_grid): qframe_top = QFrame() qframe_top.setFixedHeight(3) stacked_wgt = self.pv_phase_stacked_wgt() qframe_bottom = QFrame() qframe_bottom.setFixedHeight(3) if self.grid_loc: a = self.grid_loc[0] b = self.grid_loc[1] c = self.grid_loc[2] d = self.grid_loc[3] else: a = irow b = 0 c = 1 d = 4 wgt_grid.addWidget(qframe_top, a, b, c, d, Qt.AlignLeft | Qt.AlignVCenter) wgt_grid.addWidget(stacked_wgt, a+1, b, c+3, d, Qt.AlignLeft | Qt.AlignVCenter) wgt_grid.addWidget(qframe_bottom, a+2, b, c, d, Qt.AlignLeft | Qt.AlignVCenter) #wgt_grid.addWidget(qframe_top, irow, self.input_wgt_grid_column, 1, 4) #wgt_grid.addWidget(stacked_wgt, irow+1, # self.input_wgt_grid_column, 1, 4) #wgt_grid.addWidget(qframe_bottom, irow+2, # self.input_wgt_grid_column, 1, 4) return stacked_wgt def input_wgt_qhline(self, irow, wgt_grid): qHLine = QHLine() qHLine.setFixedHeight(10) if self.grid_loc: a = self.grid_loc[0] b = self.grid_loc[1] c = self.grid_loc[2] d = self.grid_loc[3] else: a = irow b = 0 c = 1 d = 4 wgt_grid.addWidget(qHLine, a, b, c, d) #wgt_grid.addWidget(qHLine, irow, self.input_wgt_grid_column, 1, 4) def input_wgt_qvline(self, irow, wgt_grid): qVLine = QVLine() qVLine.setFixedWidth(20) qVLine.setFixedHeight(17 * wgt_grid.rowCount()) if self.grid_loc: a = self.grid_loc[0] b = self.grid_loc[1] c = self.grid_loc[2] d = self.grid_loc[3] else: a = irow b = 0 c = 1 d = 4 wgt_grid.addWidget(qVLine, a, b, c, d) #wgt_grid.addWidget(qVLine, irow, self.input_wgt_grid_column, 1, 4) def input_wgt_frame(self, irow, wgt_grid): qFrame = QFrame() qFrame.setFixedHeight(10) if self.grid_loc: a = self.grid_loc[0] b = self.grid_loc[1] c = self.grid_loc[2] d = self.grid_loc[3] else: a = irow b = 0 c = 1 d = 4 wgt_grid.addWidget(qHLine, a, b, c, d) #wgt_grid.addWidget(qFrame, irow, self.input_wgt_grid_column, 1, 4) def input_wgt(self, buddy, label, key, value, irow=0, wgt_grid=None,): if wgt_grid is None: wgt_grid = self.input_wgt_grid label_text = label label = QLabel(label_text if buddy != "QCheckBox".upper() else "") label.setFixedHeight(24) label.setFont(self.font_pts10) label.setContentsMargins(5, 0, 0, 0) line = None #Add callbacksand actions if buddy == "QComboBox".upper(): line = self.input_wgt_qcombobox(label, key, value, irow, wgt_grid) elif buddy == "QRadioButton".upper(): line = self.input_wgt_qradiobutton(key, value, irow, wgt_grid) elif buddy == "QCheckBox".upper(): line = self.input_wgt_qcheckbox(key, value, label_text, irow, wgt_grid) elif buddy == "QLabel".upper(): line = self.input_wgt_qlabel(value, irow, wgt_grid) elif buddy == "QLineRead".upper(): line = self.input_wgt_qlineread(key, value, label, irow, wgt_grid) elif buddy == "QDoubleSpinBox".upper(): line = self.input_wgt_qdoublespinbox(key, value, label, irow, wgt_grid) elif buddy == "QSpinBox".upper(): line = self.input_wgt_qspinbox(key, value, label, irow, wgt_grid) elif buddy == "QPhaseStackedWidget".upper(): line = self.input_wgt_qphasestacked(irow, wgt_grid) elif buddy == "QEnergyStackedWidget".upper(): line = self.input_wgt_qenergystacked(irow, wgt_grid) elif buddy == "QLineEdit".upper(): line = self.input_wgt_qlineedit(key, value, label, irow, wgt_grid) elif buddy == "QHLine".upper(): self.input_wgt_qhline(irow, wgt_grid) elif buddy == "QVLine".upper(): self.input_wgt_qvline(irow, wgt_grid) elif buddy == "QFrame".upper(): self.input_wgt_qframe(irow, wgt_grid) elif buddy == "QTabWidget".upper(): #print("buddy/label", buddy, label, key, value, flush=True) line = self.qtab_wgt(key, irow, wgt_grid) wgt_grid.addWidget(line, irow, 0, 1, 4, Qt.AlignLeft | Qt.AlignVCenter) else: wgt_list = ["QCheckBox", "QComboBox", "QDoubleSpinBox", "QFrame", "QHLine", "QLabel", "QLineEdit", "QLineRead", "QRadioButton", "QSpinBox", "QTabWidget","QVLine", "QPhaseStackedWidget", "QEnergyStackedWidget"] print("Widget {0} is not Supported".format(buddy)) print("Supported widgets are: {0}".format(wgt_list)) pass if line is not None: self.line_sender_dict[key] = line def enter_input_parameters(self): #"undulator": {"flag": 0, "data" : #{"widget": "None", "text" : "Target:", "value" : "Aramis"}}, #"deflector": {"flag": 1, "data" : #{"widget": "QComboBox", "text" : "RF Tranverse Deflector:", # "forwardLink": ["RFDeflector"]}}, #profileMonitor": {"flag": 1, "data" : #{"widget": "QComboBox", "text" : "Profile Monitor:", # "link": ["RFDeflector", "SINDI01", "profileMonitor"] #json input "Parameters" key # All key values added to self.input_parameters #flag 1: add to self.input_parameters for analysis thread #widget None: does not add widget into container #text: Will add label to a widget #forwardLink: Will substitute for target and execute #link: Will substitute for target #operator self.operator_parameters_group = self.operator_parameters_groupbox() if self.facility == Facility.SwissFEL: try: if self.settings.data["Parameters"]["undulator"]["flag"]: self.operator_parameters_group.layout().addWidget( self.parent.gui_header.operator_group_header()) except KeyError: pass def draw_widget(buddy): flag = self.settings.data["Parameters"][key]["flag"] if buddy == "NONE": return False elif buddy not in ["QHLINE", "QVLINE", "QFRAME"] and not flag: return False return True ##in_j = 0 for (key, val), label in zip(self.all_input_parameters.items(), self.all_input_labels.values()): buddy = self.settings.data[ "Parameters"][key]["data"]["widget"].upper() j = self.input_wgt_grid.rowCount() ##if j > 4: ## self.input_wgt_grid_column = 6 ## in_j += 1 ##else: ## in_j = j #if buddy != "NONE": self.grid_loc = None if "useGrid" in self.settings.data: if self.settings.data["useGrid"]: if 'grid' in self.settings.data["Parameters"][key]["data"]: self.grid_loc = self.settings.data["Parameters"][key]["data"]["grid"] if draw_widget(buddy): self.input_wgt(buddy=buddy, label=label, key=key, value=val, irow=j, wgt_grid=self.input_wgt_grid) ##if j == 4: ## in_j = 1 ##self.input_wgt_grid.setHorizontalSpacing(10) self.operator_parameters_group.layout().addLayout(self.input_wgt_grid) self.operator_parameters_group.setMinimumHeight(INPUT_PARAMETERS_HEIGHT) lo = QGridLayout() lo.setContentsMargins(9, 19, 9, 9) lo.addWidget(self.operator_parameters_group, 0, 0, 4, 1, alignment=Qt.AlignTop) #if random.randint(1, 10) > 9: # lo.addWidget(self.create_analysis_wgt(), 0, 1) #else: if self.has_optics: lo.addWidget(self.optics_groupbox(), 0, 1) row_count = 1 else: row_count = 0 if self.has_procedure: if 'V' in self.orientation_procedure.upper(): lo.addWidget(self.analysis_procedure_group(), row_count, 1, 1, 1) else: lo.addWidget(self.analysis_procedure_group(), lo.rowCount(), 0, 1, 1) #Centers input parameters group box lo.setAlignment(Qt.AlignHCenter | Qt.AlignTop) lo.setHorizontalSpacing(20) lo.setVerticalSpacing(10) self.operator_wgt.setLayout(lo) #expert self.expert_parameters_group = self.expert_parameters_groupbox() for (key, val), label in zip(self.all_expert_parameters.items(), self.all_expert_labels.values()): self.grid_loc = None if "useGrid" in self.settings.data: if self.settings.data["useGrid"]: if 'grid' in self.settings.data["Expert"][key]["data"]: self.grid_loc = self.settings.data["Expert"][key]["data"]["grid"] buddy = self.settings.data[ "Expert"][key]["data"]["widget"].upper() j = self.expert_wgt_grid.rowCount() if buddy != "NONE": self.input_wgt(buddy=buddy, label=label, key=key, value=val, irow=j, wgt_grid=self.expert_wgt_grid) #Add expert parameters to input_parameters self.input_parameters.update(self.expert_parameters) #Need to emit radio button here as input_parameters is overwritten by sel.expoert_parameters #which give a list [Gasusian and FWMH] if self.radiobutton: self.radiobutton[0].toggled.emit(True) if self.expert_wgt_grid: self.expert_parameters_group.layout().addLayout( self.expert_wgt_grid) if "pipeline" in self.parent.input_parameters.keys(): self.expert_parameters_group.layout().addWidget( self.image_parameters_group()) self.expert_parameters_group.layout().addWidget(QHLine()) if "autoRestore" in self.parent.input_parameters.keys() and \ "autoCycle" in self.parent.input_parameters.keys(): hbox = QHBoxLayout() hbox.addWidget(self.checkbox_autoRestore(hline="None")) hbox.addWidget(self.checkbox_autoCycle(hline="None")) self.expert_parameters_group.layout().addLayout(hbox) elif "autoRestore" in self.parent.input_parameters.keys(): self.expert_parameters_group.layout().addWidget( self.checkbox_autoRestore(hline="TOP")) elif "autoCycle" in self.parent.input_parameters.keys(): self.expert_parameters_group.layout().addWidget( self.checkbox_autoCycle()) if "keepImages" in self.parent.input_parameters.keys(): self.expert_parameters_group.layout().addWidget( self.checkbox_keepImages()) ##self.expert_parameters_group.layout().addWidget(QHLine()) #hbox2.addWidget(self.checkbox_debug(hline="None")) #hbox2.addWidget(self.checkbox_simulation(hline="None")) _matches = ["debug", "debugLevel", "simulation"] match = False for tag in _matches: if any([x in tag for x in self.parent.input_parameters.keys()]): match = True break if match: hbox2 = QHBoxLayout() if "debug" in self.parent.input_parameters.keys(): hbox2.addWidget(self.checkbox_debug(hline="None")) #self.expert_parameters_group.layout().addWidget( # self.checkbox_debug(hline="TOP")) if "simulation" in self.parent.input_parameters.keys(): hbox2.addWidget(self.checkbox_simulation(hline="None")) #self.expert_parameters_group.layout().addWidget( # self.checkbox_simulation(hline="BOTTOM")) self.expert_parameters_group.layout().addLayout(hbox2) self.expert_parameters_group.layout().addLayout( self.input_wgt_logging(self.expert_wgt_grid.rowCount(), self.expert_wgt_grid)) self.expert_parameters_group.layout().addWidget(QHLine()) self.expert_parameters_group.setMinimumHeight(EXPERT_PARAMETERS_HEIGHT) lo = QGridLayout() lo.setContentsMargins(9, 19, 9, 9) lo.addWidget(self.expert_parameters_group, 0, 0, 1, 1, alignment=Qt.AlignTop) lo.setAlignment(Qt.AlignHCenter | Qt.AlignTop) lo.setHorizontalSpacing(20) self.expert_wgt.setLayout(lo) #test def radio_buttons(self, key="", options: list = [], start_idx=0): widget = QWidget() layout = QGridLayout() layout.setContentsMargins(5, 0, 5, 0) widget.setMaximumWidth(220) #self.setLayout(layout) target_list = options color_list = ["#894961", "teal", "darkblue"] self.radiobutton = [None] * len(options) for i, title in enumerate(options): self.radiobutton[i] = QRadioButton(title) width_list = [70, 70, 70] if key in self.settings.data["Expert"].keys(): top_key = "Expert" elif key in self.settings.data["Parameters"].keys(): top_key = "Parameters" _width = 1 _full_width = _width * len(options) + 1 layout.addWidget(QHLine(), 0, 0, 1, _full_width) layout.addWidget(QLabel( self.settings.data[top_key][key]['data']['text']), 1, 0, 1, 1, Qt.AlignCenter) for i, (radio, target, color, width) in enumerate( zip(self.radiobutton, target_list, color_list, width_list)): radio.setFont(self.font_pts10) radio.setStyleSheet("color : {0};".format(color)) radio.target = target layout.addWidget(radio, 1, _width*i+1, 1, _width, Qt.AlignCenter) #_qf = QFrame() #_qf.setFixedWidth(10) #layout.addWidget(_qf, 1, layout.columnCount()) #self.radiobutton[start_idx].setChecked(True) #self.radiobutton[start_idx].toggled.emit(True) layout.addWidget(QHLine(), 2, 0, 1, layout.columnCount()) widget.setLayout(layout) return widget def create_table_wgt(self, pv_list: list): return CAQTableWidget(self, pv_list=pv_list, init_column=True, notify_freq_hz=5, notify_unison=True) def create_table_layout(self, branches): @Slot(int) def on_table_tab_changed(tab_idx): #print("Changed", tab_idx, self.table_wgt[tab_idx].tab_text) #print("PM", self.line_sender_dict['profileMonitor'].currentIndex()) #print("PM", self.line_sender_dict['profileMonitor'].currentText()) #key = self.line_sender_dict['profileMonitor'].currentText() #cannot enumerate self.branches!! for i, table in enumerate(self.table_wgt): branch_key = self.branches[i]['key'] #branch['key'] key = self.line_sender_dict[branch_key].currentText() #print(branch_key, key, i, tab_idx) #if key in self.branches[i]['lastLine'][key]: # print("i/idx", i, tab_idx, # self.branches[i]['lastLine'][key]) #else: # self.branches[i]['lastLine'][key] = [' ',' '] #print(self.branches[i]['tagged']) if i == tab_idx: if key in self.branches[i]['tagged']: table.paint_rows(reset=True, last_row=[' ', ' ']) table.paint_rows( row_range=self.branches[i]['tagged'][key], reset=False, last_row=self.branches[i]['lastLine'][key]) else: table.paint_rows(reset=True, last_row=[' ', ' ']) self.branches = branches pv_dict = {} for i in range(0, len(self.branches)): _title = self.branches[i]['tabTitle'] pv_dict[_title] = self.branches[i]['magnets'] self.table_layout = QGridLayout() self.table_layout.setAlignment(Qt.AlignCenter | Qt.AlignTop) self.table_tab_wgt = QTabWidget() self.table_tab_wgt.setFixedWidth(600) self.table_tab_wgt.tabBar().setShape(QTabBar.TriangularNorth) #self.table_tab_wgt.tabBar().setDrawBase(False) #Initialization has to loop self.table_wgt = [None] * len(pv_dict) for i, tab_text in enumerate(pv_dict.keys()): self.table_wgt[i] = self.create_table_wgt( pv_list=pv_dict[tab_text]) self.table_wgt[i].tab_text = tab_text self.table_wgt[i].setFont(self.font_pts10) self.table_tab_wgt.addTab(self.table_wgt[i], tab_text) color = self.branches[i]['tabTextColor'] self.table_tab_wgt.tabBar().setTabTextColor(i, QColor(color)) self.table_tab_wgt.currentChanged.connect(on_table_tab_changed) self.table_layout.addWidget(self.table_tab_wgt, 0, 0) #self.line_sender_dict['tableTabIdx'] = self.table_tab_wgt self.table_layout.setContentsMargins(0, 27, 0, 0) self.table_tab_wgt.currentChanged.emit(0) return self.table_layout #tab_wgt