2424from python_toolkit .bhom_tkinter .widgets ._widgets_base import BHoMBaseWidget
2525from python_toolkit .bhom_tkinter .widgets .button import Button
2626import python_toolkit
27- from theming .theme import ThemeManager
27+ from python_toolkit . bhom_tkinter . theming .theme import ThemeManager
2828
2929class BHoMBaseWindow (tk .Tk ):
3030 """
@@ -44,13 +44,15 @@ def __init__(
4444 show_submit : bool = True ,
4545 submit_text : str = "Submit" ,
4646 submit_command : Optional [Callable ] = None ,
47+ close_on_submit : bool = True ,
4748 show_close : bool = True ,
4849 close_text : str = "Close" ,
4950 close_command : Optional [Callable ] = None ,
5051 on_close_window : Optional [Callable ] = None ,
5152 theme_mode :str = "auto" ,
52- widgets : List [BHoMBaseWidget ] = [] ,
53+ widgets : Optional [ List [BHoMBaseWidget ]] = None ,
5354 top_most : bool = True ,
55+ fullscreen : bool = False ,
5456 buttons_side : Literal ["left" , "right" ] = "right" ,
5557 grid_dimensions : Optional [tuple [int , int ]] = None ,
5658 ** kwargs
@@ -77,6 +79,7 @@ def __init__(
7779 on_close_window (callable, optional): Command when X is pressed.
7880 theme_path (Path, optional): Path to custom TCL theme file. If None, uses default style.tcl.
7981 theme_mode (str): Theme mode - "light", "dark", or "auto" to detect from system (default: "auto").
82+ fullscreen (bool): Whether the window starts in fullscreen mode (default: False).
8083 buttons_side (str): Side for buttons - "left" or "right" (default: "right").
8184 grid_dimensions (tuple[int, int], optional): If provided, configures content area with specified rows and columns for grid layout.
8285 **kwargs
@@ -91,7 +94,10 @@ def __init__(
9194 if self .top_most :
9295 self .attributes ("-topmost" , True )
9396
94- self .widgets = widgets
97+ self .fullscreen = fullscreen
98+
99+ # Avoid sharing widget instances across windows/runs.
100+ self .widgets = list (widgets ) if widgets is not None else []
95101
96102 # Hide window during setup to prevent flash
97103 self .withdraw ()
@@ -107,6 +113,7 @@ def __init__(
107113 self .fixed_height = height
108114 self .center_on_screen = center_on_screen
109115 self .submit_command = submit_command
116+ self .close_on_submit = close_on_submit
110117 self .close_command = close_command
111118 self .result = None
112119 self ._is_exiting = False
@@ -120,6 +127,7 @@ def __init__(
120127 self ._auto_fit_height = height is None
121128 self ._post_show_size_applied = False
122129 self .grid_dimensions = grid_dimensions
130+ self ._cached_widget_values : dict [str , object ] = {}
123131
124132 # Handle window close (X button)
125133 self .protocol ("WM_DELETE_WINDOW" , lambda : self ._on_close_window (on_close_window ))
@@ -337,9 +345,13 @@ def _build_banner(self, parent: ttk.Frame, title: str, logo_path: Optional[Path]
337345 from PIL import Image , ImageTk
338346 img = Image .open (logo_path )
339347 img .thumbnail ((80 , 80 ), Image .Resampling .LANCZOS )
340- self .logo_image = ImageTk .PhotoImage (img )
348+ # Bind image to this root explicitly to avoid stale image handles
349+ # when previous runs failed and tore down a different Tk interpreter.
350+ self .logo_image = ImageTk .PhotoImage (img , master = self )
341351 logo_label = Label (logo_container , image = self .logo_image )
342352 logo_label .pack (fill = tk .BOTH , expand = True )
353+ except tk .TclError :
354+ pass
343355 except ImportError :
344356 pass # PIL not available, skip logo
345357
@@ -459,6 +471,13 @@ def _apply_sizing(self) -> None:
459471 else :
460472 final_height = max (self .min_height , required_height )
461473
474+ # Fullscreen overrides normal sizing/positioning
475+ if self .fullscreen :
476+ self .attributes ("-fullscreen" , True )
477+ self .after (0 , self ._show_window_with_styling )
478+ self ._is_resizing = False
479+ return
480+
462481 # Position
463482 if self .center_on_screen and not self ._has_been_shown :
464483 screen_width = self .winfo_screenwidth ()
@@ -567,11 +586,30 @@ def _exit(self, result: str, callback: Optional[Callable] = None) -> None:
567586 except Exception as ex :
568587 print (f"Warning: Exit callback raised an exception: { ex } " )
569588 finally :
589+ # Capture values while widgets still exist so `get()` remains usable
590+ # after root teardown.
591+ self ._cached_widget_values = self ._collect_widget_values ()
570592 self .destroy_root ()
571593
572594 def _on_submit (self ) -> None :
573595 """Handle submit button click."""
574- self ._exit ("submit" , self .submit_command )
596+ if self .close_on_submit :
597+ self ._exit ("submit" , self .submit_command )
598+ return
599+
600+ self .result = "submit"
601+ try :
602+ if self .submit_command :
603+ self .submit_command ()
604+ except tk .TclError as ex :
605+ message = str (ex ).lower ()
606+ if not ("image" in message and "doesn't exist" in message ):
607+ print (f"Warning: Exit callback raised an exception: { ex } " )
608+ except Exception as ex :
609+ print (f"Warning: Exit callback raised an exception: { ex } " )
610+ finally :
611+ self ._cached_widget_values = self ._collect_widget_values ()
612+
575613
576614 def _on_close (self ) -> None :
577615 """Handle close button click."""
@@ -581,6 +619,30 @@ def _on_close_window(self, callback: Optional[Callable]) -> None:
581619 """Handle window X button click."""
582620 self ._exit ("window_closed" , callback )
583621
622+ def get (self ):
623+ try :
624+ if not self .winfo_exists ():
625+ return dict (self ._cached_widget_values )
626+ except Exception :
627+ return dict (self ._cached_widget_values )
628+
629+ widget_values = self ._collect_widget_values ()
630+ self ._cached_widget_values = dict (widget_values )
631+ return widget_values
632+
633+ def _collect_widget_values (self ) -> dict [str , object ]:
634+ """Collect values from all registered widgets."""
635+ widget_values : dict [str , object ] = {}
636+
637+ for widget in self .widgets :
638+
639+ if hasattr (widget , "get" ):
640+ try :
641+ widget_values [widget .id ] = widget .get ()
642+ except Exception as ex :
643+ print (f"Warning: Failed to get value from widget { widget } : { ex } " )
644+ return widget_values
645+
584646
585647if __name__ == "__main__" :
586648
@@ -591,11 +653,12 @@ def _on_close_window(self, callback: Optional[Callable]) -> None:
591653
592654 test = BHoMBaseWindow (
593655 title = "Test Window" ,
594- theme_mode = "auto " ,
656+ theme_mode = "light " ,
595657 )
596658
597659 test .widgets .append (Label (test .content_frame , text = "Hello, World!" ))
598660 test .widgets .append (Button (test .content_frame , text = "Click Me" , command = lambda : print ("Button Clicked!" ), helper_text = "This is a button." , item_title = "Button Widget Title" ))
599661
600662 test .build ()
601663 test .mainloop ()
664+ print (test .get ())
0 commit comments