天天看點

Qt模型視圖架構:QSortFilterProxyModel

一、描述

QSortFilterProxyModel 可用于對項目進行排序、過濾。該模型而無需對底層資料進行任何轉換,也無需複制記憶體中的資料。

要對自定義模型提供的項目進行排序和過濾。需要建立一個 QSortFilterProxyModel,以 MyItemModel 作為參數調用 setSourceModel(),并在視圖上安裝 QSortFilterProxyModel:

QTreeView *treeView = new QTreeView;
        MyItemModel *sourceModel = new MyItemModel(this);
        QSortFilterProxyModel *proxyModel = new QSortFilterProxyModel(this);

        proxyModel->setSourceModel(sourceModel);
        treeView->setModel(proxyModel);
           

原始資料顯示在視圖中。通過 QSortFilterProxyModel 所做的任何更改都會應用于原始模型。

QSortFilterProxyModel 充當原始模型的包裝器。源 QModelIndexes 與排序/過濾的模型索引互相轉換請使用 mapToSource()、mapFromSource()、mapSelectionToSource() 、mapSelectionFromSource()。

預設情況下,隻要原始模型發生變化,模型就會動态重新排序和重新過濾資料。 可以通過設定 dynamicSortFilter 屬性來更改此行為。

二、屬性成員

1、autoAcceptChildRows : bool

如果為 true将不會過濾掉接受行的子項(即使它們本身被過濾掉的時候)。預設值為false。

2、dynamicSortFilter : bool

是否在源模型的内容發生變化時動态排序和過濾。

請注意,當 dynamicSortFilter 為 true 時,不應通過代理模型更新源模型。 例如,如果在 QComboBox 上設定代理模型,則使用更新模型的函數(例如 addItem())将無法按預期工作。 另一種方法是将 dynamicSortFilter 設定為 false 并在将項目添加到 QComboBox 後調用 sort()。

3、filterCaseSensitivity : Qt::CaseSensitivity

用于過濾源模型内容的 QRegularExpression 模式是否區分大小寫。預設區分大小寫。

4、filterKeyColumn : int

用于過濾源模型内容的鍵從中讀取的列。預設值為 0。如果值為 -1,将從所有列中讀取鍵。

5、filterRegularExpression : QRegularExpression

用于過濾源模型内容的 QRegularExpression

通過 QRegularExpression 重載設定此屬性會覆寫目前的 filterCaseSensitivity。預設情況下, QRegularExpression 是一個比對所有内容的空字元串。

如果未設定 QRegularExpression 或空字元串,則将接受源模型中的所有内容。

6、filterRole : int

在過濾項目時用于查詢源模型資料的項目角色。預設值為 Qt::DisplayRole。

7、isSortLocaleAware : bool

在對字元串排序時,是否考慮本地因素。預設情況下不考慮(QLocale)。

8、recursiveFilteringEnabled : bool

過濾器是否遞歸應用于子項,并且對于任何比對的子項,其父項也将可見。預設值為false。

9、sortCaseSensitivity : Qt::CaseSensitivity

用于在排序時比較字元串的區分大小寫設定。預設情況下,排序區分大小寫。

10、sortRole : int

對項目進行排序時查詢源模型的資料角色。預設值為 Qt::DisplayRole。

三、重要成員函數

1、void invalidate()

使目前的排序和過濾無效。

2、void setFilterFixedString(const QString &pattern)

設定用于過濾源模型内容的字元串。

3、void setFilterWildcard(const QString &pattern)

設定用于過濾源模型内容的通配符表達式。

4、void setFilterWildcard(const QString &pattern)

設定用于過濾源模型内容的通配符表達式。

5、bool filterAcceptsColumn(int source_column, const QModelIndex &source_parent)

如果source_column 和 source_parent 訓示的列中的項目應包含在模型中,則傳回 true。

預設實作始終傳回 true。

6、bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) 

如果由給定的 source_row 和 source_parent 訓示的行中的項目應包含在模型中,則傳回 true。

如果相關項所持有的值與過濾器字元串、通配符字元串或正規表達式比對,則預設實作将傳回 true。

注意:預設情況下,Qt::DisplayRole 用于确定是否應接受該行。 這可以通過設定 filterRole 屬性來更改。

7、void invalidateColumnsFilter()

使列的目前過濾無效。

如果正在進行自定義過濾(通過 filterAcceptsColumn()),并且過濾器參數已更改,則應調用此函數。 這與 invalidateFilter() 的不同之處在于它不會調用 filterAcceptsRow(),而隻會調用 filterAcceptsColumn()。 如果要隐藏或顯示行不變的列,可以使用它代替 invalidateFilter()。

8、void invalidateFilter()

使目前過濾無效。

如果正在進行自定義過濾(例如 filterAcceptsRow()),并且過濾器參數已更改,則應調用此函數。

9、void invalidateRowsFilter()

使行的目前過濾無效。

如果正在進行自定義過濾(通過 filterAcceptsRow()),并且過濾器參數已更改,則應調用此函數。 這與 invalidateFilter() 的不同之處在于它不會調用 filterAcceptsColumn(),而隻會調用 filterAcceptsRow()。 如果要隐藏或顯示列不變的行,可以使用它代替 invalidateFilter()。

10、bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) 

如果索引 source_left 引用的項的值小于索引 source_right 引用的項的值,則傳回 true,否則傳回 false。

此函數在排序時用作 < 運算符,并處理以下 QVariant 類型:

  • QMetaType::Int
  • QMetaType::UInt
  • QMetaType::LongLong
  • QMetaType::ULongLong
  • QMetaType::Float
  • QMetaType::Double
  • QMetaType::QChar
  • QMetaType::QDate
  • QMetaType::QTime
  • QMetaType::QDateTime
  • QMetaType::QString

任何其他類型都将使用 QVariant::toString() 轉換為 QString。

預設情況下,與 QModelIndexes 關聯的 Qt::DisplayRole 用于比較。 這可以通過設定 sortRole 屬性來更改。

注意:傳入的索引是源模型的索引。

四、使用示例

1、官方demo:Basic Sort/Filter Model Example

Window::Window()
{
    proxyModel = new QSortFilterProxyModel;

    sourceView = new QTreeView;
    sourceView->setRootIsDecorated(false);
    sourceView->setAlternatingRowColors(true);

    proxyView = new QTreeView;
    proxyView->setRootIsDecorated(false);
    proxyView->setAlternatingRowColors(true);
    proxyView->setModel(proxyModel);
    proxyView->setSortingEnabled(true);

    auto sortCaseSensitivityCheckBox = new QCheckBox(tr("區分大小寫的排序"));
    filterCaseSensitivityCheckBox = new QCheckBox(tr("區分大小寫的過濾器"));

    filterPatternLineEdit = new QLineEdit;
    auto filterPatternLabel = new QLabel(tr("過濾字元:"));
    filterPatternLabel->setBuddy(filterPatternLineEdit);

    filterSyntaxComboBox = new QComboBox;
    filterSyntaxComboBox->addItem(tr("正規表達式"), QRegExp::RegExp);
    filterSyntaxComboBox->addItem(tr("通配符"), QRegExp::Wildcard);
    filterSyntaxComboBox->addItem(tr("固定字元串"), QRegExp::FixedString);
    auto filterSyntaxLabel = new QLabel(tr("過濾器文法:"));
    filterSyntaxLabel->setBuddy(filterSyntaxComboBox);

    filterColumnComboBox = new QComboBox;
    filterColumnComboBox->addItem(tr("Subject"));
    filterColumnComboBox->addItem(tr("Sender"));
    filterColumnComboBox->addItem(tr("Date"));
    auto filterColumnLabel = new QLabel(tr("過濾行:"));
    filterColumnLabel->setBuddy(filterColumnComboBox);

    connect(filterPatternLineEdit, &QLineEdit::textChanged,this, &Window::filterRegExpChanged);
    connect(filterSyntaxComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),this, &Window::filterRegExpChanged);
    connect(filterColumnComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),this, &Window::filterColumnChanged);
    connect(filterCaseSensitivityCheckBox, &QAbstractButton::toggled,this, &Window::filterRegExpChanged);
    connect(sortCaseSensitivityCheckBox, &QAbstractButton::toggled,this, &Window::sortChanged);

    auto sourceGroupBox = new QGroupBox(tr("原始模型"));
    auto proxyGroupBox = new QGroupBox(tr("排序/過濾模型"));

    QHBoxLayout *sourceLayout = new QHBoxLayout;
    sourceLayout->addWidget(sourceView);
    sourceGroupBox->setLayout(sourceLayout);

    QGridLayout *proxyLayout = new QGridLayout;
    proxyLayout->addWidget(proxyView, 0, 0, 1, 3);
    proxyLayout->addWidget(filterPatternLabel, 1, 0);
    proxyLayout->addWidget(filterPatternLineEdit, 1, 1, 1, 2);
    proxyLayout->addWidget(filterSyntaxLabel, 2, 0);
    proxyLayout->addWidget(filterSyntaxComboBox, 2, 1, 1, 2);
    proxyLayout->addWidget(filterColumnLabel, 3, 0);
    proxyLayout->addWidget(filterColumnComboBox, 3, 1, 1, 2);
    proxyLayout->addWidget(filterCaseSensitivityCheckBox, 4, 0, 1, 2);
    proxyLayout->addWidget(sortCaseSensitivityCheckBox, 4, 2);
    proxyGroupBox->setLayout(proxyLayout);

    QVBoxLayout *mainLayout = new QVBoxLayout;

    mainLayout->addWidget(sourceGroupBox);
    mainLayout->addWidget(proxyGroupBox);

    setLayout(mainLayout);
    resize(500, 450);

    proxyView->sortByColumn(1, Qt::AscendingOrder);
    filterColumnComboBox->setCurrentIndex(1);

    filterPatternLineEdit->setText("Andy|Grace");
    filterCaseSensitivityCheckBox->setChecked(true);
    sortCaseSensitivityCheckBox->setChecked(true);
}

void Window::setSourceModel(QAbstractItemModel *model)
{
    proxyModel->setSourceModel(model);
    sourceView->setModel(model);
}

void Window::filterRegExpChanged()
{
    QRegExp::PatternSyntax syntax = QRegExp::PatternSyntax(filterSyntaxComboBox->itemData(filterSyntaxComboBox->currentIndex()).toInt());
    Qt::CaseSensitivity caseSensitivity = filterCaseSensitivityCheckBox->isChecked() ? Qt::CaseSensitive : Qt::CaseInsensitive;

    QRegExp regExp(filterPatternLineEdit->text(), caseSensitivity, syntax);
    proxyModel->setFilterRegExp(regExp);
}

void Window::filterColumnChanged()
{
    proxyModel->setFilterKeyColumn(filterColumnComboBox->currentIndex());
}

void Window::sortChanged()
{
    proxyModel->setSortCaseSensitivity(sortCaseSensitivityCheckBox->isChecked() ? Qt::CaseSensitive : Qt::CaseInsensitive);
}
           
Qt模型視圖架構:QSortFilterProxyModel

2、官方demo:Custom Sort/Filter Model Example

#ifndef MYSORTFILTERPROXYMODEL_H
#define MYSORTFILTERPROXYMODEL_H

#include <QDate>
#include <QSortFilterProxyModel>

class MySortFilterProxyModel : public QSortFilterProxyModel
{
    Q_OBJECT

public:
    MySortFilterProxyModel(QObject *parent = nullptr);
    QDate filterMinimumDate() const
    {
        return minDate;
    }
    QDate filterMaximumDate() const
    {
        return maxDate;
    }
    void setFilterMinimumDate(const QDate &date);
    void setFilterMaximumDate(const QDate &date);

protected:
    bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
    bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;

private:
    bool dateInRange(const QDate &date) const;
    QDate minDate;
    QDate maxDate;
};

#endif // MYSORTFILTERPROXYMODEL_H
           
#include <QtWidgets>
#include "mysortfilterproxymodel.h"

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

void MySortFilterProxyModel::setFilterMinimumDate(const QDate &date)
{
    minDate = date;
    invalidateFilter();
}

void MySortFilterProxyModel::setFilterMaximumDate(const QDate &date)
{
    maxDate = date;
    invalidateFilter();
}

bool MySortFilterProxyModel::filterAcceptsRow(int sourceRow,const QModelIndex &sourceParent) const
{
    QModelIndex index0 = sourceModel()->index(sourceRow, 0, sourceParent);
    QModelIndex index1 = sourceModel()->index(sourceRow, 1, sourceParent);
    QModelIndex index2 = sourceModel()->index(sourceRow, 2, sourceParent);

    return (sourceModel()->data(index0).toString().contains(filterRegExp()) ||
            sourceModel()->data(index1).toString().contains(filterRegExp()))
            && dateInRange(sourceModel()->data(index2).toDate());
}

bool MySortFilterProxyModel::lessThan(const QModelIndex &left,
                                      const QModelIndex &right) const
{
    QVariant leftData = sourceModel()->data(left);
    QVariant rightData = sourceModel()->data(right);

    if (leftData.type() == QVariant::DateTime)
    {
        return leftData.toDateTime() < rightData.toDateTime();
    }
    else
    {
        static const QRegularExpression emailPattern("[\\w\\.]*@[\\w\\.]*");

        QString leftString = leftData.toString();
        if (left.column() == 1)
        {
            const QRegularExpressionMatch match = emailPattern.match(leftString);
            if (match.hasMatch())
                leftString = match.captured(0);
        }
        QString rightString = rightData.toString();
        if (right.column() == 1)
        {
            const QRegularExpressionMatch match = emailPattern.match(rightString);
            if (match.hasMatch())
                rightString = match.captured(0);
        }

        return QString::localeAwareCompare(leftString, rightString) < 0;
    }
}

bool MySortFilterProxyModel::dateInRange(const QDate &date) const
{
    return (!minDate.isValid() || date > minDate) && (!maxDate.isValid() || date < maxDate);
}
           

使用:

proxyModel->setFilterMinimumDate(fromDateEdit->date());
    proxyModel->setFilterMaximumDate(toDateEdit->date());
           

此模型設定一個日期區間,比對在此區間内的資料。

Qt模型視圖架構:QSortFilterProxyModel