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?
-
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?
Hi @gardiol
There is no way to do so unless you callsetContextProperty
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 likebeginResetModel()
andendResetModel()
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)
-
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)
-
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?
-
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?
@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.
-
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.