この 2 つ目のチュートリアルでは、第 1 部で定義した Listview ActiveX コントロールをカスタマイズし、選択セットが変更されたときに表示が自動的に更新されるようにします。
ActiveX コントロールは、DotNet フレームワークとそのコントロールを考慮して、最新バージョンの Microsoft Windows オペレーティング システムで非推奨となりました。
MAXScript では引き続き ActiveX コントロールがサポートされますが、これらは MAXScript にアクセス可能なシステムにインストールおよび登録する必要があります。
ActiveX コントロールの代わりに、MAXScript では、3ds Max 9 以降において DotNet コントロールがサポートされます。
「ActiveX ListView コントロールの DotNet ListView コントロールへの変換」を参照してください
関連トピック:
MAXScript ロールアウト内の ActiveX コントロール
全体の流れ:
既存の macroScript を拡張します。
ロールアウトをグローバル変数として定義し、外部コードからアクセスできるようにします。
Listview のバックグラウンド カラーをきれいな薄いシアンに変更します。
Listview の最初の列のチェックボックスを使用可能にし、選択オブジェクトのレンダリング可能状態を表すために使用します。
layout_def 配列の各列に 2 つ目の値を指定することにより、列の幅を指定します。
新しいデータをリストに入力する前に、リストの項目をクリアします。
シーンの選択セットが変更された場合にリスト (グローバルにアクセスできるロールアウトのユーザ インタフェース要素として表示) を更新するコールバックを登録します。
エラー防止のため、ロールアウトを閉じるときにコールバックを登録解除します。
スクリプト:
macroScript SceneListView category: "HowTo" ( global listview_rollout try(destroyDialog listview_rollout)catch() rollou tlistview_rollout "ListView Selected" ( fn initListView lv = ( lv.gridLines = true lv.View = #lvwReport lv.fullRowSelect = true lv.backColor = color 225 215 210 lv.checkboxes = true layout_def = #(#("On",28), #("Object Name",120), #("Object Class",80), #("Verts",45), #("Faces",45), #("Material",120)) for i inlayout_def do ( column = lv.ColumnHeaders.add() column.text = i[1] ) LV_FIRST = 0x1000 LV_SETCOLUMNWIDTH = (LV_FIRST + 30) for i = 0 to layout_def.count-1 do windows.sendMessage lv.hwnd LV_SETCOLUMNWIDTH i layout_def[1+i][2] ) fn fillInSpreadSheet lv = ( lv.ListItems.clear() for o in selection do ( li = lv.ListItems.add() li.checked = o.Renderable sub_li = li.ListSubItems.add() sub_li.text = o.name sub_li = li.ListSubItems.add() sub_li.text = (classof o) as string sub_li = li.ListSubItems.add() sub_li.text =try((o.mesh.numverts) as string)catch("--") sub_li = li.ListSubItems.add() sub_li.text =try((o.mesh.numfaces) as string)catch("--") sub_li = li.ListSubItems.add() sub_li.text = (o.material) as string ) ) activeXControl lv_objects "MSComctlLib.ListViewCtrl" width:490 height:190 align:#center on listview_rollout open do ( initListView lv_objects fillInSpreadSheet lv_objects ) on listview_rollout close do callbacks.removeScripts #selectionSetChanged id:#SceneListView ) createDialog listview_rollout500 200 callbacks.addScript #selectionSetChanged "listview_rollout.fillInSpreadSheet listview_rollout.lv_objects" id:#SceneListView )
結果:
macroScript SceneListView category:"HowTo"
( global listview_rollout
macroScript のスコープ (この場合はコールバック スクリプト) の外からロールアウトをアクセスできるように、ロールアウト変数をグローバルとして定義します。
rollout listview_rollout "ListView Selected"
(
fn initListView lv =
(
lv.gridLines = true
lv.View = #lvwReport
lv.fullRowSelect = true
lv.backColor = color 225 215 210
Listview の backColor プロパティに新しいカラーを割り当てます。
Microsoft のカラー値は BGR(青色/緑/赤)であり、3ds Max および MAXScript の RGB とは異なります。MAXScript では、上記のカラーは薄いピンク色になります。これらのカラーはたとえば次のような方法で見ることができます。
display ( bitmap 128 128 color:(color 225 215 210) )--pale pink
display ( bitmap 128 128 color:(color 215 210 225) )--pale blue
lv.checkboxes = true
Listview に用意されているチェックボックスを使用可能にして、ノードレベルのレンダリング可能プロパティを表すために使用します。
layout_def = #(#("On",28), #("Object Name",120), #("Object Class",80), #("Verts",45), #("Faces",45), #("Material",120))
既存のレイアウト定義配列を拡張します。列の名前だけを格納するのではなく、列の名前と幅の両方のサブ配列を格納します。将来、太字のブール値フラグ、テキストのフォアグラウンド カラーなど、列ごとにデータを追加して格納することもできます。
for i in layout_def do
(
column = lv.ColumnHeaders.add()
column.text = i[1]
)
LV_FIRST = 0x1000
LV_SETCOLUMNWIDTH = (LV_FIRST + 30)
ユーザ変数を 2 つ定義します。1 つには最初の列のアドレスが含まれ、もう 1 つには列幅の設定を行う Listview の Windows ハンドルに送るメッセージ id が含まれます。ここは「難解な部分」であり、一般的な Microsoft Windows および ActiveX コントロールのプログラミングに関する文献に説明があります。ここでは、単純にここに示されているとおりに使用してください。
for i = 0 to layout_def.count-1 do
windows.sendMessage lv.hwnd LV_SETCOLUMNWIDTH i layout_def[1+i][2]
0 から列数マイナス 1 までループします。windows.sendMessage
メソッドでは列のインデックスが 0 から始まることを前提としていますが、MAXScript の配列のインデックスは 1 から始まるためです。
windows.sendMessage
メソッドには、ActiveX コントロールの Windows ハンドル (lv.hwnd)、送信するメッセージ、幅を設定する列のインデックス、および幅の値を指定します。 幅の値は、インデックスが 1 から始まる配列のインデックス(i+1)のサブ配列の 2 つ目の項目に格納されています。
)
fn fillInSpreadSheet lv =
( lv.ListItems.clear()
Listview にデータを入力する前に、必ずリストを空にします。ここでは、ダイアログ ボックスを閉じてから再び開くのではなく、瞬時にリストが更新されるようになっているためです。
for o in selection do
(
li = lv.ListItems.add() li.checked = o.Renderable
最初の列にはチェックボックスだけが入っています。現在のオブジェクトのノードレベルの .renderable
プロパティから返されたブール値を、.checked プロパティに設定します。
sub_li = li.ListSubItems.add()
以前は最初の列であったオブジェクト名の列が 2 番目の列になったため、サブリスト項目を作成します。
sub_li.text = o.name
サブリスト項目の .text プロパティに、前と同じようにオブジェクトの名前を設定します。
sub_li = li.ListSubItems.add()
sub_li.text = (classof o) as string
sub_li = li.ListSubItems.add()
sub_li.text = try((o.mesh.numverts) as string)catch("--")
sub_li = li.ListSubItems.add()
sub_li.text = try((o.mesh.numfaces) as string)catch("--")
sub_li = li.ListSubItems.add()
sub_li.text = (o.material) as string
)
)
activeXControl lv_objects "MSComctlLib.ListViewCtrl" width:490 height:190 align:#center
on listview_rollout open do
(
initListView lv_objects
fillInSpreadSheet lv_objects
)
on listview_rollout close do callbacks.removeScripts #selectionSetChanged id:#SceneListView
タイトルバーの右上隅の[X]ボタンを押す、または MacroScript を再び呼び出して、ダイアログ ボックスを閉じるときには、選択セットの変更に合わせて表示を更新するために登録するコールバックを削除します。そうしないと、シーン内のオブジェクトを選択したときにコールバックがすでに閉じたロールアウトにアクセスを試みてしまい、エラーが発生することになります。
)
try(destroyDialog listview_rollout)catch()
createDialoglistview_rollout 500 200 callbacks.addScript #selectionSetChanged "listview_rollout.fillInSpreadSheet listview_rollout.lv_objects" id:#SceneListView
最後に、ユーザがシーン内の選択セットを変更した場合に Listview を更新するコールバックを登録します。
callbacks.addScript
は、新しいコールバックを登録することを MAXScript に通知します。
#selectionSetChanged
は、選択セットに変更があった場合に 3ds Max がブロードキャストする通知メッセージの名前です。
この文字列にはコールバックが起動したときに実際に実行されるスクリプトが含まれており、グローバルになったロールアウト定義のプロパティとして Listview にアクセスし、先に定義した fillInSpreadSheet
関数を呼び出します。
id:#SceneListView
はこの特別なコールバック(たとえば上記の callbacks.removeScripts
の呼び出し)だけに影響を与えることのできるユーザ定義の名前です。他の開発者が定義したコールバックや 3ds Max の出荷版には影響しません。
)
スクリプトを評価すると、チュートリアルの第 1 部で定義した[HowTo]カテゴリの SceneListView ActionItem が更新されます。
SceneListView スクリプトに対応するボタンを押すか、メニュー項目を選択するか、キーボード ショートカットを押すと、Listview ActiveX コントロールのダイアログ ボックスが表示されます。選択されたオブジェクトがない場合、リストは空になります。
オブジェクトをいくつか選択し、リストが自動的に更新される様子を確認してみます。選択セットを再び変更すると、リストは動的に更新されます。オブジェクトを選択してから右クリックして[プロパティ] (Properties)を選択し、レンダリング可能チェックボックスのチェックマークを外します。Listview のチェックボックスのチェックマークも外されることに注意してください。
次に行うべき手順は、Listview 内のチェックボックスを .Renderable
プロパティに双方向的にリンクするためのイベント ハンドラを追加し、Listview での変更がノード プロパティにも反映されるようにすることです。
そのほか、レイアウト配列に列定義をさらに追加して他のオブジェクト プロパティも表示できるようにすることなどがあります。