Implement disconnect function for Python callback.

Reviewed by Hugo Parente <hugo.lima@openbossa.org>
This commit is contained in:
renato 2009-12-30 11:45:09 -03:00 • committed by Hugo Lima
commit 359c973b42
8 changed files with 175 additions and 14 deletions

View file

@ -80,5 +80,47 @@ static bool qobjectConnectCallback(QObject* source, const char* signal, PyObject
} }
slotIndex = metaObject->indexOfSlot(slot); slotIndex = metaObject->indexOfSlot(slot);
} }
return QMetaObject::connect(source, signalIndex, receiver, slotIndex, type); if (QMetaObject::connect(source, signalIndex, receiver, slotIndex, type)) {
if (usingGlobalReceiver)
signalManager.globalReceiverConnectNotify(slotIndex);
return true;
}
return false;
}
static bool qobjectDisconnectCallback(QObject* source, const char* signal, PyObject* callback)
{
if (!PySide::checkSignal(signal))
return false;
PySide::SignalManager& signalManager = PySide::SignalManager::instance();
// Extract receiver from callback
bool usingGlobalReceiver;
QObject* receiver = 0;
PyObject* self;
if (PyMethod_Check(callback)) {
self = PyMethod_GET_SELF(callback);
if (SbkQObject_Check(self))
receiver = SbkQObject_cptr(self);
}
usingGlobalReceiver = !receiver;
if (usingGlobalReceiver)
receiver = signalManager.globalReceiver();
const QMetaObject* metaObject = receiver->metaObject();
const QByteArray callbackSig = PySide::getCallbackSignature(signal, callback, usingGlobalReceiver).toAscii();
QByteArray qtSlotName(callbackSig);
qtSlotName = qtSlotName.prepend('1');
if (QObject::disconnect(source, signal, receiver, qtSlotName.constData())) {
if (usingGlobalReceiver) {
int slotIndex = metaObject->indexOfSlot(callbackSig.constData());
signalManager.globalReceiverDisconnectNotify(slotIndex);
}
return true;
}
return false;
} }

View file

@ -1346,9 +1346,18 @@
%PYARG_0 = %CONVERTTOPYTHON[bool](PySide::SignalManager::instance().emitSignal(%CPPSELF, %1, %PYARG_2)); %PYARG_0 = %CONVERTTOPYTHON[bool](PySide::SignalManager::instance().emitSignal(%CPPSELF, %1, %PYARG_2));
</inject-code> </inject-code>
</add-function> </add-function>
<!-- <modify-function signature="disconnect(const QObject*, const char*)" remove="all"/> --> <add-function signature="disconnect(const char *, PyCallable*)" return-type="bool">
<!-- <modify-function signature="disconnect(const char*, const QObject*, const char*)" remove="all"/> --> <inject-code class="target" position="beginning">
<!-- <modify-function signature="disconnect(const QObject*, const char*, const QObject*, const char *)" remove="all"/> --> // %FUNCTION_NAME() - disable generation of function call.
%PYARG_0 = %CONVERTTOPYTHON[bool](qobjectDisconnectCallback(%CPPSELF, %1, %2));
</inject-code>
</add-function>
<add-function signature="disconnect(const QObject*, const char*, PyCallable*)" return-type="bool">
<inject-code class="target" position="beginning">
// %FUNCTION_NAME() - disable generation of function call.
%PYARG_0 = %CONVERTTOPYTHON[bool](qobjectDisconnectCallback(%1, %2, %3));
</inject-code>
</add-function>
<inject-code class="native" position="beginning"> <inject-code class="native" position="beginning">
static bool static bool
qobjectInheritsInternal(PyTypeObject *objType, const char *class_name) qobjectInheritsInternal(PyTypeObject *objType, const char *class_name)

View file

@ -39,6 +39,7 @@
#include <QObject> #include <QObject>
#include <cstring> #include <cstring>
#include <QDebug> #include <QDebug>
#include <QMetaMethod>
using namespace PySide; using namespace PySide;
@ -83,6 +84,20 @@ void DynamicQMetaObject::addSlot(const char* slot)
updateMetaObject(); updateMetaObject();
} }
void DynamicQMetaObject::removeSlot(uint index)
{
QMetaMethod m = method(index);
if (m_slots.removeAll(m.signature()))
updateMetaObject();
}
void DynamicQMetaObject::removeSignal(uint index)
{
QMetaMethod m = method(index);
if (m_signals.removeAll(m.signature()))
updateMetaObject();
}
void DynamicQMetaObject::updateMetaObject() void DynamicQMetaObject::updateMetaObject()
{ {
// these values are from moc source code, generator.cpp:66 // these values are from moc source code, generator.cpp:66

View file

@ -50,9 +50,13 @@ class PYSIDE_API DynamicQMetaObject : public QMetaObject
public: public:
DynamicQMetaObject(const char* className, const QMetaObject* metaObject); DynamicQMetaObject(const char* className, const QMetaObject* metaObject);
~DynamicQMetaObject(); ~DynamicQMetaObject();
void addSignal(const char* signal); void addSignal(const char* signal);
void addSlot(const char* slot); void addSlot(const char* slot);
void removeSignal(uint idex);
void removeSlot(uint index);
private: private:
QLinkedList<QByteArray> m_signals; QLinkedList<QByteArray> m_signals;
QLinkedList<QByteArray> m_slots; QLinkedList<QByteArray> m_slots;

View file

@ -39,16 +39,87 @@
#include <autodecref.h> #include <autodecref.h>
#include "typeresolver.h" #include "typeresolver.h"
namespace PySide
{
class DynamicSlotData
{
public:
DynamicSlotData(PyObject *callback);
void incRef();
void decRef();
int refCount() const;
PyObject *callback() const;
~DynamicSlotData();
private:
int m_refCount;
PyObject *m_callback;
};
}
using namespace PySide; using namespace PySide;
GlobalReceiver::GlobalReceiver() : m_metaObject("GlobalReceiver", &QObject::staticMetaObject) DynamicSlotData::DynamicSlotData(PyObject *callback)
: m_refCount(0)
{
m_callback = callback;
Py_INCREF(callback);
}
void DynamicSlotData::incRef()
{
m_refCount++;
}
void DynamicSlotData::decRef()
{
m_refCount--;
}
int DynamicSlotData::refCount() const
{
return m_refCount;
}
PyObject *DynamicSlotData::callback() const
{
return m_callback;
}
DynamicSlotData::~DynamicSlotData()
{
Py_XDECREF(m_callback);
}
GlobalReceiver::GlobalReceiver()
: m_metaObject("GlobalReceiver", &QObject::staticMetaObject)
{ {
} }
GlobalReceiver::~GlobalReceiver() GlobalReceiver::~GlobalReceiver()
{ {
foreach(PyObject* obj, m_slotReceivers) foreach(DynamicSlotData* data, m_slotReceivers)
Py_DECREF(obj); delete data;
}
void GlobalReceiver::connectNotify(int slotId)
{
if (m_slotReceivers.contains(slotId))
m_slotReceivers[slotId]->incRef();
}
void GlobalReceiver::disconnectNotify(int slotId)
{
if (m_slotReceivers.contains(slotId)) {
DynamicSlotData *data = m_slotReceivers[slotId];
data->decRef();
if (data->refCount() == 0)
removeSlot(slotId);
}
} }
const QMetaObject* GlobalReceiver::metaObject() const const QMetaObject* GlobalReceiver::metaObject() const
@ -60,8 +131,9 @@ void GlobalReceiver::addSlot(const char* slot, PyObject* callback)
{ {
m_metaObject.addSlot(slot); m_metaObject.addSlot(slot);
int slotId = m_metaObject.indexOfSlot(slot); int slotId = m_metaObject.indexOfSlot(slot);
Py_INCREF(callback); if (!m_slotReceivers.contains(slotId)) {
m_slotReceivers[slotId] = callback; m_slotReceivers[slotId] = new DynamicSlotData(callback);
}
bool isShortCircuit = true; bool isShortCircuit = true;
for (int i = 0; slot[i]; ++i) { for (int i = 0; slot[i]; ++i) {
@ -78,8 +150,9 @@ void GlobalReceiver::addSlot(const char* slot, PyObject* callback)
void GlobalReceiver::removeSlot(int slotId) void GlobalReceiver::removeSlot(int slotId)
{ {
PyObject* obj = m_slotReceivers.take(slotId); delete m_slotReceivers.take(slotId);
Py_XDECREF(obj); m_metaObject.removeSlot(slotId);
m_shortCircuitSlots.remove(slotId);
} }
int GlobalReceiver::qt_metacall(QMetaObject::Call call, int id, void** args) int GlobalReceiver::qt_metacall(QMetaObject::Call call, int id, void** args)
@ -89,14 +162,15 @@ int GlobalReceiver::qt_metacall(QMetaObject::Call call, int id, void** args)
QMetaMethod slot = m_metaObject.method(id); QMetaMethod slot = m_metaObject.method(id);
Q_ASSERT(slot.methodType() == QMetaMethod::Slot); Q_ASSERT(slot.methodType() == QMetaMethod::Slot);
PyObject* callback = m_slotReceivers.value(id); DynamicSlotData* data = m_slotReceivers.value(id);
if (!callback) { if (!data) {
qWarning() << "Unknown global slot, id:" << id; qWarning() << "Unknown global slot, id:" << id;
return -1; return -1;
} }
int numArgs; int numArgs;
PyObject* retval = 0; PyObject* retval = 0;
PyObject* callback = data->callback();
if (m_shortCircuitSlots.contains(id)) { if (m_shortCircuitSlots.contains(id)) {
retval = PyObject_CallObject(callback, reinterpret_cast<PyObject*>(args[1])); retval = PyObject_CallObject(callback, reinterpret_cast<PyObject*>(args[1]));
} else { } else {

View file

@ -44,6 +44,8 @@
namespace PySide namespace PySide
{ {
class DynamicSlotData;
class GlobalReceiver : public QObject class GlobalReceiver : public QObject
{ {
public: public:
@ -53,10 +55,12 @@ public:
const QMetaObject* metaObject() const; const QMetaObject* metaObject() const;
void addSlot(const char* slot, PyObject* callback); void addSlot(const char* slot, PyObject* callback);
void removeSlot(int slotId); void removeSlot(int slotId);
void connectNotify(int slotId);
void disconnectNotify(int slotId);
private: private:
DynamicQMetaObject m_metaObject; DynamicQMetaObject m_metaObject;
QHash<int, PyObject* > m_slotReceivers;
QSet<int> m_shortCircuitSlots; QSet<int> m_shortCircuitSlots;
QHash<int, DynamicSlotData* > m_slotReceivers;
}; };
} }

View file

@ -164,6 +164,16 @@ QObject* SignalManager::globalReceiver()
return &m_d->m_globalReceiver; return &m_d->m_globalReceiver;
} }
void SignalManager::globalReceiverConnectNotify(int slotIndex)
{
m_d->m_globalReceiver.connectNotify(slotIndex);
}
void SignalManager::globalReceiverDisconnectNotify(int slotIndex)
{
m_d->m_globalReceiver.disconnectNotify(slotIndex);
}
void SignalManager::addGlobalSlot(const char* slot, PyObject* callback) void SignalManager::addGlobalSlot(const char* slot, PyObject* callback)
{ {
m_d->m_globalReceiver.addSlot(slot, callback); m_d->m_globalReceiver.addSlot(slot, callback);

View file

@ -59,6 +59,9 @@ public:
static int qt_metacall(QObject* object, QMetaObject::Call call, int id, void** args); static int qt_metacall(QObject* object, QMetaObject::Call call, int id, void** args);
void addGlobalSlot(const char* slot, PyObject* callback); void addGlobalSlot(const char* slot, PyObject* callback);
void globalReceiverConnectNotify(int slotIndex);
void globalReceiverDisconnectNotify(int slotIndex);
private: private:
struct SignalManagerPrivate; struct SignalManagerPrivate;
SignalManagerPrivate* m_d; SignalManagerPrivate* m_d;