How to create a new instance of a c++ Model exposed to qml via setContextProperty ?
-
Greetings!
I've implemented a custom QAbstractListModel in c++ and exposed it to QML with setContextProperty like so:int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); QQuickView view; MyListItem* myListItem = new MyListItem(); view.rootContext()->setContextProperty("myListItem", myListItem); view.setSource(QUrl(QLatin1String("qrc:/main.qml"))); return app.exec(); }
I use a listview to display the data in the model, and it works well. However I often repopulate my model with new data and am wondering how can I retain the previous data.
For example, if I have a model for Actors that connects to a database and retrieves actors based on a query, a user can query for all the actors who star in movie A which will populate the model with those actors, a user can then make another query for all the actors who star in movie B, at this point the model will be repopulated with actors from movie B.
I would like a way to go back and forth between those results. I thought a solution would be to make a new instance of the model for each user query, but I don't know how to do it using
setContextProperty
.Do I do something like this ?
QString functionCalledOnUserQuery(QQuickView &view) { MyListItem* myListItem1 = new MyListItem(); view.rootContext()->setContextProperty("myListItem1", myListItem1); return "myListItem1"; }
How would I use myListItem1 in QML instead of myListItem ? Can I dynamically assign models in Listview
model property ? -
Yes, you can dynamically swap the model. I'm doing this right now.
I'm not using c++ with my ListView - I'm building an object model in JSI have a qml file with a List view wrapped in an Item. That item has an object:
property var model : [];
The ListView's model: references that variable.
Outside, in other files that I use that component I overwrite this variable such as if I gave the id: formItem;
formItem.model = tmpModModel;
you could retain the old model by doing the opposite:
tmpModModel= formItem.model ;
I don't know about your database questions nor have I proven the c++ ... I fully expect yes seeing I can say yes, I am swapping ListView models inside QML and doing it probably too much - right now I'm getting it to work and later if any performance issues... it's very quick right now...
-
@Curtwagner1984 Another option could be caching the data inside the c++ model. By reading your explanation I suppose you have a simple list of Actor data (like QList<Actor>). Just create another list, you can even make a dynamic history list. In the c++ model you have a member function goBackInHistory or swapHistory or whatever. It has
beginResetModel(); // do the swap so that subsequent calls to data(), rowCount() etc. will give correct results with new data endResetModel();
If you want to have several views with different data you should have several models, this one would work only if one data set is visible at a time. I believe C++ caching saves execution time and space compared to several model objects.
-
@6thC
That's a good idea, I'll try to see if your suggestion works with c++ models as well.
Assuming it will work, I now am more worried about how to manage the models.@Eeli-K
That's a very interesting idea.
My actual use case is as follows:
I have Actors, Movies, and Tags in my database, and each of them has a separate model, each of them has two views. a list view which lists all actors movie or tags in the database and a detail view which is slightly different for each one. An Actor detail view has a list of movies and this actors tags, similarly a Movie detail view has a list of actors and a list of movie tags.I'm using the same Actor model every time I display an actor list, so there is an actor list in the actor list view which lists all the actors, and there is a actor list in the Movie and Tags detail view.
I'm using a StackView to navigate between the views. So when I go to the actor list view, it populates the Actor model with all the actors and then pushes the view on to the stack, then if I go to a movie detail view, it populates the Actor model with different actors and pushes that view on to the stack. Now when I pop the current item from the stack, I'm back at the Actor List view, but the Actor Model is populated with actors from the Movie Detail view.
What I wanted is to create a new instance of the model for each of those views, but your suggestion seems to be better.
Though I'm not really sure how to implement it ... To be more specific how to sync and swap the correct lists in the Model.Maybe I should pass the StackView index every time I repopulate the model.
Let's say I haveQList<Actor> actors
member in my model, I'll add a hashmap that will store all the dynamically created new lists
QMap<QString,QList<Actor>> actorsLists
and each time I repopulate the model I will store the new list inactorsLists[currentStackIndex] = newModelData;
-
Well, there's no reason for it not to work - they are both the same ListView QML object?
It's just dynamically swapping in and out object models to it's model right?Regarding managing them ... wouldn't it be the same sets of data you are using in c++ code?
Swapped under the same conditions? It's just the language you do it in... no doubt c++ may well be quicker.Saying that, QML / JS is dead simple - I have put one of my controls bound to a 1ms Timer as:
Timer { interval: 1; running: true; repeat: true onTriggered: control.value = Math.floor((Math.random()*1000)+1); }
It's rendering model updates and index changes amazingly well.
This is my hardware: https://valid.x86.fr/8qm8px - I consider this rather low spec.I'm generating and swapping my model every value update. If I had performance issues I would be less lazy but it works and works well apparently so I can now move on to other development tasks.
P.S. the size of the model is the range between new and old value plus some so can change from say 3 to thousands
-
The issue was solved by registering the C++ model with QML as an Instantiable Object Type as described here and then instantiating the model from QML for each new search page, as described in this topic: Is it possible to expose a C++ Class that doesn't have a default constructor to QML ?
Thank you for everyone who tried to help!