Clock_out_Time_Calculator/ui/goal_widget.py
KINDNICK 5fb8655a47 v2.11.0: UI 전면 다크 리디자인 + 라인 아이콘 + 적립 가드/삭제
- 모던 다크 미니멀 테마(NanumSquare 번들, 단일 accent #4DABF7, 8px radius, flat 버튼, 다크 기본값)
- 라인 아이콘 시스템(ui/icons.py, QtSvg) — 앱 전반 이모지 교체
- 다크 깨짐 수정: 테이블 헤더/코너 흰색, 도움말 탭 흰 라인, 트레이/미니위젯 메뉴
- fix: 자동 적립 OFF가 자동 퇴근 경로에서 무시되던 버그(게이팅)
- feat: 연장근무 적립 기록 삭제(우클릭)
- 테스트 3건 추가

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 18:21:54 +09:00

101 lines
3.9 KiB
Python

"""
목표 진행률 위젯.
월 연장근무 상한 + 일평균 목표를 stats_view 또는 메인에 표시.
설정값이 0이면 비활성 (위젯 자체 hide).
"""
from __future__ import annotations
from datetime import datetime, date
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel, QProgressBar
from PyQt5.QtCore import Qt
class GoalWidget(QWidget):
"""월간 목표 진행률 표시."""
def __init__(self, db, parent=None):
super().__init__(parent)
self.db = db
layout = QVBoxLayout()
layout.setContentsMargins(8, 6, 8, 6)
layout.setSpacing(4)
title = QLabel("이번 달 목표")
title.setStyleSheet("font-weight: bold;")
layout.addWidget(title)
# 연장근무 상한
ot_row = QHBoxLayout()
self.ot_label = QLabel("연장근무:")
self.ot_label.setFixedWidth(100)
self.ot_bar = QProgressBar()
self.ot_bar.setTextVisible(True)
self.ot_bar.setFixedHeight(18)
ot_row.addWidget(self.ot_label)
ot_row.addWidget(self.ot_bar, 1)
layout.addLayout(ot_row)
# 일평균
avg_row = QHBoxLayout()
self.avg_label = QLabel("일평균:")
self.avg_label.setFixedWidth(100)
self.avg_bar = QProgressBar()
self.avg_bar.setTextVisible(True)
self.avg_bar.setFixedHeight(18)
avg_row.addWidget(self.avg_label)
avg_row.addWidget(self.avg_bar, 1)
layout.addLayout(avg_row)
self.setLayout(layout)
def refresh(self):
"""현재 설정값과 이번 달 통계로 진행률 갱신. 0=비활성 시 row 숨김."""
try:
ot_target = int(self.db.get_setting('goal_overtime_max_monthly', '0') or 0)
avg_target = float(self.db.get_setting('goal_avg_hours_daily', '0') or 0)
except (ValueError, TypeError):
ot_target, avg_target = 0, 0.0
# 둘 다 비활성이면 위젯 자체 숨김
if ot_target <= 0 and avg_target <= 0:
self.setVisible(False)
return
self.setVisible(True)
now = datetime.now()
stats = self.db.get_monthly_stats(now.year, now.month)
ot_total = (stats.get('total_overtime_minutes') or 0)
total_h = stats.get('total_hours') or 0
work_days = stats.get('work_days') or 1
# 연장근무 상한 (낮을수록 좋음)
if ot_target > 0:
self.ot_label.setVisible(True)
self.ot_bar.setVisible(True)
self.ot_bar.setMaximum(ot_target)
self.ot_bar.setValue(min(ot_total, ot_target))
ratio = ot_total / ot_target if ot_target else 0
ot_h, ot_m = ot_total // 60, ot_total % 60
tg_h, tg_m = ot_target // 60, ot_target % 60
self.ot_bar.setFormat(f"{ot_h}h {ot_m}m / {tg_h}h {tg_m}m")
color = '#51CF66' if ratio < 0.6 else ('#FAB005' if ratio < 1.0 else '#FA5252')
self.ot_bar.setStyleSheet(f"QProgressBar::chunk {{ background-color: {color}; }}")
else:
self.ot_label.setVisible(False)
self.ot_bar.setVisible(False)
# 일평균 (목표 시간보다 적으면 좋음)
if avg_target > 0:
self.avg_label.setVisible(True)
self.avg_bar.setVisible(True)
avg = total_h / work_days if work_days else 0
self.avg_bar.setMaximum(int(avg_target * 100))
self.avg_bar.setValue(int(min(avg, avg_target) * 100))
self.avg_bar.setFormat(f"{avg:.1f}h / {avg_target:.1f}h")
ratio = avg / avg_target if avg_target else 0
color = '#51CF66' if ratio < 0.9 else ('#FAB005' if ratio < 1.1 else '#FA5252')
self.avg_bar.setStyleSheet(f"QProgressBar::chunk {{ background-color: {color}; }}")
else:
self.avg_label.setVisible(False)
self.avg_bar.setVisible(False)