开发者

使用Python实现屏幕截图工具

工具介绍

我们基于python代码实现屏幕截图,基于此工具可以进行简单的截图,截图预览,保存截图,将已经截取的图片复制到粘贴板;

完整代码

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 platformphp.system() == 'Windows':
    try:
        SCALE_FACTOR = ctypes.windll.shcore.GetScaleFactorForDevice(0)
        ctypes.windll.shcore.SetProcessDpiAwareness(1)
    except (AttributeError, OSError):
        pass
 
 
def scale(value):
    """DPI 缩放计算"""
    return int(value * SCALE_FACTOR / 100)
 
 
# === 应用状态管理 ===
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):
        """重置临时状态"""
        self.start_pos = (0, 0)
        self.end_pos = (0, 0)
        self.current_image = None
 
    @property
    def valid_selection(self):
        """检查选区有效性"""
        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):
        """添加历史记录并执行清理"""
        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  # 当前缩放比例
 
    def _init_ui(self):
        """初始化主界面"""
        self.root.title("专业截图工具 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"),
            (" 复制", 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)
 
        # 历史导航
        self._setup_history_controls(toolbar)
 
        # 主画布
        self.canvas = tk.Canvas(self.root, bg="white")
        self._setup_scrollbars()
 
        # 状态栏
        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("初始化失败"android, str(e))
            self.root.attributes('-alpha', 1.0)
 
    def _create_capture_window(self):
        """创建全屏截图窗口"""
        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):
        """设置信息标签"""
        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):
        """鼠标按下事件"""
        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="正在选择区域...")
 
    def _on_drag(self, event):
        """鼠标拖动事件"""
        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):
        """鼠标释放事件"""
        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="选区无效(最小10x10像素)")
 
    def _show_action_buttons(self, event):
        """显示操作按钮"""
        action_frame = tk.Frame(self.capture_wpythonindow, 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="确认", 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):
        """确认选区"""
        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):
        """取消选区"""
        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("无图像可保存", "请先进行截图")
            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):
        """复制到剪贴板"""
        if not self.state.has_history:
            self._show_error("无图像可复制", "请先进行截图")
            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="已复制到剪贴板")
 
    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):
        """导航历史记录"""
        if not self.state.has_history:
            self._show_error("无历史记录", "请先进行截图")
            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):
        """更新历史记录标签"""
        total = len(self.state.history)
        current = self.state.history_index + 1
        self.history_label.config(text=f"历史记录: {current}/{total}")
 
    def _add_metadata(self, image):
        """添加元数据"""
        metadata = {
            "Software": "专业截图工具 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):
        """调整画布大小时重新显示图像"""
        if self.state.current_image:
            self._display_result(self.state.current_image)
 
    def _on_zoom(self, event):
        """鼠标滚轮缩放"""
        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):
        """更新光标位置信息"""
        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" | 选区: {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):
        """在主窗口中显示结果图像"""
        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()

效果如下

使用Python实现屏幕截图工具

方法扩展

下面小编为大家整理了一些其他Python进行屏幕截图的方法,希望对大家有所帮助

1.结合pyautogui库进行全屏截图

#截图 alt+F4 可以退出
#非专业人士 .给需要用的人
 
import tkinter as tk
import pyautogui
import win32gui
import os,syjavascripts,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'''  保存指定区域屏图 '''
    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('截图工具')           # 设置窗口标题。
try:
    root.iconbitmap('3D.ico')     # 设置窗口左上角的图标。
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)        # 设置是否禁止调整窗口的宽和高。
win_width = root.winfo_width()                #获取窗口宽度(单位:像素)
win_height = root.winfo_height()              #获取窗口高度(单位:像素)
x=root.winfo_x()                              #获取窗口左上角的 x 坐标(单位:像素)
y=root.winfo_y()                              #获取窗口左上角的 y 坐标(单位:像素)
root.overrideredirect(True)                 #参数True,隐藏窗口标题栏。即不显示窗口标题和最大化、最小化、关闭按钮。
root.attributes("-alpha",0.5)                 #设置窗口的透明度 0.8, 参数为 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'] = '你移动了光标的所在位置'
    lb2['text'] = '目前光标位置: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('光标当前位置',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='没有任何事件触发', 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)#鼠标左键按下事件
w.bind('<ButtonPress-3>',handle_MR)#鼠标右键按下事件
w.bind('<ButtonRelease-1>',handle_RL)#鼠标左键抬起事件
w.bind('<ButtonRelease-3>',handle_RR)#鼠标右键抬起事件
tk.mainloop()

2.调用windows API

调用windows API,速度快但是使用较复杂,有更好用的PyQt。

import time
import win32gui, win32ui, win32con, win32api
def window_capturejs(filename):
    hwnd = 0  # 窗口的编号,0号表示当前活跃窗口
    # 根据窗口句柄获取窗口的设备上下文DC(Divice Context)
    hwndDC = win32gui.GetWindowDC(hwnd)
    # 根据窗口的DC获取mfcDC
    mfcDC = win32ui.CreateDCFromHandle(hwndDC)
    # mfcDC创建可兼容的DC
    saveDC = mfcDC.CreateCompatibleDC()
    # 创建bigmap准备保存图片
    saveBitMap = win32ui.CreateBitmap()
    # 获取监控器信息
    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)

到此这篇关于使用Python实现屏幕截图工具的文章就介绍到这了,更多相关Python屏幕截图内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新开发

开发排行榜