Tkinter自动滚动框架几乎正常工作。 Apreciate输入

时间:2017-04-26 18:44:14

标签: scroll tkinter scrollbar frame autoscroll

我正在尝试在tkinter中开发一个可滚动的框架,可以像普通框架一样使用。 感谢这个论坛中的许多提示,我开发了一些代码,如果我在根窗口中打包了一个scrolllframe,那就完全符合预期。

不幸的是,如果我使用地方或网格,它就会失败。

这里是Frame的代码

    import tkinter as tk

    class ScrollFrame(tk.Frame):  #this frame will be placed on a canvas that is in a frame that goes on the parent window
        class AutoScrollbar(tk.Scrollbar):
            def set(self, *args):
                if float(args[0])==0 and float(args[1])==1: self.grid_forget()
                else:
                    if self.cget('orient')=="vertical": self.grid(row=0,column=1,sticky="ns")
                    else: self.grid(row=1,column=0,sticky="ew")
                tk.Scrollbar.set(self, *args)

        def __init__(self, root,*args,**args2):
            self.outer_frame=tk.Frame(root,*args,**args2)   #this is the frame that will be packed in th parent window
            self.outer_frame.grid_columnconfigure(0, weight=1)
            self.outer_frame.grid_rowconfigure(0, weight=2)
            self.canvas = tk.Canvas(self.outer_frame, borderwidth=0, background="#ffffff")
            tk.Frame.__init__(self, self.canvas,*args,**args2)
            self.vscroll = ScrollFrame.AutoScrollbar(self.outer_frame, orient="vertical", command=self.canvas.yview)
            self.hscroll = ScrollFrame.AutoScrollbar(self.outer_frame, orient="horizontal",  command=self.canvas.xview)
            self.canvas.configure(yscrollcommand=self.vscroll.set, xscrollcommand=self.hscroll.set)

            self.canvas.create_window((0,0), window=self, anchor="nw")
            self.canvas.grid(row=0,column=0,sticky="news")
            self.hscroll.grid(row=1,column=0,sticky="ew")
            self.vscroll.grid(row=0,column=1,sticky="ns")

            self.bind("<Configure>", self.onFrameConfigure)

        def onFrameConfigure(self, event): #Adapt the scroll region   #does the resizing
            self.canvas.config(scrollregion=self.canvas.bbox("all"))
            self.canvas.config(width=event.width, height=event.height)

        #convenience functions so the ScrollFrame can be treated like a normal frame

        def destr_org(self):tk.Frame.destroy(self)
        def destroy(self):
            self.destroy=self.destr_org
            self.outer_frame.destroy()
        def pack(self,*args,**arg2):
            self.outer_frame.pack(*args,**arg2)
        def place(self,*args,**arg2):
            self.outer_frame.place(*args,**arg2)
        def grid(self,*args,**arg2):
            self.outer_frame.grid(*args,**arg2)
        def pack_forget(self,*args,**arg2):
            self.outer_frame.pack_forget(*args,**arg2)
        def place_forget(self,*args,**arg2):
            self.outer_frame.place_forget(*args,**arg2)
        def grid_forget(self,*args,**arg2):
            self.outer_frame.grid_forget(*args,**arg2)
        def config(self,*args,**arg2):
            self.outer_frame.config(*args,**arg2)
            tk.Frame.config(self,*args,**arg2)
        def configure(self,*args,**arg2):
            self.outer_frame.config(*args,**arg2)
            tk.Frame.config(self,*args,**arg2)

这里是我用来测试它的代码。只需取消注释f.place和f.grid行即可尝试。

    win=tk.Tk()
    f=ScrollFrame(win)

    for n in range(10):
        o=tk.Button(f,text="-----------------------"+str(n)+"------------------------")
        o.pack()

    f.pack(expand=True, fill=tk.BOTH)
    #f.place(x=0, y=0)
    #f.grid(column=0,row=0)    

由于我没有错误,我失去了一些,并会感谢提示为什么它不起作用。 我知道有可滚动框架的包,但我真的想要一个没有额外导入的框架。 它还有点比它更复杂,但这是因为我试图以一种可以填充和放置的方式设计它,就像一个tk.Frame

非常感谢

1 个答案:

答案 0 :(得分:0)

好的,我明白了。 这适用于pack()和place()(没有尝试让网格工作)(在Linux上) place()的关键是绑定父窗口的大小,而对于pack(),内框(= self)的大小调整似乎很重要。

即使小说的代码(见上面的评论)比我的更优雅,但它有时会给我一些错误: - )

            class ScrollFrame(tk.Frame):
                class AutoScrollbar(tk.Scrollbar):
                    def set(self, *args):
                        if float(args[0])==0 and float(args[1])==1: self.grid_forget()
                        else:
                            if self.cget('orient')=="vertical": self.grid(row=0,column=1,sticky="ns")
                            else: self.grid(row=1,column=0,sticky="ew")
                        tk.Scrollbar.set(self, *args)

                def __init__(self, root,*args,**args2):
                    self.rootwin=root
                    self.dir=tk.BOTH
                    if "dir" in args2:
                        self.dir=args2["dir"]
                        del(args2["dir"])
                    self.outer_frame=tk.Frame(root,*args,**args2)
                    self.outer_frame.grid_columnconfigure(0, weight=1)
                    self.outer_frame.grid_rowconfigure(0, weight=2)
                    self.canvas = tk.Canvas(self.outer_frame, borderwidth=0, background="#ffffff")
                    tk.Frame.__init__(self, self.canvas,*args,**args2)
                    if self.dir==tk.Y or self.dir==tk.BOTH :
                        self.vscroll = ScrollFrame.AutoScrollbar(self.outer_frame, orient="vertical", command=self.canvas.yview)
                        self.canvas.configure(yscrollcommand=self.vscroll.set)
                        self.vscroll.grid(row=0,column=1,sticky="ns")
                    if self.dir==tk.X or self.dir==tk.BOTH :
                        self.hscroll = ScrollFrame.AutoScrollbar(self.outer_frame, orient="horizontal",  command=self.canvas.xview)
                        self.canvas.configure(xscrollcommand=self.hscroll.set)
                        self.hscroll.grid(row=1,column=0,sticky="ew")
                    self.canvas.create_window((0,0), window=self, anchor="nw")
                    self.canvas.grid(row=0,column=0,sticky="news")

                    self.canvas.bind("<Enter>", self._bind_mouse)
                    self.canvas.bind("<Leave>", self._unbind_mouse)

                def onFrameConfigure(self, event): #Adapt the scroll region
                    bb=self.canvas.bbox("all")
                    self.canvas.config(scrollregion=bb)
                    self.canvas.config(height=event.height,width=event.width)
                def onRootConf(self, event):
                    bb=self.canvas.bbox("all")
                    self.canvas.config(scrollregion=bb)
                    w=bb[2]-bb[0]
                    h=bb[3]-bb[1]
                    rw=self.rootwin.winfo_width()-self.outer_frame.winfo_x()-20*int(self.vscroll.winfo_ismapped())
                    rh=self.rootwin.winfo_height()-self.outer_frame.winfo_y()-20*int(self.hscroll.winfo_ismapped())
                    if rh<h and (self.dir==tk.Y or self.dir==tk.BOTH):
                        h=rh
                    if rw<w and (self.dir==tk.X or self.dir==tk.BOTH):
                        w=rw
                    self.canvas.config(height=h,width=w)

                def destr_org(self):tk.Frame.destroy(self)
                def destroy(self):
                    self.destroy=self.destr_org
                    self.outer_frame.destroy()
                def pack(self,*args,**arg2):
                    self.bind("<Configure>", self.onFrameConfigure)
                    self.outer_frame.pack(*args,**arg2)
                def place(self,*args,**arg2):
                    self.outer_frame.place(*args,**arg2)
                    self.bind("<Configure>",self.onRootConf)
                    self.rootwin.bind("<Configure>",self.onRootConf)
                def grid(self,*args,**arg2):
                    self.outer_frame.grid(*args,**arg2)
                def pack_forget(self,*args,**arg2):
                    self.outer_frame.pack_forget(*args,**arg2)
                def place_forget(self,*args,**arg2):
                    self.outer_frame.place_forget(*args,**arg2)
                def grid_forget(self,*args,**arg2):
                    self.outer_frame.grid_forget(*args,**arg2)
                def config(self,*args,**arg2):
                    self.outer_frame.config(*args,**arg2)
                    tk.Frame.config(self,*args,**arg2)
                def configure(self,*args,**arg2):
                    self.outer_frame.config(*args,**arg2)
                    tk.Frame.config(self,*args,**arg2)
                def winfo_ismapped(self):
                    return self.outer_frame.winfo_ismapped()

                def _bind_mouse(self, event=None):
                    self.canvas.bind_all("<4>", self._on_mousewheel)
                    self.canvas.bind_all("<5>", self._on_mousewheel)
                    self.canvas.bind_all("<MouseWheel>", self._on_mousewheel)
                def _unbind_mouse(self, event=None):
                    self.canvas.unbind_all("<4>")
                    self.canvas.unbind_all("<5>")
                    self.canvas.unbind_all("<MouseWheel>")
                def _on_mousewheel(self, event):    
                    if event.num == 4 or event.delta == 120: self.canvas.yview_scroll(-1, "units" )
                    elif event.num == 5 or event.delta == -120: self.canvas.yview_scroll(1, "units" )