git.lucas.co / hou-control
git clone https://git.lucas.co/hou-control.git

python3.11libs/hc/hclayout.py (6.3K)

  1 import hou
  2 from .hcsession import HCSession
  3 
  4 
  5 DEFAULT_FLOATING_PANE_TYPES = (
  6     hou.paneTabType.Parm,
  7     hou.paneTabType.NetworkEditor,
  8 )
  9 
 10 
 11 class HCLayout:
 12     """Wraps the current Houdini desktop / pane / floating-panel state."""
 13 
 14 
 15     """ Desktop """
 16 
 17 
 18     def desktop(self):
 19         return hou.ui.curDesktop()
 20 
 21     def desktopName(self):
 22         return self.desktop().name()
 23 
 24     def mode(self):
 25         return "detached" if self.desktopName() == "hc_detached" else "attached"
 26 
 27     def setDesktop(self, name):
 28         for d in hou.ui.desktops():
 29             if d.name() == name:
 30                 d.setAsCurrent()
 31                 return True
 32         return False
 33 
 34 
 35     """ Panes """
 36 
 37 
 38     def panes(self):
 39         return self.desktop().panes()
 40 
 41     def paneCount(self):
 42         return len(self.panes())
 43 
 44     def paneTabs(self):
 45         tabs = []
 46         for pane in self.panes():
 47             tabs.extend(pane.tabs())
 48         return tabs
 49 
 50     def paneTabTypes(self):
 51         return [tab.type() for tab in self.paneTabs()]
 52 
 53 
 54     """ Floating """
 55 
 56 
 57     def floatingPanels(self):
 58         return [p for p in hou.ui.floatingPanels() if p.qtParentWindow() is not None]
 59 
 60     def floatingPaneTabs(self):
 61         tabs = []
 62         for fp in self.floatingPanels():
 63             tabs.extend(fp.paneTabs())
 64         return tabs
 65 
 66     def floatingPaneTabTypes(self):
 67         return [tab.type() for tab in self.floatingPaneTabs()]
 68 
 69     def launchFloating(self, pane_types=None):
 70         """Open one floating panel per pane type, skipping any already present.
 71 
 72         Each floating panel is a separate OS window the WM can tile. The main
 73         Houdini window still exists alongside them. Each new pane tab gets
 74         initialize() called on its HC* wrapper so per-type defaults can apply.
 75         """
 76         from .hcpane import HCPane
 77         if pane_types is None:
 78             pane_types = DEFAULT_FLOATING_PANE_TYPES
 79         existing = set(self.floatingPaneTabTypes())
 80         created = []
 81         for pane_type in pane_types:
 82             if pane_type in existing:
 83                 continue
 84             panel = self.desktop().createFloatingPanel(pane_type, immediate=True)
 85             panel.attachToDesktop(True)
 86             created.append(panel)
 87             initialized_panes = set()
 88             for hou_tab in panel.paneTabs():
 89                 pane = hou_tab.pane()
 90                 if pane is None:
 91                     continue
 92                 hc_pane = HCPane(pane)
 93                 if pane.id() not in initialized_panes:
 94                     try:
 95                         hc_pane.initialize()
 96                     except Exception as e:
 97                         print(f"[HCLayout] HCPane.initialize() failed: {e!r}")
 98                     initialized_panes.add(pane.id())
 99                 hc_tab = hc_pane.convertTab(hou_tab)
100                 try:
101                     hc_tab.initialize()
102                 except Exception as e:
103                     print(f"[HCLayout] initialize() failed for {hou_tab.type().name()}: {e!r}")
104         return created
105 
106 
107     """ Snapshot """
108 
109 
110     def info(self):
111         return {
112             "desktop": self.desktopName(),
113             "mode": self.mode(),
114             "pane_count": self.paneCount(),
115             "pane_tab_types": [t.name() for t in self.paneTabTypes()],
116             "floating_pane_count": len(self.floatingPanels()),
117             "floating_pane_tab_types": [t.name() for t in self.floatingPaneTabTypes()],
118         }
119 
120 
121 def launchFloatingLayout(pane_types=None):
122     return HCLayout().launchFloating(pane_types)
123 
124 
125 """ Legacy free functions (broken — kept for reference) """
126 
127 
128 def setLayoutQuad(self):
129     session = HCSession()
130     session.clearLayout()
131     session.allTabs()[0].setType("PythonShell")
132     session.allPanes()[0].splitHorizontal()
133     session.allPanes()[0].splitVertical()
134     session.allPanes()[1].splitVertical()
135 
136 def setLayoutRamp(self):
137     session = HCSession()
138     session.removeEventLoopCallbacks()
139     session.clearLayout()
140     session.allPanes()[0].splitVertical()
141     session.allTabs()[1].setType("Parm")
142     session.allPanes()[1].setSplitRatio(0.3)
143     session.allPanes()[1].createTab()
144 
145 def setLayoutTriH(self):
146     session = HCSession()
147     session.removeEventLoopCallbacks()
148     session.clearLayout()
149     session.allTabs()[0].setType("PythonShell")
150     session.allPanes()[0].splitHorizontal()
151     session.allPanes()[1].splitHorizontal()
152     session.allPanes()[1].createTab("PythonShell")
153     session.allPanes()[1].allTabs()[0].setIsCurrentTab()
154     session.allPanes()[0].tabs()[0].setType("SceneViewer")
155     session.allPanes()[1].tabs()[0].setType("Parm")
156     session.allPanes()[1].tabs()[1].setType("DetailsView")
157     session.allPanes()[2].tabs()[0].setType("NetworkEditor")
158     session.allPanes()[0].setSplitFraction(0.5)
159     session.prev_pane = self.pane()
160     hou.ui.addEventLoopCallback(triHCallback)
161 
162 def triHCallback(session):
163     panes = session.panes()
164     pane = session.pane()
165     if str(pane) != str(hou.session.lastPane):
166         hou.session.last_pane = pane
167         if str(pane) == str(panes[1]):
168             pane.setSplitFraction(0.6)
169         elif str(pane) == str(panes[2]):
170             pane.setSplitFraction(0.3)
171     return True
172 
173 def setLayoutTriV(session):
174     session.removeEventLoopCallbacks()
175     session.clearLayout()
176     session.allPanes()[0].tabs()[0].setType("PythonShell")
177     session.allPanes()[0].splitHorizontal()
178     session.allPanes()[1].splitVertical()
179     session.allPanes()[1].createTab("PythonShell")
180     session.allPanes()[1].allTabs()[0].setIsCurrentTab()
181     session.allPanes()[0].tabs()[0].setType("SceneViewer")
182     session.allPanes()[1].tabs()[0].setType("Parm")
183     session.allPanes()[1].tabs()[1].setType("DetailsView")
184     session.allPanes()[2].tabs()[0].setType("NetworkEditor")
185     session.allPanes()[0].setSplitFraction(0.66)
186     hou.session.last_pane = self.pane()
187     hou.ui.addEventLoopCallback(self.triVCallback)
188 
189 def triVCallback(session):
190     panes = session.allPanes()
191     pane = session.currentPane()
192     if str(pane) != str(hou.session.last_pane):
193         hou.session.last_pane = pane
194         if str(pane) == str(panes[0]):
195             pane.setSplitFraction(0.7)
196         elif str(pane) == str(panes[1]):
197             panes[0].setSplitFraction(0.4)
198             pane.setSplitFraction(0.33)
199         elif str(pane) == str(panes[2]):
200             panes[0].setSplitFraction(0.4)
201             pane.setSplitFraction(0.66)
202     return True