From 959aa385f576aca35f86fc04f8336e6a4ce044f1 Mon Sep 17 00:00:00 2001 From: Renato Filho Date: Mon, 23 May 2011 11:39:17 -0300 Subject: [PATCH] Created DestroyListener class. This class is used to keep the Python object live until the signal destroyed emission. With this is possible to use the QObject on destruction signal. Fixes bug #505. Reviewer: Marcelo Lira Hugo Parente Lima --- libpyside/CMakeLists.txt | 6 +++ libpyside/destroylistener.cpp | 67 ++++++++++++++++++++++++++++++ libpyside/destroylistener.h | 32 ++++++++++++++ libpyside/pyside.cpp | 3 ++ tests/QtCore/CMakeLists.txt | 1 + tests/QtCore/destroysignal_test.py | 27 ++++++++++++ tests/QtGui/bug_576.py | 1 + 7 files changed, 137 insertions(+) create mode 100644 libpyside/destroylistener.cpp create mode 100644 libpyside/destroylistener.h create mode 100644 tests/QtCore/destroysignal_test.py diff --git a/libpyside/CMakeLists.txt b/libpyside/CMakeLists.txt index 2d2a613..e05a8cd 100644 --- a/libpyside/CMakeLists.txt +++ b/libpyside/CMakeLists.txt @@ -1,7 +1,11 @@ project(libpyside) + +qt4_wrap_cpp(DESTROYLISTENER_MOC "destroylistener.h") + set(libpyside_SRC dynamicqmetaobject.cpp + destroylistener.cpp signalmanager.cpp globalreceiver.cpp pysideclassinfo.cpp @@ -11,6 +15,7 @@ set(libpyside_SRC pysideproperty.cpp pysideweakref.cpp pyside.cpp + ${DESTROYLISTENER_MOC} ) include_directories(${CMAKE_CURRENT_SOURCE_DIR} @@ -34,6 +39,7 @@ set_target_properties(pyside PROPERTIES # set(libpyside_HEADERS + destroylistener.h dynamicqmetaobject.h globalreceiver.h pysideclassinfo.h diff --git a/libpyside/destroylistener.cpp b/libpyside/destroylistener.cpp new file mode 100644 index 0000000..600d922 --- /dev/null +++ b/libpyside/destroylistener.cpp @@ -0,0 +1,67 @@ +#include "destroylistener.h" + +#include +#include +#include +#include +#include + +PySide::DestroyListener* PySide::DestroyListener::m_instance = 0; + +namespace PySide +{ + +struct DestroyListenerPrivate +{ + static bool m_destroyed; +}; + + +DestroyListener* DestroyListener::instance() +{ + if (!m_instance) + m_instance = new DestroyListener(0); + return m_instance; +} + +void DestroyListener::destroy() +{ + if (m_instance) { + m_instance->disconnect(); + delete m_instance; + m_instance = 0; + } +} + +void DestroyListener::listen(QObject *obj) +{ + SbkObject* wrapper = Shiboken::BindingManager::instance().retrieveWrapper(obj); + if (!wrapper) // avoid problem with multiple inheritance + return; + + if (Py_IsInitialized() == 0) + onObjectDestroyed(obj); + else + QObject::connect(obj, SIGNAL(destroyed(QObject*)), this, SLOT(onObjectDestroyed(QObject*)), Qt::DirectConnection); +} + +void DestroyListener::onObjectDestroyed(QObject* obj) +{ + SbkObject* wrapper = Shiboken::BindingManager::instance().retrieveWrapper(obj); + if (wrapper) //make sure the object exists before destroy + Shiboken::Object::destroy(wrapper, obj); +} + +DestroyListener::DestroyListener(QObject *parent) + : QObject(parent) +{ + m_d = new DestroyListenerPrivate(); +} + +DestroyListener::~DestroyListener() +{ + delete m_d; +} + +}//namespace + diff --git a/libpyside/destroylistener.h b/libpyside/destroylistener.h new file mode 100644 index 0000000..54dda2c --- /dev/null +++ b/libpyside/destroylistener.h @@ -0,0 +1,32 @@ +#ifndef PYSIDE_DESTROY_LISTENER +#define PYSIDE_DESTROY_LISTENER + + +#include +#include "pysidemacros.h" + +namespace PySide +{ +class DestroyListenerPrivate; +class PYSIDE_API DestroyListener : public QObject +{ + Q_OBJECT + public: + static DestroyListener* instance(); + static void destroy(); + void listen(QObject* obj); + + public slots: + void onObjectDestroyed(QObject* obj); + + private: + static DestroyListener* m_instance; + DestroyListenerPrivate* m_d; + DestroyListener(QObject *parent); + ~DestroyListener(); +}; + +}//namespace + +#endif + diff --git a/libpyside/pyside.cpp b/libpyside/pyside.cpp index a4fcbcc..af763aa 100644 --- a/libpyside/pyside.cpp +++ b/libpyside/pyside.cpp @@ -31,6 +31,7 @@ #include "pysidemetafunction_p.h" #include "pysidemetafunction.h" #include "dynamicqmetaobject.h" +#include "destroylistener.h" #include #include @@ -101,10 +102,12 @@ void registerCleanupFunction(CleanupFunction func) void runCleanupFunctions() { + //PySide::DestroyListener::instance()->destroy(); while (!cleanupFunctionList.isEmpty()) { CleanupFunction f = cleanupFunctionList.pop(); f(); } + PySide::DestroyListener::destroy(); } static void destructionVisitor(SbkObject* pyObj, void* data) diff --git a/tests/QtCore/CMakeLists.txt b/tests/QtCore/CMakeLists.txt index 251add1..365305f 100644 --- a/tests/QtCore/CMakeLists.txt +++ b/tests/QtCore/CMakeLists.txt @@ -21,6 +21,7 @@ PYSIDE_TEST(classinfo_test.py) PYSIDE_TEST(child_event_test.py) PYSIDE_TEST(deepcopy_test.py) PYSIDE_TEST(deletelater_test.py) +PYSIDE_TEST(destroysignal_test.py) PYSIDE_TEST(duck_punching_test.py) PYSIDE_TEST(hash_test.py) PYSIDE_TEST(max_signals.py) diff --git a/tests/QtCore/destroysignal_test.py b/tests/QtCore/destroysignal_test.py new file mode 100644 index 0000000..1c5f986 --- /dev/null +++ b/tests/QtCore/destroysignal_test.py @@ -0,0 +1,27 @@ +from PySide.QtCore import QTimer, QObject +import sys +import unittest + +class TestDestroySignal(unittest.TestCase): + def onObjectDestroyed(self, timer): + self.assert_(isinstance(timer, QTimer)) + self._destroyed = True + + def testSignal(self): + self._destroyed = False + t = QTimer() + t.destroyed[QObject].connect(self.onObjectDestroyed) + del t + self.assert_(self._destroyed) + + def testWithParent(self): + self._destroyed = False + p = QTimer() + t = QTimer(p) + t.destroyed[QObject].connect(self.onObjectDestroyed) + del p + self.assert_(self._destroyed) + +if __name__ == '__main__': + unittest.main() + diff --git a/tests/QtGui/bug_576.py b/tests/QtGui/bug_576.py index b3c11a3..e66e109 100644 --- a/tests/QtGui/bug_576.py +++ b/tests/QtGui/bug_576.py @@ -8,6 +8,7 @@ import unittest class Bug576(unittest.TestCase): def onButtonDestroyed(self, button): self._destroyed = True + self.assert_(isinstance(button, QtGui.QPushButton)) def testWidgetParent(self): self._destroyed = False