diff options
author | Hugo Lima <hugo.lima@openbossa.org> | 2009-12-23 16:50:19 -0200 |
---|---|---|
committer | Hugo Lima <hugo.lima@openbossa.org> | 2009-12-30 14:35:45 -0200 |
commit | 73b5357057b7ca2a72806313877860488cce0854 (patch) | |
tree | b5f91861de7f4ad420c93629799c0c74dcfcae34 | |
parent | 750a56af8ac182147c63d7a29ec3aa987d0e0ed7 (diff) |
Initial implementation of the new architeture for signal/slots.
Reviewed by Renato Araújo <renato.filho@openbossa.org>
-rw-r--r-- | PySide/QtCore/glue/qobject_connect.cpp | 80 | ||||
-rw-r--r-- | PySide/QtCore/typesystem_core.xml | 27 | ||||
-rw-r--r-- | libpyside/CMakeLists.txt | 5 | ||||
-rw-r--r-- | libpyside/abstractqobjectconnection.cpp | 58 | ||||
-rw-r--r-- | libpyside/abstractqobjectconnection.h | 91 | ||||
-rw-r--r-- | libpyside/dynamicqmetaobject.cpp | 62 | ||||
-rw-r--r-- | libpyside/dynamicqmetaobject.h | 14 | ||||
-rw-r--r-- | libpyside/globalreceiver.cpp (renamed from libpyside/signalslotconnection.cpp) | 79 | ||||
-rw-r--r-- | libpyside/globalreceiver.h (renamed from libpyside/signalslotconnection.h) | 28 | ||||
-rw-r--r-- | libpyside/proxyslot.h | 88 | ||||
-rw-r--r-- | libpyside/signalmanager.cpp | 160 | ||||
-rw-r--r-- | libpyside/signalmanager.h | 11 | ||||
-rw-r--r-- | libpyside/signalsignalconnection.cpp | 51 | ||||
-rw-r--r-- | libpyside/signalsignalconnection.h | 54 | ||||
-rw-r--r-- | libpyside/typeresolver.cpp | 6 | ||||
-rw-r--r-- | libpyside/typeresolver.h | 1 |
16 files changed, 290 insertions, 525 deletions
diff --git a/PySide/QtCore/glue/qobject_connect.cpp b/PySide/QtCore/glue/qobject_connect.cpp new file mode 100644 index 000000000..5fe0ac427 --- /dev/null +++ b/PySide/QtCore/glue/qobject_connect.cpp @@ -0,0 +1,80 @@ +static int qobjectGetMethodIndex(QObject* source, const char* signature, QMetaMethod::MethodType type) +{ + const QMetaObject* metaObject = source->metaObject(); + int methodIndex = metaObject->indexOfMethod(signature); + // Create the dynamic signal is needed + if (methodIndex == -1) { + SbkBaseWrapper* self = (SbkBaseWrapper*) BindingManager::instance().retrieveWrapper(source); + if (!self->containsCppWrapper) { + qWarning() << "You can't add dynamic signals or slots on an object originated from C++."; + return false; + } + PySide::DynamicQMetaObject* dynMetaObj = reinterpret_cast<PySide::DynamicQMetaObject*>(const_cast<QMetaObject*>(metaObject)); + if (type == QMetaMethod::Signal) + dynMetaObj->addSignal(signature); + else + dynMetaObj->addSlot(signature); + methodIndex = metaObject->indexOfMethod(signature); + } + return methodIndex; +} + +static bool qobjectConnect(QObject* source, const char* signal, QObject* receiver, const char* slot, Qt::ConnectionType type) +{ + if (!PySide::checkSignal(signal)) + return false; + signal++; + + const QMetaObject* metaObject = source->metaObject(); + int signalIndex = qobjectGetMethodIndex(source, signal, QMetaMethod::Signal); + + bool isSignal = PySide::isSignal(slot); + slot++; + metaObject = receiver->metaObject(); + int slotIndex = qobjectGetMethodIndex(receiver, slot, isSignal ? QMetaMethod::Signal : QMetaMethod::Slot); + return QMetaObject::connect(source, signalIndex, receiver, slotIndex, type); +} + +static bool qobjectConnectCallback(QObject* source, const char* signal, PyObject* callback, Qt::ConnectionType type) +{ + if (!PySide::checkSignal(signal)) + return false; + signal++; + + const QMetaObject* metaObject = source->metaObject(); + int signalIndex = qobjectGetMethodIndex(source, signal, QMetaMethod::Signal); + + 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(); + + metaObject = receiver->metaObject(); + const QByteArray callbackSig = PySide::getCallbackSignature(signal, callback).toAscii(); + const char* slot = callbackSig.constData(); + int slotIndex = metaObject->indexOfSlot(slot); + if (slotIndex == -1) { + if (!usingGlobalReceiver and !((SbkBaseWrapper*)self)->containsCppWrapper) { + qWarning() << "You cant add dynamic slots on an object originated from C++."; + return false; + } + if (usingGlobalReceiver) { + signalManager.addGlobalSlot(slot, callback); + } else { + PySide::DynamicQMetaObject* dynMetaObj = reinterpret_cast<PySide::DynamicQMetaObject*>(const_cast<QMetaObject*>(metaObject)); + dynMetaObj->addSlot(slot); + } + slotIndex = metaObject->indexOfSlot(slot); + } + return QMetaObject::connect(source, signalIndex, receiver, slotIndex, type); +} diff --git a/PySide/QtCore/typesystem_core.xml b/PySide/QtCore/typesystem_core.xml index 21562fc66..2bc00110a 100644 --- a/PySide/QtCore/typesystem_core.xml +++ b/PySide/QtCore/typesystem_core.xml @@ -1293,16 +1293,19 @@ <modify-function signature="thread() const" remove="all"/> <modify-function signature="connect(const QObject*, const char*, const char *, Qt::ConnectionType) const" remove="all"/> <modify-function signature="connect(const QObject*, const char*, const QObject*, const char *, Qt::ConnectionType)"> - <inject-code class="target" position="beginning"> - %PYARG_0 = %CONVERTTOPYTHON[bool](PySide::SignalManager::instance().connect(%1, %2, %3, %4, %5)); + <inject-code class="target" position="beginning" file=""> + // %FUNCTION_NAME() - disable generation of function call. + %PYARG_0 = %CONVERTTOPYTHON[bool](qobjectConnect(%1, %2, %3, %4, %5)); </inject-code> </modify-function> + <inject-code class="native" position="beginning" file="glue/qobject_connect.cpp" /> <add-function signature="connect(const QObject*, const char*, PyCallable*, Qt::ConnectionType)" return-type="bool"> <modify-argument index="4"> <replace-default-expression with="Qt::AutoConnection" /> </modify-argument> <inject-code class="target" position="beginning"> - %PYARG_0 = %CONVERTTOPYTHON[bool](PySide::SignalManager::instance().connect(%1, %2, %PYARG_3, %4)); + // %FUNCTION_NAME() - disable generation of function call. + %PYARG_0 = %CONVERTTOPYTHON[bool](qobjectConnectCallback(%1, %2, %PYARG_3, %4)); </inject-code> </add-function> <!-- static version --> @@ -1311,7 +1314,8 @@ <replace-default-expression with="Qt::AutoConnection" /> </modify-argument> <inject-code class="target" position="beginning"> - %PYARG_0 = %CONVERTTOPYTHON[bool](PySide::SignalManager::instance().connect(%CPPSELF, %1, %PYARG_2, %3)); + // %FUNCTION_NAME() - disable generation of function call. + %PYARG_0 = %CONVERTTOPYTHON[bool](qobjectConnectCallback(%CPPSELF, %1, %PYARG_2, %3)); </inject-code> </add-function> <add-function signature="connect(const char*, const QObject*, const char *, Qt::ConnectionType)" return-type="bool"> @@ -1319,7 +1323,8 @@ <replace-default-expression with="Qt::AutoConnection" /> </modify-argument> <inject-code class="target" position="beginning"> - %PYARG_0 = %CONVERTTOPYTHON[bool](PySide::SignalManager::instance().connect(%CPPSELF, %1, %2, %3, %4)); + // %FUNCTION_NAME() - disable generation of function call. + %PYARG_0 = %CONVERTTOPYTHON[bool](qobjectConnect(%CPPSELF, %1, %2, %3, %4)); </inject-code> </add-function> <add-function signature="emit(const char*, ...)" return-type="bool"> @@ -1327,12 +1332,12 @@ %PYARG_0 = %CONVERTTOPYTHON[bool](PySide::SignalManager::instance().emitSignal(%CPPSELF, %1, %PYARG_2)); </inject-code> </add-function> - <modify-function signature="disconnect(const QObject*, const char*)" remove="all"/> - <modify-function signature="disconnect(const char*, const QObject*, const char*)" remove="all"/> - <modify-function signature="disconnect(const QObject*, const char*, const QObject*, const char *)" remove="all"/> +<!-- <modify-function signature="disconnect(const QObject*, const char*)" remove="all"/> --> +<!-- <modify-function signature="disconnect(const char*, const QObject*, const char*)" remove="all"/> --> +<!-- <modify-function signature="disconnect(const QObject*, const char*, const QObject*, const char *)" remove="all"/> --> <inject-code class="native" position="beginning"> static bool - PyObject_inherits_internal(PyTypeObject *objType, const char *class_name) + qobjectInheritsInternal(PyTypeObject *objType, const char *class_name) { if (strcmp(objType->tp_name, class_name) == 0) return true; @@ -1340,12 +1345,12 @@ PyTypeObject* base = (objType)->tp_base; if (base == 0) return false; - return PyObject_inherits_internal(base, class_name); + return qobjectInheritsInternal(base, class_name); } </inject-code> <modify-function signature="inherits(const char*) const"> <inject-code class="target" position="beginning"> - bool retval = PyObject_inherits_internal(self->ob_type, %1) ? true : %CPPSELF.%FUNCTION_NAME(%1); + bool retval = qobjectInheritsInternal(self->ob_type, %1) ? true : %CPPSELF.%FUNCTION_NAME(%1); %PYARG_0 = %CONVERTTOPYTHON[bool](retval); </inject-code> </modify-function> diff --git a/libpyside/CMakeLists.txt b/libpyside/CMakeLists.txt index 2c2aa4d68..4a5049554 100644 --- a/libpyside/CMakeLists.txt +++ b/libpyside/CMakeLists.txt @@ -1,12 +1,9 @@ project(libpyside) set(libpyside_SRC -abstractqobjectconnection.cpp dynamicqmetaobject.cpp -proxyslot.cpp signalmanager.cpp -signalsignalconnection.cpp -signalslotconnection.cpp +globalreceiver.cpp typeresolver.cpp ) diff --git a/libpyside/abstractqobjectconnection.cpp b/libpyside/abstractqobjectconnection.cpp deleted file mode 100644 index 1ecd5ae58..000000000 --- a/libpyside/abstractqobjectconnection.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* -* This file is part of the Shiboken Python Bindings Generator project. -* -* Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -* -* Contact: PySide team <contact@pyside.org> -* -* This program is free software; you can redistribute it and/or -* modify it under the terms of the GNU Lesser General Public License -* version 2.1 as published by the Free Software Foundation. Please -* review the following information to ensure the GNU Lesser General -* Public License version 2.1 requirements will be met: -* http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -* -* As a special exception to the GNU Lesser General Public License -* version 2.1, the object code form of a "work that uses the Library" -* may incorporate material from a header file that is part of the -* Library. You may distribute such object code under terms of your -* choice, provided that the incorporated material (i) does not exceed -* more than 5% of the total size of the Library; and (ii) is limited to -* numerical parameters, data structure layouts, accessors, macros, -* inline functions and templates. -* -* This program 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 program; if not, write to the Free Software -* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -* 02110-1301 USA -*/ - -#include "abstractqobjectconnection.h" -#include "typeresolver.h" -#include "signalmanager.h" -#include <QDebug> - -using namespace PySide; - -AbstractQObjectConnection::AbstractQObjectConnection(QObject* source, const char* signal, Qt::ConnectionType connectionType) - : m_source(source), m_connectionType(connectionType), m_slotIndex(-1) -{ - m_signalArgs = getArgsFromSignature(signal); - m_signalIndex = source->metaObject()->indexOfSignal(signal); -} - -void AbstractQObjectConnection::trigger(void** args) -{ - int numArgs = m_signalArgs.count(); - PyObject* pyArgs = PyTuple_New(numArgs); - for (int i = 0; i < numArgs; ++i) { - PyObject* arg = TypeResolver::get(m_signalArgs[i])->toPython(args[i+1]); - PyTuple_SET_ITEM(pyArgs, i, arg); - } - trigger(pyArgs); -} diff --git a/libpyside/abstractqobjectconnection.h b/libpyside/abstractqobjectconnection.h deleted file mode 100644 index 5ba43194c..000000000 --- a/libpyside/abstractqobjectconnection.h +++ /dev/null @@ -1,91 +0,0 @@ -/* -* This file is part of the Shiboken Python Bindings Generator project. -* -* Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -* -* Contact: PySide team <contact@pyside.org> -* -* This program is free software; you can redistribute it and/or -* modify it under the terms of the GNU Lesser General Public License -* version 2.1 as published by the Free Software Foundation. Please -* review the following information to ensure the GNU Lesser General -* Public License version 2.1 requirements will be met: -* http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -* -* As a special exception to the GNU Lesser General Public License -* version 2.1, the object code form of a "work that uses the Library" -* may incorporate material from a header file that is part of the -* Library. You may distribute such object code under terms of your -* choice, provided that the incorporated material (i) does not exceed -* more than 5% of the total size of the Library; and (ii) is limited to -* numerical parameters, data structure layouts, accessors, macros, -* inline functions and templates. -* -* This program 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 program; if not, write to the Free Software -* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -* 02110-1301 USA -*/ - -#ifndef ABSTRACTQOBJECTCONNECTION_H -#define ABSTRACTQOBJECTCONNECTION_H -#include <Python.h> -#include <Qt> -#include <QStringList> - -class QObject; - -namespace PySide -{ -class AbstractQObjectConnection -{ -public: - AbstractQObjectConnection(QObject* source, const char* signal, Qt::ConnectionType connectionType); - virtual ~AbstractQObjectConnection() {} - - QObject* source() const - { - return m_source; - } - void setSignalIndex(int signalIndex) - { - m_signalIndex = signalIndex; - } - - int signalIndex() const - { - return m_signalIndex; - } - - void setSlotIndex(int slotIndex) - { - m_slotIndex = slotIndex; - } - - int slotIndex() const - { - return m_slotIndex; - } - - Qt::ConnectionType type() const - { - return m_connectionType; - } - - void trigger(void** args); - virtual void trigger(PyObject* args) = 0; -private: - QObject* m_source; - QStringList m_signalArgs; - Qt::ConnectionType m_connectionType; - int m_signalIndex; - int m_slotIndex; -}; -} - -#endif diff --git a/libpyside/dynamicqmetaobject.cpp b/libpyside/dynamicqmetaobject.cpp index 47baf6c82..4cf249434 100644 --- a/libpyside/dynamicqmetaobject.cpp +++ b/libpyside/dynamicqmetaobject.cpp @@ -38,6 +38,7 @@ #include <QList> #include <QObject> #include <cstring> +#include <QDebug> using namespace PySide; @@ -54,21 +55,19 @@ static int registerString(const QByteArray& s, QList<QByteArray>* strings) return idx; } -DynamicQMetaObject::DynamicQMetaObject(const QObject* object) : m_originalMetaObject(object->metaObject()) +DynamicQMetaObject::DynamicQMetaObject(const QMetaObject* metaObject) { - m_metaObject.d.superdata = m_originalMetaObject; - m_metaObject.d.stringdata = 0; - m_metaObject.d.data = 0; - m_metaObject.d.extradata = 0; - m_stringData = 0; - m_data = 0; + d.superdata = metaObject; + d.stringdata = 0; + d.data = 0; + d.extradata = 0; updateMetaObject(); } DynamicQMetaObject::~DynamicQMetaObject() { - delete[] m_stringData; - delete[] m_data; + delete[] d.stringdata; + delete[] d.data; } void DynamicQMetaObject::addSignal(const char* signal) @@ -79,7 +78,7 @@ void DynamicQMetaObject::addSignal(const char* signal) void DynamicQMetaObject::addSlot(const char* slot) { - m_signals << QByteArray(slot); + m_slots << QByteArray(slot); updateMetaObject(); } @@ -112,32 +111,33 @@ void DynamicQMetaObject::updateMetaObject() const int HEADER_LENGHT = sizeof(header)/sizeof(int); header[5] = HEADER_LENGHT; // header size + 5 indexes per method + an ending zero - delete[] m_data; - m_data = new uint[HEADER_LENGHT + n_methods*5 + 1]; - std::memcpy(m_data, header, sizeof(header)); + delete[] d.data; + unsigned int* data; + data = new unsigned int[HEADER_LENGHT + n_methods*5 + 1]; + std::memcpy(data, header, sizeof(header)); QList<QByteArray> strings; - registerString(m_originalMetaObject->className(), &strings); // register class string + registerString(d.superdata->className(), &strings); // register class string const int NULL_INDEX = registerString("", &strings); // register a null string int index = HEADER_LENGHT; //write signals foreach(QByteArray signal, m_signals) { - m_data[index++] = registerString(signal, &strings); // func name - m_data[index++] = NULL_INDEX; // arguments - m_data[index++] = NULL_INDEX; // normalized type - m_data[index++] = NULL_INDEX; // tags - m_data[index++] = AccessPublic | MethodSignal; // flags + data[index++] = registerString(signal, &strings); // func name + data[index++] = NULL_INDEX; // arguments + data[index++] = NULL_INDEX; // normalized type + data[index++] = NULL_INDEX; // tags + data[index++] = AccessPublic | MethodSignal; // flags } //write slots foreach(QByteArray slot, m_slots) { - m_data[index++] = registerString(slot, &strings); // func name - m_data[index++] = NULL_INDEX; // arguments - m_data[index++] = NULL_INDEX; // normalized type - m_data[index++] = NULL_INDEX; // tags - m_data[index++] = AccessPublic | MethodSlot; // flags + data[index++] = registerString(slot, &strings); // func name + data[index++] = NULL_INDEX; // arguments + data[index++] = NULL_INDEX; // normalized type + data[index++] = NULL_INDEX; // tags + data[index++] = AccessPublic | MethodSlot; // flags } - m_data[index++] = 0; // the end + data[index++] = 0; // the end // create the m_metadata string QByteArray str; @@ -145,11 +145,9 @@ void DynamicQMetaObject::updateMetaObject() str.append(signature); str.append(char(0)); } - delete[] m_stringData; - m_stringData = new char[str.count()]; - std::copy(str.begin(), str.end(), m_stringData); - - // create metaobject - m_metaObject.d.stringdata = m_stringData; - m_metaObject.d.data = m_data; + delete[] d.stringdata; + char* stringData = new char[str.count()]; + std::copy(str.begin(), str.end(), stringData); + d.data = data; + d.stringdata = stringData; } diff --git a/libpyside/dynamicqmetaobject.h b/libpyside/dynamicqmetaobject.h index a7fb3b6f2..eca78af5f 100644 --- a/libpyside/dynamicqmetaobject.h +++ b/libpyside/dynamicqmetaobject.h @@ -35,6 +35,7 @@ #ifndef DYNAMICQMETAOBJECT_H #define DYNAMICQMETAOBJECT_H +#include "pysidemacros.h" #include <QMetaObject> #include <QLinkedList> #include <QByteArray> @@ -44,26 +45,17 @@ class QObject; namespace PySide { -class DynamicQMetaObject +class PYSIDE_API DynamicQMetaObject : public QMetaObject { public: - DynamicQMetaObject(const QObject* object); + DynamicQMetaObject(const QMetaObject* metaObject); ~DynamicQMetaObject(); void addSignal(const char* signal); void addSlot(const char* slot); - const QMetaObject* metaObject() const - { - return &m_metaObject; - } private: - const QMetaObject* m_originalMetaObject; - QMetaObject m_metaObject; - QLinkedList<QByteArray> m_signals; QLinkedList<QByteArray> m_slots; - unsigned int* m_data; - char* m_stringData; void updateMetaObject(); }; diff --git a/libpyside/signalslotconnection.cpp b/libpyside/globalreceiver.cpp index 125396345..880c2503d 100644 --- a/libpyside/signalslotconnection.cpp +++ b/libpyside/globalreceiver.cpp @@ -32,53 +32,68 @@ * 02110-1301 USA */ -#include "signalslotconnection.h" +#include "globalreceiver.h" +#include <QMetaMethod> #include <QDebug> +#include "signalmanager.h" +#include <autodecref.h> +#include "typeresolver.h" using namespace PySide; -SignalSlotConnection::SignalSlotConnection(QObject* source, const char* signal, PyObject* callback, Qt::ConnectionType connectionType) - : AbstractQObjectConnection(source, signal, connectionType), m_receiver(0) +GlobalReceiver::GlobalReceiver() : m_metaObject(&QObject::staticMetaObject) { - if (PyMethod_Check(callback)) { - m_function = PyMethod_GET_FUNCTION(callback); - if (PyObject* self = PyMethod_GET_SELF(callback)) - m_receiver = self; - } else { - m_function = callback; - } - Py_INCREF(m_function); - PyCodeObject* objCode = reinterpret_cast<PyCodeObject*>(PyFunction_GET_CODE(m_function)); - m_numSlotArgs = objCode->co_flags & CO_VARARGS ? -1 : objCode->co_argcount; } -SignalSlotConnection::~SignalSlotConnection() +GlobalReceiver::~GlobalReceiver() +{ + foreach(PyObject* obj, m_slotReceivers) + Py_DECREF(obj); +} + +const QMetaObject* GlobalReceiver::metaObject() const +{ + return &m_metaObject; +} + +void GlobalReceiver::addSlot(const char* slot, PyObject* callback) { - Py_DECREF(m_function); + m_metaObject.addSlot(slot); + int slotId = m_metaObject.indexOfSlot(slot); + Py_INCREF(callback); + m_slotReceivers[slotId] = callback; + + Q_ASSERT(slotId >= QObject::staticMetaObject.methodCount()); } -void SignalSlotConnection::trigger(PyObject* args) +void GlobalReceiver::removeSlot(int slotId) { - Q_ASSERT(PySequence_Size(args) >= m_numSlotArgs); + PyObject* obj = m_slotReceivers.take(slotId); + Py_XDECREF(obj); +} - const int useSelf = m_receiver ? 1 : 0; - int numSlotArgs = m_numSlotArgs; +int GlobalReceiver::qt_metacall(QMetaObject::Call call, int id, void** args) +{ + Q_ASSERT(call == QMetaObject::InvokeMetaMethod); + Q_ASSERT(id >= QObject::staticMetaObject.methodCount()); + QMetaMethod slot = m_metaObject.method(id); + Q_ASSERT(slot.methodType() == QMetaMethod::Slot); + QList<QByteArray> paramTypes = slot.parameterTypes(); - if (numSlotArgs == -1) - numSlotArgs = PySequence_Size(args) + useSelf; + PyObject* callback = m_slotReceivers.value(id); + if (!callback) { + qWarning("Unknown global slot."); + return -1; + } - PyObject* preparedArgs = PyTuple_New(numSlotArgs); - if (m_receiver) - PyTuple_SetItem(preparedArgs, 0, m_receiver); - for (int i = 0, max = numSlotArgs - useSelf; i < max; ++i) { - PyTuple_SET_ITEM(preparedArgs, i + useSelf, PyTuple_GET_ITEM(args, i)); + Shiboken::AutoDecRef preparedArgs(PyTuple_New(paramTypes.count())); + for (int i = 0, max = paramTypes.count(); i < max; ++i) { + PyObject* arg = TypeResolver::get(paramTypes[i].constData())->toPython(args[i+1]); + PyTuple_SET_ITEM(preparedArgs.object(), i, arg); } - PyObject* retval = PyObject_CallObject(m_function, preparedArgs); - if (retval) { - Py_DECREF(retval); - } else { + Shiboken::AutoDecRef retval(PyObject_CallObject(callback, preparedArgs)); + if (!retval) qWarning("Error calling slot"); - } - Py_DECREF(preparedArgs); + return -1; } diff --git a/libpyside/signalslotconnection.h b/libpyside/globalreceiver.h index 4c9015c98..32fc71539 100644 --- a/libpyside/signalslotconnection.h +++ b/libpyside/globalreceiver.h @@ -32,27 +32,31 @@ * 02110-1301 USA */ -#ifndef SIGNALSLOTCONNECTION_H -#define SIGNALSLOTCONNECTION_H +#ifndef GLOBALRECEIVER_H +#define GLOBALRECEIVER_H -#include "abstractqobjectconnection.h" - -class QObject; +#include <Python.h> +#include <QObject> +#include <QHash> +#include "dynamicqmetaobject.h" namespace PySide { -class SignalSlotConnection : public AbstractQObjectConnection +class GlobalReceiver : public QObject { public: - SignalSlotConnection(QObject* source, const char* signal, PyObject* callback, Qt::ConnectionType connectionType); - ~SignalSlotConnection(); - void trigger(PyObject* args); + GlobalReceiver(); + ~GlobalReceiver(); + int qt_metacall(QMetaObject::Call call, int id, void** args); + const QMetaObject* metaObject() const; + void addSlot(const char* slot, PyObject* callback); + void removeSlot(int slotId); private: - PyObject* m_receiver; - PyObject* m_function; - int m_numSlotArgs; + DynamicQMetaObject m_metaObject; + QHash<int, PyObject* > m_slotReceivers; }; } + #endif diff --git a/libpyside/proxyslot.h b/libpyside/proxyslot.h deleted file mode 100644 index c2334f799..000000000 --- a/libpyside/proxyslot.h +++ /dev/null @@ -1,88 +0,0 @@ -/* -* This file is part of the Shiboken Python Bindings Generator project. -* -* Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -* -* Contact: PySide team <contact@pyside.org> -* -* This program is free software; you can redistribute it and/or -* modify it under the terms of the GNU Lesser General Public License -* version 2.1 as published by the Free Software Foundation. Please -* review the following information to ensure the GNU Lesser General -* Public License version 2.1 requirements will be met: -* http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -* -* As a special exception to the GNU Lesser General Public License -* version 2.1, the object code form of a "work that uses the Library" -* may incorporate material from a header file that is part of the -* Library. You may distribute such object code under terms of your -* choice, provided that the incorporated material (i) does not exceed -* more than 5% of the total size of the Library; and (ii) is limited to -* numerical parameters, data structure layouts, accessors, macros, -* inline functions and templates. -* -* This program 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 program; if not, write to the Free Software -* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -* 02110-1301 USA -*/ - -#include <QObject> -#include <QHash> -#include "dynamicqmetaobject.h" - -namespace PySide -{ - -class AbstractQObjectConnection; - -class ProxySlot : public QObject -{ -public: - ProxySlot(const QObject* signalSource); - bool connect(AbstractQObjectConnection* connection); - DynamicQMetaObject* dynamicQMetaObject() - { - return &m_metaObject; - } - -protected: - /** - * Qt's meta-object system uses the qt_metacall() function to access the - * meta-information for a particular QObject object (its signals, slots, - * properties, etc.). - * - * About the implemmentation - * - * The call may indicate access to the meta-object of the QObject base - * class, we need to take care of this use case. - * If the QObject::qt_metacall() call returns -1, this means that the - * metacall has been handled by QObject and that there is nothing to do. - * In that case, we return immediately. Similarly, if the metacall isn't - * a slot invocation, we follow QObject's convention and return an - * identifier that can be handled by a subclass. - * - * If all goes well, we invoke the specified slot and return -1 to - * indicate that the metacall has been processed. - * - * \see \link http://doc.trolltech.com/qq/qq16-dynamicqobject.html - */ - int qt_metacall(QMetaObject::Call call, int id, void **args); - -private: - DynamicQMetaObject m_metaObject; - const QObject* m_signalSource; - int m_nextSlotIndex; - // slot_index => connection_info, used by qt_metacall - // to recover the connection_info - QHash<int, AbstractQObjectConnection*> m_connections; - - void attachAbstractConnection(AbstractQObjectConnection* connection); -}; - -}
\ No newline at end of file diff --git a/libpyside/signalmanager.cpp b/libpyside/signalmanager.cpp index 464e49410..a222e0371 100644 --- a/libpyside/signalmanager.cpp +++ b/libpyside/signalmanager.cpp @@ -33,10 +33,13 @@ */ #include "signalmanager.h" -#include "proxyslot.h" + #include <QHash> #include <QStringList> +#include <QMetaMethod> +#include <autodecref.h> #include <QDebug> +#include <limits> #if QSLOT_CODE != 1 || QSIGNAL_CODE != 2 #error QSLOT_CODE and/or QSIGNAL_CODE changed! change the hardcoded stuff to the correct value! @@ -44,12 +47,16 @@ #define PYSIDE_SLOT '1' #define PYSIDE_SIGNAL '2' #include "typeresolver.h" -#include "signalslotconnection.h" -#include "signalsignalconnection.h" +#include "globalreceiver.h" using namespace PySide; -static bool checkSignal(const char* signal) +bool PySide::isSignal(const char* signal) +{ + return signal[0] == PYSIDE_SIGNAL; +} + +bool PySide::checkSignal(const char* signal) { if (signal[0] != PYSIDE_SIGNAL) { PyErr_SetString(PyExc_TypeError, "Use the function PySide.QtCore.SIGNAL on signals"); @@ -58,6 +65,33 @@ static bool checkSignal(const char* signal) return true; } +QString PySide::getCallbackSignature(const char* signal, PyObject* callback) +{ + PyObject* function; + int useSelf = PyMethod_Check(callback); + if (useSelf) { + function = PyMethod_GET_FUNCTION(callback); + } else { + function = callback; + } + + PyCodeObject* objCode = reinterpret_cast<PyCodeObject*>(PyFunction_GET_CODE(function)); + QString signature(PyString_AS_STRING(objCode->co_name)); + signature.append(QString::number(quint64(callback), 16)); + signature.append('('); + int numArgs = objCode->co_flags & CO_VARARGS ? -1 : objCode->co_argcount; + + QStringList args = getArgsFromSignature(signal); + if (numArgs == -1) + numArgs = std::numeric_limits<int>::max(); + while (args.count() > numArgs - useSelf) { + args.removeLast(); + } + signature.append(args.join(",")); + signature.append(')'); + return signature; +} + QStringList PySide::getArgsFromSignature(const char* signature) { QString qsignature(signature); @@ -76,23 +110,7 @@ QStringList PySide::getArgsFromSignature(const char* signature) struct SignalManager::SignalManagerPrivate { - QHash<const QObject*, ProxySlot*> m_proxies; - - ProxySlot* findProxy(const QObject* signalSource) const - { - return m_proxies.value(signalSource); - } - - ProxySlot* getProxy(const QObject* signalSource) - { - ProxySlot* proxy = findProxy(signalSource); - if (!proxy) { - proxy = new ProxySlot(signalSource); - m_proxies[signalSource] = proxy; - QObject::connect(signalSource, SIGNAL(destroyed()), proxy, SLOT(deleteLater())); - } - return proxy; - } + GlobalReceiver m_globalReceiver; }; SignalManager::SignalManager() : m_d(new SignalManagerPrivate) @@ -110,57 +128,14 @@ SignalManager& SignalManager::instance() return me; } -bool SignalManager::connect(QObject* source, const char* signal, PyObject* callback, Qt::ConnectionType type) +QObject* SignalManager::globalReceiver() { - if (!checkSignal(signal)) - return false; - signal++; - - ProxySlot* proxy = m_d->getProxy(source); - if (source->metaObject()->indexOfSignal(signal) == -1) - proxy->dynamicQMetaObject()->addSignal(signal); - AbstractQObjectConnection* connection = new SignalSlotConnection(source, signal, callback, type); - return proxy->connect(connection); + return &m_d->m_globalReceiver; } -bool SignalManager::connect(QObject* source, const char* signal, QObject* receiver, const char* slot, Qt::ConnectionType type) +void SignalManager::addGlobalSlot(const char* slot, PyObject* callback) { - if (!checkSignal(signal)) - return false; - signal++; - - if (!QMetaObject::checkConnectArgs(signal, slot)) - return false; - - // Check if is a dynamic signal - ProxySlot* proxy = m_d->getProxy(source); - int signalIndex = source->metaObject()->indexOfSignal(signal); - if (signalIndex == -1) { - proxy->dynamicQMetaObject()->addSignal(signal); - signalIndex = source->metaObject()->indexOfSignal(signal); - } - - int slotIndex; - bool slotIsSignal = checkSignal(slot); - slot++; - // Signal -> Signal connection - if (slotIsSignal) { - slotIndex = receiver->metaObject()->indexOfSignal(slot); - if (slotIndex == -1) { - ProxySlot* proxy = m_d->getProxy(receiver); - proxy->dynamicQMetaObject()->addSignal(slot); - slotIndex = receiver->metaObject()->indexOfSignal(slot); - } - AbstractQObjectConnection* connection = new SignalSignalConnection(source, signal, receiver, slot, type); - proxy->connect(connection); - } else { - // Signal -> Slot connection - slotIndex = receiver->metaObject()->indexOfSlot(slot); - if (slotIndex == -1) - return false; - } - - return QMetaObject::connect(source, signalIndex, receiver, slotIndex, type); + m_d->m_globalReceiver.addSlot(slot, callback); } bool SignalManager::emitSignal(QObject* source, const char* signal, PyObject* args) @@ -174,25 +149,56 @@ bool SignalManager::emitSignal(QObject* source, const char* signal, PyObject* ar int signalIndex = source->metaObject()->indexOfSignal(signal); if (signalIndex != -1) { QStringList argTypes = getArgsFromSignature(signal); + if (argsGiven > argTypes.count()) { + PyErr_SetString(PyExc_TypeError, "Too many arguments for this signal."); + return false; + } void* signalArgs[argsGiven+1]; signalArgs[0] = 0; for (int i = 0; i < argsGiven; ++i) signalArgs[i+1] = TypeResolver::get(argTypes[i])->toCpp(PySequence_GetItem(args, i)); QMetaObject::activate(source, signalIndex, signalArgs); + // FIXME: This will cause troubles with non-direct connections. + for (int i = 0; i < argsGiven; ++i) + TypeResolver::get(argTypes[i])->deleteObject(signalArgs[i+1]); return true; } return false; } -void PySide::SignalManager::removeProxySlot(const QObject* signalSource) +int PySide::SignalManager::qt_metacall(QObject* object, QMetaObject::Call call, int id, void** args) { - m_d->m_proxies.remove(signalSource); -} + const QMetaObject* metaObject = object->metaObject(); + // only meta method invocation is supported right now. + if (call != QMetaObject::InvokeMetaMethod) { + qWarning("Only meta method invocation is supported right now by PySide."); + return id - metaObject->methodCount(); + } + QMetaMethod method = metaObject->method(id); -const QMetaObject* PySide::SignalManager::getMetaObject(const QObject* object) const -{ - ProxySlot* proxy = m_d->findProxy(object); - if (proxy) - return proxy->dynamicQMetaObject()->metaObject(); - return 0; + if (method.methodType() == QMetaMethod::Signal) { + // emit python signal + QMetaObject::activate(object, id, args); + } else { + // call python slot + QList<QByteArray> paramTypes = method.parameterTypes(); + PyObject* self = Shiboken::BindingManager::instance().retrieveWrapper(object); + Shiboken::AutoDecRef preparedArgs(PyTuple_New(paramTypes.count()+1)); + + PyTuple_SET_ITEM(preparedArgs.object(), 0, self); + for (int i = 0, max = paramTypes.count(); i < max; ++i) { + PyObject* arg = TypeResolver::get(paramTypes[i].constData())->toPython(args[i+1]); + PyTuple_SET_ITEM(preparedArgs.object(), i + 1, arg); + } + + QString methodName = method.signature(); + methodName = methodName.left(methodName.indexOf('(')); + + Shiboken::AutoDecRef pyMethodName(PyString_FromString(qPrintable(methodName))); + Shiboken::AutoDecRef pyMethod(PyObject_GetAttr(self, pyMethodName)); + Shiboken::AutoDecRef retval(PyObject_CallObject(pyMethod, preparedArgs)); + if (!retval) + qWarning("Error calling slot"); + } + return -1; } diff --git a/libpyside/signalmanager.h b/libpyside/signalmanager.h index 4e6fe6d9f..14ade8a6d 100644 --- a/libpyside/signalmanager.h +++ b/libpyside/signalmanager.h @@ -45,17 +45,20 @@ class QObject; namespace PySide { +PYSIDE_API bool isSignal(const char* signal); +PYSIDE_API bool checkSignal(const char* signal); +PYSIDE_API QString getCallbackSignature(const char* signal, PyObject* callback); QStringList getArgsFromSignature(const char* signature); class PYSIDE_API SignalManager { public: static SignalManager& instance(); - bool connect(QObject* source, const char* signal, PyObject* callback, Qt::ConnectionType type = Qt::AutoConnection); - bool connect(QObject* source, const char* signal, QObject* receiver, const char* slot, Qt::ConnectionType type = Qt::AutoConnection); + QObject* globalReceiver(); bool emitSignal(QObject* source, const char* signal, PyObject* args); - void removeProxySlot(const QObject* signalSource); - const QMetaObject* getMetaObject(const QObject* object) const; + + static int qt_metacall(QObject* object, QMetaObject::Call call, int id, void** args); + void addGlobalSlot(const char* slot, PyObject* callback); private: struct SignalManagerPrivate; SignalManagerPrivate* m_d; diff --git a/libpyside/signalsignalconnection.cpp b/libpyside/signalsignalconnection.cpp deleted file mode 100644 index 2a03dfe3b..000000000 --- a/libpyside/signalsignalconnection.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* -* This file is part of the Shiboken Python Bindings Generator project. -* -* Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -* -* Contact: PySide team <contact@pyside.org> -* -* This program is free software; you can redistribute it and/or -* modify it under the terms of the GNU Lesser General Public License -* version 2.1 as published by the Free Software Foundation. Please -* review the following information to ensure the GNU Lesser General -* Public License version 2.1 requirements will be met: -* http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -* -* As a special exception to the GNU Lesser General Public License -* version 2.1, the object code form of a "work that uses the Library" -* may incorporate material from a header file that is part of the -* Library. You may distribute such object code under terms of your -* choice, provided that the incorporated material (i) does not exceed -* more than 5% of the total size of the Library; and (ii) is limited to -* numerical parameters, data structure layouts, accessors, macros, -* inline functions and templates. -* -* This program 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 program; if not, write to the Free Software -* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -* 02110-1301 USA -*/ - -#include "signalsignalconnection.h" -#include <QDebug> -#include "signalmanager.h" - -using namespace PySide; - -SignalSignalConnection::SignalSignalConnection(QObject* source, const char* signal, QObject* receiver, const char* otherSignal, Qt::ConnectionType connectionType) - : AbstractQObjectConnection(source, signal, connectionType), m_receiver(receiver), m_signal(otherSignal) -{ - m_signal.prepend('2'); -} - -void SignalSignalConnection::trigger(PyObject* args) -{ - SignalManager::instance().emitSignal(m_receiver, m_signal.constData(), args); -} - diff --git a/libpyside/signalsignalconnection.h b/libpyside/signalsignalconnection.h deleted file mode 100644 index e444ed101..000000000 --- a/libpyside/signalsignalconnection.h +++ /dev/null @@ -1,54 +0,0 @@ -/* -* This file is part of the Shiboken Python Bindings Generator project. -* -* Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -* -* Contact: PySide team <contact@pyside.org> -* -* This program is free software; you can redistribute it and/or -* modify it under the terms of the GNU Lesser General Public License -* version 2.1 as published by the Free Software Foundation. Please -* review the following information to ensure the GNU Lesser General -* Public License version 2.1 requirements will be met: -* http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -* -* As a special exception to the GNU Lesser General Public License -* version 2.1, the object code form of a "work that uses the Library" -* may incorporate material from a header file that is part of the -* Library. You may distribute such object code under terms of your -* choice, provided that the incorporated material (i) does not exceed -* more than 5% of the total size of the Library; and (ii) is limited to -* numerical parameters, data structure layouts, accessors, macros, -* inline functions and templates. -* -* This program 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 program; if not, write to the Free Software -* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -* 02110-1301 USA -*/ - -#ifndef SIGNALSIGNALCONNECTION_H -#define SIGNALSIGNALCONNECTION_H -#include "abstractqobjectconnection.h" - -namespace PySide -{ - -class SignalSignalConnection : public AbstractQObjectConnection -{ -public: - SignalSignalConnection(QObject* source, const char* signal, QObject* receiver, const char* otherSignal, Qt::ConnectionType connectionType); - virtual void trigger(PyObject* args); -private: - QObject* m_receiver; - QByteArray m_signal; -}; - -} - -#endif diff --git a/libpyside/typeresolver.cpp b/libpyside/typeresolver.cpp index d4c704ab9..49ca19f75 100644 --- a/libpyside/typeresolver.cpp +++ b/libpyside/typeresolver.cpp @@ -116,3 +116,9 @@ PyObject* TypeResolver::toPython(void* cppObj) return m_d->cppToPython(cppObj); } +void TypeResolver::deleteObject(void* object) +{ + if (m_d->deleteObject) + m_d->deleteObject(object); +} + diff --git a/libpyside/typeresolver.h b/libpyside/typeresolver.h index 25ccfdf7f..84191f578 100644 --- a/libpyside/typeresolver.h +++ b/libpyside/typeresolver.h @@ -89,6 +89,7 @@ public: const char* typeName() const; PyObject* toPython(void* cppObj); void* toCpp(PyObject* pyObj); + void deleteObject(void* object); private: struct TypeResolverPrivate; TypeResolverPrivate* m_d; |