""" Base frame """ from datetime import datetime from decimal import Decimal import inspect import os import re from matplotlib.backends.backend_qt5agg import ( FigureCanvasQTAgg, NavigationToolbar2QT as NavigationToolbar) from qtpy.QtCore import QSize, Qt, Slot from qtpy.QtGui import QColor, QFont, QFontMetricsF, QIcon, 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) from pyqtacc.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): #super(GUIFrame, self).__init__() super().__init__() self.parent = parent self.has_optics = has_optics 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 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.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(11) 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.canvas_fig_dict = {} 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: 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() 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) self.init_central_tab_widget() self.enter_input_parameters() 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 "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_widget.setLayout(top_layout) self.results_tab_wgt.addTab(top_widget, subtitle) self.results_layout.addWidget(self.results_tab_wgt) 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 """ group_box = QGroupBox("Procedure") group_box.setObjectName("OUTER") hbox = QVBoxLayout() hbox.addLayout(self.create_analysis_wgt()) hbox.setContentsMargins(9, 19, 9, 9) hbox.setSpacing(5) hbox.setAlignment(Qt.AlignTop | Qt.AlignHCenter) group_box.setMaximumWidth(208) group_box.setMinimumHeight(196) group_box.setFont(self.font_gui) group_box.setAlignment(Qt.AlignTop | Qt.AlignHCenter) group_box.setLayout(hbox) 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 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) checkbox = QCheckBox(text) checkbox.setToolTip(tip) checkbox.setFont(self.font_gui) checkbox.stateChanged.connect(on_checkbox_change) #checkbox.setFixedHeight(30) 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) return checkbox def create_label_qdoublespinbox(self, key, label="", min_val=0, max_val=1, step=0.1, start_val=0.5): """ Generic QDoubleSpinbox with buddy label """ def callback(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 = 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) 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) return lab, wgt def create_label_qcombobox(self, key, label="", values: list = []): """ Generic QCombobox with buddy label """ def cb(new_text): self.input_parameters[key] = new_text 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) wgt.setCurrentIndex(0) wgt.currentTextChanged.emit(values[0]) 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) return lab, wgt def image_parameters_group(self): group_box = QGroupBox("Image Pipeline Parameters") box = QHBoxLayout() box.setSpacing(10) box.addWidget(self.create_checkbox( top_key="Expert", key="goodRegion", text="Good region", tip="Enables good region in pipeline", default_value=Qt.Checked)) box.addWidget(self.create_checkbox( top_key="Expert", key="subtractBackground", text="Subtract bkgrnd", tip="Enables background subtraction in pipeline", default_value=Qt.Checked)) group_box.setAlignment(Qt.AlignCenter) group_box.setObjectName("INNERCENTER") good_region = QGroupBox("Good Region") good_region.setObjectName("INNER") lab_thresh, thresh = self.create_label_qdoublespinbox( key="grThreshold", label="Threshold:") lab_gfscale, gfscale = self.create_label_qspinbox( key="grScale", label="Scale:") layout = QGridLayout() layout.addWidget(lab_thresh, 0, 0) layout.addWidget(thresh, 0, 1) layout.addWidget(lab_gfscale, 1, 0) layout.addWidget(gfscale, 1, 1) good_region.setLayout(layout) slicing = QGroupBox("Slicing") slicing.setObjectName("INNER") lab_slices, slices = self.create_label_qspinbox( key="slices", label="No. Slices:", start_val=21) lab_scale, scale = self.create_label_qspinbox( key="slicingScale", label="Scale:") lab_orientation, self.slice_orientation = self.create_label_qcombobox( key="orientation", label="Orientation:", values=["vertical", "horizontal"]) 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) slicing.setLayout(layout) layout_top = QGridLayout() layout_top.addLayout(box, 0, 0, 1, 2) layout_top.addWidget(good_region, 1, 0, 1, 1, Qt.AlignHCenter | Qt.AlignTop) layout_top.addWidget(slicing, 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 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"): 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() 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) 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) 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 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.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 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 ##### GUI Input Widget def pv_stacked_wgt(self, pvlist, title, default_value, key, object_name): 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) #else: # val = 0.0 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_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" def combo_cb(new_text): self.input_parameters[key] = 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 "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) box.addWidget(label) box.addWidget(line) wgt_grid.addLayout(box, irow, 0, 1, 3, Qt.AlignLeft | Qt.AlignVCenter) return 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) rblist = line.findChildren(QRadioButton) for rb in rblist: rb.toggled.connect(on_radiobutton_change) wgt_grid.addWidget(line, irow, 0, 1, 4, Qt.AlignLeft) rblist[0].setChecked(True) rblist[0].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(220) line.setFixedHeight(28) wgt_grid.addWidget(line, irow, 0, 1, 4, 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) wgt_grid.addWidget(line, irow, 0, 1, 4) return line def input_wgt_qlineread(self, value, label, irow, wgt_grid): #Read only has no callback suggested = "WW" line = QLineEdit() line.setObjectName("Read") 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 + 24) wgt_grid.addWidget(label, irow, 0, 1, 2, Qt.AlignLeft | Qt.AlignVCenter) wgt_grid.addWidget(line, irow, 2, 1, 2, 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 suggested = "WW" line = QLineEdit() line.setObjectName("Write") line.setFixedHeight(24) line.textEdited.connect(line_cb) line.setText(str(value)) fm = QFontMetricsF(line.font()) param_width = max(fm.width(str(value)), fm.width(suggested)) line.setMaximumWidth(param_width + 20) wgt_grid.addWidget(label, irow, 0, 1, 2, Qt.AlignLeft | Qt.AlignVCenter) wgt_grid.addWidget(line, irow, 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) min_val = self.settings.data["Expert"][key]["data"]["min"] max_val = self.settings.data["Expert"][key]["data"]["max"] step = self.settings.data["Expert"][key]["data"]["step"] 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) wgt_grid.addWidget(label, irow, 0, 1, 2, Qt.AlignLeft | Qt.AlignVCenter) wgt_grid.addWidget(line, irow, 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) wgt_grid.addWidget(label, irow, 0, 1, 2, Qt.AlignLeft | Qt.AlignVCenter) wgt_grid.addWidget(line, irow, 2, 1, 2, Qt.AlignLeft | Qt.AlignVCenter) return line def input_wgt_qenergystacked(self, irow, wgt_grid): qFrame = QFrame() qFrame.setFixedHeight(10) line = self.pv_energy_stacked_wgt() wgt_grid.addWidget(qFrame, irow, 0, 1, 4) wgt_grid.addWidget(line, irow+1, 0, 1, 4) return line def input_wgt_qphasestacked(self, irow, wgt_grid): qFrame = QFrame() qFrame.setFixedHeight(10) line = self.pv_phase_stacked_wgt() wgt_grid.addWidget(qFrame, irow, 0, 1, 4) wgt_grid.addWidget(line, irow+1, 0, 1, 4) return line def input_wgt_qhline(self, irow, wgt_grid): qHLine = QHLine() qHLine.setFixedHeight(10) wgt_grid.addWidget(qHLine, irow, 0, 1, 4) def input_wgt_frame(self, irow, wgt_grid): qFrame = QFrame() qFrame.setFixedHeight(10) wgt_grid.addWidget(qFrame, irow, 0, 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(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 == "QFrame".upper(): self.input_wgt_qframe(irow, wgt_grid) elif buddy == "QTabWidget".upper(): pass else: wgt_list = ["QCheckBox", "QComboBox", "QDoubleSpinBox", "QFrame", "QHLine", "QLabel", "QLineEdit", "QLineRead", "QRadioButton", "QSpinBox", "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 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 buddy != "NONE": self.input_wgt(buddy=buddy, label=label, key=key, value=val, irow=j, wgt_grid=self.input_wgt_grid) 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 lo.addWidget(self.analysis_procedure_group(), row_count, 1) #Centers input parameters group box lo.setAlignment(Qt.AlignHCenter | Qt.AlignTop) lo.setHorizontalSpacing(20) lo.setVerticalSpacing(40) 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()): 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 = QHBoxLayout() hbox2.addWidget(self.checkbox_debug(hline="None")) hbox2.addWidget(self.checkbox_simulation(hline="None")) self.expert_parameters_group.layout().addLayout(hbox2) #if "debug" in self.parent.input_parameters.keys(): # self.expert_parameters_group.layout().addWidget( # self.checkbox_debug(hline="TOP")) #if "simulation" in self.parent.input_parameters.keys(): # self.expert_parameters_group.layout().addWidget( # self.checkbox_simulation(hline="BOTTOM")) 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.setFixedWidth(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