diff --git a/base.py b/base.py index b3cfc12..cdcb04d 100644 --- a/base.py +++ b/base.py @@ -5,6 +5,7 @@ from collections import OrderedDict from datetime import datetime import getpass import h5py +import logging import inspect import platform import os @@ -30,6 +31,7 @@ from pyqtacc.bdbase.readjson import ReadJSON from pyqtacc.bdbase.savehdf import QSaveHDF from pyqtacc.bdbase.hdf5filemenu import HDF5GroupBox from pyqtacc.bdbase.sendelog import QSendToELOG + from pyqtacc.bdbase.screenshot import QScreenshot from pyqtacc.bdbase.guiframe import GUIFrame @@ -317,7 +319,7 @@ class BaseWindow(QMainWindow): def __init__(self, parent=None, pymodule=None, appversion=None, title="", user_mode=UserMode.OPERATION, facility=Facility.SwissFEL, - extended=True, has_optics=True): + extended=True, has_optics=True, has_procedure=True): super(BaseWindow, self).__init__(parent) self.parent = parent @@ -331,7 +333,8 @@ class BaseWindow(QMainWindow): self.facility = facility self.user_mode = user_mode self.appname, self.appext = self.pymodule.split(".") - + + print("=============================================") print("Starting {0} at: {1}".format(self.appname, datetime.now())) print("User: {0} Host: {1}".format(os.getlogin(), os.uname()[1])) @@ -340,6 +343,8 @@ class BaseWindow(QMainWindow): self.settings = ReadJSON(self.appname) + #Read out current_logbook + self.cafe = PyCafe.CyCafe() self.cyca = PyCafe.CyCa() self.cafe_exception = PyCafe.CafeException @@ -390,6 +395,12 @@ class BaseWindow(QMainWindow): self.stdlog_dest = (self.settings.data["stdlog"]["destination"] + self.appname + "-" + os.getlogin() + ".log") + self.logging = logging + #self.logging.basicConfig(filename=self.stdlog_dest, level=logging.DEBUG) + self.logging.basicConfig(level=logging.DEBUG) + self.logger = self.logging.getLogger(__name__) + self.logger.info("Logging activated") + self.date_str = None self.autopost_elog = True @@ -492,14 +503,17 @@ class BaseWindow(QMainWindow): if self.facility == Facility.SwissFEL: from pyqtacc.sf.guiheader import GUIHeader + from pyqtacc.bdbase.sendelog import QSendToELOG elif self.facility == Facility.SLS: from pyqtacc.sls.guiheader import GUIHeader + from pyqtacc.sls.sendelogsls import QSendToELOG self.gui_header = GUIHeader(self, user_mode=self.user_mode, extended=extended) self.mainwindow_layout.addWidget(self.gui_header.header_wgt) - self.gui_frame = GUIFrame(self, has_optics=self.has_optics) + self.gui_frame = GUIFrame(self, has_optics=self.has_optics, + has_procedure=has_procedure) self.show_log_message = self.gui_frame.show_log_message if self.autopost_hdf: @@ -553,7 +567,7 @@ class BaseWindow(QMainWindow): if self.settings.data["Parameters"][key]["flag"]: self.input_parameters[key] = val - self.all_input_parameters[key] = val + self.all_input_parameters[key] = val #print("val=",val, key) except KeyError as e: @@ -599,8 +613,8 @@ class BaseWindow(QMainWindow): if self.settings.data["Expert"][key]["flag"]: self.expert_parameters[key] = val - self.all_expert_parameters[key] = val - #print("val=",val, key) + self.all_expert_parameters[key] = val + #print("val=",val, key) except KeyError as e: print("Key Error in base.py initialization:", e) @@ -841,7 +855,7 @@ class BaseWindow(QMainWindow): """ #Close all dock widgets #self.removeDockWidget(self.hdf_dock_widget) - + self.logger.info("Closing Application") self.save_application_settings() QApplication.processEvents() self.cafe.monitorStopAll() @@ -1177,6 +1191,7 @@ class BaseWindow(QMainWindow): #print("filename", self.hdf_filename) def verify_send_to_elog(self): + if self.analysis_thread is not None: if self.analysis_thread.isRunning(): _mess = ("Measurement in progress. " + @@ -1210,6 +1225,9 @@ class BaseWindow(QMainWindow): def send_to_elog(self): """ Response to elog_action; normally overwritten """ + if not self.verify_send_to_elog(): + return + ''' if self.analysis_thread is not None: if self.analysis_thread.isRunning(): _mess = ("Measurement in progress. " + @@ -1218,6 +1236,7 @@ class BaseWindow(QMainWindow): QMessageBox.Ok) return + ''' #_elog_sf = ElogSwissFEL() _logbook = None @@ -1227,6 +1246,7 @@ class BaseWindow(QMainWindow): _attach_files = [] _message = "" + QSendToELOG(self, logbook=_logbook, categoryIdx=_category_idx, domainIdx=_domain_idx, sectionIdx=_section_idx, title=self.title, message=_message, @@ -1716,18 +1736,26 @@ class BaseWindow(QMainWindow): @Slot(dict) def receive_analysis_results(self, all_dict): - print("receive analysis results===>") self.all_data = all_dict self.gui_frame.canvas_update(all_dict['Figure data']) - print("here") + + if self.gui_frame.results_output_wgt_dict: + #all_dict['Processed data']['Results']={} + #all_dict['Processed data']['Results']['mean'] = 123.23 + #all_dict['Processed data']['Results']['SD'] = 0.23 + try: + results_data = all_dict['Processed data']['Results'] + self.gui_frame.send_to_results_output_wgt(results_data) + except: + pass + self.gui_frame.central_tab_widget.setCurrentIndex(1) - self.gui_frame.results_tab_wgt.setCurrentIndex(0) + self.gui_frame.results_tab_wgt.setCurrentIndex(0) @Slot() def receive_abort_analysis(self): """Instantiate action on abort """ - print("Aborting", flush=True) if self.analysis_procedure.abort: return self.gui_frame.in_abort_procedure() diff --git a/enumkind.py b/enumkind.py index 3311e95..cb99888 100644 --- a/enumkind.py +++ b/enumkind.py @@ -18,6 +18,7 @@ class Facility(IntEnum): PSI = 0 SwissFEL = 1 SLS = 2 + HIPA = 3 class MsgSeverity(IntEnum): """ For use with message logger diff --git a/guiframe.py b/guiframe.py index b91c83b..deffbb2 100644 --- a/guiframe.py +++ b/guiframe.py @@ -17,7 +17,7 @@ from qtpy.QtWidgets import (QCheckBox, QComboBox, QDoubleSpinBox, QFrame, QStackedWidget, QTabBar, QTabWidget, QToolButton, QVBoxLayout, QWidget) from caqtwidgets.pvwidgets import (CAQLabel, CAQTableWidget, QMessageWidget, - QHLine) + QHLine, QVLine) from pyqtacc.bdbase.enumkind import Facility, MsgSeverity, UserMode @@ -42,11 +42,12 @@ def _line(): class GUIFrame(QWidget): """ GUI Class """ - def __init__(self, parent, has_optics=True): + def __init__(self, parent, has_optics=True, has_procedure=True): #super(GUIFrame, self).__init__() super().__init__() self.parent = parent self.has_optics = has_optics + self.has_procedure = has_procedure self.appname = parent.appname self.facility = parent.facility self.settings = parent.settings @@ -61,6 +62,8 @@ class GUIFrame(QWidget): self.all_expert_parameters = parent.all_expert_parameters self.all_expert_labels = parent.all_expert_labels + self.results_output_wgt_dict = {} + try: self.widget_height = self.settings.data["StyleGuide"][ "widgetHeight"] @@ -75,6 +78,7 @@ class GUIFrame(QWidget): self.expert_parameters_group = None self.operator_parameters_group = None + self.output_parameters_wgt = None self.optics_group = None self.energy_stacked_widget = None @@ -268,6 +272,76 @@ class GUIFrame(QWidget): self.optics_group.cycle_quads = None return self.optics_group + def output_parameters_group(self, title="Results Output"): + """ Generic group box for user supplied input + """ + self.output_parameters_wgt = QWidget() + qtop = QVBoxLayout() + self.output_parameters_group_box = QGroupBox(title) + self.output_parameters_group_box.setObjectName("OUTER") + vbox = QVBoxLayout() + vbox.setContentsMargins(9, 19, 9, 9) + vbox.setAlignment(Qt.AlignTop | Qt.AlignHCenter) + keys = self.settings.data["Results"].keys() + max_str_len = 4 + for key in keys: + if len(key) > max_str_len: + max_str_len = len(key) + + for key in keys: + ikey = self.settings.data["Results"][key] + + if ikey['flag']: + _widget = ikey["data"]["widget"].upper() + _text = ikey["data"]["text"] + _value = str(ikey["data"]["value"]) + _width = int(ikey["data"]["width"]) + if _widget == "QLINEEDIT": + ql = QLabel(str(_text)) + _label_width = (ql.fontMetrics().averageCharWidth() * + max_str_len+2) + ql.setMinimumWidth(_label_width) + ql.setFixedHeight(self.widget_height) + ql.setFont(self.font_pts10) + + wgt = QLineEdit() + wgt.setFixedWidth(_width) + wgt.setObjectName("ReadRight") + wgt.setFixedHeight(self.widget_height) + wgt.setFont(self.font_pts10) + wgt.setAlignment(Qt.AlignRight) + self.results_output_wgt_dict[key] = wgt + if "NONE" not in _value.upper(): + self.results_output_wgt_dict[key].setText(_value) + + hbox = QHBoxLayout() + hbox.setContentsMargins(0, 0, 0, 0) + hbox.addWidget(ql) + hbox.addWidget(wgt) + hbox.setAlignment(Qt.AlignLeft) + vbox.addLayout(hbox) + + self.output_parameters_group_box.setContentsMargins(9, 19, 9, 9) + self.output_parameters_group_box.setMinimumWidth(140) + self.output_parameters_group_box.setMaximumWidth(300) + #self.output_parameters_group_box.setMaximumHeight(400) + self.output_parameters_group_box.setFont(self.font_gui) + self.output_parameters_group_box.setAlignment(Qt.AlignTop | + Qt.AlignHCenter) + + qf = QFrame() + qf.setFixedHeight(35) + self.output_parameters_group_box.setLayout(vbox) + qtop.addWidget(qf) + qtop.addWidget(self.output_parameters_group_box) + self.output_parameters_wgt.setLayout(qtop) + return self.output_parameters_wgt + + def send_to_results_output_wgt(self, results_data): + for key, value in results_data.items(): + self.results_output_wgt_dict[key].setText(str(value)) + + def input_parameters_group(self, title="Input Parameters"): """ Generic group box for user supplied input """ @@ -309,11 +383,12 @@ class GUIFrame(QWidget): #print(self.input_parameters) checkbox = QCheckBox(text) + checkbox.setToolTip(tip) checkbox.setFont(self.font_gui) checkbox.stateChanged.connect(on_checkbox_change) #checkbox.setFixedHeight(30) - + #checkbox.setLayoutDirection(Qt.RightToLeft) checked_value = default_value if key in self.parent.input_parameters.keys(): if "data" in self.settings.data[top_key][key].keys(): @@ -324,16 +399,18 @@ class GUIFrame(QWidget): checked_value = Qt.Unchecked checkbox.setCheckState(checked_value) + checkbox.stateChanged.emit(checked_value) return checkbox def create_label_qdoublespinbox(self, key, label="", min_val=0, max_val=1, - step=0.1, start_val=0.5): + step=0.1, start_val=0.5, digits=1): """ Generic QDoubleSpinbox with buddy label """ + def callback(value): - self.input_parameters[key] = value + self.input_parameters[key] = round(value, digits) widget_height = 24 suggested = "WWW" @@ -355,6 +432,7 @@ class GUIFrame(QWidget): wgt.setMaximumWidth(param_width + 40) wgt.valueChanged.connect(callback) wgt.setValue(start_val) + wgt.valueChanged.emit(start_val) return lab, wgt @@ -366,6 +444,7 @@ class GUIFrame(QWidget): def cb(value): self.input_parameters[key] = value + widget_height = 24 suggested = "WWW" lab = QLabel(label) @@ -385,15 +464,17 @@ class GUIFrame(QWidget): wgt.setMaximumWidth(param_width + 40) wgt.valueChanged.connect(cb) wgt.setValue(start_val) + wgt.valueChanged.emit(start_val) return lab, wgt - def create_label_qcombobox(self, key, label="", values: list = []): + def create_label_qcombobox(self, key, label="", values: list = [], start_val: str =""): """ Generic QCombobox with buddy label """ def cb(new_text): self.input_parameters[key] = new_text + print( self.input_parameters) widget_height = 26 suggested = "WWW" @@ -410,8 +491,11 @@ class GUIFrame(QWidget): wgt.setFont(self.font_pts10) wgt.setContentsMargins(5, 0, 0, 0) wgt.currentTextChanged.connect(cb) - wgt.setCurrentIndex(0) - wgt.currentTextChanged.emit(values[0]) + + start_idx = wgt.findText(start_val) + start_idx = start_idx if start_idx > 0 else 0 + wgt.setCurrentIndex(start_idx) + wgt.currentTextChanged.emit(values[start_idx]) value_for_width = max(values, key=len) @@ -419,23 +503,33 @@ class GUIFrame(QWidget): 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") + group_box = QGroupBox("Image Pipeline Expert") box = QHBoxLayout() box.setSpacing(10) + try: + start_val_gr = self.settings.data["Expert"]["pipeline"]["data"]["goodRegion"] + start_val_gr = Qt.Checked if start_val_gr else Qt.Unchecked + except KeyError as ex: + start_val_gr = Qt.Unchecked box.addWidget(self.create_checkbox( top_key="Expert", key="goodRegion", text="Good region", - tip="Enables good region in pipeline", default_value=Qt.Checked)) - + tip="Enables good region in pipeline", default_value=start_val_gr)) + try: + start_val_sub = self.settings.data["Expert"]["pipeline"]["data"]["subtractBackground"] + start_val_sub = Qt.Checked if start_val_sub else Qt.Unchecked + except KeyError as ex: + start_val_sub = Qt.Unchecked box.addWidget(self.create_checkbox( top_key="Expert", key="subtractBackground", text="Subtract bkgrnd", tip="Enables background subtraction in pipeline", - default_value=Qt.Checked)) + default_value=start_val_sub)) group_box.setAlignment(Qt.AlignCenter) group_box.setObjectName("INNERCENTER") @@ -443,10 +537,20 @@ class GUIFrame(QWidget): good_region = QGroupBox("Good Region") good_region.setObjectName("INNER") + try: + start_val_grThreshold = self.settings.data["Expert"]["pipeline"]["data"]["grThreshold"] + except KeyError as ex: + start_val_grThreshold = 21 + lab_thresh, thresh = self.create_label_qdoublespinbox( - key="grThreshold", label="Threshold:") + key="grThreshold", label="Threshold:", start_val=start_val_grThreshold) + try: + start_val_grScale = self.settings.data["Expert"]["pipeline"]["data"]["grScale"] + except KeyError as ex: + start_val_grScale = 6 lab_gfscale, gfscale = self.create_label_qspinbox( - key="grScale", label="Scale:") + key="grScale", label="Scale:", start_val=start_val_grScale) + layout = QGridLayout() layout.addWidget(lab_thresh, 0, 0) layout.addWidget(thresh, 0, 1) @@ -457,13 +561,28 @@ class GUIFrame(QWidget): slicing = QGroupBox("Slicing") slicing.setObjectName("INNER") + try: + start_val_nslices = self.settings.data["Expert"]["pipeline"]["data"]["slices"] + except KeyError as ex: + start_val_nslices = 21 lab_slices, slices = self.create_label_qspinbox( - key="slices", label="No. Slices:", start_val=21) + key="slices", label="No. Slices:", start_val=start_val_nslices) + + try: + start_val_slicing_scale = self.settings.data["Expert"]["pipeline"]["data"]["slicingScale"] + except KeyError as ex: + start_val_slicing_scale = 5 lab_scale, scale = self.create_label_qspinbox( - key="slicingScale", label="Scale:") + key="slicingScale", label="Scale:", start_val=start_val_slicing_scale) + + try: + start_val_orientation = self.settings.data["Expert"]["pipeline"]["data"]["orientation"] + except KeyError as ex: + start_val_orientation = "vertical" + lab_orientation, self.slice_orientation = self.create_label_qcombobox( key="orientation", label="Orientation:", - values=["vertical", "horizontal"]) + values=["vertical", "horizontal"], start_val=start_val_orientation) layout = QGridLayout() @@ -564,6 +683,13 @@ class GUIFrame(QWidget): for table in self.table_wgt: table.init_value_button.setEnabled(False) table.restore_value_button.setEnabled(False) + + if self.output_parameters_wgt is not None: + for wgt in self.results_output_wgt_dict.values(): + wgt.setText("") + + #self.output_parameters_wg.setEnabled(False) + def in_abort_procedure(self): self.abort_wgt.setEnabled(False) @@ -585,6 +711,8 @@ class GUIFrame(QWidget): for table in self.table_wgt: table.init_value_button.setEnabled(True) table.restore_value_button.setEnabled(True) + #if self.output_parameters_wgt is not None: + # self.output_parameters_wgt.setEnabled(True) def in_hdf_measurement_procedure(self): self.parent.h5_groupbox.analyze_h5_widget.setEnabled(False) @@ -748,7 +876,7 @@ class GUIFrame(QWidget): except KeyError as error: print(("KeyError in guiframe.py; Unknown key {0} " + "in def checkbox_wgt: {1}").format(key, error)) - text = key + text = key checkbox = QCheckBox(text) checkbox.setToolTip(tooltip) @@ -756,6 +884,7 @@ class GUIFrame(QWidget): if object_name: checkbox.setObjectName(object_name) + checkbox.setLayoutDirection(Qt.LeftToRight) #is default checkbox.stateChanged.connect(on_change) checked_value = Qt.Unchecked @@ -1007,7 +1136,7 @@ class GUIFrame(QWidget): stack.style().polish(stack) stacked_widget.addWidget(stack) - + stacked_widget.setMaximumHeight(320) self.stacked_wgt_dict[stacked_widget] = stack_list return stacked_widget @@ -1291,7 +1420,7 @@ class GUIFrame(QWidget): 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) + #print("KEY================", key, self.parent.input_parameters[key], new_value) if key in self.settings.data["Expert"].keys(): top_key = "Expert" @@ -1319,6 +1448,8 @@ class GUIFrame(QWidget): self.line_sender_dict[item].setText(str(value_list)) line = self.radio_buttons(key=key, options=value) + line.setContentsMargins(0, 0, 0, 0) + rblist = line.findChildren(QRadioButton) for rb in rblist: @@ -1326,9 +1457,24 @@ class GUIFrame(QWidget): wgt_grid.addWidget(line, irow, 0, 1, 4, Qt.AlignLeft) - rblist[0].setChecked(True) - rblist[0].toggled.emit(True) - + _idx=0 + + try: + if key in self.settings.data["Expert"].keys(): + top_key = "Expert" + elif key in self.settings.data["Parameters"].keys(): + top_key = "Parameters" + _choice =self.settings.data[top_key][key]['data']['default'] + _values = self.settings.data[top_key][key]['data']['value'] + if isinstance(_choice, (str, bytes)): + _idx = _values.index(_choice) + elif isinstance(_choice, int): + _idx = _choice if _choice < len(_values) else 0 + except KeyError: + pass + + rblist[_idx].setChecked(True) + rblist[_idx].toggled.emit(True) return line @@ -1340,10 +1486,26 @@ class GUIFrame(QWidget): line.setChecked(Qt.Checked if value else Qt.Unchecked) line.stateChanged.connect(check_cb) line.setMaximumWidth(320) - line.setMinimumWidth(220) + line.setMinimumWidth(100) line.setFixedHeight(28) + + + if key in self.settings.data["Expert"].keys(): + top_key = "Expert" + elif key in self.settings.data["Parameters"].keys(): + top_key = "Parameters" - wgt_grid.addWidget(line, irow, 0, 1, 4, Qt.AlignLeft | Qt.AlignVCenter) + col_wdt = 4 + try: + orientation = self.settings.data[top_key][key]["data"]["orientation"] + orientation = Qt.RightToLeft if "RightToLeft" in orientation \ + else Qt.LeftToRight + line.setLayoutDirection(orientation) + col_wdt=3 + except KeyError: + pass + + wgt_grid.addWidget(line, irow, 0, 1, col_wdt, Qt.AlignLeft | Qt.AlignVCenter) return line @@ -1362,22 +1524,27 @@ class GUIFrame(QWidget): return line - def input_wgt_qlineread(self, value, label, irow, wgt_grid): - #Read only has no callback + def input_wgt_qlineread(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("Read") line.setText(str(value)) #"{0: 3.1f}".format(value)) line.setFixedHeight(24) + line.textChanged.connect(line_cb) 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) - + extra_width = 22 if len(str(value)) < 16 else 26 + line.setFixedWidth(param_width + extra_width) + qframe_buffer = QFrame() + qframe_buffer.setFixedWidth(3) + wgt_grid.addWidget(label, irow, 0, 1, 1, Qt.AlignLeft | Qt.AlignVCenter) + wgt_grid.addWidget(qframe_buffer, irow, 1, 1, 1, Qt.AlignLeft | Qt.AlignVCenter) + wgt_grid.addWidget(line, irow, 2, 1, 1, Qt.AlignLeft | Qt.AlignVCenter) + return line def input_wgt_qlineedit(self, key, value, label, irow, wgt_grid): @@ -1386,13 +1553,15 @@ class GUIFrame(QWidget): 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) + param_width = max(fm.width(str(value)), fm.width(suggested)) + extra_width = 22 if len(str(value)) < 16 else 26 + line.setFixedWidth(param_width + extra_width) wgt_grid.addWidget(label, irow, 0, 1, 2, Qt.AlignLeft | Qt.AlignVCenter) wgt_grid.addWidget(line, irow, 2, 1, 2, Qt.AlignLeft | Qt.AlignVCenter) @@ -1459,22 +1628,30 @@ class GUIFrame(QWidget): 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) + qframe_top = QFrame() + qframe_top.setFixedHeight(3) + stacked_wgt = self.pv_energy_stacked_wgt() + qframe_bottom = QFrame() + qframe_bottom.setFixedHeight(3) - return line + wgt_grid.addWidget(qframe_top, irow, 0, 1, 4) + wgt_grid.addWidget(stacked_wgt, irow+1, 0, 1, 4) + wgt_grid.addWidget(qframe_bottom, irow+2, 0, 1, 4) + + return stacked_wgt 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) + qframe_top = QFrame() + qframe_top.setFixedHeight(3) + stacked_wgt = self.pv_phase_stacked_wgt() + qframe_bottom = QFrame() + qframe_bottom.setFixedHeight(3) - return line + wgt_grid.addWidget(qframe_top, irow, 0, 1, 4) + wgt_grid.addWidget(stacked_wgt, irow+1, 0, 1, 4) + wgt_grid.addWidget(qframe_bottom, irow+2, 0, 1, 4) + + return stacked_wgt def input_wgt_qhline(self, irow, wgt_grid): qHLine = QHLine() @@ -1490,6 +1667,9 @@ class GUIFrame(QWidget): def input_wgt(self, buddy, label, key, value, irow=0, wgt_grid=None): + if "QVLINE" in buddy: + print("QVLine", label, key, value) + if wgt_grid is None: wgt_grid = self.input_wgt_grid @@ -1515,7 +1695,7 @@ class GUIFrame(QWidget): line = self.input_wgt_qlabel(value, irow, wgt_grid) elif buddy == "QLineRead".upper(): - line = self.input_wgt_qlineread(value, label, irow, wgt_grid) + line = self.input_wgt_qlineread(key, value, label, irow, wgt_grid) elif buddy == "QDoubleSpinBox".upper(): line = self.input_wgt_qdoublespinbox(key, value, label, irow, @@ -1581,12 +1761,21 @@ class GUIFrame(QWidget): except KeyError: pass + def draw_widget(buddy): + flag = self.settings.data["Parameters"][key]["flag"] + if buddy == "NONE": + return False + elif buddy not in ["QHLINE", "QFRAME"] and not flag: + return False + return True + 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": + #if buddy != "NONE": + if draw_widget(buddy): self.input_wgt(buddy=buddy, label=label, key=key, value=val, irow=j, wgt_grid=self.input_wgt_grid) @@ -1605,7 +1794,9 @@ class GUIFrame(QWidget): row_count = 1 else: row_count = 0 - lo.addWidget(self.analysis_procedure_group(), row_count, 1) + + if self.has_procedure: + lo.addWidget(self.analysis_procedure_group(), row_count, 1) #Centers input parameters group box @@ -1666,7 +1857,7 @@ class GUIFrame(QWidget): self.expert_parameters_group.layout().addWidget( self.checkbox_keepImages()) - self.expert_parameters_group.layout().addWidget(QHLine()) + ##self.expert_parameters_group.layout().addWidget(QHLine()) hbox2 = QHBoxLayout() hbox2.addWidget(self.checkbox_debug(hline="None")) @@ -1697,7 +1888,7 @@ class GUIFrame(QWidget): widget = QWidget() layout = QGridLayout() layout.setContentsMargins(5, 0, 5, 0) - widget.setFixedWidth(220) + widget.setMaximumWidth(220) #self.setLayout(layout) target_list = options @@ -1715,7 +1906,7 @@ class GUIFrame(QWidget): elif key in self.settings.data["Parameters"].keys(): top_key = "Parameters" - _width = 1 + _width = 1 _full_width = _width * len(options) + 1 layout.addWidget(QHLine(), 0, 0, 1, _full_width) diff --git a/sendelogframe.py b/sendelogframe.py new file mode 100644 index 0000000..19f2fb7 --- /dev/null +++ b/sendelogframe.py @@ -0,0 +1,327 @@ +import getpass +import inspect +import os +import time + +from qtpy.QtCore import Qt +from qtpy.QtWidgets import (QComboBox, QDialog, QFileDialog, QHBoxLayout, + QLabel, QLineEdit, QPushButton, QTextEdit, + QVBoxLayout) + +import elog # https://github.com/paulscherrerinstitute/py_elog +from pyqtacc.bdbase.enumkind import MsgSeverity + +_version = "1.0.0" +_pymodule = os.path.basename(__file__) +_appname, _appext = _pymodule.split(".") + +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 QSendToELOGFrame(QDialog): + """ Graphical interface to elog + """ + def __init__(self, parent, logbook=None, title=None, message=None, + attachFile=None, destination_folder=None): + super().__init__() + + self.files_text = '' + self.fflag = False + self.parent = parent + + if destination_folder is None: + self.destination = self.parent.elog_dest + " / " + self.parent.appname + else: + self.destination = destination_folder + self.window_title = self.parent.appname + ": " + _appname + self.pymodule = _pymodule + + self.setWindowTitle(self.window_title) + + elog_books = list(self.parent.settings.data["ElogBooks"]) + self.elog_items = QComboBox() + self.elog_items.addItems(elog_books) + self.elog_items.currentIndexChanged.connect(self.on_elog_change) + idx = 0 + + if logbook is not None: + try: + idx = elog_books.index(logbook) + except Exception: + mess = "Index not found for logbook {0}".format(logbook) + self.parent.show_log_message(MsgSeverity.WARN, self.pymodule, + _line(), mess) + + + else: + logbook = self.parent.settings.data["Elog"]["book"] + try: + idx = elog_books.index(logbook) + except Exception: + mess = "Index not found for logbook {0}".format(logbook) + self.parent.show_log_message(MsgSeverity.WARN, self.pymodule, + _line(), mess) + + self.logbook = logbook + print("logbook===>", self.logbook) + print("message===>", message) + self.elog_items.setCurrentIndex(idx) + self.elog_items.currentIndexChanged.emit(idx) + self.elog_items.setObjectName("Elog") + + self.title_items = QHBoxLayout() + self.title_items.addWidget(QLabel('Title: ')) + self.titleline = QLineEdit() + self.titleline.setObjectName('Elog') + #self.titleline.setStatusTip('elog') + if title is None: + title = self.parent.appname + self.titleline.setText(str(title)) + self.titleline.setFixedWidth(300) + self.titleline.setAlignment(Qt.AlignCenter) + self.title_items.addWidget(self.titleline) + + self.applicationbox = QHBoxLayout() + self.applicationbox.addWidget(QLabel('Application:')) + self.applicationlabel = QLabel(self.parent.pymodule) + self.applicationlabel.setObjectName("Elog") + # self.applicationbox.addStretch() + self.applicationbox.addWidget(self.applicationlabel) + + logbook = QHBoxLayout() + logbook.addWidget(QLabel('Logbook: ')) + logbook.addWidget(self.elog_items) + + + authorbox = QHBoxLayout() + authorbox.addWidget(QLabel('Author: ')) + self.author = QLineEdit() + self.author.setObjectName('Elog') + self.author.setFixedWidth(195) + self.author.setStatusTip('elog') + self.author.setText(getpass.getuser()) + authorbox.addWidget(self.author) + + self.files = [] + self.attributes = {} + + self.layout = QVBoxLayout(self) + self.layout.addLayout(logbook) + self.layout.addLayout(self.applicationbox) + + self.layout.addLayout(authorbox) + self.layout.addLayout(self.title_items) + + report = QLabel('Report: ') + self.layout.addWidget(report) + self.message = QTextEdit(message) + self.layout.addWidget(self.message) + + filebox = QHBoxLayout() + qlfile = QLabel('Attach:') + qlfile.setAlignment(Qt.AlignTop) + filebox.addWidget(qlfile) + + self.filesE = QTextEdit() + self.filesE.setAlignment(Qt.AlignTop) + self.filesE.setFixedHeight(80) + self.filesE.setReadOnly(True) + + self.attachFile = attachFile + + if self.attachFile is not None: + _attachFile = [] + if isinstance(self.attachFile, str): + _attachFile.append(self.attachFile) + elif isinstance(self.attachFile, list): + _attachFile = self.attachFile + for i, file in enumerate(_attachFile): + _attach_base = os.path.basename(file) + if i > 0: + self.files_text += "\n" + self.files_text += str(_attach_base) + self.filesE.setText(self.files_text) + self.fflag = True + + filebox.addWidget(self.filesE) + + openCloseVBox = QVBoxLayout() + self.openBtn = QPushButton('Add') + self.openBtn.setAutoDefault(False) + self.openBtn.clicked.connect(self.openFiles) + openCloseVBox.addWidget(self.openBtn) + + closeBtn = QPushButton('Clear') + closeBtn.setAutoDefault(False) + closeBtn.clicked.connect(self.clearFiles) + openCloseVBox.addWidget(closeBtn) + + filebox.addLayout(openCloseVBox) + self.layout.addLayout(filebox) + + btnLayout = QHBoxLayout() + self.okBtn = QPushButton('Send') + self.okBtn.setAutoDefault(False) + self.okBtn.clicked.connect(self.ok) + + self.cancelBtn = QPushButton('Cancel') + self.cancelBtn.setAutoDefault(False) + self.cancelBtn.clicked.connect(self.close) + btnLayout.addWidget(self.okBtn) + btnLayout.addWidget(self.cancelBtn) + self.messagelbl = QLabel('') + self.messagelbl.setStyleSheet("QLabel { color : red; }") + self.layout.addWidget(self.messagelbl) + self.layout.addLayout(btnLayout) + self.setMinimumWidth(440) + #self.exec() + + def on_elog_change(self, elog_change_val): + if "test" in self.elog_items.currentText(): + _bgcolor = "QComboBox {background: plum; color : black;}" + else: + _bgcolor = "QComboBox {background: lightblue; color : black;}" + self.elog_items.setStyleSheet(_bgcolor) + + + def ok(self): + message = self.message.document().toPlainText() + if not message: + self.messagelbl.setText('Please enter a brief Report') + return + title = self.titleline.text() + if not title: + self.messagelbl.setText('Titel attribute is required') + return + + author = self.author.text() + application = self.applicationlabel.text() + self.attributes['Autor'] = author + self.attributes['Author'] = author + self.attributes['Application'] = application + self.attributes['Titel'] = title + self.attributes['Title'] = title + self.attributes['When'] = str(time.time()) + self.attributes['Wann'] = str(time.time()) + + + print("self.attachFile", self.attachFile) + + if self.attachFile is not None: + _attachFile = [] + if isinstance(self.attachFile, str): + _attachFile.append(self.attachFile) + elif isinstance(self.attachFile, list): + _attachFile = self.attachFile + for i in range(0, len(_attachFile)): + if "/tmp" in _attachFile[i]: + self.files.append(str(_attachFile[i])) + elif "/afs/psi.ch" in _attachFile[i]: + self.files.append(str(_attachFile[i])) + elif "/sls/bd/data/" in _attachFile[i]: + self.files.append(str(_attachFile[i])) + else: + self.files.append(self.destination + str(_attachFile[i])) + + print("self.files", self.files) + + el = self.elog_items.currentText() + print("el==============>", el) + url = self.parent.settings.data["ElogBooks"][el]["url"] + + self.logbook = elog.open(url, user='robot', password='robot') + + print("sendelogframe.py========>: ", url, flush=True) + print(message, flush=True) + try: + if self.files: + self.logbook.post(message, attributes=self.attributes, + attachments=self.files) + else: + self.logbook.post(message, attributes=self.attributes) + + #self.trigger_elog_entry.emit(True, url, "OK") + self.receive_elog_notification(True, url, "OK") + except Exception as ex: + #self.trigger_elog_entry.emit(False, url, str(ex)) + print("Exception in sendelog.py", str(ex)) + self.receive_elog_notification(False, url, str(ex)) + + #for file in self.files: + # if os.path.exists(file): + # os.remove(file) + + self.close() + + + def clearFiles(self): + self.attachFile = [] + self.filesE.clear() + self.files = [] + self.files_text = '' + self.fflag = False + + def openFiles(self): + # Notethat openFiles also gets called when qDialog cacnel is clicked! + qFileDialog = QFileDialog() + extensions = ("Images (*.bmp *.eps *.gif *.jpeg *.jpg *.pdf *.png " + + "*.ps *.tiff *.xpm);;Text files (*.txt *.text);;" + + "JSON/XML files (*.json *.xml)") + + flocal = qFileDialog.getOpenFileNames( + self, 'Open File', self.destination, extensions)[0] + + if not flocal: + return + self.files.append(flocal[0]) + if self.fflag: + self.files_text += '\n' + self.files_text += str(flocal[0].split('/')[-1]) + self.fflag = True + self.filesE.setText(self.files_text) + + + def receive_elog_notification(self, is_accepted, logbook_url, elog_message): + '''Receive notification from ELOG, and report to log window''' + + yes_no = "made" if is_accepted else "failed" + mess = "Entry into ELOG: {0} {1}".format(logbook_url, yes_no) + + if is_accepted: + self.parent.show_log_message(MsgSeverity.INFO, self.pymodule, + _line(), mess) + else: + mess += ".\n" + elog_message + self.parent.show_log_message(MsgSeverity.WARN, self.pymodule, + _line(), mess) + self.parent.statusbar.showMessage(mess) + + + def get_logbook_specific_items(self, logbook): + '''Retrieve logbook specific options''' + #First check what is the logbook being requested? + #find layout items + layout_items = [] + layout_items_optional = [] + try: + layout_items = list(self.parent.settings.data[ + "ElogBooks"][logbook]['Required'].keys()) + except KeyError: + pass + try: + layout_items_optional = list(self.parent.settings.data[ + "ElogBooks"][logbook]['Optional'].keys()) + except KeyError: + pass + + layout_items.extend(layout_items_optional) + + return layout_items