Re: [Tutor] [ctypes-users] Press ESC to exit()

2017-05-02 Thread Michael C
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()

2017-05-01 Thread eryk sun
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()

2017-05-01 Thread eryk sun
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