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

python3.13libs/hc/hcpane.py (6.8K)

  1 import hou
  2 from .hctab import HCTab
  3 from .hcpathtab import HCPathTab
  4 from .hcparametertab import HCParameterTab
  5 from .hcsceneviewer import HCSceneViewer
  6 from .hcnetworkeditor import HCNetworkEditor
  7 from .hccommands import command
  8 
  9 class HCPane:
 10     def __init__(self, hou_pane):
 11         self.hou_pane = hou_pane
 12 
 13     def initialize(self):
 14         """Per-pane setup hook. Called by factory callers (HCLayout.launchFloating)
 15         after a pane is created so per-pane defaults can apply."""
 16         self.showTabs(False)
 17 
 18     """ Initialization """
 19 
 20     def initializeObject(self, obj):
 21         """Initialize a newly created hou.Pane or hou.PaneTab by type.
 22 
 23         This centralizes the post-create setup used by split panes, new tabs,
 24         floating panels, etc. Pane-level setup runs once for pane objects;
 25         tab-level setup is delegated through convertTab() so HCNetworkEditor,
 26         HCSceneViewer, HCParameterTab, etc. can apply type-specific defaults.
 27         """
 28         if obj is None:
 29             return None
 30 
 31         # Pane-like object: has currentTab()/tabs(), but not necessarily type().
 32         if hasattr(obj, 'currentTab') and hasattr(obj, 'tabs'):
 33             pane = HCPane(obj)
 34             pane.initialize()
 35             for hou_tab in obj.tabs():
 36                 pane.initializeObject(hou_tab)
 37             return pane
 38 
 39         # PaneTab-like object: has type()/pane().
 40         if hasattr(obj, 'type') and hasattr(obj, 'pane'):
 41             tab = self.convertTab(obj)
 42             tab.initialize()
 43             return tab
 44 
 45         return None
 46 
 47     def _newPanesAfter(self, before_ids):
 48         panes = []
 49         desktop = hou.ui.curDesktop()
 50         if desktop is None:
 51             return panes
 52         for pane in desktop.panes():
 53             if pane.id() not in before_ids:
 54                 panes.append(pane)
 55         return panes
 56 
 57     def _splitAndInitialize(self, split_fn):
 58         before_ids = {pane.id() for pane in hou.ui.curDesktop().panes()}
 59         result = split_fn()
 60         initialized = self.initializeObject(result)
 61         if initialized is not None:
 62             return initialized
 63 
 64         # Some HOM split methods may return None; discover newly created panes.
 65         new_panes = self._newPanesAfter(before_ids)
 66         initialized_panes = []
 67         for pane in new_panes:
 68             initialized_panes.append(self.initializeObject(pane))
 69         return initialized_panes[0] if len(initialized_panes) == 1 else initialized_panes
 70 
 71     """ Adjust size """
 72 
 73     def setIsMaximized(self, bool):
 74         self.hou_pane.setIsMaximized(bool)
 75 
 76     def setIsSplitMaximized(self, bool):
 77         self.hou_pane.setIsSplitMaximized(bool)
 78 
 79     def setSplitFraction(self, fraction):
 80         self.hou_pane.setSplitFraction(fraction)
 81 
 82     @command("Split Pane Horizontally")
 83     def splitHorizontal(self):
 84         return self._splitAndInitialize(self.hou_pane.splitHorizontally)
 85 
 86     @command("Split Rotate")
 87     def splitRotate(self):
 88         self.hou_pane.splitRotate()
 89 
 90     @command("Split Swap")
 91     def splitSwap(self):
 92         self.hou_pane.splitSwap()
 93 
 94     @command("Split Pane Vertically")
 95     def splitVertical(self):
 96         return self._splitAndInitialize(self.hou_pane.splitVertically)
 97 
 98     @command("Contract Pane")
 99     def contract(self):
100         fraction = round(self.splitFraction(), 3) + 0.1
101         self.setSplitFraction(fraction)
102         hou.ui.setStatusMessage("Pane fraction: " + str(fraction))
103 
104     @command("Expand Pane")
105     def expand(self):
106         fraction = round(self.splitFraction(), 3) - 0.1
107         self.setSplitFraction(fraction)
108         hou.ui.setStatusMessage("Pane fraction: " + str(fraction))
109 
110     def setRatioHalf(self):
111         self.setSplitFraction(0.5)
112 
113     def setRatioQuarter(self):
114         self.setSplitFraction(0.25)
115 
116     def setRatioThird(self):
117         self.setSplitFraction(0.333)
118 
119     def toggleSplitMaximized(self):
120         self.setIsSplitMaximized(not self.isSplitMaximized())
121 
122     @command("Maximize Pane", state="isMaximized")
123     def toggleMaximize(self):
124         self.hou_pane.setIsMaximized(not self.isMaximized())
125 
126 
127     """ Size info """
128 
129     def isMaximized(self):
130         return self.hou_pane.isMaximized()
131 
132     def qtScreenGeometry(self):
133         return self.hou_pane.qtScreenGeometry()
134 
135     def isSplitMaximized(self):
136         return self.hou_pane.isSplitMaximized()
137 
138     def splitFraction(self):
139         return self.hou_pane.getSplitFraction()
140 
141 
142     """ Tabs """
143 
144     def close(self):
145         for tab in self.tabs():
146             tab.close()
147 
148     def convertTab(self, hou_tab):
149         hou_tab_type = hou_tab.type()
150         if hou_tab_type == hou.paneTabType.SceneViewer:
151             return HCSceneViewer(hou_tab)
152         elif hou_tab_type == hou.paneTabType.NetworkEditor:
153             return HCNetworkEditor(hou_tab)
154         elif hou_tab_type == hou.paneTabType.DetailsView:
155             return HCPathTab(hou_tab)
156         elif hou_tab_type == hou.paneTabType.Parm:
157             return HCParameterTab(hou_tab)
158         elif isinstance(hou_tab, hou.PathBasedPaneTab):
159             # Tree view, context viewer, APEX editor, render gallery...:
160             # anything with a pwd() gets the path commands (Pin among them).
161             return HCPathTab(hou_tab)
162         else:
163             return HCTab(hou_tab)
164 
165     def currentTab(self):
166         hou_tab = self.hou_pane.currentTab()
167         return self.convertTab(hou_tab)
168 
169     def isShowingTabs(self):
170         return self.hou_pane.isShowingPaneTabs()
171 
172     def newTab(self, tab_type, python_panel_interface=None):
173         hou_tab = self.hou_pane.createTab(tab_type, python_panel_interface)
174         initialized = self.initializeObject(hou_tab)
175         hou.ui.setStatusMessage('Created new tab', hou.severityType.Message)
176         return initialized
177 
178     def _stepTab(self, step):
179         # Index by tab object, not name: two tabs of the same type in one pane
180         # share a name, and looking up by name always lands on the first one.
181         hou_tabs = list(self.hou_pane.tabs())
182         current = self.hou_pane.currentTab()
183         if len(hou_tabs) < 2 or current is None:
184             return
185         try:
186             index = hou_tabs.index(current)
187         except ValueError:
188             index = 0
189         hou_tabs[(index + step) % len(hou_tabs)].setIsCurrentTab()
190 
191     @command("Next Tab")
192     def nextTab(self):
193         self._stepTab(1)
194 
195     @command("Previous Tab")
196     def prevTab(self):
197         self._stepTab(-1)
198 
199     def showTabs(self, value):
200         self.hou_pane.showPaneTabs(value)
201         self.hou_pane.showPaneTabsStow(value)
202 
203     def tabs(self):
204         tabs = []
205         for hou_tab in self.hou_pane.tabs():
206             tabs.append(HCTab(hou_tab))
207         return tabs
208 
209     def tabNames(self):
210         tab_names = []
211         for tab in self.tabs():
212             tab_names.append(tab.name())
213         return tab_names
214 
215     @command("Pane Tabs", state="isShowingTabs")
216     def toggleTabs(self):
217         self.showTabs(not self.isShowingTabs())