from __future__ import absolute_import, print_function
from qtpy import QtCore, QtWidgets, uic
from collections import OrderedDict
import os
import logging
import pyqtgraph as pg
import threading
[docs]class OrderedAttrDict(object):
def __init__(self):
self._odict = OrderedDict()
[docs] def add(self, name, obj):
self._odict[name] = obj
self.__dict__[name] = obj
return obj
[docs] def keys(self):
return self._odict.keys()
[docs] def values(self):
return self._odict.values()
[docs] def items(self):
return self._odict.items()
def __len__(self):
return len(self._odict)
def __getitem__(self, key):
return self._odict[key]
def __contains__(self, k):
return self._odict.__contains__(k)
[docs]def sibling_path(a, b):
"""
Returns the path of a filename *b* in the same folder as *a*
(i.e. dirname(a)/b )
============== ==============================
**Arguments:**
*a* directory name of pathname *a*
b path suffix
============== ==============================
:returns: combined path of above arguments.
"""
return os.path.join(os.path.dirname(a), b)
[docs]def load_qt_ui_file(ui_filename):
"""
Loads a QT user interface file (files ending in .ui).
This function is typically called from :class:`Measurement` level modules.
"""
### PySide version
#ui_loader = QtUiTools.QUiLoader()
#ui_file = QtCore.QFile(ui_filename)
#ui_file.open(QtCore.QFile.ReadOnly)
#ui = ui_loader.load(ui_file)
#ui_file.close()
### qtpy / PyQt version
ui = uic.loadUi(ui_filename)
return ui
[docs]def confirm_on_close(widget,
title="Close ScopeFoundry?",
message="Do you wish to shut down ScopeFoundry?",
func_on_close=None):
"""
Calls the :class:`ConfirmCloseEventEater` class which asks for user
confirmation in a pop-up dialog upon closing the ScopeFoundry app.
"""
widget.closeEventEater = ConfirmCloseEventEater(title, message, func_on_close)
widget.installEventFilter(widget.closeEventEater)
[docs]class ConfirmCloseEventEater(QtCore.QObject):
"""
Tells the Qt to confirm the closing of the app by the user with a pop-up
confirmation dialog.
"""
def __init__(self, title="Close ScopeFoundry?",
message="Do you wish to shut down ScopeFoundry?",
func_on_close=None):
QtCore.QObject.__init__(self)
self.title = title
self.message = message
self.func_on_close = func_on_close
[docs] def eventFilter(self, obj, event):
"""
Listens for QtCore.QEvent.Close signal and asks the user whether to
close the app in a pop-up dialog."""
if event.type() == QtCore.QEvent.Close:
# eat close event
logging.debug("close")
reply = QtWidgets.QMessageBox.question(None,
self.title,
self.message,
QtWidgets.QMessageBox.Yes,
QtWidgets.QMessageBox.No)
if reply == QtWidgets.QMessageBox.Yes:
logging.debug("closing")
if self.func_on_close:
self.func_on_close()
QtWidgets.QApplication.quit()
event.accept()
else:
event.ignore()
return True
else:
# standard event processing
return QtCore.QObject.eventFilter(self,obj, event)
[docs]def ignore_on_close(widget):
"""
Calls the :class:`IgnoreCloseEventEater` class which intercepts the
QtCore.QEvent.Close signal and prevents the deletion of subwindows and their
associated objects.
"""
widget.ignoreCloseEventEater = IgnoreCloseEventEater()
widget.installEventFilter(widget.ignoreCloseEventEater)
[docs]class IgnoreCloseEventEater(QtCore.QObject):
"""
This class is utilized to prevent the closing of important subwindows
and prevent the deletion of related python objects. Tells the Qt event
manager to ignore calls to close the subwindow when user hits [x] on
the subwindow.
"""
[docs] def eventFilter(self, obj, event):
if event.type() == QtCore.QEvent.Close:
# eat close event
event.ignore()
return True
else:
# standard event processing
return QtCore.QObject.eventFilter(self, obj, event)
[docs]def replace_spinbox_in_layout(old_widget, **kwargs):
new_spinbox = pg.SpinBox()
return replace_widget_in_layout(old_widget, new_widget=new_spinbox, **kwargs)
[docs]def print_all_connected(qobject, signal=None):
if signal is None:
signals = qobject.signals()
else:
signals = [signal]
for signal in qobject.signals():
for slot in qobject.connectedSlots():
print(slot)
[docs]def print_signals_and_slots(obj):
# http://visitusers.org/index.php?title=PySide_Recipes
for i in range(obj.metaObject().methodCount()):
m = obj.metaObject().method(i)
if m.methodType() == QtCore.QMetaMethod.MethodType.Signal:
print("SIGNAL: sig=", m.signature(), "hooked to nslots=",obj.receivers(QtCore.SIGNAL(m.signature())))
elif m.methodType() == QtCore.QMetaMethod.MethodType.Slot:
print("SLOT: sig=", m.signature())
[docs]def get_logger_from_class(obj):
""" returns a named Logger from the logging package using the
full name of the class of the object (obj) as the log name
"""
#return logging.getLogger(obj.__module__ + "." + obj.__class__.__name__)
return logging.getLogger(obj.__class__.__name__)
[docs]def str2bool(v):
return v.lower() in ("yes", "true", "t", "1")
[docs]class QLock(QtCore.QMutex):
[docs] def acquire(self):
self.lock()
[docs] def release(self):
self.unlock()
def __enter__(self):
self.acquire()
return self
def __exit__(self, *args):
self.release()
# https://stackoverflow.com/questions/5327614/logging-lock-acquire-and-release-calls-in-multi-threaded-application
[docs]class LogLock(object):
def __init__(self, name):
self.name = str(name)
self.lock = threading.Lock()
self.log = logging.getLogger('LogLock')
[docs] def acquire(self, blocking=True):
self.log.debug("{0:x} Trying to acquire {1} lock".format(
id(self), self.name))
ret = self.lock.acquire(blocking)
if ret == True:
self.log.debug("{0:x} Acquired {1} lock".format(
id(self), self.name))
else:
self.log.debug("{0:x} Non-blocking aquire of {1} lock failed".format(
id(self), self.name))
return ret
[docs] def release(self):
self.log.debug("{0:x} Releasing {1} lock".format(id(self), self.name))
self.lock.release()
def __enter__(self):
self.acquire()
def __exit__(self, exc_type, exc_val, exc_tb):
self.release()
return False # Do not swallow exceptions