コンテンツにスキップ

TreeViewの中身を正規表現で検索する

PySideの各種Viewは、ProxyModelを使用することでリスト内の絞り込み検索を
追加することができます。

ただ、TreeViewの場合などとくに顕著ですが
文字列で「絞り込んでほしくないItem」、例としてGroup用の階層などがある場合は
あえて検索文字で絞り込みたくない場合などもあります。

ので、その辺踏まえてTreeViewで検索をするための構造を作ってみます。

コード

まずはコード。

解説

まずはQAbstractItemModelを使用してTreeViewとModelを作成します。

実行するとこんな感じのUIが表示されます。

まず、このTreeViewの場合
特になにもしない場合 GroupA GroupBも検索で引っかかってしまい
「テスト」などで検索をしてもなにも表示されなくなってしまいます。
(親が検索にひっかからないと子は表示されないため)

ので、より細かく検索条件を分岐させる必要があります。

その分岐をするのが、 QSortFilterProxyModelのVirtual関数、「filterAcceptRow」 です。

class TestProxyFilter(QtCore.QSortFilterProxyModel):

    def __init__(self, parent=None):
        super(TestProxyFilter, self).__init__(parent)

    def filterAcceptsRow(self, row, parent):
        item = parent.internalPointer()
        # 親ItemがGroupItemは、ProxyModelの検索対象にする
        if isinstance(item, GroupItem):
            return super(TestProxyFilter, self).filterAcceptsRow(row, parent)
        # それ以外は検索で消えてほしくないのでTrueにする
        return True

filterAcceptsRow関数は、表示するかどうかを各Itemごとに判定して
表示するかどうかを bool で返します。

parentとrowを引数として受け取ります。
これは、表示するかどうか判定したいItemからみた「親」アイテムと、その親から見た「何番目のItemか」
という情報になります。

このparentはModelIndexになるので internalPointer を使用して実体のItemを取得してしています。
internalPointerについては以前の QtCore.QAbstractItemModelを使用したカスタムモデルの作成
参考にしてください。

で。
今回の場合は、「親がGroupItem」オブジェクトだった場合は検索対象のItem(TreeItem)なので
正規表現によるチェックをするために、オーバーライドする前の filterAcceptsRow を実行するようにします。
それ以外の場合、この場合でいうと GroupItemの場合は
かならず表示するようにしたいので True を返すようにします。

このようにすると、 GroupItemの場合は
ProxyModelでどのような条件がきたとしても「絶対表示する」ようになるので
今回のTreeViewのように特定のItemは判定外にする...といった事が可能になります。

余談ですが、filterAcceptsRow意外にもfilterAcceptsColumn関数もあるので
条件をRowではなくColumn単位で指定したい場合は
同じような形でfilterAcceptsColumnで実装すればOKです。

検索文字列を指定のものにしたい場合

今回のサンプルの場合は列が1つなので問題ないのですが
例えば列が複数あって、すべてを検索対象にしたい...というケースも発生します。

その場合は、ProxyModelの self.proxymodel.setFilterRole(PROXY_FILTER_ROLE) を使用することで
検索対象にする文字列を Model側で指定することが出来ます。

まずは、Proxy側で setFilterRole に対してRoleを指定します。
Roleは、 UserRole を使用しても良いし、すでに使っている場合は↓のように + 1 したものを
別途定義するとかでもOKです。

# Proxyで使用するRoleを定義しておく。
PROXY_FILTER_ROLE = QtCore.Qt.UserRole + 1
# どのRoleをProxyの対象にするのかをセットしておく
self.proxymodel.setFilterRole(PROXY_FILTER_ROLE)
    def data(self, index, role=QtCore.Qt.DisplayRole):

        if not index.isValid():
            return None

        item = index.internalPointer()

        if role == QtCore.Qt.DisplayRole:
            return item.data(index.column())
        # ProxyModelを使用した検索時の検索対象文字列を返す
        if role == PROXY_FILTER_ROLE:
            # とりあえず同じものを返す
            return item.data(index.column())
        return None

そして、Modelの data で、PROXY_FILTER_ROLEだった場合は、検索対象にしたい
文字列を返すようにしておくと、ProxyModelでの絞り込み対象の文字列を
カスタマイズすることができます。


最終更新日: 2020-03-19 15:16:19