handOver2/ui/components/train_info_popup.py

220 lines
6.6 KiB
Python

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