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())