本文将从最基础的 GUI 设计开始,逐步深入 tkinter 的学习。(参考资料: Python GUI Programming Cookbook - Second Edition

tkinter 基础示例#

接下来我们将逐步揭开 tkinter 的面纱。

最简单的窗口#

下面的代码将是 tkinter 的最精简的代码,但它也完成了最简单的 GUI 窗口设计。

import tkinter as tk

# 创建一个窗口实例
win = tk.Tk()   

# 为窗口添加标题  
win.title("Python GUI")

#======================
# 启动 GUI
#======================
win.mainloop()

显示的效果是:

图1 最简单的 GUI

从图1 可以看出该窗口可以最小化、最大化、关闭,也可以水平或者竖直拉长窗口。

为了限制用户对窗口的尺寸进行改变,可以这样:

import tkinter as tk

# 创建一个窗口实例
win = tk.Tk()   

# 为窗口添加标题  
win.title("Python GUI")
# 限制 resizing 窗口
win.resizable(False, False) 
#======================
# 启动 GUI
#======================
win.mainloop()

效果图:

图2 被限制改变尺寸的窗口

从图2 可以看出最大化功能被禁用了,同时用户也无法水平或者竖直拉长窗口。当然,您可以设置 win.resizable(True, False) 让用户可以水平拉长窗口,而无法竖直拉长窗口。

添加 ttk.Label#

为了让的窗口更加有意思,需要添加 Label 用以显示一些信息给用户:

from tkinter import ttk, Tk

# 创建一个窗口实例
win = Tk()   

# 为窗口添加标题  
win.title("Python GUI")
# 添加 Label
ttk.Label(win, text="A Label").grid(column=0, row=0) 
#======================
# 启动 GUI
#======================
win.mainloop()

ttk 中存放了许多 tkinter 的主题化的界面外观(look and feel)让 GUI 看起来更符合您的操作系统(比如 Windows 风格的界面)。

效果图:

图3 为窗口添加 Label

改变 Label 属性的按钮:ttk.Button#

下面的代码可以通过按钮改变 Label 的前景颜色:

from tkinter import ttk, Tk

# 创建一个窗口实例
win = Tk()

# 为窗口添加标题
win.title("Python GUI")

# 添加 Label
a_label = ttk.Label(win, text="A Label")
a_label.grid(column=0, row=0)

# 按钮触发事件的函数
def click_me():
    action.configure(text="**已经点击了按钮**")
    a_label.configure(foreground='red')
    a_label['text'] = 'A Red Label'

# 添加一个按钮
action = ttk.Button(win, text="点我呀!", command=click_me)
action.grid(column=1, row=0)
# ======================
# 启动 GUI
# ======================
win.mainloop()

ttk 下的主题化的小部件均支持两种改变属性的方法:

  1. 使用 configure 方法,比如 action.configure(text="**已经点击了按钮**")

  2. 使用赋值的方式,比如:a_label['text'] = 'A Red Label'

效果图:

图4 改变 Label 属性的按钮

ttk.Button 的参数 command 可以用来绑定一些事件或者行为,比如本例的改变 Label 的属性之类的行为。

一行文本框:ttk.Entry#

先看代码:

from tkinter import ttk, Tk, StringVar

# 创建一个窗口实例
win = Tk()

# 为窗口添加标题
win.title("Python GUI")

# 添加 Label
a_label = ttk.Label(win, text="A Label")
a_label.grid(column=0, row=0)

# 按钮触发事件的函数
def click_me():
    action.configure(text='您好 ' + name.get())

# 覆盖 a_label
ttk.Label(win, text="键入您的名字:").grid(column=0, row=0)

# 加入一行文本框 Entry widget
name = StringVar()
name_entered = ttk.Entry(win, width=12, textvariable=name)
name_entered.grid(column=0, row=1)

# 添加一个按钮
action = ttk.Button(win, text="点我呀!", command=click_me)
action.grid(column=1, row=1)
# ======================
# 启动 GUI
# ======================
win.mainloop()

代码演示了 grid 如果设定在同一个位置,会覆盖前面的小部件;添加了字符串变量 StringVar 用于追踪小部件的字符串信息。小部件 ttk.Entry 提供用户输入一行文档字符串信息。

效果图:

图5 提供用户输入一行的文本框

注意:您可以将上述的代码的最后的设定部分添加 name_entered.focus()(鼠标光标)和 action['state'] = 'disabled'(禁用按钮):

action['state'] = 'disabled'
name_entered.focus()      # 进入 Entry 后,放置鼠标的光标
# 启动 GUI
win.mainloop()

效果图:

图6 禁用按钮并显示鼠标光标

组合框:ttk.Combobox#

先看代码:

from tkinter import ttk, Tk, StringVar

# 创建一个窗口实例
win = Tk()

# 为窗口添加标题
win.title("Python GUI")


# 按钮触发事件的函数
def click_me():
    action.configure(text=f'您好 {name.get()}, 选择了{number_chosen.get()}')


# 覆盖 a_label
ttk.Label(win, text="键入您的名字:").grid(column=0, row=0)

# 加入一行文本框 Entry widget
name = StringVar()
name_entered = ttk.Entry(win, width=12, textvariable=name)
name_entered.grid(column=0, row=1)

# 添加一个按钮
action = ttk.Button(win, text="点我呀!", command=click_me)
action.grid(column=2, row=1)

ttk.Label(win, text="选择一个数字:").grid(column=1, row=0)
number = tk.StringVar()
number_chosen = ttk.Combobox(
    win, width=12, textvariable=number, state='readonly')
number_chosen['values'] = (1, 2, 4, 42, 100)
number_chosen.grid(column=1, row=1)
number_chosen.current(0)  # 设置预选列表的第一个数字为初始值

name_entered.focus()      # 进入 Entry 后,放置鼠标的光标
# 启动 GUI
win.mainloop()

组合框通过 number_chosen['values'] = (1, 2, 4, 42, 100) 通过用户可选择的数字;number_chosen.current(0) 设定了初始值。

效果图:

图7 组合框

上面的代码 number_chosen = ttk.Combobox( win, width=12, textvariable=number, state='readonly') 设定用户无法改变列表中的数字,用户为了让可以自定义数字,只需要将 state 删除即可。

复选按钮:ttk.Checkbutton#

先看代码:

from tkinter import ttk, Tk, IntVar

# 创建一个窗口实例
win = Tk()

# 为窗口添加标题
win.title("Python GUI")
# 设定被勾选的而用户无法修改的选项
chVarDis = IntVar()
check1 = ttk.Checkbutton(win, text="Disabled", variable=chVarDis, state='disabled')
check1.grid(column=0, row=4, sticky='w') 
chVarDis.set(1) # 打勾
# 设定没有被勾选的而用户可以修改的选项
chVarUn = IntVar()
check2 = ttk.Checkbutton(win, text="UnChecked", variable=chVarUn)
check2.grid(column=1, row=4, sticky='w')   
# 设定被勾选的且用户可以修改的选项
chVarEn = IntVar()
check3 = ttk.Checkbutton(win, text="Enabled", variable=chVarEn)
check3.grid(column=2, row=4, sticky='w')  
chVarEn.set(1) # # 打勾

# 启动 GUI
win.mainloop()

此代码的含义很简单,直接看效果图:

图8 复选框

可以设定仅仅有一个复选框是有效的:

...
def checkCallback(*ignoredArgs):
    # 仅仅保留一个 checkbutton
    if chVarUn.get():
        check3.configure(state='disabled')
    else:
        check3.configure(state='normal')
    if chVarEn.get():
        check2.configure(state='disabled')
    else:
        check2.configure(state='normal')

# trace 这两个 checkbuttons
chVarUn.trace_add('write', lambda unused0, unused1, unused2: checkCallback())
chVarEn.trace_add('write', lambda unused0, unused1, unused2: checkCallback())

# 启动 GUI
win.mainloop()

代码中的 trace_add 用于跟踪变量,并绑定 checkCallback 函数进行回调。达到“仅仅保留一个 checkbutton”的效果。

效果:

图9  用于跟踪变量

单选按钮:ttk.Radiobutton#

设定可以改变背景的单选按钮:

from tkinter import ttk, Tk, IntVar

# 创建一个窗口实例
win = Tk()

# 为窗口添加标题
win.title("Python GUI")
# Radiobutton 全局变量
COLOR1 = "Blue"
COLOR2 = "Gold"
COLOR3 = "Red"

# Radiobutton Callback
def radCall():
    radSel = radVar.get()
    if radSel == 1:
        win.configure(background=COLOR1)
    elif radSel == 2:
        win.configure(background=COLOR2)
    elif radSel == 3:
        win.configure(background=COLOR3)

# 创建单选项
radVar = IntVar()
rad1 = ttk.Radiobutton(win, text=COLOR1, variable=radVar,
                       value=1, command=radCall)
rad1.grid(column=0, row=0, sticky='w', columnspan=3)
rad2 = ttk.Radiobutton(win, text=COLOR2, variable=radVar,
                       value=2, command=radCall)
rad2.grid(column=0, row=1, sticky='w', columnspan=3)
rad3 = ttk.Radiobutton(win, text=COLOR3, variable=radVar,
                       value=3, command=radCall)
rad3.grid(column=0, row=2, sticky='w', columnspan=3)

# 启动 GUI
win.mainloop()

效果图:

图10 可以改变背景的单选按钮

更多的颜色设定参考 tcl 手册:http://www.tcl.tk/man/tcl8.5/TkCmd/colors.htm

可滚动的文本框: ScrolledText#

滚动文本小部件比简单的"条目"(ttk.Entry)小部件大得多,并且支持跨越多行。有点类似于记事本,当文本大于 ScrollEdText 小部件的高度时,会自动启用垂直滚动条。

from tkinter import ttk, Tk, IntVar
from tkinter import scrolledtext

# 创建一个窗口实例
win = Tk()

# 为窗口添加标题
win.title("Python GUI")
# Using a scrolled Text control    
scrol_w  = 30
scrol_h  =  3
scr = scrolledtext.ScrolledText(win, width=scrol_w, height=scrol_h, wrap='word')
scr.grid(column=0, columnspan=3)

# 启动 GUI
win.mainloop()

效果:

图11 可滚动的文本框