This commit is contained in:
Voussoir 2015-08-12 20:19:58 -07:00
parent 694a3ca1b3
commit 27f13eed96
5 changed files with 474 additions and 12 deletions

423
Clock/clock.py Normal file
View file

@ -0,0 +1,423 @@
import tkinter
import datetime
import math
import time
COLOR_BACKGROUND = '#000'
COLOR_FONT = '#666'
COLOR_DROPDOWN = '#aaa'
COLOR_DROPDOWN_ACTIVE = '#999'
MODE_CLOCK = 'clock'
MODE_COUNTDOWN = 'countdown'
MODE_STOPWATCH = 'stopwatch'
FONT = 'Consolas'
# Used for resizing the clock font to fit the frame.
# For consolas, 1.33333 is a good value. It may differ
# for other fonts
FONT_YX_RATIO = 1.33333
MONTHS = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec']
MONTHS_DICT = {'jan':1, 'feb':2, 'mar':3, 'apr':4, 'may':5, 'jun':6, 'jul':7, 'aug':8, 'sep':9, 'oct':10, 'nov':11, 'dec':12}
class Clock:
def __init__(self):
self.t = tkinter.Tk()
self.t.configure(bg=COLOR_BACKGROUND)
self.mode_methods = {
MODE_CLOCK: self.build_gui_clock,
MODE_COUNTDOWN: self.build_gui_countdown,
MODE_STOPWATCH: self.build_gui_stopwatch,
}
self.dropstring = tkinter.StringVar(self.t)
self.dropstring.trace('w', self.trigger_choose_mode)
modes = list(self.mode_methods.keys())
modes.sort(key=lambda x: x.lower())
self.drop = tkinter.OptionMenu(self.t, self.dropstring, *modes)
self.drop.configure(relief='flat', bg=COLOR_DROPDOWN, activebackground=COLOR_DROPDOWN_ACTIVE, direction='below', highlightthickness=0, anchor='w')
self.drop.pack(fill='x', anchor='n')
#self.trigger_choose_mode()
self.frame_applet = tkinter.Frame(self.t, width=400, height=95, bg=COLOR_BACKGROUND)
self.frame_applet.pack(side='bottom', anchor='s', expand=True, fill='both')
self.frame_applet.pack_propagate(0)
self.frame_applet.grid_propagate(0)
self.elements = []
# Instance is used to keep track of how many times we have
# swapped modes.
# This is to make sure that any `self.t.after` loops are broken
# when we leave that mode.
self.instance = 0
self.dropstring.set(MODE_COUNTDOWN)
self.t.mainloop()
@property
def mode(self):
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
###### ### ##### ###### ### ###
####### ### ####### ####### ### ###
### ### ### ### ### #####
####### ####### ####### ####### ### ###
###### ####### ##### ###### ### ###
'''
def build_gui_clock(self):
def tick_clock():
if this_instance != self.instance:
# used to break the "after" loop
return
#now = datetime.datetime.now()
#now = now.strftime('%H:%M:%S')
now = time.strftime('%H:%M:%S')
label_clock.configure(text=now)
self.t.after(1000, tick_clock)
this_instance = self.instance
label_clock = tkinter.Label(self.frame_applet, text='x', bg=COLOR_BACKGROUND, fg=COLOR_FONT)
label_clock.pack(anchor='center', expand=True)
self.elements.append(label_clock)
tick_clock()
self.frame_applet.bind('<Configure>', lambda event: self.resize_widget_font(self.frame_applet, label_clock))
self.resize_widget_font(self.frame_applet, label_clock)
'''count
###### ##### ### ### ##### #######
####### ####### ### ### ####### #######
### ### ### ### ### ### ### ###
####### ####### ####### ### ### ###
###### ##### ##### ### ### ###
'''
def build_gui_countdown(self):
def toggle_mode():
reset_countdown()
if label_countdown.mode is 'until':
for item in elements_until:
item.grid_forget()
button_reset.grid(row=0, column=6)
spinbox_hour.configure(to=999)
label_countdown.mode = 'in'
else:
spinbox_day.grid(row=0, column=1)
spinbox_month.grid(row=0, column=2)
spinbox_year.grid(row=0, column=3)
spinbox_hour.configure(to=23)
#button_reset.grid_forget()
label_countdown.mode = 'until'
button_countdownmode.configure(text=label_countdown.mode)
def tick_countdown():
if this_instance != self.instance:
return
if not label_countdown.is_running:
return
now = time.time()
if now > label_countdown.destination:
reset_countdown()
return
until_dest = label_countdown.destination - now
hours, minutes, seconds = self.hms_divmod(until_dest)
display = '%02d:%02d:%04.1f' % (hours, minutes, seconds)
display = display.replace('60.0', '00.0')
previous_size = len(label_countdown.cget('text'))
label_countdown.configure(text=display)
if len(display) != previous_size:
self.resize_widget_font(frame_display, label_countdown)
self.t.after(100, tick_countdown)
def start_countdown():
if label_countdown.mode is 'until':
if label_countdown.destination is None:
try:
d = int(spinbox_day.get())
mo = MONTHS_DICT[spinbox_month.get()]
y = int(spinbox_year.get())
h = int(spinbox_hour.get())
m = int(spinbox_minute.get())
s = int(spinbox_second.get())
strp = '%d %s %d %d %d %d' % (d, mo, y, h, m, s)
strp = datetime.datetime.strptime(strp, '%d %m %Y %H %M %S')
label_countdown.destination = strp.timestamp()
except ValueError:
return
else:
now = time.time()
if label_countdown.destination is None:
try:
h = int(spinbox_hour.get())
m = int(spinbox_minute.get())
s = int(spinbox_second.get())
label_countdown.destination = now + (3600*h) + (60*m) + (s)
except ValueError:
return
else:
backlog = now - label_countdown.backlog
#print(backlog)
label_countdown.destination += backlog
label_countdown.is_running = True
button_toggle.configure(text='stop')
tick_countdown()
def stop_countdown():
if label_countdown.mode is 'until':
pass
else:
label_countdown.backlog = time.time()
label_countdown.is_running = False
button_toggle.configure(text='start')
def reset_countdown():
stop_countdown()
label_countdown.configure(text='00:00:00.0')
label_countdown.destination = None
label_countdown.backlog = 0
def toggle_countdown():
if label_countdown.is_running is True:
stop_countdown()
else:
start_countdown()
def reset_spinboxes():
for item in (spinbox_hour, spinbox_minute, spinbox_second, spinbox_day, spinbox_month, spinbox_year):
item.configure(bg=COLOR_BACKGROUND, fg=COLOR_FONT, buttonbackground=COLOR_DROPDOWN, activebackground=COLOR_DROPDOWN_ACTIVE)
item.delete(0, 'end')
spinbox_hour.insert(0, 0)
spinbox_minute.insert(0, 0)
spinbox_second.insert(0, 0)
spinbox_day.insert(0, time.strftime('%d'))
spinbox_month.insert(0, time.strftime('%b').lower())
spinbox_year.insert(0, time.strftime('%Y'))
this_instance = self.instance
elements_until = []
frame_display = 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)
label_countdown = tkinter.Label(frame_display, text='00:00:00.0', bg=COLOR_BACKGROUND, fg=COLOR_FONT)
label_countdown.mode = 'until'
label_countdown.destination = None
label_countdown.backlog = 0
label_countdown.is_running = False
button_countdownmode = tkinter.Button(frame_controls, text='0', command=toggle_mode, bg=COLOR_DROPDOWN, activebackground=COLOR_DROPDOWN_ACTIVE)
button_countdownmode.configure(width=5)
button_toggle = tkinter.Button(frame_controls, text='start', command=toggle_countdown, bg=COLOR_DROPDOWN, activebackground=COLOR_DROPDOWN_ACTIVE)
button_reset = tkinter.Button(frame_controls, text='reset', command=reset_countdown, bg=COLOR_DROPDOWN, activebackground=COLOR_DROPDOWN_ACTIVE)
spinbox_hour = tkinter.Spinbox(frame_spinboxes, from_=0, to=999, width=3)
spinbox_minute = tkinter.Spinbox(frame_spinboxes, from_=0, to=59, width=2)
spinbox_second = tkinter.Spinbox(frame_spinboxes, from_=0, to=59, width=2)
spinbox_day = tkinter.Spinbox(frame_spinboxes, from_=0, to=31, width=2)
spinbox_month = tkinter.Spinbox(frame_spinboxes, values=MONTHS, width=4)
spinbox_year = tkinter.Spinbox(frame_spinboxes, from_=2015, to=9999, width=4)
reset_spinboxes()
self.frame_applet.rowconfigure(0, weight=1)
self.frame_applet.columnconfigure(0, weight=1)
frame_controls.columnconfigure(1, weight=1)
frame_display.grid(row=0, column=0, sticky='news')
label_countdown.pack(anchor='center', expand=True)
frame_controls.grid(row=1, column=0, sticky='ew')
button_countdownmode.grid(row=0, column=0, sticky='ew')
frame_spinboxes.grid(row=0, column=1, sticky='ew')
spinbox_hour.grid(row=0, column=4)
spinbox_minute.grid(row=0, column=5)
spinbox_second.grid(row=0, column=6)
button_toggle.grid(row=0, column=7)
self.elements.append(frame_display)
self.elements.append(frame_controls)
self.elements.append(frame_spinboxes)
self.elements.append(label_countdown)
self.elements.append(button_countdownmode)
self.elements.append(spinbox_hour)
self.elements.append(spinbox_minute)
self.elements.append(spinbox_second)
self.elements.append(button_toggle)
self.elements.append(button_reset)
elements_until.append(spinbox_day)
elements_until.append(spinbox_month)
elements_until.append(spinbox_year)
toggle_mode()
self.frame_applet.bind('<Configure>', lambda event: self.resize_widget_font(frame_display, label_countdown))
self.t.update()
self.resize_widget_font(frame_display, label_countdown)
'''stop
##### ####### ##### ######
####### ####### ####### ### ###
### ### ### ### ######
####### ### ####### ###
##### ### ##### ###
'''
def build_gui_stopwatch(self):
def tick_stopwatch():
if this_instance != self.instance:
return
if not label_stopwatch.is_running:
return
elapsed = time.time() - label_stopwatch.started_at
elapsed += label_stopwatch.backlog
hours, minutes, seconds = self.hms_divmod(elapsed)
#seconds, centi = divmod(seconds, 100)
display = '%02d:%02d:%06.3f' % (hours, minutes, seconds)
display = display.replace('60.0', '00.0')
previous_size = len(label_stopwatch.cget('text'))
label_stopwatch.configure(text=display)
if len(display) != previous_size:
self.resize_widget_font(frame_display, label_stopwatch)
self.t.after(10, tick_stopwatch)
def toggle_stopwatch(*event):
if label_stopwatch.is_running:
stop_stopwatch()
else:
start_stopwatch()
def stop_stopwatch():
if label_stopwatch.started_at is not None:
elapsed = time.time() - label_stopwatch.started_at
label_stopwatch.backlog += elapsed
label_stopwatch.started_at = None
label_stopwatch.is_running = False
button_toggle.configure(text='start')
def start_stopwatch():
label_stopwatch.started_at = time.time()
label_stopwatch.is_running = True
button_toggle.configure(text='stop')
tick_stopwatch()
def reset_stopwatch(*event):
stop_stopwatch()
label_stopwatch.backlog = 0
label_stopwatch.configure(text='00:00:00.000')
this_instance = self.instance
frame_display = tkinter.Frame(self.frame_applet, bg=COLOR_BACKGROUND)
frame_controls = tkinter.Frame(self.frame_applet, bg=COLOR_BACKGROUND)
label_stopwatch = tkinter.Label(frame_display, text='00:00:00.000', bg=COLOR_BACKGROUND, fg=COLOR_FONT)
label_stopwatch.started_at = None
# Backlog keeps track of how much time was on the watch when we stopped it
# So when we start it again, the counter starts from 0 and we add the backlog
# to get a total.
label_stopwatch.backlog = 0
label_stopwatch.is_running = False
button_toggle = tkinter.Button(frame_controls, text='start', command=toggle_stopwatch, bg=COLOR_DROPDOWN, activebackground=COLOR_DROPDOWN_ACTIVE)
button_reset = tkinter.Button(frame_controls, text='reset', command=reset_stopwatch, bg=COLOR_DROPDOWN, activebackground=COLOR_DROPDOWN_ACTIVE)
self.frame_applet.rowconfigure(0, weight=1)
self.frame_applet.columnconfigure(0, weight=1)
frame_display.grid(row=0, column=0, sticky='news')
label_stopwatch.pack(anchor='center', expand=True)
frame_controls.grid(row=1, column=0, sticky='ew')
frame_controls.columnconfigure(0, weight=1)
frame_controls.columnconfigure(1, weight=1)
button_toggle.grid(row=0, column=1, sticky='ew')
button_reset.grid(row=0, column=0, sticky='ew')
self.elements.append(frame_display)
self.elements.append(frame_controls)
self.elements.append(label_stopwatch)
self.elements.append(button_toggle)
self.elements.append(button_reset)
self.frame_applet.bind('<Configure>', lambda event: self.resize_widget_font(frame_display, label_stopwatch))
# Update so that the window width and height are correct
# when we call resize_widget_font
self.t.update()
self.resize_widget_font(frame_display, label_stopwatch)
'''other
##### ####### ### ### ####### ######
####### ####### ### ### ##### ### ###
### ### ### ####### ### ######
####### ### ### ### ##### ### ###
##### ### ### ### ####### ### ###
'''
def resize_widget_font(self, parent, subordinate):
self.t.update()
frame_w = parent.winfo_width()
frame_h = parent.winfo_height()
#print(frame_w, frame_h)
text = subordinate.cget('text')
font = self.font_by_pixels(frame_w, frame_h, text)
subordinate.configure(font=font)
def font_by_pixels(self, frame_w, frame_h, text):
lines = text.split('\n')
label_w = max(len(line) for line in lines)
label_h = len(lines)
# Padding to not look dumb
frame_h -= 20
# At 72 ppi, 1 point = 1 pixel height
point_heightbased = int(frame_h / label_h)
# but width requires involving the ratio
point_widthbased = int(frame_w * FONT_YX_RATIO / label_w)
point_smaller = min(point_widthbased, point_heightbased)
point_smaller = max(point_smaller, 1)
font = (FONT, point_smaller)
return font
def hms_divmod(self, amount):
hours, minutes = divmod(amount, 3600)
minutes, seconds = divmod(minutes, 60)
return hours, minutes, seconds
def trigger_choose_mode(self, *args):
'''
This method is fired when the drop-down menu item is selected.
It will choose which build_gui method to use based on the value
of the optionmenu text.
'''
mode = self.mode
self.t.title(mode)
method = self.mode_methods[mode]
self.instance += 1
self.delete_applet_elements()
method()
c = Clock()

View file

@ -50,6 +50,20 @@ class TextureTile:
self.t.mainloop()
def fit_into_bounds(self, iw, ih, fw, fh):
'''
Given the w+h of the image and the w+h of the frame,
return new w+h that fits the image into the frame
while maintaining the aspect ratio and leaving blank space
everywhere else
'''
ratio = min(fw/iw, fh/ih)
w = int(iw * ratio)
h = int(ih * ratio)
return (w, h)
def file_load_display(self, *event):
filename = self.entry_filename.get()
# I want to check the spinbox values up
@ -79,12 +93,9 @@ class TextureTile:
h = expanded.size[1]
fw = self.label_image.winfo_width()
fh = self.label_image.winfo_height()
ratio = min(fw/w, fh/h)
w = int(w * ratio)
h = int(h * ratio)
wh = self.fit_into_bounds(w, h, fw, fh)
expanded = expanded.resize((w, h))
expanded = expanded.resize(wh)
image = ImageTk.PhotoImage(expanded)
self.label_image.configure(image=image)
self.label_image.dont_garbage_me_bro = image

View file

@ -3,6 +3,9 @@ Toddo
Commandline to-do-list manager.
07 August 2015
- sqlite connection and cursor will now lazy-load instead of loading
when the object is instantiated.
<p align="center">
<img src="https://github.com/voussoir/else/blob/master/.GitImages/toddo01.png?raw=true" alt="to-do list manager"/>

Binary file not shown.

View file

@ -28,16 +28,35 @@ Use `toddo all` to see if there are entries for other tables.'''
HELP_REMOVE = '''Provide an ID number to remove.'''
DISPLAY_INDIVIDUAL = '''
ID: _id_
Table: _table_
Created: _human_
Message: _message_'''
class ToddoExc(Exception):
pass
class Toddo():
def __init__(self, dbname='C:/git/else/toddo/toddo.db'):
self.sql = sqlite3.connect(dbname)
self.cur = self.sql.cursor()
self.cur.execute('CREATE TABLE IF NOT EXISTS meta(key TEXT, val TEXT)')
self.cur.execute('CREATE TABLE IF NOT EXISTS todos(id INT, todotable TEXT, created INT, message TEXT)')
self.cur.execute('CREATE INDEX IF NOT EXISTS todoindex on todos(id)')
self.dbname = dbname
self._sql = None
self._cur = None
@property
def sql(self):
if self._sql is None:
self._sql = sqlite3.connect(self.dbname)
return self._sql
@property
def cur(self):
if self._cur is None:
self._cur = self.sql.cursor()
self._cur.execute('CREATE TABLE IF NOT EXISTS meta(key TEXT, val TEXT)')
self._cur.execute('CREATE TABLE IF NOT EXISTS todos(id INT, todotable TEXT, created INT, message TEXT)')
self._cur.execute('CREATE INDEX IF NOT EXISTS todoindex on todos(id)')
return self._cur
def add_todo(self, message=None):
'''
@ -88,8 +107,14 @@ class Toddo():
width = shutil.get_terminal_size()[0] - (messageleft + 1)
message = nicewrap(message, width, messageleft)
return 'ID: %d\nTable: %s\nCreated: %s\nMessage: %s' % (
todo[SQL_ID], todo[SQL_TODOTABLE], human(todo[SQL_CREATED]), message)
output = DISPLAY_INDIVIDUAL
output = output.replace('_id_', str(todo[SQL_ID]))
output = output.replace('_table_', todo[SQL_TODOTABLE])
output = output.replace('_human_', human(todo[SQL_CREATED]))
output = output.replace('_message_', message)
return output
def display_active_todos(self):
'''