Skip to content

Instantly share code, notes, and snippets.

@ales-erjavec
Created October 14, 2016 11:40
Show Gist options
  • Save ales-erjavec/859f18c18c922c6bc310fd5f8e18eee3 to your computer and use it in GitHub Desktop.
Save ales-erjavec/859f18c18c922c6bc310fd5f8e18eee3 to your computer and use it in GitHub Desktop.
import weakref
from types import SimpleNamespace as namespace
from PyQt4.QtCore import QObject
class qobjref(object):
"""
A 'guarded reference' to a QObject.
An instance of a `qobjref` holds a reference to a `QObject` for as long
as it is alive (not destroyed by C++ destructor).
Example
-------
>>> obj = QObject()
>>> ref = qobjref(obj)
>>> assert ref() is obj
>>> sip.delete(obj) # forcibly delete it
>>> assert ref() is None
Note
----
This is not thread safe in the sense that the object can be deleted
from a different thread while the ref() is returning it.
Note
----
`qobjref` keeps a reference to the object, meaning it will not be
garbage collected until qobjref is also reaped. Use qobjref_weak
for weak references.
See also
--------
QPointer
"""
__slots__ = ("__obj", "__state", "__weakref__")
def __init__(self, obj):
if not isinstance(obj, QObject):
raise TypeError(
"'qobjref' requires a 'QObject' but received a '{}'"
.format(type(obj).__name__))
self.__obj = obj
# finalize when self is collected
def disconnect(weakselfref):
# print("disconnect")
if state.objref is None:
return
objref = state.objref()
if objref is not None:
objref.destroyed.disconnect(state.zero_ref)
# destroy/zero the reference when QObject is destroyed
def zero_ref():
# print("zero_ref")
selfref = state.selfref()
if selfref is not None:
selfref.__obj = None
state.objref = None
state = namespace(
selfref=weakref.ref(self, disconnect),
objref=weakref.ref(obj),
zero_ref=zero_ref,
disconnect=disconnect)
self.__state = state
# Must not capture self in the zero_ref's closure
self.__obj.destroyed.connect(zero_ref)
def __call__(self):
"""
Return the QObject instance or None if it was destroyed.
Return
------
obj : Optional[QObject]
"""
return self.__obj
def __repr__(self):
objrep = "to " + repr(self.__obj)
if self.__obj is None:
objrep = "dead"
return "<qobjref at 0x%x; " % id(self) + objrep + ">"
class qobjref_weak(object):
"""
A weak 'guarded reference' to a QObject.
Similar to `qobjref`, except that the reference to the QObject is weak
Example
-------
>>> obj = QObject()
>>> ref = qobjref_weak(obj)
>>> assert ref() is obj
>>> sip.delete(obj) # forcibly delete it
>>> assert ref() is None
>>> obj = QObject()
>>> ref = qobjref_weak(obj)
>>> assert ref() is obj
>>> del obj # assuming ref count is 1
>>> assert ref() is None
Note
----
This is not thread safe in the sense that the object can be
deleted from a different thread while the ref() is returning it.
See also
--------
qobjref, QPointer
"""
__slots__ = ("__obj_ref", "__state", "__weakref__")
def __init__(self, obj):
if not isinstance(obj, QObject):
raise TypeError(
"qobjref_weak(obj) requires a 'QObject' but received a '{}'"
.format(type(obj).__name__))
self.__obj_ref = weakref.ref(obj)
# finalize when self is collected
def disconnect(weakselfref):
if state.objref is None:
return
objref = state.objref()
if objref is not None:
objref.destroyed.disconnect(state.zero_ref)
# destroy/zero the reference when QObject is destroyed
def zero_ref():
# print("zero_ref")
selfref = state.selfref()
if selfref is not None:
selfref.__obj_ref = None
state.objref = None
state = namespace(
selfref=weakref.ref(self, disconnect),
objref=weakref.ref(obj),
zero_ref=zero_ref,
disconnect=disconnect)
self.__state = state
# Must not capture self in the zero_ref's closure
obj.destroyed.connect(zero_ref)
def __call__(self):
"""
Return the QObject instance or None if it is no longer alive
Return
------
obj : Optional[QObject]
"""
ref = self.__obj_ref
if ref is not None:
return ref()
else:
return None
def __repr__(self):
obj = self.__call__()
if obj is not None:
objrep = "to " + repr(obj)
else:
objrep = "dead"
return "<qobjref_weak at 0x%x; " % id(self) + objrep + ">"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment