如何在tkinter中等待多次按键?

时间:2019-03-18 21:21:23

标签: python python-3.x tkinter

我的代码如下:

from tkinter import *
window = Tk()
print("What is 5 times 5?")
optionA = 25
optionB = 10
optionC = 3125
optionD = 55
print("A:", optionA)
print("B:", optionB)
print("C:", optionC)
print("D:", optionD)
def chooseA():
    userAnswer = optionA
def chooseB():
    userAnswer = optionB
def chooseC():
    userAnswer = optionC
def chooseD():
    userAnswer = optionD
window.bind("<Left>", chooseA)
window.bind("<Up>", chooseB)
window.bind("<Down>", chooseC)
window.bind("<Right>", chooseD)
if userAnswer == 25:
    print("You correctly answered the question!")

我的问题是我收到一条错误消息,提示未定义userAnswer。这是因为该代码没有等待用户按下四个箭头键之一的功能。是否有人对如何使程序等待4个按键中的1个有建议?非常感谢您的回答! (PS。从键盘模块使用keyboard.wait命令不会等待多个键。)

2 个答案:

答案 0 :(得分:1)

这是一个解决方案,但也许不是最好的解决方案(可能有一些模块专门用于此目的,但我想尽可能地练习使用线程进行编码)。

from threading import Thread
from tkinter import *

def check_with_thread(): # make a function our thread can attach to
    global userAnswer
    local_check = userAnswer
    while True:
        if userAnswer != local_check: # user has selected a new value
            if userAnswer == 25:
                print("That's correct!")
                break # have our thread break infinite loop and end it's execution
            else:
                print(userAnswer)
                local_check = userAnswer # Make it able to detect changes in values


window = Tk() # Actually make a window! Why is this not in the OP???
print("What is 5 times 5?")
optionA = 25
optionB = 10
optionC = 3125
optionD = 55
print("A:", optionA)
print("B:", optionB)
print("C:", optionC)
print("D:", optionD)
def chooseA(event):
    global userAnswer # we are modifying the global variable our thread is looking at
    userAnswer = optionA
def chooseB(event):
    global userAnswer
    userAnswer = optionB
def chooseC(event): # the .bind() sends events, so these need a positional argument for them
    global userAnswer
    userAnswer = optionC
def chooseD(event):
    global userAnswer
    userAnswer = optionD
window.bind("<Left>", chooseA)
window.bind("<Up>", chooseB)
window.bind("<Down>", chooseC)
window.bind("<Right>", chooseD)
userAnswer = None
t = Thread(target=check_with_thread) # make a thread to check the input
t.start() # get our thread running before generating the window

window.mainloop()

工作原理

首先,我们为线程定义了一个要执行的函数(线程是您的程序能够立即执行两件事的方法之一。之所以需要这样做是因为window.mainloop()会阻塞脚本的主线程不做任何其他事情)。我们指定函数需要使用userAnswer读取全局变量global userAnswer,否则将期望该变量是局部变量(在函数内定义,并且在函数结束时被忘记,除非返回该变量)。然后,我们将局部变量local_check初始化为调用函数时碰巧发生的userAnswer。最后,我们进入无限循环。在此循环中,我们将通过与以前创建的local_check进行比较来检查用户是否已更新答案。如果答案已更新,则这些值将有所不同!我们将继续检查新的答案,直到他们选择25的正确答案为止,在此我们将调用break以结束我们的while循环(否则它将永远持续下去)。至此,我们已经到达函数的末尾,因此分配给它的线程将在此点消失。接下来,我们将创建userAnswers并为其分配一个值None,以便当我们的线程到达行local_check = userAnswer时它将知道该怎么做,并且不会遇到NameError。现在剩下的就是创建线程了。我们使用t = Thread(target=check_with_thread)进行操作,然后立即启动它。线程现在启动,并检查输入是否更改,我们终于可以在脚本的主线程中启动window.mainloop()

为什么要使用线程?

使用线程是因为window.mainloop()正在阻塞。这意味着如果我们尝试过:

# stuff from previous script
window.mainloop()
thing = 5
print(thing)

直到用户关闭后,我们才会看到打印的任何内容。这意味着我们的绑定不再有效,并且不能用于更改用户选择的内容。通过使用线程,我们可以有两个无限循环同时同时处理信息,这对于程序员来说是一个非常有价值的窍门(为什么我会尽可能地练习)

答案 1 :(得分:0)

尝试添加:

window = tkinter.Tk()

位于代码顶部,然后

window.mainloop()

最后。这将创建一个窗口,该窗口将保持打开状态,直到您手动将其关闭。