Tkinter

For discussion I'll continue using Jupyter notebooks for Tkinter for consistency. If they don't work for you and you want to run the code yourself, you can just copy the relevant code into Spyder or whatever IDE works for you.

In [1]:
import tkinter as Tk

In lecture, we learned how to use Tkinter to make GUIs (graphical user interfaces). One example was the following program:

In [2]:
root = Tk.Tk()
w = Tk.Canvas(root, width=500, height=500)
w.pack()
def rectangle(event):
    w.create_rectangle(event.x,event.y, event.x+20, event.y+20, fill="blue")
w.bind("<Button-1>", rectangle)
root.mainloop()

Of course, there is so much more we can do. Some useful things we can add are

  • Labels
  • Text Boxes
  • Buttons
  • Radio Buttons

and other events we can check for are

  • Other mouse clicks/mouse releases
  • Key presses

among other things. For example, here is a GUI that uses all of these. This is a lot and it looks very intimidating, but the basic principle here is the same as the example above. And don't worry if you don't understand everything that's going on below, that's what we're here to learn.

In [3]:
import tkinter as Tk
root = Tk.Tk()

# Canvas
w = Tk.Canvas(root, width=500, height=500)
w.pack()

# Text Box
t = Tk.Text(root, height=3)
t.insert(Tk.END, "Here is text you can edit.")
t.pack()

# Label
l = Tk.Label(text = "This text cannot be edited.")
l.pack()

# Radio Button
v = Tk.IntVar()

rb1 = Tk.Radiobutton(root, text="Option 1", variable=v, value=1)
rb2 = Tk.Radiobutton(root, text="Option 2", variable=v, value=2)
rb1.pack()
rb2.pack()

# Button
def button_press():
    w.create_rectangle(100, 0, 200, 100, fill="red")
    print(v.get())
b = Tk.Button(root, text="Press me!", command=button_press)
b.pack()

# Events
def blue(event):
    w.create_rectangle(0, 0, 100, 100, fill="blue")
root.bind("q", blue) # press "q"

def green(event):
    w.create_rectangle(200, 0, 300, 100, fill="green")
root.bind("<Button-3>", green) # right click

root.mainloop()

Now let's use these to work on some exercises:

Exercse 1: Make a window and draw a red square and a blue square in it.

(solution below)

.

.

.

.

.

.

.

.

.

In [4]:
root = Tk.Tk()
c = Tk.Canvas(root, width=200, height=100)
c.create_rectangle(0, 0, 100, 100, fill="blue")
c.create_rectangle(100, 0, 200, 100, fill="red")
c.pack()

root.mainloop()

Exercise 2: Create a little Tkinter quiz. Use radio buttons to ask a multiple choice question and include a submit button. The submit button should print (using the print() function) whether the answer was correct or not.

(solution below)

.

.

.

.

.

.

.

.

.

In [5]:
root = Tk.Tk()
q = Tk.Label(root, text="What day is today?")
q.pack()

v = Tk.IntVar()

ans1 = Tk.Radiobutton(root, text="Tuesday", variable=v, value=1)
ans2 = Tk.Radiobutton(root, text="Thursday", variable=v, value=2)
ans1.pack()
ans2.pack()

def check_answer():
    if v.get() == 1:
        print("Correct!")
    else:
        print("Incorrect.")
    
b = Tk.Button(root, text="Submit Answer", command=check_answer)
b.pack()

root.mainloop()

Question: How could we display the results to the window instead?

(solution below)

.

.

.

.

.

.

.

.

.

In [6]:
root = Tk.Tk()
q = Tk.Label(root, text="What day is today?")
q.pack()

v = Tk.IntVar()

ans1 = Tk.Radiobutton(root, text="Tuesday", variable=v, value=1)
ans2 = Tk.Radiobutton(root, text="Thursday", variable=v, value=2)
ans1.pack()
ans2.pack()

def check_answer():
    if v.get() == 1:
        ans_label['text'] = "Correct!"
    else:
        ans_label['text'] = "Incorrect."
b = Tk.Button(root, text="Submit Answer", command=check_answer)
b.pack()

ans_label = Tk.Label(root)
ans_label.pack()

root.mainloop()

Exercise 3: The code below imports the Ball class from lecture and recreates the bouncing ball demo. Make sure you have bouncingBall.py downloaded in the same folder as the notebook.

(solution below)

.

.

.

.

.

.

.

.

.

In [7]:
import bouncingBall as bb

bb.WIDTH = 800
bb.HEIGHT = 500
bb.SIZE = 50
bb.root = Tk.Tk()
bb.canvas = Tk.Canvas(bb.root, width=bb.WIDTH, height=bb.HEIGHT, bg="grey")
bb.canvas.pack()
bb.color = "black"
ball = bb.Ball(bb.root,0,0)
ball2 = bb.Ball(bb.root,200,200)
ball3 = bb.Ball(bb.root,400,400)
bb.root.mainloop()

Add keyboard commands the balls move faster and slower. Remember that the speeds are stored in Ball.speedx and Ball.speedy.

(solution below)

.

.

.

.

.

.

.

.

.

In [8]:
def faster(event):
    ball.speedx *= 2
    ball.speedy *= 2
    
    ball2.speedx *= 2
    ball2.speedy *= 2
    
    ball3.speedx *= 2
    ball3.speedy *= 2
    
def slower(event):
    ball.speedx *= 0.5
    ball.speedy *= 0.5
    
    ball2.speedx *= 0.5
    ball2.speedy *= 0.5
    
    ball3.speedx *= 0.5
    ball3.speedy *= 0.5
    
bb.WIDTH = 800
bb.HEIGHT = 500
bb.SIZE = 50
bb.root = Tk.Tk()
bb.canvas = Tk.Canvas(bb.root, width=bb.WIDTH, height=bb.HEIGHT, bg="grey")
bb.canvas.pack()
bb.color = "black"
ball = bb.Ball(bb.root,0,0)
ball2 = bb.Ball(bb.root,200,200)
ball3 = bb.Ball(bb.root,400,400)

bb.root.bind('s', slower)
bb.root.bind('f', faster)
bb.root.mainloop()

Question: What if we wanted to change the direction?

(solution below)

.

.

.

.

.

.

.

.

.

In [9]:
def flip(event):
    ball.speedx *= -1
    ball.speedy *= -1
    
    ball2.speedx *= -1
    ball2.speedy *= -1
    
    ball3.speedx *= -1
    ball3.speedy *= -1
    
def color(event):
    bb.color = "red"
    
bb.WIDTH = 800
bb.HEIGHT = 500
bb.SIZE = 50
bb.root = Tk.Tk()
bb.canvas = Tk.Canvas(bb.root, width=bb.WIDTH, height=bb.HEIGHT, bg="grey")
bb.canvas.pack()
bb.color = "black"
ball = bb.Ball(bb.root,0,0)
ball2 = bb.Ball(bb.root,200,200)
ball3 = bb.Ball(bb.root,400,400)

bb.root.bind('f', flip)
bb.root.mainloop()

Exercise 4: Using the bouncing ball demo as inspiration, create an animation of a square sliding left and right on a canvas.

(solution below)

.

.

.

.

.

.

.

.

.

In [10]:
SIZE = 100
WIDTH = 800
HEIGHT = 100

class Square:
    def __init__(self, master, pos_x, pos_y):
        self.shape = canvas.create_rectangle(pos_x, pos_y, 
                                        pos_x+SIZE, pos_y+SIZE, fill='blue')
        self.speedx = 10
        self.active = True
        self.move_active()

    def ball_update(self):
        canvas.move(self.shape, self.speedx, 0)
        pos = canvas.coords(self.shape)
        if pos[2] >= WIDTH or pos[0] <= 0:
            self.speedx *= -1

    def move_active(self):
        if self.active:
            self.ball_update()
            root.after(40, self.move_active)

root = Tk.Tk()
canvas = Tk.Canvas(root, width=WIDTH, height=HEIGHT, bg="grey")
canvas.pack()
color = "black"
s = Square(root,0,0)
root.mainloop()