Subclassed QSortFilterProxyModel setData always returns false



  • Hello,

    I am using a subclassed QSortFilterProxyModel to replace a boolean column with a checkbox (Using the codes i found here: http://qt-project.org/wiki/QSortFilterProxyModel_subclass_for_readonly_columns_columns_with_checkboxes_and_password_columns). I have managed to display a checkbox on a QTableView but clicking the column or the checkbox itself does not toggle the values in the column.

    I used QSqlQueryModel as source model. Here are my codes:

    testtableviewcheckbox.h

    @#ifndef TESTTABLEVIEWCHECKBOX_H
    #define TESTTABLEVIEWCHECKBOX_H

    #include <QWidget>

    namespace Ui {
    class TestTableViewCheckBox;
    }

    class TestTableViewCheckBox : public QWidget
    {
    Q_OBJECT

    public:
    explicit TestTableViewCheckBox(QWidget *parent = 0);
    ~TestTableViewCheckBox();

    private:
    Ui::TestTableViewCheckBox *ui;
    };

    #endif // TESTTABLEVIEWCHECKBOX_H@

    testtableviewcheckbox.cpp

    @#include "testtableviewcheckbox.h"
    #include "ui_testtableviewcheckbox.h"
    #include <QtSql/QSqlDatabase>
    #include <QAbstractTableModel>
    #include "testtableview.h"
    #include <QSqlQuery>
    #include <QSqlQueryModel>
    #include "checkablesortfilterproxymodel.h"

    QSqlDatabase testdb = QSqlDatabase::addDatabase("QPSQL");

    TestTableViewCheckBox::TestTableViewCheckBox(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::TestTableViewCheckBox)
    {
    ui->setupUi(this);

    testdb.setHostName("localhost");
    testdb.setDatabaseName("paciolidb");
    testdb.setUserName("postgres");
    testdb.setPassword("postgres");
    
    //TestCheckableTableModel *model = new TestCheckableTableModel;
    bool opendb = testdb.open();
    QSqlQueryModel *model = new QSqlQueryModel;
    CheckableSortFilterProxyModel *filterProxyModel = new CheckableSortFilterProxyModel;
    
    if (opendb == true)
    {
        QSqlQuery query(testdb);
    
        query.prepare("SELECT * FROM pacioli.fget_user_privileges(?)");
        query.bindValue(0,4);
        query.exec&#40;&#41;;
    
        model->setQuery(query);
        filterProxyModel->setSourceModel(model);
    
        testdb.close();
    }
    
    ui->tableView->setModel(filterProxyModel);
    

    }

    TestTableViewCheckBox::~TestTableViewCheckBox()
    {
    delete ui;
    }@

    checkablesortfilterproxymodel.h

    @#ifndef CHECKABLESORTFILTERPROXYMODEL_H
    #define CHECKABLESORTFILTERPROXYMODEL_H

    #include <QSortFilterProxyModel>

    class CheckableSortFilterProxyModel : public QSortFilterProxyModel
    {
    Q_OBJECT
    public:
    explicit CheckableSortFilterProxyModel(QObject *parent = 0);

    protected:
    QVariant data(const QModelIndex &index, int role) const;
    bool setData(const QModelIndex &index, const QVariant &value, int role);
    Qt::ItemFlags flags(const QModelIndex &index) const;

    signals:

    public slots:

    };

    #endif // CHECKABLESORTFILTERPROXYMODEL_H@

    checkablesoftfilterproxymodel.cpp

    @#include "checkablesortfilterproxymodel.h"
    #include <QDebug>

    CheckableSortFilterProxyModel::CheckableSortFilterProxyModel(QObject *parent) :
    QSortFilterProxyModel(parent)
    {
    }

    QVariant CheckableSortFilterProxyModel::data(const QModelIndex &index, int role) const
    {

    if (!index.isValid())
    {
        return QVariant();
    }
    
    if ((index.column() == 3) && (role == Qt::CheckStateRole || role == Qt::DisplayRole))
    {
    
        if (role == Qt::CheckStateRole)
        {
            return index.data(Qt::EditRole).toBool() ? QVariant(Qt::Checked) : QVariant(Qt::Unchecked);
        }
        else if (role == Qt::DisplayRole)
        {
            return QVariant();
        }
    
    }
    else
    {
        return QSortFilterProxyModel::data(index, role);
    }
    

    }

    bool CheckableSortFilterProxyModel::setData(const QModelIndex &index, const QVariant &value, int role)
    {

    if(!index.isValid())
    {
        return false;
    }
    
    if(index.column() == 3 && role == Qt::CheckStateRole)
    {
        QVariant data = value.toInt() == Qt::Checked ? QVariant(1) : QVariant(0);
        qDebug() << data;
        qDebug() << QSortFilterProxyModel::setData(index, data, Qt::CheckStateRole);
        return QSortFilterProxyModel::setData(index, data, Qt::EditRole);
    }
    else
    {
        return QSortFilterProxyModel::setData(index,value,role);
    }
    

    }

    Qt::ItemFlags CheckableSortFilterProxyModel::flags(const QModelIndex &index) const
    {

    if(!index.isValid())
    {
        return Qt::ItemIsEnabled;
    }
    
    if(index.column() == 3)
    {
        return Qt::ItemIsUserCheckable | Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable;
    }
    else
    {
        return QSortFilterProxyModel::flags(index);
    }
    

    }
    @

    Just in case it would help, here is the actual stored procedure that populates QSqlQueryModel:

    @SET search_path TO pacioli;

    CREATE OR REPLACE FUNCTION fget_user_privileges(IN _user_id bigint)
    RETURNS TABLE (module_id bigint,
    privilege_id bigint,
    privilege character varying(50),
    user_privilege integer)
    AS
    $$
    BEGIN
    SET search_path TO pacioli;

    RETURN QUERY SELECT pl.module_id,
    pl.privilege_id,
    pl.privilege,
    CASE WHEN a.privilege_id IS NULL THEN 0
    ELSE 1
    END AS user_privilege
    FROM tprivilege_list pl
    LEFT JOIN (SELECT ua.privilege_id
    FROM tuser_access ua
    WHERE ua.user_id = _user_id) a
    ON pl.privilege_id = a.privilege_id;
    END;
    $$ LANGUAGE plpgsql;@

    Changing the value on the column would not change values on a table directly (The source table is just a view). I only need to get a toggle signal so that I can perform an insert to another table when the cell on a column is checked and perform a delete when a cell is unchecked. The QTableView can be refreshed when a value is changed. I am quite new to Qt. I hope someone could help. Thanks in advance.



  • Hello again,

    I have managed to make this work by ignoring the return value and by emitting a signal but it feels more like a dirty hack than an actual solution.

    I had to remove a line that sets the return value of setData to avoid crashing the program (when the model is refreshed). The return value always returns false anyway so I only need to emit signal.

    This is what I did with the setData method:

    @
    bool CheckableSortFilterProxyModel::setData(const QModelIndex &index, const QVariant &value, int role)
    {
    bool setDataReturn = false;

    if(!index.isValid())
    {
        setDataReturn = false;
    }
    
    if(index.column() == 0 && role == Qt::CheckStateRole)
    {
        QVariant data = value.toInt() == Qt::Checked ? QVariant(1) : QVariant(0);
        emit checkBoxToggled(data.toInt(), index.row());
        //setDataReturn = QSortFilterProxyModel::setData(index, data, Qt::EditRole); this line crashes the program when the model is repopulated/refreshed.
    }
    else
    {
        setDataReturn = QSortFilterProxyModel::setData(index,value,role);
    }
    
    return setDataReturn;
    

    }
    @

    I added this line to checkablesortfilterproxymodel.h :

    @
    signals:
    void checkBoxToggled(int newValue,int rowIndex);
    @

    I added this slot to the class that would use the proxymodel :
    @
    private slots:

    void modifyPrivilege(int newValue, int rowIndex);
    

    @

    I've connected the emitted signal in the constructor of my class:

    @
    connect(privilegeProxy, SIGNAL(checkBoxToggled(int,int)),
    this, SLOT(modifyPrivilege(int,int)));
    @

    Here is the slot:
    @
    void UserWindow::modifyPrivilege(int newValue, int rowIndex)
    {
    User userInfo;
    qint64 userID = ui->accessUserIDLineEdit->text().toInt();
    QModelIndex privilegeIDIndex = privilegeProxy->index(rowIndex, 3, QModelIndex());
    qint64 privilegeID = ui->accessPrivilegesTableView->model()->data(privilegeIDIndex).toInt();
    QModelIndex userAccessIDIndex = privilegeProxy->index(rowIndex, 1, QModelIndex());
    qint64 userAccessID = ui->accessPrivilegesTableView->model()->data(userAccessIDIndex).toInt();

    if (newValue == 1)
    {
        qDebug() << "userID: " << userID << "privilegeID: " << privilegeID;
        userInfo.addUserPrivilege(userID, privilegeID);
    }
    else if (newValue == 0)
    {
        qDebug() << "userAccessID: " << userAccessID;
        userInfo.removeUserPrivilege(userAccessID);
    }
    
    moduleIndex = ui->accessUserModuleTableView->selectionModel()->currentIndex();
    int moduleRow = moduleIndex.row();
    moduleIndex = moduleIndex.sibling(moduleRow, 0);
    moduleID = ui->accessUserModuleTableView->model()->data(moduleIndex).toString();
    
    populatePrivilegeTableView();
    

    }
    @

    Here is populatePrivilegeTableView() :
    @
    void UserWindow::populatePrivilegeTableView()
    {
    User userInfo;
    qint64 userID = ui->accessUserIDLineEdit->text().toInt();

    QSqlQueryModel *privilegeListModel = new QSqlQueryModel;
    
    privilegeListModel->setQuery(userInfo.getUserPrivileges(userID));
    privilegeProxy->setSourceModel(privilegeListModel); 
    privilegeProxy->setFilterFixedString(moduleID);
    
    ui->accessPrivilegesTableView->setModel(privilegeProxy);
    

    }
    @
    While this code already works as I intended, maybe someone could point out why the setData always returns false (on my first post) and why the program crashes when the model is refreshed (when the commented line on setData is uncommented).


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.