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ハンドル

このCOP Pythonハンドルのサンプルは、drawable2d.Dragger2Dオブジェクトやhou.Drawable2Dオブジェクトを使用してパラメータ値を移動させる方法を説明しています。

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)

    # ハンドルパラメータを定義します。
    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)

    return template    

class Handle(object):
    def __init__(self, **kwargs):
        """ ハンドルのインスタンスが作成された時にコールされます。
        """
        self.__dict__.update(kwargs)

        self.pivot = None

        # すべてのパラメータを制御します。
        self._dragger = d2d.Dragger2D(self.scene_viewer)

def onActivate(self,kwargs):
    """ ハンドルがアクティブになった時にコールされます。
        ハンドルに必要なすべてのDrawablesを作成します。
    """
    # ハンドルのピボットを表現した円Drawableを作成します。
    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):
    """ マウスイベントを利用して、ハンドルのインタラクティブな操作を実装します。
    """
    ui_event = kwargs["ui_event"]
    dev = ui_event.device()
    reason = ui_event.reason()

    if reason == hou.uiEventReason.Changed:
        # マウスアップ時に現在のドラッグ操作を終了します。
        self._dragger.endDrag()
        return True

    # ピボットDrawableを使用してドラッガーを更新します。
    if not self._dragger.update(kwargs, self.pivot):
        # ピボットDrawableが見つからない場合、ドラッガーは失敗します。
        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

このハンドラーは、ビューポート内でが押された時にハンドルがクリックされたかどうかに関わらずトリガーされます。 通常では、推測された入力に基づいて移動、回転、スケールといった自由形式なドラッグ操作を有効にするために使用します。

  • onMouseIndirectEventは、indirectMouseComputeDistanceで決定される、マウスとの距離が近いハンドルに対してのみコールされます。

  • ローレベルのマウス操作データにアクセスするには、hou.ViewerEvent(kwargs["ui_event"])を使用します。

  • Houdiniは、次のイベント理由に対してのみonMouseIndirectEventをコールします:hou.uiEventReason.Start,hou.uiEventReason.Active,hou.uiEventReason.Changed。

  • またはでマウスをドラッグしても、このハンドラーはトリガーされません。

  • イベントが処理された場合には、ハンドラーはTrueを返さなければなりません。

  • indirectMouseIsSupportedおよびindirectMouseComputeDistanceのいずれも実装されていない場合、Houdiniはそのハンドルが間接的なマウス操作に対応していないものとみなします。HUDハンドルの場合、indirectMouseComputeDistanceは必須ではありません。

3D Pythonハンドルのサンプル

import viewerhandle.utils as hu

class Handle(base_class.Handle):
    PIVOT_SIZE = 0.3

    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"]})

        # ピボットガジェット
        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):
    """ ハンドルのピボットとスクリーン空間のXY座標との距離を計算します。
    """
    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):
    """ 間接的なマウス操作をサポートするためにTrueを返します。
    """
    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)

    # ハンドルパラメータを定義します。
    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

2D Pythonハンドルのサンプル

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):
        """ ハンドルのインスタンスを作成した時にコールされます。
        """
        self.__dict__.update(kwargs)

        self._dragger = d2d.Dragger2D(self.scene_viewer)

    def onActivate(self,kwargs):
        """ ハンドルがアクティブになった時にコールされます。
            注: ハンドルに必要なすべてのDrawableは、ここで作成しなければなりません。
        """                    
        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のグループ化。
        self._handle_group = d2d.Drawable2DGroup("drawables",[axisX, axisY, pivot])

        # ハンドルパラメータの現在の状態に基づいてDrawableを有効にします。
        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 ):
        """ ハンドルDrawableのレンダリングでコールされます。
        """
        self.handle_context.draw(kwargs["draw_handle"])

    def onMouseIndirectEvent(self, kwargs):
        """ ビューポート内でマウス位置からピボットをドラッグします。
        """
        ui_event = kwargs["ui_event"]
        dev = ui_event.device()
        reason = ui_event.reason()

        if reason == hou.uiEventReason.Changed:
            # マウスアップ時に現在のドラッグ操作を終了します。
            self._dragger.endDrag()
            return True

        # ピボット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]:
            # ハンドルを自由にドラッグします。
            self._dragger.dragXY()

        else:
            consumed = False

        return consumed

    def indirectMouseComputeDistance(self, x, y):
        """ ハンドルのピボットとスクリーン空間内のXY座標との距離を計算します。
        """
        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):
        """ 間接的なマウス操作をサポートするためにTrueを返します。
        """
        return True

def createViewerHandleTemplate():
    """ 登録するビューアハンドルテンプレートを作成/返すのに必須となるエントリポイント。
    """

    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")

    # ハンドルパラメータを定義します。
    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 )

    # Houdiniにエクスポートするパラメータを伝えます。
    # これらのパラメータは、ステート内でハンドルをノードにバインドするのに使用することができます。
    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"])

    # 設定パラメータは、ハンドルの挙動を制御するのに使用します。
    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ハンドル

Pythonハンドルパラメータの扱い方は、COP Pythonハンドルでもほぼ同様です。

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)

    # 設定パラメータは、ハンドルの挙動を制御するのに使用します。
    template.bindSetting(hou.parmTemplateType.Float, name="smoothness", label="Smoothness", default_value=0.5)

    return template

class Handle(object):
    def __init__(self, **kwargs):
        """ ハンドルのインスタンスを作成する時にコールされます。
        """
        self.__dict__.update(kwargs)
        self.pivot = None

    def onParmChangeEvent(self, kwargs):
        """
        ハンドルのパラメータまたは設定が変更された時にコールされます。
        """
        parm_name = kwargs['parm_name']
        parm_value = kwargs['parm_value']

        if parm_name in PARMS:
            # 新しい値でハンドルのトランスフォームを更新します。
            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":
            # 設定パラメータは、kwargsツールからアクセスすることができます。
            self.log("smoothness", parms["smoothness"]["value"])

def onActivate(self,kwargs):
    """ ハンドルがアクティブになった時にコールされます。
        ハンドルに必要なすべてのDrawableを作成します。
    """
    # ハンドルのピボットを表現した円Drawableを作成します。
    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 

描画

Pythonハンドルは、その外観のレンダリングにonDrawコールバックを利用します。 3D Pythonハンドルに関してはonDrawSetupもサポートされており、Drawableスケーリングの計算に使用されます。 hou.Drawable2DオブジェクトのDrawableスケーリングはHoudini側で管理されるため、COP PythonハンドルではDrawableスケーリングの計算は不要です。

  • 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ビューアハンドル

プラグインタイプ