Update C++ list model from QML
-
[Forked from https://forum.qt.io/topic/142156/passing-data-structures-from-qml-to-c --JKSH]
@JKSH thanks, that solved part of the problem. I can verify that I'm creating an item (and adding it to a QList in C++).
Somehow, despite my coding my list as per the demo I mentioned earlier, my displays aren't getting updated with the new list element. Any ideas on this?
-
The simplest answer to this is that you should not try this.
To add a new listitem from QML, you can have a invokable C++ method you call, but that should not have anything other than default types. Like ints, strings, date etc.
(Only exception to this is when you pass a class back from QML which was earlier passed into QML from C++).So, you can make your append() method simply be the one that instantiates the item on your list, and then emit the signal to indicate the list has been changed.
-
@TomZ when you say "should not try this," what specifically are you referring to? The tutorial I mentioned above uses a QVector<a_struct> as the basis for its list.
The tutorial does have a couple curious aspects, though. I've watched it 3 times now (at 50 minutes per!), and I still don't understand why he creates a new C++ class (ToDoList) as the "back end," then references that class in his QML. Isn't the point of a model to insulate the rest of the code from the back end?
Thanks...
-
@mzimmers said in Passing data structures from QML to C++:
when you say "should not try this," what specifically are you referring to?
The idea to write
Q_INVOKABLE void appendItem(Activity item)
and the implications of it. Rationale (and solution) I wrote in my previous reply. -
@TomZ if I understand you, you're objecting to my use of a struct as my list element? Not sure what I'd do instead. Here's the code for appendItem():
void ActivityModel::appendItem(Activity item) { m_list->appendItem(item); // another class with the actual list. emit listChanged(m_list); }
Not sure what I'd do differently here. I could modify appendItem() to not have a parameter, but then I don't know how I'd populate the newly-created item in QML.
Sorry if I sound confused; it's only because I really am.
-
@mzimmers said in Passing data structures from QML to C++:
if I understand you, you're objecting to my use of a struct as my list element?
No, I stated you should not have an invokable append() method which has the struct.
I further stated; "To add a new listitem from QML, you can have a invokable C++ method you call, but that should not have anything other than default types. Like ints, strings, date etc."So go ahead and have anything as your list item. Most of us use QObject or string based list-items.
-
Maybe the point can be driven home by simply understanding object ownership.
Your list is owned by the model. The QML can read it, but can never directly delete items from your list. The model is the one that creates new items and deletes items. It owns the list and its list-items.
So if you want your QML to add a new item somehow, you will still need the model to be the one that instantiates the new list-item. And understand that as soon as the list changes, the view will follow.
-
@TomZ I've modified my append function as follows:
void ActivityModel::appendItem(Activity item) { Activity newItem = item; m_list->appendItem(newItem); emit listChanged(m_list); }
Using the debugger, I verified that the list now contains the new item. But the view doesn't change.
-
@JoeCFD that did it. Following the example in the tutorial, I had created this function:
void ActivityModel::setList(ActivityList *list) { beginResetModel(); if (m_list != nullptr) { m_list->disconnect(this); } m_list = list; if (m_list != nullptr) { connect(m_list, &ActivityList::preItemAppended, this, [=]() { const int index = m_list->activities().size(); beginInsertRows(QModelIndex(), index, index); }); connect(m_list, &ActivityList::postItemAppended, this, [=]() { endInsertRows(); }); connect(m_list, &ActivityList::preItemRemoved, this, [=](int index) { beginRemoveRows(QModelIndex(), index, index); }); connect(m_list, &ActivityList::postItemRemoved, this, [=]() { endRemoveRows(); }); } endResetModel(); }
but I guess it didn't get invoked.
The tutorial really is confusing in the area where it creates a new class for the list. It adds a level of indirection that makes it harder to understand the interaction between C++ and QML.
-
You can use a QAbstractListModel to update the list model from QML. The QAbstractListModel provides an interface for accessing data from a list of items. It can be used to update the list model from QML by using the setData() method.
To update the list model from QML, you will need to create a QAbstractListModel subclass and implement the setData() method. The setData() method should take the index of the item to be updated and the new value for the item. You can then call the setData() method from QML to update the list model.
For example, if you have a list of strings, you can create a QAbstractListModel subclass and implement the setData() method as follows:
void MyListModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (index.isValid() && role == Qt::EditRole) {
int row = index.row();
QStringList list = data(index, Qt::DisplayRole).toStringList();
list[row] = value.toString();
setData(index, QVariant::fromValue(list), Qt::DisplayRole);
}
}You can then call the setData() method from QML to update the list model. For example:
MyListModel {
id: myListModel
// ...
}Button {
text: "Update List Model"
onClicked: {
myListModel.setData(myListModel.index(0, 0), "New Value", Qt.EditRole);
}
}This will update the first item in the list model with the new value.
-
-