Pythonで機械学習を回すGUIアプリを作っているときに、進捗状況を確認できればいいなーと思い色々調べたのでメモ
コード
結果から言うとこれで上手いこといきました。恐らくこれが一番シンプルだと思います。
sumple.pyの出力結果を非同期で取得して、textbox(名前はtb1)に表示しています。
sumple.pyは1秒ずつ文字列を出力するだけのプログラムです。
GUIにはボタンとtextboxだけ置いてます。
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");
}
}
}
}
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の実行が終わるまで待たなければならず上手くいきませんでした。
標準出力が一つだけのコードを繰り返し使うような場合には良いかもしれません。
注意
全体的にふんわりとした理解なので(特にイベントハンドラ周り)、間違ったことを言っている可能性は十二分にあります。
何かありましたらご指摘いただけると有難いです。
参考
- C# GUIアプリケーションからPythonスクリプトを実行する
https://qiita.com/kimisyo/items/0879fc9f1315e2abfd1f - 非同期で標準出力(コンソールアプリケーション)の内容をプログラムで受け取る - C#プログラミング
https://www.ipentec.com/document/csharp-get-standard-output-async - Taskを極めろ!async/await完全攻略
https://qiita.com/acple@github/items/8f63aacb13de9954c5da