|
46 | 46 | #include "PythonQtSignal.h" |
47 | 47 | #include "PythonQtSignalReceiver.h" |
48 | 48 | #include "PythonQtConversion.h" |
| 49 | +#include "PythonQtProperty.h" |
49 | 50 | #include "PythonQtStdIn.h" |
50 | 51 | #include "PythonQtStdOut.h" |
51 | 52 | #include "PythonQtCppWrapperFactory.h" |
52 | 53 | #include "PythonQtVariants.h" |
53 | 54 | #include "PythonQtStdDecorators.h" |
54 | 55 | #include "PythonQtQFileImporter.h" |
55 | 56 | #include "PythonQtBoolResult.h" |
| 57 | +#include "PythonQtSlotDecorator.h" |
56 | 58 |
|
57 | 59 | #include <QDir> |
58 | 60 |
|
@@ -261,6 +263,11 @@ void PythonQt::init(int flags, const QByteArray& pythonQtModuleName) |
261 | 263 | } |
262 | 264 |
|
263 | 265 | _self->priv()->pythonQtModule().addObject("Debug", _self->priv()->_debugAPI); |
| 266 | + |
| 267 | + PyModule_AddObject(pack, "Slot", (PyObject*)&PythonQtSlotDecorator_Type); |
| 268 | + PyModule_AddObject(pack, "Signal", (PyObject*)&PythonQtSignalFunction_Type); |
| 269 | + PyModule_AddObject(pack, "Property", (PyObject*)&PythonQtProperty_Type); |
| 270 | + |
264 | 271 | } |
265 | 272 | } |
266 | 273 |
|
@@ -303,6 +310,16 @@ PythonQt::PythonQt(int flags, const QByteArray& pythonQtModuleName) |
303 | 310 | } |
304 | 311 | Py_INCREF(&PythonQtSignalFunction_Type); |
305 | 312 |
|
| 313 | + if (PyType_Ready(&PythonQtSlotDecorator_Type) < 0) { |
| 314 | + std::cerr << "could not initialize PythonQtSlotDecorator_Type" << ", in " << __FILE__ << ":" << __LINE__ << std::endl; |
| 315 | + } |
| 316 | + Py_INCREF(&PythonQtSlotDecorator_Type); |
| 317 | + |
| 318 | + if (PyType_Ready(&PythonQtProperty_Type) < 0) { |
| 319 | + std::cerr << "could not initialize PythonQtProperty_Type" << ", in " << __FILE__ << ":" << __LINE__ << std::endl; |
| 320 | + } |
| 321 | + Py_INCREF(&PythonQtProperty_Type); |
| 322 | + |
306 | 323 | PythonQtBoolResult_Type.tp_new = PyType_GenericNew; |
307 | 324 | if (PyType_Ready(&PythonQtBoolResult_Type) < 0) { |
308 | 325 | std::cerr << "could not initialize PythonQtBoolResult_Type" << ", in " << __FILE__ << ":" << __LINE__ << std::endl; |
@@ -1911,6 +1928,178 @@ bool PythonQtPrivate::isMethodDescriptor(PyObject* object) const |
1911 | 1928 | return false; |
1912 | 1929 | } |
1913 | 1930 |
|
| 1931 | +// We need this for the dynamic meta object building: |
| 1932 | +#include <private/qmetaobjectbuilder_p.h> |
| 1933 | + |
| 1934 | +const QMetaObject* PythonQtPrivate::getDynamicMetaObject(PythonQtInstanceWrapper* wrapper, const QMetaObject* prototypeMetaObject) |
| 1935 | +{ |
| 1936 | + PythonQtDynamicClassInfo* info = wrapper->dynamicClassInfo(); |
| 1937 | + if (info) { |
| 1938 | + if (!info->_dynamicMetaObject) { |
| 1939 | + buildDynamicMetaObject(((PythonQtClassWrapper*)Py_TYPE(wrapper)), prototypeMetaObject); |
| 1940 | + } |
| 1941 | + return info->_dynamicMetaObject; |
| 1942 | + } |
| 1943 | + return prototypeMetaObject; |
| 1944 | +} |
| 1945 | + |
| 1946 | +void PythonQtPrivate::buildDynamicMetaObject(PythonQtClassWrapper* type, const QMetaObject* prototypeMetaObject) |
| 1947 | +{ |
| 1948 | + QMetaObjectBuilder builder; |
| 1949 | + builder.setSuperClass(prototypeMetaObject); |
| 1950 | + builder.setClassName(((PyTypeObject*)type)->tp_name); |
| 1951 | + |
| 1952 | + PyObject* dict = ((PyTypeObject*)type)->tp_dict; |
| 1953 | + Py_ssize_t pos = NULL; |
| 1954 | + PyObject* value = NULL; |
| 1955 | + PyObject* key = NULL; |
| 1956 | + static PyObject* qtSlots = PyString_FromString("_qtSlots"); |
| 1957 | + |
| 1958 | + bool needsMetaObject = false; |
| 1959 | + // Iterate over all members and check if they affect the QMetaObject: |
| 1960 | + // First look for signals: |
| 1961 | + while (PyDict_Next(dict, &pos, &key, &value)) { |
| 1962 | + if (PythonQtSignalFunction_Check(value)) { |
| 1963 | + // A signal object, register with the meta object |
| 1964 | + PythonQtSignalFunctionObject* signal = (PythonQtSignalFunctionObject*)value; |
| 1965 | + if (signal->_dynamicInfo) { |
| 1966 | + signal->_dynamicInfo->name = PyString_AsString(key); |
| 1967 | + foreach(QByteArray sig, signal->_dynamicInfo->signatures) { |
| 1968 | + QMetaMethodBuilder method = builder.addSignal(signal->_dynamicInfo->name + "(" + sig + ")"); |
| 1969 | + needsMetaObject = true; |
| 1970 | + } |
| 1971 | + } |
| 1972 | + } |
| 1973 | + } |
| 1974 | + pos = NULL; |
| 1975 | + value = NULL; |
| 1976 | + key = NULL; |
| 1977 | + // Now look for slots: (this is a bug in QMetaObjectBuilder, all signals need to be added first) |
| 1978 | + while (PyDict_Next(dict, &pos, &key, &value)) { |
| 1979 | + if (PythonQtProperty_Check(value)) { |
| 1980 | + PythonQtProperty* prop = (PythonQtProperty*)value; |
| 1981 | + QMetaPropertyBuilder newProp = builder.addProperty(PyString_AsString(key), prop->data->cppType); |
| 1982 | + newProp.setReadable(true); |
| 1983 | + newProp.setWritable(prop->data->fset != NULL); |
| 1984 | + newProp.setResettable(prop->data->freset != NULL); |
| 1985 | + newProp.setDesignable(prop->data->designable); |
| 1986 | + newProp.setScriptable(prop->data->scriptable); |
| 1987 | + newProp.setStored(prop->data->stored); |
| 1988 | + newProp.setUser(prop->data->user); |
| 1989 | + newProp.setConstant(prop->data->constant); |
| 1990 | + newProp.setFinal(prop->data->final); |
| 1991 | + if (prop->data->notify) { |
| 1992 | + PythonQtSignalFunctionObject* signal = (PythonQtSignalFunctionObject*)prop->data->notify; |
| 1993 | + if (signal->_dynamicInfo) { |
| 1994 | + QByteArray sig = signal->_dynamicInfo->signatures.at(0); |
| 1995 | + QByteArray fullSig = signal->_dynamicInfo->name + "(" + sig + ")"; |
| 1996 | + int idx = builder.indexOfSignal(fullSig); |
| 1997 | + if (idx != -1) { |
| 1998 | + newProp.setNotifySignal(builder.method(idx)); |
| 1999 | + } else { |
| 2000 | + std::cerr << "could not find notify signal signature " << fullSig.constData(); |
| 2001 | + } |
| 2002 | + } |
| 2003 | + } |
| 2004 | + } |
| 2005 | + if (PyFunction_Check(value) && PyObject_HasAttr(value, qtSlots)) { |
| 2006 | + // A function which has a "_qtSlots" signature list, add the slots to the meta object |
| 2007 | + PyObject* signatures = PyObject_GetAttr(value, qtSlots); |
| 2008 | + Py_ssize_t count = PyList_Size(signatures); |
| 2009 | + for (Py_ssize_t i = 0; i < count; i++) { |
| 2010 | + PyObject* signature = PyList_GET_ITEM(signatures, i); |
| 2011 | + QByteArray sig = PyString_AsString(signature); |
| 2012 | + // Split the return type and the rest of the signature, |
| 2013 | + // no spaces should be in the rest of the signature... |
| 2014 | + QList<QByteArray> parts = sig.split(' '); |
| 2015 | + QMetaMethodBuilder method = builder.addSlot(parts[1]); |
| 2016 | + // set the return type of the slot |
| 2017 | + method.setReturnType(parts[0]); |
| 2018 | + needsMetaObject = true; |
| 2019 | + } |
| 2020 | + } |
| 2021 | + // TODO: handle enums, classinfo, ... |
| 2022 | + } |
| 2023 | + if (needsMetaObject) { |
| 2024 | + type->_dynamicClassInfo->_dynamicMetaObject = builder.toMetaObject(); |
| 2025 | + type->_dynamicClassInfo->_classInfo = new PythonQtClassInfo(); |
| 2026 | + type->_dynamicClassInfo->_classInfo->setupQObject(type->_dynamicClassInfo->_dynamicMetaObject); |
| 2027 | + } else { |
| 2028 | + // we don't need an own meta object, just use the one from our base class |
| 2029 | + type->_dynamicClassInfo->_dynamicMetaObject = prototypeMetaObject; |
| 2030 | + } |
| 2031 | +} |
| 2032 | + |
| 2033 | + |
| 2034 | +int PythonQtPrivate::handleMetaCall(QObject* object, PythonQtInstanceWrapper* wrapper, QMetaObject::Call call, int id, void** args) |
| 2035 | +{ |
| 2036 | + const QMetaObject* meta = object->metaObject(); |
| 2037 | + int methodCount = meta->methodCount(); |
| 2038 | + if (call == QMetaObject::InvokeMetaMethod) { |
| 2039 | + QMetaMethod method = meta->method(id); |
| 2040 | + if (method.methodType() == QMetaMethod::Signal) { |
| 2041 | + // just emit the signal, there is no Python code |
| 2042 | + QMetaObject::activate(object, id, args); |
| 2043 | + } else { |
| 2044 | + callMethodInPython(method, wrapper, args); |
| 2045 | + } |
| 2046 | + } else { |
| 2047 | + QMetaProperty metaProp = meta->property(id); |
| 2048 | + if (!metaProp.isValid()) { |
| 2049 | + return id - methodCount; |
| 2050 | + } |
| 2051 | + PythonQtProperty* prop = NULL; |
| 2052 | + // Get directly from the Python class, since we don't want to get the value of the property |
| 2053 | + PyObject* maybeProp = PyBaseObject_Type.tp_getattro((PyObject*)wrapper, PyString_FromString(metaProp.name())); |
| 2054 | + if (maybeProp && PythonQtProperty_Check(maybeProp)) { |
| 2055 | + prop = (PythonQtProperty*)maybeProp; |
| 2056 | + } else { |
| 2057 | + return id - methodCount; |
| 2058 | + } |
| 2059 | + const PythonQtMethodInfo::ParameterInfo& info = PythonQtMethodInfo::getParameterInfoForMetaType(metaProp.userType()); |
| 2060 | + |
| 2061 | + if (call == QMetaObject::WriteProperty) { |
| 2062 | + PyObject* value = PythonQtConv::ConvertQtValueToPython(info, args[0]); |
| 2063 | + bool ok = prop->data->callSetter((PyObject*)wrapper, value); |
| 2064 | + Py_XDECREF(value); |
| 2065 | + |
| 2066 | + return ok ? 0 : -1; |
| 2067 | + |
| 2068 | + } else if (call == QMetaObject::ReadProperty) { |
| 2069 | + |
| 2070 | + PyObject* value = prop->data->callGetter((PyObject*)wrapper); |
| 2071 | + if (value) { |
| 2072 | + void* result = PythonQtConv::ConvertPythonToQt(info, value, false, NULL, args[0]); |
| 2073 | + Py_DECREF(value); |
| 2074 | + return (result == NULL ? -1 : 0); |
| 2075 | + } else { |
| 2076 | + return -1; |
| 2077 | + } |
| 2078 | + } else if (call == QMetaObject::ResetProperty) { |
| 2079 | + bool ok = prop->data->callReset((PyObject*)wrapper); |
| 2080 | + return ok ? 0 : -1; |
| 2081 | + } |
| 2082 | + } |
| 2083 | + return id - methodCount; |
| 2084 | +} |
| 2085 | + |
| 2086 | +void PythonQtPrivate::callMethodInPython(QMetaMethod &method, PythonQtInstanceWrapper* wrapper, void** args) |
| 2087 | +{ |
| 2088 | + QByteArray methodSig = method.methodSignature(); |
| 2089 | + PyObject* func = PyObject_GetAttrString((PyObject*)wrapper, method.name()); |
| 2090 | + if (func) { |
| 2091 | + const PythonQtMethodInfo* methodInfo = PythonQtMethodInfo::getCachedMethodInfo(method, NULL); |
| 2092 | + PyObject* result = PythonQtSignalTarget::call(func, methodInfo, args, false); |
| 2093 | + if (result) { |
| 2094 | + PythonQtConv::ConvertPythonToQt(methodInfo->parameters().at(0), result, false, NULL, args[0]); |
| 2095 | + // TODO: handle error? |
| 2096 | + //PythonQt::priv()->handleVirtualOverloadReturnError("devType", methodInfo, result); |
| 2097 | + } |
| 2098 | + Py_XDECREF(result); |
| 2099 | + Py_DECREF(func); |
| 2100 | + } |
| 2101 | +} |
| 2102 | + |
1914 | 2103 | QString PythonQtPrivate::getSignature(PyObject* object) |
1915 | 2104 | { |
1916 | 2105 | QString signature; |
|
0 commit comments