Houdini 20.0 Pythonスクリプト

ネットワークエディタの拡張

On this page

ネットワークエディタのほぼすべてのユーザ操作は、$HHP内の名前がnodegraphで始まるモジュールに定義されているPythonコードで処理されています。 すべてのユーザアクションと色々なアプリケーションが生成するイベントは、その処理ができるように、このPythonコードに送られています。

Tip

$HHP環境変数は、Houdiniのインストールディレクトリ内のPythonライブラリを含んだサブディレクトリ($HFS/houdini/pythonX.Ylibs/)を指しています。

コードの基本構成としては、すべてのイベントが現在のコンテキストモジュール(デフォルトではnodegraph.py)のhandleEvent()という関数に渡されるようになっています。 hou.NetworkEditor.pushEventContextをコールすることで、現在のコンテキストを変更することができます。 このメソッドは、そのイベントで必要な事をなんでも実行することができます。 nodegraph.pyで使われている手法では、このイベントをPythonコルーチンに渡しています。 このPythonコルーチンはループ内でyieldステートメントを使用して、次のイベントが来るまでルーチンの実行を効率的に一時停止します。 このようにしてコルーチンを使用することで、グローバル変数を使わずにコードが、あるイベントから次のイベントまでの状態を簡単に維持することができます。 yieldステートメントを使用することで、コードを単純なループとして記述することもできます。各イベントが来ると、中断した箇所から自動的にコードが再開されます。

コルーチンのイベンドハンドリングループ内では、ある特別なイベント(例えば、Escapeが押されたり、ユーザがネットワークを切り替えたことを示すイベント)がループ内で直接処理されます。 ユーザイベント(例えばマウスボタンを押す)は、createEventHandlerという名前の関数に送られます。 この関数の目的は、ユーザ操作の開始(例えば2つのノードを接続したり、ノードを動かす)を識別することで、通常では(必ずしもそうだとは限りませんが)キーボードイベントやマウスボタンプレスを意味します。 ユーザ操作が開始されると、この関数は(nodegraphbase.pyで定義された)EventHandlerオブジェクトを返します。 今後のイベントは、このイベントハンドラーオブジェクトのhandleEvent()メソッドに直接送られます。 このメソッドはcreateEventHandlerと同様にEventHandlerオブジェクトも返します。 ユーザ操作が完了(オペレーションを開始したマウスボタンを離した時がそうです)すると、handleEventNoneを返し、次のユーザイベントが他のユーザ操作の開始を探すためのcreateEventHandlerをコールします。 ユーザ操作がまだ継続(マウスドラッグのようなイベントがそうです)していると、このメソッドはselfを返し、イベントの受信を継続します。 または場合によっては、別のEventHandlerオブジェクトが返されることがあります。これは、ユーザ操作がより専門的になったり、別の操作に移り変わったことを示します。 理由が何であれば、この新しいEventHandlerは、イベントが受信された時にイベントの受信を開始します。

イベントタイプ

handleEvent関数に送られる各イベントは、$HHP/canvaseventtypes.pyモジュールで定義されているイベントタイプのどれかです。

InitializationEvent

ネットワークエディタを初めてHoudiniインターフェース内に作成した時にそのネットワークエディタに一度だけ送られます。

editor

イベントを受信するhou.NetworkEditor

eventtype

イベントを意味した文字列。initializationは、このクラスにある唯一の値です。

ContextEvent

ネットワークエディタを別のネットワークロケーションに変更(例えば/objから/obj/geo1に移動)した時に送られます。

editor

イベントを受信するhou.NetworkEditor

eventtype

イベントを意味した文字列。contextは、このクラスにある唯一の値です。

oldcontext

以前のネットワークのフルパスを含んだ文字列。

context

新しいネットワークのフルパスを含んだ文字列。

ContextClearEvent

ユーザが新しくファイルを作成したり別のhipファイルを読み込んだことで、現在読み込まれているhipファイルがクリアされた時に送られます。 ネットワークエディタにhipファイル固有のデータをクリアする機会が与えられます。

editor

イベントを受信するhou.NetworkEditor

eventtype

イベントを意味した文字列。contextclearは、このクラスにある唯一の値です。

context

現在のネットワークのフルパスを含んだ文字列。

MouseEvent

ユーザがマウスを動かしたり、マウスボタンを押したり離したり、スクロールホイールを動かしたり、マウスボタンをダブルクリックした時に送られます。

editor

イベントを受信するhou.NetworkEditor

eventtype

イベントを意味した文字列。

mouseenter

マウスカーソルがネットワークエディタペイン内に入りました。

mousemove

マウスカーソルがネットワークエディタペイン内で移動しました。

mouseexit

マウスカーソルがネットワークエディタペインから出ました。

mousedown

マウスボタンが押されました。

mousedrag

マウスカーソルがボタンが押されたまま移動しました。

mouseup

マウスボタンが離されました。

doubleclick

マウスボタンがダブルクリックされた。このイベントは常にmousedown, mouseupや他のmousedownイベントの後に発生します。 マウスボタンが2回離されると、2回目のmouseupイベントは、このイベントの後に発生します。

mousewheel

マウスホイールが回されました。

mousepos

マウスカーソルの現在位置をスクリーン座標で保持したhou.Vector2オブジェクト。

mousestartpos

マウスボタンが現在押されていると、これはマウスボタンが押された時のマウスを位置を保持したhou.Vector2オブジェクトです。

mousestate

マウスボタンの現在の状態を示したMouseStateオブジェクト。

dragging

マウスボタンが押されて、そのボタンが押された状態でユーザがドラッグ操作をしていると見なすことができるほど十分に大きく移動したらTrueになるbool値。 ユーザがマウスボタンを押して、不意で数ピクセルだけマウスを動かしてしまっても(タブレット入力でよくある問題)、この値はFalseのままになるはずです。

modifierstate

キーボードの修飾キーの現在の状態を示したModifierStateオブジェクト。

located

マウス下のユーザインターフェースガジェットを示したNetworkComponentオブジェクト。

selected

マウスボタンが現在押されていると、これは、マウスボタンが押された時のそのマウス下のユーザインターフェースガジェットを示したNetworkComponentオブジェクトです。

wheelvalue

mousewheelイベントでない場合はゼロ、mousewheelイベントの場合はそのマウスホイールの移動の大きさと方向を示した値になる整数。

time

このイベントのウィンドウシステムのタイムスタンプを返します。 このタイムスタンプは、例えばウィンドウシステムが起動した時刻などの任意の時点からの秒単位です。

KeyboardEvent

ユーザがキーボードのキーを押した時に送られます。 keyhitイベントの代わりに特定のキーを登録して、別のkeyupとkeydownのイベントを生成することもできます。 これは、volatile(一時的な)ステートの実装に必要です。

editor

イベントを受信するhou.NetworkEditor

eventtype

イベントを意味した文字列。

keyhit

キーがキーボード上で押されました。キーを十分に長く押したままにすると、設定したキーのリピートレートに基づいて一連のkeyhitイベントを生成することができます。

parentkeyhit

ペインがList Modeの時にネットワークエディタペイン上でキーやメニューアイテムを選択した時の特殊ケースです。 つまり、ネットワーク自体が不可視でも、ネットワークエディタコードによって、まだキーを処理する機会があるということです。

keydown

hou.NetworkEditor.setVolatileKeysを使ってvolatileキーとして登録されたキーが押されました。

keyup

hou.NetworkEditor.setVolatileKeysを使ってvolatileキーとして登録されたキーが離されました。

key

押されたキーを示す文字列。この文字列は、実際にキーボードのキーを押してイベントが生成された場合は、キーそのものを示しています(Shift+HまたはCtrl+T)。 このイベントがメニューアイテムの選択によって生成された場合では、代わりにホットキーシンボル(h.pane.wsheet.jump)になります。 nodegraphdisplay.setKeyPrompt()関数は、ホットキーシンボルに対してキーをテストする(さらにユーザに何のキーが押されたのかを示せるようにペインの下部にテキストを促す)のに役立ちます。

rawkey

押されたキーそのままを示した文字列。 イベントがキーボード上の実際のキーを押すことで生成される場合、この文字列はキー自体の説明(Shift+HCtrl+T)となります。 それ以外の場合は空っぽの文字列となります。

今後は、イベントがキーボード上の実際のキーを押すことで生成された場合でも、keyがホットキーシンボルとなる予定です。 この文字列は、引き続き実際のキー自体の説明になります。

modifierstate

キーボードの修飾キーの現在の状態を示したModifierStateオブジェクト。

located

キーが押された時のマウス下のユーザインターフェースガジェットを示したNetworkComponentオブジェクト。

mousepos

マウスカーソルの現在の位置をスクリーン座標で保持したhou.Vector2オブジェクト。

mousestate

マウスボタンの現在の状態を示したMouseStateオブジェクト。

time

このイベントのウィンドウシステムのタイムスタンプを返します。 このタイムスタンプは、例えばウィンドウシステムが起動した時刻などの任意の時点からの秒単位です。

TimerEvent

hou.NetworkEditor.scheduleTimerEventをコールした後でTime Intervalが経過した時に送られます。

editor

イベントを受信するhou.NetworkEditor

eventtype

イベントを意味した文字列。timerは、このクラスにある唯一の値です。

timerid

タイマーを開始したhou.NetworkEditor.scheduleTimerEventコールによって返されたタイマーの固有の整数識別子。

ValueEvent

ユーザが hou.NetworkEditor.openNameEditor, hou.NetworkEditor.openCommentEditor, hou.NetworkEditor.openNoteEditorのコールを介してネットワークエディタに取り込まれた入力フィールドの編集を終えた時に送られます。

editor

イベントを受信するhou.NetworkEditor

eventtype

イベントを意味した文字列。editvalueは、このクラスにある唯一の値です。

valueid

入力フィールドを開いたコールが返すその入力フィールドの固有の整数識別子。

value

入力フィールドの最終文字列値。

ModalUIEvent

Houdini UIガジェット(通常ではTabメニュー)をネットワークエディタ上で開いたり閉じた時に送られます。

editor

イベントを受信するhou.NetworkEditor

eventtype

イベントを意味した文字列。

startmodalui

メニューが開かれました。

endmodalui

メニューが閉じられました。

interfacename

開かれているまたは閉じられているインターフェースガジェットの名前。現在のところ、唯一の値はTabMenuです。

ヘルパークラス

上記で定義されているイベントのいくつかは、マウス下のインターフェースコンポーネントまたはマウスボタンの現行状態を表わすためのヘルパークラスを使用しています。 これらのクラスは、$HHP/canvaseventtypes.pyにも定義されています。

MouseState

すべてのマウスボタンの状態を表わしています。

lmb

左マウスボタンが押された時にTrueに設定されるbool値。

mmb

中マウスボタンが押された時にTrueに設定されるbool値。

rmb

右マウスボタンが押された時にTrueに設定されるbool値。

ModifierState

キーボードのすべての修飾キーの状態を表わしています。

alt

Altキーが押された時にTrueに設定されるbool値。

ctrl

Ctrlキーが押された時にTrueに設定されるbool値。

shift

Shiftキーが押された時にTrueに設定されるbool値。

NetworkComponent

ネットワークエディタペインに表示されるUIエレメントを表わしています。

item

UIエレメントの一部であるhou.NetworkItem。これは、hou.Node, hou.NodeConnectionまたはその他のhou.NetworkItemサブクラスが該当します。

name

特定のUIエレメントを表わした文字列。利用可能な値は、itemフィールド内のオブジェクトのタイプに依存します。

index

UIエレメントをさらに洗練する整数。例えば、itemhou.Nodeで、nameinput(入力コネクタを示します)であれば、indexはどの入力コネクタなのかを示します。

座標空間

ネットワークエディタで使用されている座標系が2つあります。 1つ目の座標系が“ネットワーク”座標です。これは、ネットワークのネイティブの座標系です。 この座標系では、ノードタイルが一般的に約1ユニット幅です。 hou.Node.positionは、ネットワーク座標内のノードの左下コーナーの位置を返します。

2つ目の座標系がスクリーン座標系です。 この座標系では、(0, 0)がネットワークエディタペインの左下コーナーで、1ユニット1ピクセルです。

名前に“screen”または“mouse”を持たないほぼすべてのメソッドとデータは、ネットワーク座標で表現されています。 そして、hou.NetworkEditorには座標間を変換するためのメソッドがあります。 そのため、hou.NetworkEditor.posToScreenは、ネットワーク座標の位置をスクリーン座標の位置に変換します。 hou.NetworkEditor.visibleBoundsは、ネットワークエディタの可視領域をネットワーク座標で返し、 hou.NetworkEditor.screenBoundsは、その境界をスクリーン座標で返します。 ネットワークエディタをパンしてもスクリーン境界は変わりませんが、可視境界は変わります。

グローバルにイベントを傍受する

ネットワークエディタの挙動をカスタマイズするための最初の機会は、createEventHandler関数で用意されています。 イベントを見る前に、イベントはnodegraphhooks.createEventHandler()に送られます。 この関数のデフォルトの実装では、イベントが正常に処理されているかどうかを示す値を返します。 しかし、$HHP内のnodegraphhooksモジュールよりも先に見つかるようにPythonパス内に新しいnodegraphhooksモジュールを作成することができます。 進行中のユーザ操作がない限りは、どのイベントも傍受する機会があります(EventHandlerが既にアクティブなら、それらのイベントを傍受する機会はありません)。

この関数には、イベントオブジェクトと、通常では無視できるpending_actionsパラメータが渡されます。 この関数は、EventHandlerオブジェクトまたはNoneの後に、イベントが処理されたかどうかを示すbool値で構成されたタプルを返さなければなりません。 キーボードのキーが押されるなどのイベントの場合では、ユーザ操作は単一イベントで完了し、セットアップされたEventHandlerがなく、イベントの処理はもうこれ以上は行なわれないことを示す(None,True)を返します。

ホットキーのカスタム処理に加えて、フックのよく使用する他の使い方は、特定の操作の挙動を変更することです(例えば、特定の修飾キーを使ってノードをドラッグすると、単なるノードの移動以外の何かを行なうようにする)。 この場合、ほとんどのノードの挙動をそのままにしつつ、特定の環境下でユーザ操作をカスタマイズしたいことでしょう。 これを実現する最も良い方法は、ネットワークエディタコントロールモジュール内に既に存在する多くのEventHandlerサブクラスのどれかのサブクラスを作成することです。 そのサブクラスのhandleEventメソッド内では、ほとんどのイベントが基底クラスに委ねることができますが、特定のイベントを傍受して、目的の挙動に変更することができます。

HDAのイベントを傍受する

HDAのPythonModule内にcreateEventHandler関数を定義することもできます。 この関数は、グローバルのhook関数と同じパラメータを受け取り、同じ値を返しますが、 ユーザからのイベントが、HDAのインスタンスのノードタイルの一部のマウスイベントだった時にのみコールされます。 これによって、HDAを操作した時に特別な挙動(例えば、ダブルクリックイベントがノード内の特定の位置にダイブするようにカスタマイズ)を用意することができます。 PythonModuleはHDA定義の一部なので、これらのカスタム挙動は、グローバルのhook関数を変更することなくそのHDAがインストールされていれば、どこでも動作します。

サンプル

以下のコードは、ノードのダイブをするホットキーの挙動を変更して、現行ノードの代わりにマウス下のノードにダイブします。 このコードを$HHP/nodegraphhooks.pyに配置してください:

import hou
from canvaseventtypes import *
import nodegraphdisplay as display
import nodegraphview as view

def createEventHandler(uievent, pending_actions):
    if isinstance(uievent, KeyboardEvent) and \
       uievent.eventtype == 'keyhit':
        # これは、key hitイベントです。'dive in'キーをチェックしてください。
        editor = uievent.editor
        if display.setKeyPrompt(editor, uievent, 'h.pane.wsheet.jump'):
            # マウス下のノードを調べます。
            pos = uievent.mousepos
            items = editor.networkItemsInBox(pos, pos, for_select = True)
            for (item, name, index) in items:
                if isinstance(item, hou.Node) and item.isNetwork():
                    view.diveIntoNode(editor, item)
                    break
            # このイベントを処理しましたが、イベントハンドラーを返す必要はありません。
            # なぜなら、これは一度限りのイベントだからです。次に何か起きるのかは気にしません。
            return None, True

    return None, False

以下のコードは、⌃ Ctrl + ⇧ Shift + Hキーを傍受して、現行ノードをマウス下に移動させてネットワークエディタを固定ズームレベルに設定します。 このコードを$HOUDINI_USER_PREF_DIR/scripts/nodegraphhooks.pyに配置してください:

from canvaseventtypes import *

def createEventHandler(uievent, pending_actions):
    if isinstance(uievent, KeyboardEvent) and \
       uievent.eventtype == 'keyhit' and \
       uievent.key == 'Ctrl+Shift+H':
        # 現在の境界を取得します。
        screenbounds = uievent.editor.screenBounds()
        bounds = uievent.editor.visibleBounds()
        # ネットワークエディタ単位あたり100ピクセルのズームレベルになるように
        # 現在の境界をスケールするのに必要な量を調べます。
        currentzoom = screenbounds.size().x() / bounds.size().x()
        desiredzoom = 100.0
        scale = currentzoom / desiredzoom

        # 現行ノードを取得します。
        currentnode = uievent.editor.currentNode()
        if currentnode is not None:
            mousepos = uievent.mousepos
            mousepos = uievent.editor.posFromScreen(mousepos)
            noderect = uievent.editor.itemRect(currentnode)
            # マウス位置を中心にズームします。
            zoomcenter = mousepos
            bounds.translate(-zoomcenter)
            bounds.scale((scale, scale))
            bounds.translate(zoomcenter)
            # 現行ノードがマウス下に位置するようにビューを動かします。
            bounds.translate(noderect.center() - mousepos)
            # 新しい境界を設定します。
            uievent.editor.setVisibleBounds(bounds)

        # このイベントを処理しましたが、イベントハンドラーを返す必要はありません。
        # なぜなら、これは一度限りのイベントだからです。次に何か起きるのかは気にしません。
        return None, True

    return None, False

以下のコードは、ノードのダブルクリックイベントの挙動を上書きするEventHandlerサブクラスの作成方法について説明しています。 これは、ノードにダイブせずに何もしません。 これは、グローバルのhookとして望ましい変更ではありませんが、特定のHDAのPythonModuleの一部として役立ちます:

import hou
import nodegraph
from canvaseventtypes import *

class NoDiveNodeMouseHandler(nodegraph.NodeMouseHandler):
    def handleEvent(self, uievent, pending_actions):
        if isinstance(uievent, MouseEvent) and \
           uievent.eventtype == 'doubleclick':
            return None

        return nodegraph.NodeMouseHandler.handleEvent(
            self, uievent, pending_actions)

def createEventHandler(uievent, pending_actions):
    if isinstance(uievent, MouseEvent) and \
       uievent.eventtype == 'mousedown' and \
       isinstance(uievent.selected.item, hou.Node):
        return NoDiveNodeMouseHandler(uievent), True

    return None, False

Pythonスクリプト

はじめよう

次のステップ

リファレンス

  • hou

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

導師レベル

Python Viewerステート

Pythonビューアハンドル

プラグインタイプ