見出し画像

電気部材等のマリオネットオブジェクトをプラグインオブジェクトにしてみた


以前作った、電気部材等のマリオネットオブジェクトは、それなりに使い勝手が良く、住宅などの小さなプロジェクトでは多用しています。

ただし、

・別のファイルで作成するときに、既に使っているファイルからオブジェクトをコピペして持ってこないといけない。
・オブジェクトをたくさん複製した時に、(共通しているはずの)マリオネットのコード自体も無駄に複製されるのが気持ちが悪かった。
・pythonの練習をしてみたかった。

の3点の理由からpythonでコードを書いて、1点型のプラグインオブジェクトにしてみました。

最初はスクリプトで情報パレットを操作しようとしてみた

マリオネットの時は、けっこう苦労して、任意のフォルダの中のシンボル名のリストを取得してポップアップで選択できるようにしたのですが、プラグインオブジェクトで設定するパラメータのポップアップのアイテムは、そのままではスクリプトで変更できないので、いちいち、シンボル名のリストを手打ち(またはコピペ)する必要があります。

マリオネットでできたのだから、スクリプトでもポップアップの選択肢を設定できるはず、と思い、いろいろ調べていたところ、以下のページを見つけました。

よく分からないけれども、vsoのつく関数で情報パレットの中身を操作できそうです。

この辺の情報もほとんど発見できずに、ひたすら試行錯誤を繰り返したところ、シンボル名リストのポップアップ化も含めて、何とかパラメータのリストを情報パレットに表示できるところまではいきました。

ただ、結果を変数として取得する方法がどうしても分からなかったのと、いくつかバグらしき挙動をするところがあって、あと少しのところで諦めました。(情報があんまりないということは、たぶん、使いづらいもしくは使えないのかな、と)

ダイアログボックスで代替

結局、スクリプトで情報パレットのポップアップアイテムを制御するのは諦め、必要に応じてシンボルを選択するダイアログボックスを立ち上げるように方向転換しました。(壁のスタイルを置き換える操作を参考に)

以下はそのコードと設定。

import vs

bt=vs.Pbt
sym3d = vs.PSym3d
sym2d = sym3d + '-2D'
symz = vs.PSet_z
folder = vs.Pfolder
offset=int(vs.POffset)

#2d locus

p = [(60,-52.5),(40,-52.5),(-60,-52.5),(-40,-52.5)]

for pt in p:
   vs.Locus(pt)
sym = vs.LNewObj()
rhh=vs.GetParent(sym)

#3d symbol

p = (0,-65)
vs.Symbol(sym3d, p, 0)
sym = vs.LNewObj()
vs.Move3DObj(sym, 0, 0, symz )

#2d symbol

p=(0,-65-250*offset)
vs.Symbol(sym2d, p, 0)
sym = vs.LNewObj()
cName = vs.GetClass(sym)


#Rcord


vs.SetRecord(rhh, 'setubi')
vs.SetRField(rhh, 'setubi', 'name', sym3d)
vs.SetRField(rhh, 'setubi', 'type', cName)


#write Z

if vs.PWrite_z :
   ztx = 'H='+str(symz)
   vs.MoveTo(110,-215-250*offset )
   vs.TextSize(7)
   vs.CreateText(ztx)

#pop-up
def DialogHandler(item , data):
		if item==1:#OK
			(outSelectedIndex, outSelectedChoiceText) = vs.GetSelectedChoiceInfo(dialogID, 3, 0)
			(BOOLEAN, objName,objHd,recHd,wallHd)=vs.GetCustomObjectInfo()
			vs.SetRField(objHd,vs.GetName(recHd),'Sym3d',outSelectedChoiceText)
			vs.SetRField(objHd,vs.GetName(recHd),'bt', 'このまま')
			vs.Move3DObj(rhh, 0, 0, 0)
		if item==2:#END
			pass
		if item==12255:#setup
			symid =vs.FInFolder(vs.GetObject(folder))	
			aa=0
			while vs.GetTypeN(symid) ==16:
				symname=vs.GetName(symid)
				symid=vs.NextSymDef(symid)
				vs.AddChoice(dialogID, 3, symname, 0)
				aa+=1
	
if bt=='選択する':
	dialogID = vs.CreateLayout( 'シンボルを選択', False, 'OK', 'キャンセル' )
	vs.CreatePullDownMenu(dialogID, 3,30)
			
	vs.SetFirstLayoutItem( dialogID, 3 )
	result=vs.RunLayoutDialog( dialogID, DialogHandler )
画像1

ツール→プラグイン→プラグインマネージャ で一点型オブジェクトのカスタムプラグイン作成。

スクリプト言語をPythonスクリプトにして上記コードを入力。

画像2

パラメータは上画像のように設定。シンボル選択のポップアップで'選択する'を選択した時にダイアログボックスが立ち上がるようにしています。

画像3

シンボル変換時に再描画するフックとしてオブジェクトを同位置に移動させているので、「移動すると、コマンドを再実行する」にチェック。

画像4

パラメータで3Dシンボル格納フォルダに指定しているフォルダの中に、関連する3Dシンボルを格納しておきます。

画像5

それぞれの3Dシンボルに対応する2Dシンボルを、3Dシンボルの名前に「-2D」を追加した名前で、別の場所に作成しておきます。

これで、動くはずです。

画像6

シンボル選択のポップアップもうまく表示されました。

コードの説明

以下、コードの説明。(見様見真似でしてるので、変なところもあるかもしれませんがご了承ください。)

import vs

bt=vs.Pbt
sym3d = vs.PSym3d
sym2d = sym3d + '-2D'
symz = vs.PSet_z
folder = vs.Pfolder
offset=int(vs.POffset)

最初にpythonにvectorscriptのライブラリをインポートしてます。これで、vectorscriptの関数等は頭に'vs.'を追加することで使えるようになります。

また、オブジェクトのパラメータで設定したものはパラメータ名の頭に'vs.P'を追加することで呼び出せるので呼び出しておきます。

#2d locus

p = [(60,-52.5),(40,-52.5),(-60,-52.5),(-40,-52.5)]

for pt in p:
   vs.Locus(pt)

配置ガイド用の2D基準点を描画してます。(105角の柱や間柱に、コンセントボックスをつけた時の柱等の角の位置)

この辺はマリオネットよりpythonの方がだいぶ楽。(もっと省略化した記述もできそう。)

sym = vs.LNewObj()
rhh=vs.GetParent(sym)

後々のために、最後に描画したシンボルのハンドルを取得し、その親、つまりこのオブジェクトのハンドルを取得してます。

#3d symbol

p = (0,-65)
vs.Symbol(sym3d, p, 0)
sym = vs.LNewObj()
vs.Move3DObj(sym, 0, 0, symz )

3Dシンボル名(sym3d)でシンボル作成。位置は壁厚130の壁芯を原点として指定していますが、試行錯誤中にこうなっただけで特に理由はないです。

その後、シンボルを指定した高さに調整。

#2d symbol

p=(0,-65-250*offset)
vs.Symbol(sym2d, p, 0)
sym = vs.LNewObj()
cName = vs.GetClass(sym)

2Dシンボル名(sym2d=3Dシンボル名+'-2d')でシンボル作成。位置はオフセット値*250ずらしてます。(オフセットはポップアップで0,1,2,3,4,5,6から選択)

オブジェクト内に3Dオブジェクトと2Dオブジェクトを両方描画すると自動的にハイブリットシンボル(2D属性と3D属性を併せもったシンボル)として扱われるようです。

2Dシンボルはそのオプションで、照明や機械設備等、設備の種類に応じてクラスを割り当てていますが、そのクラスを取得しておきます。

#Rcord


vs.SetRecord(rhh, 'setubi')
vs.SetRField(rhh, 'setubi', 'name', sym3d)
vs.SetRField(rhh, 'setubi', 'type', cName)

オブジェクトのレコードフォーマット'setubi'にシンボル名と先ほど取得したクラス名を入れています。これは、後でワークシートなどで種類ごとに集計できるようにするためです。

レコードフォーマットはプラグインオブジェクト本体に設定するので先に取得していた親ハンドルを使ってます。

#write Z

if vs.PWrite_z :
   ztx = 'H='+str(symz)
   vs.MoveTo(110,-215-250*offset )
   vs.TextSize(7)
   vs.CreateText(ztx)

'高さ表記'にチェックしている(vs.PWrite_z==True)場合に高さを描画するようにしています。(オフセット値に応じて位置調整)

#pop-up
def DialogHandler(item , data):
		if item==1:#OK
			(outSelectedIndex, outSelectedChoiceText) = vs.GetSelectedChoiceInfo(dialogID, 3, 0)
			(BOOLEAN, objName,objHd,recHd,wallHd)=vs.GetCustomObjectInfo()
			vs.SetRField(objHd,vs.GetName(recHd),'Sym3d',outSelectedChoiceText)
			vs.SetRField(objHd,vs.GetName(recHd),'bt', 'このまま')
			vs.Move3DObj(rhh, 0, 0, 0)
		if item==2:#END
			pass
		if item==12255:#setup
			symid =vs.FInFolder(vs.GetObject(folder))	
			aa=0
			while vs.GetTypeN(symid) ==16:
				symname=vs.GetName(symid)
				symid=vs.NextSymDef(symid)
				vs.AddChoice(dialogID, 3, symname, 0)
				aa+=1
	
if bt=='選択する':
	dialogID = vs.CreateLayout( 'シンボルを選択', False, 'OK', 'キャンセル' )
	vs.CreatePullDownMenu(dialogID, 3,30)
			
	vs.SetFirstLayoutItem( dialogID, 3 )
	result=vs.RunLayoutDialog( dialogID, DialogHandler )

最後に、シンボルを選択で'選択する'を選んだ時にダイアログボックスを呼び出しています。

関数の定義は飛ばして、先に'if bt=='選択する':'以降を説明します。

	dialogID = vs.CreateLayout( 'シンボルを選択', False, 'OK', 'キャンセル' )
	vs.CreatePullDownMenu(dialogID, 3,30)
			
	vs.SetFirstLayoutItem( dialogID, 3 )
	result=vs.RunLayoutDialog( dialogID, DialogHandler )

最初にダイアログのレイアウトを作成し、ダイアログ番号を取得します。

次に、プルダウンメニューを作成。ItemIDを3に設定しています。(たぶん1と2はOKとキャンセルに割り当てられてるので使わないほうが良さそう。たぶん)

その後、作成したアイテムを有効化し、実行のために関数を呼び出します。

以後関数について

def DialogHandler(item , data):
		if item==1:#OK
			(outSelectedIndex, outSelectedChoiceText) = vs.GetSelectedChoiceInfo(dialogID, 3, 0)
			(BOOLEAN, objName,objHd,recHd,wallHd)=vs.GetCustomObjectInfo()
			vs.SetRField(objHd,vs.GetName(recHd),'Sym3d',outSelectedChoiceText)
			vs.SetRField(objHd,vs.GetName(recHd),'bt', 'このまま')
			vs.Move3DObj(rhh, 0, 0, 0)

ダイアログボックスへの操作内容に応じて対応を記述します。

item==1はOKボタンが押された場合です。
まず、ポップアップで選択されている内容を取得。

その後、オブジェクトのパラメータを変更したいのですが、変数を変えても反映されないようで、レコードフィールド値を直接変更しないといけないようです。
そのためにオブジェクトとレコードのハンドル取得し、直接フィールド値を指定しています。(この際、パラメータ名に'vs.P'はつけなくてもよい)
まず、シンボル名をポップアップで選択した内容に置き換え、’シンボルを選択'を'このまま'に戻しています。

ただ、フィールド値のシンボル名を変更しても画面はそのままなので、最後に同位置(0,0,0)に移動して、コマンドを再実行させています。(移動したら再実行するように設定しておく必要あり。)

		if item==2:#END
			pass

次、item==2はキャンセルボタンが押された場合です。これは何もしなくても勝手にキャンセルされるので'pass'してます。

		if item==12255:#setup
			symid =vs.FInFolder(vs.GetObject(folder))	
			aa=0
			while vs.GetTypeN(symid) ==16:
				symname=vs.GetName(symid)
				symid=vs.NextSymDef(symid)
				vs.AddChoice(dialogID, 3, symname, 0)
				aa+=1

次ですが、ポップアップの内容を指定する方法が分からず、かなり苦労しました。

ここをよく見たら書いていますが、最初は意味がわからなかったです。

また、初めてダイアログが開かれたときにはSetupDialogC、ダイアログが閉じられる直前に、SetdownDialogCが必ず返ります。

初めてダイアログが開かれたときに、受け取ったitemの値がSetupDialogC=12255になるようです。(SetupDialogCがなんなのか、かなりの間分からずにいました。)

で、その12255を受け取った時にポップアップのアイテムを追加すればうまくいきました。

フォルダの中のシンボル名を列記する方法は、以前見つけていたマリオネットのノードを見ることで分かりました。
まず、フォルダの中の最初のアイテムのIDを取得し、アイテムのタイプがシンボル(==16)である限り、次のアイテム、次のアイテムと繰り返しながらシンボル名を取得し、それをポップアップアイテムに追加してます。

最初に見つけたサンプルがInsertChoice(廃止されてるよう)を使ってたこともあって、ここまで行き着くのがもうめちゃめちゃ大変でした。

やってみて

最初の

・別のファイルで作成するときに、既に使っているファイルからオブジェクトをコピペして持ってこないといけない。
・オブジェクトをたくさん複製した時に、(共通しているはずの)マリオネットのコード自体も無駄に複製されるのが気持ちが悪かった。
・pythonの練習をしてみたかった。

というのはある程度解消されたように思います。

マリオネットオブジェクトからプラグインオブジェクトにすることで、ポップアップメニューから選択するという1操作が、シンボル選択を選択→ポップアップメニューから選択→OKをクリックの3操作に増えたけどまー許容範囲かな。

vectorscriptからpythonに変えたのも結構メリットがあったと思います。
同じ内容を書き換えるのはそんなに難しくはないけれども、変数等をいちいち定義しなくていい、マリオネットのノードを参考にできる、基本的な文法の情報は入手が簡単なのでそこで迷う必要がない、などが大きかった。

vs関数の使い方や挙動がめちゃめちゃ試行錯誤しないと理解出来ないので、とにかく、参考になるサンプルが大量にあるサイトとかを知りたいですね。(この記事くらい説明があればなお良い)

もし、プラグインやシンボルのデータが欲しい方がいれば対応します。(途中から有料記事を試してみようかとも思いましたが、ニーズなさそうだし、こういうのはオープンにするのがお互い様かな、と思いやめました。)

同じ要領で照明の配置もプラグインオブジェクト化してみてます。(これまでペンダント照明などの吊り下げコードの長さ調整がけっこうな手間だったので、パラメータで設定できるようにしてみた。)今日はアップする余力がないのでまた。

-----------------------------------------------

余談ですが、、スクリプトで情報パレットのポップアップアイテムを制御しようとしてた途中のコードも置いておきます。

vs.CreateText('test')
folder='設備配置用3D'
#result = vs.SetObjPropVS(8, True)
#result = vs.SetObjPropVS(12, True)
#(BOOLEAN, outWidgetID)=vs.vsoPrmName2WidgetID( 'a','aa')
#vs.vsoWidgetSetText(outWidgetID, 'bbb')
#vs.CreateText(outWidgetID)

#オブジェクト情報ウィジェットセット

(result, objectName, objectHand, recordHand, wallHand) = vs.GetCustomObjectInfo()
(theEvent, message )=vs.vsoGetEventInfo()

if theEvent==3:
	result = vs.SetObjPropVS(8, True)
	result = vs.SetObjPropVS(12, True)
	result = vs.vsoInsertAllParams
	#result = vs.vsoInsertWidget(0, 4, 100, '種類-', 0)
	#vs.vsoWidgetSetVisible(100, False)
	result = vs.vsoInsertWidget(1, 4, 101, '3D格納フォルダ',0)
	result = vs.vsoInsertWidget(2, 8, 102, '種類', 3)
	result = vs.vsoInsertWidget(3, 1, 103, 'コード長', 2)
	result = vs.vsoInsertWidget(4, 18, 104, 'コードクラス', 3)
	result = vs.vsoInsertWidget(5, 18, 105, '2Dクラス', 4)
	
	vs.CreateText(result)
	#vs.vsoWidgetPopupClear(102)
	
	symid =vs.FInFolder(vs.GetObject(folder))	
	aa=0
	while vs.GetTypeN(symid) ==16:
		symname=vs.GetName(symid)
		symid=vs.NextSymDef(symid)
		vs.vsoWidgetPopupAdd(102, aa, symname)
		aa+=1

'''
if theEvent==41:
	vs.vsoWidgetSetEnable(100,True)
	vs.vsoWidgetSetEnable(101,True)
	#vs.vsoSetEventResult(-8)
'''
cc=vs.vsoWidgetGetRecParam(102)
vs.CreateText(cc)

ここまでいくのもかなり大変だったのですが、結局、

・結果の値を取得する方法がわからなかった。(致命的)
・情報パレットの表示がずれたり、同じものがいくつも表示されたり、popアップのID等の番号や順番によっては上手く作動せずに原因が分からなかったり、と原因不明挙動が多数あった。

の2点で諦めました。(見つけたサンプルはかなり古くて今のバージョンとの対応も分からず、他に参考になるものを見つけられなくて何をしてるのか理解できず・・・)
上手く行けばこっちの方が操作は楽(つくるのは面倒だけど)になるので、こうすればいい、というのが分かる方がいれば是非教えて下さい。


プラグインとサンプルファイルはここに置いておきます。



この記事が気に入ったらサポートをしてみませんか?