PySideでツリー型のモデルを作成した際に、すべてのアイテムを順次アクセスしたい場合がある。
QTreeWidgetを使用ている場合は、QTreeWidgetItemIteratorで順次アクセスする事ができる。(C++用のライブラリなのでpythonだと若干使い勝手が悪いが)
QStandardItemModelや独自のモデルでもイテレータを使いたいので作成した。
コードはpython2だが、xrangeを使っていないのでpython3でもそのまま動く。
コード
# ! usr/bin/python
# -*- coding: utf-8 -*-
from __future__ import unicode_literals, print_function, absolute_import
from PySide.QtCore import *
from PySide.QtGui import *
def model_iter(model, parent_index=QModelIndex(), col_iter=True):
""" モデルのイテレータ
:rtype: generator(QModelIndex)
:type col_iter: bool
:type parent_index: QModelIndex
:type model: QAbstractItemModel
"""
index = model.index(0, 0, parent_index)
while True:
if col_iter:
for col in range(0, model.columnCount(parent_index)):
yield index.sibling(index.row(), col)
else:
yield index
if model.rowCount(index) > 0:
for _ in model_iter(model, index, col_iter):
yield _
index = index.sibling(index.row() + 1, index.column())
if not index.isValid():
break
使い方は簡単で、モデルを渡すだけでイテレータを返してくれる。
tree_model = QStandardItemModel()
item = QStandardItem("A")
item.appendRow(QStandardItem("A-1"))
item.appendRow(QStandardItem("A-2"))
tree_model.appendRow(item)
item = QStandardItem("B")
item.appendRow(QStandardItem("B-1"))
item.appendRow(QStandardItem("B-2"))
tree_model.appendRow(item)
item = QStandardItem("C")
tree_model.appendRow(item)
print([_.data() for _ in model_iter(tree_model)])
>>> [u'A', u'A-1', u'A-2', u'B', u'B-1', u'B-2', u'C']
ツリー型だけでなく、テーブル型のモデルにも対応している。
table_model = QStandardItemModel(3, 3)
for r in range(3):
for c in range(3):
table_model.setItem(r, c, QStandardItem("%d-%d" % (r, c)))
print([_.data() for _ in model_iter(table_model)])
>>> [u'0-0', u'0-1', u'0-2', u'1-0', u'1-1', u'1-2', u'2-0', u'2-1', u'2-2']
逆順イテレータ
ついでに逆順イテレータも作成した。
def model_iter_r(model, parent_index=QModelIndex(), col_iter=True):
""" モデルのイテレータ(逆順)
:rtype: generator(QModelIndex)
:type col_iter: bool
:type parent_index: QModelIndex
:type model: QAbstractItemModel
"""
index = model.index(model.rowCount(parent_index) - 1, 0, parent_index)
while True:
if model.rowCount(index) > 0:
for _ in model_iter_r(model, index, col_iter):
yield _
if col_iter:
for col in range(model.columnCount(parent_index) - 1, -1, -1):
yield index.sibling(index.row(), col)
else:
yield index
index = index.sibling(index.row() - 1, index.column())
if not index.isValid():
break
使い方は、正順のものとまったく同じ。
print([_.data() for _ in model_iter_r(tree_model)])
>>> [u'C', u'B-2', u'B-1', u'B', u'A-2', u'A-1', u'A']
print([_.data() for _ in model_iter_r(table_model)])
>>> [u'2-2', u'2-1', u'2-0', u'1-2', u'1-1', u'1-0', u'0-2', u'0-1', u'0-0']
ちゃんとツリー構造も逆順に辿っている。
モデルの順次アクセスはよく使う機能だが、再帰を使うためいちいち実装するのは面倒である。
この関数をコピペしておけば何かと便利だ。
次回は、この関数を使って検索オブジェクトを作成する。