【问题标题】:Python curses dilemmaPython诅咒困境
【发布时间】:2012-04-08 22:00:14
【问题描述】:

我正在玩一些 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),它都不会弄乱终端?

【问题讨论】:

    标签: python ncurses curses


    【解决方案1】:

    我相信你正在寻找 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)
    

    【讨论】:

      【解决方案2】:

      你可以这样做:

      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 脚本执行 reset 命令以使您的终端设置恢复可用状态:

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

        ... 将其称为run.sh 并感到高兴。如果您将参数作为命令输入(更准确地说,如果您将参数用硬引号括起来),这应该几乎与您的 shell 一样运行您的命令。

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

        您还可以使用Python: atexit Handlers 注册您的所有清理操作。但是可能仍然存在您的代码没有被调用的情况 --- 某种不可捕获的信号以及调用 os._exit() 的任何情况。

        即使在这些情况下,我的小 shell 脚本包装器也应该相当健壮。

        【讨论】:

          【解决方案4】:

          你可以:

          • 将您的代码包装在调用curses.endwin()try/finally 块中
          • 专门通过signal 库捕获中断信号
          • 使用atexit 库。

          对于基本情况,第一个选项可能是最简单的(如果您没有运行太多代码)。

          第二个选项是最具体的,如果你想为 Ctrl+C 做一些特殊

          如果您总是想执行某些关闭操作,无论您的程序如何结束,最后一个选项是最可靠的。

          【讨论】:

          • atexit 真的比finally 更健壮吗?
          • @asmeurer 更多的是finally 版本要求您保证所有程序代码都在try 块内。例如,在多线程环境中,情况不一定如此。
          【解决方案5】:

          您需要捕获信号并在捕获期间运行endwin()

          有关此信息,请查看此 SO 答案:How do I capture SIGINT in Python?

          【讨论】:

            猜你喜欢
            • 2014-04-23
            • 2013-11-22
            • 2017-03-19
            • 1970-01-01
            • 2014-09-17
            • 2018-05-08
            • 1970-01-01
            • 2012-01-24
            • 2021-08-15
            相关资源
            最近更新 更多