# -*- 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)