2

使用cursesWindows 上的模块(通过这个 PDCurses),我试图打破一个KeyboardInterrupt异常,但是当我按下 ctrl+c 时它不会引发。

一些提炼的代码:

from curses import wrapper
items = ['a', 'very', 'long', 'list', 'of', 'strings']

def main(screen):
    for item in items:
        screen.addstr(0, 0, item)
        screen.getch()
        screen.refresh()

wrapper(main)

清单很长,现在items我不能中途停止执行。我必须反复按键,直到我到达终点。天堂禁止我尝试这个while True:

当我按 ctrl+c 时,不会引发异常。它确实传递给我getch()的 as 3。SOP是在getch接收时手动提高3,还是有更合适的方法来避免吞咽KeyboardInterrupt

4

3 回答 3

2

默认curses使用raw模式,关闭文档中的中断/退出/挂起等

在 raw 模式下,普通行缓冲和中断、退出、挂起和流控键的处理被关闭;字符被一一呈现给curses输入函数

从 C 的 curses文档中:

这两个函数 (rawcbreak) 之间的区别在于将控制字符如挂起 (CTRL-Z)、中断和退出 (CTRL-C) 传递给程序的方式。在该raw()模式下,这些字符直接传递给程序而不产生信号。

由于 python在发送 aKeyboardInterrupt时会引发 a SIGINT,因此预计不会引发它。3你看到的确实代表一个中断。

由于这是由 C 库处理的事情,因此无法避免这种“吞噬”异常。但是,您可以使用一个简单的包装器来getch检查它何时返回3并相应地引发错误。

于 2014-01-14T20:55:19.017 回答
1

使用 UGETCHAR_ (在下面实现)而不是getch

def UGETCHAR_(scr):
    import curses
    h = scr.getch()
    if h == 3:
        raise KeyboardInterrupt
    if h == 26:
        raise EOFError
    return h

让我解释。

因此,首先在调用该函数时,它会导入curses[ import curses]。

接下来,它运行您正在使用的 getch() 并将结果放入一个名为h. [ h = scr.getch()]

raise KeyboardInterrupt然后,如果h是 3 ( ^C) [ ] ,它会引发 KeyboardInterrupt [ ],如果是 26 ( ) [ ],它会引发if h == 3:EOFError [ ]。raise KeyboardInterrupth^Zif h == 26:

最后,它返回h[ return h] 的值。

于 2020-03-15T21:03:29.647 回答
0

很久以前就问过这个问题,但我遇到了完全相同的问题。我想要一个使用 curses 的 Python 程序在 Windows 和 Linux 上运行。KeyboardInterrupt 在 Linux 但不是 Windows 上完全按预期工作。我尝试了所有的诅咒设置功能,但永远无法让 Ctrl+C 进入执行。

下面的代码似乎有效,但并不理想。到目前为止我找不到更好的方法。这种方法在 Windows 上的问题在于它不会中断。代码将在检查输入之前执行当前循环迭代中所做的任何工作。(它仍然可以在 Linux 上完美运行。)

import curses
import time

def Main(stdscr):
    stdscr.addstr(0, 0, "Main starting.  Ctrl+C to exit.")
    stdscr.refresh()
    try:
        i = 0
        while True:
            i = i + 1
            stdscr.addstr(1, 0, "Do work in loop. i=" + str(i))
            stdscr.refresh()
            time.sleep(1)

            stdscr.nodelay(1) # Don't block waiting for input.
            c = stdscr.getch() # Get char from input.  If none is available, will return -1.
            if c == 3:
                stdscr.addstr(2, 0, "getch() got Ctrl+C")
                stdscr.refresh()
                raise KeyboardInterrupt
            else:
                curses.flushinp() # Clear out buffer.  We only care about Ctrl+C.
        
    except KeyboardInterrupt:
        stdscr.addstr(3, 0, "Ctrl+C detected, Program Stopping")
        stdscr.refresh()
    
    finally:
        stdscr.addstr(4, 0, "Program cleanup")
        stdscr.refresh()
        time.sleep(3) # This delay just so we can see final screen output

curses.wrapper(Main)

Linux 上的输出:

Main starting.  Ctrl+C to exit.
Do work in loop. i=4

Ctrl+C detected, Program Stopping
Program cleanup

Windows 上的输出:

Main starting.  Ctrl+C to exit.
Do work in loop. i=6
getch() got Ctrl+C
Ctrl+C detected, Program Stopping
Program cleanup
于 2021-02-07T10:56:41.820 回答