前回は下準備でした。

Introduction

今回の目的は、WPFからPythonスクリプト、つまり .py ファイルを呼び出します。
また、WPFなので、MVVMを使います。MVVMフレームワークは MVVM Light Toolkit ですが、そのあたりは自由に適宜選択してください。
ソースは下記になります

Python側

Python側のプロジェクトを作成します。
と言っても、Python側はソースを管理し、コーディングの際にインテリセンスを有効にするためだけで、C#側から直接プロジェクトを参照するというわけではありません。
Pythonのプロジェクトは、Python Tools for Visual Studioをインストールした上で、ソリューションに対してプロジェクトを追加する際、下記のテンプレートを選択します。

追加したプロジェクトには、python1.pyだけが存在します。
エクスプローラ上には、Python1.pyprojpython1.pyというシンプル構成です。
次に、Pythonのソースを。

1
2
3
class PythonTest:
def showMessage(self):
return "Hello world from Python"

PythonTestというクラスが、showMessageというメソッドを持っているだけです。
目的は、C#側から、このメソッドを呼び出すことです。
つまり、クラスのインスタンス化、クラスメソッドの呼び出し、戻り値の表示、という3つの基礎的な題材をテストできます。

C#側

いつも通り、簡単にMVVMを使用して簡単なプロジェクトを作成します。
今回はボタンを押すと、メッセージボックスが表示され、pythonスクリプトに書かれた、つまりshowMessageメソッドの戻りの値が表示されるというアプリケーションになります。
Xaml側は省略します。
最初に、IronPythonを利用するために、参照を追加します。
C:\Program Files (x86)\IronPython 2.7\PlatformsNet35Net40Sl5 というフォルダが存在します。
それらのうちの一つを選び配下から、IronPython.dllMicrosoft.Scripting.dllMicrosoft.Dynamic.dll をプロジェクトに追加します。
これでプロジェクトの設定は終了です。
次に、Pythonを呼び出す側のコードです。

1
2
3
4
5
6
7
8
9
10
11
namespace WPFPython1.ViewModels.Interfaces
{

public interface IPythonWrapper
{

string ShowMessage();

}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
using System;
using System.IO;
using Microsoft.Scripting.Hosting;
using WPFPython1.ViewModels.Interfaces;

namespace WPFPython1.ViewModels
{

public sealed class PythonWrapper : IPythonWrapper
{

#region フィールド

private readonly ScriptRuntime _ScriptRuntime;

private dynamic _PythonObject;

private dynamic _PythonTest;

private readonly string _Path;

#endregion

#region コンストラクタ

public PythonWrapper(ScriptRuntime scriptRuntime, string path)
{
if (scriptRuntime == null)
throw new ArgumentNullException(nameof(scriptRuntime));
if (path == null)
throw new ArgumentNullException(nameof(path));

if (!File.Exists(path))
throw new FileNotFoundException(path);

this._ScriptRuntime = scriptRuntime;
this._Path = path;
this._PythonObject = this._ScriptRuntime.UseFile(this._Path);
this._PythonTest = this._PythonObject.PythonTest();
}

#endregion

#region メソッド

public string ShowMessage()
{
return this._PythonTest.showMessage();
}

#endregion

}

}

ScriptRuntime 変数は、Microsoft.Scripting.Hosting.ScriptRuntime 型で、Pythonの実行環境を表します。
次のソースの IronPython.Hosting.Python.CreateRuntime メソッドの戻りでもあります。
UseFile メソッドには呼び出したいPythonファイルを指定します。戻りを dynamic 型にして、メンバ変数に格納します。
_PythonObject メンバ変数から PythonTest クラスをインスタンス化して、_PythonTest メンバ変数に格納します。

そして、ShowMessage メソッドで、pythonの showMessage 関数を呼び出します。
dynamic型なので、コンパイル時にエラーは解決できませんので、記述が間違っていてもエラーになりませんので注意が必要です。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
using System.Windows;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Ioc;
using IronPython.Hosting;
using Microsoft.Practices.ServiceLocation;
using Microsoft.Scripting.Hosting;
using WPFPython1.ViewModels;
using WPFPython1.ViewModels.Interfaces;

namespace WPFPython1
{
/// <summary>
/// This class contains static references to all the view models in the
/// application and provides an entry point for the bindings.
/// <para>
/// See http://www.galasoft.ch/mvvm
/// </para>
/// </summary>
public class ViewModelLocator
{
static ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

if (ViewModelBase.IsInDesignModeStatic)
{
// SimpleIoc.Default.Register<IDataService, Design.DesignDataService>();
}
else
{
// SimpleIoc.Default.Register<IDataService, DataService>();
}

SimpleIoc.Default.Register<IMessageDialog>(() => new MessageDialog(Application.Current.MainWindow));

var py = Python.CreateRuntime();
SimpleIoc.Default.Register<IPythonWrapper>(()=> new PythonWrapper(py, @"pythons\Python1.py"));
SimpleIoc.Default.Register<IMainViewModel, MainViewModel>();
}

/// <summary>
/// Gets the Main property.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
"CA1822:MarkMembersAsStatic",
Justification = "This non-static member is needed for data binding purposes.")]
public IMainViewModel Main
{
get
{
return ServiceLocator.Current.GetInstance<IMainViewModel>();
}
}
}
}

見たままです。
ScriptRuntimeを生成し、実行したいPythonスクリプトをパス指定しているだけです。
MessageDialogは単純に System.Windows.MessageBox.Show メソッドを呼ぶためだけのinterfaceです。

テスト

実際に実行してみます。
サンプルソースをビルドすると実行ディレクトリに、pythonsフォルダが生成され、Python1.pyファイルがコピーされます。
このファイルはPythonプロジェクトのファイルそのものです。
実行するとボタンが一つ。

ボタンを押すと、Python1.pyのshowMessageの戻りの内容が表示されます。

アプリを終了し、Python1.pyのshowMessage の内容を書き換え、再度実行するとメッセージの内容が書き換わっているのがわかります。

Conclusion

簡単に実行できました。
Pythonの実行処理を分離したので、I/Fさえ維持すれば、呼び出す対象のライブラリをPythonからC++にする!!とかいう選択も可能になるでしょう。
このあたり、MVVMできちんと分離してあるのが良い感じです。

Source Code

https://github.com/takuya-takeuchi/Demo/tree/master/WPF/01_WPF.Python1