Python - Viewer Handle Module - Current Node

   910   3   2
User Avatar
Member
143 posts
Joined: May 2017
Offline
Hello everyone,

I have a question about the Viewer Handle Module, namely how to access the current node from the module. Unlike Viewer State, where it is available from almost every event handler from the dictionary (kwargs) as a "Node" key, it does not exist in the Viewer Handle context.
...
    def onEnter(self, kwargs):
        self.node = kwargs["node"]

It's basically not necessary if it's just to read the node parameters, since you can bind them and export them to viewer state.
def createViewerHandleTemplate():
    ...
    # Register handle parameters.
    template.bindParameter(hou.parmTemplateType.Float, name="tx", default_value=0.0)
    template.bindParameter(hou.parmTemplateType.Float, name="ty", default_value=0.0)
    template.bindParameter(hou.parmTemplateType.Float, name="tz", default_value=0.0)

    # Export handle parameters.
    template.exportParameters(["tx", "ty", "tz"])
But is limited to float and interger values. So I could not use a string to make certain settings in the handle module. As I understand the concept so far, such management settings are done in the viewer state. But I am curious if there is a solution for the handle module.

For example, there is a way to access it through the "scene_viewer" entry, but I don't get the current node that the handle is bound to (SOP), instead the parent GEO node.
class Handle(object):
    def __init__(self, **kwargs):
        ...

        self.scene_viewer = kwargs["scene_viewer"] 
        self.node = self.scene_viewer.pwd() 
        
        print(self.node) 
        # geo1

I could now output a list via the children() method and somehow try to access the node I'm looking for. But this seems to be quite undynamic.

Does anyone know a better way?
Edited by viklc - April 15, 2023 10:19:12
User Avatar
Member
122 posts
Joined: June 2019
Offline
One workaround I've found here: https://www.sidefx.com/forum/topic/79333/ [www.sidefx.com] is passing node session id as an integer from viewer state.

but basically I guess it's an antipattern to create a handle depending on node or viewer state
ideally it's a stateless manipulator that can just export its values to parameters so it can be usable everywhere
User Avatar
Member
143 posts
Joined: May 2017
Offline
Hi elovikov,
I actually did a similar attempt with applySettings, but with the node path, as a string.
    
...
    def onEnter(self, kwargs):
        node_path = kwargs["node"].path()
        self.handle.applySettings("node_path({})".format(node_path))

But strings are not updated this way. Then I came across to your solution, which works without further ado - thanks. Session ID was unfamiliar to me, seems to be very handy. It can also be passed via the parameters since it is an integer.

elovikov
but basically I guess it's an antipattern to create a handle depending on node or viewer state

Yes, this is obvious after some practical attempts. I felt a bit confused at the beginning, since in the Viewer State you can basically achieve any functionality with a Gatget Drawable that corresponds to a Handle. In the UI event the picking, dragging, i.e. the interaction with the gatget (handle); And for node parameters a custom ParmTupleChanged callback can be registered or a pre-built one can be bound. Especially since the onDraw event always reacts to parameters, but this should be kept apart.
def createViewerStateTemplate():
    ...
    template.bindNodeChangeEvent((hou.nodeEventType.NameChanged, ))
    
    return template

class State(object):
    ...
  
    def onNodeChangeEvent(self, kwargs):
        print("Node parm. changed.")

What I mean by that is you don't have to register a handle and work with its event handler like onHandleToState or onStateToHandle. But after all they are useful. The same way to keep Viewer Handle separate from Viewer State. For example, you can create a library of different handles and access and manage them anytime in the viewer state - which is quite convenient.

-

I have another approach for the Node Session ID for accessing SOP Geometry. But this is as you say tied to the node/HDA only, which might be useful for some cases. As the HDA is usually selected to access the viewer state, you can simply do the following in the viewer handle module:
class Handle(object):
    def __init__(self, **kwargs):
        ...

        self.node = hou.selectedNodes()
        geo = self.node[0].node("OUT_Handle_Geo").geometry()
        self.handle_gatget.setGeometry(geo)
        self.handle_gatget.show(True)

This also works if you want to pass SOP Geometry to a handle drawable. However, Side Fx points out in the documentation that verbs should be used for handles.


Cheers
Edited by viklc - April 17, 2023 18:31:21
User Avatar
Staff
401 posts
Joined: Feb. 2018
Offline
It's important to understand that Python handles much like any built-in handles are nodes and states agnostic and should be designed independently in order to be generic as much as possible and re-usable with other states. The concept of handles is to allow users to control node parms by manipulating the handle parameters from the viewport, you shouldn't have to pass a node or geometry to the handle to achieve this functionality.

As for your initial questions, handle parameters are limited to integers and floats by design. You can however create handle settings with bindSetting which offers a much wider choice of types. These settings are meant for initializing the handles at runtime.
  • Quick Links