So I am writing a project where I run a program that constantly receives/sends messages to other computers running the same program.
The receiver/sender of data is running on a thread and prints to stdout. I get stuff like this:
[INFO] User 'blah' wants to send message to you.
[INFO] some other info
[MSG REC] Message 'hello' received from blah.
Now the issue is that sometimes I wish to input commands into the terminal, the problem is when I try to enter a command and a new info message or MSG REC
is printed to stdout. I have commands such as quit
and status
etc.
>> indicates the input line.
Something like this may happen:
[INFO] User 'blah' wants to send message to you.
[INFO] some other info
[MSG REC] Message 'hello' received from blah.
>> stat[MSG REC] Message 'sup' received from Bob.
us
Then I would press enter and the command status
gets executed but looks so poor in the terminal. A message appears every 2-4 seconds so this is an issue. Is there a good way to solve this? I tried using ANSI cursor commands to try and insert a new line before the last line so the last line would always remain as the input line and I could type in "stat", wait for a while and finish it with "us" without any issues.
I also saw people recommend curses
but attempting to integrate that with my program completely messed up the formatting of my output among other things (and I think its overkill perhaps).
So is there an easy way to make the thread insert new MSG REC
lines 1 line above the last line so the last line would always remain as the input line with >> and whatever else I have typed in.
Using Python2.7 on Linux.
EDIT: Change that made James Mills answer work: I had to use this whenever my thread was printing a new line.
myY, myX = stdscr.getyx();
str = "blah blah"; #my message I want to print
stdscr.addstr(len(lines), 0, str)
lines.append(str)
stdscr.move(myY, myX) #move cursor back to proper position
Ctrl-D is the canonical way to terminate keyboard stdin in any shell command.
And to scroll down in the terminal, use Shift + PageDown. To go up or down in the terminal by line, use Ctrl + Shift + Up or Ctrl + Shift + Down respectively.
The most used newline character If you don't want to use echo repeatedly to create new lines in your shell script, then you can use the \n character. The \n is a newline character for Unix-based systems; it helps to push the commands that come after it onto a new line.
There are many other ways to capture the first line too, including sed 1q (quit after first line), sed -n 1p (only print first line, but read everything), awk 'FNR == 1' (only print first line, but again, read everything) etc. Save this answer.
Here is a basic example:
Code:
#!/usr/bin/env python
from string import printable
from curses import erasechar, wrapper
PRINTABLE = map(ord, printable)
def input(stdscr):
ERASE = input.ERASE = getattr(input, "ERASE", ord(erasechar()))
Y, X = stdscr.getyx()
s = []
while True:
c = stdscr.getch()
if c in (13, 10):
break
elif c == ERASE:
y, x = stdscr.getyx()
if x > X:
del s[-1]
stdscr.move(y, (x - 1))
stdscr.clrtoeol()
stdscr.refresh()
elif c in PRINTABLE:
s.append(chr(c))
stdscr.addch(c)
return "".join(s)
def prompt(stdscr, y, x, prompt=">>> "):
stdscr.move(y, x)
stdscr.clrtoeol()
stdscr.addstr(y, x, prompt)
return input(stdscr)
def main(stdscr):
Y, X = stdscr.getmaxyx()
lines = []
max_lines = (Y - 3)
stdscr.clear()
while True:
s = prompt(stdscr, (Y - 1), 0) # noqa
if s == ":q":
break
# scroll
if len(lines) > max_lines:
lines = lines[1:]
stdscr.clear()
for i, line in enumerate(lines):
stdscr.addstr(i, 0, line)
stdscr.addstr(len(lines), 0, s)
lines.append(s)
stdscr.refresh()
wrapper(main)
This basically sets up a demo curses app which prompts the user for input and displays the prompt at (24, 0)
. The demo terminates on the user entering :q
. For any other input it appends the input to the top of the screen. Enjoy! (<BACKSAPCE>
also works!) :)
See: curses; all of the API I used in this example is straight from this standard library. Whilst using curses may or may not be "overkill" IHMO I would recommend the use of urwid especially if the complexity of your application starts to outgrow plain 'ol curses.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With