git.lucas.co / hou-control
SideFX Houdini customization package
git clone https://git.lucas.co/hou-control.git

commitf52e11c0ade42093806ea050a7c644e22ee4bcad
parente581639448
authorLucas Galante <[email protected]>
date2026-09-14 08:25
hc: make the HC Panel answer arrows and Escape

SelectionDialog had Up/Down handling in keyPressEvent and QDialog closes
on Escape by default, but neither ever fired. Houdini's hotkey manager
runs off Qt's ShortcutOverride event: a widget receives a KeyPress only
if it accepted the override first, and QLineEdit accepts it for printable
characters -- typing into the filter worked -- but not for the arrows or
Escape, so Houdini claimed those keys before the dialog saw them.

An event filter on the dialog, the list and the line edit now accepts the
override for Up, Down, Escape, Ctrl+N and Ctrl+P, moves the selection on
the first four and closes the panel on Escape.

Co-Authored-By: Claude Fable 5.1 <[email protected]>

 python3.13libs/hc/hcwidgets.py | 51 ++++++++++++++++++++++++++++++++----------
 1 file changed, 39 insertions(+), 12 deletions(-)

diff --git a/python3.13libs/hc/hcwidgets.py b/python3.13libs/hc/hcwidgets.py
index ead78e9..9fc6296 100644
--- a/python3.13libs/hc/hcwidgets.py
+++ b/python3.13libs/hc/hcwidgets.py
@@ -2,7 +2,7 @@ import hou
 from fuzzyfinder import fuzzyfinder
 from importlib import reload
 from PySide6 import QtWidgets
-from PySide6.QtCore import Qt
+from PySide6.QtCore import QEvent, Qt
 
 class HCWidgets:
 
@@ -44,6 +44,8 @@ class HCWidgets:
             self.input_line = QtWidgets.QLineEdit()
             self.input_line.returnPressed.connect(self.execute)
             self.input_line.textEdited.connect(self.list.filter)
+            for widget in (self, self.list, self.input_line):
+                widget.installEventFilter(self)
 
             """ Layout """
             self.layout = QtWidgets.QVBoxLayout()
@@ -67,18 +69,43 @@ class HCWidgets:
         def closeEvent(self, event):
             self.setParent(None)
 
+        # Keys the dialog handles itself, even with focus in the line edit.
+        #
+        # Houdini runs its hotkey manager off Qt's ShortcutOverride event: a
+        # widget that accepts the override for a key gets the KeyPress, and
+        # one that does not sees Houdini claim the key first. QLineEdit
+        # accepts the override for printable characters, which is why typing
+        # into the filter works, but not for the arrows or Escape -- so the
+        # old keyPressEvent below the line edit never saw them and the panel
+        # neither moved its selection nor closed. Claiming the override here
+        # is what makes those keys reach the dialog.
+        def _navigation(self, event):
+            key = event.key()
+            ctrl = event.modifiers() == Qt.ControlModifier
+            if key == Qt.Key_Up or (ctrl and key == Qt.Key_P):
+                return self.list.selectPrev
+            if key == Qt.Key_Down or (ctrl and key == Qt.Key_N):
+                return self.list.selectNext
+            if key == Qt.Key_Escape:
+                return self.close
+            return None
+
+        def eventFilter(self, watched, event):
+            if event.type() == QEvent.ShortcutOverride:
+                if self._navigation(event) is not None:
+                    event.accept()
+                    return True
+            elif event.type() == QEvent.KeyPress:
+                action = self._navigation(event)
+                if action is not None:
+                    action()
+                    return True
+            return super().eventFilter(watched, event)
+
         def keyPressEvent(self, event):
-            if event.key() == Qt.Key_Up:
-                self.list.selectPrev()
-                return
-            elif event.key() == Qt.Key_Down:
-                self.list.selectNext()
-                return
-            elif event.key() == Qt.Key_P and event.modifiers() == Qt.ControlModifier:
-                self.list.selectPrev()
-                return
-            elif event.key() == Qt.Key_N and event.modifiers() == Qt.ControlModifier:
-                self.list.selectNext()
+            action = self._navigation(event)
+            if action is not None:
+                action()
                 return
             super().keyPressEvent(event)