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 <marcelo.lira@openbossa.org>
          Hugo Parente Lima <hugo.pl@gmail.com>
This commit is contained in:
Renato Filho 2011-05-23 11:39:17 -03:00
commit 959aa385f5
7 changed files with 137 additions and 0 deletions

View file

@ -1,7 +1,11 @@
project(libpyside) project(libpyside)
qt4_wrap_cpp(DESTROYLISTENER_MOC "destroylistener.h")
set(libpyside_SRC set(libpyside_SRC
dynamicqmetaobject.cpp dynamicqmetaobject.cpp
destroylistener.cpp
signalmanager.cpp signalmanager.cpp
globalreceiver.cpp globalreceiver.cpp
pysideclassinfo.cpp pysideclassinfo.cpp
@ -11,6 +15,7 @@ set(libpyside_SRC
pysideproperty.cpp pysideproperty.cpp
pysideweakref.cpp pysideweakref.cpp
pyside.cpp pyside.cpp
${DESTROYLISTENER_MOC}
) )
include_directories(${CMAKE_CURRENT_SOURCE_DIR} include_directories(${CMAKE_CURRENT_SOURCE_DIR}
@ -34,6 +39,7 @@ set_target_properties(pyside PROPERTIES
# #
set(libpyside_HEADERS set(libpyside_HEADERS
destroylistener.h
dynamicqmetaobject.h dynamicqmetaobject.h
globalreceiver.h globalreceiver.h
pysideclassinfo.h pysideclassinfo.h

View file

@ -0,0 +1,67 @@
#include "destroylistener.h"
#include <Python.h>
#include <QObject>
#include <shiboken.h>
#include <QDebug>
#include <QMutex>
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

View file

@ -0,0 +1,32 @@
#ifndef PYSIDE_DESTROY_LISTENER
#define PYSIDE_DESTROY_LISTENER
#include <QObject>
#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

View file

@ -31,6 +31,7 @@
#include "pysidemetafunction_p.h" #include "pysidemetafunction_p.h"
#include "pysidemetafunction.h" #include "pysidemetafunction.h"
#include "dynamicqmetaobject.h" #include "dynamicqmetaobject.h"
#include "destroylistener.h"
#include <basewrapper.h> #include <basewrapper.h>
#include <conversions.h> #include <conversions.h>
@ -101,10 +102,12 @@ void registerCleanupFunction(CleanupFunction func)
void runCleanupFunctions() void runCleanupFunctions()
{ {
//PySide::DestroyListener::instance()->destroy();
while (!cleanupFunctionList.isEmpty()) { while (!cleanupFunctionList.isEmpty()) {
CleanupFunction f = cleanupFunctionList.pop(); CleanupFunction f = cleanupFunctionList.pop();
f(); f();
} }
PySide::DestroyListener::destroy();
} }
static void destructionVisitor(SbkObject* pyObj, void* data) static void destructionVisitor(SbkObject* pyObj, void* data)

View file

@ -21,6 +21,7 @@ PYSIDE_TEST(classinfo_test.py)
PYSIDE_TEST(child_event_test.py) PYSIDE_TEST(child_event_test.py)
PYSIDE_TEST(deepcopy_test.py) PYSIDE_TEST(deepcopy_test.py)
PYSIDE_TEST(deletelater_test.py) PYSIDE_TEST(deletelater_test.py)
PYSIDE_TEST(destroysignal_test.py)
PYSIDE_TEST(duck_punching_test.py) PYSIDE_TEST(duck_punching_test.py)
PYSIDE_TEST(hash_test.py) PYSIDE_TEST(hash_test.py)
PYSIDE_TEST(max_signals.py) PYSIDE_TEST(max_signals.py)

View file

@ -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()

View file

@ -8,6 +8,7 @@ import unittest
class Bug576(unittest.TestCase): class Bug576(unittest.TestCase):
def onButtonDestroyed(self, button): def onButtonDestroyed(self, button):
self._destroyed = True self._destroyed = True
self.assert_(isinstance(button, QtGui.QPushButton))
def testWidgetParent(self): def testWidgetParent(self):
self._destroyed = False self._destroyed = False