图形用户界面
- GUI是图形用户界面的缩写,Python默认的GUI开发模块是tkinter,是基于Tk的,Tk是一个工具包,提供了跨平台的GUI控件。但Tk并不是最新最好的选择,也没有功能特别强大的GUI控件,事实上,开发GUI应用并不是Python最擅长的工作,如果真的需要使用Python开发GUI应用,wxPython、PyQt、PyGTK等模块都是不错的选择。
- 基本上使用tkinter来开发GUI应用需要5个步骤:
- 导入tkinter模块中我们需要的;
- 创建一个顶层窗口对象并用它来承载整个GUI应用;
- 在顶层窗口对象上添加GUI组件;
- 通过代码将这些GUI组件的功能组织起来;
- 进入主事件循环(main loop)
- GUI应用通常是事件驱动式的,之所以要进入主事件循环是要监听鼠标、键盘等各种时间的发生并执行对应的代码对事件进行处理,因为事件会持续地发生,所以需要在主循环中等待事件发生。另外,Tk为控件地摆放提供了三种布局管理器,通过布局管理器可以对控件进行定位,这三种布局管理器分别是:Placer(开发者提供控件地大小和摆放的位置)、Packer(自动将控件填充到合适的位置)、和Grid(基于网格坐标来摆放控件)。
简单实现按钮Button
import tkinter as tk
class APP:
def __init__(self,master):
frame = tk.Frame(master)
frame.pack(side = tk.LEFT,padx=10,pady=10)
self.hi_there = tk.Button(frame,text='打招呼',bg='black',fg='white',command=self.say_hi)
self.hi_there.pack()
def say_hi(self):
print('hi')
root = tk.Tk()
app = APP(root)
root.mainloop()
简单实现标签Label
from tkinter import *
root = Tk()
def callback():
var.set('冲')
frame1 = Frame(root)
frame2 = Frame(root)
# 设置字符变量
var = StringVar()
var.set('每日力扣')
textLabel = Label(frame1,
textvariable=var,
justify=LEFT,
padx=10)
textLabel.pack(side=LEFT)
photo = PhotoImage(file='leetcode.gif')
imgLabel = Label(frame1,image=photo)
imgLabel.pack(side=RIGHT)
theButton = Button(frame2,text='加油',command=callback)
theButton.pack()
frame1.pack(padx=10,pady=10)
frame2.pack(padx=10,pady=10)
root.mainloop()
eg.小窗口
import tkinter
import tkinter.messagebox
def main():
flag = True
# 修改标签上的文字
def change_label_text():
nonlocal flag
flag = not flag
color, msg = ('red', '武汉加油')\
if flag else ('blue', '期待春暖花开')
label.config(text=msg, fg=color)
# 确认退出
def confirm_to_quit():
if tkinter.messagebox.askokcancel('温馨提示', '确定要退出吗?'):
top.quit()
# 创建顶层窗口
top = tkinter.Tk()
# 设置窗口大小
top.geometry('240x160')
# 设置窗口标题
top.title('中国加油')
# 创建标签对象
label = tkinter.Label(top, text='武汉加油', font='Arial -32', fg='red')
label.pack(expand=1)
# 创建一个装按钮的容器
panel = tkinter.Frame(top)
# 创建按钮对象
button1 = tkinter.Button(panel, text='修改', command=change_label_text)
button1.pack(side='left')
button2 = tkinter.Button(panel, text='退出', command=confirm_to_quit)
button2.pack(side='right')
panel.pack(side='bottom')
# 开启主事件循环
tkinter.mainloop()
if __name__ == '__main__':
main()
运行结果:
说明:‘nonlocal’是Python3中的关键字,Python2.x中没有这个关键字。在Python2.x中闭包只能读取外部函数的变量,而不能改变变量值,而定义位全局变量‘global’很难控制,此时引入了‘nonlocal’,只要在闭包内用nonlocal声明变量,就可以让解释器在外层函数中查找变量名了。来自《Python的闭包和nonlocal》。
选择框
多选
from tkinter import *
root = Tk()
beauty = ['西施','貂蝉','王昭君','杨玉环']
v = []
for woman in beauty:
v.append(IntVar())
b = Checkbutton(root,text=woman,variable=v[-1])
b.pack(anchor=W)
mainloop()
单选
from tkinter import *
root = Tk()
v = IntVar()
Radiobutton(root,text='One',variable=v,value=1).pack(anchor=W)
Radiobutton(root,text='Two',variable=v,value=2).pack(anchor=W)
Radiobutton(root,text='Three',variable=v,value=3).pack(anchor=W)
mainloop()
from tkinter import *
root = Tk()
languages =[
('python',1),
('perl',2),
('ruby',3),
('lua',4)
]
v = IntVar()
v.set(1)
for lan,num in languages:
b = Radiobutton(root,text=lan,variable=v,value=num,indicatoron=False)
b.pack(fill=X)
mainloop()
from tkinter import *
root = Tk()
group = LabelFrame(root,text='最好的脚本语言是?',padx=5,pady=5)
group.pack(padx=10,pady=10)
languages =[
('python',1),
('perl',2),
('ruby',3),
('lua',4)
]
v = IntVar()
for lan,num in languages:
b = Radiobutton(group,text=lan,variable=v,value=num)
b.pack(anchor=W)
mainloop()
账号密码界面
from tkinter import *
root = Tk()
Label(root,text='账号:').grid(row=0,column=0)
Label(root,text='密码:').grid(row=1,column=0)
v1 = StringVar()
v2 = StringVar()
e1 = Entry(root,textvariable=v1)
e2 = Entry(root,textvariable=v2,show='*')
e1.grid(row=0,column=1,padx=10,pady=10)
e2.grid(row=1,column=1,padx=10,pady=10)
def show():
print('账号:%s'%e1.get())
print('密码:%s' % e2.get())
Button(root,text='芝麻开门',width=10,command=show).grid(row=3,column=0,sticky=W,padx=10,pady=5)
Button(root,text='退出',width=10,command=root.quit).grid(row=3,column=1,sticky=E,padx=10,pady=5)
mainloop()
内容验证
from tkinter import *
master = Tk()
v = StringVar()
def test1():
if v.get() == 'yes':
print('True')
return True
else:
print(False)
e1.delete(0,END)
return False
def test2():
print('test2')
return True
"""
'''
开启Entry对输入文本验证功能。
1、实现该功能,需要通过设置validate、validatecommand和invalidcommand三个选项。
2、启用验证的开关是validate选项,该选项可以设置以下的值:
focus:当entry组件获得或者失去焦点的时候验证
focusin:当entry组件获得焦点的时候验证
focusout:当entry组件失去焦点的时候验证
key:当输入框被编辑的时候验证
all:当出现上面任何一种情况时候验证
none:关闭验证功能。默认设置为该选项
3、validatecommand选项指定一个验证函数,该函数只能返回True或者False表示验证结果,一般情况下验证函数只需要知道输入框中的内容即可,
可以通过Entry组件的get()方法来获得该字符串。
4、invalidcommand选项指定的函数只有在validatecommand的返回值为False的时候才被调用。
'''
'''
validatecommand选项指定一个验证函数,该函数只能返回True或者False表示验证结果
invalidcommand选项指定的函数只有在validatecommand的返回值为False的时候才被调用。
"""
e1 = Entry(master,textvariable=v,validate='focusout',validatecommand=test1,invalidcommand=test2)
e2 = Entry(master)
e1.pack(padx=10,pady=10)
e2.pack(padx=10,pady=10)
mainloop()
加法计算器
from tkinter import *
master = Tk()
frame = Frame(master)
frame.pack(padx=10,pady=10)
v1 = StringVar()
v2 = StringVar()
v3 = StringVar()
def test(content):
return content.isdigit()
testCMD = master.register(test)
e1 = Entry(frame,width=10,textvariable=v1,validate='key',
validatecommand=(testCMD,'%P')
).grid(row=0,column=0)
Label(frame,text='+').grid(row=0,column=1)
e2 = Entry(frame,width=10,textvariable=v2,validate='key',
validatecommand=(testCMD,'%P')
).grid(row=0,column=2)
Label(frame,text='=').grid(row=0,column=3)
# 默认为 state=NORMAL, 文框状态,分为只读和可写,值为:normal/disabled
e3 = Entry(frame,width=10,textvariable=v3,state='readonly').grid(
row=0,column=4
)
def calc():
result = int(v1.get()) + int(v2.get())
v3.set(str(result))
Button(frame,text="计算结果",command=calc).grid(row=1,column=2,pady=5)
mainloop()
Listbox
Listbox组件根据selectmode选项提供了四种不同的选择模式:SINGLE(单选)、BROWSE(也是单选,但拖动鼠标或者通过方向键可以直接改变选项)、MULTIPLE(多选)和EXTENDED(也是多选,但需要同时按住Shift或者Ctrl或者拖拽鼠标实现)。默认BROWSE。
from tkinter import *
master = Tk()
theLB = Listbox(master,selectmode=SINGLE)
theLB.pack()
for item in ['数学','语文','英语','物理','化学','生物']:
theLB.insert(END,item)
theButton = Button(master,text='删除',command=lambda x=theLB:x.delete(ACTIVE))
theButton.pack()
mainloop()
为了在某个组件上安装垂直滚动条,需要做两件事:
- 设置该组件的yscrollbarcommand选项为Scrollbar组件的set()方法;
- 设置Scrollbar组件的command选项为该组件的yview()方法。
from tkinter import *
master = Tk()
sb = Scrollbar(master)
sb.pack(side=RIGHT,fill=Y)
lb = Listbox(master,yscrollcommand=sb.set)
for i in range(1000):
lb.insert(END,i)
lb.pack(side=LEFT,fill=BOTH)
sb.config(command=lb.yview)
mainloop()
from tkinter import *
master = Tk()
s1 = Scale(master,from_=0,to=42)
s1.pack()
s2 = Scale(master,from_=0,to=200,orient=HORIZONTAL)
s2.pack()
def show():
print(s1.get(),s2.get())
Button(master,text='获取位置',command=show).pack()
mainloop()
精准刻度
from tkinter import *
master = Tk()
Scale(master,from_=0,to=42,tickinterval=5,resolution=5,length=200).pack()
Scale(master,from_=0,to=200,tickinterval=10,orient=HORIZONTAL,
length=600).pack()
mainloop()
text中插入按钮和图片
from tkinter import *
master = Tk()
text=Text(master,width=30,height=5)
text.pack()
text.insert(INSERT,'我是一名保安')
text.insert(END,'爱吃小熊饼干')
def show():
print('点击完成')
b1 = Button(text,text='点击',command=show)
text.window_create(INSERT,window=b1)
mainloop()
from tkinter import *
master = Tk()
text=Text(master,width=100,height=100)
text.pack()
photo = PhotoImage(file='leetcode.gif')
def show():
text.image_create(END,image=photo)
b1 = Button(text,text='点击',command=show)
text.window_create(INSERT,window=b1)
mainloop()
Tags
Tags(标签)通常用于改变Text组件中内容的样式和功能。可以修改文本的字体、尺寸和颜色。另外,Tags还允许将文本、嵌入的组件和图片于键盘和鼠标等事件相关联。除了user-defined tags(用户自定义的Tags),还有一个预定义的特殊Tags:SEL。SEL或者sel用于表示对应的选中内容(如果有的话)。
可以自定义任意数量的Tags,Tags的名字是由普通字符串组成,可以是除了空白字符外的任何字符。另外,任何文本内容都支持多个Tags描述,任何Tags也可以用于描述多个不同的文本内容。为指定文本添加Tags可以使用tag_add()方法
from tkinter import *
master = Tk()
text=Text(master,width=30,height=5)
text.pack()
text.insert(INSERT,'雅俗共赏雅俗共赏雅俗共赏雅俗共赏')
# 行号.列号
text.tag_add('tag1','1.7','1.12','1.14')
text.tag_config('tag1',background='yellow',foreground='red')
mainloop()
使用tag_raise()和tag_lower()来提高和降低某个Tag的优先级。
from tkinter import *
master = Tk()
text=Text(master,width=30,height=5)
text.pack()
# 行号.列号
text.tag_add('tag1','1.7','1.12','1.14')
text.tag_config('tag1',background='yellow',foreground='red')
text.tag_add('tag2','1.7','1.12','1.14')
text.tag_config('tag2',background='blue')
text.tag_lower('tag2')
text.insert(INSERT,'雅俗共赏雅俗共赏雅俗共赏雅俗共赏',('tag2','tag1'))
mainloop()
超链接
from tkinter import *
import webbrowser
root = Tk()
text=Text(root,width=30,height=5)
text.pack()
text.insert(INSERT,'雅俗共赏雅俗共赏雅俗共赏雅俗共赏')
text.tag_add('link','1.7','1.12','1.14')
text.tag_config('link',background='yellow',foreground='red',
underline=True)
def show_arrow_cursor(event):
text.config(cursor='arrow')
def show_xterm_cursor(event):
text.config(cursor='xterm')
def click(event):
webbrowser.open('http://www.baidu.com')
text.tag_bind('link','<Enter>',show_arrow_cursor)
text.tag_bind('link','<Leave>',show_xterm_cursor)
text.tag_bind('link','<Button-1>',click)
mainloop()
检查功能
from tkinter import *
import hashlib
root = Tk()
text=Text(root,width=30,height=5)
text.pack()
text.insert(INSERT,'雅俗共赏雅俗共赏雅俗共赏雅俗共赏')
contents = text.get('1.0',END)
def getSig(contents):
m = hashlib.md5(contents.encode())
return m.digest()
sig = getSig(contents)
def check():
contents = text.get('1.0',END)
if sig!=getSig(contents):
print('内容发生改变')
else:
print('内容未发生改变')
Button(root,text='检查',command=check).pack()
mainloop()
mainloop()
全文搜索
from tkinter import *
import hashlib
root = Tk()
text=Text(root,width=30,height=5)
text.pack()
text.insert(INSERT,'雅俗共赏雅俗共赏雅俗共赏雅俗共赏')
def getIndex(text,index):
return tuple(map(int,str.split(text.index(index),'.')))
start = '1.0'
while True:
pos = text.search('俗',start,stopindex=END)
if not pos:
break
print(getIndex(text,pos))
# 指向下一个字符
start = pos + '+1c'
mainloop()
撤销
from tkinter import *
import hashlib
root = Tk()
"""
# 默认情况下,每一次完整的操作将会放入栈中。但怎么样算是一次完整的操作呢?
# Tkinter 觉得每次焦点切换、用户按下 Enter 键、删除\插入操作的转换等
# 之前的操作算是一次完整的操作,一次的“撤销”操作就会将所有的内容删除。
# 那我们能不能自定义呢?比如我希望插入一个字符就算一次完整的操作,然后
# 点击“撤销”就去掉一个字符。当然可以!
# 做法就是先将autoseparators 选项设置为 False(因为这个选项是让
# Tkinter 在认为一次完整的操作结束后自动插入“分隔符”),然后绑定键盘事
# 件,每次有输入就用 edit_separator() 方法人为地插入一个“分隔符”。
"""
text=Text(root,width=30,height=5,undo=True,autoseparator=False)
text.pack()
text.insert(INSERT,'雅俗共赏雅俗共赏雅俗共赏雅俗共赏')
def callback(event):
text.edit_separator()
text.bind('<Key>',callback)
def show():
text.edit_undo()
Button(root,text='撤销',command=show).pack()
mainloop()