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
オブジェクトも返します。
ユーザ操作が完了(オペレーションを開始したマウスボタンを離した時がそうです)すると、handleEvent
はNone
を返し、次のユーザイベントが他のユーザ操作の開始を探すための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+H
やCtrl+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エレメントをさらに洗練する整数。例えば、item
がhou.Nodeで、name
がinput
(入力コネクタを示します)であれば、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