图形用户界面(GUI、Graphical User Interface)是基于图形的界面,windows就是一个图形用户界面的操作系统,而DOS是基于字符命令交互的操作系统。图形用户界面由窗口构成,每个窗口都由标题、菜单、控制按钮、滚动条等元素组成。
Tkinter包【注1】本质上是对 Tcl/Tk 软件包的 Python 接口封装,Tcl/Tk 不是只有单个库(library),而是由几个不同的模块(modules)组成的,每个模块都有各自的功能和各自的官方文档。Tkinter属于 Python 自带的标准库模块,当您安装好 Python 后,就可以直接使用它,而无须另行安装。
tkinter —— Tcl/Tk 的 Python 接口 — Python 3.10.8 文档
【Tk图形用户界面(GUI) — Python 3.10.8 文档
TkDocs Tutorial】
【注1:包(package)、模块(Module)和库(library)
在Python中,简单地说,模块(Module)一般是单个python文件;包(package)由分层模块(Module)构成——相关目录里的模块构成。Python中的库(library)是参考其它编程语言的说法,既可以是一个模块也可以是一个包,换言之是对模块或包的通俗的说法。】
Tkinter的提供各种控件【注2】,如按钮,标签和文本框,一个GUI应用程序中使用。这些控件通常被称为控件或者部件。
【注2:tkinte文档中使用的widgets,在许多不同的翻译:组件、小部件、控件,相当于某些开发语言的Components(组件)、controls(控件)】
对 Tkinter 的支持分布在多个模块中。 大多数应用程序将需要主模块 tkinter(也称为tk)和 tkinter.ttk 模块,后者是tkinter的子模块ttk,是对前者的加强(从tkinter 8.5开始,tkinter.ttk模块可用)。ttk组件多于tkinter,界面也相对漂亮,所以使用时尽量选择ttk。按照以下方式导入,ttk中的widget(组件、小部件、控件)会默认替换掉tk的。
from tkinter import *
from tkinter.ttk import *
二者都有的widget,ttk将会覆盖Tkinter;ttk有而Tkinter没有的,将采用ttk的特性。
ttk 中有 18 种widget ,其中 12 种在 tkinter 中已包含了: Button 、 Checkbutton 、Entry 、 Frame 、 Label, LabelFrame 、 Menubutton 、PanedWindow 、Radiobutton 、 Scale 、 Scrollbar 和 Spinbox。另有 6 种是新增的: Combobox 、 Notebook 、 Progressbar 、 Separator 、 Sizegrip 和 Treeview。这些控件全都是 Widget 的子类。
tkinter有的,ttk也有;tkinter没有的,ttk也有。特别提示,两者的widget在样式方面的兼容性不是很好,例如:
tkinter 中的fg,bg 在ttk中并不被支持,ttk是通过style这个对象来实现的。
如下:
tkinter:
l1 = Tkinter.Label(text="Test", fg="black", bg="white")
l2 = Tkinter.Label(text="Test", fg="black", bg="white")
ttk:
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")
tkinter.ttk 的基本设计思路,就是尽可能地把控件的行为代码与实现其外观的代码分离开来。tkinter.ttk --- Tk 风格的控件 — Python 3.11.0 文档
可以使用tkinter.Tk() 方法生成窗口(窗体),例如
import tkinter
w=tkinter.Tk()
或
import tkinter as tk
w=tk.Tk()
或
from tkinter import *
w= Tk()
特别提示:注意大小写,否则出错。
先看一简单示例源码:
#tkinter简单示例源码
import tkinter as tk
#调用Tk()创建主窗口
root_window =tk.Tk()
#设置窗口大小和位置:窗口的宽与高,窗口距离屏幕的左边距和上边距
root_window.geometry('600x400+300+200')
#给窗口起一个名字,也就是窗口的名字
root_window.title("一个tk页面")
#开启主循环,让窗口处于显示状态
root_window.mainloop()
运行效果示意:
如何设置窗体居中显示呢?
将上面代码的如下语句
#设置窗口大小和位置:窗口的宽与高,窗口距离屏幕的左边距和上边距
root_window.geometry('600x400+300+200')
改为:
#设置窗体居中显示
SW = root_window.winfo_screenwidth()
SH = root_window.winfo_screenheight()
DW = 600
DH = 400
root_window.geometry("%dx%d+%d+%d" % (DW, DH, (SW - DW) / 2, (SH - DH) / 2))
即可。先获取平路的宽度以及高度,再根据你自定义的窗体宽高来计算居中。获取电脑屏幕的大小,winfo_screenwidth()方法【注3】获取电脑屏幕的宽度,winfo_screenheight()方法获取电脑屏幕的高度。
【注3:函数(function)与方法(method)
本质都是对一段功能代码。
与类和实例无绑定关系的function属于函数(function);
与类和实例有绑定关系的function属于方法(method)。
从它们本质都是对一段功能的抽象来看没啥区别,只不过在类里函数就叫方法,即方法是属于对象的函数,是函数的一种类型。许多资料有时将方法也称为函数。】
窗口常用方法
下面列出了窗口的常用方法,其中 window 代表主窗口对象:
window.title("my title") 接受一个字符串参数,为窗口起一个标题
window.resizable() 是否允许用户拉伸主窗口大小,默认为可更改,当设置为 resizable(0,0)或者resizable(False,False)时不可更改
window.geometry() 设定主窗口的大小以及位置,当参数值为 None 时表示获取窗口的大小和位置信息。
window.quit() 关闭当前窗口,注意,在IDLE中运行无效,双击py文件 或 右击py文件使用“打开方式 → Python”快捷菜单命令有效,若想在IDLE中窗口运行有效,可改用window.destroy()。
window.update() 刷新当前窗口。
window.mainloop() 设置窗口主循环,使窗口循环显示(一直显示,直到窗口被关闭)。【tkinter窗口mainloop()方法的作用,该方法侦听窗口中各种事件,例如鼠标、键盘等操作,并阻止在其后面的代码运行(写在mainloop()后面的代码是不会被执行的),直到窗口关闭】
window.iconbitmap() 设置窗口左上角的图标(图标是.ico文件类型)。
window.config(background ="red") 设置窗口的背景色为红色,也可以接受 16 进制的颜色值。
window.minsize(50,50) 设置窗口被允许调整的最小范围,即宽和高各50。
window.maxsize(400,400) 设置窗口被允许调整的最大范围,即宽和高各400。
window.attributes("-alpha",0.5) 用来设置窗口的一些属性,比如透明度(-alpha)、是否置顶(-topmost)即将主屏置于其他窗口之上、是否全屏(-fullscreen)全屏显示等。
window.state("normal") 用来设置窗口的显示状态,参数值 normal(正常显示),icon(最小化),zoomed(最大化)。
window.withdraw() 用来隐藏主窗口,但不会销毁窗口。
window.iconify() 设置窗口最小化
window.deiconify() 将窗口从隐藏状态还原
window.winfo_screenwidth() 和window.winfo_screenheight() 获取电脑屏幕的分辨率(尺寸)
window.winfo_width() 和window.winfo_height() 获取窗口的大小,同样也适用于其他控件,但是使用前需要使用 window.update()刷新屏幕,否则返回值为1。
window.protocol("协议名",回调函数) 启用协议处理机制,常用协议有 WN_DELETE_WINDOW,当用户点击关闭窗口时,窗口不会关闭,而是触发回调函数。
Tkinter的常用控件
控件类型 控件名称 控件作用
Button 按钮 点击按钮时触发/执行一些事件(函数)
Checkbutton 复选框 多项选择按钮,用于在程序中提供多项选择框
Entry 文本框输入框 用于接收单行文本输入
Frame 框架(容器)控件 定义一个窗体(根窗口也是一个窗体),用于承载其他控件,即作为其他控件的容器
Lable 标签控件 用于显示单行文本或者图片
LableFrame 容器控件 一个简单的容器控件,常用于复杂的窗口布局。
Listbox 列表框控件 以列表的形式显示文本
Menubutton 菜单按钮控件 用于显示菜单项
Message 信息控件 用于显示多行不可编辑的文本,与 Label控件类似,增加了自动分行的功能
messageBox 消息框控件 定义与用户交互的消息对话框
OptionMenu 选项菜单 下拉菜单
PanedWindow 窗口布局管理组件 为组件提供一个框架,允许用户自己划分窗口空间
Radiobutton 单选框 单项选择按钮,只允许从多个选项中选择一项
Scale 进度条控件 定义一个线性“滑块”用来控制范围,可以设定起始值和结束值,并显示当前位置的精确值
Spinbox 高级输入框 Entry 控件的升级版,可以通过该组件的上、下箭头选择不同的值
Scrollbar 滚动条 默认垂直方向,鼠标拖动改变数值,可以和 Text、Listbox、Canvas等控件配合使用
Text 多行文本框 接收或输出多行文本内容
控件基本属性
属性 说明
Anchor 定义控件或者文字信息在窗口内的位置
Bg bg是 background 的缩写,用来定义控件的背景颜色,参数值可以颜色的十六进制数,或者颜色英文单词
Bitmap 定义显示在控件内的位图文件
Borderwidth 定于控件的边框宽度,单位是像素
Command 该参数用于控件执行事件函数,如果使得command=函数,那么点击控件的时候将会触发函数。如按钮(Button)的的构造方法内就有command参数,当按钮事件发生(点击按钮),就可以通过command=函数,进行事件处理。这个函数就是事件处理程序。
Cursor 当鼠标指针移动到控件上时,定义鼠标指针的类型,字符换格式,参数值有 crosshair(十字光标)watch(待加载圆圈)plus(加号)arrow(箭头)等
font 若控件支持设置标题文字,就可以使用此属性来定义,它是一个数组格式的参数 (字体,大小,字体样式)
fg fg是 foreground 的缩写,用来定义控件的前景色,也就是字体的颜色
height 该参数值用来设置控件的高度,文本控件以字符的数目为高度(px),其他控件则以像素为单位
image 定义显示在控件内的图片文件
justify 定义多行文字的排列方式,此属性可以是 LEFT/CENTER/RIGHT
padx/pady 定义控件内的文字或者图片与控件边框之间的水平/垂直距离
relief 定义控件的边框样式,参数值为FLAT(平的)/RAISED(凸起的)/SUNKEN(凹陷的)/GROOVE(沟槽桩边缘)/RIDGE(脊状边缘)
text 定义控件的标题文字
state 控制控件是否处于可用状态,参数值默认为 NORMAL/DISABLED,默认为 NORMAL(正常的)
width 用于设置控件的宽度,使用方法与 height 相同
这些控件中怎么放置到Tkinter程序窗口中呢,这就涉及到Tkinter 的几何(geometry)管理对象。
几何管理对象
Tkinter 提供三种几何(geometry)管理对象[也叫布局(layout),它提供3种方法:pack、grid 和 place管理同在一个父组件下的所有组件的布局(摆放)。
☆ pack 是按添加顺序排列组件,适用于少量组件的排列,使用较为简单。在不使用任何参数的情况下,它会将控件以添加时的先后顺序,自上而下,一行一行的进行排列,并且默认居中显示。
☆ grid 是按行/列形式排列组件,grid 把组件(控件)位置作为一个二维表结构来维护, 表格内的每个单元格都可以放置一个控件,即按照行列的方式排列组件,组件位置由其所在的行号和列号决定。 行号相同而列号不同的几个组件会被彼此上下排列; 列号相同而行号不同的几个组件会被彼此左右排列。使用 Grid 布局的过程就是为各个组件指定行号和列号的过程. 不需要为每个格子指定大小, Grid 布局会自动设置一个合适的大小。
☆ place 可以指定组件的绝对位置或相对于其他组件的位置,可以指定组件的大小,可以直接根据与父窗口的关系(位置)直接放到想要的位置,可以让组件覆盖到另一个组件上。
pack方法的常用参数如下所示:
参数 说明
anchor 组件在窗口中的对齐方式,有 9 个方位参数值,比如"n"/"w"/"s"/"e"/"ne",以及 "center" 等(这里的 e w s n分别代表,东西南北)
expand 是否可扩展窗口,参数值为 True(扩展)或者 False(不扩展),默认为 False,若设置为 True,则控件的位置始终位于窗口的中央位置
fill 参数值为 X/Y/BOTH/NONE,表示允许控件在水平/垂直/同时在两个方向上进行拉伸,比如当 fill = X 时,控件会占满水平方向上的所有剩余的空间。
ipadx,ipady 需要与 fill 参数值共同使用,表示组件与内容和组件边框的距离(内边距),比如文本内容和组件边框的距离,单位为像素(p),或者厘米(c)、英寸(i)
padx,pady 用于控制组件之间的上下、左右的距离(外边距),单位为像素(p),或者厘米(c)、英寸(i)
side 组件放置在窗口的哪个位置上,参数值 'top','bottom','left','right'。注意,单词小写时需要使用字符串格式,若为大写单词则不必使用字符串格式
grid()方法的常用参数如下所示:
参数 说明
column 控件位于表格中的第几列,窗体最左边的为起始列,默认为第 0 列
columnsapn 控件实例所跨的列数,默认为 1 列,通过该参数可以合并一行中多个领近单元格。
ipadx,ipady 用于控制内边距,在单元格内部,左右、上下方向上填充指定大小的空间。
padx,pady 用于控制外边距,在单元格外部,左右、上下方向上填充指定大小的空间。
row 控件位于表格中的第几行,窗体最上面为起始行,默认为第 0 行。
rowspan 控件实例所跨的行数,默认为 1 行,通过该参数可以合并一列中多个领近单元格。
sticky 该属性用来设置控件位于单元格那个方位上,参数值和 anchor 相同,若不设置该参数则控件在单元格内居中。
Place()方法的常用参数如下所示:
参数 说明
anchor 定义控件在窗体内的方位,参数值N/NE/E/SE/S/SW/W/NW 或 CENTER,默认值是 NW
bordermode 定义控件的坐标是否要考虑边界的宽度,参数值为 OUTSIDE(排除边界) 或 INSIDE(包含边界),默认值 INSIDE。
x、y 定义控件在根窗体中水平和垂直方向上的起始绝对位置。
relx、rely 1. 定义控件相对于根窗口(或其他控件)在水平和垂直方向上的相对位置(即位移比例),取值范围再 0.0~1.0 之间。
2. 可设置 in_ 参数项,相对于某个其他控件的位置。
height、width 控件自身的高度和宽度(单位为像素)。
relheight、relwidth 控件高度和宽度相对于根窗体高度和宽度的比例,取值也在 0.0~1.0 之间。
其中,relx和rely参数指定的是控件相对于父组件的位置,而relwidth和relheight参数则是指定控件相对于父组件的尺寸大小。
现在,给出一个简单的窗体示例,更改窗体默认图标,并带关闭按钮。
请准备一个.ico文件类型的图标文件,文件名为 TestUse.ico
我这里为方便使用,将这个文件和下面的程序文件放在一个文件夹中。
程序代码如下:
#带关闭按钮的窗体示例
import tkinter as tk
#调用Tk()创建主窗口
root=tk.Tk()
#设置窗口大小和位置:窗口的宽与高,窗口距离屏幕的左边距和上边距
root.geometry('350x250+300+200')
#给窗口起一个名字,也就是窗口的名字
root.title("一个tk窗体")
#更改窗口左上角的图标,图标应是.ico文件类型
root.iconbitmap("TestUse.ico")
# 添加按钮,以及按钮的文本,并通过command 参数设置关闭窗口的功能
button=tk.Button(root,text="关闭",command=root.destroy)
# 将按钮放置在窗口的位置
button.pack(side="bottom",anchor="e")
#开启主循环,让窗口处于显示状态
root.mainloop()
程序运行效果:
对Tkinter的控件不做过多的介绍【详见
Tkinter 8.5 reference: a GUI for Python
TkDocs Tutorial 】
现在简要介绍Message 控件(widget)
Message 控件用于显示简单的文本消息。Message(消息)控件是 Label控件的变体,用于显示多行文本消息。
Message 控件使用语法:
tk.Message(parent, option, ...)
这个构造函数(constructor)返回新的消息控件。这样,就将Message控件添加到parent中——Message控件作为parent(父)参数的子项。其中,parent 参数代表父控件【如窗口或框架(frame)作为容器,可包含子控件】,Option(选项)参数用于设置控件的属性,控件有一些共有的属性也有一些专有属性。
下面给出使用Message控件示例源码
import tkinter as tk
root = tk.Tk()
#设置窗口大小和位置:窗口的宽与高,窗口距离屏幕的左边距和上边距
root.geometry('350x250+300+200')
#给窗口起一个名字,也就是窗口的名字
root.title("使用Message控件示例")
w1 = tk.Message(root, text="这是一则消息", width=100)
w1.pack()
w2 = tk.Message(root, text="哈哈,这是一则很长很长很长很长很长很长的长消息!", width=100)
w2.pack()
root.mainloop()
程序运行效果:
控件构造函数【注】中command参数的介绍
【注:构造函数(constructor),是一种特殊的方法。主要用来在创建对象时初始化对象】
如果使用command=函数,那么点击控件的时候将会触发函数执行。下面对此进一步介绍相关事项。
Widget(控件)的 command 参数使用示例源码
import tkinter
def show():
label.config(text="Click")
def oneVar():
if varOne.get():
label.config(text="One!!!")
else:
label.config(text="One")
root = tkinter.Tk()
root.title ("Widget(控件)的 command 参数使用示例1")
root.geometry("450x200")
label = tkinter.Label(root, bg="lightgreen", fg="red", height=2, width=12, font="Times 16 bold")
label.pack()
btu = tkinter.Button(root, text="Click", command=show)
btu.pack(anchor=tkinter.W)
varOne = tkinter.BooleanVar()
cbnOne = tkinter.Checkbutton(root, text="One", variable=varOne, command=oneVar)
cbnOne.pack(anchor=tkinter.W)
root.mainloop()
程序运行效果:
你可以点击“Clik”按钮和“One” 复选框试试看。
当command=函数调用函数时,有些情况下需要传递参数,但不能简单地这样command=action(args)传递参数,而要用如下解决方案,这点需要新手注意。
1)通过 lambda 函数传递参数,lambda 函数的语法如下:
lambda: argument_list: expression
下面,给出示例源码:
import tkinter
root=tkinter.Tk()
root.title("Widget(控件)的 command 参数使用示例2")
root.geometry('450x150+300+200')
def func1(args):
print(args)
def func2(args):
print(args)
btn1=tkinter.Button(root,text="One",command=func1("hello"))#传输参数:点击无输出
btn1.pack()
btn2=tkinter.Button(root,text="Two",command=lambda:func2("running"))#传输参数:点击有输出
btn2.pack()
root.mainloop()
程序运行效果:
点击“One”按钮无反应(无输出),点击“Two”按钮有反应(输出running)
2)通过使用 functools标准库(模块)中的 partial 对象来传递参数。
【functools --- 高阶函数和可调用对象上的操作 — Python 3.10.8 文档
下面,给出示例源码:
import tkinter
from functools import partial
root=tkinter.Tk()
root.title("Widget(控件)的 command 参数使用示例2b")
root.geometry('450x150+300+200')
def func(args):
print(args)
btn1=tkinter.Button(root,text="传输参数",command=partial(func,"running"))#传输参数
btn1.pack()
root.mainloop()
程序运行效果:
点击“传输参数”按钮有反应(输出running)
关于事件后面还将专题介绍,见Python 的Tkinter包系列之五:事件_软件开发技术爱好者的博客-CSDN博客。
现在,下面给出一个实用点的示例——BMI(Body mass index)计算器
# BMI(Body mass index)计算器
import tkinter as tk
from tkinter import *
# 调用Tk()创建主窗口
root_window = tk.Tk()
# 给主窗口起一个名字,也就是窗口的名字
root_window.title('BMI(Body mass index)计算器')
# 设置窗口大小 450x300
root_window.geometry('400x300')
# root_window.geometry('400x300')下面添加...
# 设置完窗口后,添加组件
tk.Label(root_window, text="请输入身高cm",height=3).grid(row=0,column=0)
cms = StringVar()
tk.Entry(root_window, width=7, textvariable=cms).grid(row=0,column=1)
tk.Label(root_window, text="cm").grid(row=0,column=2)
tk.Label(root_window, text="请输体重kg",height=3).grid(row=1,column=0)
kg = StringVar()
tk.Entry(root_window, width=7, textvariable=kg).grid(row=1,column=1)
tk.Label(root_window, text="kg").grid(row=1,column=2)
tk.Label(root_window, text="你BMI是").grid(row=2,column=0)
result = StringVar()
tk.Label(root_window, textvariable=result).grid(row=2,column=1)
def calculate(*args):
try:
cm_ = float(cms.get())
kg_ = float(kg.get())
value_ = kg_ / ((cm_/100) **2)
value_ = value_.__round__(3)
result.set(value_)
except ValueError:
pass
B = tk.Button(root_window,width=10, text="计 算", command=calculate)
B.grid(row=3,column=3)
tk.Message(root_window, width=100, text="提示:BMI正常值在20至25之间,超过25为超重,30以上则属肥胖。").grid(row=5,column=3)
# 开启主循环,让窗口处于显示状态
root_window.mainloop()
程序运行效果:
附录:
Python基于tkinter的GUI编程讲座_软件开发技术爱好者的博客-CSDN博客