Welcome, guest | Sign In | My Account | Store | Cart

Three functions useful for acting upon a key pressed in a Windows text only console application. Typical usage would be in the design of menu selections and escaping from loops.

Python, 183 lines
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
def keypress(): 
    """
    Waits for the user to press a key. Returns the ascii code 
    for the key pressed or zero for a function key pressed.
    """                             
    import msvcrt               
    while 1:
        if msvcrt.kbhit():              # Key pressed?
            a = ord(msvcrt.getch())     # get first byte of keyscan code     
            if a == 0 or a == 224:      # is it a function key?
                msvcrt.getch()          # discard second byte of key scan code
                return 0                # return 0
            else:
                return a                # else return ascii code


def funkeypress():
    """
    Waits for the user to press any key including function keys. Returns 
    the ascii code for the key or the scancode for the function key.
    """
    import msvcrt
    while 1:
        if msvcrt.kbhit():                  # Key pressed?
            a = ord(msvcrt.getch())         # get first byte of keyscan code  
            if a == 0 or a == 224:          # is it a function key?
                b = ord(msvcrt.getch())     # get next byte of key scan code
                x = a + (b*256)             # cook it.
                return x                    # return cooked scancode
            else:
                return a                    # else return ascii code

def anykeyevent():
    """
    Detects a key or function key pressed and returns its ascii or scancode.
    """
    import msvcrt
    if msvcrt.kbhit():
        a = ord(msvcrt.getch())
        if a == 0 or a == 224:
            b = ord(msvcrt.getch())
            x = a + (b*256)
            return x
        else:
            return a

# -----------------------------------------------------------------------------
                         # demo applications.

def about(): return\
    """
    Keys reported: ENTER, comma, period, greater-than, less-than.
    Upper and lower case keys:  A, C, H, Q.
    Function Keys: F1, SHIFT-F1, CTRL-F1, ALT-F1, Left arrow,
    right arrow, page up, page down.

    Any other keys are assigned to print "Default"
    Pressing ESC or Q will initiate exit query.
    Pressing A will print this text.
    """

def keycommands(x):
    if x == 13:                                 # ENTER
        print 'ENTER pressed'
        return True
    if x in map(ord,'aA'):                      # A
        print about()
        return True
    if x in map(ord,'cC'):                      # C
        print 'Continue'
        return True
    if x in map(ord,'hH'):                      # H
        print 'HELP'
        return True
    if x in map(ord,'qQ') or x == 27:           # Q or ESC
        print 'Press any key to exit.'
        keypress()
        print 'Bye'
        return False                             
    if x == ord(','):                           # ,
        print 'Comma'
        return True
    if x == ord('.'):                           # .
        print 'Period'
        return True
    if x == ord('>'):                           # >
        print 'Greater Than'
        return True
    if x == ord('<'):                           # <
        print 'Less Than'
        return True
    if x == 15104:                              # F1
        print 'F1'
        return True
    if x == 21504:                              # SHIFT-F1
        print 'SHIFT-F1'
        return True
    if x == 24064:                              # CTRL-F1
        print 'CNTRL-F1'
        return True
    if x == 26624:                              # ALT-F1
        print 'ALT-F1'
        return True
    if x == 18912:                              # PAGE UP
        print 'PAGE UP'
        return True
    if x == 20960:                              # PAGE DOWN
        print 'PAGE DOWN'
        return True
    if x == 19424:                              # LEFT ARROW KEY
        print 'LEFT ARROW KEY'
        return True
    if x == 19936:                              # RIGHT ARROW KEY
        print 'RIGHT ARROW KEY'
        return True
    print 'Default'                             # Any remaining keys
    return True

def validating(x):
    if x in map(ord,'hH'):          # query if help is needed
        print 'Would you like to see the help menu? <y/n>',
        if keypress() in map(ord,'yY'):
            return ord('h')         # help needed
        else:  return ord('c')      # help not needed
    if x in map(ord,'qQ'):          # query if quitting is requested
        print 'Would you like to quit? <y/n>',
        if keypress() in map(ord,'yY'):
            return ord('q')         # quit
        else: return ord('c')       # don't quit
    return x                        # otherwise, x is any key other than H,Q.

                    #################################
                    # The keypress interpreter demo #
                    #################################
def commandloop():                  
    print 'Keypress interpreter utility.'
    print about()
    print 'Waiting...'
    interpreting=True
    while interpreting:
        interpreting=keycommands(validating(funkeypress()))

                   ####################################
                   # The IBM scancode display utility #
                   ####################################
def scancode():                     
    print 'IBM scancode utility.\nPress CTRL-C to quit.'
    while 1:
        x=funkeypress()
        print 'Dec: %d Hex: %x' % (x,x)

                         ########################
                         # The Exit key example #
                         ########################
def exitkey():                      
    x = True
    while x != 20448:               # END key?
        print 'o',
        x = anykeyevent()           # key?
        if x == 20448 : break       # if END key touched, then break.
        elif x == None: continue    # if no key touched, continue printing.
        else:                       # if other key touched, prompt user.
            print '\nPress END key to exit. Other key to continue printing.'
            x = funkeypress()

#------------------------------------------------------------------------------
                                 # The main loop.

if __name__ == '__main__':          
    while 1:
        print """Please select one from the menu:
        [1] Keypress interpreter demo.
        [2] IBM scancode utility.
        [3] Exit key example\n"""    
        x=keypress()
        if x == ord('1'): commandloop()
        if x == ord('2'): scancode()
        if x == ord('3'): exitkey()
        print 'Press q to quit.\n'
        if keypress() in map(ord,'qQ'):
            break
        else:
            continue
        

I've included demo applications the user may select from the main loop that demonstrate keyboard detection functionality:

commandloop(), a keypress interpreter demo, implements a 'case' statement to invoke an action to perform dependant upon what key is pressed.

scancode(), an IBM keyboard scancode utility for displaying the assigned keycode for the key pressed.

exitkey(), an example of detecting a key pressed. Prints an 'o' until key is pressed. Exit if END is pressed. Otherwise prints a message and waits for user input.

Because the keypress(),funkeypress(), and anykeyevent() functions are limited to Windows text only console applications, you'll either need to run the included demo application by double-clicking on the filename of this script from Windows Explorer, drag the filename to the START/RUN file launcher, or import the script into Python.exe. A script that uses either of these functions in a GUI based application or editing a file in the IDLE environment and pressing CNTL+F5 for example, will not run properly.

4 comments

Thomas Heller 20 years, 11 months ago  # | flag

Busy waiting. Why are you looping in the first two functions, waiting for kbhit()? Calling getch() should do the trick already.

robert gillies (author) 20 years, 10 months ago  # | flag

Using mscvrt.kbhit() allows me to hit CNTL-C to abort a function. But you're right that it's not necessary to use it in conjunction with getch().

Robert

Maxim 13 years, 6 months ago  # | flag

Thanks for the solution! It was very helpfull to me. In my own script I just fix one thing:

def func():
    import msvcrt, time      # time module added
    while 1:
        if msvcrt.kbhit():
            ... (your code)
    time.sleep(0.25)         # this line added

this makes a processor idle for a 0.25 second in each cycle for me it's important, because, for example, on my Core i3 330M CPU I've got a 10-15% workload until the while-loop in progress after adding sleep(0.25) the CPU works normally without visible workload

I guess that 0.25 second is not critical if we are talking about keyboard input, anyway this value can be decreased...

...or, maybe anybody knows better solution?

Maxim 13 years, 6 months ago  # | flag

Sorry, missprinting. time.sleep(0.25) should be inside the while-loop:

def func():
    import msvcrt, time      # time module added
    while 1:
        if msvcrt.kbhit():
            ... (your code)
        time.sleep(0.25)         # this line added (fixed indentation)
Created by robert gillies on Tue, 29 Apr 2003 (PSF)
Python recipes (4591)
robert gillies's recipes (1)

Required Modules

Other Information and Tasks