Model-View C++ to QML update issue



  • I have a listview on QML:

      ListView {
            anchors.fill: parent
            model: mymodel
            delegate: ListDelegate  {
            }
        }
    

    And my delegate (it's into a saperate file called ListGelegate.qml):

    Item {
        width: parent.width
        height: button1.height
        Text {
            id: name
            text: title
            anchors.left: parent.left
            anchors.top: parent.top
            anchors.bottom: parent.bottom
            verticalAlignment: Text.AlignVCenter
            horizontalAlignment: Text.AlignLeft
        }
        Button {
            id: button1
            anchors.right: parent.right
            text: "open"
            onClicked:{
                 do my stuff here
            }
        }
    }
    

    My model is implemented in C++, and looks like:

    class MyClass
    {
        private:
            QList<QObject*> list_of_objects;
        public:
            QList<QObject*>& getModel() const
            {
                return  list_of_objects;
            }
            <other class related stuff here>
    }
    

    Then i register the model for the QML:

        myClass = new MyClass();
        engine.rootContext()->setContextProperty( "mymodel", QVariant::fromValue( myClass.getModel() ) );
    

    I can properly see in the view any item which has been already loaded in the model BEFORE i call the setContectProperty. Anything i add or remove AFTER that, well, the view does not get updated.
    Please consider the list get modified based on events which are outside the QML control so i need the C++ side to trigger an update, possibly without getting too involved in the QML since i would like to keep it independent as much as possible...

    Am i missing something?


  • Moderators

    Hi @gardiol
    There is no way to do so unless you call setContextProperty again. Its documented here.

    Note: There is no way for the view to know that the contents of a QList has changed. If the QList changes, it is necessary to reset the model by calling QQmlContext::setContextProperty() again.

    A more better and controlled approach would be to create custom model by subclassing QAbstractItemModel. You can use methods like beginResetModel() and endResetModel() or dataChanged signal to make view aware of the changes.



  • Thanks for the quick reply!

    I have a question, suppose i create my model like this:

    class MyModel: public QAbstractListModel
    {
    public:
        MyModel();
        ~MyModel();
        void addITem( QObject* item )
        {
            int n_items = _items.size();
            _items.append( item );
            emit dataChanged( n_items, n_items+1 );
        }
        int rowCount(const QModelIndex &parent) const;
        QVariant data(const QModelIndex &index, int role) const;
        QVariant headerData(int section, Qt::Orientation orientation, int role) const;
    private:
        QList<QObject*> _items;
    };
    

    How do i pass this to the QML side? If i still call (see OP for code):

    setContextProperty( "mymodel", QVariant::fromValue( myClass.getModel() ) );
    

    Would'nt this still bind a COPY of the model to the QML side, so that an update will update the MyClass internal member and NOT the QML copy?

    (supposing, of course, i change the return type of getModel() and MyClass to host a MyModel instance instead of a QList instance)


  • Moderators

    @gardiol Yes that should work.



  • Ok, i am having some pratcical issues here:

    QVariant MyModel::data(const QModelIndex &index, int /*role*/) const
    {
        if ( !index.isValid() )
            return QVariant();
        QObject* item= _items.at( index.row() );
        return item;
    }
    

    This does not compile because QVariant's void* constructor is private.... But then what do i need to do???

    Also, i had to change:

    MyModel& MyClass::getModel();
    setContextProperty( "mymodel", QVariant::fromValue( myClass.getModel() ) );
    

    to:

    MyModel* MyClass::getModel();
    setContextProperty( "mymodel", myClass.getModel() );
    

    Or it would not compile, is this correct?


  • Moderators

    @gardiol Dont return the object itself instead create setters and getters methos to get and set data. Follow the simple example here.

    Regarding second problem, it depends on how you return (reference or pointer) from that function.
    For eg:
    Following will work:

    MyModel *model = new MyModel;
    ...
    
    MyModel &MyClass::getModel()
    {
        return *model;
    }
    ...
    setContextProperty( "mymodel", QVariant::fromValue( myClass.getModel() ) );
    

    Also this too

    MyModel *model = new MyModel;
    
    MyModel *MyClass::getModel()
    {
        return model;
    }
    
    setContextProperty( "mymodel", QVariant::fromValue( myClass.getModel() ) );
    


  • Thank you very much!

    I am now returning a pointer and it works perfectly. I also studied a bit about roles in models and it seems i managed to get it working properly!

    I have to say, it's quite complex and elaborated. I understand it's a solution to a generic problem, but still a very verbose solution to a simple specific problem. Never been a fan of the model/view paradigm myself, but it's pretty flexible once you get it going.


  • Moderators

    @gardiol Yeah at first it seems to be quite complex (overriding so much stuff etc.. ) but as the data grows and the view becomes more complex the Model/View turns out to be a boon.


Log in to reply
 

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