Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. QSortFilterProxyModel subclass returns undefined records
Qt 6.11 is out! See what's new in the release blog

QSortFilterProxyModel subclass returns undefined records

Scheduled Pinned Locked Moved Unsolved General and Desktop
qsortfilterproxmodel
1 Posts 1 Posters 476 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • ? Offline
    ? Offline
    A Former User
    wrote on last edited by
    #1

    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:
    Problem.png

    I don't understand what is causing this and would really appreciate some help.

    1 Reply Last reply
    0
    • System marked this topic as a regular topic on
    • System marked this topic as a question on

    • Login

    • Login or register to search.
    • First post
      Last post
    0
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    • Get Qt Extensions
    • Unsolved