QListViewにフィルターを追加する!!

今回は、ViewにMayaのOutlinerみたいなフィルターをつけてみたいと思います!

Viewに表示するものが多いと、目的のものを見つけるのが大変なので、こういうフィルターが設定できると使い勝手がぐっと良くなりますよね!(*´ω`*)b

見た目の作成

まずは、フィルター用の文字を入れるWidgetと、肝心のリストを表示するViewを用意したいと思います。

今回は、「QWidget」に「QLineEdit」と「QListView」を突っ込んで作ります。

from PySide import QtCore
from PySide import QtGui
from maya import cmds

class ItemView(QtGui.QWidget):
	def __init__(self, *args, **kwargs):
		super(ItemView, self).__init__(*args, **kwargs)
		
		layout = QtGui.QVBoxLayout(self)
		
		self.__filter = QtGui.QLineEdit(self)
		layout.addWidget(self.__filter)
		
		self.__listView = QtGui.QListView(self)
		layout.addWidget(self.__listView)

この段階では、こんな感じの見た目になります。

QWidgetをベースにしているので、Mayaで実行する場合は、「setWindowFlags」で「QtCore.Qt.Window」の指定が必要です。

フィルターの実装1

まず、表示項目をフィルタリングしたい場合は、「QSortFilterProxyModel」をViewに設定します。

from PySide import QtCore
from PySide import QtGui
from maya import cmds

class ItemView(QtGui.QWidget):
	def __init__(self, *args, **kwargs):
		super(ItemView, self).__init__(*args, **kwargs)
		
		layout = QtGui.QVBoxLayout(self)
		
		self.__filter = QtGui.QLineEdit(self)
		layout.addWidget(self.__filter)
		
		self.__proxyModel = QtGui.QSortFilterProxyModel(self)
		
		self.__listView = QtGui.QListView(self)
		self.__listView.setModel(self.__proxyModel)
		layout.addWidget(self.__listView)

「QSortFilterProxyModel」は、QStandardItemModelなどのようにデータを追加するものではなく、設定されたModelをフィルタリングするクラスです。なので、Viewに表示するためのModelを、「QSortFilterProxyModel」に渡してあげる必要があります。

図にすると、こんな感じでしょうか。

QStandardItemModel→QSortFilterProxyModel→QListView

しかし!このままではViewにモデルを設定するルートがないので、モデルを外部から設定できるように「setSourceModel」を追加します。

from PySide import QtCore
from PySide import QtGui
from maya import cmds

class ItemView(QtGui.QWidget):
	def __init__(self, *args, **kwargs):
		super(ItemView, self).__init__(*args, **kwargs)
		
		layout = QtGui.QVBoxLayout(self)
		
		self.__filter = QtGui.QLineEdit(self)
		layout.addWidget(self.__filter)
		
		self.__proxyModel = QtGui.QSortFilterProxyModel(self)
		
		self.__listView = QtGui.QListView(self)
		self.__listView.setModel(self.__proxyModel)
		layout.addWidget(self.__listView)
		
	def setSourceModel(self, model):
		self.__proxyModel.setSourceModel(model)

フィルターの実装2

次は、QLineEditを編集したら、フィルタリングを実行する処理を追加します。

まず、QLineEditの文字が変更された時にSignal「textChanged」とコネクションします。

from PySide import QtCore
from PySide import QtGui
from maya import cmds

class ItemView(QtGui.QWidget):
	def __init__(self, *args, **kwargs):
		super(ItemView, self).__init__(*args, **kwargs)
		
		layout = QtGui.QVBoxLayout(self)
		
		self.__filter = QtGui.QLineEdit(self)
		self.__filter.textChanged.connect(self.filterChanged)
		layout.addWidget(self.__filter)
		
		self.__proxyModel = QtGui.QSortFilterProxyModel(self)
		
		self.__listView = QtGui.QListView(self)
		self.__listView.setModel(self.__proxyModel)
		layout.addWidget(self.__listView)
		
	def setSourceModel(self, model):
		self.__proxyModel.setSourceModel(model)

次に、Slot側の実装です。

from PySide import QtCore
from PySide import QtGui
from maya import cmds

class ItemView(QtGui.QWidget):
	def __init__(self, *args, **kwargs):
		super(ItemView, self).__init__(*args, **kwargs)
		
		layout = QtGui.QVBoxLayout(self)
		
		self.__filter = QtGui.QLineEdit(self)
		self.__filter.textChanged.connect(self.filterChanged)
		layout.addWidget(self.__filter)
		
		self.__proxyModel = QtGui.QSortFilterProxyModel(self)
		
		self.__listView = QtGui.QListView(self)
		self.__listView.setModel(self.__proxyModel)
		layout.addWidget(self.__listView)
		
	def setSourceModel(self, model):
		self.__proxyModel.setSourceModel(model)
		
	def filterChanged(self, filter):
		regExp = QtCore.QRegExp(
					filter,
					QtCore.Qt.CaseSensitive,
					QtCore.QRegExp.Wildcard
					)
		self.__proxyModel.setFilterRegExp(regExp)

フィルタリング用の文字列を元に、QRegExpのインスタンスを作成します。

第1引数は、元となる文字です。ここでは、QLineEditに入力した文字列が指定されます。

第2引数は、大文字・小文字を無視するかどうかです。「CaseSensitive」は区別する、「CaseInsensitive」は区別しないとなります。

第3引数は、第1引数で指定した文字のあ使い方を設定します。ここでは、ワイルドカードとして認識するように設定しています。その他の種類については、コチラをご覧ください。

最後に、Viewに設定した「QSortFilterProxyModel」の「setFilterRegExp」を使って、フィルタリングの情報(QRegExp)を設定します。

テスト

テスト用に、シーン内にあるすべてのノード名をQStandardItemModelにつっこんで、簡易的なOutlinerを作ってみます。

from PySide import QtCore
from PySide import QtGui
from maya import cmds

itemView = ItemView()
itemView.setWindowFlags(QtCore.Qt.Window)

model = QtGui.QStandardItemModel(itemView)
for node in cmds.ls():
	model.appendRow(QtGui.QStandardItem(node))

itemView.setSourceModel(model)
itemView.show()

すると、このような感じで動作します!(`・ω・´)ゞ

大文字・小文字の区別、文字パターンの種類とかを、別途オプションで用意すると、更にいい感じになりそうですねっ!∠( ゚д゚)/