How can you know when all the delegates of a View or Repeater have been created once the model is set?
-
Hi
I am having synchronization problems within my C++/QML application.After I set the
model
property of aRepeater
inside aColumn
which functions as a drawer of sorts. Then, I reparent some of the items in that Column to a Grid.In my first version, the model was a QML
XmlListModel
, and then the above worked fine.
Now, I have replaced the XmlListModel by an equivalent C++ model, exposed as a custom QML type, but things now no longer work, because the reparenting is now apparently started before theRepeater
items are created.In this new approach, I therefore need to wait until all
Repeater
items have been created. Is there an event I can use for this, other than to count (in the delegate'sonCompleted()
) the number of instantiated items myself? (I am already using that convoluted approach for another model/view part, but I'm wondering if there is a better way). -
Hi,
maybe the problem is in c++, how are you linking c++ to qml you Register the Type in c++?
Hi @Diackne
Thanks for reacting.you Register the Type in c++
Yes, I register the model as a QML-type in C++, having implemented, amongst others, the following methods:
int rowCount(const QModelIndex & parent = QModelIndex()) const; QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); Q_INVOKABLE QVariantMap get(int row);
-
@Diracsbracket said in How can you know when all the delegates of a View or Repeater have been created once the model is set?:
Q_INVOKABLE QVariantMap get(int row);
Q_INVOKABLE is the problem, you maybe in your qml code call this get(N) but not the reactive way!! if c++ ends after the first call of get(N) your result of get(N) return empty
maybe you need follow this ideactxt->setContextProperty("myModel", &model);
Repeater { model: myModel // this notify c++ --> Qml delegate: Text { text: type + ", " + size } }
but in qml who you pass the model only get(N) or inside repeater and the repeater have the model?
-
@Diracsbracket said in How can you know when all the delegates of a View or Repeater have been created once the model is set?:
Q_INVOKABLE QVariantMap get(int row);
Q_INVOKABLE is the problem, you maybe in your qml code call this get(N) but not the reactive way!! if c++ ends after the first call of get(N) your result of get(N) return empty
maybe you need follow this ideactxt->setContextProperty("myModel", &model);
Repeater { model: myModel // this notify c++ --> Qml delegate: Text { text: type + ", " + size } }
but in qml who you pass the model only get(N) or inside repeater and the repeater have the model?
At the time I created this post, I did it as follows:
Flickable{ id: sourceFlick .... Column { id: buttonList Repeater { model: xmlButtonModel ... delegate: DragTile {} } } }
I used a
Repeater
because I needed all the delegates to be instanciated at all times (as opposed to only when they are visible). But in the meanwhile, I learned that I could do the same with aListView
by setting thecacheBuffer
to an appropriate value, and now I do things like this:ListView { id: buttonList ... model: xmlButtonModel .... delegate: DragTile {} }
The model and
ListView
are part of anItem
, of which several instances are created dynamically, based on the number of XML files found in a directory:Item { id: layout XmlListModel { id: xmlButtonModel ... onStatusChanged: { if (status == XmlListModel.Ready) { ... Js.createGridPages() ... } } } Rectangle { id: drawer ... ListView { id: buttonList ... model: xmlButtonModel .... delegate: DragTile {} } } Item { id: layoutItem ... SwipeView { id: swipeView ... contentItem: ListView { ... model: ListModel { id: viewModel } delegate: ButtonGrid {} } } } }
So for a given layout
Item
, once the XML model is loaded, the layout is populated byJs.createGridPages()
, which creates the pages inswipeView
, each page containing aGrid
of cells to which items ofbuttonList
are reparented based on a user-preferences file...I must note that this reparenting process even fails occasionally when simply rebuilding + autolaunching the app via Qt Creator (target device is Android), even when I don't use my faster C++ XML model. The error is always the same: some of the
DragTile
delegates of mybuttonList
is stillundefined
. -
At the time I created this post, I did it as follows:
Flickable{ id: sourceFlick .... Column { id: buttonList Repeater { model: xmlButtonModel ... delegate: DragTile {} } } }
I used a
Repeater
because I needed all the delegates to be instanciated at all times (as opposed to only when they are visible). But in the meanwhile, I learned that I could do the same with aListView
by setting thecacheBuffer
to an appropriate value, and now I do things like this:ListView { id: buttonList ... model: xmlButtonModel .... delegate: DragTile {} }
The model and
ListView
are part of anItem
, of which several instances are created dynamically, based on the number of XML files found in a directory:Item { id: layout XmlListModel { id: xmlButtonModel ... onStatusChanged: { if (status == XmlListModel.Ready) { ... Js.createGridPages() ... } } } Rectangle { id: drawer ... ListView { id: buttonList ... model: xmlButtonModel .... delegate: DragTile {} } } Item { id: layoutItem ... SwipeView { id: swipeView ... contentItem: ListView { ... model: ListModel { id: viewModel } delegate: ButtonGrid {} } } } }
So for a given layout
Item
, once the XML model is loaded, the layout is populated byJs.createGridPages()
, which creates the pages inswipeView
, each page containing aGrid
of cells to which items ofbuttonList
are reparented based on a user-preferences file...I must note that this reparenting process even fails occasionally when simply rebuilding + autolaunching the app via Qt Creator (target device is Android), even when I don't use my faster C++ XML model. The error is always the same: some of the
DragTile
delegates of mybuttonList
is stillundefined
.I finally found what was happening.
I incorrectly assumed (from a few debug output samples) that when using a
Repeater
to instantiate the cells of myGrid
, the cells would always be listed first in thechildren
list ofGrid
and that theRepeater
item would always be listed as the last child (i.e. cells would be "inserted" in the list before theRepeater
as they got instantiated) .Based on that assumption, I directly used the cell index (let's say i) to get the corresponding item as
children[i]
.The above assumption was apparently wrong: once in a while, the
Repeater
item would not always be the last child, and a cell could come after it... Therefore, my above approach of using the grid cell index to retrieve the item sometimes resulted in getting theRepeater
item instead...To avoid this, I have eliminated the Repeater, and simply instantiate the cells dynamically in the
Component.onCompleted()
handler. This allows me to directly use the grid cell index to retrieve the item from thechildren
list, not having to worry about other types of children... -
Why not use the
Repeater
'sitemAt(index)
method? -
@GrecKo
That's a good one, I didn't think there would be such a method! Thanks for pointing it out!