pythonの他のスレッドによってプロンプトが上書きされないことなくraw_input()から入力を読み取ります
-
21-09-2019 - |
質問
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>
醜いハックに頼らずにこれを達成する方法についてのアイデアはありますか? :)
解決
私は最近、この問題に遭遇しましたが、将来の参照のためにこのソリューションをここに残したいと思います。これらのソリューションは、端末から保留中の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('> ')
出力。以前の読み取り線が正しくクリアされました:
$ ./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のいずれかの端子幅を取得する方法を示しています)
他のヒント
ターミナルウィンドウからテキストを動的に印刷/削除/上書きできるものが必要だと思います。 watch
また top
コマンドが機能します。
あなたの場合、「Prompt>」を印刷すると思いますが、「Hello World」を取得すると、「Hello World」で「プロンプト」を上書きし、以下の行に「プロンプト>」を印刷します。ターミナルへの通常の出力印刷でそれができるとは思いません。
あなたはpythonを使用してあなたがやりたいことをすることができるかもしれません 呪い 図書館。私はそれを使用したことがないので、あなたの問題を解決する方法(またはモジュールがあなたの問題を解決できるかどうかさえ)をあなたに伝えることはできませんが、私はそれを調べる価値があると思います。 「Python Cursesチュートリアル」の検索が提供されました PDFチュートリアルドキュメント 役に立つようです。
複数のスレッドからではなく、単一のスレッドからstdoutを更新する必要があります...または、インターリーブI/Oを制御できない場合があります。
出力ライティング用の単一のスレッドを作成する必要があります。
スレッドでキューを使用して、他のすべてのスレッドに出力ロギング情報を記述してもらうことができます。次に、このキューから読み取り、プロンプトメッセージとともに適切な時期にstdoutに書き込みます。
私はそれが不可能だと思います。とにかくそれはどのように振る舞うべきですか?ユーザーがEnterを押すまで何も表示されませんか?そうであれば、出力は、ユーザーがコマンド(またはシステムが期待するもの)を発行したときにのみ発生しますが、それは望ましくないように聞こえません。
スレッドは別のファイルに出力する必要があります。