QSortFilterProxyModelの選択取得問題




Viewの選択取得はよくやると思うのですが、QSortFilterProxyModelを使うといつもと同じようには選択の取得ができません。。。ちょっと癖があるので、メモも兼ねてまとめたいと思います!

QSortFilterProxyModelを使って、簡単にフィルタリングするサンプルはコチラの記事をご覧ください!!∠( ゚д゚)/

前回の記事のコードをベースに、やっていきたいと思います。

QItemSelectionModelの追加

まずは、QSortFilterProxyModelと関連付けたQItemSelectionModelを作成し、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)
		self.__filter.textChanged.connect(self.filterChanged)
		layout.addWidget(self.__filter)
		
		self.__proxyModel = QtGui.QSortFilterProxyModel(self)
		
		self.__selectionModel = QtGui.QItemSelectionModel(self.__proxyModel)
		
		self.__listView = QtGui.QListView(self)
		self.__listView.setModel(self.__proxyModel)
		self.__listView.setSelectionModel(self.__selectionModel)
		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)

確認用のWidget追加

次は、選択状況を確認するためのボタンとラベルを追加します。こちらは、お好みで変更していただければっと思います(*´ω`*)

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.__selectionModel = QtGui.QItemSelectionModel(self.__proxyModel)
		
		self.__listView = QtGui.QListView(self)
		self.__listView.setModel(self.__proxyModel)
		self.__listView.setSelectionModel(self.__selectionModel)
		layout.addWidget(self.__listView)
		
		subLayout = QtGui.QHBoxLayout(self)
		layout.addLayout(subLayout)
		
		self.__info = QtGui.QLabel(self)
		subLayout.addWidget(self.__info)
		
		button = QtGui.QPushButton('Get', self)
		button.clicked.connect(self.updateInfo)
		subLayout.addWidget(button)
	
	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)

この段階で実行すると、このような見た目になります。

選択取得の実装

ボタンをクリックしたら「updateInfo」を実行し、用意したラベルにリストで選択したアイテムの文字を表示します。

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.__selectionModel = QtGui.QItemSelectionModel(self.__proxyModel)
		
		self.__listView = QtGui.QListView(self)
		self.__listView.setModel(self.__proxyModel)
		self.__listView.setSelectionModel(self.__selectionModel)
		layout.addWidget(self.__listView)
		
		subLayout = QtGui.QHBoxLayout(self)
		layout.addLayout(subLayout)
		
		self.__info = QtGui.QLabel(self)
		subLayout.addWidget(self.__info)
		
		button = QtGui.QPushButton('Get', self)
		button.clicked.connect(self.updateInfo)
		subLayout.addWidget(button)
	
	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)
	
	def updateInfo(self):
		indexes = self.__selectionModel.selectedIndexes()
		if not indexes:
			self.__info.setText('None')
			return
		
		model = self.__proxyModel.sourceModel()
		result = ''
		for index in indexes:
			item = model.itemFromIndex(index)
			result += item.text()
		
		self.__info.setText(result)

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()

まず、QItemSelectionModelのselectedIndexesを使って、選択されたアイテムのIndexesを取得します。

もし、選択したアイテムがある場合、QSortFilterProxyModelから、元のModelを取得し、itemFromIndexを使ってIndexからアイテムを取得する流れです。

しかし!

上記のコードを実行すると、Indexが噛み合わずアイテムを取得することができません、、、

そこで使うのが「mapToSource」

QSortFilterProxyModelには、「mapToSource」と言うものがあり、QSortFilterProxyModel用のIndexを、Model用のIndex(言い方あってるかな、、、)に変換してくれるものがあります。

「model.itemFromIndex(index)」の部分を「model.itemFromIndex(self.__proxyModel.mapToSource(index))」に修正し、Model用のIndexにしてから取得します。

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.__selectionModel = QtGui.QItemSelectionModel(self.__proxyModel)
		
		self.__listView = QtGui.QListView(self)
		self.__listView.setModel(self.__proxyModel)
		self.__listView.setSelectionModel(self.__selectionModel)
		layout.addWidget(self.__listView)
		
		subLayout = QtGui.QHBoxLayout(self)
		layout.addLayout(subLayout)
		
		self.__info = QtGui.QLabel(self)
		subLayout.addWidget(self.__info)
		
		button = QtGui.QPushButton('Get', self)
		button.clicked.connect(self.updateInfo)
		subLayout.addWidget(button)
	
	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)
	
	def updateInfo(self):
		indexes = self.__selectionModel.selectedIndexes()
		if not indexes:
			self.__info.setText('None')
			return
		
		model = self.__proxyModel.sourceModel()
		result = ''
		for index in indexes:
			item = model.itemFromIndex(self.__proxyModel.mapToSource(index))
			result += item.text()
		
		self.__info.setText(result)

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()

すると、フィルターを適用したViewの選択が取れるようになります!