##// END OF EJS Templates
improved so that dict contains properties and that dir() shows all available things, including the derived base attributes...
florianlink -
r34:5daedfb035c8
parent child
Show More
@@ -1,718 +1,727
1 1 /*
2 2 *
3 3 * Copyright (C) 2006 MeVis Research GmbH All Rights Reserved.
4 4 *
5 5 * This library is free software; you can redistribute it and/or
6 6 * modify it under the terms of the GNU Lesser General Public
7 7 * License as published by the Free Software Foundation; either
8 8 * version 2.1 of the License, or (at your option) any later version.
9 9 *
10 10 * This library is distributed in the hope that it will be useful,
11 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 13 * Lesser General Public License for more details.
14 14 *
15 15 * Further, this software is distributed without any warranty that it is
16 16 * free of the rightful claim of any third person regarding infringement
17 17 * or the like. Any license provided herein, whether implied or
18 18 * otherwise, applies only to this software file. Patent licenses, if
19 19 * any, provided herein do not apply to combinations of this program with
20 20 * other software, or any other product whatsoever.
21 21 *
22 22 * You should have received a copy of the GNU Lesser General Public
23 23 * License along with this library; if not, write to the Free Software
24 24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 25 *
26 26 * Contact information: MeVis Research GmbH, Universitaetsallee 29,
27 27 * 28359 Bremen, Germany or:
28 28 *
29 29 * http://www.mevis.de
30 30 *
31 31 */
32 32
33 33 //----------------------------------------------------------------------------------
34 34 /*!
35 35 // \file PythonQt.cpp
36 36 // \author Florian Link
37 37 // \author Last changed by $Author: florian $
38 38 // \date 2006-05
39 39 */
40 40 //----------------------------------------------------------------------------------
41 41
42 42 #include "PythonQtClassInfo.h"
43 43 #include "PythonQtMethodInfo.h"
44 44 #include "PythonQt.h"
45 45 #include <QMetaMethod>
46 46
47 47 QHash<QByteArray, int> PythonQtMethodInfo::_parameterTypeDict;
48 48
49 49 PythonQtClassInfo::PythonQtClassInfo() {
50 50 _meta = NULL;
51 51 _constructors = NULL;
52 52 _destructor = NULL;
53 53 _decoratorProvider = NULL;
54 54 _decoratorProviderCB = NULL;
55 55 _pythonQtClassWrapper = NULL;
56 56 _shellSetInstanceWrapperCB = NULL;
57 57 _metaTypeId = -1;
58 58 _isQObject = false;
59 59 }
60 60
61 61 PythonQtClassInfo::~PythonQtClassInfo()
62 62 {
63 63 clearCachedMembers();
64 64
65 65 if (_constructors) {
66 66 _constructors->deleteOverloadsAndThis();
67 67 }
68 68 if (_destructor) {
69 69 _destructor->deleteOverloadsAndThis();
70 70 }
71 71 foreach(PythonQtSlotInfo* info, _decoratorSlots) {
72 72 info->deleteOverloadsAndThis();
73 73 }
74 74 }
75 75
76 76 void PythonQtClassInfo::setupQObject(const QMetaObject* meta)
77 77 {
78 78 // _wrappedClassName is already set earlier in the class setup
79 79 _isQObject = true;
80 80 _meta = meta;
81 81 }
82 82
83 83 void PythonQtClassInfo::setupCPPObject(const QByteArray& classname)
84 84 {
85 85 _isQObject = false;
86 86 _wrappedClassName = classname;
87 87 _metaTypeId = QMetaType::type(classname);
88 88 }
89 89
90 90 void PythonQtClassInfo::clearCachedMembers()
91 91 {
92 92 QHashIterator<QByteArray, PythonQtMemberInfo> i(_cachedMembers);
93 93 while (i.hasNext()) {
94 94 PythonQtMemberInfo member = i.next().value();
95 95 if (member._type== PythonQtMemberInfo::Slot) {
96 96 PythonQtSlotInfo* info = member._slot;
97 97 while (info) {
98 98 PythonQtSlotInfo* next = info->nextInfo();
99 99 delete info;
100 100 info = next;
101 101 }
102 102 }
103 103 }
104 104 }
105 105
106 106 int PythonQtClassInfo::findCharOffset(const char* sigStart, char someChar)
107 107 {
108 108 const char* sigEnd = sigStart;
109 109 char c;
110 110 do {
111 111 c = *sigEnd++;
112 112 } while (c!=someChar && c!=0);
113 113 return sigEnd-sigStart-1;
114 114 }
115 115
116 116 bool PythonQtClassInfo::lookForPropertyAndCache(const char* memberName)
117 117 {
118 118 bool found = false;
119 119 bool nameMapped = false;
120 120 const char* attributeName = memberName;
121 121 // look for properties
122 122 int i = _meta->indexOfProperty(attributeName);
123 123 if (i==-1) {
124 124 // try to map name to objectName
125 125 if (qstrcmp(attributeName, "name")==0) {
126 126 attributeName = "objectName";
127 127 nameMapped = true;
128 128 i = _meta->indexOfProperty(attributeName);
129 129 }
130 130 }
131 131 if (i!=-1) {
132 132 PythonQtMemberInfo newInfo(_meta->property(i));
133 133 _cachedMembers.insert(attributeName, newInfo);
134 134 if (nameMapped) {
135 135 _cachedMembers.insert(memberName, newInfo);
136 136 }
137 137 #ifdef PYTHONQT_DEBUG
138 138 std::cout << "caching property " << memberName << " on " << _meta->className() << std::endl;
139 139 #endif
140 140 found = true;
141 141 }
142 142 return found;
143 143 }
144 144
145 145 PythonQtSlotInfo* PythonQtClassInfo::recursiveFindDecoratorSlotsFromDecoratorProvider(const char* memberName, PythonQtSlotInfo* inputInfo, bool &found, QHash<QByteArray, PythonQtMemberInfo>& memberCache, int upcastingOffset)
146 146 {
147 147 inputInfo = findDecoratorSlotsFromDecoratorProvider(memberName, inputInfo, found, memberCache, upcastingOffset);
148 148 foreach(const ParentClassInfo& info, _parentClasses) {
149 149 inputInfo = info._parent->recursiveFindDecoratorSlotsFromDecoratorProvider(memberName, inputInfo, found, memberCache, upcastingOffset+info._upcastingOffset);
150 150 }
151 151 return inputInfo;
152 152 }
153 153
154 154 PythonQtSlotInfo* PythonQtClassInfo::findDecoratorSlotsFromDecoratorProvider(const char* memberName, PythonQtSlotInfo* tail, bool &found, QHash<QByteArray, PythonQtMemberInfo>& memberCache, int upcastingOffset) {
155 155 QObject* decoratorProvider = decorator();
156 156 int memberNameLen = strlen(memberName);
157 157 if (decoratorProvider) {
158 158 //qDebug()<< "looking " << decoratorProvider->metaObject()->className() << " " << memberName << " " << upcastingOffset;
159 159 const QMetaObject* meta = decoratorProvider->metaObject();
160 160 int numMethods = meta->methodCount();
161 161 int startFrom = QObject::staticMetaObject.methodCount();
162 162 for (int i = startFrom; i < numMethods; i++) {
163 163 QMetaMethod m = meta->method(i);
164 164 if ((m.methodType() == QMetaMethod::Method ||
165 165 m.methodType() == QMetaMethod::Slot) && m.access() == QMetaMethod::Public) {
166 166
167 167 const char* sigStart = m.signature();
168 168 bool isClassDeco = false;
169 169 if (qstrncmp(sigStart, "static_", 7)==0) {
170 170 // skip the static_classname_ part of the string
171 171 sigStart += 7 + 1 + strlen(className());
172 172 isClassDeco = true;
173 173 } else if (qstrncmp(sigStart, "new_", 4)==0) {
174 174 isClassDeco = true;
175 175 } else if (qstrncmp(sigStart, "delete_", 7)==0) {
176 176 isClassDeco = true;
177 177 }
178 178 // find the first '('
179 179 int offset = findCharOffset(sigStart, '(');
180 180
181 181 // XXX no checking is currently done if the slots have correct first argument or not...
182 182
183 183 // check if same length and same name
184 184 if (memberNameLen == offset && qstrncmp(memberName, sigStart, offset)==0) {
185 185 found = true;
186 186 PythonQtSlotInfo* info = new PythonQtSlotInfo(m, i, decoratorProvider, isClassDeco?PythonQtSlotInfo::ClassDecorator:PythonQtSlotInfo::InstanceDecorator);
187 187 info->setUpcastingOffset(upcastingOffset);
188 188 //qDebug()<< "adding " << decoratorProvider->metaObject()->className() << " " << memberName << " " << upcastingOffset;
189 189 if (tail) {
190 190 tail->setNextInfo(info);
191 191 } else {
192 192 PythonQtMemberInfo newInfo(info);
193 193 memberCache.insert(memberName, newInfo);
194 194 }
195 195 tail = info;
196 196 }
197 197 }
198 198 }
199 199 }
200 200
201 201 tail = findDecoratorSlots(memberName, memberNameLen, tail, found, memberCache, upcastingOffset);
202 202
203 203 return tail;
204 204 }
205 205
206 206 bool PythonQtClassInfo::lookForMethodAndCache(const char* memberName)
207 207 {
208 208 bool found = false;
209 209 int memberNameLen = strlen(memberName);
210 210 PythonQtSlotInfo* tail = NULL;
211 211 if (_meta) {
212 212 int numMethods = _meta->methodCount();
213 213 for (int i = 0; i < numMethods; i++) {
214 214 QMetaMethod m = _meta->method(i);
215 215 if ((m.methodType() == QMetaMethod::Method ||
216 216 m.methodType() == QMetaMethod::Slot) && m.access() == QMetaMethod::Public) {
217 217
218 218 const char* sigStart = m.signature();
219 219 // find the first '('
220 220 int offset = findCharOffset(sigStart, '(');
221 221
222 222 // check if same length and same name
223 223 if (memberNameLen == offset && qstrncmp(memberName, sigStart, offset)==0) {
224 224 found = true;
225 225 PythonQtSlotInfo* info = new PythonQtSlotInfo(m, i);
226 226 if (tail) {
227 227 tail->setNextInfo(info);
228 228 } else {
229 229 PythonQtMemberInfo newInfo(info);
230 230 _cachedMembers.insert(memberName, newInfo);
231 231 }
232 232 tail = info;
233 233 }
234 234 }
235 235 }
236 236 }
237 237
238 238 // look for dynamic decorators in this class and in derived classes
239 239 tail = recursiveFindDecoratorSlotsFromDecoratorProvider(memberName, tail, found, _cachedMembers, 0);
240 240
241 241 return found;
242 242 }
243 243
244 244 bool PythonQtClassInfo::lookForEnumAndCache(const QMetaObject* meta, const char* memberName)
245 245 {
246 246 bool found = false;
247 247 // look for enum values
248 248 int enumCount = meta->enumeratorCount();
249 249 for (int i=0;i<enumCount; i++) {
250 250 QMetaEnum e = meta->enumerator(i);
251 251 for (int j=0; j < e.keyCount(); j++) {
252 252 if (qstrcmp(e.key(j), memberName)==0) {
253 253 PythonQtMemberInfo newInfo(e.value(j));
254 254 _cachedMembers.insert(memberName, newInfo);
255 255 #ifdef PYTHONQT_DEBUG
256 256 std::cout << "caching enum " << memberName << " on " << meta->className() << std::endl;
257 257 #endif
258 258 found = true;
259 259 break;
260 260 }
261 261 }
262 262 }
263 263 return found;
264 264 }
265 265
266 266 PythonQtMemberInfo PythonQtClassInfo::member(const char* memberName)
267 267 {
268 268 PythonQtMemberInfo info = _cachedMembers.value(memberName);
269 269 if (info._type != PythonQtMemberInfo::Invalid) {
270 270 return info;
271 271 } else {
272 272 bool found = false;
273 273
274 274 found = lookForPropertyAndCache(memberName);
275 275 if (!found) {
276 276 found = lookForMethodAndCache(memberName);
277 277 }
278 278 if (!found) {
279 279 if (_meta) {
280 280 // check enums in our meta object directly
281 281 found = lookForEnumAndCache(_meta, memberName);
282 282 }
283 283 if (!found) {
284 284 // check enums in the class hierachy of CPP classes
285 285 // look for dynamic decorators in this class and in derived classes
286 286 QList<QObject*> decoObjects;
287 287 recursiveCollectDecoratorObjects(decoObjects);
288 288 foreach(QObject* deco, decoObjects) {
289 289 // call on ourself for caching, but with different metaObject():
290 290 found = lookForEnumAndCache(deco->metaObject(), memberName);
291 291 if (found) {
292 292 break;
293 293 }
294 294 }
295 295 }
296 296 }
297 297 if (!found) {
298 298 // we store a NotFound member, so that we get a quick result for non existing members (e.g. operator_equal lookup)
299 299 info._type = PythonQtMemberInfo::NotFound;
300 300 _cachedMembers.insert(memberName, info);
301 301 }
302 302 }
303 303
304 304 return _cachedMembers.value(memberName);
305 305 }
306 306
307 307 void PythonQtClassInfo::recursiveCollectDecoratorObjects(QList<QObject*>& decoratorObjects) {
308 308 QObject* deco = decorator();
309 309 if (deco) {
310 310 decoratorObjects.append(deco);
311 311 }
312 312 foreach(const ParentClassInfo& info, _parentClasses) {
313 313 info._parent->recursiveCollectDecoratorObjects(decoratorObjects);
314 314 }
315 315 }
316 316
317 317 void PythonQtClassInfo::recursiveCollectClassInfos(QList<PythonQtClassInfo*>& classInfoObjects) {
318 318 classInfoObjects.append(this);
319 319 foreach(const ParentClassInfo& info, _parentClasses) {
320 320 info._parent->recursiveCollectClassInfos(classInfoObjects);
321 321 }
322 322 }
323 323
324 324 PythonQtSlotInfo* PythonQtClassInfo::findDecoratorSlots(const char* memberName, int memberNameLen, PythonQtSlotInfo* tail, bool &found, QHash<QByteArray, PythonQtMemberInfo>& memberCache, int upcastingOffset)
325 325 {
326 326 QListIterator<PythonQtSlotInfo*> it(_decoratorSlots);
327 327 while (it.hasNext()) {
328 328
329 329 PythonQtSlotInfo* infoOrig = it.next();
330 330
331 331 const char* sigStart = infoOrig->metaMethod()->signature();
332 332 if (qstrncmp("static_", sigStart, 7)==0) {
333 333 sigStart += 7;
334 334 sigStart += findCharOffset(sigStart, '_')+1;
335 335 }
336 336 int offset = findCharOffset(sigStart, '(');
337 337 if (memberNameLen == offset && qstrncmp(memberName, sigStart, offset)==0) {
338 338 //make a copy, otherwise we will have trouble on overloads!
339 339 PythonQtSlotInfo* info = new PythonQtSlotInfo(*infoOrig);
340 340 info->setUpcastingOffset(upcastingOffset);
341 341 found = true;
342 342 if (tail) {
343 343 tail->setNextInfo(info);
344 344 } else {
345 345 PythonQtMemberInfo newInfo(info);
346 346 memberCache.insert(memberName, newInfo);
347 347 }
348 348 tail = info;
349 349 }
350 350 }
351 351 return tail;
352 352 }
353 353
354 354 void PythonQtClassInfo::listDecoratorSlotsFromDecoratorProvider(QStringList& list, bool metaOnly) {
355 355 QObject* decoratorProvider = decorator();
356 356 if (decoratorProvider) {
357 357 const QMetaObject* meta = decoratorProvider->metaObject();
358 358 int numMethods = meta->methodCount();
359 359 int startFrom = QObject::staticMetaObject.methodCount();
360 360 for (int i = startFrom; i < numMethods; i++) {
361 361 QMetaMethod m = meta->method(i);
362 362 if ((m.methodType() == QMetaMethod::Method ||
363 363 m.methodType() == QMetaMethod::Slot) && m.access() == QMetaMethod::Public) {
364 364
365 365 const char* sigStart = m.signature();
366 366 bool isClassDeco = false;
367 367 if (qstrncmp(sigStart, "static_", 7)==0) {
368 368 // skip the static_classname_ part of the string
369 369 sigStart += 7 + 1 + strlen(className());
370 370 isClassDeco = true;
371 371 } else if (qstrncmp(sigStart, "new_", 4)==0) {
372 372 continue;
373 373 } else if (qstrncmp(sigStart, "delete_", 7)==0) {
374 374 continue;
375 375 }
376 376 // find the first '('
377 377 int offset = findCharOffset(sigStart, '(');
378 378
379 379 // XXX no checking is currently done if the slots have correct first argument or not...
380 380 if (!metaOnly || isClassDeco) {
381 381 list << QString::fromLatin1(sigStart, offset);
382 382 }
383 383 }
384 384 }
385 385 }
386 386
387 387 // look for global decorator slots
388 388 QListIterator<PythonQtSlotInfo*> it(_decoratorSlots);
389 389 while (it.hasNext()) {
390 390 PythonQtSlotInfo* slot = it.next();
391 391 if (metaOnly) {
392 392 if (slot->isClassDecorator()) {
393 393 QByteArray first = slot->slotName();
394 394 if (first.startsWith("static_")) {
395 395 int idx = first.indexOf('_');
396 396 idx = first.indexOf('_', idx+1);
397 397 first = first.mid(idx+1);
398 398 }
399 399 list << first;
400 400 }
401 401 } else {
402 402 list << slot->slotName();
403 403 }
404 404 }
405 405 }
406
407 QStringList PythonQtClassInfo::memberList(bool metaOnly)
408 {
409 decorator();
410 406
407 QStringList PythonQtClassInfo::propertyList()
408 {
411 409 QStringList l;
412 QString h;
413 if (_isQObject && _meta && !metaOnly) {
410 if (_isQObject && _meta) {
414 411 int i;
415 412 int numProperties = _meta->propertyCount();
416 413 for (i = 0; i < numProperties; i++) {
417 414 QMetaProperty p = _meta->property(i);
418 415 l << QString(p.name());
419 416 }
420 417 }
418 return l;
419 }
420
421 QStringList PythonQtClassInfo::memberList(bool metaOnly)
422 {
423 decorator();
424
425 QStringList l;
426 QString h;
427 if (_isQObject && _meta && !metaOnly) {
428 l = propertyList();
429 }
421 430
422 431 // normal slots of QObject (or wrapper QObject)
423 432 if (!metaOnly && _meta) {
424 433 int numMethods = _meta->methodCount();
425 434 bool skipQObj = !_isQObject;
426 435 for (int i = skipQObj?QObject::staticMetaObject.methodCount():0; i < numMethods; i++) {
427 436 QMetaMethod m = _meta->method(i);
428 437 if ((m.methodType() == QMetaMethod::Method ||
429 438 m.methodType() == QMetaMethod::Slot) && m.access() == QMetaMethod::Public) {
430 439 QByteArray signa(m.signature());
431 440 if (signa.startsWith("new_")) continue;
432 441 if (signa.startsWith("delete_")) continue;
433 442 if (signa.startsWith("static_")) continue;
434 443 PythonQtSlotInfo slot(m, i);
435 444 l << slot.slotName();
436 445 }
437 446 }
438 447 }
439 448
440 449 {
441 450 // look for dynamic decorators in this class and in derived classes
442 451 QList<PythonQtClassInfo*> infos;
443 452 recursiveCollectClassInfos(infos);
444 453 foreach(PythonQtClassInfo* info, infos) {
445 454 info->listDecoratorSlotsFromDecoratorProvider(l, metaOnly);
446 455 }
447 456 }
448 457
449 458 // List enumerator keys...
450 459 QList<const QMetaObject*> enumMetaObjects;
451 460 if (_meta) {
452 461 enumMetaObjects << _meta;
453 462 }
454 463 // check enums in the class hierachy of CPP classes
455 464 QList<QObject*> decoObjects;
456 465 recursiveCollectDecoratorObjects(decoObjects);
457 466 foreach(QObject* deco, decoObjects) {
458 467 enumMetaObjects << deco->metaObject();
459 468 }
460 469
461 470 foreach(const QMetaObject* meta, enumMetaObjects) {
462 471 for (int i = 0; i<meta->enumeratorCount(); i++) {
463 472 QMetaEnum e = meta->enumerator(i);
464 473 for (int j=0; j < e.keyCount(); j++) {
465 474 l << QString(e.key(j));
466 475 }
467 476 }
468 477 }
469 478
470 479 return QSet<QString>::fromList(l).toList();
471 480 }
472 481
473 482 const char* PythonQtClassInfo::className()
474 483 {
475 484 return _wrappedClassName.constData();
476 485 }
477 486
478 487 void* PythonQtClassInfo::castTo(void* ptr, const char* classname)
479 488 {
480 489 if (ptr==NULL) {
481 490 return NULL;
482 491 }
483 492 if (_wrappedClassName == classname) {
484 493 return ptr;
485 494 }
486 495 foreach(const ParentClassInfo& info, _parentClasses) {
487 496 void* result = info._parent->castTo((char*)ptr + info._upcastingOffset, classname);
488 497 if (result) {
489 498 return result;
490 499 }
491 500 }
492 501 return NULL;
493 502 }
494 503
495 504 bool PythonQtClassInfo::inherits(const char* name)
496 505 {
497 506 if (_wrappedClassName == name) {
498 507 return true;
499 508 }
500 509 foreach(const ParentClassInfo& info, _parentClasses) {
501 510 if (info._parent->inherits(name)) {
502 511 return true;
503 512 }
504 513 }
505 514 return false;
506 515 }
507 516
508 517 bool PythonQtClassInfo::inherits(PythonQtClassInfo* classInfo)
509 518 {
510 519 if (classInfo == this) {
511 520 return true;
512 521 }
513 522 foreach(const ParentClassInfo& info, _parentClasses) {
514 523 if (info._parent->inherits(classInfo)) {
515 524 return true;
516 525 }
517 526 }
518 527 return false;
519 528 }
520 529
521 530 QString PythonQtClassInfo::help()
522 531 {
523 532 decorator();
524 533 QString h;
525 534 h += QString("--- ") + QString(className()) + QString(" ---\n");
526 535
527 536 if (_isQObject) {
528 537 h += "Properties:\n";
529 538
530 539 int i;
531 540 int numProperties = _meta->propertyCount();
532 541 for (i = 0; i < numProperties; i++) {
533 542 QMetaProperty p = _meta->property(i);
534 543 h += QString(p.name()) + " (" + QString(p.typeName()) + " )\n";
535 544 }
536 545 }
537 546
538 547 if (constructors()) {
539 548 h += "Constructors:\n";
540 549 PythonQtSlotInfo* constr = constructors();
541 550 while (constr) {
542 551 h += constr->fullSignature() + "\n";
543 552 constr = constr->nextInfo();
544 553 }
545 554 }
546 555
547 556 h += "Slots:\n";
548 557 h += "QString help()\n";
549 558 h += "QString className()\n";
550 559
551 560 if (_meta) {
552 561 int numMethods = _meta->methodCount();
553 562 for (int i = 0; i < numMethods; i++) {
554 563 QMetaMethod m = _meta->method(i);
555 564 if ((m.methodType() == QMetaMethod::Method ||
556 565 m.methodType() == QMetaMethod::Slot) && m.access() == QMetaMethod::Public) {
557 566 QByteArray signa(m.signature());
558 567 if (signa.startsWith("new_")) continue;
559 568 if (signa.startsWith("delete_")) continue;
560 569 if (signa.startsWith("static_")) continue;
561 570 PythonQtSlotInfo slot(m, i);
562 571 h += slot.fullSignature()+ "\n";
563 572 }
564 573 }
565 574 }
566 575
567 576 // TODO xxx : decorators and enums from decorator() are missing...
568 577 // maybe we can reuse memberlist()?
569 578
570 579 if (_meta && _meta->enumeratorCount()) {
571 580 h += "Enums:\n";
572 581 for (int i = 0; i<_meta->enumeratorCount(); i++) {
573 582 QMetaEnum e = _meta->enumerator(i);
574 583 h += QString(e.name()) + " {";
575 584 for (int j=0; j < e.keyCount(); j++) {
576 585 if (j) { h+= ", "; }
577 586 h += e.key(j);
578 587 }
579 588 h += " }\n";
580 589 }
581 590 }
582 591
583 592 if (_isQObject && _meta) {
584 593 int numMethods = _meta->methodCount();
585 594 if (numMethods>0) {
586 595 h += "Signals:\n";
587 596 for (int i = 0; i < numMethods; i++) {
588 597 QMetaMethod m = _meta->method(i);
589 598 if (m.methodType() == QMetaMethod::Signal) {
590 599 h += QString(m.signature()) + "\n";
591 600 }
592 601 }
593 602 }
594 603 }
595 604 return h;
596 605 }
597 606
598 607 PythonQtSlotInfo* PythonQtClassInfo::constructors()
599 608 {
600 609 if (!_constructors) {
601 610 // force creation of lazy decorator, which will register the decorators
602 611 decorator();
603 612 }
604 613 return _constructors;
605 614 }
606 615
607 616 PythonQtSlotInfo* PythonQtClassInfo::destructor()
608 617 {
609 618 if (!_destructor) {
610 619 // force creation of lazy decorator, which will register the decorators
611 620 decorator();
612 621 }
613 622 return _destructor;
614 623 }
615 624
616 625 void PythonQtClassInfo::addConstructor(PythonQtSlotInfo* info)
617 626 {
618 627 PythonQtSlotInfo* prev = constructors();
619 628 if (prev) {
620 629 info->setNextInfo(prev->nextInfo());
621 630 prev->setNextInfo(info);
622 631 } else {
623 632 _constructors = info;
624 633 }
625 634 }
626 635
627 636 void PythonQtClassInfo::addDecoratorSlot(PythonQtSlotInfo* info)
628 637 {
629 638 _decoratorSlots.append(info);
630 639 }
631 640
632 641 void PythonQtClassInfo::setDestructor(PythonQtSlotInfo* info)
633 642 {
634 643 if (_destructor) {
635 644 _destructor->deleteOverloadsAndThis();
636 645 }
637 646 _destructor = info;
638 647 }
639 648
640 649 void PythonQtClassInfo::setMetaObject(const QMetaObject* meta)
641 650 {
642 651 _meta = meta;
643 652 clearCachedMembers();
644 653 }
645 654
646 655 QObject* PythonQtClassInfo::decorator()
647 656 {
648 657 if (!_decoratorProvider && _decoratorProviderCB) {
649 658 _decoratorProvider = (*_decoratorProviderCB)();
650 659 if (_decoratorProvider) {
651 660 _decoratorProvider->setParent(PythonQt::priv());
652 661 PythonQt::priv()->addDecorators(_decoratorProvider, PythonQtPrivate::ConstructorDecorator | PythonQtPrivate::DestructorDecorator);
653 662 }
654 663 }
655 664 return _decoratorProvider;
656 665 }
657 666
658 667 bool PythonQtClassInfo::hasOwnerMethodButNoOwner(void* object)
659 668 {
660 669 PythonQtMemberInfo info = member("hasOwner");
661 670 if (info._type == PythonQtMemberInfo::Slot) {
662 671 void* obj = object;
663 672 bool result = false;
664 673 void* args[2];
665 674 args[0] = &result;
666 675 args[1] = &obj;
667 676 info._slot->decorator()->qt_metacall(QMetaObject::InvokeMetaMethod, info._slot->slotIndex(), args);
668 677 return !result;
669 678 } else {
670 679 return false;
671 680 }
672 681 }
673 682
674 683 void* PythonQtClassInfo::recursiveCastDownIfPossible(void* ptr, char** resultClassName)
675 684 {
676 685 if (!_polymorphicHandlers.isEmpty()) {
677 686 foreach(PythonQtPolymorphicHandlerCB* cb, _polymorphicHandlers) {
678 687 void* resultPtr = (*cb)(ptr, resultClassName);
679 688 if (resultPtr) {
680 689 return resultPtr;
681 690 }
682 691 }
683 692 }
684 693 foreach(const ParentClassInfo& info, _parentClasses) {
685 694 if (!info._parent->isQObject()) {
686 695 void* resultPtr = info._parent->recursiveCastDownIfPossible((char*)ptr + info._upcastingOffset, resultClassName);
687 696 if (resultPtr) {
688 697 return resultPtr;
689 698 }
690 699 }
691 700 }
692 701 return NULL;
693 702 }
694 703
695 704 void* PythonQtClassInfo::castDownIfPossible(void* ptr, PythonQtClassInfo** resultClassInfo)
696 705 {
697 706 char* className;
698 707 // this would do downcasting recursively...
699 708 // void* resultPtr = recursiveCastDownIfPossible(ptr, &className);
700 709
701 710 // we only do downcasting on the base object, not on the whole inheritance tree...
702 711 void* resultPtr = NULL;
703 712 if (!_polymorphicHandlers.isEmpty()) {
704 713 foreach(PythonQtPolymorphicHandlerCB* cb, _polymorphicHandlers) {
705 714 resultPtr = (*cb)(ptr, &className);
706 715 if (resultPtr) {
707 716 break;
708 717 }
709 718 }
710 719 }
711 720 if (resultPtr) {
712 721 *resultClassInfo = PythonQt::priv()->getClassInfo(className);
713 722 } else {
714 723 *resultClassInfo = this;
715 724 resultPtr = ptr;
716 725 }
717 726 return resultPtr;
718 727 }
@@ -1,240 +1,243
1 1 #ifndef _PYTHONQTCLASSINFO_H
2 2 #define _PYTHONQTCLASSINFO_H
3 3
4 4 /*
5 5 *
6 6 * Copyright (C) 2006 MeVis Research GmbH All Rights Reserved.
7 7 *
8 8 * This library is free software; you can redistribute it and/or
9 9 * modify it under the terms of the GNU Lesser General Public
10 10 * License as published by the Free Software Foundation; either
11 11 * version 2.1 of the License, or (at your option) any later version.
12 12 *
13 13 * This library is distributed in the hope that it will be useful,
14 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 16 * Lesser General Public License for more details.
17 17 *
18 18 * Further, this software is distributed without any warranty that it is
19 19 * free of the rightful claim of any third person regarding infringement
20 20 * or the like. Any license provided herein, whether implied or
21 21 * otherwise, applies only to this software file. Patent licenses, if
22 22 * any, provided herein do not apply to combinations of this program with
23 23 * other software, or any other product whatsoever.
24 24 *
25 25 * You should have received a copy of the GNU Lesser General Public
26 26 * License along with this library; if not, write to the Free Software
27 27 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 28 *
29 29 * Contact information: MeVis Research GmbH, Universitaetsallee 29,
30 30 * 28359 Bremen, Germany or:
31 31 *
32 32 * http://www.mevis.de
33 33 *
34 34 */
35 35
36 36 #include <QMetaObject>
37 37 #include <QMetaMethod>
38 38 #include <QHash>
39 39 #include <QByteArray>
40 40 #include <QList>
41 41 #include "PythonQt.h"
42 42
43 43 class PythonQtSlotInfo;
44 44
45 45 struct PythonQtMemberInfo {
46 46 enum Type {
47 47 Invalid, Slot, EnumValue, Property, NotFound
48 48 };
49 49
50 50 PythonQtMemberInfo():_slot(NULL),_enumValue(0),_type(Invalid) { }
51 51
52 52 PythonQtMemberInfo(PythonQtSlotInfo* info) {
53 53 _type = Slot;
54 54 _slot = info;
55 55 _enumValue = 0;
56 56 }
57 57
58 58 PythonQtMemberInfo(unsigned int enumValue) {
59 59 _type = EnumValue;
60 60 _slot = NULL;
61 61 _enumValue = enumValue;
62 62 }
63 63
64 64 PythonQtMemberInfo(const QMetaProperty& prop) {
65 65 _type = Property;
66 66 _slot = NULL;
67 67 _enumValue = 0;
68 68 _property = prop;
69 69 }
70 70
71 71 Type _type;
72 72
73 73 // TODO: this could be a union...
74 74 PythonQtSlotInfo* _slot;
75 75 unsigned int _enumValue;
76 76 QMetaProperty _property;
77 77 };
78 78
79 79 //! a class that stores all required information about a Qt object (and an optional associated C++ class name)
80 80 /*! for fast lookup of slots when calling the object from Python
81 81 */
82 82 class PythonQtClassInfo {
83 83
84 84 public:
85 85 PythonQtClassInfo();
86 86 ~PythonQtClassInfo();
87 87
88 88 //! store information about parent classes
89 89 struct ParentClassInfo {
90 90 ParentClassInfo(PythonQtClassInfo* parent, int upcastingOffset=0):_parent(parent),_upcastingOffset(upcastingOffset)
91 91 {};
92 92
93 93 PythonQtClassInfo* _parent;
94 94 int _upcastingOffset;
95 95 };
96 96
97 97
98 98 //! setup as a QObject, taking the meta object as meta information about the QObject
99 99 void setupQObject(const QMetaObject* meta);
100 100
101 101 //! setup as a CPP (non-QObject), taking the classname
102 102 void setupCPPObject(const QByteArray& classname);
103 103
104 104 //! get the Python method definition for a given slot name (without return type and signature)
105 105 PythonQtMemberInfo member(const char* member);
106 106
107 107 //! get access to the constructor slot (which may be overloaded if there are multiple constructors)
108 108 PythonQtSlotInfo* constructors();
109 109
110 110 //! get access to the destructor slot
111 111 PythonQtSlotInfo* destructor();
112 112
113 113 //! add a constructor, ownership is passed to classinfo
114 114 void addConstructor(PythonQtSlotInfo* info);
115 115
116 116 //! set a destructor, ownership is passed to classinfo
117 117 void setDestructor(PythonQtSlotInfo* info);
118 118
119 119 //! add a decorator slot, ownership is passed to classinfo
120 120 void addDecoratorSlot(PythonQtSlotInfo* info);
121 121
122 122 //! get the classname (either of the QObject or of the wrapped CPP object)
123 123 const char* className();
124 124
125 125 //! returns if the QObject
126 126 bool isQObject() { return _isQObject; }
127 127
128 128 //! returns if the class is a CPP wrapper
129 129 bool isCPPWrapper() { return !_isQObject; }
130 130
131 131 //! get the meta object
132 132 const QMetaObject* metaObject() { return _meta; }
133 133
134 134 //! set the meta object, this will reset the caching
135 135 void setMetaObject(const QMetaObject* meta);
136 136
137 137 //! returns if this class inherits from the given classname
138 138 bool inherits(const char* classname);
139 139
140 140 //! returns if this class inherits from the given classinfo
141 141 bool inherits(PythonQtClassInfo* info);
142 142
143 143 //! casts the given \c ptr to an object of type \c classname, returns the new pointer
144 144 //! which might be different to \c ptr due to C++ multiple inheritance
145 145 //! (if the cast is not possible or if ptr is NULL, NULL is returned)
146 146 void* castTo(void* ptr, const char* classname);
147 147
148 148 //! get help string for the metaobject
149 149 QString help();
150 150
151 //! get list of all properties (on QObjects only, otherwise the list is empty)
152 QStringList propertyList();
153
151 154 //! get list of all members
152 155 QStringList memberList(bool metaOnly = false);
153 156
154 157 //! get the meta type id of this class (only valid for isCPPWrapper() == true)
155 158 int metaTypeId() { return _metaTypeId; }
156 159
157 160 //! set an additional decorator provider that offers additional decorator slots for this class
158 161 void setDecoratorProvider(PythonQtQObjectCreatorFunctionCB* cb) { _decoratorProviderCB = cb; _decoratorProvider = NULL; }
159 162
160 163 //! get the decorator qobject instance
161 164 QObject* decorator();
162 165
163 166 //! add the parent class info of a CPP object
164 167 void addParentClass(const ParentClassInfo& info) { _parentClasses.append(info); }
165 168
166 169 //! check if the special method "hasOwner" is implemented and if it returns false, which means that the object may be destroyed
167 170 bool hasOwnerMethodButNoOwner(void* object);
168 171
169 172 //! set the associated PythonQtClassWrapper (which handles instance creation of this type)
170 173 void setPythonQtClassWrapper(PyObject* obj) { _pythonQtClassWrapper = obj; }
171 174
172 175 //! get the associated PythonQtClassWrapper (which handles instance creation of this type)
173 176 PyObject* pythonQtClassWrapper() { return _pythonQtClassWrapper; }
174 177
175 178 //! set the shell set instance wrapper cb
176 179 void setShellSetInstanceWrapperCB(PythonQtShellSetInstanceWrapperCB* cb) {
177 180 _shellSetInstanceWrapperCB = cb;
178 181 }
179 182
180 183 //! get the shell set instance wrapper cb
181 184 PythonQtShellSetInstanceWrapperCB* shellSetInstanceWrapperCB() {
182 185 return _shellSetInstanceWrapperCB;
183 186 }
184 187
185 188 //! add a handler for polymorphic downcasting
186 189 void addPolymorphicHandler(PythonQtPolymorphicHandlerCB* cb) { _polymorphicHandlers.append(cb); }
187 190
188 191 //! cast the pointer down in the class hierarchy if a polymorphic handler allows to do that
189 192 void* castDownIfPossible(void* ptr, PythonQtClassInfo** resultClassInfo);
190 193
191 194 private:
192 195 //! clear all cached members
193 196 void clearCachedMembers();
194 197
195 198 void* recursiveCastDownIfPossible(void* ptr, char** resultClassName);
196 199
197 200 PythonQtSlotInfo* findDecoratorSlotsFromDecoratorProvider(const char* memberName, PythonQtSlotInfo* inputInfo, bool &found, QHash<QByteArray, PythonQtMemberInfo>& memberCache, int upcastingOffset);
198 201 void listDecoratorSlotsFromDecoratorProvider(QStringList& list, bool metaOnly);
199 202 PythonQtSlotInfo* recursiveFindDecoratorSlotsFromDecoratorProvider(const char* memberName, PythonQtSlotInfo* inputInfo, bool &found, QHash<QByteArray, PythonQtMemberInfo>& memberCache, int upcastingOffset);
200 203
201 204 void recursiveCollectClassInfos(QList<PythonQtClassInfo*>& classInfoObjects);
202 205 void recursiveCollectDecoratorObjects(QList<QObject*>& decoratorObjects);
203 206
204 207 bool lookForPropertyAndCache(const char* memberName);
205 208 bool lookForMethodAndCache(const char* memberName);
206 209 bool lookForEnumAndCache(const QMetaObject* m, const char* memberName);
207 210
208 211 PythonQtSlotInfo* findDecoratorSlots(const char* memberName, int memberNameLen, PythonQtSlotInfo* tail, bool &found, QHash<QByteArray, PythonQtMemberInfo>& memberCache, int upcastingOffset);
209 212 int findCharOffset(const char* sigStart, char someChar);
210 213
211 214 QHash<QByteArray, PythonQtMemberInfo> _cachedMembers;
212 215
213 216 PythonQtSlotInfo* _constructors;
214 217 PythonQtSlotInfo* _destructor;
215 218 QList<PythonQtSlotInfo*> _decoratorSlots;
216 219
217 220 const QMetaObject* _meta;
218 221
219 222 QByteArray _wrappedClassName;
220 223 QList<ParentClassInfo> _parentClasses;
221 224
222 225 QList<PythonQtPolymorphicHandlerCB*> _polymorphicHandlers;
223 226
224 227 QObject* _decoratorProvider;
225 228 PythonQtQObjectCreatorFunctionCB* _decoratorProviderCB;
226 229
227 230 PyObject* _pythonQtClassWrapper;
228 231
229 232 PythonQtShellSetInstanceWrapperCB* _shellSetInstanceWrapperCB;
230 233
231 234 int _metaTypeId;
232 235
233 236 bool _isQObject;
234 237
235 238 };
236 239
237 240 //---------------------------------------------------------------
238 241
239 242
240 243 #endif
@@ -1,567 +1,571
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 PythonQtInstanceWrapper.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 "PythonQtInstanceWrapper.h"
43 43 #include <QObject>
44 44 #include "PythonQt.h"
45 45 #include "PythonQtSlot.h"
46 46 #include "PythonQtClassInfo.h"
47 47 #include "PythonQtConversion.h"
48 48 #include "PythonQtClassWrapper.h"
49 49
50 50 PythonQtClassInfo* PythonQtInstanceWrapperStruct::classInfo()
51 51 {
52 52 // take the class info from our type object
53 53 return ((PythonQtClassWrapper*)ob_type)->_classInfo;
54 54 }
55 55
56 56 static void PythonQtInstanceWrapper_deleteObject(PythonQtInstanceWrapper* self, bool force = false) {
57 57
58 58 // is this a C++ wrapper?
59 59 if (self->_wrappedPtr) {
60 60 //mlabDebugConst("Python","c++ wrapper removed " << self->_wrappedPtr << " " << self->_obj->className() << " " << self->classInfo()->wrappedClassName().latin1());
61 61
62 62 PythonQt::priv()->removeWrapperPointer(self->_wrappedPtr);
63 63 // we own our qobject, so we delete it now:
64 64 delete self->_obj;
65 65 self->_obj = NULL;
66 66 if (force || self->classInfo()->hasOwnerMethodButNoOwner(self->_wrappedPtr) || self->_ownedByPythonQt) {
67 67 int type = self->classInfo()->metaTypeId();
68 68 if (self->_useQMetaTypeDestroy && type>=0) {
69 69 // use QMetaType to destroy the object
70 70 QMetaType::destroy(type, self->_wrappedPtr);
71 71 } else {
72 72 PythonQtSlotInfo* slot = self->classInfo()->destructor();
73 73 if (slot) {
74 74 void* args[2];
75 75 args[0] = NULL;
76 76 args[1] = &self->_wrappedPtr;
77 77 slot->decorator()->qt_metacall(QMetaObject::InvokeMetaMethod, slot->slotIndex(), args);
78 78 self->_wrappedPtr = NULL;
79 79 } else {
80 80 if (type>=0) {
81 81 // use QMetaType to destroy the object
82 82 QMetaType::destroy(type, self->_wrappedPtr);
83 83 } else {
84 84 // TODO: warn about not being able to destroy the object?
85 85 }
86 86 }
87 87 }
88 88 }
89 89 } else {
90 90 //mlabDebugConst("Python","qobject wrapper removed " << self->_obj->className() << " " << self->classInfo()->wrappedClassName().latin1());
91 91 if (self->_objPointerCopy) {
92 92 PythonQt::priv()->removeWrapperPointer(self->_objPointerCopy);
93 93 }
94 94 if (self->_obj) {
95 95 if (force || self->_ownedByPythonQt) {
96 96 if (force || !self->_obj->parent()) {
97 97 delete self->_obj;
98 98 }
99 99 } else {
100 100 if (self->_obj->parent()==NULL) {
101 101 // tell someone who is interested that the qobject is no longer wrapped, if it has no parent
102 102 PythonQt::qObjectNoLongerWrappedCB(self->_obj);
103 103 }
104 104 }
105 105 }
106 106 }
107 107 self->_obj = NULL;
108 108 }
109 109
110 110 static void PythonQtInstanceWrapper_dealloc(PythonQtInstanceWrapper* self)
111 111 {
112 112 PythonQtInstanceWrapper_deleteObject(self);
113 113 self->_obj.~QPointer<QObject>();
114 114 self->ob_type->tp_free((PyObject*)self);
115 115 }
116 116
117 117 static PyObject* PythonQtInstanceWrapper_new(PyTypeObject *type, PyObject * args, PyObject * /*kwds*/)
118 118 {
119 119 //PythonQtClassWrapper *classType = (PythonQtClassWrapper*)type;
120 120 PythonQtInstanceWrapper *self;
121 121 static PyObject* emptyTuple = NULL;
122 122 if (emptyTuple==NULL) {
123 123 emptyTuple = PyTuple_New(0);
124 124 }
125 125
126 126 self = (PythonQtInstanceWrapper*)PyBaseObject_Type.tp_new(type, emptyTuple, NULL);
127 127
128 128 if (self != NULL) {
129 129 new (&self->_obj) QPointer<QObject>();
130 130 self->_wrappedPtr = NULL;
131 131 self->_ownedByPythonQt = false;
132 132 self->_useQMetaTypeDestroy = false;
133 133 self->_isShellInstance = false;
134 134 }
135 135 return (PyObject *)self;
136 136 }
137 137
138 138 int PythonQtInstanceWrapper_init(PythonQtInstanceWrapper * self, PyObject * args, PyObject * kwds)
139 139 {
140 140 if (args == PythonQtPrivate::dummyTuple()) {
141 141 // we are called from the internal PythonQt API, so our data will be filled later on...
142 142 return 0;
143 143 }
144 144
145 145 // we are called from python, try to construct our object
146 146 if (self->classInfo()->constructors()) {
147 147 void* directCPPPointer = NULL;
148 148 PythonQtSlotFunction_CallImpl(NULL, self->classInfo()->constructors(), args, kwds, NULL, &directCPPPointer);
149 149 if (PyErr_Occurred()) {
150 150 return -1;
151 151 }
152 152 if (directCPPPointer) {
153 153 // change ownershipflag to be owned by PythonQt
154 154 self->_ownedByPythonQt = true;
155 155 self->_useQMetaTypeDestroy = false;
156 156 if (self->classInfo()->isCPPWrapper()) {
157 157 self->_wrappedPtr = directCPPPointer;
158 158 // TODO xxx: if there is a wrapper factory, we might want to generate a wrapper for our class?!
159 159 } else {
160 160 self->setQObject((QObject*)directCPPPointer);
161 161 }
162 162 // register with PythonQt
163 163 PythonQt::priv()->addWrapperPointer(directCPPPointer, self);
164 164
165 165 PythonQtShellSetInstanceWrapperCB* cb = self->classInfo()->shellSetInstanceWrapperCB();
166 166 if (cb) {
167 167 // if we are a derived python class, we set the wrapper
168 168 // to activate the shell class, otherwise we just ignore that it is a shell...
169 169 // we detect it be checking if the type does not have PythonQtInstanceWrapper_Type as direct base class,
170 170 // which is the case for all non-python derived types
171 171 if (((PyObject*)self)->ob_type->tp_base != &PythonQtInstanceWrapper_Type) {
172 172 // set the wrapper and remember that we have a shell instance!
173 173 (*cb)(directCPPPointer, self);
174 174 self->_isShellInstance = true;
175 175 }
176 176 }
177 177 }
178 178 } else {
179 179 QString error = QString("No constructors available for ") + self->classInfo()->className();
180 180 PyErr_SetString(PyExc_ValueError, error.toLatin1().data());
181 181 return -1;
182 182 }
183 183 return 0;
184 184 }
185 185
186 186 static PyObject *PythonQtInstanceWrapper_classname(PythonQtInstanceWrapper* obj)
187 187 {
188 188 return PyString_FromString(obj->ob_type->tp_name);
189 189 }
190 190
191 191 static PyObject *PythonQtInstanceWrapper_help(PythonQtInstanceWrapper* obj)
192 192 {
193 193 return PythonQt::self()->helpCalled(obj->classInfo());
194 194 }
195 195
196 196 static PyObject *PythonQtInstanceWrapper_delete(PythonQtInstanceWrapper * self)
197 197 {
198 198 PythonQtInstanceWrapper_deleteObject(self, true);
199 199 Py_INCREF(Py_None);
200 200 return Py_None;
201 201 }
202 202
203 203
204 204 static PyMethodDef PythonQtInstanceWrapper_methods[] = {
205 205 {"className", (PyCFunction)PythonQtInstanceWrapper_classname, METH_NOARGS,
206 206 "Return the classname of the object"
207 207 },
208 208 {"help", (PyCFunction)PythonQtInstanceWrapper_help, METH_NOARGS,
209 209 "Shows the help of available methods for this class"
210 210 },
211 211 {"delete", (PyCFunction)PythonQtInstanceWrapper_delete, METH_NOARGS,
212 212 "Deletes the C++ object (at your own risk, my friend!)"
213 213 },
214 214 {NULL, NULL, 0, NULL} /* Sentinel */
215 215 };
216 216
217 217
218 218 static PyObject *PythonQtInstanceWrapper_getattro(PyObject *obj,PyObject *name)
219 219 {
220 220 const char *attributeName;
221 221 PythonQtInstanceWrapper *wrapper = (PythonQtInstanceWrapper *)obj;
222 222
223 223 if ((attributeName = PyString_AsString(name)) == NULL) {
224 224 return NULL;
225 225 }
226 226
227 227 if (qstrcmp(attributeName, "__dict__")==0) {
228 QStringList l = wrapper->classInfo()->memberList(false);
229 PyObject* dict = PyDict_New();
228 PyObject* dict = PyBaseObject_Type.tp_getattro(obj, name);
229 dict = PyDict_Copy(dict);
230
231 // only the properties are missing, the rest is already available from
232 // PythonQtClassWrapper...
233 QStringList l = wrapper->classInfo()->propertyList();
230 234 foreach (QString name, l) {
231 235 PyObject* o = PyObject_GetAttrString(obj, name.toLatin1().data());
232 PyDict_SetItemString(dict, name.toLatin1().data(), o);
233 Py_DECREF(o);
236 if (o) {
237 PyDict_SetItemString(dict, name.toLatin1().data(), o);
238 Py_DECREF(o);
239 } else {
240 std::cerr << "PythonQtInstanceWrapper: something is wrong, could not get attribute " << name.toLatin1().data();
241 }
234 242 }
235 // TODO xxx:
236 // this does include python member methods, but not attributes, from where can we get
237 // the correct dict with the attributes of the derive
238
239 243 // Note: we do not put children into the dict, is would look confusing?!
240 244 return dict;
241 245 }
242 246
243 247 // first look in super, to return derived methods from base object first
244 248 PyObject* superAttr = PyBaseObject_Type.tp_getattro(obj, name);
245 249 if (superAttr) {
246 250 return superAttr;
247 251 }
248 252 PyErr_Clear();
249 253
250 254 if (!wrapper->_obj && !wrapper->_wrappedPtr) {
251 255 QString error = QString("Trying to read attribute '") + attributeName + "' from a destroyed " + wrapper->classInfo()->className() + " object";
252 256 PyErr_SetString(PyExc_ValueError, error.toLatin1().data());
253 257 return NULL;
254 258 }
255 259
256 260 // mlabDebugConst("Python","get " << attributeName);
257 261
258 262 // TODO: dynamic properties are missing
259 263
260 264 PythonQtMemberInfo member = wrapper->classInfo()->member(attributeName);
261 265 switch (member._type) {
262 266 case PythonQtMemberInfo::Property:
263 267 if (wrapper->_obj) {
264 268 if (member._property.userType() != QVariant::Invalid) {
265 269 return PythonQtConv::QVariantToPyObject(member._property.read(wrapper->_obj));
266 270 } else {
267 271 Py_INCREF(Py_None);
268 272 return Py_None;
269 273 }
270 274 }
271 275 break;
272 276 case PythonQtMemberInfo::Slot:
273 277 return PythonQtSlotFunction_New(member._slot, obj, NULL);
274 278 break;
275 279 case PythonQtMemberInfo::EnumValue:
276 280 return PyInt_FromLong(member._enumValue);
277 281 break;
278 282 default:
279 283 // is an invalid type, go on
280 284 break;
281 285 }
282 286
283 287 // look for the interal methods (className(), help())
284 288 PyObject* internalMethod = Py_FindMethod( PythonQtInstanceWrapper_methods, obj, (char*)attributeName);
285 289 if (internalMethod) {
286 290 return internalMethod;
287 291 }
288 292 PyErr_Clear();
289 293
290 294 if (wrapper->_obj) {
291 295 // look for a child
292 296 QObjectList children = wrapper->_obj->children();
293 297 for (int i = 0; i < children.count(); i++) {
294 298 QObject *child = children.at(i);
295 299 if (child->objectName() == attributeName) {
296 300 return PythonQt::self()->priv()->wrapQObject(child);
297 301 }
298 302 }
299 303 }
300 304
301 305 QString error = QString(wrapper->classInfo()->className()) + " has no attribute named '" + QString(attributeName) + "'";
302 306 PyErr_SetString(PyExc_AttributeError, error.toLatin1().data());
303 307 return NULL;
304 308 }
305 309
306 310 static int PythonQtInstanceWrapper_setattro(PyObject *obj,PyObject *name,PyObject *value)
307 311 {
308 312 QString error;
309 313 char *attributeName;
310 314 PythonQtInstanceWrapper *wrapper = (PythonQtInstanceWrapper *)obj;
311 315
312 316 if ((attributeName = PyString_AsString(name)) == NULL)
313 317 return -1;
314 318
315 319 if (!wrapper->_obj) {
316 320 error = QString("Trying to set attribute '") + attributeName + "' on a destroyed " + wrapper->classInfo()->className() + " object";
317 321 PyErr_SetString(PyExc_AttributeError, error.toLatin1().data());
318 322 return -1;
319 323 }
320 324
321 325 PythonQtMemberInfo member = wrapper->classInfo()->member(attributeName);
322 326 if (member._type == PythonQtMemberInfo::Property) {
323 327 QMetaProperty prop = member._property;
324 328 if (prop.isWritable()) {
325 329 QVariant v;
326 330 if (prop.isEnumType()) {
327 331 // this will give us either a string or an int, everything else will probably be an error
328 332 v = PythonQtConv::PyObjToQVariant(value);
329 333 } else {
330 334 int t = prop.userType();
331 335 v = PythonQtConv::PyObjToQVariant(value, t);
332 336 }
333 337 bool success = false;
334 338 if (v.isValid()) {
335 339 success = prop.write(wrapper->_obj, v);
336 340 }
337 341 if (success) {
338 342 return 0;
339 343 } else {
340 344 error = QString("Property '") + attributeName + "' of type '" +
341 345 prop.typeName() + "' does not accept an object of type "
342 346 + QString(value->ob_type->tp_name) + " (" + PythonQtConv::PyObjGetRepresentation(value) + ")";
343 347 }
344 348 } else {
345 349 error = QString("Property '") + attributeName + "' of " + obj->ob_type->tp_name + " object is not writable";
346 350 }
347 351 } else if (member._type == PythonQtMemberInfo::Slot) {
348 352 error = QString("Slot '") + attributeName + "' can not be overwritten on " + obj->ob_type->tp_name + " object";
349 353 } else if (member._type == PythonQtMemberInfo::EnumValue) {
350 354 error = QString("EnumValue '") + attributeName + "' can not be overwritten on " + obj->ob_type->tp_name + " object";
351 355 } else if (member._type == PythonQtMemberInfo::NotFound) {
352 356 // if we are a derived python class, we allow setting attributes.
353 357 // if we are a direct CPP wrapper, we do NOT allow it, since
354 358 // it would be confusing to allow it because a wrapper will go away when it is not seen by python anymore
355 359 // and when it is recreated from a CPP pointer the attributes are gone...
356 360 if (obj->ob_type->tp_base != &PythonQtInstanceWrapper_Type) {
357 361 return PyBaseObject_Type.tp_setattro(obj,name,value);
358 362 } else {
359 363 error = QString("'") + attributeName + "' does not exist on " + obj->ob_type->tp_name + " and creating new attributes on C++ objects is not allowed";
360 364 }
361 365 }
362 366
363 367 PyErr_SetString(PyExc_AttributeError, error.toLatin1().data());
364 368 return -1;
365 369 }
366 370
367 371 static PyObject * PythonQtInstanceWrapper_str(PyObject * obj)
368 372 {
369 373 PythonQtInstanceWrapper* wrapper = (PythonQtInstanceWrapper*)obj;
370 374 const char* typeName = obj->ob_type->tp_name;
371 375 QObject *qobj = wrapper->_obj;
372 376 if (wrapper->_wrappedPtr) {
373 377 QString str = PythonQtConv::CPPObjectToString(wrapper->classInfo()->metaTypeId(), wrapper->_wrappedPtr);
374 378 if (!str.isEmpty()) {
375 379 return PyString_FromFormat("%s", str.toLatin1().constData());
376 380 } else
377 381 if (wrapper->_obj) {
378 382 return PyString_FromFormat("%s (C++ Object %p wrapped by %s %p))", typeName, wrapper->_wrappedPtr, wrapper->_obj->metaObject()->className(), qobj);
379 383 } else {
380 384 return PyString_FromFormat("%s (C++ Object %p)", typeName, wrapper->_wrappedPtr);
381 385 }
382 386 } else {
383 387 return PyString_FromFormat("%s (QObject %p)", typeName, qobj);
384 388 }
385 389 }
386 390
387 391 static PyObject * PythonQtInstanceWrapper_repr(PyObject * obj)
388 392 {
389 393 PythonQtInstanceWrapper* wrapper = (PythonQtInstanceWrapper*)obj;
390 394 const char* typeName = obj->ob_type->tp_name;
391 395
392 396 QObject *qobj = wrapper->_obj;
393 397 if (wrapper->_wrappedPtr) {
394 398 QString str = PythonQtConv::CPPObjectToString(wrapper->classInfo()->metaTypeId(), wrapper->_wrappedPtr);
395 399 if (!str.isEmpty()) {
396 400 return PyString_FromFormat("%s(%s, %p)", typeName, str.toLatin1().constData(), wrapper->_wrappedPtr);
397 401 } else
398 402 if (wrapper->_obj) {
399 403 return PyString_FromFormat("%s (C++ Object %p wrapped by %s %p))", typeName, wrapper->_wrappedPtr, wrapper->_obj->metaObject()->className(), qobj);
400 404 } else {
401 405 return PyString_FromFormat("%s (C++ Object %p)", typeName, wrapper->_wrappedPtr);
402 406 }
403 407 } else {
404 408 return PyString_FromFormat("%s (QObject %p)", typeName, wrapper->classInfo()->className(), qobj);
405 409 }
406 410 }
407 411
408 412 static int PythonQtInstanceWrapper_compare(PyObject * obj1, PyObject * obj2)
409 413 {
410 414 if (PyObject_TypeCheck(obj1, &PythonQtInstanceWrapper_Type) &&
411 415 PyObject_TypeCheck(obj2, &PythonQtInstanceWrapper_Type)) {
412 416
413 417 PythonQtInstanceWrapper* w1 = (PythonQtInstanceWrapper*)obj1;
414 418 PythonQtInstanceWrapper* w2 = (PythonQtInstanceWrapper*)obj2;
415 419 // check pointers directly first:
416 420 if (w1->_wrappedPtr != NULL) {
417 421 if (w1->_wrappedPtr == w2->_wrappedPtr) {
418 422 return 0;
419 423 }
420 424 } else if (w1->_obj == w2->_obj) {
421 425 return 0;
422 426 }
423 427 const char* class1 = w1->classInfo()->className();
424 428 const char* class2 = w2->classInfo()->className();
425 429 if (strcmp(class1, class2) == 0) {
426 430 // same class names, so we can try the operator_equal
427 431 PythonQtMemberInfo info = w1->classInfo()->member("operator_equal");
428 432 if (info._type == PythonQtMemberInfo::Slot) {
429 433 bool result = false;
430 434 void* obj1 = w1->_wrappedPtr;
431 435 if (!obj1) {
432 436 obj1 = w1->_obj;
433 437 }
434 438 if (!obj1) { return -1; }
435 439 void* obj2 = w2->_wrappedPtr;
436 440 if (!obj2) {
437 441 obj2 = w2->_obj;
438 442 }
439 443 if (!obj2) { return -1; }
440 444 if (info._slot->isInstanceDecorator()) {
441 445 // call on decorator QObject
442 446 void* args[3];
443 447 args[0] = &result;
444 448 args[1] = &obj1; // this is a pointer, so it needs a pointer to a pointer
445 449 args[2] = obj2; // this is a reference, so it needs the direct pointer
446 450 info._slot->decorator()->qt_metacall(QMetaObject::InvokeMetaMethod, info._slot->slotIndex(), args);
447 451 return result?0:-1;
448 452 } else {
449 453 // call directly on QObject
450 454 if (w1->_obj && w2->_obj) {
451 455 void* args[2];
452 456 args[0] = &result;
453 457 args[2] = obj2; // this is a reference, so it needs the direct pointer
454 458 w1->_obj->qt_metacall(QMetaObject::InvokeMetaMethod, info._slot->slotIndex(), args);
455 459 }
456 460 }
457 461 }
458 462 }
459 463 }
460 464 return -1;
461 465 }
462 466
463 467 static int PythonQtInstanceWrapper_nonzero(PyObject *obj)
464 468 {
465 469 PythonQtInstanceWrapper* wrapper = (PythonQtInstanceWrapper*)obj;
466 470 return (wrapper->_wrappedPtr == NULL && wrapper->_obj == NULL)?0:1;
467 471 }
468 472
469 473
470 474 static long PythonQtInstanceWrapper_hash(PythonQtInstanceWrapper *obj)
471 475 {
472 476 if (obj->_wrappedPtr != NULL) {
473 477 return reinterpret_cast<long>(obj->_wrappedPtr);
474 478 } else {
475 479 QObject* qobj = obj->_obj; // get pointer from QPointer wrapper
476 480 return reinterpret_cast<long>(qobj);
477 481 }
478 482 }
479 483
480 484
481 485
482 486 // we override nb_nonzero, so that one can do 'if' expressions to test for a NULL ptr
483 487 static PyNumberMethods PythonQtInstanceWrapper_as_number = {
484 488 0, /* nb_add */
485 489 0, /* nb_subtract */
486 490 0, /* nb_multiply */
487 491 0, /* nb_divide */
488 492 0, /* nb_remainder */
489 493 0, /* nb_divmod */
490 494 0, /* nb_power */
491 495 0, /* nb_negative */
492 496 0, /* nb_positive */
493 497 0, /* nb_absolute */
494 498 PythonQtInstanceWrapper_nonzero, /* nb_nonzero */
495 499 0, /* nb_invert */
496 500 0, /* nb_lshift */
497 501 0, /* nb_rshift */
498 502 0, /* nb_and */
499 503 0, /* nb_xor */
500 504 0, /* nb_or */
501 505 0, /* nb_coerce */
502 506 0, /* nb_int */
503 507 0, /* nb_long */
504 508 0, /* nb_float */
505 509 0, /* nb_oct */
506 510 0, /* nb_hex */
507 511 0, /* nb_inplace_add */
508 512 0, /* nb_inplace_subtract */
509 513 0, /* nb_inplace_multiply */
510 514 0, /* nb_inplace_divide */
511 515 0, /* nb_inplace_remainder */
512 516 0, /* nb_inplace_power */
513 517 0, /* nb_inplace_lshift */
514 518 0, /* nb_inplace_rshift */
515 519 0, /* nb_inplace_and */
516 520 0, /* nb_inplace_xor */
517 521 0, /* nb_inplace_or */
518 522 0, /* nb_floor_divide */
519 523 0, /* nb_true_divide */
520 524 0, /* nb_inplace_floor_divide */
521 525 0, /* nb_inplace_true_divide */
522 526 };
523 527
524 528 PyTypeObject PythonQtInstanceWrapper_Type = {
525 529 PyObject_HEAD_INIT(&PythonQtClassWrapper_Type)
526 530 0, /*ob_size*/
527 531 "PythonQt.PythonQtInstanceWrapper", /*tp_name*/
528 532 sizeof(PythonQtInstanceWrapper), /*tp_basicsize*/
529 533 0, /*tp_itemsize*/
530 534 (destructor)PythonQtInstanceWrapper_dealloc, /*tp_dealloc*/
531 535 0, /*tp_print*/
532 536 0, /*tp_getattr*/
533 537 0, /*tp_setattr*/
534 538 PythonQtInstanceWrapper_compare, /*tp_compare*/
535 539 PythonQtInstanceWrapper_repr, /*tp_repr*/
536 540 &PythonQtInstanceWrapper_as_number, /*tp_as_number*/
537 541 0, /*tp_as_sequence*/
538 542 0, /*tp_as_mapping*/
539 543 (hashfunc)PythonQtInstanceWrapper_hash, /*tp_hash */
540 544 0, /*tp_call*/
541 545 PythonQtInstanceWrapper_str, /*tp_str*/
542 546 PythonQtInstanceWrapper_getattro, /*tp_getattro*/
543 547 PythonQtInstanceWrapper_setattro, /*tp_setattro*/
544 548 0, /*tp_as_buffer*/
545 549 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
546 550 "PythonQtInstanceWrapper object", /* tp_doc */
547 551 0, /* tp_traverse */
548 552 0, /* tp_clear */
549 553 0, /* tp_richcompare */
550 554 0, /* tp_weaklistoffset */
551 555 0, /* tp_iter */
552 556 0, /* tp_iternext */
553 557 0, /* tp_methods */
554 558 0, /* tp_members */
555 559 0, /* tp_getset */
556 560 0, /* tp_base */
557 561 0, /* tp_dict */
558 562 0, /* tp_descr_get */
559 563 0, /* tp_descr_set */
560 564 0, /* tp_dictoffset */
561 565 (initproc)PythonQtInstanceWrapper_init, /* tp_init */
562 566 0, /* tp_alloc */
563 567 PythonQtInstanceWrapper_new, /* tp_new */
564 568 };
565 569
566 570 //-------------------------------------------------------
567 571
General Comments 0
You need to be logged in to leave comments. Login now