Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions Python_Engine/Python/src/python_toolkit/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,3 @@
if os.name == "nt":
# override "HOME" in case this is set to something other than default for windows
os.environ["HOME"] = (Path("C:/Users/") / getpass.getuser()).as_posix()


# set plotting style for modules within this toolkit
plt.style.use(BHOM_DIRECTORY / "bhom.mplstyle")
88 changes: 88 additions & 0 deletions Python_Engine/Python/src/python_toolkit/bhom_dark.mplstyle
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Default matplotlib settings for this toolkit.

# Text
text.color: white

# Set custom colors. All colors are in web style hex format.
axes.prop_cycle: cycler('color', ['702F8A', 'E63187', '00A9E0', 'FFCF04', '6CC24E', 'EB671C', '00A499', 'D50032', '24135F', '6D104E', '006DA8', 'D06A13', '5D822D', 'F0AC1B', '1C3660', 'BC204B', '8F72B0', 'FCD16D', '8DB9CA', 'EE7837', 'AFC1A2', 'B72B77', 'A0D2C9', 'E6484D'])

# Face settings
axes.facecolor: black
axes.edgecolor: white

# Style spines
axes.linewidth: 0.8
axes.spines.top: False
axes.spines.left: True
axes.spines.right: False
axes.spines.bottom: True

# Set line styling for line plots
lines.linewidth: 1
lines.solid_capstyle: round
lines.dash_capstyle: round

# Grid style
axes.axisbelow: True
axes.grid: true
axes.grid.axis: both
grid.color: 958B82
grid.linestyle: --
grid.linewidth: 0.5

# Setting font sizes and spacing
axes.labelsize: medium
axes.labelweight: semibold
axes.labelcolor: white
axes.ymargin: 0.1
font.family: sans-serif
font.sans-serif: Segoe UI
font.size: 10
xtick.labelsize: medium
xtick.labelcolor: white
xtick.major.pad: 3.5
ytick.labelsize: medium
ytick.labelcolor: white
ytick.major.pad: 3.5

# date formatter
date.autoformatter.day: %b-%d
date.autoformatter.hour: %b-%d %H
date.autoformatter.microsecond: %M:%S.%f
date.autoformatter.minute: %d %H:%M
date.autoformatter.month: %b
date.autoformatter.second: %H:%M:%S
date.autoformatter.year: %Y

# Title
axes.titlelocation: left
axes.titlepad: 6
axes.titlesize: large
axes.titleweight: bold

# Remove major and minor ticks except for on the x-axis.
xtick.color: white
xtick.major.size: 3
xtick.minor.size: 2
ytick.color: white
ytick.major.size: 3
ytick.minor.size: 2

# Set spacing for figure and also DPI.
figure.subplot.left: 0.08
figure.subplot.right: 0.95
figure.subplot.bottom: 0.07
figure.figsize: 12, 5
figure.dpi: 150
figure.facecolor: black

# Properties for saving the figure. Ensure a high DPI when saving so we have a good resolution.
savefig.dpi: 300
savefig.facecolor: black
savefig.bbox: tight
savefig.pad_inches: 0.2

# Legend Styling
legend.framealpha: 0
legend.frameon: False
legend.facecolor: inherit
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@
WarningBox,
)

from .theming import (
TclTheme,
ThemeManager,
LIGHT,
DARK,
)

__all__ = [
"BHoMBaseWidget",
"PackingOptions",
Expand All @@ -38,4 +45,6 @@
"LandingPage",
"ProcessingWindow",
"WarningBox",
"TclTheme",
"ThemeManager"
]
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from python_toolkit.bhom_tkinter.widgets._widgets_base import BHoMBaseWidget
from python_toolkit.bhom_tkinter.widgets.button import Button
import python_toolkit
from theming.theme import ThemeManager
from python_toolkit.bhom_tkinter.theming.theme import ThemeManager

class BHoMBaseWindow(tk.Tk):
"""
Expand All @@ -49,8 +49,9 @@ def __init__(
close_command: Optional[Callable] = None,
on_close_window: Optional[Callable] = None,
theme_mode:str = "auto",
widgets: List[BHoMBaseWidget] = [],
widgets: Optional[List[BHoMBaseWidget]] = None,
top_most: bool = True,
fullscreen: bool = False,
buttons_side: Literal["left", "right"] = "right",
grid_dimensions: Optional[tuple[int, int]] = None,
**kwargs
Expand All @@ -77,6 +78,7 @@ def __init__(
on_close_window (callable, optional): Command when X is pressed.
theme_path (Path, optional): Path to custom TCL theme file. If None, uses default style.tcl.
theme_mode (str): Theme mode - "light", "dark", or "auto" to detect from system (default: "auto").
fullscreen (bool): Whether the window starts in fullscreen mode (default: False).
buttons_side (str): Side for buttons - "left" or "right" (default: "right").
grid_dimensions (tuple[int, int], optional): If provided, configures content area with specified rows and columns for grid layout.
**kwargs
Expand All @@ -91,7 +93,10 @@ def __init__(
if self.top_most:
self.attributes("-topmost", True)

self.widgets = widgets
self.fullscreen = fullscreen

# Avoid sharing widget instances across windows/runs.
self.widgets = list(widgets) if widgets is not None else []

# Hide window during setup to prevent flash
self.withdraw()
Expand Down Expand Up @@ -120,6 +125,7 @@ def __init__(
self._auto_fit_height = height is None
self._post_show_size_applied = False
self.grid_dimensions = grid_dimensions
self._cached_widget_values: dict[str, object] = {}

# Handle window close (X button)
self.protocol("WM_DELETE_WINDOW", lambda: self._on_close_window(on_close_window))
Expand Down Expand Up @@ -337,9 +343,13 @@ def _build_banner(self, parent: ttk.Frame, title: str, logo_path: Optional[Path]
from PIL import Image, ImageTk
img = Image.open(logo_path)
img.thumbnail((80, 80), Image.Resampling.LANCZOS)
self.logo_image = ImageTk.PhotoImage(img)
# Bind image to this root explicitly to avoid stale image handles
# when previous runs failed and tore down a different Tk interpreter.
self.logo_image = ImageTk.PhotoImage(img, master=self)
logo_label = Label(logo_container, image=self.logo_image)
logo_label.pack(fill=tk.BOTH, expand=True)
except tk.TclError:
pass
except ImportError:
pass # PIL not available, skip logo

Expand Down Expand Up @@ -459,6 +469,13 @@ def _apply_sizing(self) -> None:
else:
final_height = max(self.min_height, required_height)

# Fullscreen overrides normal sizing/positioning
if self.fullscreen:
self.attributes("-fullscreen", True)
self.after(0, self._show_window_with_styling)
self._is_resizing = False
return

# Position
if self.center_on_screen and not self._has_been_shown:
screen_width = self.winfo_screenwidth()
Expand Down Expand Up @@ -567,6 +584,9 @@ def _exit(self, result: str, callback: Optional[Callable] = None) -> None:
except Exception as ex:
print(f"Warning: Exit callback raised an exception: {ex}")
finally:
# Capture values while widgets still exist so `get()` remains usable
# after root teardown.
self._cached_widget_values = self._collect_widget_values()
self.destroy_root()

def _on_submit(self) -> None:
Expand All @@ -581,6 +601,30 @@ def _on_close_window(self, callback: Optional[Callable]) -> None:
"""Handle window X button click."""
self._exit("window_closed", callback)

def get(self):
try:
if not self.winfo_exists():
return dict(self._cached_widget_values)
except Exception:
return dict(self._cached_widget_values)

widget_values = self._collect_widget_values()
self._cached_widget_values = dict(widget_values)
return widget_values

def _collect_widget_values(self) -> dict[str, object]:
"""Collect values from all registered widgets."""
widget_values: dict[str, object] = {}

for widget in self.widgets:

if hasattr(widget, "get"):
try:
widget_values[widget.id] = widget.get()
except Exception as ex:
print(f"Warning: Failed to get value from widget {widget}: {ex}")
return widget_values


if __name__ == "__main__":

Expand All @@ -599,3 +643,4 @@ def _on_close_window(self, callback: Optional[Callable]) -> None:

test.build()
test.mainloop()
print(test.get())
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .theme import TclTheme, ThemeManager, LIGHT, DARK
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,41 @@ namespace eval ttk::theme::bhom_dark {
active $colors(-hover-bg) \
disabled $colors(-disabled-bg)]

# Radiobutton - sleek hover effect with bold font
# Larger checkbutton variant used by CheckboxSelection widget
ttk::style layout Checkbox.TCheckbutton {
Checkbutton.padding -sticky nswe -children {
Checkbutton.indicator -side left -sticky {}
Checkbutton.label -side left -sticky w
}
}

ttk::style configure Checkbox.TCheckbutton \
-font {{Segoe UI} 11} \
-padding {6 8} \
-indicatormargin {0 0 10 0} \
-indicatorrelief flat \
-indicatorsize 18 \
-borderwidth 0 \
-relief flat \
-focusthickness 0 \
-indicatorcolor $colors(-inputbg) \
-indicatorbackground $colors(-inputbg)

ttk::style map Checkbox.TCheckbutton \
-background [list \
active $colors(-bg) \
selected $colors(-bg)] \
-foreground [list \
active $colors(-primary) \
disabled $colors(-disabled-fg)] \
-indicatorbackground [list \
selected $colors(-primary) \
active $colors(-inputbg) \
disabled $colors(-disabled-bg)] \
-indicatorcolor [list \
selected $colors(-primary) \
active $colors(-inputbg) \
disabled $colors(-disabled-bg)]
ttk::style configure TRadiobutton \
-background $colors(-bg) \
-foreground $colors(-fg) \
Expand All @@ -410,6 +444,51 @@ namespace eval ttk::theme::bhom_dark {
active $colors(-hover-bg) \
disabled $colors(-disabled-bg)]

# Larger radiobutton variant used by RadioSelection widget
ttk::style layout Radio.TRadiobutton {
Radiobutton.padding -sticky nswe -children {
Radiobutton.indicator -side left -sticky {}
Radiobutton.label -side left -sticky w
}
}

ttk::style configure Radio.TRadiobutton \
-font {{Segoe UI} 11} \
-padding {6 8} \
-indicatormargin {0 0 10 0} \
-indicatorsize 15 \
-borderwidth 0 \
-relief flat \
-focusthickness 0 \
-indicatorbackground $colors(-inputbg) \
-indicatorforeground $colors(-inputbg) \
-upperbordercolor $colors(-border) \
-lowerbordercolor $colors(-border)

ttk::style map Radio.TRadiobutton \
-background [list \
active $colors(-bg) \
selected $colors(-bg)] \
-foreground [list \
active $colors(-primary) \
disabled $colors(-disabled-fg)] \
-indicatorbackground [list \
selected $colors(-primary) \
active $colors(-inputbg) \
disabled $colors(-disabled-bg)] \
-indicatorforeground [list \
selected $colors(-primary) \
active $colors(-inputbg) \
disabled $colors(-disabled-bg)] \
-upperbordercolor [list \
selected $colors(-primary) \
active $colors(-border) \
disabled $colors(-disabled-bg)] \
-lowerbordercolor [list \
selected $colors(-primary) \
active $colors(-border) \
disabled $colors(-disabled-bg)]

# Scrollbar - minimal sleek design without arrows
ttk::style configure TScrollbar \
-background $colors(-border) \
Expand Down
Loading