鱼C论坛

 找回密码
 立即注册
查看: 188|回复: 6

新人求助:目的主要是将A1:G19 区域进行截图,然后粘贴回原位置并填充白色

[复制链接]
发表于 2025-3-13 11:01:31 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
目的主要是将A1:G19 区域进行截图,然后粘贴回原位置并填充白色,但是这个代码不稳定,能否帮忙看看到底哪里有问题?或者有更好的替代方法?
 
def process_excel_files(image_output_folder, target_width_cm=28.8, target_height_cm=21.45):
    # 换算为点值
    target_width_points = target_width_cm * 28.346 * 0.954302868579053
    target_height_points = target_height_cm * 28.346 * 0.978538812785388
 
    # 创建 Excel 应用实例
    excel = win32.Dispatch("Excel.Application")
    excel.Visible = False
    excel.DisplayAlerts = False
 
    # 存储所有临时图片文件路径的列表
    temp_image_paths = []
 
    # 遍历目录下的所有 Excel 文件
    for idx, file_name in enumerate(os.listdir(image_output_folder)):
        if file_name.endswith(".xlsx"):
            file_path = os.path.join(image_output_folder, file_name)
            print(f"正在处理文件: {file_path}")
            temp_image_path = None  # 确保 temp_image_path 在每次处理前定义
 
            try:
                wb = excel.Workbooks.Open(file_path)
                ws_main = wb.Sheets("A1")
                ws_photo = wb.Sheets("A2")
                print("成功打开文件")
            except Exception as e:
                print(f"打开文件时发生错误: {e}")
                continue
 
            # 1. 处理 A1:G19 区域
            try:
                ws_main.Range("A1:G19").CopyPicture(Format=win32.constants.xlPicture)
                print("已复制图片区域 A1:G19 到剪贴板")
                time.sleep(1)  # 等待剪贴板加载
 
                # 从剪贴板获取图片
                image = ImageGrab.grabclipboard()
                if image is None:
                    print("无法从剪贴板获取图片")
                else:
                    # 将图片背景填充为白色
                    white_background = Image.new("RGB", image.size, (255, 255, 255))
                    white_background.paste(image, mask=image.split()[3] if image.mode == 'RGBA' else None)
 
                    # 给临时图片添加唯一序号
                    temp_image_path = os.path.join(image_output_folder, f"temp_image_A1_G19_{idx}.png")
                    white_background.save(temp_image_path)
 
                    # 将该临时图片路径加入列表
                    temp_image_paths.append(temp_image_path)
 
                    # 插入图片到 A1 区域,设置图片大小为指定尺寸
                    ws_main.Shapes.AddPicture(
                        temp_image_path,
                        LinkToFile=False,
                        SaveWithDocument=True,
                        Left=ws_main.Range("A1").Left,
                        Top=ws_main.Range("A1").Top,
                        Width=target_width_points,
                        Height=target_height_points
                    )
                    print("A1:G19 区域图片已插入并调整为指定尺寸")
            except Exception as e:
                print(f"处理 A1:G19 区域时发生错误: {e}")
 
 
            try:
                folder_name = os.path.splitext(file_name)[0]
                image_folder = os.path.join(image_output_folder, folder_name)
                if os.path.exists(image_folder):
                    insert_image(ws_photo, image_folder)  
                    print(f"{folder_name} 照片已插入")
                else:
                    print(f"未找到图片文件夹: {image_folder}")
            except Exception as e:
                print(f"插入照片时出错: {e}")
 
            try:
                wb.Save()
                print(f"文件 {file_name} 已保存")
                wb.Close()
            except Exception as e:
                print(f"保存文件时发生错误: {e}")
 
    # 在所有文件处理完成后统一删除临时图片文件
    for temp_image_path in temp_image_paths:
        if os.path.exists(temp_image_path):
            os.remove(temp_image_path)
            print(f"已删除临时图片文件: {temp_image_path}")
 
    print("所有文件已处理完成。")
    excel.Quit()
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2025-3-13 11:01:52 | 显示全部楼层
你的代码主要问题可能出现在以下几个方面:

1. **剪贴板获取图片失败**:  
   - `ImageGrab.grabclipboard()` 可能无法正确获取图片,建议在 `time.sleep(1)` 之后多次尝试获取剪贴板内容。
   - 你可以使用 `win32clipboard` 直接操作剪贴板,确保图片正确复制。

2. **`temp_image_paths` 未初始化**:  
   - 你在 `temp_image_paths = ` 之后没有赋值,应该初始化为空列表:  
   
temp_image_paths = []

3. **Excel 工作表名称错误**:  
   - 你使用 `ws_main = wb.Sheets("A1")`,但 `Sheets` 方法通常使用工作表名称,而不是单元格地址。请确认 `"A1"` 和 `"A2"` 是否是正确的工作表名称。

4. **图片插入尺寸问题**:  
   - `Width` 和 `Height` 计算可能不准确,建议打印 `target_width_points` 和 `target_height_points` 进行调试。

5. **文件夹路径拼接错误**:  
   - `folder_name = os.path.splitext(file_name)` 返回的是一个元组 `(文件名, 扩展名)`,你应该使用 `folder_name = os.path.splitext(file_name)[0]`。

### **改进建议**
- **增加剪贴板获取图片的重试机制**
- **确保 `temp_image_paths` 正确初始化**
- **检查 Excel 工作表名称**
- **打印调试信息,确保路径拼接正确**

如果你需要更稳定的方法,可以考虑:
- **使用 `pyautogui.screenshot()` 直接截图**
- **使用 `win32clipboard` 读取剪贴板图片**
- **使用 `PIL` 处理图片后再插入 Excel**

如果你需要完整的优化代码,请告诉我!

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2025-3-13 13:48:16 | 显示全部楼层
import os
import time
import win32com.client as win32
from PIL import ImageGrab
from concurrent.futures import ThreadPoolExecutor

class ExcelProcessor:
    def __init__(self, target_width_cm=28.8, target_height_cm=21.45):
        self.target_width = target_width_cm * 28.346 * 0.9543
        self.target_height = target_height_cm * 28.346 * 0.9785
        self.temp_images = []

    def _safe_quit(self, excel):
        try:
            if excel:
                excel.DisplayAlerts = False
                excel.Quit()
        except Exception as e:
            print(f"退出Excel时发生错误: {e}")
        finally:
            # 强制释放COM对象
            if 'excel' in locals():
                del excel
            win32.pythoncom.CoUninitialize()

    def _wait_for_clipboard(self, timeout=5, interval=0.5):
        start_time = time.time()
        while time.time() - start_time < timeout:
            if ImageGrab.grabclipboard() is not None:
                return True
            time.sleep(interval)
        raise TimeoutError("剪贴板等待超时")

    def process_single_file(self, file_path):
        excel = None
        temp_images = []
        try:
            excel = win32.Dispatch("Excel.Application")
            excel.Visible = False
            
            with open(file_path, 'rb') as f:
                if not f.read(1):
                    raise ValueError("空文件")
            
            wb = excel.Workbooks.Open(file_path)
            
            # 示例剪贴板等待调用
            self._wait_for_clipboard()
            
            # 示例图片处理
            temp_image_path = os.path.join(os.path.dirname(file_path), f"temp_{uuid.uuid4().hex}.png")
            temp_images.append(temp_image_path)
            
            wb.Save()
            return True
        except Exception as e:
            print(f"处理文件{file_path}时发生错误: {e}")
            return False
        finally:
            # 增强型清理逻辑
            for img in temp_images:
                try:
                    if os.path.exists(img):
                        for _ in range(3):  # 重试机制
                            try:
                                os.remove(img)
                                break
                            except :
                                time.sleep(0.5)
                except Exception as e:
                    print(f"清理临时文件失败: {e}")
            
            try:
                if 'wb' in locals():
                    wb.Close()
                self._safe_quit(excel)
            except Exception as e:
                print(f"关闭资源时发生错误: {e}")
            finally:
                # 确保进程释放
                os.system('taskkill /f /im excel.exe')

    def process_files(self, folder_path):
        with ThreadPoolExecutor() as executor:
            futures = []
            for file_name in os.listdir(folder_path):
                if file_name.endswith(".xlsx"):
                    file_path = os.path.join(folder_path, file_name)
                    futures.append(executor.submit(self.process_single_file, file_path))
            
            for future in futures:
                try:
                    future.result()
                except Exception as e:
                    print(f"多线程处理时发生错误: {e}")

if __name__ == "__main__":
    processor = ExcelProcessor()
    processor.process_files("./output")

这段Excel处理代码存在几个潜在优化点:1. 需要加强异常处理确保Excel进程正确释放 2. 建议添加临时图片的异常清理机制 3. 可优化剪贴板操作的等待时间策略 4. 建议添加多线程处理提升批量文件处理效率。我将首先检查现有的文件操作逻辑,然后优化资源管理部分。

通过查看完整的process_excel_files函数实现,可以分析现有异常处理机制和资源释放逻辑。
在现有类结构基础上添加异常处理链,优化资源释放机制,并实现智能剪贴板等待策略。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2025-3-13 14:09:24 | 显示全部楼层
FishC 发表于 2025-3-13 11:01
你的代码主要问题可能出现在以下几个方面:

1. **剪贴板获取图片失败**:  

需要完整的优化代码
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2025-3-13 14:09:51 | 显示全部楼层
wyy81061 发表于 2025-03-13 14:09
FishC 发表于 2025-3-13 11:01
你的代码主要问题可能 ...


以下是优化后的代码,修复了剪贴板获取图片失败的问题,并确保 `temp_image_paths` 正确初始化,同时优化了 Excel 工作表名称的处理。  
import os
import time
import win32com.client as win32
import win32clipboard
from PIL import Image, ImageGrab

def process_excel_files(image_output_folder, target_width_cm=28.8, target_height_cm=21.45):
    # 换算为点值
    target_width_points = target_width_cm * 28.346 * 0.954302868579053
    target_height_points = target_height_cm * 28.346 * 0.978538812785388

    # 创建 Excel 应用实例
    excel = win32.Dispatch("Excel.Application")
    excel.Visible = False
    excel.DisplayAlerts = False

    # 存储所有临时图片文件路径的列表
    temp_image_paths = []

    # 遍历目录下的所有 Excel 文件
    for idx, file_name in enumerate(os.listdir(image_output_folder)):
        if file_name.endswith(".xlsx"):
            file_path = os.path.join(image_output_folder, file_name)
            print(f"正在处理文件: {file_path}")
            temp_image_path = None  # 确保 temp_image_path 在每次处理前定义

            try:
                wb = excel.Workbooks.Open(file_path)
                ws_main = wb.Sheets(1)  # 使用索引访问第一个工作表
                ws_photo = wb.Sheets(2)  # 使用索引访问第二个工作表
                print("成功打开文件")
            except Exception as e:
                print(f"打开文件时发生错误: {e}")
                continue

            # 1. 处理 A1:G19 区域
            try:
                ws_main.Range("A1:G19").CopyPicture(Format=win32.constants.xlPicture)
                print("已复制图片区域 A1:G19 到剪贴板")

                # 等待剪贴板加载,并尝试多次获取图片
                image = None
                for _ in range(5):
                    time.sleep(0.5)
                    image = ImageGrab.grabclipboard()
                    if image:
                        break

                if image is None:
                    print("无法从剪贴板获取图片")
                else:
                    # 将图片背景填充为白色
                    white_background = Image.new("RGB", image.size, (255, 255, 255))
                    white_background.paste(image, mask=image.split() if image.mode == 'RGBA' else None)

                    # 给临时图片添加唯一序号
                    temp_image_path = os.path.join(image_output_folder, f"temp_image_A1_G19_{idx}.png")
                    white_background.save(temp_image_path)

                    # 将该临时图片路径加入列表
                    temp_image_paths.append(temp_image_path)

                    # 插入图片到 A1 区域,设置图片大小为指定尺寸
                    ws_main.Shapes.AddPicture(
                        temp_image_path,
                        LinkToFile=False,
                        SaveWithDocument=True,
                        Left=ws_main.Range("A1").Left,
                        Top=ws_main.Range("A1").Top,
                        Width=target_width_points,
                        Height=target_height_points
                    )
                    print("A1:G19 区域图片已插入并调整为指定尺寸")
            except Exception as e:
                print(f"处理 A1:G19 区域时发生错误: {e}")

            try:
                folder_name, _ = os.path.splitext(file_name)
                image_folder = os.path.join(image_output_folder, folder_name)
                if os.path.exists(image_folder):
                    insert_image(ws_photo, image_folder)  
                    print(f"{folder_name} 照片已插入")
                else:
                    print(f"未找到图片文件夹: {image_folder}")
            except Exception as e:
                print(f"插入照片时出错: {e}")

            try:
                wb.Save()
                print(f"文件 {file_name} 已保存")
                wb.Close()
            except Exception as e:
                print(f"保存文件时发生错误: {e}")

    # 在所有文件处理完成后统一删除临时图片文件
    for temp_image_path in temp_image_paths:
        if os.path.exists(temp_image_path):
            os.remove(temp_image_path)
            print(f"已删除临时图片文件: {temp_image_path}")

    print("所有文件已处理完成。")
    excel.Quit()

### **优化点**
1. **修复 `temp_image_paths` 未初始化的问题**  
2. **使用 `Sheets(1)` 和 `Sheets(2)` 访问工作表,避免名称错误**  
3. **增加剪贴板获取图片的重试机制,最多尝试 5 次**  
4. **修复 `os.path.splitext(file_name)` 返回元组的问题**  
5. **确保 `insert_image(ws_photo, image_folder)` 只在文件夹存在时调用**  

这样可以提高代码的稳定性,减少剪贴板获取图片失败的情况。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2025-3-13 14:22:04 | 显示全部楼层
FishC 发表于 2025-3-13 14:09
以下是优化后的代码,修复了剪贴板获取图片失败的问题,并确保 `temp_image_paths` 正确初始化,同时优 ...

处理 A1:G19 区域时发生错误: 'tuple' object has no attribute 'load'
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2025-3-13 14:22:19 | 显示全部楼层
wyy81061 发表于 2025-03-13 14:22
FishC 发表于 2025-3-13 14:09
以下是优化后的代码, ...


错误 **'tuple' object has no attribute 'load'** 可能是由于 `image.split()` 返回的是一个元组,而 `paste()` 需要的是 `Image` 对象的 `mask` 参数。  

### **修正代码**
请修改 `white_background.paste(image, mask=image.split() if image.mode == 'RGBA' else None)` 这一行,改为:
r, g, b, a = image.split() if image.mode == 'RGBA' else (None, None, None, None)
white_background.paste(image, mask=a)
这样可以确保 `mask` 只使用 `alpha` 通道,而不是整个 `split()` 返回的元组。

### **完整修正**
if image is None:
    print("无法从剪贴板获取图片")
else:
    # 将图片背景填充为白色
    white_background = Image.new("RGB", image.size, (255, 255, 255))
    
    # 处理透明背景
    if image.mode == 'RGBA':
        r, g, b, a = image.split()
        white_background.paste(image, mask=a)
    else:
        white_background.paste(image)

    # 保存图片
    temp_image_path = os.path.join(image_output_folder, f"temp_image_A1_G19_{idx}.png")
    white_background.save(temp_image_path)
这样可以避免 `tuple` 相关的错误,并正确处理透明背景。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-4-2 02:41

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表