Tkinter 在可滚动框架上添加小部件最后一项未显示

Tkinter add widget on scroolable frame last item does not shown

我想创建一个 window 允许为文件传输输入一对多字段。 我创建了一个 Scrollable Frame 并在运行时添加了 Entry-Text 对。如果我第一次点击按钮,一切顺利。第二次之后,UI 端没有任何反应。第二次单击后它可以完美运行。但是我看到所有的pair都添加成功了,只是UI没有显示出来。有人知道如何解决吗?

import tkinter as tk

class VerticalScroolFrame(tk.Frame):
    """A frame with a vertical scroolbar"""

    def __init__(self, master, *args, **kwargs):
        main_frame = tk.Frame(master)
        main_frame.grid()

        main_frame.rowconfigure(0, weight=1)
        main_frame.columnconfigure(0, weight=1)

        self._canvas = tk.Canvas(main_frame)
        self._canvas.grid(row=0, column=0, sticky=tk.NSEW)

        scrollbar = tk.Scrollbar(main_frame, command=self._canvas.yview)
        scrollbar.grid(row=0, column=1, sticky=tk.N+tk.S+tk.W)

        self._canvas.configure(yscrollcommand=scrollbar.set)
        self._canvas.bind('<Configure>', lambda *_: self.on_configure())

        super().__init__(self._canvas, *args, **kwargs)
        self._canvas.create_window((0, 0), window=self, anchor=tk.NW)

    def on_configure(self):
        """update scrollregion after starting 'mainloop'
        when all widgets are in self._canvas. And also need to be triggered
        whenever a widget added as a child.
        """
        self._canvas.configure(scrollregion=self._canvas.bbox('all'))



class AdvancedTransfer:
    """Opens a window that allows you to enter source file list
    and targets for them. One-to-many relation.
    """
    def __init__(self, root):
        self._scroolable_frame = VerticalScroolFrame(root)
        self._entry_text_dict = {}

        self._button = tk.Button(root, text="Add", command=self.add_item)
        self._button.grid()

    def add_item(self):
        """Add entry-text widget group"""

        row = len(self._entry_text_dict)

        entry = tk.Entry(self._scroolable_frame)
        entry.insert(0, "row number: {0}".format(row))
        entry.grid(row=row, column=0)
        text = tk.Text(self._scroolable_frame)
        text.grid(row=row, column=1)

        self._entry_text_dict[entry] = text

        self._scroolable_frame.on_configure()


def main():
    root = tk.Tk()

    main_frame = tk.Frame(root)
    main_frame.grid()

    AdvancedTransfer(main_frame)

    root.mainloop()


if __name__ == "__main__":
    main()

重现步骤:

  1. 运行下面的代码。
  2. 点击按钮两次。
  3. 您应该看到 2 对,但只显示了 1 对。

这是因为您在错误的小部件上绑定了 <Configure> 事件。您应该绑定到内部框架(即 VerticalScroolFrame 的实例)而不是 canvas (self._canvas):

class VerticalScroolFrame(tk.Frame):
    """A frame with a vertical scroolbar"""

    def __init__(self, master, *args, **kwargs):
        main_frame = tk.Frame(master)
        main_frame.grid()

        main_frame.rowconfigure(0, weight=1)
        main_frame.columnconfigure(0, weight=1)

        self._canvas = tk.Canvas(main_frame)
        self._canvas.grid(row=0, column=0, sticky=tk.NSEW)

        scrollbar = tk.Scrollbar(main_frame, command=self._canvas.yview)
        scrollbar.grid(row=0, column=1, sticky=tk.N+tk.S+tk.W)

        self._canvas.configure(yscrollcommand=scrollbar.set)
        #self._canvas.bind('<Configure>', lambda *_: self.on_configure())

        super().__init__(self._canvas, *args, **kwargs)
        self._canvas.create_window((0, 0), window=self, anchor=tk.NW)

        # bind <Configure> event on itself
        self.bind('<Configure>', lambda _: self.on_configure())