Reputation: 1023
I'm working on some basic animations for python cli interfaces that appear when the script is running. This is a problem I have with pretty much every script I've written. If I'm executing the following animation;
def animatedSpinner(*arg):
animation = ["|","/","-","\\"]
a = 0
while True:
print(animation[a % len(animation)], end="\r")
a += 1
time.sleep(0.1)
It runs fine, but any key presses a user makes while it's running shows up on the screen. How can I prevent key presses from appearing on screen during an animation or any time a functioning is running?
Upvotes: 5
Views: 504
Reputation: 365707
The ways to do this on POSIX and Windows are so different that, unless you want to search for a very high-level wrapper library (which do exist—some of them are lowest-common-denominator but probably have enough functionality for this, while others are very complicated), you might as well consider them separate problems. Since you said POSIX is what you really care about, I'll explain that.
The right way to do this is with termios
. But this can be a bit hairy for beginners, so I'll come back to that at the end.
If you want a quick&dirty solution, you can just call out to the stty
tool:
import subprocess
def echooff():
subprocess.run(['stty', '-echo'], check=True)
def echoon():
subprocess.run(['stty', 'echo'], check=True)
Whichever way you do it, make sure that you always call echoon
before exit, no matter what. Otherwise, you'll leave the console in non-echoing mode, and your user (or you) will have to blindly do a reset
or stty echo
.
For example, in your main code:
try:
echooff()
# do stuff
finally:
echoon()
Or, better, using contextlib
:
@contextlib.contextmanager
def echo_disabled():
try:
echooff()
yield
finally:
echoon()
Then:
with echo_disabled():
# do stuff
The tty
module's source code is good sample code for getting started—but in this case, what you're doing is pretty close to the example right there in the termios
docs:
@contextlib.contextmanager
def echo_disabled():
fd = sys.stdin.fileno()
old = termios.tcgetattr(fd)
new = termios.tcgetattr(fd)
new[3] = new[3] & ~termios.ECHO # lflags
try:
yield
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old)
Upvotes: 4