Breaking out of a while loop with a key press?
Bengt Richter
bokr at oz.net
Tue Apr 1 00:00:01 EST 2003
On Mon, 31 Mar 2003 16:12:20 +0100, "Richard" <richardd at hmgcc.gov.uk> wrote:
>Hi,
>
>Can anyone suggest a simple way of detecting a key press and existing the
>program? I have a program which at the moment uses a while True: statement.
>However this is not ideal as I have to close down the console window to
>exist the program. Can anyone tell me the proper way of doing this? So that
>a CTRL-C, for example, can quit the program correctly?
>
Some time ago I coded this little experiment in interrupting a loop,
including keyboard input echoing the catching of Ctrl-C and Ctrl-Break
and putting off loop exit until the while condition. Perhaps you can
transform it into something serving your purposes. IIRC, if you comment
out the kbhit line, you will get different behavior. I don't recall testing
it on linux. Too lazy right now ;-) Note that the loop ends on the enter key
only if there was a Ctrl-C or Ctrl-Break during the line input and you have
not backspaced over their echoed representation (which should take one backspace
for one control char, even though the echo is multichar).
====< interruptpy.py >================================================
#!/usr/bin/python
# maybe useful part
import signal
class SigHandler:
def __init__(self):
self.signaled = 0
self.sn=None
reset = __init__
def __call__(self, sn, sf):
self.sn = sn # latest
self.signaled += 1
def test():
import msvcrt
sh = SigHandler()
old_SIGINT_Handler = signal.signal(signal.SIGINT,sh)
old_SIGBREAK_Handler = signal.signal(signal.SIGBREAK,sh)
signames = {
signal.SIGINT:'SIGINT',
signal.SIGBREAK:'SIGBREAK'
}
# just for this demo, but main loop might be useful
def puts(s): # helper
for c in s: msvcrt.putch(c)
CTRL_C_ECHO = '<CC>' # same length as repr, to line up
SIGINT_ECHO = '<Ctrl-C signal seen>'
SIGBREAK_ECHO = '<Ctrl-Break signal seen>'
# this plays the role of your loop body, see loop below
def dostuff():
print 'Enter a line: ',
line = c = ''
sigEchoes=[]
sh.reset()
while c != '\r': # Enter key ends input
# we could break on signals, but we want to finish line, so
# just echo and record them FTHOI
if sh.signaled and sh.sn:
sigEchoes.append((len(line),sh.sn)) # log where
if sh.sn==signal.SIGBREAK:
puts(SIGBREAK_ECHO)
elif sh.sn==signal.SIGINT:
puts(SIGINT_ECHO)
sh.sn = 0 # reset latest flag
if not msvcrt.kbhit(): continue # apparently screens ctrl-c?
c = msvcrt.getch() # should get ctrl-c but not ctrl-break?
if c=='\r': break # Enter
if c=='\b':
if sigEchoes and len(line)==sigEchoes[-1][0]:
# "erase" break effect"
puts('\b \b'*len(
(SIGINT_ECHO,SIGBREAK_ECHO)[sigEchoes[-1][1]==signal.SIGBREAK]
))
sh.signaled -= 1
sigEchoes = sigEchoes[:-1]
assert sh.signaled == len(sigEchoes) # how fast can you type ;-)
continue
elif line[-1:]=='\x03': # inc case it's getting to getch
puts('\b \b'*len(CTRL_C_ECHO))
else:
puts('\b \b'*(len(repr(line[-1:]))-2))
line = line[:-1]
elif c=='\x03':
puts(CTRL_C_ECHO)
line += c
else:
puts(repr(c)[1:-1])
line += c
print '\nIts repr was %s' % `line`
return sigEchoes
# main loop
print 'Type Ctrl-C(s) and/or Ctrl-Break(s) anywhere in input line.'
while not sh.signaled: # put this condition in your program loop
result = dostuff() # your stuff goes here
print """\
This should appear after dostuff() has done everything
as if not interrupted by signals."""
print 'Loop was terminated gracefully by %d signal%s\n' \
' %s\n' \
'setting a polled variable (not via exception).' % (
sh.signaled, 's'[sh.signaled==1:],
', '.join(map(lambda x: signames[x], [e[1] for e in result]))
)
# restore old signal handlers
signal.signal(signal.SIGINT,old_SIGINT_Handler)
signal.signal(signal.SIGBREAK,old_SIGBREAK_Handler)
if __name__ == '__main__':
test()
======================================================================
Example of running this on windows with python 2.2.2:
[20:55] C:\pywk>interruptpy.py
Type Ctrl-C(s) and/or Ctrl-Break(s) anywhere in input line.
Enter a line: 123<Ctrl-C signal seen>456<Ctrl-C signal seen>789<Ctrl-Break signal seen>abc.
Its repr was '123456789abc.'
This should appear after dostuff() has done everything
as if not interrupted by signals.
Loop was terminated gracefully by 3 signals
SIGINT, SIGINT, SIGBREAK
setting a polled variable (not via exception).
Regards,
Bengt Richter
More information about the Python-list
mailing list