[源码]Python Tkiner 写的截图小工具 实现了大部分基础功能
最近在写一些小工具,因为是给别人用的需要界面,刚开始使用pyQT5 功能强大,但是打包后一个小工具 就有几十兆 有点蛋疼而且还很消耗资源;后面改用TK,TK明显简洁很多,功能同样也若很多! 因为有截图需求,就抽空写了这个工具,全当练习。过程当中遇到过好多蛋疼的事情,功能基本实现后发出来给大家分享一下,我个人还是比较喜欢python这门语言的! 实现思路:3.1.新建一个最小化窗口,利用线程启动监听"ctrl+a"启动截图窗口3.2.截图窗口初始化时利用 pyautogui.screenshot 截取全屏(类似PRTSC)作为子窗口的背景(在这搞了好久,后面没办法只有利用canvas设置背景),子窗口最大化取消菜单栏3.3.在主窗口上的canvas上用绑定鼠标事件,根据鼠标选取截图范围,和鼠标操作相关功能3.4.保存功能是根据截图范围重新截图生成文件保存 代码 main_UI.py fromtkinter.messageboximportshowinfo fromSc_Tool.Sc_Winimport* importkeyboard import_thread scwd=None #新建窗口对象 win=Tk() #最小化主窗口 win.wm_state('icon') win.iconify() #启用热键‘Ctrl+a’截屏 defstar_sc(): globalscwd scwd=windSc(win) scwd.newwind() defexit_sc(): print("exit") scwd.exit_sc(scwd) defstartThread1(event=None): keyboard.add_hotkey('ctrl+a',star_sc,args=(),suppress=False) defstartThread2(event=None): keyboard.add_hotkey('esc',exit_sc,args=(),suppress=False) try: _thread.start_new_thread(startThread1,()) _thread.start_new_thread(startThread2,()) exceptExceptionase: print(e) #启动app win.mainloop() scwd=None #新建窗口对象 win=Tk() win.wm_state('icon') win.iconify() #启用热键‘Ctrl+a’截屏 defstar_sc(): globalscwd scwd=windSc(win) scwd.newwind() defexit_sc(): scwd.exit_sc(scwd) defctrl_z(): scwd.ctrl_z(scwd) defstartThread1(event=None): keyboard.add_hotkey('ctrl+a',star_sc,args=(),suppress=False) defstartThread2(event=None): keyboard.add_hotkey('esc',exit_sc,args=(),suppress=False) defstartThread3(event=None): keyboard.add_hotkey('ctrl+z',ctrl_z,args=(),suppress=False) try: _thread.start_new_thread(startThread1,()) _thread.start_new_thread(startThread2,()) _thread.start_new_thread(startThread3,()) exceptExceptionase: print(e) #启动app win.mainloop() Sc_Tool/Sc_Win.py importos fromtkinterimport* importtkinter importpyautoguiaspyautogui fromPILimportImage,ImageTk importtkinter.fontastkFont importtime classwindSc: win,file_path,im,winAppSc,mouEvent,canvas=None,None,None,None,None,None #窗口宽窗口高宽高截图起点x坐标截图起点y坐标绘图起点x坐标绘图起点y坐标 win_w,win_h,width,height,x,y,dx,dy=0,0,0,0,0,0,0,0 #截图结束点坐标 end_x,end_y=0,0 #开始添加图形的起始坐标 cr_x,cr_y=0,0 #记录按钮的状态、文本输入按钮状态 but1_stat,but_text_stat=False,False #记录按钮类型1矩形2圆形 but_type=0 #tag标记文本输入单击次数标记 i,p=1,0 #工具栏部件 but1,but2,but3,but4,buttext,text_lable=None,None,None,None,None,None #画笔大小,文本框行数 size,row=1,1 #画笔颜色 color="black" #字体 font=None #按钮图片 img1,img2,img3,imgs,imgp1,imgp2,imgp3=None,None,None,None,None,None,None imgred,imgwhite,imgblack,imgblue,imggreen,imgyellow,imgback=None,None,None,None,None,None,None #工具按钮列表 but_list=[] #图形id列表 ids=[] def__init__(self,win): self.win=win #获取屏幕分辨率 self.width,self.height=win.winfo_screenwidth(),win.winfo_screenheight() #窗口宽度和高度 self.win_w,self.win_h=self.width,self.height image=pyautogui.screenshot(region=[0,0,self.width,self.height]).resize((self.width,self.height)) self.im=ImageTk.PhotoImage(image) self.img1=tkinter.PhotoImage(file="Sc_Tool/icon/1.png") self.img2=tkinter.PhotoImage(file="Sc_Tool/icon/2.png") self.img3=tkinter.PhotoImage(file="Sc_Tool/icon/3.png") self.imgtext=tkinter.PhotoImage(file="Sc_Tool/icon/Text.png") self.imgs=tkinter.PhotoImage(file="Sc_Tool/icon/s.png") self.imgp1=tkinter.PhotoImage(file="Sc_Tool/icon/p1.png") self.imgp2=tkinter.PhotoImage(file="Sc_Tool/icon/p2.png") self.imgp3=tkinter.PhotoImage(file="Sc_Tool/icon/p3.png") self.imgblack=tkinter.PhotoImage(file="Sc_Tool/icon/black.png") self.imgwhite=tkinter.PhotoImage(file="Sc_Tool/icon/white.png") self.imgred=tkinter.PhotoImage(file="Sc_Tool/icon/red.png") self.imggreen=tkinter.PhotoImage(file="Sc_Tool/icon/green.png") self.imgblue=tkinter.PhotoImage(file="Sc_Tool/icon/blue.png") self.imgyellow=tkinter.PhotoImage(file="Sc_Tool/icon/yellow.png") self.imgback=tkinter.PhotoImage(file="Sc_Tool/icon/b.png") defnewwind(self,event=''): #最小化主窗口 self.win.wm_state('icon') self.win.iconify() self.winAppSc=Toplevel(self.win) #winAppSc.attributes("-alpha",1) #删除标题栏 self.winAppSc.overrideredirect(True) self.winAppSc.geometry("%dx%d+%d+%d"%(self.win_w,self.win_h,(self.width-self.win_w)/2,(self.height-self.win_h)/2)) self.winAppSc.config(bg="white") self.winAppSc.title('截屏') self.winAppSc.attributes("-topmost",True) canvas=tkinter.Canvas(self.winAppSc, width=self.win_w,#指定Canvas组件的宽度 height=self.win_h,#指定Canvas组件的高度 bg='red')#指定Canvas组件的背景色 canvas.create_image(0,0,image=self.im,anchor='nw',tag=('r','r0')) canvas.pack(fill='both',expand='yes') self.canvas=canvas #工具栏部件 self.but1=Button(self.winAppSc,text="保存",image=self.imgs) self.but2=Button(self.winAppSc,text="矩形",image=self.img1) self.but3=Button(self.winAppSc,text="圆形",image=self.img2) self.but4=Button(self.winAppSc,text="箭头",image=self.img3) self.buttext=Button(self.winAppSc,text="文本",image=self.imgtext) self.but5=Button(self.winAppSc,text="返回",image=self.imgback) #画笔大小 self.sizebut1=Button(self.winAppSc,text="1",image=self.imgp1) self.sizebut2=Button(self.winAppSc,text="5",image=self.imgp2) self.sizebut3=Button(self.winAppSc,text="10",image=self.imgp3) #画笔颜色 self.color_red=Button(self.winAppSc,text="red",image=self.imgred) self.color_green=Button(self.winAppSc,text="red",image=self.imggreen) self.color_blue=Button(self.winAppSc,text="red",image=self.imgblue) self.color_yellow=Button(self.winAppSc,text="red",image=self.imgyellow) self.color_black=Button(self.winAppSc,text="red",image=self.imgblack) self.color_white=Button(self.winAppSc,text="red",image=self.imgwhite) #文本框 self.text_entry=Text(self.winAppSc,bg=None,relief=None,height=self.row,width=4) canvas.bind('',lambdaevent:self.setxy(event,canvas)) canvas.bind('',lambdaevent:self.getxy(event,canvas)) canvas.bind('',lambdaevent:self.setBtn(event,self.winAppSc)) canvas.bind('',lambdaevent:self.clear(canvas)) self.but1.bind('',lambdaevent:self.bu_save(event)) self.but2.bind('',lambdaevent:self.bu_click(event,canvas,1)) self.but3.bind('',lambdaevent:self.bu_click(event,canvas,2)) self.but4.bind('',lambdaevent:self.bu_click(event,canvas,3)) self.buttext.bind('',lambdaevent:self.bu_click_text(event,canvas)) self.sizebut1.bind('',lambdaevent:self.sizebut_click(1)) self.sizebut2.bind('',lambdaevent:self.sizebut_click(5)) self.sizebut3.bind('',lambdaevent:self.sizebut_click(10)) self.color_red.bind('',lambdaevent:self.colorbut_click("red")) self.color_green.bind('',lambdaevent:self.colorbut_click("green")) self.color_blue.bind('',lambdaevent:self.colorbut_click("blue")) self.color_yellow.bind('',lambdaevent:self.colorbut_click("yellow")) self.color_white.bind('',lambdaevent:self.colorbut_click("white")) self.color_black.bind('',lambdaevent:self.colorbut_click("black")) self.text_entry.bind('',lambdaevent:self.entry(event)) self.but5.bind('',lambdaevent:self.ctrl_z()) #先清空but_list以防止下次截图时还保存有之前的按钮而之前的按钮的父窗口已经销毁 self.but_list.clear() self.but_list.append(self.but1) self.but_list.append(self.but2) self.but_list.append(self.but3) self.but_list.append(self.but4) self.but_list.append(self.buttext) self.but_list.append(self.sizebut1) self.but_list.append(self.sizebut2) self.but_list.append(self.sizebut3) self.but_list.append(self.color_red) self.but_list.append(self.color_green) self.but_list.append(self.color_blue) self.but_list.append(self.color_yellow) self.but_list.append(self.color_white) self.but_list.append(self.color_black) self.but_list.append(self.but5) #鼠标点击调用初始化绘制截图的起点位置 defsetxy(self,event,canvas): #清空上一次鼠标事件,并设置截图的起始位置 self.mouEvent=None if(self.but1_stat): #如果超出截图区域改变起始坐标为截图起始坐标 if(event.x>self.xandevent.x<self.end_x): self.dx=event.x if(event.y>self.yandevent.y<self.end_y): self.dy=event.y else: self.x,self.y=event.x,event.y forbutinself.but_list: but.place_forget() canvas.delete(self,'j0') #文本输入操作 if(self.but_text_statandself.but_type==4): self.p=self.p+1 if(self.p%2==1): self.text_entry.delete('1.0','end') self.font=tkFont.Font(family='宋体',size=(self.size+5)*3) self.text_entry.config(font=self.font,fg=self.color,width=4,height=1) self.text_entry.place(x=event.x,y=event.y) else: canvas.create_text(self.text_entry.winfo_x(),self.text_entry.winfo_y()+15,text=self.text_entry.get(1.0,END),font=self.font,justify=LEFT,fill=self.color,anchor=W) self.text_entry.place_forget() #获取所有图形对象 self.ids=list(canvas.find_all()) defPreventOutOfBounds(self,eventx,eventy): if(eventx<self.x): eventx=self.x elif(eventx>self.end_x): eventx=self.end_x if(eventy<self.y): eventy=self.y elif(eventy>self.end_y): eventy=self.end_y returneventx,eventy #鼠标按下左键拖动时调用绘制截图的终点位置 defgetxy(self,event,canvas): #记录上一次的鼠标事件,如果按钮有按下不消除选取框矩形没有按钮按下消除上一个选取矩形 if(self.but1_stat): ifself.but_type==1: #销毁上一个鼠标暂停点绘制的图形 canvas.delete(self,"j%d"%(self.i)) #防止图形画出界 event.x,event.y=self.PreventOutOfBounds(event.x,event.y) canvas.create_rectangle(self.dx,self.dy,event.x,event.y,width=self.size,outline=self.color,tag=('j',"j%d"%(self.i))) elifself.but_type==2: canvas.delete(self,"j%d"%(self.i)) event.x,event.y=self.PreventOutOfBounds(event.x,event.y) canvas.create_oval(self.dx,self.dy,event.x,event.y,width=self.size,outline=self.color,tag=('j',"j%d"%(self.i))) elifself.but_type==3: canvas.delete(self,"j%d"%(self.i)) event.x,event.y=self.PreventOutOfBounds(event.x,event.y) canvas.create_line(self.dx,self.dy,event.x,event.y,width=self.size,fill=self.color,arrow=tkinter.LAST,tag=('j',"j%d"%(self.i))) else: #新截图框 #print("销毁截图矩形") self.mouEvent=event #删除上一个矩形 canvas.delete(self,"j0") #绘制新矩形并设置结束点坐标 canvas.create_rectangle(self.x,self.y,event.x,event.y,tag=('j','j0')) self.end_x,self.end_y=event.x,event.y self.ids=list(canvas.find_all()) #鼠标释放的时候绘制工具按钮如果上次释放鼠标左键的上一个鼠标事件是拖动则显示按钮 defsetBtn(self,event,winAppSc): #获取截图区域大小如果长宽小于5个像素则不显示按钮 w,h=event.x-self.x,event.y-self.y if(self.mouEvent!=Noneandw>5andh>5): x,y=self.x,event.y forbutinself.but_list: but.place(x=x,y=y) x=x+35 #释放鼠标新的图形动作tags标记变更 self.i=self.i+1 defbu_click(self,event,canvas,but_name): #按钮按下记录其状态为True self.but1_stat=True self.i=self.i+1 self.but_type=but_name #清除文本框 self.text_entry.place_forget() #按下鼠标右键取消截图初始化参数 defclear(self,canvas): #print("取消截图") self.i=1 self.but1_stat=False #销毁所有对象 canvas.delete(ALL) #从新加载IMG对象,此时依旧是之前的截图背景 canvas.create_image(0,0,image=self.im,anchor='nw',tag=('r','r0')) canvas.pack(fill='both',expand='yes') #隐藏按钮删除截图框 forbutinself.but_list: but.place_forget() #设置画笔大小 defsizebut_click(self,size): self.but1_stat=True self.size=size self.font=tkFont.Font(family='宋体',size=(self.size+5)*3) self.text_entry.config(font=self.font) #设置画笔颜色 defcolorbut_click(self,color): self.but1_stat=True self.color=color self.text_entry.config(fg=self.color) #退出窗口 defexit_sc(self,scwd): try: scwd.winAppSc.destroy() exceptExceptionase: print(e) defbu_click_text(self,event,canvas): #输入文本框 self.but1_stat=True self.but_text_stat=True self.but_type=4 defentry(self,event): keysym=event.keysym ifkeysym=="Return": self.row=self.row+1 self.text_entry.config(height=self.row) elifkeysym=="BackSpace": text=self.text_entry.get("%d.0"%(self.row),"%d.end"%(self.row)) iflen(text)==0: self.row=self.row-1 ifself.row<1: self.row=1 self.text_entry.config(height=self.row) else: tmp=0 foriinrange(1,self.row+1): pattern="[\u4e00-\u9fa5]+" regex=re.compile(pattern) text=self.text_entry.get("%d.0"%(i),"%d.end"%(i)) ch=regex.findall(text) chstr=''.join(ch) text_len=len(text)+len(chstr) iftext_len>=tmp:tmp=text_len if(tmp>=2):self.text_entry.config(width=tmp+3) defbu_save(self,event): image=pyautogui.screenshot(region=[self.x+1,self.y+1,self.end_x-self.x-1,self.end_y-self.y-1]) save_dir="save" ifnotos.path.exists(save_dir): os.makedirs(save_dir) t=int(time.time()) image.save("%s/%d.jpg"%(save_dir,t)) #截图完成销毁窗口 self.winAppSc.destroy() defctrl_z(self): iflen(self.ids)>2: self.canvas.delete(self.ids[-1]) self.ids.pop() 截图 源码因为打包了python环境比较大 链接:https://pan.baidu.com/s/1hND-HARBPt6mumgnvvUtqA 提取码:WLWL