Tkinter scrollbar for frame

后端 未结 4 773
难免孤独
难免孤独 2020-11-22 09:47

My objective is to add a vertical scroll bar to a frame which has several labels in it. The scroll bar should automatically enabled as soon as the labels inside the frame ex

相关标签:
4条回答
  • 2020-11-22 10:19

    Am i doing it right?Is there better/smarter way to achieve the output this code gave me?

    Generally speaking, yes, you're doing it right. Tkinter has no native scrollable container other than the canvas. As you can see, it's really not that difficult to set up. As your example shows, it only takes 5 or 6 lines of code to make it work -- depending on how you count lines.

    Why must i use grid method?(i tried place method, but none of the labels appear on the canvas?)

    You ask about why you must use grid. There is no requirement to use grid. Place, grid and pack can all be used. It's simply that some are more naturally suited to particular types of problems. In this case it looks like you're creating an actual grid -- rows and columns of labels -- so grid is the natural choice.

    What so special about using anchor='nw' when creating window on canvas?

    The anchor tells you what part of the window is positioned at the coordinates you give. By default, the center of the window will be placed at the coordinate. In the case of your code above, you want the upper left ("northwest") corner to be at the coordinate.

    0 讨论(0)
  • 2020-11-22 10:19

    We can add scroll bar even without using Canvas. I have read it in many other post we can't add vertical scroll bar in frame directly etc etc. But after doing many experiment found out way to add vertical as well as horizontal scroll bar :). Please find below code which is used to create scroll bar in treeView and frame.

    f = Tkinter.Frame(self.master,width=3)
    f.grid(row=2, column=0, columnspan=8, rowspan=10, pady=30, padx=30)
    f.config(width=5)
    self.tree = ttk.Treeview(f, selectmode="extended")
    scbHDirSel =tk.Scrollbar(f, orient=Tkinter.HORIZONTAL, command=self.tree.xview)
    scbVDirSel =tk.Scrollbar(f, orient=Tkinter.VERTICAL, command=self.tree.yview)
    self.tree.configure(yscrollcommand=scbVDirSel.set, xscrollcommand=scbHDirSel.set)           
    self.tree["columns"] = (self.columnListOutput)
    self.tree.column("#0", width=40)
    self.tree.heading("#0", text='SrNo', anchor='w')
    self.tree.grid(row=2, column=0, sticky=Tkinter.NSEW,in_=f, columnspan=10, rowspan=10)
    scbVDirSel.grid(row=2, column=10, rowspan=10, sticky=Tkinter.NS, in_=f)
    scbHDirSel.grid(row=14, column=0, rowspan=2, sticky=Tkinter.EW,in_=f)
    f.rowconfigure(0, weight=1)
    f.columnconfigure(0, weight=1)
    
    0 讨论(0)
  • 2020-11-22 10:37

    Please note that the proposed code is only valid with Python 2

    Here is an example:

    from Tkinter import *   # from x import * is bad practice
    from ttk import *
    
    # http://tkinter.unpythonic.net/wiki/VerticalScrolledFrame
    
    class VerticalScrolledFrame(Frame):
        """A pure Tkinter scrollable frame that actually works!
        * Use the 'interior' attribute to place widgets inside the scrollable frame
        * Construct and pack/place/grid normally
        * This frame only allows vertical scrolling
    
        """
        def __init__(self, parent, *args, **kw):
            Frame.__init__(self, parent, *args, **kw)            
    
            # create a canvas object and a vertical scrollbar for scrolling it
            vscrollbar = Scrollbar(self, orient=VERTICAL)
            vscrollbar.pack(fill=Y, side=RIGHT, expand=FALSE)
            canvas = Canvas(self, bd=0, highlightthickness=0,
                            yscrollcommand=vscrollbar.set)
            canvas.pack(side=LEFT, fill=BOTH, expand=TRUE)
            vscrollbar.config(command=canvas.yview)
    
            # reset the view
            canvas.xview_moveto(0)
            canvas.yview_moveto(0)
    
            # create a frame inside the canvas which will be scrolled with it
            self.interior = interior = Frame(canvas)
            interior_id = canvas.create_window(0, 0, window=interior,
                                               anchor=NW)
    
            # track changes to the canvas and frame width and sync them,
            # also updating the scrollbar
            def _configure_interior(event):
                # update the scrollbars to match the size of the inner frame
                size = (interior.winfo_reqwidth(), interior.winfo_reqheight())
                canvas.config(scrollregion="0 0 %s %s" % size)
                if interior.winfo_reqwidth() != canvas.winfo_width():
                    # update the canvas's width to fit the inner frame
                    canvas.config(width=interior.winfo_reqwidth())
            interior.bind('<Configure>', _configure_interior)
    
            def _configure_canvas(event):
                if interior.winfo_reqwidth() != canvas.winfo_width():
                    # update the inner frame's width to fill the canvas
                    canvas.itemconfigure(interior_id, width=canvas.winfo_width())
            canvas.bind('<Configure>', _configure_canvas)
    
    
    if __name__ == "__main__":
    
        class SampleApp(Tk):
            def __init__(self, *args, **kwargs):
                root = Tk.__init__(self, *args, **kwargs)
    
    
                self.frame = VerticalScrolledFrame(root)
                self.frame.pack()
                self.label = Label(text="Shrink the window to activate the scrollbar.")
                self.label.pack()
                buttons = []
                for i in range(10):
                    buttons.append(Button(self.frame.interior, text="Button " + str(i)))
                    buttons[-1].pack()
    
        app = SampleApp()
        app.mainloop()
    

    It does not yet have the mouse wheel bound to the scrollbar but it is possible. Scrolling with the wheel can get a bit bumpy, though.

    edit:

    to 1)
    IMHO scrolling frames is somewhat tricky in Tkinter and does not seem to be done a lot. It seems there is no elegant way to do it.
    One problem with your code is that you have to set the canvas size manually - that's what the example code I posted solves.

    to 2)
    You are talking about the data function? Place works for me, too. (In general I prefer grid).

    to 3)
    Well, it positions the window on the canvas.

    One thing I noticed is that your example handles mouse wheel scrolling by default while the one I posted does not. Will have to look at that some time.

    0 讨论(0)
  • 2020-11-22 10:41

    Please see my class that is a scrollable frame. It's vertical scrollbar is binded to <Mousewheel> event as well. So, all you have to do is to create a frame, fill it with widgets the way you like, and then make this frame a child of my ScrolledWindow.scrollwindow. Feel free to ask if something is unclear.

    Used a lot from @ Brayan Oakley answers to close to this questions

    class ScrolledWindow(tk.Frame):
        """
        1. Master widget gets scrollbars and a canvas. Scrollbars are connected 
        to canvas scrollregion.
    
        2. self.scrollwindow is created and inserted into canvas
    
        Usage Guideline:
        Assign any widgets as children of <ScrolledWindow instance>.scrollwindow
        to get them inserted into canvas
    
        __init__(self, parent, canv_w = 400, canv_h = 400, *args, **kwargs)
        docstring:
        Parent = master of scrolled window
        canv_w - width of canvas
        canv_h - height of canvas
    
        """
    
    
        def __init__(self, parent, canv_w = 400, canv_h = 400, *args, **kwargs):
            """Parent = master of scrolled window
            canv_w - width of canvas
            canv_h - height of canvas
    
           """
            super().__init__(parent, *args, **kwargs)
    
            self.parent = parent
    
            # creating a scrollbars
            self.xscrlbr = ttk.Scrollbar(self.parent, orient = 'horizontal')
            self.xscrlbr.grid(column = 0, row = 1, sticky = 'ew', columnspan = 2)         
            self.yscrlbr = ttk.Scrollbar(self.parent)
            self.yscrlbr.grid(column = 1, row = 0, sticky = 'ns')         
            # creating a canvas
            self.canv = tk.Canvas(self.parent)
            self.canv.config(relief = 'flat',
                             width = 10,
                             heigh = 10, bd = 2)
            # placing a canvas into frame
            self.canv.grid(column = 0, row = 0, sticky = 'nsew')
            # accociating scrollbar comands to canvas scroling
            self.xscrlbr.config(command = self.canv.xview)
            self.yscrlbr.config(command = self.canv.yview)
    
            # creating a frame to inserto to canvas
            self.scrollwindow = ttk.Frame(self.parent)
    
            self.canv.create_window(0, 0, window = self.scrollwindow, anchor = 'nw')
    
            self.canv.config(xscrollcommand = self.xscrlbr.set,
                             yscrollcommand = self.yscrlbr.set,
                             scrollregion = (0, 0, 100, 100))
    
            self.yscrlbr.lift(self.scrollwindow)        
            self.xscrlbr.lift(self.scrollwindow)
            self.scrollwindow.bind('<Configure>', self._configure_window)  
            self.scrollwindow.bind('<Enter>', self._bound_to_mousewheel)
            self.scrollwindow.bind('<Leave>', self._unbound_to_mousewheel)
    
            return
    
        def _bound_to_mousewheel(self, event):
            self.canv.bind_all("<MouseWheel>", self._on_mousewheel)   
    
        def _unbound_to_mousewheel(self, event):
            self.canv.unbind_all("<MouseWheel>") 
    
        def _on_mousewheel(self, event):
            self.canv.yview_scroll(int(-1*(event.delta/120)), "units")  
    
        def _configure_window(self, event):
            # update the scrollbars to match the size of the inner frame
            size = (self.scrollwindow.winfo_reqwidth(), self.scrollwindow.winfo_reqheight())
            self.canv.config(scrollregion='0 0 %s %s' % size)
            if self.scrollwindow.winfo_reqwidth() != self.canv.winfo_width():
                # update the canvas's width to fit the inner frame
                self.canv.config(width = self.scrollwindow.winfo_reqwidth())
            if self.scrollwindow.winfo_reqheight() != self.canv.winfo_height():
                # update the canvas's width to fit the inner frame
                self.canv.config(height = self.scrollwindow.winfo_reqheight())
    
    0 讨论(0)
提交回复
热议问题