win10画图程序本质上是一个位图处理程序。其最主要的功能是画各种图形,包括线、矩形、椭圆(圆)和各种多边形等,并将各种图形保存到位图中。线、矩形、椭圆(圆)和各种多边形都可以用一个矩形定位,这点在前边几篇博文中已详细讨论过了。win10画图程序采用拖动鼠标方法画各种图形,即鼠标单击处作为矩形的一个顶点坐标保持不变,矩形相对顶点坐标随鼠标移动而改变,即矩形形状将随着鼠标移动改变,从而导致矩形定位的各种图形形状也跟着改变,鼠标抬起画图结束。不建议直接把图形画在位图中后,再用鼠标拖动这个位图中的图形完成画图工作,这将使拖动鼠标画各种图形变得十分复杂。因为图形画在位图中后,鼠标移动到新位置画新图形前,要把旧图删除,如果这个图形和其它图形不重叠,比较简单,用背景色重画一次这个图形即可,但有重叠,就要恢复其下方图形的颜色,这样就必须在画图前先记录将要画的图形所有坐标位置的颜色,再画这个图形,鼠标移动画新图,删除旧图要根据记录,逐点恢复每点原来的颜色。
前边博文中详细介绍了在Canvas上用鼠标拖动图形来画各种图形的方法,拖动的图形都是canvas上图形类实例,用很少语句实现图形移动、改变形状和删除。因此在画图程序中,可在画图开始前,先创建在Canvas上图形类实例,鼠标移动,拖动这个Canvas上图形类实例改变其形状,当鼠标抬起后,拖动鼠标画图形结束,再把这个图形保存到位图中并显示位图,同时删除在Canvas上的图形实例,这样可极大简化程序。
下面是拖动画圆的程序,仅用来说明画图程序实现用鼠标拖动画各种图形的基本结构。当然要实现画各种图形,还要在鼠标左键按下事件、鼠标左键被按下时移动鼠标事件和鼠标左键松开事件的事件处理函数中根据所画图形类型不同,编写相应的代码。另外还有编写选择所画图形的外轮廓线颜色,粗细,线型以及填充色等代码,但这些主要是工作量,困难并不太大。
import tkinter as tk
from PIL import Image,ImageTk,ImageDraw
root = tk.Tk()
root.geometry('400x300')
cv = tk.Canvas(root, width=400, height=300, bg='white')
cv.pack()
image1 = Image.new("RGB", (400, 300), 'white')#用来保存位图,宽、高和背景和cv必须相同
img = ImageTk.PhotoImage(image=image1)
mainImage=cv.create_image(200,150,image=img)#在Canvas上显示image1
draw = ImageDraw.Draw(image1) #将用draw在image1上画图
#cv.itemconfig(mainImage,image=img)
def StartMove(event): #开始建立一个Canvas图形实例
global first_x,first_y
first_x,first_y = event.x,event.y
cv.create_oval(event.x,event.y,event.x+2,event.y+2,tags=('L'),fill ='red',outline='green')
def OnMotion(event): #鼠标移动,拖动这个Canvas图形实例改变形状
global first_x,first_y
cv.coords('L',first_x,first_y,event.x,event.y)
def StopMove(event): #鼠标抬起,将所画图形保存到image1位图中,并在canvas上显示
global first_x,first_y,img
cv.coords('L',first_x,first_y,event.x,event.y)
if ((abs(event.x-first_x)+abs(event.y-first_y))<6):#避免在窗体点一下,出一个点
cv.delete('L')
return #下句在image1位图中画图形
draw.ellipse((first_x,first_y,event.x,event.y),fill ='red',outline='green')
img = ImageTk.PhotoImage(image=image1)
cv.itemconfig(mainImage,image=img) #在Canvas上显示image1
cv.delete('L') #删除开始建立的Canvas图形实例
#image1.show() #用操作系统默认显示图形软件显示位图
cv.bind("<ButtonPress-1>",StartMove) #绑定鼠标左键按下事件
cv.bind("<ButtonRelease-1>",StopMove) #绑定鼠标左键松开事件
cv.bind("<B1-Motion>", OnMotion) #绑定鼠标左键被按下时移动鼠标事件
root.mainloop()