PythonQt.cpp
1792 lines
| 58.9 KiB
| text/x-c
|
CppLexer
/ src / PythonQt.cpp
ezust
|
r0 | /* | |
* | |||
florianlink
|
r133 | * Copyright (C) 2010 MeVis Medical Solutions AG All Rights Reserved. | |
ezust
|
r0 | * | |
* This library is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 2.1 of the License, or (at your option) any later version. | |||
* | |||
* This library 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 | |||
* Lesser General Public License for more details. | |||
* | |||
* Further, this software is distributed without any warranty that it is | |||
* free of the rightful claim of any third person regarding infringement | |||
* or the like. Any license provided herein, whether implied or | |||
* otherwise, applies only to this software file. Patent licenses, if | |||
* any, provided herein do not apply to combinations of this program with | |||
* other software, or any other product whatsoever. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public | |||
* License along with this library; if not, write to the Free Software | |||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |||
* | |||
florianlink
|
r133 | * Contact information: MeVis Medical Solutions AG, Universitaetsallee 29, | |
ezust
|
r0 | * 28359 Bremen, Germany or: | |
* | |||
* http://www.mevis.de | |||
* | |||
*/ | |||
//---------------------------------------------------------------------------------- | |||
/*! | |||
// \file PythonQt.cpp | |||
// \author Florian Link | |||
// \author Last changed by $Author: florian $ | |||
// \date 2006-05 | |||
*/ | |||
//---------------------------------------------------------------------------------- | |||
#include "PythonQt.h" | |||
#include "PythonQtImporter.h" | |||
#include "PythonQtClassInfo.h" | |||
#include "PythonQtMethodInfo.h" | |||
florianlink
|
r173 | #include "PythonQtSignal.h" | |
ezust
|
r0 | #include "PythonQtSignalReceiver.h" | |
#include "PythonQtConversion.h" | |||
florianlink
|
r161 | #include "PythonQtStdIn.h" | |
ezust
|
r0 | #include "PythonQtStdOut.h" | |
#include "PythonQtCppWrapperFactory.h" | |||
#include "PythonQtVariants.h" | |||
#include "PythonQtStdDecorators.h" | |||
florianlink
|
r8 | #include "PythonQtQFileImporter.h" | |
ezust
|
r0 | #include <pydebug.h> | |
florianlink
|
r10 | #include <vector> | |
ezust
|
r0 | ||
PythonQt* PythonQt::_self = NULL; | |||
florianlink
|
r4 | int PythonQt::_uniqueModuleCount = 0; | |
ezust
|
r0 | ||
florianlink
|
r109 | void PythonQt_init_QtGuiBuiltin(PyObject*); | |
void PythonQt_init_QtCoreBuiltin(PyObject*); | |||
florianlink
|
r98 | ||
florianlink
|
r191 | ||
PyObject* PythonQtConvertFromStringRef(const void* inObject, int /*metaTypeId*/) | |||
{ | |||
return PythonQtConv::QStringToPyObject(((QStringRef*)inObject)->toString()); | |||
} | |||
florianlink
|
r109 | void PythonQt::init(int flags, const QByteArray& pythonQtModuleName) | |
ezust
|
r0 | { | |
if (!_self) { | |||
florianlink
|
r109 | _self = new PythonQt(flags, pythonQtModuleName); | |
florianlink
|
r194 | ||
PythonQt::priv()->setupSharedLibrarySuffixes(); | |||
ezust
|
r0 | ||
florianlink
|
r109 | PythonQtMethodInfo::addParameterTypeAlias("QObjectList", "QList<QObject*>"); | |
qRegisterMetaType<QList<QObject*> >("QList<void*>"); | |||
florianlink
|
r84 | ||
florianlink
|
r191 | int stringRefId = qRegisterMetaType<QStringRef>("QStringRef"); | |
PythonQtConv::registerMetaTypeToPythonConverter(stringRefId, PythonQtConvertFromStringRef); | |||
florianlink
|
r109 | PythonQtRegisterToolClassesTemplateConverter(int); | |
PythonQtRegisterToolClassesTemplateConverter(float); | |||
PythonQtRegisterToolClassesTemplateConverter(double); | |||
PythonQtRegisterToolClassesTemplateConverter(qint32); | |||
PythonQtRegisterToolClassesTemplateConverter(quint32); | |||
PythonQtRegisterToolClassesTemplateConverter(qint64); | |||
PythonQtRegisterToolClassesTemplateConverter(quint64); | |||
// TODO: which other POD types should be available for QList etc. | |||
ezust
|
r0 | ||
florianlink
|
r109 | PythonQt_init_QtCoreBuiltin(NULL); | |
PythonQt_init_QtGuiBuiltin(NULL); | |||
florianlink
|
r157 | ||
florianlink
|
r180 | PythonQt::self()->addDecorators(new PythonQtStdDecorators()); | |
PythonQt::self()->registerCPPClass("QMetaObject",0, "QtCore", PythonQtCreateObject<PythonQtWrapper_QMetaObject>); | |||
florianlink
|
r125 | PythonQtRegisterToolClassesTemplateConverter(QByteArray); | |
florianlink
|
r109 | PythonQtRegisterToolClassesTemplateConverter(QDate); | |
PythonQtRegisterToolClassesTemplateConverter(QTime); | |||
PythonQtRegisterToolClassesTemplateConverter(QDateTime); | |||
PythonQtRegisterToolClassesTemplateConverter(QUrl); | |||
PythonQtRegisterToolClassesTemplateConverter(QLocale); | |||
PythonQtRegisterToolClassesTemplateConverter(QRect); | |||
PythonQtRegisterToolClassesTemplateConverter(QRectF); | |||
PythonQtRegisterToolClassesTemplateConverter(QSize); | |||
PythonQtRegisterToolClassesTemplateConverter(QSizeF); | |||
PythonQtRegisterToolClassesTemplateConverter(QLine); | |||
PythonQtRegisterToolClassesTemplateConverter(QLineF); | |||
PythonQtRegisterToolClassesTemplateConverter(QPoint); | |||
PythonQtRegisterToolClassesTemplateConverter(QPointF); | |||
PythonQtRegisterToolClassesTemplateConverter(QRegExp); | |||
PythonQtRegisterToolClassesTemplateConverter(QFont); | |||
PythonQtRegisterToolClassesTemplateConverter(QPixmap); | |||
PythonQtRegisterToolClassesTemplateConverter(QBrush); | |||
PythonQtRegisterToolClassesTemplateConverter(QColor); | |||
PythonQtRegisterToolClassesTemplateConverter(QPalette); | |||
PythonQtRegisterToolClassesTemplateConverter(QIcon); | |||
PythonQtRegisterToolClassesTemplateConverter(QImage); | |||
PythonQtRegisterToolClassesTemplateConverter(QPolygon); | |||
PythonQtRegisterToolClassesTemplateConverter(QRegion); | |||
PythonQtRegisterToolClassesTemplateConverter(QBitmap); | |||
PythonQtRegisterToolClassesTemplateConverter(QCursor); | |||
PythonQtRegisterToolClassesTemplateConverter(QSizePolicy); | |||
PythonQtRegisterToolClassesTemplateConverter(QKeySequence); | |||
PythonQtRegisterToolClassesTemplateConverter(QPen); | |||
PythonQtRegisterToolClassesTemplateConverter(QTextLength); | |||
PythonQtRegisterToolClassesTemplateConverter(QTextFormat); | |||
PythonQtRegisterToolClassesTemplateConverter(QMatrix); | |||
PyObject* pack = PythonQt::priv()->packageByName("QtCore"); | |||
PyObject* pack2 = PythonQt::priv()->packageByName("Qt"); | |||
PyObject* qtNamespace = PythonQt::priv()->getClassInfo("Qt")->pythonQtClassWrapper(); | |||
const char* names[16] = {"SIGNAL", "SLOT", "qAbs", "qBound","qDebug","qWarning","qCritical","qFatal" | |||
florianlink
|
r67 | ,"qFuzzyCompare", "qMax","qMin","qRound","qRound64","qVersion","qrand","qsrand"}; | |
florianlink
|
r109 | for (unsigned int i = 0;i<16; i++) { | |
PyObject* obj = PyObject_GetAttrString(qtNamespace, names[i]); | |||
if (obj) { | |||
PyModule_AddObject(pack, names[i], obj); | |||
Py_INCREF(obj); | |||
PyModule_AddObject(pack2, names[i], obj); | |||
} else { | |||
std::cerr << "method not found " << names[i]; | |||
} | |||
florianlink
|
r67 | } | |
} | |||
ezust
|
r0 | } | |
void PythonQt::cleanup() | |||
{ | |||
if (_self) { | |||
delete _self; | |||
_self = NULL; | |||
} | |||
} | |||
florianlink
|
r161 | PythonQt* PythonQt::self() { return _self; } | |
florianlink
|
r109 | PythonQt::PythonQt(int flags, const QByteArray& pythonQtModuleName) | |
ezust
|
r0 | { | |
_p = new PythonQtPrivate; | |||
_p->_initFlags = flags; | |||
_p->_PythonQtObjectPtr_metaId = qRegisterMetaType<PythonQtObjectPtr>("PythonQtObjectPtr"); | |||
florianlink
|
r84 | ||
florianlink
|
r150 | if ((flags & PythonAlreadyInitialized) == 0) { | |
florianlink
|
r161 | Py_SetProgramName(const_cast<char*>("PythonQt")); | |
florianlink
|
r144 | if (flags & IgnoreSiteModule) { | |
// this prevents the automatic importing of Python site files | |||
Py_NoSiteFlag = 1; | |||
} | |||
Py_Initialize(); | |||
ezust
|
r0 | } | |
florianlink
|
r157 | ||
ezust
|
r0 | // add our own python object types for qt object slots | |
if (PyType_Ready(&PythonQtSlotFunction_Type) < 0) { | |||
std::cerr << "could not initialize PythonQtSlotFunction_Type" << ", in " << __FILE__ << ":" << __LINE__ << std::endl; | |||
} | |||
Py_INCREF(&PythonQtSlotFunction_Type); | |||
florianlink
|
r84 | ||
florianlink
|
r173 | if (PyType_Ready(&PythonQtSignalFunction_Type) < 0) { | |
std::cerr << "could not initialize PythonQtSignalFunction_Type" << ", in " << __FILE__ << ":" << __LINE__ << std::endl; | |||
} | |||
Py_INCREF(&PythonQtSignalFunction_Type); | |||
florianlink
|
r18 | // according to Python docs, set the type late here, since it can not safely be stored in the struct when declaring it | |
PythonQtClassWrapper_Type.tp_base = &PyType_Type; | |||
// add our own python object types for classes | |||
florianlink
|
r16 | if (PyType_Ready(&PythonQtClassWrapper_Type) < 0) { | |
std::cerr << "could not initialize PythonQtClassWrapper_Type" << ", in " << __FILE__ << ":" << __LINE__ << std::endl; | |||
ezust
|
r0 | } | |
florianlink
|
r16 | Py_INCREF(&PythonQtClassWrapper_Type); | |
florianlink
|
r18 | ||
// add our own python object types for CPP instances | |||
if (PyType_Ready(&PythonQtInstanceWrapper_Type) < 0) { | |||
PythonQt::handleError(); | |||
std::cerr << "could not initialize PythonQtInstanceWrapper_Type" << ", in " << __FILE__ << ":" << __LINE__ << std::endl; | |||
} | |||
Py_INCREF(&PythonQtInstanceWrapper_Type); | |||
florianlink
|
r84 | ||
ezust
|
r0 | // add our own python object types for redirection of stdout | |
if (PyType_Ready(&PythonQtStdOutRedirectType) < 0) { | |||
std::cerr << "could not initialize PythonQtStdOutRedirectType" << ", in " << __FILE__ << ":" << __LINE__ << std::endl; | |||
} | |||
Py_INCREF(&PythonQtStdOutRedirectType); | |||
florianlink
|
r84 | ||
florianlink
|
r161 | // add our own python object types for redirection of stdin | |
if (PyType_Ready(&PythonQtStdInRedirectType) < 0) { | |||
std::cerr << "could not initialize PythonQtStdInRedirectType" << ", in " << __FILE__ << ":" << __LINE__ << std::endl; | |||
} | |||
Py_INCREF(&PythonQtStdInRedirectType); | |||
florianlink
|
r109 | initPythonQtModule(flags & RedirectStdOut, pythonQtModuleName); | |
ezust
|
r0 | ||
} | |||
PythonQt::~PythonQt() { | |||
delete _p; | |||
_p = NULL; | |||
} | |||
PythonQtPrivate::~PythonQtPrivate() { | |||
florianlink
|
r8 | delete _defaultImporter; | |
_defaultImporter = NULL; | |||
florianlink
|
r24 | ||
florianlink
|
r8 | { | |
florianlink
|
r24 | QHashIterator<QByteArray, PythonQtClassInfo *> i(_knownClassInfos); | |
ezust
|
r0 | while (i.hasNext()) { | |
delete i.next().value(); | |||
} | |||
} | |||
PythonQtConv::global_valueStorage.clear(); | |||
PythonQtConv::global_ptrStorage.clear(); | |||
PythonQtConv::global_variantStorage.clear(); | |||
PythonQtMethodInfo::cleanupCachedMethodInfos(); | |||
} | |||
florianlink
|
r161 | void PythonQt::setRedirectStdInCallback(PythonQtInputChangedCB* callback, void * callbackData) | |
{ | |||
if (!callback) | |||
{ | |||
std::cerr << "PythonQt::setRedirectStdInCallback - callback parameter is NULL !" << std::endl; | |||
return; | |||
} | |||
PythonQtObjectPtr sys; | |||
PythonQtObjectPtr in; | |||
sys.setNewRef(PyImport_ImportModule("sys")); | |||
// Backup original 'sys.stdin' if not yet done | |||
PyRun_SimpleString("if not hasattr(sys, 'pythonqt_original_stdin'):" | |||
"sys.pythonqt_original_stdin = sys.stdin"); | |||
in = PythonQtStdInRedirectType.tp_new(&PythonQtStdInRedirectType, NULL, NULL); | |||
((PythonQtStdInRedirect*)in.object())->_cb = callback; | |||
((PythonQtStdInRedirect*)in.object())->_callData = callbackData; | |||
// replace the built in file objects with our own objects | |||
PyModule_AddObject(sys, "stdin", in); | |||
// Backup custom 'stdin' into 'pythonqt_stdin' | |||
PyRun_SimpleString("sys.pythonqt_stdin = sys.stdin"); | |||
} | |||
void PythonQt::setRedirectStdInCallbackEnabled(bool enabled) | |||
{ | |||
if (enabled) | |||
{ | |||
PyRun_SimpleString("if hasattr(sys, 'pythonqt_stdin'):" | |||
"sys.stdin = sys.pythonqt_stdin"); | |||
} | |||
else | |||
{ | |||
PyRun_SimpleString("if hasattr(sys,'pythonqt_original_stdin'):" | |||
"sys.stdin = sys.pythonqt_original_stdin"); | |||
} | |||
} | |||
ezust
|
r0 | PythonQtImportFileInterface* PythonQt::importInterface() | |
{ | |||
florianlink
|
r8 | return _self->_p->_importInterface?_self->_p->_importInterface:_self->_p->_defaultImporter; | |
ezust
|
r0 | } | |
florianlink
|
r4 | void PythonQt::qObjectNoLongerWrappedCB(QObject* o) | |
{ | |||
if (_self->_p->_noLongerWrappedCB) { | |||
(*_self->_p->_noLongerWrappedCB)(o); | |||
}; | |||
} | |||
florianlink
|
r24 | void PythonQt::registerClass(const QMetaObject* metaobject, const char* package, PythonQtQObjectCreatorFunctionCB* wrapperCreator, PythonQtShellSetInstanceWrapperCB* shell) | |
florianlink
|
r10 | { | |
florianlink
|
r24 | _p->registerClass(metaobject, package, wrapperCreator, shell); | |
florianlink
|
r10 | } | |
florianlink
|
r119 | void PythonQtPrivate::registerClass(const QMetaObject* metaobject, const char* package, PythonQtQObjectCreatorFunctionCB* wrapperCreator, PythonQtShellSetInstanceWrapperCB* shell, PyObject* module, int typeSlots) | |
ezust
|
r0 | { | |
// we register all classes in the hierarchy | |||
const QMetaObject* m = metaobject; | |||
florianlink
|
r10 | bool first = true; | |
ezust
|
r0 | while (m) { | |
florianlink
|
r24 | PythonQtClassInfo* info = lookupClassInfoAndCreateIfNotPresent(m->className()); | |
if (!info->pythonQtClassWrapper()) { | |||
florianlink
|
r119 | info->setTypeSlots(typeSlots); | |
florianlink
|
r24 | info->setupQObject(m); | |
florianlink
|
r109 | createPythonQtClassWrapper(info, package, module); | |
florianlink
|
r24 | if (m->superClass()) { | |
PythonQtClassInfo* parentInfo = lookupClassInfoAndCreateIfNotPresent(m->superClass()->className()); | |||
info->addParentClass(PythonQtClassInfo::ParentClassInfo(parentInfo)); | |||
} | |||
florianlink
|
r157 | } else if (first && module) { | |
// There is a wrapper already, but if we got a module, we want to place the wrapper into that module as well, | |||
// since it might have been placed into "private" earlier on. | |||
// If the wrapper was already added to module before, it is just readded, which does no harm. | |||
PyObject* classWrapper = info->pythonQtClassWrapper(); | |||
// AddObject steals a reference, so we need to INCREF | |||
Py_INCREF(classWrapper); | |||
PyModule_AddObject(module, info->className(), classWrapper); | |||
florianlink
|
r10 | } | |
if (first) { | |||
first = false; | |||
if (wrapperCreator) { | |||
info->setDecoratorProvider(wrapperCreator); | |||
} | |||
florianlink
|
r24 | if (shell) { | |
info->setShellSetInstanceWrapperCB(shell); | |||
} | |||
ezust
|
r0 | } | |
m = m->superClass(); | |||
} | |||
} | |||
florianlink
|
r109 | void PythonQtPrivate::createPythonQtClassWrapper(PythonQtClassInfo* info, const char* package, PyObject* module) | |
florianlink
|
r18 | { | |
florianlink
|
r109 | PyObject* pack = module?module:packageByName(package); | |
PyObject* pyobj = (PyObject*)createNewPythonQtClassWrapper(info, pack); | |||
florianlink
|
r24 | PyModule_AddObject(pack, info->className(), pyobj); | |
florianlink
|
r109 | if (!module && package && strncmp(package,"Qt",2)==0) { | |
florianlink
|
r19 | // since PyModule_AddObject steals the reference, we need a incref once more... | |
Py_INCREF(pyobj); | |||
florianlink
|
r18 | // put all qt objects into Qt as well | |
florianlink
|
r24 | PyModule_AddObject(packageByName("Qt"), info->className(), pyobj); | |
florianlink
|
r18 | } | |
info->setPythonQtClassWrapper(pyobj); | |||
} | |||
ezust
|
r0 | PyObject* PythonQtPrivate::wrapQObject(QObject* obj) | |
{ | |||
if (!obj) { | |||
Py_INCREF(Py_None); | |||
return Py_None; | |||
} | |||
florianlink
|
r16 | PythonQtInstanceWrapper* wrap = findWrapperAndRemoveUnused(obj); | |
florianlink
|
r183 | if (wrap && wrap->_wrappedPtr) { | |
// uh oh, we want to wrap a QObject, but have a C++ wrapper at that | |||
// address, so probably that C++ wrapper has been deleted earlier and | |||
// now we see a QObject with the same address. | |||
// Do not use the old wrapper anymore. | |||
wrap = NULL; | |||
} | |||
ezust
|
r0 | if (!wrap) { | |
// smuggling it in... | |||
florianlink
|
r24 | PythonQtClassInfo* classInfo = _knownClassInfos.value(obj->metaObject()->className()); | |
if (!classInfo || classInfo->pythonQtClassWrapper()==NULL) { | |||
ezust
|
r0 | registerClass(obj->metaObject()); | |
florianlink
|
r24 | classInfo = _knownClassInfos.value(obj->metaObject()->className()); | |
ezust
|
r0 | } | |
florianlink
|
r16 | wrap = createNewPythonQtInstanceWrapper(obj, classInfo); | |
florianlink
|
r18 | // mlabDebugConst("MLABPython","new qobject wrapper added " << " " << wrap->_obj->className() << " " << wrap->classInfo()->wrappedClassName().latin1()); | |
ezust
|
r0 | } else { | |
Py_INCREF(wrap); | |||
florianlink
|
r18 | // mlabDebugConst("MLABPython","qobject wrapper reused " << wrap->_obj->className() << " " << wrap->classInfo()->wrappedClassName().latin1()); | |
ezust
|
r0 | } | |
return (PyObject*)wrap; | |||
} | |||
PyObject* PythonQtPrivate::wrapPtr(void* ptr, const QByteArray& name) | |||
{ | |||
if (!ptr) { | |||
Py_INCREF(Py_None); | |||
return Py_None; | |||
} | |||
florianlink
|
r24 | ||
florianlink
|
r16 | PythonQtInstanceWrapper* wrap = findWrapperAndRemoveUnused(ptr); | |
florianlink
|
r183 | PythonQtInstanceWrapper* possibleStillAliveWrapper = NULL; | |
if (wrap && wrap->_wrappedPtr) { | |||
// we have a previous C++ wrapper... if the wrapper is for a C++ object, | |||
// we are not sure if it may have been deleted earlier and we just see the same C++ | |||
// pointer once again. To make sure that we do not reuse a wrapper of the wrong type, | |||
// we compare the classInfo() pointer and only reuse the wrapper if it has the same | |||
// info. This is only needed for non-QObjects, since we know it when a QObject gets deleted. | |||
possibleStillAliveWrapper = wrap; | |||
wrap = NULL; | |||
} | |||
ezust
|
r0 | if (!wrap) { | |
florianlink
|
r24 | PythonQtClassInfo* info = _knownClassInfos.value(name); | |
ezust
|
r0 | if (!info) { | |
florianlink
|
r24 | // maybe it is a PyObject, which we can return directly | |
if (name == "PyObject") { | |||
PyObject* p = (PyObject*)ptr; | |||
Py_INCREF(p); | |||
return p; | |||
} | |||
florianlink
|
r183 | // we do not know the metaobject yet, but we might know it by its name: | |
ezust
|
r0 | if (_knownQObjectClassNames.find(name)!=_knownQObjectClassNames.end()) { | |
// yes, we know it, so we can convert to QObject | |||
QObject* qptr = (QObject*)ptr; | |||
registerClass(qptr->metaObject()); | |||
florianlink
|
r24 | info = _knownClassInfos.value(qptr->metaObject()->className()); | |
ezust
|
r0 | } | |
} | |||
florianlink
|
r24 | if (info && info->isQObject()) { | |
ezust
|
r0 | QObject* qptr = (QObject*)ptr; | |
// if the object is a derived object, we want to switch the class info to the one of the derived class: | |||
if (name!=(qptr->metaObject()->className())) { | |||
registerClass(qptr->metaObject()); | |||
florianlink
|
r24 | info = _knownClassInfos.value(qptr->metaObject()->className()); | |
ezust
|
r0 | } | |
florianlink
|
r16 | wrap = createNewPythonQtInstanceWrapper(qptr, info); | |
florianlink
|
r18 | // mlabDebugConst("MLABPython","new qobject wrapper added " << " " << wrap->_obj->className() << " " << wrap->classInfo()->wrappedClassName().latin1()); | |
florianlink
|
r24 | return (PyObject*)wrap; | |
} | |||
florianlink
|
r167 | // not a known QObject, try to wrap via foreign wrapper factories | |
PyObject* foreignWrapper = NULL; | |||
for (int i=0; i<_foreignWrapperFactories.size(); i++) { | |||
foreignWrapper = _foreignWrapperFactories.at(i)->wrap(name, ptr); | |||
if (foreignWrapper) { | |||
return foreignWrapper; | |||
} | |||
} | |||
florianlink
|
r24 | // not a known QObject, so try our wrapper factory: | |
QObject* wrapper = NULL; | |||
for (int i=0; i<_cppWrapperFactories.size(); i++) { | |||
wrapper = _cppWrapperFactories.at(i)->create(name, ptr); | |||
if (wrapper) { | |||
break; | |||
ezust
|
r0 | } | |
} | |||
florianlink
|
r26 | ||
if (info) { | |||
// try to downcast in the class hierarchy, which will modify info and ptr if it is successfull | |||
ptr = info->castDownIfPossible(ptr, &info); | |||
florianlink
|
r173 | ||
// if downcasting found out that the object is a QObject, | |||
// handle it like one: | |||
if (info && info->isQObject()) { | |||
QObject* qptr = (QObject*)ptr; | |||
// if the object is a derived object, we want to switch the class info to the one of the derived class: | |||
if (name!=(qptr->metaObject()->className())) { | |||
registerClass(qptr->metaObject()); | |||
info = _knownClassInfos.value(qptr->metaObject()->className()); | |||
} | |||
wrap = createNewPythonQtInstanceWrapper(qptr, info); | |||
// mlabDebugConst("MLABPython","new qobject wrapper added " << " " << wrap->_obj->className() << " " << wrap->classInfo()->wrappedClassName().latin1()); | |||
return (PyObject*)wrap; | |||
} | |||
florianlink
|
r26 | } | |
florianlink
|
r24 | if (!info || info->pythonQtClassWrapper()==NULL) { | |
// still unknown, register as CPP class | |||
registerCPPClass(name.constData()); | |||
info = _knownClassInfos.value(name); | |||
} | |||
if (wrapper && (info->metaObject() != wrapper->metaObject())) { | |||
// if we a have a QObject wrapper and the metaobjects do not match, set the metaobject again! | |||
info->setMetaObject(wrapper->metaObject()); | |||
} | |||
florianlink
|
r161 | ||
florianlink
|
r183 | if (possibleStillAliveWrapper && possibleStillAliveWrapper->classInfo() == info) { | |
wrap = possibleStillAliveWrapper; | |||
Py_INCREF(wrap); | |||
} else { | |||
wrap = createNewPythonQtInstanceWrapper(wrapper, info, ptr); | |||
} | |||
florianlink
|
r24 | // mlabDebugConst("MLABPython","new c++ wrapper added " << wrap->_wrappedPtr << " " << wrap->_obj->className() << " " << wrap->classInfo()->wrappedClassName().latin1()); | |
ezust
|
r0 | } else { | |
Py_INCREF(wrap); | |||
florianlink
|
r18 | //mlabDebugConst("MLABPython","c++ wrapper reused " << wrap->_wrappedPtr << " " << wrap->_obj->className() << " " << wrap->classInfo()->wrappedClassName().latin1()); | |
ezust
|
r0 | } | |
return (PyObject*)wrap; | |||
} | |||
florianlink
|
r18 | PyObject* PythonQtPrivate::dummyTuple() { | |
static PyObject* dummyTuple = NULL; | |||
if (dummyTuple==NULL) { | |||
dummyTuple = PyTuple_New(1); | |||
PyTuple_SET_ITEM(dummyTuple, 0 , PyString_FromString("dummy")); | |||
} | |||
return dummyTuple; | |||
} | |||
ezust
|
r0 | ||
florianlink
|
r16 | PythonQtInstanceWrapper* PythonQtPrivate::createNewPythonQtInstanceWrapper(QObject* obj, PythonQtClassInfo* info, void* wrappedPtr) { | |
florianlink
|
r18 | // call the associated class type to create a new instance... | |
PythonQtInstanceWrapper* result = (PythonQtInstanceWrapper*)PyObject_Call(info->pythonQtClassWrapper(), dummyTuple(), NULL); | |||
ezust
|
r0 | ||
florianlink
|
r4 | result->setQObject(obj); | |
ezust
|
r0 | result->_wrappedPtr = wrappedPtr; | |
result->_ownedByPythonQt = false; | |||
florianlink
|
r10 | result->_useQMetaTypeDestroy = false; | |
florianlink
|
r84 | ||
ezust
|
r0 | if (wrappedPtr) { | |
_wrappedObjects.insert(wrappedPtr, result); | |||
} else { | |||
_wrappedObjects.insert(obj, result); | |||
florianlink
|
r4 | if (obj->parent()== NULL && _wrappedCB) { | |
// tell someone who is interested that the qobject is wrapped the first time, if it has no parent | |||
(*_wrappedCB)(obj); | |||
} | |||
ezust
|
r0 | } | |
return result; | |||
} | |||
florianlink
|
r109 | PythonQtClassWrapper* PythonQtPrivate::createNewPythonQtClassWrapper(PythonQtClassInfo* info, PyObject* parentModule) { | |
florianlink
|
r16 | PythonQtClassWrapper* result; | |
florianlink
|
r18 | ||
PyObject* className = PyString_FromString(info->className()); | |||
PyObject* baseClasses = PyTuple_New(1); | |||
PyTuple_SET_ITEM(baseClasses, 0, (PyObject*)&PythonQtInstanceWrapper_Type); | |||
florianlink
|
r84 | ||
florianlink
|
r18 | PyObject* typeDict = PyDict_New(); | |
florianlink
|
r109 | PyObject* moduleName = PyObject_GetAttrString(parentModule, "__name__"); | |
PyDict_SetItemString(typeDict, "__module__", moduleName); | |||
florianlink
|
r18 | ||
PyObject* args = Py_BuildValue("OOO", className, baseClasses, typeDict); | |||
// set the class info so that PythonQtClassWrapper_new can read it | |||
_currentClassInfoForClassWrapperCreation = info; | |||
// create the new type object by calling the type | |||
result = (PythonQtClassWrapper *)PyObject_Call((PyObject *)&PythonQtClassWrapper_Type, args, NULL); | |||
Py_DECREF(baseClasses); | |||
Py_DECREF(typeDict); | |||
Py_DECREF(args); | |||
Py_DECREF(className); | |||
ezust
|
r0 | return result; | |
} | |||
florianlink
|
r56 | PyObject* PythonQtPrivate::createEnumValueInstance(PyObject* enumType, unsigned int enumValue) | |
{ | |||
PyObject* args = Py_BuildValue("(i)", enumValue); | |||
PyObject* result = PyObject_Call(enumType, args, NULL); | |||
Py_DECREF(args); | |||
return result; | |||
} | |||
florianlink
|
r51 | PyObject* PythonQtPrivate::createNewPythonQtEnumWrapper(const char* enumName, PyObject* parentObject) { | |
PyObject* result; | |||
florianlink
|
r84 | ||
florianlink
|
r51 | PyObject* className = PyString_FromString(enumName); | |
florianlink
|
r84 | ||
florianlink
|
r51 | PyObject* baseClasses = PyTuple_New(1); | |
PyTuple_SET_ITEM(baseClasses, 0, (PyObject*)&PyInt_Type); | |||
florianlink
|
r84 | ||
florianlink
|
r51 | PyObject* module = PyObject_GetAttrString(parentObject, "__module__"); | |
PyObject* typeDict = PyDict_New(); | |||
PyDict_SetItemString(typeDict, "__module__", module); | |||
florianlink
|
r84 | ||
florianlink
|
r51 | PyObject* args = Py_BuildValue("OOO", className, baseClasses, typeDict); | |
florianlink
|
r84 | ||
florianlink
|
r51 | // create the new int derived type object by calling the core type | |
result = PyObject_Call((PyObject *)&PyType_Type, args, NULL); | |||
Py_DECREF(baseClasses); | |||
Py_DECREF(typeDict); | |||
Py_DECREF(args); | |||
Py_DECREF(className); | |||
florianlink
|
r84 | ||
florianlink
|
r51 | return result; | |
} | |||
ezust
|
r0 | ||
PythonQtSignalReceiver* PythonQt::getSignalReceiver(QObject* obj) | |||
{ | |||
PythonQtSignalReceiver* r = _p->_signalReceivers[obj]; | |||
if (!r) { | |||
r = new PythonQtSignalReceiver(obj); | |||
_p->_signalReceivers.insert(obj, r); | |||
} | |||
return r; | |||
} | |||
bool PythonQt::addSignalHandler(QObject* obj, const char* signal, PyObject* module, const QString& objectname) | |||
{ | |||
bool flag = false; | |||
PythonQtObjectPtr callable = lookupCallable(module, objectname); | |||
if (callable) { | |||
PythonQtSignalReceiver* r = getSignalReceiver(obj); | |||
flag = r->addSignalHandler(signal, callable); | |||
if (!flag) { | |||
// signal not found | |||
} | |||
} else { | |||
// callable not found | |||
} | |||
return flag; | |||
} | |||
bool PythonQt::addSignalHandler(QObject* obj, const char* signal, PyObject* receiver) | |||
{ | |||
bool flag = false; | |||
PythonQtSignalReceiver* r = getSignalReceiver(obj); | |||
if (r) { | |||
flag = r->addSignalHandler(signal, receiver); | |||
} | |||
return flag; | |||
} | |||
bool PythonQt::removeSignalHandler(QObject* obj, const char* signal, PyObject* module, const QString& objectname) | |||
{ | |||
bool flag = false; | |||
PythonQtObjectPtr callable = lookupCallable(module, objectname); | |||
if (callable) { | |||
PythonQtSignalReceiver* r = _p->_signalReceivers[obj]; | |||
if (r) { | |||
flag = r->removeSignalHandler(signal, callable); | |||
} | |||
} else { | |||
// callable not found | |||
} | |||
return flag; | |||
} | |||
bool PythonQt::removeSignalHandler(QObject* obj, const char* signal, PyObject* receiver) | |||
{ | |||
bool flag = false; | |||
PythonQtSignalReceiver* r = _p->_signalReceivers[obj]; | |||
if (r) { | |||
flag = r->removeSignalHandler(signal, receiver); | |||
} | |||
return flag; | |||
} | |||
PythonQtObjectPtr PythonQt::lookupCallable(PyObject* module, const QString& name) | |||
{ | |||
PythonQtObjectPtr p = lookupObject(module, name); | |||
if (p) { | |||
if (PyCallable_Check(p)) { | |||
return p; | |||
} | |||
} | |||
PyErr_Clear(); | |||
return NULL; | |||
} | |||
PythonQtObjectPtr PythonQt::lookupObject(PyObject* module, const QString& name) | |||
{ | |||
QStringList l = name.split('.'); | |||
PythonQtObjectPtr p = module; | |||
PythonQtObjectPtr prev; | |||
QByteArray b; | |||
for (QStringList::ConstIterator i = l.begin(); i!=l.end() && p; ++i) { | |||
prev = p; | |||
b = (*i).toLatin1(); | |||
florianlink
|
r25 | if (PyDict_Check(p)) { | |
p = PyDict_GetItemString(p, b.data()); | |||
} else { | |||
p.setNewRef(PyObject_GetAttrString(p, b.data())); | |||
} | |||
ezust
|
r0 | } | |
PyErr_Clear(); | |||
return p; | |||
} | |||
PythonQtObjectPtr PythonQt::getMainModule() { | |||
//both borrowed | |||
PythonQtObjectPtr dict = PyImport_GetModuleDict(); | |||
return PyDict_GetItemString(dict, "__main__"); | |||
} | |||
florianlink
|
r144 | PythonQtObjectPtr PythonQt::importModule(const QString& name) | |
{ | |||
PythonQtObjectPtr mod; | |||
mod.setNewRef(PyImport_ImportModule(name.toLatin1().constData())); | |||
return mod; | |||
} | |||
florianlink
|
r25 | QVariant PythonQt::evalCode(PyObject* object, PyObject* pycode) { | |
ezust
|
r0 | QVariant result; | |
if (pycode) { | |||
florianlink
|
r25 | PyObject* dict = NULL; | |
florianlink
|
r191 | PyObject* globals = NULL; | |
florianlink
|
r25 | if (PyModule_Check(object)) { | |
dict = PyModule_GetDict(object); | |||
florianlink
|
r191 | globals = dict; | |
florianlink
|
r25 | } else if (PyDict_Check(object)) { | |
dict = object; | |||
florianlink
|
r191 | globals = dict; | |
} else { | |||
dict = PyObject_GetAttrString(object, "__dict__"); | |||
globals = PyObject_GetAttrString(PyImport_ImportModule(PyString_AS_STRING(PyObject_GetAttrString(object, "__module__"))),"__dict__"); | |||
florianlink
|
r25 | } | |
PyObject* r = NULL; | |||
if (dict) { | |||
florianlink
|
r191 | r = PyEval_EvalCode((PyCodeObject*)pycode, globals , dict); | |
florianlink
|
r25 | } | |
ezust
|
r0 | if (r) { | |
result = PythonQtConv::PyObjToQVariant(r); | |||
Py_DECREF(r); | |||
} else { | |||
handleError(); | |||
} | |||
} else { | |||
handleError(); | |||
} | |||
return result; | |||
} | |||
florianlink
|
r25 | QVariant PythonQt::evalScript(PyObject* object, const QString& script, int start) | |
ezust
|
r0 | { | |
QVariant result; | |||
PythonQtObjectPtr p; | |||
florianlink
|
r25 | PyObject* dict = NULL; | |
if (PyModule_Check(object)) { | |||
dict = PyModule_GetDict(object); | |||
} else if (PyDict_Check(object)) { | |||
dict = object; | |||
} | |||
if (dict) { | |||
p.setNewRef(PyRun_String(script.toLatin1().data(), start, dict, dict)); | |||
} | |||
ezust
|
r0 | if (p) { | |
result = PythonQtConv::PyObjToQVariant(p); | |||
} else { | |||
handleError(); | |||
} | |||
return result; | |||
} | |||
void PythonQt::evalFile(PyObject* module, const QString& filename) | |||
{ | |||
PythonQtObjectPtr code = parseFile(filename); | |||
if (code) { | |||
evalCode(module, code); | |||
} else { | |||
handleError(); | |||
} | |||
} | |||
PythonQtObjectPtr PythonQt::parseFile(const QString& filename) | |||
{ | |||
PythonQtObjectPtr p; | |||
p.setNewRef(PythonQtImport::getCodeFromPyc(filename)); | |||
if (!p) { | |||
handleError(); | |||
} | |||
return p; | |||
} | |||
florianlink
|
r4 | PythonQtObjectPtr PythonQt::createModuleFromFile(const QString& name, const QString& filename) | |
{ | |||
PythonQtObjectPtr code = parseFile(filename); | |||
PythonQtObjectPtr module = _p->createModule(name, code); | |||
return module; | |||
} | |||
PythonQtObjectPtr PythonQt::createModuleFromScript(const QString& name, const QString& script) | |||
{ | |||
PyErr_Clear(); | |||
QString scriptCode = script; | |||
if (scriptCode.isEmpty()) { | |||
// we always need at least a linefeed | |||
scriptCode = "\n"; | |||
} | |||
PythonQtObjectPtr pycode; | |||
pycode.setNewRef(Py_CompileString((char*)scriptCode.toLatin1().data(), "", Py_file_input)); | |||
PythonQtObjectPtr module = _p->createModule(name, pycode); | |||
return module; | |||
} | |||
PythonQtObjectPtr PythonQt::createUniqueModule() | |||
{ | |||
static QString pyQtStr("PythonQt_module"); | |||
QString moduleName = pyQtStr+QString::number(_uniqueModuleCount++); | |||
return createModuleFromScript(moduleName); | |||
} | |||
florianlink
|
r25 | void PythonQt::addObject(PyObject* object, const QString& name, QObject* qObject) | |
ezust
|
r0 | { | |
florianlink
|
r25 | if (PyModule_Check(object)) { | |
PyModule_AddObject(object, name.toLatin1().data(), _p->wrapQObject(qObject)); | |||
} else if (PyDict_Check(object)) { | |||
PyDict_SetItemString(object, name.toLatin1().data(), _p->wrapQObject(qObject)); | |||
} else { | |||
PyObject_SetAttrString(object, name.toLatin1().data(), _p->wrapQObject(qObject)); | |||
} | |||
ezust
|
r0 | } | |
florianlink
|
r25 | void PythonQt::addVariable(PyObject* object, const QString& name, const QVariant& v) | |
ezust
|
r0 | { | |
florianlink
|
r25 | if (PyModule_Check(object)) { | |
PyModule_AddObject(object, name.toLatin1().data(), PythonQtConv::QVariantToPyObject(v)); | |||
} else if (PyDict_Check(object)) { | |||
PyDict_SetItemString(object, name.toLatin1().data(), PythonQtConv::QVariantToPyObject(v)); | |||
} else { | |||
PyObject_SetAttrString(object, name.toLatin1().data(), PythonQtConv::QVariantToPyObject(v)); | |||
} | |||
ezust
|
r0 | } | |
florianlink
|
r25 | void PythonQt::removeVariable(PyObject* object, const QString& name) | |
ezust
|
r0 | { | |
florianlink
|
r25 | if (PyDict_Check(object)) { | |
PyDict_DelItemString(object, name.toLatin1().data()); | |||
} else { | |||
PyObject_DelAttrString(object, name.toLatin1().data()); | |||
} | |||
ezust
|
r0 | } | |
florianlink
|
r25 | QVariant PythonQt::getVariable(PyObject* object, const QString& objectname) | |
ezust
|
r0 | { | |
QVariant result; | |||
florianlink
|
r25 | PythonQtObjectPtr obj = lookupObject(object, objectname); | |
ezust
|
r0 | if (obj) { | |
result = PythonQtConv::PyObjToQVariant(obj); | |||
} | |||
return result; | |||
} | |||
QStringList PythonQt::introspection(PyObject* module, const QString& objectname, PythonQt::ObjectType type) | |||
{ | |||
QStringList results; | |||
florianlink
|
r84 | ||
ezust
|
r0 | PythonQtObjectPtr object; | |
if (objectname.isEmpty()) { | |||
object = module; | |||
} else { | |||
object = lookupObject(module, objectname); | |||
if (!object && type == CallOverloads) { | |||
PyObject* dict = lookupObject(module, "__builtins__"); | |||
if (dict) { | |||
object = PyDict_GetItemString(dict, objectname.toLatin1().constData()); | |||
} | |||
} | |||
} | |||
if (object) { | |||
florianlink
|
r173 | results = introspectObject(object, type); | |
} | |||
return results; | |||
} | |||
florianlink
|
r84 | ||
florianlink
|
r173 | QStringList PythonQt::introspectObject(PyObject* object, ObjectType type) | |
{ | |||
QStringList results; | |||
if (type == CallOverloads) { | |||
if (PythonQtSlotFunction_Check(object)) { | |||
PythonQtSlotFunctionObject* o = (PythonQtSlotFunctionObject*)object; | |||
PythonQtSlotInfo* info = o->m_ml; | |||
while (info) { | |||
results << info->fullSignature(); | |||
info = info->nextInfo(); | |||
} | |||
} else if (PythonQtSignalFunction_Check(object)) { | |||
PythonQtSignalFunctionObject* o = (PythonQtSignalFunctionObject*)object; | |||
PythonQtSlotInfo* info = o->m_ml; | |||
while (info) { | |||
results << info->fullSignature(); | |||
info = info->nextInfo(); | |||
} | |||
} else if (object->ob_type == &PythonQtClassWrapper_Type) { | |||
PythonQtClassWrapper* o = (PythonQtClassWrapper*)object; | |||
PythonQtSlotInfo* info = o->classInfo()->constructors(); | |||
while (info) { | |||
results << info->fullSignature(); | |||
info = info->nextInfo(); | |||
} | |||
} else { | |||
QString signature = _p->getSignature(object); | |||
if (!signature.isEmpty()) { | |||
results << signature; | |||
ezust
|
r0 | } else { | |
PyObject* doc = PyObject_GetAttrString(object, "__doc__"); | |||
if (doc) { | |||
results << PyString_AsString(doc); | |||
Py_DECREF(doc); | |||
} | |||
} | |||
florianlink
|
r173 | } | |
} else { | |||
PyObject* keys = NULL; | |||
bool isDict = false; | |||
if (PyDict_Check(object)) { | |||
keys = PyDict_Keys(object); | |||
isDict = true; | |||
ezust
|
r0 | } else { | |
florianlink
|
r173 | keys = PyObject_Dir(object); | |
} | |||
if (keys) { | |||
int count = PyList_Size(keys); | |||
PyObject* key; | |||
PyObject* value; | |||
QString keystr; | |||
for (int i = 0;i<count;i++) { | |||
key = PyList_GetItem(keys,i); | |||
if (isDict) { | |||
value = PyDict_GetItem(object, key); | |||
Py_INCREF(value); | |||
} else { | |||
value = PyObject_GetAttr(object, key); | |||
} | |||
if (!value) continue; | |||
keystr = PyString_AsString(key); | |||
static const QString underscoreStr("__tmp"); | |||
if (!keystr.startsWith(underscoreStr)) { | |||
switch (type) { | |||
case Anything: | |||
results << keystr; | |||
break; | |||
case Class: | |||
if (value->ob_type == &PyClass_Type || value->ob_type == &PyType_Type) { | |||
results << keystr; | |||
} | |||
break; | |||
case Variable: | |||
if (value->ob_type != &PyClass_Type | |||
&& value->ob_type != &PyCFunction_Type | |||
&& value->ob_type != &PyFunction_Type | |||
&& value->ob_type != &PyMethod_Type | |||
&& value->ob_type != &PyModule_Type | |||
&& value->ob_type != &PyType_Type | |||
&& value->ob_type != &PythonQtSlotFunction_Type | |||
) { | |||
results << keystr; | |||
} | |||
break; | |||
case Function: | |||
if (value->ob_type == &PyCFunction_Type || | |||
value->ob_type == &PyFunction_Type || | |||
value->ob_type == &PyMethod_Type || | |||
value->ob_type == &PythonQtSlotFunction_Type | |||
) { | |||
ezust
|
r0 | results << keystr; | |
} | |||
florianlink
|
r173 | break; | |
case Module: | |||
if (value->ob_type == &PyModule_Type) { | |||
results << keystr; | |||
} | |||
break; | |||
default: | |||
std::cerr << "PythonQt: introspection: unknown case" << ", in " << __FILE__ << ":" << __LINE__ << std::endl; | |||
ezust
|
r0 | } | |
} | |||
florianlink
|
r173 | Py_DECREF(value); | |
ezust
|
r0 | } | |
florianlink
|
r173 | Py_DECREF(keys); | |
} | |||
florianlink
|
r180 | if ((type == Anything) || (type == Variable)) { | |
if (object->ob_type == &PythonQtClassWrapper_Type) { | |||
PythonQtClassWrapper* o = (PythonQtClassWrapper*)object; | |||
PythonQtClassInfo* info = o->classInfo(); | |||
results += info->propertyList(); | |||
} | |||
} | |||
florianlink
|
r173 | } | |
return results; | |||
} | |||
PyObject* PythonQt::getObjectByType(const QString& typeName) | |||
{ | |||
PythonQtObjectPtr sys; | |||
sys.setNewRef(PyImport_ImportModule("sys")); | |||
PythonQtObjectPtr modules = lookupObject(sys, "modules"); | |||
Q_ASSERT(PyDict_Check(modules)); | |||
QStringList tmp = typeName.split("."); | |||
QString simpleTypeName = tmp.takeLast(); | |||
QString moduleName = tmp.join("."); | |||
PyObject* object = NULL; | |||
PyObject* moduleObject = PyDict_GetItemString(modules, moduleName.toLatin1().constData()); | |||
if (moduleObject) { | |||
object = PyObject_GetAttrString(moduleObject, simpleTypeName.toLatin1().constData()); | |||
} | |||
if (!object) { | |||
moduleObject = PyDict_GetItemString(modules, "__builtin__"); | |||
if (moduleObject) { | |||
object = PyObject_GetAttrString(moduleObject, simpleTypeName.toLatin1().constData()); | |||
} | |||
} | |||
return object; | |||
} | |||
QStringList PythonQt::introspectType(const QString& typeName, ObjectType type) | |||
{ | |||
QStringList results; | |||
PyObject* object = getObjectByType(typeName); | |||
if (!object) { | |||
// the last item may be a member, split it away and try again | |||
QStringList tmp = typeName.split("."); | |||
QString memberName = tmp.takeLast(); | |||
florianlink
|
r180 | QString typeName; | |
if (tmp.isEmpty()) { | |||
typeName = memberName; | |||
memberName.clear(); | |||
} else { | |||
typeName = tmp.takeLast(); | |||
} | |||
florianlink
|
r173 | PyObject* typeObject = getObjectByType(typeName); | |
if (typeObject) { | |||
object = PyObject_GetAttrString(typeObject, memberName.toLatin1().constData()); | |||
ezust
|
r0 | } | |
} | |||
florianlink
|
r173 | if (object) { | |
results = introspectObject(object, type); | |||
Py_DECREF(object); | |||
} | |||
ezust
|
r0 | return results; | |
} | |||
florianlink
|
r189 | QVariant PythonQt::call(PyObject* object, const QString& name, const QVariantList& args, const QVariantMap& kwargs) | |
florianlink
|
r36 | { | |
PythonQtObjectPtr callable = lookupCallable(object, name); | |||
if (callable) { | |||
florianlink
|
r189 | return call(callable, args, kwargs); | |
florianlink
|
r36 | } else { | |
return QVariant(); | |||
} | |||
} | |||
florianlink
|
r189 | QVariant PythonQt::call(PyObject* callable, const QVariantList& args, const QVariantMap& kwargs) | |
ezust
|
r0 | { | |
QVariant r; | |||
florianlink
|
r82 | PythonQtObjectPtr result; | |
florianlink
|
r189 | result.setNewRef(callAndReturnPyObject(callable, args, kwargs)); | |
florianlink
|
r82 | if (result) { | |
r = PythonQtConv::PyObjToQVariant(result); | |||
} else { | |||
PythonQt::self()->handleError(); | |||
} | |||
return r; | |||
} | |||
florianlink
|
r189 | PyObject* PythonQt::callAndReturnPyObject(PyObject* callable, const QVariantList& args, const QVariantMap& kwargs) | |
florianlink
|
r82 | { | |
PyObject* result = NULL; | |||
ezust
|
r0 | if (callable) { | |
florianlink
|
r189 | bool err = false; | |
ezust
|
r0 | PythonQtObjectPtr pargs; | |
int count = args.size(); | |||
florianlink
|
r189 | if ((count > 0) || (kwargs.count() > 0)) { // create empty tuple if kwargs are given | |
ezust
|
r0 | pargs.setNewRef(PyTuple_New(count)); | |
florianlink
|
r189 | ||
// transform QVariant arguments to Python | |||
for (int i = 0; i < count; i++) { | |||
PyObject* arg = PythonQtConv::QVariantToPyObject(args.at(i)); | |||
if (arg) { | |||
// steals reference, no unref | |||
PyTuple_SetItem(pargs, i,arg); | |||
} else { | |||
err = true; | |||
break; | |||
} | |||
ezust
|
r0 | } | |
} | |||
if (!err) { | |||
florianlink
|
r189 | if (kwargs.isEmpty()) { | |
// do a direct call if we have no keyword arguments | |||
PyErr_Clear(); | |||
result = PyObject_CallObject(callable, pargs); | |||
} else { | |||
// convert keyword arguments to Python | |||
PythonQtObjectPtr pkwargs; | |||
pkwargs.setNewRef(PyDict_New()); | |||
QMapIterator<QString, QVariant> it(kwargs); | |||
while (it.hasNext()) { | |||
it.next(); | |||
PyObject* arg = PythonQtConv::QVariantToPyObject(it.value()); | |||
if (arg) { | |||
PyDict_SetItemString(pkwargs, it.key().toLatin1().constData(), arg); | |||
} else { | |||
err = true; | |||
break; | |||
} | |||
} | |||
if (!err) { | |||
// call with arguments and keyword arguments | |||
PyErr_Clear(); | |||
result = PyObject_Call(callable, pargs, pkwargs); | |||
} | |||
} | |||
ezust
|
r0 | } | |
} | |||
florianlink
|
r82 | return result; | |
ezust
|
r0 | } | |
void PythonQt::addInstanceDecorators(QObject* o) | |||
{ | |||
florianlink
|
r10 | _p->addDecorators(o, PythonQtPrivate::InstanceDecorator); | |
ezust
|
r0 | } | |
void PythonQt::addClassDecorators(QObject* o) | |||
{ | |||
florianlink
|
r10 | _p->addDecorators(o, PythonQtPrivate::StaticDecorator | PythonQtPrivate::ConstructorDecorator | PythonQtPrivate::DestructorDecorator); | |
ezust
|
r0 | } | |
void PythonQt::addDecorators(QObject* o) | |||
{ | |||
florianlink
|
r10 | _p->addDecorators(o, PythonQtPrivate::AllDecorators); | |
ezust
|
r0 | } | |
void PythonQt::registerQObjectClassNames(const QStringList& names) | |||
{ | |||
_p->registerQObjectClassNames(names); | |||
} | |||
void PythonQt::setImporter(PythonQtImportFileInterface* importInterface) | |||
{ | |||
florianlink
|
r8 | _p->_importInterface = importInterface; | |
florianlink
|
r84 | PythonQtImport::init(); | |
ezust
|
r0 | } | |
void PythonQt::setImporterIgnorePaths(const QStringList& paths) | |||
{ | |||
_p->_importIgnorePaths = paths; | |||
} | |||
const QStringList& PythonQt::getImporterIgnorePaths() | |||
{ | |||
return _p->_importIgnorePaths; | |||
} | |||
void PythonQt::addWrapperFactory(PythonQtCppWrapperFactory* factory) | |||
{ | |||
_p->_cppWrapperFactories.append(factory); | |||
} | |||
florianlink
|
r167 | void PythonQt::addWrapperFactory( PythonQtForeignWrapperFactory* factory ) | |
{ | |||
_p->_foreignWrapperFactories.append(factory); | |||
} | |||
ezust
|
r0 | //--------------------------------------------------------------------------------------------------- | |
PythonQtPrivate::PythonQtPrivate() | |||
{ | |||
_importInterface = NULL; | |||
florianlink
|
r8 | _defaultImporter = new PythonQtQFileImporter; | |
florianlink
|
r4 | _noLongerWrappedCB = NULL; | |
_wrappedCB = NULL; | |||
florianlink
|
r18 | _currentClassInfoForClassWrapperCreation = NULL; | |
florianlink
|
r157 | _profilingCB = NULL; | |
florianlink
|
r18 | } | |
florianlink
|
r82 | void PythonQtPrivate::setupSharedLibrarySuffixes() | |
{ | |||
_sharedLibrarySuffixes.clear(); | |||
PythonQtObjectPtr imp; | |||
imp.setNewRef(PyImport_ImportModule("imp")); | |||
int cExtensionCode = imp.getVariable("C_EXTENSION").toInt(); | |||
QVariant result = imp.call("get_suffixes"); | |||
florianlink
|
r157 | #ifdef __linux | |
#ifdef _DEBUG | |||
// First look for shared libraries with the '_d' suffix in debug mode on Linux. | |||
// This is a workaround, because python does not append the '_d' suffix on Linux | |||
// and would always load the release library otherwise. | |||
_sharedLibrarySuffixes << "_d.so"; | |||
#endif | |||
#endif | |||
florianlink
|
r82 | foreach (QVariant entry, result.toList()) { | |
QVariantList suffixEntry = entry.toList(); | |||
if (suffixEntry.count()==3) { | |||
int code = suffixEntry.at(2).toInt(); | |||
if (code == cExtensionCode) { | |||
_sharedLibrarySuffixes << suffixEntry.at(0).toString(); | |||
} | |||
} | |||
} | |||
} | |||
florianlink
|
r18 | PythonQtClassInfo* PythonQtPrivate::currentClassInfoForClassWrapperCreation() | |
{ | |||
PythonQtClassInfo* info = _currentClassInfoForClassWrapperCreation; | |||
_currentClassInfoForClassWrapperCreation = NULL; | |||
return info; | |||
ezust
|
r0 | } | |
florianlink
|
r10 | void PythonQtPrivate::addDecorators(QObject* o, int decoTypes) | |
ezust
|
r0 | { | |
o->setParent(this); | |||
int numMethods = o->metaObject()->methodCount(); | |||
for (int i = 0; i < numMethods; i++) { | |||
QMetaMethod m = o->metaObject()->method(i); | |||
if ((m.methodType() == QMetaMethod::Method || | |||
m.methodType() == QMetaMethod::Slot) && m.access() == QMetaMethod::Public) { | |||
if (qstrncmp(m.signature(), "new_", 4)==0) { | |||
florianlink
|
r84 | if ((decoTypes & ConstructorDecorator) == 0) continue; | |
florianlink
|
r55 | const PythonQtMethodInfo* info = PythonQtMethodInfo::getCachedMethodInfo(m, NULL); | |
florianlink
|
r134 | if (info->parameters().at(0).pointerCount == 1) { | |
ezust
|
r0 | QByteArray signature = m.signature(); | |
QByteArray nameOfClass = signature.mid(4, signature.indexOf('(')-4); | |||
florianlink
|
r24 | PythonQtClassInfo* classInfo = lookupClassInfoAndCreateIfNotPresent(nameOfClass); | |
florianlink
|
r54 | PythonQtSlotInfo* newSlot = new PythonQtSlotInfo(NULL, m, i, o, PythonQtSlotInfo::ClassDecorator); | |
florianlink
|
r24 | classInfo->addConstructor(newSlot); | |
ezust
|
r0 | } | |
} else if (qstrncmp(m.signature(), "delete_", 7)==0) { | |||
florianlink
|
r84 | if ((decoTypes & DestructorDecorator) == 0) continue; | |
ezust
|
r0 | QByteArray signature = m.signature(); | |
QByteArray nameOfClass = signature.mid(7, signature.indexOf('(')-7); | |||
florianlink
|
r24 | PythonQtClassInfo* classInfo = lookupClassInfoAndCreateIfNotPresent(nameOfClass); | |
florianlink
|
r54 | PythonQtSlotInfo* newSlot = new PythonQtSlotInfo(NULL, m, i, o, PythonQtSlotInfo::ClassDecorator); | |
florianlink
|
r24 | classInfo->setDestructor(newSlot); | |
ezust
|
r0 | } else if (qstrncmp(m.signature(), "static_", 7)==0) { | |
florianlink
|
r84 | if ((decoTypes & StaticDecorator) == 0) continue; | |
ezust
|
r0 | QByteArray signature = m.signature(); | |
florianlink
|
r193 | QByteArray nameOfClass = signature.mid(7); | |
ezust
|
r0 | nameOfClass = nameOfClass.mid(0, nameOfClass.indexOf('_')); | |
florianlink
|
r24 | PythonQtClassInfo* classInfo = lookupClassInfoAndCreateIfNotPresent(nameOfClass); | |
florianlink
|
r54 | PythonQtSlotInfo* newSlot = new PythonQtSlotInfo(NULL, m, i, o, PythonQtSlotInfo::ClassDecorator); | |
florianlink
|
r24 | classInfo->addDecoratorSlot(newSlot); | |
ezust
|
r0 | } else { | |
florianlink
|
r84 | if ((decoTypes & InstanceDecorator) == 0) continue; | |
florianlink
|
r55 | const PythonQtMethodInfo* info = PythonQtMethodInfo::getCachedMethodInfo(m, NULL); | |
ezust
|
r0 | if (info->parameters().count()>1) { | |
PythonQtMethodInfo::ParameterInfo p = info->parameters().at(1); | |||
florianlink
|
r134 | if (p.pointerCount==1) { | |
florianlink
|
r24 | PythonQtClassInfo* classInfo = lookupClassInfoAndCreateIfNotPresent(p.name); | |
florianlink
|
r54 | PythonQtSlotInfo* newSlot = new PythonQtSlotInfo(NULL, m, i, o, PythonQtSlotInfo::InstanceDecorator); | |
florianlink
|
r24 | classInfo->addDecoratorSlot(newSlot); | |
ezust
|
r0 | } | |
} | |||
} | |||
} | |||
} | |||
} | |||
void PythonQtPrivate::registerQObjectClassNames(const QStringList& names) | |||
{ | |||
foreach(QString name, names) { | |||
_knownQObjectClassNames.insert(name.toLatin1(), true); | |||
} | |||
} | |||
florianlink
|
r4 | void PythonQtPrivate::removeSignalEmitter(QObject* obj) | |
ezust
|
r0 | { | |
florianlink
|
r4 | _signalReceivers.remove(obj); | |
ezust
|
r0 | } | |
bool PythonQt::handleError() | |||
{ | |||
bool flag = false; | |||
if (PyErr_Occurred()) { | |||
florianlink
|
r84 | ||
ezust
|
r0 | // currently we just print the error and the stderr handler parses the errors | |
PyErr_Print(); | |||
florianlink
|
r84 | ||
ezust
|
r0 | /* | |
// EXTRA: the format of the ptype and ptraceback is not really documented, so I use PyErr_Print() above | |||
PyObject *ptype; | |||
PyObject *pvalue; | |||
PyObject *ptraceback; | |||
PyErr_Fetch( &ptype, &pvalue, &ptraceback); | |||
florianlink
|
r84 | ||
ezust
|
r0 | Py_XDECREF(ptype); | |
Py_XDECREF(pvalue); | |||
Py_XDECREF(ptraceback); | |||
*/ | |||
PyErr_Clear(); | |||
flag = true; | |||
} | |||
return flag; | |||
} | |||
florianlink
|
r14 | void PythonQt::addSysPath(const QString& path) | |
{ | |||
PythonQtObjectPtr sys; | |||
sys.setNewRef(PyImport_ImportModule("sys")); | |||
PythonQtObjectPtr obj = lookupObject(sys, "path"); | |||
florianlink
|
r84 | PyList_Insert(obj, 0, PythonQtConv::QStringToPyObject(path)); | |
florianlink
|
r14 | } | |
ezust
|
r0 | void PythonQt::overwriteSysPath(const QStringList& paths) | |
{ | |||
PythonQtObjectPtr sys; | |||
sys.setNewRef(PyImport_ImportModule("sys")); | |||
PyModule_AddObject(sys, "path", PythonQtConv::QStringListToPyList(paths)); | |||
} | |||
void PythonQt::setModuleImportPath(PyObject* module, const QStringList& paths) | |||
{ | |||
PyModule_AddObject(module, "__path__", PythonQtConv::QStringListToPyList(paths)); | |||
} | |||
void PythonQt::stdOutRedirectCB(const QString& str) | |||
{ | |||
florianlink
|
r161 | if (!PythonQt::self()) { | |
std::cout << str.toLatin1().data() << std::endl; | |||
return; | |||
} | |||
ezust
|
r0 | emit PythonQt::self()->pythonStdOut(str); | |
} | |||
void PythonQt::stdErrRedirectCB(const QString& str) | |||
{ | |||
florianlink
|
r161 | if (!PythonQt::self()) { | |
std::cerr << str.toLatin1().data() << std::endl; | |||
return; | |||
} | |||
ezust
|
r0 | emit PythonQt::self()->pythonStdErr(str); | |
} | |||
florianlink
|
r4 | void PythonQt::setQObjectWrappedCallback(PythonQtQObjectWrappedCB* cb) | |
{ | |||
_p->_wrappedCB = cb; | |||
} | |||
void PythonQt::setQObjectNoLongerWrappedCallback(PythonQtQObjectNoLongerWrappedCB* cb) | |||
{ | |||
_p->_noLongerWrappedCB = cb; | |||
} | |||
florianlink
|
r157 | void PythonQt::setProfilingCallback(ProfilingCB* cb) | |
{ | |||
_p->_profilingCB = cb; | |||
} | |||
ezust
|
r0 | ||
static PyMethodDef PythonQtMethods[] = { | |||
{NULL, NULL, 0, NULL} | |||
}; | |||
florianlink
|
r109 | void PythonQt::initPythonQtModule(bool redirectStdOut, const QByteArray& pythonQtModuleName) | |
ezust
|
r0 | { | |
florianlink
|
r109 | QByteArray name = "PythonQt"; | |
if (!pythonQtModuleName.isEmpty()) { | |||
name = pythonQtModuleName; | |||
} | |||
_p->_pythonQtModule = Py_InitModule(name.constData(), PythonQtMethods); | |||
_p->_pythonQtModuleName = name; | |||
florianlink
|
r157 | ||
ezust
|
r0 | if (redirectStdOut) { | |
PythonQtObjectPtr sys; | |||
PythonQtObjectPtr out; | |||
PythonQtObjectPtr err; | |||
sys.setNewRef(PyImport_ImportModule("sys")); | |||
// create a redirection object for stdout and stderr | |||
out = PythonQtStdOutRedirectType.tp_new(&PythonQtStdOutRedirectType,NULL, NULL); | |||
((PythonQtStdOutRedirect*)out.object())->_cb = stdOutRedirectCB; | |||
err = PythonQtStdOutRedirectType.tp_new(&PythonQtStdOutRedirectType,NULL, NULL); | |||
((PythonQtStdOutRedirect*)err.object())->_cb = stdErrRedirectCB; | |||
// replace the built in file objects with our own objects | |||
PyModule_AddObject(sys, "stdout", out); | |||
PyModule_AddObject(sys, "stderr", err); | |||
} | |||
} | |||
florianlink
|
r173 | QString PythonQt::getReturnTypeOfWrappedMethod(PyObject* module, const QString& name) | |
{ | |||
QStringList tmp = name.split("."); | |||
QString methodName = tmp.takeLast(); | |||
QString variableName = tmp.join("."); | |||
// TODO: the variableName may be a type name, this needs to be handled differently, | |||
// because it is not necessarily known in the module context | |||
PythonQtObjectPtr variableObject = lookupObject(module, variableName); | |||
if (variableObject.isNull()) { | |||
return ""; | |||
} | |||
return getReturnTypeOfWrappedMethodHelper(variableObject, methodName, name); | |||
} | |||
QString PythonQt::getReturnTypeOfWrappedMethod(const QString& typeName, const QString& methodName) | |||
{ | |||
PythonQtObjectPtr typeObject = getObjectByType(typeName); | |||
if (typeObject.isNull()) { | |||
return ""; | |||
} | |||
return getReturnTypeOfWrappedMethodHelper(typeObject, methodName, typeName + "." + methodName); | |||
} | |||
QString PythonQt::getReturnTypeOfWrappedMethodHelper(const PythonQtObjectPtr& variableObject, const QString& methodName, const QString& context) | |||
{ | |||
PythonQtObjectPtr methodObject; | |||
if (PyDict_Check(variableObject)) { | |||
methodObject = PyDict_GetItemString(variableObject, methodName.toLatin1().constData()); | |||
} else { | |||
methodObject.setNewRef(PyObject_GetAttrString(variableObject, methodName.toLatin1().constData())); | |||
} | |||
if (methodObject.isNull()) { | |||
return ""; | |||
} | |||
QString type; | |||
if (methodObject->ob_type == &PyClass_Type || methodObject->ob_type == &PyType_Type) { | |||
// the methodObject is not a method, but the name of a type/class. This means | |||
// a constructor is called. Return the context. | |||
type = context; | |||
} else if (methodObject->ob_type == &PythonQtSlotFunction_Type) { | |||
QString className; | |||
if (PyObject_TypeCheck(variableObject, &PythonQtInstanceWrapper_Type)) { | |||
// the type name of wrapped instance is the class name | |||
className = variableObject->ob_type->tp_name; | |||
} else { | |||
PyObject* classNameObject = PyObject_GetAttrString(variableObject, "__name__"); | |||
if (classNameObject) { | |||
Q_ASSERT(PyString_Check(classNameObject)); | |||
className = PyString_AsString(classNameObject); | |||
Py_DECREF(classNameObject); | |||
} | |||
} | |||
if (!className.isEmpty()) { | |||
PythonQtClassInfo* info = _p->_knownClassInfos.value(className.toLatin1().constData()); | |||
if (info) { | |||
PythonQtSlotInfo* slotInfo = info->member(methodName.toLatin1().constData())._slot; | |||
if (slotInfo) { | |||
if (slotInfo->metaMethod()) { | |||
type = slotInfo->metaMethod()->typeName(); | |||
if (!type.isEmpty()) { | |||
QChar c = type.at(type.length()-1); | |||
while (c == '*' || c == '&') { | |||
type.truncate(type.length()-1); | |||
if (!type.isEmpty()) { | |||
c = type.at(type.length()-1); | |||
} else { | |||
break; | |||
} | |||
} | |||
// split away template arguments | |||
type = type.split("<").first(); | |||
// split away const | |||
type = type.split(" ").last().trimmed(); | |||
// if the type is a known class info, then create the full type name, i.e. include the | |||
// module name. For example, the slot may return a QDate, then this looks up the | |||
// name _PythonQt.QtCore.QDate. | |||
PythonQtClassInfo* typeInfo = _p->_knownClassInfos.value(type.toLatin1().constData()); | |||
if (typeInfo && typeInfo->pythonQtClassWrapper()) { | |||
PyObject* s = PyObject_GetAttrString(typeInfo->pythonQtClassWrapper(), "__module__"); | |||
Q_ASSERT(PyString_Check(s)); | |||
type = QString(PyString_AsString(s)) + "." + type; | |||
Py_DECREF(s); | |||
s = PyObject_GetAttrString(typeInfo->pythonQtClassWrapper(), "__name__"); | |||
Q_ASSERT(PyString_Check(s)); | |||
Py_DECREF(s); | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} | |||
return type; | |||
} | |||
florianlink
|
r24 | void PythonQt::registerCPPClass(const char* typeName, const char* parentTypeName, const char* package, PythonQtQObjectCreatorFunctionCB* wrapperCreator, PythonQtShellSetInstanceWrapperCB* shell) | |
ezust
|
r0 | { | |
florianlink
|
r24 | _p->registerCPPClass(typeName, parentTypeName, package, wrapperCreator, shell); | |
ezust
|
r0 | } | |
florianlink
|
r24 | PythonQtClassInfo* PythonQtPrivate::lookupClassInfoAndCreateIfNotPresent(const char* typeName) | |
ezust
|
r0 | { | |
florianlink
|
r24 | PythonQtClassInfo* info = _knownClassInfos.value(typeName); | |
florianlink
|
r10 | if (!info) { | |
florianlink
|
r24 | info = new PythonQtClassInfo(); | |
info->setupCPPObject(typeName); | |||
_knownClassInfos.insert(typeName, info); | |||
florianlink
|
r10 | } | |
florianlink
|
r24 | return info; | |
} | |||
florianlink
|
r26 | void PythonQt::addPolymorphicHandler(const char* typeName, PythonQtPolymorphicHandlerCB* cb) | |
{ | |||
_p->addPolymorphicHandler(typeName, cb); | |||
} | |||
void PythonQtPrivate::addPolymorphicHandler(const char* typeName, PythonQtPolymorphicHandlerCB* cb) | |||
{ | |||
PythonQtClassInfo* info = lookupClassInfoAndCreateIfNotPresent(typeName); | |||
info->addPolymorphicHandler(cb); | |||
} | |||
florianlink
|
r24 | bool PythonQt::addParentClass(const char* typeName, const char* parentTypeName, int upcastingOffset) | |
{ | |||
return _p->addParentClass(typeName, parentTypeName, upcastingOffset); | |||
} | |||
bool PythonQtPrivate::addParentClass(const char* typeName, const char* parentTypeName, int upcastingOffset) | |||
{ | |||
PythonQtClassInfo* info = _knownClassInfos.value(typeName); | |||
if (info) { | |||
PythonQtClassInfo* parentInfo = lookupClassInfoAndCreateIfNotPresent(parentTypeName); | |||
info->addParentClass(PythonQtClassInfo::ParentClassInfo(parentInfo, upcastingOffset)); | |||
return true; | |||
} else { | |||
return false; | |||
} | |||
} | |||
florianlink
|
r119 | void PythonQtPrivate::registerCPPClass(const char* typeName, const char* parentTypeName, const char* package, PythonQtQObjectCreatorFunctionCB* wrapperCreator, PythonQtShellSetInstanceWrapperCB* shell, PyObject* module, int typeSlots) | |
florianlink
|
r24 | { | |
PythonQtClassInfo* info = lookupClassInfoAndCreateIfNotPresent(typeName); | |||
if (!info->pythonQtClassWrapper()) { | |||
florianlink
|
r119 | info->setTypeSlots(typeSlots); | |
florianlink
|
r24 | info->setupCPPObject(typeName); | |
florianlink
|
r109 | createPythonQtClassWrapper(info, package, module); | |
florianlink
|
r24 | } | |
if (parentTypeName && strcmp(parentTypeName,"")!=0) { | |||
addParentClass(typeName, parentTypeName, 0); | |||
florianlink
|
r10 | } | |
if (wrapperCreator) { | |||
info->setDecoratorProvider(wrapperCreator); | |||
} | |||
florianlink
|
r24 | if (shell) { | |
info->setShellSetInstanceWrapperCB(shell); | |||
} | |||
ezust
|
r0 | } | |
florianlink
|
r19 | PyObject* PythonQtPrivate::packageByName(const char* name) | |
florianlink
|
r10 | { | |
if (name==NULL || name[0]==0) { | |||
florianlink
|
r101 | name = "private"; | |
florianlink
|
r10 | } | |
florianlink
|
r19 | PyObject* v = _packages.value(name); | |
florianlink
|
r10 | if (!v) { | |
florianlink
|
r109 | v = PyImport_AddModule((_pythonQtModuleName + "." + name).constData()); | |
florianlink
|
r10 | _packages.insert(name, v); | |
florianlink
|
r19 | // AddObject steals the reference, so increment it! | |
Py_INCREF(v); | |||
florianlink
|
r10 | PyModule_AddObject(_pythonQtModule, name, v); | |
} | |||
return v; | |||
} | |||
florianlink
|
r67 | void PythonQtPrivate::handleVirtualOverloadReturnError(const char* signature, const PythonQtMethodInfo* methodInfo, PyObject* result) | |
{ | |||
QString error = "Return value '" + PythonQtConv::PyObjGetString(result) + "' can not be converted to expected C++ type '" + methodInfo->parameters().at(0).name + "' as return value of virtual method " + signature; | |||
PyErr_SetString(PyExc_AttributeError, error.toLatin1().data()); | |||
PythonQt::self()->handleError(); | |||
} | |||
florianlink
|
r10 | ||
ezust
|
r0 | PyObject* PythonQt::helpCalled(PythonQtClassInfo* info) | |
florianlink
|
r84 | { | |
ezust
|
r0 | if (_p->_initFlags & ExternalHelp) { | |
emit pythonHelpRequest(QByteArray(info->className())); | |||
return Py_BuildValue(""); | |||
} else { | |||
return PyString_FromString(info->help().toLatin1().data()); | |||
} | |||
} | |||
florianlink
|
r4 | ||
florianlink
|
r157 | void PythonQt::clearNotFoundCachedMembers() | |
{ | |||
foreach(PythonQtClassInfo* info, _p->_knownClassInfos) { | |||
info->clearNotFoundCachedMembers(); | |||
} | |||
} | |||
florianlink
|
r168 | void PythonQt::removeWrapperFactory( PythonQtCppWrapperFactory* factory ) | |
{ | |||
_p->_cppWrapperFactories.removeAll(factory); | |||
} | |||
void PythonQt::removeWrapperFactory( PythonQtForeignWrapperFactory* factory ) | |||
{ | |||
_p->_foreignWrapperFactories.removeAll(factory); | |||
} | |||
florianlink
|
r4 | void PythonQtPrivate::removeWrapperPointer(void* obj) | |
{ | |||
_wrappedObjects.remove(obj); | |||
} | |||
florianlink
|
r18 | void PythonQtPrivate::addWrapperPointer(void* obj, PythonQtInstanceWrapper* wrapper) | |
{ | |||
_wrappedObjects.insert(obj, wrapper); | |||
} | |||
florianlink
|
r16 | PythonQtInstanceWrapper* PythonQtPrivate::findWrapperAndRemoveUnused(void* obj) | |
florianlink
|
r4 | { | |
florianlink
|
r16 | PythonQtInstanceWrapper* wrap = _wrappedObjects.value(obj); | |
florianlink
|
r4 | if (wrap && !wrap->_wrappedPtr && wrap->_obj == NULL) { | |
// this is a wrapper whose QObject was already removed due to destruction | |||
// so the obj pointer has to be a new QObject with the same address... | |||
// we remove the old one and set the copy to NULL | |||
wrap->_objPointerCopy = NULL; | |||
removeWrapperPointer(obj); | |||
wrap = NULL; | |||
} | |||
return wrap; | |||
} | |||
PythonQtObjectPtr PythonQtPrivate::createModule(const QString& name, PyObject* pycode) | |||
{ | |||
PythonQtObjectPtr result; | |||
if (pycode) { | |||
result.setNewRef(PyImport_ExecCodeModule((char*)name.toLatin1().data(), pycode)); | |||
} else { | |||
PythonQt::self()->handleError(); | |||
} | |||
return result; | |||
} | |||
florianlink
|
r167 | ||
void* PythonQtPrivate::unwrapForeignWrapper( const QByteArray& classname, PyObject* obj ) | |||
{ | |||
void* foreignObject = NULL; | |||
for (int i=0; i<_foreignWrapperFactories.size(); i++) { | |||
foreignObject = _foreignWrapperFactories.at(i)->unwrap(classname, obj); | |||
if (foreignObject) { | |||
return foreignObject; | |||
} | |||
} | |||
return NULL; | |||
florianlink
|
r173 | } | |
bool PythonQtPrivate::isMethodDescriptor(PyObject* object) const | |||
{ | |||
// This implementation is the same as in inspect.ismethoddescriptor(), inspect.py. | |||
if (PyObject_HasAttrString(object, "__get__") && | |||
!PyObject_HasAttrString(object, "__set__") && | |||
!PyMethod_Check(object) && | |||
!PyFunction_Check(object) && | |||
!PyClass_Check(object)) { | |||
return true; | |||
} | |||
return false; | |||
} | |||
QString PythonQtPrivate::getSignature(PyObject* object) | |||
{ | |||
QString signature; | |||
if (object) { | |||
PyMethodObject* method = NULL; | |||
PyFunctionObject* func = NULL; | |||
bool decrefMethod = false; | |||
if (object->ob_type == &PyClass_Type || object->ob_type == &PyType_Type) { | |||
method = (PyMethodObject*)PyObject_GetAttrString(object, "__init__"); | |||
decrefMethod = true; | |||
} else if (object->ob_type == &PyFunction_Type) { | |||
func = (PyFunctionObject*)object; | |||
} else if (object->ob_type == &PyMethod_Type) { | |||
method = (PyMethodObject*)object; | |||
} | |||
if (method) { | |||
if (PyFunction_Check(method->im_func)) { | |||
func = (PyFunctionObject*)method->im_func; | |||
} else if (isMethodDescriptor((PyObject*)method)) { | |||
QString docstr; | |||
PyObject* doc = PyObject_GetAttrString(object, "__doc__"); | |||
if (doc) { | |||
docstr = PyString_AsString(doc); | |||
Py_DECREF(doc); | |||
} | |||
PyObject* s = PyObject_GetAttrString(object, "__name__"); | |||
if (s) { | |||
Q_ASSERT(PyString_Check(s)); | |||
signature = PyString_AsString(s); | |||
if (docstr.startsWith(signature + "(")) { | |||
signature = docstr; | |||
} else { | |||
signature += "(...)"; | |||
if (!docstr.isEmpty()) { | |||
signature += "\n\n" + docstr; | |||
} | |||
} | |||
Py_DECREF(s); | |||
} | |||
} | |||
} | |||
if (func) { | |||
QString funcName; | |||
PyObject* s = PyObject_GetAttrString((PyObject*)func, "__name__"); | |||
if (s) { | |||
Q_ASSERT(PyString_Check(s)); | |||
funcName = PyString_AsString(s); | |||
Py_DECREF(s); | |||
} | |||
if (method && funcName == "__init__") { | |||
PyObject* s = PyObject_GetAttrString(object, "__name__"); | |||
if (s) { | |||
Q_ASSERT(PyString_Check(s)); | |||
funcName = PyString_AsString(s); | |||
Py_DECREF(s); | |||
} | |||
} | |||
QStringList arguments; | |||
QStringList defaults; | |||
QString varargs; | |||
QString varkeywords; | |||
// NOTE: This implementation is based on function getargs() in inspect.py. | |||
// inspect.getargs() can handle anonymous (tuple) arguments, while this code does not. | |||
// It can be implemented, but it may be rarely needed and not necessary. | |||
PyCodeObject* code = (PyCodeObject*)func->func_code; | |||
if (code->co_varnames) { | |||
int nargs = code->co_argcount; | |||
Q_ASSERT(PyTuple_Check(code->co_varnames)); | |||
for (int i=0; i<nargs; i++) { | |||
PyObject* name = PyTuple_GetItem(code->co_varnames, i); | |||
Q_ASSERT(PyString_Check(name)); | |||
arguments << PyString_AsString(name); | |||
} | |||
if (code->co_flags & CO_VARARGS) { | |||
PyObject* s = PyTuple_GetItem(code->co_varnames, nargs); | |||
Q_ASSERT(PyString_Check(s)); | |||
varargs = PyString_AsString(s); | |||
nargs += 1; | |||
} | |||
if (code->co_flags & CO_VARKEYWORDS) { | |||
PyObject* s = PyTuple_GetItem(code->co_varnames, nargs); | |||
Q_ASSERT(PyString_Check(s)); | |||
varkeywords = PyString_AsString(s); | |||
} | |||
} | |||
PyObject* defaultsTuple = func->func_defaults; | |||
if (defaultsTuple) { | |||
Q_ASSERT(PyTuple_Check(defaultsTuple)); | |||
for (Py_ssize_t i=0; i<PyTuple_Size(defaultsTuple); i++) { | |||
PyObject* d = PyTuple_GetItem(defaultsTuple, i); | |||
PyObject* s = PyObject_Repr(d); | |||
Q_ASSERT(PyString_Check(s)); | |||
defaults << PyString_AsString(s); | |||
Py_DECREF(s); | |||
} | |||
} | |||
int firstdefault = arguments.size() - defaults.size(); | |||
for (int i=0; i<arguments.size(); i++) { | |||
if (!signature.isEmpty()) { signature += ", "; } | |||
if (!method || i>0 || arguments[i] != "self") { | |||
signature += arguments[i]; | |||
if (i >= firstdefault) { | |||
signature += "=" + defaults[i-firstdefault]; | |||
} | |||
} | |||
} | |||
if (!varargs.isEmpty()) { | |||
if (!signature.isEmpty()) { signature += ", "; } | |||
signature += "*" + varargs; | |||
} | |||
if (!varkeywords.isEmpty()) { | |||
if (!signature.isEmpty()) { signature += ", "; } | |||
signature += "**" + varkeywords; | |||
} | |||
signature = funcName + "(" + signature + ")"; | |||
} | |||
if (method && decrefMethod) { | |||
Py_DECREF(method); | |||
} | |||
} | |||
return signature; | |||
} | |||
florianlink
|
r180 | ||
void PythonQtPrivate::shellClassDeleted( void* shellClass ) | |||
{ | |||
PythonQtInstanceWrapper* wrap = _wrappedObjects.value(shellClass); | |||
if (wrap && wrap->_wrappedPtr) { | |||
// this is a pure C++ wrapper and the shell has gone, so we need | |||
// to set the _wrappedPtr to NULL on the wrapper | |||
wrap->_wrappedPtr = NULL; | |||
// and then we remove the wrapper, since the wrapped class is gone | |||
_wrappedObjects.remove(shellClass); | |||
} | |||
// if the wrapper is a QObject, we do not handle this here, | |||
// it will be handled by the QPointer<> to the QObject, which becomes NULL | |||
// via the QObject destructor. | |||
florianlink
|
r193 | } | |
PyObject* PythonQtPrivate::wrapMemoryAsBuffer( const void* data, Py_ssize_t size ) | |||
{ | |||
// P3K port needed later on! | |||
return PyBuffer_FromMemory((void*)data, size); | |||
} | |||
PyObject* PythonQtPrivate::wrapMemoryAsBuffer( void* data, Py_ssize_t size ) | |||
{ | |||
// P3K port needed later on! | |||
return PyBuffer_FromReadWriteMemory(data, size); | |||
florianlink
|
r180 | } |