我正在尝试在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
非常感谢
答案 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" )