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.
my_app.bind("<Configure>", my_app.resize_status_bar())
will immediately callmy_app.resize_status_bar()
and then pass the result (None
) to the bind command. In other words, this is no different thanmy_app.bind("<Configure>", None)