2

I'd like to make a scrollable frame which fills entire cell in app window. When the window is resized the widgets in the frame should stay centered. Since frames are not scrollable I used canvas and placed a frame in its window. Scroll region was associated with the frame. However I cannot make the frame expand and fill entire canvas area.

I tried to create a window in canvas for the frame having entire canvas width and associate the change of width of the window with canvas configuration event. However I get a wired result. The frame occupies only the right part of the window. When I expand the window it moves to the left. I colored the canvas in yellow and the frame in green to make things visible. Thanks for any help!

import tkinter as tk


class FrameWithScrollBar(tk.Frame):

    def __init__(self, parent, *args, **kwargs):
        super().__init__(parent, *args, **kwargs)
        self.canvas = tk.Canvas(self, bg='yellow')
        self.frame = tk.Frame(self.canvas, bg='green')
        self.scrollbar = tk.Scrollbar(self, orient='vertical',
                                command=self.canvas.yview)
        self.canvas.configure(yscrollcommand=self.scrollbar.set)
        self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        self.scrollbar.pack(side=tk.RIGHT, fill=tk.BOTH)
        self.frame.pack(fill=tk.BOTH, expand=True)
        self._frame_id = self.canvas.create_window(
                                 self.canvas.winfo_reqwidth(), 0,
                                 anchor='n',
                                 window=self.frame)
        self.frame.bind('<Configure>', self.onFrameConfigure)
        self.canvas.bind('<Configure>', self.onCanvasConfigure)

    def onFrameConfigure(self, event):       
        self.canvas.configure(scrollregion=self.frame.bbox('all'))

    def onCanvasConfigure(self, event):
        width = event.width
        self.canvas.itemconfigure(self._frame_id, width=width)


if __name__ == '__main__':

    root = tk.Tk()
    fws = FrameWithScrollBar(root)
    buttons = list()
    for i in range(5):
        for j in range(25):
            button = tk.Button(fws.frame, text='Button ' + str(i) + ','+str(j))
            button.grid(row=j, column=i, sticky='wesn')
            tk.Grid.columnconfigure(fws.frame, j, weight=1)
    fws.pack(expand=True, fill=tk.BOTH)
    root.mainloop()
ilya_s
  • 71
  • 4
  • The canvas acts like a geometry manager, therefore `Frame` looses the ability to use `.columnconfigure(fws.frame, j, weight=1)`. Beside this `j` is the row, colum is `i`. – stovfl Sep 01 '19 at 09:32

1 Answers1

5

Thanks, stovfl! Working code in case someone would need it


import tkinter as tk


class FrameWithScrollBar(tk.Frame):

    def __init__(self, parent, *args, **kwargs):
        super().__init__(parent, *args, **kwargs)
        self.canvas = tk.Canvas(self, bg='yellow')
        self.frame = tk.Frame(self.canvas, bg='green')
        self.scrollbar = tk.Scrollbar(self, orient='vertical',
                                command=self.canvas.yview)
        self.canvas.configure(yscrollcommand=self.scrollbar.set)
        self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        self.scrollbar.pack(side=tk.RIGHT, fill=tk.BOTH)
        self.frame.pack(fill=tk.BOTH, expand=True)
        self._frame_id = self.canvas.create_window(
                                 self.canvas.winfo_width(), 0,
                                 anchor='nw',
                                 window=self.frame)
        self.frame.bind('<Configure>', self.onFrameConfigure)
        self.canvas.bind('<Configure>', self.onCanvasConfigure)

    def onFrameConfigure(self, event):       
        self.canvas.configure(scrollregion=self.frame.bbox('all'))

    def onCanvasConfigure(self, event):
        width = event.width
        self.canvas.itemconfigure(self._frame_id, width=self.canvas.winfo_width())


if __name__ == '__main__':

    root = tk.Tk()
    fws = FrameWithScrollBar(root)
    buttons = list()
    for i in range(5):
        for j in range(25):
            button = tk.Button(fws.frame, text='Button ' + str(i) + ','+str(j))
            button.grid(row=j, column=i, sticky='wesn')
            tk.Grid.columnconfigure(fws.frame, i, weight=1)
    fws.pack(expand=True, fill=tk.BOTH)
    root.mainloop()
ilya_s
  • 71
  • 4