01-13. tkinter与银行系统实战05-06
1 最基本的框架
简单示例
import tkinter as tk#创建一个主窗口,用于容纳整个GUI程序win = tk.Tk()#设置主窗口对象的标题栏win.title("test")#设置大小和位置,400x400:窗口大小,200:水平自左侧偏移,20:垂直自上侧偏移win.geometry("400x400+200+20")#添加一个Label组件,Label组件是GUI程序中最常用的组件之一# Label组件可以显示文本、图标或者图片#在这里让它显示指定文本theLabel = tk.Label (win, text="我的第一个窗口程序!")#然后调用Label组件的pack()方法,用于自动调节组件自身的尺寸theLabel.pack()#注意,这时候窗口还是不会显示的,除非执行下面这句代码win.mainloop()
进阶版示例
import tkinter as tkclass App(object):def __init__(self, win):# 创建一个框架,然后在里边添加一个Button按钮组件# 框架一般是在复杂的布局中起到将组件分组的作用frame = tk.Frame(win)frame.pack()# 创建一个按钮组件,fg就是foreground的缩写,设置前景色的意思self.btn = tk.Button(frame, text="Hello", fg="blue", command=self.say_hi)self.btn.pack()def say_hi(self):print("This is Test Windows!")# 创建一个toplevel的根窗口,并把它作为参数实例化app对象win = tk.Tk()win.geometry("400x400+200+20")app = App(win)win.mainloop()
可修改pack()方法的side参数,side参数可以设置为LEFT、RIGHT、TOP和BOTTOM四个方位,默认的设置是side=tkinter.TOP。
如果不想按钮挨着“墙角”,可以通过设置pack()方法的padx和pady参数自定义按钮的偏移位置。
self.btn.pack(side=tk.TOP, padx=10, pady=20)
fg参数设置前景色,bg参数设置背景色。
2 tkinter组件
Label
Label组件是用于在界面上输出描述的标签。
import tkinter as tkwin=tk.Tk()#创建一个文本Label对象textLabel=tk.Label(win, text="textTestLabel!")textLabel.pack(side=tk.LEFT)# 创建一个图像Label对象# 用PhotoImage实例化一个图片对象(支持gif格式的图片)photo=tk.PhotoImage(file="1.gif")imgLabel=tk.Label(win, image=photo)imgLabel.pack(side=tk.RIGHT)win.mainloop()
如果想将文字部分左对齐,并在水平位置与边框留有一定的距离,只需要设置Label的justify和padx选项即可:
textLabel=tk.Label(win, text="textTestLabel!", justify=tk.LEFT, padx=10)
如将图片作为背景,文字显示在图片的上面,只需要设置compound选项即可。
import tkinter as tkwin=tk.Tk()photo=tk.PhotoImage(file="1.gif")#win 父窗体#text 显示的文本内容#bg 背景色#fg 字体颜色#wraplength 指定text文本中多宽进行换行#justify 设置换行后的对齐方法#anchor 位置 n北 e东 s南 w西 center居中 ne se sw nwtextLabel=tk.Label(win,text="textTestLabel!",justify=tk.LEFT,image=photo, #设置文本和图像的混合模式font=("黑体", 20),fg="white",bg = "blue",width = 10,height = 4,wraplength = 100,anchor = "center")textLabel.pack()win.mainloop()
Button
Button组件用于实现一个按钮,它的绝大多数选项与Label组件是一样的。不过Button组件有一个Label组件实现不了的功能,那就是可以接收用户的信息。
Button组件有一个command选项,用于指定一个函数或方法,当用户单击按钮的时候,Tkinter就会自动地去调用这个函数或方法了。添加一个按钮,在按钮被单击之后Label文本发生改变。想要文本发生改变,只需要设置textvariable选项为Tkinter变量即可。
import tkinter as tkdef callback():var.set("I dont believe~")win=tk.Tk()frm1=tk.Frame(win)frm2=tk.Frame(win)var=tk.StringVar()var.set("ddddsy")textLabel=tk.Label(win, textvariable=var, justify=tk.LEFT)textLabel.pack()btn=tk.Button(frm2, text="Already 18!", command=callback)btn.pack()frm1.pack(padx=10, pady=10)frm2.pack(padx=10, pady=10)win.mainloop()
Entry
Entry组件就是平时所说的输入框。
import tkinter as tkwin=tk.Tk()win.geometry("400x400")#绑定变量e=tk.Variable()#show 密文显示 show="*"ety=tk.Entry(win, textvariable=e)e.set("skdjfiuqo")ety.delete(0, tk.END)ety.insert(0,"test---")print(e.get())print(ety.get())ety.pack()win.mainloop()
复杂的Entry可以用grid()来布局。
import tkinter as tkwin=tk.Tk()# Tkinter总共提供了三种布局组件的方法:pack () , gird()和place ()# grid()方法允许用表格的形式来管理组件的位置# row选项代表行,column选项代表列#例如,row=1, column=2表示第二行第三列(0表示第一行)tk.Label(win, text="作品:").grid (row=0)tk.Label(win, text="作者:").grid (row=1)e1 = tk.Entry(win)e2 = tk.Entry(win)e1.grid(row=0, column=1, padx=10, pady=5)e2.grid(row=1, column=1, padx=10, pady=5)def show():print ("作品:《%s》" % (e1. get ()))print ("作者:%s" % (e2.get ()))e1.delete(0, tk.END)e2.delete(0, tk.END)#如果表格大于组件,那么可以使用sticky选项来设置组件的位置#同样需要使用N, E, S, W以及它们的组合NE, SE, SW, NW来表示方位tk.Button (win, text="获取信息", width=10, command=show).grid(row=3, column=0, sticky=tk.W, padx=10, pady=5)tk.Button (win, text="退出", width=10, command=win.quit) .grid(row=3, column=1, sticky=tk.E, padx=10, pady=5)win.mainloop()
Entry组件还支持验证输入内容的合法性。例如输入框要求输入的是数字,用户输入了字母那就属于“非法”。实现该功能,需要通过设置validate、validatecommand和invalidcommand三个选项。首先启用验证的“开关”是validate选项,该选项可以设置的值如下表所示。
validate选项可以设置的值
| 值 | 含义 |
|---|---|
| ‘focus’ | 当Entry组件获得或失去焦点的时候验证 |
| ‘focusin’ | 当Entry组件获得焦点的时候验证 |
| ‘focusout* | 当Entry组件失去焦点的时候验证 |
| ‘key’ | 当输入框被编辑的时候验证 |
| ‘all’ | 当出现上面任何一种情况的时候验证 |
| ‘none’ | 关闭验证功能,默认设置该选项(即不启用验证)。注意,是字符串的’none’,而非None |
其次是为validatecommand选项指定一个验证函数,该函数只能返回True或False表示验证的结果。一般情况下验证函数只需要知道输入框的内容即可,可以通过Entry组件的get()方法获得该字符串。
import tkinter as tkwin=tk.Tk()win.geometry("100x100")def test():if e1.get()!="100":print("Wrong")e1.delete(0, tk.END)#不是100就删除清空return Falseelse:print("Right")return Truev=tk.StringVar()e1=tk.Entry(win, textvariable=v, validate="focusout", validatecommand=test)e2=tk.Entry(win)e1.pack()e2.pack()win.mainloop()
invalidcommand选项指定的函数只有在validatecommand的返回值为False的时候才被调用。
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()方法将验证函数包装起来。
import tkinter as tkwin = tk.Tk()v = tk.StringVar()def test (content, reason, name):if content == "test":print ("正确!" )print (content, reason, name)return Trueelse :print ("错误! ")print (content, reason, name)return FalsetestCMD = win.register(test)e1 = tk.Entry(win, textvariable=v, validate="focusout", validatecommand= (testCMD, '%P', '%v', '%W'))e2 = tk.Entry(win)e1.pack(padx=10, pady=10)e2.pack(padx=10, pady=10)win.mainloop()
示例:一个简单计算器
import tkinter as tkwin = tk.Tk()frame = tk.Frame(win)frame.pack(padx=10, pady=10)v1 = tk.StringVar()v2 = tk.StringVar()v3 = tk.StringVar()def test(content):#注意,这里不能使用e1.get ()或者v1.get ()来获取输入的内容#因为validate选项指定为"key”的时候,有任何输入操作都会被拦截到这个函数中#也就是说先拦截,只有这个函数返回True,那么输入的内容才会到变量里边#所以要使用%P来获取最新的输入框内容if content.isdigit ():return Trueelse :return FalsetestCMD = win.register(test)tk.Entry (frame, textvariable=v1, width=10, validate="key", validatecommand=(testCMD, '%P')).grid(row=0, column=0)tk.Label(frame, text="+").grid(row=0, column=1)tk.Entry(frame, textvariable=v2, width=10, validate="key", validatecommand=(testCMD, '%P')).grid(row=0, column=2)tk.Label(frame, text="=").grid(row=0, column=3)tk.Entry(frame, textvariable=v3, width=10, validate="key", validatecommand=(testCMD, '%P')).grid(row=0, column=4)def calc():result = int (v1.get()) + int(v2.get())v3.set (result)tk.Button(frame, text="结果", command=calc).grid (row=1, column=2, pady=5)win.mainloop ()
Checkbutton
Checkbutton组件就是常见的多选按钮,而Radiobutton则是单选按钮。
import tkinter as tkwin = tk.Tk ()#需要一个Tkinter变量,用于表示该按钮是否被选中v = tk.IntVar()ckb = tk.Checkbutton(win, text="测试—下", variable=v)ckb.pack()#如果选项被选中,那么变量v被赋值为1,否则为0#可以用一个Label标签动态地给大家展示label = tk.Label(win, textvariable=v)label.pack()win.mainloop()
import tkinterwin = tkinter.Tk()win.title("sunck")win.geometry("400x400+200+20")def updata():message = ""if hobby1.get() == True:message += "money\n"if hobby2.get() == True:message += "power\n"if hobby3.get() == True:message += "people\n"#清除text中的所有内容text.delete(0.0, tkinter.END)text.insert(tkinter.INSERT, message)#要绑定的变量hobby1 = tkinter.BooleanVar()#多选框check1 = tkinter.Checkbutton(win,text="money", variable=hobby1, command=updata)check1.pack()hobby2 = tkinter.BooleanVar()check2 = tkinter.Checkbutton(win,text="power", variable=hobby2, command=updata)check2.pack()hobby3 = tkinter.BooleanVar()check3 = tkinter.Checkbutton(win,text="people", variable=hobby3, command=updata)check3.pack()text = tkinter.Text(win, width=50, height=5)text.pack()win.mainloop()
Radiobutton
Radiobutton组件与Checkbutton组件的用法基本一致,唯一不同的是Radiobutton实现的是“单选”的效果。要实现这种互斥的效果,同一组内的所有Radiobutton只能共享一个variable选项,并且需要设置不同的value选项值。
import tkinter as tkwin=tk.Tk()var=tk.IntVar()tk.Radiobutton(win, text="one", variable=var, value=1).pack(anchor=tk.W)tk.Radiobutton(win, text="two", variable=var, value=2).pack(anchor=tk.W)tk.Radiobutton(win, text="three", variable=var, value=3).pack(anchor=tk.W)win.mainloop()
如果你不喜欢前面这个小圆圈,还可以改成按钮的样式(添加indicatoron和fill参数):
tk.Radiobutton(win, text="one", variable=var, value=1, indicatoron=False).pack(fill=tk.X)
LabelFrame
LabelFrame组件是Frame框架的进化版,从样式上来看,也就是添加了Label的Frame,但有了它,Checkbutton和Radiobutton的组件分组就变得简单了。
import tkinter as tkwin=tk.Tk()group = tk.LabelFrame (win, text="最好的脚本语言是? ", padx=5, pady=5)group.pack(padx=10, pady=10)LANGS =[("Python", 1),("Perl", 2),("Ruby", 3),("Lua", 4)]var = tk.IntVar()var.set(1)for lang, num in LANGS:b = tk.Radiobutton(group, text=lang, variable=var, value=num)b.pack(anchor=tk.W)win.mainloop()

Listbox
使用insert()方法添加文本,该方法有两个参数:第一个参数是插入的索引号,第二个参数是插入的字符串。索引号通常是项目的序号(第一项的序号是0)。
import tkinter as tkwin=tk.Tk()listBox=tk.Listbox(win, setgrid=True)listBox.pack()for item in ['a', 'b', 'c']:listBox.insert(tk.END, item)tk.Button(win, text="delete", command=lambda x=listBox:x.delete(tk.ACTIVE)).pack()win.mainloop()
使用delete()方法删除列表中的项目,最常用的操作是删除列表中的所有项目:
listBox.delete(0, tk.END)
当然也可以删除指定的项目,下面添加一个独立按钮来删除ACTIVE状态的项目。
Listbox组件根据selectmode选项提供了四种不同的选择模式(默认的选择模式是BROWSE):
- SINGLE(单选);
- BROWSE(也是单选,但拖动鼠标或通过方向键可以直接改变选项);
- MULTIPLE(多选);
- EXTENDED(也是多选,但需要配合Shift/Ctrl键来实现,也可以通过拖动光标进行多选)。
Scrollbar
虽然滚动条是作为一个独立的组件存在,不过平时它几乎都是与其他组件配合使用。下面演示如何使用垂直滚动条。为了在某个组件上安装垂直滚动条,需要做两件事:(1)设置该组件的yscrollbarcommand选项为Scrollbar组件的set()方法。(2)设置Scrollbar组件的command选项为该组件的yview()方法。
import tkinter as tkwin=tk.Tk()sBar=tk.Scrollbar(win)sBar.pack(side=tk.RIGHT, fill=tk.Y)label=tk.Listbox(win, yscrollcommand=sBar.set)for i in range(100):label.insert(tk.END, i)label.pack(side=tk.LEFT, fill=tk.BOTH)sBar.config(command=label.yview)win.mainloop()
Scale
Scale组件主要通过滑块来表示某个范围内的一个数字,可以通过修改选项设置范围以及分辨率(精度)。当希望用户输入某个范围内的一个数值时,使用Scale组件可以很好地代替Entry组件。
创建一个指定范围的Scale组件,只需要指定它的from和to两个选项即可。但由于from本身是Python的关键字,所以为了区分需要在后面紧跟一个下画线,如from_。orient=tk.HORIZONTAL/tk.VERTICAL代表该组件是横向/竖向的,如果不写,默认是竖向的。
使用get()方法可以获取当前滑块的位置:通过resolution选项控制步长,通过tickinterval选项设置刻度。刻度应该间距比步长大,否则刻度容易不起作用。
import tkinter as tkwin=tk.Tk()tk.Scale(win, from_=0, to=100, tickinterval=20, resolution=5, length=600).pack()sc=tk.Scale(win, from_=0, to=200, orient=tk.HORIZONTAL)sc.pack()def show1():print('横条的值是:%d'%(sc.get()))tk.Button(win, text="获取值", command=show1).pack()win.mainloop()
Text
文本组件用于显示和处理多行文本。在Tkinter的所有组件中,Text组件显得异常强大和灵活,它适用于处理多种任务。虽然该组件的主要目的是显示多行文本,但它常常也被作为简单的文本编辑器和网页浏览器使用。当创建一个Text组件的时候,它里面是没有内容的。为了给其插入内容,可以使用insert()方法以及INSERT或END索引号。
Text组件不仅支持插入和编辑文本,还支持插入image对象和window组件。
import tkinter as tkwin = tk.Tk()txt = tk.Text(win, width=50, height=3)txt.pack()txt.insert(tk.INSERT, "I love \n")txt.insert(tk.END, "you!")photo=tk.PhotoImage(file="test.gif")def insertImage():txt.image_create(tk.END, image=photo)btn = tk.Button(win, text="click",command=insertImage);txt.window_create(tk.INSERT, window=btn)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开始。还可以使用以下语法构建索引:
“%d.%d”%(line, column)
指定超出现有文本的最后一行的行号,或超出一行中列数的列号都不会引发错误。对于这样的指定,Tkinter解释为已有内容的末尾的下一个位置。需要注意的是,使用“行/列”的索引方式看起来像是浮点值。其实不只是像而已,在需要指定索引的时候使用浮点值代替也是可以的:
text.insert(tkinter.INSERT, “I love you!”)print(text.get(“1.2”, 1.6))
2)”line.end”行号加上字符串”.end”的格式表示为该行最后一个字符的位置:
text.insert(tkinter.INSERT, “I love you!”)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的文本的最后一个字符之后)语法表示标签的范围:
“%s.first” % tagname“%s.last” % tagname
8)selection(SEL_FIRST,SEL_LAST)
selection是一个名为SEL(或”sel”)的特殊tag,表示当前被选中的范围,可以使用SEL_FIRST到SEL_LAST来表示这个范围。如果没有选中的内容,那么Tkinter会抛出一个TclError异常。
9)window coordinate(”@x,y”)可以使用窗口坐标作为索引。例如在一个事件绑定中,可以使用以下代码找到最接近鼠标位置的字符:
“@%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或格式化操作来创建一个表达式字符串。下面例子演示了如何删除插入光标前面的一个字符:
def backspace(event):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事实上就是索引,用于表示位置:
import tkinter as tkwin = tk.Tk()txt = tk.Text(win, width=50, height=3)txt.pack()txt.insert(tk.INSERT, "I love \n")txt.insert(tk.END, "you!")txt.mark_set("k", 1.3) #将第一行第3个字符后面设置标记,名称为"k"txt.insert("k", "test")win.mainloop()
默认插入内容到Mark,是插入到它的左侧(就是说插入一个字符的话,Mark向后移动了一个字符的位置)。通过mark_gravity()方法就可以插入到Mark的右侧。
import tkinter as tkwin = tk.Tk()txt = tk.Text(win, width=50, height=3)txt.pack()txt.insert(tk.INSERT, "I love \n")txt.insert(tk.END, "you!")txt.mark_set("k", 1.3)txt.mark_gravity("k", tk.LEFT) #不写这句默认为RIGHT,即插入到右侧txt.insert("k", "test")win.mainloop()
Tag用法
Tag(标签)通常用于改变Text组件中内容的样式和功能,可以用来修改文本的字体、尺寸和颜色。另外,Tag还允许将文本、嵌入的组件和图片与键盘和鼠标等事件相关联。除了user-defined tags(用户自定义的Tag),还有一个预定义的特殊Tag:SEL。SEL(或”sel”)用于表示对应的选中内容(如果有的话)。可以自定义任意数量的Tag,Tag的名字由普通字符串组成,可以是除了空白字符外的任何字符。另外,任何文本内容都支持多个Tag描述,任何Tag也可以用于描述多个不同的文本内容。为指定文本添加Tag可以使用tag_add()方法:
import tkinter as tkwin = tk.Tk()txt = tk.Text(win, width=50, height=3)txt.pack()txt.insert(tk.INSERT, "test test test!")txt.tag_add("tag1", 1.2, "1.4", "1.6")#标记[1.2, 1.4)及1.6,即每2个组成一个区间txt.tag_configure("tag1", background="yellow", foreground="red")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:
txt. tag_config('tag2', foreground="blue") # 新的 Tag#那么新创建的Tag2会覆盖比较旧的Tag1的相同选项#注意,与下面的调用顺序没有关系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)。
import tkinter as tkimport webbrowserwin = tk.Tk()txt = tk.Text(win, width=50, height=3)txt.pack()txt.insert(tk.INSERT, 'I love FishC.com!')txt.tag_add('link', 1.7, '1.16')txt.tag_config("link", foreground="blue", underline=True)def show_hand_cursor(event):txt.config(cursor="arrow")def show_arrow_cursor(event):txt.config(cursor="xterm")def click(event):webbrowser.open("http://www.fishc.com")txt .tag_bind("link", "<Enter>", show_hand_cursor)txt .tag_bind("link", "<Leave>", show_arrow_cursor)txt .tag_bind("link", "<Button-1>", click)win.mainloop()
例题1:检查text文本框内容是否修改
import tkinter as tkimport hashlibwin = tk.Tk()txt = tk.Text(win, width=50, height=3)txt.pack()txt.insert(tk.INSERT, "I love python!")contents=txt.get(1.0, tk.END)def getSig(contents):hash=hashlib.md5(contents.encode())return hash.digest()sig=getSig(contents)def check():contents=txt.get(1.0, tk.END)if sig!=getSig(contents):print("内容修改啦")else:print("Same")tk.Button(win, text="检查", command=check).pack()win.mainloop()
例题2:查找操作
使用search()方法可以搜索Text组件中的内容。可以提供一个确切的目标进行搜索(默认),也可以使用Tcl格式的正则表达式进行搜索(需设置regexp选项为True):
import tkinter as tkwin = tk.Tk()txt = tk.Text(win, width=50, height=3)txt.pack()txt.insert(tk.INSERT, "I love python!")#将索引以元组方式输出def getIndex(text, index):return tuple(map(int, str.split(text.index(index), ".")))start=1.0while True:pos=txt.search("o", start, stopindex=tk.END)if not pos:breakprint("找到啦,位置是:", getIndex(txt, pos))start=pos + "+1c" #将start指向下一个字符win.mainloop()
如果忽略stopindex选项,表示直到文本的末尾结束搜索。设置backwards选项为True,则是修改搜索的方向(变为向后搜索,那么start变量应该设置为END,stopindex选项设置为1.0,最后”+1c”改为”-1c”)。
例题3:“恢复”和“撤销”操作
通过设置undo选项为True,可以开启Text组件的“撤销”功能,然后用edit_undo()方法实现“撤销”操作,用edit_redo()方法实现“恢复”操作。
import tkinter as tkimport hashlibwin = tk.Tk()txt = tk.Text(win, width=50, height=3, undo=True)txt.pack()txt.insert(tk.INSERT, "I love python!")def show():txt.edit_undo()tk.Button(win, text="撤销", command=show).pack()win.mainloop()
这是因为Text组件内部有一个栈专门用于记录内容的每次变动,所以每次“撤销”操作就是一次弹栈操作,“恢复”就是再次压栈。
默认情况下,每一次完整的操作都会放入栈中。但怎么样算是一次完整的操作呢?Tkinter觉得每次焦点切换、用户按下回车键、删除/插入操作的转换等之前的操作算是一次完整的操作。也就是说,连续输入“FishC”的话,一次“撤销”操作就会将所有的内容删除。那能不能自定义呢?例如希望插入一个字符就算一次完整的操作,然后每次单击“撤销”就去掉一个字符。
当然可以!做法就是先将autoseparators选项设置为False(因为这个选项是让Tkinter在认为一次完整的操作结束后自动插入“分隔符”),然后绑定键盘事件,每次有输入就用edit_separator()方法人为地插入一个“分隔符”:
import tkinter as tkimport hashlibwin = tk.Tk()txt = tk.Text(win, width=50, height=3, autoseparators=False, undo=True, maxundo=10)txt.pack()def callback(event):txt.edit_separator()txt.bind('<key>', callback)txt.insert(tk.INSERT, "I love python!")def show():txt.edit_undo()tk.Button(win, text="撤销", command=show).pack()win.mainloop()
Canvas
Canvas是一个通用的组件,它通常用于显示和编辑图形,可以用它来绘制直线、圆形、多边形,甚至是绘制其他组件。在Canvas组件上绘制对象,可以用create_xxx()方法(xxx表示对象类型,例如直线line、矩形rectangle和文本text等):
import tkinter as tkwin = tk.Tk()c=tk.Canvas(win, height=200, width=200)c.pack()# 画一条黄色的横线c.create_line(0, 50, 200, 50, fill="yellow")# 画一条红色的竖线(虚线)c.create_line(100, 0, 100, 100, fill="red", dash=(4, 4))# 中间画一个蓝色的矩形c.create_rectangle(50, 25, 150, 75, fill="blue")win.mainloop()
注意,添加到Canvas上的对象会一直保留着。如果希望修改它们,可以使用coords()、itemconfig()和move()方法来移动画布上的对象,或者使用delete()方法来删除:
import tkinter as tkwin = tk.Tk()c=tk.Canvas(win, height=200, width=200)c.pack()line1 = c.create_line(0, 50, 200, 50, fill="yellow")line2 = c.create_line(100, 0, 100, 100, fill="red", dash=(4, 4))rect1 = c.create_rectangle(50, 25, 150, 75, fill="blue")c.coords(line1, 0, 25, 200, 25)c.itemconfig(rect1, fill="red")c.delete(line2)tk.Button(win, text="删除全部", command=(lambda x=tk.ALL : c.delete(x))).pack()win.mainloop()
还可以在Canvas上显示文本,使用的是create_text()方法:
……c..create_text(100, 50, text="I love Python!")……
使用create_oval()方法绘制椭圆形(或圆形),参数是指定一个限定矩形(Tkinter会自动在这个矩形内绘制一个椭圆):
import tkinter as tkwin = tk.Tk()w = tk.Canvas(win, width=200, height=100)w.pack()w.create_rectangle(40, 20, 160, 80, dash=(4, 4))w.create_oval(40, 20, 160, 80, fill="pink")w.create_text(100, 50, text="test")win.mainloop()
可以通过绘制一个超小的椭圆形来表示一个“点”。在下面的例子中,通过响应“鼠标左键按住拖动”事件(),在鼠标拖动的同时获取鼠标的实时位置(x, y),并绘制一个超小的椭圆来代表一个“点”:
import tkinter as tkwin = tk.Tk()w = tk.Canvas(win, width=400, height=200)w.pack()def paint(event):x1, y1 = (event.x - 1), (event.y - 1)x2, y2 = (event.x + 1), (event.y + 1)w.create_oval(x1, y1, x2, y2, fill="red")w.bind("<B1-Motion>", paint)tk.Label(win, text="按住鼠标左键并移动,开始绘制你的理想蓝图吧......").pack(side=tk.BOTTOM)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()方法将命令和其他子菜单添加进去:
import tkinter as tkwin = tk.Tk()menubar = tk.Menu(win)def callback():print("~~test~~")menubar.add_command(label="Hello", command=callback)menubar.add_command(label="Quit", command=win.quit)win.config(menu=menubar)win.mainloop()
创建一个下拉菜单(或者其他子菜单),方法大同小异,最主要的区别是它们最后需要添加到主菜单上(而不是窗口上):
import tkinter as tkwin = tk.Tk()menubar = tk.Menu(win)def callback():print("test")menuFile = tk.Menu(menubar, tearoff=False)menuFile.add_command(label="open", command=callback)menuFile.add_command(label="save", command=callback)menuFile.add_separator()menuFile.add_command(label="quit", command=win.quit)menubar.add_cascade(label="file", menu=menuFile)win.config(menu=menubar)win.mainloop()
创建一个弹出菜单的方法也是一致的,不过需要使用post()方法明确地将其显示出来:
import tkinter as tkwin = tk.Tk()def callback():print("test")menubar = tk.Menu(win, tearoff=False)menubar.add_command(label="undo", command=callback)menubar.add_command(label="redo", command=callback)frame = tk.Frame(win, width=512, height=512)frame.pack()def pop(event):menubar.post(event.x_root, event.y_root)frame.bind("<Button-3>", pop)win.config(menu=menubar)win.mainloop()
这个菜单不仅可以添加常见的命令菜单项,还可以添加单选按钮或多选按钮,那么用法就与Checkbutton组件和Radiobutton组件类似了。
import tkinter as tkwin = tk.Tk()def callback():print("~被调用了~")# 创建一个顶级菜单menubar = tk.Menu(win)# 创建 checkbutton 关联变量openVar = tk.IntVar()saveVar = tk.IntVar()exitVar = tk.IntVar()# 创建一个下拉菜单“文件”,然后将它添加到顶级菜单中filemenu = tk.Menu(menubar, tearoff=True)filemenu.add_checkbutton(label="打开", command=callback, variable=openVar)filemenu.add_checkbutton(label="保存", command=callback, variable=saveVar)filemenu.add_separator()filemenu.add_checkbutton(label="退出", command=win.quit, variable=exitVar)menubar.add_cascade(label="文件", menu=filemenu)# 创建 radiobutton 关联变量editVar = tk.IntVar()editVar.set(1)# 创建另一个下拉菜单“编辑”,然后将它添加到顶级菜单中editmenu = tk.Menu(menubar, tearoff=True)editmenu.add_radiobutton(label="剪切", command=callback, variable=editVar, value=1)editmenu.add_radiobutton(label="拷贝", command=callback, variable=editVar, value=2)editmenu.add_radiobutton(label="粘贴", command=callback, variable=editVar, value=3)menubar.add_cascade(label="编辑", menu=editmenu)# 显示菜单win.config(menu=menubar)win.mainloop()
OptionMenu
OptionMenu(选项菜单)事实上是下拉菜单的改版,它的发明弥补了Listbox组件无法实现下拉列表框的遗憾。创建一个选择菜单非常简单,只需要一个Tkinter变量(用于记录用户选择了什么)以及若干选项即可:
import tkinter as tkwin=tk.Tk()option=["one","two","three","four"]var=tk.StringVar()var.set(option[0])om=tk.OptionMenu(win, var, *option)om.pack()def callback():print(var.get())tk.Button(win, text="click", command=callback).pack()win.mainloop()
Spinbox
Spinbox组件(Tk8.4新增)是Entry组件的变体,用于从一些固定的值中选取一个。Spinbox组件与Entry组件用法非常相似,主要区别是使用Spinbox组件时,可以通过范围或者元组指定允许用户输入的内容。
import tkinter as tkwin=tk.Tk()w = tk.Spinbox(win, from_=0, to=10)w.pack()v=tk.Spinbox(win, values=("h", "k", "l","a"))v.pack()win.mainloop()
