Welcome to Software Development on Codidact!
Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.
Listen for key events in a CLI app
I have a Python program like this:
done = False
while u and not done:
i = u.pop()
print(f"Processing {i}")
do_big_task(i)
finish_up()
Since this takes a long time, the user might get tired of waiting. I want the program to also continually listen for a keystroke, such as space, and if the user presses this done
will be set to True
so that the loop will automatically stop at the next iteration.
Note that I still want finish_up()
to run even if the loop is terminated early.
How can I do this?
1 answer
First of all, the standard keystroke for interrupting a CLI program would be ctrl + C
or whatever the windows equivalent is.
Therefore, you will get the most consistent user experience if you use this standard keystroke for interrupting the program (rather than something custom like space).
One big advantage of using standard keystrokes, is that Python will raise a KeyboardInterrupt
exception upon detection.
As a result, we just have to write error handling code to stop the program properly.
A Minimal Working Example (MWE) of this code could look as follows:
import time
done = False
while not done:
try:
print("looping")
time.sleep(1)
except KeyboardInterrupt:
done = True
print("done")
It might be even (slightly) better to wrap the entire code as follows:
import time
try:
while True:
print("looping")
time.sleep(1)
except KeyboardInterrupt: # not strictly necessary
pass
finally:
print("done")
Addendum: as indicated in the linked documentation (and mentioned in the comments), it would be important that the finish_up
function has minimal runtime and allows to exit the program as quickly as possible.
If the code in finish_up
is lengthy, it might be useful/necessary to split up the code in a part that makes sure the program leaves a consistent state (if possible without too much overhead) and a (possibly slow) post-processing part that would appear inside of the try
block. E.g.
import time
try:
while True:
print("looping")
time.sleep(1)
post_processing()
except KeyboardInterrupt: # not strictly necessary
pass
finally:
print("finishing")
clean_up()
3 comment threads