PyQt5 InsertRowsを使ってQTreeViewに行を挿入する

Share on:

QTreeViewのInsertRowsメソッドを使って行の挿入を試してみました。

ツリーのデータを編集しようと思ったときに、行を挿入したり、アイテムに子を追加する機能が必要になります。その機能はQAbstractItemModelのInsertRowsをオーバーライドしてメソッドを作成することで実装できます。

insertRowsメソッド

insertRowsでは下記の通りに処理をするだけです。

  • 親のインデックスを取得
  • beginInsertRowsを発行
  • 親アイテムにアイテムを追加
  • endInsertRowsを発行
1def insertRows(self, row, count, parent=QtCore.QModelIndex()):
2    if parent == QtCore.QModelIndex():
3        parent_item = self.root_item
4    else:
5        parent_item = parent.internalPointer()
6    items = [ Item(parent_item, {}) for i in range(count) ]
7    self.beginInsertRows(parent, row, row + count - 1)
8    parent_item.insertChildren(items, row)
9    self.endInsertRows()

選択したインデックスの次の行にアイテムを挿入

選択したインデックスが無い場合は、ルートの最後にアイテムを追加して、選択したインデックスが有る場合は、そのインデックスの次の行にアイテムを挿入するようにしました。選択可能なアイテムが1つだけなら単純に次の行に挿入するだけですが、アイテムを複数選択可能にしている場合は少し面倒でした。

まずは下記のように、選択したインデックスのリストを親ごとのリストに分けて、そのリストを親のインデックスをキーとした辞書に入れます。

1parent_splited = {}
2for index in indexes:
3    if not index.parent() in parent_splited:
4        parent_splited[index.parent()] = []
5    parent_splited[index.parent()].append(index)

次に親ごとに分けたリストを行番号でソートして、そのリストの最後から行を挿入していきます。リストの最初から追加していった場合、追加するたびに行が増えていくため、挿入位置がずれていってしまいます。

1for key in parent_splited:
2    _sorted = sorted( parent_splited[key], key=lambda t:t.row() )[::-1]
3    for index in _sorted:
4        self.model.insertRows(index.row() + 1, 1, index.parent())

上記のコードをまとめて、行の挿入メソッドは下記の通りにしました。

 1def insert_item(self):
 2    indexes = self.unique_row_indexes(self.ui.treeView.selectedIndexes())
 3
 4    if len(indexes) == 0:
 5        self.model.insertRows(self.model.root_item.childCount(), 1, QtCore.QModelIndex())
 6        return
 7    
 8    parent_splited = {}
 9    for index in indexes:
10        if not index.parent() in parent_splited:
11            parent_splited[index.parent()] = []
12        parent_splited[index.parent()].append(index)
13    
14    for key in parent_splited:
15        _sorted = sorted( parent_splited[key], key=lambda t:t.row() )[::-1]
16        for index in _sorted:
17            self.model.insertRows(index.row() + 1, 1, index.parent())

選択したアイテムに子を追加する

子の追加は、行の挿入よりも単純です。選択したアイテムの最後に子を追加するだけです。また、こちらも選択したインデックスが無い場合は、ルートの最後に子を追加する処理にしています。子の追加は順番にinsertRowsで追加していくだけで問題ありませんでした。

1def add_childitem(self):
2    indexes = self.unique_row_indexes(self.ui.treeView.selectedIndexes())
3    if len(indexes) == 0:
4        self.model.insertRows(self.model.root_item.childCount(), 1, QtCore.QModelIndex())
5        return
6    
7    for index in indexes:
8        item = index.internalPointer()
9        self.model.insertRows(item.childCount() + 1, 1, index)

これで行の挿入や子の追加が出来るようになりました。次は削除を実装してみます。

関連記事