Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Processing a key pressed in Python 3.6

3,049 views
Skip to first unread message

Virgil Stokes

unread,
Jan 23, 2018, 2:01:17 PM1/23/18
to
I would appreciate help on finding a solution to following problem. This
is the general structure of the "problem" code:

> while True
>     # Get some data from the web and process it
>     ...
>     ...
>     # Write these data to a file
>     ...
>     ...
>     # Check if a_key has been pressed in the command window
>     ...
>     ...
>     if a_key_pressed:
>         # Perform some pre-termination tasks
>         ...
>         ...
>         # Close the output data file
>         ...
>         ...
>         raise SystemExit('Exit')

I am running the code with Python 3.6 on a windows 10 platform. I have
tried many approaches (mainly those posted on stackoverflow) but I have
yet to find an approach that works for this structure.

Note:
  1) The code is executed in the windows 10 command window
  2) I am not using wxPython, IDLE, or pyGame in this application.
  3) The time to get the data, process it and write it to a file can
     take from 0.5 sec to 1.5 sec
  4) The key hit need not be echoed to the command window


Chris Angelico

unread,
Jan 23, 2018, 2:16:15 PM1/23/18
to
Are you okay with demanding a specific key, rather than simply "press
any key"? Even better, key combination? Handle Ctrl-C by catching
KeyboardInterrupt and you can take advantage of Python's existing
cross-platform handling of the standard interrupt signal.

If Ctrl-C won't work for you, how about stipulating that it be Enter?
"Press Enter to quit" isn't too much worse than "Press any key to
quit" (plus you have less chance of accidentally terminating the
program when you don't want to). Spin off a thread to wait for enter.
I've tested this only on Linux, but it ought to work:

import threading
import time

shutdown = False
def wait_for_enter():
print("Hit Enter to quit.")
input()
global shutdown; shutdown = True

threading.Thread(target=wait_for_enter).start()

while "more work to do":
print("Getting data...")
time.sleep(1)
print("Saving data to file...")
time.sleep(1)
if shutdown:
print("Pre-termination...")
time.sleep(1)
raise SystemExit("exit")

If it doesn't, try switching around which is the secondary thread and
which is the primary - spin off a thread to do the work, then call
input() in the main thread.

ChrisA

Virgil Stokes

unread,
Jan 23, 2018, 6:29:59 PM1/23/18
to
Another follow-up question:

How would this code be modified to handle using the "Esc" key instead of
the "Enter" key?

eryk sun

unread,
Jan 23, 2018, 8:10:29 PM1/23/18
to
On Tue, Jan 23, 2018 at 11:29 PM, Virgil Stokes <v...@it.uu.se> wrote:
>
> How would this code be modified to handle using the "Esc" key instead of the
> "Enter" key?

The input() function depends on the console or terminal to read a line
of input. If you're using the Windows console, it calls the high-level
ReadConsole (or ReadFile) function, which performs a 'cooked' read. In
this case some keys are reserved. Escape is consumed to clear/flush
the input buffer. Function keys and arrows are consumed for line
editing and history navigation. And for some reason line-feed (^J) is
always ignored. Additionally, normal operation requires the following
input modes to be enabled:

ENABLE_PROCESSED_INPUT
process Ctrl+C as CTRL_C_EVENT and carriage return (^M)
as carriage return plus line feed (CRLF). consume
backspace (^H) to delete the previous character.

ENABLE_LINE_INPUT
return only when a carriage return is read.

ENABLE_ECHO_INPUT
echo read characters to the active screen.

Reading the escape key from the console requires the low-level
ReadConsoleInput, GetNumberOfConsoleInputEvents, and
FlushConsoleInputBuffer functions. These access the console's raw
stream of input event records, which includes key events, mouse
events, and window/buffer resize events. It's a bit complicated to use
this API, but fortunately the C runtime wraps it with convenient
functions such as _kbhit, _getwch, and _getwche (w/ echo). On Windows
only, you'll find these functions in the msvcrt module, but named
without the leading underscore. Here's an example of reading the
escape character without echo:

>>> msvcrt.getwch()
'\x1b'

Simple, no?

MRAB

unread,
Jan 23, 2018, 8:14:15 PM1/23/18
to
On 2018-01-23 23:29, Virgil Stokes wrote:
> Another follow-up question:
>
> How would this code be modified to handle using the "Esc" key instead of
> the "Enter" key?
>
This version uses msvcrt on Windows:

import msvcrt
import threading
import time

shutdown = False

def wait_for_enter():
global shutdown

print("Hit Enter to quit.")
# b'\x0D' is the bytestring produced by the Enter key.
# b'\x1B' is the bytestring produced by the Esc key.
if msvcrt.getch() == b'\x1B':
shutdown = True

threading.Thread(target=wait_for_enter).start()

while "more work to do":
print("Getting data...")
time.sleep(1)
print("Saving data to file...")
time.sleep(1)

if shutdown:
print("Pre-termination...")
time.sleep(1)
raise SystemExit("exit")


Another function of note is "msvcrt.kbhit()", which will tell you if
there's a key waiting to be read.

This is all in the docs for the msvcrt module.

Virgil Stokes

unread,
Jan 23, 2018, 8:14:24 PM1/23/18
to
Ok Dennis,

You were correct. The following also works in the Windows 10 command window.

> import time
> import msvcrt
>
> while "more work to do":
>     print("Getting data...")
>     time.sleep(1)
>     print("Saving data to file...")
>     time.sleep(1)
>     key = msvcrt.getwch()
>     #print('key: %s'%key)  # just to show the result
>     if key == chr(27):
>         print("Pre-termination...")
>         time.sleep(1)
>         raise SystemExit("exit")
Note, I am using the "Esc" key to exit, which is one answer to my last
posting on this topic.


On 2018-01-23 20:37, Dennis Lee Bieber wrote:
> On Tue, 23 Jan 2018 19:50:57 +0100, Virgil Stokes <v...@it.uu.se> declaimed
> the following:
>
>> I am running the code with Python 3.6 on a windows 10 platform. I have
>> tried many approaches (mainly those posted on stackoverflow) but I have
>> yet to find an approach that works for this structure.
>>
>> Note:
>>   1) The code is executed in the windows 10 command window
>>   2) I am not using wxPython, IDLE, or pyGame in this application.
>>   3) The time to get the data, process it and write it to a file can
>>      take from 0.5 sec to 1.5 sec
>>   4) The key hit need not be echoed to the command window
> And none of your searching found
> https://docs.python.org/3/library/msvcrt.html
> which is part of the standard library (and documented in the library
> manual). The module IS Windows specific, you'd have to rewrite the code to
> run on Linux.
>
> Now, if your requirement was to detect a keypress WHILE the data
> fetch/processing was happening, the solution will be different.

Virgil Stokes

unread,
Jan 24, 2018, 3:03:16 PM1/24/18
to
Yes, I am aware of this Dennis. However, but, on my system I actually
had it running without the msvcrt.kbhit()

This occurred during testing while trying different options. And I was
able to reproduce this several times. Why? I do not have an answer. This
is one reason why I posted the code. It would be interesting to know if
anyone else has obtained the same results.

Note: 1) 3.6.2 (v3.6.2:5fd33b5, Jul  8 2017, 04:57:36) [MSC v.1900 64
bit (AMD64)]
           2) msvcrt is built-in (at least on my system)

On 2018-01-24 18:28, Dennis Lee Bieber wrote:
> On Wed, 24 Jan 2018 02:13:35 +0100, Virgil Stokes <v...@it.uu.se>*declaimed* ?
> the following:
>
>
>
>>>     key = msvcrt.getwch()
> NOTE: per documentation, that is a blocking read... It won't return
> unless a key (any key) has been pressed. That means your "more work to do"
> loop requires a key press for each loop.
>
> Recommendation would be to use kbhit() first.
>
>
> if msvcrt.kbhit():
> key = msvcrt.getwch()
> ...
>
>
> Consider
>
> -=-=-=-=-
>
> import msvcrt as ms
> import time
>
> ESCAPE = chr(27)
>
> workPending = True
> cycle = 0
>
> while workPending:
> print("Getting imaginary data %s..." % cycle)
> time.sleep(1.5)
> print("\tSaving imaginary data %s..." % cycle)
> time.sleep(1.5)
>
> if ms.kbhit():
> key = ms.getwch()
> if key == ESCAPE:
> print("Pre-termination on cycle %s..." % cycle)
> break
> else:
> print("Random keypress %r found on cycle %s..." % (key, cycle))
>
> cycle += 1
>
> time.sleep(1.5)
> print("Termination")
> -=-=-=-=-
> C:\Users\Wulfraed\Documents\Python Progs>kbhit
> Getting imaginary data 0...
> Saving imaginary data 0...
> Getting imaginary data 1...
> Saving imaginary data 1...
> Random keypress u'd' found on cycle 1...
> Getting imaginary data 2...
> Saving imaginary data 2...
> Getting imaginary data 3...
> Saving imaginary data 3...
> Random keypress u'a' found on cycle 3...
> Getting imaginary data 4...
> Saving imaginary data 4...
> Pre-termination on cycle 4...
> Termination
>
> C:\Users\Wulfraed\Documents\Python Progs>

eryk sun

unread,
Jan 24, 2018, 5:09:01 PM1/24/18
to
On Wed, Jan 24, 2018 at 8:02 PM, Virgil Stokes <v...@it.uu.se> wrote:
> Yes, I am aware of this Dennis. However, but, on my system I actually had it
> running without the msvcrt.kbhit()

_getwch loops calling ReadConsoleInput until a key event is read. The
key can be typed manually in the console, or posted (i.e. PostMessage,
not SendMessage) to the console window as a WM_KEYDOWN message, or the
key event can be written directly to the input buffer via
WriteConsoleInput.

_kbhit calls PeekConsoleInput to scan for key events without removing
them from the input buffer. This call does not block.
0 new messages