Держите линию stdin в верхней или нижней части экрана терминала

Итак, я пишу проект, в котором запускаю программу, которая постоянно принимает/отправляет сообщения другим компьютерам, использующим ту же программу.

Получатель/отправитель данных работает в потоке и печатает на stdout. Я получаю такие вещи:

[INFO] User 'blah' wants to send message to you.
[INFO] some other info
[MSG REC] Message 'hello' received from blah.

Теперь проблема в том, что иногда я хочу вводить команды в терминал, проблема в том, что я пытаюсь ввести команду, а новое информационное сообщение или MSG REC будет напечатано на stdout. У меня есть команды, такие как quit и status и т.д.

→ указывает строку ввода.

Что-то вроде этого может случиться:

[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

Затем я нажимаю enter и команда status выполняется, но выглядит так плохо в терминале. Сообщение появляется каждые 2-4 секунды, так что это проблема. Есть ли хороший способ решить эту проблему? Я попытался использовать команды курсора ANSI, чтобы попытаться вставить новую строку до последней строки, чтобы последняя строка всегда оставалась в качестве строки ввода, и я мог бы ввести "stat", подождать некоторое время и закончить ее с помощью "us" без каких-либо вопросы.

Я также видел, что люди рекомендуют curses, но попытка интегрировать это с моей программой полностью испортила форматирование моего вывода среди других вещей (и я думаю, что это может быть избыточно).

Итак, есть простой способ сделать поток вставить новую строку MSG REC линии 1 над предыдущей строкой, чтобы последняя строка всегда оставалась в качестве строки ввода с → и любым другим, что я набрал.

Использование Python2.7 в Linux.

РЕДАКТИРОВАТЬ: Изменение, которое заставило Джеймса Миллса ответить: Я должен был использовать это, когда мой поток печатал новую строку.

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

Ответ 1

Вот пример:

Код:

#!/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)

В основном это создает приложение demo curses, которое запрашивает пользователя для ввода и отображает приглашение в (24, 0). Демонстрация завершается при входе пользователя :q. Для любого другого входа он добавляет вход в верхнюю часть экрана. Наслаждайтесь! (<BACKSAPCE> также работает!):)

Смотрите: curses; весь API, который я использовал в этом примере, прямо из этой стандартной библиотеки. Хотя использование проклятий может или не может быть "чрезмерным" IHMO, я бы рекомендовал использовать urwid, особенно если сложность вашего приложения начнет перерастать простые проклятия.