5

我有一个使用子窗口的 curses 应用程序,但我似乎无法删除它们。

例如,此代码不起作用:

import curses
def fill(window, ch):
    y, x = window.getmaxyx()
    s = ch * (x - 1)
    for line in range(y):
        window.addstr(line, 0, s)

def main(stdscr):
    fill(stdscr, 'M')
    stdscr.refresh()
    stdscr.getch()

    subwin = stdscr.subwin(1, 28, 20, 13)
    fill(subwin, 'J')
    subwin.refresh()
    subwin.getch()

    del subwin
    stdscr.touchwin()
    stdscr.refresh()
    stdscr.getch()

curses.wrapper(main)

当您运行此代码时,屏幕会填充“M”,然后当您按下某个键时,会创建一个子窗口并填充“J”。最后,当您再次按下某个键时,代码会删除子窗口并完全重绘屏幕。但是,那些Js还在。

经过一些实验,我发现调用 stdscr 的 clear() 方法会使子窗口消失,但我想将背景恢复原样,而不是将其清空和重写。有谁知道可以做到这一点的方法?

4

2 回答 2

10

您使用子窗口有充分的理由吗?如果您创建一个新的顶级窗口,则代码可以正常工作 - 只需更改stdscr.subwincurses.newwin,它就可以按您的预期工作。

我不是诅咒专家,但我相信子窗口与其父窗口共享字符缓冲区,因此对其中任何一个的更改也会影响另一个。因此,如果您希望将窗口细分为逻辑区域(可能是菜单栏、主区域和状态栏),那么子窗口很有用。但是,如果您正在寻找更像对话框或弹出菜单的东西,那么您需要的是一个全新的窗口(带有自己的单独缓冲区)。

我找不到任何同意或不同意我的 ncurses 的明确参考,但AIX 的手册页似乎证实了这一点:

回想一下,子窗口共享其父窗口缓冲区。通过父窗口或其任何子窗口对子窗口覆盖区域中的共享窗口缓冲区所做的更改会影响共享窗口缓冲区的所有窗口。

当然,这对于 ncurses 来说不是确定的,但我找不到任何相反的东西,它似乎确实可以解释观察到的行为。我还做了一个粗略的实验,subwin.getch()在您示例中的行之后,我立即添加了以下行:

raise Exception(stdscr.instr(20, 15, 3))

在您的示例中,我得到JJJ了实际主窗口的内容。如果我改为使用curses.newwin()来创建窗口而不是stdscr.subwin()我得到预期的MMM.

我不知道有多少特定的 Python curses 资源,但是大多数关于 ncurses 的标准教程和文档对于这种级别非常有用。当我不得不在其中做一些工作时,这个文档非常有用。如果您向下滚动到“示例”部分,您会看到菜单弹出窗口不是子窗口 - 他通过以下稍微模糊的解释暗示了这一点:

我们不希望这个新窗口覆盖背景上先前写入的字符。菜单关闭后,他们应该留在那里。这就是为什么不能将菜单窗口创建为 stdscr 的子窗口的原因。

另外,我记得同时使用stdscr和您自己的窗口可能会导致问题 - “官方” ncurses 介绍对这类事情有一些警告。它还建议完全避免重叠窗口,因为它们显然容易出错,但我不记得它们对短期瞬态模态对话框有任何问题(这是我放置它们的唯一用途)。当然,仅仅因为我的简单用例没有暴露任何问题并不意味着没有任何问题。然而,在像 ncurses 这样复杂的事情中,我可以看到让事情尽可能简单的智慧。

我希望这会有所帮助。正如我所说,我绝不是诅咒专家,但希望这能让你更进一步。

于 2013-02-06T15:56:43.053 回答
1

这段代码有两个问题。

首先,正如之前的海报所指出的,子窗口与父窗口共享一个缓冲区,所以curses.newwin()如果你想要一个完全独立的窗口,你应该使用它。

其次,使用del删除窗口是有问题的,因为它依赖于引用计数/垃圾收集才能正常工作。(一方面,您必须删除对窗口的所有引用才能使其工作。)我建议使用该curses.panel模块来显式显示/隐藏窗口。

于 2014-12-09T05:07:48.833 回答