Commit e4b949ab authored by Sebastian Sauer's avatar Sebastian Sauer
Browse files

* made signals+slots working again for the python-backend.

* more work on scripting functions.


svn path=/trunk/koffice/; revision=602647
parent 51605ef8
......@@ -17,9 +17,10 @@ set(libkrosspythoncxx_SRCS
set(krosspython_PART_SRCS ${libkrosspythoncxx_SRCS}
pythonvariant.cpp
pythonfunction.cpp
pythonextension.cpp
pythonmodule.cpp
pythonscript.cpp
pythonmodule.cpp
pythonscript.cpp
pythoninterpreter.cpp
)
......
......@@ -20,6 +20,7 @@
#include "pythonextension.h"
#include "pythoninterpreter.h"
#include "pythonvariant.h"
#include "pythonfunction.h"
#include <QWidget>
#include <QMetaMethod>
......@@ -49,6 +50,9 @@ namespace Kross {
/// The cached list of enumerations.
QHash<QByteArray, int> enumerations;
/// The \a PythonFunction instances.
QHash<QByteArray, PythonFunction*> functions;
/// The cached list of methodnames.
Py::List methodnames;
/// The cached list of membernames.
......@@ -91,7 +95,7 @@ PythonExtension::PythonExtension(QObject* object)
//add_varargs_method("__toPointer__", &PythonExtension::toPointer, "Return the void* pointer of the QObject.");
//add_varargs_method("__fromPointer__", &PythonExtension::fromPointer, "Set the QObject* to the passed void* pointer.");
add_varargs_method("connect", &PythonExtension::doConnect, "Connect signal, slots or python functions together.");
//add_varargs_method("emit", &PythonExtension::emitSignal, "Emit a signal the QObject provides.");
add_varargs_method("disconnect", &PythonExtension::doDisconnect, "Disconnect signal, slots or python functions that are connected together.");
d->proxymethod = new Py::MethodDefExt<PythonExtension>(
"", // methodname, not needed cause we use the method only internaly.
......@@ -114,7 +118,6 @@ PythonExtension::PythonExtension(QObject* object)
self[0] = Py::Object(this); // reference to this instance
self[1] = Py::Int(i); // the first index used for faster access
self[2] = Py::String(name); // the name of the method
d->methods.insert(name, Py::Object(PyCFunction_New( &d->proxymethod->ext_meth_def, self.ptr() ), true));
d->methodnames.append(self[2]);
}
......@@ -150,6 +153,7 @@ PythonExtension::~PythonExtension()
#ifdef KROSS_PYTHON_EXTENSION_CTORDTOR_DEBUG
krossdebug( QString("PythonExtension::Destructor object=%1").arg(d->debuginfo) );
#endif
qDeleteAll(d->functions);
delete d->proxymethod;
delete d;
}
......@@ -369,17 +373,16 @@ Py::Object PythonExtension::doConnect(const Py::Tuple& args)
}
}
if( args[idx].isCallable() ) { // connect(..., pyfunction)
Py::Callable func(args[idx]);
//TODO add adaptor
//Py::Object result = func.apply( PythonType<QVariantList,Py::Tuple>::toPyObject(args) );
krossdebug( QString("TOOOOOOOOOOO-DOOOOOOOOOOOO PythonExtension::doConnect sender=%1 signal=%2 pyfunction=%3").arg(sender->objectName()).arg(sendersignal.constData()).arg(func.as_string().c_str()).toLatin1().constData() );
QObject* receiver; // the receiver object
QByteArray receiverslot; // the receiver slot
if( args[idx].isCallable() ) { // connect with python function
Py::Callable func(args[idx]); // the callable python function
PythonFunction* function = new PythonFunction(sender, sendersignal, func);
d->functions.insertMulti(sendersignal, function);
receiver = function;
receiverslot = sendersignal;
}
else {
QObject* receiver; // the receiver object
QByteArray receiverslot; // the receiver slot
else { // connect with receiver+slot
if( args[idx].isString() ) { // connect(..., slot)
receiver = d->object;
receiverslot = PythonType<QByteArray>::toVariant( args[idx] );
......@@ -403,30 +406,31 @@ Py::Object PythonExtension::doConnect(const Py::Tuple& args)
}
receiverslot = PythonType<QByteArray>::toVariant( args[idx] );
}
if( args.size() > idx + 1 ) {
Py::TypeError( QString("To much arguments specified.").toLatin1().constData() );
return PythonType<bool>::toPyObject(false);
}
}
// Dirty hack to replace SIGNAL() and SLOT() macros. If the user doesn't
// defined them explicit, we assume it's wanted to connect from a signal to
// a slot. This seems to be the most flexible solution so far...
if( ! sendersignal.startsWith('1') && ! sendersignal.startsWith('2') )
sendersignal.prepend('2'); // prepending 2 means SIGNAL(...)
if( ! receiverslot.startsWith('1') && ! receiverslot.startsWith('2') )
receiverslot.prepend('1'); // prepending 1 means SLOT(...)
// Dirty hack to replace SIGNAL() and SLOT() macros. If the user doesn't
// defined them explicit, we assume it's wanted to connect from a signal to
// a slot. This seems to be the most flexible solution so far...
if( ! sendersignal.startsWith('1') && ! sendersignal.startsWith('2') )
sendersignal.prepend('2'); // prepending 2 means SIGNAL(...)
if( ! receiverslot.startsWith('1') && ! receiverslot.startsWith('2') )
receiverslot.prepend('1'); // prepending 1 means SLOT(...)
krossdebug( QString("PythonExtension::doConnect sender=%1 signal=%2 receiver=%3 slot=%4").arg(sender->objectName()).arg(sendersignal.constData()).arg(receiver->objectName()).arg(receiverslot.constData()).toLatin1().constData() );
krossdebug( QString("PythonExtension::doConnect sender=%1 signal=%2 receiver=%3 slot=%4").arg(sender->objectName()).arg(sendersignal.constData()).arg(receiver->objectName()).arg(receiverslot.constData()).toLatin1().constData() );
if(! QObject::connect(sender, sendersignal, receiver, receiverslot) ) {
krosswarning( QString("PythonExtension::doConnect Failed to connect").toLatin1().constData() );
return PythonType<bool>::toPyObject(false);
}
if(! QObject::connect(sender, sendersignal, receiver, receiverslot) ) {
krosswarning( QString("PythonExtension::doConnect Failed to connect").toLatin1().constData() );
return PythonType<bool>::toPyObject(false);
}
return PythonType<bool>::toPyObject(true);
}
Py::Object PythonExtension::doDisconnect(const Py::Tuple&)
{
//TODO
return PythonType<bool>::toPyObject(false);
}
PyObject* PythonExtension::proxyhandler(PyObject *_self_and_name_tuple, PyObject *args)
{
try {
......
......@@ -116,8 +116,10 @@ namespace Kross {
//Py::Object toPointer(const Py::Tuple&);
//Py::Object fromPointer(const Py::Tuple&);
/// Connect signal with python function.
/// Connect signal, slots or python functions together.
Py::Object doConnect(const Py::Tuple&);
/// Disconnect signal, slots or python functions that are connected together.
Py::Object doDisconnect(const Py::Tuple&);
/**
* The static proxy-handler which will be used to dispatch
......
/***************************************************************************
* pythonfunction.cpp
* This file is part of the KDE project
* copyright (C)2006 by Sebastian Sauer (mail@dipe.org)
*
* Parts of the code are from kjsembed4 SlotProxy
* Copyright (C) 2005, 2006 KJSEmbed Authors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
* 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
* Library General Public License for more details.
* You should have received a copy of the GNU Library General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
***************************************************************************/
#include "pythonfunction.h"
#include "pythonvariant.h"
#include "../core/metatype.h"
#include <QByteArray>
#include <QPointer>
#include <QVariant>
#include <QMetaMethod>
#include <QMetaObject>
using namespace Kross;
namespace Kross {
/// \internal d-pointer class.
class PythonFunction::Private
{
public:
QPointer<QObject> sender;
QByteArray signature;
Py::Callable callable;
QByteArray stringData;
uint data[21];
};
}
PythonFunction::PythonFunction(QObject* sender, const QByteArray& sendersignal, const Py::Callable& callable)
: QObject()
, d( new Private() )
{
d->sender = sender;
d->signature = QMetaObject::normalizedSignature( sendersignal );
d->callable = callable;
//krossdebug(QString("PythonFunction::PythonFunction sender=\"%1\" signal=\"%2\"").arg(sender->objectName()).arg(d->signature.constData()));
uint signatureSize = d->signature.size() + 1;
// content
d->data[0] = 1; // revision
d->data[1] = 0; // classname
d->data[2] = 0; // classinfo
d->data[3] = 0; // classinfo
d->data[4] = 1; // methods
d->data[5] = 15; // methods
d->data[6] = 0; // properties
d->data[7] = 0; // properties
d->data[8] = 0; // enums/sets
d->data[9] = 0; // enums/sets
// slots
d->data[15] = 15; // signature start
d->data[16] = 15 + signatureSize; // parameters start
d->data[17] = 15 + signatureSize; // type start
d->data[18] = 15 + signatureSize; // tag start
d->data[19] = 0x0a; // flags
d->data[20] = 0; // eod
// data
d->stringData = QByteArray("PythonFunction\0", 15);
d->stringData += d->signature;
d->stringData += QByteArray("\0\0", 2);
// static metaobject
staticMetaObject.d.superdata = &QObject::staticMetaObject;
staticMetaObject.d.stringdata = d->stringData.data();
staticMetaObject.d.data = d->data;
staticMetaObject.d.extradata = 0;
}
PythonFunction::~PythonFunction()
{
//krossdebug("PythonFunction::~PythonFunction");
delete d;
}
const QMetaObject* PythonFunction::metaObject() const
{
return &staticMetaObject;
}
void* PythonFunction::qt_metacast(const char *_clname)
{
if (! _clname)
return 0;
if (! strcmp(_clname, d->stringData))
return static_cast<void*>( const_cast<PythonFunction*>(this) );
return QObject::qt_metacast(_clname);
}
int PythonFunction::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
{
_id = QObject::qt_metacall(_c, _id, _a);
//krossdebug(QString("PythonFunction::qt_metacall id=%1").arg(_id));
if (_id >= 0 && _c == QMetaObject::InvokeMetaMethod) {
switch (_id) {
case 0: {
// convert the arguments
QVariantList args;
QMetaMethod method = metaObject()->method( metaObject()->indexOfMethod(d->signature) );
QList<QByteArray> params = method.parameterTypes();
int idx = 1;
foreach(QByteArray param, params) {
int tp = QVariant::nameToType( param.constData() );
if(tp != QVariant::Invalid) {
args.append( QVariant(tp, _a[idx]) );
}
else {
krosswarning("PythonFunction::qt_metacall: Not supported yet!");
//TODO implement
}
++idx;
}
// call the python function
Py::Object result = d->callable.apply( PythonType<QVariantList,Py::Tuple>::toPyObject(args) );
//TODO finally set the returnvalue
//QVariant v = PythonType<QVariant>::toVariant(result);
//_a[0] = Kross::MetaTypeVariant<QVariant>(v).toVoidStar();
} break;
}
_id -= 1;
}
return _id;
}
/***************************************************************************
* pythonfunction.h
* This file is part of the KDE project
* copyright (C)2006 by Sebastian Sauer (mail@dipe.org)
*
* Parts of the code are from kjsembed4 SlotProxy
* Copyright (C) 2005, 2006 KJSEmbed Authors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
* 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
* Library General Public License for more details.
* You should have received a copy of the GNU Library General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
***************************************************************************/
#ifndef KROSS_PYTHONFUNCTION_H
#define KROSS_PYTHONFUNCTION_H
#include "pythonconfig.h"
#include <QObject>
class QMetaObject;
class QByteArray;
namespace Kross {
/**
* The PythonFunction class implements a QObject to provide
* an adaptor between Qt signals+slots and python functions.
*/
class PythonFunction : public QObject
{
public:
PythonFunction(QObject* sender, const QByteArray& sendersignal, const Py::Callable& callable);
virtual ~PythonFunction();
QMetaObject staticMetaObject;
const QMetaObject *metaObject() const;
void *qt_metacast(const char *_clname);
int qt_metacall(QMetaObject::Call _c, int _id, void **_a);
private:
/// \internal d-pointer class.
class Private;
/// \internal d-pointer instance.
Private* const d;
};
}
#endif
......@@ -78,6 +78,7 @@ class TestObject : public QObject
signals:
void signalVoid();
void signalBool(bool);
void signalInt(int);
void signalString(const QString&);
......
......@@ -145,9 +145,13 @@ class TestKross(unittest.TestCase):
#self.assert_( self.object1.testEnum( self.object1.TESTENUM3 ) == 4 )
def testSignalsSlots(self):
ok = self.object1.connect("signalString(const QString&)", self.object2, "func_qstring_qstring(const QString&)")
print "============================> %s\n" % ok
#self.assert_( self.object1.TESTENUM1 == 1 )
self.assert_( self.object1.connect("signalBool(bool)", "func_bool_bool(bool)") )
self.assert_( self.object1.connect("signalInt(int)", self.object2, "func_int_int(int)") )
def callback(s):
self.assert_(s == " The Argument String ")
self.assert_( self.object1.connect("signalString(const QString&)", callback) )
self.object1.signalString(" The Argument String ")
#def testExpectedFailures(self):
# to less arguments
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment