- Meal time dialog (right-click lunch/dinner button to enter actual times) - Calendar right-click context: add/edit/delete past records - Monthly goal settings + progress widget (overtime cap, avg daily) - CSV import (our standard format) with conflict policy - Global crash handler with Gitea Issues auto-report Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
109 lines
4.2 KiB
Python
109 lines
4.2 KiB
Python
"""
|
|
점심/저녁 실제 시간 입력 다이얼로그.
|
|
|
|
기본 60분 자동 차감 모드와 별개로, 사용자가 정확한 시작/종료 시각을
|
|
입력하면 그 값을 break_records.break_type='lunch'/'dinner'로 저장.
|
|
"""
|
|
from __future__ import annotations
|
|
from datetime import datetime, timedelta
|
|
from PyQt5.QtWidgets import (QDialog, QVBoxLayout, QHBoxLayout, QLabel,
|
|
QPushButton, QTimeEdit, QMessageBox)
|
|
from PyQt5.QtCore import QTime, Qt
|
|
|
|
from ui.styles import apply_dark_titlebar
|
|
|
|
|
|
class MealTimeDialog(QDialog):
|
|
"""점심/저녁 실제 시작·종료 시간 입력."""
|
|
|
|
def __init__(self, parent=None, meal_type: str = 'lunch', default_minutes: int = 60):
|
|
super().__init__(parent)
|
|
self.meal_type = meal_type
|
|
title_kr = '점심' if meal_type == 'lunch' else '저녁'
|
|
self.setWindowTitle(f"{title_kr} 시간 입력")
|
|
self.setModal(True)
|
|
self.setFixedSize(360, 220)
|
|
|
|
layout = QVBoxLayout()
|
|
layout.setSpacing(10)
|
|
layout.setContentsMargins(20, 16, 20, 16)
|
|
|
|
info = QLabel(f"{title_kr} 시작·종료 시각을 입력하세요.\n자동 적용된 {default_minutes}분 대신 정확한 시간으로 기록됩니다.")
|
|
info.setWordWrap(True)
|
|
info.setStyleSheet("color: #888; padding-bottom: 6px;")
|
|
layout.addWidget(info)
|
|
|
|
# 시작
|
|
start_row = QHBoxLayout()
|
|
start_row.addWidget(QLabel("시작:"))
|
|
self.start_edit = QTimeEdit()
|
|
self.start_edit.setDisplayFormat("HH:mm")
|
|
# 기본값: 점심=12:00, 저녁=18:00
|
|
default_start = QTime(12, 0) if meal_type == 'lunch' else QTime(18, 0)
|
|
self.start_edit.setTime(default_start)
|
|
start_row.addWidget(self.start_edit)
|
|
start_row.addStretch()
|
|
layout.addLayout(start_row)
|
|
|
|
# 종료
|
|
end_row = QHBoxLayout()
|
|
end_row.addWidget(QLabel("종료:"))
|
|
self.end_edit = QTimeEdit()
|
|
self.end_edit.setDisplayFormat("HH:mm")
|
|
default_end = QTime(13, 0) if meal_type == 'lunch' else QTime(19, 0)
|
|
self.end_edit.setTime(default_end)
|
|
end_row.addWidget(self.end_edit)
|
|
end_row.addStretch()
|
|
layout.addLayout(end_row)
|
|
|
|
# 미리보기 라벨
|
|
self.preview = QLabel("")
|
|
self.preview.setStyleSheet("color: #4caf50; font-weight: bold; padding-top: 6px;")
|
|
layout.addWidget(self.preview)
|
|
self._update_preview()
|
|
self.start_edit.timeChanged.connect(self._update_preview)
|
|
self.end_edit.timeChanged.connect(self._update_preview)
|
|
|
|
# 버튼
|
|
btn_row = QHBoxLayout()
|
|
btn_row.addStretch()
|
|
ok_btn = QPushButton("저장")
|
|
ok_btn.setObjectName("btn_primary")
|
|
ok_btn.clicked.connect(self.accept)
|
|
cancel_btn = QPushButton("취소")
|
|
cancel_btn.clicked.connect(self.reject)
|
|
btn_row.addWidget(ok_btn)
|
|
btn_row.addWidget(cancel_btn)
|
|
layout.addLayout(btn_row)
|
|
|
|
self.setLayout(layout)
|
|
apply_dark_titlebar(self)
|
|
|
|
def _update_preview(self):
|
|
s = self.start_edit.time()
|
|
e = self.end_edit.time()
|
|
start_dt = datetime.combine(datetime.today(), s.toPyTime())
|
|
end_dt = datetime.combine(datetime.today(), e.toPyTime())
|
|
if end_dt < start_dt:
|
|
end_dt += timedelta(days=1)
|
|
minutes = int((end_dt - start_dt).total_seconds() / 60)
|
|
if minutes <= 0:
|
|
self.preview.setText("⚠️ 시작이 종료보다 늦습니다")
|
|
self.preview.setStyleSheet("color: #f44336;")
|
|
else:
|
|
self.preview.setText(f"총 {minutes}분")
|
|
self.preview.setStyleSheet("color: #4caf50; font-weight: bold;")
|
|
|
|
def get_times(self) -> tuple[str, str, int]:
|
|
"""('HH:MM:SS', 'HH:MM:SS', total_minutes) 반환."""
|
|
s = self.start_edit.time().toPyTime()
|
|
e = self.end_edit.time().toPyTime()
|
|
start_str = f"{s.hour:02d}:{s.minute:02d}:00"
|
|
end_str = f"{e.hour:02d}:{e.minute:02d}:00"
|
|
s_dt = datetime.combine(datetime.today(), s)
|
|
e_dt = datetime.combine(datetime.today(), e)
|
|
if e_dt < s_dt:
|
|
e_dt += timedelta(days=1)
|
|
minutes = int((e_dt - s_dt).total_seconds() / 60)
|
|
return start_str, end_str, minutes
|