比.grid()更好的Tkinter几何管理器是什么?
我的抱怨
我现在正在深入学习Tkinter这个图形用户界面(GUI),发现它的.grid()
布局管理器有几个地方不太好用:
布局是根据里面最大的组件来决定的,这样会导致位置不准确。
在Windows 7上,使用Python 2.7.3时,程序似乎不太管我设置的行号,而是按照组件的顺序来排列。
我的代码
我现在正在做一个非常简单的文本编辑器,想在窗口顶部放几个按钮。但是我发现我的组件要么被放在屏幕中央巨大文本框的左边,要么右边,无法做到我想要的布局。
========Class __init__ Stuff============
def widget(self):#Place widgets here
#Save Button
self.saveButton = Button (self, text = "Save", command = self.saveMe)
self.saveButton.grid(column = 0, row = 0, sticky = W)
#Open Button
self.openButton = Button (self, text = "Open", command = self.openMe)
self.openButton.grid(column = 0, row = 1, sticky = W)
#Area where you write
self.text = Text (self, width = (root.winfo_screenwidth() - 20),
height = (root.winfo_screenheight() - 10))
self.text.grid(row = 2)
==============Mainloop/Command Stuff============
我的问题
有没有其他更准确的方法来使用.grid()
布局管理器,或者我应该换个方法来做呢?
谢谢!
4 个回答
我觉得 place
管理器是三种管理器中最直观、最准确的,因为它可以让你同时指定绝对和相对的长度来设置位置和大小:
mywidget.place(x=..., y=..., width=..., height=...) # absolute
mywidget.place(relx=..., rely=..., relwidth=..., relheight=...) # relative
# combination
pad_left=5
pad_right=5
pad_top=5
pad_bottom=5
mywidget.place(relx=0, x=pad_left, rely=0, y=pad_top,\
relwidth=1, width=-pad_left-pad_right,\
relheight=1, height=-pad_top-pad_bottom, anchor="e") # use the whole space except some padding
不知道为什么,大家似乎不太喜欢 .place
管理器,但我觉得它在定义小部件(也就是你界面上的各种元素)在父窗口调整大小时应该如何移动和调整大小方面做得特别好。
为了方便构建复杂的窗口,有时候我会用一个函数(我想这有点像模拟网格的行为)来帮助垂直放置,比如:
def offset_y(line_no):
if line_no == 0:
return 0
else:
return height_label + (line_no - 1)*height_button
mylabel.place(..., y=offset_y(0))
mybtn1.place(..., y=offset_y(1))
etc.
这样可以先放一个标签,然后再放一系列按钮。(当然,在这种情况下也可以使用 .pack
管理器,但我更喜欢 .place
管理器,因为它能让我更准确地理解当窗口调整大小时,东西会如何移动。)
选择“最佳”的布局管理器其实要看你想要做什么,没有一种布局管理器适合所有情况。在你这个具体的例子中,使用pack可能是更好的选择。还有一点要注意的是,在一个应用程序中可能会有不止一种“最佳”选择。通常在不同的地方同时使用grid和pack是很正常的。我很少创建一个不同时使用grid和pack的图形用户界面(GUI)。偶尔,我也会用到place。
pack特别适合把东西放在单行或单列中。例如,工具栏就是一个很好的使用pack的例子,因为你希望所有的按钮都对齐在左边。而grid,顾名思义,适合需要固定行和列的情况。place则在grid和pack都不合适的特殊情况下很有用,因为它可以让你把小部件放在精确的固定位置或相对位置。
我的建议是把你的应用程序的小部件分成几个组,然后为每个组使用合适的布局管理器。在你的例子中,有两个逻辑组:顶部的工具栏和底部的文本框加滚动条组合。所以,先创建两个框架。因为工具栏在上面,文本框在下面,使用pack是最合适的。
toolbar = tk.Frame(...)
main = tk.Frame(...)
toolbar.pack(side="top", fill="x", expand=False)
main.pack(side="bottom", fill="both", expand=True)
这样一来,你就得到了两个可以独立修改的区域。
对于工具栏,使用pack也是最自然的选择。不过在这种情况下,你希望按钮在左边,而不是在顶部或底部:
b1 = tk.Button(toolbar, ...)
b2 = tk.Button(toolbar, ...)
b1.pack(side="left")
b2.pack(side="left")
...
最后是底部区域。你的示例代码没有显示滚动条,但我假设你最终会想要添加它们。如果你同时使用水平和垂直滚动条,grid会很合适。我会这样布局:
hsb = tk.Scrollbar(main, ..., orient="horizontal", ...)
vsb = tk.Scrollbar(main, ..., orient="vertical", ...)
text = tk.Text(main, ...)
vsb.grid(row=0, column=1, sticky="ns")
hsb.grid(row=1, column=0, sticky="ew")
text.grid(row=0, column=0, sticky="nsew")
main.grid_rowconfigure(0, weight=1)
main.grid_columnconfigure(0, weight=1)
最后一点很重要——你总是需要给至少一行和一列设置权重。这告诉grid在窗口调整大小时,哪些行和列应该扩展或缩小。
你可以使用三种布局管理器,分别是 grid
、pack
和 place
。第三种是最灵活的,但使用起来也比较复杂。我个人更喜欢用 grid
。需要注意的是,你可以把小部件放在其他小部件里面,或者你可以指定 columnspan
。所以,如果你想要得到下面这样的布局:
-------------------------
| Button1 | Button2 |
-------------------------
| Big Widget |
-------------------------
使用 .grid
有两种常见的方法。第一种方法是使用 columnspan
:
import Tkinter as Tk
root = Tk.Tk()
b1 = Tk.Button(root,text="Button1")
b1.grid(row=0,column=0)
b2 = Tk.Button(root,text="Button2")
b2.grid(row=0,column=1)
big_widget = Tk.Canvas(root)
big_widget.grid(row=1,column=0,columnspan=2)
*注意,还有一个类似的选项叫 rowspan
。
第二种方法是使用 Frame
来放置你的按钮:
import Tkinter as Tk
root = Tk.Tk()
f = Tk.Frame(root)
f.grid(row=0,column=0)
#place buttons on the *frame*
b1 = Tk.Button(f,text="Button1")
b1.grid(row=0,column=0)
b2 = Tk.Button(f,text="Button2")
b2.grid(row=0,column=1)
big_widget = Tk.Canvas(root)
big_widget.grid(row=1,column=0) #don't need columnspan any more.
这种方法对于创建复杂布局非常有用——我真不知道如果不使用像 Frame
这样的对象,怎么能创建复杂的布局……