I'm using SWIG to generate Python Bindings for my qt app. I have several places where I use QLists and I would like to integrate those QLists like std::vector from the SWIG Library (see http://www.swig.org/Doc1.3/Library.html#Library_nn15).
This means:
- The QList objects should be iterable from python (= they must be an iterable python object)
- It should be possible to pass a python list to a function which takes a qlist
- ... and all the other features listed in the SWIG Library for std::vector
To achieve that I use the following Code:
https://github.com/osmandapp/OsmAnd-core/blob/master/swig/java/QList.i
Later in my classes using QLists, I add code like:
%import "qlist.i"
%template(listfilter) QList<Interface_Filter*>;
class A {
public:
//.....
QList<Interface_Filter*> get_filters();
};
This works so far, but it doesn't give me the kind of integration I get with std::vector.
I'm having trouble finding out which parts of std_vector.i, std_container.i,... make an object iterable.
How do I need to extend the QList interface file to make my QList's iterable?
What you are asking for -- a qlist.i swig file that achieves the same level of integration for
QList
in python as std_vector.i does forstd::vector
-- is a non-trivial task.I provide a very basic extended qlist.i file (and qlisttest.i to show you how to use it) and will try to explain what steps are required.
qlist.i
:qlisttest.i
:Wrapping of
QList
to make it and its methods accessible from pythonThis is achieved by making the (partial) class definition available to swig. That is what your current
qlist.i
does.Note: You might need to add a "template specialization" for the case
QList<T*>
that typedefsconst_reference
asconst T*
since you are using aQList
of pointers. Otherwise,QList<T*>::const_reference
will beconst T*&
, which apparently might confuse swig. (see swig/Lib/std/std_vector.i)Automatic conversion between python list and
QList
This is generally achieved by using swig typemaps. For instance, if you want a function
f(const QList<int>& list)
to be able to accept a python list, you need to specify an input typemap that performs the conversion from a python list to aQList<int>
:Here, the situation is more difficult in several ways:
QList
: For this to work, you need to handle both cases in the typemap.T
to aQList<T>
:This also involves a conversion for every element of the list from the wrapped type
T
to the plainT
. This is achieved by the swig functionSWIG_ConvertPtr
.%qlist_conversions(Type)
that you can use to attach the typemap to theQList<Type>
for a specificType
.For the other conversion direction (
QList
-> python list) you should first consider what you want. Consider a C++ function that returns aQList<int>
. Calling this from python, should this return a wrappedQList
object, or should it automatically convert theQList
to a python list?Accessing the wrapped
QList
as a python sequence, i.e., makelen
and[]
work from pythonFor this, you need to extend the
QList
class in the qlist.i file using%extend { ... }
and implement__len__
and__getitem__
methods.If slicing should also work, you need to provide a
__getitem__(PySliceObject *slice)__
member method and input and "typecheck" typemaps forPySliceObject
s.If you want to be able to modify values in the wrapped
QList
using[]
from python, you need to implement__setitem__
.For a list of all the useful methods you can implement to achieve better integration, see the python documentation on "builtin types" and "abstract base classes for containers".
Note: If you use the swig
-builtin
feature, then you need to additionally register the above functions to the appropriate "slots" using e.g.Making the wrapped
QList
iterable from pythonFor this you need to extend the
QList
class and implement an__iter__()
method that returns a python iterator object.A python iterator object is an object that provides the methods
__iter__()
and__next__()
(next()
for older python), where__next__()
returns the next value and raises the python exceptionStopIteration
to signal the end.As mentioned before, you can implement the iterator object in python or C++. I show an example of doing this in python.
I hope this helps as a basis for you to tweak the functionality that you require.