使用 pyHook 的 Tkinter 文本输入挂起 GUI window
Tkinter text entry with pyHook hangs GUI window
我有一个 Tkinter GUI 应用程序,我需要在其中输入文本。我不能假设该应用程序会有焦点,所以我实现了 pyHook,键盘记录器风格。
当 GUI window 没有焦点时,文本输入工作正常并且 StringVar 正确更新。当 GUI window 确实 有焦点并且我尝试输入文本时,整个过程崩溃了。
即,如果我在启动程序后单击控制台 window 或其他任何东西,文本输入工作。如果我尝试立即输入文本(GUI 以焦点开始),或者我在任何时候重新聚焦 window 并输入文本,它就会崩溃。
怎么回事?
下面是一个最小的完整可验证示例来说明我的意思:
from Tkinter import *
import threading
import time
try:
import pythoncom, pyHook
except ImportError:
print 'The pythoncom or pyHook modules are not installed.'
# main gui box
class TestingGUI:
def __init__(self, root):
self.root = root
self.root.title('TestingGUI')
self.search = StringVar()
self.searchbox = Label(root, textvariable=self.search)
self.searchbox.grid()
def ButtonPress(self, scancode, ascii):
self.search.set(ascii)
root = Tk()
TestingGUI = TestingGUI(root)
def keypressed(event):
key = chr(event.Ascii)
threading.Thread(target=TestingGUI.ButtonPress, args=(event.ScanCode,key)).start()
return True
def startlogger():
obj = pyHook.HookManager()
obj.KeyDown = keypressed
obj.HookKeyboard()
pythoncom.PumpMessages()
# need this to run at the same time
logger = threading.Thread(target=startlogger)
# quits on main program exit
logger.daemon = True
logger.start()
# main gui loop
root.mainloop()
我修改了问题(和另一个)中给出的源代码,以便 pyHook
相关回调函数发送键盘事件相关数据到
队列。 GUI 对象被通知事件的方式可能看起来
不必要的复杂。正在尝试调用 root.event_generate
keypressed
好像挂了。还有 set
方法
threading.Event
调用时似乎引起了麻烦
keypressed
.
调用keypressed
的上下文,可能在
麻烦了。
from Tkinter import *
import threading
import pythoncom, pyHook
from multiprocessing import Pipe
import Queue
import functools
class TestingGUI:
def __init__(self, root, queue, quitfun):
self.root = root
self.root.title('TestingGUI')
self.queue = queue
self.quitfun = quitfun
self.button = Button(root, text="Withdraw", command=self.hide)
self.button.grid()
self.search = StringVar()
self.searchbox = Label(root, textvariable=self.search)
self.searchbox.grid()
self.root.bind('<<pyHookKeyDown>>', self.on_pyhook)
self.root.protocol("WM_DELETE_WINDOW", self.on_quit)
self.hiding = False
def hide(self):
if not self.hiding:
print 'hiding'
self.root.withdraw()
# instead of time.sleep + self.root.deiconify()
self.root.after(2000, self.unhide)
self.hiding = True
def unhide(self):
self.root.deiconify()
self.hiding = False
def on_quit(self):
self.quitfun()
self.root.destroy()
def on_pyhook(self, event):
if not queue.empty():
scancode, ascii = queue.get()
print scancode, ascii
if scancode == 82:
self.hide()
self.search.set(ascii)
root = Tk()
pread, pwrite = Pipe(duplex=False)
queue = Queue.Queue()
def quitfun():
pwrite.send('quit')
TestingGUI = TestingGUI(root, queue, quitfun)
def hook_loop(root, pipe):
while 1:
msg = pipe.recv()
if type(msg) is str and msg == 'quit':
print 'exiting hook_loop'
break
root.event_generate('<<pyHookKeyDown>>', when='tail')
# functools.partial puts arguments in this order
def keypressed(pipe, queue, event):
queue.put((event.ScanCode, chr(event.Ascii)))
pipe.send(1)
return True
t = threading.Thread(target=hook_loop, args=(root, pread))
t.start()
hm = pyHook.HookManager()
hm.HookKeyboard()
hm.KeyDown = functools.partial(keypressed, pwrite, queue)
try:
root.mainloop()
except KeyboardInterrupt:
quit_event.set()
我有一个 Tkinter GUI 应用程序,我需要在其中输入文本。我不能假设该应用程序会有焦点,所以我实现了 pyHook,键盘记录器风格。
当 GUI window 没有焦点时,文本输入工作正常并且 StringVar 正确更新。当 GUI window 确实 有焦点并且我尝试输入文本时,整个过程崩溃了。
即,如果我在启动程序后单击控制台 window 或其他任何东西,文本输入工作。如果我尝试立即输入文本(GUI 以焦点开始),或者我在任何时候重新聚焦 window 并输入文本,它就会崩溃。
怎么回事?
下面是一个最小的完整可验证示例来说明我的意思:
from Tkinter import *
import threading
import time
try:
import pythoncom, pyHook
except ImportError:
print 'The pythoncom or pyHook modules are not installed.'
# main gui box
class TestingGUI:
def __init__(self, root):
self.root = root
self.root.title('TestingGUI')
self.search = StringVar()
self.searchbox = Label(root, textvariable=self.search)
self.searchbox.grid()
def ButtonPress(self, scancode, ascii):
self.search.set(ascii)
root = Tk()
TestingGUI = TestingGUI(root)
def keypressed(event):
key = chr(event.Ascii)
threading.Thread(target=TestingGUI.ButtonPress, args=(event.ScanCode,key)).start()
return True
def startlogger():
obj = pyHook.HookManager()
obj.KeyDown = keypressed
obj.HookKeyboard()
pythoncom.PumpMessages()
# need this to run at the same time
logger = threading.Thread(target=startlogger)
# quits on main program exit
logger.daemon = True
logger.start()
# main gui loop
root.mainloop()
我修改了问题(和另一个)中给出的源代码,以便 pyHook
相关回调函数发送键盘事件相关数据到
队列。 GUI 对象被通知事件的方式可能看起来
不必要的复杂。正在尝试调用 root.event_generate
keypressed
好像挂了。还有 set
方法
threading.Event
调用时似乎引起了麻烦
keypressed
.
调用keypressed
的上下文,可能在
麻烦了。
from Tkinter import *
import threading
import pythoncom, pyHook
from multiprocessing import Pipe
import Queue
import functools
class TestingGUI:
def __init__(self, root, queue, quitfun):
self.root = root
self.root.title('TestingGUI')
self.queue = queue
self.quitfun = quitfun
self.button = Button(root, text="Withdraw", command=self.hide)
self.button.grid()
self.search = StringVar()
self.searchbox = Label(root, textvariable=self.search)
self.searchbox.grid()
self.root.bind('<<pyHookKeyDown>>', self.on_pyhook)
self.root.protocol("WM_DELETE_WINDOW", self.on_quit)
self.hiding = False
def hide(self):
if not self.hiding:
print 'hiding'
self.root.withdraw()
# instead of time.sleep + self.root.deiconify()
self.root.after(2000, self.unhide)
self.hiding = True
def unhide(self):
self.root.deiconify()
self.hiding = False
def on_quit(self):
self.quitfun()
self.root.destroy()
def on_pyhook(self, event):
if not queue.empty():
scancode, ascii = queue.get()
print scancode, ascii
if scancode == 82:
self.hide()
self.search.set(ascii)
root = Tk()
pread, pwrite = Pipe(duplex=False)
queue = Queue.Queue()
def quitfun():
pwrite.send('quit')
TestingGUI = TestingGUI(root, queue, quitfun)
def hook_loop(root, pipe):
while 1:
msg = pipe.recv()
if type(msg) is str and msg == 'quit':
print 'exiting hook_loop'
break
root.event_generate('<<pyHookKeyDown>>', when='tail')
# functools.partial puts arguments in this order
def keypressed(pipe, queue, event):
queue.put((event.ScanCode, chr(event.Ascii)))
pipe.send(1)
return True
t = threading.Thread(target=hook_loop, args=(root, pread))
t.start()
hm = pyHook.HookManager()
hm.HookKeyboard()
hm.KeyDown = functools.partial(keypressed, pwrite, queue)
try:
root.mainloop()
except KeyboardInterrupt:
quit_event.set()