Image Update Files

The component to package mapping file, .cpm.csv, defines the relationship between components and packages. The .cpm.csv file maps the component tags and files to packages. The .cpm.csv file is identical to the .csv files used in the multi-XIP build system.

Component to package mapping files specify in which package a module, file, or registry key belongs to.

The .cpm.csv file is also one of the inputs used by the Manifest Build Tool to generate the build side manifest, .bsm.xml, file. For more information, see Package Generation Process.

The following three files are merged:

  • Microsoft modules are mapped by the /Public/%_TGTPROJ%/Oak/Files/%_TGTPROJ%.cpm.csv file
  • OEM modules are mapped by the Oem.cpm.csv file, which is located in one or more of the following directories: /Platform/%_TGTPLAT%/Files/Cebase, /Platform/%_TGTPLAT%/Files/Smartfon, and /Platform/%_TGTPLAT%/Files/Wpc.
  • /Public/Common/Oak/Files/Metadata.cpm.csv file

//这里是说wm会把上面三个.cp.csv文件merge成Cexipmodl.cpm.csv 因此如果我们在bsp中定义像

GWES.exe 在public下已经定义过的 makimg时候就会被提示重复定义。还有 buildpkg是不会重新进行merge的。

The final output file is Cexipmodl.cpm.csv merged.

You can define the .cpm.csv file using any one of the following syntax:

  • Explicit file mapping
    <File name>,<Package name>

    Example: BubbleBreaker.exe,Games

    Use explicit mappings sparingly because this cannot be used to map registry entries.

  • Component-based mapping
    <CE_MODULES_X>,<Package name>

    Example: APPS_MODULES_BUBBLEBREAKER,Games

    This mapping is based on the closest surrounding CESYSGEN tag. This type of mapping works for registry entries and .bib files.

    The optional <BOOTHIVE> modifier forces all registry keys into Boot.hv. This replaces the <DEFAULTREG> option from the Multi-XIP cpm.csv files.

  • XIPREGION tags

    XIPREGION tags behave the same way as CESYSGEN tags. You need to surround the group of .bib or .reg file entries that you wish to map with ";@XIPREGION IF XXX" and ";@XIPREGION ENDIF XXX". You can replace XXX with any name you want, there is no strict naming convention.

    Project.bib, Project.reg, and sometimes Platform.bib do not get CEFiltered so you cannot use CESYSGEN tags. In this case, use XIPREGION tags instead.

In the Wpc.cpm.csv file, CE_MODULES_RAMFMD should ALWAYS be set to IGNORE. There should be no IMG flag dependencies in the base files. If OEMs want to include CE_MODULES_RAMFMD in their device, OEMs should add the line CE_MODULES_RAMFMD,OEMDRIVERS to the Oem.cpm.csv file.

import os import tkinter as tk from tkinter import filedialog, messagebox, ttk from PIL import Image, ImageTk import threading import xlwings as xw import pythoncom import subprocess class WPSImageInserter: def __init__(self, root): self.root = root self.root.title("WPS表格自由图片插入工具") self.root.geometry("900x1000") self.root.resizable(True, True) # 变量初始化 self.excel_file = tk.StringVar() self.image_folder = tk.StringVar() self.sheet_name_var = tk.StringVar(value="") self.start_cell_var = tk.StringVar(value="B19") # 起始单元格 self.end_cell_var = tk.StringVar(value="E22") # 结束单元格 self.status_var = tk.StringVar(value="准备就绪") self.current_preview_path = None # 当前预览图片的完整路径 self.processing = False self.all_image_files = [] # 所有图片文件 self.selected_image_files = [] # 选中的图片文件 self.sheet_names = [] # 存储工作表名称 self.preview_image = None # 预览图片对象 self.inserted_images = [] # 存储已插入的图片 # 设置默认选项(硬编码) self.keep_aspect = True # 默认保持宽高比 self.backup = True # 默认创建备份 self.create_widgets() def create_widgets(self): # 主框架 main_frame = ttk.Frame(self.root, padding="15") main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) # 配置行列权重 self.root.columnconfigure(0, weight=1) self.root.rowconfigure(0, weight=1) main_frame.columnconfigure(1, weight=1) main_frame.rowconfigure(9, weight=1) # 图片选择区域行 # 文件选择区域 ttk.Label(main_frame, text="WPS表格文件:", font=("Arial", 11)).grid( row=1, column=0, sticky=tk.W, pady=5) excel_frame = ttk.Frame(main_frame) excel_frame.grid(row=1, column=1, columnspan=3, sticky=(tk.W, tk.E), pady=5) excel_frame.columnconfigure(0, weight=1) excel_entry = ttk.Entry(excel_frame, textvariable=self.excel_file) excel_entry.grid(row=0, column=0, sticky=(tk.W, tk.E), padx=(0, 10)) ttk.Button(excel_frame, text="选择文件", command=self.browse_excel).grid(row=0, column=1) ttk.Label(main_frame, text="图片文件夹:", font=("Arial", 11)).grid( row=2, column=0, sticky=tk.W, pady=5) image_frame = ttk.Frame(main_frame) image_frame.grid(row=2, column=1, columnspan=3, sticky=(tk.W, tk.E), pady=5) image_frame.columnconfigure(0, weight=1) image_entry = ttk.Entry(image_frame, textvariable=self.image_folder) image_entry.grid(row=0, column=0, sticky=(tk.W, tk.E), padx=(0, 10)) ttk.Button(image_frame, text="选择文件夹", command=self.browse_image_folder).grid(row=0, column=1) # 工作表选择 ttk.Label(main_frame, text="选择工作表:", font=("Arial", 11)).grid( row=3, column=0, sticky=tk.W, pady=5) sheet_frame = ttk.Frame(main_frame) sheet_frame.grid(row=3, column=1, columnspan=3, sticky=(tk.W, tk.E), pady=5) sheet_frame.columnconfigure(0, weight=1) self.sheet_combo = ttk.Combobox(sheet_frame, textvariable=self.sheet_name_var, state="readonly") self.sheet_combo.grid(row=0, column=0, sticky=(tk.W, tk.E), padx=(0, 10)) ttk.Button(sheet_frame, text="刷新工作表", command=self.refresh_sheets).grid(row=0, column=1) # 插入区域设置 ttk.Label(main_frame, text="插入区域设置:", font=("Arial", 12, "bold")).grid( row=4, column=0, sticky=tk.W, columnspan=4, pady=(10, 5)) # 起始单元格 ttk.Label(main_frame, text="起始单元格:").grid(row=5, column=0, sticky=tk.W, pady=5) start_cell_entry = ttk.Entry(main_frame, textvariable=self.start_cell_var, width=10) start_cell_entry.grid(row=5, column=1, sticky=tk.W, pady=5) ttk.Label(main_frame).grid(row=1, column=1, sticky=tk.W, padx=(80, 0), pady=5) # 结束单元格 ttk.Label(main_frame, text="结束单元格:").grid(row=5, column=2, sticky=tk.W, pady=5) end_cell_entry = ttk.Entry(main_frame, textvariable=self.end_cell_var, width=10) end_cell_entry.grid(row=5, column=3, sticky=tk.W, pady=5) ttk.Label(main_frame).grid(row=2, column=3, sticky=tk.W, padx=(80, 0), pady=5) # 区域信息显示 self.area_info_var = tk.StringVar(value="区域信息将在这里显示") ttk.Label(main_frame, textvariable=self.area_info_var, font=("Arial", 10), foreground="blue").grid(row=6, column=0, columnspan=4, sticky=tk.W, pady=5) # 图片选择区域 ttk.Label(main_frame, text="图片选择 (双击添加或预览):", font=("Arial", 12, "bold")).grid( row=7, column=0, sticky=tk.W, columnspan=4, pady=(10, 5)) # 创建左右分栏 selection_frame = ttk.Frame(main_frame) selection_frame.grid(row=8, column=0, columnspan=4, sticky=(tk.W, tk.E, tk.N, tk.S), pady=5) selection_frame.columnconfigure(0, weight=1) selection_frame.columnconfigure(1, weight=1) selection_frame.rowconfigure(0, weight=1) # 左侧:所有图片列表 - 增加数量显示 self.all_images_label = ttk.LabelFrame(selection_frame, text="所有图片 (0)") self.all_images_label.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), padx=(0, 10)) self.all_images_label.columnconfigure(0, weight=1) self.all_images_label.rowconfigure(0, weight=1) self.all_images_listbox = tk.Listbox(self.all_images_label, selectmode=tk.SINGLE, font=("Consolas", 9), height=20) # 增加高度 scrollbar_left = ttk.Scrollbar(self.all_images_label, orient="vertical", command=self.all_images_listbox.yview) self.all_images_listbox.configure(yscrollcommand=scrollbar_left.set) self.all_images_listbox.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) scrollbar_left.grid(row=0, column=1, sticky=(tk.N, tk.S)) # 右侧:选中图片列表 - 增加数量显示 self.selected_images_label = ttk.LabelFrame(selection_frame, text="已选图片 (0)") self.selected_images_label.grid(row=0, column=1, sticky=(tk.W, tk.E, tk.N, tk.S), padx=(10, 0)) self.selected_images_label.columnconfigure(0, weight=1) self.selected_images_label.rowconfigure(0, weight=1) self.selected_images_listbox = tk.Listbox(self.selected_images_label, font=("Consolas", 9), height=20) # 增加高度 scrollbar_right = ttk.Scrollbar(self.selected_images_label, orient="vertical", command=self.selected_images_listbox.yview) self.selected_images_listbox.configure(yscrollcommand=scrollbar_right.set) self.selected_images_listbox.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) scrollbar_right.grid(row=0, column=1, sticky=(tk.N, tk.S)) # 选择按钮 button_frame = ttk.Frame(selection_frame) button_frame.grid(row=1, column=0, columnspan=2, pady=10) ttk.Button(button_frame, text="全选", command=self.select_all).pack(side=tk.LEFT, padx=5) ttk.Button(button_frame, text="添加", command=self.add_selected).pack(side=tk.LEFT, padx=5) ttk.Button(button_frame, text="清除选择", command=self.clear_selection).pack(side=tk.LEFT, padx=5) ttk.Button(button_frame, text="上移", command=self.move_up).pack(side=tk.LEFT, padx=5) ttk.Button(button_frame, text="下移", command=self.move_down).pack(side=tk.LEFT, padx=5) ttk.Button(button_frame, text="移除选中", command=self.remove_selected).pack(side=tk.LEFT, padx=5) # 图片预览区域 ttk.Label(main_frame, text="图片预览:", font=("Arial", 12, "bold")).grid( row=9, column=0, sticky=tk.W, columnspan=4, pady=(10, 5)) preview_frame = ttk.Frame(main_frame) preview_frame.grid(row=10, column=0, columnspan=4, sticky=(tk.W, tk.E), pady=6) # 预览画布 self.preview_canvas = tk.Canvas(preview_frame, width=300, height=150, bg="#f0f0f0", highlightthickness=1, highlightbackground="#cccccc") self.preview_canvas.pack(side=tk.LEFT, padx=(0, 20)) self.preview_canvas.create_text(150, 75, text="双击图片预览", fill="#666666") # 添加双击事件绑定 self.preview_canvas.bind('<Double-Button-1>', self.open_original_image) # 图片信息显示 self.preview_info = tk.Text(preview_frame, height=8, width=20, wrap=tk.WORD, font=("Consolas", 9), bg="#f8f8f8") scrollbar_preview = ttk.Scrollbar(preview_frame, orient="vertical", command=self.preview_info.yview) self.preview_info.configure(yscrollcommand=scrollbar_preview.set) self.preview_info.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) scrollbar_preview.pack(side=tk.RIGHT, fill=tk.Y) self.preview_info.insert("1.0", "双击列表中的图片可在此预览\n图片信息将显示在这里\n\n提示: 双击预览图片可打开原图") self.preview_info.configure(state="disabled") # 绑定列表事件 self.all_images_listbox.bind('<Double-Button-1>', self.preview_and_select_image) self.selected_images_listbox.bind('<Double-Button-1>', self.preview_selected_image) # 进度和控制区域 control_frame = ttk.Frame(main_frame) control_frame.grid(row=12, column=0, columnspan=4, sticky=(tk.W, tk.E), pady=10) self.progress = ttk.Progressbar(control_frame, orient="horizontal", mode="determinate") self.progress.pack(fill=tk.X, pady=5) status_label = ttk.Label(control_frame, textvariable=self.status_var) status_label.pack(pady=5) action_frame = ttk.Frame(control_frame) action_frame.pack(pady=15) self.start_button = ttk.Button(action_frame, text="开始插入图片", command=self.start_insertion) self.start_button.pack(side=tk.LEFT, padx=10) ttk.Button(action_frame, text="刷新图片列表", command=self.refresh_image_list).pack(side=tk.LEFT, padx=10) ttk.Button(action_frame, text="打开文件位置", command=self.open_file_location).pack(side=tk.LEFT, padx=10) ttk.Button(action_frame, text="退出", command=self.root.quit).pack(side=tk.LEFT, padx=10) # 配置权重 main_frame.rowconfigure(8, weight=1) # 图片选择区域行 def update_count_labels(self): """更新所有图片和已选图片的数量显示""" self.all_images_label.config(text=f"所有图片 ({len(self.all_image_files)})") self.selected_images_label.config(text=f"已选图片 ({len(self.selected_image_files)})") def browse_excel(self): filetypes = [ ("所有文件", "*.*") ] filename = filedialog.askopenfilename(title="选择WPS表格文件", filetypes=filetypes) if filename: self.excel_file.set(filename) self.status_var.set("表格文件已选择") self.refresh_sheets() def refresh_sheets(self): excel_file = self.excel_file.get() if excel_file and os.path.exists(excel_file): try: # 使用xlwings获取工作表名称 app = xw.App(visible=False) wb = app.books.open(excel_file) self.sheet_names = [sheet.name for sheet in wb.sheets] wb.close() app.quit() self.sheet_combo['values'] = self.sheet_names if self.sheet_names: self.sheet_name_var.set(self.sheet_names[0]) self.status_var.set(f"找到 {len(self.sheet_names)} 个工作表") else: self.status_var.set("未找到工作表") except Exception as e: messagebox.showerror("错误", f"读取工作表失败: {str(e)}") def browse_image_folder(self): folder = filedialog.askdirectory(title="选择图片文件夹") if folder: self.image_folder.set(folder) self.status_var.set("图片文件夹已选择") self.refresh_image_list() def refresh_image_list(self): folder = self.image_folder.get() if folder and os.path.exists(folder): supported_formats = ('.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.webp') try: all_files = os.listdir(folder) self.all_image_files = [f for f in all_files if f.lower().endswith(supported_formats)] # 更新所有图片列表 self.all_images_listbox.delete(0, tk.END) for filename in self.all_image_files: self.all_images_listbox.insert(tk.END, filename) # 更新颜色和数量显示 self.update_all_listbox_colors() self.update_count_labels() self.update_area_info() # 自动选择并预览第一张图片 self.auto_preview_first_image() except Exception as e: messagebox.showerror("错误", f"读取文件夹错误: {e}") def auto_preview_first_image(self): """自动预览第一张可用图片""" if self.all_image_files: # 选择第一张图片 self.all_images_listbox.selection_clear(0, tk.END) self.all_images_listbox.selection_set(0) self.all_images_listbox.see(0) # 预览第一张图片 first_image_path = os.path.join(self.image_folder.get(), self.all_image_files[0]) self.update_preview(first_image_path) self.show_preview_info(first_image_path) else: # 如果没有图片,清空预览 self.clear_preview() def clear_preview(self): """清空预览区域""" self.preview_canvas.delete("all") self.preview_canvas.create_text(150, 75, text="双击图片预览", fill="#666666") self.current_preview_path = None self.preview_info.configure(state="normal") self.preview_info.delete("1.0", "end") self.preview_info.insert("1.0", "双击列表中的图片可在此预览\n图片信息将显示在这里\n\n提示: 双击预览图片可打开原图") self.preview_info.configure(state="disabled") def update_all_listbox_colors(self): """更新所有图片列表的颜色,已选的置灰""" for i, filename in enumerate(self.all_image_files): if filename in self.selected_image_files: self.all_images_listbox.itemconfig(i, {'fg': 'gray'}) else: self.all_images_listbox.itemconfig(i, {'fg': 'black'}) def update_area_info(self): """更新区域信息""" start_cell = self.start_cell_var.get().strip() end_cell = self.end_cell_var.get().strip() if start_cell and end_cell: try: start_row, start_col = self.parse_cell_reference(start_cell) end_row, end_col = self.parse_cell_reference(end_cell) if start_row and end_row: rows = end_row - start_row + 1 cols = end_col - start_col + 1 total_cells = rows * cols info_text = f"区域: {start_cell}:{end_cell} | 行数: {rows} | 列数: {cols} | 总单元格: {total_cells}" self.area_info_var.set(info_text) return except: pass self.area_info_var.set("请输入有效的单元格区域") def add_selected(self): """添加选中的图片到已选图片列表,并选中下一张""" selection = self.all_images_listbox.curselection() if selection: index = selection[0] filename = self.all_image_files[index] # 添加到已选列表(如果尚未选中) if filename not in self.selected_image_files: self.selected_image_files.append(filename) self.selected_images_listbox.insert(tk.END, filename) # 更新列表颜色和数量显示 self.update_all_listbox_colors() self.update_count_labels() # 选中下一张图片(如果有) next_index = index + 1 if next_index < len(self.all_image_files): self.all_images_listbox.selection_clear(0, tk.END) self.all_images_listbox.selection_set(next_index) self.all_images_listbox.see(next_index) # 显示预览 image_path = os.path.join(self.image_folder.get(), self.all_image_files[next_index]) self.update_preview(image_path) self.show_preview_info(image_path) else: # 如果是最后一张,保持当前预览 image_path = os.path.join(self.image_folder.get(), filename) self.update_preview(image_path) self.show_preview_info(image_path) def preview_and_select_image(self, event): """双击预览并选中图片""" selection = self.all_images_listbox.curselection() if selection: index = selection[0] filename = self.all_image_files[index] # 添加到已选列表(如果尚未选中) if filename not in self.selected_image_files: self.selected_image_files.append(filename) self.selected_images_listbox.insert(tk.END, filename) # 更新列表颜色和数量显示 self.update_all_listbox_colors() self.update_count_labels() # 显示预览 image_path = os.path.join(self.image_folder.get(), filename) self.update_preview(image_path) self.show_preview_info(image_path) def preview_selected_image(self, event): """双击已选图片列表预览""" selection = self.selected_images_listbox.curselection() if selection: index = selection[0] filename = self.selected_image_files[index] # 显示预览 image_path = os.path.join(self.image_folder.get(), filename) self.update_preview(image_path) self.show_preview_info(image_path) def update_preview(self, image_path): """更新图片预览""" try: img = Image.open(image_path) # 保持宽高比进行缩放 img.thumbnail((280, 130), Image.LANCZOS) # 稍微增大预览尺寸 self.preview_image = ImageTk.PhotoImage(img) self.preview_canvas.delete("all") self.preview_canvas.create_image(150, 75, image=self.preview_image) # 更新中心点 # 保存当前预览图片路径 self.current_preview_path = image_path except Exception as e: self.preview_canvas.delete("all") self.preview_canvas.create_text(150, 75, text="预览失败", fill="red") print(f"预览错误: {e}") self.current_preview_path = None def show_preview_info(self, image_path): """显示图片详细信息""" try: img = Image.open(image_path) filename = os.path.basename(image_path) file_size = os.path.getsize(image_path) / 1024 # KB file_size_str = f"{file_size:.1f} KB" if file_size < 1024 else f"{file_size / 1024:.1f} MB" info_text = f"文件名: {filename}\n" info_text += f"尺寸: {img.width} × {img.height} 像素\n" info_text += f"格式: {img.format}\n" info_text += f"大小: {file_size_str}\n" info_text += f"模式: {img.mode}\n" self.preview_info.configure(state="normal") self.preview_info.delete("1.0", "end") self.preview_info.insert("1.0", info_text) self.preview_info.configure(state="disabled") except Exception as e: self.preview_info.configure(state="normal") self.preview_info.delete("1.0", "end") self.preview_info.insert("1.0", f"无法读取图片信息: {str(e)}") self.preview_info.configure(state="disabled") def open_original_image(self, event=None): """使用系统默认程序打开原图""" if not self.current_preview_path or not os.path.exists(self.current_preview_path): messagebox.showwarning("警告", "没有可用的图片预览或图片不存在") return try: # 根据操作系统使用不同的方式打开图片 if os.name == 'nt': # Windows os.startfile(self.current_preview_path) elif os.name == 'posix': # macOS or Linux if os.uname().sysname == 'Darwin': # macOS subprocess.call(('open', self.current_preview_path)) else: # Linux subprocess.call(('xdg-open', self.current_preview_path)) self.status_var.set(f"已打开原图: {os.path.basename(self.current_preview_path)}") except Exception as e: messagebox.showerror("错误", f"无法打开图片: {str(e)}") self.status_var.set("打开图片失败") def select_all(self): """全选所有图片""" self.selected_image_files = self.all_image_files.copy() self.selected_images_listbox.delete(0, tk.END) for filename in self.selected_image_files: self.selected_images_listbox.insert(tk.END, filename) # 更新列表颜色和数量显示 self.update_all_listbox_colors() self.update_count_labels() # 如果所有图片列表中有图片,预览第一张 if self.all_image_files: image_path = os.path.join(self.image_folder.get(), self.all_image_files[0]) self.update_preview(image_path) self.show_preview_info(image_path) def clear_selection(self): """清除所有选择""" self.selected_image_files = [] self.selected_images_listbox.delete(0, tk.END) # 更新列表颜色和数量显示 self.update_all_listbox_colors() self.update_count_labels() # 如果所有图片列表中有图片,预览第一张 if self.all_image_files: image_path = os.path.join(self.image_folder.get(), self.all_image_files[0]) self.update_preview(image_path) self.show_preview_info(image_path) else: self.clear_preview() def move_up(self): """上移选中图片""" selection = self.selected_images_listbox.curselection() if selection: index = selection[0] if index > 0: # 交换位置 self.selected_image_files[index], self.selected_image_files[index - 1] = \ self.selected_image_files[index - 1], self.selected_image_files[index] # 更新列表显示 self.update_selected_listbox() self.selected_images_listbox.select_set(index - 1) def move_down(self): """下移选中图片""" selection = self.selected_images_listbox.curselection() if selection: index = selection[0] if index < len(self.selected_image_files) - 1: # 交换位置 self.selected_image_files[index], self.selected_image_files[index + 1] = \ self.selected_image_files[index + 1], self.selected_image_files[index] # 更新列表显示 self.update_selected_listbox() self.selected_images_listbox.select_set(index + 1) def remove_selected(self): """移除选中的图片""" selection = self.selected_images_listbox.curselection() if selection: # 从后往前删除,避免索引变化 for index in sorted(selection, reverse=True): if 0 <= index < len(self.selected_image_files): del self.selected_image_files[index] self.update_selected_listbox() # 更新列表颜色和数量显示 self.update_all_listbox_colors() self.update_count_labels() # 自动预览第一张可用图片 self.auto_preview_first_image() def update_selected_listbox(self): """更新已选图片列表显示""" self.selected_images_listbox.delete(0, tk.END) for filename in self.selected_image_files: self.selected_images_listbox.insert(tk.END, filename) def parse_cell_reference(self, cell_ref): """解析单元格引用,返回行和列号""" import re match = re.match(r'^([A-Za-z]+)(\d+)$', cell_ref) if not match: return None, None col_letter = match.group(1).upper() row_number = int(match.group(2)) # 将列字母转换为列号 col_number = 0 for char in col_letter: col_number = col_number * 26 + (ord(char) - ord('A') + 1) return row_number, col_number def column_number_to_letter(self, n): """将列号转换为列字母""" result = "" while n > 0: n, remainder = divmod(n - 1, 26) result = chr(65 + remainder) + result return result def cm_to_points(self, cm): """将厘米转换为点""" return cm * 28.3465 def remove_inserted_images(self): """移除已插入的图片""" # 从所有图片列表中移除已插入的图片 for img in self.inserted_images: if img in self.all_image_files: self.all_image_files.remove(img) # 从已选图片列表中移除已插入的图片 for img in self.inserted_images: if img in self.selected_image_files: self.selected_image_files.remove(img) # 更新列表显示 self.all_images_listbox.delete(0, tk.END) for filename in self.all_image_files: self.all_images_listbox.insert(tk.END, filename) self.update_selected_listbox() # 更新颜色和数量显示 self.update_all_listbox_colors() self.update_count_labels() # 自动预览第一张可用图片 self.auto_preview_first_image() # 清空已插入图片列表 self.inserted_images = [] def start_insertion(self): if self.processing: return # 验证输入 excel_file = self.excel_file.get() image_folder = self.image_folder.get() sheet_name = self.sheet_name_var.get() start_cell = self.start_cell_var.get().strip() end_cell = self.end_cell_var.get().strip() if not excel_file or not os.path.exists(excel_file): messagebox.showerror("错误", "请选择有效的WPS表格文件!") return if not image_folder or not os.path.exists(image_folder): messagebox.showerror("错误", "请选择有效的图片文件夹!") return if not sheet_name: messagebox.showerror("错误", "请选择工作表!") return if not start_cell or not end_cell: messagebox.showerror("错误", "请输入起始和结束单元格!") return if not self.selected_image_files: messagebox.showerror("错误", "请选择要插入的图片!") return # 解析单元格区域 start_row, start_col = self.parse_cell_reference(start_cell) end_row, end_col = self.parse_cell_reference(end_cell) if not all([start_row, start_col, end_row, end_col]): messagebox.showerror("错误", "请输入有效的单元格引用!") return if start_row > end_row or start_col > end_col: messagebox.showerror("错误", "起始单元格必须在结束单元格的左上方!") return # 清空已插入图片列表 self.inserted_images = [] self.processing = True self.start_button.config(state="disabled") self.status_var.set("正在插入图片...") self.progress["value"] = 0 # 在新线程中处理 thread = threading.Thread( target=self.insert_images, args=(excel_file, image_folder, sheet_name, start_row, start_col, end_row, end_col) ) thread.daemon = True thread.start() def insert_images(self, excel_file, image_folder, sheet_name, start_row, start_col, end_row, end_col): try: # 创建备份文件 if self.backup: backup_path = excel_file.replace('.xlsx', '_backup.xlsx').replace('.xls', '_backup.xls') import shutil shutil.copy2(excel_file, backup_path) self.root.after(0, lambda: self.update_status(f"已创建备份文件: {os.path.basename(backup_path)}")) # 初始化COM pythoncom.CoInitialize() # 打开Excel文件 self.root.after(0, lambda: self.update_status("正在打开WPS表格...")) app = xw.App(visible=False) wb = app.books.open(excel_file) # 选择指定的工作表 try: ws = wb.sheets[sheet_name] except: self.root.after(0, lambda: self.update_status(f"找不到工作表: {sheet_name}")) ws = wb.sheets[0] self.root.after(0, lambda: self.update_status(f"使用默认工作表: {ws.name}")) # 计算区域大小 rows = end_row - start_row + 1 cols = end_col - start_col + 1 total_cells = rows * cols # 转换厘米为点 width_points = self.cm_to_points(4.31) height_points = self.cm_to_points(7.37) # 插入图片 self.root.after(0, lambda: self.update_status("正在插入图片...")) inserted_count = 0 for i, filename in enumerate(self.selected_image_files): if i >= total_cells: break # 超出区域范围的图片不插入 image_path = os.path.join(image_folder, filename) # 计算目标单元格位置 cell_row = start_row + (i // cols) cell_col = start_col + (i % cols) target_cell = f"{self.column_number_to_letter(cell_col)}{cell_row}" try: # 获取目标单元格位置 cell_range = ws.range(target_cell) left = cell_range.left top = cell_range.top # 插入图片 pic = ws.pictures.add(image_path, left=left, top=top, width=width_points, height=height_points) # 保持宽高比 if self.keep_aspect: pic.api.ShapeRange.LockAspectRatio = 1 inserted_count += 1 # 记录已插入的图片 self.inserted_images.append(filename) self.root.after(0, lambda idx=i, cell=target_cell: self.update_status(f"插入图片 {idx + 1} 到 {cell}")) progress = (i + 1) / min(len(self.selected_image_files), total_cells) * 100 self.root.after(0, lambda p=progress: self.progress.config(value=p)) except Exception as e: self.root.after(0, lambda: self.update_status(f"插入图片到 {target_cell} 失败: {str(e)}")) # 保存文件 self.root.after(0, lambda: self.update_status("正在保存文件...")) wb.save() wb.close() app.quit() pythoncom.CoUninitialize() self.root.after(0, lambda: self.update_status(f"完成!成功插入 {inserted_count} 张图片")) self.root.after(0, self.process_complete, True, inserted_count) except Exception as e: self.root.after(0, lambda: self.update_status(f"处理过程中出错: {str(e)}")) self.root.after(0, self.process_complete, False) try: pythoncom.CoUninitialize() except: pass def update_status(self, message): self.status_var.set(message) def process_complete(self, success, inserted_count=0): self.processing = False self.start_button.config(state="normal") if success: # 询问用户是否要删除已插入的图片 if messagebox.askyesno("完成", f"处理完成!\n成功插入 {inserted_count} 张图片\n" f"图片尺寸: 4.31cm × 7.37cm\n" f"插入区域: {self.start_cell_var.get()} 到 {self.end_cell_var.get()}\n\n" "是否从列表中移除已插入的图片?"): # 移除已插入的图片 self.remove_inserted_images() else: messagebox.showerror("错误", "处理失败,请查看状态信息") def open_file_location(self): excel_file = self.excel_file.get() if excel_file and os.path.exists(excel_file): try: folder_path = os.path.dirname(excel_file) if os.name == 'nt': # Windows os.startfile(folder_path) elif os.name == 'posix': # macOS or Linux if os.uname().sysname == 'Darwin': # macOS os.system(f'open "{folder_path}"') else: # Linux os.system(f'xdg-open "{folder_path}"') except: messagebox.showerror("错误", "无法打开文件夹位置") else: messagebox.showerror("错误", "文件不存在") def main(): root = tk.Tk() app = WPSImageInserter(root) root.mainloop() if __name__ == "__main__": main() 首次插入图片后,手动输入起始单元格,自动生成结束单元格,如输入的是B1结束单元格自动更新为D1,如果输入的是B2结束单元格为D2以此类推,另外区域信息需要在输入后可以自动计算,目前仅会计算首次
最新发布
09-02
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值