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

Win10有一截屏程序,可截全屏和截取屏幕选定区域截屏。如截取屏幕指定区域的图像时,整个屏幕似被雾遮住,在雾下可见屏幕上图像和文字。用鼠标拖动画矩形选择截屏区域,选择部分的雾被去除,当鼠标抬起,所选截图显示到主窗体,雾全部消失。可将图像保存。本文所设计的截屏程序要实现这些功能。先看本文所设计程序的效果图。
在这里插入图片描述

在这里插入图片描述

图1是程序运行后的主窗体。有4个按钮,标题分别是:定位截屏、截全屏、保存图像和帮助。这是本程序实现的4个功能。拷贝图像是一个重要功能。但要希望将图像粘贴到window系统的其它程序,例如画图程序,必须把图像拷贝到系统的剪贴板,需要调用window系统API,比较复杂,本人怕麻烦,没有加。网上有介绍这方面内容的网页,需要可自己增加。
单击“定位截屏”按钮后,出现的界面如图2。实现具体方法是,首先将主窗体最小化,以免影响截屏(第7条语句)。打开一个非模式窗体(第8条),使窗体半透明(第9条),使窗体无标题栏(第10条),使窗体和屏幕同宽同高(第11-14条)。这样使整个屏幕就似被雾遮住,在雾下可见屏幕上图像和文字。还在右侧中间位置增加了一个关闭按钮(第19条),方便关闭这个非模式窗体,后边将看到,如希望不截屏退出非模式窗体,只能使用这个按钮。为了在窗体画矩形,在此窗体增加Canvas实例(第17条),使该实例和窗体的宽和高同步变化(第18条)。为了使所画矩形内的雾消失,所画矩形的填充色和外轮廓是透明的,因此定义了一个透明灰色(第15-16条)。为拖动画矩形,为鼠标绑定了3个事件函数(第20-22条)。
所谓拖动鼠标画矩形,首先响应鼠标左键单击事件(第26-29条),记住矩形左上角坐标保持不变(第28条),画一个小矩形(第29条);保持鼠标按下并移动,响应鼠标移动事件(第45-47条),矩形左上角坐标保持不变,将矩形右下角坐标变为鼠标当前坐标,消去旧矩形,画新矩形(第47条),随着鼠标移动,看到矩形左上角位置保持不变,矩形形状大小不断变化,矩形内雾消失,使使用者直观看到所选截屏区域,见图3;鼠标抬起,响应鼠标抬起事件(第30-44条),画矩形结束。如截取的图像太小无意义,可能是误操作,删除这个误操作所画矩形,不截屏,否则从所选区域截屏。为了截屏,必须得到所选区域的屏幕坐标系的坐标(第35-38条),然后截屏(第39条)。然后将所截图像在主窗体显示(第40-42条),关闭非模式窗体(第43条),雾消失,使主窗体正常显示(第44条),见图4。
截全屏很好理解,但有一个问题是,如在“截全屏”按钮的事件函数中完成截屏,虽然主窗体已最小化(第49条),主窗体仍然被截取,这是不允许的。首先想到让截屏在另一方法中完成,因此创建一个自定义事件,在“截全屏”按钮的事件函数中发自定义事件,在自定义事件函数中截屏,主窗体仍然被截取。观察程序最小化时,似乎有一个动画效果,不是马上消失,而是慢慢消失。因此现在采用延迟方法。
完整程序如下:

import tkinter as tk
from PIL import ImageGrab,Image,ImageTk
from threading import Timer
import tkinter.filedialog,tkinter.messagebox
def screenGrab():
    global f1,cv,TRANSCOLOUR        #在Toplevel窗口和主窗口可以互相使用对方的变量和方法。    
    root.state('icon')              #主窗体最小化。icon:最小化,normal:正常显示,zoomed:最大化。或root.iconify()
    f1 = tk.Toplevel(root)                   #用Toplevel类创建独立主窗口的新窗口,非模式窗体
    f1.wm_attributes("-alpha", 0.6)          #设置窗体透明度(0.0~1.0)
    f1.overrideredirect(True)                #设置窗体无标题栏    
    ws = f1.winfo_screenwidth()              #屏幕长和宽 
    hs = f1.winfo_screenheight()
    s=str(ws)+'x'+str(hs)+'+0+0'
    f1.geometry(s)                  #Toplevel窗体充满屏幕,整个屏幕似乎被雾遮住,点击屏幕任意处,将使该窗体退出
    TRANSCOLOUR = 'gray'
    f1.wm_attributes('-transparentcolor', TRANSCOLOUR)  #设置灰色为透明颜色
    cv = tk.Canvas(f1)                                  #在Toplevel窗体增加Canvas实例,用来画透明矩形
    cv.pack(fill=tk.BOTH, expand=tk.Y)                  #使Canvas实例自动充满Toplevel窗体
    tk.Button(cv,text="关闭", command=closeDialog).pack(side='right')     #该按钮将出现在屏幕右侧中间位置
    cv.bind("<ButtonPress-1>",StartMove)  #绑定鼠标左键按下事件,为在Toplevel窗体上拖动鼠标画矩形做准备
    cv.bind("<ButtonRelease-1>",StopMove) #绑定鼠标左键松开事件
    cv.bind("<B1-Motion>", OnMotion)      #绑定鼠标左键被按下时移动鼠标事件
def closeDialog():    
    f1.destroy()            #关闭对话框
    root.state('normal')    #使主窗体正常显示    
def StartMove(event):       #为拖动画矩形做准备
    global first_x,first_y,cv
    first_x,first_y = event.x,event.y       #拖动鼠标画矩形其左上角坐标必须记住,保持不变
    cv.create_rectangle(first_x,first_y,event.x+1,event.y+1,fill=TRANSCOLOUR, outline=TRANSCOLOUR,tags=('L'))    
def StopMove(event):                #鼠标抬起,截取所选择屏幕区域图像
    global first_x,first_y,cv,f1,img,p
    if abs(first_x-event.x)<10 or abs(first_y-event.y)<10:      #如截取的图像太小无意义,可能是误操作
        cv.delete('L')                                          #删除这个误操作所画矩形
        return
    x=f1.winfo_rootx()+first_x      #x=Toplevel窗体在屏幕坐标系中的x坐标+所画透明矩形左上角x坐标
    y=f1.winfo_rooty()+first_y
    x1=x+abs(first_x-event.x)       #abs(first_x-event.x)是所画透明矩形的宽
    y1=y+abs(first_y-event.y)       #abs(first_y-event.y)是所画透明矩形的高
    p=ImageGrab.grab((x,y,x1,y1))   #截取屏幕透明矩形内图像。因PIL的问题,必须将显示设置里的缩放比例调成100%
    img = ImageTk.PhotoImage(image=p)              #将image1转换为canvas能显示的格式
    cvM.delete('P')                                #删除上一个截取图像
    cvM.create_image(0,0,image=img,tags=('P'),anchor=('nw'))   #将img在主窗口显示,img必须是全局变量,不能丢失
    f1.destroy()                    #关闭Toplevel窗体
    root.state('normal')            #使主窗体正常显示    
def OnMotion(event):                #拖动画矩形
    global first_x,first_y,cv
    cv.coords('L',first_x,first_y,event.x,event.y)  #移动透明矩形到新位置,左上角坐标不变,右下角为新位置
def grabAllScreen():                    #截全屏    
    root.state('icon')                  #如在该函数内直接截屏,主窗体动画最小化未完成,主窗体将被截到。
    t = Timer(0.2, doGrabAllScreen)     #后来创建事件,在此发出事件,在事件函数中截屏,问题未解决。
    #最后用多线程,上句创建定时器,0.2秒后在其它线程执行参数2指定函数。由于时间延迟,显得截图略慢
    t.start()       #此句启动定时器。如主窗体仍被截到,可将0.2秒变大,例如0.3、0.4、0.5等。    
def saveImage():                        #保存截屏所得图像。下句打开对话框,选择保存文件夹及文件名和扩展名
    fname=tkinter.filedialog.asksaveasfilename(title=u'保存文件')
    p.save(str(fname))
def Help(): #帮助按钮事件处理函数
    s='因PIL的问题,显示设置的缩放比例必须调成100%,否则截图尺寸出错。\n'+\
    '单击"定位截屏"按钮,整个屏幕似被雾遮住,鼠标点击要截屏图像左上角后,\n'+\
    '拖动鼠标画矩形,矩形内雾被去掉,抬起鼠标,截图显示到主窗体,雾全部消失'+\
    '\n保存文件必须填写文件名和图像扩展名。截全屏后显示图像为原图的0.87,'+\
    '\n但保存的文件尺寸未改变。                              保留所有版权'
    tkinter.messagebox.showinfo(title="帮助",message=s)
def doGrabAllScreen():  #Timer(0.2,doGrabAllScreen)语句参数2指定的在其它线程执行的方法。实际的截全屏方法
    global img,p
    ws = root.winfo_screenwidth()              #屏幕长和宽 
    hs = root.winfo_screenheight()
    p=ImageGrab.grab((0,0,ws,hs))#截全屏。因PIL的问题,必须将显示设置里的缩放比例调成100%,否则截取尺寸出错
    p1=p.resize((ws*87//100,hs*87//100))  #为显示所截全部图形将图形缩小原图*0.87,但保存的文件尺寸未改变
    img = ImageTk.PhotoImage(image=p1)              #将image1转换为canvas能显示的格式
    cvM.delete('P')                                 #删除上一个截取图像    
    cvM.create_image(0,0,image=img,tags=('P'),anchor=('nw'))#将img在主窗口显示,img必须是全局变量,不能丢失
    root.state('normal')                           #使主窗体正常显示
root = tk.Tk()
root.geometry('500x500+50+50')
frm = tk.Frame(root)
frm.pack(fill=tk.BOTH)
tk.Button(frm,text="定位截屏", command=screenGrab).pack(side='left')
tk.Button(frm,text="截全屏", command=grabAllScreen).pack(side='left')
tk.Button(frm,text="保存图像", command=saveImage).pack(side='left')
tk.Button(frm,text="帮助", command=Help).pack(side='left')
cvM = tk.Canvas(root,bg='lightgray')        #主窗体中canvac实例
cvM.pack(fill=tk.BOTH, expand=tk.Y)
root.mainloop()
  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值