17

一連の操作を実行し、応答をSTDOUTに出力するPythonプログラムがあります。今、私はその既存のコードを呼び出すGUIを書いています、そして私はSTDOUTの代わりにGUIで同じ内容を印刷したいと思います。この目的でテキストウィジェットを使用します。タスクを実行する既存のコードを変更したくありません(このコードは他のプログラムでも使用されています)。

この既存のタスク定義を使用し、そのSTDOUT結果を使用して、テキストウィジェットに挿入する方法を教えてもらえますか?メインGUIプログラムで、このタスク定義を呼び出し、その結果をSTDOUTに出力します。この情報を使用する方法はありますか?

4

5 に答える 5

22

sys.stdoutテキストウィジェットに書き込む独自のファイルのようなオブジェクトに置き換えることで、おそらくこれを解決できます。

例えば:

import Tkinter as tk
import sys

class ExampleApp(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        toolbar = tk.Frame(self)
        toolbar.pack(side="top", fill="x")
        b1 = tk.Button(self, text="print to stdout", command=self.print_stdout)
        b2 = tk.Button(self, text="print to stderr", command=self.print_stderr)
        b1.pack(in_=toolbar, side="left")
        b2.pack(in_=toolbar, side="left")
        self.text = tk.Text(self, wrap="word")
        self.text.pack(side="top", fill="both", expand=True)
        self.text.tag_configure("stderr", foreground="#b22222")

        sys.stdout = TextRedirector(self.text, "stdout")
        sys.stderr = TextRedirector(self.text, "stderr")

    def print_stdout(self):
        '''Illustrate that using 'print' writes to stdout'''
        print "this is stdout"

    def print_stderr(self):
        '''Illustrate that we can write directly to stderr'''
        sys.stderr.write("this is stderr\n")

class TextRedirector(object):
    def __init__(self, widget, tag="stdout"):
        self.widget = widget
        self.tag = tag

    def write(self, str):
        self.widget.configure(state="normal")
        self.widget.insert("end", str, (self.tag,))
        self.widget.configure(state="disabled")

app = ExampleApp()
app.mainloop()
于 2012-09-10T12:57:54.260 に答える
12

Pythonでは、print('examplestring')を呼び出すと、間接的にsys.stdout.write('examplestring')を呼び出します。

from tkinter import *
root=Tk()
textbox=Text(root)
textbox.pack()
button1=Button(root, text='output', command=lambda : print('printing to GUI'))
button1.pack()

方法1:GUIで印刷する

def redirector(inputStr):
    textbox.insert(INSERT, inputStr)

sys.stdout.write = redirector #whenever sys.stdout.write is called, redirector is called.

root.mainloop()

実際、print-(callsfor)-> sys.stdout.write-(callsfor)->redirectorを呼び出しています

方法2:デコレータを作成する-CLIとGUIの両方で印刷する

def decorator(func):
    def inner(inputStr):
        try:
            textbox.insert(INSERT, inputStr)
            return func(inputStr)
        except:
            return func(inputStr)
    return inner

sys.stdout.write=decorator(sys.stdout.write)
#print=decorator(print)  #you can actually write this but not recommended

root.mainloop()

デコレータが行うことは、実際にfuncsys.stdout.writeをfuncininに割り当てることです。

sys.stdout.write=inner

func innerは、実際のsys.stdout.writeをコールバックする前に、コードを1行追加します。

これは、古いfuncsys.stdout.writeを更新して新しい機能を追加する方法です。テキストボックスへの印刷でエラーが発生した場合に、少なくともsys.stdout.writeの元の関数をCLIに保持する場合を除いて、tryを使用したことに気付くでしょう。

方法3:ブライアンオークリーの例

...
    sys.stdout = TextRedirector(self.text, "stdout")
...
class TextRedirector(object):
    def __init__(self, widget, tag="stdout"):
        self.widget = widget
        self.tag = tag

    def write(self, str):
        self.widget.configure(state="normal")
        self.widget.insert("end", str, (self.tag,))
        self.widget.configure(state="disabled")

彼がしたことは、sys.stdoutをメソッド.write(str)を使用してClassTextRedirectorに割り当てたことです。

したがって、print('string')-calls for-> sys.stdout.write('string')-callsfor-> TextRedirector.write('string')を呼び出します

于 2015-07-13T16:18:45.337 に答える
0

を使用してCLIプログラムを呼び出し、subprocess.Popen生成されたstdoutを取得して、テキストウィジェットに表示できます。

(未テスト)の線に沿った何か:

import subprocess

with subprocess.Popen(your_CLI_program, stdout=subprocess.PIPE) as cli
    line = cli.stdout.readline()

    #process the output of your_CLI_program
    print (line)

これは、CLIプログラムの実行が終了し、GUIがフリーズするまでブロックされることに注意してください。ブロッキングを回避するには、このコードをに入れthreading.Threadて、スレッドが終了するのを待っている間にGUIを更新させます。

于 2012-09-10T13:24:29.703 に答える
-1

実際、この問題はに限定されていないと思いますtkinter。実際にリダイレクトしているため、任意のフレームワークを適用できますsys.stdout

これを行うためにクラス(RedirectStdMsg)を作成しました。

tl; dr

original = sys.stdout
sys.stdout = everything_you_like
...
sys.stdout = original  # restore

import sys
from typing import TextIO
from typing import Callable
# import tkinter as tk

class RedirectStdMsg:
    __slots__ = ('original', 'output_device',)

    def __init__(self, sys_std: TextIO):
        self.output_device = None
        self.original = sys_std

    def __call__(self, output_device=Callable[[str], None]):
        self.output_device = output_device
        return self

    def __enter__(self):
        if self.output_device is None:
            raise AttributeError('output_device is empty')
        self.start(self.output_device)

    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_val:
            self.write(str(exc_val))
        self.stop()

    def start(self, output_device):
        self.output_device = output_device
        std_name = self.original.name.translate(str.maketrans({'<': '', '>': ''}))
        exec(f'sys.{std_name} = self')  # just like: ``sys.stderr = self``

    def stop(self):
        std_name = self.original.name.translate(str.maketrans({'<': '', '>': ''}))
        exec(f'sys.{std_name} = self.original')
        self.output_device = None

    def write(self, message: str):
        """ When sys.{stderr, stdout ...}.write is called, it will redirected here"""
        if self.output_device is None:
            self.original.write(message)
            self.original.flush()
            return
        self.output_device(message)

テスト

tkクラスをテストする

@BryanOakleyから変更

class ExampleApp(tk.Tk):
    def __init__(self, **options):
        tk.Tk.__init__(self)
        toolbar = tk.Frame(self)
        toolbar.pack(side="top", fill="x")
        b1 = tk.Button(self, text="print to stdout", command=self.print_stdout)
        b2 = tk.Button(self, text="print to stderr", command=self.print_stderr)
        b1.pack(in_=toolbar, side="left")
        b2.pack(in_=toolbar, side="left")
        self.text = tk.Text(self, wrap="word")
        self.text.pack(side="top", fill="both", expand=True)
        self.text.tag_configure("stderr", foreground="#b22222")

        self.re_stdout = options.get('stdout')
        self.re_stderr = options.get('stderr')

        if self.re_stderr or self.re_stderr:
            tk.Button(self, text='Start redirect', command=self.start_redirect).pack(in_=toolbar, side="left")
            tk.Button(self, text='Stop redirect', command=self.stop_redirect).pack(in_=toolbar, side="left")

    def start_redirect(self):
        self.re_stdout.start(TextRedirector(self.text, "stdout").write) if self.re_stdout else ...
        self.re_stderr.start(TextRedirector(self.text, "stderr").write) if self.re_stderr else ...

    def stop_redirect(self):
        self.re_stdout.stop() if self.re_stdout else ...
        self.re_stderr.stop() if self.re_stderr else ...

    @staticmethod
    def print_stdout():
        """Illustrate that using 'print' writes to stdout"""
        print("this is stdout")

    @staticmethod
    def print_stderr():
        """Illustrate that we can write directly to stderr"""
        sys.stderr.write("this is stderr\n")


class TextRedirector(object):
    def __init__(self, widget, tag="stdout"):
        self.widget = widget
        self.tag = tag

    def write(self, msg):
        self.widget.configure(state="normal")
        self.widget.insert("end", msg, (self.tag,))
        self.widget.configure(state="disabled")

テストケース

def test_tk_without_stop_btn():
    app = ExampleApp()
    with RedirectStdMsg(sys.stdout)(TextRedirector(app.text, "stdout").write), \
            RedirectStdMsg(sys.stderr)(TextRedirector(app.text, "stderr").write):
        app.mainloop()


def test_tk_have_stop_btn():
    director_out = RedirectStdMsg(sys.stdout)
    director_err = RedirectStdMsg(sys.stderr)
    app = ExampleApp(stdout=director_out, stderr=director_err)
    app.mainloop()


def test_to_file():

    # stdout test
    with open('temp.stdout.log', 'w') as file_obj:
        with RedirectStdMsg(sys.stdout)(file_obj.write):
            print('stdout to file')
    print('stdout to console')


    # stderr test
    with open('temp.stderr.log', 'w') as file_obj:
        with RedirectStdMsg(sys.stderr)(file_obj.write):
            sys.stderr.write('stderr to file')
    sys.stderr.write('stderr to console')

    # another way
    cs_stdout = RedirectStdMsg(sys.stdout)
    cs_stdout.start(open('temp.stdout.log', 'a').write)
    print('stdout to file 2')
    ...
    cs_stdout.stop()
    print('stdout to console 2')


if __name__ == '__main__':
    test_to_file()
    test_tk_without_stop_btn()
    test_tk_have_stop_btn()

これはtest_tk_have_stop_btn(): ここに画像の説明を入力してください

于 2020-04-08T09:57:43.903 に答える
-2

通常はstdoutに出力する関数は、代わりにテキストをテキストウィジェットに配置する必要があります。

于 2012-09-10T12:40:45.450 に答える