如何在Python/Curses子窗口中滚动文本?

28 投票
6 回答
39740 浏览
提问于 2025-04-15 20:50

在我的Python脚本中,我使用了Curses库,并创建了一个子窗口来显示一些文本。因为文本的长度可能会超过窗口的大小,所以这些文本应该是可以滚动的。

看起来Curses窗口没有类似于CSS中“overflow”的属性。Python/Curses的文档在这方面也比较难懂。

这里有没有人知道我该如何用Python编写一个可以滚动的Curses子窗口,并实际实现滚动功能呢?

\编辑:更具体的问题

6 个回答

5

设置窗口的滚动功能为开启状态(scrollok(True))。

查看文档

12

好的,我之前对如何使用“pads”(用来滚动文本)有点困惑,看了这篇帖子后还是没搞明白;尤其是我想在一个已经存在的“行数组”中使用它。所以我准备了一个小例子,展示了newpadsubpad之间的相似之处和不同之处:

#!/usr/bin/env python2.7
import curses

# content - array of lines (list)
mylines = ["Line {0} ".format(id)*3 for id in range(1,11)]

import pprint
pprint.pprint(mylines)

def main(stdscr):
  hlines = begin_y = begin_x = 5 ; wcols = 10
  # calculate total content size
  padhlines = len(mylines)
  padwcols = 0
  for line in mylines:
    if len(line) > padwcols: padwcols = len(line)
  padhlines += 2 ; padwcols += 2 # allow border
  stdscr.addstr("padhlines "+str(padhlines)+" padwcols "+str(padwcols)+"; ")
  # both newpad and subpad are <class '_curses.curses window'>:
  mypadn = curses.newpad(padhlines, padwcols)
  mypads = stdscr.subpad(padhlines, padwcols, begin_y, begin_x+padwcols+4)
  stdscr.addstr(str(type(mypadn))+" "+str(type(mypads)) + "\n")
  mypadn.scrollok(1)
  mypadn.idlok(1)
  mypads.scrollok(1)
  mypads.idlok(1)
  mypadn.border(0) # first ...
  mypads.border(0) # ... border
  for line in mylines:
    mypadn.addstr(padhlines-1,1, line)
    mypadn.scroll(1)
    mypads.addstr(padhlines-1,1, line)
    mypads.scroll(1)
  mypadn.border(0) # second ...
  mypads.border(0) # ... border
  # refresh parent first, to render the texts on top
  #~ stdscr.refresh()
  # refresh the pads next
  mypadn.refresh(0,0, begin_y,begin_x, begin_y+hlines, begin_x+padwcols)
  mypads.refresh()
  mypads.touchwin()
  mypadn.touchwin()
  stdscr.touchwin() # no real effect here
  #stdscr.refresh() # not here! overwrites newpad!
  mypadn.getch()
  # even THIS command erases newpad!
  # (unless stdscr.refresh() previously):
  stdscr.getch()

curses.wrapper(main)

当你运行这个代码时,最开始你会看到类似这样的效果(左边是newpad,右边是subpad):

 ┌────────────────────────┐    ┌────────────────────────┐
 │Line 1 Line 1 Line 1 ───│    │Line 1 Line 1 Line 1 ───│
 │Line 2 Line 2 Line 2    │    │Line 2 Line 2 Line 2    │
 │Line 3 Line 3 Line 3    │    │Line 3 Line 3 Line 3    │
 │Line 4 Line 4 Line 4    │    │Line 4 Line 4 Line 4    │
 │Line 5 Line 5 Line 5    │    │Line 5 Line 5 Line 5    │
                               │Line 6 Line 6 Line 6    │
                               │Line 7 Line 7 Line 7    │
                               │Line 8 Line 8 Line 8    │
                               │Line 9 Line 9 Line 9    │
                               │Line 10 Line 10 Line 10 │
                               └────────────────────────┘

一些注意事项:

  • 无论是newpad还是subpad,它们的宽度和高度都应该根据内容来设置(行数和每行的最大宽度)加上可能的边框空间
  • 在这两种情况下,你都可以通过scrollok()来允许额外的行,但不能增加宽度
  • 在这两种情况下,你基本上是“推”一行到pad的底部;然后用scroll()向上滚动以为下一行腾出空间
  • newpad有一个特殊的refresh方法,可以让你在屏幕上显示这“整个内容”的一部分;而subpad基本上必须以它创建时的大小显示
  • 如果你在添加内容字符串之前先画出pads的边框,那么边框也会一起向上滚动(这就是在...Line 1 ───│部分看到的───)。

有用的链接:

34

window.scroll 的方法太复杂了,移动窗口里的内容不太好实现。相反,curses.newpad 就能帮我解决这个问题。

首先,创建一个新的面板:

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

然后,你可以通过根据 cmd 中的 window.getch() 输入来增加或减少 mypad_pos 的值,从而实现滚动:

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)

撰写回答