[英]Scrollable frame class in tkinter
I have implemented my own scrollable frame class in tkinter:我在 tkinter 中实现了我自己的可滚动框架 class:
class scrolledFrame(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.canvas = tk.Canvas(self)
self.canvas.pack(fill = "both", expand = True, side = "left")
self.scroll = tk.Scrollbar(self, command = self.canvas.yview)
self.scroll.pack(side = "right", fill = "y")
self.canvas.config(yscrollcommand = self.scroll.set)
self.content = tk.Frame(self.canvas)
self.content.bind("<Configure>", self.resizeCanvas)
self.contentWindow = self.canvas.create_window((0,0), window = self.content, anchor = "nw")
self.content.bind("<Enter>", self.enableScrollCanvas)
self.content.bind("<Leave>", self.disableScrollCanvas)
def scrollCanvas(self, event):
self.canvas.yview_scroll(int(-1*(event.delta/120)), "units")
def enableScrollCanvas(self, event):
self.canvas.bind_all("<MouseWheel>", self.scrollCanvas)
def disableScrollCanvas(self, event):
self.canvas.unbind_all("<MouseWheel>")
def resizeCanvas(self, event):
self.update_idletasks()
self.canvas.config(scrollregion = self.canvas.bbox("all"))
self.canvas.itemconfig(self.contentWindow, width = self.canvas.winfo_width())
root = tk.Tk()
exampleFrame = scrolledFrame(root)
exampleFrame.pack()
exampleLabel = tk.Label(exampleFrame.content, text = "I'm in the scrolled frame!")
exampleLabel.pack()
root.mainloop()
This works fine, but the problem is to add widgets to the scrolled frame, the parent has to be exampleFrame.content
.这工作正常,但问题是将小部件添加到滚动框架,父级必须是
exampleFrame.content
。 I have looked at several other examples which all have the same limitation.我查看了其他几个具有相同限制的示例。 Is it possible to configure the class so
exampleFrame
can be the parent of the widgets instead of exampleFrame.content
?是否可以配置 class 以便
exampleFrame
可以是小部件的父级而不是exampleFrame.content
? Thanks in advance.提前致谢。
If you don't mind a little trickery, you can simulate what you want.如果你不介意一点诡计,你可以模拟你想要的。 It's a bit of a hack though.
不过,这有点骇人听闻。
The trick is that when you call tk.Frame.__init__
, you need to be giving it the canvas as the parent, making self
the content frame.诀窍是,当您调用
tk.Frame.__init__
时,您需要将 canvas 作为父级,使self
成为内容框架。 Of course, to do that you have to create the canvas first, and to create the canvas you have to create the outer frame.当然,要做到这一点,你必须先创建 canvas,然后创建 canvas,你必须创建外框。
It looks something like this:它看起来像这样:
class scrolledFrame(tk.Frame):
def __init__(self, parent):
self.outer = tk.Frame(parent)
self.canvas = tk.Canvas(self.outer)
self.scroll = tk.Scrollbar(self.outer, command = self.canvas.yview)
tk.Frame.__init__(self, self.canvas)
self.contentWindow = self.canvas.create_window((0,0), window = self, anchor = "nw")
However, when you do the above and you try to call pack
, place
, or grid
on the instance of scrolledFrame
it's going to do the wrong thing since the instance points to the inner frame rather than the outer frame.但是,当您执行上述操作并尝试在
scrolledFrame
的实例上调用pack
、 place
或grid
时,它会做错事,因为实例指向内部框架而不是外部框架。
Here's the trickery: the solution to that is to redirect calls to pack
, place
, and grid
to the outer frame.诡计如下:解决方案是将对
pack
、 place
和grid
的调用重定向到外框。
class scrolledFrame(tk.Frame):
def __init__(self, parent):
...
self.pack = self.outer.pack
self.place = self.outer.place
self.grid = self.outer.grid
With that, you can use scrolledFrame
like you want, as long as you use pack
, place
, or grid
when adding it to the layout.这样,您就可以随意使用
scrolledFrame
,只要在将其添加到布局时使用pack
、 place
或grid
。
exampleFrame = scrolledFrame(root)
exampleFrame.pack(fill="both", expand=True)
for i in range(100):
exampleLabel = tk.Label(exampleFrame, text = f"I'm in the scrolled frame! ({i})")
exampleLabel.pack()
Here's a complete working example, though I've removed the mousewheel code for brevity.这是一个完整的工作示例,尽管为简洁起见,我删除了鼠标滚轮代码。
import tkinter as tk
class scrolledFrame(tk.Frame):
def __init__(self, parent):
self.outer = tk.Frame(parent)
self.canvas = tk.Canvas(self.outer)
self.scroll = tk.Scrollbar(self.outer, command = self.canvas.yview)
tk.Frame.__init__(self, self.canvas)
self.contentWindow = self.canvas.create_window((0,0), window = self, anchor = "nw")
self.canvas.pack(fill = "both", expand = True, side = "left")
self.scroll.pack(side = "right", fill = "y")
self.canvas.config(yscrollcommand = self.scroll.set)
self.bind("<Configure>", self.resizeCanvas)
self.pack = self.outer.pack
self.place = self.outer.place
self.grid = self.outer.grid
def resizeCanvas(self, event):
self.canvas.config(scrollregion = self.canvas.bbox("all"))
self.canvas.itemconfig(self.contentWindow, width = self.canvas.winfo_width())
root = tk.Tk()
exampleFrame = scrolledFrame(root)
exampleFrame.pack(fill="both", expand=True)
for i in range(100):
exampleLabel = tk.Label(exampleFrame, text = f"I'm in the scrolled frame! ({i})")
exampleLabel.pack(fill="x")
root.mainloop()
Thanks to @Novel for their help answering this question.感谢@Novel 帮助回答了这个问题。
The desired behaviour can be achieved by adding a __getattr__
method to the class.可以通过向 class 添加
__getattr__
方法来实现所需的行为。 This won't work if the class inherits from tk.Frame
so that's been removed.如果 class 从
tk.Frame
继承,这将不起作用,因此已将其删除。 Here's the code:这是代码:
class scrolledFrame():
def __init__(self, parent):
self.wrapper = tk.Frame(parent)
self.canvas = tk.Canvas(self.wrapper)
self.canvas.pack(fill = "both", expand = True, side = "left")
self.scroll = tk.Scrollbar(self.wrapper, command = self.canvas.yview)
self.scroll.pack(side = "right", fill = "y")
self.canvas.config(yscrollcommand = self.scroll.set)
self.content = tk.Frame(self.canvas)
self.content.bind("<Configure>", self.resizeCanvas)
self.contentWindow = self.canvas.create_window((0,0), window = self.content, anchor = "nw")
self.content.bind("<Enter>", self.enableScrollCanvas)
self.content.bind("<Leave>", self.disableScrollCanvas)
self.attrib = set(dir(tk.Widget))
def __getattr__(self, item):
if item in self.attrib:
return getattr(self.wrapper, item)
else:
return getattr(self.content, item)
def __str__(self):
return str(self.wrapper)
def scrollCanvas(self, event):
self.canvas.yview_scroll(int(-1*(event.delta/120)), "units")
def enableScrollCanvas(self, event):
self.canvas.bind_all("<MouseWheel>", self.scrollCanvas)
def disableScrollCanvas(self, event):
self.canvas.unbind_all("<MouseWheel>")
def resizeCanvas(self, event):
self.update_idletasks()
self.canvas.config(scrollregion = self.canvas.bbox("all"))
self.canvas.itemconfig(self.contentWindow, width = self.canvas.winfo_width())
root = tk.Tk()
exampleFrame = scrolledFrame(root)
exampleFrame.pack()
exampleLabel = tk.Label(exampleFrame, text = "I'm in the scrolled frame!")
exampleLabel.pack()
root.mainloop()
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.