Home > Software engineering >  Keyboard input without repeat delay
Keyboard input without repeat delay

Time:12-08

Hey I'm trying to solve a problem where I got a character moving around on a 2D-plane using the WASD-keys. However when I hold a button the code writes once then makes a delay and then writes nonstop.

How to remove this delay? I've seen some solutions using after() with canvas() but not sure how to apply this to a label() using "text=".

Thanks for any ideas!

from tkinter import *
import tkinter as tk

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


#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_y
    player_y -= 1
    if player_y < 1:
        player_y  = 1
    update_world()

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

def keypress_d(event):
    global player_x
    player_x  = 1
    if player_x > 9:
         player_x -= 1
    update_world()

def keypress_s(event):
    global player_y
    player_y  = 1
    if player_y > 9:
        player_y -= 1
    update_world()
    
def keypress_esc(event):
    sys.exit()

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))

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

    #Print map
    width = 9
    coord_values = coordinates.values()
    i=0
    world=""

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

    world = world[:-1]

    #refresh the label that displays the world
    world_label.place(x=220,y=100)
    world_label.configure(font=("Courier", 24), text=world, bg="white")



root = Tk() #Create window

world_label = Label() #Create World Label widget
world_label.pack()
update_world() #Function update_world


#Sends pressed key values to functions keypress_"x"
root.bind("<w>", keypress_w)
root.bind("<a>", keypress_a)
root.bind("<s>", keypress_s)
root.bind("<d>", keypress_d)
root.bind("<Escape>", keypress_esc) 



root.geometry("800x600") #Size of window 
root.title('Game 0.1') #Title of window 
root.mainloop() #

CodePudding user response:

You can use timer method root.after(delay, func) to trigger function to simulate keyboard repeat.

New demo code for you. It work for both key pressed at the same time.

from time import sleep
import tkinter as tk

class Root(tk.Tk):

    def __init__(self, title, width, height):
        super().__init__()
        self.title(title)
        self.width = width
        self.height = height
        self.canvas = tk.Canvas(self, width=width, height=height, bg='green')
        self.canvas.pack()
        self.geometry(f'{width}x{height}')
        self.radius = 20
        self.delay = 20
        self.step = 5
        self.x = width // 2
        self.y = height // 2
        self.press_left  = False
        self.press_right = False
        self.press_up    = False
        self.press_down  = False
        self.ball = None
        self.draw_ball()
        self.binding()

    def draw_ball(self):
        if self.ball:
            self.canvas.delete(self.ball)
        self.ball = self.canvas.create_oval(self.x - self.radius, self.y - self.radius,
            self.x   self.radius, self.y   self.radius, fill='white')
        self.canvas.update()

    def binding(self):
        self.bind("<KeyPress-a>",   self.left_down)
        self.bind("<KeyPress-d>",   self.right_down)
        self.bind("<KeyPress-w>",   self.up_down)
        self.bind("<KeyPress-s>",   self.down_down)
        self.bind("<KeyRelease-a>", self.left_up)
        self.bind("<KeyRelease-d>", self.right_up)
        self.bind("<KeyRelease-w>", self.up_up)
        self.bind("<KeyRelease-s>", self.down_up)
        self.bind("<Escape>",       self.escape)

    def left_down(self, event):
        def left():
            if self.press_left:
                if self.x >= self.radius   self.step:
                    self.x -= self.step
                    self.draw_ball()
                self.after(self.delay, left)
            else:
                self.bind("<KeyPress-a>", self.left_down)
        self.unbind("<KeyPress-a>")
        self.press_left = True
        left()

    def left_up(self, event):
        self.press_left = False

    def right_down(self, event):
        def right():
            if self.press_right:
                if self.x <= self.width - self.radius - self.step:
                    self.x  = self.step
                    self.draw_ball()
                self.after(self.delay, right)
            else:
                self.bind("<KeyPress-d>", self.right_down)
        self.unbind("<KeyPress-d>")
        self.press_right = True
        right()

    def right_up(self, event):
        self.press_right = False

    def up_down(self, event):
        def up():
            if self.press_up:
                if self.y >= self.radius   self.step:
                    self.y -= self.step
                    self.draw_ball()
                self.after(self.delay, up)
            else:
                self.bind("<KeyPress-w>", self.up_down)
        self.unbind("<KeyPress-w>")
        self.press_up = True
        up()

    def up_up(self, event):
        self.press_up = False

    def down_down(self, event):
        def down():
            if self.press_down:
                if self.y <= self.height - self.radius - self.step:
                    self.y  = self.step
                    self.draw_ball()
                self.after(self.delay, down)
            else:
                self.bind("<KeyPress-s>", self.down_down)
        self.unbind("<KeyPress-s>")
        self.press_down = True
        down()

    def down_up(self, event):
        self.press_down = False

    def escape(self, event):
        root.destroy()

root = Root("Game 0.1", 800, 600)
root.mainloop()
  • Related