1

I use Python 2.7 and I have a scrollable frame where the canvas is not shrinking to fit the frame I want to make scrollable.

I looked at this question for an answer but it does not work when I run it: How to resize a scrollable frame to fill the canvas?

When I print the width of the frame inside the canvas, it says 0.

I also ran the code from the answer of this question on my computer : Scrollable Frame does not resize properly using tkinter in Python but it will still show the white canvas to the left of the labels, and it does not resize when the labels are deleted.

It looks like this:

Canvas not resizing to fit scrollable frame

This is my code, based on the answer in this question: Adding a scrollbar to a group of widgets in Tkinter

from Tkinter import *

class Scrollable_frame(Frame):

    def __init__(self, parent, title, values):

        self.parent = parent

        Frame.__init__(self, self.parent)

        self.canvas = Canvas(self, borderwidth=0, background="#ffffff")
        self.scrollbar = Scrollbar(self, command=self.canvas.yview)
        self.innerFrame = Radiobutton_frame(self.canvas,title,values)

        self.canvas.configure(yscrollcommand=self.scrollbar.set)

        self.canvas.grid(row=0, column=0, sticky= N+S)
        self.scrollbar.grid(row=0, column=1, sticky = N+S)
        self.canvas.create_window((0,0),window = self.innerFrame,anchor="nw")

        self.innerFrame.bind("<Configure>", self.set_canvas_scrollregion)

        self.grid_rowconfigure(0, weight=1)
        self.grid_columnconfigure(0, weight=1)


    def set_canvas_scrollregion(self, event):

        width = event.width - 4
        self.canvas.itemconfigure("self.innerFrame ", width=width)
        self.canvas.config(scrollregion=self.canvas.bbox("all"))


class Radiobutton_frame(LabelFrame):

    def __init__(self, parent, title, values):

        """            
        In: parent - Canvas
            title - String
            values - List of Int
        """

        self.radiobuttons = {}
        self.parent = parent

        self.selection = StringVar()
        self.selection.set("init")

        LabelFrame.__init__(self, self.parent, text = title)

        for value in values:
            self.add_radiobutton(value)


    def add_radiobutton(self, value):

        """
        Adds a radiobutton to the frame.

        In: item - String
        """

        # Associate to same variable to make them function as a group
        self.radiobuttons[value] = Radiobutton(master = self,
                                                variable = self.selection,
                                                text = value,
                                                value = value)
        self.radiobuttons[value].pack(anchor=W)

# Usage example

root = Tk()
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)
scrollableFrame = Scrollable_frame(root, "Canvas not resizing", range(30))
scrollableFrame.grid(row=0, column=0, sticky=N+S+E+W)

if __name__ == '__main__':
    root.mainloop()
gefdel
  • 59
  • 1
  • 7
  • Could you provide an image of what you'd want as well? – Nae Feb 03 '18 at 14:41
  • 1
    You say that you tried the suggestion to bind on the `` event of the canvas and it didn't work. That's the solution, so can you show what you tried and explain what "didn't work" means? – Bryan Oakley Feb 03 '18 at 14:48

1 Answers1

2

I don't think above question's code snippet fits to a Minimal, Complete, and Verifiable example but at the very least it's runnable.


You have three mistakes compared to that of: How to resize a scrollable frame to fill the canvas?

The most significant of which is that in the linked question, the OP uses the option tags where you don't. Replace:

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

with:

self.canvas.create_window((0,0),window = self.innerFrame, anchor="nw", tags="my_tag")

Another mistake is that you're binding the event of a frame's resizing as opposed to the actual Canvas' resizing, also pointed out in Bryan's comment here. Replace:

self.innerFrame.bind("<Configure>", self.set_canvas_scrollregion)

with:

self.canvas.bind("<Configure>", self.set_canvas_scrollregion)

Lastly, tkinter doesn't seem to accept space character with tags, replace:

self.canvas.itemconfigure("self.innerFrame ", width=width)

with:

self.canvas.itemconfigure("my_tag", width=width)

Finally, you should have:

from Tkinter import *

class Scrollable_frame(Frame):

    def __init__(self, parent, title, values):

        self.parent = parent

        Frame.__init__(self, self.parent)

        self.canvas = Canvas(self, borderwidth=0, background="#ffffff")
        self.scrollbar = Scrollbar(self, command=self.canvas.yview)
        self.innerFrame = Radiobutton_frame(self.canvas,title,values)

        self.canvas.configure(yscrollcommand=self.scrollbar.set)

        self.canvas.grid(row=0, column=0, sticky= N+S)
        self.scrollbar.grid(row=0, column=1, sticky = N+S)
        self.canvas.create_window((0,0),window = self.innerFrame,anchor="nw",
                                                                tags="my_tag")

        self.canvas.bind("<Configure>", self.set_canvas_scrollregion)

        self.grid_rowconfigure(0, weight=1)
        self.grid_columnconfigure(0, weight=1)


    def set_canvas_scrollregion(self, event):

        width = event.width - 4
        self.canvas.itemconfigure("my_tag", width=width)
        self.canvas.config(scrollregion=self.canvas.bbox("all"))


class Radiobutton_frame(LabelFrame):

    def __init__(self, parent, title, values):

        """            
        In: parent - Canvas
            title - String
            values - List of Int
        """

        self.radiobuttons = {}
        self.parent = parent

        self.selection = StringVar()
        self.selection.set("init")

        LabelFrame.__init__(self, self.parent, text = title)

        for value in values:
            self.add_radiobutton(value)


    def add_radiobutton(self, value):

        """
        Adds a radiobutton to the frame.

        In: item - String
        """

        # Associate to same variable to make them function as a group
        self.radiobuttons[value] = Radiobutton(master = self,
                                                variable = self.selection,
                                                text = value,
                                                value = value)
        self.radiobuttons[value].pack(anchor=W)

# Usage example

root = Tk()
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)
scrollableFrame = Scrollable_frame(root, "Canvas not resizing", range(30))
scrollableFrame.grid(row=0, column=0, sticky=N+S+E+W)

if __name__ == '__main__':
    root.mainloop()
Nae
  • 14,209
  • 7
  • 52
  • 79
  • Thanks a lot for the help, I appreciate your time and effort for helping me out. Actually I wanted the canvas to shrink to the inner frame size and not the inner frame to expand to fill the canvas. Its not a problem since I can hardcode the size of the canvas to be approximately the size of the inner frame. It surprised me that a print statement tells me the inner frame is 0 after it has been painted on the canvas, maybe somebody knows why? Again thank you Nae. – gefdel Feb 04 '18 at 22:12
  • Perhaps try `width=self.innerFrame.winfo_reqwidth()` on canvas. – Nae Feb 04 '18 at 22:24
  • I tried it but unfortunately the canvas shrinks below the frame width, so the frame shows nothing. I can still move the scrollbar. – gefdel Feb 14 '18 at 14:44