使用Python结合Tkinter和PyAutoGUI开发精确截图工具

 更新时间:2025年03月31日 08:27:13   作者:winfredzhang  
在日常工作中,截图是一个非常常见的需求,虽然 Windows 自带截图工具,但有时我们需要更精确的截图方式,比如选取特定区域、快速保存截图并进行预览,本篇博客将介绍一个使用 Python 结合 Tkinter 和 PyAutoGUI 开发的精确截图工具,需要的朋友可以参考下

运行结果

全部代码

import tkinter as tk
from tkinter import ttk
import pyautogui
import numpy as np
from PIL import Image, ImageTk, ImageGrab
import os
from datetime import datetime
import time

class ImprovedScreenshotTool:
    def __init__(self):
        # Create the main window
        self.root = tk.Tk()
        self.root.title("精确截图工具")
        self.root.geometry("400x200")
        self.root.resizable(False, False)
        
        # Center the window
        self.center_window()
        
        # Create a frame for the controls
        control_frame = ttk.Frame(self.root)
        control_frame.pack(pady=10, fill=tk.X)
        
        # Create and place the screenshot button
        self.screenshot_btn = ttk.Button(
            control_frame, 
            text="开始截图",
            width=20,
            command=self.prepare_screenshot
        )
        self.screenshot_btn.pack(pady=10)
        
        # Status label
        self.status_label = ttk.Label(control_frame, text="就绪")
        self.status_label.pack(pady=5)
        
        # Preview frame
        self.preview_frame = ttk.LabelFrame(self.root, text="最近截图预览")
        self.preview_frame.pack(pady=10, padx=10, fill=tk.BOTH, expand=True)
        
        self.preview_label = ttk.Label(self.preview_frame)
        self.preview_label.pack(fill=tk.BOTH, expand=True)
        
        # Variables for region selection
        self.start_x = 0
        self.start_y = 0
        self.end_x = 0
        self.end_y = 0
        
        # Save directory
        pictures_dir = os.path.join(os.path.expanduser("~"), "Pictures")
        if os.path.exists(pictures_dir):
            self.save_dir = pictures_dir
        else:
            self.save_dir = os.path.join(os.path.expanduser("~"), "Desktop")
            
        # Make sure the directory exists
        if not os.path.exists(self.save_dir):
            os.makedirs(self.save_dir)
            
        # Last saved file
        self.last_saved_file = None
        
    def center_window(self):
        # Get screen width and height
        screen_width = self.root.winfo_screenwidth()
        screen_height = self.root.winfo_screenheight()
        
        # Calculate position
        x = (screen_width - 400) // 2
        y = (screen_height - 200) // 2
        
        # Set window position
        self.root.geometry(f"400x200+{x}+{y}")
            
    def prepare_screenshot(self):
        # Update status
        self.status_label.config(text="准备截图...")
        self.root.update()  # Force UI update
        
        # Minimize window
        self.root.withdraw()
        
        # Wait a moment for UI to update
        self.root.after(500, self.take_screenshot)
    
    def take_screenshot(self):
        # Create overlay window for selection
        overlay = ScreenshotOverlay(self)
        self.root.wait_window(overlay.overlay)
        
        # If cancelled, just return to normal
        if not hasattr(self, 'screenshot_region') or not self.screenshot_region:
            self.root.deiconify()
            self.status_label.config(text="已取消截图")
            return
            
        # Get the selection coordinates
        x1, y1, x2, y2 = self.screenshot_region
        
        # Convert to proper coordinates (top-left, bottom-right)
        left = min(x1, x2)
        top = min(y1, y2)
        right = max(x1, x2)
        bottom = max(y1, y2)
        
        # Ensure minimum size
        if right - left < 5 or bottom - top < 5:
            self.root.deiconify()
            self.status_label.config(text="选择区域太小")
            return
        
        # Take screenshot
        try:
            # Wait a moment to ensure overlay is gone
            time.sleep(0.3)
            
            # Capture the screen region
            screenshot = pyautogui.screenshot(region=(left, top, right-left, bottom-top))
            
            # Generate filename
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            filename = os.path.join(self.save_dir, f"screenshot_{timestamp}.png")
            
            # Save the screenshot
            screenshot.save(filename)
            self.last_saved_file = filename
            
            # Update preview
            self.update_preview(screenshot)
            
            # Update status
            self.status_label.config(text=f"截图已保存: {os.path.basename(filename)}")
            
        except Exception as e:
            self.status_label.config(text=f"截图错误: {str(e)}")
        
        # Show the main window again
        self.root.deiconify()
    
    def update_preview(self, image):
        # Resize for preview if needed
        preview_width = 360
        preview_height = 100
        
        width, height = image.size
        ratio = min(preview_width/width, preview_height/height)
        new_size = (int(width * ratio), int(height * ratio))
        
        resized = image.resize(new_size, Image.LANCZOS)
        
        # Convert to PhotoImage
        photo = ImageTk.PhotoImage(resized)
        
        # Update label
        self.preview_label.config(image=photo)
        self.preview_label.image = photo  # Keep a reference
    
    def run(self):
        self.root.mainloop()


class ScreenshotOverlay:
    def __init__(self, parent):
        self.parent = parent
        
        # Create fullscreen overlay window
        self.overlay = tk.Toplevel()
        self.overlay.attributes('-fullscreen', True)
        self.overlay.attributes('-alpha', 0.3)
        self.overlay.attributes('-topmost', True)
        
        # Make it semi-transparent with dark background
        self.overlay.configure(bg='black')
        
        # Add a canvas for drawing
        self.canvas = tk.Canvas(
            self.overlay,
            bg='#1a1a1a',
            highlightthickness=0,
            cursor="crosshair"
        )
        self.canvas.pack(fill=tk.BOTH, expand=True)
        
        # Variables for tracking
        self.start_x = None
        self.start_y = None
        self.rect_id = None
        self.magnifier_id = None
        self.coords_text_id = None
        
        # Instructions text
        self.canvas.create_text(
            self.overlay.winfo_screenwidth() // 2,
            50,
            text="单击并拖动鼠标选择截图区域 | 按ESC取消",
            fill="white",
            font=("Arial", 16)
        )
        
        # Bind events
        self.canvas.bind("<ButtonPress-1>", self.on_press)
        self.canvas.bind("<B1-Motion>", self.on_drag)
        self.canvas.bind("<ButtonRelease-1>", self.on_release)
        self.overlay.bind("<Escape>", self.on_cancel)
        
        # Take a full screenshot for the magnifier
        self.screen_image = pyautogui.screenshot()
        self.screen_array = np.array(self.screen_image)
    
    def on_press(self, event):
        # Record start position
        self.start_x = event.x
        self.start_y = event.y
        
        # Create rectangle
        self.rect_id = self.canvas.create_rectangle(
            self.start_x, self.start_y, 
            self.start_x, self.start_y,
            outline="#00ff00", width=2, fill=""
        )
        
        # Create magnifier circle
        self.magnifier_id = self.canvas.create_oval(
            event.x - 50, event.y - 50,
            event.x + 50, event.y + 50,
            outline="#ffffff", width=2, fill="#333333"
        )
        
        # Create coordinate display
        self.coords_text_id = self.canvas.create_text(
            event.x, event.y - 60,
            text=f"({event.x}, {event.y})",
            fill="#ffffff",
            font=("Arial", 10)
        )
    
    def on_drag(self, event):
        # Update rectangle
        self.canvas.coords(
            self.rect_id,
            self.start_x, self.start_y,
            event.x, event.y
        )
        
        # Update magnifier position
        self.canvas.coords(
            self.magnifier_id,
            event.x - 50, event.y - 50,
            event.x + 50, event.y + 50
        )
        
        # Update coordinate display
        self.canvas.coords(
            self.coords_text_id,
            event.x, event.y - 60
        )
        self.canvas.itemconfig(
            self.coords_text_id,
            text=f"({event.x}, {event.y}) | 大小: {abs(event.x - self.start_x)}x{abs(event.y - self.start_y)}"
        )
        
        # Draw selection area with fill (using a valid color format)
        self.canvas.itemconfig(
            self.rect_id, 
            fill="#22ff22"  # Changed to a valid semi-transparent green
        )
    
    def on_release(self, event):
        # Store the selection coordinates in the parent
        self.parent.screenshot_region = (
            self.start_x, self.start_y, 
            event.x, event.y
        )
        
        # Close the overlay
        self.overlay.destroy()
    
    def on_cancel(self, event):
        # Reset parent's screenshot region
        self.parent.screenshot_region = None
        
        # Close overlay
        self.overlay.destroy()


if __name__ == "__main__":
    app = ImprovedScreenshotTool()
    app.run()

1. 工具介绍

该工具具有以下功能:

  • 自定义截图区域:通过鼠标拖动选择截图区域。
  • 自动保存截图:截图会自动保存到 Pictures 文件夹或桌面。
  • 截图预览:最近一次截图会在工具界面中进行预览。
  • 用户友好的操作提示:提供状态提示,让用户清楚当前操作。

2. 主要技术栈

本工具基于以下 Python 库实现:

  • tkinter:用于构建 GUI 界面。
  • pyautogui:用于屏幕截图。
  • PIL (Pillow):用于图像处理和预览。
  • numpy:用于优化图像处理。
  • datetime 和 os:用于管理文件存储。

3. 代码结构解析

3.1 主窗口的创建

import tkinter as tk
from tkinter import ttk
import pyautogui
import numpy as np
from PIL import Image, ImageTk
import os
from datetime import datetime
import time

class ImprovedScreenshotTool:
    def __init__(self):
        self.root = tk.Tk()
        self.root.title("精确截图工具")
        self.root.geometry("400x200")
        self.root.resizable(False, False)
        self.center_window()
        
        self.create_widgets()
        self.setup_save_directory()

    def center_window(self):
        screen_width = self.root.winfo_screenwidth()
        screen_height = self.root.winfo_screenheight()
        x = (screen_width - 400) // 2
        y = (screen_height - 200) // 2
        self.root.geometry(f"400x200+{x}+{y}")

功能解析:

  • 创建 Tk 窗口,设置标题、大小并居中。
  • center_window 方法用于获取屏幕尺寸并计算窗口居中位置。

3.2 创建 GUI 组件

    def create_widgets(self):
        control_frame = ttk.Frame(self.root)
        control_frame.pack(pady=10, fill=tk.X)
        
        self.screenshot_btn = ttk.Button(control_frame, text="开始截图", width=20, command=self.prepare_screenshot)
        self.screenshot_btn.pack(pady=10)
        
        self.status_label = ttk.Label(control_frame, text="就绪")
        self.status_label.pack(pady=5)
        
        self.preview_frame = ttk.LabelFrame(self.root, text="最近截图预览")
        self.preview_frame.pack(pady=10, padx=10, fill=tk.BOTH, expand=True)
        
        self.preview_label = ttk.Label(self.preview_frame)
        self.preview_label.pack(fill=tk.BOTH, expand=True)

功能解析:

  • 创建按钮 开始截图,绑定 prepare_screenshot 方法。
  • 状态标签用于提示当前状态。
  • preview_frame 用于显示最近截图的预览。

3.3 截图逻辑

    def prepare_screenshot(self):
        self.status_label.config(text="准备截图...")
        self.root.update()
        self.root.withdraw()
        self.root.after(500, self.take_screenshot)

功能解析:

  • 按下截图按钮后,状态标签显示 “准备截图…”。
  • withdraw() 让主窗口最小化,避免干扰截图。
  • after(500, self.take_screenshot) 让窗口延迟 0.5 秒后调用 take_screenshot
    def take_screenshot(self):
        overlay = ScreenshotOverlay(self)
        self.root.wait_window(overlay.overlay)
        
        if not hasattr(self, 'screenshot_region') or not self.screenshot_region:
            self.root.deiconify()
            self.status_label.config(text="已取消截图")
            return
        
        x1, y1, x2, y2 = self.screenshot_region
        left, top = min(x1, x2), min(y1, y2)
        right, bottom = max(x1, x2), max(y1, y2)
        
        if right - left < 5 or bottom - top < 5:
            self.root.deiconify()
            self.status_label.config(text="选择区域太小")
            return
        
        time.sleep(0.3)
        screenshot = pyautogui.screenshot(region=(left, top, right-left, bottom-top))
        filename = os.path.join(self.save_dir, f"screenshot_{datetime.now().strftime('%Y%m%d_%H%M%S')}.png")
        screenshot.save(filename)
        self.last_saved_file = filename
        
        self.update_preview(screenshot)
        self.status_label.config(text=f"截图已保存: {os.path.basename(filename)}")
        self.root.deiconify()

功能解析:

  • ScreenshotOverlay 负责创建全屏幕覆盖窗口,允许用户选择截图区域。
  • 获取选定区域的坐标,并检查区域大小是否有效。
  • pyautogui.screenshot(region=(left, top, width, height)) 实现精准截图。
  • 保存截图并更新预览区域。

3.4 截图选择区域的实现

class ScreenshotOverlay:
    def __init__(self, parent):
        self.parent = parent
        self.overlay = tk.Toplevel()
        self.overlay.attributes('-fullscreen', True)
        self.overlay.attributes('-alpha', 0.3)
        self.overlay.attributes('-topmost', True)
        self.overlay.configure(bg='black')
        
        self.canvas = tk.Canvas(self.overlay, bg='#1a1a1a', highlightthickness=0, cursor="crosshair")
        self.canvas.pack(fill=tk.BOTH, expand=True)
        
        self.canvas.bind("<ButtonPress-1>", self.on_press)
        self.canvas.bind("<B1-Motion>", self.on_drag)
        self.canvas.bind("<ButtonRelease-1>", self.on_release)
        self.overlay.bind("<Escape>", self.on_cancel)

功能解析:

  • 创建全屏幕透明窗口,允许用户使用鼠标选择截图区域。
  • 监听鼠标点击 (on_press)、拖动 (on_drag)、释放 (on_release) 事件。

以上就是使用Python结合Tkinter和PyAutoGUI开发精确截图工具的详细内容,更多关于Python Tkinter PyAutoGUI截图工具的资料请关注脚本之家其它相关文章!

相关文章

  • python 巧用正则寻找字符串中的特定字符的位置方法

    python 巧用正则寻找字符串中的特定字符的位置方法

    下面小编就为大家分享一篇python 巧用正则寻找字符串中的特定字符的位置方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-05-05
  • 使用Python的PIL库给图像进行过滤

    使用Python的PIL库给图像进行过滤

    PIL是一个用于图像处理的Python库,它提供了各种功能,包括加载、保存、编辑和处理图像,你可以使用PIL库进行图像缩放、裁剪、旋转、滤镜应用等操作,本文将介绍如何使用Python的PIL库给图像进行过滤,需要的朋友可以参考下
    2023-08-08
  • Python实现封装打包自己写的代码,被python import

    Python实现封装打包自己写的代码,被python import

    这篇文章主要介绍了Python实现封装打包自己写的代码,被python import,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-07-07
  • python: line=f.readlines()消除line中\n的方法

    python: line=f.readlines()消除line中\n的方法

    这篇文章主要介绍了python: line=f.readlines()消除line中\n的方法,需要的朋友可以参考下
    2018-03-03
  • Python实现SMTP发送邮件详细教程

    Python实现SMTP发送邮件详细教程

    这篇文章主要为大家详细介绍了Python实现SMTP发送邮件详细教程,附SMTP邮件发送代码,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-08-08
  • 详解Python中namedtuple的使用

    详解Python中namedtuple的使用

    这篇文章主要介绍了Python中namedtuple的使用,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-04-04
  • Python实现发票自动校核微信机器人的方法

    Python实现发票自动校核微信机器人的方法

    这篇文章主要介绍了Python实现发票自动校核微信机器人的方法,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-05-05
  • python添加列表元素append(),extend()及 insert()

    python添加列表元素append(),extend()及 insert()

    这篇文章主要介绍了python添加列表元素append(),extend()及 insert(),列表是储存元素的数据类型,既然能存储元素,那么就类似数据库一样,增删改查的一些功能就不能少了。下面我们就来先看看添加列表元素方法有哪些,需要的朋友可以参考一下
    2022-03-03
  • pytorch-神经网络拟合曲线实例

    pytorch-神经网络拟合曲线实例

    今天小编就为大家分享一篇pytorch-神经网络拟合曲线实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-01-01
  • 详解Python之数据序列化(json、pickle、shelve)

    详解Python之数据序列化(json、pickle、shelve)

    本篇文章主要介绍了Python之数据序列化,本节要介绍的就是Python内置的几个用于进行数据序列化的模块,有兴趣的可以了解一下。
    2017-03-03

最新评论