Houdini 17.0 Pythonスクリプト

Pythonステート コンテキストメニュー

独自ステートのコンテキストメニューをセットアップして、そのメニューを使ってユーザー操作に反応させる方法。

On this page

Pythonビューアステート

概要

hou.ViewerStateMenuオブジェクトを使用することで、ステートがアクティブ中にユーザーがクリックした時に表示される独自のコンテキストメニューを構築することができます。

ステートのコンテキストメニューをデザインする方法に関しては、Viewer Stateのユーザーインターフェースガイドラインを参照してください。

メニューを構築してバインドする方法

ステートテンプレートを生成するコードの部分で、hou.ViewerStateMenuオブジェクトを使ってコンテキストメニューを作成してから、 hou.ViewerStateTemplate.bindMenu()を使って、そのメニューをステートテンプレートにバインドさせます。

from __future__ import print_function


# ステートの挙動を実装したクラス
class MyState(object):
    def __init__(self, state_name, scene_viewer):
        self.state_name = state_name
        self.scene_viewer = scene_viewer

    def onMenuAction(self, kwargs):
        print("You chose:", kwargs["menu_item"])


# ステートテンプレートを生成する関数。このステートテンプレートは、コンテキストメニューにバインドさせるステート、メニュー、ハンドル、セレクターなどを表現します。
def createViewerStateTemplate():
    # テンプレートオブジェクトを作成します。
    template = hou.ViewerStateTemplate(
        "mystate", "My State", hou.sopNodeTypeCategory()
    )
    # 実装したクラスをバインドします。
    template.bindFactory(MyState)

    # コンテキストメニューを作成して、バインドします。
    menu = hou.ViewerStateMenu('bend_menu', 'Bend')
    menu.addActionItem("first", "Action One")
    menu.addActionItem("second", "Action Two")
    menu.addActionItem("third", "Action Three")
    template.bindMenu(menu)

    return template

メニューを構築する方法は、hou.ViewerStateMenuオブジェクトを参照してください。

メニューのアクションに反応させる方法

ユーザーがメニューを開いてアイテムを選択した時、Houdiniは、あなたのステートのonMenuActionメソッドをコールします。 このメソッドに渡される辞書には、選択したメニューアイテムのIDを含んだmenu_itemキーが含まれています。

Tip

これは、アクションメニューアイテムが選択されたタイミングのみを追跡し、ユーザーの"設定"変更(チェックボックスのオン/オフ、ラジオ選択の変更)は無視します。 他のハンドラーメソッドでその設定項目の現行値を読み込んで、その設定を使用することで、そのステートの挙動に影響を与えることができます。

  • アクションメニューアイテムとトグルアイテムに関しては、その辞書のmenu_itemがアイテムに設定したIDと同じであれば、そのアイテムが選択されています。

    # メニューを作成する時
    menu = hou.ViewerStateMenu('bend_menu', 'Bend')
    menu.addActionItem("delete", "Delete Current")
    
    # ...
    
    # メニューハンドラーメソッド内
    def onMenuAction(self, kwargs):
        if kwargs["menu_item"] == "delete":
            # Code to implement the "delete" action
            # ...
    
  • 以下のようにメニュー内のすべてのアクションに対して面倒なif/elseツリーをコーディングするよりは:

    def onMenuAction(self, kwargs):
        menu_item = kwargs["menu_item"]
    
        # 以下のコードだと、メニューアイテムがたくさんあると読み書きが面倒です。
        if menu_item == "delete":
            # ...
        elif menu_item == "dup":
            # ...
        elif menu_item == "done":
            # ...
    

    何かしらのPythonテクニックを使うことで、メニューアイテムIDに基づいてアクションハンドルをメソッドに分けることができます:

    # メニューを作成する時。
    menu = hou.ViewerStateMenu('bend_menu', 'Bend')
    menu.addActionItem("delete", "Delete")
    menu.addActionItem("dup", "Duplicate")
    menu.addActionItem("done", "Finish")
    
    # ...
    
    # ステートクラス内
    class MyState(object):
        def __init__(self, state_name, scene_viewer):
            self.state_name = state_name
            self.scene_viewer = scene_viewer
    
        def onMenuAction(self, kwargs):
            # 選択したIDと同じ名前のメソッドをコールします
            # (ただし、メソッド名の頭にはアンダースコアを付けてください)
            menu_item = kwargs["menu_item"]
            method = getattr(self, "_" + menu_item)
            return method(kwargs)
    
        def _delete(self, kwargs):
            # ...
    
        def _dup(self, kwargs):
            # ...
    
        def _done(self, kwargs):
            # ...
    
  • トグルアイテムに関しては、メニューアイテムのIDをキーとして使用した辞書からトグルの新しいオン/オフの状態を取得することができます。

    # メニューを作成する時。
    menu = hou.ViewerStateMenu('bend_menu', 'Bend')
    menu.addToggleItem("showpoints", "Show Points")
    
    # ...
    
    # メニューハンドラーメソッド内
    def onMenuAction(self, kwargs):
        if kwargs["menu_item"] == "showpoints":
            # チェックボックスの現在の状態を読み込みます。
            showpoints = kwargs["showpoints"]
    
  • ラジオストリップアイテムに関しては、辞書内のmenu_itemラジオストリップ (個々のラジオアイテムではなく)に設定したIDと同じであれば、そのラジオストリップのアイテムが選択されています。

    ラジオストリップIDをキーとして使用した辞書から新しい現行アイテムインデックスを取得することができます。

    # メニューを作成する時。
    menu = hou.ViewerStateMenu('bend_menu', 'Bend')
    menu.addRadioStrip("deform_type", "Deformation Type")
    menu.addRadioStripItem("deform_type", "bend", "Bend")
    menu.addRadioStripItem("deform_type", "squash", "Squash")
    
    # ...
    
    # メニューハンドラーメソッド内
    def onMenuAction(self, kwargs):
        if kwargs["menu_item"] == "deform_type":
            # 現在選択されているアイテムのインデックスを取得します。
            current = kwargs["deform_type"]
    

メニュー設定を読み込む方法

コンテキストメニューにチェックボックスのメニューアイテムを追加(hou.ViewerStateMenu.addToggleItem()を使用)またはラジオボタンアイテムを追加(hou.ViewerStateMenu.addRadioStrip()を使用)すると、 Houdiniは、渡されたすべてのkwargs辞書内のそれらのアイテムの現在の状態をすべてのイベントハンドラーメソッドに渡します。

これによって、メニュー設定をチェックしてから特定のイベントの挙動を変更するのが簡単になります。 以下のサンプルのonMouseEventコードは、メニュー設定をチェックしてから、マウスクリックの挙動を変更しています。

from __future__ import print_function


# ステートの挙動を実装したクラス
class MyState(object):
    def __init__(self, state_name, scene_viewer):
        self.state_name = state_name
        self.scene_viewer = scene_viewer

    def onMouseEvent(self, kwargs):
        ui_event = kwargs["ui_event"]
        if ui_event.device().isLeftButton():
            # メニュー内の"Bend"チェックボックスアイテムが有効になっているかどうかチェックします。
            if kwargs["bend"]:
                option = kwargs["bend_parm_option"]
                # ...
            else:
                # ...


# ステートのコンテキストメニューを生成する関数。
def create_menu():
    menu = hou.ViewerStateMenu('bend_menu', 'Bend')

    menu.addToggleItem( 'bend', 'Bend', True )

    menu.addSeparator()

    select_parm_menu = hou.ViewerStateMenu( 'select_parm_menu', 'Select Parm...' )
    select_parm_menu.addRadioStrip( 'bend_parm_option', 'Bend Parm', 0 )  
    select_parm_menu.addRadioStripItem( 'bend_parm_option', 'bend', 'Bend' )
    select_parm_menu.addRadioStripItem( 'bend_parm_option', 'twist', 'Twist' )
    select_parm_menu.addRadioStripItem( 'bend_parm_option', 'lengthscale', 'Length Scale' )
    select_parm_menu.addRadioStripItem( 'bend_parm_option', 'taper', 'Taper' )
    select_parm_menu.addRadioStripItem( 'bend_parm_option', 'squish', 'Squish' )

    menu.addMenu(select_parm_menu)

    return menu


# ステートテンプレートを生成する関数。このステートテンプレートは、コンテキストメニューにバインドさせるステート、メニュー、ハンドル、セレクターなどを表現します。
def createViewerStateTemplate():
    # テンプレートオブジェクトを作成します。
    template = hou.ViewerStateTemplate(
        "mystate", "My State", hou.sopNodeTypeCategory()
    )
    # 実装したクラスをバインドします。
    template.bindFactory(MyState)

    # ハンドル、セレクター、メニューなどをバインドします。
    template.bindMenu(create_menu())

    return template

メニューホットキー

Note

Houdiniのホットキーシステムは、新しいシステムに移行中なので、将来のバージョンで置換される可能性があります。

  • Houdiniは、キー自体(例えば、shift+p)の代わりに記号(例えば、h.pane.gview.world.selectall)を使用してホットキー可能なアクションを表現していて、ユーザーはホットキーエディタを使ってそれらのアクションを編集することができます。

    ステートメニューを構築する時は、独自のホットキー記号を作成し、それをステートコンテキストメニューのメニューアイテムに割り当てることができます。 これによって、ユーザーがホットキーをメニューアイテムに割り当てることができます。

  • hou.ViewerStateMenu.addActionItem(), hou.ViewerStateMenu.addToggleItem(), hou.ViewerStateMenu.addRadioStripItem()のメソッドは、オプションの引数としてHoudiniの ホットキー記号 文字列を受け取ることができます。

  • ホットキー記号は、ドットの接頭辞を付けることで、そのホットキーが利用可能な コンテキスト を指定します(これによって、ユーザーは同じホットキーにコンテキストの異なる別のアクションを割り当てることができます)。

    SOP Viewer Stateに関しては、そのホットキーの接頭辞は以下のようになります:

    h.pane.gview.state.sop.state_name.

    state_nameは、ステートテンプレートを作成する時に指定するステートの内部名です。

  • ホットキー記号を作成するには、コンテキストの最後に特定のアクションの名前を追加します。例えば、Deleteホットキーを指定したいのであれば、以下の記号を使用することができます:

    h.pane.gview.state.sop.state_name.delete

import hou

# ホットキーシンボルを作成します。

# まず最初にaddContextを使ってホットキーコンテキストを作成する必要があります。
key_context = "h.pane.gview.state.sop.mystate"
hou.hotkeys.addContext(
    key_context, "mystate Operation",
    "These keys apply to the mystate operation."
)

# その記号、名前、説明を使ってaddCommandをコールします。
del_key = key_context + ".delete"
hou.hotkeys.addCommand(del_key, "Delete", "Delete the current change")
dup_key = key_context + ".duplicate"
hou.hotkeys.addCommand(
    dup_key, "Duplicate", "Copies the current change into a new change"
)
end_key = key_context + ".finish"
hou.hotkeys.addCommand(end_key, "Finish", "Saves the current change")

# メニューを構築します。
menu = hou.ViewerStateMenu('bend_menu', 'Bend')
# 追加引数としてホットキー記号を使って、addなんちゃらItemメソッドをコールします。
menu.addActionItem("delete", "Delete", del_key)
menu.addActionItem("dup", "Duplicate", dup_key)
menu.addActionItem("done", "Finish", end_key)

Pythonビューアステート

Pythonスクリプト

はじめよう

次のステップ

Pythonビューアステート

Pythonでビューアステートを記述することで、ビューポート内でノードのユーザー操作をカスタマイズすることができます。

導師レベル

リファレンス

  • hou

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

  • Alembic拡張関数

    Alembicファイルから情報を抽出するための便利な関数です。