ノードのカスタマイズ
ノードの実装と編集
マリオネットノードは、Pythonコードによって実装されます。 マリオネットネットワークを実行すると、接続されたすべてのノードとノード間のデータの連携方法を走査しPythonスクリプトを構築します。 各ノードの動作を制御するPythonコードを編集することによって、特別な動作をするオリジナルのノードを作成することができます。 各ノードのPythonコードには、オブジェクト情報パレットの「編集...」ボタンからアクセスできます。
※ 標準搭載のノードをベースに編集して新たなノードを作成する場合、 スクリプトを保存(記憶)するには、OK ボタンでエディタを閉じる前に「#COMMAND;READONLYREFFILE」の記述を含む行をまるごと削除する必要があります。 この「#COMMAND;READONLYREFFILE」は1行目に記述してある場合がほとんどです。
2つの入力ポートと1つの出力ポートを持つ足し算のノード「add」を使って、ノードの定義を解説していきます。
図面上の「add」ノードを選択し、オブジェクト情報パレットの「編集...」ボタンから、Pythonコードの編集画面を開くことができます。
@Marionette.NodeDefinition class Params(metaclass = Marionette.OrderedClass): this = Marionette.Node( 'Add' ) a = Marionette.PortIn( 0 ) b = Marionette.PortIn( 0 ) out = Marionette.PortOut() k = Marionette.OIPControl( 'Multiplier', Marionette.WidgetType.Real, 1 ) def RunNode(self): sum = self.Params.a.value + self.Params.b.value self.Params.out.value = sum * self.Params.k.value
ノードを定義するコードは、2つの部品から構成されています。
- Params : ノードで使用するデータおよび入出力のパラメーターを定義します。 ノードのオブジェクト情報パレット(OIP)に表示されるコントロールウィジェットの定義もここで行います。
- RunNode : プログラム実行時のノードの動作を定義します。データの受け取り方法によって、1回または複数回実行されます。
タイプの定義
this = Marionette.Node( 'Add' )
Marionette.Nodeクラスは、タイプ名をパラメータとして設定することでノードを定義します。 ここで指定したタイプ名は図面上のノードに表示されます。この名前は英語(半角英数字)を推奨しています。 名前を別の言語で表示する必要がある場合は、ノードのローカライズを参照してください。
入力ポートの定義
a = Marionette.PortIn( 0 )
Marionette.PortInクラスを記述することで、ノードに入力ポートを追加します。 実行時には、Marionette.PortInクラスのタイプ変数から値を取得して利用します。 入力ポートは、デフォルト値を引数に設定します。入力ポートに接続がない状態でプログラムを実行すると、このポートの変数はデフォルト値に初期化されます。 また、変数名は、図面上のノードのポートの表示テキストとして使用されます。 この表示をカスタマイズする必要がある場合、名前を別の言語で表示する必要がある場合は、ノードのローカライズを参照してください。
出力ポートの定義
out = Marionette.PortOut()
Marionette.PortOutクラスを記述することで、ノードに出力ポートを追加します。 プログラム実行時に、Marionette.PortOutクラスのタイプ変数に値を代入して利用します。 また、変数名は、図面上のノードのポートの表示テキストとして使用されます。 この表示をカスタマイズする必要がある場合、名前を別の言語で表示する必要がある場合は、ノードのローカライズを参照してください。
def RunNode(self): sum = self.Params.a.value + self.Params.b.value self.Params.out.value = sum
プログラム実行時には、「RunNode」関数に記述したコードを実行します。 「RunNode」の基本的な仕組みは、入力ポート用の変数から値を取得し、実行結果を出力ポート用の変数に値を代入することです。 「RunNode」関数内では、「Params」クラスで定義されているポート変数名を使用して、ポートの値にアクセスできます。
self.Params.[varName].value
「add」ノードの場合、入力ポート変数"a"と"b"から値を取得し、足し算を実行します。実行結果を出力ポート変数"out"に代入します。
ノードが入力ポートから複数のデータを受け取った場合、データの数だけ「RunNode」のコードを繰り返し実行します。
この「RunNode」関数のコードをカスタマイズすることによって特別な動作をするノードを作成することができます。 ここでは、例えば組み込みのPython関数やVectorworksPythonスクリプトのAPIを呼び出して使用することができます。
ノードのParamsクラスのコードでは、入力ポートと出力ポートに加えて、オブジェクト情報パレット(OIP)のコントロールウィジェットを定義できます。
k = Marionette.OIPControl( 'Multiplier', Marionette.WidgetType.Real, 1)
Marionette.OIPControlによりコントロールウィジェットを定義します。Marionette.OIPControlの引数は次の通りです。
- テキスト : OIPに表示されるコントロールウィジェットの左側に表示するテキストを指定します。
- タイプ : コントロールの種類を指定します。使用可能な種類については、以下を参照してください。
- デフォルト値 : ウィジェットの初期値を指定します。コントロールの種類に応じた型で設定する必要があります。
利用可能なコントロールウィジェット一覧
- Integer : 整数値フィールド
p0 = Marionette.OIPControl( 'Integer', Marionette.WidgetType.Int, 1)
p1 = Marionette.OIPControl( 'Boolean', Marionette.WidgetType.Bool, True)
p2 = Marionette.OIPControl( 'Real', Marionette.WidgetType.Real, 3.14)
p3 = Marionette.OIPControl( 'Dimension', Marionette.WidgetType.RealCoord, 3.14)
p4 = Marionette.OIPControl( 'Angle', Marionette.WidgetType.RealAngle, 3.14)
p5 = Marionette.OIPControl( 'Area', Marionette.WidgetType.RealArea, 3.14)
p6 = Marionette.OIPControl( 'Volume', Marionette.WidgetType.RealVolume, 3.14)
p7 = Marionette.OIPControl( 'Text', Marionette.WidgetType.Text, 'Text')
p8 = Marionette.OIPControl( 'Static Information', Marionette.WidgetType.TextStatic, 'Text')
p9 = Marionette.OIPControl( 'Static Value', Marionette.WidgetType.TextStaticParam, 'Text')
p10 = Marionette.OIPControl( 'Popup', Marionette.WidgetType.Popup, 0, ['choice 1', 'choice 2'])
p11 = Marionette.OIPControl( 'Class', Marionette.WidgetType.PopupClasses, 'None')
p12 = Marionette.OIPControl( 'Layer', Marionette.WidgetType.PopupLayers, 'Design Layer-1')
p13 = Marionette.OIPControl( 'Separator', Marionette.WidgetType.Separator, 'Separator Text')
p14 = Marionette.OIPControl('Texture', Marionette.WidgetType.PopupTextures, 0)
p15 = Marionette.OIPControl( 'Button', Marionette.WidgetType.Button, 'script', 'description')
p16 = Marionette.OIPControl( 'Integer', Marionette.WidgetType.Slider, 0, 'description', 0, 25)
p17 = Marionette.OIPControl( 'Radio Button', Marionette.WidgetType.RadioButton, 0, ['choice 1', 'choice 2'])
「SetDescription」関数は、ノードの機能と入出力パラメーターの意味を理解するのに役立つ説明テキストを定義します。 説明テキストを付与したいタイプ変数に対して、SetDescription関数を呼び出して使用します。
注:「\ n」記号を改行として使用し、複数行の説明テキストを設定することができます。
説明テキストを別の言語で表示する必要がある場合は、ノードのローカライズを参照してください。
@Marionette.NodeDefinition class Params(metaclass = Marionette.OrderedClass): this = Marionette.Node( 'Add' ) this.SetDescription( 'This node will add the two numbers from the input ports and produce a the sum in the result port\n' 'Port a : input value for the sum\n' 'Port b : input value for the sum\n' 'Port out : output sum of the input ports' ) a = Marionette.PortIn( 0 ) b = Marionette.PortIn( 0 ) out = Marionette.PortOut() def RunNode(self): sum = self.Params.a.value + self.Params.b.value self.Params.out.value = sum
ノードが入力データを受け取った時、その受け取り方によってデータの数だけコードを繰り返し実行するかどうか定義することができます。 デフォルト(何も定義しない)状態では、ノードの処理は受け取ったデータの数だけ繰り返し実行されます。 ノードを定義するMarionette.Nodeのタイプ変数に対して「SetListAbsorb」関数を呼び出した場合、ノードの処理は受け取ったデータの数によらず一回だけ実行されます。 ノードの処理を1回だけ実行する場合、受け取った入力データはリスト型として処理します。
単一のポートからのすべての入力値を合計し、単一の値を出力する合計ノード「add list」の例を次に示します。
@Marionette.NodeDefinition class Params(metaclass = Marionette.OrderedClass): this = Marionette.Node( 'add list' ) this.SetListAbsorb() # mark this node as list absorbing data = Marionette.PortIn( [] ) # almost always the input defaults should be empty list out = Marionette.PortOut() def RunNode(self): result = 0 try: for v in self.Params.data.value: result = result + v except: result = self.Params.data.value self.Params.out.value = result
ノードのローカライズを実装する方法は2つあります。カスタムリソース(.vwr)を提供可能なサードパーティでは、カススタムローカライズが推奨されます。 Vectorworksで実装されるデフォルトのノードは自動ローカライズを使用しています。
@Marionette.NodeDefinition class Params(metaclass = Marionette.OrderedClass): this = Marionette.Node( 'Add', 'Localized Node Name' ) this.SetDescription( 'localized description' ) a = Marionette.PortIn( 0, 'localized port a' ) b = Marionette.PortIn( 0, 'localized port b' ) out = Marionette.PortOut( 'localized port out' ) k = Marionette.OIPControl( 'localzed OIP name', Marionette.WidgetType.Real, 1 ) def RunNode(self): sum = self.Params.a.value + self.Params.b.value self.Params.out.value = sum * self.Params.k.value
カスタムローカライズ
ローカライズされた文字列は、ノードやポートの定義時に呼び出す関数のオプションの引数として設定します。 または、オプションでない文字列の引数にローカライズされたテキストを反映する場合もあります。
- ノード定義(Marionette.Node) : オプション引数に指定した、ローカライズされた文字列を図面上のタイプ表示に反映します。
- 説明テキスト(SetDescription) : オプションでない説明テキストの引数を、ローカライズされた文字列として使用します。
- 入出力ポート(PortIn) : オプション引数に指定した、ローカライズされた文字列を図面上のポート名に反映します。
- コントロールウィジェット(OIPControl) : オプションでないウィジェット名の引数を、ローカライズされた文字列として使用します。
ただし実際には、ノード内のコードで固定の文字列を使用することは、ノードの使用が1つの言語に制限されるため推奨されません。 Vectorworksには、リソースのパスと文字列キーからローカライズされた文字列を取得するように設計されたリソースシステムがあります。 リソースシステムは、UTF-16エンコードでローカライズされた文字列を記述したテキストファイル(.vwstring)を内包するVWRファイルに基づいています。
ノード内のコードでは、VectorworksPythonスクリプトAPIの「vs.GetVWRString」関数を使用して、ローカライズされた文字列を取得できます。
@Marionette.NodeDefinition class Params(metaclass = Marionette.OrderedClass): this = Marionette.Node( 'Add', vsGetVWRString( 'CustomResource/Strings/MyResource', 'Add' ) ) this.SetDescription( vsGetVWRString( 'CustomResource/Strings/MyResource', 'Add.Description' ) ) a = Marionette.PortIn( 0, vsGetVWRString( 'CustomResource/Strings/MyResource', 'Add.PortIn.a' ) ) b = Marionette.PortIn( 0, vsGetVWRString( 'CustomResource/Strings/MyResource', 'Add.PortIn.b' ) ) out = Marionette.PortOut( vsGetVWRString( 'CustomResource/Strings/MyResource', 'Add.PortOut.out' ) ) k = Marionette.OIPControl( vsGetVWRString( 'CustomResource/Strings/MyResource', 'Add.OIP.Multiplier' ), Marionette.WidgetType.Real, 1 ) def RunNode(self): sum = self.Params.a.value + self.Params.b.value self.Params.out.value = sum
この場合「CustomResource.vwr」リソースファイルの「MyResource.vwstrings」ファイルは次のようになります。
"Add" = "Localized Node Name"; "Add.Description" = "localized description"; "Add.PortIn.a" = "localized port a"; "Add.PortIn.b" = "localized port b"; "Add.PortOut.out" = "localized port out"; "Add.OIP.Multiplier" = "localzed OIP name";
この例では、文字列キーを自動ローカライズで使用する命名基準と統一していますが、異なる命名基準で文字列キーを設定することもできます。
自動ローカライズ
自動ローカライズでは、カスタムローカライズのようにオプションの引数ででローカライズされた文字列を指定しない場合、 「Marionette.vwr」リソースファイルを自動的に検索し、ローカライズされた文字列が存在する場合に適用されます。
注 : 自動ローカライズのリソースシステムは、Vectorworksのデフォルトノードで動作するように設計されており、 Vectorworksにバンドルしたリソースファイルを基にしています。サードパーティの開発者がVectorworksのリソースを変更することは推奨されていません。 ソフトウェアを更新またはアップグレードするときにそれらのリソースが保持されることを保証する方法がないためです。
「Marionette.vwr」リソースファイルの文字列の「LocalizedNodeStrings.vwstrings」ファイルから、 ノード名、ポート名、およびOIPコントロールのローカライズされた文字列を自動的に呼び出します。 自動検索システムでは、対応するノード名、ポート名、または固定文字列からキーを構築します。
- ノード定義(Marionette.Node) : "[ノード名]"
- 説明テキスト(SetDescription) : "[ノード名].Description"
- 入力ポート(PortIn) : "[ノード名].PortIn.[ポート名]"
- 出力ポート(PortOut) : "[ノード名].PortOut.[ポート名]"
- コントロールウィジェット(OIPControl) : "[ノード名].OIP.[テキスト]"
例えば、自動ローカライズするノードの実装が次のようなとき、
@Marionette.NodeDefinition class Params(metaclass = Marionette.OrderedClass): this = Marionette.Node( 'Add' ) a = Marionette.PortIn( 0 ) b = Marionette.PortIn( 0 ) out = Marionette.PortOut() k = Marionette.OIPControl( 'Multiplier', Marionette.WidgetType.Real, 1) p10 = Marionette.OIPControl( 'Popup', Marionette.WidgetType.Popup, 0, ['choice 1', 'choice 2']) def RunNode(self): sum = self.Params.a.value + self.Params.b.value self.Params.out.value = sum * self.Params.k.value
この場合「LocalizedNodeStrings.vwstrings」リソースの構築は次のようになります。
"Add" = "Localized Node Name"; "Add.Description" = "localized description"; "Add.PortIn.a" = "localized port a"; "Add.PortIn.b" = "localized port b"; "Add.PortOut.out" = "localized port out"; "Add.OIP.Multiplier" = "localzed OIP name"; "Add.OIP.Popup" = "localized popup OIP name"; "Add.OIPChoice.choice 1" = "localized choice 1"; "Add.OIPChoicechoice 2" = "localized choice 2";
変数タイプのエラー処理
ノードのカスタマイズでは予期せぬエラーを回避するため、入力ポートの変数のタイプを常に確認するようにコードを設計してください。 このノードがネットワークでどのように使用されるかは不明であるため、どのような値が渡されるか保証されていません。
マリオネットに関するお問い合わせ
マリオネットに関するお問合せは、以下からお問い合せください。