6
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【WPF】Pythonの標準出力をリアルタイムに表示する

Last updated at Posted at 2021-06-05

Pythonで機械学習を回すGUIアプリを作っているときに、進捗状況を確認できればいいなーと思い色々調べたのでメモ

コード

結果から言うとこれで上手いこといきました。恐らくこれが一番シンプルだと思います。

sumple.pyの出力結果を非同期で取得して、textbox(名前はtb1)に表示しています。
sumple.pyは1秒ずつ文字列を出力するだけのプログラムです。
GUIにはボタンとtextboxだけ置いてます。

MainWindow.xaml.cs
using System;
using System.Windows;
using System.Diagnostics;

namespace ConsoleRead
{
    public partial class MainWindow : Window
    {
        Process pr = null;

        public MainWindow()
        {
            InitializeComponent();
        }

        private void Button_Start(object sender, RoutedEventArgs e)
        {
            HeavyMethod();
        }

        /// <summary>
        /// pythonコードを非同期で実行
        /// <summary>
        public void HeavyMethod()
        {
            pr = new Process();

            // pythonファイル(sumple.py)の指定
            pr.StartInfo.FileName = "python.exe";
            pr.StartInfo.Arguments = "-u sumple.py";

            // コンソール画面を表示させない
            pr.StartInfo.CreateNoWindow = true;

            // 非同期実行に必要
            pr.StartInfo.UseShellExecute = false;
            pr.StartInfo.RedirectStandardOutput = true;

            // イベントハンドラ登録(標準出力時)
            pr.OutputDataReceived += process_DataReceived;

            // イベントハンドラ登録(プロセス終了時)
            pr.EnableRaisingEvents = true;
            pr.Exited += onExited;

            pr.Start();
            pr.BeginOutputReadLine(); //非同期で標準出力読み取り
        }

        /// <summary>
        /// 標準出力があった時に実行
        /// </summary>
        public void process_DataReceived(object sender, DataReceivedEventArgs e)
        {
            string output = e.Data + "\r\n";
            Dispatcher.BeginInvoke(new Action(() =>
            {
                tb1.Text += output;
            }));
        }

        /// <summary>
        /// プロセス終了時に実行
        /// </summary>
        public void onExited(object sender, EventArgs e)
        {
            if (pr != null)
            {
                pr.Close();
                pr.Dispose();

                MessageBox.Show("end");
            }
        }
    }
}
sumple.py
import time

for i in range(10):
    time.sleep(1)
    print("process" ,i*1)
}

ポイント

1. 非同期実行

pr.BeginOutputReadLine() を記述することで非同期で標準出力を読み取ることができます。

2. 標準出力の検知

pr.OutputDataReceived にメソッドを追加することで、標準出力が行われたときに処理をすることができます。

3. UIスレッドからのtextboxの操作

BeginInvoke でUIスレッドに戻してからtb1に結果を追加しています。

4. python実行時に-uオプションを付ける

これを付けないと標準出力が最後にまとめて表示されます。
これで半日悩んで結局teratailに質問投げたんですが、小一時間で回答が返ってきて解決してしまいインターネットしゅごい…となりました。

余談

最初async/awaitを使っていたのですが、awaitを使うとpythonの実行が終わるまで待たなければならず上手くいきませんでした。
標準出力が一つだけのコードを繰り返し使うような場合には良いかもしれません。

注意

全体的にふんわりとした理解なので(特にイベントハンドラ周り)、間違ったことを言っている可能性は十二分にあります。
何かありましたらご指摘いただけると有難いです。

参考

6
7
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?