aboutsummaryrefslogtreecommitdiffstats
path: root/libpyside
diff options
context:
space:
mode:
authorRenato Filho <renato.filho@openbossa.org>2011-08-18 17:51:13 -0300
committerHugo Parente Lima <hugo.pl@gmail.com>2012-03-08 16:54:46 -0300
commit7b2c4242ad1d86bcc2da43a8482dbd39b3ac988e (patch)
treeaffbe074d73898afb10de5ae4685d698c5fb2f70 /libpyside
parentd00af325c696acc390dd3565be82cd22b3b94658 (diff)
Implemented GlobalReceiverV2.
A new implementation of GlobalRecever that optmize the signal connection. Fixes bug #928. Reviewer: Marcelo Lira <marcelo.lira@openbossa.org> Luciano Wolf <luciano.wolf@openbossa.org>
Diffstat (limited to 'libpyside')
-rw-r--r--libpyside/CMakeLists.txt1
-rw-r--r--libpyside/globalreceiver.cpp1
-rw-r--r--libpyside/globalreceiver.h1
-rw-r--r--libpyside/globalreceiverv2.cpp308
-rw-r--r--libpyside/globalreceiverv2.h131
-rw-r--r--libpyside/signalmanager.cpp66
-rw-r--r--libpyside/signalmanager.h25
7 files changed, 525 insertions, 8 deletions
diff --git a/libpyside/CMakeLists.txt b/libpyside/CMakeLists.txt
index e05a8cd7e..219294509 100644
--- a/libpyside/CMakeLists.txt
+++ b/libpyside/CMakeLists.txt
@@ -8,6 +8,7 @@ set(libpyside_SRC
destroylistener.cpp
signalmanager.cpp
globalreceiver.cpp
+ globalreceiverv2.cpp
pysideclassinfo.cpp
pysidemetafunction.cpp
pysidesignal.cpp
diff --git a/libpyside/globalreceiver.cpp b/libpyside/globalreceiver.cpp
index 435e22aa0..17f0090f7 100644
--- a/libpyside/globalreceiver.cpp
+++ b/libpyside/globalreceiver.cpp
@@ -297,3 +297,4 @@ int GlobalReceiver::qt_metacall(QMetaObject::Call call, int id, void** args)
return -1;
}
+
diff --git a/libpyside/globalreceiver.h b/libpyside/globalreceiver.h
index 76c1246d6..4eb2b9af9 100644
--- a/libpyside/globalreceiver.h
+++ b/libpyside/globalreceiver.h
@@ -56,3 +56,4 @@ private:
}
#endif
+
diff --git a/libpyside/globalreceiverv2.cpp b/libpyside/globalreceiverv2.cpp
new file mode 100644
index 000000000..c31a9d2f6
--- /dev/null
+++ b/libpyside/globalreceiverv2.cpp
@@ -0,0 +1,308 @@
+/*
+* This file is part of the PySide project.
+*
+* Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
+*
+* Contact: PySide team <contact@pyside.org>
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 2.1 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "globalreceiverv2.h"
+#include "dynamicqmetaobject_p.h"
+#include "pysideweakref.h"
+
+#include <QMetaMethod>
+#include <QDebug>
+#include <QEvent>
+#include <QLinkedList>
+#include <autodecref.h>
+#include <gilstate.h>
+
+#include "typeresolver.h"
+#include "signalmanager.h"
+
+#define RECEIVER_DESTROYED_SLOT_NAME "__receiverDestroyed__(QObject*)"
+
+namespace
+{
+ static int DESTROY_SIGNAL_ID = 0;
+ static int DESTROY_SLOT_ID = 0;
+}
+
+namespace PySide
+{
+class DynamicSlotDataV2
+{
+ public:
+ DynamicSlotDataV2(PyObject* callback, GlobalReceiverV2* parent);
+ ~DynamicSlotDataV2();
+
+ int addSlot(const char* signature);
+ int id(const char* signature) const;
+ PyObject* call(PyObject* args);
+ QByteArray hash() const;
+ void notify();
+
+ static void onCallbackDestroyed(void* data);
+ static QByteArray hash(PyObject *callback);
+
+
+ private:
+ bool m_isMethod;
+ PyObject* m_callback;
+ PyObject* m_pythonSelf;
+ PyObject* m_pyClass;
+ PyObject* m_weakRef;
+ QMap<QByteArray, int> m_signatures;
+ GlobalReceiverV2* m_parent;
+ QByteArray m_hash;
+};
+
+}
+
+using namespace PySide;
+
+DynamicSlotDataV2::DynamicSlotDataV2(PyObject* callback, GlobalReceiverV2* parent)
+ : m_pythonSelf(0), m_pyClass(0), m_weakRef(0), m_parent(parent)
+{
+ m_isMethod = PyMethod_Check(callback);
+ if (m_isMethod) {
+ //Can not store calback pointe because this will be destroyed at the end of the scope
+ //To avoid increment intance reference keep the callback information
+ m_callback = PyMethod_GET_FUNCTION(callback);
+ m_pyClass = PyMethod_GET_CLASS(callback);
+ m_pythonSelf = PyMethod_GET_SELF(callback);
+
+ //monitor class from method lifetime
+ m_weakRef = WeakRef::create(m_pythonSelf, DynamicSlotDataV2::onCallbackDestroyed, this);
+
+ m_hash = QByteArray::number((qlonglong)PyObject_Hash(m_callback))
+ + QByteArray::number((qlonglong)PyObject_Hash(m_pythonSelf));
+
+ } else {
+ m_callback = callback;
+ Py_INCREF(m_callback);
+
+ m_hash = QByteArray::number((qlonglong)PyObject_Hash(m_callback));
+ }
+}
+
+QByteArray DynamicSlotDataV2::hash() const
+{
+ return m_hash;
+}
+
+QByteArray DynamicSlotDataV2::hash(PyObject* callback)
+{
+ Shiboken::GilState gil;
+ if (PyMethod_Check(callback))
+ return QByteArray::number((qlonglong)PyObject_Hash(PyMethod_GET_FUNCTION(callback)))
+ + QByteArray::number((qlonglong)PyObject_Hash(PyMethod_GET_SELF(callback)));
+ else
+ return QByteArray::number((qlonglong)PyObject_Hash(callback));
+}
+
+PyObject* DynamicSlotDataV2::call(PyObject* args)
+{
+ PyObject* callback = m_callback;
+
+ //create a callback based on method data
+ if (m_isMethod)
+ callback = PyMethod_New(m_callback, m_pythonSelf, m_pyClass);
+
+ PyObject* result = PyObject_CallObject(callback, args);
+
+ if (m_isMethod)
+ Py_DECREF(callback);
+
+ return result;
+}
+
+int DynamicSlotDataV2::id(const char* signature) const
+{
+ if (m_signatures.contains(signature))
+ return m_signatures[signature];
+ return -1;
+}
+
+int DynamicSlotDataV2::addSlot(const char* signature)
+{
+ int index = id(signature);
+ if (index == -1) {
+ DynamicQMetaObject *dmo = const_cast<DynamicQMetaObject*>(reinterpret_cast<const DynamicQMetaObject*>(m_parent->metaObject()));
+ index = m_signatures[signature] = dmo->addSlot(signature);
+ }
+ return index;
+}
+
+void DynamicSlotDataV2::onCallbackDestroyed(void *data)
+{
+ DynamicSlotDataV2* self = reinterpret_cast<DynamicSlotDataV2*>(data);
+ self->m_weakRef = 0;
+ delete self->m_parent;
+}
+
+DynamicSlotDataV2::~DynamicSlotDataV2()
+{
+ Shiboken::GilState gil;
+
+ Py_XDECREF(m_weakRef);
+ m_weakRef = 0;
+
+ if (!m_isMethod)
+ Py_DECREF(m_callback);
+}
+
+GlobalReceiverV2::GlobalReceiverV2(PyObject *callback, SharedMap map)
+ : QObject(0), m_metaObject(GLOBAL_RECEIVER_CLASS_NAME, &QObject::staticMetaObject), m_sharedMap(map)
+{
+ m_data = new DynamicSlotDataV2(callback, this);
+ m_metaObject.addSlot(RECEIVER_DESTROYED_SLOT_NAME);
+ m_metaObject.update();
+ m_refs.append(NULL);
+
+
+ if (DESTROY_SIGNAL_ID == 0)
+ DESTROY_SIGNAL_ID = QObject::staticMetaObject.indexOfSignal("destroyed(QObject*)");
+
+ if (DESTROY_SLOT_ID == 0)
+ DESTROY_SLOT_ID = m_metaObject.indexOfSlot(RECEIVER_DESTROYED_SLOT_NAME);
+
+
+}
+
+GlobalReceiverV2::~GlobalReceiverV2()
+{
+ m_refs.clear();
+ //Remove itself from map
+ m_sharedMap->remove(m_data->hash());
+ delete m_data;
+}
+
+int GlobalReceiverV2::addSlot(const char* signature)
+{
+ return m_data->addSlot(signature);
+}
+
+void GlobalReceiverV2::incRef(const QObject* link)
+{
+ if (link) {
+ if (!m_refs.contains(link)) {
+ if (QMetaObject::connect(link, DESTROY_SIGNAL_ID, this, DESTROY_SLOT_ID))
+ m_refs.append(link);
+ else
+ Q_ASSERT(false);
+ } else {
+ m_refs.append(link);
+ }
+ } else {
+ m_refs.append(NULL);
+ }
+}
+
+void GlobalReceiverV2::decRef(const QObject* link)
+{
+ if (m_refs.size() <= 0)
+ return;
+
+
+ m_refs.removeOne(link);
+ if (link) {
+ if (!m_refs.contains(link)) {
+ bool result = QMetaObject::disconnect(link, DESTROY_SIGNAL_ID, this, DESTROY_SLOT_ID);
+ Q_ASSERT(result);
+ if (!result)
+ return;
+ }
+ }
+
+ if (m_refs.size() == 0)
+ delete this;
+
+}
+
+int GlobalReceiverV2::refCount(const QObject* link) const
+{
+ if (link)
+ return m_refs.count(link);
+
+ return m_refs.size();
+}
+
+void GlobalReceiverV2::notify()
+{
+ QSet<const QObject*> objs = QSet<const QObject*>::fromList(m_refs);
+ foreach(const QObject* o, objs) {
+ QMetaObject::disconnect(o, DESTROY_SIGNAL_ID, this, DESTROY_SLOT_ID);
+ QMetaObject::connect(o, DESTROY_SIGNAL_ID, this, DESTROY_SLOT_ID);
+ }
+}
+
+QByteArray GlobalReceiverV2::hash() const
+{
+ return m_data->hash();
+}
+
+QByteArray GlobalReceiverV2::hash(PyObject* callback)
+{
+ return DynamicSlotDataV2::hash(callback);
+}
+
+const QMetaObject* GlobalReceiverV2::metaObject() const
+{
+ return m_metaObject.update();
+}
+
+int GlobalReceiverV2::qt_metacall(QMetaObject::Call call, int id, void** args)
+{
+ Q_ASSERT(call == QMetaObject::InvokeMetaMethod);
+ Q_ASSERT(id >= QObject::staticMetaObject.methodCount());
+
+ QMetaMethod slot = metaObject()->method(id);
+ Q_ASSERT(slot.methodType() == QMetaMethod::Slot);
+
+ if (id == DESTROY_SLOT_ID) {
+ if (m_refs.size() == 0)
+ return -1;
+ QObject *obj = *(QObject**)args[1];
+ incRef(); //keep the object live (safe ref)
+ m_refs.removeAll(obj); // remove all refs to this object
+ decRef(); //remove the safe ref
+ } else {
+ Shiboken::GilState gil;
+ PyObject* retval = 0;
+
+ bool isShortCurt = (strstr(slot.signature(), "(") == 0);
+ if (isShortCurt) {
+ retval = m_data->call(reinterpret_cast<PyObject*>(args[1]));
+ } else {
+ QList<QByteArray> paramTypes = slot.parameterTypes();
+ Shiboken::AutoDecRef preparedArgs(PyTuple_New(paramTypes.count()));
+ for (int i = 0, max = paramTypes.count(); i < max; ++i) {
+ PyObject* arg = Shiboken::TypeResolver::get(paramTypes[i].constData())->toPython(args[i+1]); // Do not increment the reference
+ PyTuple_SET_ITEM(preparedArgs.object(), i, arg);
+ }
+ retval = m_data->call(preparedArgs);
+ }
+
+ if (!retval)
+ PyErr_Print();
+ else
+ Py_DECREF(retval);
+ }
+
+ return -1;
+}
diff --git a/libpyside/globalreceiverv2.h b/libpyside/globalreceiverv2.h
new file mode 100644
index 000000000..28f470306
--- /dev/null
+++ b/libpyside/globalreceiverv2.h
@@ -0,0 +1,131 @@
+/*
+* This file is part of the PySide project.
+*
+* Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
+*
+* Contact: PySide team <contact@pyside.org>
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 2.1 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef GLOBALRECEIVER_V2_H
+#define GLOBALRECEIVER_V2_H
+
+#include <Python.h>
+#include <QObject>
+#include <QHash>
+#include <QSet>
+#include <QSharedPointer>
+#include <QLinkedList>
+#include <QByteArray>
+
+#include "dynamicqmetaobject.h"
+
+namespace PySide
+{
+
+class DynamicSlotDataV2;
+class GlobalReceiverV2;
+
+typedef QSharedPointer< QMap<QByteArray, GlobalReceiverV2*> > SharedMap;
+
+/**
+ * A class used to make the link between the C++ Signal/Slot and Python callback
+ * This class is used internally by SignalManager
+ **/
+
+class GlobalReceiverV2 : public QObject
+{
+public:
+ /**
+ * Create a GlobalReceiver object that will call 'callback' argumentent
+ *
+ * @param callback A Python callable object (can be a method or not)
+ * @param ma A SharedPointer used on Signal manager that contains all instaces of GlobalReceiver
+ **/
+ GlobalReceiverV2(PyObject *callback, SharedMap map);
+
+ /**
+ * Destructor
+ **/
+ ~GlobalReceiverV2();
+
+ /**
+ * Reimplemented function from QObject
+ **/
+ int qt_metacall(QMetaObject::Call call, int id, void** args);
+ const QMetaObject* metaObject() const;
+
+ /**
+ * Add a extra slot to this object
+ *
+ * @param signature The signature of the slot to be added
+ * @return The index of this slot on metaobject
+ **/
+ int addSlot(const char* signature);
+
+ /**
+ * Notify to GlobalReceiver about when a new connection was made
+ **/
+ void notify();
+
+ /**
+ * Used to increment the reference of the GlobalReceiver object
+ *
+ * @param link This is a optional paramenter used to link the ref to some QObject life
+ **/
+ void incRef(const QObject* link = 0);
+
+ /**
+ * Used to decrement the reference of the GlobalReceiver object
+ *
+ * @param link This is a optional paramenter used to dismiss the link ref to some QObject
+ **/
+ void decRef(const QObject* link = 0);
+
+ /*
+ * Return the count of refs which the GlobalReceiver has
+ *
+ * @param link If any QObject was passed, the function return the number of references relative to this 'link' object
+ * @return The number of references
+ **/
+ int refCount(const QObject* link) const;
+
+ /**
+ * Use to retrive the unique hash of this GlobalReceiver object
+ *
+ * @return a string with a unique id based on GlobalReceiver contents
+ **/
+ QByteArray hash() const;
+
+ /**
+ * Use to retrive the unique hash of the PyObject based on GlobalReceiver rules
+ *
+ * @param callback The Python callable object used to calculate the id
+ * @return a string with a unique id based on GlobalReceiver contents
+ **/
+ static QByteArray hash(PyObject* callback);
+
+private:
+ DynamicQMetaObject m_metaObject;
+ DynamicSlotDataV2 *m_data;
+ QList<const QObject*> m_refs;
+ int m_ref;
+ SharedMap m_sharedMap;
+};
+
+}
+
+#endif
diff --git a/libpyside/signalmanager.cpp b/libpyside/signalmanager.cpp
index b7b9b3d93..c0dffd431 100644
--- a/libpyside/signalmanager.cpp
+++ b/libpyside/signalmanager.cpp
@@ -43,6 +43,7 @@
#endif
#define PYSIDE_SLOT '1'
#define PYSIDE_SIGNAL '2'
+#include "globalreceiverv2.h"
#include "globalreceiver.h"
#define PYTHON_TYPE "PyObject"
@@ -177,7 +178,26 @@ using namespace PySide;
struct SignalManager::SignalManagerPrivate
{
+ SharedMap m_globalReceivers;
+
+ //Deprecated
GlobalReceiver m_globalReceiver;
+
+ SignalManagerPrivate()
+ {
+ m_globalReceivers = SharedMap( new QMap<QByteArray, GlobalReceiverV2*>() );
+ }
+
+ ~SignalManagerPrivate()
+ {
+ if (!m_globalReceivers.isNull()) {
+ QList<GlobalReceiverV2*> values = m_globalReceivers->values();
+ m_globalReceivers->clear();
+ if (values.size()) {
+ qDeleteAll(values);
+ }
+ }
+ }
};
static void clearSignalManager()
@@ -245,6 +265,50 @@ int SignalManager::addGlobalSlotGetIndex(const char* slot, PyObject* callback)
return m_d->m_globalReceiver.addSlot(slot, callback);
}
+QObject* SignalManager::globalReceiver(QObject *sender, PyObject *callback)
+{
+ SharedMap globalReceivers = m_d->m_globalReceivers;
+ QByteArray hash = GlobalReceiverV2::hash(callback);
+ GlobalReceiverV2* gr = 0;
+ if (!globalReceivers->contains(hash) && sender) {
+ gr = (*globalReceivers)[hash] = new GlobalReceiverV2(callback, globalReceivers);
+ gr->incRef(sender); // create a link reference
+ gr->decRef(); // remove extra reference
+ } else {
+ gr = (*globalReceivers)[hash];
+ if (sender)
+ gr->incRef(sender);
+ }
+
+ return reinterpret_cast<QObject*>(gr);
+}
+
+int SignalManager::countConnectionsWith(const QObject *object)
+{
+ int count = 0;
+ foreach(GlobalReceiverV2* g, m_d->m_globalReceivers->values()) {
+ if (g->refCount(object))
+ count++;
+ }
+ return count;
+}
+
+void SignalManager::notifyGlobalReceiver(QObject* receiver)
+{
+ reinterpret_cast<GlobalReceiverV2*>(receiver)->notify();
+}
+
+void SignalManager::releaseGlobalReceiver(const QObject* source, QObject* receiver)
+{
+ GlobalReceiverV2* gr = reinterpret_cast<GlobalReceiverV2*>(receiver);
+ gr->decRef(source);
+}
+
+int SignalManager::globalReceiverSlotIndex(QObject* receiver, const char* signature) const
+{
+ return reinterpret_cast<GlobalReceiverV2*>(receiver)->addSlot(signature);
+}
+
static bool emitShortCircuitSignal(QObject* source, int signalIndex, PyObject* args)
{
void* signalArgs[2] = {0, args};
@@ -480,6 +544,7 @@ bool SignalManager::hasConnectionWith(const QObject *object)
return m_d->m_globalReceiver.hasConnectionWith(object);
}
+
const QMetaObject* SignalManager::retriveMetaObject(PyObject *self)
{
Shiboken::GilState gil;
@@ -498,3 +563,4 @@ const QMetaObject* SignalManager::retriveMetaObject(PyObject *self)
return mo;
}
+
diff --git a/libpyside/signalmanager.h b/libpyside/signalmanager.h
index b1b798600..1c8ecfc2e 100644
--- a/libpyside/signalmanager.h
+++ b/libpyside/signalmanager.h
@@ -57,15 +57,14 @@ class PYSIDE_API SignalManager
{
public:
static SignalManager& instance();
- QObject* globalReceiver();
- bool emitSignal(QObject* source, const char* signal, PyObject* args);
- static int qt_metacall(QObject* object, QMetaObject::Call call, int id, void** args);
- void addGlobalSlot(const char* slot, PyObject* callback);
- int addGlobalSlotGetIndex(const char* slot, PyObject* callback);
+ QObject* globalReceiver(QObject* sender, PyObject* callback);
+ void releaseGlobalReceiver(const QObject* sender, QObject* receiver);
+ int globalReceiverSlotIndex(QObject* sender, const char* slotSignature) const;
+ void notifyGlobalReceiver(QObject* receiver);
- void globalReceiverConnectNotify(QObject *sender, int slotIndex);
- void globalReceiverDisconnectNotify(QObject *sender, int slotIndex);
+ bool emitSignal(QObject* source, const char* signal, PyObject* args);
+ static int qt_metacall(QObject* object, QMetaObject::Call call, int id, void** args);
// Used to register a new signal/slot on QMetaobject of source.
static bool registerMetaMethod(QObject* source, const char* signature, QMetaMethod::MethodType type);
@@ -75,10 +74,20 @@ public:
static const QMetaObject* retriveMetaObject(PyObject* self);
// Used to discovery if SignalManager was connected with object "destroyed()" signal.
- bool hasConnectionWith(const QObject *object);
+ int countConnectionsWith(const QObject *object);
// Disconnect all signals managed by Globalreceiver
void clear();
+
+
+ PYSIDE_DEPRECATED(QObject* globalReceiver());
+ PYSIDE_DEPRECATED(void addGlobalSlot(const char* slot, PyObject* callback));
+ PYSIDE_DEPRECATED(int addGlobalSlotGetIndex(const char* slot, PyObject* callback));
+
+ PYSIDE_DEPRECATED(void globalReceiverConnectNotify(QObject *sender, int slotIndex));
+ PYSIDE_DEPRECATED(void globalReceiverDisconnectNotify(QObject *sender, int slotIndex));
+ PYSIDE_DEPRECATED(bool hasConnectionWith(const QObject *object));
+
private:
struct SignalManagerPrivate;
SignalManagerPrivate* m_d;