赞
踩
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()
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。