Re: [Tutor] [ctypes-users] Press ESC to exit()
holy cow On Mon, May 1, 2017 at 8:02 PM eryk sun wrote: > On Mon, May 1, 2017 at 6:28 PM, Michael C > wrote: > > Hi all, I found out that one way to press ESC to kill the script was to > use > > my previous > > script language, AutoHotKey and this is how it works: > > > > AutoHotKey code > > ## function that kills the window with title '*Python 3.6.1 Shell*' > > > > kill() > > { > > WinKill, *Python 3.6.1 Shell* > > } > > > > ## When ESC is pressed, runs the function 'kill' > > Esc::kill() > > > > Yes, that's the entire script. > > Is there a way to write it in Python on windows? > > Instead of using a hotkey, you can set a global low-level keyboard > hook. Then optionally you can check for a particular foreground window > before handling the key. For example: > > import ctypes > import win32con > import win32gui > > from ctypes import wintypes > > user32 = ctypes.WinDLL('user32', use_last_error=True) > > VK_ESCAPE = 0x1B > > LLKHF_EXTENDED = 0x0001 > LLKHF_LOWER_IL_INJECTED = 0x0002 > LLKHF_INJECTED = 0x0010 > LLKHF_ALTDOWN = 0x0020 > LLKHF_UP= 0x0080 > > ULONG_PTR = wintypes.WPARAM > class KBDLLHOOKSTRUCT(ctypes.Structure): > _fields_ = (('vkCode', wintypes.DWORD), > ('scanCode',wintypes.DWORD), > ('flags', wintypes.DWORD), > ('time',wintypes.DWORD), > ('dwExtraInfo', ULONG_PTR)) > > HOOKPROC = ctypes.WINFUNCTYPE(wintypes.LPARAM, ctypes.c_int, > wintypes.WPARAM, wintypes.LPARAM) > > user32.SetWindowsHookExW.restype = wintypes.HHOOK > user32.SetWindowsHookExW.argtypes = ( > ctypes.c_int, # _In_ idHook > HOOKPROC, # _In_ lpfn > wintypes.HINSTANCE, # _In_ hMod > wintypes.DWORD) # _In_ dwThreadId > > user32.CallNextHookEx.restype = wintypes.LPARAM > user32.CallNextHookEx.argtypes = ( > wintypes.HHOOK, # _In_opt_ hhk > ctypes.c_int,# _In_ nCode > wintypes.WPARAM, # _In_ wParam > wintypes.LPARAM) # _In_ lParam > > def keyboard_hook(handler, hwnd=None): > @HOOKPROC > def hookfunc(nCode, wParam, lParam): > event = KBDLLHOOKSTRUCT.from_address(lParam) > if hwnd is not None and win32gui.GetForegroundWindow() == hwnd: > handler(event) > return user32.CallNextHookEx(hHook, nCode, wParam, lParam) > > hHook = user32.SetWindowsHookExW(win32con.WH_KEYBOARD_LL, hookfunc, > None, 0) > if not hHook: > raise ctypes.WinError(ctypes.get_last_error()) > > win32gui.PumpMessages() > > if __name__ == '__main__': > import msvcrt > import threading > import win32console > > print('Press escape to quit') > > def escape_handler(event): > if event.vkCode == VK_ESCAPE: > # kill the hook thread > win32gui.PostQuitMessage(0) > elif not (event.flags & LLKHF_UP): > print('Virtual Key Code: {:#04x} [{}]'.format(event.vkCode, > event.time)) > > t = threading.Thread(target=keyboard_hook, args=(escape_handler, > win32console.GetConsoleWindow()), daemon=True) > t.start() > > # consume keyboard input > while t.is_alive(): > if msvcrt.kbhit(): > msvcrt.getwch() > > The __main__ demo should be run as a console script. The keyboard hook > filters on the console window handle from GetConsoleWindow(). > ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] [ctypes-users] Press ESC to exit()
On Tue, May 2, 2017 at 3:03 AM, Michael C wrote: > holy cow The code for a global keyboard hook is a bit complex - mostly because I had to use ctypes (properly instead of an unreliable hack). Normally an application has one or more windows and a message loop, in which case there's no need for such an awkward approach. But you're asking to do this from a console application, which doesn't own a window. The window procedure that gets keyboard input from the system is in the console host process (conhost.exe). It processes input as a sequence of events that it stores in its input buffer. Thus you could use the input buffer instead of a global keyboard hook, but that comes with its own set of problems. You're no longer getting notified as soon as the character is typed in the window. Instead, the escape-key monitor has to share the input buffer with the main application. Events can be read without removing them via PeekConsoleInput, or read and removed via ReadConsoleInput. They can also be flushed (discarded) via FlushConsoleInputBuffer. If your script doesn't read from the console and the escape-key monitory only peeks the input buffer, it will grow without bound. However, it shouldn't read or flush input events that the main task needs, and the main task shouldn't read events that causes the escape-key monitoring thread to miss the user pressing escape. It needs an integrated design. ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] [ctypes-users] Press ESC to exit()
On Mon, May 1, 2017 at 6:28 PM, Michael C wrote: > Hi all, I found out that one way to press ESC to kill the script was to use > my previous > script language, AutoHotKey and this is how it works: > > AutoHotKey code > ## function that kills the window with title '*Python 3.6.1 Shell*' > > kill() > { > WinKill, *Python 3.6.1 Shell* > } > > ## When ESC is pressed, runs the function 'kill' > Esc::kill() > > Yes, that's the entire script. > Is there a way to write it in Python on windows? Instead of using a hotkey, you can set a global low-level keyboard hook. Then optionally you can check for a particular foreground window before handling the key. For example: import ctypes import win32con import win32gui from ctypes import wintypes user32 = ctypes.WinDLL('user32', use_last_error=True) VK_ESCAPE = 0x1B LLKHF_EXTENDED = 0x0001 LLKHF_LOWER_IL_INJECTED = 0x0002 LLKHF_INJECTED = 0x0010 LLKHF_ALTDOWN = 0x0020 LLKHF_UP= 0x0080 ULONG_PTR = wintypes.WPARAM class KBDLLHOOKSTRUCT(ctypes.Structure): _fields_ = (('vkCode', wintypes.DWORD), ('scanCode',wintypes.DWORD), ('flags', wintypes.DWORD), ('time',wintypes.DWORD), ('dwExtraInfo', ULONG_PTR)) HOOKPROC = ctypes.WINFUNCTYPE(wintypes.LPARAM, ctypes.c_int, wintypes.WPARAM, wintypes.LPARAM) user32.SetWindowsHookExW.restype = wintypes.HHOOK user32.SetWindowsHookExW.argtypes = ( ctypes.c_int, # _In_ idHook HOOKPROC, # _In_ lpfn wintypes.HINSTANCE, # _In_ hMod wintypes.DWORD) # _In_ dwThreadId user32.CallNextHookEx.restype = wintypes.LPARAM user32.CallNextHookEx.argtypes = ( wintypes.HHOOK, # _In_opt_ hhk ctypes.c_int,# _In_ nCode wintypes.WPARAM, # _In_ wParam wintypes.LPARAM) # _In_ lParam def keyboard_hook(handler, hwnd=None): @HOOKPROC def hookfunc(nCode, wParam, lParam): event = KBDLLHOOKSTRUCT.from_address(lParam) if hwnd is not None and win32gui.GetForegroundWindow() == hwnd: handler(event) return user32.CallNextHookEx(hHook, nCode, wParam, lParam) hHook = user32.SetWindowsHookExW(win32con.WH_KEYBOARD_LL, hookfunc, None, 0) if not hHook: raise ctypes.WinError(ctypes.get_last_error()) win32gui.PumpMessages() if __name__ == '__main__': import msvcrt import threading import win32console print('Press escape to quit') def escape_handler(event): if event.vkCode == VK_ESCAPE: # kill the hook thread win32gui.PostQuitMessage(0) elif not (event.flags & LLKHF_UP): print('Virtual Key Code: {:#04x} [{}]'.format(event.vkCode, event.time)) t = threading.Thread(target=keyboard_hook, args=(escape_handler, win32console.GetConsoleWindow()), daemon=True) t.start() # consume keyboard input while t.is_alive(): if msvcrt.kbhit(): msvcrt.getwch() The __main__ demo should be run as a console script. The keyboard hook filters on the console window handle from GetConsoleWindow(). ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor