-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmytodos.py
229 lines (181 loc) · 9.54 KB
/
mytodos.py
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
# Modules
import customtkinter
from CTkMessagebox import CTkMessagebox
from CTkListbox import *
import pickle
import os
# myApp setting & more..
customtkinter.set_appearance_mode("Dark")
customtkinter.set_default_color_theme("Dark-Theme.json")
myApp = customtkinter.CTk()
myApp.title("MyTodos")
myApp.geometry(f"{800}x{600}")
myApp.resizable(False, False)
# Fonts
tite_font = customtkinter.CTkFont(family="Montserrat",size=60,weight="bold")
todos_font = customtkinter.CTkFont(family="Roboto",size=21,weight="bold")
todos_font_checked = customtkinter.CTkFont(family="Roboto",size=25,weight="bold", overstrike=True)
# (List) -->Store todos data
todos_data = []
# ---FUNCTIONS---
# ---> Create todos_frame for each todo
def create_frame():
# Length of child_widgets
num_children = len(todos_listbox.winfo_children())
row_num = num_children
todos_frame = customtkinter.CTkFrame(master=todos_listbox, height=350, border_color="#586b78", border_width=1)
todos_frame.grid(row=row_num, column=0, padx=5, pady=5)
return todos_frame
# ---> Add todo to listbox(can also use scrollableframe)
def add_todo():
todo = todos_entry.get()
if todo:
todos_frame = create_frame()
todos_checkbox = customtkinter.CTkCheckBox(master=todos_frame, text=todo, font=todos_font, width=555, height=50, checkbox_height=50, checkbox_width=50)
todos_checkbox.configure(command=lambda cb=todos_checkbox: checked(cb))
todos_checkbox.grid(row=0, column=0, pady=5, padx=5, sticky="nsew")
todos_data.append((todo, todos_checkbox, todos_frame))
edit_button = customtkinter.CTkButton(master=todos_frame, text="Edit", command=lambda index=len(todos_data)-1: edit_todo(index),
font=todos_font, corner_radius=8, width=75, height=50, cursor="hand2")
edit_button.grid(row=0, column=1, pady=5, padx=5, sticky="nswe")
remove_button = customtkinter.CTkButton(master=todos_frame, text="Remove", fg_color="#a70f0f", hover_color="#e00000", command=lambda frame=todos_frame: remove_todo(frame),
font=todos_font, corner_radius=8, width=75, height=50, cursor="hand2")
remove_button.grid(row=0, column=2, pady=5, padx=5, sticky="nswe")
todos_entry.delete(0, customtkinter.END)
show_progress()
save_todos()
else:
# Error MsgBox
error_msg = CTkMessagebox(title="MyTodos-Error",
message="Check you! Add a todo.",
fg_color="#000000",
icon="cancel",
width=300, height=150)
error_msg = error_msg.get()
# ---> Cross/Uncross when todo is checked/done
def checked(todos_checkbox):
checkbox_state = todos_checkbox.get()
if checkbox_state:
todos_checkbox.configure(font=todos_font_checked)
else:
todos_checkbox.configure(font=todos_font)
show_progress()
save_todos()
# ---> Remove todo & destroy parent_frame
def remove_todo(todos_frame):
# Locate Index to remove & do notthing if not found
for index, (todo, todos_checkbox, todos_frame) in enumerate(todos_data):
if todos_frame:
todos_data.pop(index)
break
else:
return
# Check for unmarked & prompt to cancel or proceed
if not todos_checkbox.get():
# Confirmation MsgBox
response = CTkMessagebox(title="MyTodos-Confirm",
message="Check you! todo's not done. Remove anyway?",
fg_color="#000000",
icon="warning",
option_1="Yes", option_2="No",
width=300, height=150).get()
# Remove frame if yes from todos_data
if response == "Yes":
todos_frame.destroy()
# Refresh & reposition remaining frames, & update progress_button
for i, (todo, todos_checkbox, todos_frame) in enumerate(todos_data):
todos_frame.grid(row=i, column=0, padx=5, pady=5)
show_progress()
save_todos()
# ---> Edit todo
def edit_todo(index):
current_todo, todos_checkbox, todos_frame = todos_data[index]
new_todo = customtkinter.CTkInputDialog(text="Edit Todo here:", title="MyTodos-Editor").get_input()
if new_todo:
todos_checkbox.configure(text=new_todo)
todos_data[index] = (new_todo, todos_checkbox, todos_frame)
save_todos()
# ---> Remove all done todos
def remove_done_todos():
# Track done_todos
done_todos = False
# Reverse iterate, del marked checkbox's master & update data
for i in range(len(todos_data) - 1, -1, -1):
todo, todos_checkbox, todos_frame = todos_data[i]
if todos_checkbox.get():
todos_frame.destroy()
todos_data.pop(i)
done_todos = True
# Refresh & reposition remaining frames
for i, (todo, todos_checkbox, todos_frame) in enumerate(todos_data):
todos_frame.grid(row=i, column=0, padx=5, pady=5)
# Msgbox if no done & empty listbox
if not done_todos:
msg= "Oops! There's no todos yet!" if not todos_data else "Oops! There's no completed todos yet!"
CTkMessagebox(title="MyTodos-Info",
message=msg,
fg_color="#000000",
icon="info",
width=300, height=150).get()
show_progress()
save_todos()
# ---> save todos in txt file -todos.txt (use pickle or sqlite)
def save_todos():
with open("todos.txt", "wb") as file:
todos_to_save = [(todo, todos_checkbox.get()) for todo, todos_checkbox, todos_frame in todos_data]
pickle.dump(todos_to_save, file)
# ---> Load todos from saved txt file
def load_todos():
if os.path.exists("todos.txt"):
with open("todos.txt", "rb") as file:
todos_to_load = pickle.load(file)
for todo, is_checked in todos_to_load:
todos_frame = create_frame()
todos_checkbox = customtkinter.CTkCheckBox(master=todos_frame, text=todo, font=todos_font, width=555, height=50, checkbox_height=50, checkbox_width=50)
todos_checkbox.configure(command=lambda cb=todos_checkbox: checked(cb))
todos_checkbox.grid(row=0, column=0, pady=5, padx=5, sticky="nsew")
if is_checked:
todos_checkbox.select()
todos_checkbox.configure(font=todos_font_checked)
todos_data.append((todo, todos_checkbox, todos_frame))
edit_button = customtkinter.CTkButton(master=todos_frame, text="Edit", command=lambda index=len(todos_data)-1: edit_todo(index),
font=todos_font, corner_radius=8, width=75, height=50, cursor="hand2")
edit_button.grid(row=0, column=1, pady=5, padx=5, sticky="nswe")
remove_button = customtkinter.CTkButton(master=todos_frame, text="Remove", fg_color="#a70f0f", hover_color="#e00000", command=lambda frame=todos_frame: remove_todo(frame),
font=todos_font, corner_radius=8, width=75, height=50, cursor="hand2")
remove_button.grid(row=0, column=2, pady=5, padx=5, sticky="nswe")
# # ---> Show todos_progress, update dynamically
def show_progress():
total_todos = len(todos_data)
done_todos = sum(todo_checkbox.get() for todo, todo_checkbox, todo_frame in todos_data)
progress_button.configure(text=f"{done_todos} of {total_todos} done")
# ---WIDGETS---
# Frames
entry_label_frame = customtkinter.CTkFrame(myApp, height=300)
entry_label_frame.pack(side="top", expand=True, fill="both", padx=5, pady=5)
todos_listbox = CTkListbox(myApp,
width=800,
height=350,
border_color="#586b78",
border_width=2)
todos_listbox.pack(expand=True, fill="both", padx=5, pady=5)
control_progress_frame = customtkinter.CTkFrame(myApp, height=100)
control_progress_frame.pack(side="bottom", expand=True, fill="both", padx=5, pady=5)
# myApp name/label
myApp.name = customtkinter.CTkLabel(master=entry_label_frame, text="MyTodos", font=tite_font, anchor="center", cursor="hand2")
myApp.name.grid(row=0, column=0, pady=10, padx=10, sticky="nswe", columnspan=5)
# Todos Entry & Buttons
todos_entry = customtkinter.CTkEntry(master=entry_label_frame, font=todos_font, placeholder_text="Type a ToDo here...", corner_radius=8, width=670, height=50)
todos_entry.grid(row=1, column=0, pady=5, padx=5, sticky="nswe")
add_button = customtkinter.CTkButton(master=entry_label_frame, text="Add", command=add_todo, font=todos_font, corner_radius=8, width=100, height=50, cursor="hand2")
add_button.grid(row=1, column=1, pady=5, padx=5, sticky="nswe")
progress_button = customtkinter.CTkButton(master=control_progress_frame, fg_color="#32CD32", text=f"{0} of {0} done", command=show_progress, font=todos_font, corner_radius=8, height=50, width=300, cursor="hand2", state="disabled")
progress_button.pack(side="left", pady=5, padx=10)
remove_all_button = customtkinter.CTkButton(master=control_progress_frame, text="Remove All Done", fg_color="#a70f0f", hover_color="#e00000", command=remove_done_todos, font=todos_font, corner_radius=8, height=50, width=300, cursor="hand2")
remove_all_button.pack(side="right", pady=5, padx=10)
# Load todos at startup
load_todos()
# Update progress
show_progress()
# Run myApp(as admin😁)
myApp.mainloop()