最近在写一些小工具,因为是给别人用的需要界面,刚开始使用pyQT5 功能强大,但是打包后一个小工具 就有几十兆 有点蛋疼而且还很消耗资源;后面改用TK,TK明显简洁很多,功能同样也若很多!
因为有截图需求,就抽空写了这个工具,全当练习。过程当中遇到过好多蛋疼的事情,功能基本实现后发出来给大家分享一下,我个人还是比较喜欢python这门语言的!
-
实现思路:
3.1.新建一个最小化窗口,利用线程启动监听"ctrl+a"启动截图窗口
3.2.截图窗口初始化时利用 pyautogui.screenshot 截取全屏(类似PRTSC)作为子窗口的背景(在这搞了好久,后面没办法只有利用canvas设置背景),子窗口最大化取消菜单栏
3.3.在主窗口上的canvas上用绑定鼠标事件,根据鼠标选取截图范围,和鼠标操作相关功能
3.4.保存功能是根据截图范围重新截图生成文件保存
-
代码
main_UI.py
tkinter.messagebox showinfo Sc_Tool.Sc_Win * keyboard _thread (): showinfo(, ) scwd = win = Tk() win.wm_state() win.iconify() (): scwd scwd = windSc(win) scwd.newwind() (): () scwd.exit_sc(scwd) (): keyboard.add_hotkey(, star_sc, =(),= ) (): keyboard.add_hotkey(, exit_sc, =(),= ) : _thread.start_new_thread(startThread1, ()) _thread.start_new_thread(startThread2, ()) e: (e) win.mainloop() (): showinfo(, ) scwd = win = Tk() win.wm_state() win.iconify() (): scwd scwd = windSc(win) scwd.newwind() (): scwd.exit_sc(scwd) (): scwd.ctrl_z(scwd) (): keyboard.add_hotkey(, star_sc, =(),= ) (): keyboard.add_hotkey(, exit_sc, =(),= ) (): keyboard.add_hotkey(, ctrl_z, =(), = ) : _thread.start_new_thread(startThread1, ()) _thread.start_new_thread(startThread2, ()) _thread.start_new_thread(startThread3, ()) e: (e) win.mainloop()
Sc_Tool/Sc_Win.py
import os from tkinter import * import tkinter import pyautogui as pyautogui from PIL import Image, ImageTk import tkinter.font as tkFont import time class windSc: 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") def newwind(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('<1>', lambda event:self.setxy(event,canvas)) canvas.bind('<B1-Motion>',lambda event:self.getxy(event,canvas)) canvas.bind('<ButtonRelease-1>', lambda event: self.setBtn(event,self.winAppSc)) canvas.bind('<3>', lambda event: self.clear(canvas)) self.but1.bind('<1>', lambda event: self.bu_save(event)) self.but2.bind('<1>', lambda event: self.bu_click(event,canvas,1)) self.but3.bind('<1>', lambda event: self.bu_click(event, canvas,2)) self.but4.bind('<1>', lambda event: self.bu_click(event, canvas,3)) self.buttext.bind('<1>', lambda event: self.bu_click_text(event, canvas)) self.sizebut1.bind('<1>', lambda event: self.sizebut_click(1)) self.sizebut2.bind('<1>', lambda event: self.sizebut_click(5)) self.sizebut3.bind('<1>', lambda event: self.sizebut_click(10)) self.color_red.bind('<1>', lambda event: self.colorbut_click("red")) self.color_green.bind('<1>', lambda event: self.colorbut_click("green")) self.color_blue.bind('<1>', lambda event: self.colorbut_click("blue")) self.color_yellow.bind('<1>', lambda event: self.colorbut_click("yellow")) self.color_white.bind('<1>', lambda event: self.colorbut_click("white")) self.color_black.bind('<1>', lambda event: self.colorbut_click("black")) self.text_entry.bind('<Key>',lambda event: self.entry(event)) self.but5.bind('<1>', lambda event: 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) # 鼠标点击调用初始化绘制截图的起点位置 def setxy(self,event,canvas): #清空上一次鼠标事件,并设置截图的起始位置 self.mouEvent = None if (self.but1_stat): #如果超出截图区域改变起始坐标为截图起始坐标 if (event.x > self.x and event.x < self.end_x): self.dx = event.x if (event.y > self.y and event.y < self.end_y): self.dy = event.y else: self.x, self.y=event.x, event.y for but in self.but_list: but.place_forget() canvas.delete(self, 'j0') #文本输入操作 if (self.but_text_stat and self.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()) def PreventOutOfBounds(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 return eventx,eventy # 鼠标按下左键拖动时调用绘制截图的终点位置 def getxy(self,event,canvas): # 记录上一次的鼠标事件 , 如果按钮有按下不消除选取框矩形 没有按钮按下消除上一个选取矩形 if (self.but1_stat): if self.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))) elif self.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))) elif self.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()) # 鼠标释放的时候绘制工具按钮 如果上次释放鼠标左键的上一个鼠标事件是拖动<B1-Motion> 则显示按钮 def setBtn(self,event,winAppSc): # 获取截图区域大小 如果长宽小于5个像素则不显示按钮 w, h = event.x - self.x, event.y - self.y if (self.mouEvent != None and w > 5 and h > 5): x, y = self.x, event.y for but in self.but_list: but.place(x=x,y=y) x= x+35 #释放鼠标新的图形动作tags标记变更 self.i = self.i + 1 def bu_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() #按下鼠标右键取消截图初始化参数 def clear(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') # 隐藏按钮 删除截图框 for but in self.but_list: but.place_forget() # 设置画笔大小 def sizebut_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) # 设置画笔颜色 def colorbut_click(self, color): self.but1_stat = True self.color = color self.text_entry.config(fg=self.color) # 退出窗口 def exit_sc(self,scwd): try: scwd.winAppSc.destroy() except Exception as e: print(e) def bu_click_text(self, event, canvas): # 输入文本框 self.but1_stat = True self.but_text_stat = True self.but_type = 4 def entry(self,event): keysym = event.keysym if keysym == "Return": self.row = self.row + 1 self.text_entry.config(height=self.row) elif keysym == "BackSpace": text = self.text_entry.get("%d.0" % (self.row), "%d.end" % (self.row)) if len(text) == 0: self.row = self.row - 1 if self.row < 1: self.row = 1 self.text_entry.config(height=self.row) else: tmp = 0 for i in range(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) if text_len >= tmp: tmp = text_len if (tmp >= 2): self.text_entry.config(width=tmp + 3) def bu_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" if not os.path.exists(save_dir): os.makedirs(save_dir) t = int(time.time()) image.save("%s/%d.jpg" %(save_dir,t)) # 截图完成销毁窗口 self.winAppSc.destroy() def ctrl_z(self): if len(self.ids) > 2: self.canvas.delete(self.ids[-1]) self.ids.pop()
-
截图
源码因为打包了python环境比较大
链接:https://pan.baidu.com/s/1hND-HARBPt6mumgnvvUtqA
-
提取码:WLWL