On this page |
|
Pythonビューアステート
概要
Viewer Stateは、ビューポート内でのインタラクティブな操作を制御することができます。
例えば、 RotateツールはViewer Stateです。
Handlesツールは、現行ノードに関係したViewer Stateにアクセスすることができます。
Houdiniは、Pythonで独自のViewer Stateを作成して登録することができます。
独自のViewer Stateの例:
-
ディスプレイハンドルは、ユーザ側でハンドルを制御して、パラメータ/設定を変更することができます。
-
ジオメトリとの対話操作は、サーフェス上で描画したり、マウスカーソル下の情報を表示させることができます。
-
ガイド表示は、ジオメトリをガイドとして表示することができます。
-
ステートパラメータを使ってユーザ操作に反応せることができます。
-
ユーザにジオメトリを選択させることができます。
-
独自の右クリックメニューを表示することができます。
-
ローレベルでマウス、キーボード、タブレットのイベントに反応させることができます。
-
Undoブロックで変更内容を制御することができます。
-
複数のコンテキストレベルで実行することができます。
Houdiniデジタルアセットには、Viewer Stateを指定することができます(例えば、そのタイプのノードが現行になった時に Handlesツールを使用させることができます)。
結局のところ、(Inspectionツールのようにノードを作成しないツールの)シェルフツールスクリプトでは、hou.SceneViewer.setCurrentState()をコールすることで、プログラム的にステートを変更することができます。
コードを今すぐにでも試したいのであれば、以下のViewer Stateの実装方法を参照してください。そこでは、最低限のViewer Stateの実装、登録、実行の方法について説明しています。
Tip
Viewer Stateの動作を見たいのであれば、$HH/viewer_states/examples
下にあるデモシーンとアセットファイルをチェックしてください。
これらのサンプルは、Viewer Stateの機能の詳細が網羅されています。
制限事項
-
現在のところ、Pythonステートは、次のコンテキストで利用可能です: SOP (ジオメトリ), OBJ , DOP , LOP のレベル。
-
ハンドルをノードなしステートにバインドすることはできません。
ステート名
ステートは、 内部名 とUIで表示する人が解読可能な ラベル を持ちます。 新しく独自のステートを作成する場合、その内部名は固有でなければなりません。もし2人の作成者が同じステート名を使用してしまった場合、どちらかのステートは登録に失敗します。 あなたが作成したステートまたはアセットがいつの日か他のユーザ/スタジオで共有もしくは製品として販売することを考慮すれば、適切な固有名になるようにちゃんと時間をかけて決めてください。
Houdiniはアセット毎に自動的に汎用ステートを作成するということも相まって、既存のノードタイプの名前をステート名として使用することはできません。
-
ステートを2つ以上のノード間で共有したい場合やノードに関連付けたくない場合、必ずどのアセットの汎用ステートとも干渉しないようなステート名を付けなければなりません。この一番良い方法は、アセットで使用しているのと同じネームスペースを組み込むことです。
例えば、あなたがExample.com映画スタジオで働いていて、アセットのネームスペースの接頭辞に
examplecom::
を使用していると仮定すると、"scrub"ステートを作成する時は、そのステート名をexamplecom::scrub
にしてください。 -
ステート名には、ノードタイプ名やノード名と同じ文字制限がありません。ステート名は多かれ少なかれ任意の文字列にすることができます。
-
ファイル/ディレクトリ名として名前を使用する必要が出てくる場合があります(例えば、コードをファイルに保存する場合)。
ツール + ステート vs. 自己完結型ステート
ほとんどじゃなくても多くの"ネイティブ"のHoudiniノードステートは自身の選択を制御することもノードを作成することもしません。 代わりに、それらのノードステートは、選択の促しとノード作成をシェルフツールに頼っています。 それらのノードステートは単にハンドルの表示を担っているだけです。
独自のPythonステートを使ってこのワークフローを使用することもできます。特に、そのステートがアセットに密接に関係している時です。 選択を促してノードを作成する(そして、その選択をノードの Group フィールドに設定する)シェルフツールスクリプトを書くことができます。
別の方法としては、ステートを自己完結型にすることができます。 つまり、ノードをビューアから呼び出した時にそのノードを作成して、そこに独自のセレクターを持たせることができます。
(ステートにノードが不要な場合(例えば、検査タイプのツール)、その詳細はノードを使用しないステートの書き方を参照してください)
-
2つ以上のタイプの選択を要求する必要がある場合(例えば、"いくつかカーブを選択"した後に"それらのカーブ上のいくつかのポイントを選択する"場合)、ツールスクリプトを使用してください。現在のところ、Pythonステートは、1個のタイプの選択のみ受け入れることができます。
-
コードがノードを作成 する前に 何が選択されているのかを知る必要がある場合、ツールスクリプトを使用してください。
ステートでノードインスタンスを制御する詳細は、ノードの扱い方を参照してください。
Houdiniにステートをインストールする方法
HoudiniでViewerステートを作成してインストールする最も便利で簡単な方法は、Viewer State Code Generatorダイアログを使用することです。 このコードジェネレータは、即座に完全に機能するステートを用意し、ゼロからViewerステートを記述するのに必要なすべての詳細を理解するのに役立ちます。
現在のところ、Houdiniで利用可能なカスタムステートを作成する方法が2通りあります: アセットにステートのコードを埋め込む方法(HDA Viewerステート)とHoudiniパスの正しいディレクトリにそのコードを含んだPythonファイルを配置する方法です。
アセットにステートを埋め込む方法
HDA Viewerステートを作成するには、Operator Type PropertiesウィンドウのViewer State Editorタブで利用可能なコードジェネレータを使用します。
以下の手順は、アセットを作成し、そこに最低限のステートを試せるようにコードを記述します。
-
ステートを追加できるようにアセットの作成から始めます。サンプルをシンプルにするために、OBJアセットを使って始めましょう。
Note
空っぽのアセットから始めたい場合は、に従うことでSOPアセットを、に従うことでOBJアセットを作成することができます。 コードジェネレータで生成されたViewerステートは、どちらのアセットタイプでも動作します。
実験できるように"空っぽ"のSOPアセットを手軽に作成するには:
-
オブジェクトレベルで、⇥ Tabメニューを使って
Geoオブジェクトを作成します。
-
geo1
ノードをダブルクリックして、ジオメトリネットワークの中に入ります。 -
⇥ Tabメニューを使ってSubnetworkノードを作成します。
-
subnet1
ノードを右クリックして、 Create Digital Asset を選択します。 -
Operator Name を
statedemo
、 Operator Label をState Demo
、 Save to Library をEmbedded
に設定します。ライブラリの場所を
Embedded
に設定することで、アセットライブラリではなく、現行シーンファイルにアセットが保存されます。
実験できるように"空っぽ"のOBJアセットを手軽に作成するには:
-
オブジェクトレベルで、⇥ Tabメニューを使ってSubnetworkノードを作成します。
-
subnet1
ノードを右クリックして、 Create Digital Asset を選択します。 -
Operator Name を
statedemo
、 Operator Label をState Demo
、 Save to Library をEmbedded
に設定します。ライブラリの場所を
Embedded
に設定することで、アセットライブラリではなく、現行シーンファイルにアセットが保存されます。
-
-
そのアセットのType Propertiesウィンドウを開きます(アセットタイプのインスタンスを右クリックして、 Type Properties を選択します)。
-
Interactive|Viewer State タブをクリックします。
-
New… ボタンをクリックして、ステートコードを生成します。
-
onMouseEvent イベントハンドラーを選択して、 Accept をクリックします。
Viewer State Browserツリーにリストされた関数ステートがStatedemo
として表示されているはずです。
import hou import viewerstate.utils as su class State(object): def __init__(self, state_name, scene_viewer): self.state_name = state_name self.scene_viewer = scene_viewer def onMouseEvent(self, kwargs): """マウスイベントを処理します """ ui_event = kwargs["ui_event"] dev = ui_event.device() self.log("Mouse:", dev.mouseX(), dev.mouseY(), dev.isLeftButton()) # イベントを消費するにはTrueを返さなければなりません return False def createViewerStateTemplate(): """登録するViewerステートテンプレートを作成して返すのに必須となるエントリーポイント""" state_typename = kwargs["type"].definition().sections()["DefaultState"].contents() state_label = "Statedemo" state_cat = hou.objNodeTypeCategory() template = hou.ViewerStateTemplate(state_typename, state_label, state_cat) template.bindFactory(State) template.bindIcon(kwargs["type"].icon()) return template
ステートクラスに機能を追加する方法の詳細は、以下のステートを実装する方法を参照してください。
このステートをテストするために、ネットワークエディタ内でそのアセットを選択します。マウスをシーンビューア内に移動させて、Enterを押します。 マウスを動かすと、そのマウス座標のログがViewer State Browserコンソールに記録されているはずです。
Houdini Pathからステートを読み込む
File Viewerステートを作成するには、Viewer State Browserウィンドウのコードジェネレータを使用します。
以下の手順では、複数のアセット間で"共有"可能なFile Viewerステートを作成します。 File Viewerステートは、Houdiniを起動した時に自動的に登録されます。
-
New Pane Tab Type ▸ Viewer State Browser メニューからViewer State Browserウィンドウを開きます。
-
ツールバーからObjectカテゴリを選択します。
-
File ▸ New State… メニューからViewer State Code Generatorを開きます。
-
Name フィールドで、 ステートの名前 として
statedemo
と入力します。 -
onMouseEvent イベントハンドラーを選択して、 Accept をクリックします。
この新しいFile Viewerステートを$HOUDINI_USER_PREF_DIR/viewer_states/statedemo.py
として保存すると、Viewer State BrowserツリーでStatedemo
としてリストされます。
これで、Viewer State Browserツリーにリストされた関数ステートがStatedemo
として表示されているはずです。
import hou import viewerstate.utils as su class State(object): def __init__(self, state_name, scene_viewer): self.state_name = state_name self.scene_viewer = scene_viewer def onMouseEvent(self, kwargs): """マウスイベントを処理します """ ui_event = kwargs["ui_event"] dev = ui_event.device() self.log("Mouse:", dev.mouseX(), dev.mouseY(), dev.isLeftButton()) # イベントを消費するにはTrueを返さなければなりません return False def createViewerStateTemplate(): """登録するViewerステートテンプレートを作成して返すのに必須となるエントリーポイント""" state_typename = "statedemo" state_label = "Statedemo" state_cat = hou.objNodeTypeCategory() template = hou.ViewerStateTemplate(state_typename, state_label, state_cat) template.bindFactory(State) template.bindIcon("MISC_python") return template
このステートをテストするために、ステートを追加したいアセットを探すか、で空っぽのSOPアセットを作成するか、でOBJアセットを作成します。サンプルをシンプルにするために、OBJアセットを作成します。
実験できるように"空っぽ"のSOPアセットを手軽に作成するには:
-
オブジェクトレベルで、⇥ Tabメニューを使って
Geoオブジェクトを作成します。
-
geo1
ノードをダブルクリックして、ジオメトリネットワークの中に入ります。 -
⇥ Tabメニューを使ってSubnetworkノードを作成します。
-
subnet1
ノードを右クリックして、 Create Digital Asset を選択します。 -
Operator Name を
statedemo
、 Operator Label をState Demo
、 Save to Library をEmbedded
に設定します。ライブラリの場所を
Embedded
に設定することで、アセットライブラリではなく、現行シーンファイルにアセットが保存されます。
実験できるように"空っぽ"のOBJアセットを手軽に作成するには:
-
オブジェクトレベルで、⇥ Tabメニューを使ってSubnetworkノードを作成します。
-
subnet1
ノードを右クリックして、 Create Digital Asset を選択します。 -
Operator Name を
statedemo
、 Operator Label をState Demo
、 Save to Library をEmbedded
に設定します。ライブラリの場所を
Embedded
に設定することで、アセットライブラリではなく、現行シーンファイルにアセットが保存されます。
-
アセットのType Propertiesウィンドウを開きます(アセットタイプのインスタンスを右クリックして、 Type Properties を選択します)。
-
Node タブをクリックします。
-
Default State フィールドにステート名を設定します(上記のコードの例だと、これは
statedemo
です)。 -
Type Propertiesウィンドウの下部にある Accept をクリックします。
-
アセットがロックされていた場合、Houdiniは、変更を保存できるようにアセットのロックを解除するように促します。
-
-
このステートをテストするために、ネットワークエディタ内でそのアセットを選択します。マウスをシーンビューア内に移動させて、Enterを押します。 マウスを動かすと、そのマウス座標のログがViewer State Browserコンソールに記録されているはずです。
-
ビューアの上部にあるツールバーは、左側にこのステートのラベルを表示します。
-
Houdiniを起動すると、ステートテンプレートにアクセスして登録を実行するためのcreateViewerStateTemplate
がコールされます。
省略した場合、Houdiniは単にステート登録をスキップします。
ステートクラスに機能を追加する方法の詳細は、以下のステートを実装する方法を参照してください。
Tip
Houdiniセッション中でFile Viewerステートをリロードするには、Viewer State Browserツリーにリストされているステート名の上でして、コンテキストメニューから Reload を選択します。
これによって、Houdiniを再起動することなくステートへの変更をテストすることができます。
Pythonを使って、ステートの名前でhou.ui.reloadViewerState()をコールすることで、ステートをリロードすることもできます。
ステートを実装する方法
以下のセクションでは、ステートを実装するクラスに追加可能なハイレベルのメソッドの概要について載せています。
特定の機能の実装方法のガイドに関しては、以下のページを参照してください:
イニシャライザ(必須)
def __init__(self, state_name, scene_viewer):
state_name
このステートが登録されたステート名の文字列。
scene_viewer
ツールが作用しているシーンビューアを表現したhou.SceneViewerオブジェクト。 このオブジェクトには、ステートを実装する際に使用可能な便利なメソッドがたくさんあります。例えば、hou.SceneViewer.currentGeometrySelection()やhou.SceneViewer.setCurrentGeometrySelection()です。
一般的には、他のメソッドが引数を必要とする場合に、それらの引数をオブジェクトアトリビュートに格納したいことでしょう:
class MyState(object): def __init__(self, state_name, scene_viewer): self.state_name = state_name self.scene_viewer = scene_viewer # イベントハンドラー # ...
イベントハンドラー
イベントハンドラーは、単一引数、色々な便利アイテムを含んだ辞書を使ってコールします。 この辞書には、典型的には(必ずとは限りません)以下のアイテムが含まれています:
node
現行ステートで作用しているノードを表現したhou.Nodeインスタンスを含んでいます。
Menu items
メニューアイテム名をキーとして使用したメニューアイテム関連の現行値を含んでいます。
state_parms
現行ステートにバインドされたステートパラメータを表現した名前を含んでいます。 この辞書は、パラメータステートを変更する際に使用します。 詳細は、ここを参照してください。
state_flags
ステートに関連した色々なフラグを含んだ辞書。
ステートフラグは、kwargs
引数を介して、すべてのステートハンドラーで設定することができます。
フラグ |
メモ |
---|---|
|
(
あなたのステートではマウスドラッグイベントが不要な場合は、 class MyState(object): def __init__(self, state_name, scene_viewer): self.state_name = state_name self.scene_viewer = scene_viewer onEnter( self, kwargs): kwargs['state_flags']['mouse_drag'] = False ... |
|
このフラグは、 デフォルトでは、ビューポートは常に再描画します。大規模シーンのパフォーマンス問題を軽減したいのであれば、あなたのステートがでビューポートの再描画が不要な時には class MyState(object): def __init__(self, state_name, scene_viewer): self.state_name = state_name self.scene_viewer = scene_viewer onMouseEvent( self, kwargs): kwargs['state_flags']['redraw'] = False if __some_redraw_test__: kwargs['state_flags']['redraw'] = True ... |
以下のテーブルには、ステートクラスを定義可能なイベントハンドラーを載せています。
ライフサイクルのイベントハンドラー
メソッド名 |
コールされるタイミング |
メモ |
---|---|---|
|
ネットワークからステートが実行された時 |
このメソッドは、ユーザが新しいノードを作成、または、既存ノードを選択してビューポート内でEnterを押したことによってステートがアクティブになった時にコールされます。 |
|
ステートが中断された時 |
このメソッドは以下の時にコールされます:
|
|
ステートを終了させた時 |
このメソッドは以下の時にコールされます:
Note ノードを削除すると、 |
|
中断を再開した時 |
このメソッドは以下の時にコールされます:
|
|
既存ノードを使わずにステートに入った時 |
このメソッドは、ユーザが既存ノードから ではなくて 、例えば、hou.SceneViewer.setCurrentState()をコールするシェルフツールスクリプトからステートに入ることでステートがアクティブになった時にコールされます。 ノードを使わずに動作するステートに関する詳細は、ノードなしステートを参照してください。 アセットに関連付けられたステートに関しては、理論的には、このメソッドでノードを作成することができますが、代わりにツールスクリプトでノードを作成することを強く推奨します。 詳細は、Pythonステートでノードを扱う方法を参照してください。 このメソッドに渡される辞書には、 |
補足メモ:
-
Houdiniは、ツールがアクティブになった時に
onEnter
(ステートが既存ノードに対して動作する場合) またはonGenerate
(ステートが新しいノードを作成する場合)の どちらか をコールします。両方をコールすることはありません。onEnter
に渡される辞書にはnode
アイテムが含まれており、既存ノードをhou.Nodeオブジェクトとしてアクセスすることができます。詳細は、ノードの編集を参照してください。 -
Houdiniは、ステートを開始する時にマウスポインタがビューア上になくても
onEnter
/onGenerate
をコールします。マウスポインタ周辺に何かの視覚的な表示を行ないたいのであれば、それらを表示できるようにonMouseEvent
コールを待ってください。 -
ステートを"中断"している間は、UIイベントは受信されません。
-
onInterrupt
コールの後にそれに呼応するonResume
が必ず実行されるようにすることはできません。ステートを中断している間にユーザがステートを終了すると、onResume
コールではなくonExit
が受信されます。 -
ユーザがステートのコンテキストメニューを開いた場合、マウスポインタをそのメニュー上に動かすと
onInterrupt
が受信され、そのメニューから出るとonResume
コールが受信されます。 -
ステートが現行で中断された際にHoudiniがバックグラウンドにあれば、マウスポインタがビューア上にあったとしても、ユーザから何かの入力(例えば、マウスの移動)がない限りは、ステートは
onResume
コールを受信しません。
UIのイベントハンドラー
詳細は、UIイベントの制御を参照してください。
これらのメソッドに渡される辞書には、以下の追加項目が含まれています:
ui_event
イベントに関する情報を持ったhou.ViewerEventインスタンスを含んでいます(例えば、マウスイベントに関しては、現行マウス座標、ボタンがクリックされたかどうか)。
メソッド名 |
コールされるタイミング |
メモ |
---|---|---|
|
マウスを移動/クリックした時 |
マウスの制御を参照してください。 |
|
マウスホイールをスクロールした時 |
hou.UIEventDevice.mouseWheel()は、スクロールの方向に応じて マウスホイールの制御を参照してください。 |
|
キーイベント用 |
詳細は、キーボードデバイスを読み込む方法を参照してください。 |
|
キー遷移イベント用 |
詳細は、キーボードデバイスを読み込む方法を参照してください。 |
|
コンテキストメニューを選択した時 |
コンテキストメニューの制御を参照してください。 |
|
メニューステートを更新した時 |
コンテキストメニューの更新を参照してください。 ハンドラー
更新するメニューの識別子。
更新値が格納されたメニューステートの辞書。 対応しているステート
更新値が格納されたメニュー項目ステートの辞書。メニュー項目ハンドルを使って、その辞書のインデックスを指定します。値の部分には、そのメニュー項目のステートの辞書が格納されています。 対応しているステート
Note
|
|
ステートパラメータイベント |
ステートパラメータの制御を参照してください。 |
|
汎用コマンドイベント |
hou.SceneViewer.runStateCommand()を呼び出すことでコールされます。ハンドラー
コマンド文字列識別子。
コマンド固有の引数を保持した |
補足メモ:
-
onCommand
は、ステートに固有のアクションを実装する際に使用します。例えば、onCommand
を使って、ステートパラメータを設定したり、独自の通知の仕組みを実装することができます。詳細は、hou.SceneViewer.runStateCommand()を参照してください。
ハンドルのイベントハンドラー
ステートに動的ハンドルをバインドしていると、Houdiniは以下のメソッドをコールします。詳細は、Pythonステートハンドルを参照してください。
メソッド名 |
コールされるタイミング |
メモ |
---|---|---|
|
ユーザがハンドルを使って操作した時 |
これは、ハンドルを動かしてノードパラメータ(またはステート/ディスプレイ)を更新することができます。 このハンドラーに渡される辞書には、以下の追加項目が含まれています:
ハンドルの文字列ID。
新しいハンドルパラメータ値を含んだ辞書。
変更されたパラメータ名のリスト。
以前のハンドルパラメータ値を含んだ辞書。 これは、差分を計算するのに役立ちます。
ドラッグの開始またはドラッグの停止といったハンドル状態に関する情報を含んだhou.UIEventオブジェクト。 |
|
ノードパラメータが変更された時 |
これは、ノードパラメータを変更した際にハンドルパラメータを更新することができます。 このハンドラーに渡される辞書には、以下の追加項目が含まれています:
ハンドルの文字列ID。
新しいノードパラメータ値を含んだ辞書。 |
|
ハンドルを使ってユーザが操作を開始した時 |
これによって、ユーザがハンドルの操作を開始したことが分かります。 このメソッドに渡される辞書には、以下の追加項目が含まれています:
ハンドルの文字列ID。
ドラッグを開始または停止といったハンドル状態を持ったhou.UIEventオブジェクト。 |
|
ハンドルを使ってユーザが操作を終了した時 |
これによって、ユーザがハンドルの操作を終了したことが分かります。 このメソッドに渡される辞書には、以下の追加項目が含まれています:
ハンドルの文字列ID。
ドラッグの開始または停止といったハンドル情報を持ったhou.UIEventオブジェクト。 |
選択のイベントハンドラー
ステートにセレクターをバインドしていると、Houdiniは以下のメソッドをコールします。詳細は、選択の制御を参照してください。
メソッド名 |
コールされるタイミング |
メモ |
---|---|---|
|
ユーザが選択を開始した時 |
このハンドラーに渡される辞書には、以下の項目が含まれています:
現在のアクティブセレクターの名前(hou.ViewerStateTemplate.bindGeometrySelector()を参照してください)。 |
|
ユーザがジオメトリを選択した時 |
このハンドラーに渡される辞書には、以下の追加項目が含まれています:
完了した選択を表現したhou.GeometrySelectionオブジェクト。
現在のアクティブセレクターの名前。 現行選択を"受け入れて"セレクターを終了するようにステートに命令を送るには、 このメソッドからTrue を返さなければなりません。
このメソッドがそれ以外の値を返す場合(または、 |
|
ユーザが選択を終了した時 |
このハンドラーに渡される辞書には、以下の項目が含まれています:
現在のアクティブセレクターの名前。 |
ドラッグアンドドロップのイベントハンドラー
Houdiniは、Pythonステートがアクティブな時にビューア内で発生したdrag drop
イベントを制御できるように以下のメソッドをコールします。
詳細は、ドラッグアンドドロップの制御を参照してください。
メソッド名 |
メモ |
---|---|
|
ユーザが |
|
ユーザ側で選択可能な |
|
選択した |
描画のイベントハンドラー
これらのメソッドは、描画イベントが発生した時にコールされます。 例えば、これらのメソッドは、hou.AdvancedDrawableオブジェクトを処理する時に実行されます。
メソッド名 |
コールされるタイミング |
メモ |
---|---|---|
|
描画イベント |
このメソッドは以下のタイミングでコールされます:
|
|
描画イベント |
このメソッドは以下のタイミングでコールされます:
|
補足メモ:
-
onDraw
は、高度なDrawableで必須になります。kwargs["draw_handle"]
が返したハンドルは、レンダーオペレーションを実行するためには必ずDrawableに渡さなければなりません。def onDraw(self, kwargs): handle = kwargs['draw_handle'] params = { 'translate': hou.Vector3(self.mouse_pos[0],self.mouse_pos[1],self.mouse_pos[2]) } self.cursor.render(handle, params ) params = { 'translate': hou.Vector3(self.translate[0],self.translate[1],self.translate[2]), 'color1': hou.Color(self.rgba[0],self.rgba[1],self.rgba[2],self.rgba[3]), 'blur_width': self.blur_width } self.face_drawable.render(handle, params)
-
onDrawInterrupt
は任意で、ステートが中断している間に描画が必要になった時にのみ高度なDrawableで使用します。
Viewerステートの検査
Houdiniは、Viewer State Browserウィンドウを使って、登録されているすべてのViewerステートを表示することができます。 このブラウザは、Python Panelメニューを介して開くことができます。
デバッグに関するTips
Houdiniは、Python Viewerステートのデバッグに対応しています。 最も役立つのは、Viewer State Browserです。 ここには、デバッグ情報を表示するための統合ツールが備わっています。 Pythonのprint関数といった基本的な方法を使うこともありですが、このブラウザには、デバッグ関連の機能がたくさん用意されています。
デバッグ情報を表示する方法
最も基本的だけどデバッグで役立つのは、スクリプトを実行した時に変数の内容などの情報をプリントすることです。 Houdiniは、このタイプの情報を表示するのにいくつかの方法を用意しています。
printを使用する主なメリットは、(現在のPythonコールスタックのような)複数行のテキストを含むたくさんの情報を出力することができ、それを後にスクロールして読むことができることです。
self.logメソッド
log
メソッドは、Pythonステートクラスで利用可能で、Pythonのprint関数と同様の機能を持っています。
log
メソッドは、標準出力にプリントするのではなくて、Viewer State Browserコンソールにメッセージをプリントします。
-
Viewer State Browserでは、
Debug Log
ボタンを切り替えることで、メッセージをログに記録するタイミングを制御することができます。 これによって、デバッグが必要でない時にコメントアウトしなくても、あなたのコード内にlog
メソッドコールを保持することができます。 -
Houdiniは、オブジェクトが作成された後にPythonステートに動的に
log
メソッドを追加します。 そのため、__init__
がコールされた時点では、まだlog
は利用できません。 Viewer State Browserコンソールのメッセージをログに記録するための回避策は、__init__
からviewerstate.utils.log
を使用することです。
import traceback class MyState(object): def __init__(self, state_name, scene_viewer): self.state_name = state_name self.scene_viewer = scene_viewer def onMouseEvent(self, kwargs): # マウス位置をログに記録します。 ui_event = kwargs["ui_event"] device = ui_event.device() self.log("Mouse position x=", device.mouseX(), "y=", device.mouseY()) # 現在のPythonコールスタックをログに記録します。 self.log(''.join(traceback.format_stack()))
Pythonのprint関数
この出力は、Houdiniの起動方法やウィンドウの開き方に応じて、コンソールウィンドウ、HoudiniのPythonシェルウィンドウ、Houdiniを起動させた時のシェルに表示させることができます。
Tip
print
をステートメントではなく関数として使用することに慣れてしまっているのあれば、from __future__ import print
を使用すると良いでしょう。
この関数は、使いやすくて多機能です。
from __future__ import print import traceback class MyState(object): def __init__(self, state_name, scene_viewer): self.state_name = state_name self.scene_viewer = scene_viewer def onMouseEvent(self, kwargs): # マウス位置をプリントします。 ui_event = kwargs["ui_event"] device = ui_event.device() print("Mouse position x=", device.mouseX(), "y=", device.mouseY()) # 現在のPythonコールスタックをプリントします。 traceback.print_stack()
hou.SceneViewer.setPromptMessage
hou.SceneViewer.setPromptMessage()とhou.SceneViewer.clearPromptMessage()の関数は、メインのHoudiniウィンドウの下部のスタータスラインにユーザ操作を促すことができます。これらの関数を"応用"することで、デバッグ情報を表示させることができます。
-
このメソッドのメリットは、Houdiniを操作する際にウィンドウの正面中心に情報を表示できることです。
-
このデメリットは、ステータスラインは一度に一行でしか情報を表示できないので、変更があると前のメッセージが消えてしまうことです。
class MyState(object): def __init__(self, state_name, scene_viewer): self.state_name = state_name self.scene_viewer = scene_viewer def onMouseEvent(self, kwargs): # ステータスラインにマウス位置を表示します。 ui_event = kwargs["ui_event"] device = ui_event.device() message = "Mouse x=%d y=%d" % (device.mouseX(), device.mouseY()) self.scene_viewer.setPromptMessage(message)
hou.ui.displayMessage
hou.ui.displayMessage()関数は、メッセージウィンドウをポップアップして、ユーザがOKかCancelをクリックするのを待ちます。
-
これは、デバッグするのにいくつかメリットがあります。その1つは、待機中にスクリプトが一時停止されるので、非常に簡単に変更内容を調べることができます。また、クリックされたボタンに応じていくつかのフィードバックをスクリプトに渡すことができます。もう1つは、この関数はキーワード引数を持っているので、デフォルトで非表示になっている"詳細"ブロックを追加して展開することができます。これは、例えば現在のPythonコールスタックを表示するのに役立ちます。
-
おそらくループでこの関数を使用したくないことでしょう。というのも、複数のメッセージウィンドウを閉じるのに一々クリックしなければならないので面倒なことになります。
import traceback import hou class MyState(object): def __init__(self, state_name, scene_viewer): self.state_name = state_name self.scene_viewer = scene_viewer def onMouseEvent(self, kwargs): ui_event = kwargs["ui_event"] device = ui_event.device() # クリックするとメッセージのみが表示されます。 if device.isLeftButton(): message = "Mouse x=%d y=%d" % (device.mouseX(), device.mouseY()) # スクリプト内のこの時点でのコールスタックを取得し、それをメッセージの"詳細"として追加できるように文字列の書式に変換します。 details = "".join(traceback.format_stack()) # メッセージを表示して、ユーザがメッセージウィンドウのボタンをクリックするのを待ちます。 clicked = hou.ui.displayMessage( message, buttons=("OK", "Error"), details_label="Current call stack", details=details ) # ユーザが"Error"ボタンをクリックすると、エラーを引き起こします。 if clicked == 1: raise Exception("Don't blame me!")
ステートのリロード
ディスク上に定義されたステートに関しては、そのディスク上のファイルを変更したら、hou.ui.reloadViewerState()を使って、Houdiniにそのステートをリロードさせることができます。 HDAに埋め込まれたステートをリロードするには、Viewer State Editorを使用します。
デバッグコンテキストメニュー
HOM API
以下に、Python Viewerステート関連のHOM
APIをいくつか挙げています。
Pythonビューアステート