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

Breaking out of a while loop with a key press?

44 views
Skip to first unread message

Richard

unread,
Mar 31, 2003, 10:12:20 AM3/31/03
to
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?

Cheers

Richard


Gilles Lenfant

unread,
Mar 31, 2003, 11:51:20 AM3/31/03
to
"Richard" <rich...@hmgcc.gov.uk> a écrit dans le message de news:
3e88...@mail.hmgcc.gov.uk...

Catch the KeyboardInterrupt exception.

>
> Cheers
>
> Richard
>
>

Robin Munn

unread,
Mar 31, 2003, 1:08:16 PM3/31/03
to

You could use a "try: ... except KeyboardInterrupt: ..." block to detect
Ctrl-C. Although KeyboardInterrupt *should* by default not be caught, so
a Ctrl-C *should* be able to shut down your program. Are you by any
chance using a bare "try: ... except: ..." block somewhere? Don't do
that. Bare "except:" without specifying which exceptions you want to
catch is a surefire way of catching way too much, including things like
KeyboardInterrupt which you really want to allow to pass through
uncaught.

--
Robin Munn <rm...@pobox.com>
http://www.rmunn.com/
PGP key ID: 0x6AFB6838 50FF 2478 CFFB 081A 8338 54F7 845D ACFD 6AFB 6838

Carl Banks

unread,
Mar 31, 2003, 2:35:56 PM3/31/03
to
Robin Munn wrote:
> Richard <rich...@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?
>
> You could use a "try: ... except KeyboardInterrupt: ..." block to detect
> Ctrl-C. Although KeyboardInterrupt *should* by default not be caught, so
> a Ctrl-C *should* be able to shut down your program.

In this case, it shouldn't. He's running his program in a console
window created within Python. The idea of a console is
(theoretically) to give a program complete control of this input;
therefore, console windows often disable KeyboardInterrupt and treat
Control-C like any regular keystroke.


--
CARL BANKS

Carl Banks

unread,
Mar 31, 2003, 2:35:56 PM3/31/03
to

We would have to know what kind of console this is to answer this. Is
it curses? Some kind of GUI widget? Things that provide consoles
within Python often do not trap Control-C, so you'd have to do it
yourself. And that, of course, depends highly on what sort of console
you're talking about.


--
CARL BANKS

Bengt Richter

unread,
Apr 1, 2003, 12:00:01 AM4/1/03
to

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

Richard

unread,
Apr 1, 2003, 1:59:44 AM4/1/03
to
Hi guys,

Thank you all for your suggestions. I'm sorry my question was a bit
ambiguous with regard to the 'console window' reference. I was in fact
referring to a Win2K Command Prompt (cmd.exe).

I will try catching the KeyboardInterrupt exception and check that I have no
bare 'except's anywhere that could be catching this for me.

Thanks for your help...

Richard

"Richard" <rich...@hmgcc.gov.uk> wrote in message
news:3e88...@mail.hmgcc.gov.uk...

Rüdiger Mähl

unread,
Apr 1, 2003, 2:40:55 AM4/1/03
to
"Richard" <rich...@hmgcc.gov.uk> wrote:

Hi Richard,

perhaps, you are looking for a simple approach like
the following sample.

HTH, Ruediger

# ========================================
import sys
import time
import atexit
import signal

CONTROL_C = False

def program_exit():
# You may do some clean-up here, but you don't have to.
print "Exiting application"


def ctrlCHandler(*whatever):
# Just sets the value of CONTROL_C
global CONTROL_C
CONTROL_C = True
print
print "Interrupt caught! Please wait..."
print
# You must check CONTROL_C in your program

# call this procedure, if control-c is pressed.
signal.signal(signal.SIGINT, ctrlCHandler)

# program_exit is called, when sys.exit is executed.
atexit.register(program_exit)

while 1:
print "Waiting for Ctrl-C to be pressed..."
time.sleep(2)
if CONTROL_C: sys.exit(0)
# ========================================

Bengt Richter

unread,
Apr 1, 2003, 5:49:43 AM4/1/03
to
On Tue, 1 Apr 2003 07:59:44 +0100, "Richard" <rich...@hmgcc.gov.uk> wrote:

>Hi guys,
>
>Thank you all for your suggestions. I'm sorry my question was a bit
>ambiguous with regard to the 'console window' reference. I was in fact
>referring to a Win2K Command Prompt (cmd.exe).
>
>I will try catching the KeyboardInterrupt exception and check that I have no
>bare 'except's anywhere that could be catching this for me.
>

Note that catching the exception means allowing the exception to interrupt
wherever in your loop execution may be, so you can not expect such a loop exit
to give you a final complete loop execution. See suggestions (mine and
others) for using signal handlers to accomplish orderly loop termination
based on modifying a while loop condition from a signal handler (which
returns to continue execution of the loop where interrupted).

Regards,
Bengt Richter

Robin Munn

unread,
Apr 1, 2003, 10:06:38 AM4/1/03
to

Ah. I was assuming something like the Linux console, or the Windows
"console" that's almost-but-not-entirely like a Unix console in
behavior...

0 new messages