@@ -172,7 +172,7 @@ class ProcessingReport:
172172 message : str | None = None
173173
174174
175- class ObjectProp (QW .QTabWidget ):
175+ class ObjectProp (QW .QWidget ):
176176 """Object handling panel properties
177177
178178 Args:
@@ -182,8 +182,16 @@ class ObjectProp(QW.QTabWidget):
182182
183183 def __init__ (self , panel : BaseDataPanel , objclass : SignalObj | ImageObj ) -> None :
184184 super ().__init__ (panel )
185- self .setTabBarAutoHide (True )
186- self .setTabPosition (QW .QTabWidget .West )
185+
186+ # Create vertical layout for the container
187+ layout = QW .QVBoxLayout (self )
188+ layout .setContentsMargins (0 , 0 , 0 , 0 )
189+ layout .setSpacing (0 )
190+
191+ # Create the tab widget
192+ self .tabwidget = QW .QTabWidget (self )
193+ self .tabwidget .setTabBarAutoHide (True )
194+ self .tabwidget .setTabPosition (QW .QTabWidget .West )
187195
188196 self .panel = panel
189197 self .objclass = objclass
@@ -204,12 +212,6 @@ def __init__(self, panel: BaseDataPanel, objclass: SignalObj | ImageObj) -> None
204212 self .properties .setEnabled (False )
205213 self .__original_values : dict [str , Any ] = {}
206214
207- self .add_prop_layout = QW .QHBoxLayout ()
208- playout : QW .QGridLayout = self .properties .edit .layout
209- playout .addLayout (
210- self .add_prop_layout , playout .rowCount () - 1 , 0 , 1 , 1 , QC .Qt .AlignLeft
211- )
212-
213215 # Create Analysis and History widgets
214216 font = Conf .proc .small_mono_font .get_font ()
215217
@@ -221,25 +223,42 @@ def __init__(self, panel: BaseDataPanel, objclass: SignalObj | ImageObj) -> None
221223 self .analysis_parameters .setReadOnly (True )
222224 self .analysis_parameters .setFont (font )
223225
224- self .addTab (self .processing_history , get_icon ("history.svg" ), _ ("History" ))
225- self .addTab (
226+ self .tabwidget .addTab (
227+ self .processing_history , get_icon ("history.svg" ), _ ("History" )
228+ )
229+ self .tabwidget .addTab (
226230 self .analysis_parameters , get_icon ("analysis.svg" ), _ ("Analysis parameters" )
227231 )
228- self .addTab (self .properties , get_icon ("properties.svg" ), _ ("Properties" ))
232+ self .tabwidget .addTab (
233+ self .properties , get_icon ("properties.svg" ), _ ("Properties" )
234+ )
229235
230236 self .processing_history .textChanged .connect (self ._update_tab_visibility )
231237 self .analysis_parameters .textChanged .connect (self ._update_tab_visibility )
232238
239+ # Create a permanent button area at the bottom, always visible regardless of tab
240+ self .add_prop_layout = QW .QHBoxLayout ()
241+ self .add_prop_layout .setContentsMargins (5 , 5 , 5 , 5 )
242+ self .add_prop_layout .setSpacing (10 )
243+ self .add_prop_layout .addStretch ()
244+
245+ # Add tab widget and button area to main layout
246+ layout .addWidget (self .tabwidget )
247+ layout .addLayout (self .add_prop_layout )
248+
233249 def _update_tab_visibility (self ) -> None :
234250 """Update visibility of tabs based on their content."""
235251 for textedit in (self .processing_history , self .analysis_parameters ):
236- tab_index = self .indexOf (textedit )
252+ tab_index = self .tabwidget . indexOf (textedit )
237253 if tab_index >= 0 :
238254 has_content = bool (textedit .toPlainText ().strip ())
239- self .setTabVisible (tab_index , has_content )
255+ self .tabwidget . setTabVisible (tab_index , has_content )
240256
241257 def add_button (self , button : QW .QPushButton ) -> None :
242- """Add additional button on bottom of properties panel"""
258+ """Add additional button to the permanent action bar.
259+
260+ Buttons added here are always visible regardless of which tab is active.
261+ """
243262 self .add_prop_layout .addWidget (button )
244263
245264 def display_analysis_parameters (self , obj : SignalObj | ImageObj ) -> bool :
@@ -380,13 +399,13 @@ def update_properties_from(self, obj: SignalObj | ImageObj | None = None) -> Non
380399 # Remove only Creation and Processing tabs (dynamic tabs)
381400 # Use widget references instead of text labels for reliable identification
382401 if self .creation_scroll is not None :
383- index = self .indexOf (self .creation_scroll )
402+ index = self .tabwidget . indexOf (self .creation_scroll )
384403 if index >= 0 :
385- self .removeTab (index )
404+ self .tabwidget . removeTab (index )
386405 if self .processing_scroll is not None :
387- index = self .indexOf (self .processing_scroll )
406+ index = self .tabwidget . indexOf (self .processing_scroll )
388407 if index >= 0 :
389- self .removeTab (index )
408+ self .tabwidget . removeTab (index )
390409
391410 # Reset references for dynamic tabs
392411 self .creation_param_editor = None
@@ -413,13 +432,13 @@ def update_properties_from(self, obj: SignalObj | ImageObj | None = None) -> Non
413432 # 3. Processing tab if it exists
414433 # 4. Properties tab
415434 if has_analysis_parameters :
416- self .setCurrentWidget (self .analysis_parameters )
435+ self .tabwidget . setCurrentWidget (self .analysis_parameters )
417436 elif has_creation_tab :
418- self .setCurrentWidget (self .creation_scroll )
437+ self .tabwidget . setCurrentWidget (self .creation_scroll )
419438 elif has_processing_tab :
420- self .setCurrentWidget (self .processing_scroll )
439+ self .tabwidget . setCurrentWidget (self .processing_scroll )
421440 else :
422- self .setCurrentWidget (self .properties )
441+ self .tabwidget . setCurrentWidget (self .properties )
423442
424443 def get_changed_properties (self ) -> dict [str , Any ]:
425444 """Get dictionary of properties that have changed from original values.
@@ -504,21 +523,23 @@ def setup_creation_tab(
504523
505524 # Remove existing Creation tab if it exists
506525 if self .creation_scroll is not None :
507- index = self .indexOf (self .creation_scroll )
526+ index = self .tabwidget . indexOf (self .creation_scroll )
508527 if index >= 0 :
509- self .removeTab (index )
528+ self .tabwidget . removeTab (index )
510529
511530 # Set the parameter editor as the scroll area widget
512531 # Creation tab is always at index 0 (before all other tabs)
513532 self .creation_scroll = QW .QScrollArea ()
514533 self .creation_scroll .setWidgetResizable (True )
515534 self .creation_scroll .setWidget (editor )
516535 icon_name = "new_sig.svg" if isinstance (obj , SignalObj ) else "new_ima.svg"
517- self .insertTab (0 , self .creation_scroll , get_icon (icon_name ), _ ("Creation" ))
536+ self .tabwidget .insertTab (
537+ 0 , self .creation_scroll , get_icon (icon_name ), _ ("Creation" )
538+ )
518539
519540 # Set as current tab if requested
520541 if set_current :
521- self .setCurrentWidget (self .creation_scroll )
542+ self .tabwidget . setCurrentWidget (self .creation_scroll )
522543
523544 return True
524545
@@ -654,15 +675,16 @@ def setup_processing_tab(
654675
655676 # Remove existing Processing tab if it exists
656677 if self .processing_scroll is not None :
657- index = self .indexOf (self .processing_scroll )
678+ index = self .tabwidget . indexOf (self .processing_scroll )
658679 if index >= 0 :
659- self .removeTab (index )
680+ self .tabwidget . removeTab (index )
660681
661682 # Processing tab comes after Creation tab (if it exists)
662683 # Find the correct insertion index: after Creation (index 0) if it exists,
663684 # otherwise at index 0
664685 has_creation = (
665- self .creation_scroll is not None and self .indexOf (self .creation_scroll ) >= 0
686+ self .creation_scroll is not None
687+ and self .tabwidget .indexOf (self .creation_scroll ) >= 0
666688 )
667689 insert_index = 1 if has_creation else 0
668690
@@ -675,7 +697,7 @@ def setup_processing_tab(
675697 )
676698
677699 self .processing_scroll .setWidget (editor )
678- self .insertTab (
700+ self .tabwidget . insertTab (
679701 insert_index ,
680702 self .processing_scroll ,
681703 get_icon ("libre-tech-ram.svg" ),
@@ -684,7 +706,7 @@ def setup_processing_tab(
684706
685707 # Set as current tab if requested
686708 if set_current :
687- self .setCurrentWidget (self .processing_scroll )
709+ self .tabwidget . setCurrentWidget (self .processing_scroll )
688710
689711 return True
690712
0 commit comments