Fixes problems calling dynamic python slots.
Reviewed by Marcelo Lira <marcelo.lira@openbossa.org>
This commit is contained in:
parent
e8e1ecc260
commit
85048bbd6c
5 changed files with 137 additions and 58 deletions
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue