PyQt개발강좌, QAbstractTableModel과 QAbstractListModel로 커스텀 모델 만들기

PyQt는 Python 언어로 Qt 애플리케이션을 개발할 수 있도록 해주는 강력한 GUI 툴킷입니다. 그 중에서도 데이터 모델을 관리하는 데 매우 유용한 클래스가 QAbstractTableModelQAbstractListModel입니다. 이들 클래스를 사용하여 커스텀 데이터 모델을 생성함으로써, PyQt에서 복잡한 데이터 구조를 쉽게 표현하고 조작할 수 있습니다. 본 강좌에서는 QAbstractTableModelQAbstractListModel을 사용하여 커스텀 모델을 만드는 방법을 자세히 알아보겠습니다.

QAbstractTableModel 개요

QAbstractTableModel은 2차원 데이터 구조를 위한 모델 클래스로, 표 형태의 데이터를 관리하는 데 사용됩니다. 이는 Qt의 MVC (Model-View-Controller) 아키텍처에 따라 데이터와 표시를 분리하는 데 유용합니다. QAbstractTableModel을 상속하여 새로운 모델을 생성하면, 데이터를 쉽고 효율적으로 표시할 수 있습니다.

QAbstractTableModel의 주요 메소드

  • rowCount(self, parent=QModelIndex()): 모델의 행 수를 반환합니다.
  • columnCount(self, parent=QModelIndex()): 모델의 열 수를 반환합니다.
  • data(self, index: QModelIndex, role=int): 특정 인덱스에 해당하는 데이터를 반환합니다.
  • setData(self, index: QModelIndex, value, role=int): 특정 인덱스의 데이터를 설정합니다.
  • headerData(self, section, orientation, role=int): 헤더 데이터를 반환합니다.
  • flags(self, index: QModelIndex): 특정 인덱스의 플래그를 반환합니다.

QAbstractListModel 개요

QAbstractListModel은 1차원 데이터 구조를 위한 모델 클래스입니다. 리스트 형태의 데이터를 관리하는 데 이상적입니다. 사용자 인터페이스에서 리스트 데이터를 표시해야 할 경우, 이 클래스를 사용하여 커스텀 모델을 쉽게 구현할 수 있습니다.

QAbstractListModel의 주요 메소드

  • rowCount(self, parent=QModelIndex()): 모델의 아이템 수를 반환합니다.
  • data(self, index: QModelIndex, role=int): 특정 인덱스에 해당하는 데이터를 반환합니다.
  • setData(self, index: QModelIndex, value, role=int): 특정 인덱스의 데이터를 설정합니다.
  • flags(self, index: QModelIndex): 특정 인덱스의 플래그를 반환합니다.

커스텀 모델 만들기

이제 QAbstractTableModelQAbstractListModel을 이용해 커스텀 모델을 만드는 예제를 살펴보겠습니다. 먼저 QAbstractTableModel을 상속한 커스텀 모델을 만들어 보겠습니다.

지속 가능한 데이터 모델 생성: MyTableModel

import sys
from PyQt5.QtCore import QAbstractTableModel, QModelIndex, Qt
from PyQt5.QtWidgets import QApplication, QTableView, QVBoxLayout, QWidget, QPushButton


class MyTableModel(QAbstractTableModel):
    def __init__(self, data):
        super().__init__()
        self._data = data  # 데이터를 저장하는 리스트

    def rowCount(self, parent=QModelIndex()):
        return len(self._data)  # 행 수 반환

    def columnCount(self, parent=QModelIndex()):
        return len(self._data[0]) if self._data else 0  # 열 수 반환

    def data(self, index: QModelIndex, role=int):
        if role == Qt.DisplayRole:
            return self._data[index.row()][index.column()]  # 데이터 반환
        return None

    def setData(self, index: QModelIndex, value, role=int):
        if role == Qt.EditRole:
            self._data[index.row()][index.column()] = value  # 데이터 설정
            self.dataChanged.emit(index, index)  # 데이터 변경 알림
            return True
        return False

    def headerData(self, section, orientation, role=int):
        if role == Qt.DisplayRole:
            if orientation == Qt.Horizontal:
                return f'Column {section + 1}'  # 헤더 데이터 반환
            else:
                return f'Row {section + 1}'
        return None


class MyTableView(QWidget):
    def __init__(self):
        super().__init__()

        self.setWindowTitle("My Table Model")
        self.resize(500, 300)

        layout = QVBoxLayout(self)
        self.model = MyTableModel([['Data 1', 'Data 2'], ['Data 3', 'Data 4']])
        self.table_view = QTableView(self)
        self.table_view.setModel(self.model)

        layout.addWidget(self.table_view)

        # 수정 버튼
        self.button = QPushButton("Edit Cell", self)
        self.button.clicked.connect(self.edit_cell)
        layout.addWidget(self.button)

    def edit_cell(self):
        # (0, 0) 위치의 데이터를 변경
        self.model.setData(self.model.index(0, 0), "New Data", Qt.EditRole)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MyTableView()
    window.show()
    sys.exit(app.exec_())

위의 예제는 QAbstractTableModel을 상속받은 MyTableModel 클래스를 정의하고, 초기 데이터 리스트를 전달받아 모델을 생성합니다. 사용자 인터페이스에서 이 모델을 사용하여 데이터를 표시합니다. setData 메소드를 통해 셀 데이터를 수정할 수 있습니다.

리스트형 데이터 모델 생성: MyListModel

이번에는 QAbstractListModel을 상속받는 커스텀 모델을 만들어 보겠습니다.

from PyQt5.QtCore import QAbstractListModel, Qt
from PyQt5.QtWidgets import QApplication, QListView, QVBoxLayout, QWidget, QPushButton


class MyListModel(QAbstractListModel):
    def __init__(self, items):
        super().__init__()
        self._items = items  # 리스트 항목 저장

    def rowCount(self, parent=QModelIndex()):
        return len(self._items)  # 아이템 수 반환

    def data(self, index: QModelIndex, role=int):
        if role == Qt.DisplayRole:
            return self._items[index.row()]  # 데이터 반환
        return None

    def setData(self, index: QModelIndex, value, role=int):
        if role == Qt.EditRole:
            self._items[index.row()] = value  # 데이터 설정
            self.dataChanged.emit(index, index)  # 데이터 변경 알림
            return True
        return False

    def flags(self, index: QModelIndex):
        return Qt.ItemIsEditable  # 편집 가능 플래그 설정


class MyListView(QWidget):
    def __init__(self):
        super().__init__()

        self.setWindowTitle("My List Model")
        self.resize(300, 400)

        layout = QVBoxLayout(self)
        self.model = MyListModel(['Item 1', 'Item 2', 'Item 3'])
        self.list_view = QListView(self)
        self.list_view.setModel(self.model)

        layout.addWidget(self.list_view)

        # 수정 버튼
        self.button = QPushButton("Edit First Item", self)
        self.button.clicked.connect(self.edit_item)
        layout.addWidget(self.button)

    def edit_item(self):
        # 첫 번째 아이템 변경
        self.model.setData(self.model.index(0), "New Item 1", Qt.EditRole)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MyListView()
    window.show()
    sys.exit(app.exec_())

이 예제는 QAbstractListModel을 상속받은 MyListModel 클래스를 정의합니다. 리스트 항목을 처리하고, 사용자가 리스트 아이템을 수정할 수 있는 기능을 제공합니다.

데이터 모델과 사용자 인터페이스 연동

모델과 뷰를 연동하는 것은 PyQt에서 매우 중요합니다. 사용자에게 데이터를 표시할 뿐만 아니라, 데이터를 편집하고 업데이트하는 일련의 작업을 모델과 뷰 간의 원활한 통신을 통해 수행합니다. 이 과정에서 데이터를 변경할 때마다 dataChanged 신호를 발송하여 뷰에 변경 사항을 알려주어야 합니다. 이는 모델이 데이터의 변화를 감지하고 사용자 인터페이스가 최신 상태를 유지하도록 돕습니다.

결론

본 강좌에서는 QAbstractTableModelQAbstractListModel을 사용하여 커스텀 데이터 모델을 만드는 방법을 배웠습니다. 데이터 모델과 뷰간의 상호작용을 이해하고 이를 통해 복잡한 데이터를 관리하는 방법을 익히는 것은 PyQt 애플리케이션 개발에서 매우 중요한 부분입니다. 이 강좌에서 배운 내용을 바탕으로 여러분은 더 복잡하고 다양한 데이터를 처리하는 모델을 구현할 수 있을 것입니다.

이제 여러분이 커스텀 모델을 만들 능력을 갖추었으니, 다양한 응용 프로그램에서 이 지식을 활용하시기 바랍니다. Happy coding!