PYTHON GUI

[ PySide6 ] QTableWidget 열 너비 자동 조절 후 마우스로 수동 조절하는 방법

나루하루001 2025. 5. 24. 23:15
반응형
목차
  1. QTableWidget 열 너비 조절의 기본 문제
  2. QHeaderView의 ResizeMode 이해하기
  3. 열 너비 자동 조절과 수동 조절 동시에 구현하기
  4. 코드 구현 및 상세 설명
  5. 다양한 활용 예시
  6. 자주 발생하는 문제와 해결 방법
  7. 마무리



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: 열 내용에 맞게 자동으로 너비가 조절됩니다.

문제 상황: Stretch 모드는 테이블 전체 너비를 꽉 채우는 장점이 있지만 사용자가 마우스로 열 너비를 조절할 수 없습니다. 반면 Interactive 모드는 사용자가 마우스로 열 너비를 조절할 수 있지만, 처음 데이터 로드 시 테이블 전체 너비를 자동으로 채우지 않습니다. 이 두 가지 기능을 어떻게 동시에 구현할 수 있을까요?

 

열 너비 자동 조절과 수동 조절 동시에 구현하기


이 문제를 해결하기 위한 핵심 아이디어는 다음과 같은 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 모드가 중요한가?

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) 블로그 목록

반응형