Python功能包之Tkinter——窗口视图GUI
窗口
窗口与分辨率
import tkinter as tk
window = tk.Tk()# 创建一个Tk对象,这是创建GUI的主窗口
window.title('my window') # 设置主窗口的标题为'my window'
# window.geometry('200x100+200+200') # 设置主窗口的大小为宽200x高100,并设置打开位置
a = window.maxsize() # 获取分辨率
# print(a)
k,g=a
window.geometry(f'{k//2}x{g//2}') # 设置主窗口的大小为获取分辨率的一半,并设置打开位置
window.mainloop()
输出:
(1536, 864)
他的大小为一半
窗口缩放与图标
import tkinter as tk
a1 = tk.Tk()
a1.title('111')
a1.geometry('500x500+100+100')
# 设置窗口锁定缩放(False就是锁定)
a1.resizable(width=False, height=False)
# 设置窗口图标(ico格式的)
a1.iconbitmap("C:\\Users\HP\Pictures\\10.ico")
a1.mainloop()
输出:
窗口背景与透明度
import tkinter as tk
a1 = tk.Tk()
a1.title('111')
a1.geometry('500x500')
# 设置窗口背景颜色(颜色编码值或者写英文)
a1.configure(bg='#990066')
# 设置窗口透明度(0-1的透明度设置)
a1.attributes('-alpha', 0.5)
a1.mainloop()
输出:
窗口置顶与销毁
import tkinter as tk
a1 = tk.Tk()
a1.title('111')
a1.geometry('500x500')
# 设置窗口置顶
a1.attributes('-topmost', True)
def guan():
# 这里可以设置弹窗确认是否确认关闭的操作
print('1111')
# 销毁窗口
a1.destroy()
# 设置窗口关闭时执行函数
a1.protocol('WM_DELETE_WINDOW', guan)
a1.mainloop()
组件
标签与按钮
Label与Button
import tkinter as tk
window = tk.Tk()# 创建一个Tk对象,这是创建GUI的主窗口
window.title('my window') # 设置主窗口的标题为'my window'
window.geometry('200x100') # 设置主窗口的大小为宽200x高100
var = tk.StringVar() # 创建一个StringVar对象,用于存储和操作字符串
l = tk.Label(window, textvariable=var, bg='green', font=('Arial', 12), width=15, height=2)
'''
创建一个Label(标签)对象
window:标签所属的父窗口
textvariable=var:标签的文本内容由var变量决定
text='':直接设置里面内容,而不是用bar变量决定
fg='rea':设置文本颜色为红色
bg='green':标签的背景颜色为绿色
font=('Arial', 12):标签的字体为Arial,大小为12
width=15:标签的宽度为15个字符
height=2:标签的高度为2行
'''
l.pack() # 将标签放置到主窗口中
on_hit = False # 初始化一个布尔变量on_hit为False,为后面按钮用的
def hit_me():
global on_hit # 声明on_hit为全局变量
if on_hit == False:
on_hit = True
var.set('you hit me')
else:
on_hit = False
var.set('')
# 当on_hit为False时,将on_hit设为True,并将标签的文本设置为'you hit me'
# 当on_hit为True时,将on_hit设为False,并将标签的文本设置为空字符串
b = tk.Button(window, text='hit me', width=15, height=2, command=hit_me)
'''创建一个Button(按钮)对象
window:按钮所属的父窗口
text='hit me':按钮上的文本为'hit me'
width=15:按钮的宽度为15个字符
height=2:按钮的高度为2行
command=hit_me:按钮被点击时调用hit_me函数'''
b.pack() # 将按钮放置到主窗口中
window.mainloop() # 进入主事件循环,使窗口保持显示状态并处理用户交互
输出:
当没点击时
点击后
输入与文本框
Entry与Text
import tkinter as tk
window = tk.Tk()
window.title('my window')
window.geometry('200x200')
e = tk.Entry(window, show='*')
# 创建一个Entry(输入框)对象,用于用户输入
# show='*'表示输入的字符会以星号显示,通常用于密码输入(可以设置为None就可以看对应的输入了)
e.pack()
def insert_point():
var = e.get()
t.insert('insert', var)
# 函数的功能是获取输入框e中的文本,并将其插入到文本框t的当前光标位置
def insert_end():
var = e.get()
t.insert('end', var)
# 函数的功能是获取输入框e中的文本,并将其插入到文本框t的末尾,‘end’可以改为其他的,比如1.1就是一行一位;2.1就是第二行第二位
b1 = tk.Button(window, text='insert point', width=15, height=2, command=insert_point)
'''创建一个Button(按钮)对象b1
按钮上的文本为'insert point',宽度为15,高度为2
当按钮被点击时,调用insert_point函数'''
b1.pack()
b2 = tk.Button(window, text='insert end', width=15, height=2, command=insert_end)
b2.pack()
t = tk.Text(window, height=2) # 创建一个Text(文本框)对象t,高度为2行
t.pack() # 将文本框t添加到主窗口中
window.mainloop()
输出:
光标在第二位,但点击insert end会自动加到最后
列表部件
Listbox
import tkinter as tk
# 创建主窗口
window = tk.Tk()
window.title('my window') # 设置窗口标题
window.geometry('200x200') # 设置窗口大小
# 创建一个字符串变量
var1 = tk.StringVar()
# 创建一个标签,背景色为黄色,宽度为4,文本内容绑定到var1变量
l = tk.Label(window, bg='yellow', width=4, textvariable=var1)
l.pack()
def print_selection():
# 获取列表框中当前选中项的值
value = lb.get(lb.curselection())
# 将获取到的值设置到var1变量中,这样会更新标签的显示内容
var1.set(value)
# 创建一个按钮,文本为'print selection',宽度为15,高度为2,点击时执行print_selection函数
b1 = tk.Button(window, text='print_selection', width=15, height=2, command=print_selection)
b1.pack()
# 创建一个字符串变量,并设置其初始值为(11, 22, 33, 44)
var2 = tk.StringVar()
var2.set((11, 22, 33, 44))
# 创建一个列表框,其内容由var2变量决定
lb = tk.Listbox(window, listvariable=var2)
lb.insert(1, 'first') # 在列表框的索引为1的位置插入'first'
lb.insert(2, 'second')
lb.delete(2) # 删除列表框中索引为2的元素
lb.pack()
window.mainloop()
输出:
下拉列表(组合框)
import tkinter as tk
from tkinter.ttk import *
root = tk.Tk()
root.title("Text")
root.geometry("500x500")
# 创建一个整型变量,用于存储Combobox的选择值
sexVar = tk.IntVar()
# 创建一个Combobox(下拉列表框),并将其与sexVar变量绑定
cmb_ip_list = tk.ttk.Combobox(root, textvariable=sexVar)
# 定义一个列表,包含"男"和"女"两个选项
sex_list = ["男", "女"]
# 设置Combobox的选项值为sex_list中的内容
cmb_ip_list["value"] = sex_list
# 设置Combobox的默认选中项为列表中的第二项(索引为1,即"女")
cmb_ip_list.current(1)
cmb_ip_list.pack()
root.mainloop()
输出:
选择按钮
Radiobutton
import tkinter as tk
window = tk.Tk()
window.title('my window')
window.geometry('500x500')
var = tk.StringVar()
l = tk.Label(window, bg='yellow', width=40,text='')
l.pack()
def print_selection():
l.config(text='you have selected ' + var.get())
# 创建第一个单选按钮(Radiobutton)对象
# 文本为'Option A',关联变量为var,选项值为'A',选择时调用print_selection函数
r1 = tk.Radiobutton(window, text='Option A', variable=var, value='A', command=print_selection)
# 将第一个单选按钮放置到窗口中
r1.pack()
r2 = tk.Radiobutton(window, text='Option B', variable=var, value='B', command=print_selection)
r2.pack()
r3 = tk.Radiobutton(window, text='Option C', variable=var, value='C', command=print_selection)
r3.pack()
window.mainloop()
输出:
尺度
Scale
import tkinter as tk
# 创建主窗口
window = tk.Tk()
window.title('my window') # 设置窗口标题
window.geometry('300x300') # 设置窗口大小
l = tk.Label(window, bg='yellow', width=20, text='')
l.pack()
# 定义一个函数,用于处理Scale滑块的事件
def print_selection(v):
# 更新标签的文本内容,显示滑块当前的值
l.config(text='you have selected ' + v)
# 创建一个Scale滑块
s = tk.Scale(window,
label='try me', # 设置滑块的标签为'try me'
from_=0, to=12, # 设置滑块的取值范围从0到12
orient=tk.HORIZONTAL, # 设置滑块为水平方向
length=200, # 设置滑块的长度为200像素
showvalue=0, # 不显示滑块当前的值
tickinterval=3, # 设置刻度间隔为3
resolution=0.01, # 设置滑块的精度为0.01
command=print_selection) # 当滑块值改变时,调用print_selection函数
s.pack()
window.mainloop()
输出:
勾选项
Checkbutton
import tkinter as tk
# 创建主窗口
window = tk.Tk()
window.title('my window') # 设置窗口标题
window.geometry('300x300') # 设置窗口大小
l = tk.Label(window, bg='yellow', width=20, text='')
l.pack()
def print_selection():
# 根据两个复选框的选择状态,更新标签的文本内容
if (var1.get() == 1) and (var2.get() == 0):
l.config(text='I love only Python ')
elif (var1.get() == 0) and (var2.get() == 1):
l.config(text='I love only C++')
elif (var1.get() == 0) and (var2.get() == 0):
l.config(text='I do not love either')
else:
l.config(text='I love both')
# 创建两个整型变量,用于存储复选框的状态
var1 = tk.IntVar()
var2 = tk.IntVar()
# 文本为'Python',关联变量为var1,选中时值为1,未选中时值为0,点击时调用print_selection函数
c1 = tk.Checkbutton(window, text='Python', variable=var1, onvalue=1, offvalue=0, command=print_selection)
c2 = tk.Checkbutton(window, text='C++', variable=var2, onvalue=1, offvalue=0, command=print_selection)
c1.pack()
c2.pack()
window.mainloop()
输出:
画布
Canvas
import tkinter as tk
# 创建主窗口
window = tk.Tk()
window.title('my window') # 设置窗口标题
window.geometry('400x400') # 设置窗口大小
# 创建一个画布(Canvas),背景颜色为蓝色,高度为300像素,宽度为200像素
canvas = tk.Canvas(window, bg='blue', height=300, width=200)
image_file = tk.PhotoImage(file='D:\pythonProject\image/9.jpg')
# 在画布上创建图像,位置为(10, 10),锚点为左上角(northwest),使用加载的图片
image = canvas.create_image(10, 10, anchor='nw', image=image_file)
x0, y0, x1, y1 = 50, 50, 80, 80
line = canvas.create_line(x0, y0, x1, y1) # 在画布上创建一条线,起点为(x0, y0),终点为(x1, y1)
oval = canvas.create_oval(x0, y0, x1, y1, fill='red') # 在画布上创建一个填充为红色的椭圆,使用定义的坐标点作为外接矩形
arc = canvas.create_arc(x0+30,y0+30,x1+30,y1+30,start=0, extent=180) # 在画布上创建一个弧形,起始角度为0度,扩展角度为180度,位置基于之前的坐标点偏移
rect= canvas.create_rectangle(100,30,100+20,30+20) # 在画布上创建一个矩形,位置为(100, 30),大小为20x20
canvas.pack()
def moveit():
# 在画布上移动矩形,水平移动0像素,垂直移动2像素
canvas.move(rect,0,2)
# 创建一个按钮,文本为'move',点击时调用moveit函数,并将按钮放置到窗口中
b = tk.Button(window, text='move', command=moveit).pack()
window.mainloop()
输出:
若点击几次移动
菜单
Menubar
import tkinter as tk
window = tk.Tk()
window.title('my window')
window.geometry('200x200')
l = tk.Label(window, text="", bg='yellow')
l.pack()
# 初始化一个计数器变量
counter = 0
# 定义一个函数,用于更新标签的文本内容并增加计数器的值
def do_job():
global counter
# 更新标签的文本内容,显示'do'加上当前的计数器值
l.config(text='do ' + str(counter))
counter += 1
# 创建菜单栏
menubar = tk.Menu(window)
# 创建文件菜单
filemenu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label='File', menu=filemenu) # 将文件菜单添加到菜单栏中,标签为'File'
filemenu.add_command(label='New', command=do_job) # 向文件菜单中添加'New'命令,点击时调用do_job函数
filemenu.add_command(label='Open', command=do_job)
filemenu.add_command(label='Save', command=do_job)
# 在文件菜单中添加一条分隔线
filemenu.add_separator()
filemenu.add_command(label='Exit', command=window.quit) # 向文件菜单中添加'Exit'命令,点击时关闭窗口
# 创建编辑菜单
editmenu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label='Edit', menu=editmenu) # 将编辑菜单添加到菜单栏中,标签为'Edit'
editmenu.add_command(label='Cut', command=do_job)
editmenu.add_command(label='Copy', command=do_job)
editmenu.add_command(label='Paste', command=do_job)
# 创建子菜单
submenu = tk.Menu(filemenu)
filemenu.add_cascade(label='Import', menu=submenu, underline=0) # 将子菜单添加到文件菜单中,标签为'Import'
submenu.add_command(label="Submenu1", command=do_job) # 向子菜单中添加'Submenu1'命令,点击时调用do_job函数
window.config(menu=menubar) # 将菜单栏配置到窗口中
window.mainloop()
输出:
框架
Frame
import tkinter as tk
window = tk.Tk()
window.title('my window')
window.geometry('200x200')
tk.Label(window, text='on the window').pack()
# 创建一个框架(Frame),它属于window,并将其放置在窗口中
frm = tk.Frame(window)
frm.pack()
# 创建两个子框架,它们都属于frm
frm_l = tk.Frame(frm)
frm_r = tk.Frame(frm)
# 将frm_l框架放置在frm的左侧
frm_l.pack(side='left')
# 将frm_r框架放置在frm的右侧
frm_r.pack(side='right')
# 在frm_l框架中创建一个标签,显示文本'on the frm_l1',并将其放置在frm_l中
tk.Label(frm_l, text='on the frm_l1').pack()
tk.Label(frm_l, text='on the frm_l2').pack()
# 在frm_r框架中创建一个标签,显示文本'on the frm_r1',并将其放置在frm_r中
tk.Label(frm_r, text='on the frm_r1').pack()
window.mainloop()
输出:
弹窗
messagebox
import tkinter as tk
from tkinter import messagebox
window = tk.Tk()
window.title('my window')
window.geometry('200x200')
def hit_me():
# tk.messagebox.showinfo(title='Hi', message='hahaha')
# 这行代码如果取消注释,会弹出一个信息提示框,标题为'Hi',内容为'hahaha'
# tk.messagebox.showwarning(title='Hi', message='nonono')
# 这行代码如果取消注释,会弹出一个警告提示框,标题为'Hi',内容为'nonono'
# tk.messagebox.showerror(title='Hi', message='No!! never')
# 这行代码如果取消注释,会弹出一个错误提示框,标题为'Hi',内容为'No!! never'
# print(tk.messagebox.askquestion(title='Hi', message='hahaha'))
# 这行代码如果取消注释,会弹出一个询问框,标题为'Hi',内容为'hahaha',并且会在控制台打印出'yes'或'no'
print(tk.messagebox.askyesno(title='Hi', message='hahaha'))
# 这行代码会弹出一个询问是否的对话框,标题为'Hi',内容为'hahaha',并且会在控制台打印出True或False
tk.Button(window, text='hit me', command=hit_me).pack()
window.mainloop()
输出:
顶层窗口
那么除了弹窗会弹出新窗口外,我们自己也可以写窗口里创建新的窗口,利用的方法是tk.Toplevel()方法,具体如何运用请见:
示例里的登录窗口中:点击注册按钮的弹窗
放置位置
pack grid place
# 法一
import tkinter as tk
window = tk.Tk()
window.title('my window')
window.geometry('200x200')
tk.Label(window, text='1').pack(side='top')
tk.Label(window, text='1').pack(side='bottom')
tk.Label(window, text='1').pack(side='left')
tk.Label(window, text='1').pack(side='right')
window.mainloop()
输出:
import tkinter as tk
window = tk.Tk()
window.title('my window')
window.geometry('200x200')
for i in range(4):
for j in range(3):
tk.Label(window, text=1).grid(row=i, column=j, ipadx=10, ipady=10)
# 创建了一个 3x3 的网格,每个标签内部有 10 像素的水平和垂直填充。
window.mainloop()
输出:
import tkinter as tk
window = tk.Tk()
window.title('my window')
window.geometry('200x200')
tk.Label(window, text=1).place(x=10,y=100, anchor='nw')
# x=10表示标签的x坐标为10,y=100表示标签的y坐标为100
# anchor='nw'表示标签的左上角(northwest)作为锚点,用于定位
window.mainloop()
输出:
示例
登录窗口
import tkinter as tk
from PIL import Image, ImageTk
import pickle
from tkinter import messagebox
window = tk.Tk()
window.title('Welcome to my Python')
window.geometry('450x300')
# welcome image
canvas = tk.Canvas(window, height=200, width=300)
# 使用Pillow库加载图像
image_file = Image.open(r"D:\pythonProject\image\welcome.png")
image_file = ImageTk.PhotoImage(image_file)
image = canvas.create_image(0, 0, anchor='nw', image=image_file)
# 在画布的左上角(northwest)创建一个图像,使用之前加载的图像文件
canvas.pack(side='top')
# user information
tk.Label(window, text='User name: ').place(x=50, y=150)
# 在窗口中创建一个标签,显示文本'User name: ',并将其放置在坐标(50, 150)处
tk.Label(window, text='Password: ').place(x=50, y=190)
var_usr_name = tk.StringVar() # 用于存储用户名
var_usr_name.set('example@python.com') # 设置字符串变量的初始值为'example@python.com'
entry_usr_name = tk.Entry(window, textvariable=var_usr_name)
# 创建一个输入框(Entry),属于window窗口,并且将其文本变量设置为之前创建的var_usr_name
entry_usr_name.place(x=160, y=150)
var_usr_pwd = tk.StringVar() # 创建一个字符串变量,用于存储用户密码
entry_usr_pwd = tk.Entry(window, textvariable=var_usr_pwd, show='*')
# 创建一个输入框(Entry),属于window窗口,并且将其文本变量设置为之前创建的var_usr_pwd,show='*'表示输入的字符将以星号显示,用于密码输入框
entry_usr_pwd.place(x=160, y=190)
# 定义用户登录函数
def usr_login():
# 获取用户输入的用户名和密码
usr_name = var_usr_name.get()
usr_pwd = var_usr_pwd.get()
try:
# 尝试以二进制读取模式打开用户信息文件
with open('usrs_info.pickle', 'rb') as usr_file:
usrs_info = pickle.load(usr_file)
except FileNotFoundError:
# 如果文件不存在,则创建一个默认的用户信息文件
with open('usrs_info.pickle', 'wb') as usr_file:
usrs_info = {'admin': 'admin'}
pickle.dump(usrs_info, usr_file)
# 检查用户名是否存在于用户信息中
if usr_name in usrs_info:
# 如果用户名存在,检查密码是否正确
if usr_pwd == usrs_info[usr_name]:
# 如果密码正确,显示欢迎信息
tk.messagebox.showinfo('Welcome', 'How are you? ' + usr_name)
else:
# 如果密码错误,显示错误信息
tk.messagebox.showerror('Error', 'Your password is wrong, try again.')
else:
# 如果用户名不存在,询问用户是否要注册
is_sign_up = tk.messagebox.askyesno('Welcome', 'You have not sign up yet. Sign up?')
if is_sign_up:
# 如果用户选择注册,调用注册函数
usr_sign_up()
# 定义用户注册函数
def usr_sign_up():
# 定义内部函数,用于处理注册逻辑
def sign_to_Mofan_Python():
# 获取用户输入的新密码、确认密码和新用户名
np = new_pwd.get()
npf = new_pwd_confirm.get()
nn = new_name.get()
with open('usrs_info.pickle', 'rb') as usr_file:
exist_usr_info = pickle.load(usr_file)
# 检查新密码和确认密码是否一致
if np!= npf:
# 如果不一致,显示错误信息
tk.messagebox.showerror('Error', 'Password and confirm password must be the same!')
elif nn in exist_usr_info:
# 如果用户名已存在,显示错误信息
tk.messagebox.showerror('Error', 'The user has already existed!')
else:
# 如果所有条件都满足,将新用户信息添加到用户信息文件中
exist_usr_info[nn] = np
with open('usrs_info.pickle', 'wb') as usr_file:
pickle.dump(exist_usr_info, usr_file)
# 显示注册成功信息,并关闭注册窗口
tk.messagebox.showinfo('Welcome', 'You have successfully signed up!')
window_sign_up.destroy()
# 创建一个新的注册窗口
window_sign_up = tk.Toplevel(window)
window_sign_up.geometry('350x200')
window_sign_up.title('Sign up window')
# 创建用于存储新用户名的变量,并设置默认值
new_name = tk.StringVar()
new_name.set('example@python.com')
tk.Label(window_sign_up, text='User name: ').place(x=10, y=10)
entry_new_name = tk.Entry(window_sign_up, textvariable=new_name)
entry_new_name.place(x=150, y=10)
# 创建用于存储新密码的变量
new_pwd = tk.StringVar()
tk.Label(window_sign_up, text='Password: ').place(x=10, y=50)
entry_usr_pwd = tk.Entry(window_sign_up, textvariable=new_pwd, show='*')
entry_usr_pwd.place(x=150, y=50)
# 创建用于存储确认密码的变量
new_pwd_confirm = tk.StringVar()
tk.Label(window_sign_up, text='Confirm password: ').place(x=10, y=90)
entry_usr_pwd_confirm = tk.Entry(window_sign_up, textvariable=new_pwd_confirm, show='*')
entry_usr_pwd_confirm.place(x=150, y=90)
# 创建注册按钮,点击时调用注册逻辑函数
btn_comfirm_sign_up = tk.Button(window_sign_up, text='Sign up', command=sign_to_Mofan_Python)
btn_comfirm_sign_up.place(x=150, y=130)
# 创建登录按钮,点击时调用登录函数
btn_login = tk.Button(window, text='Login', command=usr_login)
btn_login.place(x=170, y=230)
# 创建注册按钮,点击时调用注册函数
btn_sign_up = tk.Button(window, text='Sign up', command=usr_sign_up)
btn_sign_up.place(x=270, y=230)
# 进入主事件循环,保持窗口显示
window.mainloop()
输出:
补充知识
pickle是 Python 中的一个标准库,用于实现数据的序列化和反序列化操作。
- 序列化(Pickling)
序列化是将对象转换为字节流的过程,这样可以将对象保存到文件中、存储在数据库中或者通过网络传输。
import pickle
data = {'name': 'John', 'age': 30, 'city': 'New York'}
with open('data.pickle', 'wb') as file:
pickle.dump(data, file)
上述代码中,pickle.dump()函数用于将data字典对象序列化并保存到data.pickle文件中。这里使用wb模式,表示以二进制写入模式打开文件。
- 反序列化(Unpickling)
反序列化是将字节流转换回对象的过程,这样可以从文件、数据库或者网络中获取数据并恢复为原始的对象。
import pickle
with open('data.pickle', 'rb') as file:
loaded_data = pickle.load(file)
print(loaded_data)
上述代码中,pickle.load()函数用于从data.pickle文件中读取字节流并反序列化恢复为原始的对象。这里使用rb模式,表示以二进制读取模式打开文件。
设置顶层窗口聚焦:
window.focus_set() # 利用该函数,在跳转的窗口会自动聚焦