你已經熟悉了隻在視窗中顯示一塊畫布的“海龜繪圖”,現在讓我們嘗試編寫更通用的圖形界面程式——使用Python标準庫中的圖形界面工具包tkinter,之前介紹的IDLE和turtle實際上都是基于tkinter實作的。tkinter的特點是簡單輕便、适合入門之用,今後你也可以學習其他更“進階”的第三方包來開發圖形界面——例如Spyder所使用的PyQt https://pypi.python.org/pypi/PyQt5
圖形界面的各種構成元素統稱“可視化部件”(Widget),每一種部件都對應某一種類型,首先你需要生成特定類型的部件對象,然後調用對象的方法即可任意控制圖形界面的外觀和行為了。現在讓我們來制作一個最簡單的圖形界面程式——隻包含一個最基本的部件——“視窗”,對應類型名為Tk。
import tkinter as tk
window = tk.Tk() # 生成視窗對象
window.geometry("500x300") # 設定視窗大小
window.title("簡單圖形界面程式") # 設定視窗标題
# 這裡可以生成其他部件并放入視窗
tk.mainloop() # 開始主循環
以上程式中生成視窗對象的“Tk()”是一種特别的函數,函數名就是類型名,稱為類型的“構造函數”、“構造方法”或“構造器”(Constructor),這幾個術語指的都是同一樣東西。任何類型的對象都可以用相應構造器來生成——例如“int("100")”将生成一個整數“100”(如果不帶參數則是生成整數“0”)。所有對象設定完成之後執行mainloop()函數将啟動程式的“主事件循環”,顯示定義好的圖形界面并開始使用者互動——對于這個最簡單的程式來說,你可以進行預設的視窗操作:移動、縮放、最小化、最大化和關閉等等。
其他常用的可視化部件有“标簽”(Label)、“按鈕”(Button)、“輸入框”(Entry)、“文本區”(Text)等等,這些部件都不能獨立存在而是從屬于視窗這樣的“容器”,你可以使用特定的布局方法例如pack()把它們放進容器對象,預設從上到下放置:
label = tk.Label(window, text="測試标簽")
label.pack()
button = tk.Button(window, text="測試按鈕")
button.pack(side="bottom") # 放到容器底部
entry = tk.Entry(window, width=50) # 輸入框寬50字元
entry.pack()
text = tk.Text(window, width=50, height=12, background="wheat") # 文本區寬50字元高12字元,麥色背景
text.pack()
使用網格布局方法grid()可以在同一行放置多個部件,但此方法不可與pack()同時使用。還有一種定位布局方法place()指定部件在容器中的絕對坐标(原點在左上角,x軸向右,y軸向下),以下代碼改用grid()和place()調整視窗布局:
label = tk.Label(window, text="測試标簽")
label.grid(row=0, column=0) # 标簽放在0行0列
button = tk.Button(window, text="測試按鈕")
button.grid(row=0, column=1) # 按鈕放在0行1列
entry = tk.Entry(window, width=50)
entry.grid(row=1, column=0, columnspan=2, padx=20, pady=10) # 輸入框在1行0列,橫跨兩列,橫向留白20像素,縱向留白10像素
text = tk.Text(window, width=50, height=12, background="wheat")
text.place(x=20, y=100) # 文本區放在指定的坐标
在生成部件對象時可以設定對象事件的處理函數,實作各種“人機互動”功能——讓我們繼續改程序式,為按鈕對象加入點選事件的處理函數,把你在輸入框輸入的文本添加到文本區的現有文本中:
"""tktest.pyw 實作按鈕點選事件處理
"""
import tkinter as tk
def change(widget, var):
"""事件處理函數:改變Text部件的文本
在widget現有文本末尾插入新的文本var
"""
widget.config(state="normal")
widget.insert("end", var + "\n")
widget.config(state="disabled")
def main():
"""主函數:設定視窗部件,指定按鈕點選事件處理函數
"""
window = tk.Tk()
window.geometry("400x300")
window.title("簡單圖形界面程式")
label = tk.Label(window, text="請輸入文本并點選添加")
label.grid(row=0, column=0)
entry = tk.Entry(window, width=50)
entry.grid(row=1, column=0, columnspan=2, padx=20, pady=10)
text = tk.Text(window, width=50, height=12, background="wheat")
text.config(state="disabled")
text.place(x=20, y=100)
button = tk.Button(window, text="添加",
command=lambda: change(text, entry.get()))
button.grid(row=0, column=1)
tk.mainloop()
if __name__ == "__main__":
main()
接下來的示例是一個圖檔檢視器——顯示圖檔需要配合使用“畫布”(Canvas)和“圖像”(PhotoImage)部件(tkinter支援的圖檔格式有PNG和GIF等),這個程式還引入了tkinter包中的另一個子產品filedialog以便顯示标準的打開檔案對話框:
"""tkimage.pyw 簡單的圖檔檢視器
"""
import tkinter as tk
import tkinter.filedialog as fd
def openimage(canvas):
"""事件處理函數:使用檔案對話框打開圖檔
"""
filename = fd.askopenfilename(filetypes=[("PNG圖檔", "*.png"),
("GIF圖檔", "*.gif")])
global image # 注意這個需要定義為全局變量
image = tk.PhotoImage(file=filename)
canvas.create_image((0, 0), image=image, anchor="nw")
def main():
"""主函數:設定視窗部件,指定按鈕點選事件處理函數
"""
window = tk.Tk()
window.geometry("600x480")
window.title("簡單的圖檔檢視器")
canvas = tk.Canvas(window, width=600, height=440)
canvas.pack(side="bottom")
button = tk.Button(window, text="打開圖檔",
command=lambda: openimage(canvas))
button.pack()
tk.mainloop()
if __name__ == "__main__":
main()

10_image.png
——程式設計原來是這樣……
程式設計小提示
你已經看到tkinter所支援的圖像功能和檔案格式非常有限,如果你需要處理JPG等其他常用圖檔檔案格式,可以安裝第三方包“pillow”(Python Imaging Library,簡稱PIL)——注意:軟體包名是pillow而子產品名是PIL:
from PIL import Image, ImageTk
# image = tk.PhotoImage(file=filename) 這一句改為下面這兩句
image = Image.open(filename)
image = ImageTk.PhotoImage(image)
canvas.create_image((0, 0), image=image, anchor="nw")
修改後的程式就可以打開幾乎任何格式的圖檔了,有關pillow的詳情可以檢視釋出頁面 https://pypi.python.org/pypi/Pillow/5.1.0