I am trying to dynamically add labels to a frame contained within a canvas, for scrollbar capability. The labels are being added via a function that is called from a button. The function works fine if called on startup, the frame updates as expected and I have scrollbar capabilities. If I call the function from the button command the frame updates with the labels, but only up to the limit of the starting frame/canvas size. The additional area of the frame containing the rest of the labels won't be visible as the scrollbar isn't "activated"
I tried searching but couldn't find this question being asked before. I am new to Python and Tkinter so apologies if I'm missing something obvious.
from tkinter import *
from tkinter import ttk
root = Tk()
root.geometry("550x550")
main_frame = Frame(root)
main_frame.grid(row = 0, column = 0)
button_frame = Frame(root)
button_frame.grid(row = 1, column = 0)
image_canvas_0 = Canvas(main_frame, height = 500, width = 500)
image_canvas_0.grid(row = 0, column = 0)
image_canvas_0_scrollbar = ttk.Scrollbar(main_frame, orient = VERTICAL, command = image_canvas_0.yview)
image_canvas_0_scrollbar.grid(column = 1, row = 0, sticky = (N,S))
image_canvas_0.config(yscrollcommand = image_canvas_0_scrollbar.set)
image_canvas_0.bind('<Configure>', lambda e: image_canvas_0.configure(scrollregion = image_canvas_0.bbox("all")))
second_frame = Frame(image_canvas_0)
image_canvas_0.create_window((0,0), window = second_frame, anchor = 'nw')
def test_function(*args):
for i in range(100):
label_text = 'test' str(i)
Label(second_frame, text = label_text).grid(row = i, column = 0)
func_button = Button(button_frame, text = 'Click Me', command = test_function)
func_button.grid(row = 0, column = 0)
#test_function()
root.mainloop()
CodePudding user response:
You aren't changing the scrollregion
after you've added widgets to the frame. The canvas doesn't know that there's new data that should be scrollable.
Normally this is done by adding a bind to the frame's <Configure>
event, since that event fires whenever the frame changes size.
second_frame.bind("<Configure>", lambda e: image_canvas_0.configure(scrollregion = image_canvas_0.bbox("all")))
The reason your code seems to work when calling the function directly at startup is that you have a similar binding on the canvas itself, which automatically fires once the widget is actually shown on the screen. That happens after you call test_function()
when mainloop
first starts to run. Once the program starts, the canvas <Configure>
event doesn't fire a second time.