I have a very weird print() bug while using Threads and Termios. I have a repeating Thread catching a key via Termios while printing some stuff. But always it prints a new line it doesn't start at the beginning of the line but where the last line ended.
This is my code:
def func1():
while True:
try:
var = int(inputChar())
except ValueError:
var = 0
Thread(target=func1).start()
while True:
print("stuff")
time.sleep(2)
This is my inputChar() function:
import sys, tty, termios
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
try:
tty.setraw(sys.stdin.fileno())
ch = sys.stdin.read(1)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
return ch
This ist the expected output:
stuff
stuff
stuff
stuff
stuff
This is the output:
stuff
stuff
stuff
stuff
stuff
I have no idea why this is happening but you can fix it by replacing the print command with
print("stuff\r")
or
sys.stdout.write("stuff\n\r")
the \r at the end is known as a cartridge return. It's useful when sys.stdout.write() is not a viable option
Related
import sys
import threading
import tty
import termios
def loop():
fd = sys.stdin.fileno()
mode = termios.tcgetattr(fd)
tty.setraw(fd)
try:
while True:
sys.stdin.read(1)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, mode)
threading.Thread(target=loop).start()
I tried to manually send End-Of-Transmission character using sys.stdin.write(chr(4)) but it results in io.UnsupportedOperation: not writable.
I tried to close stdin but it will not terminate blocking read(1) method immediately. It triggers an exception after I typed another key.
I tried seletors but selector.select() need extra keys after I invoked selector.close(). And selector it self needs an event loop and this loop becomes uninterruptable.
If you run the loop in a thread then it will be hard to interrupt and also keep original process running. But it's easy to run this loop in a subprocess. So you can interrupt the subprocess to interrupt the blocking stdin.read(1)
import os
import sys
import tty
import termios
import time
import signal
from multiprocessing import Process
def loop():
# https://stackoverflow.com/questions/30134297/python-multiprocessing-stdin-input
# Subprocess stdin will be set to os.devnull. So we need to reopen it here.
# Use parent process stdin(fd=0) instead of subprocess stdin(fd=7)
sys.stdin = open(0) #
fd = sys.stdin.fileno()
mode = termios.tcgetattr(fd)
tty.setraw(fd)
try:
while True:
ch = sys.stdin.read(1)
if ord(ch) == 3:
break
except KeyboardInterrupt:
print("interrupted!\r\n", end="")
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, mode)
if __name__ == '__main__':
p = Process(target=loop)
p.start()
time.sleep(1)
os.kill(p.pid, signal.SIGINT)
p.join()
print("done")
I made a console game in python, and I would like to disable the console when it prints the story. It looks like this:
print("First line of story")
time.sleep(2)
print("Second line of story")
time.sleep(2)
And so on...
So my problem is that the player can type and mess up with the console while it's writing the story. Can I disable the typing somehow?
If you are on Unix, you can disable echoing like this:
import sys
import termios
import time
fd = sys.stdin.fileno()
old = termios.tcgetattr(fd)
new = termios.tcgetattr(fd)
new[3] &= ~termios.ECHO
termios.tcsetattr(fd, termios.TCSADRAIN, new)
print("First line of story")
time.sleep(2)
print("Second line of story")
time.sleep(2)
termios.tcsetattr(fd, termios.TCSADRAIN, old)
If you don't want the suppressed input to be echoed after the last tcsetattr call, you can substitute the last TCSADRAIN with TCSAFLUSH.
Documentation for the termios module can be found here, which is also where the example is taken from.
I have been trying to write a keyboard listener without installing any packages. What I wanted was to create a non-blocking way of reading only one character of user input. So I created another thread besides the main one. Here is my code:
import sys, os
import thread
import time
try:
from msvcrt import getch
except ImportError:
def getch():
import sys, tty, termios
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
try:
tty.setraw(sys.stdin.fileno())
ch = sys.stdin.read(1)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
return ch
char = None;
def key_listener():
global char;
while True:
char = getch()
# escape key to exit
if ord(char) == 27:
break
#print char #comment this line for test
thread.start_new_thread(key_listener, ())
while True:
print("Whatever")
time.sleep(1);
And the printed strings are a bit weird:
Yinans-MacBook-Pro:anaconda game Yinan$ python inputtest.py
Whatever
Whatever
Whatever
Whatever
Whatever
Whatever
See those indents? I never expected to have that. And I have been trying a whole afternoon to solve it but failed. Does anybody know how to solve this? I will be so grateful. (btw I'm using a macbook pro.)
Putting STDIN in raw mode put STDOUT in raw mode as well, so the normal \n is not expanded to a CRLF. You will need to print a \r at the end of your string in order to return the cursor to the first column.
I'm using Pycharm on unix. I'm trying to read a single character (like 'y' or 'n') from the console. It runs fine, when I execute it on the command line, but when I'm running the program inside Pycharm I get the following error:
termios.error: (25, 'Inappropriate ioctl for device')
I know, that the ide is not a tty, but I haven't found a workaround.
This is my function (seems to be pretty standard) to read the character.
def getch():
import sys, tty, termios
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
while True:
char = getch()
if char == 'q':
exit()
else:
print char
Any time I use the recipe at http://code.activestate.com/recipes/134892/ I can't seem to get it working. It always throws the following error:
Traceback (most recent call last):
...
old_settings = termios.tcgetattr(fd)
termios.error: (22, 'Invalid argument)
My best thought is that it is because I'm running it in Eclipse so termios is throwing a fit about the file descriptor.
This is working on Ubuntu 8.04.1 , Python 2.5.2, i get no such error. May be you should try it from command line, eclipse may be using its own stdin, i get exact same error if I run it from Wing IDE, but from command line it works great.
Reason is that IDE e.g Wing is using there own class netserver.CDbgInputStream as sys.stdin
so sys.stdin.fileno is zero, thats why the error.
Basically IDE stdin is not a tty (print sys.stdin.isatty() is False)
class _GetchUnix:
def __init__(self):
import tty, sys
def __call__(self):
import sys, tty, termios
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
try:
tty.setraw(sys.stdin.fileno())
ch = sys.stdin.read(1)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
return ch
getch = _GetchUnix()
print getch()
Putting terminal into raw mode isn't always a good idea. Actually it's enough to clear ICANON bit. Here is another version of getch() with timeout support:
import tty, sys, termios
import select
def setup_term(fd, when=termios.TCSAFLUSH):
mode = termios.tcgetattr(fd)
mode[tty.LFLAG] = mode[tty.LFLAG] & ~(termios.ECHO | termios.ICANON)
termios.tcsetattr(fd, when, mode)
def getch(timeout=None):
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
try:
setup_term(fd)
try:
rw, wl, xl = select.select([fd], [], [], timeout)
except select.error:
return
if rw:
return sys.stdin.read(1)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
if __name__ == "__main__":
print getch()