Created
October 14, 2016 11:40
-
-
Save ales-erjavec/859f18c18c922c6bc310fd5f8e18eee3 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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