1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt for Python.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39
40 #include "pysideqmlregistertype.h"
41
42 // shiboken
43 #include <shiboken.h>
44 #include <signature.h>
45
46 // pyside
47 #include <pyside.h>
48 #include <pyside_p.h>
49 #include <pysideproperty.h>
50
51 // auto generated headers
52 #include "pyside2_qtcore_python.h"
53 #include "pyside2_qtqml_python.h"
54
55 #ifndef PYSIDE_MAX_QML_TYPES
56 // Maximum number of different Qt QML types the user can export to QML using
57 // qmlRegisterType. This limit exists because the QML engine instantiates objects
58 // by calling a function with one argument (a void *pointer where the object should
59 // be created), and thus does not allow us to choose which object to create. Thus
60 // we create a C++ factory function for each new registered type at compile time.
61 #define PYSIDE_MAX_QML_TYPES 50
62 #endif
63
64 // Forward declarations.
65 static void propListMetaCall(PySideProperty *pp, PyObject *self, QMetaObject::Call call,
66 void **args);
67
68 // All registered python types and their creation functions.
69 static PyObject *pyTypes[PYSIDE_MAX_QML_TYPES];
70 static void (*createFuncs[PYSIDE_MAX_QML_TYPES])(void *);
71
72 // Mutex used to avoid race condition on PySide::nextQObjectMemoryAddr.
73 static QMutex nextQmlElementMutex;
74
75 template<int N>
76 struct ElementFactoryBase
77 {
createIntoElementFactoryBase78 static void createInto(void *memory)
79 {
80 QMutexLocker locker(&nextQmlElementMutex);
81 PySide::setNextQObjectMemoryAddr(memory);
82 Shiboken::GilState state;
83 PyObject *obj = PyObject_CallObject(pyTypes[N], 0);
84 if (!obj || PyErr_Occurred())
85 PyErr_Print();
86 PySide::setNextQObjectMemoryAddr(0);
87 }
88 };
89
90 template<int N>
91 struct ElementFactory : ElementFactoryBase<N>
92 {
initElementFactory93 static void init()
94 {
95 createFuncs[N] = &ElementFactoryBase<N>::createInto;
96 ElementFactory<N-1>::init();
97 }
98 };
99
100 template<>
101 struct ElementFactory<0> : ElementFactoryBase<0>
102 {
initElementFactory103 static void init()
104 {
105 createFuncs[0] = &ElementFactoryBase<0>::createInto;
106 }
107 };
108
qmlRegisterType(PyObject * pyObj,const char * uri,int versionMajor,int versionMinor,const char * qmlName)109 int PySide::qmlRegisterType(PyObject *pyObj, const char *uri, int versionMajor,
110 int versionMinor, const char *qmlName)
111 {
112 using namespace Shiboken;
113
114 static PyTypeObject *qobjectType = Shiboken::Conversions::getPythonTypeObject("QObject*");
115 assert(qobjectType);
116 static int nextType = 0;
117
118 if (nextType >= PYSIDE_MAX_QML_TYPES) {
119 PyErr_Format(PyExc_TypeError, "You can only export %d custom QML types to QML.",
120 PYSIDE_MAX_QML_TYPES);
121 return -1;
122 }
123
124 PyTypeObject *pyObjType = reinterpret_cast<PyTypeObject *>(pyObj);
125 if (!PySequence_Contains(pyObjType->tp_mro, reinterpret_cast<PyObject *>(qobjectType))) {
126 PyErr_Format(PyExc_TypeError, "A type inherited from %s expected, got %s.",
127 qobjectType->tp_name, pyObjType->tp_name);
128 return -1;
129 }
130
131 const QMetaObject *metaObject = PySide::retrieveMetaObject(pyObjType);
132 Q_ASSERT(metaObject);
133
134 QQmlPrivate::RegisterType type;
135 type.version = 0;
136
137 // Allow registering Qt Quick items.
138 bool registered = false;
139 #ifdef PYSIDE_QML_SUPPORT
140 QuickRegisterItemFunction quickRegisterItemFunction = getQuickRegisterItemFunction();
141 if (quickRegisterItemFunction) {
142 registered = quickRegisterItemFunction(pyObj, uri, versionMajor, versionMinor,
143 qmlName, &type);
144 }
145 #endif
146
147 // Register as simple QObject rather than Qt Quick item.
148 if (!registered) {
149 // Incref the type object, don't worry about decref'ing it because
150 // there's no way to unregister a QML type.
151 Py_INCREF(pyObj);
152
153 pyTypes[nextType] = pyObj;
154
155 // FIXME: Fix this to assign new type ids each time.
156 type.typeId = qMetaTypeId<QObject *>();
157 type.listId = qMetaTypeId<QQmlListProperty<QObject> >();
158 type.attachedPropertiesFunction = QQmlPrivate::attachedPropertiesFunc<QObject>();
159 type.attachedPropertiesMetaObject = QQmlPrivate::attachedPropertiesMetaObject<QObject>();
160
161 type.parserStatusCast =
162 QQmlPrivate::StaticCastSelector<QObject, QQmlParserStatus>::cast();
163 type.valueSourceCast =
164 QQmlPrivate::StaticCastSelector<QObject, QQmlPropertyValueSource>::cast();
165 type.valueInterceptorCast =
166 QQmlPrivate::StaticCastSelector<QObject, QQmlPropertyValueInterceptor>::cast();
167
168 int objectSize = static_cast<int>(PySide::getSizeOfQObject(
169 reinterpret_cast<SbkObjectType *>(pyObj)));
170 type.objectSize = objectSize;
171 type.create = createFuncs[nextType];
172 type.uri = uri;
173 type.versionMajor = versionMajor;
174 type.versionMinor = versionMinor;
175 type.elementName = qmlName;
176
177 type.extensionObjectCreate = 0;
178 type.extensionMetaObject = 0;
179 type.customParser = 0;
180 ++nextType;
181 }
182 type.metaObject = metaObject; // Snapshot may have changed.
183
184 int qmlTypeId = QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
185 if (qmlTypeId == -1) {
186 PyErr_Format(PyExc_TypeError, "QML meta type registration of \"%s\" failed.",
187 qmlName);
188 }
189 return qmlTypeId;
190 }
191
192 extern "C"
193 {
194
195 // This is the user data we store in the property.
196 struct QmlListProperty
197 {
198 PyTypeObject *type;
199 PyObject *append;
200 PyObject *at;
201 PyObject *clear;
202 PyObject *count;
203 };
204
propListTpInit(PyObject * self,PyObject * args,PyObject * kwds)205 static int propListTpInit(PyObject *self, PyObject *args, PyObject *kwds)
206 {
207 static const char *kwlist[] = {"type", "append", "at", "clear", "count", 0};
208 PySideProperty *pySelf = reinterpret_cast<PySideProperty *>(self);
209 QmlListProperty *data = new QmlListProperty;
210 memset(data, 0, sizeof(QmlListProperty));
211
212 if (!PyArg_ParseTupleAndKeywords(args, kwds,
213 "OO|OOO:QtQml.ListProperty", (char **) kwlist,
214 &data->type,
215 &data->append,
216 &data->at,
217 &data->clear,
218 &data->count)) {
219 return -1;
220 }
221 PySide::Property::setMetaCallHandler(pySelf, &propListMetaCall);
222 PySide::Property::setTypeName(pySelf, "QQmlListProperty<QObject>");
223 PySide::Property::setUserData(pySelf, data);
224
225 return 0;
226 }
227
propListTpFree(void * self)228 void propListTpFree(void *self)
229 {
230 auto pySelf = reinterpret_cast<PySideProperty *>(self);
231 delete reinterpret_cast<QmlListProperty *>(PySide::Property::userData(pySelf));
232 // calls base type constructor
233 Py_TYPE(pySelf)->tp_base->tp_free(self);
234 }
235
236 static PyType_Slot PropertyListType_slots[] = {
237 {Py_tp_init, (void *)propListTpInit},
238 {Py_tp_free, (void *)propListTpFree},
239 {Py_tp_dealloc, (void *)Sbk_object_dealloc},
240 {0, 0}
241 };
242 static PyType_Spec PropertyListType_spec = {
243 "2:PySide2.QtQml.ListProperty",
244 sizeof(PySideProperty),
245 0,
246 Py_TPFLAGS_DEFAULT,
247 PropertyListType_slots,
248 };
249
250
PropertyListTypeF(void)251 PyTypeObject *PropertyListTypeF(void)
252 {
253 static PyTypeObject *type = nullptr;
254 if (!type) {
255 PyObject *bases = Py_BuildValue("(O)", PySidePropertyTypeF());
256 type = (PyTypeObject *)SbkType_FromSpecWithBases(&PropertyListType_spec, bases);
257 Py_XDECREF(bases);
258 }
259 return type;
260 }
261
262 } // extern "C"
263
264 // Implementation of QQmlListProperty<T>::AppendFunction callback
propListAppender(QQmlListProperty<QObject> * propList,QObject * item)265 void propListAppender(QQmlListProperty<QObject> *propList, QObject *item)
266 {
267 Shiboken::GilState state;
268
269 Shiboken::AutoDecRef args(PyTuple_New(2));
270 PyTuple_SET_ITEM(args, 0, Shiboken::Conversions::pointerToPython((SbkObjectType *)SbkPySide2_QtCoreTypes[SBK_QOBJECT_IDX], propList->object));
271 PyTuple_SET_ITEM(args, 1, Shiboken::Conversions::pointerToPython((SbkObjectType *)SbkPySide2_QtCoreTypes[SBK_QOBJECT_IDX], item));
272
273 auto data = reinterpret_cast<QmlListProperty *>(propList->data);
274 Shiboken::AutoDecRef retVal(PyObject_CallObject(data->append, args));
275
276 if (PyErr_Occurred())
277 PyErr_Print();
278 }
279
280 // Implementation of QQmlListProperty<T>::CountFunction callback
propListCount(QQmlListProperty<QObject> * propList)281 int propListCount(QQmlListProperty<QObject> *propList)
282 {
283 Shiboken::GilState state;
284
285 Shiboken::AutoDecRef args(PyTuple_New(1));
286 PyTuple_SET_ITEM(args, 0, Shiboken::Conversions::pointerToPython((SbkObjectType *)SbkPySide2_QtCoreTypes[SBK_QOBJECT_IDX], propList->object));
287
288 auto data = reinterpret_cast<QmlListProperty *>(propList->data);
289 Shiboken::AutoDecRef retVal(PyObject_CallObject(data->count, args));
290
291 // Check return type
292 int cppResult = 0;
293 PythonToCppFunc pythonToCpp = 0;
294 if (PyErr_Occurred())
295 PyErr_Print();
296 else if ((pythonToCpp = Shiboken::Conversions::isPythonToCppConvertible(Shiboken::Conversions::PrimitiveTypeConverter<int>(), retVal)))
297 pythonToCpp(retVal, &cppResult);
298 return cppResult;
299 }
300
301 // Implementation of QQmlListProperty<T>::AtFunction callback
propListAt(QQmlListProperty<QObject> * propList,int index)302 QObject *propListAt(QQmlListProperty<QObject> *propList, int index)
303 {
304 Shiboken::GilState state;
305
306 Shiboken::AutoDecRef args(PyTuple_New(2));
307 PyTuple_SET_ITEM(args, 0, Shiboken::Conversions::pointerToPython((SbkObjectType *)SbkPySide2_QtCoreTypes[SBK_QOBJECT_IDX], propList->object));
308 PyTuple_SET_ITEM(args, 1, Shiboken::Conversions::copyToPython(Shiboken::Conversions::PrimitiveTypeConverter<int>(), &index));
309
310 auto data = reinterpret_cast<QmlListProperty *>(propList->data);
311 Shiboken::AutoDecRef retVal(PyObject_CallObject(data->at, args));
312
313 QObject *result = 0;
314 if (PyErr_Occurred())
315 PyErr_Print();
316 else if (PyType_IsSubtype(Py_TYPE(retVal), data->type))
317 Shiboken::Conversions::pythonToCppPointer((SbkObjectType *)SbkPySide2_QtCoreTypes[SBK_QOBJECT_IDX], retVal, &result);
318 return result;
319 }
320
321 // Implementation of QQmlListProperty<T>::ClearFunction callback
propListClear(QQmlListProperty<QObject> * propList)322 void propListClear(QQmlListProperty<QObject> * propList)
323 {
324 Shiboken::GilState state;
325
326 Shiboken::AutoDecRef args(PyTuple_New(1));
327 PyTuple_SET_ITEM(args, 0, Shiboken::Conversions::pointerToPython((SbkObjectType *)SbkPySide2_QtCoreTypes[SBK_QOBJECT_IDX], propList->object));
328
329 auto data = reinterpret_cast<QmlListProperty *>(propList->data);
330 Shiboken::AutoDecRef retVal(PyObject_CallObject(data->clear, args));
331
332 if (PyErr_Occurred())
333 PyErr_Print();
334 }
335
336 // qt_metacall specialization for ListProperties
propListMetaCall(PySideProperty * pp,PyObject * self,QMetaObject::Call call,void ** args)337 static void propListMetaCall(PySideProperty *pp, PyObject *self, QMetaObject::Call call, void **args)
338 {
339 if (call != QMetaObject::ReadProperty)
340 return;
341
342 auto data = reinterpret_cast<QmlListProperty *>(PySide::Property::userData(pp));
343 QObject *qobj;
344 Shiboken::Conversions::pythonToCppPointer((SbkObjectType *)SbkPySide2_QtCoreTypes[SBK_QOBJECT_IDX], self, &qobj);
345 QQmlListProperty<QObject> declProp(qobj, data, &propListAppender, &propListCount, &propListAt, &propListClear);
346
347 // Copy the data to the memory location requested by the meta call
348 void *v = args[0];
349 *reinterpret_cast<QQmlListProperty<QObject> *>(v) = declProp;
350 }
351
352 // VolatileBool (volatile bool) type definition.
353
354 static PyObject *
QtQml_VolatileBoolObject_new(PyTypeObject * type,PyObject * args,PyObject * kwds)355 QtQml_VolatileBoolObject_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
356 {
357 static const char *kwlist[] = {"x", 0};
358 PyObject *x = Py_False;
359 long ok;
360
361 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O:bool", const_cast<char **>(kwlist), &x))
362 return Q_NULLPTR;
363 ok = PyObject_IsTrue(x);
364 if (ok < 0)
365 return Q_NULLPTR;
366
367 QtQml_VolatileBoolObject *self
368 = reinterpret_cast<QtQml_VolatileBoolObject *>(type->tp_alloc(type, 0));
369
370 if (self != Q_NULLPTR)
371 self->flag = ok;
372
373 return reinterpret_cast<PyObject *>(self);
374 }
375
376 static PyObject *
QtQml_VolatileBoolObject_get(QtQml_VolatileBoolObject * self)377 QtQml_VolatileBoolObject_get(QtQml_VolatileBoolObject *self)
378 {
379 if (self->flag)
380 return Py_True;
381 return Py_False;
382 }
383
384 static PyObject *
QtQml_VolatileBoolObject_set(QtQml_VolatileBoolObject * self,PyObject * args)385 QtQml_VolatileBoolObject_set(QtQml_VolatileBoolObject *self, PyObject *args)
386 {
387 PyObject *value = Py_False;
388 long ok;
389
390 if (!PyArg_ParseTuple(args, "O:bool", &value)) {
391 return Q_NULLPTR;
392 }
393
394 ok = PyObject_IsTrue(value);
395 if (ok < 0) {
396 PyErr_SetString(PyExc_TypeError, "Not a boolean value.");
397 return Q_NULLPTR;
398 }
399
400 if (ok > 0)
401 self->flag = true;
402 else
403 self->flag = false;
404
405 Py_RETURN_NONE;
406 }
407
408 static PyMethodDef QtQml_VolatileBoolObject_methods[] = {
409 {"get", reinterpret_cast<PyCFunction>(QtQml_VolatileBoolObject_get), METH_NOARGS,
410 "B.get() -> Bool. Returns the value of the volatile boolean"
411 },
412 {"set", reinterpret_cast<PyCFunction>(QtQml_VolatileBoolObject_set), METH_VARARGS,
413 "B.set(a) -> None. Sets the value of the volatile boolean"
414 },
415 {Q_NULLPTR} /* Sentinel */
416 };
417
418 static PyObject *
QtQml_VolatileBoolObject_repr(QtQml_VolatileBoolObject * self)419 QtQml_VolatileBoolObject_repr(QtQml_VolatileBoolObject *self)
420 {
421 PyObject *s;
422
423 if (self->flag)
424 s = PyBytes_FromFormat("%s(True)",
425 Py_TYPE(self)->tp_name);
426 else
427 s = PyBytes_FromFormat("%s(False)",
428 Py_TYPE(self)->tp_name);
429 Py_XINCREF(s);
430 return s;
431 }
432
433 static PyObject *
QtQml_VolatileBoolObject_str(QtQml_VolatileBoolObject * self)434 QtQml_VolatileBoolObject_str(QtQml_VolatileBoolObject *self)
435 {
436 PyObject *s;
437
438 if (self->flag)
439 s = PyBytes_FromFormat("%s(True) -> %p",
440 Py_TYPE(self)->tp_name, &(self->flag));
441 else
442 s = PyBytes_FromFormat("%s(False) -> %p",
443 Py_TYPE(self)->tp_name, &(self->flag));
444 Py_XINCREF(s);
445 return s;
446 }
447
448 static PyType_Slot QtQml_VolatileBoolType_slots[] = {
449 {Py_tp_repr, (void *)reinterpret_cast<reprfunc>(QtQml_VolatileBoolObject_repr)},
450 {Py_tp_str, (void *)reinterpret_cast<reprfunc>(QtQml_VolatileBoolObject_str)},
451 {Py_tp_methods, (void *)QtQml_VolatileBoolObject_methods},
452 {Py_tp_new, (void *)QtQml_VolatileBoolObject_new},
453 {Py_tp_dealloc, (void *)Sbk_object_dealloc},
454 {0, 0}
455 };
456 static PyType_Spec QtQml_VolatileBoolType_spec = {
457 "2:PySide2.QtQml.VolatileBool",
458 sizeof(QtQml_VolatileBoolObject),
459 0,
460 Py_TPFLAGS_DEFAULT,
461 QtQml_VolatileBoolType_slots,
462 };
463
464
QtQml_VolatileBoolTypeF(void)465 PyTypeObject *QtQml_VolatileBoolTypeF(void)
466 {
467 static PyTypeObject *type = reinterpret_cast<PyTypeObject *>(
468 SbkType_FromSpec(&QtQml_VolatileBoolType_spec));
469 return type;
470 }
471
472 static const char *PropertyList_SignatureStrings[] = {
473 "PySide2.QtQml.ListProperty(type:type,append:typing.Callable,"
474 "at:typing.Callable=None,clear:typing.Callable=None,count:typing.Callable=None)",
475 nullptr}; // Sentinel
476
477 static const char *VolatileBool_SignatureStrings[] = {
478 "PySide2.QtQml.VolatileBool.get()->bool",
479 "PySide2.QtQml.VolatileBool.set(a:object)",
480 nullptr}; // Sentinel
481
initQmlSupport(PyObject * module)482 void PySide::initQmlSupport(PyObject *module)
483 {
484 ElementFactory<PYSIDE_MAX_QML_TYPES - 1>::init();
485
486 // Export QmlListProperty type
487 if (InitSignatureStrings(PropertyListTypeF(), PropertyList_SignatureStrings) < 0) {
488 PyErr_Print();
489 qWarning() << "Error initializing PropertyList type.";
490 return;
491 }
492
493 Py_INCREF(reinterpret_cast<PyObject *>(PropertyListTypeF()));
494 PyModule_AddObject(module, PepType_GetNameStr(PropertyListTypeF()),
495 reinterpret_cast<PyObject *>(PropertyListTypeF()));
496
497 if (InitSignatureStrings(QtQml_VolatileBoolTypeF(), VolatileBool_SignatureStrings) < 0) {
498 PyErr_Print();
499 qWarning() << "Error initializing VolatileBool type.";
500 return;
501 }
502
503 Py_INCREF(QtQml_VolatileBoolTypeF());
504 PyModule_AddObject(module, PepType_GetNameStr(QtQml_VolatileBoolTypeF()),
505 reinterpret_cast<PyObject *>(QtQml_VolatileBoolTypeF()));
506 }
507