python GUI编程

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()  # 进入事件(消息)循环

 程序运行效果:

# -*- coding: utf-8 -*- # Form implementation generated from reading ui file 'aaa.ui' # # Created by: PyQt5 UI code generator 5.11.3 # # WARNING! All changes made in this file will be lost! import sys, os from PyQt5 import QtCore, QtWidgets, QtGui from PyQt5.QtCore import * from PyQt5.QtWidgets import * from PyQt5.QtGui import * from PyQt5 import QtCore, QtGui, QtWidgets class U_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") MainWindow.resize(1204, 836) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(MainWindow.sizePolicy().hasHeightForWidth()) MainWindow.setSizePolicy(sizePolicy) self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") self.tableWidget = QtWidgets.QTableWidget(self.centralwidget) self.tableWidget.setGeometry(QtCore.QRect(80, 240, 1041, 371)) self.tableWidget.setAutoScrollMargin(9) self.tableWidget.setRowCount(9) self.tableWidget.setColumnCount(8) self.tableWidget.setObjectName("tableWidget") item = QtWidgets.QTableWidgetItem() self.tableWidget.setVerticalHeaderItem(0, item) item = QtWidgets.QTableWidgetItem() self.tableWidget.setVerticalHeaderItem(1, item) item = QtWidgets.QTableWidgetItem() self.tableWidget.setVerticalHeaderItem(2, item) item = QtWidgets.QTableWidgetItem() self.tableWidget.setVerticalHeaderItem(3, item) item = QtWidgets.QTableWidgetItem() self.tableWidget.setVerticalHeaderItem(4, item) item = QtWidgets.QTableWidgetItem() self.tableWidget.setVerticalHeaderItem(5, item) item = QtWidgets.QTableWidgetItem() self.tableWidget.setVerticalHeaderItem(6, item) item = Q
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值