aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHugo Lima <hugo.lima@openbossa.org>2009-12-29 17:04:56 -0200
committerHugo Lima <hugo.lima@openbossa.org>2010-01-04 15:48:36 -0200
commit85048bbd6c5d4231f71852cab18fd581c5068141 (patch)
treef1a1b38b04e748c0c50825d51d38c7dafe7ee60e
parente8e1ecc260028d045efcbc3c71d7a50009950f5b (diff)
Fixes problems calling dynamic python slots.
Reviewed by Marcelo Lira <marcelo.lira@openbossa.org>
-rw-r--r--PySide/QtCore/glue/qobject_connect.cpp6
-rw-r--r--libpyside/globalreceiver.cpp39
-rw-r--r--libpyside/globalreceiver.h2
-rw-r--r--libpyside/signalmanager.cpp144
-rw-r--r--libpyside/signalmanager.h4
5 files changed, 137 insertions, 58 deletions
diff --git a/PySide/QtCore/glue/qobject_connect.cpp b/PySide/QtCore/glue/qobject_connect.cpp
index 5fe0ac427..846ab5012 100644
--- a/PySide/QtCore/glue/qobject_connect.cpp
+++ b/PySide/QtCore/glue/qobject_connect.cpp
@@ -54,13 +54,17 @@ static bool qobjectConnectCallback(QObject* source, const char* signal, PyObject
self = PyMethod_GET_SELF(callback);
if (SbkQObject_Check(self))
receiver = SbkQObject_cptr(self);
+ } else if (PyCFunction_Check(callback)) {
+ self = PyCFunction_GET_SELF(callback);
+ if (self && 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 QByteArray callbackSig = PySide::getCallbackSignature(signal, callback, usingGlobalReceiver).toAscii();
const char* slot = callbackSig.constData();
int slotIndex = metaObject->indexOfSlot(slot);
if (slotIndex == -1) {
diff --git a/libpyside/globalreceiver.cpp b/libpyside/globalreceiver.cpp
index b058cf537..0d6ce7cf2 100644
--- a/libpyside/globalreceiver.cpp
+++ b/libpyside/globalreceiver.cpp
@@ -41,8 +41,7 @@
using namespace PySide;
-GlobalReceiver::GlobalReceiver()
- : m_metaObject("GlobalReceiver", &QObject::staticMetaObject)
+GlobalReceiver::GlobalReceiver() : m_metaObject("GlobalReceiver", &QObject::staticMetaObject)
{
}
@@ -64,6 +63,16 @@ void GlobalReceiver::addSlot(const char* slot, PyObject* callback)
Py_INCREF(callback);
m_slotReceivers[slotId] = callback;
+ bool isShortCircuit = true;
+ for (int i = 0; slot[i]; ++i) {
+ if (slot[i] == '(') {
+ isShortCircuit = false;
+ break;
+ }
+ }
+ if (isShortCircuit)
+ m_shortCircuitSlots << slotId;
+
Q_ASSERT(slotId >= QObject::staticMetaObject.methodCount());
}
@@ -79,22 +88,32 @@ int GlobalReceiver::qt_metacall(QMetaObject::Call call, int id, void** args)
Q_ASSERT(id >= QObject::staticMetaObject.methodCount());
QMetaMethod slot = m_metaObject.method(id);
Q_ASSERT(slot.methodType() == QMetaMethod::Slot);
- QList<QByteArray> paramTypes = slot.parameterTypes();
PyObject* callback = m_slotReceivers.value(id);
if (!callback) {
- qWarning("Unknown global slot.");
+ qWarning() << "Unknown global slot, id:" << id;
return -1;
}
- 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);
+ int numArgs;
+ PyObject* retval = 0;
+ if (m_shortCircuitSlots.contains(id)) {
+ retval = PyObject_CallObject(callback, reinterpret_cast<PyObject*>(args[1]));
+ } else {
+ QList<QByteArray> paramTypes = slot.parameterTypes();
+ numArgs = paramTypes.count();
+ 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);
+ }
+
+ retval = PyObject_CallObject(callback, preparedArgs);
}
- Shiboken::AutoDecRef retval(PyObject_CallObject(callback, preparedArgs));
if (!retval)
- qWarning("Error calling slot");
+ qWarning() << "Error calling slot" << m_metaObject.method(id).signature();
+ else
+ Py_DECREF(retval);
return -1;
}
diff --git a/libpyside/globalreceiver.h b/libpyside/globalreceiver.h
index 32fc71539..e1e230b7b 100644
--- a/libpyside/globalreceiver.h
+++ b/libpyside/globalreceiver.h
@@ -38,6 +38,7 @@
#include <Python.h>
#include <QObject>
#include <QHash>
+#include <QSet>
#include "dynamicqmetaobject.h"
namespace PySide
@@ -55,6 +56,7 @@ public:
private:
DynamicQMetaObject m_metaObject;
QHash<int, PyObject* > m_slotReceivers;
+ QSet<int> m_shortCircuitSlots;
};
}
diff --git a/libpyside/signalmanager.cpp b/libpyside/signalmanager.cpp
index a222e0371..c8b55e77f 100644
--- a/libpyside/signalmanager.cpp
+++ b/libpyside/signalmanager.cpp
@@ -65,44 +65,75 @@ bool PySide::checkSignal(const char* signal)
return true;
}
-QString PySide::getCallbackSignature(const char* signal, PyObject* callback)
+static QString codeCallbackName(PyObject* callback, const QString& funcName)
{
- PyObject* function;
- int useSelf = PyMethod_Check(callback);
- if (useSelf) {
- function = PyMethod_GET_FUNCTION(callback);
- } else {
- function = callback;
+ return funcName+QString::number(quint64(callback), 16);
+}
+
+QString PySide::getCallbackSignature(const char* signal, PyObject* callback, bool encodeName)
+{
+ QString functionName;
+ int numArgs = -1;
+ bool useSelf = false;
+
+ bool isMethod = PyMethod_Check(callback);
+ bool isFunction = PyFunction_Check(callback);
+
+ if (isMethod || isFunction) {
+ PyObject* function = isMethod ? PyMethod_GET_FUNCTION(callback) : callback;
+ PyCodeObject* objCode = reinterpret_cast<PyCodeObject*>(PyFunction_GET_CODE(function));
+ functionName = PyString_AS_STRING(objCode->co_name);
+ useSelf = isMethod;
+ numArgs = objCode->co_flags & CO_VARARGS ? -1 : objCode->co_argcount;
+ } else if (PyCFunction_Check(callback)) {
+ functionName = ((PyCFunctionObject*)callback)->m_ml->ml_name;
+ useSelf = ((PyCFunctionObject*)callback)->m_self;
+ int flags = ((PyCFunctionObject*)callback)->m_ml->ml_flags;
+ if (flags & METH_O)
+ numArgs = 1;
+ else if (flags & METH_VARARGS)
+ numArgs = -1;
+ else if (flags & METH_NOARGS)
+ numArgs = 0;
}
+ Q_ASSERT(!functionName.isEmpty());
+
+ QString signature;
+ if (encodeName)
+ signature = codeCallbackName(callback, functionName);
+ else
+ signature = functionName;
- 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();
+ bool isShortCircuit;
+ QStringList args = getArgsFromSignature(signal, &isShortCircuit);
+
+ if (!isShortCircuit) {
+ signature.append('(');
+ if (numArgs == -1)
+ numArgs = std::numeric_limits<int>::max();
+ while (args.count() > numArgs - useSelf) {
+ args.removeLast();
+ }
+ signature.append(args.join(","));
+ signature.append(')');
}
- signature.append(args.join(","));
- signature.append(')');
return signature;
}
-QStringList PySide::getArgsFromSignature(const char* signature)
+QStringList PySide::getArgsFromSignature(const char* signature, bool* isShortCircuit)
{
QString qsignature(signature);
QStringList result;
QRegExp splitRegex("\\s*,\\s*");
+ if (isShortCircuit)
+ *isShortCircuit = !qsignature.contains('(');
if (qsignature.contains("()") || qsignature.contains("(void)")) {
return result;
} else if (qsignature.contains('(')) {
+ static QRegExp regex(".+\\((.*)\\)");
//get args types
- QString types = qsignature.replace(QRegExp(".+\\((.*)\\)"), "\\1");
+ QString types = qsignature.replace(regex, "\\1");
result = types.split(splitRegex);
}
return result;
@@ -138,31 +169,52 @@ void SignalManager::addGlobalSlot(const char* slot, PyObject* callback)
m_d->m_globalReceiver.addSlot(slot, callback);
}
+static bool emitShortCircuitSignal(QObject* source, int signalIndex, PyObject* args)
+{
+ void* signalArgs[2] = {0, args};
+ QMetaObject::activate(source, signalIndex, signalArgs);
+ return true;
+}
+
+static bool emitNormalSignal(QObject* source, int signalIndex, const char* signal, PyObject* args, const QStringList& argTypes)
+{
+ int argsGiven = PySequence_Size(args);
+ if (argsGiven > argTypes.count()) {
+ QString msg = QString("%1 only accepts %2 arguments, %3 given!").arg(signal).arg(argTypes.count()).arg(argsGiven);
+ PyErr_SetString(PyExc_TypeError, msg.toLocal8Bit().constData());
+ 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;
+}
+
bool SignalManager::emitSignal(QObject* source, const char* signal, PyObject* args)
{
if (!checkSignal(signal))
return false;
signal++;
- int argsGiven = PySequence_Size(args);
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;
+ bool isShortCircuit;
+ QStringList argTypes = getArgsFromSignature(signal, &isShortCircuit);
+
+ if (isShortCircuit)
+ return emitShortCircuitSignal(source, signalIndex, args);
+ else
+ return emitNormalSignal(source, signalIndex, signal, args, argTypes);
}
+ qWarning() << "Error emitting signal: " << signal;
return false;
}
@@ -183,22 +235,24 @@ int PySide::SignalManager::qt_metacall(QObject* object, QMetaObject::Call call,
// call python slot
QList<QByteArray> paramTypes = method.parameterTypes();
PyObject* self = Shiboken::BindingManager::instance().retrieveWrapper(object);
- Shiboken::AutoDecRef preparedArgs(PyTuple_New(paramTypes.count()+1));
+ Shiboken::AutoDecRef preparedArgs(PyTuple_New(paramTypes.count()));
- 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);
+ PyTuple_SET_ITEM(preparedArgs.object(), i, 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");
+ Shiboken::AutoDecRef pyMethod(PyObject_GetAttrString(self, qPrintable(methodName)));
+ if (pyMethod) {
+ Shiboken::AutoDecRef retval(PyObject_CallObject(pyMethod, preparedArgs));
+ if (!retval)
+ qWarning() << "Error calling slot" << methodName;
+ } else {
+ qWarning() << "Dynamic slot" << methodName << "not found!";
+ }
}
return -1;
}
diff --git a/libpyside/signalmanager.h b/libpyside/signalmanager.h
index 14ade8a6d..870aecdfe 100644
--- a/libpyside/signalmanager.h
+++ b/libpyside/signalmanager.h
@@ -47,8 +47,8 @@ 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);
+PYSIDE_API QString getCallbackSignature(const char* signal, PyObject* callback, bool encodeName);
+QStringList getArgsFromSignature(const char* signature, bool* isShortCircuit = 0);
class PYSIDE_API SignalManager
{