git clone https://git.lucas.co/hou-control.git
keycam: fully implement PluginHotkey system with menu-linked symbols
viewer_states/keycam.py | 149 ++++++++++++++++++++----------------------------
1 file changed, 61 insertions(+), 88 deletions(-)
diff --git a/viewer_states/keycam.py b/viewer_states/keycam.py
index a8d4c53..4be4c41 100644
--- a/viewer_states/keycam.py
+++ b/viewer_states/keycam.py
@@ -66,32 +66,16 @@ class State(object):
viewport.lockCameraToView(False)
def onKeyEvent(self, kwargs):
- self.cam.fitAspectRatio()
- ui_event = kwargs['ui_event']
- device = ui_event.device()
- key = device.keyString()
- is_shift = device.isShiftKey()
-
- # Manual mapping as fallback/primary if symbols fail to resolve
- key_map = {
- 'h': self.cam.rotateLeft,
- 'l': self.cam.rotateRight,
- 'k': self.cam.rotateUp,
- 'j': self.cam.rotateDown,
- 'f': self.cam.frame,
- 'o': self.cam.toggleProjection,
- }
-
- shift_key_map = {
- 'H': self.cam.translateLeft,
- 'L': self.cam.translateRight,
- 'K': self.cam.translateUp,
- 'J': self.cam.translateDown,
- }
+ """Keys are now handled via PluginHotkeyDefinitions and onMenuAction."""
+ return False
- ctx = 'h.pane.gview.state.obj.keycam'
+ def onKeyboardEvent(self, kwargs):
+ return self.onKeyEvent(kwargs)
- # 1. Define zoom behavior based on projection
+ def onMenuAction(self, kwargs):
+ menu_item = kwargs['menu_item']
+
+ # Define zoom behavior based on projection
def zoom_in():
self.cam.zoom('in') if self.cam.projection == 'perspective' else self.cam.zoomOrtho('in')
def zoom_out():
@@ -101,67 +85,36 @@ class State(object):
def zoom_shift_out():
self.cam.zoomOrtho('out') if self.cam.projection == 'perspective' else self.cam.zoom('out')
- # 2. Check defined plugin hotkeys
- hc_symbol_map = {
- f'{ctx}.rotate_left': self.cam.rotateLeft,
- f'{ctx}.rotate_right': self.cam.rotateRight,
- f'{ctx}.rotate_up': self.cam.rotateUp,
- f'{ctx}.rotate_down': self.cam.rotateDown,
- f'{ctx}.trans_left': self.cam.translateLeft,
- f'{ctx}.trans_right': self.cam.translateRight,
- f'{ctx}.trans_up': self.cam.translateUp,
- f'{ctx}.trans_down': self.cam.translateDown,
- f'{ctx}.frame': self.cam.frame,
- f'{ctx}.toggle_proj': self.cam.toggleProjection,
- f'{ctx}.zoom_in': zoom_in,
- f'{ctx}.zoom_out': zoom_out,
- f'{ctx}.zoom_ortho_in': zoom_shift_in,
- f'{ctx}.zoom_ortho_out':zoom_shift_out,
- }
-
- for symbol, func in hc_symbol_map.items():
- if hou.hotkeys.isKeyMatch(ui_event, symbol):
- func()
- self.guides.update()
- return True
-
- # 3. Handle Navigation Fallback
- if is_shift:
- # Shift+K might come through as 'K' or 'Shift+K'
- # We strip 'Shift+' and use uppercase
- lookup = key.replace('Shift+', '').upper()
- if lookup in shift_key_map:
- shift_key_map[lookup]()
- self.guides.update()
- return True
- else:
- lookup = key.lower()
- if lookup in key_map:
- key_map[lookup]()
- self.guides.update()
- return True
-
- # 4. Explicitly consume 'j' to prevent "Modify Channels"
- if key.lower() == 'j':
- return True
-
- return False
-
- def onKeyboardEvent(self, kwargs):
- return self.onKeyEvent(kwargs)
-
- def onMenuAction(self, kwargs):
- menu_item = kwargs['menu_item']
- guides = self.guides
action_map = {
- 'frame': self.cam.frame,
- 'reset': self.cam.reset
+ 'frame': self.cam.frame,
+ 'reset': self.cam.reset,
+ 'rotate_left': self.cam.rotateLeft,
+ 'rotate_right': self.cam.rotateRight,
+ 'rotate_up': self.cam.rotateUp,
+ 'rotate_down': self.cam.rotateDown,
+ 'trans_left': self.cam.translateLeft,
+ 'trans_right': self.cam.translateRight,
+ 'trans_up': self.cam.translateUp,
+ 'trans_down': self.cam.translateDown,
+ 'toggle_proj': self.cam.toggleProjection,
+ 'zoom_in': zoom_in,
+ 'zoom_out': zoom_out,
+ 'zoom_ortho_in': zoom_shift_in,
+ 'zoom_ortho_out':zoom_shift_out,
}
+
if menu_item in action_map:
- return action_map[menu_item]()
- else:
+ res = action_map[menu_item]()
+ self.guides.update()
+ return res
+
+ # Handle Guide toggles
+ if menu_item in self.guides.states:
self.guides.states[menu_item] = kwargs[menu_item]
- return
+ self.guides.update()
+ return True
+
+ return False
def onMenuPreOpen(self, kwargs):
menu_id = kwargs['menu']
@@ -310,13 +263,11 @@ def createViewerStateTemplate():
template.bindFactory(State)
template.bindIcon('DESKTOP_application_sierra')
- # Hotkey Definitions
- # Using the official system to define context-sensitive hotkeys
+ # Define Commands and Default Bindings
hotkeys = hou.PluginHotkeyDefinitions()
ctx = 'h.pane.gview.state.obj.keycam'
hotkeys.addContext(ctx, 'HC Keycam', 'Hotkeys for the HC Keycam viewer state')
- # Define Commands
commands = {
'rotate_left': ('Rotate Left', ['h']),
'rotate_right': ('Rotate Right', ['l']),
@@ -332,6 +283,7 @@ def createViewerStateTemplate():
'zoom_out': ('Zoom Out', ['-']),
'zoom_ortho_in': ('Zoom Ortho In', ['Shift+=']),
'zoom_ortho_out':('Zoom Ortho Out',['Shift+-']),
+ 'reset': ('Reset', ['r']),
}
for cmd_id, (label, defaults) in commands.items():
@@ -499,8 +451,10 @@ def createViewerStateTemplate():
""" Context Menu """
menu = hou.ViewerStateMenu('keycam_menu', 'Keycam Menu')
- menu.addActionItem('frame', 'Frame', hotkey='h.pane.gview.state.obj.keycam.frame')
- menu.addActionItem('reset', 'Reset')
+ ctx = 'h.pane.gview.state.obj.keycam'
+
+ menu.addActionItem('frame', 'Frame', hotkey=f'{ctx}.frame')
+ menu.addActionItem('reset', 'Reset', hotkey=f'{ctx}.reset')
set_view_menu = hou.ViewerStateMenu('set_view_menu', 'Set View')
set_view_menu.addActionItem('top', 'Top')
@@ -522,8 +476,27 @@ def createViewerStateTemplate():
menu.addMenu(guide_menu)
menu.addSeparator()
- # Add toggle projection to menu so we can bind the hotkey
- menu.addToggleItem('toggle_proj', 'Toggle Projection', 0, hotkey='h.pane.gview.state.obj.keycam.toggle_proj')
+ menu.addToggleItem('toggle_proj', 'Toggle Projection', 0, hotkey=f'{ctx}.toggle_proj')
+
+ # We add the remaining hotkey actions to a submenu to keep them registered
+ # but out of the main menu if desired.
+ nav_menu = hou.ViewerStateMenu('nav_menu', 'Navigation')
+ nav_menu.addActionItem('rotate_left', 'Rotate Left', hotkey=f'{ctx}.rotate_left')
+ nav_menu.addActionItem('rotate_right', 'Rotate Right', hotkey=f'{ctx}.rotate_right')
+ nav_menu.addActionItem('rotate_up', 'Rotate Up', hotkey=f'{ctx}.rotate_up')
+ nav_menu.addActionItem('rotate_down', 'Rotate Down', hotkey=f'{ctx}.rotate_down')
+ nav_menu.addSeparator()
+ nav_menu.addActionItem('trans_left', 'Translate Left', hotkey=f'{ctx}.trans_left')
+ nav_menu.addActionItem('trans_right', 'Translate Right', hotkey=f'{ctx}.trans_right')
+ nav_menu.addActionItem('trans_up', 'Translate Up', hotkey=f'{ctx}.trans_up')
+ nav_menu.addActionItem('trans_down', 'Translate Down', hotkey=f'{ctx}.trans_down')
+ nav_menu.addSeparator()
+ nav_menu.addActionItem('zoom_in', 'Zoom In', hotkey=f'{ctx}.zoom_in')
+ nav_menu.addActionItem('zoom_out', 'Zoom Out', hotkey=f'{ctx}.zoom_out')
+ nav_menu.addActionItem('zoom_ortho_in', 'Zoom Ortho In', hotkey=f'{ctx}.zoom_ortho_in')
+ nav_menu.addActionItem('zoom_ortho_out','Zoom Ortho Out', hotkey=f'{ctx}.zoom_ortho_out')
+
+ menu.addMenu(nav_menu)
template.bindMenu(menu)