Python的curses模块在接收第一个字符前不刷新pad

8 投票
3 回答
3606 浏览
提问于 2025-04-18 17:01

我有一段代码,可以让你在一块文本区域里上下滚动。每次你滚动(也就是处理用户输入)的时候,这块文本区域都会按预期更新。不过,在第一次按键之前,什么都不会显示,尽管我在第一次按键之前也调用了pad.refresh(),和每次用户输入后一样。

我的代码是这样的:

def main(self,stdscr):

    x,y = 20,150 # size of the window
    u,a = 10,20 # where to place window - up,across
    pad = curses.newpad(20,150) # nlines, ncols
    pad_pos = 0
    exit = False

    pad.addstr(0,0,str(self.all_results))

    while not exit:
        pad.addstr(0,0,str(self.format_results()))
        ++ stdscr.refresh()
        pad.refresh(pad_pos,10, u,a, x,y)

        -- cmd = stdscr.getch()
        ++ cmd = pad.getch()

        stdscr.nodelay(1)

        + pad.getch() - caused the screen not to update
        + stdscr.refresh() - no change

        if cmd != -1:
            + pad.getch() - - caused the screen not to update
            if  cmd == curses.KEY_DOWN:
                if pad_pos < 3:
                    pad_pos += 1
                try:
                    pad.refresh(pad_pos,0, u,a, x,y)
                except curses.error:
                    pass
            elif cmd == curses.KEY_UP:
                if pad_pos != 0:
                    pad_pos -= 1
                try:
                    pad.refresh(pad_pos,0, u,a, x,y)
                except curses.error:
                    pass

补充说明:代码中显示了我尝试过的修改(+,++,--)

3 个回答

4

stdscr.getch() 这个命令会自动刷新 stdscr(在这之前没有更新),结果就是把屏幕上的 pad 给覆盖掉了,变成了空白的 stdscr。你可以试试用 pad.getch(),或者在第一次使用 pad.refresh() 之前先刷新一下 stdscr

13

看起来在使用标准屏幕(stdscr)之前,有必要先调用一次 stdscr.refresh(),这样一切才能正常工作。我把这个加到了你的例子里(之前我还得先让它能运行 - 见下面的代码):

import curses

def main(stdscr):
    stdscr.keypad(True)
    stdscr.refresh() # This is the necessary initial refresh

    heigth, width = 20, 150 # size of the pad
    top, left = 10, 20 # where to place pad
    viewportHeight = 10
    scrollOffset = 0

    # generate a string to fill pad
    string = ''.join(chr(ord('a') + (x % 26)) for x in range(heigth*width-1))

    pad = curses.newpad(heigth, width)
    pad.addstr(string)
    pad.refresh(scrollOffset, 0, top, left, top + 10, left + width)

    cmd = stdscr.getch()
    while True:
        if  cmd == curses.KEY_DOWN and scrollOffset < heigth - viewportHeight - 1:
            scrollOffset += 1
        elif cmd == curses.KEY_UP and scrollOffset > 0:
            scrollOffset -= 1
        if cmd:
            pad.refresh(scrollOffset, 0, top, left, top + 10, left + width)
        cmd = stdscr.getch()

curses.wrapper(main)

几点说明:

  • 为了将来更方便,我建议你使用 curses.wrapper(main) 这个函数,它会设置好 curses 环境,并把 stdscr 作为参数传给你的 main(stdscr) 方法。这个函数还会在你的代码出错时,妥善处理 curses,避免终端出现问题。你可以看看 Kuchling 和 Raymond 的教程,了解如何使用 Python 的 curses 包。

  • 请提供一个可复现的例子。你的例子里有像 self.all_result 这样的变量,我不得不猜测才能理解你的问题。实际上,我花了更多时间去弄清楚你的代码到底想实现什么,以及复现你描述的错误行为,而不是找到实际的解决方案。

如果你能编辑你的问题,聚焦在实际问题上,我觉得这对很多使用 Python 的 curses 模块的人会很有帮助。

我刚发现你为官方来源的回复提供了悬赏,所以我又多查了一下,发现了 一个在 SO 上的类似回复,也提到这个 - 我们可以称之为 bug - 目前没有文档说明。根据我对 libncurses 的研究,我猜测可能是因为在我的情况下,curses.wrapper 手动调用了一个 清屏方法,在初始化的某个时刻。因此,清屏标志可能被设置了,这样在第一次刷新时就会清空屏幕,而不是我们预期的行为。

0

试着在你挂断 stdscr.getch() 之前,先调用 stdscr.nodelay(1)

撰写回答