Python中的GUI编程,也就是用Python创建图形用户界面(GUI)的过程。通过GUI,用户可以与程序进行交互,比如通过按钮、菜单、文本框等控件来操作程序。
Python提供了多个库和框架来实现GUI编程,其中最常用的有Tkinter、wxPython、PyQt和PyGTK等。每个库都有自己的特点和适用场景。
Tkinter:Python的标准GUI库,支持跨平台开发,特别适合初学者和小型程序。
wxPython:功能更强大,整体设计框架类似于MFC(Microsoft Foundation Classes),适合大型应用程序开发。
PyQT:适用于大型GUI程序开发,是Qt工具包标准的Python实现,可以使用Qt Designer快速设计界面。
常见的GUI组件
在Python的GUI编程中,常见的组件有:
Label:用于显示文本或图像,可以设置字体、大小、颜色等属性。
Button:代表按钮组件,用户可以通过点击按钮来触发事件。
Canvas:提供绘图功能,可以绘制直线、矩形、椭圆、多边形等。
复选框(Checkbutton):可供用户勾选的复选框。
单行输入框(Entry):用户可以输入内容的地方。
Frame:容器组件,用于装载其他GUI组件。
菜单(Menu):提供多种选项供用户选择。
菜单按钮(Menubutton):包含菜单的按钮,可以是下拉式或层叠式。
单选钮(Radiobutton):可供用户点边的单选钮。
滑动条(Scale):用户可以通过拖动滑块来设定起始值和结束值。
微调选择器(Spinbox):用户可以通过向上、向下箭头选择不同的值。
滚动条(Scrollbar):用于为组件提供滚动功能。
多行文本框(Text):显示多行文本的地方。
一.tkinter
1.简介
tkinter是Python的标准GUI库。
tkinter的优点是简单易用,与Python结合度好。
tkinter在Python 3.x下默认集成,不需要额外安装。因此,想要使用tkinter进行GUI编程,可直接使用import语句导入tkinter模块。
import tkinter
2.使用
创建一个GUI应用程序需要以下5个主要步骤。
样例1
第一个tkinter实例——创建一个窗口用于输出“Hello world“。
import tkinter #导入tkinter模块
top = tkinter.Tk() #创建一个顶层窗口对象
label = tkinter.Label(top,text='Hello World',fg='red')#添加一个Label控件,用于显示文本
label.pack() #调用Label控件的pack()方法
top.mainloop() #进入主事件循环
程序运行效果:
3.常用控件
在GUI编程中,顶层窗口对象包含组成GUI应用程序的所有小窗口对象,它们可能是文字标签、按钮、列表框等,这些独立的GUI组件称为控件。
3.1窗口
窗口也称为框架(Frame),是屏幕上的一块矩形区域,多用来作为容器布局窗体。
窗口中可包含标签、菜单、按钮等其他控件,其运行之后可移动和缩放。
样例:
创建一个300×200的窗口,其标题为“金企鹅”,运行后该窗口宽不可变,高可变。
import tkinter #导入tkinter库
window = tkinter.Tk() #创建tkinter对象
window.title("金企鹅") #设置标题
window.geometry("300x200") #设置窗口大小,注意是字母x
window.resizable(width=False, height=True) #宽不可变,高可变,默认为True
window.mainloop() #进入主事件循环
程序运行效果:
3.2Label控件
Label控件是用于在界面上输出描述信息的标签,可以显示文本和图像。
样例:
创建一个200×100的窗口,其标题为“金企鹅”,在窗口中创建一个标签,用于显示“金企鹅联合出版中心”,并设置其字体、颜色、宽度和高度。
import tkinter #导入tkinter库
window = tkinter.Tk() #创建tkinter对象
window.title("金企鹅") #设置标题
window.geometry("200x100") #设置窗口大小,注意是字母x
#创建标签,text设置文本,bg设置背景色,fg设置前景色,font设置字体,width设置宽,height设置高
label1 = tkinter.Label(window, text="金企鹅联合出版中心", bg="white", fg="blue", font=("宋体"), width=20, height=3)
label1.pack() #显示Label
window.mainloop() #进入主事件循环
程序运行效果:
3.3Button控件
通过Button控件可以方便地与用户进行交互。Button控件有一个command属性,用于指定一个函数或方法,当用户单击按钮时,tkinter就会自动调用该函数或方法。
样例:
编写程序实现通过按下按钮来执行指定操作(改变标签的内容)。
import tkinter as tk #导入tkinter模块重命名为tk
#定义函数,用于实现改变标签的内容
def btnHelloClicked():
labelHello.config(text = "Hello tkinter!")
top = tk.Tk() #创建tkinter对象
top.geometry("200x150") #设置窗口大小,注意是字母x
top.title("Button Test") #设置窗口标题
#创建原始标签
labelHello = tk.Label(top, text = "Press the button...", height = 5, width = 20, fg = "blue")
labelHello.pack() #显示标签
#创建按钮,显示“Hello”,单击按钮调用btnHelloClicked函数
btn = tk.Button(top, text = "Hello", command = btnHelloClicked)
btn.pack() #显示按钮
top.mainloop() #进入主事件循环
程序运行效果:
3.4Entry控件
Entry控件就是输入框,用来输入单行内容,可以方便地向程序传递用户参数。获取输入框的内容可以使用Entry控件的get()方法。
样例:
编写摄氏度转华氏度的小程序,要求从输入框输入摄氏度的值,单击计算按钮后得到华氏度的值。计算公式:F = 1.8×C+32。
import tkinter as tk #导入并重命名tkinter模块
#定义函数用于读取Entry控件的内容并将计算结果进行输出
def btnHelloClicked():
cd = float(entryCd.get()) #读取Entry控件的内容
labelHello.config(text = "%.2f°C = %.2f°F" %(cd, cd*1.8+32))
top = tk.Tk() #创建tkinter对象
top.title("Entry Test") #设置窗口标题
#创建标签
labelHello = tk.Label(top, text = "摄氏度转华氏度", height = 5, width = 20, fg = "blue")
labelHello.pack() #显示标签
entryCd = tk.Entry(top, text = "0") #创建输入框
entryCd.pack() #显示输入框
#创建按钮
btnCal = tk.Button(top, text = "计算", command = btnHelloClicked)
btnCal.pack() #显示按钮
top.mainloop() #进入主事件循环
程序运行效果:
样例2:
#实现类似网页中的占位符效果(即当用户点击输入框时,默认文本消失)
import tkinter as tk
def on_entry_click(event):
"""当输入框获得焦点时删除默认文本"""
if entryCd.get() == "摄氏度转华氏度":
entryCd.delete(0, tk.END) # 删除所有内容
#delete 方法:用于从指定的起始索引到结束索引之间删除文本
#start_index:表示要删除的文本的起始位置,使用整数表示字符的位置。索引从 0 开始,即第一个字符的索引为 0。
#end_index:表示要删除的文本的结束位置(不包括该位置的字符)。可以使用一个整数或特殊常量 tk.END。
#tk.END 是 tkinter 模块中的一个特殊常量,表示字符串的末尾。它告诉 delete 方法一直删除到输入框中的最后一个字符。
entryCd.config(fg='black') # 改变字体颜色
def on_focusout(event):
"""当输入框失去焦点且为空时恢复默认文本"""
if entryCd.get() == "": #可以试一下删掉的效果
entryCd.insert(0, "摄氏度转华氏度")
entryCd.config(fg='red')
def btnHelloClicked():
try:
cd = float(entryCd.get()) # 读取 Entry 控件的内容
labelHello.config(text="%.2f°C = %.2f°F" % (cd, cd * 1.8 + 32))
except ValueError:
labelHello.config(text="请输入有效的数字")
top = tk.Tk()
top.title("Entry Test")
# 创建标签
labelHello = tk.Label(top, text="摄氏度转华氏度", height=5, width=20, fg="blue")
labelHello.pack()
# 创建输入框,并插入默认文本
entryCd = tk.Entry(top, fg='grey')
entryCd.insert(0, "摄氏度转华氏度")
entryCd.bind('<FocusIn>', on_entry_click)
#<FocusIn>:这是一个事件类型,表示当 Entry 控件获得焦点时触发。
# 所谓“获得焦点”,是指用户通过点击或键盘导航(如 Tab 键)选择了这个输入框,准备在其中输入内容。
entryCd.bind('<FocusOut>', on_focusout)
#<FocusOut>:这也是一个事件类型,表示当 Entry 控件失去焦点时触发。
# 所谓“失去焦点”,是指用户点击了窗口中的其他地方或使用 Tab 键离开了这个输入框。
entryCd.pack()
entryCd2 = tk.Entry(top, fg='grey')
entryCd2.pack()
# 创建按钮
btnCal = tk.Button(top, text="计算", command=btnHelloClicked)
btnCal.pack()
top.mainloop()
3.5Radiobutton和Checkbutton控件
Radiobutton和Checkbutton控件分别用于实现选项的单选和复选功能。
例子:编写程序,实现通过单选框和复选框设置文字样式的功能。
import tkinter as tk #导入tkinter模块重命名为tk
#定义函数用于修改标签中文字的颜色
def colorChecked():
labelHello.config(fg = color.get())
#定义函数用于修改标签中文字的字体
def typeChecked():
textType = typeBlod.get() + typeItalic.get() #两个复选框的值相加
if textType == 1: #单选typeBlod复选框
labelHello.config(font = ("Arial", 12, "bold"))
elif textType == 2: #单选typeItalic复选框
labelHello.config(font = ("Arial", 12, "italic"))
elif textType == 3: #同时选中两个复选框
labelHello.config(font = ("Arial", 12, "bold italic"))
else: #两个都不选
labelHello.config(font = ("Arial", 12))
top = tk.Tk() #创建tkinter对象
top.title("Radio & Check Test") #设置窗口标题
#创建标签
labelHello = tk.Label(top, text = "Check the format of text.", height = 3, font=("Arial", 12))
labelHello.pack() #显示标签
color = tk.StringVar() #获取单选框输入
#创建三个单选框并显示
tk.Radiobutton(top, text = "Red", variable = color, value = "red", command = colorChecked).pack()
tk.Radiobutton(top, text = "Blue", variable = color, value = "blue", command = colorChecked).pack()
tk.Radiobutton(top, text = "Green", variable = color, value = "green", command = colorChecked).pack()
#获取复选框输入
typeBlod = tk.IntVar()
typeItalic = tk.IntVar()
#创建2个复选框
tk.Checkbutton(top, text = "Blod", variable = typeBlod, onvalue = 1, offvalue = 0, command = typeChecked).pack()
tk.Checkbutton(top, text = "Italic", variable = typeItalic, onvalue = 2, offvalue = 0, command = typeChecked).pack()
top.mainloop() #进入主事件循环
可以使用tkinter.StringVar()创建与特定控件关联的字符串变量,使用tkinter.IntVar()创建与特定控件关联的整型变量。
程序运行效果:
3.6Menu控件
tkinter提供了Menu控件,用于实现顶级菜单、下拉菜单和弹出菜单。
3.6.1.顶级菜单
创建一个顶级菜单,需要先创建一个菜单实例,然后使用add()方法将命令添加进去。
import tkinter #导入tkinter库
#定义函数用于显示信息
def callback():
print('单击了“显示”菜单!')
window = tkinter.Tk() #创建tkinter对象
window.title("标题") #设置标题
window.geometry("200x100") #设置窗口大小
menubar = tkinter.Menu(window) #创建一个顶级菜单窗口
#给菜单实例增加菜单项
menubar.add_command(label='显示', command = callback)
menubar.add_command(label='退出', command = window.quit)
window.config(menu = menubar) #显示菜单
window.mainloop() #进入主事件循环
程序运行效果:
3.6.2.下拉菜单
创建一个下拉菜单,方法同创建顶级菜单类似,最主要的区别是下拉菜单需要添加到主菜单上。
例:创建下拉菜单实例。
import tkinter
window = tkinter.Tk()
window.title("标题")
window.geometry("200x100")
#创建一个顶级菜单实例
menubar = tkinter.Menu(window)
#为每个子菜单实例添加菜单项
#创建文件菜单项,并添加子菜单
fmenu = tkinter.Menu(menubar)
for each in ['新建','打开','保存','另存为']:
fmenu .add_command(label = each)
#创建视图菜单项,并添加子菜单
vmenu = tkinter.Menu(menubar)
for each in ['复制','粘贴','剪切']:
vmenu.add_command(label = each)
#创建编辑菜单项,并添加子菜单
emenu = tkinter.Menu(menubar)
for each in ['默认视图','新式视图']:
emenu.add_command(label = each)
#创建关于菜单项,添加子菜单
amenu = tkinter.Menu(menubar)
for each in ['版权信息','联系我们']:
amenu.add_command(label = each)
#为顶级菜单实例添加菜单,并绑定相应的子菜单实例
menubar.add_cascade(label='文件',menu=fmenu)
menubar.add_cascade(label='视图',menu=vmenu)
menubar.add_cascade(label='编辑',menu=emenu)
menubar.add_cascade(label='关于',menu=amenu)
window.config(menu = menubar) #显示菜单
window.mainloop() #进入主事件循环
程序运行效果:
3.6.3.弹出菜单
创建一个弹出菜单的方法也是类似的,不过需要使用post()方法将其显示出来。
from tkinter import *
root = Tk()
#定义函数用于输出提示信息
def hello():
print( "选择了菜单!")
root.geometry("200x100")
#创建一个顶级菜单实例
menu = Menu(root)
menu.add_command(label="显示", command=hello)
menu.add_command(label="退出", command=root.quit)#关闭窗口并退出程序和window.quit效果一样
#弹出菜单
frame = Frame(root, width=512, height=512)
#创建一个 Frame 小部件,并设置其宽度为 512 像素,高度为 512 像素。
#虽然这里设置了较大的尺寸,但窗口本身的大小是通过 geometry 方法设置的(200×100),所以实际显示效果会根据窗口大小调整。
frame.pack()
#定义函数,调用post()方法显示
def popup(event):
menu.post(event.x_root, event.y_root)
#post:在指定的全局坐标处显示弹出菜单。
#event.x_root 和 event.y_root:分别表示鼠标点击位置的全局坐标(相对于屏幕)。
#绑定鼠标右键
frame.bind("<Button-3>", popup)
#将 <Button-3> 事件(通常是鼠标右键点击)绑定到 popup 函数上。
#当用户在框架内右键点击时,会触发 popup 函数,从而显示弹出菜单。
root.config(menu = menu)
root.mainloop()
程序运行效果:
3.7事件绑定
一个tkinter应用程序的大部分时间花费在事件循环上(通过mainloop()方法进入)。
事件可以有多种来源,包括用户触发的鼠标、键盘操作或是系统事件。
tkinter提供了强大的事件处理机制,对于每个控件来说,可以通过bind()方法将函数或方法绑定到具体的事件上,其语法格式如下:
例子:捕获鼠标点击事件实例。
from tkinter import * #导入tkinter库中所有内容
root = Tk() #创建tkinter对象
#定义函数,用于输出鼠标单击的坐标
def callback(event):
print ("clicked at", event.x, event.y)
frame = Frame(root, width=200, height=100) #创建窗体
frame.bind("<Button-1>", callback) #绑定鼠标左键
frame.pack() #显示窗体
root.mainloop() #进入主事件循环
程序运行效果:
3.7.1事件序列
tkinter使用一种称为事件序列的机制来允许用户定义事件,事件序列以字符串的形式表示,其语法格式如下:
<modifier-type-detail>
说明:
(1)事件序列必须用尖括号括起来;
(2)type字段是最重要的,它通常用于描述事件类型,如鼠标单击、键盘输入等;
(3)modifier字段是可选的,它通常用于描述组合键,如Ctrl、Shift等;
(4)detail字段也是可选的,它通常用于描述具体的按键,如Button-1表示单击鼠标左键。
type字段常用的关键字及含义:
modifier字段常用的关键字及含义
3.7.2事件对象
当tkinter调用预先定义的函数时,会将事件对象(作为参数)传递给函数,事件对象的属性及含义如下表所示。
例子:事件绑定实例。
import tkinter #导入tkinter库
window = tkinter.Tk() #创建tkinter对象
window.title("标题") #设置标题
window.geometry("200x100") #设置窗口大小
#鼠标单击绑定事件
def func(event):
print("单击!")
window.bind("<Button-1>",func)
#鼠标双击绑定事件
def func1(event):
print("双击!")
window.bind("<Double-Button-1>",func1)
#鼠标移入绑定事件
def func2(event):
print("鼠标移入!")
window.bind("<Enter>",func2)
#实现拖拽功能
def func3(event):
x=str(event.x_root)
y=str(event.y_root)
#event.x_root和event.y_root:分别表示鼠标点击位置的全局坐标(相对于屏幕)
window.geometry("200x100+"+x+"+"+y)
#geometry 方法:重新设置窗口的大小和位置。
#格式为 "宽度x高度+x坐标+y坐标",
#例如 "200x100+100+100" 表示窗口大小为 200×100,位置在屏幕的 (100, 100) 处。
window.bind("<B1-Motion>",func3)
#<B1-Motion>:这是一个事件类型,表示当鼠标左键按下并在窗口内移动时触发。B1 表示鼠标左键,Motion 表示移动。
window.mainloop()
程序运行效果:
4.布局管理器
4.1pack布局
pack布局是按添加的顺序排列控件,即向容器中添加控件,第一个添加的控件在最上方,然后依次向下排列。
例:pack布局实例。
from tkinter import * #导入tkinter库中所有内容
root = Tk() #创建tkinter对象
#创建三个标签
Label(root, text = 'pack1', bg = 'red').pack()
Label(root, text = 'pack2', bg = 'blue').pack()
Label(root, text = 'pack3', bg = 'green').pack()
root.mainloop() #进入主事件循环
程序运行效果:
pack布局的常用属性
pack布局适用于少量控件的排列,当界面复杂度增加时,要实现某种布局效果,需要分层来实现。
例:分层实现较复杂布局。
from tkinter import *
root = Tk()
root.title("Pack - Example")
#这段代码使用 tkinter 库创建了一个带有两个框架(Frame)的窗口,每个框架中包含若干按钮,并展示了如何使用 pack() 方法来布局这些小部件。
#使用Frame增加一层容器
fm1 = Frame(root)
#创建3个按钮,从上到下排列
Button(fm1, text='Top').pack(side=TOP, anchor=W, fill=X)
Button(fm1, text='Center').pack(side=TOP, anchor=W, fill=X)
Button(fm1, text='Bottom').pack(side=TOP, anchor=N, fill=Y)
#pack 方法:side=TOP:指定按钮在框架内从上到下排列。anchor=W:指定按钮对齐方式为左对齐(西方向)。fill=X:指定按钮在水平方向上填充整个可用空间。
fm1.pack(side=LEFT, fill=Y)
#将框架 fm1 添加到主窗口中,并指定它从左到右排列,同时在垂直方向上填充整个可用空间。
#使用Frame再增加一层容器
fm2 = Frame(root)
#创建3个按钮,从左到右排列
Button(fm2, text='Left').pack(side=LEFT)
Button(fm2, text='This is the Center button').pack(side=LEFT)
Button(fm2, text='Right').pack(side=LEFT)
#side=LEFT:指定按钮在框架内从左到右排列。
fm2.pack(side=LEFT, padx=10)
#将框架 fm2 添加到主窗口中,并指定它从左到右排列,同时在左右两边各留出 10 像素的间距。
root.mainloop()
程序运行效果:
4.2grid布局
grid布局又称为网格布局,是tkinter布局管理器中最灵活多变的布局方法。由于大多数程序界面都是矩形的,我们可以将它划分为由行和列组成的网格,然后根据行号和列号,将控件放置于网格之中。
例:grid布局实例。
from tkinter import * #导入tkinter库中所有内容
root = Tk() #创建tkinter对象
colours = ['red','green','orange','white','yellow','blue'] #定义颜色列表
r = 0
#循环创建标签和不同颜色的输入框
for c in colours:
Label(root,text=c, relief=SUNKEN,width=15).grid(row=r,column=0)#设置标签边框样式为凸起(RIDGE)
Entry(root,bg=c, relief=SUNKEN,width=10).grid(row=r,column=1)#设置输入框边框样式为凹陷(SUNKEN),设置输入框宽度为 10 个字符宽
r = r + 1
root.mainloop() #进入主事件循环
程序运行效果:
4.3place布局
place布局使用控件坐标来放置控件的位置。
例:place布局实例。
from tkinter import * #导入tkinter库中所有内容
root = Tk() #创建tkinter对象
root.geometry("200x100") #设置窗口大小
la = Label(root,text = 'hello Place a') #创建标签la
la.place(x = 0,y = 0,anchor = NW) #使用绝对坐标将Label放置到(0,0)位置上
#指定标签左上角的位置为窗口的 (0, 0) 点,即窗口的左上角。
#指定标签的锚点为西北(NW),即标签的左上角对齐到指定的坐标点。
lb = Label(root,text = 'hello Place b') #创建标签lb
lb.place(relx = 0.5,rely = 0.5,anchor = CENTER) #使用相对坐标将标签放置到窗口中央
#指定标签相对于窗口宽度和高度的相对位置。
#relx=0.5 表示标签在窗口宽度方向上的 50% 处,rely=0.5 表示标签在窗口高度方向上的 50% 处。
#指定标签的锚点为中心(CENTER),即标签的中心对齐到指定的相对坐标点。
root.mainloop() #进入主事件循环
程序运行效果:
提示:(1)在同一个主窗口中不要混用这3种布局管理器。 (2)不推荐使用place布局,因为在不同分辨率下,界面往往有较大差异。
5.标准对话框
5.1messagebox模块
messagebox模块用于显示一个模式对话框,其中包含一个系统图标、一组按钮和一个简短的特定于应用程序的消息,如状态或错误信息。
例:messagebox模块实例。
import tkinter as tk #导入tkinter模块并命名为tk
from tkinter import messagebox as msgbox #导入tkinter. messagebox模块并命名
#定义各个函数用于相应按钮事件
def btn1_clicked():
msgbox.showinfo("Info", " showinfo测试!")
# showinfo:显示信息消息框。
def btn2_clicked():
msgbox.showwarning("Warning", " showwarning测试!")
# showwarning:显示警告消息框。
def btn3_clicked():
msgbox.showerror("Error", "showerror测试!")
# showerror:显示错误消息框。
def btn4_clicked():
msgbox.askquestion("Question", "askquestion测试!")
# askquestion:显示询问消息框(返回用户选择的字符串 "yes" 或 "no")。
def btn5_clicked():
msgbox.askokcancel("OkCancel", "askokcancel测试!")
# askokcancel:显示确认消息框(返回布尔值 True 或 False)。
def btn6_clicked():
msgbox.askyesno("YesNo", "askyesno测试!")
# askyesno:显示是/否消息框(返回布尔值 True 或 False)。
def btn7_clicked():
msgbox.askretrycancel("Retry", "askretrycancel测试!")
# askretrycancel:显示重试/取消消息框(返回布尔值 True 或 False)。
top = tk.Tk() #创建tkinter对象
top.title("MsgBox Test") #设置标题
#创建按钮用于触发各个对话框函数
btn1 = tk.Button(top, text = "showinfo", command = btn1_clicked)
btn1.pack(fill = tk.X)
btn2 = tk.Button(top, text = "showwarning", command = btn2_clicked)
btn2.pack(fill = tk.X)
btn3 = tk.Button(top, text = "showerror", command = btn3_clicked)
btn3.pack(fill = tk.X)
btn4 = tk.Button(top, text = "askquestion", command = btn4_clicked)
btn4.pack(fill = tk.X)
btn5 = tk.Button(top, text = "askokcancel", command = btn5_clicked)
btn5.pack(fill = tk.X)
btn6 = tk.Button(top, text = "askyesno", command = btn6_clicked)
btn6.pack(fill = tk.X)
btn7 = tk.Button(top, text = "askretrycancel", command = btn7_clicked)
btn7.pack(fill = tk.X)
top.mainloop() #进入主事件循环
程序运行效果:
5.2filedialog模块
filedialog模块用于打开文件对话框,该模块提供了2个函数:
askopenfilename()函数用于打开“打开”对话框
asksaveasfilename()函数用于打开“另存为”对话框
例:filedialog模块实例。
import tkinter.filedialog as fd #导入tkinter.filedialog模块
#import tkinter.filedialog
#tkinter 模块本身确实是一个包含多个子模块和组件的大型库,但为了保持模块加载的效率和灵活性,Python 并不会自动导入所有可能用到的子模块。
#因此,尽管 filedialog 是 tkinter 的一部分,它并不会随着 tkinter 的基础导入而自动可用。
from tkinter import * #导入tkinter模块
root = Tk() #创建tkinter对象
#定义函数用于响应按钮事件
def callback():
fileName = fd.askopenfilename() #打开“打开”对话框
#fileName = tkinter.filedialog.askopenfilename()
#这是一个对话框方法,用于显示“打开文件”对话框,允许用户选择一个文件。
#返回值是用户选择的文件路径(字符串),如果用户取消选择,则返回空字符串。
#fileName = tkinter.filedialog.askopenfilename(filetypes=[("Text files", "*.txt")])
#只允许选择 .txt 文件
#设置文件类型过滤:可以通过filetypes参数限制用户可以选择的文件类型。
print(fileName) #输出文件名
#创建按钮用于触发事件
Button(root,text='打开文件',command=callback).pack()
root.mainloop() #进入主事件循环
程序运行效果:
5.3colorchooser模块
colorchooser模块用于打开颜色选择对话框,由askcolor()函数实现。
例:colorchooser模块实例。
import tkinter.colorchooser #导入tkinter.colorchooser模块
from tkinter import * #导入tkinter模块
root = Tk() #创建tkinter对象
#定义函数用于相应按钮事件
def callback():
fileName = tkinter.colorchooser.askcolor() #打开颜色选择对话框
print(fileName) #输出颜色信息
#创建按钮
Button(root,text="选择颜色",command=callback).pack()
root.mainloop() #进入主事件循环
程序运行效果:
6.典型案例
例:编写计算器应用程序,实现简单的算术运算功能。
根据分析,计算器应用程序可分为两大功能模块:
(1)创建计算器界面:计算器界面由多个按钮(如数字按钮、符号按钮等)和一个标签(用于输出按钮信息和计算结果)构成,可利用tkinter提供的Button控件和Label控件实现,再利用布局管理器(grid布局)将各个控件排列显示,其中创建Button控件时利用其command属性调用相应的功能函数。
(2)创建按钮键值类:该类中定义一个构造方法用于接收按钮值,然后定义多个方法用于实现具体的按键功能(供Button控件调用),包括实现添加数值功能的方法,实现删除功能的方法,实现清空功能的方法,实现切换正负号功能的方法,实现添加小数点功能的方法,以及实现计算功能的方法。
import tkinter, decimal, math # 加载各种库
#tkinter:用于创建图形用户界面(GUI)。
#decimal:用于高精度的十进制浮点数运算,确保计算结果更准确。
#math:提供数学函数,如平方根 (sqrt) 和倒数 (1/x)。
window = tkinter.Tk() # 创建tkinter对象
window.title('计算器') # 设置标题
window.resizable(0, 0) # 设置大小不可变
# 全局变量,storage存储列表,vartext存储字符串,result存储结果,symbol存储符号
global storage, vartext, result, symbol
result = symbol = None # 变量result和symbol设置值为None
#result 和 symbol:分别用于存储计算结果和当前操作符,初始值为 None。
vartext = tkinter.StringVar() # 创建StringVar对象
#StringVar 对象,用于动态更新标签或按钮上的文本。
storage = [] # 变量storage设置为列表
# 按键值类
class key_press:
global storage, vartext, result, symbol # 引用全局变量
# 引用全局变量的语句必须出现在函数或方法体的顶部,而不是类的顶部。
#storage是一个列表,vartext是一个Tkinter的StringVar对象。这两者都是可变对象(mutable objects)。
# 当你对这些可变对象进行修改时,实际上是在改变这些对象的内容或状态,而不是创建新的对象。由于这些对象本身没有被重新赋值,因此所有引用它们的地方都会看到这些变化。
#相比之下,result和symbol是基本数据类型(可能是整数、浮点数或字符串等不可变对象),当你在clear法中将它们设置为None时,实际上是在创建新的对象,并试图让局部变量指向这些新对象。
# 但是,因为没有正确地使用global 关键字声明这些变量,所以这些操作只会影响方法内部的局部副本,不会影响到全局变量。所以需要在方法的顶部引入变量。
# 构造方法,设置按键属性
def __init__(self, anjian):
self.key = anjian # 按键名称
# 添加,用于将按键值添加到storage列表,再将storage的值设置为vartext的值
def jia(self):
storage.append(self.key) # 将按键值添加到storage列表
vartext.set(''.join(storage)) # 将storage的值连接生成一个新的字符串,并设置为vartext的值
#将列表转换为字符串
# 删除功能
def retreat(self):
storage.pop() # 移除storage列表的值(默认最后一个元素)
vartext.set(''.join(storage)) # 将storage的值连接生成一个新的字符串,并设置为vartext的值
# 清除功能
def clear(self):
global storage, vartext, result, symbol
storage.clear() # 清空storage列表
vartext.set('') # 设置vartext的值为空
result = None # 变量result结果改为None
symbol = None # 变量symbol结果改为None
# 切换正负功能
def plus_minus(self):
if storage[0]: # 先判断storage[0]是否有值
if storage[0] == '-': # 如果storage[0]值为“-”
storage[0] = '+' # 将storage[0]值改为“+”
elif storage[0] == '+': # 如果storage[0]值为“+”
storage[0] = '-' # 将storage[0]值为“-”
else:
storage.insert(0, '-') # 如果storage[0]的值既不是“+”,又不是“-”,则表示没有符号,添加一个“-”
vartext.set(''.join(storage)) # 将storage的值连接生成一个新的字符串,并设置为vartext的值
# 添加小数点功能
def decimal_point(self):
if storage.count('.') >= 1: # 如果已经存在小数点,则什么都不做
pass
else:
if storage == []: # 如果storage为空
storage.append('0') # 给storage队列添加0
storage.append('.') # 给storage队列添加.
vartext.set(''.join(storage)) # 将storage的值连接生成一个新的字符串,并设置为vartext的值
# 运算
def operation(self):
global storage, vartext, result, symbol # 引用全局变量
if vartext.get() == '': # 如果vartext中没有值,则什么也不做
pass
#pass 是一个占位语句,它什么也不做。它的主要用途是作为语法上的占位符,允许你定义一个不执行任何操作的代码块。
else:
get1 = decimal.Decimal(vartext.get()) # 获取输入的数值
if self.key in ('1/x', 'sqrt'): # 如果按键值属于'1/x'和'sqrt'其中之一
if self.key == '1/x': # 如果按键值是“1/x”
result = 1 / get1 # 将1/变量get1的结果赋值给变量result
elif self.key == 'sqrt': # 如果按键值是“sqrt”
result = math.sqrt(get1) # 计算变量get1的平方根
elif self.key in ('+', '-', '*', '/', '='): # 判断按键值
if symbol is not None: # 如果symbol变量不是None
get1 = decimal.Decimal(result) # 获取第一次输入的数值
get2 = decimal.Decimal(vartext.get()) # 获取第二次输入的数值
if symbol == '+': # 如果符号是“+”
result = get1 + get2 # 相加
elif symbol == '-': # 如果符号是“-”
result = get1 - get2 # 相减
elif symbol == '*': # 如果符号是“*”
result = get1 * get2 # 相乘t
elif symbol == '/': # 如果符号是“/”
result = get1 / get2 # 相除
else: # 如果没有输入符号
result = get1 # 获取get1的值
if self.key == '=': # 如果输入的键值为“=”
symbol = None # 变量symbol则赋值为None
else: # 输入的键值不为“=”
symbol = self.key # 将输入的键值赋值给symbol
print(symbol) # 输出变量symbol,表示符号
print(result) # 输出变量result,表示结果
vartext.set(str(result)) # 将结果转为字符串形式,并设置为vartext的值
storage.clear() # 清空storage列表
# 计算器布局
def layout(window):
global storage, vartext, result, symbol # 引用全局变量
# 设置顶部标签,用于展示按键的值
entry1 = tkinter.Label(window, width=30, height=2, bg='white', anchor='se', \
textvariable=vartext)
#width = 30:设置标签的宽度为30字符宽。这里的宽度是指字符数,而不是像素数,因此它会根据字体大小自动调整实际宽度。
#height = 2:设置标签的高度为2行。同样,高度是以行为单位的,而不是像素。
#anchor='se':设置标签中文本的对齐方式。anchor 参数可以接受多个值,表示文本相对于标签的位置:
#'n':北(顶部居中);'s':南(底部居中);'e':东(右对齐);'w':西(左对齐);'ne':东北(右上角);'nw':西北(左上角);'se':东南(右下角);'sw':西南(左下角);'center':居中
#在这里,'se' 表示文本将靠右下角对齐,模仿了传统计算器屏幕上数字的显示方式。
#textvariable=vartext:将标签的内容绑定到一个 StringVar 对象 vartext。这意味着标签显示的内容会随着 vartext 的值变化而自动更新。
#StringVar 是 tkinter 提供的一种特殊变量类型,用于动态管理字符串值。
#反斜杠\用作续行符(line continuation character)。它的作用是告诉 Python 解释器当前行的代码将继续到下一行。这允许你将一个长语句拆分成多行书写,以提高代码的可读性。
#反斜杠后面不能有任何非空白字符(包括空格),否则会导致语法错误。它必须紧接在行尾,并且下一行应该紧接着续行的内容。
#如果你在一个括号、方括号或花括号内的表达式中,通常不需要使用反斜杠。Python 允许在这种情况下自然地跨多行编写代码。
entry1.grid(row=0, columnspan=5)
# 添加按钮
buttonJ = tkinter.Button(window, text='←', width=5, command=key_press('c').retreat)
buttonCE = tkinter.Button(window, text='', width=5)
buttonC = tkinter.Button(window, text=' C ', width=5, command=key_press('c').clear)
button12 = tkinter.Button(window, text='±', width=5, command=key_press('c').plus_minus)
buttonD = tkinter.Button(window, text='√', width=5, command=key_press('sqrt').operation)
buttonJ.grid(row=2, column=0)
buttonCE.grid(row=2, column=1)
buttonC.grid(row=2, column=2)
button12.grid(row=2, column=3)
buttonD.grid(row=2, column=4)
button7 = tkinter.Button(window, text=' 7 ', width=5, command=key_press('7').jia)
button8 = tkinter.Button(window, text=' 8 ', width=5, command=key_press('8').jia)
button9 = tkinter.Button(window, text=' 9 ', width=5, command=key_press('9').jia)
buttonc = tkinter.Button(window, text=' / ', width=5, command=key_press('/').operation)
buttonf = tkinter.Button(window, text=' ', width=5)
button7.grid(row=3, column=0)
button8.grid(row=3, column=1)
button9.grid(row=3, column=2)
buttonc.grid(row=3, column=3)
buttonf.grid(row=3, column=4)
button4 = tkinter.Button(window, text=' 4 ', width=5, command=key_press('4').jia)
button5 = tkinter.Button(window, text=' 5 ', width=5, command=key_press('5').jia)
button6 = tkinter.Button(window, text=' 6 ', width=5, command=key_press('6').jia)
buttonx = tkinter.Button(window, text=' * ', width=5, command=key_press('*').operation)
buttonfs = tkinter.Button(window, text='1/x', width=5, command=key_press('1/x').operation)
button4.grid(row=4, column=0)
button5.grid(row=4, column=1)
button6.grid(row=4, column=2)
buttonx.grid(row=4, column=3)
buttonfs.grid(row=4, column=4)
button1 = tkinter.Button(window, text=' 1 ', width=5, command=key_press('1').jia)
button2 = tkinter.Button(window, text=' 2 ', width=5, command=key_press('2').jia)
button3 = tkinter.Button(window, text=' 3 ', width=5, command=key_press('3').jia)
button_ = tkinter.Button(window, text=' - ', width=5, command=key_press('-').operation)
buttondy = tkinter.Button(window, text=' \n = \n ', width=5, command=key_press('=').operation)
button1.grid(row=5, column=0)
button2.grid(row=5, column=1)
button3.grid(row=5, column=2)
button_.grid(row=5, column=3)
buttondy.grid(row=5, column=4, rowspan=2)
button0 = tkinter.Button(window, text=' 0 ', width=11, command=key_press('0').jia)
buttonjh = tkinter.Button(window, text=' . ', width=5, command=key_press('c').decimal_point)
buttonjia = tkinter.Button(window, text=' + ', width=5, command=key_press('+').operation)
button0.grid(row=6, column=0, columnspan=2)
buttonjh.grid(row=6, column=2)
buttonjia.grid(row=6, column=3)
layout(window) # window窗口应用layout布局
window.mainloop() # 进入事件(消息)循环
程序运行效果: