##// END OF EJS Templates
changed to support dicts for variable lookup and eval...
florianlink -
r25:60e7b3114ac6
parent child
Show More
@@ -1,1115 +1,1164
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 375 if (!info || info->pythonQtClassWrapper()==NULL) {
376 376 // still unknown, register as CPP class
377 377 registerCPPClass(name.constData());
378 378 info = _knownClassInfos.value(name);
379 379 }
380 380 if (wrapper && (info->metaObject() != wrapper->metaObject())) {
381 381 // if we a have a QObject wrapper and the metaobjects do not match, set the metaobject again!
382 382 info->setMetaObject(wrapper->metaObject());
383 383 }
384 384 wrap = createNewPythonQtInstanceWrapper(wrapper, info, ptr);
385 385 // mlabDebugConst("MLABPython","new c++ wrapper added " << wrap->_wrappedPtr << " " << wrap->_obj->className() << " " << wrap->classInfo()->wrappedClassName().latin1());
386 386 } else {
387 387 Py_INCREF(wrap);
388 388 //mlabDebugConst("MLABPython","c++ wrapper reused " << wrap->_wrappedPtr << " " << wrap->_obj->className() << " " << wrap->classInfo()->wrappedClassName().latin1());
389 389 }
390 390 return (PyObject*)wrap;
391 391 }
392 392
393 393 PyObject* PythonQtPrivate::dummyTuple() {
394 394 static PyObject* dummyTuple = NULL;
395 395 if (dummyTuple==NULL) {
396 396 dummyTuple = PyTuple_New(1);
397 397 PyTuple_SET_ITEM(dummyTuple, 0 , PyString_FromString("dummy"));
398 398 }
399 399 return dummyTuple;
400 400 }
401 401
402 402
403 403 PythonQtInstanceWrapper* PythonQtPrivate::createNewPythonQtInstanceWrapper(QObject* obj, PythonQtClassInfo* info, void* wrappedPtr) {
404 404 // call the associated class type to create a new instance...
405 405 PythonQtInstanceWrapper* result = (PythonQtInstanceWrapper*)PyObject_Call(info->pythonQtClassWrapper(), dummyTuple(), NULL);
406 406
407 407 result->setQObject(obj);
408 408 result->_wrappedPtr = wrappedPtr;
409 409 result->_ownedByPythonQt = false;
410 410 result->_useQMetaTypeDestroy = false;
411 411
412 412 if (wrappedPtr) {
413 413 _wrappedObjects.insert(wrappedPtr, result);
414 414 } else {
415 415 _wrappedObjects.insert(obj, result);
416 416 if (obj->parent()== NULL && _wrappedCB) {
417 417 // tell someone who is interested that the qobject is wrapped the first time, if it has no parent
418 418 (*_wrappedCB)(obj);
419 419 }
420 420 }
421 421 return result;
422 422 }
423 423
424 424 PythonQtClassWrapper* PythonQtPrivate::createNewPythonQtClassWrapper(PythonQtClassInfo* info, const char* package) {
425 425 PythonQtClassWrapper* result;
426 426
427 427 PyObject* className = PyString_FromString(info->className());
428 428
429 429 PyObject* baseClasses = PyTuple_New(1);
430 430 PyTuple_SET_ITEM(baseClasses, 0, (PyObject*)&PythonQtInstanceWrapper_Type);
431 431
432 432 PyObject* typeDict = PyDict_New();
433 433 QByteArray moduleName("PythonQt");
434 434 if (package && strcmp(package, "")!=0) {
435 435 moduleName += ".";
436 436 moduleName += package;
437 437 }
438 438 PyDict_SetItemString(typeDict, "__module__", PyString_FromString(moduleName.constData()));
439 439
440 440 PyObject* args = Py_BuildValue("OOO", className, baseClasses, typeDict);
441 441
442 442 // set the class info so that PythonQtClassWrapper_new can read it
443 443 _currentClassInfoForClassWrapperCreation = info;
444 444 // create the new type object by calling the type
445 445 result = (PythonQtClassWrapper *)PyObject_Call((PyObject *)&PythonQtClassWrapper_Type, args, NULL);
446 446
447 447 Py_DECREF(baseClasses);
448 448 Py_DECREF(typeDict);
449 449 Py_DECREF(args);
450 450 Py_DECREF(className);
451 451
452 452 return result;
453 453 }
454 454
455 455
456 456 PythonQtSignalReceiver* PythonQt::getSignalReceiver(QObject* obj)
457 457 {
458 458 PythonQtSignalReceiver* r = _p->_signalReceivers[obj];
459 459 if (!r) {
460 460 r = new PythonQtSignalReceiver(obj);
461 461 _p->_signalReceivers.insert(obj, r);
462 462 }
463 463 return r;
464 464 }
465 465
466 466 bool PythonQt::addSignalHandler(QObject* obj, const char* signal, PyObject* module, const QString& objectname)
467 467 {
468 468 bool flag = false;
469 469 PythonQtObjectPtr callable = lookupCallable(module, objectname);
470 470 if (callable) {
471 471 PythonQtSignalReceiver* r = getSignalReceiver(obj);
472 472 flag = r->addSignalHandler(signal, callable);
473 473 if (!flag) {
474 474 // signal not found
475 475 }
476 476 } else {
477 477 // callable not found
478 478 }
479 479 return flag;
480 480 }
481 481
482 482 bool PythonQt::addSignalHandler(QObject* obj, const char* signal, PyObject* receiver)
483 483 {
484 484 bool flag = false;
485 485 PythonQtSignalReceiver* r = getSignalReceiver(obj);
486 486 if (r) {
487 487 flag = r->addSignalHandler(signal, receiver);
488 488 }
489 489 return flag;
490 490 }
491 491
492 492 bool PythonQt::removeSignalHandler(QObject* obj, const char* signal, PyObject* module, const QString& objectname)
493 493 {
494 494 bool flag = false;
495 495 PythonQtObjectPtr callable = lookupCallable(module, objectname);
496 496 if (callable) {
497 497 PythonQtSignalReceiver* r = _p->_signalReceivers[obj];
498 498 if (r) {
499 499 flag = r->removeSignalHandler(signal, callable);
500 500 }
501 501 } else {
502 502 // callable not found
503 503 }
504 504 return flag;
505 505 }
506 506
507 507 bool PythonQt::removeSignalHandler(QObject* obj, const char* signal, PyObject* receiver)
508 508 {
509 509 bool flag = false;
510 510 PythonQtSignalReceiver* r = _p->_signalReceivers[obj];
511 511 if (r) {
512 512 flag = r->removeSignalHandler(signal, receiver);
513 513 }
514 514 return flag;
515 515 }
516 516
517 517 PythonQtObjectPtr PythonQt::lookupCallable(PyObject* module, const QString& name)
518 518 {
519 519 PythonQtObjectPtr p = lookupObject(module, name);
520 520 if (p) {
521 521 if (PyCallable_Check(p)) {
522 522 return p;
523 523 }
524 524 }
525 525 PyErr_Clear();
526 526 return NULL;
527 527 }
528 528
529 529 PythonQtObjectPtr PythonQt::lookupObject(PyObject* module, const QString& name)
530 530 {
531 531 QStringList l = name.split('.');
532 532 PythonQtObjectPtr p = module;
533 533 PythonQtObjectPtr prev;
534 534 QString s;
535 535 QByteArray b;
536 536 for (QStringList::ConstIterator i = l.begin(); i!=l.end() && p; ++i) {
537 537 prev = p;
538 538 b = (*i).toLatin1();
539 p.setNewRef(PyObject_GetAttrString(p, b.data()));
539 if (PyDict_Check(p)) {
540 p = PyDict_GetItemString(p, b.data());
541 } else {
542 p.setNewRef(PyObject_GetAttrString(p, b.data()));
543 }
540 544 }
541 545 PyErr_Clear();
542 546 return p;
543 547 }
544 548
545 549 PythonQtObjectPtr PythonQt::getMainModule() {
546 550 //both borrowed
547 551 PythonQtObjectPtr dict = PyImport_GetModuleDict();
548 552 return PyDict_GetItemString(dict, "__main__");
549 553 }
550 554
551 QVariant PythonQt::evalCode(PyObject* module, PyObject* pycode) {
555 QVariant PythonQt::evalCode(PyObject* object, PyObject* pycode) {
552 556 QVariant result;
553 557 if (pycode) {
554 PyObject* r = PyEval_EvalCode((PyCodeObject*)pycode, PyModule_GetDict((PyObject*)module) , PyModule_GetDict((PyObject*)module));
558 PyObject* dict = NULL;
559 if (PyModule_Check(object)) {
560 dict = PyModule_GetDict(object);
561 } else if (PyDict_Check(object)) {
562 dict = object;
563 }
564 PyObject* r = NULL;
565 if (dict) {
566 r = PyEval_EvalCode((PyCodeObject*)pycode, dict , dict);
567 }
555 568 if (r) {
556 569 result = PythonQtConv::PyObjToQVariant(r);
557 570 Py_DECREF(r);
558 571 } else {
559 572 handleError();
560 573 }
561 574 } else {
562 575 handleError();
563 576 }
564 577 return result;
565 578 }
566 579
567 QVariant PythonQt::evalScript(PyObject* module, const QString& script, int start)
580 QVariant PythonQt::evalScript(PyObject* object, const QString& script, int start)
568 581 {
569 582 QVariant result;
570 583 PythonQtObjectPtr p;
571 p.setNewRef(PyRun_String(script.toLatin1().data(), start, PyModule_GetDict(module), PyModule_GetDict(module)));
584 PyObject* dict = NULL;
585 if (PyModule_Check(object)) {
586 dict = PyModule_GetDict(object);
587 } else if (PyDict_Check(object)) {
588 dict = object;
589 }
590 if (dict) {
591 p.setNewRef(PyRun_String(script.toLatin1().data(), start, dict, dict));
592 }
572 593 if (p) {
573 594 result = PythonQtConv::PyObjToQVariant(p);
574 595 } else {
575 596 handleError();
576 597 }
577 598 return result;
578 599 }
579 600
580 601 void PythonQt::evalFile(PyObject* module, const QString& filename)
581 602 {
582 603 PythonQtObjectPtr code = parseFile(filename);
583 604 if (code) {
584 605 evalCode(module, code);
585 606 } else {
586 607 handleError();
587 608 }
588 609 }
589 610
590 611 PythonQtObjectPtr PythonQt::parseFile(const QString& filename)
591 612 {
592 613 PythonQtObjectPtr p;
593 614 p.setNewRef(PythonQtImport::getCodeFromPyc(filename));
594 615 if (!p) {
595 616 handleError();
596 617 }
597 618 return p;
598 619 }
599 620
600 621 PythonQtObjectPtr PythonQt::createModuleFromFile(const QString& name, const QString& filename)
601 622 {
602 623 PythonQtObjectPtr code = parseFile(filename);
603 624 PythonQtObjectPtr module = _p->createModule(name, code);
604 625 return module;
605 626 }
606 627
607 628 PythonQtObjectPtr PythonQt::createModuleFromScript(const QString& name, const QString& script)
608 629 {
609 630 PyErr_Clear();
610 631 QString scriptCode = script;
611 632 if (scriptCode.isEmpty()) {
612 633 // we always need at least a linefeed
613 634 scriptCode = "\n";
614 635 }
615 636 PythonQtObjectPtr pycode;
616 637 pycode.setNewRef(Py_CompileString((char*)scriptCode.toLatin1().data(), "", Py_file_input));
617 638 PythonQtObjectPtr module = _p->createModule(name, pycode);
618 639 return module;
619 640 }
620 641
621 642 PythonQtObjectPtr PythonQt::createUniqueModule()
622 643 {
623 644 static QString pyQtStr("PythonQt_module");
624 645 QString moduleName = pyQtStr+QString::number(_uniqueModuleCount++);
625 646 return createModuleFromScript(moduleName);
626 647 }
627 648
628 void PythonQt::addObject(PyObject* module, const QString& name, QObject* object)
649 void PythonQt::addObject(PyObject* object, const QString& name, QObject* qObject)
629 650 {
630 PyModule_AddObject(module, name.toLatin1().data(), _p->wrapQObject(object));
651 if (PyModule_Check(object)) {
652 PyModule_AddObject(object, name.toLatin1().data(), _p->wrapQObject(qObject));
653 } else if (PyDict_Check(object)) {
654 PyDict_SetItemString(object, name.toLatin1().data(), _p->wrapQObject(qObject));
655 } else {
656 PyObject_SetAttrString(object, name.toLatin1().data(), _p->wrapQObject(qObject));
657 }
631 658 }
632 659
633 void PythonQt::addVariable(PyObject* module, const QString& name, const QVariant& v)
660 void PythonQt::addVariable(PyObject* object, const QString& name, const QVariant& v)
634 661 {
635 PyModule_AddObject(module, name.toLatin1().data(), PythonQtConv::QVariantToPyObject(v));
662 if (PyModule_Check(object)) {
663 PyModule_AddObject(object, name.toLatin1().data(), PythonQtConv::QVariantToPyObject(v));
664 } else if (PyDict_Check(object)) {
665 PyDict_SetItemString(object, name.toLatin1().data(), PythonQtConv::QVariantToPyObject(v));
666 } else {
667 PyObject_SetAttrString(object, name.toLatin1().data(), PythonQtConv::QVariantToPyObject(v));
668 }
636 669 }
637 670
638 void PythonQt::removeVariable(PyObject* module, const QString& name)
671 void PythonQt::removeVariable(PyObject* object, const QString& name)
639 672 {
640 PyObject_DelAttrString(module, name.toLatin1().data());
673 if (PyDict_Check(object)) {
674 PyDict_DelItemString(object, name.toLatin1().data());
675 } else {
676 PyObject_DelAttrString(object, name.toLatin1().data());
677 }
641 678 }
642 679
643 QVariant PythonQt::getVariable(PyObject* module, const QString& objectname)
680 QVariant PythonQt::getVariable(PyObject* object, const QString& objectname)
644 681 {
645 682 QVariant result;
646 PythonQtObjectPtr obj = lookupObject(module, objectname);
683 PythonQtObjectPtr obj = lookupObject(object, objectname);
647 684 if (obj) {
648 685 result = PythonQtConv::PyObjToQVariant(obj);
649 686 }
650 687 return result;
651 688 }
652 689
653 690 QStringList PythonQt::introspection(PyObject* module, const QString& objectname, PythonQt::ObjectType type)
654 691 {
655 692 QStringList results;
656 693
657 694 PythonQtObjectPtr object;
658 695 if (objectname.isEmpty()) {
659 696 object = module;
660 697 } else {
661 698 object = lookupObject(module, objectname);
662 699 if (!object && type == CallOverloads) {
663 700 PyObject* dict = lookupObject(module, "__builtins__");
664 701 if (dict) {
665 702 object = PyDict_GetItemString(dict, objectname.toLatin1().constData());
666 703 }
667 704 }
668 705 }
669 706
670 707 if (object) {
671 708 if (type == CallOverloads) {
672 709 if (PythonQtSlotFunction_Check(object)) {
673 710 PythonQtSlotFunctionObject* o = (PythonQtSlotFunctionObject*)object.object();
674 711 PythonQtSlotInfo* info = o->m_ml;
675 712
676 713 while (info) {
677 714 results << info->fullSignature();
678 715 info = info->nextInfo();
679 716 }
680 717 } else if (object->ob_type == &PythonQtClassWrapper_Type) {
681 718 PythonQtClassWrapper* o = (PythonQtClassWrapper*)object.object();
682 719 PythonQtSlotInfo* info = o->classInfo()->constructors();
683 720
684 721 while (info) {
685 722 results << info->fullSignature();
686 723 info = info->nextInfo();
687 724 }
688 725 } else {
689 726 //TODO: use pydoc!
690 727 PyObject* doc = PyObject_GetAttrString(object, "__doc__");
691 728 if (doc) {
692 729 results << PyString_AsString(doc);
693 730 Py_DECREF(doc);
694 731 }
695 732 }
696 733 } else {
697 PyObject* keys = PyObject_Dir(object);
734 PyObject* keys = NULL;
735 bool isDict = false;
736 if (PyDict_Check(object)) {
737 keys = PyDict_Keys(object);
738 isDict = true;
739 } else {
740 keys = PyObject_Dir(object);
741 }
698 742 if (keys) {
699 743 int count = PyList_Size(keys);
700 744 PyObject* key;
701 745 PyObject* value;
702 746 QString keystr;
703 747 for (int i = 0;i<count;i++) {
704 748 key = PyList_GetItem(keys,i);
705 value = PyObject_GetAttr(object, key);
749 if (isDict) {
750 value = PyDict_GetItem(object, key);
751 Py_INCREF(value);
752 } else {
753 value = PyObject_GetAttr(object, key);
754 }
706 755 if (!value) continue;
707 756 keystr = PyString_AsString(key);
708 757 static const QString underscoreStr("__tmp");
709 758 if (!keystr.startsWith(underscoreStr)) {
710 759 switch (type) {
711 760 case Anything:
712 761 results << keystr;
713 762 break;
714 763 case Class:
715 764 if (value->ob_type == &PyClass_Type) {
716 765 results << keystr;
717 766 }
718 767 break;
719 768 case Variable:
720 769 if (value->ob_type != &PyClass_Type
721 770 && value->ob_type != &PyCFunction_Type
722 771 && value->ob_type != &PyFunction_Type
723 772 && value->ob_type != &PyModule_Type
724 773 ) {
725 774 results << keystr;
726 775 }
727 776 break;
728 777 case Function:
729 778 if (value->ob_type == &PyFunction_Type ||
730 779 value->ob_type == &PyMethod_Type
731 780 ) {
732 781 results << keystr;
733 782 }
734 783 break;
735 784 case Module:
736 785 if (value->ob_type == &PyModule_Type) {
737 786 results << keystr;
738 787 }
739 788 break;
740 789 default:
741 790 std::cerr << "PythonQt: introspection: unknown case" << ", in " << __FILE__ << ":" << __LINE__ << std::endl;
742 791 }
743 792 }
744 793 Py_DECREF(value);
745 794 }
746 795 Py_DECREF(keys);
747 796 }
748 797 }
749 798 }
750 799 return results;
751 800 }
752 801
753 802 QVariant PythonQt::call(PyObject* module, const QString& name, const QVariantList& args)
754 803 {
755 804 QVariant r;
756 805
757 806 PythonQtObjectPtr callable = lookupCallable(module, name);
758 807 if (callable) {
759 808 PythonQtObjectPtr pargs;
760 809 int count = args.size();
761 810 if (count>0) {
762 811 pargs.setNewRef(PyTuple_New(count));
763 812 }
764 813 bool err = false;
765 814 // transform QVariants to Python
766 815 for (int i = 0; i < count; i++) {
767 816 PyObject* arg = PythonQtConv::QVariantToPyObject(args.at(i));
768 817 if (arg) {
769 818 // steals reference, no unref
770 819 PyTuple_SetItem(pargs, i,arg);
771 820 } else {
772 821 err = true;
773 822 break;
774 823 }
775 824 }
776 825
777 826 if (!err) {
778 827 PyErr_Clear();
779 828 PythonQtObjectPtr result;
780 829 result.setNewRef(PyObject_CallObject(callable, pargs));
781 830 if (result) {
782 831 // ok
783 832 r = PythonQtConv::PyObjToQVariant(result);
784 833 } else {
785 834 PythonQt::self()->handleError();
786 835 }
787 836 }
788 837 }
789 838 return r;
790 839 }
791 840
792 841 void PythonQt::addInstanceDecorators(QObject* o)
793 842 {
794 843 _p->addDecorators(o, PythonQtPrivate::InstanceDecorator);
795 844 }
796 845
797 846 void PythonQt::addClassDecorators(QObject* o)
798 847 {
799 848 _p->addDecorators(o, PythonQtPrivate::StaticDecorator | PythonQtPrivate::ConstructorDecorator | PythonQtPrivate::DestructorDecorator);
800 849 }
801 850
802 851 void PythonQt::addDecorators(QObject* o)
803 852 {
804 853 _p->addDecorators(o, PythonQtPrivate::AllDecorators);
805 854 }
806 855
807 856 void PythonQt::registerQObjectClassNames(const QStringList& names)
808 857 {
809 858 _p->registerQObjectClassNames(names);
810 859 }
811 860
812 861 void PythonQt::setImporter(PythonQtImportFileInterface* importInterface)
813 862 {
814 863 PythonQtImport::init();
815 864 _p->_importInterface = importInterface;
816 865 }
817 866
818 867 void PythonQt::setImporterIgnorePaths(const QStringList& paths)
819 868 {
820 869 _p->_importIgnorePaths = paths;
821 870 }
822 871
823 872 const QStringList& PythonQt::getImporterIgnorePaths()
824 873 {
825 874 return _p->_importIgnorePaths;
826 875 }
827 876
828 877 void PythonQt::addWrapperFactory(PythonQtCppWrapperFactory* factory)
829 878 {
830 879 _p->_cppWrapperFactories.append(factory);
831 880 }
832 881
833 882 //---------------------------------------------------------------------------------------------------
834 883 PythonQtPrivate::PythonQtPrivate()
835 884 {
836 885 _importInterface = NULL;
837 886 _defaultImporter = new PythonQtQFileImporter;
838 887 _noLongerWrappedCB = NULL;
839 888 _wrappedCB = NULL;
840 889 _currentClassInfoForClassWrapperCreation = NULL;
841 890 }
842 891
843 892 PythonQtClassInfo* PythonQtPrivate::currentClassInfoForClassWrapperCreation()
844 893 {
845 894 PythonQtClassInfo* info = _currentClassInfoForClassWrapperCreation;
846 895 _currentClassInfoForClassWrapperCreation = NULL;
847 896 return info;
848 897 }
849 898
850 899 void PythonQtPrivate::addDecorators(QObject* o, int decoTypes)
851 900 {
852 901 o->setParent(this);
853 902 int numMethods = o->metaObject()->methodCount();
854 903 for (int i = 0; i < numMethods; i++) {
855 904 QMetaMethod m = o->metaObject()->method(i);
856 905 if ((m.methodType() == QMetaMethod::Method ||
857 906 m.methodType() == QMetaMethod::Slot) && m.access() == QMetaMethod::Public) {
858 907 const PythonQtMethodInfo* info = PythonQtMethodInfo::getCachedMethodInfo(m);
859 908 if (qstrncmp(m.signature(), "new_", 4)==0) {
860 909 if ((decoTypes & ConstructorDecorator) == 0) continue;
861 910 // either it returns a * or a QVariant and the name starts with "new_"
862 911 bool isVariantReturn = info->parameters().at(0).typeId == PythonQtMethodInfo::Variant;
863 912 if ((info->parameters().at(0).isPointer || isVariantReturn)) {
864 913 QByteArray signature = m.signature();
865 914 QByteArray nameOfClass = signature.mid(4, signature.indexOf('(')-4);
866 915 PythonQtClassInfo* classInfo = lookupClassInfoAndCreateIfNotPresent(nameOfClass);
867 916 PythonQtSlotInfo* newSlot = new PythonQtSlotInfo(m, i, o, PythonQtSlotInfo::ClassDecorator);
868 917 classInfo->addConstructor(newSlot);
869 918 }
870 919 } else if (qstrncmp(m.signature(), "delete_", 7)==0) {
871 920 if ((decoTypes & DestructorDecorator) == 0) continue;
872 921 QByteArray signature = m.signature();
873 922 QByteArray nameOfClass = signature.mid(7, signature.indexOf('(')-7);
874 923 PythonQtClassInfo* classInfo = lookupClassInfoAndCreateIfNotPresent(nameOfClass);
875 924 PythonQtSlotInfo* newSlot = new PythonQtSlotInfo(m, i, o, PythonQtSlotInfo::ClassDecorator);
876 925 classInfo->setDestructor(newSlot);
877 926 } else if (qstrncmp(m.signature(), "static_", 7)==0) {
878 927 if ((decoTypes & StaticDecorator) == 0) continue;
879 928 QByteArray signature = m.signature();
880 929 QByteArray nameOfClass = signature.mid(signature.indexOf('_')+1);
881 930 nameOfClass = nameOfClass.mid(0, nameOfClass.indexOf('_'));
882 931 PythonQtClassInfo* classInfo = lookupClassInfoAndCreateIfNotPresent(nameOfClass);
883 932 PythonQtSlotInfo* newSlot = new PythonQtSlotInfo(m, i, o, PythonQtSlotInfo::ClassDecorator);
884 933 classInfo->addDecoratorSlot(newSlot);
885 934 } else {
886 935 if ((decoTypes & InstanceDecorator) == 0) continue;
887 936 if (info->parameters().count()>1) {
888 937 PythonQtMethodInfo::ParameterInfo p = info->parameters().at(1);
889 938 if (p.isPointer) {
890 939 PythonQtClassInfo* classInfo = lookupClassInfoAndCreateIfNotPresent(p.name);
891 940 PythonQtSlotInfo* newSlot = new PythonQtSlotInfo(m, i, o, PythonQtSlotInfo::InstanceDecorator);
892 941 classInfo->addDecoratorSlot(newSlot);
893 942 }
894 943 }
895 944 }
896 945 }
897 946 }
898 947 }
899 948
900 949 void PythonQtPrivate::registerQObjectClassNames(const QStringList& names)
901 950 {
902 951 foreach(QString name, names) {
903 952 _knownQObjectClassNames.insert(name.toLatin1(), true);
904 953 }
905 954 }
906 955
907 956 void PythonQtPrivate::removeSignalEmitter(QObject* obj)
908 957 {
909 958 _signalReceivers.remove(obj);
910 959 }
911 960
912 961 bool PythonQt::handleError()
913 962 {
914 963 bool flag = false;
915 964 if (PyErr_Occurred()) {
916 965
917 966 // currently we just print the error and the stderr handler parses the errors
918 967 PyErr_Print();
919 968
920 969 /*
921 970 // EXTRA: the format of the ptype and ptraceback is not really documented, so I use PyErr_Print() above
922 971 PyObject *ptype;
923 972 PyObject *pvalue;
924 973 PyObject *ptraceback;
925 974 PyErr_Fetch( &ptype, &pvalue, &ptraceback);
926 975
927 976 Py_XDECREF(ptype);
928 977 Py_XDECREF(pvalue);
929 978 Py_XDECREF(ptraceback);
930 979 */
931 980 PyErr_Clear();
932 981 flag = true;
933 982 }
934 983 return flag;
935 984 }
936 985
937 986 void PythonQt::addSysPath(const QString& path)
938 987 {
939 988 PythonQtObjectPtr sys;
940 989 sys.setNewRef(PyImport_ImportModule("sys"));
941 990 PythonQtObjectPtr obj = lookupObject(sys, "path");
942 991 PyList_Insert(obj, 0, PythonQtConv::QStringToPyObject(path));
943 992 }
944 993
945 994 void PythonQt::overwriteSysPath(const QStringList& paths)
946 995 {
947 996 PythonQtObjectPtr sys;
948 997 sys.setNewRef(PyImport_ImportModule("sys"));
949 998 PyModule_AddObject(sys, "path", PythonQtConv::QStringListToPyList(paths));
950 999 }
951 1000
952 1001 void PythonQt::setModuleImportPath(PyObject* module, const QStringList& paths)
953 1002 {
954 1003 PyModule_AddObject(module, "__path__", PythonQtConv::QStringListToPyList(paths));
955 1004 }
956 1005
957 1006 void PythonQt::stdOutRedirectCB(const QString& str)
958 1007 {
959 1008 emit PythonQt::self()->pythonStdOut(str);
960 1009 }
961 1010
962 1011 void PythonQt::stdErrRedirectCB(const QString& str)
963 1012 {
964 1013 emit PythonQt::self()->pythonStdErr(str);
965 1014 }
966 1015
967 1016 void PythonQt::setQObjectWrappedCallback(PythonQtQObjectWrappedCB* cb)
968 1017 {
969 1018 _p->_wrappedCB = cb;
970 1019 }
971 1020
972 1021 void PythonQt::setQObjectNoLongerWrappedCallback(PythonQtQObjectNoLongerWrappedCB* cb)
973 1022 {
974 1023 _p->_noLongerWrappedCB = cb;
975 1024 }
976 1025
977 1026
978 1027
979 1028 static PyMethodDef PythonQtMethods[] = {
980 1029 {NULL, NULL, 0, NULL}
981 1030 };
982 1031
983 1032 void PythonQt::initPythonQtModule(bool redirectStdOut)
984 1033 {
985 1034 _p->_pythonQtModule = Py_InitModule("PythonQt", PythonQtMethods);
986 1035
987 1036 if (redirectStdOut) {
988 1037 PythonQtObjectPtr sys;
989 1038 PythonQtObjectPtr out;
990 1039 PythonQtObjectPtr err;
991 1040 sys.setNewRef(PyImport_ImportModule("sys"));
992 1041 // create a redirection object for stdout and stderr
993 1042 out = PythonQtStdOutRedirectType.tp_new(&PythonQtStdOutRedirectType,NULL, NULL);
994 1043 ((PythonQtStdOutRedirect*)out.object())->_cb = stdOutRedirectCB;
995 1044 err = PythonQtStdOutRedirectType.tp_new(&PythonQtStdOutRedirectType,NULL, NULL);
996 1045 ((PythonQtStdOutRedirect*)err.object())->_cb = stdErrRedirectCB;
997 1046 // replace the built in file objects with our own objects
998 1047 PyModule_AddObject(sys, "stdout", out);
999 1048 PyModule_AddObject(sys, "stderr", err);
1000 1049 }
1001 1050 }
1002 1051
1003 1052 void PythonQt::registerCPPClass(const char* typeName, const char* parentTypeName, const char* package, PythonQtQObjectCreatorFunctionCB* wrapperCreator, PythonQtShellSetInstanceWrapperCB* shell)
1004 1053 {
1005 1054 _p->registerCPPClass(typeName, parentTypeName, package, wrapperCreator, shell);
1006 1055 }
1007 1056
1008 1057
1009 1058 PythonQtClassInfo* PythonQtPrivate::lookupClassInfoAndCreateIfNotPresent(const char* typeName)
1010 1059 {
1011 1060 PythonQtClassInfo* info = _knownClassInfos.value(typeName);
1012 1061 if (!info) {
1013 1062 info = new PythonQtClassInfo();
1014 1063 info->setupCPPObject(typeName);
1015 1064 _knownClassInfos.insert(typeName, info);
1016 1065 }
1017 1066 return info;
1018 1067 }
1019 1068
1020 1069 bool PythonQt::addParentClass(const char* typeName, const char* parentTypeName, int upcastingOffset)
1021 1070 {
1022 1071 return _p->addParentClass(typeName, parentTypeName, upcastingOffset);
1023 1072 }
1024 1073
1025 1074 bool PythonQtPrivate::addParentClass(const char* typeName, const char* parentTypeName, int upcastingOffset)
1026 1075 {
1027 1076 PythonQtClassInfo* info = _knownClassInfos.value(typeName);
1028 1077 if (info) {
1029 1078 PythonQtClassInfo* parentInfo = lookupClassInfoAndCreateIfNotPresent(parentTypeName);
1030 1079 info->addParentClass(PythonQtClassInfo::ParentClassInfo(parentInfo, upcastingOffset));
1031 1080 return true;
1032 1081 } else {
1033 1082 return false;
1034 1083 }
1035 1084 }
1036 1085
1037 1086 void PythonQtPrivate::registerCPPClass(const char* typeName, const char* parentTypeName, const char* package, PythonQtQObjectCreatorFunctionCB* wrapperCreator, PythonQtShellSetInstanceWrapperCB* shell)
1038 1087 {
1039 1088 PythonQtClassInfo* info = lookupClassInfoAndCreateIfNotPresent(typeName);
1040 1089 if (!info->pythonQtClassWrapper()) {
1041 1090 info->setupCPPObject(typeName);
1042 1091 createPythonQtClassWrapper(info, package);
1043 1092 }
1044 1093 if (parentTypeName && strcmp(parentTypeName,"")!=0) {
1045 1094 addParentClass(typeName, parentTypeName, 0);
1046 1095 }
1047 1096 if (wrapperCreator) {
1048 1097 info->setDecoratorProvider(wrapperCreator);
1049 1098 }
1050 1099 if (shell) {
1051 1100 info->setShellSetInstanceWrapperCB(shell);
1052 1101 }
1053 1102 }
1054 1103
1055 1104 PyObject* PythonQtPrivate::packageByName(const char* name)
1056 1105 {
1057 1106 if (name==NULL || name[0]==0) {
1058 1107 return _pythonQtModule;
1059 1108 }
1060 1109 PyObject* v = _packages.value(name);
1061 1110 if (!v) {
1062 1111 v = PyImport_AddModule((QByteArray("PythonQt.") + name).constData());
1063 1112 _packages.insert(name, v);
1064 1113 // AddObject steals the reference, so increment it!
1065 1114 Py_INCREF(v);
1066 1115 PyModule_AddObject(_pythonQtModule, name, v);
1067 1116 }
1068 1117 return v;
1069 1118 }
1070 1119
1071 1120
1072 1121 PyObject* PythonQt::helpCalled(PythonQtClassInfo* info)
1073 1122 {
1074 1123 if (_p->_initFlags & ExternalHelp) {
1075 1124 emit pythonHelpRequest(QByteArray(info->className()));
1076 1125 return Py_BuildValue("");
1077 1126 } else {
1078 1127 return PyString_FromString(info->help().toLatin1().data());
1079 1128 }
1080 1129 }
1081 1130
1082 1131 void PythonQtPrivate::removeWrapperPointer(void* obj)
1083 1132 {
1084 1133 _wrappedObjects.remove(obj);
1085 1134 }
1086 1135
1087 1136 void PythonQtPrivate::addWrapperPointer(void* obj, PythonQtInstanceWrapper* wrapper)
1088 1137 {
1089 1138 _wrappedObjects.insert(obj, wrapper);
1090 1139 }
1091 1140
1092 1141 PythonQtInstanceWrapper* PythonQtPrivate::findWrapperAndRemoveUnused(void* obj)
1093 1142 {
1094 1143 PythonQtInstanceWrapper* wrap = _wrappedObjects.value(obj);
1095 1144 if (wrap && !wrap->_wrappedPtr && wrap->_obj == NULL) {
1096 1145 // this is a wrapper whose QObject was already removed due to destruction
1097 1146 // so the obj pointer has to be a new QObject with the same address...
1098 1147 // we remove the old one and set the copy to NULL
1099 1148 wrap->_objPointerCopy = NULL;
1100 1149 removeWrapperPointer(obj);
1101 1150 wrap = NULL;
1102 1151 }
1103 1152 return wrap;
1104 1153 }
1105 1154
1106 1155 PythonQtObjectPtr PythonQtPrivate::createModule(const QString& name, PyObject* pycode)
1107 1156 {
1108 1157 PythonQtObjectPtr result;
1109 1158 if (pycode) {
1110 1159 result.setNewRef(PyImport_ExecCodeModule((char*)name.toLatin1().data(), pycode));
1111 1160 } else {
1112 1161 PythonQt::self()->handleError();
1113 1162 }
1114 1163 return result;
1115 1164 }
@@ -1,511 +1,511
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 68 typedef void PythonQtQObjectWrappedCB(QObject* object);
69 69 typedef void PythonQtQObjectNoLongerWrappedCB(QObject* object);
70 70
71 71 typedef void PythonQtShellSetInstanceWrapperCB(void* object, PythonQtInstanceWrapper* wrapper);
72 72
73 73 template<class T> void PythonQtSetInstanceWrapperOnShell(void* object, PythonQtInstanceWrapper* wrapper) { ((T*)object)->_wrapper = wrapper; };
74 74
75 75 //! returns the offset that needs to be added to upcast an object of type T1 to T2
76 76 template<class T1, class T2> int PythonQtUpcastingOffset() {
77 77 return (((char*)(static_cast<T2*>(reinterpret_cast<T1*>(0x100)))) - ((char*)reinterpret_cast<T1*>(0x100)));
78 78 }
79 79
80 80 //! callback to create a QObject lazily
81 81 typedef QObject* PythonQtQObjectCreatorFunctionCB();
82 82
83 83 //! helper template to create a derived QObject class
84 84 template<class T> QObject* PythonQtCreateObject() { return new T(); };
85 85
86 86 //! the main interface to the Python Qt binding, realized as a singleton
87 87 class PYTHONQT_EXPORT PythonQt : public QObject {
88 88
89 89 Q_OBJECT
90 90
91 91 public:
92 92 enum InitFlags {
93 93 RedirectStdOut = 1, //!<< sets if the std out/err is redirected to pythonStdOut() and pythonStdErr() signals
94 94 IgnoreSiteModule = 2, //!<< sets if Python should ignore the site module
95 95 ExternalHelp = 4 //!<< sets if help() calls on PythonQt modules are forwarded to the pythonHelpRequest() signal
96 96 };
97 97
98 98 //! initialize the python qt binding (flags are a or combination of InitFlags)
99 99 static void init(int flags = IgnoreSiteModule | RedirectStdOut);
100 100
101 101 //! cleanup
102 102 static void cleanup();
103 103
104 104 //! get the singleton instance
105 105 static PythonQt* self() { return _self; }
106 106
107 107 //-----------------------------------------------------------------------------
108 108 // Public API:
109 109
110 110 //! defines the object types for introspection
111 111 enum ObjectType {
112 112 Class,
113 113 Function,
114 114 Variable,
115 115 Module,
116 116 Anything,
117 117 CallOverloads
118 118 };
119 119
120 120 //! overwrite the python sys path (call this directly after PythonQt::init() if you want to change the std python sys path)
121 121 void overwriteSysPath(const QStringList& paths);
122 122
123 123 //! prepend a path to sys.path to allow importing from it
124 124 void addSysPath(const QString& path);
125 125
126 126 //! sets the __path__ list of a module to the given list (important for local imports)
127 127 void setModuleImportPath(PyObject* module, const QStringList& paths);
128 128
129 129 //! get the __main__ module of python
130 130 PythonQtObjectPtr getMainModule();
131 131
132 132 //! registers a QObject derived class to PythonQt (this is implicitly called by addObject as well)
133 133 /* Since Qt4 does not offer a way to detect if a given classname is derived from QObject and thus has a QMetaObject,
134 134 you MUST register all your QObject derived classes here when you want them to be detected in signal and slot calls */
135 135 void registerClass(const QMetaObject* metaobject, const char* package = NULL, PythonQtQObjectCreatorFunctionCB* wrapperCreator = NULL, PythonQtShellSetInstanceWrapperCB* shell = NULL);
136 136
137 137 //! add a wrapper object for the given QMetaType typeName, also does an addClassDecorators() to add constructors for variants
138 138 //! (ownership of wrapper is passed to PythonQt)
139 139 /*! Make sure that you have done a qRegisterMetaType first, if typeName is a user type!
140 140
141 141 This will add a wrapper object that is used to make calls to the given classname \c typeName.
142 142 All slots that take a pointer to typeName as the first argument will be callable from Python on
143 143 a variant object that contains such a type.
144 144 */
145 145 void registerCPPClass(const char* typeName, const char* parentTypeName = NULL, const char* package = NULL, PythonQtQObjectCreatorFunctionCB* wrapperCreator = NULL, PythonQtShellSetInstanceWrapperCB* shell = NULL);
146 146
147 147 //! as an alternative to registerClass, you can tell PythonQt the names of QObject derived classes
148 148 //! and it will register the classes when it first sees a pointer to such a derived class
149 149 void registerQObjectClassNames(const QStringList& names);
150 150
151 151 //! add a parent class relation to the \c given typeName, the upcastingOffset is needed for multiple inheritance
152 152 //! and can be calculated using PythonQtUpcastingOffset<type,parentType>(), which also verifies that
153 153 //! type is really derived from parentType.
154 154 //! Returns false if the typeName was not yet registered.
155 155 bool addParentClass(const char* typeName, const char* parentTypeName, int upcastingOffset=0);
156 156
157 157 //! parses the given file and returns the python code object, this can then be used to call evalCode()
158 158 PythonQtObjectPtr parseFile(const QString& filename);
159 159
160 160 //! evaluates the given code and returns the result value (use Py_Compile etc. to create pycode from string)
161 161 //! If pycode is NULL, a python error is printed.
162 QVariant evalCode(PyObject* module, PyObject* pycode);
162 QVariant evalCode(PyObject* object, PyObject* pycode);
163 163
164 164 //! evaluates the given script code and returns the result value
165 QVariant evalScript(PyObject* module, const QString& script, int start = Py_file_input);
165 QVariant evalScript(PyObject* object, const QString& script, int start = Py_file_input);
166 166
167 167 //! evaluates the given script code from file
168 void evalFile(PyObject* module, const QString& filename);
168 void evalFile(PyObject* object, const QString& filename);
169 169
170 170 //! creates the new module \c name and evaluates the given file in the context of that module
171 171 //! If the \c script is empty, the module contains no initial code. You can use evalScript/evalCode to add code
172 172 //! to a module later on.
173 173 //! The user needs to make sure that the \c name is unique in the python module dictionary.
174 174 PythonQtObjectPtr createModuleFromFile(const QString& name, const QString& filename);
175 175
176 176 //! creates the new module \c name and evaluates the given script in the context of that module.
177 177 //! If the \c script is empty, the module contains no initial code. You can use evalScript/evalCode to add code
178 178 //! to a module later on.
179 179 //! The user needs to make sure that the \c name is unique in the python module dictionary.
180 180 PythonQtObjectPtr createModuleFromScript(const QString& name, const QString& script = QString());
181 181
182 182 //! create a uniquely named module, you can use evalFile or evalScript to populate the module with
183 183 //! script code
184 184 PythonQtObjectPtr createUniqueModule();
185 185
186 186 //@{ Signal handlers
187 187
188 188 //! add a signal handler to the given \c signal of \c obj and connect it to a callable \c objectname in module
189 189 bool addSignalHandler(QObject* obj, const char* signal, PyObject* module, const QString& objectname);
190 190
191 191 //! remove a signal handler from the given \c signal of \c obj
192 192 bool removeSignalHandler(QObject* obj, const char* signal, PyObject* module, const QString& objectname);
193 193
194 194 //! add a signal handler to the given \c signal of \c obj and connect it to a callable \c receiver
195 195 bool addSignalHandler(QObject* obj, const char* signal, PyObject* receiver);
196 196
197 197 //! remove a signal handler from the given \c signal of \c obj
198 198 bool removeSignalHandler(QObject* obj, const char* signal, PyObject* receiver);
199 199
200 200 //@}
201 201
202 202 //@{ Variable access
203 203
204 //! add the given \c object to the \c module as a variable with \c name (it can be removed via clearVariable)
205 void addObject(PyObject* module, const QString& name, QObject* object);
204 //! add the given \c qObject to the python \c object as a variable with \c name (it can be removed via clearVariable)
205 void addObject(PyObject* object, const QString& name, QObject* qObject);
206 206
207 //! add the given variable to the module
208 void addVariable(PyObject* module, const QString& name, const QVariant& v);
207 //! add the given variable to the object
208 void addVariable(PyObject* object, const QString& name, const QVariant& v);
209 209
210 210 //! remove the given variable
211 211 void removeVariable(PyObject* module, const QString& name);
212 212
213 //! get the variable with the \c name of the \c module, returns an invalid QVariant on error
214 QVariant getVariable(PyObject* module, const QString& name);
213 //! get the variable with the \c name of the \c object, returns an invalid QVariant on error
214 QVariant getVariable(PyObject* object, const QString& name);
215 215
216 //! read vars etc. in scope of a module, optional looking inside of an object \c objectname
217 QStringList introspection(PyObject* module, const QString& objectname, ObjectType type);
216 //! read vars etc. in scope of an \c object, optional looking inside of an object \c objectname
217 QStringList introspection(PyObject* object, const QString& objectname, ObjectType type);
218 218
219 219 //! returns the found callable object or NULL
220 220 //! @return new reference
221 PythonQtObjectPtr lookupCallable(PyObject* module, const QString& name);
221 PythonQtObjectPtr lookupCallable(PyObject* object, const QString& name);
222 222
223 223 //@}
224 224
225 225 //@{ Calling of python callables
226 226
227 227 //! call the given python method, returns the result converted to a QVariant
228 228 QVariant call(PyObject* module, const QString& callable, const QVariantList& args = QVariantList());
229 229
230 230 //@}
231 231
232 232 //@{ Decorations, constructors, wrappers...
233 233
234 234
235 235 //! add an object whose slots will be used as decorator slots for
236 236 //! other QObjects or CPP classes. The slots need to follow the
237 237 //! convention that the first argument is a pointer to the wrapped object.
238 238 //! (ownership is passed to PythonQt)
239 239 /*!
240 240 Example:
241 241
242 242 A slot with the signature
243 243
244 244 \code
245 245 bool doSomething(QWidget* w, int a)
246 246 \endcode
247 247
248 248 will extend QWidget instances (and derived classes) with a "bool doSomething(int a)" slot
249 249 that will be called with the concrete instance as first argument.
250 250 So in Python you can now e.g. call
251 251
252 252 \code
253 253 someWidget.doSomething(12)
254 254 \endcode
255 255
256 256 without QWidget really having this method. This allows to easily make normal methods
257 257 of Qt classes callable by forwarding them with such decorator slots
258 258 or to make CPP classes (which are not derived from QObject) callable from Python.
259 259 */
260 260 void addInstanceDecorators(QObject* o);
261 261
262 262 //! add an object whose slots will be used as decorator slots for
263 263 //! class objects (ownership is passed to PythonQt)
264 264 /*!
265 265 The slots need to follow the following convention:
266 266 - SomeClass* new_SomeClass(...)
267 267 - QVariant new_SomeClass(...)
268 268 - void delete_SomeClass(SomeClass*)
269 269 - ... static_SomeClass_someName(...)
270 270
271 271 This will add:
272 272 - a constructor
273 273 - a constructor which generates a QVariant
274 274 - a destructor (only useful for CPP objects)
275 275 - a static decorator slot which will be available on the MetaObject (visible in PythonQt module)
276 276
277 277 */
278 278 void addClassDecorators(QObject* o);
279 279
280 280 //! this will add the object both as class and instance decorator (ownership is passed to PythonQt)
281 281 void addDecorators(QObject* o);
282 282
283 283 //! add the given factory to PythonQt (ownership stays with caller)
284 284 void addWrapperFactory(PythonQtCppWrapperFactory* factory);
285 285
286 286 //@}
287 287
288 288 //@{ Custom importer (to replace internal import implementation of python)
289 289
290 290 //! replace the internal import implementation and use the supplied interface to load files (both py and pyc files)
291 291 //! (this method should be called directly after initialization of init() and before calling overwriteSysPath().
292 292 //! On the first call to this method, it will install a generic PythonQt importer in Pythons "path_hooks".
293 293 //! This is not reversible, so even setting setImporter(NULL) afterwards will
294 294 //! keep the custom PythonQt importer with a QFile default import interface.
295 295 //! Subsequent python import calls will make use of the passed importInterface
296 296 //! which forwards all import calls to the given \c importInterface.
297 297 //! Passing NULL will install a default QFile importer.
298 298 //! (\c importInterface ownership stays with caller)
299 299 void setImporter(PythonQtImportFileInterface* importInterface);
300 300
301 301 //! this installs the default QFile importer (which effectively does a setImporter(NULL))
302 302 //! (without calling setImporter or installDefaultImporter at least once, the default python import
303 303 //! mechanism is in place)
304 304 //! the default importer allows to import files from anywhere QFile can read from,
305 305 //! including the Qt resource system using ":". Keep in mind that you need to extend
306 306 //! "sys.path" with ":" to be able to import from the Qt resources.
307 307 void installDefaultImporter() { setImporter(NULL); }
308 308
309 309 //! set paths that the importer should ignore
310 310 void setImporterIgnorePaths(const QStringList& paths);
311 311
312 312 //! get paths that the importer should ignore
313 313 const QStringList& getImporterIgnorePaths();
314 314
315 315 //@}
316 316
317 317 //! get access to internal data (should not be used on the public API, but is used by some C functions)
318 318 static PythonQtPrivate* priv() { return _self->_p; }
319 319
320 320 //! get access to the file importer (if set)
321 321 static PythonQtImportFileInterface* importInterface();
322 322
323 323 //! handle a python error, call this when a python function fails. If no error occurred, it returns false.
324 324 //! The error is currently just output to the python stderr, future version might implement better trace printing
325 325 bool handleError();
326 326
327 327 //! set a callback that is called when a QObject with parent == NULL is wrapped by pythonqt
328 328 void setQObjectWrappedCallback(PythonQtQObjectWrappedCB* cb);
329 329 //! set a callback that is called when a QObject with parent == NULL is no longer wrapped by pythonqt
330 330 void setQObjectNoLongerWrappedCallback(PythonQtQObjectNoLongerWrappedCB* cb);
331 331
332 332 //! call the callback if it is set
333 333 static void qObjectNoLongerWrappedCB(QObject* o);
334 334
335 335 signals:
336 336 //! emitted when python outputs something to stdout (and redirection is turned on)
337 337 void pythonStdOut(const QString& str);
338 338 //! emitted when python outputs something to stderr (and redirection is turned on)
339 339 void pythonStdErr(const QString& str);
340 340
341 341 //! emitted when help() is called on a PythonQt object and \c ExternalHelp is enabled
342 342 void pythonHelpRequest(const QByteArray& cppClassName);
343 343
344 344
345 345 public:
346 346 //! called by internal help methods
347 347 PyObject* helpCalled(PythonQtClassInfo* info);
348 348
349 349 //! returns the found object or NULL
350 350 //! @return new reference
351 351 PythonQtObjectPtr lookupObject(PyObject* module, const QString& name);
352 352
353 353 private:
354 354 void initPythonQtModule(bool redirectStdOut);
355 355
356 356 //! callback for stdout redirection, emits pythonStdOut signal
357 357 static void stdOutRedirectCB(const QString& str);
358 358 //! callback for stderr redirection, emits pythonStdErr signal
359 359 static void stdErrRedirectCB(const QString& str);
360 360
361 361 //! get (and create if not available) the signal receiver of that QObject, signal receiver is made child of the passed \c obj
362 362 PythonQtSignalReceiver* getSignalReceiver(QObject* obj);
363 363
364 364 PythonQt(int flags);
365 365 ~PythonQt();
366 366
367 367 static PythonQt* _self;
368 368 static int _uniqueModuleCount;
369 369
370 370 PythonQtPrivate* _p;
371 371
372 372 };
373 373
374 374 //! internal PythonQt details
375 375 class PYTHONQT_EXPORT PythonQtPrivate : public QObject {
376 376
377 377 Q_OBJECT
378 378
379 379 public:
380 380 PythonQtPrivate();
381 381 ~PythonQtPrivate();
382 382
383 383 enum DecoratorTypes {
384 384 StaticDecorator = 1,
385 385 ConstructorDecorator = 2,
386 386 DestructorDecorator = 4,
387 387 InstanceDecorator = 8,
388 388 AllDecorators = 0xffff
389 389 };
390 390
391 391 //! returns if the id is the id for PythonQtObjectPtr
392 392 bool isPythonQtObjectPtrMetaId(int id) { return _PythonQtObjectPtr_metaId == id; }
393 393
394 394 //! add the wrapper pointer (for reuse if the same obj appears while wrapper still exists)
395 395 void addWrapperPointer(void* obj, PythonQtInstanceWrapper* wrapper);
396 396 //! remove the wrapper ptr again
397 397 void removeWrapperPointer(void* obj);
398 398
399 399 //! add parent class relation
400 400 bool addParentClass(const char* typeName, const char* parentTypeName, int upcastingOffset);
401 401
402 402 //! lookup existing classinfo and return new if not yet present
403 403 PythonQtClassInfo* lookupClassInfoAndCreateIfNotPresent(const char* typeName);
404 404
405 405 //! called when a signal emitting QObject is destroyed to remove the signal handler from the hash map
406 406 void removeSignalEmitter(QObject* obj);
407 407
408 408 //! wrap the given QObject into a Python object (or return existing wrapper!)
409 409 PyObject* wrapQObject(QObject* obj);
410 410
411 411 //! 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 412 PyObject* wrapPtr(void* ptr, const QByteArray& name);
413 413
414 414 //! registers a QObject derived class to PythonQt (this is implicitly called by addObject as well)
415 415 /* Since Qt4 does not offer a way to detect if a given classname is derived from QObject and thus has a QMetaObject,
416 416 you MUST register all your QObject derived classes here when you want them to be detected in signal and slot calls */
417 417 void registerClass(const QMetaObject* metaobject, const char* package = NULL, PythonQtQObjectCreatorFunctionCB* wrapperCreator = NULL, PythonQtShellSetInstanceWrapperCB* shell = NULL);
418 418
419 419 //! add a wrapper object for the given QMetaType typeName, also does an addClassDecorators() to add constructors for variants
420 420 //! (ownership of wrapper is passed to PythonQt)
421 421 /*! Make sure that you have done a qRegisterMetaType first, if typeName is a user type!
422 422
423 423 This will add a wrapper object that is used to make calls to the given classname \c typeName.
424 424 All slots that take a pointer to typeName as the first argument will be callable from Python on
425 425 a variant object that contains such a type.
426 426 */
427 427 void registerCPPClass(const char* typeName, const char* parentTypeName = NULL, const char* package = NULL, PythonQtQObjectCreatorFunctionCB* wrapperCreator = NULL, PythonQtShellSetInstanceWrapperCB* shell = NULL);
428 428
429 429 //! as an alternative to registerClass, you can tell PythonQt the names of QObject derived classes
430 430 //! and it will register the classes when it first sees a pointer to such a derived class
431 431 void registerQObjectClassNames(const QStringList& names);
432 432
433 433 //! add a decorator object
434 434 void addDecorators(QObject* o, int decoTypes);
435 435
436 436 //! check if the enum is either part of the \c meta class or contains a scope and is
437 437 //! an enum of another known metaobject (and as last resort, of the Qt namespace)
438 438 bool isEnumType(const QMetaObject* meta, const QByteArray& name);
439 439
440 440 //! helper method that creates a PythonQtClassWrapper object
441 441 PythonQtClassWrapper* createNewPythonQtClassWrapper(PythonQtClassInfo* info, const char* package = NULL);
442 442
443 443 //! helper method that creates a PythonQtInstanceWrapper object and registers it in the object map
444 444 PythonQtInstanceWrapper* createNewPythonQtInstanceWrapper(QObject* obj, PythonQtClassInfo* info, void* wrappedPtr = NULL);
445 445
446 446 //! get the class info for a meta object (if available)
447 447 PythonQtClassInfo* getClassInfo(const QMetaObject* meta) { return _knownClassInfos.value(meta->className()); }
448 448
449 449 //! get the class info for a meta object (if available)
450 450 PythonQtClassInfo* getClassInfo(const QByteArray& className) { return _knownClassInfos.value(className); }
451 451
452 452 //! creates the new module from the given pycode
453 453 PythonQtObjectPtr createModule(const QString& name, PyObject* pycode);
454 454
455 455 //! get the current class info (for the next PythonQtClassWrapper that is created) and reset it to NULL again
456 456 PythonQtClassInfo* currentClassInfoForClassWrapperCreation();
457 457
458 458 //! the dummy tuple (which is empty and may be used to detected that a wrapper is called from internal wrapper creation
459 459 static PyObject* dummyTuple();
460 460
461 461 private:
462 462
463 463 //! create a new pythonqt class wrapper and place it in the pythonqt module
464 464 void createPythonQtClassWrapper(PythonQtClassInfo* info, const char* package);
465 465
466 466 //! get/create new package module (the returned object is a borrowed reference)
467 467 PyObject* packageByName(const char* name);
468 468
469 469 //! get the wrapper for a given pointer (and remove a wrapper of an already destroyed qobject)
470 470 PythonQtInstanceWrapper* findWrapperAndRemoveUnused(void* obj);
471 471
472 472 //! stores pointer to PyObject mapping of wrapped QObjects AND C++ objects
473 473 QHash<void* , PythonQtInstanceWrapper *> _wrappedObjects;
474 474
475 475 //! stores the meta info of known Qt classes
476 476 QHash<QByteArray, PythonQtClassInfo *> _knownClassInfos;
477 477
478 478 //! names of qobject derived classes that can be casted to qobject savely
479 479 QHash<QByteArray, bool> _knownQObjectClassNames;
480 480
481 481 //! stores signal receivers for QObjects
482 482 QHash<QObject* , PythonQtSignalReceiver *> _signalReceivers;
483 483
484 484 //! the PythonQt python module
485 485 PythonQtObjectPtr _pythonQtModule;
486 486
487 487 //! the importer interface (if set)
488 488 PythonQtImportFileInterface* _importInterface;
489 489
490 490 //! the default importer
491 491 PythonQtQFileImporter* _defaultImporter;
492 492
493 493 PythonQtQObjectNoLongerWrappedCB* _noLongerWrappedCB;
494 494 PythonQtQObjectWrappedCB* _wrappedCB;
495 495
496 496 QStringList _importIgnorePaths;
497 497
498 498 //! the cpp object wrapper factories
499 499 QList<PythonQtCppWrapperFactory*> _cppWrapperFactories;
500 500
501 501 QHash<QByteArray, PyObject*> _packages;
502 502
503 503 PythonQtClassInfo* _currentClassInfoForClassWrapperCreation;
504 504
505 505 int _initFlags;
506 506 int _PythonQtObjectPtr_metaId;
507 507
508 508 friend class PythonQt;
509 509 };
510 510
511 511 #endif
@@ -1,566 +1,575
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 PythonQtScriptingConsole.cpp
36 36 // \author Florian Link
37 37 // \author Last changed by $Author: florian $
38 38 // \date 2006-10
39 39 */
40 40 //----------------------------------------------------------------------------------
41 41
42 42 #include "PythonQtScriptingConsole.h"
43 43
44 44 #include <QMenu>
45 45 #include <QMouseEvent>
46 46 #include <QKeyEvent>
47 47 #include <QApplication>
48 48 #include <QTextDocumentFragment>
49 49 #include <QTextBlock>
50 50 #include <QTextCursor>
51 51 #include <QDebug>
52 52 #include <QCompleter>
53 53 #include <QStringListModel>
54 54 #include <QScrollBar>
55 55
56 56 //-----------------------------------------------------------------------------
57 57
58 58 PythonQtScriptingConsole::PythonQtScriptingConsole(QWidget* parent, const PythonQtObjectPtr& context, Qt::WindowFlags windowFlags)
59 59 : QTextEdit(parent) {
60 60
61 61 setWindowFlags(windowFlags);
62 62
63 63 _defaultTextCharacterFormat = currentCharFormat();
64 64 _context = context;
65 65 _historyPosition = 0;
66 66 _hadError = false;
67 67
68 68 _completer = new QCompleter(this);
69 69 _completer->setWidget(this);
70 70 QObject::connect(_completer, SIGNAL(activated(const QString&)),
71 71 this, SLOT(insertCompletion(const QString&)));
72 72
73 73 clear();
74 74
75 75 connect(PythonQt::self(), SIGNAL(pythonStdOut(const QString&)), this, SLOT(stdOut(const QString&)));
76 76 connect(PythonQt::self(), SIGNAL(pythonStdErr(const QString&)), this, SLOT(stdErr(const QString&)));
77 77 }
78 78
79 79 //-----------------------------------------------------------------------------
80 80
81 81 void PythonQtScriptingConsole::stdOut(const QString& s)
82 82 {
83 83 _stdOut += s;
84 84 int idx;
85 85 while ((idx = _stdOut.indexOf('\n'))!=-1) {
86 86 consoleMessage(_stdOut.left(idx));
87 87 std::cout << _stdOut.left(idx).toLatin1().data() << std::endl;
88 88 _stdOut = _stdOut.mid(idx+1);
89 89 }
90 90 }
91 91
92 92 void PythonQtScriptingConsole::stdErr(const QString& s)
93 93 {
94 94 _hadError = true;
95 95 _stdErr += s;
96 96 int idx;
97 97 while ((idx = _stdErr.indexOf('\n'))!=-1) {
98 98 consoleMessage(_stdErr.left(idx));
99 99 std::cerr << _stdErr.left(idx).toLatin1().data() << std::endl;
100 100 _stdErr = _stdErr.mid(idx+1);
101 101 }
102 102 }
103 103
104 104 void PythonQtScriptingConsole::flushStdOut()
105 105 {
106 106 if (!_stdOut.isEmpty()) {
107 107 stdOut("\n");
108 108 }
109 109 if (!_stdErr.isEmpty()) {
110 110 stdErr("\n");
111 111 }
112 112 }
113 113
114 114 //-----------------------------------------------------------------------------
115 115
116 116 PythonQtScriptingConsole::~PythonQtScriptingConsole() {
117 117 }
118 118
119 119
120 120
121 121 //-----------------------------------------------------------------------------
122 122
123 123 void PythonQtScriptingConsole::clear() {
124 124
125 125 QTextEdit::clear();
126 126 appendCommandPrompt();
127 127 }
128 128
129 129 //-----------------------------------------------------------------------------
130 130
131 131 void PythonQtScriptingConsole::executeLine(bool storeOnly)
132 132 {
133 133 QTextCursor textCursor = this->textCursor();
134 134 textCursor.movePosition(QTextCursor::End);
135 135
136 136 // Select the text from the command prompt until the end of the block
137 137 // and get the selected text.
138 138 textCursor.setPosition(commandPromptPosition());
139 139 textCursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);
140 140 QString code = textCursor.selectedText();
141 141
142 142 // i don't know where this trailing space is coming from, blast it!
143 143 if (code.endsWith(" ")) {
144 144 code.truncate(code.length()-1);
145 145 }
146 146
147 147 if (!code.isEmpty()) {
148 148 // Update the history
149 149 _history << code;
150 150 _historyPosition = _history.count();
151 151 _currentMultiLineCode += code + "\n";
152 152
153 153 if (!storeOnly) {
154 154 executeCode(_currentMultiLineCode);
155 155 _currentMultiLineCode = "";
156 156 }
157 157 }
158 158 // Insert a new command prompt
159 159 appendCommandPrompt(storeOnly);
160 160
161 161 }
162 162
163 163 void PythonQtScriptingConsole::executeCode(const QString& code)
164 164 {
165 165 // put visible cursor to the end of the line
166 166 QTextCursor cursor = QTextEdit::textCursor();
167 167 cursor.movePosition(QTextCursor::End);
168 168 setTextCursor(cursor);
169 169
170 170 int cursorPosition = this->textCursor().position();
171 171
172 172 // evaluate the code
173 173 _stdOut = "";
174 174 _stdErr = "";
175 175 PythonQtObjectPtr p;
176 p.setNewRef(PyRun_String(code.toLatin1().data(), Py_single_input, PyModule_GetDict(_context), PyModule_GetDict(_context)));
176 PyObject* dict = NULL;
177 if (PyModule_Check(_context)) {
178 dict = PyModule_GetDict(_context);
179 } else if (PyDict_Check(_context)) {
180 dict = _context;
181 }
182 if (dict) {
183 p.setNewRef(PyRun_String(code.toLatin1().data(), Py_single_input, dict, dict));
184 }
185
177 186 if (!p) {
178 187 PythonQt::self()->handleError();
179 188 }
180 189
181 190 flushStdOut();
182 191
183 192 bool messageInserted = (this->textCursor().position() != cursorPosition);
184 193
185 194 // If a message was inserted, then put another empty line before the command prompt
186 195 // to improve readability.
187 196 if (messageInserted) {
188 197 append(QString());
189 198 }
190 199 }
191 200
192 201
193 202 //-----------------------------------------------------------------------------
194 203
195 204 void PythonQtScriptingConsole::appendCommandPrompt(bool storeOnly) {
196 205 if (storeOnly) {
197 206 _commandPrompt = "...> ";
198 207 } else {
199 208 _commandPrompt = "py> ";
200 209 }
201 210 append(_commandPrompt);
202 211
203 212 QTextCursor cursor = textCursor();
204 213 cursor.movePosition(QTextCursor::End);
205 214 setTextCursor(cursor);
206 215 }
207 216
208 217
209 218
210 219 //-----------------------------------------------------------------------------
211 220
212 221 void PythonQtScriptingConsole::setCurrentFont(const QColor& color, bool bold) {
213 222
214 223 QTextCharFormat charFormat(_defaultTextCharacterFormat);
215 224
216 225 QFont font(charFormat.font());
217 226 font.setBold(bold);
218 227 charFormat.setFont(font);
219 228
220 229 QBrush brush(charFormat.foreground());
221 230 brush.setColor(color);
222 231 charFormat.setForeground(brush);
223 232
224 233 setCurrentCharFormat(charFormat);
225 234 }
226 235
227 236
228 237
229 238 //-----------------------------------------------------------------------------
230 239
231 240 int PythonQtScriptingConsole::commandPromptPosition() {
232 241
233 242 QTextCursor textCursor(this->textCursor());
234 243 textCursor.movePosition(QTextCursor::End);
235 244
236 245 return textCursor.block().position() + _commandPrompt.length();
237 246 }
238 247
239 248
240 249
241 250 //-----------------------------------------------------------------------------
242 251
243 252 void PythonQtScriptingConsole::insertCompletion(const QString& completion)
244 253 {
245 254 QTextCursor tc = textCursor();
246 255 tc.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor);
247 256 if (tc.selectedText()==".") {
248 257 tc.insertText(QString(".") + completion);
249 258 } else {
250 259 tc = textCursor();
251 260 tc.movePosition(QTextCursor::StartOfWord, QTextCursor::MoveAnchor);
252 261 tc.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
253 262 tc.insertText(completion);
254 263 setTextCursor(tc);
255 264 }
256 265 }
257 266
258 267 //-----------------------------------------------------------------------------
259 268 void PythonQtScriptingConsole::handleTabCompletion()
260 269 {
261 270 QTextCursor textCursor = this->textCursor();
262 271 int pos = textCursor.position();
263 272 textCursor.setPosition(commandPromptPosition());
264 273 textCursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);
265 274 int startPos = textCursor.selectionStart();
266 275
267 276 int offset = pos-startPos;
268 277 QString text = textCursor.selectedText();
269 278
270 279 QString textToComplete;
271 280 int cur = offset;
272 281 while (cur--) {
273 282 QChar c = text.at(cur);
274 283 if (c.isLetterOrNumber() || c == '.' || c == '_') {
275 284 textToComplete.prepend(c);
276 285 } else {
277 286 break;
278 287 }
279 288 }
280 289
281 290
282 291 QString lookup;
283 292 QString compareText = textToComplete;
284 293 int dot = compareText.lastIndexOf('.');
285 294 if (dot!=-1) {
286 295 lookup = compareText.mid(0, dot);
287 296 compareText = compareText.mid(dot+1, offset);
288 297 }
289 298 if (!lookup.isEmpty() || !compareText.isEmpty()) {
290 299 compareText = compareText.toLower();
291 300 QStringList found;
292 301 QStringList l = PythonQt::self()->introspection(_context, lookup, PythonQt::Anything);
293 302 foreach (QString n, l) {
294 303 if (n.toLower().startsWith(compareText)) {
295 304 found << n;
296 305 }
297 306 }
298 307
299 308 if (!found.isEmpty()) {
300 309 _completer->setCompletionPrefix(compareText);
301 310 _completer->setCompletionMode(QCompleter::PopupCompletion);
302 311 _completer->setModel(new QStringListModel(found, _completer));
303 312 _completer->setCaseSensitivity(Qt::CaseInsensitive);
304 313 QTextCursor c = this->textCursor();
305 314 c.movePosition(QTextCursor::StartOfWord);
306 315 QRect cr = cursorRect(c);
307 316 cr.setWidth(_completer->popup()->sizeHintForColumn(0)
308 317 + _completer->popup()->verticalScrollBar()->sizeHint().width());
309 318 cr.translate(0,8);
310 319 _completer->complete(cr);
311 320 } else {
312 321 _completer->popup()->hide();
313 322 }
314 323 } else {
315 324 _completer->popup()->hide();
316 325 }
317 326 }
318 327
319 328 void PythonQtScriptingConsole::keyPressEvent(QKeyEvent* event) {
320 329
321 330 if (_completer && _completer->popup()->isVisible()) {
322 331 // The following keys are forwarded by the completer to the widget
323 332 switch (event->key()) {
324 333 case Qt::Key_Return:
325 334 if (!_completer->popup()->currentIndex().isValid()) {
326 335 insertCompletion(_completer->currentCompletion());
327 336 _completer->popup()->hide();
328 337 event->accept();
329 338 }
330 339 event->ignore();
331 340 return;
332 341 break;
333 342 case Qt::Key_Enter:
334 343 case Qt::Key_Escape:
335 344 case Qt::Key_Tab:
336 345 case Qt::Key_Backtab:
337 346
338 347 event->ignore();
339 348 return; // let the completer do default behavior
340 349 default:
341 350 break;
342 351 }
343 352 }
344 353 bool eventHandled = false;
345 354 QTextCursor textCursor = this->textCursor();
346 355
347 356 int key = event->key();
348 357 switch (key) {
349 358
350 359 case Qt::Key_Left:
351 360
352 361 // Moving the cursor left is limited to the position
353 362 // of the command prompt.
354 363
355 364 if (textCursor.position() <= commandPromptPosition()) {
356 365
357 366 QApplication::beep();
358 367 eventHandled = true;
359 368 }
360 369 break;
361 370
362 371 case Qt::Key_Up:
363 372
364 373 // Display the previous command in the history
365 374 if (_historyPosition>0) {
366 375 _historyPosition--;
367 376 changeHistory();
368 377 }
369 378
370 379 eventHandled = true;
371 380 break;
372 381
373 382 case Qt::Key_Down:
374 383
375 384 // Display the next command in the history
376 385 if (_historyPosition+1<_history.count()) {
377 386 _historyPosition++;
378 387 changeHistory();
379 388 }
380 389
381 390 eventHandled = true;
382 391 break;
383 392
384 393 case Qt::Key_Return:
385 394
386 395 executeLine(event->modifiers() & Qt::ShiftModifier);
387 396 eventHandled = true;
388 397 break;
389 398
390 399 case Qt::Key_Backspace:
391 400
392 401 if (textCursor.hasSelection()) {
393 402
394 403 cut();
395 404 eventHandled = true;
396 405
397 406 } else {
398 407
399 408 // Intercept backspace key event to check if
400 409 // deleting a character is allowed. It is not
401 410 // allowed, if the user wants to delete the
402 411 // command prompt.
403 412
404 413 if (textCursor.position() <= commandPromptPosition()) {
405 414
406 415 QApplication::beep();
407 416 eventHandled = true;
408 417 }
409 418 }
410 419 break;
411 420
412 421 case Qt::Key_Delete:
413 422
414 423 cut();
415 424 eventHandled = true;
416 425 break;
417 426
418 427 default:
419 428
420 429 if (key >= Qt::Key_Space && key <= Qt::Key_division) {
421 430
422 431 if (textCursor.hasSelection() && !verifySelectionBeforeDeletion()) {
423 432
424 433 // The selection must not be deleted.
425 434 eventHandled = true;
426 435
427 436 } else {
428 437
429 438 // The key is an input character, check if the cursor is
430 439 // behind the last command prompt, else inserting the
431 440 // character is not allowed.
432 441
433 442 int commandPromptPosition = this->commandPromptPosition();
434 443 if (textCursor.position() < commandPromptPosition) {
435 444
436 445 textCursor.setPosition(commandPromptPosition);
437 446 setTextCursor(textCursor);
438 447 }
439 448 }
440 449 }
441 450 }
442 451
443 452 if (eventHandled) {
444 453
445 454 _completer->popup()->hide();
446 455 event->accept();
447 456
448 457 } else {
449 458
450 459 QTextEdit::keyPressEvent(event);
451 460 QString text = event->text();
452 461 if (!text.isEmpty()) {
453 462 handleTabCompletion();
454 463 } else {
455 464 _completer->popup()->hide();
456 465 }
457 466 eventHandled = true;
458 467 }
459 468 }
460 469
461 470
462 471
463 472 //-----------------------------------------------------------------------------
464 473
465 474 void PythonQtScriptingConsole::cut() {
466 475
467 476 bool deletionAllowed = verifySelectionBeforeDeletion();
468 477 if (deletionAllowed) {
469 478 QTextEdit::cut();
470 479 }
471 480 }
472 481
473 482
474 483
475 484 //-----------------------------------------------------------------------------
476 485
477 486 bool PythonQtScriptingConsole::verifySelectionBeforeDeletion() {
478 487
479 488 bool deletionAllowed = true;
480 489
481 490
482 491 QTextCursor textCursor = this->textCursor();
483 492
484 493 int commandPromptPosition = this->commandPromptPosition();
485 494 int selectionStart = textCursor.selectionStart();
486 495 int selectionEnd = textCursor.selectionEnd();
487 496
488 497 if (textCursor.hasSelection()) {
489 498
490 499 // Selected text may only be deleted after the last command prompt.
491 500 // If the selection is partly after the command prompt set the selection
492 501 // to the part and deletion is allowed. If the selection occurs before the
493 502 // last command prompt, then deletion is not allowed.
494 503
495 504 if (selectionStart < commandPromptPosition ||
496 505 selectionEnd < commandPromptPosition) {
497 506
498 507 // Assure selectionEnd is bigger than selection start
499 508 if (selectionStart > selectionEnd) {
500 509 int tmp = selectionEnd;
501 510 selectionEnd = selectionStart;
502 511 selectionStart = tmp;
503 512 }
504 513
505 514 if (selectionEnd < commandPromptPosition) {
506 515
507 516 // Selection is completely before command prompt,
508 517 // so deletion is not allowed.
509 518 QApplication::beep();
510 519 deletionAllowed = false;
511 520
512 521 } else {
513 522
514 523 // The selectionEnd is after the command prompt, so set
515 524 // the selection start to the commandPromptPosition.
516 525 selectionStart = commandPromptPosition;
517 526 textCursor.setPosition(selectionStart);
518 527 textCursor.setPosition(selectionStart, QTextCursor::KeepAnchor);
519 528 setTextCursor(textCursor);
520 529 }
521 530 }
522 531
523 532 } else { // if (hasSelectedText())
524 533
525 534 // When there is no selected text, deletion is not allowed before the
526 535 // command prompt.
527 536 if (textCursor.position() < commandPromptPosition) {
528 537
529 538 QApplication::beep();
530 539 deletionAllowed = false;
531 540 }
532 541 }
533 542
534 543 return deletionAllowed;
535 544 }
536 545
537 546
538 547
539 548 //-----------------------------------------------------------------------------
540 549
541 550 void PythonQtScriptingConsole::changeHistory() {
542 551
543 552 // Select the text after the last command prompt ...
544 553 QTextCursor textCursor = this->textCursor();
545 554 textCursor.movePosition(QTextCursor::End);
546 555 textCursor.setPosition(commandPromptPosition(), QTextCursor::KeepAnchor);
547 556
548 557 // ... and replace it with the history text.
549 558 textCursor.insertText(_history.value(_historyPosition));
550 559
551 560 textCursor.movePosition(QTextCursor::End);
552 561 setTextCursor(textCursor);
553 562 }
554 563
555 564
556 565
557 566 //-----------------------------------------------------------------------------
558 567
559 568 void PythonQtScriptingConsole::consoleMessage(const QString & message) {
560 569
561 570 append(QString());
562 571 insertPlainText(message);
563 572
564 573 // Reset all font modifications done by the html string
565 574 setCurrentCharFormat(_defaultTextCharacterFormat);
566 575 }
General Comments 0
You need to be logged in to leave comments. Login now