From ae504d188b643508d18d69215e240744c464606d Mon Sep 17 00:00:00 2001 From: "kujirahand.mac2021.win11" Date: Mon, 20 Apr 2026 16:31:42 +0900 Subject: [PATCH 1/7] feat(theme): improve Windows UI defaults and add theme guide - Change Button use_ttk_buttons default behavior to True on Windows when omitted - Keep explicit use_ttk_buttons=True/False behavior unchanged - Keep Windows default theme as vista and document theme usage/customization - Add docs/theme.md and link it from docs/README.md - Add tests/theme/ttk_button.py example and lint docstring fix --- TkEasyGUI/utils.py | 2 - TkEasyGUI/widgets.py | 5 +- TkEasyGUI/widgets_window.py | 2 + docs/README.md | 1 + docs/theme.md | 107 ++++++++++++++++++++++++++++++++++++ tests/theme/ttk_button.py | 18 ++++++ 6 files changed, 131 insertions(+), 4 deletions(-) create mode 100644 docs/theme.md create mode 100644 tests/theme/ttk_button.py diff --git a/TkEasyGUI/utils.py b/TkEasyGUI/utils.py index c3d6f54..222db8b 100644 --- a/TkEasyGUI/utils.py +++ b/TkEasyGUI/utils.py @@ -259,8 +259,6 @@ def set_default_theme() -> None: set_theme("aqua") elif is_win(): # ('winnative', 'clam', 'alt', 'default', 'classic', 'vista', 'xpnative') - # set_theme("winnative") - # set_theme("default") set_theme("vista") else: set_theme("clam") diff --git a/TkEasyGUI/widgets.py b/TkEasyGUI/widgets.py index e8af16c..9fbecfb 100644 --- a/TkEasyGUI/widgets.py +++ b/TkEasyGUI/widgets.py @@ -2371,14 +2371,15 @@ def __init__( expand_y: bool = False, pad: Optional[PadType] = None, # other - use_ttk_buttons: bool = False, + use_ttk_buttons: Optional[bool] = None, metadata: Optional[dict[str, Any]] = None, **kw, ) -> None: """Create a Button element.""" key = button_text if (key is None) or (key == "") else key super().__init__("Button", "TButton", key, False, metadata, **kw) - self.use_ttk = use_ttk_buttons # can select ttk or tk button + # On Windows, ttk buttons look more modern by default. + self.use_ttk = utils.is_win() if use_ttk_buttons is None else use_ttk_buttons self.disabled = False if disabled is not None: self.props["disabled"] = self.disabled = disabled diff --git a/TkEasyGUI/widgets_window.py b/TkEasyGUI/widgets_window.py index a394971..b21537c 100644 --- a/TkEasyGUI/widgets_window.py +++ b/TkEasyGUI/widgets_window.py @@ -52,6 +52,8 @@ def _create_root(self) -> tk.Tk: self._root.eval("tk::PlaceWindow . center") self._root.withdraw() if self._theme_name == "": + # windows: winnative, clam, alt, default, classic, vista, xpnative + # macOS: aqua, clam, alt, default, classic if _is_mac(): self.set_theme("aqua") elif _is_win(): diff --git a/docs/README.md b/docs/README.md index cf6b7ff..e22e456 100644 --- a/docs/README.md +++ b/docs/README.md @@ -68,6 +68,7 @@ Here is a list of elements: ### TkEasyGUI Original features - [Custom Events](https://github.com/kujirahand/tkeasygui-python/blob/main/docs/custom_events.md) +- [Theme Guide](https://github.com/kujirahand/tkeasygui-python/blob/main/docs/theme.md) ### Package developper diff --git a/docs/theme.md b/docs/theme.md new file mode 100644 index 0000000..9b10380 --- /dev/null +++ b/docs/theme.md @@ -0,0 +1,107 @@ +# TkEasyGUI Theme Guide + +This document explains how to use themes and how to change theme-related defaults in TkEasyGUI. + +## Overview + +TkEasyGUI uses `ttk` themes for modern widget rendering. +You can switch themes at runtime using `set_theme()` or `theme()`. + +Default theme by platform: + +- macOS: `aqua` +- Windows: `vista` +- Linux/others: `clam` + +## Basic theme usage + +### Change theme with `set_theme` + +```python +import TkEasyGUI as eg + +eg.set_theme("clam") +window = eg.Window("Theme sample", [[eg.Button("OK", use_ttk_buttons=True)]]) +window.read() +window.close() +``` + +### Alias: `theme` + +`theme()` is an alias of `set_theme()`. + +```python +import TkEasyGUI as eg + +eg.theme("default") +``` + +## List available themes + +Use `get_tnemes()` to inspect themes available in your current Tk runtime. + +```python +import TkEasyGUI as eg + +print(eg.get_tnemes()) +``` + +Note: +- Function name is `get_tnemes()` in current API. + +## Change button style behavior + +`Button` can render with `ttk.Button` (modern) or `tk.Button` (classic). + +- `use_ttk_buttons=True`: use `ttk.Button` +- `use_ttk_buttons=False`: use `tk.Button` +- `use_ttk_buttons=None` (default): Windows uses `ttk`, other OS keep previous default behavior + +```python +import TkEasyGUI as eg + +layout = [ + [eg.Button("Modern", use_ttk_buttons=True)], + [eg.Button("Classic", use_ttk_buttons=False)], +] +window = eg.Window("Buttons", layout) +window.read() +window.close() +``` + +## How to customize defaults + +### Global theme default + +If you want to change the default theme for your app, call `set_theme()` before creating windows. + +```python +import TkEasyGUI as eg + +eg.set_theme("winnative") +``` + +### Revert to old Windows look + +If you want behavior close to older Windows appearance: + +```python +import TkEasyGUI as eg + +eg.set_theme("vista") +layout = [[eg.Button("OK", use_ttk_buttons=False)]] +window = eg.Window("Legacy look", layout) +window.read() +window.close() +``` + +## Recommended test scripts + +After changing theme defaults, validate with: + +```bash +python tests/file_viewer.py +python tests/theme/ttk_button.py +python tests/ui_test.py +python tests/many_buttons.py +``` diff --git a/tests/theme/ttk_button.py b/tests/theme/ttk_button.py new file mode 100644 index 0000000..c767d80 --- /dev/null +++ b/tests/theme/ttk_button.py @@ -0,0 +1,18 @@ +"""Compare ttk and classic button rendering.""" + +import TkEasyGUI as eg + +layout = [ + [eg.Text("This is a modern button using ttk styles.")], + [eg.Button("Modern", use_ttk_buttons=True)], + [eg.HSeparator()], + [eg.Text("This is a classic button using Tkinter styles.")], + [eg.Button("Classic", use_ttk_buttons=False)], +] +window = eg.Window("Buttons", layout) +for event, values in window.event_iter(): + if event == "Modern": + eg.popup("You clicked the modern button!") + if event == "Classic": + eg.popup("You clicked the classic button!") +window.close() From ccf73a8dfad34cda409e3329e7439949868209c2 Mon Sep 17 00:00:00 2001 From: "kujirahand.mac2021.win11" Date: Mon, 20 Apr 2026 16:44:49 +0900 Subject: [PATCH 2/7] change default parameter: use_ttk_button=True --- TkEasyGUI/widgets.py | 15 ++++++++++----- docs/theme.md | 8 ++++++++ 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/TkEasyGUI/widgets.py b/TkEasyGUI/widgets.py index 9fbecfb..1298d5c 100644 --- a/TkEasyGUI/widgets.py +++ b/TkEasyGUI/widgets.py @@ -2337,6 +2337,7 @@ class Button(Element): **Example** The program below changes the button's label to "Pushed" when the button is pressed. + ```python import TkEasyGUI as eg button:eg.Button = eg.Button("Push me") @@ -2346,6 +2347,12 @@ class Button(Element): button.set_text("Pushed") break ``` + + `use_ttk_buttons` parameter is set to `True` by default, which makes buttons look more modern. + However, on macOS, the default `ttk.Button` has a gray background and does not support changing the background color. + If you want to change the button color on macOS, set `use_ttk_buttons=False` to use `tk.Button` instead. + Also, when `use_ttk_buttons=True`, explicit button height settings are ignored due to a `ttk` limitation. + If you need to control button height, set `use_ttk_buttons=False`. """ # pylint: disable=too-many-arguments, too-many-locals @@ -2363,15 +2370,13 @@ def __init__( font: Optional[FontType] = None, # font color: Optional[str] = None, # text color text_color: Optional[str] = None, # same as color - background_color: Optional[ - str - ] = None, # background color (not supported on macOS) + background_color: Optional[str] = None, # background color (not supported on macOS) # pack props expand_x: bool = False, expand_y: bool = False, pad: Optional[PadType] = None, # other - use_ttk_buttons: Optional[bool] = None, + use_ttk_buttons: bool = True, metadata: Optional[dict[str, Any]] = None, **kw, ) -> None: @@ -2379,7 +2384,7 @@ def __init__( key = button_text if (key is None) or (key == "") else key super().__init__("Button", "TButton", key, False, metadata, **kw) # On Windows, ttk buttons look more modern by default. - self.use_ttk = utils.is_win() if use_ttk_buttons is None else use_ttk_buttons + self.use_ttk = use_ttk_buttons self.disabled = False if disabled is not None: self.props["disabled"] = self.disabled = disabled diff --git a/docs/theme.md b/docs/theme.md index 9b10380..7eea26f 100644 --- a/docs/theme.md +++ b/docs/theme.md @@ -69,6 +69,14 @@ window.read() window.close() ``` +Note: +- If you set `use_ttk_buttons=True`, explicit button height settings are ignored. +- This is a limitation of `ttk`. +- If you need to control button height, use `use_ttk_buttons=False`. + + + + ## How to customize defaults ### Global theme default From b5204dd3bbce28a50d46e54c220483b8eafc0f4d Mon Sep 17 00:00:00 2001 From: kujirahand Date: Mon, 20 Apr 2026 16:50:27 +0900 Subject: [PATCH 3/7] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- docs/theme.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/theme.md b/docs/theme.md index 7eea26f..38e3639 100644 --- a/docs/theme.md +++ b/docs/theme.md @@ -53,9 +53,8 @@ Note: `Button` can render with `ttk.Button` (modern) or `tk.Button` (classic). -- `use_ttk_buttons=True`: use `ttk.Button` +- `use_ttk_buttons=True` (default): use `ttk.Button` - `use_ttk_buttons=False`: use `tk.Button` -- `use_ttk_buttons=None` (default): Windows uses `ttk`, other OS keep previous default behavior ```python import TkEasyGUI as eg From 9e16a2276eb69365b28a2aa7c7382d8bff1dea76 Mon Sep 17 00:00:00 2001 From: kujirahand Date: Mon, 20 Apr 2026 19:54:42 +0900 Subject: [PATCH 4/7] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- tests/theme/ttk_button.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/theme/ttk_button.py b/tests/theme/ttk_button.py index c767d80..78e6725 100644 --- a/tests/theme/ttk_button.py +++ b/tests/theme/ttk_button.py @@ -13,6 +13,6 @@ for event, values in window.event_iter(): if event == "Modern": eg.popup("You clicked the modern button!") - if event == "Classic": + elif event == "Classic": eg.popup("You clicked the classic button!") window.close() From 59346f212934d7a8c90c5dfb29be2524030b3b1d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 20 Apr 2026 10:58:05 +0000 Subject: [PATCH 5/7] fix: add get_themes alias and update theme guide naming Agent-Logs-Url: https://github.com/kujirahand/tkeasygui-python/sessions/5529a64b-e51e-4a12-a682-716cdc1177c8 Co-authored-by: kujirahand <2153829+kujirahand@users.noreply.github.com> --- TkEasyGUI/utils.py | 7 ++++++- docs/theme.md | 7 ++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/TkEasyGUI/utils.py b/TkEasyGUI/utils.py index 222db8b..10380e3 100644 --- a/TkEasyGUI/utils.py +++ b/TkEasyGUI/utils.py @@ -223,7 +223,7 @@ def str_to_float(value: str, default_value: float = 0) -> float: _tkeasygui_info: dict[str, Any] = {} -def get_tnemes() -> tuple[str, ...]: +def get_themes() -> tuple[str, ...]: """ Get theme list @@ -236,6 +236,11 @@ def get_tnemes() -> tuple[str, ...]: return ttk.Style().theme_names() +def get_tnemes() -> tuple[str, ...]: + """Alias of get_themes (kept for backward compatibility).""" + return get_themes() + + def theme(name: str) -> None: """Set theme (alias of set_theme)""" set_theme(name) diff --git a/docs/theme.md b/docs/theme.md index 38e3639..60b27de 100644 --- a/docs/theme.md +++ b/docs/theme.md @@ -38,16 +38,17 @@ eg.theme("default") ## List available themes -Use `get_tnemes()` to inspect themes available in your current Tk runtime. +Use `get_themes()` to inspect themes available in your current Tk runtime. ```python import TkEasyGUI as eg -print(eg.get_tnemes()) +print(eg.get_themes()) ``` Note: -- Function name is `get_tnemes()` in current API. +- `get_themes()` is the preferred API name. +- `get_tnemes()` is still available as a backward-compatible alias. ## Change button style behavior From b81101d41b7736bec7113863f39d8ce6dc251d7d Mon Sep 17 00:00:00 2001 From: "kujirahand.mac2021.win11" Date: Mon, 20 Apr 2026 22:03:57 +0900 Subject: [PATCH 6/7] merge main --- TkEasyGUI/widgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TkEasyGUI/widgets.py b/TkEasyGUI/widgets.py index 1298d5c..d938aa2 100644 --- a/TkEasyGUI/widgets.py +++ b/TkEasyGUI/widgets.py @@ -2383,7 +2383,7 @@ def __init__( """Create a Button element.""" key = button_text if (key is None) or (key == "") else key super().__init__("Button", "TButton", key, False, metadata, **kw) - # On Windows, ttk buttons look more modern by default. + # ttk buttons look more modern by default. self.use_ttk = use_ttk_buttons self.disabled = False if disabled is not None: From 7b3babf7e0e02a4e50646dcfb2e010707b74a1d4 Mon Sep 17 00:00:00 2001 From: kujirahand Date: Mon, 20 Apr 2026 23:25:52 +0900 Subject: [PATCH 7/7] set use_ttk_buttons --- TkEasyGUI/widgets.py | 19 ++++++- tests/calc2.py | 74 +++++++++++++++++++++++++++ tests/tabgroup.py | 46 +++++++++++++++++ tests/ui_test2.py | 117 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 254 insertions(+), 2 deletions(-) create mode 100644 tests/calc2.py create mode 100644 tests/tabgroup.py create mode 100644 tests/ui_test2.py diff --git a/TkEasyGUI/widgets.py b/TkEasyGUI/widgets.py index d938aa2..2ddb148 100644 --- a/TkEasyGUI/widgets.py +++ b/TkEasyGUI/widgets.py @@ -2370,13 +2370,15 @@ def __init__( font: Optional[FontType] = None, # font color: Optional[str] = None, # text color text_color: Optional[str] = None, # same as color - background_color: Optional[str] = None, # background color (not supported on macOS) + background_color: Optional[ + str + ] = None, # background color (not supported on macOS) # pack props expand_x: bool = False, expand_y: bool = False, pad: Optional[PadType] = None, # other - use_ttk_buttons: bool = True, + use_ttk_buttons: Optional[bool] = None, metadata: Optional[dict[str, Any]] = None, **kw, ) -> None: @@ -2384,6 +2386,8 @@ def __init__( key = button_text if (key is None) or (key == "") else key super().__init__("Button", "TButton", key, False, metadata, **kw) # ttk buttons look more modern by default. + if use_ttk_buttons is None: + use_ttk_buttons = True self.use_ttk = use_ttk_buttons self.disabled = False if disabled is not None: @@ -2396,6 +2400,16 @@ def __init__( self.tooltip: Union[str, None] = tooltip if button_color is not None: self.set_button_color(button_color, update=False) + # check size + self.tk_button_height = 1 + size = self.props.get("size", None) + if size is not None: + if isinstance(size, tuple) and len(size) == 2: + h = self.props["height"] = size[1] + if h >= 2: + self.use_ttk = False + self.tk_button_height = h + self._set_text_props( font=font, text_align=text_align, @@ -2422,6 +2436,7 @@ def create(self, win: Window, parent: tk.Widget) -> tk.Widget: **self.props, ) else: + self.props["height"] = self.tk_button_height self.widget = tk.Button( parent, command=lambda: self.disptach_event({"event_type": "command"}), diff --git a/tests/calc2.py b/tests/calc2.py new file mode 100644 index 0000000..ad21493 --- /dev/null +++ b/tests/calc2.py @@ -0,0 +1,74 @@ +""" +### Calculator Sample + +This sample demonstrates how to create a simple calculator using TkEasyGUI. +It also shows how to create multiple buttons and handle button events effectively. +""" + +import TkEasyGUI as eg + +eg.set_theme("alt") + +# define the calculator buttons +calc_buttons = [ + ["C", "←", "//", "/"], + ["7", "8", "9", "*"], + ["4", "5", "6", "-"], + ["1", "2", "3", "+"], + ["0", ".", "%", "="], +] +font = ("Helvetica", 20) +# create window +layout = [ + [ + eg.Input( + "0", + key="-output-", + background_color="white", + color="black", + readonly_background_color="white", + readonly=True, + expand_x=True, + ) + ], +] +# create many buttons +for row in calc_buttons: + buttons = [] + for ch in row: + btn = eg.Button( + ch, # label + key=f"-btn{ch}", + size=(5, 3), + ) + buttons.append(btn) + layout.append(buttons) +window = eg.Window("Calc", layout, font=font) + +# event loop +output = "0" +for event, values in window.event_iter(): + if event == eg.WINDOW_CLOSED: + break + # when a button is pressed + if event.startswith("-btn"): + # get label + ch = window[event].get() + # clear if text is empty (0 or error) + if output == "0" or output.startswith("E:"): + output = "" + # check label + if ch == "C": # clear key + output = "0" + elif ch == "←": # bs key + output = output[:-1] + elif ch == "=": # calc + try: + output = str(eval(output)) + except Exception as e: + output = "E:" + str(e) + else: + # add other key + output += ch + # update display + window["-output-"].update(output) diff --git a/tests/tabgroup.py b/tests/tabgroup.py new file mode 100644 index 0000000..fa77ac6 --- /dev/null +++ b/tests/tabgroup.py @@ -0,0 +1,46 @@ +import TkEasyGUI as eg + +layout = [ + [ + eg.TabGroup( + layout=[ + [ + eg.Tab( + "Tab 1", + [ + [eg.Text("This is Tab 1")], + [eg.Text("This is Tab 1")], + [eg.Text("This is Tab 1")], + [eg.Text("This is Tab 1")], + [eg.Text("This is Tab 1")], + [eg.Button("Button 1")], + ], + ) + ], + [ + eg.Tab( + "Tab 2", + [[eg.Text("This is Tab 2")], [eg.Button("Button 2")]], + ) + ], + ], + vertical_alignment="top", + expand_x=True, + expand_y=True, + ) + ] +] + +# window create +window = eg.Window( + "UI test", + layout=layout, + font=("Arial", 12), + finalize=True, + resizable=True, +) +# event loop +while window.is_running(): + event, values = window.read() + print("#", event, values) +window.close() diff --git a/tests/ui_test2.py b/tests/ui_test2.py new file mode 100644 index 0000000..deb7563 --- /dev/null +++ b/tests/ui_test2.py @@ -0,0 +1,117 @@ +""" +UI Test +""" + +import TkEasyGUI as eg + +layout_left = eg.Column( + [ + [ + eg.Frame( + "OS", + [ + [eg.Checkbox("Windows")], + [eg.Checkbox("macOS")], + [eg.Checkbox("Ubuntu")], + [eg.Checkbox("ChromeOS")], + ], + expand_x=True, + ) + ], + [ + eg.Frame( + "Animal", + [ + [eg.Radio("Dog", group_id="animal")], + [eg.Radio("Cat", group_id="animal")], + [eg.Radio("Rabit", group_id="animal")], + ], + expand_x=True, + expand_y=True, + ) + ], + [eg.Listbox(["AAA", "BBB", "CCC"], width=10, expand_y=True)], + [eg.VPush()], + ], + expand_y=True, + vertical_alignment="top", +) + +layout_center = eg.Frame( + "Profile", + [ + [eg.Text("Name:")], + [eg.Input("TkEasyGUI", key="-name", expand_x=True)], + [eg.Text("Hobby:")], + [eg.Input("Programming", key="-name", expand_x=True)], + [eg.Text("Fruits:")], + [eg.Combo(["Mango", "Banana", "Apple"], default_value="Mango", expand_x=True)], + [eg.Text("Number:")], + [eg.Combo(["One", "Two", "Three"], default_value="One", expand_x=True)], + [eg.Text("Language:")], + [eg.Combo(["Python", "Rust", "C/C++"], default_value="One", expand_x=True)], + [eg.Button("Hello", expand_x=True)], + [eg.Button("World", expand_x=True)], + [eg.Button("Test", expand_x=True)], + ], + expand_x=True, + expand_y=True, +) + +name_table = eg.Table( + [["Alice", "30"], ["Bob", "25"], ["Charlie", "35"]], + headings=["Name", "Age"], + key="-table-", + expand_x=True, + expand_y=True, +) + + +layout_right = eg.Frame( + "Info", + [ + [eg.Text("This is the right column", width=50)], + [eg.HSeparator()], + [ + eg.TabGroup( + layout=[ + [ + eg.Tab( + "Tab 1", + [ + [name_table], + ], + ) + ], + [ + eg.Tab( + "Tab 2", + [[eg.Text("This is Tab 2")], [eg.Button("Button 2")]], + ) + ], + ], + # vertical_alignment="top", + # width=300, + expand_y=True, + ) + ], + ], + expand_x=True, + expand_y=True, +) + +layout = [[eg.Column([[layout_left, layout_center, layout_right]])]] +# event loop +# window create +window = eg.Window( + "UI test", + layout=layout, + font=("Arial", 12), + finalize=True, + resizable=True, +) +# event loop +while window.is_running(): + event, values = window.read() + print("#", event, values) +window.close()