I'm trying to let the user input commands at a console using raw_input(), this works fine. The problem is I have background threads that occasionally output log-information to the screen and when they do they mess up the input prompt (since the output go wherever the cursor happens to be at the moment).
This is a small Python program that illustrate what i mean.
#!/usr/bin/env python
import threading
import time
def message_loop():
while True:
time.sleep(1)
print "Hello World"
thread = threading.Thread(target = message_loop)
thread.start()
while True:
input = raw_input("Prompt> ")
print "You typed", input
This is an example of what it could look like when I run it:
Prompt> Hello World
Hello World
Hello World
Hello World
test
You typed test
Prompt> Hello World
Hello World
Hello World
hellHello World
o
You typed hello
Prompt> Hello World
Hello World
Hello World
Hello World
What I want is for the prompt to move along with the output from the thread. Like so:
Hello World
Hello World
Prompt> test
You typed test
Hello World
Hello World
Hello World
Hello World
Hello World
Prompt> hello
You typed hello
Hello World
Hello World
Hello World
Hello World
Prompt>
Any ideas on how to achieve this without resorting to ugly hacks? :)
I recently encountered this problem, and would like to leave this solution here for future reference.
These solutions clear the pending raw_input (readline) text from the terminal, print the new text, then reprint to the terminal what was in the raw_input buffer.
This first program is pretty simple, but only works correctly when there is only 1 line of text waiting for raw_input:
#!/usr/bin/python
import time,readline,thread,sys
def noisy_thread():
while True:
time.sleep(3)
sys.stdout.write('\r'+' '*(len(readline.get_line_buffer())+2)+'\r')
print 'Interrupting text!'
sys.stdout.write('> ' + readline.get_line_buffer())
sys.stdout.flush()
thread.start_new_thread(noisy_thread, ())
while True:
s = raw_input('> ')
Output:
$ ./threads_input.py
Interrupting text!
Interrupting text!
Interrupting text!
> WELL, PRINCE, Genoa and Lucca are now no more than private estates of the Bo
Interrupting text!
> WELL, PRINCE, Genoa and Lucca are now no more than private estates of the Bo
naparte family. No, I warn you, that if you do not tell me we are at war,
The second correctly handles 2 or more buffered lines, but has more (standard) module dependencies and requires a wee bit of terminal hackery:
#!/usr/bin/python
import time,readline,thread
import sys,struct,fcntl,termios
def blank_current_readline():
# Next line said to be reasonably portable for various Unixes
(rows,cols) = struct.unpack('hh', fcntl.ioctl(sys.stdout, termios.TIOCGWINSZ,'1234'))
text_len = len(readline.get_line_buffer())+2
# ANSI escape sequences (All VT100 except ESC[0G)
sys.stdout.write('\x1b[2K') # Clear current line
sys.stdout.write('\x1b[1A\x1b[2K'*(text_len/cols)) # Move cursor up and clear line
sys.stdout.write('\x1b[0G') # Move to start of line
def noisy_thread():
while True:
time.sleep(3)
blank_current_readline()
print 'Interrupting text!'
sys.stdout.write('> ' + readline.get_line_buffer())
sys.stdout.flush() # Needed or text doesn't show until a key is pressed
if __name__ == '__main__':
thread.start_new_thread(noisy_thread, ())
while True:
s = raw_input('> ')
Output. Previous readline lines cleared properly:
$ ./threads_input2.py
Interrupting text!
Interrupting text!
Interrupting text!
Interrupting text!
> WELL, PRINCE, Genoa and Lucca are now no more than private estates of the Bo
naparte family. No, I warn you, that if you do not tell me we are at war,
Useful sources:
How to get Linux console window width in Python
apt like column output - python library
(This code sample shows how to get terminal width for either Unix or Windows)
http://en.wikipedia.org/wiki/ANSI_escape_code
I think you need something that lets you dynamically print/delete/overwrite text from the terminal window e.g. how the UNIX watch or top commands work.
I think in your case you would print "Prompt>" but then when you get a "Hello World" you overwrite "Prompt>" with "Hello World", and then print "Prompt>" on the line below. I don't think you can do that with regular output printing to the terminal.
You might be able to do what you want using Python's curses library. I have never used it so I can't tell you how to solve your problem (or if the module will even be able to solve your problem), but I think it is worth taking a look into. A search for "python curses tutorial" provided a PDF tutorial document which seems helpful.
you need to update stdout from a single thread, not from multiple threads... or else you have no control over interleaved i/o.
you will want to create a single thread for output writing.
you can use a Queue in the thread and have all other threads write their output logging info to it.. then read from this Queue and write to stdout at appropriate times along with your prompt message.
I don't think it's possible. How should that behave anyways? Nothing shows up until the user presses Enter? If that's so, output would only come when the user issues a command (or whatever your system expects), and that doesn't sound desirable.
Methinks your threads should output to another file.
Related
Python print flush:
from time import sleep
print("Hello, world!", end='')
sleep(5)
print("Bye!!!")
Ideally, in this scenario, Hello world! and Bye!! should be printed together as flush is defaulted to False. But when I am running this, it is print Hello world! and then after 5 seconds, printing Bye!!.
and I have tried the flush=False and flush=True one by one. But these also behaving exactly same
can any one please help me in finding a way to observe the behavior of flush, practically?
I want to automate some clicks on Windows. I use pyautogui for that.
But when my code meets pyautogui.click(...), it execute it, and after that doesn't execute any other code except for pyautogui.
When I launch this code (I launch it through cmd with python myFile.py):
import pyautogui
print("Hello, World!")
pyautogui.click(500, 500)
print("Hello, World! x2")
It outputs:
Hello, World!
And then there is only blinking _
If I press any button (Enter for example) it outputs "Hello, World! x2" and returns control to me (so I can type any other command like cls, dir etc).
So instead of (Way #1):
Output "Hello, World!"
Click with coordinates
Output "Hello, World! x2"
Finish executing
It does (Way #2):
Output "Hello, World!"
Click with coordinates
Pauses until I press a button.
Why is that?
What should I do to run code Way #1?
By the way, if I write:
import pyautogui
print("Hello, World!")
pyautogui.click(500, 500)
pyautogui.click(600, 600)
print("Hello, World! x2")
It does click two times and then "freezes" the same way.
Just ensure that the click is outside the command prompt window. You may use pyautogui.mouseDown paired with pyautogui.dragRel for this.
So, what is really going on there? If you click in the command prompt window, it starts "selection mode", and the code runs in the background until a key is pressed. More on what really goes on.
I assume you wish to automate something. I suggest: make a .bat to go with your .py file; then, use pyinstaller from the command prompt to make a .exe file, and schedule it to start as often as you need. You could follow this tutorial.
You probably already done this, but I would check everything for updates (pyautogui, python, maybe your devise), because it works for fine me.
python terminal
Well, I have read that stdout is line-buffered. But the code works differently in Pydroid 3(unaware of the exact version) and Python 3.8.3.
import time
print('Hello', end = '')
time.sleep(5)
print('World')
In Pydroid 3, both Hello and World are printed after (at least after) 5 seconds while in Python 3.8.3, Hello is printed first, and World is printed after 5 seconds.
Why is the code working differently?
It is probably not a Python version issue, but a different terminal issue.
Some terminals (or more accurately files/streams, stdout included) only flush after a newline (which the first print doesn't have), while others can flush after every write.
to force a flush you can use flush=True as a param to print, try this:
import time
print('Hello', end='', flush=True)
time.sleep(5)
print('World')
I have one python script that prints a mixture of stocks, headlines, my google calendar agenda for today, and the weather outside. I'm trying to figure out how to use this text output as the in-line argument for Matt Dyson's amazing Google translate, text to speech script: http://mattdyson.org/blog/2014/07/text-to-speech-on-a-raspberry-pi-using-google-translate/
Basically, I want my first script to be spoken every time the script is run.
I'm shooting for a single script that leaves the first "printing" script intact, as it's modular and just runs a bunch of execfile commands for other scripts.
I hope this makes sense!?
Cheers!
Have you thought about passing the argument to a subprocess that calls Matt's script?
Like this;
import subprocess
...
textcommand = #command to call your script
myProcess = subprocess.Popen(textCommand.split(), stdout=subprocess.PIPE)
textOutput = myProcess.communicate()[0]
....
mattCommand = 'python matts_script.py ' + outputText
mattProcess = subprocess.Popen(mattCommand.split())
Edited based on OP input.
I have a simple command-line binary program hello which outputs to STDOUT:
What is your name?
and waits for the user to input it. After receiving their input it outputs:
Hello, [name]!
and terminates.
I want to use Python to run computations on the final output of this program ("Hello, [name]!"), however before the final output I want the Python script to essentially "be" the binary program. In other words I'd like Python to forward all of the prompts to STDOUT and then accept the user's input and give it to the program. However I want to hide the final output so I can process it and show my own results to the user. I do not want to replicate the hello's behavior in the script, as this simple program is a stand-in for a more complex program that I am actually working with.
I was hoping there would be some sort of mechanic in subprocess where I would be able to do something akin to:
while process.is_running():
next_char = process.stdout.read(1)
if next_char == input_prompt_thing: # somehow check if the program is waiting for input
user_input = raw_input(buffer)
process.stdin.write(user_input)
else:
buffer += next_char
I have been playing with subprocess and essentially got as far as realizing I could use process.stdout.read(1) to read from the program before it began blocking, but I can't figure out how to break this loop before the process blocks my Python script. I am not too familiar with console I/O and it is not an area of much expertise for me, so I am starting to feel pretty lost. I appreciate any help!
You could try winpexpect (not tested):
import re
from winpexpect import winspawn
p = winspawn('hello')
prompt = "What is your name?"
p.expect(re.escape(prompt))
name = raw_input(prompt)
p.sendline(name)
p.expect("Hello, .*!")
output = p.after
# ...