Tkinter устраняет лишнее с помощью pyHook

У меня есть приложение Tkinter GUI, которое мне нужно скрыть при нажатии кнопки. Я не могу предположить, что у приложения будет фокус, поэтому я реализовал pyHook в стиле кейлоггера. Однако всякий раз, когда я вызываю метод remove() из функции, запущенной pyHook, окно зависает, и мне приходится принудительно закрывать его.

Для проверки я добавил кнопку внутри самого графического интерфейса для вызова точно такой же функции, и она отлично работает. Что происходит? 'hiding' печатает оба раза, поэтому я знаю, что он действительно висит на самом вызове remove().

Ниже приведен минимальный полный проверяемый пример, чтобы продемонстрировать, что я имею в виду:

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

person heidi    schedule 19.06.2016    source источник
comment
Потоки и tkinter плохо сочетаются, см., например, stackoverflow.com/a/10556698/5781248   -  person J.J. Hakala    schedule 19.06.2016
comment
Я знаю, что существуют проблемы, однако вы заметите, что mainloop() работает в основном потоке. Кроме того, весь этот проект отлично работает в Linux, когда я использую контекст записи X. Это определенно проблема, связанная с pyHook.   -  person heidi    schedule 20.06.2016
comment
threading.Thread(target=TestingGUI.ButtonPress, args=(event.ScanCode,key)).start() в функции keypressed выглядит так, как будто виджет tk вызывается вне основного потока, то есть вызовы self.root.withdraw() и self.root.deiconify().   -  person J.J. Hakala    schedule 20.06.2016


Ответы (1)


Решено в соответствии с этим ответом здесь:

По сути, функция внутри класса Tkinter, отвечающая за обработку нажатий клавиш, ButtonPress, не может быть вызвана из другого потока.

person heidi    schedule 21.06.2016