tkinter 基礎教程

HonyL發表於2021-08-13

介紹

Tkinter 模組(Tk 介面)是 Python 的標準 Tk GUI 工具包的介面 .Tk 和 Tkinter 可以在大多數的 Unix 平臺下使用,同樣可以應用在 Windows 和 Macintosh 系統裡。Tk8.0 的後續版本可以實現本地視窗風格,並良好地執行在絕大多數平臺中。

Tkinter 是 Python 的標準 GUI 庫。Python 使用 Tkinter 可以快速的建立 GUI 應用程式。

由於 Tkinter 是內建到 python 的安裝包中、只要安裝好 Python 之後就能 import Tkinter 庫、而且 IDLE 也是用 Tkinter 編寫而成、對於簡單的圖形介面 Tkinter 還是能應付自如。

注意:Python3.x 版本使用的庫名為 tkinter,即首寫字母 T小寫

模組

匯入方式

import tkinter

驗證模組是否已安裝

終端中執行命令:python -m tkinter ,執行後介面會彈出一個 tk 小視窗,表示安裝成功,而且告訴你 Tcl/Tk 的版本號,通過這個版本號,你就可以參考對應的 Tcl/Tk 文件了。

API 使用

主視窗

建立 GUI 介面,作為根視窗:

# 建立主視窗
root = tk.Tk()
root.title("tk Root")  # 設定視窗名稱
root.geometry('200x200')  # 設定視窗大小

執行視窗

# 執行
root.mainloop()

元件列表介紹

名稱 簡介
Toplevel 頂層 容器類,為其他元件提供單獨容器
Button 按鈕 按鈕元件
Canvas 畫布 提供繪圖功能,包括:直線,矩形,橢圓,多邊形,點陣圖等
Checkbutton 核取方塊 可選的核取方塊
Entry 單行輸入框 可輸入內容
Frame 容器 裝載其它GUI元件
Label 標籤 顯示不可編輯的文字或圖示
LabelFrame 容器 容器元件,類似Frame支援新增標題
Listbox 列表框 列出多個選項,供選擇
Menu 選單 選單元件
Menubutton 選單按鈕 包含選單的按鈕(下拉式,層疊式)
OptionMeny 選單按鈕 Menubutton的子類,可通過按鈕開啟一個選單
Message 訊息框 類似標籤,但可以顯示多行文字
PanedWindow 分割槽視窗 該容器會被劃分成多個區域,每新增一個元件佔一個區域,使用者可以拖動分割線改變各區域大小
Radiobutton 單選鈕 供使用者點邊的單選妞
Scale 滑動條 拖動滑動塊可設定起始值和結束值,可顯示當前位置的精確值
Spinbox 微調選擇器 可通過該元件的向上、向下箭頭選擇不同的值
Scrollbar 滾動條 為元件提供滾動功能
Text 多行文字框 顯示多行文字

Label 標籤

屬性說明:

width, height=寬高 font(name,size)=字型 image=影像(只支援GIF格式)

fg(foreground)=前景色 bg(background)=背景色 justify(left|center|right)=對齊方式

# 設定 label 屬性
L = tk.Label(root,               # 視窗
             text='展示標籤!',   # 描述
             bg='green',         # 背景
             borderwidth=5,   # 邊界寬度
             relief="solid",  # 指定元件 3D 效果
             justify="right",  # 字型對齊方式
             font=('微軟雅黑', 12),  # 字型
             width=15, height=2)  # 寬高
# 放置 label 控制元件在視窗中的位置
L.pack(side='right')    # 上top下bottom 左left右right
# 配置GIF圖片方式
gifpath = r"D:\xxx\www.gif"
photo = tk.PhotoImage(file=gifpath)  # 例項化圖片物件
lab = tk.Label(image=photo)          # 設定圖片標籤
lab.pack()

Button 按鈕

# 定義空字串變數,用於標籤屬性中接收資料
var = tk.StringVar()
# 設定 label 標籤屬性
L = tk.Label(root,               # 視窗
             # text='展示標籤!',   # 描述
             textvariable=var,   # 接收外部傳參
             bg='green',         # 背景
             font=('微軟雅黑', 12),  # 字型
             width=15, height=2)  # 寬高
# 放置 label 位置
L.pack()    # 上下 左left右right

on_func = False  # 定義一個開關
# 按鈕事件
def func():
    global on_func
    if on_func:
        on_func = False
        var.set('')
    else:
        on_func = True
        var.set('要顯示內容')
# 設定 button 按鈕屬性
B = tk.Button(root,  text='按鈕',  width=15, height=2,
              command=func)        # 設定按鈕事件,函式
B.pack()

Options 屬性選項

元件中的屬性,可以通過 Options 設定元件對應的屬性,從而控制元件的各種狀態。比如:寬度、高度、背景色等等。

有三種方式設定 Options 選項,在各種 GUI 元件中用法都一致。

  1. 建立物件時,使用命名引數(也叫關鍵字引數):
fred = Button(width=15, height=2, bg="blue")
  1. 建立物件後,使用字典索引方式:
fred['fg'] = "red"
fred['bg'] = "blue"
  1. 建立物件後,使用 config() 方式:
fred.config(width=15, height=2, bg="blue")
  • 屬性選項列表:
屬性名 簡介
activebackground 指定元件處於啟用狀態時的背景色
activeforeground 指定元件處於啟用狀態時的前景色
disabledforeground 指定元件處於禁用狀態時的前景色
anchor 指定元件內的字元在元件中顯示位置,選項值(N,NE,E,SE,S,SW,W,NW,CENTER)
background 元件正常的背景色(bg)
foreground 元件正常的前景色(fg)
bitmap 指定在元件上顯示該選項指定的點陣圖,點陣圖的顯示方式受anchor、justify影響。如果同時指定bitmap和text,那麼bitmap覆蓋文字;如果同時指定bitmap和image,那麼image覆蓋bitmap。
relief 元件的3D效果,支援: raised(凸起),sunken(凹陷),flat(平坦),ridge(隆起),solid(實心),groove(凹槽)
borderwidth 指定元件正常顯示的3D邊框寬度
cursor 指定游標在元件上的樣式
command 指定元件關聯的命令方法。該方法在元件離開元件時觸發
font 元件上顯示的文字字型
highlightbackground 指定元件在高亮狀態下的背景色
highlightcolor 指定元件在高亮狀態下的前景色
highlightthickness 指定元件在高亮狀態下的周圍方形區域的寬度
state 元件的當前狀態,支援:NORMAL(正常),DISABLE(禁用)
height 元件的高度
width 元件的寬度
wraplength 對於能支援字元換行的元件,該選項指定每行顯示的最大字元數,超過設定自動換行
image 元件中顯示的圖片
justify 元件內部內容對齊方式,支援(小寫):LEFT(左對齊),CENTER(居中),RIGHT(右對齊)
padx 元件內部在水平方向上兩邊的空白
pady 元件內部在垂直方向上兩邊的空白
takefocus 指定元件在鍵盤遍歷(Tab或Shift+Tab)時是否接收焦點,將選項設為1:接收焦點;設為0:不接受焦點
text 元件顯示的文字
textvariable 指定一個變數名,元件負責顯示該變數值轉換得到的字串
underline 指定元件文字的第幾個字元新增下劃線,該選項相當於為元件繫結了快捷鍵
xscrollcommand 通常用於將元件的水平滾動改變與水平滾動條的 set 方法關聯,從而讓元件的水平滾動改變傳遞到水平滾動條
yscrollcommand 通常用於將元件的垂直滾動改變與垂直滾動條的 set 方法關聯,從而讓元件的垂直滾動改變傳遞到垂直滾動條

文字框

Entry 單行文字框

Entry 用來接收一行字串的控制元件,如果使用者輸入的內容長度超過 Entry 控制元件的寬度時,文字會自動換行。

簡單示例:

def login():
    print('名稱:'+entry1.get())  # 接收文字框的內容
labe = tk.Label(root, text="名稱:")
labe.pack()
 
v1 = tk.StringVar() # 設定空字串變數,屬性value='可設定預設值'
# 單行文字框
# show屬性可將輸入的內容轉為指定字元方式顯示,比如輸入密碼
entry1 = tk.Entry(root, textvariable=v1, show='*')
entry1.pack()
v1.set('設定初始值...')  # 方式二:單獨設定預設值
tk.Button(root, text='傳送文字中內容', command=login).pack()  

Text 多行文字框

主要用來顯示多行文字,還可以顯示網頁連結、圖片、HTML、CSS樣式表、新增元件等等。

wrap=換行方式,word:單詞換行

undo,True:開啟撤銷功能, False:不開啟

示例:

''' Text 多行文字框  '''
tk.Label(root, text='文字框:', width=30).pack(anchor='w')
# 建立多行文字
tex = tk.Text(root, width=40, height=12)
tex.pack()
  • insert(index, 'str') 插入屬性

index:位置,可數字表示(行從1起,列從0起),或INSERT(開始)、END(結尾)

str:需要插入的內容

規則:不包頭,包尾

tex.insert('insert', 'ABCD\nEFG') # 在第一行第0列位置插入內容
tex.insert(2.2, '12345')  # 在第二行第3列位置插入內容
'''結果:
ABCD
EF12345G
'''
  • get(insert, end) 獲取內容屬性

insert:起始位置,int型別,用1.0表示(行從1起,列從0起);

end:結尾,str型別或 int型別,用1.0表示(行從1起,列從0起);

tex.get(3.2, 3.7)  # 指定範圍內
tex.get(3.2, 'end')  # 指定起始位置至結尾
  • 在文字框中放入圖片:
gifpath1 = r"D:\xxxx\www.gif"
photo1 = tk.PhotoImage(file=gifpath1)
tex.image_create('end', image=photo1)  # 在結尾放入圖片

文字框屬性:

background, bd, bg, borderwidth, cursor,exportselection, fg, font, foreground, ighlightbackground,

highlightcolor, highlightthickness, insertbackground,insertborderwidth, insertofftime, insertontime,

insertwidth,invalidcommand, invcmd, justify, relief, selectbackground,selectborderwidth,

electforeground, show, state, takefocus,textvariable, validate, validatecommand, vcmd, width,xscrollcommand

選擇框

Radiobutton 單選框

value: 定義單選按鈕返回的內容

''' 單選按鈕 '''
def confirm():
    messagebox.showinfo('測試', '選擇的內容:' + var.get())
var = tk.StringVar()
var.set("J")
tk.Label(root, text='單選: ', width=10).pack(anchor='w')
radio1 = tk.Radiobutton(root, text='Python', value='P', variable=var)
radio2 = tk.Radiobutton(root, text='Java', value='J', variable=var)
radio1.pack(anchor='w');  radio2.pack(anchor='w')
tk.Button(root, text='確認', command=confirm).pack()

Checkbutton 多選框

onvalue=1  # 1 表示預設選狀態,0 表示不選
offvalue=0 # 未選擇的返回值
def check():
    if code.get() == 1 and vide.get() == 1:
        messagebox.showinfo('多選', '選擇:python, java')
    elif vide.get() == 1:
        messagebox.showinfo('多選', '選擇:java')
    elif code.get() == 1:
        messagebox.showinfo('多選', '選擇:python')
    else:
        messagebox.showinfo('多選', '都沒選擇!')
tk.Label(root, text='多選:', width=10).pack(anchor='w')
code = tk.IntVar();  vide = tk.IntVar()   # 接收int型別引數
check1 = tk.Checkbutton(root, text='python', onvalue=1, offvalue=0, variable=code)
check2 = tk.Checkbutton(root, text='Java', onvalue=1, offvalue=0, variable=vide)
check1.pack(anchor='w'); check2.pack(anchor='w')
tk.Button(root, text='多選確認', command=check).pack()

選擇框屬性:

activebackground, activeforeground, anchor,background, bd, bg, bitmap, borderwidth,

command, cursor,disabledforeground, fg, font, foreground, height,highlightbackground,

highlightcolor, highlightthickness, image,indicatoron, justify, offvalue, onvalue, padx, pady,

relief,selectcolor, selectimage, state, takefocus, text, textvariable,underline, variable, width, wraplength

通用訊息彈窗

messagebox 訊息彈框

Messagebox 用於顯示彈出訊息。Messagebox主要提供了7種訊息提示,如:showinfo()、showerror()、showwarning()、askquestion()、askokcancel()、askyesno()、askretyrcancel()。

匯入方式:

from tkinter import messagebox

訊息對話方塊函式列表:

返回值:選擇項

函式 說明
showinfo(title, message, **options) 訊息提示框
showerror(title, message, **options) 錯誤提示框
showwarning(title, message, **options) 警告提示框
askquestion(title, message, **options) 疑問提示框
askokcancel(title, message, **options) 確定/取消
askyesno(title, message, **options) yes/No
askretrycancel(title, message, **options) 重試/取消

簡單示例:

import tkinter as tk
from tkinter import messagebox
root = tk.Tk()
root.title("TK title")
root.geometry("500x1000+100+200")
def msg1():
    messagebox.showinfo('information', 'Hi! You got a prompt.')
    messagebox.showerror('error', 'Something went wrong!')
    messagebox.showwarning('warning', 'accept T&C')
    messagebox.askquestion('Ask Question', 'Do you want to continue?')
    messagebox.askokcancel('Ok Cancel', 'Are You sure?')
    messagebox.askyesno('Yes|No', 'Do you want to proceed?')
    messagebox.askretrycancel('retry', 'Failed! want to try again?')
tk.Button(root, text='Click Me', command=msg1).pack(pady=50)

canvas 畫布

canvas 畫布是一個矩形區域,可以放置圖形、影像、元件等等。

import tkinter as tk

class Application(tk.Frame):
    def __init__(self, master=None):
        # 代表父類的定義,不是父類物件
        super().__init__(master)
        self.master = master
        self.pack()
        self.createWidget()
    def createWidget(self):
        self.canvas = tk.Canvas(self, width=300, height=200, bg='green')
        self.canvas.pack()
        # 畫一條直線
        self.canvas.create_line(10,10,30,20,40,50)
        # 畫一個矩形
        self.canvas.create_rectangle(50,50,100,100)
        # 畫一個圓
        self.canvas.create_oval(50,50,100,100)
        # 加圖片
        path = "D:\wangxin_m\selenium_py36\gui_tkinter\www.gif"
        global photo
        photo = tk.PhotoImage(file=path)
        self.canvas.create_image(110,300, image=photo)
if __name__ == '__main__':
    root = tk.Tk()
    root.title('畫布練習')
    root.geometry('500x200+200+200')
    app = Application(master=root)
    root.mainloop()

佈局管理

一個 GUI 應用程式必然有大量的元件,tkinter提供了佈局管理器幫助我們組織、管理在父元件中子元件的佈局方式。提供了三種管理器:pack、grid、place

grid 表格佈局

採用表格結構組織元件;子元件的位置由行和列的單元格來確定,並且可以跨行和跨列,從而實現複雜的佈局。

grid()方法提供的選項:

選項 說明
column 單元格的列號(從0開始)
columnspan 跨列
row 單元格的行號(從0開始)
rowspan 跨行
ipadx,ipady 設定子元件之間的間隔
padx,pady 並列的元件之間的間隔
sticky 元件緊貼所在單元格的某一角,對應對應東南西北及4個角
import tkinter as tk

class Application(tk.Frame):
    def __init__(self, master=None):
        # 代表父類的定義,不是父類物件
        super().__init__(master)
        self.master = master
        self.pack()
        self.gridWidget()    
    def gridWidget(self):
        self.labY = tk.Label(self, text='使用者名稱')
        self.labP = tk.Label(self, text='iPhone')
        self.enrY = tk.Entry(self)
        self.enrP = tk.Entry(self)
        # 設定控制元件位置
        self.labY.grid(row=0, column=0) # row=0, column=0 表示:第1行第1列位置 
        self.enrY.grid(row=0, column=1)
        self.labP.grid(row=1, column=0)
        self.enrP.grid(row=1, column=1)
        tk.Button(self, text='登入').grid(row=2, column=0, sticky='w')
        tk.Button(self, text='取消').grid(row=2, column=1, sticky='e')
        # 跨行,多行文字框
        self.text = tk.Text(self, width=28, height=3)
        self.text.grid(row=3, column=0, columnspan=2)
if __name__ == '__main__':
    root = tk.Tk()
    root.title('畫布練習')
    app = Application(master=root)
    root.mainloop()

計算器佈局示例:

class Application(tk.Frame):
    def __init__(self, master=None):
        # 代表父類的定義,不是父類物件
        super().__init__(master)
        self.master = master
        self.pack()
        self.jsqWidget()    
    def jsqWidget(self):
        btnText = (("MC", "M+", "M-", "MR"),  ("C", "±", "/", "*"),
            (7,8,9,"-"),(4,5,6,"+"),(1,2,3,"="), (0,  ".")     )
        # 顯示框,第一行
        tk.Entry(self).grid(row=0, column=0, columnspan=4, pady=10)
        # 按鈕控制元件佈局
        for rindx, r in enumerate(btnText):
            for cindx, c in enumerate(r):
                if c == "=":
                    but_d = tk.Button(self, text=c, width=3)
                    but_d.grid(row=rindx+1, column=cindx, rowspan=2, sticky='nsWE')
                elif c == 0:
                    but_0 = tk.Button(self, text=c, width=3)
                    but_0.grid(row=rindx+1, column=cindx, columnspan=2, sticky='nsWE')
                elif c == '.':
                    but_d = tk.Button(self, text=c, width=3)
                    but_d.grid(row=rindx+1, column=cindx+1, sticky='nsWE')
                else:
                    tk.Button(self, text=c, width=3).grid(row=rindx+1, column=cindx, sticky='nsWE')

pack 佈局

按照元件的建立順序將子元件新增到父元件中,按照“垂直”或者“水平”的方向自然排序。預設在父元件中自頂向下垂直新增元件。

pack()方法提供選項:

選項 說明
expand 當值為"YES"時,side選項無效,元件顯示在父元件中心位置,若fill選項為"both",則填充父元件的剩餘空間;'yes'或自然數、'no'或0(預設)
fill 填充x或y方向的空間,x=水平,y=垂直,both=兩邊填充;當屬性side='top'或'bottom'時,填充x方向;當屬性side='left'或'right'時,填充y方向;當expand選項為'yes'時,填充父元件的剩餘空間
ipadx,ipady 設定子元件之間的間隔
padx,pady 與之並列的元件之間的間隔
side 定義停靠父元件的那個位置(left'、'right'、'top'、'bottom')
before 將本元件於所選元件物件之前pack,類似於先建立本元件選定的元件
after 將本元件與選擇元件物件之後pack,類似先建立選定元件後再建立本元件
anchor 對齊方式(n,s,w,e,nw,sw,se,ne,center)
in_ 將本元件所選元件物件的子元件,類似於指定本元件的masster為選定元件

示例:

root = tk.Tk()
root.title('畫布練習')
root.geometry("700x220")
# Frame 畫布區域,用於放置子元件
f1 = tk.Frame(root, bg='blue');  f1.pack()
f2 = tk.Frame(root,  bg='yellow');  f2.pack()
btnText = ["A", "B", "C", "D"]
for t in btnText:
    tk.Button(f1, text=t).pack(side='left', padx="10")
for i in range(1, 10):
    tk.Label(f2, text=i, width=2, height=3, borderwidth=1, relief="raised",
bg="red").pack(side="left", padx=4)
root.mainloop()

place 佈局

通過座標精確控制元件的位置,適用於一些佈局更加靈活的場景。

place()方法選項:

選項 說明 取值範圍
x,y 元件左上角的絕對座標(相對於視窗) 非負整數,x和y選項用於設定偏移(畫素),如果同時設定relx(rely) 和x(y),那麼place將優先計算relx和rely,然後在實現x和y指定的偏移值
relx,rely 元件左上角的座標(相對於父視窗) 相對父元件位置,0為最左邊,0.5為中間,1位最右邊
width,height 元件的寬度和高度 正整數
relwidth,relheight 元件的寬度和高度(相對於父視窗) 與relx,rely類似,但是相對於父元件的尺寸
anchor 對齊方式 n,s,w,e,nw,sw,se,ne,center

示例:

root = tk.Tk()
root.title('畫布練習')
root.geometry("500x300")
# Frame 畫布區域,用於放置子元件
f1 = tk.Frame(root, width=200, height=200, bg='blue');  f1.place(x=20, y=20)
tk.Button(root, text='root位置').place(relx=0.1, relwidth=0.3, relheight=0.2)
tk.Button(f1, text='f1位置').place(relx=0.2, rely=0.5)
root.mainloop()

事件處理

一個 GUI 應用整個生命週期都處在一個訊息迴圈(eventloop)中。它等待事件的發生,並作出相應的處理。

tkinter 提供了用以處理相關事件的機制,處理函式可被繫結給各個空間的各個事件。

widget.bind(event,handler) 如果相關事件發生, handler 函式會觸發,事件物件 event 會傳遞給 handler 函式。

event 代表事件

handler 代表事件要處理的方式(函式)

滑鼠和鍵盤事件

程式碼 說明
<Button-1> 或 <ButtonPress-1>或<1> 滑鼠左鍵按下。1=左鍵,2=中鍵,3=右鍵
<ButtonRelease-1> 滑鼠左鍵釋放。 1=左鍵,2=中鍵,3=右鍵
<B1-Motion> 滑鼠左鍵移動
<Double-Button-1> 雙擊左鍵
<Enter> 滑鼠指標進入某一元件區域
<Leane> 滑鼠指標離開某一元件區域
<MouseWheel> 滾動滑輪
<KeyPress> 響應按下的任意鍵
<KeyPress-a> 按下 a 鍵,a可自定義(a-zA-Z)
<KeyRelease-a> 釋放 a 鍵,a可自定義其它小寫字母
<Alt-KeyPress-a> 同時按下 alt和a ;alt可用Ctrl和shift代替
<Double-KeyPress-a> 快速雙擊a
<Control-V> Ctrl和V鍵同時按下,V可以自定義

event 物件常用屬性

名稱 說明
char 按鍵字元,僅對鍵盤事件有效
Keycode 按鍵編碼,僅對鍵盤事件有效
Keysym 按鍵名稱,僅對鍵盤事件有效。比如按下 a 鍵:char:a、 Keycode:65、Keysym:a
num 滑鼠按鍵,僅對滑鼠事件有效
type 所觸發的事件型別
widget 引起事件的元件
width,height 元件改變後的大小,僅Configure有效
x,y 滑鼠當前位置,相對於父容器
x_root,y_root 滑鼠當前位置,相對於整個螢幕

示例:

import tkinter as tk
# 建立主視窗
root = tk.Tk()
root.title("TK title")
root.geometry("500x500+10+20")
''' 滑鼠和鍵盤事件 '''
C1 = tk.Canvas(root, width=200, height=200, bg='green')
C1.pack()
def mouseT(event):
    print("滑鼠左鍵單擊位置(父容器):{},{}".format(event.x,event.y))
    print("滑鼠左鍵單擊位置(螢幕):{},{}".format(event.x_root,event.y_root))
    print("事件繫結的元件:{}".format(event.widget))
def dragT(event):
    print("滑鼠移動點位置:", event.x, event.y, event.x+1, event.y+1)
    C1.create_oval(event.x, event.y, event.x+10, event.y+10)
def keyboardT(event):
    '''響應按下的任意鍵'''
    print("鍵的keyconde:{}, 鍵的char:{}, 鍵的keysym:{}".format(event.keycode, event.char, event.keysym))
def press_a_T(event):
    print("按下a鍵")
def release_a_T(event):
    print("釋放a鍵")
# 對 C1 畫布範圍內有效
C1.bind("<Button-1>", mouseT)
C1.bind("<B1-Motion>", dragT)
# 對整個GUI介面有效
root.bind("<KeyPress>", keyboardT)
# 只針對小寫字母 a 有效
root.bind("<KeyPress-a>", press_a_T)
root.bind("<KeyRelease-a>", release_a_T)

root.mainloop()  # 執行GUI

多種事件繫結方式

  • 元件物件繫結

    1. 通過 command 屬性繫結(適合不需要獲取event物件)
    Button(root, text='登入', command=login)
    
    1. 通過 bind()方法繫結(合適需要獲取event物件)
    c1 = tkinter.Canvas()
    c1.bind("<Button-1>", drawLine)
    
  • 元件類的繫結

    呼叫物件的 bind_class函式,將該元件型別的所有元件繫結事件:w.bind_class('Widget', 'event', eventhanler)

    w 代表控制元件

    Widget=控制元件,event=事件,eventhanler=呼叫函式

    # 比如給GUI上的所有按鈕繫結事件
    btn01.bind_class("Button", "<Button-1>",func)
    

    示例:

    import tkinter as tk
    root = tk.Tk()
    root.title("TK title")
    def mouseT(event):
        print("bind()方式繫結,可以獲取event物件:{}".format(event.widget))
    def mouseT1(a,b):
        print("command方式繫結,不能獲取event物件:a={},b={}".format(a,b))
    def mouset2(event):
        print("右鍵單擊事件,給所有的按鈕繫結事件:{}".format(event.widget))
    btn1 = tk.Button(root, text="測試bind()事件繫結")
    btn1.pack()
    btn1.bind("<Button-1>", mouseT)   # bind方式繫結
    
    btn2 = tk.Button(root, text="測試command繫結", command=lambda :mouseT1('AA', "BB"))
    btn2.pack()
    # 繫結給所有的按鈕右鍵事件
    btn2.bind_class("Button", "<Button-3>", mouset2)
    
    root.mainloop()  # 執行GUI
    

OptionMenu 下拉選擇項

下拉選項,用來做多選一,選中的項在頂部顯示。

示例:

import tkinter as tk
root = tk.Tk()
root.title("TK title")
strvar = tk.StringVar() 
strvar.set('程式設計')   # 設定預設值
om = tk.OptionMenu(root, strvar, 'java', 'python', 'php')
om['width'] = 10
om.pack()
root.mainloop()  # 執行GUI

Scale 移動滑塊

用於在指定的數值區間,通過滑塊的移動選擇值。

相關屬性:

activebackground, background, bigincrement, bd,bg, borderwidth, command, cursor, digits, fg, font, foreground, from,highlightbackground, highlightcolor, highlightthickness, label,
length, orient, relief, repeatdelay, repeatinterval, resolution,showvalue, sliderlength, liderrelief, state, takefocus, tickinterval, to, troughcolor, variable, width

示例:(預設為垂直方向)

import tkinter as tk
root = tk.Tk()
root.title("TK title")
def test(value):
    print("滑塊的值:", value)
    # 重新設定按鈕字型的大小,滑塊控制
    lab.config(font=('宋體', value))
sca = tk.Scale(root, from_=5, to=50, length=200, tickinterval=5, command=test, orient='horizontal')  # 滑塊方式,horizontal=水平
sca.pack()
lab = tk.Label(root, text='測試滑塊', width=10, height=1, fg='white', bg='black', font=('宋體', 5))
lab.pack()
root.mainloop()  # 執行GUI

顏色選擇框

可以幫助我們設定背景色、前景色、畫筆色、字型顏色等等。

匯入方式:

from tkinter.colorchooser import askcolor

示例:

import tkinter as tk
from tkinter.colorchooser import askcolor
root = tk.Tk()
root.title("TK title")
def test():
    # 建立顏色選擇器,color=預設顏色
    ask = askcolor(color='red', title='顏色標題')
    print("背景色:", ask)  # 返回是 RGB 值
    root.config(bg=ask[1])  # 修改背景顏色
tk.Button(root, text='選擇', command=test).pack()
root.mainloop()  # 執行GUI

檔案對話方塊

可以幫助我們實現視覺化的操作目錄、檔案。最後將檔案、目錄的資訊傳入到程式中。

匯入方式:

from tkinter.filedialog import askopenfilename

常用函式:

函式名 對話方塊 說明
askopenfilename(**options) 檔案對話方塊 返回開啟的檔名
askopenfilenames(**options) 檔案對話方塊 返回開啟的多個檔名列表
askopenfile(**options) 檔案對話方塊 返回開啟檔案物件
askopenfiles(**options) 檔案對話方塊 返回開啟的檔案物件的列表
askdirectory(**options) 目錄對話方塊 返回目錄名
asksaveasfile(**options) 儲存對話方塊 返回儲存的檔案物件
asksaveasfilename(**options) 儲存對話方塊 返回儲存的檔名

options 常用的值:

引數 說明
defaultextension 預設字尾:.xxx 使用者沒有輸入則自動新增
filetypes=[(label1,pattern1),(label2,pattern2)] 檔案顯示過濾器
initaldir 初始目錄
initalfile 初始目錄
parent 父視窗,預設根視窗
title 視窗標題

選擇檔案示例:

import tkinter as tk
from tkinter.filedialog import askopenfilename
root = tk.Tk()
root.title("TK title")
# initialdir=預設開啟目錄路徑,filetypes=檔案過濾
def test():
    ask = askopenfilename(title='上傳檔案', initialdir='d:\\', filetypes=[('PY檔案','.py')])
    show["text"] = ask
    vv.set(ask)  # 將選擇的檔案絕對路徑,輸入到文字框中
show = tk.Button(root, text='選擇檔案', command=test)
show.pack()
vv = tk.StringVar()
e = tk.Entry(root, textvariable=vv, width=50) 
e.pack()
root.mainloop()  # 執行GUI

直接讀取檔案的內容示例:

import tkinter as tk
from tkinter.filedialog import askopenfile
root = tk.Tk()
root.title("TK title")
def test():
    with askopenfile(title='上傳檔案', initialdir="d:\\", filetypes=[('檔案', '.txt')]) as f:
        show.delete(1.0, 'end')  # 刪除文字框中之前的內容
        show.insert('insert', f.read())  # 將讀取到的檔案內容寫入到文字框中
tk.Button(root, text='選擇檔案', command=test).pack()
show = tk.Text(root, width=60, height=20)
show.pack()
root.mainloop()  # 執行GUI

對話彈框

simpledialog(簡單對話方塊)包含如下常用函式:

函式 說明
askfloat(title,prompt,**kw) 輸入並返回浮點數
askinteger(title,prompt,**kw) 輸入並返回整數
askstring(title,prompt,**kw) 輸入並返回字串

title 代表視窗標題;prompt 是提示描述內容;
kw 代表不定長引數,可以為:initialvalue(初始值), minvalue(最小值), maxvalue(最大值)

示例:

import tkinter as tk
from tkinter.simpledialog import askfloat, askinteger,askstring
root = tk.Tk()
root.title("TK title")
def integer_t(): # 其他兩個用法一樣
    integer = askinteger(title='整數', prompt='請輸入整數:', initialvalue=10, minvalue=0, maxvalue=100)
    show.insert('end', integer)
But = tk.Button(root, text='輸入整數', command=integer_t)
But.pack()
show = tk.Text(root, width=60, height=20)
show.pack()
root.mainloop()  # 執行GUI

選單

主選單通常位於 GUI 程式的上方位置。

匯入方式:

from tkinter.filedialog import *

建立主選單步驟:

  1. 建立主選單欄物件:
menubar = tk.Menu(master=root)
  1. 建立選單選項,並新增到主選單欄物件中:
menuFile = tk.Menu(menubar)
menubar.add_cascade(label="檔案(F)", menu=menuFile)
menuEdit = tk.Menu(menubar)
menubar.add_cascade(label="編輯(E)", menu=menuEdit)
  1. 新增子選單項到第2步驟的選單中:
menuFile.add_command(label="新建", accelerator="ctrl+n", command=self.newfile)
menuFile.add_command(label="開啟", accelerator="ctrl+o", command=self.openfile)
menuEdit.add_command(label="查詢", accelerator="ctrl+f", command=self.test)
menuEdit.add_command(label="刪除", accelerator="ctrl+d", command=self.test)
  1. 將選單新增到root視窗:
root["menu"] = menubar

建立選單示例:

# ''' 建立主選單 '''
menubar = tk.Menu(root)
# ''' 建立子選單 '''
menuFile = tk.Menu(menubar)
menuEdit = tk.Menu(menubar)
menuHelp = tk.Menu(menubar)
# '''將子選單加入到主選單欄'''
# label=名稱;menu=對應的選單
menubar.add_cascade(label="檔案(F)", menu=menuFile)
menubar.add_cascade(label="編輯(E)", menu=menuEdit)
menubar.add_cascade(label="幫助(H)", menu=menuHelp)
# '''新增檔案菜中的子選單'''
# label=名稱;accelerator=快捷鍵方式描述;command=選項執行事件
menuFile.add_command(label="新建", accelerator="ctrl+n", command=self.newfile)
menuFile.add_command(label="開啟", accelerator="ctrl+o", command=self.openfile)
menuFile.add_command(label="儲存", accelerator="ctrl+s", command=self.savefile)
menuFile.add_separator()  # 新增分割線
menuFile.add_command(label="退出", accelerator="ctrl+q", command=self.exit)
# '''新增編輯菜中的子選單'''
menuEdit.add_command(label="查詢", accelerator="ctrl+f", command=self.test)
menuEdit.add_command(label="刪除", accelerator="ctrl+d", command=self.test)
# '''新增幫助菜中的子選單'''
menuHelp.add_command(label="幫助", accelerator="ctrl+h", command=self.test)

# ''' 將主選單新增到 root 主視窗 '''
root["menu"] = menubar

建立滑鼠右鍵選單步驟:

  1. 建立選單物件:
contextMenu = tk.Menu(root)
  1. 建立選單選項:
contextMenu.add_command(label="背景顏色", command=openAskcolor)
def openAskcolor():
   color = askcolor(color='red', title="選擇背景顏色", )
   textpad.config(bg=color[1])
  1. 繫結滑鼠右鍵事件:
def createContextMenu(event):
   # 右鍵選單,在滑鼠右鍵時顯示選單
   contextMenu.post(event.x_root, event.y_root)
root.bind("<Button-3>", createContextMenu)

示例:

# 文字編輯區
self.textpad = Text(root, width=60, height=30)
self.textpad.pack()
# 建立滑鼠右鍵上下選單
self.contextMenu = tk.Menu(root)
self.contextMenu.add_command(label="背景顏色", command=self.openAskcolor)

# 右鍵繫結事件
root.bind("<Button-3>", self.createContextMenu)
def openAskcolor(self):
    color = askcolor(color='red', title="選擇背景顏色", )
    self.textpad.config(bg=color[1])  # 修改背景顏色

簡單記事本案例:

import tkinter as tk
from tkinter import filedialog, messagebox
from tkinter.ttk import Scrollbar, Checkbutton, Label, Button
import os

class NotePad(tk.Tk):

    def __init__(self):
        super().__init__()
        self.set_window()
        self.create_menu_bar()
        self.create_body()

    def set_window(self):
        '''設定視窗引數'''
        self.title("記事本")
        max_width, max_height = self.maxsize()  # 螢幕的大小
        align_center = "800x600+%d+%d" % ((max_width-800)/2, (max_height-600)/2)
        self.geometry(align_center)
        self.iconbitmap("www.gif")  # 圖示

    def create_menu_bar(self):
        ''' 建立選單物件 '''
        menu_bar = tk.Menu(self)

        # 新增選單專案  tearoff=去掉預設分隔符
        File_menu = tk.Menu(menu_bar, tearoff=0)
        menu_bar.add_cascade(label="檔案", menu=File_menu)  # 選單名稱
        File_menu.add_command(label="開啟", accelerator="ctrl+o", command=self.open_file)  # 下拉選單名稱
        File_menu.add_separator()
        File_menu.add_command(label="退出", accelerator="Ait+F4", command=self.quit)  # 二級選單

        Editor_menu = tk.Menu(menu_bar, tearoff=0)
        menu_bar.add_cascade(label="編輯", menu=Editor_menu)  # 選單名稱
        Editor_menu.add_command(label="查詢", accelerator="Ctrl+F", command=self.find_text_dialog)

        View_menu = tk.Menu(menu_bar, tearoff=0)
        menu_bar.add_cascade(label="檢視", menu=View_menu)  # 選單名稱
        # 顯示行號
        self.is_show_line = tk.IntVar()
        self.is_show_line.set(1)  # 預設從1開始
        View_menu.add_checkbutton(label="顯示行號", onvalue=0, offvalue=1,
                                  variable=self.is_show_line, command=self.update_line_num)
        # 高亮當前行
        self.is_height_line = tk.IntVar()
        View_menu.add_checkbutton(label="高亮當前行", variable=self.is_height_line, command=self.toggle_row)
        # 主題,下拉選單巢狀選單
        themes_menu = tk.Menu(menu_bar, tearoff=0)
        View_menu.add_cascade(label="主題", menu=themes_menu)  # 選單名稱
        themes_menu.add_command(label="主題1", accelerator="a+b", command="")

        About_menu = tk.Menu(menu_bar, tearoff=0)
        menu_bar.add_cascade(label="關於", menu=About_menu)  # 選單名稱
        About_menu.add_command(label="", command="")

        # 將設定的選單專案加的選單欄中
        self.config(menu=menu_bar)

    def create_body(self):
        ''' 左邊=行號,中間編輯區,右邊=滾動條 '''
        # 行號區域
        self.line_num_bar = tk.Text(self, width=2, padx=1, takefocus=0, border=0,
                                    background="#AAADAA", state="disable")
        self.line_num_bar.pack(side="left", fill="y")
        # 文字編輯區
        self.context_text = tk.Text(self, wrap="word", undo=True)
        self.context_text.pack(fill="both", expand='yes')

        # 熱鍵繫結-注意按鍵引數會傳入到指定事件中
        self.context_text.bind("<Control-o>", self.open_file)
        self.context_text.bind("<Any-KeyPress>", self.update_line_num)
        self.context_text.bind('<Button-1>', self.toggle_row)

        # 設定文字輸入區域
        self.context_text.tag_config("active_line", background="#F7E3AD")

        # 垂直滾動條
        scroll_bar = Scrollbar(self.context_text)  # 將滾動條與文字框關聯
        scroll_bar['command'] = self.context_text.yview  # 將滾動條與文字框的Y軸關聯
        self.context_text["yscrollcommand"] = scroll_bar.set  # 將文字框的Y軸滾動交給滾動條處理
        scroll_bar.pack(side="right", fill="y")

    def open_file(self, event=None):
        ''' 開啟檔案   '''
        input_fiel = filedialog.askopenfilename(filetype=[("文字型別", ".txt"), ("所有型別", "*.*")])
        if input_fiel:
            # 不同狀態,修改標題顯示
            self.title("{}**".format(os.path.basename(input_fiel)))
            self.context_text.delete(1.0, "end")
            with open(input_fiel, "r") as f:
                self.context_text.insert(1.0, f.read())
        else:
            messagebox.showerror("錯誤", "開啟檔案失敗!")

    def update_line_num(self, event=None):
        ''' 行號處理 '''
        # self.is_show_line.get  0:代表選中, 1:代表未選中
        if self.is_show_line.get() == 0:
            # 獲取所有的行
            row, col = self.context_text.index("end").split(".")
            # 列舉每行的行號
            line_num_content = "\n".join([str(i) for i in range(1, int(row))])
            self.line_num_bar.config(state="normal")
            self.line_num_bar.delete(1.0, "end")
            self.line_num_bar.insert(1.0, line_num_content)
            self.line_num_bar.config(state="disable")
        else:
            self.line_num_bar.config(state="normal")
            self.line_num_bar.delete(1.0, "end")
            self.line_num_bar.config(state="disable")

    def toggle_row(self, event=None):
        ''' 高亮行 '''
        if self.is_height_line.get():
            self.context_text.tag_remove("active_line", 1.0, "end")
            # 設定高亮
            self.context_text.tag_add("active_line", "insert linestart", "insert lineend+1c")
            # 通過遞迴方式進行處理 或 滑鼠左鍵事件
            # self.context_text.after(200, self.toggle_row)
        else:
            self.context_text.tag_remove("active_line", 1.0, "end")

    def find_text_dialog(self):
        ''' 查詢對話方塊 '''
        # 查詢對話方塊大小設定
        search_dialog = tk.Toplevel(self)
        search_dialog.title("查詢文字")
        max_width, max_height = self.maxsize()
        align_center = "300x80+{}+{}".format(int((max_width-300)/2), int((max_height-80)/2))
        search_dialog.geometry(align_center)
        search_dialog.resizable(False, False)
        Label(search_dialog, text="查詢全部").grid(row=0, column=0, sticky="e")
        search_text = tk.Entry(search_dialog, width=25)
        search_text.grid(row=0, column=1, padx=2, pady=2, sticky="we")
        search_text.focus_set()
        # 忽略大小寫
        ignore_case_value = tk.IntVar()
        Checkbutton(search_dialog, text="忽略大小寫", variable=ignore_case_value).grid(
            row=1, column=1, padx=2, pady=2, sticky="e")
        Button(search_dialog, text="查詢", command=lambda: self.search_result(search_text.get(),
                             ignore_case_value.get(), search_dialog, search_text)).grid(row=0, column=2,
                                                                        sticky='w'+"e", padx=10, pady=1)
        # 關閉
        def colse_search_dialog():
            self.context_text.tag_remove('match', 1.0, 'end')
            search_dialog.destroy()
        search_dialog.protocol('WM_DELETE_WINDOW', colse_search_dialog)
        return "break"

    def search_result(self, text, ignore_case, search_dialog, search_box):
        '''
        :param text:  要查詢的文字內容
        :param ignore_case:  是否忽略大小寫
        :param search_dialog: 查詢的目標窗體
        :param search_box:  查詢結果焦點
        '''
        # 清楚匹配內容
        self.context_text.tag_remove('match', 1.0, 'end')
        matches_found = 0
        if text:
            start_pos = 1.0
            while True:
                start_pos = self.context_text.search(text, start_pos, nocase=ignore_case, stopindex='end')
                if not start_pos:
                    break
                end_pos = "{}+{}c".format(start_pos, len(text))
                self.context_text.tag_add('match', start_pos, end_pos)
                matches_found += 1
                start_pos = end_pos
            self.context_text.tag_config('match', foreground="red", background="yellow")
        search_box.focus_set()
        search_dialog.title("發現了 %d 個匹配項" % matches_found)

if __name__ == '__main__':
    app = NotePad()
    app.mainloop()

相關文章