##// END OF EJS Templates
SIGNAL/SLOT methods improved and supported in connect/disconnect...
florianlink -
r62:b7a51b352089
parent child
Show More
@@ -1,1227 +1,1227
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 PyObject* PythonQtPrivate::wrapQObject(QObject* obj)
280 280 {
281 281 if (!obj) {
282 282 Py_INCREF(Py_None);
283 283 return Py_None;
284 284 }
285 285 PythonQtInstanceWrapper* wrap = findWrapperAndRemoveUnused(obj);
286 286 if (!wrap) {
287 287 // smuggling it in...
288 288 PythonQtClassInfo* classInfo = _knownClassInfos.value(obj->metaObject()->className());
289 289 if (!classInfo || classInfo->pythonQtClassWrapper()==NULL) {
290 290 registerClass(obj->metaObject());
291 291 classInfo = _knownClassInfos.value(obj->metaObject()->className());
292 292 }
293 293 wrap = createNewPythonQtInstanceWrapper(obj, classInfo);
294 294 // mlabDebugConst("MLABPython","new qobject wrapper added " << " " << wrap->_obj->className() << " " << wrap->classInfo()->wrappedClassName().latin1());
295 295 } else {
296 296 Py_INCREF(wrap);
297 297 // mlabDebugConst("MLABPython","qobject wrapper reused " << wrap->_obj->className() << " " << wrap->classInfo()->wrappedClassName().latin1());
298 298 }
299 299 return (PyObject*)wrap;
300 300 }
301 301
302 302 PyObject* PythonQtPrivate::wrapPtr(void* ptr, const QByteArray& name)
303 303 {
304 304 if (!ptr) {
305 305 Py_INCREF(Py_None);
306 306 return Py_None;
307 307 }
308 308
309 309 PythonQtInstanceWrapper* wrap = findWrapperAndRemoveUnused(ptr);
310 310 if (!wrap) {
311 311 PythonQtClassInfo* info = _knownClassInfos.value(name);
312 312 if (!info) {
313 313 // maybe it is a PyObject, which we can return directly
314 314 if (name == "PyObject") {
315 315 PyObject* p = (PyObject*)ptr;
316 316 Py_INCREF(p);
317 317 return p;
318 318 }
319 319
320 320 // we do not know the metaobject yet, but we might know it by it's name:
321 321 if (_knownQObjectClassNames.find(name)!=_knownQObjectClassNames.end()) {
322 322 // yes, we know it, so we can convert to QObject
323 323 QObject* qptr = (QObject*)ptr;
324 324 registerClass(qptr->metaObject());
325 325 info = _knownClassInfos.value(qptr->metaObject()->className());
326 326 }
327 327 }
328 328 if (info && info->isQObject()) {
329 329 QObject* qptr = (QObject*)ptr;
330 330 // if the object is a derived object, we want to switch the class info to the one of the derived class:
331 331 if (name!=(qptr->metaObject()->className())) {
332 332 registerClass(qptr->metaObject());
333 333 info = _knownClassInfos.value(qptr->metaObject()->className());
334 334 }
335 335 wrap = createNewPythonQtInstanceWrapper(qptr, info);
336 336 // mlabDebugConst("MLABPython","new qobject wrapper added " << " " << wrap->_obj->className() << " " << wrap->classInfo()->wrappedClassName().latin1());
337 337 return (PyObject*)wrap;
338 338 }
339 339
340 340 // not a known QObject, so try our wrapper factory:
341 341 QObject* wrapper = NULL;
342 342 for (int i=0; i<_cppWrapperFactories.size(); i++) {
343 343 wrapper = _cppWrapperFactories.at(i)->create(name, ptr);
344 344 if (wrapper) {
345 345 break;
346 346 }
347 347 }
348 348
349 349 if (info) {
350 350 // try to downcast in the class hierarchy, which will modify info and ptr if it is successfull
351 351 ptr = info->castDownIfPossible(ptr, &info);
352 352 }
353 353
354 354 if (!info || info->pythonQtClassWrapper()==NULL) {
355 355 // still unknown, register as CPP class
356 356 registerCPPClass(name.constData());
357 357 info = _knownClassInfos.value(name);
358 358 }
359 359 if (wrapper && (info->metaObject() != wrapper->metaObject())) {
360 360 // if we a have a QObject wrapper and the metaobjects do not match, set the metaobject again!
361 361 info->setMetaObject(wrapper->metaObject());
362 362 }
363 363 wrap = createNewPythonQtInstanceWrapper(wrapper, info, ptr);
364 364 // mlabDebugConst("MLABPython","new c++ wrapper added " << wrap->_wrappedPtr << " " << wrap->_obj->className() << " " << wrap->classInfo()->wrappedClassName().latin1());
365 365 } else {
366 366 Py_INCREF(wrap);
367 367 //mlabDebugConst("MLABPython","c++ wrapper reused " << wrap->_wrappedPtr << " " << wrap->_obj->className() << " " << wrap->classInfo()->wrappedClassName().latin1());
368 368 }
369 369 return (PyObject*)wrap;
370 370 }
371 371
372 372 PyObject* PythonQtPrivate::dummyTuple() {
373 373 static PyObject* dummyTuple = NULL;
374 374 if (dummyTuple==NULL) {
375 375 dummyTuple = PyTuple_New(1);
376 376 PyTuple_SET_ITEM(dummyTuple, 0 , PyString_FromString("dummy"));
377 377 }
378 378 return dummyTuple;
379 379 }
380 380
381 381
382 382 PythonQtInstanceWrapper* PythonQtPrivate::createNewPythonQtInstanceWrapper(QObject* obj, PythonQtClassInfo* info, void* wrappedPtr) {
383 383 // call the associated class type to create a new instance...
384 384 PythonQtInstanceWrapper* result = (PythonQtInstanceWrapper*)PyObject_Call(info->pythonQtClassWrapper(), dummyTuple(), NULL);
385 385
386 386 result->setQObject(obj);
387 387 result->_wrappedPtr = wrappedPtr;
388 388 result->_ownedByPythonQt = false;
389 389 result->_useQMetaTypeDestroy = false;
390 390
391 391 if (wrappedPtr) {
392 392 _wrappedObjects.insert(wrappedPtr, result);
393 393 } else {
394 394 _wrappedObjects.insert(obj, result);
395 395 if (obj->parent()== NULL && _wrappedCB) {
396 396 // tell someone who is interested that the qobject is wrapped the first time, if it has no parent
397 397 (*_wrappedCB)(obj);
398 398 }
399 399 }
400 400 return result;
401 401 }
402 402
403 403 PythonQtClassWrapper* PythonQtPrivate::createNewPythonQtClassWrapper(PythonQtClassInfo* info, const char* package) {
404 404 PythonQtClassWrapper* result;
405 405
406 406 PyObject* className = PyString_FromString(info->className());
407 407
408 408 PyObject* baseClasses = PyTuple_New(1);
409 409 PyTuple_SET_ITEM(baseClasses, 0, (PyObject*)&PythonQtInstanceWrapper_Type);
410 410
411 411 PyObject* typeDict = PyDict_New();
412 412 QByteArray moduleName("PythonQt");
413 413 if (package && strcmp(package, "")!=0) {
414 414 moduleName += ".";
415 415 moduleName += package;
416 416 }
417 417 PyDict_SetItemString(typeDict, "__module__", PyString_FromString(moduleName.constData()));
418 418
419 419 PyObject* args = Py_BuildValue("OOO", className, baseClasses, typeDict);
420 420
421 421 // set the class info so that PythonQtClassWrapper_new can read it
422 422 _currentClassInfoForClassWrapperCreation = info;
423 423 // create the new type object by calling the type
424 424 result = (PythonQtClassWrapper *)PyObject_Call((PyObject *)&PythonQtClassWrapper_Type, args, NULL);
425 425
426 426 Py_DECREF(baseClasses);
427 427 Py_DECREF(typeDict);
428 428 Py_DECREF(args);
429 429 Py_DECREF(className);
430 430
431 431 return result;
432 432 }
433 433
434 434 PyObject* PythonQtPrivate::createEnumValueInstance(PyObject* enumType, unsigned int enumValue)
435 435 {
436 436 PyObject* args = Py_BuildValue("(i)", enumValue);
437 437 PyObject* result = PyObject_Call(enumType, args, NULL);
438 438 Py_DECREF(args);
439 439 return result;
440 440 }
441 441
442 442 PyObject* PythonQtPrivate::createNewPythonQtEnumWrapper(const char* enumName, PyObject* parentObject) {
443 443 PyObject* result;
444 444
445 445 PyObject* className = PyString_FromString(enumName);
446 446
447 447 PyObject* baseClasses = PyTuple_New(1);
448 448 PyTuple_SET_ITEM(baseClasses, 0, (PyObject*)&PyInt_Type);
449 449
450 450 PyObject* module = PyObject_GetAttrString(parentObject, "__module__");
451 451 PyObject* typeDict = PyDict_New();
452 452 PyDict_SetItemString(typeDict, "__module__", module);
453 453
454 454 PyObject* args = Py_BuildValue("OOO", className, baseClasses, typeDict);
455 455
456 456 // create the new int derived type object by calling the core type
457 457 result = PyObject_Call((PyObject *)&PyType_Type, args, NULL);
458 458
459 459 Py_DECREF(baseClasses);
460 460 Py_DECREF(typeDict);
461 461 Py_DECREF(args);
462 462 Py_DECREF(className);
463 463
464 464 return result;
465 465 }
466 466
467 467 PythonQtSignalReceiver* PythonQt::getSignalReceiver(QObject* obj)
468 468 {
469 469 PythonQtSignalReceiver* r = _p->_signalReceivers[obj];
470 470 if (!r) {
471 471 r = new PythonQtSignalReceiver(obj);
472 472 _p->_signalReceivers.insert(obj, r);
473 473 }
474 474 return r;
475 475 }
476 476
477 477 bool PythonQt::addSignalHandler(QObject* obj, const char* signal, PyObject* module, const QString& objectname)
478 478 {
479 479 bool flag = false;
480 480 PythonQtObjectPtr callable = lookupCallable(module, objectname);
481 481 if (callable) {
482 482 PythonQtSignalReceiver* r = getSignalReceiver(obj);
483 483 flag = r->addSignalHandler(signal, callable);
484 484 if (!flag) {
485 485 // signal not found
486 486 }
487 487 } else {
488 488 // callable not found
489 489 }
490 490 return flag;
491 491 }
492 492
493 493 bool PythonQt::addSignalHandler(QObject* obj, const char* signal, PyObject* receiver)
494 494 {
495 495 bool flag = false;
496 496 PythonQtSignalReceiver* r = getSignalReceiver(obj);
497 497 if (r) {
498 498 flag = r->addSignalHandler(signal, receiver);
499 499 }
500 500 return flag;
501 501 }
502 502
503 503 bool PythonQt::removeSignalHandler(QObject* obj, const char* signal, PyObject* module, const QString& objectname)
504 504 {
505 505 bool flag = false;
506 506 PythonQtObjectPtr callable = lookupCallable(module, objectname);
507 507 if (callable) {
508 508 PythonQtSignalReceiver* r = _p->_signalReceivers[obj];
509 509 if (r) {
510 510 flag = r->removeSignalHandler(signal, callable);
511 511 }
512 512 } else {
513 513 // callable not found
514 514 }
515 515 return flag;
516 516 }
517 517
518 518 bool PythonQt::removeSignalHandler(QObject* obj, const char* signal, PyObject* receiver)
519 519 {
520 520 bool flag = false;
521 521 PythonQtSignalReceiver* r = _p->_signalReceivers[obj];
522 522 if (r) {
523 523 flag = r->removeSignalHandler(signal, receiver);
524 524 }
525 525 return flag;
526 526 }
527 527
528 528 PythonQtObjectPtr PythonQt::lookupCallable(PyObject* module, const QString& name)
529 529 {
530 530 PythonQtObjectPtr p = lookupObject(module, name);
531 531 if (p) {
532 532 if (PyCallable_Check(p)) {
533 533 return p;
534 534 }
535 535 }
536 536 PyErr_Clear();
537 537 return NULL;
538 538 }
539 539
540 540 PythonQtObjectPtr PythonQt::lookupObject(PyObject* module, const QString& name)
541 541 {
542 542 QStringList l = name.split('.');
543 543 PythonQtObjectPtr p = module;
544 544 PythonQtObjectPtr prev;
545 545 QString s;
546 546 QByteArray b;
547 547 for (QStringList::ConstIterator i = l.begin(); i!=l.end() && p; ++i) {
548 548 prev = p;
549 549 b = (*i).toLatin1();
550 550 if (PyDict_Check(p)) {
551 551 p = PyDict_GetItemString(p, b.data());
552 552 } else {
553 553 p.setNewRef(PyObject_GetAttrString(p, b.data()));
554 554 }
555 555 }
556 556 PyErr_Clear();
557 557 return p;
558 558 }
559 559
560 560 PythonQtObjectPtr PythonQt::getMainModule() {
561 561 //both borrowed
562 562 PythonQtObjectPtr dict = PyImport_GetModuleDict();
563 563 return PyDict_GetItemString(dict, "__main__");
564 564 }
565 565
566 566 QVariant PythonQt::evalCode(PyObject* object, PyObject* pycode) {
567 567 QVariant result;
568 568 if (pycode) {
569 569 PyObject* dict = NULL;
570 570 if (PyModule_Check(object)) {
571 571 dict = PyModule_GetDict(object);
572 572 } else if (PyDict_Check(object)) {
573 573 dict = object;
574 574 }
575 575 PyObject* r = NULL;
576 576 if (dict) {
577 577 r = PyEval_EvalCode((PyCodeObject*)pycode, dict , dict);
578 578 }
579 579 if (r) {
580 580 result = PythonQtConv::PyObjToQVariant(r);
581 581 Py_DECREF(r);
582 582 } else {
583 583 handleError();
584 584 }
585 585 } else {
586 586 handleError();
587 587 }
588 588 return result;
589 589 }
590 590
591 591 QVariant PythonQt::evalScript(PyObject* object, const QString& script, int start)
592 592 {
593 593 QVariant result;
594 594 PythonQtObjectPtr p;
595 595 PyObject* dict = NULL;
596 596 if (PyModule_Check(object)) {
597 597 dict = PyModule_GetDict(object);
598 598 } else if (PyDict_Check(object)) {
599 599 dict = object;
600 600 }
601 601 if (dict) {
602 602 p.setNewRef(PyRun_String(script.toLatin1().data(), start, dict, dict));
603 603 }
604 604 if (p) {
605 605 result = PythonQtConv::PyObjToQVariant(p);
606 606 } else {
607 607 handleError();
608 608 }
609 609 return result;
610 610 }
611 611
612 612 void PythonQt::evalFile(PyObject* module, const QString& filename)
613 613 {
614 614 PythonQtObjectPtr code = parseFile(filename);
615 615 if (code) {
616 616 evalCode(module, code);
617 617 } else {
618 618 handleError();
619 619 }
620 620 }
621 621
622 622 PythonQtObjectPtr PythonQt::parseFile(const QString& filename)
623 623 {
624 624 PythonQtObjectPtr p;
625 625 p.setNewRef(PythonQtImport::getCodeFromPyc(filename));
626 626 if (!p) {
627 627 handleError();
628 628 }
629 629 return p;
630 630 }
631 631
632 632 PythonQtObjectPtr PythonQt::createModuleFromFile(const QString& name, const QString& filename)
633 633 {
634 634 PythonQtObjectPtr code = parseFile(filename);
635 635 PythonQtObjectPtr module = _p->createModule(name, code);
636 636 return module;
637 637 }
638 638
639 639 PythonQtObjectPtr PythonQt::createModuleFromScript(const QString& name, const QString& script)
640 640 {
641 641 PyErr_Clear();
642 642 QString scriptCode = script;
643 643 if (scriptCode.isEmpty()) {
644 644 // we always need at least a linefeed
645 645 scriptCode = "\n";
646 646 }
647 647 PythonQtObjectPtr pycode;
648 648 pycode.setNewRef(Py_CompileString((char*)scriptCode.toLatin1().data(), "", Py_file_input));
649 649 PythonQtObjectPtr module = _p->createModule(name, pycode);
650 650 return module;
651 651 }
652 652
653 653 PythonQtObjectPtr PythonQt::createUniqueModule()
654 654 {
655 655 static QString pyQtStr("PythonQt_module");
656 656 QString moduleName = pyQtStr+QString::number(_uniqueModuleCount++);
657 657 return createModuleFromScript(moduleName);
658 658 }
659 659
660 660 void PythonQt::addObject(PyObject* object, const QString& name, QObject* qObject)
661 661 {
662 662 if (PyModule_Check(object)) {
663 663 PyModule_AddObject(object, name.toLatin1().data(), _p->wrapQObject(qObject));
664 664 } else if (PyDict_Check(object)) {
665 665 PyDict_SetItemString(object, name.toLatin1().data(), _p->wrapQObject(qObject));
666 666 } else {
667 667 PyObject_SetAttrString(object, name.toLatin1().data(), _p->wrapQObject(qObject));
668 668 }
669 669 }
670 670
671 671 void PythonQt::addVariable(PyObject* object, const QString& name, const QVariant& v)
672 672 {
673 673 if (PyModule_Check(object)) {
674 674 PyModule_AddObject(object, name.toLatin1().data(), PythonQtConv::QVariantToPyObject(v));
675 675 } else if (PyDict_Check(object)) {
676 676 PyDict_SetItemString(object, name.toLatin1().data(), PythonQtConv::QVariantToPyObject(v));
677 677 } else {
678 678 PyObject_SetAttrString(object, name.toLatin1().data(), PythonQtConv::QVariantToPyObject(v));
679 679 }
680 680 }
681 681
682 682 void PythonQt::removeVariable(PyObject* object, const QString& name)
683 683 {
684 684 if (PyDict_Check(object)) {
685 685 PyDict_DelItemString(object, name.toLatin1().data());
686 686 } else {
687 687 PyObject_DelAttrString(object, name.toLatin1().data());
688 688 }
689 689 }
690 690
691 691 QVariant PythonQt::getVariable(PyObject* object, const QString& objectname)
692 692 {
693 693 QVariant result;
694 694 PythonQtObjectPtr obj = lookupObject(object, objectname);
695 695 if (obj) {
696 696 result = PythonQtConv::PyObjToQVariant(obj);
697 697 }
698 698 return result;
699 699 }
700 700
701 701 QStringList PythonQt::introspection(PyObject* module, const QString& objectname, PythonQt::ObjectType type)
702 702 {
703 703 QStringList results;
704 704
705 705 PythonQtObjectPtr object;
706 706 if (objectname.isEmpty()) {
707 707 object = module;
708 708 } else {
709 709 object = lookupObject(module, objectname);
710 710 if (!object && type == CallOverloads) {
711 711 PyObject* dict = lookupObject(module, "__builtins__");
712 712 if (dict) {
713 713 object = PyDict_GetItemString(dict, objectname.toLatin1().constData());
714 714 }
715 715 }
716 716 }
717 717
718 718 if (object) {
719 719 if (type == CallOverloads) {
720 720 if (PythonQtSlotFunction_Check(object)) {
721 721 PythonQtSlotFunctionObject* o = (PythonQtSlotFunctionObject*)object.object();
722 722 PythonQtSlotInfo* info = o->m_ml;
723 723
724 724 while (info) {
725 725 results << info->fullSignature();
726 726 info = info->nextInfo();
727 727 }
728 728 } else if (object->ob_type == &PythonQtClassWrapper_Type) {
729 729 PythonQtClassWrapper* o = (PythonQtClassWrapper*)object.object();
730 730 PythonQtSlotInfo* info = o->classInfo()->constructors();
731 731
732 732 while (info) {
733 733 results << info->fullSignature();
734 734 info = info->nextInfo();
735 735 }
736 736 } else {
737 737 //TODO: use pydoc!
738 738 PyObject* doc = PyObject_GetAttrString(object, "__doc__");
739 739 if (doc) {
740 740 results << PyString_AsString(doc);
741 741 Py_DECREF(doc);
742 742 }
743 743 }
744 744 } else {
745 745 PyObject* keys = NULL;
746 746 bool isDict = false;
747 747 if (PyDict_Check(object)) {
748 748 keys = PyDict_Keys(object);
749 749 isDict = true;
750 750 } else {
751 751 keys = PyObject_Dir(object);
752 752 }
753 753 if (keys) {
754 754 int count = PyList_Size(keys);
755 755 PyObject* key;
756 756 PyObject* value;
757 757 QString keystr;
758 758 for (int i = 0;i<count;i++) {
759 759 key = PyList_GetItem(keys,i);
760 760 if (isDict) {
761 761 value = PyDict_GetItem(object, key);
762 762 Py_INCREF(value);
763 763 } else {
764 764 value = PyObject_GetAttr(object, key);
765 765 }
766 766 if (!value) continue;
767 767 keystr = PyString_AsString(key);
768 768 static const QString underscoreStr("__tmp");
769 769 if (!keystr.startsWith(underscoreStr)) {
770 770 switch (type) {
771 771 case Anything:
772 772 results << keystr;
773 773 break;
774 774 case Class:
775 775 if (value->ob_type == &PyClass_Type) {
776 776 results << keystr;
777 777 }
778 778 break;
779 779 case Variable:
780 780 if (value->ob_type != &PyClass_Type
781 781 && value->ob_type != &PyCFunction_Type
782 782 && value->ob_type != &PyFunction_Type
783 783 && value->ob_type != &PyModule_Type
784 784 ) {
785 785 results << keystr;
786 786 }
787 787 break;
788 788 case Function:
789 789 if (value->ob_type == &PyFunction_Type ||
790 790 value->ob_type == &PyMethod_Type
791 791 ) {
792 792 results << keystr;
793 793 }
794 794 break;
795 795 case Module:
796 796 if (value->ob_type == &PyModule_Type) {
797 797 results << keystr;
798 798 }
799 799 break;
800 800 default:
801 801 std::cerr << "PythonQt: introspection: unknown case" << ", in " << __FILE__ << ":" << __LINE__ << std::endl;
802 802 }
803 803 }
804 804 Py_DECREF(value);
805 805 }
806 806 Py_DECREF(keys);
807 807 }
808 808 }
809 809 }
810 810 return results;
811 811 }
812 812
813 813 QVariant PythonQt::call(PyObject* object, const QString& name, const QVariantList& args)
814 814 {
815 815 PythonQtObjectPtr callable = lookupCallable(object, name);
816 816 if (callable) {
817 817 return call(callable, args);
818 818 } else {
819 819 return QVariant();
820 820 }
821 821 }
822 822
823 823 QVariant PythonQt::call(PyObject* callable, const QVariantList& args)
824 824 {
825 825 QVariant r;
826 826 if (callable) {
827 827 PythonQtObjectPtr pargs;
828 828 int count = args.size();
829 829 if (count>0) {
830 830 pargs.setNewRef(PyTuple_New(count));
831 831 }
832 832 bool err = false;
833 833 // transform QVariants to Python
834 834 for (int i = 0; i < count; i++) {
835 835 PyObject* arg = PythonQtConv::QVariantToPyObject(args.at(i));
836 836 if (arg) {
837 837 // steals reference, no unref
838 838 PyTuple_SetItem(pargs, i,arg);
839 839 } else {
840 840 err = true;
841 841 break;
842 842 }
843 843 }
844 844
845 845 if (!err) {
846 846 PyErr_Clear();
847 847 PythonQtObjectPtr result;
848 848 result.setNewRef(PyObject_CallObject(callable, pargs));
849 849 if (result) {
850 850 // ok
851 851 r = PythonQtConv::PyObjToQVariant(result);
852 852 } else {
853 853 PythonQt::self()->handleError();
854 854 }
855 855 }
856 856 }
857 857 return r;
858 858 }
859 859
860 860 void PythonQt::addInstanceDecorators(QObject* o)
861 861 {
862 862 _p->addDecorators(o, PythonQtPrivate::InstanceDecorator);
863 863 }
864 864
865 865 void PythonQt::addClassDecorators(QObject* o)
866 866 {
867 867 _p->addDecorators(o, PythonQtPrivate::StaticDecorator | PythonQtPrivate::ConstructorDecorator | PythonQtPrivate::DestructorDecorator);
868 868 }
869 869
870 870 void PythonQt::addDecorators(QObject* o)
871 871 {
872 872 _p->addDecorators(o, PythonQtPrivate::AllDecorators);
873 873 }
874 874
875 875 void PythonQt::registerQObjectClassNames(const QStringList& names)
876 876 {
877 877 _p->registerQObjectClassNames(names);
878 878 }
879 879
880 880 void PythonQt::setImporter(PythonQtImportFileInterface* importInterface)
881 881 {
882 882 PythonQtImport::init();
883 883 _p->_importInterface = importInterface;
884 884 }
885 885
886 886 void PythonQt::setImporterIgnorePaths(const QStringList& paths)
887 887 {
888 888 _p->_importIgnorePaths = paths;
889 889 }
890 890
891 891 const QStringList& PythonQt::getImporterIgnorePaths()
892 892 {
893 893 return _p->_importIgnorePaths;
894 894 }
895 895
896 896 void PythonQt::addWrapperFactory(PythonQtCppWrapperFactory* factory)
897 897 {
898 898 _p->_cppWrapperFactories.append(factory);
899 899 }
900 900
901 901 //---------------------------------------------------------------------------------------------------
902 902 PythonQtPrivate::PythonQtPrivate()
903 903 {
904 904 _importInterface = NULL;
905 905 _defaultImporter = new PythonQtQFileImporter;
906 906 _noLongerWrappedCB = NULL;
907 907 _wrappedCB = NULL;
908 908 _currentClassInfoForClassWrapperCreation = NULL;
909 909 }
910 910
911 911 PythonQtClassInfo* PythonQtPrivate::currentClassInfoForClassWrapperCreation()
912 912 {
913 913 PythonQtClassInfo* info = _currentClassInfoForClassWrapperCreation;
914 914 _currentClassInfoForClassWrapperCreation = NULL;
915 915 return info;
916 916 }
917 917
918 918 void PythonQtPrivate::addDecorators(QObject* o, int decoTypes)
919 919 {
920 920 o->setParent(this);
921 921 int numMethods = o->metaObject()->methodCount();
922 922 for (int i = 0; i < numMethods; i++) {
923 923 QMetaMethod m = o->metaObject()->method(i);
924 924 if ((m.methodType() == QMetaMethod::Method ||
925 925 m.methodType() == QMetaMethod::Slot) && m.access() == QMetaMethod::Public) {
926 926 if (qstrncmp(m.signature(), "new_", 4)==0) {
927 927 if ((decoTypes & ConstructorDecorator) == 0) continue;
928 928 const PythonQtMethodInfo* info = PythonQtMethodInfo::getCachedMethodInfo(m, NULL);
929 929 if (info->parameters().at(0).isPointer) {
930 930 QByteArray signature = m.signature();
931 931 QByteArray nameOfClass = signature.mid(4, signature.indexOf('(')-4);
932 932 PythonQtClassInfo* classInfo = lookupClassInfoAndCreateIfNotPresent(nameOfClass);
933 933 PythonQtSlotInfo* newSlot = new PythonQtSlotInfo(NULL, m, i, o, PythonQtSlotInfo::ClassDecorator);
934 934 classInfo->addConstructor(newSlot);
935 935 }
936 936 } else if (qstrncmp(m.signature(), "delete_", 7)==0) {
937 937 if ((decoTypes & DestructorDecorator) == 0) continue;
938 938 QByteArray signature = m.signature();
939 939 QByteArray nameOfClass = signature.mid(7, signature.indexOf('(')-7);
940 940 PythonQtClassInfo* classInfo = lookupClassInfoAndCreateIfNotPresent(nameOfClass);
941 941 PythonQtSlotInfo* newSlot = new PythonQtSlotInfo(NULL, m, i, o, PythonQtSlotInfo::ClassDecorator);
942 942 classInfo->setDestructor(newSlot);
943 943 } else if (qstrncmp(m.signature(), "static_", 7)==0) {
944 944 if ((decoTypes & StaticDecorator) == 0) continue;
945 945 QByteArray signature = m.signature();
946 946 QByteArray nameOfClass = signature.mid(signature.indexOf('_')+1);
947 947 nameOfClass = nameOfClass.mid(0, nameOfClass.indexOf('_'));
948 948 PythonQtClassInfo* classInfo = lookupClassInfoAndCreateIfNotPresent(nameOfClass);
949 949 PythonQtSlotInfo* newSlot = new PythonQtSlotInfo(NULL, m, i, o, PythonQtSlotInfo::ClassDecorator);
950 950 classInfo->addDecoratorSlot(newSlot);
951 951 } else {
952 952 if ((decoTypes & InstanceDecorator) == 0) continue;
953 953 const PythonQtMethodInfo* info = PythonQtMethodInfo::getCachedMethodInfo(m, NULL);
954 954 if (info->parameters().count()>1) {
955 955 PythonQtMethodInfo::ParameterInfo p = info->parameters().at(1);
956 956 if (p.isPointer) {
957 957 PythonQtClassInfo* classInfo = lookupClassInfoAndCreateIfNotPresent(p.name);
958 958 PythonQtSlotInfo* newSlot = new PythonQtSlotInfo(NULL, m, i, o, PythonQtSlotInfo::InstanceDecorator);
959 959 classInfo->addDecoratorSlot(newSlot);
960 960 }
961 961 }
962 962 }
963 963 }
964 964 }
965 965 }
966 966
967 967 void PythonQtPrivate::registerQObjectClassNames(const QStringList& names)
968 968 {
969 969 foreach(QString name, names) {
970 970 _knownQObjectClassNames.insert(name.toLatin1(), true);
971 971 }
972 972 }
973 973
974 974 void PythonQtPrivate::removeSignalEmitter(QObject* obj)
975 975 {
976 976 _signalReceivers.remove(obj);
977 977 }
978 978
979 979 bool PythonQt::handleError()
980 980 {
981 981 bool flag = false;
982 982 if (PyErr_Occurred()) {
983 983
984 984 // currently we just print the error and the stderr handler parses the errors
985 985 PyErr_Print();
986 986
987 987 /*
988 988 // EXTRA: the format of the ptype and ptraceback is not really documented, so I use PyErr_Print() above
989 989 PyObject *ptype;
990 990 PyObject *pvalue;
991 991 PyObject *ptraceback;
992 992 PyErr_Fetch( &ptype, &pvalue, &ptraceback);
993 993
994 994 Py_XDECREF(ptype);
995 995 Py_XDECREF(pvalue);
996 996 Py_XDECREF(ptraceback);
997 997 */
998 998 PyErr_Clear();
999 999 flag = true;
1000 1000 }
1001 1001 return flag;
1002 1002 }
1003 1003
1004 1004 void PythonQt::addSysPath(const QString& path)
1005 1005 {
1006 1006 PythonQtObjectPtr sys;
1007 1007 sys.setNewRef(PyImport_ImportModule("sys"));
1008 1008 PythonQtObjectPtr obj = lookupObject(sys, "path");
1009 1009 PyList_Insert(obj, 0, PythonQtConv::QStringToPyObject(path));
1010 1010 }
1011 1011
1012 1012 void PythonQt::overwriteSysPath(const QStringList& paths)
1013 1013 {
1014 1014 PythonQtObjectPtr sys;
1015 1015 sys.setNewRef(PyImport_ImportModule("sys"));
1016 1016 PyModule_AddObject(sys, "path", PythonQtConv::QStringListToPyList(paths));
1017 1017 }
1018 1018
1019 1019 void PythonQt::setModuleImportPath(PyObject* module, const QStringList& paths)
1020 1020 {
1021 1021 PyModule_AddObject(module, "__path__", PythonQtConv::QStringListToPyList(paths));
1022 1022 }
1023 1023
1024 1024 void PythonQt::stdOutRedirectCB(const QString& str)
1025 1025 {
1026 1026 emit PythonQt::self()->pythonStdOut(str);
1027 1027 }
1028 1028
1029 1029 void PythonQt::stdErrRedirectCB(const QString& str)
1030 1030 {
1031 1031 emit PythonQt::self()->pythonStdErr(str);
1032 1032 }
1033 1033
1034 1034 void PythonQt::setQObjectWrappedCallback(PythonQtQObjectWrappedCB* cb)
1035 1035 {
1036 1036 _p->_wrappedCB = cb;
1037 1037 }
1038 1038
1039 1039 void PythonQt::setQObjectNoLongerWrappedCallback(PythonQtQObjectNoLongerWrappedCB* cb)
1040 1040 {
1041 1041 _p->_noLongerWrappedCB = cb;
1042 1042 }
1043 1043
1044 1044
1045 1045
1046 1046 static PyMethodDef PythonQtMethods[] = {
1047 1047 {NULL, NULL, 0, NULL}
1048 1048 };
1049 1049
1050 1050 void PythonQt::initPythonQtModule(bool redirectStdOut)
1051 1051 {
1052 1052 _p->_pythonQtModule = Py_InitModule("PythonQt", PythonQtMethods);
1053 1053
1054 1054 if (redirectStdOut) {
1055 1055 PythonQtObjectPtr sys;
1056 1056 PythonQtObjectPtr out;
1057 1057 PythonQtObjectPtr err;
1058 1058 sys.setNewRef(PyImport_ImportModule("sys"));
1059 1059 // create a redirection object for stdout and stderr
1060 1060 out = PythonQtStdOutRedirectType.tp_new(&PythonQtStdOutRedirectType,NULL, NULL);
1061 1061 ((PythonQtStdOutRedirect*)out.object())->_cb = stdOutRedirectCB;
1062 1062 err = PythonQtStdOutRedirectType.tp_new(&PythonQtStdOutRedirectType,NULL, NULL);
1063 1063 ((PythonQtStdOutRedirect*)err.object())->_cb = stdErrRedirectCB;
1064 1064 // replace the built in file objects with our own objects
1065 1065 PyModule_AddObject(sys, "stdout", out);
1066 1066 PyModule_AddObject(sys, "stderr", err);
1067 1067 }
1068 1068 }
1069 1069
1070 1070 void PythonQt::registerCPPClass(const char* typeName, const char* parentTypeName, const char* package, PythonQtQObjectCreatorFunctionCB* wrapperCreator, PythonQtShellSetInstanceWrapperCB* shell)
1071 1071 {
1072 1072 _p->registerCPPClass(typeName, parentTypeName, package, wrapperCreator, shell);
1073 1073 }
1074 1074
1075 1075
1076 1076 PythonQtClassInfo* PythonQtPrivate::lookupClassInfoAndCreateIfNotPresent(const char* typeName)
1077 1077 {
1078 1078 PythonQtClassInfo* info = _knownClassInfos.value(typeName);
1079 1079 if (!info) {
1080 1080 info = new PythonQtClassInfo();
1081 1081 info->setupCPPObject(typeName);
1082 1082 _knownClassInfos.insert(typeName, info);
1083 1083 }
1084 1084 return info;
1085 1085 }
1086 1086
1087 1087 void PythonQt::addPolymorphicHandler(const char* typeName, PythonQtPolymorphicHandlerCB* cb)
1088 1088 {
1089 1089 _p->addPolymorphicHandler(typeName, cb);
1090 1090 }
1091 1091
1092 1092 void PythonQtPrivate::addPolymorphicHandler(const char* typeName, PythonQtPolymorphicHandlerCB* cb)
1093 1093 {
1094 1094 PythonQtClassInfo* info = lookupClassInfoAndCreateIfNotPresent(typeName);
1095 1095 info->addPolymorphicHandler(cb);
1096 1096 }
1097 1097
1098 1098 bool PythonQt::addParentClass(const char* typeName, const char* parentTypeName, int upcastingOffset)
1099 1099 {
1100 1100 return _p->addParentClass(typeName, parentTypeName, upcastingOffset);
1101 1101 }
1102 1102
1103 1103 bool PythonQtPrivate::addParentClass(const char* typeName, const char* parentTypeName, int upcastingOffset)
1104 1104 {
1105 1105 PythonQtClassInfo* info = _knownClassInfos.value(typeName);
1106 1106 if (info) {
1107 1107 PythonQtClassInfo* parentInfo = lookupClassInfoAndCreateIfNotPresent(parentTypeName);
1108 1108 info->addParentClass(PythonQtClassInfo::ParentClassInfo(parentInfo, upcastingOffset));
1109 1109 return true;
1110 1110 } else {
1111 1111 return false;
1112 1112 }
1113 1113 }
1114 1114
1115 1115 void PythonQtPrivate::registerCPPClass(const char* typeName, const char* parentTypeName, const char* package, PythonQtQObjectCreatorFunctionCB* wrapperCreator, PythonQtShellSetInstanceWrapperCB* shell)
1116 1116 {
1117 1117 PythonQtClassInfo* info = lookupClassInfoAndCreateIfNotPresent(typeName);
1118 1118 if (!info->pythonQtClassWrapper()) {
1119 1119 info->setupCPPObject(typeName);
1120 1120 createPythonQtClassWrapper(info, package);
1121 1121 }
1122 1122 if (parentTypeName && strcmp(parentTypeName,"")!=0) {
1123 1123 addParentClass(typeName, parentTypeName, 0);
1124 1124 }
1125 1125 if (wrapperCreator) {
1126 1126 info->setDecoratorProvider(wrapperCreator);
1127 1127 }
1128 1128 if (shell) {
1129 1129 info->setShellSetInstanceWrapperCB(shell);
1130 1130 }
1131 1131 }
1132 1132
1133 1133 static PyObject *PythonQt_SIGNAL(PyObject * /*type*/, PyObject *args)
1134 1134 {
1135 1135 const char* value;
1136 1136 if (!PyArg_ParseTuple(args, "s", &value)) {
1137 1137 return NULL;
1138 1138 }
1139 1139 // we do not prepend 0,1 or 2, why should we?
1140 return PyString_FromString(value);
1140 return PyString_FromString(QByteArray("2") + value);
1141 1141 }
1142 1142
1143 1143 static PyObject *PythonQt_SLOT(PyObject * /*type*/, PyObject *args)
1144 1144 {
1145 1145 const char* value;
1146 1146 if (!PyArg_ParseTuple(args, "s", &value)) {
1147 1147 return NULL;
1148 1148 }
1149 1149 // we do not prepend 0,1 or 2, why should we?
1150 return PyString_FromString(value);
1150 return PyString_FromString(QByteArray("1") + value);
1151 1151 }
1152 1152
1153 1153 static PyMethodDef PythonQt_Qt_methods[] = {
1154 1154 {"SIGNAL", (PyCFunction)PythonQt_SIGNAL, METH_VARARGS,
1155 1155 "Returns a signal string"
1156 1156 },
1157 1157 {"SLOT", (PyCFunction)PythonQt_SLOT, METH_VARARGS,
1158 1158 "Returns a slot string"
1159 1159 }
1160 1160 };
1161 1161
1162 1162 PyObject* PythonQtPrivate::packageByName(const char* name)
1163 1163 {
1164 1164 if (name==NULL || name[0]==0) {
1165 1165 return _pythonQtModule;
1166 1166 }
1167 1167 PyObject* v = _packages.value(name);
1168 1168 if (!v) {
1169 1169 v = PyImport_AddModule((QByteArray("PythonQt.") + name).constData());
1170 1170 if (strcmp(name,"Qt")==0 || strcmp(name,"QtCore")==0) {
1171 1171 // add SIGNAL and SLOT functions
1172 1172 PyModule_AddObject(v, "SIGNAL", PyCFunction_New(PythonQt_Qt_methods, v));
1173 1173 PyModule_AddObject(v, "SLOT", PyCFunction_New(PythonQt_Qt_methods+1, v));
1174 1174 }
1175 1175 _packages.insert(name, v);
1176 1176 // AddObject steals the reference, so increment it!
1177 1177 Py_INCREF(v);
1178 1178 PyModule_AddObject(_pythonQtModule, name, v);
1179 1179 }
1180 1180 return v;
1181 1181 }
1182 1182
1183 1183
1184 1184 PyObject* PythonQt::helpCalled(PythonQtClassInfo* info)
1185 1185 {
1186 1186 if (_p->_initFlags & ExternalHelp) {
1187 1187 emit pythonHelpRequest(QByteArray(info->className()));
1188 1188 return Py_BuildValue("");
1189 1189 } else {
1190 1190 return PyString_FromString(info->help().toLatin1().data());
1191 1191 }
1192 1192 }
1193 1193
1194 1194 void PythonQtPrivate::removeWrapperPointer(void* obj)
1195 1195 {
1196 1196 _wrappedObjects.remove(obj);
1197 1197 }
1198 1198
1199 1199 void PythonQtPrivate::addWrapperPointer(void* obj, PythonQtInstanceWrapper* wrapper)
1200 1200 {
1201 1201 _wrappedObjects.insert(obj, wrapper);
1202 1202 }
1203 1203
1204 1204 PythonQtInstanceWrapper* PythonQtPrivate::findWrapperAndRemoveUnused(void* obj)
1205 1205 {
1206 1206 PythonQtInstanceWrapper* wrap = _wrappedObjects.value(obj);
1207 1207 if (wrap && !wrap->_wrappedPtr && wrap->_obj == NULL) {
1208 1208 // this is a wrapper whose QObject was already removed due to destruction
1209 1209 // so the obj pointer has to be a new QObject with the same address...
1210 1210 // we remove the old one and set the copy to NULL
1211 1211 wrap->_objPointerCopy = NULL;
1212 1212 removeWrapperPointer(obj);
1213 1213 wrap = NULL;
1214 1214 }
1215 1215 return wrap;
1216 1216 }
1217 1217
1218 1218 PythonQtObjectPtr PythonQtPrivate::createModule(const QString& name, PyObject* pycode)
1219 1219 {
1220 1220 PythonQtObjectPtr result;
1221 1221 if (pycode) {
1222 1222 result.setNewRef(PyImport_ExecCodeModule((char*)name.toLatin1().data(), pycode));
1223 1223 } else {
1224 1224 PythonQt::self()->handleError();
1225 1225 }
1226 1226 return result;
1227 1227 }
@@ -1,511 +1,510
1 1 #ifndef _PYTHONQTDOC_H
2 2 #define _PYTHONQTDOC_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 PythonQtDoc.h
39 39 // \author Florian Link
40 40 // \author Last changed by $Author: florian $
41 41 // \date 2006-10
42 42 */
43 43 //----------------------------------------------------------------------------------
44 44
45 45 /*!
46 46 \if USE_GLOBAL_DOXYGEN_DOC
47 47 \page PythonQtPage PythonQt Overview
48 48 \else
49 49 \mainpage PythonQt Overview
50 50 \endif
51 51
52 52 \section Introduction
53 53
54 54 \b PythonQt is a dynamic Python (http://www.python.org) binding for Qt (http://www.qtsoftware.com).
55 55 It offers an easy way to embed the Python scripting language into
56 56 your Qt applications. It makes heavy use of the QMetaObject system and thus requires Qt4.x.
57 57
58 58 The focus of PythonQt is on embedding Python into an existing C++ application, not on writing the whole
59 59 application completely in Python. If you want to write your whole application in Python,
60 60 you should use <a href="http://www.riverbankcomputing.co.uk/pyqt/">PyQt</a> instead.
61 61
62 62 If you are looking for a simple way to embed Python objects into your C++/Qt Application
63 63 and to script parts of your application via Python, PythonQt is the way to go!
64 64
65 65 PythonQt is a stable library that was developed to make the
66 66 Image Processing and Visualization platform MeVisLab (http://www.mevislab.de)
67 67 scriptable from Python.
68 68
69 69 \section Download
70 70
71 71 PythonQt is hosted on SourceForge at http://sourceforge.net/projects/pythonqt , you can access it via SVN
72 72 or download a tarball.
73 73
74 74 \section Licensing
75 75
76 76 PythonQt is distributed under the LGPL license, so it pairs well with the LGPL of the Qt 4.5 release and allows
77 77 to be used in commercial applications when following the LGPL 2.1 obligations.
78 78
79 79 \section LicensingWrapper Licensing of Wrapper Generator
80 80
81 81 The build system of PythonQt makes use of a modified version of the GPL'ed QtScript generator,
82 82 located in the "generator" directory.
83 83
84 84 See http://labs.trolltech.com/page/Projects/QtScript/Generator for details on the original project.
85 85 Thanks a lot to the QtJambi guys and the QtScript Generator project for the C++ parser and
86 86 Qt typesystem files!
87 87
88 88 The PythonQt wrappers generated by the generator located in the "generated_cpp" directory are distributed under the LGPL,
89 89 they are not restriced by the GPL.
90 90
91 91 The generated wrappers are pre-generated and checked-in for Qt 4.4.3, so you only need to build and run the
92 92 generator when you want to build additional wrappers or you want to upgrade/downgrade to an newer Qt version.
93 93 You may use the generator to generate C++ bindings for your own C++ classes (e.g. to make them deriveable in Python),
94 94 , but this is currently not documented and involves creating your own typesystem files.
95 95
96 96 \section Features
97 97
98 98 The following are the built-in features of the PythonQt library:
99 99
100 100 - Access all \b slots, \b properties, children and registered enums of any QObject derived class from Python
101 101 - Connecting Qt Signals to Python functions (both from within Python and from C++)
102 102 - Easy wrapping of Python objects from C++ with smart, reference-counting PythonQtObjectPtr.
103 103 - Convenient conversions to/from QVariant for PythonQtObjectPtr.
104 104 - Wrapping of C++ objects (which are not derived from QObject) via PythonQtCppWrapperFactory
105 105 - Extending C++ and QObject derived classes with additional slots, static methods and constructors (see Decorators)
106 106 - StdOut/Err redirection to Qt signals instead of cout
107 107 - Interface for creating your own \c import replacement, so that Python scripts can be e.g. signed/verified before they are executed (PythonQtImportFileInterface)
108 108 - Mapping of plain-old-datatypes and ALL QVariant types to and from Python
109 109 - Support for wrapping of user QVariant types which are registerd via QMetaType
110 110 - Support for Qt namespace (with all enumerators)
111 111 - All PythonQt wrapped objects support the dir() statement, so that you can see easily which attributes a QObject, CPP object or QVariant has
112 112 - No preprocessing/wrapping tool needs to be started, PythonQt can script any QObject without prior knowledge about it (except for the MetaObject information from the \b moc)
113 113 - Multiple inheritance for C++ objects (e.g. a QWidget is derived from QObject and QPaintDevice, PythonQt will automatically cast a QWidget to a QPaintDevice when needed)
114 114 - Polymorphic downcasting (if e.g. PythonQt sees a QEvent, it can downcast it depending on the type(), so the Python e.g. sees a QPaintEvent instead of a plain QEvent)
115 115 - Deriving C++ objects from Python and overwriting virtual method with a Python implementation (requires usage of wrapper generator or manual work!)
116 116 - Extensible handler for Python/C++ conversion of complex types, e.g. mapping of QVector<SomeObject> to/from a Python array
117 117
118 118 \section FeaturesQtAll Features (with PythonQt_QtAll linked in)
119 119
120 120 Thanks to the new wrapper generator, PythonQt now offers the additional PythonQt_QtAll library which wraps the complete Qt API, including all C++ classes and all non-slots on QObject derived classes.
121 121 This offers the following features:
122 122
123 123 - Complete Qt API wrapped and accessible
124 124 - The following modules are available as submodule of the PythonQt module:
125 125 - QtCore
126 126 - QtGui
127 127 - QtNetwork
128 128 - QtOpenGL
129 129 - QtSql
130 130 - QtSvg
131 131 - QtUiTools
132 132 - QtWebKit
133 133 - QtXml
134 134 - QtXmlPatterns
135 135 - (phonon, QtHelp, assistant, designer are currently not supported, this would require some additional effort on the code generator)
136 136 - For convenience, all classes are also available in the PythonQt.Qt module, for people who do not care in which module a class is located
137 137 - Any Qt class that has virtual methods can be easily derived from Python and the virtual methods can be reimplemented in Python
138 138 - Polymorphic downcasting on QEvent, QGraphicsItem, QStyleOption, ...
139 139 - Multiple inheritance support (e.g. QGraphicsTextItem is a QObject AND a QGraphicsItem, PythonQt will handle this well)
140 140
141 141 \section Comparision Comparision with PyQt
142 142
143 143 - PythonQt is not as Pythonic as PyQt in many details (e.g. operator mapping, pickling, translation support, ...) and it is mainly thought for embedding and intercommunication between Qt/Cpp and Python
144 144 - PythonQt allows to communicate in both directions, e.g. calling a Python object from C++ AND calling a C++ method from Python, while PyQt only handles the Python->C++ direction
145 145 - PythonQt offers properties as Python attributes, while PyQt offers them as setter/getter methods (e.g. QWidget.width is a property in PythonQt and a method in PyQt)
146 - PythonQt offer QtCore.SIGNAL() and QtCore.SLOT() methods for compability, but no 0/1/3 is prepended
147 146 - PythonQt does not support instanceof checks for Qt classes, except for the exact match and derived Python classes
148 147 - QObject.emit to emit Qt signals from Python is not yet implemented
149 148 - PythonQt does not offer to add new signals to Python/C++ objects
150 149 - Ownership of objects is a bit different in PythonQt, currently Python classes derived from a C++ class need to be manually references in PythonQt to not get deleted too early (this will be fixed)
151 150 - Probably there are lots of details that differ, I do not know PyQt that well to list them all.
152 151
153 152
154 153 \section Interface
155 154
156 155 The main interface to PythonQt is the PythonQt singleton.
157 156 PythonQt needs to be initialized via PythonQt::init() once.
158 157 Afterwards you communicate with the singleton via PythonQt::self().
159 158 PythonQt offers a complete Qt binding, which
160 159 needs to be enabled via PythonQt_QtAll::init().
161 160
162 161
163 162 \section Datatype Datatype Mapping
164 163
165 164 The following table shows the mapping between Python and Qt objects:
166 165 <table>
167 166 <tr><th>Qt/C++</th><th>Python</th></tr>
168 167 <tr><td>bool</td><td>bool</td></tr>
169 168 <tr><td>double</td><td>float</td></tr>
170 169 <tr><td>float</td><td>float</td></tr>
171 170 <tr><td>char/uchar,int/uint,short,ushort,QChar</td><td>integer</td></tr>
172 171 <tr><td>long</td><td>integer</td></tr>
173 172 <tr><td>ulong,longlong,ulonglong</td><td>long</td></tr>
174 173 <tr><td>QString</td><td>unicode string</td></tr>
175 174 <tr><td>QByteArray</td><td>str</td></tr>
176 175 <tr><td>char*</td><td>str</td></tr>
177 176 <tr><td>QStringList</td><td>tuple of unicode strings</td></tr>
178 177 <tr><td>QVariantList</td><td>tuple of objects</td></tr>
179 178 <tr><td>QVariantMap</td><td>dict of objects</td></tr>
180 179 <tr><td>QVariant</td><td>depends on type, see below</td></tr>
181 180 <tr><td>QSize, QRect and all other standard Qt QVariants</td><td>variant wrapper that supports complete API of the respective Qt classes</td></tr>
182 181 <tr><td>OwnRegisteredMetaType</td><td>C++ wrapper, optionally with additional information/wrapping provided by registerCPPClass()</td></tr>
183 182 <tr><td>QList<AnyObject*></td><td>converts to a list of CPP wrappers</td></tr>
184 183 <tr><td>EnumType</td><td>integer (all enums that are known via the moc and the Qt namespace are supported)</td></tr>
185 184 <tr><td>QObject (and derived classes)</td><td>QObject wrapper</td></tr>
186 185 <tr><td>C++ object</td><td>CPP wrapper, either wrapped via PythonQtCppWrapperFactory or just decorated with decorators</td></tr>
187 186 <tr><td>PyObject</td><td>PyObject</td></tr>
188 187 </table>
189 188
190 189 PyObject is passed as simple pointer, which allows to pass/return any Python Object directly to/from
191 190 a Qt slot.
192 191 QVariants are mapped recursively as given above, e.g. a dictionary can
193 192 contain lists of dictionaries of doubles.
194 193 For example a QVariant of type "String" is mapped to a python unicode string.
195 194 All Qt QVariant types are implemented, PythonQt supports the complete Qt API for these object.
196 195
197 196 \section QObject QObject Wrapping
198 197
199 198 All classes derived from QObject are automatically wrapped with a python wrapper class
200 199 when they become visible to the Python interpreter. This can happen via
201 200 - the PythonQt::addObject() method
202 201 - when a Qt \b slot returns a QObject derived object to python
203 202 - when a Qt \b signal contains a QObject and is connected to a python function
204 203
205 204 It is important that you call PythonQt::registerClass() for any QObject derived class
206 205 that may become visible to Python, except when you add it via PythonQt::addObject().
207 206 This will register the complete parent hierachy of the registered class, so that
208 207 when you register e.g. a QPushButton, QWidget will be registered as well (and all intermediate
209 208 parents).
210 209
211 210 From Python, you can talk to the returned QObjects in a natural way by calling
212 211 their slots and receiving the return values. You can also read/write all
213 212 properties of the objects as if they where normal python properties.
214 213
215 214 In addition to this, the wrapped objects support
216 215 - className() - returns a string that reprents the classname of the QObject
217 216 - help() - shows all properties, slots, enums, decorator slots and constructors of the object, in a printable form
218 217 - delete() - deletes the object (use with care, especially if you passed the ownership to C++)
219 218 - connect(signal, function) - connect the signal of the given object to a python function
220 219 - connect(signal, qobject, slot) - connect the signal of the given object to a slot of another QObject
221 220 - disconnect(signal, function) - disconnect the signal of the given object from a python function
222 221 - disconnect(signal, qobject, slot) - disconnect the signal of the given object from a slot of another QObject
223 222 - children() - returns the children of the object
224 223 - setParent(QObject) - set the parent
225 224 - QObject* parent() - get the parent
226 225
227 226 The below example shows how to connect signals in Python:
228 227
229 228 \code
230 229 # define a signal handler function
231 230 def someFunction(flag):
232 231 print flag
233 232
234 233 # button1 is a QPushButton that has been added to Python via addObject()
235 234 # connect the clicked signal to a python function:
236 235 button1.connect("clicked(bool)", someFunction)
237 236
238 237 \endcode
239 238
240 239 \section CPP CPP Wrapping
241 240
242 241 You can create dedicated wrapper QObjects for any C++ class. This is done by deriving from PythonQtCppWrapperFactory
243 242 and adding your factory via addWrapperFactory().
244 243 Whenever PythonQt encounters a CPP pointer (e.g. on a slot or signal)
245 244 and it does not known it as a QObject derived class, it will create a generic CPP wrapper. So even unknown C++ objects
246 245 can be passed through Python. If the wrapper factory supports the CPP class, a QObject wrapper will be created for each
247 246 instance that enters Python. An alternative to a complete wrapper via the wrapper factory are decorators, see \ref Decorators
248 247
249 248 \section MetaObject Meta Object/Class access
250 249
251 250 For each known C++ class, PythonQt provides a Python class. These classes are visible
252 251 inside of the "PythonQt" python module or in subpackages if a package is given when the class is registered.
253 252
254 253 A Meta class supports:
255 254
256 255 - access to all declared enum values
257 256 - constructors
258 257 - static methods
259 258 - unbound non-static methods
260 259 - help() and className()
261 260
262 261 From within Python, you can import the module "PythonQt" to access these classes and the Qt namespace.
263 262
264 263 \code
265 264 from PythonQt import QtCore
266 265
267 266 # namespace access:
268 267 print QtCore.Qt.AlignLeft
269 268
270 269 # constructors
271 270 a = QtCore.QSize(12,13)
272 271 b = QtCore.QFont()
273 272
274 273 # static method
275 274 QtCore.QDate.currentDate()
276 275
277 276 # enum value
278 277 QtCore.QFont.UltraCondensed
279 278
280 279 \endcode
281 280
282 281 \section Decorators Decorator slots
283 282
284 283 PythonQt introduces a new generic approach to extend any wrapped QObject or CPP object with
285 284
286 285 - constructors
287 286 - destructors (for CPP objects)
288 287 - additional slots
289 288 - static slots (callable on both the Meta object and the instances)
290 289
291 290 The idea behind decorators is that we wanted to make it as easy as possible to extend
292 291 wrapped objects. Since we already have an implementation for invoking any Qt Slot from
293 292 Python, it looked promising to use this approach for the extension of wrapped objects as well.
294 293 This avoids that the PythonQt user needs to care about how Python arguments are mapped from/to
295 294 Qt when he wants to create static methods, constructors and additional member functions.
296 295
297 296 The basic idea about decorators is to create a QObject derived class that implements slots
298 297 which take one of the above roles (e.g. constructor, destructor etc.) via a naming convention.
299 298 These slots are then assigned to other classes via the naming convention.
300 299
301 300 - SomeClassName* new_SomeClassName(...) - defines a constructor for "SomeClassName" that returns a new object of type SomeClassName (where SomeClassName can be any CPP class, not just QObject classes)
302 301 - void delete_SomeClassName(SomeClassName* o) - defines a destructor, which should delete the passed in object o
303 302 - anything static_SomeClassName_someMethodName(...) - defines a static method that is callable on instances and the meta class
304 303 - anything someMethodName(SomeClassName* o, ...) - defines a slot that will be available on SomeClassName instances (and derived instances). When such a slot is called the first argument is the pointer to the instance and the rest of the arguments can be used to make a call on the instance.
305 304
306 305 The below example shows all kinds of decorators in action:
307 306
308 307 \code
309 308
310 309 // an example CPP object
311 310 class YourCPPObject {
312 311 public:
313 312 YourCPPObject(int arg1, float arg2) { a = arg1; b = arg2; }
314 313
315 314 float doSomething(int arg1) { return arg1*a*b; };
316 315
317 316 private:
318 317
319 318 int a;
320 319 float b;
321 320 };
322 321
323 322 // an example decorator
324 323 class ExampleDecorator : public QObject
325 324 {
326 325 Q_OBJECT
327 326
328 327 public slots:
329 328 // add a constructor to QSize that takes a QPoint
330 329 QSize* new_QSize(const QPoint& p) { return new QSize(p.x(), p.y()); }
331 330
332 331 // add a constructor for QPushButton that takes a text and a parent widget
333 332 QPushButton* new_QPushButton(const QString& text, QWidget* parent=NULL) { return new QPushButton(text, parent); }
334 333
335 334 // add a constructor for a CPP object
336 335 YourCPPObject* new_YourCPPObject(int arg1, float arg2) { return new YourCPPObject(arg1, arg2); }
337 336
338 337 // add a destructor for a CPP object
339 338 void delete_YourCPPObject(YourCPPObject* obj) { delete obj; }
340 339
341 340 // add a static method to QWidget
342 341 QWidget* static_QWidget_mouseGrabber() { return QWidget::mouseGrabber(); }
343 342
344 343 // add an additional slot to QWidget (make move() callable, which is not declared as a slot in QWidget)
345 344 void move(QWidget* w, const QPoint& p) { w->move(p); }
346 345
347 346 // add an additional slot to QWidget, overloading the above move method
348 347 void move(QWidget* w, int x, int y) { w->move(x,y); }
349 348
350 349 // add a method to your own CPP object
351 350 int doSomething(YourCPPObject* obj, int arg1) { return obj->doSomething(arg1); }
352 351 };
353 352
354 353 ...
355 354
356 355 PythonQt::self()->addDecorators(new ExampleDecorator());
357 356 PythonQt::self()->registerCPPClass("YourCPPObject");
358 357
359 358 \endcode
360 359
361 360 After you have registered an instance of the above ExampleDecorator, you can do the following from Python
362 361 (all these calls are mapped to the above decorator slots):
363 362
364 363 \code
365 364 from PythonQt import QtCore, QtGui, YourCPPObject
366 365
367 366 # call our new constructor of QSize
368 367 size = QtCore.QSize(QPoint(1,2));
369 368
370 369 # call our new QPushButton constructor
371 370 button = QtGui.QPushButton("sometext");
372 371
373 372 # call the move slot (overload1)
374 373 button.move(QPoint(0,0))
375 374
376 375 # call the move slot (overload2)
377 376 button.move(0,0)
378 377
379 378 # call the static method
380 379 grabber = QtGui.QWidget.mouseWrapper();
381 380
382 381 # create a CPP object via constructor
383 382 yourCpp = YourCPPObject(1,11.5)
384 383
385 384 # call the wrapped method on CPP object
386 385 print yourCpp.doSomething(1);
387 386
388 387 # destructor will be called:
389 388 yourCpp = None
390 389
391 390 \endcode
392 391
393 392 \section Building
394 393
395 394 PythonQt requires at least Qt 4.2.2 (or higher) and Python 2.3, 2.4, 2.5 or 2.6 on Windows, Linux and MacOS X. It has not yet been tested with Python 3.x, but it should only require minor changes.
396 395 To compile PythonQt, you will need a python developer installation which includes Python's header files and
397 396 the python2x.[lib | dll | so | dynlib].
398 397 The build scripts a currently set to use Python 2.5.
399 398 You may need to tweak the \b build/python.prf file to set the correct Python includes and libs on your system.
400 399
401 400 \subsection Windows
402 401
403 402 On Windows, the (non-source) Python Windows installer can be used.
404 403 Make sure that you use the same compiler, the current Python distribution is built
405 404 with Visual Studio 2003. If you want to use another compiler, you will need to build
406 405 Python yourself, using your compiler.
407 406
408 407 To build PythonQt, you need to set the environment variable \b PYTHON_PATH to point to the root
409 408 dir of the python installation and \b PYTHON_LIB to point to
410 409 the directory where the python lib file is located.
411 410
412 411 When using the prebuild Python installer, this will be:
413 412
414 413 \code
415 414 > set PYTHON_PATH = c:\Python25
416 415 > set PYTHON_LIB = c:\Python25\libs
417 416 \endcode
418 417
419 418 When using the python sources, this will be something like:
420 419
421 420 \code
422 421 > set PYTHON_PATH = c:\yourDir\Python-2.5.1\
423 422 > set PYTHON_LIB = c:\yourDir\Python-2.5.1\PCbuild8\Win32
424 423 \endcode
425 424
426 425 To build all, do the following (after setting the above variables):
427 426
428 427 \code
429 428 > cd PythonQtRoot
430 429 > vcvars32
431 430 > qmake
432 431 > nmake
433 432 \endcode
434 433
435 434 This should build everything. If Python can not be linked or include files can not be found,
436 435 you probably need to tweak \b build/python.prf
437 436
438 437 The tests and examples are located in PythonQt/lib.
439 438
440 439 \subsection Linux
441 440
442 441 On Linux, you need to install a Python-dev package.
443 442 If Python can not be linked or include files can not be found,
444 443 you probably need to tweak \b build/python.prf
445 444
446 445 To build PythonQt, just do a:
447 446
448 447 \code
449 448 > cd PythonQtRoot
450 449 > qmake
451 450 > make all
452 451 \endcode
453 452
454 453 The tests and examples are located in PythonQt/lib.
455 454 You should add PythonQt/lib to your LD_LIBRARY_PATH so that the runtime
456 455 linker can find the *.so files.
457 456
458 457 \subsection MacOsX
459 458
460 459 On Mac, Python is installed as a Framework, so you should not need to install it.
461 460 To build PythonQt, just do a:
462 461
463 462 \code
464 463 > cd PythonQtRoot
465 464 > qmake
466 465 > make all
467 466 \endcode
468 467
469 468 \section Tests
470 469
471 470 There is a unit test that tests most features of PythonQt, see the \b tests subdirectory for details.
472 471
473 472 \section Examples
474 473
475 474 Examples are available in the \b examples directory. The PyScriptingConsole implements a simple
476 475 interactive scripting console that shows how to script a simple application.
477 476
478 477 The following shows how to integrate PythonQt into you Qt application:
479 478
480 479 \code
481 480 #include "PythonQt.h"
482 481 #include <QApplication>
483 482 ...
484 483
485 484 int main( int argc, char **argv )
486 485 {
487 486
488 487 QApplication qapp(argc, argv);
489 488
490 489 // init PythonQt and Python itself
491 490 PythonQt::init(PythonQt::IgnoreSiteModule | PythonQt::RedirectStdOut);
492 491
493 492
494 493 // get a smart pointer to the __main__ module of the Python interpreter
495 494 PythonQtObjectPtr mainContext = PythonQt::self()->getMainModule();
496 495
497 496 // add a QObject as variable of name "example" to the namespace of the __main__ module
498 497 PyExampleObject example;
499 498 PythonQt::self()->addObject(mainContext, "example", &example);
500 499
501 500 // do something
502 501 PythonQt::self()->runScript(mainContext, "print example\n");
503 502 PythonQt::self()->runScript(mainContext, "def multiply(a,b):\n return a*b;\n");
504 503 QVariantList args;
505 504 args << 42 << 47;
506 505 QVariant result = PythonQt::self()->call(mainContext,"multiply", args);
507 506 ...
508 507 \endcode
509 508
510 509
511 510 */
@@ -1,125 +1,155
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 PythonQtStdDecorators.cpp
36 36 // \author Florian Link
37 37 // \author Last changed by $Author: florian $
38 38 // \date 2007-04
39 39 */
40 40 //----------------------------------------------------------------------------------
41 41
42 42 #include "PythonQtStdDecorators.h"
43 43 #include "PythonQt.h"
44 44 #include "PythonQtClassInfo.h"
45 45
46 46
47 47 bool PythonQtStdDecorators::connect(QObject* sender, const QByteArray& signal, PyObject* callable)
48 48 {
49 QByteArray signalTmp("2");
50 signalTmp += signal;
49 QByteArray signalTmp;
50 char first = signal.at(0);
51 if (first>='0' && first<='9') {
52 signalTmp = signal;
53 } else {
54 signalTmp = "2" + signal;
55 }
56
51 57 if (sender) {
52 58 return PythonQt::self()->addSignalHandler(sender, signalTmp, callable);
53 59 } else {
54 60 return false;
55 61 }
56 62 }
57 63
58 64 bool PythonQtStdDecorators::connect(QObject* sender, const QByteArray& signal, QObject* receiver, const QByteArray& slot)
59 65 {
60 66 bool r = false;
61 67 if (sender && receiver) {
62 QByteArray signalTmp("2");
63 signalTmp += signal;
64 QByteArray slotTmp("1");
65 slotTmp += slot;
66 if (receiver) {
67 r = QObject::connect(sender, signalTmp, receiver, slotTmp);
68 QByteArray signalTmp;
69 char first = signal.at(0);
70 if (first>='0' && first<='9') {
71 signalTmp = signal;
72 } else {
73 signalTmp = "2" + signal;
68 74 }
75
76 QByteArray slotTmp;
77 first = slot.at(0);
78 if (first>='0' && first<='9') {
79 slotTmp = slot;
80 } else {
81 slotTmp = "1" + slot;
82 }
83 r = QObject::connect(sender, signalTmp, receiver, slotTmp);
69 84 }
70 85 return r;
71 86 }
72 87
73 88 bool PythonQtStdDecorators::disconnect(QObject* sender, const QByteArray& signal, PyObject* callable)
74 89 {
75 QByteArray signalTmp("2");
76 signalTmp += signal;
90 QByteArray signalTmp;
91 char first = signal.at(0);
92 if (first>='0' && first<='9') {
93 signalTmp = signal;
94 } else {
95 signalTmp = "2" + signal;
96 }
77 97 if (sender) {
78 98 return PythonQt::self()->removeSignalHandler(sender, signalTmp, callable);
79 99 } else {
80 100 return false;
81 101 }
82 102 }
83 103
84 104 bool PythonQtStdDecorators::disconnect(QObject* sender, const QByteArray& signal, QObject* receiver, const QByteArray& slot)
85 105 {
86 106 bool r = false;
87 107 if (sender && receiver) {
88 QByteArray signalTmp("2");
89 signalTmp += signal;
90 QByteArray slotTmp("1");
91 slotTmp += slot;
92 if (receiver) {
93 r = QObject::disconnect(sender, signalTmp, receiver, slotTmp);
108 QByteArray signalTmp;
109 char first = signal.at(0);
110 if (first>='0' && first<='9') {
111 signalTmp = signal;
112 } else {
113 signalTmp = "2" + signal;
94 114 }
115
116 QByteArray slotTmp;
117 first = slot.at(0);
118 if (first>='0' && first<='9') {
119 slotTmp = slot;
120 } else {
121 slotTmp = "1" + slot;
122 }
123
124 r = QObject::disconnect(sender, signalTmp, receiver, slotTmp);
95 125 }
96 126 return r;
97 127 }
98 128
99 129 #undef emit
100 130 void PythonQtStdDecorators::emit(QObject* sender, const QByteArray& signal, PyObject* arg1 ,PyObject* arg2 ,
101 131 PyObject* arg3 ,PyObject* arg4 ,PyObject* arg5 ,PyObject* arg6 ,PyObject* arg7 )
102 132 {
103 133 // TODO xxx
104 134 // use normal PythonQtSlot calling code, add "allowSignal" to member lookup?!
105 135 }
106 136 #define emit
107 137
108 138 QObject* PythonQtStdDecorators::parent(QObject* o) {
109 139 return o->parent();
110 140 }
111 141
112 142 void PythonQtStdDecorators::setParent(QObject* o, QObject* parent)
113 143 {
114 144 o->setParent(parent);
115 145 }
116 146
117 147 QVariantList PythonQtStdDecorators::children(QObject* o)
118 148 {
119 149 QVariantList v;
120 150 QListIterator<QObject*> it(o->children());
121 151 while (it.hasNext()) {
122 152 v << qVariantFromValue(it.next());
123 153 }
124 154 return v;
125 155 }
General Comments 0
You need to be logged in to leave comments. Login now