본문 바로가기

PyQt GUI

PyQt GUI (17) QTreeView를 이용하여 폴더 트리 만들기 + 드래그-앤-드롭 drag-and-drop을 이용하여 파일 이름을 옮기기

728x90

이번 포스팅에서는 윈도우 파일 탐색기의 왼쪽 프래임에서 볼 수 있는 폴더 트리를 만들어 보겠습니다. 폴더 트리는 

위 탐색기 캡쳐에서 왼쪽 프래임에 있는 것 입니다. 전체 파일/폴더의 구조를 보여줍니다. 이 폴더 트리를 이용하여 파일/폴더의 구조를 파악하거나 파일/폴더를 선택할 수 도 있습니다. 

 

오늘 만들 GUI의 전체 코드는 아래와 같습니다. 

import sys
from PyQt5.QtWidgets import *

class QTreeView(QTreeView):
    def __init__(self):
        super(QTreeView, self).__init__()

    def edit(self, index, trigger, event):
        return False


class QLineEdit(QLineEdit):
    def __init__(self):
        super(QLineEdit, self).__init__()
        self.setAcceptDrops(True)

    def dragEnterEvent(self, event):
        data = event.mimeData()
        urls = data.urls()
        if (urls and urls[0].scheme() == 'file'):
            event.acceptProposedAction()

    def dropEvent(self, event):
        data = event.mimeData()
        urls = data.urls()
        if (urls and urls[0].scheme() == 'file'):
            filepath = str(urls[0].path())[1:]
            self.setText(filepath)


class Main(QDialog):
    def __init__(self):
        super().__init__()
        self.init_ui()

    def init_ui(self):
        layout = QVBoxLayout()

        root_path = "C:/"
        self.model_file_system = QFileSystemModel()
        self.model_file_system.setRootPath(root_path)
        self.model_file_system.setReadOnly(False)

        self.tree_view = QTreeView()
        self.tree_view.setModel(self.model_file_system)
        self.tree_view.setRootIndex(self.model_file_system.index(root_path))
        self.tree_view.doubleClicked.connect(lambda index : self.item_double_clicked(index))

        self.tree_view.setDragEnabled(True)
        self.tree_view.setColumnWidth(0, 300)

        lineedit = QLineEdit()

        layout.addWidget(self.tree_view)
        layout.addWidget(lineedit)

        self.setLayout(layout)
        self.resize(800, 500)
        self.show()


    def item_double_clicked(self, index):
        print(self.model_file_system.filePath(index))

if __name__ == '__main__':
    app = QApplication(sys.argv)
    main = Main()
    sys.exit(app.exec_())

위 코드를 실행하면, 다음과 같은 UI를 얻을 수 있습니다. QVBoxLayout을 이용하여 두개이 위젯을 배치한 것인데, 위에는 QTreeView위젯, 그리고 아래에는 평범한 QLineEdit 위젯 입니다. QTreeView위젯을 통해서 파일/폴더를 탐색하고, 원하는 파일(의 경로)를 아래 QLineEdit에 드래그-앤-드롭drag-and-drop 하여 옮길 것 입니다. 

위 코드에서 새롭게 소개되는 부분을 설명하겠습니다. 

 

        '''
        QFileSystemModel를 생성하고, 설정
        '''
        root_path = "C:\Windows"
        self.model_file_system = QFileSystemModel()
        self.model_file_system.setRootPath(root_path)
        self.model_file_system.setReadOnly(False)

		'''
        QTreeView 위젯을 생성하고 설정
        '''
        self.tree_view = QTreeView()
        self.tree_view.setModel(self.model_file_system)
        self.tree_view.setRootIndex(self.model_file_system.index(root_path))
        self.tree_view.doubleClicked.connect(lambda index : self.item_double_clicked(index))

	### 파일/폴더의 이름을 드래그 할 수 있도록 설정
        self.tree_view.setDragEnabled(True)
        
        ### QTreeView의 0 번째 컬럼의 크기를 300 으로 설정
        self.tree_view.setColumnWidth(0, 300)

폴터 탐색기는 QTreeView위젯을 통해 구현됩니다. 

 

QTreeView 위젯을 생성하기에 앞서, 어떤 구조의 파일/폴더 시스템을 QTreeView를 통해서 보여줄 것 인가를 지정해야 합니다. 이는 QFileSystemModel를 통해서 할 수 있는데요, QFileSystemModel 클래스를 생성하고, 루트 경로를 설정합니다. 간단하게 .setRootPath(루트경로) 메서드를 사용합니다. 

 

그 다음은 QTreeView 위젯을 생생해 줍니다. 

그리고 앞에서 만든 QFileSysteModel을 QTreeView에 세팅해 줍니다. QFileSystemModel에서 정의한 파일 시스템 모델을 QTreeView를 통해 디스플레이 합니다. QTreeView 위젯의 루트 경로를 위와 같이 설정하면 됩니다. 

 

그 다음에 있는 doubleClicked.connect 메서드를 사용한 부분은, QTreeView의 파일이나 폴더의 이름이 더블 클릭 되었을 때, 어떤 Slot을 실행시킬 것인가를 정의해 주는 것 입니다. 원하는 함수를 slot으로 연결하면 되는데, 이번 예제에서는 선택된 파일/폴더의 index 값을 활용하는 것을 해보도록 하겠습니다. 

 

setDragEnabled(True)를 이용하여 파일/폴더가 드래그 될 수 있도록 설정할 수 있고, setColumnWidth(0, 300)을 이용하여 0번째 컬럼의 가로 방향 크기를 300으로 고정해 주었습니다. 0 자리에 원하는 숫자(컬럼의 순서)를 300자리에 원하는 가로 길이를 넣어주면 됩니다. 

    def item_double_clicked(self, index):
        print(self.model_file_system.filePath(index))

doubleClicked.connet() 시그널에 연결되는 slot의 함수 입니다. QTreeView의 파일/폴더가 더블 클릭이 되면, 해당 파일/폴더의 index를 전달하게 되고, item_double_clicked 함수에서는 index를 파일/폴더의 경로로 바꾸어 출력하게 됩니다. 즉, QFileSystemModel 클래스의 .filePath(index) 메서드를 실행하면, QTreeView에서 더블 클릭 된(정확히는 QTreeView가 보여게되는 QFileSystemModel에서 선택된) 파일/폴더의 경로를 참조하게 됩니다. 

 

더블 클릭된 파일/폴더의 경로를 알게 되었으니, 이를 이용하여 원하는 동작을 추가할 수 있습니다. 파일이나 폴더를 실행한다든지 할 수 있습니다. 

 

코드의 맨 앞부분에는 QTreeView 클래스를 오버라이딩 하는 것이 나오는데,

class QTreeView(QTreeView):
    def __init__(self):
        super(QTreeView, self).__init__()

    def edit(self, index, trigger, event):
        return False

QTreeView 위젯의 edit 메서드를 위와 같이 수정해 주었습니다. 기본적으로 QTreeView의 파일/폴더 이름이 더블 클릭 되면, 파일/폴더의 이름을 변경할 수 있도록 파일/폴더 이름이 편집 모드로 바뀌게 됩니다. 그러나, 이번에 우리가 만들 GUI에서는 파일/폴더의 이름을 더블 클릭하면, 원하는 기능이 작동 될 수 있도록 (원하는 slot에 signal을 줄 수 있도록) 하고 싶기 때문에, 위와 같이 오버라이딩 해 준 것 입니다. 

 

그 다음 부분에는 QLineEdit을 오버라이딩 하는 것이 나오는데,

class QLineEdit(QLineEdit):
    def __init__(self):
        super(QLineEdit, self).__init__()
        self.setAcceptDrops(True)

    def dragEnterEvent(self, event):
        data = event.mimeData()
        urls = data.urls()
        if (urls and urls[0].scheme() == 'file'):
            event.acceptProposedAction()

    def dropEvent(self, event):
        data = event.mimeData()
        urls = data.urls()
        if (urls and urls[0].scheme() == 'file'):
            filepath = str(urls[0].path())[1:]
            self.setText(filepath)

위 코드에서도 볼 수 있듯, QLineEdit이 드롭을 받을 수 있도록 록 한 것 입니다. QTreeView의 파일/폴더를 드래그 해서, QLineEdit에 드롭하여 QLineEdit에 파일/폴더 이름가 쓰일 수 있도록 할 것 입니다. 

 

그렇게 하기 위해서는 위와 같이 dragEnterEvent와 dropEvent를 오버라이딩 해야하는데, 말 그대로 QLineEdit에 drag와 drop 이벤트가 발생했을 때, 이를 어떻게 처리하면 되는지에 대한 설정입니다. 

 

QLineEdit에 드래그, 드롭 이벤트를 설정하지 않는다면 (즉, 클래스 오버라이딩을 하지 않는 기본 QLineEdit을 사용한다면)

위와 같이 파일/폴더 이름을 드래그 하여 QLineEdit위에 놓았을 때, 금지 표시가 뜨게 됩니다. 즉, QLineEdit에 해당 폴더/파일 이름을 드롭할 수 없다는 것 입니다. 

 

그러나, 위 코드와 같이 드래그와 드롭 이벤트가 발생시, 어떻게 처리할 것인가를 설정해 준다면, 

위와 같이 파일/폴더의 이름을 QLineEdit에 드롭 할 수 있습니다. 그리고 QLineEdit에는 파일/폴더의 경로가 쓰여지게 됩니다. 

 

폴더 트리에서 파일/폴더의 이름을 드래그-앤-드롭하여 선택 한 뒤, 해당 파일/폴더를 추가적인 과정을 거쳐 쉽게 사용할 수 있습니다. 

 

파일이나 폴더의 경로를 설정할 때에는 지난 포스팅에서 본 바 있는 QFileDialog.getOpenFileName를 이용하여 파일/폴더 선택 대화창 이용할 수 있지만, QTreeView는 한 번에 파일/폴더의 구조를 보여 준다는 장점이 있습니다. 필요에 따라서 두 위젯 중 효율적으로 GUI를 구현할 수 있는 위젯을 선택하면 됩니다. 

 

 

728x90