皆様こんにちは。
AMDlabの秋山です。
業務でDynamoやGrasshopperを日常的に使用しているのですが、
Dynamoを使っていると、次のような課題に直面することがあります。
例えば、次のようなことはないでしょうか。
・ノードの入力が増えると配線がぐちゃぐちゃになる
・値を入力するたびにノードを触るのが面倒
・「ツールっぽく」ボタンを押して実行したい
こうした悩みを解決する方法の一つが、UI(ユーザーインターフェース)を作ることです。
今回は、DynamoのPython Scriptを使って WPF UIを段階的に作成する方法 を解説します。
前回までのシリーズでは、ZeroTouchNodeを用いてノードのUIを少しリッチにする方法を紹介しました。
ただし、開発環境の構築やC#でのコーディング、ビルド作業などが必要になり、実際にノードとして配置できるようになるまでには少々手間がかかる内容でした。
Python Scriptは、Dynamo上でそのまま書いて実行できるため、
・とにかく速く試せる
・すぐ修正できる
・小さなツールを作るのに向いている
・C#よりPythonのほうが学習コストが低い
などプロトタイプを実現するのには向いているので、今回はPython Scriptで解説していきたいと思います。
まず、「WPF UI」とは何なのかを簡単に説明します。
WPF(Windows Presentation Foundation)は、
Windows上で動くアプリケーションの画面(GUI)を作るための.NET技術です。
ボタン、テキストボックス、プルダウン、リストなど、
UIを構成する部品が標準で用意されており、それらを組み合わせることで画面を作ることができます。
Dynamoは内部的に .NET環境上で動作しており、.NETライブラリを直接利用できるIronPythonを使うことで、PythonからWPF UIを呼び出すことができます。
※DynamoでWPFを扱う場合は、CPythonではなくIronPython2の使用を推奨します。
WPFを使うことで、以下のようなUIが作れるようになります。
・きれいに整った入力画面を作れる
・入力をまとめて一度に受け取れる
・ボタンで実行するような「ツール形式」にできる
・将来的にC#アドインへ発展しやすい
では、実際に作成していきましょう。
今回作成するノードは全部で4つです。
Step1:ボタンだけの最小UIを作るノード
Step2:TextBoxを追加して入力できるようにするノード
Step3:Dropdown(ComboBox)を作るノード
Step4:ListBoxで複数選択を作るノード
上記のステップを体系的に学んでいきます。
この記事で紹介するすべてのコードは、基本的に同じ構造です。
- .NETのUIライブラリを読み込む
- Windowクラスを作る
- UI部品(Buttonなど)を追加する
- OKボタンを押したときに値を保存する
- UIを閉じてOUTに返す
Step1:ボタンだけの最小UIを作るノード
まずはUIを表示し、ボタンを押したら閉じるだけの最小構成です。
|
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 |
import clr clr.AddReference("PresentationFramework") clr.AddReference("PresentationCore") clr.AddReference("WindowsBase") from System.Windows import Window, Thickness from System.Windows.Controls import Button, StackPanel result = None class SimpleUI(Window): def __init__(self): self.Title = "Step1: Button" self.Width = 250 self.Height = 120 panel = StackPanel() panel.Margin = Thickness(10) btn = Button() btn.Content = "クリック" btn.Margin = Thickness(10) # ボタンが押されたら on_click を実行する btn.Click += self.on_click panel.Children.Add(btn) self.Content = panel def on_click(self, sender, args): global result result = "ボタンがクリックされました!" self.Close() ui = SimpleUI() ui.ShowDialog() OUT = result |
|
1 |
btn.Click += self.on_click |
これは「クリックされたら、この関数を呼び出してね」という意味です。
|
1 |
self.Close() |
ウィンドウを閉じます。
OUT = result
Dynamoへ返す値です。
Step2:TextBoxを追加して入力できるようにするノード
次は文字入力を追加します。
UIで入力した文字をOUTに返す形です。
|
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 |
import clr clr.AddReference("PresentationFramework") clr.AddReference("PresentationCore") clr.AddReference("WindowsBase") from System.Windows import Window, Thickness from System.Windows.Controls import Button, StackPanel, TextBox, TextBlock result = None class SimpleUI(Window): def __init__(self): self.Title = "Step2: TextBox" self.Width = 300 self.Height = 180 panel = StackPanel() panel.Margin = Thickness(10) panel.Children.Add(TextBlock(Text="Your Name:")) self.tb = TextBox() self.tb.Margin = Thickness(0, 5, 0, 10) panel.Children.Add(self.tb) btn = Button(Content="OK") btn.Click += self.on_click panel.Children.Add(btn) self.Content = panel def on_click(self, sender, args): global result result = self.tb.Text # TextBoxの入力を取得 self.Close() ui = SimpleUI() ui.ShowDialog() OUT = result |
|
1 |
self.tb.Text |
TextBoxに入力された文字は、Text で取得できます。
Step3:Dropdown(ComboBox)を作る
次はプルダウン選択です。
WPFでは ComboBox を使います。
|
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 |
import clr clr.AddReference("PresentationFramework") clr.AddReference("PresentationCore") clr.AddReference("WindowsBase") from System.Windows import Window, Thickness from System.Windows.Controls import Button, StackPanel, ComboBox OPTIONS = ["赤", "緑", "青"] result = None class SimpleUI(Window): def __init__(self): self.Title = "Step3: Dropdown" self.Width = 250 self.Height = 150 panel = StackPanel() panel.Margin = Thickness(10) self.cb = ComboBox() self.cb.Margin = Thickness(0, 5, 0, 10) for o in OPTIONS: self.cb.Items.Add(o) self.cb.SelectedIndex = 0 # 初期選択を設定 panel.Children.Add(self.cb) btn = Button(Content="OK") btn.Click += self.on_click panel.Children.Add(btn) self.Content = panel def on_click(self, sender, args): global result result = self.cb.SelectedItem # 選択された項目 self.Close() ui = SimpleUI() ui.ShowDialog() OUT = result |
|
1 |
Items.Add() |
プルダウンに項目を追加します。
|
1 |
SelectedIndex = 0 |
最初から1つ選択された状態にできます。
(これを入れないと、選択されていない状態で返る場合があります)
Step4:ListBoxで複数選択を作る
最後はListBoxです。
Ctrlを押しながら複数選択できるようにします。
|
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 |
import clr clr.AddReference("PresentationFramework") clr.AddReference("PresentationCore") clr.AddReference("WindowsBase") from System.Windows import Window, Thickness from System.Windows.Controls import Button, StackPanel, ListBox, SelectionMode ITEMS = ["リンゴ", "バナナ", "オレンジ", "グレープ"] result = [] class SimpleUI(Window): def __init__(self): self.Title = "Step4: ListBox" self.Width = 300 self.Height = 300 panel = StackPanel() panel.Margin = Thickness(10) self.lb = ListBox() self.lb.SelectionMode = SelectionMode.Extended self.lb.Height = 200 self.lb.Margin = Thickness(0, 5, 0, 10) for item in ITEMS: self.lb.Items.Add(item) panel.Children.Add(self.lb) btn = Button(Content="OK") btn.Click += self.on_click panel.Children.Add(btn) self.Content = panel def on_click(self, sender, args): global result result = [x for x in self.lb.SelectedItems] self.Close() ui = SimpleUI() ui.ShowDialog() OUT = result |
|
1 |
SelectionMode.Extended |
これを設定すると、Ctrlで複数選択できるようになります。
SelectedItems
ListBoxで選択された複数項目は SelectedItems に入っています。
ただしそのまま返すと扱いにくいので、Pythonのリストに変換しています。
今回は、DynamoのPython ScriptからWPFを使って、ボタン・テキストボックス・プルダウン・リストボックスを順番に作成し、入力した値をOUTへ返すところまで確認しました。
一見難しそうに見えるWPF UIですが、基本構造はとてもシンプルです。
「入力を受け取り、処理して返す」という流れを理解できれば、応用もスムーズに進められます。
まずは今回のコードをそのまま動かしてみて、UIが表示されること・入力結果が返ることを体感するところから始めてみてください。
次回は、OK/Cancelボタンの追加や入力チェックなど、より実用的なUIへ発展させていきたいと思います。












COMMENTS