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