用python实现win10画图程序画选择框复制剪切粘贴及拖动选择框内图形或粘贴后图形到指定位置

Posted geng_zhaoying

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了用python实现win10画图程序画选择框复制剪切粘贴及拖动选择框内图形或粘贴后图形到指定位置相关的知识,希望对你有一定的参考价值。

本文介绍用python实现win10画图程序剪贴功能的方法。win10画图程序本质上是一个位图处理程序。其最主要的功能是画各种图形,包括线、矩形、椭圆(圆)和各种多边形等,并将各种图形保存到位图中。该程序的剪贴功能是把选定区域的位图移到指定区域,一般包括复制、剪切和粘贴功能。首先要用鼠标拖动画矩形作为选定区域,可直接用鼠标拖动该选定区域位图到指定区域;也可用复制功能把该选定区域位图保存,供粘贴使用;剪切和复制功能相同,但要删除选定区域位图,即将选定区域修改为背景色;粘贴功能是把复制、剪切的位图重新放到指定位置,一般是先放到固定位置,然后用鼠标拖粘贴后的图形到指定位置。
要用python实现win10画图程序的剪贴功能,需要创建PIL Image类实例用来记录所画的所有图形的位图,称为总位图。要创建Canvas类实例,用来显示总位图。两者宽、高背景颜色都必须相同。
剪贴功能首先要用鼠标拖动画选择框,作为复制和剪切的区域,这个选择框是一个canvas上的矩形类实例,用selectRec记录其左上角和右下角的坐标,具体实现方法前边博文已经详细介绍过了。程序需用鼠标拖动画选择框和用鼠标拖动图形到指定位置,两者都要响应鼠标左键按下事件、鼠标左键松开事件和鼠标左键被按下时移动鼠标事件,即都要调用鼠标事件处理函数StartMove、StopMove和OnMotion,但两者完成的功能不同,在鼠标事件处理函数中需有不同的语句。因此用一个全局变量state记录这两个状态值,画选择框为’SelRec’,是初始值,即程序运行后首先画选择框,用鼠标拖动图形是’paste’。
当单击复制按钮,执行复制功能,见第35-37条语句。其中image_crop 是一个全局变量,用来保存复制或剪切所得到位图图形。selectRec也是一个全局变量,为复制和剪切的区域。
当单击剪切按钮,执行剪切功能,见第23-33条语句。首先执行复制功能,然后创建一个和被复制位图相同尺寸的位图,背景色为Canvas的背景色,将其粘贴到总位图的被剪切处,即清除剪切处的图形,然后将新的总位图在Canvas上显示。这时选择框内图形应被删除,选择框也要被删除。显然此时复制和剪切按钮应无法使用。
当单击粘贴按钮,执行粘贴功能,见第39-52条语句。因要用鼠标拖动粘贴位图到指定位置,这时不能直接把图形粘贴到总位图中,而是用粘贴位图在canvas上创建一个图形类实例,用鼠标拖动图形类实例到指定位置后,才把图形粘贴到总位图中。此时要进入粘贴状态’paste’,以区别画选择框状态’SelRec’。请注意,单击粘贴按钮事件处理函数有2个参数,默认值都为0。当单击按钮粘贴时,是把图形类实例放在固定位置(10,10),让使用者拖动。但在拖动选择框内图形时,要把图形类实例放在选择框处,即把选择框左上角坐标作为实参。
请注意复制、剪切和粘贴按钮的状态,什么时候可用,什么时候不可用。如选择框不存在或在粘贴状态(state==‘paste’)时,复制和剪切按钮不可用,否则可用。在粘贴状态(state==‘paste’)时粘贴按钮不可用,其它情况都可用。
现在看一下鼠标左键按下事件处理函数StartMove。在state为’paste’时,是处理用鼠标拖动图形程序。如鼠标点击处在图形内,拖动图形,如鼠标点击处在图形外,结束拖动图形,把拖动图形粘贴到总位图中被拖动后所在位置,并在Canvas显示,同时删除canvas上的图形对象和矩形框,令state=‘SelRec’,粘贴按钮可用。而当在state为’SelRec’时,如鼠标点击点在用来复制或剪切的矩形内,表示要拖动选择框中图形到指定位置,要先调用剪切按钮事件处理函数crop(),执行复制功能,并清除选择框中图形;然后调用粘贴按钮事件函数paste(selectRec[0],selectRec[1]),请注意,调用该函数的两个实参,要把图形类实例放在选择框处,看起来图形未发生变化,但实际上,此时已做好了被拖动的准备,令state==‘paste’,再次进入鼠标左键按下事件处理函数StartMove后,要执行被拖动语句。如鼠标点击点在用来复制或剪切的矩形外,则完成画选择框功能。
鼠标移动事件处理函数changeCursor(),是为了改变鼠标光标形状,鼠标在被拖动图形内或在选择框内,光标为通用的表示移动的光标,否则为默认光标。
win10画图程序剪贴有些功能本程序未实现。第一,选择框是闪动的,这可以在另一线程中每隔0.5秒发一事件,在事件处理函数中,改变选择框断续线设置达到动画效果。第二,选择框可被拖动改变尺寸,即鼠标移到选择框上下左右边,光标变为上下或左右箭头,按下左键并上下或左右移动,可拖动边线移动,鼠标移到选择框的4个顶点,鼠标改变,按下左键并移动,可拖动选择框顶点移动,从而改变选择框尺寸。实现方法是在选择框每边中点和四个角放置小矩形,有不同tag值,通过为不同tag值绑定鼠标移动事件,在其事件处理函数可改变光标形状;为不同tag值绑定鼠标左键按下并移动事件,在其事件处理函数中完成改变选择框尺寸的功能。
通过前边及本篇博文,可以实现win10画图程序大部分功能。有些功能虽然未提到,使用PIL大部分都能实现。例如存取位图文件、橡皮、调整大小、旋转、画任意曲线、填充、取色、添加字符等,有兴趣可以试一试。而用博文“用方法create_bitmap在Canvas上生成xbm文件图像创建win10风格工具栏”中的方法可设计出和win10类似的工具栏。
用python实现win10画图程序的剪贴功能的完整程序如下:

import tkinter as tk
from PIL import Image,ImageTk,ImageDraw
root = tk.Tk()
root.geometry('400x350')
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)    #在cv上显示image1,此时image1为空(白色)

draw = ImageDraw.Draw(image1)                       #将用draw在image1上画图
draw.line([220, 20, 280, 80], fill='red',width=3)   #画线
draw.ellipse((100,100, 200, 200), fill ='red',outline='green')  #画圆

img = ImageTk.PhotoImage(image=image1)
cv.itemconfig(mainImage,image=img)               #在cv上显示所画的圆和线   

selectRec=(0,0,0,0)               #用来复制和剪切的矩形。下句image_crop记录复制或剪切得到的位图
image_crop=Image.new("RGB", (0, 0), 'white')    #初始宽和高都为0,粘贴时判断宽和高都为0,不粘贴
state='SelRec'    #画或已画完矩形用来复制和剪切,另一状态为'paste',粘贴后用鼠标移动黏贴的图形

def crop():     #剪切按钮的事件处理函数
    global image_crop,selectRec,img,state
    image_crop =image1.crop(box=selectRec)      #复制selectRec指定矩形内的图形
    image2=Image.new("RGB",(image_crop.width,image_crop.height),'white') #建宽高和复制图形相同位图  
    image1.paste(image2,selectRec)              #将image2粘贴到image1的被剪切处,即清除剪切处的图形
    img = ImageTk.PhotoImage(image=image1)      #显示清除剪切处的图形后的图形
    cv.itemconfig(mainImage,image=img)           #img必须是全局变量
    cv.delete('S')                              #删除用来复制和剪切的矩形
    but1.config(state="disabled")               #因无复制和剪切的矩形,复制和剪切按钮不能使用
    but2.config(state="disabled")
    state='SelRec'
    
def copy():                         #复制按钮的事件处理函数
    global image_crop,selectRec
    image_crop =image1.crop(box=selectRec)    
    
def paste(x=10,y=10):               #粘贴按钮的事件处理函数
    global img,img1,state
    if image_crop.width==0 and image_crop.height==0:    #判断宽和高都为0,初始状态,不粘贴
        return
    cv.delete('S')                      #删除用来复制或剪切的矩形
    but1.config(state="disabled")       #复制按钮不能使用
    but2.config(state="disabled")       #剪切按钮不能使用
    but3.config(state="disabled")       #粘贴按钮不能使用
    image2 = Image.new("RGB", (image_crop.width, image_crop.height), 'white')#建新位图,image_crop是复制的位图
    image2.paste(image_crop,(0,0,image_crop.width,image_crop.height))        #将复制的位图复制到新位图
    img1 = ImageTk.PhotoImage(image=image2)
    cv.create_image(x,y,image=img1,anchor="nw",tags=('S')) #在Canvas上创建位图对象方便移动,下句矩形显示复制的位图外轮廓
    cv.create_rectangle(x,y,x+image_crop.width,y+image_crop.height,tags=('S','S1'),dash=(3,5))   #注意tags=('S')
    state='paste'           #进入粘贴状态    
    
def StartMove(event):
    global first_x,first_y,selectRec,state,img       
    if state=='paste':               #此状态是拖动图形,鼠标点击处在图形内,拖动图形,在图形外,结束拖动
        x1,y1,x2,y2=cv.bbox('S1')    #得到被拖动图形所在矩形左上角右下角坐标,下句判断单击点是否在该矩形内  
        if event.x>min(x1,x2) and event.x<max(x1,x2) and event.y>min(y1,y2) and event.y<max(y1,y2):
            first_x,first_y = event.x,event.y    #如在矩形内,拖动图形,否则,结束拖动        
        else:       #结束拖动图形,把拖动图形粘贴到被拖动位置,并在Canvas显示
            image1.paste(image_crop,(x1,y1,x1+image_crop.width,y1+image_crop.height))
            img = ImageTk.PhotoImage(image=image1)
            mainImage=cv.create_image(200,150,image=img)
            cv.delete('S')      #删除canvas上的图形对象和矩形框
            state='SelRec'      #回到画用来复制或剪切的矩形状态
            selectRec=(0,0,0,0)
            but3.config(state="normal")

    if state=='SelRec':     #在此状态下,如鼠标点击点在用来复制或剪切的矩形内,拖动该图形
        x1,y1,x2,y2=selectRec
        if event.x>min(x1,x2) and event.x<max(x1,x2) and event.y>min(y1,y2) and event.y<max(y1,y2):
            crop()
            paste(selectRec[0],selectRec[1])
            first_x,first_y = event.x,event.y            
        else:               #否则,在另一处画用来复制或剪切的矩形
            selectRec=(0,0,0,0)
            cv.delete('S')
            first_x,first_y = event.x,event.y
        
def StopMove(event):
    global first_x,first_y,selectRec,state
    if state=='SelRec':
        cv.delete('S')    
        cv.create_rectangle(first_x,first_y,event.x,event.y,tags=('S'),dash=(3,5))
        selectRec=(first_x,first_y,event.x,event.y)
        if ((abs(event.x-first_x)+abs(event.y-first_y))<6):     #如用来复制或剪切的矩形太小
            cv.delete('S')                                      #删除这个矩形
            selectRec=(0,0,0,0)
            but1.config(state="disabled")
            but2.config(state="disabled")
        else:                                                   #否则运行复制或剪切
            but1.config(state="normal")         #but1['state']="disabled"也可
            but2.config(state="normal")
            
def OnMotion(event): 
    global first_x,first_y,selectRec
    if state=='paste':
        cv.move('S',event.x-first_x,event.y-first_y)
        first_x,first_y = event.x,event.y
    if state=='SelRec':
        cv.delete('S')
        cv.create_rectangle(first_x,first_y,event.x,event.y,tags=('S'),dash=(3,5))

def changeCursor(event):            #改变鼠标光标形状
    if state=='paste':
        x1,y1,x2,y2=cv.bbox('S1')   #下句鼠标在被拖动图形内,光标为通用的表示移动的光标
        if event.x>min(x1,x2) and event.x<max(x1,x2) and event.y>min(y1,y2) and event.y<max(y1,y2):
            cv.config(cursor="fleur")
        else:
            cv.config(cursor="")        #恢复默认光标
    if but1['state']!="disabled":       #如复制按钮可用,表示选择框存在
        x1,y1,x2,y2=selectRec           #鼠标在选择框内,光标为通用的表示移动的光标
        if event.x>min(x1,x2) and event.x<max(x1,x2) and event.y>min(y1,y2) and event.y<max(y1,y2):
            cv.config(cursor="fleur")
        else:
            cv.config(cursor="")

but1=tk.Button(root,command=copy,text='复制',state="disabled")
but1.pack(side="left")
but2=tk.Button(root,command=crop,text='剪切',state="disabled")
but2.pack(side="left")
but3=tk.Button(root,command=paste,text='粘贴')
but3.pack(side="left")                  #,cursor='sb_v_double_arrow'

cv.bind("<ButtonPress-1>",StartMove)    #绑定鼠标左键按下事件
cv.bind("<ButtonRelease-1>",StopMove)   #绑定鼠标左键松开事件
cv.bind("<B1-Motion>", OnMotion)        #绑定鼠标左键被按下时移动鼠标事件
cv.bind("<Motion>",changeCursor)        #绑定鼠标移动事件
root.mainloop()

以上是关于用python实现win10画图程序画选择框复制剪切粘贴及拖动选择框内图形或粘贴后图形到指定位置的主要内容,如果未能解决你的问题,请参考以下文章

windows画图工具打字只显示一半 应该如何解决 比如 打个 张 字 光出现下面半个 上半个就没了

仿Win10截屏程序:用83条python语句编写可截全屏和拖动鼠标在屏幕画矩形选定区域后截屏的实用程序

仿Win10截屏程序:用83条python语句编写可截全屏和拖动鼠标在屏幕画矩形选定区域后截屏的实用程序

origin画图,怎么把两个数据画在一个图里,而一个用直线表示,一个用散点表示?

用python画小王八裤(turtle库)

arcgis连线怎么画实线