diff options
author | Renato Filho <renato.filho@openbossa.org> | 2011-08-18 17:51:13 -0300 |
---|---|---|
committer | Renato Filho <renato.filho@openbossa.org> | 2011-08-19 14:28:13 -0300 |
commit | ad3a8ed9ce6bdba3c819df0a58b84deea2be6879 (patch) | |
tree | 75935d871c073d0e50439fdfcb25d700eccf4a31 /libpyside | |
parent | cccee9c2c9bb2bc8c1ae93c6c43a67895f537af0 (diff) | |
download | pyside-ad3a8ed9ce6bdba3c819df0a58b84deea2be6879.tar.gz pyside-ad3a8ed9ce6bdba3c819df0a58b84deea2be6879.tar.xz pyside-ad3a8ed9ce6bdba3c819df0a58b84deea2be6879.zip |
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.txt | 1 | ||||
-rw-r--r-- | libpyside/globalreceiver.cpp | 1 | ||||
-rw-r--r-- | libpyside/globalreceiver.h | 1 | ||||
-rw-r--r-- | libpyside/globalreceiverv2.cpp | 308 | ||||
-rw-r--r-- | libpyside/globalreceiverv2.h | 131 | ||||
-rw-r--r-- | libpyside/signalmanager.cpp | 66 | ||||
-rw-r--r-- | libpyside/signalmanager.h | 25 |
7 files changed, 525 insertions, 8 deletions
diff --git a/libpyside/CMakeLists.txt b/libpyside/CMakeLists.txt index e05a8cd..2192945 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 435e22a..17f0090 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 76c1246..4eb2b9a 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 0000000..c31a9d2 --- /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 0000000..28f4703 --- /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 b7b9b3d..c0dffd4 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 b1b7986..1c8ecfc 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; |