1

I know there is a lot of similar questions here, but none of these that i found seems to work for me.Here is the deal: I need to load dynamically checkboxes inside a frame, but i dont know how many can they be.So I need a scrollbar.I've read that there is no way to put a scrollbar into frame, unless i put the frame into canvas and add the scrollbar to the canvas.I think the problem is something about scrollregion,but I'm not very sure.

Here is that part of my code :

def checkboxes_container(self):
    # Checkboxes frame
    self.checkboxes_frame = \
        tk.Frame(self,
                 height=450,
                 bg='red', bd=0,
                 highlightthickness=0)
    # Canvas widget to add scroll to the checkboxes holder
    self.canvas = \
        tk.Canvas(self.checkboxes_frame,
                  bg='blue', bd=0,
                  highlightthickness=0)
    # Canvas sizer
    canvas_sizer = tk.Frame(self.canvas, height=350,
                            bg='#444444', bd=0,
                            highlightthickness=0)
    canvas_sizer.pack(side=tk.LEFT)
    # Checkboxes holder
    self.checkbox_pane = \
        tk.Frame(self.canvas,
                 bg='#444444', bd=0,
                 highlightthickness=0)
    self.checkbox_pane.grid_propagate(False)
    # Scrollbar for checkbox pane
    self.scrollbar = tk.Scrollbar(self.checkboxes_frame,
                                  bg='grey', bd=0,
                                  activebackground='#A3A3A3',
                                  troughcolor='#444444',
                                  width=16,
                                  orient=tk.VERTICAL)

    self.canvas.create_window(0, 0, window=self.checkbox_pane)
    # Grid holder
    self.checkboxes_frame.grid(row=1, column=0, sticky=tk.W+tk.E)
    # Grid widgets to the holder
    self.canvas.pack(expand=True, side=tk.LEFT, fill=tk.BOTH)
    self.checkbox_pane.pack(expand=True, fill=tk.BOTH)
    self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

    self.scrollbar.config(command=self.canvas.yview)
    self.canvas.config(yscrollcommand=self.scrollbar.set,
                       scrollregion=
                       self.canvas.bbox('all'))

self is my basic frame widget where i put containers with other widgets(like this one)

I'll be very glad if someone help me.I'm trying to do this for all day...I must be very close...or very stupid

Thanks in advance Best regards

EDIT: Adding the edited code:

class ScrollableFrame(tk.Frame):
    def __init__(self, master, **kwargs):
        tk.Frame.__init__(self, master, kwargs)

        # create a canvas object and a vertical scrollbar for scrolling it
        self.vscrollbar = tk.Scrollbar(self, orient=tk.VERTICAL)
        self.vscrollbar.pack(side='right', fill="y",  expand="false")
        self.canvas = tk.Canvas(self,
                                bg='#444444', bd=0,
                                height=350,
                                highlightthickness=0,
                                yscrollcommand=self.vscrollbar.set)
        self.canvas.pack(side="left", fill="both", expand="true")
        self.vscrollbar.config(command=self.canvas.yview)

        # reset the view
        self.canvas.xview_moveto(0)
        self.canvas.yview_moveto(0)

        # create a frame inside the canvas which will be scrolled with it
        self.interior = tk.Frame(self.canvas, kwargs)
        self.canvas.create_window(0, 0, window=self.interior, anchor="nw")


class Application(tk.Frame):
    # ...

    def checkboxes_container(self):
        self.checkbox_pane = ScrollableFrame(self,
                                             bg='#444444')
        self.checkbox_pane.grid(row=1, column=0,
                                columnspan=3,
                                sticky='nwes')
dragonator
  • 137
  • 5
  • 14

1 Answers1

3

It might be easier to create a class and then call it instead of Tkinter.Frame.

Note that there is an instance variable called interior that you need to use to display widgets inside the scrolling frame.

import Tkinter as tk

class ScrollableFrame(tk.Frame):
    def __init__(self, master, **kwargs):
        tk.Frame.__init__(self, master, **kwargs)

        # create a canvas object and a vertical scrollbar for scrolling it
        self.vscrollbar = tk.Scrollbar(self, orient=tk.VERTICAL)
        self.vscrollbar.pack(side='right', fill="y",  expand="false")
        self.canvas = tk.Canvas(self,
                                bg='#444444', bd=0,
                                height=350,
                                highlightthickness=0,
                                yscrollcommand=self.vscrollbar.set)
        self.canvas.pack(side="left", fill="both", expand="true")
        self.vscrollbar.config(command=self.canvas.yview)

        # reset the view
        self.canvas.xview_moveto(0)
        self.canvas.yview_moveto(0)

        # create a frame inside the canvas which will be scrolled with it
        self.interior = tk.Frame(self.canvas, **kwargs)
        self.canvas.create_window(0, 0, window=self.interior, anchor="nw")

        self.bind('<Configure>', self.set_scrollregion)


    def set_scrollregion(self, event=None):
        """ Set the scroll region on the canvas"""
        self.canvas.configure(scrollregion=self.canvas.bbox('all'))


if __name__ == '__main__':
    root = tk.Tk()
    checkbox_pane = ScrollableFrame(root, bg='#444444')
    checkbox_pane.pack(expand="true", fill="both")

    def button_callback():
        for x in range(1,20):
            tk.Checkbutton(checkbox_pane.interior, text="hello world! %s" % x).grid(row=x, column=0)

    btn_checkbox = tk.Button(checkbox_pane.interior, text="Click Me!", command=button_callback)
    btn_checkbox.grid(row=0, column=0)
    root.mainloop()

Code taken from here

Community
  • 1
  • 1
James Burke
  • 2,229
  • 1
  • 26
  • 34
  • That way woker almost perfectly...the problem now is that there seems to have no limit for the scrollregion...i can scroll up and down, no matter if I have items there...I'll edit the question to add the code I have now. – dragonator May 06 '14 at 03:19
  • You don't have the self.bind command (last line of VFrame.__init__) and the corresponding callback in your sample code. – James Burke May 06 '14 at 03:59
  • I found the problem ,but still don't know hoe to fix it.The problem is that I add the checkbuttons after calling the mainloop().This way theframe inside the canvas doesn't position properly and it's been cropped.How can I add the checkbuttons after clicking on a button ? – dragonator May 06 '14 at 13:23
  • I updated my answer to include a button with callback that adds the checkboxes. – James Burke May 06 '14 at 19:30
  • I dont thik you've tried that code.I've got the exact same behaviour.Scrollbar doen't resize and you are able to scroll up and down forever :) – dragonator May 07 '14 at 01:58
  • Your right... I just modified the existing code :( I updated it to what I had in my .py file now. – James Burke May 07 '14 at 21:36
  • I did noticed the lates changes you've made before try it.There are not the problem.Just run the code and see.Notice the scrollbar: doesn't resize and u can scroll up and down no matter if there are checkbuttons or not... – dragonator May 08 '14 at 05:35
  • and when u click the button ? – dragonator May 08 '14 at 10:41
  • Aha! I think I found the issue. You're using checkbox_pane.grid(...), whereas you need to use .pack(). updated answer. If you want to use grid you'll need to change the ScrollFrame widgets to grid also. – James Burke May 08 '14 at 20:36
  • I fix the problem otherwise.I found that if I grid the checkbutton before i grid the whole Scrollable frame to the body of the app everythinks looks fine.What I make is this : I have one major_pane attribute to my class,which holds different frames.So every time when I want to change the content of it, I make a new Scrollable frame with the contetns I need and then replace it with the old one.Is that a good practice or it is better to change only the content.Like this the code is more readable and the switching frames is easier - just create a new scrollable frame and replace the old one. – dragonator May 09 '14 at 12:07
  • I don't think it matters too much, as long as you're not duplicating code. If you need to, then it's better to have another frame inside the scrolling frame. – James Burke May 10 '14 at 04:41