Python addstr使getstr返回信号

Python addstr使getstr返回信号,python,ncurses,curses,Python,Ncurses,Curses,我有一个修改过的python诅咒代码,基本上有两个“线程”。它们不是真正的线程——一个是主子窗口处理函数,另一个是不同的子窗口处理函数,在计时器上执行。我遇到了一个有趣的效果: 主窗口代码正在使用getstr()等待用户的输入 同时,计时器中断会出现,中断代码会在不同的子窗口中输出某些内容 计时器函数的输出将导致getstr()返回空输入 是什么造成了这种影响? 除了检查返回字符串之外,还有其他方法可以避免这种影响吗 重现问题的示例代码: #!/usr/bin/env python # S

我有一个修改过的python诅咒代码,基本上有两个“线程”。它们不是真正的线程——一个是主子窗口处理函数,另一个是不同的子窗口处理函数,在计时器上执行。我遇到了一个有趣的效果:

  • 主窗口代码正在使用getstr()等待用户的输入
  • 同时,计时器中断会出现,中断代码会在不同的子窗口中输出某些内容
  • 计时器函数的输出将导致getstr()返回空输入
是什么造成了这种影响? 除了检查返回字符串之外,还有其他方法可以避免这种影响吗


重现问题的示例代码:

#!/usr/bin/env python
# Simple code to show timer updates

import curses
import os, signal, sys, time, traceback
import math

UPDATE_INTERVAL = 2
test_bed_windows = []
global_count = 0

def signal_handler(signum, frame):
    global test_bed_windows
    global global_count

    if (signum == signal.SIGALRM):
        # Update all the test bed windows
        # restart the timer.
        signal.alarm(UPDATE_INTERVAL)
        global_count += 1

        for tb_window in test_bed_windows:
            tb_window.addstr(1, 1, "Upd: {0}.{1}".format(global_count, test_bed_windows.index(tb_window)))
            tb_window.refresh()
    else:
        print("unexpected signal: {0}".format(signam))
        pass

def main(stdscr):
    # window setup
    screen_y, screen_x = stdscr.getmaxyx()
    stdscr.box()

    # print version
    version_str = " Timer Demo v:0 "
    stdscr.addstr(0, screen_x - len(version_str) - 1, version_str)
    stdscr.refresh()

    window = stdscr.subwin(screen_y-2,screen_x-2,1,1)

    for i in range(3):
        subwin = window.derwin(3,12,1,2 + (15*i))

        test_bed_windows.append(subwin)
        subwin.box()
        subwin.refresh()

    signal.signal(signal.SIGALRM, signal_handler)
    signal.alarm(UPDATE_INTERVAL)

    # Output the prompt and wait for the input:
    window.addstr(12, 1, "Enter Q/q to exit\n")
    window.refresh()

    the_prompt = "Enter here> "
    while True:
        window.addstr(the_prompt)
        window.refresh()

        curses.echo()
        selection = window.getstr()
        curses.noecho()

        if selection == '':
            continue
        elif selection.upper() == 'Q':
            break
        else:
            window.addstr("Entered: {0}".format(selection))
            window.refresh()


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

我怀疑不是写入子窗口导致getstr()返回空字符串,而是报警信号本身。(从信号处理程序中写入窗口也可能没有很好的定义,但这是一个单独的问题。)

Python的Curses模块通常构建在C库Curses之上,当任何信号(除了它内部处理的几个信号)传入时,它将从大部分阻塞输入调用返回。在C语言中,有一个针对这种情况定义的API(函数返回-1并将errno设置为EINTER)

Python模块表示,如果curses函数返回错误,它将引发异常。我不知道为什么它在这种情况下不这样做

编辑:一个可能的解决方案是使用比curses更为程序员友好的控制台UI库。显示(在我简要浏览手册时)以支持事件驱动的UI更新(包括计时器上的更新),同时处理键盘输入。学习这一点可能比处理信号和诅咒之间的粗略和缺乏文档记录的交互更容易

编辑:从手册页编辑:

在SVr4和XSI Curses文档中未指定getch和friends在处理信号存在时的行为。在Historic curses实现中,它的变化取决于操作系统对已处理信号接收的实现是否中断正在进行的读取(2)调用,以及(在某些实现中)取决于是否设置了输入超时或非阻塞模式

关注可移植性的程序员应该为以下两种情况之一做好准备:(a)信号接收不会中断getch;(b) 信号接收中断getch,并使其返回ERR,errno设置为EINTR。在ncurses实现下,处理的信号从不中断getch

我尝试使用getch而不是getstr,它在信号中返回-1。如果由getstr实现,则(负返回值)将解决此问题。
因此,现在的选项是(1)使用错误处理编写自己的getstr,或(2)使用。这可能是Python库的错误吗?

仅供参考。 所以,为了用最简单的方法解决这个问题,我只编写了自己的函数来做一些事情。它不会在出现错误时退出。欢迎所有评论、更正和优化

'''
Function to read a string from the current cursor position.
It supports some simple editing: Ctrl+A, Ctrl+E, Backspace, Del, Home, End,
'''
def getstr(window, prompt = "> ", end_on_error = False):
    result = ""
    starty, startx = window.getyx()
    window.move(starty, 0)
    window.deleteln()
    window.addstr(prompt)
    window.refresh()
    window.keypad(True)

    starty, startx = window.getyx()
    endy, endx = window.getyx()
    maxy, maxx = window.getmaxyx()
    while True:
        try:
            selection = -1
            while (selection < 0 and end_on_error == False):
                selection = window.getch()
        except:
            e = sys.exc_info()[0]
            window.addstr("<p>Error: %s</p>" % e)
            break

        if (selection == curses.KEY_ENTER or selection == ord('\n')):
            break
        elif (selection == curses.KEY_HOME or selection == 1):
            window.move(starty, startx)
            continue
        elif (selection == curses.KEY_END or selection == 5):
            window.move(endy, endx)
            continue
        elif (selection == curses.KEY_DC):
            cy, cx = window.getyx()
            window.delch()
            result = result[:(cx - startx)] + result[(cx - startx + 1):]
            endx -= 1
            continue
        elif (selection == curses.KEY_LEFT):
            cy, cx = window.getyx()
            if (cx > startx):
                window.move(cy, cx-1)
            continue
        elif (selection == curses.KEY_RIGHT):
            cy, cx = window.getyx()
            if (cx < endx):
                window.move(cy, cx+1)
            continue
        elif (selection == curses.KEY_BACKSPACE or selection == 127):
            cy, cx = window.getyx()
            if (cx == startx):
                # no more to backspace
                continue
            else:
                window.move(cy, cx-1)
                window.delch()
                endx -= 1
                cx -= 1
                result = result[:(cx - startx)] + result[(cx - startx + 1):]
                continue
        else:
            endy, endx = window.getyx()
            if (selection < 256 and endx+1 < maxx):
                result = result[:(endx - startx)] + chr(selection) + result[(endx - startx):]
                window.addstr(result[(endx - startx):])
                window.move(endy, endx+1)
                endy, endx = window.getyx()


    window.keypad(False)
    output(result)
    return result
“”
函数从当前光标位置读取字符串。
它支持一些简单的编辑:Ctrl+A、Ctrl+E、Backspace、Del、Home、End、,
'''
def GETSR(窗口,提示符=“>”,结束时出错=False):
result=“”
starty,startx=window.getyx()
窗口移动(开始,0)
window.deleteln()
window.addstr(提示符)
window.refresh()
窗口键盘(真)
starty,startx=window.getyx()
endy,endx=window.getyx()
maxy,maxx=window.getmaxyx()
尽管如此:
尝试:
选择=-1
while(选择<0,结束\u on\u error==False):
selection=window.getch()
除:
e=sys.exc_info()[0]
window.addstr(“错误:%s

%e) 打破 如果(selection==curses.KEY\u ENTER或selection==ord('\n')): 打破 elif(selection==curses.KEY\u HOME或selection==1): 窗口移动(starty,startx) 持续 elif(选择==curses.KEY\u结束或选择==5): window.move(endy,endx) 持续 elif(selection==curses.KEY\u DC): cy,cx=window.getyx() window.delch() 结果=结果[:(cx-startx)]+结果[(cx-startx+1):] endx-=1 持续 elif(selection==curses.KEY_LEFT): cy,cx=window.getyx() 如果(cx>startx): 窗口移动(cy,cx-1) 持续 elif(selection==curses.KEY\u RIGHT): cy,cx=window.getyx() 如果(cx
您能否与我们分享您代码的相关部分?为什么阻塞呼叫会返回?如果信号没有被处理,我理解,但在这种情况下,我有处理程序。另一个问题:如何处理它?我还想让用户点击ret