为什么place()不适用于画布中的框架中的小部件?

时间:2015-06-24 14:07:32

标签: python tkinter

我正在尝试创建一个可以包含其他小部件的可滚动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_widthinner_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

1 个答案:

答案 0 :(得分:5)

这是因为place不会调整父窗口的大小以容纳子窗口小部件。使用place时,您需要显式设置父框架的大小。您的标签在那里,但框架的默认大小为1x1像素,有效地使标签不可见。

来自official documentation for place

  

与许多其他几何管理器(例如打包器)不同   不会尝试操纵主数据的几何   窗口或从属窗口的父母(即它没有设置他们的   要求的尺寸)。要控制这些窗口的大小,请制作它们   窗口,如框架和画布,提供配置选项   为了这个目的。

如果您需要做的就是在具有绝对控制的可滚动画布上管理一堆小部件,只需使用画布方法create_window,这将允许您以精确的坐标将标签直接放在画布上。这比使用place将小部件放在框架中,然后将框架放在画布中要容易得多。