研究開発

ノードのカスタマイズ

ノードの実装と編集

マリオネットノードは、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)
    
  • Boolean : チェックボックスコントロール(真偽値)
  • 	p1 = Marionette.OIPControl( 'Boolean', Marionette.WidgetType.Bool, True)
    
  • Real : 実数値フィールド
  • 	p2 = Marionette.OIPControl( 'Real', Marionette.WidgetType.Real, 3.14)
    
  • Dimension : 現在の書類の単位での寸法値フィールド
  • 	p3 = Marionette.OIPControl( 'Dimension', Marionette.WidgetType.RealCoord, 3.14)
    
  • Angle : 現在の書類の単位での角度フィールド
  • 	p4 = Marionette.OIPControl( 'Angle', Marionette.WidgetType.RealAngle, 3.14)
    
  • Area : 現在の書類の単位での面積フィールド
  • 	p5 = Marionette.OIPControl( 'Area', Marionette.WidgetType.RealArea, 3.14)
    
  • Volume : 現在の書類の単位での体積フィールド
  • 	p6 = Marionette.OIPControl( 'Volume', Marionette.WidgetType.RealVolume, 3.14)
    
  • Text : 文字列フィールド
  • 	p7 = Marionette.OIPControl( 'Text', Marionette.WidgetType.Text, 'Text')
    
  • Static information : 編集不可能な固定の文字列
  • 	p8 = Marionette.OIPControl( 'Static Information', Marionette.WidgetType.TextStatic, 'Text')
    
  • Static Value : 編集不可能な固定の文字列(太字)
  • 	p9 = Marionette.OIPControl( 'Static Value', Marionette.WidgetType.TextStaticParam, 'Text')
    
  • Popup : ポップアップコントロール
  • 	p10 = Marionette.OIPControl( 'Popup', Marionette.WidgetType.Popup, 0, ['choice 1', 'choice 2'])
    
  • Class : クラスポップアップコントロール
  • 	p11 = Marionette.OIPControl( 'Class', Marionette.WidgetType.PopupClasses, 'None')
    
  • Layer : レイヤポップアップコントロール
  • 	p12 = Marionette.OIPControl( 'Layer', Marionette.WidgetType.PopupLayers, 'Design Layer-1')
    
  • Separator : オブジェクト情報パレットでのパラメータ表示の整理のための区切り線を表示します。ノードに動作に影響を与えません。
  • 	p13 = Marionette.OIPControl( 'Separator', Marionette.WidgetType.Separator, 'Separator Text')
    
  • Textures : テクスチャポップアップコントロール
  • 	p14 = Marionette.OIPControl('Texture', Marionette.WidgetType.PopupTextures, 0)
    
  • Button : ボタンコントロール
  • 	p15 = Marionette.OIPControl( 'Button', Marionette.WidgetType.Button, 'script', 'description')
    
  • Slider : スライダーコントロール
  • 	p16 = Marionette.OIPControl( 'Integer', Marionette.WidgetType.Slider, 0, 'description', 0, 25)
    
  • RadioButton : ラジオボタンコントロール
  • 	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";
変数タイプのエラー処理

ノードのカスタマイズでは予期せぬエラーを回避するため、入力ポートの変数のタイプを常に確認するようにコードを設計してください。 このノードがネットワークでどのように使用されるかは不明であるため、どのような値が渡されるか保証されていません。

マリオネットに関するお問い合わせ

マリオネットに関するお問合せは、以下からお問い合せください。

エーアンドエー株式会社 研究開発室

email:develop@aanda.co.jp