我正在尝试创建一个可以包含其他小部件的可滚动Python Tkinter小部件。以下虚拟代码(主要取自this SO question的答案,然后根据我的风格进行调整)似乎几乎完全符合我的要求:
import Tkinter as tk
class ScrollFrame(tk.Frame):
def __init__(self, root, *args, **kwargs):
# Start up self
tk.Frame.__init__(self, root, *args, **kwargs)
# Put a canvas in the frame (self), along with scroll bars
self.canvas = tk.Canvas(self)
self.horizontal_scrollbar = tk.Scrollbar(
self, orient="horizontal", command=self.canvas.xview
)
self.vertical_scrollbar = tk.Scrollbar(
self, orient="vertical", command=self.canvas.yview
)
self.canvas.configure(
yscrollcommand=self.vertical_scrollbar.set,
xscrollcommand=self.horizontal_scrollbar.set
)
# Put a frame in the canvas, to hold all the widgets
self.inner_frame = tk.Frame(self.canvas)
# Pack the scroll bars and the canvas (in self)
self.horizontal_scrollbar.pack(side="bottom", fill="x")
self.vertical_scrollbar.pack(side="right", fill="y")
self.canvas.pack(side="left", fill="both", expand=True)
self.canvas.create_window((0,0), window=self.inner_frame, anchor="nw")
self.inner_frame.bind("<Configure>", self.OnFrameConfigure)
def OnFrameConfigure(self, event):
'''Reset the scroll region to encompass the inner frame'''
self.canvas.configure(scrollregion=self.canvas.bbox("all"))
root = tk.Tk()
frame = ScrollFrame(root, borderwidth=2, relief="sunken")
labels = []
for i in range(10):
labels.append(
tk.Label(
frame.inner_frame, text="Row {}".format(i) + "_"*20
) # Unfortunately, this widget's parent cannot just be frame but has to be frame.inner_frame
)
frame.place(x=20, y=20, width=150, height=150)
for i,label in enumerate(labels):
label.grid(row=i,column=0)
#label.place(x=0, y=20*i, width=100, height=20)
root.mainloop()
(有10个标签,每个标签最后都有一些垃圾邮件,用于测试垂直和水平滚动是否都有效。)
但是,对于我的实际应用,我需要非常精细地控制每个小部件的结束位置,这迫使我使用place
几何管理器而不是grid
。 (请注意,我place()
了框架。)但是,如果我将grid()
每个标签的行替换为使用place()
的行,则标签不会显示再也没有了。 (在上面的代码中,通过从底部注释掉第三行来模拟它,并从底部取消注释第二行。)
为什么这不起作用?我该如何解决?
编辑:
接受的答案让我通过将inner_width
和inner_height
传递给ScrollFrame
类的初始化,按照预期的方式工作,然后将width
类作为height
传递给inner_frame
类。 {}}}的{1}}和import Tkinter as tk
class ScrollFrame(tk.Frame):
def __init__(self, root, inner_width, inner_height, *args, **kwargs):
# Start up self
tk.Frame.__init__(self, root, *args, **kwargs)
# Put a canvas in the frame (self)
self.canvas = tk.Canvas(self)
# Put scrollbars in the frame (self)
self.horizontal_scrollbar = tk.Scrollbar(
self, orient="horizontal", command=self.canvas.xview
)
self.vertical_scrollbar = tk.Scrollbar(
self, orient="vertical", command=self.canvas.yview
)
self.canvas.configure(
yscrollcommand=self.vertical_scrollbar.set,
xscrollcommand=self.horizontal_scrollbar.set
)
# Put a frame in the canvas, to hold all the widgets
self.inner_frame = tk.Frame(
self.canvas, width=inner_width, height=inner_height
)
# Pack the scroll bars and the canvas (in self)
self.horizontal_scrollbar.pack(side="bottom", fill="x")
self.vertical_scrollbar.pack(side="right", fill="y")
self.canvas.pack(side="left", fill="both", expand=True)
self.canvas.create_window((0,0), window=self.inner_frame, anchor="nw")
self.inner_frame.bind("<Configure>", self.on_frame_configure)
def on_frame_configure(self, event):
"""Reset the scroll region to encompass the inner frame"""
self.canvas.configure(scrollregion=self.canvas.bbox("all"))
root = tk.Tk()
frame = ScrollFrame(root, 150, 200, borderwidth=2, relief="sunken")
labels = []
for i in range(10):
labels.append(
tk.Label(
frame.inner_frame, text="Row {}".format(i) + "_"*20, anchor="nw"
) # Unfortunately, this widget's parent cannot just be frame but has to be frame.inner_frame
)
frame.place(x=20, y=20, width=150, height=150)
for i,label in enumerate(labels):
#label.grid(row=i,column=0)
label.place(x=0, y=20*i, width=100, height=20)
root.mainloop()
参数:
grails install-plugin spring-security-core
答案 0 :(得分:5)
这是因为place
不会调整父窗口的大小以容纳子窗口小部件。使用place
时,您需要显式设置父框架的大小。您的标签在那里,但框架的默认大小为1x1像素,有效地使标签不可见。
来自official documentation for place:
与许多其他几何管理器(例如打包器)不同 不会尝试操纵主数据的几何 窗口或从属窗口的父母(即它没有设置他们的 要求的尺寸)。要控制这些窗口的大小,请制作它们 窗口,如框架和画布,提供配置选项 为了这个目的。
如果您需要做的就是在具有绝对控制的可滚动画布上管理一堆小部件,只需使用画布方法create_window
,这将允许您以精确的坐标将标签直接放在画布上。这比使用place将小部件放在框架中,然后将框架放在画布中要容易得多。