# -*- coding: utf-8 -*- """ 커스텀 버튼 모듈 다양한 스타일의 커스텀 버튼을 정의합니다. 이 모듈은 다음 버튼 유형을 제공합니다: - CustomButton: 기본 커스텀 버튼 - IconButton: 아이콘 버튼 - ToggleButton: 토글 버튼 """ from PySide6.QtWidgets import QPushButton, QGraphicsDropShadowEffect from PySide6.QtCore import Qt, Signal, QPropertyAnimation, QEasingCurve, Property from PySide6.QtGui import QColor, QFont, QIcon, QPainter, QLinearGradient from core.config import ConfigManager from core.logger import get_logger logger = get_logger(__name__) class CustomButton(QPushButton): """ 커스텀 스타일 버튼 현대적인 디자인의 커스텀 버튼입니다. 다양한 스타일(primary, secondary, danger, success)을 지원합니다. Attributes: style_type: 버튼 스타일 유형 Examples: >>> btn = CustomButton("저장", style_type="primary") >>> btn.clicked.connect(self.on_save) """ STYLES = { "primary": { "bg": "#3b82f6", "bg_hover": "#2563eb", "bg_pressed": "#1d4ed8", "text": "#ffffff", }, "secondary": { "bg": "#64748b", "bg_hover": "#475569", "bg_pressed": "#334155", "text": "#ffffff", }, "danger": { "bg": "#ef4444", "bg_hover": "#dc2626", "bg_pressed": "#b91c1c", "text": "#ffffff", }, "success": { "bg": "#22c55e", "bg_hover": "#16a34a", "bg_pressed": "#15803d", "text": "#ffffff", }, "warning": { "bg": "#f59e0b", "bg_hover": "#d97706", "bg_pressed": "#b45309", "text": "#ffffff", }, "outline": { "bg": "transparent", "bg_hover": "#f1f5f9", "bg_pressed": "#e2e8f0", "text": "#3b82f6", "border": "#3b82f6", }, "ghost": { "bg": "transparent", "bg_hover": "#f1f5f9", "bg_pressed": "#e2e8f0", "text": "#64748b", }, } def __init__( self, text: str = "", parent=None, style_type: str = "primary", icon: QIcon = None, fixed_width: int = None, fixed_height: int = 40 ): super().__init__(text, parent) self.config = ConfigManager() self.style_type = style_type # 기본 설정 self.setCursor(Qt.PointingHandCursor) self.setFont(QFont("GmarketSans", 13)) if icon: self.setIcon(icon) if fixed_width: self.setFixedWidth(fixed_width) if fixed_height: self.setFixedHeight(fixed_height) self._apply_style() def apply_style(self, style_type: str): """외부에서 동적으로 스타일을 변경할 때 사용""" self.style_type = style_type self._apply_style() def _apply_style(self): """스타일 적용""" style = self.STYLES.get(self.style_type, self.STYLES["primary"]) border = f"border: 2px solid {style.get('border', 'transparent')};" self.setStyleSheet(f""" QPushButton {{ background-color: {style['bg']}; color: {style['text']}; {border} border-radius: 8px; padding: 8px 20px; font-weight: 600; }} QPushButton:hover {{ background-color: {style['bg_hover']}; }} QPushButton:pressed {{ background-color: {style['bg_pressed']}; }} QPushButton:disabled {{ background-color: #94a3b8; color: #cbd5e1; }} """) def set_style_type(self, style_type: str): """스타일 유형 변경""" self.style_type = style_type self._apply_style() class IconButton(QPushButton): """ 아이콘 버튼 아이콘만 표시되는 원형 또는 사각형 버튼입니다. Examples: >>> btn = IconButton("🔄", tooltip="새로고침") >>> btn.clicked.connect(self.on_refresh) """ def __init__( self, icon_text: str = "", parent=None, size: int = 36, circle: bool = True, tooltip: str = "" ): super().__init__(icon_text, parent) self.config = ConfigManager() self._size = size self._circle = circle # 기본 설정 self.setFixedSize(size, size) self.setCursor(Qt.PointingHandCursor) self.setFont(QFont("Segoe UI Emoji", size // 2)) if tooltip: self.setToolTip(tooltip) self._apply_style() def _apply_style(self): """스타일 적용""" theme = self.config.theme radius = self._size // 2 if self._circle else 8 if theme == 'dark': bg = "#334155" bg_hover = "#475569" text_color = "#f8fafc" else: bg = "#e2e8f0" bg_hover = "#cbd5e1" text_color = "#1e293b" self.setStyleSheet(f""" QPushButton {{ background-color: {bg}; color: {text_color}; border: none; border-radius: {radius}px; }} QPushButton:hover {{ background-color: {bg_hover}; }} QPushButton:pressed {{ background-color: {bg}; }} """) class ToggleButton(QPushButton): """ 토글 버튼 켜짐/꺼짐 상태를 전환하는 버튼입니다. Signals: toggled_signal: 토글 상태 변경 시그널 (bool) Examples: >>> btn = ToggleButton("알림", initial_state=True) >>> btn.toggled_signal.connect(self.on_toggle) """ toggled_signal = Signal(bool) def __init__( self, text: str = "", parent=None, initial_state: bool = False, on_text: str = None, off_text: str = None ): super().__init__(text, parent) self.config = ConfigManager() self._is_on = initial_state self._on_text = on_text or text self._off_text = off_text or text # 기본 설정 self.setCheckable(True) self.setChecked(initial_state) self.setCursor(Qt.PointingHandCursor) self.setFont(QFont("GmarketSans", 13)) self.setFixedHeight(40) # 시그널 연결 self.clicked.connect(self._on_clicked) self._update_state() def _on_clicked(self): """클릭 이벤트""" self._is_on = self.isChecked() self._update_state() self.toggled_signal.emit(self._is_on) def _update_state(self): """상태 업데이트""" self.setText(self._on_text if self._is_on else self._off_text) self._apply_style() def _apply_style(self): """스타일 적용""" if self._is_on: bg = "#22c55e" bg_hover = "#16a34a" else: bg = "#64748b" bg_hover = "#475569" self.setStyleSheet(f""" QPushButton {{ background-color: {bg}; color: white; border: none; border-radius: 8px; padding: 8px 20px; font-weight: 600; }} QPushButton:hover {{ background-color: {bg_hover}; }} """) @property def is_on(self) -> bool: """현재 상태 반환""" return self._is_on def set_state(self, state: bool): """상태 설정""" self._is_on = state self.setChecked(state) self._update_state() class GradientButton(QPushButton): """ 그라데이션 버튼 배경에 그라데이션 효과가 있는 버튼입니다. """ def __init__( self, text: str = "", parent=None, start_color: str = "#3b82f6", end_color: str = "#8b5cf6" ): super().__init__(text, parent) self.start_color = start_color self.end_color = end_color # 기본 설정 self.setCursor(Qt.PointingHandCursor) self.setFont(QFont("GmarketSans", 13, QFont.Bold)) self.setFixedHeight(44) self._apply_style() def _apply_style(self): """스타일 적용""" self.setStyleSheet(f""" QPushButton {{ background: qlineargradient( x1:0, y1:0, x2:1, y2:0, stop:0 {self.start_color}, stop:1 {self.end_color} ); color: white; border: none; border-radius: 10px; padding: 10px 24px; font-weight: bold; }} QPushButton:hover {{ background: qlineargradient( x1:0, y1:0, x2:1, y2:0, stop:0 {self._darken(self.start_color)}, stop:1 {self._darken(self.end_color)} ); }} QPushButton:pressed {{ background: qlineargradient( x1:0, y1:0, x2:1, y2:0, stop:0 {self._darken(self.start_color, 0.2)}, stop:1 {self._darken(self.end_color, 0.2)} ); }} """) @staticmethod def _darken(hex_color: str, amount: float = 0.1) -> str: """색상을 어둡게""" color = QColor(hex_color) h, s, l, a = color.getHslF() l = max(0, l - amount) color.setHslF(h, s, l, a) return color.name()