PySide를 사용하여 QListWidget과 Matplotlib 연동하기
이 글에서는 PySide6를 사용하여 QListWidget
과 Matplotlib
를 이용하여
Image viewer를 만드는 방법을 설명함.
사용자는 디렉토리에서 PNG 파일을 선택하고
선택한 이미지를 Matplotlib를 사용하여 표시할 수 있음.
예제 코드를 통해 이를 구현하는 방법을 단계별로 살펴보겠음.
프로젝트 설정
먼저 PySide6와 Matplotlib를 설치해야 함. 이를 위해 아래의 명령어를 실행하기 바람.
pip install PySide6 matplotlib
주요 클래스 및 메서드 소개
ImageCanvas 클래스
ImageCanvas
클래스는 Matplotlib의 FigureCanvasQTAgg
를 상속하여 이미지를 표시하는 기능을 제공함.
이 클래스는 PNG 파일을 읽어 Matplotlib 축에 표시함.
class ImageCanvas(FigureCanvas):
def __init__(self, parent=None):
# Matplotlib Figure 객체 생성
self.fig = Figure()
# Figure에 축(axes)을 추가
self.ax = self.fig.add_subplot(111)
super().__init__(self.fig)
self.setParent(parent)
self.setStyleSheet("background-color: #2f2f2f;")
# 축을 숨김
self.ax.axis('off')
# Figure의 여백을 조정하여 이미지가 전체 영역을 차지하도록 설정
self.fig.subplots_adjust(
left=0, right=1,
top=1, bottom=0
)
def display_image(self, image_path):
# 축을 지움
self.ax.clear()
# 이미지 파일을 읽어서 축에 표시
img = mpimg.imread(image_path)
self.ax.imshow(img)
# 축을 숨김
self.ax.axis('off')
# Figure의 여백을 조정하여 이미지가 전체 영역을 차지하도록 설정
self.fig.subplots_adjust(
left=0, right=1,
top=1, bottom=0
)
# 그림을 다시 그림
self.draw()
FigureCanvas 통해 Matplotlib 을 PySide에서 이용하는 보다 자세한 방법 설명은 다음 URL을 참고할 것.
2024.04.29 - [Python/PySide PyQt] - [PySide6] matplotlib 이용하기
MainWindow 클래스
MainWindow
클래스는 응용 프로그램의 main window를 구성함.
여기에는 QListWidget
과 Matplotlib의 ImageCanvas
, 그리고 기타 UI 요소들이 포함됨.
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("PNG Viewer")
# 메인 레이아웃 설정
main_layout = QHBoxLayout()
central_widget = QWidget()
central_widget.setLayout(main_layout)
self.setCentralWidget(central_widget)
right_layout = QVBoxLayout()
right_widget = QWidget()
right_widget.setLayout(right_layout)
# QListWidget 생성 및 설정
self.list_widget = QListWidget()
self.list_widget.itemClicked.connect(self.on_item_clicked)
main_layout.addWidget(self.list_widget)
# Matplotlib FigureCanvas 생성 및 설정
self.canvas = ImageCanvas(self)
# NavigationToolbar 생성 및 설정
self.nav_toolbar = NavigationToolbar(self.canvas, self)
right_layout.addWidget(self.nav_toolbar)
right_layout.addWidget(self.canvas)
main_layout.addWidget(right_widget)
# StatusBar 생성
self.status_bar = QStatusBar()
self.setStatusBar(self.status_bar)
# 메뉴바 설정
menubar = self.menuBar()
file_menu = menubar.addMenu("Select Img Dir")
menubar.setNativeMenuBar(False)
# 디렉토리 선택 액션 추가
open_action = QAction("Open Directory", self)
open_action.triggered.connect(self.open_directory)
file_menu.addAction(open_action)
self.show()
def open_directory(self):
# 디렉토리 선택 다이얼로그 열기
directory = QFileDialog.getExistingDirectory(self, "Select Directory")
if directory:
self.list_widget.clear()
# 디렉토리의 png 파일 목록 가져오기
png_files = [f for f in os.listdir(directory) if f.endswith('.png')]
for png_file in png_files:
# QListWidgetItem 생성
item = QListWidgetItem(png_file)
# 파일 경로를 Qt.UserRole에 저장
item.setData(Qt.UserRole, os.path.join(directory, png_file))
# QListWidget에 아이템 추가
self.list_widget.addItem(item)
def on_item_clicked(self, item):
# 선택된 아이템의 파일 경로를 가져와서 이미지 표시
file_path = item.data(Qt.UserRole)
self.canvas.display_image(file_path)
self.status_bar.showMessage(file_path)
주요 기능 설명
- QListWidget 설정:
QListWidget
는 사용자가 PNG 파일을 선택할 수 있는 리스트를 제공함
- 디렉토리 열기:
- 메뉴에서 "Open Directory" 옵션을 선택하면 파일 다이얼로그가 열리고,
- 사용자가 선택한 디렉토리의 PNG 파일 목록이
QListWidget
에 표시됨
- 이미지 표시:
- 사용자가 리스트에서 파일을 선택하면,
- 해당 파일의 경로가
ImageCanvas
로 전달되어 이미지가 표시됨
- 상태바:
- 선택한 파일의 경로가 상태바에 표시됨
QListWidgetItem 사용법과 Qt.UserRole의 의미
- QListWidgetItem:
QListWidgetItem
은QListWidget
에 표시되는 각 항목을 나타내는 클래스임.- 각 항목은 텍스트와 데이터를 가질 수 있음. 텍스트는 항목의 표시 이름을 나타내고, 데이터는 항목에 관련된 추가 정보를 저장함.
- item.setData(Qt.UserRole, value):
setData
메서드는 항목에 데이터를 설정함.Qt.UserRole
은 사용자가 정의한 데이터를 저장할 수 있는 역할(role)을 나타냄.Qt.UserRole
을 사용하면 항목과 관련된 추가 정보를 저장할 수 있음.- 예를 들어, 파일 경로를 저장하여 항목이 클릭될 때 해당 경로를 참조할 수 있음.
전체 소스 코드는 다음과 같음.
import sys
import os
from PySide6.QtWidgets import (
QApplication, QMainWindow, QListWidget,
QListWidgetItem,
QHBoxLayout, QVBoxLayout, QWidget, QFileDialog, QMenuBar,
QStatusBar)
from PySide6.QtCore import Qt
from PySide6.QtGui import QAction
from matplotlib.figure import Figure
from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qtagg import NavigationToolbar2QT as NavigationToolbar
import matplotlib.image as mpimg
class ImageCanvas(FigureCanvas):
def __init__(self, parent=None):
self.fig = Figure()
self.ax = self.fig.add_subplot(111)
super().__init__(self.fig)
self.setParent(parent)
self.setStyleSheet("background-color: #2f2f2f;")
self.ax.axis('off') # Hide the axis
self.fig.subplots_adjust(
left=0, right=1,
top=1, bottom=0
)
def display_image(self, image_path):
self.ax.clear()
img = mpimg.imread(image_path)
self.ax.imshow(img)
self.ax.axis('off') # Hide the axis
self.fig.subplots_adjust(
left=0, right=1,
top=1, bottom=0
)
self.draw()
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("PNG Viewer")
# 메인 레이아웃 설정
main_layout = QHBoxLayout()
central_widget = QWidget()
central_widget.setLayout(main_layout)
self.setCentralWidget(central_widget)
right_layout = QVBoxLayout()
right_widget = QWidget()
right_widget.setLayout(right_layout)
# QListWidget 생성 및 설정
self.list_widget = QListWidget()
self.list_widget.itemClicked.connect(self.on_item_clicked)
main_layout.addWidget(self.list_widget)
# Matplotlib FigureCanvas 생성 및 설정
self.canvas = ImageCanvas(self)
# NavigationToolbar 생성 및 설정.
self.nav_toolbar = NavigationToolbar(self.canvas, self)
right_layout.addWidget(self.nav_toolbar)
right_layout.addWidget(self.canvas)
main_layout.addWidget(right_widget)
# StatusBar 생성
self.status_bar = QStatusBar()
self.setStatusBar(self.status_bar)
# 메뉴바 설정
menubar = self.menuBar()
file_menu = menubar.addMenu("Select Img Dir")
menubar.setNativeMenuBar(False)
# 디렉토리 선택 액션 추가
open_action = QAction("Open Directory", self)
open_action.triggered.connect(self.open_directory)
file_menu.addAction(open_action)
self.show()
def open_directory(self):
# 디렉토리 선택 다이얼로그 열기
directory = QFileDialog.getExistingDirectory(self, "Select Directory")
if directory:
self.list_widget.clear()
# 디렉토리의 png 파일 목록 가져오기
png_files = [f for f in os.listdir(directory) if f.endswith('.png')]
for png_file in png_files:
item = QListWidgetItem(png_file)
item.setData(Qt.UserRole, os.path.join(directory, png_file))
self.list_widget.addItem(item)
def on_item_clicked(self, item):
# 선택된 아이템의 파일 경로를 가져와서 이미지 표시
file_path = item.data(Qt.UserRole)
self.canvas.display_image(file_path)
self.status_bar.showMessage(file_path)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
sys.exit(app.exec())
참고: Qt.UserRole 외의 argument 사용 예시
QListWidgetItem의 데이터는 setData 메서드를 통해 설정하고, data 메서드를 통해 가져올 수 있음.
이 때 설정 가능한 Data의 종류는 다음과 같음.
Qt.DisplayRole
기본적으로 항목에 표시되는 텍스트를 설정함.
item.setData(Qt.DisplayRole, "파일 이름")
Qt.ToolTipRole
항목에 마우스를 올렸을 때 표시되는 툴팁 텍스트를 설정함.
item.setData(Qt.ToolTipRole, "툴팁 텍스트")
Qt.StatusTipRole
상태바에 표시되는 텍스트를 설정함.
signal 처리가 필요함. 아래 예제 참고
item.setData(Qt.StatusTipRole, "상태바 텍스트")
...
# QListWidget에 항목 위에 마우스를 올렸을 때 StatusTipRole 데이터를 상태바에 표시
self.list_widget.itemEntered.connect(self.show_status_tip)
...
def show_status_tip(self, item):
# 항목의 StatusTipRole 데이터를 상태바에 표시
status_tip = item.data(Qt.StatusTipRole)
self.status_bar.showMessage(status_tip)
Qt.WhatsThisRole
항목에 대해 "What's This?" 도움말 텍스트를 설정함.
별도로 "What's This?" 모드로 들어가는 처리 필요 (아래 예제 코드 확인)
from PySide6.QtWidgets import QWhatsThis
item.setData(Qt.WhatsThisRole, "이 항목에 대한 설명")
...
def toggle_whats_this_mode(self):
# "What's This?" 모드를 토글함. 버튼 등에 연결됨.
if QWhatsThis.inWhatsThisMode():
QWhatsThis.leaveWhatsThisMode()
else:
QWhatsThis.enterWhatsThisMode()
Qt.FontRole
항목의 텍스트 폰트를 설정함.
from PySide6.QtGui import QFont
font = QFont("Arial", 12, QFont.Bold)
item.setData(Qt.FontRole, font)
Qt.TextAlignmentRole
항목의 텍스트 정렬을 설정함.
from PySide6.QtCore import Qt
item.setData(Qt.TextAlignmentRole, Qt.AlignCenter)
Qt.BackgroundRole
항목의 배경색을 설정함.
from PySide6.QtGui import QColor
item.setData(Qt.BackgroundRole, QColor(Qt.yellow))
Qt.ForegroundRole
항목의 텍스트 색상을 설정함.
from PySide6.QtGui import QColor
item.setData(Qt.ForegroundRole, QColor(Qt.red))
Qt.CheckStateRole
항목의 체크 상태를 설정함 (체크박스가 있는 경우).
from PySide6.QtCore import Qt
item.setData(Qt.CheckStateRole, Qt.Checked)
Qt.DecorationRole
항목의 아이콘 또는 이미지를 설정함.
from PySide6.QtGui import QIcon
icon = QIcon("path/to/icon.png")
item.setData(Qt.DecorationRole, icon)
전체 예제 코드
아래는 다양한 역할들을 사용하는 전체 예제 코드임.
상태바를 추가하여 각 역할의 효과를 확인할 수 있음.
import sys
from PySide6.QtWidgets import (
QApplication, QMainWindow, QListWidget,
QListWidgetItem, QVBoxLayout, QWidget,
QStatusBar, QPushButton, QWhatsThis,
)
from PySide6.QtGui import QFont, QColor, QIcon
from PySide6.QtCore import Qt
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("QListWidget Roles Example")
layout = QVBoxLayout()
central_widget = QWidget()
central_widget.setLayout(layout)
self.setCentralWidget(central_widget)
self.list_widget = QListWidget()
layout.addWidget(self.list_widget)
# StatusBar 생성
self.status_bar = QStatusBar()
self.setStatusBar(self.status_bar)
# "What's This?" 버튼 생성
self.whats_this_button = QPushButton("What's This?")
self.whats_this_button.clicked.connect(self.toggle_whats_this_mode)
layout.addWidget(self.whats_this_button)
# "Edit Item" 버튼 생성
self.edit_button = QPushButton("Edit Item")
self.edit_button.clicked.connect(self.edit_selected_item)
layout.addWidget(self.edit_button)
# QListWidgetItem 생성
item1 = QListWidgetItem("Display Role Text 1")
item1.setData(Qt.DisplayRole, "Updated Display Role Text 1")
# item1.setData(Qt.EditRole, "Edit Role Text 1") #not working
item1.setData(Qt.ToolTipRole, "ToolTip Role Text 1")
item1.setData(Qt.StatusTipRole, "StatusTip Role Text 1")
item1.setData(Qt.WhatsThisRole, "What's This Role Text 1")
font1 = QFont("Arial", 12, QFont.Bold)
item1.setData(Qt.FontRole, font1)
item1.setData(Qt.TextAlignmentRole, Qt.AlignCenter)
background_color1 = QColor(Qt.yellow)
item1.setData(Qt.BackgroundRole, background_color1)
foreground_color1 = QColor(Qt.red)
item1.setData(Qt.ForegroundRole, foreground_color1)
item1.setData(Qt.CheckStateRole, Qt.Checked)
icon1 = QIcon("path/to/icon1.png") # 아이콘 파일 경로
item1.setData(Qt.DecorationRole, icon1)
# QListWidget에 아이템 추가
self.list_widget.addItem(item1)
# 두번째 QListWidgetItem 생성
item2 = QListWidgetItem("Display Role Text 2")
item2.setData(Qt.DisplayRole, "Updated Display Role Text 2")
# item2.setData(Qt.EditRole, "Edit Role Text 2") # not working
item2.setData(Qt.ToolTipRole, "ToolTip Role Text 2")
item2.setData(Qt.StatusTipRole, "StatusTip Role Text 2")
item2.setData(Qt.WhatsThisRole, "What's This Role Text 2")
font2 = QFont("Times New Roman", 10)
font2.setItalic(True)
item2.setData(Qt.FontRole, font2)
item2.setData(Qt.TextAlignmentRole, Qt.AlignRight)
background_color2 = QColor(Qt.green)
item2.setData(Qt.BackgroundRole, background_color2)
foreground_color2 = QColor(Qt.blue)
item2.setData(Qt.ForegroundRole, foreground_color2)
item2.setData(Qt.CheckStateRole, Qt.Unchecked)
icon2 = QIcon("path/to/icon2.png") # 아이콘 파일 경로
item2.setData(Qt.DecorationRole, icon2)
# QListWidget에 아이템 추가
self.list_widget.addItem(item2)
# QListWidget에 항목 위에 마우스를 올렸을 때 StatusTipRole 데이터를 상태바에 표시
self.list_widget.itemEntered.connect(self.show_status_tip)
# QListWidget의 항목에 마우스를 올리기 위해 설정
self.list_widget.setMouseTracking(True)
def toggle_whats_this_mode(self):
# "What's This?" 모드를 토글함
if QWhatsThis.inWhatsThisMode():
QWhatsThis.leaveWhatsThisMode()
else:
QWhatsThis.enterWhatsThisMode()
def show_status_tip(self, item):
# 항목의 StatusTipRole 데이터를 상태바에 표시
status_tip = item.data(Qt.StatusTipRole)
self.status_bar.showMessage(status_tip)
def edit_selected_item(self):
# 선택된 항목을 편집 모드로 바꿈
current_item = self.list_widget.currentItem()
current_item.setFlags(current_item.flags() | Qt.ItemIsEditable) # 편집 가능하도록 설정
if current_item:
self.list_widget.editItem(current_item)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())
이 예제는 다양한 역할들을 사용하여 QListWidgetItem
의 속성을 설정하는 방법을 보여줌.
각 역할은 항목의 다양한 속성들을 제어할 수 있도록 함.
각 역할은 특정 기능을 가지고 있으며, 이를 적절히 활용하면 더욱 풍부한 UI를 만들 수 있음.
'Python > PySide PyQt' 카테고리의 다른 글
[PySide] Ex: Matplotlib 에서 상호작용 기능 구현 (0) | 2024.05.19 |
---|---|
[PySide] FigureCanvas.mpl_connect (0) | 2024.05.19 |
[PySide6] pyside6-uic 사용하기 (0) | 2024.05.07 |
[PySide6] Qt Designer6 (0) | 2024.05.06 |
[PySide6] QUiLoader 를 Qt Designer의 .ui 사용하기. (0) | 2024.05.06 |