@@ -52,6 +52,7 | |||
|
52 | 52 | #include <pydebug.h> |
|
53 | 53 | |
|
54 | 54 | PythonQt* PythonQt::_self = NULL; |
|
55 | int PythonQt::_uniqueModuleCount = 0; | |
|
55 | 56 | |
|
56 | 57 | |
|
57 | 58 | void PythonQt::init(int flags) |
@@ -211,6 +212,13 void PythonQt::registerClass(const QMetaObject* metaobject) | |||
|
211 | 212 | _p->registerClass(metaobject); |
|
212 | 213 | } |
|
213 | 214 | |
|
215 | void PythonQt::qObjectNoLongerWrappedCB(QObject* o) | |
|
216 | { | |
|
217 | if (_self->_p->_noLongerWrappedCB) { | |
|
218 | (*_self->_p->_noLongerWrappedCB)(o); | |
|
219 | }; | |
|
220 | } | |
|
221 | ||
|
214 | 222 | void PythonQtPrivate::registerClass(const QMetaObject* metaobject) |
|
215 | 223 | { |
|
216 | 224 | // we register all classes in the hierarchy |
@@ -259,7 +267,7 PyObject* PythonQtPrivate::wrapQObject(QObject* obj) | |||
|
259 | 267 | Py_INCREF(Py_None); |
|
260 | 268 | return Py_None; |
|
261 | 269 | } |
|
262 |
PythonQtWrapper* wrap = |
|
|
270 | PythonQtWrapper* wrap = findWrapperAndRemoveUnused(obj); | |
|
263 | 271 | if (!wrap) { |
|
264 | 272 | // smuggling it in... |
|
265 | 273 | PythonQtClassInfo* classInfo = _knownQtClasses.value(obj->metaObject()->className()); |
@@ -268,8 +276,6 PyObject* PythonQtPrivate::wrapQObject(QObject* obj) | |||
|
268 | 276 | classInfo = _knownQtClasses.value(obj->metaObject()->className()); |
|
269 | 277 | } |
|
270 | 278 | wrap = createNewPythonQtWrapper(obj, classInfo); |
|
271 | // insert destroyed handler | |
|
272 | connect(obj, SIGNAL(destroyed(QObject*)), this, SLOT(wrappedObjectDestroyed(QObject*))); | |
|
273 | 279 | // mlabDebugConst("MLABPython","new qobject wrapper added " << " " << wrap->_obj->className() << " " << wrap->_info->wrappedClassName().latin1()); |
|
274 | 280 | } else { |
|
275 | 281 | Py_INCREF(wrap); |
@@ -284,7 +290,7 PyObject* PythonQtPrivate::wrapPtr(void* ptr, const QByteArray& name) | |||
|
284 | 290 | Py_INCREF(Py_None); |
|
285 | 291 | return Py_None; |
|
286 | 292 | } |
|
287 |
PythonQtWrapper* wrap = |
|
|
293 | PythonQtWrapper* wrap = findWrapperAndRemoveUnused(ptr); | |
|
288 | 294 | if (!wrap) { |
|
289 | 295 | PythonQtClassInfo* info = _knownQtClasses.value(name); |
|
290 | 296 | if (!info) { |
@@ -304,8 +310,6 PyObject* PythonQtPrivate::wrapPtr(void* ptr, const QByteArray& name) | |||
|
304 | 310 | info = _knownQtClasses.value(qptr->metaObject()->className()); |
|
305 | 311 | } |
|
306 | 312 | wrap = createNewPythonQtWrapper(qptr, info); |
|
307 | // insert destroyed handler | |
|
308 | connect(qptr, SIGNAL(destroyed(QObject*)), this, SLOT(wrappedObjectDestroyed(QObject*))); | |
|
309 | 313 | // mlabDebugConst("MLABPython","new qobject wrapper added " << " " << wrap->_obj->className() << " " << wrap->_info->wrappedClassName().latin1()); |
|
310 | 314 | } else { |
|
311 | 315 | // maybe it is a PyObject, which we can return directly |
@@ -360,7 +364,7 PythonQtWrapper* PythonQtPrivate::createNewPythonQtWrapper(QObject* obj, PythonQ | |||
|
360 | 364 | result = (PythonQtWrapper *)PythonQtWrapper_Type.tp_new(&PythonQtWrapper_Type, |
|
361 | 365 | NULL, NULL); |
|
362 | 366 | |
|
363 | result->_obj = obj; | |
|
367 | result->setQObject(obj); | |
|
364 | 368 | result->_info = info; |
|
365 | 369 | result->_wrappedPtr = wrappedPtr; |
|
366 | 370 | result->_ownedByPythonQt = false; |
@@ -369,6 +373,10 PythonQtWrapper* PythonQtPrivate::createNewPythonQtWrapper(QObject* obj, PythonQ | |||
|
369 | 373 | _wrappedObjects.insert(wrappedPtr, result); |
|
370 | 374 | } else { |
|
371 | 375 | _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 | } | |
|
372 | 380 | } |
|
373 | 381 | return result; |
|
374 | 382 | } |
@@ -400,8 +408,6 PythonQtSignalReceiver* PythonQt::getSignalReceiver(QObject* obj) | |||
|
400 | 408 | if (!r) { |
|
401 | 409 | r = new PythonQtSignalReceiver(obj); |
|
402 | 410 | _p->_signalReceivers.insert(obj, r); |
|
403 | // insert destroyed handler | |
|
404 | connect(obj, SIGNAL(destroyed(QObject*)), _p ,SLOT(destroyedSignalEmitter(QObject*))); | |
|
405 | 411 | } |
|
406 | 412 | return r; |
|
407 | 413 | } |
@@ -540,6 +546,34 PythonQtObjectPtr PythonQt::parseFile(const QString& filename) | |||
|
540 | 546 | return p; |
|
541 | 547 | } |
|
542 | 548 | |
|
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 | ||
|
543 | 577 | void PythonQt::addObject(PyObject* module, const QString& name, QObject* object) |
|
544 | 578 | { |
|
545 | 579 | PyModule_AddObject(module, name.toLatin1().data(), _p->wrapQObject(object)); |
@@ -763,6 +797,8 const QList<PythonQtConstructorHandler*>& PythonQt::constructorHandlers() | |||
|
763 | 797 | PythonQtPrivate::PythonQtPrivate() |
|
764 | 798 | { |
|
765 | 799 | _importInterface = NULL; |
|
800 | _noLongerWrappedCB = NULL; | |
|
801 | _wrappedCB = NULL; | |
|
766 | 802 | } |
|
767 | 803 | |
|
768 | 804 | void PythonQtPrivate::addDecorators(QObject* o, bool instanceDeco, bool classDeco) |
@@ -829,20 +865,9 QList<PythonQtSlotInfo*> PythonQtPrivate::getDecoratorSlots(const QByteArray& cl | |||
|
829 | 865 | return _knownQtDecoratorSlots.values(className); |
|
830 | 866 | } |
|
831 | 867 | |
|
832 |
void PythonQtPrivate:: |
|
|
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) | |
|
844 | 869 | { |
|
845 |
_signalReceivers. |
|
|
870 | _signalReceivers.remove(obj); | |
|
846 | 871 | } |
|
847 | 872 | |
|
848 | 873 | bool PythonQt::handleError() |
@@ -892,6 +917,16 void PythonQt::stdErrRedirectCB(const QString& str) | |||
|
892 | 917 | emit PythonQt::self()->pythonStdErr(str); |
|
893 | 918 | } |
|
894 | 919 | |
|
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 | ||
|
895 | 930 | |
|
896 | 931 | |
|
897 | 932 | static PyMethodDef PythonQtMethods[] = { |
@@ -944,3 +979,33 PyObject* PythonQt::helpCalled(PythonQtClassInfo* info) | |||
|
944 | 979 | return PyString_FromString(info->help().toLatin1().data()); |
|
945 | 980 | } |
|
946 | 981 | } |
|
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 | } |
@@ -66,6 +66,9 class PythonQtImportFileInterface; | |||
|
66 | 66 | class PythonQtCppWrapperFactory; |
|
67 | 67 | class PythonQtConstructorHandler; |
|
68 | 68 | |
|
69 | typedef void PythonQtQObjectWrappedCB(QObject* object); | |
|
70 | typedef void PythonQtQObjectNoLongerWrappedCB(QObject* object); | |
|
71 | ||
|
69 | 72 | //! the main interface to the Python Qt binding, realized as a singleton |
|
70 | 73 | class PYTHONQT_EXPORT PythonQt : public QObject { |
|
71 | 74 | |
@@ -137,6 +140,22 public: | |||
|
137 | 140 | //! evaluates the given script code from file |
|
138 | 141 | void evalFile(PyObject* module, const QString& filename); |
|
139 | 142 | |
|
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 | ||
|
140 | 159 | //@{ Signal handlers |
|
141 | 160 | |
|
142 | 161 | //! 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 public: | |||
|
280 | 299 | //! The error is currently just output to the python stderr, future version might implement better trace printing |
|
281 | 300 | bool handleError(); |
|
282 | 301 | |
|
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 | ||
|
283 | 310 | signals: |
|
284 | 311 | //! emitted when python outputs something to stdout (and redirection is turned on) |
|
285 | 312 | void pythonStdOut(const QString& str); |
@@ -313,6 +340,7 private: | |||
|
313 | 340 | ~PythonQt(); |
|
314 | 341 | |
|
315 | 342 | static PythonQt* _self; |
|
343 | static int _uniqueModuleCount; | |
|
316 | 344 | |
|
317 | 345 | PythonQtPrivate* _p; |
|
318 | 346 | |
@@ -331,7 +359,10 public: | |||
|
331 | 359 | bool isPythonQtObjectPtrMetaId(int id) { return _PythonQtObjectPtr_metaId == id; } |
|
332 | 360 | |
|
333 | 361 | //! remove the wrapper ptr again |
|
334 |
void removeWrapperPointer(void* 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); | |
|
335 | 366 | |
|
336 | 367 | //! wrap the given QObject into a Python object (or return existing wrapper!) |
|
337 | 368 | PyObject* wrapQObject(QObject* obj); |
@@ -379,15 +410,14 public: | |||
|
379 | 410 | //! get the destructor slot for the given classname |
|
380 | 411 | PythonQtSlotInfo* getDestructorSlot(const QByteArray& className) { return _destructorSlots.value(className); } |
|
381 | 412 | |
|
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); | |
|
388 | 415 | |
|
389 | 416 | private: |
|
390 | 417 | |
|
418 | //! get the wrapper for a given pointer (and remove a wrapper of an already destroyed qobject) | |
|
419 | PythonQtWrapper* findWrapperAndRemoveUnused(void* obj); | |
|
420 | ||
|
391 | 421 | //! stores pointer to PyObject mapping of wrapped QObjects AND C++ objects |
|
392 | 422 | QHash<void* , PythonQtWrapper *> _wrappedObjects; |
|
393 | 423 | |
@@ -412,6 +442,9 private: | |||
|
412 | 442 | //! the importer interface (if set) |
|
413 | 443 | PythonQtImportFileInterface* _importInterface; |
|
414 | 444 | |
|
445 | PythonQtQObjectNoLongerWrappedCB* _noLongerWrappedCB; | |
|
446 | PythonQtQObjectWrappedCB* _wrappedCB; | |
|
447 | ||
|
415 | 448 | QStringList _importIgnorePaths; |
|
416 | 449 | |
|
417 | 450 | //! the cpp object wrapper factories |
@@ -296,7 +296,8 return Py_None; | |||
|
296 | 296 | } |
|
297 | 297 | } else { |
|
298 | 298 | 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); | |
|
300 | 301 | } else { |
|
301 | 302 | // not matching |
|
302 | 303 | } |
@@ -487,6 +488,8 return Py_None; | |||
|
487 | 488 | if (ok) { |
|
488 | 489 | PythonQtValueStorage_ADD_VALUE(global_valueStorage, unsigned int, val, ptr); |
|
489 | 490 | return ptr; |
|
491 | } else { | |
|
492 | return NULL; | |
|
490 | 493 | } |
|
491 | 494 | } |
|
492 | 495 | } |
@@ -730,7 +733,8 QVariant PythonQtConv::PyObjToQVariant(PyObject* val, int type) | |||
|
730 | 733 | // is this worth anything? we loose the knowledge of the cpp object type |
|
731 | 734 | v = qVariantFromValue(wrap->_wrappedPtr); |
|
732 | 735 | } else { |
|
733 | v = qVariantFromValue(wrap->_obj); | |
|
736 | QObject* myObject = wrap->_obj; | |
|
737 | v = qVariantFromValue(myObject); | |
|
734 | 738 | } |
|
735 | 739 | return v; |
|
736 | 740 | } else if (val->ob_type==&PyDict_Type) { |
@@ -998,7 +1002,8 bool PythonQtConv::ConvertPythonListToQListOfType(PyObject* obj, QList<void*>* l | |||
|
998 | 1002 | } |
|
999 | 1003 | } else { |
|
1000 | 1004 | if (wrap->_info->inherits(type)) { |
|
1001 | list->append((void*)wrap->_obj); | |
|
1005 | QObject* myObject = wrap->_obj; | |
|
1006 | list->append((void*)myObject); | |
|
1002 | 1007 | } else { |
|
1003 | 1008 | result = false; |
|
1004 | 1009 | break; |
@@ -620,10 +620,12 PythonQtImport::getCodeFromData(const QString& path, int isbytecode,int ispackag | |||
|
620 | 620 | else { |
|
621 | 621 | // mlabDebugConst("MLABPython", "compiling source " << path); |
|
622 | 622 | 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 | } | |
|
627 | 629 | } |
|
628 | 630 | return code; |
|
629 | 631 | } |
@@ -776,7 +778,7 void PythonQtImport::init() | |||
|
776 | 778 | PyObject* classobj = PyDict_GetItemString(PyModule_GetDict(mod), "PythonQtImporter"); |
|
777 | 779 | PyObject* path_hooks = PySys_GetObject("path_hooks"); |
|
778 | 780 | PyList_Append(path_hooks, classobj); |
|
779 | ||
|
781 | ||
|
780 | 782 | #ifndef WIN32 |
|
781 | 783 | // reload the encodings module, because it might fail to custom import requirements (e.g. encryption). |
|
782 | 784 | PyObject* modules = PyImport_GetModuleDict(); |
@@ -143,7 +143,7 static PyObject *PythonQtMetaObjectWrapper_getattro(PyObject *obj,PyObject *name | |||
|
143 | 143 | if (member._type == PythonQtMemberInfo::Slot && member._slot->isClassDecorator()) { |
|
144 | 144 | return PythonQtSlotFunction_New(member._slot, obj, NULL); |
|
145 | 145 | } |
|
146 | ||
|
146 | ||
|
147 | 147 | // look for the interal methods (className(), help()) |
|
148 | 148 | PyObject* internalMethod = Py_FindMethod( PythonQtMetaObjectWrapper_methods, obj, (char*)attributeName); |
|
149 | 149 | if (internalMethod) { |
@@ -194,6 +194,11 static int PythonQtMetaObjectWrapper_compare(PyObject * obj1, PyObject * obj2) | |||
|
194 | 194 | } |
|
195 | 195 | } |
|
196 | 196 | |
|
197 | static long PythonQtMetaObjectWrapper_hash(PythonQtMetaObjectWrapper *obj) | |
|
198 | { | |
|
199 | return reinterpret_cast<long>(obj->_info); | |
|
200 | } | |
|
201 | ||
|
197 | 202 | PyTypeObject PythonQtMetaObjectWrapper_Type = { |
|
198 | 203 | PyObject_HEAD_INIT(NULL) |
|
199 | 204 | 0, /*ob_size*/ |
@@ -209,7 +214,7 PyTypeObject PythonQtMetaObjectWrapper_Type = { | |||
|
209 | 214 | 0, /*tp_as_number*/ |
|
210 | 215 | 0, /*tp_as_sequence*/ |
|
211 | 216 | 0, /*tp_as_mapping*/ |
|
212 |
|
|
|
217 | (hashfunc)PythonQtMetaObjectWrapper_hash, /*tp_hash */ | |
|
213 | 218 | PythonQtMetaObjectWrapper_call, /*tp_call*/ |
|
214 | 219 | 0, /*tp_str*/ |
|
215 | 220 | PythonQtMetaObjectWrapper_getattro, /*tp_getattro*/ |
@@ -45,12 +45,46 | |||
|
45 | 45 | #include "PythonQtConversion.h" |
|
46 | 46 | #include <QMetaObject> |
|
47 | 47 | #include <QMetaMethod> |
|
48 | #include "funcobject.h" | |
|
48 | 49 | |
|
49 | 50 | void PythonQtSignalTarget::call(void **arguments) const |
|
50 | 51 | { |
|
52 | // Note: we check if the callable is a PyFunctionObject and has a fixed number of arguments | |
|
53 | // if that is the case, we only pass these arguments to python and skip the additional arguments from the signal | |
|
54 | ||
|
55 | int numPythonArgs = -1; | |
|
56 | if (PyFunction_Check(_callable)) { | |
|
57 | PyObject* o = _callable; | |
|
58 | PyFunctionObject* func = (PyFunctionObject*)o; | |
|
59 | PyCodeObject* code = (PyCodeObject*)func->func_code; | |
|
60 | if (!(code->co_flags & 0x04)) { | |
|
61 | numPythonArgs = code->co_argcount; | |
|
62 | } else { | |
|
63 | // variable numbers of arguments allowed | |
|
64 | } | |
|
65 | } else if (PyMethod_Check(_callable)) { | |
|
66 | PyObject* o = _callable; | |
|
67 | PyMethodObject* method = (PyMethodObject*)o; | |
|
68 | if (PyFunction_Check(method->im_func)) { | |
|
69 | PyFunctionObject* func = (PyFunctionObject*)method->im_func; | |
|
70 | PyCodeObject* code = (PyCodeObject*)func->func_code; | |
|
71 | if (!(code->co_flags & 0x04)) { | |
|
72 | numPythonArgs = code->co_argcount - 1; // we subtract one because the first is "self" | |
|
73 | } else { | |
|
74 | // variable numbers of arguments allowed | |
|
75 | } | |
|
76 | } | |
|
77 | } | |
|
78 | ||
|
51 | 79 | const PythonQtMethodInfo* m = methodInfo(); |
|
52 | // paramterCount includes return value: | |
|
80 | // parameterCount includes return value: | |
|
53 | 81 | int count = m->parameterCount(); |
|
82 | if (numPythonArgs!=-1) { | |
|
83 | if (count>numPythonArgs+1) { | |
|
84 | // take less arguments | |
|
85 | count = numPythonArgs+1; | |
|
86 | } | |
|
87 | } | |
|
54 | 88 | |
|
55 | 89 | PyObject* pargs = NULL; |
|
56 | 90 | if (count>1) { |
@@ -97,6 +131,7 PythonQtSignalReceiver::PythonQtSignalReceiver(QObject* obj):PythonQtSignalRecei | |||
|
97 | 131 | |
|
98 | 132 | PythonQtSignalReceiver::~PythonQtSignalReceiver() |
|
99 | 133 | { |
|
134 | PythonQt::priv()->removeSignalEmitter(_obj); | |
|
100 | 135 | } |
|
101 | 136 | |
|
102 | 137 |
@@ -70,10 +70,18 static PyObject *PythonQtStdOutRedirect_write(PyObject *self, PyObject *args) | |||
|
70 | 70 | return Py_BuildValue(""); |
|
71 | 71 | } |
|
72 | 72 | |
|
73 | static PyObject *PythonQtStdOutRedirect_flush(PyObject *self, PyObject *args) | |
|
74 | { | |
|
75 | return Py_BuildValue(""); | |
|
76 | } | |
|
77 | ||
|
78 | ||
|
73 | 79 | |
|
74 | 80 | static PyMethodDef PythonQtStdOutRedirect_methods[] = { |
|
75 | 81 | {"write", (PyCFunction)PythonQtStdOutRedirect_write, METH_VARARGS, |
|
76 |
|
|
|
82 | "redirect the writing to a callback"}, | |
|
83 | {"flush", (PyCFunction)PythonQtStdOutRedirect_flush, METH_VARARGS, | |
|
84 | "flush the output, currently not implemented but needed for logging framework" | |
|
77 | 85 | }, |
|
78 | 86 | {NULL} /* Sentinel */ |
|
79 | 87 | }; |
@@ -171,6 +171,18 QString qVariantToString(const QVariant& v) { | |||
|
171 | 171 | case QVariant::Time: |
|
172 | 172 | r = v.toTime().toString(Qt::ISODate); |
|
173 | 173 | break; |
|
174 | case QVariant::Pixmap: | |
|
175 | { | |
|
176 | QPixmap p = qvariant_cast<QPixmap>(v); | |
|
177 | r = QString("Pixmap ") + QString::number(p.width()) + ", " + QString::number(p.height()); | |
|
178 | } | |
|
179 | break; | |
|
180 | case QVariant::Image: | |
|
181 | { | |
|
182 | QImage img = qvariant_cast<QImage>(v); | |
|
183 | r = QString("Image ") + QString::number(img.width()) + ", " + QString::number(img.height()); | |
|
184 | } | |
|
185 | break; | |
|
174 | 186 | //TODO: add more printing for other variant types |
|
175 | 187 | default: |
|
176 | 188 | r = v.toString(); |
@@ -182,6 +194,9 static PyObject * PythonQtVariantWrapper_str(PyObject * obj) | |||
|
182 | 194 | { |
|
183 | 195 | PythonQtVariantWrapper* wt = (PythonQtVariantWrapper*)obj; |
|
184 | 196 | QString val = qVariantToString(*wt->_variant); |
|
197 | if (val.isEmpty()) { | |
|
198 | val = wt->_variant->typeName(); | |
|
199 | } | |
|
185 | 200 | return PyString_FromFormat("%s", val.toLatin1().constData()); |
|
186 | 201 | } |
|
187 | 202 |
@@ -49,9 +49,9 | |||
|
49 | 49 | static void PythonQtWrapper_dealloc(PythonQtWrapper* self) |
|
50 | 50 | { |
|
51 | 51 | if (self->_wrappedPtr) { |
|
52 | ||
|
52 | ||
|
53 | 53 | //mlabDebugConst("Python","c++ wrapper removed " << self->_wrappedPtr << " " << self->_obj->className() << " " << self->_info->wrappedClassName().latin1()); |
|
54 | ||
|
54 | ||
|
55 | 55 | PythonQt::priv()->removeWrapperPointer(self->_wrappedPtr); |
|
56 | 56 | // we own our qobject, so we delete it now: |
|
57 | 57 | delete self->_obj; |
@@ -68,27 +68,37 static void PythonQtWrapper_dealloc(PythonQtWrapper* self) | |||
|
68 | 68 | // TODO: print a warning? we can not destroy that object |
|
69 | 69 | } |
|
70 | 70 | } |
|
71 | } else if (self->_obj) { | |
|
71 | } else { | |
|
72 | 72 | //mlabDebugConst("Python","qobject wrapper removed " << self->_obj->className() << " " << self->_info->wrappedClassName().latin1()); |
|
73 | PythonQt::priv()->removeWrapperPointer(self->_obj); | |
|
74 | if (self->_ownedByPythonQt) { | |
|
75 | if (!self->_obj->parent()) { | |
|
76 |
|
|
|
77 | self->_obj = NULL; | |
|
73 | if (self->_objPointerCopy) { | |
|
74 | PythonQt::priv()->removeWrapperPointer(self->_objPointerCopy); | |
|
75 | } | |
|
76 | if (self->_obj) { | |
|
77 | if (self->_ownedByPythonQt) { | |
|
78 | if (!self->_obj->parent()) { | |
|
79 | delete self->_obj; | |
|
80 | } | |
|
81 | } else { | |
|
82 | if (self->_obj->parent()==NULL) { | |
|
83 | // tell someone who is interested that the qobject is no longer wrapped, if it has no parent | |
|
84 | PythonQt::qObjectNoLongerWrappedCB(self->_obj); | |
|
85 | } | |
|
78 | 86 | } |
|
79 | 87 | } |
|
80 | 88 | } |
|
89 | self->_obj = NULL; | |
|
90 | self->_obj.~QPointer<QObject>(); | |
|
81 | 91 | self->ob_type->tp_free((PyObject*)self); |
|
82 | 92 | } |
|
83 | 93 | |
|
84 | 94 | static PyObject* PythonQtWrapper_new(PyTypeObject *type, PyObject *args, PyObject *kwds) |
|
85 | 95 | { |
|
86 | 96 | PythonQtWrapper *self; |
|
87 | ||
|
97 | ||
|
88 | 98 | self = (PythonQtWrapper *)type->tp_alloc(type, 0); |
|
89 | 99 | if (self != NULL) { |
|
90 | 100 | self->_info = NULL; |
|
91 | self->_obj = NULL; | |
|
101 | new (&self->_obj) QPointer<QObject>(); | |
|
92 | 102 | self->_wrappedPtr = NULL; |
|
93 | 103 | self->_ownedByPythonQt = false; |
|
94 | 104 | } |
@@ -126,21 +136,21 static PyObject *PythonQtWrapper_getattro(PyObject *obj,PyObject *name) | |||
|
126 | 136 | { |
|
127 | 137 | const char *attributeName; |
|
128 | 138 | PythonQtWrapper *wt = (PythonQtWrapper *)obj; |
|
129 | ||
|
139 | ||
|
130 | 140 | if ((attributeName = PyString_AsString(name)) == NULL) { |
|
131 | 141 | return NULL; |
|
132 | 142 | } |
|
133 | ||
|
143 | ||
|
134 | 144 | if (!wt->_obj && !wt->_wrappedPtr) { |
|
135 | 145 | QString error = QString("Trying to read attribute '") + attributeName + "' from a destroyed " + wt->_info->className() + " object"; |
|
136 | 146 | PyErr_SetString(PyExc_ValueError, error.toLatin1().data()); |
|
137 | 147 | return NULL; |
|
138 | 148 | } |
|
139 | ||
|
149 | ||
|
140 | 150 | // mlabDebugConst("Python","get " << attributeName); |
|
141 | ||
|
151 | ||
|
142 | 152 | // TODO: dynamic properties are missing |
|
143 | ||
|
153 | ||
|
144 | 154 | PythonQtMemberInfo member = wt->_info->member(attributeName); |
|
145 | 155 | switch (member._type) { |
|
146 | 156 | case PythonQtMemberInfo::Property: |
@@ -155,7 +165,7 static PyObject *PythonQtWrapper_getattro(PyObject *obj,PyObject *name) | |||
|
155 | 165 | return PyInt_FromLong(member._enumValue); |
|
156 | 166 | break; |
|
157 | 167 | } |
|
158 | ||
|
168 | ||
|
159 | 169 | // look for the interal methods (className(), help()) |
|
160 | 170 | PyObject* internalMethod = Py_FindMethod( PythonQtWrapper_methods, obj, (char*)attributeName); |
|
161 | 171 | if (internalMethod) { |
@@ -186,7 +196,7 static PyObject *PythonQtWrapper_getattro(PyObject *obj,PyObject *name) | |||
|
186 | 196 | return dict; |
|
187 | 197 | } |
|
188 | 198 | |
|
189 | ||
|
199 | ||
|
190 | 200 | QString error = QString(wt->_info->className()) + " has no attribute named '" + QString(attributeName) + "'"; |
|
191 | 201 | PyErr_SetString(PyExc_AttributeError, error.toLatin1().data()); |
|
192 | 202 | return NULL; |
@@ -197,16 +207,16 static int PythonQtWrapper_setattro(PyObject *obj,PyObject *name,PyObject *value | |||
|
197 | 207 | QString error; |
|
198 | 208 | char *attributeName; |
|
199 | 209 | PythonQtWrapper *wt = (PythonQtWrapper *)obj; |
|
200 | ||
|
210 | ||
|
201 | 211 | if ((attributeName = PyString_AsString(name)) == NULL) |
|
202 | 212 | return -1; |
|
203 | ||
|
213 | ||
|
204 | 214 | if (!wt->_obj) { |
|
205 | 215 | error = QString("Trying to set attribute '") + attributeName + "' on a destroyed " + wt->_info->className() + " object"; |
|
206 | 216 | PyErr_SetString(PyExc_AttributeError, error.toLatin1().data()); |
|
207 | 217 | return -1; |
|
208 | 218 | } |
|
209 | ||
|
219 | ||
|
210 | 220 | PythonQtMemberInfo member = wt->_info->member(attributeName); |
|
211 | 221 | if (member._type == PythonQtMemberInfo::Property) { |
|
212 | 222 | QMetaProperty prop = member._property; |
@@ -240,7 +250,7 static int PythonQtWrapper_setattro(PyObject *obj,PyObject *name,PyObject *value | |||
|
240 | 250 | error = QString("EnumValue '") + attributeName + "' can not be overwritten on " + wt->_info->className() + " object"; |
|
241 | 251 | } |
|
242 | 252 | } |
|
243 | ||
|
253 | ||
|
244 | 254 | PyErr_SetString(PyExc_AttributeError, error.toLatin1().data()); |
|
245 | 255 | return -1; |
|
246 | 256 | } |
@@ -248,14 +258,15 static int PythonQtWrapper_setattro(PyObject *obj,PyObject *name,PyObject *value | |||
|
248 | 258 | static PyObject * PythonQtWrapper_repr(PyObject * obj) |
|
249 | 259 | { |
|
250 | 260 | PythonQtWrapper* wt = (PythonQtWrapper*)obj; |
|
261 | QObject *qobj = wt->_obj; | |
|
251 | 262 | if (wt->_wrappedPtr) { |
|
252 | 263 | if (wt->_obj) { |
|
253 |
return PyString_FromFormat("%s (C++ Object |
|
|
264 | return PyString_FromFormat("%s (C++ Object %p wrapped by %s %p))", wt->_info->className(), wt->_wrappedPtr, wt->_obj->metaObject()->className(), qobj); | |
|
254 | 265 | } else { |
|
255 |
return PyString_FromFormat("%s (C++ Object |
|
|
266 | return PyString_FromFormat("%s (C++ Object %p unwrapped)", wt->_info->className(), wt->_wrappedPtr); | |
|
256 | 267 | } |
|
257 | 268 | } else { |
|
258 |
return PyString_FromFormat("%s (QObject |
|
|
269 | return PyString_FromFormat("%s (QObject %p)", wt->_info->className(), qobj); | |
|
259 | 270 | } |
|
260 | 271 | } |
|
261 | 272 | |
@@ -263,7 +274,7 static int PythonQtWrapper_compare(PyObject * obj1, PyObject * obj2) | |||
|
263 | 274 | { |
|
264 | 275 | if (obj1->ob_type == &PythonQtWrapper_Type && |
|
265 | 276 | obj2->ob_type == &PythonQtWrapper_Type) { |
|
266 | ||
|
277 | ||
|
267 | 278 | PythonQtWrapper* w1 = (PythonQtWrapper*)obj1; |
|
268 | 279 | PythonQtWrapper* w2 = (PythonQtWrapper*)obj2; |
|
269 | 280 | if (w1->_wrappedPtr != NULL) { |
@@ -288,6 +299,19 static int PythonQtWrapper_nonzero(PyObject *obj) | |||
|
288 | 299 | return (wt->_wrappedPtr == NULL && wt->_obj == NULL)?0:1; |
|
289 | 300 | } |
|
290 | 301 | |
|
302 | ||
|
303 | static long PythonQtWrapper_hash(PythonQtWrapper *obj) | |
|
304 | { | |
|
305 | if (obj->_wrappedPtr != NULL) { | |
|
306 | return reinterpret_cast<long>(obj->_wrappedPtr); | |
|
307 | } else { | |
|
308 | QObject* qobj = obj->_obj; // get pointer from QPointer wrapper | |
|
309 | return reinterpret_cast<long>(qobj); | |
|
310 | } | |
|
311 | } | |
|
312 | ||
|
313 | ||
|
314 | ||
|
291 | 315 | // we override nb_nonzero, so that one can do 'if' expressions to test for a NULL ptr |
|
292 | 316 | static PyNumberMethods PythonQtWrapper_as_number = { |
|
293 | 317 | 0, /* nb_add */ |
@@ -345,7 +369,7 PyTypeObject PythonQtWrapper_Type = { | |||
|
345 | 369 | &PythonQtWrapper_as_number, /*tp_as_number*/ |
|
346 | 370 | 0, /*tp_as_sequence*/ |
|
347 | 371 | 0, /*tp_as_mapping*/ |
|
348 |
|
|
|
372 | (hashfunc)PythonQtWrapper_hash, /*tp_hash */ | |
|
349 | 373 | 0, /*tp_call*/ |
|
350 | 374 | 0, /*tp_str*/ |
|
351 | 375 | PythonQtWrapper_getattro, /*tp_getattro*/ |
@@ -45,6 +45,7 | |||
|
45 | 45 | #include <Python.h> |
|
46 | 46 | |
|
47 | 47 | #include "PythonQtSystem.h" |
|
48 | #include <QPointer> | |
|
48 | 49 | |
|
49 | 50 | #include "structmember.h" |
|
50 | 51 | #include "methodobject.h" |
@@ -61,8 +62,18 extern PYTHONQT_EXPORT PyTypeObject PythonQtWrapper_Type; | |||
|
61 | 62 | typedef struct { |
|
62 | 63 | PyObject_HEAD |
|
63 | 64 | |
|
65 | //! set the QObject pointer | |
|
66 | void setQObject(QObject* object) { | |
|
67 | _obj = object; | |
|
68 | _objPointerCopy = object; | |
|
69 | } | |
|
70 | ||
|
64 | 71 | //! pointer to the wrapped Qt object or if _wrappedPtr is set, the Qt object that wraps the C++ Ptr |
|
65 |
QObject |
|
|
72 | QPointer<QObject> _obj; | |
|
73 | //! a copy of the _obj pointer, which is required because the wrapper needs to | |
|
74 | //! deregister itself via the _obj pointer, even when the QPointer<QObject> object was destroyed | |
|
75 | void* _objPointerCopy; | |
|
76 | ||
|
66 | 77 | //! optional C++ object Ptr that is wrapped by the above _obj |
|
67 | 78 | void* _wrappedPtr; |
|
68 | 79 |
General Comments 0
You need to be logged in to leave comments.
Login now