Home > Blockchain >  How to update a Tkinter window on keypress?
How to update a Tkinter window on keypress?

Time:12-01

I recently started learning Python and yesterday I started making a basic way of moving around a '@'-char (player) on a 10x10 map of '#'-chars. The whole thing was working fine in the Shell running in a while-loop.

Today I started the adventure of trying to learn Tkinter and transfering this code to a refreshable window. I have managed to print the "map" in a window and have keys update the variables signifying "player position" or player coordinates.

However the big issue that I cannot seem to solve easily is how to actually make the window refresh or button press? Or constantly for that matter.

Hopefully the code isn't too hard to understand, I've tried to add comments to describe segments.

To most here I'm sure this code will make your eyes bleed the way I've tried to solve things but please bear with me! Cheers!

import tkinter as tk

coordinates = {
    "x1y1": "#", "x1y2": "#", "x1y3": "#", "x1y4": "#", "x1y5": "#", "x1y6": "#", "x1y7": "#", "x1y8": "#", "x1y9": "#", "x1y10": "#",
    "x2y1": "#", "x2y2": "#", "x2y3": "#", "x2y4": "#", "x2y5": "#", "x2y6": "#", "x2y7": "#", "x2y8": "#", "x2y9": "#", "x2y10": "#",
    "x3y1": "#", "x3y2": "#", "x3y3": "#", "x3y4": "#", "x3y5": "#", "x3y6": "#", "x3y7": "#", "x3y8": "#", "x3y9": "#", "x3y10": "#",
    "x4y1": "#", "x4y2": "#", "x4y3": "#", "x4y4": "#", "x4y5": "#", "x4y6": "#", "x4y7": "#", "x4y8": "#", "x4y9": "#", "x4y10": "#",
    "x5y1": "#", "x5y2": "#", "x5y3": "#", "x5y4": "#", "x5y5": "#", "x5y6": "#", "x5y7": "#", "x5y8": "#", "x5y9": "#", "x5y10": "#",
    "x6y1": "#", "x6y2": "#", "x6y3": "#", "x6y4": "#", "x6y5": "#", "x6y6": "#", "x6y7": "#", "x6y8": "#", "x6y9": "#", "x6y10": "#",
    "x7y1": "#", "x7y2": "#", "x7y3": "#", "x7y4": "#", "x7y5": "#", "x7y6": "#", "x7y7": "#", "x7y8": "#", "x7y9": "#", "x7y10": "#",
    "x8y1": "#", "x8y2": "#", "x8y3": "#", "x8y4": "#", "x8y5": "#", "x8y6": "#", "x8y7": "#", "x8y8": "#", "x8y9": "#", "x8y10": "#",
    "x9y1": "#", "x9y2": "#", "x9y3": "#", "x9y4": "#", "x9y5": "#", "x9y6": "#", "x9y7": "#", "x9y8": "#", "x9y9": "#", "x9y10": "#",
    "x10y1":"#","x10y2": "#", "x10y3":"#", "x10y4":"#", "x10y5":"#", "x10y6":"#", "x10y7":"#", "x10y8":"#", "x10y9":"#", "x10y10":"#",
}

#Player starting position
player_x = 5
player_y = 5

#Onpress update player X/Y positions and make sure they don't exceed coordinate system
def keypress_w(event):
    global player_x
    player_x -= 1
    if player_x < 1:
        player_x  = 1

def keypress_a(event):
    global player_y
    player_y -= 1
    if player_y < 1:
        player_y  = 1

def keypress_d(event):
    global player_y
    player_y  = 1
    if player_y > 10:
         player_y -= 1

def keypress_s(event):
    global player_x
    player_x  = 1
    if player_x > 10:
        player_x -= 1


#Create string with player coordinates and input into dictionary
player_xy = "x", player_x, "y", player_y
player_coord = ''.join(map(str, player_xy))
coordinates[player_coord] = '@ '


#Update previous player tile to map tile
coord_keys = coordinates.keys()
for coords in coord_keys:
    if coords != player_coord:
        coordinates[coords] = '# '


#Print map
width = 10
coord_values = coordinates.values()
i=0
world=''

for item in coord_values:
    world = world   "  "   item
    i  = 1
    if i % width == 0:
        world = world   '\n'


root = Tk()
label = tk.Label(text=world)
label.pack()

root.bind("<w>", keypress_w)
root.bind("<a>", keypress_a)
root.bind("<s>", keypress_s)
root.bind("<d>", keypress_d)

root.mainloop()

CodePudding user response:

The basic strategy is to create a function that updates the world on the screen. You then call that function whenever the game data changes.

In your specific case that's pretty easy - just create a function called update_world that contains all of the code that draws the board. As a final step in that function, you can update the label with the new data.

def update_world():
    #Create string with player coordinates and input into dictionary
    player_xy = "x", player_x, "y", player_y
    player_coord = ''.join(map(str, player_xy))
    coordinates[player_coord] = '@ '


    #Update previous player tile to map tile
    coord_keys = coordinates.keys()
    for coords in coord_keys:
        if coords != player_coord:
            coordinates[coords] = '# '


    #Print map
    width = 10
    coord_values = coordinates.values()
    i=0
    world=''

    for item in coord_values:
        world = world   "  "   item
        i  = 1
        if i % width == 0:
            world = world   '\n'

    # refresh the label that displays the world.
    label.configure(text=world)

You need to modify the code that creates label, and then call update_world() directly after creating the label:

label = tk.Label()
label.pack()

update_world()

Finally, you can call update_world() in each of your function that update the board data.

def keypress_w(event):
    global player_x
    player_x -= 1
    if player_x < 1:
        player_x  = 1
    update_world()

In your specific case, this is pretty inefficient since you're redrawing the entire board on every move. A better solution would be to use something that is more directly accessible than a label, such as the Canvas widget. With that, instead of redrawing the whole board on each iteration, you can just move the user while leaving the rest of the board unchanged. For example, instead of update_world you would instead write an update_user method.

  • Related