[ PySide6 ] QTableWidget 열 너비 자동 조절 후 마우스로 수동 조절하는 방법
- QTableWidget 열 너비 조절의 기본 문제
- QHeaderView의 ResizeMode 이해하기
- 열 너비 자동 조절과 수동 조절 동시에 구현하기
- 코드 구현 및 상세 설명
- 다양한 활용 예시
- 자주 발생하는 문제와 해결 방법
- 마무리
QTableWidget 열 너비 조절의 기본 문제
PySide6로 데이터 테이블을 구현할 때
QTableWidget은 매우 유용한 위젯입니다.
하지만 많은 개발자들이 테이블 열 너비 조절과 관련하여 다음과 같은 딜레마에 빠지곤 합니다.
1. 데이터를 처음 로드할 때는 테이블 전체 너비를 꽉 채워서 보기 좋게 표시하고 싶다.
2. 동시에 사용자가 나중에 마우스로 열 너비를 자유롭게 조절할 수 있게 하고 싶다.
이 두 가지 요구사항을 동시에 만족시키는 것이 쉽지 않은 이유는 QHeaderView의 ResizeMode 설정 방식 때문입니다.
QHeaderView의 ResizeMode 이해하기
PySide6의 QHeaderView는 테이블의 행과 열 헤더를 관리하는 클래스로,
열 너비 조절 방식을 결정하는 setSectionResizeMode() 메서드를 제공합니다.
이 메서드는 다음과 같은 ResizeMode 값을 받습니다:
QHeaderView.Interactive 모드
Interactive 모드는 사용자가 마우스로 열 너비를 직접 조절할 수 있게 해주는 모드입니다.
이 모드의 특징은:
- 사용자가 마우스로 열 경계를 드래그하여 너비를 자유롭게 조절할 수 있습니다.
- 기본적으로 열 너비는 내용에 맞게 자동으로 조절되지 않습니다.
- 테이블 크기가 변경되어도 열 너비는 고정된 상태로 유지됩니다.
- 개발자가 프로그래밍 방식으로 setColumnWidth()를 사용하여 열 너비를 설정할 수 있습니다.
QHeaderView.Stretch 모드
Stretch 모드는 테이블의 전체 너비에 맞게 모든 열을 자동으로 분배하는 모드입니다.
이 모드의 특징은:
- 테이블의 전체 너비에 맞게 모든 열이 균등하게 분배됩니다.
- 테이블 크기가 변경되면 열 너비도 자동으로 조정됩니다.
- 사용자가 마우스로 열 너비를 조절할 수 없습니다.
- 개발자가 setColumnWidth()를 호출해도 효과가 없습니다.
기타 ResizeMode
- QHeaderView.Fixed: 열 너비가 고정되며 사용자가 조절할 수 없습니다.
- QHeaderView.ResizeToContents: 열 내용에 맞게 자동으로 너비가 조절됩니다.
열 너비 자동 조절과 수동 조절 동시에 구현하기
이 문제를 해결하기 위한 핵심 아이디어는 다음과 같은 2단계 접근법입니다:
1. 처음에는 QHeaderView.Stretch 모드를 사용하여 테이블 전체 너비에 맞게 열을 분배합니다.
2. 약간의 지연 후에 각 열의 현재 너비를 저장한 다음,
모든 열을 QHeaderView.Interactive 모드로 변경하고 저장해둔 너비를 다시 적용합니다.
이렇게 하면 처음에는 테이블 전체 너비를 꽉 채우면서도,
이후에는 사용자가 마우스로 열 너비를 자유롭게 조절할 수 있게 됩니다.
코드 구현 및 상세 설명
다음은 엑셀 파일을 QTableWidget에 로드하면서 위에서 설명한 2단계 접근법을 구현한 PySide6 코드입니다:
def Load_Excel_file(self, file_path):
df = pd.read_excel(file_path)
rows, columns = df.shape
self.ui.tableWidget.setRowCount(rows)
self.ui.tableWidget.setColumnCount(columns)
self.ui.tableWidget.setHorizontalHeaderLabels(df.columns)
# 데이터 채우기
for row in range(rows):
for col in range(columns):
value = str(df.iloc[row, col])
item = QTableWidgetItem(value)
self.ui.tableWidget.setItem(row, col, item)
# 1단계: 먼저 Stretch 모드로 설정하여 테이블 너비에 꽉 차게 표시
header = self.ui.tableWidget.horizontalHeader()
header.setSectionResizeMode(QHeaderView.Stretch)
# 2단계: QTimer를 사용하여 약간의 지연 후 Interactive 모드로 전환
# 이렇게 하면 먼저 Stretch로 적절히 배분된 너비가 유지되면서
# 이후에는 사용자가 조절 가능해짐
QTimer.singleShot(100, self.make_columns_interactive)
def make_columns_interactive(self):
header = self.ui.tableWidget.horizontalHeader()
# 현재 각 열의 너비를 저장
column_widths = []
for i in range(self.ui.tableWidget.columnCount()):
column_widths.append(self.ui.tableWidget.columnWidth(i))
# 모든 열을 Interactive 모드로 변경
for i in range(self.ui.tableWidget.columnCount()):
header.setSectionResizeMode(i, QHeaderView.Interactive)
# 저장해둔 너비를 다시 적용
for i, width in enumerate(column_widths):
self.ui.tableWidget.setColumnWidth(i, width)
코드 상세 설명
1. 엑셀 파일 로드 및 테이블 초기화:
- pandas를 사용하여 엑셀 파일을 DataFrame으로 로드합니다.
- QTableWidget의 행과 열 수를 DataFrame의 크기에 맞게 설정합니다.
- 테이블 헤더를 DataFrame의 열 이름으로 설정합니다.
2. 데이터 채우기:
- DataFrame의 모든 데이터를 QTableWidgetItem으로 변환하여 테이블에 추가합니다.
- 모든 값을 문자열로 변환하여 표시합니다.
3. 1단계: Stretch 모드 설정:
- `header.setSectionResizeMode(QHeaderView.Stretch)`를 사용하여
모든 열이 테이블 전체 너비에 맞게 균등하게 분배되도록 합니다.
- 이 상태에서는 사용자가 마우스로 열 너비를 조절할 수 없습니다.
4. 2단계: 지연 후 Interactive 모드로 전환:
- `QTimer.singleShot(100, self.make_columns_interactive)`를 사용하여 100ms 후에 make_columns_interactive 함수가 호출되도록 합니다. - 이 지연은 테이블이 화면에 렌더링되고
Stretch 모드가 적용된 후에 Interactive 모드로 전환하기 위함입니다.
5.make_columns_interactive 함수:
- 현재 각 열의 너비를 저장합니다. -
모든 열을 QHeaderView.Interactive 모드로 변경합니다.
- 저장해둔 너비를 다시 적용하여 레이아웃이 변경되지 않게 합니다.
Interactive 모드는 사용자가 마우스로 열 너비를 직접 조절할 수 있게 해주는 모드입니다. 이는 다양한 데이터를 보는 사용자에게 중요한 기능입니다. 예를 들어, 특정 열의 데이터가 더 중요하거나 자세히 볼 필요가 있을 때 해당 열을 넓게 조절할 수 있습니다. 또한 화면 공간이 제한된 경우 덜 중요한 열은 좁게 조절하여 공간을 효율적으로 사용할 수 있습니다.
다양한 활용 예시
이 접근법은 다양한 상황에서 활용할 수 있습니다. 몇 가지 예시를 살펴보겠습니다.
예시 1: CSV 파일 로드 시 적용하기
CSV 파일을 로드할 때도 동일한 방식을 적용할 수 있습니다:
def Load_CSV_file(self, file_path):
df = pd.read_csv(file_path)
rows, columns = df.shape
self.ui.tableWidget.setRowCount(rows)
self.ui.tableWidget.setColumnCount(columns)
self.ui.tableWidget.setHorizontalHeaderLabels(df.columns)
# 데이터 채우기
for row in range(rows):
for col in range(columns):
value = str(df.iloc[row, col])
item = QTableWidgetItem(value)
self.ui.tableWidget.setItem(row, col, item)
# 1단계: Stretch 모드 설정
header = self.ui.tableWidget.horizontalHeader()
header.setSectionResizeMode(QHeaderView.Stretch)
# 2단계: 지연 후 Interactive 모드로 전환
QTimer.singleShot(100, self.make_columns_interactive)
예시 2: 특정 열만 고정 너비로 설정하기
일부 열은 고정 너비로, 나머지 열은 자동 조절되도록 설정하고 싶을 때:
def make_columns_interactive(self):
header = self.ui.tableWidget.horizontalHeader()
# 현재 각 열의 너비를 저장
column_widths = []
for i in range(self.ui.tableWidget.columnCount()):
column_widths.append(self.ui.tableWidget.columnWidth(i))
# 모든 열을 Interactive 모드로 변경
for i in range(self.ui.tableWidget.columnCount()):
header.setSectionResizeMode(i, QHeaderView.Interactive)
# 저장해둔 너비를 다시 적용 (특정 열은 고정 너비로 설정)
for i, width in enumerate(column_widths):
if i == 0: # 첫 번째 열
self.ui.tableWidget.setColumnWidth(i, 100) # 100px로 고정
elif i == 1: # 두 번째 열
self.ui.tableWidget.setColumnWidth(i, 150) # 150px로 고정
else:
self.ui.tableWidget.setColumnWidth(i, width) # 나머지 열은 원래 너비 유지
예시 3: 내용에 맞게 열 너비 조정 후 Interactive 모드로 전환
먼저 내용에 맞게 열 너비를 조정한 후 사용자가 조절할 수 있게 하고 싶을 때:
def Load_Excel_file(self, file_path):
df = pd.read_excel(file_path)
rows, columns = df.shape
self.ui.tableWidget.setRowCount(rows)
self.ui.tableWidget.setColumnCount(columns)
self.ui.tableWidget.setHorizontalHeaderLabels(df.columns)
# 데이터 채우기
for row in range(rows):
for col in range(columns):
value = str(df.iloc[row, col])
item = QTableWidgetItem(value)
self.ui.tableWidget.setItem(row, col, item)
# 내용에 맞게 열 너비 조정
self.ui.tableWidget.resizeColumnsToContents()
# QTimer를 사용하여 약간의 지연 후 Interactive 모드로 전환
QTimer.singleShot(100, self.make_columns_interactive_with_min_width)
def make_columns_interactive_with_min_width(self):
header = self.ui.tableWidget.horizontalHeader()
# 현재 각 열의 너비를 저장
column_widths = []
for i in range(self.ui.tableWidget.columnCount()):
column_widths.append(self.ui.tableWidget.columnWidth(i))
# 모든 열을 Interactive 모드로 변경
for i in range(self.ui.tableWidget.columnCount()):
header.setSectionResizeMode(i, QHeaderView.Interactive)
# 저장해둔 너비를 다시 적용 (최소 너비 제한 추가)
for i, width in enumerate(column_widths):
# 최소 너비를 80px로 설정
self.ui.tableWidget.setColumnWidth(i, max(width, 80))
예시 4: 마지막 열만 Stretch 모드로 유지하기
대부분의 열은 Interactive 모드로, 마지막 열만 Stretch 모드로 설정하고 싶을 때:
def make_columns_interactive(self):
header = self.ui.tableWidget.horizontalHeader()
# 현재 각 열의 너비를 저장
column_widths = []
for i in range(self.ui.tableWidget.columnCount()):
column_widths.append(self.ui.tableWidget.columnWidth(i))
# 마지막 열을 제외한 모든 열을 Interactive 모드로 변경
for i in range(self.ui.tableWidget.columnCount() - 1):
header.setSectionResizeMode(i, QHeaderView.Interactive)
self.ui.tableWidget.setColumnWidth(i, column_widths[i])
# 마지막 열은 Stretch 모드로 설정
header.setSectionResizeMode(self.ui.tableWidget.columnCount() - 1, QHeaderView.Stretch)
예시 5: 테이블 크기 변경 시 열 너비 비율 유지하기
테이블 크기가 변경될 때 열 너비의 비율을 유지하려면:
def resizeEvent(self, event):
super().resizeEvent(event)
# 테이블이 로드되었을 때만 실행
if self.ui.tableWidget.columnCount() > 0 and hasattr(self, 'column_ratios'):
# 새 테이블 너비에 맞게 각 열 조정
new_width = self.ui.tableWidget.width() - 2 # 테이블 테두리 고려
for i, ratio in enumerate(self.column_ratios):
self.ui.tableWidget.setColumnWidth(i, int(new_width * ratio))
def make_columns_interactive(self):
header = self.ui.tableWidget.horizontalHeader()
# 현재 각 열의 너비를 저장
column_widths = []
for i in range(self.ui.tableWidget.columnCount()):
column_widths.append(self.ui.tableWidget.columnWidth(i))
# 열 너비 비율 계산 및 저장
total_width = sum(column_widths)
self.column_ratios = [width / total_width for width in column_widths]
# 모든 열을 Interactive 모드로 변경
for i in range(self.ui.tableWidget.columnCount()):
header.setSectionResizeMode(i, QHeaderView.Interactive)
# 저장해둔 너비를 다시 적용
for i, width in enumerate(column_widths):
self.ui.tableWidget.setColumnWidth(i, width)
자주 발생하는 문제와 해결 방법
QTableWidget의 열 너비 조절과 관련하여 자주 발생하는 문제와 해결 방법을 살펴보겠습니다.
문제 1: QTimer 지연 시간이 충분하지 않을 때
일부 환경에서는 100ms의 지연 시간이 충분하지 않을 수 있습니다.
이 경우 지연 시간을 늘려볼 수 있습니다:
QTimer.singleShot(200, self.make_columns_interactive) # 지연 시간을 200ms로 늘림
문제 2: 테이블 크기 변경 시 열 너비가 초기화되는 문제
테이블이 포함된 창의 크기가 변경될 때 열 너비가 초기화되는 문제는
사용자 수동 조정 감지 기능을 추가하여 해결할 수 있습니다:
def __init__(self):
# 기존 초기화 코드...
# 헤더 섹션 크기 변경 시그널 연결
self.ui.tableWidget.horizontalHeader().sectionResized.connect(self.on_section_resized)
# 사용자 수동 조정 플래그
self.columns_manually_resized = False
def on_section_resized(self, index, old_size, new_size):
# 사용자가 열 너비를 수동으로 조정했음을 표시
self.columns_manually_resized = True
def resizeEvent(self, event):
super().resizeEvent(event)
# 테이블이 로드되었을 때만 실행하고, 사용자가 수동으로 조정하지 않았을 때만 자동 조절
if self.ui.tableWidget.columnCount() > 0 and not self.columns_manually_resized:
# 자동 열 너비 조절 코드...
문제 3: 특정 열이 너무 좁아지는 문제
특정 열이 너무 좁아지는 것을 방지하기 위해 최소 너비를 설정할 수 있습니다:
def make_columns_interactive(self):
header = self.ui.tableWidget.horizontalHeader()
# 현재 각 열의 너비를 저장
column_widths = []
for i in range(self.ui.tableWidget.columnCount()):
column_widths.append(self.ui.tableWidget.columnWidth(i))
# 모든 열을 Interactive 모드로 변경하고 최소 너비 설정
for i in range(self.ui.tableWidget.columnCount()):
header.setSectionResizeMode(i, QHeaderView.Interactive)
header.setMinimumSectionSize(50) # 최소 너비를 50px로 설정
# 저장해둔 너비를 다시 적용
for i, width in enumerate(column_widths):
self.ui.tableWidget.setColumnWidth(i, max(width, 50)) # 최소 50px 보장
문제 4: 테이블 초기화 시 이전 설정이 유지되는 문제
테이블을 새로 초기화할 때 이전 설정이 유지되는 문제를 해결하려면:
def reset_table(self):
# 테이블 초기화
self.ui.tableWidget.setRowCount(0)
self.ui.tableWidget.setColumnCount(0)
# 수동 조정 플래그 초기화
self.columns_manually_resized = False
# 열 비율 정보 초기화
if hasattr(self, 'column_ratios'):
del self.column_ratios
문제 5: 특정 열만 ResizeToContents로 설정하기
일부 열은 내용에 맞게 자동 조절되고, 나머지는 Interactive 모드로 설정하고 싶을 때:
def make_columns_interactive(self):
header = self.ui.tableWidget.horizontalHeader()
# 현재 각 열의 너비를 저장
column_widths = []
for i in range(self.ui.tableWidget.columnCount()):
column_widths.append(self.ui.tableWidget.columnWidth(i))
# 열별로 다른 ResizeMode 설정
for i in range(self.ui.tableWidget.columnCount()):
if i == 2: # 세 번째 열은 내용에 맞게 자동 조절
header.setSectionResizeMode(i, QHeaderView.ResizeToContents)
else: # 나머지 열은 Interactive 모드
header.setSectionResizeMode(i, QHeaderView.Interactive)
self.ui.tableWidget.setColumnWidth(i, column_widths[i])
마무리
이 글에서는 PySide6의 QTableWidget에서
초기에는 테이블 너비에 꽉 차게 열을 표시하면서도
이후에는 사용자가 마우스로 열 너비를 자유롭게 조절할 수 있도록 하는 방법을 알아보았습니다.
핵심 접근법은 다음과 같습니다:
1. 처음에는 QHeaderView.Stretch 모드를 사용하여 테이블 전체 너비에 맞게 열을 분배
2. QTimer를 사용하여 약간의 지연 후에 각 열의 현재 너비를 저장
3. 모든 열을 QHeaderView.Interactive 모드로 변경
4. 저장해둔 너비를 다시 적용하여 레이아웃이 변경되지 않게 함
이 방법을 사용하면 사용자 경험을 향상시키면서도 개발자가 원하는 대로 테이블의 초기 레이아웃을 설정할 수 있습니다.
1. QHeaderView의 ResizeMode는 열 너비 조절 방식을 결정합니다:
- Interactive: 사용자가 마우스로 열 너비를 조절할 수 있음
- Stretch: 테이블 전체 너비에 맞게 열이 자동으로 분배됨
- ResizeToContents: 내용에 맞게 열 너비가 자동으로 조절됨
- Fixed: 열 너비가 고정됨
2. 두 가지 모드의 장점을 모두 활용하는 방법:
- 처음에는 Stretch 모드로 시작
- 지연 후 Interactive 모드로 전환하면서 너비 유지
- QTimer.singleShot을 사용하여 지연 구현
[ 파이썬 관련 블로그 글 목록 ]
파이썬(Python) 블로그 목록
.