Fixes problems calling dynamic python slots.

Reviewed by Marcelo Lira <marcelo.lira@openbossa.org>
This commit is contained in:
Hugo Lima 2009-12-29 17:04:56 -02:00
commit 85048bbd6c
5 changed files with 137 additions and 58 deletions

View file

@ -54,13 +54,17 @@ static bool qobjectConnectCallback(QObject* source, const char* signal, PyObject
self = PyMethod_GET_SELF(callback); self = PyMethod_GET_SELF(callback);
if (SbkQObject_Check(self)) if (SbkQObject_Check(self))
receiver = SbkQObject_cptr(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; usingGlobalReceiver = !receiver;
if (usingGlobalReceiver) if (usingGlobalReceiver)
receiver = signalManager.globalReceiver(); receiver = signalManager.globalReceiver();
metaObject = receiver->metaObject(); 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(); const char* slot = callbackSig.constData();
int slotIndex = metaObject->indexOfSlot(slot); int slotIndex = metaObject->indexOfSlot(slot);
if (slotIndex == -1) { if (slotIndex == -1) {

View file

@ -41,8 +41,7 @@
using namespace PySide; using namespace PySide;
GlobalReceiver::GlobalReceiver() GlobalReceiver::GlobalReceiver() : m_metaObject("GlobalReceiver", &QObject::staticMetaObject)
: m_metaObject("GlobalReceiver", &QObject::staticMetaObject)
{ {
} }
@ -64,6 +63,16 @@ void GlobalReceiver::addSlot(const char* slot, PyObject* callback)
Py_INCREF(callback); Py_INCREF(callback);
m_slotReceivers[slotId] = 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()); 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()); Q_ASSERT(id >= QObject::staticMetaObject.methodCount());
QMetaMethod slot = m_metaObject.method(id); QMetaMethod slot = m_metaObject.method(id);
Q_ASSERT(slot.methodType() == QMetaMethod::Slot); Q_ASSERT(slot.methodType() == QMetaMethod::Slot);
QList<QByteArray> paramTypes = slot.parameterTypes();
PyObject* callback = m_slotReceivers.value(id); PyObject* callback = m_slotReceivers.value(id);
if (!callback) { if (!callback) {
qWarning("Unknown global slot."); qWarning() << "Unknown global slot, id:" << id;
return -1; return -1;
} }
Shiboken::AutoDecRef preparedArgs(PyTuple_New(paramTypes.count())); int numArgs;
for (int i = 0, max = paramTypes.count(); i < max; ++i) { PyObject* retval = 0;
PyObject* arg = TypeResolver::get(paramTypes[i].constData())->toPython(args[i+1]); if (m_shortCircuitSlots.contains(id)) {
PyTuple_SET_ITEM(preparedArgs.object(), i, arg); 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) if (!retval)
qWarning("Error calling slot"); qWarning() << "Error calling slot" << m_metaObject.method(id).signature();
else
Py_DECREF(retval);
return -1; return -1;
} }

View file

@ -38,6 +38,7 @@
#include <Python.h> #include <Python.h>
#include <QObject> #include <QObject>
#include <QHash> #include <QHash>
#include <QSet>
#include "dynamicqmetaobject.h" #include "dynamicqmetaobject.h"
namespace PySide namespace PySide
@ -55,6 +56,7 @@ public:
private: private:
DynamicQMetaObject m_metaObject; DynamicQMetaObject m_metaObject;
QHash<int, PyObject* > m_slotReceivers; QHash<int, PyObject* > m_slotReceivers;
QSet<int> m_shortCircuitSlots;
}; };
} }

View file

@ -65,44 +65,75 @@ bool PySide::checkSignal(const char* signal)
return true; return true;
} }
QString PySide::getCallbackSignature(const char* signal, PyObject* callback) static QString codeCallbackName(PyObject* callback, const QString& funcName)
{ {
PyObject* function; return funcName+QString::number(quint64(callback), 16);
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 PySide::getCallbackSignature(const char* signal, PyObject* callback, bool encodeName)
QString signature(PyString_AS_STRING(objCode->co_name)); {
signature.append(QString::number(quint64(callback), 16)); QString functionName;
signature.append('('); int numArgs = -1;
int numArgs = objCode->co_flags & CO_VARARGS ? -1 : objCode->co_argcount; bool useSelf = false;
QStringList args = getArgsFromSignature(signal); bool isMethod = PyMethod_Check(callback);
if (numArgs == -1) bool isFunction = PyFunction_Check(callback);
numArgs = std::numeric_limits<int>::max();
while (args.count() > numArgs - useSelf) { if (isMethod || isFunction) {
args.removeLast(); 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;
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; return signature;
} }
QStringList PySide::getArgsFromSignature(const char* signature) QStringList PySide::getArgsFromSignature(const char* signature, bool* isShortCircuit)
{ {
QString qsignature(signature); QString qsignature(signature);
QStringList result; QStringList result;
QRegExp splitRegex("\\s*,\\s*"); QRegExp splitRegex("\\s*,\\s*");
if (isShortCircuit)
*isShortCircuit = !qsignature.contains('(');
if (qsignature.contains("()") || qsignature.contains("(void)")) { if (qsignature.contains("()") || qsignature.contains("(void)")) {
return result; return result;
} else if (qsignature.contains('(')) { } else if (qsignature.contains('(')) {
static QRegExp regex(".+\\((.*)\\)");
//get args types //get args types
QString types = qsignature.replace(QRegExp(".+\\((.*)\\)"), "\\1"); QString types = qsignature.replace(regex, "\\1");
result = types.split(splitRegex); result = types.split(splitRegex);
} }
return result; return result;
@ -138,31 +169,52 @@ void SignalManager::addGlobalSlot(const char* slot, PyObject* callback)
m_d->m_globalReceiver.addSlot(slot, 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) bool SignalManager::emitSignal(QObject* source, const char* signal, PyObject* args)
{ {
if (!checkSignal(signal)) if (!checkSignal(signal))
return false; return false;
signal++; signal++;
int argsGiven = PySequence_Size(args);
int signalIndex = source->metaObject()->indexOfSignal(signal); int signalIndex = source->metaObject()->indexOfSignal(signal);
if (signalIndex != -1) { if (signalIndex != -1) {
QStringList argTypes = getArgsFromSignature(signal); bool isShortCircuit;
if (argsGiven > argTypes.count()) { QStringList argTypes = getArgsFromSignature(signal, &isShortCircuit);
PyErr_SetString(PyExc_TypeError, "Too many arguments for this signal.");
return false; if (isShortCircuit)
} return emitShortCircuitSignal(source, signalIndex, args);
void* signalArgs[argsGiven+1]; else
signalArgs[0] = 0; return emitNormalSignal(source, signalIndex, signal, args, argTypes);
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;
} }
qWarning() << "Error emitting signal: " << signal;
return false; return false;
} }
@ -183,22 +235,24 @@ int PySide::SignalManager::qt_metacall(QObject* object, QMetaObject::Call call,
// call python slot // call python slot
QList<QByteArray> paramTypes = method.parameterTypes(); QList<QByteArray> paramTypes = method.parameterTypes();
PyObject* self = Shiboken::BindingManager::instance().retrieveWrapper(object); 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) { for (int i = 0, max = paramTypes.count(); i < max; ++i) {
PyObject* arg = TypeResolver::get(paramTypes[i].constData())->toPython(args[i+1]); 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(); QString methodName = method.signature();
methodName = methodName.left(methodName.indexOf('(')); methodName = methodName.left(methodName.indexOf('('));
Shiboken::AutoDecRef pyMethodName(PyString_FromString(qPrintable(methodName))); Shiboken::AutoDecRef pyMethod(PyObject_GetAttrString(self, qPrintable(methodName)));
Shiboken::AutoDecRef pyMethod(PyObject_GetAttr(self, pyMethodName)); if (pyMethod) {
Shiboken::AutoDecRef retval(PyObject_CallObject(pyMethod, preparedArgs)); Shiboken::AutoDecRef retval(PyObject_CallObject(pyMethod, preparedArgs));
if (!retval) if (!retval)
qWarning("Error calling slot"); qWarning() << "Error calling slot" << methodName;
} else {
qWarning() << "Dynamic slot" << methodName << "not found!";
}
} }
return -1; return -1;
} }

View file

@ -47,8 +47,8 @@ namespace PySide
PYSIDE_API bool isSignal(const char* signal); PYSIDE_API bool isSignal(const char* signal);
PYSIDE_API bool checkSignal(const char* signal); PYSIDE_API bool checkSignal(const char* signal);
PYSIDE_API QString getCallbackSignature(const char* signal, PyObject* callback); PYSIDE_API QString getCallbackSignature(const char* signal, PyObject* callback, bool encodeName);
QStringList getArgsFromSignature(const char* signature); QStringList getArgsFromSignature(const char* signature, bool* isShortCircuit = 0);
class PYSIDE_API SignalManager class PYSIDE_API SignalManager
{ {