Home > Software design >  Making variable width Text widgets with tkinter, in a scrollable window
Making variable width Text widgets with tkinter, in a scrollable window

Time:05-17

I'm trying to make a GUI where I want several text widgets in a scrollable area, and that the width of the text widget should vary when the window size is changed. But I'm having trouble getting it to work. I think it has something to do with the create_window(), as I can make it work without the scroll function.

Example code below

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

CodePudding user response:

You need to do two things: you need to set the width of the embedded window when the canvas changes size, and use the anchor option when creating the window.

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)
  • Related