handOver2/ui/base/base_widget.py

299 lines
8.0 KiB
Python

# -*- coding: utf-8 -*-
"""
기본 위젯 클래스 모듈
모든 커스텀 위젯의 기반 클래스를 정의합니다.
이 모듈은 다음 기능을 제공합니다:
- 공통 스타일링
- 이벤트 처리
- 로깅 통합
- 시그널 연결
"""
from PySide6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QGraphicsDropShadowEffect
from PySide6.QtCore import Qt, Signal, QPropertyAnimation, QEasingCurve
from PySide6.QtGui import QColor, QFont
from core.logger import get_logger
from core.config import ConfigManager
from core.signals import GlobalSignals
# 로거 설정
logger = get_logger(__name__)
class BaseWidget(QWidget):
"""
기본 위젯 클래스
모든 커스텀 위젯이 상속받는 기반 클래스입니다.
공통 기능과 스타일링을 제공합니다.
Attributes:
config: 설정 관리자
signals: 전역 시그널
Examples:
>>> class MyWidget(BaseWidget):
... def __init__(self, parent=None):
... super().__init__(parent)
... self.setup_ui()
"""
# 커스텀 시그널
widget_ready = Signal()
widget_error = Signal(str)
def __init__(self, parent=None):
"""
위젯 초기화
Args:
parent: 부모 위젯
"""
super().__init__(parent)
# 공통 객체 초기화
self.config = ConfigManager()
self.signals = GlobalSignals()
# 기본 설정
self._setup_base()
logger.debug(f"{self.__class__.__name__} 초기화")
def _setup_base(self):
"""기본 설정"""
# 폰트 설정
self._apply_font()
# 기본 속성
self.setAttribute(Qt.WA_StyledBackground, True)
def _apply_font(self):
"""폰트 적용"""
font = QFont(self.config.get('app', 'font_family', 'GmarketSans'))
font.setPixelSize(self.config.get('app', 'font_size', 14))
self.setFont(font)
def add_shadow(self, blur_radius: int = 20, offset: tuple = (0, 4), color: str = "#00000040"):
"""
위젯에 그림자 효과를 추가합니다.
Args:
blur_radius: 블러 반경
offset: 그림자 오프셋 (x, y)
color: 그림자 색상
"""
shadow = QGraphicsDropShadowEffect(self)
shadow.setBlurRadius(blur_radius)
shadow.setOffset(*offset)
shadow.setColor(QColor(color))
self.setGraphicsEffect(shadow)
def create_fade_animation(
self,
start_value: float = 0.0,
end_value: float = 1.0,
duration: int = 300
) -> QPropertyAnimation:
"""
페이드 애니메이션을 생성합니다.
Args:
start_value: 시작 투명도
end_value: 종료 투명도
duration: 애니메이션 시간 (ms)
Returns:
QPropertyAnimation 객체
"""
from PySide6.QtWidgets import QGraphicsOpacityEffect
opacity_effect = QGraphicsOpacityEffect(self)
self.setGraphicsEffect(opacity_effect)
animation = QPropertyAnimation(opacity_effect, b"opacity")
animation.setDuration(duration)
animation.setStartValue(start_value)
animation.setEndValue(end_value)
animation.setEasingCurve(QEasingCurve.InOutQuad)
return animation
def fade_in(self, duration: int = 300):
"""페이드 인 효과"""
anim = self.create_fade_animation(0.0, 1.0, duration)
anim.start()
def fade_out(self, duration: int = 300):
"""페이드 아웃 효과"""
anim = self.create_fade_animation(1.0, 0.0, duration)
anim.start()
def apply_theme(self, theme: str = None):
"""
테마를 적용합니다.
Args:
theme: 테마 이름 ('dark' 또는 'light')
"""
if theme is None:
theme = self.config.theme
# 자식 클래스에서 오버라이드
pass
def show_error(self, message: str):
"""
에러 메시지를 표시합니다.
Args:
message: 에러 메시지
"""
logger.error(f"{self.__class__.__name__}: {message}")
self.widget_error.emit(message)
self.signals.error_occurred.emit(self.__class__.__name__, message)
def show_status(self, message: str, timeout: int = 3000):
"""
상태 메시지를 표시합니다.
Args:
message: 상태 메시지
timeout: 표시 시간 (ms)
"""
self.signals.status_message.emit(message, timeout)
class CardWidget(BaseWidget):
"""
카드 스타일 위젯
둥근 모서리와 그림자가 있는 카드 형태의 위젯입니다.
"""
def __init__(self, parent=None, padding: int = 16, radius: int = 12):
"""
카드 위젯 초기화
Args:
parent: 부모 위젯
padding: 내부 여백
radius: 모서리 반경
"""
super().__init__(parent)
self.padding = padding
self.radius = radius
self._setup_card()
def _setup_card(self):
"""카드 스타일 설정"""
# 레이아웃 설정
self.layout = QVBoxLayout(self)
self.layout.setContentsMargins(
self.padding, self.padding,
self.padding, self.padding
)
self.layout.setSpacing(8)
# 그림자 추가
self.add_shadow()
# 스타일 적용
self._apply_card_style()
def _apply_card_style(self):
"""카드 스타일시트 적용"""
theme = self.config.theme
if theme == 'dark':
bg_color = "#1e293b"
border_color = "#334155"
else:
bg_color = "#ffffff"
border_color = "#e2e8f0"
self.setStyleSheet(f"""
CardWidget {{
background-color: {bg_color};
border: 1px solid {border_color};
border-radius: {self.radius}px;
}}
""")
class ContainerWidget(BaseWidget):
"""
컨테이너 위젯
다른 위젯들을 담는 컨테이너 역할을 합니다.
수직 또는 수평 레이아웃을 지원합니다.
"""
def __init__(
self,
parent=None,
orientation: str = 'vertical',
spacing: int = 8,
margins: tuple = (0, 0, 0, 0)
):
"""
컨테이너 위젯 초기화
Args:
parent: 부모 위젯
orientation: 레이아웃 방향 ('vertical' 또는 'horizontal')
spacing: 위젯 간 간격
margins: 여백 (left, top, right, bottom)
"""
super().__init__(parent)
self.orientation = orientation
self.spacing = spacing
self.margins = margins
self._setup_container()
def _setup_container(self):
"""컨테이너 설정"""
if self.orientation == 'vertical':
self.layout = QVBoxLayout(self)
else:
self.layout = QHBoxLayout(self)
self.layout.setSpacing(self.spacing)
self.layout.setContentsMargins(*self.margins)
def add_widget(self, widget: QWidget, stretch: int = 0):
"""
위젯을 추가합니다.
Args:
widget: 추가할 위젯
stretch: 늘어남 비율
"""
self.layout.addWidget(widget, stretch)
def add_spacing(self, size: int):
"""
간격을 추가합니다.
Args:
size: 간격 크기
"""
self.layout.addSpacing(size)
def add_stretch(self, stretch: int = 1):
"""
늘어남 영역을 추가합니다.
Args:
stretch: 늘어남 비율
"""
self.layout.addStretch(stretch)