diff --git a/CMakeLists.txt b/CMakeLists.txt index 7126b1e6..0e6d646c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,7 +46,7 @@ endif() # Qt components set(qt5libs Core Widgets Network OpenGL Sql Svg UiTools Xml XmlPatterns) -if(PythonQt_QT_VERSION VERSION_GREATER "4" AND "${Qt5_VERSION}" VERSION_LESS "5.6.0") +if(PythonQt_QT_VERSION VERSION_GREATER "4") list(APPEND qt5libs WebKitWidgets) endif() set(qt4libs core gui network opengl sql svg uitools webkit xml xmlpatterns) diff --git a/extensions/PythonQt_QtAll/PythonQt_QtAll.pro b/extensions/PythonQt_QtAll/PythonQt_QtAll.pro index 8de38b96..afcbbc42 100644 --- a/extensions/PythonQt_QtAll/PythonQt_QtAll.pro +++ b/extensions/PythonQt_QtAll/PythonQt_QtAll.pro @@ -1,13 +1,14 @@ # If Qt has support for webkit, add it: qtHaveModule(webkit):CONFIG += PythonQtWebKit -TARGET = PythonQt_QtAll +TARGET = PythonQt_QtAll-Qt5-PythonXY TEMPLATE = lib DESTDIR = ../../lib include ( ../../build/common.prf ) include ( ../../build/PythonQt.prf ) +TARGET = $$replace(TARGET, PythonXY, Python$${PYTHON_VERSION}) CONFIG += dll qt diff --git a/generator/qtscript_masterinclude.h b/generator/qtscript_masterinclude.h index 98640df5..a91f6de4 100644 --- a/generator/qtscript_masterinclude.h +++ b/generator/qtscript_masterinclude.h @@ -43,6 +43,12 @@ #define Q_BYTE_ORDER Q_LITTLE_ENDIAN #define QT_NO_STL +#define override +#define final +#define QOPENGLVERSIONFUNCTIONS_H +#define QOPENGLFUNCTIONS_H +#define QOPENGLEXTRAFUNCTIONS_H + #include #include #include diff --git a/src/PythonQt.cpp b/src/PythonQt.cpp index 4e379788..7b0020d7 100644 --- a/src/PythonQt.cpp +++ b/src/PythonQt.cpp @@ -1955,15 +1955,31 @@ const QMetaObject* PythonQtPrivate::getDynamicMetaObject(PythonQtInstanceWrapper PythonQtDynamicClassInfo* info = wrapper->dynamicClassInfo(); if (info) { if (!info->_dynamicMetaObject) { - buildDynamicMetaObject(((PythonQtClassWrapper*)Py_TYPE(wrapper)), prototypeMetaObject); + setupDynamicMetaObjectChain(((PythonQtClassWrapper*)Py_TYPE(wrapper)), prototypeMetaObject); } return info->_dynamicMetaObject; } return prototypeMetaObject; } -void PythonQtPrivate::buildDynamicMetaObject(PythonQtClassWrapper* type, const QMetaObject* prototypeMetaObject) +const QMetaObject* PythonQtPrivate::setupDynamicMetaObjectChain(PythonQtClassWrapper* type, const QMetaObject* prototypeMetaObject) { + if (!type->_dynamicClassInfo->_dynamicMetaObject) { + PyTypeObject* superType = ((PyTypeObject *)type)->tp_base; + const QMetaObject* metaObjectOfParent = prototypeMetaObject; + if (((PythonQtClassWrapper*)superType)->_dynamicClassInfo) { + metaObjectOfParent = setupDynamicMetaObjectChain((PythonQtClassWrapper*)superType, prototypeMetaObject); + } + return buildDynamicMetaObject(type, metaObjectOfParent); + } else { + return type->_dynamicClassInfo->_dynamicMetaObject; + } +} + +const QMetaObject* PythonQtPrivate::buildDynamicMetaObject(PythonQtClassWrapper* type, const QMetaObject* prototypeMetaObject) +{ + //std::cout << "creating " << ((PyTypeObject*)type)->tp_name << " derived from " << prototypeMetaObject->className() << std::endl; + QMetaObjectBuilder builder; builder.setSuperClass(prototypeMetaObject); builder.setClassName(((PyTypeObject*)type)->tp_name); @@ -2048,6 +2064,7 @@ void PythonQtPrivate::buildDynamicMetaObject(PythonQtClassWrapper* type, const Q // we don't need an own meta object, just use the one from our base class type->_dynamicClassInfo->_dynamicMetaObject = prototypeMetaObject; } + return type->_dynamicClassInfo->_dynamicMetaObject; } diff --git a/src/PythonQt.h b/src/PythonQt.h index 375c6c39..9171011f 100644 --- a/src/PythonQt.h +++ b/src/PythonQt.h @@ -742,7 +742,11 @@ class PYTHONQT_EXPORT PythonQtPrivate : public QObject { //! get the dynamic meta object for the given wrapper. It will contain the signals/slots that have been added in Python const QMetaObject* getDynamicMetaObject(PythonQtInstanceWrapper* wrapper, const QMetaObject* prototypeMetaObject); - void buildDynamicMetaObject(PythonQtClassWrapper* type, const QMetaObject* prototypeMetaObject); + //! recursively creates the dynamic meta object chain down to the Qt class wrapper. + const QMetaObject* setupDynamicMetaObjectChain(PythonQtClassWrapper* type, const QMetaObject* prototypeMetaObject); + + //! builds and returns the dynamic meta object for the given type, derived from prototypeMetaObject. + const QMetaObject* buildDynamicMetaObject(PythonQtClassWrapper* type, const QMetaObject* prototypeMetaObject); //! redirected from shell classes, tries to call the given meta call on the Python wrapper. int handleMetaCall(QObject* object, PythonQtInstanceWrapper* wrapper, QMetaObject::Call call, int id, void** args); diff --git a/src/PythonQtClassInfo.cpp b/src/PythonQtClassInfo.cpp index 06496150..d26453c8 100644 --- a/src/PythonQtClassInfo.cpp +++ b/src/PythonQtClassInfo.cpp @@ -61,6 +61,7 @@ PythonQtClassInfo::PythonQtClassInfo() { _typeSlots = 0; _isQObject = false; _enumsCreated = false; + _richCompareDetectionDone = false; _searchPolymorphicHandlerOnParent = true; _searchRefCountCB = true; _refCallback = NULL; @@ -737,14 +738,14 @@ QObject* PythonQtClassInfo::decorator() _decoratorProvider->setParent(PythonQt::priv()); // setup enums early, since they might be needed by the constructor decorators: if (!_enumsCreated) { - createEnumWrappers(); + createEnumWrappers(_decoratorProvider); } 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(); + createEnumWrappers(_decoratorProvider); } return _decoratorProvider; } @@ -856,18 +857,20 @@ void PythonQtClassInfo::createEnumWrappers(const QMetaObject* meta) } } -void PythonQtClassInfo::createEnumWrappers() +void PythonQtClassInfo::createEnumWrappers(const QObject* decoratorProvider) { if (!_enumsCreated) { _enumsCreated = true; if (_meta) { createEnumWrappers(_meta); } - if (decorator()) { - createEnumWrappers(decorator()->metaObject()); + if (decoratorProvider) { + createEnumWrappers(decoratorProvider->metaObject()); } Q_FOREACH(const ParentClassInfo& info, _parentClasses) { - info._parent->createEnumWrappers(); + // trigger decorator() instead of createEnumWrappers(), + // which will then call createEnumWrappers(). + info._parent->decorator(); } } } @@ -875,7 +878,9 @@ void PythonQtClassInfo::createEnumWrappers() PyObject* PythonQtClassInfo::findEnumWrapper(const char* name) { // force enum creation if (!_enumsCreated) { - createEnumWrappers(); + // trigger decorator() instead of createEnumWrappers(), + // which will then call createEnumWrappers(). + decorator(); } Q_FOREACH(const PythonQtObjectPtr& p, _enumWrappers) { const char* className = ((PyTypeObject*)p.object())->tp_name; @@ -1029,11 +1034,42 @@ PythonQtClassInfo* PythonQtClassInfo::getClassInfoForProperty( const QString& na } } if (!typeName.isEmpty()) { + if (typeName.endsWith("*")) { + typeName.truncate(typeName.length() - 1); + } PythonQtClassInfo* classInfo = PythonQt::priv()->getClassInfo(typeName); return classInfo; } return NULL; } + +bool PythonQtClassInfo::supportsRichCompare() +{ + if (_typeSlots & PythonQt::Type_RichCompare) { + return true; + } + if (!_richCompareDetectionDone) { + _richCompareDetectionDone = true; + static QList names; + if (names.isEmpty()) { + names << "__eq__"; + names << "__ne__"; + names << "__lt__"; + names << "__le__"; + names << "__gt__"; + names << "__ge__"; + } + foreach (const QByteArray& name, names) { + if (member(name)._type == PythonQtMemberInfo::Slot) { + // we found one of the operators, so we can support the type slot + _typeSlots |= PythonQt::Type_RichCompare; + break; + } + } + } + return (_typeSlots & PythonQt::Type_RichCompare); +} + //------------------------------------------------------------------------- PythonQtMemberInfo::PythonQtMemberInfo( PythonQtSlotInfo* info ) diff --git a/src/PythonQtClassInfo.h b/src/PythonQtClassInfo.h index 26a7b33a..9b0a4479 100644 --- a/src/PythonQtClassInfo.h +++ b/src/PythonQtClassInfo.h @@ -185,7 +185,7 @@ class PYTHONQT_EXPORT PythonQtClassInfo { void setShellSetInstanceWrapperCB(PythonQtShellSetInstanceWrapperCB* cb) { _shellSetInstanceWrapperCB = cb; } - + //! get the shell set instance wrapper cb PythonQtShellSetInstanceWrapperCB* shellSetInstanceWrapperCB() { return _shellSetInstanceWrapperCB; @@ -230,10 +230,16 @@ class PYTHONQT_EXPORT PythonQtClassInfo { //! Returns the class info for given property, if available. PythonQtClassInfo* getClassInfoForProperty( const QString& name ); + //! Returns if the class supports rich compare. This tests for + //! __eq__, __ne__, __lt__, __le__, __gt__, __ge__ slots and if + //! any of the slots is present it returns true and modifies the + //! _typeSlots with Type_RichCompare. The result is cached internally. + bool supportsRichCompare(); + private: void updateRefCountingCBs(); - void createEnumWrappers(); + void createEnumWrappers(const QObject* decoratorProvider); void createEnumWrappers(const QMetaObject* meta); PyObject* findEnumWrapper(const char* name); @@ -289,6 +295,7 @@ class PYTHONQT_EXPORT PythonQtClassInfo { bool _isQObject; bool _enumsCreated; + bool _richCompareDetectionDone; bool _searchPolymorphicHandlerOnParent; bool _searchRefCountCB; diff --git a/src/PythonQtClassWrapper.cpp b/src/PythonQtClassWrapper.cpp index f9d7835f..69ebd8a3 100644 --- a/src/PythonQtClassWrapper.cpp +++ b/src/PythonQtClassWrapper.cpp @@ -366,8 +366,17 @@ static int PythonQtClassWrapper_init(PythonQtClassWrapper* self, PyObject* args, // take the class info from the superType self->_classInfo = ((PythonQtClassWrapper*)superType)->classInfo(); - self->_dynamicClassInfo = new PythonQtDynamicClassInfo(); + + // take the class info from the superType and fill the whole chain + PyTypeObject* typeChain = (PyTypeObject *)self; + while (typeChain && Py_TYPE(typeChain) != &PythonQtClassWrapper_Type) { + + ((PythonQtClassWrapper*)typeChain)->_classInfo = ((PythonQtClassWrapper*)superType)->classInfo(); + ((PythonQtClassWrapper*)typeChain)->_dynamicClassInfo = new PythonQtDynamicClassInfo(); + + typeChain = typeChain->tp_base; + } } return 0; @@ -457,12 +466,10 @@ static PyObject *PythonQtClassWrapper_getattro(PyObject *obj, PyObject *name) } PyObject* dict = PyDict_New(); - QStringList l = wrapper->classInfo()->memberList(); - Q_FOREACH (QString name, l) { - if (name.startsWith("py_get_")) { - // add slot getters as normal properties - name = name.mid(7); - } + QSet completeSet = QSet::fromList(wrapper->classInfo()->memberList()); + completeSet.unite(QSet::fromList(wrapper->classInfo()->propertyList())); + + Q_FOREACH (QString name, completeSet) { if (name.startsWith("py_")) { // do not expose internal slots continue; diff --git a/src/PythonQtConversion.cpp b/src/PythonQtConversion.cpp index 36f56c61..a27dd051 100644 --- a/src/PythonQtConversion.cpp +++ b/src/PythonQtConversion.cpp @@ -55,6 +55,8 @@ PythonQtValueStorageWithCleanup PythonQtConv::global_variantStora QHash PythonQtConv::_metaTypeToPythonConverters; QHash PythonQtConv::_pythonToMetaTypeConverters; +PythonQtConvertPythonSequenceToQVariantListCB* PythonQtConv::_pythonSequenceToQVariantListCB = NULL; + PyObject* PythonQtConv::GetPyBool(bool val) { PyObject* r = val?Py_True:Py_False; @@ -102,7 +104,7 @@ PyObject* PythonQtConv::ConvertQtValueToPython(const PythonQtMethodInfo::Paramet } } - if (info.typeId >= QMetaType::User) { + if (info.typeId >= QMetaType::User || info.typeId == QMetaType::QByteArrayList) { // if a converter is registered, we use is: PythonQtConvertMetaTypeToPythonCB* converter = _metaTypeToPythonConverters.value(info.typeId); if (converter) { @@ -668,7 +670,7 @@ void* PythonQtConv::ConvertPythonToQt(const PythonQtMethodInfo::ParameterInfo& i } // We only do this for registered type > QMetaType::User for performance reasons. - if (info.typeId >= QMetaType::User) { + if (info.typeId >= QMetaType::User || info.typeId == QMetaType::QByteArrayList) { // Maybe we have a special converter that is registered for that type: PythonQtConvertPythonToMetaTypeCB* converter = _pythonToMetaTypeConverters.value(info.typeId); if (converter) { @@ -1028,13 +1030,13 @@ QVariant PythonQtConv::PyObjToQVariant(PyObject* val, int type) v = qVariantFromValue(myObject); } return v; + } else if (val == Py_None) { + // none is invalid + type = QVariant::Invalid; } else if (PyDict_Check(val)) { type = QVariant::Map; } else if (PyList_Check(val) || PyTuple_Check(val) || PySequence_Check(val)) { type = QVariant::List; - } else if (val == Py_None) { - // none is invalid - type = QVariant::Invalid; } else { // this used to be: // type = QVariant::String; @@ -1151,20 +1153,30 @@ QVariant PythonQtConv::PyObjToQVariant(PyObject* val, int type) pythonToMapVariant(val, v); break; case QVariant::List: - if (PySequence_Check(val)) { + { + bool isListOrTuple = PyList_Check(val) || PyTuple_Check(val); + if (isListOrTuple || PySequence_Check(val)) { + if (!isListOrTuple && _pythonSequenceToQVariantListCB) { + // Only call this if we don't have a tuple or list. + QVariant result = (*_pythonSequenceToQVariantListCB)(val); + if (result.isValid()) { + return result; + } + } int count = PySequence_Size(val); if (count >= 0) { // only get items if size is valid (>= 0) QVariantList list; PyObject* value; - for (int i = 0;i >(#type"<"#innertype">"); \ @@ -164,6 +165,10 @@ class PYTHONQT_EXPORT PythonQtConv { //! 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); @@ -194,6 +199,7 @@ class PYTHONQT_EXPORT PythonQtConv { protected: static QHash _metaTypeToPythonConverters; static QHash _pythonToMetaTypeConverters; + static PythonQtConvertPythonSequenceToQVariantListCB* _pythonSequenceToQVariantListCB; //! handle automatic conversion of some special types (QColor, QBrush, ...) static void* handlePythonToQtAutoConversion(int typeId, PyObject* obj, void* alreadyAllocatedCPPObject); diff --git a/src/PythonQtInstanceWrapper.cpp b/src/PythonQtInstanceWrapper.cpp index 36b67ed6..8fd3d8b6 100644 --- a/src/PythonQtInstanceWrapper.cpp +++ b/src/PythonQtInstanceWrapper.cpp @@ -240,7 +240,7 @@ static PyObject *PythonQtInstanceWrapper_richcompare(PythonQtInstanceWrapper* wr } } - if ((wrapper->classInfo()->typeSlots() & PythonQt::Type_RichCompare) == 0) { + if (!wrapper->classInfo()->supportsRichCompare()) { // shortcut if richcompare is not supported: if (validPtrs && code == Py_EQ) { return PythonQtConv::GetPyBool(areSamePtrs); @@ -454,12 +454,21 @@ static PyObject *PythonQtInstanceWrapper_getattro(PyObject *obj,PyObject *name) wrapper->_obj->metaObject(); } } - if (wrapper->dynamicClassInfo() && wrapper->dynamicClassInfo()->_classInfo) { - PythonQtMemberInfo member = wrapper->dynamicClassInfo()->_classInfo->member(attributeName); - if (member._type == PythonQtMemberInfo::Signal) { - PyObject* boundSignal = PythonQtSignalFunction_New(member._slot, (PyObject*)wrapper, NULL); - Py_DECREF(superAttr); - return boundSignal; + if (wrapper->dynamicClassInfo()) { + // go through the whole inheritance chain to find a signal in the dynamic class info: + PythonQtClassInfo* classInfo = NULL; + PythonQtClassWrapper* classType = (PythonQtClassWrapper*)Py_TYPE(wrapper); + while (classType->_dynamicClassInfo) { + classInfo = classType->_dynamicClassInfo->_classInfo; + if (classInfo) { + PythonQtMemberInfo member = classInfo->member(attributeName); + if (member._type == PythonQtMemberInfo::Signal) { + PyObject* boundSignal = PythonQtSignalFunction_New(member._slot, (PyObject*)wrapper, NULL); + Py_DECREF(superAttr); + return boundSignal; + } + } + classType = (PythonQtClassWrapper*)(((PyTypeObject*)classType)->tp_base); } } } diff --git a/src/PythonQtSignalReceiver.cpp b/src/PythonQtSignalReceiver.cpp index e3ac27f2..54341657 100644 --- a/src/PythonQtSignalReceiver.cpp +++ b/src/PythonQtSignalReceiver.cpp @@ -109,6 +109,11 @@ PyObject* PythonQtSignalTarget::call(PyObject* callable, const PythonQtMethodInf for (int i = 1; i < count; i++) { const PythonQtMethodInfo::ParameterInfo& param = params.at(i); PyObject* arg = PythonQtConv::ConvertQtValueToPython(param, arguments[i]); + if (arg && (param.pointerCount == 1) && (param.name == "PyObject")) { + // ConvertQtValueToPython does not ref-count the PyObject, so we have to + // do it ourselves... + Py_INCREF(arg); + } if (arg) { // steals reference, no unref PyTuple_SetItem(pargs, i-1,arg); diff --git a/src/PythonQtSlot.cpp b/src/PythonQtSlot.cpp index 8cedbcbb..ca594a55 100644 --- a/src/PythonQtSlot.cpp +++ b/src/PythonQtSlot.cpp @@ -80,6 +80,8 @@ bool PythonQtCallSlot(PythonQtClassInfo* classInfo, QObject* objectToCall, PyObj const QList& params = info->parameters(); const PythonQtSlotInfo::ParameterInfo& returnValueParam = params.at(0); + bool isVoidReturnValue = (returnValueParam.typeId == QMetaType::Void); + // set return argument to NULL argList[0] = NULL; @@ -131,7 +133,7 @@ bool PythonQtCallSlot(PythonQtClassInfo* classInfo, QObject* objectToCall, PyObj } // parameters are ok, now create the qt return value which is assigned to by metacall - if (returnValueParam.typeId != QMetaType::Void) { + if (!isVoidReturnValue) { // create empty default value for the return value if (!directReturnValuePointer) { // create empty default value for the return value @@ -203,6 +205,11 @@ bool PythonQtCallSlot(PythonQtClassInfo* classInfo, QObject* objectToCall, PyObj QByteArray what("std::exception: "); what += e.what(); PyErr_SetString(PyExc_RuntimeError, what.constData()); +#ifdef PYTHONQT_CATCH_ALL_EXCEPTIONS + } catch (...) { + hadException = true; + PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception."); +#endif } } @@ -212,7 +219,7 @@ bool PythonQtCallSlot(PythonQtClassInfo* classInfo, QObject* objectToCall, PyObj // handle the return value (which in most cases still needs to be converted to a Python object) if (!hadException) { - if (argList[0] || returnValueParam.typeId == QMetaType::Void) { + if (argList[0] || isVoidReturnValue) { if (directReturnValuePointer) { result = NULL; } else { @@ -225,9 +232,11 @@ bool PythonQtCallSlot(PythonQtClassInfo* classInfo, QObject* objectToCall, PyObj QString e = QString("Called ") + info->fullSignature() + ", return type '" + returnValueParam.name + "' is ignored because it is unknown to PythonQt. Probably you should register it using qRegisterMetaType() or add a default constructor decorator to the class."; PyErr_SetString(PyExc_ValueError, e.toLatin1().data()); result = NULL; + ok = false; } } else { result = NULL; + ok = false; } } recursiveEntry--; @@ -248,8 +257,11 @@ bool PythonQtCallSlot(PythonQtClassInfo* classInfo, QObject* objectToCall, PyObj } // NOTE: a return value can not pass the ownership to CPP, it would not make sense... } + // Either we have a + // a) a result value + // b) a directReturnValuePointer and it has a value or the return value is void // NOTE: it is important to only return here, otherwise the stack will not be popped!!! - return result || (directReturnValuePointer && *directReturnValuePointer); + return ok && (result || (directReturnValuePointer && (*directReturnValuePointer || isVoidReturnValue))); } //----------------------------------------------------------------------------------- diff --git a/src/PythonQtStdOut.cpp b/src/PythonQtStdOut.cpp index 775fb861..5160c235 100644 --- a/src/PythonQtStdOut.cpp +++ b/src/PythonQtStdOut.cpp @@ -47,6 +47,7 @@ static PyObject *PythonQtStdOutRedirect_new(PyTypeObject *type, PyObject * /*arg self = (PythonQtStdOutRedirect *)type->tp_alloc(type, 0); self->softspace = 0; + self->closed = false; self->_cb = NULL; return (PyObject *)self; @@ -117,6 +118,9 @@ static PyMemberDef PythonQtStdOutRedirect_members[] = { {const_cast("softspace"), T_INT, offsetof(PythonQtStdOutRedirect, softspace), 0, const_cast("soft space flag") }, + { const_cast("closed"), T_BOOL, offsetof(PythonQtStdOutRedirect, closed), 0, + const_cast("soft space flag") + }, {NULL} /* Sentinel */ }; diff --git a/src/PythonQtStdOut.h b/src/PythonQtStdOut.h index 01724deb..ea22f6ec 100644 --- a/src/PythonQtStdOut.h +++ b/src/PythonQtStdOut.h @@ -59,6 +59,7 @@ typedef struct { PyObject_HEAD PythonQtOutputChangedCB* _cb; int softspace; + bool closed; } PythonQtStdOutRedirect; #endif diff --git a/src/src.pro b/src/src.pro index eb44027a..09d2da6a 100644 --- a/src/src.pro +++ b/src/src.pro @@ -4,7 +4,7 @@ # $Source$ # -------------------------------------------------- -TARGET = PythonQt +TARGET = PythonQt-Qt5-PythonXY TEMPLATE = lib @@ -22,6 +22,8 @@ isEmpty(PYTHONQT_STATIC) { CONFIG += static } +DEFINES += PYTHONQT_CATCH_ALL_EXCEPTIONS + contains(QT_MAJOR_VERSION, 5) { QT += widgets core-private } @@ -33,6 +35,7 @@ INCLUDEPATH += $$PWD include ( ../build/common.prf ) include ( ../build/python.prf ) +TARGET = $$replace(TARGET, PythonXY, Python$${PYTHON_VERSION}) include ( src.pri )