3.1 tkinter 之 主題化的基礎(chǔ) widgets 詳解

先載入一些必備包:

from tkinter import ttk, Tk
from tkinter import N, W, E, S
from tkinter import HORIZONTAL, VERTICAL
from tkinter import StringVar

1 標(biāo)簽:ttk.Label

標(biāo)簽是顯示文本或圖像的小部件建瘫,用戶通常只能查看文本或圖像,但不能與之交互。標(biāo)簽用于識(shí)別控件或用戶界面的其他部分、提供文本反饋或結(jié)果等弯屈。一般的使用方法:

label = ttk.Label(parent, text='')

下面詳細(xì)介紹其主要參數(shù),在 tk/tcl 中被稱為配置選項(xiàng)(configuration option):

1.1 展示文本

在窗口顯示的信息是由 text 設(shè)定的拇勃,對(duì)于用戶來(lái)說(shuō)全释,是不可更改的,但對(duì)于開(kāi)發(fā)者來(lái)說(shuō)闭翩,它是可以改變的挣郭。

root = Tk() # 主窗口
# 設(shè)置標(biāo)簽
label = ttk.Label(root, text='您好')
label.grid() # 在 root 布局 

可以通過(guò)索引的方式獲取文本信息:

label['text']

顯示結(jié)果為:

'您好'

也可以通過(guò) configure 函數(shù)獲取文本信息:

label.configure('text')

顯示結(jié)果為:

('text', 'text', 'Text', '', '您好')

由于有兩種獲取文本信息的方式,所以疗韵,對(duì)應(yīng)的也有兩種修改文本信息的方式:

  1. 索引賦值
label['text'] = '早上好'
  1. 修改 configuretext 參數(shù)的值:
label.configure(text='早上好')

1.2 監(jiān)視標(biāo)簽

可以使用 "textvariable" 選項(xiàng)監(jiān)視您的代碼的變量兑障,同時(shí)也支持修改變量取值。

res = StringVar() # 用于監(jiān)視的變量
label['textvariable'] = res

使用 set 方法可以修改監(jiān)視變量:

res.set('再見(jiàn)')

此時(shí) label['text'] 的取值也被改變:

label['text']

顯示結(jié)果為:

'再見(jiàn)'

使用get方法可以獲取監(jiān)視變量的取值:

res.get()

顯示結(jié)果為:

'再見(jiàn)'

1.3 顯示圖片

展示圖片需要兩步,首先使用 PhotoImage 載入圖片旺垒,最后使用 ttk.Label'image' 選項(xiàng)彩库。

from tkinter import PhotoImage
# 先載入圖片
img = PhotoImage(file='images/image.gif')
label['image'] = img # 設(shè)置配置選項(xiàng)

可以設(shè)置 'compound' 選項(xiàng)用于設(shè)定圖片與文本的混搭顯示布局。取值的含義分別為 'text'(僅顯示文本)先蒋,'image'(僅顯示圖片)骇钦,'center'(文本在圖片中央),'top'(圖片在文本上方)竞漾,'left'眯搭,'bottom''right'。比如:

label['compound'] = 'top'

1.4 布局設(shè)置

雖然標(biāo)簽的整體布局(即標(biāo)簽位于用戶界面中的位置和大幸邓辍)由幾何管理器確定鳞仙,但幾個(gè)特殊的選項(xiàng)可以幫助您更好地控制標(biāo)簽在幾何管理器提供的框中的顯示方式。

如果提供給標(biāo)簽的框大于標(biāo)簽對(duì)其內(nèi)容的要求笔时,則可以使用 "anchor" 選項(xiàng)指定標(biāo)簽應(yīng)附加到哪個(gè)邊或角棍好,這將在相反的邊緣或角中留下任何空白≡使ⅲ可能的值被指定為指南針?lè)较颍?code>"n"(北邊或上邊緣)借笙、"ne"(東北或右上角)、"e"较锡、"s"业稼,"sw""w"蚂蕴,"nw""center"低散。

您還可以使用 "justify" 選項(xiàng)控制文本的對(duì)齊方式,該選項(xiàng)可以具有值 "left", "center" or "right"骡楼。如果只有一行文本熔号,這與使用 "anchor" 選項(xiàng)幾乎相同,但多行文本更有用君编。

比如跨嘉,設(shè)置:

label['anchor'] = 'sw'

1.5 字體、顏色等

帶有 "style" 選項(xiàng)的小部件可用來(lái)修改字體吃嘿、顏色等祠乃。您可以使用 "font" 配置選項(xiàng)指定用于顯示標(biāo)簽文本的字體。

圖1 字體選項(xiàng)

前景(文本)和背景顏色也可以通過(guò) "foreground""background" 選項(xiàng)進(jìn)行更改兑燥。

label['font'] = 'Arial 20'
label["foreground"] = 'red'
label["background"] = 'lightblue'
# 顯示窗口
root.mainloop()

顯示結(jié)果為:

圖2 ttk.Label 示例

2 文本輸入框:ttk.Entry

Entry(文本輸入框)為用戶提供了一個(gè)單行文本字段亮瓷,他們可以用來(lái)輸入字符串值。 這些幾乎可以是任何東西:名字降瞳,城市嘱支,密碼蚓胸,社會(huì)保險(xiǎn)號(hào)等等。

使用 ttk.Entry 函數(shù)創(chuàng)建 Entry除师∨嫔牛可以指定 "width" 配置選項(xiàng),以提供 Entry 的寬的字符數(shù)汛聚。例如锹安,允許您為郵政編碼提供一個(gè)較短的 Entry。

通常使用 "textvariable" 配置選項(xiàng)指定的鏈接變量來(lái)訪問(wèn) Entry 的值倚舀。 請(qǐng)注意叹哭,與各種按鈕不同,Entry 旁邊沒(méi)有單獨(dú)的文本或圖像來(lái)標(biāo)識(shí)它們痕貌。 為此使用單獨(dú)的標(biāo)簽小部件风罩。

您也可以直接獲取或更改 Entry 小部件的值,而無(wú)需通過(guò)鏈接變量舵稠。 "get" 方法返回當(dāng)前值超升,"delete""insert"(對(duì)應(yīng)插入光標(biāo)的當(dāng)前位置),"end"(對(duì)應(yīng)已存在文本的后一個(gè)位置),"anchor"(如果有的話哺徊,對(duì)應(yīng)第一個(gè)被選中的字符)方法可讓您更改內(nèi)容廓俭,例如

print('current value is %s' % name.get())
name.delete(0,'end')          # delete between two indices, 0-based
name.insert(0, 'your name')   # insert new text at a given index

請(qǐng)注意,Entry小部件沒(méi)有 "command" 選項(xiàng)唉工,每當(dāng) Entry 更改時(shí),該選項(xiàng)都將調(diào)用回調(diào)汹忠。 要監(jiān)視更改淋硝,您應(yīng)該監(jiān)視鏈接變量的更改。

2.1 密碼:Passwords

Entry可用于密碼宽菜,其中實(shí)際內(nèi)容顯示為項(xiàng)目符號(hào)或其他符號(hào)谣膳。 為此,將 "show" 配置選項(xiàng)設(shè)置為要顯示的字符铅乡,例如 “*”继谚。

下面直接看一個(gè)例子:

from tkinter import ttk, Tk, StringVar

if __name__ == '__main__':
    root = Tk() # 主窗口
    root.title('Entry 測(cè)試')
    root.geometry('400x300') # 設(shè)定窗口大小
    user_name = StringVar() # 記錄用戶名稱
    user_name.set('Tom')
    # 設(shè)置字符個(gè)數(shù)不得超過(guò) 7
    name = ttk.Entry(root, textvariable=user_name, width=7) # root 上的 Entry 小部件
    name.pack()
    # 隱藏名稱
    hide_name = ttk.Entry(root, textvariable=user_name, show=' ')
    hide_name.pack()
    # 隱藏為 *
    hide_name1 = ttk.Entry(root, textvariable=user_name, show='*')
    hide_name1.pack()
    root.mainloop()

顯示結(jié)果為:

圖3 Entry 的一個(gè)例子

2.2 小部件狀態(tài):Widget States

像各種按鈕一樣,也可以通過(guò) "state" 命令將 Entry 置于禁用狀態(tài)(并使用 "instate" 查詢)阵幸。 Entry 也可以使用狀態(tài)標(biāo)志 "readonly"花履; 如果設(shè)置,則用戶無(wú)法更改 Entry挚赊,盡管他們?nèi)匀豢梢赃x擇其中的文本(并將其復(fù)制到剪貼板)诡壁。 還有一個(gè) "invalid" 狀態(tài),如果輸入窗口小部件驗(yàn)證失敗則設(shè)置荠割。(具體說(shuō)明見(jiàn)按鈕:ttk.Button)

2.3 驗(yàn)證輸入內(nèi)容的合法性的

我們需要利用 ttk.Entry'validate''validatecommand' 選項(xiàng)妹卿,檢查輸入的文本是否合法旺矾,具體的步驟是:

  1. 定義一個(gè)負(fù)責(zé)檢查輸入內(nèi)容的回調(diào)函數(shù),如果合法則返回 True夺克,否則返回 False箕宙;
  2. 使用 ttk.Entry 的方法 register 將回調(diào)函數(shù)封裝為 Tcl,它會(huì)返回一個(gè)字符串铺纽,用它來(lái)設(shè)定 'validatecommand' 選項(xiàng)柬帕;
  3. 設(shè)置 'validate',聲明調(diào)用回調(diào)函數(shù)的時(shí)機(jī)室囊,常用的選項(xiàng)有:
    • 'focus':輸入框獲取或者失去焦點(diǎn)時(shí)
    • 'focusin':輸入框獲取焦點(diǎn)時(shí)
    • 'focusin':輸入框失去焦點(diǎn)時(shí)
    • 'key':內(nèi)容改變時(shí)
    • 'all':以上任何情況發(fā)生時(shí)
    • 'none':關(guān)閉內(nèi)容檢查雕崩,這是默認(rèn)值

具體的選項(xiàng)描述見(jiàn)下表:

選項(xiàng) 描述
'validate' 該選項(xiàng)設(shè)置是否啟用內(nèi)容驗(yàn)證
'invalidcommand' 1. 指定當(dāng)輸入框輸入的內(nèi)容“非法”時(shí)調(diào)用的函數(shù);2. 也就是指定當(dāng) 'validatecommand' 選項(xiàng)指定的函數(shù)返回 False 時(shí)的函數(shù)
'validatecommand' 1. 該選項(xiàng)指定一個(gè)驗(yàn)證函數(shù)融撞,用于驗(yàn)證輸入框內(nèi)容是否合法盼铁;2. 驗(yàn)證函數(shù)需要返回 TrueFalse 表示驗(yàn)證結(jié)果;3. 注意尝偎,該選項(xiàng)只有當(dāng) 'validate' 的值非 "none" 時(shí)才有效

比如饶火,下面的代碼驗(yàn)證輸入內(nèi)容是否為 "Python":

class Window(Tk):
    def __init__(self):
        super().__init__()
        self.in_var = StringVar() # 輸入變量
        self.out_var = StringVar()  # 輸出變量
        self.input_entry = ttk.Entry(textvariable=self.in_var)
        self.input_entry['validate'] = "focusout"
        self.input_entry['validatecommand'] = self.test
        # 測(cè)試 self.input_entry 光標(biāo) 離開(kāi)之后的驗(yàn)證
        self.show_entry = ttk.Entry(textvariable=self.out_var)
        self._layout()
        
    def test(self):
        '''驗(yàn)證輸入內(nèi)容是否為 Python'''
        if self.input_entry.get() == 'Python':
            self.out_var.set('輸入正確')
            return True
        else:
            self.out_var.set('輸入錯(cuò)誤')
            return False
        
    def _layout(self):
        self.input_entry.grid()
        self.show_entry.grid()

window = Window()
window.mainloop()

添加如下兩個(gè)設(shè)置:

self.input_entry['invalidcommand'] = self.test2
def test2(self):   
    self.show_entry.insert('end', " 我被調(diào)用了......")    
    return True

便可在驗(yàn)證 'invalidcommand' 的使用情況。

2.4 使用注冊(cè)機(jī)制驗(yàn)證輸入內(nèi)容的合法性的

我們也可以使用注冊(cè)機(jī)制驗(yàn)證輸入內(nèi)容的合法性的致扯。即使用配置選項(xiàng) validatecommand=(register_func, s1, s2, ...)肤寝,其中 register_func 為驗(yàn)證函數(shù)名,s1抖僵、s2 這些是額外的選項(xiàng)鲤看,這些選項(xiàng)會(huì)作為參數(shù)依次傳給 register_func 函數(shù)。下表列出額外的選項(xiàng)的描述:

額外選項(xiàng) 含義
'%d' 操作代碼:0 表示刪除操作耍群;1 表示插入操作义桂;2 表示獲得、失去焦點(diǎn)或 'textvariable' 變量的值被修改
'%i' 1. 當(dāng)用戶嘗試插入或刪除操作的時(shí)候蹈垢,該選線表示插入或刪除的位置(索引號(hào))慷吊;2. 如果是由于獲得、失去焦點(diǎn)或 'textvariable' 變量的值被修改而調(diào)用驗(yàn)證函數(shù)曹抬,那么該值是 -1
'%P' 1. 當(dāng)輸入框的值允許改變的時(shí)候溉瓶,該值有效;2. 該值為輸入框的最新文本內(nèi)容
'%s' 該值為調(diào)用驗(yàn)證函數(shù)前輸入框的文本內(nèi)容
'%S' 1. 當(dāng)插入或刪除操作觸發(fā)驗(yàn)證函數(shù)的時(shí)候谤民,該值有效堰酿;2. 該選項(xiàng)表示文本被插入和刪除的內(nèi)容
'%v' 該組件當(dāng)前的 'validate' 選項(xiàng)的值
'%V' 1. 調(diào)用驗(yàn)證函數(shù)的原因;2. 該值是 'focusin'赖临,'focusout'胞锰,'key' 或 'forced'('textvariable' 選項(xiàng)指定的變量值被修改)中的一個(gè)
'%W' 該組件的名字

下面看一個(gè)例子:

class Window(Tk):
    def __init__(self):
        super().__init__()
        self.in_var = StringVar() # 輸入變量
        self.out_var = StringVar()  # 輸出變量
        self.input_entry = ttk.Entry(textvariable=self.in_var)
        self.input_entry['validate'] = "focusout"
        self.test_cmd= self.register(self.test) # 注冊(cè)
        self.input_entry['validatecommand'] = (self.test_cmd, '%P', '%v', '%W')
        # 測(cè)試 self.input_entry 光標(biāo) 離開(kāi)之后的驗(yàn)證
        self.show_label = ttk.Label(textvariable=self.out_var)
        self._layout()
        
        
    def test(self, content, reason, name):
        '''驗(yàn)證輸入內(nèi)容是否為 Python'''
        if content == 'Python':
            str1 = '輸入正確\n'
            str1 += f"{content}, {reason}, {name}"
            self.out_var.set(str1)
            return True
        else:
            str1 = '輸入錯(cuò)誤\n'
            str1 += f"{content}, {reason}, {name}"
            self.out_var.set(str1)
            return False
        
    def _layout(self):
        self.input_entry.grid()
        self.show_label.grid()

window = Window()
window.mainloop()

顯示效果:

圖4 驗(yàn)證輸入內(nèi)容

您也可以使用類的重寫(xiě)功能修改驗(yàn)證內(nèi)容,比如:

class ValidatingEntry(Entry):
    # base class for validating entry widgets

    def __init__(self, master, value="", **kw):
        apply(Entry.__init__, (self, master), kw)
        self.__value = value
        self.__variable = StringVar()
        self.__variable.set(value)
        self.__variable.trace("w", self.__callback)
        self.config(textvariable=self.__variable)

    def __callback(self, *dummy):
        value = self.__variable.get()
        newvalue = self.validate(value)
        if newvalue is None:
            self.__variable.set(self.__value)
        elif newvalue != value:
            self.__value = newvalue
            self.__variable.set(self.newvalue)
        else:
            self.__value = value

    def validate(self, value):
        # override: return value, new value, or None if invalid
        return value

3 按鈕:ttk.Button

按鈕非常適合用戶交互兢榨,特別是點(diǎn)擊按鈕以執(zhí)行某些操作嗅榕。與標(biāo)簽一樣顺饮,它們可以顯示文本或圖像,但也具有用于控制其行為的一系列新選項(xiàng)凌那。通常它們的內(nèi)容和命令回調(diào)是同時(shí)設(shè)置的:

button = ttk.Button(parent, text='Okay', command=submitForm)

3.1 文本或圖像

按鈕同樣有的 "text", "textvariable"(很少使用)兼雄,"image""compound" 配置選項(xiàng)作為標(biāo)簽,用于控制按鈕是否顯示文本或圖像(同 ttk.Entry)帽蝶。按鈕還有一個(gè) "default" 選項(xiàng)赦肋,告訴 Tk 按鈕是用戶界面中的默認(rèn)按鈕(即,如果用戶點(diǎn)擊 Enter 或 Return 將調(diào)用的按鈕)(可以使用鍵盤(pán)的 Tab 進(jìn)行按鈕間的跳轉(zhuǎn))励稳。將此選項(xiàng)設(shè)置為 "active" 以指定這默認(rèn)按鈕為激活狀態(tài)佃乘;常規(guī)狀態(tài)為 "normal"。請(qǐng)注意驹尼,設(shè)置此選項(xiàng)不會(huì)創(chuàng)建事件綁定趣避,需要使返回或輸入鍵激活按鈕。示例如下:

root = Tk()
root.geometry('200x100')
b = ttk.Button(root, text='普通狀態(tài)')
b.grid()
b1 = ttk.Button(root, text='激活狀態(tài)')
b1.grid() 
b1["default"] = "active"
root.mainloop()

顯示結(jié)果為:

圖5 ttk.Button 的不同默認(rèn)狀態(tài)

3.2 按鈕狀態(tài)

按鈕和許多其他小部件的狀態(tài)可以是:正常狀態(tài)新翎,被按下的狀態(tài)程帕,被禁用狀態(tài)(按鈕灰顯且無(wú)法按下,當(dāng)按鈕的命令在給定時(shí)間點(diǎn)不適用時(shí)地啰,將執(zhí)行此操作)愁拭。

其實(shí),所有主題小部件都帶有一個(gè)內(nèi)部狀態(tài)亏吝,這是一系列二進(jìn)制標(biāo)志岭埠。您可以設(shè)置或清除這些不同的標(biāo)志,以及使用 "state""instate" 方法檢查當(dāng)前設(shè)置蔚鸥。按鈕使用 "disabled" 標(biāo)志來(lái)控制用戶是否可以按下該按鈕枫攀。例如:

圖6 按鈕狀態(tài)

主題小部件可用的狀態(tài)標(biāo)志的列表有:"active", "disabled", "focus", "pressed", "selected", "background", "readonly", "alternate" 和 "invalid"。這些都在主題小部件參考中有描述株茶。

3.3 命令回調(diào)

"command" 選項(xiàng)用于在按鈕的操作和應(yīng)用程序之間提供接口。當(dāng)用戶單擊按鈕時(shí)图焰,解釋器將調(diào)用該選項(xiàng)提供的腳本启盛。直接看一個(gè)例子:

root = Tk()
var = StringVar()    # 設(shè)置文字變量?jī)?chǔ)存器
lb = ttk.Label(root,
              textvariable=var,   # 使用 textvariable 替換 text, 因?yàn)檫@個(gè)可以變化
              width=15,
              background='green', font='Arial 18')
lb.grid()

on_hit = False  # 默認(rèn)初始狀態(tài)為 False


def hit_me():
    global on_hit
    if on_hit:  # 從 True 狀態(tài)變成 False 狀態(tài)
        on_hit = False
        var.set('')  # 設(shè)置 文字為空
    else:     # 從 False 狀態(tài)變成 True 狀態(tài)
        on_hit = True
        var.set('你打了我')   # 設(shè)置標(biāo)簽的文字


b = ttk.Button(root, text='打我', command=hit_me)
b.grid()
root.mainloop()

顯示結(jié)果為:

圖7 ttk.Button 命令回調(diào)的例子

實(shí)現(xiàn)的功能是:當(dāng)點(diǎn)擊按鈕時(shí),顯示文字 你打了我技羔,再點(diǎn)一次文字便會(huì)消失僵闯。

還可以要求按鈕從應(yīng)用程序調(diào)用(invoke)命令回調(diào)。這很有用藤滥,因此您不需要重復(fù)在程序中多次使用 command鳖粟,即使按鈕的狀態(tài)發(fā)生改變也無(wú)需修改代碼∽景恚可以看一個(gè)例子:

class ClickInvoke(Tk):
    def __init__(self):
        super().__init__()
        self.lb_var1 = StringVar()
        self.lb_var2 = StringVar()
        self.layout()

    def layout(self):
        self.lb1 = ttk.Label(textvariable=self.lb_var1, widt=15,
                             background='green', font='Arial 18')
        self.lb2 = ttk.Label(textvariable=self.lb_var2, widt=15,
                             background='red', font='Arial 18')
        self.b1 = ttk.Button(text='Button 1', command=self.click1)
        self.b2 = ttk.Button(text='Button 2', command=self.click2)
        self.b3 = ttk.Button(text='Button 3', command=self.click3)
        self.lb1.grid()
        self.b1.grid(column=0, row=1, sticky=(W, E))
        self.b2.grid(column=1, row=1, sticky=(W, E))
        self.b3.grid(column=2, row=1, sticky=(W, E))
        self.lb2.grid()

    def click1(self):
        self.lb_var1.set('Button 1 clicked.')
        self.lb_var2.set('')

    def click2(self):
        self.lb_var2.set('Button 2 clicked.')
        self.lb_var1.set('')

    def click3(self):
        self.b1.invoke() # 調(diào)用按鈕 `Button 1`
        self.lb_var2.set('Button 3 clicked.')

app = ClickInvoke()
app.mainloop()

顯示的結(jié)果為:

圖8 ttk.Button 的 invoke 使用

實(shí)現(xiàn)的功能是:點(diǎn)擊按鈕 Button 1 時(shí)僅僅顯示綠色區(qū)域的文字向图,點(diǎn)擊按鈕 Button 2 僅僅顯示紅色區(qū)域的文字泳秀,點(diǎn)擊按鈕 Button 3 時(shí),紅色區(qū)域的文字發(fā)生改變榄攀,同時(shí)也回調(diào)了按鈕 Button 1嗜傅。

4 復(fù)選按鈕:ttk.Checkbutton

復(fù)選按鈕類似于常規(guī)按鈕,不同之處在于用戶不僅可以按下它調(diào)用命令回調(diào)檩赢,而且還包含某種二進(jìn)制值(如 toggle)吕嘀。當(dāng)要求用戶在兩個(gè)選項(xiàng)(例如,兩個(gè)不同的值)之間進(jìn)行選擇時(shí)贞瞒,便可使用復(fù)選按鈕偶房。

創(chuàng)建方法:

measureSystem = StringVar()
check = ttk.Checkbutton(parent, text='Use Metric',
                        command=metricChanged, 
                        variable=measureSystem, 
                        onvalue='metric', offvalue='imperial')

復(fù)選按鈕使用的選項(xiàng)與常規(guī)按鈕相同: "text", "textvariable"军浆,"image" 和 "compound" 選項(xiàng)控制標(biāo)簽的顯示(復(fù)選框本身旁邊)棕洋,并且 "state" 和 "instate" 方法允許您設(shè)置 " disabled" 狀態(tài)標(biāo)志以啟用或禁用復(fù)選按鈕。 同樣瘾敢, "command" 選項(xiàng)使您可以指定每次用戶切換檢查按鈕時(shí)都要調(diào)用的腳本拍冠, "invoke" 方法也將執(zhí)行相同的回調(diào)。

我們之前已經(jīng)了解了如何使用 "textvariable" 選項(xiàng)將小部件的標(biāo)簽與程序中的變量相關(guān)聯(lián)簇抵。 與之類似庆杜,復(fù)選按鈕使用 "variable" 選項(xiàng)用于讀取或更改窗口小部件的當(dāng)前值,并在切換窗口小部件時(shí)進(jìn)行更新碟摆。 默認(rèn)情況下晃财,選中小部件時(shí)典蜕,復(fù)選按鈕使用的值為 “1” ,未選中時(shí)使用的值為 “0” ,可以使用 "onvalue"(選中的取值)和 "offvalue"(未選中的取值) 選項(xiàng)將其更改為幾乎所有值。

看一個(gè)例子:

from tkinter import IntVar

class MyLove(Tk):
    def __init__(self):
        super().__init__()
        self.title('愛(ài)好')  # 設(shè)定標(biāo)題
        self.geometry('200x100')  # 設(shè)定尺寸
        self.var1 = IntVar()
        self.var2 = IntVar()
        self.lb = ttk.Label(background='yellow', width=20, text='')
        self.c1 = ttk.Checkbutton(text='Python', variable=self.var1,
                                  command=self.print_love)
        self.c2 = ttk.Checkbutton(text='C++', variable=self.var2,
                                  command=self.print_love)
        self.lb.grid()
        self.c1.grid()
        self.c2.grid()

    def print_love(self):
        if (self.var1.get() == 1) & (self.var2.get() == 0):
            self.lb.config(text='僅僅喜歡 Python ')
        elif (self.var1.get() == 0) & (self.var2.get() == 1):
            self.lb.config(text='僅僅喜歡 C++')
        elif (self.var1.get() == 0) & (self.var2.get() == 0):
            self.lb['text'] = '都不喜歡'
        else:
            self.lb['text'] = '都是我的菜'

root = MyLove()
root.mainloop()

顯示結(jié)果為:

圖9 ttk.Checkbutton 的一個(gè)示例

當(dāng)鏈接的變量既不包含 "onvalue" 也不包含 "offvalue"(甚至不存在)時(shí)碍讨,會(huì)發(fā)生什么? 在這種情況下,復(fù)選按鈕將進(jìn)入特殊的 "tristate" 或不確定模式净捅; 您有時(shí)會(huì)在用戶界面中看到此信息,其中的復(fù)選框僅包含一個(gè)破折號(hào),而不是空白或帶有復(fù)選標(biāo)記。 在這種狀態(tài)下柑晒,將設(shè)置狀態(tài)標(biāo)志 "alternate",因此可以使用 "instate" 方法進(jìn)行檢查:

check.instate(['alternate'])

由于復(fù)選按鈕不會(huì)自動(dòng)設(shè)置(或創(chuàng)建)鏈接變量嗅绸,因此您的程序需要確保將變量設(shè)置為適當(dāng)?shù)钠鹗贾怠?/p>

5 單選按鈕:ttk.Radiobutton

單選按鈕使您可以在多個(gè)互斥的選項(xiàng)之一之間進(jìn)行選擇羹铅;與選擇按鈕不同,它不僅限于兩個(gè)選擇芳室。 單選按鈕始終在一組中一起使用,當(dāng)選擇的數(shù)量相當(dāng)少(例如3-5)時(shí),它是一個(gè)不錯(cuò)的選擇。

單選按鈕是使用 ttk.Radiobutton 函數(shù)創(chuàng)建的,通常作為一個(gè)集合來(lái)創(chuàng)建:

phone = StringVar()
home = ttk.Radiobutton(parent, text='Home', variable=phone, value='home')
office = ttk.Radiobutton(parent, text='Office', variable=phone, value='office')
cell = ttk.Radiobutton(parent, text='Mobile', variable=phone, value='cell')

單選按鈕與復(fù)選按鈕共享大多數(shù)相同的配置選項(xiàng)禁悠。 一個(gè)例外是將 "onvalue""offvalue" 選項(xiàng)替換為單個(gè) "value" 選項(xiàng)站玄。 該集合中的每個(gè)單選按鈕將具有相同的鏈接變量,但具有不同的值。 當(dāng)變量具有給定值時(shí)晾剖,單選按鈕將被選中锉矢,否則未選中。 當(dāng)鏈接的變量不存在時(shí)齿尽,單選按鈕還會(huì)顯示 "tristate" 或不確定狀態(tài)沽损,可以通過(guò) "alternate" 狀態(tài)標(biāo)志進(jìn)行檢查。

看一個(gè)例子:

class RadioLove(Tk):
    def __init__(self):
        super().__init__()
        self.title('獨(dú)愛(ài)')  # 設(shè)定標(biāo)題
        self.geometry('200x100')  # 設(shè)定尺寸
        self.var = StringVar()
        self.lb = ttk.Label(background='yellow', width=20, text='')
        self.lb.grid()
        r1 = self.radio('選 蘋(píng)果', "蘋(píng)果")
        r2 = self.radio('選 香蕉', "香蕉")
        r3 = self.radio('選 橘子', "橘子")
        [r.grid() for r in (r1, r2, r3)]

    def print_love(self):
        self.lb['text'] = f"鐘愛(ài)于{self.var.get()}"

    def radio(self, text, value):
        kw = {
            'text': text,
            'variable': self.var,
            'value': value,
            'command': self.print_love
        }
        return ttk.Radiobutton(**kw)


root = RadioLove()
root.mainloop()

顯示結(jié)果為:

圖10 ttk.Radiobutton 的一個(gè)示例

6 ttk.Frame:框架

框架是顯示為簡(jiǎn)單矩形框的小部件循头∶喙溃框架主要用作其他小部件的容器〈叮框架是使用 ttk.Frame 類創(chuàng)建的

frame = ttk.Frame(parent)

框架可以采用幾種不同的配置選項(xiàng)改變其的顯示方式壹士。

6.1 要求的尺寸

像任何其他小部件一樣,創(chuàng)建后偿警,它通過(guò)(父)幾何管理器添加到用戶界面躏救。 通常,框架將向幾何管理器請(qǐng)求的大小將由其中包含的任何小部件的大小和布局確定(這些小部件在管理框架本身內(nèi)容的幾何管理器的控制下)螟蒸。

如果出于某種原因盒使,您想要一個(gè)不包含其他小部件的空框架,則應(yīng)該使用 "width" 或者 "height" 配置選項(xiàng)顯式設(shè)置框架從其父幾何管理器請(qǐng)求的尺寸(否則七嫌,最終會(huì)得到一個(gè)很小的框架)少办。

一般地,幾何管理器的尺寸(Size)使用 "width" 或者 "height" 的配置選項(xiàng)進(jìn)行設(shè)定诵原,其單位默認(rèn)是 pixel(像素)英妓。比如 "350","350c"绍赛,"350i"蔓纠,"350p" 分別表示“350像素”,“350厘米”吗蚌,“350英寸”腿倚,“350打印機(jī)點(diǎn)(1/72 英寸)”。

6.2 Padding:填充

"padding" 配置選項(xiàng)用于在窗口小部件內(nèi)部請(qǐng)求額外的空間蚯妇。這樣敷燎,如果您要在框架中放置其他小部件,則始終會(huì)有一些余量箩言。 單個(gè)數(shù)字始終指定相同的填充硬贯,兩個(gè)數(shù)字的列表可讓您指定水平和垂直填充,而四個(gè)數(shù)字的列表可讓您依次指定左側(cè)陨收,頂部饭豹,右側(cè)和底部的填充。比如:

frame['padding'] = (5,10)

6.3 邊框:Borders

為此,您需要設(shè)置 "borderwidth" 配置選項(xiàng)(默認(rèn)為 0墨状,即沒(méi)有邊框),以及 "relief" 選項(xiàng)菲饼,該選項(xiàng)指定邊框的視覺(jué)外觀: "flat" (默認(rèn))肾砂,"raised", "sunken" 宏悦,"solid"镐确,"ridge" 或 "groove"。比如:

frame['borderwidth'] = 2
frame['relief'] = 'sunken'

6.4 改變風(fēng)格

還有一個(gè) "style" 配置選項(xiàng)饼煞,這對(duì)于所有主題小部件都是通用的源葫,它可以讓您控制其外觀或行為的幾乎任何方面。比如:

style = ttk.Style()
style.configure("BW.TLabel", foreground="black", background="white")

l1 = ttk.Label(text="Test", style="BW.TLabel")
l2 = ttk.Label(text="Test", style="BW.TLabel")

7 一個(gè)例子:創(chuàng)建一個(gè)英尺轉(zhuǎn)換為米的工具

代碼:

from tkinter import ttk, Tk, StringVar
from tkinter import N, W, E, S

class App(ttk.Frame):
    def __init__(self, master=None):
        super().__init__(master)
        self.master = master
        self.feet = StringVar()
        self.meters = StringVar()
        self._layout()
        
        for child in self.winfo_children(): 
            child.grid_configure(padx=5, pady=5)
        
    @property
    def widgets(self):
        _widgets = [
            [ttk.Entry(self, width=14, textvariable=self.feet),
             ttk.Label(self, text="feet")],
            [ttk.Label(self, text="is equivalent to"),
             ttk.Label(self, textvariable=self.meters),
             ttk.Label(self, text="meters")],
            [ttk.Button(self, text="Calculate", command=self.calculate)]
        ]
        return _widgets
    
    def grid_layout(self):
        for n_row, row in enumerate(self.widgets):
            for n_col, widget in enumerate(row):
                widget.grid(column=n_col, row=n_row, sticky=(E, W))
    
    def _layout(self):
        # 設(shè)定 master 的 Resize
        self.master.columnconfigure(0, weight=1)
        self.master.rowconfigure(0, weight=1)
        self['padding'] = 3, 3, 12, 12
        self.grid(column=0, row=0, sticky=(N, W, E, S))
        self.grid_layout()
        
    def calculate(self, *args):
        try:
            value = float(self.feet.get())
            self.meters.set((0.3048 * value * 10000.0 + 0.5)/10000.0)
        except ValueError:
            pass

root = Tk()
root.title('Feet to Meters')
app = App(master=root)
app.mainloop()

顯示結(jié)果為:

圖11 英尺轉(zhuǎn)換為米

為了支持 <Enter> (鍵盤(pán)回車(chē))鍵盤(pán)觸發(fā)事件砖瞧,可以這樣:

class App1(App):
    def __init__(self, master=None):
        super().__init__(master)
        self.widgets[0][0].focus()
        self.master.bind('<Return>', self.calculate)
# 鍵盤(pán)觸發(fā)事件 
root = Tk()
root.title('Feet to Meters')
app = App1(master=root)
app.mainloop()

如果想要為 ttk.Frame 添加統(tǒng)一的配置息堂,可以這樣:

class App(ttk.Frame):
    def __init__(self):
        super().__init__()
        self.option_add("*Font", "arial 40 bold") # 添加統(tǒng)一的字體設(shè)定
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請(qǐng)通過(guò)簡(jiǎn)信或評(píng)論聯(lián)系作者块促。
  • 序言:七十年代末荣堰,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子竭翠,更是在濱河造成了極大的恐慌振坚,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,194評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件斋扰,死亡現(xiàn)場(chǎng)離奇詭異渡八,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)传货,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén)屎鳍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人损离,你說(shuō)我怎么就攤上這事哥艇。” “怎么了僻澎?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,780評(píng)論 0 346
  • 文/不壞的土叔 我叫張陵貌踏,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我窟勃,道長(zhǎng)祖乳,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,388評(píng)論 1 283
  • 正文 為了忘掉前任秉氧,我火速辦了婚禮眷昆,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己亚斋,他們只是感情好作媚,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著帅刊,像睡著了一般纸泡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上赖瞒,一...
    開(kāi)封第一講書(shū)人閱讀 49,764評(píng)論 1 290
  • 那天女揭,我揣著相機(jī)與錄音,去河邊找鬼栏饮。 笑死吧兔,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的袍嬉。 我是一名探鬼主播境蔼,決...
    沈念sama閱讀 38,907評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼冬竟!你這毒婦竟也來(lái)了欧穴?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,679評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤泵殴,失蹤者是張志新(化名)和其女友劉穎涮帘,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體笑诅,經(jīng)...
    沈念sama閱讀 44,122評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡调缨,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了吆你。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片弦叶。...
    茶點(diǎn)故事閱讀 38,605評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖妇多,靈堂內(nèi)的尸體忽然破棺而出伤哺,到底是詐尸還是另有隱情,我是刑警寧澤者祖,帶...
    沈念sama閱讀 34,270評(píng)論 4 329
  • 正文 年R本政府宣布立莉,位于F島的核電站,受9級(jí)特大地震影響七问,放射性物質(zhì)發(fā)生泄漏蜓耻。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評(píng)論 3 312
  • 文/蒙蒙 一械巡、第九天 我趴在偏房一處隱蔽的房頂上張望刹淌。 院中可真熱鬧饶氏,春花似錦、人聲如沸有勾。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,734評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蔼卡。三九已至皮仁,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間菲宴,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,961評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工趋急, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留喝峦,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,297評(píng)論 2 360
  • 正文 我出身青樓呜达,卻偏偏與公主長(zhǎng)得像谣蠢,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子查近,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容