git clone https://git.lucas.co/hou-control.git
hc: repackage the package
python3.11libs/hc/__init__.py | 43 ++++++
python3.11libs/hc/bindings.py | 308 +++++++++++++++++++++++++++++++++++++
python3.11libs/hc/cam.py | 268 ++++++++++++++++++++++++++++++++
python3.11libs/hc/defaultcam.py | 24 +++
python3.11libs/hc/geo.py | 18 +++
python3.11libs/hc/guides.py | 245 +++++++++++++++++++++++++++++
python3.11libs/hc/layouts.py | 88 +++++++++++
python3.11libs/hc/listener.py | 100 ++++++++++++
python3.11libs/hc/menuutils.py | 28 ++++
python3.11libs/hc/networkeditor.py | 143 +++++++++++++++++
python3.11libs/hc/node.py | 97 ++++++++++++
python3.11libs/hc/pane.py | 127 +++++++++++++++
python3.11libs/hc/panel.py | 226 +++++++++++++++++++++++++++
python3.11libs/hc/pathtab.py | 26 ++++
python3.11libs/hc/prefs.py | 3 +
python3.11libs/hc/radial.py | 186 ++++++++++++++++++++++
python3.11libs/hc/sceneviewer.py | 275 +++++++++++++++++++++++++++++++++
python3.11libs/hc/session.py | 295 +++++++++++++++++++++++++++++++++++
python3.11libs/hc/status.py | 69 +++++++++
python3.11libs/hc/tab.py | 73 +++++++++
python3.11libs/hc/viewport.py | 15 ++
python3.11libs/hc/visualizers.py | 133 ++++++++++++++++
python3.11libs/hc/widgets.py | 19 +++
23 files changed, 2809 insertions(+)
diff --git a/python3.11libs/hc/__init__.py b/python3.11libs/hc/__init__.py
new file mode 100755
index 0000000..509cd63
--- /dev/null
+++ b/python3.11libs/hc/__init__.py
@@ -0,0 +1,43 @@
+from .bindings import Bindings
+from .cam import Cam
+from .defaultcam import DefaultCam
+from .geo import Geo
+from .guides import Guides
+from .networkeditor import NetworkEditor
+from .node import Node
+from .panel import Panel
+from .pane import Pane
+from .pathtab import PathTab
+from .prefs import Prefs
+from .radial import Radial
+from .sceneviewer import SceneViewer
+from .session import Session
+from .status import Status
+from .tab import Tab
+from .viewport import Viewport
+from .visualizers import Visualizers
+from .widgets import Widgets
+
+__version__ = "1.0.0"
+
+__all__ = [
+ "Bindings",
+ "Cam",
+ "DefaultCam",
+ "Geo",
+ "Guides",
+ "NetworkEditor",
+ "Node",
+ "Panel",
+ "Pane",
+ "PathTab",
+ "Prefs",
+ "Radial",
+ "Session",
+ "SceneViewer",
+ "Status",
+ "Tab",
+ "Viewport",
+ "Visualizers",
+ "Widgets"
+]
diff --git a/python3.11libs/hc/bindings.py b/python3.11libs/hc/bindings.py
new file mode 100644
index 0000000..03c5512
--- /dev/null
+++ b/python3.11libs/hc/bindings.py
@@ -0,0 +1,308 @@
+import hou, platform
+
+# KEY NAMES
+# uparrow, downarrow, leftarrow, rightarrow
+# alt, cmd, shift, ctrl, tab, del, backspace, enter, esc
+# f1...f12
+# pageup, pagedown, end, home, insert
+# /, \\
+# pad0...pad9
+# padstar, padslash
+# pause
+# space
+
+class Bindings:
+ def __init__(self):
+ return
+
+ def load(self):
+ self.clearAssignments()
+ self.addAssignments()
+
+ def clearAssignments(self):
+ assignments = (
+ #
+ # deskmgr
+ #
+ 'deskmgr.new',
+ 'deskmgr.save',
+ #
+ # h
+ #
+ 'h.add_key',
+ 'h.aliases',
+ 'h.audio_panel',
+ 'h.auto_key',
+ 'h.cache_manager',
+ 'h.channeleditor',
+ 'h.clear_cop_caches',
+ 'h.comp_project_manager',
+ 'h.context_help',
+ 'h.desktop_mgr',
+ 'h.find',
+ 'h.global_animation_options',
+ 'h.mat_palette',
+ 'h.merge',
+ 'h.new',
+ 'h.next_key',
+ 'h.open',
+ 'h.perf_monitor',
+ 'h.python_shell',
+ 'h.textport',
+ #
+ # h.pane
+ #
+ 'h.pane.clone',
+ 'h.pane.closetab',
+ 'h.pane.copytab',
+ 'h.pane.edit_bookmark',
+ 'h.pane.editpathastext',
+ 'h.pane.link_minus',
+ 'h.pane.link_plus',
+ 'h.pane.nexttab',
+ 'h.pane.prevtab',
+ 'h.pane.to_cop_view',
+ 'h.pane.to_chanedit',
+ 'h.pane.to_chop_view',
+ 'h.pane.to_geosheet',
+ 'h.pane.to_ipr_viewer',
+ 'h.pane.to_mat_palette',
+ 'h.pane.to_net',
+ 'h.pane.to_old_net',
+ 'h.pane.to_parm',
+ 'h.pane.to_scene_view',
+ 'h.pane.to_textport',
+ 'h.pane.to_treeview',
+ #
+ # h.pane.bundle
+ #
+ 'h.pane.bundle.addbundle',
+ 'h.pane.bundle.duplicate',
+ #
+ # h.pane.chedit
+ #
+ 'h.pane.chedit.bsave',
+ 'h.pane.chedit.load',
+ 'h.pane.chedit.save',
+ 'h.pane.chedit.scroll_to_current_frame',
+ 'h.pane.chedit.show_groups',
+ 'h.pane.chedit.show_layers',
+ 'h.pane.chedit.show_parmbox',
+ #
+ # h.pane.chlist
+ #
+ 'h.pane.chlist.parmbox.show_filter',
+ #
+ # h.pane.group
+ #
+ 'h.pane.group.addgroup',
+ 'h.pane.group.equals',
+ 'h.pane.group.minus',
+ 'h.pane.group.selectall',
+ 'h.pane.group.selectnone',
+ 'h.pane.group.togpin',
+ 'h.pane.group.togpinall',
+ #
+ # h.pane.gview
+ #
+ 'h.pane.gview.increase_subd',
+ 'h.pane.gview.decrease_subd',
+ 'h.pane.gview.delete',
+ 'h.pane.gview.dockhudhandles',
+ 'h.pane.gview.edit_snap_options',
+ 'h.pane.gview.operator_menu_branch',
+ 'h.pane.gview.state.view.home_selection',
+ 'h.pane.gview.repeat_current',
+ 'h.pane.gview.repeat_current_branch',
+ 'h.pane.gview.restore_prevcam',
+ 'h.pane.gview.selectstylebox',
+ 'h.pane.gview.selectstylebrush',
+ 'h.pane.gview.selectstylelaser',
+ 'h.pane.gview.selectstylelasso',
+ #
+ # h.pane.gview.handle
+ #
+ 'h.pane.gview.handle.remove_keyframe',
+ 'h.pane.gview.handle.set_keyframe',
+ #
+ # h.pane.gview.handle.agentcollisionlayer
+ #
+ 'h.pane.gview.handle.agentcollisionlayer.boxhandle',
+ 'h.pane.gview.handle.agentcollisionlayer.orientationhandle',
+ #
+ # h.pane.gview.handle.agentconfigurejoints
+ #
+ 'h.pane.gview.handle.agentconfigurejoints.conehandle',
+ 'h.pane.gview.handle.agentconfigurejoints.childrotationhandle',
+ #
+ # h.pane.gview.state
+ #
+ 'h.pane.gview.state.volatile_chmodify',
+ #
+ # h.pane.wsheet
+ #
+ '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.color_palette',
+ 'h.pane.wsheet.doautomovenodes',
+ 'h.pane.wsheet.dosnapping',
+ 'h.pane.wsheet.expose',
+ 'h.pane.wsheet.flag1_mode',
+ 'h.pane.wsheet.flag2_mode',
+ 'h.pane.wsheet.flag3_mode',
+ 'h.pane.wsheet.flag4_mode',
+ 'h.pane.wsheet.home',
+ 'h.pane.wsheet.home_selected',
+ 'h.pane.wsheet.up',
+ 'h.pane.wsheet.down',
+ 'h.pane.wsheet.left',
+ 'h.pane.wsheet.right',
+ 'h.pane.wsheet.edit_images',
+ 'h.pane.wsheet.layout',
+ 'h.pane.wsheet.layout_all',
+ 'h.pane.wsheet.layout_mode',
+ '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.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.rename_node',
+ 'h.pane.wsheet.scope_chans',
+ 'h.pane.wsheet.select',
+ 'h.pane.wsheet.select_mode',
+ 'h.pane.wsheet.snap_netbox_bounds',
+ 'h.pane.wsheet.stitch_mode',
+ 'h.pane.wsheet.zoom_in',
+ 'h.pane.wsheet.zoom_out',
+ #
+ # h.playbar
+ #
+ 'h.playbar.next_bookmark',
+ 'h.playbar.prev_bookmark',
+ 'h.playbar.show_range',
+ #
+ # inputfield
+ #
+ 'inputfield.context_help',
+ 'inputfield.editor',
+ )
+
+ for assignment in assignments:
+ context = assignment.rpartition('.')[0]
+ symbol = assignment
+ hou.hotkeys.clearAssignments(context, symbol)
+
+
+ def addAssignments(self):
+ assignments = (
+ #
+ # h
+ #
+ ('h.hc_panel', 'alt+shift+d'),
+ ('h.stow_shelf', 'ctrl+]'),
+ #
+ # h.pane
+ #
+ ('h.pane.shelf', 'ctrl+['),
+ ('h.pane.nexttab', 'ctrl+tab'),
+ ('h.pane.prevtab', 'ctrl+shift+tab'),
+ #
+ # h.pane.gview
+ #
+ ('h.pane.gview.refplane', 'shift+g'),
+ ('h.pane.gview.state.view.home_selection', 'shift+s'),
+ #
+ # h.pane.parms
+ #
+ ('h.pane.parms.edit_expression', 'alt+e'),
+ #
+ # h.pane.editparms
+ #
+ ('h.pane.editparms.selectall', 'alt+h'),
+ #
+ # h.pane.wsheet
+ #
+ ('h.pane.wsheet.bypass_mode', 'q'),
+ # ('h.pane.wsheet.desel_noncurrent', 'ctrl+d'),
+ ('h.pane.wsheet.doautomovenodes', 'ctrl+pad9'),
+ ('h.pane.wsheet.down', 'shift+j'),
+ ('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.left', 'shift+h'),
+ ('h.pane.wsheet.node_quick_nav', 'ctrl+f'),
+ ('h.pane.wsheet.right', 'shift+l'),
+ ('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.wsheet.up', 'shift+k'),
+ #
+ # h.pane.textport
+ #
+ ('h.pane.textport.up', 'ctrl+p'),
+ ('h.pane.textport.down', 'ctrl+n'),
+ ('h.pane.textport.next', 'ctrl+f'),
+ ('h.pane.textport.prev', 'ctrl+b'),
+ ('h.pane.textport.top', 'ctrl+a'),
+ ('h.pane.textport.bottom', 'ctrl+e'),
+ ('h.pane.textport.nextword', 'alt+f'),
+ ('h.pane.textport.pageup', 'alt+v'),
+ ('h.pane.textport.pagedown', 'ctrl+v'),
+ ('h.pane.textport.firstline', 'ctrl+shift+,'),
+ ('h.pane.textport.lastline', 'ctrl+shift+.'),
+ ('h.pane.textport.jump_to_line', 'alt+g'),
+ ('h.pane.textport.selectup', 'ctrl+shift+p'),
+ ('h.pane.textport.selectdown', 'ctrl+shift+n'),
+ ('h.pane.textport.selectnext', 'ctrl+shift+f'),
+ ('h.pane.textport.selectprev', 'ctrl+shift+b'),
+ ('h.pane.textport.selectbottom', 'ctrl+shift+e'),
+ ('h.pane.textport.selecttop', 'ctrl+shift+a'),
+ ('h.pane.textport.selectnextword', 'alt+shift+f'),
+ ('h.pane.textport.toggle_comment', 'ctrl+/'),
+ #
+ # inputfield
+ #
+ ('inputfield.up', 'ctrl+p'),
+ ('inputfield.down', 'ctrl+n'),
+ ('inputfield.next', 'ctrl+f'),
+ ('inputfield.prev', 'ctrl+b'),
+ ('inputfield.next_word', 'alt+f'),
+ ('inputfield.prev_word', 'alt+b'),
+ ('inputfield.end', 'ctrl+e'),
+ ('inputfield.home', 'ctrl+a'),
+ )
+
+ for assignment in assignments:
+ context = assignment[0].rpartition('.')[0]
+ symbol = assignment[0]
+ key = assignment[1]
+ hou.hotkeys.addAssignment(context, symbol, key)
+
+
+ def bindCommands(self):
+
+ list = ()
+ for command in list:
+ context = binding[0]
+ command = binding[1]
+ hou.hotkeys.addCommandBinding(context, command)
diff --git a/python3.11libs/hc/cam.py b/python3.11libs/hc/cam.py
new file mode 100644
index 0000000..aa0ea99
--- /dev/null
+++ b/python3.11libs/hc/cam.py
@@ -0,0 +1,268 @@
+import hou
+from .geo import Geo
+from .sceneviewer import SceneViewer
+
+"""
+Accepts 1 camera node object and 1 scene viewer object as arguments.
+"""
+
+
+class Cam:
+ def __init__(self, node, hou_scene_viewer):
+ self.node = node
+ self.hou_scene_viewer = hou_scene_viewer
+ self.scene_viewer = SceneViewer(hou_scene_viewer)
+
+ self.node = node
+ self._t = hou.Vector3(0, 0, 0)
+ self._r = hou.Vector3(0, 0, 0)
+ self._p = hou.Vector3(0, 0, 0)
+ self._ow = 10
+ self._projection = 'perspective'
+ self._resx = 1000
+ self._resy = 1000
+ self._aspect_ratio = 1
+ self.local_x = hou.Vector3(1, 0, 0)
+ self.local_y = hou.Vector3(0, 1, 0)
+ self.local_z = hou.Vector3(0, 0, 1)
+ self.global_x = hou.Vector3(1, 0, 0)
+ self.global_y = hou.Vector3(0, 1, 0)
+ self.global_z = hou.Vector3(0, 0, 1)
+ # self.zoom = 10
+ self.delta_t = 1
+ self.delta_r = 15
+ self.delta_zoom = 1
+ self.target = None
+
+ self.fitAspectRatio()
+ self.lock()
+ self.reset()
+
+ """ Movement """
+
+ def center(self):
+ centroid = self.scene_viewer.geo().centroid()
+ self.t = hou.Vector3(centroid)
+ self.p = hou.Vector3(centroid)
+
+ def frame(self):
+ centroid = self.geo().centroid()
+ self.t = hou.Vector3(centroid)
+ self.p = hou.Vector3(centroid)
+ # self.ow = 10
+ self.setZoom(10)
+
+ def home(self):
+ centroid = self.geo().centroid()
+ self.t = centroid
+ self.p = centroid
+ # self.ow = 10
+ # self.setZoom(6)
+
+ # def movePivot(self):
+ # If origin
+ # if self.target == 0:
+ # self.t = [0, 0, self.zoom]
+ # self.r = [45, 45, 0]
+ # self.p = [0, 0, self.zoom * -1]
+ # self.ow = 10
+
+ def rotateUp(self):
+ delta = hou.Vector3(self.delta_r, 0, 0)
+ self.r += delta
+ m = hou.hmath.buildRotateAboutAxis(self.local_x, self.delta_r)
+ self.t -= self.p
+ self.t *= m
+ self.t += self.p
+ self.local_x *= m
+ self.local_y *= m
+ self.local_z *= m
+
+ def rotateDown(self):
+ delta = hou.Vector3(-self.delta_r, 0, 0)
+ self.r += delta
+ m = hou.hmath.buildRotateAboutAxis(self.local_x, -self.delta_r)
+ self.t -= self.p
+ self.t *= m
+ self.t += self.p
+ self.local_x *= m
+ self.local_y *= m
+ self.local_z *= m
+
+ def rotateLeft(self):
+ delta = hou.Vector3(0, -self.delta_r, 0)
+ self.r += delta
+ m = hou.hmath.buildRotateAboutAxis(self.global_y, -self.delta_r)
+ self.t -= self.p
+ self.t *= m
+ self.t += self.p
+ self.local_x *= m
+ self.local_y *= m
+ self.local_z *= m
+
+ def rotateRight(self):
+ delta = hou.Vector3(0, self.delta_r, 0)
+ self.r += delta
+ m = hou.hmath.buildRotateAboutAxis(self.global_y, self.delta_r)
+ self.t -= self.p
+ self.t *= m
+ self.t += self.p
+ self.local_x *= m
+ self.local_y *= m
+ self.local_z *= m
+
+ def setZoom(self, amt):
+ move = self.local_z * amt
+ self.t += move
+
+ def translateUp(self):
+ move = self.local_y * self.delta_t
+ self.t += move
+ self.p += move
+
+ def translateDown(self):
+ move = self.local_y * self.delta_t * -1
+ self.t += move
+ self.p += move
+
+ def translateLeft(self):
+ move = self.local_x * self.delta_t * -1
+ self.t += move
+ self.p += move
+
+ def translateRight(self):
+ move = self.local_x * self.delta_t
+ self.t += move
+ self.p += move
+
+ def zoom(self, dir):
+ dir_map = {'out': 1, 'in': -1}
+ move = self.local_z * self.delta_zoom * dir_map[dir]
+ self.t += move
+
+ def zoomOrtho(self, dir):
+ dir_map = {'out': 1, 'in': -1}
+ self.ow += self.delta_zoom * dir_map[dir]
+
+
+ """ Props """
+
+ @property
+ def t(self):
+ return self._t
+ @t.setter
+ def t(self, val):
+ self._t = val
+ self.node.parmTuple('t').set(val)
+
+ @property
+ def r(self):
+ return self._r
+ @r.setter
+ def r(self, val):
+ self._r = val
+ self.node.parmTuple('r').set(val)
+
+ @property
+ def p(self):
+ return self._p
+ @p.setter
+ def p(self, val):
+ self._p = val
+
+ @property
+ def ow(self):
+ return self._ow
+ @ow.setter
+ def ow(self, val):
+ self._ow = val
+ self.node.parm('orthowidth').set(val)
+
+ @property
+ def projection(self):
+ return self._projection
+ @projection.setter
+ def projection(self, val):
+ self._projection = val
+ self.node.parm('projection').set(val)
+
+ @property
+ def resx(self):
+ return self._resx
+ @resx.setter
+ def resx(self, val):
+ self._resx = val
+ self.node.parm('resx').set(val)
+
+ @property
+ def resy(self):
+ return self._resy
+ @resy.setter
+ def resy(self, val):
+ self._resy = val
+ self.node.parm('resy').set(val)
+
+ @property
+ def aspect_ratio(self):
+ return self._aspect_ratio
+ @aspect_ratio.setter
+ def aspect_ratio(self, val):
+ self._aspect_ratio = val
+ self.node.parm('aspect').set(val)
+
+ """ Util """
+
+ def fitAspectRatio(self):
+ self.resx = 1000
+ self.resy = 1000
+ ratio = self.viewport().size()[2] / self.viewport().size()[3]
+ self.aspect_ratio = ratio
+
+ def geo(self):
+ return self.scene_viewer.geo()
+
+ def lock(self):
+ viewport = self.viewport()
+ viewport.setCamera(self.node)
+ viewport.lockCameraToView(1)
+
+ def reset(self):
+ self.t = hou.Vector3(0, 0, 2)
+ self.r = hou.Vector3(0, 0, 0)
+ self.p = hou.Vector3(0, 0, 0)
+ # self.zoom = 10
+ self.ow = 10
+ self.delta_t = 1
+ self.delta_r = 15
+ self.delta_zoom = 1
+ self.local_x = hou.Vector3(1, 0, 0)
+ self.local_y = hou.Vector3(0, 1, 0)
+ self.local_z = hou.Vector3(0, 0, 1)
+ self.global_x = hou.Vector3(1, 0, 0)
+ self.global_y = hou.Vector3(0, 1, 0)
+ self.global_z = hou.Vector3(0, 0, 1)
+
+ def setView(self):
+ view_map = {
+ 'top': hou.Vector3(270, 0, 0),
+ 'bottom': hou.Vector3(90, 0, 0),
+ 'front': hou.Vector3(0, 180, 0),
+ 'back': hou.Vector3(0, 0, 0),
+ 'right': hou.Vector3(0, 90, 0),
+ 'left': hou.Vector3(0, 270, 0)
+ }
+ self.r = view_map[self.view]
+
+ def toggleProjection(self):
+ projection_map = {
+ 'perspective': 'ortho',
+ 'ortho': 'perspective'
+ }
+ self.projection = projection_map[self.projection]
+
+ def unlock(self):
+ self.viewport().lockCameraToView(0)
+
+ def viewport(self):
+ return self.scene_viewer.allViewports()[3]
+
diff --git a/python3.11libs/hc/defaultcam.py b/python3.11libs/hc/defaultcam.py
new file mode 100644
index 0000000..a5e7074
--- /dev/null
+++ b/python3.11libs/hc/defaultcam.py
@@ -0,0 +1,24 @@
+import hou
+
+class DefaultCam:
+ def __init__(self, hou_defaultcam):
+ self.hou_defaultcam = hou_defaultcam
+
+ def frame(self):
+ for viewport in self.viewports():
+ hou_defaultcam = viewport.camera()
+ # Is cam default or node.
+ if not hou_defaultcam:
+ viewport.frameAll()
+
+ def nextView(self):
+ return
+
+ def rotate(self, key):
+ return
+
+ def translate(self, key):
+ return
+
+ def zoom(self):
+ return
diff --git a/python3.11libs/hc/geo.py b/python3.11libs/hc/geo.py
new file mode 100644
index 0000000..695f61d
--- /dev/null
+++ b/python3.11libs/hc/geo.py
@@ -0,0 +1,18 @@
+import hou
+
+class Geo:
+ def __init__(self, hou_geo):
+ self.hou_geo = hou_geo
+
+ def bbox(self):
+ return self.hou_geo.boundingBox()
+
+ def centroid(self):
+ geo_out = hou.Geometry()
+ extract_centroid = hou.sopNodeTypeCategory().nodeVerb("extractcentroid")
+ extract_centroid.setParms(
+ {'partitiontype': 2}
+ )
+ extract_centroid.execute(geo_out, [self.hou_geo])
+ pt = geo_out.point(0)
+ return pt.position()
diff --git a/python3.11libs/hc/guides.py b/python3.11libs/hc/guides.py
new file mode 100644
index 0000000..b266650
--- /dev/null
+++ b/python3.11libs/hc/guides.py
@@ -0,0 +1,245 @@
+import hou
+
+class Guides:
+ def __init__(self, state):
+ self.state = state
+ self.cam_node = state.cam_node
+ self.cam = state.cam
+ self.hou_scene_viewer = state.hou_scene_viewer
+ self.scene_viewer = state.scene_viewer
+
+ self.options = {
+ 'axis_size': 1,
+ 'tie_axis_to_radius': 0
+ }
+
+ self.states = {
+ 'cam_axis': 0,
+ 'pivot_axis': 1,
+ 'bbox': 0,
+ 'perim': 0,
+ 'pivot_2d': 0,
+ 'pivot_3d': 0,
+ 'ray': 0
+ }
+
+ self.cam_axis = hou.GeometryDrawable(
+ scene_viewer=self.hou_scene_viewer,
+ geo_type=hou.drawableGeometryType.Line,
+ name='cam_axis'
+ )
+
+ self.pivot_axis = hou.GeometryDrawable(
+ scene_viewer=self.hou_scene_viewer,
+ geo_type=hou.drawableGeometryType.Line,
+ name='pivot_axis',
+ params={
+ 'color1': hou.Vector4((1, 1, 1, 0.5))
+ }
+ )
+
+ self.bbox = hou.GeometryDrawable(
+ scene_viewer=self.hou_scene_viewer,
+ geo_type=hou.drawableGeometryType.Line,
+ name='bbox',
+ params={
+ 'color1': hou.Vector4((1, 1, 1, 0.3)),
+ 'fade_factor': 0.0
+ }
+ )
+
+ self.perim = hou.GeometryDrawable(
+ scene_viewer=self.hou_scene_viewer,
+ geo_type=hou.drawableGeometryType.Line,
+ name='perim'
+ )
+
+ self.pivot_2d = hou.GeometryDrawable(
+ scene_viewer=self.hou_scene_viewer,
+ geo_type=hou.drawableGeometryType.Line,
+ name='pivot_2d'
+ )
+
+ self.pivot_3d = hou.GeometryDrawable(
+ scene_viewer=self.hou_scene_viewer,
+ geo_type=hou.drawableGeometryType.Face,
+ name='pivot_3d',
+ params={
+ 'color1': hou.Vector4(0.2, 0.8, 0.2, 0.6),
+ 'fade_factor': 0.2
+ }
+ )
+
+ self.ray = hou.GeometryDrawable(
+ scene_viewer=self.hou_scene_viewer,
+ geo_type=hou.drawableGeometryType.Line,
+ name='ray',
+ params={
+ 'color1': hou.Vector4((1, 0.8, 1, 0.5))
+ }
+ )
+
+ def draw(self, kwargs):
+ func_map = {
+ 'bbox': self.bbox.draw,
+ 'cam_axis': self.cam_axis.draw,
+ 'pivot_axis': self.pivot_axis.draw,
+ 'perim': self.perim.draw,
+ 'pivot_2d': self.pivot_2d.draw,
+ 'pivot_3d': self.pivot_3d.draw,
+ 'ray': self.ray.draw
+ }
+ for name, value in self.states.items():
+ if value:
+ func_map[name](kwargs['draw_handle'], {})
+
+ def update(self):
+ func_map = {
+ 'bbox': self.makeBbox,
+ 'cam_axis': self.makeCamAxis,
+ 'pivot_axis': self.makePivotAxis,
+ 'perim': self.makePerim,
+ 'pivot_2d': self.makePivot2d,
+ 'pivot_3d': self.makePivot3d,
+ 'ray': self.makeRay
+ }
+ for name, value in self.states.items():
+ if value:
+ func_map[name]()
+ # self.cam_axis.show(self.states['cam_axis'])
+ self.pivot_axis.show(self.states['pivot_axis'])
+ self.bbox.show(self.states['bbox'])
+ # self.cam.show(self.states['cam'])
+ # self.perim.show(self.states['perim'])
+ # self.pivot_2d.show(self.states['pivot_2d'])
+ self.pivot_3d.show(self.states['pivot_3d'])
+ # self.ray.show(self.states['ray'])
+
+
+ """ Construction """
+
+ def makeCamAxis(self):
+ axes = (
+ self.cam.local_x,
+ self.cam.local_y,
+ self.cam.local_z
+ )
+ geo = hou.Geometry()
+ for i in range(3):
+ P0 = self.cam.t + axes[i]
+ P1 = self.cam.t + axes[i] * -1
+ pts = geo.createPoints((P0, P1))
+ poly = geo.createPolygon(is_closed=False)
+ poly.addVertex(pts[0])
+ poly.addVertex(pts[1])
+ self.cam_axis.setGeometry(geo)
+
+ def makePivotAxis(self):
+ print('test')
+ axes = (
+ self.cam.local_x,
+ self.cam.local_y,
+ self.cam.local_z
+ # hou.Vector3(1, 0, 0),
+ # hou.Vector3(0, 1, 0),
+ # hou.Vector3(0, 0, 1)
+ )
+ colors = (
+ [1.0, 0.7, 0.7],
+ [0.7, 1.0, 0.7],
+ [0.7, 0.7, 1.0]
+ )
+ geo = hou.Geometry()
+ geo.addAttrib(hou.attribType.Point, 'Cd', (0.1, 0.1, 0.1))
+ for i in range(3):
+ P0 = self.cam.p + axes[i]
+ P1 = self.cam.p + axes[i] * -1
+ pts = geo.createPoints((P0, P1))
+ pts[0].setAttribValue('Cd', colors[i])
+ pts[1].setAttribValue('Cd', colors[i])
+ poly = geo.createPolygon(is_closed=False)
+ poly.addVertex(pts[0])
+ poly.addVertex(pts[1])
+ self.pivot_axis.setGeometry(geo)
+ self.pivot_axis.setParams(
+ {'fade_factor': 0.0}
+ )
+
+ def makeBbox(self):
+ guide_geo = hou.Geometry()
+ target_geo = self.scene_viewer.geo()
+ bbox = target_geo.bbox()
+ box = hou.sopNodeTypeCategory().nodeVerb('box')
+ box.setParms(
+ {
+ 'size': bbox.sizevec(),
+ 't': bbox.center()
+ }
+ )
+ box.execute(guide_geo, [])
+ self.bbox.setGeometry(guide_geo)
+
+ def makePerim(self):
+ guide_geo = hou.Geometry()
+ circle = hou.sopNodeTypeCategory().nodeVerb('circle')
+ circle.setParms(
+ {
+ 'divs': 128,
+ 'type': 1,
+ 't': self.cam.p,
+ 'scale': self.cam.p.distanceTo(self.cam.t),
+ 'orient': 2
+ }
+ )
+ circle.execute(guide_geo, [])
+ self.perim.setParams(
+ {
+ 'color1': hou.Vector4(1.0, 1.0, 1.0, 0.25),
+ 'fade_factor': 1.0
+ }
+ )
+ self.perim.setGeometry(guide_geo)
+
+ def makePivot2d(self):
+ guide_geo = hou.Geometry()
+ circle = hou.sopNodeTypeCategory().nodeVerb('circle')
+ circle.setParms(
+ {
+ 'type': 1,
+ 'r': self.cam.r,
+ 't': self.cam.p,
+ 'scale': self.cam.ow * 0.0075
+ }
+ )
+ circle.execute(guide_geo, [])
+ self.pivot_2d.setParams(
+ {
+ 'color1': hou.Vector4(0.0, 0.0, 1, 1),
+ 'fade_factor': 1.0
+ }
+ )
+ self.pivot_2d.setGeometry(guide_geo)
+
+ def makePivot3d(self):
+ guide_geo = hou.Geometry()
+ sphere = hou.sopNodeTypeCategory().nodeVerb('sphere')
+ scale = self.cam.t.distanceTo(self.cam.p) * 0.01
+ sphere.setParms(
+ {
+ 'freq': 7,
+ 'scale': scale,
+ 'type': 1,
+ 't': self.cam.p
+ }
+ )
+ sphere.execute(guide_geo, [])
+ self.pivot_3d.setGeometry(guide_geo)
+
+ def makeRay(self):
+ guide_geo = hou.Geometry()
+ guide_geo.addAttrib(hou.attribType.Point, 'Cd', (1, 0, 0))
+ pts = guide_geo.createPoints((self.cam.p, self.cam.t))
+ poly = guide_geo.createPolygon()
+ poly.addVertex(pts[0])
+ poly.addVertex(pts[1])
+ self.ray.setGeometry(guide_geo)
diff --git a/python3.11libs/hc/layouts.py b/python3.11libs/hc/layouts.py
new file mode 100644
index 0000000..d5edef8
--- /dev/null
+++ b/python3.11libs/hc/layouts.py
@@ -0,0 +1,88 @@
+from .session import Session
+
+def setLayoutQuad(self):
+ session = Session()
+ session.clearLayout()
+ session.allTabs()[0].setType("PythonShell")
+ session.allPanes()[0].splitHorizontal()
+ session.allPanes()[0].splitVertical()
+ session.allPanes()[1].splitVertical()
+
+def setLayoutRamp(self):
+ session = Session()
+ session.removeEventLoopCallbacks()
+ session.clearLayout()
+ session.allPanes()[0].splitVertical()
+ session.allTabs()[1].setType("Parm")
+ session.allPanes()[1].setSplitRatio(0.3)
+ session.allPanes()[1].createTab()
+
+def setLayoutTriH(self):
+ session = Session()
+ session.removeEventLoopCallbacks()
+ session.clearLayout()
+ # Make panes
+ session.allTabs()[0].setType("PythonShell")
+ session.allPanes()[0].splitHorizontal()
+ session.allPanes()[1].splitHorizontal()
+ # Make tabs
+ session.allPanes()[1].createTab("PythonShell")
+ session.allPanes()[1].allTabs()[0].setIsCurrentTab()
+ # Set types
+ session.allPanes()[0].allTabs()[0].setType("SceneViewer")
+ session.allPanes()[1].allTabs()[0].setType("Parm")
+ session.allPanes()[1].allTabs()[1].setType("DetailsView")
+ session.allPanes()[2].allTabs()[0].setType("NetworkEditor")
+ # Ratios
+ session.allPanes()[0].setSplitFraction(0.5)
+ hou.session.last_pane = self.pane()
+ hou.ui.addEventLoopCallback(triHCallback)
+
+def triHCallback(session):
+ panes = session.panes()
+ pane = session.pane()
+ if str(pane) != str(hou.session.lastPane):
+ hou.session.last_pane = pane
+ if str(pane) == str(panes[1]):
+ pane.setSplitFraction(0.6)
+ elif str(pane) == str(panes[2]):
+ pane.setSplitFraction(0.3)
+ return True
+
+def setLayoutTriV(session):
+ # Remove callbacks
+ session.removeEventLoopCallbacks()
+ # Reset layout
+ session.clearLayout()
+ # Make panes
+ session.allPanes()[0].allTabs()[0].setType("PythonShell")
+ session.allPanes()[0].splitHorizontal()
+ session.allPanes()[1].splitVertical()
+ # Make pane tabs
+ session.allPanes()[1].createTab("PythonShell")
+ session.allPanes()[1].allTabs()[0].setIsCurrentTab()
+ # Set types
+ session.allPanes()[0].allTabs()[0].setType("SceneViewer")
+ session.allPanes()[1].allTabs()[0].setType("Parm")
+ session.allPanes()[1].allTabs()[1].setType("DetailsView")
+ session.allPanes()[2].allTabs()[0].setType("NetworkEditor")
+ # Set ratios
+ session.allPanes()[0].setSplitFraction(0.66)
+ # Ok
+ hou.session.last_pane = self.pane()
+ hou.ui.addEventLoopCallback(self.triVCallback)
+
+def triVCallback(session):
+ panes = session.allPanes()
+ pane = session.currentPane()
+ if str(pane) != str(hou.session.last_pane):
+ hou.session.last_pane = pane
+ if str(pane) == str(panes[0]):
+ pane.setSplitFraction(0.7)
+ elif str(pane) == str(panes[1]):
+ panes[0].setSplitFraction(0.4)
+ pane.setSplitFraction(0.33)
+ elif str(pane) == str(panes[2]):
+ panes[0].setSplitFraction(0.4)
+ pane.setSplitFraction(0.66)
+ return True
diff --git a/python3.11libs/hc/listener.py b/python3.11libs/hc/listener.py
new file mode 100644
index 0000000..c8d4c5a
--- /dev/null
+++ b/python3.11libs/hc/listener.py
@@ -0,0 +1,100 @@
+import hou
+from .session import Session
+from .pane import Pane
+from .tab import Tab
+
+
+class Listener():
+ def __init__(self):
+ self.report_tab = 1
+ self.tab = None
+ self.pane = None
+ self.session = None
+ self.pane = None
+ self.tab = None
+ self.sceneviewer = None
+ self.networkeditor = None
+ self.project_path = hou.hipFile.path()
+ self.network_path = None
+ self.tab_type = None
+ self.update_objects()
+ return
+
+ def start(self):
+ hou.ui.addEventLoopCallback(self.listener)
+
+ def stop(self):
+ hou.ui.removeEventLoopCallback(self.listener)
+
+ def update_objects(self):
+ self.tab = hou.ui.paneTabUnderCursor()
+ self.pane = hou.ui.paneUnderCursor()
+
+ self.session = Session()
+ self.pane = Pane(hou.ui.paneUnderCursor())
+ self.tab = Tab(self.tab)
+
+ if self.tab != None:
+
+ if self.tab.hasNetworkControls():
+ self.network_path = self.tab.path()
+ self.tab_type = self.tab.type()
+
+ if self.tab.type() == "SceneViewer:"
+ self.sceneviewer = SceneViewer(self.tab)
+ else:
+ self.sceneviewer = None
+
+ if self.tab.type() == "NetworkEditor":
+ self.networkeditor = NetworkEditor(self.tab)
+ else:
+ self.networkeditor = None
+
+ def listener(self):
+ tab = hou.ui.paneTabUnderCursor()
+ if tab == None:
+ hou.session.tab = None
+ elif tab != hou.session.tab:
+ print(tab)
+ self.update_objects()
+
+ def lists(self):
+ # Arrays for navigating pane tabs
+ self.tab_types = (
+ hou.paneTabType.ApexEditor,
+ hou.paneTabType.CompositorViewer,
+ hou.paneTabType.DetailsView,
+ hou.paneTabType.NetworkEditor,
+ hou.paneTabType.Parm,
+ hou.paneTabType.PythonPanel,
+ hou.paneTabType.PythonShell,
+ hou.paneTabType.SceneViewer,
+ hou.paneTabType.Textport
+ )
+ self.tab_type_names = (
+ "ApexEditor",
+ "CompositorViewer",
+ "DetailsView",
+ "NetworkEditor",
+ "Parm",
+ "PythonPanel",
+ "PythonShell",
+ "SceneViewer",
+ "Textport"
+ )
+ self.tab_names = [tab.name() for tab in hou.session.session.tabs()]
+
+ # Populate pane tab labels array
+ self.tab_labels = []
+ for tab in self.session.tabs():
+ index = self.tab_types.index(tab.type())
+ label = self.tab_type_names[index]
+ self.tab_labels.append(label)
+
+ def networkPath(self):
+ return str(hou.session.tab.pwd())
+
+ def projectPath(self):
+ return hou.hipFile.name()
+ # ct = self.project_path.count("/")
+ # self.project_path = self.project_path.split("/", ct - 2)[-1]
diff --git a/python3.11libs/hc/menuutils.py b/python3.11libs/hc/menuutils.py
new file mode 100644
index 0000000..a8f7701
--- /dev/null
+++ b/python3.11libs/hc/menuutils.py
@@ -0,0 +1,28 @@
+import hou
+
+
+def vector3Test(kwargs):
+ parms = kwargs['parms']
+ print(parms[0])
+ if len(parms) == 3 \
+ and parms[0].parmTemplate().type() == hou.parmTemplateType.Float \
+ and parms[1].parmTemplate().type() == hou.parmTemplateType.Float \
+ and parms[2].parmTemplate().type() == hou.parmTemplateType.Float:
+ return True
+ else:
+ return False
+
+def setVector3(kwargs, x, y, z):
+ parms = kwargs['parms']
+ name0 = parms[0].name()
+ name1 = parms[1].name()
+ name2 = parms[2].name()
+ node = parms[0].node()
+ node.setParmExpressions({name0: x, name1: y, name2: z})
+
+def vector3XMaster(kwargs):
+ name0 = parms[0].name()
+ name1 = parms[1].name()
+ name2 = parms[2].name()
+ node = parms[0].node()
+ node.setParmExpressions({name1: 'ch("'+name0+'")', name2: 'ch("'+name0+'")'})
diff --git a/python3.11libs/hc/networkeditor.py b/python3.11libs/hc/networkeditor.py
new file mode 100644
index 0000000..daf5c36
--- /dev/null
+++ b/python3.11libs/hc/networkeditor.py
@@ -0,0 +1,143 @@
+import hou, math, types
+from .pathtab import PathTab
+from .prefs import Prefs
+
+
+class NetworkEditor(PathTab):
+ def __init__(self, hou_tab):
+ self.hou_tab = hou_tab
+ self.delta_t = 2
+
+ """ Move network objects """
+
+ def arrangeNodes(self):
+ return
+
+ def snapToGrid(self, node):
+ P = node.position()
+ P[0] = round(P[0] - 0.5) + 0.5
+ P[1] = round(P[1] - 0.85) + 0.85
+ node.setPosition(P)
+
+ def translateNodes(self, direction):
+ for node in self.pwd().selectedChildren():
+ self.snapToGrid(node)
+ P = node.position()
+ # do the move
+ if direction == 'up':
+ P[1] += 1
+ elif direction == 'down':
+ P[1] -= 1
+ elif direction == 'left':
+ P[0] -= 1
+ elif direction == 'right':
+ P[0] += 1
+ node.setPosition(P)
+
+ """ Viewport """
+
+ def bounds(self):
+ return self.hou_tab.visibleBounds()
+
+ def cursorPosition(self):
+ return self.hou_tab.cursorPosition()
+
+ def frameAll(self):
+ self.hou_tab.requestZoomReset()
+
+ def screenSize(self):
+ return self.hou_tab.screenBounds().size()
+
+ def size(self):
+ return self.bounds().size()
+
+ def setBounds(self, bounds):
+ self.hou_tab.setVisibleBounds(bounds)
+
+ def translateView(self, direction):
+ xform_map = {
+ 'up': hou.Vector2(0, self.delta_t * self.zoomLevel()),
+ 'down': hou.Vector2(0, self.delta_t * self.zoomLevel() * -1),
+ 'left': hou.Vector2(self.delta_t * self.zoomLevel() * -1, 0),
+ 'right': hou.Vector2(self.delta_t * self.zoomLevel(), 0)
+ }
+ bounds = self.bounds()
+ bounds.translate(xform_map[direction])
+ self.setBounds(bounds)
+
+ def zoom(self, direction):
+ scalemap = {
+ 'in': (0.75, 0.75),
+ 'out': (1.25, 1.25)
+ }
+ bounds = self.bounds()
+ bounds.scale(scalemap[direction])
+ self.setBounds(bounds)
+
+ def zoomLevel(self):
+ zoomlevel = self.size()[0] / self.size()[0]
+ return zoomlevel
+
+ """ Interface states & utils """
+
+ def isMenuOpen(self):
+ value = self.hou_tab.getPref('showmenu')
+ return int(value)
+
+ def setMenuOpen(self, value):
+ self.hou_tab.setPref('showmenu', str(value))
+
+ def showPathMessage(self):
+ self.hou_tab.flashMessage(image=None, message=self.path(), duration=1)
+
+ # def showRadialMenu(self):
+ # from .radialutils import editorRadialMain
+ # menu = editorRadialMain()
+ # self.hou_tab.displayRadialMenu("hc_editor_radial_menu")
+
+ def toggleDimUnusedNodes(self):
+ map = {
+ '0': '1',
+ '1': '0'
+ }
+ mode = self.hou_tab.getPref('dimunusednodes')
+ self.hou_tab.setPref('dimunusednodes', map[mode])
+
+ def toggleGridMode(self):
+ map = {
+ '0': '1',
+ '1': '2',
+ '2': '0'
+ }
+ mode = self.hou_tab.getPref('gridmode')
+ self.hou_tab.setPref('gridmode', map[mode])
+
+ def toggleMenu(self):
+ map = {
+ '0': '1',
+ '1': '0'
+ }
+ mode = self.hou_tab.getPref('showmenu')
+ self.hou_tab.setPref('showmenu', map[mode])
+
+ """ Utility """
+
+ def type(self):
+ return "NetworkEditor"
+
+ def renameNode(self):
+ node = self.currentNode()
+ name = hou.ui.readInput("Rename_node", buttons=("Yes", "No"))
+ if name[0] == 0:
+ node.setName(name[1])
+
+ def setNodeColors(self):
+ color = hou.ui.selectColor(hou.Color(Prefs().node_color))
+ nodes = self.pwd().selectedChildren()
+ for node in nodes:
+ node.setColor(color)
+
+ def setNodeShapes(self):
+ nodes = self.pwd().children()
+ for node in nodes:
+ node.setUserData("nodeshape", "rect")
diff --git a/python3.11libs/hc/node.py b/python3.11libs/hc/node.py
new file mode 100644
index 0000000..b09bc28
--- /dev/null
+++ b/python3.11libs/hc/node.py
@@ -0,0 +1,97 @@
+from .geo import Geo
+import hou
+
+class Node:
+ def __init__(self, hou_node):
+ self.hou_node = hou_node
+
+
+ """ Selection """
+
+ def currentNode(self):
+ return self.__class__(self.hou_node.currentNode())
+
+ def deselectAll(self):
+ for child in self.selectedChildren():
+ child.setSelected(False)
+
+ def selectAllChildren(self):
+ for child in self.children():
+ child.setSelected(True)
+
+ def selectedChildren(self):
+ hou_nodes = self.hou_node.selectedChildren()
+ return [self.__class__(hou_node) for hou_node in hou_nodes]
+
+ def setSelected(self, state):
+ self.hou_node.setSelected(state)
+
+
+ """ Visibility """
+
+ def displayNode(self):
+ display_node = self.hou_node.displayNode()
+ if display_node:
+ return self.__class__(display_node)
+ else:
+ return None
+
+ def visibleNodes(self):
+ stack = self.children()
+ visible_nodes = []
+
+ while stack:
+ node = stack.pop()
+ if node.hou_node.isSubNetwork():
+ child_cat = node.childCat()
+ if child_cat == 'Object' and node.hou_node.isDisplayFlagSet():
+ stack.extend(node.children())
+ elif child_cat == 'Sop' and node.hou_node.isDisplayFlagSet():
+ display_node = node.displayNode()
+ if display_node:
+ visible_nodes.append(display_node)
+
+ return visible_nodes
+
+
+ """ Geometry """
+
+ def geometry(self):
+ return self.hou_node.geometry()
+
+ def hou_geo(self):
+ geo = hou.Geometry()
+ for node in self.visibleNodes():
+ geo.merge(node.geometry())
+ return geo
+
+
+ """ Children """
+
+ def childCat(self):
+ """ Possibilities: Object, Sop, Lop """
+ return self.hou_node.childTypeCategory().name()
+
+ def children(self):
+ hou_nodes = self.hou_node.children()
+ return [self.__class__(hou_node) for hou_node in hou_nodes]
+
+ def netPos(self):
+ """ net editor xy position """
+ return self.hou_node.position()
+
+
+ """ Network """
+
+ def position(self):
+ return self.hou_node.position()
+
+ def setColor(self, color):
+ self.hou_node.setColor(color)
+
+ def setPosition(self, pos):
+ self.hou_node.setPosition(pos)
+
+ def setUserData(self, a, b):
+ self.hou_node.setUserData(a, b)
+
diff --git a/python3.11libs/hc/pane.py b/python3.11libs/hc/pane.py
new file mode 100644
index 0000000..f92166f
--- /dev/null
+++ b/python3.11libs/hc/pane.py
@@ -0,0 +1,127 @@
+import hou
+from .tab import Tab
+from .pathtab import PathTab
+from .sceneviewer import SceneViewer
+from .networkeditor import NetworkEditor
+
+
+class Pane:
+ def __init__(self, hou_pane):
+ self.hou_pane = hou_pane
+
+
+ """ Pane """
+
+ def close(self):
+ for tab in self.allTabs():
+ tab.close()
+
+ def isMaximized(self):
+ return self.hou_pane.isMaximized()
+
+ def qtScreenGeometry(self):
+ return self.hou_pane.qtScreenGeometry()
+
+ def setIsMaximized(self, bool):
+ self.hou_pane.setIsMaximized(bool)
+
+ def toggleMaximize(self):
+ self.hou_pane.setIsMaximized(not self.isMaximized())
+
+
+ """ Split """
+
+ def contract(self):
+ fraction = round(self.splitFraction(), 3) + 0.1
+ self.setSplitFraction(fraction)
+ hou.ui.setStatusMessage("Pane fraction: " + str(fraction))
+
+ def expand(self):
+ fraction = round(self.splitFraction(), 3) - 0.1
+ self.setSplitFraction(fraction)
+ hou.ui.setStatusMessage("Pane fraction: " + str(fraction))
+
+ def isSplitMaximized(self):
+ return self.hou_pane.isSplitMaximized()
+
+ def setIsSplitMaximized(self, bool):
+ self.hou_pane.setIsSplitMaximized(bool)
+
+ def setRatioHalf(self):
+ self.setSplitFraction(0.5)
+
+ def setRatioQuarter(self):
+ self.setSplitFraction(0.25)
+
+ def setRatioThird(self):
+ self.setSplitFraction(0.333)
+
+ def setSplitFraction(self, fraction):
+ self.hou_pane.setSplitFraction(fraction)
+
+ def splitFraction(self):
+ return self.hou_pane.getSplitFraction()
+
+ def splitHorizontal(self):
+ self.hou_pane.splitHorizontally()
+
+ def splitRotate(self):
+ self.hou_pane.splitRotate()
+
+ def splitSwap(self):
+ self.hou_pane.splitSwap()
+
+ def splitVertical(self):
+ self.hou_pane.splitVertically()
+
+ def toggleSplitMaximized(self):
+ self.setIsSplitMaximized(not self.isSplitMaximized())
+
+
+ """ Interface """
+
+ def allTabs(self):
+ tabs = []
+ for hou_tab in self.hou_pane.tabs():
+ tabs.append(self.convertTab(hou_tab))
+ return tabs
+
+ def convertTab(self, hou_tab):
+ hou_type = hou_tab.type()
+ if hou_type == hou.paneTabType.SceneViewer:
+ return SceneViewer(hou_tab)
+ elif hou_type == hou.paneTabType.NetworkEditor:
+ return NetworkEditor(hou_tab)
+ elif hou_type == hou.paneTabType.DetailsView:
+ return PathTab(hou_tab)
+ elif hou_type == hou.paneTabType.Parm:
+ return PathTab(hou_tab)
+ else:
+ return Tab(hou_tab)
+
+ def currentTab(self):
+ hou_tab = self.hou_pane.currentTab()
+ return self.convertTab(hou_tab)
+
+ def isShowingTabs(self):
+ return self.hou_pane.isShowingPaneTabs()
+
+ def nextTab(self):
+ tabs = self.hou_pane.tabs()
+ tab = self.hou_pane.currentTab()
+ index = tabs.index(tab)
+ index = (index + 1) % len(tabs)
+ tabs[index].setIsCurrentTab()
+
+ def previousTab(self):
+ tabs = self.hou_pane.tabs()
+ tab = self.hou_pane.currentTab()
+ index = tabs.index(tab)
+ index = (index - 1) % len(tabs)
+ tabs[index].setIsCurrentTab()
+
+ def showTabs(self, value):
+ self.hou_pane.showPaneTabs(value)
+
+ def toggleTabs(self):
+ self.showTabs(not self.isShowingTabs())
diff --git a/python3.11libs/hc/panel.py b/python3.11libs/hc/panel.py
new file mode 100644
index 0000000..1d00fcf
--- /dev/null
+++ b/python3.11libs/hc/panel.py
@@ -0,0 +1,226 @@
+import hou, inspect
+from PySide6 import QtCore, QtGui, QtWidgets
+from PySide6.QtCore import Qt
+from fuzzyfinder import fuzzyfinder
+from .session import Session
+from .pane import Pane
+from .tab import Tab
+from importlib import reload
+
+
+class Panel(QtWidgets.QDialog):
+ def __init__(self):
+ super(Panel, self).__init__(hou.qt.mainWindow())
+
+ # Style
+ self.resize(900, 400)
+ self.setWindowTitle("Panel")
+ self.setWindowFlags(Qt.Tool | Qt.WindowStaysOnTopHint)
+
+ # Context
+ self.session = Session()
+ self.tab = self.session.currentTab()
+ self.pane = self.tab.pane()
+ self.map = {}
+ type = self.tab.type()
+ print("\n###\nPanel Context: " + str(type) + "\n###")
+ if type == "Tab":
+ self.map = self.baseMap()
+ elif type == "PathTab":
+ self.map = self.baseMap() | self.pathTabMap()
+ elif type == "NetworkEditor":
+ self.map = self.baseMap() | self.pathTabMap() | self.networkEditorMap()
+ elif type == "SceneViewer":
+ self.map = self.baseMap() | self.pathTabMap() | self.sceneViewerMap()
+
+ # Function list
+ self.function_list = FunctionList()
+ self.populate()
+ self.function_list.setIndex(0)
+ # Signals
+ self.function_list.itemClicked.connect(self.exec)
+ self.function_list.setSelectionMode(QtWidgets.QListWidget.SingleSelection)
+
+ # Input line
+ self.input_line = QtWidgets.QLineEdit()
+ # Signals
+ self.input_line.returnPressed.connect(self.exec)
+ self.input_line.textEdited.connect(self.function_list.filter)
+ # Set focus
+
+ # Layout
+ self.layout = QtWidgets.QVBoxLayout()
+ self.layout.addWidget(self.function_list)
+ self.layout.addWidget(self.input_line)
+ self.setLayout(self.layout)
+
+ # Do this last I guess
+ self.input_line.setFocus()
+
+ def closeEvent(self, event):
+ self.setParent(None)
+
+ def keyPressEvent(self, event):
+ key = event.key()
+ mod = event.modifiers()
+
+ if key == Qt.Key_Up:
+ self.function_list.selectPrevious()
+ return
+ elif key == Qt.Key_Down:
+ self.function_list.selectNext()
+ return
+ elif key == Qt.Key_P and mod == Qt.ControlModifier:
+ self.function_list.selectPrevious()
+ return
+ elif key == Qt.Key_N and mod == Qt.ControlModifier:
+ self.function_list.selectNext()
+ return
+
+ super().keyPressEvent(event)
+
+ def exec(self):
+ items = self.function_list.items()
+ index = items.index(self.function_list.currentItem())
+ item = items[index]
+ function = self.map[item.text()]
+ function()
+
+ def populate(self):
+ items = []
+ for key in self.map:
+ items.append(key)
+ self.function_list.addItems(items)
+
+ def baseMap(self):
+ return {
+ "Close Other Tabs": self.tab.closeOtherTabs,
+ "Close Tab": self.tab.close,
+ "Color Editor": self.session.colorEditor,
+ "Contract Pane": self.pane.contract,
+ "Expand Pane": self.pane.expand,
+ "Floating Parameter Editor": self.session.floatingParameterEditor,
+ "Hide Shelf": self.session.hideShelf,
+ "Maximize Pane": self.pane.toggleMaximize,
+ "Next Tab": self.pane.nextTab,
+ "Open File": self.session.openFile,
+ "Open Preferences": self.session.openPreferences,
+ "Previous Tab": self.pane.previousTab,
+ "Reload Colors": self.session.reloadColorSchemes,
+ "Reload Hotkeys": self.session.reloadHotkeys,
+ "Reload Keycam": self.session.reloadKeycam,
+ "Rename Tabs": self.session.renameTabs,
+ "Save": self.session.save,
+ "Set Type Network Editor": self.tab.setTypeNetworkEditor,
+ "Set Type Parameters": self.tab.setTypeParm,
+ "Set Type Python Shell": self.tab.setTypePythonShell,
+ "Set Type Scene Viewer": self.tab.setTypeSceneViewer,
+ "Set Type Spreadsheet": self.tab.setTypeDetailsView,
+ "Show Shelf": self.session.showShelf,
+ "Split Rotate": self.pane.splitRotate,
+ "Split Swap": self.pane.splitSwap,
+ "Toggle all Menus": self.session.toggleMenus,
+ "Toggle All Paths": self.session.toggleNetworkControls,
+ "Toggle All Tabs": self.session.toggleTabs,
+ "Toggle Autosave": self.session.toggleAutoSave,
+ "Toggle Main Menu": self.session.toggleMainMenu,
+ "Toggle Stowbars": self.session.toggleStowbars,
+ "Toggle Tabs": self.pane.toggleTabs,
+ "Toggle Update Mode": self.session.toggleUpdateMode,
+ "Update Mode: Auto": self.session.setUpdateModeAuto,
+ "Update Mode: Manual": self.session.setUpdateModeManual
+ }
+
+ def sceneViewerMap(self):
+ return {
+ "Frame": self.tab.frame,
+ "Home Viewports": self.tab.homeAllViewports,
+ "Keycam": self.tab.keycam,
+ "Toggle Backface": self.tab.toggleBackface,
+ "Toggle Bars": self.tab.toggleBars,
+ "Toggle Display Bar": self.tab.toggleDisplayBar,
+ "Toggle Grid": self.tab.toggleGrid,
+ "Toggle Group List": self.tab.toggleGroupList,
+ "Toggle Light Geo": self.tab.toggleLightGeo,
+ "Toggle Operation Bar": self.tab.toggleOperationBar,
+ "Toggle Point Markers": self.tab.togglePointMarkers,
+ "Toggle Point Normals": self.tab.togglePointNormals,
+ "Toggle Point Numbers": self.tab.togglePointNumbers,
+ "Toggle Prim Normals": self.tab.togglePrimNormals,
+ "Toggle Prim Numbers": self.tab.togglePrimNumbers,
+ "ToggleSelectionBar": self.tab.toggleSelectionBar,
+ "Toggle Vectors": self.tab.toggleVectors
+ }
+
+ def pathTabMap(self):
+ return {
+ "Toggle Path": self.tab.toggleNetworkControls,
+ "Toggle Pin": self.tab.togglePin
+ }
+
+ def networkEditorMap(self):
+ return {
+ "Arrange Nodes": self.tab.arrangeNodes,
+ "Deselect All": self.tab.pwd().deselectAll,
+ "Frame All": self.tab.frameAll,
+ "Rename Node": self.tab.renameNode,
+ "Set Node Colors": self.tab.setNodeColors,
+ "Set Node Shapes": self.tab.setNodeShapes,
+ "Show Path Message": self.tab.showPathMessage,
+ "Toggle Grid Mode": self.tab.toggleGridMode,
+ "Toggle Menu": self.tab.toggleMenu
+ # "Show Radial Menu": self.tab.showRadialMenu,
+ }
+
+
+class FunctionList(QtWidgets.QListWidget):
+ def currentItem(self):
+ return self.selectedItems()[0]
+
+ def filter(self, query):
+ items = self.items()
+ item_names = [item.text() for item in items]
+ matches = list(fuzzyfinder(query, item_names))
+ for item in items:
+ if item.text() in matches:
+ item.setHidden(0)
+ else:
+ item.setHidden(1)
+ self.setIndex(0)
+
+ def items(self):
+ items = []
+ for i in range(self.count()):
+ items.append(self.item(i))
+ return(items)
+
+ def selectNext(self):
+ items = self.visibleItems()
+ index = items.index(self.currentItem())
+ index = (index + 1) % len(items)
+ self.setIndex(index)
+
+ def selectPrevious(self):
+ items = self.visibleItems()
+ index = items.index(self.currentItem())
+ index = (index - 1) % len(items)
+ self.setIndex(index)
+
+ def setIndex(self, index):
+ counter = 0
+ for item in self.visibleItems():
+ if counter == index:
+ self.setCurrentItem(item)
+ counter += 1
+
+ def visibleItems(self):
+ items = []
+ for i in range(self.count()):
+ item = self.item(i)
+ if not item.isHidden():
+ items.append(item)
+ return(items)
+
+
+# class InputLine(QtWidgets.QLineEdit):
+
diff --git a/python3.11libs/hc/pathtab.py b/python3.11libs/hc/pathtab.py
new file mode 100644
index 0000000..d1ed0bb
--- /dev/null
+++ b/python3.11libs/hc/pathtab.py
@@ -0,0 +1,26 @@
+import hou
+from .geo import Geo
+from .node import Node
+from .tab import Tab
+
+class PathTab(Tab):
+ def __init__(self, hou_tab):
+ self.hou_tab = hou_tab
+
+ def geo(self):
+ return Geo(self.hou_geo())
+
+ def hou_geo(self):
+ visible_nodes = self.pwd().visibleNodes()
+ geo = hou.Geometry()
+
+ for node in visible_nodes:
+ geo.merge(node.geometry())
+
+ return geo
+
+ def path(self):
+ return self.pwd().path()
+
+ def pwd(self):
+ return Node(self.hou_tab.pwd())
diff --git a/python3.11libs/hc/prefs.py b/python3.11libs/hc/prefs.py
new file mode 100644
index 0000000..af32f46
--- /dev/null
+++ b/python3.11libs/hc/prefs.py
@@ -0,0 +1,3 @@
+class Prefs:
+ def __init__(self):
+ self.node_color = (0.66, 0.66, 0.66)
diff --git a/python3.11libs/hc/radial.py b/python3.11libs/hc/radial.py
new file mode 100644
index 0000000..5dcef18
--- /dev/null
+++ b/python3.11libs/hc/radial.py
@@ -0,0 +1,186 @@
+import hou
+
+#
+# PaneTab object is retrieved by calling kwargs["pane"]
+#
+
+class Radial:
+ def __init__(self):
+ return
+
+ def createItem(label="label", pos=0, submenu=False, script="script"):
+ item = hou.ui.createRadialItem(submenu=submenu)
+ item.setLabel(label)
+ item.setScript(script)
+ hou.ui.injectRadialItem(pos, item)
+
+ #
+ # Scene Viewer
+ #
+
+ def sceneViewerMain(**kwargs):
+ menu = hou.ui.createRadialMenu("hc_radial_sceneviewer_main", "HC Radial Scene Viewer Main")
+ createItem(
+ pos=0,
+ label="UI",
+ submenu=True, script="from hc import Radial; Radial.sceneViewerUI()"
+ )
+ createItem(
+ pos=7,
+ label="Operation bar",
+ script="from hc import SceneViewer; SceneViewer(kwargs['pane']).toggleOperationBar()"
+ )
+ createItem(
+ pos=6,
+ label="Display bar",
+ script="from hc import SceneViewer; SceneViewer(kwargs['pane']).toggleDisplayOptionsToolbar()"
+ )
+ createItem(
+ pos=5,
+ label="Selection bar",
+ script="from hc import SceneViewer; SceneViewer(kwargs['pane']).toggleSelectionBar()"
+ )
+ createItem(
+ pos=4,
+ label="Keycam",
+ script="from hc import SceneViewer; SceneViewer(kwargs['pane']).keycam()"
+ )
+ return menu
+
+ def sceneViewerLayout(**kwargs):
+ menu = hou.ui.createRadialMenu("hc_radial_sceneviewer_layout", "HC Radial - Scene Viewer Layout")
+ createItem(
+ pos=0,
+ label="DoubleSide",
+ script="from hc import SceneViewer; SceneViewer(kwargs['pane']).setLayout('DoubleSide')"
+ )
+ createItem(
+ pos=1,
+ label="DoubleStack",
+ script="from hc import SceneViewer; SceneViewer(kwargs['pane']).setLayout('DoubleStack')"
+ )
+ createItem(
+ pos=2,
+ label="Quad",
+ script="from hc import SceneViewer; SceneViewer(kwargs['pane']).setLayout('Quad')"
+ )
+ createItem(
+ pos=3,
+ label="QuadBottomSplit",
+ script="from hc import SceneViewer; SceneViewer(kwargs['pane']).setLayout('QuadBottomSplit')"
+ )
+ createItem(
+ pos=4,
+ label="QuadLeftSplit",
+ script="from hc import SceneViewer; SceneViewer(kwargs['pane']).setLayout('QuadLeftSplit')"
+ )
+ createItem(
+ pos=5,
+ label="Single",
+ script="from hc import SceneViewer; SceneViewer(kwargs['pane']).setLayout('Single')"
+ )
+ createItem(
+ pos=6,
+ label="TripleBottomSplit",
+ script="from hc import SceneViewer; SceneViewer(kwargs['pane']).setLayout('TripleBottomSplit')"
+ )
+ createItem(
+ pos=7,
+ label="TripleLeftSplit",
+ script="from hc import SceneViewer; SceneViewer(kwargs['pane']).setLayout('TripleLeftSplit')"
+ )
+ return menu
+
+ def sceneViewerUI(**kwargs):
+ menu = hou.ui.createRadialMenu("hc_radial_sceneviewer_ui", "HC Radial - Scene Viewer UI")
+ createItem(
+ pos=0,
+ label="Tabs",
+ script="from hc import Tab; Tab(kwargs['pane']).pane().toggleTabs()"
+ )
+ createItem(
+ pos=7,
+ label="Path",
+ script="from hc import Tab; Tab(kwargs['pane']).toggleNetworkControls()"
+ )
+ createItem(
+ pos=6,
+ label="Maximize",
+ script="from hc import Tab; Tab(kwargs['pane']).pane().toggleMaximize()"
+ )
+ createItem(
+ pos=5,
+ label="Stowbars",
+ script="from hc import Session; Session().toggleStowbars()"
+ )
+ createItem(
+ pos=4,
+ label="All tabs",
+ script="from hc import Session; Session().toggleTabs()"
+ )
+ createItem(
+ pos=3,
+ label="All paths",
+ script="from hc import Session; Session().toggleNetworkControls()"
+ )
+ createItem(
+ pos=2,
+ label="Layout",
+ submenu=True, script="from hc import radialutils; radialutils.viewerRadialLayout()"
+ )
+ return menu
+
+
+ def networkEditorMain(**kwargs):
+ menu = hou.ui.createRadialMenu("hc_radial_networkeditor", "HC Radial - Network Editor")
+ createItem(
+ pos=0,
+ submenu=True,
+ label="UI",
+ script="from hc import Radial; Radial.networkEditorUI()"
+ )
+ createItem(
+ pos=7,
+ label="Menu",
+ script="from hc import NetworkEditor; NetworkEditor(kwargs['pane']).toggleMenu()"
+ )
+ createItem(
+ pos=6,
+ label="Frame all",
+ script="from hc import NetworkEditor; NetworkEditor(kwargs['pane']).frameAll()"
+ )
+ return menu
+
+ def networkEditorUI(**kwargs):
+ menu = hou.ui.createRadialMenu("hc_radial_networkeditor", "HC Radial - Network Editor")
+ createItem(
+ pos=0,
+ label="Tabs",
+ script="from hc import Tab; Tab(kwargs['pane']).pane().toggleTabs()"
+ )
+ createItem(
+ pos=7,
+ label="Path",
+ script="from hc import Tab; Tab(kwargs['pane']).toggleNetworkControls()"
+ )
+ createItem(
+ pos=6,
+ label="All tabs",
+ script="from hc import Session; Session().toggleTabs()"
+ )
+ createItem(
+ pos=5,
+ label="All paths",
+ script="from hc import Session; Session().toggleNetworkControls()"
+ )
+ createItem(
+ pos=4,
+ label="Stowbars",
+ script="from hc import Session; Session().toggleStowbars()"
+ )
+ createItem(
+ pos=3,
+ label="Maximize",
+ script="from hc import Tab; Tab(kwargs['pane']).pane().toggleMaximize()"
+ )
+ return menu
diff --git a/python3.11libs/hc/sceneviewer.py b/python3.11libs/hc/sceneviewer.py
new file mode 100644
index 0000000..11d3292
--- /dev/null
+++ b/python3.11libs/hc/sceneviewer.py
@@ -0,0 +1,275 @@
+import hou, types
+from .pathtab import PathTab
+
+"""
+Layout/viewport IDs
+
+DoubleSide: 2 3
+
+DoubleStack: 3
+ 0
+
+Quad: 2 3
+ 1 0
+
+QuadBottomSplit: 3
+ 2 1 0
+
+QuadLeftSplit: 2
+ 1 3
+ 0
+
+TripleBottomSplit: 3
+ 1 0
+
+TripleLeftSplit: 2
+ 0 3
+ 1
+
+Single:
+setViewportLayout(layout, single=-1)
+-1: current viewport (viewportmouse is/was over)
+0: top-left quad viewport (default: Top)
+1: top-right quad viewport (default: Perspective)
+2: bottom-left quad viewport (default: Front)
+3: bottom-right quad viewport (default: Right)
+"""
+
+class SceneViewer(PathTab):
+ def __init__(self, hou_tab):
+ self.hou_tab = hou_tab
+
+
+ """ Geometry """
+
+ def displaySets(self):
+ displaySets = []
+ for viewport in self.viewports():
+ settings = viewport.settings()
+ displaySet = settings.displaySet(hou.displaySetType.DisplayModel)
+ displaySets.append(displaySet)
+ return(displaySets)
+
+ def toggleLightGeo(self):
+ self.setShowLights(not self.showLights())
+
+ def toggleBackface(self):
+ visible = 0
+ displaySets = self.displaySets()
+ for displaySet in displaySets:
+ if displaySet.isShowingPrimBackfaces():
+ visible = 1
+ for displaySet in displaySets:
+ displaySet.showPrimBackfaces(not visible)
+
+ def togglePointMarkers(self):
+ visible = 0
+ displaySets = self.displaySets()
+ for displaySet in displaySets:
+ if displaySet.isShowingPointMarkers():
+ visible = 1
+ for displaySet in displaySets:
+ displaySet.showPointMarkers(not visible)
+
+ def togglePointNormals(self):
+ visible = 0
+ displaySets = self.displaySets()
+ for displaySet in displaySets:
+ if displaySet.isShowingPointNormals():
+ visible = 1
+ for displaySet in displaySets:
+ displaySet.showPointNormals(not visible)
+
+ def togglePointNumbers(self):
+ visible = 0
+ displaySets = self.displaySets()
+ for displaySet in displaySets:
+ if displaySet.isShowingPointNumbers():
+ visible = 1
+ for displaySet in displaySets:
+ displaySet.showPointNumbers(not visible)
+
+ def togglePrimNormals(self):
+ visible = 0
+ displaySets = self.displaySets()
+ for displaySet in displaySets:
+ if displaySet.isShowingPrimNormals():
+ visible = 1
+ for displaySet in displaySets:
+ displaySet.showPrimNormals(not visible)
+
+ def togglePrimNumbers(self):
+ visible = 0
+ displaySets = self.displaySets()
+ for displaySet in displaySets:
+ if displaySet.isShowingPrimNumbers():
+ visible = 1
+ for displaySet in displaySets:
+ displaySet.showPrimNumbers(not visible)
+
+ def toggleVectors(self):
+ for viewport in self.viewports():
+ settings = viewport.settings()
+ scale = settings.vectorScale()
+ if scale == 1:
+ settings.setVectorScale(0)
+ elif scale == 0:
+ settings.setVectorScale(1)
+ else:
+ settings.setVectorScale(1)
+
+
+ """ Grids """
+
+ def referencePlane(self):
+ return self.hou_tab.referencePlane()
+
+ def toggleGrid(self):
+ refplane = self.referencePlane()
+ refplane.setIsVisible(not refplane.isVisible())
+
+
+ """ Layout """
+
+ def layout(self):
+ return self.hou_tab.viewportLayout()
+
+ def layouts(self):
+ return (
+ hou.geometryViewportLayout.DoubleSide,
+ hou.geometryViewportLayout.DoubleStack,
+ hou.geometryViewportLayout.Quad,
+ hou.geometryViewportLayout.QuadBottomSplit,
+ hou.geometryViewportLayout.QuadLeftSplit,
+ hou.geometryViewportLayout.TripleBottomSplit,
+ hou.geometryViewportLayout.TripleLeftSplit,
+ hou.geometryViewportLayout.Single,
+ )
+
+ def layoutIndices(self):
+ return (
+ (2, 3),
+ (3, 0),
+ (2, 3, 1, 0),
+ (3, 2, 1, 0),
+ (2, 1, 0, 3),
+ (3, 1, 0),
+ (2, 3, 1),
+ (3),
+ )
+
+ def nextLayout(self):
+ layouts = self.layouts()
+ layout = self.layout()
+ index = layouts.index(layout)
+ index = (index+1) % len(layouts)
+ self.setLayout(layouts[index])
+ return
+
+ def setLayout(self, layout):
+ layout_map = {
+ "DoubleSide": hou.geometryViewportLayout.DoubleSide,
+ "DoubleStack": hou.geometryViewportLayout.DoubleStack,
+ "Quad": hou.geometryViewportLayout.Quad,
+ "QuadBottomSplit": hou.geometryViewportLayout.QuadBottomSplit,
+ "QuadLeftSplit": hou.geometryViewportLayout.QuadLeftSplit,
+ "Single": hou.geometryViewportLayout.Single,
+ "TripleBottomSplit": hou.geometryViewportLayout.TripleBottomSplit,
+ "TripleLeftSplit": hou.geometryViewportLayout.TripleLeftSplit
+ }
+ self.hou_tab.setViewportLayout(layout_map[layout])
+
+
+ """ UI """
+
+ def isVisibleDisplayBar(self):
+ return self.hou_tab.isShowingDisplayOptionsBar()
+
+ def isVisibleGroupList(self):
+ return self.hou_tab.isGroupListVisible()
+
+ def isVisibleOperationBar(self):
+ return self.hou_tab.isShowingOperationBar()
+
+ def isVisibleSelectionBar(self):
+ return self.hou_tab.isShowingSelectionBar()
+
+ def showDisplayBar(self, value):
+ self.hou_tab.showDisplayOptionsBar(value)
+
+ def showGroupList(self, value):
+ self.hou_tab.setGroupListVisible(value)
+
+ def showOperationBar(self, value):
+ self.hou_tab.showOperationBar(value)
+
+ def showSelectionBar(self, value):
+ self.hou_tab.showSelectionBar(value)
+
+ def toggleDisplayBar(self):
+ self.showDisplayBar(not self.isVisibleDisplayBar())
+
+ def toggleGroupList(self):
+ self.showGroupList(not self.isVisibleGroupList)
+
+ def toggleOperationBar(self):
+ self.showOperationBar(not self.isVisibleOperationBar())
+
+ def toggleSelectionBar(self):
+ self.showSelectionBar(not self.isVisibleSelectionBar())
+
+ def toggleBars(self):
+ state = self.hou_tab.isShowingOperationBar() + self.hou_tab.isShowingDisplayOptionsBar() + self.hou_tab.isShowingSelectionBar()
+ if state > 0:
+ self.showOperationBar(0)
+ self.showDisplayBar(0)
+ self.showSelectionBar(0)
+ else:
+ self.showOperationBar(1)
+ self.showDisplayBar(1)
+ self.showSelectionBar(1)
+
+
+ """ Utils """
+
+ def type(self):
+ return "SceneViewer"
+
+ def keycam(self):
+ """
+ Contexts:
+ Chop, ChopNet, Cop, Cop2, CopNet, Data, Director, Dop, Driver, Lop, Manager, Object, Shop, Sop, Top, TopNet, Vop, VopNet
+ """
+ context_map = {
+ "Object": "Entered keycam in an obj context",
+ "Sop": "Entered keycam in a sop context",
+ "Lop": "Entered keycam in a lop context",
+ }
+ context = self.pwd().childCat()
+ if context in context_map:
+ self.hou_tab.setCurrentState('keycam')
+ hou.ui.setStatusMessage(context_map[context])
+ else:
+ hou.ui.setStatusMessage("No obj, sop or lop context", hou.severityType.Error)
+
+
+ """ Viewports """
+
+ def homeAllViewports(self):
+ for viewport in self.viewports():
+ viewport.home()
+
+ def currentViewport(self):
+ return self.hou_tab.curViewport()
+
+ def allViewports(self):
+ return self.hou_tab.viewports()
+
+ def frame(self):
+ for viewport in self.allViewports():
+ cam = viewport.camera()
+ # Is camera node or default
+ if not cam:
+ viewport.frameAll()
+ else:
+ viewport.frameAll()
diff --git a/python3.11libs/hc/session.py b/python3.11libs/hc/session.py
new file mode 100644
index 0000000..a5cb232
--- /dev/null
+++ b/python3.11libs/hc/session.py
@@ -0,0 +1,295 @@
+import hou
+from .networkeditor import NetworkEditor
+from .sceneviewer import SceneViewer
+from .pane import Pane
+from .pathtab import PathTab
+from .tab import Tab
+
+
+class Session:
+ def __init__(self):
+ return
+
+
+ """ Context """
+
+ def currentNode(self):
+ return self.currentTab().currentNode()
+
+ def projectPath(self):
+ return hou.hipFile.path()
+
+
+ """ Layout """
+ """ 'allTabs' and 'currentTab' methods intentionally do not use the hou.ui.paneTabUnderCursor method """
+
+ def allNetworkEditors(self):
+ editors = []
+ for tab in self.allTabs():
+ if tab.type() == "NetworkEditor":
+ editors.append(tab)
+ return editors
+
+ def allPanes(self):
+ panes = []
+ for hou_pane in self.desktop().panes():
+ panes.append(Pane(hou_pane))
+ return panes
+
+ def allSceneViewers(self):
+ viewers = []
+ for tab in self.tabs():
+ if tab.type() == "SceneViewer":
+ viewers.append(SceneViewer(tab))
+ return viewers
+
+ def allTabs(self):
+ tabs = []
+ for pane in self.allPanes():
+ for tab in pane.allTabs():
+ tabs.append(tab)
+ return tabs
+
+ def allViewports(self):
+ viewports = []
+ viewers = self.sceneViewers
+ for viewer in viewers:
+ for viewport in viewer.viewports():
+ viewports.append(viewport)
+ return viewports
+
+ def clearLayout(self):
+ tabs = self.tabs()
+ for tab in tabs:
+ if tab != tabs[0]:
+ tab.close()
+
+ def currentPane(self):
+ return Pane(hou.ui.paneUnderCursor())
+
+ def currentTab(self):
+ return self.currentPane().currentTab()
+
+ def desktop(self):
+ return hou.ui.curDesktop()
+
+ def printLayout(self):
+ panes = hou.ui.panes()
+ root = panes[0].getSplitParent()
+ lefts = []
+ tops = []
+ for pane in self.panes():
+ geo = pane.qtScreenGeometry()
+ lefts.append(geo.left())
+ tops.append(geo.top())
+ print(lefts)
+ print(tops)
+
+ def renameTabs(self):
+ tabs = hou.ui.paneTabs()
+ i = 0
+ for tab in tabs:
+ tab.setName('placeholder' + str(i))
+ i += 1
+ i = 0
+ for tab in tabs:
+ tab.setName('panetab' + str(i))
+ i += 1
+
+ def root(self):
+ root = hou.ui.panes()[0].getSplitParent().getSplitParent()
+
+
+ """ Settings """
+
+ def autosave(self):
+ return hou.getPreference('autoSave')
+
+ def openPreferences(self):
+ hou.ui.openPreferences('ui', '')
+
+ def reloadColorSchemes(self):
+ hou.ui.reloadColorScheme()
+ hou.ui.reloadViewportColorSchemes()
+
+ def reloadStudioUtils(self):
+ hou.ui.reloadPackage(hou.homeHoudiniDirectory() + '/packages/houdini-studio-utils.json')
+
+ def reloadHotkeys(self):
+ from .bindings import Bindings
+ Bindings().load()
+
+ def reloadKeycam(self):
+ hou.ui.reloadViewerState('keycam')
+
+ def setUpdateModeAuto(self):
+ hou.setUpdateMode(hou.updateMode.AutoUpdate)
+
+ def setUpdateModeManual(self):
+ hou.setUpdateMode(hou.updateMode.Manual)
+
+ def triggerUpdate(self):
+ hou.ui.triggerUpdate()
+
+ def toggleAutoSave(self):
+ map = {'0': '1', '1': '0'}
+ hou.setPreference('autoSave', map[hou.getPreference('autoSave')])
+
+ def toggleUpdateMode(self):
+ mode_map = {
+ "updateMode.AutoUpdate": hou.updateMode.Manual,
+ "updateMode.Manual": hou.updateMode.AutoUpdate
+ }
+ hou.setUpdateMode(mode_map(str(hou.updateModeSetting)))
+
+ def updateMainMenuBar(self):
+ hou.ui.updateMainMenuBar()
+
+
+ """ Utils """
+
+ def colorEditor(self):
+ hou.ui.selectColor()
+
+ def keycam(self):
+ msgmap = {
+ "Object": "Entered keycam in an obj/object context",
+ "Sop": "Entered keycam in a sop/geometry context",
+ "Lop": "Entered keycam in a lop context"
+ }
+ viewer = self.sceneViewers()[0]
+ category = viewer.pwd().childTypeCategory().name()
+ if category in msgmap:
+ viewer.setCurrentState('keycam')
+ hou.ui.setStatusMessage(msgmap[category])
+ else:
+ hou.ui.setStatusMessage("Keycam is only available in obj, sop and lop contexts",
+ hou.severityType.Error)
+
+ def openFile(self):
+ file = hou.ui.selectFile()
+ hou.hipFile.load(file)
+
+ def removeEventLoopCallbacks(self):
+ callbacks = hou.ui.eventLoopCallbacks()
+ for callback in callbacks:
+ hou.ui.removeEventLoopCallback(callback)
+
+ # def restartHoudini(self):
+ # import os
+ # import subprocess
+ # executable = sys.argv[0]
+ # executable = os.environ.get("HFS") + "/bin/houdini"
+ # subprocess.Popen([executable])
+ # hou.exit()
+ # return
+
+ def save(self):
+ hou.hipFile.save()
+
+
+ """ Interface """
+
+ def isVisibleMainMenu(self):
+ value = hou.getPreference('showmenu.val')
+ return int(value)
+
+ def isVisibleMenus(self):
+ visible = 0
+ panes = self.allPanes()
+ tabs = self.allTabs()
+ # Main menu
+ if self.isVisibleMainMenu():
+ visible = 1
+ # Tabs
+ for tab in tabs:
+ if tab.type() == "NetworkEditor":
+ if tab.isMenuOpen():
+ visible = 1
+ elif tab.isShowingNetworkControls():
+ visible = 1
+ elif tab.type() == "PathTab":
+ if tab.isShowingNetworkControls():
+ visible = 1
+ elif tab.type() == "SceneViewer":
+ if tab.isShowingNetworkControls():
+ visible = 1
+ elif tab.isVisibleOperationBar():
+ visible = 1
+ elif tab.isVisibleDisplayBar():
+ visible = 1
+ elif tab.isVisibleSelectionBar():
+ visible = 1
+ # Panes
+ for pane in panes:
+ if pane.isShowingTabs():
+ visible = 1
+ return visible
+
+ def floatingParameterEditor(self):
+ tab = self.currentTab()
+ if tab.type() == "NetworkEditor":
+ hou.ui.showFloatingParameterEditor(self.currentNode())
+ else:
+ hou.ui.setStatusMessage("Not a network editor", hou.severityType.Error)
+
+ def hideShelf(self):
+ self.desktop().shelfDock().show(0)
+
+ def showMainMenu(self, value):
+ hou.setPreference('showmenu.val', str(value))
+
+ def showShelf(self):
+ self.shelfDock().show(1)
+
+ def toggleMainMenu(self):
+ value = (self.isVisibleMainMenu()+1) % 2
+ self.showMainMenu(value)
+
+ def toggleMenus(self):
+ visible = self.isVisibleMenus()
+ panes = self.allPanes()
+ tabs = self.allTabs()
+ # Set state
+ self.showMainMenu((visible+1) % 2)
+ for tab in tabs:
+ if tab.type() == "NetworkEditor":
+ tab.showNetworkControls(not visible)
+ tab.setMenuOpen(0)
+ elif tab.type() == "PathTab":
+ tab.showNetworkControls(not visible)
+ elif tab.type() == "SceneViewer":
+ tab.showNetworkControls(not visible)
+ tab.showOperationBar(not visible)
+ tab.showDisplayBar(not visible)
+ tab.showSelectionBar(not visible)
+ for pane in panes:
+ pane.showTabs(not visible)
+ # Apply (needs to be called twice for some reason)
+ # hou.ui.setHideAllMinimizedStowbars(visible)
+ # hou.ui.setHideAllMinimizedStowbars(visible)
+
+ def toggleNetworkControls(self):
+ visible = 0
+ tabs = self.allTabs()
+ for tab in tabs:
+ if tab.isShowingNetworkControls():
+ visible = 1
+ for tab in tabs:
+ tab.showNetworkControls(not visible)
+
+ def toggleStowbars(self):
+ if hou.ui.hideAllMinimizedStowbars():
+ hou.ui.setHideAllMinimizedStowbars(False)
+ else:
+ hou.ui.setHideAllMinimizedStowbars(True)
+ hou.ui.setHideAllMinimizedStowbars(True)
+
+ def toggleTabs(self):
+ visible = 0
+ panes = self.allPanes()
+ for pane in panes:
+ if pane.isShowingTabs():
+ visible = 1
+ for pane in panes:
+ pane.showTabs(not visible)
diff --git a/python3.11libs/hc/status.py b/python3.11libs/hc/status.py
new file mode 100644
index 0000000..3f519a4
--- /dev/null
+++ b/python3.11libs/hc/status.py
@@ -0,0 +1,69 @@
+import hou
+from PySide6 import QtWidgets
+from PySide6.QtWidgets import QLabel, QVBoxLayout
+from PySide6.QtCore import Qt
+
+
+class Status(QtWidgets.QDialog):
+ def __init__(self):
+ super(Dialog, self).__init__(hou.qt.mainWindow())
+ self.resize(500, 200)
+ self.setWindowTitle("Status")
+ self.setWindowFlags(Qt.Tool | Qt.WindowStaysOnTopHint)
+
+ # Widgets
+ self.project_path_label = QLabel("Project Path")
+ self.network_path_label = QLabel("Network Path")
+ self.tab_type_label = QLabel("Tab Type")
+
+ # Layout
+ self.layout = QVBoxLayout()
+ self.layout.addWidget(self.project_path_label)
+ self.layout.addWidget(self.network_path_label)
+ self.layout.addWidget(self.tab_type_label)
+ self.layout.addStretch()
+ self.setLayout(self.layout)
+
+ self.update()
+
+ def closeEvent(self, event):
+ self.setParent(None)
+
+ def lists(self):
+ # Arrays for navigating pane tabs
+ self.tab_types = (
+ hou.paneTabType.ApexEditor,
+ hou.paneTabType.CompositorViewer,
+ hou.paneTabType.DetailsView,
+ hou.paneTabType.NetworkEditor,
+ hou.paneTabType.Parm,
+ hou.paneTabType.PythonPanel,
+ hou.paneTabType.PythonShell,
+ hou.paneTabType.SceneViewer,
+ hou.paneTabType.Textport
+ )
+ self.tab_type_names = (
+ "ApexEditor",
+ "CompositorViewer",
+ "DetailsView",
+ "NetworkEditor",
+ "Parm",
+ "PythonPanel",
+ "PythonShell",
+ "SceneViewer",
+ "Textport"
+ )
+ self.tab_names = [tab.name() for tab in self.session.allTabs()]
+
+ # Populate pane tab labels array
+ self.tab_labels = []
+ for tab in self.session.allTabs():
+ index = self.tab_types.index(tab.type())
+ label = self.tab_type_names[index]
+ self.tab_labels.append(label)
+
+ def update(self):
+ # Labels
+ self.project_path_label.setText("Project path: " + hou.session.projectPath)
+ self.network_path_label.setText("Network Path: " + hou.session.networkPath)
+ self.tab_type_label.setText("Tab type: " + str(hou.session.tabType))
diff --git a/python3.11libs/hc/tab.py b/python3.11libs/hc/tab.py
new file mode 100644
index 0000000..8bbf2c0
--- /dev/null
+++ b/python3.11libs/hc/tab.py
@@ -0,0 +1,73 @@
+import hou
+import types
+
+
+class Tab():
+ def __init__(self, hou_tab):
+ self.hou_tab = hou_tab
+
+ #
+ # Tab
+ #
+
+ def close(self):
+ self.hou_tab.close()
+
+ def closeOtherTabs(self):
+ for tab in self.pane().allTabs():
+ if tab != self.tab:
+ tab.close()
+
+ def pane(self):
+ from .pane import Pane
+ return Pane(self.hou_tab.pane())
+
+ def type(self):
+ return "Tab"
+
+ def setTypeDetailsView(self):
+ self.setType(hou.paneTabType.DetailsView)
+
+ def setTypeNetworkEditor(self):
+ self.setType(hou.paneTabType.NetworkEditor)
+
+ def setTypeParm(self):
+ self.setType(hou.paneTabType.Parm)
+
+ def setTypePythonShell(self):
+ self.setType(hou.paneTabType.PythonShell)
+
+ def setTypeSceneViewer(self):
+ self.setType(hou.paneTabType.SceneViewer)
+
+ def setType(self, type):
+ tab = self.tab.setType(type)
+ return tab
+
+ #
+ # UI
+ #
+
+ def hasNetworkControls(self):
+ return self.hou_tab.hasNetworkControls()
+
+ def isPin(self):
+ return self.hou_tab.isPin()
+
+ def isShowingNetworkControls(self):
+ value = self.hou_tab.isShowingNetworkControls()
+ return value
+
+ def setPin(self, value):
+ self.hou_tab.setPin(value)
+
+ def showNetworkControls(self, value):
+ self.hou_tab.showNetworkControls(value)
+
+ def toggleNetworkControls(self):
+ if self.hasNetworkControls():
+ self.showNetworkControls(not self.isShowingNetworkControls())
+
+ def togglePin(self):
+ self.setPin(not self.isPin())
+
diff --git a/python3.11libs/hc/viewport.py b/python3.11libs/hc/viewport.py
new file mode 100644
index 0000000..69bf803
--- /dev/null
+++ b/python3.11libs/hc/viewport.py
@@ -0,0 +1,15 @@
+import hou
+
+
+class Viewport():
+ def __init__(self, hou_viewport):
+ self.hou_viewport = hou_viewport
+ return
+
+ def visualizers(self):
+ category = hou.viewportVisualizerCategory.Scene
+ vis_arr = hou.viewportVisualizers.visualizers(category)
+ return vis_arr
+
+ def setType(self, type):
+ self.hou_viewport.changeType(type)
diff --git a/python3.11libs/hc/visualizers.py b/python3.11libs/hc/visualizers.py
new file mode 100644
index 0000000..0e8c3ab
--- /dev/null
+++ b/python3.11libs/hc/visualizers.py
@@ -0,0 +1,133 @@
+import hou
+from fuzzyfinder import fuzzyfinder
+from PySide6 import QtCore
+from PySide6.QtWidgets import QListWidget, QListWidgetItem, QDialog, QLineEdit, QBoxLayout
+from PySide6.QtCore import Qt, QEvent
+from .session import Session
+
+class Visualizers(QDialog):
+ def __init__(self):
+ super(VisualizerMenu, self).__init__(hou.qt.mainWindow())
+
+ # Resources
+ self.session = Session()
+ self.viewport = self.session.viewport()
+ self.vis_arr = self.viewport.visualizers()
+
+ # Filter box
+ self.filter_box = filterBox()
+ self.filter_box.onTab.connect(self.listNext)
+ self.filter_box.returnPressed.connect(self.itemToggle)
+ self.filter_box.textEdited.connect(self.listFilter)
+
+ # List widget
+ self.list_widget = QListWidget()
+ if self.vis_arr:
+ for vis in self.vis_arr:
+ list_item = QListWidgetItem()
+ item_label = vis.label()
+ item_state = vis.isActive(self.curViewport)
+
+ list_item.setText(item_label)
+
+ if item_state:
+ list_item.setCheckState(Qt.Checked)
+ else:
+ list_item.setCheckState(Qt.Unchecked)
+
+ self.list_widget.addItem(list_item)
+
+ self.list_widget.itemClicked.connect(self.item_toggle)
+
+ # Layout
+ self.layout = QBoxLayout(QBoxLayout.Direction.TopToBottom)
+ self.layout.addWidget(self.filter_box)
+ self.layout.addWidget(self.list_widget)
+ self.setLayout(self.layout)
+ self.listSetIndex(0)
+
+ def closeEvent(self, event):
+ print("Closing")
+ self.setParent(None)
+
+ def itemToggle(self):
+ current_item = self.list_widget.selectedItems()[0]
+ item_name = current_tem.text()
+ items = self.listGetItems()
+ item_names = [item.text() for item in items]
+ index = item_names.index(item_name)
+
+ vis = self.vis_arr[index]
+ state = vis.isActive(self.viewport)
+
+ vis.setIsActive(not state, self.viewport)
+
+ if state:
+ current_item.setCheckState(Qt.Unchecked)
+ else:
+ current_item.setCheckState(Qt.Checked)
+
+ def listFilter(self):
+ text = self.input_box.text()
+ items = self.getItems()
+ names = [item.text() for item in items]
+ suggestions = fuzzyfinder(text, names)
+ suggestions = list(suggestions)
+ for item in items:
+ if item.text() in suggestions:
+ item.setHidden(0)
+ else:
+ item.setHidden(1)
+ self.listSetIndex(0)
+
+ def listGetItems(self):
+ item_count = self.list_widget.count()
+ items = [self.list_widget.item(i) for i in range(item_count)]
+ return(items)
+
+ def listGetVisibleItems(self):
+ item_count = self.list_widget.count()
+ items = []
+ for i in range(item_count):
+ item = self.list_widget.item(i)
+ if not item.isHidden():
+ items.append(item)
+ return(items)
+
+ def listNext(self):
+ visible_items = self.listGetVisibleItems()
+ current_item = self.list_widget.selectedItems()[0]
+ index = visible_items.index(current_item)
+ index = (index + 1) % len(visible_items)
+ self.listSetIndex(index)
+
+ def listPrev(self):
+ visible_items = self.listGetVisibleItems()
+ current_item = self.list_widget.selectedItems()[0]
+ index = visible_items.index(current_item)
+ index = (index - 1) % len(visible_items)
+ self.listSetIndex(index)
+
+ def listSetIndex(self, index):
+ visible_items = self.listGetVisibleItems()
+ counter = 0
+ for visible_item in visible_items:
+ if counter == index:
+ self.list_widget.setItemSelected(visible_item, 1)
+ return
+ counter += 1
+
+ class filterBox(QLineEdit):
+ def __init__(self):
+ # Key handler
+ self.onTab = QtCore.Signal()
+
+ def event(self, event):
+ if event.type() == QEvent.Type.KeyPress:
+ key = event.key()
+ if key == Qt.Key_Tab:
+ self.onTab.emit()
+ return True
+ return QLineEdit.event(self, event)
+
+
diff --git a/python3.11libs/hc/widgets.py b/python3.11libs/hc/widgets.py
new file mode 100644
index 0000000..c599795
--- /dev/null
+++ b/python3.11libs/hc/widgets.py
@@ -0,0 +1,19 @@
+from importlib import reload
+from PySide6 import QtWidgets
+from PySide6.QtCore import Qt
+
+class Widgets:
+
+ class Button(QtWidgets.QPushButton):
+ def __init__(self, text, parent=None):
+ super().__init__(text, parent)
+ self.setStyleSheet("text-align: left; padding: 0 0 0 0")
+ self.setFlat(1)
+ self.setFixedSize(120, 16)
+
+
+ class Menu(QtWidgets.QPushButton):
+ def __init__(self, label, parent=None):
+ super().__init__(label, parent)
+ self.setStyleSheet("text-align: left; padding: 0 0 0 0")
+ self.setFlat(1)