903 lines
31 KiB
Python
903 lines
31 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
전동차 편성관리 다이얼로그 모듈
|
|
편성번호별 전동차 정보를 관리하는 다이얼로그입니다.
|
|
"""
|
|
|
|
from datetime import date
|
|
from typing import Optional, Dict, Any
|
|
from PySide6.QtWidgets import (
|
|
QWidget, QVBoxLayout, QHBoxLayout, QTableWidget, QTableWidgetItem,
|
|
QPushButton, QLineEdit, QComboBox, QDateEdit, QSpinBox,
|
|
QLabel, QHeaderView, QAbstractItemView, QMessageBox, QButtonGroup
|
|
)
|
|
from PySide6.QtCore import Qt, QDate, Signal
|
|
from PySide6.QtGui import QFont
|
|
|
|
from ui.base.base_dialog import BaseDialog
|
|
from ui.components.chips.choice_chip_button import ChoiceChipButton
|
|
from database.common_db_manager import CommonDatabaseManager
|
|
from database.models import TrainFormation
|
|
from core.logger import get_logger
|
|
|
|
logger = get_logger(__name__)
|
|
|
|
|
|
class TrainFormationDialog(BaseDialog):
|
|
"""전동차 편성관리 다이얼로그"""
|
|
|
|
def __init__(self, parent=None):
|
|
super().__init__(
|
|
parent=parent,
|
|
title="전동차 편성관리",
|
|
width=1200,
|
|
height=700,
|
|
min_width=1000,
|
|
min_height=600
|
|
)
|
|
|
|
self.db = CommonDatabaseManager()
|
|
self.current_formation_id = None
|
|
|
|
self._setup_ui()
|
|
self._load_formations()
|
|
self._apply_black_theme()
|
|
|
|
def _setup_ui(self):
|
|
"""UI 설정"""
|
|
# 검색 및 필터 영역
|
|
filter_widget = QWidget()
|
|
filter_layout = QHBoxLayout(filter_widget)
|
|
filter_layout.setContentsMargins(0, 0, 0, 0)
|
|
filter_layout.setSpacing(12)
|
|
|
|
# 검색 입력
|
|
self.search_input = QLineEdit()
|
|
self.search_input.setPlaceholderText("편성번호로 검색...")
|
|
self.search_input.textChanged.connect(self._on_search_changed)
|
|
filter_layout.addWidget(QLabel("검색:"))
|
|
filter_layout.addWidget(self.search_input)
|
|
|
|
# 배속지 필터 (필터 칩 사용)
|
|
filter_layout.addWidget(QLabel("배속지:"))
|
|
|
|
depot_chip_container = QWidget()
|
|
depot_chip_layout = QHBoxLayout(depot_chip_container)
|
|
depot_chip_layout.setContentsMargins(0, 0, 0, 0)
|
|
depot_chip_layout.setSpacing(8)
|
|
|
|
self.depot_filter_group = QButtonGroup()
|
|
self.depot_filter_group.setExclusive(True)
|
|
self.selected_depot = "전체"
|
|
|
|
def create_depot_chip(text: str, key: str):
|
|
# 초기 상태: 전체가 선택된 상태
|
|
is_selected = (key == "전체")
|
|
bg_color = "#3b82f6" if is_selected else "#404040"
|
|
|
|
chip = ChoiceChipButton(text=text, key=key, bg=bg_color)
|
|
self.depot_filter_group.addButton(chip)
|
|
depot_chip_layout.addWidget(chip)
|
|
|
|
if is_selected:
|
|
chip.setChecked(True)
|
|
|
|
def make_depot_handler(depot_key: str):
|
|
def handler():
|
|
self.selected_depot = depot_key
|
|
# 선택된 칩 색상 변경
|
|
for btn in self.depot_filter_group.buttons():
|
|
if isinstance(btn, ChoiceChipButton):
|
|
if btn.key == depot_key:
|
|
btn.set_bg("#3b82f6") # 선택됨: 파란색
|
|
else:
|
|
btn.set_bg("#404040") # 선택안됨: 회색
|
|
self._on_filter_changed(depot_key)
|
|
return handler
|
|
|
|
chip.clicked_key.connect(make_depot_handler(key))
|
|
return chip
|
|
|
|
create_depot_chip("전체", "전체")
|
|
create_depot_chip("신평", "신평")
|
|
create_depot_chip("노포", "노포")
|
|
|
|
filter_layout.addWidget(depot_chip_container)
|
|
|
|
filter_layout.addStretch()
|
|
|
|
# 추가 버튼
|
|
self.add_btn = QPushButton("추가")
|
|
self.add_btn.clicked.connect(self._on_add)
|
|
filter_layout.addWidget(self.add_btn)
|
|
|
|
self.content_layout.addWidget(filter_widget)
|
|
|
|
# 테이블
|
|
self.table = QTableWidget()
|
|
self.table.setColumnCount(9)
|
|
self.table.setHorizontalHeaderLabels([
|
|
"편성번호", "신차/구차", "제조사", "도입일",
|
|
"배속지", "별칭", "도입단계", "도입량", "관리"
|
|
])
|
|
|
|
# 테이블 설정
|
|
self.table.setSelectionBehavior(QAbstractItemView.SelectRows)
|
|
self.table.setSelectionMode(QAbstractItemView.SingleSelection)
|
|
self.table.setAlternatingRowColors(True)
|
|
self.table.setShowGrid(False)
|
|
self.table.verticalHeader().setVisible(False)
|
|
self.table.setEditTriggers(QAbstractItemView.NoEditTriggers)
|
|
|
|
# 헤더 설정
|
|
header = self.table.horizontalHeader()
|
|
header.setStretchLastSection(False)
|
|
header.setSectionResizeMode(QHeaderView.Interactive)
|
|
header.setDefaultAlignment(Qt.AlignCenter)
|
|
|
|
# 컬럼 너비 설정
|
|
self.table.setColumnWidth(0, 100) # 편성번호
|
|
self.table.setColumnWidth(1, 100) # 신차/구차
|
|
self.table.setColumnWidth(2, 120) # 제조사
|
|
self.table.setColumnWidth(3, 120) # 도입일
|
|
self.table.setColumnWidth(4, 100) # 배속지
|
|
self.table.setColumnWidth(5, 150) # 별칭
|
|
self.table.setColumnWidth(6, 120) # 도입단계
|
|
self.table.setColumnWidth(7, 100) # 도입량
|
|
self.table.setColumnWidth(8, 150) # 관리
|
|
|
|
self.content_layout.addWidget(self.table, 1)
|
|
|
|
# 버튼 영역
|
|
button_layout = QHBoxLayout()
|
|
button_layout.addStretch()
|
|
|
|
self.close_btn = QPushButton("닫기")
|
|
self.close_btn.clicked.connect(self.close)
|
|
button_layout.addWidget(self.close_btn)
|
|
|
|
self.content_layout.addLayout(button_layout)
|
|
|
|
def _apply_black_theme(self):
|
|
"""블랙 테마 적용"""
|
|
self.setStyleSheet("""
|
|
QDialog {
|
|
background-color: #0a0a0a;
|
|
}
|
|
|
|
#dialogContainer {
|
|
background-color: #1a1a1a;
|
|
border: 1px solid #333333;
|
|
border-radius: 16px;
|
|
}
|
|
|
|
#dialogTitle {
|
|
color: #ffffff;
|
|
font-family: 'GmarketSans';
|
|
font-size: 18pt;
|
|
font-weight: bold;
|
|
}
|
|
|
|
#closeButton {
|
|
background-color: transparent;
|
|
border: none;
|
|
color: #ffffff;
|
|
font-size: 16px;
|
|
border-radius: 16px;
|
|
}
|
|
|
|
#closeButton:hover {
|
|
background-color: #dc2626;
|
|
color: white;
|
|
}
|
|
|
|
QLabel {
|
|
color: #e0e0e0;
|
|
font-family: 'GmarketSans';
|
|
font-size: 11pt;
|
|
}
|
|
|
|
QLineEdit, QComboBox, QDateEdit, QSpinBox {
|
|
background-color: #2a2a2a;
|
|
color: #ffffff;
|
|
border: 1px solid #404040;
|
|
border-radius: 6px;
|
|
padding: 8px 12px;
|
|
font-family: 'GmarketSans';
|
|
font-size: 11pt;
|
|
}
|
|
|
|
QLineEdit:focus, QComboBox:focus, QDateEdit:focus, QSpinBox:focus {
|
|
border-color: #3b82f6;
|
|
outline: none;
|
|
}
|
|
|
|
QComboBox::drop-down {
|
|
border: none;
|
|
background-color: #404040;
|
|
width: 30px;
|
|
}
|
|
|
|
QComboBox::down-arrow {
|
|
image: none;
|
|
border-left: 5px solid transparent;
|
|
border-right: 5px solid transparent;
|
|
border-top: 6px solid #ffffff;
|
|
width: 0;
|
|
height: 0;
|
|
}
|
|
|
|
QComboBox QAbstractItemView {
|
|
background-color: #2a2a2a;
|
|
color: #ffffff;
|
|
border: 1px solid #404040;
|
|
selection-background-color: #3b82f6;
|
|
}
|
|
|
|
QDateEdit::drop-down {
|
|
border: none;
|
|
background-color: #404040;
|
|
width: 30px;
|
|
}
|
|
|
|
QPushButton {
|
|
background-color: #3b82f6;
|
|
color: white;
|
|
border: none;
|
|
border-radius: 8px;
|
|
padding: 8px 20px;
|
|
font-family: 'GmarketSans';
|
|
font-size: 11pt;
|
|
font-weight: bold;
|
|
}
|
|
|
|
QPushButton:hover {
|
|
background-color: #2563eb;
|
|
}
|
|
|
|
QPushButton:pressed {
|
|
background-color: #1d4ed8;
|
|
}
|
|
|
|
QPushButton#editBtn {
|
|
background-color: #64748b;
|
|
}
|
|
|
|
QPushButton#editBtn:hover {
|
|
background-color: #475569;
|
|
}
|
|
|
|
QPushButton#deleteBtn {
|
|
background-color: #ef4444;
|
|
}
|
|
|
|
QPushButton#deleteBtn:hover {
|
|
background-color: #dc2626;
|
|
}
|
|
|
|
QTableWidget {
|
|
background-color: #1a1a1a;
|
|
color: #ffffff;
|
|
border: 1px solid #333333;
|
|
border-radius: 8px;
|
|
gridline-color: #333333;
|
|
font-family: 'GmarketSans';
|
|
font-size: 11pt;
|
|
}
|
|
|
|
QTableWidget::item {
|
|
padding: 8px;
|
|
border: none;
|
|
}
|
|
|
|
QTableWidget::item:selected {
|
|
background-color: #3b82f6;
|
|
color: white;
|
|
}
|
|
|
|
QTableWidget::item:alternate {
|
|
background-color: #222222;
|
|
}
|
|
|
|
QHeaderView::section {
|
|
background-color: #2a2a2a;
|
|
color: #ffffff;
|
|
padding: 10px;
|
|
border: none;
|
|
border-bottom: 2px solid #3b82f6;
|
|
font-family: 'GmarketSans';
|
|
font-size: 11pt;
|
|
font-weight: bold;
|
|
}
|
|
|
|
QCheckBox {
|
|
color: #ffffff;
|
|
font-family: 'GmarketSans';
|
|
font-size: 11pt;
|
|
}
|
|
|
|
QCheckBox::indicator {
|
|
width: 18px;
|
|
height: 18px;
|
|
border: 2px solid #404040;
|
|
border-radius: 4px;
|
|
background-color: #2a2a2a;
|
|
}
|
|
|
|
QCheckBox::indicator:checked {
|
|
background-color: #3b82f6;
|
|
border-color: #3b82f6;
|
|
}
|
|
""")
|
|
|
|
def _load_formations(self, search_text: str = "", depot_filter: str = "전체"):
|
|
"""편성 목록 로드"""
|
|
self.table.setRowCount(0)
|
|
|
|
query = "SELECT * FROM train_formations WHERE 1=1"
|
|
params = []
|
|
|
|
if search_text:
|
|
query += " AND train_number LIKE ?"
|
|
params.append(f"%{search_text}%")
|
|
|
|
if depot_filter != "전체":
|
|
query += " AND depot = ?"
|
|
params.append(depot_filter)
|
|
|
|
query += " ORDER BY train_number"
|
|
|
|
formations = self.db.fetch_all(query, tuple(params) if params else None)
|
|
|
|
for formation in formations:
|
|
self._add_table_row(formation)
|
|
|
|
def _add_table_row(self, formation: Dict[str, Any]):
|
|
"""테이블에 행 추가"""
|
|
row = self.table.rowCount()
|
|
self.table.insertRow(row)
|
|
|
|
# 편성번호
|
|
train_number_item = QTableWidgetItem(formation.get('train_number', ''))
|
|
train_number_item.setTextAlignment(Qt.AlignCenter)
|
|
train_number_item.setData(Qt.UserRole, formation.get('id'))
|
|
self.table.setItem(row, 0, train_number_item)
|
|
|
|
# 신차/구차
|
|
is_new = formation.get('is_new_train', 1)
|
|
new_old_item = QTableWidgetItem("신차" if is_new else "구차")
|
|
new_old_item.setTextAlignment(Qt.AlignCenter)
|
|
self.table.setItem(row, 1, new_old_item)
|
|
|
|
# 제조사
|
|
manufacturer_item = QTableWidgetItem(formation.get('manufacturer', ''))
|
|
manufacturer_item.setTextAlignment(Qt.AlignCenter)
|
|
self.table.setItem(row, 2, manufacturer_item)
|
|
|
|
# 도입일
|
|
intro_date = formation.get('introduction_date')
|
|
if intro_date:
|
|
if isinstance(intro_date, str):
|
|
date_str = intro_date
|
|
else:
|
|
date_str = intro_date.strftime('%Y-%m-%d')
|
|
else:
|
|
date_str = ""
|
|
date_item = QTableWidgetItem(date_str)
|
|
date_item.setTextAlignment(Qt.AlignCenter)
|
|
self.table.setItem(row, 3, date_item)
|
|
|
|
# 배속지
|
|
depot_item = QTableWidgetItem(formation.get('depot', ''))
|
|
depot_item.setTextAlignment(Qt.AlignCenter)
|
|
self.table.setItem(row, 4, depot_item)
|
|
|
|
# 별칭
|
|
alias_item = QTableWidgetItem(formation.get('alias', ''))
|
|
alias_item.setTextAlignment(Qt.AlignCenter)
|
|
self.table.setItem(row, 5, alias_item)
|
|
|
|
# 도입단계
|
|
stage_item = QTableWidgetItem(formation.get('introduction_stage', ''))
|
|
stage_item.setTextAlignment(Qt.AlignCenter)
|
|
self.table.setItem(row, 6, stage_item)
|
|
|
|
# 도입량
|
|
count_item = QTableWidgetItem(str(formation.get('introduction_count', 0)))
|
|
count_item.setTextAlignment(Qt.AlignCenter)
|
|
self.table.setItem(row, 7, count_item)
|
|
|
|
# 관리 버튼
|
|
button_widget = QWidget()
|
|
button_layout = QHBoxLayout(button_widget)
|
|
button_layout.setContentsMargins(4, 4, 4, 4)
|
|
button_layout.setSpacing(4)
|
|
|
|
edit_btn = QPushButton("수정")
|
|
edit_btn.setObjectName("editBtn")
|
|
edit_btn.setFixedHeight(32)
|
|
edit_btn.clicked.connect(lambda checked, fid=formation.get('id'): self._on_edit(fid))
|
|
button_layout.addWidget(edit_btn)
|
|
|
|
delete_btn = QPushButton("삭제")
|
|
delete_btn.setObjectName("deleteBtn")
|
|
delete_btn.setFixedHeight(32)
|
|
delete_btn.clicked.connect(lambda checked, fid=formation.get('id'): self._on_delete(fid))
|
|
button_layout.addWidget(delete_btn)
|
|
|
|
self.table.setCellWidget(row, 8, button_widget)
|
|
|
|
def _on_search_changed(self, text: str):
|
|
"""검색어 변경"""
|
|
self._load_formations(text, self.selected_depot)
|
|
|
|
def _on_filter_changed(self, depot_filter: str = None):
|
|
"""필터 변경"""
|
|
if depot_filter is None:
|
|
depot_filter = self.selected_depot
|
|
search_text = self.search_input.text()
|
|
self._load_formations(search_text, depot_filter)
|
|
|
|
def _on_add(self):
|
|
"""추가 버튼 클릭"""
|
|
dialog = TrainFormationEditDialog(self)
|
|
if dialog.exec():
|
|
self._load_formations()
|
|
|
|
def _on_edit(self, formation_id: int):
|
|
"""수정 버튼 클릭"""
|
|
formation = self.db.fetch_one(
|
|
"SELECT * FROM train_formations WHERE id = ?",
|
|
(formation_id,)
|
|
)
|
|
if formation:
|
|
dialog = TrainFormationEditDialog(self, formation)
|
|
if dialog.exec():
|
|
self._load_formations()
|
|
|
|
def _on_delete(self, formation_id: int):
|
|
"""삭제 버튼 클릭"""
|
|
reply = QMessageBox.question(
|
|
self,
|
|
"삭제 확인",
|
|
"정말 삭제하시겠습니까?",
|
|
QMessageBox.Yes | QMessageBox.No,
|
|
QMessageBox.No
|
|
)
|
|
|
|
if reply == QMessageBox.Yes:
|
|
try:
|
|
self.db.execute(
|
|
"DELETE FROM train_formations WHERE id = ?",
|
|
(formation_id,)
|
|
)
|
|
self.db.commit()
|
|
self._load_formations()
|
|
except Exception as e:
|
|
logger.error(f"편성 삭제 실패: {e}")
|
|
QMessageBox.critical(self, "오류", f"삭제 중 오류가 발생했습니다: {e}")
|
|
|
|
|
|
class TrainFormationEditDialog(BaseDialog):
|
|
"""편성 편집 다이얼로그"""
|
|
|
|
def __init__(self, parent=None, formation: Optional[Dict[str, Any]] = None):
|
|
super().__init__(
|
|
parent=parent,
|
|
title="편성 정보 편집" if formation else "편성 추가",
|
|
width=500,
|
|
height=600,
|
|
min_width=450,
|
|
min_height=550
|
|
)
|
|
|
|
self.db = CommonDatabaseManager()
|
|
self.formation = formation
|
|
|
|
self._setup_ui()
|
|
self._apply_black_theme()
|
|
|
|
if formation:
|
|
self._load_formation_data(formation)
|
|
|
|
def _setup_ui(self):
|
|
"""UI 설정"""
|
|
# 편성번호
|
|
self.train_number_input = QLineEdit()
|
|
self.train_number_input.setPlaceholderText("예: 134a, 134b, 1A")
|
|
self._add_form_row("편성번호 *", self.train_number_input)
|
|
|
|
# 신차/구차 (필터 칩 사용)
|
|
new_old_container = QWidget()
|
|
new_old_layout = QHBoxLayout(new_old_container)
|
|
new_old_layout.setContentsMargins(0, 0, 0, 0)
|
|
new_old_layout.setSpacing(8)
|
|
|
|
self.new_old_group = QButtonGroup()
|
|
self.new_old_group.setExclusive(True)
|
|
self.is_new_train = True
|
|
|
|
def create_new_old_chip(text: str, key: bool):
|
|
# 초기 상태: True(신차)가 기본값
|
|
is_selected = (key == True)
|
|
bg_color = "#3b82f6" if is_selected else "#404040"
|
|
|
|
chip = ChoiceChipButton(text=text, key=str(key), bg=bg_color)
|
|
self.new_old_group.addButton(chip)
|
|
new_old_layout.addWidget(chip)
|
|
|
|
if is_selected:
|
|
chip.setChecked(True)
|
|
|
|
def make_handler(is_new: bool):
|
|
def handler():
|
|
self.is_new_train = is_new
|
|
# 선택된 칩 색상 변경
|
|
for btn in self.new_old_group.buttons():
|
|
if isinstance(btn, ChoiceChipButton):
|
|
if btn.key == str(is_new):
|
|
btn.set_bg("#3b82f6") # 선택됨: 파란색
|
|
else:
|
|
btn.set_bg("#404040") # 선택안됨: 회색
|
|
return handler
|
|
|
|
chip.clicked_key.connect(make_handler(key))
|
|
return chip
|
|
|
|
create_new_old_chip("신차", True)
|
|
create_new_old_chip("구차", False)
|
|
|
|
new_old_layout.addStretch()
|
|
|
|
row = QWidget()
|
|
row_layout = QHBoxLayout(row)
|
|
row_layout.setContentsMargins(0, 0, 0, 0)
|
|
row_layout.setSpacing(12)
|
|
|
|
label = QLabel("신차/구차")
|
|
label.setMinimumWidth(100)
|
|
row_layout.addWidget(label)
|
|
row_layout.addWidget(new_old_container, 1)
|
|
|
|
self.content_layout.addWidget(row)
|
|
|
|
# 제조사
|
|
self.manufacturer_input = QLineEdit()
|
|
self.manufacturer_input.setPlaceholderText("제조사명 입력")
|
|
self._add_form_row("제조사", self.manufacturer_input)
|
|
|
|
# 도입일
|
|
self.introduction_date_input = QDateEdit()
|
|
self.introduction_date_input.setCalendarPopup(True)
|
|
self.introduction_date_input.setDate(QDate.currentDate())
|
|
self.introduction_date_input.setDisplayFormat("yyyy-MM-dd")
|
|
self._add_form_row("도입일", self.introduction_date_input)
|
|
|
|
# 배속지 (필터 칩 사용)
|
|
depot_container = QWidget()
|
|
depot_layout = QHBoxLayout(depot_container)
|
|
depot_layout.setContentsMargins(0, 0, 0, 0)
|
|
depot_layout.setSpacing(8)
|
|
|
|
self.depot_group = QButtonGroup()
|
|
self.depot_group.setExclusive(True)
|
|
self.selected_depot_edit = "신평" # 기본값
|
|
|
|
def create_depot_chip(text: str, key: str):
|
|
# 초기 상태: 신평이 기본값
|
|
is_selected = (key == "신평")
|
|
bg_color = "#3b82f6" if is_selected else "#404040"
|
|
|
|
chip = ChoiceChipButton(text=text, key=key, bg=bg_color)
|
|
self.depot_group.addButton(chip)
|
|
depot_layout.addWidget(chip)
|
|
|
|
if is_selected:
|
|
chip.setChecked(True)
|
|
|
|
def make_handler(depot_key: str):
|
|
def handler():
|
|
self.selected_depot_edit = depot_key
|
|
# 선택된 칩 색상 변경
|
|
for btn in self.depot_group.buttons():
|
|
if isinstance(btn, ChoiceChipButton):
|
|
if btn.key == depot_key:
|
|
btn.set_bg("#3b82f6") # 선택됨: 파란색
|
|
else:
|
|
btn.set_bg("#404040") # 선택안됨: 회색
|
|
return handler
|
|
|
|
chip.clicked_key.connect(make_handler(key))
|
|
return chip
|
|
|
|
create_depot_chip("신평", "신평")
|
|
create_depot_chip("노포", "노포")
|
|
|
|
depot_layout.addStretch()
|
|
|
|
row = QWidget()
|
|
row_layout = QHBoxLayout(row)
|
|
row_layout.setContentsMargins(0, 0, 0, 0)
|
|
row_layout.setSpacing(12)
|
|
|
|
label = QLabel("배속지")
|
|
label.setMinimumWidth(100)
|
|
row_layout.addWidget(label)
|
|
row_layout.addWidget(depot_container, 1)
|
|
|
|
self.content_layout.addWidget(row)
|
|
|
|
# 별칭
|
|
self.alias_input = QLineEdit()
|
|
self.alias_input.setPlaceholderText("별칭 입력")
|
|
self._add_form_row("별칭", self.alias_input)
|
|
|
|
# 도입단계
|
|
self.stage_input = QLineEdit()
|
|
self.stage_input.setPlaceholderText("도입단계 입력")
|
|
self._add_form_row("도입단계", self.stage_input)
|
|
|
|
# 도입량
|
|
self.count_input = QSpinBox()
|
|
self.count_input.setMinimum(0)
|
|
self.count_input.setMaximum(9999)
|
|
self.count_input.setValue(0)
|
|
self._add_form_row("도입량", self.count_input)
|
|
|
|
# 버튼
|
|
self.add_confirm_cancel_buttons()
|
|
|
|
def _add_form_row(self, label_text: str, widget: QWidget):
|
|
"""폼 행 추가"""
|
|
row = QWidget()
|
|
row_layout = QHBoxLayout(row)
|
|
row_layout.setContentsMargins(0, 0, 0, 0)
|
|
row_layout.setSpacing(12)
|
|
|
|
label = QLabel(label_text)
|
|
label.setMinimumWidth(100)
|
|
row_layout.addWidget(label)
|
|
|
|
row_layout.addWidget(widget, 1)
|
|
|
|
self.content_layout.addWidget(row)
|
|
|
|
def _load_formation_data(self, formation: Dict[str, Any]):
|
|
"""편성 데이터 로드"""
|
|
self.train_number_input.setText(formation.get('train_number', ''))
|
|
|
|
# 신차/구차 설정
|
|
is_new = bool(formation.get('is_new_train', True))
|
|
self.is_new_train = is_new
|
|
for btn in self.new_old_group.buttons():
|
|
if isinstance(btn, ChoiceChipButton):
|
|
if btn.key == str(is_new):
|
|
btn.setChecked(True)
|
|
btn.set_bg("#3b82f6") # 선택됨: 파란색
|
|
else:
|
|
btn.setChecked(False)
|
|
btn.set_bg("#404040") # 선택안됨: 회색
|
|
|
|
manufacturer = formation.get('manufacturer', '')
|
|
if manufacturer:
|
|
self.manufacturer_input.setText(manufacturer)
|
|
|
|
intro_date = formation.get('introduction_date')
|
|
if intro_date:
|
|
if isinstance(intro_date, str):
|
|
date_obj = QDate.fromString(intro_date, "yyyy-MM-dd")
|
|
else:
|
|
date_obj = QDate.fromString(intro_date.strftime('%Y-%m-%d'), "yyyy-MM-dd")
|
|
if date_obj.isValid():
|
|
self.introduction_date_input.setDate(date_obj)
|
|
|
|
# 배속지 설정
|
|
depot = formation.get('depot', '')
|
|
self.selected_depot_edit = depot
|
|
for btn in self.depot_group.buttons():
|
|
if isinstance(btn, ChoiceChipButton):
|
|
if btn.key == depot:
|
|
btn.setChecked(True)
|
|
btn.set_bg("#3b82f6") # 선택됨: 파란색
|
|
else:
|
|
btn.setChecked(False)
|
|
btn.set_bg("#404040") # 선택안됨: 회색
|
|
|
|
alias = formation.get('alias', '')
|
|
if alias:
|
|
self.alias_input.setText(alias)
|
|
|
|
stage = formation.get('introduction_stage', '')
|
|
if stage:
|
|
self.stage_input.setText(stage)
|
|
|
|
count = formation.get('introduction_count', 0)
|
|
self.count_input.setValue(int(count) if count else 0)
|
|
|
|
def _apply_black_theme(self):
|
|
"""블랙 테마 적용"""
|
|
self.setStyleSheet("""
|
|
QDialog {
|
|
background-color: #0a0a0a;
|
|
}
|
|
|
|
#dialogContainer {
|
|
background-color: #1a1a1a;
|
|
border: 1px solid #333333;
|
|
border-radius: 16px;
|
|
}
|
|
|
|
#dialogTitle {
|
|
color: #ffffff;
|
|
font-family: 'GmarketSans';
|
|
font-size: 18pt;
|
|
font-weight: bold;
|
|
}
|
|
|
|
#closeButton {
|
|
background-color: transparent;
|
|
border: none;
|
|
color: #ffffff;
|
|
font-size: 16px;
|
|
border-radius: 16px;
|
|
}
|
|
|
|
#closeButton:hover {
|
|
background-color: #dc2626;
|
|
color: white;
|
|
}
|
|
|
|
QLabel {
|
|
color: #e0e0e0;
|
|
font-family: 'GmarketSans';
|
|
font-size: 11pt;
|
|
}
|
|
|
|
QLineEdit, QComboBox, QDateEdit, QSpinBox {
|
|
background-color: #2a2a2a;
|
|
color: #ffffff;
|
|
border: 1px solid #404040;
|
|
border-radius: 6px;
|
|
padding: 8px 12px;
|
|
font-family: 'GmarketSans';
|
|
font-size: 11pt;
|
|
}
|
|
|
|
QLineEdit:focus, QComboBox:focus, QDateEdit:focus, QSpinBox:focus {
|
|
border-color: #3b82f6;
|
|
outline: none;
|
|
}
|
|
|
|
QComboBox::drop-down {
|
|
border: none;
|
|
background-color: #404040;
|
|
width: 30px;
|
|
}
|
|
|
|
QComboBox::down-arrow {
|
|
image: none;
|
|
border-left: 5px solid transparent;
|
|
border-right: 5px solid transparent;
|
|
border-top: 6px solid #ffffff;
|
|
width: 0;
|
|
height: 0;
|
|
}
|
|
|
|
QComboBox QAbstractItemView {
|
|
background-color: #2a2a2a;
|
|
color: #ffffff;
|
|
border: 1px solid #404040;
|
|
selection-background-color: #3b82f6;
|
|
}
|
|
|
|
QDateEdit::drop-down {
|
|
border: none;
|
|
background-color: #404040;
|
|
width: 30px;
|
|
}
|
|
|
|
QPushButton {
|
|
background-color: #3b82f6;
|
|
color: white;
|
|
border: none;
|
|
border-radius: 8px;
|
|
padding: 8px 20px;
|
|
font-family: 'GmarketSans';
|
|
font-size: 11pt;
|
|
font-weight: bold;
|
|
}
|
|
|
|
QPushButton:hover {
|
|
background-color: #2563eb;
|
|
}
|
|
|
|
QPushButton:pressed {
|
|
background-color: #1d4ed8;
|
|
}
|
|
|
|
QPushButton#editBtn {
|
|
background-color: #64748b;
|
|
}
|
|
|
|
QPushButton#editBtn:hover {
|
|
background-color: #475569;
|
|
}
|
|
|
|
QPushButton#deleteBtn {
|
|
background-color: #ef4444;
|
|
}
|
|
|
|
QPushButton#deleteBtn:hover {
|
|
background-color: #dc2626;
|
|
}
|
|
|
|
QCheckBox {
|
|
color: #ffffff;
|
|
font-family: 'GmarketSans';
|
|
font-size: 11pt;
|
|
}
|
|
|
|
QCheckBox::indicator {
|
|
width: 18px;
|
|
height: 18px;
|
|
border: 2px solid #404040;
|
|
border-radius: 4px;
|
|
background-color: #2a2a2a;
|
|
}
|
|
|
|
QCheckBox::indicator:checked {
|
|
background-color: #3b82f6;
|
|
border-color: #3b82f6;
|
|
}
|
|
""")
|
|
|
|
def _on_confirm(self):
|
|
"""확인 버튼 클릭"""
|
|
train_number = self.train_number_input.text().strip()
|
|
|
|
if not train_number:
|
|
QMessageBox.warning(self, "입력 오류", "편성번호를 입력해주세요.")
|
|
return
|
|
|
|
try:
|
|
is_new = 1 if self.is_new_train else 0
|
|
manufacturer = self.manufacturer_input.text().strip() or None
|
|
qdate = self.introduction_date_input.date()
|
|
intro_date = date(qdate.year(), qdate.month(), qdate.day()) if qdate.isValid() else None
|
|
depot = self.selected_depot_edit or None
|
|
alias = self.alias_input.text().strip() or None
|
|
stage = self.stage_input.text().strip() or None
|
|
count = self.count_input.value()
|
|
|
|
if self.formation:
|
|
# 수정
|
|
self.db.execute("""
|
|
UPDATE train_formations
|
|
SET train_number = ?, is_new_train = ?, manufacturer = ?,
|
|
introduction_date = ?, depot = ?, alias = ?,
|
|
introduction_stage = ?, introduction_count = ?,
|
|
updated_at = CURRENT_TIMESTAMP
|
|
WHERE id = ?
|
|
""", (
|
|
train_number, is_new, manufacturer, intro_date,
|
|
depot, alias, stage, count, self.formation.get('id')
|
|
))
|
|
else:
|
|
# 추가
|
|
self.db.execute("""
|
|
INSERT INTO train_formations
|
|
(train_number, is_new_train, manufacturer, introduction_date,
|
|
depot, alias, introduction_stage, introduction_count)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
""", (
|
|
train_number, is_new, manufacturer, intro_date,
|
|
depot, alias, stage, count
|
|
))
|
|
|
|
self.db.commit()
|
|
self.accept()
|
|
|
|
except Exception as e:
|
|
logger.error(f"편성 저장 실패: {e}")
|
|
QMessageBox.critical(self, "오류", f"저장 중 오류가 발생했습니다: {e}")
|