系列文章目录
文章目录
- 系列文章目录
- 前言
- 一、GUI设计(上)
- 二、GUI设计(中)
- 二、GUI设计(下)
- 总结
前言
一、GUI设计(上)
GUI全称(Graphical User Interface),图形用户接口。
tkinter 是一个开放源码的图形接口开发工具,在安装python时,就已经同时安装此模块了,在使用前只需要导入即可;
import tkinter
print(tkinter.TkVersion)
8.6
1.建立窗口
from tkinter import *
root = Tk()
root.mainloop()
通常将使用TK()方法建立的窗口称为根窗口,默认名称是tk,之后可以在根窗口中建立许多控件,也可以在此根窗口中建立上层窗口。mainloop()方法让程序继续执行,同时进入等待与处理窗口事件,单击窗口的“关闭”,此程序才会结束。
2.与窗口相关的方法
方法 | 说明 |
---|---|
title | 可以设置窗口的标题 |
geometry(“widthxheight+x+y”) | 根据窗口宽width与高height,单位是像素,设定窗口位置 |
maxsize(width,height) | 拖曳时窗口最大的宽和高 |
minsize(width,height) | 拖曳时窗口最小的宽和高 |
configure(bg=“color”) | 设置窗口的背景颜色 |
resizable(True,Ture) | 可设置是否可更改窗口大小,第一个参数是宽,第二个参数是高,如果要固定窗口宽与高,可以使用resizable(0,0) |
state(“zoomed”) | 最大化窗口 |
iconify() | 最小化窗口 |
iconbitmap(“xx.ico”) | 更改默认窗口图标 |
2.建立自定义窗口
from tkinter import *
root = Tk()
root.title("MyWindow")
root.geometry("300x160+400+200")
root.configure(bg="#33ff33")
root.iconbitmap("mystar.ico")
root.mainloop()
Traceback (most recent call last):
File “.\GUI.py”, line 23, in
root.iconbitmap(“mystar.ico”)
File “E:\py\python3.7\Python37\lib\tkinter_init_.py”, line 1868, in wm_iconbitmap
return self.tk.call(‘wm’, ‘iconbitmap’, self._w, bitmap)
_tkinter.TclError: bitmap “mystar.ico” not defined
原因: 出现这个错误的原因是因为在我们的.py文件中引入了外部图片,打包成.exe文件放到桌面上运行,在同一目录下没有找到该图片。
解决方式:
1、将图片放到指定路径下;
geometry中width和height用x分隔,表示窗口的宽和高,+x是窗口左边距离屏幕左边的位置,如果是-x则是表示窗口右边距离屏幕右边的距离,同理,+y或-y同理;
在tkinter模块中可以使用下列方法获得屏幕的宽度和高度:
- winfo_screenwidth(): 屏幕宽度
- winfo_screenheight(): 屏幕高度
下面的程序将窗口显示在屏幕中央:
from tkinter import *
root = Tk()
root.title("MyWindow")
root.geometry("300x160+400+200")
root.configure(bg="#33ff33")
root.iconbitmap("mystar.ico")
screenWidth = root.winfo_screenwidth()
screenHeight = root.winfo_screenheight()
w = 300
h = 160
x = (screenWidth - w) / 2
y = (screenHeight -h) / 2
root.geometry("%dx%d+%d+%d" % (w,h,x,y))
root.mainloop()
3.tkinter的Widget
Widget可以翻译为控件或组件或部件。窗口建立后下一步就是在窗口建立控件。
控件:
• Button:按钮;
• Canvas:画布;
• Checkbutton:多选按钮;
• Entry:文本框;
• Frame:框架;
• Label:标签;
• LabelFrame:标签框架;
• Listbox:列表框;
• Menu:菜单;
• Message:消息;
• OptionMenu:下拉菜单;
• PanedWindow:面板;
• Radiobutton:单选按钮;
• Scale:尺度;
• Scrolbar:滚动条;
• Spinbox:可微调输入控件;
• Text:文字区域;
• Toplevel:上层窗口。
加强版模块tkinter.ttk中新增Widget:
• Combobox;
• Notebook;
• Progressbat;
• Separator;
• Sizegrip;
• Treeview;
Widget的共同属性:
• Dimensions:大小;
• Colors:颜色;
• Fonts:字体;
• Anchor:锚(位置参考点);
• Relief styles:属性边框;
• Bitmaps:显示位图;
• Cursors:鼠标外形;
Widget 的共同方法:
- Configuration
• config(option=value):Widget属性可以在建立时设置,也可以在程序执行时使用config()重新设置;
• cget(“option”):取得option参数值;
• keys():可以用此方法获得所有该Widget的参数。 - Event Processing
• mainloop():让程序继续执行,同时进入等待与处理窗口事件;
• quit():Python Shell窗口结束,但是所建立的窗口继续执行;
• update():更新窗口画面。 - Event callbacks
• bind(event,callback):事件绑定;
• unbind(event):接触绑定。 - Alarm handlers
• after(time,callback):间隔指定时间后调用callback()方法。
4.标签
Label()方法用于在窗口内建立文字或图像标签。
Label(父对象,options,…)
常用options参数:
- anchor:如果空间大于所需时,控制标签的位置,默认是CENTER(居中);√ 3.2
- bg或background:背景颜色;√ 3.1
- bitmap:使用默认图标当做标签内容;√ 3.6
- borderwidth或bd:标签边界宽度,默认为1;
- compound:可以设置标签内含图形和文字时,彼此的位置关系;√ 3.7
- cursor:当鼠标光标在标签上方时的外形;√ 3.12
- fg或foreground:前景颜色;√ 3.1
- font:可选择字形、字形样式与大小;√ 3.4
- height:标签高度,单位是字符;√ 3.1
- image:标签以图形形式呈现;√ 3.10
- justify:存在多行文本时最后一行的对齐方式,可取left/center(默认)/right;√ 3.5
- padx/pady:标签文字与标签区间的间距,单位是像素;√ 3.9
- relief:默认为FLASE,可控制标签的外框;√ 3.8
- text:标签文字内容,使用\n可输入多行;√ all
- textvariable:可以设置标签以变量的形式显示;
- underline:可以设置第几个文字有下划线,从0开始,默认为-1,表示无下划线;
- width:标签宽度,单位是字符;√ 3.1
- wraplength:文本到多少宽度后换行,单位是像素。√ 3.3
4.1 Color、Dimensions
Widget的共同属性。
bg、fg设置背景、前景色。
width、height的单位是字符。
from tkinter import *
root = Tk()
root.title("EX")
root.geometry("200x80")
label = Label(root, text = "I like tkinter", bg = "#EF72AA", fg = "white", height = 3, width = 20)
label.pack() #包装与定位组件
root.mainloop()
4.2 Anchor
Widget的共同属性。
Anchor是指标签文字在标签区域的输出位置。
备注:anchor的参数设置也可以使用小写常数,同时省略字符串的双引号。
from tkinter import *
root = Tk()
root.title("EX")
root.geometry("200x80")
label = Label(root, text = "I like tkinter", bg = "#EF72AA",
fg = "white", height = 3, width = 20, anchor = SE)
label.pack() #包装与定位组件
root.mainloop()
4.3 Wraplength
wraplength = 35
from tkinter import *
root = Tk()
root.title("EX")
root.geometry("200x80")
label = Label(root, text = "I like tkinter", bg = "#EF72AA",
fg = "white", height = 3, width = 20, anchor = NW, wraplength = 35)
label.pack() #包装与定位组件
root.mainloop()
4.4 Font
Widget的共同属性。
(height和width都是字号联动的。)
用于设置文字字形。
- family:字形,如Helvetica、Times等;
- size:字号,单位是像素;
- weight:如bold、normal;
- slant:如italic、roman;
- underline:True、False;
- overstrike:True、False。
from tkinter import *
root = Tk()
root.title("EX")
root.geometry("200x80")
label = Label(root, text = "I like tkinter", bg = "#EF72AA",
fg = "white", height = 3, width = 20, font = "Helnetic 20 bold italic")
label.pack() #包装与定位组件
root.mainloop()
from tkinter import *
root = Tk()
root.title("EX")
root.geometry("200x80")
label = Label(root, text = "I like tkinter", bg = "#EF72AA",
fg = "white", height = 3, width = 20, font = ("Helnetic", 20,"bold", "italic"))
label.pack() #包装与定位组件
root.mainloop()
4.5 Justify
justify = “right”
from tkinter import *
root = Tk()
root.title("EX")
#root.geometry("200x80")
label = Label(root, text = "I like tkinter", bg = "#EF72AA",
fg = "white", height = 3, width = 20, anchor = NW, wraplength = 35, justify = "right")
label.pack() #包装与定位组件
root.mainloop()
4.6 Bitmaps
Widget的共同属性。
在标签位置放置内建位图。
依次为:error、hourglass、info、questhead、question、warning、gray12、gray25、gray50、gray75
4.7 Compound
图像与文字共存时,使用此参数定义文字与图像的位置关系。
- left:图像在左;
- right:图像在右;
- top:图像在上;
- bottom:图像在下;
- center:文字覆盖在图像上方
label = Label(root, bitmap = “question”, text = “Question”, compound = “left”)
from tkinter import *
root = Tk()
root.title("EX")
#root.geometry("200x80")
label = Label(root, bitmap = "question", text = "Question", compound = "left")
label.pack() #包装与定位组件
root.mainloop()
4.8 Compound
Widget的共同属性。
用于建立Widget的边框。
from tkinter import *
root = Tk()
root.title("EX")
#root.geometry("200x80")
label = Label(root, text = "raised", relief = "raised")
label.pack() #包装与定位组件
root.mainloop()
4.9 Padx/Pady
padx用于设置标签文字左右边界与标签区间的x轴间距,pady用于设置文字上下边界与标签取件单y轴间距。
与width和height作用相似,可以互相替代。
from tkinter import *
root = Tk()
root.title("EX")
#root.geometry("200x80")
root.geometry("300x100")
label = Label(root, text = "raised", bg = "lightyellow", relief = "raised", padx = 5, pady = 10) #上下间距10 左右间距5
label.pack() #包装与定位组件
root.mainloop()
4.10 Image
图片可以应用在许多地方,例如标签、功能按钮、选项按钮文字区域等。在使用前可以使用PhotoImage()方法建立图像对象,然后再将此对象应用在其他窗口组件上。
png图:
from tkinter import *
from PIL import Image, ImageTk
root = Tk()
root.title("EX")
image_obj = Image.open("37.png")
jpg_obj = ImageTk.PhotoImage(image_obj)
label = Label(root, image = jpg_obj)
label.pack() #包装与定位组件
root.mainloop()
gif图:
from tkinter import *
from PIL import Image, ImageTk
root = Tk()
root.title("EX")
image_obj = PhotoImage(file = "2.gif")
#jpg_obj = ImageTk.PhotoImage(image_obj)
label = Label(root, image = image_obj)
label.pack() #包装与定位组件
root.mainloop()
注意:bitmap和image不能共存。
4.11 Cursors
Widget的共同属性。
表示光标形状,实际形状可能会因操作系统不同而有所差异。
4.12 keys()
Widget的共同方法。
可以以列表的形式传回Widget的所有参数。
from tkinter import *
root = Tk()
root.title("Ex")
label = Label(root, text = "tkinter")
label.pack()
print(label.keys())
[‘activebackground’, ‘activeforeground’, ‘anchor’, ‘background’, ‘bd’, ‘bg’, ‘bitmap’, ‘borderwidth’, ‘compound’, ‘cursor’, ‘disabledforeground’, ‘fg’, ‘font’, ‘foreground’, ‘height’, ‘highlightbackground’, ‘highlightcolor’, ‘highlightthickness’, ‘image’, ‘justify’, ‘padx’, ‘pady’, ‘relief’, ‘state’, ‘takefocus’, ‘text’, ‘textvariable’, ‘underline’, ‘width’, ‘wraplength’]
4.13 config()
Widget的共同方法。
Widget控件在建立时可以直接设置对象属性,若是部分属性未建立,未来在程序执行时可以使用config()方法建立或更改属性。此方法内属性设置的参数用法与建立时相同。
计数器:
from tkinter import *
counter = 0
def run_counter(digit):
def counting():
global counter
counter += 1
digit.config(text=str(counter))
digit.after(1000,counting)
counting()
root = Tk()
root.title("Ex")
digit = Label(root, bg = "yellow", \
height = 3, width = 10, \
font = "Helvetic 20 bold")
digit.pack()
run_counter(digit)
root.mainloop()
4.14 Separator
用于添加分隔线;
Separator(父对象,options)
options为HORIZONTAL表示建立水平分隔线,VERTICAL表示建立垂直分隔线。
from tkinter import *
from tkinter.ttk import Separator
root = Tk()
root.title("Ex")
myTitle = "我喜欢Tkinter"
myContent = """今天,我开始学习Tkinter。
Tkinter 是 Python 的标准 GUI 库,
Python 使用 Tkinter 可以快速的创建 GUI 应用程序。"""
label1 = Label(root, text = myTitle, font = "Helvetic 20 bold")
label1.pack(padx = 10, pady = 10)
sep = Separator(root, orient = HORIZONTAL)
sep.pack(fill = X, padx = 5)
label2 = Label(root, text = myContent)
label2.pack(padx = 10, pady = 10)
root.mainloop()
5.窗口控件管理
5.1 简介
Widget Layout Manager 用于将多个Widget控件配置到容器或窗口内,有三种方法:
- pack
- grid
- place
5.2 pack
最常用的控件配置管理员,使用相对位置类处理控件,至于控件的正确位置则是由pack方法自动完成的。
pack(options,…)
-after,-anchor,-before,-expand,-fill,-in,-ipadx,-ipady,-pady,-side
1、 side:用于水平或垂直配置控件
- TOP:默认值,从上到下排列;
- BOTTOM:从下到上排列;
- LEFT:从左到右排列;
- RIGHT:从右到左排列。
from tkinter import *
from tkinter.ttk import Separator
root = Tk()
root.title("Stu")
label1 = Label(root, text = "兰州交通大学", bg = "lightyellow", width = 30)
label2 = Label(root, text = "华北电力大学", bg = "lightgreen", width = 15)
label3 = Label(root, text = "哈尔滨工程大学", bg = "lightblue", width = 15)
label1.pack(side = BOTTOM)
label2.pack(side = RIGHT)
label3.pack(side = LEFT)
root.mainloop()
列出所有的relief属性:
from tkinter import *
from tkinter.ttk import Separator
root = Tk()
root.title("EX")
Reliefs = ["flat", "groove", "raised", "ridge", "solid", "sunken"]
for Relief in Reliefs:
Label(root, text = Relief, relief = Relief,fg = "darkgreen",
font = "Times 20 bold").pack(side = LEFT, padx = 5)
root.mainloop()
列出所有的位图:
from tkinter import *
from tkinter.ttk import Separator
root = Tk()
root.title("EX")
bitMaps = ["error", "hourglass", "info", "questhead", "question",
"warning", "gray12", "gray25", "gray50", "gray75"]
for bitMap in bitMaps:
Label(root, bitmap = bitMap).pack(side = LEFT, padx = 5)
root.mainloop()
2、padx/pady
设定控件边界与容器或其它控件边界之间的距离,默认为1
3、ipadx/ipady
控制文字与标签容器的x/y轴间距
4、anchor
设定Widge控件在窗口中的位置
from tkinter import *
from tkinter.ttk import Separator
root = Tk()
root.title("EX")
root.geometry("300x180")
label1 = Label(root, text = "Next", font = "Times 15 bold", fg = "white", bg = "blue")
label2 = Label(root, text = "Previous", font = "Times 15 bold", fg = "white", bg = "red")
label1.pack(anchor = S, side = RIGHT, padx = 10, pady = 10)
label2.pack(anchor = S, side = RIGHT, padx = 10, pady = 10)
root.mainloop()
5、 fill
设置控件填满所分配容器的方式
- fill = X:填满所分配控件的X轴不留白
- fill = Y:填满Y轴不留白
- fill = BOTH:填满X轴和Y轴
- fill = NONE:默认值,保持原大小
如果所分配容器区间已经满了,则使用fill参数不会有任何作用。
from tkinter import *
from tkinter.ttk import Separator
root = Tk()
root.title("Ex")
label1 = Label(root, text = "兰州交通大学", bg = "lightyellow")
label2 = Label(root, text = "哈尔滨工程大学", bg = "lightgreen")
label3 = Label(root, text = "东北林业大学", bg = "lightblue")
label1.pack(side = LEFT, fill = Y)
label2.pack()
label3.pack(fill = X)
root.mainloop()
6、expend
设定Widge控件是否填满额外的父容器界面,默认为False
label2.pack(fill = BOTH, expand = True)
from tkinter import *
from tkinter.ttk import Separator
root = Tk()
root.title("Ex")
label1 = Label(root, text = "兰州交通大学", bg = "lightyellow")
label2 = Label(root, text = "哈尔滨工程大学", bg = "lightgreen")
label3 = Label(root, text = "东北林业大学", bg = "lightblue")
label1.pack(side = LEFT, fill = Y)
label2.pack(fill = BOTH, expand = True)
label3.pack(fill = X)
root.mainloop()
7、pack的方法
pack其实在tkinter中也是一个类别,有以下方法可供使用
- slaves():传回所有的Widget控件对象;
- info():传回pack选项的对应值;
- forget():隐藏Widget控件,可以用pack(opions,…)复原显示;
- location(x,y):传回此点是否在单元格,如果是传回坐标,如果不是传回(-1,-1);
- size():传回Widget的控件大小;
- propagate():参数是True表示父窗口大小由子控件决定,默认为True;
print(root.pack_slaves())
5.3 grid
这是一种以格状或者类似Excel表格方式包装和定位窗口组件的方法。
grid(opions,…)
-column, -columnspan, -in, -ipadx, -ipady, -padx, -pady, -row, -rowspan, -sticky
1、 row、column
label1.grid(row = 0, column = 0)
label2.grid(row = 1, column = 2)
label3.grid(row = 2, column = 1)
from tkinter import *
from tkinter.ttk import Separator
root = Tk()
root.title("Ex")
label1 = Label(root, text = "兰州交通大学", bg = "lightyellow")
label2 = Label(root, text = "哈尔滨工程大学", bg = "lightgreen")
label3 = Label(root, text = "东北林业大学", bg = "lightblue")
label1.grid(row = 0, column = 0)
label2.grid(row = 1, column = 2)
label3.grid(row = 2, column = 1)
root.mainloop()
2、 rowspan、columnspan
可以设定row或column方向的合并数量
3、padx/pady
与pack方法中参数相同
4、sticky
类似于anchor,只能设定N/S/W/E,即上/下/左/右对齐,但可以组合使用。
- N+W:左上角;
- N+E:右上角;
- S+W:左下角;
- S+E:右下角;
- N+S:拉长高度让控件在顶端和底端对齐;
- W+E:拉长宽度让控件在左边和右边对齐;
- N+S+W:顶端和底端对齐,切齐左边;
- N+S+E:顶端和底端对齐,切齐右边;
- W+E+N:左边和右边对齐,切齐顶端;
- W+E+S:左边和右边对齐,切齐底端;
- N+S+W+E:拉伸平铺。
5、rowconfigure()、columnconfigure()
用于设定某行或列在窗口缩放大小时的缩放比例。
6、grid方法
建立色彩标签:
from tkinter import *
from tkinter.ttk import Separator
root = Tk()
root.title("Ex")
Colors = ["red", "orange", "yellow", "green","cyan", "blue", "purple"]
r = 0
for color in Colors:
Label(root, text = color, relief = "groove", width = 20).grid(row = r, column = 0)
Label(root, bg = color, relief = "ridge", width = 20).grid(row = r,column = 1)
r += 1
root.mainloop()
5.4 place
使用直接指定方式将Widget控件放在容器中的方法。
place(opions,…)
1、x/y
直接设定控件左上角位置,单位是像素。
窗口区左上角是(x = 0 , y = 0)x向右递增,y向下递增;
2、width/height
在插入图片的同时设定图片的大小。
from tkinter import *
from tkinter.ttk import Separator
from PIL import Image, ImageTk
root = Tk()
#root=Toplevel()
root.title("Ex")
root.geometry("520x520")
flower = ImageTk.PhotoImage(Image.open("37.png"))
scenery = ImageTk.PhotoImage(Image.open("31.png"))
p1 = Label(root, image = flower)
p2 = Label(root, image = scenery)
p1.place(x=20, y=20, width = 200, height = 200)
p2.place(x=240, y=240, width = 260, height = 260)
root.mainloop()
3、relx/rely、relwidth/relheight
relx/rely用于设置相对于父容器的位置,relwidth/relheight用于设置相对于父容器的大小,取值为0-1;
6.功能按钮Button
在窗口组件中可以设计在单击功能按钮时,执行某一个特定的动作,这个动作称为callback方法;
Button(父对象,options,…)
6.1 常用的options参数:
- borderwidth或bd:边界宽度,默认2像素;
- bg或background:背景色彩;
- command:单击功能按钮时,执行此方法;
- cursor:当鼠标光标移至按钮上时的形状;
- fg或foreground:前景色彩;
- font:字形;
- height:高,单位是字符;
- highlightbackground:当功能按钮取得焦点时的背景色彩;
- highlightcolor:当功能按钮取得焦点时的色彩;
- image:功能按钮上的图像;
- justify:当有多行文字时,最后一行文字的对齐方式;
- padx/pady:默认为1;
- relief:默认为FLAT;
- state:默认为NORMAL,若设置为DISABLED则以灰阶显示按钮表示无法使用;
- text:功能按钮文字;
- underline:设置第几个文字有下划线;
- width:宽,单位是字符;
- wraplength:限制每行的字符数。
6.2 示例一:
单击Show Message显示文本:
from tkinter import *
from tkinter.ttk import Separator
from PIL import Image, ImageTk
def msgShow():
label["text"] = "I love tkinter."
label["bg"] = "lightyellow"
label["fb"] = "blue"
label.config(text = "I love tkinter.", bg = "lightyellow", fg = "blue")
root = Tk()
root.title("Ex")
label = Label(root)
btn1 = Button(root, text = "Show Message", width = 15, command = msgShow)
btn2 = Button(root, text = "Exit", width = 15, command = root.destroy)
label.pack()
btn1.pack(side = LEFT)
btn2.pack(side = LEFT)
root.mainloop()
6.3 Lambda
from tkinter import *
from tkinter.ttk import Separator
from PIL import Image, ImageTk
def bColor(bgColor):
root.config(bg = bgColor)
root = Tk()
root.title("Ex")
root.geometry("300x200")
#建立3个按钮
bluebtn = Button(root, text = "Blue", command = lambda:bColor("blue"))
yellowbtn = Button(root, text = "Yellow", command = lambda:bColor("yellow"))
exitbtn = Button(root, text = "Exit", command = root.destroy)
exitbtn.pack(anchor = S, side = RIGHT, padx = 5, pady = 5)
yellowbtn.pack(anchor = S, side = RIGHT, padx = 5, pady = 5)
bluebtn.pack(anchor = S, side = RIGHT, padx = 5, pady = 5)
root.mainloop()
7.文本框Entry
通常是指单行文本框,是GUI设计中用于输入的最基本的Widget控件,可以用来输入单行字符串。
如果输入的字符串长度大于文本框的宽度,输入的文字会自动隐藏造成部分内容无法显示出来,此时可以用箭头移动鼠标光标到看不到的区域。
如果需要处理多行文字,则需要使用Text。
Entry(父对象,options,…)
常用的options参数:
- bg,bd,cursor,fg,font,height,highlightbackground,highlightcolor,justify,relief,width;
- command:当用户更改内容时,会自动执行此函数;
- exportselection:选取的字符串自动输出至剪切板,使用exportselection = 0来避免;
- selectbackground:被选取字符串时的背景色彩;
- selectborderwidth:被选取字符串时的边界宽度,默认为1;
- selectforeground:被选取字符串时的前景色彩;
- show:显示输入字符,例如show = '*'表示显示星号,常用于输入密码;
- state:输入状态,默认NORMAL表示可以输入,DISABLE表示无法输入;
- textvariable:文字变量;
- xscrollcommand:在x轴使用滚动条。
7.1 示例
from tkinter import *
from tkinter.ttk import Separator
from PIL import Image, ImageTk
root = Tk()
root.title("Ex")
accountL = Label(root, text = "Account:")
accountL.grid(row = 0)
passwordL = Label(root, text = "Password:")
passwordL.grid(row = 1)
accountE = Entry(root)
accountE.grid(row = 0,column = 1)
passwordE = Entry(root, show = '*')
passwordE.grid(row = 1, column = 1)
root.mainloop()
7.2 get()
用于获取目前Entry的字符串内容
from tkinter import *
from tkinter.ttk import Separator
from PIL import Image, ImageTk
root = Tk()
root.title("Ex")
accountL = Label(root, text = "Account:")
accountL.grid(row = 0)
passwordL = Label(root, text = "Password:")
passwordL.grid(row = 1)
def printInfo():
print("Account:%s\nPassword:%s" % (accountE.get(), passwordE.get()))
...
loginbtn = Button(root, text = "Login", command = printInfo)
loginbtn.grid(row = 3)
quitbtn = Button(root, text = "Quit", command = root.destroy)
quitbtn.grid(row = 3, column = 1)
accountE = Entry(root)
accountE.grid(row = 0,column = 1)
passwordE = Entry(root, show = '*')
passwordE.grid(row = 1, column = 1)
root.mainloop()
Account:用余生去守护
Password:123456
7.3 insert()
用于建立默认文字。
7.4 delete()
用于删除文字。
delete(first,last = None) #删除从first到last-1字符之间的字符串(last = None则仅删除第first个字符)
delete(0,END) #删除文本框内整个字符串
7.5 eval()
用于将字符串转换为python语句执行,可以直接计算数学表达式。
result = eval(expression) #expression是字符串
数学表达式计算:
from tkinter import *
import numpy as np
def cal():
out.configure(text = "结果:" + str(eval(equ.get())))
root = Tk()
root.title("Ex")
label = Label(root, text = "请输入数学表达式:")
label.pack()
equ = Entry(root)
equ.pack(padx = 20, pady = 5)
out = Label(root)
out.pack()
btn = Button(root, text = "计算", command = cal)
btn.pack(pady = 5)
root.mainloop()
8.变量类型
要将Widget控件的参数以变量的方式处理时,需要借助tkinter模块内的变量类别,这个类别有4个子类别,每一个类别其实是一个数据类型的构造方法,我们可以通过这四个子类别的数据类型将它们与Widget控件的相关参数结合。
- x = IntVar() #整型变量,默认是0
- x = DoubleVar() #浮点型变量,默认是0.0
- x = StringVar #字符串变量,默认是""
- x = BooleanVar() #布尔型变量
8.1 get()、set()
使用get()方法取得变量内容,使用set()方法设置变量内容。
def btn_hit():
if x.get() == "":
x.set("I like tkinter")
else:
x.set("")
from tkinter import *
msg_on = False
def btn_hit():
global msg_on
if msg_on == False:
msg_on = True
x.set("I like tkinter")
else:
msg_on = False
x.set("")
root = Tk()
root.title("Ex")
x = StringVar()
label = Label(root, textvariable = x, fg = "blue", bg = "lightyellow",
font = "Verdana 15 bold", width = 25, height = 2)
label.pack()
btn = Button(root, text = "Show", command = btn_hit)
btn.pack(pady = 5)
root.mainloop()
8.2 trace()
利用变量追踪Widget控件,当其内容更改时,让程序执行callback函数。
from tkinter import *
def callback(*args):
print("data changed:",xE.get())
root = Tk()
root.title("Ex")
xE = StringVar()
entry = Entry(root, textvariable = xE)
entry.pack()
xE.trace('w', callback)
root.mainloop()
data changed: t
data changed: tk
data changed: tki
data changed: tkin
data changed: tkint
data changed: tkinte
data changed: tkinter
w表示当写入时,自动执行callback函数,这个动作称为变动追踪。
r表示当控件内容被读取时,执行待定函数。
from tkinter import *
def callbackW(*args):
xL.set(xE.get())
def callbackR(*args):
print("Waring:数据被读取!")
def hit():
print("读取数据:", xE.get())
root = Tk()
root.title("Ex")
xE = StringVar()
entry = Entry(root, textvariable = xE)
entry.pack(padx = 20, pady = 5)
xE.trace('w', callbackW)
xE.trace('r', callbackR)
xL = StringVar()
label = Label(root, textvariable = xL)
xL.set("同步显示")
label.pack(padx = 20, pady = 5)
btn = Button(root, text = "读取", command = hit)
btn.pack(padx = 20, pady = 5)
root.mainloop()
Waring:数据被读取!
读取数据: tkinter
callback函数有三个参数:tk变量名称、index索引、mode模式
8.3 计算器
from tkinter import *
def calculate():
r = re.findall(".+", equ.get())
result = eval(str(r[-1]))
equ.set(equ.get() + "\n" + str(result))
def show(buttonString):
content = equ.get()
if content == "0":
content = ""
equ.set(content + buttonString)
def backspace():
equ.set(str(equ.get()[:-1]))
def clear():
equ.set("0")
root = Tk()
root.title("calculator")
equ = StringVar()
equ.set("0") #默认显示0
#显示区
label = Label(root, width = 25, height = 2, relief = "raised", \
anchor = SE, textvariable = equ)
label.grid(row = 0, column = 0, columnspan = 4, padx = 5, pady = 5)
#按钮设计
Button(root, text = "C", fg = "blue", width = 5, \
command = clear).grid(row = 1, column = 0)
Button(root,text="DEL",width=5,command=backspace).grid(row=1,column=1)
Button(root,text="%",width=5,command=lambda:show("%")).grid(row=1,column=2)
Button(root,text="/",width=5,command=lambda:show("/")).grid(row=1,column=3)
Button(root,text="7",width=5,command=lambda:show("7")).grid(row=2,column=0)
Button(root,text="8",width=5,command=lambda:show("8")).grid(row=2,column=1)
Button(root,text="9",width=5,command=lambda:show("9")).grid(row=2,column=2)
Button(root,text="*",width=5,command=lambda:show("*")).grid(row=2,column=3)
Button(root,text="4",width=5,command=lambda:show("4")).grid(row=3,column=0)
Button(root,text="5",width=5,command=lambda:show("5")).grid(row=3,column=1)
Button(root,text="6",width=5,command=lambda:show("6")).grid(row=3,column=2)
Button(root,text="-",width=5,command=lambda:show("-")).grid(row=3,column=3)
Button(root,text="1",width=5,command=lambda:show("1")).grid(row=4,column=0)
Button(root,text="2",width=5,command=lambda:show("2")).grid(row=4,column=1)
Button(root,text="3",width=5,command=lambda:show("3")).grid(row=4,column=2)
Button(root,text="+",width=5,command=lambda:show("+")).grid(row=4,column=3)
Button(root, text="0", width=12, \
command = lambda:show("0")).grid(row = 5, column = 0, columnspan = 2)
Button(root, text=".", width=5, \
command = lambda:show(".")).grid(row = 5, column = 2)
Button(root, text="=", bg = "lightyellow", width=5, \
command = calculate).grid(row = 5, column = 3)
root.mainloop()
二、GUI设计(中)
9.Radiobutton
选项按钮:可以用鼠标单击方式选取,一次只能有一个选项被选取。
radiobutton(父对象,options,…)
常用options参数:
- anchor,bg,bitmap,bd,command,cursor,fg,font,height,highlightbackground、highlightcolor,image,justify,padx,pady,state,text,textvariable,underline,width,wraplength;
- activebackground:鼠标光标放在选项按钮上时的背景色彩;
- activeforeground:鼠标光标放在选项按钮上时的前景色彩;
- indicatoron:当此值为0时,可以建立盒子选项按钮;
- selectcolor:当选项按钮被选取时的颜色;
- selectimage:如果设置图像选项按钮时,可由此设置当选项按钮被选取时的不同图像;
- value:选项按钮的值,可以区分所选取的选项按钮;
- variable:设置或取得目前选取的单选按钮,通常为IntVar或StringVar类型。
使用get()方法,可以传回所选选项的参数value的值,使用set()可以设置初始默认value选项。
9.1 示例
from tkinter import *
def printSelection():
label.config(text = "You are a " + var.get() + ".")
root = Tk()
root.title("Ex")
var = StringVar()
var.set("boy")
label = Label(root, text = "Please choose.", bg = "lightyellow", width = 30)
label.pack()
rbman = Radiobutton(root, text = "boy", variable = var, value = "boy", command = printSelection)
rbman.pack()
rbwoman = Radiobutton(root, text = "gril", variable = var, value = "gril", command = printSelection)
rbwoman.pack()
root.mainloop()
9.2 字典
from tkinter import *
cities = {0:"北京", 1:"哈尔滨", 2:"天津", 3:"西安", 4:"杭州"}
def printSelection():
print(cities[var.get()])
root = Tk()
root.title("Ex")
var = IntVar()
var.set(0)
label = Label(root, text = "选择最喜欢的城市", bg = "lightyellow", width = 30)
label.pack()
for val, city in cities.items():
Radiobutton(root, text = city, variable = var, value = val, command = printSelection).pack()
root.mainloop()
9.3 盒子的选项按钮
Radiobutton(…,indicatoron = 0,width = 30)
from tkinter import *
cities = {0:"北京", 1:"哈尔滨", 2:"天津", 3:"西安", 4:"杭州"}
def printSelection():
print(cities[var.get()])
root = Tk()
root.title("Ex")
var = IntVar()
var.set(0)
label = Label(root, text = "选择最喜欢的城市", bg = "lightyellow", width = 30)
label.pack()
for val, city in cities.items():
Radiobutton(root, text = city, variable = var, value = val, command = printSelection, indicatoron = 0, width = 30).pack()
root.mainloop()
10.Checkbutton
复选框,可以复选
Checkbutton(父对象,options,…)
options:
- …通用参数
- offvalue:控制变量,复选框未勾选时选value的值,默认为0;
- onvalue:控制变量,复选框勾选时,选value的值,默认为1;
from tkinter import *
cities = {0:"北京", 1:"哈尔滨", 2:"天津", 3:"西安", 4:"杭州"}
def printInfo():
selection = ""
for i in checkboxes:
if checkboxes[i].get() == True:
selection += cities[i] + "\t"
print(selection)
root = Tk()
root.title("Ex")
label = Label(root, text = "选择喜欢的城市", bg = "lightyellow", width = 30)
label.pack()
checkboxes = {}
for i in range(len(cities)):
checkboxes[i] = BooleanVar()
Checkbutton(root, text = cities[i], variable = checkboxes[i]).pack()
Button(root, text = "确定", width = 10, command = printInfo).pack()
root.mainloop()
11.容器
Frame可译为框架,LabelFrame即标签框架;
11.1 Frame
Frame(父对象,options,…) #父对象可以省略
options参数:
bg,bd,cursor,height,highlightbackground,highlightcolor,highlighthickness,relief,width
from tkinter import *
root = Tk()
root.title("Ex")
frameUpper = Frame(root, bg = "lightyellow", height = 30, width = 150)
frameUpper.pack()
btnRed = Button(frameUpper, text = "Red", fg = "red").pack(side = LEFT)
btnGreen = Button(frameUpper, text = "Green", fg = "green").pack(side = LEFT)
frameLower = Frame(root, bg = "lightblue", height = 30, width = 150)
frameLower.pack()
btnPurple = Button(frameLower, text = "Purple", fg = "purple").pack()
root.mainloop()
11.2 LabelFrame
LabelFrame(父对象,options,…)
bg,bd,cursor,font,height,highlightbackground,highlightcolor,highlighthickness,labelAnchor,relief,text,width
labf = LabelFrame(root, text = "选择喜欢的城市")
checkboxes = {}
for i in range(len(cities)):
checkboxes[i] = BooleanVar()
Checkbutton(labf, text = cities[i], variable = checkboxes[i]).pack()
labf.pack()
from tkinter import *
cities = {0:"北京", 1:"哈尔滨", 2:"天津", 3:"西安", 4:"杭州"}
def printInfo():
selection = ""
for i in checkboxes:
if checkboxes[i].get() == True:
selection += cities[i] + "\t"
print(selection)
root = Tk()
root.title("Ex")
labf = LabelFrame(root, text = "选择喜欢的城市")
checkboxes = {}
for i in range(len(cities)):
checkboxes[i] = BooleanVar()
Checkbutton(labf, text = cities[i], variable = checkboxes[i]).pack()
labf.pack()
Button(root, text = "确定", width = 10, command = printInfo).pack()
root.mainloop()
11.3 Toplevel
功能类似于Frame,但是这个控件所产生的容器有一个独立的窗口,有自己的标题栏和边框,可以制作对话框。
- bg,bd,cursor,fg,font,height,width
import random
from tkinter import *
root = Tk()
root.title("Ex")
msg = {1:"Yes", 2:"No", 3:"Exit"}
def MessageBox():
msgType = random.randint(1,3)
labTxt = msg[msgType]
tl = Toplevel()
tl.geometry("300x180")
tl.title("Message Box")
Label(tl, text = labTxt, bg = "lightgreen").pack(fill = BOTH, expand = True)
btn = Button(root, text = "Click Me", command = MessageBox)
btn.pack()
root.mainloop()
12.与数字有关
12.1 Scale
用移动尺度条产生某一范围的数字。
Scale(父对象,opitons,…)
- activebackground,bg,bd,command,cursor,fg,font,highlightbackground,highlightcolor,relief,state,width;
- digits:数值尺度,读取时需使用IntVar、DoubleVar、StringVar变量类型读取;
- from_:尺度条范围值的初值;
- label:便签文字,默认没有;
- length:默认100像素;
- orient:可设置HORIZONTAL或VERTICAL,默认水平;
- repeatdelay:可设置需要按住尺度条多久后才能移动此尺度条,单位是ms,默认值300;
- resolution:分辨率,数值间隔;
- showvalue:正常会显示尺度条的当前值,设为0以不显示;
- takefocus:正常时此尺度条可以循环取得焦点,设为0则无法取得焦点;
- tickinterval:尺度条的标记刻度分隔;
- to:尺度条范围值的终值;
- troughcolor:槽的色彩;
- variable:设置或取得目前的尺度值。
使用3个尺度条更改窗口背景色彩:
from tkinter import *
def bgUpdate(source):
"""更改窗口背景色彩"""
red = rSlider.get()
green = gSlider.get()
blue = bSlider.get()
#转换为十六进制
myColor = "#%02x%02x%02x" % (red, green, blue)
root.config(bg = myColor)
root = Tk()
root.title("Ex")
root.geometry("360x240")
rSlider = Scale(root, from_ = 0, to = 255, label = "R", \
resolution = 1, troughcolor = "red", command = bgUpdate)
gSlider = Scale(root, from_ = 0, to = 255, label = "G", \
resolution = 1, troughcolor = "green", command = bgUpdate)
bSlider = Scale(root, from_ = 0, to = 255, label = "B", \
resolution = 1, troughcolor = "blue", command = bgUpdate)
rSlider.pack(anchor = S, side = LEFT)
gSlider.pack(anchor = S, side = LEFT)
bSlider.pack(anchor = S, side = LEFT)
root.mainloop()
12.2 askcolor()
tkinter模块的colorchoose模块中有askcolor()方法,利用此方法可以开启“色彩”对话框。
from tkinter.colorchooser import *
from tkinter import *
def bgUpdate():
"""更改窗口背景色彩"""
myColor = askcolor()
root.config(bg = myColor[1])
root = Tk()
root.title("Ex")
root.geometry("360x240")
btn = Button(text = "Slect color", command = bgUpdate).pack()
root.mainloop()
12.3 Spinbox
数值输入。
Spinbox(父对象,options,…)
- activebackground,bg,bd,command,disablebackground,disableforeground,fg,font,justify,relief,state,textvariable,width;
- format:格式化的字符串;
- from_:范围值的初值;
- increment:每次点击up/down按钮的增量;
- repeatdalay:可以单击up/down的时间间隔,单位是ms,默认300;
- values:可以是元组或其他序列值;
- to:范围的终值;
- wrap:单击up/down可以让数值重新开始;
- xscrollcommand:在x轴使用滚动条。
from tkinter import *
def printInfo():
print(spin.get())
root = Tk()
root.title("Ex")
root.geometry("300x100")
spin = Spinbox(root, from_ = 10, to = 30, increment = 2, \
command = printInfo)
spin.pack()
root.mainloop()
序列存储:
将数值存储在序列数据中,单击up/down时相当于观察元组或列表中的值
spin = Spinbox(root, values = (10, 30, 170, 100), command = printInfo)
from tkinter import *
def printInfo():
print(spin.get())
root = Tk()
root.title("Ex")
root.geometry("300x100")
spin = Spinbox(root, values = (10, 30, 170, 100), command = printInfo)
spin.pack()
root.mainloop()
也可以存储非数值数据:
spin = Spinbox(root, values = ("北京", "哈尔滨", "天津"), command = printInfo)
from tkinter import *
def printInfo():
print(spin.get())
root = Tk()
root.title("Ex")
root.geometry("300x100")
spin = Spinbox(root, values = ("北京", "哈尔滨", "天津"), command = printInfo)
spin.pack()
root.mainloop()
13.消息提示
13.1 Message
主要用于显示短消息,与Label功能和用法类似,但是使用更加灵活。
Message(父对象,options)
- anchor,bg,bitmap,cursor,fg,font,height,image,justify,padx/pady,relief,text,textvariable,underline,width,wraplength;
- aspect:控件宽度与高度比,默认150%。
13.2 Messagebox
tkinter模块内有Messagebox模块,提供了8个对话框;
-
showinfo(title, message):显示一般提示信息;
-
showwarning(title, message):显示警告信息;
-
showwarning(title, message):显示警告信息;
-
askquestion(title, message):显示询问消息,传回"yes"或"no";
-
askokcancel(title, message):显示确定或取消消息,传回True或False;
-
askyesno(title, message):显示是或否,传回True或False;
-
askyesnocancel(title, message):显示是或否或取消,传回True或Flase或None;
-
askretrycancel(title, message):显示重试或取消,传回True或False。
提示:title是对话框的名称,message是对话框内的文字。
from tkinter import *
def myMsg():
messagebox.showinfo("My message Box","Python Tkinter 早安")
root = Tk()
root.title("Ex")
root.geometry("300x160")
Button(root, text = "Good Morning", command = myMsg).pack()
root.mainloop()
出现错误:
PS E:\py\python3.7\test2\test58GUI> python3 .\GUI.py
Exception in Tkinter callback
Traceback (most recent call last):
File "E:\py\python3.7\Python37\lib\tkinter\__init__.py", line 1702, in __call__
return self.func(*args)
File ".\GUI.py", line 765, in myMsg
messagebox.showinfo("My message Box","Python Tkinter 早安")
NameError: name 'messagebox' is not defined
原因分析:
messagebox输入子模块,不会默认导入,然后在开头加入了messagebox的导入,程序就可以正常执行了
from tkinter import *
from tkinter import messagebox
def myMsg():
# messagebox.showinfo("My message Box","Python Tkinter 早安")
# messagebox.showwarning("My message Box","Python Tkinter 早安")
# messagebox.showerror("My message Box","Python Tkinter 早安")
# messagebox.askquestion("My message Box","Python Tkinter 早安")
# messagebox.askokcancel("My message Box","Python Tkinter 早安")
# messagebox.askyesno("My message Box","Python Tkinter 早安")
# messagebox.askyesnocancel("My message Box","Python Tkinter 早安")
messagebox.askretrycancel("My message Box","Python Tkinter 早安")
root = Tk()
root.title("Ex")
root.geometry("300x160")
Button(root, text = "Good Morning", command = myMsg).pack()
root.mainloop()
将用户相应传回,在窗口中列出相应内容:
def myMsg():
ret = messagebox.askyesnocancel("My message Box", "是或否或取消?")
print(ret)
利用传回值可以进行进一步操作。
14.事件和绑定
其实GUI是一种事件导向的应用程序设计,事件的来源可能是用户点击鼠标,键盘输入或是Widget状态的改变,tkinter提供一些机制让我们可以针对这些事件做更进一步的处理,这些处理方式称为事件处理程序。
14.1 事件绑定
前面介绍的command参数其实就是一种事件绑定,command= callback就是事件处理程序。在tkinter应用程序中最后一个指令是mainloop(),这个方法是让程序进入事件等待循环,tkinter也提供了为事件绑定处理程序的机制,它的语法为:
widget.bind(event,handler)
widget是事件的来源,可以是root窗口对象,或是任意的Widget控件,handler是事件处理程序。
有了以上事件绑定后,就可以自行设计事件绑定效果了,若是在Button内省略command参数,可以用以下方法重建:
btn.bind(“”, event_handler)
鼠标绑定的基本应用:
from tkinter import *
from tkinter import messagebox
def callback(event):
print("Clicked at", event.x, event.y)
root = Tk()
root.title("Ex")
root.geometry("300x160")
root.bind("<Button-1>", callback)
root.mainloop()
移动鼠标时在窗口下方显示鼠标目前的坐标:
from tkinter import *
def mouseMotion(event):
x = event.x
y = event.y
textvar = "Mouse location - x:{}, y:{}".format(x,y)
var.set(textvar)
root = Tk()
root.title("Ex")
root.geometry("300x160")
x, y = 0, 0
var = StringVar()
text = "Mouse location - x:{}, y:{}".format(x,y)
var.set(text)
lab = Label(root, textvariable = var)
lab.pack(anchor = S, side = RIGHT, padx = 10, pady = 10)
root.bind("<Motion>", mouseMotion)
root.mainloop()
鼠标进入/离开Exit功能:
from tkinter import *
def enter(event):
x.set("鼠标进入Exit功能按钮")
def leave(event):
x.set("鼠标离开Exit功能按钮")
root = Tk()
root.title("Ex")
root.geometry("300x160")
btn = Button(root, text = "Exit", command = root.destroy)
btn.pack(pady = 30)
btn.bind("<Enter>", enter)
btn.bind("<Leave>", leave)
x = StringVar()
lab = Label(root, textvariable = x, bg = "yellow", fg = "blue", \
height = 5, width = 20)
lab.pack(pady = 20)
root.mainloop()
键盘ESC键测试:
from tkinter import *
from tkinter import messagebox
def leave(event):
ret = messagebox.askyesno("Exit", "是否离开?")
if ret == True:
root.destroy()
else:
return
root = Tk()
root.title("Ex")
root.geometry("300x160")
root.bind("<Escape>",leave)
lab = Label(root, text = "Esc键测试", bg = "yellow", fg = "blue")
lab.pack(pady = 30)
root.mainloop()
a-z按键测试:
from tkinter import *
from tkinter import messagebox
def key(event):
print("按了 " + repr(event.char) + " 键")
root = Tk()
root.title("Ex")
root.geometry("300x160")
root.bind("<Key>",key)
lab = Label(root, text = "a~z键测试", bg = "yellow", fg = "blue")
lab.pack(pady = 30)
root.mainloop()
注意:当事件绑定与Frame有关时,必须在Frame获得焦点时,键盘绑定才可生效。
from tkinter import *
from tkinter import messagebox
def key(event):
print("按了 " + repr(event.char) + " 键")
def coordXY(event):
frame.focus_set()
print("鼠标坐标:", event.x, event.y)
root = Tk()
root.title("Ex")
root.geometry("300x160")
frame = Frame(root, width = 100, height = 100, bg = "blue")
frame.bind("<Key>",key)
frame.bind("<Button-1>", coordXY)
frame.pack()
lab = Label(frame, text = "a~z键测试", bg = "yellow", fg = "blue")
lab.pack(padx = 30, pady = 30)
root.mainloop()
14.2 取消绑定
boj.unbind(“”)
通过复选框控制是否绑定:
from tkinter import *
from tkinter import messagebox
def buttonClicked(event):
print("I like tkinter")
def toggle(onoff):
if var.get() == True:
onoff.bind("<Button-1>", buttonClicked)
else:
onoff.unbind("<Button-1>")
root = Tk()
root.title("Ex")
root.geometry("300x160")
btn = Button(root, text = "tkinter")
btn.pack(anchor = W, padx = 10, pady = 10)
var = BooleanVar()
cbtn = Checkbutton(root, text = "bind/unbind", variable = var, \
command = lambda:toggle(btn))
cbtn.pack(anchor = W, padx = 10)
root.mainloop()
14.3 绑定多个事件处理程序
tkinter也允许我们将一个事件绑定多个事件处理程序,同样是使用bind()方法,但是新增加的事件处理程序需要在bind()方法内增加参数add = “+”
from tkinter import *
from tkinter import messagebox
def buttonClicked1():
print("Command event handler, I like tkinter")
def buttonClicked2(event):
print("Bind event handler, I like tkinter")
root = Tk()
root.title("Ex")
root.geometry("300x160")
btn = Button(root, text = "tkinter", command = buttonClicked1)
btn.pack(anchor = W, padx = 10, pady = 10)
btn.bind("<Button-1>", buttonClicked2, add = "+")
root.mainloop()
bind()程序先被执行,command后执行;
14.4 Protocols
Protocols为窗口管理程序与应用程序之间的通信协议。
单击image按钮可以关闭窗口,它的名称是WM_DELETE_WINDOW,使用程序修改此协议,改为单击此按钮后弹出Messagebox,询问结束或取消,若单击确定才会结束此程序。
from tkinter import *
from tkinter import messagebox
def callback():
res = messagebox.askokcancel("Exit", "确定要退出吗?")
if res == True:
root.destroy()
else:
return
root = Tk()
root.title("Ex")
root.geometry("300x160")
root.protocol("WM_DELETE_WINDOW", callback)
root.mainloop()
15.Listbox
列表框(Listbox)是一个显示一系列选项的Widget控件,用户可以进行单项或多项的选择。
Listbox(父对象,options,…)
- bg,bd,cursor,fg,font,height,highlightcolor,highlightthickness,relief,width;
- listvariable:以变量方式处理选项内容;
- selectbackground:被选取字符串的背景色彩;
- selectmode:决定有多少个选项可以被选取,以及鼠标拖曳如何影响选项;
1)BROWSE:默认值,可以选择一个选项,如果选取一个选项同时拖曳鼠标,将由鼠标释放的位置决定被选取的选项;
2)SINGLE:只能选择一个选项,可以用单击方式选取,不可用拖曳方式更改所选的项目;
3)MULTIPLE:可以选择多个选项,单击项目切换是否选取该项目;
4)EXTENDED:按住Ctrl可以多选,单击第一个项目然后拖曳到最后一个项目,即可选取这个区间的一系列选项,单击一个选项并按住Shift单击另一个选项可以选取区间项目; - xscrollcommand:在x轴使用滚动条;
- yscrollcommand:在y轴使用滚动条。
15.1 insert()
可以使用insert()方法为列表框建立项目。
insert(index, element)
index是项目插入位置,如果是在最后面可以使用END。
from tkinter import *
from tkinter import messagebox
fruits = ["Banana", "Watermelon", "Pineapple", \
"Orange", "Grapes", "Mango"]
root = Tk()
root.title("Ex")
root.geometry("300x160")
lb = Listbox(root, selectmode = MULTIPLE)
for fruit in fruits:
lb.insert(END, fruit)
lb.pack(pady = 10)
root.mainloop()
15.2 Listbox()基本操作
-
size():传回列表项目的数量
print(“items数目”, lb.size()) -
selection_set():选取特定索引项
lb.selection_set(0)
设定初始选取项目为第一个。
lb.selection_set(0, 3)
设定初始选取项目为前3个。 -
delete():删除特定索引项
lb.delete(0)
lb.delete(0, 3) -
get():传回指定索引项
print(lb.get(0)) -
curselection():传回选取项目的索引
打印所选取的项目。 -
selection_includes():检查指定索引是否被选取
def callback():
print(lb.select_includes(0))
from tkinter import *
from tkinter import messagebox
def callback():
indexs = lb.curselection()
for index in indexs:
print(lb.get(index))
fruits = ["Banana", "Watermelon", "Pineapple", \
"Orange", "Grapes", "Mango"]
root = Tk()
root.title("Ex")
root.geometry("300x300")
lb = Listbox(root, selectmode = EXTENDED)
for fruit in fruits:
lb.insert(END, fruit)
lb.pack(pady = 10)
btn = Button(root, text = "Print", command = callback)
btn.pack(pady = 5)
root.mainloop()
15.3 绑定事件
当Listbox执行选取操作时会产生<>虚拟事件,可以由此设置时间处理程序。
当选择Listbox中的项目时,在上方列出所选的项目:
from tkinter import *
from tkinter import messagebox
def itemSelected(event):
obj = event.widget
index = obj.curselection()
var.set(obj.get(index))
fruits = ["Banana", "Watermelon", "Pineapple", \
"Orange", "Grapes", "Mango"]
root = Tk()
root.title("Ex")
root.geometry("300x300")
var = StringVar()
lab = Label(root, text = "", textvariable = var)
lab.pack(pady = 5)
lb = Listbox(root, selectmode = SINGLE)
for fruit in fruits:
lb.insert(END, fruit)
lb.pack(pady = 10)
lb.bind("<<ListboxSelect>>", itemSelected)
root.mainloop()
15.4 增删项目示例
Entry是输入控件,可以输入项目名称,输入完成后单击“增加”按钮可以将此项目加入Listbox,同时Entry被清空。若是选择Listbox内的项目后单击“删除”,就可以从Listbox删除所选项目。
from tkinter import *
from tkinter import messagebox
def itemAdded():
varAdd = entry.get()
if len(varAdd.strip()) == 0:
return
lb.insert(END, varAdd)
entry.delete(0, END)
def itemDeleted():
index = lb.curselection()
if len(index) == 0:
return
lb.delete(index)
root = Tk()
root.title("Ex")
entry = Entry(root)
entry.grid(row = 0, column = 0, padx = 5, pady = 5)
btnAdd = Button(root, text = "增加", width = 10, command = itemAdded)
btnAdd.grid(row = 0, column = 1, padx = 5, pady = 5)
lb = Listbox(root)
lb.grid(row = 1, column = 0, columnspan = 2, padx = 5, pady = 5, stick = W)
btnDel = Button(root, text = "删除", width = 10, command = itemDeleted)
btnDel.grid(row = 2, column = 0, padx = 5, pady = 5, stick = W)
root.mainloop()
15.5 项目排序
from tkinter import *
from tkinter import messagebox
def itemSorted():
listTmp = list(lb.get(0, END))
sortedList = sorted(listTmp, reverse = var.get())
lb.delete(0, END)
for item in sortedList:
lb.insert(END, item)
fruits = ["Banana", "Watermelon", "Pineapple", \
"Orange", "Grapes", "Mango"]
root = Tk()
root.title("Ex")
lb = Listbox(root)
for fruit in fruits:
lb.insert(END, fruit)
lb.pack(padx = 10, pady = 5)
btn = Button(root, text = "排序", command = itemSorted)
btn.pack(side = LEFT, padx = 10, pady = 5)
var = BooleanVar()
cb = Checkbutton(root, text = "倒序", variable = var)
cb.pack(side = LEFT)
root.mainloop()
15.6 拖曳项目
from tkinter import *
from tkinter import messagebox
def getIndex(event):
lb.index = lb.nearest(event.y) #目前选项的索引
def dragJob(event):
newIndex = lb.nearest(event.y)
x = lb.get(lb.index)
lb.delete(lb.index)
lb.insert(newIndex, x)
lb.selection_set(newIndex)
lb.index = newIndex
fruits = ["Banana", "Watermelon", "Pineapple", \
"Orange", "Grapes", "Mango"]
root = Tk()
root.title("Ex")
lb = Listbox(root)
for fruit in fruits:
lb.insert(END, fruit)
lb.bind("<Button-1>", getIndex)
lb.bind("<B1-Motion>", dragJob) #拖曳动作
lb.pack(padx = 10, pady = 5)
root.mainloop()
15.7 Scrollbar
在默认的情况下Listbox是没有滚动条的,如果选项太多将造成部分选项无法显示,此时可将滚动条Scrollbar加入Listbox。
另外,滚动条也可以应用在Text和Canvas控件上。
Scrollbar(父对象,options,…)
- bg,bd,cursor,orient,width;
- activebackground:当光标经过滚动条时,滚动条和方向箭头的色彩;
- command:滚动条移动时所触发的方法;
- elementborderwidth:滚动条和方向箭头的外部宽度,默认为1;
- highlightbackground;
- highlightcolor:当滚动条获得焦点时的色彩;
- highlightthickness;
- jump:默认为0,每次拖曳滚动条都会触发command方法,如果设为1则只有放开鼠标时才会触发command方法;
- repeatdalay:设置按住滚动条移动的停滞时间,单位是ms,默认值300;
- takefocus:正常可以用Tab键的方式切换滚动条成为焦点,设为0可以取消此设置;
- troughcolor:滚动条色槽的颜色。
from tkinter import *
from tkinter import messagebox
root = Tk()
root.title("Ex")
scrollbar = Scrollbar(root)
scrollbar.pack(side = RIGHT, fill = Y)
# 将Listbox与滚动条做连动
lb = Listbox(root, yscrollcommand = scrollbar.set)
for i in range(50):
lb.insert(END, "Line " + str(i+1))
lb.pack(side = LEFT, fill = BOTH, expand = True)
scrollbar.config(command = lb.yview)
root.mainloop()
16.OptionMenu
下拉式列表,用户可以从中选择一项。
OptionMenu(父对象,var,*value)
其中*value是一系列下拉列表。
使用set可以建立默认选项。
from tkinter import *
from tkinter import messagebox
root = Tk()
root.title("Ex")
root.geometry("250x100")
omTuple = ("Python", "Matlab", "C")
var = StringVar()
var.set("Python")
optionmenu = OptionMenu(root, var, *omTuple)
optionmenu.pack()
root.mainloop()
17.Combobox
组合框,tkinter.ttk中的控件,特性与OptionMenu类似,是Entry与下拉菜单的组合。
Combobox(父对象,options)
- textvariable:可以设置Combobox的变量值;
- value:Combobox的选项内容,以元组方式存在。
使用current()设置默认选项。
from tkinter import *
from tkinter import messagebox
from tkinter.ttk import *
root = Tk()
root.title("Ex")
root.geometry("250x100")
var = StringVar()
cb = Combobox(root, textvariable = var)
cb["value"] = ("Python", "Matlab", "C")
cb.current(0)
cb.pack()
root.mainloop()
当Combobox中的选项有变动时,会产生<>虚拟事件,可以用此特性绑定事件处理方法。
18.PanedWindow
面板,可以在此容器内建立任意数量的子控件。
PanedWindow(父对象,options,…)
- bg,bd,borderwidth,cursor,height,orient(默认HORIZONTAL),relief(默认FLAT),width;
- handlepad:面板显示宽度,默认为8;
- handlesize:面板显示大小,默认为8;
- sashcursor:分割线光标;
- sashrelief:面板分割线外框,默认RAISED;
- showhandle:滑块属性,可设定是否显示。
18.1 add()
使用add(child, options)可以插入子对象。
from tkinter import *
from tkinter import messagebox
root = Tk()
root.title("Ex")
pw = PanedWindow(orient = VERTICAL)
pw.pack(fill = BOTH, expand = True)
top = Label(pw, text = "Top Pane")
pw.add(top)
bottom = Label(pw, text = "Bottom Pane")
pw.add(bottom)
root.mainloop()
18.2 LabelFrame子对象
常见的应用是将PanedWindow分成两三份,然后可以将所设计的控件适度分配位置。
from tkinter import *
from tkinter import messagebox
root = Tk()
root.title("Ex")
pw = PanedWindow(orient = HORIZONTAL)
pw.pack(fill = BOTH, expand = True)
leftframe = LabelFrame(pw, text = "Left Pane", width = 120, height = 150)
pw.add(leftframe)
rightframe = LabelFrame(pw, text = "Right Pane", width = 100, height = 150)
pw.add(rightframe)
root.mainloop()
使用ttk中的weight参数可以设置更改窗口大小时各个对象缩放的比例,都设为1以同比例缩放。
from tkinter.ttk import *
pw.add(leftframe, weight = 1)
pw.add(rightframe, weight = 1)
19.Notebook
tkinter.ttk中的控件。
也是一个容器控件,它的特点是可以有许多选项卡,当选择不同选项卡时可以看到不同的子控件内容。
Notebook(父对象,options)
- height,width;
- padding:设置Notebook外围的额外空间,可以设置4个数值代表left、top、right、bottom。
使用Notebook()建立框架的步骤如下:
- 建立Notebook对象,名为notebook;
- 调用add子方法插入子对象并添加选项卡add(子对象, text = “xxx”);
- 正常add(子对象, options).
options:
- compound:设置当选项卡内同时含 图像和文字时,彼此之间的位置关系;
- image:选项卡以图像方式 呈现;
- padding:设置面板的额外空间;
- state:默认normal,设为disable表示无法使用,设为hidden表示隐藏;
- sticky:指出子窗口面板的配置方式,n/s/e/w;
- text:选项卡中的字符串内容;
- underline。
from tkinter import *
from tkinter.ttk import *
from tkinter import messagebox
def msg():
messagebox.showinfo("Notebook", "欢迎使用Notebook")
root = Tk()
root.title("Ex")
root.geometry("300x160")
notebook = Notebook(root)
frame1 = Frame()
frame2 = Frame()
label = Label(frame1, text = "Tkinter")
label.pack(padx = 10, pady = 10)
btn = Button(frame2, text = "Help", command = msg)
btn.pack(padx = 10, pady = 10)
notebook.add(frame1, text = "标签页1")
notebook.add(frame2, text = "标签页2")
notebook.pack(padx = 10, pady = 10, fill = BOTH, expand = True)
root.mainloop()
二、GUI设计(下)
20.Progressbar
进度条,主要用来当做一个工作进度指针,在这个控件中会有一个指针,由此指针可以了解工作进度。
Progressbar(父对象,options,…)
- length:进度条的长度,默认100像素;
- mode:两种模式;
1)determinate:默认,一个指针会从起点移至终点,通常当我们知道所需工作时间时,可以使用此模式;
2)indeterminate:一个指针会在起点和终点之间来回移动,通常当我们不知道工作所需时间时,可以使用此模式; - maximum:进度条的最大值,默认是100;
- name:进度条的名称,供程序参考引用;
- orient:进度条的方向;
- value:进度条的当前值;
- variable:记录进度条当前值的变量。
使用update方法更新value值,可以达到动画效果。
Progressbar的方法:
- start(interval):每隔interval时间移动一次指针,默认50ms,每次指针移动时调用一次step(delta);
- step(delta):每次增加delta,默认值1.0;
- stop():停止start()的运行。
21.Menu
Menu(父对象,options,…)
- bd,bg,cursor,font,fg;
- activebackground:当光标移至此菜单列表上时的背景色彩;
- activeborderwidth:当光标移至此菜单列表上时的前景色彩;
- disabledforeground:菜单列表是DISABLED时的色彩;
- image:菜单的图标;
- tearoff:菜单上方的分割线,True或False;
- add_cascade():建立分层菜单,同时让此子功能列表与父菜单建立链接;
- add_command():增加菜单列表;
- add_separator():增加菜单列表的分割线。
from tkinter import *
from tkinter.ttk import *
from tkinter import messagebox
def hello():
messagebox.showinfo("Hello", "欢迎使用菜单")
root = Tk()
root.title("Ex")
root.geometry("300x160")
menubar = Menu(root)
menubar.add_command(label = "Hello", command = hello)
menubar.add_command(label = "Exit", command = root.destroy)
root.config(menu = menubar)
root.mainloop()
21.1 下拉菜单
from tkinter import *
from tkinter.ttk import *
from tkinter import messagebox
def File():
messagebox.showinfo("File", "New File")
root = Tk()
root.title("Ex")
root.geometry("300x160")
menubar = Menu(root)
filemenu = Menu(menubar, tearoff = False)
menubar.add_cascade(label = "File", menu = filemenu)
filemenu.add_command(label = "New File", command = File)
filemenu.add_command(label = "Exit", command = root.destroy)
root.config(menu = menubar)
root.mainloop()
分割线:在add_command()中间穿插使用filemenu.add_separator()即可。
21.2 Alt快捷键
快捷键是在某个菜单类别或列表指定的英文字符串内为单一字母增加下划线,然后可以用Alt键先启动根菜单,在菜单中可以直接按字母键启动相应功能。
设计方法是在add_command()和add_cascade()中使用underline参数。
menubar.add_cascade(label = "File", menu = filemenu, underline = 0)
filemenu.add_command(label = "New File", command = File, underline = 0)
filemenu.add_command(label = "Exit", command = root.destroy, underline = 0)
21.3 Ctrl快捷键
filemenu.add_command(label = "New File", command = File, underline = 0, \
accelerator = "Ctrl+N")
…
root.bind("<Control-n>", \
lambda event:messagebox.showinfo("File", "New File"))
21.4 弹出式菜单
右键快捷菜单。
建立Menu对象后直接与右键绑定即可。
from tkinter import *
from tkinter.ttk import *
from tkinter import messagebox
def minimizeIcon():
root.iconify()
def showPopupMenu(event):
popupmenu.post(event.x_root, event.y_root)
root = Tk()
root.title("Ex")
root.geometry("300x160")
popupmenu = Menu(root, tearoff = False)
popupmenu.add_command(label = "Minimize", command = minimizeIcon)
popupmenu.add_command(label = "Exit", command = root.destroy)
root.bind("<Button-3>", showPopupMenu)
root.mainloop()
21.5 add_checkbutton()
from tkinter import *
from tkinter.ttk import *
from tkinter import messagebox
def status():
if demoStatus.get():
statusLabel.pack(side = BOTTOM, fill = X)
else:
statusLabel.pack_forget()
root = Tk()
root.title("Ex")
root.geometry("300x160")
menubar = Menu(root)
filemenu = Menu(menubar, tearoff = False)
menubar.add_cascade(label = "File", menu = filemenu)
filemenu.add_command(label = "Exit", command = root.destroy)
viewmenu = Menu(menubar, tearoff = False)
menubar.add_cascade(label = "View", menu = viewmenu)
demoStatus = BooleanVar()
demoStatus.set(True)
viewmenu.add_checkbutton(label = "Status", command = status, \
variable = demoStatus)
root.config(menu = menubar)
statusVar = StringVar()
statusVar.set("显示")
statusLabel = Label(root, textvariable = statusVar, relief = "raised")
statusLabel.pack(side = BOTTOM, fill = X)
root.mainloop()
22.Text
Text控件可以看做是Entry的扩充,可以处理多行输入,也可以在文字中嵌入图像或提供格式化功能,实际上可以将Text当做文字处理软件,甚至当做网页浏览器。
Text(父对象,options,…)
- bg,bd,cursor,fg,font,height,highlightbackground,- highlightcolor,highlightthickness,padx,pady,relief,selectbackground,selectborderwidth,selectforeground,width,wrap,xscrollcommand,yscrollcommand;
- exportselection:执行选择操作时,所选择的字符串会自动输出至剪贴板,设置exportselection=0以避免;
- insertbackground:插入光标的色彩,默认是黑色;
- insertborderwidth:围绕插入游标的3D厚度,默认为0;
- state:默认NORMAL,DISABLED则是无法编辑;
- tab:可设置按Tab键时,如何定位插入点。
22.1 insert()
使用insert(index, string)可以将字符串插入到指定位置。
from tkinter import *
from tkinter.ttk import *
from tkinter import messagebox
root = Tk()
root.title("Ex")
text = Text(root, height = 3, width = 30)
text.pack()
text.insert(END, "Python\nTkinter\n")
text.insert(END, "I like you.")
root.mainloop()
22.2 Scrollbar
from tkinter import *
from tkinter.ttk import *
from tkinter import messagebox
root = Tk()
root.title("Ex")
yscrollbar = Scrollbar(root)
yscrollbar.pack(side = RIGHT, fill = Y)
text = Text(root, height = 5, width = 30)
text.pack()
yscrollbar.config(command = text.yview)
text.config(yscrollcommand = yscrollbar.set)
string = """Tkinter模块("Tk 接口")是Python的标准Tk GUI工具包的接口.
Tk和Tkinter可以在大多数的Unix平台下使用,同样可以应用在Windows和
Macintosh系统里.Tk8.0的后续版本可以实现本地窗口风格,并良好地运行
在绝大多数平台中."""
text.insert(END, string)
root.mainloop()
是Text文字区域随着窗口的扩充而扩充:
text.pack(fill = BOTH, expand = True)
22.3 字形
- family:Arial(默认)、Times、Courier;
- weight:norma(默认)、bold;
- size:字号。
from tkinter import *
from tkinter.ttk import *
from tkinter.font import Font
from tkinter import messagebox
def familyChanged(event):
f = Font(family = familyVar.get())
text.configure(font = f)
def weightChanged(event):
f = Font(weight = weightVar.get())
text.configure(font = f)
def sizeSelected(event):
f = Font(size = sizeVar.get())
text.config(font = f)
root = Tk()
root.title("Ex")
root.geometry("300x160")
toolbar = Frame(root, relief = RAISED, borderwidth = 1)
toolbar.pack(side = TOP, fill = X, padx = 2, pady = 1)
familyVar = StringVar()
familyFamily = ("", "Arial", "Times", "Courier")
familyVar.set(familyFamily[1])
family = OptionMenu(toolbar, familyVar, *familyFamily, command = familyChanged)
family.pack(side = LEFT, pady = 2)
weightVar = StringVar()
weightFamily = ("", "normal", "bold")
weightVar.set(weightFamily[1])
weight = OptionMenu(toolbar, weightVar, *weightFamily, command = weightChanged)
weight.pack(side = LEFT, pady = 2)
sizeVar = IntVar()
size = Combobox(toolbar, textvariable = sizeVar)
sizeFamily = [x for x in range(8, 31)]
size["value"] = sizeFamily
size.current(4)
size.bind("<<ComboboxSelected>>", sizeSelected)
size.pack(side = LEFT, pady = 2)
text = Text(root)
text.pack(fill = BOTH, expand = True, padx = 2, pady = 2)
text.focus_set()
root.mainloop()
22.4 选取文字
在使用Text文字区域时,如果有选取文字的操作发生时,Text对象会将所选文字的起始索引放在SEL_FIRST,结束索引放在SEL_LAST,将它们当做get()的参数,就可以获得目前所选的文字。
from tkinter import *
from tkinter.ttk import *
from tkinter.font import Font
from tkinter import messagebox
def selectedText():
try:
selText = text.get(SEL_FIRST, SEL_LAST)
print("选取文字:", selText)
except TclError:
print("没有选取文字!")
root = Tk()
root.title("Ex")
root.geometry("300x160")
btn = Button(root, text = "Print selection", command = selectedText)
btn.pack(pady = 3)
text = Text(root)
text.pack(fill = BOTH, expand = True, padx = 3, pady = 3)
text.insert(END, "Love You Like A Love Song")
root.mainloop()
认识Text的索引:
Text对象的索引并不是单一数字,而是一个字符串,索引的目的是让Text控件处理更进一步的文件操作。
- line/column(“line.column”):计数方式line从1开始。column从0开始,中间用句点分隔;
- INSERT:目前插入点的位置;
- CURRENT:光标目前为止相对于字符的位置;
- END:缓冲区最后一个字符的位置;
- Expression:索引使用表达式;
1)“+count chars”:count是数字,例如"+2c"索引往后移动两个字符;
2)“-count chars”:count是数字,例如"-2c"索引往前移动两个字符。
22.5 书签
在编辑文件时,可以在文件特殊位置建立书签,方便查询。书签是无法显示的,但会在编辑系统内被记录,如果书签内容被删除,则此书签也将自动被删除。
- index(mark):传回指定书签的line和column;
- mark_names():传回这个Text对象所有的书签;
- mark_set(mark, index):在指定的index位置设置书签;
- mark_unset(mark):取消指定书签。
text.mark_set(“mark1”, “1.9”)
text.mark_set(“mark2”, “1.13”)
print(text.get(“mark1”, “mark2”))
22.6 标签
标签是一个文字区域,我们可以为这个区域去一个名字,有了标签后,我们可以针对此标签做更进一步的工作,例如,将字形、色彩等应用在此标签上。
-
tag_add(tagname, startindex[, endindex] …):将startindex和endindex之间的文字命名为tagname标签;
-
tag_config(tagname, options, …):可以为标签执行特定的编辑,或动作绑定;
1)background,borderwidth,font,foreground,justify;
2)overstrike:如果为True,加上删除线;
3)underline:如果为True,加上下划线;
4)wrap:当使用wrap模式时,可以使用NONE、CHAR、WORD; -
tag_delete(tagname):删除此标签,同时会移除此标签特殊的编辑或绑定;
-
tag_remove(tagname[, startindex[, endindex]] …):删除标签,但是不移除此标签特殊的编辑或绑定。
除了可以使用tag_add()自行定义标签外,系统还有一个内建标签SEL,代表所选取的区间。
text.tag_add(“tag1”, “mark1”, “mark2”)
text.tag_config(“tag1”, foreground = “blue”, background = “pink”)
22.7 Cut/Copy/Paste
编辑文件时剪切/复制/粘贴(Cut/Copy/Paste)是很常用的功能,这些功能已经被内建在tkinter中了。
设计弹出菜单。
from tkinter import *
from tkinter.ttk import *
from tkinter.font import Font
from tkinter import messagebox
def cutJob():
copyJob()
text.delete(SEL_FIRST, SEL_LAST)
def copyJob():
try:
text.clipboard_clear() #清除剪切板
copyText = text.get(SEL_FIRST, SEL_LAST)
text.clipboard_append(copyText)
except TclError:
print("没有选取")
def pasteJob():
try:
copyText = text.clipboard_get()
text.insert(INSERT, copyText)
except:
print("剪贴板没有数据")
def showPopupMenu(event):
popupmenu.post(event.x_root, event.y_root)
root = Tk()
root.title("Bouncing Ball")
root.geometry("300x180")
popupmenu = Menu(root, tearoff = False)
popupmenu.add_command(label = "Cut", command = cutJob)
popupmenu.add_command(label = "Copy", command = copyJob)
popupmenu.add_command(label = "Paste", command = pasteJob)
root.bind("<Button-3>", showPopupMenu)
text = Text(root)
text.pack(fill = BOTH, expand = True, padx = 3, pady = 2)
text.insert(END, "Five Hundred Miles\n")
text.insert(END, "If you miss the train I'm on,\n")
text.insert(END, "You will know that I am gone.\n")
text.insert(END, "You can hear the whistle blow\n")
text.insert(END, "A hundred miles.")
root.mainloop()
由于没有选取文字或剪贴板没有数据就进行读取会发生TclError,所以设计时增加了try… except设计。
直接引用tkinter的虚拟事件:
def cutJob():
text.event_generate("<<Cut>>")
def copyJob():
text.event_generate("<<Copy>>")
def pasteJob():
text.event_generate("<<Paste>>")
22.8 undo/redo
def undoJob():
try:
text.edit_undo()
except:
print("先前没有动作")
def redoJob():
try:
text.edit_redo()
except:
print("先前没有动作")
toolbar = Frame(root, relief = RAISED, borderwidth = 1)
toolbar.pack(side = TOP, fill = X, padx = 2, pady = 1)
undoBtn = Button(toolbar, text = "Undo", command = undoJob).pack(side = LEFT)
redoBtn = Button(toolbar, text = "Redo", command = redoJob).pack(side = LEFT)
text = Text(root, undo = True)
…
22.9 查找文字
在Text控件内可以使用search()方法查找特定的字符串。
pos = text.search(key, startindex, endindex)
- pos:传回所到的字符串的索引位置,如果查找失败则传回空字符串;
- key:所查找的字符串;
- startindex:查找起始位置;
- endindex:查找结束位置,如果查找到文档末尾可以使用END。
from tkinter import *
from tkinter.ttk import *
from tkinter.font import Font
from tkinter import messagebox
def mySearch():
text.tag_remove("found", "1.0", END)
start = "1.0"
key = entry.get()
if len(key.strip()) == 0:
return
while True:
pos = text.search(key, start, END)
if pos == "":
break
text.tag_add("found", pos, "%s+%dc" % (pos, len(key)))
start = "%s+%dc" % (pos, len(key))
root = Tk()
root.title("Bouncing Ball")
root.geometry("300x180")
root.rowconfigure(1, weight = 1)
root.columnconfigure(0, weight = 1)
btn = Button(root, text = "查找", command = mySearch)
btn.grid(row = 0, column = 1, padx = 5, pady = 5)
entry = Entry()
entry.grid(row = 0, column = 0, padx = 5, pady = 5, \
sticky = W+E)
text = Text(root, undo = True)
text.grid(row = 1, column = 0, columnspan = 2, padx = 3, pady = 5, \
sticky = N+S+W+E)
text.insert(END, "Five Hundred Miles\n")
text.insert(END, "If you miss the train I'm on,\n")
text.insert(END, "You will know that I am gone.\n")
text.insert(END, "You can hear the whistle blow\n")
text.insert(END, "A hundred miles.")
text.tag_configure("found", background = "yellow")
root.mainloop()
22.10 存储Text控件内容
使用tkinter.filedialog模块中的asksaveasfilename可以启动“另存为”对话框,然后选择文档存储的位置和文件名。
from tkinter import *
from tkinter.ttk import *
from tkinter.font import Font
from tkinter import messagebox
from tkinter.filedialog import asksaveasfilename
def saveAsFile():
global filename
textContent = text.get("1.0", END)
filename = asksaveasfilename()
if filename == "":
return
with open(filename, "w") as output:
output.write(textContent)
root.title(filename)
root = Tk()
root.title("Ex")
root.geometry("300x180")
menubar = Menu(root)
filemenu = Menu(menubar, tearoff = False)
menubar.add_cascade(label = "File", menu = filemenu)
filemenu.add_command(label = "Save", command = saveAsFile)
filemenu.add_command(label = "Exit", command = root.destroy)
root.config(menu = menubar)
text = Text(root, undo = True)
text.pack(fill = BOTH, expand = True)
text.insert(END, "Five Hundred Miles\n")
text.insert(END, "If you miss the train I'm on,\n")
text.insert(END, "You will know that I am gone.\n")
text.insert(END, "You can hear the whistle blow\n")
text.insert(END, "A hundred miles.")
root.mainloop()
22.11 新建文档
def newFile():
text.delete("1.0", END)
root.title("Untitled")
filemenu.add_command(label = "New", command = newFile)
22.12 打开文档
from tkinter.filedialog import asksaveasfilename, askopenfilename
def openFile():
global filename
filename = askopenfilename()
if filename == "":
return
with open(filename, "r") as fileObj:
content = fileObj.read()
text.delete("1.0", END)
text.insert(END, content)
root.title(filename)
filemenu.add_command(label = "Open", command = openFile)
22.13 默认滚动条
from tkinter.scrolledtext import ScrolledText
text = ScrolledText(root, undo = True)
22.14 插入图像
Text控件是允许插入图像文件的,所插入的图像文件会被视为一个字符,所呈现的大小是图像文件的实际大小。
from tkinter import *
from tkinter.ttk import *
from tkinter.font import Font
from tkinter import messagebox
from PIL import Image, ImageTk
root = Tk()
root.title("Ex")
root.geometry("300x160")
img = Image.open("37.png")
pic = ImageTk.PhotoImage(img)
text = Text()
text.image_create(END, image = pic)
text.insert(END, "\n")
text.insert(END, "情不知所起,一往而深")
text.pack(fill = BOTH, expand = True)
root.mainloop()
23.Treeview
tkinter.ttk中的控件,用于提供多栏的显示功能,可以成为树状表格数据。在设计时也可以在左边栏设计成树状结构(层次结构),用户可以隐藏任何部分。
Treeview(父对象,options,…)
-
cursor,height;
-
columns:栏位的字符串。其中,第一个栏位的图标栏,这是默认的,不在此进行设置,如果设置columns = (“Name”, “Age”),则控件有3栏,其中,第一个栏位是最左栏的图标栏,可以进行展开(expand)和隐藏(collapse)操作,另两栏是Name和Age;
-
displaycolumns:可以设置栏位显示顺序;
1)如果参数是"#all"表示显示所有栏,依照建立顺序显示;
2)如果设置columns = (“Name”, “Age”, “Date”),使用insert()插入元素时需要依次插入元素,同样状况如果使用columns(2, 0),则图标栏在最前面,紧跟着是Date栏,然后是Name栏; -
padding:可以用1~4个参数设置内容与控件框的间距,规则如下,
-
selectmode:用户可以使用鼠标选择项目的方式;
1)selectmode = BROWSE:一次选择一项(默认);
2)selectmode = EXTENDED:一次可以选择多项;
3)selectmode = NONE:无法用鼠标执行选择; -
show:默认为"tree",显示图标栏,设为"headings"以不显示;
-
takefocus:默认为True,设为False以不被访问。
from tkinter import *
from tkinter.ttk import *
root = Tk()
root.title("Ex")
tree = Treeview(root, columns = ("cities"))
tree.heading("#0", text = "State")
tree.heading("#1", text = "Cities")
tree.insert("", index = END, text = "伊利诺", values = "芝加哥")
tree.insert("", index = END, text = "加州", values = "洛杉矶")
tree.insert("", index = END, text = "江苏", values = "南京")
tree.pack()
root.mainloop()
#0是指最左栏图标栏位,#1是指第一个栏位,当所建立的栏是最顶层时,父对象id可以使用""处理。
tree = Treeview(root, columns = (“cities”), show = “headings”)
text:图标栏的内容;
values:一般栏位的内容,有多栏时使用元组输入,如果所设置的内容数太少时其他栏将是空白,如果所设置的内容太多时多出的内容将被抛弃。
使用列表方法建立栏位内容:
from tkinter import *
from tkinter.ttk import *
root = Tk()
root.title("Ex")
list1 = ["芝加哥", "800"]
list2 = ["洛杉矶", "1000"]
list3 = ["南京", "900"]
tree = Treeview(root, columns = ("cities", "populations"))
tree.heading("#0", text = "State")
tree.heading("#1", text = "Cities")
tree.heading("#2", text = "Populations")
tree.insert("", index = END, text = "伊利诺", values = list1)
tree.insert("", index = END, text = "加州", values = list2)
tree.insert("", index = END, text = "江苏", values = list3)
tree.pack()
root.mainloop()
23.1 格式化
column(id, options)
id是指出特定栏位,可以用字符串表达,或是用"#index"索引方式,下列options参数:
- anchor:设置栏位内容参考位置;
- minwidth:最小栏宽,默认20像素;
- stretch:默认为1,当控件大小改变时栏宽将随之改变;
- width:默认200像素。
设置栏宽和对齐方式:
tree.column(“#1”, anchor = CENTER, width = 100)
如果此方法不含参数,将以字典方式传回特定栏所有参数的内容。
ret = column(id)
cityDict = tree.column(“cities”)
print(cityDict)
23.2 不同颜色
fag_configure(“tagName”,options,…)
- background,font,foreground,image。
from tkinter import *
from tkinter.ttk import *
root = Tk()
root.title("Ex")
stateCity = {
"伊利诺":"芝加哥","加州":"洛杉矶",
"德州":"休斯顿","华盛顿":"西雅图",
"江苏":"南京","山东":"青岛",
"广东":"广州","福建":"厦门"
}
tree = Treeview(root, columns = ("cities"))
tree.heading("#0", text = "State")
tree.heading("cities", text = "City")
tree.column("cities", anchor = CENTER)
tree.tag_configure("evenColor", background = "lightblue")
rowCount = 1
for state in stateCity.keys():
if rowCount%2 == 1:
tree.insert("", index = END, text = state, values = stateCity[state])
else:
tree.insert("", index = END, text = state, values = stateCity[state], \
tags = ("evenColor"))
rowCount +=1
tree.pack()
root.mainloop()
23.3 层级式Treeview
from tkinter import *
from tkinter.ttk import *
root = Tk()
root.title("Ex")
asia = {"中国":"北京","日本":"东京","泰国":"曼谷","韩国":"首尔"}
euro = {"英国":"伦敦","法国":"巴黎","德国":"柏林","挪威":"奥斯陆"}
tree = Treeview(root, columns = ("capital"))
tree.heading("#0", text = "国家")
tree.heading("capital", text = "首都")
idAsia = tree.insert("", index = END, text = "Asia")
idEuro = tree.insert("", index = END, text = "Europe")
for country in asia.keys():
tree.insert(idAsia, index = END, text = country, values = asia[country])
for country in euro.keys():
tree.insert(idEuro, index = END, text = country, values = euro[country])
tree.pack()
root.mainloop()
23.4 事件触发
def treeSelect(event):
widgetObj = event.widget
itemselected = widgetObj.selection()[0]
col1 = widgetObj.item(itemselected, "text")
col2 = widgetObj.item(itemselected, "value")[0]
Str = "{0} : {1}".format(col1, col2)
var.set(Str)
tree.bind("<<TreeviewSelect>>", treeSelect)
var = StringVar()
label = Label(root, textvariable = var, relief = "groove")
label.pack(fill = BOTH, expand = True)
23.5 删除项目
def removeItem():
ids = tree.selection()
for iid in ids:
tree.delete(iid)
tree = Treeview(root, columns = ("cities"), selectmode = EXTENDED)
rmBtn = Button(root, text = "Remove", command = removeItem).pack(pady = 5)
from tkinter import *
from tkinter.ttk import *
def removeItem():
ids = tree.selection()
for iid in ids:
tree.delete(iid)
root = Tk()
root.title("Ex")
stateCity = {
"伊利诺":"芝加哥","加州":"洛杉矶",
"德州":"休斯顿","华盛顿":"西雅图",
"江苏":"南京","山东":"青岛",
"广东":"广州","福建":"厦门"
}
tree = Treeview(root, columns = ("cities"), selectmode = EXTENDED)
tree.heading("#0", text = "State")
tree.heading("cities", text = "City")
for state in stateCity.keys():
tree.insert("", index = END, text = state, values = stateCity[state])
tree.pack()
rmBtn = Button(root, text = "Remove", command = removeItem).pack(pady = 5)
root.mainloop()
23.6 插入项目
def insertItem():
state = stateEntry.get()
city = cityEntry.get()
if (len(state.strip()) == 0 or len(city.strip()) == 0):
return
tree.insert("",END,text=state,values=city)
stateEntry.delete(0,END)
cityEntry.delete(0,END)
# 建立上层插入项目
stateLeb = Label(root,text="State:")
stateLeb.grid(row=0,column=0,padx=5,pady=3,sticky=W)
stateEntry = Entry()
stateEntry.grid(row=0,column=1,sticky=W+E,padx=5,pady=3)
cityLab = Label(root,text="City:")
cityLab.grid(row=0,column=3,sticky=E)
cityEntry = Entry()
cityEntry.grid(row=0,column=3,sticky=W+E,padx=5,pady=3)
# 建立Insert按钮
InBtn = Button(root,text="插入",command=insertItem)
InBtn.grid(row=0,column=4,padx=5,pady=3)
23.7 双击项目
def doubleClick(event):
e = event.widget #取得事件控件
iid = e.identify("item",event.x,event.y) #取得双击项目id
state = e.item(iid,"text") #取得State
city = e.item(iid,"values")[0] #取得City
Str = "{0}:{1}".format(state,city) #格式化
messagebox.showinfo("Double Clicked",Str) # 输出
tree.bind("<Double-1>",doubleClick)
23.8 绑定滚动条
tree = Treeview(root, columns = ("cities"), selectmode = EXTENDED)
yscrollbar = Scrollbar(root)
yscrollbar.pack(side = RIGHT, fill = Y)
tree.pack()
yscrollbar.config(command = tree.yview)
tree.configure(yscrollcommand = yscrollbar.set)
23.8 排序
单击栏标题可以排序,再次单击反向排序。
from tkinter import *
from tkinter.ttk import *
from tkinter.font import Font
from tkinter import messagebox
from PIL import Image, ImageTk
def treeview_sortSolumn(col):
global reverseFlag
lst = [(tree.set(st, col), st) for st in tree.get_children("")]
#print(lst)
lst.sort(reverse = reverseFlag)
#print(lst)
for index, item in enumerate(lst):
tree.move(item[1], "", index)
reverseFlag = not reverseFlag
root = Tk()
root.title("Ex")
states = {
"伊利诺", "加州", "德州", "华盛顿", "江苏", "山东", "广东", "福建"
}
tree = Treeview(root, columns = ("states"), \
show = "headings", selectmode = EXTENDED)
tree.pack()
tree.heading("#1", text = "State", \
command = lambda c = "states": treeview_sortSolumn(c))
for state in states:
tree.insert("", index = END, values = (state,))
root.mainloop()
24.Canvas
这个模块可以绘图,也可以制作动画,而动画也是设计游戏的基础。
24.1 绘画功能
-
建立画布
canvas = Canvas(父对象, width = xx, height = yy)
画布建立完成后,左上角是坐标(0,0),向右x轴递增,向下y轴递增。 -
create_line()
绘制线条。
create_line(x1, y1, x2, y2, …, xn, yn, options)
options:
1)arrow:默认是没有箭头,使用arrow = FIRST在起始线末端有箭头,arrow = LAST在最后一条线末端有箭头,使用arrow = BOTH在两端有箭头;
2)arrowshape:使用元组(d1, d2, d3)代表三角形三边长度来控制箭头形状,默认是(8, 10, 3);
3)capstyle:线条终点的样式,默认为BUTT,可以选择PROJECTING,ROUND;
4)dash:建立虚线,使用元组存储数字数据, 第一个数字是实线,第二个数字是空白,如此循环完当前所有元组数字又重新开始;
5)dashoffset:与dash一样产生虚线,但是一开始是空白的宽度;
6)fill:设置线条颜色;
7)joinstyle:线条相交的设置,默认为ROUND,也可以选择BEVEL、MITER;
8)stipple:绘制位图线条;
9)width:线条宽度。
import math
from tkinter import *
from tkinter.ttk import *
from tkinter.font import Font
from tkinter import messagebox
from PIL import Image, ImageTk
root = Tk()
root.title("Ex")
canvas = Canvas(root, width = 640, height = 480)
canvas.pack()
x_center, y_center, r = 320, 240, 100
x, y = [], []
for i in range(12):
x.append(x_center + r * math.cos(i*math.pi/6))
y.append(y_center + r * math.sin(i*math.pi/6))
for i in range(12):
for j in range(12):
canvas.create_line(x[i], y[i], x[j], y[j])
root.mainloop()
- create_rectangle()
绘制矩形。
create_rectangle(x1, y1, x2, y2, options)
fill:为矩阵填充颜色;
outline:设置矩阵线条颜色。
4. create_arc()
绘制圆弧。
create_arc(x1, y1, x2, y2, extent = angle, options)
extent:1~359,绘制圆弧的角度;
start:圆弧起点位置;
style:ARC、CHORD、PIESLICE。
绘制方向是逆时针。
-
create_oval()
绘制原或椭圆。
create_oval(x1, y1, x2, y2, options) -
create_polygon()
绘制多边形。
create_polygon(x1, y1, x2, y2, …, xn, yn, options) -
create_text()
输出文字。
create_text(x, y, text = “字符串”, options)
(x, y)是字符串输出的中心坐标。
anchor:默认为CENTER;
fill,font,justify,stipple,text,width。 -
画布色彩
Canvas(…, bg = “yellow”) -
插入图像
create_image(x, y, options)
(x, y)是图像左上角的位置。
24.2 鼠标拖曳绘制线条
圆的左上角坐标与右下角坐标相同,可以认为是点(极小化的圆)
import math
from tkinter import *
from tkinter.ttk import *
from tkinter.font import Font
from tkinter import messagebox
from PIL import Image, ImageTk
def paint(event):
x, y = (event.x, event.y)
canvas.create_oval(x, y, x, y, fill = "blue")
def clear():
canvas.delete("all")
root = Tk()
root.title("Ex")
canvas = Canvas(root, width = 640, height = 300)
canvas.pack()
btn = Button(root, text = "清除", command = clear)
btn.pack(pady = 5)
canvas.bind("<B1-Motion>", paint)
root.mainloop()
24.3 动画设计
1、基本动画
动画设计所使用的的方法是move()。
canvas.move(id, xMove, yMove) #id是对象标号
canvas.update() #重绘画布
xMove、yMove分别是对象沿x和y轴移动的距离,单位是像素。
import time
from tkinter import *
from tkinter.ttk import *
from tkinter.font import Font
from tkinter import messagebox
from PIL import Image, ImageTk
root = Tk()
root.title("Ex")
canvas = Canvas(root, width = 500, height = 150)
canvas.pack()
canvas.create_oval(10, 50, 60, 100, fill = "yellow", outline = "blue")
for x in range(0,80):
canvas.move(1, 5, 0)
root.update()
time.sleep(0.05)
root.mainloop()
2、反弹球游戏
import time
from tkinter import *
from tkinter.ttk import *
from tkinter.font import Font
from tkinter import messagebox
from PIL import Image, ImageTk
from tkinter import *
from random import *
import time
class Ball:
def __init__(self, canvas, color, winW, winH, racket):
self.canvas = canvas
self.racket = racket
self.id = canvas.create_oval(winW/2-10, winH/2-10, \
winW/2+10, winH/2+10, fill = color)
self.x = step
self.y = -step
self.notTouchBottom = True
def hitRacket(self, ballPos):
racketPos = self.canvas.coords(self.racket.id)
if ballPos[2] >= racketPos[0] and ballPos[0] <= racketPos[2]:
if ballPos[3] >= racketPos[1] and ballPos[3] <= racketPos[3]:
return True
return False
def ballMove(self):
self.canvas.move(self.id, self.x, self.y)
ballPos = self.canvas.coords(self.id)
if ballPos[0] <= 0:
self.x = step
if ballPos[1] <= 0:
self.y = step
if ballPos[2] >= winW:
self.x = -step
if ballPos[3] >= winH:
self.y = -step
if self.hitRacket(ballPos) == True:
self.y = -step
if ballPos[3] >= winH:
self.notTouchBottom = False
class Racket:
def __init__(self, canvas, color):
self.canvas = canvas
self.id = canvas.create_rectangle(270, 400, 370, 415, fill = color)
self.x = 0
self.canvas.bind_all("<KeyPress-Right>", self.moveRight)
self.canvas.bind_all("<KeyPress-Left>", self.moveLeft)
def racketMove(self):
self.canvas.move(self.id, self.x, 0)
racketPos = self.canvas.coords(self.id)
if racketPos[0] <= 0:
self.x = 0
elif racketPos[2] >= winW:
self.x = 0
def moveLeft(self, event):
self.x = -3
def moveRight(self, event):
self.x = 3
#定义画布尺寸
winW = 640
winH = 480
#定义位移步长
step = 3
#定义移动速度
speed = 100
root = Tk()
root.title("Bouncing Ball")
root.wm_attributes('-topmost', 1) #确保游戏窗口在屏幕最上层
canvas = Canvas(root, width = winW, height = winH) #建立画布
canvas.pack()
root.update()
racket = Racket(canvas, "purple")
ball = Ball(canvas, "yellow", winW, winH, racket)
while ball.notTouchBottom: #如果球未接触画布底端
try:
ball.ballMove()
except:
print("单击关闭按钮终止程序执行")
break
racket.racketMove()
root.update()
time.sleep(1/speed)
root.mainloop()
总结
分享:
我梦想,来到塞外的大漠中,在夕阳的金黄中,感受长河落日圆”的情怀;我梦想,坐在家乡的明月下,在满月的银辉中,体会
“月是故乡明”的感慨;我梦想,置身于江南的古镇中,在绵绵的细雨里,体味“沾衣欲湿杏花雨”的情调;我梦想,徜徉在东海之滨,在阵阵海浪声中,感受“烟雨奔苍苍”的气魄。