This is related to a previous question: Tkinter dynamically create widgets from button
At the time that I asked the previous question, I believed that it would be easy to add a scrollable frame around the dynamic GUI. Instead, I have had a single problem with the scrollbar not detecting the new frames and entry boxes after the button is pressed. How do I solve this without editing the ScrollFrame class much?
I know that the Scrollbarframe works with other widgets it is just that the dynamic component is causing issues. When I shrink the vertical size of the window past the original location of the createWidgets button, the scrollbar appears, but the scrollbar is not present for the rest of the dynamically created widgets. Does the canvas not detect that the vertical size of the frame increases with a button press?
Note: I am aware that wildcard imports are awful. I'm just using one for the example
from tkinter import *
class AutoScrollbar(Scrollbar):
# A scrollbar that hides itself if it's not needed.
# Only works if you use the grid geometry manager!
def set(self, lo, hi):
if float(lo) <= 0.0 and float(hi) >= 1.0:
# grid_remove is currently missing from Tkinter!
self.tk.call("grid", "remove", self)
else:
self.grid()
Scrollbar.set(self, lo, hi)
def pack(self, **kw):
raise TclError("cannot use pack with this widget")
def place(self, **kw):
raise TclError("cannot use place with this widget")
class ScrollFrame:
def __init__(self, master):
self.vscrollbar = AutoScrollbar(master)
self.vscrollbar.grid(row=0, column=1, sticky=N+S)
self.hscrollbar = AutoScrollbar(master, orient=HORIZONTAL)
self.hscrollbar.grid(row=1, column=0, sticky=E+W)
self.canvas = Canvas(master, yscrollcommand=self.vscrollbar.set,
xscrollcommand=self.hscrollbar.set)
self.canvas.grid(row=0, column=0, sticky=N+S+E+W)
self.vscrollbar.config(command=self.canvas.yview)
self.hscrollbar.config(command=self.canvas.xview)
# make the canvas expandable
master.grid_rowconfigure(0, weight=1)
master.grid_columnconfigure(0, weight=1)
# create frame inside canvas
self.frame = Frame(self.canvas)
self.frame.rowconfigure(1, weight=1)
self.frame.columnconfigure(1, weight=1)
def update(self):
self.canvas.create_window(0, 0, anchor=NW, window=self.frame)
self.frame.update_idletasks()
self.canvas.config(scrollregion=self.canvas.bbox("all"))
if self.frame.winfo_reqwidth() != self.canvas.winfo_width():
# update the canvas's width to fit the inner frame
self.canvas.config(width = self.frame.winfo_reqwidth())
if self.frame.winfo_reqheight() != self.canvas.winfo_height():
# update the canvas's width to fit the inner frame
self.canvas.config(height = self.frame.winfo_reqheight())
frames = []
widgets = []
def createwidgets():
global widgetNames
global frameNames
frame = Frame(o.frame, borderwidth=2, relief="groove")
frames.append(frame)
frame.pack(side="top", fill="x")
widget = Entry(frame)
widgets.append(widget)
widget.pack(side="left")
root = Tk()
o = ScrollFrame(root)
label = Label(o.frame, text = "test")
label1 = Label(o.frame, text = "test")
label2 = Label(o.frame, text = "test")
label3 = Label(o.frame, text = "test")
label.pack()
label1.pack()
label2.pack()
label3.pack()
createWidgetButton = Button(o.frame, text="createWidgets",
command=createwidgets)
createWidgetButton.pack(side="bottom", fill="x")
o.update()
root.mainloop()
This is what the window would look like if it was fully expanded

If I were to shrink the window, it should immediately create a vertical scrollbar because that would cover a widget. However, the scrollbar acts like the program was still in its initial state.
Incorrect Scrollbar(at the moment that the scrollbar appears)

You need to make sure that you update the canvas scrollregion whenever you add widgets to the inner frame. The most common solution is to bind to the frame's <Configure> event, which will fire whenever the frame changes size.
In ScrollFrame.__init__ add the following line after you create the frame:
self.frame.bind("<Configure>", self.reset_scrollregion)
Then, add this function to ScrollFrame:
def reset_scrollregion(self, event):
self.canvas.configure(scrollregion=self.canvas.bbox("all")
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With