【Qt编程之Widgets模块】-006:QSortFilterProxyModel代理的使用方法

  • QSortFilterProxyModel是model的代理,不能单独使用,真正的数据需要另外的一个model提供,它的工鞥呢是对被代理的model(source model)进行排序和过滤。所谓过滤:也就是说按着你输入的内容进行数据的筛选,因为器过滤功能是基本正则表达式,所以功能强大。
  • QsortFilterProxyModel类用来为model和view之间提供强大的排序和过滤支持。将模型排序或者过滤后在视图上显示,并且无需对模型中的数据进行任何转换,也无需对模型在中数据进行修改

1 QSortFilterProxyModel使用提要

  • 使用过滤器需要指定一个数据模型(QStandardItemModel)作为数据源,并且该数据模型无需设置到表对象上;
    过滤器指定好数据源后设置到表对象上即可正常使用
    过滤器不指定过滤列时,默认过滤列为0列

2.QSortFilterProxyModel自定义排序

  • 自定义排序需要子类化QsortFilterProxyModel,然后重写lessThan().
  • 注意 : 如果重写了lessThan(),那么就不会再调用model的sort方法了.
    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);
    switch ( source_left.column() )
    {
        case 0 :     //序号,需要判断数字
        case 3 :     //信号ID,需要判断数字
            return leftData.toInt() < rightData.toInt();
            break;
        default :    //其它,只判断字符串
            return leftData.toString() < rightData.toString();
            break;
    }
 
    return true;     
 
}

除了排序外,QSortFilterProxyModel还可以用来隐藏与某个过滤器不匹配的项。使用QRegExp对象指定筛选器,并将筛选器应用于给定列的每个项的filterRole() (默认情况下为Qt::DisplayRole)。QRegExp对象可用于匹配正则表达式、通配符模式或固定字符串。

3.过滤方法1-使用setFilterKeyColumn()过滤列

  • 首先需要通过void QsortFilterProxyModel::setFilterRegExp(const QRegExp ®Exp)来设置FilterProxyModel的过滤器. 然后通过QsortFilterProxyModel::setFilterKeyColumn(int)来过滤某一列.如果要更改大小写匹配,可以通过QsortFilterProxyModel::sortCaseSensitivity()来设置.
  • 示例代码如下所示:
QTableView *view = new QTableView;   
MyItemModel *sourceModel = new MyItemModel(this);
QSortFilterProxyModel *proxyModel = new QSortFilterProxyModel(this);
proxyModel->setSourceModel(sourceModel); //将model放入代理中   
view->setModel(proxyModel);             //在视图中安装代理
 
QRegExp regExp("^(-?\\d+)(\\.\\d+)?$", Qt::CaseSensitive, QRegExp::RegExp);
//通过^(-?\d+)(\.\d+)?$来匹配整数
proxyModel->setFilterRegExp(regExp);   //安装过滤器
 
proxyModel->setFilterKeyColumn(0);    
proxyModel->setFilterKeyColumn(2);    //将第一列和第三列同时是整数的数据显示出来.
  • 每当过滤格式改变,则setFilterRegExp()重新更新过滤器即可.
    弊端:
  • 但是这样只能"与方式"显示model,要第一列和第三列公共是整数的才能显示出来,不能实现"或方式"显示.
    所以,如果要使用联合多列过滤,建议使用过滤方法2来实现.

4.过滤方法2-重写filterAcceptsRow成员函数

以实现"只要第一列有整数或者第三列有整数的都显示出来"为例,首先需要子类化QsortFilterProxyModel类,然后重写filterAcceptsRow()或者filterAcceptsColumn()函数. 由于我们筛选第一列和第三列,列号是明确的,而行号是未知的, 所以我们只重写filterAcceptsRow()函数.

示例代码如下所示:

bool SortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
{
    //获取model中实际的数据
    QString dataColumn1 =  sourceModel()->index(source_row, 0, source_parent).data(Qt::DisplayRole).toString();
    QString dataColumn3 =  sourceModel()->index(source_row, 2, source_parent).data(Qt::DisplayRole).toString();
    if(dataColumn1.contains(this->filterRegExp()))     
    {
        return true;
    }
    else if(dataColumn3.contains(this->filterRegExp())) 
    {
        return true;
    }
    return false;        
 
}
  • 然后创建SortFilterProxyModel类时,只需要安装过滤器即可:
SortFilterProxyModel *proxyModel = new SortFilterProxyModel();
proxyModel->setSourceModel(sourceModel);            //将model放入代理中   
treeView->setModel(proxyModel);                     //在视图中安装代理
proxyModel->setFilterRegExp("^(-?\\d+)(\\.\\d+)?$"); //安装过滤器
  • 每当过滤格式改变,则setFilterRegExp()重新更新过滤器即可.

注意事项:

  • 如果过滤方式改变了,比如从过滤第1列变成了过滤第2列,需要调用invalidateFilter()函数,使之前的过滤失效,激活当前过滤.
setSourceModel:用于设置哪个model被代理
setSortCaseSensitivity:用来设置排序时是否区分大小写
setFilterKeyColumn:用来制定当前过滤的列,参数为列好
setFiliterRegExp:用于设置过滤时的筛选规则,参数类型为QRegExp

HardwareLogWidget.h

class CHardwareLogWidget : public QWidget
    {
        Q_OBJECT

    public:
        CHardwareLogWidget(QWidget *parent = Q_NULLPTR);
        ~CHardwareLogWidget();

    protected:
        void showEvent(QShowEvent *event);

    private slots:
        // 重置筛选条件
        void onReset();

        // 查询
        void onQuery();

        // 导出
        void onExport();

    private:
        // 初始化原始model
        void initialModel();

        std::vector<HardWareOperateLogStr> getFilterLog();

    private:
        Ui::CHardwareLogWidget ui;
        CLogProxyModel *m_pLogProxyModel{ nullptr };        // 数据模型对象指针
        QStandardItemModel *m_pRrecordModel{ nullptr };     // 数据模型对象指针
    };

HardwareLogWidget.cpp


CHardwareLogWidget::CHardwareLogWidget(QWidget *parent)
    : QWidget(parent)
{
    ui.setupUi(this);
    connect(ui.m_pBtnReset, SIGNAL(clicked()), this, SLOT(onReset()));
    connect(ui.m_pBtnQuery, SIGNAL(clicked()), this, SLOT(onQuery()));     
    connect(ui.m_pBtnExport, SIGNAL(clicked()), this, SLOT(onExport()));
    ui.m_pLineEditUserName->setValidator(new QRegExpValidator(QRegExp("[a-zA-Z0-9]{1,16}")));
    // 弹出日历的形式
    ui.dateTimeEdit->setCalendarPopup(true);
    ui.dateTimeEdit_2->setCalendarPopup(true);
    // 全部/添加用户/删除用户...
    ui.m_pComboType->addItem(tr("All"));
    for (int i = UserAdd; i <= EquipmentModify; i++)
    {
        ui.m_pComboType->addItem(CLogTypeConverter::logtype2String(static_cast<HardWareOperateType>(i)));
    }
    // 设置的选择0好索引
    ui.m_pComboType->setCurrentIndex(0);
    
	//===============数据模型(QStandardItemModel)===============
	//建立数据模型对象空间并指定父对象
    // 设置以项数据(item data)为基础的标准数据模型
    m_pRrecordModel = new QStandardItemModel(this);
    // 添加列标题
    m_pRrecordModel->setHorizontalHeaderItem(0, new QStandardItem(tr("Operate Time")));
    m_pRrecordModel->setHorizontalHeaderItem(1, new QStandardItem(tr("User name")));
    m_pRrecordModel->setHorizontalHeaderItem(2, new QStandardItem(tr("Operate Type")));
    m_pRrecordModel->setHorizontalHeaderItem(3, new QStandardItem(tr("Operate Detail")));

    for (size_t i = 0; i < 4; i++)
    {
        m_pRrecordModel->horizontalHeaderItem(i)->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter);
    }
	
	//===============过滤器模型(QSortFilterProxyModel)===============
    //建立过滤器模型对象空间并指定父对象
    // 设置CLogProxyModel::QSortFilterProxyModel代理
    m_pLogProxyModel = new CLogProxyModel(this);
    // 指定过滤器模型的数据源模型
    m_pLogProxyModel->setSourceModel(m_pRrecordModel);
	// 指定初始化过滤列
    m_pFilterModel->setFilterKeyColumn(0);
    //! 将过滤器模型设置到表对象上
    //! 数据模型就单纯当过滤器模型的数据源即可
    ui->tableView->setModel(m_pFilterModel);
    
	// 获取tableView的水平表头
    QHeaderView *verticalHeader = ui.tableView->verticalHeader();
    // 使用固定行高大小
    verticalHeader->setSectionResizeMode(QHeaderView::Fixed);
    // 设置tableview所有列的默认行高为54
    verticalHeader->setDefaultSectionSize(54);
    // 所有对setColumnWidth()的调用都要放在setModel()之后,所有对setColumnWidth()的调用都要放在setModel()之后
    ui.tableView->setModel(m_pLogProxyModel);
    // 设置表格列宽
    ui.tableView->setColumnWidth(0, 300);
    ui.tableView->setColumnWidth(1, 250);
    ui.tableView->setColumnWidth(2, 250);

    // 隐藏表头
    verticalHeader->hide();
    // 不显示网格
    ui.tableView->setShowGrid(false);
    // 行选择
    ui.tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
    // 设置为可以选中多个目标
    ui.tableView->setSelectionMode(QAbstractItemView::ExtendedSelection);
    // 设置为不可编辑状态
    ui.tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
    // 设置无交点
    ui.tableView->setFocusPolicy(Qt::NoFocus);
    // 初始化
    initialModel();
    // 重置
    onReset();
}
void CHardwareLogWidget::onReset()
{
     ui.m_pLineEditUserName->clear();
     m_pLogProxyModel->setFilterKey("");
     ui.m_pComboType->setCurrentIndex(0);
     m_pLogProxyModel->setFilterLogType(ui.m_pComboType->currentText());
     // 默认筛选时间为一年内
     ui.dateTimeEdit_2->setDateTime(QDateTime::currentDateTime());
     ui.dateTimeEdit->setDateTime(QDateTime::currentDateTime().addYears(-1));
     m_pLogProxyModel->setDateTimeRange(QDateTime::currentDateTime().addYears(-1), QDateTime::currentDateTime());
     m_pLogProxyModel->setFilterRegExp(QRegExp());
     m_pLogProxyModel->setSourceModel(m_pRrecordModel);
 }

 //
 //
 void CHardwareLogWidget::onQuery()
 {
     m_pLogProxyModel->setFilterKey(ui.m_pLineEditUserName->text());
     m_pLogProxyModel->setFilterLogType(ui.m_pComboType->currentText());
     QDateTime min = ui.dateTimeEdit->dateTime();
     QDateTime max = ui.dateTimeEdit_2->dateTime();
     if (min.toTime_t() > max.toTime_t())
     {
         QToolTip::showText(ui.dateTimeEdit->mapToGlobal(QPoint(0, 0)), tr("max time must be more than min time"));
         return;
     }
     QString str1 = min.toString("yyyy-MM-dd hh:mm:ss");
     m_pLogProxyModel->setDateTimeRange(min, max);
     m_pLogProxyModel->setFilterRegExp(QRegExp());
     m_pLogProxyModel->setSourceModel(m_pRrecordModel);
 }

2 获取过滤器当前选中的文本

  • 获取文本比较简单,步骤如下:
    通过表对象的点击事件能拿到点击位置的QModelIndex对象
    使用转到槽/自定义槽函数+链接信号槽接收点击信号发出的QModelIndex对象
    在槽函数中实现获取当前选中的文本
std::vector<HardWareOperateLogStr> CHardwareLogWidget::getFilterLog()
{
    std::vector<HardWareOperateLogStr> vecReturn;
    int rowCount = m_pLogProxyModel->rowCount();
    for (int i = 0; i < rowCount; i++)
    {
        HardWareOperateLogStr struLog;
        // 通过当前视图中的index位置获取model中实际的数据
        struLog.logTime = m_pLogProxyModel->data(m_pLogProxyModel->index(i, 0)).toString();
        struLog.strUserNumber = m_pLogProxyModel->data(m_pLogProxyModel->index(i, 1)).toString();
        struLog.strOperateType= m_pLogProxyModel->data(m_pLogProxyModel->index(i, 2)).toString();
        struLog.strDetailInfo = m_pLogProxyModel->data(m_pLogProxyModel->index(i, 3)).toString();
        vecReturn.push_back(struLog);
    }
    return vecReturn;
}

3 修改当前选中item的文本

修改item文本,首先需要获取到item对象,方法和获取文本一样都需要接收发出点击信号和参数,但槽函数内容如下:

存放当前点击对象的item指针,在.h/.cpp文件中如下定义

4 实现多列分别过滤

20181207151416201

原理,重写QSortFilterProxyModel类中的filterAcceptsRow即可:

源码如下:

mysortfilterproxymodel.h

#ifndef MYSORTFILTERPROXYMODEL_H
#define MYSORTFILTERPROXYMODEL_H
 
#include <QSortFilterProxyModel>
#include <QRegExp>
 
class MySortFilterProxyModel : public QSortFilterProxyModel
{
    Q_OBJECT
public:
   MySortFilterProxyModel(QObject *parent = 0);
   void setRxCol1(const QString rx);
   void setRxCol2(const QString rx);
 
protected:
   bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const Q_DECL_OVERRIDE;
 
private:
   QString m_rxCol1;
   QString m_rxCol2;
 
};
 
#endif // MYSORTFILTERPROXYMODEL_H

widget.h

#ifndef WIDGET_H
#define WIDGET_H
 
#include <QWidget>
 
QT_BEGIN_NAMESPACE
class QStandardItemModel;
class QSortFilterProxyModel;
QT_END_NAMESPACE
 
class MySortFilterProxyModel;
 
namespace Ui {
class Widget;
}
 
class Widget : public QWidget
{
    Q_OBJECT
 
public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();
 
protected slots:
    void col1LineEditChanged(const QString text);
    void col2LineEidtChanged(const QString text);
 
protected:
    void intsertModel(const int row, const int col, const QString data);
 
private:
    Ui::Widget *ui;
    QStandardItemModel *m_model;
    MySortFilterProxyModel *m_filterModel;
};
 
#endif // WIDGET_H

main.cpp

#include "widget.h"
#include <QApplication>
 
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
 
    return a.exec();
}

mysortfilterproxymodel.cpp

#include "mysortfilterproxymodel.h"
#include <QModelIndex>
#include <QDebug>
 
MySortFilterProxyModel::MySortFilterProxyModel(QObject *parent) : QSortFilterProxyModel(parent)
{
    m_rxCol1 = "";
    m_rxCol2 = "";
}
 
void MySortFilterProxyModel::setRxCol1(const QString rx)
{
    m_rxCol1 = rx;
}
 
void MySortFilterProxyModel::setRxCol2(const QString rx)
{
    m_rxCol2 = rx;
}
 
bool MySortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
{
    QModelIndex index0 = sourceModel()->index(source_row, 0, source_parent);
    QModelIndex index1 = sourceModel()->index(source_row, 1, source_parent);
 
    return (sourceModel()->data(index0).toString().contains(m_rxCol1)
            && sourceModel()->data(index1).toString().contains(m_rxCol2));
}

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include "mysortfilterproxymodel.h"
#include <QStandardItemModel>
#include <QRegExp>
#include <QDebug>
#include <QSortFilterProxyModel>
 
Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
 
    this->setWindowTitle("CSDN IT1995");
    m_model = new QStandardItemModel;
    m_filterModel = new MySortFilterProxyModel;
    QStringList headList;
    headList << "第一列" << "第二列" << "第三列" << "第四列";
    m_model->setHorizontalHeaderLabels(headList);
 
    ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
 
    for(int row = 0; row < 100; row++){
 
        for(int col = 0; col < 4; col++){
 
            intsertModel(row, col, "第" + QString::number(row) + "行,第" + QString::number(col) + "列");
        }
    }
 
    intsertModel(100, 0, "中文");
    intsertModel(100, 1, "China");
    intsertModel(100, 2, "japan");
    intsertModel(100, 3, "日本人");
    intsertModel(101, 0, "中文");
    intsertModel(101, 1, "东京");
    intsertModel(101, 2, "东京热");
    intsertModel(101, 3, "东京冷");
 
 
 
 
    connect(ui->colOneLineEdit,SIGNAL(textEdited(QString)), this, SLOT(col1LineEditChanged(QString)));
    connect(ui->colTwoLineEdit,SIGNAL(textEdited(QString)), this, SLOT(col2LineEidtChanged(QString)));
 
    m_filterModel->setSourceModel(m_model);
    ui->tableView->setModel(m_filterModel);
}
 
Widget::~Widget()
{
    delete ui;
}
 
void Widget::col1LineEditChanged(const QString text)
{
    m_filterModel->setRxCol1(text);
    m_filterModel->setSourceModel(m_model);
}
 
void Widget::col2LineEidtChanged(const QString text)
{
    m_filterModel->setRxCol2(text);
    m_filterModel->setSourceModel(m_model);
}
 
void Widget::intsertModel(const int row, const int col, const QString data)
{
    QStandardItem *newItem = new QStandardItem(data);
    newItem->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
    m_model->setItem(row, col, newItem);
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

隨意的風

如果你觉得有帮助,期待你的打赏

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

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

打赏作者

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

抵扣说明:

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

余额充值