WingHexPy/PythonQt/PythonQtConversion.h

645 lines
27 KiB
C++

#ifndef PYTHONQTCONVERSION_H
#define PYTHONQTCONVERSION_H
/*
*
* 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 PythonQtConversion.h
// \author Florian Link
// \author Last changed by $Author: florian $
// \date 2006-05
*/
//----------------------------------------------------------------------------------
#include "PythonQt.h"
#include "PythonQtClassInfo.h"
#include "PythonQtMethodInfo.h"
#include "PythonQtMisc.h"
#include <QList>
#include <vector>
typedef PyObject *PythonQtConvertMetaTypeToPythonCB(const void *inObject,
int metaTypeId);
typedef bool PythonQtConvertPythonToMetaTypeCB(PyObject *inObject,
void *outObject, int metaTypeId,
bool strict);
typedef QVariant
PythonQtConvertPythonSequenceToQVariantListCB(PyObject *inObject);
#define PythonQtRegisterListTemplateConverter(type, innertype) \
{ \
int typeId = qRegisterMetaType<type<innertype>>(#type "<" #innertype ">"); \
PythonQtConv::registerPythonToMetaTypeConverter( \
typeId, PythonQtConvertPythonListToListOfValueType<type<innertype>, \
innertype>); \
PythonQtConv::registerMetaTypeToPythonConverter( \
typeId, PythonQtConvertListOfValueTypeToPythonList<type<innertype>, \
innertype>); \
}
#define PythonQtRegisterListTemplateConverterForKnownClass(type, innertype) \
{ \
int typeId = qRegisterMetaType<type<innertype>>(#type "<" #innertype ">"); \
PythonQtConv::registerPythonToMetaTypeConverter( \
typeId, PythonQtConvertPythonListToListOfKnownClass<type<innertype>, \
innertype>); \
PythonQtConv::registerMetaTypeToPythonConverter( \
typeId, PythonQtConvertListOfKnownClassToPythonList<type<innertype>, \
innertype>); \
}
#define PythonQtRegisterQPairConverter(type1, type2) \
{ \
int typeId = qRegisterMetaType<QPair<type1, type2>>("QPair<" #type1 \
"," #type2 ">"); \
PythonQtConv::registerPythonToMetaTypeConverter( \
typeId, PythonQtConvertPythonToPair<type1, type2>); \
PythonQtConv::registerMetaTypeToPythonConverter( \
typeId, PythonQtConvertPairToPython<type1, type2>); \
}
#define PythonQtRegisterIntegerMapConverter(type, innertype) \
{ \
int typeId = qRegisterMetaType<type<int, innertype>>( \
#type "<int, " #innertype ">"); \
PythonQtConv::registerPythonToMetaTypeConverter( \
typeId, \
PythonQtConvertPythonToIntegerMap<type<int, innertype>, innertype>); \
PythonQtConv::registerMetaTypeToPythonConverter( \
typeId, \
PythonQtConvertIntegerMapToPython<type<int, innertype>, innertype>); \
}
#define PythonQtRegisterListTemplateQPairConverter(listtype, type1, type2) \
{ \
qRegisterMetaType<QPair<type1, type2>>("QPair<" #type1 "," #type2 ">"); \
int typeId = qRegisterMetaType<listtype<QPair<type1, type2>>>( \
#listtype "<QPair<" #type1 "," #type2 ">>"); \
PythonQtConv::registerPythonToMetaTypeConverter( \
typeId, \
PythonQtConvertPythonListToListOfPair<listtype<QPair<type1, type2>>, \
type1, type2>); \
PythonQtConv::registerMetaTypeToPythonConverter( \
typeId, \
PythonQtConvertListOfPairToPythonList<listtype<QPair<type1, type2>>, \
type1, type2>); \
}
#define PythonQtRegisterToolClassesTemplateConverter(innertype) \
PythonQtRegisterListTemplateConverter(QList, innertype); \
PythonQtRegisterListTemplateConverter(QVector, innertype); \
PythonQtRegisterListTemplateConverter(std::vector, innertype);
#define PythonQtRegisterToolClassesTemplateConverterForKnownClass(innertype) \
PythonQtRegisterListTemplateConverterForKnownClass(QList, innertype); \
PythonQtRegisterListTemplateConverterForKnownClass(QVector, innertype); \
PythonQtRegisterListTemplateConverterForKnownClass(std::vector, innertype);
//! a static class that offers methods for type conversion
class PYTHONQT_EXPORT PythonQtConv {
public:
//! get a ref counted True or False Python object
static PyObject *GetPyBool(bool val);
//! converts the Qt parameter given in \c data, interpreting it as a \c info
//! parameter, into a Python object,
static PyObject *
ConvertQtValueToPython(const PythonQtMethodInfo::ParameterInfo &info,
const void *data);
//! convert python object to Qt (according to the given parameter) and if the
//! conversion should be strict (classInfo is currently not used anymore)
static void *ConvertPythonToQt(const PythonQtMethodInfo::ParameterInfo &info,
PyObject *obj, bool strict,
PythonQtClassInfo *classInfo,
void *alreadyAllocatedCPPObject = NULL);
//! creates a data storage for the passed parameter type and returns a void
//! pointer to be set as arg[0] of qt_metacall
static void *
CreateQtReturnValue(const PythonQtMethodInfo::ParameterInfo &info);
//! converts QString to Python string (unicode!)
static PyObject *QStringToPyObject(const QString &str);
//! converts QStringList to Python tuple
static PyObject *QStringListToPyObject(const QStringList &list);
//! converts QStringList to Python list
static PyObject *QStringListToPyList(const QStringList &list);
//! get string representation of py object
static QString PyObjGetRepresentation(PyObject *val);
//! get string value from py object
static QString PyObjGetString(PyObject *val) {
bool ok;
QString s = PyObjGetString(val, false, ok);
return s;
}
//! get string value from py object
static QString PyObjGetString(PyObject *val, bool strict, bool &ok);
//! get bytes from py object
static QByteArray PyObjGetBytes(PyObject *val, bool strict, bool &ok);
//! get int from py object
static int PyObjGetInt(PyObject *val, bool strict, bool &ok);
//! get int64 from py object
static qint64 PyObjGetLongLong(PyObject *val, bool strict, bool &ok);
//! get int64 from py object
static quint64 PyObjGetULongLong(PyObject *val, bool strict, bool &ok);
//! get double from py object
static double PyObjGetDouble(PyObject *val, bool strict, bool &ok);
//! get bool from py object
static bool PyObjGetBool(PyObject *val, bool strict, bool &ok);
//! create a string list from python sequence
static QStringList PyObjToStringList(PyObject *val, bool strict, bool &ok);
//! convert python object to qvariant, if type is given it will try to create
//! a qvariant of that type, otherwise it will guess from the python type
static QVariant PyObjToQVariant(PyObject *val, int type = -1);
//! convert QVariant from PyObject
static PyObject *QVariantToPyObject(const QVariant &v);
static PyObject *QVariantHashToPyObject(const QVariantHash &m);
static PyObject *QVariantMapToPyObject(const QVariantMap &m);
static PyObject *QVariantListToPyObject(const QVariantList &l);
//! get human readable string from CPP object (when the metatype is known)
static QString CPPObjectToString(int type, const void *data);
//! register a converter callback from python to cpp for given metatype
static void
registerPythonToMetaTypeConverter(int metaTypeId,
PythonQtConvertPythonToMetaTypeCB *cb) {
_pythonToMetaTypeConverters.insert(metaTypeId, cb);
}
//! register a converter callback from cpp to python for given metatype
static void
registerMetaTypeToPythonConverter(int metaTypeId,
PythonQtConvertMetaTypeToPythonCB *cb) {
_metaTypeToPythonConverters.insert(metaTypeId, cb);
}
//! set a callback that is called when a Python sequence should be converted
//! to a QVariantList to allow special conversion.
static void setPythonSequenceToQVariantListCallback(
PythonQtConvertPythonSequenceToQVariantListCB *cb) {
_pythonSequenceToQVariantListCB = cb;
}
//! converts the Qt parameter given in \c data, interpreting it as a \c type
//! registered qvariant/meta type, into a Python object,
static PyObject *convertQtValueToPythonInternal(int type, const void *data);
//! creates a copy of given object, using the QMetaType
static PyObject *createCopyFromMetaType(int type, const void *object);
//! cast wrapper to given className if possible
static void *castWrapperTo(PythonQtInstanceWrapper *wrapper,
const QByteArray &className, bool &ok);
static bool convertToPythonQtObjectPtr(PyObject *obj,
void * /* PythonQtObjectPtr* */ outPtr,
int /*metaTypeId*/, bool /*strict*/);
static PyObject *
convertFromPythonQtObjectPtr(const void * /* PythonQtObjectPtr* */ inObject,
int /*metaTypeId*/);
static bool convertToQListOfPythonQtObjectPtr(
PyObject *obj, void * /* QList<PythonQtObjectPtr>* */ outList,
int /*metaTypeId*/, bool /*strict*/);
static PyObject *convertFromQListOfPythonQtObjectPtr(
const void * /* QList<PythonQtObjectPtr>* */ inObject,
int /*metaTypeId*/);
static PyObject *convertFromStringRef(const void *inObject,
int /*metaTypeId*/);
//! Returns the name of the equivalent CPP type (for signals and slots)
static QByteArray getCPPTypeName(PyObject *type);
//! Returns if the given object is a string (or unicode string)
static bool isStringType(PyTypeObject *type);
public:
static PythonQtValueStorage<qint64, 128> global_valueStorage;
static PythonQtValueStorage<void *, 128> global_ptrStorage;
static PythonQtValueStorageWithCleanup<QVariant, 128> global_variantStorage;
protected:
static QHash<int, PythonQtConvertMetaTypeToPythonCB *>
_metaTypeToPythonConverters;
static QHash<int, PythonQtConvertPythonToMetaTypeCB *>
_pythonToMetaTypeConverters;
static PythonQtConvertPythonSequenceToQVariantListCB
*_pythonSequenceToQVariantListCB;
//! handle automatic conversion of some special types (QColor, QBrush, ...)
static void *handlePythonToQtAutoConversion(int typeId, PyObject *obj,
void *alreadyAllocatedCPPObject);
//! converts the list of pointers of given type to Python
static PyObject *ConvertQListOfPointerTypeToPythonList(
QList<void *> *list, const PythonQtMethodInfo::ParameterInfo &info);
//! tries to convert the python object to a QList of pointers to \c type
//! objects, returns true on success
static bool ConvertPythonListToQListOfPointerType(
PyObject *obj, QList<void *> *list,
const PythonQtMethodInfo::ParameterInfo &info, bool strict);
//! helper template method for conversion from Python to QVariantMap/Hash
template <typename Map>
static void pythonToMapVariant(PyObject *val, QVariant &result);
//! helper template function for QVariantMapToPyObject/QVariantHashToPyObject
template <typename Map> static PyObject *mapToPython(const Map &m);
};
template <class ListType, class T>
PyObject *
PythonQtConvertListOfValueTypeToPythonList(const void * /*QList<T>* */ inList,
int metaTypeId) {
ListType *list = (ListType *)inList;
static const int innerType = PythonQtMethodInfo::getInnerTemplateMetaType(
QByteArray(QMetaType::typeName(metaTypeId)));
if (innerType == QVariant::Invalid) {
std::cerr
<< "PythonQtConvertListOfValueTypeToPythonList: unknown inner type "
<< QMetaType::typeName(metaTypeId) << std::endl;
}
PyObject *result = PyTuple_New(list->size());
int i = 0;
Q_FOREACH (const T &value, *list) {
PyTuple_SET_ITEM(
result, i,
PythonQtConv::convertQtValueToPythonInternal(innerType, &value));
i++;
}
return result;
}
template <class ListType, class T>
bool PythonQtConvertPythonListToListOfValueType(PyObject *obj,
void * /*QList<T>* */ outList,
int metaTypeId,
bool /*strict*/) {
ListType *list = (ListType *)outList;
static const int innerType = PythonQtMethodInfo::getInnerTemplateMetaType(
QByteArray(QMetaType::typeName(metaTypeId)));
if (innerType == QVariant::Invalid) {
std::cerr
<< "PythonQtConvertPythonListToListOfValueType: unknown inner type "
<< QMetaType::typeName(metaTypeId) << std::endl;
}
bool result = false;
if (PySequence_Check(obj)) {
int count = PySequence_Size(obj);
if (count >= 0) {
result = true;
PyObject *value;
for (int i = 0; i < count; i++) {
value = PySequence_GetItem(obj, i);
// this is quite some overhead, but it avoids having another large
// switch...
QVariant v = PythonQtConv::PyObjToQVariant(value, innerType);
Py_XDECREF(value);
if (v.isValid()) {
list->push_back(qvariant_cast<T>(v));
} else {
result = false;
break;
}
}
}
}
return result;
}
//--------------------------------------------------------------------------------------------------------------------
template <class ListType, class T>
PyObject *
PythonQtConvertListOfKnownClassToPythonList(const void * /*QList<T>* */ inList,
int metaTypeId) {
ListType *list = (ListType *)inList;
static PythonQtClassInfo *innerType =
PythonQt::priv()->getClassInfo(PythonQtMethodInfo::getInnerListTypeName(
QByteArray(QMetaType::typeName(metaTypeId))));
if (innerType == NULL) {
std::cerr
<< "PythonQtConvertListOfKnownClassToPythonList: unknown inner type "
<< innerType->className().constData() << std::endl;
}
PyObject *result = PyTuple_New(list->size());
int i = 0;
Q_FOREACH (const T &value, *list) {
T *newObject = new T(value);
PythonQtInstanceWrapper *wrap =
(PythonQtInstanceWrapper *)PythonQt::priv()->wrapPtr(
newObject, innerType->className());
wrap->_ownedByPythonQt = true;
PyTuple_SET_ITEM(result, i, (PyObject *)wrap);
i++;
}
return result;
}
template <class ListType, class T>
bool PythonQtConvertPythonListToListOfKnownClass(PyObject *obj,
void * /*QList<T>* */ outList,
int metaTypeId,
bool /*strict*/) {
ListType *list = (ListType *)outList;
static PythonQtClassInfo *innerType =
PythonQt::priv()->getClassInfo(PythonQtMethodInfo::getInnerListTypeName(
QByteArray(QMetaType::typeName(metaTypeId))));
if (innerType == NULL) {
std::cerr
<< "PythonQtConvertListOfKnownClassToPythonList: unknown inner type "
<< innerType->className().constData() << std::endl;
}
bool result = false;
if (PySequence_Check(obj)) {
int count = PySequence_Size(obj);
if (count >= 0) {
result = true;
PyObject *value;
for (int i = 0; i < count; i++) {
value = PySequence_GetItem(obj, i);
if (PyObject_TypeCheck(value, &PythonQtInstanceWrapper_Type)) {
PythonQtInstanceWrapper *wrap = (PythonQtInstanceWrapper *)value;
bool ok;
T *object = (T *)PythonQtConv::castWrapperTo(
wrap, innerType->className(), ok);
Py_XDECREF(value);
if (ok) {
list->push_back(*object);
} else {
result = false;
break;
}
} else {
Py_XDECREF(value);
result = false;
break;
}
}
}
}
return result;
}
//--------------------------------------------------------------------------------------------------------------------
template <class T1, class T2>
PyObject *PythonQtConvertPairToPython(const void * /*QPair<T1,T2>* */ inPair,
int metaTypeId) {
QPair<T1, T2> *pair = (QPair<T1, T2> *)inPair;
static int innerType1 = -1;
static int innerType2 = -1;
if (innerType1 == -1) {
QByteArray innerTypes = PythonQtMethodInfo::getInnerTemplateTypeName(
QByteArray(QMetaType::typeName(metaTypeId)));
QList<QByteArray> names = innerTypes.split(',');
innerType1 = QMetaType::type(names.at(0).trimmed());
innerType2 = QMetaType::type(names.at(1).trimmed());
}
if (innerType1 == QVariant::Invalid || innerType2 == QVariant::Invalid) {
std::cerr << "PythonQtConvertPairToPython: unknown inner type "
<< QMetaType::typeName(metaTypeId) << std::endl;
}
PyObject *result = PyTuple_New(2);
PyTuple_SET_ITEM(
result, 0,
PythonQtConv::convertQtValueToPythonInternal(innerType1, &pair->first));
PyTuple_SET_ITEM(
result, 1,
PythonQtConv::convertQtValueToPythonInternal(innerType2, &pair->second));
return result;
}
template <class T1, class T2>
bool PythonQtConvertPythonToPair(PyObject *obj,
void * /*QPair<T1,T2>* */ outPair,
int metaTypeId, bool /*strict*/) {
QPair<T1, T2> *pair = (QPair<T1, T2> *)outPair;
static int innerType1 = -1;
static int innerType2 = -1;
if (innerType1 == -1) {
QByteArray innerTypes = PythonQtMethodInfo::getInnerTemplateTypeName(
QByteArray(QMetaType::typeName(metaTypeId)));
QList<QByteArray> names = innerTypes.split(',');
innerType1 = QMetaType::type(names.at(0).trimmed());
innerType2 = QMetaType::type(names.at(1).trimmed());
}
if (innerType1 == QVariant::Invalid || innerType2 == QVariant::Invalid) {
std::cerr << "PythonQtConvertPythonToPair: unknown inner type "
<< QMetaType::typeName(metaTypeId) << std::endl;
}
bool result = false;
if (PySequence_Check(obj)) {
int count = PySequence_Size(obj);
if (count == 2) {
result = true;
PyObject *value;
value = PySequence_GetItem(obj, 0);
// this is quite some overhead, but it avoids having another large
// switch...
QVariant v = PythonQtConv::PyObjToQVariant(value, innerType1);
Py_XDECREF(value);
if (v.isValid()) {
pair->first = qvariant_cast<T1>(v);
} else {
return false;
}
value = PySequence_GetItem(obj, 1);
// this is quite some overhead, but it avoids having another large
// switch...
v = PythonQtConv::PyObjToQVariant(value, innerType2);
Py_XDECREF(value);
if (v.isValid()) {
pair->second = qvariant_cast<T2>(v);
} else {
return false;
}
}
}
return result;
}
//--------------------------------------------------------------------------------------------------------------------
template <class ListType, class T1, class T2>
PyObject *PythonQtConvertListOfPairToPythonList(
const void * /*QList<QPair<T1,T2> >* */ inList, int metaTypeId) {
ListType *list = (ListType *)inList;
static int innerType = PythonQtMethodInfo::getInnerTemplateMetaType(
QByteArray(QMetaType::typeName(metaTypeId)));
if (innerType == QVariant::Invalid) {
std::cerr << "PythonQtConvertListOfPairToPythonList: unknown inner type "
<< QMetaType::typeName(metaTypeId) << std::endl;
}
PyObject *result = PyTuple_New(list->size());
int i = 0;
typedef const QPair<T1, T2> Pair;
Q_FOREACH (Pair &value, *list) {
PyObject *object = PythonQtConvertPairToPython<T1, T2>(&value, innerType);
PyTuple_SET_ITEM(result, i, object);
i++;
}
return result;
}
template <class ListType, class T1, class T2>
bool PythonQtConvertPythonListToListOfPair(
PyObject *obj, void * /*QList<QPair<T1,T2> >* */ outList, int metaTypeId,
bool /*strict*/) {
ListType *list = (ListType *)outList;
static int innerType = PythonQtMethodInfo::getInnerTemplateMetaType(
QByteArray(QMetaType::typeName(metaTypeId)));
if (innerType == QVariant::Invalid) {
std::cerr << "PythonQtConvertPythonListToListOfPair: unknown inner type "
<< QMetaType::typeName(metaTypeId) << std::endl;
}
bool result = false;
if (PySequence_Check(obj)) {
int count = PySequence_Size(obj);
if (count >= 0) {
result = true;
PyObject *value;
for (int i = 0; i < count; i++) {
QPair<T1, T2> pair;
value = PySequence_GetItem(obj, i);
if (PythonQtConvertPythonToPair<T1, T2>(value, &pair, innerType,
false)) {
Py_XDECREF(value);
list->push_back(pair);
} else {
Py_XDECREF(value);
result = false;
break;
}
}
}
}
return result;
}
//--------------------------------------------------------------------------------------------------------------------
template <class MapType, class T>
PyObject *
PythonQtConvertIntegerMapToPython(const void * /*QMap<int, T>* */ inMap,
int metaTypeId) {
MapType *map = (MapType *)inMap;
static int innerType = -1;
if (innerType == -1) {
QByteArray innerTypes = PythonQtMethodInfo::getInnerTemplateTypeName(
QByteArray(QMetaType::typeName(metaTypeId)));
QList<QByteArray> names = innerTypes.split(',');
innerType = QMetaType::type(names.at(1).trimmed());
}
if (innerType == QVariant::Invalid) {
std::cerr << "PythonQtConvertIntegerMapToPython: unknown inner type "
<< QMetaType::typeName(metaTypeId) << std::endl;
}
PyObject *result = PyDict_New();
typename MapType::const_iterator t = map->constBegin();
PyObject *key;
PyObject *val;
for (; t != map->constEnd(); t++) {
key = PyInt_FromLong(t.key());
val = PythonQtConv::convertQtValueToPythonInternal(innerType, &t.value());
PyDict_SetItem(result, key, val);
Py_DECREF(key);
Py_DECREF(val);
}
return result;
}
template <class MapType, class T>
bool PythonQtConvertPythonToIntegerMap(PyObject *val,
void * /*QMap<int, T>* */ outMap,
int metaTypeId, bool /*strict*/) {
MapType *map = (MapType *)outMap;
static int innerType = -1;
if (innerType == -1) {
QByteArray innerTypes = PythonQtMethodInfo::getInnerTemplateTypeName(
QByteArray(QMetaType::typeName(metaTypeId)));
QList<QByteArray> names = innerTypes.split(',');
innerType = QMetaType::type(names.at(1).trimmed());
}
if (innerType == QVariant::Invalid) {
std::cerr << "PythonQtConvertPythonToIntegerMap: unknown inner type "
<< QMetaType::typeName(metaTypeId) << std::endl;
}
bool result = false;
if (PyMapping_Check(val)) {
result = true;
PyObject *items = PyMapping_Items(val);
if (items) {
int count = PyList_Size(items);
PyObject *value;
PyObject *key;
PyObject *tuple;
for (int i = 0; i < count; i++) {
tuple = PyList_GetItem(items, i);
key = PyTuple_GetItem(tuple, 0);
value = PyTuple_GetItem(tuple, 1);
bool ok;
int intKey = PythonQtConv::PyObjGetInt(key, false, ok);
// this is quite some overhead, but it avoids having another large
// switch...
QVariant v = PythonQtConv::PyObjToQVariant(value, innerType);
if (v.isValid() && ok) {
map->insert(intKey, qvariant_cast<T>(v));
} else {
result = false;
break;
}
}
Py_DECREF(items);
}
}
return result;
}
#endif