【问题标题】:Python - Check for keypresses without using 100% cpuPython - 在不使用 100% cpu 的情况下检查按键
【发布时间】:2013-01-10 00:27:41
【问题描述】:

我正在使用 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”

【问题讨论】:

    标签: python multithreading winapi tkinter keypress


    【解决方案1】:

    您想捕捉按键事件,而不是轮询当前的键盘状态。

    请参阅 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)
    

    GetCoords 是在Application.__init__ 中定义的本地函数。但是您尝试将其用作Application 的方法。你不能做self.GetCoords,除非GetCoordsself的方法。这正是错误消息AttributeError: Application instance has no attribute 'GetCoords' 的含义。

    但是你可以只传递本地函数GetCoords,只需去掉self.前缀。我不确定这会如你所想(因为我不确定你是否可以像那样关闭StringVar),但是……试试看。

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

    【讨论】:

    • 所以我尝试使用绑定,但我知道我做错了什么,我无法完全弄清楚我必须做什么。我使用了这个:root.bind_all("", self.GetMouse) 但是当我尝试运行脚本时我得到了这个: AttributeError: Application instance has no attribute 'GetMouse' 我想也许我把它放在了错误的地方但无论我把它放在哪里,我都会得到一个错误。
    • 首先,我不确定您为什么要将特定的击键绑定到名为GetMouse 的方法,而不是doScrollLock。但是无所谓。您是否真的在 Application 类上创建了一个名为 GetMouse 的方法?
    • @WichidNixin:好的,现在我看到你更新的问题了。请查看我的更新答案。
    • 非常感谢您帮助我了解发生了什么,我现在可以正常工作了,我只需要删除“自我”。从 bind_all 语句
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-16
    • 2019-04-25
    • 2021-07-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多