Responsive Status Bar & bind <Configure> problem

Question:

I am new to Tkinter and I am experimenting, trying to build a little library of helper classes.

I am presently trying to create a status bar class, based upon a Label widget. I want to be able to make the width of the label adjust. I am actually using customtkinter, but I believe that the challenge is essentially the same as with tkinter.

To achieve the resizing I added a method to my CBtkStatusBar class, which determines the width of the window, in which the status bar is placed, and performs the resize. I am also binding a function to perform the resize of the status bar, when the window is resized. My classes for creating my app and frames also have published methods, which act as relays to the status bar’s resize method.

The problem I am having, is that the status bar resize function, only seems to fire, upon initial launch of the application. When I subsequently widen the window, the function is not firing. I have a print statement in my status bar methods which also shows when the function is activated:

def auto_size_status_bar(self):
    print('Resizing....')
    self._master.update_idletasks()
    self._app_width = self._master.winfo_width()
    self._status_bar.configure(width=self._app_width))

My man-in-the-middle methods look like this:

def resize_status_bar(self):
    if self._status_bar:
        self._status_bar.auto_size_status_bar()
    else:
        print(f'WARNING: Attempt to resize nonexistent status bar on window "{self._window_name}"')
        raise cbtkx.NoStatusBar

The above is from my class which creates a frame and includes my status bar widget. I then bind the method:

my_app.bind("<Configure>", my_app.resize_status_bar())          # Root
launch_top.bind("<Configure>", launch_top.resize_status_bar())  # frame

In more complete context:

if __name__ == "__main__":
    my_app = CBTkApp(app_name='CTKComponent Test', appearance_mode='Dark')

    my_app.frm_right = ctk.CTkFrame(master=my_app, borderwidth=0, corner_radius=0)
    my_app.frm_right.grid(row=0, column=1, sticky="nsew", padx=0, pady=0)

    my_app.frm_left = ctk.CTkFrame(master=my_app, borderwidth=0, width=700)
    my_app.frm_left.grid(row=0, column=0, sticky="nsew", padx=5, pady=5)

    btn_save = ctk.CTkButton(master=my_app.frm_right,
                             text="Save",
                             border_width=2,
                             fg_color=None,
                             command=my_app.on_closing)
    btn_save.grid(row=2, column=0, pady=10, padx=(10, 10), sticky="ew")

    btn_cancel = ctk.CTkButton(master=my_app.frm_right,
                               text="Cancel",
                               border_width=2,
                               fg_color=None,
                               command=my_app.on_closing)
    btn_cancel.grid(row=5, column=0, pady=(10, 10), padx=(10, 10), sticky="ew")

    cancel_tooltip = CBtkToolTip(btn_cancel, 'Press to quit')
    my_app.set_status('Config loaded...')
    launch_top = CBTkToplevel(master=my_app, window_name='Launched Window!', status_bar=True)
    launch_top.frm_right = ctk.CTkFrame(master=launch_top, borderwidth=0, corner_radius=0)
    launch_top.frm_right.grid(row=0, column=1, sticky="nsew", padx=0, pady=0)

    launch_top.frm_left = ctk.CTkFrame(master=launch_top, borderwidth=0, width=700)
    launch_top.frm_left.grid(row=0, column=0, sticky="nsew", padx=5, pady=5)
    my_app.bind("<Configure>", my_app.resize_status_bar())
    launch_top.bind("<Configure>", launch_top.resize_status_bar())

    my_app.mainloop()

The label class looks like this:

class CBtkStatusBar():
    def __init__(self, master, fg_color: tuple = ("gray82", "gray23")):
        self._master = master
        self._master.update_idletasks()
        self._app_width = self._master.winfo_width()
        self._appearance_mode = ctk.get_appearance_mode()
        self._int_mode = self._str_mode_to_int()

        self._bg_color = self._get_color_from_name('text')

        self._default_fg_color = fg_color

        self._status_bar = ctk.CTkLabel(master, relief=tk.SUNKEN, text='', anchor='w', width=self._app_width,
                                        fg_color=self._default_fg_color)
        self._status_bar.grid(row=99, column=0, padx=0, pady=0, columnspan=4, sticky='w')

    def auto_size_status_bar(self):
        print('Resizing....')
        self._master.update_idletasks()
        self._app_width = self._master.winfo_width()
        self._status_bar.configure(width=self._app_width)
        self._master.update_idletasks()

    def set_status_text(self, status_text: str, fg_color: tuple = ("gray82", "gray23")):
        self._status_bar.configure(text='  ' + status_text)

    def set_text_color(self, text_color):
        self._status_bar.configure(text_color=text_color)

    def _str_mode_to_int(self):
        if self._appearance_mode == "Light":
            return 0
        return 1

    def set_fg_color(self, fg_color):
        self._status_bar.configure(fg_color=fg_color)

    def _get_color_from_name(self, name: str):
        return ThemeManager.theme["color"][name][self._int_mode]

    @staticmethod
    def _get_property_by_name(prop_name: str):
        return ThemeManager.theme[prop_name]

and the calling classes include the following __init_snippet:

if status_bar:
    self._status_bar = CBtkStatusBar(master=self)
    self._status_bar.set_status_text('Launched!')

Apologies if I have over doe it with the code snippets.

Any idea why this only workds on initial application load?

Thanks.

Asked By: Clive Bostock

||

Answers:

I solved it. I was missing the event parameter in my method signatures!

def resize_status_bar(self, event):
    if self._status_bar:
        self._status_bar.auto_size_status_bar(event)
    else:
        print(f'WARNING: Attempt to resize nonexistent status bar on window "{self._window_name}"')
        raise cbtkx.NoStatusBar

I then had to change the binds to reference the methods like so:

my_app.bind("<Configure>", my_app.resize_status_bar)
launch_top.bind("<Configure>", launch_top.resize_status_bar)

That’s is to say, I removed the parentheses.

Answered By: Clive Bostock