# -*- coding: utf-8 -*- """ 편성 입력 다이얼로그 모듈 일상검수 편성 입력을 위한 다이얼로그입니다. """ from datetime import date from typing import Optional, List, Dict, Any from PySide6.QtWidgets import ( QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QFrame, QScrollArea, QButtonGroup, QTextEdit, QCheckBox, QLineEdit ) from PySide6.QtCore import Qt, Signal from PySide6.QtGui import QFont from ui.base.base_dialog import BaseDialog from ui.components.chips.choice_chip_button import ChoiceChipButton from ui.components.flow_layout import FlowLayout from ui.components.train_selection import TrainSelectionWidget from core.config import ConfigManager from core.logger import get_logger from database.common_db_manager import CommonDatabaseManager logger = get_logger(__name__) class CleaningChip(QPushButton): """ 청소유형 선택 칩 청소유형을 표시하며, 유형에 따라 다른 아이콘을 표시합니다. - 없음: 기본 - 중청소: 파란 네모 □ - 대청소: 빨간 동그라미 ○ """ chip_selected = Signal(str) # cleaning_type def __init__(self, cleaning_type: str, parent=None): super().__init__(parent) self.config = ConfigManager() self.cleaning_type = cleaning_type self._is_selected = False # 아이콘과 텍스트 설정 if cleaning_type == "중청소": self.setText("□ 중청소") self.icon_color = "#3b82f6" # 파란색 elif cleaning_type == "대청소": self.setText("○ 대청소") self.icon_color = "#ef4444" # 빨간색 else: self.setText("없음") self.icon_color = "#64748b" self.setMinimumWidth(80) self.setFixedHeight(36) self.setCursor(Qt.PointingHandCursor) self.setFont(QFont("GmarketSans", 11, QFont.Bold)) self.clicked.connect(self._on_clicked) self._apply_style() def _on_clicked(self): """클릭 이벤트""" self.chip_selected.emit(self.cleaning_type) def set_selected(self, selected: bool): """선택 상태 설정""" self._is_selected = selected self._apply_style() def _apply_style(self): """스타일 적용""" theme = self.config.theme if self._is_selected: self.setStyleSheet(f""" QPushButton {{ background-color: {self.icon_color}; color: white; border: 3px solid {'#1d4ed8' if self.cleaning_type == '중청소' else '#b91c1c' if self.cleaning_type == '대청소' else '#475569'}; border-radius: 8px; padding: 0 12px; font-weight: bold; }} """) else: if theme == 'dark': bg = "#334155" text = "#94a3b8" else: bg = "#e2e8f0" text = "#64748b" self.setStyleSheet(f""" QPushButton {{ background-color: {bg}; color: {text}; border: 2px solid transparent; border-radius: 8px; padding: 0 12px; }} QPushButton:hover {{ background-color: {self.icon_color}; color: white; }} """) class WorkChip(QPushButton): """ 작업여부 선택 칩 작업여부를 표시합니다. - 없음: 기본 - 있음: 노란색 느낌표 """ chip_selected = Signal(bool) # has_work def __init__(self, has_work: bool, label: str, parent=None): super().__init__(parent) self.config = ConfigManager() self.has_work = has_work self.label = label self._is_selected = False # 텍스트 설정 if has_work: self.setText("! 있음") self.icon_color = "#f59e0b" # 노란색 else: self.setText("없음") self.icon_color = "#64748b" self.setMinimumWidth(70) self.setFixedHeight(36) self.setCursor(Qt.PointingHandCursor) self.setFont(QFont("GmarketSans", 11, QFont.Bold)) self.clicked.connect(self._on_clicked) self._apply_style() def _on_clicked(self): """클릭 이벤트""" self.chip_selected.emit(self.has_work) def set_selected(self, selected: bool): """선택 상태 설정""" self._is_selected = selected self._apply_style() def _apply_style(self): """스타일 적용""" theme = self.config.theme if self._is_selected: self.setStyleSheet(f""" QPushButton {{ background-color: {self.icon_color}; color: white; border: 3px solid {'#d97706' if self.has_work else '#475569'}; border-radius: 8px; padding: 0 12px; font-weight: bold; }} """) else: if theme == 'dark': bg = "#334155" text = "#94a3b8" else: bg = "#e2e8f0" text = "#64748b" self.setStyleSheet(f""" QPushButton {{ background-color: {bg}; color: {text}; border: 2px solid transparent; border-radius: 8px; padding: 0 12px; }} QPushButton:hover {{ background-color: {self.icon_color}; color: white; }} """) class TrainInputDialog(BaseDialog): """ 편성 입력 다이얼로그 일상검수 편성 및 청소 유형을 입력합니다. """ def __init__( self, parent=None, shift_type: str = "", slot_number: int = 0, inspection_date: date = None, existing_trains: List[str] = None ): super().__init__( parent, title=f"{shift_type} {slot_number}번 슬롯", width=420, height=700 ) self.db = CommonDatabaseManager() self.shift_type = shift_type self.slot_number = slot_number self.inspection_date = inspection_date or date.today() self.existing_trains = existing_trains or [] self._selected_train_number: str = "" self._selected_cleaning: str = "없음" self._selected_has_work: bool = False self._work_content: str = "" self._is_work_completed: bool = False # self._train_chips: List[TrainChip] = [] # Removed self._cleaning_chips: List[CleaningChip] = [] self._work_chips: List[WorkChip] = [] self._setup_fields() self.add_confirm_cancel_buttons() def _setup_fields(self): """필드 설정""" theme = self.config.theme text_color = "#f8fafc" if theme == 'dark' else "#1e293b" sub_text_color = "#94a3b8" if theme == 'dark' else "#64748b" border_color = "#334155" if theme == 'dark' else "#e2e8f0" # === 편성번호 섹션 === train_section = QWidget() train_layout = QVBoxLayout(train_section) train_layout.setContentsMargins(0, 0, 0, 0) train_layout.setSpacing(8) # 헤더 (라벨 + 토글) header = QWidget() header_layout = QHBoxLayout(header) header_layout.setContentsMargins(0, 0, 0, 0) header_layout.setSpacing(8) train_label = QLabel("편성번호") train_label.setFont(QFont("GmarketSans", 12, QFont.Bold)) train_label.setStyleSheet(f"color: {text_color};") header_layout.addWidget(train_label) required_mark = QLabel("*") required_mark.setFont(QFont("GmarketSans", 12, QFont.Bold)) required_mark.setStyleSheet("color: #ef4444;") header_layout.addWidget(required_mark) header_layout.addStretch() # 편성 선택 위젯 self.train_selection = TrainSelectionWidget(existing_trains=self.existing_trains) self.train_selection.train_selected.connect(self._on_train_selected) train_layout.addWidget(self.train_selection) self.content_layout.addWidget(train_section) # 구분선 separator1 = QFrame() separator1.setFrameShape(QFrame.HLine) separator1.setStyleSheet(f"color: {border_color};") self.content_layout.addWidget(separator1) # === 청소유형 섹션 === cleaning_section = QWidget() cleaning_layout = QVBoxLayout(cleaning_section) cleaning_layout.setContentsMargins(0, 0, 0, 0) cleaning_layout.setSpacing(8) cleaning_label = QLabel("청소유형") cleaning_label.setFont(QFont("GmarketSans", 12, QFont.Bold)) cleaning_label.setStyleSheet(f"color: {text_color};") cleaning_layout.addWidget(cleaning_label) # 청소유형 칩 컨테이너 cleaning_container = QWidget() cleaning_chip_layout = QHBoxLayout(cleaning_container) cleaning_chip_layout.setContentsMargins(0, 0, 0, 0) cleaning_chip_layout.setSpacing(8) cleaning_options = ["없음", "중청소", "대청소"] for option in cleaning_options: chip = CleaningChip(option) chip.chip_selected.connect(self._on_cleaning_selected) self._cleaning_chips.append(chip) cleaning_chip_layout.addWidget(chip) # 기본 선택: 없음 self._cleaning_chips[0].set_selected(True) cleaning_chip_layout.addStretch() cleaning_layout.addWidget(cleaning_container) self.content_layout.addWidget(cleaning_section) # 구분선 separator2 = QFrame() separator2.setFrameShape(QFrame.HLine) separator2.setStyleSheet(f"color: {border_color};") self.content_layout.addWidget(separator2) # === 작업여부 섹션 === work_section = QWidget() work_layout = QVBoxLayout(work_section) work_layout.setContentsMargins(0, 0, 0, 0) work_layout.setSpacing(8) work_label = QLabel("작업여부") work_label.setFont(QFont("GmarketSans", 12, QFont.Bold)) work_label.setStyleSheet(f"color: {text_color};") work_layout.addWidget(work_label) # 작업여부 칩 컨테이너 work_container = QWidget() work_chip_layout = QHBoxLayout(work_container) work_chip_layout.setContentsMargins(0, 0, 0, 0) work_chip_layout.setSpacing(8) # 없음, 있음 칩 work_none = WorkChip(False, "없음") work_none.chip_selected.connect(self._on_work_selected) self._work_chips.append(work_none) work_chip_layout.addWidget(work_none) work_has = WorkChip(True, "있음") work_has.chip_selected.connect(self._on_work_selected) self._work_chips.append(work_has) work_chip_layout.addWidget(work_has) # 기본 선택: 없음 self._work_chips[0].set_selected(True) work_chip_layout.addStretch() work_layout.addWidget(work_container) # 작업 상세 입력 (작업 있음 선택 시 표시) self.work_detail_container = QWidget() work_detail_layout = QVBoxLayout(self.work_detail_container) work_detail_layout.setContentsMargins(0, 0, 0, 0) work_detail_layout.setSpacing(8) # 작업 내용 입력 work_content_label = QLabel("작업 내용") work_content_label.setFont(QFont("GmarketSans", 11)) work_content_label.setStyleSheet(f"color: {text_color};") work_detail_layout.addWidget(work_content_label) self.work_content_edit = QTextEdit() self.work_content_edit.setPlaceholderText("작업 내용을 입력하세요...") self.work_content_edit.setFixedHeight(80) self.work_content_edit.setStyleSheet(f""" QTextEdit {{ background-color: {'#334155' if theme == 'dark' else '#f1f5f9'}; border: 1px solid {border_color}; border-radius: 8px; padding: 8px; color: {text_color}; }} """) work_detail_layout.addWidget(self.work_content_edit) # 작업 완료 여부 self.work_completed_check = QCheckBox("작업 완료") self.work_completed_check.setFont(QFont("GmarketSans", 11)) self.work_completed_check.setStyleSheet(f""" QCheckBox {{ color: {text_color}; spacing: 8px; }} QCheckBox::indicator {{ width: 20px; height: 20px; border: 2px solid {border_color}; border-radius: 4px; }} QCheckBox::indicator:checked {{ background-color: #3b82f6; border-color: #3b82f6; image: url(resources/icons/check.png); /* 아이콘이 없다면 체크 표시만 */ }} """) work_detail_layout.addWidget(self.work_completed_check) self.work_detail_container.setVisible(False) work_layout.addWidget(self.work_detail_container) self.content_layout.addWidget(work_section) self.content_layout.addStretch() # 초기 편성 칩 생성 (신평 모드로 시작) # self._load_trains(depot_filter="신평") # Handled by widget # def _load_trains(self, depot_filter: str = None): ... (Removed) # def _create_train_chips(self, trains: List[Dict[str, Any]]): ... (Removed) # def _on_depot_filter_changed(self, depot: str): ... (Removed) def _on_train_selected(self, train_number: str): """편성 선택""" self._selected_train_number = train_number # 라벨 업데이트 (Optional, widget handles selection visual) # theme = self.config.theme # text_color = "#f8fafc" if theme == 'dark' else "#1e293b" # self.selected_train_label.setText(f"선택: {train_number}") # self.selected_train_label.setStyleSheet(f"color: {text_color}; font-weight: bold;") def _on_cleaning_selected(self, cleaning_type: str): """청소유형 선택""" for chip in self._cleaning_chips: chip.set_selected(chip.cleaning_type == cleaning_type) self._selected_cleaning = cleaning_type def _on_work_selected(self, has_work: bool): """작업여부 선택""" for chip in self._work_chips: chip.set_selected(chip.has_work == has_work) self._selected_has_work = has_work self.work_detail_container.setVisible(has_work) # 다이얼로그 크기 조정 (내용에 맞게) self.adjustSize() def get_data(self) -> dict: """입력 데이터 반환""" return { "train_number": self._selected_train_number, "cleaning_type": self._selected_cleaning, "has_work": self._selected_has_work, "work_content": self.work_content_edit.toPlainText(), "is_work_completed": self.work_completed_check.isChecked(), }