Files
bdbase/guiframe.py
2022-10-06 08:26:50 +02:00

1821 lines
69 KiB
Python

""" 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 = """<p> <font style="color:{0}; background-color:'white'";>
# {1} <br> {2} </font>
# </p>
# """.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