diff --git a/src/PythonQt.cpp b/src/PythonQt.cpp index f97bdf1..2011de1 100644 --- a/src/PythonQt.cpp +++ b/src/PythonQt.cpp @@ -80,7 +80,7 @@ void PythonQt::init(int flags, const QByteArray& pythonQtModuleName) PythonQt_init_QtCoreBuiltin(NULL); PythonQt_init_QtGuiBuiltin(NULL); - + PythonQtRegisterToolClassesTemplateConverter(QByteArray); PythonQtRegisterToolClassesTemplateConverter(QDate); PythonQtRegisterToolClassesTemplateConverter(QTime); @@ -157,7 +157,7 @@ PythonQt::PythonQt(int flags, const QByteArray& pythonQtModuleName) } Py_Initialize(); } - + // 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; @@ -245,6 +245,14 @@ void PythonQtPrivate::registerClass(const QMetaObject* metaobject, const char* p PythonQtClassInfo* parentInfo = lookupClassInfoAndCreateIfNotPresent(m->superClass()->className()); info->addParentClass(PythonQtClassInfo::ParentClassInfo(parentInfo)); } + } 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); } if (first) { first = false; @@ -913,6 +921,7 @@ PythonQtPrivate::PythonQtPrivate() _noLongerWrappedCB = NULL; _wrappedCB = NULL; _currentClassInfoForClassWrapperCreation = NULL; + _profilingCB = NULL; } void PythonQtPrivate::setupSharedLibrarySuffixes() @@ -922,6 +931,14 @@ void PythonQtPrivate::setupSharedLibrarySuffixes() imp.setNewRef(PyImport_ImportModule("imp")); int cExtensionCode = imp.getVariable("C_EXTENSION").toInt(); QVariant result = imp.call("get_suffixes"); +#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 foreach (QVariant entry, result.toList()) { QVariantList suffixEntry = entry.toList(); if (suffixEntry.count()==3) { @@ -1066,6 +1083,10 @@ void PythonQt::setQObjectNoLongerWrappedCallback(PythonQtQObjectNoLongerWrappedC _p->_noLongerWrappedCB = cb; } +void PythonQt::setProfilingCallback(ProfilingCB* cb) +{ + _p->_profilingCB = cb; +} static PyMethodDef PythonQtMethods[] = { @@ -1080,7 +1101,7 @@ void PythonQt::initPythonQtModule(bool redirectStdOut, const QByteArray& pythonQ } _p->_pythonQtModule = Py_InitModule(name.constData(), PythonQtMethods); _p->_pythonQtModuleName = name; - + if (redirectStdOut) { PythonQtObjectPtr sys; PythonQtObjectPtr out; @@ -1194,6 +1215,13 @@ PyObject* PythonQt::helpCalled(PythonQtClassInfo* info) } } +void PythonQt::clearNotFoundCachedMembers() +{ + foreach(PythonQtClassInfo* info, _p->_knownClassInfos) { + info->clearNotFoundCachedMembers(); + } +} + void PythonQtPrivate::removeWrapperPointer(void* obj) { _wrappedObjects.remove(obj); diff --git a/src/PythonQt.h b/src/PythonQt.h index 050dadc..a4d53bb 100644 --- a/src/PythonQt.h +++ b/src/PythonQt.h @@ -75,7 +75,7 @@ template void PythonQtSetInstanceWrapperOnShell(void* object, PythonQtI //! returns the offset that needs to be added to upcast an object of type T1 to T2 template int PythonQtUpcastingOffset() { - return (((char*)(static_cast(reinterpret_cast(0x100)))) - ((char*)reinterpret_cast(0x100))); + return (((char*)(static_cast(reinterpret_cast(0x100)))) - ((char*)reinterpret_cast(0x100))); } //! callback to create a QObject lazily @@ -143,10 +143,20 @@ public: }; + //! enum for profiling callback + enum ProfilingCallbackState { + Enter = 1, + Leave = 2 + }; + + //! callback for profiling. className and methodName are only passed when state == Enter, otherwise + //! they are NULL. + typedef void ProfilingCB(ProfilingCallbackState state, const char* className, const char* methodName); + //--------------------------------------------------------------------------- //! \name Singleton Initialization //@{ - + //! initialize the python qt binding (flags are a or combination of PythonQt::InitFlags), if \c pythonQtModuleName is given //! it defines the name of the python module that PythonQt will add, otherwise "PythonQt" is used. //! This can be used to e.g. pass in PySide or PyQt4 to make it more compatible. @@ -159,7 +169,7 @@ public: static PythonQt* self() { return _self; } //@} - + //! defines the object types for introspection enum ObjectType { Class, @@ -186,13 +196,13 @@ public: //! to a module later on. //! The user needs to make sure that the \c name is unique in the python module dictionary. PythonQtObjectPtr createModuleFromFile(const QString& name, const QString& filename); - + //! creates the new module \c name and evaluates the given script in the context of that module. //! If the \c script is empty, the module contains no initial code. You can use evalScript/evalCode to add code //! to a module later on. //! The user needs to make sure that the \c name is unique in the python module dictionary. PythonQtObjectPtr createModuleFromScript(const QString& name, const QString& script = QString()); - + //! create a uniquely named module, you can use evalFile or evalScript to populate the module with //! script code PythonQtObjectPtr createUniqueModule(); @@ -213,20 +223,20 @@ public: void setModuleImportPath(PyObject* module, const QStringList& paths); //@} - + //--------------------------------------------------------------------------- //! \name Registering Classes //@{ - + //! registers a QObject derived class to PythonQt (this is implicitly called by addObject as well) /* Since Qt4 does not offer a way to detect if a given classname is derived from QObject and thus has a QMetaObject, you MUST register all your QObject derived classes here when you want them to be detected in signal and slot calls */ void registerClass(const QMetaObject* metaobject, const char* package = NULL, PythonQtQObjectCreatorFunctionCB* wrapperCreator = NULL, PythonQtShellSetInstanceWrapperCB* shell = NULL); - + //! add a wrapper object for the given QMetaType typeName, also does an addClassDecorators() to add constructors for variants //! (ownership of wrapper is passed to PythonQt) /*! Make sure that you have done a qRegisterMetaType first, if typeName is a user type! - + This will add a wrapper object that is used to make calls to the given classname \c typeName. All slots that take a pointer to typeName as the first argument will be callable from Python on a variant object that contains such a type. @@ -251,7 +261,7 @@ public: //--------------------------------------------------------------------------- //! \name Script Parsing and Evaluation //@{ - + //! parses the given file and returns the python code object, this can then be used to call evalCode() PythonQtObjectPtr parseFile(const QString& filename); @@ -287,7 +297,7 @@ public: //--------------------------------------------------------------------------- //! \name Variable access - //@{ + //@{ //! add the given \c qObject to the python \c object as a variable with \c name (it can be removed via clearVariable) void addObject(PyObject* object, const QString& name, QObject* qObject); @@ -312,7 +322,7 @@ public: //--------------------------------------------------------------------------- //! \name Calling Python Objects - //@{ + //@{ //! call the given python \c callable in the scope of object, returns the result converted to a QVariant QVariant call(PyObject* object, const QString& callable, const QVariantList& args = QVariantList()); @@ -385,10 +395,10 @@ public: //--------------------------------------------------------------------------- //! \name Custom Importer //@{ - + //! replace the internal import implementation and use the supplied interface to load files (both py and pyc files) //! (this method should be called directly after initialization of init() and before calling overwriteSysPath(). - //! On the first call to this method, it will install a generic PythonQt importer in Pythons "path_hooks". + //! On the first call to this method, it will install a generic PythonQt importer in Pythons "path_hooks". //! This is not reversible, so even setting setImporter(NULL) afterwards will //! keep the custom PythonQt importer with a QFile default import interface. //! Subsequent python import calls will make use of the passed importInterface @@ -427,6 +437,10 @@ public: //! The error is currently just output to the python stderr, future version might implement better trace printing bool handleError(); + //! clear all NotFound entries on all class infos, to ensure that + //! newly loaded wrappers can add methods even when the object was wrapped by PythonQt before the wrapper was loaded + void clearNotFoundCachedMembers(); + //! set a callback that is called when a QObject with parent == NULL is wrapped by pythonqt void setQObjectWrappedCallback(PythonQtQObjectWrappedCB* cb); //! set a callback that is called when a QObject with parent == NULL is no longer wrapped by pythonqt @@ -437,11 +451,14 @@ public: //! called by internal help methods PyObject* helpCalled(PythonQtClassInfo* info); - + //! returns the found object or NULL //! @return new reference PythonQtObjectPtr lookupObject(PyObject* module, const QString& name); + //! sets a callback that is called before and after function calls for profiling + void setProfilingCallback(ProfilingCB* cb); + //@} signals: @@ -507,7 +524,7 @@ public: //! add a handler for polymorphic downcasting void addPolymorphicHandler(const char* typeName, PythonQtPolymorphicHandlerCB* cb); - + //! lookup existing classinfo and return new if not yet present PythonQtClassInfo* lookupClassInfoAndCreateIfNotPresent(const char* typeName); @@ -528,13 +545,13 @@ public: //! add a wrapper object for the given QMetaType typeName, also does an addClassDecorators() to add constructors for variants //! (ownership of wrapper is passed to PythonQt) /*! Make sure that you have done a qRegisterMetaType first, if typeName is a user type! - + This will add a wrapper object that is used to make calls to the given classname \c typeName. All slots that take a pointer to typeName as the first argument will be callable from Python on a variant object that contains such a type. */ void registerCPPClass(const char* typeName, const char* parentTypeName = NULL, const char* package = NULL, PythonQtQObjectCreatorFunctionCB* wrapperCreator = NULL, PythonQtShellSetInstanceWrapperCB* shell = NULL, PyObject* module = NULL, int typeSlots = 0); - + //! as an alternative to registerClass, you can tell PythonQt the names of QObject derived classes //! and it will register the classes when it first sees a pointer to such a derived class void registerQObjectClassNames(const QStringList& names); @@ -571,10 +588,13 @@ public: //! 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); - + //! get access to the PythonQt module PythonQtObjectPtr pythonQtModule() const { return _pythonQtModule; } + //! returns the profiling callback, which may be NULL + PythonQt::ProfilingCB* profilingCB() const { return _profilingCB; } + private: //! Setup the shared library suffixes by getting them from the "imp" module. void setupSharedLibrarySuffixes(); @@ -605,13 +625,13 @@ private: //! the name of the PythonQt python module QByteArray _pythonQtModuleName; - + //! the importer interface (if set) PythonQtImportFileInterface* _importInterface; //! the default importer PythonQtQFileImporter* _defaultImporter; - + PythonQtQObjectNoLongerWrappedCB* _noLongerWrappedCB; PythonQtQObjectWrappedCB* _wrappedCB; @@ -625,6 +645,8 @@ private: PythonQtClassInfo* _currentClassInfoForClassWrapperCreation; + PythonQt::ProfilingCB* _profilingCB; + int _initFlags; int _PythonQtObjectPtr_metaId; diff --git a/src/PythonQtClassInfo.cpp b/src/PythonQtClassInfo.cpp index b201ce3..287f95f 100644 --- a/src/PythonQtClassInfo.cpp +++ b/src/PythonQtClassInfo.cpp @@ -847,3 +847,22 @@ PyObject* PythonQtClassInfo::findEnumWrapper(const char* name) { 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 it(_cachedMembers); + while (it.hasNext()) { + it.next(); + if (it.value()._type == PythonQtMemberInfo::NotFound) { + it.remove(); + } + } +} diff --git a/src/PythonQtClassInfo.h b/src/PythonQtClassInfo.h index 463b07e..72e032a 100644 --- a/src/PythonQtClassInfo.h +++ b/src/PythonQtClassInfo.h @@ -166,7 +166,7 @@ public: int metaTypeId() { return _metaTypeId; } //! set an additional decorator provider that offers additional decorator slots for this class - void setDecoratorProvider(PythonQtQObjectCreatorFunctionCB* cb) { _decoratorProviderCB = cb; _decoratorProvider = NULL; } + void setDecoratorProvider(PythonQtQObjectCreatorFunctionCB* cb); //! get the decorator qobject instance QObject* decorator(); @@ -201,7 +201,10 @@ public: //! returns if the localScope has an enum of that type name or if the enum contains a :: scope, if that class contails the enum static PyObject* findEnumWrapper(const QByteArray& name, PythonQtClassInfo* localScope, bool* isLocalEnum = NULL); - + + //! clear all members that where cached as "NotFound" + void clearNotFoundCachedMembers(); + private: void createEnumWrappers(); void createEnumWrappers(const QMetaObject* meta); @@ -225,7 +228,7 @@ private: PythonQtSlotInfo* findDecoratorSlots(const char* memberName, int memberNameLen, PythonQtSlotInfo* tail, bool &found, QHash& memberCache, int upcastingOffset); int findCharOffset(const char* sigStart, char someChar); - + QHash _cachedMembers; PythonQtSlotInfo* _constructors; diff --git a/src/PythonQtClassWrapper.cpp b/src/PythonQtClassWrapper.cpp index 4c3e280..561074f 100644 --- a/src/PythonQtClassWrapper.cpp +++ b/src/PythonQtClassWrapper.cpp @@ -266,6 +266,31 @@ static PyObject *PythonQtClassWrapper_help(PythonQtClassWrapper* type) return PythonQt::self()->helpCalled(type->classInfo()); } +PyObject *PythonQtClassWrapper_delete(PythonQtClassWrapper *type, PyObject *args) +{ + Q_UNUSED(type); + + Py_ssize_t argc = PyTuple_Size(args); + if (argc>0) { + PyObject* self = PyTuple_GET_ITEM(args, 0); + if (PyObject_TypeCheck(self, &PythonQtInstanceWrapper_Type)) { + return PythonQtInstanceWrapper_delete((PythonQtInstanceWrapper*)self); + } + } + return NULL; +} + +PyObject *PythonQtClassWrapper_inherits(PythonQtClassWrapper *type, PyObject *args) +{ + Q_UNUSED(type); + PythonQtInstanceWrapper* wrapper = NULL; + char *name = NULL; + if (!PyArg_ParseTuple(args, "O!s:PythonQtClassWrapper.inherits",&PythonQtInstanceWrapper_Type, &wrapper, &name)) { + return NULL; + } + return PythonQtConv::GetPyBool(wrapper->classInfo()->inherits(name)); +} + PyObject *PythonQtClassWrapper__init__(PythonQtClassWrapper *type, PyObject *args) { Py_ssize_t argc = PyTuple_Size(args); @@ -295,16 +320,23 @@ PyObject *PythonQtClassWrapper__init__(PythonQtClassWrapper *type, PyObject *arg return NULL; } + static PyMethodDef PythonQtClassWrapper_methods[] = { {"__init__", (PyCFunction)PythonQtClassWrapper__init__, METH_VARARGS, - "Return the classname of the object" + "Init function" }, {"className", (PyCFunction)PythonQtClassWrapper_classname, METH_NOARGS, "Return the classname of the object" }, + {"inherits", (PyCFunction)PythonQtClassWrapper_inherits, METH_VARARGS, + "Returns if the class inherits or is of given type name" + }, {"help", (PyCFunction)PythonQtClassWrapper_help, METH_NOARGS, "Shows the help of available methods for this class" }, + {"delete", (PyCFunction)PythonQtClassWrapper_delete, METH_VARARGS, + "Deletes the given C++ object" + }, {NULL, NULL, 0 , NULL} /* Sentinel */ }; @@ -313,7 +345,7 @@ static PyObject *PythonQtClassWrapper_getattro(PyObject *obj, PyObject *name) { const char *attributeName; PythonQtClassWrapper *wrapper = (PythonQtClassWrapper *)obj; - + if ((attributeName = PyString_AsString(name)) == NULL) { return NULL; } @@ -328,7 +360,7 @@ static PyObject *PythonQtClassWrapper_getattro(PyObject *obj, PyObject *name) return dict; } dict = PyDict_Copy(dict); - + QStringList l = wrapper->classInfo()->memberList(false); foreach (QString name, l) { PyObject* o = PyObject_GetAttrString(obj, name.toLatin1().data()); @@ -344,7 +376,7 @@ static PyObject *PythonQtClassWrapper_getattro(PyObject *obj, PyObject *name) PyDict_SetItemString(dict, "__init__", func); Py_DECREF(func); } - for (int i = 1;i<3;i++) { + for (int i = 1; PythonQtClassWrapper_methods[i].ml_name != NULL; i++) { PyObject* func = PyCFunction_New(&PythonQtClassWrapper_methods[i], obj); PyDict_SetItemString(dict, PythonQtClassWrapper_methods[i].ml_name, func); Py_DECREF(func); @@ -396,7 +428,7 @@ static PyObject * PythonQtClassWrapper_repr(PyObject * obj) { PythonQtClassWrapper* wrapper = (PythonQtClassWrapper*)obj; if (wrapper->classInfo()->isCPPWrapper()) { - const QMetaObject* meta = wrapper->classInfo()->metaObject(); + const QMetaObject* meta = wrapper->classInfo()->metaObject(); if (!meta) { QObject* decorator = wrapper->classInfo()->decorator(); if (decorator) { diff --git a/src/PythonQtConversion.cpp b/src/PythonQtConversion.cpp index 1e3523f..64f7bcb 100644 --- a/src/PythonQtConversion.cpp +++ b/src/PythonQtConversion.cpp @@ -47,7 +47,7 @@ PythonQtValueStorage PythonQtConv::global_valueStorage; PythonQtValueStorage PythonQtConv::global_ptrStorage; -PythonQtValueStorage PythonQtConv::global_variantStorage; +PythonQtValueStorageWithCleanup PythonQtConv::global_variantStorage; QHash PythonQtConv::_metaTypeToPythonConverters; QHash PythonQtConv::_pythonToMetaTypeConverters; diff --git a/src/PythonQtConversion.h b/src/PythonQtConversion.h index 23aaa72..12b5e7a 100644 --- a/src/PythonQtConversion.h +++ b/src/PythonQtConversion.h @@ -144,7 +144,7 @@ public: static PythonQtValueStorage global_valueStorage; static PythonQtValueStorage global_ptrStorage; - static PythonQtValueStorage global_variantStorage; + static PythonQtValueStorageWithCleanup global_variantStorage; protected: static QHash _metaTypeToPythonConverters; diff --git a/src/PythonQtCppWrapperFactory.h b/src/PythonQtCppWrapperFactory.h index ce3afc4..6d1b3e2 100644 --- a/src/PythonQtCppWrapperFactory.h +++ b/src/PythonQtCppWrapperFactory.h @@ -58,4 +58,4 @@ public: }; -#endif \ No newline at end of file +#endif diff --git a/src/PythonQtImportFileInterface.h b/src/PythonQtImportFileInterface.h index 7619436..f89bb46 100644 --- a/src/PythonQtImportFileInterface.h +++ b/src/PythonQtImportFileInterface.h @@ -46,7 +46,7 @@ #include #include -//! Defines an abstract interface to file access for the Python import statement. +//! Defines an abstract interface to file access for the Python import statement. //! see PythonQt::setImporter() class PythonQtImportFileInterface { @@ -67,6 +67,9 @@ public: //! get the last modified data of a file virtual QDateTime lastModifiedDate(const QString& filename) = 0; + //! indicates that *.py files which are newer than their corresponding *.pyc files + //! are ignored + virtual bool ignoreUpdatedPythonSourceFiles() { return false; } }; -#endif \ No newline at end of file +#endif diff --git a/src/PythonQtImporter.cpp b/src/PythonQtImporter.cpp index 2046ff9..4a6434b 100644 --- a/src/PythonQtImporter.cpp +++ b/src/PythonQtImporter.cpp @@ -114,6 +114,7 @@ PythonQtImport::ModuleInfo PythonQtImport::getModuleInfo(PythonQtImporter* self, info.fullPath = test; info.moduleName = subname; info.type = MI_SHAREDLIBRARY; + return info; } } return info; @@ -263,7 +264,7 @@ PythonQtImporter_load_module(PyObject *obj, PyObject *args) } else { PythonQtObjectPtr imp; imp.setNewRef(PyImport_ImportModule("imp")); - + // Create a PyList with the current path as its single element, // which is required for find_module (it won't accept a tuple...) PythonQtObjectPtr pathList; @@ -276,12 +277,21 @@ PythonQtImporter_load_module(PyObject *obj, PyObject *args) args.append(QVariant::fromValue(pathList)); QVariant result = imp.call("find_module", args); if (result.isValid()) { - // This will return a tuple with (file, pathname, description) + // This will return a tuple with (file, pathname, description=(suffix,mode,type)) QVariantList list = result.toList(); if (list.count()==3) { // We prepend the full module name (including package prefix) list.prepend(fullname); - // And call "load_module" with (fullname, file, pathname, description) +#ifdef __linux + #ifdef _DEBUG + // imp_find_module() does not respect the debug suffix '_d' on Linux, + // so it does not return the correct file path and we correct it now + // find_module opened a file to the release library, but that file handle is + // ignored on Linux and Windows, maybe on MacOS also. + list[2] = info.fullPath; + #endif +#endif + // And call "load_module" with (fullname, file, pathname, description=(suffix,mode,type)) PythonQtObjectPtr module = imp.call("load_module", list); mod = module.object(); if (mod) { @@ -559,12 +569,16 @@ PythonQtImport::unmarshalCode(const QString& path, const QByteArray& data, time_ return Py_None; } - if (mtime != 0 && !(getLong((unsigned char *)buf + 4) == mtime)) { - if (Py_VerboseFlag) - PySys_WriteStderr("# %s has bad mtime\n", - path.toLatin1().constData()); - Py_INCREF(Py_None); - return Py_None; + if (mtime != 0) { + time_t timeDiff = getLong((unsigned char *)buf + 4) - mtime; + if (timeDiff<0) { timeDiff = -timeDiff; } + if (timeDiff > 1) { + if (Py_VerboseFlag) + PySys_WriteStderr("# %s has bad mtime\n", + path.toLatin1().constData()); + Py_INCREF(Py_None); + return Py_None; + } } code = PyMarshal_ReadObjectFromString(buf + 8, size - 8); @@ -645,7 +659,10 @@ PythonQtImport::getMTimeOfSource(const QString& path) path2.truncate(path.length()-1); if (PythonQt::importInterface()->exists(path2)) { - mtime = PythonQt::importInterface()->lastModifiedDate(path2).toTime_t(); + QDateTime t = PythonQt::importInterface()->lastModifiedDate(path2); + if (t.isValid()) { + mtime = t.toTime_t(); + } } return mtime; @@ -675,7 +692,12 @@ PythonQtImport::getModuleCode(PythonQtImporter *self, const char* fullname, QStr int ispackage = zso->type & IS_PACKAGE; int isbytecode = zso->type & IS_BYTECODE; - if (isbytecode) { + // if ignoreUpdatedPythonSourceFiles() returns true, then mtime stays 0 + // and unmarshalCode() in getCodeFromData() will always read an existing *.pyc file, + // even if a newer *.py file exists. This is a release optimization where + // typically only *.pyc files are delivered without *.py files and reading file + // modification time is slow. + if (isbytecode && !PythonQt::importInterface()->ignoreUpdatedPythonSourceFiles()) { mtime = getMTimeOfSource(test); } code = getCodeFromData(test, isbytecode, ispackage, mtime); @@ -713,7 +735,14 @@ PyObject* PythonQtImport::getCodeFromPyc(const QString& file) QString pyc = replaceExtension(file, pycStr); if (PythonQt::importInterface()->exists(pyc)) { time_t mtime = 0; - mtime = getMTimeOfSource(pyc); + // if ignoreUpdatedPythonSourceFiles() returns true, then mtime stays 0 + // and unmarshalCode() in getCodeFromData() will always read an existing *.pyc file, + // even if a newer *.py file exists. This is a release optimization where + // typically only *.pyc files are delivered without *.py files and reading file + // modification time is slow. + if (!PythonQt::importInterface()->ignoreUpdatedPythonSourceFiles()) { + mtime = getMTimeOfSource(pyc); + } code = getCodeFromData(pyc, true, false, mtime); if (code != Py_None && code != NULL) { return code; diff --git a/src/PythonQtInstanceWrapper.cpp b/src/PythonQtInstanceWrapper.cpp index 77b45fa..b9eb052 100644 --- a/src/PythonQtInstanceWrapper.cpp +++ b/src/PythonQtInstanceWrapper.cpp @@ -288,12 +288,21 @@ static PyObject *PythonQtInstanceWrapper_classname(PythonQtInstanceWrapper* obj) return PyString_FromString(obj->ob_type->tp_name); } +PyObject *PythonQtInstanceWrapper_inherits(PythonQtInstanceWrapper* obj, PyObject *args) +{ + char *name = NULL; + if (!PyArg_ParseTuple(args, "s:PythonQtInstanceWrapper.inherits",&name)) { + return NULL; + } + return PythonQtConv::GetPyBool(obj->classInfo()->inherits(name)); +} + static PyObject *PythonQtInstanceWrapper_help(PythonQtInstanceWrapper* obj) { return PythonQt::self()->helpCalled(obj->classInfo()); } -static PyObject *PythonQtInstanceWrapper_delete(PythonQtInstanceWrapper * self) +PyObject *PythonQtInstanceWrapper_delete(PythonQtInstanceWrapper * self) { PythonQtInstanceWrapper_deleteObject(self, true); Py_INCREF(Py_None); @@ -305,6 +314,9 @@ static PyMethodDef PythonQtInstanceWrapper_methods[] = { {"className", (PyCFunction)PythonQtInstanceWrapper_classname, METH_NOARGS, "Return the classname of the object" }, + {"inherits", (PyCFunction)PythonQtInstanceWrapper_inherits, METH_VARARGS, + "Returns if the class inherits or is of given type name" + }, {"help", (PyCFunction)PythonQtInstanceWrapper_help, METH_NOARGS, "Shows the help of available methods for this class" }, @@ -371,7 +383,23 @@ static PyObject *PythonQtInstanceWrapper_getattro(PyObject *obj,PyObject *name) case PythonQtMemberInfo::Property: if (wrapper->_obj) { if (member._property.userType() != QVariant::Invalid) { - return PythonQtConv::QVariantToPyObject(member._property.read(wrapper->_obj)); + + PythonQt::ProfilingCB* profilingCB = PythonQt::priv()->profilingCB(); + if (profilingCB) { + QString methodName = "getProperty("; + methodName += attributeName; + methodName += ")"; + profilingCB(PythonQt::Enter, wrapper->_obj->metaObject()->className(), methodName.toLatin1()); + } + + PyObject* value = PythonQtConv::QVariantToPyObject(member._property.read(wrapper->_obj)); + + if (profilingCB) { + profilingCB(PythonQt::Leave, NULL, NULL); + } + + return value; + } else { Py_INCREF(Py_None); return Py_None; @@ -475,7 +503,19 @@ static int PythonQtInstanceWrapper_setattro(PyObject *obj,PyObject *name,PyObjec } bool success = false; if (v.isValid()) { + PythonQt::ProfilingCB* profilingCB = PythonQt::priv()->profilingCB(); + if (profilingCB) { + QString methodName = "setProperty("; + methodName += attributeName; + methodName += ")"; + profilingCB(PythonQt::Enter, wrapper->_obj->metaObject()->className(), methodName.toLatin1()); + } + success = prop.write(wrapper->_obj, v); + + if (profilingCB) { + profilingCB(PythonQt::Leave, NULL, NULL); + } } if (success) { return 0; @@ -605,17 +645,17 @@ static PyObject * PythonQtInstanceWrapper_repr(PyObject * obj) if (str.startsWith(typeName)) { return PyString_FromFormat("%s", str.toLatin1().constData()); } else { - return PyString_FromFormat("%s(%s, %p)", typeName, str.toLatin1().constData(), wrapper->_wrappedPtr); + return PyString_FromFormat("%s (%s, at: %p)", typeName, str.toLatin1().constData(), wrapper->_wrappedPtr ? wrapper->_wrappedPtr : qobj); } } if (wrapper->_wrappedPtr) { if (wrapper->_obj) { - return PyString_FromFormat("%s (C++ Object %p wrapped by %s %p))", typeName, wrapper->_wrappedPtr, wrapper->_obj->metaObject()->className(), qobj); + return PyString_FromFormat("%s (C++ object at: %p wrapped by %s at: %p)", typeName, wrapper->_wrappedPtr, wrapper->_obj->metaObject()->className(), qobj); } else { - return PyString_FromFormat("%s (C++ Object %p)", typeName, wrapper->_wrappedPtr); + return PyString_FromFormat("%s (C++ object at: %p)", typeName, wrapper->_wrappedPtr); } } else { - return PyString_FromFormat("%s (%s %p)", typeName, wrapper->classInfo()->className(), qobj); + return PyString_FromFormat("%s (%s at: %p)", typeName, wrapper->classInfo()->className(), qobj); } } diff --git a/src/PythonQtInstanceWrapper.h b/src/PythonQtInstanceWrapper.h index 18ffd0a..ded5867 100644 --- a/src/PythonQtInstanceWrapper.h +++ b/src/PythonQtInstanceWrapper.h @@ -90,9 +90,11 @@ typedef struct PythonQtInstanceWrapperStruct { //! stores if the object is a shell instance bool _isShellInstance; - + } PythonQtInstanceWrapper; int PythonQtInstanceWrapper_init(PythonQtInstanceWrapper * self, PyObject * args, PyObject * kwds); -#endif \ No newline at end of file +PyObject *PythonQtInstanceWrapper_delete(PythonQtInstanceWrapper * self); + +#endif diff --git a/src/PythonQtMethodInfo.cpp b/src/PythonQtMethodInfo.cpp index 740e4ab..dfa248d 100644 --- a/src/PythonQtMethodInfo.cpp +++ b/src/PythonQtMethodInfo.cpp @@ -192,7 +192,6 @@ int PythonQtMethodInfo::nameToType(const char* name) _parameterTypeDict.insert("qreal", QMetaType::Double); _parameterTypeDict.insert("QChar", QMetaType::QChar); _parameterTypeDict.insert("QByteArray", QMetaType::QByteArray); - _parameterTypeDict.insert("Q3CString", QMetaType::QByteArray); _parameterTypeDict.insert("QString", QMetaType::QString); _parameterTypeDict.insert("", QMetaType::Void); _parameterTypeDict.insert("void", QMetaType::Void); @@ -203,7 +202,6 @@ int PythonQtMethodInfo::nameToType(const char* name) _parameterTypeDict.insert("qulonglong", QMetaType::ULongLong); _parameterTypeDict.insert("qint64", QMetaType::LongLong); _parameterTypeDict.insert("quint64", QMetaType::ULongLong); - _parameterTypeDict.insert("QIconSet", QMetaType::QIcon); _parameterTypeDict.insert("QVariantMap", QMetaType::QVariantMap); _parameterTypeDict.insert("QVariantList", QMetaType::QVariantList); _parameterTypeDict.insert("QMap", QMetaType::QVariantMap); @@ -216,15 +214,14 @@ int PythonQtMethodInfo::nameToType(const char* name) _parameterTypeDict.insert("QUrl", QMetaType::QUrl); _parameterTypeDict.insert("QLocale", QMetaType::QLocale); _parameterTypeDict.insert("QRect", QMetaType::QRect); - _parameterTypeDict.insert("QRectf", QMetaType::QRectF); + _parameterTypeDict.insert("QRectF", QMetaType::QRectF); _parameterTypeDict.insert("QSize", QMetaType::QSize); - _parameterTypeDict.insert("QSizef", QMetaType::QSizeF); + _parameterTypeDict.insert("QSizeF", QMetaType::QSizeF); _parameterTypeDict.insert("QLine", QMetaType::QLine); - _parameterTypeDict.insert("QLinef", QMetaType::QLineF); + _parameterTypeDict.insert("QLineF", QMetaType::QLineF); _parameterTypeDict.insert("QPoint", QMetaType::QPoint); - _parameterTypeDict.insert("QPointf", QMetaType::QPointF); + _parameterTypeDict.insert("QPointF", QMetaType::QPointF); _parameterTypeDict.insert("QRegExp", QMetaType::QRegExp); -// _parameterTypeDict.insert("QColorGroup", QMetaType::QColorGroup); _parameterTypeDict.insert("QFont", QMetaType::QFont); _parameterTypeDict.insert("QPixmap", QMetaType::QPixmap); _parameterTypeDict.insert("QBrush", QMetaType::QBrush); @@ -232,7 +229,7 @@ int PythonQtMethodInfo::nameToType(const char* name) _parameterTypeDict.insert("QCursor", QMetaType::QCursor); _parameterTypeDict.insert("QPalette", QMetaType::QPalette); _parameterTypeDict.insert("QIcon", QMetaType::QIcon); - _parameterTypeDict.insert("QImage", QMetaType::QPolygon); + _parameterTypeDict.insert("QImage", QMetaType::QImage); _parameterTypeDict.insert("QRegion", QMetaType::QRegion); _parameterTypeDict.insert("QBitmap", QMetaType::QBitmap); _parameterTypeDict.insert("QSizePolicy", QMetaType::QSizePolicy); diff --git a/src/PythonQtMisc.h b/src/PythonQtMisc.h index 664aa1f..a895184 100644 --- a/src/PythonQtMisc.h +++ b/src/PythonQtMisc.h @@ -89,13 +89,6 @@ public: _chunks.clear(); } - //! reset the storage to 0 (without freeing memory, thus caching old entries for reuse) - void reset() { - _chunkIdx = 0; - _chunkOffset = 0; - _currentChunk = _chunks.at(0); - } - //! get the current position to be restored with setPos void getPos(PythonQtValueStoragePosition & pos) { pos.chunkIdx = _chunkIdx; @@ -129,7 +122,7 @@ public: return newEntry; }; -private: +protected: QList _chunks; int _chunkIdx; @@ -138,5 +131,45 @@ private: }; +//! a helper class that stores basic C++ value types in chunks and clears the unused values on setPos() usage. +template class PythonQtValueStorageWithCleanup : public PythonQtValueStorage +{ +public: + void setPos(const PythonQtValueStoragePosition& pos) { + if (_chunkIdx > pos.chunkIdx) { + T* firstChunk = _chunks.at(pos.chunkIdx); + // clear region in first chunk + for (int i = pos.chunkOffset; i < chunkEntries; i++) { + firstChunk[i] = T(); + } + for (int chunk = pos.chunkIdx + 1; chunk < _chunkIdx; chunk++) { + // clear the full chunks between the first and last chunk + T* fullChunk = _chunks.at(chunk); + for (int i = 0; i < chunkEntries; i++) { + fullChunk[i] = T(); + } + } + // clear region in last chunk + T* lastChunk = _chunks.at(_chunkIdx); + for (int i = 0; i < _chunkOffset; i++) { + lastChunk[i] = T(); + } + } else if (_chunkIdx == pos.chunkIdx) { + // clear the region in the last chunk only + T* lastChunk = _chunks.at(_chunkIdx); + for (int i = pos.chunkOffset; i<_chunkOffset; i++) { + lastChunk[i] = T(); + } + } + + PythonQtValueStorage::setPos(pos); + } + +private: + using PythonQtValueStorage::_chunks; + using PythonQtValueStorage::_chunkIdx; + using PythonQtValueStorage::_chunkOffset; + using PythonQtValueStorage::_currentChunk; +}; #endif diff --git a/src/PythonQtObjectPtr.h b/src/PythonQtObjectPtr.h index 12d9d24..6d5bf65 100644 --- a/src/PythonQtObjectPtr.h +++ b/src/PythonQtObjectPtr.h @@ -57,21 +57,21 @@ public: setObject(p.object()); } - //! If the given variant holds a PythonQtObjectPtr, extract the value from it and hold onto the reference. This results in an increment of the reference count. + //! If the given variant holds a PythonQtObjectPtr, extract the value from it and hold onto the reference. This results in an increment of the reference count. PythonQtObjectPtr(const QVariant& variant):_object(NULL) { - fromVariant(variant); + fromVariant(variant); } - + PythonQtObjectPtr(PyObject* o) { _object = o; if (o) Py_INCREF(_object); } - ~PythonQtObjectPtr() { if (_object) Py_DECREF(_object); } - - //! If the given variant holds a PythonQtObjectPtr, extract the value from it and hold onto the reference. This results in an increment of the reference count. + ~PythonQtObjectPtr() { if (_object) { Py_DECREF(_object); } } + + //! If the given variant holds a PythonQtObjectPtr, extract the value from it and hold onto the reference. This results in an increment of the reference count. bool fromVariant(const QVariant& variant); - + PythonQtObjectPtr &operator=(const PythonQtObjectPtr &p) { setObject(p.object()); return *this; @@ -82,13 +82,13 @@ public: return *this; } - + PythonQtObjectPtr &operator=(const QVariant& variant) { fromVariant(variant); return *this; } - + bool operator==( const PythonQtObjectPtr &p ) const { return object() == p.object(); } @@ -116,7 +116,7 @@ public: //! sets the object and passes the ownership (stealing the reference, in Python slang) void setNewRef(PyObject* o) { if (o != _object) { - if (_object) Py_DECREF(_object); + if (_object) { Py_DECREF(_object); } _object = o; } } @@ -157,9 +157,9 @@ protected: void setObject(PyObject* o) { if (o != _object) { - if (_object) Py_DECREF(_object); + if (_object) { Py_DECREF(_object); } _object = o; - if (_object) Py_INCREF(_object); + if (_object) { Py_INCREF(_object); } } } @@ -171,4 +171,4 @@ private: // register it to the meta type system Q_DECLARE_METATYPE(PythonQtObjectPtr) -#endif \ No newline at end of file +#endif diff --git a/src/PythonQtSignalReceiver.cpp b/src/PythonQtSignalReceiver.cpp index d4074b1..0217c73 100644 --- a/src/PythonQtSignalReceiver.cpp +++ b/src/PythonQtSignalReceiver.cpp @@ -56,9 +56,11 @@ void PythonQtSignalTarget::call(void **arguments) const { PyObject* PythonQtSignalTarget::call(PyObject* callable, const PythonQtMethodInfo* methodInfos, void **arguments, bool skipFirstArgumentOfMethodInfo) { + Q_UNUSED(skipFirstArgumentOfMethodInfo) + // Note: we check if the callable is a PyFunctionObject and has a fixed number of arguments // if that is the case, we only pass these arguments to python and skip the additional arguments from the signal - + int numPythonArgs = -1; if (PyFunction_Check(callable)) { PyObject* o = callable; @@ -135,7 +137,7 @@ PyObject* PythonQtSignalTarget::call(PyObject* callable, const PythonQtMethodInf PythonQtSignalReceiver::PythonQtSignalReceiver(QObject* obj):PythonQtSignalReceiverBase(obj) { _obj = obj; - + // fetch the class info for object, since we will need to for correct enum resolution in // signals _objClassInfo = PythonQt::priv()->getClassInfo(obj->metaObject()); diff --git a/src/PythonQtSignalReceiver.h b/src/PythonQtSignalReceiver.h index dfbcbc6..210f8d6 100644 --- a/src/PythonQtSignalReceiver.h +++ b/src/PythonQtSignalReceiver.h @@ -50,7 +50,7 @@ class PythonQtMethodInfo; class PythonQtClassInfo; //! stores information about a signal target -/*! copy construction and assignment works fine with the C++ standard behaviour and are thus not implemented +/*! copy construction and assignment works fine with the C++ standard behavior and are thus not implemented */ class PYTHONQT_EXPORT PythonQtSignalTarget { public: diff --git a/src/PythonQtSlot.cpp b/src/PythonQtSlot.cpp index 96f8e8d..74bc69c 100644 --- a/src/PythonQtSlot.cpp +++ b/src/PythonQtSlot.cpp @@ -53,7 +53,7 @@ bool PythonQtCallSlot(PythonQtClassInfo* classInfo, QObject* objectToCall, PyObject* args, bool strict, PythonQtSlotInfo* info, void* firstArgument, PyObject** pythonReturnValue, void** directReturnValuePointer) { static unsigned int recursiveEntry = 0; - + if (directReturnValuePointer) { *directReturnValuePointer = NULL; } @@ -65,19 +65,19 @@ bool PythonQtCallSlot(PythonQtClassInfo* classInfo, QObject* objectToCall, PyObj PythonQtConv::global_valueStorage.getPos(globalValueStoragePos); PythonQtConv::global_ptrStorage.getPos(globalPtrStoragePos); PythonQtConv::global_variantStorage.getPos(globalVariantStoragePos); - + recursiveEntry++; - + // the arguments that are passed to qt_metacall void* argList[PYTHONQT_MAX_ARGS]; PyObject* result = NULL; int argc = info->parameterCount(); const QList& params = info->parameters(); - + const PythonQtSlotInfo::ParameterInfo& returnValueParam = params.at(0); // set return argument to NULL argList[0] = NULL; - + bool ok = true; bool skipFirst = false; if (info->isInstanceDecorator()) { @@ -114,7 +114,7 @@ bool PythonQtCallSlot(PythonQtClassInfo* classInfo, QObject* objectToCall, PyObj } } } - + if (ok) { // parameters are ok, now create the qt return value which is assigned to by metacall if (returnValueParam.typeId != QMetaType::Void) { @@ -133,8 +133,8 @@ bool PythonQtCallSlot(PythonQtClassInfo* classInfo, QObject* objectToCall, PyObj if (result) { argList[0] = ((PythonQtInstanceWrapper*)result)->_wrappedPtr; } - Py_DECREF(emptyTuple); - } + Py_DECREF(emptyTuple); + } } } else { // we can use our pointer directly! @@ -142,9 +142,26 @@ bool PythonQtCallSlot(PythonQtClassInfo* classInfo, QObject* objectToCall, PyObj } } + + PythonQt::ProfilingCB* profilingCB = PythonQt::priv()->profilingCB(); + if (profilingCB) { + const char* className = NULL; + if (info->decorator()) { + className = info->decorator()->metaObject()->className(); + } else { + className = objectToCall->metaObject()->className(); + } + + profilingCB(PythonQt::Enter, className, info->metaMethod()->signature()); + } + // invoke the slot via metacall (info->decorator()?info->decorator():objectToCall)->qt_metacall(QMetaObject::InvokeMetaMethod, info->slotIndex(), argList); + if (profilingCB) { + profilingCB(PythonQt::Leave, NULL, NULL); + } + // handle the return value (which in most cases still needs to be converted to a Python object) if (argList[0] || returnValueParam.typeId == QMetaType::Void) { if (directReturnValuePointer) { @@ -162,12 +179,12 @@ bool PythonQtCallSlot(PythonQtClassInfo* classInfo, QObject* objectToCall, PyObj } } recursiveEntry--; - + // reset the parameter storage position to the stored pos to "pop" the parameter stack PythonQtConv::global_valueStorage.setPos(globalValueStoragePos); PythonQtConv::global_ptrStorage.setPos(globalPtrStoragePos); PythonQtConv::global_variantStorage.setPos(globalVariantStoragePos); - + *pythonReturnValue = result; // NOTE: it is important to only return here, otherwise the stack will not be popped!!! return result || (directReturnValuePointer && *directReturnValuePointer); @@ -286,7 +303,7 @@ PyObject *PythonQtSlotFunction_CallImpl(PythonQtClassInfo* classInfo, QObject* o PyErr_SetString(PyExc_ValueError, e.toLatin1().data()); } } - + return r; } diff --git a/src/PythonQtStdDecorators.cpp b/src/PythonQtStdDecorators.cpp index 1a42d51..24550dc 100644 --- a/src/PythonQtStdDecorators.cpp +++ b/src/PythonQtStdDecorators.cpp @@ -53,7 +53,7 @@ bool PythonQtStdDecorators::connect(QObject* sender, const QByteArray& signal, P } else { signalTmp = "2" + signal; } - + if (sender) { return PythonQt::self()->addSignalHandler(sender, signalTmp, callable); } else { @@ -112,7 +112,7 @@ bool PythonQtStdDecorators::disconnect(QObject* sender, const QByteArray& signal } else { signalTmp = "2" + signal; } - + QByteArray slotTmp; first = slot.at(0); if (first>='0' && first<='9') { @@ -120,21 +120,12 @@ bool PythonQtStdDecorators::disconnect(QObject* sender, const QByteArray& signal } else { slotTmp = "1" + slot; } - + r = QObject::disconnect(sender, signalTmp, receiver, slotTmp); } return r; } -#undef emit -void PythonQtStdDecorators::emit(QObject* sender, const QByteArray& signal, PyObject* arg1 ,PyObject* arg2 , - PyObject* arg3 ,PyObject* arg4 ,PyObject* arg5 ,PyObject* arg6 ,PyObject* arg7 ) -{ - // TODO xxx - // use normal PythonQtSlot calling code, add "allowSignal" to member lookup?! -} -#define emit - QObject* PythonQtStdDecorators::parent(QObject* o) { return o->parent(); } @@ -244,7 +235,7 @@ QObject* PythonQtStdDecorators::findChild(QObject* parent, const char* typeName, if (!name.isNull() && obj->objectName() != name) continue; - if ((typeName && obj->inherits(typeName)) || + if ((typeName && obj->inherits(typeName)) || (meta && meta->cast(obj))) return obj; } @@ -274,7 +265,7 @@ int PythonQtStdDecorators::findChildren(QObject* parent, const char* typeName, c if (!name.isNull() && obj->objectName() != name) continue; - if ((typeName && obj->inherits(typeName)) || + if ((typeName && obj->inherits(typeName)) || (meta && meta->cast(obj))) { list += obj; } @@ -301,7 +292,7 @@ int PythonQtStdDecorators::findChildren(QObject* parent, const char* typeName, c if (regExp.indexIn(obj->objectName()) == -1) continue; - if ((typeName && obj->inherits(typeName)) || + if ((typeName && obj->inherits(typeName)) || (meta && meta->cast(obj))) { list += obj; } diff --git a/src/PythonQtStdDecorators.h b/src/PythonQtStdDecorators.h index 1624f63..2b4f204 100644 --- a/src/PythonQtStdDecorators.h +++ b/src/PythonQtStdDecorators.h @@ -62,11 +62,6 @@ public slots: bool disconnect(QObject* sender, const QByteArray& signal, PyObject* callable); bool disconnect(QObject* sender, const QByteArray& signal, QObject* receiver, const QByteArray& slot); -#undef emit - void emit(QObject* sender, const QByteArray& signal, PyObject* arg1 = NULL,PyObject* arg2 = NULL, - PyObject* arg3 = NULL,PyObject* arg4 = NULL,PyObject* arg5 = NULL,PyObject* arg6 = NULL,PyObject* arg7 = NULL); -#define emit - QObject* parent(QObject* o); void setParent(QObject* o, QObject* parent); @@ -74,19 +69,19 @@ public slots: QObject* findChild(QObject* parent, PyObject* type, const QString& name = QString()); QList findChildren(QObject* parent, PyObject* type, const QString& name= QString()); QList findChildren(QObject* parent, PyObject* type, const QRegExp& regExp); - + bool setProperty(QObject* o, const char* name, const QVariant& value); QVariant property(QObject* o, const char* name); 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()); } + void static_Qt_qDebug(const QByteArray& msg) { qDebug("%s", msg.constData()); } // TODO: multi arg qDebug... - void static_Qt_qWarning(const QByteArray& msg) { qWarning(msg.constData()); } + void static_Qt_qWarning(const QByteArray& msg) { qWarning("%s", msg.constData()); } // TODO: multi arg qWarning... - void static_Qt_qCritical(const QByteArray& msg) { qCritical(msg.constData()); } + void static_Qt_qCritical(const QByteArray& msg) { qCritical("%s", msg.constData()); } // TODO: multi arg qCritical... - void static_Qt_qFatal(const QByteArray& msg) { qFatal(msg.constData()); } + void static_Qt_qFatal(const QByteArray& msg) { qFatal("%s", 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); } @@ -98,7 +93,7 @@ public slots: void static_Qt_qsrand(uint a) { qsrand(a); } QString tr(QObject* obj, const QByteArray& text, const QByteArray& ambig = QByteArray(), int n = -1); - + QByteArray static_Qt_SIGNAL(const QByteArray& s) { return QByteArray("2") + s; } QByteArray static_Qt_SLOT(const QByteArray& s) { return QByteArray("1") + s; }