Search code examples
pythontkintercanvasscrollbar

Auto Resizing / Expanding Column with Vertical-Scrollbar


Is it possible to make a list out of widgets, like labels / buttons etc. in Y-Direction scrollable
but still let one column in X-Direction auto expand like in the code below?

import tkinter as tk

root = tk.Tk()
root.geometry("600x100")

frame = tk.Frame(root)
frame.pack(fill="both", expand=True)
frame.columnconfigure(1, weight=1)
frame.columnconfigure((0,2,3), minsize=100)
frame.rowconfigure(0, minsize=40)



coln = ["NO.", "Description","Name", "Button"]

for i in range(len(coln)):
    label = tk.Label(frame, text=coln[i], borderwidth=2, relief="solid")
    label.grid(column=i, row=0, sticky="NESW")

for i in range(20):
    label = tk.Label(frame, text=i+1, borderwidth=1, relief="solid")
    label.grid(column=0, row=i+1, sticky="NESW")

    label2 = tk.Label(frame, text="TEST_Descr.", borderwidth=1, relief="solid")
    label2.grid(column=1, row=i+1, sticky="NESW")

    label3 = tk.Label(frame, text="NAME", borderwidth=1, relief="solid")
    label3.grid(column=2, row=i+1, sticky="NESW")

    label4 = tk.Label(frame, borderwidth=1, relief="solid")
    label4.grid(column=3, row=i + 1, sticky="NESW")

    button = tk.Button(label4, text="OK!")
    button.pack(fill="x", expand=True)

root.mainloop()

Solution

  • You can use the common technique of adding the frame to a canvas for the scrolling. You can add a binding to the canvas so that when it resizes, you force the frame to be the width of the canvas minus the canvas border.

    import tkinter as tk
    
    root = tk.Tk()
    root.geometry("600x100")
    
    canvas = tk.Canvas(root, bg=root.cget("background"))
    scrollbar = tk.Scrollbar(root, command=canvas.yview)
    canvas.configure(yscrollcommand=scrollbar.set)
    
    scrollbar.pack(side="right", fill="y")
    canvas.pack(side="left", fill="both", expand=True)
    
    frame = tk.Frame(root, bg="pink")
    canvas.create_window(0, 0, anchor="nw", window=frame, tags=("inner_frame",))
    
    def canvas_resize(event):
        border = int(canvas.cget("borderwidth"))
        event.widget.itemconfigure("inner_frame", width=event.width-border*2)
    
    def frame_resize(event):
        canvas.configure(scrollregion=canvas.bbox("all"))
    
    canvas.bind("<Configure>", canvas_resize)
    frame.bind("<Configure>", frame_resize)
    
    frame.columnconfigure(1, weight=1)
    frame.columnconfigure((0,2,3), minsize=100)
    frame.rowconfigure(0, minsize=40)
    coln = ["NO.", "Description","Name", "Button"]
    
    for i in range(len(coln)):
        label = tk.Label(frame, text=coln[i], borderwidth=2, relief="solid")
        label.grid(column=i, row=0, sticky="NESW")
    
    for i in range(20):
        label = tk.Label(frame, text=i+1, borderwidth=1, relief="solid")
        label.grid(column=0, row=i+1, sticky="NESW")
    
        label2 = tk.Label(frame, text="TEST_Descr.", borderwidth=1, relief="solid")
        label2.grid(column=1, row=i+1, sticky="NESW")
    
        label3 = tk.Label(frame, text="NAME", borderwidth=1, relief="solid")
        label3.grid(column=2, row=i+1, sticky="NESW")
    
        label4 = tk.Label(frame, borderwidth=1, relief="solid")
        label4.grid(column=3, row=i + 1, sticky="NESW")
    
        button = tk.Button(label4, text="OK!")
        button.pack(fill="x", expand=True)
    
    root.mainloop()