使用 tkinter 制作可变宽度的文本小部件,可滚动 window

Making variable width Text widgets with tkinter, in a scrollable window

我正在尝试制作一个 GUI,我希望在可滚动区域中放置多个文本小部件,并且文本小部件的宽度应该在 window 大小更改时发生变化。但是我无法让它工作。我认为它与 create_window() 有关,因为我可以在没有滚动功能的情况下使其工作。

下面的示例代码

import tkinter as tk


class Window:
    """Main window"""
    def __init__(self):
        """Initiate new gui window"""
        self.root = tk.Tk()
        self.root.minsize(800, 200)
        self.root.grid_columnconfigure(0, weight=1)

        background_grid = tk.Frame(master=self.root, bg="lightblue")
        background_grid.grid(row=0, column=0, sticky="ew")
        background_grid.grid_columnconfigure(0, weight=1)

        # Scroll area
        scroll = ScrollArea(background_grid, (0, 0))
        scroll.grid.configure(bg="darkgrey")
        scroll.canvas.grid_columnconfigure(0, weight=1)
        scroll.grid.grid_columnconfigure(0, weight=1)

        for i in range(0, 20):
            tk.Label(master=scroll.grid, text="Text").grid(row=i * 2, column=0, sticky="w")

            # Text box that I want to follow the size of the window
            text_box = tk.Text(master=scroll.grid, height=10)
            text_box.grid(row=i * 2 + 1, column=0, sticky="ew")
            text_box.grid_columnconfigure(0, weight=1)

            tk.Button(master=scroll.grid, text="Button").grid(row=i*2+1, column=1, sticky="e")

        scroll.update(self.root)


class ScrollArea:
    """Generate a scrollable area"""
    def __init__(self, master: tk.Frame or tk.Tk, coordinates: tuple, max_height=500, color="orange"):
        column, row = coordinates
        self.canvas = tk.Canvas(master=master, bg=color,
                                highlightcolor="grey", highlightbackground="grey", highlightthickness=1)
        self.canvas.grid(row=row, column=column, sticky="ew")

        self.grid = tk.Frame(master=self.canvas)
        self.grid.grid(row=0, column=0, sticky="ew")

        self.scrollbar = tk.Scrollbar(master=master, orient=tk.VERTICAL, command=self.canvas.yview)
        self.scrollbar.grid(row=row, column=column + 1, sticky="ns")

        self.canvas.configure(yscrollcommand=self.scrollbar.set)
        self.canvas.create_window((0, 0), window=self.grid)
        self.max_height = max_height

    def update(self, root_window: tk.Tk or tk.Toplevel):
        """Update the gui"""
        root_window.update_idletasks()

        # Set size of the grid to use for scroll region
        bbox = self.canvas.bbox(tk.ALL)
        display_width, display_height = self.grid.winfo_width(), min(self.max_height, self.grid.winfo_height())
        self.canvas.configure(scrollregion=bbox, width=display_width, height=display_height)
        self.grid.bind("<Enter>", self._bound_to_mousewheel)
        self.grid.bind("<Leave>", self._unbound_to_mousewheel)

        # Scroll to the top
        self.canvas.yview_moveto(0)

    def _bound_to_mousewheel(self, event):
        """Binds mouse wheel to scroll when the mouse is over the report area"""
        self.canvas.bind_all("<MouseWheel>", self._on_mousewheel)
        _ = event  # dummy

    def _unbound_to_mousewheel(self, event):
        """Removes the mouse wheel bind when the mouse leaves the report area"""
        self.canvas.unbind_all("<MouseWheel>")
        _ = event  # dummy

    def _on_mousewheel(self, event):
        """On scroll, move the canvas"""
        self.canvas.yview_scroll(int(-1 * (event.delta / 120)), "units")


def main():
    window = Window()

    window.root.mainloop()


if __name__ == '__main__':
    main()

你需要做两件事:你需要在 canvas 改变大小时设置内嵌 window 的宽度,并在创建 [=] 时使用 anchor 选项15=].

class ScrollArea:
    def __init__(self, master: tk.Frame or tk.Tk, coordinates: tuple, 
        ...
        self.grid_id = self.canvas.create_window((0, 0), window=self.grid, anchor="nw")

        self.canvas.bind("<Configure>", self._resize_grid)

    def _resize_grid(self, event):
        self.canvas.itemconfigure(self.grid_id, width=event.width)