1 // This implements the helpers for QObject.
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 <ctype.h>
22
23 #include <Python.h>
24
25 #include <QMetaObject>
26 #include <QMetaType>
27 #include <QObject>
28 #include <QVariant>
29
30 #include "qpycore_api.h"
31 #include "qpycore_chimera.h"
32 #include "qpycore_misc.h"
33 #include "qpycore_objectified_strings.h"
34 #include "qpycore_pyqtboundsignal.h"
35 #include "qpycore_pyqtproperty.h"
36 #include "qpycore_pyqtpyobject.h"
37 #include "qpycore_pyqtslot.h"
38 #include "qpycore_qobject_helpers.h"
39 #include "qpycore_types.h"
40
41 #include "sipAPIQtCore.h"
42
43
44 // Forward declarations.
45 static int qt_metacall_worker(sipSimpleWrapper *pySelf, PyTypeObject *pytype,
46 sipTypeDef *base, QMetaObject::Call _c, int _id, void **_a);
47
48
49 // This is the helper for all implementations of QObject::metaObject().
qpycore_qobject_metaobject(sipSimpleWrapper * pySelf,sipTypeDef * base)50 const QMetaObject *qpycore_qobject_metaobject(sipSimpleWrapper *pySelf,
51 sipTypeDef *base)
52 {
53 sipWrapperType *wt = (pySelf ? (sipWrapperType *)Py_TYPE(pySelf) : 0);
54
55 return qpycore_get_qmetaobject(wt, base);
56 }
57
58
59 // This is the helper for all implementations of QObject::qt_metacall().
qpycore_qobject_qt_metacall(sipSimpleWrapper * pySelf,sipTypeDef * base,QMetaObject::Call _c,int _id,void ** _a)60 int qpycore_qobject_qt_metacall(sipSimpleWrapper *pySelf, sipTypeDef *base,
61 QMetaObject::Call _c, int _id, void **_a)
62 {
63 // Check if the Python object has gone.
64 if (!pySelf)
65 return -1;
66
67 return qt_metacall_worker(pySelf, Py_TYPE(pySelf), base, _c, _id, _a);
68 }
69
70
71 // This does the real work for all implementations of QObject::qt_metacall().
qt_metacall_worker(sipSimpleWrapper * pySelf,PyTypeObject * pytype,sipTypeDef * base,QMetaObject::Call _c,int _id,void ** _a)72 static int qt_metacall_worker(sipSimpleWrapper *pySelf, PyTypeObject *pytype,
73 sipTypeDef *base, QMetaObject::Call _c, int _id, void **_a)
74 {
75 // See if this is a wrapped C++ type rather than a Python sub-type.
76 if (pytype == sipTypeAsPyTypeObject(base))
77 return _id;
78
79 PyTypeObject *tp_base;
80
81 #if PY_VERSION_HEX >= 0x03040000
82 tp_base = reinterpret_cast<PyTypeObject *>(
83 PyType_GetSlot(pytype, Py_tp_base));
84 #else
85 tp_base = pytype->tp_base;
86 #endif
87
88 _id = qt_metacall_worker(pySelf, tp_base, base, _c, _id, _a);
89
90 if (_id < 0)
91 return _id;
92
93 qpycore_metaobject *qo = reinterpret_cast<qpycore_metaobject *>(
94 sipGetTypeUserData((sipWrapperType *)pytype));
95
96 bool ok = true;
97
98 if (_c == QMetaObject::InvokeMetaMethod)
99 {
100 if (_id < qo->nr_signals + qo->pslots.count())
101 {
102 if (_id < qo->nr_signals)
103 {
104 QObject *qthis = reinterpret_cast<QObject *>(sipGetCppPtr(pySelf, sipType_QObject));
105
106 Py_BEGIN_ALLOW_THREADS
107 QMetaObject::activate(qthis, qo->mo, _id, _a);
108 Py_END_ALLOW_THREADS
109 }
110 else
111 {
112 PyQtSlot *slot = qo->pslots.at(_id - qo->nr_signals);
113
114 ok = slot->invoke(_a, (PyObject *)pySelf, _a[0]);
115 }
116 }
117
118 _id -= qo->nr_signals + qo->pslots.count();
119 }
120 else if (_c == QMetaObject::ReadProperty)
121 {
122 if (_id < qo->pprops.count())
123 {
124 qpycore_pyqtProperty *prop = qo->pprops.at(_id);
125
126 if (prop->pyqtprop_get)
127 {
128 PyObject *py = PyObject_CallFunction(prop->pyqtprop_get,
129 const_cast<char *>("O"), pySelf);
130
131 if (py)
132 {
133 ok = prop->pyqtprop_parsed_type->fromPyObject(py, _a[0]);
134 Py_DECREF(py);
135 }
136 else
137 {
138 ok = false;
139 }
140 }
141 }
142
143 _id -= qo->pprops.count();
144 }
145 else if (_c == QMetaObject::WriteProperty)
146 {
147 if (_id < qo->pprops.count())
148 {
149 qpycore_pyqtProperty *prop = qo->pprops.at(_id);
150
151 if (prop->pyqtprop_set)
152 {
153 PyObject *py = prop->pyqtprop_parsed_type->toPyObject(_a[0]);
154
155 if (py)
156 {
157 PyObject *res = PyObject_CallFunction(prop->pyqtprop_set,
158 const_cast<char *>("OO"), pySelf, py);
159
160 if (res)
161 Py_DECREF(res);
162 else
163 ok = false;
164
165 Py_DECREF(py);
166 }
167 else
168 {
169 ok = false;
170 }
171 }
172 }
173
174 _id -= qo->pprops.count();
175 }
176 else if (_c == QMetaObject::ResetProperty)
177 {
178 if (_id < qo->pprops.count())
179 {
180 qpycore_pyqtProperty *prop = qo->pprops.at(_id);
181
182 if (prop->pyqtprop_reset)
183 {
184 PyObject *py = PyObject_CallFunction(prop->pyqtprop_reset,
185 const_cast<char *>("O"), pySelf);
186
187 if (py)
188 Py_DECREF(py);
189 else
190 ok = false;
191 }
192 }
193
194 _id -= qo->pprops.count();
195 }
196 else if (_c == QMetaObject::QueryPropertyDesignable)
197 _id -= qo->pprops.count();
198 else if (_c == QMetaObject::QueryPropertyScriptable)
199 _id -= qo->pprops.count();
200 else if (_c == QMetaObject::QueryPropertyStored)
201 _id -= qo->pprops.count();
202 else if (_c == QMetaObject::QueryPropertyEditable)
203 _id -= qo->pprops.count();
204 else if (_c == QMetaObject::QueryPropertyUser)
205 _id -= qo->pprops.count();
206
207 // Handle any Python errors.
208 if (!ok)
209 {
210 pyqt5_err_print();
211 return -1;
212 }
213
214 return _id;
215 }
216
217
218 // This is the helper for all implementations of QObject::qt_metacast().
qpycore_qobject_qt_metacast(sipSimpleWrapper * pySelf,const sipTypeDef * base,const char * _clname,void ** sipCpp)219 bool qpycore_qobject_qt_metacast(sipSimpleWrapper *pySelf,
220 const sipTypeDef *base, const char *_clname, void **sipCpp)
221 {
222 *sipCpp = 0;
223
224 if (!_clname)
225 return true;
226
227 // Check if the Python object has gone.
228 if (!pySelf)
229 return true;
230
231 bool is_py_class = false;
232
233 SIP_BLOCK_THREADS
234
235 PyTypeObject *base_pytype = sipTypeAsPyTypeObject(base);
236
237 // We only need to look at the MRO if the object is a wrapped type (rather
238 // than a user sub-class).
239 if (base_pytype != Py_TYPE(pySelf))
240 {
241 PyObject *mro;
242
243 #if PY_VERSION_HEX >= 0x03040000
244 mro = PyObject_GetAttr((PyObject *)Py_TYPE(pySelf),
245 qpycore_dunder_mro);
246 Q_ASSERT(mro);
247 #else
248 mro = Py_TYPE(pySelf)->tp_mro;
249 #endif
250
251 for (Py_ssize_t i = 0; i < PyTuple_Size(mro); ++i)
252 {
253 PyTypeObject *pytype = (PyTypeObject *)PyTuple_GetItem(mro, i);
254
255 const sipTypeDef *td = sipTypeFromPyTypeObject(pytype);
256
257 if (!td || !qpycore_is_pyqt_class(td))
258 continue;
259
260 if (qstrcmp(sipPyTypeName(pytype), _clname) == 0)
261 {
262 // The generated type definitions represent the C++ (rather
263 // than Python) hierachy. If the C++ hierachy doesn't match
264 // then the super-type must be provided by a mixin. Note that
265 // we check the type in both "directions" as we may be either
266 // "side" of the base type in the MRO.
267 if (PyType_IsSubtype(pytype, base_pytype) || PyType_IsSubtype(base_pytype, pytype))
268 *sipCpp = sipGetAddress(pySelf);
269 else
270 *sipCpp = sipGetMixinAddress(pySelf, td);
271
272 is_py_class = true;
273 break;
274 }
275
276 const char *iface = reinterpret_cast<const pyqt5ClassPluginDef *>(
277 sipTypePluginData(td))->qt_interface;
278
279 if (iface && qstrcmp(iface, _clname) == 0)
280 {
281 *sipCpp = sipGetMixinAddress(pySelf, td);
282 is_py_class = true;
283 break;
284 }
285 }
286
287 #if PY_VERSION_HEX >= 0x03040000
288 Py_DECREF(mro);
289 #endif
290 }
291
292 SIP_UNBLOCK_THREADS
293
294 return is_py_class;
295 }
296
297
298 // This is a helper for QObject.staticMetaObject %GetCode.
qpycore_qobject_staticmetaobject(PyObject * type_obj)299 PyObject *qpycore_qobject_staticmetaobject(PyObject *type_obj)
300 {
301 const QMetaObject *mo = qpycore_get_qmetaobject((sipWrapperType *)type_obj);
302
303 if (!mo)
304 {
305 // We assume that this is a side effect of a wrapped class not being
306 // fully ready until sip's meta-class's __init__() has run (rather than
307 // after its __new__() method as might be expected).
308 PyErr_SetString(PyExc_AttributeError,
309 "staticMetaObject isn't available until the meta-class's __init__ returns");
310
311 return 0;
312 }
313
314 return sipConvertFromType(const_cast<QMetaObject *>(mo),
315 sipType_QMetaObject, 0);
316 }
317