On this page |
Pythonビューアハンドル
概要 ¶
Houdiniでは、拡張機能を使ってPythonでビューアハンドルを実装することで、Pythonステートのワークフローをカスタマイズすることができます:
-
Drawable Gadgetsを使用してハンドルコンポーネントを描画、ピック、ハイライトすることができます。
-
Handle Draggersを使用してマウス制御を拡張することができます。
-
Houdiniパラメータを使用してパラメータを定義し、特定のパラメータをPythonステートにエクスポートするように選択することができます。
-
Geometry Drawablesを使用してガイドジオメトリを作成することができます。
-
ローレベルでマウス、キーボード、タブレットのイベントに応答することができます。
-
ユーザによるパラメータ設定の操作に応答することができます。
-
独自のコンテキストメニューを表示することができます。
-
様々なコンテキストレベルで実行することができます。
-
変更操作をUndoブロックにまとめることができます。
Viewer Handle Code Generatorを使用することで、様々なサンプルソースからソースコードを作成することができます。 Pythonハンドルソースコードを配置してHoudiniに登録すると、Pythonステートは、ハンドルのバインドを介して一般的な方法でそれらのコードを使用することができます。
Houdiniにビューアハンドルをインストールする方法 ¶
Pythonハンドルは、Pythonファイルモジュールを使ってHoudiniにコードを読み込んだり、HDAに埋め込むことができます。 どちらの方法でも、Pythonステートの登録に登録コールバックを使用したのと同様に、登録コールバックを介してPythonハンドルをHoudiniにインストールする必要があります。
createViewerHandleTemplate
という名前のコールバックでは、Pythonハンドルの内容(ハンドルのタイプ名、パラメータ、ガジェットなど)を記述します。
Houdiniは、PythonステートでPythonハンドルが必要になった時に、この情報を使用してそのPythonハンドルのインスタンスを生成します。
Pythonハンドルの内容を記述する方法の詳細は、hou.ViewerHandleTemplateを参照してください。
Viewer Handle Code Generatorを使用して、登録関数と共にハンドルスケルトンコードを生成します。 このViewer Handle Code Generatorは、Viewer Handle Browserウィンドウ、または、HDAに埋め込まれた ビューアハンドル のHandle Scriptタブから利用可能です。
もちろん自分自身でゼロからコードを作成することもできますが、Viewer Handle Code Generatorを使用した方が、構文エラーなしですぐにPythonハンドルを作成して実行することができます。
この登録処理は、起動時またはhou.ui.registerViewerHandleやhou.ui.reloadViewerHandleなどの登録APIを使用した時に行なわれます。
典型的な登録コールバックは以下のようになります:
def createViewerHandleTemplate(): """ このエントリーポイントは、ビューアハンドルをHoudiniに登録するのに必須です。 """ handle_type = 'viewer_handle_intro1' handle_label = 'Viewer Handle Intro1' handle_cat = [hou.sopNodeTypeCategory()] template = hou.ViewerHandleTemplate(handle_type, handle_label, handle_cat) template.bindFactory(Handle) # 他のバインドをここで実装します。 return template
Note
PythonハンドルとPythonステートは、Houdini起動時にほぼ同時にインストールされますが、これら2つにおける登録の順番は重要ではありません。 Houdiniでは、PythonステートがPythonハンドルをバインドできるようにそのPythonハンドルが先にインストールされている必要はありません。 この登録の過程では、PythonハンドルパラメータとPythonステートノードパラメータ間のバインド情報のみが収集されます。 これらのバインドは、後でPythonステートが実行された時にHoudiniによって使用され、この登録の過程では、適切な検証が行なわれ、問題があればバインドエラーが起きます。
アセット内にハンドルを埋め込む方法 ¶
以下の手順は、Python ビューアハンドル を埋め込むためのSOPアセットの作成方法について説明しています。
-
Objectレベルで、⇥ TabメニューからGeoオブジェクトを作成します。
-
その
geo1
ノードをダブルクリックして、ジオメトリネットワークの中に入ります。 -
⇥ TabメニューからSubnetworkノードを作成します。
-
その
subnet1
ノードを右クリックして、 Digital Asset → Create New を選択します。 -
Type Name を
handle_demo
、 Asset Label をHandle Demo
、 Library Path をEmbedded in .hip File
に設定します。ライブラリの保存先を
Embedded
に設定すると、そのアセットはアセットライブラリではなく現行シーンファイル内に保存されます。 -
Author 、 Branch 、 Version のチェックボックスのチェックを外します。
-
そのアセットのType Propertiesウィンドウが開きます(そのアセットタイプのインスタンスを右クリックして Type Properties を選択)。
-
Interactive|Handle Script タブをクリックします。
-
ハンドルコードを生成するために New… ボタンをクリックします。
-
Samples 項目で Rotate を選択して、 Accept をクリックします。
-
Apply ボタンをクリックして、その新しいハンドルを登録します。
このHandle Scriptエディターには、ジオメトリを軸回転させることができるPythonハンドルのコードが表示されているはずです。
この新しいハンドルは、Viewer Handle Browserペインのツリー内のHandle Demo
ノード下にリストされているはずです。
この新しいハンドルをテストするには、そのハンドルとバインドさせるビューアステートを作成する必要があります。
-
Interactive|State Script タブをクリックします。
-
ステートコードを生成するために New… ボタンをクリックします。
-
Name フィールドには、 ステートの名前 として
state_rotate_demo
と入力します。 -
Samples 項目で Static Handle を選択して、 Accept をクリックします。
-
以下の行を
HANDLE_TYPENAME = "unknown"
以下の内容に置換します。
HANDLE_TYPENAME = "handle_demo"
-
以下の行を
template.bindHandleStatic( HANDLE_TYPENAME, HANDLE_NAME, [] )
以下の内容に置換します。
template.bindHandleStatic( HANDLE_TYPENAME, HANDLE_NAME, [("ry","ry")] )
-
アセット内に
ry
という名前のfloatパラメータを追加します。 -
Accept をクリックします。
この新しいstate_rotate_demo
ステートは、Viewer State Browserペインのツリー内にリストされているはずです。
このstate_rotate_demo
ノードを展開すると、その新しいhandle_demo
エントリーが見つかります。
それでは、geo1
オブジェクト内にBox SOPを作成してディスプレイフラグを有効にし、そのBoxボックスのRotate Y
フィールドにch("../subnet1/ry")
を追加してください。
ネットワークエディタ内でHandle demo
アセットノードを選択します。マウスをシーンビューア内に移動させてEnterを押します。すると、ハンドルが緑色のリングで表示され、そのリングをドラッグすると、ボックスが回転します。
Houdiniパスからハンドルを読み込む方法 ¶
以下の手順では、Houdini起動時に自動的に登録されるPythonハンドルモジュールの作成方法について説明しています。
-
New Pane Tab Type ▸ Inspectors ▸ Viewer Handle Browser メニューからViewer Handle Browserウィンドウを開きます。
-
ブラウザのツールバーのリストメニューから
Sop
カテゴリを選択します。 -
File ▸ New Handle… メニューでViewer Handle Code Generatorを開きます。
-
ハンドルの名前 として Name フィールドに
handle_demo
と入力します。 -
Samples オプションで
Gadget
を選択して、 Accept をクリックします。
サンプルコードを含んだ新しいPythonハンドルファイルが$HOUDINI_USER_PREF_DIR/viewer_handlers/handle_demo.py
として保存され、Viewer Handle BrowserツリーにHandle demo
としてリストされているはずです。
import hou import resourceutils as ru import viewerhandle.utils as hu #使用方法: 何も処理をせずハンドルガジェットを描画するだけの単純なハンドル。 GADGET_PIVOT = "pivot" SCALE = 250.0 class Handle(object): def __init__(self, **kwargs): self.__dict__.update(kwargs) # ハンドルのトランスフォーム操作をサポートするユーティリティクラス。 self.xform_aid = hu.TransformAid(self,kwargs) # ここでは、ガジェットをクラスアトリビュートとして保存します。 # ガジェットはself.handle_gadgets attributeを介していつでも利用可能なので、これは任意です。 color_options = ru.ColorOptions(self.scene_viewer) self.pivot = self.handle_gadgets[GADGET_PIVOT] self.pivot.setParams({"draw_color":color_options.colorFromName("HandlePivotColor")}) # ピボットガジェットにBoxジオメトリを割り当てます。 sops = hou.sopNodeTypeCategory() verb = sops.nodeVerb("box") verb.setParms({"type":1, "scale":0.1, "divrate":(2,2,2)}) pivot = hou.Geometry() verb.execute(pivot, []) self.pivot.setGeometry(pivot) self.pivot.show(True) def onDraw( self, kwargs ): draw_handle = kwargs["draw_handle"] # ピボットガジェットを描画します。 self.pivot.draw(draw_handle) def onDrawSetup(self, kwargs): """ 描画、ピック、ロケート(マウス下の検索)の操作をする前にコールされます。""" # カメラ位置に関係なくスケール係数でピボットガジェットをスケールします。 origin = hou.Vector3() scale = self.handle_context.scaleFactor(origin)*SCALE scales = [scale]*3 xform = self.xform_aid.updateTransform(s=scales) self.pivot.setTransform(xform) def createViewerHandleTemplate(): """ 登録するビューアハンドルテンプレートを作成して返すための必須エントリーポイント。""" handle_type = "handle_demo" handle_label = "Handle demo" handle_cat = [hou.sopNodeTypeCategory()] template = hou.ViewerHandleTemplate(handle_type, handle_label, handle_cat) template.bindFactory(Handle) template.bindIcon("MISC_python") # Drawable Gadgetをハンドルにバインドします。 # このガジェットは、基本的にはビューポート内でインタラクティブに操作可能なハンドルコンポーネントを定義する際に使用されるGeometry Drawablesです。 # # このガジェットは、マウス下の位置の特定やピックといった一般的なハンドル操作を実行する際にHoudiniで使用されます。 # # ガジェットインスタンスは、Houdiniで生成され、このデータメンバーを介してHandleオブジェクトで利用可能にします: # self.handle_gadgets: 名前でキーを設定可能なガジェットの辞書。 template.bindGadget( hou.drawableGeometryType.Face, GADGET_PIVOT ) return template
このハンドルをテストするために、以下の手順に従います:
-
Objectレベルで⇥ Tabメニューを使用してGeoオブジェクトを作成します。
-
その
geo1
ノードをダブルクリックしてGeometryネットワークの中に入ります。 -
⇥ Tabメニューを使用してSubnetworkノードを作成します。
-
その
subnet1
ノードを右クリックして Digital Asset → Create New を選択します。 -
Type Name を
state_handle_demo
、 Asset Label をState Handle Demo
、 Library Path をEmbedded in .hip File
に設定します。このライブラリの保存場所を
Embedded
に設定することで、アセットライブラリではなく現行シーンファイルにアセットが保存されます。 -
Author 、 Branch 、 Version のチェックボックスのチェックを外します。
-
このアセットのType Propertiesウィンドウが開きます(アセットタイプのインスタンスを右クリックして Type Properties を選択)。
-
Interactive|State Script タブをクリックします。
-
New… ボタンをクリックすると、Viewer State Code Generatorダイアログが開きます。
-
Samples オプションで Static Handle を選択して Accept をクリックします。
-
その新しいステートソースコードの中の HANDLE_TYPENAME に“handle_demo”に設定して Accept をクリックします。
-
state handle demo
ノードを選択して、ビューア内でEnterキーを押します。
これでPythonハンドルデモがアクティブになり、原点にピボットガジェットが表示されているはずです。
ハンドルを実装する方法 ¶
このセクションでは、Pythonハンドルの実装に関する詳細について説明しています。 Pythonハンドルをゼロから実装するのはエラーが起こりがちで難易度が高いです。 ここでは、Pythonハンドルの作成にViewer Handle Code Generatorを使用することを想定します。 Viewer Handle Code Generatorには、様々なサンプルが用意されていて、それらのサンプルから選択することでPythonハンドルのスケルトンコードを生成することができます。
Pythonハンドルの作成は、Pythonクラスの実装で構成します。次のセクションでは、それに対応しているクラスメソッドの概要について説明しています。
Note
Houdiniには、viewerhandle.utils
というPythonモジュールが用意されています。
このモジュールには、ビューアハンドルのインストールをサポートしたり、独自のハンドルの実装に役立つドキュメント化された様々なユーティリティ関数/クラスが含まれています。
このモジュールは、$HHP/viewerhandle
フォルダに配置されています。
Tip
$HHP
環境変数は、Houdiniのインストールディレクトリ内のPythonライブラリを含んだサブディレクトリ($HFS/houdini/pythonX.Ylibs/
)を指しています。
特定の機能の実装に関する詳細は、以下のページを参照してください:
Pythonビューアハンドル
イニシャライザー ¶
def __init__(self, **kwargs)
Pythonクラスの__init__
メソッドは、Pythonハンドルクラスを初期化するのに必須です。
Houdiniでは、このメソッドがkwargs
辞書を引数として受け取る必要があります。
このkwargs
辞書には、事前に以下のキーエントリーが含まれています:
handle_name
登録時に設定されるビューアハンドルのタイプ名。
handle_label
登録時に設定されるPythonハンドルのラベル。
handle_instance_name
Pythonハンドルをhou.ViewerStateTemplate.bindHandleまたはhou.ViewerStateTemplate.bindHandleStaticにバインドする時にPythonステートで指定するハンドルインスタンス名。
scene_viewer
Pythonハンドルが実行されているシーンビューアを表現したhou.SceneViewerのインスタンス。
handle_context
アクティブハンドルに関する様々なコンテキスト情報にアクセス可能なコンテキストオブジェクト。
handle_gadgets
hou.ViewerHandleTemplate.bindGadgetで定義されているgadgetオブジェクトの辞書。 ガジェットの名前を辞書キーとして使用することで、そのガジェットオブジェクトを照会することができます。
handle_parms
Pythonハンドルのパタメータ値と設定値を含んだ辞書。 この辞書には、hou.ViewerHandleTemplate.bindParametersとhou.ViewerHandleTemplate.bindSettingsで与えられた情報から収集されます。 パラメータ名または設定名を使用することで、その辞書の値を照会することができます。
Note
kwargs
の内容はアトリビュートとして__init__
内のクラスに格納することができます。
これによって、kwargs
を照会することなく、コード内で直接これらのエントリーにアクセスすることができます。
def __init__(self, **kwargs) self.__dict__.update(kwargs) ... def onActivate(self, kwargs): self.log("Handle parameters", self.handle_parms)
イベントハンドラー ¶
Pythonハンドルイベントに反応できるようにいくつかのハンドルがサポートされています。
これらのイベントハンドラーはどれも、そのイベント関連の特定の値を含んだ単一辞書引数(kwargs
)を使ってコールされます。
すべてのハンドラーに共通するkwargs
エントリーを以下に載せています:
handle_context
アクティブハンドルに関する様々な情報にアクセス可能なコンテキストオブジェクト。 このキャッシュは、クラスアトリビュートとしても利用可能です。
handle_parms
Pythonハンドルにバインドされたパラメータと設定を表現した名前を含んだ書き込み可能な辞書。
以下のテーブルでは、すべてのイベントハンドラーを(あれば)特定のkwargs
と併せてカテゴリ別に載せています。
UI ¶
すべてのUIイベントハンドラーに共通するkwargs
エントリーを以下に載せています:
ui_event
イベントに関する情報(例えばマウスイベントの場合、現行マウス座標やボタンがクリックされたかどうか)を持ったhou.ViewerEventインスタンスを含んでいます。
メソッド名 |
説明 |
---|---|
|
Pythonハンドルガジェットがロケートまたはピックされてドラッグされた時にコールされます。 マウスのハンドリングを参照してください。 |
|
ビューポート内の任意の場所からでマウスがドラッグされた時にコールされます。 間接的なマウスの制御を参照してください。 |
|
マウススクロールが発生した時にコールされます。
hou.UIEventDevice.mouseWheelは、スクロール方向に応じて |
|
キーが押された時にコールされます。 詳細は、キーボードデバイスの読み込みを参照してください。 |
|
キートランジションイベントが発生した時にコールされます。 詳細は、キーボードデバイスの読み込みを参照してください。 |
|
ユーザがコンテキストメニュー項目を選択した時にコールされます。 コンテキストメニューのハンドリングに関しては、Pythonステートのコンテキストメニューを参照してください。
Note このハンドラーは、 |
|
コンテキストメニューが開かれる前にコールされます。 コンテキストメニューの更新を参照してください。
|
|
ハンドルのパラメータまたは設定が変更された時にコールされます。 ハンドルパラメータのハンドリングを参照してください。
|
描画 ¶
メソッド名 |
説明 |
---|
以下のメソッドは、描画イベントが生成された時にコールされます。 ハンドルの描画を参照してください。
|
HoudiniがPythonハンドルの再描画を必要とした時にコールされます。このメソッドは以下の時にコールされます:
|
|
|
ライフサイクル ¶
メソッド名 |
説明 |
---|---|
|
ハンドルがアクティブに設定された時にコールされます。これは以下の時に起こります:
|
|
ハンドルが非アクティブに設定された時にコールされます。これは以下の時に起こります:
|
|
ハンドル設定がHoudiniに読み込まれて処理される準備が整った時にコールされます。
一つの設定が変更される度にコールされる |
Undo対応 ¶
Houdiniは、ハンドルやノードパラメータが変更された時に自動的にUndoスタック上にアイテムを生成します。 とはいえ、独自にUndo対応が必要な場合、hou.undosモジュールとhou.SceneViewerのUndoメソッドを使用することで、 Undo可能なオペレーションをUndoスタック上に1個のエントリーにまとめることができます。 詳細は、ステートのUndo対応を参照してください。
ユーティリティ ¶
Houdiniには、Pythonハンドルの実装に役立つ様々なユーティリティクラスが用意されています。
例えば、ColorOptions
やhou.SceneViewer.hudInfoといったユーティリティは、Houdiniの標準と整合性が合うように設計されており、
これらのユーティリティティを使用することを強く推奨します。
これらのユーティリティの使い方のサンプルは、Pythonハンドルのデモを参照してください。
-
viewerhandle.utils.TransformAid
:TransformAid
は、Pythonハンドルのトランスフォーム操作を制御します。updateTransform
やtoScreen
といったメソッドは、ビューポートがワールド空間に設定された時にハンドルのオブジェクトトランスフォームを補完します。 -
viewerhandle.utils.DebugAid
:Pythonハンドルのデバッグに役立つユーティリティクラス。
-
resourceutils.ColorOptions
:Houdiniカラーオプションにアクセスできるユーティリティクラス。 詳細は、Gadget Drawablesを参照してください。
-
resourceutils.DisplayGroup
:このクラスは、Drawablesをビューポート内にグループとして表示することができます。 例えば、Houdiniトランスフォームモードキーの
Y
と同様に循環形式でハンドルガジェットのグループの表示を切り替えたい場合にこれが役立ちます。 -
resourceutils.CursorLabel
:カーソル周辺に何かしらのテキストラベルを表示したい時にこのクラスを使用します。 これはPythonハンドルで非常に役立ち、このクラスの使い方の詳細は
move_tool_demo
のハンドルを参照してください。 -
resourceutils.DebugMenu
:Pythonハンドルコンテキストメニューにデバッグメニューエントリーを作成する際にHoudiniで使用されます。 実際にViewer Handle Code Generatorは、これを使用してハンドルのコンテキストメニューを作成しています。
サンプル ¶
Viewer Handle Code Generatorのサンプルだけでなく、サンプルシーンを使用してPythonハンドルを実験することができます。
以下のシーンは、viewer_handle_demo
パッケージと一緒に$HFS/package/viewer_handle_demo/scenes
下に配布されています。
以下のシーンを使用する前に、Viewer Handle Browserの File|Load Examples メニューをクリックしてそのパッケージをHoudiniに読み込んでください。
Demo Viewer Handle シェルフツールまたはメインメニューバー内の File|Open… メニューからサンプルシーンを読み込むことができます。
viewer_handle_intro.hip
シーンでは、Pythonハンドルの実装について紹介されており、そのシーンからPythonハンドルの基本機能を学ぶことができます。
move_tool_demo.hip
シーンでは、ジオメトリを移動/回転/スケールさせるPythonハンドルのもっと完璧で複雑な実装のサンプルが用意されています。
このPythonハンドルのサンプルは、 Viewer Handle Browser で利用可能なので、そこで調べてみてください。
ビューアハンドルの使い方 ¶
Pythonハンドルとビルトインハンドルの制御で使用するワークフローの違いはほとんどありません。 例えば、ビルトインハンドルだろうとPythonハンドルだろうとどちらを使用しても以下のUI機能をユーザは使用できるはずです:
-
Pythonハンドルを表示するボタン。
-
パラメータと設定にアクセスすることができるHandle Parameterダイアログ。
-
Pythonハンドルを持続型として有効化することができるPersistent Handle Editor。
-
Draggersのスナップオプションを設定するダイアログ。
-
Pythonハンドルのスケールを調整するカメラズーム。
-
キーフレームによるPythonハンドルパラメータのアニメーション。
Note
ただし、Handle Preferenceダイアログは、Houdiniビルトインハンドルの設定を制御するように設計されているので、Pythonハンドル向けに意図されていません。 これらの設定の一部は、良いタイミングで将来Pythonハンドル向けに露出される予定です。
ビューアハンドルを検査する方法 ¶
Houdiniは、登録済みのすべてのPythonハンドルをViewer Handle Browserウィンドウで表示することができます。 このブラウザはViewer State Browserと同様で、ほぼ同じ機能が用意されています:
-
登録済みのハンドルをブラウズするツリー。
-
メッセージをログに出すコンソール。
-
アクティブハンドルのクラスアトリビュートをダンプする様々なオペレーション。
-
Viewer Handle Code Generator。
-
Pythonハンドルの編集と読み込みに対応。
デバッグに関するTips ¶
Pythonハンドルで利用可能なデバッグ機能は、Pythonステートのデバッグ機能とほぼ同じです。 これらの機能について学習したいのであれば、Pythonステートのデバッグに関するTipsの章を参照してください。
DebugAid ¶
ユーティリティクラスのviewerhandle.utils.DebugAid
には、Pythonハンドルのデバッグサポートが用意されています。
このユーティリティは、Pythonハンドルブラウザでメッセージをログに出すのに便利ですが、さらにPythonハンドルブラウザで現在利用可能なすべてのログ機能をメソッドとして用意しています:
-
アクティブなPythonハンドルを検査します。
-
コンソールにマーカーを追加します。
-
デバッグトレースを有効化します。
-
ロギングコンソールを有効化します。
-
実行中のPythonハンドルをリロードします。
import traceback from viewerhandle.utils import DebugAid class Handle(object): def __init__(self, **kwargs): self.__dict__.update(kwargs) self.dbg = DebugAid(self) def onEnter(self, kwargs): # ハンドルを検査します。 self.dbg.marker() self.dbg.inspect() # トレースを開始します。 self.dbg.trace() def onMouseEvent(self, kwargs): # マウス位置をログに出します。 ui_event = kwargs["ui_event"] device = ui_event.device() self.dbg.marker() self.dbg.log("Mouse position x=", device.mouseX(), "y=", device.mouseY()) # 現在のPythonコールスタックをログに出します。 self.dbg.log(''.join(traceback.format_stack()))
ハンドルをリロードする方法 ¶
HoudiniからPythonハンドルモジュールをリロードできれば、Pythonハンドルの開発サイクルが向上します。 Pythonハンドルをリロードする方法がいくつか用意されています:
-
ソースコードがHoudiniのViewer Handle Editorで開かれていれば、
Accept
ボタンをクリックします。 -
Viewer Handle BrowserペインのFileメニューまたはブラウザツリー内から
Reload
コンテキストメニューを選択します。 -
ハンドルがHDAに埋め込まれている場合は、Python Handleエディタからリロードします。
Note
Pythonハンドルをリロードすると、そのハンドルのインスタンスが使用中であれば、実行中のPythonステートが自動的に抜けます。
デバッグコンテキストメニュー ¶
HOM API ¶
Pythonハンドル関連のHOM
APIのいくつかを以下に載せます。
Pythonビューアハンドル