@@ -80,8 +80,6 @@ def run(self):
8080class BCASLStudioWindow (QMainWindow ):
8181 def __init__ (self ):
8282 super ().__init__ ()
83- self .setWindowTitle ("BCASL Studio (Standalone)" )
84- self .resize (1400 , 900 )
8583 self .workspace : Optional [Path ] = None
8684 self .plugins_dir : Optional [Path ] = None
8785 self .cfg = None
@@ -90,69 +88,95 @@ def __init__(self):
9088
9189 self ._load_ui_from_designer ()
9290 self ._wire_menu_from_ui ()
93- self ._normalize_tab_positions ()
9491 self ._wire_logic ()
9592
9693 # ---------- UI loading from Designer ----------
9794 def _load_ui_from_designer (self ):
95+ # Load the .ui into a temporary QMainWindow, then copy its full state/layout into this subclass instance.
9896 loader = QUiLoader ()
9997 ui_path = Path (__file__ ).resolve ().parents [1 ] / "ui" / "studio.ui"
10098 f = QFile (str (ui_path ))
10199 if not f .open (QFile .ReadOnly ):
102100 raise RuntimeError (f"Cannot open UI file: { ui_path } " )
103- # Load without parenting to avoid premature deletions; keep strong ref
104- w = loader .load (f )
105- f .close ()
106- if w is None :
107- raise RuntimeError ("Failed to load UI" )
108- self ._ui_loaded = w # keep strong reference to prevent GC/deletion
109- # Adopt menubar/statusbar/central widget and docks
110101 try :
111- from PySide6 .QtWidgets import QMenuBar , QStatusBar
112- mb = w .findChild (QMenuBar , "menubar" )
113- if mb :
114- self .setMenuBar (mb )
115- sb = w .findChild (QStatusBar , "statusbar" )
116- if sb :
117- self .setStatusBar (sb )
118- except Exception :
119- pass
120- cw = w .findChild (QWidget , "centralwidget" )
121- if cw :
122- self .setCentralWidget (cw )
123- # Docks by name (reparent into this main window)
124- for name in ("dock_workspace" , "dock_plugins" , "dock_settings" , "dock_log" ):
102+ temp = loader .load (f )
103+ if temp is None :
104+ raise RuntimeError ("Failed to load UI" )
105+ finally :
106+ f .close ()
107+ # temp is a QMainWindow instance created from the .ui
108+ try :
109+ # Apply geometry and top-level properties
125110 try :
126- dw = w .findChild (QDockWidget , name )
127- if dw :
128- area = Qt .LeftDockWidgetArea if name in ("dock_workspace" , "dock_plugins" ) else (Qt .RightDockWidgetArea if name == "dock_settings" else Qt .BottomDockWidgetArea )
129- self .addDockWidget (area , dw )
111+ self .setObjectName (temp .objectName ())
130112 except Exception :
131- continue
132- # Expose widgets by attribute for logic (from loaded root)
133- self .ws_path = w .findChild (QLineEdit , "ws_path" )
134- self .ws_browse = w .findChild (QPushButton , "ws_browse" )
135- self .plugins_path = w .findChild (QLineEdit , "plugins_path" )
136- self .plugins_browse = w .findChild (QPushButton , "plugins_browse" )
137- self .btn_load_ws = w .findChild (QPushButton , "btn_load_ws" )
138- self .btn_save_cfg = w .findChild (QPushButton , "btn_save_cfg" )
139- self .btn_run = w .findChild (QPushButton , "btn_run" )
140- self .filter_text = w .findChild (QLineEdit , "filter_text" )
141- self .filter_phase = w .findChild (QComboBox , "filter_phase" )
142- self .list_plugins = w .findChild (QListWidget , "list_plugins" )
143- self .sp_timeout2 = w .findChild (QSpinBox , "sp_timeout2" )
144- self .chk_sandbox2 = w .findChild (QCheckBox , "chk_sandbox2" )
145- self .sp_parallel2 = w .findChild (QSpinBox , "sp_parallel2" )
146- self .sp_timeout = w .findChild (QSpinBox , "sp_timeout" )
147- self .chk_sandbox = w .findChild (QCheckBox , "chk_sandbox" )
148- self .sp_parallel = w .findChild (QSpinBox , "sp_parallel" )
149- self .txt_log = w .findChild (QPlainTextEdit , "txt_log" )
113+ pass
114+ try :
115+ self .setWindowTitle (temp .windowTitle ())
116+ except Exception :
117+ pass
118+ try :
119+ self .setGeometry (temp .geometry ())
120+ except Exception :
121+ pass
122+ # Menubar/Statusbar
123+ try :
124+ from PySide6 .QtWidgets import QMenuBar , QStatusBar
125+ mb = temp .findChild (QMenuBar )
126+ if mb :
127+ self .setMenuBar (mb )
128+ sb = temp .findChild (QStatusBar )
129+ if sb :
130+ self .setStatusBar (sb )
131+ except Exception :
132+ pass
133+ # Central widget
134+ cw = temp .centralWidget ()
135+ if cw :
136+ self .setCentralWidget (cw )
137+ # Docks: transfer all docks and restore original state to preserve positions
138+ docks = temp .findChildren (QDockWidget )
139+ for dw in docks :
140+ # Add to some area first; we'll restore precise layout below
141+ try :
142+ self .addDockWidget (Qt .LeftDockWidgetArea , dw )
143+ except Exception :
144+ pass
145+ try :
146+ state = temp .saveState ()
147+ self .restoreState (state )
148+ except Exception :
149+ pass
150+ finally :
151+ try :
152+ temp .deleteLater ()
153+ except Exception :
154+ pass
155+ # Expose commonly used widgets by name
156+ self ._ui_loaded = self
157+ self .ws_path = self .findChild (QLineEdit , "ws_path" )
158+ self .ws_browse = self .findChild (QPushButton , "ws_browse" )
159+ self .plugins_path = self .findChild (QLineEdit , "plugins_path" )
160+ self .plugins_browse = self .findChild (QPushButton , "plugins_browse" )
161+ self .btn_load_ws = self .findChild (QPushButton , "btn_load_ws" )
162+ self .btn_save_cfg = self .findChild (QPushButton , "btn_save_cfg" )
163+ self .btn_run = self .findChild (QPushButton , "btn_run" )
164+ self .filter_text = self .findChild (QLineEdit , "filter_text" )
165+ self .filter_phase = self .findChild (QComboBox , "filter_phase" )
166+ self .list_plugins = self .findChild (QListWidget , "list_plugins" )
167+ self .sp_timeout2 = self .findChild (QSpinBox , "sp_timeout2" )
168+ self .chk_sandbox2 = self .findChild (QCheckBox , "chk_sandbox2" )
169+ self .sp_parallel2 = self .findChild (QSpinBox , "sp_parallel2" )
170+ self .sp_timeout = self .findChild (QSpinBox , "sp_timeout" )
171+ self .chk_sandbox = self .findChild (QCheckBox , "chk_sandbox" )
172+ self .sp_parallel = self .findChild (QSpinBox , "sp_parallel" )
173+ self .txt_log = self .findChild (QPlainTextEdit , "txt_log" )
150174 # Menus/actions
151- self .actOpenWorkspace = w .findChild (QAction , "actOpenWorkspace" )
152- self .actSelectPluginsDir = w .findChild (QAction , "actSelectPluginsDir" )
153- self .actSaveConfig = w .findChild (QAction , "actSaveConfig" )
154- self .actRun = w .findChild (QAction , "actRun" )
155- self .menuTheme = w .findChild (QMenu , "menuTheme" )
175+ self .actOpenWorkspace = self .findChild (QAction , "actOpenWorkspace" )
176+ self .actSelectPluginsDir = self .findChild (QAction , "actSelectPluginsDir" )
177+ self .actSaveConfig = self .findChild (QAction , "actSaveConfig" )
178+ self .actRun = self .findChild (QAction , "actRun" )
179+ self .menuTheme = self .findChild (QMenu , "menuTheme" )
156180
157181 def _wire_menu_from_ui (self ):
158182 # Connect actions
@@ -166,7 +190,15 @@ def _wire_menu_from_ui(self):
166190 self .actRun .triggered .connect (self ._run )
167191 # Populate theme menu
168192 self .m_theme = self .menuTheme
169- self ._populate_theme_menu ()
193+ if not self .m_theme :
194+ try :
195+ mb = self .menuBar () if hasattr (self , 'menuBar' ) else None
196+ if mb :
197+ self .m_theme = mb .findChild (QMenu , "menuTheme" )
198+ except Exception :
199+ self .m_theme = None
200+ if self .m_theme :
201+ self ._populate_theme_menu ()
170202
171203 def _wire_logic (self ):
172204 # Buttons (defensive: only connect when widgets are present)
@@ -228,6 +260,8 @@ def _apply_theme_file(self, path: Path):
228260
229261 def _populate_theme_menu (self ):
230262 # Scan themes/ at repo root
263+ if not getattr (self , "m_theme" , None ):
264+ return
231265 self .m_theme .clear ()
232266 try :
233267 root = Path (__file__ ).resolve ().parents [3 ]
@@ -240,8 +274,11 @@ def _populate_theme_menu(self):
240274 act = self .m_theme .addAction (qss .name )
241275 act .triggered .connect (lambda _ = False , p = qss : self ._apply_theme_file (p ))
242276 except Exception :
243- act = self .m_theme .addAction ("Themes load error" )
244- act .setEnabled (False )
277+ try :
278+ act = self .m_theme .addAction ("Themes load error" )
279+ act .setEnabled (False )
280+ except Exception :
281+ pass
245282
246283 # ---------- Workspace and Plugins ----------
247284 def _browse_ws (self ):
0 commit comments