\ 迷ったらまずTechAcademyの無料カウンセリング! /
PySide Model/View【第2弾】QTreeViewで作るフォルダ構造・階層リストのGUI表示

前回の記事では QTableView と QStandardItemModel を使って、表形式のデータをきれいに表示する方法を学びました。
しかし実際のアプリでは「フォルダ構造」や「カテゴリ分けされたリスト」のように、階層構造を持つデータを扱いたい場面がよくあります。
そこで今回は、そのような場面で威力を発揮する QTreeView を取り上げます。
QTreeView を使えば、エクスプローラーのような ツリー形式のGUI を簡単に作ることができます。さらに、QStandardItemModel と組み合わせることで、前回の表形式の知識をそのまま応用できるのも大きな魅力です。
この記事では、初心者でも理解しやすいように QTreeView の基本操作から実践的な使い方まで、順を追って解説していきます。
QTreeViewとは?
QTreeView
は 階層構造を表示できるビュー です。
内部的には QStandardItemModel を組み合わせて使うことが多く、ツリー形式のデータを簡単に作成できます。
基本コード例(階層データの表示)
from PySide6.QtWidgets import QApplication, QTreeView, QMainWindow
from PySide6.QtGui import QStandardItemModel, QStandardItem
import sys
class Window(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("QTreeView の基本")
self.resize(400, 300)
# モデル作成
model = QStandardItemModel()
model.setHorizontalHeaderLabels(["項目名", "説明"])
# 階層データを追加
parent1 = QStandardItem("フルーツ")
parent1.appendRow([QStandardItem("リンゴ"), QStandardItem("赤い果物")])
parent1.appendRow([QStandardItem("バナナ"), QStandardItem("黄色い果物")])
parent2 = QStandardItem("野菜")
parent2.appendRow([QStandardItem("ニンジン"), QStandardItem("オレンジ色の野菜")])
parent2.appendRow([QStandardItem("キャベツ"), QStandardItem("緑の野菜")])
model.appendRow([parent1, QStandardItem("果物のカテゴリ")])
model.appendRow([parent2, QStandardItem("野菜のカテゴリ")])
# TreeView 設定
tree = QTreeView()
tree.setModel(model)
tree.expandAll() # すべて展開して表示
self.setCentralWidget(tree)
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec())

主なプロパティとメソッド一覧(全解説)
No | メソッド / プロパティ | 説明 |
---|---|---|
1 | tree.setModel(model) / tree.model() | View と Model を関連付け/取得します。 |
2 | model.setHorizontalHeaderLabels(list) | ヘッダー(列名)をまとめて設定します。 |
3 | model.invisibleRootItem() | 見えない最上位ルートを取得します(階層の起点)。 |
4 | item.appendRow(items) / item.insertRow(i, items) / item.removeRow(i) | 親アイテムの子行を追加/挿入/削除します。 |
5 | model.indexFromItem(item) / model.itemFromIndex(index) | Item と QModelIndex を相互変換します。 |
6 | model.findItems(text, flags, column) | テキスト検索(部分一致/正規表現/再帰)を行います。 |
7 | tree.setEditTriggers(flags) | 編集開始のトリガーを制御します。 |
8 | tree.setSelectionMode(mode) / tree.setSelectionBehavior(behav) | 選択の方法(単一・複数/行・項目)を制御します。 |
9 | tree.expandAll() / tree.collapseAll() / tree.setExpanded(idx, bool) | 展開・折りたたみを制御します。 |
10 | tree.setHeaderHidden(bool) / tree.header() → setSectionResizeMode(...) / resizeColumnToContents(col) | ヘッダーの表示や列幅の自動調整など見た目を制御します。 |
各プロパティ・メソッド詳細解説
【1】モデルを設定する
概要:QTreeView
は自分でデータを持たないため、QStandardItemModel
などのモデルを関連付けて表示します。複数の View で同じ Model を共有できます。
メソッド:tree.setModel(model)
/ tree.model()
使い方(コード例):
model = QStandardItemModel(0, 2) # 0行・2列
model.setHorizontalHeaderLabels(["項目名", "説明"])
# TreeView 設定
tree = QTreeView()
tree.setModel(model)
tree.expandAll() # すべて展開して表示
self.setCentralWidget(tree)

【2】ヘッダー(列名)をまとめて設定する
概要:ツリーの列見出しを一括設定します。複数列構成のツリーでも可読性を保てます。
メソッド:model.setHorizontalHeaderLabels(list)
使い方(コード例):
model.setHorizontalHeaderLabels(["カテゴリ", "説明"])

【3】ルート(見えない最上位)を取得する
概要:invisibleRootItem()
は、画面には表示されない最上位の親です。ここから appendRow
で第一層(トップレベル)を作ります。
メソッド:model.invisibleRootItem()
使い方(コード例):
root = model.invisibleRootItem()
root.appendRow([QStandardItem("フォルダA"), QStandardItem("説明A")])

【4】子行の追加/挿入/削除
概要:任意の QStandardItem
の下に子行を追加・挿入し、階層データを構築します。
メソッド:item.appendRow(items)
/ item.insertRow(i, items)
/ item.removeRow(i)
使い方(コード例):
parent = QStandardItem("親カテゴリ")
desc = QStandardItem("このカテゴリの説明")
model.invisibleRootItem().appendRow([parent, desc])
child1 = QStandardItem("子1")
child1_desc = QStandardItem("子1の説明")
parent.appendRow([child1, child1_desc]) # 末尾に追加
parent.insertRow(0, [QStandardItem("先頭子"), QStandardItem("説明")]) # 先頭に挿入
parent.removeRow(1) # 2番目の子を削除

【5】Item ⇄ Index 相互変換
概要:選択やカーソルは QModelIndex
で渡されます。表示や編集の実体(QStandardItem
)と相互に行き来できると便利です。
メソッド:model.indexFromItem(item)
/ model.itemFromIndex(index)
使い方(コード例):
idx = model.indexFromItem(parent) # Item → Index
item = model.itemFromIndex(idx) # Index → Item
【6】文字列検索(部分一致・再帰)
概要:findItems
は列単位にテキスト検索します。Qt.MatchRecursive
を付けると子孫ノードまで再帰的に検索します。
メソッド:model.findItems(text, flags=Qt.MatchContains, column=0)
使い方(コード例):
from PySide6.QtCore import Qt
hits = model.findItems("親カテゴリ", flags=Qt.MatchContains | Qt.MatchRecursive, column=0)
for it in hits:
idx = model.indexFromItem(it)
tree.setExpanded(idx, True) # 見つけた項目を展開表示
print(idx)
# <PySide6.QtCore.QModelIndex(1,0,0x223145fb0b0,QStandardItemModel(0x223146ab8f0)) at 0x000002233B215440>

【7】編集開始トリガーを制御する
概要:どの操作でセル編集を始めるかをコントロールします(ダブルクリック/クリック/キー入力など)。
メソッド:tree.setEditTriggers(QAbstractItemView.EditTrigger)
使い方(コード例):
from PySide6.QtWidgets import QAbstractItemView as AIV
tree.setEditTriggers(AIV.NoEditTriggers) # すべて禁止
# 例:ダブルクリックで編集
tree.setEditTriggers(AIV.DoubleClicked)
例 | 説明 |
---|---|
tree.setEditTriggers(QAbstractItemView.DoubleClicked) | 編集できるタイミングを設定 |
tree.setEditTriggers(QAbstractItemView.NoEditTriggers) | 編集不可 |
tree.setEditTriggers(QAbstractItemView.CurrentChanged) | カレントセルが変わった時に編集可 |
tree.setEditTriggers(QAbstractItemView.DoubleClicked) | ダブルクリックで編集可 |
tree.setEditTriggers(QAbstractItemView.SelectedClicked) | 選択中のセルをクリックで編集可 |
treew.setEditTriggers(QAbstractItemView.EditKeyPressed) | EnterキーやF2キーで編集可 |
tree.setEditTriggers(QAbstractItemView.AnyKeyPressed) | 文字入力で即編集開始 |
tree.setEditTriggers(QAbstractItemView.AllEditTriggers) | すべてのトリガーで編集可 |
【8】選択モード/選択単位を制御する
概要:単一選択/複数選択、行単位/項目単位など選択の挙動を決めます。
メソッド:tree.setSelectionMode(mode)
/ tree.setSelectionBehavior(behavior)
使い方(コード例):
from PySide6.QtWidgets import QAbstractItemView as AIV
tree.setSelectionMode(AIV.ExtendedSelection) # Ctrl/Shift による複数選択
tree.setSelectionBehavior(AIV.SelectItems) # 項目(セル)単位で選択
例 | 説明 |
---|---|
tree.setSelectionMode(QAbstractItemView.SingleSelection) | 選択方法を設定 |
tree.setSelectionMode(QAbstractItemView.NoSelection) | 選択不可 |
tree.setSelectionMode(QAbstractItemView.SingleSelection) | 1セルのみ選択可 |
tree.setSelectionMode(QAbstractItemView.MultiSelection) | 複数セル選択可(Ctrlクリック) |
tree.setSelectionMode(QAbstractItemView.ExtendedSelection) | 複数セル選択可(ShiftやCtrl併用) |
tree.setSelectionBehavior(QAbstractItemView.SelectRows) | 選択対象(セル・行・列)を設定 |
tree.setSelectionBehavior(QAbstractItemView.SelectItems) | セル単位で選択 |
tree.setSelectionBehavior(QAbstractItemView.SelectRows) | 行単位で選択 |
tree.setSelectionBehavior(QAbstractItemView.SelectColumns) | 列単位で選択 |
【9】展開/折りたたみを制御する
概要:個別ノードやツリー全体の展開・折りたたみを制御します。初期状態の見せ方に有効です。
メソッド:tree.expandAll()
/ tree.collapseAll()
/ tree.setExpanded(index, bool)
使い方(コード例):
tree.expandAll()
# 特定ノードだけ展開
idx = model.indexFromItem(parent)
tree.setExpanded(idx, True)
【10】ヘッダー表示と列幅調整
概要:ヘッダーの表示/非表示、列幅の伸縮モードや内容に応じた自動調整を行います。
メソッド:
tree.setHeaderHidden(bool)
tree.header().setSectionResizeMode(section, mode)
tree.resizeColumnToContents(col)
使い方(コード例):
from PySide6.QtWidgets import QHeaderView
tree.setHeaderHidden(True)
tree.header().setSectionResizeMode(0, QHeaderView.ResizeToContents)
tree.header().setSectionResizeMode(1, QHeaderView.Stretch)

よくある質問(FAQ)
- QTreeViewとQTableViewの違いは?
-
QTableView
は表形式(2次元)専用ですが、QTreeView
は階層(ツリー)構造を持てる点が違います。 - QTableWidgetのように簡単に使えますか?
-
QTreeWidget
なら簡単に使えますが、拡張性を考えるならQTreeView + QStandardItemModel
が推奨です。 - ノードの展開イベントを拾えますか?
-
はい、
expanded
やcollapsed
シグナルを使えます。 - ユーザーが編集できるようにできますか?
-
item.setEditable(True)
を設定すれば可能です。 - ツリーをファイルシステム風に使えますか?
-
QFileSystemModel + QTreeView
を組み合わせれば可能です。
まとめ
今回の記事では QTreeView + QStandardItemModel を使って、階層構造を持つデータを表示・操作する方法を解説しました。
前回の QTableView が「表形式の一覧表示」に適していたのに対して、QTreeView は フォルダツリーや設定階層のような「親子関係を持つデータ」 を表現できるのが大きな特徴です。
実際の開発では、ファイルブラウザ風のツリー、オプション設定の階層、カテゴリごとの一覧など、応用範囲は非常に広いです。
特に QStandardItemModel は「手軽に使える柔軟なモデル」なので、Model/View の入門から実践まで幅広く活用できます。
次のステップとしては、独自モデル(QAbstractItemModel を継承)で柔軟なデータ管理を行う方法 や、チェックボックス・アイコン付きのツリーUI に進むとさらに応用力が高まるでしょう。
コメント