Canvas 组件
虽然我们能用 Tkinter 设计不少东西了,但是还没法随心所欲地去绘制我们想要的界面。
Canvas 组件,是一个可以让你任性的组件,一个可以让你随心所欲地绘制界面的组件。Canvas 是一个通用的组件,它通常用于显示和编辑图形。可以用它来绘制直线、圆形、多边形,甚至是绘制其他组件。
在 Canvas 组件上绘制对象,可以用 create_xxx()
的方法(xxx 表示对象类型,例如直线 line、矩形 rectangle 和文本 text 等):
绘制线条
import tkinter as tk
root = tk.Tk()
root.geometry('500x300')
w = tk.Canvas(root)
w.pack(fill=tk.BOTH, expand=True)
# 画一条黄色的横线 (0,150), (500, 150)
w.create_line(0, 150, 500, 150, fill="yellow")
# 画一条红色的竖线(虚线) (250, 0), (250, 300)
w.create_line(250, 0, 250, 300, fill="red", dash=(4, 4))
root.mainloop()
注意,添加到 Canvas上的对象会一直保留着。如果你希望修改它们,你可以使用 coords()、itemconfig() 和 move() 方法来移动画布上的对象,或者使用 delete() 方法来删除:
import tkinter as tk
root = tk.Tk()
root.geometry('500x300')
w = tk.Canvas(root)
w.pack(fill=tk.BOTH, expand=True)
line1 = w.create_line(0, 150, 500, 150, fill="yellow")
line2 = w.create_line(250, 0, 250, 300, fill="red", dash=(4, 4))
# 创建一个矩形
rect1 = w.create_rectangle(100, 75, 300, 225, fill="blue")
# 修改线条的位置
w.coords(line1, 0, 75, 200, 75)
# 填充矩形的颜色
w.itemconfig(rect1, fill="red")
# 删除一条线
w.delete(line2)
tk.Button(root, text="删除全部", command=(lambda x=tk.ALL: w.delete(x))).pack()
root.mainloop()
绘制矩形
还可以在 Canvas上显示文本,使用的是 create_text()
方法:
import tkinter as tk
root = tk.Tk()
w = tk.Canvas(root, width=200, height=100)
w.pack()
w.create_line(0, 0, 200, 100, fill="green", width=3)
w.create_line(200, 0, 0, 100, fill="green", width=3)
w.create_rectangle(40, 20, 160, 80, fill="green")
w.create_rectangle(65, 35, 135, 65, fill="yellow")
w.create_text(100, 50, text="hello")
tk.Button(root, text="删除全部", command=(lambda x=tk.ALL: w.delete(x))).pack()
root.mainloop()
使用 create_oval()
方法绘制椭圆形(或圆形),参数是指定一个限定矩形(Tkinter 会自动在这个矩形内绘制一个椭圆)
而绘制圆形就是把限定矩形设置为正方形即可:
import tkinter as tk
width = 500
height = 300
root = tk.Tk()
root.geometry('{}x{}'.format(width, height))
w = tk.Canvas(root)
w.pack(fill=tk.BOTH, expand=True)
w.create_rectangle(width * 0.2, height * 0.2, width * 0.8, height * 0.8, dash=(4, 4), fill="green")
w.create_oval(width * 0.2, height * 0.2, width * 0.8, height * 0.8, fill="pink")
w.create_text(width * 0.5, height * 0.5, text="hello")
tk.Button(root, text="删除全部", command=(lambda x=tk.ALL: w.delete(x))).pack()
root.mainloop()
绘制多边形
如果想要绘制多边形,可以使用 create_polygon()
方法。好,现在带大家来画一个五角星。首先,要先确定五个角的坐标。那么高中数学不是体育老师教的鱼油们应该看得懂这张图(看不懂也没关系哈,知道结果就行,因为现在的目标是用 Tkinter 画五角星,而不是学三角函数)
import tkinter as tk
import math
width = 500
height = 300
root = tk.Tk()
root.geometry('{}x{}'.format(width, height))
w = tk.Canvas(root)
w.pack(fill=tk.BOTH, expand=True)
center_x = width / 2
center_y = height / 2
r = 100
points = [
# 左上点
center_x - int(r * math.sin(2 * math.pi / 5)),
center_y - int(r * math.cos(2 * math.pi / 5)),
# 右上点
center_x + int(r * math.sin(2 * math.pi / 5)),
center_y - int(r * math.cos(2 * math.pi / 5)),
# 左下点
center_x - int(r * math.sin(math.pi / 5)),
center_y + int(r * math.cos(math.pi / 5)),
# 顶点
center_x,
center_y - r,
# 右下点
center_x + int(r * math.sin(math.pi / 5)),
center_y + int(r * math.cos(math.pi / 5))
]
w.create_polygon(points, outline='green', fill='yellow')
root.mainloop()
绘制画板
接着设计一个像 Windows 画图工具那样的面板,让用户可以在上面随心所欲地绘画。
其实实现原理也很简单,就是获取用户拖动鼠标的坐标,然后每个坐标对应绘制一个点上去就可以了。在这里,不得不承认有点遗憾让人的是Tkinter并没有提供画“点”的方法。
但是程序是死的,程序员是活的!可以通过绘制一个超小的椭圆形来表示一个“点”。在下面的例子中,通过响应“鼠标左键按住拖动”事件(<B1-Motion>),在鼠标拖动的同时获取鼠标的实时位置(x,y),并绘制一个超小的椭圆来代表一个“点”:
import tkinter as tk
import math
root = tk.Tk()
w = tk.Canvas(root, width=200, height=100, background='#ffffff')
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='blue')
w.bind('<B1-Motion>', paint)
tk.Label(root, text='按住鼠标并移动,开始绘制理想的蓝图').pack()
root.mainloop()
总结
关于画布对象还有些概念,我们觉得必须了解,这里给大家做个总结:
arc(弧形、弦或扇形)。
bitmap(内建的位图文件或XBM格式的文件)。
image(BitmapImage或PhotoImage的实例对象)。
line(线)。
oval(圆或椭圆形)。
polygon(多边形)。
rectangle(矩形)。
text(文本)。
window(组件)。
其中,弦、扇形、椭圆形、圆形、多边形和矩形这些“封闭式”图形都是由轮廓线和填充颜色组成的,通过 outline 和 fill 选项设置它们的颜色,还可以设置为透明(传入空字符串表示透明)。
坐标系
由于画布可能比窗口大(带有滚动条的 Canvas 组件),因此 Canvas 组件可以选择使用两种坐标系:
窗口坐标系——以窗口的左上角作为坐标原点。
画布坐标系——以画布的左上角作为坐标原点。
画布对象显示的顺序
Canvas 组件中创建的画布对象都会被列入显示列表中,越接近背景的画布对象位于显示列表的越下方。显示列表决定当两个画布对象重叠的时候是如何覆盖的(默认情况下新创建的会覆盖旧的画布对象的重叠部分,即位于显示列表上方的画布对象将覆盖下方那个)。当然,显示列表中的画布对象可以被重新排序。