|
|
/*
|
|
|
*
|
|
|
* Copyright (C) 2010 MeVis Medical Solutions AG All Rights Reserved.
|
|
|
*
|
|
|
* 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
|
|
|
*
|
|
|
* Contact information: MeVis Medical Solutions AG, Universitaetsallee 29,
|
|
|
* 28359 Bremen, Germany or:
|
|
|
*
|
|
|
* http://www.mevis.de
|
|
|
*
|
|
|
*/
|
|
|
|
|
|
//----------------------------------------------------------------------------------
|
|
|
/*!
|
|
|
// \file PythonQt.cpp
|
|
|
// \author Florian Link
|
|
|
// \author Last changed by $Author: florian $
|
|
|
// \date 2006-05
|
|
|
*/
|
|
|
//----------------------------------------------------------------------------------
|
|
|
|
|
|
#include "PythonQtClassInfo.h"
|
|
|
#include "PythonQtMethodInfo.h"
|
|
|
#include "PythonQt.h"
|
|
|
#include "PythonQtConversion.h"
|
|
|
#include <QMetaMethod>
|
|
|
#include <QMetaObject>
|
|
|
#include <QMetaEnum>
|
|
|
|
|
|
QHash<QByteArray, int> PythonQtMethodInfo::_parameterTypeDict;
|
|
|
|
|
|
PythonQtClassInfo::PythonQtClassInfo() {
|
|
|
_meta = NULL;
|
|
|
_constructors = NULL;
|
|
|
_destructor = NULL;
|
|
|
_decoratorProvider = NULL;
|
|
|
_decoratorProviderCB = NULL;
|
|
|
_pythonQtClassWrapper = NULL;
|
|
|
_shellSetInstanceWrapperCB = NULL;
|
|
|
_metaTypeId = -1;
|
|
|
_typeSlots = 0;
|
|
|
_isQObject = false;
|
|
|
_enumsCreated = false;
|
|
|
_searchPolymorphicHandlerOnParent = true;
|
|
|
}
|
|
|
|
|
|
PythonQtClassInfo::~PythonQtClassInfo()
|
|
|
{
|
|
|
clearCachedMembers();
|
|
|
|
|
|
if (_constructors) {
|
|
|
_constructors->deleteOverloadsAndThis();
|
|
|
}
|
|
|
if (_destructor) {
|
|
|
_destructor->deleteOverloadsAndThis();
|
|
|
}
|
|
|
Q_FOREACH(PythonQtSlotInfo* info, _decoratorSlots) {
|
|
|
info->deleteOverloadsAndThis();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void PythonQtClassInfo::setupQObject(const QMetaObject* meta)
|
|
|
{
|
|
|
// _wrappedClassName is already set earlier in the class setup
|
|
|
_isQObject = true;
|
|
|
_meta = meta;
|
|
|
}
|
|
|
|
|
|
void PythonQtClassInfo::setupCPPObject(const QByteArray& classname)
|
|
|
{
|
|
|
_isQObject = false;
|
|
|
_wrappedClassName = classname;
|
|
|
_metaTypeId = QMetaType::type(classname);
|
|
|
if (_metaTypeId == 0) {
|
|
|
_metaTypeId = -1;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void PythonQtClassInfo::clearCachedMembers()
|
|
|
{
|
|
|
QHashIterator<QByteArray, PythonQtMemberInfo> i(_cachedMembers);
|
|
|
while (i.hasNext()) {
|
|
|
PythonQtMemberInfo member = i.next().value();
|
|
|
if (member._type== PythonQtMemberInfo::Slot || member._type== PythonQtMemberInfo::Signal) {
|
|
|
PythonQtSlotInfo* info = member._slot;
|
|
|
while (info) {
|
|
|
PythonQtSlotInfo* next = info->nextInfo();
|
|
|
delete info;
|
|
|
info = next;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
int PythonQtClassInfo::findCharOffset(const char* sigStart, char someChar)
|
|
|
{
|
|
|
const char* sigEnd = sigStart;
|
|
|
char c;
|
|
|
do {
|
|
|
c = *sigEnd++;
|
|
|
} while (c!=someChar && c!=0);
|
|
|
return sigEnd-sigStart-1;
|
|
|
}
|
|
|
|
|
|
bool PythonQtClassInfo::lookForPropertyAndCache(const char* memberName)
|
|
|
{
|
|
|
if (!_meta) return false;
|
|
|
|
|
|
bool found = false;
|
|
|
bool nameMapped = false;
|
|
|
const char* attributeName = memberName;
|
|
|
// look for properties
|
|
|
int i = _meta->indexOfProperty(attributeName);
|
|
|
if (i==-1) {
|
|
|
// try to map name to objectName
|
|
|
if (qstrcmp(attributeName, "name")==0) {
|
|
|
attributeName = "objectName";
|
|
|
nameMapped = true;
|
|
|
i = _meta->indexOfProperty(attributeName);
|
|
|
}
|
|
|
}
|
|
|
if (i!=-1) {
|
|
|
PythonQtMemberInfo newInfo(_meta->property(i));
|
|
|
_cachedMembers.insert(attributeName, newInfo);
|
|
|
if (nameMapped) {
|
|
|
_cachedMembers.insert(memberName, newInfo);
|
|
|
}
|
|
|
#ifdef PYTHONQT_DEBUG
|
|
|
std::cout << "caching property " << memberName << " on " << _meta->className() << std::endl;
|
|
|
#endif
|
|
|
found = true;
|
|
|
}
|
|
|
return found;
|
|
|
}
|
|
|
|
|
|
PythonQtSlotInfo* PythonQtClassInfo::recursiveFindDecoratorSlotsFromDecoratorProvider(const char* memberName, PythonQtSlotInfo* inputInfo, bool &found, QHash<QByteArray, PythonQtMemberInfo>& memberCache, int upcastingOffset)
|
|
|
{
|
|
|
inputInfo = findDecoratorSlotsFromDecoratorProvider(memberName, inputInfo, found, memberCache, upcastingOffset);
|
|
|
Q_FOREACH(const ParentClassInfo& info, _parentClasses) {
|
|
|
inputInfo = info._parent->recursiveFindDecoratorSlotsFromDecoratorProvider(memberName, inputInfo, found, memberCache, upcastingOffset+info._upcastingOffset);
|
|
|
}
|
|
|
return inputInfo;
|
|
|
}
|
|
|
|
|
|
PythonQtSlotInfo* PythonQtClassInfo::findDecoratorSlotsFromDecoratorProvider(const char* memberName, PythonQtSlotInfo* tail, bool &found, QHash<QByteArray, PythonQtMemberInfo>& memberCache, int upcastingOffset) {
|
|
|
QObject* decoratorProvider = decorator();
|
|
|
if (decoratorProvider) {
|
|
|
//qDebug()<< "looking " << decoratorProvider->metaObject()->className() << " " << memberName << " " << upcastingOffset;
|
|
|
const QMetaObject* meta = decoratorProvider->metaObject();
|
|
|
int numMethods = meta->methodCount();
|
|
|
int startFrom = QObject::staticMetaObject.methodCount();
|
|
|
for (int i = startFrom; i < numMethods; i++) {
|
|
|
QMetaMethod m = meta->method(i);
|
|
|
if ((m.methodType() == QMetaMethod::Method ||
|
|
|
m.methodType() == QMetaMethod::Slot) && m.access() == QMetaMethod::Public) {
|
|
|
|
|
|
QByteArray signature = PythonQtUtils::methodName(m);
|
|
|
bool isClassDeco = false;
|
|
|
if (signature.startsWith("static_")) {
|
|
|
// skip the static_classname_ part of the string
|
|
|
signature = signature.mid(7 + 1 + static_cast<int>(strlen(className())));
|
|
|
isClassDeco = true;
|
|
|
} else if (signature.startsWith("new_")) {
|
|
|
isClassDeco = true;
|
|
|
} else if (signature.startsWith("delete_")) {
|
|
|
isClassDeco = true;
|
|
|
}
|
|
|
// XXX no checking is currently done if the slots have correct first argument or not...
|
|
|
|
|
|
// check if same length and same name
|
|
|
if (signature == memberName) {
|
|
|
found = true;
|
|
|
PythonQtSlotInfo* info = new PythonQtSlotInfo(this, m, i, decoratorProvider, isClassDeco?PythonQtSlotInfo::ClassDecorator:PythonQtSlotInfo::InstanceDecorator);
|
|
|
info->setUpcastingOffset(upcastingOffset);
|
|
|
//qDebug()<< "adding " << decoratorProvider->metaObject()->className() << " " << memberName << " " << upcastingOffset;
|
|
|
if (tail) {
|
|
|
tail->setNextInfo(info);
|
|
|
} else {
|
|
|
PythonQtMemberInfo newInfo(info);
|
|
|
memberCache.insert(signature, newInfo);
|
|
|
}
|
|
|
tail = info;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
tail = findDecoratorSlots(memberName, tail, found, memberCache, upcastingOffset);
|
|
|
|
|
|
// now look for slots/signals/methods on this level of the meta object
|
|
|
if (_meta) {
|
|
|
int numMethods = _meta->methodCount();
|
|
|
// start from methodOffset, to only add slots which are located in this class,
|
|
|
// and not in the parent class, which is traversed recursively later on.
|
|
|
// (if the class in not a QObject, we are working with a script wrapper QObject
|
|
|
// and need to read all slots/signals starting from 0).
|
|
|
int methodOffset = _isQObject?_meta->methodOffset():0;
|
|
|
for (int i = methodOffset; i < numMethods; i++) {
|
|
|
QMetaMethod m = _meta->method(i);
|
|
|
if (((m.methodType() == QMetaMethod::Method ||
|
|
|
m.methodType() == QMetaMethod::Slot) && m.access() == QMetaMethod::Public)
|
|
|
|| m.methodType()==QMetaMethod::Signal) {
|
|
|
|
|
|
QByteArray signature = PythonQtUtils::methodName(m);
|
|
|
|
|
|
// check if same length and same name
|
|
|
if (signature == memberName) {
|
|
|
found = true;
|
|
|
PythonQtSlotInfo* info = new PythonQtSlotInfo(this, m, i);
|
|
|
if (tail) {
|
|
|
tail->setNextInfo(info);
|
|
|
} else {
|
|
|
PythonQtMemberInfo newInfo(info);
|
|
|
memberCache.insert(signature, newInfo);
|
|
|
}
|
|
|
tail = info;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
return tail;
|
|
|
}
|
|
|
|
|
|
bool PythonQtClassInfo::lookForMethodAndCache(const char* memberName)
|
|
|
{
|
|
|
bool found = false;
|
|
|
PythonQtSlotInfo* tail = NULL;
|
|
|
|
|
|
// look for dynamic decorators in this class and in derived classes
|
|
|
// (do this first to allow overloading of existing slots with generated wrappers,
|
|
|
// e.g. QDialog::accept is overloaded with PythonQtWrapper_QDialog::accept decorator)
|
|
|
tail = recursiveFindDecoratorSlotsFromDecoratorProvider(memberName, tail, found, _cachedMembers, 0);
|
|
|
|
|
|
return found;
|
|
|
}
|
|
|
|
|
|
bool PythonQtClassInfo::lookForEnumAndCache(const QMetaObject* meta, const char* memberName)
|
|
|
{
|
|
|
bool found = false;
|
|
|
// look for enum values
|
|
|
int enumCount = meta->enumeratorCount();
|
|
|
for (int i=0;i<enumCount; i++) {
|
|
|
QMetaEnum e = meta->enumerator(i);
|
|
|
// we do not want flags, they will cause our values to appear two times
|
|
|
if (e.isFlag()) continue;
|
|
|
|
|
|
for (int j=0; j < e.keyCount(); j++) {
|
|
|
if (qstrcmp(e.key(j), memberName)==0) {
|
|
|
PyObject* enumType = findEnumWrapper(e.name());
|
|
|
if (enumType) {
|
|
|
PythonQtObjectPtr enumValuePtr;
|
|
|
enumValuePtr.setNewRef(PythonQtPrivate::createEnumValueInstance(enumType, e.value(j)));
|
|
|
PythonQtMemberInfo newInfo(enumValuePtr);
|
|
|
_cachedMembers.insert(memberName, newInfo);
|
|
|
#ifdef PYTHONQT_DEBUG
|
|
|
std::cout << "caching enum " << memberName << " on " << meta->className()->constData() << std::endl;
|
|
|
#endif
|
|
|
found = true;
|
|
|
break;
|
|
|
} else {
|
|
|
std::cout << "enum " << e.name() << " not found on " << className().constData() << std::endl;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
return found;
|
|
|
}
|
|
|
|
|
|
PythonQtMemberInfo PythonQtClassInfo::member(const char* memberName)
|
|
|
{
|
|
|
PythonQtMemberInfo info = _cachedMembers.value(memberName);
|
|
|
if (info._type != PythonQtMemberInfo::Invalid) {
|
|
|
return info;
|
|
|
} else {
|
|
|
bool found = false;
|
|
|
|
|
|
found = lookForPropertyAndCache(memberName);
|
|
|
if (!found) {
|
|
|
found = lookForMethodAndCache(memberName);
|
|
|
}
|
|
|
if (!found) {
|
|
|
if (_meta) {
|
|
|
// check enums in our meta object directly
|
|
|
found = lookForEnumAndCache(_meta, memberName);
|
|
|
}
|
|
|
if (!found) {
|
|
|
// check enums in the class hierachy of CPP classes
|
|
|
// look for dynamic decorators in this class and in derived classes
|
|
|
QList<QObject*> decoObjects;
|
|
|
recursiveCollectDecoratorObjects(decoObjects);
|
|
|
Q_FOREACH(QObject* deco, decoObjects) {
|
|
|
// call on ourself for caching, but with different metaObject():
|
|
|
found = lookForEnumAndCache(deco->metaObject(), memberName);
|
|
|
if (found) {
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
if (!found) {
|
|
|
// maybe it is an enum wrapper?
|
|
|
PyObject* p = findEnumWrapper(memberName);
|
|
|
if (p) {
|
|
|
info._type = PythonQtMemberInfo::EnumWrapper;
|
|
|
info._pythonType = p;
|
|
|
_cachedMembers.insert(memberName, info);
|
|
|
found = true;
|
|
|
}
|
|
|
// maybe it is a nested class?
|
|
|
Q_FOREACH(PythonQtClassInfo* nestedClass, _nestedClasses) {
|
|
|
PyObject* pyClass = nestedClass->pythonQtClassWrapper();
|
|
|
if (pyClass) {
|
|
|
if (strcmp(memberName, nestedClass->unscopedClassName().constData()) == 0) {
|
|
|
info._type = PythonQtMemberInfo::NestedClass;
|
|
|
info._pythonType = pyClass;
|
|
|
_cachedMembers.insert(memberName, info);
|
|
|
found = true;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
if (!found) {
|
|
|
// since python keywords can not be looked up, we check if the name contains a single trailing _
|
|
|
// and remove that and look again, so that we e.g. find exec on an exec_ lookup
|
|
|
QByteArray mbrName(memberName);
|
|
|
if ((mbrName.length()>2) &&
|
|
|
(mbrName.at(mbrName.length()-1) == '_') &&
|
|
|
(mbrName.at(mbrName.length()-2) != '_')) {
|
|
|
mbrName = mbrName.mid(0,mbrName.length()-1);
|
|
|
found = lookForMethodAndCache(mbrName.constData());
|
|
|
if (found) {
|
|
|
return _cachedMembers.value(mbrName);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
if (!found) {
|
|
|
// we store a NotFound member, so that we get a quick result for non existing members (e.g. operator_equal lookup)
|
|
|
info._type = PythonQtMemberInfo::NotFound;
|
|
|
_cachedMembers.insert(memberName, info);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return _cachedMembers.value(memberName);
|
|
|
}
|
|
|
|
|
|
void PythonQtClassInfo::recursiveCollectDecoratorObjects(QList<QObject*>& decoratorObjects) {
|
|
|
QObject* deco = decorator();
|
|
|
if (deco) {
|
|
|
decoratorObjects.append(deco);
|
|
|
}
|
|
|
Q_FOREACH(const ParentClassInfo& info, _parentClasses) {
|
|
|
info._parent->recursiveCollectDecoratorObjects(decoratorObjects);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void PythonQtClassInfo::recursiveCollectClassInfos(QList<PythonQtClassInfo*>& classInfoObjects) {
|
|
|
classInfoObjects.append(this);
|
|
|
Q_FOREACH(const ParentClassInfo& info, _parentClasses) {
|
|
|
info._parent->recursiveCollectClassInfos(classInfoObjects);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
PythonQtSlotInfo* PythonQtClassInfo::findDecoratorSlots(const char* memberName, PythonQtSlotInfo* tail, bool &found, QHash<QByteArray, PythonQtMemberInfo>& memberCache, int upcastingOffset)
|
|
|
{
|
|
|
QListIterator<PythonQtSlotInfo*> it(_decoratorSlots);
|
|
|
while (it.hasNext()) {
|
|
|
|
|
|
PythonQtSlotInfo* infoOrig = it.next();
|
|
|
QByteArray signature = PythonQtUtils::methodName(*infoOrig->metaMethod());
|
|
|
if (signature.startsWith("static_")) {
|
|
|
int offset = signature.indexOf('_', 7);
|
|
|
signature = signature.mid(offset+1);
|
|
|
}
|
|
|
if (signature == memberName) {
|
|
|
//make a copy, otherwise we will have trouble on overloads!
|
|
|
PythonQtSlotInfo* info = new PythonQtSlotInfo(*infoOrig);
|
|
|
info->setUpcastingOffset(upcastingOffset);
|
|
|
found = true;
|
|
|
if (tail) {
|
|
|
tail->setNextInfo(info);
|
|
|
} else {
|
|
|
PythonQtMemberInfo newInfo(info);
|
|
|
memberCache.insert(signature, newInfo);
|
|
|
}
|
|
|
tail = info;
|
|
|
}
|
|
|
}
|
|
|
return tail;
|
|
|
}
|
|
|
|
|
|
void PythonQtClassInfo::listDecoratorSlotsFromDecoratorProvider(QStringList& list, bool metaOnly) {
|
|
|
QObject* decoratorProvider = decorator();
|
|
|
if (decoratorProvider) {
|
|
|
const QMetaObject* meta = decoratorProvider->metaObject();
|
|
|
int numMethods = meta->methodCount();
|
|
|
int startFrom = QObject::staticMetaObject.methodCount();
|
|
|
for (int i = startFrom; i < numMethods; i++) {
|
|
|
QMetaMethod m = meta->method(i);
|
|
|
if ((m.methodType() == QMetaMethod::Method ||
|
|
|
m.methodType() == QMetaMethod::Slot) && m.access() == QMetaMethod::Public) {
|
|
|
|
|
|
QByteArray signature = PythonQtUtils::methodName(m);
|
|
|
bool isClassDeco = false;
|
|
|
if (signature.startsWith("static_")) {
|
|
|
// skip the static_classname_ part of the string
|
|
|
signature = signature.mid(7 + 1 + static_cast<int>(strlen(className())));
|
|
|
isClassDeco = true;
|
|
|
} else if (signature.startsWith("new_")) {
|
|
|
continue;
|
|
|
} else if (signature.startsWith("delete_")) {
|
|
|
continue;
|
|
|
} else if (signature.startsWith("py_")) {
|
|
|
// hide everything that starts with py_
|
|
|
continue;
|
|
|
}
|
|
|
// XXX no checking is currently done if the slots have correct first argument or not...
|
|
|
if (!metaOnly || isClassDeco) {
|
|
|
list << QString::fromLatin1(signature.constData());
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// look for global decorator slots
|
|
|
QListIterator<PythonQtSlotInfo*> it(_decoratorSlots);
|
|
|
while (it.hasNext()) {
|
|
|
PythonQtSlotInfo* slot = it.next();
|
|
|
if (metaOnly) {
|
|
|
if (slot->isClassDecorator()) {
|
|
|
QByteArray first = slot->slotName();
|
|
|
if (first.startsWith("static_")) {
|
|
|
int idx = first.indexOf('_');
|
|
|
idx = first.indexOf('_', idx+1);
|
|
|
first = first.mid(idx+1);
|
|
|
}
|
|
|
list << first;
|
|
|
}
|
|
|
} else {
|
|
|
list << slot->slotName();
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
QStringList PythonQtClassInfo::propertyList()
|
|
|
{
|
|
|
QStringList l;
|
|
|
if (_isQObject && _meta) {
|
|
|
int i;
|
|
|
int numProperties = _meta->propertyCount();
|
|
|
for (i = 0; i < numProperties; i++) {
|
|
|
QMetaProperty p = _meta->property(i);
|
|
|
l << QString(p.name());
|
|
|
}
|
|
|
}
|
|
|
return l;
|
|
|
}
|
|
|
|
|
|
QStringList PythonQtClassInfo::memberList()
|
|
|
{
|
|
|
decorator();
|
|
|
|
|
|
QStringList l;
|
|
|
QString h;
|
|
|
// normal slots of QObject (or wrapper QObject)
|
|
|
if (_meta) {
|
|
|
int numMethods = _meta->methodCount();
|
|
|
bool skipQObj = !_isQObject;
|
|
|
for (int i = skipQObj?QObject::staticMetaObject.methodCount():0; i < numMethods; i++) {
|
|
|
QMetaMethod m = _meta->method(i);
|
|
|
if (((m.methodType() == QMetaMethod::Method ||
|
|
|
m.methodType() == QMetaMethod::Slot) && m.access() == QMetaMethod::Public)
|
|
|
|| m.methodType()==QMetaMethod::Signal) {
|
|
|
l << PythonQtUtils::methodName(m);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
{
|
|
|
// look for dynamic decorators in this class and in derived classes
|
|
|
QList<PythonQtClassInfo*> infos;
|
|
|
recursiveCollectClassInfos(infos);
|
|
|
Q_FOREACH(PythonQtClassInfo* info, infos) {
|
|
|
info->listDecoratorSlotsFromDecoratorProvider(l, false);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// List enumerator keys...
|
|
|
QList<const QMetaObject*> enumMetaObjects;
|
|
|
if (_meta) {
|
|
|
enumMetaObjects << _meta;
|
|
|
}
|
|
|
// check enums in the class hierachy of CPP classes
|
|
|
QList<QObject*> decoObjects;
|
|
|
recursiveCollectDecoratorObjects(decoObjects);
|
|
|
Q_FOREACH(QObject* deco, decoObjects) {
|
|
|
enumMetaObjects << deco->metaObject();
|
|
|
}
|
|
|
|
|
|
Q_FOREACH(const QMetaObject* meta, enumMetaObjects) {
|
|
|
for (int i = 0; i<meta->enumeratorCount(); i++) {
|
|
|
QMetaEnum e = meta->enumerator(i);
|
|
|
l << e.name();
|
|
|
// we do not want flags, they will cause our values to appear two times
|
|
|
if (e.isFlag()) continue;
|
|
|
|
|
|
for (int j=0; j < e.keyCount(); j++) {
|
|
|
l << QString(e.key(j));
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
Q_FOREACH(PythonQtClassInfo* nestedClass, _nestedClasses) {
|
|
|
PyObject* pyClass = nestedClass->pythonQtClassWrapper();
|
|
|
if (pyClass) {
|
|
|
QByteArray name = nestedClass->unscopedClassName();
|
|
|
l << QString(name);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return QSet<QString>::fromList(l).toList();
|
|
|
}
|
|
|
|
|
|
const QByteArray& PythonQtClassInfo::className() const
|
|
|
{
|
|
|
return _wrappedClassName;
|
|
|
}
|
|
|
|
|
|
void* PythonQtClassInfo::castTo(void* ptr, const char* classname)
|
|
|
{
|
|
|
if (ptr==NULL) {
|
|
|
return NULL;
|
|
|
}
|
|
|
if (_wrappedClassName == classname) {
|
|
|
return ptr;
|
|
|
}
|
|
|
Q_FOREACH(const ParentClassInfo& info, _parentClasses) {
|
|
|
void* result = info._parent->castTo((char*)ptr + info._upcastingOffset, classname);
|
|
|
if (result) {
|
|
|
return result;
|
|
|
}
|
|
|
}
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
bool PythonQtClassInfo::inherits(const char* name)
|
|
|
{
|
|
|
if (_wrappedClassName == name) {
|
|
|
return true;
|
|
|
}
|
|
|
Q_FOREACH(const ParentClassInfo& info, _parentClasses) {
|
|
|
if (info._parent->inherits(name)) {
|
|
|
return true;
|
|
|
}
|
|
|
}
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
bool PythonQtClassInfo::inherits(PythonQtClassInfo* classInfo)
|
|
|
{
|
|
|
if (classInfo == this) {
|
|
|
return true;
|
|
|
}
|
|
|
Q_FOREACH(const ParentClassInfo& info, _parentClasses) {
|
|
|
if (info._parent->inherits(classInfo)) {
|
|
|
return true;
|
|
|
}
|
|
|
}
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
QString PythonQtClassInfo::help()
|
|
|
{
|
|
|
decorator();
|
|
|
QString h;
|
|
|
h += QString("--- ") + QString(className()) + QString(" ---\n");
|
|
|
|
|
|
if (_isQObject) {
|
|
|
h += "Properties:\n";
|
|
|
|
|
|
int i;
|
|
|
int numProperties = _meta->propertyCount();
|
|
|
for (i = 0; i < numProperties; i++) {
|
|
|
QMetaProperty p = _meta->property(i);
|
|
|
h += QString(p.name()) + " (" + QString(p.typeName()) + " )\n";
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (constructors()) {
|
|
|
h += "Constructors:\n";
|
|
|
PythonQtSlotInfo* constr = constructors();
|
|
|
while (constr) {
|
|
|
h += constr->fullSignature() + "\n";
|
|
|
constr = constr->nextInfo();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
h += "Slots:\n";
|
|
|
h += "QString help()\n";
|
|
|
h += "QString className()\n";
|
|
|
|
|
|
if (_meta) {
|
|
|
int numMethods = _meta->methodCount();
|
|
|
for (int i = 0; i < numMethods; i++) {
|
|
|
QMetaMethod m = _meta->method(i);
|
|
|
if ((m.methodType() == QMetaMethod::Method ||
|
|
|
m.methodType() == QMetaMethod::Slot) && m.access() == QMetaMethod::Public) {
|
|
|
PythonQtSlotInfo slot(this, m, i);
|
|
|
h += slot.fullSignature()+ "\n";
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// TODO xxx : decorators and enums from decorator() are missing...
|
|
|
// maybe we can reuse memberlist()?
|
|
|
|
|
|
if (_meta && _meta->enumeratorCount()) {
|
|
|
h += "Enums:\n";
|
|
|
for (int i = 0; i<_meta->enumeratorCount(); i++) {
|
|
|
QMetaEnum e = _meta->enumerator(i);
|
|
|
h += QString(e.name()) + " {";
|
|
|
for (int j=0; j < e.keyCount(); j++) {
|
|
|
if (j) { h+= ", "; }
|
|
|
h += e.key(j);
|
|
|
}
|
|
|
h += " }\n";
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (_isQObject && _meta) {
|
|
|
int numMethods = _meta->methodCount();
|
|
|
if (numMethods>0) {
|
|
|
h += "Signals:\n";
|
|
|
for (int i = 0; i < numMethods; i++) {
|
|
|
QMetaMethod m = _meta->method(i);
|
|
|
if (m.methodType() == QMetaMethod::Signal) {
|
|
|
h += QString(PythonQtUtils::signature(m)) + "\n";
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
return h;
|
|
|
}
|
|
|
|
|
|
PythonQtSlotInfo* PythonQtClassInfo::constructors()
|
|
|
{
|
|
|
if (!_constructors) {
|
|
|
// force creation of lazy decorator, which will register the decorators
|
|
|
decorator();
|
|
|
}
|
|
|
return _constructors;
|
|
|
}
|
|
|
|
|
|
PythonQtSlotInfo* PythonQtClassInfo::destructor()
|
|
|
{
|
|
|
if (!_destructor) {
|
|
|
// force creation of lazy decorator, which will register the decorators
|
|
|
decorator();
|
|
|
}
|
|
|
if (!_destructor) {
|
|
|
if (!_parentClasses.isEmpty()) {
|
|
|
_destructor = _parentClasses.at(0)._parent->destructor();
|
|
|
}
|
|
|
}
|
|
|
return _destructor;
|
|
|
}
|
|
|
|
|
|
void PythonQtClassInfo::addConstructor(PythonQtSlotInfo* info)
|
|
|
{
|
|
|
PythonQtSlotInfo* prev = constructors();
|
|
|
if (prev) {
|
|
|
info->setNextInfo(prev->nextInfo());
|
|
|
prev->setNextInfo(info);
|
|
|
} else {
|
|
|
_constructors = info;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void PythonQtClassInfo::addDecoratorSlot(PythonQtSlotInfo* info)
|
|
|
{
|
|
|
_decoratorSlots.append(info);
|
|
|
}
|
|
|
|
|
|
void PythonQtClassInfo::setDestructor(PythonQtSlotInfo* info)
|
|
|
{
|
|
|
if (_destructor) {
|
|
|
_destructor->deleteOverloadsAndThis();
|
|
|
}
|
|
|
_destructor = info;
|
|
|
}
|
|
|
|
|
|
void PythonQtClassInfo::setMetaObject(const QMetaObject* meta)
|
|
|
{
|
|
|
_meta = meta;
|
|
|
clearCachedMembers();
|
|
|
}
|
|
|
|
|
|
QObject* PythonQtClassInfo::decorator()
|
|
|
{
|
|
|
if (!_decoratorProvider && _decoratorProviderCB) {
|
|
|
_decoratorProvider = (*_decoratorProviderCB)();
|
|
|
if (_decoratorProvider) {
|
|
|
_decoratorProvider->setParent(PythonQt::priv());
|
|
|
// setup enums early, since they might be needed by the constructor decorators:
|
|
|
if (!_enumsCreated) {
|
|
|
createEnumWrappers();
|
|
|
}
|
|
|
PythonQt::priv()->addDecorators(_decoratorProvider, PythonQtPrivate::ConstructorDecorator | PythonQtPrivate::DestructorDecorator);
|
|
|
}
|
|
|
}
|
|
|
// check if enums need to be created and create them if they are not yet created
|
|
|
if (!_enumsCreated) {
|
|
|
createEnumWrappers();
|
|
|
}
|
|
|
return _decoratorProvider;
|
|
|
}
|
|
|
|
|
|
void* PythonQtClassInfo::recursiveCastDownIfPossible(void* ptr, const char** resultClassName)
|
|
|
{
|
|
|
if (!_polymorphicHandlers.isEmpty()) {
|
|
|
Q_FOREACH(PythonQtPolymorphicHandlerCB* cb, _polymorphicHandlers) {
|
|
|
void* resultPtr = (*cb)(ptr, resultClassName);
|
|
|
if (resultPtr) {
|
|
|
return resultPtr;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
Q_FOREACH(const ParentClassInfo& info, _parentClasses) {
|
|
|
if (!info._parent->isQObject()) {
|
|
|
void* resultPtr = info._parent->recursiveCastDownIfPossible((char*)ptr + info._upcastingOffset, resultClassName);
|
|
|
if (resultPtr) {
|
|
|
return resultPtr;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
void* PythonQtClassInfo::castDownIfPossible(void* ptr, PythonQtClassInfo** resultClassInfo)
|
|
|
{
|
|
|
const char* className;
|
|
|
// this would do downcasting recursively...
|
|
|
// It is too expensive, since it walks the whole hierarchy on each cast...
|
|
|
// void* resultPtr = recursiveCastDownIfPossible(ptr, &className);
|
|
|
|
|
|
if (_polymorphicHandlers.isEmpty() && _searchPolymorphicHandlerOnParent) {
|
|
|
// if we don't have a polymorphic handler, seach
|
|
|
// in the first parent class hierarchy, not for multiple inheritance...
|
|
|
// (for speed reasons and because of casting offsets...)
|
|
|
// Only do this once...
|
|
|
_searchPolymorphicHandlerOnParent = false;
|
|
|
if (_parentClasses.count()>0) {
|
|
|
PythonQtClassInfo* parent = _parentClasses[0]._parent;
|
|
|
while (parent) {
|
|
|
if (parent->_polymorphicHandlers.count()>0) {
|
|
|
// copy handlers from parent class, to speedup next lookup
|
|
|
_polymorphicHandlers = parent->_polymorphicHandlers;
|
|
|
break;
|
|
|
}
|
|
|
if (parent->_parentClasses.count()>0) {
|
|
|
parent = parent->_parentClasses[0]._parent;
|
|
|
} else {
|
|
|
parent = NULL;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// we only do downcasting on the base object, not on the whole inheritance tree...
|
|
|
void* resultPtr = NULL;
|
|
|
if (!_polymorphicHandlers.isEmpty()) {
|
|
|
Q_FOREACH(PythonQtPolymorphicHandlerCB* cb, _polymorphicHandlers) {
|
|
|
resultPtr = (*cb)(ptr, &className);
|
|
|
if (resultPtr) {
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
if (resultPtr) {
|
|
|
*resultClassInfo = PythonQt::priv()->getClassInfo(className);
|
|
|
} else {
|
|
|
*resultClassInfo = this;
|
|
|
resultPtr = ptr;
|
|
|
}
|
|
|
return resultPtr;
|
|
|
}
|
|
|
|
|
|
PyObject* PythonQtClassInfo::findEnumWrapper(const QByteArray& name, PythonQtClassInfo* localScope, bool* isLocalEnum)
|
|
|
{
|
|
|
if (isLocalEnum) {
|
|
|
*isLocalEnum = true;
|
|
|
}
|
|
|
int scopePos = name.lastIndexOf("::");
|
|
|
if (scopePos != -1) {
|
|
|
if (isLocalEnum) {
|
|
|
*isLocalEnum = false;
|
|
|
}
|
|
|
// split into scope and enum name
|
|
|
QByteArray enumScope = name.mid(0,scopePos);
|
|
|
QByteArray enumName = name.mid(scopePos+2);
|
|
|
PythonQtClassInfo* info = PythonQt::priv()->getClassInfo(enumScope);
|
|
|
if (info) {
|
|
|
return info->findEnumWrapper(enumName);
|
|
|
} else{
|
|
|
return NULL;
|
|
|
}
|
|
|
}
|
|
|
if (localScope) {
|
|
|
return localScope->findEnumWrapper(name);
|
|
|
} else {
|
|
|
return NULL;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void PythonQtClassInfo::createEnumWrappers(const QMetaObject* meta)
|
|
|
{
|
|
|
for (int i = meta->enumeratorOffset();i<meta->enumeratorCount();i++) {
|
|
|
QMetaEnum e = meta->enumerator(i);
|
|
|
PythonQtObjectPtr p;
|
|
|
p.setNewRef(PythonQtPrivate::createNewPythonQtEnumWrapper(e.name(), _pythonQtClassWrapper));
|
|
|
_enumWrappers.append(p);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void PythonQtClassInfo::createEnumWrappers()
|
|
|
{
|
|
|
if (!_enumsCreated) {
|
|
|
_enumsCreated = true;
|
|
|
if (_meta) {
|
|
|
createEnumWrappers(_meta);
|
|
|
}
|
|
|
if (decorator()) {
|
|
|
createEnumWrappers(decorator()->metaObject());
|
|
|
}
|
|
|
Q_FOREACH(const ParentClassInfo& info, _parentClasses) {
|
|
|
info._parent->createEnumWrappers();
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
PyObject* PythonQtClassInfo::findEnumWrapper(const char* name) {
|
|
|
// force enum creation
|
|
|
if (!_enumsCreated) {
|
|
|
createEnumWrappers();
|
|
|
}
|
|
|
Q_FOREACH(const PythonQtObjectPtr& p, _enumWrappers) {
|
|
|
const char* className = ((PyTypeObject*)p.object())->tp_name;
|
|
|
if (qstrcmp(className, name)==0) {
|
|
|
return p.object();
|
|
|
}
|
|
|
}
|
|
|
Q_FOREACH(const ParentClassInfo& info, _parentClasses) {
|
|
|
PyObject* p = info._parent->findEnumWrapper(name);
|
|
|
if (p) return p;
|
|
|
}
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
void PythonQtClassInfo::setDecoratorProvider( PythonQtQObjectCreatorFunctionCB* cb )
|
|
|
{
|
|
|
_decoratorProviderCB = cb;
|
|
|
_decoratorProvider = NULL;
|
|
|
_enumsCreated = false;
|
|
|
}
|
|
|
|
|
|
void PythonQtClassInfo::clearNotFoundCachedMembers()
|
|
|
{
|
|
|
// remove all not found entries, since a new decorator means new slots,
|
|
|
// which might have been cached as "NotFound" already.
|
|
|
QMutableHashIterator<QByteArray, PythonQtMemberInfo> it(_cachedMembers);
|
|
|
while (it.hasNext()) {
|
|
|
it.next();
|
|
|
if (it.value()._type == PythonQtMemberInfo::NotFound) {
|
|
|
it.remove();
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void PythonQtClassInfo::addNestedClass(PythonQtClassInfo* info)
|
|
|
{
|
|
|
_nestedClasses.append(info);
|
|
|
}
|
|
|
|
|
|
QByteArray PythonQtClassInfo::unscopedClassName() const
|
|
|
{
|
|
|
int index = _wrappedClassName.indexOf("::");
|
|
|
if (index > 0) {
|
|
|
return _wrappedClassName.mid(index + 2);
|
|
|
} else {
|
|
|
return _wrappedClassName;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
PyObject* PythonQtClassInfo::copyObject( void* cppObject )
|
|
|
{
|
|
|
PythonQtClassInfo* info;
|
|
|
cppObject = castDownIfPossible(cppObject, &info);
|
|
|
if (info->metaTypeId() != PythonQtMethodInfo::Unknown) {
|
|
|
// use meta type to copy the object
|
|
|
return PythonQtConv::createCopyFromMetaType(info->metaTypeId(), cppObject);
|
|
|
} else {
|
|
|
// use copy constructor
|
|
|
PythonQtSlotInfo* slot = info->getCopyConstructor();
|
|
|
if (slot) {
|
|
|
void* result;
|
|
|
void* args[2];
|
|
|
args[0] = &result;
|
|
|
args[1] = cppObject;
|
|
|
slot->decorator()->qt_metacall(QMetaObject::InvokeMetaMethod, slot->slotIndex(), args);
|
|
|
if (result) {
|
|
|
PythonQtInstanceWrapper* wrapper = (PythonQtInstanceWrapper*)PythonQt::priv()->wrapPtr(result, info->className());
|
|
|
if (wrapper) {
|
|
|
wrapper->_ownedByPythonQt = true;
|
|
|
}
|
|
|
return (PyObject*)wrapper;
|
|
|
}
|
|
|
} else {
|
|
|
std::cerr << "PythonQt: Can't create a copy of '" << info->_wrappedClassName.constData() << "', either use qRegisterMetaType() or add a copy constructor to the decorator/wrapper." << std::endl;
|
|
|
}
|
|
|
}
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
PythonQtSlotInfo* PythonQtClassInfo::getCopyConstructor()
|
|
|
{
|
|
|
PythonQtSlotInfo* construc = constructors();
|
|
|
while (construc) {
|
|
|
if ((construc->parameterCount() == 2) &&
|
|
|
(construc->parameters().at(1).name == _wrappedClassName) &&
|
|
|
(construc->parameters().at(1).pointerCount == 0)) {
|
|
|
return construc;
|
|
|
}
|
|
|
construc = construc->nextInfo();
|
|
|
}
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
|
|
|
|
PythonQtMemberInfo::PythonQtMemberInfo( PythonQtSlotInfo* info )
|
|
|
{
|
|
|
if (info->metaMethod()->methodType() == QMetaMethod::Signal) {
|
|
|
_type = Signal;
|
|
|
} else {
|
|
|
_type = Slot;
|
|
|
}
|
|
|
_slot = info;
|
|
|
_enumValue = NULL;
|
|
|
_pythonType = NULL;
|
|
|
}
|
|
|
|
|
|
PythonQtMemberInfo::PythonQtMemberInfo( const PythonQtObjectPtr& enumValue )
|
|
|
{
|
|
|
_type = EnumValue;
|
|
|
_slot = NULL;
|
|
|
_enumValue = enumValue;
|
|
|
_pythonType = NULL;
|
|
|
}
|
|
|
|
|
|
PythonQtMemberInfo::PythonQtMemberInfo( const QMetaProperty& prop )
|
|
|
{
|
|
|
_type = Property;
|
|
|
_slot = NULL;
|
|
|
_property = prop;
|
|
|
_enumValue = NULL;
|
|
|
_pythonType = NULL;
|
|
|
}
|