pytho GUI編程之Tkinter


摘錄 python核心編程s

GUI(Graphical User Interface)圖形用戶界面。

Tcl、Tk和Tkinter

Tkinter是python的默認GUI庫。它基於Tk工具包,該工具包最初是為工具命令語言(Tcl)設計的。Tk普及后,被移植到很多其他的腳本語言中,包括Perl(Perl/Tk)、Ruby(Ruby/Tk)和Python(Tkinter)。

安裝和使用Tkinter

Tkinter在系統中不是默認必須安裝的,可以通過在python解釋器中嘗試導入Tkinter模塊(在Python3中重命名為tkinter)來檢查Tkinter是否可用。這里我測試的環境是Python3.6,這里沒有發生錯誤:

>>> import tkinter
>>>

如果失敗了的話,就需要重新編譯Python解釋器以使用tkinter了。

tkinter和Python編程

讓GUI程序啟動和運行起來需要以下5個步驟:

  1. 導入tkinter模塊
  2. 創建一個頂層窗口對象,用於容納整個GUI應用
  3. 在頂層窗口對象之上構建所有的GUI組件及其功能
  4. 通過底層的應用代碼將這些GUI組件連接起來
  5. 進入主事件循環

窗口和控件

在GUI編程中,頂層的根窗口對象包含組成GUI應用的所有的小窗口對象。它們可能是文字標簽、按鈕、列表框等。這些獨立的GUI組件稱為控件(widget)。所以當我們說創建一個頂層窗口時,只是表示需要一個地方來擺放所有的控件。一般寫成:

top = tkinter.Tk() #或者當采用‘from tkinter import *’ 時,也可直接寫成 Tk()

tkinter.Tk()返回的對象通常稱為根窗口,這也是一些應用使用root而不是top來指代它的原因。

頂層窗口是那些在應用中獨立顯示的部分。GUI程序中可以有多個頂層窗口,但是只有一個是根窗口。

控件可以獨立存在,也可以作為容器存在。如果一個控件包含其他控件,就可以將其認為是那些控件的父控件。相應的,如果一個控件被其他控件包含,則稱這個控件是那個控件的子控件。

事件驅動處理

通常,控件有一些相關的行為,比如按下按鈕、將文本寫入文本框、敲擊回車等。這些行為稱為事件。而GUI對這類事件的響應稱為回調。

一個GUI程序從開始到結束就是通過整套事件體系來驅動的,這種方式稱為事件驅動處理。

比如,最簡單的鼠標移動。假設鼠標正停在GUI應用頂層窗口某處,將鼠標移動到另一部分,鼠標移動的行為會被復制到屏幕的光標上,於是看起來像是你的手在移動。系統必須處理的這些鼠標移動事件可以繪制窗口上的指針移動。當釋放鼠標時,不再有事件需要處理,此時屏幕會重新恢復閑置的狀態。

事件驅動的GUI處理本質上非常適合客戶端/服務器架構。當啟動一個GUI應用時,需要一些啟動步驟來准備核心部分的執行,就像網絡服務器啟動時必須先分配套接字並將其綁定到本地地址上一樣。GUI應用必須先創建所有的GUI組件,然后將他們繪制在屏幕上。這是布局管理器的職責所在。當布局管理器排列好所有空間后,GUI應用進入類似服務器的無限循環。這個循環一直運行,直到出現GUI事件,進行處理。

布局管理器

Tk有3種布局管理器來幫助控件進行定位。最原始的一種稱為Placer。他的做法非常直接:你提供控件的大小和位置,然后交給布局管理器。這就存在很大的問題,你必須對所有控件進行這些操作,開發者很大負擔。

第二種布局管理器,Packer,也是我們主要是用的。它會把控件填充到正確的位置(即指定的父控件中),然后對於之后的每個控件,回去尋找剩余的空間進行填充。這個處理很像是旅行時往行李箱中填充行李的過程。

第三種布局管理器是Grid。你可以基於網格坐標,使用Grid來指定GUI空間的放置。Grid會在它們的網格位置上渲染GUI應用中的每個對象。

本次學習中我們使用Packer。一旦Packer確定好所有控件的大小和對齊方式,它就會在屏幕上將其位置放置妥當。

當所有的控件擺放好后,可以讓應用進入無限主循環中,代碼如下:Tkinter.mainloop()

一般來說,這是程序運行的最后一段代碼。當進入主循環后,GUI就從這里開始接管程序的執行,所有其他行為都會通過回調來處理,包括退出應用。

頂層窗口:Tkinter.Tk()

之前提到的所有主要控件都是構建在頂層窗口對象之上的,該對象在tkinter中使用Tk類進行創建:

>>> import tkinter
>>> top = tkinter.Tk()

在這個窗口中,可以放置獨立的控件,也可以將多個組件拼湊在一起構成GUI程序。下面開始介紹Tk控件

Tk控件

下面介紹了18種Tk控件(更多或者更詳細的資料參考Python主站上的Tkinter主題頁):

控件 描述
Button 於Label類似,但提供額外的功能,如鼠標懸停、按下、釋放以及鍵盤事件
Canvas 提供繪制形狀的選擇(線段、橢圓、多邊形、矩形),可以包含圖像和位圖
Checkbutton 一組選框,可以勾選其中的任意個(和HTML中的checkbox類似)
Entry 單行文本框,用於收集鍵盤輸入
Frame 包含其他控件的純容器
Label 用於包含文本或圖像
LabelFrame 標簽和框架的組合,擁有額外的標簽屬性
Listbox 給用戶顯示一個選項列表進行選擇
Menu 按下Menubutton后彈出的選項列表,用戶可以從中選擇
Menubutton 用於包含菜單(下拉、級聯等)
Message 消息,於Label類似,不過可以顯示成多行
PanedWindow 一個可以控制其他控件在其中擺放的容器控件
Radiobutton 一組按鈕,其中只有一個可以按下
Scale 線型‘滑塊’控件,根據已設定的起始值和終止值,給出當前設定的精確值
Scrollbar 為Text、Canvas、Listbox、Enter等支持的控件提供滾動功能
Spinbox Entry和Button的組合,允許對值進行調整
Text 多行文本框,用於收集或顯示用戶輸入的文本
Toplevel 和Frame類似,不過他提供了一個單獨的窗口容器

下面先來看看一下簡單的使用:

Label控件

下面tkhello.py腳本中,展示了Tkinter應用如何啟動,着重強調了Label控件:

#python 3.6

import tkinter

top = tkinter.Tk()#創建頂層窗口
label = tkinter.Label(top,text='Hello world!')#創建標簽label,包含連個參數
label.pack()#使用Packer來管理和顯示控件
tkinter.mainloop()#運行GUI程序。這是必須的

運行效果:

 Button控件

下面的tkhello2.py腳本中,展示了按鈕控件:

#python 3.6

import tkinter

top = tkinter.Tk()#創建頂層窗口
quit = tkinter.Button(top,text='Hello World!',command=top.quit)#創建按鈕quit,包含三個參數,其中第三個參數給按鈕安裝了一個回調函數:當按下按鈕並釋放后,程序就會退出
quit.pack()#使用Packer來管理和顯示控件
tkinter.mainloop()#運行GUI程序。這是必須的

運行效果:

Label和Button控件

下面的tkhello3.py腳本中,結合了標簽和按鈕的功能,並使用了更多的參數:既包括控件的非默認參數,又展示了Packer的一些參數。fill參數告訴Packer讓‘退出’按鈕占據剩余的水平空間,expand參數則會引導它填充整個水平可視空間,將按鈕拉伸到左右窗口邊緣。同時,如果Packer沒有收到其他指示,所有控件都是垂直排列的(自上而下依次排列):

 

#python 3.6

import tkinter

top = tkinter.Tk()
hello = tkinter.Label(top,text='Hello Tkinter!') #創建一個標簽
hello.pack()

quit = tkinter.Button(top,text='退出',command=top.quit,bg='red',fg='white')#創建一個退出按鈕,除了綁定一個回調函數外,還設置了背景色以及釋放按鈕時的顏色
quit.pack(fill = tkinter.X,expand=1)#

tkinter.mainloop()

 

運行效果:

Label、Button和Scale控件

下面的tkhello4.py腳本中,在之前的基礎上增加了Scale控件:

#python 3.6
from tkinter import * #雖然這種寫法會污染命名空間,但是這里依然使用這種寫法是因為該腳本會大量引用此模塊

#該函數依附於Scale控件,當Scale控件的滑塊移動時,這個函數就會被激活。該函數會調整Label控件中的文本大小
def resize(ev = None):
    label.config(font = 'Helvetica -%d bold' % scale.get())
    
top = Tk()#頂層窗口
top.geometry('250x150+1000+500')#設置頂層窗口的屬性,250x150是指窗口大小,1000+500是指窗口在屏幕上的位置

#創建一個Label控件,擁有默認大小的字體
label = Label(top,text = 'hello world!',font = 'Helvetica -12 bold')
label.pack(fill = Y,expand=1)#通過Packer管理和顯示label控件
#創建Scale控件,該控件通過回調resize函數用來控制Label控件中字體大小的;from_屬性是滑塊最小值,to是滑塊最大值
scale = Scale(top,from_=10,to=40,orient=HORIZONTAL,command=resize)
scale.set(30)#設置滑塊的初始值
scale.pack(fill=Y,expand = 1)#通過Packer管理和顯示Scale控件

#創建一個退出按鈕控件,按下按鈕前的顏色是白色,釋放按鈕后的顏色是紅色
quit = Button(top,text='退出',command=top.quit,activeforeground='white',activebackground='red')
quit.pack()#通過Packer管理和顯示Button控件

mainloop()#主循環

運行效果:

 tkinter結合偏函數應用實例

何謂偏函數:當函數的參數個數比較多的時候,可以使用functools.partial創建一個新的函數,這個新函數會固定住(凍住)原函數的部分參數,之后,當獲得需要的剩余參數后,可以將它們解凍,一起傳入到最終的參數中,從而使用最終確定的所有參數去調用函數。簡單說就是:。functools.partial會把一個函數的某些參數設置默認值,並返回一個新函數,我們可以直接調用這個新函數。友情鏈接:偏函數

偏函數的一大優點就是它不局限於函數,而是可以用於任何可調用對象(任何包括函數接口的對象),只需要使用圓括號即可,包括類、方法或可調用實例。

對於很多可調用對象,並且許多調用都反復使用相同參數的情況,使用偏函數會更合適。

GUI編程是一個很好的偏函數用例,因為你很有可能需要GUI控件在外觀上具有某種一致性,而這種一致性來自於使用相同參數創建相似的對象時。想象有這么一個應用,有很多按鈕擁有相同的背景色和前景色,對於這種只有細微差別的按鈕,每次都使用相同的參數創建相同的實例簡直是一種負擔和浪費。

下面的pfaGUI2.py腳本中,展示了一個交通路標的實例,該應用會嘗試創建文字版本的路標,並將其根據標志類型進行區分,比如嚴重、警告、通知。而標志類型決定了創建時的顏色方案:嚴重級別的是白底紅字,警告級別的是黃底黑字,通知級別的是白底黑字。這里,‘嚴禁駛入’和‘錯誤路線’屬於嚴重級別,‘交通擁堵’‘火車交匯口’屬於警告級別,而‘限速’和‘單行線’屬於通知級別。該應用會創建這些標志的按鈕,當用戶按下按鈕時,會彈出相應的對話框。

這里要注意的版本問題,python2.x中的tkMessageBox模塊在python3.x中被重命名為messagebox,並被整合進tkinter模塊中:

#python 3.6

from functools import partial#使用偏函數
from tkinter import Tk,X,Button#僅僅導入tkinter模塊中用到的屬性
from tkinter.messagebox import showinfo,showwarning,showerror #python 3.xb版本中對tkMessageBox重命名。這里僅僅導入三個對話框
 #python2.x中使用from tkMessageBox import showinfo,showwarning,showerror

#定義了三個標志 警告、嚴重、通知
WARN = 'warn'
CRIT = 'crit'
REGU = 'regu'
SIGNS = {
    '嚴謹駛入': CRIT,
    '火車交匯': WARN,
    '限速': REGU,
    '錯誤路線': CRIT,
    '交通擁堵': WARN,
    '單行線': REGU,
    'do not': CRIT
}

#對話框用作按鈕的回調函數,將在創建每個按鈕時使用他們
critCB = lambda:showerror('Error','按下錯誤按鈕')
warnCB = lambda:showwarning('Warning','按下警告按鈕')
infoCB  = lambda:showinfo('Info','按下通知按鈕')
#啟動Tk,設置標題、位置
top = Tk()
top.title('路標')
top.geometry('250x250+700+500')#設置頂層窗口的屬性,250x250是指窗口大小,1000+500是指窗口在屏幕上的位置
Button(top,text='退出',command = top.quit,bg = 'red',fg = 'white').pack() #創建一個退出按鈕。設置前景色、背景色,並用Packer管理

MyButton = partial(Button,top)#創建了一個一階偏函數,模板化Button類和根窗口top。MyButton效果相當於tkinter.Button() 並將top作為他的第一個參數
#下面是三個二階偏函數。二階偏函數是對一階偏函數的再次模板化。最終效果相當於使用top、回調函數和顏色這幾個參數去調用Button。
CritButton = partial(MyButton,command = critCB,bg = 'white',fg = 'red')
WarnButton = partial(MyButton,command = warnCB,bg = 'yellow')
ReguButton = partial(MyButton,command = infoCB,bg = 'white')

for eachSign in SIGNS:
    signType = SIGNS[eachSign]
    #構建一個可求值字符串cmd,該字符串包含按鈕名、傳給按鈕標簽的文本參數 和 pack()操作組成。如果是嚴重級別,會把字符大寫,否則按照標題格式輸出。這里還使用了三元操作符。格式化字符串的時候要注意%s和%r的區別.
    #標題化函數title(),即所有單詞的首字母都大寫,其他的字母都小寫
    cmd = '%sButton(text = %r%s).pack(fill = X,expand = True)' % \
    (signType.title(),eachSign,'.upper()' if signType ==CRIT else '.title()') #
    eval(cmd)#該函數用於執行一個字符串表達式,這里是實例化按鈕
    
top.mainloop()#主循環,用於啟動GUI程序

運行效果圖:

 

自此,我們算是入門了。下節我們展示一個中級的tkinter示例~咩

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM