diff --git a/TkEasyGUI/utils.py b/TkEasyGUI/utils.py index c3d6f54..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) @@ -259,8 +264,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..2ddb148 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 @@ -2371,14 +2378,17 @@ 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 + # 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: self.props["disabled"] = self.disabled = disabled @@ -2390,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, @@ -2416,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/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..60b27de --- /dev/null +++ b/docs/theme.md @@ -0,0 +1,115 @@ +# 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_themes()` to inspect themes available in your current Tk runtime. + +```python +import TkEasyGUI as eg + +print(eg.get_themes()) +``` + +Note: +- `get_themes()` is the preferred API name. +- `get_tnemes()` is still available as a backward-compatible alias. + +## Change button style behavior + +`Button` can render with `ttk.Button` (modern) or `tk.Button` (classic). + +- `use_ttk_buttons=True` (default): use `ttk.Button` +- `use_ttk_buttons=False`: use `tk.Button` + +```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() +``` + +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 + +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/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/theme/ttk_button.py b/tests/theme/ttk_button.py new file mode 100644 index 0000000..78e6725 --- /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!") + elif event == "Classic": + eg.popup("You clicked the classic button!") +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()