Skip to content

Commit 529175e

Browse files
author
florianlink
committed
merged in features from the MeVisLab repository
- added createModuleFromFile/createModuleFromScript/createUniqueModule - switched object destruction to use QPointer and lazy wrapper removal to avoid expensive objectDestroyed signal connections - added hash() support for PythnQtWrapper object - added support for signal to python function connections where the function has less arguments than the emitted signal - added setQObject[NoLonger]WrappedCallback API to support external reference counting on QObjects that are exposed to PythonQt - implemented flush on std redirect to support python logging framework - improved QVariant printing and fixed print error on MacX git-svn-id: http://svn.code.sf.net/p/pythonqt/code/trunk@39 ea8d5007-eb21-0410-b261-ccb3ea6e24a9
1 parent ba58876 commit 529175e

10 files changed

+271
-68
lines changed

src/PythonQt.cpp

+87-22
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
#include <pydebug.h>
5353

5454
PythonQt* PythonQt::_self = NULL;
55+
int PythonQt::_uniqueModuleCount = 0;
5556

5657

5758
void PythonQt::init(int flags)
@@ -211,6 +212,13 @@ void PythonQt::registerClass(const QMetaObject* metaobject)
211212
_p->registerClass(metaobject);
212213
}
213214

215+
void PythonQt::qObjectNoLongerWrappedCB(QObject* o)
216+
{
217+
if (_self->_p->_noLongerWrappedCB) {
218+
(*_self->_p->_noLongerWrappedCB)(o);
219+
};
220+
}
221+
214222
void PythonQtPrivate::registerClass(const QMetaObject* metaobject)
215223
{
216224
// we register all classes in the hierarchy
@@ -259,7 +267,7 @@ PyObject* PythonQtPrivate::wrapQObject(QObject* obj)
259267
Py_INCREF(Py_None);
260268
return Py_None;
261269
}
262-
PythonQtWrapper* wrap = _wrappedObjects.value(obj);
270+
PythonQtWrapper* wrap = findWrapperAndRemoveUnused(obj);
263271
if (!wrap) {
264272
// smuggling it in...
265273
PythonQtClassInfo* classInfo = _knownQtClasses.value(obj->metaObject()->className());
@@ -268,8 +276,6 @@ PyObject* PythonQtPrivate::wrapQObject(QObject* obj)
268276
classInfo = _knownQtClasses.value(obj->metaObject()->className());
269277
}
270278
wrap = createNewPythonQtWrapper(obj, classInfo);
271-
// insert destroyed handler
272-
connect(obj, SIGNAL(destroyed(QObject*)), this, SLOT(wrappedObjectDestroyed(QObject*)));
273279
// mlabDebugConst("MLABPython","new qobject wrapper added " << " " << wrap->_obj->className() << " " << wrap->_info->wrappedClassName().latin1());
274280
} else {
275281
Py_INCREF(wrap);
@@ -284,7 +290,7 @@ PyObject* PythonQtPrivate::wrapPtr(void* ptr, const QByteArray& name)
284290
Py_INCREF(Py_None);
285291
return Py_None;
286292
}
287-
PythonQtWrapper* wrap = _wrappedObjects.value(ptr);
293+
PythonQtWrapper* wrap = findWrapperAndRemoveUnused(ptr);
288294
if (!wrap) {
289295
PythonQtClassInfo* info = _knownQtClasses.value(name);
290296
if (!info) {
@@ -304,8 +310,6 @@ PyObject* PythonQtPrivate::wrapPtr(void* ptr, const QByteArray& name)
304310
info = _knownQtClasses.value(qptr->metaObject()->className());
305311
}
306312
wrap = createNewPythonQtWrapper(qptr, info);
307-
// insert destroyed handler
308-
connect(qptr, SIGNAL(destroyed(QObject*)), this, SLOT(wrappedObjectDestroyed(QObject*)));
309313
// mlabDebugConst("MLABPython","new qobject wrapper added " << " " << wrap->_obj->className() << " " << wrap->_info->wrappedClassName().latin1());
310314
} else {
311315
// maybe it is a PyObject, which we can return directly
@@ -360,7 +364,7 @@ PythonQtWrapper* PythonQtPrivate::createNewPythonQtWrapper(QObject* obj, PythonQ
360364
result = (PythonQtWrapper *)PythonQtWrapper_Type.tp_new(&PythonQtWrapper_Type,
361365
NULL, NULL);
362366

363-
result->_obj = obj;
367+
result->setQObject(obj);
364368
result->_info = info;
365369
result->_wrappedPtr = wrappedPtr;
366370
result->_ownedByPythonQt = false;
@@ -369,6 +373,10 @@ PythonQtWrapper* PythonQtPrivate::createNewPythonQtWrapper(QObject* obj, PythonQ
369373
_wrappedObjects.insert(wrappedPtr, result);
370374
} else {
371375
_wrappedObjects.insert(obj, result);
376+
if (obj->parent()== NULL && _wrappedCB) {
377+
// tell someone who is interested that the qobject is wrapped the first time, if it has no parent
378+
(*_wrappedCB)(obj);
379+
}
372380
}
373381
return result;
374382
}
@@ -400,8 +408,6 @@ PythonQtSignalReceiver* PythonQt::getSignalReceiver(QObject* obj)
400408
if (!r) {
401409
r = new PythonQtSignalReceiver(obj);
402410
_p->_signalReceivers.insert(obj, r);
403-
// insert destroyed handler
404-
connect(obj, SIGNAL(destroyed(QObject*)), _p ,SLOT(destroyedSignalEmitter(QObject*)));
405411
}
406412
return r;
407413
}
@@ -540,6 +546,34 @@ PythonQtObjectPtr PythonQt::parseFile(const QString& filename)
540546
return p;
541547
}
542548

549+
PythonQtObjectPtr PythonQt::createModuleFromFile(const QString& name, const QString& filename)
550+
{
551+
PythonQtObjectPtr code = parseFile(filename);
552+
PythonQtObjectPtr module = _p->createModule(name, code);
553+
return module;
554+
}
555+
556+
PythonQtObjectPtr PythonQt::createModuleFromScript(const QString& name, const QString& script)
557+
{
558+
PyErr_Clear();
559+
QString scriptCode = script;
560+
if (scriptCode.isEmpty()) {
561+
// we always need at least a linefeed
562+
scriptCode = "\n";
563+
}
564+
PythonQtObjectPtr pycode;
565+
pycode.setNewRef(Py_CompileString((char*)scriptCode.toLatin1().data(), "", Py_file_input));
566+
PythonQtObjectPtr module = _p->createModule(name, pycode);
567+
return module;
568+
}
569+
570+
PythonQtObjectPtr PythonQt::createUniqueModule()
571+
{
572+
static QString pyQtStr("PythonQt_module");
573+
QString moduleName = pyQtStr+QString::number(_uniqueModuleCount++);
574+
return createModuleFromScript(moduleName);
575+
}
576+
543577
void PythonQt::addObject(PyObject* module, const QString& name, QObject* object)
544578
{
545579
PyModule_AddObject(module, name.toLatin1().data(), _p->wrapQObject(object));
@@ -763,6 +797,8 @@ const QList<PythonQtConstructorHandler*>& PythonQt::constructorHandlers()
763797
PythonQtPrivate::PythonQtPrivate()
764798
{
765799
_importInterface = NULL;
800+
_noLongerWrappedCB = NULL;
801+
_wrappedCB = NULL;
766802
}
767803

768804
void PythonQtPrivate::addDecorators(QObject* o, bool instanceDeco, bool classDeco)
@@ -829,20 +865,9 @@ QList<PythonQtSlotInfo*> PythonQtPrivate::getDecoratorSlots(const QByteArray& cl
829865
return _knownQtDecoratorSlots.values(className);
830866
}
831867

832-
void PythonQtPrivate::wrappedObjectDestroyed(QObject* obj)
833-
{
834-
// mlabDebugConst("MLABPython","PyWrapper QObject destroyed " << o << " " << o->name() << " " << o->className());
835-
PythonQtWrapper* wrap = _wrappedObjects[obj];
836-
if (wrap) {
837-
_wrappedObjects.remove(obj);
838-
// remove the pointer but keep the wrapper alive in python
839-
wrap->_obj = NULL;
840-
}
841-
}
842-
843-
void PythonQtPrivate::destroyedSignalEmitter(QObject* obj)
868+
void PythonQtPrivate::removeSignalEmitter(QObject* obj)
844869
{
845-
_signalReceivers.take(obj);
870+
_signalReceivers.remove(obj);
846871
}
847872

848873
bool PythonQt::handleError()
@@ -892,6 +917,16 @@ void PythonQt::stdErrRedirectCB(const QString& str)
892917
emit PythonQt::self()->pythonStdErr(str);
893918
}
894919

920+
void PythonQt::setQObjectWrappedCallback(PythonQtQObjectWrappedCB* cb)
921+
{
922+
_p->_wrappedCB = cb;
923+
}
924+
925+
void PythonQt::setQObjectNoLongerWrappedCallback(PythonQtQObjectNoLongerWrappedCB* cb)
926+
{
927+
_p->_noLongerWrappedCB = cb;
928+
}
929+
895930

896931

897932
static PyMethodDef PythonQtMethods[] = {
@@ -944,3 +979,33 @@ PyObject* PythonQt::helpCalled(PythonQtClassInfo* info)
944979
return PyString_FromString(info->help().toLatin1().data());
945980
}
946981
}
982+
983+
void PythonQtPrivate::removeWrapperPointer(void* obj)
984+
{
985+
_wrappedObjects.remove(obj);
986+
}
987+
988+
PythonQtWrapper* PythonQtPrivate::findWrapperAndRemoveUnused(void* obj)
989+
{
990+
PythonQtWrapper* wrap = _wrappedObjects.value(obj);
991+
if (wrap && !wrap->_wrappedPtr && wrap->_obj == NULL) {
992+
// this is a wrapper whose QObject was already removed due to destruction
993+
// so the obj pointer has to be a new QObject with the same address...
994+
// we remove the old one and set the copy to NULL
995+
wrap->_objPointerCopy = NULL;
996+
removeWrapperPointer(obj);
997+
wrap = NULL;
998+
}
999+
return wrap;
1000+
}
1001+
1002+
PythonQtObjectPtr PythonQtPrivate::createModule(const QString& name, PyObject* pycode)
1003+
{
1004+
PythonQtObjectPtr result;
1005+
if (pycode) {
1006+
result.setNewRef(PyImport_ExecCodeModule((char*)name.toLatin1().data(), pycode));
1007+
} else {
1008+
PythonQt::self()->handleError();
1009+
}
1010+
return result;
1011+
}

src/PythonQt.h

+40-7
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ class PythonQtImportFileInterface;
6666
class PythonQtCppWrapperFactory;
6767
class PythonQtConstructorHandler;
6868

69+
typedef void PythonQtQObjectWrappedCB(QObject* object);
70+
typedef void PythonQtQObjectNoLongerWrappedCB(QObject* object);
71+
6972
//! the main interface to the Python Qt binding, realized as a singleton
7073
class PYTHONQT_EXPORT PythonQt : public QObject {
7174

@@ -137,6 +140,22 @@ class PYTHONQT_EXPORT PythonQt : public QObject {
137140
//! evaluates the given script code from file
138141
void evalFile(PyObject* module, const QString& filename);
139142

143+
//! creates the new module \c name and evaluates the given file in the context of that module
144+
//! If the \c script is empty, the module contains no initial code. You can use evalScript/evalCode to add code
145+
//! to a module later on.
146+
//! The user needs to make sure that the \c name is unique in the python module dictionary.
147+
PythonQtObjectPtr createModuleFromFile(const QString& name, const QString& filename);
148+
149+
//! creates the new module \c name and evaluates the given script in the context of that module.
150+
//! If the \c script is empty, the module contains no initial code. You can use evalScript/evalCode to add code
151+
//! to a module later on.
152+
//! The user needs to make sure that the \c name is unique in the python module dictionary.
153+
PythonQtObjectPtr createModuleFromScript(const QString& name, const QString& script = QString());
154+
155+
//! create a uniquely named module, you can use evalFile or evalScript to populate the module with
156+
//! script code
157+
PythonQtObjectPtr createUniqueModule();
158+
140159
//@{ Signal handlers
141160

142161
//! add a signal handler to the given \c signal of \c obj and connect it to a callable \c objectname in module
@@ -280,6 +299,14 @@ class PYTHONQT_EXPORT PythonQt : public QObject {
280299
//! The error is currently just output to the python stderr, future version might implement better trace printing
281300
bool handleError();
282301

302+
//! set a callback that is called when a QObject with parent == NULL is wrapped by pythonqt
303+
void setQObjectWrappedCallback(PythonQtQObjectWrappedCB* cb);
304+
//! set a callback that is called when a QObject with parent == NULL is no longer wrapped by pythonqt
305+
void setQObjectNoLongerWrappedCallback(PythonQtQObjectNoLongerWrappedCB* cb);
306+
307+
//! call the callback if it is set
308+
static void qObjectNoLongerWrappedCB(QObject* o);
309+
283310
signals:
284311
//! emitted when python outputs something to stdout (and redirection is turned on)
285312
void pythonStdOut(const QString& str);
@@ -313,6 +340,7 @@ class PYTHONQT_EXPORT PythonQt : public QObject {
313340
~PythonQt();
314341

315342
static PythonQt* _self;
343+
static int _uniqueModuleCount;
316344

317345
PythonQtPrivate* _p;
318346

@@ -331,7 +359,10 @@ class PythonQtPrivate : public QObject {
331359
bool isPythonQtObjectPtrMetaId(int id) { return _PythonQtObjectPtr_metaId == id; }
332360

333361
//! remove the wrapper ptr again
334-
void removeWrapperPointer(void* obj) { _wrappedObjects.take(obj); }
362+
void removeWrapperPointer(void* obj);
363+
364+
//! called when a signal emitting QObject is destroyed to remove the signal handler from the hash map
365+
void removeSignalEmitter(QObject* obj);
335366

336367
//! wrap the given QObject into a Python object (or return existing wrapper!)
337368
PyObject* wrapQObject(QObject* obj);
@@ -379,15 +410,14 @@ class PythonQtPrivate : public QObject {
379410
//! get the destructor slot for the given classname
380411
PythonQtSlotInfo* getDestructorSlot(const QByteArray& className) { return _destructorSlots.value(className); }
381412

382-
protected slots:
383-
//! called when a wrapped QObject is destroyed
384-
void wrappedObjectDestroyed(QObject* obj);
385-
386-
//! called when a signal emitting QObject is destroyed to remove the signal handler from the hash map
387-
void destroyedSignalEmitter(QObject* obj);
413+
//! creates the new module from the given pycode
414+
PythonQtObjectPtr createModule(const QString& name, PyObject* pycode);
388415

389416
private:
390417

418+
//! get the wrapper for a given pointer (and remove a wrapper of an already destroyed qobject)
419+
PythonQtWrapper* findWrapperAndRemoveUnused(void* obj);
420+
391421
//! stores pointer to PyObject mapping of wrapped QObjects AND C++ objects
392422
QHash<void* , PythonQtWrapper *> _wrappedObjects;
393423

@@ -412,6 +442,9 @@ protected slots:
412442
//! the importer interface (if set)
413443
PythonQtImportFileInterface* _importInterface;
414444

445+
PythonQtQObjectNoLongerWrappedCB* _noLongerWrappedCB;
446+
PythonQtQObjectWrappedCB* _wrappedCB;
447+
415448
QStringList _importIgnorePaths;
416449

417450
//! the cpp object wrapper factories

src/PythonQtConversion.cpp

+8-3
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,8 @@ return Py_None;
296296
}
297297
} else {
298298
if (wrap->_info->inherits(info.name)) {
299-
PythonQtValueStorage_ADD_VALUE(global_ptrStorage, void*, wrap->_obj, ptr);
299+
QObject* myObject = wrap->_obj;
300+
PythonQtValueStorage_ADD_VALUE(global_ptrStorage, void*, myObject, ptr);
300301
} else {
301302
// not matching
302303
}
@@ -487,6 +488,8 @@ return Py_None;
487488
if (ok) {
488489
PythonQtValueStorage_ADD_VALUE(global_valueStorage, unsigned int, val, ptr);
489490
return ptr;
491+
} else {
492+
return NULL;
490493
}
491494
}
492495
}
@@ -730,7 +733,8 @@ QVariant PythonQtConv::PyObjToQVariant(PyObject* val, int type)
730733
// is this worth anything? we loose the knowledge of the cpp object type
731734
v = qVariantFromValue(wrap->_wrappedPtr);
732735
} else {
733-
v = qVariantFromValue(wrap->_obj);
736+
QObject* myObject = wrap->_obj;
737+
v = qVariantFromValue(myObject);
734738
}
735739
return v;
736740
} else if (val->ob_type==&PyDict_Type) {
@@ -998,7 +1002,8 @@ bool PythonQtConv::ConvertPythonListToQListOfType(PyObject* obj, QList<void*>* l
9981002
}
9991003
} else {
10001004
if (wrap->_info->inherits(type)) {
1001-
list->append((void*)wrap->_obj);
1005+
QObject* myObject = wrap->_obj;
1006+
list->append((void*)myObject);
10021007
} else {
10031008
result = false;
10041009
break;

src/PythonQtImporter.cpp

+7-5
Original file line numberDiff line numberDiff line change
@@ -620,10 +620,12 @@ PythonQtImport::getCodeFromData(const QString& path, int isbytecode,int ispackag
620620
else {
621621
// mlabDebugConst("MLABPython", "compiling source " << path);
622622
code = compileSource(path, qdata);
623-
// save a pyc file if possible
624-
QDateTime time;
625-
time = hasImporter?PythonQt::importInterface()->lastModifiedDate(path):QFileInfo(path).lastModified();
626-
writeCompiledModule((PyCodeObject*)code, path+"c", time.toTime_t());
623+
if (code) {
624+
// save a pyc file if possible
625+
QDateTime time;
626+
time = hasImporter?PythonQt::importInterface()->lastModifiedDate(path):QFileInfo(path).lastModified();
627+
writeCompiledModule((PyCodeObject*)code, path+"c", time.toTime_t());
628+
}
627629
}
628630
return code;
629631
}
@@ -776,7 +778,7 @@ void PythonQtImport::init()
776778
PyObject* classobj = PyDict_GetItemString(PyModule_GetDict(mod), "PythonQtImporter");
777779
PyObject* path_hooks = PySys_GetObject("path_hooks");
778780
PyList_Append(path_hooks, classobj);
779-
781+
780782
#ifndef WIN32
781783
// reload the encodings module, because it might fail to custom import requirements (e.g. encryption).
782784
PyObject* modules = PyImport_GetModuleDict();

src/PythonQtMetaObjectWrapper.cpp

+7-2
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ static PyObject *PythonQtMetaObjectWrapper_getattro(PyObject *obj,PyObject *name
143143
if (member._type == PythonQtMemberInfo::Slot && member._slot->isClassDecorator()) {
144144
return PythonQtSlotFunction_New(member._slot, obj, NULL);
145145
}
146-
146+
147147
// look for the interal methods (className(), help())
148148
PyObject* internalMethod = Py_FindMethod( PythonQtMetaObjectWrapper_methods, obj, (char*)attributeName);
149149
if (internalMethod) {
@@ -194,6 +194,11 @@ static int PythonQtMetaObjectWrapper_compare(PyObject * obj1, PyObject * obj2)
194194
}
195195
}
196196

197+
static long PythonQtMetaObjectWrapper_hash(PythonQtMetaObjectWrapper *obj)
198+
{
199+
return reinterpret_cast<long>(obj->_info);
200+
}
201+
197202
PyTypeObject PythonQtMetaObjectWrapper_Type = {
198203
PyObject_HEAD_INIT(NULL)
199204
0, /*ob_size*/
@@ -209,7 +214,7 @@ PyTypeObject PythonQtMetaObjectWrapper_Type = {
209214
0, /*tp_as_number*/
210215
0, /*tp_as_sequence*/
211216
0, /*tp_as_mapping*/
212-
0, /*tp_hash */
217+
(hashfunc)PythonQtMetaObjectWrapper_hash, /*tp_hash */
213218
PythonQtMetaObjectWrapper_call, /*tp_call*/
214219
0, /*tp_str*/
215220
PythonQtMetaObjectWrapper_getattro, /*tp_getattro*/

0 commit comments

Comments
 (0)