I have an application that runs in the background in an infinite loop. How do I stop the loop using hotkeys ( Ctrl + F1 ~ F12 )?
I have an application that runs in the background in an infinite loop. How do I stop the loop using hotkeys ( Ctrl + F1 ~ F12 )?
First, it takes a means of detecting that the user is trying to press a key without actually reading that key (otherwise the loop would stop and wait for user input). According to this question in SOen , this is done through the select
" or - as the relevant function is not supported in Windows - msvcrt.kbhit
.
An example in Windows would be:
>>> indice = 0
>>> while not msvcrt.kbhit() and indice < 1000000:
... indice = indice + 1
...
_
(cursor blinks, but loop continues to execute, until user enters any key)
>>> indice
401247
As for detecting hotkeys , what I noticed in my tests (I do not know if this is the best way to do it) is that if you try to read the user input using msvcrt.getch
" (still in Windows) after such a sequence, it will give \x00
(F1 to F10) or \xe0
(F11 and F12):
>>> msvcrt.getch()
'\x00'
And if you try to read again , then it will generate a second character, depending on the sequence you typed:
Ctrl + F1
>>> msvcrt.getch()
'\x00'
>>> msvcrt.getch()
'^'
Ctrl + F10
>>> msvcrt.getch()
'\x00'
>>> msvcrt.getch()
'g'
Ctrl + F12
>>> msvcrt.getch()
'\xe0'
>>> msvcrt.getch()
'\x8a'
Etc
F1 F2 F3 F4 F5 F6
'\x00^' '\x00_' '\x00'' '\x00a' '\x00b' '\x00c'
F7 F8 F9 F10 F11 F12
'\x00d' '\x00e' '\x00f' '\x00g' '\xe0\x89' '\xe0\x8a'
The complete code would then be:
while True:
# Faz alguma coisa
if msvcrt.kbhit():
a = msvcrt.getch()
b = msvcrt.getch() if msvcrt.kbhit() else None
if a == '\x00' and b == '^': # Ctrl + F1
break
On Linux (and presumably also on Mac) the solution involves select
to do polling on the keyboard, and termios
to get the keys pressed (such as detailed my answer to a related question):
>>> import sys, tty, termios, select
>>> def getch():
... fd = sys.stdin.fileno()
... old_settings = termios.tcgetattr(fd)
... try:
... tty.setraw(fd)
... ch = sys.stdin.read(1)
... finally:
... termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
... return ch
...
>>> def kbhit():
... fd = sys.stdin.fileno()
... old_settings = termios.tcgetattr(fd)
... try:
... tty.setraw(fd)
... i,o,e = select.select([sys.stdin],[],[],0.0001)
... for s in i:
... if s == sys.stdin:
... return True
... return False
... finally:
... termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
...
>>> while True:
... # Faz alguma coisa
... if kbhit():
... if getch() == '\x1b':
... a, b, c, d = getch(), getch(), getch(), getch()
... if a == '[' and b == '1' and c == '1' and d == '~': # Ctrl + F1
... break
...
>>>
In my tests it generated 5 characters, the first one always \x1b
and the following 4 in this way:
F1: [ 1 1 ~
F2: [ 1 2 ~
F3: [ 1 3 ~
F4: [ 1 4 ~
F5: [ 1 5 ~
F6: [ 1 7 ~
F7: [ 1 8 ~
F8: [ 1 9 ~
F9: [ 2 0 ~
F10: [ 2 1 ~
F11: [ 2 3 ~
F12: [ 2 4 ~
Reiterating, I do not know if this is the best way, or if it is portable. But it worked successfully in my tests on Windows 7 and Ubuntu.