##// END OF EJS Templates
added support for downcasting via polymorphic handlers...
florianlink -
r26:dcd28715f924
parent child
Show More
@@ -1,1164 +1,1181
1 1 /*
2 2 *
3 3 * Copyright (C) 2006 MeVis Research GmbH All Rights Reserved.
4 4 *
5 5 * This library is free software; you can redistribute it and/or
6 6 * modify it under the terms of the GNU Lesser General Public
7 7 * License as published by the Free Software Foundation; either
8 8 * version 2.1 of the License, or (at your option) any later version.
9 9 *
10 10 * This library is distributed in the hope that it will be useful,
11 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 13 * Lesser General Public License for more details.
14 14 *
15 15 * Further, this software is distributed without any warranty that it is
16 16 * free of the rightful claim of any third person regarding infringement
17 17 * or the like. Any license provided herein, whether implied or
18 18 * otherwise, applies only to this software file. Patent licenses, if
19 19 * any, provided herein do not apply to combinations of this program with
20 20 * other software, or any other product whatsoever.
21 21 *
22 22 * You should have received a copy of the GNU Lesser General Public
23 23 * License along with this library; if not, write to the Free Software
24 24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 25 *
26 26 * Contact information: MeVis Research GmbH, Universitaetsallee 29,
27 27 * 28359 Bremen, Germany or:
28 28 *
29 29 * http://www.mevis.de
30 30 *
31 31 */
32 32
33 33 //----------------------------------------------------------------------------------
34 34 /*!
35 35 // \file PythonQt.cpp
36 36 // \author Florian Link
37 37 // \author Last changed by $Author: florian $
38 38 // \date 2006-05
39 39 */
40 40 //----------------------------------------------------------------------------------
41 41
42 42 #include "PythonQt.h"
43 43 #include "PythonQtImporter.h"
44 44 #include "PythonQtClassInfo.h"
45 45 #include "PythonQtMethodInfo.h"
46 46 #include "PythonQtSignalReceiver.h"
47 47 #include "PythonQtConversion.h"
48 48 #include "PythonQtStdOut.h"
49 49 #include "PythonQtCppWrapperFactory.h"
50 50 #include "PythonQtVariants.h"
51 51 #include "PythonQtStdDecorators.h"
52 52 #include "PythonQtQFileImporter.h"
53 53 #include <pydebug.h>
54 54 #include <vector>
55 55
56 56 PythonQt* PythonQt::_self = NULL;
57 57 int PythonQt::_uniqueModuleCount = 0;
58 58
59 59 void PythonQt::init(int flags)
60 60 {
61 61 if (!_self) {
62 62 _self = new PythonQt(flags);
63 63 }
64 64
65 65 PythonQtMethodInfo::addParameterTypeAlias("QObjectList", "QList<QObject*>");
66 66 qRegisterMetaType<QList<QObject*> >("QList<void*>");
67 67
68 68 PythonQtRegisterToolClassesTemplateConverter(int);
69 69 PythonQtRegisterToolClassesTemplateConverter(float);
70 70 PythonQtRegisterToolClassesTemplateConverter(double);
71 71 // TODO: which other POD types should be available for QList etc.
72 72
73 73 PythonQt::self()->addDecorators(new PythonQtStdDecorators());
74 74
75 75 PythonQt::self()->registerCPPClass("Qt", "", "QtCore", PythonQtCreateObject<PythonQtWrapper_Qt>);
76 76 PythonQt::self()->registerCPPClass("QBitArray", "", "QtCore", PythonQtCreateObject<PythonQtWrapper_QBitArray>);
77 77 PythonQt::self()->registerCPPClass("QDate", "", "QtCore", PythonQtCreateObject<PythonQtWrapper_QDate>);
78 78 PythonQt::self()->registerCPPClass("QTime", "", "QtCore", PythonQtCreateObject<PythonQtWrapper_QTime>);
79 79 PythonQt::self()->registerCPPClass("QDateTime", "", "QtCore", PythonQtCreateObject<PythonQtWrapper_QDateTime>);
80 80 PythonQt::self()->registerCPPClass("QUrl", "", "QtCore", PythonQtCreateObject<PythonQtWrapper_QUrl>);
81 81 PythonQt::self()->registerCPPClass("QLocale", "", "QtCore", PythonQtCreateObject<PythonQtWrapper_QLocale>);
82 82 PythonQt::self()->registerCPPClass("QRect", "", "QtCore", PythonQtCreateObject<PythonQtWrapper_QRect>);
83 83 PythonQt::self()->registerCPPClass("QRectF", "", "QtCore", PythonQtCreateObject<PythonQtWrapper_QRectF>);
84 84 PythonQt::self()->registerCPPClass("QSize", "", "QtCore", PythonQtCreateObject<PythonQtWrapper_QSize>);
85 85 PythonQt::self()->registerCPPClass("QSizeF", "", "QtCore", PythonQtCreateObject<PythonQtWrapper_QSizeF>);
86 86 PythonQt::self()->registerCPPClass("QLine", "", "QtCore", PythonQtCreateObject<PythonQtWrapper_QLine>);
87 87 PythonQt::self()->registerCPPClass("QLineF", "", "QtCore", PythonQtCreateObject<PythonQtWrapper_QLineF>);
88 88 PythonQt::self()->registerCPPClass("QPoint", "", "QtCore", PythonQtCreateObject<PythonQtWrapper_QPoint>);
89 89 PythonQt::self()->registerCPPClass("QPointF", "", "QtCore", PythonQtCreateObject<PythonQtWrapper_QPointF>);
90 90 PythonQt::self()->registerCPPClass("QRegExp", "", "QtCore", PythonQtCreateObject<PythonQtWrapper_QRegExp>);
91 91
92 92 PythonQtRegisterToolClassesTemplateConverter(QDate);
93 93 PythonQtRegisterToolClassesTemplateConverter(QTime);
94 94 PythonQtRegisterToolClassesTemplateConverter(QDateTime);
95 95 PythonQtRegisterToolClassesTemplateConverter(QUrl);
96 96 PythonQtRegisterToolClassesTemplateConverter(QLocale);
97 97 PythonQtRegisterToolClassesTemplateConverter(QRect);
98 98 PythonQtRegisterToolClassesTemplateConverter(QRectF);
99 99 PythonQtRegisterToolClassesTemplateConverter(QSize);
100 100 PythonQtRegisterToolClassesTemplateConverter(QSizeF);
101 101 PythonQtRegisterToolClassesTemplateConverter(QLine);
102 102 PythonQtRegisterToolClassesTemplateConverter(QLineF);
103 103 PythonQtRegisterToolClassesTemplateConverter(QPoint);
104 104 PythonQtRegisterToolClassesTemplateConverter(QPointF);
105 105 PythonQtRegisterToolClassesTemplateConverter(QRegExp);
106 106
107 107 PythonQt::self()->registerCPPClass("QFont", "", "QtGui", PythonQtCreateObject<PythonQtWrapper_QFont>);
108 108 PythonQt::self()->registerCPPClass("QPixmap", "", "QtGui", PythonQtCreateObject<PythonQtWrapper_QPixmap>);
109 109 PythonQt::self()->registerCPPClass("QBrush", "", "QtGui", PythonQtCreateObject<PythonQtWrapper_QBrush>);
110 110 PythonQt::self()->registerCPPClass("QColor", "", "QtGui", PythonQtCreateObject<PythonQtWrapper_QColor>);
111 111 PythonQt::self()->registerCPPClass("QPalette", "", "QtGui", PythonQtCreateObject<PythonQtWrapper_QPalette>);
112 112 PythonQt::self()->registerCPPClass("QIcon", "", "QtGui", PythonQtCreateObject<PythonQtWrapper_QIcon>);
113 113 PythonQt::self()->registerCPPClass("QImage", "", "QtGui", PythonQtCreateObject<PythonQtWrapper_QImage>);
114 114 PythonQt::self()->registerCPPClass("QPolygon", "", "QtGui", PythonQtCreateObject<PythonQtWrapper_QPolygon>);
115 115 PythonQt::self()->registerCPPClass("QRegion", "", "QtGui", PythonQtCreateObject<PythonQtWrapper_QRegion>);
116 116 PythonQt::self()->registerCPPClass("QBitmap", "", "QtGui", PythonQtCreateObject<PythonQtWrapper_QBitmap>);
117 117 PythonQt::self()->registerCPPClass("QCursor", "", "QtGui", PythonQtCreateObject<PythonQtWrapper_QCursor>);
118 118 PythonQt::self()->registerCPPClass("QSizePolicy", "", "QtGui", PythonQtCreateObject<PythonQtWrapper_QSizePolicy>);
119 119 PythonQt::self()->registerCPPClass("QKeySequence", "", "QtGui", PythonQtCreateObject<PythonQtWrapper_QKeySequence>);
120 120 PythonQt::self()->registerCPPClass("QPen", "", "QtGui", PythonQtCreateObject<PythonQtWrapper_QPen>);
121 121 PythonQt::self()->registerCPPClass("QTextLength", "", "QtGui", PythonQtCreateObject<PythonQtWrapper_QTextLength>);
122 122 PythonQt::self()->registerCPPClass("QTextFormat", "", "QtGui", PythonQtCreateObject<PythonQtWrapper_QTextFormat>);
123 123 PythonQt::self()->registerCPPClass("QMatrix", "", "QtGui", PythonQtCreateObject<PythonQtWrapper_QMatrix>);
124 124
125 125 PythonQtRegisterToolClassesTemplateConverter(QFont);
126 126 PythonQtRegisterToolClassesTemplateConverter(QPixmap);
127 127 PythonQtRegisterToolClassesTemplateConverter(QBrush);
128 128 PythonQtRegisterToolClassesTemplateConverter(QColor);
129 129 PythonQtRegisterToolClassesTemplateConverter(QPalette);
130 130 PythonQtRegisterToolClassesTemplateConverter(QIcon);
131 131 PythonQtRegisterToolClassesTemplateConverter(QImage);
132 132 PythonQtRegisterToolClassesTemplateConverter(QPolygon);
133 133 PythonQtRegisterToolClassesTemplateConverter(QRegion);
134 134 PythonQtRegisterToolClassesTemplateConverter(QBitmap);
135 135 PythonQtRegisterToolClassesTemplateConverter(QCursor);
136 136 PythonQtRegisterToolClassesTemplateConverter(QSizePolicy);
137 137 PythonQtRegisterToolClassesTemplateConverter(QKeySequence);
138 138 PythonQtRegisterToolClassesTemplateConverter(QPen);
139 139 PythonQtRegisterToolClassesTemplateConverter(QTextLength);
140 140 PythonQtRegisterToolClassesTemplateConverter(QTextFormat);
141 141 PythonQtRegisterToolClassesTemplateConverter(QMatrix);
142 142
143 143 }
144 144
145 145 void PythonQt::cleanup()
146 146 {
147 147 if (_self) {
148 148 delete _self;
149 149 _self = NULL;
150 150 }
151 151 }
152 152
153 153 PythonQt::PythonQt(int flags)
154 154 {
155 155 _p = new PythonQtPrivate;
156 156 _p->_initFlags = flags;
157 157
158 158 _p->_PythonQtObjectPtr_metaId = qRegisterMetaType<PythonQtObjectPtr>("PythonQtObjectPtr");
159 159
160 160 Py_SetProgramName("PythonQt");
161 161 if (flags & IgnoreSiteModule) {
162 162 // this prevents the automatic importing of Python site files
163 163 Py_NoSiteFlag = 1;
164 164 }
165 165 Py_Initialize();
166 166
167 167 // add our own python object types for qt object slots
168 168 if (PyType_Ready(&PythonQtSlotFunction_Type) < 0) {
169 169 std::cerr << "could not initialize PythonQtSlotFunction_Type" << ", in " << __FILE__ << ":" << __LINE__ << std::endl;
170 170 }
171 171 Py_INCREF(&PythonQtSlotFunction_Type);
172 172
173 173 // according to Python docs, set the type late here, since it can not safely be stored in the struct when declaring it
174 174 PythonQtClassWrapper_Type.tp_base = &PyType_Type;
175 175 // add our own python object types for classes
176 176 if (PyType_Ready(&PythonQtClassWrapper_Type) < 0) {
177 177 std::cerr << "could not initialize PythonQtClassWrapper_Type" << ", in " << __FILE__ << ":" << __LINE__ << std::endl;
178 178 }
179 179 Py_INCREF(&PythonQtClassWrapper_Type);
180 180
181 181 // add our own python object types for CPP instances
182 182 if (PyType_Ready(&PythonQtInstanceWrapper_Type) < 0) {
183 183 PythonQt::handleError();
184 184 std::cerr << "could not initialize PythonQtInstanceWrapper_Type" << ", in " << __FILE__ << ":" << __LINE__ << std::endl;
185 185 }
186 186 Py_INCREF(&PythonQtInstanceWrapper_Type);
187 187
188 188 // add our own python object types for redirection of stdout
189 189 if (PyType_Ready(&PythonQtStdOutRedirectType) < 0) {
190 190 std::cerr << "could not initialize PythonQtStdOutRedirectType" << ", in " << __FILE__ << ":" << __LINE__ << std::endl;
191 191 }
192 192 Py_INCREF(&PythonQtStdOutRedirectType);
193 193
194 194 initPythonQtModule(flags & RedirectStdOut);
195 195
196 196 }
197 197
198 198 PythonQt::~PythonQt() {
199 199 delete _p;
200 200 _p = NULL;
201 201 }
202 202
203 203 PythonQtPrivate::~PythonQtPrivate() {
204 204 delete _defaultImporter;
205 205 _defaultImporter = NULL;
206 206
207 207 {
208 208 QHashIterator<QByteArray, PythonQtClassInfo *> i(_knownClassInfos);
209 209 while (i.hasNext()) {
210 210 delete i.next().value();
211 211 }
212 212 }
213 213 PythonQtConv::global_valueStorage.clear();
214 214 PythonQtConv::global_ptrStorage.clear();
215 215 PythonQtConv::global_variantStorage.clear();
216 216
217 217 PythonQtMethodInfo::cleanupCachedMethodInfos();
218 218 }
219 219
220 220 PythonQtImportFileInterface* PythonQt::importInterface()
221 221 {
222 222 return _self->_p->_importInterface?_self->_p->_importInterface:_self->_p->_defaultImporter;
223 223 }
224 224
225 225 void PythonQt::qObjectNoLongerWrappedCB(QObject* o)
226 226 {
227 227 if (_self->_p->_noLongerWrappedCB) {
228 228 (*_self->_p->_noLongerWrappedCB)(o);
229 229 };
230 230 }
231 231
232 232 void PythonQt::registerClass(const QMetaObject* metaobject, const char* package, PythonQtQObjectCreatorFunctionCB* wrapperCreator, PythonQtShellSetInstanceWrapperCB* shell)
233 233 {
234 234 _p->registerClass(metaobject, package, wrapperCreator, shell);
235 235 }
236 236
237 237 void PythonQtPrivate::registerClass(const QMetaObject* metaobject, const char* package, PythonQtQObjectCreatorFunctionCB* wrapperCreator, PythonQtShellSetInstanceWrapperCB* shell)
238 238 {
239 239 // we register all classes in the hierarchy
240 240 const QMetaObject* m = metaobject;
241 241 bool first = true;
242 242 while (m) {
243 243 PythonQtClassInfo* info = lookupClassInfoAndCreateIfNotPresent(m->className());
244 244 if (!info->pythonQtClassWrapper()) {
245 245 info->setupQObject(m);
246 246 createPythonQtClassWrapper(info, package);
247 247 if (m->superClass()) {
248 248 PythonQtClassInfo* parentInfo = lookupClassInfoAndCreateIfNotPresent(m->superClass()->className());
249 249 info->addParentClass(PythonQtClassInfo::ParentClassInfo(parentInfo));
250 250 }
251 251 }
252 252 if (first) {
253 253 first = false;
254 254 if (wrapperCreator) {
255 255 info->setDecoratorProvider(wrapperCreator);
256 256 }
257 257 if (shell) {
258 258 info->setShellSetInstanceWrapperCB(shell);
259 259 }
260 260 }
261 261 m = m->superClass();
262 262 }
263 263 }
264 264
265 265 void PythonQtPrivate::createPythonQtClassWrapper(PythonQtClassInfo* info, const char* package)
266 266 {
267 267 PyObject* pack = packageByName(package);
268 268 PyObject* pyobj = (PyObject*)createNewPythonQtClassWrapper(info, package);
269 269 PyModule_AddObject(pack, info->className(), pyobj);
270 270 if (package && strncmp(package,"Qt",2)==0) {
271 271 // since PyModule_AddObject steals the reference, we need a incref once more...
272 272 Py_INCREF(pyobj);
273 273 // put all qt objects into Qt as well
274 274 PyModule_AddObject(packageByName("Qt"), info->className(), pyobj);
275 275 }
276 276 info->setPythonQtClassWrapper(pyobj);
277 277 }
278 278
279 279 bool PythonQtPrivate::isEnumType(const QMetaObject* meta, const QByteArray& name) {
280 280 int i = meta?meta->indexOfEnumerator(name.constData()):-1;
281 281 if (i!=-1) {
282 282 return true;
283 283 } else {
284 284 // look for scope
285 285 int scopePos = name.indexOf("::");
286 286 if (scopePos != -1) {
287 287 // slit into scope and enum name
288 288 QByteArray enumScope = name.mid(0,scopePos);
289 289 QByteArray enumName = name.mid(scopePos+2);
290 290 if (enumScope == "Qt") {
291 291 // special qt namespace case
292 292 return isEnumType(&staticQtMetaObject, enumName);
293 293 } else {
294 294 // look for known classes as scope
295 295 // TODO: Q_GADGETS are not yet handled
296 296 PythonQtClassInfo* info = _knownClassInfos.value(enumScope);
297 297 if (info) {
298 298 return isEnumType(info->metaObject(), enumName);
299 299 }
300 300 }
301 301 }
302 302 }
303 303 return false;
304 304 }
305 305
306 306 PyObject* PythonQtPrivate::wrapQObject(QObject* obj)
307 307 {
308 308 if (!obj) {
309 309 Py_INCREF(Py_None);
310 310 return Py_None;
311 311 }
312 312 PythonQtInstanceWrapper* wrap = findWrapperAndRemoveUnused(obj);
313 313 if (!wrap) {
314 314 // smuggling it in...
315 315 PythonQtClassInfo* classInfo = _knownClassInfos.value(obj->metaObject()->className());
316 316 if (!classInfo || classInfo->pythonQtClassWrapper()==NULL) {
317 317 registerClass(obj->metaObject());
318 318 classInfo = _knownClassInfos.value(obj->metaObject()->className());
319 319 }
320 320 wrap = createNewPythonQtInstanceWrapper(obj, classInfo);
321 321 // mlabDebugConst("MLABPython","new qobject wrapper added " << " " << wrap->_obj->className() << " " << wrap->classInfo()->wrappedClassName().latin1());
322 322 } else {
323 323 Py_INCREF(wrap);
324 324 // mlabDebugConst("MLABPython","qobject wrapper reused " << wrap->_obj->className() << " " << wrap->classInfo()->wrappedClassName().latin1());
325 325 }
326 326 return (PyObject*)wrap;
327 327 }
328 328
329 329 PyObject* PythonQtPrivate::wrapPtr(void* ptr, const QByteArray& name)
330 330 {
331 331 if (!ptr) {
332 332 Py_INCREF(Py_None);
333 333 return Py_None;
334 334 }
335 335
336 336 PythonQtInstanceWrapper* wrap = findWrapperAndRemoveUnused(ptr);
337 337 if (!wrap) {
338 338 PythonQtClassInfo* info = _knownClassInfos.value(name);
339 339 if (!info) {
340 340 // maybe it is a PyObject, which we can return directly
341 341 if (name == "PyObject") {
342 342 PyObject* p = (PyObject*)ptr;
343 343 Py_INCREF(p);
344 344 return p;
345 345 }
346 346
347 347 // we do not know the metaobject yet, but we might know it by it's name:
348 348 if (_knownQObjectClassNames.find(name)!=_knownQObjectClassNames.end()) {
349 349 // yes, we know it, so we can convert to QObject
350 350 QObject* qptr = (QObject*)ptr;
351 351 registerClass(qptr->metaObject());
352 352 info = _knownClassInfos.value(qptr->metaObject()->className());
353 353 }
354 354 }
355 355 if (info && info->isQObject()) {
356 356 QObject* qptr = (QObject*)ptr;
357 357 // if the object is a derived object, we want to switch the class info to the one of the derived class:
358 358 if (name!=(qptr->metaObject()->className())) {
359 359 registerClass(qptr->metaObject());
360 360 info = _knownClassInfos.value(qptr->metaObject()->className());
361 361 }
362 362 wrap = createNewPythonQtInstanceWrapper(qptr, info);
363 363 // mlabDebugConst("MLABPython","new qobject wrapper added " << " " << wrap->_obj->className() << " " << wrap->classInfo()->wrappedClassName().latin1());
364 364 return (PyObject*)wrap;
365 365 }
366 366
367 367 // not a known QObject, so try our wrapper factory:
368 368 QObject* wrapper = NULL;
369 369 for (int i=0; i<_cppWrapperFactories.size(); i++) {
370 370 wrapper = _cppWrapperFactories.at(i)->create(name, ptr);
371 371 if (wrapper) {
372 372 break;
373 373 }
374 374 }
375
376 if (info) {
377 // try to downcast in the class hierarchy, which will modify info and ptr if it is successfull
378 ptr = info->castDownIfPossible(ptr, &info);
379 }
380
375 381 if (!info || info->pythonQtClassWrapper()==NULL) {
376 382 // still unknown, register as CPP class
377 383 registerCPPClass(name.constData());
378 384 info = _knownClassInfos.value(name);
379 385 }
380 386 if (wrapper && (info->metaObject() != wrapper->metaObject())) {
381 387 // if we a have a QObject wrapper and the metaobjects do not match, set the metaobject again!
382 388 info->setMetaObject(wrapper->metaObject());
383 389 }
384 390 wrap = createNewPythonQtInstanceWrapper(wrapper, info, ptr);
385 391 // mlabDebugConst("MLABPython","new c++ wrapper added " << wrap->_wrappedPtr << " " << wrap->_obj->className() << " " << wrap->classInfo()->wrappedClassName().latin1());
386 392 } else {
387 393 Py_INCREF(wrap);
388 394 //mlabDebugConst("MLABPython","c++ wrapper reused " << wrap->_wrappedPtr << " " << wrap->_obj->className() << " " << wrap->classInfo()->wrappedClassName().latin1());
389 395 }
390 396 return (PyObject*)wrap;
391 397 }
392 398
393 399 PyObject* PythonQtPrivate::dummyTuple() {
394 400 static PyObject* dummyTuple = NULL;
395 401 if (dummyTuple==NULL) {
396 402 dummyTuple = PyTuple_New(1);
397 403 PyTuple_SET_ITEM(dummyTuple, 0 , PyString_FromString("dummy"));
398 404 }
399 405 return dummyTuple;
400 406 }
401 407
402 408
403 409 PythonQtInstanceWrapper* PythonQtPrivate::createNewPythonQtInstanceWrapper(QObject* obj, PythonQtClassInfo* info, void* wrappedPtr) {
404 410 // call the associated class type to create a new instance...
405 411 PythonQtInstanceWrapper* result = (PythonQtInstanceWrapper*)PyObject_Call(info->pythonQtClassWrapper(), dummyTuple(), NULL);
406 412
407 413 result->setQObject(obj);
408 414 result->_wrappedPtr = wrappedPtr;
409 415 result->_ownedByPythonQt = false;
410 416 result->_useQMetaTypeDestroy = false;
411 417
412 418 if (wrappedPtr) {
413 419 _wrappedObjects.insert(wrappedPtr, result);
414 420 } else {
415 421 _wrappedObjects.insert(obj, result);
416 422 if (obj->parent()== NULL && _wrappedCB) {
417 423 // tell someone who is interested that the qobject is wrapped the first time, if it has no parent
418 424 (*_wrappedCB)(obj);
419 425 }
420 426 }
421 427 return result;
422 428 }
423 429
424 430 PythonQtClassWrapper* PythonQtPrivate::createNewPythonQtClassWrapper(PythonQtClassInfo* info, const char* package) {
425 431 PythonQtClassWrapper* result;
426 432
427 433 PyObject* className = PyString_FromString(info->className());
428 434
429 435 PyObject* baseClasses = PyTuple_New(1);
430 436 PyTuple_SET_ITEM(baseClasses, 0, (PyObject*)&PythonQtInstanceWrapper_Type);
431 437
432 438 PyObject* typeDict = PyDict_New();
433 439 QByteArray moduleName("PythonQt");
434 440 if (package && strcmp(package, "")!=0) {
435 441 moduleName += ".";
436 442 moduleName += package;
437 443 }
438 444 PyDict_SetItemString(typeDict, "__module__", PyString_FromString(moduleName.constData()));
439 445
440 446 PyObject* args = Py_BuildValue("OOO", className, baseClasses, typeDict);
441 447
442 448 // set the class info so that PythonQtClassWrapper_new can read it
443 449 _currentClassInfoForClassWrapperCreation = info;
444 450 // create the new type object by calling the type
445 451 result = (PythonQtClassWrapper *)PyObject_Call((PyObject *)&PythonQtClassWrapper_Type, args, NULL);
446 452
447 453 Py_DECREF(baseClasses);
448 454 Py_DECREF(typeDict);
449 455 Py_DECREF(args);
450 456 Py_DECREF(className);
451 457
452 458 return result;
453 459 }
454 460
455 461
456 462 PythonQtSignalReceiver* PythonQt::getSignalReceiver(QObject* obj)
457 463 {
458 464 PythonQtSignalReceiver* r = _p->_signalReceivers[obj];
459 465 if (!r) {
460 466 r = new PythonQtSignalReceiver(obj);
461 467 _p->_signalReceivers.insert(obj, r);
462 468 }
463 469 return r;
464 470 }
465 471
466 472 bool PythonQt::addSignalHandler(QObject* obj, const char* signal, PyObject* module, const QString& objectname)
467 473 {
468 474 bool flag = false;
469 475 PythonQtObjectPtr callable = lookupCallable(module, objectname);
470 476 if (callable) {
471 477 PythonQtSignalReceiver* r = getSignalReceiver(obj);
472 478 flag = r->addSignalHandler(signal, callable);
473 479 if (!flag) {
474 480 // signal not found
475 481 }
476 482 } else {
477 483 // callable not found
478 484 }
479 485 return flag;
480 486 }
481 487
482 488 bool PythonQt::addSignalHandler(QObject* obj, const char* signal, PyObject* receiver)
483 489 {
484 490 bool flag = false;
485 491 PythonQtSignalReceiver* r = getSignalReceiver(obj);
486 492 if (r) {
487 493 flag = r->addSignalHandler(signal, receiver);
488 494 }
489 495 return flag;
490 496 }
491 497
492 498 bool PythonQt::removeSignalHandler(QObject* obj, const char* signal, PyObject* module, const QString& objectname)
493 499 {
494 500 bool flag = false;
495 501 PythonQtObjectPtr callable = lookupCallable(module, objectname);
496 502 if (callable) {
497 503 PythonQtSignalReceiver* r = _p->_signalReceivers[obj];
498 504 if (r) {
499 505 flag = r->removeSignalHandler(signal, callable);
500 506 }
501 507 } else {
502 508 // callable not found
503 509 }
504 510 return flag;
505 511 }
506 512
507 513 bool PythonQt::removeSignalHandler(QObject* obj, const char* signal, PyObject* receiver)
508 514 {
509 515 bool flag = false;
510 516 PythonQtSignalReceiver* r = _p->_signalReceivers[obj];
511 517 if (r) {
512 518 flag = r->removeSignalHandler(signal, receiver);
513 519 }
514 520 return flag;
515 521 }
516 522
517 523 PythonQtObjectPtr PythonQt::lookupCallable(PyObject* module, const QString& name)
518 524 {
519 525 PythonQtObjectPtr p = lookupObject(module, name);
520 526 if (p) {
521 527 if (PyCallable_Check(p)) {
522 528 return p;
523 529 }
524 530 }
525 531 PyErr_Clear();
526 532 return NULL;
527 533 }
528 534
529 535 PythonQtObjectPtr PythonQt::lookupObject(PyObject* module, const QString& name)
530 536 {
531 537 QStringList l = name.split('.');
532 538 PythonQtObjectPtr p = module;
533 539 PythonQtObjectPtr prev;
534 540 QString s;
535 541 QByteArray b;
536 542 for (QStringList::ConstIterator i = l.begin(); i!=l.end() && p; ++i) {
537 543 prev = p;
538 544 b = (*i).toLatin1();
539 545 if (PyDict_Check(p)) {
540 546 p = PyDict_GetItemString(p, b.data());
541 547 } else {
542 548 p.setNewRef(PyObject_GetAttrString(p, b.data()));
543 549 }
544 550 }
545 551 PyErr_Clear();
546 552 return p;
547 553 }
548 554
549 555 PythonQtObjectPtr PythonQt::getMainModule() {
550 556 //both borrowed
551 557 PythonQtObjectPtr dict = PyImport_GetModuleDict();
552 558 return PyDict_GetItemString(dict, "__main__");
553 559 }
554 560
555 561 QVariant PythonQt::evalCode(PyObject* object, PyObject* pycode) {
556 562 QVariant result;
557 563 if (pycode) {
558 564 PyObject* dict = NULL;
559 565 if (PyModule_Check(object)) {
560 566 dict = PyModule_GetDict(object);
561 567 } else if (PyDict_Check(object)) {
562 568 dict = object;
563 569 }
564 570 PyObject* r = NULL;
565 571 if (dict) {
566 572 r = PyEval_EvalCode((PyCodeObject*)pycode, dict , dict);
567 573 }
568 574 if (r) {
569 575 result = PythonQtConv::PyObjToQVariant(r);
570 576 Py_DECREF(r);
571 577 } else {
572 578 handleError();
573 579 }
574 580 } else {
575 581 handleError();
576 582 }
577 583 return result;
578 584 }
579 585
580 586 QVariant PythonQt::evalScript(PyObject* object, const QString& script, int start)
581 587 {
582 588 QVariant result;
583 589 PythonQtObjectPtr p;
584 590 PyObject* dict = NULL;
585 591 if (PyModule_Check(object)) {
586 592 dict = PyModule_GetDict(object);
587 593 } else if (PyDict_Check(object)) {
588 594 dict = object;
589 595 }
590 596 if (dict) {
591 597 p.setNewRef(PyRun_String(script.toLatin1().data(), start, dict, dict));
592 598 }
593 599 if (p) {
594 600 result = PythonQtConv::PyObjToQVariant(p);
595 601 } else {
596 602 handleError();
597 603 }
598 604 return result;
599 605 }
600 606
601 607 void PythonQt::evalFile(PyObject* module, const QString& filename)
602 608 {
603 609 PythonQtObjectPtr code = parseFile(filename);
604 610 if (code) {
605 611 evalCode(module, code);
606 612 } else {
607 613 handleError();
608 614 }
609 615 }
610 616
611 617 PythonQtObjectPtr PythonQt::parseFile(const QString& filename)
612 618 {
613 619 PythonQtObjectPtr p;
614 620 p.setNewRef(PythonQtImport::getCodeFromPyc(filename));
615 621 if (!p) {
616 622 handleError();
617 623 }
618 624 return p;
619 625 }
620 626
621 627 PythonQtObjectPtr PythonQt::createModuleFromFile(const QString& name, const QString& filename)
622 628 {
623 629 PythonQtObjectPtr code = parseFile(filename);
624 630 PythonQtObjectPtr module = _p->createModule(name, code);
625 631 return module;
626 632 }
627 633
628 634 PythonQtObjectPtr PythonQt::createModuleFromScript(const QString& name, const QString& script)
629 635 {
630 636 PyErr_Clear();
631 637 QString scriptCode = script;
632 638 if (scriptCode.isEmpty()) {
633 639 // we always need at least a linefeed
634 640 scriptCode = "\n";
635 641 }
636 642 PythonQtObjectPtr pycode;
637 643 pycode.setNewRef(Py_CompileString((char*)scriptCode.toLatin1().data(), "", Py_file_input));
638 644 PythonQtObjectPtr module = _p->createModule(name, pycode);
639 645 return module;
640 646 }
641 647
642 648 PythonQtObjectPtr PythonQt::createUniqueModule()
643 649 {
644 650 static QString pyQtStr("PythonQt_module");
645 651 QString moduleName = pyQtStr+QString::number(_uniqueModuleCount++);
646 652 return createModuleFromScript(moduleName);
647 653 }
648 654
649 655 void PythonQt::addObject(PyObject* object, const QString& name, QObject* qObject)
650 656 {
651 657 if (PyModule_Check(object)) {
652 658 PyModule_AddObject(object, name.toLatin1().data(), _p->wrapQObject(qObject));
653 659 } else if (PyDict_Check(object)) {
654 660 PyDict_SetItemString(object, name.toLatin1().data(), _p->wrapQObject(qObject));
655 661 } else {
656 662 PyObject_SetAttrString(object, name.toLatin1().data(), _p->wrapQObject(qObject));
657 663 }
658 664 }
659 665
660 666 void PythonQt::addVariable(PyObject* object, const QString& name, const QVariant& v)
661 667 {
662 668 if (PyModule_Check(object)) {
663 669 PyModule_AddObject(object, name.toLatin1().data(), PythonQtConv::QVariantToPyObject(v));
664 670 } else if (PyDict_Check(object)) {
665 671 PyDict_SetItemString(object, name.toLatin1().data(), PythonQtConv::QVariantToPyObject(v));
666 672 } else {
667 673 PyObject_SetAttrString(object, name.toLatin1().data(), PythonQtConv::QVariantToPyObject(v));
668 674 }
669 675 }
670 676
671 677 void PythonQt::removeVariable(PyObject* object, const QString& name)
672 678 {
673 679 if (PyDict_Check(object)) {
674 680 PyDict_DelItemString(object, name.toLatin1().data());
675 681 } else {
676 682 PyObject_DelAttrString(object, name.toLatin1().data());
677 683 }
678 684 }
679 685
680 686 QVariant PythonQt::getVariable(PyObject* object, const QString& objectname)
681 687 {
682 688 QVariant result;
683 689 PythonQtObjectPtr obj = lookupObject(object, objectname);
684 690 if (obj) {
685 691 result = PythonQtConv::PyObjToQVariant(obj);
686 692 }
687 693 return result;
688 694 }
689 695
690 696 QStringList PythonQt::introspection(PyObject* module, const QString& objectname, PythonQt::ObjectType type)
691 697 {
692 698 QStringList results;
693 699
694 700 PythonQtObjectPtr object;
695 701 if (objectname.isEmpty()) {
696 702 object = module;
697 703 } else {
698 704 object = lookupObject(module, objectname);
699 705 if (!object && type == CallOverloads) {
700 706 PyObject* dict = lookupObject(module, "__builtins__");
701 707 if (dict) {
702 708 object = PyDict_GetItemString(dict, objectname.toLatin1().constData());
703 709 }
704 710 }
705 711 }
706 712
707 713 if (object) {
708 714 if (type == CallOverloads) {
709 715 if (PythonQtSlotFunction_Check(object)) {
710 716 PythonQtSlotFunctionObject* o = (PythonQtSlotFunctionObject*)object.object();
711 717 PythonQtSlotInfo* info = o->m_ml;
712 718
713 719 while (info) {
714 720 results << info->fullSignature();
715 721 info = info->nextInfo();
716 722 }
717 723 } else if (object->ob_type == &PythonQtClassWrapper_Type) {
718 724 PythonQtClassWrapper* o = (PythonQtClassWrapper*)object.object();
719 725 PythonQtSlotInfo* info = o->classInfo()->constructors();
720 726
721 727 while (info) {
722 728 results << info->fullSignature();
723 729 info = info->nextInfo();
724 730 }
725 731 } else {
726 732 //TODO: use pydoc!
727 733 PyObject* doc = PyObject_GetAttrString(object, "__doc__");
728 734 if (doc) {
729 735 results << PyString_AsString(doc);
730 736 Py_DECREF(doc);
731 737 }
732 738 }
733 739 } else {
734 740 PyObject* keys = NULL;
735 741 bool isDict = false;
736 742 if (PyDict_Check(object)) {
737 743 keys = PyDict_Keys(object);
738 744 isDict = true;
739 745 } else {
740 746 keys = PyObject_Dir(object);
741 747 }
742 748 if (keys) {
743 749 int count = PyList_Size(keys);
744 750 PyObject* key;
745 751 PyObject* value;
746 752 QString keystr;
747 753 for (int i = 0;i<count;i++) {
748 754 key = PyList_GetItem(keys,i);
749 755 if (isDict) {
750 756 value = PyDict_GetItem(object, key);
751 757 Py_INCREF(value);
752 758 } else {
753 759 value = PyObject_GetAttr(object, key);
754 760 }
755 761 if (!value) continue;
756 762 keystr = PyString_AsString(key);
757 763 static const QString underscoreStr("__tmp");
758 764 if (!keystr.startsWith(underscoreStr)) {
759 765 switch (type) {
760 766 case Anything:
761 767 results << keystr;
762 768 break;
763 769 case Class:
764 770 if (value->ob_type == &PyClass_Type) {
765 771 results << keystr;
766 772 }
767 773 break;
768 774 case Variable:
769 775 if (value->ob_type != &PyClass_Type
770 776 && value->ob_type != &PyCFunction_Type
771 777 && value->ob_type != &PyFunction_Type
772 778 && value->ob_type != &PyModule_Type
773 779 ) {
774 780 results << keystr;
775 781 }
776 782 break;
777 783 case Function:
778 784 if (value->ob_type == &PyFunction_Type ||
779 785 value->ob_type == &PyMethod_Type
780 786 ) {
781 787 results << keystr;
782 788 }
783 789 break;
784 790 case Module:
785 791 if (value->ob_type == &PyModule_Type) {
786 792 results << keystr;
787 793 }
788 794 break;
789 795 default:
790 796 std::cerr << "PythonQt: introspection: unknown case" << ", in " << __FILE__ << ":" << __LINE__ << std::endl;
791 797 }
792 798 }
793 799 Py_DECREF(value);
794 800 }
795 801 Py_DECREF(keys);
796 802 }
797 803 }
798 804 }
799 805 return results;
800 806 }
801 807
802 808 QVariant PythonQt::call(PyObject* module, const QString& name, const QVariantList& args)
803 809 {
804 810 QVariant r;
805 811
806 812 PythonQtObjectPtr callable = lookupCallable(module, name);
807 813 if (callable) {
808 814 PythonQtObjectPtr pargs;
809 815 int count = args.size();
810 816 if (count>0) {
811 817 pargs.setNewRef(PyTuple_New(count));
812 818 }
813 819 bool err = false;
814 820 // transform QVariants to Python
815 821 for (int i = 0; i < count; i++) {
816 822 PyObject* arg = PythonQtConv::QVariantToPyObject(args.at(i));
817 823 if (arg) {
818 824 // steals reference, no unref
819 825 PyTuple_SetItem(pargs, i,arg);
820 826 } else {
821 827 err = true;
822 828 break;
823 829 }
824 830 }
825 831
826 832 if (!err) {
827 833 PyErr_Clear();
828 834 PythonQtObjectPtr result;
829 835 result.setNewRef(PyObject_CallObject(callable, pargs));
830 836 if (result) {
831 837 // ok
832 838 r = PythonQtConv::PyObjToQVariant(result);
833 839 } else {
834 840 PythonQt::self()->handleError();
835 841 }
836 842 }
837 843 }
838 844 return r;
839 845 }
840 846
841 847 void PythonQt::addInstanceDecorators(QObject* o)
842 848 {
843 849 _p->addDecorators(o, PythonQtPrivate::InstanceDecorator);
844 850 }
845 851
846 852 void PythonQt::addClassDecorators(QObject* o)
847 853 {
848 854 _p->addDecorators(o, PythonQtPrivate::StaticDecorator | PythonQtPrivate::ConstructorDecorator | PythonQtPrivate::DestructorDecorator);
849 855 }
850 856
851 857 void PythonQt::addDecorators(QObject* o)
852 858 {
853 859 _p->addDecorators(o, PythonQtPrivate::AllDecorators);
854 860 }
855 861
856 862 void PythonQt::registerQObjectClassNames(const QStringList& names)
857 863 {
858 864 _p->registerQObjectClassNames(names);
859 865 }
860 866
861 867 void PythonQt::setImporter(PythonQtImportFileInterface* importInterface)
862 868 {
863 869 PythonQtImport::init();
864 870 _p->_importInterface = importInterface;
865 871 }
866 872
867 873 void PythonQt::setImporterIgnorePaths(const QStringList& paths)
868 874 {
869 875 _p->_importIgnorePaths = paths;
870 876 }
871 877
872 878 const QStringList& PythonQt::getImporterIgnorePaths()
873 879 {
874 880 return _p->_importIgnorePaths;
875 881 }
876 882
877 883 void PythonQt::addWrapperFactory(PythonQtCppWrapperFactory* factory)
878 884 {
879 885 _p->_cppWrapperFactories.append(factory);
880 886 }
881 887
882 888 //---------------------------------------------------------------------------------------------------
883 889 PythonQtPrivate::PythonQtPrivate()
884 890 {
885 891 _importInterface = NULL;
886 892 _defaultImporter = new PythonQtQFileImporter;
887 893 _noLongerWrappedCB = NULL;
888 894 _wrappedCB = NULL;
889 895 _currentClassInfoForClassWrapperCreation = NULL;
890 896 }
891 897
892 898 PythonQtClassInfo* PythonQtPrivate::currentClassInfoForClassWrapperCreation()
893 899 {
894 900 PythonQtClassInfo* info = _currentClassInfoForClassWrapperCreation;
895 901 _currentClassInfoForClassWrapperCreation = NULL;
896 902 return info;
897 903 }
898 904
899 905 void PythonQtPrivate::addDecorators(QObject* o, int decoTypes)
900 906 {
901 907 o->setParent(this);
902 908 int numMethods = o->metaObject()->methodCount();
903 909 for (int i = 0; i < numMethods; i++) {
904 910 QMetaMethod m = o->metaObject()->method(i);
905 911 if ((m.methodType() == QMetaMethod::Method ||
906 912 m.methodType() == QMetaMethod::Slot) && m.access() == QMetaMethod::Public) {
907 913 const PythonQtMethodInfo* info = PythonQtMethodInfo::getCachedMethodInfo(m);
908 914 if (qstrncmp(m.signature(), "new_", 4)==0) {
909 915 if ((decoTypes & ConstructorDecorator) == 0) continue;
910 916 // either it returns a * or a QVariant and the name starts with "new_"
911 917 bool isVariantReturn = info->parameters().at(0).typeId == PythonQtMethodInfo::Variant;
912 918 if ((info->parameters().at(0).isPointer || isVariantReturn)) {
913 919 QByteArray signature = m.signature();
914 920 QByteArray nameOfClass = signature.mid(4, signature.indexOf('(')-4);
915 921 PythonQtClassInfo* classInfo = lookupClassInfoAndCreateIfNotPresent(nameOfClass);
916 922 PythonQtSlotInfo* newSlot = new PythonQtSlotInfo(m, i, o, PythonQtSlotInfo::ClassDecorator);
917 923 classInfo->addConstructor(newSlot);
918 924 }
919 925 } else if (qstrncmp(m.signature(), "delete_", 7)==0) {
920 926 if ((decoTypes & DestructorDecorator) == 0) continue;
921 927 QByteArray signature = m.signature();
922 928 QByteArray nameOfClass = signature.mid(7, signature.indexOf('(')-7);
923 929 PythonQtClassInfo* classInfo = lookupClassInfoAndCreateIfNotPresent(nameOfClass);
924 930 PythonQtSlotInfo* newSlot = new PythonQtSlotInfo(m, i, o, PythonQtSlotInfo::ClassDecorator);
925 931 classInfo->setDestructor(newSlot);
926 932 } else if (qstrncmp(m.signature(), "static_", 7)==0) {
927 933 if ((decoTypes & StaticDecorator) == 0) continue;
928 934 QByteArray signature = m.signature();
929 935 QByteArray nameOfClass = signature.mid(signature.indexOf('_')+1);
930 936 nameOfClass = nameOfClass.mid(0, nameOfClass.indexOf('_'));
931 937 PythonQtClassInfo* classInfo = lookupClassInfoAndCreateIfNotPresent(nameOfClass);
932 938 PythonQtSlotInfo* newSlot = new PythonQtSlotInfo(m, i, o, PythonQtSlotInfo::ClassDecorator);
933 939 classInfo->addDecoratorSlot(newSlot);
934 940 } else {
935 941 if ((decoTypes & InstanceDecorator) == 0) continue;
936 942 if (info->parameters().count()>1) {
937 943 PythonQtMethodInfo::ParameterInfo p = info->parameters().at(1);
938 944 if (p.isPointer) {
939 945 PythonQtClassInfo* classInfo = lookupClassInfoAndCreateIfNotPresent(p.name);
940 946 PythonQtSlotInfo* newSlot = new PythonQtSlotInfo(m, i, o, PythonQtSlotInfo::InstanceDecorator);
941 947 classInfo->addDecoratorSlot(newSlot);
942 948 }
943 949 }
944 950 }
945 951 }
946 952 }
947 953 }
948 954
949 955 void PythonQtPrivate::registerQObjectClassNames(const QStringList& names)
950 956 {
951 957 foreach(QString name, names) {
952 958 _knownQObjectClassNames.insert(name.toLatin1(), true);
953 959 }
954 960 }
955 961
956 962 void PythonQtPrivate::removeSignalEmitter(QObject* obj)
957 963 {
958 964 _signalReceivers.remove(obj);
959 965 }
960 966
961 967 bool PythonQt::handleError()
962 968 {
963 969 bool flag = false;
964 970 if (PyErr_Occurred()) {
965 971
966 972 // currently we just print the error and the stderr handler parses the errors
967 973 PyErr_Print();
968 974
969 975 /*
970 976 // EXTRA: the format of the ptype and ptraceback is not really documented, so I use PyErr_Print() above
971 977 PyObject *ptype;
972 978 PyObject *pvalue;
973 979 PyObject *ptraceback;
974 980 PyErr_Fetch( &ptype, &pvalue, &ptraceback);
975 981
976 982 Py_XDECREF(ptype);
977 983 Py_XDECREF(pvalue);
978 984 Py_XDECREF(ptraceback);
979 985 */
980 986 PyErr_Clear();
981 987 flag = true;
982 988 }
983 989 return flag;
984 990 }
985 991
986 992 void PythonQt::addSysPath(const QString& path)
987 993 {
988 994 PythonQtObjectPtr sys;
989 995 sys.setNewRef(PyImport_ImportModule("sys"));
990 996 PythonQtObjectPtr obj = lookupObject(sys, "path");
991 997 PyList_Insert(obj, 0, PythonQtConv::QStringToPyObject(path));
992 998 }
993 999
994 1000 void PythonQt::overwriteSysPath(const QStringList& paths)
995 1001 {
996 1002 PythonQtObjectPtr sys;
997 1003 sys.setNewRef(PyImport_ImportModule("sys"));
998 1004 PyModule_AddObject(sys, "path", PythonQtConv::QStringListToPyList(paths));
999 1005 }
1000 1006
1001 1007 void PythonQt::setModuleImportPath(PyObject* module, const QStringList& paths)
1002 1008 {
1003 1009 PyModule_AddObject(module, "__path__", PythonQtConv::QStringListToPyList(paths));
1004 1010 }
1005 1011
1006 1012 void PythonQt::stdOutRedirectCB(const QString& str)
1007 1013 {
1008 1014 emit PythonQt::self()->pythonStdOut(str);
1009 1015 }
1010 1016
1011 1017 void PythonQt::stdErrRedirectCB(const QString& str)
1012 1018 {
1013 1019 emit PythonQt::self()->pythonStdErr(str);
1014 1020 }
1015 1021
1016 1022 void PythonQt::setQObjectWrappedCallback(PythonQtQObjectWrappedCB* cb)
1017 1023 {
1018 1024 _p->_wrappedCB = cb;
1019 1025 }
1020 1026
1021 1027 void PythonQt::setQObjectNoLongerWrappedCallback(PythonQtQObjectNoLongerWrappedCB* cb)
1022 1028 {
1023 1029 _p->_noLongerWrappedCB = cb;
1024 1030 }
1025 1031
1026 1032
1027 1033
1028 1034 static PyMethodDef PythonQtMethods[] = {
1029 1035 {NULL, NULL, 0, NULL}
1030 1036 };
1031 1037
1032 1038 void PythonQt::initPythonQtModule(bool redirectStdOut)
1033 1039 {
1034 1040 _p->_pythonQtModule = Py_InitModule("PythonQt", PythonQtMethods);
1035 1041
1036 1042 if (redirectStdOut) {
1037 1043 PythonQtObjectPtr sys;
1038 1044 PythonQtObjectPtr out;
1039 1045 PythonQtObjectPtr err;
1040 1046 sys.setNewRef(PyImport_ImportModule("sys"));
1041 1047 // create a redirection object for stdout and stderr
1042 1048 out = PythonQtStdOutRedirectType.tp_new(&PythonQtStdOutRedirectType,NULL, NULL);
1043 1049 ((PythonQtStdOutRedirect*)out.object())->_cb = stdOutRedirectCB;
1044 1050 err = PythonQtStdOutRedirectType.tp_new(&PythonQtStdOutRedirectType,NULL, NULL);
1045 1051 ((PythonQtStdOutRedirect*)err.object())->_cb = stdErrRedirectCB;
1046 1052 // replace the built in file objects with our own objects
1047 1053 PyModule_AddObject(sys, "stdout", out);
1048 1054 PyModule_AddObject(sys, "stderr", err);
1049 1055 }
1050 1056 }
1051 1057
1052 1058 void PythonQt::registerCPPClass(const char* typeName, const char* parentTypeName, const char* package, PythonQtQObjectCreatorFunctionCB* wrapperCreator, PythonQtShellSetInstanceWrapperCB* shell)
1053 1059 {
1054 1060 _p->registerCPPClass(typeName, parentTypeName, package, wrapperCreator, shell);
1055 1061 }
1056 1062
1057 1063
1058 1064 PythonQtClassInfo* PythonQtPrivate::lookupClassInfoAndCreateIfNotPresent(const char* typeName)
1059 1065 {
1060 1066 PythonQtClassInfo* info = _knownClassInfos.value(typeName);
1061 1067 if (!info) {
1062 1068 info = new PythonQtClassInfo();
1063 1069 info->setupCPPObject(typeName);
1064 1070 _knownClassInfos.insert(typeName, info);
1065 1071 }
1066 1072 return info;
1067 1073 }
1068 1074
1075 void PythonQt::addPolymorphicHandler(const char* typeName, PythonQtPolymorphicHandlerCB* cb)
1076 {
1077 _p->addPolymorphicHandler(typeName, cb);
1078 }
1079
1080 void PythonQtPrivate::addPolymorphicHandler(const char* typeName, PythonQtPolymorphicHandlerCB* cb)
1081 {
1082 PythonQtClassInfo* info = lookupClassInfoAndCreateIfNotPresent(typeName);
1083 info->addPolymorphicHandler(cb);
1084 }
1085
1069 1086 bool PythonQt::addParentClass(const char* typeName, const char* parentTypeName, int upcastingOffset)
1070 1087 {
1071 1088 return _p->addParentClass(typeName, parentTypeName, upcastingOffset);
1072 1089 }
1073 1090
1074 1091 bool PythonQtPrivate::addParentClass(const char* typeName, const char* parentTypeName, int upcastingOffset)
1075 1092 {
1076 1093 PythonQtClassInfo* info = _knownClassInfos.value(typeName);
1077 1094 if (info) {
1078 1095 PythonQtClassInfo* parentInfo = lookupClassInfoAndCreateIfNotPresent(parentTypeName);
1079 1096 info->addParentClass(PythonQtClassInfo::ParentClassInfo(parentInfo, upcastingOffset));
1080 1097 return true;
1081 1098 } else {
1082 1099 return false;
1083 1100 }
1084 1101 }
1085 1102
1086 1103 void PythonQtPrivate::registerCPPClass(const char* typeName, const char* parentTypeName, const char* package, PythonQtQObjectCreatorFunctionCB* wrapperCreator, PythonQtShellSetInstanceWrapperCB* shell)
1087 1104 {
1088 1105 PythonQtClassInfo* info = lookupClassInfoAndCreateIfNotPresent(typeName);
1089 1106 if (!info->pythonQtClassWrapper()) {
1090 1107 info->setupCPPObject(typeName);
1091 1108 createPythonQtClassWrapper(info, package);
1092 1109 }
1093 1110 if (parentTypeName && strcmp(parentTypeName,"")!=0) {
1094 1111 addParentClass(typeName, parentTypeName, 0);
1095 1112 }
1096 1113 if (wrapperCreator) {
1097 1114 info->setDecoratorProvider(wrapperCreator);
1098 1115 }
1099 1116 if (shell) {
1100 1117 info->setShellSetInstanceWrapperCB(shell);
1101 1118 }
1102 1119 }
1103 1120
1104 1121 PyObject* PythonQtPrivate::packageByName(const char* name)
1105 1122 {
1106 1123 if (name==NULL || name[0]==0) {
1107 1124 return _pythonQtModule;
1108 1125 }
1109 1126 PyObject* v = _packages.value(name);
1110 1127 if (!v) {
1111 1128 v = PyImport_AddModule((QByteArray("PythonQt.") + name).constData());
1112 1129 _packages.insert(name, v);
1113 1130 // AddObject steals the reference, so increment it!
1114 1131 Py_INCREF(v);
1115 1132 PyModule_AddObject(_pythonQtModule, name, v);
1116 1133 }
1117 1134 return v;
1118 1135 }
1119 1136
1120 1137
1121 1138 PyObject* PythonQt::helpCalled(PythonQtClassInfo* info)
1122 1139 {
1123 1140 if (_p->_initFlags & ExternalHelp) {
1124 1141 emit pythonHelpRequest(QByteArray(info->className()));
1125 1142 return Py_BuildValue("");
1126 1143 } else {
1127 1144 return PyString_FromString(info->help().toLatin1().data());
1128 1145 }
1129 1146 }
1130 1147
1131 1148 void PythonQtPrivate::removeWrapperPointer(void* obj)
1132 1149 {
1133 1150 _wrappedObjects.remove(obj);
1134 1151 }
1135 1152
1136 1153 void PythonQtPrivate::addWrapperPointer(void* obj, PythonQtInstanceWrapper* wrapper)
1137 1154 {
1138 1155 _wrappedObjects.insert(obj, wrapper);
1139 1156 }
1140 1157
1141 1158 PythonQtInstanceWrapper* PythonQtPrivate::findWrapperAndRemoveUnused(void* obj)
1142 1159 {
1143 1160 PythonQtInstanceWrapper* wrap = _wrappedObjects.value(obj);
1144 1161 if (wrap && !wrap->_wrappedPtr && wrap->_obj == NULL) {
1145 1162 // this is a wrapper whose QObject was already removed due to destruction
1146 1163 // so the obj pointer has to be a new QObject with the same address...
1147 1164 // we remove the old one and set the copy to NULL
1148 1165 wrap->_objPointerCopy = NULL;
1149 1166 removeWrapperPointer(obj);
1150 1167 wrap = NULL;
1151 1168 }
1152 1169 return wrap;
1153 1170 }
1154 1171
1155 1172 PythonQtObjectPtr PythonQtPrivate::createModule(const QString& name, PyObject* pycode)
1156 1173 {
1157 1174 PythonQtObjectPtr result;
1158 1175 if (pycode) {
1159 1176 result.setNewRef(PyImport_ExecCodeModule((char*)name.toLatin1().data(), pycode));
1160 1177 } else {
1161 1178 PythonQt::self()->handleError();
1162 1179 }
1163 1180 return result;
1164 1181 }
@@ -1,511 +1,518
1 1 #ifndef _PYTHONQT_H
2 2 #define _PYTHONQT_H
3 3
4 4 /*
5 5 *
6 6 * Copyright (C) 2006 MeVis Research GmbH All Rights Reserved.
7 7 *
8 8 * This library is free software; you can redistribute it and/or
9 9 * modify it under the terms of the GNU Lesser General Public
10 10 * License as published by the Free Software Foundation; either
11 11 * version 2.1 of the License, or (at your option) any later version.
12 12 *
13 13 * This library is distributed in the hope that it will be useful,
14 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 16 * Lesser General Public License for more details.
17 17 *
18 18 * Further, this software is distributed without any warranty that it is
19 19 * free of the rightful claim of any third person regarding infringement
20 20 * or the like. Any license provided herein, whether implied or
21 21 * otherwise, applies only to this software file. Patent licenses, if
22 22 * any, provided herein do not apply to combinations of this program with
23 23 * other software, or any other product whatsoever.
24 24 *
25 25 * You should have received a copy of the GNU Lesser General Public
26 26 * License along with this library; if not, write to the Free Software
27 27 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 28 *
29 29 * Contact information: MeVis Research GmbH, Universitaetsallee 29,
30 30 * 28359 Bremen, Germany or:
31 31 *
32 32 * http://www.mevis.de
33 33 *
34 34 */
35 35
36 36 //----------------------------------------------------------------------------------
37 37 /*!
38 38 // \file PythonQt.h
39 39 // \author Florian Link
40 40 // \author Last changed by $Author: florian $
41 41 // \date 2006-05
42 42 */
43 43 //----------------------------------------------------------------------------------
44 44
45 45 #include "PythonQtSystem.h"
46 46 #include "PythonQtInstanceWrapper.h"
47 47 #include "PythonQtClassWrapper.h"
48 48 #include "PythonQtSlot.h"
49 49 #include "PythonQtObjectPtr.h"
50 50 #include <QObject>
51 51 #include <QVariant>
52 52 #include <QList>
53 53 #include <QHash>
54 54 #include <QByteArray>
55 55 #include <QStringList>
56 56 #include <QtDebug>
57 57 #include <iostream>
58 58
59 59
60 60 class PythonQtClassInfo;
61 61 class PythonQtPrivate;
62 62 class PythonQtMethodInfo;
63 63 class PythonQtSignalReceiver;
64 64 class PythonQtImportFileInterface;
65 65 class PythonQtCppWrapperFactory;
66 66 class PythonQtQFileImporter;
67 67
68 typedef void PythonQtQObjectWrappedCB(QObject* object);
69 typedef void PythonQtQObjectNoLongerWrappedCB(QObject* object);
68 typedef void PythonQtQObjectWrappedCB(QObject* object);
69 typedef void PythonQtQObjectNoLongerWrappedCB(QObject* object);
70 typedef void* PythonQtPolymorphicHandlerCB(const void *ptr, char **class_name);
70 71
71 72 typedef void PythonQtShellSetInstanceWrapperCB(void* object, PythonQtInstanceWrapper* wrapper);
72 73
73 74 template<class T> void PythonQtSetInstanceWrapperOnShell(void* object, PythonQtInstanceWrapper* wrapper) { ((T*)object)->_wrapper = wrapper; };
74 75
75 76 //! returns the offset that needs to be added to upcast an object of type T1 to T2
76 77 template<class T1, class T2> int PythonQtUpcastingOffset() {
77 78 return (((char*)(static_cast<T2*>(reinterpret_cast<T1*>(0x100)))) - ((char*)reinterpret_cast<T1*>(0x100)));
78 79 }
79 80
80 81 //! callback to create a QObject lazily
81 82 typedef QObject* PythonQtQObjectCreatorFunctionCB();
82 83
83 84 //! helper template to create a derived QObject class
84 85 template<class T> QObject* PythonQtCreateObject() { return new T(); };
85 86
86 87 //! the main interface to the Python Qt binding, realized as a singleton
87 88 class PYTHONQT_EXPORT PythonQt : public QObject {
88 89
89 90 Q_OBJECT
90 91
91 92 public:
92 93 enum InitFlags {
93 94 RedirectStdOut = 1, //!<< sets if the std out/err is redirected to pythonStdOut() and pythonStdErr() signals
94 95 IgnoreSiteModule = 2, //!<< sets if Python should ignore the site module
95 96 ExternalHelp = 4 //!<< sets if help() calls on PythonQt modules are forwarded to the pythonHelpRequest() signal
96 97 };
97 98
98 99 //! initialize the python qt binding (flags are a or combination of InitFlags)
99 100 static void init(int flags = IgnoreSiteModule | RedirectStdOut);
100 101
101 102 //! cleanup
102 103 static void cleanup();
103 104
104 105 //! get the singleton instance
105 106 static PythonQt* self() { return _self; }
106 107
107 108 //-----------------------------------------------------------------------------
108 109 // Public API:
109 110
110 111 //! defines the object types for introspection
111 112 enum ObjectType {
112 113 Class,
113 114 Function,
114 115 Variable,
115 116 Module,
116 117 Anything,
117 118 CallOverloads
118 119 };
119 120
120 121 //! overwrite the python sys path (call this directly after PythonQt::init() if you want to change the std python sys path)
121 122 void overwriteSysPath(const QStringList& paths);
122 123
123 124 //! prepend a path to sys.path to allow importing from it
124 125 void addSysPath(const QString& path);
125 126
126 127 //! sets the __path__ list of a module to the given list (important for local imports)
127 128 void setModuleImportPath(PyObject* module, const QStringList& paths);
128 129
129 130 //! get the __main__ module of python
130 131 PythonQtObjectPtr getMainModule();
131 132
132 133 //! registers a QObject derived class to PythonQt (this is implicitly called by addObject as well)
133 134 /* Since Qt4 does not offer a way to detect if a given classname is derived from QObject and thus has a QMetaObject,
134 135 you MUST register all your QObject derived classes here when you want them to be detected in signal and slot calls */
135 136 void registerClass(const QMetaObject* metaobject, const char* package = NULL, PythonQtQObjectCreatorFunctionCB* wrapperCreator = NULL, PythonQtShellSetInstanceWrapperCB* shell = NULL);
136 137
137 138 //! add a wrapper object for the given QMetaType typeName, also does an addClassDecorators() to add constructors for variants
138 139 //! (ownership of wrapper is passed to PythonQt)
139 140 /*! Make sure that you have done a qRegisterMetaType first, if typeName is a user type!
140 141
141 142 This will add a wrapper object that is used to make calls to the given classname \c typeName.
142 143 All slots that take a pointer to typeName as the first argument will be callable from Python on
143 144 a variant object that contains such a type.
144 145 */
145 146 void registerCPPClass(const char* typeName, const char* parentTypeName = NULL, const char* package = NULL, PythonQtQObjectCreatorFunctionCB* wrapperCreator = NULL, PythonQtShellSetInstanceWrapperCB* shell = NULL);
146 147
147 148 //! as an alternative to registerClass, you can tell PythonQt the names of QObject derived classes
148 149 //! and it will register the classes when it first sees a pointer to such a derived class
149 150 void registerQObjectClassNames(const QStringList& names);
150 151
151 152 //! add a parent class relation to the \c given typeName, the upcastingOffset is needed for multiple inheritance
152 153 //! and can be calculated using PythonQtUpcastingOffset<type,parentType>(), which also verifies that
153 154 //! type is really derived from parentType.
154 155 //! Returns false if the typeName was not yet registered.
155 156 bool addParentClass(const char* typeName, const char* parentTypeName, int upcastingOffset=0);
156 157
158 //! add a handler for polymorphic downcasting
159 void addPolymorphicHandler(const char* typeName, PythonQtPolymorphicHandlerCB* cb);
160
157 161 //! parses the given file and returns the python code object, this can then be used to call evalCode()
158 162 PythonQtObjectPtr parseFile(const QString& filename);
159 163
160 164 //! evaluates the given code and returns the result value (use Py_Compile etc. to create pycode from string)
161 165 //! If pycode is NULL, a python error is printed.
162 166 QVariant evalCode(PyObject* object, PyObject* pycode);
163 167
164 168 //! evaluates the given script code and returns the result value
165 169 QVariant evalScript(PyObject* object, const QString& script, int start = Py_file_input);
166 170
167 171 //! evaluates the given script code from file
168 172 void evalFile(PyObject* object, const QString& filename);
169 173
170 174 //! creates the new module \c name and evaluates the given file in the context of that module
171 175 //! If the \c script is empty, the module contains no initial code. You can use evalScript/evalCode to add code
172 176 //! to a module later on.
173 177 //! The user needs to make sure that the \c name is unique in the python module dictionary.
174 178 PythonQtObjectPtr createModuleFromFile(const QString& name, const QString& filename);
175 179
176 180 //! creates the new module \c name and evaluates the given script in the context of that module.
177 181 //! If the \c script is empty, the module contains no initial code. You can use evalScript/evalCode to add code
178 182 //! to a module later on.
179 183 //! The user needs to make sure that the \c name is unique in the python module dictionary.
180 184 PythonQtObjectPtr createModuleFromScript(const QString& name, const QString& script = QString());
181 185
182 186 //! create a uniquely named module, you can use evalFile or evalScript to populate the module with
183 187 //! script code
184 188 PythonQtObjectPtr createUniqueModule();
185 189
186 190 //@{ Signal handlers
187 191
188 192 //! add a signal handler to the given \c signal of \c obj and connect it to a callable \c objectname in module
189 193 bool addSignalHandler(QObject* obj, const char* signal, PyObject* module, const QString& objectname);
190 194
191 195 //! remove a signal handler from the given \c signal of \c obj
192 196 bool removeSignalHandler(QObject* obj, const char* signal, PyObject* module, const QString& objectname);
193 197
194 198 //! add a signal handler to the given \c signal of \c obj and connect it to a callable \c receiver
195 199 bool addSignalHandler(QObject* obj, const char* signal, PyObject* receiver);
196 200
197 201 //! remove a signal handler from the given \c signal of \c obj
198 202 bool removeSignalHandler(QObject* obj, const char* signal, PyObject* receiver);
199 203
200 204 //@}
201 205
202 206 //@{ Variable access
203 207
204 208 //! add the given \c qObject to the python \c object as a variable with \c name (it can be removed via clearVariable)
205 209 void addObject(PyObject* object, const QString& name, QObject* qObject);
206 210
207 211 //! add the given variable to the object
208 212 void addVariable(PyObject* object, const QString& name, const QVariant& v);
209 213
210 214 //! remove the given variable
211 215 void removeVariable(PyObject* module, const QString& name);
212 216
213 217 //! get the variable with the \c name of the \c object, returns an invalid QVariant on error
214 218 QVariant getVariable(PyObject* object, const QString& name);
215 219
216 220 //! read vars etc. in scope of an \c object, optional looking inside of an object \c objectname
217 221 QStringList introspection(PyObject* object, const QString& objectname, ObjectType type);
218 222
219 223 //! returns the found callable object or NULL
220 224 //! @return new reference
221 225 PythonQtObjectPtr lookupCallable(PyObject* object, const QString& name);
222 226
223 227 //@}
224 228
225 229 //@{ Calling of python callables
226 230
227 231 //! call the given python method, returns the result converted to a QVariant
228 232 QVariant call(PyObject* module, const QString& callable, const QVariantList& args = QVariantList());
229 233
230 234 //@}
231 235
232 236 //@{ Decorations, constructors, wrappers...
233 237
234 238
235 239 //! add an object whose slots will be used as decorator slots for
236 240 //! other QObjects or CPP classes. The slots need to follow the
237 241 //! convention that the first argument is a pointer to the wrapped object.
238 242 //! (ownership is passed to PythonQt)
239 243 /*!
240 244 Example:
241 245
242 246 A slot with the signature
243 247
244 248 \code
245 249 bool doSomething(QWidget* w, int a)
246 250 \endcode
247 251
248 252 will extend QWidget instances (and derived classes) with a "bool doSomething(int a)" slot
249 253 that will be called with the concrete instance as first argument.
250 254 So in Python you can now e.g. call
251 255
252 256 \code
253 257 someWidget.doSomething(12)
254 258 \endcode
255 259
256 260 without QWidget really having this method. This allows to easily make normal methods
257 261 of Qt classes callable by forwarding them with such decorator slots
258 262 or to make CPP classes (which are not derived from QObject) callable from Python.
259 263 */
260 264 void addInstanceDecorators(QObject* o);
261 265
262 266 //! add an object whose slots will be used as decorator slots for
263 267 //! class objects (ownership is passed to PythonQt)
264 268 /*!
265 269 The slots need to follow the following convention:
266 270 - SomeClass* new_SomeClass(...)
267 271 - QVariant new_SomeClass(...)
268 272 - void delete_SomeClass(SomeClass*)
269 273 - ... static_SomeClass_someName(...)
270 274
271 275 This will add:
272 276 - a constructor
273 277 - a constructor which generates a QVariant
274 278 - a destructor (only useful for CPP objects)
275 279 - a static decorator slot which will be available on the MetaObject (visible in PythonQt module)
276 280
277 281 */
278 282 void addClassDecorators(QObject* o);
279 283
280 284 //! this will add the object both as class and instance decorator (ownership is passed to PythonQt)
281 285 void addDecorators(QObject* o);
282 286
283 287 //! add the given factory to PythonQt (ownership stays with caller)
284 288 void addWrapperFactory(PythonQtCppWrapperFactory* factory);
285 289
286 290 //@}
287 291
288 292 //@{ Custom importer (to replace internal import implementation of python)
289 293
290 294 //! replace the internal import implementation and use the supplied interface to load files (both py and pyc files)
291 295 //! (this method should be called directly after initialization of init() and before calling overwriteSysPath().
292 296 //! On the first call to this method, it will install a generic PythonQt importer in Pythons "path_hooks".
293 297 //! This is not reversible, so even setting setImporter(NULL) afterwards will
294 298 //! keep the custom PythonQt importer with a QFile default import interface.
295 299 //! Subsequent python import calls will make use of the passed importInterface
296 300 //! which forwards all import calls to the given \c importInterface.
297 301 //! Passing NULL will install a default QFile importer.
298 302 //! (\c importInterface ownership stays with caller)
299 303 void setImporter(PythonQtImportFileInterface* importInterface);
300 304
301 305 //! this installs the default QFile importer (which effectively does a setImporter(NULL))
302 306 //! (without calling setImporter or installDefaultImporter at least once, the default python import
303 307 //! mechanism is in place)
304 308 //! the default importer allows to import files from anywhere QFile can read from,
305 309 //! including the Qt resource system using ":". Keep in mind that you need to extend
306 310 //! "sys.path" with ":" to be able to import from the Qt resources.
307 311 void installDefaultImporter() { setImporter(NULL); }
308 312
309 313 //! set paths that the importer should ignore
310 314 void setImporterIgnorePaths(const QStringList& paths);
311 315
312 316 //! get paths that the importer should ignore
313 317 const QStringList& getImporterIgnorePaths();
314 318
315 319 //@}
316 320
317 321 //! get access to internal data (should not be used on the public API, but is used by some C functions)
318 322 static PythonQtPrivate* priv() { return _self->_p; }
319 323
320 324 //! get access to the file importer (if set)
321 325 static PythonQtImportFileInterface* importInterface();
322 326
323 327 //! handle a python error, call this when a python function fails. If no error occurred, it returns false.
324 328 //! The error is currently just output to the python stderr, future version might implement better trace printing
325 329 bool handleError();
326 330
327 331 //! set a callback that is called when a QObject with parent == NULL is wrapped by pythonqt
328 332 void setQObjectWrappedCallback(PythonQtQObjectWrappedCB* cb);
329 333 //! set a callback that is called when a QObject with parent == NULL is no longer wrapped by pythonqt
330 334 void setQObjectNoLongerWrappedCallback(PythonQtQObjectNoLongerWrappedCB* cb);
331 335
332 336 //! call the callback if it is set
333 337 static void qObjectNoLongerWrappedCB(QObject* o);
334 338
335 339 signals:
336 340 //! emitted when python outputs something to stdout (and redirection is turned on)
337 341 void pythonStdOut(const QString& str);
338 342 //! emitted when python outputs something to stderr (and redirection is turned on)
339 343 void pythonStdErr(const QString& str);
340 344
341 345 //! emitted when help() is called on a PythonQt object and \c ExternalHelp is enabled
342 346 void pythonHelpRequest(const QByteArray& cppClassName);
343 347
344 348
345 349 public:
346 350 //! called by internal help methods
347 351 PyObject* helpCalled(PythonQtClassInfo* info);
348 352
349 353 //! returns the found object or NULL
350 354 //! @return new reference
351 355 PythonQtObjectPtr lookupObject(PyObject* module, const QString& name);
352 356
353 357 private:
354 358 void initPythonQtModule(bool redirectStdOut);
355 359
356 360 //! callback for stdout redirection, emits pythonStdOut signal
357 361 static void stdOutRedirectCB(const QString& str);
358 362 //! callback for stderr redirection, emits pythonStdErr signal
359 363 static void stdErrRedirectCB(const QString& str);
360 364
361 365 //! get (and create if not available) the signal receiver of that QObject, signal receiver is made child of the passed \c obj
362 366 PythonQtSignalReceiver* getSignalReceiver(QObject* obj);
363 367
364 368 PythonQt(int flags);
365 369 ~PythonQt();
366 370
367 371 static PythonQt* _self;
368 372 static int _uniqueModuleCount;
369 373
370 374 PythonQtPrivate* _p;
371 375
372 376 };
373 377
374 378 //! internal PythonQt details
375 379 class PYTHONQT_EXPORT PythonQtPrivate : public QObject {
376 380
377 381 Q_OBJECT
378 382
379 383 public:
380 384 PythonQtPrivate();
381 385 ~PythonQtPrivate();
382 386
383 387 enum DecoratorTypes {
384 388 StaticDecorator = 1,
385 389 ConstructorDecorator = 2,
386 390 DestructorDecorator = 4,
387 391 InstanceDecorator = 8,
388 392 AllDecorators = 0xffff
389 393 };
390 394
391 395 //! returns if the id is the id for PythonQtObjectPtr
392 396 bool isPythonQtObjectPtrMetaId(int id) { return _PythonQtObjectPtr_metaId == id; }
393 397
394 398 //! add the wrapper pointer (for reuse if the same obj appears while wrapper still exists)
395 399 void addWrapperPointer(void* obj, PythonQtInstanceWrapper* wrapper);
396 400 //! remove the wrapper ptr again
397 401 void removeWrapperPointer(void* obj);
398 402
399 403 //! add parent class relation
400 404 bool addParentClass(const char* typeName, const char* parentTypeName, int upcastingOffset);
401 405
406 //! add a handler for polymorphic downcasting
407 void addPolymorphicHandler(const char* typeName, PythonQtPolymorphicHandlerCB* cb);
408
402 409 //! lookup existing classinfo and return new if not yet present
403 410 PythonQtClassInfo* lookupClassInfoAndCreateIfNotPresent(const char* typeName);
404 411
405 412 //! called when a signal emitting QObject is destroyed to remove the signal handler from the hash map
406 413 void removeSignalEmitter(QObject* obj);
407 414
408 415 //! wrap the given QObject into a Python object (or return existing wrapper!)
409 416 PyObject* wrapQObject(QObject* obj);
410 417
411 418 //! wrap the given ptr into a Python object (or return existing wrapper!) if there is a known QObject of that name or a known wrapper in the factory
412 419 PyObject* wrapPtr(void* ptr, const QByteArray& name);
413 420
414 421 //! registers a QObject derived class to PythonQt (this is implicitly called by addObject as well)
415 422 /* Since Qt4 does not offer a way to detect if a given classname is derived from QObject and thus has a QMetaObject,
416 423 you MUST register all your QObject derived classes here when you want them to be detected in signal and slot calls */
417 424 void registerClass(const QMetaObject* metaobject, const char* package = NULL, PythonQtQObjectCreatorFunctionCB* wrapperCreator = NULL, PythonQtShellSetInstanceWrapperCB* shell = NULL);
418 425
419 426 //! add a wrapper object for the given QMetaType typeName, also does an addClassDecorators() to add constructors for variants
420 427 //! (ownership of wrapper is passed to PythonQt)
421 428 /*! Make sure that you have done a qRegisterMetaType first, if typeName is a user type!
422 429
423 430 This will add a wrapper object that is used to make calls to the given classname \c typeName.
424 431 All slots that take a pointer to typeName as the first argument will be callable from Python on
425 432 a variant object that contains such a type.
426 433 */
427 434 void registerCPPClass(const char* typeName, const char* parentTypeName = NULL, const char* package = NULL, PythonQtQObjectCreatorFunctionCB* wrapperCreator = NULL, PythonQtShellSetInstanceWrapperCB* shell = NULL);
428 435
429 436 //! as an alternative to registerClass, you can tell PythonQt the names of QObject derived classes
430 437 //! and it will register the classes when it first sees a pointer to such a derived class
431 438 void registerQObjectClassNames(const QStringList& names);
432 439
433 440 //! add a decorator object
434 441 void addDecorators(QObject* o, int decoTypes);
435 442
436 443 //! check if the enum is either part of the \c meta class or contains a scope and is
437 444 //! an enum of another known metaobject (and as last resort, of the Qt namespace)
438 445 bool isEnumType(const QMetaObject* meta, const QByteArray& name);
439 446
440 447 //! helper method that creates a PythonQtClassWrapper object
441 448 PythonQtClassWrapper* createNewPythonQtClassWrapper(PythonQtClassInfo* info, const char* package = NULL);
442 449
443 450 //! helper method that creates a PythonQtInstanceWrapper object and registers it in the object map
444 451 PythonQtInstanceWrapper* createNewPythonQtInstanceWrapper(QObject* obj, PythonQtClassInfo* info, void* wrappedPtr = NULL);
445 452
446 453 //! get the class info for a meta object (if available)
447 454 PythonQtClassInfo* getClassInfo(const QMetaObject* meta) { return _knownClassInfos.value(meta->className()); }
448 455
449 456 //! get the class info for a meta object (if available)
450 457 PythonQtClassInfo* getClassInfo(const QByteArray& className) { return _knownClassInfos.value(className); }
451 458
452 459 //! creates the new module from the given pycode
453 460 PythonQtObjectPtr createModule(const QString& name, PyObject* pycode);
454 461
455 462 //! get the current class info (for the next PythonQtClassWrapper that is created) and reset it to NULL again
456 463 PythonQtClassInfo* currentClassInfoForClassWrapperCreation();
457 464
458 465 //! the dummy tuple (which is empty and may be used to detected that a wrapper is called from internal wrapper creation
459 466 static PyObject* dummyTuple();
460 467
461 468 private:
462 469
463 470 //! create a new pythonqt class wrapper and place it in the pythonqt module
464 471 void createPythonQtClassWrapper(PythonQtClassInfo* info, const char* package);
465 472
466 473 //! get/create new package module (the returned object is a borrowed reference)
467 474 PyObject* packageByName(const char* name);
468 475
469 476 //! get the wrapper for a given pointer (and remove a wrapper of an already destroyed qobject)
470 477 PythonQtInstanceWrapper* findWrapperAndRemoveUnused(void* obj);
471 478
472 479 //! stores pointer to PyObject mapping of wrapped QObjects AND C++ objects
473 480 QHash<void* , PythonQtInstanceWrapper *> _wrappedObjects;
474 481
475 482 //! stores the meta info of known Qt classes
476 483 QHash<QByteArray, PythonQtClassInfo *> _knownClassInfos;
477 484
478 485 //! names of qobject derived classes that can be casted to qobject savely
479 486 QHash<QByteArray, bool> _knownQObjectClassNames;
480 487
481 488 //! stores signal receivers for QObjects
482 489 QHash<QObject* , PythonQtSignalReceiver *> _signalReceivers;
483 490
484 491 //! the PythonQt python module
485 492 PythonQtObjectPtr _pythonQtModule;
486 493
487 494 //! the importer interface (if set)
488 495 PythonQtImportFileInterface* _importInterface;
489 496
490 497 //! the default importer
491 498 PythonQtQFileImporter* _defaultImporter;
492 499
493 500 PythonQtQObjectNoLongerWrappedCB* _noLongerWrappedCB;
494 501 PythonQtQObjectWrappedCB* _wrappedCB;
495 502
496 503 QStringList _importIgnorePaths;
497 504
498 505 //! the cpp object wrapper factories
499 506 QList<PythonQtCppWrapperFactory*> _cppWrapperFactories;
500 507
501 508 QHash<QByteArray, PyObject*> _packages;
502 509
503 510 PythonQtClassInfo* _currentClassInfoForClassWrapperCreation;
504 511
505 512 int _initFlags;
506 513 int _PythonQtObjectPtr_metaId;
507 514
508 515 friend class PythonQt;
509 516 };
510 517
511 518 #endif
@@ -1,671 +1,706
1 1 /*
2 2 *
3 3 * Copyright (C) 2006 MeVis Research GmbH All Rights Reserved.
4 4 *
5 5 * This library is free software; you can redistribute it and/or
6 6 * modify it under the terms of the GNU Lesser General Public
7 7 * License as published by the Free Software Foundation; either
8 8 * version 2.1 of the License, or (at your option) any later version.
9 9 *
10 10 * This library is distributed in the hope that it will be useful,
11 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 13 * Lesser General Public License for more details.
14 14 *
15 15 * Further, this software is distributed without any warranty that it is
16 16 * free of the rightful claim of any third person regarding infringement
17 17 * or the like. Any license provided herein, whether implied or
18 18 * otherwise, applies only to this software file. Patent licenses, if
19 19 * any, provided herein do not apply to combinations of this program with
20 20 * other software, or any other product whatsoever.
21 21 *
22 22 * You should have received a copy of the GNU Lesser General Public
23 23 * License along with this library; if not, write to the Free Software
24 24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 25 *
26 26 * Contact information: MeVis Research GmbH, Universitaetsallee 29,
27 27 * 28359 Bremen, Germany or:
28 28 *
29 29 * http://www.mevis.de
30 30 *
31 31 */
32 32
33 33 //----------------------------------------------------------------------------------
34 34 /*!
35 35 // \file PythonQt.cpp
36 36 // \author Florian Link
37 37 // \author Last changed by $Author: florian $
38 38 // \date 2006-05
39 39 */
40 40 //----------------------------------------------------------------------------------
41 41
42 42 #include "PythonQtClassInfo.h"
43 43 #include "PythonQtMethodInfo.h"
44 44 #include "PythonQt.h"
45 45 #include <QMetaMethod>
46 46
47 47 QHash<QByteArray, int> PythonQtMethodInfo::_parameterTypeDict;
48 48
49 49 PythonQtClassInfo::PythonQtClassInfo() {
50 50 _meta = NULL;
51 51 _constructors = NULL;
52 52 _destructor = NULL;
53 53 _decoratorProvider = NULL;
54 54 _decoratorProviderCB = NULL;
55 55 _pythonQtClassWrapper = NULL;
56 56 _shellSetInstanceWrapperCB = NULL;
57 57 _metaTypeId = -1;
58 58 _isQObject = false;
59 59 }
60 60
61 61 PythonQtClassInfo::~PythonQtClassInfo()
62 62 {
63 63 clearCachedMembers();
64 64
65 65 if (_constructors) {
66 66 _constructors->deleteOverloadsAndThis();
67 67 }
68 68 if (_destructor) {
69 69 _destructor->deleteOverloadsAndThis();
70 70 }
71 71 foreach(PythonQtSlotInfo* info, _decoratorSlots) {
72 72 info->deleteOverloadsAndThis();
73 73 }
74 74 }
75 75
76 76 void PythonQtClassInfo::setupQObject(const QMetaObject* meta)
77 77 {
78 78 // _wrappedClassName is already set earlier in the class setup
79 79 _isQObject = true;
80 80 _meta = meta;
81 81 }
82 82
83 83 void PythonQtClassInfo::setupCPPObject(const QByteArray& classname)
84 84 {
85 85 _isQObject = false;
86 86 _wrappedClassName = classname;
87 87 _metaTypeId = QMetaType::type(classname);
88 88 }
89 89
90 90 void PythonQtClassInfo::clearCachedMembers()
91 91 {
92 92 QHashIterator<QByteArray, PythonQtMemberInfo> i(_cachedMembers);
93 93 while (i.hasNext()) {
94 94 PythonQtMemberInfo member = i.next().value();
95 95 if (member._type== PythonQtMemberInfo::Slot) {
96 96 PythonQtSlotInfo* info = member._slot;
97 97 while (info) {
98 98 PythonQtSlotInfo* next = info->nextInfo();
99 99 delete info;
100 100 info = next;
101 101 }
102 102 }
103 103 }
104 104 }
105 105
106 106 int PythonQtClassInfo::findCharOffset(const char* sigStart, char someChar)
107 107 {
108 108 const char* sigEnd = sigStart;
109 109 char c;
110 110 do {
111 111 c = *sigEnd++;
112 112 } while (c!=someChar && c!=0);
113 113 return sigEnd-sigStart-1;
114 114 }
115 115
116 116 bool PythonQtClassInfo::lookForPropertyAndCache(const char* memberName)
117 117 {
118 118 bool found = false;
119 119 bool nameMapped = false;
120 120 const char* attributeName = memberName;
121 121 // look for properties
122 122 int i = _meta->indexOfProperty(attributeName);
123 123 if (i==-1) {
124 124 // try to map name to objectName
125 125 if (qstrcmp(attributeName, "name")==0) {
126 126 attributeName = "objectName";
127 127 nameMapped = true;
128 128 i = _meta->indexOfProperty(attributeName);
129 129 }
130 130 }
131 131 if (i!=-1) {
132 132 PythonQtMemberInfo newInfo(_meta->property(i));
133 133 _cachedMembers.insert(attributeName, newInfo);
134 134 if (nameMapped) {
135 135 _cachedMembers.insert(memberName, newInfo);
136 136 }
137 137 #ifdef PYTHONQT_DEBUG
138 138 std::cout << "caching property " << memberName << " on " << _meta->className() << std::endl;
139 139 #endif
140 140 found = true;
141 141 }
142 142 return found;
143 143 }
144 144
145 145 PythonQtSlotInfo* PythonQtClassInfo::recursiveFindDecoratorSlotsFromDecoratorProvider(const char* memberName, PythonQtSlotInfo* inputInfo, bool &found, QHash<QByteArray, PythonQtMemberInfo>& memberCache, int upcastingOffset)
146 146 {
147 147 inputInfo = findDecoratorSlotsFromDecoratorProvider(memberName, inputInfo, found, memberCache, upcastingOffset);
148 148 foreach(const ParentClassInfo& info, _parentClasses) {
149 149 inputInfo = info._parent->recursiveFindDecoratorSlotsFromDecoratorProvider(memberName, inputInfo, found, memberCache, upcastingOffset+info._upcastingOffset);
150 150 }
151 151 return inputInfo;
152 152 }
153 153
154 154 PythonQtSlotInfo* PythonQtClassInfo::findDecoratorSlotsFromDecoratorProvider(const char* memberName, PythonQtSlotInfo* tail, bool &found, QHash<QByteArray, PythonQtMemberInfo>& memberCache, int upcastingOffset) {
155 155 QObject* decoratorProvider = decorator();
156 156 int memberNameLen = strlen(memberName);
157 157 if (decoratorProvider) {
158 158 //qDebug()<< "looking " << decoratorProvider->metaObject()->className() << " " << memberName << " " << upcastingOffset;
159 159 const QMetaObject* meta = decoratorProvider->metaObject();
160 160 int numMethods = meta->methodCount();
161 161 int startFrom = QObject::staticMetaObject.methodCount();
162 162 for (int i = startFrom; i < numMethods; i++) {
163 163 QMetaMethod m = meta->method(i);
164 164 if ((m.methodType() == QMetaMethod::Method ||
165 165 m.methodType() == QMetaMethod::Slot) && m.access() == QMetaMethod::Public) {
166 166
167 167 const char* sigStart = m.signature();
168 168 bool isClassDeco = false;
169 169 if (qstrncmp(sigStart, "static_", 7)==0) {
170 170 // skip the static_classname_ part of the string
171 171 sigStart += 7 + 1 + strlen(className());
172 172 isClassDeco = true;
173 173 } else if (qstrncmp(sigStart, "new_", 4)==0) {
174 174 isClassDeco = true;
175 175 } else if (qstrncmp(sigStart, "delete_", 7)==0) {
176 176 isClassDeco = true;
177 177 }
178 178 // find the first '('
179 179 int offset = findCharOffset(sigStart, '(');
180 180
181 181 // XXX no checking is currently done if the slots have correct first argument or not...
182 182
183 183 // check if same length and same name
184 184 if (memberNameLen == offset && qstrncmp(memberName, sigStart, offset)==0) {
185 185 found = true;
186 186 PythonQtSlotInfo* info = new PythonQtSlotInfo(m, i, decoratorProvider, isClassDeco?PythonQtSlotInfo::ClassDecorator:PythonQtSlotInfo::InstanceDecorator);
187 187 info->setUpcastingOffset(upcastingOffset);
188 188 //qDebug()<< "adding " << decoratorProvider->metaObject()->className() << " " << memberName << " " << upcastingOffset;
189 189 if (tail) {
190 190 tail->setNextInfo(info);
191 191 } else {
192 192 PythonQtMemberInfo newInfo(info);
193 193 memberCache.insert(memberName, newInfo);
194 194 }
195 195 tail = info;
196 196 }
197 197 }
198 198 }
199 199 }
200 200
201 201 tail = findDecoratorSlots(memberName, memberNameLen, tail, found, memberCache, upcastingOffset);
202 202
203 203 return tail;
204 204 }
205 205
206 206 bool PythonQtClassInfo::lookForMethodAndCache(const char* memberName)
207 207 {
208 208 bool found = false;
209 209 int memberNameLen = strlen(memberName);
210 210 PythonQtSlotInfo* tail = NULL;
211 211 if (_meta) {
212 212 int numMethods = _meta->methodCount();
213 213 for (int i = 0; i < numMethods; i++) {
214 214 QMetaMethod m = _meta->method(i);
215 215 if ((m.methodType() == QMetaMethod::Method ||
216 216 m.methodType() == QMetaMethod::Slot) && m.access() == QMetaMethod::Public) {
217 217
218 218 const char* sigStart = m.signature();
219 219 // find the first '('
220 220 int offset = findCharOffset(sigStart, '(');
221 221
222 222 // check if same length and same name
223 223 if (memberNameLen == offset && qstrncmp(memberName, sigStart, offset)==0) {
224 224 found = true;
225 225 PythonQtSlotInfo* info = new PythonQtSlotInfo(m, i);
226 226 if (tail) {
227 227 tail->setNextInfo(info);
228 228 } else {
229 229 PythonQtMemberInfo newInfo(info);
230 230 _cachedMembers.insert(memberName, newInfo);
231 231 }
232 232 tail = info;
233 233 }
234 234 }
235 235 }
236 236 }
237 237
238 238 // look for dynamic decorators in this class and in derived classes
239 239 tail = recursiveFindDecoratorSlotsFromDecoratorProvider(memberName, tail, found, _cachedMembers, 0);
240 240
241 241 return found;
242 242 }
243 243
244 244 bool PythonQtClassInfo::lookForEnumAndCache(const QMetaObject* meta, const char* memberName)
245 245 {
246 246 bool found = false;
247 247 // look for enum values
248 248 int enumCount = meta->enumeratorCount();
249 249 for (int i=0;i<enumCount; i++) {
250 250 QMetaEnum e = meta->enumerator(i);
251 251 for (int j=0; j < e.keyCount(); j++) {
252 252 if (qstrcmp(e.key(j), memberName)==0) {
253 253 PythonQtMemberInfo newInfo(e.value(j));
254 254 _cachedMembers.insert(memberName, newInfo);
255 255 #ifdef PYTHONQT_DEBUG
256 256 std::cout << "caching enum " << memberName << " on " << meta->className() << std::endl;
257 257 #endif
258 258 found = true;
259 259 break;
260 260 }
261 261 }
262 262 }
263 263 return found;
264 264 }
265 265
266 266 PythonQtMemberInfo PythonQtClassInfo::member(const char* memberName)
267 267 {
268 268 PythonQtMemberInfo info = _cachedMembers.value(memberName);
269 269 if (info._type != PythonQtMemberInfo::Invalid) {
270 270 return info;
271 271 } else {
272 272 bool found = false;
273 273
274 274 found = lookForPropertyAndCache(memberName);
275 275 if (!found) {
276 276 found = lookForMethodAndCache(memberName);
277 277 }
278 278 if (!found) {
279 279 if (_meta) {
280 280 // check enums in our meta object directly
281 281 found = lookForEnumAndCache(_meta, memberName);
282 282 }
283 283 if (!found) {
284 284 // check enums in the class hierachy of CPP classes
285 285 // look for dynamic decorators in this class and in derived classes
286 286 QList<QObject*> decoObjects;
287 287 recursiveCollectDecoratorObjects(decoObjects);
288 288 foreach(QObject* deco, decoObjects) {
289 289 // call on ourself for caching, but with different metaObject():
290 290 found = lookForEnumAndCache(deco->metaObject(), memberName);
291 291 if (found) {
292 292 break;
293 293 }
294 294 }
295 295 }
296 296 }
297 297 if (!found) {
298 298 // we store a NotFound member, so that we get a quick result for non existing members (e.g. operator_equal lookup)
299 299 info._type = PythonQtMemberInfo::NotFound;
300 300 _cachedMembers.insert(memberName, info);
301 301 }
302 302 }
303 303
304 304 return _cachedMembers.value(memberName);
305 305 }
306 306
307 307 void PythonQtClassInfo::recursiveCollectDecoratorObjects(QList<QObject*>& decoratorObjects) {
308 308 QObject* deco = decorator();
309 309 if (deco) {
310 310 decoratorObjects.append(deco);
311 311 }
312 312 foreach(const ParentClassInfo& info, _parentClasses) {
313 313 info._parent->recursiveCollectDecoratorObjects(decoratorObjects);
314 314 }
315 315 }
316 316
317 317 void PythonQtClassInfo::recursiveCollectClassInfos(QList<PythonQtClassInfo*>& classInfoObjects) {
318 318 classInfoObjects.append(this);
319 319 foreach(const ParentClassInfo& info, _parentClasses) {
320 320 info._parent->recursiveCollectClassInfos(classInfoObjects);
321 321 }
322 322 }
323 323
324 324 PythonQtSlotInfo* PythonQtClassInfo::findDecoratorSlots(const char* memberName, int memberNameLen, PythonQtSlotInfo* tail, bool &found, QHash<QByteArray, PythonQtMemberInfo>& memberCache, int upcastingOffset)
325 325 {
326 326 QListIterator<PythonQtSlotInfo*> it(_decoratorSlots);
327 327 while (it.hasNext()) {
328 328
329 329 PythonQtSlotInfo* infoOrig = it.next();
330 330
331 331 const char* sigStart = infoOrig->metaMethod()->signature();
332 332 if (qstrncmp("static_", sigStart, 7)==0) {
333 333 sigStart += 7;
334 334 sigStart += findCharOffset(sigStart, '_')+1;
335 335 }
336 336 int offset = findCharOffset(sigStart, '(');
337 337 if (memberNameLen == offset && qstrncmp(memberName, sigStart, offset)==0) {
338 338 //make a copy, otherwise we will have trouble on overloads!
339 339 PythonQtSlotInfo* info = new PythonQtSlotInfo(*infoOrig);
340 340 info->setUpcastingOffset(upcastingOffset);
341 341 found = true;
342 342 if (tail) {
343 343 tail->setNextInfo(info);
344 344 } else {
345 345 PythonQtMemberInfo newInfo(info);
346 346 memberCache.insert(memberName, newInfo);
347 347 }
348 348 tail = info;
349 349 }
350 350 }
351 351 return tail;
352 352 }
353 353
354 354 void PythonQtClassInfo::listDecoratorSlotsFromDecoratorProvider(QStringList& list, bool metaOnly) {
355 355 QObject* decoratorProvider = decorator();
356 356 if (decoratorProvider) {
357 357 const QMetaObject* meta = decoratorProvider->metaObject();
358 358 int numMethods = meta->methodCount();
359 359 int startFrom = QObject::staticMetaObject.methodCount();
360 360 for (int i = startFrom; i < numMethods; i++) {
361 361 QMetaMethod m = meta->method(i);
362 362 if ((m.methodType() == QMetaMethod::Method ||
363 363 m.methodType() == QMetaMethod::Slot) && m.access() == QMetaMethod::Public) {
364 364
365 365 const char* sigStart = m.signature();
366 366 bool isClassDeco = false;
367 367 if (qstrncmp(sigStart, "static_", 7)==0) {
368 368 // skip the static_classname_ part of the string
369 369 sigStart += 7 + 1 + strlen(className());
370 370 isClassDeco = true;
371 371 } else if (qstrncmp(sigStart, "new_", 4)==0) {
372 372 continue;
373 373 } else if (qstrncmp(sigStart, "delete_", 7)==0) {
374 374 continue;
375 375 }
376 376 // find the first '('
377 377 int offset = findCharOffset(sigStart, '(');
378 378
379 379 // XXX no checking is currently done if the slots have correct first argument or not...
380 380 if (!metaOnly || isClassDeco) {
381 381 list << QString::fromLatin1(sigStart, offset);
382 382 }
383 383 }
384 384 }
385 385 }
386 386
387 387 // look for global decorator slots
388 388 QListIterator<PythonQtSlotInfo*> it(_decoratorSlots);
389 389 while (it.hasNext()) {
390 390 PythonQtSlotInfo* slot = it.next();
391 391 if (metaOnly) {
392 392 if (slot->isClassDecorator()) {
393 393 QByteArray first = slot->slotName();
394 394 if (first.startsWith("static_")) {
395 395 int idx = first.indexOf('_');
396 396 idx = first.indexOf('_', idx+1);
397 397 first = first.mid(idx+1);
398 398 }
399 399 list << first;
400 400 }
401 401 } else {
402 402 list << slot->slotName();
403 403 }
404 404 }
405 405 }
406 406
407 407 QStringList PythonQtClassInfo::memberList(bool metaOnly)
408 408 {
409 409 decorator();
410 410
411 411 QStringList l;
412 412 QString h;
413 413 if (_isQObject && _meta && !metaOnly) {
414 414 int i;
415 415 int numProperties = _meta->propertyCount();
416 416 for (i = 0; i < numProperties; i++) {
417 417 QMetaProperty p = _meta->property(i);
418 418 l << QString(p.name());
419 419 }
420 420 }
421 421
422 422 // normal slots of QObject (or wrapper QObject)
423 423 if (!metaOnly && _meta) {
424 424 int numMethods = _meta->methodCount();
425 425 bool skipQObj = !_isQObject;
426 426 for (int i = skipQObj?QObject::staticMetaObject.methodCount():0; i < numMethods; i++) {
427 427 QMetaMethod m = _meta->method(i);
428 428 if ((m.methodType() == QMetaMethod::Method ||
429 429 m.methodType() == QMetaMethod::Slot) && m.access() == QMetaMethod::Public) {
430 430 QByteArray signa(m.signature());
431 431 if (signa.startsWith("new_")) continue;
432 432 if (signa.startsWith("delete_")) continue;
433 433 if (signa.startsWith("static_")) continue;
434 434 PythonQtSlotInfo slot(m, i);
435 435 l << slot.slotName();
436 436 }
437 437 }
438 438 }
439 439
440 440 {
441 441 // look for dynamic decorators in this class and in derived classes
442 442 QList<PythonQtClassInfo*> infos;
443 443 recursiveCollectClassInfos(infos);
444 444 foreach(PythonQtClassInfo* info, infos) {
445 445 info->listDecoratorSlotsFromDecoratorProvider(l, metaOnly);
446 446 }
447 447 }
448 448
449 449 // List enumerator keys...
450 450 QList<const QMetaObject*> enumMetaObjects;
451 451 if (_meta) {
452 452 enumMetaObjects << _meta;
453 453 }
454 454 // check enums in the class hierachy of CPP classes
455 455 QList<QObject*> decoObjects;
456 456 recursiveCollectDecoratorObjects(decoObjects);
457 457 foreach(QObject* deco, decoObjects) {
458 458 enumMetaObjects << deco->metaObject();
459 459 }
460 460
461 461 foreach(const QMetaObject* meta, enumMetaObjects) {
462 462 for (int i = 0; i<meta->enumeratorCount(); i++) {
463 463 QMetaEnum e = meta->enumerator(i);
464 464 for (int j=0; j < e.keyCount(); j++) {
465 465 l << QString(e.key(j));
466 466 }
467 467 }
468 468 }
469 return l;
469
470 return QSet<QString>::fromList(l).toList();
470 471 }
471 472
472 473 const char* PythonQtClassInfo::className()
473 474 {
474 475 return _wrappedClassName.constData();
475 476 }
476 477
477 478 void* PythonQtClassInfo::castTo(void* ptr, const char* classname)
478 479 {
479 480 if (ptr==NULL) {
480 481 return NULL;
481 482 }
482 483 if (_wrappedClassName == classname) {
483 484 return ptr;
484 485 }
485 486 foreach(const ParentClassInfo& info, _parentClasses) {
486 487 void* result = info._parent->castTo((char*)ptr + info._upcastingOffset, classname);
487 488 if (result) {
488 489 return result;
489 490 }
490 491 }
491 492 return NULL;
492 493 }
493 494
494 495 bool PythonQtClassInfo::inherits(const char* name)
495 496 {
496 497 if (_wrappedClassName == name) {
497 498 return true;
498 499 }
499 500 foreach(const ParentClassInfo& info, _parentClasses) {
500 501 if (info._parent->inherits(name)) {
501 502 return true;
502 503 }
503 504 }
504 505 return false;
505 506 }
506 507
507 508 bool PythonQtClassInfo::inherits(PythonQtClassInfo* classInfo)
508 509 {
509 510 if (classInfo == this) {
510 511 return true;
511 512 }
512 513 foreach(const ParentClassInfo& info, _parentClasses) {
513 514 if (info._parent->inherits(classInfo)) {
514 515 return true;
515 516 }
516 517 }
517 518 return false;
518 519 }
519 520
520 521 QString PythonQtClassInfo::help()
521 522 {
522 523 decorator();
523 524 QString h;
524 525 h += QString("--- ") + QString(className()) + QString(" ---\n");
525 526
526 527 if (_isQObject) {
527 528 h += "Properties:\n";
528 529
529 530 int i;
530 531 int numProperties = _meta->propertyCount();
531 532 for (i = 0; i < numProperties; i++) {
532 533 QMetaProperty p = _meta->property(i);
533 534 h += QString(p.name()) + " (" + QString(p.typeName()) + " )\n";
534 535 }
535 536 }
536 537
537 538 if (constructors()) {
538 539 h += "Constructors:\n";
539 540 PythonQtSlotInfo* constr = constructors();
540 541 while (constr) {
541 542 h += constr->fullSignature() + "\n";
542 543 constr = constr->nextInfo();
543 544 }
544 545 }
545 546
546 547 h += "Slots:\n";
547 548 h += "QString help()\n";
548 549 h += "QString className()\n";
549 550
550 551 if (_meta) {
551 552 int numMethods = _meta->methodCount();
552 553 for (int i = 0; i < numMethods; i++) {
553 554 QMetaMethod m = _meta->method(i);
554 555 if ((m.methodType() == QMetaMethod::Method ||
555 556 m.methodType() == QMetaMethod::Slot) && m.access() == QMetaMethod::Public) {
556 557 QByteArray signa(m.signature());
557 558 if (signa.startsWith("new_")) continue;
558 559 if (signa.startsWith("delete_")) continue;
559 560 if (signa.startsWith("static_")) continue;
560 561 PythonQtSlotInfo slot(m, i);
561 562 h += slot.fullSignature()+ "\n";
562 563 }
563 564 }
564 565 }
565 566
566 567 // TODO xxx : decorators and enums from decorator() are missing...
567 568 // maybe we can reuse memberlist()?
568 569
569 570 if (_meta && _meta->enumeratorCount()) {
570 571 h += "Enums:\n";
571 572 for (int i = 0; i<_meta->enumeratorCount(); i++) {
572 573 QMetaEnum e = _meta->enumerator(i);
573 574 h += QString(e.name()) + " {";
574 575 for (int j=0; j < e.keyCount(); j++) {
575 576 if (j) { h+= ", "; }
576 577 h += e.key(j);
577 578 }
578 579 h += " }\n";
579 580 }
580 581 }
581 582
582 583 if (_isQObject && _meta) {
583 584 int numMethods = _meta->methodCount();
584 585 if (numMethods>0) {
585 586 h += "Signals:\n";
586 587 for (int i = 0; i < numMethods; i++) {
587 588 QMetaMethod m = _meta->method(i);
588 589 if (m.methodType() == QMetaMethod::Signal) {
589 590 h += QString(m.signature()) + "\n";
590 591 }
591 592 }
592 593 }
593 594 }
594 595 return h;
595 596 }
596 597
597 598 PythonQtSlotInfo* PythonQtClassInfo::constructors()
598 599 {
599 600 if (!_constructors) {
600 601 // force creation of lazy decorator, which will register the decorators
601 602 decorator();
602 603 }
603 604 return _constructors;
604 605 }
605 606
606 607 PythonQtSlotInfo* PythonQtClassInfo::destructor()
607 608 {
608 609 if (!_destructor) {
609 610 // force creation of lazy decorator, which will register the decorators
610 611 decorator();
611 612 }
612 613 return _destructor;
613 614 }
614 615
615 616 void PythonQtClassInfo::addConstructor(PythonQtSlotInfo* info)
616 617 {
617 618 PythonQtSlotInfo* prev = constructors();
618 619 if (prev) {
619 620 info->setNextInfo(prev->nextInfo());
620 621 prev->setNextInfo(info);
621 622 } else {
622 623 _constructors = info;
623 624 }
624 625 }
625 626
626 627 void PythonQtClassInfo::addDecoratorSlot(PythonQtSlotInfo* info)
627 628 {
628 629 _decoratorSlots.append(info);
629 630 }
630 631
631 632 void PythonQtClassInfo::setDestructor(PythonQtSlotInfo* info)
632 633 {
633 634 if (_destructor) {
634 635 _destructor->deleteOverloadsAndThis();
635 636 }
636 637 _destructor = info;
637 638 }
638 639
639 640 void PythonQtClassInfo::setMetaObject(const QMetaObject* meta)
640 641 {
641 642 _meta = meta;
642 643 clearCachedMembers();
643 644 }
644 645
645 646 QObject* PythonQtClassInfo::decorator()
646 647 {
647 648 if (!_decoratorProvider && _decoratorProviderCB) {
648 649 _decoratorProvider = (*_decoratorProviderCB)();
649 650 if (_decoratorProvider) {
650 651 _decoratorProvider->setParent(PythonQt::priv());
651 652 PythonQt::priv()->addDecorators(_decoratorProvider, PythonQtPrivate::ConstructorDecorator | PythonQtPrivate::DestructorDecorator);
652 653 }
653 654 }
654 655 return _decoratorProvider;
655 656 }
656 657
657 658 bool PythonQtClassInfo::hasOwnerMethodButNoOwner(void* object)
658 659 {
659 660 PythonQtMemberInfo info = member("hasOwner");
660 661 if (info._type == PythonQtMemberInfo::Slot) {
661 662 void* obj = object;
662 663 bool result = false;
663 664 void* args[2];
664 665 args[0] = &result;
665 666 args[1] = &obj;
666 667 info._slot->decorator()->qt_metacall(QMetaObject::InvokeMetaMethod, info._slot->slotIndex(), args);
667 668 return !result;
668 669 } else {
669 670 return false;
670 671 }
671 672 }
673
674 void* PythonQtClassInfo::recursiveCastDownIfPossible(void* ptr, char** resultClassName)
675 {
676 if (!_polymorphicHandlers.isEmpty()) {
677 foreach(PythonQtPolymorphicHandlerCB* cb, _polymorphicHandlers) {
678 void* resultPtr = (*cb)(ptr, resultClassName);
679 if (resultPtr) {
680 return resultPtr;
681 }
682 }
683 }
684 foreach(const ParentClassInfo& info, _parentClasses) {
685 if (!info._parent->isQObject()) {
686 void* resultPtr = info._parent->recursiveCastDownIfPossible((char*)ptr + info._upcastingOffset, resultClassName);
687 if (resultPtr) {
688 return resultPtr;
689 }
690 }
691 }
692 return NULL;
693 }
694
695 void* PythonQtClassInfo::castDownIfPossible(void* ptr, PythonQtClassInfo** resultClassInfo)
696 {
697 char* className;
698 void* resultPtr = recursiveCastDownIfPossible(ptr, &className);
699 if (resultPtr) {
700 *resultClassInfo = PythonQt::priv()->getClassInfo(className);
701 } else {
702 *resultClassInfo = this;
703 resultPtr = ptr;
704 }
705 return resultPtr;
706 }
@@ -1,230 +1,240
1 1 #ifndef _PYTHONQTCLASSINFO_H
2 2 #define _PYTHONQTCLASSINFO_H
3 3
4 4 /*
5 5 *
6 6 * Copyright (C) 2006 MeVis Research GmbH All Rights Reserved.
7 7 *
8 8 * This library is free software; you can redistribute it and/or
9 9 * modify it under the terms of the GNU Lesser General Public
10 10 * License as published by the Free Software Foundation; either
11 11 * version 2.1 of the License, or (at your option) any later version.
12 12 *
13 13 * This library is distributed in the hope that it will be useful,
14 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 16 * Lesser General Public License for more details.
17 17 *
18 18 * Further, this software is distributed without any warranty that it is
19 19 * free of the rightful claim of any third person regarding infringement
20 20 * or the like. Any license provided herein, whether implied or
21 21 * otherwise, applies only to this software file. Patent licenses, if
22 22 * any, provided herein do not apply to combinations of this program with
23 23 * other software, or any other product whatsoever.
24 24 *
25 25 * You should have received a copy of the GNU Lesser General Public
26 26 * License along with this library; if not, write to the Free Software
27 27 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 28 *
29 29 * Contact information: MeVis Research GmbH, Universitaetsallee 29,
30 30 * 28359 Bremen, Germany or:
31 31 *
32 32 * http://www.mevis.de
33 33 *
34 34 */
35 35
36 36 #include <QMetaObject>
37 37 #include <QMetaMethod>
38 38 #include <QHash>
39 39 #include <QByteArray>
40 40 #include <QList>
41 41 #include "PythonQt.h"
42 42
43 43 class PythonQtSlotInfo;
44 44
45 45 struct PythonQtMemberInfo {
46 46 enum Type {
47 47 Invalid, Slot, EnumValue, Property, NotFound
48 48 };
49 49
50 50 PythonQtMemberInfo():_slot(NULL),_enumValue(0),_type(Invalid) { }
51 51
52 52 PythonQtMemberInfo(PythonQtSlotInfo* info) {
53 53 _type = Slot;
54 54 _slot = info;
55 55 _enumValue = 0;
56 56 }
57 57
58 58 PythonQtMemberInfo(unsigned int enumValue) {
59 59 _type = EnumValue;
60 60 _slot = NULL;
61 61 _enumValue = enumValue;
62 62 }
63 63
64 64 PythonQtMemberInfo(const QMetaProperty& prop) {
65 65 _type = Property;
66 66 _slot = NULL;
67 67 _enumValue = 0;
68 68 _property = prop;
69 69 }
70 70
71 71 Type _type;
72 72
73 73 // TODO: this could be a union...
74 74 PythonQtSlotInfo* _slot;
75 75 unsigned int _enumValue;
76 76 QMetaProperty _property;
77 77 };
78 78
79 79 //! a class that stores all required information about a Qt object (and an optional associated C++ class name)
80 80 /*! for fast lookup of slots when calling the object from Python
81 81 */
82 82 class PythonQtClassInfo {
83 83
84 84 public:
85 85 PythonQtClassInfo();
86 86 ~PythonQtClassInfo();
87 87
88 88 //! store information about parent classes
89 89 struct ParentClassInfo {
90 90 ParentClassInfo(PythonQtClassInfo* parent, int upcastingOffset=0):_parent(parent),_upcastingOffset(upcastingOffset)
91 91 {};
92 92
93 93 PythonQtClassInfo* _parent;
94 94 int _upcastingOffset;
95 95 };
96 96
97 97
98 98 //! setup as a QObject, taking the meta object as meta information about the QObject
99 99 void setupQObject(const QMetaObject* meta);
100 100
101 101 //! setup as a CPP (non-QObject), taking the classname
102 102 void setupCPPObject(const QByteArray& classname);
103 103
104 104 //! get the Python method definition for a given slot name (without return type and signature)
105 105 PythonQtMemberInfo member(const char* member);
106 106
107 107 //! get access to the constructor slot (which may be overloaded if there are multiple constructors)
108 108 PythonQtSlotInfo* constructors();
109 109
110 110 //! get access to the destructor slot
111 111 PythonQtSlotInfo* destructor();
112 112
113 113 //! add a constructor, ownership is passed to classinfo
114 114 void addConstructor(PythonQtSlotInfo* info);
115 115
116 116 //! set a destructor, ownership is passed to classinfo
117 117 void setDestructor(PythonQtSlotInfo* info);
118 118
119 119 //! add a decorator slot, ownership is passed to classinfo
120 120 void addDecoratorSlot(PythonQtSlotInfo* info);
121 121
122 122 //! get the classname (either of the QObject or of the wrapped CPP object)
123 123 const char* className();
124 124
125 125 //! returns if the QObject
126 126 bool isQObject() { return _isQObject; }
127 127
128 128 //! returns if the class is a CPP wrapper
129 129 bool isCPPWrapper() { return !_isQObject; }
130 130
131 131 //! get the meta object
132 132 const QMetaObject* metaObject() { return _meta; }
133 133
134 134 //! set the meta object, this will reset the caching
135 135 void setMetaObject(const QMetaObject* meta);
136 136
137 137 //! returns if this class inherits from the given classname
138 138 bool inherits(const char* classname);
139 139
140 140 //! returns if this class inherits from the given classinfo
141 141 bool inherits(PythonQtClassInfo* info);
142 142
143 143 //! casts the given \c ptr to an object of type \c classname, returns the new pointer
144 144 //! which might be different to \c ptr due to C++ multiple inheritance
145 145 //! (if the cast is not possible or if ptr is NULL, NULL is returned)
146 146 void* castTo(void* ptr, const char* classname);
147 147
148 148 //! get help string for the metaobject
149 149 QString help();
150 150
151 151 //! get list of all members
152 152 QStringList memberList(bool metaOnly = false);
153 153
154 154 //! get the meta type id of this class (only valid for isCPPWrapper() == true)
155 155 int metaTypeId() { return _metaTypeId; }
156 156
157 157 //! set an additional decorator provider that offers additional decorator slots for this class
158 158 void setDecoratorProvider(PythonQtQObjectCreatorFunctionCB* cb) { _decoratorProviderCB = cb; _decoratorProvider = NULL; }
159 159
160 160 //! get the decorator qobject instance
161 161 QObject* decorator();
162 162
163 163 //! add the parent class info of a CPP object
164 164 void addParentClass(const ParentClassInfo& info) { _parentClasses.append(info); }
165 165
166 166 //! check if the special method "hasOwner" is implemented and if it returns false, which means that the object may be destroyed
167 167 bool hasOwnerMethodButNoOwner(void* object);
168 168
169 169 //! set the associated PythonQtClassWrapper (which handles instance creation of this type)
170 170 void setPythonQtClassWrapper(PyObject* obj) { _pythonQtClassWrapper = obj; }
171 171
172 172 //! get the associated PythonQtClassWrapper (which handles instance creation of this type)
173 173 PyObject* pythonQtClassWrapper() { return _pythonQtClassWrapper; }
174 174
175 175 //! set the shell set instance wrapper cb
176 176 void setShellSetInstanceWrapperCB(PythonQtShellSetInstanceWrapperCB* cb) {
177 177 _shellSetInstanceWrapperCB = cb;
178 178 }
179 179
180 180 //! get the shell set instance wrapper cb
181 181 PythonQtShellSetInstanceWrapperCB* shellSetInstanceWrapperCB() {
182 182 return _shellSetInstanceWrapperCB;
183 183 }
184 184
185 //! add a handler for polymorphic downcasting
186 void addPolymorphicHandler(PythonQtPolymorphicHandlerCB* cb) { _polymorphicHandlers.append(cb); }
187
188 //! cast the pointer down in the class hierarchy if a polymorphic handler allows to do that
189 void* castDownIfPossible(void* ptr, PythonQtClassInfo** resultClassInfo);
190
185 191 private:
186 192 //! clear all cached members
187 193 void clearCachedMembers();
188 194
195 void* recursiveCastDownIfPossible(void* ptr, char** resultClassName);
196
189 197 PythonQtSlotInfo* findDecoratorSlotsFromDecoratorProvider(const char* memberName, PythonQtSlotInfo* inputInfo, bool &found, QHash<QByteArray, PythonQtMemberInfo>& memberCache, int upcastingOffset);
190 198 void listDecoratorSlotsFromDecoratorProvider(QStringList& list, bool metaOnly);
191 199 PythonQtSlotInfo* recursiveFindDecoratorSlotsFromDecoratorProvider(const char* memberName, PythonQtSlotInfo* inputInfo, bool &found, QHash<QByteArray, PythonQtMemberInfo>& memberCache, int upcastingOffset);
192 200
193 201 void recursiveCollectClassInfos(QList<PythonQtClassInfo*>& classInfoObjects);
194 202 void recursiveCollectDecoratorObjects(QList<QObject*>& decoratorObjects);
195 203
196 204 bool lookForPropertyAndCache(const char* memberName);
197 205 bool lookForMethodAndCache(const char* memberName);
198 206 bool lookForEnumAndCache(const QMetaObject* m, const char* memberName);
199 207
200 208 PythonQtSlotInfo* findDecoratorSlots(const char* memberName, int memberNameLen, PythonQtSlotInfo* tail, bool &found, QHash<QByteArray, PythonQtMemberInfo>& memberCache, int upcastingOffset);
201 209 int findCharOffset(const char* sigStart, char someChar);
202 210
203 211 QHash<QByteArray, PythonQtMemberInfo> _cachedMembers;
204 212
205 213 PythonQtSlotInfo* _constructors;
206 214 PythonQtSlotInfo* _destructor;
207 215 QList<PythonQtSlotInfo*> _decoratorSlots;
208 216
209 217 const QMetaObject* _meta;
210 218
211 219 QByteArray _wrappedClassName;
212 220 QList<ParentClassInfo> _parentClasses;
213 221
222 QList<PythonQtPolymorphicHandlerCB*> _polymorphicHandlers;
223
214 224 QObject* _decoratorProvider;
215 225 PythonQtQObjectCreatorFunctionCB* _decoratorProviderCB;
216 226
217 227 PyObject* _pythonQtClassWrapper;
218 228
219 229 PythonQtShellSetInstanceWrapperCB* _shellSetInstanceWrapperCB;
220 230
221 231 int _metaTypeId;
222 232
223 233 bool _isQObject;
224 234
225 235 };
226 236
227 237 //---------------------------------------------------------------
228 238
229 239
230 240 #endif
General Comments 0
You need to be logged in to leave comments. Login now