如何用Tkinter/Pillow实现可框选区域的屏幕截图功能?
问题描述
我开发了一个用于存储错误信息的错误日志程序,希望为其添加截图存储功能。尝试用Pillow实现截图后,当前代码无法完成点击按钮后启动截图、框选区域并保存的完整流程。现有代码如下:
import csv import datetime import tkinter as tk from tkinter import messagebox import os import configparser from PIL import ImageGrab config = configparser.ConfigParser() config.read("C:/Users/Public/Documents/Intel/Strata/CfgFiles/Stationdata.ini") #Locationdata for PD dataframe. for key, value in config["StationData"].items(): print(value) snip_count = 0 config.read("C:/Users/Public/Documents/Intel/Strata/CfgFiles/Appinfo.ini") for key, version in config["About"].items(): print(version) def log_error(category, error): with open("//datagrovera.ra.intel.com/DeviceLabUser/Shared/Device lab Error Logger/error_log.txt", 'a', newline='')as error_log: writer = csv.writer(error_log, delimiter=',') writer.writerow([datetime.datetime.now(), error, category, value, version]) def main(): root = tk.Tk() root.geometry('300x515') root.title(value) category_var = tk.StringVar(root) category_var.set('Strata') category_dropdown = tk.OptionMenu(root, category_var, 'Strata', 'Prober', 'General') category_dropdown.pack(pady=10) error_label = tk.Label(root, text='Error:') error_label.pack() v=tk.Scrollbar(root, orient='vertical') v.pack(side=tk.RIGHT, fill=tk.Y) error_entry = tk.Text(root, yscrollcommand=v.set) error_entry.pack(expand = True, fill = tk.BOTH ) def log_error_callback(): category = category_var.get() error = error_entry.get('1.0','end') if not error: messagebox.showerror('Error', 'Please enter an error message') return log_error(category, error) messagebox.showinfo('Success', f'Error logged for {category} category') def delete(): error_entry.delete("1.0","end") log_button = tk.Button(root, text='Log Error', command=lambda:[log_error_callback(), delete()]) log_button.pack() # Define a function to capture the screen snip def capture_snip(): # Use the global variable global snip_count # Get the coordinates of the selected region x1 = root.winfo_rootx() + canvas.winfo_x() y1 = root.winfo_rooty() + canvas.winfo_y() x2 = x1 + canvas.winfo_width() y2 = y1 + canvas.winfo_height() # Grab the image of the region and save it to a folder with a unique name image = ImageGrab.grab().crop((x1,y1,x2,y2)) image.save(f"snip_{snip_count}.png") # Increment the snip count snip_count += 1 # Create a canvas to draw the region canvas = tk.Canvas(root, width=300, height=200, bg='white') canvas.pack() # Create a button to capture the screen snip button = tk.Button(root, text='Capture', command=capture_snip) button.pack() root.mainloop() if __name__ == '__main__': main()
现有代码的问题:仅固定截取程序内canvas区域,未实现自由框选屏幕区域的交互逻辑。
实现自由区域截图的完整方案
可以通过Tkinter结合Pillow实现完整截图流程,核心逻辑为:点击截图按钮后创建全屏半透明遮罩窗口,监听鼠标事件记录框选的起点与终点,完成框选后截取对应区域并保存。
修改后的完整代码如下:
import csv import datetime import tkinter as tk from tkinter import messagebox, Toplevel import os import configparser from PIL import ImageGrab config = configparser.ConfigParser() config.read("C:/Users/Public/Documents/Intel/Strata/CfgFiles/Stationdata.ini") for key, value in config["StationData"].items(): print(value) snip_count = 0 config.read("C:/Users/Public/Documents/Intel/Strata/CfgFiles/Appinfo.ini") for key, version in config["About"].items(): print(version) def log_error(category, error): with open("//datagrovera.ra.intel.com/DeviceLabUser/Shared/Device lab Error Logger/error_log.txt", 'a', newline='') as error_log: writer = csv.writer(error_log, delimiter=',') writer.writerow([datetime.datetime.now(), error, category, value, version]) def main(): root = tk.Tk() root.geometry('300x515') root.title(value) category_var = tk.StringVar(root) category_var.set('Strata') category_dropdown = tk.OptionMenu(root, category_var, 'Strata', 'Prober', 'General') category_dropdown.pack(pady=10) error_label = tk.Label(root, text='Error:') error_label.pack() v = tk.Scrollbar(root, orient='vertical') v.pack(side=tk.RIGHT, fill=tk.Y) error_entry = tk.Text(root, yscrollcommand=v.set) error_entry.pack(expand=True, fill=tk.BOTH) def log_error_callback(): category = category_var.get() error = error_entry.get('1.0', 'end') if not error.strip(): messagebox.showerror('Error', 'Please enter an error message') return log_error(category, error) messagebox.showinfo('Success', f'Error logged for {category} category') def delete(): error_entry.delete("1.0", "end") log_button = tk.Button(root, text='Log Error', command=lambda: [log_error_callback(), delete()]) log_button.pack() # 区域截图核心逻辑 def capture_snip(): global snip_count # 创建全屏半透明遮罩窗口 snip_win = Toplevel(root) snip_win.attributes('-fullscreen', True) snip_win.attributes('-alpha', 0.3) # 半透明效果 snip_win.attributes('-topmost', True) # 记录框选坐标 start_x = start_y = end_x = end_y = 0 selection_rect = None # 鼠标按下事件:记录起点 def on_mouse_down(event): nonlocal start_x, start_y, selection_rect start_x = event.x_root start_y = event.y_root # 创建矩形选框 selection_rect = snip_win.canvas.create_rectangle(start_x, start_y, start_x, start_y, outline='red', width=2) # 鼠标拖动事件:更新选框大小 def on_mouse_drag(event): nonlocal end_x, end_y, selection_rect end_x = event.x_root end_y = event.y_root snip_win.canvas.coords(selection_rect, start_x, start_y, end_x, end_y) # 鼠标释放事件:完成截图 def on_mouse_up(event): nonlocal start_x, start_y, end_x, end_y snip_win.destroy() # 关闭遮罩窗口 # 修正坐标顺序(确保x1<x2,y1<y2) x1 = min(start_x, end_x) y1 = min(start_y, end_y) x2 = max(start_x, end_x) y2 = max(start_y, end_y) # 截取区域并保存 if x2 > x1 and y2 > y1: # 确保选框有效 image = ImageGrab.grab(bbox=(x1, y1, x2, y2)) save_path = f"snip_{snip_count}.png" image.save(save_path) global snip_count snip_count += 1 messagebox.showinfo('Success', f"Screenshot saved as {save_path}") else: messagebox.showwarning('Warning', 'Invalid selection, no screenshot saved') # 在遮罩窗口添加画布用于绘制选框 snip_win.canvas = tk.Canvas(snip_win, cursor='cross') snip_win.canvas.pack(fill=tk.BOTH, expand=True) # 绑定鼠标事件 snip_win.canvas.bind('<ButtonPress-1>', on_mouse_down) snip_win.canvas.bind('<B1-Motion>', on_mouse_drag) snip_win.canvas.bind('<ButtonRelease-1>', on_mouse_up) # 按ESC取消截图 snip_win.bind('<Escape>', lambda e: snip_win.destroy()) # 创建截图按钮 snip_button = tk.Button(root, text='Capture Screenshot', command=capture_snip) snip_button.pack(pady=10) root.mainloop() if __name__ == '__main__': main()
关键功能说明
- 全屏遮罩窗口:点击截图按钮后生成全屏半透明窗口,让用户清晰定位截取区域,同时设置为置顶窗口避免被遮挡。
- 鼠标交互逻辑:
- 鼠标按下时记录框选起点,生成红色矩形选框
- 鼠标拖动时实时更新选框大小
- 鼠标释放时关闭遮罩并截取选框内区域
- 坐标修正:自动调整坐标顺序,确保截取区域的有效性
- 取消机制:按ESC键可取消当前截图操作
- 截图保存:用递增的
snip_count保证文件名唯一,保存为PNG格式图片
内容的提问来源于stack exchange,提问作者Marshmellow Physics
相关产品推荐
相关产品推荐

