雖然 Frame 物件我還沒時間做一個較完整的測試, 但這周在用 Tkinter 開發公司的維運小軟體時直接拿來上陣使用, 發覺用 Frame 元件搭配最簡單常用的 pack 版面布局也可以將會疊串在一起的 Entry, RadionButton, CheckButton, 與 Button 等元件水平擺放得很整齊.
下面以簡化的系統登入與操作按鈕等元件來說明 Frame 在排版上的妙用. 視窗中包含帳號密碼輸入框, 登入按鈕, 指令輸入框, 執行按鈕, 以及一個執行結果文字區域等元件 :
測試 1 : pack() 的堆疊式的版面布局
import tkinter as tk
from tkinter import ttk
def login():
pass
def run():
pass
# 建立視窗
root=tk.Tk() # 建立視窗物件
root.title('Frame + Pack 布局測試') # 設定視窗標題
root.geometry('800x600') # 設定視窗尺寸
# 建立登入介面與指令輸入介面
account=tk.Entry(root, width=10) # 帳號輸入框
account.pack() # 元件放入視窗
password=tk.Entry(root, width=10, show='*') # 密碼輸入框
password.pack() # 元件放入視窗
login_btn=tk.Button(root, text='登入', command=login) # 登入按鈕
login_btn.pack() # 元件放入視窗
command=tk.Entry(root, width=20) # 指令輸入框
command.pack() # 元件放入視窗
run_btn=tk.Button(root, text='執行', command=run) # 登入按鈕
run_btn.pack() # 元件放入視窗
# 建立執行結果文字區域
result=tk.Text(root)
result.pack(fill=tk.BOTH, expand=True)
result.pack()
root.mainloop()
結果如下 :
可見 pack 版面管理員的布局方式基本上就是由上而下堆疊, 雖然可以在呼叫 pack() 時傳入 side=tk.RIGHT/LEFT/TOP/BOTTOM 指定上下左右之垂直水平位置, 或傳入 anchor=tk.E/W/S/N/CENTER/NW/NE/SW/SE 來定位元件在父容器中的位置, 但光用 side 與 anchor 參數無法達成讓上方輸入框與按鈕元件以水平方式排在最上方, 且登入在左, 執行在右的需求.
解決辦法之一是使用 Frame 容器來放置元件, 並且每個元件在呼叫 pack() 時傳入 side=tk.LEFT 指定由左向右水平排列, 版面結構如下圖所示 :
import tkinter as tk
from tkinter import ttk
def login():
pass
def run():
pass
# 建立視窗
root=tk.Tk() # 建立視窗物件
root.title('Frame + Pack 布局測試') # 設定視窗標題
root.geometry('800x600') # 設定視窗尺寸
# 建立登入介面與指令輸入介面
frame=tk.Frame(root) # 外部 Frame 元件
frame.pack()
# 建立以 Frame 為父容器的元件
account=tk.Entry(frame, width=10) # 帳號輸入框
account.pack(side=tk.LEFT) # 元件放入視窗
password=tk.Entry(frame, width=10, show='*') # 密碼輸入框
password.pack(side=tk.LEFT) # 元件放入視窗
login_btn=tk.Button(frame, text='登入', command=login) # 登入按鈕
login_btn.pack(side=tk.LEFT) # 元件放入視窗
command=tk.Entry(frame, width=20) # 指令輸入框
command.pack(side=tk.LEFT) # 元件放入視窗
run_btn=tk.Button(frame, text='執行', command=run) # 登入按鈕
run_btn.pack(side=tk.LEFT) # 元件放入視窗
# 建立執行結果文字區域
result=tk.Text(root)
result.pack(fill=tk.BOTH, expand=True)
result.pack()
root.mainloop()
此例先建立一個 Frame 元件, 然後先後建立以此 Frame 元件為父容器 (不是 root 了) 的輸入框 Entry 與 Button 按鈕等元件, 但 Text 元件依然是以 root 為父元件. 結果如下 :
可見輸入框與按鈕都在 Frame 內呈水平排列. 注意, Frame 內的所有元件呼叫 pack() 時均傳入 tk.LEFT, 這表示元件是在 Frame 內由左向右依序排列之意, 並非定位在左邊 (定位要用 ANCHOR 參數); 同理, tk.RIGHT 表示由右向左排列. 如果將上面範例的 command 與 run_btn 兩個元件的 pack() 改為傳入 tk.RIGHT 會讓此兩元件順序顛倒 :
測試 3 : 呼叫 pack() 傳入 side=tk.RIGHT 為由右向左排列
import tkinter as tk
from tkinter import ttk
def login():
pass
def run():
pass
# 建立視窗
root=tk.Tk() # 建立視窗物件
root.title('Frame + Pack 布局測試') # 設定視窗標題
root.geometry('800x600') # 設定視窗尺寸
# 建立登入介面與指令輸入介面
frame=tk.Frame(root) # 外部 Frame 元件
frame.pack()
account=tk.Entry(frame, width=10) # 帳號輸入框
account.pack(side=tk.LEFT) # 元件放入視窗
password=tk.Entry(frame, width=10, show='*') # 密碼輸入框
password.pack(side=tk.LEFT) # 元件放入視窗
login_btn=tk.Button(frame, text='登入', command=login) # 登入按鈕
login_btn.pack(side=tk.LEFT) # 元件放入視窗
command=tk.Entry(frame, width=20) # 指令輸入框
command.pack(side=tk.RIGHT) # 元件放入視窗
run_btn=tk.Button(frame, text='執行', command=run) # 登入按鈕
run_btn.pack(side=tk.RIGHT) # 元件放入視窗
# 建立執行結果文字區域
result=tk.Text(root)
result.pack(fill=tk.BOTH, expand=True)
result.pack()
root.mainloop()
此例程式碼與上面不同之處僅在於 command 與 run_btn 這兩個元件呼叫 pack() 時傳入 side=tk.RIGHT 而已, 結果兩個元件就由右向左排列 :
如果要讓這些 Frame 內水平排列的元件有所區隔, 可以用一個無顯示字串的 Label 來當隱形的佔位元件, 可透過 width (字元數, 不是 px) 來控制間隔距離, 例如 :
測試 4 : 利用無顯示字串的 Label 元件佔位
import tkinter as tk
from tkinter import ttk
def login():
pass
def run():
pass
# 建立視窗
root=tk.Tk() # 建立視窗物件
root.title('Frame + Pack 布局測試') # 設定視窗標題
root.geometry('800x600') # 設定視窗尺寸
# 建立登入介面與指令輸入介面
frame=tk.Frame(root) # 外部 Frame 元件
frame.pack()
account=tk.Entry(frame, width=10) # 帳號輸入框
account.pack(side=tk.LEFT) # 元件放入視窗
password=tk.Entry(frame, width=10, show='*') # 密碼輸入框
password.pack(side=tk.LEFT) # 元件放入視窗
login_btn=tk.Button(frame, text='登入', command=login) # 登入按鈕
login_btn.pack(side=tk.LEFT) # 元件放入視窗
placeholder=tk.Label(frame, width=10) # 佔位用的 Label
placeholder.pack(side=tk.LEFT)
command=tk.Entry(frame, width=20) # 指令輸入框
command.pack(side=tk.LEFT) # 元件放入視窗
run_btn=tk.Button(frame, text='執行', command=run) # 登入按鈕
run_btn.pack(side=tk.LEFT) # 元件放入視窗
# 建立執行結果文字區域
result=tk.Text(root)
result.pack(fill=tk.BOTH, expand=True)
result.pack()
root.mainloop()
此例在 login 鈕與 command 輸入框之間插入一個無顯示字串 (即 text='') 且設定 width=10 的 Lable 元件當佔位元件 (空元件), 這樣就在兩元件之間製造了 10 個字元寬度的間隔, 結果如下 :
注意, 間隔寬度若設太大, 撐破 Frame 的寬度時, 後面的元件可能會跳到下一行而影響版面外觀.
除了 Frame 容器外, 也可以用 PanedWindow 來做版面布局, 茲將上面範例中的 Frame 改成 PanedWindow, 結果相同 :
測試 5 : 使用 PanedWindow 排版
import tkinter as tk
from tkinter import ttk
def login():
pass
def run():
pass
# 建立視窗
root=tk.Tk() # 建立視窗物件
root.title('pw + Pack 布局測試') # 設定視窗標題
root.geometry('800x600') # 設定視窗尺寸
# 建立登入介面與指令輸入介面
pw=tk.PanedWindow(root) # 外部 pw 元件
pw.pack()
account=tk.Entry(pw, width=10) # 帳號輸入框
account.pack(side=tk.LEFT) # 元件放入視窗
password=tk.Entry(pw, width=10, show='*') # 密碼輸入框
password.pack(side=tk.LEFT) # 元件放入視窗
login_btn=tk.Button(pw, text='登入', command=login) # 登入按鈕
login_btn.pack(side=tk.LEFT) # 元件放入視窗
placeholder=tk.Label(pw, width=10) # 佔位用的 Label
placeholder.pack(side=tk.LEFT)
command=tk.Entry(pw, width=20) # 指令輸入框
command.pack(side=tk.LEFT) # 元件放入視窗
run_btn=tk.Button(pw, text='執行', command=run) # 登入按鈕
run_btn.pack(side=tk.LEFT) # 元件放入視窗
# 建立執行結果文字區域
result=tk.Text(root)
result.pack(fill=tk.BOTH, expand=True)
result.pack()
root.mainloop()
此例建立一個 PanedWindow 物件 pw, 然後底下程式碼中的 frame 全部改成 pw, 結果如下 :
可見結果與上面用 Frame 排版的效果一模一樣.
除了直接呼叫元件的 pack() 方法將元件加入父容器 PanedWindow 物件外, 也可以呼叫該物件的 add() 方法, 它預設也是用 tk.LEFT 由左向右將元件放入容器中, 例如 :
測試 6 : 使用 PanedWindow 的 add() 方法將元件加入容器
import tkinter as tk
from tkinter import ttk
def login():
pass
def run():
pass
# 建立視窗
root=tk.Tk() # 建立視窗物件
root.title('pw + Pack 布局測試') # 設定視窗標題
root.geometry('800x600') # 設定視窗尺寸
# 建立登入介面與指令輸入介面
pw=tk.PanedWindow(root) # 外部 pw 元件
pw.pack()
account=tk.Entry(pw, width=10) # 帳號輸入框
pw.add(account) # 元件放入視窗
password=tk.Entry(pw, width=10, show='*') # 密碼輸入框
pw.add(password) # 元件放入視窗
login_btn=tk.Button(pw, text='登入', command=login) # 登入按鈕
pw.add(login_btn) # 元件放入視窗
placeholder=tk.Label(pw, width=10) # 佔位用的 Label
pw.add(placeholder)
command=tk.Entry(pw, width=20) # 指令輸入框
pw.add(command) # 元件放入視窗
run_btn=tk.Button(pw, text='執行', command=run) # 登入按鈕
pw.add(run_btn) # 元件放入視窗
# 建立執行結果文字區域
result=tk.Text(root)
result.pack(fill=tk.BOTH, expand=True)
result.pack()
root.mainloop()
結果與上面範例相同.
沒有留言:
張貼留言