1

I'm trying to register a system-wide hotkey that will trigger some action in my Tkinter program. I'm using this as reference. When I run the code from that link, it works fine. What I'm struggling with is integrating the loop there with the event loop in Tkinter.

The Tkinter loop needs to periodically check for the hotkey being pressed. This should be trivial, using root.after(). However, the program doesn't seem to pick up on the hotkey being pressed at all, not even when it's in focus.

Here is a succinct code example - it's as short as I could get it. It represents my attempts to modify the code from the link to play nicely with Tkinter.

from tkinter import *
import ctypes
from ctypes import wintypes
import win32con

user32 = ctypes.windll.user32
byref = ctypes.byref

def hotkey_handler(root):
    msg = wintypes.MSG()
    if user32.GetMessageA(byref(msg), None, 0, 0) != 0:
        if msg.message == win32con.WM_HOTKEY:
            if msg.wParam == 1:
                print("hotkey pressed")
    user32.TranslateMessage(byref(msg))
    user32.DispatchMessageA(byref(msg))
    root.after(1, hotkey_handler, root)

root = Tk()

if user32.RegisterHotKey(None, 1, win32con.MOD_SHIFT, ord("v")) != 0:
    print("--Hotkey registered!")

root.after(1, hotkey_handler, root)

root.mainloop()

One quirk I've noticed is that if I set the first argument of root.after() to zero, the GUI doesn't draw correctly and Python sometimes ends up crashing.

henrebotha
  • 1,244
  • 2
  • 14
  • 36
  • 1
    If you set the value to zero, it starves the event loop -- it is so busy emptying the never-empty "after" queue that it never has time to service any other types of events (such as redraw requests). – Bryan Oakley May 07 '14 at 20:58
  • @BryanOakley: Thanks, that clears that up. I figured it was something like that, but I have some trouble getting my head around the concept of event queues. – henrebotha May 07 '14 at 21:00
  • @eryksun: I know virtually nothing about multithreading. I am trying to avoid that sort of approach at all costs, partly because it seems like complete overkill for what I'm trying to achieve. The hotkey, when pressed, must trigger some sort of response in the Tk window. Doesn't it therefore follow that the hotkey message must be posted to the Tk window? Or am I misunderstanding (very likely!) how messages work? – henrebotha May 08 '14 at 10:41
  • @eryksun: Hallelujah! Thank you so much! Can't believe it was such a dumb thing... but then it always is, isn't it? :) (I wouldn't use Shift+V, this was just for illustration purposes. Win+V is actually my goal.) – henrebotha May 08 '14 at 13:22

1 Answers1

1

eryksun pointed out the problem - I was using the wrong case for the hotkey. ord("v") should have been ord("V").

henrebotha
  • 1,244
  • 2
  • 14
  • 36
  • 1
    I think [this answer](http://stackoverflow.com/a/40177310/2617691) is right way to handle RegisterHotKey and GetMessageA in tkinter. You would also need to quit (for example on closing app) waiting for message as GetMessageA returns only after it receives something. – matof Oct 21 '16 at 13:02
  • Thanks for the link man. Tkinter seems very poorly understood. – henrebotha Oct 21 '16 at 13:12