Python 为什么place()不适用于画布框架中的小部件?
我正在尝试创建一个可滚动的PythonTkinter小部件,它可以包含其他小部件。下面的伪代码(主要是从答案中提取出来的,然后根据我的风格进行调整)似乎几乎完全符合我的要求:Python 为什么place()不适用于画布框架中的小部件?,python,tkinter,Python,Tkinter,我正在尝试创建一个可滚动的PythonTkinter小部件,它可以包含其他小部件。下面的伪代码(主要是从答案中提取出来的,然后根据我的风格进行调整)似乎几乎完全符合我的要求: import Tkinter as tk class ScrollFrame(tk.Frame): def __init__(self, root, *args, **kwargs): # Start up self tk.Frame.__init__(self, root, *a
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()
这是因为
place
不会调整父窗口的大小以容纳子窗口小部件。使用place
时,需要显式设置父帧的大小。标签在那里,但帧的默认大小为1x1像素,这实际上使标签不可见
从:
与许多其他几何体管理器(如封隔器)不同,砂矿
不尝试操纵主控形状的几何图形
窗口或从属窗口的父窗口(即,它不设置其
要求的尺寸)。要控制这些窗口的大小,请使其
提供配置选项的类似窗口的框架和画布
为此目的
如果您需要做的只是在具有绝对控制权的可滚动画布上管理一组小部件,只需使用画布方法
create\u window
,它将允许您在画布上的精确坐标处直接放置标签。这比使用place将小部件放在一个框架中,然后将框架放在画布中要容易得多。您可以使用网格
布局管理器完美地控制小部件的放置位置,您只需要知道它是如何工作的。我真的不鼓励使用place
,可能是因为我从未使用过它(可能只使用过一次),但无论如何,这不是你问题的解决方案,只是一个建议(可能)…@Christopher Yeah,我知道在使用Tkinter时通常不鼓励使用place()。我不知道我是否可以使用grid()而不引起奇怪,但即使我可以,转换所有与UI相关的代码也需要很多小时,我不希望这样做。:)@克里斯托弗·瓦莱斯:那不是真的——你不能完全控制小部件在网格中的位置,除非你走极端,为每个像素创建一个网格单元的布局。必须在画布中使用框架吗?Canvas对象有一个名为create_window的方法,该方法获取另一个小部件并“将其放入其中”。@ChristopherWallace:哦,我同意!你确实有“必要和充分的控制”。然而,您的声明是您对网格有“完美”的控制,我认为这并不准确place
为您提供绝对(“完美”)的控制,grid
和pack
都非常方便,而且几乎总能产生更好的结果。我从不推荐地点
,除非是最不寻常的情况。在过去几十年的tk使用中,我使用了place
大概3-4次。接受!我不会按照你的建议使用create\u window
(只是为了与其他小部件保持一致,它们确实使用place
)。相反,在实例化内部框架时,我将一个inner_width和inner_height参数传递给ScrollFrame类,然后将其传递给内部框架的width和height参数。(即,在创建滚动框时,指定可以滚动的区域大小。)
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()