"""Excel批量插图-匹配表格内容需要库:pip install xlwings PyQt6"""import osimport reimport webbrowserfrom typing import Dictimport xlwings as xwfrom PyQt6.QtWidgets import ( QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QLineEdit, QTabWidget, QTextEdit, QFileDialog, QMessageBox, QCheckBox)from PyQt6.QtGui import QFont, QColorfrom PyQt6.QtCore import Qtclass ExcelImageMatcherPro(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("Excel批量插图-匹配表格内容") self.resize(600, 700) self.col_image_map: Dict[str, str] = {} self.row_image_map: Dict[str, str] = {} # 创建主窗口 self.central_widget = QWidget() self.setCentralWidget(self.central_widget) # 主布局 self.main_layout = QVBoxLayout(self.central_widget) self.main_layout.setContentsMargins(20, 20, 20, 20) self.main_layout.setSpacing(20) # 创建标题栏 self.create_title_bar() # 创建标签页 self.create_tab_widget() # 创建状态栏 self.create_status_bar() # 窗口居中 self.center_window() # 显示帮助信息 self.show_help_guide() # 设置窗口置顶 self.setWindowFlags(self.windowFlags() | Qt.WindowType.WindowStaysOnTopHint) def create_title_bar(self): """创建标题栏""" title_frame = QWidget() title_frame.setStyleSheet(""" QWidget { background: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #4CAF50, stop:1 #45a049); border-radius: 10px; } """) title_layout = QVBoxLayout(title_frame) title_layout.setContentsMargins(30, 20, 30, 20) title_label = QLabel("Excel批量插图-匹配表格内容") title_label.setFont(QFont("Microsoft YaHei", 14, QFont.Weight.Bold)) title_label.setStyleSheet("color: white;") title_layout.addWidget(title_label) self.main_layout.addWidget(title_frame) def create_tab_widget(self): """创建标签页""" self.tab_widget = QTabWidget() self.tab_widget.setStyleSheet(""" QTabWidget { background: transparent; } QTabBar { background: #f5f5f5; border-radius: 8px; margin-bottom: 10px; } QTabBar::tab { background: white; color: #666666; padding: 12px 25px; margin: 5px; border-radius: 6px; font: 10pt 'Microsoft YaHei'; font-weight: bold; } QTabBar::tab:selected { background: #4CAF50; color: white; } QTabBar::tab:hover:!selected { background: #f0f0f0; } """) # 创建列匹配标签页 self.create_column_tab() # 创建行匹配标签页 self.create_row_tab() self.main_layout.addWidget(self.tab_widget) def create_column_tab(self): """创建列匹配标签页""" tab = QWidget() self.tab_widget.addTab(tab, "列匹配模式") tab_layout = QVBoxLayout(tab) tab_layout.setContentsMargins(0, 0, 0, 0) tab_layout.setSpacing(15) # 描述 desc_label = QLabel("列匹配模式:按垂直方向匹配插入,适合单列数据匹配。") desc_label.setStyleSheet("color: #666666; font: 9pt 'Microsoft YaHei';") tab_layout.addWidget(desc_label) # 参数卡片 param_card = QWidget() param_card.setStyleSheet(""" QWidget { background: white; border-radius: 10px; border: 1px solid #e0e0e0; } """) param_layout = QVBoxLayout(param_card) param_layout.setContentsMargins(20, 20, 20, 20) param_layout.setSpacing(15) # 参数标题 param_title = QLabel("列匹配参数") param_title.setFont(QFont("Microsoft YaHei", 11, QFont.Weight.Bold)) param_title.setStyleSheet("color: #4CAF50;") param_layout.addWidget(param_title) # 参数输入 input_layout = QHBoxLayout() input_layout.setSpacing(20) # 待匹配列 match_col_layout = QVBoxLayout() match_col_label = QLabel("待匹配列:") match_col_label.setStyleSheet("color: #333333; font: 9pt 'Microsoft YaHei';") self.col_match = QLineEdit() self.col_match.setText("A") self.col_match.setStyleSheet(""" QLineEdit { padding: 8px; border: 1px solid #e0e0e0; border-radius: 6px; font: 9pt 'Microsoft YaHei'; background: #fafafa; } QLineEdit:focus { border: 1px solid #4CAF50; background: white; } """) self.col_match.setFixedWidth(80) match_col_layout.addWidget(match_col_label) match_col_layout.addWidget(self.col_match) input_layout.addLayout(match_col_layout) # 插入列 insert_col_layout = QVBoxLayout() insert_col_label = QLabel("插入列:") insert_col_label.setStyleSheet("color: #333333; font: 9pt 'Microsoft YaHei';") self.col_insert = QLineEdit() self.col_insert.setText("B") self.col_insert.setStyleSheet(""" QLineEdit { padding: 8px; border: 1px solid #e0e0e0; border-radius: 6px; font: 9pt 'Microsoft YaHei'; background: #fafafa; } QLineEdit:focus { border: 1px solid #4CAF50; background: white; } """) self.col_insert.setFixedWidth(80) insert_col_layout.addWidget(insert_col_label) insert_col_layout.addWidget(self.col_insert) input_layout.addLayout(insert_col_layout) # 边距 margin_layout = QVBoxLayout() margin_label = QLabel("边距:") margin_label.setStyleSheet("color: #333333; font: 9pt 'Microsoft YaHei';") self.col_margin = QLineEdit() self.col_margin.setText("2") self.col_margin.setStyleSheet(""" QLineEdit { padding: 8px; border: 1px solid #e0e0e0; border-radius: 6px; font: 9pt 'Microsoft YaHei'; background: #fafafa; } QLineEdit:focus { border: 1px solid #4CAF50; background: white; } """) self.col_margin.setFixedWidth(80) margin_layout.addWidget(margin_label) margin_layout.addWidget(self.col_margin) input_layout.addLayout(margin_layout) param_layout.addLayout(input_layout) # 文件夹选择 folder_layout = QHBoxLayout() folder_label = QLabel("图片文件夹:") folder_label.setStyleSheet("color: #333333; font: 9pt 'Microsoft YaHei';") folder_label.setFixedWidth(80) self.col_folder_var = QLineEdit() self.col_folder_var.setReadOnly(True) self.col_folder_var.setStyleSheet(""" QLineEdit { padding: 8px; border: 1px solid #e0e0e0; border-radius: 6px; font: 9pt 'Microsoft YaHei'; background: #fafafa; color: #666666; } """) browse_btn = QPushButton("浏览...") browse_btn.setStyleSheet(""" QPushButton { background: #4CAF50; color: white; padding: 8px 16px; border: none; border-radius: 6px; font: 9pt 'Microsoft YaHei'; } QPushButton:hover { background: #45a049; } """) browse_btn.clicked.connect(lambda: self.select_folder("column")) folder_layout.addWidget(folder_label) folder_layout.addWidget(self.col_folder_var) folder_layout.addWidget(browse_btn) param_layout.addLayout(folder_layout) tab_layout.addWidget(param_card) # 执行按钮 execute_btn = QPushButton("执行列匹配插入") execute_btn.setStyleSheet(""" QPushButton { background: #4CAF50; color: white; padding: 12px; border: none; border-radius: 8px; font: 10pt 'Microsoft YaHei'; font-weight: bold; } QPushButton:hover { background: #45a049; } """) execute_btn.clicked.connect(self.run_column_match) tab_layout.addWidget(execute_btn) # 日志卡片 log_card = QWidget() log_card.setStyleSheet(""" QWidget { background: white; border-radius: 10px; border: 1px solid #e0e0e0; } """) log_layout = QVBoxLayout(log_card) log_layout.setContentsMargins(20, 20, 20, 20) # 日志标题 log_title = QLabel("操作日志") log_title.setFont(QFont("Microsoft YaHei", 11, QFont.Weight.Bold)) log_title.setStyleSheet("color: #4CAF50;") log_layout.addWidget(log_title) # 日志文本框 self.col_log = QTextEdit() self.col_log.setReadOnly(True) self.col_log.setStyleSheet(""" QTextEdit { background: #fafafa; border: 1px solid #e0e0e0; border-radius: 6px; padding: 10px; font: 9pt 'Microsoft YaHei'; color: #333333; } """) self.col_log.setMinimumHeight(200) log_layout.addWidget(self.col_log) tab_layout.addWidget(log_card) def create_row_tab(self): """创建行匹配标签页""" tab = QWidget() self.tab_widget.addTab(tab, "行匹配模式") tab_layout = QVBoxLayout(tab) tab_layout.setContentsMargins(0, 0, 0, 0) tab_layout.setSpacing(15) # 描述 desc_label = QLabel("行匹配模式:按水平方向匹配插入,适合单行数据匹配。") desc_label.setStyleSheet("color: #666666; font: 9pt 'Microsoft YaHei';") tab_layout.addWidget(desc_label) # 参数卡片 param_card = QWidget() param_card.setStyleSheet(""" QWidget { background: white; border-radius: 10px; border: 1px solid #e0e0e0; } """) param_layout = QVBoxLayout(param_card) param_layout.setContentsMargins(20, 20, 20, 20) param_layout.setSpacing(15) # 参数标题 param_title = QLabel("行匹配参数") param_title.setFont(QFont("Microsoft YaHei", 11, QFont.Weight.Bold)) param_title.setStyleSheet("color: #4CAF50;") param_layout.addWidget(param_title) # 参数输入 input_layout = QHBoxLayout() input_layout.setSpacing(20) # 待匹配行 match_row_layout = QVBoxLayout() match_row_label = QLabel("待匹配行:") match_row_label.setStyleSheet("color: #333333; font: 9pt 'Microsoft YaHei';") self.row_match = QLineEdit() self.row_match.setText("1") self.row_match.setStyleSheet(""" QLineEdit { padding: 8px; border: 1px solid #e0e0e0; border-radius: 6px; font: 9pt 'Microsoft YaHei'; background: #fafafa; } QLineEdit:focus { border: 1px solid #4CAF50; background: white; } """) self.row_match.setFixedWidth(80) match_row_layout.addWidget(match_row_label) match_row_layout.addWidget(self.row_match) input_layout.addLayout(match_row_layout) # 插入行 insert_row_layout = QVBoxLayout() insert_row_label = QLabel("插入行:") insert_row_label.setStyleSheet("color: #333333; font: 9pt 'Microsoft YaHei';") self.row_insert = QLineEdit() self.row_insert.setText("2") self.row_insert.setStyleSheet(""" QLineEdit { padding: 8px; border: 1px solid #e0e0e0; border-radius: 6px; font: 9pt 'Microsoft YaHei'; background: #fafafa; } QLineEdit:focus { border: 1px solid #4CAF50; background: white; } """) self.row_insert.setFixedWidth(80) insert_row_layout.addWidget(insert_row_label) insert_row_layout.addWidget(self.row_insert) input_layout.addLayout(insert_row_layout) # 边距 margin_layout = QVBoxLayout() margin_label = QLabel("边距:") margin_label.setStyleSheet("color: #333333; font: 9pt 'Microsoft YaHei';") self.row_margin = QLineEdit() self.row_margin.setText("2") self.row_margin.setStyleSheet(""" QLineEdit { padding: 8px; border: 1px solid #e0e0e0; border-radius: 6px; font: 9pt 'Microsoft YaHei'; background: #fafafa; } QLineEdit:focus { border: 1px solid #4CAF50; background: white; } """) self.row_margin.setFixedWidth(80) margin_layout.addWidget(margin_label) margin_layout.addWidget(self.row_margin) input_layout.addLayout(margin_layout) param_layout.addLayout(input_layout) # 文件夹选择 folder_layout = QHBoxLayout() folder_label = QLabel("图片文件夹:") folder_label.setStyleSheet("color: #333333; font: 9pt 'Microsoft YaHei';") folder_label.setFixedWidth(80) self.row_folder_var = QLineEdit() self.row_folder_var.setReadOnly(True) self.row_folder_var.setStyleSheet(""" QLineEdit { padding: 8px; border: 1px solid #e0e0e0; border-radius: 6px; font: 9pt 'Microsoft YaHei'; background: #fafafa; color: #666666; } """) browse_btn = QPushButton("浏览...") browse_btn.setStyleSheet(""" QPushButton { background: #4CAF50; color: white; padding: 8px 16px; border: none; border-radius: 6px; font: 9pt 'Microsoft YaHei'; } QPushButton:hover { background: #45a049; } """) browse_btn.clicked.connect(lambda: self.select_folder("row")) folder_layout.addWidget(folder_label) folder_layout.addWidget(self.row_folder_var) folder_layout.addWidget(browse_btn) param_layout.addLayout(folder_layout) tab_layout.addWidget(param_card) # 执行按钮 execute_btn = QPushButton("执行行匹配插入") execute_btn.setStyleSheet(""" QPushButton { background: #4CAF50; color: white; padding: 12px; border: none; border-radius: 8px; font: 10pt 'Microsoft YaHei'; font-weight: bold; } QPushButton:hover { background: #45a049; } """) execute_btn.clicked.connect(self.run_row_match) tab_layout.addWidget(execute_btn) # 日志卡片 log_card = QWidget() log_card.setStyleSheet(""" QWidget { background: white; border-radius: 10px; border: 1px solid #e0e0e0; } """) log_layout = QVBoxLayout(log_card) log_layout.setContentsMargins(20, 20, 20, 20) # 日志标题 log_title = QLabel("操作日志") log_title.setFont(QFont("Microsoft YaHei", 11, QFont.Weight.Bold)) log_title.setStyleSheet("color: #4CAF50;") log_layout.addWidget(log_title) # 日志文本框 self.row_log = QTextEdit() self.row_log.setReadOnly(True) self.row_log.setStyleSheet(""" QTextEdit { background: #fafafa; border: 1px solid #e0e0e0; border-radius: 6px; padding: 10px; font: 9pt 'Microsoft YaHei'; color: #333333; } """) self.row_log.setMinimumHeight(200) log_layout.addWidget(self.row_log) tab_layout.addWidget(log_card) def create_status_bar(self): """创建状态栏""" status_frame = QWidget() status_layout = QHBoxLayout(status_frame) status_layout.setContentsMargins(0, 0, 0, 0) status_layout.setSpacing(20) # 左侧按钮 left_layout = QHBoxLayout() left_layout.setSpacing(15) # 窗口置顶 self.topmost_check = QCheckBox("窗口置顶") self.topmost_check.setChecked(True) self.topmost_check.setStyleSheet(""" QCheckBox { color: #666666; font: 9pt 'Microsoft YaHei'; } """) self.topmost_check.stateChanged.connect(self.toggle_topmost) left_layout.addWidget(self.topmost_check) # 帮助按钮 help_btn = QPushButton("帮助") help_btn.setStyleSheet(""" QPushButton { background: #f0f0f0; color: #666666; padding: 6px 12px; border: none; border-radius: 4px; font: 9pt 'Microsoft YaHei'; } QPushButton:hover { background: #e0e0e0; } """) help_btn.clicked.connect(self.show_help_guide) left_layout.addWidget(help_btn) # 右侧作者信息 author_label = QLabel("By Savetime2022") author_label.setStyleSheet(""" QLabel { color: #999999; font: 9pt 'Microsoft YaHei'; } QLabel:hover { color: #4CAF50; } """) author_label.setCursor(Qt.CursorShape.PointingHandCursor) status_layout.addLayout(left_layout) status_layout.addStretch() status_layout.addWidget(author_label) self.main_layout.addWidget(status_frame) def toggle_topmost(self, state): """切换窗口置顶状态""" if state == 2: # Qt.Checked self.setWindowFlags(self.windowFlags() | Qt.WindowType.WindowStaysOnTopHint) else: self.setWindowFlags(self.windowFlags() & ~Qt.WindowType.WindowStaysOnTopHint) self.show() def center_window(self): """窗口居中""" screen = QApplication.primaryScreen().geometry() size = self.geometry() self.move( (screen.width() - size.width()) // 2, (screen.height() - size.height()) // 2 ) def show_help_guide(self): """显示帮助信息""" help_text = """【新手操作指南】 1. 准备工作: - 打开Excel文件 - 准备图片文件夹(支持jpg/png/webp/bmp格式) 2. 参数设置: - 匹配列/行:包含名称的列或行(如A列或1行) - 插入列/行:图片要插入的位置(如B列或2行) - 边距:图片与单元格边界的距离(推荐2,0表示撑满) 3. 执行步骤: (1) 选择图片文件夹 (2) 点击"执行匹配插入"按钮 ★ 注意事项: - 图片名称需与单元格内容完全一致(不区分大小写) - 示例:单元格"产品A" → 图片"产品A.jpg" - 插入过程中请不要操作Excel """ current_tab = self.tab_widget.currentIndex() if current_tab == 0: self.col_log.setPlainText(help_text) else: self.row_log.setPlainText(help_text) def select_folder(self, mode): """选择图片文件夹""" folder_path = QFileDialog.getExistingDirectory( self, "选择图片文件夹", "." ) if folder_path: if mode == "column": self.col_folder_var.setText(folder_path) log_widget = self.col_log self.col_image_map = self.build_image_map_from_folder(folder_path) else: self.row_folder_var.setText(folder_path) log_widget = self.row_log self.row_image_map = self.build_image_map_from_folder(folder_path) self.log_message("开始加载图片...", log_widget) image_count = len(self.col_image_map if mode == "column" else self.row_image_map) self.log_message(f"加载完成:找到 {image_count} 张支持的图片。", log_widget) 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) return image_map def validate_column_params(self) -> Dict: """验证列模式参数有效性""" params = { 'match_col': self.col_match.text().upper(), 'insert_col': self.col_insert.text().upper(), 'margin': self.col_margin.text() } if not re.match(r'^[A-Z]{1,3}$', params['match_col']): raise ValueError("匹配列格式错误 (例如: A, B, AA)") if not re.match(r'^[A-Z]{1,3}$', params['insert_col']): raise ValueError("插入列格式错误 (例如: A, B, AA)") 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': 2, 'margin': int(params['margin']) } def validate_row_params(self) -> Dict: """验证行模式参数有效性""" params = { 'match_row': self.row_match.text(), 'insert_row': self.row_insert.text(), 'margin': self.row_margin.text() } 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']) } def excel_operation(self, app): """Excel操作上下文管理器""" class ExcelOperation: def __init__(self, app): self.app = app self.wb = None self.ws = None def __enter__(self): try: self.wb = self.app.books.active if not self.wb: raise Exception("没有活动的Excel工作簿。") self.ws = self.wb.sheets.active if not self.ws: raise Exception("活动工作簿中没有活动的工作表。") return self.wb, self.ws except Exception as e: raise Exception(f"无法访问活动工作簿或工作表: {str(e)}") def __exit__(self, exc_type, exc_val, exc_tb): pass return ExcelOperation(app) def insert_image(self, ws, image_path, cell_addr, margin, log_widget): """插入图片到指定单元格""" 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) 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) return False def run_column_match(self): """执行列匹配插入""" self.log_message("开始列匹配处理...", self.col_log, clear=True) try: if not self.col_folder_var.text(): QMessageBox.warning(self, "提示", "请先选择图片文件夹!") self.log_message("错误: 未选择图片文件夹。", self.col_log) return params = self.validate_column_params() app = xw.apps.active or xw.App(visible=True) with self.excel_operation(app) as (wb, ws): max_row = ws.used_range.last_cell.row success_inserts = 0 processed_excel_rows = 0 self.log_message( f"将在列 {params['match_col']} 中查找名称,图片插入到列 {params['insert_col']},从行 {params['start_row']} 开始。", self.col_log ) 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 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 ) 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 ) else: self.log_message( f"{match_cell_addr}:{name_to_match} → 未找到匹配图片", self.col_log ) summary = f"\n共处理 {processed_excel_rows} 行数据,成功插入 {success_inserts} 张图片。" self.log_message(summary, self.col_log) except Exception as e: error_msg = f"列匹配错误: {str(e)}" self.log_message(error_msg, self.col_log) QMessageBox.critical(self, "错误", error_msg) def run_row_match(self): """执行行匹配插入""" self.log_message("开始行匹配处理...", self.row_log, clear=True) try: if not self.row_folder_var.text(): QMessageBox.warning(self, "提示", "请先选择图片文件夹!") self.log_message("错误: 未选择图片文件夹。", self.row_log) return params = self.validate_row_params() app = xw.apps.active or xw.App(visible=True) with self.excel_operation(app) as (wb, ws): max_col = ws.used_range.last_cell.column success_inserts = 0 processed_excel_cols = 0 self.log_message( f"将在行 {params['match_row']} 中查找名称,图片插入到行 {params['insert_row']}。", self.row_log ) 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 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 ) 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 ) else: self.log_message( f"{match_cell_addr}:{name_to_match} → 未找到匹配图片", self.row_log ) summary = f"\n共处理 {processed_excel_cols} 列数据,成功插入 {success_inserts} 张图片。" self.log_message(summary, self.row_log) except Exception as e: error_msg = f"行匹配错误: {str(e)}" self.log_message(error_msg, self.row_log) QMessageBox.critical(self, "错误", 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) return self.log_message("【插入位置预览】", log_widget) for name, path in image_map.items(): self.log_message( f"{name} -> {os.path.basename(path)}", log_widget ) def log_message(self, message, log_widget, clear=False): """记录日志消息""" if clear: log_widget.setPlainText(message + "\n") else: current_text = log_widget.toPlainText() log_widget.setPlainText(current_text + message + "\n") log_widget.verticalScrollBar().setValue(log_widget.verticalScrollBar().maximum())if __name__ == "__main__": import sys app = QApplication(sys.argv) app.setStyle("Fusion") window = ExcelImageMatcherPro() window.show() sys.exit(app.exec())