QSortFilterProxyModel subclass returns undefined records
Unsolved
General and Desktop
-
I have a Proxy class that inherits the QSortFilterProxyModel to enable filtering a model. The following is how the class behaves:
proxy.hpp:
class Proxy : public QSortFilterProxyModel { Q_OBJECT // Q_PROPERTY Q_PROPERTY(bool filterStatus READ getFilterStatus WRITE setFilterStatus NOTIFY filterStatusChanged) Q_PROPERTY(bool sensitivityStatus READ getSensitivityStatus WRITE setSensitivityStatus NOTIFY sensitivityStatusChanged) Q_PROPERTY(QString filterValue READ getFilterValue WRITE setFilterValue NOTIFY filterValueChanged) Q_PROPERTY(QString roleText READ getRoleText WRITE setRoleText NOTIFY roleTextChanged) public: explicit Proxy(QObject *parent = nullptr); ~Proxy(); // Fields private: bool m_FilterStatus; bool m_SensitivityStatus; QString m_FilterValue; QString m_RoleText; // Signals signals: void filterStatusChanged(); void sensitivityStatusChanged(); void filterValueChanged(); void roleTextChanged(); // PUBLIC Methods public: Q_INVOKABLE QVariantMap get(int proxyRow) const; // PRIVATE Methods private: int mapToRole(const QString &role) const; // PUBLIC Getters public: bool getFilterStatus() const; bool getSensitivityStatus() const; QString getFilterValue() const; QString getRoleText() const; // PUBLIC Setters public: void setFilterStatus(bool newFilterStatus); void setSensitivityStatus(bool newSensitivityStatus); void setRoleText(const QString &newRoleText); void setFilterValue(const QString &newFilterValue); // OVERRIDES protected: bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; };
proxy.cpp:
Proxy::Proxy(QObject *parent) : QSortFilterProxyModel(parent) , m_FilterStatus (true) , m_SensitivityStatus(true) , m_FilterValue("") , m_RoleText("") { #ifdef QT_DEBUG qDebug() << "objectName :" << this->objectName(); qDebug() << "Arguments :" << "None"; qDebug() << "Log Output :" << "None"; #endif } Proxy::~Proxy() { #ifdef QT_DEBUG qDebug() << "objectName :" << this->objectName(); qDebug() << "Arguments :" << "None"; qDebug() << "Log Output :" << "None"; #endif } QVariantMap Proxy::get(int proxyRow) const { QModelIndex proxyIndex = index(proxyRow, 0); QModelIndex sourceIndex = mapToSource(proxyIndex); QVariantMap itemMap; const auto roles = roleNames(); for (auto it = roles.constBegin(); it != roles.constEnd(); ++it) { itemMap.insert( QString::fromUtf8(it.value()), sourceModel()->data(sourceIndex, it.key()) ); } return (itemMap); } int Proxy::mapToRole(const QString &role) const { int roleIndex = sourceModel()->roleNames().key(role.toUtf8(), -1); return (roleIndex); } bool Proxy::getFilterStatus() const { return (m_FilterStatus); } bool Proxy::getSensitivityStatus() const { return (m_SensitivityStatus); } QString Proxy::getFilterValue() const { return (m_FilterValue); } QString Proxy::getRoleText() const { return (m_RoleText); } void Proxy::setFilterStatus(bool newFilterStatus) { if (m_FilterStatus == newFilterStatus) { return; } m_FilterStatus = newFilterStatus; emit filterStatusChanged(); // Force filter to update: invalidateFilter(); } void Proxy::setSensitivityStatus(bool newSensitivityStatus) { if (m_SensitivityStatus == newSensitivityStatus) { return; } m_SensitivityStatus = newSensitivityStatus; emit sensitivityStatusChanged(); m_SensitivityStatus == true ? setFilterCaseSensitivity(Qt::CaseSensitive) : setFilterCaseSensitivity(Qt::CaseInsensitive); // Force filter to update: invalidateFilter(); } void Proxy::setFilterValue(const QString &newFilterValue) { if (m_FilterValue == newFilterValue) { return; } m_FilterValue = newFilterValue; emit filterValueChanged(); // Force filter to update: invalidateFilter(); } void Proxy::setRoleText(const QString &newRoleText) { if (m_RoleText == newRoleText) { return; } m_RoleText = newRoleText; emit roleTextChanged(); // Force filter to update: invalidateFilter(); } bool Proxy::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { if (m_FilterStatus == false) { return (true); // Accept all rows } if(m_FilterValue == "") { return (true); // Accept all rows } if(m_RoleText == "") { return (true); // Accept all rows } QString data = sourceModel()->data( sourceModel()->index(sourceRow, 0, sourceParent), mapToRole(m_RoleText) ).toString(); #ifdef QT_DEBUG qDebug() << "Data found: " << data; #endif return (data == m_FilterValue); }
I expose the classes via a context to QML:
int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); QQmlApplicationEngine engine; Proxy *proxyModelName = new Proxy(&app); Proxy *proxyModelCity = new Proxy(&app); // Expose filters to QML via context property engine.rootContext()->setContextProperty("proxyModelName", proxyModelName); engine.rootContext()->setContextProperty("proxyModelCity", proxyModelCity); const QUrl url(QStringLiteral("qrc:/Model/main.qml")); QObject::connect( &engine, &QQmlApplicationEngine::objectCreated, &app, [url](QObject *obj, const QUrl &objUrl) { if (!obj && url == objUrl) QCoreApplication::exit(-1); }, Qt::QueuedConnection); engine.load(url); return app.exec(); }
To test the functionality I created a test model in QML:
import QtQuick 2.15 import QtQuick.Controls 2.15 import QtQuick.Layouts 1.15 ApplicationWindow { id: window visible: true width: 800 height: 600 title: "Proxy Model Filter Test" // A ListModel with multiple items and roles. ListModel { id: myListModel ListElement { name: "Alice"; age: 30; city: "New York" } ListElement { name: "Bob"; age: 25; city: "San Francisco" } ListElement { name: "Charlie"; age: 30; city: "Boston" } ListElement { name: "Diana"; age: 40; city: "Chicago" } ListElement { name: "Eve"; age: 25; city: "Los Angeles" } ListElement { name: "Frank"; age: 30; city: "Boston" } ListElement { name: "George"; age: 35; city: "Seattle" } ListElement { name: "Hannah"; age: 28; city: "Boston" } ListElement { name: "Ian"; age: 32; city: "New York" } ListElement { name: "Jane"; age: 29; city: "San Francisco" } ListElement { name: "Kevin"; age: 45; city: "Chicago" } ListElement { name: "Laura"; age: 30; city: "Los Angeles" } ListElement { name: "Mike"; age: 33; city: "Seattle" } ListElement { name: "Nina"; age: 27; city: "New York" } ListElement { name: "Oliver"; age: 31; city: "Boston" } ListElement { name: "Paula"; age: 26; city: "Chicago" } } Component.onCompleted: { proxyModelCity.roleText = "city"; proxyModelName.roleText = "name"; proxyModelCity.sourceModel = myListModel; proxyModelName.sourceModel = proxyModelCity; } ColumnLayout { anchors.fill: parent spacing: 10 RowLayout { spacing: 10 // TextField for filtering by name. Label { text: "Name:" Layout.alignment: Qt.AlignVCenter } TextField { id: nameField placeholderText: "Enter name" Layout.fillWidth: true onTextChanged: { // Update the filterRules: if empty, remove the name filter. if (nameField.text.trim() === "") { proxyModelName.filterValue = ""; } else { proxyModelName.filterValue = nameField.text.trim(); } } } // ComboBox for filtering by city. Label { text: "City:" Layout.alignment: Qt.AlignVCenter } ComboBox { id: cityComboBox model: ["All", "New York", "San Francisco", "Boston", "Chicago", "Los Angeles", "Seattle"] Layout.preferredWidth: 150 onCurrentTextChanged: { if (cityComboBox.currentText === "All") { proxyModelCity.filterValue = ""; } else { proxyModelCity.filterValue = cityComboBox.currentText.trim(); } } } } // ListView to display the filtered items. ListView { id: listView Layout.fillWidth: true Layout.preferredHeight: 300 model: proxyModelName delegate: Rectangle { width: listView.width height: 40 border.width: 1 Row { anchors.verticalCenter: parent.verticalCenter spacing: 10 Text { text: "Name: " + name; width: 150 } Text { text: "Age: " + age; width: 50 } Text { text: "City: " + city; width: 150 } Button { onClicked: { // 'index' is provided by the delegate. var itemData = proxyModelName.get(index); console.log("Row " + index + " data:", itemData["name"]); // This way you can pass the entire itemData map to something else. } } } } } } }
The good news is that the filters work correctly in finding the records that I specify, but the problem is that along with the correct records come a bunch of other ones which are undefined or non-existent:
I don't understand what is causing this and would really appreciate some help.
-
S Saviz marked this topic as a regular topic on
-
S Saviz marked this topic as a question on