以下內容為本人的學習筆記,如需要轉載,請聲明原文鏈接 微信公眾號「ENG八戒」https://mp.weixin.qq.com/s/B1hH5Qzd2RkAiiUId1tLWw
(資料圖片僅供參考)
本文大概 2874 個字,閱讀需花 10 分鐘內容不多,但也花了一些精力如要交流,歡迎關注我然后評論區留言
進入正文之前先說一件小事,本公眾號已改名為【ENG八戒】,原名是【englyf】。改名的理由是什么?以后會告訴朋友們的!
這篇文章屬于系列文章《Python 內置界面開發框架 Tkinter入門篇》的第三篇,上接《Python 內置界面開發框架 Tkinter入門篇乙》,歡迎關注我的微信公眾號「ENG八戒」查看這個系列相關文章。
界面布局
關于 Tkinter 框架的 GUI 布局,其實官方沒有提供對應的圖形化工具可用,但是網上有一些開源的小工具可以使用。這里不打算介紹這些小工具的使用,而是直接用框架提供的幾何圖形管理器來布局,比如上面提到過的 pack() 就是其中一種。這里提到的幾何圖形管理器也就是其它框架里常說的布局管理器。
Tkinter 框架提供的布局管理器有:pack、grid、place 三種。每一個控件只可以使用一種布局管理器,不同控件使用的布局管理器可以不一樣。
pack
形象點說, pack 就是把控件包裝在一個矩形區域,這個區域大小足夠放置控件,而且默認置中。pack 是最簡單的布局管理器,也稱之為包裝布局。
直接試一試用 pack 來布局三個靜態標簽 Label,默認設置(pack() 傳入參數為空)
import tkinter as tkwindow = tk.Tk()lbl_1 = tk.Label( master=window, text="label 1", fg="black", bg="red", width=10, height=5 )lbl_1.pack()lbl_2 = tk.Label( master=window, text="label 2", fg="black", bg="yellow", width=10, height=5 )lbl_2.pack()lbl_3 = tk.Label( master=window, text="label 3", fg="black", bg="blue", width=10, height=5 )lbl_3.pack()window.mainloop()
看看顯示效果
可以看到,默認 pack 會在父窗口 window 中垂直方向按順序包裝排列這三個靜態標簽 Label。
那么,如果我需要讓這幾個標簽水平排列呢?可以這樣子改
lbl_1.pack(side=tk.LEFT)...lbl_2.pack(side=tk.LEFT)...lbl_3.pack(side=tk.LEFT)
看看顯示效果
pack(side=tk.TOP) 和默認設置等價。
簡單匯總介紹一下其它的參數
參數 | 賦值 | 說明 |
---|---|---|
after | 控件 widget | 將此控件包裝在控件 widget 后邊 |
anchor | NSEW (or subset) | 根據方向定位此控件,NSEW 表示北南東西四個方向 |
before | 控件 widget | 將此控件包裝在控件 widget 前邊 |
expand | bool 類型值 | 跟著父控件一起伸縮 |
fill | NONE、X、Y、BOTH | 選擇當控件伸縮時按照哪個方向填充 |
ipadx | amount | 在x方向添加內部填充 |
ipady | amount | 在y方向添加內部填充 |
padx | amount | 在x方向添加填充 |
pady | amount | 在y方向添加填充 |
side | TOP、BOTTOM、LEFT、RIGHT | 把控件往哪邊添加 |
很多時候開發界面都需要讓里邊的控件跟隨窗口自動拉伸大小,來看一下上面的代碼應該怎么改
lbl_1.pack(fill=tk.BOTH, expand=tk.TRUE)...lbl_2.pack(fill=tk.BOTH, expand=tk.TRUE)...lbl_3.pack(fill=tk.BOTH, expand=tk.TRUE)
啟動的時候,還沒有拉伸窗口
然后拉伸看看
grid
如名字表述,grid 會把父窗口劃分成行列,然后根據調用時傳入參數 row,column 確定把控件放置在對應的行列中。grid 也稱之為格子布局。
還是以靜態標簽為例,創建 3x3 的矩陣標簽。為了凸顯各個標簽的邊界,這里還需要添加 Frame 控件,每個標簽放置于單獨的 Frame 中。
import tkinter as tkwindow = tk.Tk()for i in range(3): for j in range(3): frame = tk.Frame( master=window, relief=tk.RAISED, borderwidth=1 ) frame.grid(row=i, column=j) label = tk.Label( master=frame, text=f"Row {i}\nColumn {j}" ) label.pack()window.mainloop()
可以看到,上面這個例子布局時,只有 Frame 才需要應用 grid 管理器,因為每個標簽和 Frame 一一對應,所以標簽不需要重復應用格子布局。
看看顯示效果
簡單匯總介紹一下其它的參數
參數 | 賦值 | 說明 |
---|---|---|
column | 列序號 | 指定放置的列,從0開始 |
columnspan | 列數 | 放置的控件橫跨多少列 |
ipadx | amount | 在 x 方向添加內部填充 |
ipady | amount | 在 y 方向添加內部填充 |
padx | amount | 在 x 方向添加外部填充 |
pady | amount | 在 y 方向添加外部填充 |
row | 行序號 | 指定放置的行,從0開始 |
rowspan | number | 放置的控件橫跨多少行 |
sticky | NSEW | 如果單元格比較大,那么控件的指定邊界將貼著單元格。NSEW分別對應頂部、底部、右邊、左邊邊界 |
細心的朋友會發現,一旦拉伸上面的那個矩陣標簽窗口,窗口界面就會露出部分底面,矩陣標簽沒有跟隨一起拉伸。
這樣明顯和我們的預期不符合,這個問題怎么解決呢?
可以調用父窗口的 columnconfigure() 和 rowconfigure() 方法配置各列和行的伸縮比。這兩個方法都有三個輸入參數,看下面的表格
參數 | 說明 |
---|---|
index | 序號,指定特定的行或列,可以是單個行列序號值,也可以是代表多個行或列的列表 |
weight | 伸縮權重 |
minsize | 最小寬度值 |
現在來看看怎么改,才能讓矩陣標簽跟隨父窗口一起拉伸?
import tkinter as tkwindow = tk.Tk()for i in range(3): window.rowconfigure(i, weight=1) window.columnconfigure(i, weight=1) for j in range(3): frame = tk.Frame( master=window, relief=tk.RAISED, borderwidth=1 ) frame.grid(row=i, column=j) label = tk.Label( master=frame, text=f"Row {i}\nColumn {j}" ) label.pack()window.mainloop()
看看拉伸之后的顯示效果
perfect!另外需要提一下,grid 具有 pack 能做的所有功能,但是使用的形式更簡單,因此應該作為更優先的布局管理器。
place
place 用于對控件精確定位的場合。使用的時候需要傳入參數 x 和 y 分別用于指定控件的放置位置坐標值 x 和 y,傳入的 x 和 y 是基于父控件的左上角為原點的坐標系,單位是像素點。
大多數界面應用里,控件都不需要精確的定位。但是某些,比如地圖應用里,就的確需要對元素的精確定位了。
下面舉個栗子,在窗口里不同位置放置各一個標簽
import tkinter as tkwindow = tk.Tk()label1 = tk.Label( master=window, text="place (0, 0)", bg="yellow" )label1.place(x=0, y=0)label2 = tk.Label( master=window, text="place (40, 40)", bg="blue" )label2.place(x=40, y=40)window.mainloop()
看看上面代碼的顯示效果
說回來,place 的參數xy單位是像素,那么在不同的系統下,字體類型和大小都是不同的,那么被放置的控件就有可能超出窗口邊界。因此 place 真的不常用,關于 place 進一步的信息就不再展開了。
交互
上面介紹的內容都僅限于 Tkinter 界面的可視化設計,那么現在是時候介紹一下Tkinter 界面和用戶的互動了。
比如,Tkinter 界面對于事件的響應是怎么發生的?
一般,在 Tkinter 中通過預先綁定事件和響應處理函數,每當事件發生時,主窗口的 mainloop 就會收到事件,然后根據綁定信息,查找到響應處理函數并調用,來達到交互的效果。綁定的通用方法是調用各個控件的 bind()。
比如,下面我們來實現一個簡單的鍵盤響應互動,每次按鍵按下時就把對應的按鍵打印出來
import tkinter as tkdef handle_keypress(event): print(event.char)window = tk.Tk()window.bind("", handle_keypress)window.mainloop()
上面的代碼沒有添加額外的控件,除了有個空白的主窗口。其中,對主窗口 window 綁定了按鍵事件 Key 和處理函數 handle_keypress。
調用 bind() 最少要求輸入兩個參數,一個是形式為 "
定義處理函數 handle_keypress 時,唯一做的事情是把傳入的事件字符打印到標準終端。
每當按下鍵盤的按鍵,命令行終端就會輸出對應的按鍵。
但是,對于按鈕 Button 來說,點擊事件的觸發處理可以不需要使用 bind(),僅需要在實例化控件 Button 時,傳入處理函數給 command 參數即可,初始化過程會自動綁定 click 事件的響應。
上代碼看看
import tkinter as tkdef handle_keypress(): print("clicked")window = tk.Tk()button = tk.Button( master=window, text="click me!", command=handle_keypress )button.pack()window.mainloop()
運行程序
用鼠標點擊一下界面上的按鈕,發現終端會輸出字符串 clicked
由于篇幅受限,本系列教程還未完結,下一篇《Python 內置界面開發框架 Tkinter入門篇 丁》將在本公眾號稍后推送
關鍵詞: 編程算法