1

基本的に2つの「スレッド」を備えた、作り直されたpython cursesコードがあります。それらは実際のスレッドではありません。1 つはメインのサブウィンドウ処理関数であり、もう 1 つは別のサブウィンドウ処理関数であり、タイマーで実行されます。そして、私は興味深い効果に出くわしました:

  • メイン ウィンドウ コードは、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)
4

2 に答える 2

0

これは参照用です。したがって、この問題を最も簡単な方法で解決するために、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
于 2012-07-07T00:33:49.760 に答える
0

getstr() が空の文字列を返す原因となっているのは、サブウィンドウへの書き込みではなく、アラーム信号自体であると思われます。(シグナルハンドラ内からのウィンドウへの書き込みも明確に定義されていない可能性がありますが、それは別の問題です。)

Python の curses モジュールがその上に構築されることが多い C ライブラリの Curses は、何らかのシグナル (内部で処理するいくつかを除く) が着信すると、ほとんどのブロッキング入力呼び出しから戻ります。C では、状況に応じて定義された API があります (関数は -1 を返し、errno を EINTER に設定します)。

Python モジュールは、curses 関数がエラーを返した場合に例外を発生させると述べています。この場合、なぜそうしないのかわかりません。

編集: 考えられる解決策は、curses よりもプログラマフレンドリーなコンソール UI ライブラリを使用することです。Urwidは、キーボード入力を同時に処理しながら、イベント駆動型の UI 更新 (タイマーによる更新を含む) をサポートしているように見えます (マニュアルを簡単に参照したところ)。信号と呪いの間の大ざっぱで文書化されていない相互作用に対処するよりも、それを学ぶ方が簡単かもしれません.

編集: getch()のマニュアルページから:

処理されたシグナルが存在する場合の getch とその仲間の動作は、SVr4 および XSI Curses のドキュメントでは規定されていません。歴史的な curses の実装では、処理されたシグナル受信のオペレーティング システムの実装が進行中の read(2) 呼び出しを中断するかどうかに応じて、また (一部の実装では) 入力タイムアウトまたは非ブロック モードが行われたかどうかに応じて異なります。設定。

移植性を懸念するプログラマーは、次の 2 つのケースのいずれかに備えておく必要があります。(a) シグナルの受信によって getch が中断されない。(b) シグナル受信により getch が中断され、errno が EINTR に設定された ERR が返されます。ncursesの実装では、処理されたシグナルがgetchを中断することはありません

getstrの代わりにgetchを使用してみましたが、シグナルで -1 が返されます。getstrによって実装されていれば、その (負の戻り値) はこの問題を解決します。したがって、オプションは、(1)エラー処理を使用して独自のgetstrを記述するか、(2) Urwidを使用することです。これは Python ライブラリのバグでしょうか?

于 2012-07-04T00:15:15.467 に答える