11

ユーザーがraw_input()を使用してコンソールでコマンドを入力できるようにしようとしていますが、これは正常に機能します。問題は、ログ情報を画面に出力することがあるバックグラウンドスレッドがあり、それらが入力プロンプトを台無しにすることです(カーソルがその時点で発生する場所に出力が移動するため)。

これは私が何を意味するかを説明する小さなPythonプログラムです。

#!/usr/bin/env python
import threading
import time

def message_loop():
    while True:
        time.sleep(1)
        print "Hello World"

thread = threading.Thread(target = message_loop)
thread.start()

while True:
    input = raw_input("Prompt> ")
    print "You typed", input

これは、実行したときにどのように見えるかの例です。

Prompt> Hello World
Hello World
Hello World
Hello World
test
You typed test
Prompt> Hello World
Hello World
Hello World
hellHello World
o
You typed hello
Prompt> Hello World
Hello World
Hello World
Hello World

私が欲しいのは、プロンプトがスレッドからの出力と一緒に移動することです。そのようです:

Hello World
Hello World
Prompt> test
You typed test
Hello World
Hello World
Hello World
Hello World
Hello World
Prompt> hello
You typed hello
Hello World
Hello World
Hello World
Hello World
Prompt> 

醜いハックに頼らずにこれを達成する方法についてのアイデアはありますか?:)

4

4 に答える 4

24

最近この問題に遭遇したので、今後の参考のためにこの解決策をここに残しておきます。これらのソリューションは、保留中の raw_input (readline) テキストを端末からクリアし、新しいテキストを出力してから、raw_input バッファーにあったものを端末に再出力します。

この最初のプログラムは非常に単純ですが、raw_input を待っているテキストが 1 行しかない場合にのみ正しく機能します。

#!/usr/bin/python

import time,readline,thread,sys

def noisy_thread():
    while True:
        time.sleep(3)
        sys.stdout.write('\r'+' '*(len(readline.get_line_buffer())+2)+'\r')
        print 'Interrupting text!'
        sys.stdout.write('> ' + readline.get_line_buffer())
        sys.stdout.flush()

thread.start_new_thread(noisy_thread, ())
while True:
    s = raw_input('> ')

出力:

$ ./threads_input.py
Interrupting text!
Interrupting text!
Interrupting text!
> WELL, PRINCE, Genoa and Lucca are now no more than private estates of the Bo
Interrupting text!
> WELL, PRINCE, Genoa and Lucca are now no more than private estates of the Bo
naparte family. No, I warn you, that if you do not tell me we are at war,

2 つ目は 2 つ以上のバッファリングされた行を正しく処理しますが、より多くの (標準の) モジュール依存関係があり、ターミナル ハッカーが少し必要です。

#!/usr/bin/python

import time,readline,thread
import sys,struct,fcntl,termios

def blank_current_readline():
    # Next line said to be reasonably portable for various Unixes
    (rows,cols) = struct.unpack('hh', fcntl.ioctl(sys.stdout, termios.TIOCGWINSZ,'1234'))

    text_len = len(readline.get_line_buffer())+2

    # ANSI escape sequences (All VT100 except ESC[0G)
    sys.stdout.write('\x1b[2K')                         # Clear current line
    sys.stdout.write('\x1b[1A\x1b[2K'*(text_len/cols))  # Move cursor up and clear line
    sys.stdout.write('\x1b[0G')                         # Move to start of line


def noisy_thread():
    while True:
        time.sleep(3)
        blank_current_readline()
        print 'Interrupting text!'
        sys.stdout.write('> ' + readline.get_line_buffer())
        sys.stdout.flush()          # Needed or text doesn't show until a key is pressed


if __name__ == '__main__':
    thread.start_new_thread(noisy_thread, ())
    while True:
        s = raw_input('> ')

出力。前の readline 行が適切にクリアされました:

$ ./threads_input2.py
Interrupting text!
Interrupting text!
Interrupting text!
Interrupting text!
> WELL, PRINCE, Genoa and Lucca are now no more than private estates of the Bo
naparte family. No, I warn you, that if you do not tell me we are at war,

有用な情報源:

Python で Linux コンソール ウィンドウの幅を取得する方法

apt like column output - python ライブラリ (このコード サンプルは、Unix または Windows の端末幅を取得する方法を示しています)

http://en.wikipedia.org/wiki/ANSI_escape_code

于 2011-01-11T01:22:25.453 に答える
3

watchUNIXやtopコマンドがどのように機能するかなど、端末ウィンドウからテキストを動的に印刷/削除/上書きできるものが必要だと思います。

あなたの場合、「Prompt>」を出力すると思いますが、「Hello World」を取得すると、「Prompt>」を「Hello World」で上書きし、下の行に「Prompt>」を出力します。端末への通常の出力印刷ではそれができないと思います。

Python のcursesライブラリを使用して、やりたいことができるかもしれません。私はそれを使用したことがないので、問題を解決する方法を説明することはできません (または、モジュールが問題を解決できるかどうかもわかりません) が、検討する価値はあると思います。「python curses チュートリアル」を検索すると、役立つと思われるPDF チュートリアル ドキュメントが提供されました。

于 2010-01-17T20:16:46.770 に答える
1

複数のスレッドからではなく、単一のスレッドから stdout を更新する必要があります...そうしないと、インターリーブされた i/o を制御できません。

出力書き込み用に単一のスレッドを作成する必要があります。

スレッドでキューを使用し、他のすべてのスレッドに出力ログ情報を書き込むことができます..次に、このキューから読み取り、プロンプトメッセージとともに適切なタイミングで stdout に書き込みます。

于 2010-01-17T21:17:15.347 に答える
0

ありえないと思います。とにかくそれはどのように振る舞うべきですか?ユーザーが Enter キーを押すまで何も表示されませんか? その場合、ユーザーがコマンド (またはシステムが期待するもの) を発行したときにのみ出力が行われ、望ましくないように思えます。

スレッドは別のファイルに出力する必要があると思います。

于 2010-01-17T20:01:22.827 に答える