1 // This contains the main implementation of qmlRegisterType.
2 //
3 // Copyright (c) 2021 Riverbank Computing Limited <info@riverbankcomputing.com>
4 //
5 // This file is part of PyQt5.
6 //
7 // This file may be used under the terms of the GNU General Public License
8 // version 3.0 as published by the Free Software Foundation and appearing in
9 // the file LICENSE included in the packaging of this file. Please review the
10 // following information to ensure the GNU General Public License version 3.0
11 // requirements will be met: http://www.gnu.org/copyleft/gpl.html.
12 //
13 // If you do not wish to use this file under the terms of the GPL version 3.0
14 // then you may purchase a commercial license. For more information contact
15 // info@riverbankcomputing.com.
16 //
17 // This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
18 // WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19
20
21 #include <Python.h>
22
23 #include <qqmlprivate.h>
24 #include <QByteArray>
25 #include <QString>
26 #include <QQmlListProperty>
27 #include <QQmlParserStatus>
28 #include <QQmlPropertyValueSource>
29
30 #include "qpyqml_api.h"
31 #include "qpyqmlobject.h"
32 #include "qpyqmlvalidator.h"
33
34 #include "sipAPIQtQml.h"
35
36
37 class QQmlPropertyValueInterceptor;
38
39
40 // Forward declarations.
41 static QQmlPrivate::RegisterType *init_type(PyTypeObject *py_type, bool ctor,
42 int revision, PyTypeObject *attached);
43 static void complete_init(QQmlPrivate::RegisterType *rt, int revision);
44 static int register_type(QQmlPrivate::RegisterType *rt);
45
46
47 // The registration data for the QValidator proxy types.
48 const int NrOfQValidatorTypes = 10;
49 static QQmlPrivate::RegisterType validator_proxy_types[NrOfQValidatorTypes];
50
51 // The registration data for the QObject/QAbstractItemModel proxy types.
52 const int NrOfQObjectTypes = 60;
53 static QQmlPrivate::RegisterType object_proxy_types[NrOfQObjectTypes];
54
55
56 // Register a Python type.
qpyqml_register_type(PyTypeObject * py_type,PyTypeObject * attached)57 int qpyqml_register_type(PyTypeObject *py_type, PyTypeObject *attached)
58 {
59 // Initialise the registration data structure.
60 QQmlPrivate::RegisterType *rt = init_type(py_type, false, -1, attached);
61
62 if (!rt)
63 return -1;
64
65 return register_type(rt);
66 }
67
68
69 // Register a library Python type.
qpyqml_register_library_type(PyTypeObject * py_type,const char * uri,int major,int minor,const char * qml_name,int revision,PyTypeObject * attached)70 int qpyqml_register_library_type(PyTypeObject *py_type, const char *uri,
71 int major, int minor, const char *qml_name, int revision,
72 PyTypeObject *attached)
73 {
74 // Initialise the registration data structure.
75 QQmlPrivate::RegisterType *rt = init_type(py_type, true, revision,
76 attached);
77
78 if (!rt)
79 return -1;
80
81 rt->uri = uri;
82 rt->versionMajor = major;
83 rt->versionMinor = minor;
84 rt->elementName = qml_name;
85
86 return register_type(rt);
87 }
88
89
90 // Register an uncreatable library Python type.
qpyqml_register_uncreatable_type(PyTypeObject * py_type,const char * uri,int major,int minor,const char * qml_name,const QString & reason,int revision)91 int qpyqml_register_uncreatable_type(PyTypeObject *py_type, const char *uri,
92 int major, int minor, const char *qml_name, const QString &reason,
93 int revision)
94 {
95 // Initialise the registration data structure.
96 QQmlPrivate::RegisterType *rt = init_type(py_type, false, revision, 0);
97
98 if (!rt)
99 return -1;
100
101 rt->noCreationReason = reason;
102 rt->uri = uri;
103 rt->versionMajor = major;
104 rt->versionMinor = minor;
105 rt->elementName = qml_name;
106
107 return register_type(rt);
108 }
109
110
111 // Register the proxy type with QML.
register_type(QQmlPrivate::RegisterType * rt)112 static int register_type(QQmlPrivate::RegisterType *rt)
113 {
114 int type_id = QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, rt);
115
116 if (type_id < 0)
117 {
118 PyErr_SetString(PyExc_RuntimeError,
119 "unable to register type with QML");
120 return -1;
121 }
122
123 return type_id;
124 }
125
126
127 #define QPYQML_TYPE_INIT(t, n) \
128 case n##U: \
129 QPyQml##t##n::staticMetaObject = *mo; \
130 QPyQml##t##n::attachedPyType = attached; \
131 rt->typeId = qRegisterNormalizedMetaType<QPyQml##t##n *>(ptr_name); \
132 rt->listId = qRegisterNormalizedMetaType<QQmlListProperty<QPyQml##t##n> >(list_name); \
133 rt->objectSize = ctor ? sizeof(QPyQml##t##n) : 0; \
134 if (ctor) rt->create = QQmlPrivate::createInto<QPyQml##t##n>; else rt->create = 0; \
135 rt->attachedPropertiesFunction = attached_mo ? QPyQml##t##n::attachedProperties : 0; \
136 rt->parserStatusCast = is_parser_status ? QQmlPrivate::StaticCastSelector<QPyQml##t##n,QQmlParserStatus>::cast() : -1; \
137 rt->valueSourceCast = is_value_source ? QQmlPrivate::StaticCastSelector<QPyQml##t##n,QQmlPropertyValueSource>::cast() : -1; \
138 rt->valueInterceptorCast = QQmlPrivate::StaticCastSelector<QPyQml##t##n,QQmlPropertyValueInterceptor>::cast(); \
139 break
140
141
142 // This is needed for GCC v4.6 and earlier.
143 #define QPYQML_TYPE_IMPL(t, n) \
144 template void QQmlPrivate::createInto<QPyQml##t##n>(void *)
145
146
147 QPYQML_TYPE_IMPL(Validator, 0);
148 QPYQML_TYPE_IMPL(Validator, 1);
149 QPYQML_TYPE_IMPL(Validator, 2);
150 QPYQML_TYPE_IMPL(Validator, 3);
151 QPYQML_TYPE_IMPL(Validator, 4);
152 QPYQML_TYPE_IMPL(Validator, 5);
153 QPYQML_TYPE_IMPL(Validator, 6);
154 QPYQML_TYPE_IMPL(Validator, 7);
155 QPYQML_TYPE_IMPL(Validator, 8);
156 QPYQML_TYPE_IMPL(Validator, 9);
157
158 QPYQML_TYPE_IMPL(Object, 0);
159 QPYQML_TYPE_IMPL(Object, 1);
160 QPYQML_TYPE_IMPL(Object, 2);
161 QPYQML_TYPE_IMPL(Object, 3);
162 QPYQML_TYPE_IMPL(Object, 4);
163 QPYQML_TYPE_IMPL(Object, 5);
164 QPYQML_TYPE_IMPL(Object, 6);
165 QPYQML_TYPE_IMPL(Object, 7);
166 QPYQML_TYPE_IMPL(Object, 8);
167 QPYQML_TYPE_IMPL(Object, 9);
168 QPYQML_TYPE_IMPL(Object, 10);
169 QPYQML_TYPE_IMPL(Object, 11);
170 QPYQML_TYPE_IMPL(Object, 12);
171 QPYQML_TYPE_IMPL(Object, 13);
172 QPYQML_TYPE_IMPL(Object, 14);
173 QPYQML_TYPE_IMPL(Object, 15);
174 QPYQML_TYPE_IMPL(Object, 16);
175 QPYQML_TYPE_IMPL(Object, 17);
176 QPYQML_TYPE_IMPL(Object, 18);
177 QPYQML_TYPE_IMPL(Object, 19);
178 QPYQML_TYPE_IMPL(Object, 20);
179 QPYQML_TYPE_IMPL(Object, 21);
180 QPYQML_TYPE_IMPL(Object, 22);
181 QPYQML_TYPE_IMPL(Object, 23);
182 QPYQML_TYPE_IMPL(Object, 24);
183 QPYQML_TYPE_IMPL(Object, 25);
184 QPYQML_TYPE_IMPL(Object, 26);
185 QPYQML_TYPE_IMPL(Object, 27);
186 QPYQML_TYPE_IMPL(Object, 28);
187 QPYQML_TYPE_IMPL(Object, 29);
188 QPYQML_TYPE_IMPL(Object, 30);
189 QPYQML_TYPE_IMPL(Object, 31);
190 QPYQML_TYPE_IMPL(Object, 32);
191 QPYQML_TYPE_IMPL(Object, 33);
192 QPYQML_TYPE_IMPL(Object, 34);
193 QPYQML_TYPE_IMPL(Object, 35);
194 QPYQML_TYPE_IMPL(Object, 36);
195 QPYQML_TYPE_IMPL(Object, 37);
196 QPYQML_TYPE_IMPL(Object, 38);
197 QPYQML_TYPE_IMPL(Object, 39);
198 QPYQML_TYPE_IMPL(Object, 40);
199 QPYQML_TYPE_IMPL(Object, 41);
200 QPYQML_TYPE_IMPL(Object, 42);
201 QPYQML_TYPE_IMPL(Object, 43);
202 QPYQML_TYPE_IMPL(Object, 44);
203 QPYQML_TYPE_IMPL(Object, 45);
204 QPYQML_TYPE_IMPL(Object, 46);
205 QPYQML_TYPE_IMPL(Object, 47);
206 QPYQML_TYPE_IMPL(Object, 48);
207 QPYQML_TYPE_IMPL(Object, 49);
208 QPYQML_TYPE_IMPL(Object, 50);
209 QPYQML_TYPE_IMPL(Object, 51);
210 QPYQML_TYPE_IMPL(Object, 52);
211 QPYQML_TYPE_IMPL(Object, 53);
212 QPYQML_TYPE_IMPL(Object, 54);
213 QPYQML_TYPE_IMPL(Object, 55);
214 QPYQML_TYPE_IMPL(Object, 56);
215 QPYQML_TYPE_IMPL(Object, 57);
216 QPYQML_TYPE_IMPL(Object, 58);
217 QPYQML_TYPE_IMPL(Object, 59);
218
219
220 // Return a pointer to the initialised registration structure for a type.
init_type(PyTypeObject * py_type,bool ctor,int revision,PyTypeObject * attached)221 static QQmlPrivate::RegisterType *init_type(PyTypeObject *py_type, bool ctor,
222 int revision, PyTypeObject *attached)
223 {
224 PyTypeObject *qobject_type = sipTypeAsPyTypeObject(sipType_QObject);
225
226 // Check the type is derived from QObject and get its meta-object.
227 if (!PyType_IsSubtype(py_type, qobject_type))
228 {
229 PyErr_SetString(PyExc_TypeError,
230 "type being registered must be a sub-type of QObject");
231 return 0;
232 }
233
234 const QMetaObject *mo = pyqt5_qtqml_get_qmetaobject(py_type);
235
236 // See if the type is a parser status.
237 bool is_parser_status = PyType_IsSubtype(py_type,
238 sipTypeAsPyTypeObject(sipType_QQmlParserStatus));
239
240 // See if the type is a property value source.
241 bool is_value_source = PyType_IsSubtype(py_type,
242 sipTypeAsPyTypeObject(sipType_QQmlPropertyValueSource));
243
244 // Check any attached type is derived from QObject and get its meta-object.
245 const QMetaObject *attached_mo;
246
247 if (attached)
248 {
249 if (!PyType_IsSubtype(attached, qobject_type))
250 {
251 PyErr_SetString(PyExc_TypeError,
252 "attached properties type must be a sub-type of QObject");
253 return 0;
254 }
255
256 attached_mo = pyqt5_qtqml_get_qmetaobject(attached);
257
258 Py_INCREF((PyObject *)attached);
259 }
260 else
261 {
262 attached_mo = 0;
263 }
264
265 QByteArray ptr_name(sipPyTypeName(py_type));
266 ptr_name.append('*');
267
268 QByteArray list_name(sipPyTypeName(py_type));
269 list_name.prepend("QQmlListProperty<");
270 list_name.append('>');
271
272 QQmlPrivate::RegisterType *rt;
273
274 // See if we have the QQuickItem registation helper from the QtQuick
275 // module. Check each time because it could be imported at any point.
276
277 typedef sipErrorState (*QQuickItemRegisterFn)(PyTypeObject *, const QMetaObject *, const QByteArray &, const QByteArray &, QQmlPrivate::RegisterType **);
278
279 static QQuickItemRegisterFn qquickitem_register = 0;
280
281 if (!qquickitem_register)
282 qquickitem_register = (QQuickItemRegisterFn)sipImportSymbol(
283 "qtquick_register_item");
284
285 if (qquickitem_register)
286 {
287 sipErrorState estate = qquickitem_register(py_type, mo, ptr_name,
288 list_name, &rt);
289
290 if (estate == sipErrorFail)
291 return 0;
292
293 if (estate == sipErrorNone)
294 {
295 complete_init(rt, revision);
296 return rt;
297 }
298 }
299
300 // Initialise the specific type.
301
302 static const sipTypeDef *qvalidator_td = 0;
303
304 if (!qvalidator_td)
305 qvalidator_td = sipFindType("QValidator");
306
307 if (qvalidator_td && PyType_IsSubtype(py_type, sipTypeAsPyTypeObject(qvalidator_td)))
308 {
309 int type_nr = QPyQmlValidatorProxy::addType(py_type);
310
311 if (type_nr >= NrOfQValidatorTypes)
312 {
313 PyErr_Format(PyExc_TypeError,
314 "a maximum of %d QValidator types may be registered with QML",
315 NrOfQValidatorTypes);
316 return 0;
317 }
318
319 rt = &validator_proxy_types[type_nr];
320
321 // Initialise those members that depend on the C++ type.
322 switch (type_nr)
323 {
324 QPYQML_TYPE_INIT(Validator, 0);
325 QPYQML_TYPE_INIT(Validator, 1);
326 QPYQML_TYPE_INIT(Validator, 2);
327 QPYQML_TYPE_INIT(Validator, 3);
328 QPYQML_TYPE_INIT(Validator, 4);
329 QPYQML_TYPE_INIT(Validator, 5);
330 QPYQML_TYPE_INIT(Validator, 6);
331 QPYQML_TYPE_INIT(Validator, 7);
332 QPYQML_TYPE_INIT(Validator, 8);
333 QPYQML_TYPE_INIT(Validator, 9);
334 }
335 }
336 else
337 {
338 int type_nr = QPyQmlObjectProxy::addType(py_type);
339
340 if (type_nr >= NrOfQObjectTypes)
341 {
342 PyErr_Format(PyExc_TypeError,
343 "a maximum of %d types may be registered with QML",
344 NrOfQObjectTypes);
345 return 0;
346 }
347
348 rt = &object_proxy_types[type_nr];
349
350 // Initialise those members that depend on the C++ type.
351 switch (type_nr)
352 {
353 QPYQML_TYPE_INIT(Object, 0);
354 QPYQML_TYPE_INIT(Object, 1);
355 QPYQML_TYPE_INIT(Object, 2);
356 QPYQML_TYPE_INIT(Object, 3);
357 QPYQML_TYPE_INIT(Object, 4);
358 QPYQML_TYPE_INIT(Object, 5);
359 QPYQML_TYPE_INIT(Object, 6);
360 QPYQML_TYPE_INIT(Object, 7);
361 QPYQML_TYPE_INIT(Object, 8);
362 QPYQML_TYPE_INIT(Object, 9);
363 QPYQML_TYPE_INIT(Object, 10);
364 QPYQML_TYPE_INIT(Object, 11);
365 QPYQML_TYPE_INIT(Object, 12);
366 QPYQML_TYPE_INIT(Object, 13);
367 QPYQML_TYPE_INIT(Object, 14);
368 QPYQML_TYPE_INIT(Object, 15);
369 QPYQML_TYPE_INIT(Object, 16);
370 QPYQML_TYPE_INIT(Object, 17);
371 QPYQML_TYPE_INIT(Object, 18);
372 QPYQML_TYPE_INIT(Object, 19);
373 QPYQML_TYPE_INIT(Object, 20);
374 QPYQML_TYPE_INIT(Object, 21);
375 QPYQML_TYPE_INIT(Object, 22);
376 QPYQML_TYPE_INIT(Object, 23);
377 QPYQML_TYPE_INIT(Object, 24);
378 QPYQML_TYPE_INIT(Object, 25);
379 QPYQML_TYPE_INIT(Object, 26);
380 QPYQML_TYPE_INIT(Object, 27);
381 QPYQML_TYPE_INIT(Object, 28);
382 QPYQML_TYPE_INIT(Object, 29);
383 QPYQML_TYPE_INIT(Object, 30);
384 QPYQML_TYPE_INIT(Object, 31);
385 QPYQML_TYPE_INIT(Object, 32);
386 QPYQML_TYPE_INIT(Object, 33);
387 QPYQML_TYPE_INIT(Object, 34);
388 QPYQML_TYPE_INIT(Object, 35);
389 QPYQML_TYPE_INIT(Object, 36);
390 QPYQML_TYPE_INIT(Object, 37);
391 QPYQML_TYPE_INIT(Object, 38);
392 QPYQML_TYPE_INIT(Object, 39);
393 QPYQML_TYPE_INIT(Object, 40);
394 QPYQML_TYPE_INIT(Object, 41);
395 QPYQML_TYPE_INIT(Object, 42);
396 QPYQML_TYPE_INIT(Object, 43);
397 QPYQML_TYPE_INIT(Object, 44);
398 QPYQML_TYPE_INIT(Object, 45);
399 QPYQML_TYPE_INIT(Object, 46);
400 QPYQML_TYPE_INIT(Object, 47);
401 QPYQML_TYPE_INIT(Object, 48);
402 QPYQML_TYPE_INIT(Object, 49);
403 QPYQML_TYPE_INIT(Object, 50);
404 QPYQML_TYPE_INIT(Object, 51);
405 QPYQML_TYPE_INIT(Object, 52);
406 QPYQML_TYPE_INIT(Object, 53);
407 QPYQML_TYPE_INIT(Object, 54);
408 QPYQML_TYPE_INIT(Object, 55);
409 QPYQML_TYPE_INIT(Object, 56);
410 QPYQML_TYPE_INIT(Object, 57);
411 QPYQML_TYPE_INIT(Object, 58);
412 QPYQML_TYPE_INIT(Object, 59);
413 }
414 }
415
416 rt->metaObject = mo;
417 rt->attachedPropertiesMetaObject = attached_mo;
418
419 complete_init(rt, revision);
420
421 return rt;
422 }
423
424
425 // Complete the initialisation of a type registration structure.
complete_init(QQmlPrivate::RegisterType * rt,int revision)426 static void complete_init(QQmlPrivate::RegisterType *rt, int revision)
427 {
428 if (revision < 0)
429 {
430 rt->version = 0;
431 rt->revision = 0;
432 }
433 else
434 {
435 rt->version = 1;
436 rt->revision = revision;
437 }
438
439 rt->uri = 0;
440 rt->versionMajor = 0;
441 rt->versionMinor = 0;
442 rt->elementName = 0;
443 rt->extensionObjectCreate = 0;
444 rt->extensionMetaObject = 0;
445 rt->customParser = 0;
446 }
447
448
449 // Return the proxy that created an object. This is called with the GIL.
qpyqml_find_proxy_for(QObject * proxied)450 QObject *qpyqml_find_proxy_for(QObject *proxied)
451 {
452 QSetIterator<QObject *> oit(QPyQmlObjectProxy::proxies);
453
454 while (oit.hasNext())
455 {
456 QPyQmlObjectProxy *proxy = static_cast<QPyQmlObjectProxy *>(oit.next());
457
458 if (proxy->proxied.data() == proxied)
459 return proxy;
460 }
461
462 QSetIterator<QObject *> vit(QPyQmlValidatorProxy::proxies);
463
464 while (vit.hasNext())
465 {
466 QPyQmlValidatorProxy *proxy = static_cast<QPyQmlValidatorProxy *>(vit.next());
467
468 if (proxy->proxied.data() == proxied)
469 return proxy;
470 }
471
472 PyErr_Format(PyExc_TypeError,
473 "QObject instance at %p was not created from QML", proxied);
474
475 return 0;
476 }
477