2
For general Scribus (>=1.3.2) copyright and licensing information please refer
3
to the COPYING file provided with the program. Following this notice may exist
4
a copyright and/or license notice that predates the release of Scribus 1.3.2
5
for which a new license (GPL+exception) is in place.
7
#include "cmdgetsetprop.h"
10
#include <QMetaObject>
11
#include <QMetaProperty>
14
#include <QObjectList>
16
QObject* getQObjectFromPyArg(PyObject* arg)
18
if (PyString_Check(arg))
19
// It's a string. Look for a pageItem by that name. Do NOT accept a
21
return getPageItemByName(QString::fromUtf8(PyString_AsString(arg)));
22
else if (PyCObject_Check(arg))
24
// It's a PyCObject, ie a wrapped pointer. Check it's not NULL
26
// FIXME: Try to check that its a pointer to a QObject instance
27
QObject* tempObject = (QObject*)PyCObject_AsVoidPtr(arg);
30
PyErr_SetString(PyExc_TypeError, "INTERNAL: Passed NULL PyCObject");
38
// It's not a type we know what to do with
39
PyErr_SetString(PyExc_TypeError, QObject::tr("Argument must be page item name, or PyCObject instance").toLocal8Bit().constData());
45
PyObject* wrapQObject(QObject* obj)
47
return PyCObject_FromVoidPtr((void*)obj, NULL);
51
const char* getpropertytype(QObject* obj, const char* propname, bool includesuper)
53
const QMetaObject* objmeta = obj->metaObject();
54
int i = objmeta->indexOfProperty(propname);
57
QMetaProperty propmeta = objmeta->property(i);
58
if (!propmeta.isValid())
60
const char* type = propmeta.typeName();
65
PyObject* scribus_propertyctype(PyObject* /*self*/, PyObject* args, PyObject* kw)
67
PyObject* objArg = NULL;
68
char* propertyname = NULL;
70
char* kwargs[] = {const_cast<char*>("object"),
71
const_cast<char*>("property"),
72
const_cast<char*>("includesuper"),
74
if (!PyArg_ParseTupleAndKeywords(args, kw, "Oes|i", kwargs,
75
&objArg, "ascii", &propertyname, &includesuper))
78
// Get the QObject* the object argument refers to
79
QObject* obj = getQObjectFromPyArg(objArg);
82
objArg = NULL; // no need to decref, it's borrowed
84
// Look up the property and retrive its type information
85
const char* type = getpropertytype( (QObject*)obj, propertyname, includesuper);
88
PyErr_SetString(PyExc_KeyError, QObject::tr("Property not found").toLocal8Bit().constData());
91
return PyString_FromString(type);
94
PyObject* convert_QStringList_to_PyListObject(QStringList& origlist)
96
PyObject* resultList = PyList_New(0);
100
for ( QStringList::Iterator it = origlist.begin(); it != origlist.end(); ++it )
101
if (PyList_Append(resultList, PyString_FromString((*it).toUtf8().data())) == -1)
108
PyObject* convert_QObjectList_to_PyListObject(QObjectList* origlist)
110
PyObject* resultList = PyList_New(0);
114
PyObject* objPtr = NULL;
115
// Loop over the objects in the list and add them to the python
116
// list wrapped in PyCObjects .
117
for (int i = 0; i < origlist->count(); ++i)
119
// Wrap up the object pointer
120
objPtr = wrapQObject(origlist->at(i));
123
// Failed to wrap the object. An exception is already set.
124
Py_DECREF(resultList);
127
// and add it to the list
128
if (PyList_Append(resultList, (PyObject*)objPtr) == -1)
134
/*Qt4 we either need to copy QObject::qChildHelper or rewrite this
136
PyObject* scribus_getchildren(PyObject* , PyObject* args, PyObject* kw)
138
PyObject* objArg = NULL;
139
char* ofclass = NULL;
143
char* kwnames[] = {const_cast<char*>("object"),
144
const_cast<char*>("ofclass"),
145
const_cast<char*>("ofname"),
146
const_cast<char*>("regexpmatch"),
147
const_cast<char*>("recursive"),
149
if (!PyArg_ParseTupleAndKeywords(args, kw, "O|esesii", kwnames,
150
&objArg, "ascii", &ofclass, "ascii", &ofname, ®expmatch, &recursive))
153
// Get the QObject* the object argument refers to
154
QObject* obj = getQObjectFromPyArg(objArg);
157
objArg = NULL; // no need to decref, it's borrowed
159
// Our job is to return a Python list containing the children of this
160
// widget (as PyCObjects).
161
//qt4 FIXME QObjectList* children;
162
//qt4 FIXME children = obj->queryList(ofclass, ofname, regexpmatch, recursive);
163
PyObject* itemlist = 0;
164
//qt4 FIXME itemlist = convert_QObjectList_to_PyListObject(children);
165
//qt4 FIXME delete children;
170
// Perform a recursive (by default) search for the named child, possibly of a
172
PyObject* scribus_getchild(PyObject* , PyObject* args, PyObject* kw)
174
PyObject* objArg = NULL;
175
char* childname = NULL;
176
char* ofclass = NULL;
177
bool recursive = true;
178
char* kwnames[] = {const_cast<char*>("object"),
179
const_cast<char*>("childname"),
180
const_cast<char*>("ofclass"),
181
const_cast<char*>("recursive"),
183
if (!PyArg_ParseTupleAndKeywords(args, kw, "Oes|esi", kwnames,
184
&objArg, "ascii", &childname, "ascii", &ofclass, &recursive))
187
// Get the QObject* the object argument refers to
188
QObject* obj = getQObjectFromPyArg(objArg);
191
objArg = NULL; // no need to decref, it's borrowed
193
// Search for the child, possibly restricting the search to children
194
// of a particular type, and possibly recursively searching through
195
// grandchildren etc.
196
QObject* child = obj->child(childname, ofclass, recursive);
199
PyErr_SetString(PyExc_KeyError, QObject::tr("Child not found").toLocal8Bit().constData());
203
return wrapQObject(child);
207
PyObject* scribus_getpropertynames(PyObject* /*self*/, PyObject* args, PyObject* kw)
209
PyObject* objArg = NULL;
210
int includesuper = 1;
211
char* kwargs[] = {const_cast<char*>("object"),
212
const_cast<char*>("includesuper"),
214
if (!PyArg_ParseTupleAndKeywords(args, kw, "O|i", kwargs,
215
&objArg, &includesuper))
218
// Get the QObject* the object argument refers to
219
QObject* obj = getQObjectFromPyArg(objArg);
222
objArg = NULL; // no need to decref, it's borrowed
224
// Retrive the object's meta object so we can query it
225
const QMetaObject* objmeta = obj->metaObject();
229
// Return the list of properties
230
QStringList propertyNames;
231
int propertyOffset = includesuper ? 0 : objmeta->propertyOffset();
232
for(int i = propertyOffset; i < objmeta->propertyCount(); ++i)
234
QString propName = objmeta->property(i).name();
235
propertyNames << QString::fromLatin1(objmeta->property(i).name());
237
return convert_QStringList_to_PyListObject(propertyNames);
241
PyObject* scribus_getproperty(PyObject* /*self*/, PyObject* args, PyObject* kw)
243
PyObject* objArg = NULL;
244
char* propertyName = NULL;
245
char* kwargs[] = {const_cast<char*>("object"),
246
const_cast<char*>("property"),
248
if (!PyArg_ParseTupleAndKeywords(args, kw, "Oes", kwargs,
249
&objArg, "ascii", &propertyName))
252
// Get the QObject* the object argument refers to
253
QObject* obj = getQObjectFromPyArg(objArg);
256
objArg = NULL; // no need to decref, it's borrowed
258
// Get the QMetaProperty for the property, so we can check
259
// if it's a set/enum and do name/value translation.
260
const QMetaObject* objmeta = obj->metaObject();
261
int i = objmeta->indexOfProperty(propertyName);
264
PyErr_SetString(PyExc_ValueError,
265
QObject::tr("Property not found").toLocal8Bit().data());
269
QMetaProperty propmeta = objmeta->property(i);
270
if (!propmeta.isValid())
272
PyErr_SetString(PyExc_ValueError,
273
QObject::tr("Invalid property").toLocal8Bit().data());
277
// Get the property value as a variant type
278
QVariant prop = obj->property(propertyName);
280
// Convert the property to an instance of the closest matching Python type.
281
PyObject* resultobj = NULL;
283
if (prop.type() == QVariant::Int)
284
resultobj = PyLong_FromLong(prop.toInt());
285
else if (prop.type() == QVariant::Double)
286
resultobj = PyFloat_FromDouble(prop.toDouble());
288
else if (prop.type() == QVariant::Bool)
289
resultobj = PyBool_FromLong(prop.toBool());
291
else if (prop.type() == QVariant::ByteArray)
292
resultobj = PyString_FromString(prop.toByteArray().data());
293
else if (prop.type() == QVariant::String)
294
resultobj = PyString_FromString(prop.toString().toUtf8().data());
295
// HIGHER ORDER TYPES
296
else if (prop.type() == QVariant::Point)
298
// Return a QPoint as an (x,y) tuple.
299
QPoint pt = prop.toPoint();
300
return Py_BuildValue("(ii)", pt.x(), pt.y());
302
else if (prop.type() == QVariant::Rect)
304
// Return a QRect as an (x,y,width,height) tuple.
305
// FIXME: We should really construct and return an object that
306
// matches the API of QRect and has properties to keep
307
// left/top/right/bottom and x/y/width/height in sync.
308
QRect r = prop.toRect();
309
return Py_BuildValue("(iiii)", r.x(), r.y(), r.width(), r.height());
311
else if (prop.type() == QVariant::StringList)
313
QStringList tmp = prop.toStringList();
314
return convert_QStringList_to_PyListObject(tmp);
319
PyErr_SetString(PyExc_TypeError, QObject::tr("Couldn't convert result type '%1'.").arg(prop.typeName()).toLocal8Bit().constData() );
323
// Return the resulting Python object
324
if (resultobj == NULL)
326
// An exception was set while assigning to resultobj
327
assert(PyErr_Occurred());
336
PyObject* scribus_setproperty(PyObject* /*self*/, PyObject* args, PyObject* kw)
338
PyObject* objArg = NULL;
339
char* propertyName = NULL;
340
PyObject* objValue = NULL;
341
char* kwargs[] = {const_cast<char*>("object"),
342
const_cast<char*>("property"),
343
const_cast<char*>("value"),
345
if (!PyArg_ParseTupleAndKeywords(args, kw, "OesO", kwargs,
346
&objArg, "ascii", &propertyName, &objValue))
349
// We're going to hang on to the value object for a while, so
350
// claim a reference to it.
353
// Get the QObject* the object argument refers to
354
QObject* obj = getQObjectFromPyArg(objArg);
357
objArg = NULL; // no need to decref, it's borrowed
359
const char* propertyTypeName = getpropertytype(obj, propertyName, true);
360
if (propertyTypeName == NULL)
362
QString propertyType = QString::fromLatin1(propertyTypeName);
364
// Did we know how to convert the value argument to the right type?
365
bool matched = false;
366
// Did the set call succceed?
367
bool success = false;
369
// Check the C++ type of the property, and try to convert the passed
370
// PyObject to something sensible looking for it.
371
// FIXME: handle enums/sets
373
// These are unfortuately a TOTAL PITA because of the multitude of
374
// C and Python numeric types. TODO This needs to be broken out into a subroutine.
375
if (propertyType == "bool")
378
if (PyObject_IsTrue(objValue) == 0)
379
success = obj->setProperty(propertyName, 0);
380
else if (PyObject_IsTrue(objValue) == 1)
381
success = obj->setProperty(propertyName, 1);
382
else if (PyInt_Check(objValue))
383
success = obj->setProperty(propertyName, PyInt_AsLong(objValue) == 0);
384
else if (PyLong_Check(objValue))
385
success = obj->setProperty(propertyName, PyLong_AsLong(objValue) == 0);
389
else if (propertyType == "int")
392
if (PyObject_IsTrue(objValue) == 0)
393
success = obj->setProperty(propertyName, 0);
394
else if (PyObject_IsTrue(objValue) == 1)
395
success = obj->setProperty(propertyName, 1);
396
else if (PyInt_Check(objValue))
397
success = obj->setProperty(propertyName, (int)PyInt_AsLong(objValue));
398
else if (PyLong_Check(objValue))
399
success = obj->setProperty(propertyName, (int)PyLong_AsLong(objValue));
403
else if (propertyType == "double")
406
// FIXME: handle int, long and bool too
407
if (PyFloat_Check(objValue))
408
success = obj->setProperty(propertyName, PyFloat_AsDouble(objValue));
413
else if (propertyType == "QString")
416
if (PyString_Check(objValue))
417
success = obj->setProperty(propertyName, QString::fromUtf8(PyString_AsString(objValue)));
418
else if (PyUnicode_Check(objValue))
420
// Get a pointer to the internal buffer of the Py_Unicode object, which is UCS2 formatted
421
const unsigned short * ucs2Data = (const unsigned short *)PyUnicode_AS_UNICODE(objValue);
422
// and make a new QString from it (the string is copied)
423
success = obj->setProperty(propertyName, QString::fromUtf16(ucs2Data));
428
else if (propertyType == "QCString")
431
if (PyString_Check(objValue))
433
// FIXME: should raise an exception instead of mangling the string when
434
// out of charset chars present.
435
QString utfString = QString::fromUtf8(PyString_AsString(objValue));
436
success = obj->setProperty(propertyName, utfString.toAscii());
438
else if (PyUnicode_Check(objValue))
440
// Get a pointer to the internal buffer of the Py_Unicode object, which is UCS2 formatted
441
const unsigned short * utf16Data = (const unsigned short *)PyUnicode_AS_UNICODE(objValue);
442
// and make a new QString from it (the string is copied)
443
success = obj->setProperty(propertyName, QString::fromUtf16(utf16Data).toAscii());
448
// HIGHER ORDER TYPES
449
// ... which I can't be stuffed supporting yet. FIXME.
453
PyErr_SetString(PyExc_TypeError,
454
QObject::tr("Property type '%1' not supported").arg(propertyType).toLocal8Bit().constData());
458
// If `matched' is false, we recognised the C type but weren't able to
459
// convert the passed Python value to anything suitable.
462
// Get a string representation of the object
463
PyObject* objRepr = PyObject_Repr(objValue);
464
Py_DECREF(objValue); // We're done with it now
467
// Extract the repr() string
468
QString reprString = QString::fromUtf8(PyString_AsString(objRepr));
471
// And return an error
472
PyErr_SetString(PyExc_TypeError,
473
QObject::tr("Couldn't convert '%1' to property type '%2'").arg(reprString).arg(propertyType).toLocal8Bit().constData());
477
// `success' is the return value of the setProperty() call
481
PyErr_SetString(PyExc_ValueError, QObject::tr("Types matched, but setting property failed.").toLocal8Bit().constData());
486
// Py_INCREF(Py_None);
491
/*! HACK: this removes "warning: 'blah' defined but not used" compiler warnings
492
with header files structure untouched (docstrings are kept near declarations)
494
void cmdgetsetpropdocwarnings()
497
s << scribus_propertyctype__doc__ << scribus_getpropertynames__doc__ << scribus_getproperty__doc__ << scribus_setproperty__doc__;
498
//Qt4 << scribus_getchildren__doc__ << scribus_getchild__doc__;