1

I'm currently creating a program that will constantly check for a keypress, and if it's W or S it'll change the speed. But I've got a problem of Tk not responding? Anyone help cheers. My code is:

 import tkinter as tk #Importing the GUI Library
import time #Importing Time#
def onKeyPress(event): #Defining my function of keypress, and using GUI Library to get the keypress.
        time.sleep(1) #Telling it to wait one second otherwise it will crash.
        text.insert('end', 'You pressed %s\n' % (event.char, )) #Telling the user if he pressed the key or not.
        speed = 50 #Setting the speed to 50 default
        while speed > 0:
            if event.char == 'w': #if key pressed = w:
                speed = speed + 1 #Change speed by 1
                time.sleep(1)
            if event.char == 's':
                speed = speed - 1
                time.sleep(1)
                print(speed)


root = tk.Tk()
root.geometry('300x200')
text = tk.Text(root, background='black', foreground='white', font=('Comic Sans MS', 12))
text.pack()
root.bind('<KeyPress>', onKeyPress)
root.mainloop()

4 Answers 4

2

Your main issue is that you're entering an infinite loop when you press a letter other than 's'. When you press 's' it will take 50 seconds to exit the loop only to begin again upon the next key press. Additionally your speed variable is only inside the keypress function so you can't access it anyway, you are also resetting it to 50 every time a key is pressed

A couple of key things you need to know about tkinter is that time.sleep should be heavily avoided and trying to have while loops running in the background.

Now there are a couple of options to do this. You can either bind the function to each individual key.

import tkinter as tk

def onKeyPress(event, value):
    global speed # alter the global speed variable inside a function

    # Keep in mind this insert will only occur for the selected keys
    text.insert('end', 'You pressed %s\n' % (event.char, ))
    speed += value # update speed variable with value
    print(speed) # print current speed  

speed = 50

root = tk.Tk()
root.geometry('300x200')
text = tk.Text(root, background='black', foreground='white', font=('Comic Sans MS', 12))
text.pack()

# Individual key bindings
root.bind('<KeyPress-w>', lambda e: onKeyPress(e, 1)) # value is 1 for 'w'
root.bind('<KeyPress-s>', lambda e: onKeyPress(e, -1)) # value is -1 for 's'

root.mainloop()

Or alternative as before you can use if statements to check the letter entered. if elif is used instead of an if for each letter is because although it has the same effect, the if block will terminate when a condition is True otherwise it will go through all the if statements.

import tkinter as tk

def onKeyPress(event):
    global speed # alter the global speed variable inside a function

    text.insert('end', 'You pressed %s\n' % (event.char, ))

    if event.char == 'w':
        speed += 1
    elif event.char == 's':
        speed -= 1
    print(speed) # print current speed

speed = 50

root = tk.Tk()
root.geometry('300x200')
text = tk.Text(root, background='black', foreground='white', font=('Comic Sans MS', 12))
text.pack()
root.bind('<KeyPress>', onKeyPress)

root.mainloop()

Side Note:

Currently when I press a letter it will insert "sYou pressed s" because of the initial keypress. If you add this before the insert line then it will delete the typed character giving "You pressed s" instead.

text.delete("%s-1c" % 'insert', 'insert')
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks! Exactly what I wanted.
1

Tk is not responding because the while loop in your event handler will never finish. It seems that you don't even need it in that place, so instead of

def onKeyPress(event):
    while True:
        time.sleep(1)
        text.insert('end', 'You pressed %s\n' % (event.char, ))
    ....

simply

def onKeyPress(event):
    text.insert('end', 'You pressed %s\n' % (event.char, ))
    ...

should do the job.

The main loop is not in the single event handlers, but outside and already started by your last line.

Comments

0

you can never do two things like gui and looping same time in the main thread or on main process.you are using while loop .so what happens when program enter in while loop it continuously runs without giving any resource to gui.
so make use of threading module and queue to do this work.

Comments

-1

I don't see the relation between what you say you want and what you have in your code:

def onKeyPress(event):
    time.sleep(1) #Telling it to wait one second otherwise it will crash.
    text.insert('end', 'You pressed %s\n' % (event.char, ))
    speed = 50      # This is set on each keypress?
    while speed > 0:         # why?
        if event.char == 'w':
            speed = speed + 1
            time.sleep(1)
        if event.char == 's':
            speed = speed - 1
            time.sleep(1)
            print(speed)     # inside the second if? Why?

First, you're setting the speed at 50 each time you press a key (any key).

Second, why do you have a loop inside your 'keypress' function?
This function is called each time you press a (any) key, so if you press 'w' and then 's' or even 't', you'll have one function call per key.

Let's say you press 'w', then:
- the function is called: you set speed = 50
- you enter a while loop, (speed >0)
- check for the pressed key.
- Since it's 'w', speed = 51
- since speed >0, redo the loop.
-----> This will ''never'' end, because speed is never <0 and no speed value is printed.

Let's say you press 's', then:
- the function is called: you set speed = 50
- you enter a while loop, (speed >0)
- check for the pressed key.
- Since it's 's', speed = 49
- print speed value
- since speed >0, redo the loop. -----> This will eventually end.

Let's say you press some other key, then:
- the function is called: you set speed = 50
- you enter a while loop, (speed >0)
- check for the pressed key.
- Since it's another key, speed = 50, not changed - since speed >0, redo the loop.... forever... ----> In this case you'll have a never ending loop with no printing of anything.


What do (I think) you want? This worked for me.

speed = 50   #Leave it out of the function.
def onKeyPress(event):
    #time.sleep(1) # You shouldn't have a sleep() inside a function called by tkinter.
    text.insert('end', 'You pressed %s\n' % (event.char, ))
    global speed     #So you can alter its value
    #No loop inside function
    if event.char == 'w':
        speed = speed + 1
    if event.char == 's':
        speed = speed - 1
    print(speed)     # Outside of function, so you can see the present value.

2 Comments

any solution that calls sleep in a tkinter application is fundamentally flawed. While the app is sleeping, tkinter cannot process any events (including refreshing the screen).
@BryanOakley, I totally agree. I just left it because the user said the application crashed otherwise.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.