Files
bdbase/base.py
2022-01-10 13:15:41 +01:00

1005 lines
39 KiB
Python

""" Base accelerator module
"""
import argparse
from collections import OrderedDict
import datetime
import inspect
import platform
import os
import sys
import time
# Third-party modules
from qtpy.QtCore import (PYQT_VERSION_STR, Signal, Slot, QFile, QFileInfo,
QIODevice, QMutex, QSettings, QSize, Qt, QTemporaryDir,
QThread, QTimer, qVersion)
from qtpy.QtCore import __version__ as QT_VERSION_STR
from qtpy.QtGui import (QColor, QKeySequence, QFont, QIcon, QPainter, QPalette,
QPixmap)
from qtpy.QtPrintSupport import QPrinter, QPrintDialog
from qtpy.QtWidgets import (QAbstractItemView, QAction, QActionGroup,
QApplication, QButtonGroup, QComboBox, QDialog,
QDockWidget, QDoubleSpinBox, QFileDialog, QFrame,
QGridLayout, QGroupBox, QHBoxLayout, QLabel,
QLayout, QLineEdit, QListWidget, QMainWindow, QMenu,
QMenuBar, QMessageBox, QPlainTextEdit, QProgressBar,
QPushButton, QScrollArea, QSizePolicy, QSlider,
QSpinBox, QSplashScreen, QStyle, QStyleOptionSlider,
QToolButton, QVBoxLayout, QWidget)
from matplotlib.backends.backend_qt5agg import (
FigureCanvasQTAgg, NavigationToolbar2QT as NavigationToolbar)
import pyqtacc.bdbase.pyrcc5.qrc_resources
from pyqtacc.bdbase.enumkind import Facility, MsgSeverity, UserMode
from pyqtacc.bdbase.helpbrowser import HelpBrowser
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
from pyqtacc.sf.enumkind import ElogSwissFEL
from caqtwidgets.pvwidgets import QHDFDockWidget, QNoDockWidget
import PyCafe
_pymodule = os.path.basename(__file__)
_appname, _appext = _pymodule.split(".")
_appversion = "1.0.0"
PROGRESS_BAR_THREAD_INIT = 0
PROGRESS_BAR_THREAD_START = 1
PROGRESS_BAR_THREAD_ABORTING = 2
PROGRESS_BAR_THREAD_ABORTED = 3
PROGRESS_BAR_THREAD_ERROR = 4
PROGRESS_BAR_THREAD_END = 100
CENTRAL_WIDGET_MINIMUM_HEIGHT = 840
CENTRAL_WIDGET_MINIMUM_WIDTH = 1240
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 BaseWindow(QMainWindow):
""" BaseWindow
"""
####trigger_elog_entry = Signal(bool, str, str)
trigger_log_message = Signal(str, str, int, str, dict)
trigger_progressbar = Signal(int)
class LoadH5Thread(QThread):
"""Thread for hdf5 analysis
"""
trigger_thread_event = Signal(dict)
def __init__(self, parent, analysis_procedure):
QThread.__init__(self)
self.parent = parent
self.analysis_procedure = analysis_procedure
self.h5_filename = self.parent.h5_filename
def __del__(self):
self.wait()
def run(self):
"""Run hdf5 thread
"""
#Open hdf5file here and
mess = "HDF file {} analysis proceeding...".format(
self.h5_filename)
self.parent.trigger_log_message.emit(MsgSeverity.INFO.name,
_pymodule, _line(),
mess, {})
all_dict = self.analysis_procedure.reanalyze(self.input_parameters)
# Emit results
if all_dict is not None:
self.trigger_thread_event.emit(all_dict)
mess = "HDF file {} analysis succeeded".format(self.h5_filename)
self.parent.trigger_log_message.emit(
MsgSeverity.INFO.name, _pymodule, _line(), mess, {})
else:
mess = "HDF file {} has wrong content!!".format(
self.h5_filename)
self.parent.trigger_log_message.emit(
MsgSeverity.ERROR.name, _pymodule, _line(), mess, {})
class AnalysisThread(QThread):
"""Analysis thread
"""
trigger_thread_event = Signal(dict)
def __init__(self, parent, analysis_procedure, input_parameters):
QThread.__init__(self)
self.parent = parent
self.analysis_procedure = analysis_procedure
self.input_parameters = input_parameters
print (self.input_parameters)
def __del__(self):
self.wait()
def run(self):
"""Run thread
"""
all_dict = self.analysis_procedure.measure_and_analyze(
self.input_parameters)
# Emit results
if all_dict is not None:
self.trigger_thread_event.emit(all_dict)
mess = "Analysis completed"
self.parent.trigger_log_message.emit(
MsgSeverity.INFO.name, _pymodule, _line(), mess, {})
else:
mess = "No data returned from analysis procedure."
self.parent.trigger_log_message.emit(
MsgSeverity.WARN.name, _pymodule, _line(), mess, {})
def __init__(self, parent=None, pymodule=None, appversion=None, title="",
user_mode=UserMode.OPERATION, facility=Facility.SwissFEL,
extended=True):
super(BaseWindow, self).__init__(parent)
self.parent = parent
self.pymodule = pymodule if pymodule else _pymodule
self.appversion = appversion if appversion else _appversion
self.title = title
self.facility = facility
self.user_mode = user_mode
print(self.pymodule)
print(self.appversion)
self.appname, self.appext = self.pymodule.split(".")
self.cafe = PyCafe.CyCafe()
self.cyca = PyCafe.CyCa()
self.cafe_exception = PyCafe.CafeException
self.cafe.enableExceptions = False
self.caget = self.cafe.caget
self.caput = self.cafe.caput
self.application_recent_files = []
self.application_geometry = None
self.filename = None
self.screenshot_titles = []
self.screenshot_items = []
self.simulation = False
self.setObjectName("MainWindow")
self.setWindowTitle(self.appname)
self.statusbar_label = QLabel()
self.statusbar = self.statusBar()
self.progressbar = QProgressBar(self)
self.menu = self.menuBar()
temp_dir = QTemporaryDir()
if temp_dir.isValid():
temp_file = temp_dir.path() + "/cNodes.xml"
if QFile.copy(":/cNodes.xml", temp_file):
status = self.cafe.loadCollectionsFromXML(temp_file)
if status != self.cyca.ICAFE_NORMAL:
options = {}
options['statusCode'] = "{0} {1}".format(
str(status), self.cafe.getStatusCodeAsString(status))
options['statusInfo'] = self.cafe.getStatusInfo(status)
print(options['statusCode'], options['statusInfo'])
else:
print("QFile copy failed for file {0}".format(temp_file))
else:
print("Invalid temporary directory {0}".format(temp_dir))
self.settings = ReadJSON(self.appname)
self.elog_dest = self.settings.data["Elog"]["destination"]
self.screenshot_dest = self.settings.data["screenshot"]["destination"]
self.stdlog_dest = (self.settings.data["stdlog"]["destination"] +
self.appname + ".log")
self.input_parameters = {}
self.input_labels = {}
#self.input_value_dict = {}
self.expert_parameters = {}
self.expert_labels = {}
for key, dictval in self.settings.data["Parameters"].items():
#print (key, dictval)
if self.settings.data["Parameters"][key]["flag"]:
if "value" in dictval["data"]:
self.input_parameters[key] = dictval["data"]["value"]
else:
try:
link_list = []
if 'forwardLink' in dictval["data"]:
link_list = dictval["data"]["forwardLink"]
elif 'link' in dictval["data"]:
link_list = dictval["data"]["link"]
print("link_list", link_list)
if len(link_list) > 1:
val = self.settings.data[link_list[0]]
for link in link_list[1:]:
val = val[link]
print("lval", val)
else:
val = []
for inner_key in self.settings.data[link_list[0]].keys():
val.append(inner_key)
self.input_parameters[key] = val
print("val=",val)
except KeyError as e:
print("Key Error in base.py initialization:", e)
self.input_labels[key] = dictval["data"]["text"]
#print(key, self.settings.data["Parameters"][key]["flag"])
print(self.input_parameters)
#sys.exit(1)
for key, dictval in self.settings.data["Expert"].items():
#print (key, dictval)
if self.settings.data["Expert"][key]["flag"]:
self.expert_parameters[key] = dictval["data"]["value"]
self.expert_labels[key] = dictval["data"]["text"]
self.analysis_thread = None
self.analysis_procedure = None
try:
from src.analysis import AnalysisProcedure
self.analysis_procedure = AnalysisProcedure(self)
except ImportError as e:
print("Import Error:", e)
self.loadh5_thread = None
###self.trigger_elog_entry.connect(self.receive_elog_notification)
"""
self.alarm_severity_color = {
self.cyca.SEV_NO_ALARM: self.settings.data['StyleGuide'][
'fgAlarmNoAlarm'],
self.cyca.SEV_MINOR: self.settings.data['StyleGuide'][
'fgAlarmMinor'],
self.cyca.SEV_MAJOR: self.settings.data['StyleGuide'][
'fgAlarmMajor'],
self.cyca.SEV_INVALID: self.settings.data['StyleGuide'][
'fgAlarmInvalid'],
self.cyca.ICAFE_CA_OP_CONN_DOWN: self.settings.data['StyleGuide'][
'fgAlarmInvalid']
}
"""
self.msg_severity_qcolor = {
MsgSeverity.FATAL: QColor(
self.settings.data["MsgSeverity"]["fatal"]),
MsgSeverity.ERROR: QColor(
self.settings.data["MsgSeverity"]["error"]),
MsgSeverity.WARN: QColor(
self.settings.data["MsgSeverity"]["warn"]),
MsgSeverity.WARNING: QColor(
self.settings.data["MsgSeverity"]["warn"]),
MsgSeverity.INFO: QColor(
self.settings.data["MsgSeverity"]["info"]),
MsgSeverity.DEBUG: QColor(
self.settings.data["MsgSeverity"]["debug"]),
MsgSeverity.FATAL.name: QColor(
self.settings.data["MsgSeverity"]["fatal"]),
MsgSeverity.ERROR.name: QColor(
self.settings.data["MsgSeverity"]["error"]),
MsgSeverity.WARN.name: QColor(
self.settings.data["MsgSeverity"]["warn"]),
MsgSeverity.WARNING.name: QColor(
self.settings.data["MsgSeverity"]["warn"]),
MsgSeverity.INFO.name: QColor(
self.settings.data["MsgSeverity"]["info"]),
MsgSeverity.DEBUG.name: QColor(
self.settings.data["MsgSeverity"]["debug"])
}
#QSetttings
self.restore_application_settings()
self.init_toolbar()
self.init_statusbar_wgt()
self.init_progressbar_wgt()
self.trigger_log_message.connect(self.receive_log_message)
self.trigger_progressbar.connect(self.receive_progressbar)
self.statusbar.addPermanentWidget(self.progressbar, 0)
####
self.appname, appext = self.pymodule.split(".")
self.mainwindow = QWidget()
self.mainwindow_layout = QVBoxLayout()
#self.mainwindow_layout.addLayout(self.gui_header.top_layout)
if self.facility == Facility.SwissFEL:
from pyqtacc.sf.guiheader import GUIHeader
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, self.appname)
self.show_log_message = self.gui_frame.show_log_message
self.hdf_dock_widget = QNoDockWidget(" HDF5", self)
self.init_hdf_analysis_wgt()
self.mainwindow_layout.addWidget(self.gui_frame.central_tab_widget)
self.mainwindow.setLayout(self.mainwindow_layout)
self.mainwindow.setMinimumHeight(CENTRAL_WIDGET_MINIMUM_HEIGHT)
self.mainwindow.setMinimumWidth(CENTRAL_WIDGET_MINIMUM_WIDTH)
self.setCentralWidget(self.mainwindow)
self.show_log_message(MsgSeverity.INFO, _pymodule, _line(),
"Application configured")
def init_toolbar(self):
""" Prepare toolbar
"""
#menu / toolbar
font = self.menu.font()
font.setPointSize(12)
self.menu.setFont(font)
menu_file = self.menu.addMenu("&Output")
menu_file.setFont(font)
menu_file_actions = []
menu_view = self.menu.addMenu("&View")
menu_view.setFont(font)
menu_view_actions = []
menu_help = self.menu.addMenu("&Help")
menu_help.setFont(font)
menu_help_actions = []
file_toolbar = self.addToolBar("Output")
file_toolbar.setObjectName("FileToolBar")
view_toolbar = self.addToolBar("View")
view_toolbar.setObjectName("ViewToolBar")
if False:
menu_daq = self.menu.addMenu("&DAQ")
menu_daq_actions = []
daq_toolbar = self.addToolBar("DAQ")
daq_toolbar.setObjectName("ViewToolBar")
help_toolbar = self.addToolBar("Help")
help_toolbar.setObjectName("HelpToolBar")
a = False
if a:
epics_action = self.create_action(
"&EPICS", slot=self.write_to_epics, shortcut="Ctrl+Shift+E",
icon="epics", tip="Write to EPICS PVs")
menu_file_actions.append(epics_action)
if a:
hdf_action = self.create_action(
"Save&HDF", slot=self.save_to_hdf, shortcut="Ctrl+H",
icon="hdf", tip="Save as HDF")
menu_file_actions.append(hdf_action)
elog_action = self.create_action(
"&Elog", slot=self.send_to_elog, shortcut="Alt+E", icon="elog",
tip="Send to ELOG")
menu_file_actions.append(elog_action)
menu_file_actions.append(QAction().setSeparator(True))
screenshot_action = self.create_action(
"&Screenshot", slot=self.take_screenshot, shortcut="Alt+S",
icon="screenshot", tip="Screenshot of selected items")
menu_file_actions.append(screenshot_action)
print_action = self.create_action(
"&Print", slot=self.send_to_printer, shortcut=QKeySequence.Print,
icon="fileprint", tip="Send to printer")
menu_file_actions.append(print_action)
stdout_action = self.create_action(
"stdout &Log", slot=self.open_stdout_log, shortcut="Alt+L",
icon="filestdlog", tip="Open stdout log file")
menu_view_actions.append(stdout_action)
clear_log_action = self.create_action(
"&Clear log window", slot=self.clear_log_window, shortcut="Alt+C",
icon="viewclearlog", tip="Clears message log window")
menu_view_actions.append(clear_log_action)
quit_action = self.create_action(
"&Quit", slot=self.close, shortcut="Ctrl+Q", icon="filequit",
tip="Exit application, closing all windows")
menu_file_actions.append(QAction().setSeparator(True))
menu_file_actions.append(quit_action)
help_action = self.create_action(
"Help", slot=self.show_help, shortcut=QKeySequence.HelpContents,
icon="home", tip="Help!")
menu_help_actions.append(help_action)
about_action = self.create_action(
"About", slot=self.show_about, shortcut=QKeySequence.WhatsThis,
icon="helpabout", tip="About this app")
menu_help_actions.append(about_action)
self.add_actions(menu_file, tuple(menu_file_actions))
self.add_actions(menu_view, tuple(menu_view_actions))
self.add_actions(menu_help, tuple(menu_help_actions))
self.add_actions(file_toolbar, tuple(menu_file_actions[:-1]))
self.add_actions(view_toolbar, tuple(menu_view_actions))
self.add_actions(help_toolbar, tuple(menu_help_actions))
#toolbar
# Place ExitWidget on the far right
exit_toolbar = self.addToolBar("EXIT")
exit_toolbar.setObjectName("ExitToolBar")
spacer_wgt = QWidget()
spacer_wgt.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
spacer_wgt.setVisible(True)
exit_toolbar.addWidget(spacer_wgt)
self.add_actions(exit_toolbar, (quit_action,))
################# Actions
def add_actions(self, target, actions):
""" Add to toolbar
"""
for action in actions:
if action is None:
target.addSeparator()
else:
target.addAction(action)
def create_action(self, text, slot=None, shortcut=None, icon=None, tip=None,
checkable=False, signal="triggered()"):
"""
"""
action = QAction(text, self)
if icon is not None:
action.setIcon(QIcon(":/{0}.png".format(icon)))
if shortcut is not None:
action.setShortcut(shortcut)
if tip is not None:
action.setToolTip(tip)
if slot is not None:
action.triggered.connect(slot)
if checkable:
action.setCheckable(True)
return action
def closeEvent(self, event):
""" Overrides QMainWindow method
"""
if self.analysis_thread is not None:
if self.analysis_thread.isRunning():
qmbox = QMessageBox()
qmbox.setText(("Measurement in progress." +
"Please try again in a few seconds."))
qmbox.exec()
return event.ignore()
#Close all dock widgets
#self.removeDockWidget(self.hdf_dock_widget)
self.save_application_settings()
QApplication.processEvents()
self.cafe.monitorStopAll()
time.sleep(0.05)
self.cafe.terminate()
time.sleep(0.05)
QApplication.closeAllWindows()
event.accept()
@Slot()
def clear_log_window(self):
""" Requests user confirmation before wiping out log message
window
"""
reply = QMessageBox.question(
self, "Clear Message Log",
"Do you wish to wipe out all messages from the log window?",
QMessageBox.Yes | QMessageBox.No)
if reply == QMessageBox.No:
return False
elif reply == QMessageBox.Yes:
return self.gui_frame.log_wgt.clear()
@Slot()
def open_stdout_log(self):
""" Slot to stdout_action
"""
log_title = self.appname + ": stdout"
sys.stdout.flush()
file = None
if QFile.exists(self.stdlog_dest):
file = open(self.stdlog_dest)
else:
message = "Error Log File: '{0}' does not exist!".format(
self.stdlog_dest)
QMessageBox.warning(self, log_title, message)
return
text = file.read()
file.close()
text_edit = QPlainTextEdit()
text_edit.setPlainText(text)
text_edit.setReadOnly(True)
bgcolor = ("QPlainTextEdit{background-color:" +
self.settings.data["StyleGuide"]["bgErrorLogFile"] + "}")
text_edit.setStyleSheet(bgcolor)
text_edit.verticalScrollBar().setValue(
text_edit.verticalScrollBar().maximum())
text_edit.horizontalScrollBar().setValue(
text_edit.horizontalScrollBar().maximum())
scroll_area = QScrollArea()
scroll_area.setBackgroundRole(QPalette.Light)
scroll_area.setWidgetResizable(True)
scroll_area.setMinimumHeight(600)
scroll_area.setMinimumWidth(700)
scroll_area.setWindowTitle(log_title)
scroll_area.verticalScrollBar().setValue(
scroll_area.verticalScrollBar().maximum())
scroll_area.horizontalScrollBar().setValue(
scroll_area.horizontalScrollBar().maximum())
scroll_area.verticalScrollBar().setSliderPosition(
scroll_area.verticalScrollBar().maximum())
scroll_area.horizontalScrollBar().setSliderPosition(
scroll_area.horizontalScrollBar().maximum())
scroll_area.setWidget(text_edit)
text_edit.scrollArea = scroll_area
layout = QVBoxLayout()
layout.addWidget(scroll_area)
layout.setContentsMargins(0, 0, 0, 0)
qdialog = QDialog(self)
qdialog.setWindowTitle(log_title)
qdialog.setLayout(layout)
qdialog.show()
@Slot()
def save_to_hdf(self):
""" Abstract method to be overwritten by user
"""
QM = QMessageBox()
QM.setText(
str(NotImplementedError("save_to_hdf method has not been " +
"implemented in this application. \n" +
"If not required, consider removing the " +
"icon from the application/config file."))
)
QM.exec()
@Slot()
def save_to_hdf_dialog(self):
""" This uses the widget interface to allow the user to enter
additional meta-data
"""
class QSaveHDF5(QSaveHDF):
"""local instanstiation"""
def save(self):
"""overriding save method"""
user_dict = self.get_data()
h5_filename = user_dict['Destination']
h5_handle = h5py.File(h5_filename, 'w')
for key in user_dict:
if key not in ['Destination']:
lowkey = key.lower()
h5_handle.create_dataset(
'metadata/'+lowkey.replace(' ', '_'),
data=user_dict[key])
'''
for key in self.parent.results_dict:
if key in ['Data1','Data2']:
h5_handle.create_dataset(
'data/' + key,
data = self.parent.results_dict[key])
'''
h5_handle.close()
self.close()
input_options = OrderedDict()
_now = datetime.datetime.now()
_date = _now.strftime("%m/%d/%Y, %H:%M:%S")
input_options['Date'] = _date
#QCombobox if list
#input_options['QComboBox'] = ['one', 'two', 'three']
#input_options['Comment'] = 'Please enter a comment'
QSaveHDF5(self, input_options=input_options)
@Slot()
def send_to_elog(self):
""" Response to elog_action
"""
if self.analysis_thread is not None:
if self.analysis_thread.isRunning():
qmbox = QMessageBox()
qmbox.setText(("Measurement in progress. " +
"Please try again in a few seconds."))
qmbox.exec()
return
_elog_sf = ElogSwissFEL()
_logbook = None
_category_idx = _elog_sf.category.MEASUREMENT
_domain_idx = _elog_sf.system.NONE
_system_idx = _elog_sf.system.BEAMDYNAMICS
_attach_files = []
_message = ""
QSendToELOG(self, logbook=_logbook, categoryIdx=_category_idx,
domainIdx=_domain_idx, sectionIdx=0, title=_title,
message=_message, attachFile=_attach_files)
QApplication.processEvents()
@Slot()
def write_to_epics(self):
""" Abstract method to be overwritten by user
"""
QM = QMessageBox()
QM.setText(
str(NotImplementedError("write_to_epics method has not been " +
"implemented in this application. \n" +
"If not required, consider removing the " +
"icon from the application/config file."))
)
QM.exec()
@Slot()
def send_to_printer(self):
""" Response to printer_action
"""
self.printer = QPrinter(QPrinter.HighResolution)
self.printer.setPaperSize(QPrinter.A4)
self.printer.setOrientation(QPrinter.Landscape)
form = QPrintDialog(self.printer, self)
if form.exec_():
painter = QPainter(self.printer)
w = self.centralWidget()
xscale = self.printer.pageRect().width() / (w.width())
yscale = self.printer.pageRect().height() / (w.height())
scale = min(xscale, yscale)
painter.translate(self.printer.paperRect().x() +
self.printer.pageRect().width() / 2,
self.printer.paperRect().y() +
self.printer.pageRect().height() / 2)
painter.scale(scale, scale)
painter.translate(-w.width()/2, -w.height()/2)
w.render(painter)
@Slot()
def show_about(self):
""" To overide by application
"""
QApplication.processEvents()
QMessageBox.about(
self, "About",
"""<b>{0}</b> v {1}
<p>Copyright &copy; Paul Scherrer Institut (PSI).
All rights reserved.</p>
<p>Author: J. Chrin </p>
<p>1st Responsible: J. Chrin, Tel. 2930,
+41 76 206 0988, jan.chrin@psi.ch </p>
<p>2nd Responsible: 2nd name if appropriate </p>
<p>A main-window style application with menus,
a status bar, a toolbar and log window </p>
<p>Python {2} - Qt {3} - PyQt {4} <br>
cafe {5} - epics {6} on {7}""".format(
_pymodule, _appversion, platform.python_version(),
QT_VERSION_STR, PYQT_VERSION_STR,
self.cafe.CAFE_version(), self.cafe.EPICS_version(),
platform.system()))
QApplication.processEvents()
@Slot()
def show_help(self):
""" Invoke help pages from qrc_resources
"""
index_html = self.appname + "/index.html"
help_base = ":/help/" + self.appname
help_page = HelpBrowser(help_base, index_html, self)
help_page.show()
@Slot()
def take_screenshot(self):
QScreenshot(self, window_title=self.appname,
screen_items=self.screenshot_items,
screen_titles=self.screenshot_titles)
def add_screenshot(self, title=None, item=None):
if title and item:
if title not in self.screenshot_titles and \
item not in self.screenshot_items:
self.screenshot_titles.append(title)
self.screenshot_items.append(item)
def clear_screenshot(self, title=None, item=None):
if title and item:
if title in self.screenshot_titles and \
item in self.screenshot_items:
self.screenshot_titles.remove(title)
self.screenshot_items.remove(item)
def clear_screenshot(self, title=None, item=None):
if title and item:
if title in self.screenshot_titles and \
item in self.screenshot_items:
self.screenshot_titles.remove(title)
self.screenshot_items.remove(item)
############################### Init
def init_statusbar_wgt(self):
""" Configure statusbar
"""
self.statusbar_label.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken)
self.statusbar.setSizeGripEnabled(False)
self.statusbar.addPermanentWidget(self.statusbar_label)
self.statusbar.clearMessage()
self.statusbar.showMessage("Initialization complete")
def init_progressbar_wgt(self):
self.progressbar_simulation = "simulation"
self.progressbar_standard = "blue"
self.progressbar_abort = "abort"
self.progressbar_color = self.progressbar_standard
self.progressbar.setObjectName(self.progressbar_color)
self.progressbar.setRange(PROGRESS_BAR_THREAD_START,
PROGRESS_BAR_THREAD_END)
self.progressbar.setTextVisible(True)
self.progressbar.setAlignment(Qt.AlignCenter)
self.progressbar.setVisible(False)
def init_hdf_analysis_wgt(self):
h5_groupbox = HDF5GroupBox(self, title="HDF5 Analysis", layout="Hor")
self.hdf_dock_widget.setObjectName("hdfDockWidget")
self.hdf_dock_widget.setVisible(False)
#hdfDockWidget.setFloating(True)
self.hdf_dock_widget.setContentsMargins(9, 9, 9, 9)
h5_groupbox.analyze_h5_widget.clicked.connect(
self.start_h5_thread)
self.hdf_dock_widget.setWidget(h5_groupbox)
self.addDockWidget(Qt.TopDockWidgetArea, self.hdf_dock_widget)
def restore_application_settings(self):
""" Restore platform-independent application settings
"""
qsettings = QSettings()
if qsettings.value("RecentFiles"):
self.application_recent_files = str(
qsettings.value("RecentFiles")).split(',')
if qsettings.value("BaseWindow/Geometry"):
self.restoreGeometry(qsettings.value("BaseWindow/Geometry"))
if qsettings.value("BaseWindow/State"):
self.restoreState(qsettings.value("BaseWindow/State"))
self.application_geometry = self.geometry()
def save_application_settings(self):
""" Save platform-independent application settings
"""
settings = QSettings()
if self.filename:
settings.setValue("LastFile", str(self.filename))
if self.application_recent_files:
recent_files = ', '.join(self.application_recent_files)
settings.setValue("RecentFiles", (recent_files))
settings.setValue("BaseWindow/Geometry", (self.saveGeometry()))
settings.setValue("BaseWindow/State", (self.saveState()))
@Slot()
def start_h5_thread(self):
pass
@Slot()
def start_analysis_thread(self):
##self.analysis_input_parameters()
if not self.analysis_procedure:
mess = "Analysis thread not configured for this application"
self.show_log_message(MsgSeverity.ERROR, _pymodule, _line(), mess)
self.statusbar.showMessage(mess)
return
self.analysis_thread = self.AnalysisThread(
self, self.analysis_procedure, self.input_parameters)
self.analysis_thread.trigger_thread_event.connect(
self.receive_analysis_results)
self.analysis_thread.started.connect(self.analysis_thread_started)
self.analysis_thread.finished.connect(self.analysis_thread_finished)
self.analysis_thread.start()
QApplication.processEvents()
@Slot()
def analysis_thread_started(self):
""" Change state of widgets when measuring
"""
self.gui_frame.in_measurement_procedure()
QApplication.processEvents()
print("Thread Started")
@Slot()
def analysis_thread_finished(self):
""" Reset widgets to intial pre-measurement state
"""
self.gui_frame.reset_procedure()
#self.progressbar.setObjectName(self.progressbar_color)
#self.progressbar.setFormat("")
#self.progressbar.setValue(PROGRESS_BAR_THREAD_INIT)
#self.progressbar.style().polish(self.progressbar)
QApplication.processEvents()
print("Thread Finished")
@Slot(dict)
def receive_analysis_results(self, all_dict):
print("receive analysis results", all_dict)
self.gui_frame.canvas_update( all_dict['Figure data'])
self.gui_frame.central_tab_widget.setCurrentIndex(1)
self.gui_frame.results_tab_wgt.setCurrentIndex(0)
@Slot()
def receive_abort_analysis(self):
"""Instantiate action on abort
"""
print("Aborting")
if self.analysis_procedure.abort:
return
self.gui_frame.in_abort_procedure()
# Trigger abort signal to the analysis thread
self.analysis_procedure.trigger_abort.emit()
#self.trigger_progressbar.emit(PROGRESS_BAR_THREAD_ABORTING)
QApplication.processEvents()
@Slot(str, str, int, str, dict)
def receive_log_message(self, severity, module, line, message, options={}):
'''Receive message from thread for routing to log window'''
self.show_log_message(severity, module, line, message, options)
self.statusbar.showMessage(message)
@Slot(int)
def receive_progressbar(self, value):
'''Receives update of message'''
if value == PROGRESS_BAR_THREAD_INIT:
self.progressbar.setVisible(False)
self.progressbar.setFormat("")
self.progressbar.reset()
self.progressbar.setObjectName(self.progressbar_color)
elif value == PROGRESS_BAR_THREAD_START:
self.progressbar.setVisible(True)
self.progressbar.setFormat("Measurement started")
self.progressbar.setValue(value)
self.progressbar.setObjectName(self.progressbar_color)
elif value == PROGRESS_BAR_THREAD_ABORTING:
self.progressbar.setFormat(
"Aborting procedure at the next available break point")
self.progressbar.setObjectName(self.progressbar_abort)
mess = "Aborting measurement procedure"
self.show_log_message(
MsgSeverity.WARN.name, _pymodule, _line(), mess)
self.statusbar.showMessage(mess)
elif value == PROGRESS_BAR_THREAD_ABORTED:
self.progressbar.setFormat("Procedure aborted")
self.progressbar.setObjectName(self.progressbar_abort)
mess = "Measurement procedure aborted"
self.show_log_message(
MsgSeverity.WARN.name, _pymodule, _line(), mess)
self.statusbar.showMessage(mess)
QTimer.singleShot(5000, lambda: self.trigger_progressbar.emit(
PROGRESS_BAR_THREAD_INIT))
elif value == PROGRESS_BAR_THREAD_ERROR:
self.progressbar.setFormat("No data returned!")
self.progressbar.setObjectName(self.progressbar_abort)
elif value == PROGRESS_BAR_THREAD_END:
self.progressbar.setFormat("Measurement completed")
self.progressbar.setValue(value)
self.progressbar.setObjectName(self.progressbar_color)
self.daq_analysis_completed = True
QTimer.singleShot(5000, lambda: self.progressbar.setVisible(False))
else:
self.progressbar.setFormat("Measurement in progress...")
self.progressbar.setValue(value)
self.progressbar.setObjectName(self.progressbar_color)
self.progressbar.style().polish(self.progressbar)
QApplication.processEvents()
time.sleep(0.001)
def initialize_application(self, appname=_appname, delay=None,
facility="PSI"):
"""
Initialize application configuration for use by QSettings.
Loads application stylesheet from qrc_resources or input file
These are saved into $HOME/.config
e.g., $HOME/.config/<facility>/appname.conf
Provide QSplashScreen if delay is set.
"""
self.setStyle("motif")
self.setOrganizationName(facility)
self.setOrganizationDomain("psi.ch")
self.setApplicationName(appname)
self.setWindowIcon(QIcon(":/icon.png"))
parser = argparse.ArgumentParser(description="stylesheet")
parser.add_argument("-q", "--qss", action="store", dest="qss")
parser.add_argument("-c", "--config")
parser.add_argument("-f", "--facility")
parser.add_argument("-u", "--user")
#result, unknown = parser.parse_known_args(["--qss", ":/sfop.qss"])
#result, unknown = parser.parse_known_args(["--qss", ":/sfop.qss", "-c", "--facility", "--user"])
#print(result)
#print(unknown)
result=parser.parse_args()
qss_file = result.qss if result.qss else ":/acc.qss"
print(qss_file)
qss = QFile(qss_file)
if qss.open(QIODevice.ReadOnly):
fByteArray = qss.readAll()
self.setStyleSheet(str(fByteArray.data(), encoding='utf-8'))
qss.close()
else:
print("COULD NOT OPEN :/acc.qss stylesheet")
exit(1)
if delay is not None:
delay = max(delay, 5)
start = time.time()
splash_screen = QSplashScreen(QPixmap(":/loading.gif"))
splash_screen.setAttribute(Qt.WA_TranslucentBackground, False)
splash_screen.setWindowFlags(Qt.WindowStaysOnTopHint)
splash_screen.showMessage(
"""
<br><p style='color:black; font-weight:bold;
font-size:28px; margin-right:10px;'>
LOADING APPLICATION:
<font color=royalblue > {0} </font> </p>
<p style='color:black; font-weight:bold;
font-size:20px; margin-right:22px;'>
This message will self-destruct in {1} seconds<br></p>
""".format(appname, round(delay)), Qt.AlignCenter)
width = 860 + (len(appname)-10)*15
height = 220
splash_screen.resize(width, height)
splash_screen.show()
while time.time()-start < delay:
time.sleep(0.01)
self.processEvents()
return splash_screen