企业项目管理、ORK、研发管理与敏捷开发工具平台

网站首页 > 精选文章 正文

1000 张图插入 Excel 要过夜?CodeBuddy 双模式批量处理,

wudianyun 2025-05-28 20:49:28 精选文章 2 ℃

本文所使用的 CodeBuddy 免费下载链接:[腾讯云代码助手 CodeBuddy - AI 时代的智能编程伙伴](
https://copilot.tencent.com/?fromSource=
gwzcw.9661261.9661261.9661261&utm_medium=cpc&utm_id=
gwzcw.9661261.9661261.9661261&from_column=20421&from=20421)

## 关于Excel图片批量插入的痛点

**单张插入的重复性劳动**

传统 Excel 仅支持 **逐张插入图片**(通过「插入图片」功能或复制粘贴),插入 1000 张图需重复操作千次,即便熟练用户每分钟插入 10 张,也需 **16 小时以上**,严重影响工作效率。

就好比我们老师在批量登记我们学生的成绩,脑子突然抽抽下信息就填错了

**缺乏原生批量导入功能**

Excel 本身未提供「批量选择文件夹内所有图片」的入口,用户需借助第三方插件或 VBA 代码实现批量插入,对非技术用户门槛极高。

_常见问题_:图片遮挡数据、单元格高度混乱、跨页显示不全等,需反复调试。

**图片位置与对齐失控**

手动拖动图片时,难以精准对齐单元格网格线,尤其在多列多行间操作,易出现「图片错位、行列间距不均」等问题,影响表格美观度。

**嵌入图片导致文件爆炸式增大**

每张高清图片嵌入 Excel 后,文件体积可能从几十 KB 飙升至数百 MB(如 1000 张 500KB 的图片直接使文件超过 500MB),导致 Excel 打开缓慢、保存卡顿,甚至触发「内存不足」报错。

| **传统 Excel 痛点** | **CodeBuddy 双模式的解决方案** |

| ---------------- | ---------------------------------------- |

| 手动插入耗时、批量功能缺失 | 「双模式」(文件夹批量导入 + 指定图片路径)一键处理,1000 张图分钟级完成 |

| 图片格式与单元格适配困难 | 自动匹配单元格尺寸、锁定位置,支持批量设置格式(如缩放比例、对齐方式) |

| 文件体积大、性能卡顿 | 提供「链接模式」(轻量)与「嵌入模式」(安全),按需选择,减少资源占用 |

| 操作门槛高(需 VBA 或插件) | 可视化界面操作,无需代码基础,新手可快速上手 |

随着CodeBuddy的爆火,我是否想能否生成一款属于我自己的批量化Excel文件图片的插入操作应用呢?

想法存在,那么我们立刻开始实操

## 使用CodeBuddy实现一个强大的Excel图片批量插入工具

我们打开vscode进入到拓展中直接搜索CodeBuddy,点击安装这个插件就行了

我们点击左侧的插件图标就能进行使用了

我这里准备了一张关于这款应用简单介绍的README文件,我们让CodeBuddy帮我们优化下

他这里介绍的很详细

那么我们直接再次@这个README文件,让他依据这个文件进行开发操作

这里也是很快的将应用代码生成出来了

我们在终端输入命令`code main.py`运行试试

这里我们直接将报错信息复制给CodeBuddy让它处理下

这里他开始进行了快速分析以及代码生成修改

代码修改好了之后,我们继续进行测试

这里我们成功打开了应用,看着风格还是不错的

我们选择一个图片文件夹

选中我们想插入的Excel文件

我们这里也是成功的将图片进行了插入操作了

因为我们这里没有将框框拉开,所以显现的有点狭窄了,但是整体的实现效果还是不错的,大家也可以去试试

示例代码如下:

```Python

import os

import re

import tkinter as tk

import webbrowser

from tkinter import filedialog, messagebox, ttk

from typing import Dict, Optional, Tuple


import xlwings as xw



class ExcelImageMatcherPro:

def __init__(self, master):

self.master = master

master.title("Excel批量匹配插图工具")

master.geometry("600x700")

# 应用主题和配色方案

self.setup_theme()

# 初始化变量

self.col_image_map: Dict[str, str] = {}

self.row_image_map: Dict[str, str] = {}

self.topmost_var = tk.BooleanVar(value=True)

# 创建主界面

self.create_main_interface()

# 窗口居中

self.center_window(master)

# 初始化帮助系统

self._create_help_tags()

self.show_help_guide()

# 绑定事件

self.notebook.bind("<<NotebookTabChanged>>", self.on_tab_changed)

master.attributes('-topmost', self.topmost_var.get())


def setup_theme(self):

"""设置应用主题和配色方案"""

style = ttk.Style()

# 主色调 - 紫色系

primary_color = "#7B1FA2"

secondary_color = "#9C27B0"

accent_color = "#E1BEE7"

# 文本颜色

text_color = "#333333"

light_text = "#FFFFFF"

# 状态颜色

success_color = "#4CAF50"

warning_color = "#FFC107"

error_color = "#F44336"

info_color = "#2196F3"

# 配置主题

style.theme_create("custom_theme", parent="clam", settings={

"TFrame": {"configure": {"background": "#F5F5F5"}},

"TLabel": {"configure": {"foreground": text_color, "background": "#F5F5F5", "font": ('Microsoft YaHei', 9)}},

"TButton": {

"configure": {

"foreground": light_text,

"background": primary_color,

"font": ('Microsoft YaHei', 9),

"padding": 5,

"borderwidth": 1,

"relief": "raised"

},

"map": {

"background": [("active", secondary_color), ("disabled", "#CCCCCC")],

"foreground": [("disabled", "#999999")]

}

},

"TEntry": {

"configure": {

"fieldbackground": "white",

"foreground": text_color,

"insertcolor": text_color,

"font": ('Microsoft YaHei', 9)

}

},

"TCombobox": {

"configure": {

"fieldbackground": "white",

"foreground": text_color,

"selectbackground": accent_color,

"font": ('Microsoft YaHei', 9)

}

},

"TNotebook": {

"configure": {

"background": "#F5F5F5",

"tabmargins": [2, 5, 2, 0]

}

},

"TNotebook.Tab": {

"configure": {

"background": "#E0E0E0",

"foreground": text_color,

"padding": [10, 5],

"font": ('Microsoft YaHei', 9, 'bold')

},

"map": {

"background": [("selected", "#FFFFFF"), ("active", "#EEEEEE")],

"expand": [("selected", [1, 1, 1, 0])]

}

},

"TScrollbar": {

"configure": {

"background": "#E0E0E0",

"troughcolor": "#F5F5F5",

"arrowcolor": text_color

}

},

"Horizontal.TProgressbar": {

"configure": {

"background": primary_color,

"troughcolor": "#E0E0E0",

"borderwidth": 0,

"lightcolor": primary_color,

"darkcolor": primary_color

}

}

})

style.theme_use("custom_theme")


def create_main_interface(self):

"""创建主界面组件"""

# 主容器

main_frame = ttk.Frame(self.master)

main_frame.pack(fill="both", expand=True, padx=10, pady=10)

# 标题栏

title_frame = ttk.Frame(main_frame)

title_frame.pack(fill="x", pady=(0, 10))

title_label = ttk.Label(

title_frame,

text="Excel批量匹配插图工具",

font=('Microsoft YaHei', 12, 'bold'),

foreground="#7B1FA2"

)

title_label.pack(side="left")

# 标签页控件

self.notebook = ttk.Notebook(main_frame)

self.notebook.pack(fill="both", expand=True)

# 创建两个标签页

self.create_column_tab()

self.create_row_tab()

# 状态栏

self.create_status_bar()


def create_column_tab(self):

"""创建列匹配模式标签页"""

tab = ttk.Frame(self.notebook)

self.notebook.add(tab, text="列匹配模式")

# 描述区域

desc_frame = ttk.LabelFrame(

tab,

text="说明",

padding=10,

style="Custom.TLabelframe"

)

desc_frame.pack(fill="x", padx=5, pady=5)

ttk.Label(

desc_frame,

text="列匹配模式:按垂直方向匹配插入,适合单列数据匹配。\n图片名称需与指定列中的单元格内容完全匹配。",

foreground="#616161",

font=('Microsoft YaHei', 9)

).pack(anchor="w")

# Excel文件选择区域

excel_frame = ttk.LabelFrame(tab, text="Excel文件设置", padding=10)

excel_frame.pack(fill="x", padx=5, pady=5)

self.col_excel_var = tk.StringVar(value="使用当前活动工作簿")

ttk.Label(excel_frame, text="Excel文件:").grid(row=0, column=0, padx=5, pady=5, sticky="e")

excel_entry = ttk.Entry(

excel_frame,

textvariable=self.col_excel_var,

width=40,

state="readonly",

style="Custom.TEntry"

)

excel_entry.grid(row=0, column=1, padx=5, pady=5, sticky="ew")

btn_frame = ttk.Frame(excel_frame)

btn_frame.grid(row=0, column=2, padx=5, pady=5, sticky="e")

ttk.Button(

btn_frame,

text="浏览...",

command=lambda: self.select_excel_file(self.col_excel_var),

style="Accent.TButton"

).pack(side="left", padx=2)

ttk.Button(

btn_frame,

text="清除",

command=lambda: self.col_excel_var.set("使用当前活动工作簿")

).pack(side="left", padx=2)

# 参数设置区域

param_frame = ttk.LabelFrame(tab, text="匹配参数设置", padding=10)

param_frame.pack(fill="x", padx=5, pady=5)

# 第一行参数

row1_frame = ttk.Frame(param_frame)

row1_frame.pack(fill="x", pady=5)

ttk.Label(row1_frame, text="起始行号:").pack(side="left", padx=5)

self.col_start_row = ttk.Entry(row1_frame, width=8)

self.col_start_row.pack(side="left", padx=5)

self.col_start_row.insert(0, "2")

ttk.Label(row1_frame, text="匹配列:").pack(side="left", padx=5)

self.col_match = ttk.Entry(row1_frame, width=8)

self.col_match.pack(side="left", padx=5)

self.col_match.insert(0, "A")

ttk.Label(row1_frame, text="插入列:").pack(side="left", padx=5)

self.col_insert = ttk.Entry(row1_frame, width=8)

self.col_insert.pack(side="left", padx=5)

self.col_insert.insert(0, "B")

# 第二行参数

row2_frame = ttk.Frame(param_frame)

row2_frame.pack(fill="x", pady=5)

ttk.Label(row2_frame, text="边距:").pack(side="left", padx=5)

self.col_margin = ttk.Entry(row2_frame, width=8)

self.col_margin.pack(side="left", padx=5)

self.col_margin.insert(0, "2")

# 图片文件夹选择

folder_frame = ttk.Frame(param_frame)

folder_frame.pack(fill="x", pady=10)

self.col_folder_var = tk.StringVar()

ttk.Label(folder_frame, text="图片文件夹:").pack(side="left", padx=5)

folder_entry = ttk.Entry(

folder_frame,

textvariable=self.col_folder_var,

width=40,

state="readonly"

)

folder_entry.pack(side="left", padx=5, expand=True, fill="x")

ttk.Button(

folder_frame,

text="浏览...",

command=lambda: self.select_folder(self.col_folder_var, mode="column"),

style="Accent.TButton"

).pack(side="left", padx=5)

# 执行按钮

btn_frame = ttk.Frame(tab)

btn_frame.pack(fill="x", padx=5, pady=10)

ttk.Button(

btn_frame,

text="执行列匹配插入",

command=self.run_column_match,

style="Primary.TButton"

).pack(fill="x", expand=True)

# 日志区域

log_frame = ttk.LabelFrame(tab, text="操作日志", padding=10)

log_frame.pack(fill="both", expand=True, padx=5, pady=5)

self.col_log = tk.Text(

log_frame,

wrap=tk.WORD,

height=10,

state="disabled",

font=('Microsoft YaHei', 9),

bg="white",

fg="#333333",

padx=5,

pady=5

)

scroll = ttk.Scrollbar(log_frame, command=self.col_log.yview)

self.col_log.configure(yscrollcommand=scroll.set)

self.col_log.pack(side="left", fill="both", expand=True)

scroll.pack(side="right", fill="y")


def create_row_tab(self):

"""创建行匹配模式标签页"""

tab = ttk.Frame(self.notebook)

self.notebook.add(tab, text="行匹配模式")

# 描述区域

desc_frame = ttk.LabelFrame(tab, text="说明", padding=10)

desc_frame.pack(fill="x", padx=5, pady=5)

ttk.Label(

desc_frame,

text="行匹配模式:按水平方向匹配插入,适合单行数据匹配。\n图片名称需与指定行中的单元格内容完全匹配。",

foreground="#616161",

font=('Microsoft YaHei', 9)

).pack(anchor="w")

# Excel文件选择区域

excel_frame = ttk.LabelFrame(tab, text="Excel文件设置", padding=10)

excel_frame.pack(fill="x", padx=5, pady=5)

self.row_excel_var = tk.StringVar(value="使用当前活动工作簿")

ttk.Label(excel_frame, text="Excel文件:").grid(row=0, column=0, padx=5, pady=5, sticky="e")

excel_entry = ttk.Entry(

excel_frame,

textvariable=self.row_excel_var,

width=40,

state="readonly"

)

excel_entry.grid(row=0, column=1, padx=5, pady=5, sticky="ew")

btn_frame = ttk.Frame(excel_frame)

btn_frame.grid(row=0, column=2, padx=5, pady=5, sticky="e")

ttk.Button(

btn_frame,

text="浏览...",

command=lambda: self.select_excel_file(self.row_excel_var),

style="Accent.TButton"

).pack(side="left", padx=2)

ttk.Button(

btn_frame,

text="清除",

command=lambda: self.row_excel_var.set("使用当前活动工作簿")

).pack(side="left", padx=2)

# 参数设置区域

param_frame = ttk.LabelFrame(tab, text="匹配参数设置", padding=10)

param_frame.pack(fill="x", padx=5, pady=5)

# 参数行

row_frame = ttk.Frame(param_frame)

row_frame.pack(fill="x", pady=10)

ttk.Label(row_frame, text="匹配行:").pack(side="left", padx=5)

self.row_match = ttk.Entry(row_frame, width=8)

self.row_match.pack(side="left", padx=5)

self.row_match.insert(0, "1")

ttk.Label(row_frame, text="插入行:").pack(side="left", padx=5)

self.row_insert = ttk.Entry(row_frame, width=8)

self.row_insert.pack(side="left", padx=5)

self.row_insert.insert(0, "2")

ttk.Label(row_frame, text="边距:").pack(side="left", padx=5)

self.row_margin = ttk.Entry(row_frame, width=8)

self.row_margin.pack(side="left", padx=5)

self.row_margin.insert(0, "2")

# 图片文件夹选择

folder_frame = ttk.Frame(param_frame)

folder_frame.pack(fill="x", pady=10)

self.row_folder_var = tk.StringVar()

ttk.Label(folder_frame, text="图片文件夹:").pack(side="left", padx=5)

folder_entry = ttk.Entry(

folder_frame,

textvariable=self.row_folder_var,

width=40,

state="readonly"

)

folder_entry.pack(side="left", padx=5, expand=True, fill="x")

ttk.Button(

folder_frame,

text="浏览...",

command=lambda: self.select_folder(self.row_folder_var, mode="row"),

style="Accent.TButton"

).pack(side="left", padx=5)

# 执行按钮

btn_frame = ttk.Frame(tab)

btn_frame.pack(fill="x", padx=5, pady=10)

ttk.Button(

btn_frame,

text="执行行匹配插入",

command=self.run_row_match,

style="Primary.TButton"

).pack(fill="x", expand=True)

# 日志区域

log_frame = ttk.LabelFrame(tab, text="操作日志", padding=10)

log_frame.pack(fill="both", expand=True, padx=5, pady=5)

self.row_log = tk.Text(

log_frame,

wrap=tk.WORD,

height=10,

state="disabled",

font=('Microsoft YaHei', 9),

bg="white",

fg="#333333",

padx=5,

pady=5

)

scroll = ttk.Scrollbar(log_frame, command=self.row_log.yview)

self.row_log.configure(yscrollcommand=scroll.set)

self.row_log.pack(side="left", fill="both", expand=True)

scroll.pack(side="right", fill="y")


def create_status_bar(self):

"""创建状态栏"""

status_frame = ttk.Frame(self.master, padding=(10, 5))

status_frame.pack(side="bottom", fill="x")

# 窗口置顶按钮

ttk.Checkbutton(

status_frame,

text="窗口置顶",

variable=self.topmost_var,

command=lambda: self.master.attributes('-topmost', self.topmost_var.get())

).pack(side="left", padx=(0, 10))

# 帮助按钮

ttk.Button(

status_frame,

text="帮助",

width=8,

command=self.show_help_guide

).pack(side="left", padx=(0, 10))

# 版本信息

version_label = ttk.Label(

status_frame,

text="版本: 1.0.0",

foreground="gray"

)

version_label.pack(side="left", padx=(0, 10))

# 作者信息

author_label = tk.Label(

status_frame,

text="By 创客白泽",

fg="gray",

cursor="hand2",

font=('Microsoft YaHei', 9)

)

author_label.bind("<Enter>", lambda e: author_label.config(fg="#7B1FA2"))

author_label.bind("<Leave>", lambda e: author_label.config(fg="gray"))

author_label.bind(

"<Button-1>",

lambda e: webbrowser.open("https://www.52pojie.cn/thread-2030255-1-1.html")

)

author_label.pack(side="right")


def _create_help_tags(self):

"""创建日志文本标签样式"""

for log in [self.col_log, self.row_log]:

log.tag_config("title", foreground="#7B1FA2", font=('Microsoft YaHei', 10, 'bold'))

log.tag_config("success", foreground="#4CAF50")

log.tag_config("warning", foreground="#FF9800")

log.tag_config("error", foreground="#F44336")

log.tag_config("info", foreground="#2196F3")

log.tag_config("preview", foreground="#616161")

log.tag_config("highlight", background="#E1BEE7")


def center_window(self, window):

"""窗口居中显示"""

window.update_idletasks()

width = window.winfo_width()

height = window.winfo_height()

screen_width = window.winfo_screenwidth()

screen_height = window.winfo_screenheight()

x = (screen_width - width) // 2

y = (screen_height - height) // 2

window.geometry(f"{width}x{height}+{x}+{y}")


def show_help_guide(self, target_log=None):

"""显示帮助指南"""

help_text = """【新手操作指南 - 点下方"帮助"按钮可再次显示】


1. 准备工作:

- 选择Excel文件或使用当前活动工作簿

- 准备图片文件夹(支持jpg/png/webp/bmp格式)


2. 参数设置:

- Excel文件:选择要操作的工作簿(可选)

- 起始行号:从哪一行开始匹配(默认为2)

- 匹配列/行:包含名称的列或行(如A列或1行)

- 插入列/行:图片要插入的位置(如B列或2行)

- 边距:图片与单元格边界的距离(推荐2,0表示撑满)


3. 执行步骤:

(1) 选择Excel文件(可选)

(2) 选择图片文件夹

(3) 点击"执行匹配插入"按钮


★ 注意事项:

- 图片名称需与单元格内容完全一致(不区分大小写)

- 示例:单元格"产品A" → 图片"产品A.jpg"

- 插入过程中请不要操作Excel

"""


if target_log is None:

current_tab = self.notebook.index("current")

target_log = self.col_log if current_tab == 0 else self.row_log


self.log_message(help_text, target_log, append=False, tags="info")


def select_excel_file(self, var_tk_stringvar):

"""选择Excel文件"""

file_path = filedialog.askopenfilename(

filetypes=[("Excel文件", "*.xls *.xlsx *.xlsm"), ("所有文件", "*.*")])

if file_path:

var_tk_stringvar.set(file_path)


def select_folder(self, var_tk_stringvar, mode):

"""选择图片文件夹"""

folder_path = filedialog.askdirectory()

if folder_path:

var_tk_stringvar.set(folder_path)

log_widget = self.col_log if mode == "column" else self.row_log


self.log_message("开始加载图片...", log_widget, append=False)


current_image_map = self.build_image_map_from_folder(folder_path)


if mode == "column":

self.col_image_map = current_image_map

elif mode == "row":

self.row_image_map = current_image_map


if len(current_image_map) > 0:

self.log_message(

f"加载完成:找到 {len(current_image_map)} 张支持的图片。",

log_widget,

tags="success"

)

else:

self.log_message("警告: 未找到任何支持的图片文件。", log_widget, tags="warning")


self.preview_insert_positions(mode)


def build_image_map_from_folder(self, folder_path: str) -> Dict[str, str]:

"""从文件夹构建图片名称到路径的映射"""

image_map: Dict[str, str] = {}

extensions = ('.jpg', '.jpeg', '.png', '.bmp', '.webp')

try:

for root, _, files in os.walk(folder_path):

for file in files:

if file.lower().endswith(extensions):

name_without_ext = os.path.splitext(file)[0].strip().lower()

image_map[name_without_ext] = os.path.abspath(os.path.join(root, file))

except Exception as e:

self.log_message(

f"构建图片映射出错: {e}",

self.col_log if 'col' in self._current_mode else self.row_log,

tags="error"

)

return image_map


def validate_column_params(self) -> Dict:

"""验证列匹配参数"""

params = {

'match_col': self.col_match.get().upper(),

'insert_col': self.col_insert.get().upper(),

'start_row': self.col_start_row.get(),

'margin': self.col_margin.get(),

'excel_file': self.col_excel_var.get()

}

if not re.match(r'^[A-Z]{1,3}#39;, params['match_col']):

raise ValueError("匹配列格式错误 (例如: A, B, AA)")

if not re.match(r'^[A-Z]{1,3}#39;, params['insert_col']):

raise ValueError("插入列格式错误 (例如: A, B, AA)")

if not params['start_row'].isdigit() or int(params['start_row']) < 1:

raise ValueError("起始行号必须是大于0的数字")

if not params['margin'].isdigit() or int(params['margin']) < 0:

raise ValueError("边距必须是非负数字")

return {

'match_col': params['match_col'],

'insert_col': params['insert_col'],

'start_row': int(params['start_row']),

'margin': int(params['margin']),

'excel_file': params['excel_file']

}


def validate_row_params(self) -> Dict:

"""验证行匹配参数"""

params = {

'match_row': self.row_match.get(),

'insert_row': self.row_insert.get(),

'margin': self.row_margin.get(),

'excel_file': self.row_excel_var.get()

}

if not params['match_row'].isdigit() or int(params['match_row']) < 1:

raise ValueError("匹配行必须是大于0的数字")

if not params['insert_row'].isdigit() or int(params['insert_row']) < 1:

raise ValueError("插入行必须是大于0的数字")

if not params['margin'].isdigit() or int(params['margin']) < 0:

raise ValueError("边距必须是非负数字")

return {

'match_row': int(params['match_row']),

'insert_row': int(params['insert_row']),

'margin': int(params['margin']),

'excel_file': params['excel_file']

}


def get_excel_app(self) -> xw.App:

"""获取Excel应用实例,优先使用已打开的实例"""

try:

# 尝试获取已打开的Excel应用

app = xw.apps.active

if app is not None:

return app

# 如果没有打开的Excel,尝试获取第一个Excel实例

if len(xw.apps) > 0:

return xw.apps[0]

# 如果都没有,则创建新实例

return xw.App(visible=True)

except Exception as e:

raise Exception(f"无法获取Excel应用实例: {str(e)}")


def excel_operation(self, excel_file: Optional[str] = None) -> Tuple[xw.Book, xw.Sheet]:

"""Excel操作上下文管理器,正确处理已打开的工作簿"""

app = self.get_excel_app()

try:

if excel_file and excel_file != "使用当前活动工作簿":

# 检查工作簿是否已经打开

for book in app.books:

if book.fullname.lower() == os.path.abspath(excel_file).lower():

wb = book

break

else:

wb = app.books.open(excel_file)

else:

# 使用活动工作簿或第一个工作簿

wb = app.books.active

if wb is None and len(app.books) > 0:

wb = app.books[0]

if wb is None:

raise Exception("没有可用的工作簿,请先打开或创建一个工作簿")

ws = wb.sheets.active

if ws is None:

raise Exception("工作簿中没有活动的工作表")

return wb, ws

except Exception as e:

raise Exception(f"Excel操作错误: {str(e)}")


def insert_image(self, ws, image_path, cell_addr, margin, log_widget):

"""在Excel中插入图片"""

try:

abs_image_path = os.path.abspath(image_path)

if not os.path.exists(abs_image_path):

self.log_message(f"错误: 图片文件不存在 - {abs_image_path}", log_widget, tags="error")

return False


target_cell = ws.range(cell_addr)


left = target_cell.left + margin

top = target_cell.top + margin

width = target_cell.width - 2 * margin

height = target_cell.height - 2 * margin


ws.pictures.add(

abs_image_path,

left=left,

top=top,

width=width,

height=height

)

return True


except Exception as e:

error_msg = f"插入图片失败: {str(e)}"

self.log_message(error_msg, log_widget, tags="error")

return False


def run_column_match(self):

"""执行列匹配插入"""

self.log_message("开始列匹配处理...", self.col_log, append=False, tags="title")

try:

if not self.col_folder_var.get():

messagebox.showwarning("提示", "请先选择图片文件夹!")

self.log_message("错误: 未选择图片文件夹。", self.col_log, tags="error")

return


params = self.validate_column_params()

wb, ws = self.excel_operation(params['excel_file'])

max_row = ws.used_range.last_cell.row

success_inserts = 0

processed_excel_rows = 0

non_empty_match_cells = 0


self.log_message(

f"将在列 {params['match_col']} 中查找名称,图片插入到列 {params['insert_col']},从行 {params['start_row']} 开始。",

self.col_log,

tags="info"

)


for row_num_excel in range(params['start_row'], max_row + 1):

processed_excel_rows += 1

match_cell_addr = f"{params['match_col']}{row_num_excel}"


cell_value = ws.range(match_cell_addr).value

name_to_match = str(cell_value).strip() if cell_value is not None else ""


if not name_to_match:

continue


non_empty_match_cells += 1

insert_cell_addr = f"{params['insert_col']}{row_num_excel}"

lower_name_to_match = name_to_match.lower()


if lower_name_to_match in self.col_image_map:

image_file_path = os.path.abspath(self.col_image_map[lower_name_to_match])

if not os.path.exists(image_file_path):

self.log_message(

f"错误: 图片文件不存在 - {image_file_path}",

self.col_log,

tags="error"

)

continue


if self.insert_image(ws, image_file_path, insert_cell_addr, params['margin'], self.col_log):

success_inserts += 1

self.log_message(

f"{match_cell_addr}:{name_to_match} → {insert_cell_addr}:{os.path.basename(image_file_path)}",

self.col_log,

tags="success"

)

else:

self.log_message(

f"{match_cell_addr}:{name_to_match} → 未找到匹配图片",

self.col_log,

tags="warning"

)


summary = f"\n共处理 {processed_excel_rows} 行数据,成功插入 {success_inserts} 张图片。"

self.log_message(summary, self.col_log, tags="info")


except Exception as e:

error_msg = f"列匹配错误: {str(e)}"

self.log_message(error_msg, self.col_log, tags="error")

messagebox.showerror("错误", error_msg)


def run_row_match(self):

"""执行行匹配插入"""

self.log_message("开始行匹配处理...", self.row_log, append=False, tags="title")

try:

if not self.row_folder_var.get():

messagebox.showwarning("提示", "请先选择图片文件夹!")

self.log_message("错误: 未选择图片文件夹。", self.row_log, tags="error")

return


params = self.validate_row_params()

wb, ws = self.excel_operation(params['excel_file'])

max_col = ws.used_range.last_cell.column

success_inserts = 0

processed_excel_cols = 0

non_empty_match_cells = 0


self.log_message(

f"将在行 {params['match_row']} 中查找名称,图片插入到行 {params['insert_row']}。",

self.row_log,

tags="info"

)


for col_num_excel in range(1, max_col + 1):

processed_excel_cols += 1

match_cell_addr = f"{xw.utils.col_name(col_num_excel)}{params['match_row']}"


cell_value = ws.range(match_cell_addr).value

name_to_match = str(cell_value).strip() if cell_value is not None else ""


if not name_to_match:

continue


non_empty_match_cells += 1

insert_cell_addr = f"{xw.utils.col_name(col_num_excel)}{params['insert_row']}"

lower_name_to_match = name_to_match.lower()


if lower_name_to_match in self.row_image_map:

image_file_path = os.path.abspath(self.row_image_map[lower_name_to_match])

if not os.path.exists(image_file_path):

self.log_message(

f"错误: 图片文件不存在 - {image_file_path}",

self.row_log,

tags="error"

)

continue


if self.insert_image(ws, image_file_path, insert_cell_addr, params['margin'], self.row_log):

success_inserts += 1

self.log_message(

f"{match_cell_addr}:{name_to_match} → {insert_cell_addr}:{os.path.basename(image_file_path)}",

self.row_log,

tags="success"

)

else:

self.log_message(

f"{match_cell_addr}:{name_to_match} → 未找到匹配图片",

self.row_log,

tags="warning"

)


summary = f"\n共处理 {processed_excel_cols} 列数据,成功插入 {success_inserts} 张图片。"

self.log_message(summary, self.row_log, tags="info")


except Exception as e:

error_msg = f"行匹配错误: {str(e)}"

self.log_message(error_msg, self.row_log, tags="error")

messagebox.showerror("错误", error_msg)


def preview_insert_positions(self, mode):

"""预览插入位置"""

image_map = self.col_image_map if mode == "column" else self.row_image_map

log_widget = self.col_log if mode == "column" else self.row_log


if not image_map:

self.log_message("没有图片可供预览。请先选择图片文件夹。", log_widget, tags="warning")

return


self.log_message("【插入位置预览】", log_widget, tags="title")

for name, path in image_map.items():

self.log_message(

f"{name} -> {os.path.basename(path)}",

log_widget,

tags="preview"

)


def log_message(self, message, log_widget, append=True, tags=None, clear=False):

"""记录日志消息"""

log_widget.config(state="normal")

if clear:

log_widget.delete(1.0, tk.END)

if not append:

log_widget.delete(1.0, tk.END)

log_widget.insert(tk.END, message + "\n", tags)

log_widget.see(tk.END)

log_widget.config(state="disabled")


def on_tab_changed(self, event):

"""标签页切换事件处理"""

self.show_help_guide()



if __name__ == "__main__":

root = tk.Tk()

app = ExcelImageMatcherPro(root)

root.mainloop()

```

## 总结

我们搞编程的都清楚,在数据处理这块,效率就是王道。Excel 大家都用得多,但它原生的图片插入功能,碰到大量图片时就拉胯了。一张一张插,重复操作累死人不说,格式调整也麻烦,还特别占性能,处理起来真糟心。

CodeBuddy 生成的这工具就不一样了,双模式批量处理的设计挺巧妙。不管是文件夹批量导入,还是指定图片路径,都能快速把大量图片塞进 Excel 里,而且在格式适配和资源占用控制上也有一手,还没什么操作门槛。

从我们的专业视角看,它给了咱新思路,怎么用简洁的代码实现高效功能,降低用户使用难度。真心希望 CodeBuddy 能不断优化,也盼着以后能有更多这种实用工具冒出来,让咱搞数据处理轻松些,代码少写点弯路,工作更顺溜!

最近发表
标签列表