Houdini 17.0 Pythonスクリプト

Pythonで独自のViewer Stateを記述する方法

Viewer Stateは、ビューア内でのマウスの動き、クリック、キーといった解釈の方法を制御します。

On this page

Pythonビューアステート

概要

Viewer Stateは、ビューポート内でのインタラクティブな操作を制御することができます。 例えば、 RotateツールはViewer Stateです。 Handlesツールは、現行ノードに関係したViewer Stateにアクセスすることができます。 Houdiniは、Pythonで独自のViewer Stateを作成して登録することができます。

独自のViewer Stateの例:

  • ディスプレイハンドルは、ユーザー側でハンドルを制御して、パラメータ/設定を変更することができます。

  • ジオメトリとの対話操作は、サーフェス上で描画したり、マウスカーソル下の情報を表示させることができます。

  • ガイド表示は、ジオメトリをガイドとして表示することができます。

Houdiniデジタルアセットには、Viewer Stateを指定することができます(例えば、そのタイプのノードが現行になった時に Handlesツールを使用させることができます)。 結局のところ、(Inspectionツールのようにノードを作成しないツールの)シェルフツールスクリプトでは、hou.SceneViewer.setCurrentState()をコールすることで、プログラム的にステートを変更することができます。

コードを今すぐにでも試したいのであれば、以下のViewer Stateの実装方法を参照してください。そこでは、最低限のViewer Stateの実装、登録、実行の方法について説明しています。

制限事項

  • 現在のところ、Pythonステートは、 SOP(ジオメトリ)レベルとOBJレベルでのみ利用可能です 。現在のところ、例えばダイナミクスネットワークやコンポジットネットワークなどの他のネットワークタイプで独自のViewer Stateを作成することはできません。

  • 現在のところ、Viewer Stateにアイコンを指定する方法がありません。

ステート名

ステートは、 内部名 とUIで表示する人が解読可能な ラベル を持ちます。 新しく独自のステートを作成する場合、その内部名は固有でなければなりません。もし2人の作成者が同じステート名を使用してしまった場合、どちらかのステートは登録に失敗します。 あなたが作成したステートまたはアセットがいつの日か他のユーザー/スタジオで共有もしくは製品として販売することを考慮すれば、適切な固有名になるようにちゃんと時間をかけて決めてください。

Houdiniはアセット毎に自動的に汎用ステートを作成するということも相まって、既存のノードタイプの名前をステート名として使用することはできません。

  • ステートが特定のアセットで固有であれば、そのアセットの名前(ネームスペース付き)をステート名として使用し、最後に.pystateを付けてください。これによって、ステートとアセットの関係が明確になり、自動的に作成されるアセットの汎用ステートとの干渉を回避することができます

    例えば、OnInstallイベントスクリプトで自動的に以下のような名前を付けることができます:

    state_name = kwargs["type"].type().name() + ".pystate"
    

    詳細は、以下の登録の方法を参照してください。

  • ステートを2つ以上のノード間で共有したい場合やノードに関連付けたくない場合、必ずどのアセットの汎用ステートとも干渉しないようなステート名を付けなければなりません。この一番良い方法は、アセットで使用しているのと同じネームスペースを組み込むことです。

    例えば、あなたがExample.com映画スタジオで働いていて、アセットのネームスペースの接頭辞にexamplecom::を使用していると仮定すると、"scrub"ステートを作成する時は、そのステート名をexamplecom::scrubにしてください。

  • ステート名には、ノードタイプ名やノード名と同じ文字制限がありません。ステート名は多かれ少なかれ任意の文字列にすることができます。

  • ファイル/ディレクトリ名として名前を使用する必要が出てくる場合があります(例えば、コードをファイルに保存する場合)。

ツール + ステート vs. 自己完結型ステート

ほとんどじゃなくても多くの"ネイティブ"のHoudiniノードステートは自身の選択を制御することもノードを作成することもしません。 代わりに、それらのノードステートは、選択の促しとノード作成をシェルフツールに頼っています。 それらのノードステートは単にハンドルの表示を担っているだけです。

独自のPythonステートを使ってこのワークフローを使用することもできます。特に、そのステートがアセットに密接に関係している時です。 選択を促してノードを作成する(そして、その選択をノードの Group フィールドに設定する)シェルフツールスクリプトを書くことができます。

別の方法としては、ステートを自己完結型にすることができます。 つまり、ノードをビューアから呼び出した時にそのノードを作成して、そこに独自のセレクターを持たせることができます。

(ステートにノードが不要な場合(例えば、検査タイプのツール)、その詳細はノードを使用しないステートの書き方を参照してください)

  • 2つ以上のタイプの選択を要求する必要がある場合(例えば、"いくつかカーブを選択"した後に"それらのカーブ上のいくつかのポイントを選択する"場合)、ツールスクリプトを使用してください。現在のところ、Pythonステートは、1個のタイプの選択のみ受け入れることができます。

  • コードがノードを作成 する前に 何が選択されているのかを知る必要がある場合、ツールスクリプトを使用してください。

ステートでノードインスタンスを制御する詳細は、ノードの扱い方を参照してください。

Houdiniにステートをインストールする方法

現在のところ、Houdiniで利用可能なカスタムステートを作成する方法が2通りあります: アセットにステートのコードを埋め込む方法とHoudiniパスの正しいディレクトリにそのコードを含んだPythonファイルを配置する方法です。

アセットにステートを埋め込む方法

以下の手順は、アセットを作成し、そこに最低限のステートを試せるようにコードを記述します。

Note

これらの手順は、SOPステートまたはOBJステートの作成にも使うことができます。

  1. ステートを追加できるようにアセットの作成から始めます。

    空っぽのアセットから始めたい場合は、に従うことでSOPアセットを、に従うことでOBJアセットを作成することができます。

    実験できるように"空っぽ"のSOPアセットを手軽に作成するには:

    1. オブジェクトレベルで、⇥ Tabメニューを使ってGeoオブジェクトを作成します。

    2. geo1ノードをダブルクリックして、ジオメトリネットワークの中に入ります。

    3. ⇥ Tabメニューを使ってSubnetworkノードを作成します。

    4. subnet1ノードを右クリックして、 Create Digital Asset を選択します。

    5. Operator NamestatedemoOperator LabelState DemoSave to LibraryEmbeddedに設定します。

      ライブラリの場所をEmbeddedに設定することで、アセットライブラリではなく、現行シーンファイルにアセットが保存されます。

    実験できるように"空っぽ"のOBJアセットを手軽に作成するには:

    1. オブジェクトレベルで、⇥ Tabメニューを使ってSubnetworkノードを作成します。

    2. subnet1ノードを右クリックして、 Create Digital Asset を選択します。

    3. Operator NamestatedemoOperator LabelState DemoSave to LibraryEmbeddedに設定します。

      ライブラリの場所をEmbeddedに設定することで、アセットライブラリではなく、現行シーンファイルにアセットが保存されます。

  2. そのアセットのType Propertiesウィンドウを開きます(アセットタイプのインスタンスを右クリックして、 Type Properties を選択します)。

  3. Scripts タブをクリックします。

  4. 左側のスクリプトのリスト下で、 Event Handler ポップアップメニューから"Python Module"を選択します。

    左側でそのPythonModuleスクリプトを選択すると、右側のエディタでそのモジュールの内容を編集することができます。

  5. 以下の最低限のステートコードをエディタ内にペーストします。

    from __future__ import print_function
    
    
    # 最低限のステートハンドラーの実装。
    class MyState(object):
        def __init__(self, state_name, scene_viewer):
            self.state_name = state_name
            self.scene_viewer = scene_viewer
    
        # ハンドラーメソッドをここに記述します。
        def onMouseEvent(self, kwargs):
            dev = kwargs["ui_event"].device()
            print("Mouse:", dev.mouseX(), dev.mouseY(), dev.isLeftButton())
    
    
    # アセットのノードタイプの参照を取得します。
    nodetype = kwargs['type']
    # そのノードの名前とラベルをステートの名前とラベルとして使用します。
    state_name = nodetype.name() + ".pystate"
    label = nodetype.description()
    category = nodetype.category()
    # そのステートの名前とラベルとノードタイプカテゴリでViewerStateTemplateをインスタンス化します。
    template = hou.ViewerStateTemplate(state_name, label, category)
    
    # このバインドは必須です。
    template.bindFactory(MyState)
    
    # 他の任意のバインドをここに記述します...
    

    ステートクラスに機能を追加する方法の詳細は、以下のステートを実装する方法を参照してください。

    Tip

    (上記で示したように)モジュールレベルでテンプレートオブジェクトを構築し、module.templateを使ってそれを参照することができます。 しかし、関数内でテンプレートオブジェクトの作成をラップしたいのであれば、一時変数名(例えば、上記のstate_namelabel)がモジュールのネームスペースから漏れないようにしてください:

    # モジュールレベルでテンプレートを作成するのではなく、関数内にテンプレート作成コードをラップします。
    def createViewerStateTemplate():
        # ノードの名前とラベルをステートの名前とラベルとして使用します。
        state_name = nodetype.name() + ".pystate"
        label = nodetype.description()
        category = nodetype.category()
        # そのステートの名前とラベルとノードタイプカテゴリでViewerStateTemplateをインスタンス化します。
        template = hou.ViewerStateTemplate(state_name, label, category)
    
        # このバインドは必須です。
        template.bindFactory(MyState)
    
        # 他の任意のバインドをここに記述します。
    
        return template
    

    次にモジュールの外でmodule.createViewerStateTemplate()を使えば、そのテンプレートを参照ではなく直接取得することができます。

  6. 左側のスクリプトのリスト下で、 Event Handler ポップアップメニューから"On Install"を選択します。

    左側でそのOnInstallスクリプトを選択すると、右側のエディタでそのモジュールの内容を編集することができます。

    ここでは、ステートを登録するためにOnInstallスクリプト(ノードタイプがセッションにインストールされると実行されます)を使用しました。

  7. 以下のイベントスクリプトをエディタ内にペーストします:

    module = kwargs['type'].hdaModule()
    hou.ui.registerViewerState(module.template)
    

    これは、そのステートをHoudiniに登録します。

  8. 次に、このステートの登録を解除するために他のアセットイベントスクリプトも追加しようと思います。

    左側のスクリプトのリスト下で、 Event Handler ポップアップメニューから"On Uninstall"を選択します。

    左側でそのOnUninstallスクリプトを選択すると、右側のエディタでそのモジュールの内容を編集することができます。

    以下のイベントスクリプトをエディタ内にペーストします:

    state_name = kwargs['type'].name() + ".pystate"
    hou.ui.unregisterViewerState(state_name)
    

    これは、そのノードタイプをセッションからアンインストールすると、Houdiniからそのステートの登録が解除されます。

    OnInstallOnUninstallのスクリプトを使用することで、 アセットを保存することでステートの違いをテスト できるようになります。これによって、ステートが再度登録させて、私達の変更を反映させます。

  9. 最後に、この新しいステートを使用するようにアセットを設定します。Type Propertiesウィンドウの Node タブをクリックします。

  10. Default State フィールドにそのステート名(ウィンドウの上部に表示されている Operator Type: の隣のノードの内部名と.pystate接尾辞)を設定します。

  11. Type Propertiesウィンドウの下部にある Accept をクリックします。

    アセットがロックされていた場合、Houdiniは、あなたの変更を保存できるようにそのアセットのロックを解除するのか尋ねてきます。

  12. このステートをテストするために、ネットワークエディタ内でそのアセットを選択します。マウスをシーンビューア内に移動させて、Enterを押します。

    • ビューアの上部にあるツールバーは、左側にこのステートのラベルを表示します。

Tip

上記の手順では、 アセットのノードタイプ の名前を ステート の名前として使用しました。 これは賢明で、ステートとアセットを関係づけるのが簡単になります。 しかし別の方法として、一部の人は、 まず最初 に__Node__ タブの Default State フィールドを入力すると、自動的に そのフィールドの名前がステート名として使用される という便利な方法を発見しました。 Default State フィールドの内容は、埋め込んだステート名と同期しなくなることはありませんが、その名前はノードから自動的に派生しません。

この方法でステートに名前を付けるには、まず最初に名前を付けたいステート名の Default State フィールドを入力します(これは、すべてのステートとインストールされたアセット名に被らないようにしなければなりません)。 次に、スクリプト内でノード名からステート名を計算する箇所で、以下のように名前を取得できるようにスクリプトを変更します:

state_name = node_type.definition().sections()['DefaultState'].contents()

( Default State フィールドの内容は、DefaultStateという名前で"Extra Files"に内部的に保存されるので、このメソッドを使えば、スクリプトでそれを読み込んだり変更することができます。)

Houdiniパスからステートを読み込む方法

以下の手順では、複数のアセット間で"共有"可能なViewer Stateを作成します。このタイプのステート(self-installedステートという名前にします)は、Houdiniを起動した時に登録されます。

  1. $HOUDINI_USER_PREF_DIR下にviewer_statesという新しいフォルダを作成します。そのフォルダの中にmystate.pyという新しいファイルを作成します。

    HOUDINIPATH /
        viewer_states /
            mystate.py
    
  2. テキストエディタでmystate.pyを開き、以下の最低限のステートの実装をペーストします。

    from __future__ import print_function
    
    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):
            dev = kwargs["ui_event"].device()
            print("Mouse:", dev.mouseX(), dev.mouseY(), dev.isLeftButton())
    
    # 登録エントリーポイント
    def createViewerStateTemplate():
        # 名前とラベルを選択します。
        state_name = "mystate"
        state_label = "My New State"
        category = hou.sopNodeTypeCategory()
    
        # テンプレートを作成します。
        template = hou.ViewerStateTemplate(state_name, state_label, category)
        template.bindFactory(MyState)
    
        # 他の任意のバインドをここに記述します...
    
        # 'mystate'テンプレートを返します。
        return template
    
  3. ステートを追加したいアセットを探します。

    空っぽのアセットから始めたい場合は、SOPアセットに関しては、OBJアセットに関してはに従ってください。

    実験できるように"空っぽ"のSOPアセットを手軽に作成するには:

    1. オブジェクトレベルで、⇥ Tabメニューを使ってGeoオブジェクトを作成します。

    2. geo1ノードをダブルクリックして、ジオメトリネットワークの中に入ります。

    3. ⇥ Tabメニューを使ってSubnetworkノードを作成します。

    4. subnet1ノードを右クリックして、 Create Digital Asset を選択します。

    5. Operator NamestatedemoOperator LabelState DemoSave to LibraryEmbeddedに設定します。

      ライブラリの場所をEmbeddedに設定することで、アセットライブラリではなく、現行シーンファイルにアセットが保存されます。

    実験できるように"空っぽ"のOBJアセットを手軽に作成するには:

    1. オブジェクトレベルで、⇥ Tabメニューを使ってSubnetworkノードを作成します。

    2. subnet1ノードを右クリックして、 Create Digital Asset を選択します。

    3. Operator NamestatedemoOperator LabelState DemoSave to LibraryEmbeddedに設定します。

      ライブラリの場所をEmbeddedに設定することで、アセットライブラリではなく、現行シーンファイルにアセットが保存されます。

  4. アセットのType Propertiesウィンドウを開きます(アセットタイプのインスタンスを右クリックして、 Type Properties を開きます)。

  5. Node タブをクリックします。

  6. Default State フィールドにステート名を設定します(例えば上記のコードでは、これはmystateです)。

  7. Type Propertiesウィンドウの下部にある Accept をクリックします。

    アセットがロックされていた場合、Houdiniは、あなたの変更を保存できるようにそのアセットのロックを解除するのか尋ねてきます。

  8. このステートをテストするために、ネットワークエディタ内でそのアセットを選択します。マウスをシーンビューア内に移動させて、Enterを押します。

    • ビューアの上部にあるツールバーは、左側にこのステートのラベルを表示します。

Houdiniは起動時にcreateViewerStateTemplateをコールして、そのステートテンプレートにアクセスして、それを登録します。省略すると、Houdiniは単にそのステートの登録をスキップします。

ステートクラスに機能性を追加する方法の詳細は、以下のステートを実装する方法を参照してください。

Tip

どのファイルがどのステートを保持しているのか把握しやすくするためには、テンプレートをインスタンス化する時に使用するステート名(例えば、examplestudio::bend)と同様にファイル名(例えば、examplestudio_bend.py)を維持してください。

Tip

Houdiniセッション中にself-installedステートを再読込するには、そのステートの名前を使ってhou.ui.reloadViewerState()をコールしなければなりません。 これによって、Houdiniを再起動することなくステートに変更を加えてテストすることができます。

ステートを実装する方法

以下のセクションでは、ステートを実装するクラスに追加可能なハイレベルのメソッドの概要について載せています。

特定の機能の実装方法のガイドに関しては、以下のページを参照してください:

イニシャライザ(必須)

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

メニューアイテム名をキーとして使用したメニューアイテム関連の現行値を含んでいます。

以下のテーブルには、ステートクラスを定義可能なイベントハンドラーを載せています。

ライフサイクルのイベントハンドラー

メソッド名

コールされるタイミング

メモ

onEnter

ネットワークからステートが実行された時

このメソッドは、ユーザーが新しいノードを作成、または、既存ノードを選択してビューポート内でEnterを押したことによってステートがアクティブになった時にコールされます。

onGenerate

既存ノードを使わずにステートが実行された時

このメソッドは、ユーザーが既存ノード 以外 からステートを実行した時、例えば、シェルフツールスクリプトからhou.SceneViewer.setCurrentState()をコールすることによってステートがアクティブになったときにコールされます。 ノードを使わないで動作するステートに関する情報は、ノードを使用しないステートを参照してください。

アセット関連のステートに関しては、理論的にはこのメソッドでノードを作成することができますが、代わりにツールスクリプトからノードを作成することを強く推奨します。 詳細は、Pythonステートでノードを扱う方法を参照してください。

このメソッドに渡される辞書には、nodeアイテムを含んでいません。hou.SceneViewer.pwdを使ったビューアに相当するネットワーク位置を取得することができます。

onInterrupt

ステートが中断された時

このメソッドは以下の時にコールされます:

  • ウィンドウがフォーカスを失なった時。

  • ポインタがビューアから抜けた時(メニューの上に移動した時も含む)。

  • ユーザーが"Volatile"ツール(例えば、((S)を押したままにすると表示されるツール)を押してVolatile選択ツールに切り替えた時)。

onResume

中断を終了した時

このメソッドは以下の時にコールされます:

  • ポインタが再度ビューアに戻った時。

  • ユーザーが"Volatile"ツールからこのステートに戻った時(例えば、Volatile選択ツールを使った後にSを離した時)。

onExit

ステートを終了させた時

このメソッドは以下の時にコールされます:

  • ユーザーまたはHoudiniが別のツールに切り替えた時。

  • ユーザーがビューアを閉じた時、または別のペインタブに切り替えた時。

  • ユーザーがHoudiniを終了した時。

補足メモ:

  • 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インスタンスを含んでいます(例えば、マウスイベントに関しては、現行マウス座標、ボタンがクリックされたかどうか)。

メソッド名

コールされるタイミング

メモ

onMouseEvent

マウスを移動/クリックした時

マウスの制御を参照してください。

onMouseWheelEvent

マウスホイールをスクロールした時

hou.UIEventDevice.mouseWheel()は、スクロールの方向に応じて-1または1を返します。

マウスホイールの制御を参照してください。

onMenuAction

コンテキストメニューを選択した時

コンテキストメニューの制御を参照してください。

選択のイベントハンドラー

ステートにセレクターをバインドしていると、Houdiniは以下のメソッドをコールします。詳細は、選択の制御を参照してください。

メソッド名

コールされるタイミング

メモ

onStartSelection

ユーザーが選択を開始した時

なし

onSelection

ユーザーがジオメトリを選択した時

このハンドラーに渡される辞書には、以下の追加アイテムが含まれています:

selection

完了した選択を表現したhou.GeometrySelectionオブジェクト。

現行選択を"受け入れて"セレクターを終了するようにステートに命令を送るには、 このメソッドからTrue を返さなければなりません。 このメソッドがそれ以外の値を返す場合(または、returnステートメントを含んでいない場合)、セレクターはアクティブなままになります。

onStopSelection

ユーザーが選択を終了した時

onSelection()Trueを返したり、選択を受け入れる前にステートを終了させることによって、そのステートが選択を"受け入れた"時にコールされます。

ハンドルのイベントハンドラー

ステートにハンドルをバインドしていると、Houdiniは以下のメソッドをコールします。詳細は、Pythonステートハンドルを参照してください。

メソッド名

コールされるタイミング

メモ

onHandleToState

ユーザーがハンドルを使って操作した時

これは、ハンドルを動かしてノードパラメータ(またはステート/ディスプレイ)を更新することができます。

このハンドラーに渡される辞書には、以下の追加アイテムが含まれています:

handle

ハンドルの文字列ID。

parms

新しいハンドルパラメータ値を含んだ辞書。

mod_parms

変更されたパラメータ名のリスト。

prev_parms

以前のハンドルパラメータ値を含んだ辞書。 これは、差分を計算するのに役立ちます。

onStateToHandle

ノードパラメータが変更された時

これは、ノードパラメータを変更した際にハンドルパラメータを更新することができます。

このハンドラーに渡される辞書には、以下の追加アイテムが含まれています:

handle

ハンドルの文字列ID。

parms

新しいノードパラメータ値を含んだ辞書。

HOM API

デバッグに関するTips

ステートを再読込する方法

ディスク上に定義されたステートに関しては、そのディスク上のファイルを変更したら、hou.ui.reloadViewerState()を使うことで、Houdiniにそのステートを再読込するように伝えることができます。

デバッグ情報を表示する方法

最も基本的だけどデバッグで役立つのは、スクリプトを実行した時に変数の内容などの情報をプリントすることです。 Houdiniは、このタイプの情報を表示するのにいくつかの方法を用意しています。

  • スクリプト内でPythonのprint関数を使用することで、情報をプリントすることができます。

    • printを使用する主なメリットは、(現在のPythonコールスタックのような)複数行のテキストを含むたくさんの情報を出力することができ、それを後にスクロールして読むことができることです。

    • この出力は、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.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()関数は、メッセージウィンドウをポップアップして、ユーザーが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!")
    

Pythonビューアステート

Pythonスクリプト

はじめよう

次のステップ

Pythonビューアステート

Pythonでビューアステートを記述することで、ビューポート内でノードのユーザー操作をカスタマイズすることができます。

導師レベル

リファレンス

  • hou

    Houdiniにアクセスできるサブモジュール、クラス、ファンクションを含んだモジュール。

  • Alembic拡張関数

    Alembicファイルから情報を抽出するための便利な関数です。