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

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

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



insertRowsメソッド

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

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

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

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

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

parent_splited = {}
for index in indexes:
    if not index.parent() in parent_splited:
        parent_splited[index.parent()] = []
    parent_splited[index.parent()].append(index)

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

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

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

def insert_item(self):
    indexes = self.unique_row_indexes(self.ui.treeView.selectedIndexes())

    if len(indexes) == 0:
        self.model.insertRows(self.model.root_item.childCount(), 1, QtCore.QModelIndex())
        return
    
    parent_splited = {}
    for index in indexes:
        if not index.parent() in parent_splited:
            parent_splited[index.parent()] = []
        parent_splited[index.parent()].append(index)
    
    for key in parent_splited:
        _sorted = sorted( parent_splited[key], key=lambda t:t.row() )[::-1]
        for index in _sorted:
            self.model.insertRows(index.row() + 1, 1, index.parent())



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

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

def add_childitem(self):
    indexes = self.unique_row_indexes(self.ui.treeView.selectedIndexes())
    if len(indexes) == 0:
        self.model.insertRows(self.model.root_item.childCount(), 1, QtCore.QModelIndex())
        return
    
    for index in indexes:
        item = index.internalPointer()
        self.model.insertRows(item.childCount() + 1, 1, index)

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

コメント

タイトルとURLをコピーしました