0

I have created (with help) a Frame class with scrollbars on it. Seems to work well but I would like the buttons to size themselves to the size of the frame. Right now if you look they are all bunched up to the left side. I have looked through lots of examples and this seems common. Included below is some easy run code so you can see the issue. Attached below is a picture of the issue.

Thank you.

from tkinter import *


class ScrolledFrame(Frame):
    def __init__(self, top, *args, **kwargs):
        Frame.__init__(self, top, *args, **kwargs)

        hscrollbar = Scrollbar(self, orient=HORIZONTAL)
        hscrollbar.grid(row=1, column=0, sticky=E+W)

        vscrollbar = Scrollbar(self, orient=VERTICAL)
        vscrollbar.grid(row=0, column=1, sticky=N+S)

        self.canvas = Canvas(self, xscrollcommand=hscrollbar.set, yscrollcommand=vscrollbar.set)
        self.canvas.grid(row=0, column=0, sticky=N+S+E+W)

        hscrollbar.config(command = self.canvas.xview)
        vscrollbar.config(command = self.canvas.yview)

        # Make the canvas expandable
        self.grid_rowconfigure(0, weight=1)
        self.grid_columnconfigure(0, weight=1)

        # Create the canvas contents
        self.frame = Frame(self.canvas)
        self.frame.rowconfigure(1, weight=1)
        self.frame.columnconfigure(1, weight=1)

        self.canvas.create_window(0, 0, window=self.frame, anchor=N+W)
        self.canvas.config(scrollregion=self.canvas.bbox('all'))

        self.frame.bind('<Configure>', self.frame_changed)

    def frame_changed(self, event):
        self.frame.update_idletasks()
        self.canvas.config(scrollregion=self.canvas.bbox('all'))


root = Tk()
frame = ScrolledFrame(root)
frame.grid(row=0, column=0, sticky=N+E+W+S)
root.rowconfigure(0, weight=1)
root.columnconfigure(0, weight=1)

for i in range(20):
    button = Button(frame.frame, text='xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx %d' % i)
    button.pack(fill=BOTH, side=TOP, expand=True)

root.mainloop()

enter image description here

Carl Von
  • 180
  • 9

1 Answers1

1

Actually, your buttons are filling the frame. When you give the different elements background colors you can see this. I've made your outer frame red, your canvas green and your frame with the buttons blue:

You can see that it's actually the inner frame not filling up the canvas, rather than your buttons not filling up the frame.

To make the frame fill the canvas, as explained in the answer to this question, you can make a binding on the canvas <Configure> event to stretch the frame to the canvas size if the canvas is bigger than the minimum size of the buttons. to do this you must save the id of the placed frame on the canvas:

self.canvas_frame = self.canvas.create_window(0, 0, window=self.frame, anchor=N+W)
self.canvas.bind('<Configure>', self.canvas_changed)

def canvas_changed(self, event):
    if self.frame.winfo_reqwidth() < self.canvas.winfo_width():
        self.canvas.itemconfigure(self.canvas_frame, width=self.canvas.winfo_width())

To make sure that there is no horizontal scrollbar when it isn't necessary, you might want to reduce the width of the frame a bit (width=self.canvas.winfo_width()-4).

fhdrsdg
  • 10,297
  • 2
  • 41
  • 62