Arvutiteaduse instituut
  1. Kursused
  2. 2018/19 sügis
  3. Programmeerimise alused II (MTAT.03.256)
EN
Logi sisse

Programmeerimise alused II 2018/19 sügis

  • Home
  • Grading
  • Links

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.

  • Arvutiteaduse instituut
  • Loodus- ja täppisteaduste valdkond
  • Tartu Ülikool
Tehniliste probleemide või küsimuste korral kirjuta:

Kursuse sisu ja korralduslike küsimustega pöörduge kursuse korraldajate poole.
Õppematerjalide varalised autoriõigused kuuluvad Tartu Ülikoolile. Õppematerjalide kasutamine on lubatud autoriõiguse seaduses ettenähtud teose vaba kasutamise eesmärkidel ja tingimustel. Õppematerjalide kasutamisel on kasutaja kohustatud viitama õppematerjalide autorile.
Õppematerjalide kasutamine muudel eesmärkidel on lubatud ainult Tartu Ülikooli eelneval kirjalikul nõusolekul.
Tartu Ülikooli arvutiteaduse instituudi kursuste läbiviimist toetavad järgmised programmid:
euroopa sotsiaalfondi logo