Passing QLists to QML



  • Hi guys,

    I have some problems witch passing data to my QML layer.
    Currently I'm using QAbstractListModel but this doesn't seem to work for me.
    because based on the model i need several subsets of data based on that model.

    What I need is to pass QList<myobject*> to the QML layer but I must be able to set
    parameters for these lists like filter etc.

    What is ne correct way to pass a QList of Custom Objects to QML ?
    I need to register these list types so that i can use them as return type or something.

    Thanks for help.



  • try QTPATH\examples\quick\models\abstractitemmodel first.



  • I had the code for it once, i'll try to find it.

    --Update--

    I sadly haven't found it but you could read this article:
    "How To Expose Lists To QML":http://qt-project.org/wiki/How_to_expose_lists_to_QML

    If i find something new, i'll post it here.



  • Thanks guy,

    but the QDeclarativeListProperty don't seem to work in this case.
    Because I have no chance to filter these list properties.

    I Tried something like this:
    @
    QList<QVariant> ViewInterface::getClipByFklz(QString fklz)
    {
    QList<QVariant> list;
    QModelIndexList Items =this->model->match(
    this->model->index(0, 0),
    ClipModel::Fklz,
    QVariant::fromValue(fklz),
    -1,
    Qt::MatchRecursive);

    for( int i=0; i<Items.count(); ++i )
    {
        list.append(this->model->data(Items[i],ClipModel::ObjectRole));
    }
    return list;
    

    }
    @

    This is supposed to filter my model and build a QVariantList containing all Items in the model matching the filter criteria. This is one way to get a list up to the QML layer and I assume that the list will be deleted from memory as soon as the qml ListView will be destroyed.

    But this doesn't seem to work as a Model. The item are in the model but I'm unable to access the item properties. The delegate count is correct but the properties are undefined. If i loop through the model via JS everything is find and accessible.



  • You're welcome. Have you tried to set breakpoints in the data(), rowCount(), columnCount() functions in your datamodel and see which parts your qml access and which not?



  • I'll provide you a simple and easy to understnad sample Model i just made:

    model.h
    @#include <QModelIndex>

    class Model : public QAbstractListModel
    {
    Q_OBJECT
    public:
    Model(QObject *parent);
    ~Model();
    int rowCount(const QModelIndex &parent = QModelIndex()) const;
    int columnCount(const QModelIndex &parent = QModelIndex()) const;
    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
    enum Roles {
    NameRole = Qt::UserRole
    };
    QHash<int, QByteArray> roleNames() const {
    QHash<int ,QByteArray> roles;
    roles[NameRole] = "name";
    return roles;
    }
    };@
    model.cpp
    @#include "Model.h"
    #include <QImage>

    Model::Model(QObject *parent)
    :QAbstractListModel(parent)
    {
    }

    int Model::rowCount(const QModelIndex &parent) const
    {
    return 4;
    }
    int Model::columnCount(const QModelIndex &parent) const
    {
    return 2;
    }
    QVariant Model::data(const QModelIndex &index, int role) const
    {
    if(role == NameRole)
    {
    return QString("Row%1, Column%2")
    .arg(index.row() + 1)
    .arg(index.column() +1);
    }
    return QVariant();
    }
    Model::~Model(){

    }@

    This one can return a QString on request. It works fine if you pass it as a Model, it also should work if you add multiple of these into a QList<Model> and pass the QList to QML.

    I've set my a customrole in enum Roles {}. You can set as many roles as you want, just split them with a comma. I've set an let's call it "alias" for my roles which will replace the QML propertyname you pass with the given role in the QHash<int, QByteArray>.

    Feel free to ask if you've got still some Questions.



  • Hey onek24,

    thanks for this nice example it looks pretty much like my model.

    In the example above I showed you how I try to pass a list to qml containing a subset of the model.

    This is my data() function.
    @
    QVariant ClipModel::data(const QModelIndex &index, int role) const {
    if (!index.isValid())
    return QVariant();
    if (index.row() >= m_clips.size() || index.row() < 0)
    return QVariant();

    if (role == ObjectRole)
        return QVariant::fromValue(this->m_clips.at(index.row()));
    else if (role == Fklz)
        return QVariant::fromValue(this->m_clips.at(index.row())->getFklz());
    else
        return QVariant::fromValue(this->m_clips.at(index.row()));
    

    }
    @

    This creates a QVariantList for qml.
    Here I load QVariants with the ObjectRole of my model.
    @QList<QVariant> ViewInterface::getClipByFklz(QString fklz)
    {
    QList<QVariant> list;
    QModelIndexList Items =this->model->match(
    this->model->index(0, 0),
    ClipModel::Fklz,
    QVariant::fromValue(fklz),
    -1,
    Qt::MatchRecursive);

    for( int i=0; i<Items.count(); ++i )
    {
        list.append(this->model->data(Items[i],ClipModel::ObjectRole));
    }
    return list;
    

    }@

    My ClipModel stores ClipElements objects.

    On the QML side I try the following.
    model: viewinterface.getClipByFklz(curr_nav_fklz)
    The models of the listviews are set via the ViewInterface::getClipByFklz() function.
    @
    ListView {
    id: grid_clip_list
    anchors.fill: parent
    interactive: false
    orientation: ListView.Horizontal
    clip:true
    model: viewinterface.getClipByFklz(curr_nav_fklz) //each listview gets a model based on the current "fklz" key
    delegate: clip_delegate
    Component.onCompleted: {
    var temp = viewinterface.getClipByFklz(curr_nav_fklz);
    for (var i = 0; i < temp.length; i++)
    {
    console.log(temp[i].fklz); //This prints the fklz property of each ClipElement in the list.
    }
    }
    }
    @

    For debug purposes I load each list a second time in a temporary JS variable to print them.
    The console.log(temp[i].fklz); prints the property perfectly as expected.

    But in the clip_delegate I get an "ReferenceError: fklz is not defined" error.
    @
    Text {
    anchors.fill: parent
    text: {
    console.log(fklz);
    "fklz = " + fklz
    }
    }
    @

    Why can't I access the properties of the ClipElement objects in the delegate ?
    How can I debug the current ListElement of a delegate ?



  • Whats the point of getting Lists into QML ? Why is the support of model handling so neglected ?

    Why is there no way to just pass a simple QList<CustomObject*> to qml ?
    I found nothing like that.

    This article explains basically what I need but I didn't understand how this has to be implemented.
    http://lemirep.wordpress.com/2013/04/06/a-practical-case-exposing-qt-c-models-to-qml/

    Any ideas guy ?



  • As a workaround for the Problem that i couldn't access the properties of
    the model item. I'm referring to them via model.modelData.

    This seem to work for now.
    @
    Text {
    anchors.fill: parent
    text: {
    console.log(model.modelData.fklz);
    "fklz = " + model.modelData.fklz
    }
    }
    @



  • [quote author="Schneidi" date="1392207961"]Whats the point of getting Lists into QML ? Why is the support of model handling so neglected ?
    Why is there no way to just pass a simple QList<CustomObject*> to qml ?
    I found nothing like that.
    [/quote]

    That's an answer that I am also waiting for. Everything besides simple data types is like royal PITA or at least full of boilerplate code.



  • Sorry that i couldn't answer. It looks like it is working now? Glad to hear.
    What i would've tried is to create a QList<myModel> and append my models to it. Each model with it's own values. Then you can pass the QList to QML as a model. Theoretically this should work.



  • The problem is here that you need to derive myModel from QObject and thus must use QList<myModel*> and not QList<myModel>.



  • I found a way that works for me.
    As shown above I request submodels as QList<QVariant>.
    This allows me to pass lists of custom objects to qml.

    If I try to pass them as QList<myModel> I would need to register the type to
    qml or not ? QML tells me that the typ QList<myModel> is unkown.



  • [quote author="Schneidi" date="1392375824"]I found a way that works for
    If I try to pass them as QList<myModel> I would need to register the type to
    qml or not ? QML tells me that the typ QList<myModel> is unkown.
    [/quote]

    IMHO, yes.

    The funny thing is that Qt has solved that for QList<QObject*>.



  • [quote author="hardcodes.de" date="1392373170"]The problem is here that you need to derive myModel from QObject and thus must use QList<myModel*> and not QList<myModel>.[/quote]
    [quote author="Schneidi" date="1392375824"]I found a way that works for me.
    As shown above I request submodels as QList<QVariant>.
    This allows me to pass lists of custom objects to qml.

    If I try to pass them as QList<myModel> I would need to register the type to
    qml or not ? QML tells me that the typ QList<myModel> is unkown.

    [/quote]

    Yes, sorry, my bad. Like hardcodes.de already said: You'll have to derive your model from QObject and create the List like QList<QObject*> and append your models to it.



  • So far I see only two ways to handle this:

    1. you can use something like

    @
    Q_PROPERTY(QQmlListPropertyYourNameSpace::YourCustomType nameOfList READ nameOfList)
    @

    instead of a Q_PROPERTY with a QList. Therefore you need to register your custom type with

    @
    qmlRegisterTypeYourNameSpace::YourCustomType("Name_you_reference_in_QML", majorversionnumber, minorversionnumber, "QMLNameWhateverNeededFor");
    @

    And you have to register the class holding the list with setContextProperty. This QQmlListProperty introduces an extra way for the QML side to access the data in your model. But that doesn't work just out the box (again), you have to write extra functions to get/set the data from/into the list (yes, you keep the QList and must add extra functions, e.g. see QQmlListProperty::AppendFunction.
    What have we got here? A model has been changed (or maybe even polluted if you want), we added all that Q_PROPERTY stuff and the extra functions. IMHO that is ugly.

    You see the C++ and QML worlds as what they are: 2 different worlds with totally different approaches, object models and data types that need a translator for every communication between the two worlds. They are aliens.
    From this point of view an adapter pattern would come in handy and I think that's the way to look at all the variations of AbstractItem... models. Those are somehow like the guys with the white flag that transport messages between two parties at war. But this comes at the price that you have to write a lot of bolier plate code to fulfill the protocol both parties have agreed about: the interface functions. IMHO ugly, again. But the model can stay untouched, at least this feels better for me!

    Either way: more work, both not perfect.
    Strange when you think about where Qt came from: it originated as C++ framework that enabled you to write UIs with C++ (and yes, there were also AbstractItem... models). My problem is not so much that the UI has to written in QML, that's totally fine. I totally understand why they did it: each new platform and each change in an already supported platform introduced a huge amount of work. This work was transferred to the developer that uses the framework. This developer has to adapt the look and feel of the platform (if there is no library available that does that for you), but from a good starting position and with quite good tools.
    The point I can't understand is, that they somehow "forgot" an easy and universal way (in the sense of just a few lines of code or even better: no code at all) to get data in and out.
    Some say it's for your own good: a clean separation of ui and logic. To me it's like someone is saying "we found out that we gave you a hammer and you treated every problem as a nail". And then "our solution is: you keep the nails, we take away the hammer and give you a screwdriver instead". Ah, BTW: you can write Javascript inside QML, so much for separation of UI and logic.

    So now I took this thread as far off topic as possible and yes it's opinionated:-)


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.