0

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.

2
  • You seem to be working very hard to get the autosize to work. Why not let tkinter do all of the work? Use pack or grid with the appropriate options and it will automatically grow or shrink to fit the window. Commented Jul 19, 2022 at 14:40
  • my_app.bind("<Configure>", my_app.resize_status_bar()) will immediately call my_app.resize_status_bar() and then pass the result (None) to the bind command. In other words, this is no different than my_app.bind("<Configure>", None) Commented Jul 19, 2022 at 14:48

1 Answer 1

0

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.