| On this page |
概要 ¶
(Pythonハンドルの実装方法の基本はViewerハンドルを参照してください。)
ノードパラメータのインタラクティブな変更を適切に対応させるには、Pythonハンドルがマウス移動、マウスクリック、パラメータ変更などの様々なローレベルなイベントを解釈する必要があります。 キーボードイベントやメニューイベントなどの他のUIイベント対応も可能です。詳細は、入力イベントを参照してください。
マウスのハンドリング ¶
Houdiniは、3つの異なるハンドラーを使ってマウスイベントを制御することができます: onMouseEvent,onMouseIndirectEvent, onMouseWheelEvent。
onMouseEvent ¶
ハンドラーは、Pythonハンドルのインタラクティブなワークフローのほとんどの実装に使用します。
-
onMouseEventは、マウスがハンドルガジェットの上にある時にイベントと
イベントを制御することができます。
-
onMouseEventは、アクティブステートのonMouseEvent前に必ず処理されます。 -
hou.ViewerEvent(
kwargs["ui_event"])を使用することで、マウスクリックの検知などのローレベルでのマウス操作を追跡することができます。 このイベントがコンポジットビューアで発生した場合、ui_eventは代わりにhou.CompositorViewerEventインスタンスとなります。 -
hou.ViewerHandleContext(
self.handle_context)を使用することで、ガジェットのステートにアクセスすることができます。 -
onMouseEventをコールする前に、Houdiniは、マウス下にどのジオメトリが存在するのか検索するパスを実行し、そのジオメトリに関連付けられている(アクティブな)ガジェットを使ってself.handle_contextを更新します。自作でそのガジェットやDrawableを検索するためのジオメトリ交差を実装する必要はありません。 -
onMouseEvent内でself.handle_contextからアクティブなガジェットまたはDrawableにアクセスすることで、実行したいアクションを選択することができます。 -
3D Pythonハンドルの場合、hou.ViewerHandleDraggerを使用することで、マウス移動を軸、平面、コンストラクション平面に沿うように拘束するマウス操作やマウスアクションを実装することができます。
-
drawable2d.Dragger2Dを使用することで、2D COP Pythonハンドルに特化したインタラクティブなマウスアクションを実装することができます。
3D Pythonハンドル ¶
ハンドルドラッガーとハンドルガジェットを使ってハンドルの移動パラメータを変更する方法を説明したサンプルを以下に載せます。
import viewerhandle.utils as hu def createViewerHandleTemplate(): handle_type = 'handle_example' handle_label = 'Handle example' handle_cat = [hou.sopNodeTypeCategory()] template = hou.ViewerHandleTemplate(handle_type, handle_label, handle_cat) template.bindFactory(Handle) # ハンドルパラメータを定義します。 template.bindParameter( hou.parmTemplateType.Float, name="tx", label="Tx", min_limit=-10.0, max_limit=10.0, default_value=0.0 ) template.bindParameter( hou.parmTemplateType.Float, name="ty", label="Ty", min_limit=-10.0, max_limit=10.0, default_value=0.0 ) template.bindParameter( hou.parmTemplateType.Float, name="tz", label="Tz", min_limit=-10.0, max_limit=10.0, default_value=0.0 ) # すべてのパラメータをエクスポートします。 template.exportParameters(["tx", "ty", "tz"]) # ピボットガジェットをバインドします。 template.bindGadget( hou.drawableGeometryType.Face, "pivot" ) return template class Handle(base_class.Handle): def __init__(self, **kwargs): self.__dict__.update(kwargs) # ハンドルを移動させるドラッガーを作成します。 self.translate_dragger = hou.ViewerHandleDragger("translate_dragger") # ハンドルのトランスフォーム操作をサポートしたユーティリティクラス。 self.xform_aid = hu.TransformAid(self, parm_names={"translate":["tx","ty","tz"]}) def onMouseEvent( self, kwargs ): """ ガジェットをピックしてドラッグした時にコールされます。 """ # ロケートまたはピックされているアクティブガジェットを取得します。 if self.handle_context.gadget() == "pivot": ui_event = kwargs["ui_event"] reason = ui_event.reason() # ピボットガジェットは、ユーザがドラッグして移動パラメータを変更するためのガジェットです。 if reason == hou.uiEventReason.Start: # ハンドルのtx,ty,tzパラメータをhou.Vector3オブジェクトとして取得します。 handle_pos = self.xform_aid.parm3("translate") # hou.ViewerHandleDraggerオブジェクトを使ってハンドルの移動を開始します。 # このドラッガーには、直線沿い、または、平面沿いにドラッグといった特別な操作をするための様々なメソッドが用意されています。 # ここでは、単にピボットをワールド空間で移動させます。 self.translate_dragger.startDrag(ui_event, handle_pos) elif reason in [hou.uiEventReason.Changed, hou.uiEventReason.Active]: # ピボットを途切れることなくドラッグします。 drag_values = self.translate_dragger.drag(ui_event) # ドラッガーが返した位置を使ってハンドルのtx,ty,tzパラメータを更新します。 self.xform_aid.addToParm3("translate", drag_values["delta_position"]) # 注: このトランスフォームはonDrawSetup内で計算されます。 if reason == hou.uiEventReason.Changed: # 終了してドラッグを抜けます。 self.translate_dragger.endDrag() # イベントを消費します。 return True return False def onDraw(self, kwargs): self.pivot.draw(kwargs["draw_handle"])
この実装パターンは、非常に汎用的で、たいていのPythonハンドルで再利用することができます。
onMouseEventはガジェットがピックされた時に常にコールされるので、まず最初にマウスが押されているかどうかをチェックする必要がありません。
代わりに、どのガジェットがactiveになっているのかチェックして、それに対して作用させます。
hou.uiEventReason.Startは、マウスが押されたことを知らせるので、この時点でのドラッガーは、そのハンドルのtranslate位置(xyz parameters)でstartDragをコールしてセットアップする必要があります。
UIイベントがhou.uiEventReason.Changedまたはhou.uiEventReason.Activeに設定された時にdragメソッドをコールし、
ドラッガーが返した差分値でハンドルのtranslateパラメータを更新します。
マウスが離されると(hou.uiEventReason.Changed)、endDragをコールしてドラッガーを終了します。
おそらくself.handle_contextが__init__関数内で明示的に作成されていないことにお気づきかと思います。
これはHoudiniによって作成されるクラスアトリビュートなので、あなた自身で作成する必要はありません。
self.handle_contextは、ピックとロケートに関するガジェットの状態を格納したhou.ViewerHandleContextオブジェクトで設定されます。
このオブジェクトは、最新コンテキスト情報で常に更新されるようにHoudini側で管理されます。
2D Python handle ¶
This COP Python handle example demonstrates how to translate parameters using a drawable2d.Dragger2D and hou.Drawable2D objects.
import drawable2d as d2d import resourceutils as ru import viewerhandle.utils as hu TX = "tx" TY = "ty" PARMS = [TX,TY] PIVOT = "pivot" PIVOT_C = [1.0,0.0,0.0] PIVOT_R = 0.05 def createViewerHandleTemplate(): handle_type = 'cop_handle_example' handle_label = 'COP Handle example' handle_cat = [hou.copNodeTypeCategory()] template = hou.ViewerHandleTemplate(handle_type, handle_label, handle_cat) template.bindFactory(Handle) # Define the handle parameters template.bindParameter(hou.parmTemplateType.Float, name=TX, label="TX", min_limit=0.0, max_limit=10.0, default_value=0.0) template.bindParameter(hou.parmTemplateType.Float, name=TY, label="TY", min_limit=0.0, max_limit=10.0, default_value=0.0) # Export all parameters template.exportParameters(PARMS) return template class Handle(object): def __init__(self, **kwargs): """ Called when creating an instance of the handle. """ self.__dict__.update(kwargs) self.pivot = None # Handle all parameters self._dragger = d2d.Dragger2D(self.scene_viewer) def onActivate(self,kwargs): """ Called when the handle is activated. Creates all drawables needed for the handle. """ # Create a circle drawable to represent the handle pivot params={"color":PIVOT_C, "position": [0,0], "radius":PIVOT_R, "fill":1} self.pivot = hou.Drawable2D(self.scene_viewer, hou.drawable2DType.Circle, params=params, name=PIVOT, pickable=True) def onMouseEvent(self, kwargs): """ Use the mouse event to implement the handle interactive operations. """ ui_event = kwargs["ui_event"] dev = ui_event.device() reason = ui_event.reason() if reason == hou.uiEventReason.Changed: # End the current drag operation on mouse up self._dragger.endDrag() return True # Update the dragger with the pivot drawable if not self._dragger.update(kwargs, self.pivot): # The dragger may fail if no drawable is located return False consumed = True if reason == hou.uiEventReason.Start: self._dragger.startDrag(x=self.handle_parms[TX], y=self.handle_parms[TY]) elif reason in [hou.uiEventReason.Active, hou.uiEventReason.Changed]: if drawable_name == self.handle_context.drawable(): self._dragger.dragXY() return consumed def onDraw(self, kwargs): self.pivot.draw(kwargs["draw_handle"])
onMouseIndirectEvent ¶
The handler is triggered when is pressed in the viewport, regardless of whether a handle was clicked. It is typically used to enable freeform dragging interactions such as translate, rotate, or scale based on inferred input.
-
onMouseIndirectEventis called only for the handle closest to the mouse, as determined byindirectMouseComputeDistance. -
Use hou.ViewerEvent (
kwargs["ui_event"]) to access low-level mouse interaction data. -
Houdini only calls
onMouseIndirectEventfor the following event reasons:hou.uiEventReason.Start,hou.uiEventReason.Active,hou.uiEventReason.Changed. -
Dragging the mouse with
or
will not trigger this handler.
-
The handler must return True when the event is consumed.
-
If neither
indirectMouseIsSupportednorindirectMouseComputeDistanceis implemented, Houdini assumes the handle does not support indirect mouse handling. For HUD Handles,indirectMouseComputeDistanceis not required.
3D Python Handle Example ¶
import viewerhandle.utils as hu class Handle(base_class.Handle): PIVOT_SIZE = 0.3 def __init__(self, **kwargs): self.__dict__.update(kwargs) # ハンドルを移動させるドラッガーを作成します。Creates a dragger for translating the handle self.translate_dragger = hou.ViewerHandleDragger("translate_dragger") # ハンドルのトランスフォーム操作をサポートしたユーティリティクラス。 self.xform_aid = hu.TransformAid(self, parm_names={"translate":["tx","ty","tz"]}) # ピボットガジェット sops = hou.sopNodeTypeCategory() verb = sops.nodeVerb("box") psize = Handle.PIVOT_SIZE verb.setParms( { "type" : 1, "size" : (psize,psize,psize), "divrate": (2,2,2) }) pivot = hou.Geometry() verb.execute(pivot, []) self.pivot = self.handle_gadgets["pivot"] self.pivot.setGeometry(pivot) self.pivot.show(True) def onMouseIndirectEvent(self, kwargs): """ マウスがMMBドラッグされた時にコールされます。 """ ui_event = kwargs["ui_event"] reason = ui_event.reason() consumed = False # このサンプルは、MMBが押されている時にピボットを動かします。 # # この実装は`onMouseEvent`とさほど違いはありません。 # # onMouseInteractEventがコールされる時はマウスはガジェット上にないわけなので、 # アクティブガジェットをチェックする必要がありません。 # # "拘束のない"ドラッガーを使用すると、マウス下のワールド空間位置を基準に # ハンドル位置が計算されます。 if reason == hou.uiEventReason.Start: # ハンドルのtx,ty,tzパラメータをhou.Vector3オブジェクトとして取得します。 handle_pos = self.xform_aid.parm3("translate") # ドラッガーを初期化します。 self.translate_dragger.startDrag(ui_event, handle_pos) consumed = True elif reason in [hou.uiEventReason.Changed, hou.uiEventReason.Active]: # ピボットを途切れることなくドラッグします。 drag_values = self.translate_dragger.drag(ui_event) # ドラッガーが返した位置を使ってハンドルのtx,ty,tzパラメータを更新します。 self.xform_aid.addToParm3("translate", drag_values["delta_position"]) # 注: このトランスフォームはonDrawSetup内で計算されます。 if reason == hou.uiEventReason.Changed: # 終了してドラッグを抜けます。 self.translate_dragger.endDrag() consumed = True return consumed def indirectMouseComputeDistance(self, x, y): """ Compute the distance between the handle pivot and the xy point in screen-space. """ p1 = hou.Vector2(x,y) screen_pos = self.scene_viewer.curViewport().mapToScreen( hou.Vector3(self.handle_parms["tx""]["value"], self.handle_parms["ty"]["value"], 0)) p2 = hou.Vector2(screen_pos.x(), screen_pos.y()) return p1.distanceTo(p2) def indirectMouseIsSupported(self): """ Returns True to support indirect mouse manipulation. """ return True def onDraw( self, kwargs ): self.pivot.draw(kwargs["draw_handle"]) def createViewerHandleTemplate(): handle_type = 'handle_example' handle_label = 'Handle example' handle_cat = [hou.sopNodeTypeCategory()] template = hou.ViewerHandleTemplate(handle_type, handle_label, handle_cat) template.bindFactory(Handle) # Define the handle parameters template.bindParameter( hou.parmTemplateType.Float, name="tx", label="Tx", min_limit=-10.0, max_limit=10.0, default_value=0.0 ) template.bindParameter( hou.parmTemplateType.Float, name="ty", label="Ty", min_limit=-10.0, max_limit=10.0, default_value=0.0 ) template.bindParameter( hou.parmTemplateType.Float, name="tz", label="Tz", min_limit=-10.0, max_limit=10.0, default_value=0.0 ) # Export all parameters template.exportParameters(["tx", "ty", "tz"]) # Bind the pivot gadget template.bindGadget( hou.drawableGeometryType.Face, "pivot" ) return template
2D Python Handle Example ¶
import hou import drawable2d as d2d import viewerhandle.utils as hu TX = "tx" TY = "ty" AXISX = "axisx" AXISY = "axisy" PIVOT = "pivot" XAXIS_C = hu.HXAXIS_COLOR YAXIS_C = hu.HYAXIS_COLOR PIVOT_C = hu.HPIVOT_COLOR AXIS_L = 0.1 class Handle(object): def __init__(self, **kwargs): """ Called when creating an instance of the handle. """ self.__dict__.update(kwargs) self._dragger = d2d.Dragger2D(self.scene_viewer) def onActivate(self,kwargs): """ Called when the handle is activated. NOTE: all drawables needed for the handle must be created here. """ params={"color":XAXIS_C, "points": [-AXIS_L,0, AXIS_L,0], "start_cap": hou.drawable2DCapStyle.Arrow, "end_cap": hou.drawable2DCapStyle.Arrow} axisX = hou.Drawable2D(self.scene_viewer, hou.drawable2DType.Line, params=params, name=AXISX, pickable=True) params={"color":YAXIS_C, "points": [0,AXIS_L, 0,-AXIS_L], "start_cap": hou.drawable2DCapStyle.Arrow, "end_cap": hou.drawable2DCapStyle.Arrow} axisY = hou.Drawable2D(self.scene_viewer, hou.drawable2DType.Line, params=params, name=AXISY, pickable=True) params={"color":PIVOT_C, "position": [0,0], "size":hou.drawable2DMarkerSize.Small, "style": hou.drawable2DMarkerStyle.Dot} pivot = hou.Drawable2D(self.scene_viewer, hou.drawable2DType.Marker, params=params, name=PIVOT, pickable=True) # Drawable grouping self._handle_group = d2d.Drawable2DGroup("drawables",[axisX, axisY, pivot]) # Enable drawables based on the current state of the handle # parameters. tx_enabled = self.handle_context.isParameterEnabled(TX) ty_enabled = self.handle_context.isParameterEnabled(TY) self._handle_group.enable(AXISX, tx_enabled) self._handle_group.enable(AXISY, ty_enabled) self._handle_group.enable(PIVOT, tx_enabled or ty_enabled) self._handle_group.show(True) def onDraw( self, kwargs ): """ Called for rendering the handle drawables. """ self.handle_context.draw(kwargs["draw_handle"]) def onMouseIndirectEvent(self, kwargs): """ Drag the pivot from the mouse position in the viewport. """ ui_event = kwargs["ui_event"] dev = ui_event.device() reason = ui_event.reason() if reason == hou.uiEventReason.Changed: # End the current drag operation on mouse up self._dragger.endDrag() return True # Update the dragger with the pivot drawable pivot = self._handle_group.find(PIVOT) if not self._dragger.update(kwargs, self._handle_group, mapping_drawable=pivot): return False consumed = True if reason == hou.uiEventReason.Start: tx = self.handle_parms[TX] ty = self.handle_parms[TY] self._dragger.startDrag(p1=tx, p2=ty) elif reason in [hou.uiEventReason.Active]: # Drag the handle freely self._dragger.dragXY() else: consumed = False return consumed def indirectMouseComputeDistance(self, x, y): """ Compute the distance between the handle pivot and the xy point in screen-space. """ p1 = hou.Vector2(x,y) spos = self.scene_viewer.curViewport().mapToScreen( hou.Vector3(self.handle_parms[TX]["value"], self.handle_parms[TY]["value"], 0)) p2 = hou.Vector2(spos.x(), spos.y()) return p1.distanceTo(p2) def indirectMouseIsSupported(self): """ Returns True to support indirect mouse manipulation. """ return True def createViewerHandleTemplate(): """ Mandatory entry point to create and return the viewer handle template to register. """ handle_typename = "handle_example2d" handle_label = "Handle Example 2D" handle_cat = [hou.copNodeTypeCategory()] template = hou.ViewerHandleTemplate(handle_typename, handle_label, handle_cat) template.bindFactory(Handle) template.bindIcon("MISC_python") # Defines the handle parameters template.bindParameter( hou.parmTemplateType.Float, name=TX, label=TX.upper(), min_limit=-10.0, max_limit=10.0, default_value=0.0 ) template.bindParameter( hou.parmTemplateType.Float, name=TY, label=TY.upper(), min_limit=-10.0, max_limit=10.0, default_value=0.0 ) # Tells Houdini what are the parameters to export. These parameters # can be used by a state for binding the handle to a node. template.exportParameters([TX, TY]) return template
onMouseWheelEvent ¶
マウスホイールの反応は、onMouseWheelEventによって実現されます。
このハンドラーは、PythonステートのonMouseWheelEventハンドラーとほぼ同じように動作します。
しかし、これらの2つのハンドラーには重大な違いがあります。
PythonハンドルのonMouseWheelEventハンドラーは、ロケート(マウス下の検索)されたDrawableでのみコールされるので、
マウスホイールイベントを発動するには、マウスカーソルがDrawable上に必ずなければなりません。
場合によっては、ロケートされたDrawable上にマウスを置き続けるのは面倒になるので、専用Drawableまたは固定Drawableのどれかでマウスホイールイベントを有効にして、
状況に応じてPythonハンドルを設計すると良いでしょう。
他にも、HoudiniはPythonステートのonMouseWheelEventハンドラーの前に常にPythonハンドルのonMouseWheelEventをコールします。
Pythonステートのハンドラーを間違って処理しないように必ずPythonハンドルのonMouseWheelEventがこのイベントを消費するようにしてください。
パラメータ ¶
パラメータは、Pythonハンドルの重要な部分です。 パラメータを使用しなかった場合、ビューポート内でノードパラメータを変更する際にPythonハンドルを使用することができません。
ハンドルパラメータの定義は、登録処理の一部であり、hou.ViewerHandleTemplate.bindParameterを使用することで、Pythonハンドルテンプレートにパラメータを追加することができます。 定義されたすべてのパラメータは、適切にエクスポートされた場合にのみインタラクティブにビューポート内で制御することができます。 パラメータは、hou.ViewerHandleTemplate.exportParametersを使用してエクスポートします。 このステップでは、PythonステートがPythonハンドルパラメータをノードパラメータにバインドすることができます。 特定のパラメータをエクスポートしないように選択した場合、そのパラメータは外部で利用できなくなります。
Pythonハンドルパラメータだけでなく、hou.ViewerHandleTemplate.bindSettingを使用して 設定 パラメータもPythonハンドルに追加することができます。
これらの設定パラメータは、典型的にはPythonハンドルの構成設定として使用します。
例えば、ガジェット用に異なるカラー値やスケール値などを格納する設定パラメータを作成すると良いでしょう。
Pythonハンドルのパラメータと設定は、Handle Parameter DialogやPythonハンドルのhandle_parmsクラスアトリビュートから変更することができます。
ただし、設定パラメータはエクスポート不可なので、ビューポートからインタラクティブに変更することはできません。
Note
パラメータ値と設定値はシーンに保存され、そのシーンを読み直すと最後に保存された値に戻ります。
パラメータ変更に反応させる方法 ¶
onParmChangeEventハンドラーを使用することで、パラメータ変更に反応させることができます。
Houdiniは、Pythonハンドルのパラメータまたは設定パラメータが変更された後でそのハンドラーをコールします。
詳細は、onParmChangeEventを参照してください。
3D Python Handle ¶
以下のサンプルでは、Pythonハンドルのパラメータを定義してパラメータ変更に反応させる方法を説明しています。
import viewerhandle.utils as hu def createViewerHandleTemplate(): handle_type = 'handle_example' handle_label = 'Handle example' handle_cat = [hou.sopNodeTypeCategory()] template = hou.ViewerHandleTemplate(handle_type, handle_label, handle_cat) template.bindFactory(Handle) # ハンドルパラメータは、典型的にはガジェットとそれにバインドされたノードパラメータを制御するのに使用します。 template.bindParameter( hou.parmTemplateType.Float, name="tx", label="Tx", min_limit=-10.0, max_limit=10.0, default_value=0.0 ) template.bindParameter( hou.parmTemplateType.Float, name="ty", label="Ty", min_limit=-10.0, max_limit=10.0, default_value=0.0 ) template.bindParameter( hou.parmTemplateType.Float, name="tz", label="Tz", min_limit=-10.0, max_limit=10.0, default_value=0.0 ) # ... また、ハンドルパラメータはエクスポートする必要があるので、Houdiniに使用させたいハンドルパラメータのみをエクスポートします。 # エクスポートしたパラメータは、Pythonステート内でノードパラメータをバインド(例えば、hou.ViewerStateTemplate.bindHandleStatic)するのに使用することができます。 template.exportParameters(["tx", "ty", "tz"]) # Settingは、ハンドルの挙動を制御するのに使用されるパラメータです。 template.bindSetting( hou.parmTemplateType.Toggle, name="face", label="Face", default_value=True, align=True ) template.bindSetting( hou.parmTemplateType.Toggle, name="wire", label="Wire", default_value=True, align=True ) template.bindSetting( hou.parmTemplateType.Toggle, name="pivot", label="Pivot", default_value=True, align=True ) template.bindSetting( hou.parmTemplateType.Toggle, name="knob", label="Knob", default_value=True, align=False ) # ガジェットをバインドします。 template.bindGadget( hou.drawableGeometryType.Face, "pivot" ) template.bindGadget( hou.drawableGeometryType.Face, "face" ) template.bindGadget( hou.drawableGeometryType.Line, "wire" ) template.bindGadget( hou.drawableGeometryType.Point, "knob" ) return template class Handle(base_class.Handle): def __init__(self, **kwargs): self.__dict__.update(kwargs) self.xform_aid = hu.TransformAid(self, parm_names={"translate":["tx","ty","tz"]}) # 簡潔にするためにガジェットの初期化はスキップしました。 def onParmChangeEvent(self, kwargs): """ ハンドルのパラメータまたは設定が変更された時にコールされます。 """ parm_name = kwargs['parm_name'] parm_value = kwargs['parm_value'] if parm_name in ["tx","ty","tz"]: # ハンドルトランスフォームを新しい値で更新します。 self.xform_aid.updateTransform() elif parm_name == "face": kwargs["handle_gadgets"]["face"].show(parm_value) elif parm_name == "wire": kwargs["handle_gadgets"]["wire"].show(parm_value) elif parm_name == "pivot": kwargs["handle_gadgets"]["pivot"].show(parm_value) elif parm_name == "knob": kwargs["handle_gadgets"]["knob"].show(parm_value) # ビューポートを更新します。 self.scene_viewer.curViewport().draw()
2D Python Handle ¶
Handling of Python handle parameters is pretty much the same for COP Python handles.
import resourceutils as ru import viewerhandle.utils as hu TX = "tx" TY = "ty" PARMS = [TX,TY] PIVOT = "pivot" PIVOT_C = [1.0,0.0,0.0] PIVOT_R = 0.05 def createViewerHandleTemplate(): handle_type = 'cop_handle_example' handle_label = 'COP Handle example' handle_cat = [hou.copNodeTypeCategory()] template = hou.ViewerHandleTemplate(handle_type, handle_label, handle_cat) template.bindFactory(Handle) # ハンドルパラメータを定義します。 template.bindParameter(hou.parmTemplateType.Float, name=TX, label="TX", min_limit=0.0, max_limit=10.0, default_value=0.0) template.bindParameter(hou.parmTemplateType.Float, name=TY, label="TY", min_limit=0.0, max_limit=10.0, default_value=0.0) # すべてのパラメータをエクスポートします。 template.exportParameters(PARMS) # Settings are parameters used for controlling the handle behavior. template.bindSetting(hou.parmTemplateType.Float, name="smoothness", label="Smoothness", default_value=0.5) return template class Handle(object): def __init__(self, **kwargs): """ Called when creating an instance of the handle. """ self.__dict__.update(kwargs) self.pivot = None def onParmChangeEvent(self, kwargs): """ Called when a handle parameter or setting has changed. """ parm_name = kwargs['parm_name'] parm_value = kwargs['parm_value'] if parm_name in PARMS: # update the handle transform with the new values parms = self.handle_parms mat = ru.buildTransform2D( x=parms[TX]["value"], y=parms[TY]["value"]) self.pivot.setTransform(mat) self.scene_viewer.curViewport().draw() elif parm_name == "smoothness": # Settings can be accessed from the kwargs too self.log("smoothness", parms["smoothness"]["value"]) def onActivate(self,kwargs): """ Called when the handle is activated. Creates all drawables needed for the handle. """ # Create a circle drawable to represent the handle pivot params={"color":PIVOT_C, "position": [0,0], "radius":PIVOT_R, "fill":1} self.pivot = hou.Drawable2D(self.scene_viewer, hou.drawable2DType.Circle, params=params, name=PIVOT, pickable=True)
パラメータ値へのアクセス ¶
Pythonハンドルのパラメータと設定は、handle_parms辞書からアクセスすることができます。 この辞書は、どのPythonハンドルハンドラーからでもアクセス可能です。
def createViewerHandleTemplate(): handle_type = 'handle_example' handle_label = 'Handle example' handle_cat = [hou.sopNodeTypeCategory()] template = hou.ViewerHandleTemplate(handle_type, handle_label, handle_cat) template.bindFactory(Handle) # ハンドルパラメータ。 template.bindParameter( hou.parmTemplateType.Float, name="tx", label="Tx", min_limit=-10.0, max_limit=10.0, default_value=0.0 ) template.bindParameter( hou.parmTemplateType.Float, name="ty", label="Ty", min_limit=-10.0, max_limit=10.0, default_value=1.0 ) template.bindParameter( hou.parmTemplateType.Float, name="tz", label="Tz", min_limit=-10.0, max_limit=10.0, default_value=0.0 ) template.bindParameter( hou.parmTemplateType.Float, name="float3"", label="Float 3", min_limit=-10.0, max_limit=10.0, default_value=[1.0,1.0,1.0], num_components=3 ) # ハンドル設定。 template.bindSetting( hou.parmTemplateType.Toggle, name="ui_guides", label="Draw UI guides", default_value=True) return template class Handle(object): def __init__(self, **kwargs): self.__dict__.update(kwargs) def onActivate(self, kwargs): self.log("Handle tx parm value", kwargs["handle_parms"]["tx"]["value"]) self.log("Handle ty parm value", kwargs["handle_parms"]["ty"]["value"]) self.log("Handle tz parm value", kwargs["handle_parms"]["tz"]["value"]) self.log("Handle float3 parm value", kwargs["handle_parms"]["float3"]["value"]) self.log("UI guides value", kwargs["handle_parms"]["ui_guides"]["value"]) # 出力 'Handle tx parm value' 0.0 'Handle ty parm value' 1.0 'Handle tz parm value' 0.0 'Handle float3 parm value' [1.0, 1.0, 1.0] 'UI guides value' 1
描画 ¶
A Python handle utilizes the onDraw callback for rendering its visuals. onDrawSetup is also supported for 3D Python handles to
compute their drawable scaling. Drawable scaling for hou.Drawable2D objects is managed by Houdini; therefore, COP
Python handles do not need to compute their drawable scaling.
-
onDraw:Pythonハンドルガジェットと他のDrawablesを描画する際に使用します。 これは実装するのが非常に簡単で、基本的にハンドルガジェットとDrawablesの
drawメソッドにデリゲート(委任)します。 このハンドラーは、PythonステートのonDrawハンドラーと同様です。 -
onDrawSetup:現在のカメラ位置に関係なく固定サイズを維持できるようにするには、3D Pythonハンドルを動的にスケールさせる必要があります。
onDrawSetupは、典型的にはhou.ViewerHandleContext.scaleFactorの補助を使ってハンドルスケールを計算する際に使用します。 新しく計算されたスケール値を使用すると、PythonハンドルはDrawableガイドのトランスフォーム行列と一緒に自身のトランスフォーム行列も更新します。
def onDrawSetup(self, kwargs): # 現在のハンドル位置を使用してスケール値を計算します。 hpos = self.xform_aid.parm3("translate") # スケール値を計算します。 fixed_scale_value = 100.0 scale = self.handle_context.scaleFactor(hpos)*fixed_scale_value # 新しいスケールを使ってハンドルのトランスフォーム行列を再構築します。 xform = self.xform_aid.updateTransform(s=[scale,scale,scale]) # ガジェットのトランスフォームを更新します。 kwargs["handle_gadgets"]["face"].setTransform(xform) kwargs["handle_gadgets"]["wire"].setTransform(xform) kwargs["handle_gadgets"]["pivot"].setTransform(xform) kwargs["handle_gadgets"]["knob"].setTransform(xform)