26

在我使用 Curses 的 Python 脚本中,我有一个 subwin 分配了一些文本。因为文本长度可能比窗口大小长,所以文本应该是可滚动的。

似乎 Curses 窗口没有任何 CSS-“溢出”之类的属性。Python/Curses 文档在这方面也相当神秘。

这里有人知道如何使用 Python 编写可滚动的 Curses 子窗口并实际滚动浏览它吗?

\编辑:更精确的问题

4

6 回答 6

32

好的,window.scroll移动窗口的内容太复杂了。相反,curses.newpad为我做了。

创建一个垫:

mypad = curses.newpad(40,60)
mypad_pos = 0
mypad.refresh(mypad_pos, 0, 5, 5, 10, 60)

mypad_pos然后,您可以根据window.getch()in的输入增加/减少滚动cmd

if  cmd == curses.KEY_DOWN:
    mypad_pos += 1
    mypad.refresh(mypad_pos, 0, 5, 5, 10, 60)
elif cmd == curses.KEY_UP:
    mypad_pos -= 1
    mypad.refresh(mypad_pos, 0, 5, 5, 10, 60)
于 2010-03-26T11:55:42.687 回答
11
于 2013-05-13T12:01:07.323 回答
4

设置 window.scrollok(True)。

文档

于 2010-03-25T13:23:21.433 回答
2

我想使用滚动板来显示一些大型文本文件的内容,但这效果不佳,因为文本可能有换行符,而且很难确定一次显示多少个字符以适应大量的列和行。

所以我决定首先将我的文本文件分割成正好是 COLUMNS 字符的行,当行太短时用空格填充。然后滚动文本变得更加容易。

这是显示任何文本文件的示例代码:

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

主要技巧是这条线:

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()]))

首先,文本中的表格被转换为空格,然后我使用 splitlines() 方法将我的文本转换为行数组。但是有些行可能比我们的 COLUMNS 数长,所以我将每一行拆分为 COLUMNS 字符块,然后使用 reduce 将结果列表转换为行列表。最后,我使用 map 用尾随空格填充每一行,使其长度正好是 COLUMNS 个字符。

希望这可以帮助。

于 2016-01-03T06:25:44.857 回答
0

这是这个问题的答案: How to make a scrolling menu in python-curses

此代码允许您从字符串列表的框中创建一个小的滚动菜单。
您还可以使用此代码从 sqlite 查询或 csv 文件中获取字符串列表。
要编辑菜单的最大行数,您只需编辑max_row.
如果按下回车,程序将打印选定的字符串值及其位置。

from __future__ import division  #You don't need this in Python3
import curses
from math import *



screen = curses.initscr()
curses.noecho()
curses.cbreak()
curses.start_color()
screen.keypad( 1 )
curses.init_pair(1,curses.COLOR_BLACK, curses.COLOR_CYAN)
highlightText = curses.color_pair( 1 )
normalText = curses.A_NORMAL
screen.border( 0 )
curses.curs_set( 0 )
max_row = 10 #max number of rows
box = curses.newwin( max_row + 2, 64, 1, 1 )
box.box()


strings = [ "a", "b", "c", "d", "e", "f", "g", "h", "i", "l", "m", "n" ] #list of strings
row_num = len( strings )

pages = int( ceil( row_num / max_row ) )
position = 1
page = 1
for i in range( 1, max_row + 1 ):
    if row_num == 0:
        box.addstr( 1, 1, "There aren't strings", highlightText )
    else:
        if (i == position):
            box.addstr( i, 2, str( i ) + " - " + strings[ i - 1 ], highlightText )
        else:
            box.addstr( i, 2, str( i ) + " - " + strings[ i - 1 ], normalText )
        if i == row_num:
            break

screen.refresh()
box.refresh()

x = screen.getch()
while x != 27:
    if x == curses.KEY_DOWN:
        if page == 1:
            if position < i:
                position = position + 1
            else:
                if pages > 1:
                    page = page + 1
                    position = 1 + ( max_row * ( page - 1 ) )
        elif page == pages:
            if position < row_num:
                position = position + 1
        else:
            if position < max_row + ( max_row * ( page - 1 ) ):
                position = position + 1
            else:
                page = page + 1
                position = 1 + ( max_row * ( page - 1 ) )
    if x == curses.KEY_UP:
        if page == 1:
            if position > 1:
                position = position - 1
        else:
            if position > ( 1 + ( max_row * ( page - 1 ) ) ):
                position = position - 1
            else:
                page = page - 1
                position = max_row + ( max_row * ( page - 1 ) )
    if x == curses.KEY_LEFT:
        if page > 1:
            page = page - 1
            position = 1 + ( max_row * ( page - 1 ) )

    if x == curses.KEY_RIGHT:
        if page < pages:
            page = page + 1
            position = ( 1 + ( max_row * ( page - 1 ) ) )
    if x == ord( "\n" ) and row_num != 0:
        screen.erase()
        screen.border( 0 )
        screen.addstr( 14, 3, "YOU HAVE PRESSED '" + strings[ position - 1 ] + "' ON POSITION " + str( position ) )

    box.erase()
    screen.border( 0 )
    box.border( 0 )

    for i in range( 1 + ( max_row * ( page - 1 ) ), max_row + 1 + ( max_row * ( page - 1 ) ) ):
        if row_num == 0:
            box.addstr( 1, 1, "There aren't strings",  highlightText )
        else:
            if ( i + ( max_row * ( page - 1 ) ) == position + ( max_row * ( page - 1 ) ) ):
                box.addstr( i - ( max_row * ( page - 1 ) ), 2, str( i ) + " - " + strings[ i - 1 ], highlightText )
            else:
                box.addstr( i - ( max_row * ( page - 1 ) ), 2, str( i ) + " - " + strings[ i - 1 ], normalText )
            if i == row_num:
                break



    screen.refresh()
    box.refresh()
    x = screen.getch()

curses.endwin()
exit()
于 2015-06-14T11:50:13.223 回答
0

另一种方法是使用带有切片符号的列表上的 for-loop 打印可见项目。也就是说,您选择要显示的列表的特定部分,然后每次按向上或向下键时添加或减去增量以更改范围。
比如y是开始的地方,而list[ y : y + coverage ]覆盖范围决定了您要显示列表中的项目数。

from curses import wrapper
import curses

def main(stdscr):
    mY = curses.LINES
    win = curses.newwin(100,50,0,50)
    win.keypad(True)
    numbers = [n for n in range(0,1001)]
    ylen = len(numbers)
    iny = 0
    border_y = mY-5
    def scroll(window):
        [window.addstr(y, 0, f'{b} \n') for y, b in enumerate(numbers[iny:iny+border_y])]
        window.refresh()
    scroll(win)

    
    ###    KEY PRESS    ###
    while(True):
        ch = win.getkey()
        if ch == 'KEY_UP':
            if(iny>0):
                iny-=1
                scroll(win) 
        elif ch == 'KEY_DOWN':
            if(iny<ylen-border_y):
                iny+=1
                scroll(win)
        elif ch == 'q':
            break
wrapper(main)
于 2021-04-28T11:06:50.963 回答