Initial commit with all project files

This commit is contained in:
mel1sg 2026-04-18 15:53:27 +03:00
parent fccc8acb63
commit 44a7ef6179
9 changed files with 590 additions and 0 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

3
.idea/.gitignore generated vendored Normal file
View File

@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml

8
.idea/Treker.iml generated Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="jdk" jdkName="Python 3.13" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

7
.idea/misc.xml generated Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Black">
<option name="sdkName" value="Python 3.13" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.13" project-jdk-type="Python SDK" />
</project>

8
.idea/modules.xml generated Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/Treker.iml" filepath="$PROJECT_DIR$/.idea/Treker.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

103
core.py Normal file
View File

@ -0,0 +1,103 @@
from datetime import date, datetime
class HarmCalculator:
harms = {
"Никотин": {"сигарета": 11, "стик": 5, "тяжка": 3},
"Алкоголь": {"бутылка (500мл)": 15, "банка (500мл)": 12, "бокал (150мл)": 8, "рюмка (50мл)": 28, "глоток (30мл)": 4, "порция (250мл)": 18},
"Кофе": {"чашка (200мл)": 1, "стакан (350мл)": 1.5, "глоток (30мл)": 0.2, "порция (200мл)": 1},
"Энергетики": {"банка (250мл)": 2, "банка (330мл)": 3, "банка (500мл)": 5},
}
icons = {"Никотин": "🚬", "Алкоголь": "🍷", "Кофе": "", "Энергетики": ""}
colors = {"Никотин": "#8fb0d9", "Алкоголь": "#f0a2bc", "Кофе": "#c8a07a", "Энергетики": "#7ea8ff"}
gender_mult = {"Мужской": 1.0, "Женский": 1.2, "М": 1.0, "Ж": 1.2}
def __init__(self, gender="Мужской", age=20):
self.gender = gender
self.age = age
def set_profile(self, gender, age):
self.gender = gender
self.age = age
def age_mult(self, age):
if age < 18: return 0.9
if age < 25: return 0.95
if age < 40: return 1.0
if age < 60: return 1.2
return 1.5
def calc_one(self, kind, unit, qty):
base = self.harms[kind][unit]
g = self.gender_mult.get(self.gender, 1.0)
a = self.age_mult(self.age)
return base * qty * g * a
def calc_total(self, hist):
total = 0
for e in hist:
total += self.calc_one(e["kind"], e["unit"], e["qty"])
return total
def fmt_time(self, mins):
if mins < 60: return f"{int(mins)} мин"
if mins < 1440: return f"{mins/60:.1f} ч"
if mins < 43200: return f"{mins/1440:.2f} дн"
return f"{mins/43200:.2f} мес"
def fmt_timer(self, mins):
h = int(mins // 60)
m = int(mins % 60)
s = int((mins * 60) % 60)
return f"{h:02d}:{m:02d}:{s:02d}"
def get_units(self, kind):
return list(self.harms.get(kind, {}).keys())
def get_config(self, kind):
return {
"icon": self.icons.get(kind, "📌"),
"color": self.colors.get(kind, "#cccccc"),
"units": self.get_units(kind),
}
def period_harm(self, hist, period="day"):
today = date.today()
filt = []
for e in hist:
d = datetime.fromisoformat(e["date"]).date()
if period == "day" and d == today:
filt.append(e)
elif period == "month" and d.year == today.year and d.month == today.month:
filt.append(e)
elif period == "year" and d.year == today.year:
filt.append(e)
elif period == "all":
filt.append(e)
return self.calc_total(filt), len(filt)
def days_with_harms(self, hist, year, month):
days = {}
for e in hist:
d = datetime.fromisoformat(e["date"]).date()
if d.year == year and d.month == month:
h = self.calc_one(e["kind"], e["unit"], e["qty"])
days[d.day] = days.get(d.day, 0) + h
return days
def summary(self, hist):
day = self.period_harm(hist, "day")
month = self.period_harm(hist, "month")
year = self.period_harm(hist, "year")
allt = self.period_harm(hist, "all")
return {
"day": {"mins": day[0], "fmt": self.fmt_time(day[0]), "cnt": day[1]},
"month": {"mins": month[0], "fmt": self.fmt_time(month[0]), "cnt": month[1]},
"year": {"mins": year[0], "fmt": self.fmt_time(year[0]), "cnt": year[1]},
"all": {"mins": allt[0], "fmt": self.fmt_time(allt[0]), "cnt": allt[1]},
}
def to_days(self, mins):
return mins / 1440

449
main.py Normal file
View File

@ -0,0 +1,449 @@
import calendar
from datetime import date
import tkinter as tk
from tkinter import ttk
from core import HarmCalculator
bg = "#eef6ff"
surf = "#ffffff"
surf_alt = "#f6fbff"
head = "#dcebff"
acc = "#4f8df7"
acc_dark = "#3e73cc"
acc_soft = "#d6e9ff"
txt = "#15314a"
muted = "#6b86a1"
bord = "#c8dcf4"
harm_col = "#ff6b6b"
def_habits = [
{"kind": "Алкоголь", "title": "Вечер", "qty": 2, "unit": "бутылка (500мл)", "icon": "🍷", "accent": "#f0a2bc"},
{"kind": "Никотин", "title": "Перерыв", "qty": 4, "unit": "сигарета", "icon": "🚬", "accent": "#8fb0d9"},
{"kind": "Кофе", "title": "Утренний кофе", "qty": 1, "unit": "чашка (200мл)", "icon": "", "accent": "#c8a07a"},
]
class MainWindow(tk.Tk):
def __init__(self):
super().__init__()
self.title("Трекер вредных привычек")
self.geometry("430x780")
self.minsize(370, 700)
self.configure(bg=bg)
self.calc = HarmCalculator(gender="Мужской", age=30)
self.p_name = "Пользователь"
self.p_gender = "Мужской"
self.p_age = 30
self.sett_win = None
self.cal_win = None
self.hab_win = None
self.edit_idx = None
self.cur_date = date.today().replace(day=1)
self.habits = list(def_habits)
self._styles()
self._ui()
self._upd_timer()
def _styles(self):
self.style = ttk.Style(self)
self.style.theme_use("clam")
self.style.configure("Light.Vertical.TScrollbar", troughcolor=bg, background="#9ec5fb", bordercolor=bg, arrowcolor=acc, relief="flat", width=7)
self.style.configure("Light.TCombobox", fieldbackground=surf, foreground=txt, arrowcolor=acc, bordercolor=bord, padding=6)
def _ui(self):
root = tk.Frame(self, bg=bg)
root.pack(fill="both", expand=True)
self._top_bar(root)
self._center(root)
self._list(root)
def _circle(self, parent, text, cmd, size=36, fill=acc, outline=acc):
c = tk.Canvas(parent, width=size, height=size, bg=parent.cget("bg"), highlightthickness=0, cursor="hand2")
def draw(_=None):
c.delete("all")
c.create_oval(1, 1, size-1, size-1, fill=fill, outline=outline)
c.create_text(size/2, size/2, text=text, fill="white", font=("Helvetica", 14, "bold"))
c.bind("<Configure>", draw)
draw()
c.bind("<Button-1>", lambda e: cmd())
return c
def _top_bar(self, parent):
bar = tk.Frame(parent, bg=head, padx=18, pady=16)
bar.pack(fill="x")
self.p_label = tk.Label(bar, text=self.p_name, bg=head, fg=txt, font=("Helvetica", 16, "bold"))
self.p_label.pack(side="left")
btns = tk.Frame(bar, bg=head)
btns.pack(side="right")
self._circle(btns, "🗓", self._cal, 36).pack(side="left", padx=(0, 10))
self._circle(btns, "", self._sett, 36).pack(side="left")
def _center(self, parent):
c = tk.Frame(parent, bg=bg, pady=14)
c.pack(fill="x")
w = tk.Frame(c, bg=bg)
w.pack(pady=8)
self.circ = tk.Canvas(w, width=200, height=200, bg=bg, highlightthickness=0)
self.circ.pack()
self._draw_circ(self.circ, 100, 100, 72)
self.circ.bind("<Button-1>", lambda e: self._open_form())
self.timer = tk.Label(c, text="00:00:00", bg=bg, fg=txt, font=("Helvetica", 28, "bold"))
self.timer.pack(pady=2)
tk.Label(c, text="потеряно за сегодня", bg=bg, fg=muted, font=("Helvetica", 10)).pack(pady=6)
def _list(self, parent):
s = tk.Frame(parent, bg=bg, padx=18, pady=10)
s.pack(fill="both", expand=True)
tk.Label(s, text="Сегодня Вы употребляли", bg=bg, fg=txt, font=("Helvetica", 14, "bold"), anchor="w").pack(fill="x", pady=(0, 12))
w = tk.Frame(s, bg=bg)
w.pack(fill="both", expand=True)
self.canv = tk.Canvas(w, bg=bg, highlightthickness=0)
scr = ttk.Scrollbar(w, orient="vertical", command=self.canv.yview, style="Light.Vertical.TScrollbar")
self.canv.configure(yscrollcommand=scr.set)
scr.pack(side="right", fill="y")
self.canv.pack(side="left", fill="both", expand=True)
self.inner = tk.Frame(self.canv, bg=bg)
self.canv.create_window((0, 0), window=self.inner, anchor="nw")
self.inner.bind("<Configure>", lambda e: self.canv.configure(scrollregion=self.canv.bbox("all")))
self.canv.bind("<Configure>", lambda e: self.canv.itemconfig(self.canv.find_withtag("win")[0], width=e.width) if self.canv.find_withtag("win") else None)
self.canv.create_window((0, 0), window=self.inner, anchor="nw", tags="win")
self._render()
def _render(self):
for w in self.inner.winfo_children():
w.destroy()
for i, h in enumerate(self.habits):
self._card(self.inner, h, i)
def _card(self, parent, habit, idx):
card = tk.Canvas(parent, height=78, bg=bg, highlightthickness=0)
card.pack(fill="x", pady=7)
card.bind("<Configure>", lambda e, c=card: self._draw_card(c, e.width, habit))
self._draw_card(card, 380, habit)
card.bind("<Button-1>", lambda e, i=idx: self._open_form(i))
def _draw_card(self, c, w, h):
c.delete("all")
self._round(c, 0, 0, w, 78, 20, fill=surf, outline=bord)
c.create_oval(18, 15, 66, 63, fill=h["accent"], outline="")
c.create_text(42, 39, text=h["icon"], fill="white", font=("Helvetica", 17, "bold"))
c.create_text(78, 28, anchor="w", text=h["title"], fill=txt, font=("Helvetica", 12, "bold"))
c.create_text(78, 50, anchor="w", text=h["kind"], fill=muted, font=("Helvetica", 10))
c.create_text(w-18, 39, anchor="e", text=f'{h["qty"]} {h["unit"]}', fill=acc_dark, font=("Helvetica", 12, "bold"))
def _round(self, c, x1, y1, x2, y2, r, **kw):
pts = [x1+r, y1, x2-r, y1, x2, y1, x2, y1+r, x2, y2-r, x2, y2, x2-r, y2, x1+r, y2, x1, y2, x1, y2-r, x1, y1+r, x1, y1]
c.create_polygon(pts, smooth=True, splinesteps=24, **kw)
def _draw_circ(self, c, cx, cy, r):
c.create_oval(cx-r-9, cy-r-9, cx+r+9, cy+r+9, fill="#dcecff", outline="")
c.create_oval(cx-r, cy-r, cx+r, cy+r, fill="white", outline=bord, width=2)
c.create_oval(cx-r+12, cy-r+12, cx-r+34, cy-r+34, fill=acc_soft, outline="")
c.create_text(cx, cy, text="+", fill=acc, font=("Helvetica", 26, "bold"))
def _upd_timer(self):
total = sum(self.calc.calc_one(h["kind"], h["unit"], h["qty"]) for h in self.habits)
self.timer.config(text=self.calc.fmt_timer(total))
def _day_stat(self):
total = sum(self.calc.calc_one(h["kind"], h["unit"], h["qty"]) for h in self.habits)
return total, len(self.habits)
def _month_stat(self):
d, c = self._day_stat()
return d * 30, c * 30
def _year_stat(self):
d, c = self._day_stat()
return d * 365, c * 365
def _open_form(self, idx=None):
if self.hab_win and self.hab_win.winfo_exists():
self.hab_win.destroy()
self.edit_idx = idx
data = self.habits[idx] if idx is not None else None
win = tk.Toplevel(self)
win.title("Привычка" if idx is None else "Редактировать")
win.configure(bg=bg)
win.geometry("450x520")
win.resizable(False, False)
win.transient(self)
win.grab_set()
self.hab_win = win
c = tk.Frame(win, bg=bg, padx=18, pady=18)
c.pack(fill="both", expand=True)
tk.Label(c, text="Привычка" if idx is None else "Редактировать", bg=bg, fg=txt, font=("Helvetica", 16, "bold")).pack(anchor="w", pady=(0, 16))
tk.Label(c, text="Тип", bg=bg, fg=muted, font=("Helvetica", 10, "bold")).pack(anchor="w")
self.kind_var = tk.StringVar(value=data["kind"] if data else "Никотин")
kind_cb = ttk.Combobox(c, textvariable=self.kind_var, values=list(self.calc.harms.keys()), state="readonly", style="Light.TCombobox")
kind_cb.pack(fill="x", ipady=4, pady=(0, 12))
kind_cb.bind("<<ComboboxSelected>>", self._kind_chg)
tk.Label(c, text="Единица", bg=bg, fg=muted, font=("Helvetica", 10, "bold")).pack(anchor="w")
self.unit_var = tk.StringVar()
self.unit_cb = ttk.Combobox(c, textvariable=self.unit_var, state="readonly", style="Light.TCombobox")
self.unit_cb.pack(fill="x", ipady=4, pady=(0, 12))
self.title_en = self._entry(c, "Название", data["title"] if data else "")
self.qty_en = self._entry(c, "Количество", str(data["qty"]) if data else "1")
self.qty_en.bind("<KeyRelease>", lambda e: self._prev())
self.prev = tk.Label(c, text="", bg=bg, fg=harm_col, font=("Helvetica", 10, "bold"))
self.prev.pack(anchor="w", pady=6)
self.err = tk.Label(c, text="", bg=bg, fg="#ef6f8a", font=("Helvetica", 10))
self.err.pack(anchor="w", pady=(10, 10))
self._upd_units()
if data:
self.unit_var.set(data["unit"])
self._prev()
if idx is None:
tk.Button(c, text="Добавить", command=self._save, bg=acc, fg="white", font=("Helvetica", 12, "bold"), relief="flat", padx=20, pady=10).pack(fill="x")
else:
row = tk.Frame(c, bg=bg)
row.pack(fill="x")
self._circle(row, "", self._save, 42, acc).pack(side="left")
self._circle(row, "", self._del, 42, "#ef6f8a").pack(side="right")
def _entry(self, parent, label, init):
f = tk.Frame(parent, bg=bg)
f.pack(fill="x", pady=(0, 12))
tk.Label(f, text=label, bg=bg, fg=muted, font=("Helvetica", 10, "bold")).pack(anchor="w")
e = tk.Entry(f, bg=surf, fg=txt, relief="flat", font=("Helvetica", 11), highlightthickness=1, highlightbackground=bord, highlightcolor=acc)
e.pack(fill="x", ipady=8)
e.insert(0, init)
return e
def _upd_units(self):
units = self.calc.get_units(self.kind_var.get())
self.unit_cb['values'] = units
if units:
self.unit_var.set(units[0])
def _kind_chg(self, e=None):
self._upd_units()
self._prev()
def _prev(self):
try:
q = float(self.qty_en.get().strip() if self.qty_en else "1")
k = self.kind_var.get()
u = self.unit_var.get()
if k and u:
h = self.calc.calc_one(k, u, q)
self.prev.config(text=f"⚠️ Потеря: {self.calc.fmt_time(h)} жизни")
except:
self.prev.config(text="⚠️ Введите число")
def _save(self):
k = self.kind_var.get()
u = self.unit_var.get()
t = self.title_en.get().strip()
try:
q = float(self.qty_en.get().strip())
except:
self.err.config(text="Количество — число.")
return
if not t:
t = k
cfg = self.calc.get_config(k)
self.err.config(text="")
pay = {"kind": k, "title": t, "qty": q, "unit": u, "icon": cfg["icon"], "accent": cfg["color"]}
if self.edit_idx is None:
self.habits.append(pay)
else:
self.habits[self.edit_idx] = pay
self._render()
self._upd_timer()
self._close()
h = self.calc.calc_one(k, u, q)
self._notify(k, q, u, h)
def _notify(self, k, q, u, h):
pop = tk.Toplevel(self)
pop.title("⚠️ Вред")
pop.configure(bg="#fff3e0")
pop.geometry("350x160")
pop.resizable(False, False)
pop.transient(self)
pop.update_idletasks()
x = self.winfo_x() + (self.winfo_width() - 350) // 2
y = self.winfo_y() + (self.winfo_height() - 160) // 2
pop.geometry(f"+{x}+{y}")
ic = self.calc.icons.get(k, "⚠️")
tk.Label(pop, text=f"{ic} {k}", font=("Helvetica", 14, "bold"), bg="#fff3e0", fg="#e65100").pack(pady=(20, 5))
tk.Label(pop, text=f"{q} {u}", font=("Helvetica", 12), bg="#fff3e0", fg=txt).pack()
tk.Label(pop, text=f"Потеряно: {self.calc.fmt_time(h)} жизни", font=("Helvetica", 12, "bold"), bg="#fff3e0", fg=harm_col).pack(pady=(10, 0))
pop.after(3000, pop.destroy)
def _del(self):
if self.edit_idx is not None and 0 <= self.edit_idx < len(self.habits):
del self.habits[self.edit_idx]
self._render()
self._upd_timer()
self._close()
def _close(self):
if self.hab_win:
self.hab_win.destroy()
self.hab_win = None
self.edit_idx = None
def _sett(self):
if self.sett_win and self.sett_win.winfo_exists():
self.sett_win.focus_set()
return
win = tk.Toplevel(self)
win.title("Настройки")
win.configure(bg=bg)
win.geometry("400x380")
win.resizable(False, False)
win.transient(self)
win.grab_set()
self.sett_win = win
c = tk.Frame(win, bg=bg, padx=18, pady=18)
c.pack(fill="both", expand=True)
tk.Label(c, text="Профиль", bg=bg, fg=txt, font=("Helvetica", 16, "bold")).pack(anchor="w", pady=(0, 16))
self.name_en = self._entry(c, "Имя", "" if self.p_name == "Пользователь" else self.p_name)
tk.Label(c, text="Пол", bg=bg, fg=muted, font=("Helvetica", 10, "bold")).pack(anchor="w")
self.gen_var = tk.StringVar(value=self.p_gender)
ttk.Combobox(c, textvariable=self.gen_var, values=["Мужской", "Женский"], state="readonly", style="Light.TCombobox").pack(fill="x", ipady=4, pady=(0, 12))
self.age_en = self._entry(c, "Возраст", str(self.p_age))
tk.Button(c, text="Сохранить", command=self._save_prof, bg=acc, fg="white", font=("Helvetica", 12, "bold"), relief="flat", padx=20, pady=10).pack(fill="x", pady=(8, 0))
def _save_prof(self):
n = self.name_en.get().strip()
self.p_name = n or "Пользователь"
self.p_gender = self.gen_var.get()
try:
self.p_age = int(self.age_en.get().strip())
except:
self.p_age = 20
self.calc.set_profile(self.p_gender, self.p_age)
self.p_label.config(text=self.p_name)
self._upd_timer()
if self.sett_win:
self.sett_win.destroy()
self.sett_win = None
def _cal_title(self):
m = ["Январь", "Февраль", "Март", "Апрель", "Май", "Июнь", "Июль", "Август", "Сентябрь", "Октябрь", "Ноябрь", "Декабрь"]
return f"{m[self.cur_date.month - 1]} {self.cur_date.year}"
def _cal(self):
if self.cal_win and self.cal_win.winfo_exists():
self.cal_win.focus_set()
return
win = tk.Toplevel(self)
win.title("Календарь")
win.configure(bg=bg)
win.geometry("460x600")
win.resizable(False, False)
win.transient(self)
win.grab_set()
self.cal_win = win
h = tk.Frame(win, bg=head, padx=18, pady=18)
h.pack(fill="x")
nav = tk.Frame(h, bg=head)
nav.pack(fill="x")
self._circle(nav, "", self._prev_m, 30).pack(side="left")
self.cal_lbl = tk.Label(nav, text=self._cal_title(), bg=head, fg=txt, font=("Helvetica", 14, "bold"))
self.cal_lbl.pack(side="left", expand=True)
self._circle(nav, "", self._next_m, 30).pack(side="right")
body = tk.Frame(win, bg=bg, padx=18, pady=4)
body.pack(fill="both", expand=True)
self.grid = tk.Frame(body, bg=surf)
self.grid.pack(fill="x", pady=(0, 10))
sf = tk.Frame(body, bg=surf)
sf.pack(fill="x", pady=5)
si = tk.Frame(sf, bg=surf, padx=12, pady=12)
si.pack(fill="x")
tk.Label(si, text="📊 Статистика", bg=surf, fg=txt, font=("Helvetica", 12, "bold"), anchor="w").pack(fill="x", pady=(0, 8))
self.day_lbl = tk.Label(si, text="", bg=surf, fg=muted, font=("Helvetica", 10), anchor="w")
self.day_lbl.pack(fill="x")
self.mon_lbl = tk.Label(si, text="", bg=surf, fg=muted, font=("Helvetica", 10), anchor="w")
self.mon_lbl.pack(fill="x")
self.yr_lbl = tk.Label(si, text="", bg=surf, fg=muted, font=("Helvetica", 10), anchor="w")
self.yr_lbl.pack(fill="x")
self.cht = tk.Text(body, bg=surf, fg=txt, font=("Courier", 9), height=10, relief="flat", borderwidth=0)
self.cht.pack(fill="both", expand=True, pady=5)
self._refresh()
def _upd_period(self):
d, dc = self._day_stat()
m, mc = self._month_stat()
y, yc = self._year_stat()
self.day_lbl.config(text=f"📅 Сегодня: {self.calc.fmt_time(d)} ({dc} пр.)")
self.mon_lbl.config(text=f"📆 Месяц: {self.calc.fmt_time(m)} (×30)")
self.yr_lbl.config(text=f"📈 Год: {self.calc.fmt_time(y)} (×365)")
def _draw_cht(self):
self.cht.delete("1.0", tk.END)
self.cht.insert(tk.END, f"{self._cal_title()}:\n\n")
d, _ = self._day_stat()
bar = "" * min(30, int(d/10)) + "" * (30 - min(30, int(d/10)))
self.cht.insert(tk.END, f"Сегодня: {bar} {self.calc.fmt_time(d)}\n")
def _refresh(self):
if not self.cal_win: return
self.cal_lbl.config(text=self._cal_title())
self._draw_grid(self.grid, self.cur_date.year, self.cur_date.month)
self._upd_period()
self._draw_cht()
def _draw_grid(self, p, y, m):
for w in p.winfo_children():
w.destroy()
today = date.today()
dm, _ = self._day_stat()
for col, day in enumerate(["Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Вс"]):
tk.Label(p, text=day, bg=surf, fg=muted, font=("Helvetica", 10, "bold"), width=4, pady=6).grid(row=0, column=col, sticky="nsew")
for r, week in enumerate(calendar.monthcalendar(y, m), 1):
for c, day in enumerate(week):
if day == 0:
cell = tk.Label(p, text="", bg=surf, width=4, height=2)
else:
is_t = (today.year == y and today.month == m and today.day == day)
if is_t and dm > 0:
if dm > 120: bg_c, fg_c = "#ff4444", "white"
elif dm > 60: bg_c, fg_c = "#ff7777", "white"
else: bg_c, fg_c = "#ffaaaa", txt
elif is_t:
bg_c, fg_c = acc, "white"
else:
bg_c, fg_c = surf_alt, txt
cell = tk.Label(p, text=str(day), bg=bg_c, fg=fg_c, font=("Helvetica", 10, "bold"), width=4, height=2)
cell.grid(row=r, column=c, padx=3, pady=3, sticky="nsew")
for i in range(7):
p.grid_columnconfigure(i, weight=1)
def _prev_m(self):
y, m = self.cur_date.year, self.cur_date.month - 1
if m == 0: m, y = 12, y - 1
self.cur_date = date(y, m, 1)
self._refresh()
def _next_m(self):
y, m = self.cur_date.year, self.cur_date.month + 1
if m == 13: m, y = 1, y + 1
self.cur_date = date(y, m, 1)
self._refresh()
if __name__ == "__main__":
app = MainWindow()
app.mainloop()