Python curses困境

12

我正在尝试使用Python和curses进行一些实验。

当我运行

import time
import curses

def main():
    curses.initscr()
    curses.cbreak()
    for i in range(3):
        time.sleep(1)
        curses.flash()
        pass
    print( "Hello World" )
    curses.endwin()

if __name__ == '__main__':
    main()

如果我一直等到程序执行结束,curses.endwin() 将被调用,那么一切都能顺利进行。然而,如果我使用 Ctrl-C 来提前结束程序,curses.endwin() 就永远不会得到调用,这会破坏我的终端会话。

如何正确应对这种情况?我怎样才能确保无论我如何试图结束/中断程序(例如Ctrl-C,Ctrl-Z),它都不会破坏终端?

5个回答

46

我相信您正在寻找 curses.wrapper。请参阅http://docs.python.org/dev/library/curses.html#curses.wrapper

它会在初始化时执行 curses.cbreak(),curses.noecho() 和 curses_screen.keypad(1),并在退出时进行反转,即使退出是异常情况也一样。

您的程序可作为函数传递给该包装器, 例如:

def main(screen):
    """screen is a curses screen passed from the wrapper"""
    ...

if __name__ == '__main__':
    curses.wrapper(main)

7
你可以这样做:
def main():
    curses.initscr()

    try:
        curses.cbreak()
        for i in range(3):
            time.sleep(1)
            curses.flash()
            pass
        print( "Hello World" )
    finally:
        curses.endwin()

或者更好的方法是,创建一个上下文包装器:
class CursesWindow(object):
    def __enter__(self):
        curses.initscr()

    def __exit__(self):
        curses.endwin()

def main():
    with CursesWindow():
        curses.cbreak()
        for i in range(3):
            time.sleep(1)
            curses.flash()
            pass
        print( "Hello World" )

3

我的建议:为了测试目的,使用一个简单的包装shell脚本调用您的脚本;让shell脚本执行reset命令,以使您的终端设置恢复到可用状态:

#!/bin/sh
eval "$@"
stty -sane
reset

...将其命名为run.sh并愉快地运行。这将以几乎相同的方式运行您的命令,就像您输入命令参数一样(更确切地说,如果您在硬引号中包装参数)。

为了确保您的程序在遇到未捕获的异常和异常终止时可以保持终端处于强大状态...要么使用curses.wrapper()方法调用您的顶级入口点(可能是main()或任何您选择实现的main_curses_ui()),要么使用您自己的一系列curses.*方法来包装代码,以恢复光标可见性、恢复“cbreak”(规范/熟食输入)模式、恢复正常的“echo”设置和其他可能已经被你搞砸的设置。

您还可以使用Python: atexit Handlers注册所有清理操作。但仍然可能出现一些情况,您的代码不会被调用-某些不可捕获的信号类型和任何情况下都会调用os._exit()的情况。

即使在这些情况下,我的小壳脚本包装器也应该还是相当强大的。


1
你可以:
  • 将你的代码放入一个try/finally块中,并调用curses.endwin()
  • 通过signal库特别捕获中断信号
  • 使用atexit库。

如果只是运行少量代码,第一种方法可能是最简单的。

如果你想要在Ctrl+C下执行一些特殊操作,第二种方法是最具体的。

如果你希望不论程序如何结束都执行某些关闭操作,最后一种方法是最强大的。


atexit 真的比 finally 更健壮吗? - asmeurer
@asmeurer,更准确地说,finally 版本要求你保证所有程序代码都在 try 块内。在多线程环境中,这并不一定成立。 - Amber

-1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接