QSortFilterProxyModel的使用

引言

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();
}

完整代码

Demo效果如下:
在这里插入图片描述

#include <QMainWindow>
#include <QDialog>
#include <QSortFilterProxyModel>
#include <QStringListModel>
#include <QListView>
#include <QComboBox>
#include <QLineEdit>
#include <QStandardItemModel>
#include <QStyledItemDelegate>
#include <QTreeView>

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()) ;
}
  • 4
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Arui丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值