Tkinter text entry with pyHook hangs GUI window

心已入冬 提交于 2019-12-13 07:04:52

问题


I have a Tkinter GUI application that I need to enter text in. I cannot assume that the application will have focus, so I implemented pyHook, keylogger-style.

When the GUI window does not have focus, text entry works just fine and the StringVar updates correctly. When the GUI window does have focus and I try to enter text, the whole thing crashes.

i.e., if I click on the console window or anything else after launching the program, text entry works. If I try entering text immediately (the GUI starts with focus), or I refocus the window at any point and enter text, it crashes.

What's going on?

Below is a minimal complete verifiable example to demonstrate what I mean:

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()

回答1:


I modified the source code given in the question (and the other one) so that the pyHook related callback function sends keyboard event related data to a queue. The way the GUI object is notified about the event may look needlessly complicated. Trying to call root.event_generate in keypressed seemed to hang. Also the set method of threading.Event seemed to cause trouble when called in keypressed.

The context where keypressed is called, is probably behind the trouble.

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()


来源:https://stackoverflow.com/questions/37909484/tkinter-text-entry-with-pyhook-hangs-gui-window

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!