Skip to content

styles

tkinter styles

Uses ttk Themed Styles

Docs: https://tkdocs.com/tutorial/styles.html List of Themes: https://wiki.tcl-lang.org/page/List+of+ttk+Themes awlight/awdark theme download: https://wiki.tcl-lang.org/page/awthemes Fonts: https://tkdocs.com/tutorial/fonts.html

create_hover(parent, top_left=(0.1, 0.1))

Create tkinter frame hovering above the current widget

E.G. window_frame, close = create_hover(widget) ttk.Button(window_frame, text='Close', command=close).pack()

Parameters:

Name Type Description Default
parent Misc | RootWithStyle

tk widget or root

required
top_left tuple[float, float]

(relx, rely) widget top-left corner relative to parent top-left corner

(0.1, 0.1)

Returns:

Type Description

function close() -> None (releases widget and destroys hover window)

Source code in mmg_toolbox/tkguis/misc/styles.py
def create_hover(parent: tk.Misc | RootWithStyle, top_left: tuple[float, float] = (0.1, 0.1)):
    """
    Create tkinter frame hovering above the current widget

    E.G.
    window_frame, close = create_hover(widget)
    ttk.Button(window_frame, text='Close', command=close).pack()

    :param parent: tk widget or root
    :param top_left: (relx, rely) widget top-left corner relative to parent top-left corner
    :returns: ttk.Frame object inside tk.TopLevel with no window management
    :returns: function close() -> None (releases widget and destroys hover window)
    """

    root = ttk.Frame(parent)
    root.place(relx=top_left[0], rely=top_left[1])

    window = ttk.Frame(root, borderwidth=20, relief=tk.RAISED)
    window.pack(side=tk.TOP, fill=tk.BOTH)

    def destroy(event=None):
        root.grab_release()
        root.destroy()

    root.grab_set()
    return window, destroy

create_root(window_title, parent=None)

Create tkinter root object with style attribute

Source code in mmg_toolbox/tkguis/misc/styles.py
def create_root(window_title: str, parent: tk.Misc | RootWithStyle | None = None) -> RootWithStyle:
    """Create tkinter root object with style attribute"""
    # TODO: this is the slow part of the startup, investigate further.
    if parent:
        root = tk.Toplevel(parent)
        # root.geometry(f"+{parent.winfo_x()+100}+{parent.winfo_y()+100}")
        # root.transient(parent)
        if hasattr(parent, 'style'):
            root.style = parent.style
    else:
        root = tk.Tk()

    def update(event):
        root.update()
        print(root.winfo_reqwidth(), root.winfo_screenheight())
        print(root.winfo_screenwidth(), root.winfo_screenheight())

    # root.bind('<Configure>', update)

    if not hasattr(root, 'style'):
        style = create_style(root)
        root.style = style

    # Fix background (on windows)
    bg = get_style_background(root, root.style)
    root.configure(bg=bg)

    root.wm_title(window_title)
    # self.root.minsize(width=640, height=480)
    # root.maxsize(width=root.winfo_screenwidth() * 3 // 4, height=root.winfo_screenheight() * 3 // 4)
    root.maxsize(width=int(root.winfo_screenwidth() - 50), height=int(root.winfo_screenheight()) - 100)
    return root

extra_styles(style)

Add additional styles to use on individual ttk components

Source code in mmg_toolbox/tkguis/misc/styles.py
def extra_styles(style: ttk.Style):
    """
    Add additional styles to use on individual ttk components
    """
    # style.configure("Red.TLabel", foreground='red', font='TkDefaultFont 24 bold')
    style.configure("title.TLabel", foreground='red', font='times 24 bold')
    style.configure("subtitle.TLabel", foreground='black', font='times 18 bold')
    style.configure("error.TLabel", foreground='red')
    style.configure("smallMsg.TLabel", font='TkDefaultFont 10 bold')

get_style_background(widget, style=None)

Return the background colour of the current style

Source code in mmg_toolbox/tkguis/misc/styles.py
def get_style_background(widget: tk.Misc, style: ttk.Style | None = None) -> str:
    """
    Return the background colour of the current style
    """
    style = style or ttk.Style()

    # Get the system color name
    color_name = style.lookup('.', 'background')  # e.g., 'SystemButtonFace'

    # Convert to RGB (16-bit per channel)
    r16, g16, b16 = widget.winfo_rgb(color_name)

    # Normalize to 8-bit and format as hex
    r, g, b = (r16 // 256, g16 // 256, b16 // 256)
    hex_color = f"#{r:02x}{g:02x}{b:02x}"
    return hex_color

stylename_elements_options(widget)

Function to expose the options of every element associated to a widget stylename.

widget = ttk.Button(None)
class_ = widget.winfo_class()
stylename_elements_options(class_, widget)
Source code in mmg_toolbox/tkguis/misc/styles.py
def stylename_elements_options(widget: tk.Misc):
    """
    Function to expose the options of every element associated to a widget stylename.

        widget = ttk.Button(None)
        class_ = widget.winfo_class()
        stylename_elements_options(class_, widget)
    """

    try:
        # Get widget elements
        stylename = widget.winfo_class()
        style = ttk.Style()
        layout = style.layout(stylename)
        config = widget.configure()

        print('{:*^50}\n'.format(f'Style = {stylename}'))

        print('{:*^50}'.format('Config'))
        for key, value in config.items():
            print('{:<15}{:^10}{}'.format(key, '=>', value))

        print('\n{:*^50}'.format('Layout'))
        elements = _iter_layout(layout)

        # Get options of widget elements
        print('\n{:*^50}'.format('element options'))
        for element in elements:
            print('{0:30} options: {1}'.format(
                element, style.element_options(element)))

    except tk.TclError:
        print('_tkinter.TclError: "{0}" in function'
                'widget_elements_options({0}) is not a regonised stylename.'
                .format(stylename))

theme_menu(style)

Generate theme menu dict

Source code in mmg_toolbox/tkguis/misc/styles.py
def theme_menu(style: ttk.Style) -> dict:
    """Generate theme menu dict"""
    menu = {
        "Themes": {
            name: lambda n=name: use_theme(style, n)
            for name in style.theme_names()
        }
    }
    return menu

update_text_style(widget, style)

Update a tk.Text widget with the current style

Source code in mmg_toolbox/tkguis/misc/styles.py
def update_text_style(widget: tk.Text, style: ttk.Style):
    """
    Update a tk.Text widget with the current style
    """
    widget.configure(
        bg=style.lookup('.', 'background'),
        fg=style.lookup('.', 'foreground'),
        font=style.lookup('.', 'font') or 'TkDefaultFont',
    )