引言
Tkinter 與Python 編程
Tkinter 模塊
Tk 組件庫
Tkinter 使用舉例
標簽、按鈕與進度條組件
一個使用 Tk 的中級范例
其他 GUI 簡介(Tix, Pmw, wxPython, PyGTK)
相關模塊和其他 GUI
Python 的默認GUI 工具集是Tk,它也是我們將使用的最基本的GUI 工具集。
我們可以通過Python 接口Tkinter 來使用Tk(Tkinter 正是“Tk 接口”之意)。
Tk 並非“最強、最新”也不是包含GUI 構建模塊最多的工具集,但它非常簡單,並且可以開發 出能運行於大多數平台的GUI 程序。
19.1 簡介
什么是Tcl、Tk 和Tkinter?
Tkinter 是Python 的默認GUI 庫,它基於
Tk 工具集,后者最初是為
工具命令語言(Tcl)設計
的。Tk 流行后被移植到許多其他腳本語言中,包括Perl(Perl/Tk)、Ruby(Ruby/Tk)和 Python
(Tkinter)。借助於Tk 開發GUI 的可移植性和靈活性,加上腳本語言的簡潔和系統語言的強勁,我
們得到了一件可與商業軟件相匹敵的利器,它可以用於快速開發各種GUI 程序。
Python 基於Tk 提供了一種更高效的快速原型系統用以創建應用。
別忘了您同時還享有Python 的系統訪問、網絡操作、XML、數字可視化、數據庫訪問、以及所有其他標准庫和第三方模塊。
安裝和使用Tkinter
類似於線程模塊,系統中的Tkinter 未必是默認開啟的。
通過嘗試導入Tkinter 模塊 來判斷它是否能被Python 解釋器使用。
客戶端/服務器架構
窗口系統就是軟件服務 器的另一個例子,它們運行在一個有顯示設備的機器上,比如帶有一個某種類型的顯示器。當然還
有客戶端(那些需要窗口環境來運行的程序,也就是我們所說的GUI 程序),這些程序無法脫離窗口 系統單獨運行。
19.2 Tkinter 與 Python 編程
Tkinter 模塊:把Tk 引入您的程序
Tkinter 模塊:把Tk 引入您的程序
要創建並運行您的GUI 程序,下面五步是基本的:
1. 導入Tkinter 模塊(import Tkinter,或者,from Tkinter import *)。
2. 創建一個頂層窗口對象,來容納您的整個GUI 程序。
3. 在您的頂層窗口對象上(或者說在“其中”)創建所有的GUI 模塊(以及功能)。
4. 把這些GUI 模塊與底層程序代碼相連接。
5. 進入主事件循環。
GUI 程序開發簡介
創建GUI 程序與畫家作畫有些相似。通常畫家只會在一塊畫布上開展自己的創作。工作步驟或
許是這樣的:首先要找來一塊干凈的石板,您將在這個“頂層”窗口對象上創建所有其他模塊。可
以把這一步想象成一座房屋的地基或者某個畫家的畫架。換言之,在搭建各實物或展開畫布之前,
您必須先給地基澆灌好混凝土或者架好畫架。對Tkinter 而言,這個基礎被稱為
頂層窗口對象。
在GUI 程序中,會有一個頂層根窗口對象,它包含着所有小窗口對象,它們共同組成一個完整
的GUI 程序。這些小窗口對象可以是文字標簽、按鈕、列表框等等。這些獨立的GUI 構件就是所謂
的組件。所以當我們說創建一個頂層窗口的時候,我們實際上是指您需要一個放置所有組件的地方。
典型的Python 語句如下行:
top = Tkinter.Tk() # 如果上文是“from Tkinter import *",Tk()就夠了
Tkinter.Tk()返回的對象通常被稱作根窗口,正因為如此,有些程序用root 來指示它,而非top。
頂層窗口是指那些在您的程序中獨立顯示的部分。您可以在GUI 程序中創建多個頂層窗口,但它們
中
只能有一個是根窗口。您可以采用先完全設計好組件再添加實際功能的開發方式,也可以二者同
時進行。(這意味着交替執行上述五步中的第三步和第四步。)
組件既可以是獨立的也可以作為容器存在。如果一個組件“包含”其他組件,它就被認為是這
些組件的父組件。相應地,如果一個組件被“包含”在其他組件中,它就被認為是父組件的孩子,
父組件則是直接包圍其外的那個容器組件。
通常,組件會有一些相應的行為,例如按鈕被按下,或者文本框被寫入。這種形式的用戶行為
被稱為
事件,而GUI 程序對事件所采取的響應動作被稱為
回調。
用戶操作包括按下(以及釋放)按鈕、移動鼠標、按下RETURN 或Enter 鍵等等,所有的這些從
系統角度都被看作事件。GUI 程序正是由這伴隨其始末的整套事件體系所驅動的。這個過程被稱作
事
件驅動處理。
一個事件及其回調的例子是鼠標移動。我們假設鼠標指針停在您GUI 程序的某處。如果鼠標被
移到了程序的別處,一定是有什么東西引起了屏幕上指針的移動,從而表現這種位置的轉移。系統
必須處理這些鼠標移動事件才能展現(並實現)鼠標在窗口上的移動。一旦您釋放了鼠標,就不再
會有事件需要處理,相應地,屏幕上的一切又復歸平靜。
GUI 程序的事件驅動特性恰好體現出它的客戶端/服務器架構。當您啟動一個GUI 程序時,它必
須執行一些初始化例程來為核心功能的運行做准備,正如啟動一個網絡服務器時必須先申請一個套
接字並把它綁定在一個本地地址上一樣。
Tk 有兩個坐標管理器用來協助把組件放在正確的位置上;
您將經常用到的一個稱為“包”,亦即
packer。另一個坐標管理器是網格(
Grid)--您可以用它來把
GUI 組件放在網格坐標系中,Grid 將依據GUI 中的網格坐標來生成每個對象。我們將緊扣packer 講
解。
一旦packer 決定好您所有組件的尺寸和對齊方式,它將為您在屏幕上放置它們。當所有這些組
件,包括頂層窗口,最終顯示在您屏幕上時,GUI 程序就會進入一個“服務器式”的無限循環。這個
無限循環包括等待GUI 事件、處理事件、然后返回等待模式,等待下一個事件。
上述最后一步說明所有組件就緒后立即進入主循環。這正是我們提及的
“服務器式”無限循環。
對Tkinter 而言,相應代碼如下:
Tkinter.mainloop()
這通常是您程序執行的最后一段代碼。一旦進入主循環,GUI 便從此掌握控制權。所有其他動作
都來自回調函數,包括程序退出。當您拉下文件菜單點擊“退出”菜單項或直接關閉窗口時,必須
要喚起一個回調來結束您的程序。
頂層窗口:Tkinter.Tk()
所有的主要組件都建立在頂層窗口對象內。
這個對象是由Tkinter 中的Tk 類創建 的,並且是由普通構造函數創建的:
>>> import Tkinter
>>> top = Tkinter.Tk()
在這個窗口中,您可以放置獨立組件或集成的模塊來構建您的GUI。
Tk 組件
Tk 組件
Tk 目前有15 種組件。
核心注釋:缺省參數是您的朋友
GUI 開發從Python 的缺省參數機制獲益匪淺,因為Tkinter 組件有大量的默認動作。除非您熟
知自己使用的每一個組件的每一個可用選項,否則最好只設置您關心的參數而把其他的交由系統處
理。這些缺省值是精心選出的。
如果您沒有提供這些值也不必擔心程序會在屏幕上表現怪異。作為一條基本規則,程序都由一
系列經優化的缺省值創建,並且只有當您明確知道如何配置您的組件時,才有必要用自己的值替換
這些缺省值。
組件 描述
Button 按鈕。類似標簽,但提供額外的功能,例如鼠標掠過、按下、釋放以及鍵盤操作/事件
Canvas 畫布。提供繪圖功能(直線、橢圓、多邊形、矩形);可以包含圖形或位圖
Checkbutton 選擇按鈕。一組方框,可以選擇其中的任意個(類似HTML 中的checkbox)
Entry 文本框。單行文字域,用來收集鍵盤輸入(類似HTML 中的text)
Frame 框架。包含其他組件的純容器
Label 標簽。用來顯示文字或圖片
Listbox 列表框。一個選項列表,用戶可以從中選擇
Menu 菜單。點下菜單按鈕后彈出的一個選項列表,用戶可以從中選擇
Menubutton 菜單按鈕。用來包含菜單的組件(有下拉式、層疊式等等)
Message 消息框。類似於標簽,但可以顯示多行文本
Radiobutton 單選按鈕。一組按鈕,其中只有一個可被“按下”(類似HTML 中的radio)
Scale 進度條。線性“滑塊”組件,可設定起始值和結束值,會顯示當前位置的精確值
Scrollbar 滾動條。對其支持的組件(文本域、畫布、列表框、文本框)提供滾動功能
Text 文本域。多行文字區域,可用來收集(或顯示)用戶輸入的文字(類似HTML 中的textarea)
Toplevel 頂級。類似框架,但提供一個獨立的窗口容器。
19.3 Tkinter 舉例
標簽組件
1 #!/usr/bin/env python 2 3 import Tkinter 4 5 top = Tkinter.Tk() 6 label = Tkinter.Label(top, text='Hello World!') 7 label.pack() 8 Tkinter.mainloop()
按鈕組件
1 #!/usr/bin/env python 2 3 import Tkinter 4 5 top = Tkinter.Tk() 6 quit = Tkinter.Button(top, text='Hello World!',\ 7 command=top.quit) 8 quit.pack() 9 Tkinter.mainloop()
1 #!/usr/bin/env python 2 3 import Tkinter 4 top = Tkinter.Tk() 5 6 hello = Tkinter.Label(top, text='Hello World!') 7 hello.pack() 8 9 quit = Tkinter.Button(top, text='QUIT', 10 command=top.quit, bg='red', fg='white') 11 quit.pack(fill=Tkinter.X, expand=1) 12 13 Tkinter.mainloop()
標簽、按鈕和進度條組件

1 #!/usr/bin/env python 2 3 from Tkinter import * 4 5 def resize(ev=None): 6 label.config(font='Helvetica -%d bold' % \ 7 scale.get()) 8 9 top = Tk() 10 top.geometry('250x150') 11 12 label = Label(top, text='Hello World!', 13 font='Helvetica -12 bold') 14 label.pack(fill=Y, expand=1) 15 16 scale = Scale(top, from_=10, to=40, 17 orient=HORIZONTAL, command=resize) 18 scale.set(12) 19 scale.pack(fill=X, expand=1) 20 21 quit = Button(top, text='QUIT', 22 command=top.quit, activeforeground='white', 23 activebackground='red') 24 quit.pack() 25 26 mainloop()
1 #!/usr/bin/env python 2 3 import os 4 from time import sleep 5 from Tkinter import * 6 7 class DirList(object): 8 9 def __init__(self, initdir=None): 10 self.top = Tk() 11 self.label = Label(self.top, 12 text='Directory Lister v1.1') 13 self.label.pack() 14 15 self.cwd = StringVar(self.top) 16 17 self.dirl = Label(self.top, fg='blue', 18 font=('Helvetica', 12, 'bold')) 19 self.dirl.pack() 20 21 self.dirfm = Frame(self.top) 22 self.dirsb = Scrollbar(self.dirfm) 23 self.dirsb.pack(side=RIGHT, fill=Y) 24 self.dirs = Listbox(self.dirfm, height=15, 25 width=50, yscrollcommand=self.dirsb.set) 26 self.dirs.bind('<Double-1>', self.setDirAndGo) 27 self.dirsb.config(command=self.dirs.yview) 28 self.dirs.pack(side=LEFT, fill=BOTH) 29 self.dirfm.pack() 30 31 self.dirn = Entry(self.top, width=50, 32 textvariable=self.cwd) 33 self.dirn.bind('<Return>', self.doLS) 34 self.dirn.pack() 35 36 self.bfm = Frame(self.top) 37 self.clr = Button(self.bfm, text='Clear', 38 command=self.clrDir, 39 activeforeground='white', 40 activebackground='blue') 41 self.ls = Button(self.bfm, 42 text='List Directory', 43 command=self.doLS, 44 activeforeground='white', 45 activebackground='green') 46 self.quit = Button(self.bfm, text='Quit', 47 command=self.top.quit, 48 activeforeground='white', 49 activebackground='red') 50 self.clr.pack(side=LEFT) 51 self.ls.pack(side=LEFT) 52 self.quit.pack(side=LEFT) 53 self.bfm.pack() 54 55 if initdir: 56 self.cwd.set(os.curdir) 57 self.doLS() 58 59 def clrDir(self, ev=None): 60 self.cwd.set('') 61 62 def setDirAndGo(self, ev=None): 63 self.last = self.cwd.get() 64 self.dirs.config(selectbackground='red') 65 check = self.dirs.get(self.dirs.curselection()) 66 if not check: 67 check = os.curdir 68 self.cwd.set(check) 69 self.doLS() 70 71 def doLS(self, ev=None): 72 error = '' 73 tdir = self.cwd.get() 74 if not tdir: tdir = os.curdir 75 76 if not os.path.exists(tdir): 77 error = tdir + ':no such file' 78 elif not os.path.isdir(tdir): 79 error = tdir + ': not a directory' 80 81 if error: 82 self.cwd.set(error) 83 self.top.update() 84 #sleep(2) # sleep ?? 85 if not (hasattr(self, 'last') \ 86 and self.last): 87 self.last = os.curdir 88 self.cwd.set(self.last) 89 self.dirs.config(\ 90 selectbackground='LightSkyBlue') 91 self.top.update() 92 return 93 94 self.cwd.set(\ 95 'FETCHING DIRECTORY CONTENTS...') 96 self.top.update() 97 dirlist = os.listdir(tdir) 98 dirlist.sort() 99 os.chdir(tdir) 100 self.dirl.config(text=os.getcwd()) 101 self.dirs.delete(0, END) 102 self.dirs.insert(END, os.curdir) 103 self.dirs.insert(END, os.pardir) 104 for eachFile in dirlist: 105 self.dirs.insert(END, eachFile) 106 print eachFile 107 self.cwd.set(os.curdir) 108 self.dirs.config(\ 109 selectbackground='LightSkyBlue') 110 111 def main(): 112 d = DirList(os.curdir) 113 mainloop() 114 115 if __name__ == '__main__': 116 main()

19.4 其他GUI 簡介