Additional material - More examples on tkinter
Here is some additional material which might be useful for your project.
Although tkinter is well suited for drawing graphs, a different coordinate system causes some sort of discomfort - we are used that y is increased up (not down). To get rid of this problem, we can use move method which allows to move canvas objects horizontally and vertically. Therefore, we place all objects in a regular coordinate system and then use the move method.
The following program plots y = sin (x):
from tkinter import *
from math import sin
window = Tk()
w = 500 # canvas width
h = 500 # canvas height
area = Canvas(window, width=w, height=h, background="white")
area.grid()
# vertical axis
area.create_line(0, h/2, 0, -h/2, arrow=LAST)
# horisontal axis
area.create_line(-w/2, 0, w/2, 0, arrow=LAST)
points = []
# generate graph points in the form [x0,f(x0), x1,f(x1),..., xn, f(xn)]
for x in range(w // -2, w // 2):
increase = 30
points.append(x)
y = sin(x * 1.0 / increase)
points.append(y * increase)
# draw a graph (using a list with points as an argument)
area.create_line(points, fill="red")
# shift all objects to the right and down (250px)
area.move(ALL, w/2, h/2)
window.mainloop()
You can also add a .gif, .pgm, or .ppm image in the canvas.
Save the following files into the same folder as the program: ball.gif, opened.gif, closed.gif and run the program:
from tkinter import *
window = Tk()
window.title("Images")
area = Canvas(window, width=600, height=600, background="white")
area.grid()
# first, download the image and place it in the canvas
ball = PhotoImage(file="ball.gif")
img = area.create_image(450, 80, image=ball)
# activeimage specifies the image that is displayed when the mouse cursor is above the image
# anchor shows how the image is placed (in this example, in the upper left corner)
closed = PhotoImage(file="closed.gif")
opened = PhotoImage(file="opened.gif")
img = area.create_image(50, 400, image=closed, activeimage=opened, anchor=NW)
window.mainloop()
In the following program, each mouse click invokes a function that registers mouse clicks:
from tkinter import *
def register_mouse_click(event):
print("The click was made on the position:", event.x, event.y)
clicks.append([event.x, event.y])
print("All made clicks:", clicks)
window = Tk()
window.title("Mouse clicks")
area = Canvas(window, width=600, height=600, background="white")
area.grid()
# <1> indicates the left mouse button
area.bind('<1>', register_mouse_click)
clicks = []
window.mainloop()
Update the content of the canvas according to the user's activities
Save the image juku.gif to the same folder.
from tkinter import *
from random import randint
# some additional constants
juku_move_length = 50
area_width = 600
area_height = 600
# functions that are invoked according to the user activity
def mouse_click_on_juku(event):
# move Juku to a random position
new_x = randint(0, area_width-50)
new_y = randint(0, area_height-50)
area.coords(juku_id, new_x, new_y)
def key_up(event):
area.move(juku_id, 0, -juku_move_length)
def key_down(event):
area.move(juku_id, 0, juku_move_length)
def key_left(event):
area.move(juku_id, -juku_move_length, 0)
def key_right(event):
area.move(juku_id, juku_move_length, 0)
# window and canvas
window = Tk()
window.title("Juku")
area = Canvas(window, width=area_width, height=area_height, background="white")
area.grid()
# image
juku = PhotoImage(file="juku.gif")
# create a variable juku_id for the image
juku_id = area.create_image(100, 100, image=juku)
# the corresponding function is associated with the mouse clicks on image using the image id
# <1> indicates the left mouse button
area.tag_bind(juku_id, '<1>', mouse_click_on_juku)
# bind the keys with the corresponding functions
window.bind_all("<Up>", key_up)
window.bind_all("<Down>", key_down)
window.bind_all("<Left>", key_left)
window.bind_all("<Right>", key_right)
window.mainloop()
The program shown above is an example of how to automatically change the position of an image if the image is clicked.
Let's now look at a more general way how to change the content of an image (for example, on mouse click). Save the following files into the same folder as the program opened.gif, closed.gif and run the program.
from tkinter import *
def change_image(event):
# global declaration allows to change the variable defined outside the function
global shown_images
# change the image
if shown_images == closed:
shown_images = opened
else:
shown_images = closed
# update the image in the canvas
area.itemconfigure(image_id, image=shown_images)
window = Tk()
window.title("Images")
area = Canvas(window, width=600, height=600, background="white")
area.grid()
closed = PhotoImage(file="closed.gif")
opened = PhotoImage(file="opened.gif")
shown_images = closed
image_id = area.create_image(200, 200, image=shown_images, anchor=NW)
area.tag_bind(image_id, '<1>', change_image)
window.mainloop()
One more example - currency converter:
from tkinter import *
eur_usd_rate = 1.1888
#prepare the functions that is run when something is typed in the corresponding box
def calculate_euros_to_usd(event):
calculate(eur_text, eur_result, eur_usd_rate, "USD")
def calculate_usd_to_euros(event):
calculate(usd_text, usd_result, 1.0 / eur_usd_rate, "EUR")
def calculate(fromc, to, rate, result_symbol):
text = fromc.get()
try:
ssum = round(float(text),2)
to['text'] = str(round((ssum * rate),2))+" "+result_symbol
except:
#if the float conversion fails, the program will arrive here
to['text'] = "???"
#window
window = Tk()
window.title("Currency converter")
#LabelFrame makes bounds and a title area
first_part = LabelFrame(window, text=" EUR -> USD ")
first_part.grid(row=0, column=0, padx=10, pady=8)
#the input widget and the result widget go to LabelFrame
eur_text = Entry(first_part, width=10)
eur_text.grid(row=0, column=0, padx=5, pady=5)
#put the function made above to respond to the upgrade key
eur_text.bind("<KeyRelease>", calculate_euros_to_usd)
eur_result = Label(first_part, text="", width=15)
eur_result.grid(row=0, column=1, padx=5, pady=5)
#the same story with the second part
second_part = LabelFrame(window, text=" USD -> EUR ")
second_part.grid(row=1, column=0, padx=10, pady=(0,8))
usd_text = Entry(second_part, width=10)
usd_text.grid(row=0, column=0, padx=5, pady=5)
usd_text.bind("<KeyRelease>", calculate_usd_to_euros)
usd_result = Label(second_part, text="", width=15)
usd_result.grid(row=0, column=1, padx=5, pady=5)
window.mainloop()
Tic Tac Toe. The status on the move during the game and the check for the winner were discussed and implemented during Session 1. Now, save the following images for the game into the same folder as the program: empty.gif, x.gif, o.gif and run the program.
from tkinter import *
from tkinter import messagebox
#function which checks if the elements in the list are the same or not
def same_three(llist):
if llist[0] == llist[1] == llist[2] and(llist[0] == 'O' or llist[0] == 'X'):
return True
return False
# function which creates lists and looks for the winner
def control(mx):
for row in mx:
if same_three(row):
return row[0]
for i in range(len(mx[0])):
column = []
for row in mx:
column.append(row[i])
if same_three(column):
return column[0]
diagonal = []
diagonal2 = []
for i in range(len(mx)):
for j in range(len(mx[i])):
if i == j:
diagonal.append(mx[i][j])
if i == len(mx[i]) - j - 1:
diagonal2.append(mx[i][j])
if same_three(diagonal):
return diagonal[0]
if same_three(diagonal2):
return diagonal2[0]
return "?"
#function which checks if the game ends with a draw
def draw_control(mx):
draw = True
for i in range(3):
for j in range(3):
if mx[i][j] == " ":
draw = False
return draw
#function for starting a new game (create empty images and an empty matrix)
def new_game():
# global variables can be changed outside the function
global turn
global sol_mx
global images_mx
images_mx = []
sol_mx = []
turn = "X"
#in the following loop, create 9 images and place them in the canvas (3x3 grid)
# in addition, save the image IDs to a 3x3 matrix and create an empty 3x3 solution matrix
for i in range (3):
images_row = []
row = []
for j in range(3):
# calculate the coordinates of the image according to the column and row numbers
xc = 50 + j * 105
yc = 50 + i * 105
image_id = area.create_image(xc, yc, image=empty)
# the corresponding function is associated with mouse clicks on images using image id
area.tag_bind(image_id, '<1>', mouse_click)
#save the image to the appropriate place in the list
images_row.append(image_id)
row.append(" ")
# one line is ready, add it to the matrix
images_mx.append(images_row)
sol_mx.append(row)
# this function is executed when the image is clicked on
def mouse_click(event):
global turn
global sol_mx
global images_mx
#the id of the object that was clicking on
image_id = area.find_withtag(CURRENT)[0]
#look at the images_mx matrix to find out where the image with that id is
for i in range(3):
for j in range(3):
if image_id == images_mx[i][j]:
if sol_mx[i][j] == " " and turn == "X":
turn = "O" #change the turn
sol_mx[i][j] = "X" #update the solution matrix
#update the image in the canvas
area.itemconfigure(images_mx[i][j], image=x)
elif sol_mx[i][j] == " " and turn == "O":
turn = "X" #change the turn
sol_mx[i][j] = "O" #update the solution matrix
#update the image in the canvas
area.itemconfigure(images_mx[i][j], image=o)
#check if there is a winner
if control(sol_mx) != "?":
winner = 'Winner ' + control(sol_mx)
messagebox.showinfo(message=winner)
print(winner) #we can output to the console as well
new_game() #starting the new game
#check if it is a draw
if draw_control(sol_mx):
winner = 'It is a draw'
messagebox.showinfo(message=winner)
print(winner)
new_game() #starting the new game
#window and canvas
window = Tk()
window.title("TicTacToe")
area = Canvas(window, width=310, height=310, background="black")
area.grid()
#load content of the images
empty = PhotoImage(file="empty.gif")
x = PhotoImage(file="x.gif")
o = PhotoImage(file="o.gif")
#start the game
new_game()
window.mainloop()
The TicTacToe example demonstrates how to put a number of images on a canvas using the loop. In order to allow images to be accessed later, the image IDs are saved in the matrix.