Python Forum

Full Version: msvcrt.getch prints twice inside loop
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
A recent Windows update (Windows 10 version 1803 feature update) seems to have changed the behaviour of msvcrt.getch(). I confirmed this by testing on a second computer before and after the update.

this code:
import msvcrt

while True:
    character = msvcrt.getch()
    print('Hello World')
prints twice for each keystroke:
Output:
Hello World Hello World
tested with python 3.6.5 and 3.5.3

Am I going crazy, or am I using msvcrt.getch() wrong since the Windows update?

What I really need is to put a keystroke in to a variable and print it to the command line, but it's printing an extra line after each character and my program no longer works.

hitting the 'b' key three times with this code:
import msvcrt

while True:
    character = msvcrt.getch().decode('utf-8')
    print(character)
Gives this output:
Output:
b b b
Any ideas as to what has changed?
Can do a test,use repr() to see what really going on.
import msvcrt

while True:
    character = msvcrt.getch()
    print(repr(character))
    if character == b'q':
        break
So i get b'\x00' normal key and b'\xe0' for function keys.
>>> import msvcrt
>>> help(msvcrt.getch)
Help on built-in function getch in module msvcrt:

getch()
    Read a keypress and return the resulting character as a byte string.
    
    Nothing is echoed to the console. This call will block if a keypress is
    not already available, but will not wait for Enter to be pressed. If the
    pressed key was a special function key, this will return '\000' or
    '\xe0'; the next call will return the keycode. The Control-C keypress
    cannot be read with this function.
So it say that next call will return keycode.
Then can write it like this.
import msvcrt
import sys

while True:
    ch = msvcrt.getch()
    if ch in b'\x00':
        ch = msvcrt.getch() # Second call returns the scan code
    if ch in b'\xe0':
        ch = msvcrt.getch() # Second call Function keys
    if ch == b'q':
       sys.exit()
    else:
       print(f'Key Pressed: {ch}')
Output:
E:\1 λ python b1.py Key Pressed: b'h' Key Pressed: b'e' Key Pressed: b'l' Key Pressed: b'l' Key Pressed: b'o' Key Pressed: b'I' Key Pressed: b'I' Key Pressed: b'I'
Writing hello and 3 times Page Up,as you see no extra space.
Not doing any encoding here utf-8 may be wrong as key are ASCII.
Here is the table.
I pushed Page Up 3 times last b'I'
Let see if it match with table that say Page Up is 73.
>>> k = b'I'
>>> ord(k)
73
Also look at doc there are eg msvcrt.getwche() which return Unicode.
Did not test that,maybe it easier to keep all as bytes and use ord() if need also need function key decoding.
Snippsat, I didn't think I'd get such a helpful response so quickly. I think I understand what is happening now.

When I run:
import msvcrt

count = 0

while True:
    count += 1
    character = msvcrt.getch()
    
    print('loop ' + str(count))
    print(character, end='\n\n')
        
    if character == b'q':
        break
I get two lines returned for each key press because the loop goes through two iterations per key press.
Hitting the 'E' key once returns this:
Output:
loop 1 b'e' loop 2 b'\x00'
According to the doc you referenced, what I should have been using all this time is msvcrt.getwch() to put a unicode character in to a variable without echoing the key stroke to the command line.

I still wonder why my programs were running fine until Windows 10 feature update 1803, but at least I have the right fix.

Thank you so much for your help!
I just realised I have one Windows 10 computer not updated to 1803 yet, and sure enough, msvcrt.getch() does not return the scan code. Only one call to msvcrt.getch() is made when in a loop:

The same script as before only returns the key pressed:

Output:
loop 1 b'e'
What changed with Windows feature update 1803?
Great stuff!

BTW ReallyBigTeeth,
You can simplify your print statement below with:
print('loop', count)
(Jun-23-2018, 05:29 AM)ReallyBigTeeth Wrote: [ -> ]When I run:
import msvcrt

count = 0

while True:
    count += 1
    character = msvcrt.getch()
    
    print('loop ' + str(count))
    print(character, end='\n\n')
        
    if character == b'q':
        break
Good point. I think I get that from converting character and string types in C++. It drives me insane. I wish I could you use python syntax for C++ code.