## v1.5.3 (2024-03-21)
- 优化了完整流程处理逻辑: - 修改了OCR处理逻辑,当遇到已处理的图片时自动跳过并继续执行 - 改进了错误处理,避免因图片已处理而中断流程 - 优化了日志提示信息,提供更清晰的处理状态反馈 - 改进了OCRService的process_image方法: - 添加了文件存在性检查 - 添加了文件类型验证 - 添加了已处理文件检查 - 优化了错误处理和日志记录
This commit is contained in:
Binary file not shown.
+21
-8
@@ -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:
|
||||
"""创建默认配置"""
|
||||
|
||||
Binary file not shown.
@@ -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
@@ -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]:
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user