Reputation: 377
I am writing a small program, which the intention to run a resize method, when the window size is changed. for this reason, I used toplevel.bind("<Configure>", self.resize)
. While this does work for resizing, the method gets called hundreds of times: when scrolling, when using some of the buttons, even though the window size doesn't change with any of these actions. How can I bind the event, so that it only gets called upon changing the window size?
import tkinter as tk
def resize(event):
print("widget", event.widget)
print("height", event.height, "width", event.width)
parent = tk.Toplevel()
canvas = tk.Canvas(parent)
scroll_y = tk.Scrollbar(parent, orient="vertical", command=canvas.yview)
scroll_x = tk.Scrollbar(parent, orient="horizontal", command=canvas.xview)
frame = tk.Frame(canvas)
# put the frame in the canvas
canvas.create_window(0, 0, anchor='nw', window=frame)
# make sure everything is displayed before configuring the scrollregion
canvas.update_idletasks()
canvas.configure(scrollregion=canvas.bbox('all'),
yscrollcommand=scroll_y.set,
xscrollcommand=scroll_x.set)
scroll_y.pack(fill='y', side='right')
scroll_x.pack(fill='x', side='bottom')
canvas.pack(fill='both', expand=True, side='left')
parent.bind("<Configure>", resize)
parent.mainloop()
Output:
widget .!toplevel
height 200 width 200
widget .!toplevel.!scrollbar
height 286 width 17
widget .!toplevel.!scrollbar2
height 17 width 382
widget .!toplevel.!canvas
height 269 width 382
widget .!toplevel
height 286 width 399
widget .!toplevel
height 286 width 399
widget .!toplevel.!can
Eventhough there is only one canvas plus the scrollbars on the window, the resize event gets called 7 times, before the user even has a change to interact with the window. How can I stop this madness and only call the resize function, when the window does get resized?
Upvotes: 10
Views: 17480
Reputation: 11
I modified the solution and came up with something:
Bind the event to any function and define a variable to store windows size.
self.win_size = 0
self.bind("<Configure>", self.catch_win_size)
and then in the function, check if the windows size has changed or not
def catch_win_size(self,event):
x = str(event.widget)
if x == ".": # . is toplevel window
if (self.win_size != event.height):
self.win_size = event.height
print(f"The height of Toplevel is {self.win_size}")
In my case, I only want to track changes in height.
Upvotes: 1
Reputation: 123481
As far as I know, when you bind '<Configure>' events to a root or toplevel window it also gets bound to every widget it contains — and the only workaround is to filter-out all those other calls. Fortunately that's not too difficult. To avoid using global variables, I've defined a class to deal with the dirty details.
import tkinter as tk
class Tracker:
""" Toplevel windows resize event tracker. """
def __init__(self, toplevel):
self.toplevel = toplevel
self.width, self.height = toplevel.winfo_width(), toplevel.winfo_height()
self._func_id = None
def bind_config(self):
self._func_id = self.toplevel.bind("<Configure>", self.resize)
def unbind_config(self): # Untested.
if self._func_id:
self.toplevel.unbind("<Configure>", self._func_id)
self._func_id = None
def resize(self, event):
if(event.widget == self.toplevel and
(self.width != event.width or self.height != event.height)):
print(f'{event.widget=}: {event.height=}, {event.width=}\n')
self.width, self.height = event.width, event.height
root = tk.Tk()
root.withdraw()
parent = tk.Toplevel(master=root)
parent.title('parent')
canvas = tk.Canvas(parent)
scroll_y = tk.Scrollbar(parent, orient="vertical", command=canvas.yview)
scroll_x = tk.Scrollbar(parent, orient="horizontal", command=canvas.xview)
frame = tk.Frame(canvas)
# put the frame in the canvas
canvas.create_window(0, 0, anchor='nw', window=frame)
# make sure everything is displayed before configuring the scrollregion
canvas.update_idletasks()
canvas.configure(scrollregion=canvas.bbox('all'),
yscrollcommand=scroll_y.set,
xscrollcommand=scroll_x.set)
scroll_y.pack(fill='y', side='right')
scroll_x.pack(fill='x', side='bottom')
canvas.pack(fill='both', expand=True, side='left')
tracker = Tracker(parent)
tracker.bind_config()
parent.mainloop()
Upvotes: 5
Reputation: 12672
In effbot DOC:
<Configure>
The widget changed size (or location, on some platforms). The new size is provided in the width and height attributes of the event object passed to the callback.
When you run your code, it will draw those widget in the window, so the width and the height of the window will change.
You could use a if
statement to check whether the width and height of the Toplevel
changed.
Change the function resize()
and initialize the global variable in your code:
def resize(event):
global window_width, window_height
if event.widget.widgetName == "toplevel":
if (window_width != event.width) and (window_height != event.height):
window_width, window_height = event.width,event.height
print(f"The width of Toplevel is {window_width} and the height of Toplevel "
f"is {window_height}")
window_width, window_height = 0, 0
You could avoid the global
variables by using a class (OOP).
Upvotes: 10