ListView not displaying results of a QSortFilterProxyModel.
-
Hi, all -
I'm having a weird problem using a QSortFilterProxyModel to feed a QML ListView. The QML looks like this:
ListView { model: sanitizerModel delegate: Rectangle { height: 50; width: 50; color: 'red' }
And the function from the model:
bool SanitizerModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { bool rc = false; QModelIndex qmi = createIndex(sourceRow, 0); EquipmentCategory equipmentCategory = m_equipmentModel->data(qmi, EquipmentModel::EquipmentRoles::CategoryRole).value<EquipmentCategory>(); QUuid equipmentUuid = m_equipmentModel->data(qmi, EquipmentModel::EquipmentRoles::UuidRole).toUuid(); QUuid spaceUuid = m_spaceModel->getUuid(m_spaceIndex); if (equipmentCategory == CATEGORY_SANITIZER) { if (m_spaceModel->spaceContainsEquipment(spaceUuid, equipmentUuid)) { // ************ rc = true; } // ************ } return rc; }
Notice the lines with the // ************. If I remove that test, the delegate is displayed. With that test, though, the function still returns true, but the delegate isn't displayed.
I'm no expert with proxy models, but I've used a few others, and they all seem to work. Any idea what's going on here?
Thanks...
-
@mzimmers
If something does not make sense in code please put inqDebug()
statements proving your assertions. Do not rely on telling us what you perhaps think you understood from a debugger. Assumingif (m_spaceModel->spaceContainsEquipment(spaceUuid, equipmentUuid))
has no side effects it should not affect the result. Put in aqDebug()
with the result for every route out of your function. -
bool SanitizerModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { bool rc = false; QModelIndex qmi = createIndex(sourceRow, 0); EquipmentCategory equipmentCategory = m_equipmentModel->data(qmi, EquipmentModel::EquipmentRoles::CategoryRole).value<EquipmentCategory>(); QUuid equipmentUuid = m_equipmentModel->data(qmi, EquipmentModel::EquipmentRoles::UuidRole).toUuid(); QUuid spaceUuid = m_spaceModel->getUuid(m_spaceIndex); if (equipmentCategory == CATEGORY_SANITIZER) { if (m_spaceModel->spaceContainsEquipment(spaceUuid, equipmentUuid)) { rc = true; qDebug() << "Sanitizer found in space"; } else { qDebug() << "Sanitizer NOT found in space"; rc = false; } } return rc; }
Output:
11:07:58: Starting C:\Users\michael.zimmers\Qt_projects\NgaIcdFw\build\Desktop_Qt_6_5_3_MinGW_64_bit-Debug\appNgaIcdFw\appNgaIcdFw.exe... debug:default:qMain: int qMain(int, char**) translator.load() returned false. debug:default:qMain: int qMain(int, char**) style set to "Universal". debug:qml:expression for onCompleted: Main.qml: sysArch is x86_64. debug:default:SanitizerModel::filterAcceptsRow: Sanitizer NOT found in space. // at startup (before I display the ListView) debug:default:SanitizerModel::filterAcceptsRow: Sanitizer found in space. // when I display the ListView.
-
@JonB it's actually the opposite: when the app starts up, it reports false. When I navigate to the screen that contains the ListView, the filter is invoked again, and this time returns true. I've verified this with telltales as well as stepping through the debugger.
But: somehow, this extra test causes the ListView not to display the item. I honestly have no idea whether this is a QML problem, or a problem within the QSortFilterProxyModel class.
-
@mzimmers I am not sure I understand everything.
In your debug output, it displays "NOT found" then "found", does that mean that the proxy model eventually returns
true
and your line is displayed in theListView
or does theListView
still doesn't display the delegate even thought the proxy model returnstrue
?Also your code is incorrect, I don't think it should matter here but better safe than sorry. You are using a QModelIndex from your proxy model when calling your source model data method, you are not meant to do that.
Instead use a QModelIndex from the source model:QModelIndex qmi = m_equipmentModel->index(sourceRow, 0);
Also are you invalidating your filter at the right time? (when
spaceContainsEquipment
result is suspected to change) -
@GrecKo said in ListView not displaying results of a QSortFilterProxyModel.:
In your debug output, it displays "NOT found" then "found", does that mean that the proxy model eventually returns
true
and your line is displayed in theListView
or does theListView
still doesn't display the delegate even thought the proxy model returnstrue
?It's the latter. The "NOT found" occurs at application startup (I'm not sure why; I guess the QML engine pre-loads some stuff), but the "found" occurs when I navigate to the screen containing the ListView.
Also your code is incorrect, I don't think it should matter here but better safe than sorry. You are using a QModelIndex from your proxy model when calling your source model data method, you are not meant to do that.
Instead use a QModelIndex from the source model:QModelIndex qmi = m_equipmentModel->index(sourceRow, 0);
OK, fixed that. Thanks.
Also are you invalidating your filter at the right time? (when
spaceContainsEquipment
result is suspected to change)This may be the problem. I'm not explicitly invalidating the filter, because I'm not sure where to do it. The space model contains a list of spaces, each of which in turn contain a list of equipment. Do I need to send a signal from my space model to my equipment proxy model when that list of equipment changes?
Thanks...
EDIT:
I just noticed that upon closing and reopening the screen with the ListView, I get another confirmation from the proxy model that the equipment is in the space list, and...the delegate now shows up! I don't know what to make of this, but it's certainly interesting.
-
Anyone have any ideas on this? I'm really stuck. I've tried invalidating the model upon completing the screen, and I still don't get the update the first time I open it.
Component.onCompleted: { var index = spaceModel.getIndex(space.uuid) sanitizerModel.setSpaceIndex(index) }
void SanitizerModel::invalidateModel() { invalidate(); qDebug() << "SanitizerModel::invalidateModel: model invalidated; index is" << m_spaceIndex; } void SanitizerModel::setSpaceIndex(int index) { m_spaceIndex = index; invalidateModel(); // needed to force a refresh of the filtering. }
Thanks for any ideas...
-
@JoeCFD said in ListView not displaying results of a QSortFilterProxyModel.:
@mzimmers As we know, in any setting call a signal is needed to inform qml part for update. I do not see any signal.
Oh? I wasn't aware of that. Do I need to explicitly invoke a QML Connection to receive the signal?
EDIT:
I do have this:
SanitizerModel::SanitizerModel(EquipmentModel *equipmentModel, SpaceModel *spaceModel, QObject *parent) : m_equipmentModel(equipmentModel), m_spaceModel(spaceModel), QSortFilterProxyModel(parent) { QSortFilterProxyModel::setSourceModel(equipmentModel); QObject::connect(m_spaceModel, &SpaceModel::equipmentListChanged, this, &SanitizerModel::invalidateModel); }
And when the space model updates the equipment list of a space, it does send that signal to the proxy model. I thought that was sufficient, but I guess you're telling me that my proxy model needs to send a signal as well?
-
@mzimmers One example
auto InfoTableModel::setData( const QModelIndex & index, const QVariant & value, int role ) ->bool { switch( role ) { case Qt::DisplayRole: if ( index.row() >= 0 && index.row() < m_myList.size() ) { if ( m_myList[ index.row() ] != value.toString() ) { m_myList[ index.row() ] = value.toString(); emit dataChanged( index, index ); } } break; default: break; } return true; }
-
Both of my models properly implement the setData() function as far as I can tell.
Maybe I'm doing my proxy model incorrectly. The proxy model is set on an EquipmentModel (derived from a QAbstractListModel), but it's my SpaceModel (another QAbstractListModel) that actually sends the dataChanged() signal (hence the connection I show above). Does the signal need to come from the model that the proxy model is set on?
The SpaceModel also emits a equipmentListChanged() signal when the list changes, and I connect that signal to my proxy model in its constructor (as shown above). Is this not sufficient to provoke the screen update?