import tkinter as tkfrom tkinter import ttkclass ExcelImageMatcherPro: def __init__(self, root): self.root = root self.root.title("Excel批量添加图片批注(欢迎关注微信公众号:码海听潮)") self.root.geometry("900x650") self.root.resizable(False, False) # 设置中文字体 self.font_family = ("SimHei", "WenQuanYi Micro Hei", "Heiti TC") # 创建UI self.create_widgets() def create_widgets(self): # 主框架 - 使用grid作为根布局 main_frame = tk.Frame(self.root, bg="#f0f0f0") main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) # 配置grid行列权重 main_frame.grid_rowconfigure(1, weight=1) # 中间行可扩展 main_frame.grid_columnconfigure(0, weight=1) # ==================== 顶部控制区域 ==================== top_frame = tk.Frame(main_frame, bg="#f0f0f0", height=210) top_frame.grid(row=0, column=0, sticky="ew", pady=(0, 10)) top_frame.grid_propagate(False) # 第一行控制 - 图片文件夹选择 folder_frame = ttk.LabelFrame(top_frame, text=" 图片文件夹 ", padding=(5, 5, 5, 5)) folder_frame.pack(fill=tk.X, pady=(5, 0)) folder_label = tk.Label(folder_frame, text="图片文件夹:", bg="#f0f0f0", font=(self.font_family[0], 11)) folder_label.pack(side=tk.LEFT, padx=(0, 5)) self.folder_path_var = tk.StringVar() folder_entry = tk.Entry(folder_frame, textvariable=self.folder_path_var, font=(self.font_family[0], 10), width=40) folder_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 5)) browse_folder_btn = tk.Button(folder_frame, text="浏览", command=self.select_folder, font=(self.font_family[0], 10), cursor="hand2", padx=10) browse_folder_btn.pack(side=tk.LEFT) load_btn = tk.Button(folder_frame, text="加载图片", command=self.load_images_from_entry, font=(self.font_family[0], 10), bg="#4CAF50", fg="white", cursor="hand2", padx=10) load_btn.pack(side=tk.RIGHT, padx=(5, 0)) # 第二行控制 - 模式选择和参数设置(紧凑布局) control_row2 = ttk.LabelFrame(top_frame, text=" 匹配设置 ", padding=(5, 3, 5, 3)) control_row2.pack(fill=tk.X, pady=(5, 0)) settings_container = tk.Frame(control_row2, bg="#f0f0f0") settings_container.pack(fill=tk.X, padx=3, pady=3) # 模式选择 mode_frame = tk.Frame(settings_container, bg="#f0f0f0") mode_frame.pack(side=tk.LEFT, padx=(0, 8)) mode_label = tk.Label(mode_frame, text="匹配模式:", font=(self.font_family[0], 11)) mode_label.pack(side=tk.LEFT, padx=(0, 5)) self.mode_var = tk.StringVar(value="列匹配模式") mode_combo = ttk.Combobox(mode_frame, textvariable=self.mode_var, values=["列匹配模式", "行匹配模式"], width=12, state="readonly") mode_combo.pack(side=tk.LEFT) mode_combo.bind("<<ComboboxSelected>>", self.on_mode_change) # 参数区域 - 紧凑排列 params_frame = tk.Frame(settings_container, bg="#f0f0f0") params_frame.pack(side=tk.LEFT, fill=tk.X, expand=True) # ========== 列模式参数 ========== self.col_param_frame = tk.Frame(params_frame, bg="#f0f0f0") # 匹配列 col_match_frame = tk.Frame(self.col_param_frame, bg="#f0f0f0") col_match_frame.pack(side=tk.LEFT, padx=2) ttk.Label(col_match_frame, text="匹配列:", font=(self.font_family[0], 10))\ .pack(side=tk.LEFT, padx=0, pady=0) self.col_match_entry = ttk.Entry(col_match_frame, width=4) self.col_match_entry.pack(side=tk.LEFT, padx=1) self.col_match_entry.insert(0, "A") # 插入列 col_insert_frame = tk.Frame(self.col_param_frame, bg="#f0f0f0") col_insert_frame.pack(side=tk.LEFT, padx=2) ttk.Label(col_insert_frame, text="插入列:", font=(self.font_family[0], 10))\ .pack(side=tk.LEFT, padx=0, pady=0) self.col_insert_entry = ttk.Entry(col_insert_frame, width=4) self.col_insert_entry.pack(side=tk.LEFT, padx=1) self.col_insert_entry.insert(0, "B") # 边距 col_margin_frame = tk.Frame(self.col_param_frame, bg="#f0f0f0") col_margin_frame.pack(side=tk.LEFT, padx=2) ttk.Label(col_margin_frame, text="边距:", font=(self.font_family[0], 10))\ .pack(side=tk.LEFT, padx=0, pady=0) self.col_margin_entry = ttk.Entry(col_margin_frame, width=4) self.col_margin_entry.pack(side=tk.LEFT, padx=1) self.col_margin_entry.insert(0, "2") # 批注大小设置 comment_size_frame = tk.Frame(self.col_param_frame, bg="#f0f0f0") comment_size_frame.pack(side=tk.LEFT, padx=5) ttk.Label(comment_size_frame, text="批注宽:", font=(self.font_family[0], 10))\ .pack(side=tk.LEFT, padx=0, pady=0) self.comment_width_entry = ttk.Entry(comment_size_frame, width=4) self.comment_width_entry.pack(side=tk.LEFT, padx=1) self.comment_width_entry.insert(0, "200") ttk.Label(comment_size_frame, text="高:", font=(self.font_family[0], 10))\ .pack(side=tk.LEFT, padx=0, pady=0) self.comment_height_entry = ttk.Entry(comment_size_frame, width=4) self.comment_height_entry.pack(side=tk.LEFT, padx=1) self.comment_height_entry.insert(0, "150") # ========== 行模式参数 ========== self.row_param_frame = tk.Frame(params_frame, bg="#f0f0f0") # 匹配行 row_match_frame = tk.Frame(self.row_param_frame, bg="#f0f0f0") row_match_frame.pack(side=tk.LEFT, padx=2) ttk.Label(row_match_frame, text="匹配行:", font=(self.font_family[0], 10))\ .pack(side=tk.LEFT, padx=0, pady=0) self.row_match_entry = ttk.Entry(row_match_frame, width=4) self.row_match_entry.pack(side=tk.LEFT, padx=1) self.row_match_entry.insert(0, "1") # 插入行 row_insert_frame = tk.Frame(self.row_param_frame, bg="#f0f0f0") row_insert_frame.pack(side=tk.LEFT, padx=2) ttk.Label(row_insert_frame, text="插入行:", font=(self.font_family[0], 10))\ .pack(side=tk.LEFT, padx=0, pady=0) self.row_insert_entry = ttk.Entry(row_insert_frame, width=4) self.row_insert_entry.pack(side=tk.LEFT, padx=1) self.row_insert_entry.insert(0, "2") # 边距 row_margin_frame = tk.Frame(self.row_param_frame, bg="#f0f0f0") row_margin_frame.pack(side=tk.LEFT, padx=2) ttk.Label(row_margin_frame, text="边距:", font=(self.font_family[0], 10))\ .pack(side=tk.LEFT, padx=0, pady=0) self.row_margin_entry = ttk.Entry(row_margin_frame, width=4) self.row_margin_entry.pack(side=tk.LEFT, padx=1) self.row_margin_entry.insert(0, "2") # 批注大小设置(行模式也添加) row_comment_size_frame = tk.Frame(self.row_param_frame, bg="#f0f0f0") row_comment_size_frame.pack(side=tk.LEFT, padx=5) ttk.Label(row_comment_size_frame, text="批注宽:", font=(self.font_family[0], 10))\ .pack(side=tk.LEFT, padx=0, pady=0) self.row_comment_width_entry = ttk.Entry(row_comment_size_frame, width=4) self.row_comment_width_entry.pack(side=tk.LEFT, padx=1) self.row_comment_width_entry.insert(0, "200") ttk.Label(row_comment_size_frame, text="高:", font=(self.font_family[0], 10))\ .pack(side=tk.LEFT, padx=0, pady=0) self.row_comment_height_entry = ttk.Entry(row_comment_size_frame, width=4) self.row_comment_height_entry.pack(side=tk.LEFT, padx=1) self.row_comment_height_entry.insert(0, "150") # 默认显示列模式参数 self.col_param_frame.pack(side=tk.LEFT, fill=tk.X, expand=True) self.row_param_frame.pack_forget() # 第三行控制 - 执行按钮 control_row3 = ttk.LabelFrame(top_frame, text=" 执行 ", padding=(5, 5, 5, 5)) control_row3.pack(fill=tk.X, pady=(5, 0)) self.execute_btn = tk.Button(control_row3, text="插入图片批注", command=self.run_matching, font=(self.font_family[0], 10), bg="#2196F3", fg="white", cursor="hand2", padx=10, pady=8) self.execute_btn.pack(side=tk.TOP, padx=(10, 0)) # ==================== 中间内容区域 ==================== middle_frame = tk.Frame(main_frame, bg="#ffffff", bd=1, relief=tk.SUNKEN) middle_frame.grid(row=1, column=0, sticky="nsew", pady=(0, 10)) # 左侧图片预览区域 preview_frame = tk.Frame(middle_frame, bg="#ffffff") preview_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5, pady=5) self.preview_label = tk.Label(preview_frame, text="图片预览", bg="#e0e0e0", font=(self.font_family[0], 12)) self.preview_label.pack(fill=tk.X, pady=(0, 5)) self.image_container = tk.Frame(preview_frame, bg="#f5f5f5") self.image_container.pack(fill=tk.BOTH, expand=True) self.image_label = tk.Label(self.image_container, bg="#f5f5f5") self.image_label.pack(expand=True) # 右侧图片列表区域 list_frame = tk.Frame(middle_frame, bg="#ffffff", width=250) list_frame.pack(side=tk.RIGHT, fill=tk.BOTH, pady=5) list_frame.pack_propagate(False) list_header = tk.Frame(list_frame, bg="#e0e0e0", height=25) list_header.pack(fill=tk.X, pady=(0, 5)) list_header.pack_propagate(False) list_label = tk.Label(list_header, text="图片列表", bg="#e0e0e0", font=(self.font_family[0], 12)) list_label.pack(side=tk.LEFT, padx=(80, 0)) # 全选复选框 self.select_all_var = tk.BooleanVar(value=False) select_all_check = tk.Checkbutton(list_header, text="全选", variable=self.select_all_var, font=(self.font_family[0], 10), bg="#e0e0e0", cursor="hand2", command=self.toggle_select_all) select_all_check.pack(side=tk.RIGHT, padx=(0, 5)) # 创建Canvas和Scrollbar self.canvas = tk.Canvas(list_frame, bg="#ffffff", highlightthickness=0) scrollbar = tk.Scrollbar(list_frame, orient=tk.VERTICAL, command=self.canvas.yview) self.canvas.configure(yscrollcommand=scrollbar.set) scrollbar.pack(side=tk.RIGHT, fill=tk.Y) self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) # 创建Canvas中的Frame用于放置图片项 self.images_frame = tk.Frame(self.canvas, bg="#ffffff") self.canvas_frame = self.canvas.create_window((0, 0), window=self.images_frame, anchor="nw") # 绑定Canvas的大小变化事件 self.images_frame.bind("<Configure>", self.on_frame_configure) self.canvas.bind("<Configure>", self.on_canvas_configure) # ==================== 底部状态区域 ==================== bottom_frame = tk.Frame(main_frame, bg="#f0f0f0", height=60) bottom_frame.grid(row=2, column=0, sticky="ew") bottom_frame.grid_propagate(False) # 进度条 self.progress_var = tk.DoubleVar() progress_bar = ttk.Progressbar(bottom_frame, variable=self.progress_var, length=100, mode='determinate') progress_bar.pack(fill=tk.X, padx=10, pady=(10, 5)) # 状态标签 self.status_var = tk.StringVar(value="就绪") status_label = tk.Label(bottom_frame, textvariable=self.status_var, bg="#f0f0f0", font=(self.font_family[0], 10)) status_label.pack(pady=(0, 5)) def on_frame_configure(self, event): """当Frame大小变化时,更新Canvas的滚动区域""" self.canvas.configure(scrollregion=self.canvas.bbox("all")) def on_canvas_configure(self, event): """当Canvas大小变化时,调整内部Frame的宽度""" self.canvas.itemconfig(self.canvas_frame, width=event.width) def select_folder(self): """选择图片文件夹""" pass def load_images_from_entry(self): """从输入框加载图片""" pass def on_mode_change(self, event=None): """模式切换事件""" selected_mode = self.mode_var.get() if selected_mode == "列匹配模式": self.row_param_frame.pack_forget() self.col_param_frame.pack(side=tk.LEFT, fill=tk.X, expand=True) else: self.col_param_frame.pack_forget() self.row_param_frame.pack(side=tk.LEFT, fill=tk.X, expand=True) def toggle_select_all(self): """全选/取消全选""" pass def run_matching(self): """执行匹配插入""" passif __name__ == "__main__": root = tk.Tk() app = ExcelImageMatcherPro(root) root.mainloop()