Skip to content

Commit a15f2cd

Browse files
committed
- wrapped instances are now wrapped by class specific subtypes to facilitate future deriving from python
- object creation has changed by using the python type system (calling the type object with PyObject_Call)
1 parent 05e2f30 commit a15f2cd

11 files changed

Lines changed: 322 additions & 248 deletions

src/PythonQt.cpp

Lines changed: 91 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -170,17 +170,20 @@ PythonQt::PythonQt(int flags)
170170
}
171171
Py_INCREF(&PythonQtSlotFunction_Type);
172172

173-
// add our own python object types for qt objects
174-
if (PyType_Ready(&PythonQtInstanceWrapper_Type) < 0) {
175-
std::cerr << "could not initialize PythonQtInstanceWrapper_Type" << ", in " << __FILE__ << ":" << __LINE__ << std::endl;
176-
}
177-
Py_INCREF(&PythonQtInstanceWrapper_Type);
178-
179-
// add our own python object types for qt objects
173+
// according to Python docs, set the type late here, since it can not safely be stored in the struct when declaring it
174+
PythonQtClassWrapper_Type.tp_base = &PyType_Type;
175+
// add our own python object types for classes
180176
if (PyType_Ready(&PythonQtClassWrapper_Type) < 0) {
181177
std::cerr << "could not initialize PythonQtClassWrapper_Type" << ", in " << __FILE__ << ":" << __LINE__ << std::endl;
182178
}
183179
Py_INCREF(&PythonQtClassWrapper_Type);
180+
181+
// add our own python object types for CPP instances
182+
if (PyType_Ready(&PythonQtInstanceWrapper_Type) < 0) {
183+
PythonQt::handleError();
184+
std::cerr << "could not initialize PythonQtInstanceWrapper_Type" << ", in " << __FILE__ << ":" << __LINE__ << std::endl;
185+
}
186+
Py_INCREF(&PythonQtInstanceWrapper_Type);
184187

185188
// add our own python object types for redirection of stdout
186189
if (PyType_Ready(&PythonQtStdOutRedirectType) < 0) {
@@ -274,16 +277,8 @@ void PythonQtPrivate::registerClass(const QMetaObject* metaobject, const char* p
274277
while (m) {
275278
PythonQtClassInfo* info = _knownQtClasses.value(m->className());
276279
if (!info) {
277-
info = new PythonQtClassInfo(m);
280+
info = createPythonQtClassInfo(m, NULL, package);
278281
_knownQtClasses.insert(m->className(), info);
279-
PythonQtObjectPtr pack = packageByName(package);
280-
PyObject* pyobj = (PyObject*)createNewPythonQtClassWrapper(info);
281-
PyModule_AddObject(pack, m->className(), pyobj);
282-
if (package && strncmp(package,"Qt",2)==0) {
283-
// put all qt objects into Qt as well
284-
PythonQtObjectPtr pack = packageByName("Qt");
285-
PyModule_AddObject(pack, m->className(), pyobj);
286-
}
287282
}
288283
if (first) {
289284
first = false;
@@ -295,6 +290,21 @@ void PythonQtPrivate::registerClass(const QMetaObject* metaobject, const char* p
295290
}
296291
}
297292

293+
PythonQtClassInfo* PythonQtPrivate::createPythonQtClassInfo(const QMetaObject* meta, const char* cppClassName, const char* package)
294+
{
295+
PythonQtClassInfo* info = new PythonQtClassInfo(meta, cppClassName);
296+
PythonQtObjectPtr pack = packageByName(package);
297+
PyObject* pyobj = (PyObject*)createNewPythonQtClassWrapper(info, package);
298+
PyModule_AddObject(pack, meta?meta->className():cppClassName, pyobj);
299+
if (package && strncmp(package,"Qt",2)==0) {
300+
// put all qt objects into Qt as well
301+
PythonQtObjectPtr pack = packageByName("Qt");
302+
PyModule_AddObject(pack, meta?meta->className():cppClassName, pyobj);
303+
}
304+
info->setPythonQtClassWrapper(pyobj);
305+
return info;
306+
}
307+
298308
bool PythonQtPrivate::isEnumType(const QMetaObject* meta, const QByteArray& name) {
299309
int i = meta?meta->indexOfEnumerator(name.constData()):-1;
300310
if (i!=-1) {
@@ -337,10 +347,10 @@ PyObject* PythonQtPrivate::wrapQObject(QObject* obj)
337347
classInfo = _knownQtClasses.value(obj->metaObject()->className());
338348
}
339349
wrap = createNewPythonQtInstanceWrapper(obj, classInfo);
340-
// mlabDebugConst("MLABPython","new qobject wrapper added " << " " << wrap->_obj->className() << " " << wrap->_info->wrappedClassName().latin1());
350+
// mlabDebugConst("MLABPython","new qobject wrapper added " << " " << wrap->_obj->className() << " " << wrap->classInfo()->wrappedClassName().latin1());
341351
} else {
342352
Py_INCREF(wrap);
343-
// mlabDebugConst("MLABPython","qobject wrapper reused " << wrap->_obj->className() << " " << wrap->_info->wrappedClassName().latin1());
353+
// mlabDebugConst("MLABPython","qobject wrapper reused " << wrap->_obj->className() << " " << wrap->classInfo()->wrappedClassName().latin1());
344354
}
345355
return (PyObject*)wrap;
346356
}
@@ -371,7 +381,7 @@ PyObject* PythonQtPrivate::wrapPtr(void* ptr, const QByteArray& name)
371381
info = _knownQtClasses.value(qptr->metaObject()->className());
372382
}
373383
wrap = createNewPythonQtInstanceWrapper(qptr, info);
374-
// mlabDebugConst("MLABPython","new qobject wrapper added " << " " << wrap->_obj->className() << " " << wrap->_info->wrappedClassName().latin1());
384+
// mlabDebugConst("MLABPython","new qobject wrapper added " << " " << wrap->_obj->className() << " " << wrap->classInfo()->wrappedClassName().latin1());
375385
} else {
376386
// maybe it is a PyObject, which we can return directly
377387
if (name == "PyObject") {
@@ -389,32 +399,37 @@ PyObject* PythonQtPrivate::wrapPtr(void* ptr, const QByteArray& name)
389399
}
390400
PythonQtClassInfo* info = _knownQtWrapperClasses.value(name);
391401
if (!info) {
392-
info = new PythonQtClassInfo(wrapper?wrapper->metaObject():NULL, name);
393-
_knownQtWrapperClasses.insert(name, info);
394-
PyModule_AddObject(_pythonQtModule, name, (PyObject*)createNewPythonQtClassWrapper(info));
395-
} else {
396-
if (wrapper && (info->metaObject() != wrapper->metaObject())) {
397-
info->setMetaObject(wrapper->metaObject());
398-
}
402+
registerCPPClass(name.constData());
403+
info = _knownQtWrapperClasses.value(name);
404+
}
405+
if (wrapper && (info->metaObject() != wrapper->metaObject())) {
406+
info->setMetaObject(wrapper->metaObject());
399407
}
400408
wrap = createNewPythonQtInstanceWrapper(wrapper, info, ptr);
401-
// mlabDebugConst("MLABPython","new c++ wrapper added " << wrap->_wrappedPtr << " " << wrap->_obj->className() << " " << wrap->_info->wrappedClassName().latin1());
409+
// mlabDebugConst("MLABPython","new c++ wrapper added " << wrap->_wrappedPtr << " " << wrap->_obj->className() << " " << wrap->classInfo()->wrappedClassName().latin1());
402410
}
403411
} else {
404412
Py_INCREF(wrap);
405-
//mlabDebugConst("MLABPython","c++ wrapper reused " << wrap->_wrappedPtr << " " << wrap->_obj->className() << " " << wrap->_info->wrappedClassName().latin1());
413+
//mlabDebugConst("MLABPython","c++ wrapper reused " << wrap->_wrappedPtr << " " << wrap->_obj->className() << " " << wrap->classInfo()->wrappedClassName().latin1());
406414
}
407415
return (PyObject*)wrap;
408416
}
409417

418+
PyObject* PythonQtPrivate::dummyTuple() {
419+
static PyObject* dummyTuple = NULL;
420+
if (dummyTuple==NULL) {
421+
dummyTuple = PyTuple_New(1);
422+
PyTuple_SET_ITEM(dummyTuple, 0 , PyString_FromString("dummy"));
423+
}
424+
return dummyTuple;
425+
}
426+
410427

411428
PythonQtInstanceWrapper* PythonQtPrivate::createNewPythonQtInstanceWrapper(QObject* obj, PythonQtClassInfo* info, void* wrappedPtr) {
412-
PythonQtInstanceWrapper* result;
413-
result = (PythonQtInstanceWrapper *)PythonQtInstanceWrapper_Type.tp_new(&PythonQtInstanceWrapper_Type,
414-
NULL, NULL);
429+
// call the associated class type to create a new instance...
430+
PythonQtInstanceWrapper* result = (PythonQtInstanceWrapper*)PyObject_Call(info->pythonQtClassWrapper(), dummyTuple(), NULL);
415431

416432
result->setQObject(obj);
417-
result->_info = info;
418433
result->_wrappedPtr = wrappedPtr;
419434
result->_ownedByPythonQt = false;
420435
result->_useQMetaTypeDestroy = false;
@@ -431,11 +446,36 @@ PythonQtInstanceWrapper* PythonQtPrivate::createNewPythonQtInstanceWrapper(QObje
431446
return result;
432447
}
433448

434-
PythonQtClassWrapper* PythonQtPrivate::createNewPythonQtClassWrapper(PythonQtClassInfo* info) {
449+
PythonQtClassWrapper* PythonQtPrivate::createNewPythonQtClassWrapper(PythonQtClassInfo* info, const char* package) {
435450
PythonQtClassWrapper* result;
436-
result = (PythonQtClassWrapper *)PythonQtClassWrapper_Type.tp_new(&PythonQtClassWrapper_Type,
437-
NULL, NULL);
438-
result->_info = info;
451+
452+
PyObject* className = PyString_FromString(info->className());
453+
454+
PyObject* baseClasses = PyTuple_New(1);
455+
PyTuple_SET_ITEM(baseClasses, 0, (PyObject*)&PythonQtInstanceWrapper_Type);
456+
457+
PyObject* typeDict = PyDict_New();
458+
QByteArray moduleName("PythonQt");
459+
if (package && strcmp(package, "")!=0) {
460+
moduleName += ".";
461+
moduleName += package;
462+
}
463+
PyDict_SetItemString(typeDict, "__module__", PyString_FromString(moduleName.constData()));
464+
465+
PyObject* args = Py_BuildValue("OOO", className, baseClasses, typeDict);
466+
467+
// set the class info so that PythonQtClassWrapper_new can read it
468+
_currentClassInfoForClassWrapperCreation = info;
469+
// create the new type object by calling the type
470+
result = (PythonQtClassWrapper *)PyObject_Call((PyObject *)&PythonQtClassWrapper_Type, args, NULL);
471+
472+
Py_DECREF(baseClasses);
473+
Py_DECREF(typeDict);
474+
Py_DECREF(args);
475+
Py_DECREF(className);
476+
477+
//TODO XXX why is this incref needed? It looks like the types get garbage collected somehow?!
478+
Py_INCREF((PyObject*)result);
439479
return result;
440480
}
441481

@@ -666,7 +706,7 @@ QStringList PythonQt::introspection(PyObject* module, const QString& objectname,
666706
}
667707
} else if (object->ob_type == &PythonQtClassWrapper_Type) {
668708
PythonQtClassWrapper* o = (PythonQtClassWrapper*)object.object();
669-
PythonQtSlotInfo* info = o->_info->constructors();
709+
PythonQtSlotInfo* info = o->classInfo()->constructors();
670710

671711
while (info) {
672712
results << info->fullSignature(false);
@@ -817,23 +857,21 @@ void PythonQt::addWrapperFactory(PythonQtCppWrapperFactory* factory)
817857
_p->_cppWrapperFactories.append(factory);
818858
}
819859

820-
void PythonQt::addConstructorHandler(PythonQtConstructorHandler* factory)
821-
{
822-
_p->_constructorHandlers.append(factory);
823-
}
824-
825-
const QList<PythonQtConstructorHandler*>& PythonQt::constructorHandlers()
826-
{
827-
return _p->_constructorHandlers;
828-
};
829-
830860
//---------------------------------------------------------------------------------------------------
831861
PythonQtPrivate::PythonQtPrivate()
832862
{
833863
_importInterface = NULL;
834864
_defaultImporter = new PythonQtQFileImporter;
835865
_noLongerWrappedCB = NULL;
836866
_wrappedCB = NULL;
867+
_currentClassInfoForClassWrapperCreation = NULL;
868+
}
869+
870+
PythonQtClassInfo* PythonQtPrivate::currentClassInfoForClassWrapperCreation()
871+
{
872+
PythonQtClassInfo* info = _currentClassInfoForClassWrapperCreation;
873+
_currentClassInfoForClassWrapperCreation = NULL;
874+
return info;
837875
}
838876

839877
void PythonQtPrivate::addDecorators(QObject* o, int decoTypes)
@@ -1006,16 +1044,8 @@ void PythonQtPrivate::registerCPPClass(const char* typeName, const char* parentT
10061044
{
10071045
PythonQtClassInfo* info = _knownQtWrapperClasses.value(typeName);
10081046
if (!info) {
1009-
info = new PythonQtClassInfo(NULL, typeName);
1047+
info = createPythonQtClassInfo(NULL, typeName, package);
10101048
_knownQtWrapperClasses.insert(typeName, info);
1011-
PythonQtObjectPtr pack = packageByName(package);
1012-
PyObject* pyobj = (PyObject*)createNewPythonQtClassWrapper(info);
1013-
PyModule_AddObject(pack, typeName, pyobj);
1014-
if (package && strncmp(package,"Qt",2)==0) {
1015-
// put all qt objects into Qt as well
1016-
PythonQtObjectPtr pack = packageByName("Qt");
1017-
PyModule_AddObject(pack, typeName, pyobj);
1018-
}
10191049
}
10201050
if (parentTypeName) {
10211051
info->setWrappedParentClassName(parentTypeName);
@@ -1055,6 +1085,11 @@ void PythonQtPrivate::removeWrapperPointer(void* obj)
10551085
_wrappedObjects.remove(obj);
10561086
}
10571087

1088+
void PythonQtPrivate::addWrapperPointer(void* obj, PythonQtInstanceWrapper* wrapper)
1089+
{
1090+
_wrappedObjects.insert(obj, wrapper);
1091+
}
1092+
10581093
PythonQtInstanceWrapper* PythonQtPrivate::findWrapperAndRemoveUnused(void* obj)
10591094
{
10601095
PythonQtInstanceWrapper* wrap = _wrappedObjects.value(obj);

src/PythonQt.h

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@ class PythonQtMethodInfo;
6363
class PythonQtSignalReceiver;
6464
class PythonQtImportFileInterface;
6565
class PythonQtCppWrapperFactory;
66-
class PythonQtConstructorHandler;
6766
class PythonQtQFileImporter;
6867

6968
typedef void PythonQtQObjectWrappedCB(QObject* object);
@@ -269,12 +268,6 @@ class PYTHONQT_EXPORT PythonQt : public QObject {
269268
//! add the given factory to PythonQt (ownership stays with caller)
270269
void addWrapperFactory(PythonQtCppWrapperFactory* factory);
271270

272-
//! add the given constructor handler to PythonQt (ownership stays with caller)
273-
void addConstructorHandler(PythonQtConstructorHandler* handler);
274-
275-
//! get list of constructor handlers
276-
const QList<PythonQtConstructorHandler*>& constructorHandlers();
277-
278271
//@}
279272

280273
//@{ Custom importer (to replace internal import implementation of python)
@@ -383,6 +376,8 @@ class PythonQtPrivate : public QObject {
383376
//! returns if the id is the id for PythonQtObjectPtr
384377
bool isPythonQtObjectPtrMetaId(int id) { return _PythonQtObjectPtr_metaId == id; }
385378

379+
//! add the wrapper pointer (for reuse if the same obj appears while wrapper still exists)
380+
void addWrapperPointer(void* obj, PythonQtInstanceWrapper* wrapper);
386381
//! remove the wrapper ptr again
387382
void removeWrapperPointer(void* obj);
388383

@@ -425,7 +420,7 @@ class PythonQtPrivate : public QObject {
425420
bool isEnumType(const QMetaObject* meta, const QByteArray& name);
426421

427422
//! helper method that creates a PythonQtClassWrapper object
428-
PythonQtClassWrapper* createNewPythonQtClassWrapper(PythonQtClassInfo* info);
423+
PythonQtClassWrapper* createNewPythonQtClassWrapper(PythonQtClassInfo* info, const char* package = NULL);
429424

430425
//! helper method that creates a PythonQtInstanceWrapper object and registers it in the object map
431426
PythonQtInstanceWrapper* createNewPythonQtInstanceWrapper(QObject* obj, PythonQtClassInfo* info, void* wrappedPtr = NULL);
@@ -445,7 +440,17 @@ class PythonQtPrivate : public QObject {
445440
//! creates the new module from the given pycode
446441
PythonQtObjectPtr createModule(const QString& name, PyObject* pycode);
447442

443+
//! get the current class info (for the next PythonQtClassWrapper that is created) and reset it to NULL again
444+
PythonQtClassInfo* currentClassInfoForClassWrapperCreation();
445+
446+
//! the dummy tuple (which is empty and may be used to detected that a wrapper is called from internal wrapper creation
447+
static PyObject* dummyTuple();
448+
448449
private:
450+
451+
//! create a new class info and python wrapper type
452+
PythonQtClassInfo* createPythonQtClassInfo(const QMetaObject* meta, const char* cppClassName, const char* package);
453+
449454
//! get/create new package module
450455
PythonQtObjectPtr packageByName(const char* name);
451456

@@ -487,14 +492,13 @@ class PythonQtPrivate : public QObject {
487492
//! the cpp object wrapper factories
488493
QList<PythonQtCppWrapperFactory*> _cppWrapperFactories;
489494

490-
//! the cpp object wrapper factories
491-
QList<PythonQtConstructorHandler*> _constructorHandlers;
492-
493495
QHash<QByteArray , PythonQtSlotInfo *> _constructorSlots;
494496
QHash<QByteArray , PythonQtSlotInfo *> _destructorSlots;
495497

496498
QHash<QByteArray, PythonQtObjectPtr> _packages;
497499

500+
PythonQtClassInfo* _currentClassInfoForClassWrapperCreation;
501+
498502
int _initFlags;
499503
int _PythonQtObjectPtr_metaId;
500504

src/PythonQtClassInfo.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ PythonQtClassInfo::PythonQtClassInfo(const QMetaObject* meta, const QByteArray&
5454
_parentClassInfoResolved = false;
5555
_decoratorProvider = NULL;
5656
_decoratorProviderCB = NULL;
57+
_pythonQtClassWrapper = NULL;
5758
if (wrappedClassName.isEmpty()) {
5859
_metaTypeId = -1;
5960
} else {

src/PythonQtClassInfo.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,12 @@ class PythonQtClassInfo {
125125
//! check if the special method "hasOwner" is implemented and if it returns false, which means that the object may be destroyed
126126
bool hasOwnerMethodButNoOwner(void* object);
127127

128+
//! set the associated PythonQtClassWrapper (which handles instance creation of this type)
129+
void setPythonQtClassWrapper(PyObject* obj) { _pythonQtClassWrapper = obj; }
130+
131+
//! get the associated PythonQtClassWrapper (which handles instance creation of this type)
132+
PyObject* pythonQtClassWrapper() { return _pythonQtClassWrapper; }
133+
128134
private:
129135
//! resolve the parent class from either meta object or cpp parent class name
130136
void resolveParentClassInfo();
@@ -150,6 +156,8 @@ class PythonQtClassInfo {
150156
PythonQtQObjectCreatorFunctionCB* _decoratorProviderCB;
151157
PythonQtClassInfo* _parentClassInfo;
152158

159+
PyObject* _pythonQtClassWrapper;
160+
153161
bool _parentClassInfoResolved;
154162
int _metaTypeId;
155163

0 commit comments

Comments
 (0)