How to scroll text in Python/Curses subwindow?

后端 未结 5 2244
陌清茗
陌清茗 2021-01-30 23:01

In my Python script which uses Curses, I have a subwin to which some text is assigned. Because the text length may be longer than the window size, the text should be scrollable.

5条回答
  •  天命终不由人
    2021-01-30 23:46

    I wanted to use a scrolling pad to display content of some large text files but this didn't work well because texts can have line breaks and it was pretty hard to figure out how many characters to display at a time to fit the good number of columns and rows.

    So I decided to first split my text files in lines of exactly COLUMNS characters, padding with spaces when lines were too short. Then scrolling the text become more easy.

    Here is a sample code to display any text file:

    #!/usr/bin/python
    # -*- coding: utf-8 -*-
    
    import curses
    import locale
    import sys
    
    def main(filename, filecontent, encoding="utf-8"):
        try:
            stdscr = curses.initscr()
            curses.noecho()
            curses.cbreak()
            curses.curs_set(0)
            stdscr.keypad(1)
            rows, columns = stdscr.getmaxyx()
            stdscr.border()
            bottom_menu = u"(↓) Next line | (↑) Previous line | (→) Next page | (←) Previous page | (q) Quit".encode(encoding).center(columns - 4)
            stdscr.addstr(rows - 1, 2, bottom_menu, curses.A_REVERSE)
            out = stdscr.subwin(rows - 2, columns - 2, 1, 1)
            out_rows, out_columns = out.getmaxyx()
            out_rows -= 1
            lines = map(lambda x: x + " " * (out_columns - len(x)), reduce(lambda x, y: x + y, [[x[i:i+out_columns] for i in xrange(0, len(x), out_columns)] for x in filecontent.expandtabs(4).splitlines()]))
            stdscr.refresh()
            line = 0
            while 1:
                top_menu = (u"Lines %d to %d of %d of %s" % (line + 1, min(len(lines), line + out_rows), len(lines), filename)).encode(encoding).center(columns - 4)
                stdscr.addstr(0, 2, top_menu, curses.A_REVERSE)
                out.addstr(0, 0, "".join(lines[line:line+out_rows]))
                stdscr.refresh()
                out.refresh()
                c = stdscr.getch()
                if c == ord("q"):
                    break
                elif c == curses.KEY_DOWN:
                    if len(lines) - line > out_rows:
                        line += 1
                elif c == curses.KEY_UP:
                    if line > 0:
                        line -= 1
                elif c == curses.KEY_RIGHT:
                    if len(lines) - line >= 2 * out_rows:
                        line += out_rows
                elif c == curses.KEY_LEFT:
                    if line >= out_rows:
                        line -= out_rows
        finally:
            curses.nocbreak(); stdscr.keypad(0); curses.echo(); curses.curs_set(1)
            curses.endwin()
    
    if __name__ == '__main__':
        locale.setlocale(locale.LC_ALL, '')
        encoding = locale.getpreferredencoding()
        try:
            filename = sys.argv[1]
        except:
            print "Usage: python %s FILENAME" % __file__
        else:
            try:
                with open(filename) as f:
                    filecontent = f.read()
            except:
                print "Unable to open file %s" % filename
            else:
                main(filename, filecontent, encoding)
    

    The main trick is the line:

    lines = map(lambda x: x + " " * (out_columns - len(x)), reduce(lambda x, y: x + y, [[x[i:i+out_columns] for i in xrange(0, len(x), out_columns)] for x in filecontent.expandtabs(4).splitlines()]))
    

    First, tabulations in the text are converted to spaces, then I used splitlines() method to convert my text in array of lines. But some lines may be longer than our COLUMNS number, so I splitted each line in chunk of COLUMNS characters and then used reduce to transform the resulting list in a list of lines. Finally, I used map to pad each line with trailing spaces so that its length is exactly COLUMNS characters.

    Hope this helps.

提交回复
热议问题