鱼C论坛

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

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

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

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

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

x
目的主要是将A1:G19 区域进行截图,然后粘贴回原位置并填充白色,但是这个代码不稳定,能否帮忙看看到底哪里有问题?或者有更好的替代方法?

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

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

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

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

  17.             try:
  18.                 wb = excel.Workbooks.Open(file_path)
  19.                 ws_main = wb.Sheets("A1")
  20.                 ws_photo = wb.Sheets("A2")
  21.                 print("成功打开文件")
  22.             except Exception as e:
  23.                 print(f"打开文件时发生错误: {e}")
  24.                 continue

  25.             # 1. 处理 A1:G19 区域
  26.             try:
  27.                 ws_main.Range("A1:G19").CopyPicture(Format=win32.constants.xlPicture)
  28.                 print("已复制图片区域 A1:G19 到剪贴板")
  29.                 time.sleep(1)  # 等待剪贴板加载

  30.                 # 从剪贴板获取图片
  31.                 image = ImageGrab.grabclipboard()
  32.                 if image is None:
  33.                     print("无法从剪贴板获取图片")
  34.                 else:
  35.                     # 将图片背景填充为白色
  36.                     white_background = Image.new("RGB", image.size, (255, 255, 255))
  37.                     white_background.paste(image, mask=image.split()[3] if image.mode == 'RGBA' else None)

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

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

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


  56.             try:
  57.                 folder_name = os.path.splitext(file_name)[0]
  58.                 image_folder = os.path.join(image_output_folder, folder_name)
  59.                 if os.path.exists(image_folder):
  60.                     insert_image(ws_photo, image_folder)  
  61.                     print(f"{folder_name} 照片已插入")
  62.                 else:
  63.                     print(f"未找到图片文件夹: {image_folder}")
  64.             except Exception as e:
  65.                 print(f"插入照片时出错: {e}")

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

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

  77.     print("所有文件已处理完成。")
  78.     excel.Quit()
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

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

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

2. **`temp_image_paths` 未初始化**:  
   - 你在 `temp_image_paths = ` 之后没有赋值,应该初始化为空列表:  
   
  1. 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 小助理,如未能正确解答您的问题,请继续追问。
小甲鱼最新课程 -> https://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函数实现,可以分析现有异常处理机制和资源释放逻辑。
在现有类结构基础上添加异常处理链,优化资源释放机制,并实现智能剪贴板等待策略。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

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

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

需要完整的优化代码
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

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


以下是优化后的代码,修复了剪贴板获取图片失败的问题,并确保 `temp_image_paths` 正确初始化,同时优化了 Excel 工作表名称的处理。  

  1. import os
  2. import time
  3. import win32com.client as win32
  4. import win32clipboard
  5. from PIL import Image, ImageGrab

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  86.     print("所有文件已处理完成。")
  87.     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 小助理,如未能正确解答您的问题,请继续追问。
小甲鱼最新课程 -> https://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'
小甲鱼最新课程 -> https://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)` 这一行,改为:
  1. r, g, b, a = image.split() if image.mode == 'RGBA' else (None, None, None, None)
  2. white_background.paste(image, mask=a)
复制代码

这样可以确保 `mask` 只使用 `alpha` 通道,而不是整个 `split()` 返回的元组。

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

  13.     # 保存图片
  14.     temp_image_path = os.path.join(image_output_folder, f"temp_image_A1_G19_{idx}.png")
  15.     white_background.save(temp_image_path)
复制代码

这样可以避免 `tuple` 相关的错误,并正确处理透明背景。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

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

本版积分规则

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

GMT+8, 2025-5-5 20:14

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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