Python - 在不使用100%cpu的情况下检查按键

时间:2013-01-10 00:27:42

标签: python multithreading winapi tkinter keypress

我正在使用Tkinter在Python中编写GUI程序,我需要一种方法来检查是否发生了按键操作而不使用我的所有cpu。目前我正在使用线程模块启动一个线程,该线程将检查按键而不冻结接口(Tkinter)。我在我的线程中的while循环中使用win32api.GetKeyState(),以便它不断检查键的状态,因为它需要能够判断键是否被按下,即使窗口没有焦点。问题是程序在我启动线程时使用100%cpu。如果我在循环中放置一个time.sleep(),它会大大减少cpu的使用量,但实际的按键和它知道你按下一个键的时间之间会有一个延迟。 有没有一种方法可以在按下窗口失焦的瞬间捕获按键,而无需使用如此多的CPU?

from Tkinter import *
import win32api

class Application(Frame):
    def __init__(self, master=None):
        Frame.__init__(self, master)
        self.pack()

        coords = StringVar()

        Label(master=self, textvariable=coords).pack()

        def GetCoords():
            coords.set(str(win32api.GetCursorPos()))

        root.bind_all("<Scroll_Lock>", self.GetCoords)



root = Tk()
app = Application(master=root)
#root.wm_iconbitmap(default='INSERT ICON HERE')
#root.wm_title("TITLE OF PROGRAM")
#app.master.maxsize(640, 480)
app.master.minsize(640, 480)
app.master.resizable(0, 0)
app.mainloop()
app.quit()

该脚本给出了以下结果: AttributeError:应用程序实例没有属性“GetCoords”

1 个答案:

答案 0 :(得分:7)

您希望捕获关键事件,而不是轮询当前的键盘状态。

请参阅TkInter文档中的Events and Bindings,其中有一个简单的示例,可以完全符合您的要求(另外,它是跨平台而不是仅限Win32)。

这通常是所有GUI编程(以及网络服务器)的问题,答案总是一样的。无论是否有线程,您都不会直接使用非阻塞“检查当前X状态”调用。在主线程中,你要求事件循环“当X状态改变时调用我的函数”,或者你创建一个后台线程并阻塞“等待直到X发生”调用。

Event loop上的维基百科页面实际上对此有很好的描述。


查看您编辑的版本,您现在遇到了一个全新的问题:

class Application(Frame):
    def __init__(self, master=None):
        Frame.__init__(self, master)
        self.pack()

        coords = StringVar()

        Label(master=self, textvariable=coords).pack()

        def GetCoords():
            coords.set(str(win32api.GetCursorPos()))

        root.bind_all("<Scroll_Lock>", self.GetCoords)

GetCoordsApplication.__init__中定义的本地函数。但是你试图使用它,好像它是Application的一种方法。除非self.GetCoordsGetCoords的方法,否则您无法self。这正是错误消息AttributeError: Application instance has no attribute 'GetCoords'的含义。

但是你只能通过取出GetCoords前缀来传递本地函数self.。我不确定这会做你的想法(因为我不确定你是否可以关闭StringVar这样的事情,但是......试试看看。

或者,您可以制作 GetCoords方法,只需将其移出def __init__并为其提供self参数即可。然后你可以访问self.GetCoords,并获得一个绑定方法,你可以按照你想要的方式完全传递。但是,在这种情况下,它将无法再访问coords,因为它是__init__内的局部变量。要解决此问题,请在self.coords__init__(以及其他任何地方)使用GetCoords而不是coords,将该局部变量更改为成员变量。