使用Python實(shí)現(xiàn)屏幕截圖工具
工具介紹
我們基于python代碼實(shí)現(xiàn)屏幕截圖,基于此工具可以進(jìn)行簡單的截圖,截圖預(yù)覽,保存截圖,將已經(jīng)截取的圖片復(fù)制到粘貼板;
完整代碼
import tkinter as tk from tkinter import messagebox, filedialog from PIL import ImageGrab, ImageTk, Image import platform import ctypes from datetime import datetime from io import BytesIO import traceback # === DPI 縮放處理 === SCALE_FACTOR = 100 if platform.system() == 'Windows': try: SCALE_FACTOR = ctypes.windll.shcore.GetScaleFactorForDevice(0) ctypes.windll.shcore.SetProcessDpiAwareness(1) except (AttributeError, OSError): pass def scale(value): """DPI 縮放計(jì)算""" return int(value * SCALE_FACTOR / 100) # === 應(yīng)用狀態(tài)管理 === class AppState: def __init__(self, max_history=10): self.history = [] self.history_index = -1 self.max_history = max_history self.reset_temp_state() def reset_temp_state(self): """重置臨時(shí)狀態(tài)""" self.start_pos = (0, 0) self.end_pos = (0, 0) self.current_image = None @property def valid_selection(self): """檢查選區(qū)有效性""" return abs(self.end_pos[0] - self.start_pos[0]) > 10 and \ abs(self.end_pos[1] - self.start_pos[1]) > 10 @property def has_history(self): """檢查是否存在有效歷史""" return 0 <= self.history_index < len(self.history) def add_history(self, image): """添加歷史記錄并執(zhí)行清理""" self.history.append(image) if len(self.history) > self.max_history: del self.history[0] self.history_index = len(self.history) - 1 # === 界面組件 === class HoverButton(tk.Label): def __init__(self, parent, command, **kwargs): self._default_fg = kwargs.get('fg', '#333') self._hover_fg = kwargs.pop('hover_fg', '#25C253') self._click_fg = kwargs.pop('click_fg', '#1E9D3A') super().__init__(parent, cursor="hand2", **kwargs) self.command = command self.bind("<Enter>", self._on_enter) self.bind("<Leave>", self._on_leave) self.bind("<Button-1>", self._on_click) self.bind("<ButtonRelease-1>", self._on_release) def _on_enter(self, event): self.config(fg=self._hover_fg) def _on_leave(self, event): self.config(fg=self._default_fg) def _on_click(self, event): self.config(fg=self._click_fg) def _on_release(self, event): self.config(fg=self._default_fg) self.command() class ScreenshotApp: def __init__(self, root): self.root = root self.state = AppState() self._init_ui() self._setup_bindings() # 資源緩存 self.fullscreen_photo = None self.result_photo = None self.selection_rect = None self.capture_window = None self.current_scale = 1.0 # 當(dāng)前縮放比例 def _init_ui(self): """初始化主界面""" self.root.title("專業(yè)截圖工具 v3.2") self.root.geometry(f"{scale(800)}x{scale(600)}") # 工具欄 toolbar = tk.Frame(self.root, bg="#f0f0f0", height=scale(40)) toolbar.pack(fill=tk.X, padx=2, pady=2) # 功能按鈕 controls = [ ("??? 截圖", self.start_capture, "#25C253"), ("?? 保存", self.save_image, "#0078D7"), ("?? 復(fù)制", self.copy_to_clipboard, "#FF9500"), ("??? 清除", self.clear_history, "#FF3B30") ] for text, cmd, color in controls: HoverButton( toolbar, cmd, text=text, font=("微軟雅黑", scale(10)), bg="#f0f0f0", fg="#333", hover_fg=color ).pack(side=tk.LEFT, padx=5) # 歷史導(dǎo)航 self._setup_history_controls(toolbar) # 主畫布 self.canvas = tk.Canvas(self.root, bg="white") self._setup_scrollbars() # 狀態(tài)欄 self.status = tk.Label( self.root, text="就緒", bd=1, relief=tk.SUNKEN, anchor=tk.W, bg="#f0f0f0", fg="#666", font=("微軟雅黑", scale(9)) ) self.status.pack(side=tk.BOTTOM, fill=tk.X) def _setup_history_controls(self, parent): """歷史記錄控制""" frame = tk.Frame(parent, bg="#f0f0f0") frame.pack(side=tk.RIGHT, padx=scale(10)) self.history_label = tk.Label( frame, text="歷史記錄: 0/0", bg="#f0f0f0", fg="#666", font=("微軟雅黑", scale(9)) ) self.history_label.pack(side=tk.LEFT) nav_buttons = [ ("?", lambda: self.navigate(-1)), ("?", lambda: self.navigate(1)) ] for text, cmd in nav_buttons: HoverButton( frame, cmd, text=text, font=("微軟雅黑", scale(12)), bg="#f0f0f0", fg="#666", hover_fg="#0078D7" ).pack(side=tk.LEFT, padx=scale(2)) def _setup_scrollbars(self): """滾動條配置""" self.x_scroll = tk.Scrollbar(self.root, orient="horizontal", command=self.canvas.xview) self.y_scroll = tk.Scrollbar(self.root, orient="vertical", command=self.canvas.yview) self.canvas.configure(xscrollcommand=self.x_scroll.set, yscrollcommand=self.y_scroll.set) self.x_scroll.pack(side="bottom", fill="x") self.y_scroll.pack(side="right", fill="y") self.canvas.pack(fill="both", expand=True) def _setup_bindings(self): """全局快捷鍵綁定""" self.root.bind("<Control-n>", lambda e: self.start_capture()) self.root.bind("<Control-s>", lambda e: self.save_image()) self.root.bind("<Control-c>", lambda e: self.copy_to_clipboard()) self.canvas.bind("<Configure>", self._on_canvas_resize) self.canvas.bind("<MouseWheel>", self._on_zoom) self.canvas.bind("<Double-Button-1>", self._reset_zoom) # === 核心功能 === def start_capture(self): """啟動截圖流程""" try: self.root.attributes('-alpha', 0.0) self._create_capture_window() self._capture_fullscreen() except Exception as e: self._show_error("初始化失敗", str(e)) self.root.attributes('-alpha', 1.0) def _create_capture_window(self): """創(chuàng)建全屏截圖窗口""" if self.capture_window: self.capture_window.destroy() self.capture_window = tk.Toplevel() self.capture_window.attributes('-fullscreen', True) self.capture_window.attributes('-topmost', True) self.capture_window.bind("<Escape>", lambda e: self._safe_exit()) # 截圖畫布 self.capture_canvas = tk.Canvas( self.capture_window, cursor="crosshair", highlightthickness=0 ) self.capture_canvas.pack(fill="both", expand=True) # 信息顯示 self._setup_info_labels() self._bind_capture_events() def _setup_info_labels(self): """設(shè)置信息標(biāo)簽""" self.cursor_position_label = tk.Label( self.capture_window, bg="#fff", bd=1, relief=tk.SUNKEN, anchor=tk.E, width=20, font=("微軟雅黑", scale(9)) ) self.cursor_position_label.place(x=10, y=10) def _capture_fullscreen(self): """獲取全屏截圖""" try: screenshot = ImageGrab.grab() self.state.current_image = self._add_metadata(screenshot) self.fullscreen_photo = ImageTk.PhotoImage(screenshot) self.capture_canvas.create_image(0, 0, image=self.fullscreen_photo, anchor="nw") except Exception as e: self._show_error("截圖失敗", str(e)) self._safe_exit() def _bind_capture_events(self): """綁定截圖事件""" self.capture_canvas.bind("<ButtonPress-1>", self._on_press) self.capture_canvas.bind("<B1-Motion>", self._on_drag) self.capture_canvas.bind("<ButtonRelease-1>", self._on_release) self.capture_canvas.bind("<Motion>", self._update_cursor_info) # === 事件處理 === def _on_press(self, event): """鼠標(biāo)按下事件""" self.state.start_pos = (event.x, event.y) self.selection_rect = self.capture_canvas.create_rectangle( event.x, event.y, event.x, event.y, outline="#25C253", width=scale(2) ) self.status.config(text="正在選擇區(qū)域...") def _on_drag(self, event): """鼠標(biāo)拖動事件""" self.capture_canvas.coords( self.selection_rect, *self.state.start_pos, event.x, event.y ) self._update_cursor_info(event) def _on_release(self, event): """鼠標(biāo)釋放事件""" self.state.end_pos = (event.x, event.y) if self.state.valid_selection: self._show_action_buttons(event) self.status.config(text="選擇完成") else: self.capture_canvas.delete(self.selection_rect) self.status.config(text="選區(qū)無效(最小10x10像素)") def _show_action_buttons(self, event): """顯示操作按鈕""" action_frame = tk.Frame(self.capture_window, bg="#fff", bd=1, relief=tk.RIDGE) action_frame.place(x=event.x + 10, y=event.y + 10) confirm_button = HoverButton( action_frame, self.confirm_selection, text="確認(rèn)", fg="#333", hover_fg="#25C253", click_fg="#1E9D3A" ) confirm_button.pack(side=tk.LEFT, padx=5) cancel_button = HoverButton( action_frame, self.cancel_selection, text="取消", fg="#333", hover_fg="#FF3B30", click_fg="#CC2F2F" ) cancel_button.pack(side=tk.LEFT, padx=5) def confirm_selection(self): """確認(rèn)選區(qū)""" x1, y1 = self.state.start_pos x2, y2 = self.state.end_pos selected_area = ( min(x1, x2), min(y1, y2), max(x1, x2), max(y1, y2) ) cropped_image = self.state.current_image.crop(selected_area) self.state.add_history(cropped_image) self._display_result(cropped_image) self._safe_exit() def cancel_selection(self): """取消選區(qū)""" self.capture_canvas.delete(self.selection_rect) self.status.config(text="就緒") self._safe_exit() def _safe_exit(self): """安全退出截圖模式""" if self.capture_window: self.capture_window.destroy() self.root.attributes('-alpha', 1.0) def save_image(self): """保存圖片""" if not self.state.has_history: self._show_error("無圖像可保存", "請先進(jìn)行截圖") return file_path = filedialog.asksaveasfilename( defaultextension=".png", filetypes=[("PNG files", "*.png"), ("JPEG files", "*.jpg *.jpeg")] ) if file_path: current_image = self.state.history[self.state.history_index] current_image.save(file_path) self.status.config(text=f"已保存到 {file_path}") def copy_to_clipboard(self): """復(fù)制到剪貼板""" if not self.state.has_history: self._show_error("無圖像可復(fù)制", "請先進(jìn)行截圖") return output = BytesIO() current_image = self.state.history[self.state.history_index] current_image.convert("RGB").save(output, format="BMP") data = output.getvalue()[14:] output.close() self.root.clipboard_clear() self.root.clipboard_append(data, format='DIB') self.status.config(text="已復(fù)制到剪貼板") def clear_history(self): """清除歷史記錄""" self.state.history = [] self.state.history_index = -1 self.canvas.delete("all") self.status.config(text="歷史記錄已清除") self.update_history_label() def navigate(self, step): """導(dǎo)航歷史記錄""" if not self.state.has_history: self._show_error("無歷史記錄", "請先進(jìn)行截圖") return new_index = self.state.history_index + step if 0 <= new_index < len(self.state.history): self.state.history_index = new_index self._display_result(self.state.history[new_index]) self.update_history_label() else: self._show_error("超出范圍", "沒有更多歷史記錄") def update_history_label(self): """更新歷史記錄標(biāo)簽""" total = len(self.state.history) current = self.state.history_index + 1 self.history_label.config(text=f"歷史記錄: {current}/{total}") def _add_metadata(self, image): """添加元數(shù)據(jù)""" metadata = { "Software": "專業(yè)截圖工具 v3.2", "DateTime": datetime.now().isoformat() } exif = image.info.get("exif") image_with_meta = image.copy() image_with_meta.info["exif"] = exif image_with_meta.info.update(metadata) return image_with_meta def _show_error(self, title, message): """顯示錯誤消息""" messagebox.showerror(title, message) def _on_canvas_resize(self, event): """調(diào)整畫布大小時(shí)重新顯示圖像""" if self.state.current_image: self._display_result(self.state.current_image) def _on_zoom(self, event): """鼠標(biāo)滾輪縮放""" delta = event.delta if platform.system() != 'Darwin' else -event.delta zoom_factor = 1.1 if delta > 0 else 0.9 self.current_scale *= zoom_factor self._display_result(self.state.current_image) def _reset_zoom(self, event): """雙擊重置縮放""" self.current_scale = 1.0 self._display_result(self.state.current_image) def _update_cursor_info(self, event): """更新光標(biāo)位置信息""" info_text = f"x={event.x}, y={event.y}" if self.selection_rect: rect_coords = self.capture_canvas.coords(self.selection_rect) selection_width = abs(rect_coords[2] - rect_coords[0]) selection_height = abs(rect_coords[3] - rect_coords[1]) info_text += f" | 選區(qū): {selection_width}x{selection_height}px" self.cursor_position_label.config(text=info_text) self.status.config(text=info_text) def _display_result(self, image): """在主窗口中顯示結(jié)果圖像""" self.canvas.delete("all") # 清空畫布 self.result_photo = ImageTk.PhotoImage(image) self.canvas.create_image( 0, 0, image=self.result_photo, anchor="nw", tags="image" ) self.canvas.config(scrollregion=self.canvas.bbox("all")) self.update_history_label() if __name__ == "__main__": root = tk.Tk() app = ScreenshotApp(root) root.mainloop()
效果如下
方法擴(kuò)展
下面小編為大家整理了一些其他Python進(jìn)行屏幕截圖的方法,希望對大家有所幫助
1.結(jié)合pyautogui庫進(jìn)行全屏截圖
#截圖 alt+F4 可以退出 #非專業(yè)人士 .給需要用的人 import tkinter as tk import pyautogui import win32gui import os,sys,time from PIL import Image def getscreen(): r""" 截屏全屏""" global im im = pyautogui.screenshot() try: pass #im.save(f"{os.getcwd()}/screenshot_temp.png") except: pass return im def savescrimg(s1=30,s2=135,s3=940,s4=800): r''' 保存指定區(qū)域屏圖 ''' global im SaveFileName=time.strftime('%Y-%m-%d_%H_%M_%S'); im = pyautogui.screenshot(region=(s1,s2,s3,s4)) try: im.save(f"{os.getcwd()}/screenshot.png") except: pass pass return im getscreen()#time.sleep(1) root = tk.Tk() root.title('截圖工具') # 設(shè)置窗口標(biāo)題。 try: root.iconbitmap('3D.ico') # 設(shè)置窗口左上角的圖標(biāo)。 except: pass WIDTH =window_x = root.winfo_screenwidth(); HEIGHT =window_y = root.winfo_screenheight()#"獲取電腦屏幕尺寸大小" x =0;y =0; root.geometry(f'{WIDTH}x{HEIGHT}+{int(x)}+{int(y)}') root.resizable(width=True, height=True) # 設(shè)置是否禁止調(diào)整窗口的寬和高。 win_width = root.winfo_width() #獲取窗口寬度(單位:像素) win_height = root.winfo_height() #獲取窗口高度(單位:像素) x=root.winfo_x() #獲取窗口左上角的 x 坐標(biāo)(單位:像素) y=root.winfo_y() #獲取窗口左上角的 y 坐標(biāo)(單位:像素) root.overrideredirect(True) #參數(shù)True,隱藏窗口標(biāo)題欄。即不顯示窗口標(biāo)題和最大化、最小化、關(guān)閉按鈕。 root.attributes("-alpha",0.5) #設(shè)置窗口的透明度 0.8, 參數(shù)為 0 到 1。 w = tk.Canvas(root, width =WIDTH, height = HEIGHT) w.pack() _1x=0;_1y=0;_2x=0;_2y=0; def handleMotion(event): global _x global _y global _x global _y lb1['text'] = '你移動了光標(biāo)的所在位置' lb2['text'] = '目前光標(biāo)位置:x ='+ str(event.x)+';y='+str(event.y) w.delete("all") w.delete() w.create_rectangle(_1x, _1y, event.x,event.y, fill = "blue") #print('光標(biāo)當(dāng)前位置',event.x,event.y) def handle_ML(event): global _1x global _1y global _2x global _2y _1x=event.x _1y=event.y print("ML");print(f"{event.x},{event.y}"); pass def handle_MR(event): global _1x global _1y global _2x global _2y _2x=event.x;_2y=event.y print("MR");print(f"{event.x},{event.y}"); def handle_RL(event): print("RL");print(f"{event.x},{event.y}"); def handle_RR(event): print("RR");print(f"{event.x},{event.y}"); box =(_1x,_1y,_2x,_2y) imb = im.crop(box) imb.save(f"{os.getcwd()}/{time.strftime('%Y-%m-%d_%H_%M_%S')}.png") exit() lb1 = tk.Label(w,text='沒有任何事件觸發(fā)', bg='purple', ) ;lb1.place (x=20,y=900) lb2 = tk.Label(w,text='...'); lb2.place (x=16,y=930) w.bind('<Motion>',handleMotion)# w.bind('<ButtonPress-1>',handle_ML)#鼠標(biāo)左鍵按下事件 w.bind('<ButtonPress-3>',handle_MR)#鼠標(biāo)右鍵按下事件 w.bind('<ButtonRelease-1>',handle_RL)#鼠標(biāo)左鍵抬起事件 w.bind('<ButtonRelease-3>',handle_RR)#鼠標(biāo)右鍵抬起事件 tk.mainloop()
2.調(diào)用windows API
調(diào)用windows API,速度快但是使用較復(fù)雜,有更好用的PyQt。
import time import win32gui, win32ui, win32con, win32api def window_capture(filename): hwnd = 0 # 窗口的編號,0號表示當(dāng)前活躍窗口 # 根據(jù)窗口句柄獲取窗口的設(shè)備上下文DC(Divice Context) hwndDC = win32gui.GetWindowDC(hwnd) # 根據(jù)窗口的DC獲取mfcDC mfcDC = win32ui.CreateDCFromHandle(hwndDC) # mfcDC創(chuàng)建可兼容的DC saveDC = mfcDC.CreateCompatibleDC() # 創(chuàng)建bigmap準(zhǔn)備保存圖片 saveBitMap = win32ui.CreateBitmap() # 獲取監(jiān)控器信息 MoniterDev = win32api.EnumDisplayMonitors(None, None) w = MoniterDev[0][2][2] h = MoniterDev[0][2][3] # print w,h #圖片大小 # 為bitmap開辟空間 saveBitMap.CreateCompatibleBitmap(mfcDC, w, h) # 高度saveDC,將截圖保存到saveBitmap中 saveDC.SelectObject(saveBitMap) # 截取從左上角(0,0)長寬為(w,h)的圖片 saveDC.BitBlt((0, 0), (w, h), mfcDC, (0, 0), win32con.SRCCOPY) saveBitMap.SaveBitmapFile(saveDC, filename) beg = time.time() for i in range(10): window_capture("haha.jpg") end = time.time() print(end - beg)
到此這篇關(guān)于使用Python實(shí)現(xiàn)屏幕截圖工具的文章就介紹到這了,更多相關(guān)Python屏幕截圖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python基于smtplib實(shí)現(xiàn)異步發(fā)送郵件服務(wù)
這篇文章主要介紹了Python基于smtplib實(shí)現(xiàn)異步發(fā)送郵件服務(wù),需要的朋友可以參考下2015-05-05python實(shí)現(xiàn)自動獲取IP并發(fā)送到郵箱
這篇文章主要為大家詳細(xì)介紹了python實(shí)現(xiàn)自動獲取IP并發(fā)到郵箱,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-12-12python中Selenium+Webdriver實(shí)現(xiàn)自動化登錄
本文主要介紹了python中Selenium+Webdriver實(shí)現(xiàn)自動化登錄,包括測試環(huán)境的搭建、代碼編寫、以及注意事項(xiàng)等,具有一定的參考價(jià)值,感興趣的可以了解一下2023-09-09