##// END OF EJS Templates
fixed bad initialization of ok, swapped QColor constructors to check the error in test...
florianlink -
r59:a2cbf55b6b26
parent child
Show More
@@ -1,1131 +1,1131
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 PythonQtConversion.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 "PythonQtConversion.h"
43 43 #include "PythonQtVariants.h"
44 44 #include <QDateTime>
45 45 #include <QTime>
46 46 #include <QDate>
47 47
48 48 PythonQtValueStorage<qint64, 128> PythonQtConv::global_valueStorage;
49 49 PythonQtValueStorage<void*, 128> PythonQtConv::global_ptrStorage;
50 50 PythonQtValueStorage<QVariant, 32> PythonQtConv::global_variantStorage;
51 51
52 52 QHash<int, PythonQtConvertMetaTypeToPythonCB*> PythonQtConv::_metaTypeToPythonConverters;
53 53 QHash<int, PythonQtConvertPythonToMetaTypeCB*> PythonQtConv::_pythonToMetaTypeConverters;
54 54
55 55 PyObject* PythonQtConv::GetPyBool(bool val)
56 56 {
57 57 PyObject* r = val?Py_True:Py_False;
58 58 Py_INCREF(r);
59 59 return r;
60 60 }
61 61
62 62 PyObject* PythonQtConv::ConvertQtValueToPython(const PythonQtMethodInfo::ParameterInfo& info, const void* data) {
63 63 // is it an enum value?
64 64 if (info.enumWrapper) {
65 65 if (!info.isPointer) {
66 66 return PythonQtPrivate::createEnumValueInstance(info.enumWrapper, *((unsigned int*)data));
67 67 } else {
68 68 // we do not support pointers to enums (who needs them?)
69 69 Py_INCREF(Py_None);
70 70 return Py_None;
71 71 }
72 72 }
73 73
74 74 if (info.typeId == QMetaType::Void) {
75 75 Py_INCREF(Py_None);
76 76 return Py_None;
77 77 } else if (info.isPointer && (info.typeId == QMetaType::Char)) {
78 78 // a char ptr will probably be a null terminated string, so we support that:
79 79 return PyString_FromString(*((char**)data));
80 80 } else if ((info.typeId == PythonQtMethodInfo::Unknown || info.typeId >= QMetaType::User) &&
81 81 info.name.startsWith("QList<")) {
82 82 // it is a QList template:
83 83 // (TODO: check what happens if this is a pointer type?!)
84 84 QByteArray innerType = info.name.mid(6,info.name.length()-7);
85 85 if (innerType.endsWith("*")) {
86 86 innerType.truncate(innerType.length()-1);
87 87 return ConvertQListOfPointerTypeToPythonList((QList<void*>*)data, innerType);
88 88 }
89 89 }
90 90
91 91 if (info.typeId >= QMetaType::User) {
92 92 // if a converter is registered, we use is:
93 93 PythonQtConvertMetaTypeToPythonCB* converter = _metaTypeToPythonConverters.value(info.typeId);
94 94 if (converter) {
95 95 return (*converter)(data, info.typeId);
96 96 }
97 97 }
98 98
99 99 // special handling did not match, so we convert the usual way (either pointer or value version):
100 100 if (info.isPointer) {
101 101 // convert the pointer to a Python Object (we can handle ANY C++ object, in the worst case we just know the type and the pointer)
102 102 return PythonQt::priv()->wrapPtr(*((void**)data), info.name);
103 103 } else {
104 104 // handle values that are not yet handled and not pointers
105 105 return ConvertQtValueToPythonInternal(info.typeId, data);
106 106 }
107 107 }
108 108
109 109 PyObject* PythonQtConv::ConvertQtValueToPythonInternal(int type, const void* data) {
110 110 switch (type) {
111 111 case QMetaType::Void:
112 112 Py_INCREF(Py_None);
113 113 return Py_None;
114 114 case QMetaType::Char:
115 115 return PyInt_FromLong(*((char*)data));
116 116 case QMetaType::UChar:
117 117 return PyInt_FromLong(*((unsigned char*)data));
118 118 case QMetaType::Short:
119 119 return PyInt_FromLong(*((short*)data));
120 120 case QMetaType::UShort:
121 121 return PyInt_FromLong(*((unsigned short*)data));
122 122 case QMetaType::Long:
123 123 return PyInt_FromLong(*((long*)data));
124 124 case QMetaType::ULong:
125 125 // does not fit into simple int of python
126 126 return PyLong_FromUnsignedLong(*((unsigned long*)data));
127 127 case QMetaType::Bool:
128 128 return PythonQtConv::GetPyBool(*((bool*)data));
129 129 case QMetaType::Int:
130 130 return PyInt_FromLong(*((int*)data));
131 131 case QMetaType::UInt:
132 132 return PyInt_FromLong(*((unsigned int*)data));
133 133 case QMetaType::QChar:
134 134 return PyInt_FromLong(*((short*)data));
135 135 case QMetaType::Float:
136 136 return PyFloat_FromDouble(*((float*)data));
137 137 case QMetaType::Double:
138 138 return PyFloat_FromDouble(*((double*)data));
139 139 case QMetaType::LongLong:
140 140 return PyLong_FromLongLong(*((qint64*)data));
141 141 case QMetaType::ULongLong:
142 142 return PyLong_FromUnsignedLongLong(*((quint64*)data));
143 143 case QMetaType::QByteArray: {
144 144 QByteArray* v = (QByteArray*) data;
145 145 return PyString_FromStringAndSize(*v, v->size());
146 146 }
147 147 case QMetaType::QVariantMap:
148 148 return PythonQtConv::QVariantMapToPyObject(*((QVariantMap*)data));
149 149 case QMetaType::QVariantList:
150 150 return PythonQtConv::QVariantListToPyObject(*((QVariantList*)data));
151 151 case QMetaType::QString:
152 152 return PythonQtConv::QStringToPyObject(*((QString*)data));
153 153 case QMetaType::QStringList:
154 154 return PythonQtConv::QStringListToPyObject(*((QStringList*)data));
155 155
156 156 case PythonQtMethodInfo::Variant:
157 157 return PythonQtConv::QVariantToPyObject(*((QVariant*)data));
158 158 case QMetaType::QObjectStar:
159 159 case QMetaType::QWidgetStar:
160 160 return PythonQt::priv()->wrapQObject(*((QObject**)data));
161 161
162 162 default:
163 163 if (PythonQt::priv()->isPythonQtObjectPtrMetaId(type)) {
164 164 // special case, it is a PythonQtObjectPtr which contains a PyObject, take it directly:
165 165 PyObject* o = ((PythonQtObjectPtr*)data)->object();
166 166 Py_INCREF(o);
167 167 return o;
168 168 } else {
169 169 if (type > 0) {
170 170 // if the type is known, we can construct it via QMetaType::construct
171 171 void* newCPPObject = QMetaType::construct(type, data);
172 172 // XXX this could be optimized by using metatypeid directly
173 173 PythonQtInstanceWrapper* wrap = (PythonQtInstanceWrapper*)PythonQt::priv()->wrapPtr(newCPPObject, QMetaType::typeName(type));
174 174 wrap->_ownedByPythonQt = true;
175 175 wrap->_useQMetaTypeDestroy = true;
176 176 return (PyObject*)wrap;
177 177 }
178 178 std::cerr << "Unknown type that can not be converted to Python: " << type << ", in " << __FILE__ << ":" << __LINE__ << std::endl;
179 179 }
180 180 }
181 181 Py_INCREF(Py_None);
182 182 return Py_None;
183 183 }
184 184
185 185 void* PythonQtConv::CreateQtReturnValue(const PythonQtMethodInfo::ParameterInfo& info) {
186 186 void* ptr = NULL;
187 187 if (info.isPointer) {
188 188 PythonQtValueStorage_ADD_VALUE(global_ptrStorage, void*, NULL, ptr);
189 189 } else if (info.enumWrapper) {
190 190 // create enum return value
191 191 PythonQtValueStorage_ADD_VALUE(PythonQtConv::global_valueStorage, long, 0, ptr);
192 192 } else {
193 193 switch (info.typeId) {
194 194 case QMetaType::Char:
195 195 case QMetaType::UChar:
196 196 case QMetaType::Short:
197 197 case QMetaType::UShort:
198 198 case QMetaType::Long:
199 199 case QMetaType::ULong:
200 200 case QMetaType::Bool:
201 201 case QMetaType::Int:
202 202 case QMetaType::UInt:
203 203 case QMetaType::QChar:
204 204 case QMetaType::Float:
205 205 case QMetaType::Double:
206 206 PythonQtValueStorage_ADD_VALUE(global_valueStorage, long, 0, ptr);
207 207 break;
208 208 case PythonQtMethodInfo::Variant:
209 209 PythonQtValueStorage_ADD_VALUE(global_variantStorage, QVariant, 0, ptr);
210 210 // return the ptr to the variant
211 211 break;
212 212 default:
213 213 if (info.typeId == PythonQtMethodInfo::Unknown) {
214 214 // check if we have a QList of pointers, which we can circumvent with a QList<void*>
215 215 if (info.name.startsWith("QList<")) {
216 216 QByteArray innerType = info.name.mid(6,info.name.length()-7);
217 217 if (innerType.endsWith("*")) {
218 218 static int id = QMetaType::type("QList<void*>");
219 219 PythonQtValueStorage_ADD_VALUE(global_variantStorage, QVariant, QVariant::Type(id), ptr);
220 220 // return the constData pointer that will be filled with the result value later on
221 221 ptr = (void*)((QVariant*)ptr)->constData();
222 222 }
223 223 }
224 224 }
225 225
226 226 if (!ptr && info.typeId!=PythonQtMethodInfo::Unknown) {
227 227 // everything else is stored in a QVariant, if we know the meta type...
228 228 PythonQtValueStorage_ADD_VALUE(global_variantStorage, QVariant, QVariant::Type(info.typeId), ptr);
229 229 // return the constData pointer that will be filled with the result value later on
230 230 ptr = (void*)((QVariant*)ptr)->constData();
231 231 }
232 232 }
233 233 }
234 234 return ptr;
235 235 }
236 236
237 237 void* PythonQtConv::castWrapperTo(PythonQtInstanceWrapper* wrapper, const QByteArray& className, bool& ok)
238 238 {
239 239 void* object;
240 240 if (wrapper->classInfo()->isCPPWrapper()) {
241 241 object = wrapper->_wrappedPtr;
242 242 } else {
243 243 QObject* tmp = wrapper->_obj;
244 244 object = tmp;
245 245 }
246 246 if (object) {
247 247 // if we can be upcasted to the given name, we pass the casted pointer in:
248 248 object = wrapper->classInfo()->castTo(object, className);
249 249 ok = object!=NULL;
250 250 } else {
251 251 // if it is a NULL ptr, we need to check if it inherits, so that we might pass the NULL ptr
252 252 ok = wrapper->classInfo()->inherits(className);
253 253 }
254 254 return object;
255 255 }
256 256
257 257 void* PythonQtConv::ConvertPythonToQt(const PythonQtMethodInfo::ParameterInfo& info, PyObject* obj, bool strict, PythonQtClassInfo* /*classInfo*/, void* alreadyAllocatedCPPObject)
258 258 {
259 bool ok;
259 bool ok = false;
260 260 void* ptr = NULL;
261 261 if (PyObject_TypeCheck(obj, &PythonQtInstanceWrapper_Type) && info.typeId != PythonQtMethodInfo::Variant) {
262 262 // if we have a Qt wrapper object and if we do not need a QVariant, we do the following:
263 263 // (the Variant case is handled below in a switch)
264 264
265 265 // a C++ wrapper (can be passed as pointer or reference)
266 266 PythonQtInstanceWrapper* wrap = (PythonQtInstanceWrapper*)obj;
267 bool ok;
268 267 void* object = castWrapperTo(wrap, info.name, ok);
269 268 if (ok) {
270 269 if (info.isPointer) {
271 270 // store the wrapped pointer in an extra pointer and let ptr point to the extra pointer
272 271 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_ptrStorage, void*, object, ptr);
273 272 } else {
274 273 // store the wrapped pointer directly, since we are a reference
275 274 ptr = object;
276 275 }
277 276 } else {
278 277 // not matching
279 278 }
280 279 } else if (info.isPointer) {
281 280 // a pointer
282 281 if (info.typeId == QMetaType::Char || info.typeId == QMetaType::UChar)
283 282 {
284 283 QString str = PyObjGetString(obj, strict, ok);
285 284 if (ok) {
286 285 void* ptr2 = NULL;
287 286 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_variantStorage, QVariant, QVariant(str.toUtf8()), ptr2);
288 287 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_ptrStorage, void*, (((QByteArray*)((QVariant*)ptr2)->constData())->data()), ptr);
289 288 }
290 289 } else if (info.name == "PyObject") {
291 290 // handle low level PyObject directly
292 291 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_ptrStorage, void*, obj, ptr);
293 292 } else if (obj == Py_None) {
294 293 // None is treated as a NULL ptr
295 294 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_ptrStorage, void*, NULL, ptr);
296 295 } else {
297 296 // if we are not strict, we try if we are passed a 0 integer
298 297 if (!strict) {
299 298 bool ok;
300 299 int value = PyObjGetInt(obj, true, ok);
301 300 if (ok && value==0) {
302 301 // TODOXXX is this wise? or should it be expected from the programmer to use None?
303 302 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_ptrStorage, void*, NULL, ptr);
304 303 }
305 304 }
306 305 }
307 306 } else {
308 307 // not a pointer
309 308 switch (info.typeId) {
310 309 case QMetaType::Char:
311 310 {
312 311 int val = PyObjGetInt(obj, strict, ok);
313 312 if (ok) {
314 313 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_valueStorage, char, val, ptr);
315 314 }
316 315 }
317 316 break;
318 317 case QMetaType::UChar:
319 318 {
320 319 int val = PyObjGetInt(obj, strict, ok);
321 320 if (ok) {
322 321 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_valueStorage, unsigned char, val, ptr);
323 322 }
324 323 }
325 324 break;
326 325 case QMetaType::Short:
327 326 {
328 327 int val = PyObjGetInt(obj, strict, ok);
329 328 if (ok) {
330 329 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_valueStorage, short, val, ptr);
331 330 }
332 331 }
333 332 break;
334 333 case QMetaType::UShort:
335 334 {
336 335 int val = PyObjGetInt(obj, strict, ok);
337 336 if (ok) {
338 337 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_valueStorage, unsigned short, val, ptr);
339 338 }
340 339 }
341 340 break;
342 341 case QMetaType::Long:
343 342 {
344 343 long val = (long)PyObjGetLongLong(obj, strict, ok);
345 344 if (ok) {
346 345 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_valueStorage, long, val, ptr);
347 346 }
348 347 }
349 348 break;
350 349 case QMetaType::ULong:
351 350 {
352 351 unsigned long val = (unsigned long)PyObjGetLongLong(obj, strict, ok);
353 352 if (ok) {
354 353 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_valueStorage, unsigned long, val, ptr);
355 354 }
356 355 }
357 356 break;
358 357 case QMetaType::Bool:
359 358 {
360 359 bool val = PyObjGetBool(obj, strict, ok);
361 360 if (ok) {
362 361 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_valueStorage, bool, val, ptr);
363 362 }
364 363 }
365 364 break;
366 365 case QMetaType::Int:
367 366 {
368 367 int val = PyObjGetInt(obj, strict, ok);
369 368 if (ok) {
370 369 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_valueStorage, int, val, ptr);
371 370 }
372 371 }
373 372 break;
374 373 case QMetaType::UInt:
375 374 {
376 375 unsigned int val = (unsigned int)PyObjGetLongLong(obj, strict, ok);
377 376 if (ok) {
378 377 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_valueStorage, unsigned int, val, ptr);
379 378 }
380 379 }
381 380 break;
382 381 case QMetaType::QChar:
383 382 {
384 383 int val = PyObjGetInt(obj, strict, ok);
385 384 if (ok) {
386 385 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_valueStorage, short, val, ptr);
387 386 }
388 387 }
389 388 break;
390 389 case QMetaType::Float:
391 390 {
392 391 float val = (float)PyObjGetDouble(obj, strict, ok);
393 392 if (ok) {
394 393 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_valueStorage, float, val, ptr);
395 394 }
396 395 }
397 396 break;
398 397 case QMetaType::Double:
399 398 {
400 399 double val = (double)PyObjGetDouble(obj, strict, ok);
401 400 if (ok) {
402 401 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_valueStorage, double, val, ptr);
403 402 }
404 403 }
405 404 break;
406 405 case QMetaType::LongLong:
407 406 {
408 407 qint64 val = PyObjGetLongLong(obj, strict, ok);
409 408 if (ok) {
410 409 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_valueStorage, qint64, val, ptr);
411 410 }
412 411 }
413 412 break;
414 413 case QMetaType::ULongLong:
415 414 {
416 415 quint64 val = PyObjGetULongLong(obj, strict, ok);
417 416 if (ok) {
418 417 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_valueStorage, quint64, val, ptr);
419 418 }
420 419 }
421 420 break;
422 421 case QMetaType::QByteArray:
423 422 {
424 423 QByteArray bytes = PyObjGetBytes(obj, strict, ok);
425 424 if (ok) {
426 425 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_variantStorage, QVariant, QVariant(bytes), ptr);
427 426 ptr = (void*)((QVariant*)ptr)->constData();
428 427 }
429 428 }
430 429 break;
431 430 case QMetaType::QString:
432 431 {
433 432 QString str = PyObjGetString(obj, strict, ok);
434 433 if (ok) {
435 434 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_variantStorage, QVariant, QVariant(str), ptr);
436 435 ptr = (void*)((QVariant*)ptr)->constData();
437 436 }
438 437 }
439 438 break;
440 439 case QMetaType::QStringList:
441 440 {
442 441 QStringList l = PyObjToStringList(obj, strict, ok);
443 442 if (ok) {
444 443 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_variantStorage, QVariant, QVariant(l), ptr);
445 444 ptr = (void*)((QVariant*)ptr)->constData();
446 445 }
447 446 }
448 447 break;
449 448
450 449 case PythonQtMethodInfo::Variant:
451 450 {
452 451 QVariant v = PyObjToQVariant(obj);
453 452 if (v.isValid()) {
454 453 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_variantStorage, QVariant, v, ptr);
455 454 }
456 455 }
457 456 break;
458 457 default:
459 458 {
460 459 // check for enum case
461 460 if (info.enumWrapper) {
462 461 unsigned int val;
462 ok = false;
463 463 if ((PyObject*)obj->ob_type == info.enumWrapper) {
464 464 // we have a exact enum type match:
465 465 val = PyInt_AS_LONG(obj);
466 466 ok = true;
467 467 } else if (!strict) {
468 468 // we try to get any integer, when not being strict. If we are strict, integers are not wanted because
469 469 // we want an integer overload to be taken first!
470 470 val = (unsigned int)PyObjGetLongLong(obj, false, ok);
471 471 }
472 472 if (ok) {
473 473 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_valueStorage, unsigned int, val, ptr);
474 474 return ptr;
475 475 } else {
476 476 return NULL;
477 477 }
478 478 }
479 479
480 480 if (info.typeId == PythonQtMethodInfo::Unknown || info.typeId >= QMetaType::User) {
481 481 // check for QList<AnyPtr*> case, where we will use a QList<void*> QVariant
482 482 if (info.name.startsWith("QList<")) {
483 483 QByteArray innerType = info.name.mid(6,info.name.length()-7);
484 484 if (innerType.endsWith("*")) {
485 485 innerType.truncate(innerType.length()-1);
486 486 static int id = QMetaType::type("QList<void*>");
487 487 if (!alreadyAllocatedCPPObject) {
488 488 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_variantStorage, QVariant, QVariant::Type(id), ptr);
489 489 ptr = (void*)((QVariant*)ptr)->constData();
490 490 } else {
491 491 ptr = alreadyAllocatedCPPObject;
492 492 }
493 493 ok = ConvertPythonListToQListOfPointerType(obj, (QList<void*>*)ptr, innerType, strict);
494 494 if (ok) {
495 495 return ptr;
496 496 } else {
497 497 return NULL;
498 498 }
499 499 }
500 500 }
501 501 }
502 502
503 503 // We only do this for registered type > QMetaType::User for performance reasons.
504 504 if (info.typeId >= QMetaType::User) {
505 505 // Maybe we have a special converter that is registered for that type:
506 506 PythonQtConvertPythonToMetaTypeCB* converter = _pythonToMetaTypeConverters.value(info.typeId);
507 507 if (converter) {
508 508 if (!alreadyAllocatedCPPObject) {
509 509 // create a new empty variant of concrete type:
510 510 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_variantStorage, QVariant, QVariant::Type(info.typeId), ptr);
511 511 ptr = (void*)((QVariant*)ptr)->constData();
512 512 } else {
513 513 ptr = alreadyAllocatedCPPObject;
514 514 }
515 515 // now call the converter, passing the internal object of the variant
516 516 ok = (*converter)(obj, ptr, info.typeId, strict);
517 517 if (ok) {
518 518 return ptr;
519 519 } else {
520 520 return NULL;
521 521 }
522 522 }
523 523 }
524 524 // if no type id is available, conversion to a QVariant makes no sense/is not possible
525 525 if (info.typeId != PythonQtMethodInfo::Unknown) {
526 526 // for all other types, we use the same qvariant conversion and pass out the constData of the variant:
527 527 QVariant v = PyObjToQVariant(obj, info.typeId);
528 528 if (v.isValid()) {
529 529 PythonQtValueStorage_ADD_VALUE_IF_NEEDED(alreadyAllocatedCPPObject,global_variantStorage, QVariant, v, ptr);
530 530 ptr = (void*)((QVariant*)ptr)->constData();
531 531 }
532 532 }
533 533 }
534 534 }
535 535 }
536 536 return ptr;
537 537 }
538 538
539 539
540 540 QStringList PythonQtConv::PyObjToStringList(PyObject* val, bool strict, bool& ok) {
541 541 QStringList v;
542 542 ok = false;
543 543 // if we are strict, we do not want to convert a string to a stringlist
544 544 // (strings in python are detected to be sequences)
545 545 if (strict &&
546 546 (val->ob_type == &PyString_Type ||
547 547 PyUnicode_Check(val))) {
548 548 ok = false;
549 549 return v;
550 550 }
551 551 if (PySequence_Check(val)) {
552 552 int count = PySequence_Size(val);
553 553 for (int i = 0;i<count;i++) {
554 554 PyObject* value = PySequence_GetItem(val,i);
555 555 v.append(PyObjGetString(value,false,ok));
556 556 }
557 557 ok = true;
558 558 }
559 559 return v;
560 560 }
561 561
562 562 QString PythonQtConv::PyObjGetRepresentation(PyObject* val)
563 563 {
564 564 QString r;
565 565 PyObject* str = PyObject_Repr(val);
566 566 if (str) {
567 567 r = QString(PyString_AS_STRING(str));
568 568 Py_DECREF(str);
569 569 }
570 570 return r;
571 571 }
572 572
573 573 QString PythonQtConv::PyObjGetString(PyObject* val, bool strict, bool& ok) {
574 574 QString r;
575 575 ok = true;
576 576 if (val->ob_type == &PyString_Type) {
577 577 r = QString(PyString_AS_STRING(val));
578 578 } else if (PyUnicode_Check(val)) {
579 579 #ifdef WIN32
580 580 r = QString::fromUtf16(PyUnicode_AS_UNICODE(val));
581 581 #else
582 582 PyObject *ptmp = PyUnicode_AsUTF8String(val);
583 583 if(ptmp) {
584 584 r = QString::fromUtf8(PyString_AS_STRING(ptmp));
585 585 Py_DECREF(ptmp);
586 586 }
587 587 #endif
588 588 } else if (!strict) {
589 589 // EXTRA: could also use _Unicode, but why should we?
590 590 PyObject* str = PyObject_Str(val);
591 591 if (str) {
592 592 r = QString(PyString_AS_STRING(str));
593 593 Py_DECREF(str);
594 594 } else {
595 595 ok = false;
596 596 }
597 597 } else {
598 598 ok = false;
599 599 }
600 600 return r;
601 601 }
602 602
603 603 QByteArray PythonQtConv::PyObjGetBytes(PyObject* val, bool /*strict*/, bool& ok) {
604 604 QByteArray r;
605 605 ok = true;
606 606 if (val->ob_type == &PyString_Type) {
607 607 long size = PyString_GET_SIZE(val);
608 608 r = QByteArray(PyString_AS_STRING(val), size);
609 609 } else {
610 610 ok = false;
611 611 }
612 612 return r;
613 613 }
614 614
615 615 bool PythonQtConv::PyObjGetBool(PyObject* val, bool strict, bool &ok) {
616 616 bool d = false;
617 617 ok = false;
618 618 if (val == Py_False) {
619 619 d = false;
620 620 ok = true;
621 621 } else if (val == Py_True) {
622 622 d = true;
623 623 ok = true;
624 624 } else if (!strict) {
625 625 d = PyObjGetInt(val, false, ok)!=0;
626 626 ok = true;
627 627 }
628 628 return d;
629 629 }
630 630
631 631 int PythonQtConv::PyObjGetInt(PyObject* val, bool strict, bool &ok) {
632 632 int d = 0;
633 633 ok = true;
634 634 if (val->ob_type == &PyInt_Type) {
635 635 d = PyInt_AS_LONG(val);
636 636 } else if (!strict) {
637 637 if (PyObject_TypeCheck(val, &PyInt_Type)) {
638 638 // support for derived int classes, e.g. for our enums
639 639 d = PyInt_AS_LONG(val);
640 640 } else if (val->ob_type == &PyFloat_Type) {
641 641 d = floor(PyFloat_AS_DOUBLE(val));
642 642 } else if (val->ob_type == &PyLong_Type) {
643 643 // handle error on overflow!
644 644 d = PyLong_AsLong(val);
645 645 } else if (val == Py_False) {
646 646 d = 0;
647 647 } else if (val == Py_True) {
648 648 d = 1;
649 649 } else {
650 650 ok = false;
651 651 }
652 652 } else {
653 653 ok = false;
654 654 }
655 655 return d;
656 656 }
657 657
658 658 qint64 PythonQtConv::PyObjGetLongLong(PyObject* val, bool strict, bool &ok) {
659 659 qint64 d = 0;
660 660 ok = true;
661 661 if (val->ob_type == &PyInt_Type) {
662 662 d = PyInt_AS_LONG(val);
663 663 } else if (val->ob_type == &PyLong_Type) {
664 664 d = PyLong_AsLongLong(val);
665 665 } else if (!strict) {
666 666 if (PyObject_TypeCheck(val, &PyInt_Type)) {
667 667 // support for derived int classes, e.g. for our enums
668 668 d = PyInt_AS_LONG(val);
669 669 } else if (val->ob_type == &PyFloat_Type) {
670 670 d = floor(PyFloat_AS_DOUBLE(val));
671 671 } else if (val == Py_False) {
672 672 d = 0;
673 673 } else if (val == Py_True) {
674 674 d = 1;
675 675 } else {
676 676 ok = false;
677 677 }
678 678 } else {
679 679 ok = false;
680 680 }
681 681 return d;
682 682 }
683 683
684 684 quint64 PythonQtConv::PyObjGetULongLong(PyObject* val, bool strict, bool &ok) {
685 685 quint64 d = 0;
686 686 ok = true;
687 687 if (PyObject_TypeCheck(val, &PyInt_Type)) {
688 688 d = PyInt_AS_LONG(val);
689 689 } else if (val->ob_type == &PyLong_Type) {
690 690 d = PyLong_AsLongLong(val);
691 691 } else if (!strict) {
692 692 if (PyObject_TypeCheck(val, &PyInt_Type)) {
693 693 // support for derived int classes, e.g. for our enums
694 694 d = PyInt_AS_LONG(val);
695 695 } else if (val->ob_type == &PyFloat_Type) {
696 696 d = floor(PyFloat_AS_DOUBLE(val));
697 697 } else if (val == Py_False) {
698 698 d = 0;
699 699 } else if (val == Py_True) {
700 700 d = 1;
701 701 } else {
702 702 ok = false;
703 703 }
704 704 } else {
705 705 ok = false;
706 706 }
707 707 return d;
708 708 }
709 709
710 710 double PythonQtConv::PyObjGetDouble(PyObject* val, bool strict, bool &ok) {
711 711 double d = 0;
712 712 ok = true;
713 713 if (val->ob_type == &PyFloat_Type) {
714 714 d = PyFloat_AS_DOUBLE(val);
715 715 } else if (!strict) {
716 716 if (PyObject_TypeCheck(val, &PyInt_Type)) {
717 717 d = PyInt_AS_LONG(val);
718 718 } else if (val->ob_type == &PyLong_Type) {
719 719 d = PyLong_AsLong(val);
720 720 } else if (val == Py_False) {
721 721 d = 0;
722 722 } else if (val == Py_True) {
723 723 d = 1;
724 724 } else {
725 725 ok = false;
726 726 }
727 727 } else {
728 728 ok = false;
729 729 }
730 730 return d;
731 731 }
732 732
733 733 QVariant PythonQtConv::PyObjToQVariant(PyObject* val, int type)
734 734 {
735 735 QVariant v;
736 736 bool ok = true;
737 737
738 738 if (type==-1) {
739 739 // no special type requested
740 740 if (val->ob_type==&PyString_Type || val->ob_type==&PyUnicode_Type) {
741 741 type = QVariant::String;
742 742 } else if (PyObject_TypeCheck(val, &PyInt_Type)) {
743 743 type = QVariant::Int;
744 744 } else if (val->ob_type==&PyLong_Type) {
745 745 type = QVariant::LongLong;
746 746 } else if (val->ob_type==&PyFloat_Type) {
747 747 type = QVariant::Double;
748 748 } else if (val == Py_False || val == Py_True) {
749 749 type = QVariant::Bool;
750 750 } else if (PyObject_TypeCheck(val, &PythonQtInstanceWrapper_Type)) {
751 751 PythonQtInstanceWrapper* wrap = (PythonQtInstanceWrapper*)val;
752 752 // c++ wrapper, check if the class names of the c++ objects match
753 753 if (wrap->classInfo()->isCPPWrapper()) {
754 754 if (wrap->classInfo()->metaTypeId()>0) {
755 755 // construct a new variant from the C++ object if it has a meta type (this will COPY the object!)
756 756 v = QVariant(wrap->classInfo()->metaTypeId(), wrap->_wrappedPtr);
757 757 } else {
758 758 // TODOXXX we could as well check if there is a registered meta type for "classname*", so that we may pass
759 759 // the pointer here...
760 760 // is this worth anything? we loose the knowledge of the cpp object type
761 761 v = qVariantFromValue(wrap->_wrappedPtr);
762 762 }
763 763 } else {
764 764 // this gives us a QObject pointer
765 765 QObject* myObject = wrap->_obj;
766 766 v = qVariantFromValue(myObject);
767 767 }
768 768 return v;
769 769 } else if (val->ob_type==&PyDict_Type) {
770 770 type = QVariant::Map;
771 771 } else if (val->ob_type==&PyList_Type || val->ob_type==&PyTuple_Type || PySequence_Check(val)) {
772 772 type = QVariant::List;
773 773 } else if (val == Py_None) {
774 774 // none is invalid
775 775 type = QVariant::Invalid;
776 776 } else {
777 777 // this used to be:
778 778 // type = QVariant::String;
779 779 // but now we want to transport the Python Objects directly:
780 780 PythonQtObjectPtr o(val);
781 781 v = qVariantFromValue(o);
782 782 return v;
783 783 }
784 784 }
785 785 // special type request:
786 786 switch (type) {
787 787 case QVariant::Invalid:
788 788 return v;
789 789 break;
790 790 case QVariant::Int:
791 791 {
792 792 int d = PyObjGetInt(val, false, ok);
793 793 if (ok) return QVariant(d);
794 794 }
795 795 break;
796 796 case QVariant::UInt:
797 797 {
798 798 int d = PyObjGetInt(val, false,ok);
799 799 if (ok) v = QVariant((unsigned int)d);
800 800 }
801 801 break;
802 802 case QVariant::Bool:
803 803 {
804 804 int d = PyObjGetBool(val,false,ok);
805 805 if (ok) v = QVariant((bool)(d!=0));
806 806 }
807 807 break;
808 808 case QVariant::Double:
809 809 {
810 810 double d = PyObjGetDouble(val,false,ok);
811 811 if (ok) v = QVariant(d);
812 812 break;
813 813 }
814 814 case QMetaType::Float:
815 815 {
816 816 float d = (float) PyObjGetDouble(val,false,ok);
817 817 if (ok) v = qVariantFromValue(d);
818 818 break;
819 819 }
820 820 case QMetaType::Long:
821 821 {
822 822 long d = (long) PyObjGetLongLong(val,false,ok);
823 823 if (ok) v = qVariantFromValue(d);
824 824 break;
825 825 }
826 826 case QMetaType::ULong:
827 827 {
828 828 unsigned long d = (unsigned long) PyObjGetLongLong(val,false,ok);
829 829 if (ok) v = qVariantFromValue(d);
830 830 break;
831 831 }
832 832 case QMetaType::Short:
833 833 {
834 834 short d = (short) PyObjGetInt(val,false,ok);
835 835 if (ok) v = qVariantFromValue(d);
836 836 break;
837 837 }
838 838 case QMetaType::UShort:
839 839 {
840 840 unsigned short d = (unsigned short) PyObjGetInt(val,false,ok);
841 841 if (ok) v = qVariantFromValue(d);
842 842 break;
843 843 }
844 844 case QMetaType::Char:
845 845 {
846 846 char d = (char) PyObjGetInt(val,false,ok);
847 847 if (ok) v = qVariantFromValue(d);
848 848 break;
849 849 }
850 850 case QMetaType::UChar:
851 851 {
852 852 unsigned char d = (unsigned char) PyObjGetInt(val,false,ok);
853 853 if (ok) v = qVariantFromValue(d);
854 854 break;
855 855 }
856 856
857 857 case QVariant::ByteArray:
858 858 case QVariant::String:
859 859 {
860 860 bool ok;
861 861 v = QVariant(PyObjGetString(val, false, ok));
862 862 }
863 863 break;
864 864
865 865 // these are important for MeVisLab
866 866 case QVariant::Map:
867 867 {
868 868 if (PyMapping_Check(val)) {
869 869 QMap<QString,QVariant> map;
870 870 PyObject* items = PyMapping_Items(val);
871 871 if (items) {
872 872 int count = PyList_Size(items);
873 873 PyObject* value;
874 874 PyObject* key;
875 875 PyObject* tuple;
876 876 for (int i = 0;i<count;i++) {
877 877 tuple = PyList_GetItem(items,i);
878 878 key = PyTuple_GetItem(tuple, 0);
879 879 value = PyTuple_GetItem(tuple, 1);
880 880 map.insert(PyObjGetString(key), PyObjToQVariant(value,-1));
881 881 }
882 882 Py_DECREF(items);
883 883 v = map;
884 884 }
885 885 }
886 886 }
887 887 break;
888 888 case QVariant::List:
889 889 if (PySequence_Check(val)) {
890 890 QVariantList list;
891 891 int count = PySequence_Size(val);
892 892 PyObject* value;
893 893 for (int i = 0;i<count;i++) {
894 894 value = PySequence_GetItem(val,i);
895 895 list.append(PyObjToQVariant(value, -1));
896 896 }
897 897 v = list;
898 898 }
899 899 break;
900 900 case QVariant::StringList:
901 901 {
902 902 bool ok;
903 903 QStringList l = PyObjToStringList(val, false, ok);
904 904 if (ok) {
905 905 v = l;
906 906 }
907 907 }
908 908 break;
909 909
910 910 default:
911 911 if (PyObject_TypeCheck(val, &PythonQtInstanceWrapper_Type)) {
912 912 PythonQtInstanceWrapper* wrap = (PythonQtInstanceWrapper*)val;
913 913 if (wrap->classInfo()->isCPPWrapper() && wrap->classInfo()->metaTypeId() == type) {
914 914 // construct a new variant from the C++ object if it has the same meta type
915 915 v = QVariant(type, wrap->_wrappedPtr);
916 916 } else {
917 917 v = QVariant();
918 918 }
919 919 } else {
920 920 v = QVariant();
921 921 }
922 922 }
923 923 return v;
924 924 }
925 925
926 926 PyObject* PythonQtConv::QStringToPyObject(const QString& str)
927 927 {
928 928 if (str.isNull()) {
929 929 return PyString_FromString("");
930 930 } else {
931 931 #ifdef WIN32
932 932 // return PyString_FromString(str.toLatin1().data());
933 933 return PyUnicode_FromUnicode(str.utf16(), str.length());
934 934 #else
935 935 return PyUnicode_DecodeUTF16((const char*)str.utf16(), str.length()*2, NULL, NULL);
936 936 #endif
937 937 }
938 938 }
939 939
940 940 PyObject* PythonQtConv::QStringListToPyObject(const QStringList& list)
941 941 {
942 942 PyObject* result = PyTuple_New(list.count());
943 943 int i = 0;
944 944 QString str;
945 945 foreach (str, list) {
946 946 PyTuple_SET_ITEM(result, i, PythonQtConv::QStringToPyObject(str));
947 947 i++;
948 948 }
949 949 // why is the error state bad after this?
950 950 PyErr_Clear();
951 951 return result;
952 952 }
953 953
954 954 PyObject* PythonQtConv::QStringListToPyList(const QStringList& list)
955 955 {
956 956 PyObject* result = PyList_New(list.count());
957 957 int i = 0;
958 958 for (QStringList::ConstIterator it = list.begin(); it!=list.end(); ++it) {
959 959 PyList_SET_ITEM(result, i, PythonQtConv::QStringToPyObject(*it));
960 960 i++;
961 961 }
962 962 return result;
963 963 }
964 964
965 965 PyObject* PythonQtConv::QVariantToPyObject(const QVariant& v)
966 966 {
967 967 return ConvertQtValueToPythonInternal(v.userType(), (void*)v.constData());
968 968 }
969 969
970 970 PyObject* PythonQtConv::QVariantMapToPyObject(const QVariantMap& m) {
971 971 PyObject* result = PyDict_New();
972 972 QVariantMap::const_iterator t = m.constBegin();
973 973 PyObject* key;
974 974 PyObject* val;
975 975 for (;t!=m.end();t++) {
976 976 key = QStringToPyObject(t.key());
977 977 val = QVariantToPyObject(t.value());
978 978 PyDict_SetItem(result, key, val);
979 979 Py_DECREF(key);
980 980 Py_DECREF(val);
981 981 }
982 982 return result;
983 983 }
984 984
985 985 PyObject* PythonQtConv::QVariantListToPyObject(const QVariantList& l) {
986 986 PyObject* result = PyTuple_New(l.count());
987 987 int i = 0;
988 988 QVariant v;
989 989 foreach (v, l) {
990 990 PyTuple_SET_ITEM(result, i, PythonQtConv::QVariantToPyObject(v));
991 991 i++;
992 992 }
993 993 // why is the error state bad after this?
994 994 PyErr_Clear();
995 995 return result;
996 996 }
997 997
998 998 PyObject* PythonQtConv::ConvertQListOfPointerTypeToPythonList(QList<void*>* list, const QByteArray& typeName)
999 999 {
1000 1000 PyObject* result = PyTuple_New(list->count());
1001 1001 int i = 0;
1002 1002 foreach (void* value, *list) {
1003 1003 PyTuple_SET_ITEM(result, i, PythonQt::priv()->wrapPtr(value, typeName));
1004 1004 i++;
1005 1005 }
1006 1006 return result;
1007 1007 }
1008 1008
1009 1009 bool PythonQtConv::ConvertPythonListToQListOfPointerType(PyObject* obj, QList<void*>* list, const QByteArray& type, bool /*strict*/)
1010 1010 {
1011 1011 bool result = false;
1012 1012 if (PySequence_Check(obj)) {
1013 1013 result = true;
1014 1014 int count = PySequence_Size(obj);
1015 1015 PyObject* value;
1016 1016 for (int i = 0;i<count;i++) {
1017 1017 value = PySequence_GetItem(obj,i);
1018 1018 if (PyObject_TypeCheck(value, &PythonQtInstanceWrapper_Type)) {
1019 1019 PythonQtInstanceWrapper* wrap = (PythonQtInstanceWrapper*)value;
1020 1020 bool ok;
1021 1021 void* object = castWrapperTo(wrap, type, ok);
1022 1022 if (ok) {
1023 1023 list->append(object);
1024 1024 } else {
1025 1025 result = false;
1026 1026 break;
1027 1027 }
1028 1028 }
1029 1029 }
1030 1030 }
1031 1031 return result;
1032 1032 }
1033 1033
1034 1034 int PythonQtConv::getInnerTemplateMetaType(const QByteArray& typeName)
1035 1035 {
1036 1036 int idx = typeName.indexOf("<");
1037 1037 if (idx>0) {
1038 1038 int idx2 = typeName.indexOf(">");
1039 1039 if (idx2>0) {
1040 1040 QByteArray innerType = typeName.mid(idx+1,idx2-idx-1);
1041 1041 return QMetaType::type(innerType.constData());
1042 1042 }
1043 1043 }
1044 1044 return QMetaType::Void;
1045 1045 }
1046 1046
1047 1047
1048 1048
1049 1049 QString PythonQtConv::qVariantToString(const QVariant& v) {
1050 1050 return CPPObjectToString(v.userType(), v.constData());
1051 1051 }
1052 1052
1053 1053 QString PythonQtConv::CPPObjectToString(int type, const void* data) {
1054 1054 QString r;
1055 1055 switch (type) {
1056 1056 case QVariant::Size: {
1057 1057 const QSize* s = static_cast<const QSize*>(data);
1058 1058 r = QString::number(s->width()) + ", " + QString::number(s->height());
1059 1059 }
1060 1060 break;
1061 1061 case QVariant::SizeF: {
1062 1062 const QSizeF* s = static_cast<const QSizeF*>(data);
1063 1063 r = QString::number(s->width()) + ", " + QString::number(s->height());
1064 1064 }
1065 1065 break;
1066 1066 case QVariant::Point: {
1067 1067 const QPoint* s = static_cast<const QPoint*>(data);
1068 1068 r = QString::number(s->x()) + ", " + QString::number(s->y());
1069 1069 }
1070 1070 break;
1071 1071 case QVariant::PointF: {
1072 1072 const QPointF* s = static_cast<const QPointF*>(data);
1073 1073 r = QString::number(s->x()) + ", " + QString::number(s->y());
1074 1074 }
1075 1075 break;
1076 1076 case QVariant::Rect: {
1077 1077 const QRect* s = static_cast<const QRect*>(data);
1078 1078 r = QString::number(s->x()) + ", " + QString::number(s->y());
1079 1079 r += ", " + QString::number(s->width()) + ", " + QString::number(s->height());
1080 1080 }
1081 1081 break;
1082 1082 case QVariant::RectF: {
1083 1083 const QRectF* s = static_cast<const QRectF*>(data);
1084 1084 r = QString::number(s->x()) + ", " + QString::number(s->y());
1085 1085 r += ", " + QString::number(s->width()) + ", " + QString::number(s->height());
1086 1086 }
1087 1087 break;
1088 1088 case QVariant::Date: {
1089 1089 const QDate* s = static_cast<const QDate*>(data);
1090 1090 r = s->toString(Qt::ISODate);
1091 1091 }
1092 1092 break;
1093 1093 case QVariant::DateTime: {
1094 1094 const QDateTime* s = static_cast<const QDateTime*>(data);
1095 1095 r = s->toString(Qt::ISODate);
1096 1096 }
1097 1097 break;
1098 1098 case QVariant::Time: {
1099 1099 const QTime* s = static_cast<const QTime*>(data);
1100 1100 r = s->toString(Qt::ISODate);
1101 1101 }
1102 1102 break;
1103 1103 case QVariant::Pixmap:
1104 1104 {
1105 1105 const QPixmap* s = static_cast<const QPixmap*>(data);
1106 1106 r = QString("Pixmap ") + QString::number(s->width()) + ", " + QString::number(s->height());
1107 1107 }
1108 1108 break;
1109 1109 case QVariant::Image:
1110 1110 {
1111 1111 const QImage* s = static_cast<const QImage*>(data);
1112 1112 r = QString("Image ") + QString::number(s->width()) + ", " + QString::number(s->height());
1113 1113 }
1114 1114 break;
1115 1115 case QVariant::Url:
1116 1116 {
1117 1117 const QUrl* s = static_cast<const QUrl*>(data);
1118 1118 r = s->toString();
1119 1119 }
1120 1120 break;
1121 1121 //TODO: add more printing for other variant types
1122 1122 default:
1123 1123 // this creates a copy, but that should not be expensive for typical simple variants
1124 1124 // (but we do not want to do this for our won user types!
1125 1125 if (type>0 && type < (int)QVariant::UserType) {
1126 1126 QVariant v(type, data);
1127 1127 r = v.toString();
1128 1128 }
1129 1129 }
1130 1130 return r;
1131 1131 }
General Comments 0
You need to be logged in to leave comments. Login now