Как я могу получить (и установить) текущую позицию курсора bash при использовании python readline? - PullRequest
8 голосов
/ 26 октября 2011

У меня есть скрипт на python, который управляет stdin, stdout и stderr любого приложения и позволяет корректно вставлять readline. Подумайте о любом приложении, которое имеет много консольного вывода, но также принимает команды от стандартного ввода.

В любом случае мой скрипт использует эти две функции:

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 print_line(line):
    global cmd_state
    blank_current_readline()
    print line,
    sys.stdout.write(cmd_state["prompt"] + readline.get_line_buffer())
    sys.stdout.flush()

При обработке stdout я вызываю print_line (). Это исключает все, что может набирать пользователь, печатает строку, а затем восстанавливает введенный пользователем текст. Все это происходит, когда пользователь ничего не замечает.

Проблема возникает, когда курсор находится не в конце любого ввода, который вводит пользователь. Когда курсор находится в середине теста и выводится строка, курсор автоматически помещается в конец ввода. Чтобы решить эту проблему, я хочу сделать что-то вроде этого в print_line:

def print_line(line):
    global cmd_state
    cursorPos = getCurrentCursorPos() #Doesn't exist
    blank_current_readline()
    print line,
    sys.stdout.write(cmd_state["prompt"] + readline.get_line_buffer())
    sys.stdout.setCurrentCursorPos(cursorPos) #Doesn't exist
    sys.stdout.flush()

Редактировать: чтобы попытаться визуализировать то, что я написал:

Терминал выглядит так:

----------------------------------------------
|                                            |
|                                            |
|   <scolling command output here>           |
|                                            |
|   <scolling command output here>           |
|                                            |
|: <user inputted text here>                 |
----------------------------------------------

Таким образом, выводимый текст постоянно прокручивается по мере поступления новых журналов. В то же время, пользователь в настоящее время редактирует и пишет новую команду, которая будет вставлена ​​после нажатия клавиши ввода. Так что это выглядит как консоль python, но с выводом, всегда добавляемым.

1 Ответ

4 голосов
/ 26 октября 2011

Могу ли я предложить Python curses ?

Вот Базовые инструкции

Модуль curses предоставляет интерфейс для библиотеки curses, фактического стандарта для портативной расширенной обработки терминала.

Хотя curses наиболее широко используется в среде Unix, доступны версии для DOS, OS / 2 и, возможно, других систем. Этот модуль расширения разработан с учетом API ncurses, библиотеки проклятий с открытым исходным кодом, размещенной в Linux, и вариантов Unix для BSD.


С другой стороны

Я нашел Terminal Controller здесь: Использование terminfo для переносного вывода цвета и управления курсором . Он выглядит более переносимым, чем можно предположить в sitename (MacOS упоминается в комментариях - хотя и с изменениями).

Вот пример использования, отображающий индикатор выполнения:

class ProgressBar:
    """
    A 3-line progress bar, which looks like::

                                Header
        20% [===========----------------------------------]
                           progress message

    The progress bar is colored, if the terminal supports color
    output; and adjusts to the width of the terminal.
    """
    BAR = '%3d%% ${GREEN}[${BOLD}%s%s${NORMAL}${GREEN}]${NORMAL}\n'
    HEADER = '${BOLD}${CYAN}%s${NORMAL}\n\n'

    def __init__(self, term, header):
        self.term = term
        if not (self.term.CLEAR_EOL and self.term.UP and self.term.BOL):
            raise ValueError("Terminal isn't capable enough -- you "
                             "should use a simpler progress dispaly.")
        self.width = self.term.COLS or 75
        self.bar = term.render(self.BAR)
        self.header = self.term.render(self.HEADER % header.center(self.width))
        self.cleared = 1 #: true if we haven't drawn the bar yet.
        self.update(0, '')

    def update(self, percent, message):
        if self.cleared:
            sys.stdout.write(self.header)
            self.cleared = 0
        n = int((self.width-10)*percent)
        sys.stdout.write(
            self.term.BOL + self.term.UP + self.term.CLEAR_EOL +
            (self.bar % (100*percent, '='*n, '-'*(self.width-10-n))) +
            self.term.CLEAR_EOL + message.center(self.width))

    def clear(self):
        if not self.cleared:
            sys.stdout.write(self.term.BOL + self.term.CLEAR_EOL +
                             self.term.UP + self.term.CLEAR_EOL +
                             self.term.UP + self.term.CLEAR_EOL)
            self.cleared = 1
...