1 /***************************************************************************
3 * This file is part of the KDE project
4 * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org)
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public License
15 * along with this program; see the file COPYING. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 ***************************************************************************/
20 #include "pythonextension.h"
21 #include "pythoninterpreter.h"
22 #include "pythonvariant.h"
23 #include "pythonfunction.h"
26 #include <QMetaMethod>
28 #include <QVarLengthArray>
32 /// \internal d-pointer class.
33 class PythonExtension::Private
36 /// The QObject this PythonExtension wraps.
37 QPointer
<QObject
> object
;
38 /// Defines if this PythonExtension the owner of the QObject.
41 #ifdef KROSS_PYTHON_EXTENSION_CTORDTOR_DEBUG
42 /// \internal string for debugging.
46 /// The cached list of methods.
47 QHash
<QByteArray
, Py::Int
> methods
;
48 /// The cached list of properties.
49 QHash
<QByteArray
, QMetaProperty
> properties
;
50 /// The cached list of enumerations.
51 QHash
<QByteArray
, int> enumerations
;
53 /// The \a PythonFunction instances.
54 QHash
<QByteArray
, PythonFunction
*> functions
;
56 /// The cached list of methodnames.
58 /// The cached list of membernames.
61 /// The proxymethod which will handle all calls to our \a PythonExtension instance.
62 Py::MethodDefExt
<PythonExtension
>* proxymethod
;
68 using namespace Kross
;
70 PythonExtension::PythonExtension(QObject
* object
, bool owner
)
71 : Py::PythonExtension
<PythonExtension
>()
77 #ifdef KROSS_PYTHON_EXTENSION_CTORDTOR_DEBUG
78 d
->debuginfo
= object
? QString("%1 (%2)").arg(object
->objectName()).arg(object
->metaObject()->className()) : "NULL";
79 krossdebug( QString("PythonExtension::Constructor object=%1").arg(d
->debuginfo
) );
82 behaviors().name("KrossPythonExtension");
83 behaviors().doc("The KrossPythonExtension object wraps a QObject into the world of python.");
84 //behaviors().supportPrint();
85 behaviors().supportGetattr();
86 behaviors().supportSetattr();
87 //behaviors().supportGetattro();
88 //behaviors().supportSetattro();
89 behaviors().supportCompare();
90 //behaviors().supportRepr();
91 //behaviors().supportStr();
92 behaviors().supportHash();
93 //behaviors().supportIter();
94 //behaviors().supportCall();
95 behaviors().supportSequenceType();
96 behaviors().supportMappingType();
97 behaviors().supportNumberType();
98 //behaviors().supportBufferType();
100 add_varargs_method("className", &PythonExtension::getClassName
, "Return the name of the QObject class.");
101 //add_varargs_method("classInfo", &PythonExtension::getClassInfo, "Return a list of key,value-tuples of class information.");
102 add_varargs_method("signalNames", &PythonExtension::getSignalNames
, "Return list of signal names the QObject provides.");
103 add_varargs_method("slotNames", &PythonExtension::getSlotNames
, "Return list of slot names the QObject provides.");
104 add_varargs_method("propertyNames", &PythonExtension::getPropertyNames
, "Return list of property names the QObject provides.");
105 //add_varargs_method("dynamicPropertyNames", &PythonExtension::getDynamicPropertyNames, "");
106 add_varargs_method("property", &PythonExtension::getProperty
, "Return a property value.");
107 add_varargs_method("setProperty", &PythonExtension::setProperty
, "Set a property value.");
108 add_varargs_method("__toPointer__", &PythonExtension::toPointer
, "Return the void* pointer of the QObject.");
109 //add_varargs_method("__fromPointer__", &PythonExtension::fromPointer, "Set the QObject* to the passed void* pointer.");
110 add_varargs_method("connect", &PythonExtension::doConnect
, "Connect signal, slots or python functions together.");
111 add_varargs_method("disconnect", &PythonExtension::doDisconnect
, "Disconnect signal, slots or python functions that are connected together.");
113 d
->proxymethod
= new Py::MethodDefExt
<PythonExtension
>(
114 "", // methodname, not needed cause we use the method only internaly.
115 0, // method that should handle the callback, not needed cause proxyhandler will handle it.
116 Py::method_varargs_call_handler_t( proxyhandler
), // callback handler
121 const QMetaObject
* metaobject
= d
->object
->metaObject();
123 { // initialize methods.
124 const int count
= metaobject
->methodCount();
125 for(int i
= 0; i
< count
; ++i
) {
126 QMetaMethod member
= metaobject
->method(i
);
127 const QString signature
= member
.signature();
128 const QByteArray name
= signature
.left(signature
.indexOf('(')).toLatin1();
129 if(! d
->methods
.contains(name
)) {
130 d
->methods
.insert(name
, Py::Int(i
));
131 d
->methodnames
.append(Py::String(name
));
136 { // initialize properties
137 const int count
= metaobject
->propertyCount();
138 for(int i
= 0; i
< count
; ++i
) {
139 QMetaProperty prop
= metaobject
->property(i
);
140 d
->properties
.insert(prop
.name(), prop
);
141 d
->membernames
.append( Py::String(prop
.name()) );
145 { // initialize enumerations
146 const int count
= metaobject
->enumeratorCount();
147 for(int i
= 0; i
< count
; ++i
) {
148 QMetaEnum e
= metaobject
->enumerator(i
);
149 const int kc
= e
.keyCount();
150 for(int k
= 0; k
< kc
; ++k
) {
151 const QByteArray name
= /*e.name() +*/ e
.key(k
);
152 d
->enumerations
.insert(name
, e
.value(k
));
153 d
->membernames
.append( Py::String(name
) );
160 PythonExtension::~PythonExtension()
162 #ifdef KROSS_PYTHON_EXTENSION_CTORDTOR_DEBUG
163 krossdebug( QString("PythonExtension::Destructor object=%1").arg(d
->debuginfo
) );
168 qDeleteAll(d
->functions
);//FIXME curently it may delete connections (i.e. PythonFunctions) that we want to stay!
169 delete d
->proxymethod
;
173 QObject
* PythonExtension::object() const
178 Py::Object
PythonExtension::getattr(const char* n
)
180 #ifdef KROSS_PYTHON_EXTENSION_GETATTR_DEBUG
181 krossdebug( QString("PythonExtension::getattr name='%1'").arg(n
) );
184 // handle internal methods
186 if(strcmp(n
,"__methods__") == 0)
187 return d
->methodnames
;
188 if(strcmp(n
,"__members__") == 0)
189 return d
->membernames
;
190 //if(strcmp(n,"__dict__") == 0)
191 // return PythonType<QStringList>::toPyObject( QStringList() );
192 if(strcmp(n
,"__all__") == 0) //this hack is needed to prevent "from-import-* object has no __dict__ and no __all__" exceptions
193 return d
->methodnames
;
196 // look if the attribute is a method
197 if(d
->methods
.contains(n
)) {
198 #ifdef KROSS_PYTHON_EXTENSION_GETATTR_DEBUG
199 krossdebug( QString("PythonExtension::getattr name='%1' is a method.").arg(n
) );
202 // Python is more dynamic then someone thinks of what provides us bigger problems
203 // here since we are not able to cache the whole lookup and need to create and return
204 // a new function object at each request since 1) they may used internaly by python
205 // to redirect this method/attr to somebody else and 2) those returned function object
206 // may prevent this object instance from beeing removed by the garbage collector.
208 //FIXME use callcache here to improve the performance by some factors!
210 t
[0] = Py::Object(this); // reference to this instance, set at getattr()
211 t
[1] = d
->methods
[n
]; // the first index used for faster access
212 t
[2] = Py::String(n
); // the name of the method
213 t
.increment_reference_count(); // the PyCFunction_New shoukd take care of removing our ref...
214 return Py::Object(PyCFunction_New( &d
->proxymethod
->ext_meth_def
, t
.ptr() ), true);
217 // look if the attribute is a property
218 if(d
->properties
.contains(n
) && d
->object
) {
219 QMetaProperty property
= d
->properties
[n
];
221 #ifdef KROSS_PYTHON_EXTENSION_GETATTR_DEBUG
222 krossdebug( QString("PythonExtension::getattr name='%1' is a property: type=%2 valid=%3 readable=%4 scriptable=%5 writable=%6 user=%7 usertype=%8")
223 .arg(n
).arg(property
.typeName()).arg(property
.isValid())
224 .arg(property
.isReadable()).arg(property
.isScriptable(d
->object
)).arg(property
.isWritable())
225 .arg(property
.isUser(d
->object
)).arg(property
.userType())
229 if(! property
.isReadable()) {
230 Py::AttributeError( ::QString("Attribute \"%1\" is not readable.").arg(n
).toLatin1().constData() );
234 return PythonType
<QVariant
>::toPyObject( property
.read(d
->object
) );
237 // look if the attribute is an enumerator
238 if(d
->enumerations
.contains(n
)) {
239 return Py::Int(d
->enumerations
[n
]);
242 // look if it's a dynamic property
243 if(d
->object
&& d
->object
->dynamicPropertyNames().contains(n
)) {
244 return PythonType
<QVariant
>::toPyObject( d
->object
->property(n
) );
247 // finally redirect the unhandled attribute-request...
248 //return Py::PythonExtension<PythonExtension>::getattr_methods(n);
249 return Py::PythonExtension
<PythonExtension
>::getattr(n
);
252 int PythonExtension::setattr(const char* n
, const Py::Object
& value
)
254 // look if the attribute is a property
255 if(d
->properties
.contains(n
) && d
->object
) {
256 QMetaProperty property
= d
->properties
[n
];
258 #ifdef KROSS_PYTHON_EXTENSION_SETATTR_DEBUG
259 krossdebug( QString("PythonExtension::setattr name='%1' is a property: type=%2 valid=%3 readable=%4 scriptable=%5 writable=%6 usertype=%7")
260 .arg(n
).arg(property
.typeName()).arg(property
.isValid())
261 .arg(property
.isReadable()).arg(property
.isScriptable(d
->object
)).arg(property
.isWritable())
262 .arg(property
.isUser(d
->object
)).arg(property
.userType())
266 if(! property
.isWritable()) {
267 Py::AttributeError( ::QString("Attribute \"%1\" is not writable.").arg(n
).toLatin1().constData() );
268 return -1; // indicate error
271 QVariant v
= PythonType
<QVariant
>::toVariant(value
);
272 if(! property
.write(d
->object
, v
)) {
273 Py::AttributeError( ::QString("Setting attribute \"%1\" failed.").arg(n
).toLatin1().constData() );
274 return -1; // indicate error
276 #ifdef KROSS_PYTHON_EXTENSION_SETATTR_DEBUG
277 krossdebug( QString("PythonExtension::setattr name='%1' value='%2'").arg(n
).arg(v
.toString()) );
279 return 0; // indicate success
282 // finally redirect the unhandled attribute-request...
283 return Py::PythonExtension
<PythonExtension
>::setattr(n
, value
);
286 int PythonExtension::compare(const Py::Object
& other
)
288 if(Py::PythonExtension
<PythonExtension
>::check( other
)) {
289 Py::ExtensionObject
<PythonExtension
> extobj( other
);
290 PythonExtension
* extension
= extobj
.extensionObject();
291 QObject
* obj
= extension
->object();
292 //krossdebug( QString("PythonExtension::compare this.name='%1' other.name='%2' other.pyobject='%3'").arg(d->object ? d->object->objectName() : "NULL").arg(obj ? obj->objectName() : "NULL").arg(other.as_string().c_str()) );
294 //Q_ASSERT( object() );
295 return obj
== object() ? 0 : ( obj
> object() ? -1 : 1 );
297 PyErr_SetObject(PyExc_TypeError
, other
.ptr());
301 long PythonExtension::hash()
303 return long( (QObject
*) d
->object
);
306 /* objectName is a property anyway and therefore already accessible
307 Py::Object PythonExtension::getObjectName(const Py::Tuple&)
309 return PythonType<QString>::toPyObject( d->object->objectName() );
313 Py::Object
PythonExtension::getClassName(const Py::Tuple
&)
315 return PythonType
<QString
>::toPyObject( d
->object
->metaObject()->className() );
318 Py::Object
PythonExtension::getSignalNames(const Py::Tuple
&)
321 const QMetaObject
* metaobject
= d
->object
->metaObject();
322 const int count
= metaobject
->methodCount();
323 for(int i
= 0; i
< count
; ++i
) {
324 QMetaMethod m
= metaobject
->method(i
);
325 if( m
.methodType() == QMetaMethod::Signal
)
326 list
.append( Py::String(m
.signature()) );
331 Py::Object
PythonExtension::getSlotNames(const Py::Tuple
&)
334 const QMetaObject
* metaobject
= d
->object
->metaObject();
335 const int count
= metaobject
->methodCount();
336 for(int i
= 0; i
< count
; ++i
) {
337 QMetaMethod m
= metaobject
->method(i
);
338 if( m
.methodType() == QMetaMethod::Slot
)
339 list
.append( Py::String(m
.signature()) );
344 Py::Object
PythonExtension::getPropertyNames(const Py::Tuple
&)
347 const QMetaObject
* metaobject
= d
->object
->metaObject();
348 const int count
= metaobject
->propertyCount();
349 for(int i
= 0; i
< count
; ++i
)
350 list
.append( Py::String(metaobject
->property(i
).name()) );
354 Py::Object
PythonExtension::getProperty(const Py::Tuple
& args
)
356 if( args
.size() != 1 ) {
357 Py::TypeError("Expected the propertyname as argument.");
360 return PythonType
<QVariant
>::toPyObject( d
->object
->property(
361 PythonType
<QByteArray
>::toVariant(args
[0]).constData()
365 Py::Object
PythonExtension::setProperty(const Py::Tuple
& args
)
367 if( args
.size() != 2 ) {
368 Py::TypeError("Expected the propertyname and the value as arguments.");
371 return PythonType
<bool>::toPyObject( d
->object
->setProperty(
372 PythonType
<QByteArray
>::toVariant(args
[0]).constData(),
373 PythonType
<QVariant
>::toVariant(args
[1])
377 Py::Object
PythonExtension::toPointer(const Py::Tuple
&)
379 QObject
* obj
= d
->object
;
380 PyObject
* qobjectptr
= PyLong_FromVoidPtr( (void*) obj
);
381 //PyObject* o = Py_BuildValue ("N", mw);
382 return Py::asObject( qobjectptr
);
383 //PythonPyQtExtension* pyqtextension = new PythonPyQtExtension(self, args);
384 //return pyqtextension;
388 Py::Object PythonExtension::fromPointer(fromPointer(const Py::Tuple&)
390 QObject* object = dynamic_cast< QObject* >(PyLong_AsVoidPtr( args[0] ));
394 Py::Object
PythonExtension::doConnect(const Py::Tuple
& args
)
396 #ifdef KROSS_PYTHON_EXTENSION_CONNECT_DEBUG
397 krossdebug( QString("PythonExtension::doConnect" ) );
398 for(uint i
= 0; i
< args
.size(); ++i
)
400 QVariant v
= PythonType
<QVariant
>::toVariant( args
[i
] );
401 krossdebug( QString(" Argument index=%1 variant.toString=%2 variant.typeName=%3").arg(i
).arg(v
.toString()).arg(v
.typeName()) );
403 catch(Py::Exception
& e
) { // may happen e.g. on "function" types
404 PyTypeObject
*type
= (PyTypeObject
*) args
[i
].type().ptr();
405 krossdebug( QString(" Argument index=%1 tp_name=%2").arg(i
).arg(type
->tp_name
) );
410 if( args
.size() < 2 ) {
411 Py::TypeError("Expected at least 2 arguments.");
412 return PythonType
<bool>::toPyObject(false);
415 uint idx
; // next argument to check
416 QObject
* sender
; // the sender object
417 QByteArray sendersignal
; // the sender signal
418 if( args
[0].isString() ) { // connect(signal, ...)
419 //krossdebug( QString("PythonExtension::doConnect connect(signal, ...)" ) );
421 sendersignal
= PythonType
<QByteArray
>::toVariant( args
[0] );
424 else { // connect(sender, signal, ...)
425 //krossdebug( QString("PythonExtension::doConnect connect(sender, signal, ...)" ) );
426 Py::ExtensionObject
<PythonExtension
> extobj(args
[0]);
427 PythonExtension
* extension
= extobj
.extensionObject();
429 Py::TypeError( ::QString("First argument needs to be a signalname or a sender-object.").toLatin1().constData() );
430 return PythonType
<bool>::toPyObject(false);
432 sender
= extension
->object();
433 if( ! args
[1].isString() ) {
434 Py::TypeError( ::QString("Second argument needs to be a signalname.").toLatin1().constData() );
435 return PythonType
<bool>::toPyObject(false);
437 sendersignal
= PythonType
<QByteArray
>::toVariant( args
[1] );
439 if( args
.size() <= idx
) {
440 Py::TypeError( ::QString("Expected at least %1 arguments.").arg(idx
+1).toLatin1().constData() );
441 return PythonType
<bool>::toPyObject(false);
445 QObject
* receiver
; // the receiver object
446 QByteArray receiverslot
; // the receiver slot
447 if( args
[idx
].isCallable() ) { // connect with python function
448 //krossdebug( QString("PythonExtension::doConnect connect with python function" ) );
449 Py::Callable
func(args
[idx
]); // the callable python function
450 PythonFunction
* function
= new PythonFunction(sender
, sendersignal
, func
);
451 d
->functions
.insertMulti(sendersignal
, function
);
453 receiverslot
= sendersignal
;
455 else { // connect with receiver and slot
456 //krossdebug( QString("PythonExtension::doConnect connect with receiver and slot" ) );
457 if( args
[idx
].isString() ) { // connect(..., slot)
458 receiver
= d
->object
;
459 receiverslot
= PythonType
<QByteArray
>::toVariant( args
[idx
] );
461 else { // connect(..., receiver, slot)
462 Py::ExtensionObject
<PythonExtension
> extobj(args
[idx
]);
463 PythonExtension
* extension
= extobj
.extensionObject();
465 Py::TypeError( ::QString("Receiver argument needs to be a slotname or a receiver-object.").toLatin1().constData() );
466 return PythonType
<bool>::toPyObject(false);
468 receiver
= extension
->object();
470 if( args
.size() < idx
) {
471 Py::TypeError( ::QString("Expected at least %1 arguments.").arg(idx
+1).toLatin1().constData() );
472 return PythonType
<bool>::toPyObject(false);
474 if( ! args
[idx
].isString() ) {
475 Py::TypeError( ::QString("Expected receiver slotname as argument %1.").arg(idx
+1).toLatin1().constData() );
476 return PythonType
<bool>::toPyObject(false);
478 receiverslot
= PythonType
<QByteArray
>::toVariant( args
[idx
] );
482 // Dirty hack to replace SIGNAL() and SLOT() macros. If the user doesn't
483 // defined them explicit, we assume it's wanted to connect from a signal to
484 // a slot. This seems to be the most flexible solution so far...
485 if( ! sendersignal
.startsWith('1') && ! sendersignal
.startsWith('2') )
486 sendersignal
.prepend('2'); // prepending 2 means SIGNAL(...)
487 if( ! receiverslot
.startsWith('1') && ! receiverslot
.startsWith('2') )
488 receiverslot
.prepend('1'); // prepending 1 means SLOT(...)
490 #ifdef KROSS_PYTHON_EXTENSION_CONNECT_DEBUG
491 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() );
494 if(! QObject::connect(sender
, sendersignal
, receiver
, receiverslot
) ) {
495 krosswarning( QString("PythonExtension::doConnect Failed to connect").toLatin1().constData() );
496 return PythonType
<bool>::toPyObject(false);
498 return PythonType
<bool>::toPyObject(true);
501 Py::Object
PythonExtension::doDisconnect(const Py::Tuple
&)
504 return PythonType
<bool>::toPyObject(false);
507 PyObject
* PythonExtension::proxyhandler(PyObject
*_self_and_name_tuple
, PyObject
*args
)
512 GilState() { m_gilstate = PyGILState_Ensure(); } // Acquire interpreter lock
513 ~GilState() { PyGILState_Release( m_gilstate ); } // Free interpreter lock
515 PyGILState_STATE m_gilstate;
521 Py::Tuple
selftuple(_self_and_name_tuple
);
523 int methodindex
= Py::Int(selftuple
[1]);
525 QByteArray ba
= Py::String(selftuple
[2]).as_string().c_str();
526 const char* methodname
= ba
.constData();
528 #ifdef KROSS_PYTHON_EXTENSION_CALL_DEBUG
529 krossdebug( QString("PythonExtension::proxyhandler methodname=%1 methodindex=%2").arg(methodname
).arg(methodindex
) );
532 PythonExtension
*self
= static_cast<PythonExtension
*>( selftuple
[0].ptr() );
533 if( ! self
->d
->object
) {
534 Py::RuntimeError( QString("Underlying QObject instance of method '%1' was removed.").arg(methodname
).toLatin1().constData() );
538 Py::Tuple
argstuple(args
);
539 const int argssize
= int( argstuple
.size() );
540 QMetaMethod metamethod
= self
->d
->object
->metaObject()->method( methodindex
);
541 if(metamethod
.parameterTypes().size() != argssize
) {
543 const int count
= self
->d
->object
->metaObject()->methodCount();
544 for(++methodindex
; methodindex
< count
; ++methodindex
) {
545 metamethod
= self
->d
->object
->metaObject()->method( methodindex
);
546 const QString signature
= metamethod
.signature();
547 const QByteArray name
= signature
.left(signature
.indexOf('(')).toLatin1();
548 if(name
== methodname
) {
549 if(metamethod
.parameterTypes().size() == argssize
) {
556 krosswarning( QString("PythonExtension::proxyhandler The method '%1' does not expect %2 arguments.").arg(methodname
).arg(argssize
) );
557 throw Py::TypeError( QString("Invalid number of arguments for the method %1").arg(methodname
).toLatin1().constData() );
561 #ifdef KROSS_PYTHON_EXTENSION_CALL_DEBUG
562 krossdebug( QString("PythonExtension::proxyhandler QMetaMethod idx=%1 sig=%2 tag=%3 type=%4").arg(methodindex
).arg(metamethod
.signature()).arg(metamethod
.tag()).arg(metamethod
.typeName()) );
563 for(int i
= 0; i
< argssize
; ++i
) {
566 v
= PythonType
<QVariant
>::toVariant( argstuple
[i
] );
568 catch(Py::Exception
& e
) {
569 v
= Py::value(e
).as_string().c_str();
572 krossdebug( QString(" Argument index=%1 variant.toString=%2 variant.typeName=%3").arg(i
).arg(v
.toString()).arg(v
.typeName()) );
578 QList
<QByteArray
> typelist
= metamethod
.parameterTypes();
579 const int typelistcount
= typelist
.count();
580 bool hasreturnvalue
= strcmp(metamethod
.typeName(),"") != 0;
582 // exact 1 returnvalue + 0..9 arguments
583 Q_ASSERT(typelistcount
<= 10);
584 QVarLengthArray
<MetaType
*> variantargs( typelistcount
+ 1 );
585 QVarLengthArray
<void*> voidstarargs( typelistcount
+ 1 );
587 // set the return value
589 MetaType
* returntype
= PythonMetaTypeFactory::create( metamethod
.typeName(), Py::Object(), false /*owner*/ );
590 variantargs
[0] = returntype
;
591 voidstarargs
[0] = returntype
->toVoidStar();
592 #ifdef KROSS_PYTHON_EXTENSION_CALL_DEBUG
593 krossdebug( QString("PythonExtension::proxyhandler typeName=%1").arg(metamethod
.typeName()) );
598 voidstarargs
[0] = (void*)0;
604 for(; idx
<= typelistcount
; ++idx
) {
605 variantargs
[idx
] = PythonMetaTypeFactory::create( typelist
[idx
-1].constData(), argstuple
[idx
- 1], false /*owner*/ );
606 voidstarargs
[idx
] = variantargs
[idx
]->toVoidStar();
609 catch(Py::Exception
& e
) {
610 // Seems PythonMetaTypeFactory::create raised an exception
611 // up. Clean all already allocated MetaType instances.
612 for(int i
= 0; i
< idx
; ++i
)
613 delete variantargs
[i
];
614 throw e
; // re-throw exception
617 // call the method now
618 int r
= self
->d
->object
->qt_metacall(QMetaObject::InvokeMetaMethod
, methodindex
, &voidstarargs
[0]);
619 #ifdef KROSS_PYTHON_EXTENSION_CALL_DEBUG
620 krossdebug( QString("RESULT nr=%1").arg(r
) );
625 // eval the return-value
628 if( Kross::MetaTypeHandler
* handler
= Kross::Manager::self().metaTypeHandler(metamethod
.typeName()) ) {
629 #ifdef KROSS_PYTHON_EXTENSION_CALL_DEBUG
630 krossdebug( QString("Returnvalue of type '%2' has a handler").arg(metamethod
.typeName()) );
632 void *ptr
= (*reinterpret_cast<void*(*)>( variantargs
[0]->toVoidStar() ));
633 v
= handler
->callHandler(ptr
);
636 v
= QVariant(variantargs
[0]->typeId(), variantargs
[0]->toVoidStar());
638 if( ! Kross::Manager::self().strictTypesEnabled() ) {
639 if( v
.type() == QVariant::Invalid
&& QByteArray(metamethod
.typeName()).endsWith("*") ) {
640 #ifdef KROSS_PYTHON_EXTENSION_CALL_DEBUG
641 krossdebug( QString("Returnvalue of type '%2' will be reinterpret_cast<QObject*>").arg(metamethod
.typeName()) );
643 QObject
* obj
= (*reinterpret_cast<QObject
*(*)>( variantargs
[0]->toVoidStar() ));
644 v
.setValue( (QObject
*) obj
);
648 pyresult
= PythonType
<QVariant
>::toPyObject(v
);
649 #ifdef KROSS_PYTHON_EXTENSION_CALL_DEBUG
650 krossdebug( QString("Returnvalue typeId=%1 metamethod.typename=%2 variant.toString=%3 variant.typeName=%4 pyobject=%5").arg(variantargs
[0]->typeId()).arg(metamethod
.typeName()).arg(v
.toString()).arg(v
.typeName()).arg(pyresult
.as_string().c_str()) );
654 // finally free the PythonVariable instances
655 for(int i
= 0; i
<= typelistcount
; ++i
)
656 delete variantargs
[i
];
659 pyresult
.increment_reference_count(); // don't destroy PyObject* if pyresult got destroyed.
660 return pyresult
.ptr();
662 catch(Py::Exception
& e
) {
665 PythonInterpreter::extractException(trace
, lineno
);
666 krosswarning( QString("PythonExtension::proxyhandler Had exception on line %1:\n%2 \n%3").arg(lineno
).arg(Py::value(e
).as_string().c_str()).arg(trace
.join("\n")) );
667 PyErr_Print(); //e.clear();
673 int PythonExtension::sequence_length()
675 return d
->object
->children().count();
678 Py::Object
PythonExtension::sequence_concat(const Py::Object
& obj
)
680 throw Py::RuntimeError( QString("Unsupported: PythonExtension::sequence_concat %1").arg(obj
.as_string().c_str()).toLatin1().constData() );
683 Py::Object
PythonExtension::sequence_repeat(Py_ssize_t index
)
685 // what we do here is rather evil and does not make sense at all. We assume that something
686 // like "myobjinstance * 2" means, that the pointer-address should be multiplied by 2. This
687 // is the case to keep backward-compatibility with SuperKaramba. In normal cases you wan't
688 // use such kind of logic within your python scripts anyway + we can't guarantee that the
689 // resulting number may not overflow or something like this.
690 return Py::Long( long( (QObject
*) d
->object
) * index
);
693 Py::Object
PythonExtension::sequence_item(Py_ssize_t index
)
695 if(index
< d
->object
->children().count())
696 return Py::asObject(new PythonExtension( d
->object
->children().at(index
) ));
697 return Py::asObject( Py::new_reference_to( NULL
) );
700 Py::Object
PythonExtension::sequence_slice(Py_ssize_t from
, Py_ssize_t to
)
704 const int count
= d
->object
->children().count();
705 for(int i
= from
; i
<= to
&& i
< count
; ++i
)
706 list
.append( Py::asObject(new PythonExtension( d
->object
->children().at(i
) )) );
711 int PythonExtension::sequence_ass_item(Py_ssize_t index
, const Py::Object
& obj
)
713 throw Py::RuntimeError( QString("Unsupported: PythonExtension::sequence_ass_item %1 %2").arg(index
).arg(obj
.as_string().c_str()).toLatin1().constData() );
716 int PythonExtension::sequence_ass_slice(Py_ssize_t from
, Py_ssize_t to
, const Py::Object
& obj
)
718 throw Py::RuntimeError( QString("Unsupported: PythonExtension::sequence_ass_slice %1 %2 %3").arg(from
).arg(to
).arg(obj
.as_string().c_str()).toLatin1().constData() );
721 int PythonExtension::mapping_length()
723 return d
->object
->children().count();
726 Py::Object
PythonExtension::mapping_subscript(const Py::Object
& obj
)
728 QString name
= Py::String(obj
).as_string().c_str();
729 QObject
* object
= d
->object
->findChild
< QObject
* >( name
);
731 foreach(QObject
* o
, d
->object
->children()) {
732 if(name
== o
->metaObject()->className()) {
739 return Py::asObject(new PythonExtension(object
));
740 return Py::asObject( Py::new_reference_to( NULL
) );
743 int PythonExtension::mapping_ass_subscript(const Py::Object
& obj1
, const Py::Object
& obj2
)
745 throw Py::RuntimeError( QString("Unsupported: PythonExtension::mapping_ass_subscript %1 %2").arg(obj1
.as_string().c_str()).arg(obj2
.as_string().c_str()).toLatin1().constData() );
748 int PythonExtension::number_nonzero()
750 #ifdef KROSS_PYTHON_EXTENSION_NUMERIC_DEBUG
751 krossdebug("PythonExtension::number_nonzero");
753 return d
->object
? 1 : 0;
756 Py::Object
PythonExtension::number_int()
758 #ifdef KROSS_PYTHON_EXTENSION_NUMERIC_DEBUG
759 krossdebug("PythonExtension::number_int");
761 return Py::Int( hash() );
764 Py::Object
PythonExtension::number_long()
766 #ifdef KROSS_PYTHON_EXTENSION_NUMERIC_DEBUG
767 krossdebug("PythonExtension::number_long");
769 return Py::Long( hash() );
772 Py::Object
PythonExtension::number_hex()
774 #ifdef KROSS_PYTHON_EXTENSION_NUMERIC_DEBUG
775 krossdebug("PythonExtension::number_hex");
777 void* ptr
= (QObject
*) d
->object
;
778 return Py::Object(PyString_FromFormat("%p",ptr
),true);