Skip to content

Commit ea25815

Browse files
committed
fix: critial logic wipe bug fixed | tk UI impr
1 parent 5e0369b commit ea25815

5 files changed

Lines changed: 139 additions & 43 deletions

File tree

requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
# SchedPlus dependencies
22

3-
PyQt6
3+
PyQt6
4+
tkcalendar

src/logic/scheduler.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ def load_tasks(self, filepath: str = None):
8585
try:
8686
from . import storage as _storage
8787

88-
self.tasks = _storage.load_tasks(filepath)
88+
self.tasks = _storage.load_tasks(filepath or _storage.DATA_FILE)
8989
except Exception:
9090
self.tasks = []
9191

src/logic/storage.py

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,30 @@
11
import json
22
import os
3-
from .scheduler import Task # import the dataclass Task
3+
from .scheduler import Task
44

55
SCHEMA_VERSION = 1
66

7-
# Absolute / robust path relative to this file
8-
DATA_FILE = os.path.join(os.path.dirname(__file__), "..", "data", "tasks.json")
7+
DATA_FILE = os.path.abspath(
8+
os.path.join(os.path.dirname(__file__), "..", "data", "tasks.json")
9+
)
910
os.makedirs(os.path.dirname(DATA_FILE), exist_ok=True)
1011

1112

12-
1313
def save_tasks(tasks, filepath=DATA_FILE):
14-
"""
15-
Save list of Task objects to JSON file
16-
"""
14+
print("\n[SAVE] ----------------------------------------")
15+
print("[SAVE] Path:", filepath)
16+
print("[SAVE] tasks list:", tasks)
17+
print("[SAVE] tasks count:", len(tasks))
18+
print("[SAVE] file exists before save:", os.path.exists(filepath))
19+
20+
# Prevent wiping existing file with empty list
21+
if not tasks and os.path.exists(filepath):
22+
print("[SAVE] ABORTED — empty task list would overwrite existing file")
23+
return
24+
1725
data = {
1826
"version": SCHEMA_VERSION,
19-
"tasks": [t.to_dict() for t in tasks], # convert each Task → dict
27+
"tasks": [t.to_dict() for t in tasks],
2028
}
2129

2230
try:
@@ -26,23 +34,37 @@ def save_tasks(tasks, filepath=DATA_FILE):
2634

2735
with open(filepath, "w", encoding="utf-8") as f:
2836
json.dump(data, f, indent=2, ensure_ascii=False)
37+
38+
print("[SAVE] SUCCESS — wrote JSON")
2939
except Exception as e:
30-
print(f"[ERROR] Failed to save tasks: {e}")
40+
print(f"[SAVE] ERROR:", e)
3141

3242

3343
def load_tasks(filepath=DATA_FILE):
34-
"""
35-
Load JSON file → return list of Task objects
36-
"""
44+
print("\n[LOAD] ----------------------------------------")
45+
print("[LOAD] Path:", filepath)
46+
print("[LOAD] file exists:", os.path.exists(filepath))
47+
3748
if not os.path.exists(filepath):
49+
print("[LOAD] No file — returning empty list")
3850
return []
3951

4052
try:
4153
with open(filepath, "r", encoding="utf-8") as f:
42-
data = json.load(f)
54+
raw = f.read()
55+
print("[LOAD] Raw file contents:", raw)
56+
57+
data = json.loads(raw)
4358

4459
tasks_data = data.get("tasks", [])
45-
return [Task.from_dict(t) for t in tasks_data] # convert dict → Task
60+
print("[LOAD] Parsed tasks:", tasks_data)
61+
print("[LOAD] Count:", len(tasks_data))
62+
63+
tasks = [Task.from_dict(t) for t in tasks_data]
64+
print("[LOAD] Converted to Task objects:", tasks)
65+
66+
return tasks
67+
4668
except Exception as e:
47-
print(f"[ERROR] Failed to load tasks: {e}")
48-
return []
69+
print(f"[LOAD] ERROR:", e)
70+
return []

src/main.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@
99

1010
from startup.controller import boot
1111

12+
1213
if __name__ == "__main__":
1314
boot()

src/ui/tkinter_ui.py

Lines changed: 97 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""
2-
tkinter_ui.py (v0.5)
2+
tkinter_ui.py
33
--------------------
44
Improved teaching Tkinter UI for SchedPlus.
55
@@ -10,58 +10,130 @@
1010
import tkinter as tk
1111
from tkinter import ttk
1212
from ui.shortcuts import bind_enter_key
13+
from tkcalendar import Calendar
14+
from logic.scheduler import Scheduler, Task
15+
from sys import path
1316

1417

15-
def run_ui(scheduler):
18+
def run_ui(scheduler: Scheduler) -> None:
1619
root = tk.Tk()
17-
root.title("SchedPlus v0.5 (Tkinter)")
20+
root.title("SchedPlus - Basic")
1821

19-
# Center window
20-
root.geometry("420x420")
22+
# Window sizing
23+
root.geometry("530x480")
24+
root.minsize(420, 420)
25+
root.resizable(True, True)
2126

2227
# --- Main container ---
2328
container = ttk.Frame(root, padding=15)
2429
container.grid(row=0, column=0, sticky="nsew")
2530

26-
root.columnconfigure(0, weight=1)
31+
container.columnconfigure(1, weight=1)
32+
container.columnconfigure(2, weight=0)
2733
root.rowconfigure(0, weight=1)
2834

2935
# --- Heading ---
30-
heading = ttk.Label(container, text="Add a Task", font=("Segoe UI", 12, "bold"))
31-
heading.grid(row=0, column=0, columnspan=2, pady=(0, 10))
36+
ttk.Label(container, text="Add a Task", font=("Segoe UI", 12, "bold")).grid(
37+
row=0, column=0, columnspan=3, pady=(0, 10)
38+
)
3239

33-
# --- Input fields ---
40+
# --- Date ---
3441
ttk.Label(container, text="Date (YYYY-MM-DD):").grid(row=1, column=0, sticky="w", pady=3)
35-
date_entry = ttk.Entry(container, width=25)
42+
date_entry = ttk.Entry(container)
3643
date_entry.grid(row=1, column=1, sticky="ew", pady=3)
3744

45+
def open_calendar():
46+
top = tk.Toplevel(root)
47+
top.title("Select Date")
48+
49+
cal = Calendar(top, selectmode="day", date_pattern="yyyy-mm-dd")
50+
cal.pack(padx=10, pady=10)
51+
52+
def choose():
53+
date_entry.delete(0, tk.END)
54+
date_entry.insert(0, cal.get_date())
55+
top.destroy()
56+
57+
ttk.Button(top, text="Select", command=choose).pack(pady=5)
58+
59+
ttk.Button(container, text="📅", width=3, command=open_calendar).grid(
60+
row=1, column=2, padx=(5, 0)
61+
)
62+
63+
# --- Time ---
3864
ttk.Label(container, text="Time (HH:MM):").grid(row=2, column=0, sticky="w", pady=3)
39-
time_entry = ttk.Entry(container, width=25)
65+
time_entry = ttk.Entry(container)
4066
time_entry.grid(row=2, column=1, sticky="ew", pady=3)
4167

68+
def open_time_picker():
69+
top = tk.Toplevel(root)
70+
top.title("Select Time")
71+
72+
frame = ttk.Frame(top, padding=10)
73+
frame.grid(row=1, column=1)
74+
75+
ttk.Label(frame, text="Hour:").grid(row=0, column=0, padx=5, pady=5)
76+
hour_spin = ttk.Spinbox(frame, from_=0, to=23, width=3, format="%02.0f")
77+
hour_spin.grid(row=0, column=1, padx=5, pady=5)
78+
79+
ttk.Label(frame, text="Minute:").grid(row=1, column=0, padx=5, pady=5)
80+
minute_spin = ttk.Spinbox(frame, from_=0, to=59, width=3, format="%02.0f")
81+
minute_spin.grid(row=1, column=1, padx=5, pady=5)
82+
83+
def choose():
84+
time_entry.delete(0, tk.END)
85+
time_entry.insert(0, f"{hour_spin.get()}:{minute_spin.get()}")
86+
top.destroy()
87+
88+
ttk.Button(top, text="Select", command=choose).grid(row=1, column=0, pady=10)
89+
90+
ttk.Button(container, text="🕒", width=3, command=open_time_picker).grid(
91+
row=2, column=2, padx=(5, 0)
92+
)
93+
94+
# --- Task text ---
4295
ttk.Label(container, text="Task:").grid(row=3, column=0, sticky="w", pady=3)
43-
task_entry = ttk.Entry(container, width=25)
96+
task_entry = ttk.Entry(container)
4497
task_entry.grid(row=3, column=1, sticky="ew", pady=3)
4598

4699
# --- Add Task button ---
47100
add_button = ttk.Button(container, text="Add Task")
48-
add_button.grid(row=4, column=0, columnspan=2, pady=(10, 15))
101+
add_button.grid(row=4, column=0, columnspan=3, pady=(10, 15))
49102

50-
# --- Task list frame ---
103+
# --- Task list ---
51104
list_frame = ttk.LabelFrame(container, text="Tasks", padding=10)
52-
list_frame.grid(row=5, column=0, columnspan=2, sticky="nsew")
105+
list_frame.grid(row=5, column=0, columnspan=3, sticky="nsew")
53106

54-
container.rowconfigure(5, weight=1)
55-
list_frame.rowconfigure(0, weight=1)
56-
list_frame.columnconfigure(0, weight=1)
107+
# --- Task list (Treeview) ---
108+
columns = ("date", "time", "text")
109+
110+
task_list = ttk.Treeview(
111+
list_frame,
112+
columns=columns,
113+
show="headings",
114+
height=10
115+
)
116+
117+
# Column headings
118+
task_list.heading("date", text="Date")
119+
task_list.heading("time", text="Time")
120+
task_list.heading("text", text="Task")
121+
122+
# Column widths
123+
task_list.column("date", width=100, anchor="center") # YYYY-MM-DD
124+
task_list.column("time", width=60, anchor="center") # HH:MM
125+
task_list.column("text", width=300, anchor="w") # expands
57126

58-
# --- Task list ---
59-
task_list = tk.Listbox(list_frame, width=50, height=10, borderwidth=1, relief="solid")
60127
task_list.grid(row=0, column=0, sticky="nsew")
61128

62-
# Populate existing tasks
63-
for task in scheduler.get_tasks():
64-
task_list.insert(tk.END, f"{task.date} {task.time} - {task.text}")
129+
# Scrollbar
130+
scrollbar = ttk.Scrollbar(list_frame, orient="vertical", command=task_list.yview)
131+
task_list.configure(yscrollcommand=scrollbar.set)
132+
scrollbar.grid(row=0, column=1, sticky="ns")
133+
134+
135+
for task in scheduler.get_tasks(): # type: Task
136+
task_list.insert("", "end", values=(task.date, task.time, task.text))
65137

66138
# --- Add Task logic ---
67139
def add_task():
@@ -72,11 +144,11 @@ def add_task():
72144
if date and time and text:
73145
scheduler.add_task(date, time, text)
74146
new_task = scheduler.get_tasks()[-1]
75-
76-
task_list.insert(tk.END, f"{new_task.date} {new_task.time} - {new_task.text}")
147+
task_list.insert("", "end", values=(new_task.date, new_task.time, new_task.text))
77148

78149
try:
79150
scheduler.save_tasks()
151+
print("Saving to:", filepath)
80152
except Exception:
81153
pass
82154

0 commit comments

Comments
 (0)