博客 / 詳情

返回

Python 內置界面開發框架 Tkinter入門篇 丙(文末有福利彩蛋,今天可是元宵節)

以下內容為本人的學習筆記,如需要轉載,請聲明原文鏈接 微信公眾號「ENG八戒」https://mp.weixin.qq.com/s/B1...

本文大概 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 tk

window = 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 tk

window = 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 tk

window = 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 tk

window = 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 tk

def handle_keypress(event):
    print(event.char)

window = tk.Tk()
window.bind("<Key>", handle_keypress)
window.mainloop()

上面的代碼沒有添加額外的控件,除了有個空白的主窗口。其中,對主窗口 window 綁定了按鍵事件 Key 和處理函數 handle_keypress。

調用 bind() 最少要求輸入兩個參數,一個是形式為 "<event_name>" 的事件,另一個是事件處理函數。

定義處理函數 handle_keypress 時,唯一做的事情是把傳入的事件字符打印到標準終端。

每當按下鍵盤的按鍵,命令行終端就會輸出對應的按鍵。

但是,對於按鈕 Button 來説,點擊事件的觸發處理可以不需要使用 bind(),僅需要在實例化控件 Button 時,傳入處理函數給 command 參數即可,初始化過程會自動綁定 click 事件的響應。

上代碼看看

import tkinter as tk

def 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入門篇 丁》將在本公眾號稍後推送,如果你對此教程有興趣或者想和我一起交流更多精彩內容,歡迎關注我的微信公眾號 【ENG八戒】,等着你哦!

《元宵彩蛋》

鬧元宵了,《八戒陪你一起鬧元宵2023》

user avatar u_16213589 頭像 lintp 頭像 apollo008star 頭像 qutianhang 頭像 u_16099302 頭像 friesonthepier 頭像 aitigou 頭像
7 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.