Freeze when using tkinter + pyhook. Two event loops and multithreading

为君一笑 提交于 2019-12-06 02:12:48

I've solved this problem with multiprocessing:

  1. the main process handles the GUI (MainThread) and a thread that consumes messages from the second process

  2. a child process hooks all mouse/keyboard events and pushes them to the main process (via a Queue object)

Thorbjorn

From the information that Tkinter needs to run in the main thread and not be called outside this thred, I found a solution:

My problem was that both PumpMessages and mainLoop needed to run in the main thread. In order to both receive inputs and show a Tkinter label with the amount of clicks I need to switch between running pumpMessages and briefly running mainLoop to update the display.

To make mainLoop() quit itself I used:

after(100,root.quit()) #root is the name of the Tk()
mainLoop()

so after 100 milliseconds root calls it's quit method and breaks out of its own main loop

To break out of pumpMessages I first found the pointer to the main thread:

mainThreadId = win32api.GetCurrentThreadId()

I then used a new thread that sends the WM_QUIT to the main thread (note PostQuitMessage(0) only works if it is called in the main thread):

win32api.PostThreadMessage(mainThreadId, win32con.WM_QUIT, 0, 0)

It was then possible to create a while loop which changed between pumpMessages and mainLoop, updating the labeltext in between. After the two event loops aren't running simultaneously anymore, I have had no problems:

def startTimerThread():
    while True:
        win32api.PostThreadMessage(mainThreadId, win32con.WM_QUIT, 0, 0)
        time.sleep(1)

mainThreadId = win32api.GetCurrentThreadId()
timerThread = Thread(target=startTimerThread)
timerThread.start()

while programRunning:
    label.configure(text=clicks)
    root.after(100,root.quit)
    root.mainloop()
    pythoncom.PumpMessages()

Thank you to Bryan Oakley for information about Tkinter and Boaz Yaniv for providing the information needed to stop pumpMessages() from a subthread

Tkinter isn't designed to be run from any thread other than the main one. It might help to put the GUI in the main thread and put the call to PumpMessages in a separate thread. Though you have to be careful and not call any Tkinter functions from the other thread (except perhaps event_generate).

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