208 lines
5.8 KiB
Python
208 lines
5.8 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
토글 스위치 모듈
|
|
iOS 스타일의 토글 스위치 위젯을 정의합니다.
|
|
"""
|
|
|
|
from PySide6.QtWidgets import QWidget, QHBoxLayout, QLabel
|
|
from PySide6.QtCore import Qt, Signal, Property, QPropertyAnimation, QEasingCurve, QRectF
|
|
from PySide6.QtGui import QPainter, QColor, QBrush, QPen, QFont
|
|
|
|
from core.config import ConfigManager
|
|
from core.logger import get_logger
|
|
|
|
logger = get_logger(__name__)
|
|
|
|
|
|
class ToggleSwitch(QWidget):
|
|
"""
|
|
토글 스위치 위젯
|
|
|
|
iOS 스타일의 슬라이딩 토글 스위치입니다.
|
|
부드러운 애니메이션 효과를 제공합니다.
|
|
|
|
Signals:
|
|
toggled: 토글 상태 변경 시그널 (bool)
|
|
|
|
Examples:
|
|
>>> switch = ToggleSwitch(initial_state=True)
|
|
>>> switch.toggled.connect(self.on_toggle)
|
|
"""
|
|
|
|
toggled = Signal(bool)
|
|
|
|
def __init__(
|
|
self,
|
|
parent=None,
|
|
initial_state: bool = False,
|
|
width: int = 50,
|
|
height: int = 26,
|
|
on_color: str = "#22c55e",
|
|
off_color: str = "#64748b"
|
|
):
|
|
super().__init__(parent)
|
|
|
|
self.config = ConfigManager()
|
|
self._is_on = initial_state
|
|
self._width = width
|
|
self._height = height
|
|
self._on_color = on_color
|
|
self._off_color = off_color
|
|
|
|
# 핸들 위치 (애니메이션용)
|
|
self._handle_position = width - height + 4 if initial_state else 4
|
|
|
|
# 크기 설정
|
|
self.setFixedSize(width, height)
|
|
self.setCursor(Qt.PointingHandCursor)
|
|
|
|
# 애니메이션
|
|
self._animation = QPropertyAnimation(self, b"handle_position")
|
|
self._animation.setDuration(150)
|
|
self._animation.setEasingCurve(QEasingCurve.InOutQuad)
|
|
|
|
def get_handle_position(self) -> float:
|
|
"""핸들 위치 반환"""
|
|
return self._handle_position
|
|
|
|
def set_handle_position(self, value: float):
|
|
"""핸들 위치 설정"""
|
|
self._handle_position = value
|
|
self.update()
|
|
|
|
handle_position = Property(float, get_handle_position, set_handle_position)
|
|
|
|
def paintEvent(self, event):
|
|
"""페인트 이벤트"""
|
|
painter = QPainter(self)
|
|
painter.setRenderHint(QPainter.Antialiasing)
|
|
|
|
# 배경 (라운드 렉트)
|
|
if self._is_on:
|
|
bg_color = QColor(self._on_color)
|
|
else:
|
|
bg_color = QColor(self._off_color)
|
|
|
|
painter.setBrush(QBrush(bg_color))
|
|
painter.setPen(Qt.NoPen)
|
|
painter.drawRoundedRect(
|
|
0, 0, self._width, self._height,
|
|
self._height // 2, self._height // 2
|
|
)
|
|
|
|
# 핸들 (원)
|
|
handle_size = self._height - 8
|
|
painter.setBrush(QBrush(QColor("#ffffff")))
|
|
painter.drawEllipse(
|
|
int(self._handle_position),
|
|
4,
|
|
handle_size,
|
|
handle_size
|
|
)
|
|
|
|
def mousePressEvent(self, event):
|
|
"""마우스 클릭 이벤트"""
|
|
if event.button() == Qt.LeftButton:
|
|
self.toggle()
|
|
|
|
def toggle(self):
|
|
"""토글 상태 전환"""
|
|
self._is_on = not self._is_on
|
|
|
|
# 애니메이션 실행
|
|
end_position = self._width - self._height + 4 if self._is_on else 4
|
|
self._animation.setStartValue(self._handle_position)
|
|
self._animation.setEndValue(end_position)
|
|
self._animation.start()
|
|
|
|
self.toggled.emit(self._is_on)
|
|
|
|
@property
|
|
def is_on(self) -> bool:
|
|
"""현재 상태 반환"""
|
|
return self._is_on
|
|
|
|
def set_state(self, state: bool, animate: bool = True):
|
|
"""
|
|
상태 설정
|
|
|
|
Args:
|
|
state: 새로운 상태
|
|
animate: 애니메이션 여부
|
|
"""
|
|
if state == self._is_on:
|
|
return
|
|
|
|
self._is_on = state
|
|
end_position = self._width - self._height + 4 if state else 4
|
|
|
|
if animate:
|
|
self._animation.setStartValue(self._handle_position)
|
|
self._animation.setEndValue(end_position)
|
|
self._animation.start()
|
|
else:
|
|
self._handle_position = end_position
|
|
self.update()
|
|
|
|
|
|
class LabeledToggle(QWidget):
|
|
"""
|
|
라벨이 있는 토글 스위치
|
|
|
|
라벨과 토글 스위치를 함께 제공합니다.
|
|
"""
|
|
|
|
toggled = Signal(bool)
|
|
|
|
def __init__(
|
|
self,
|
|
label: str,
|
|
parent=None,
|
|
initial_state: bool = False,
|
|
label_position: str = "left"
|
|
):
|
|
super().__init__(parent)
|
|
|
|
self.config = ConfigManager()
|
|
|
|
self._setup_ui(label, initial_state, label_position)
|
|
|
|
def _setup_ui(self, label: str, initial_state: bool, label_position: str):
|
|
"""UI 설정"""
|
|
layout = QHBoxLayout(self)
|
|
layout.setContentsMargins(0, 0, 0, 0)
|
|
layout.setSpacing(12)
|
|
|
|
# 라벨
|
|
self.label = QLabel(label)
|
|
self.label.setFont(QFont("GmarketSans", 13))
|
|
|
|
theme = self.config.theme
|
|
text_color = "#f8fafc" if theme == 'dark' else "#1e293b"
|
|
self.label.setStyleSheet(f"color: {text_color};")
|
|
|
|
# 토글 스위치
|
|
self.switch = ToggleSwitch(initial_state=initial_state)
|
|
self.switch.toggled.connect(self.toggled.emit)
|
|
|
|
# 레이아웃
|
|
if label_position == "left":
|
|
layout.addWidget(self.label)
|
|
layout.addStretch()
|
|
layout.addWidget(self.switch)
|
|
else:
|
|
layout.addWidget(self.switch)
|
|
layout.addWidget(self.label)
|
|
layout.addStretch()
|
|
|
|
@property
|
|
def is_on(self) -> bool:
|
|
"""현재 상태 반환"""
|
|
return self.switch.is_on
|
|
|
|
def set_state(self, state: bool):
|
|
"""상태 설정"""
|
|
self.switch.set_state(state)
|
|
|
|
|