0

I was trying to get the scrollbar to work, in my tkinter application with this previous post: Adding a scrollbar to a group of widgets in Tkinter. Everything is working fine if I uncomment out the code before the for loop, but to my (limited) understanding, I believe that it should also work if I uncomment out the code inside of the for loop instead. As I understand it, calling canvas.configure(scrollregion=canvas.bbox("all")) should update the scrollregion of the canvas to include all of the label widgets that are placed inside of the frame. The first one just does it automatically every time a new label widget gets added into the frame with frame.bind("<Configure>",onFrameConfigure) while the second does it manually, so what's the difference? Why doesn't it work?

import tkinter

root = tkinter.Tk()
canvas = tkinter.Canvas(root)
frame = tkinter.Frame(canvas)

scrollbar=tkinter.Scrollbar(root,orient="vertical",command=canvas.yview)
canvas.configure(yscrollcommand=scrollbar.set)

scrollbar.pack(side="right", fill="y")
canvas.pack(fill="both", expand=True)

canvas.create_window((0, 0), window=frame, anchor="nw")

# works 
#def onFrameConfigure(event):
#    canvas.configure(scrollregion=canvas.bbox("all"))
# 
#frame.bind("<Configure>", onFrameConfigure)

for x in range(100):

     new_label = tkinter.Label(frame, text="%d"%x)
     new_label.pack()

     # doesn't work
     #canvas.configure(scrollregion=canvas.bbox("all"))

root.mainloop()
JJP7
  • 33
  • 1
  • 7

1 Answers1

1

The first one just does it automatically every time a new label widget gets added into the frame with frame.bind("",onFrameConfigure) while the second does it manually, so what's the difference? Why doesn't it work?

The difference is that calling bbox in response to an event happens after the frame has actually been drawn on the screen. Calling it within the loop means it is called before the widgets are visible and their size is indeterminate.

Calling bbox inside the loop will work if you call update_idletasks (or maybe update, depending on the platform) inside the loop to force the widgets to be drawn on the screen. Though, it would be far more efficient to call it immediately after the loop has complete so that it is called once after all widgets have been added.

Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • Thanks for the answer Bryan, I also found this question https://stackoverflow.com/questions/42237310/tkinter-canvas-scrollbar?rq=1 just now, and just did root.update() then canvas.configure(scrollregion=canvas.bbox("all")) outside of the for loop and it worked, thanks. – JJP7 Mar 09 '20 at 04:15
  • Also, by the way, I was wondering why wasn't root.update() needed inside of the callback function doesn't it also need to draw the things and have the sizes before you call the bbox? I think that the event parameter had something to do with this. I searched more for a bit and found this: https://stackoverflow.com/questions/29158220/tkinter-understanding-mainloop so root.mainloop() actually does some event handling? So is the frame only drawn at root.mainloop()? it does a "root.update" and then calls the callback so that the root.update() wasn't necessary? – JJP7 Mar 11 '20 at 03:46
  • Also, you said that bbox should be called after the things have been drawn, but doesn't pack() already draw and place them on the frame? So pack() doesn't actually draw things, but it was actually the root.mainloop() that was doing it? So, only root.update_idletasks(), root.update(), and root.mainloop() draws things? is that right? pack() doesn't actually draw things, it just kind of "places" them? – JJP7 Mar 11 '20 at 03:47
  • @JJP7: _"so root.mainloop() actually does some event handling"_ - `root.mainloop()` does _all_ event handling. Requests to redraw the screen are one type of event that it handles in addition to button clicks and typing on the keyboard. When it has to draw or redraw a widget, after it is done it will figure a `` event. So, by definition the callback will be called after the widget has been drawn. That is why you don't have to call `update` from within the callback. – Bryan Oakley Mar 11 '20 at 04:39
  • @JJP7: _"doesn't pack() already draw and place them on the frame?"_ - not quite. It _requests_ that the widgets are drawn on the screen. The requests are added to the event queue. Until mainloop is allowed to process those events, or until you explicitly ask tkinter to process the events by calling `update`, the changes won't be visible. – Bryan Oakley Mar 11 '20 at 04:41