Right so, I have been making this goofy clock app in order to further my skills in programming, especially by adding an object oriented approach. I have defined a main menu of which is under main()
but I use classes to act as Frames for the different features of the app. To switch between those, I kind of had a clunky system but it worked- nearly.
"""
Goofy agh clock app to get me back into programming.
To make it harder, I limited it to only Object-Oriented programming and also uses ttkboostrap to at least make it
remotely look good.
Features:
Clock, timer (with included sounds), stop watch.
"""
from PIL import Image, ImageTk
import ttkbootstrap as ttb
from ttkbootstrap.constants import *
from datetime import *
class TimeFrame(ttb.Frame):
current_frame = True
def __init__(self, master):
self.selector = ttb.Frame(master)
# self.time_frame.grid(row=1, column=1)
now_time = datetime.now()
string_time = now_time.strftime("%H : %M : %S")
self.time_label = ttb.Label(self.selector, text=string_time,
font=("Arial Greek", 32, "bold"), bootstyle=INFO)
self.time_label.grid(row=1, column=0, padx=100)
self.update_time()
def update_time(self):
new_time = datetime.now()
new_string_time = new_time.strftime("%H: %M: %S")
self.time_label.config(text=new_string_time)
self.selector.after(1000, self.update_time)
class TimerFrame(ttb.Frame):
current_frame = False
def __init__(self, master):
self.selector = ttb.Frame(master)
self.timer_entry_frame = ttb.Frame(self.selector)
self.hour_increase = ttb.Button(self.timer_entry_frame, text="↑", bootstyle=OUTLINE)
self.minute_increase = ttb.Button(self.timer_entry_frame, text="↑", bootstyle=OUTLINE)
self.second_increase = ttb.Button(self.timer_entry_frame, text="↑", bootstyle=OUTLINE)
self.hours_entry = ttb.Entry(self.timer_entry_frame, style=PRIMARY, width=10)
self.minutes_entry = ttb.Entry(self.timer_entry_frame, style=PRIMARY, width=10)
self.seconds_entry = ttb.Entry(self.timer_entry_frame, style=PRIMARY, width=10)
self.hour_decrease = ttb.Button(self.timer_entry_frame, text="↓", bootstyle=OUTLINE)
self.minute_decrease = ttb.Button(self.timer_entry_frame, text="↓", bootstyle=OUTLINE)
self.second_decrease = ttb.Button(self.timer_entry_frame, text="↓", bootstyle=OUTLINE)
self.hour_increase.grid(row=0, column=0, padx=15, pady=5)
self.minute_increase.grid(row=0, column=1, padx=15, pady=5)
self.second_increase.grid(row=0, column=2, padx=15, pady=5)
self.hours_entry.grid(row=1, column=0, padx=15)
self.minutes_entry.grid(row=1, column=1, padx=15)
self.seconds_entry.grid(row=1, column=2, padx=15)
self.hour_decrease.grid(row=2, column=0, padx=15, pady=5)
self.minute_decrease.grid(row=2, column=1, padx=15, pady=5)
self.second_decrease.grid(row=2, column=2, padx=15, pady=5)
self.timer_entry_frame.grid(row=0, column=0, padx=50)
self.start_timer_btn = ttb.Button(self.selector, text="START", bootstyle=(SUCCESS, OUTLINE))
self.start_timer_btn.grid(row=2, column=0, padx=50, pady=50)
# ↑↓
def main():
def change_frames(new_frame):
if TimeFrame.current_frame is True:
print("First if statement responded")
TimeFrame(root).selector.grid_forget()
TimeFrame.current_frame = False
elif TimerFrame.current_frame is True:
print("Second if statement responded")
# for widget in TimerFrame(root).selector.winfo_children():
# widget.grid_forget()
TimerFrame.current_frame = False
new_frame(root).selector.grid(row=1, column=1)
new_frame.current_frame = True
print("Timer frame state", TimerFrame.current_frame, "\nNew frame calling", new_frame.current_frame)
root = ttb.Window(themename="darkly")
root.title("Clock")
root.geometry("500x500")
root.resizable(False, False)
root.iconphoto(False, ImageTk.PhotoImage(file="clock_icon.png"))
side_panel = ttb.Frame(root, width=75, height=500, bootstyle="info")
side_panel.grid(rowspan=4, column=0)
clock_image = Image.open("clock_icon.png")
resized_clock = clock_image.resize((50, 50))
timer_image = Image.open("timer_icon.png")
resized_timer = timer_image.resize((50, 50))
used_clock_image = ImageTk.PhotoImage(resized_clock)
used_timer_image = ImageTk.PhotoImage(resized_timer)
clock_button = ttb.Button(root, image=used_clock_image, bootstyle=INFO, command=lambda: change_frames(TimeFrame))
clock_button.image = used_clock_image
clock_button.grid(row=0, column=0)
timer_button = ttb.Button(root, image=used_timer_image, bootstyle=INFO, command=lambda: change_frames(TimerFrame))
timer_button.image = used_timer_image
timer_button.grid(row=1, column=0)
# Testing button
# destroybtn = ttb.Button(root, text="delete", command=lambda: TimeFrame(root).selector.grid_forget())
# destroybtn.grid(row=2, column=0)
TimeFrame(root).selector.grid(row=1, column=1)
root.mainloop()
if __name__ == '__main__':
main()
I had a common class attribute which dictates the state of the previous frame of which I use a function in main() to manage the frames based on what I clicked on the side menu and it checks over which frames are existing. It works for when I need to move from the time to the timer menu, but when I go back, the .grid_forget() does not remove the timer widgets and so both of them are just clunked together in the same window.
I did try to use wgetinfo_children, using a loop to go over it with a looping variable of widget, doing the .grid_forget() function on each widget, but it still did not work!
Also, is there a better way of managing frames than this? I am fairly sure this is clunky but I don't have a solid idea on where I could research for scenarios similar to this.
Thank you in advance.
solved, I set all the specified objects under a variable name and called that variable instead of calling potential new instances of the object.