01-13. tkinter与银行系统实战05-06

1 最基本的框架

简单示例

  1. import tkinter as tk
  2. #创建一个主窗口,用于容纳整个GUI程序
  3. win = tk.Tk()
  4. #设置主窗口对象的标题栏
  5. win.title("test")
  6. #设置大小和位置,400x400:窗口大小,200:水平自左侧偏移,20:垂直自上侧偏移
  7. win.geometry("400x400+200+20")
  8. #添加一个Label组件,Label组件是GUI程序中最常用的组件之一
  9. # Label组件可以显示文本、图标或者图片
  10. #在这里让它显示指定文本
  11. theLabel = tk.Label (win, text="我的第一个窗口程序!")
  12. #然后调用Label组件的pack()方法,用于自动调节组件自身的尺寸
  13. theLabel.pack()
  14. #注意,这时候窗口还是不会显示的,除非执行下面这句代码
  15. win.mainloop()

进阶版示例

  1. import tkinter as tk
  2. class App(object):
  3. def __init__(self, win):
  4. # 创建一个框架,然后在里边添加一个Button按钮组件
  5. # 框架一般是在复杂的布局中起到将组件分组的作用
  6. frame = tk.Frame(win)
  7. frame.pack()
  8. # 创建一个按钮组件,fg就是foreground的缩写,设置前景色的意思
  9. self.btn = tk.Button(frame, text="Hello", fg="blue", command=self.say_hi)
  10. self.btn.pack()
  11. def say_hi(self):
  12. print("This is Test Windows!")
  13. # 创建一个toplevel的根窗口,并把它作为参数实例化app对象
  14. win = tk.Tk()
  15. win.geometry("400x400+200+20")
  16. app = App(win)
  17. win.mainloop()

可修改pack()方法的side参数,side参数可以设置为LEFT、RIGHT、TOP和BOTTOM四个方位,默认的设置是side=tkinter.TOP。

如果不想按钮挨着“墙角”,可以通过设置pack()方法的padx和pady参数自定义按钮的偏移位置。

  1. self.btn.pack(side=tk.TOP, padx=10, pady=20)

fg参数设置前景色,bg参数设置背景色。

2 tkinter组件

Label

Label组件是用于在界面上输出描述的标签。

  1. import tkinter as tk
  2. win=tk.Tk()
  3. #创建一个文本Label对象
  4. textLabel=tk.Label(win, text="textTestLabel!")
  5. textLabel.pack(side=tk.LEFT)
  6. # 创建一个图像Label对象
  7. # 用PhotoImage实例化一个图片对象(支持gif格式的图片)
  8. photo=tk.PhotoImage(file="1.gif")
  9. imgLabel=tk.Label(win, image=photo)
  10. imgLabel.pack(side=tk.RIGHT)
  11. win.mainloop()

如果想将文字部分左对齐,并在水平位置与边框留有一定的距离,只需要设置Label的justify和padx选项即可:

  1. textLabel=tk.Label(win, text="textTestLabel!", justify=tk.LEFT, padx=10)

如将图片作为背景,文字显示在图片的上面,只需要设置compound选项即可。

  1. import tkinter as tk
  2. win=tk.Tk()
  3. photo=tk.PhotoImage(file="1.gif")
  4. #win 父窗体
  5. #text 显示的文本内容
  6. #bg 背景色
  7. #fg 字体颜色
  8. #wraplength 指定text文本中多宽进行换行
  9. #justify 设置换行后的对齐方法
  10. #anchor 位置 n北 e东 s南 w西 center居中 ne se sw nw
  11. textLabel=tk.Label(win,
  12. text="textTestLabel!",
  13. justify=tk.LEFT,
  14. image=photo, #设置文本和图像的混合模式
  15. font=("黑体", 20),
  16. fg="white",
  17. bg = "blue",
  18. width = 10,
  19. height = 4,
  20. wraplength = 100,
  21. anchor = "center"
  22. )
  23. textLabel.pack()
  24. win.mainloop()

Button

Button组件用于实现一个按钮,它的绝大多数选项与Label组件是一样的。不过Button组件有一个Label组件实现不了的功能,那就是可以接收用户的信息。

Button组件有一个command选项,用于指定一个函数或方法,当用户单击按钮的时候,Tkinter就会自动地去调用这个函数或方法了。添加一个按钮,在按钮被单击之后Label文本发生改变。想要文本发生改变,只需要设置textvariable选项为Tkinter变量即可。

  1. import tkinter as tk
  2. def callback():
  3. var.set("I dont believe~")
  4. win=tk.Tk()
  5. frm1=tk.Frame(win)
  6. frm2=tk.Frame(win)
  7. var=tk.StringVar()
  8. var.set("ddddsy")
  9. textLabel=tk.Label(win, textvariable=var, justify=tk.LEFT)
  10. textLabel.pack()
  11. btn=tk.Button(frm2, text="Already 18!", command=callback)
  12. btn.pack()
  13. frm1.pack(padx=10, pady=10)
  14. frm2.pack(padx=10, pady=10)
  15. win.mainloop()

Entry

Entry组件就是平时所说的输入框。

  1. import tkinter as tk
  2. win=tk.Tk()
  3. win.geometry("400x400")
  4. #绑定变量
  5. e=tk.Variable()
  6. #show 密文显示 show="*"
  7. ety=tk.Entry(win, textvariable=e)
  8. e.set("skdjfiuqo")
  9. ety.delete(0, tk.END)
  10. ety.insert(0,"test---")
  11. print(e.get())
  12. print(ety.get())
  13. ety.pack()
  14. win.mainloop()

复杂的Entry可以用grid()来布局。

  1. import tkinter as tk
  2. win=tk.Tk()
  3. # Tkinter总共提供了三种布局组件的方法:pack () , gird()和place ()
  4. # grid()方法允许用表格的形式来管理组件的位置
  5. # row选项代表行,column选项代表列
  6. #例如,row=1, column=2表示第二行第三列(0表示第一行)
  7. tk.Label(win, text="作品:").grid (row=0)
  8. tk.Label(win, text="作者:").grid (row=1)
  9. e1 = tk.Entry(win)
  10. e2 = tk.Entry(win)
  11. e1.grid(row=0, column=1, padx=10, pady=5)
  12. e2.grid(row=1, column=1, padx=10, pady=5)
  13. def show():
  14. print ("作品:《%s》" % (e1. get ()))
  15. print ("作者:%s" % (e2.get ()))
  16. e1.delete(0, tk.END)
  17. e2.delete(0, tk.END)
  18. #如果表格大于组件,那么可以使用sticky选项来设置组件的位置
  19. #同样需要使用N, E, S, W以及它们的组合NE, SE, SW, NW来表示方位
  20. tk.Button (win, text="获取信息", width=10, command=show).grid(row=3, column=0, sticky=tk.W, padx=10, pady=5)
  21. tk.Button (win, text="退出", width=10, command=win.quit) .grid(row=3, column=1, sticky=tk.E, padx=10, pady=5)
  22. win.mainloop()

Entry组件还支持验证输入内容的合法性。例如输入框要求输入的是数字,用户输入了字母那就属于“非法”。实现该功能,需要通过设置validate、validatecommand和invalidcommand三个选项。首先启用验证的“开关”是validate选项,该选项可以设置的值如下表所示。

validate选项可以设置的值

含义
‘focus’ 当Entry组件获得或失去焦点的时候验证
‘focusin’ 当Entry组件获得焦点的时候验证
‘focusout* 当Entry组件失去焦点的时候验证
‘key’ 当输入框被编辑的时候验证
‘all’ 当出现上面任何一种情况的时候验证
‘none’ 关闭验证功能,默认设置该选项(即不启用验证)。注意,是字符串的’none’,而非None

其次是为validatecommand选项指定一个验证函数,该函数只能返回True或False表示验证的结果。一般情况下验证函数只需要知道输入框的内容即可,可以通过Entry组件的get()方法获得该字符串。

  1. import tkinter as tk
  2. win=tk.Tk()
  3. win.geometry("100x100")
  4. def test():
  5. if e1.get()!="100":
  6. print("Wrong")
  7. e1.delete(0, tk.END)#不是100就删除清空
  8. return False
  9. else:
  10. print("Right")
  11. return True
  12. v=tk.StringVar()
  13. e1=tk.Entry(win, textvariable=v, validate="focusout", validatecommand=test)
  14. e2=tk.Entry(win)
  15. e1.pack()
  16. e2.pack()
  17. win.mainloop()

invalidcommand选项指定的函数只有在validatecommand的返回值为False的时候才被调用。

  1. e1=tk.Entry(win, textvariable=v, validate="focusout", validatecommand=test, invalidcommand=test2)

Tkinter还为验证函数提供一些额外的选项。

选项 含义
‘%d’ 操作代码:0表示删除操作;1表示插入操作;2表示获得、失去焦点或textvariable 变量的值被修改
‘%i’ 当用户尝试插入或删除操作的时候,该选项表示插入或删除的位置(索引号)。如果是由于获得、失去焦点或textvariable变量的值被修改而调用验证函数,那么该值是-1
‘%P’ 当输入框的值允许改变的时候,该值有效。该值为输入框的最新文本内容
‘%s’ 该值为调用验证函数前输入框的文本内容
‘%S’ 当插入或删除操作触发验证函数的时候,该值有效。该选项表示文本被插入和删除 的内容
‘%v’ 该组件当前的validate选项的值
‘%V’ 调用验证函数的原因。该值是’focusin’、’focusout’、’key’、’forced’(textvariable选项指定的变量值被修改)中的一个
‘%W’ 该组件的名字

validatecommand=(f, s1,s2,……)

其中,f是验证函数名,s1、s2、s3是额外的选项,这些选项会作为参数依次传给f函数。在此之前,需要调用register()方法将验证函数包装起来。

  1. import tkinter as tk
  2. win = tk.Tk()
  3. v = tk.StringVar()
  4. def test (content, reason, name):
  5. if content == "test":
  6. print ("正确!" )
  7. print (content, reason, name)
  8. return True
  9. else :
  10. print ("错误! ")
  11. print (content, reason, name)
  12. return False
  13. testCMD = win.register(test)
  14. e1 = tk.Entry(win, textvariable=v, validate="focusout", validatecommand= (testCMD, '%P', '%v', '%W'))
  15. e2 = tk.Entry(win)
  16. e1.pack(padx=10, pady=10)
  17. e2.pack(padx=10, pady=10)
  18. win.mainloop()

示例:一个简单计算器

  1. import tkinter as tk
  2. win = tk.Tk()
  3. frame = tk.Frame(win)
  4. frame.pack(padx=10, pady=10)
  5. v1 = tk.StringVar()
  6. v2 = tk.StringVar()
  7. v3 = tk.StringVar()
  8. def test(content):
  9. #注意,这里不能使用e1.get ()或者v1.get ()来获取输入的内容
  10. #因为validate选项指定为"key”的时候,有任何输入操作都会被拦截到这个函数中
  11. #也就是说先拦截,只有这个函数返回True,那么输入的内容才会到变量里边
  12. #所以要使用%P来获取最新的输入框内容
  13. if content.isdigit ():
  14. return True
  15. else :
  16. return False
  17. testCMD = win.register(test)
  18. tk.Entry (frame, textvariable=v1, width=10, validate="key", validatecommand=(testCMD, '%P')).grid(row=0, column=0)
  19. tk.Label(frame, text="+").grid(row=0, column=1)
  20. tk.Entry(frame, textvariable=v2, width=10, validate="key", validatecommand=(testCMD, '%P')).grid(row=0, column=2)
  21. tk.Label(frame, text="=").grid(row=0, column=3)
  22. tk.Entry(frame, textvariable=v3, width=10, validate="key", validatecommand=(testCMD, '%P')).grid(row=0, column=4)
  23. def calc():
  24. result = int (v1.get()) + int(v2.get())
  25. v3.set (result)
  26. tk.Button(frame, text="结果", command=calc).grid (row=1, column=2, pady=5)
  27. win.mainloop ()

Checkbutton

Checkbutton组件就是常见的多选按钮,而Radiobutton则是单选按钮。

  1. import tkinter as tk
  2. win = tk.Tk ()
  3. #需要一个Tkinter变量,用于表示该按钮是否被选中
  4. v = tk.IntVar()
  5. ckb = tk.Checkbutton(win, text="测试—下", variable=v)
  6. ckb.pack()
  7. #如果选项被选中,那么变量v被赋值为1,否则为0
  8. #可以用一个Label标签动态地给大家展示
  9. label = tk.Label(win, textvariable=v)
  10. label.pack()
  11. win.mainloop()
  1. import tkinter
  2. win = tkinter.Tk()
  3. win.title("sunck")
  4. win.geometry("400x400+200+20")
  5. def updata():
  6. message = ""
  7. if hobby1.get() == True:
  8. message += "money\n"
  9. if hobby2.get() == True:
  10. message += "power\n"
  11. if hobby3.get() == True:
  12. message += "people\n"
  13. #清除text中的所有内容
  14. text.delete(0.0, tkinter.END)
  15. text.insert(tkinter.INSERT, message)
  16. #要绑定的变量
  17. hobby1 = tkinter.BooleanVar()
  18. #多选框
  19. check1 = tkinter.Checkbutton(win,text="money", variable=hobby1, command=updata)
  20. check1.pack()
  21. hobby2 = tkinter.BooleanVar()
  22. check2 = tkinter.Checkbutton(win,text="power", variable=hobby2, command=updata)
  23. check2.pack()
  24. hobby3 = tkinter.BooleanVar()
  25. check3 = tkinter.Checkbutton(win,text="people", variable=hobby3, command=updata)
  26. check3.pack()
  27. text = tkinter.Text(win, width=50, height=5)
  28. text.pack()
  29. win.mainloop()

Radiobutton

Radiobutton组件与Checkbutton组件的用法基本一致,唯一不同的是Radiobutton实现的是“单选”的效果。要实现这种互斥的效果,同一组内的所有Radiobutton只能共享一个variable选项,并且需要设置不同的value选项值。

  1. import tkinter as tk
  2. win=tk.Tk()
  3. var=tk.IntVar()
  4. tk.Radiobutton(win, text="one", variable=var, value=1).pack(anchor=tk.W)
  5. tk.Radiobutton(win, text="two", variable=var, value=2).pack(anchor=tk.W)
  6. tk.Radiobutton(win, text="three", variable=var, value=3).pack(anchor=tk.W)
  7. win.mainloop()

如果你不喜欢前面这个小圆圈,还可以改成按钮的样式(添加indicatoron和fill参数):

  1. tk.Radiobutton(win, text="one", variable=var, value=1, indicatoron=False).pack(fill=tk.X)

LabelFrame

LabelFrame组件是Frame框架的进化版,从样式上来看,也就是添加了Label的Frame,但有了它,Checkbutton和Radiobutton的组件分组就变得简单了。

  1. import tkinter as tk
  2. win=tk.Tk()
  3. group = tk.LabelFrame (win, text="最好的脚本语言是? ", padx=5, pady=5)
  4. group.pack(padx=10, pady=10)
  5. LANGS =[
  6. ("Python", 1),
  7. ("Perl", 2),
  8. ("Ruby", 3),
  9. ("Lua", 4)]
  10. var = tk.IntVar()
  11. var.set(1)
  12. for lang, num in LANGS:
  13. b = tk.Radiobutton(group, text=lang, variable=var, value=num)
  14. b.pack(anchor=tk.W)
  15. win.mainloop()
  1. ![图片](data:image/png;base64,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f3kiX0LeVd5T3kvkapi/UvmK5W3ynnKWLylneVm+ErnKX6G8q3wF8u7yXK6ePn3q/v7eC3lXucpXIMyDXOU95K9M/nLM2+VLyVneVbh3Vu59OfnS8t+aPMjLPv7xr/P13/Jd/tJPf97b3bmam1z8mf/qB33v7/mHvfbaa6+9l9/09/9vfOMvnHdz8lxee+211/7bcOds8ihkXnvttdf+Sp1cDZObee2111778oryDicXIxfDvPbaa699JYahvOLE5Gyu8tprr732FRhtLoZCru6MnUYk89prr732ldnGTiZztrk4DXMzM2fz2muvvfZlDRvGMDd35oWhee211177SiTmbBrm6o7ZTm5izGuvvfbalzdn5WKGubjzYEOT5LX/fznu/4I/+I//Ht/7I5/0/2sPPgDrrOs9Dn++b97sne6d0rSldNBBBxRqa2UJXq3KxosoKAqiImq1il70gooKqIjsigwF3CLKBoXK6LQN0IZOutKRNvskOed33/fkJCerbYqlqdf/87Ql9ebMz36R86cPxbYv54HXUrlg9khKn/gFP737CXaRNPcLC7l4irHq979ly7Hv4z3FOfiq5Lkf3U3Whz/B1IFZgLHt9b9yy//8nDeHnMo3P/duqta/Tk7JLLY/uoBbH9/C2PddwafnzaQok7ho3R7++cwDVB99EScelU2Lum0v85MHavjIB7O445Y7KH2rhrb6jJrKZz57FSOLSKjkmR98mbeO/zoXnjAQ0dYabr/i+zyzq5L2xvGp736Kk4YWIZwjleQhiTgD0cynLRkChHAOD7MqdjeO4OKrL6IXSW8tepLdkUqiTbVs+OcrrN0ylK3biuh/3DyuGXc6UTN2vf4U3/7lGo4b5RHZUcofV+/l9NE72b07Rn7TCu7blMuV1TvYsiWDnKIiqhtiDJo8C29zE3u3rGd16T8pyBiHqtM55fyz2VNbz969FdRX1BFqqtnJGyteYk/quzkqI49QSno2hfmj+eDxf+DRF4r58MeuYFfpr/n1GxO48NSjANFr0HDq//Fd3nvz40SaCMSordhB06/PY2GmT1zhMM797De59KQ9VGztx4fnX0qxJ5rV8tI9f6O+MYIBImRUrH+ZG745n39syWLAtAv5wTXn0D/Nw+k5AoSIk9HCJyBaCMM5vIyaLcv49X3rySKpctNOxo86ndpdS7nnJ39kZXYB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)

Listbox

使用insert()方法添加文本,该方法有两个参数:第一个参数是插入的索引号,第二个参数是插入的字符串。索引号通常是项目的序号(第一项的序号是0)。

  1. import tkinter as tk
  2. win=tk.Tk()
  3. listBox=tk.Listbox(win, setgrid=True)
  4. listBox.pack()
  5. for item in ['a', 'b', 'c']:
  6. listBox.insert(tk.END, item)
  7. tk.Button(win, text="delete", command=lambda x=listBox:x.delete(tk.ACTIVE)).pack()
  8. win.mainloop()

使用delete()方法删除列表中的项目,最常用的操作是删除列表中的所有项目:

  1. listBox.delete(0, tk.END)

当然也可以删除指定的项目,下面添加一个独立按钮来删除ACTIVE状态的项目。

Listbox组件根据selectmode选项提供了四种不同的选择模式(默认的选择模式是BROWSE):

  • SINGLE(单选);
  • BROWSE(也是单选,但拖动鼠标或通过方向键可以直接改变选项);
  • MULTIPLE(多选);
  • EXTENDED(也是多选,但需要配合Shift/Ctrl键来实现,也可以通过拖动光标进行多选)。

Scrollbar

虽然滚动条是作为一个独立的组件存在,不过平时它几乎都是与其他组件配合使用。下面演示如何使用垂直滚动条。为了在某个组件上安装垂直滚动条,需要做两件事:(1)设置该组件的yscrollbarcommand选项为Scrollbar组件的set()方法。(2)设置Scrollbar组件的command选项为该组件的yview()方法。

  1. import tkinter as tk
  2. win=tk.Tk()
  3. sBar=tk.Scrollbar(win)
  4. sBar.pack(side=tk.RIGHT, fill=tk.Y)
  5. label=tk.Listbox(win, yscrollcommand=sBar.set)
  6. for i in range(100):
  7. label.insert(tk.END, i)
  8. label.pack(side=tk.LEFT, fill=tk.BOTH)
  9. sBar.config(command=label.yview)
  10. win.mainloop()

Scale

Scale组件主要通过滑块来表示某个范围内的一个数字,可以通过修改选项设置范围以及分辨率(精度)。当希望用户输入某个范围内的一个数值时,使用Scale组件可以很好地代替Entry组件。

创建一个指定范围的Scale组件,只需要指定它的from和to两个选项即可。但由于from本身是Python的关键字,所以为了区分需要在后面紧跟一个下画线,如from_。orient=tk.HORIZONTAL/tk.VERTICAL代表该组件是横向/竖向的,如果不写,默认是竖向的。

使用get()方法可以获取当前滑块的位置:通过resolution选项控制步长,通过tickinterval选项设置刻度。刻度应该间距比步长大,否则刻度容易不起作用。

  1. import tkinter as tk
  2. win=tk.Tk()
  3. tk.Scale(win, from_=0, to=100, tickinterval=20, resolution=5, length=600).pack()
  4. sc=tk.Scale(win, from_=0, to=200, orient=tk.HORIZONTAL)
  5. sc.pack()
  6. def show1():
  7. print('横条的值是:%d'%(sc.get()))
  8. tk.Button(win, text="获取值", command=show1).pack()
  9. win.mainloop()

Text

文本组件用于显示和处理多行文本。在Tkinter的所有组件中,Text组件显得异常强大和灵活,它适用于处理多种任务。虽然该组件的主要目的是显示多行文本,但它常常也被作为简单的文本编辑器和网页浏览器使用。当创建一个Text组件的时候,它里面是没有内容的。为了给其插入内容,可以使用insert()方法以及INSERT或END索引号。

Text组件不仅支持插入和编辑文本,还支持插入image对象和window组件。

  1. import tkinter as tk
  2. win = tk.Tk()
  3. txt = tk.Text(win, width=50, height=3)
  4. txt.pack()
  5. txt.insert(tk.INSERT, "I love \n")
  6. txt.insert(tk.END, "you!")
  7. photo=tk.PhotoImage(file="test.gif")
  8. def insertImage():
  9. txt.image_create(tk.END, image=photo)
  10. btn = tk.Button(win, text="click",command=insertImage);
  11. txt.window_create(tk.INSERT, window=btn)
  12. win.mainloop()

Indexes用法

Indexes(索引)用来指向Text组件中文本的位置,与Python的序列索引一样,Text组件索引也对应实际字符之间的位置。Tkinter提供了一系列不同的索引类型:

  • “line.column”(行/列)
  • “line.end”(某一行的末尾)
  • INSERT
  • CURRENT
  • END
  • user-defined marks
  • user-defined tags(”tag.first”,”tag.last”)
  • selection(SEL_FIRST,SEL_LAST)
  • window coordinate(”@x,y”)
  • embedded object name(window,images)
  • expressions
    1)”line.column”:用行号和列号组成的字符串是常用的索引方式,它们将索引位置的行号和列号以字符串的形式表示出来(中间以“.”分隔,例如”1.0”)。需要注意的是,行号以1开始,列号则以0开始。还可以使用以下语法构建索引:
  1. “%d.%d”%(line, column)

指定超出现有文本的最后一行的行号,或超出一行中列数的列号都不会引发错误。对于这样的指定,Tkinter解释为已有内容的末尾的下一个位置。需要注意的是,使用“行/列”的索引方式看起来像是浮点值。其实不只是像而已,在需要指定索引的时候使用浮点值代替也是可以的:

  1. text.insert(tkinter.INSERT, I love you!”)
  2. print(text.get(“1.2”, 1.6))

2)”line.end”行号加上字符串”.end”的格式表示为该行最后一个字符的位置:

  1. text.insert(tkinter.INSERT, I love you!”)
  2. print(text.get(“1.2”, 1.end”))

3)INSERT(或”insert”)对应插入光标的位置。
4)CURRENT(或”current”)对应与鼠标坐标最接近的位置。不过,如果长按任何一个按钮,那么直到松开它时才会响应。

5)END(或”end”)对应Text组件的文本缓冲区最后一个字符的下一个位置。

6)user-defined marks是对Text组件中位置的命名。INSERT和CURRENT是两个预先命名好的Marks,除此之外还可以自定义Marks。

7)User-defined tagsUser-defined tags代表可以分配给Text组件的特殊事件绑定和风格。

可以使用”tag.first”(使用tag的文本的第一个字符之前)和”tag.last”(使用tag的文本的最后一个字符之后)语法表示标签的范围:

  1. “%s.first % tagname
  2. “%s.last % tagname

8)selection(SEL_FIRST,SEL_LAST)
selection是一个名为SEL(或”sel”)的特殊tag,表示当前被选中的范围,可以使用SEL_FIRST到SEL_LAST来表示这个范围。如果没有选中的内容,那么Tkinter会抛出一个TclError异常。

9)window coordinate(”@x,y”)可以使用窗口坐标作为索引。例如在一个事件绑定中,可以使用以下代码找到最接近鼠标位置的字符:

  1. “@%d, %d % (event.x, event.y)

10)embedded object name(window,images)
embedded object name用于指向在Text组件中嵌入的window和image对象。要引用一个window,只要简单地将一个Tkinter组件实例作为索引即可。引用一个嵌入的image,只需使用相应的PhotoImage和BitmapImage对象。

11)expressions

expressions用于修改任何格式的索引,用字符串的形式实现修改索引的表达式。具体表达式实现如下表所示。

表达式 含 义
”+ count chars” 将索引向前(→)移动count个字符。可以越过换行符,但不能超过END的位置
”- count chars” 将索引向后(←)移动count个字符。可以越过换行符,但不能超过”1.0”的位置
“+ count lines” 将索引向前(→)移动count行。索引会尽量保持与移动前在同一列上,但如果移 动后的那一行字符太少,将移动到该行的末尾
”- count lines” 将索引向后(←)移动count行。索引会尽量保持与移动前在同一列上,但如果移 动后的那一行字符太少,将移动到该行的末尾
“linestart” 将索引移动到当前索引所在行的起始位置。注意:使用该表达式前面必须有一个空格隔开
”lineend” 将索引移动到当前索引所在行的末尾。注意:使用该表达式前面必须有一个空格隔开
“wordstart” 将索引移动到当前索引指向的单词的开头。单词的定义是一系列字母、数字、下画线或任何非空白字符的组合。注意:使用该表达式前面必须有一个空格隔开
”wordend” 将索引移动到当前索引指向的单词的末尾。单词的定义是一系列字母、数字、下画线或任何非空白字符的组合。注意:使用该表达式前面必须有一个空格隔开

提示:只要结果不产生歧义,关键字可以被缩写,空格也可以省略。例如,”+ 5 chars”可以简写成”+5c”。在实现中,为了确保表达式为普通字符串,可以使用str或格式化操作来创建一个表达式字符串。下面例子演示了如何删除插入光标前面的一个字符:

  1. def backspace(event):
  2. event.widget.delete("%s-1c" % tk.INSERT, tk.INSERT)

Mark用法

Mark(标记)通常是嵌入到Text组件文本中的不可见对象。事实上Mark指定字符间的位置,并跟着相应的字符一起移动。

Mark有INSERT、CURRENT和user-defined mark(用户自定义的Mark)。其中,INSERT和CURRENT是Tkinter预定义的特殊Mark,它们不能够被删除。INSERT(或”insert”)用于指定当前插入光标的位置,Tkinter会在该位置绘制一个闪烁的光标(因此并不是所有的Mark都不可见)。

CURRENT(或”current”)用于指定与鼠标坐标最接近的位置。不过,如果长按任何一个按钮,那么直到松开它时才会响应。还可以自定义任意数量的Mark,Mark的名字由普通字符串组成,可以是除了空白字符外的任何字符(为了避免歧义,应该起一个有意义的名字)。使用mark_set()方法创建和移动Mark。如果在一个Mark标记的位置之前插入或删除文本,那么Mark跟着一起移动。删除Mark需要使用mark_unset()方法,删除Mark周围的文本并不会删除Mark本身。

【例17-1】Mark事实上就是索引,用于表示位置:

  1. import tkinter as tk
  2. win = tk.Tk()
  3. txt = tk.Text(win, width=50, height=3)
  4. txt.pack()
  5. txt.insert(tk.INSERT, "I love \n")
  6. txt.insert(tk.END, "you!")
  7. txt.mark_set("k", 1.3) #将第一行第3个字符后面设置标记,名称为"k"
  8. txt.insert("k", "test")
  9. win.mainloop()

默认插入内容到Mark,是插入到它的左侧(就是说插入一个字符的话,Mark向后移动了一个字符的位置)。通过mark_gravity()方法就可以插入到Mark的右侧。

  1. import tkinter as tk
  2. win = tk.Tk()
  3. txt = tk.Text(win, width=50, height=3)
  4. txt.pack()
  5. txt.insert(tk.INSERT, "I love \n")
  6. txt.insert(tk.END, "you!")
  7. txt.mark_set("k", 1.3)
  8. txt.mark_gravity("k", tk.LEFT) #不写这句默认为RIGHT,即插入到右侧
  9. txt.insert("k", "test")
  10. win.mainloop()

Tag用法

Tag(标签)通常用于改变Text组件中内容的样式和功能,可以用来修改文本的字体、尺寸和颜色。另外,Tag还允许将文本、嵌入的组件和图片与键盘和鼠标等事件相关联。除了user-defined tags(用户自定义的Tag),还有一个预定义的特殊Tag:SEL。SEL(或”sel”)用于表示对应的选中内容(如果有的话)。可以自定义任意数量的Tag,Tag的名字由普通字符串组成,可以是除了空白字符外的任何字符。另外,任何文本内容都支持多个Tag描述,任何Tag也可以用于描述多个不同的文本内容。为指定文本添加Tag可以使用tag_add()方法:

  1. import tkinter as tk
  2. win = tk.Tk()
  3. txt = tk.Text(win, width=50, height=3)
  4. txt.pack()
  5. txt.insert(tk.INSERT, "test test test!")
  6. txt.tag_add("tag1", 1.2, "1.4", "1.6")#标记[1.2, 1.4)及1.6,即每2个组成一个区间
  7. txt.tag_configure("tag1", background="yellow", foreground="red")
  8. win.mainloop()
选 项 含 义
background 指定该Tag所描述的内容的背景颜色.
注意:bg并不是该选项的缩写。在这里bg被解释为bgstipple选项的缩写
bgstipple 指定一个位图作为背景,并使用background选项指定的颜色填充。只有设置了 background选项该选项才会生效。
默认的标准位图有”error”、”gray75”、”gray50”、”gray25”、”gray12”、”hourglass”、”info”、”questhead”、”question1”和”warning”。
borderwidth 指定文本机的宽度,默认值是0。只有设置了relief选项,该选项才会生效。
注意:该选项不能使用bd缩写。
fgstipple 指定一个位图作为前景色.欺认的标准位图有”error”、”gray75”、”gray50”、”gray25”、”gray12”、”hourglass”、”info”、”questhead”、”question1”和”warning”。
font 指定该Tag所描述的内容使用的字体
foreground 指定该Tag所描述的内容的前景色.
注意:fg并不是该选项的缩写,在这里fg被解释为fgstipple选项的缩写
justify 控制文本的对齐方式。默认是LEFT(左对齐),还可以选择RIGHT(右对齐) 和CENTER(居中).
注意:需要将Tag指向该行的第一个字符,该选项才能生效
Imargin1 设置Tag指向的文本块第一行的缩进,默认值是0。
注意:需要将Tag指向该文本块的第一个字符或整个文本块,该选项才能生效。
Imargin2 设置Tag指向的文本块除了第一行外其他行的缩进,默认值是0。
注意:需要将Tag指向整个文本块,该选项才能生效
offset 设置Tag指向的文本相对于基线的偏移距离,可以控制文本相对于基线是升高(正数值)或者降低(负数值),默认值时0。
overstrike 在Tag指定的文本范围画一条删除线,默认为False
relief 指定Tag对应范围的文本的边框样式,可以使用的(SUNKEN、RAISED、GROOVE、RIDGE或FLAT。默认值是FLAT(没有边框)
rmargin 设置Tag指向的文本块右侧的缩进,默认值是0
Spacing1 设置Tag所描述的文本块中每一行与上方的空白间隔,默认值是0。
注意:自动换行不算
Spacing2 设置Tag所描述的文本块中自动换行的各行间的空白间隔,默认值是0。
注意:换行符(^)不算
spacing3 设置Tag所描述的文本块中每一行与下方的空白间隔,默认值是0.
注意:自动换行不算
tabs 定制Tag所描述的文本块中Tab按键的功能,默认Tab被定义为8个字符的宽度,还可以定义多个制表位:
Tabs=(‘3c’, ‘5c’, ‘12c’)表示前 3 个 Tab 宽度分别为 3cm,5cm,12cm,接着的Tab 按照最后两个的差值计算,即19cm,26cm,33cm。
应该注意到了,’c’的含义是“厘米”而不是“字符”,还可以选择的单位有’i’(英寸)’m’(毫米)和 ‘p’(DPI).
如果是一个整型值,则单位是像素
underline 该选项设置为True的话,则Tag所描述的范围内文本将被画上下画线,默认为False
wrap 设置当一行文本的长度超过width选项设置的宽度时,是否自动换行。该选项的值可以是NONE (不自动换行)、CHAR (按字符自动换行)和WORD (按单词自动换行)

如果对同一个范围内的文本加上多个Tag,并且设置相同的选项,那么新创建的Tag样式会覆盖比较旧的Tag:

  1. txt. tag_config('tag2', foreground="blue") # 新的 Tag
  2. #那么新创建的Tag2会覆盖比较旧的Tag1的相同选项
  3. #注意,与下面的调用顺序没有关系
  4. txt.insert(tk.INSERT, "I love you", ('tag2', 'tag1'))

可以使用tag_raise()和tag_lower()方法来提高或降低某个Tag的优先级。
Tag还支持事件绑定,绑定事件使用的是tag_bind()方法。下面例子将文本(”FishC.com”)与鼠标事件进行绑定,当鼠标进入该文本段的时候,鼠标样式切换为”arrow”形态,离开文本段的时候切换回”xterm”形态。当触发鼠标“左键单击操作”事件的时候,使用默认浏览器打开鱼C工作室的首页(www.fishc.com)。

  1. import tkinter as tk
  2. import webbrowser
  3. win = tk.Tk()
  4. txt = tk.Text(win, width=50, height=3)
  5. txt.pack()
  6. txt.insert(tk.INSERT, 'I love FishC.com!')
  7. txt.tag_add('link', 1.7, '1.16')
  8. txt.tag_config("link", foreground="blue", underline=True)
  9. def show_hand_cursor(event):
  10. txt.config(cursor="arrow")
  11. def show_arrow_cursor(event):
  12. txt.config(cursor="xterm")
  13. def click(event):
  14. webbrowser.open("http://www.fishc.com")
  15. txt .tag_bind("link", "<Enter>", show_hand_cursor)
  16. txt .tag_bind("link", "<Leave>", show_arrow_cursor)
  17. txt .tag_bind("link", "<Button-1>", click)
  18. win.mainloop()

例题1:检查text文本框内容是否修改

  1. import tkinter as tk
  2. import hashlib
  3. win = tk.Tk()
  4. txt = tk.Text(win, width=50, height=3)
  5. txt.pack()
  6. txt.insert(tk.INSERT, "I love python!")
  7. contents=txt.get(1.0, tk.END)
  8. def getSig(contents):
  9. hash=hashlib.md5(contents.encode())
  10. return hash.digest()
  11. sig=getSig(contents)
  12. def check():
  13. contents=txt.get(1.0, tk.END)
  14. if sig!=getSig(contents):
  15. print("内容修改啦")
  16. else:
  17. print("Same")
  18. tk.Button(win, text="检查", command=check).pack()
  19. win.mainloop()

例题2:查找操作

使用search()方法可以搜索Text组件中的内容。可以提供一个确切的目标进行搜索(默认),也可以使用Tcl格式的正则表达式进行搜索(需设置regexp选项为True):

  1. import tkinter as tk
  2. win = tk.Tk()
  3. txt = tk.Text(win, width=50, height=3)
  4. txt.pack()
  5. txt.insert(tk.INSERT, "I love python!")
  6. #将索引以元组方式输出
  7. def getIndex(text, index):
  8. return tuple(map(int, str.split(text.index(index), ".")))
  9. start=1.0
  10. while True:
  11. pos=txt.search("o", start, stopindex=tk.END)
  12. if not pos:
  13. break
  14. print("找到啦,位置是:", getIndex(txt, pos))
  15. start=pos + "+1c" #将start指向下一个字符
  16. win.mainloop()

如果忽略stopindex选项,表示直到文本的末尾结束搜索。设置backwards选项为True,则是修改搜索的方向(变为向后搜索,那么start变量应该设置为END,stopindex选项设置为1.0,最后”+1c”改为”-1c”)。
例题3:“恢复”和“撤销”操作

通过设置undo选项为True,可以开启Text组件的“撤销”功能,然后用edit_undo()方法实现“撤销”操作,用edit_redo()方法实现“恢复”操作。

  1. import tkinter as tk
  2. import hashlib
  3. win = tk.Tk()
  4. txt = tk.Text(win, width=50, height=3, undo=True)
  5. txt.pack()
  6. txt.insert(tk.INSERT, "I love python!")
  7. def show():
  8. txt.edit_undo()
  9. tk.Button(win, text="撤销", command=show).pack()
  10. win.mainloop()

这是因为Text组件内部有一个栈专门用于记录内容的每次变动,所以每次“撤销”操作就是一次弹栈操作,“恢复”就是再次压栈。
默认情况下,每一次完整的操作都会放入栈中。但怎么样算是一次完整的操作呢?Tkinter觉得每次焦点切换、用户按下回车键、删除/插入操作的转换等之前的操作算是一次完整的操作。也就是说,连续输入“FishC”的话,一次“撤销”操作就会将所有的内容删除。那能不能自定义呢?例如希望插入一个字符就算一次完整的操作,然后每次单击“撤销”就去掉一个字符。

当然可以!做法就是先将autoseparators选项设置为False(因为这个选项是让Tkinter在认为一次完整的操作结束后自动插入“分隔符”),然后绑定键盘事件,每次有输入就用edit_separator()方法人为地插入一个“分隔符”:

  1. import tkinter as tk
  2. import hashlib
  3. win = tk.Tk()
  4. txt = tk.Text(win, width=50, height=3, autoseparators=False, undo=True, maxundo=10)
  5. txt.pack()
  6. def callback(event):
  7. txt.edit_separator()
  8. txt.bind('<key>', callback)
  9. txt.insert(tk.INSERT, "I love python!")
  10. def show():
  11. txt.edit_undo()
  12. tk.Button(win, text="撤销", command=show).pack()
  13. win.mainloop()

Canvas

Canvas是一个通用的组件,它通常用于显示和编辑图形,可以用它来绘制直线、圆形、多边形,甚至是绘制其他组件。在Canvas组件上绘制对象,可以用create_xxx()方法(xxx表示对象类型,例如直线line、矩形rectangle和文本text等):

  1. import tkinter as tk
  2. win = tk.Tk()
  3. c=tk.Canvas(win, height=200, width=200)
  4. c.pack()
  5. # 画一条黄色的横线
  6. c.create_line(0, 50, 200, 50, fill="yellow")
  7. # 画一条红色的竖线(虚线)
  8. c.create_line(100, 0, 100, 100, fill="red", dash=(4, 4))
  9. # 中间画一个蓝色的矩形
  10. c.create_rectangle(50, 25, 150, 75, fill="blue")
  11. win.mainloop()

注意,添加到Canvas上的对象会一直保留着。如果希望修改它们,可以使用coords()、itemconfig()和move()方法来移动画布上的对象,或者使用delete()方法来删除:

  1. import tkinter as tk
  2. win = tk.Tk()
  3. c=tk.Canvas(win, height=200, width=200)
  4. c.pack()
  5. line1 = c.create_line(0, 50, 200, 50, fill="yellow")
  6. line2 = c.create_line(100, 0, 100, 100, fill="red", dash=(4, 4))
  7. rect1 = c.create_rectangle(50, 25, 150, 75, fill="blue")
  8. c.coords(line1, 0, 25, 200, 25)
  9. c.itemconfig(rect1, fill="red")
  10. c.delete(line2)
  11. tk.Button(win, text="删除全部", command=(lambda x=tk.ALL : c.delete(x))).pack()
  12. win.mainloop()

还可以在Canvas上显示文本,使用的是create_text()方法:

  1. ……
  2. c..create_text(100, 50, text="I love Python!")
  3. ……

使用create_oval()方法绘制椭圆形(或圆形),参数是指定一个限定矩形(Tkinter会自动在这个矩形内绘制一个椭圆):

  1. import tkinter as tk
  2. win = tk.Tk()
  3. w = tk.Canvas(win, width=200, height=100)
  4. w.pack()
  5. w.create_rectangle(40, 20, 160, 80, dash=(4, 4))
  6. w.create_oval(40, 20, 160, 80, fill="pink")
  7. w.create_text(100, 50, text="test")
  8. win.mainloop()

可以通过绘制一个超小的椭圆形来表示一个“点”。在下面的例子中,通过响应“鼠标左键按住拖动”事件(),在鼠标拖动的同时获取鼠标的实时位置(x, y),并绘制一个超小的椭圆来代表一个“点”:

  1. import tkinter as tk
  2. win = tk.Tk()
  3. w = tk.Canvas(win, width=400, height=200)
  4. w.pack()
  5. def paint(event):
  6. x1, y1 = (event.x - 1), (event.y - 1)
  7. x2, y2 = (event.x + 1), (event.y + 1)
  8. w.create_oval(x1, y1, x2, y2, fill="red")
  9. w.bind("<B1-Motion>", paint)
  10. tk.Label(win, text="按住鼠标左键并移动,开始绘制你的理想蓝图吧......").pack(side=tk.BOTTOM)
  11. win.mainloop()

Canvas组件支持的对象

  • arc(弧形、弦或扇形)。
  • bitmap(内建的位图文件或XBM格式的文件)。
  • image(BitmapImage或PhotoImage的实例对象)。
  • line(线)。
  • oval(圆形或椭圆形)。
  • polygon(多边形)。
  • rectangle(矩形)。
  • text(文本)。
  • window(组件)。
    其中,弦、扇形、椭圆形、圆形、多边形和矩形这些“封闭式”图形都是由轮廓线和填充颜色组成的,通过outline和fill选项设置它们的颜色,还可以设置为透明(传入空字符串表示透明)。

坐标系

由于画布可能比窗口大(带有滚动条的Canvas组件),因此Canvas组件可以选择使用两种坐标系。

窗口坐标系:以窗口的左上角作为坐标原点。

画布坐标系:以画布的左上角作为坐标原点。

画布对象显示的顺序

Canvas组件中创建的画布对象都会被列入显示列表中,越接近背景的画布对象,就越是位于显示列表的下方。显示列表决定当两个画布对象重叠的时候是如何覆盖的(默认情况下,新创建的会覆盖旧的画布对象的重叠部分,即位于显示列表上方的画布对象将覆盖下方那个)。当然,显示列表中的画布对象可以被重新排序。

指定画布对象

Canvas组件提供几种方法用来指定画布对象:

  • Item handles。
  • Tags。
  • ALL。
  • CURRENT。
    Item handles事实上是一个用于指定某个画布对象的整型数(也称为画布对象的ID)。当在Canvas组件上创建一个画布对象的时候,Tkinter将自动为其指定一个在该Canvas组件中独一无二的整型值,然后各种Canvas的方法可以通过这个值操纵该画布对象。

Tag是附在画布对象上的标签,Tag由普通的非空白字符串组成。一个画布对象可以与多个Tag相关联,一个Tag也可用于描述多个画布对象。然而,与Text组件不同,没有指定画布对象的Tag不能进行事件绑定和配置样式。也就是说,Canvas组件的Tag仅为画布对象所拥有。

Canvas组件预定义了两个Tags:ALL和CURRENT。

  • ALL(或”all”)表示Canvas组件中的所有画布对象。
  • CURRENT(或”current”)表示鼠标指针下的画布对象(如果有的话)。

Menu

Tkinter提供了一个Menu组件,用于实现顶级菜单、下拉菜单和弹出菜单。由于该组件是底层代码实现和优化,所以不建议自行通过按钮和其他组件来实现菜单功能。

创建一个顶级菜单,需要先创建一个菜单实例,然后使用add()方法将命令和其他子菜单添加进去:

  1. import tkinter as tk
  2. win = tk.Tk()
  3. menubar = tk.Menu(win)
  4. def callback():
  5. print("~~test~~")
  6. menubar.add_command(label="Hello", command=callback)
  7. menubar.add_command(label="Quit", command=win.quit)
  8. win.config(menu=menubar)
  9. win.mainloop()

创建一个下拉菜单(或者其他子菜单),方法大同小异,最主要的区别是它们最后需要添加到主菜单上(而不是窗口上):

  1. import tkinter as tk
  2. win = tk.Tk()
  3. menubar = tk.Menu(win)
  4. def callback():
  5. print("test")
  6. menuFile = tk.Menu(menubar, tearoff=False)
  7. menuFile.add_command(label="open", command=callback)
  8. menuFile.add_command(label="save", command=callback)
  9. menuFile.add_separator()
  10. menuFile.add_command(label="quit", command=win.quit)
  11. menubar.add_cascade(label="file", menu=menuFile)
  12. win.config(menu=menubar)
  13. win.mainloop()

创建一个弹出菜单的方法也是一致的,不过需要使用post()方法明确地将其显示出来:

  1. import tkinter as tk
  2. win = tk.Tk()
  3. def callback():
  4. print("test")
  5. menubar = tk.Menu(win, tearoff=False)
  6. menubar.add_command(label="undo", command=callback)
  7. menubar.add_command(label="redo", command=callback)
  8. frame = tk.Frame(win, width=512, height=512)
  9. frame.pack()
  10. def pop(event):
  11. menubar.post(event.x_root, event.y_root)
  12. frame.bind("<Button-3>", pop)
  13. win.config(menu=menubar)
  14. win.mainloop()

这个菜单不仅可以添加常见的命令菜单项,还可以添加单选按钮或多选按钮,那么用法就与Checkbutton组件和Radiobutton组件类似了。

  1. import tkinter as tk
  2. win = tk.Tk()
  3. def callback():
  4. print("~被调用了~")
  5. # 创建一个顶级菜单
  6. menubar = tk.Menu(win)
  7. # 创建 checkbutton 关联变量
  8. openVar = tk.IntVar()
  9. saveVar = tk.IntVar()
  10. exitVar = tk.IntVar()
  11. # 创建一个下拉菜单“文件”,然后将它添加到顶级菜单中
  12. filemenu = tk.Menu(menubar, tearoff=True)
  13. filemenu.add_checkbutton(label="打开", command=callback, variable=openVar)
  14. filemenu.add_checkbutton(label="保存", command=callback, variable=saveVar)
  15. filemenu.add_separator()
  16. filemenu.add_checkbutton(label="退出", command=win.quit, variable=exitVar)
  17. menubar.add_cascade(label="文件", menu=filemenu)
  18. # 创建 radiobutton 关联变量
  19. editVar = tk.IntVar()
  20. editVar.set(1)
  21. # 创建另一个下拉菜单“编辑”,然后将它添加到顶级菜单中
  22. editmenu = tk.Menu(menubar, tearoff=True)
  23. editmenu.add_radiobutton(label="剪切", command=callback, variable=editVar, value=1)
  24. editmenu.add_radiobutton(label="拷贝", command=callback, variable=editVar, value=2)
  25. editmenu.add_radiobutton(label="粘贴", command=callback, variable=editVar, value=3)
  26. menubar.add_cascade(label="编辑", menu=editmenu)
  27. # 显示菜单
  28. win.config(menu=menubar)
  29. win.mainloop()

OptionMenu

OptionMenu(选项菜单)事实上是下拉菜单的改版,它的发明弥补了Listbox组件无法实现下拉列表框的遗憾。创建一个选择菜单非常简单,只需要一个Tkinter变量(用于记录用户选择了什么)以及若干选项即可:

  1. import tkinter as tk
  2. win=tk.Tk()
  3. option=["one",
  4. "two",
  5. "three",
  6. "four"]
  7. var=tk.StringVar()
  8. var.set(option[0])
  9. om=tk.OptionMenu(win, var, *option)
  10. om.pack()
  11. def callback():
  12. print(var.get())
  13. tk.Button(win, text="click", command=callback).pack()
  14. win.mainloop()

Spinbox

Spinbox组件(Tk8.4新增)是Entry组件的变体,用于从一些固定的值中选取一个。Spinbox组件与Entry组件用法非常相似,主要区别是使用Spinbox组件时,可以通过范围或者元组指定允许用户输入的内容。

  1. import tkinter as tk
  2. win=tk.Tk()
  3. w = tk.Spinbox(win, from_=0, to=10)
  4. w.pack()
  5. v=tk.Spinbox(win, values=("h", "k", "l","a"))
  6. v.pack()
  7. win.mainloop()