##// END OF EJS Templates
added support for setattr to allow derived classes to add own attributes...
florianlink -
r30:a523f73e74c6
parent child
Show More
@@ -1,293 +1,285
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 PythonQtClassWrapper.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 "PythonQtClassWrapper.h"
43 43 #include <QObject>
44 44
45 45 #include "PythonQt.h"
46 46 #include "PythonQtSlot.h"
47 47 #include "PythonQtClassInfo.h"
48 48 #include "PythonQtConversion.h"
49 49 #include "PythonQtInstanceWrapper.h"
50 50
51 51 static PyObject* PythonQtClassWrapper_alloc(PyTypeObject *self, Py_ssize_t nitems)
52 52 {
53 53 // call the default type alloc
54 54 PyObject* obj = PyType_Type.tp_alloc(self, nitems);
55 55
56 56 // take current class type, if we are called via newPythonQtClassWrapper()
57 57 PythonQtClassWrapper* wrap = (PythonQtClassWrapper*)obj;
58 58 wrap->_classInfo = PythonQt::priv()->currentClassInfoForClassWrapperCreation();
59 59
60 60 return obj;
61 61 }
62 62
63 63
64 64 static int PythonQtClassWrapper_init(PythonQtClassWrapper* self, PyObject* args, PyObject* kwds)
65 65 {
66 66 // call the default type init
67 67 if (PyType_Type.tp_init((PyObject *)self, args, kwds) < 0) {
68 68 return -1;
69 69 }
70 70
71 71 // if we have no CPP class information, try our base class
72 72 if (!self->classInfo()) {
73 73 PyTypeObject* superType = ((PyTypeObject *)self)->tp_base;
74 74
75 75 if (!superType || (superType->ob_type != &PythonQtClassWrapper_Type)) {
76 76 PyErr_Format(PyExc_TypeError, "type %s is not derived from PythonQtClassWrapper", ((PyTypeObject*)self)->tp_name);
77 77 return -1;
78 78 }
79 79
80 80 // take the class info from the superType
81 81 self->_classInfo = ((PythonQtClassWrapper*)superType)->classInfo();
82 82 }
83 83
84 84 return 0;
85 85 }
86 86
87 87 static PyObject *PythonQtClassWrapper_classname(PythonQtClassWrapper* type)
88 88 {
89 89 return PyString_FromString((QString("Class_") + type->classInfo()->className()).toLatin1().data());
90 90 }
91 91
92 92 static PyObject *PythonQtClassWrapper_help(PythonQtClassWrapper* type)
93 93 {
94 94 return PythonQt::self()->helpCalled(type->classInfo());
95 95 }
96 96
97 97 PyObject *PythonQtClassWrapper__init__(PythonQtClassWrapper *type, PyObject *args)
98 98 {
99 99 Py_ssize_t argc = PyTuple_Size(args);
100 100 if (argc>0) {
101 101 // we need to call __init__ of the instance
102 102 PyObject* self = PyTuple_GET_ITEM(args, 0);
103 103 if (PyObject_TypeCheck(self, (PyTypeObject*)type->classInfo()->pythonQtClassWrapper())) {
104 104 PyObject* newargs = PyTuple_New(argc-1);
105 105 for (int i = 0;i<argc-1; i++) {
106 106 PyTuple_SET_ITEM(newargs, i,PyTuple_GET_ITEM(args, i+1));
107 107 }
108 108 PythonQtInstanceWrapper* wrapper = (PythonQtInstanceWrapper*)self;
109 109 int result = PythonQtInstanceWrapper_init(wrapper, newargs, NULL);
110 110 Py_DECREF(newargs);
111 111 if (result==0) {
112 112 Py_INCREF(Py_None);
113 113 return Py_None;
114 114 } else {
115 115 // init failed!
116 116 }
117 117 } else {
118 118 // self not of correct type!
119 119 }
120 120 } else {
121 121 // wrong number of args
122 122 }
123 123 return NULL;
124 124 }
125 125
126 126 static PyMethodDef PythonQtClassWrapper_methods[] = {
127 127 {"__init__", (PyCFunction)PythonQtClassWrapper__init__, METH_VARARGS,
128 128 "Return the classname of the object"
129 129 },
130 130 {"className", (PyCFunction)PythonQtClassWrapper_classname, METH_NOARGS,
131 131 "Return the classname of the object"
132 132 },
133 133 {"help", (PyCFunction)PythonQtClassWrapper_help, METH_NOARGS,
134 134 "Shows the help of available methods for this class"
135 135 },
136 136 {NULL, NULL, 0 , NULL} /* Sentinel */
137 137 };
138 138
139 139
140 140 static PyObject *PythonQtClassWrapper_getattro(PyObject *obj, PyObject *name)
141 141 {
142 142 const char *attributeName;
143 143 PythonQtClassWrapper *wrapper = (PythonQtClassWrapper *)obj;
144 144
145 145 if ((attributeName = PyString_AsString(name)) == NULL) {
146 146 return NULL;
147 147 }
148 148 if (obj == (PyObject*)&PythonQtInstanceWrapper_Type) {
149 149 return NULL;
150 150 }
151 151
152 152 if (qstrcmp(attributeName, "__dict__")==0) {
153 153 PyObject* dict = ((PyTypeObject *)wrapper)->tp_dict;
154 154 if (!wrapper->classInfo()) {
155 155 Py_INCREF(dict);
156 156 return dict;
157 157 }
158 158 dict = PyDict_Copy(dict);
159 159
160 160 QStringList l = wrapper->classInfo()->memberList(false);
161 161 foreach (QString name, l) {
162 162 PyObject* o = PyObject_GetAttrString(obj, name.toLatin1().data());
163 163 if (o) {
164 164 PyDict_SetItemString(dict, name.toLatin1().data(), o);
165 165 Py_DECREF(o);
166 166 } else {
167 167 // it must have been a property or child, which we do not know as a class object...
168 168 }
169 169 }
170 170 if (wrapper->classInfo()->constructors()) {
171 171 PyObject* func = PyCFunction_New(&PythonQtClassWrapper_methods[0], obj);
172 172 PyDict_SetItemString(dict, "__init__", func);
173 173 Py_DECREF(func);
174 174 }
175 175 for (int i = 1;i<3;i++) {
176 176 PyObject* func = PyCFunction_New(&PythonQtClassWrapper_methods[i], obj);
177 177 PyDict_SetItemString(dict, PythonQtClassWrapper_methods[i].ml_name, func);
178 178 Py_DECREF(func);
179 179 }
180 180 return dict;
181 181 }
182 182
183 183 if (wrapper->classInfo()) {
184 184 PythonQtMemberInfo member = wrapper->classInfo()->member(attributeName);
185 185 if (member._type == PythonQtMemberInfo::EnumValue) {
186 186 return PyInt_FromLong(member._enumValue);
187 187 } else
188 188 if (member._type == PythonQtMemberInfo::Slot) {
189 189 // we return all slots, even the instance slots, since they are callable as unbound slots with self argument
190 190 return PythonQtSlotFunction_New(member._slot, obj, NULL);
191 191 }
192 192 }
193 193
194 194 // look for the interal methods (className(), help())
195 195 PyObject* internalMethod = Py_FindMethod( PythonQtClassWrapper_methods, obj, (char*)attributeName);
196 196 if (internalMethod) {
197 197 return internalMethod;
198 198 }
199 199 PyErr_Clear();
200 200
201 201 // look in super
202 202 PyObject* superAttr = PyType_Type.tp_getattro(obj, name);
203 203 if (superAttr) {
204 204 return superAttr;
205 205 }
206 206
207 207 QString error = QString(wrapper->classInfo()->className()) + " has no attribute named '" + QString(attributeName) + "'";
208 208 PyErr_SetString(PyExc_AttributeError, error.toLatin1().data());
209 209 return NULL;
210 210 }
211 211
212 static int PythonQtClassWrapper_setattro(PyObject *obj,PyObject *name,PyObject * /*value*/)
212 static int PythonQtClassWrapper_setattro(PyObject *obj,PyObject *name,PyObject *value)
213 213 {
214 QString error;
215 char *attributeName;
216 if ((attributeName = PyString_AsString(name)) == NULL) {
217 return -1;
218 }
219 PythonQtClassWrapper *wrapper = (PythonQtClassWrapper *)obj;
220
221 // TODO
222 return -1;
214 return PyType_Type.tp_setattro(obj,name,value);
223 215 }
224 216
225 217 /*
226 218 static PyObject * PythonQtClassWrapper_repr(PyObject * obj)
227 219 {
228 220 PythonQtClassWrapper* wrapper = (PythonQtClassWrapper*)obj;
229 221 if (wrapper->classInfo()->isCPPWrapper()) {
230 222 const QMetaObject* meta = wrapper->classInfo()->metaObject();
231 223 if (!meta) {
232 224 QObject* decorator = wrapper->classInfo()->decorator();
233 225 if (decorator) {
234 226 meta = decorator->metaObject();
235 227 }
236 228 }
237 229 if (meta) {
238 230 return PyString_FromFormat("%s Class (C++ wrapped by %s)", wrapper->classInfo()->className(), meta->className());
239 231 } else {
240 232 return PyString_FromFormat("%s Class (C++ unwrapped)", wrapper->classInfo()->className());
241 233 }
242 234 } else {
243 235 return PyString_FromFormat("%s Class", wrapper->classInfo()->className());
244 236 }
245 237 }
246 238
247 239 */
248 240
249 241 PyTypeObject PythonQtClassWrapper_Type = {
250 242 PyObject_HEAD_INIT(NULL)
251 243 0, /*ob_size*/
252 244 "PythonQt.PythonQtClassWrapper", /*tp_name*/
253 245 sizeof(PythonQtClassWrapper), /*tp_basicsize*/
254 246 0, /*tp_itemsize*/
255 247 0, /*tp_dealloc*/
256 248 0, /*tp_print*/
257 249 0, /*tp_getattr*/
258 250 0, /*tp_setattr*/
259 251 0, /*tp_compare*/
260 252 0, //PythonQtClassWrapper_repr, /*tp_repr*/
261 253 0, /*tp_as_number*/
262 254 0, /*tp_as_sequence*/
263 255 0, /*tp_as_mapping*/
264 256 0, /*tp_hash */
265 257 0, /*tp_call*/
266 258 0, /*tp_str*/
267 259 PythonQtClassWrapper_getattro, /*tp_getattro*/
268 260 PythonQtClassWrapper_setattro, /*tp_setattro*/
269 261 0, /*tp_as_buffer*/
270 262 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
271 263 0, /* tp_doc */
272 264 0, /* tp_traverse */
273 265 0, /* tp_clear */
274 266 0, /* tp_richcompare */
275 267 0, /* tp_weaklistoffset */
276 268 0, /* tp_iter */
277 269 0, /* tp_iternext */
278 270 0, /* tp_methods */
279 271 0, /* tp_members */
280 272 0, /* tp_getset */
281 273 0, /* tp_base */
282 274 0, /* tp_dict */
283 275 0, /* tp_descr_get */
284 276 0, /* tp_descr_set */
285 277 0, /* tp_dictoffset */
286 278 (initproc)PythonQtClassWrapper_init, /* tp_init */
287 279 PythonQtClassWrapper_alloc, /* tp_alloc */
288 280 0, /* tp_new */
289 281 0, /* tp_free */
290 282 };
291 283
292 284 //-------------------------------------------------------
293 285
@@ -1,555 +1,567
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 static PyObject *PythonQtInstanceWrapper_classname(PythonQtInstanceWrapper* type)
186 static PyObject *PythonQtInstanceWrapper_classname(PythonQtInstanceWrapper* obj)
187 187 {
188 return PyString_FromString(type->classInfo()->className());
188 return PyString_FromString(obj->ob_type->tp_name);
189 189 }
190 190
191 static PyObject *PythonQtInstanceWrapper_help(PythonQtInstanceWrapper* type)
191 static PyObject *PythonQtInstanceWrapper_help(PythonQtInstanceWrapper* obj)
192 192 {
193 return PythonQt::self()->helpCalled(type->classInfo());
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 228 QStringList l = wrapper->classInfo()->memberList(false);
229 229 PyObject* dict = PyDict_New();
230 230 foreach (QString name, l) {
231 231 PyObject* o = PyObject_GetAttrString(obj, name.toLatin1().data());
232 232 PyDict_SetItemString(dict, name.toLatin1().data(), o);
233 233 Py_DECREF(o);
234 234 }
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
235 239 // Note: we do not put children into the dict, is would look confusing?!
236 240 return dict;
237 241 }
238 242
239 243 // first look in super, to return derived methods from base object first
240 244 PyObject* superAttr = PyBaseObject_Type.tp_getattro(obj, name);
241 245 if (superAttr) {
242 246 return superAttr;
243 247 }
244 248 PyErr_Clear();
245 249
246 250 if (!wrapper->_obj && !wrapper->_wrappedPtr) {
247 251 QString error = QString("Trying to read attribute '") + attributeName + "' from a destroyed " + wrapper->classInfo()->className() + " object";
248 252 PyErr_SetString(PyExc_ValueError, error.toLatin1().data());
249 253 return NULL;
250 254 }
251 255
252 256 // mlabDebugConst("Python","get " << attributeName);
253 257
254 258 // TODO: dynamic properties are missing
255 259
256 260 PythonQtMemberInfo member = wrapper->classInfo()->member(attributeName);
257 261 switch (member._type) {
258 262 case PythonQtMemberInfo::Property:
259 263 if (wrapper->_obj) {
260 264 if (member._property.userType() != QVariant::Invalid) {
261 265 return PythonQtConv::QVariantToPyObject(member._property.read(wrapper->_obj));
262 266 } else {
263 267 Py_INCREF(Py_None);
264 268 return Py_None;
265 269 }
266 270 }
267 271 break;
268 272 case PythonQtMemberInfo::Slot:
269 273 return PythonQtSlotFunction_New(member._slot, obj, NULL);
270 274 break;
271 275 case PythonQtMemberInfo::EnumValue:
272 276 return PyInt_FromLong(member._enumValue);
273 277 break;
274 278 default:
275 279 // is an invalid type, go on
276 280 break;
277 281 }
278 282
279 283 // look for the interal methods (className(), help())
280 284 PyObject* internalMethod = Py_FindMethod( PythonQtInstanceWrapper_methods, obj, (char*)attributeName);
281 285 if (internalMethod) {
282 286 return internalMethod;
283 287 }
284 288 PyErr_Clear();
285 289
286 290 if (wrapper->_obj) {
287 291 // look for a child
288 292 QObjectList children = wrapper->_obj->children();
289 293 for (int i = 0; i < children.count(); i++) {
290 294 QObject *child = children.at(i);
291 295 if (child->objectName() == attributeName) {
292 296 return PythonQt::self()->priv()->wrapQObject(child);
293 297 }
294 298 }
295 299 }
296 300
297 301 QString error = QString(wrapper->classInfo()->className()) + " has no attribute named '" + QString(attributeName) + "'";
298 302 PyErr_SetString(PyExc_AttributeError, error.toLatin1().data());
299 303 return NULL;
300 304 }
301 305
302 306 static int PythonQtInstanceWrapper_setattro(PyObject *obj,PyObject *name,PyObject *value)
303 307 {
304 308 QString error;
305 309 char *attributeName;
306 310 PythonQtInstanceWrapper *wrapper = (PythonQtInstanceWrapper *)obj;
307 311
308 312 if ((attributeName = PyString_AsString(name)) == NULL)
309 313 return -1;
310 314
311 315 if (!wrapper->_obj) {
312 316 error = QString("Trying to set attribute '") + attributeName + "' on a destroyed " + wrapper->classInfo()->className() + " object";
313 317 PyErr_SetString(PyExc_AttributeError, error.toLatin1().data());
314 318 return -1;
315 319 }
316 320
317 321 PythonQtMemberInfo member = wrapper->classInfo()->member(attributeName);
318 322 if (member._type == PythonQtMemberInfo::Property) {
319 323 QMetaProperty prop = member._property;
320 324 if (prop.isWritable()) {
321 325 QVariant v;
322 326 if (prop.isEnumType()) {
323 327 // this will give us either a string or an int, everything else will probably be an error
324 328 v = PythonQtConv::PyObjToQVariant(value);
325 329 } else {
326 330 int t = prop.userType();
327 331 v = PythonQtConv::PyObjToQVariant(value, t);
328 332 }
329 333 bool success = false;
330 334 if (v.isValid()) {
331 335 success = prop.write(wrapper->_obj, v);
332 336 }
333 337 if (success) {
334 338 return 0;
335 339 } else {
336 340 error = QString("Property '") + attributeName + "' of type '" +
337 341 prop.typeName() + "' does not accept an object of type "
338 342 + QString(value->ob_type->tp_name) + " (" + PythonQtConv::PyObjGetRepresentation(value) + ")";
339 343 }
340 344 } else {
341 error = QString("Property '") + attributeName + "' of " + wrapper->classInfo()->className() + " object is not writable";
345 error = QString("Property '") + attributeName + "' of " + obj->ob_type->tp_name + " object is not writable";
342 346 }
343 } else {
344 if (member._type == PythonQtMemberInfo::Slot) {
345 error = QString("Slot '") + attributeName + "' can not be overwritten on " + wrapper->classInfo()->className() + " object";
347 } else if (member._type == PythonQtMemberInfo::Slot) {
348 error = QString("Slot '") + attributeName + "' can not be overwritten on " + obj->ob_type->tp_name + " object";
346 349 } else if (member._type == PythonQtMemberInfo::EnumValue) {
347 error = QString("EnumValue '") + attributeName + "' can not be overwritten on " + wrapper->classInfo()->className() + " object";
350 error = QString("EnumValue '") + attributeName + "' can not be overwritten on " + obj->ob_type->tp_name + " object";
351 } else if (member._type == PythonQtMemberInfo::NotFound) {
352 // if we are a derived python class, we allow setting attributes.
353 // if we are a direct CPP wrapper, we do NOT allow it, since
354 // it would be confusing to allow it because a wrapper will go away when it is not seen by python anymore
355 // and when it is recreated from a CPP pointer the attributes are gone...
356 if (obj->ob_type->tp_base != &PythonQtInstanceWrapper_Type) {
357 return PyBaseObject_Type.tp_setattro(obj,name,value);
358 } else {
359 error = QString("'") + attributeName + "' does not exist on " + obj->ob_type->tp_name + " and creating new attributes on C++ objects is not allowed";
348 360 }
349 361 }
350 362
351 363 PyErr_SetString(PyExc_AttributeError, error.toLatin1().data());
352 364 return -1;
353 365 }
354 366
355 367 static PyObject * PythonQtInstanceWrapper_str(PyObject * obj)
356 368 {
357 369 PythonQtInstanceWrapper* wrapper = (PythonQtInstanceWrapper*)obj;
358 370 const char* typeName = obj->ob_type->tp_name;
359 371 QObject *qobj = wrapper->_obj;
360 372 if (wrapper->_wrappedPtr) {
361 373 QString str = PythonQtConv::CPPObjectToString(wrapper->classInfo()->metaTypeId(), wrapper->_wrappedPtr);
362 374 if (!str.isEmpty()) {
363 375 return PyString_FromFormat("%s", str.toLatin1().constData());
364 376 } else
365 377 if (wrapper->_obj) {
366 378 return PyString_FromFormat("%s (C++ Object %p wrapped by %s %p))", typeName, wrapper->_wrappedPtr, wrapper->_obj->metaObject()->className(), qobj);
367 379 } else {
368 380 return PyString_FromFormat("%s (C++ Object %p)", typeName, wrapper->_wrappedPtr);
369 381 }
370 382 } else {
371 383 return PyString_FromFormat("%s (QObject %p)", typeName, qobj);
372 384 }
373 385 }
374 386
375 387 static PyObject * PythonQtInstanceWrapper_repr(PyObject * obj)
376 388 {
377 389 PythonQtInstanceWrapper* wrapper = (PythonQtInstanceWrapper*)obj;
378 390 const char* typeName = obj->ob_type->tp_name;
379 391
380 392 QObject *qobj = wrapper->_obj;
381 393 if (wrapper->_wrappedPtr) {
382 394 QString str = PythonQtConv::CPPObjectToString(wrapper->classInfo()->metaTypeId(), wrapper->_wrappedPtr);
383 395 if (!str.isEmpty()) {
384 396 return PyString_FromFormat("%s(%s, %p)", typeName, str.toLatin1().constData(), wrapper->_wrappedPtr);
385 397 } else
386 398 if (wrapper->_obj) {
387 399 return PyString_FromFormat("%s (C++ Object %p wrapped by %s %p))", typeName, wrapper->_wrappedPtr, wrapper->_obj->metaObject()->className(), qobj);
388 400 } else {
389 401 return PyString_FromFormat("%s (C++ Object %p)", typeName, wrapper->_wrappedPtr);
390 402 }
391 403 } else {
392 return PyString_FromFormat("%s (QObject %p)", wrapper->classInfo()->className(), qobj);
404 return PyString_FromFormat("%s (QObject %p)", typeName, wrapper->classInfo()->className(), qobj);
393 405 }
394 406 }
395 407
396 408 static int PythonQtInstanceWrapper_compare(PyObject * obj1, PyObject * obj2)
397 409 {
398 410 if (PyObject_TypeCheck(obj1, &PythonQtInstanceWrapper_Type) &&
399 411 PyObject_TypeCheck(obj2, &PythonQtInstanceWrapper_Type)) {
400 412
401 413 PythonQtInstanceWrapper* w1 = (PythonQtInstanceWrapper*)obj1;
402 414 PythonQtInstanceWrapper* w2 = (PythonQtInstanceWrapper*)obj2;
403 415 // check pointers directly first:
404 416 if (w1->_wrappedPtr != NULL) {
405 417 if (w1->_wrappedPtr == w2->_wrappedPtr) {
406 418 return 0;
407 419 }
408 420 } else if (w1->_obj == w2->_obj) {
409 421 return 0;
410 422 }
411 423 const char* class1 = w1->classInfo()->className();
412 424 const char* class2 = w2->classInfo()->className();
413 425 if (strcmp(class1, class2) == 0) {
414 426 // same class names, so we can try the operator_equal
415 427 PythonQtMemberInfo info = w1->classInfo()->member("operator_equal");
416 428 if (info._type == PythonQtMemberInfo::Slot) {
417 429 bool result = false;
418 430 void* obj1 = w1->_wrappedPtr;
419 431 if (!obj1) {
420 432 obj1 = w1->_obj;
421 433 }
422 434 if (!obj1) { return -1; }
423 435 void* obj2 = w2->_wrappedPtr;
424 436 if (!obj2) {
425 437 obj2 = w2->_obj;
426 438 }
427 439 if (!obj2) { return -1; }
428 440 if (info._slot->isInstanceDecorator()) {
429 441 // call on decorator QObject
430 442 void* args[3];
431 443 args[0] = &result;
432 444 args[1] = &obj1; // this is a pointer, so it needs a pointer to a pointer
433 445 args[2] = obj2; // this is a reference, so it needs the direct pointer
434 446 info._slot->decorator()->qt_metacall(QMetaObject::InvokeMetaMethod, info._slot->slotIndex(), args);
435 447 return result?0:-1;
436 448 } else {
437 449 // call directly on QObject
438 450 if (w1->_obj && w2->_obj) {
439 451 void* args[2];
440 452 args[0] = &result;
441 453 args[2] = obj2; // this is a reference, so it needs the direct pointer
442 454 w1->_obj->qt_metacall(QMetaObject::InvokeMetaMethod, info._slot->slotIndex(), args);
443 455 }
444 456 }
445 457 }
446 458 }
447 459 }
448 460 return -1;
449 461 }
450 462
451 463 static int PythonQtInstanceWrapper_nonzero(PyObject *obj)
452 464 {
453 465 PythonQtInstanceWrapper* wrapper = (PythonQtInstanceWrapper*)obj;
454 466 return (wrapper->_wrappedPtr == NULL && wrapper->_obj == NULL)?0:1;
455 467 }
456 468
457 469
458 470 static long PythonQtInstanceWrapper_hash(PythonQtInstanceWrapper *obj)
459 471 {
460 472 if (obj->_wrappedPtr != NULL) {
461 473 return reinterpret_cast<long>(obj->_wrappedPtr);
462 474 } else {
463 475 QObject* qobj = obj->_obj; // get pointer from QPointer wrapper
464 476 return reinterpret_cast<long>(qobj);
465 477 }
466 478 }
467 479
468 480
469 481
470 482 // we override nb_nonzero, so that one can do 'if' expressions to test for a NULL ptr
471 483 static PyNumberMethods PythonQtInstanceWrapper_as_number = {
472 484 0, /* nb_add */
473 485 0, /* nb_subtract */
474 486 0, /* nb_multiply */
475 487 0, /* nb_divide */
476 488 0, /* nb_remainder */
477 489 0, /* nb_divmod */
478 490 0, /* nb_power */
479 491 0, /* nb_negative */
480 492 0, /* nb_positive */
481 493 0, /* nb_absolute */
482 494 PythonQtInstanceWrapper_nonzero, /* nb_nonzero */
483 495 0, /* nb_invert */
484 496 0, /* nb_lshift */
485 497 0, /* nb_rshift */
486 498 0, /* nb_and */
487 499 0, /* nb_xor */
488 500 0, /* nb_or */
489 501 0, /* nb_coerce */
490 502 0, /* nb_int */
491 503 0, /* nb_long */
492 504 0, /* nb_float */
493 505 0, /* nb_oct */
494 506 0, /* nb_hex */
495 507 0, /* nb_inplace_add */
496 508 0, /* nb_inplace_subtract */
497 509 0, /* nb_inplace_multiply */
498 510 0, /* nb_inplace_divide */
499 511 0, /* nb_inplace_remainder */
500 512 0, /* nb_inplace_power */
501 513 0, /* nb_inplace_lshift */
502 514 0, /* nb_inplace_rshift */
503 515 0, /* nb_inplace_and */
504 516 0, /* nb_inplace_xor */
505 517 0, /* nb_inplace_or */
506 518 0, /* nb_floor_divide */
507 519 0, /* nb_true_divide */
508 520 0, /* nb_inplace_floor_divide */
509 521 0, /* nb_inplace_true_divide */
510 522 };
511 523
512 524 PyTypeObject PythonQtInstanceWrapper_Type = {
513 525 PyObject_HEAD_INIT(&PythonQtClassWrapper_Type)
514 526 0, /*ob_size*/
515 527 "PythonQt.PythonQtInstanceWrapper", /*tp_name*/
516 528 sizeof(PythonQtInstanceWrapper), /*tp_basicsize*/
517 529 0, /*tp_itemsize*/
518 530 (destructor)PythonQtInstanceWrapper_dealloc, /*tp_dealloc*/
519 531 0, /*tp_print*/
520 532 0, /*tp_getattr*/
521 533 0, /*tp_setattr*/
522 534 PythonQtInstanceWrapper_compare, /*tp_compare*/
523 535 PythonQtInstanceWrapper_repr, /*tp_repr*/
524 536 &PythonQtInstanceWrapper_as_number, /*tp_as_number*/
525 537 0, /*tp_as_sequence*/
526 538 0, /*tp_as_mapping*/
527 539 (hashfunc)PythonQtInstanceWrapper_hash, /*tp_hash */
528 540 0, /*tp_call*/
529 541 PythonQtInstanceWrapper_str, /*tp_str*/
530 542 PythonQtInstanceWrapper_getattro, /*tp_getattro*/
531 543 PythonQtInstanceWrapper_setattro, /*tp_setattro*/
532 544 0, /*tp_as_buffer*/
533 545 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
534 546 "PythonQtInstanceWrapper object", /* tp_doc */
535 547 0, /* tp_traverse */
536 548 0, /* tp_clear */
537 549 0, /* tp_richcompare */
538 550 0, /* tp_weaklistoffset */
539 551 0, /* tp_iter */
540 552 0, /* tp_iternext */
541 553 0, /* tp_methods */
542 554 0, /* tp_members */
543 555 0, /* tp_getset */
544 556 0, /* tp_base */
545 557 0, /* tp_dict */
546 558 0, /* tp_descr_get */
547 559 0, /* tp_descr_set */
548 560 0, /* tp_dictoffset */
549 561 (initproc)PythonQtInstanceWrapper_init, /* tp_init */
550 562 0, /* tp_alloc */
551 563 PythonQtInstanceWrapper_new, /* tp_new */
552 564 };
553 565
554 566 //-------------------------------------------------------
555 567
General Comments 0
You need to be logged in to leave comments. Login now