diff --git a/src/PythonQt.cpp b/src/PythonQt.cpp index 6bd2dc2..a855d6d 100644 --- a/src/PythonQt.cpp +++ b/src/PythonQt.cpp @@ -140,6 +140,22 @@ void PythonQt::init(int flags) 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" + ,"qFuzzyCompare", "qMax","qMin","qRound","qRound64","qVersion","qrand","qsrand"}; + 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]; + } + } } void PythonQt::cleanup() @@ -1130,35 +1146,6 @@ void PythonQtPrivate::registerCPPClass(const char* typeName, const char* parentT } } -static PyObject *PythonQt_SIGNAL(PyObject * /*type*/, PyObject *args) -{ - const char* value; - if (!PyArg_ParseTuple(args, "s", &value)) { - return NULL; - } - // we do not prepend 0,1 or 2, why should we? - return PyString_FromString(QByteArray("2") + value); -} - -static PyObject *PythonQt_SLOT(PyObject * /*type*/, PyObject *args) -{ - const char* value; - if (!PyArg_ParseTuple(args, "s", &value)) { - return NULL; - } - // we do not prepend 0,1 or 2, why should we? - return PyString_FromString(QByteArray("1") + value); -} - -static PyMethodDef PythonQt_Qt_methods[] = { -{"SIGNAL", (PyCFunction)PythonQt_SIGNAL, METH_VARARGS, -"Returns a signal string" -}, -{"SLOT", (PyCFunction)PythonQt_SLOT, METH_VARARGS, -"Returns a slot string" -} -}; - PyObject* PythonQtPrivate::packageByName(const char* name) { if (name==NULL || name[0]==0) { @@ -1167,11 +1154,6 @@ PyObject* PythonQtPrivate::packageByName(const char* name) PyObject* v = _packages.value(name); if (!v) { v = PyImport_AddModule((QByteArray("PythonQt.") + name).constData()); - if (strcmp(name,"Qt")==0 || strcmp(name,"QtCore")==0) { - // add SIGNAL and SLOT functions - PyModule_AddObject(v, "SIGNAL", PyCFunction_New(PythonQt_Qt_methods, v)); - PyModule_AddObject(v, "SLOT", PyCFunction_New(PythonQt_Qt_methods+1, v)); - } _packages.insert(name, v); // AddObject steals the reference, so increment it! Py_INCREF(v); @@ -1180,6 +1162,12 @@ PyObject* PythonQtPrivate::packageByName(const char* name) return v; } +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(); +} PyObject* PythonQt::helpCalled(PythonQtClassInfo* info) { diff --git a/src/PythonQt.h b/src/PythonQt.h index eca607b..a80882f 100644 --- a/src/PythonQt.h +++ b/src/PythonQt.h @@ -470,6 +470,9 @@ public: //! the dummy tuple (which is empty and may be used to detected that a wrapper is called from internal wrapper creation static PyObject* dummyTuple(); + //! called by virtual overloads when a python return value can not be converted to the required Qt type + void handleVirtualOverloadReturnError(const char* signature, const PythonQtMethodInfo* methodInfo, PyObject* result); + private: //! create a new pythonqt class wrapper and place it in the pythonqt module diff --git a/src/PythonQtClassInfo.cpp b/src/PythonQtClassInfo.cpp index 76988eb..f8adabb 100644 --- a/src/PythonQtClassInfo.cpp +++ b/src/PythonQtClassInfo.cpp @@ -308,6 +308,7 @@ PythonQtMemberInfo PythonQtClassInfo::member(const char* memberName) } } if (!found) { + // maybe it is an enum wrapper? PyObject* p = findEnumWrapper(memberName); if (p) { info._type = PythonQtMemberInfo::EnumWrapper; @@ -317,6 +318,20 @@ PythonQtMemberInfo PythonQtClassInfo::member(const char* memberName) } } 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); diff --git a/src/PythonQtDoc.h b/src/PythonQtDoc.h index 9977eca..2de50e4 100644 --- a/src/PythonQtDoc.h +++ b/src/PythonQtDoc.h @@ -144,7 +144,7 @@ - PythonQt allows to communicate in both directions, e.g. calling a Python object from C++ AND calling a C++ method from Python, while PyQt only handles the Python->C++ direction - PythonQt offers properties as Python attributes, while PyQt offers them as setter/getter methods (e.g. QWidget.width is a property in PythonQt and a method in PyQt) - PythonQt does not support instanceof checks for Qt classes, except for the exact match and derived Python classes - - QObject.emit to emit Qt signals from Python is not yet implemented + - QObject.emit to emit Qt signals from Python is not yet implemented but PythonQt allows to just emit a signal by calling it - PythonQt does not offer to add new signals to Python/C++ objects - Ownership of objects is a bit different in PythonQt, currently Python classes derived from a C++ class need to be manually references in PythonQt to not get deleted too early (this will be fixed) - Probably there are lots of details that differ, I do not know PyQt that well to list them all. diff --git a/src/PythonQtMethodInfo.cpp b/src/PythonQtMethodInfo.cpp index 32885aa..f7962a8 100644 --- a/src/PythonQtMethodInfo.cpp +++ b/src/PythonQtMethodInfo.cpp @@ -58,14 +58,24 @@ PythonQtMethodInfo::PythonQtMethodInfo(const QMetaMethod& meta, PythonQtClassInf ParameterInfo type; fillParameterInfo(type, QByteArray(meta.typeName()), classInfo); _parameters.append(type); - QByteArray name; QList names = meta.parameterTypes(); - foreach (name, names) { + foreach (const QByteArray& name, names) { fillParameterInfo(type, name, classInfo); _parameters.append(type); } } +PythonQtMethodInfo::PythonQtMethodInfo(const QByteArray& typeName, const QList& args) +{ + ParameterInfo type; + fillParameterInfo(type, typeName, NULL); + _parameters.append(type); + foreach (const QByteArray& name, args) { + fillParameterInfo(type, name, NULL); + _parameters.append(type); + } +} + const PythonQtMethodInfo* PythonQtMethodInfo::getCachedMethodInfo(const QMetaMethod& signal, PythonQtClassInfo* classInfo) { QByteArray sig(signal.signature()); @@ -79,12 +89,25 @@ const PythonQtMethodInfo* PythonQtMethodInfo::getCachedMethodInfo(const QMetaMet return result; } -const PythonQtMethodInfo* PythonQtMethodInfo::getCachedMethodInfoFromMetaObjectAndSignature(const QMetaObject* metaObject, const char* signature) +const PythonQtMethodInfo* PythonQtMethodInfo::getCachedMethodInfoFromArgumentList(int numArgs, const char** args) { - QByteArray sig = QMetaObject::normalizedSignature(signature); - int idx = metaObject->indexOfMethod(sig); - QMetaMethod meta = metaObject->method(idx); - return PythonQtMethodInfo::getCachedMethodInfo(meta, NULL); + QByteArray typeName = args[0]; + QList arguments; + QByteArray fullSig = typeName; + fullSig += "("; + for (int i =1;i1) { + fullSig += ","; + } + arguments << QByteArray(args[i]); + } + fullSig += ")"; + PythonQtMethodInfo* result = _cachedSignatures.value(fullSig); + if (!result) { + result = new PythonQtMethodInfo(typeName, arguments); + _cachedSignatures.insert(fullSig, result); + } + return result; } void PythonQtMethodInfo::fillParameterInfo(ParameterInfo& type, const QByteArray& orgName, PythonQtClassInfo* classInfo) diff --git a/src/PythonQtMethodInfo.h b/src/PythonQtMethodInfo.h index c5f6f1d..178ce29 100644 --- a/src/PythonQtMethodInfo.h +++ b/src/PythonQtMethodInfo.h @@ -74,6 +74,7 @@ public: PythonQtMethodInfo() {}; ~PythonQtMethodInfo() {}; PythonQtMethodInfo(const QMetaMethod& meta, PythonQtClassInfo* classInfo); + PythonQtMethodInfo(const QByteArray& typeName, const QList& args); PythonQtMethodInfo(const PythonQtMethodInfo& other) { _parameters = other._parameters; } @@ -82,8 +83,8 @@ public: //! multiple requests for the same method, classInfo is passed to allow local enum resolution (if NULL is passed, no local enums are recognized) static const PythonQtMethodInfo* getCachedMethodInfo(const QMetaMethod& method, PythonQtClassInfo* classInfo); - //! get the cached method info by finding the meta method on the meta object via its signature, enums are only supported with leading namespace:: - static const PythonQtMethodInfo* getCachedMethodInfoFromMetaObjectAndSignature(const QMetaObject* metaObject, const char* signature); + //! get the cached method info using the passed in list of return value and arguments, return value needs to be passed as first arg + static const PythonQtMethodInfo* getCachedMethodInfoFromArgumentList(int numArgs, const char** args); //! cleanup the cache static void cleanupCachedMethodInfos(); diff --git a/src/PythonQtSignalReceiver.cpp b/src/PythonQtSignalReceiver.cpp index 860b079..46a5bda 100644 --- a/src/PythonQtSignalReceiver.cpp +++ b/src/PythonQtSignalReceiver.cpp @@ -48,7 +48,7 @@ #include "funcobject.h" void PythonQtSignalTarget::call(void **arguments) const { - PyObject* result = call(_callable, methodInfo(), arguments, false); + PyObject* result = call(_callable, methodInfo(), arguments); if (result) { Py_DECREF(result); } @@ -86,9 +86,6 @@ PyObject* PythonQtSignalTarget::call(PyObject* callable, const PythonQtMethodInf const PythonQtMethodInfo* m = methodInfos; // parameterCount includes return value: int count = m->parameterCount(); - if (skipFirstArgumentOfMethodInfo) { - count--; - } if (numPythonArgs!=-1) { if (count>numPythonArgs+1) { // take less arguments @@ -103,12 +100,8 @@ PyObject* PythonQtSignalTarget::call(PyObject* callable, const PythonQtMethodInf bool err = false; // transform Qt values to Python const QList& params = m->parameters(); - int skipFirstOffset = 0; - if (skipFirstArgumentOfMethodInfo) { - skipFirstOffset = 1; - } for (int i = 1; i < count; i++) { - const PythonQtMethodInfo::ParameterInfo& param = params.at(i + skipFirstOffset); + const PythonQtMethodInfo::ParameterInfo& param = params.at(i); PyObject* arg = PythonQtConv::ConvertQtValueToPython(param, arguments[i]); if (arg) { // steals reference, no unref diff --git a/src/PythonQtStdDecorators.h b/src/PythonQtStdDecorators.h index 6262ade..505bd91 100644 --- a/src/PythonQtStdDecorators.h +++ b/src/PythonQtStdDecorators.h @@ -71,6 +71,28 @@ public slots: void setParent(QObject* o, QObject* parent); QVariantList children(QObject* o); + + double static_Qt_qAbs(double a) { return qAbs(a); } + double static_Qt_qBound(double a,double b,double c) { return qBound(a,b,c); } + void static_Qt_qDebug(const QByteArray& msg) { qDebug(msg.constData()); } + // TODO: multi arg qDebug... + void static_Qt_qWarning(const QByteArray& msg) { qWarning(msg.constData()); } + // TODO: multi arg qWarning... + void static_Qt_qCritical(const QByteArray& msg) { qCritical(msg.constData()); } + // TODO: multi arg qCritical... + void static_Qt_qFatal(const QByteArray& msg) { qFatal(msg.constData()); } + // TODO: multi arg qFatal... + bool static_Qt_qFuzzyCompare(double a, double b) { return qFuzzyCompare(a, b); } + double static_Qt_qMax(double a, double b) { return qMax(a, b); } + double static_Qt_qMin(double a, double b) { return qMin(a, b); } + int static_Qt_qRound(double a) { return qRound(a); } + qint64 static_Qt_qRound64(double a) { return qRound64(a); } + const char* static_Qt_qVersion() { return qVersion(); } + int static_Qt_qrand() { return qrand(); } + void static_Qt_qsrand(uint a) { qsrand(a); } + + QByteArray static_Qt_SIGNAL(const QByteArray& s) { return QByteArray("2") + s; } + QByteArray static_Qt_SLOT(const QByteArray& s) { return QByteArray("1") + s; } //TODO: add findChild/findChildren/children/... };