Tkinter如何跟踪窗口的特定大小调整?

9

我正在编写一个小程序,意图是在窗口大小调整时运行一个调整大小的方法。为此,我使用了toplevel.bind("<Configure>", self.resize)。虽然这确实可以进行调整大小,但该方法会在许多情况下被调用:滚动时、使用某些按钮时,即使窗口大小并没有因这些操作之一而改变。如何绑定事件,以便它仅在更改窗口大小时被调用?

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()

输出:

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

即使窗口只有一个画布和滚动条,调整大小事件在用户交互之前被调用了7次。我该如何停止这种疯狂的行为,并在窗口真正调整大小时才调用调整大小函数?


这个链接中的解释可能会有帮助。 - acw1668
2个回答

9
effbot DOC中:

<Configure>

小部件的大小(或位置,在某些平台上)已更改。新的大小在传递给回调函数的事件对象的widthheight属性中提供。

当您运行代码时,它会在窗口中绘制这些小部件,因此窗口的宽度和高度将发生变化。 您可以使用if语句检查Toplevel的宽度和高度是否发生了变化。
在您的代码中更改resize()函数并初始化全局变量:
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

你可以通过使用类(面向对象编程)来避免全局变量。

5
据我所知,当您将“<Configure>”事件绑定到根窗口或顶层窗口时,它也会绑定到其中包含的每个小部件-唯一的解决方法是过滤掉所有其他调用。幸运的是,这并不太困难。为了避免使用全局变量,我定义了一个类来处理这些复杂的详细信息。
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()


不要绑定和解绑,考虑在您的追踪器类中存储对根Tk()对象的引用,仅当该对象是Tk对象时进行调整大小。 - AntPalmer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接