I am making a scrollable GUI with tkinter
but the scrollbar isn't adjusting with the window size. So, if I resize the root window by dragging from the corner with mouse, I want the scrollbar to dynamically resize with the window. So basically, if the window is large enough, the scrollbar isn't needed, but if I resize it to be smaller, the scrollbar is needed. Here is my code:
import tkinter as tk
def makelabelframe(root, text, row, col):
label_frame = tk.LabelFrame(root, text=text)
label_frame.grid(row=row, column=col, sticky="news", padx=15, pady=10)
return label_frame
def makelabel(root, text, row, col):
lab = tk.Label(root, text=text)
lab.grid(row=row, column=col, sticky="news", padx=15, pady=10)
return lab
def makebutton(root, text, command, row, col):
button = tk.Button(root, text=text, command=command)
button.grid(row=row, column=col, sticky="news", padx=15, pady=10)
def makeframe(root, row, col):
frame = tk.Frame(root)
frame.grid(row=row, column=col, sticky="news", padx=15, pady=10)
return frame
def makecanvas(root, row, col):
canvas = tk.Canvas(root)
canvas.grid(row=row, column=col, sticky="news", padx=15, pady=10)
return canvas
def makescrollbar(root, canv, row, col):
scrollbar = tk.Scrollbar(root, orient=tk.VERTICAL, command=canv.yview)
scrollbar.grid(row=row, column=col, sticky="news", padx=15, pady=10)
return scrollbar
def main():
root = tk.Tk()
w, h = 500, 500
root.geometry("{}x{}".format(w, h))
mainframe = makeframe(root, 0, 0)
mainframe.place(relx=0.5, rely=0.5, anchor="center")
canv = makecanvas(mainframe, 0, 0)
sb = makescrollbar(mainframe, canv, 0, 1)
canv.configure(yscrollcommand=sb.set)
frame = makeframe(canv, 0, 1)
win = canv.create_window((0, 0), window=frame, anchor="center")
canv.bind_all("<MouseWheel>", lambda e: canv.yview_scroll(int(-e.delta/120), "units"))
frame.bind("<Configure>", lambda e: canv.configure(width=e.width, height=e.height*0.99, scrollregion=canv.bbox("all")))
button_func = lambda: print("Click")
labelframe1 = makelabelframe(frame, "Labelframe 1", 0, 0)
makelabel(labelframe1, "1", 0, 0)
makelabel(labelframe1, "Text in labelframe 1", 0, 1)
makelabel(labelframe1, "Button in labelframe 1", 1, 0)
makebutton(labelframe1, "Button 1", button_func, 1, 1)
labelframe2 = makelabelframe(frame, "Labelframe 2", 1, 0)
makelabel(labelframe2, "2", 0, 0)
makelabel(labelframe2, "Text in labelframe 2", 0, 1)
makelabel(labelframe2, "Button in labelframe 2", 1, 0)
makebutton(labelframe2, "Button 2", button_func, 1, 1)
labelframe3 = makelabelframe(frame, "Labelframe 3", 2, 0)
makelabel(labelframe3, "3", 0, 0)
makelabel(labelframe3, "Text in labelframe 3", 0, 1)
makelabel(labelframe3, "Button in labelframe 3", 1, 0)
makebutton(labelframe3, "Button 3", button_func, 1, 1)
labelframe4 = makelabelframe(frame, "Labelframe 4", 3, 0)
makelabel(labelframe4, "4", 0, 0)
makelabel(labelframe4, "Text in labelframe 4", 0, 1)
makelabel(labelframe4, "Button in labelframe 4", 1, 0)
makebutton(labelframe4, "Button 4", button_func, 1, 1)
root.mainloop()
main()
I have tried many things, such as doing this without mainframe.place()
function, but that does not work. I also tried to make a resize function:
def resize(e):
canv.itemconfig(win, height=e.height, width=e.width)
canv.bind("<Configure>", resize)
But something very weird happens with this function. I also tried to make the canv
first and the mainframe
after that, but it did not work. I also tried binding canv
, win
, mainframe
and root
to "<Configure>"
instead of frame , but none of that worked.
How could I fix this so that the scrollbar adjusts with the root window size if I make it smaller?
Note that canv
and sb
are children of mainframe
and mainframe
will not be resized when the window is resized. So both canv
and sb
will not be resized as well.
You need to:
mainframe
to resize whenever the window is resized by adding relheight=1
in mainframe.place(...)
canv
and sb
) of mainframe
by calling mainframe.rowconfigure(0, weight=1)
Note also that:
anchor="nw"
in canv.create_window(...)
canv
inside the lambda of frame.bind("<Configure>", ...)
Required changes:
def main():
...
mainframe.place(relx=0.5, rely=0.5, anchor="center", relheight=1) # added relheight=1
mainframe.rowconfigure(0, weight=1) # allocate available space vertically to row 0
...
win = canv.create_window((0, 0), window=frame, anchor="nw") # used anchor="nw" instead
...
# remove setting height in canv.configure(...)
frame.bind("<Configure>", lambda e: canv.configure(width=e.width, scrollregion=canv.bbox("all")))
...