addstr使getstr返回sign

2024-09-30 18:29:27 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个修改过的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)

Tags: the代码testsignalwindowscountwindowscreen
2条回答

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

'''
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

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

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

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

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

编辑:从getch()的手册页:

The behavior of getch and friends in the presence of handled signals is unspecified in the SVr4 and XSI Curses documentation. Under historical curses implementations, it varied depending on whether the operating system's implementation of handled signal receipt interrupts a read(2) call in progress or not, and also (in some implementations) depending on whether an input timeout or non-blocking mode has been set.

Programmers concerned about portability should be prepared for either of two cases: (a) signal receipt does not interrupt getch; (b) signal receipt interrupts getch and causes it to return ERR with errno set to EINTR. Under the ncurses implementation, handled signals never interrupt getch.

我尝试过使用getch而不是getstr,它确实在一个信号上返回-1。如果由getstr实现,那么(负返回值)将解决这个问题。 所以现在的选项是(1)编写自己的getstr,并进行错误处理;或者(2)使用Urwid。这是Python库的bug吗?在

相关问题 更多 >