Houdini 19.5 Pythonスクリプト

Python スクリプトの場所

Houdiniでは、Pythonスクリプトを使用することで、いろいろな場所で挙動をカスタマイズすることができます。

On this page

一般的なtips

  • コードが実行されたかどうかをチェックする必要がある場合、一番簡単な方法は、hou.ui.displayMessageをコールすることです:

    import hou
    hou.ui.displayMessage("I ran! I ran so far away!")
    

    これを使用することで、変数の値も表示することができます。例えば、イベントハンドラーでkwargsグローバル変数の内容を調べるには:

    import hou
    hou.ui.displayMessage(repr(kwargs))
    
  • ディスク上のモジュールにコードを置いて、それをハンドラースクリプトからコールすることで、そのコードをハンドラー間で共有することができます。例えば、ハンドラースクリプトは共通の関数にkwargsを渡すことができます:

    import companyutils
    
    companyutils.on_created(kwargs)
    
  • Houdiniには、非常に膨大で強力(なのに冗長な)APIセットが用意されています。APIリファレンスで利用可能なAPIをブラウズすることで、何がスクリプトで可能なのか調べることができます。

  • 一般的には 、Houdiniは、HOUDINIPATH/scripts/下でディスク上の“スクリプト”(通常はコールバックスクリプト)を探すのに対し、インポートされるモジュールはHOUDINIPATH/python2.7libs/に置きます。残念なことに、これでは整合性が取れてないので、例えばHoudiniはpython2.7libsではpythonrc.py初期化スクリプトを探します。

スクリプト内で他のロケーションにあるコードを参照する方法

ディスク上のPythonファイルの場合

現行.hipファイルのセッションモジュールの場合

現行ファイルのセッションモジュールを参照するには、hou.sessionを使用します。

現行ノードのPythonモジュールの場合

カスタムノードタイプでは、そのノードタイプに関係するコードを格納するためのデフォルトのPythonモジュールを作成することができます。 そして他の場所でhou.Node.hdaModule(またはhou.NodeType.hdaModule)を使用することで、そのノードタイプのPythonモジュールを参照することができます。

ボタンパラメータのコールバックスクリプトまたはアセットイベントスクリプトの例:

# コールバックスクリプトでは、現行ノードの参照は、kwargs["node"]に含まれています。
nodeutils = kwargs["node"].hdaModule()

nodeutils.my_function()

スタートアップスクリプト

Houdiniは、$HOUDINI_PATHで指定されたディレクトリで以下のディレクトリ/ファイルを探します。

python2.7libs/pythonrc.py

Houdiniは、Houdiniのパスで、このパターンに一致するファイルを探し、起動時に実行します。

例えば、$HOUDINI_USER_PREF_DIR/python2.7libs/pythonrc.pyは個人的なスクリプトに指定したり、 $HSITE/houdini14.0/python2.7libs/pythonrc.pyは、スタジオでのスクリプトに指定したりします。

scripts/123.py

Houdiniは、シーン(.hip)ファイル なし で起動すると、このスクリプトを実行します。 Houdiniは、パスで見つけた 最初の 123.pyのスクリプトのみを実行します。 これは、空っぽのシーンのカスタマイズに便利です。 例えば、あらゆるシーンをデフォルトのライティングリグで始めたい場合などです。

これは、Pythonの123.cmdHScriptファイルに相当します。 123.cmd123.pyの両方が存在する場合、Houdiniは、123.py のみ を実行します。

Note

Houdini FXのみが123.pyを実行します。Houdini Coreは起動時に123.pyではなくhoudinicore.pyを実行します。 123.pyhoudinicore.pyは異なるプロダクトに対して行なわれること以外はどちらも同じ目的を果たします。

scripts/456.py

Houdiniは、(Houdiniがシーンファイルで起動する場合など)シーンファイルがロードされる時には常にこのスクリプトを実行します。

これは、Pythonの456.cmdHScriptファイルに相当します。

scripts/category/ nodename_eventtype.py

以下のノードイベントスクリプトを参照してください。

Note

新しいスクリプトを認識させるには、Houdiniを再起動する必要があります。 とはいえ、Houdiniを再起動することなく既存のスクリプトを変更することができます。

シーンファイル(.hip)を保存する前と後でスクリプトを実行します

Houdiniは、保存の前と後でスクリプトを実行することができます。 これは、保存の度にアセット管理とソースコントロールのツールを更新するのに役立ちます。

  • HOUDINIPATH/scripts/beforescenesave.pyが存在すれば、Houdiniはシーンファイルを保存する前にそれを実行します。

  • HOUDINIPATH/scripts/afterscenesave.pyが存在すれば、Houdiniはシーンを保存した後にそれを実行します。

Tip

これらのスクリプトは、自動的にグローバル(どのシーンファイルでも動作する)で、セットアップしやすいので便利です。 その一方で、プログラム的にセットアップするのが難しく、保存関係にしか使えません。

シーンファイルイベントに反応させるもっと一般的で細かい調整が効く方法は、シーンファイルイベントハンドラーをセットアップすることです。

これらのスクリプトは、kwargsグローバル辞書に含まれているコンテキストで実行されます。この辞書には以下の項目が含まれています:

file (str)

保存先となる 予定の シーンファイルのパス(beforescenesave.pyでは、既にファイルがそこに存在していてもしていなくても構いません)。

autosave (bool)

自動保存タイマーによって保存が発動された時には、ここにTrueが格納されます。ユーザによる“通常の”保存では、ここにFalseが格納されます。

success (bool)

(afterscenesave.pyのみ)Houdiniがファイルに保存可能だった時には、ここにTrueが格納されます。

Note

指定したパスにHoudiniがファイルを実際に保存できるとは限りません(例えば、ユーザに適切な権限がなかった場合)。 そのため、afterscenesave.pyが実行された時でもそのファイルが存在しなかったり、更新されたデータが含まれていないことがあります。 保存されたデータがファイルに含まれていることを前提としているのであれば、そのスクリプトのkwargs["success"]をチェックするべきです。

以下のコードは、ファイルを保存した時にgitにコミットされるようにそのファイルを自動的に“ステージ”します:

# afterscenesave.py
import subprocess

# 保存に成功していて、それが自動保存でなかった場合にのみコマンドを実行します。
if kwargs["success"] and not kwargs["autosave"]:
    # gitコマンドにシーンファイルのパスを渡します。
    subprocess.call("git", "add", kwargs["file"])

シーンファイルのイベントコールバック

シーンファイルが変更された時にHoudiniがコールするコールバック関数を登録することができます。 Houdiniは、この関数を1個の引数を使ってコールします。この引数は、発生したイベントのタイプを表現したhou.hipFileEventTypeオブジェクトです。

このスクリプトは、特定のイベントに限定されているわけでなく、どのイベントからでもコールされ、そのイベントがあなたの興味のあるイベントなのかどうかを確認するには、そのイベントタイプをチェックする必要があります。 いくつかのイベントに反応するコードを共有させたいことが多いので、これは通常ではシーンイベントで非常に役立ちます。

このスクリプトにhou.hipFileモジュール内の関数を使用することで、現在のシーンファイルに関する情報を取得することができます。 各ファイル操作(New file, Open file, Import file, Save)には、それに相当する“Before”と“After”のイベントタイプがあるので、その操作の変更前と変更後でのシーンファイルの値を読み込むことができます。

例えば、新しいシーンファイルが読み込まれる度に何かのコードを実行するには:

def scene_was_loaded(event_type):
    if event_type == hou.hipFileEventType.AfterLoad:
        print("The user loaded", hou.hipFile.path())

パラメータのエクスプレッション

セッションモジュール

Houdiniは、シーンファイルに関連するPythonモジュールを保持します。 これは、 Window ▸ Python Source Editor を使って編集することができます。 これにより、シーンファイルに固有の関数, クラス, オブジェクトを保存するのに便利な場所が与えられ、自動的にそれらが保存されて、シーンファイルと一緒にロードされます。

シーンファイルがロードされた場合(または、ソースコードを編集する場合)、モジュールのソースコードが評価され、他のPythonコードで、モジュールをhou.sessionとして使用できるようになります。

例えば、Python Source Editor内にlog_deletion()関数を記述しておけば、以下のようにアセット削除コールバックからその関数をコールすることができます:

hou.session.log_deletion()

hou.appendSessionModuleSource()関数を使用して、プログラムでhou.sessionソースコードを修正することができます。 ソースコードは、シーンファイルに保存されます。

Note

モジュールオブジェクトが一度メモリに読み込まれた後にそのモジュールオブジェクトに加えられた動的な変更内容(例えば、hou.session.x = "foo")は、シーンファイルに保存 されません 。 そのモジュールのソースコードのみがシーンファイルに保存されます。

アセットモジュール

デジタルアセットタイプには、シーンファイルのhou.sessionモジュールに似たPythonモジュールがあります。 このモジュールを使用して、アセットタイプに固有の関数, クラス, オブジェクトを保存することができます。

モジュールのソースコードは、(Type PropertiesウィンドウExtra Files タブにある)PythonModuleという名前のアセットのセクションに保存されます。 このセクションは手動で作成することができます。または、 Scripts タブで“Python Module”イベントハンドラーを作成すると、Houdiniは、このセクションを自動的に作成します。

HDAモジュールのコードでは、以下のコードを使用することで、HDAのノードタイプの参照を取得することができます:

nodetype = kwargs["type"]

(NodeTypeオブジェクトがある場合は)hou.NodeType.hdaModuleメソッドにより、または(Nodeオブジェクトがある場合は)hou.Node.hdaModuleメソッドにより、モジュールにアクセスすることができます。 例えば、myassetオブジェクトタイプで、myfunc関数をコールするには:

hou.nodeType(hou.objNodeTypeCategory(), "myasset").hdaModule().my_function()

アセットの定義内にあるノードのPythonエクスプレッションは、相対参照によりPythonModuleにアクセスすることができます。 例えば、アセットの定義のサブネットワークにあるノードのPythonパラメータエクスプレッションは、hou.node("..").hdaModule().foo()を使用することができます。

アセット内に多くのPythonコードがあり、複数のモジュール内へ整理したい場合、他のHDAセクションを作成して、これらのモジュールを保存することができます。 例えば、Pythonコードを含むFooModuleセクションを作成することができます。 次に、メインのPythonModuleセクションで、以下のコードを使用して、FooModuleのコンテンツをfooとしてインポートします:

import toolutils
foo = toolutils.createModuleFromSection('bar', kwargs['type'], 'FooModule')

Note

Houdini18.0から、createModuleFromSection関数は、HDAセクション内にPython3スタイルのprintステートメントが必要になりました。

一般的には、これはHDAセクション内のprintステートメントが丸括弧で引数を閉じなければならないことを意味しています。

print("Hello world!")

print "Hello world!"

ディスク上のモジュール

Houdiniは、Pythonのパスに$HOUDINI_PATH/pythonX.Xlibsディレクトリを自動的に追加します。 これは、これらのディレクトリ内にあるパッケージやモジュールをPythonコードにインポートすることができるという意味です。

他の場所のパッケージやモジュールをpythonで使用できるようにするには、$PYTHONPATH環境変数を使用するか、Pythonでsys.pathリストを修正します。

例えば、$HOUDINI_USER_PREF_DIR/python2.7libs/mystuff.pyのモジュールは、以下のコードを使用してインポートします:

import mystuff

mystuff.my_function()

スクリプトを1行に維持したいのであれば(例えば、パラメータエクスプレッションやコールバック)、Pythonの__import__ビルトイン関数を使用することで、そのモジュールを取得して即座にそのアトリビュートにアクセスすることができます:

__import__("mystuff").my_function()

デジタルアセットイベントハンドラー

Pythonで、デジタルアセットイベントハンドラーを書き込むことができます。 Type Properties WindowScripts ページでイベントハンドラー(例えば、“On Created”)を作成後、必ず Edit as を“Python”に設定してください。

イベントスクリプトは、kwargsというグローバル辞書変数のイベントパラメータにアクセスすることができます。 例えば、hou.Nodeオブジェクトをイベントに関与させるには、kwargs["node"]を使用します。

Houdiniがファイル名を必要とする場所に埋め込まれたスクリプトを参照する方法に関しては、埋め込みファイルを参照する方法を参照してください。

Tip

これらのハンドラーは、 特定のノードタイプ でイベントが発生した時に実行するスクリプトをセットアップすることができます。 どの アセットでもアセットライブラリでもロードまたはアンロードされた時に実行する全般的なアセットイベントハンドラーをセットアップすることもできます。

Note

Type Propertiesインターフェース内でハンドラーを作成する時のデフォルト言語は今ではPythonになりましたが、 スクリプトを介してハンドラーセクションを作成する 場合のそのデフォルト言語は今でもHScriptです。

スクリプトを介してアセットセクションのデフォルト言語をPythonに設定するには、HDADefinition.setExtraFileOptionメソッドを使用して、EventName/IsPythonオプションをTrueに設定します:

on_created_body = """
from my_studio import register_stats
register_stats.createdSpecialNode()
"""

# アセット定義の参照を取得します。
hdadef = hou.nodeType("mystudio::Sop/flamingo::2.0").definition()
# アセット上にイベントハンドラーセクションを作成します。
hdadef.addSection('OnCreated', on_created_body)
# そのセクションに"IsPython"オプションを設定します。
hdadef.setExtraFileOption("OnCreated/IsPython", True)

イベントタイプ

以下のイベントは、スクリプトをトリガー(発動)することができます。

Before First Create (PreFirstCreate)

シーン内に 初めてのノードタイプのインスタンス が作成された時に実行されます。 これには、Houdiniがディスクからシーンファイルをロードした時にノードを作成する時も含みます

例えば、シーンにジオメトリオブジェクトがない環境でジオメトリオブジェクトを追加すると、これは、そのジオメトリオブジェクトタイプに対してPreFirstCreateハンドラーをトリガーします。 さらにジオメトリオブジェクトを追加しても、このイベントはトリガー しません

これは、アセットのインスタンスを1つ以上動作させるのに必要な環境をセットアップするのに役立ちます。 例えば、テクスチャマップを必要な場所にコピーしたり、環境変数を設定します。

  • Houdiniはhou.hipFile.isLoadingHipFile関数を使ってファイルを読み込んでいるので、スクリプト内でノードが作成されているかどうかをチェックすることができます。

  • このスクリプトで実行されたアクションをユーザがUndoすることはできません。このスクリプトによって加えられた変更をUndoしたいのであれば、PostLastDeleteハンドラー(以下参照)にそれ相当のコードを含めます。

On Created (OnCreated)

ユーザが新しいノードタイプのインスタンスを作成した後に実行されます(これは、シーンファイルをロードした時 ではありません 。下記のOnLoadedを参照)。

これを使用することで、例えば、ノードに変更(Spareパラメータを追加するなど)を加えた時にHoudiniに自動的に保存させるようにすることができます。

On Loaded (OnLoaded)

Houdiniがディスクからシーンファイルをロードした時にノードタイプのインスタンスが作成された後に実行されます(これは、ユーザがネットワークエディタ内にノードを作成した時 ではありません 。上記のOnCreatedを参照)。 これは、ノードをクリップボードからペーストした時にも実行されます。

他のアセットの内容の一部 としてノードをロードした時には実行 されません 。アセット内のノードをロードした時に何かの処理をさせたいのであれば、そのアセットのロードハンドラー内にそのコードを記述してください。

これは、すべてのノードがロードされた 後で 各ノードに対して実行されるので、このスクリプトは、シーン全体を見ることになります。

On Updated (OnUpdated)

共有の定義が変更されたためにアセットを更新する時には、そのアセットの各インスタンスに対して実行されます。

On Deleted (OnDeleted)

ノードタイプのインスタンスが削除される前に実行されます(その時点では、まだそのノードは存在します)。ユーザは新しいファイルを開始したり、ファイルを開いたり、終了したりするので、 これは、Houdiniがそのノードを“アンロード”する時を含みます

  • Houdiniはhou.hipFile.isShuttingDown関数(その名前に関わらず、Houdiniが新しいファイルを開始する時、ファイルを開く時、Houdiniを終了する時に、この関数はTrueを返します)を使ってシーンをアンロードするので、スクリプト内でノードが削除されているかどうかをチェックすることができます。

  • このイベントハンドラーとは関係なく、各ノードインスタンスにはそれぞれ独自の“Deletionスクリプト”を持たせることができます。ユーザは、パラメータエディタから ▸ Edit Deletion Script を選択することで、そのDeletionスクリプト(HScriptコマンド言語のみ)を編集することができます。OnDeletedハンドラーは、ノードにそのDeletionスクリプトが存在したら、それぞれの“Deletionスクリプト”の 前に 実行されます。

After Last Delete (PostLastDelete)

ノードタイプの 最後のインスタンス がシーン内で削除された後に実行されます。 ユーザは新しいファイルを開始したり、ファイルを開いたり、終了したりするので、 これは、Houdiniがそのノードを“アンロード”する時を含みます

これは、PreFirstCreateスクリプト(上記参照)による変更をクリーンアップするのに役立ちます。

  • ユーザは、このスクリプトで実行されたアクションをUndoすることができません。

  • Houdiniはhou.hipFile.isShuttingDown関数(その名前に関わらず、Houdiniが新しいファイルを開始する時、ファイルを開く時、Houdiniを終了する時に、この関数はTrueを返します)を使ってシーンをアンロードするので、スクリプト内でノードが削除されているかどうかをチェックすることができます。

On Input Changed (OnInputChanged)

このタイプのノードの入力が接続された時、接続解除された時、切り替えられた時に実行されます。 スクリプトにkwargs["input_index"]を使用することで、変更された入力の番号(0から始まります)を取得することができます。

On Name Changed (OnNameChanged)

ユーザがこのタイプのノードの名前を変更した後に実行されます。スクリプトにkwargs["old_name"]を使用することで、前の名前の文字列を取得することができます。

これは、例えば、名前や外部ストレージのパスによってノードに何かのインデックスを付けて、外部インデックスを最新に維持する時に役立ちます。

On Install (OnInstall)

このノードタイプをセッションにインストールした時に実行されます。

Note

HDAノードタイプに変更を保存すると、まず最初に古いHDAノードタイプがアンインストールされてから、最新の変更が反映された新しいHDAノードタイプがインストールされるので、On UninstallとOn Installのスクリプトが発動されます。

On Uninstall (OnUninstall)

このノードタイプをセッションからアンインストールした時に実行されます。

Note

HDAノードタイプに変更を保存すると、まず最初に古いHDAノードタイプがアンインストールされてから、最新の変更が反映された新しいHDAノードタイプがインストールされるので、On UninstallとOn Installのスクリプトが発動されます。

Sync Node Version (SyncNodeVersion)

これは、古いバージョン管理システムの一部で、古いバージョンのノードを自動的に最新バージョンに変換できる“アップグレード”ハンドラーを記述することができます。 このシステムは、新しいパラメータの追加などの小さな規模で上位互換のある変更に対して役立ちます。 詳細は、2つのタイプのアセットバージョン管理を参照してください。

これは、シーンファイルを保存して以来、アセットインスタンスをロードして、そのアセットの Version フィールドに変更があった時に実行されます。 これは、ノードのパラメータがロードされて解決された後で、且つ、OnLoadedまたはOnUpdatedのイベントハンドラーの前に実行されます。

スクリプトにkwargs["old_version"]kwargs["current_version"]を使用することで、古いバージョンと現行バージョンの文字列を取得することができます。

このハンドラーは、(kwargs["node"]の参照を使って)ノードが現行バージョンに合うように変更する処理を行ないます。つまり、アセットを更新する度に、そのスクリプトを更新する必要があります。

(hou.Node.syncNodeVersionIfNeededを使用することで、手動でノードのアップグレードスクリプトをトリガーすることができます。)

スクリプト変数

ハンドラースクリプトは、グローバルのkwargs辞書を使った環境で実行されます。以下のテーブルには、色々なイベントタイプに対して利用可能な辞書のアイテムを載せています:

キー

イベント

node

ノードインスタンスのhou.Node参照。

OnCreated, OnUpdated, OnInputChanged, OnNameChanged, OnDeleted, SyncNodeVersion

type

ノードタイプのhou.NodeType参照。

OnLoaded, PreFirstCreate, OnCreated, OnUpdated, OnInputChanged, OnNameChanged, OnDeleted, PostLastDelete, UpgradeLoadedParms, PythonModule

old_name

HDAインスタンスの古い名前を含む文字列。

OnNameChanged

input_index

接続/切断された入力のインデックス。

OnInputChanged

current_version

現行のHDAバージョンの文字列。

SyncNodeVersion

old_version

ノードが最後に保存されたHDAバージョンの文字列。

SyncNodeVersion

ノードのイベントハンドラーファイル

イベントハンドラースクリプトは、アセット内への埋め込みだけでなく、Houdiniパスにもファイルとして格納することができます。 デジタルアセットのイベントに反応させるために、おそらくType Propertiesウィンドウのビルトインのイベントハンドラーサポートを使用したいことでしょう。 しかし、スクリプトファイルを使用すると、以下のメリットがあります:

  • これらのディスク上のイベントスクリプトは、アセットとビルトインノードタイプの どちらでも 動作します。

  • どの ノードタイプに対しても実行できる“グローバル”なイベントハンドラーファイルをインストールすることができます。

Houdiniは、以下のパターンに合致したファイルを探します:

  • HOUDINIPATH/scripts/category/nodename_event.py(例えば、$HOUDINI_USER_PREF_DIR/scripts/obj/geo_OnCreated.py)のパターンに合致したファイルは、指定したイベントタイプが指定したタイプのノードで発生した時に実行されます。

    ネームスペース/バージョンを含んだ複雑なノードタイプ名に関しては、その名前の::セパレータを 1個のハイフン に置換してください。

    例えば、ノードタイプ名がedu.toronto3d::foo::2.0なら、edu.toronto3d-foo-2.0_OnCreate.pyのようなファイル名を使用します。

  • HOUDINIPATH/scripts/event.pyのパターンに合致したファイルは、指定したイベントタイプが どの ノードにも発生した時に実行されます。

Note

このスクリプトのパスは、“テーブル形式”ではなくノードカテゴリの“ディレクトリ”形式を使用します。 例えば、ObjectではなくobjSopではなくsopRopではなくoutです。 Houdiniでこれらの形式が異なっている不整合性は、歴史的に残念でなりません。

イベントスクリプトファイルは、ビルトインのkwargsグローバル辞書変数を使った環境で実行します。 スクリプト内で新しく作成されたノードのhou.Nodeオブジェクトを取得するには、kwargs["node"]を使用します。 この辞書で利用可能なアイテムに関する情報は、上記のスクリプト変数を参照してください。

Note

Houdiniは、HOUDINIPATH/scripts/category/nodename.py(イベント接尾辞なし。例えば、$HOUDINI_USER_PREF_DIR/scripts/obj/geo.py)に合致したファイルも検索します。 これは、ファクトリーノードの“Creationスクリプト”で、(紛らわしいですが)ノードのOnCreatedハンドラーと同じではありません。

古いドキュメントやチュートリアルでは、ノードが作成される時にコードを実行する方法として、このタイプのファイルのことを説明しています。 しかし、これはデジタルアセットでは動作しません。 今では、代わりにOnCreatedイベントスクリプトを使用します 。 “Creationスクリプト”の内部的な実装の詳細は、何の予告もなく変更されることがあるので注意してください。

個々のノードのイベントハンドラー

個々のノードインスタンス に対してイベントハンドラーをセットアップすることができます。 アセットのどのインスタンス に対してもイベントが発生した時にトリガーされるアセットイベントとは異なり、 これらのハンドラーは、それらをセットアップした 特定のノード に対してイベントが発生した時にのみ実行されます。

ノードに対してイベントハンドラーをセットアップする方法は、hou.Node.addEventCallbackを参照してください。 反応させることができるイベントのタイプは、hou.nodeEventTypeを参照してください。

全般的なアセットイベントハンドラー

アセットライブラリ を変更するハンドラーをセットアップすることができます。例えば、アセットの作成や削除、または、アセットライブラリのインストールやアンインストールの時です。

アセットライブラリのイベントハンドラーをセットアップする方法は、hou.hda.addEventCallbackを参照してください。 反応させることができるイベントのタイプは、hou.hdaEventTypeを参照してください。

シェルフツールスクリプト

ユーザがシェルフツールをクリックした時に実行するスクリプトをPythonで記述します。詳細は、ツールスクリプトを記述する方法を参照してください。

パラメータコールバックスクリプト

ノード上のパラメータ値が変更された時にコールされるPythonスクリプトを記述することができます。

デフォルトのコールバック言語が HScriptであれば、それを Pythonに変更することを忘れないでください

イベントスクリプトは、kwargsというグローバル辞書変数のイベントパラメータにアクセスすることができます。 例えば、パラメータを含むhou.Nodeオブジェクトを取得するには、kwargs["node"]を使用し、パラメータのhou.Parmオブジェクトを取得するには、kwargs["parm"]を使用します。

Tip

コールバックスクリプトを実装する最も便利な方法は、アセットのPythonモジュール内の“実際の”コールバック関数をコールする1行のPythonコードを記述して、そのコードにkwargs辞書を渡すことです。

例えば、アセットのPythonモジュール内で、オプションの辞書と現行ノードインスタンスを表現したhou.Nodeオブジェクトを受け取るmy_callback関数を作成します。 次に、パラメータのPython Callback の行で、以下のコードを使って、そのモジュールからその関数をコールするように設定します:

hou.pwd().hm().my_callback(kwargs, hou.pwd())

(hou.pwd()は、現在クックされているノードを返します。hou.Node.hmは、アセットのPythonモジュールを返します。)

以下の表は、パラメータコールバックに対するkwargs辞書のコンテンツを一覧にしたものです。

parm

コールバックスクリプトが呼び出されたhou.Parmオブジェクト。

node

パラメータを含むhou.Nodeオブジェクト。

parm_name

hou.Parmの名前。この値は、kwargs['parm'].name()と同じです。

script_parm

パラメータを含むhou.ParmTupleの名前。これは、kwargs['parm'].tuple().name()と同じです。このキーは、HScriptとの逆互換用のみに意味を持つscript_parmです。

script_value

パラメータの値です。このエントリは、kwargs['parm'].eval()と同じです。

script_value0, script_value1, …

パラメータタプルの値です。これらのエントリは、[p.eval() for p in kwargs['parm'].tuple()]と同じです。

script_multiparm_index

パラメータがmultiparmインスタンスの場合のmultiparmの番号(それ以外は-1)。尚、multiparmのフォルダブロックリストにある“First Instance”パラメータにより、multiparmのナンバリングをどこから開始するかが決まります。通常、1番目のmultiparmは1の番号が付けられ、2番目は2になり、以降、同様に番号が付けられます。

script_multiparm_nesting

このパラメータがmultiparmインスタンスでない場合、この値は0です。multiparmインスタンスであるが、インスタンスが他のmultiparmインスタンス内に含まれていない場合、値は1です。それ以外の場合、multiparmインスタンスが、multiparmインスタンスの1つのレベル内にネストされていると、ネストレベルの数を返します。

script_multiparm_index2, …, script_multiparm_indexN

これらの値は、script_multiparm_nestingが2以上の場合のみ使用することができます。これらの値は、外側のmultiparmインスタンスのmultiparmインスタンスの数に対応します。例えば、multiparmインスタンス3が、インスタンス4内にあるインスタンス5の中にネストされている場合、script_multiparm_indexは3, script_multiparm_index2 は5, script_multiparm_index3は4になります。

パラメータメニュースクリプト

Pythonを使用して、ダイナミックパラメータのメニューを生成することができます。

Tip

Houdiniは、ユーザがメニューを開いた時やパラメータが評価された時に、できるだけ単純で高速になるように、このスクリプトを実行します。

Pythonメニュースクリプトが1行しか含まれていない場合、Houdiniは、そのスクリプトをPythonエクスプレッションとして評価します。 複数行の場合は、Houdniはそれを関数本体として評価し、returnを使って、そのリストを返さなければなりません。 この挙動は、Pythonパラメータのエクスプレッションに対するものと同じです。

このスクリプトは、平坦な文字列のリストを返さなければなりません。つまり、偶数の項目がトークンで、奇数の項目がラベルです。例:

["1", "Light", "50", "Medium", "100", "Heavy"]

Tip

おそらくこれまでは以下のように、Pythonでタプルを使って値のペアを扱っていたことでしょう:

pairs = [("1", "Light"), ("50", "Medium"), ("100", "Heavy")]

そのリストを生成して扱っている時に、以下のテクニックを使うことで、Houdiniが必要とする“平坦な”リストに変換することができます:

return [item for sublist in pairs for item in sublist]
# -> ["1", "Light", "50", "Medium", "100", "Heavy"]

例えば、PythonによりObjectレベルのデジタルアセットのメニューを生成するには:

result = []
for tool in hou.shelves.tools().values():
    node_type_name = tool.toolMenuOpType(hou.paneTabType.NetworkEditor)
    if (node_type_name.startswith("Object/") and
            'Digital Assets' in tool.toolMenuLocations()):
        result.append(tool.name())
        result.append(tool.label())
return result

アセットでは、アセットのPythonModuleセクションでメニューを関数に生成するコード(例えば、generate_menu関数)を置くことができ、 hou.pwd().hdaModule().generate_menu()のメニュースクリプトでその関数をコールすることができます。

メニュースクリプトは、kwargsグローバル辞書変数のコールパラメータにアクセスすることができます。 例えば、そのパラメータを含んだhou.Nodeオブジェクトを取得するにはkwargs["node"]を使用し、そのパラメータのhou.Parmオブジェクトを取得するにはkwargs["parm"]を使用します。

以下のテーブルには、パラメータコールバックのkwargs辞書の内容を載せています:

parm

呼び出されたコールバックスクリプトのhou.Parmオブジェクト。

node

パラメータを含んだhou.Nodeオブジェクト。

script_multiparm_index

パラメータがmultiparmのインスタンスだった場合は、multiparmの番号です(そうでない場合は-1)。 multiparmのフォルダブロックリスト上の“First Instance”パラメータは、multiparmに振り始める番号を決定します。 通常では、最初のmultiparmには1、次が2というように番号が振られます。

script_multiparm_nesting

このパラメータがmultiparmのインスタンスでない場合は、この値は0です。multiparmインスタンスの場合、且つ、そのインスタンスが他のmultiparmインスタンス内に含まれていない場合は、この値は1です。 それ以外の場合、multiparmインスタンスが1レベルのmultiparmインスタンス内に入れ子化されていれば、その入れ子のレベルの番号を返します。

script_multiparm_index2, …, script_multiparm_indexN

これらの値は、script_multiparm_nestingが2以上の時にのみ利用可能です。これらの値は、外側のmultiparmインスタンスのmultiparmインスタンス番号に相当します。 例えば、multiparmインスタンス3がインスタンス4の中にあるインスタンス5の中に入れ子化されていれば、script_multiparm_indexは3、script_multiparm_index2は5、script_multiparm_index3は4になります。

ボタンストリップ/アイコンストリップのパラメータのスクリプト化

“ボタンストリップ(ボタンの帯)”と“アイコンストリップ(アイコンの帯)”のパラメータを(アセット上またはSpareパラメータとして)作成することができます。 これらのパラメータを使って、クリック可能なアクションボタンの帯、または、相互に排他的に選択可能なオプションの帯、個々にオン/オフ可能な独立したオプションの帯を用意することができます。

  • ストリップ(帯)パラメータを作成する時、そのパラメータを“Normal”(相互に排他的: 同時にどちらかのアイテムしか選択できません)または“Toggle”(各アイテムを個々に有効または無効にすることができます)に設定します。

  • “Normal”ストリップの値を読むのは簡単です。hou.Parm.evalは、選択したアイテムのインデックス(0から始まります)を返します。

    そのインデックスをメニュー内にセットアップした各アイテムに該当するトークンに変換するには、そのパラメータテンプレートからトークンのリストを取得し、そのリストにインデックスを指定します:

    def get_selected_token(parm):
        # 現在選択されているアイテムを読み込みます。
        selected = parm.eval()
    
        # パラメータテンプレートからメニュートークンのリストを取得します。
        tokens = parm.parmTemplate().menuItems()
    
        # 選択したアイテムに該当するトークンが返されます。
        return tokens[selected]
    
    # パラメータコールバックスクリプトで、以下のように記述します...
    token = get_selected_token(kwargs["parm"])
    
  • “Toggle”ストリップの値を読むと、 ビットフィールド が返されます。これは、各ビットがそのストリップ内のボタンのオン/オフ状態を2進数で表現した数値です。

    あなたがプログラミングに精通していないようであれば、おそらく非常に難しく聞こえますが、あなたが必要とするすべてのものは、以下のようにユーティリティ関数として用意しているので、ビットフィールドを使いやすい形式に変換することができます。

    # numberのn番目の位置の"ビット"が"オン"なのかどうかをテストする一般的な形式は、
    # 以下のとおりです:
    # number & (1 << n)
    
    def strip_to_tokens(parm):
        # ボタンストリップ/アイコンストリップのパラメータインスタンスの参照を受け取り、
        # オンになっているボタンに相当するトークンのリストを返します。
    
        bitfield = parm.eval()
        tokens = parm.parmTemplate().menuItems()
        return [token for n, token in enumerate(tokens) if bitfield & (1 << n)]
    
    def is_n_selected(bitfield, n):
        # bitfieldのn番目の位置(0から始まります)が"オン"ならTrueを返します。
    
        return bitfield & (1 << n)
    
    def bitfield_to_list(bitfield, size=32):
        # bitfieldを受け取り、そのbitfieldの各位置が"オン"なのか"オフ"なのかを示した
        # ブール(TrueまたはFalse)のリストを返します。
        # ("size"は、ストリップ内のボタンの数です。)
    
        return [bitfield & (1 << n) for n in range(size)]
    
  • “toggle”ストリップの値をエクスプレッションで扱うのは非常に難しいです。何かしらの理由で、Toggleストリップの設定がエクスプレッションを駆動させる必要がある場合、その回避策は、そのストリップに、ストリップの設定に基づいて非表示パラメータに単純な値を設定するコールバックスクリプトを渡すことです。次に、ストリップの代わりに、そのエクスプレッション内でその非表示パラメータを参照します。

  • アクションアイテムのツールバーのように動作するボタンストリップ/アイコンストリップを作成するには、“Toggle”ストリップを作成して、そのストリップに、オンになったアイテムを読み込んで、それに基づいて何かのアクションを実行し、 直ぐにそのオプションを再度オフにする コールバックスクリプトを渡すことです。

    例:

    import math
    
    # アクションボタンのように動作するボタンを作成するボタンストリップコールバックスクリプト。
    # このスクリプトは、メニュータイプが"Toggle"に設定されていることを前提にしています。
    
    # オンになったボタンを読み込みます。
    parm = kwargs["parm"]
    bitfield = parm.eval()
    # ビットフィールド内の1項目だけを選択してください。ビットフィールドの基底が2の対数は、
    # 選択したボタンのインデックスが得られます。
    selected_index = int(math.log(bitfield, 2))
    
    # クリックしたボタンに基づいて、ここで何かをします。
    
    # 選択したボタンをクリアします。
    # そのパラメータのビットフィールド値をゼロに設定すると、すべてのボタンがオフになります。
    parm.set(0)
    

key-valueパラメータボタンスクリプト

Pythonを使用することで、ユーザがボタンをクリックした時にkey-valueパラメータに対して新しいキーと値のペアを生成することができます。 このスクリプトは、(例えば、hou.ui.selectFromListまたはhou.ui.selectFromTreeを使って)ユーザがプリセットを選択するインターフェースを表現することができます。 これは、(key, value)タプルを返さなければなりません。

フィールド内の値が1行なら、それはPythonエクスプレッションとして評価されます。 2行以上の場合、関数ボディとして扱われるので、最後にreturnステートメントを使って値を返す必要があります。 これは、他のパラメータコールバックと同じです。

  • このスクリプトは、非文字列の戻り値をタプルで返すことができます。Houdiniは、その戻り値を文字列に変換します。

  • このスクリプトは、kwargsグローバル辞書変数を含んだ環境で実行します。kwargs["node"]には、ノードのhou.Nodeが含まれます。kwargs["parm"]には、キーと値のパラメータのhou.Parm参照が含まれます。

  • ユーザが選択をキャンセルすると、このスクリプトは、空っぽの文字列を("", "")タプルで返します。

  • アセットのPythonモジュール内のコールバックを制御するための関数を定義することを推奨します。

    例えば、アセットのPythonモジュールに以下の関数を記述することができます:

    def choose_env_variable():
        """
        ユーザに環境変数名を選択させて、key-valueパラメータに名前と値を返します。
        """
    
        # OSから環境変数辞書を取得します。
        import os
        names = os.environ.keys()
        # Houdini UI関数を使って、ユーザにリストから選択するように促します。
        results = hou.ui.selectFromList(names)
        if not results:
            # ユーザが何も選択しませんでした。
            return ("", "")
        # アイテムを1個だけ選択するようにします。ユーザが複数のアイテムを選択したら
        # 1番目のアイテムを使用します。
        chosen_index = results[0]
        # hou.ui.selectFromList()は、選択したアイテムの番号を返します。
        # その番号を名前に変換します。
        chosen_name = names[chosen_index]
        # 変数名のタプルとその変数の値を返します。
        return (chosen_name, os.environ[chosen_name])
    

    次に、これを Chooser callback フィールドに使用してコールします:

    kwargs["node"].hdaModule().choose_env_variable()
    

    コールバック関数にノード/パラメータの参照が必要な場合、それらをその関数に渡すことができます:

    kwargs["node"].hm().choose_env_variable(kwargs["node"], kwargs["parm"])
    
  • コールバック関数は、インタラクティブにユーザに何かを選択させるようにする必要が ない ことに注意してください。現在の条件に基づいてkey-valueペアを追加するボタンを持たせることができます。

    例えば、これを Chooser callback フィールドに記述します:

    (hou.frame(), hou.time())
    

    ユーザがボタンをクリックすると、このPythonエクスプレッションは、現在のフレーム番号と現在の時間を含んだタプルを評価します。

バックグラウンド処理

ユーザが作業している最中に“バックグラウンドで”実行されるPythonコードをセットアップすることができます。

  • hou.ui.addEventLoopCallbackを使用することで、そのUIがビジーでない時にHoudiniがコールする関数をセットアップすることができます。

    このコールバック関数は、 高い頻度 (1秒あたり20回程度)でコールされます。この関数は、UIを遅くさせないように実行速度を非常に速くしてください。

  • Qt QTimerオブジェクトを使用することで、今後実行されるコードをスケジューリングすることができます。特定の間隔であるコードを実行させたいのであれば、そのコード自体に他のQTimerをセットアップすることができます。

Tips

  • hou.findFile()を使用して、Houdiniのパスでファイル名を検索することができます。

  • アセット内に埋め込まれたファイルから内容を読み込むには、hou.HDADefinition.sectionsを使ってHDASectionオブジェクトのセクション名の辞書を取得してから、hou.HDASection.contentsを使ってそのセクションの内容を取得します。

    my_assetオブジェクトに埋め込まれたexampleファイルの内容を読み込む例:

    objects = hou.nodeTypeCategories()["Object"]
    my_asset = objects.nodeTypes()["my_asset"]
    my_asset_def = my_asset.definition()
    section = my_asset_def.sections()["example"]
    contents = section.contents()
    

    ショートカットとしてopdef:パスを使用することで、アセットセクションがまるでファイルであるかのようにそれらのセクションを読み込むことができます:

    content = hou.readFile("opdef:Object/my_asset?example")
    
  • hou.sessionからグローバルスコープのPython変数にアクセスするには、__main__をインポートします。例えば、xがグローバルスコープの変数であれば、その変数に__main__.xとしてアクセスすることができます。

    この方法を使用して、パラメータエクスプレッション, HDA Pythonモジュール, Pythonボタンコールバック, シェルフスクリプトなどからPythonのグローバル変数にアクセスすることができます。

See also

Pythonスクリプト

はじめよう

次のステップ

リファレンス

  • hou

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

導師レベル

Python Viewerステート

Pythonビューアハンドル

プラグインタイプ