1

I have a Tkinter GUI application that I need to hide on a buttonpress. I cannot assume that the application will have focus, so I implemented pyHook, keylogger-style. However, whenever I call withdraw() from a function launched by pyHook, the window hangs and I have to force-close it.

To test, I added a button inside the GUI itself to call exactly the same function, and it works just fine. What's going on? 'hiding' prints both times, so I know it is really hanging on the withdraw() call itself.

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.button = Button(root, text="Withdraw", command=self.Hide) # works fine
        self.button.grid()

    def ButtonPress(self, scancode, ascii):
        if scancode == 82:   # kp_0
            self.Hide() # hangs

    def Hide(self):
        print 'hiding'
        self.root.withdraw()
        time.sleep(2)
        self.root.deiconify()

root = Tk()
TestingGUI = TestingGUI(root)

def keypressed(event):
    key = chr(event.Ascii)
    # have to start thread in order to return True as required by pyHook
    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()
heidi
  • 655
  • 6
  • 20
  • Threads and tkinter don't mix well, see for example http://stackoverflow.com/a/10556698/5781248 – J.J. Hakala Jun 19 '16 at 20:30
  • I know there are existing issues, however, you'll notice that `mainloop()` *is* running in the main thread. Additionally, this whole project works just fine in linux when I use X's record context. This is definitely a pyHook-related problem. – heidi Jun 19 '16 at 22:42
  • 1
    `threading.Thread(target=TestingGUI.ButtonPress, args=(event.ScanCode,key)).start()` in function `keypressed` looks like it causes a tk widget being called outside main thread i.e. `self.root.withdraw()` and `self.root.deiconify()` calls. – J.J. Hakala Jun 19 '16 at 23:20

1 Answers1

1

Solved as per this answer here:

Essentially, the function inside the Tkinter class responsible for dealing with keypresses, ButtonPress, cannot be called from another thread.

Community
  • 1
  • 1
heidi
  • 655
  • 6
  • 20