QTableWidget 엑셀 - QTableWidget egsel

QTableWidget 은 엑셀처럼 셀 형태로 데이터를 관리하기 위해 사용하는 위젯입니다. 많은 데이터를 다룰 때 사용하는 위젯으로 평소에 사용법을 알아 두시는 것이 좋습니다. QTableWidget 의 기본 사용법과 데이터 추가/삭제, Checkbox 위젯 추가 등 여러가지 구현 방법들을 알아보겠습니다.

1. 기본 생성

테이블 위젯은 QtWidgets 패키지의 QTableWidget 클래스를 사용해서 생성합니다. QTableWidget 의 행과 열의 개수는 setRowCount() setColumnCount() 로 지정합니다. 그리고 테이블의 크기는 다른 위젯과 마찬가지로 resize(width, height) 함수로 조절했습니다.

#-*- coding: utf-8 -*-
import sys
from PyQt5.QtWidgets import QMainWindow, QApplication, QTableWidget

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        # 윈도우 설정
        self.setGeometry(200, 100, 400, 400)  # x, y, w, h
        self.setWindowTitle('QTableWidget Sample Window')

        # QTableWidget
        self.tablewidget = QTableWidget(self)
        self.tablewidget.resize(400, 400)
        self.tablewidget.setRowCount(3) # 행 개수
        self.tablewidget.setColumnCount(3) # 열 개수

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

코드를 실행한 결과는 다음과 같습니다. 현재는 아무런 데이터도 들어가 있지 않은 빈 상태입니다.

QTableWidget 엑셀 - QTableWidget egsel

2. 데이터 추가 및 수정하기

다음은 비어 있는 테이블 위젯의 셀을 채워보겠습니다. 데이터를 입력하기 위해서는 QTableWidget 각 셀에 QTableWidgetItem 객체를 추가해야 합니다. 첫 번째 데이터 입력 방법은 QTableWidgetItem 객체를 생성하면서 생성자의 인수 값으로 텍스트를 넘기는 것입니다. QTableWidgetItem 객체가 들어갈 테이블의 위치는 setItem() 의 첫 번째와 두 번째 행/열 번호로 지정합니다.

from PyQt5.QtWidgets import QTableWidget, QTableWidgetItem

# QTableWidget 에 데이터 추가하기
self.insert_data()

def insert_data(self):
    self.tablewidget.setItem(0, 0, QTableWidgetItem("1행 1열"))
    self.tablewidget.setItem(0, 1, QTableWidgetItem("1행 2열"))
    self.tablewidget.setItem(1, 0, QTableWidgetItem("2행 1열"))
    self.tablewidget.setItem(1, 1, QTableWidgetItem("2행 2열"))

코드를 실행한 결과값은 다음과 같습니다. /열로 지정한 번호에 QTableWidgetItem 객체로 셀에 데이터를 채워 넣었습니다.

QTableWidget 엑셀 - QTableWidget egsel

두 번째는 QTableWidgetItem setText() 함수를 사용해서 셀에 텍스트를 입력하는 방법입니다. 그 전에 모든 셀에는 setItem() 함수로 QTableWidgetItem() 객체를 추가해야 합니다.

for i in range(0, self.rowcount):
    for j in range(0, self.colcount):
        self.tablewidget.setItem(i, j, QTableWidgetItem())

self.tablewidget.item(0, 1).setText("1행 2열")
self.tablewidget.item(0, 2).setText("1행 3열")

3. 셀 수정 가능하게 변경

테이블 내의 셀에 입력된 값을 수정하지 못하게 읽기 모드로 바꾸는 옵션이 setEditTriggers(QAbstractItemView.NoEditTriggers) 입니다. 이것은 기본값입니다. 코드를 실행하지 않아도 기본적으로 읽기 모드입니다.

from PyQt5.QtWidgets import QTableWidget, QAbstractItemView

self.tablewidget.setEditTriggers(QAbstractItemView.NoEditTriggers)

다시 편집모드로 바꾸고 싶다면 setEditTriggers() 함수의 옵션값을 QAbstractItemView.AllEditTriggers 로 변경해야 합니다. 옵션을 적용한 후 실행해서 셀을 더블클릭 합니다. 셀의 값은 수정 및 삭제 가능한 상태가 되었습니다.

from PyQt5.QtWidgets import QTableWidget, QAbstractItemView

self.tablewidget.setEditTriggers(QAbstractItemView.AllEditTriggers)
QTableWidget 엑셀 - QTableWidget egsel

4. 테이블 데이터 조회하기

특정 위치의 셀 값을 조회하는 함수는 QTableWidget.item(row, column) 입니다. 조회하고 싶은 특정 셀의 행과 열 번호를 2개의 인수값으로 입력합니다. QTableWidget.item(row, column) 함수 실행으로 QTableWidgetItem 객체를 반환합니다. 그리고 QTableWidgetItem.text() 함수로 셀 안에 텍스트 값을 반환합니다.

data = self.tablewidget.item(0, 1)
print(data.text())

다음은 위의 코드를 응용해서 모든 셀 정보를 출력해 보겠습니다. 그러기 위해서는 테이블의 행과 열의 개수를 알아야 합니다. QTableWidget rowCount(), columnCount() 함수를 이용합니다. 행과 열의 개수만큼 이중 반복문을 돌면서 데이터 값을 콘솔에 출력합니다.

# 데이터 조회
rowcount = self.tablewidget.rowCount()
colcount = self.tablewidget.columnCount()

for i in range(0, rowcount):
    for j in range(0, colcount):
        data = self.tablewidget.item(i, j)
        if data is not None:
            print(data.text())
        else:
            print('blank')

테이블의 모든 데이터를 콘솔에 출력한 결과는 다음과 같습니다.

QTableWidget 엑셀 - QTableWidget egsel

5. 체크 박스 넣기

QTableWidget 에는 각 셀에 위젯 컨트롤을 삽입할 수 있습니다. 테이블에서 제일 많이 쓰일 만한 체크 박스인 QChcekBox 위젯을 넣어보겠습니다. 함수는 setCellWidget() 을 사용합니다. 첫 번째, 두 번째 인수는 행/열 번호입니다. 세 번째에 QCheckBox 위젯 객체를 입력합니다.

from PyQt5.QtWidgets import QTableWidget, QCheckBox

self.chbox1 = QCheckBox()
self.chbox2 = QCheckBox()

self.tablewidget.setCellWidget(0, 0, self.chbox1)
self.tablewidget.setCellWidget(1, 0, self.chbox2)

다음은 QCheckBox check 여부를 확인하는 stateChanged 시그널을 연결합니다. 시그널 실행함수에는 체크 박스로부터 전달 받은 state 값을 콘솔에 출력합니다.

# checkbox 시그널 연결
self.chbox1.stateChanged.connect(self.check_state_checkbox1)
self.chbox2.stateChanged.connect(self.check_state_checkbox2)

# check box state 변경 함수
def check_state_checkbox1(self, state):
    print('chbox1 : ' + str(state))

def check_state_checkbox2(self, state):
    print('chbox2 : ' + str(state))

실행한 결과는 다음과 같습니다. stateChanged 의 시그널에서 전달받은 state 정수 값은 0 2 이므로, 체크 여부를 확인할 수 있습니다.

QTableWidget 엑셀 - QTableWidget egsel

6. 내림차순, 오름차순 정렬하기

테이블 위젯의 헤더 부분을 클릭하면 해당 열의 값을 분석해서 오름차순/내림차순으로 정렬하는 기능이 있습니다. QTableWiget 의 함수 setSortingEnabled() 를 이용해서 사용 유무를 세팅합니다. 기본값은 False 이며, True 로 넘기면 열 정렬이 가능합니다. 현재 QTableWiget Sorting 가능 여부는 isSortingEnabled() 로 판단합니다.

# Sorting 가능한지 여부를 판단한다.
if not self.tablewidget.isSortingEnabled():
    self.tablewidget.setSortingEnabled(True)

setSortingEnabled() 함수로 설정을 변겨앟고 상단 레이블 영역을 클릭해 보세요. 오름차순, 내림차순 순으로 행이 나열됩니다.

QTableWidget 엑셀 - QTableWidget egsel

7. 열 사이즈 수정하기

열 사이즈를 수정하는 방법은 여러가지가 있습니다. 여기서 소개할 방법은 두 가지입니다. 컨텐츠 크기에 맞게 자동으로 수정되도록 설정하거나 직접 크기를 입력해서 조절하는 방법입니다. 컨텐츠 크기에 맞게 자동 수정 함수는 QTableWidget resizeColumnToContents() resizeRowToContents() 로 행과 열을 나누어서 설정합니다. 함수의 파라미터로 행/열 번호를 입력합니다.

# 자동으로 수정하기
self.tablewidget.resizeColumnToContents(1)
self.tablewidget.resizeRowToContents(1)

다음은 사이즈를 직접 조절하는 방법입니다. QTableWidget horizontalHeader() 함수로 QHeaderView 객체를 리턴받아 resizeSection() 함수로 조절합니다. 함수의 첫 번째 인수는 열 번호이며, 두 번째가 열 사이즈입니다.

from PyQt5.QtWidgets import QHeaderView

# 사이즈로 수정하기
header = self.tablewidget.horizontalHeader()
header.resizeSection(0, 10)

8. 데이터 정렬하기

QTableWidget 의 셀 내부에 데이터를 정렬하는 함수는 QTableWidgetItem setTextAlignment() 함수입니다. 인수로 들어갈 플래그는 셀에서의 위치를 나타내는 값입니다.

self.tablewidget.item(0, 2).setTextAlignment(Qt.AlignVCenter | Qt.AlignRight)
self.tablewidget.item(1, 2).setTextAlignment(Qt.AlignVCenter | Qt.AlignLeft)

플래그 종류는 다음과 같습니다. 가로와 세로로 구분되어 있습니다. 두 플래그를 조합해서 데이터를 정렬합니다.

QTableWidget 엑셀 - QTableWidget egsel

결과는 다음과 같습니다. 해당 셀의 위치는 오른쪽과 왼쪽으로 배치가 되었습니다.

QTableWidget 엑셀 - QTableWidget egsel

9. 이벤트 시그널 구현하기

다음은 QTableWidget 제공하는 시그널을 이용해서 함수를 구현해 보겠습니다. 자주 사용하는 시그널의 종류는 아래와 같습니다.

  • cellChanged : Cell의 내용이 바뀌었을 때 이벤트 발생
  • currentCellChanged : 선택된 Cell 이 바뀌었을 때 이벤트 발생
  • cellClicked : Cell 이 클릭 되었을 때 이벤트 발생

셀의 값이 변경되었을 때 발생하는 cellChanged 시그널에 cellchanged_event() 함수를 연결했습니다. 시그널로부터 행과 열 번호를 넘겨 받습니다. 함수는 이벤트가 발생한 셀에 들어가 있는 값을 읽어와서 콘솔에 출력합니다.

# 테이블 이벤트 지정
self.tablewidget.cellChanged.connect(self.cellchanged_event)

# 셀의 내용이 바뀌었을 때 이벤트
def cellchanged_event(self, row, col):
    data = self.tablewidget.item(row, col)
    print("cellchanged_event 발생 : ", data.text())
QTableWidget 엑셀 - QTableWidget egsel

다음은 셀의 포커스가 바뀌었을 때 발생하는 currentCellChanged시그널을 구현했습니다. 시그널로부터 전달받은 값은 현재 행/열 번호와 이전 포커스가 있던 행/열 번호를 받을 수 있습니다. 시그널에 연결된 함수에서는 전달받은 행/열 번호에 해당하는 셀 값을 콘솔에 출력합니다.

self.tablewidget.currentCellChanged.connect(self.currentcellchanged_event)

# 선택한 셀이 바뀌면 발생하는 이벤트
def currentcellchanged_event(self, row, col, pre_row, pre_col):
    current_data = self.tablewidget.item(row, col) # 현재 선택 셀 값
    pre_data = self.tablewidget.item(pre_row, pre_col) # 이전 선택 셀 값
    if pre_data is not None:
        print("이전 선택 셀 값 : ", pre_data.text())
    else:
        print("이전 선택 셀 값 : 없음")

    print("현재 선택 셀 값 : ", current_data.text())
QTableWidget 엑셀 - QTableWidget egsel

다음은 셀 클릭과 더블클릭 시그널인 cellClicked cellDoubleClicked 을 구현한 내용입니다. 두 시그널 모두 행과 열 번호를 넘겨받을 수 있습니다. 행과 열 번호를 이용해서 셀 값을 가져와서 콘솔에 출력합니다.

# 테이블 이벤트 지정
self.tablewidget.cellClicked.connect(self.cellclicked_event)
self.tablewidget.cellDoubleClicked.connect(self.celldoubleclicked_event)

# 셀 더블클릭 때 발생하는 이벤트
def celldoubleclicked_event(self, row, col):
    data = self.tablewidget.item(row, col)
    print("셀 더블클릭 셀 값 : ", data.text())

# 셀 선택할 때 발생하는 이벤트
def cellclicked_event(self, row, col):
    data = self.tablewidget.item(row, col)
    print("셀 클릭 셀 값 : ", data.text())
QTableWidget 엑셀 - QTableWidget egsel

지금까지 구현한 모든 내용이 들어가 있는 전체 소스는 아래와 같습니다.

#-*- coding: utf-8 -*-
import sys
from PyQt5.QtWidgets import QMainWindow, QApplication, QTableWidget, \
    QTableWidgetItem, QAbstractItemView, QTextEdit, QPushButton
from PyQt5.Qt import Qt

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        # 윈도우 설정
        self.setGeometry(200, 100, 400, 400)  # x, y, w, h
        self.setWindowTitle('QTableWidget Sample Window')

        # QTableWidget
        self.tablewidget = QTableWidget(self)
        self.tablewidget.resize(400, 200)
        self.tablewidget.setRowCount(3) # 행 개수
        self.tablewidget.setColumnCount(3) # 열 개수
        self.tablewidget.setEditTriggers(QAbstractItemView.NoEditTriggers)

        self.edit = QTextEdit(self)
        self.edit.move(10, 210)

        self.btn = QPushButton(self)
        self.btn.setText('0, 0 셀 입력')
        self.btn.move(120, 210)

        # QTableWidget 에 데이터 추가하기
        self.insert_data()

        # 수정 가능한 필드
        # self.tablewidget.setEditTriggers(QAbstractItemView.AllEditTriggers)

        # 데이터 조회
        rowcount = self.tablewidget.rowCount()
        colcount = self.tablewidget.columnCount()

        for i in range(0, rowcount):
            for j in range(0, colcount):
                data = self.tablewidget.item(i, j)
                if data is not None:
                    print(data.text())
                else:
                    print('blank')

        # self.tablewidget.item(0, 0).setText('1111')

        # 테이블 이벤트 지정
        self.tablewidget.cellChanged.connect(self.cellchanged_event)
        self.tablewidget.currentCellChanged.connect(self.currentcellchanged_event)
        self.tablewidget.cellClicked.connect(self.cellclicked_event)
        self.tablewidget.cellDoubleClicked.connect(self.celldoubleclicked_event)

    # 셀 더블클릭 때 발생하는 이벤트
    def celldoubleclicked_event(self, row, col):
        data = self.tablewidget.item(row, col)
        print("셀 더블클릭 셀 값 : ", data.text())

    # 셀 선택할 때 발생하는 이벤트
    def cellclicked_event(self, row, col):
        data = self.tablewidget.item(row, col)
        print("셀 클릭 셀 값 : ", data.text())

    # 선택한 셀이 바뀌면 발생하는 이벤트
    def currentcellchanged_event(self, row, col, pre_row, pre_col):
        current_data = self.tablewidget.item(row, col) # 현재 선택 셀 값
        pre_data = self.tablewidget.item(pre_row, pre_col) # 이전 선택 셀 값
        if pre_data is not None:
            print("이전 선택 셀 값 : ", pre_data.text())
        else:
            print("이전 선택 셀 값 : 없음")

        print("현재 선택 셀 값 : ", current_data.text())

    # 셀의 내용이 바뀌었을 때 이벤트
    def cellchanged_event(self, row, col):
        data = self.tablewidget.item(row, col)
        print("cellchanged_event 발생 : ", data.text())

    def insert_data(self):
        self.tablewidget.setItem(0, 0, QTableWidgetItem("1행 1열"))
        self.tablewidget.setItem(0, 1, QTableWidgetItem("1행 2열"))
        self.tablewidget.setItem(1, 0, QTableWidgetItem("2행 1열"))
        self.tablewidget.setItem(1, 1, QTableWidgetItem("2행 2열"))

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