Qt界面开发中经常用到Model/View视图模型框架,通常一个Model对应多个View进行显示,节省刷新资源消耗。Model在使用是经常需要进行过滤或是排序,如果重新创建Model显然不是一个明智的选择,可以使用QSortFilterProxyModel完成过滤及排序,代理只是调整了两个数据集索引的映射关系,不是原Model的拷贝,增删改原数据会在代理上显现,同时节约了资源开销。
过滤可通过重写QSortFilterProxyModel的filterAcceptsRow函数实现。原本的过滤只有一级节点,没有遍历子节点判断是否满足条件,修改如下:
bool SortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
{
if(filterRegExp().isEmpty()==false)
{
QModelIndex source_index = sourceModel()->index(source_row, this->filterKeyColumn(), source_parent) ;
if(source_index.isValid())
{
// 遍历子节点,如果子节点符合过滤条件同样显示
for(int i=0; i<sourceModel()->rowCount(source_index); ++i){
if(filterAcceptsRow(i, source_index))
return true ;
}
return filterIndexByCustom(source_index);
}
}
// parent call for initial behaviour
return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent) ;
}
而排序重写lessThan函数即可,示例如下:
bool SortFilterProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
{
// 通过当前视图中的index位置获取model中实际的数据
QVariant leftData = sourceModel()->data(source_left);
QVariant rightData = sourceModel()->data(source_right);
// 颠倒顺序
return leftData.toString() > rightData.toString();
}
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class SortFilterProxyModel;
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void slot_reapplyFilter();
private slots:
void on_addBtn_clicked();
void on_delBtn_clicked();
void on_pushButton_clicked();
private:
Ui::MainWindow *ui;
private:
QStandardItemModel* m_sourceModel;
SortFilterProxyModel *m_proxyModel;
SortFilterProxyModel *m_checkModel;
};
class SortFilterProxyModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
SortFilterProxyModel(QObject *parent = nullptr);
virtual ~SortFilterProxyModel();
private:
virtual bool filterAcceptsRow(int source_row, const QModelIndex & source_parent) const;
virtual bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const;
protected:
virtual bool filterIndexByCustom(const QModelIndex & source_index) const;
};
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->patternCombox->addItem("RegExp",QRegExp::RegExp);
ui->patternCombox->addItem("Wildcard",QRegExp::Wildcard);
ui->patternCombox->addItem("Fixed string",QRegExp::FixedString);
m_sourceModel = new QStandardItemModel(this);
foreach(QString str, QColor::colorNames()){
auto tmpItem = new QStandardItem(str);
//tmpItem->setCheckable(true);
for (int i=0; i<3; i++) {
tmpItem->appendRow(new QStandardItem(QString::number(i)));
//tmpItem->child(i)->setCheckable(true);
for (int j=0; j<2; j++) {
tmpItem->child(i)->appendRow(new QStandardItem(QString::number(j + 100)));
}
}
m_sourceModel->appendRow(tmpItem);
}
m_proxyModel = new SortFilterProxyModel(this);
m_proxyModel->setSourceModel(m_sourceModel);// 设置源模型
m_proxyModel->setFilterKeyColumn(0);// 设置应用过滤的列
m_proxyModel->setFilterRole(Qt::DisplayRole);
m_proxyModel->setSortRole(Qt::DisplayRole);
m_checkModel = new SortFilterProxyModel(this);
m_checkModel->setSourceModel(m_sourceModel);
ui->sourceView->setModel(m_sourceModel);
ui->proxyView->setModel(m_proxyModel);
connect(ui->filterEdit, &QLineEdit::textChanged, this, &MainWindow::slot_reapplyFilter);
connect(ui->patternCombox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &MainWindow::slot_reapplyFilter);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::slot_reapplyFilter()
{
QRegExp::PatternSyntax syntax = (QRegExp::PatternSyntax)ui->patternCombox->currentData().toInt();
QRegExp regExp(ui->filterEdit->text(),Qt::CaseInsensitive,syntax);
m_proxyModel->setFilterRegExp(regExp);
}
void MainWindow::on_addBtn_clicked()
{
QString itemName = ui->itemEidt->text().trimmed();
if(!itemName.isEmpty()){
m_sourceModel->appendRow(new QStandardItem(itemName));
}
}
void MainWindow::on_delBtn_clicked()
{
QModelIndex currentIndex = ui->sourceView->currentIndex();
if(currentIndex.isValid()){
m_sourceModel->removeRow(currentIndex.row());
}
}
void MainWindow::on_pushButton_clicked()
{
m_proxyModel->sort(0);
}
SortFilterProxyModel::SortFilterProxyModel(QObject *parent)
: QSortFilterProxyModel(parent)
{
}
SortFilterProxyModel::~SortFilterProxyModel()
{
}
bool SortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
{
if(filterRegExp().isEmpty()==false)
{
QModelIndex source_index = sourceModel()->index(source_row, this->filterKeyColumn(), source_parent) ;
if(source_index.isValid())
{
// 遍历子节点
for(int i=0; i<sourceModel()->rowCount(source_index); ++i){
if(filterAcceptsRow(i, source_index))
return true ;
}
// 遍历父节点
//QModelIndex parentIndex = source_parent;
//while (parentIndex.isValid()) {
// if(filterIndexByCustom(parentIndex))
// return true;
// parentIndex = parentIndex.parent();
//}
return filterIndexByCustom(source_index);
}
}
// parent call for initial behaviour
return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent) ;
}
bool SortFilterProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
{
// 通过当前视图中的index位置获取model中实际的数据
QVariant leftData = sourceModel()->data(source_left);
QVariant rightData = sourceModel()->data(source_right);
// 颠倒顺序
return leftData.toString() > rightData.toString();
}
bool SortFilterProxyModel::filterIndexByCustom(const QModelIndex &source_index) const
{
QString key = sourceModel()->data(source_index, filterRole()).toString();
return key.contains(filterRegExp()) ;
}