Graphical user interfaces
Download all scripts: graphical_user_interfaces.zip
window_w_label.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import tkinter
# Inititalize Tkinter by creating a Tk root widget (= a window with a title bar and other decoration provided by your operating system)
root = tkinter.Tk()
# Create a label as a child of the root widget
label = tkinter.Label(root, text = 'Hello, world!')
# Make the label visible and size the root widget to fit the label
label.pack()
# Enter the Tkinter event loop
root.mainloop()
window_w_frame.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import tkinter
# Inititalize Tkinter by creating a Tk root widget (= a window with a title bar and other decoration provided by your operating system)
root = tkinter.Tk()
# Define a function to be called when the button is clicked
def on_button_click():
print('...like an arrow')
# Create a frame as a child of the root widget
frame = tkinter.Frame(root)
frame.pack()
# Create a label as a child of the frame
label = tkinter.Label(frame, text = 'Hello, world!')
label.pack()
# Create a button as a child of the frame
button = tkinter.Button(frame, text = 'Time flies...', command = on_button_click)
button.pack()
# Enter the Tkinter event loop
root.mainloop()
window_w_subclassed_frame.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import tkinter
# Define a function to be called when the button is clicked
class SubclassedFrame(tkinter.Frame):
def __init__(self, parent): # Special method name __init__ (also called constructor method): this method gets called when an instance of the class is created
tkinter.Frame.__init__(self) # Call the constructor method of the tkinter.Frame class
self.label = tkinter.Label(root, text = 'Hello, world!')
self.label.pack()
self.button = tkinter.Button(root, text = 'Time flies...', command = self.on_button_click)
self.button.pack()
def on_button_click(self):
print('...like an arrow')
# Inititalize Tkinter by creating a Tk root widget (= a window with a title bar and other decoration provided by your operating system)
root = tkinter.Tk()
subclassed_frame = SubclassedFrame(root)
# Enter the Tkinter event loop
root.mainloop()
stop_watch.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import tkinter
import time
class StopWatch(tkinter.Frame): # Define a class 'StopWatch' based on a tkinter.Frame
def __init__(self, parent): # Special method name __init__ (also called constructor method): this method gets called when an instance of the class is created
print('def __init__(self, parent)')
tkinter.Frame.__init__(self) # Call the constructor method of the tkinter.Frame class
# Create widgets
self.time_label = tkinter.Label(root, text = '00:00:00')
self.time_label.pack()
self.start_stop_button = tkinter.Button(root, text = 'Start', command = self.start_stop)
self.start_stop_button.pack()
self.reset_button = tkinter.Button(root, text = 'Reset', command = self.reset)
self.reset_button.pack()
# Initialize variables
self.reset()
def reset(self):
print('def reset(self)')
self.is_running = False
self.start_stop_button['text'] = 'Start'
self.elapsed_time = 0
self.start_time = None
self.display_time()
def start_stop(self):
print('start_stop(self)')
if self.is_running: # Stop watch is running -> stop
self.is_running = False
self.start_stop_button['text'] = 'Start'
self.elapsed_time = time.time() - self.start_time
else: # Stop watch is not running -> (re-)start
self.is_running = True
self.start_stop_button['text'] = 'Stop'
self.start_time = time.time() - self.elapsed_time
self.update_time()
def display_time(self, time = 0):
print('def display_time(self, time = {})'.format(time))
m = int(time / 60) # Minutes
s = int(time - m * 60) # Seconds
cs = int((time - int(time)) * 100) # Centiseconds (= hundredths of a second)
self.time_label['text'] = '{:02d}:{:02d}:{:02d}'.format(m, s, cs)
def update_time(self):
print('def update_time(self)')
if self.is_running:
self.display_time(time.time() - self.start_time)
self.timer = self.after(10, self.update_time) # Call update_time(self) in 10 microseconds again
else:
self.timer = None
# Create a Tk root widget
root = tkinter.Tk()
# Create a tkinter.Frame as defined by the StopWatch class
stop_watch = StopWatch(root)
stop_watch.pack()
# Enter the Tkinter event loop
root.mainloop()
stop_watch_w_lap_time.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import tkinter
import time
class StopWatch(tkinter.Frame):
def __init__(self, root):
tkinter.Frame.__init__(self)
backgroundcolor = '#eee'
# Style window
root.title('Stop Watch')
root.resizable(1, 0) # Vertical, horizontal
root.configure(bg = backgroundcolor)
# Create widgets
self.duration = tkinter.Label(
font = (None, 30, 'bold'),
bg = backgroundcolor)
self.duration.pack(
side = tkinter.TOP)
self.lap_times_list = tkinter.Listbox(
borderwidth = 0)
self.lap_times_list.pack(
side = tkinter.BOTTOM,
fill = tkinter.BOTH,
expand = 1)
for i in range(0, self.lap_times_list.size(), 2): # Colorize alternating lines of the listbox
self.lap_times_list.itemconfigure(i, background='#f8f8f8')
self.start_pause_button = tkinter.Button(
text = 'Start',
command = self.start_pause,
width = 8,
default = tkinter.ACTIVE,
highlightbackground = backgroundcolor)
self.start_pause_button.pack(
side = tkinter.LEFT)
self.lap_reset_button = tkinter.Button(
text = 'Reset',
command = self.lap_reset,
width = 8,
default = tkinter.DISABLED,
highlightbackground = backgroundcolor)
self.lap_reset_button.pack(
side = tkinter.LEFT,
fill = tkinter.BOTH,
expand = 1)
root.update()
# now root.geometry() returns valid size/placement
root.minsize(root.winfo_width(), root.winfo_height())
root.maxsize(root.winfo_width(), 0)
# Bind events
root.bind('<Return>', self.start_pause) # Start / pause stopwatch
root.bind('<BackSpace>', self.lap_reset) # Reset stopwatch
root.bind('<space>', self.lap_reset) # Add lap time
# Initialize
self.start_time = 0
self.lap_reset()
def lap_reset(self, event = None):
if not self.start_time: # Stopwatch is not running: reset everything
self.start_time = 0
self.stopped_duration = 0
self.start_pause_button['text'] = 'Start'
self.lap_reset_button['text'] = 'Lap'
self.display_duration()
self.lap_times_list.delete(0, tkinter.END)
else: # Stopwatch is not running: add lap time to listbox
self.lap_times_list.insert(tkinter.END, self._mm_ss_cs(self._current_stopped_duration()))
i = self.lap_times_list.size() - 1
if i % 2:
self.lap_times_list.itemconfigure(i, background='#fafafa')
def display_duration(self, seconds = 0):
self.duration['text'] = self._mm_ss_cs(seconds)
def _current_stopped_duration(self):
return self.stopped_duration + time.time() - self.start_time
def _mm_ss_cs(self, seconds = 0):
mm = int( seconds / 60)
ss = int( seconds - mm * 60)
cs = int((seconds - mm * 60 - ss) * 100)
return '{0:02d}:{1:02d}.{2:02d}'.format(mm, ss, cs)
def start_pause(self, event = None):
if not self.start_time: # Start stopwatch
self.start_time = time.time()
self.start_pause_button['text'] = 'Pause'
self.lap_reset_button['text'] = 'Lap'
else: # Stop stopwatch
self.stopped_duration += time.time() - self.start_time
self.start_time = 0
self.start_pause_button['text'] = 'Start'
self.lap_reset_button['text'] = 'Reset'
self.update()
def update(self):
if self.start_time:
self.display_duration(self._current_stopped_duration())
self.timer = self.after(10, self.update)
else:
self.display_duration(self.stopped_duration)
self.after_cancel(self.timer)
if __name__ == '__main__':
root = tkinter.Tk()
stop_watch = StopWatch(root)
root.mainloop()
traffic_light.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import tkinter
class TrafficLight(tkinter.Frame):
def __init__(self, root):
tkinter.Frame.__init__(self)
background_color = '#eee'
# Style window
root.title('Traffic Light')
root.resizable(0, 0) # Vertical, horizontal
# root.configure(bg = background_color)
self.canvas = tkinter.Canvas(
width = 100, height = 300,
bg = background_color,
highlightthickness = 0) # Get rid of invisible border
self.canvas.pack() #fill = tkinter.BOTH, expand = 1)
self.lights = {
'red' : self.canvas.create_oval(10, 10, 90, 90, width = 0, fill = '#f00', disabledfill = '#efefef', state = tkinter.NORMAL),
'yellow': self.canvas.create_oval(10, 110, 90, 190, width = 0, fill = '#fc0', disabledfill = '#efefef', state = tkinter.DISABLED),
'green' : self.canvas.create_oval(10, 210, 90, 290, width = 0, fill = '#393', disabledfill = '#efefef', state = tkinter.DISABLED)}
self.states = [
{ 'duration' : 5, 'colors' : ('red', )},
{ 'duration' : 4, 'colors' : ('red', 'yellow')},
{ 'duration' : 5, 'colors' : ('green', )},
{ 'duration' : 2/3, 'colors' : ()},
{ 'duration' : 2/3, 'colors' : ('green', )},
{ 'duration' : 2/3, 'colors' : ()},
{ 'duration' : 2/3, 'colors' : ('green', )},
{ 'duration' : 2/3, 'colors' : ()},
{ 'duration' : 2/3, 'colors' : ('green', )},
{ 'duration' : 2/3, 'colors' : ()},
{ 'duration' : 2/3, 'colors' : ('green', )},
{ 'duration' : 4, 'colors' : ('yellow', )}
]
self.current_state = -1
self.update()
def update(self):
state = self.states[self.current_state]
for color in state['colors']:
self.canvas.itemconfig(self.lights[color], state = tkinter.DISABLED)
self.current_state = (self.current_state + 1) % len(self.states)
state = self.states[self.current_state]
for color in state['colors']:
self.canvas.itemconfig(self.lights[color], state = tkinter.NORMAL)
self.timer = self.after(int(state['duration'] * 1000), self.update)
if __name__ == '__main__':
root = tkinter.Tk()
traffic_light = TrafficLight(root)
root.mainloop()
"""
See: https://www.wien.gv.at/verkehr/ampeln/images/verkehrs-ampel.gif
"""
analogue_clock.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import math, tkinter, time
# Define a class to crreate a canvas within the Tk root widget and draw an analogue clock
class AnalogueClock(tkinter.Frame):
def __init__(self, parent, size = 250, minsize = 100, maxsize = 1000): # Special method name __init__ (also called constructor method): this method gets called when an instance of the class is created
# Setup the Tk root widget
self.parent = parent
self.parent.geometry('{}x{}+100+100'.format(size, size)) # Set the window size and position
self.parent.minsize(minsize, minsize) # Set the minimum window size
self.parent.maxsize(maxsize, maxsize) # Set the maximum window size
# Add a canvas widget within the Tk root widget
self.canvas = tkinter.Canvas(root) # Create a canvas within the Tk root widget
self.canvas.pack(fill = tkinter.BOTH, expand = 1) # Pack the canvas to fit the root widget
self.canvas.bind("<Configure>", self.update) # Will call update once on widget creation
# Add lines to canvas to use as markings to draw the clock face
self.markings = [] # Create empty list
marking_count = 12 # Set the number of markings
for marking_id in range(marking_count): # Iterate over the number of markings
width = ((marking_id % 3) == 0) * 2 + 1 # Every 3rd marker is 3 pixels wide
self.markings.append(
self.canvas.create_line(0, 0, 0, 0, fill = '#000000', width = width) #
)
# Add lines to canvas to use as hands
self.hands = {
'h': self.canvas.create_line(0, 0, 0, 0, fill = '#000000', width = 5),
'm': self.canvas.create_line(0, 0, 0, 0, fill = '#000000', width = 3),
's': self.canvas.create_line(0, 0, 0, 0, fill = '#ff0000', width = 1)
}
self.size = (0, 0)
def update(self, size = None):
# Draw an analogue clock on the canvas
now = time.localtime() # Get the localized time as named tuple [^1]
self.draw_analogue_clock(now.tm_hour, now.tm_min, now.tm_sec)
# Call the function again at the next full second
now = time.time() # Get the time as float
delay = 1000 - int(now * 1000) % 1000 # Number of milliseconds until next full second
self.canvas.after(delay, self.update)
def draw_analogue_clock(self, hours, minutes, seconds):
# Get canvas dimensions
width = self.canvas.winfo_width()
height = self.canvas.winfo_height()
# Check if the dimensions have changed
resized = self.size != (width, height)
self.size = (width, height)
# Calculate clock dimensions
radius = min(width, height) / 2
center_x = width / 2
center_y = height / 2
# Define a (sub)function to calculate the start and end coordinates for all lines
# 'max_value' determines the full circle (= 360-degree or 2π radians)
# 'value' determines the angle
def xy(value, length = 1):
angle = value * 2 * math.pi
if length > 0: # Draw the line from the center
xy = (
round(center_x),
round(center_y),
round(center_x + math.sin(angle) * radius * length),
round(center_y - math.cos(angle) * radius * length))
else: # Draw the line from the perimeter
xy = (
round(center_x + math.sin(angle) * radius * (1 + length)),
round(center_y - math.cos(angle) * radius * (1 + length)),
round(center_x + math.sin(angle) * radius),
round(center_y - math.cos(angle) * radius))
return xy
if resized:
# Move markings
marking_count = len(self.markings)
for marking_id, marking in enumerate(self.markings):
value = marking_id * 60 / marking_count
self.canvas.coords(marking, *xy(value / 60, -0.20))
# Move hands
minutes += seconds / 60
hours += minutes / 60
self.canvas.coords(self.hands['h'], *xy(hours / 12, 0.5))
self.canvas.coords(self.hands['m'], *xy(minutes / 60, 0.75))
self.canvas.coords(self.hands['s'], *xy(seconds / 60))
# Inititalize Tkinter by creating a Tk root widget (= a window with a title bar and other decoration provided by your operating system)
root = tkinter.Tk()
subclassed_frame = AnalogueClock(root)
# Enter the Tkinter event loop
root.mainloop()
"""
[^1]: The Python Standard Library - Time access and conversions
https://docs.python.org/3.7/library/time.html#time.struct_time
Index | Attribute | Values
======+===========+=======
0 | tm_year | (for example, 1993)
1 | tm_mon | range [1, 12]
2 | tm_mday | range [1, 31]
3 | tm_hour | range [0, 23]
4 | tm_min | range [0, 59]
5 | tm_sec | range [0, 61]; see (2) in strftime() description
6 | tm_wday | range [0, 6], Monday is 0
7 | tm_yday | range [1, 366]
8 | tm_isdst | 0, 1 or -1; see below
N/A | tm_zone | abbreviation of timezone name
N/A | tm_gmtoff | offset east of UTC in seconds
"""