[英]Tkinter: grid or pack inside a grid?
我正在構建一個軟件的GUI,並希望實現這一目標:
######################################
# | some title #
# menu upper |----------------------#
# | #
# | CANVAS #
# menu lower | #
# | #
#------------------------------------#
# statusbar #
######################################
菜單上部具有一些高級功能, 菜單下限根據用戶輸入而變化。 狀態欄經常更改其內容。
不幸的是,Tkinter拒絕工作。
使用網格布局管理器我無法創建穩定的設計,並在左側的菜單中添加標簽和按鈕等內容:
self.root = tk.Tk()
self.root.resizable(width=0, height=0)
self.root.title("some application")
# menu left
self.menu_left = tk.Frame(self.root, width=150, bg="#ababab")
self.menu_left.grid(row=0, column=0, rowspan=2, sticky="ns")
self.menu_left_upper = tk.Frame(self.menu_left, width=150, height=150, bg="red")
self.menu_left_upper.grid(row=0, column=0)
# this label breaks the design
#self.test = tk.Label(self.menu_left_upper, text="test")
#self.test.pack()
self.menu_left_lower = tk.Frame(self.menu_left, width=150, bg="blue")
self.menu_left_lower.grid(row=1, column=0)
# right area
self.some_title_frame = tk.Frame(self.root, bg="#dfdfdf")
self.some_title_frame.grid(row=0, column=1, sticky="we")
self.some_title = tk.Label(self.some_title_frame, text="some title", bg="#dfdfdf")
self.some_title.pack()
self.canvas_area = tk.Canvas(self.root, width=500, height=400, background="#ffffff")
self.canvas_area.grid(row=1, column=1)
self.root.mainloop()
這種設計在左側的菜單中沒有內容 。 每當我在self.menu_left_upper
或self.menu_left_lower
添加一些東西時,就像test
標簽一樣,我的設計破了:菜單框架消失了。 另外,即使有了柱子 ,我也不得不刪除狀態欄,因為當它被添加時,左邊的菜單再次消失了。
使用包布局管理器我得到了這個:
######################################
# | some title #
# |----------------------#
# menu upper | #
# | CANVAS #
# | #
# menu lower | #
# |----------------------#
# | statusbar #
######################################
因為我希望左邊的菜單框架消耗完整的y空間,所以我使用它來增長pack(side="left", expand=True, fill="both")
,但是這個設置總是拒絕狀態欄去全寬。
此外,純包管理器代碼看起來“丑陋” 。 我認為使用網格管理器的設計更“清晰”。 因此我認為網格布局中的網格或包布局會很好嗎?
有人可以幫忙嗎? 我陷入了GUI-hell:/
做布局的關鍵是有條不紊,並使用正確的工具來完成工作。 這也意味着有時你需要有創意。 這意味着在grid
放置東西時使用grid
grid
,而在從上到下或從左到右鋪設時使用pack
。
另一個關鍵是將布局代碼組合在一起。 當它在一個代碼塊中時,可視化和修改布局要容易得多。
在您的情況下,您似乎有三個或四個不同的區域,具體取決於您的計算方式。 如果要使用grid
,最簡單的方法是將“菜單上部”和“菜單下部”組合到一個框架中,並將整個框架視為表格單元格。 看起來你已經在做了,這很好。
那么,讓我們從這些主要領域開始:
self.menu_left.grid(row=0, column=0, rowspan=2, sticky="nsew")
self.some_title_frame.grid(row=0, column=1, sticky="ew")
self.canvas_area.grid(row=1, column=1, sticky="nsew")
# you don't have a status bar in the example code, but if you
# did, this is where you would put it
# self.status_frame.grid(row=2, column=0, columnspan=2, sticky="ew")
無論何時使用grid
,您都需要為至少一行和一列提供正權重,以便tkinter知道在何處使用任何未分配的空間。 通常有一個小部件是“主”小部件。 在這種情況下,它是畫布。 您希望確保畫布的行和列的權重為1(一):
self.root.grid_rowconfigure(1, weight=1)
self.root.grid_columnconfigure(1, weight=1)
注意:使用pack
代替grid
可以節省兩行代碼,因為pack
不需要像grid
那樣設置權重。
接下來,我們需要解決菜單區域的問題。 默認情況下,框架會縮小以適合其內容,這就是添加標簽會破壞布局的原因。 你沒有告訴tkinter如何處理額外的空間,所以框架縮小以適應,並且額外的空間未被使用。
由於您希望“menu_upper”和“menu_lower”分別占該區域的50%,因此pack
是最簡單的解決方案。 您可以使用grid
,但需要更多代碼行來添加行和列權重。
self.menu_left_upper.pack(side="top", fill="both", expand=True)
self.menu_left_lower.pack(side="top", fill="both", expand=True)
這是一個功能正常的版本,帶有狀態欄。 請注意它在調整窗口大小時的行為方式:
import Tkinter as tk
class Example():
def __init__(self):
self.root = tk.Tk()
self.root.title("some application")
# menu left
self.menu_left = tk.Frame(self.root, width=150, bg="#ababab")
self.menu_left_upper = tk.Frame(self.menu_left, width=150, height=150, bg="red")
self.menu_left_lower = tk.Frame(self.menu_left, width=150, bg="blue")
self.test = tk.Label(self.menu_left_upper, text="test")
self.test.pack()
self.menu_left_upper.pack(side="top", fill="both", expand=True)
self.menu_left_lower.pack(side="top", fill="both", expand=True)
# right area
self.some_title_frame = tk.Frame(self.root, bg="#dfdfdf")
self.some_title = tk.Label(self.some_title_frame, text="some title", bg="#dfdfdf")
self.some_title.pack()
self.canvas_area = tk.Canvas(self.root, width=500, height=400, background="#ffffff")
self.canvas_area.grid(row=1, column=1)
# status bar
self.status_frame = tk.Frame(self.root)
self.status = tk.Label(self.status_frame, text="this is the status bar")
self.status.pack(fill="both", expand=True)
self.menu_left.grid(row=0, column=0, rowspan=2, sticky="nsew")
self.some_title_frame.grid(row=0, column=1, sticky="ew")
self.canvas_area.grid(row=1, column=1, sticky="nsew")
self.status_frame.grid(row=2, column=0, columnspan=2, sticky="ew")
self.root.grid_rowconfigure(1, weight=1)
self.root.grid_columnconfigure(1, weight=1)
self.root.mainloop()
Example()
在一個不相關的說明:我強烈建議您不要刪除用戶調整窗口大小的能力。 他們比你更了解他們的要求。 如果您正確使用grid
和pack
,GUI將完美調整大小。
在self.root.mainloop()
之前添加以下代碼可以實現您正在尋找的內容
self.some_status = tk.Label(self.root, text="status bar", bg="#dfdfdf")
self.some_status.grid(row=3, column=0, columnspan=2, sticky="we")
通過加入:
menu_left_upper.grid_propagate(False)
在你的menu_left_upper
框架和menu_left_upper.mainloop()
這適用於:
默認情況下,容器窗口小部件會擴展或折疊,以便足以容納其內容。 因此,當您調用pack時,會導致幀縮小。 此功能稱為幾何體傳播 。
對於絕大多數應用程序,這是您想要的行為。 對於那些您想要明確設置容器大小的罕見情況,您可以關閉此功能。 要關閉它,請在容器上調用
pack_propagate
或grid_propagate
(取決於您是否在該容器上使用grid或pack),將其賦值為False
。
要獲取狀態欄,只需實現另一個框架和網格方法:
status_bar_frame = Frame(root, bg="#dfdfdf")
status_bar_frame.grid(row=3, column=0, columnspan=2, sticky="we")
status_bar = Label(status_bar_frame, text="status bar", bg="#dfdfdf")
status_bar.pack()
然后你的計划有效。 希望能幫助到你 :)
PS。 還有為什么所有的self
屬性?
編輯:你需要做的工作:
menu_left_upper = Frame(menu_left, width=225, height=225, bg="red")
menu_left_upper.grid_propagate(False)
menu_left_upper.grid(row=0, column=0)
# this label breaks the design
test = Label(menu_left_upper, text="test", bg='red')
test.grid()
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.