PyQt5でQTreeViewを使用してファイル名を取得する


# これは何

QTreeViewにファイルツリーを表示して、クリックしたファイル名を表示するまでのコードです。

いつも同じことを調べたりコードを確認したりしているので、部品ごとのメモを作成することにしました。


仕上がりは、以下のとおり。

![タイトルなし.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191337/5664fa1a-2f15-f7a3-7317-4ff7adb53b01.gif)


ファイルをクリックするとファイル名のメッセージが表示され、フォルダをクリックするとアラートとなります。

# 環境

- Darwin Darwin Kernel Version 20.3.0: Thu Jan 21 00:07:06 PST 2021; root:xnu-7195.81.3~1/RELEASE_X86_64 x86_64 i386
- Python 3.9.1 (default, Jan 15 2021, 10:22:19)
- PyQt5 5.15.2

# 手順

- Qt Designerでuiを作成
- pyuic5でpyファイルに変換
- ui用のモジュールの読み込み
- QTreeViewに関するコードを記述

では、ちゃちゃっと参ります。


## コンバート済uiファイルを読み込む場合

```fileTreeCallpy.py
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QFileSystemModel
from fileTreeUi import Ui_MainWindow

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.show()

        # ここから、ファイルツリー表示用のコード
        path = '.'
        self.model = QFileSystemModel()
        self.model.setRootPath(path)
        self.model.setNameFilters(['*.py','*.ui']) # この設定だけだと、非該当の拡張子はグレー表示
        self.model.setNameFilterDisables(False) # 上記フィルターに該当しないファイルは非表示
        view = self.ui.treeView
        view.setModel(self.model)
        view.setRootIndex(self.model.index(path))
        view.setColumnWidth(0,260)

        view.clicked.connect(self.getFileName)
    
    # クリックした際のメソッド    
    def getFileName(self, index):
        from PyQt5.QtWidgets import QMessageBox
        import os
        filepath = []
        indexItem = self.model.index(index.row(), 0, index.parent())
        if os.path.isfile(self.model.filePath(indexItem)):
            filepath.insert(0,self.model.filePath(indexItem))
            QMessageBox.information(None, "Notice!", filepath[0] + "\n\n is Selected", QMessageBox.Yes)
        else:
            QMessageBox.warning(None, "Notice!", "Select File!", QMessageBox.Yes)


app = QApplication(sys.argv)
w = MainWindow()
app.exec_()
```
## uiファイルを直接ロードする場合

uiファイルを直接ロードする場合は、

```python
from PyQt5 import uic
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        uic.loadUi('fileTreeUi.ui',self)
```

とし、イニットメソッドの中でtreeViewをインスタンス化します。

```python
view = self.treeView
```


## Qt Designerのコード

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191337/e6a90d35-5cad-8e5f-0ee8-5ec1c2891b6b.png)

コードは以下のとおり。

```fileTreeUi.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>800</width>
    <height>600</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <layout class="QGridLayout" name="gridLayout">
    <item row="0" column="0">
     <widget class="QTreeView" name="treeView"/>
    </item>
    <item row="1" column="0">
     <layout class="QHBoxLayout" name="horizontalLayout">
      <item>
       <spacer name="horizontalSpacer">
        <property name="orientation">
         <enum>Qt::Horizontal</enum>
        </property>
        <property name="sizeHint" stdset="0">
         <size>
          <width>40</width>
          <height>20</height>
         </size>
        </property>
       </spacer>
      </item>
      <item>
       <widget class="QPushButton" name="pushButton">
        <property name="text">
         <string>Quit</string>
        </property>
       </widget>
      </item>
     </layout>
    </item>
   </layout>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>800</width>
     <height>24</height>
    </rect>
   </property>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <resources/>
 <connections>
  <connection>
   <sender>pushButton</sender>
   <signal>clicked()</signal>
   <receiver>MainWindow</receiver>
   <slot>close()</slot>
   <hints>
    <hint type="sourcelabel">
     <x>756</x>
     <y>551</y>
    </hint>
    <hint type="destinationlabel">
     <x>615</x>
     <y>550</y>
    </hint>
   </hints>
  </connection>
 </connections>
</ui>
```

## ui -> pyのコード

```python
from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(800, 600)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
        self.gridLayout.setObjectName("gridLayout")
        self.treeView = QtWidgets.QTreeView(self.centralwidget)
        self.treeView.setObjectName("treeView")
        self.gridLayout.addWidget(self.treeView, 0, 0, 1, 1)
        self.horizontalLayout = QtWidgets.QHBoxLayout()
        self.horizontalLayout.setObjectName("horizontalLayout")
        spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
        self.horizontalLayout.addItem(spacerItem)
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setObjectName("pushButton")
        self.horizontalLayout.addWidget(self.pushButton)
        self.gridLayout.addLayout(self.horizontalLayout, 1, 0, 1, 1)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 24))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        self.pushButton.clicked.connect(MainWindow.close)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.pushButton.setText(_translate("MainWindow", "Quit"))
```