10. 弹出菜单
1 # Program 9.14 2 from tkinter import * 3 4 class PopupMenuDemo: 5 def __init__(self): 6 window = Tk() 7 window.title("Popup Menu Demo") 8 9 self.menu = Menu(window, tearoff = 0) 10 self.menu.add_command(label = "Draw a line", command = self.displayLine) 11 self.menu.add_command(label = "Draw an oval", command = self.displayOval) 12 self.menu.add_command(label = "Draw a rectangle", command = self.displayRect) 13 self.menu.add_command(label = "Clear", command = self.clearCanvas) 14 15 self.canvas = Canvas(window, width = 200, height = 100, bg = "white") 16 self.canvas.pack() 17 18 self.canvas.bind("", self.popup) 19 20 window.mainloop() 21 22 def displayRect(self): 23 self.canvas.create_rectangle(10, 10, 190, 90, tags = "rect") 24 25 def displayOval(self): 26 self.canvas.create_oval(10, 10, 190, 90, tags = "oval") 27 28 def displayLine(self): 29 self.canvas.create_line(10, 10, 190, 90, tags = "line") 30 self.canvas.create_line(10, 90, 190, 10, tags = "line") 31 32 def clearCanvas(self): 33 self.canvas.delete("rect", "oval", "line") 34 35 def popup(self, event): 36 self.menu.post(event.x_root, event.y_root) 37 38 PopupMenuDemo()
9-13) 用self.menu存储Menu类对象是因为后续方法中会需要访问此menu,故将其放在类的数据域中
self.menu.add_command(label, command)给一个菜单栏添加具体命令
18) self.canvas.bind(string, command)将对象self.canvas与命令command绑定起来,调用此回调函数的条件由string描述,此处"
36) 当在self.canvas中点击鼠标右键会调用popup()函数,其中self.menu.post(x, y)将菜单menu显示在坐标为(x, y)的地方,此处event代表事件对象(此处即为点击鼠标右键),event.x_root与event.y_root分别代表此事件发生的x坐标与y坐标(此处即为在画布中点击右键处的x,y坐标)。menu.post(x, y)是设计弹出菜单的关键
11. 鼠标、按键事件和绑定
前面例子使用小构件的bind方法将事件与回调处理器绑定:
widget.bind(event, handler)
其中event所代表的的事件应用引号括起来表示成字符串,具体事件有以下选项:
事件 | 描述 |
当鼠标左键按住小构件且移动鼠标时事件发生 | |
Button-1、Button-2、Button-3分别表明左键、中键和右键,单击时触发事件,且Tkinter会自动抓取鼠标指针的位置 | |
当释放鼠标左中右键时触发事件 |
|
当双击鼠标左中右键时触发事件 | |
当鼠标光标进入小构件时触发事件 | |
当单机一个键时触发事件 | |
当鼠标光标离开小构件时事件发生 | |
当单机“Enter”键时事件发生,此处亦可将Return替换为键盘上的任意键("A"、"B"、"Down“、“Right”等) | |
当单机"Shift+A"键时事件发生,可以将Alt、Shift、Control和其他键任意组合 | |
当三次单机鼠标左中右键时事件发生 |
与此同时,每个事件也对应了诸多属性,采用event.properties调用这些属性值:
事件属性 | 描述 |
char | 对 |
keycode | 对 |
keysym | 对 |
num | num=1/2/3表示按下的是鼠标哪个键 |
widget | 触发这个事件的小构件对象 |
x、y | 当前鼠标在小构件中以像素为单位的位置 |
x_root、y_root | 当前鼠标相对于屏幕左上角的以像素为单位的位置 |
下面是一个使用小构件绑定事件及事件属性的例子:
1 # Program 9.15 2 from tkinter import * 3 4 class MouseKeyEventDemo: 5 def __init__(self): 6 window = Tk() 7 window.title("Event Demo") 8 canvas = Canvas(window, bg = "white", width = 200, height = 100) 9 canvas.pack() 10 11 canvas.bind("", self.processMouseEvent) 12 13 canvas.bind(" ", self.processKeyEvent) 14 canvas.focus_set() 15 16 window.mainloop() 17 18 def processMouseEvent(self, event): 19 print("clicked at ", event.x, event.y) 20 print("Position in the screen", event.x_root, event.y_root) 21 print("Which button is clicked? ", event.num) 22 23 def processKeyEvent(self, event): 24 print("keysym? ", event.keysym) 25 print("char? ", event.char) 26 print("keycode? ", event.keycode) 27 28 MouseKeyEventDemo()
11)将canvas绑定
13)将canvas绑定
14)canvas.focus_set()在画布上设置焦点从而从键盘上获取输入(其实我心里是???焦点跟这有啥关系)
19-21、24-26) 调用了event事件的x/y/x_root/y_root/num/keysym/char/keycode等一系列属性
1 # Program 9.16 2 from tkinter import * 3 4 class EnlargeShrinkCircle: 5 def __init__(self): 6 self.radius = 50 7 8 window = Tk() 9 window.title("Control Circle Demo") 10 self.canvas = Canvas(window, bg = "white", width = 200, height = 200) 11 self.canvas.pack() 12 self.canvas.create_oval(100 - self.radius, 100 - self.radius, 100 + self.radius, 100 + self.radius, tags = "oval") 13 14 self.canvas.bind("", self.increaseCircle) 15 self.canvas.bind(" ", self.decreaseCircle) 16 17 window.mainloop() 18 19 def increaseCircle(self, event): 20 self.canvas.delete("oval") 21 if self.radius < 100: 22 self.radius += 2 23 self.canvas.create_oval(100 - self.radius, 100 - self.radius, 100 + self.radius, 100 + self.radius, tags = "oval") 24 25 def decreaseCircle(self, event): 26 self.canvas.delete("oval") 27 if self.radius > 2: 28 self.radius -= 2 29 self.canvas.create_oval(100 - self.radius, 100 - self.radius, 100 + self.radius, 100 + self.radius, tags = "oval") 30 31 EnlargeShrinkCircle()
14-15) 添加点击鼠标左键、右键事件至self.canvas,并分别与increaseCircle和decreaseCircle方法绑定
19-29) 在increaseCircle与decreaseCircle中,先调用canvas对象的delete方法清除原先的圆,再更改self.radius更新半径,最后重新调用canvas对象create_oval方法绘制新的圆
12. 动画
1 # Program 9.17 2 from tkinter import * 3 4 class AnimationDemo: 5 def __init__(self): 6 window = Tk() 7 window.title("Animation Demo") 8 9 width = 250 10 canvas = Canvas(window, bg = "white", width = 250, height = 50) 11 canvas.pack() 12 13 x = 0 14 canvas.create_text(x, 30, text = "Message moving?", tags = "text") 15 16 dx = 3 17 while True: 18 canvas.move("text", dx, 0) 19 canvas.after(100) 20 canvas.update() 21 if x < width: 22 x += dx 23 else: 24 x = 0 25 canvas.delete("text") 26 canvas.create_text(x, 30, text = "Message moving?", tags = "text") 27 28 window.mainloop() 29 30 AnimationDemo()
14) 调用canvas对象的create_text方法,create_text(x, y, text, tags)其中x, y为创建消息的坐标,text为消息内容,tags为其标签
18) 调用canvas对象的move方法,move(tags, dx, dy)其中tags为待移动的标签,dx为横向的坐标移动值(右正左负),dy为纵向的坐标移动值(下正上负)
19-20) canvas.after(100)使程序暂停100ms,canvas.update()重新显示画布
21-26) 通过变量x监测消息的当前位置,若超过width则将原消息删掉,在初始位置新建消息并更新变量x=0
1 # Program 9.18 2 from tkinter import * 3 4 class ControlAnimation: 5 def __init__(self): 6 window = Tk() 7 window.title("Control Animation Demo") 8 9 self.width = 250 10 self.canvas = Canvas(window, bg = "white", width = self.width, height = 50) 11 self.canvas.pack() 12 13 frame = Frame(window) 14 frame.pack() 15 btStop = Button(frame, text = "Stop", command = self.stop) 16 btStop.pack(side = LEFT) 17 btResume = Button(frame, text = "Resume", command = self.resume) 18 btResume.pack(side = LEFT) 19 btFaster = Button(frame, text = "Faster", command = self.faster) 20 btFaster.pack(side = LEFT) 21 btSlower = Button(frame, text = "Slower", command = self.slower) 22 btSlower.pack(side = LEFT) 23 24 self.x = 0 25 self.sleepTime = 100 26 self.canvas.create_text(self.x, 30, text = "Message moving?", tags = "text") 27 28 self.dx = 3 29 self.isStopped = False 30 self.animate() 31 32 window.mainloop() 33 34 def stop(self): 35 self.isStopped = True 36 37 def resume(self): 38 self.isStopped = False 39 self.animate() 40 41 def faster(self): 42 if self.sleepTime > 5: 43 self.sleepTime -= 20 44 45 def slower(self): 46 self.sleepTime += 20 47 48 def animate(self): 49 while not self.isStopped: 50 self.canvas.move("text", self.dx, 0) 51 self.canvas.after(self.sleepTime) 52 self.canvas.update() 53 if self.x < self.width: 54 self.x += self.dx 55 else: 56 self.x = 0 57 self.canvas.delete("text") 58 self.canvas.create_text(self.x, 30, text = "Message moving?", tags = "text") 59 60 ControlAnimation()
25) 将移动的间隔时间声明为self.sleepTime,方便后续方法中访问这个值
15-22) 新建四个按钮,分别绑定四个方法,从而实现消息移动的停止、继续、加速、减速功能
29) self.isStopped 参数决定图像是否继续移动,这一点在animate()方法中的while not self.isStopped中凸显出来
48-58)self.animate()是整个程序的核心,负责消息的移动
13. 滚动条
1 # Program 9.19 2 from tkinter import * 3 4 class ScrollText: 5 def __init__(self): 6 window = Tk() 7 window.title("Scroll Text Demo") 8 9 frame1 = Frame(window) 10 frame1.pack() 11 scrollbar = Scrollbar(frame1) 12 scrollbar.pack(side = LEFT, fill = Y) 13 text = Text(frame1, width = 40, height = 10, wrap = WORD, yscrollcommand = scrollbar.set) 14 text.pack() 15 scrollbar.config(command = text.yview) 16 17 window.mainloop() 18 19 ScrollText()
11) 创建一个Scrollbar对象,其父容器为frame1
12) 将scrollbar放在文本的左端
13) 新建一个文本对象Text(frame, width, height, wrap, yscrollcommand),其中yscrollcommand填Scrollbar对象.set,即可将此文本与滚动条对象绑定
15) scrollbar.config(command),其中command为text.yview,将文本对象与滚动条绑定为纵向滑动
其运行结果如下图:
14. 标准对话框
1 # Program 9.20 2 import tkinter.messagebox 3 import tkinter.simpledialog 4 import tkinter.colorchooser 5 6 tkinter.messagebox.showinfo("showinfo", "This is an info msg") 7 8 tkinter.messagebox.showwarning("showwarning", "This is a warning") 9 10 tkinter.messagebox.showerror("showerror", "This is an error") 11 12 isYes = tkinter.messagebox.askyesno("askyesno", "Continue?") 13 print(isYes) 14 15 isOK = tkinter.messagebox.askokcancel("askokcancel", "OK?") 16 print(isOK) 17 18 isYesNoCancel = tkinter.messagebox.askyesnocancel("askyesnocancel", "Yes, No, Cancel?") 19 print(isYesNoCancel) 20 21 name = tkinter.simpledialog.askstring("askstring", "Enter your name") 22 print(name) 23 24 age = tkinter.simpledialog.askinteger("askinteger", "Enter your age") 25 print(age) 26 27 weight = tkinter.simpledialog.askfloat("askfloat", "Enter your weight") 28 print(weight)
2-4) 弹窗的一些模块,使用时需要import
6-10) tkinter.messagebox.show(info/warning/error)(str1, str2) 创建信息/警告/错误弹窗,其中str1为该弹窗标题,str2为该弹窗的内容,三种不同的弹窗还会配上系统为信息/警告/错误预置的图标
12-18) tkinter.messagebox.ask(yesno/okcancel/yesnocalcel) (str1, str2)创建三种类型的弹窗,且皆有返回值。当三个例子中点击(yes/ok/yes)时返回值为True,当三个例子中点击(no/cancel/no)时返回值为False,当在第三个例子中点击cancel时返回值为None(python中类似null的东西)
21-28) tkinter.simpledialog.ask(string/integer/float) 创建三种类型弹窗,其中包含一输入框分别向用户请求一个字符串/整数/浮点数,返回值即为用户输入的内容,若用户点击了Cancel则返回值为None
所有对话框都是模态窗口,意味着对话框消失程序将不再继续(??不懂)
第9章 使用Tkinter进行GUI程序设计 到此结束