Solved Why binding to MyAbstractItemModel.rowCount does not work?
-
Hi
I have this ListView with pictures, sometimes when there are no pictures in the model, I want the ListView to have width and height set to 0, so I have used binding to width and height but it does not work. As a workaround I have used onCountChanged to recalculate ListView width and height. My model calls functions like beginInsertRows so why this binding does not work?
ListView { id: plantPicView snapMode: ListView.SnapOneItem highlightRangeMode: ListView.StrictlyEnforceRange orientation: Qt.Horizontal width:(PictureModel.rowCount() ? parent.width : 0) height:(PictureModel.rowCount() ? 3*width/4 : 0) anchors.horizontalCenter: parent.horizontalCenter clip: true z:10 model: PictureModel delegate: PictureDelegate { z:11 source: pic_name_role } spacing: 0 onCountChanged: { console.log("plantPicView count:"+count) width=(PictureModel.rowCount() ? parent.width : 0) height=(PictureModel.rowCount() ? 3*width/4 : 0) } } //piece of model code void PictureModel::setCatalogId(int catalog_id) { // beginResetModel(); if(picMap.count()) { beginRemoveRows(QModelIndex(), 0, picMap.count()-1); QMapIterator<int,PictureModelDataStruct*> i(picMap); while(i.hasNext()) { i.next(); delete i.value(); } picMap.clear(); endRemoveRows(); } int row=0; QList<int> list=catalogData->getPicList(catalog_id); for(int i=0;i<list.count();i++) { PictureModelDataStruct *p=new PictureModelDataStruct; p->pic_id=list.at(i); p->row=row; p->fileName=QString("images/%1.jpg").arg(p->pic_id); p->available=1; picMap.insert(p->row,p); beginInsertRows(QModelIndex(), row, row); endInsertRows(); row++; qDebug()<<"PictureModel::setCatalogId fileName:"<<p->fileName; } // endResetModel(); }
Best Regards
Marek -
@Marek width:(PictureModel.rowCount() ? parent.width : 0) is wrong, you can't bind to a function. It's evaluated only once when the binding is created.
If you want it to be simple in QML but complicated in C++ you have to write your own property to the class which is used in the QML binding. If you want it to be complicated in QML but simple in C++ you have to write signal handlers in QML for existing rowsInserted and rowsRemoved C++ signals (or your own countChanged as you apparently have done).
Maybe the former is what you want. Use Q_PROPERTY with 'changed' signal (see the examples in the Qt docs) and emit that signal when the row count is changed in C++. Then you can use that property in QML bindings, and the value is re-evaluated automatically when the signal is sent.
-
@Eeli-K thanks ;)
So in short words, don't bind to function as this is evaluated only once when binding is created, instead bind to property and send signal when property has changed. Corrected example below
ListView { id: plantPicView snapMode: ListView.SnapOneItem highlightRangeMode: ListView.StrictlyEnforceRange orientation: Qt.Horizontal width:(PictureModel.rows ? parent.width : 0) height:(PictureModel.rows ? 3*width/4 : 0) anchors.horizontalCenter: parent.horizontalCenter clip: true z:10 model: PictureModel delegate: PictureDelegate { z:11 source: pic_name_role } spacing: 0 } //part of model.h code class PictureModel : public QAbstractListModel { Q_OBJECT public: Q_PROPERTY(int rows MEMBER m_rows NOTIFY rowsChanged) //part of model.cpp code void PictureModel::setCatalogId(int catalog_id) { if(picMap.count()) { beginRemoveRows(QModelIndex(), 0, picMap.count()-1); QMapIterator<int,PictureModelDataStruct*> i(picMap); while(i.hasNext()) { i.next(); delete i.value(); } picMap.clear(); endRemoveRows(); } int row=0; QList<int> list=catalogData->getPicList(catalog_id); for(int i=0;i<list.count();i++) { PictureModelDataStruct *p=new PictureModelDataStruct; p->pic_id=list.at(i); p->row=row; p->fileName=QString("images/%1.jpg").arg(p->pic_id); p->available=1; picMap.insert(p->row,p); beginInsertRows(QModelIndex(), row, row); endInsertRows(); row++; qDebug()<<"PictureModel::setCatalogId fileName:"<<p->fileName; } m_rows=picMap.count(); emit rowsChanged(); }
-
@Marek Looks like what I meant. One clarification to my comment: the whole binding expression is evaluated when a property used in that expression is changed; in your original code it was evaluated once, in other cases it may be evaluated more often, but the function call itself (like getSomeProperty()) can't trigger a binding re-evaluation.