# -*- coding: utf-8 -*- """ 편성 정보 팝업 모듈 편성번호 위에 마우스를 올리면 표시되는 최근 고장 목록 팝업입니다. """ from typing import List from datetime import date from PySide6.QtCore import Qt, Signal, QPoint from PySide6.QtWidgets import QVBoxLayout, QLabel, QWidget, QScrollArea from PySide6.QtGui import QFont from .popup_widget import PopupWidget from database.crud import CRUDManager from database.models import Fault from core.config import ConfigManager from core.signals import GlobalSignals from core.logger import get_logger logger = get_logger(__name__) class TrainInfoPopup(PopupWidget): """ 편성 정보 팝업 편성번호 필드 위에 마우스를 올리면 해당 편성의 최근 고장 목록을 표시합니다. Signals: detail_requested: 상세 보기 요청 시그널 (편성번호) Examples: >>> popup = TrainInfoPopup() >>> popup.show_train_info("001", QPoint(100, 100)) """ detail_requested = Signal(str) # 편성번호 def __init__(self, parent=None): super().__init__( parent, title="", width=320, auto_hide=True, auto_hide_delay=5000 ) self.crud = CRUDManager() self.signals = GlobalSignals() self._current_train = "" # 시그널 연결 self.signals.show_train_popup.connect(self._on_show_popup) self.signals.hide_train_popup.connect(self.hide_popup) def show_train_info(self, train_number: str, position: QPoint): """ 편성 정보 팝업 표시 Args: train_number: 편성번호 position: 표시 위치 """ if not train_number: return self._current_train = train_number # 컨텐츠 초기화 self.clear() # 제목 업데이트 if hasattr(self, 'title_label'): self.title_label.setText(f"편성 {train_number} 최근 고장") # 최근 고장 목록 조회 faults = self.crud.get_faults_by_train(train_number, limit=5) if faults: self._add_fault_list(faults) else: self.add_text("최근 고장 기록이 없습니다.", is_secondary=True) # 액션 버튼 self.action_layout.addStretch() self.add_action("자세히 보기", "detail", primary=True) # 팝업 표시 self.show_at(position) def _add_fault_list(self, faults: List[Fault]): """ 고장 목록 추가 Args: faults: 고장 리스트 """ for fault in faults: self._add_fault_item(fault) def _add_fault_item(self, fault: Fault): """ 고장 항목 추가 Args: fault: 고장 객체 """ item = QWidget() item_layout = QVBoxLayout(item) item_layout.setContentsMargins(8, 8, 8, 8) item_layout.setSpacing(4) theme = self.config.theme if theme == 'dark': bg = "#334155" text = "#f8fafc" secondary = "#94a3b8" else: bg = "#f1f5f9" text = "#1e293b" secondary = "#64748b" item.setStyleSheet(f""" background-color: {bg}; border-radius: 8px; """) # 날짜 + 호차 date_str = "" if fault.occurrence_date: if isinstance(fault.occurrence_date, date): date_str = fault.occurrence_date.strftime("%Y-%m-%d") else: date_str = str(fault.occurrence_date)[:10] header = QLabel(f"{date_str} | {fault.car_number or ''}호차") header.setFont(QFont("GmarketSans", 11)) header.setStyleSheet(f"color: {secondary}; background: transparent;") item_layout.addWidget(header) # 고장 내용 content = fault.fault_content or "" if len(content) > 40: content = content[:40] + "..." content_label = QLabel(content) content_label.setFont(QFont("GmarketSans", 12)) content_label.setWordWrap(True) content_label.setStyleSheet(f"color: {text}; background: transparent;") item_layout.addWidget(content_label) # 장치분류 + 고장코드 if fault.device_category or fault.fault_code: tag_text = f"{fault.device_category or ''} {fault.fault_code or ''}".strip() tag_label = QLabel(tag_text) tag_label.setFont(QFont("GmarketSans", 10)) tag_label.setStyleSheet(f"color: {secondary}; background: transparent;") item_layout.addWidget(tag_label) self.content_layout.addWidget(item) def _on_show_popup(self, train_number: str, x: int, y: int): """시그널 핸들러: 팝업 표시""" self.show_train_info(train_number, QPoint(x, y)) def _on_action_clicked(self, action_id: str): """액션 버튼 클릭""" if action_id == "detail": self.detail_requested.emit(self._current_train) super()._on_action_clicked(action_id) class TrainInfoMixin: """ 편성 정보 팝업 믹스인 편성번호 필드가 있는 위젯에서 사용할 수 있는 믹스인입니다. 마우스 호버 이벤트를 처리하여 팝업을 표시합니다. Examples: >>> class MyTable(BaseTable, TrainInfoMixin): ... def __init__(self): ... super().__init__() ... self.setup_train_popup() """ def setup_train_popup(self): """편성 팝업 초기화""" self._train_popup = TrainInfoPopup(self) self._train_popup.detail_requested.connect(self.on_train_detail_requested) def show_train_popup(self, train_number: str, position: QPoint): """ 편성 팝업 표시 Args: train_number: 편성번호 position: 표시 위치 """ if hasattr(self, '_train_popup'): self._train_popup.show_train_info(train_number, position) def hide_train_popup(self): """편성 팝업 숨기기""" if hasattr(self, '_train_popup'): self._train_popup.hide_popup() def on_train_detail_requested(self, train_number: str): """ 편성 상세 정보 요청 (오버라이드 가능) Args: train_number: 편성번호 """ logger.info(f"편성 상세 정보 요청: {train_number}")