Search code examples
pythontkinter

Python Tkinter - Setting a custom style to an app


I am trying to customize my CustomTkinter app using two things:

  1. A custom style (to define colors, fonts...)
  2. Custom widgets inheriting the custom style

The custom style is defined as a class which takes an argument to select some colors or some other colors:

class Style():
    def __init__(self, selected_style):
        match selected_style:
            case "style1":
                self.colors = ["#3d3d3d", "#5c5c5c"]
            case "style2":
                self.colors = ["#c6c6c6", "#ececec"]

The custom widgets then inherit the style. A button widget is in example:

from customtkinter import CTkButton

class MyButton(CTkButton, Style):
    def __init__(self, master, style, text):
        Style.__init__(self, style)
        CTkButton.__init__(self, master, text=text, fg_color=self.colours[1])
        self.grid(sticky="WE", padx=8, pady=8)

Why I coded it this way?

  1. The custom widgets save me from specifying some parameters every time (for example, the grid settings).
  2. The style with an argument allow me to change all colors, fonts... of the app without changing all the parameters of the widgets (for example, the grid settings).

What is the problem?

When instantiating a widget, the style argument ("style1" or "style2") needs to be passed. It is defined when the app is launched and I then need to transmit it through the whole app to each widget.

How can I avoid passing the style argument through the whole app?

I understand that I might need to restructure my code significantly...


Solution

  • One convenient way is to use a class variable. Call it default_style:

    class Style:
        default_style = 'style1'
        def __init__(self, selected_style=None):
            if selected_style is None:
                self.selected_style = self.default_style
            else:
                self.selected_style = selected_style
            match self.selected_style:
                case "style1":
                    self.colors = ["#3d3d3d", "#5c5c5c"]
                case "style2":
                    self.colors = ["#c6c6c6", "#ececec"]
    

    If you set default_style at the beginning of the program with a line like this:

    Style.default_style = 'style2'
    

    that style will be used throughout the program.

    If you want to override the style for only a single class of widget, pass a non-None argument to the Style constructor:

    class MySpecialButton(CTkButton, Style):
        def __init__(self, master, text):
            Style.__init__(self, 'style2')
            CTkButton.__init__(self, master, text=text, fg_color=self.colours[1])
            self.grid(sticky="WE", padx=8, pady=8)
    

    otherwise just do this:

    class MyButton(CTkButton, Style):
        def __init__(self, master, text):
            Style.__init__(self)
            CTkButton.__init__(self, master, text=text, fg_color=self.colours[1])
            self.grid(sticky="WE", padx=8, pady=8)