Python 的內建 GUI 模組 Tkinter 測試 (一) : 建立視窗
最近因為玩樹苺派的關係, 接觸到 Python 內建的 GUI 開發模組 Tkinter (意思是 Tk Interface), 初步覺得比用 Java 的 Swing 還要來得容易, 因此就來學看看唄!
Tk 原先是為 Tcl 語言所開發的 GUI 套件, 因為是 Tcl 的第一個擴充, 所以現在都合起來稱呼為 Tcl/Tk. Tcl 是一種以 string-based 的跨平台工具命令式直譯語言 (Tool command language), 繼承了 LISP/C/Shell 等語言的優點, 並具有語法簡單, 容易擴展與可靈活嵌入其他語言的特點, 而且全面支持 unicode.
而 Tkinter 是 Python 內建的標準模組, 內部嵌入了 Tcl/Tk GUI 套件, 用來在 Python 中建構 GUI 圖形介面程式, 它具有如下優點 :
- 簡單易學 :
比 Python 其他 GUI 要容易, 甚至於我覺得比學 Java Swing 還容易. - 程式碼精簡 :
以很短的程式碼便能產生強大功能的 GUI 程式. - 跨平台 :
同樣的程式可以在 Linux/Windows/Mac 等系統上執行.
不過在 Python 2 中的模組名稱 Tkinter 到 Python 3 版後已被改為小寫的 tkinter, 使用時要注意所用之 Python 版本, 匯入時注意該用首字大寫與否. 不過 Python 2 下以 Tkinter 所寫的 GUI 程式可以利用 2to3 程式轉成 Python 3 版的程式, 詳見 :
#
https://docs.python.org/2/library/tkinter.html 在 Python 中使用 Tkinter 需先匯入模組 :
import Tkinter
或
from Tkinter import *
但書裡說不建議使用後面這種方式, 因為此方法是匯入 Tkinter 所有函數與屬性, 這樣會占用較多記憶體, 而且容易讓命名空間混淆, 並使得 debug 難度增加. 不過, 這種方式使用起來較方便.
比較常見的匯入方式是幫 Tkinter 取個別名 :
import Tkinter as tk
注意, 在 Python 3 要用小寫的 tkinter.
這樣就能使用 tk 這個別名來呼叫此模組內的函數 :
tk.函數名稱()
匯入模組就可以呼叫 Tk() 函數建立一個視窗實體 :
import Tkinter as tk
win=tk.Tk()
如果沒有取別名, 就要用 Tkinter.Tk() :
import Tkinter
win=Tkinter.Tk()
然後呼叫此視窗實體之 mainloop() 函數將此視窗加入事件監視迴圈, 這樣會產生 GUI 視窗了.
win.mainloop()
真是太簡單了, 建立一個空視窗只要三行程式碼!
如果用 Java Swing 來產生一個空視窗, 至少需要 10 行以上的程式碼 :
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
public class JFrame1 {
JFrame f;
public static void main(String argv[]) {
new JFrame1();
}
public JFrame1() {
f=new JFrame("JFrame 1");
f.setBounds(0,0,400,300);
f.setVisible(true);
}
}
這就是為什麼 Tkinter 非常適合用來快速開發 GUI 程式的原因.
在 IDLE 介面輸入此三行程式, 馬上就建立一個視窗了 :
Python 2.7.6 (default, Nov 10 2013, 19:24:18) [MSC v.1500 32 bit (Intel)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> import Tkinter as tk
>>> win=tk.Tk()
>>> win.mainloop()
注意, 視窗描繪是在加入事件監視函數 mainloop() 後才發生的唷!
此空視窗右上角有標準視窗的縮小, 放大, 以及關閉按鈕, 還可以拖曳調整視窗大小. 預設標題 (title) 是 "tk", 我們可以呼叫 title 函數來加以設定 :
>>> import Tkinter as tk
win=tk.Tk()
win.title("My First Tk GUI")
win.mainloop()
如果不想讓使用者調整視窗大小, 可以呼叫 resizable(0, 0) 來禁止 :
import Tkinter as tk
win=tk.Tk()
win.title("My First Tk GUI")
win.resizable(0,0)
win.mainloop()
這樣不僅無法拖曳縮放視窗, 右上角的放大按鈕也被禁能了, 只能縮小與關閉. 此 resizable() 函數事實上是設定視窗左上角座標, 如果設為 0,0 表示無法縮放.
Tkinter 提供了下列 21 種 GUI 元件 (稱為 widget 或 control) :
- Label
- Button
- Radiobutton
- Checkbutton
- Entry
- Frame
- LabelFrame
- Listbox
- Text
- Message
- PanedWindow
- Scrollbar
- Scale
- Spinbox
- Menu
- OptionMenu
- Menubutton
- Canvas
- Image
- Bitmap
- Toplevel
下面來測試其中的 Button 與 Label 元件, 把下列程式用記事本存成 test.py :
import Tkinter as tk
win=tk.
Tk() #建立視窗容器物件
win.title("Tk GUI")
label=tk.
Label(win, text="Hello World!") #建立標籤物件
label.
pack() #顯示元件
button=tk.
Button(win, text="OK")
button.
pack() #顯示元件
win.mainloop()
然後在命令提示字元視窗執行 :
D:\Python\test>python test.py
可見在呼叫 pack() 函數做版面管理的話, Tkinter 會將元件由上而下水平置中順序排版.
注意, 在建立 GUI 元件時, 第一個參數必須為其容器物件 (此處為 win 變數所代表之視窗), 格式如下 :
元件變數=元件名稱(容器物件變數, [元件選項])
其他為元件之選項參數, 例如標籤文字 (text), 大小(size), 邊框 (border), 前景顏色 (foreground), 或背景顏色 (background), 每一種元件可能有不同之選項, 可以在建立元件時直接設定, 也可以在建立之後呼叫 configure() 函數或其別名 config() 來設定.
其次, 所建立的元件必須利用版面管理員 (geometry manager) 於視窗容器中定位, 這樣它才會在視窗中顯現, 而此 pack() 函數就是一個版面管理員, 它會由上而下擺放元件.
如果是使用 from Tkinter import * 匯入方式, 上面程式的寫法要改為 :
from Tkinter import *
win=Tk()
win.title("Tk GUI")
label=Label(win, text="Hello World!")
button=Button(win, text="OK")
label.pack()
button.pack()
win.mainloop()
ㄟ, 這個寫法好像比較簡單哩! 不用寫 tk.
除了由上而下的 pack() 版面管理員外, 也可以呼叫 grid() 函數使用網格版面來管理元件 :
from Tkinter import *
win=Tk()
win.title("Tk GUI")
label=Label(win, text="Hello World!")
button=Button(win, text="OK")
label.grid(column=0,row=0)
button.grid(column=1,row=0)
win.mainloop()
在呼叫 grid() 時, 我們將 label 放在 0 行 0 列, button 放在 1 行 0 列, 亦即這是一個 1x2 (1 列 2 行) 的網格, 因此元件是左右各排一個.
目前這個按鈕按下去不會有反應, 因為我們還沒有幫它設定事件處理函數. 在下面範例中, 我定義了一個按鈕事件處理函數 clickOK() 來處理 OK 鍵被按事件, 並設定了一個全域變數 count 來記錄 OK 鍵被按了幾次, 當 OK 被按時, count 會增量, 並且透過呼叫元件的 configure() 或 config() 函數來更改標籤元件的文字內容 (text 屬性) :
from Tkinter import *
win=Tk()
win.title("Tk GUI")
label=Label(win, text="Hello World!")
count=0
def clickOK():
global count
count=count + 1
label.configure(text="Click OK " + str(count) + " times")
button=Button(win, text="OK", command=clickOK)
label.pack()
button.pack()
win.mainloop()
注意, 事件處理函數與 GUI 元件之間是透過 command 參數來綁定的. 另外上面也用到了 Python 內建函數 str() 來將數值類型的 count 轉成字串類型.
上面範例中的按鈕看起來似乎不是那麼好看, 所以 Tkinter 後來又推出了加強版的 ttk 模組來美化元件的外觀, 這 ttk 意思是 Themed Tkinter, 亦即主題化版本, 參考 :
# https://docs.python.org/2/library/ttk.html
此 ttk 模組包含了 17 種元件, 其中的 11 種是 Tkinter 原本已經有的 :
- Label
- Button
- Radiobutton
- Checkbutton
- Entry
- Frame
- Labelframe
- Menubutton
- Scale
- Scrollbar
- Panedwindow
另外 6 個是 ttk 推出的新元件 :
- Combobox
- Notebook
- Progressbar
- Separator
- Sizegrip
- Treeview
Ttk 的使用方法與 Tkinter 完全一樣, 使用前須先匯入此模組 :
from Tkinter import ttk
或
from ttk import *
將上面範例用 ttk 改寫如下 :
from Tkinter import *
from ttk import *
win=Tk()
win.title("ttk GUI")
label=Label(win, text="Hello World!")
count=0
def clickOK():
global count
count=count + 1
label.config(text="Click OK " + str(count) + " times")
button=Button(win, text="OK", command=clickOK)
label.pack()
button.pack()
win.mainloop()
可見按鈕真的變漂亮了!
Tk 模組有一個測試函數 _test(), 會顯示一個內建的測試視窗 :
Python 2.7.8 (default, Jun 30 2014, 16:03:49) [MSC v.1500 32 bit (Intel)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> from Tkinter import *
Tkinter._test()
我這是在 Python 2.7.6 上測試的, Tk 的版本是 8.5, 如果在 Python 3 上測試, Tk 版本應該是 8.6 版以上. 8.6 版在功能上比起 8.5 有大幅改進.
呼叫 Tcl().eval() 函數可以查詢 Tk 版本 :
>>> Tkinter.Tcl().eval('info patchlevel')
'8.5.15'
用 Tkinter 模組寫好的 Python 程式可以用 py2exe 模組轉成 Windows 執行檔, 參考 :
# 將python轉成執行檔(py2exe)
# http://www.py2exe.org/index.cgi/Tutorial
# Tk : Dialog window
其他參考 :
# 傻貓布落格 : Tkinter
# TutorialsPoint
# An Introduction to Tkinter
# Thinking in Tkinter
網頁來源:http://yhhuang1966.blogspot.tw/2016/05/python-gui-tkinter.html