-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathwidget.py
More file actions
137 lines (114 loc) · 3.26 KB
/
widget.py
File metadata and controls
137 lines (114 loc) · 3.26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
import tkinter as tk
from datetime import date
import json
import os
# ==================================================
# CONFIG
# ==================================================
TOTAL_DAYS = 30
# State file lives next to this script
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
STATE_FILE = os.path.join(SCRIPT_DIR, "state.json")
# ==================================================
# REMEMBERED STATE
# ==================================================
state = {}
if os.path.exists(STATE_FILE):
try:
with open(STATE_FILE, "r") as f:
content = f.read().strip()
if content:
state = json.loads(content)
except Exception:
state = {}
# Initialize required fields safely
if "start_date" not in state:
state["start_date"] = date.today().isoformat()
if "x" not in state:
state["x"] = 120
if "y" not in state:
state["y"] = 120
# Always write back a valid state file
with open(STATE_FILE, "w") as f:
json.dump(state, f, indent=2)
START_DATE = date.fromisoformat(state["start_date"])
WIN_X = state["x"]
WIN_Y = state["y"]
def save_position(x, y):
state["x"] = x
state["y"] = y
with open(STATE_FILE, "w") as f:
json.dump(state, f, indent=2)
def days_remaining():
elapsed = (date.today() - START_DATE).days
return max(TOTAL_DAYS - elapsed, 0)
# ==================================================
# WINDOW
# ==================================================
root = tk.Tk()
root.overrideredirect(True)
root.attributes("-topmost", True)
# Transparency (best-effort under Cinnamon)
try:
root.attributes("-alpha", 0.85)
except tk.TclError:
pass
root.geometry(f"360x130+{WIN_X}+{WIN_Y}")
root.configure(bg="#1c1f26")
# ==================================================
# DRAGGING
# ==================================================
def start_move(e):
root.x = e.x
root.y = e.y
def do_move(e):
x = e.x_root - root.x
y = e.y_root - root.y
root.geometry(f"+{x}+{y}")
save_position(x, y)
# ==================================================
# HEADER
# ==================================================
header = tk.Frame(root, bg="#232732", height=34)
header.pack(fill="x")
header.bind("<Button-1>", start_move)
header.bind("<B1-Motion>", do_move)
title = tk.Label(
header,
text="🐧 Linux Mint Challenge",
bg="#232732",
fg="#f5f7fa",
font=("Segoe UI", 11, "bold")
)
title.pack(side="left", padx=10)
close_btn = tk.Label(
header,
text="✕",
bg="#232732",
fg="#ff6b6b",
font=("Segoe UI", 11),
cursor="hand2"
)
close_btn.pack(side="right", padx=10)
close_btn.bind("<Button-1>", lambda e: root.destroy())
# ==================================================
# BODY
# ==================================================
body = tk.Frame(root, bg="#1c1f26")
body.pack(expand=True, fill="both")
main = tk.Label(
body,
text=f"{days_remaining()} days remaining",
bg="#1c1f26",
fg="#ffffff",
font=("Segoe UI", 20, "bold")
)
main.pack(expand=True)
# ==================================================
# REFRESH
# ==================================================
def refresh():
main.config(text=f"{days_remaining()} days remaining")
root.after(3_600_000, refresh) # hourly refresh
refresh()
root.mainloop()