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
Forum Updated to NodeBB v4.3 + New Features

QSortFilterProxyModel subclass returns undefined records

Scheduled Pinned Locked Moved Unsolved General and Desktop
qsortfilterproxmodel
1 Posts 1 Posters 138 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.
  • SavizS Offline
    SavizS Offline
    Saviz
    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
    • SavizS Saviz marked this topic as a regular topic on
    • SavizS Saviz 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