新增条码映射编辑功能图形化界面
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,468 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
对话框工具模块
|
||||
-------------
|
||||
提供各种弹窗和对话框显示功能
|
||||
"""
|
||||
|
||||
import os
|
||||
import tkinter as tk
|
||||
from tkinter import messagebox, ttk
|
||||
from datetime import datetime
|
||||
|
||||
def create_custom_dialog(title="提示", message="", result_file=None, time_info=None,
|
||||
count_info=None, amount_info=None, additional_info=None):
|
||||
"""
|
||||
创建自定义结果对话框
|
||||
|
||||
Args:
|
||||
title: 对话框标题
|
||||
message: 主要消息
|
||||
result_file: 结果文件路径(如果有)
|
||||
time_info: 时间信息(如:订单时间)
|
||||
count_info: 数量信息(如:处理条目数)
|
||||
amount_info: 金额信息(如:总金额)
|
||||
additional_info: 其他附加信息(字典格式)
|
||||
|
||||
Returns:
|
||||
dialog: 对话框对象
|
||||
"""
|
||||
# 创建对话框
|
||||
dialog = tk.Toplevel()
|
||||
dialog.title(title)
|
||||
dialog.geometry("450x320")
|
||||
dialog.resizable(False, False)
|
||||
|
||||
# 使弹窗居中显示
|
||||
center_window(dialog)
|
||||
|
||||
# 添加标题
|
||||
tk.Label(dialog, text=message, font=("Arial", 16, "bold")).pack(pady=10)
|
||||
|
||||
# 创建内容框架
|
||||
result_frame = tk.Frame(dialog)
|
||||
result_frame.pack(pady=10, fill=tk.BOTH, expand=True)
|
||||
|
||||
# 添加时间、数量、金额等信息
|
||||
if time_info:
|
||||
tk.Label(result_frame, text=f"时间信息: {time_info}", font=("Arial", 12)).pack(anchor=tk.W, padx=20, pady=5)
|
||||
|
||||
if count_info:
|
||||
tk.Label(result_frame, text=f"处理数量: {count_info}", font=("Arial", 12)).pack(anchor=tk.W, padx=20, pady=5)
|
||||
|
||||
if amount_info:
|
||||
tk.Label(result_frame, text=f"金额信息: {amount_info}", font=("Arial", 12)).pack(anchor=tk.W, padx=20, pady=5)
|
||||
|
||||
# 添加其他附加信息
|
||||
if additional_info and isinstance(additional_info, dict):
|
||||
for key, value in additional_info.items():
|
||||
tk.Label(result_frame, text=f"{key}: {value}", font=("Arial", 12)).pack(anchor=tk.W, padx=20, pady=5)
|
||||
|
||||
# 如果有结果文件,显示文件信息
|
||||
if result_file and os.path.exists(result_file):
|
||||
tk.Label(result_frame, text=f"输出文件: {os.path.basename(result_file)}", font=("Arial", 12)).pack(anchor=tk.W, padx=20, pady=5)
|
||||
|
||||
# 成功提示
|
||||
tk.Label(result_frame, text="处理已成功完成!", font=("Arial", 12, "bold"), fg="#28a745").pack(pady=10)
|
||||
|
||||
# 文件信息框
|
||||
file_frame = tk.Frame(result_frame, relief=tk.GROOVE, borderwidth=1)
|
||||
file_frame.pack(fill=tk.X, padx=15, pady=5)
|
||||
|
||||
tk.Label(file_frame, text="文件信息", font=("Arial", 10, "bold")).pack(anchor=tk.W, padx=10, pady=5)
|
||||
|
||||
# 获取文件大小和时间
|
||||
try:
|
||||
file_size = os.path.getsize(result_file)
|
||||
file_time = datetime.fromtimestamp(os.path.getmtime(result_file))
|
||||
|
||||
size_text = f"{file_size / 1024:.1f} KB" if file_size < 1024*1024 else f"{file_size / (1024*1024):.1f} MB"
|
||||
|
||||
tk.Label(file_frame, text=f"文件大小: {size_text}", font=("Arial", 10)).pack(anchor=tk.W, padx=10, pady=2)
|
||||
tk.Label(file_frame, text=f"创建时间: {file_time.strftime('%Y-%m-%d %H:%M:%S')}", font=("Arial", 10)).pack(anchor=tk.W, padx=10, pady=2)
|
||||
except:
|
||||
tk.Label(file_frame, text="无法获取文件信息", font=("Arial", 10)).pack(anchor=tk.W, padx=10, pady=2)
|
||||
|
||||
# 添加按钮
|
||||
button_frame = tk.Frame(dialog)
|
||||
button_frame.pack(pady=10)
|
||||
|
||||
tk.Button(button_frame, text="打开文件", command=lambda: os.startfile(result_file)).pack(side=tk.LEFT, padx=5)
|
||||
tk.Button(button_frame, text="打开所在文件夹", command=lambda: os.startfile(os.path.dirname(result_file))).pack(side=tk.LEFT, padx=5)
|
||||
tk.Button(button_frame, text="关闭", command=dialog.destroy).pack(side=tk.LEFT, padx=5)
|
||||
else:
|
||||
# 如果没有结果文件或文件不存在
|
||||
if result_file:
|
||||
tk.Label(result_frame, text="未找到输出文件", font=("Arial", 12)).pack(anchor=tk.W, padx=20, pady=5)
|
||||
tk.Label(result_frame, text="请检查输出目录", font=("Arial", 12, "bold"), fg="#dc3545").pack(pady=10)
|
||||
|
||||
# 添加按钮
|
||||
button_frame = tk.Frame(dialog)
|
||||
button_frame.pack(pady=10)
|
||||
|
||||
tk.Button(button_frame, text="打开输出目录", command=lambda: os.startfile(os.path.abspath("data/output"))).pack(side=tk.LEFT, padx=5)
|
||||
tk.Button(button_frame, text="关闭", command=dialog.destroy).pack(side=tk.LEFT, padx=5)
|
||||
|
||||
# 确保窗口显示在最前
|
||||
dialog.lift()
|
||||
dialog.attributes('-topmost', True)
|
||||
dialog.after_idle(lambda: dialog.attributes('-topmost', False))
|
||||
|
||||
return dialog
|
||||
|
||||
def show_custom_dialog(*args, **kwargs):
|
||||
"""
|
||||
显示自定义对话框
|
||||
|
||||
参数与create_custom_dialog相同
|
||||
|
||||
Returns:
|
||||
dialog: 对话框对象
|
||||
"""
|
||||
return create_custom_dialog(*args, **kwargs)
|
||||
|
||||
def center_window(window):
|
||||
"""使窗口居中显示"""
|
||||
window.update_idletasks()
|
||||
width = window.winfo_width()
|
||||
height = window.winfo_height()
|
||||
x = (window.winfo_screenwidth() // 2) - (width // 2)
|
||||
y = (window.winfo_screenheight() // 2) - (height // 2)
|
||||
window.geometry('{}x{}+{}+{}'.format(width, height, x, y))
|
||||
|
||||
def create_barcode_mapping_dialog(parent=None, on_save=None, current_mappings=None):
|
||||
"""
|
||||
创建条码映射编辑弹窗
|
||||
|
||||
Args:
|
||||
parent: 父窗口
|
||||
on_save: 保存回调函数,接收修改后的映射数据
|
||||
current_mappings: 当前的映射数据
|
||||
|
||||
Returns:
|
||||
dialog: 对话框对象
|
||||
"""
|
||||
dialog = tk.Toplevel(parent)
|
||||
dialog.title("条码映射编辑")
|
||||
dialog.geometry("600x500")
|
||||
dialog.resizable(True, True)
|
||||
|
||||
# 使弹窗居中显示
|
||||
center_window(dialog)
|
||||
|
||||
# 创建主框架
|
||||
main_frame = tk.Frame(dialog)
|
||||
main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
|
||||
|
||||
# 创建选项卡控件
|
||||
tab_control = ttk.Notebook(main_frame)
|
||||
|
||||
# 创建两个选项卡页面
|
||||
tab1 = tk.Frame(tab_control)
|
||||
tab2 = tk.Frame(tab_control)
|
||||
|
||||
tab_control.add(tab1, text="条码映射")
|
||||
tab_control.add(tab2, text="特殊处理")
|
||||
tab_control.pack(expand=True, fill=tk.BOTH)
|
||||
|
||||
# ========= 条码映射选项卡 =========
|
||||
# 顶部输入区域
|
||||
input_frame = tk.Frame(tab1)
|
||||
input_frame.pack(fill=tk.X, padx=5, pady=5)
|
||||
|
||||
tk.Label(input_frame, text="源条码:").grid(row=0, column=0, padx=5, pady=5)
|
||||
source_entry = tk.Entry(input_frame, width=20)
|
||||
source_entry.grid(row=0, column=1, padx=5, pady=5)
|
||||
|
||||
tk.Label(input_frame, text="目标条码:").grid(row=0, column=2, padx=5, pady=5)
|
||||
target_entry = tk.Entry(input_frame, width=20)
|
||||
target_entry.grid(row=0, column=3, padx=5, pady=5)
|
||||
|
||||
# 存储映射列表的变量
|
||||
mapping_list = []
|
||||
|
||||
# 映射列表显示区域
|
||||
list_frame = tk.Frame(tab1)
|
||||
list_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
|
||||
|
||||
columns = ("源条码", "目标条码")
|
||||
mapping_tree = ttk.Treeview(list_frame, columns=columns, show="headings", selectmode="browse")
|
||||
|
||||
for col in columns:
|
||||
mapping_tree.heading(col, text=col)
|
||||
mapping_tree.column(col, width=100)
|
||||
|
||||
mapping_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
||||
|
||||
# 添加滚动条
|
||||
scrollbar = ttk.Scrollbar(list_frame, orient=tk.VERTICAL, command=mapping_tree.yview)
|
||||
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
|
||||
mapping_tree.configure(yscrollcommand=scrollbar.set)
|
||||
|
||||
# ========= 特殊处理选项卡 =========
|
||||
# 顶部输入区域
|
||||
special_input_frame = tk.Frame(tab2)
|
||||
special_input_frame.pack(fill=tk.X, padx=5, pady=5)
|
||||
|
||||
tk.Label(special_input_frame, text="条码:").grid(row=0, column=0, padx=5, pady=5)
|
||||
special_barcode_entry = tk.Entry(special_input_frame, width=20)
|
||||
special_barcode_entry.grid(row=0, column=1, padx=5, pady=5)
|
||||
|
||||
tk.Label(special_input_frame, text="乘数:").grid(row=1, column=0, padx=5, pady=5)
|
||||
multiplier_entry = tk.Entry(special_input_frame, width=10)
|
||||
multiplier_entry.grid(row=1, column=1, padx=5, pady=5)
|
||||
|
||||
tk.Label(special_input_frame, text="目标单位:").grid(row=1, column=2, padx=5, pady=5)
|
||||
unit_entry = tk.Entry(special_input_frame, width=10)
|
||||
unit_entry.grid(row=1, column=3, padx=5, pady=5)
|
||||
|
||||
tk.Label(special_input_frame, text="固定单价:").grid(row=2, column=0, padx=5, pady=5)
|
||||
price_entry = tk.Entry(special_input_frame, width=10)
|
||||
price_entry.grid(row=2, column=1, padx=5, pady=5)
|
||||
|
||||
tk.Label(special_input_frame, text="规格:").grid(row=2, column=2, padx=5, pady=5)
|
||||
spec_entry = tk.Entry(special_input_frame, width=10)
|
||||
spec_entry.grid(row=2, column=3, padx=5, pady=5)
|
||||
|
||||
tk.Label(special_input_frame, text="描述:").grid(row=3, column=0, padx=5, pady=5)
|
||||
desc_entry = tk.Entry(special_input_frame, width=40)
|
||||
desc_entry.grid(row=3, column=1, columnspan=3, padx=5, pady=5)
|
||||
|
||||
# 特殊处理列表显示区域
|
||||
special_list_frame = tk.Frame(tab2)
|
||||
special_list_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
|
||||
|
||||
special_columns = ("条码", "乘数", "目标单位", "固定单价", "规格", "描述")
|
||||
special_tree = ttk.Treeview(special_list_frame, columns=special_columns, show="headings", selectmode="browse")
|
||||
|
||||
for col in special_columns:
|
||||
special_tree.heading(col, text=col)
|
||||
special_tree.column(col, width=80)
|
||||
|
||||
special_tree.column("描述", width=200)
|
||||
special_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
||||
|
||||
# 添加滚动条
|
||||
special_scrollbar = ttk.Scrollbar(special_list_frame, orient=tk.VERTICAL, command=special_tree.yview)
|
||||
special_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
|
||||
special_tree.configure(yscrollcommand=special_scrollbar.set)
|
||||
|
||||
# 存储特殊处理列表的变量
|
||||
special_list = []
|
||||
|
||||
# 按钮区域
|
||||
def add_mapping():
|
||||
source = source_entry.get().strip()
|
||||
target = target_entry.get().strip()
|
||||
|
||||
if not source or not target:
|
||||
messagebox.showwarning("输入错误", "源条码和目标条码不能为空")
|
||||
return
|
||||
|
||||
# 检查是否已存在
|
||||
for item in mapping_list:
|
||||
if item[0] == source:
|
||||
messagebox.showwarning("重复条码", f"条码 {source} 已存在映射")
|
||||
return
|
||||
|
||||
# 添加到列表
|
||||
mapping_list.append((source, target))
|
||||
mapping_tree.insert("", tk.END, values=(source, target))
|
||||
|
||||
# 清空输入框
|
||||
source_entry.delete(0, tk.END)
|
||||
target_entry.delete(0, tk.END)
|
||||
|
||||
def remove_mapping():
|
||||
selected = mapping_tree.selection()
|
||||
if not selected:
|
||||
messagebox.showwarning("未选择", "请先选择要删除的条目")
|
||||
return
|
||||
|
||||
# 获取选中项的索引
|
||||
item = mapping_tree.item(selected[0])
|
||||
source = item['values'][0]
|
||||
|
||||
# 从列表中移除
|
||||
for i, (s, _) in enumerate(mapping_list):
|
||||
if s == source:
|
||||
mapping_list.pop(i)
|
||||
break
|
||||
|
||||
# 从树中移除
|
||||
mapping_tree.delete(selected[0])
|
||||
|
||||
def add_special():
|
||||
barcode = special_barcode_entry.get().strip()
|
||||
multiplier = multiplier_entry.get().strip()
|
||||
unit = unit_entry.get().strip()
|
||||
price = price_entry.get().strip()
|
||||
spec = spec_entry.get().strip()
|
||||
desc = desc_entry.get().strip()
|
||||
|
||||
if not barcode:
|
||||
messagebox.showwarning("输入错误", "条码不能为空")
|
||||
return
|
||||
|
||||
# 检查是否已存在
|
||||
for item in special_list:
|
||||
if item[0] == barcode:
|
||||
messagebox.showwarning("重复条码", f"条码 {barcode} 已存在特殊处理")
|
||||
return
|
||||
|
||||
# 添加到列表
|
||||
special_list.append((barcode, multiplier, unit, price, spec, desc))
|
||||
special_tree.insert("", tk.END, values=(barcode, multiplier, unit, price, spec, desc))
|
||||
|
||||
# 清空输入框
|
||||
special_barcode_entry.delete(0, tk.END)
|
||||
multiplier_entry.delete(0, tk.END)
|
||||
unit_entry.delete(0, tk.END)
|
||||
price_entry.delete(0, tk.END)
|
||||
spec_entry.delete(0, tk.END)
|
||||
desc_entry.delete(0, tk.END)
|
||||
|
||||
def remove_special():
|
||||
selected = special_tree.selection()
|
||||
if not selected:
|
||||
messagebox.showwarning("未选择", "请先选择要删除的条目")
|
||||
return
|
||||
|
||||
# 获取选中项的索引
|
||||
item = special_tree.item(selected[0])
|
||||
barcode = item['values'][0]
|
||||
|
||||
# 从列表中移除
|
||||
for i, (b, _, _, _, _, _) in enumerate(special_list):
|
||||
if b == barcode:
|
||||
special_list.pop(i)
|
||||
break
|
||||
|
||||
# 从树中移除
|
||||
special_tree.delete(selected[0])
|
||||
|
||||
# 条码映射按钮
|
||||
btn_frame = tk.Frame(tab1)
|
||||
btn_frame.pack(fill=tk.X, padx=5, pady=5)
|
||||
|
||||
add_btn = tk.Button(btn_frame, text="添加映射", command=add_mapping)
|
||||
add_btn.pack(side=tk.LEFT, padx=5)
|
||||
|
||||
remove_btn = tk.Button(btn_frame, text="删除映射", command=remove_mapping)
|
||||
remove_btn.pack(side=tk.LEFT, padx=5)
|
||||
|
||||
# 特殊处理按钮
|
||||
special_btn_frame = tk.Frame(tab2)
|
||||
special_btn_frame.pack(fill=tk.X, padx=5, pady=5)
|
||||
|
||||
add_special_btn = tk.Button(special_btn_frame, text="添加特殊处理", command=add_special)
|
||||
add_special_btn.pack(side=tk.LEFT, padx=5)
|
||||
|
||||
remove_special_btn = tk.Button(special_btn_frame, text="删除特殊处理", command=remove_special)
|
||||
remove_special_btn.pack(side=tk.LEFT, padx=5)
|
||||
|
||||
# 底部按钮区域
|
||||
bottom_frame = tk.Frame(dialog)
|
||||
bottom_frame.pack(fill=tk.X, padx=10, pady=10)
|
||||
|
||||
def save_mappings():
|
||||
# 构建保存数据
|
||||
mappings = {}
|
||||
|
||||
# 添加条码映射
|
||||
for source, target in mapping_list:
|
||||
mappings[source] = {
|
||||
'map_to': target,
|
||||
'description': f'条码映射:{source} -> {target}'
|
||||
}
|
||||
|
||||
# 添加特殊处理
|
||||
for barcode, multiplier, unit, price, spec, desc in special_list:
|
||||
mappings[barcode] = {}
|
||||
|
||||
if multiplier:
|
||||
try:
|
||||
# 安全地转换multiplier为数字
|
||||
if isinstance(multiplier, str):
|
||||
if '.' in multiplier:
|
||||
mappings[barcode]['multiplier'] = float(multiplier)
|
||||
else:
|
||||
mappings[barcode]['multiplier'] = int(multiplier)
|
||||
else:
|
||||
# 已经是数字类型
|
||||
mappings[barcode]['multiplier'] = multiplier
|
||||
except ValueError:
|
||||
# 如果转换失败,保持原始字符串
|
||||
mappings[barcode]['multiplier'] = multiplier
|
||||
|
||||
if unit:
|
||||
mappings[barcode]['target_unit'] = unit
|
||||
|
||||
if price:
|
||||
try:
|
||||
# 安全地转换price为浮点数
|
||||
mappings[barcode]['fixed_price'] = float(price)
|
||||
except ValueError:
|
||||
# 如果转换失败,保持原始字符串
|
||||
mappings[barcode]['fixed_price'] = price
|
||||
|
||||
if spec:
|
||||
mappings[barcode]['specification'] = spec
|
||||
|
||||
if desc:
|
||||
mappings[barcode]['description'] = desc
|
||||
|
||||
# 调用保存回调
|
||||
if on_save:
|
||||
on_save(mappings)
|
||||
|
||||
messagebox.showinfo("保存成功", f"已保存{len(mapping_list)}个条码映射和{len(special_list)}个特殊处理规则")
|
||||
dialog.destroy()
|
||||
|
||||
def cancel():
|
||||
dialog.destroy()
|
||||
|
||||
save_btn = tk.Button(bottom_frame, text="保存", command=save_mappings)
|
||||
save_btn.pack(side=tk.RIGHT, padx=5)
|
||||
|
||||
cancel_btn = tk.Button(bottom_frame, text="取消", command=cancel)
|
||||
cancel_btn.pack(side=tk.RIGHT, padx=5)
|
||||
|
||||
# 导入当前映射数据
|
||||
if current_mappings:
|
||||
for barcode, data in current_mappings.items():
|
||||
if 'map_to' in data:
|
||||
# 这是条码映射
|
||||
mapping_list.append((barcode, data['map_to']))
|
||||
mapping_tree.insert("", tk.END, values=(barcode, data['map_to']))
|
||||
else:
|
||||
# 这是特殊处理
|
||||
multiplier = data.get('multiplier', '')
|
||||
unit = data.get('target_unit', '')
|
||||
price = data.get('fixed_price', '')
|
||||
spec = data.get('specification', '')
|
||||
desc = data.get('description', '')
|
||||
|
||||
special_list.append((barcode, multiplier, unit, price, spec, desc))
|
||||
special_tree.insert("", tk.END, values=(barcode, multiplier, unit, price, spec, desc))
|
||||
|
||||
# 确保窗口显示在最前
|
||||
dialog.transient(parent)
|
||||
dialog.grab_set()
|
||||
|
||||
return dialog
|
||||
|
||||
def show_barcode_mapping_dialog(*args, **kwargs):
|
||||
"""
|
||||
显示条码映射编辑弹窗
|
||||
|
||||
参数与create_barcode_mapping_dialog相同
|
||||
|
||||
Returns:
|
||||
dialog: 对话框对象
|
||||
"""
|
||||
# 确保已导入ttk
|
||||
import tkinter.ttk as ttk
|
||||
return create_barcode_mapping_dialog(*args, **kwargs)
|
||||
@@ -97,6 +97,36 @@ def get_logger(name: str) -> logging.Logger:
|
||||
return setup_logger(name)
|
||||
return logger
|
||||
|
||||
def set_log_level(level: str) -> None:
|
||||
"""
|
||||
设置所有日志记录器的级别
|
||||
|
||||
Args:
|
||||
level: 日志级别(DEBUG, INFO, WARNING, ERROR, CRITICAL)
|
||||
"""
|
||||
level_map = {
|
||||
'debug': logging.DEBUG,
|
||||
'info': logging.INFO,
|
||||
'warning': logging.WARNING,
|
||||
'error': logging.ERROR,
|
||||
'critical': logging.CRITICAL
|
||||
}
|
||||
|
||||
# 获取对应的日志级别
|
||||
log_level = level_map.get(level.lower(), logging.INFO)
|
||||
|
||||
# 获取所有记录器
|
||||
loggers = [logging.getLogger(name) for name in logging.root.manager.loggerDict]
|
||||
|
||||
# 设置每个记录器的级别
|
||||
for logger in loggers:
|
||||
logger.setLevel(log_level)
|
||||
|
||||
# 设置根记录器的级别
|
||||
logging.getLogger().setLevel(log_level)
|
||||
|
||||
print(f"所有日志记录器级别已设置为: {logging.getLevelName(log_level)}")
|
||||
|
||||
def close_logger(name: str) -> None:
|
||||
"""
|
||||
关闭日志记录器的所有处理器
|
||||
@@ -113,6 +143,25 @@ def close_logger(name: str) -> None:
|
||||
_handlers.pop(f"{name}_file", None)
|
||||
_handlers.pop(f"{name}_console", None)
|
||||
|
||||
def close_all_loggers() -> None:
|
||||
"""
|
||||
关闭所有日志记录器的处理器
|
||||
"""
|
||||
# 获取所有记录器
|
||||
loggers = [logging.getLogger(name) for name in logging.root.manager.loggerDict]
|
||||
|
||||
# 关闭每个记录器的处理器
|
||||
for logger in loggers:
|
||||
if hasattr(logger, 'handlers'):
|
||||
for handler in logger.handlers[:]:
|
||||
handler.close()
|
||||
logger.removeHandler(handler)
|
||||
|
||||
# 清空处理器缓存
|
||||
_handlers.clear()
|
||||
|
||||
print("所有日志记录器已关闭")
|
||||
|
||||
def cleanup_active_marker(name: str) -> None:
|
||||
"""
|
||||
清理日志活跃标记
|
||||
|
||||
Reference in New Issue
Block a user