better clock comments
master
Voussoir 2015-08-12 23:13:39 -07:00
parent 27f13eed96
commit a3eb7cb410
1 changed files with 74 additions and 29 deletions

View File

@ -12,7 +12,9 @@ MODE_CLOCK = 'clock'
MODE_COUNTDOWN = 'countdown' MODE_COUNTDOWN = 'countdown'
MODE_STOPWATCH = 'stopwatch' MODE_STOPWATCH = 'stopwatch'
# Monospace fonts work best
FONT = 'Consolas' FONT = 'Consolas'
# Used for resizing the clock font to fit the frame. # Used for resizing the clock font to fit the frame.
# For consolas, 1.33333 is a good value. It may differ # For consolas, 1.33333 is a good value. It may differ
# for other fonts # for other fonts
@ -44,6 +46,9 @@ class Clock:
self.frame_applet.pack_propagate(0) self.frame_applet.pack_propagate(0)
self.frame_applet.grid_propagate(0) self.frame_applet.grid_propagate(0)
# tkinter elements belonging to any particular applet.
# They will be destroyed when switching modes.
# The dropdown and the frame_applet are not included here.
self.elements = [] self.elements = []
# Instance is used to keep track of how many times we have # Instance is used to keep track of how many times we have
@ -52,7 +57,7 @@ class Clock:
# when we leave that mode. # when we leave that mode.
self.instance = 0 self.instance = 0
self.dropstring.set(MODE_COUNTDOWN) self.dropstring.set(MODE_CLOCK)
self.t.mainloop() self.t.mainloop()
@ -60,14 +65,6 @@ class Clock:
def mode(self): def mode(self):
return self.drop.cget('text') return self.drop.cget('text')
def delete_applet_elements(self):
self.started = 0
self.frame_applet.unbind('<Configure>')
for x in range(len(self.elements)):
self.elements.pop().destroy()
self.frame_applet.rowconfigure(0, weight=0)
self.frame_applet.columnconfigure(0, weight=0)
'''clock '''clock
###### ### ##### ###### ### ### ###### ### ##### ###### ### ###
@ -77,6 +74,9 @@ class Clock:
###### ####### ##### ###### ### ### ###### ####### ##### ###### ### ###
''' '''
def build_gui_clock(self): def build_gui_clock(self):
'''
A clock using the system's local time in 24 hour format.
'''
def tick_clock(): def tick_clock():
if this_instance != self.instance: if this_instance != self.instance:
# used to break the "after" loop # used to break the "after" loop
@ -98,7 +98,7 @@ class Clock:
self.resize_widget_font(self.frame_applet, label_clock) self.resize_widget_font(self.frame_applet, label_clock)
'''count '''countdown
###### ##### ### ### ##### ####### ###### ##### ### ### ##### #######
####### ####### ### ### ####### ####### ####### ####### ### ### ####### #######
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ###
@ -106,14 +106,23 @@ class Clock:
###### ##### ##### ### ### ### ###### ##### ##### ### ### ###
''' '''
def build_gui_countdown(self): def build_gui_countdown(self):
'''
A timer with two modes:
1. "in" - countdown a certain amount of time. Ex "5 hours"
This mode can be paused and resumed and the timer will pick up where it left off
2. "until" - countdown until a certain moment. Ex "13 august 2015 15:00"
In this mode, pausing only affects the display. Resuming the clock will skip to
maintain the correct end time.
'''
def toggle_mode(): def toggle_mode():
reset_countdown() reset_countdown()
if label_countdown.mode is 'until': if label_countdown.mode is 'until':
# "in" mode does not need the dd mmm yyyy boxes
for item in elements_until: for item in elements_until:
item.grid_forget() item.grid_forget()
button_reset.grid(row=0, column=6) # "in" mode can have arbitrary hours
spinbox_hour.configure(to=999) spinbox_hour.configure(to=999)
label_countdown.mode = 'in' label_countdown.mode = 'in'
else: else:
@ -121,7 +130,6 @@ class Clock:
spinbox_month.grid(row=0, column=2) spinbox_month.grid(row=0, column=2)
spinbox_year.grid(row=0, column=3) spinbox_year.grid(row=0, column=3)
spinbox_hour.configure(to=23) spinbox_hour.configure(to=23)
#button_reset.grid_forget()
label_countdown.mode = 'until' label_countdown.mode = 'until'
button_countdownmode.configure(text=label_countdown.mode) button_countdownmode.configure(text=label_countdown.mode)
@ -151,7 +159,7 @@ class Clock:
if label_countdown.destination is None: if label_countdown.destination is None:
try: try:
d = int(spinbox_day.get()) d = int(spinbox_day.get())
mo = MONTHS_DICT[spinbox_month.get()] mo = MONTHS_DICT[spinbox_month.get().lower()]
y = int(spinbox_year.get()) y = int(spinbox_year.get())
h = int(spinbox_hour.get()) h = int(spinbox_hour.get())
m = int(spinbox_minute.get()) m = int(spinbox_minute.get())
@ -173,18 +181,18 @@ class Clock:
except ValueError: except ValueError:
return return
else: else:
# check how long we were paused, then increase the
# destination by that much so the countdown doesn't skip.
backlog = now - label_countdown.backlog backlog = now - label_countdown.backlog
#print(backlog)
label_countdown.destination += backlog label_countdown.destination += backlog
label_countdown.is_running = True label_countdown.is_running = True
button_toggle.configure(text='stop') button_toggle.configure(text='stop')
tick_countdown() tick_countdown()
def stop_countdown(): def stop_countdown():
if label_countdown.mode is 'until': # Store the timestamp when the countdown was paused
pass # so that when it resumes, we know how long we were asleep
else: label_countdown.backlog = time.time()
label_countdown.backlog = time.time()
label_countdown.is_running = False label_countdown.is_running = False
button_toggle.configure(text='start') button_toggle.configure(text='start')
@ -195,7 +203,7 @@ class Clock:
label_countdown.backlog = 0 label_countdown.backlog = 0
def toggle_countdown(): def toggle_countdown():
if label_countdown.is_running is True: if label_countdown.is_running:
stop_countdown() stop_countdown()
else: else:
start_countdown() start_countdown()
@ -220,6 +228,9 @@ class Clock:
frame_controls = tkinter.Frame(self.frame_applet, bg=COLOR_BACKGROUND) frame_controls = tkinter.Frame(self.frame_applet, bg=COLOR_BACKGROUND)
frame_spinboxes = tkinter.Frame(frame_controls, bg=COLOR_BACKGROUND) frame_spinboxes = tkinter.Frame(frame_controls, bg=COLOR_BACKGROUND)
label_countdown = tkinter.Label(frame_display, text='00:00:00.0', bg=COLOR_BACKGROUND, fg=COLOR_FONT) label_countdown = tkinter.Label(frame_display, text='00:00:00.0', bg=COLOR_BACKGROUND, fg=COLOR_FONT)
# Although this says 'until', the applet will start in
# 'in' mode because I use the toggle towards the end to
# jog everything into place.
label_countdown.mode = 'until' label_countdown.mode = 'until'
label_countdown.destination = None label_countdown.destination = None
label_countdown.backlog = 0 label_countdown.backlog = 0
@ -252,18 +263,24 @@ class Clock:
spinbox_hour.grid(row=0, column=4) spinbox_hour.grid(row=0, column=4)
spinbox_minute.grid(row=0, column=5) spinbox_minute.grid(row=0, column=5)
spinbox_second.grid(row=0, column=6) spinbox_second.grid(row=0, column=6)
button_toggle.grid(row=0, column=7) # the day month year spinboxes are gridded
# during the toggle_mode method.
button_reset.grid(row=0, column=7)
button_toggle.grid(row=0, column=8)
self.elements.append(frame_display) self.elements.append(frame_display)
self.elements.append(frame_controls)
self.elements.append(frame_spinboxes)
self.elements.append(label_countdown) self.elements.append(label_countdown)
self.elements.append(frame_controls)
self.elements.append(button_countdownmode) self.elements.append(button_countdownmode)
self.elements.append(button_toggle)
self.elements.append(button_reset)
self.elements.append(frame_spinboxes)
self.elements.append(spinbox_hour) self.elements.append(spinbox_hour)
self.elements.append(spinbox_minute) self.elements.append(spinbox_minute)
self.elements.append(spinbox_second) self.elements.append(spinbox_second)
self.elements.append(button_toggle) self.elements.append(spinbox_day)
self.elements.append(button_reset) self.elements.append(spinbox_month)
self.elements.append(spinbox_year)
elements_until.append(spinbox_day) elements_until.append(spinbox_day)
elements_until.append(spinbox_month) elements_until.append(spinbox_month)
@ -275,7 +292,7 @@ class Clock:
self.resize_widget_font(frame_display, label_countdown) self.resize_widget_font(frame_display, label_countdown)
'''stop '''stopwatch
##### ####### ##### ###### ##### ####### ##### ######
####### ####### ####### ### ### ####### ####### ####### ### ###
### ### ### ### ###### ### ### ### ### ######
@ -283,12 +300,18 @@ class Clock:
##### ### ##### ### ##### ### ##### ###
''' '''
def build_gui_stopwatch(self): def build_gui_stopwatch(self):
'''
A timer that counts upward from 0.
'''
def tick_stopwatch(): def tick_stopwatch():
if this_instance != self.instance: if this_instance != self.instance:
return return
if not label_stopwatch.is_running: if not label_stopwatch.is_running:
return return
# started_at is reset on every press of the resume button
# so we keep track of how much was on the clock last time
# and add it.
elapsed = time.time() - label_stopwatch.started_at elapsed = time.time() - label_stopwatch.started_at
elapsed += label_stopwatch.backlog elapsed += label_stopwatch.backlog
@ -302,7 +325,7 @@ class Clock:
self.resize_widget_font(frame_display, label_stopwatch) self.resize_widget_font(frame_display, label_stopwatch)
self.t.after(10, tick_stopwatch) self.t.after(10, tick_stopwatch)
def toggle_stopwatch(*event): def toggle_stopwatch():
if label_stopwatch.is_running: if label_stopwatch.is_running:
stop_stopwatch() stop_stopwatch()
else: else:
@ -310,7 +333,11 @@ class Clock:
def stop_stopwatch(): def stop_stopwatch():
if label_stopwatch.started_at is not None: if label_stopwatch.started_at is not None:
# This check is important in case we press the "reset"
# button without having started the clock yet.
elapsed = time.time() - label_stopwatch.started_at elapsed = time.time() - label_stopwatch.started_at
# Keep track of how long the clock ran so we can
# pick up from here when we resume.
label_stopwatch.backlog += elapsed label_stopwatch.backlog += elapsed
label_stopwatch.started_at = None label_stopwatch.started_at = None
label_stopwatch.is_running = False label_stopwatch.is_running = False
@ -322,7 +349,7 @@ class Clock:
button_toggle.configure(text='stop') button_toggle.configure(text='stop')
tick_stopwatch() tick_stopwatch()
def reset_stopwatch(*event): def reset_stopwatch():
stop_stopwatch() stop_stopwatch()
label_stopwatch.backlog = 0 label_stopwatch.backlog = 0
label_stopwatch.configure(text='00:00:00.000') label_stopwatch.configure(text='00:00:00.000')
@ -372,7 +399,21 @@ class Clock:
####### ### ### ### ##### ### ### ####### ### ### ### ##### ### ###
##### ### ### ### ####### ### ### ##### ### ### ### ####### ### ###
''' '''
def delete_applet_elements(self):
self.frame_applet.unbind('<Configure>')
for x in range(len(self.elements)):
self.elements.pop().destroy()
self.frame_applet.rowconfigure(0, weight=0)
self.frame_applet.columnconfigure(0, weight=0)
def resize_widget_font(self, parent, subordinate): def resize_widget_font(self, parent, subordinate):
'''
Given a frame and a subordinate widget, resize the font of the
widget to best fit the bounds of the frame.
'''
# When initializing the widgets, the width and height is usually 1, 1.
# Must use t.update to fix everything.
self.t.update() self.t.update()
frame_w = parent.winfo_width() frame_w = parent.winfo_width()
frame_h = parent.winfo_height() frame_h = parent.winfo_height()
@ -383,6 +424,10 @@ class Clock:
subordinate.configure(font=font) subordinate.configure(font=font)
def font_by_pixels(self, frame_w, frame_h, text): def font_by_pixels(self, frame_w, frame_h, text):
'''
Given the size of a bounding box, find the best font size
to fit text in the bounds.
'''
lines = text.split('\n') lines = text.split('\n')
label_w = max(len(line) for line in lines) label_w = max(len(line) for line in lines)
label_h = len(lines) label_h = len(lines)
@ -405,7 +450,7 @@ class Clock:
hours, minutes = divmod(amount, 3600) hours, minutes = divmod(amount, 3600)
minutes, seconds = divmod(minutes, 60) minutes, seconds = divmod(minutes, 60)
return hours, minutes, seconds return (hours, minutes, seconds)
def trigger_choose_mode(self, *args): def trigger_choose_mode(self, *args):
''' '''