Houdini 21.0 Pythonスクリプト

Pythonハンドル イベント

Viewer Handleイベントをリッスン(受信)してそれに反応する方法。

On this page

Pythonビューアハンドル

概要

(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.

  • onMouseIndirectEvent is called only for the handle closest to the mouse, as determined by indirectMouseComputeDistance.

  • Use hou.ViewerEvent (kwargs["ui_event"]) to access low-level mouse interaction data.

  • Houdini only calls onMouseIndirectEvent for 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 indirectMouseIsSupported nor indirectMouseComputeDistance is implemented, Houdini assumes the handle does not support indirect mouse handling. For HUD Handles, indirectMouseComputeDistance is 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)

Pythonビューアハンドル

Pythonスクリプト

はじめよう

次のステップ

リファレンス

  • hou

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

導師レベル

Python Viewerステート

Pythonビューアハンドル

プラグインタイプ