how do i make this rectangle keep moving till a new key is pressed?

Viewed 67

i want the rectangle to move in the direction the user gives until a new key is pressed.

making a loop inside just hangs the program since the loop would just become an infinite loop. i tried putting that if statement at the bottom because i thought the root.mainloop() function causes the program to loop but that didn't work either.

import tkinter as tk

root = tk.Tk()

x1 = y1 = 250
direc = "none"

score = 0
walls = ["250 250"]

score_box = tk.Label(root, text=score)
score_box.pack()

canvas = tk.Canvas(root, width=500, height=500)
canvas.pack()

canvas.create_rectangle(x1, y1, x1+10, y1+10)

def draw_rect():
    canvas.create_rectangle(x1, y1, x1+10, y1+10, fill="green")

def score_add():
    global score, x1, y1
    score += 1
    score_box["text"] = score
    coords = str(x1) + " " + str(y1)
    if coords in walls:
        print(f"you lose! your final score was {score}")
        root.destroy()
    walls.append(coords)


def move(event):
    global x1, y1, direc
    if event.char == "d":
        x1 += 10
        direc = "right"
    draw_rect()
    if event.char == "a":
        x1 -= 10
        direc = "left"
    draw_rect()
    if event.char == "s":
        y1 += 10
        direc = "down"
    draw_rect()
    if event.char == "w":
        y1 -= 10
        direc = "up"
    draw_rect()
    score_add()

# doesn't run at all
if direc == "up":
    y1 -= 10
    print("test")
    draw_rect()
    score_add()

# runs once only at the start
print("hello")

root.bind("<Key>", move)

root.mainloop()

Any help would be greatly appreciated

2 Answers

You can use after() to keep the rectangle moving:

import tkinter as tk

dx = dy = 0
delay = 100  # in milliseconds, adjust it to set difficulty

WIDTH = HEIGHT = 500
SIZE = 10

score = 0
x1, y1 = WIDTH//2, HEIGHT//2
walls = [(x1, y1)]


def draw_rect():
    canvas.create_rectangle(x1, y1, x1+SIZE, y1+SIZE, fill="green")

def score_add():
    global score
    score += 1
    score_box["text"] = score
    coords = (x1, y1)
    if coords in walls or x1 < 0 or x1 >= WIDTH or y1 < 0 or y1 >= HEIGHT:
        print(f"you lose! your final score was {score}")
        root.destroy()
    walls.append(coords)

def move():
    global x1, y1
    if dx or dy:
        x1 += dx
        y1 += dy
        draw_rect()
        score_add()
    root.after(delay, move)

def set_direction(event):
    global dx, dy
    if event.keysym == 'Up':
        dx, dy = 0, -SIZE
    elif event.keysym == 'Down':
        dx, dy = 0, SIZE
    elif event.keysym == 'Left':
        dx, dy = -SIZE, 0
    elif event.keysym == 'Right':
        dx, dy = SIZE, 0


root = tk.Tk()

score_box = tk.Label(root, text=score)
score_box.pack()

canvas = tk.Canvas(root, width=WIDTH, height=HEIGHT)
canvas.pack()

root.bind("<Key>", set_direction)

draw_rect() # draw the starting rectangle
move() # start the moving loop

root.mainloop()

I thought of using threading to keep that rectangle moving until a different direction key is pressed. I have some code that you can try by replacing the move function with following modified portion -

from threading import Lock, Thread
from time import sleep

key_press_delay = .5  # Half a second

# Simulates last valid key press to be repeated
def hold_key_press_thread():
    global x1, y1, direc, char, key_press_delay
    loop = True
    while loop:
        if char == "d":
            x1 += 10
            direc = "right"
        elif char == "a":
            x1 -= 10
            direc = "left"
        elif char == "s":
            y1 += 10
            direc = "down"
        elif char == "w":
            y1 -= 10
            direc = "up"
        draw_rect()
        score_add()
        sleep(key_press_delay)

char = ' '
key_lock = Lock()
key_thread = Thread(target=hold_key_press_thread)
key_thread.setDaemon(True)

def move(event):
    global char, key_lock, key_thread
    key_lock.acquire()
    char = event.char
    key_lock.release()

    if not key_thread.is_alive():
        key_thread.start()

Please note that it may have issues that I am not aware of or undesirable to what you want. For example, a key press could get lost while the thread is as sleep. I would also love to know of other better ways this could have been done.

Related