QSortFilterProxyModel subclass for readonly columns columns with checkboxes and password columns

From Qt Wiki
Jump to: navigation, search
This article may require cleanup to meet the Qt Wiki's quality standards. Reason: Auto-imported from ExpressionEngine.
Please improve this article if you can. Remove the {{cleanup}} tag and add this page to Updated pages list after it's clean.

QSortFilterProxyModel subclass for readonly, checkboxes and password columns

When you try to use a QTableView with QSql*' model classes, it is difficult to make boolean column appear as check boxes or make columns readonly.

This is a QSortFilterProxyModel subclass that is designed to be generic enough to easily accomplish these tasks. It also includes some primitive 'password' field support.

If you want to add a check box to a model but you don't want to use a boolean field in the source model to keep track of the value of the check box, you can take a look at CheckableProxyModel.

You should create three
QList<int>
container classes, one each to hold the column indexes of the fields you want checkboxes for, to be displayed as passwords, or be readonly.
  • NOTE: If you are using a QSqlRelationalTableModel, there are issues with the QSortFilterProxyModel (the combo box). To resolve that, I have created another wiki article about a QSqlRelationalDelegate subclass (mySqlRelationalDelegate) that you can use.
  • EDIT: Since I discovered another problem with the handling of NULL values, I have added the possibility to this class to address it.

Usage

The subclass usage is as follows:

CheckableSortFilterProxyModel *cfpm = new CheckableSortFilterProxyModel(this);
QList<int> boolCols;
boolCols.append( usrModel->fieldIndex("isActive") );
boolCols.append( usrModel->fieldIndex("isOk") );

QList<int> readonlyCols;
readonlyCols.append( usrModel->fieldIndex("id") );

QList<int> pwdCols;
pwdCols.append( usrModel->fieldIndex("passwordFld"));

cfpm->setParameters(boolCols, readonlyCols, pwdCols);

cfpm->setSourceModel( mySqlTableModel );
myTableView->setModel(cfpm);

QList<int> nullCols;
QList<int> notNullCols;
notNullCols.append( usrModel->fieldIndex("isActive");
cfpm->setNotNullColumns(nullCols, notNullCols);

Code

So here is the code:

checkablesortfilterproxymodel.h

#ifndef CHECKABLESORTFILTERPROXYMODEL_H
#define CHECKABLESORTFILTERPROXYMODEL_H

#include <QSortFilterProxyModel>

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

void setParameters(QList<int> boolCols, QList<int> readonlyCols, QList<int> passwordCols);
 void setNullAndNotNullColumns(QList<int> nullCols, QList<int> notNullCols);

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

protected:
 bool filterAcceptsRow ( int source_row, const QModelIndex & source_parent ) const;

signals:

public slots:

private:
 QList<int> booleanSet;
 QList<int> passwordSet;
 QList<int> readonlySet;
 QList<int> notNullSet;
 QList<int> nullSet;

};

checkablesortfilterproxymodel.cpp

#include "checkablesortfilterproxymodel.h"

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

void CheckableSortFilterProxyModel::setParameters(QList<int> boolCols, QList<int> readonlyCols, QList<int> passwordCols) {
 booleanSet.clear();
 readonlySet.clear();
 passwordSet.clear();

if (!boolCols.isEmpty()) { foreach(int column , boolCols) { booleanSet.append(column); } }
 if (!readonlyCols.isEmpty()) { foreach(int column , readonlyCols) { readonlySet.append(column); } }
 if (!passwordCols.isEmpty()) { foreach(int column , passwordCols) { passwordSet.append(column); } }

}

void CheckableSortFilterProxyModel::setNullAndNotNullColumns(QList<int> nullCols, QList<int> notNullCols) {
 notNullSet.clear();
 nullSet.clear();

if (!notNullCols.isEmpty()) {
 // this only works when our model is a QSqlQueryModel subclass
 if (sourceModel()->inherits("QSqlQueryModel")) {
 foreach(int column , notNullCols) { notNullSet.append(column); }
 }
 invalidateFilter();
 }

 if (!nullCols.isEmpty()) {
 // this only works when our model is a QSqlQueryModel subclass
 if (sourceModel()->inherits("QSqlQueryModel")) {
 foreach(int column , nullCols) { nullSet.append(column); }
 }
 invalidateFilter();
 }
}

bool CheckableSortFilterProxyModel::filterAcceptsRow ( int source_row, const QModelIndex & source_parent ) const {
 Q_UNUSED(source_parent);
 if (!notNullSet.isEmpty()) {
 QSqlQueryModel '''m = static_cast<QSqlQueryModel'''>(sourceModel());
 foreach (int column, notNullSet)
 if (m->record(source_row).isNull(column))
 return false;

bool CheckableSortFilterProxyModel::filterAcceptsRow ( int source_row, const QModelIndex & source_parent ) const {
 Q_UNUSED(source_parent);

if (!notNullSet.isEmpty()) {
 QSqlQueryModel '''m = static_cast<QSqlQueryModel'''>(sourceModel());
 foreach (int column, notNullSet)
 if (m->record(source_row).isNull(column))
 return false;
 }

if (!nullSet.isEmpty()) {
 QSqlQueryModel '''m = static_cast<QSqlQueryModel'''>(sourceModel());
 foreach (int column, nullSet)
 if (!m->record(source_row).isNull(column))
 return false;
 }
 return true;
}

QVariant CheckableSortFilterProxyModel::data(const QModelIndex &index, int role) const {
 if (!index.isValid())
 return QVariant();

if (booleanSet.contains(index.column()) && (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 if (passwordSet.contains(index.column()) && (role == Qt::DisplayRole)) {
 return QVariant("''''''*");
 } else
 return QSortFilterProxyModel::data(index,role);

return QVariant();
}

bool CheckableSortFilterProxyModel::setData(const QModelIndex &index, const QVariant &value, int role) {
 if(!index.isValid())
 return false;

if(booleanSet.contains(index.column()) && roleQt::CheckStateRole)
    {
        QVariant data = (value.toInt()Qt::Checked) ? QVariant(1) : QVariant (0);
 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 (booleanSet.contains(index.column()))
 return Qt::ItemIsUserCheckable | Qt::ItemIsSelectable | Qt::ItemIsEnabled;
 else if (readonlySet.contains(index.column()))
 return Qt::ItemIsSelectable;
 else
 return QSortFilterProxyModel::flags(index);

}