## v1.5.3 (2024-03-21)

- 优化了完整流程处理逻辑:
  - 修改了OCR处理逻辑,当遇到已处理的图片时自动跳过并继续执行
  - 改进了错误处理,避免因图片已处理而中断流程
  - 优化了日志提示信息,提供更清晰的处理状态反馈
- 改进了OCRService的process_image方法:
  - 添加了文件存在性检查
  - 添加了文件类型验证
  - 添加了已处理文件检查
  - 优化了错误处理和日志记录
This commit is contained in:
2025-05-10 12:58:28 +08:00
parent 201aac35e6
commit 4a8169ff63
35 changed files with 497 additions and 145 deletions
Binary file not shown.
+21 -8
View File
@@ -39,14 +39,27 @@ class ConfigManager:
"""
if not os.path.exists(self.config_file):
self.create_default_config()
try:
self.config.read(self.config_file, encoding='utf-8')
logger.info(f"已加载配置文件: {self.config_file}")
except Exception as e:
logger.error(f"加载配置文件时出错: {e}")
logger.info("使用默认配置")
self.create_default_config(save=False)
else:
try:
# 先读取现有配置
self.config.read(self.config_file, encoding='utf-8')
# 检查是否有缺失的配置项,只添加缺失的项
for section, options in DEFAULT_CONFIG.items():
if not self.config.has_section(section):
self.config.add_section(section)
for option, value in options.items():
if not self.config.has_option(section, option):
self.config.set(section, option, value)
# 保存更新后的配置
self.save_config()
logger.info(f"已加载并更新配置文件: {self.config_file}")
except Exception as e:
logger.error(f"加载配置文件时出错: {e}")
logger.info("使用默认配置")
self.create_default_config(save=False)
def create_default_config(self, save: bool = True) -> None:
"""创建默认配置"""
+150 -1
View File
@@ -465,4 +465,153 @@ def show_barcode_mapping_dialog(*args, **kwargs):
"""
# 确保已导入ttk
import tkinter.ttk as ttk
return create_barcode_mapping_dialog(*args, **kwargs)
return create_barcode_mapping_dialog(*args, **kwargs)
def show_config_dialog(parent, config_manager, on_save=None):
"""显示配置设置对话框"""
dialog = tk.Toplevel(parent)
dialog.title("系统配置")
dialog.geometry("600x500")
dialog.resizable(False, False)
# 使窗口居中
dialog.update_idletasks()
width = dialog.winfo_width()
height = dialog.winfo_height()
x = (dialog.winfo_screenwidth() // 2) - (width // 2)
y = (dialog.winfo_screenheight() // 2) - (height // 2)
dialog.geometry('{}x{}+{}+{}'.format(width, height, x, y))
# 创建主框架
main_frame = ttk.Frame(dialog, padding="10")
main_frame.pack(fill=tk.BOTH, expand=True)
# 创建选项卡
notebook = ttk.Notebook(main_frame)
notebook.pack(fill=tk.BOTH, expand=True, pady=5)
# 创建各个配置页面的框架
api_frame = ttk.Frame(notebook, padding="10")
paths_frame = ttk.Frame(notebook, padding="10")
performance_frame = ttk.Frame(notebook, padding="10")
file_frame = ttk.Frame(notebook, padding="10")
# 添加选项卡
notebook.add(api_frame, text="API设置")
notebook.add(paths_frame, text="路径设置")
notebook.add(performance_frame, text="性能设置")
notebook.add(file_frame, text="文件设置")
# 存储所有输入框的引用
entries = {}
# API设置
ttk.Label(api_frame, text="百度OCR API设置", font=("Arial", 12, "bold")).pack(anchor=tk.W, pady=5)
# API Key
ttk.Label(api_frame, text="API Key:").pack(anchor=tk.W, pady=2)
api_key_entry = ttk.Entry(api_frame, width=50)
api_key_entry.pack(fill=tk.X, pady=2)
api_key_entry.insert(0, config_manager.get('API', 'api_key', ''))
entries[('API', 'api_key')] = api_key_entry
# Secret Key
ttk.Label(api_frame, text="Secret Key:").pack(anchor=tk.W, pady=2)
secret_key_entry = ttk.Entry(api_frame, width=50)
secret_key_entry.pack(fill=tk.X, pady=2)
secret_key_entry.insert(0, config_manager.get('API', 'secret_key', ''))
entries[('API', 'secret_key')] = secret_key_entry
# 超时设置
ttk.Label(api_frame, text="超时时间(秒):").pack(anchor=tk.W, pady=2)
timeout_entry = ttk.Entry(api_frame, width=10)
timeout_entry.pack(anchor=tk.W, pady=2)
timeout_entry.insert(0, config_manager.get('API', 'timeout', '30'))
entries[('API', 'timeout')] = timeout_entry
# 路径设置
ttk.Label(paths_frame, text="系统路径设置", font=("Arial", 12, "bold")).pack(anchor=tk.W, pady=5)
# 输入目录
ttk.Label(paths_frame, text="输入目录:").pack(anchor=tk.W, pady=2)
input_dir_entry = ttk.Entry(paths_frame, width=50)
input_dir_entry.pack(fill=tk.X, pady=2)
input_dir_entry.insert(0, config_manager.get('Paths', 'input_folder', 'data/input'))
entries[('Paths', 'input_folder')] = input_dir_entry
# 输出目录
ttk.Label(paths_frame, text="输出目录:").pack(anchor=tk.W, pady=2)
output_dir_entry = ttk.Entry(paths_frame, width=50)
output_dir_entry.pack(fill=tk.X, pady=2)
output_dir_entry.insert(0, config_manager.get('Paths', 'output_folder', 'data/output'))
entries[('Paths', 'output_folder')] = output_dir_entry
# 性能设置
ttk.Label(performance_frame, text="性能设置", font=("Arial", 12, "bold")).pack(anchor=tk.W, pady=5)
# 最大工作线程数
ttk.Label(performance_frame, text="最大工作线程数:").pack(anchor=tk.W, pady=2)
max_workers_entry = ttk.Entry(performance_frame, width=10)
max_workers_entry.pack(anchor=tk.W, pady=2)
max_workers_entry.insert(0, config_manager.get('Performance', 'max_workers', '4'))
entries[('Performance', 'max_workers')] = max_workers_entry
# 批处理大小
ttk.Label(performance_frame, text="批处理大小:").pack(anchor=tk.W, pady=2)
batch_size_entry = ttk.Entry(performance_frame, width=10)
batch_size_entry.pack(anchor=tk.W, pady=2)
batch_size_entry.insert(0, config_manager.get('Performance', 'batch_size', '5'))
entries[('Performance', 'batch_size')] = batch_size_entry
# 文件设置
ttk.Label(file_frame, text="文件设置", font=("Arial", 12, "bold")).pack(anchor=tk.W, pady=5)
# 允许的文件扩展名
ttk.Label(file_frame, text="允许的文件扩展名:").pack(anchor=tk.W, pady=2)
extensions_entry = ttk.Entry(file_frame, width=50)
extensions_entry.pack(fill=tk.X, pady=2)
extensions_entry.insert(0, config_manager.get('File', 'allowed_extensions', '.jpg,.jpeg,.png,.bmp'))
entries[('File', 'allowed_extensions')] = extensions_entry
# 最大文件大小
ttk.Label(file_frame, text="最大文件大小(MB):").pack(anchor=tk.W, pady=2)
max_size_entry = ttk.Entry(file_frame, width=10)
max_size_entry.pack(anchor=tk.W, pady=2)
max_size_entry.insert(0, config_manager.get('File', 'max_file_size_mb', '4'))
entries[('File', 'max_file_size_mb')] = max_size_entry
def save_config():
"""保存配置"""
try:
# 收集所有输入框的值
for (section, option), entry in entries.items():
value = entry.get().strip()
config_manager.update(section, option, value)
# 保存配置
config_manager.save_config()
if on_save:
on_save()
messagebox.showinfo("成功", "配置已保存")
dialog.destroy()
except Exception as e:
messagebox.showerror("错误", f"保存配置时出错: {str(e)}")
# 按钮框架
button_frame = ttk.Frame(main_frame)
button_frame.pack(fill=tk.X, pady=10)
# 保存按钮
ttk.Button(button_frame, text="保存", command=save_config).pack(side=tk.RIGHT, padx=5)
# 取消按钮
ttk.Button(button_frame, text="取消", command=dialog.destroy).pack(side=tk.RIGHT, padx=5)
# 设置模态
dialog.transient(parent)
dialog.grab_set()
# 等待窗口关闭
parent.wait_window(dialog)
Binary file not shown.
+39 -12
View File
@@ -5,6 +5,7 @@ OCR服务模块
"""
from typing import Dict, List, Optional, Tuple, Union, Any
import os
from ..config.settings import ConfigManager
from ..core.utils.log_utils import get_logger
@@ -43,23 +44,49 @@ class OCRService:
def process_image(self, image_path: str) -> Optional[str]:
"""
处理单图片
处理单图片文件
Args:
image_path: 图片路径
image_path: 图片文件路径
Returns:
输出Excel文件路径,如果处理失败则返回None
生成的Excel文件路径,如果处理失败则返回None
"""
logger.info(f"OCRService开始处理图片: {image_path}")
result = self.ocr_processor.process_image(image_path)
if result:
logger.info(f"OCRService处理图片成功: {image_path} -> {result}")
else:
logger.error(f"OCRService处理图片失败: {image_path}")
return result
try:
# 检查文件是否存在
if not os.path.exists(image_path):
logger.error(f"文件不存在: {image_path}")
return None
# 检查文件类型
if not self._is_valid_image(image_path):
logger.error(f"不支持的文件类型: {image_path}")
return None
# 检查是否已处理
excel_file = self._get_excel_path(image_path)
if os.path.exists(excel_file):
logger.info(f"文件已处理过,跳过OCR识别: {image_path}")
return excel_file
# 执行OCR识别
result = self.ocr_processor.process_image(image_path)
if not result:
logger.error(f"OCR识别失败: {image_path}")
return None
# 生成Excel文件
excel_file = self._generate_excel(result, image_path)
if not excel_file:
logger.error(f"生成Excel文件失败: {image_path}")
return None
logger.info(f"处理完成: {image_path} -> {excel_file}")
return excel_file
except Exception as e:
logger.error(f"处理图片时发生错误: {e}", exc_info=True)
return None
def process_images_batch(self, batch_size: int = None, max_workers: int = None) -> Tuple[int, int]:
"""