git.lucas.co / hou-control
git clone https://git.lucas.co/hou-control.git

commit0e47a45d3c272314fd07d3a8e59ef0af5ad630b3
parent252314e0b3
authorLucas Galante <[email protected]>
date2026-04-05 20:31
hooks: update uiready.py

 python3.11libs/uiready.py   |   8 +-
 scripts/OnCreated.py        |  23 +-
 scripts/hotkeys_assign.json |  17 --
 scripts/hotkeys_clear.json  |  32 ++-
 scripts/settings.json       |  32 +--
 viewer_states/keycam.py     | 500 +++++++++++++++++++++++---------------------
 6 files changed, 328 insertions(+), 284 deletions(-)

diff --git a/python3.11libs/uiready.py b/python3.11libs/uiready.py
index 4f397d5..f55b2fa 100644
--- a/python3.11libs/uiready.py
+++ b/python3.11libs/uiready.py
@@ -1,11 +1,11 @@
 import hou
-from hc import Session
+from hc import HCSession
 from PySide6.QtCore import Qt
 
-session = Session()
+hc_session = HCSession()
 
-session.reloadHotkeys()
-session.toggleStowbars()
+hc_session.reloadHotkeys()
+hc_session.toggleStowbars()
 
 ## Set network grid points to on
 # networkeditors = desktop.getNetworkEditors()
diff --git a/scripts/OnCreated.py b/scripts/OnCreated.py
index 1e84102..e67c799 100644
--- a/scripts/OnCreated.py
+++ b/scripts/OnCreated.py
@@ -1,13 +1,18 @@
 import hou
-from hc import Prefs
-from hc import Node
-# import from hc.NetworkEditor import snapToGrid
+from hc import HCPrefs
+from hc import HCNode
+# import from hc.HCNetworkEditor import snapToGrid
 
-# Get the node that was just created
-node = Node(kwargs['node'])
+node = HCNode(kwargs['node'])
+prefs = HCPrefs().prefs()
 
-# Force the shape to your desired JSON name
-# (e.g., "rect", "circle", or your custom "my_shape")
 node.setUserData("nodeshape", "rect")
-node.setColor(hou.Color(Prefs().node_color))
-# snapToGrid(node)
+
+hex_color = prefs.get('node_graph').get('node_color')
+hex_color = hex_color.lstrip('#')
+r = int(hex_color[0:2], 16) / 255.0
+g = int(hex_color[2:4], 16) / 255.0
+b = int(hex_color[4:6], 16) / 255.0
+print(r, g, b)
+node.setColor(hou.Color(r, g, b))
+# snapToGrid(hc_node)
diff --git a/scripts/hotkeys_assign.json b/scripts/hotkeys_assign.json
index 40eeadc..c1b1bda 100644
--- a/scripts/hotkeys_assign.json
+++ b/scripts/hotkeys_assign.json
@@ -1,38 +1,21 @@
 {
     "assignments": {
         "h.hc_panel":                             "alt+shift+d",
-        "h.stow_shelf":                           "ctrl+]",
         "h.hc_session_keycam":                    "shift+k",
-        "h.pane.shelf":                           "ctrl+[",
         "h.pane.nexttab":                         "ctrl+tab",
         "h.pane.prevtab":                         "ctrl+shift+tab",
         "h.pane.gview.refplane":                  "shift+g",
         "h.pane.gview.state.view.home_selection": "shift+s",
         "h.pane.parms.edit_expression":           "alt+e",
         "h.pane.editparms.selectall":             "alt+h",
-        "h.pane.wsheet.bypass_mode":              "q",
-        "h.pane.wsheet.doautomovenodes":          "ctrl+pad9",
         "h.pane.wsheet.up":                       "ctrl+k",
         "h.pane.wsheet.down":                     "ctrl+j",
         "h.pane.wsheet.left":                     "ctrl+h",
         "h.pane.wsheet.right":                    "ctrl+l",
         "h.pane.wsheet.drop_on_wire_mode":        "pad8",
-        "h.pane.wsheet.flag2_mode":               "w",
-        "h.pane.wsheet.flag3_mode":               "e",
         "h.pane.wsheet.home_selected":            "shift+s",
-        "h.pane.wsheet.jump_mark_1":              "pad1",
-        "h.pane.wsheet.jump_mark_2":              "pad2",
-        "h.pane.wsheet.jump_mark_3":              "pad3",
-        "h.pane.wsheet.jump_mark_4":              "pad4",
-        "h.pane.wsheet.jump_mark_5":              "pad5",
         "h.pane.wsheet.node_quick_nav":           "ctrl+f",
         "h.pane.wsheet.rename_node":              "ctrl+r",
-        "h.pane.wsheet.set_mark_1":               "ctrl+pad1",
-        "h.pane.wsheet.set_mark_2":               "ctrl+pad2",
-        "h.pane.wsheet.set_mark_3":               "ctrl+pad3",
-        "h.pane.wsheet.set_mark_4":               "ctrl+pad4",
-        "h.pane.wsheet.set_mark_5":               "ctrl+pad5",
-        "h.pane.wsheet.select":                   "ctrl+a",
         "h.pane.textport.up":                     "ctrl+p",
         "h.pane.textport.down":                   "ctrl+n",
         "h.pane.textport.next":                   "ctrl+f",
diff --git a/scripts/hotkeys_clear.json b/scripts/hotkeys_clear.json
index 3708672..57ecada 100644
--- a/scripts/hotkeys_clear.json
+++ b/scripts/hotkeys_clear.json
@@ -80,16 +80,25 @@
         "h.pane.textport.zoom_in",
         "h.pane.textport.zoom_out",
         "h.pane.textport.zoom_reset",
-        "h.pane.wsheet.change_wire_style",
         "h.pane.wsheet.add_image",
         "h.pane.wsheet.add_postit",
         "h.pane.wsheet.addfileop",
         "h.pane.wsheet.allowdroponwire",
         "h.pane.wsheet.batch_rename",
         "h.pane.wsheet.bypass_mode",
+        "h.pane.wsheet.change_wire_style",
+        "h.pane.wsheet.collapse",
         "h.pane.wsheet.color_palette",
+        "h.pane.wsheet.create_netbox",
+        "h.pane.wsheet.cut_wires_mode",
+        "h.pane.wsheet.del_netbox_and_contents",
+        "h.pane.wsheet.dirty_cook_selected_node",
+        "h.pane.wsheet.dirty_node",
         "h.pane.wsheet.doautomovenodes",
         "h.pane.wsheet.dosnapping",
+        "h.pane.wsheet.down",
+        "h.pane.wsheet.drop_on_wire_mode",
+        "h.pane.wsheet.edit_images",
         "h.pane.wsheet.expose",
         "h.pane.wsheet.flag1_mode",
         "h.pane.wsheet.flag2_mode",
@@ -97,30 +106,41 @@
         "h.pane.wsheet.flag4_mode",
         "h.pane.wsheet.home",
         "h.pane.wsheet.home_selection",
-        "h.pane.wsheet.up",
-        "h.pane.wsheet.down",
-        "h.pane.wsheet.left",
-        "h.pane.wsheet.right",
-        "h.pane.wsheet.edit_images",
+        "h.pane.wsheet.jump_mark_undo",
+        "h.pane.wsheet.jump_mark_1",
+        "h.pane.wsheet.jump_mark_2",
+        "h.pane.wsheet.jump_mark_3",
+        "h.pane.wsheet.jump_mark_4",
+        "h.pane.wsheet.jump_mark_5",
         "h.pane.wsheet.layout",
         "h.pane.wsheet.layout_all",
         "h.pane.wsheet.layout_mode",
+        "h.pane.wsheet.left",
         "h.pane.wsheet.maximize",
         "h.pane.wsheet.maximize_adjust",
         "h.pane.wsheet.minimize",
         "h.pane.wsheet.minimize_adjust",
         "h.pane.wsheet.mode_switch",
+        "h.pane.wsheet.next_wi",
         "h.pane.wsheet.node_quick_nav",
         "h.pane.wsheet.open_display_options",
         "h.pane.wsheet.open_groups",
         "h.pane.wsheet.open_treeview",
         "h.pane.wsheet.opmenu.flag_debug",
+        "h.pane.wsheet.prev_wi",
         "h.pane.wsheet.rename_node",
+        "h.pane.wsheet.right",
         "h.pane.wsheet.scope_chans",
         "h.pane.wsheet.select",
         "h.pane.wsheet.select_mode",
+        "h.pane.wsheet.set_mark_1",
+        "h.pane.wsheet.set_mark_2",
+        "h.pane.wsheet.set_mark_3",
+        "h.pane.wsheet.set_mark_4",
+        "h.pane.wsheet.set_mark_5",
         "h.pane.wsheet.snap_netbox_bounds",
         "h.pane.wsheet.stitch_mode",
+        "h.pane.wsheet.up",
         "h.pane.wsheet.zoom_in",
         "h.pane.wsheet.zoom_out",
         "h.playbar.next_bookmark",
diff --git a/scripts/settings.json b/scripts/settings.json
index f5f44ab..326e412 100644
--- a/scripts/settings.json
+++ b/scripts/settings.json
@@ -1,16 +1,16 @@
 {
     "keycam": {
         "guides": {
-            "axis_size": 1,
-            "bbox": 0,
-            "cam_axis": 0,
-            "cam_geo": 1,
-            "perim": 0,
-            "pivot_2d": 0,
-            "pivot_3d": 0,
-            "pivot_axis": 1,
-            "ray": 0,
-            "tie_axis_to_radius": 0
+            "axis_size": 0.2,
+            "bbox_enabled": 0,
+            "cam_axis_enabled": 0,
+            "cam_geo_enabled": 1,
+            "perim_enabled": 0,
+            "pivot_2d_enabled": 0,
+            "pivot_3d_enabled": 0,
+            "pivot_axis_enabled": 1,
+            "ray_enabled": 0,
+            "tie_axis_to_radius_enabled": 0
         },
         "startup": {
             "center_on_geo": 1,
@@ -18,12 +18,14 @@
             "reset": 1
         },
         "units": {
-            "r": 7.5,
-            "t": 1,
-            "ow": 1,
-            "dist": 1
+            "delta_ow": 1,
+            "delta_r": 7.5,
+            "delta_t": 0.2,
+            "delta_z": 1
         }
     },
     "node_graph": {
-        "node_shape": "rect"
+        "node_shape": "rect",
+        "node_color": "#607070"
+    }
 }
diff --git a/viewer_states/keycam.py b/viewer_states/keycam.py
index ddd88fd..a711a9f 100644
--- a/viewer_states/keycam.py
+++ b/viewer_states/keycam.py
@@ -1,269 +1,107 @@
 import hou
-from hc import Cam, Geo, Guides, SceneViewer
-
-
-def createViewerStateTemplate():
-    template = hou.ViewerStateTemplate(
-        type_name='keycam', label='keycam',
-        category=hou.objNodeTypeCategory(),
-        contexts=[hou.sopNodeTypeCategory()]
-    )
-
-    template.bindFactory(State)
-    template.bindIcon('DESKTOP_application_sierra')
-
-    # Parameters
-
-    template.bindParameter(
-        hou.parmTemplateType.Menu,
-        name='layout', label='Layout',
-        default_value='single',
-        menu_items=[
-            ('doubleside',        'DoubleSide'),
-            ('doublestack',       'DoubleStack'),
-            ('quad',              'Quad'),
-            ('quadbottomsplit',   'QuadBottomSplit'),
-            ('quadleftsplit',     'QuadLeftSplit'),
-            ('single',            'Single'),
-            ('triplebottomsplit', 'TripleBottomSplit'),
-            ('tripleleftsplit',   'TripleLeftSplit')
-        ]
-    )
-
-    template.bindParameter(
-        hou.parmTemplateType.Menu,
-        name='viewport', label='Viewport',
-        default_value='center',
-        menu_items=[('center', 'Center')]
-    )
-
-    template.bindParameter(
-        hou.parmTemplateType.Menu,
-        name='view', label='View',
-        default_value='persp',
-        menu_items=[
-            ('persp',  'Perspective'),
-            ('top',    'Top'),
-            ('front',  'Front'),
-            ('right',  'Right'),
-            ('uv',     'UV'),
-            ('bottom', 'Bottom'),
-            ('back',   'Back'),
-            ('left',   'Left')
-        ]
-    )
-
-    template.bindParameter(
-        hou.parmTemplateType.Menu,
-        name='camera', label='Camera',
-        default_value='keycam',
-        menu_items=[
-            ('keycam',  'Keycam'),
-            ('default', 'Default'),
-            ('other',   'Other')
-        ],
-        toolbox=False
-    )
-
-    template.bindParameter(
-        hou.parmTemplateType.Menu,
-        name='target', label='Target',
-        default_value='cam',
-        menu_items=[
-            ('cam',   'Camera'),
-            ('pivot', 'Pivot')
-        ],
-        toolbox=False
-    )
-
-    template.bindParameter(
-        hou.parmTemplateType.Separator,
-        toolbox=False
-    )
-
-    template.bindParameter(
-        hou.parmTemplateType.Float,
-        name='t', label='Translation',
-        num_components=3,
-        toolbox=False
-    )
-
-    # template.bindParameter(
-    #     hou.parmTemplateType.Float,
-    #     name='r', label='Rotation',
-    #     num_components=3,
-    #     toolbox=False
-    # )
-
-    template.bindParameter(
-        hou.parmTemplateType.Float,
-        name='p', label='Pivot',
-        num_components=3,
-        toolbox=False
-    )
-
-    template.bindParameter(
-        hou.parmTemplateType.Float,
-        name='zoom', label='Zoom',
-        num_components=1,
-        toolbox=False
-    )
-
-    template.bindParameter(
-        hou.parmTemplateType.Float,
-        name='ow', label='Ortho Width',
-        num_components=1,
-        toolbox=False
-    )
-
-    template.bindParameter(
-        hou.parmTemplateType.Separator,
-        toolbox=False
-    )
-
-    template.bindParameter(
-        hou.parmTemplateType.Float,
-        name='delta_t', label='Delta T',
-        default_value=1.0,
-        min_limit=0, max_limit=10.0
-    )
-
-    template.bindParameter(
-        hou.parmTemplateType.Float,
-        name='delta_r', label='Delta R',
-        default_value=15.0,
-        min_limit=-180.0, max_limit=180.0
-    )
-
-    template.bindParameter(
-        hou.parmTemplateType.Float,
-        name='delta_zoom', label='Delta Zoom',
-        default_value=1.0,
-        min_limit=0, max_limit=10.0
-    )
-
-    template.bindParameter(
-        hou.parmTemplateType.Float,
-        name='delta_ow', label='Delta OW',
-        default_value=1.0,
-        min_limit=0, max_limit=10.0
-    )
-
-
-    """ Context Menu """
-
-    menu = hou.ViewerStateMenu('keycam_menu', 'Keycam Menu')
-    menu.addActionItem('frame', 'Frame')
-    menu.addActionItem('reset', 'Reset')
-
-    setViewMenu = hou.ViewerStateMenu('set_view_menu', 'Set View')
-    setViewMenu.addActionItem('top',    'Top')
-    setViewMenu.addActionItem('bottom', 'Bottom')
-    setViewMenu.addActionItem('front',  'Front')
-    setViewMenu.addActionItem('back',   'Back')
-    setViewMenu.addActionItem('left',   'Left')
-    # setViewMenu.addActionItem('right', 'Right')
-    menu.addMenu(setViewMenu)
-
-    guideMenu = hou.ViewerStateMenu('guide_menu', 'Guides')
-    guideMenu.addToggleItem('bbox',       'Bbox',        0)
-    guideMenu.addToggleItem('cam_axis',   'Camera Axis', 0)
-    guideMenu.addToggleItem('pivot_axis', 'Pivot Axis',  0)
-    guideMenu.addToggleItem('perim',      'Perimeter',   0)
-    guideMenu.addToggleItem('pivot_2d',   '2D Pivot',    0)
-    guideMenu.addToggleItem('pivot_3d',   '3D Pivot',    0)
-    guideMenu.addToggleItem('ray',        'Ray',         0)
-    menu.addMenu(guideMenu)
-
-    menu.addSeparator()
-    template.bindMenu(menu)
-
-    # Ok
-    return template
+from hc import HCCam, HCPrefs, HCGeo, HCGuides, HCSceneViewer
 
 
+# Required to pass in hou.SceneViewer object
 class State(object):
     def __init__(self, state_name, scene_viewer):
         self.options = {
             'center_on_geo': 1,
-            'lock_cam':      1,
-            'reset':         0
+            'lock_cam': 1,
+            'reset': 0
         }
 
-        """ Check for cam node """
+        # Check for cam node
         if not hou.node('/obj/keycam'):
             cam_node = hou.node('/obj').createNode('cam')
             cam_node.setName('keycam')
             cam_node.parm('xOrd').set(0)
         self.cam_node = hou.node('/obj/keycam')
 
-        self.hou_scene_viewer = scene_viewer
-        self.scene_viewer = None
+        # Get scene viewer
+        self.scene_viewer = HCSceneViewer(scene_viewer)
+
+        # Assign state name
         self.state_name = state_name
-        self.cam = None
+
+        # Setup hc cam
+        self.cam = HCCam(self.cam_node, self.scene_viewer)
+        prefs = HCPrefs().prefs().get('keycam')
+        self.cam.delta_r = prefs.get('units').get('delta_r')
+        self.cam.delta_t = prefs.get('units').get('delta_t')
+
+
+
+        # Setup default cam
         # self.default_cam = DefaultCam(self, hou_default_cam)
+
+        # Initialize empty objects
         self.kwargs = None
         self.guides = None
         self.hud = None
 
-    def onDraw(self, kwargs):
-        self.guides.draw(kwargs)
-
-    def onExit(self, kwargs):
-        for viewport in self.scene_viewer.allViewports():
-            viewport.lockCameraToView(False)
+        # Status message
+        context = self.scene_viewer.pwd().childCat()
+        context_map = {
+            'Object': 'Keycam: obj context',
+            'Sop': 'Keycam: sop context',
+            'Lop': 'Keycam: lop context'
+        }
+        hou.ui.setStatusMessage(context_map[context])
 
     def onGenerate(self, kwargs):
         self.kwargs = kwargs
-        self.scene_viewer = SceneViewer(self.hou_scene_viewer)
-        self.cam = Cam(self.cam_node, self.hou_scene_viewer)
-        self.guides = Guides(self)
-        """ Prevent exiting state when current node changes """
+        self.guides = HCGuides(self)
+        # Prevent exiting state when current node changes
         self.kwargs['state_flags']['exit_on_node_select'] = False
         self.guides.update()
         self.hud = Hud(self)
         self.hud.update()
 
+    def onDraw(self, kwargs):
+        self.guides.draw(kwargs)
+
+    def onExit(self, kwargs):
+        for viewport in self.scene_viewer.allViewports():
+            viewport.lockCameraToView(False)
+
     def onKeyEvent(self, kwargs):
         self.cam.fitAspectRatio()
 
         key_map = {
-            'o':       self.cam.toggleProjection,
-            'h':       self.cam.rotateLeft,
-            'j':       self.cam.rotateDown,
-            'k':       self.cam.rotateUp,
-            'l':       self.cam.rotateRight,
-            # 'v':       self.scene_viewer.nextView,
+            'o': self.cam.toggleProjection,
+            'h': self.cam.rotateLeft,
+            'j': self.cam.rotateDown,
+            'k': self.cam.rotateUp,
+            'l': self.cam.rotateRight,
+            # 'v': self.scene_viewer.nextView,
             'Shift+h': self.cam.translateLeft,
             'Shift+j': self.cam.translateDown,
             'Shift+k': self.cam.translateUp,
             'Shift+l': self.cam.translateRight,
-            # 'Ctrl+l':  self.scene_viewer.nextLayout,
-            'f':       self.cam.frame
-            # 'c':       self.cam.center
+            # 'Ctrl+l': self.scene_viewer.nextLayout,
+            'f': self.cam.frame
+            # 'c': self.cam.center
         }
-
         key_map_persp = {
-            '-':       lambda: self.cam.zoom('out'),
-            '=':       lambda: self.cam.zoom('in'),
+            '-': lambda: self.cam.zoom('out'),
+            '=': lambda: self.cam.zoom('in'),
             'Shift+-': lambda: self.cam.zoomOrtho('out'),
             'Shift+=': lambda: self.cam.zoomOrtho('in')
         }
-
         key_map_ortho = {
-            '-':       lambda: self.cam.zoomOrtho('out'),
-            '=':       lambda: self.cam.zoomOrtho('in'),
+            '-': lambda: self.cam.zoomOrtho('out'),
+            '=': lambda: self.cam.zoomOrtho('in'),
             'Shift+-': lambda: self.cam.zoom('out'),
             'Shift+=': lambda: self.cam.zoom('in')
         }
 
         if self.cam.projection == 'perspective':
-            key_map.update(key_map_persp)
+            key_map = key_map | key_map_persp
         else:
-            key_map.update(key_map_ortho)
+            key_map = key_map | key_map_ortho
 
         key = kwargs['ui_event'].device().keyString()
-
         if key in key_map:
             key_map[key]()
             print(self.cam.p)
@@ -277,12 +115,10 @@ class State(object):
     def onMenuAction(self, kwargs):
         menu_item = kwargs['menu_item']
         guides = self.guides
-
         action_map = {
             'frame': self.cam.frame,
             'reset': self.cam.reset
         }
-
         if menu_item in action_map:
             return action_map[menu_item]()
         else:
@@ -291,17 +127,18 @@ class State(object):
 
     def onMenuPreOpen(self, kwargs):
         menu_id = kwargs['menu']
-        item_states = kwargs['menu_item_states']
+        menu_item_states = kwargs['menu_item_states']
+        guides = self.guides
 
-        """ Set the checkbox states when opening guide menu """
+        # Set checkbox states when opening guide menu
         if menu_id == 'guide_menu':
-            item_states['cam_axis']['value']   = self.guides.cam_axis.visible()
-            item_states['pivot_axis']['value'] = self.guides.pivot_axis.visible()
-            item_states['bbox']['value']       = self.guides.bbox.visible()
-            item_states['perim']['value']      = self.guides.perim.visible()
-            item_states['pivot_2d']['value']   = self.guides.pivot_2d.visible()
-            item_states['pivot_3d']['value']   = self.guides.pivot_3d.visible()
-            item_states['ray']['value']        = self.guides.ray.visible()
+            menu_item_states['cam_axis']['value'] = guides.cam_axis.visible()
+            menu_item_states['pivot_axis']['value'] = guides.pivot_axis.visible()
+            menu_item_states['bbox']['value'] = guides.bbox.visible()
+            menu_item_states['perim']['value'] = guides.perim.visible()
+            menu_item_states['pivot_2d']['value'] = guides.pivot_2d.visible()
+            menu_item_states['pivot_3d']['value'] = guides.pivot_3d.visible()
+            menu_item_states['ray']['value'] = guides.ray.visible()
 
     def onParmChangeEvent(self, kwargs):
         parm_map = {
@@ -320,7 +157,6 @@ class State(object):
             # 'view':       (self.cam, 'view'),
             # 'camera':     (self.cam, 'camera'),
         }
-
         obj = parm_map[kwargs['parm_name']][0]
         attr = parm_map[kwargs['parm_name']][1]
         setattr(obj, attr, kwargs['parm_value'])
@@ -329,10 +165,9 @@ class State(object):
 
 class Hud:
     def __init__(self, state):
-        self.state            = state
-        self.hou_scene_viewer = state.hou_scene_viewer
-        self.scene_viewer     = state.scene_viewer
-        self.template         = {
+        self.state = state
+        self.scene_viewer = state.scene_viewer
+        self.template = {
             'title': 'Keycam',
             'rows': [
                 {
@@ -418,9 +253,208 @@ class Hud:
         self.template['rows'][3]['count'] = n_viewports
 
         # Updates
-        self.hou_scene_viewer.hudInfo(template=self.template)
+        hou_scene_viewer = self.scene_viewer.hou_tab
+        hou_scene_viewer.hudInfo(template=self.template)
         updates = {
             'layout':   str(self.scene_viewer.layout())[23:],
             'layout_g': self.scene_viewer.layouts().index(self.scene_viewer.layout()),
         }
-        self.hou_scene_viewer.hudInfo(hud_values=updates)
+        hou_scene_viewer.hudInfo(hud_values=updates)
+
+
+def createViewerStateTemplate():
+    template = hou.ViewerStateTemplate(
+        type_name='keycam', label='keycam',
+        category=hou.objNodeTypeCategory(),
+        contexts=[hou.sopNodeTypeCategory()]
+    )
+
+    template.bindFactory(State)
+    template.bindIcon('DESKTOP_application_sierra')
+
+    # Parameters
+
+    template.bindParameter(
+        hou.parmTemplateType.Menu,
+        name='layout',
+        label='Layout',
+        default_value='single',
+        menu_items=[
+            ('doubleside',        'DoubleSide'),
+            ('doublestack',       'DoubleStack'),
+            ('quad',              'Quad'),
+            ('quadbottomsplit',   'QuadBottomSplit'),
+            ('quadleftsplit',     'QuadLeftSplit'),
+            ('single',            'Single'),
+            ('triplebottomsplit', 'TripleBottomSplit'),
+            ('tripleleftsplit',   'TripleLeftSplit')
+        ]
+    )
+
+    template.bindParameter(
+        hou.parmTemplateType.Menu,
+        name='viewport',
+        label='Viewport',
+        default_value='center',
+        menu_items=[('center', 'Center')]
+    )
+
+    template.bindParameter(
+        hou.parmTemplateType.Menu,
+        name='view',
+        label='View',
+        default_value='persp',
+        menu_items=[
+            ('persp',  'Perspective'),
+            ('top',    'Top'),
+            ('front',  'Front'),
+            ('right',  'Right'),
+            ('uv',     'UV'),
+            ('bottom', 'Bottom'),
+            ('back',   'Back'),
+            ('left',   'Left')
+        ]
+    )
+
+    template.bindParameter(
+        hou.parmTemplateType.Menu,
+        name='camera',
+        label='Camera',
+        default_value='keycam',
+        menu_items=[
+            ('keycam',  'Keycam'),
+            ('default', 'Default'),
+            ('other',   'Other')
+        ],
+        toolbox=False
+    )
+
+    template.bindParameter(
+        hou.parmTemplateType.Menu,
+        name='target',
+        label='Target',
+        default_value='cam',
+        menu_items=[
+            ('cam',   'Camera'),
+            ('pivot', 'Pivot')
+        ],
+        toolbox=False
+    )
+
+    template.bindParameter(
+        hou.parmTemplateType.Separator,
+        toolbox=False
+    )
+
+    template.bindParameter(
+        hou.parmTemplateType.Float,
+        name='t',
+        label='Translation',
+        num_components=3,
+        toolbox=False
+    )
+
+    # template.bindParameter(
+    #     hou.parmTemplateType.Float,
+    #     name='r',
+    #     label='Rotation',
+    #     num_components=3,
+    #     toolbox=False
+    # )
+
+    template.bindParameter(
+        hou.parmTemplateType.Float,
+        name='p',
+        label='Pivot',
+        num_components=3,
+        toolbox=False
+    )
+
+    template.bindParameter(
+        hou.parmTemplateType.Float,
+        name='zoom',
+        label='Zoom',
+        num_components=1,
+        toolbox=False
+    )
+
+    template.bindParameter(
+        hou.parmTemplateType.Float,
+        name='ow',
+        label='Ortho Width',
+        num_components=1,
+        toolbox=False
+    )
+
+    template.bindParameter(
+        hou.parmTemplateType.Separator,
+        toolbox=False
+    )
+
+    template.bindParameter(
+        hou.parmTemplateType.Float,
+        name='delta_t',
+        label='Delta T',
+        default_value=1.0,
+        min_limit=0,
+        max_limit=10.0
+    )
+
+    template.bindParameter(
+        hou.parmTemplateType.Float,
+        name='delta_r',
+        label='Delta R',
+        default_value=15.0,
+        min_limit=-180.0,
+        max_limit=180.0
+    )
+
+    template.bindParameter(
+        hou.parmTemplateType.Float,
+        name='delta_zoom',
+        label='Delta Zoom',
+        default_value=1.0,
+        min_limit=0,
+        max_limit=10.0
+    )
+
+    template.bindParameter(
+        hou.parmTemplateType.Float,
+        name='delta_ow',
+        label='Delta OW',
+        default_value=1.0,
+        min_limit=0,
+        max_limit=10.0
+    )
+
+    """ Context Menu """
+
+    menu = hou.ViewerStateMenu('keycam_menu', 'Keycam Menu')
+    menu.addActionItem('frame', 'Frame')
+    menu.addActionItem('reset', 'Reset')
+
+    set_view_menu = hou.ViewerStateMenu('set_view_menu', 'Set View')
+    set_view_menu.addActionItem('top',    'Top')
+    set_view_menu.addActionItem('bottom', 'Bottom')
+    set_view_menu.addActionItem('front',  'Front')
+    set_view_menu.addActionItem('back',   'Back')
+    set_view_menu.addActionItem('left',   'Left')
+    # set_view_menu.addActionItem('right', 'Right')
+    menu.addMenu(set_view_menu)
+
+    guide_menu = hou.ViewerStateMenu('guide_menu', 'Guides')
+    guide_menu.addToggleItem('bbox',       'Bbox',        0)
+    guide_menu.addToggleItem('cam_axis',   'Camera Axis', 0)
+    guide_menu.addToggleItem('pivot_axis', 'Pivot Axis',  0)
+    guide_menu.addToggleItem('perim',      'Perimeter',   0)
+    guide_menu.addToggleItem('pivot_2d',   '2D Pivot',    0)
+    guide_menu.addToggleItem('pivot_3d',   '3D Pivot',    0)
+    guide_menu.addToggleItem('ray',        'Ray',         0)
+    menu.addMenu(guide_menu)
+
+    menu.addSeparator()
+    template.bindMenu(menu)
+
+    # Ok
+    return template
+