style: normalize code formatting across the codebase
- Standardize quote style (single to double quotes) - Improve code formatting consistency - Apply formatting to utilities, GUI components, and tools - Update imports and docstrings for consistency Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -58,7 +58,7 @@ class ConfigManager:
|
||||
Returns:
|
||||
配置值
|
||||
"""
|
||||
keys = key.split('.')
|
||||
keys = key.split(".")
|
||||
value = self.config
|
||||
|
||||
try:
|
||||
@@ -78,7 +78,7 @@ class ConfigManager:
|
||||
key: 配置键
|
||||
value: 配置值
|
||||
"""
|
||||
keys = key.split('.')
|
||||
keys = key.split(".")
|
||||
obj = self.config
|
||||
|
||||
# 导航到父对象
|
||||
|
||||
@@ -78,12 +78,12 @@ class DataExtractionTab(ttk.Frame):
|
||||
label_text="ProductionID 文件:",
|
||||
file_type="file",
|
||||
file_types=[("文本文件", "*.txt"), ("所有文件", "*.*")],
|
||||
initial_dir="D:/python/playwrite/"
|
||||
initial_dir="D:/python/playwrite/",
|
||||
)
|
||||
self.input_file_selector.pack(fill=tk.X)
|
||||
|
||||
# 设置默认文件
|
||||
default_input = self.config.get('paths.production_id_file', 'ProductionID.txt')
|
||||
default_input = self.config.get("paths.production_id_file", "ProductionID.txt")
|
||||
if os.path.exists(default_input):
|
||||
self.input_file_selector.set(default_input)
|
||||
|
||||
@@ -96,14 +96,14 @@ class DataExtractionTab(ttk.Frame):
|
||||
label_text="保存为:",
|
||||
file_type="file",
|
||||
file_types=[("Excel 文件", "*.xlsx"), ("所有文件", "*.*")],
|
||||
initial_dir=self.config.get('paths.data_dir', 'data/')
|
||||
initial_dir=self.config.get("paths.data_dir", "data/"),
|
||||
)
|
||||
self.output_file_selector.pack(fill=tk.X)
|
||||
|
||||
# 设置默认输出文件
|
||||
default_output = os.path.join(
|
||||
self.config.get('paths.data_dir', 'data/'),
|
||||
self.config.get('paths.default_output', '离散备料计划维护_合并.xlsx')
|
||||
self.config.get("paths.data_dir", "data/"),
|
||||
self.config.get("paths.default_output", "离散备料计划维护_合并.xlsx"),
|
||||
)
|
||||
self.output_file_selector.set(default_output)
|
||||
|
||||
@@ -111,30 +111,42 @@ class DataExtractionTab(ttk.Frame):
|
||||
options_group = ttk.LabelFrame(parent, text="提取选项", padding=10)
|
||||
options_group.pack(fill=tk.X, pady=5)
|
||||
|
||||
self.verbose_var = tk.BooleanVar(value=self.config.get('extraction.verbose', True))
|
||||
ttk.Checkbutton(options_group, text="详细日志", variable=self.verbose_var).grid(row=0, column=0, sticky="w", padx=5)
|
||||
self.verbose_var = tk.BooleanVar(
|
||||
value=self.config.get("extraction.verbose", True)
|
||||
)
|
||||
ttk.Checkbutton(options_group, text="详细日志", variable=self.verbose_var).grid(
|
||||
row=0, column=0, sticky="w", padx=5
|
||||
)
|
||||
|
||||
self.headless_var = tk.BooleanVar(value=self.config.get('erp.headless', True))
|
||||
ttk.Checkbutton(options_group, text="无头模式 (不显示浏览器)", variable=self.headless_var).grid(row=0, column=1, sticky="w", padx=5)
|
||||
self.headless_var = tk.BooleanVar(value=self.config.get("erp.headless", True))
|
||||
ttk.Checkbutton(
|
||||
options_group, text="无头模式 (不显示浏览器)", variable=self.headless_var
|
||||
).grid(row=0, column=1, sticky="w", padx=5)
|
||||
|
||||
# 进度显示
|
||||
progress_group = ttk.LabelFrame(parent, text="进度", padding=10)
|
||||
progress_group.pack(fill=tk.X, pady=5)
|
||||
|
||||
self.progress_bar = ttk.Progressbar(progress_group, mode='determinate')
|
||||
self.progress_bar = ttk.Progressbar(progress_group, mode="determinate")
|
||||
self.progress_bar.pack(fill=tk.X, pady=5)
|
||||
|
||||
self.status_label = ttk.Label(progress_group, text="就绪", relief=tk.SUNKEN, anchor=tk.W)
|
||||
self.status_label = ttk.Label(
|
||||
progress_group, text="就绪", relief=tk.SUNKEN, anchor=tk.W
|
||||
)
|
||||
self.status_label.pack(fill=tk.X)
|
||||
|
||||
# 控制按钮
|
||||
button_frame = ttk.Frame(parent)
|
||||
button_frame.pack(fill=tk.X, pady=10)
|
||||
|
||||
self.start_button = ttk.Button(button_frame, text="开始提取", command=self.start_extraction)
|
||||
self.start_button = ttk.Button(
|
||||
button_frame, text="开始提取", command=self.start_extraction
|
||||
)
|
||||
self.start_button.pack(side=tk.LEFT, padx=5)
|
||||
|
||||
self.stop_button = ttk.Button(button_frame, text="停止", command=self.stop_extraction, state=tk.DISABLED)
|
||||
self.stop_button = ttk.Button(
|
||||
button_frame, text="停止", command=self.stop_extraction, state=tk.DISABLED
|
||||
)
|
||||
self.stop_button.pack(side=tk.LEFT, padx=5)
|
||||
|
||||
def _create_log_panel(self, parent):
|
||||
@@ -169,16 +181,14 @@ class DataExtractionTab(ttk.Frame):
|
||||
self.extracting = True
|
||||
self.start_button.config(state=tk.DISABLED)
|
||||
self.stop_button.config(state=tk.NORMAL)
|
||||
self.progress_bar['value'] = 0
|
||||
self.progress_bar["value"] = 0
|
||||
self.status_label.config(text="正在登录...")
|
||||
self.log_text.clear()
|
||||
self.log_text.info("开始数据提取...")
|
||||
|
||||
# 在后台线程中执行提取
|
||||
self.extraction_thread = threading.Thread(
|
||||
target=self._extraction_worker,
|
||||
args=(input_file, output_file),
|
||||
daemon=True
|
||||
target=self._extraction_worker, args=(input_file, output_file), daemon=True
|
||||
)
|
||||
self.extraction_thread.start()
|
||||
|
||||
@@ -197,20 +207,24 @@ class DataExtractionTab(ttk.Frame):
|
||||
|
||||
# 创建提取器实例
|
||||
self.extractor = DiscreteMaterialPlanExtractor(
|
||||
username=self.config.get('erp.username'),
|
||||
password=self.config.get('erp.password'),
|
||||
username=self.config.get("erp.username"),
|
||||
password=self.config.get("erp.password"),
|
||||
headless=self.headless_var.get(),
|
||||
verbose=self.verbose_var.get(),
|
||||
batch_size=self.config.get('extraction.batch_size', 100)
|
||||
batch_size=self.config.get("extraction.batch_size", 100),
|
||||
)
|
||||
|
||||
# 创建实时输出流,每次写入立即更新 GUI
|
||||
realtime_output = RealtimeOutput(lambda line: self._update_log(line, "INFO"))
|
||||
realtime_output = RealtimeOutput(
|
||||
lambda line: self._update_log(line, "INFO")
|
||||
)
|
||||
|
||||
# 创建进度回调函数
|
||||
def progress_callback(progress_info: ProgressInfo):
|
||||
# 计算总体进度百分比
|
||||
overall_percent = self.progress_calculator.calculate_overall_percent(progress_info)
|
||||
overall_percent = self.progress_calculator.calculate_overall_percent(
|
||||
progress_info
|
||||
)
|
||||
self._update_progress(overall_percent, progress_info.message)
|
||||
|
||||
# 重定向 stdout 并执行提取(带进度回调)
|
||||
@@ -218,7 +232,7 @@ class DataExtractionTab(ttk.Frame):
|
||||
result = self.extractor.extract(
|
||||
production_id_file=input_file,
|
||||
output_file=output_file,
|
||||
progress_callback=progress_callback
|
||||
progress_callback=progress_callback,
|
||||
)
|
||||
|
||||
if result and self.extracting:
|
||||
@@ -249,7 +263,7 @@ class DataExtractionTab(ttk.Frame):
|
||||
try:
|
||||
progress_data = self.progress_queue.get_nowait()
|
||||
value, message = progress_data
|
||||
self.progress_bar['value'] = value
|
||||
self.progress_bar["value"] = value
|
||||
self.status_label.config(text=message)
|
||||
except queue.Empty:
|
||||
break
|
||||
@@ -266,6 +280,7 @@ class DataExtractionTab(ttk.Frame):
|
||||
|
||||
def _update_log(self, message: str, level: str = "INFO"):
|
||||
"""线程安全的日志更新"""
|
||||
|
||||
def update():
|
||||
if self.extracting or level in ["ERROR", "WARNING", "SUCCESS"]:
|
||||
if level == "INFO":
|
||||
|
||||
@@ -67,7 +67,7 @@ class DataQueryTab(ttk.Frame):
|
||||
info_label = ttk.Label(
|
||||
parent,
|
||||
text="输入总排号列表(每行一个),查询对应的生产订单号信息",
|
||||
foreground="#666666"
|
||||
foreground="#666666",
|
||||
)
|
||||
info_label.pack(anchor=tk.W, pady=(0, 5))
|
||||
|
||||
@@ -82,7 +82,9 @@ class DataQueryTab(ttk.Frame):
|
||||
self.input_text = tk.Text(text_frame, height=10, wrap=tk.WORD)
|
||||
self.input_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
||||
|
||||
text_scrollbar = ttk.Scrollbar(text_frame, orient=tk.VERTICAL, command=self.input_text.yview)
|
||||
text_scrollbar = ttk.Scrollbar(
|
||||
text_frame, orient=tk.VERTICAL, command=self.input_text.yview
|
||||
)
|
||||
text_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
|
||||
self.input_text.configure(yscrollcommand=text_scrollbar.set)
|
||||
|
||||
@@ -102,20 +104,31 @@ class DataQueryTab(ttk.Frame):
|
||||
# 快捷按钮
|
||||
ttk.Separator(right_frame, orient=tk.HORIZONTAL).pack(fill=tk.X, pady=10)
|
||||
|
||||
ttk.Button(right_frame, text="加载 ProductionID.txt", command=self._load_production_id).pack(fill=tk.X, pady=2)
|
||||
ttk.Button(right_frame, text="清空输入", command=self._clear_input).pack(fill=tk.X, pady=2)
|
||||
ttk.Button(
|
||||
right_frame, text="加载 ProductionID.txt", command=self._load_production_id
|
||||
).pack(fill=tk.X, pady=2)
|
||||
ttk.Button(right_frame, text="清空输入", command=self._clear_input).pack(
|
||||
fill=tk.X, pady=2
|
||||
)
|
||||
|
||||
# 查询按钮
|
||||
button_frame = ttk.Frame(parent)
|
||||
button_frame.pack(fill=tk.X, pady=(10, 0))
|
||||
|
||||
self.query_button = ttk.Button(button_frame, text="执行查询", command=self.execute_query)
|
||||
self.query_button = ttk.Button(
|
||||
button_frame, text="执行查询", command=self.execute_query
|
||||
)
|
||||
self.query_button.pack(side=tk.LEFT, padx=5)
|
||||
|
||||
self.export_button = ttk.Button(button_frame, text="导出结果", command=self.export_results, state=tk.DISABLED)
|
||||
self.export_button = ttk.Button(
|
||||
button_frame,
|
||||
text="导出结果",
|
||||
command=self.export_results,
|
||||
state=tk.DISABLED,
|
||||
)
|
||||
self.export_button.pack(side=tk.LEFT, padx=5)
|
||||
|
||||
self.progress = ttk.Progressbar(parent, mode='indeterminate')
|
||||
self.progress = ttk.Progressbar(parent, mode="indeterminate")
|
||||
self.progress.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5)
|
||||
|
||||
def _create_result_table(self, parent):
|
||||
@@ -135,9 +148,13 @@ class DataQueryTab(ttk.Frame):
|
||||
|
||||
# 添加滚动条
|
||||
scrollbar_y = ttk.Scrollbar(parent, orient=tk.VERTICAL, command=self.tree.yview)
|
||||
scrollbar_x = ttk.Scrollbar(parent, orient=tk.HORIZONTAL, command=self.tree.xview)
|
||||
scrollbar_x = ttk.Scrollbar(
|
||||
parent, orient=tk.HORIZONTAL, command=self.tree.xview
|
||||
)
|
||||
|
||||
self.tree.configure(yscrollcommand=scrollbar_y.set, xscrollcommand=scrollbar_x.set)
|
||||
self.tree.configure(
|
||||
yscrollcommand=scrollbar_y.set, xscrollcommand=scrollbar_x.set
|
||||
)
|
||||
|
||||
# 布局
|
||||
self.tree.grid(row=0, column=0, sticky="nsew")
|
||||
@@ -154,7 +171,7 @@ class DataQueryTab(ttk.Frame):
|
||||
|
||||
def _load_production_id(self):
|
||||
"""加载 ProductionID.txt 文件"""
|
||||
default_path = self.config.get('paths.production_id_file', 'ProductionID.txt')
|
||||
default_path = self.config.get("paths.production_id_file", "ProductionID.txt")
|
||||
|
||||
# 检查默认路径
|
||||
if os.path.exists(default_path):
|
||||
@@ -162,16 +179,17 @@ class DataQueryTab(ttk.Frame):
|
||||
else:
|
||||
# 打开文件选择对话框
|
||||
from tkinter import filedialog
|
||||
|
||||
file_path = filedialog.askopenfilename(
|
||||
title="选择 ProductionID 文件",
|
||||
filetypes=[("文本文件", "*.txt"), ("所有文件", "*.*")]
|
||||
filetypes=[("文本文件", "*.txt"), ("所有文件", "*.*")],
|
||||
)
|
||||
|
||||
if not file_path:
|
||||
return
|
||||
|
||||
try:
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
with open(file_path, "r", encoding="utf-8") as f:
|
||||
content = f.read()
|
||||
|
||||
self.input_text.delete("1.0", tk.END)
|
||||
@@ -196,7 +214,9 @@ class DataQueryTab(ttk.Frame):
|
||||
return
|
||||
|
||||
# 解析总排号
|
||||
production_ids = [line.strip() for line in input_text.split('\n') if line.strip()]
|
||||
production_ids = [
|
||||
line.strip() for line in input_text.split("\n") if line.strip()
|
||||
]
|
||||
|
||||
if not production_ids:
|
||||
messagebox.showwarning("警告", "没有有效的总排号")
|
||||
@@ -215,9 +235,7 @@ class DataQueryTab(ttk.Frame):
|
||||
|
||||
# 在后台线程中执行查询
|
||||
query_thread = threading.Thread(
|
||||
target=self._query_worker,
|
||||
args=(production_ids,),
|
||||
daemon=True
|
||||
target=self._query_worker, args=(production_ids,), daemon=True
|
||||
)
|
||||
query_thread.start()
|
||||
|
||||
@@ -253,6 +271,7 @@ class DataQueryTab(ttk.Frame):
|
||||
|
||||
def _display_results(self, results: list):
|
||||
"""在主线程中显示结果"""
|
||||
|
||||
def update():
|
||||
for 总排号, 生产订单号 in results:
|
||||
self.tree.insert("", tk.END, values=(总排号, 生产订单号, ""))
|
||||
@@ -265,6 +284,7 @@ class DataQueryTab(ttk.Frame):
|
||||
|
||||
def _update_log(self, message: str, level: str = "INFO"):
|
||||
"""线程安全的日志更新"""
|
||||
|
||||
def update():
|
||||
if level == "INFO":
|
||||
self.log_text.info(message)
|
||||
@@ -287,7 +307,7 @@ class DataQueryTab(ttk.Frame):
|
||||
title="导出查询结果",
|
||||
defaultextension=".xlsx",
|
||||
filetypes=[("Excel 文件", "*.xlsx"), ("所有文件", "*.*")],
|
||||
initialfile="生产订单号查询结果.xlsx"
|
||||
initialfile="生产订单号查询结果.xlsx",
|
||||
)
|
||||
|
||||
if not output_file:
|
||||
|
||||
@@ -92,13 +92,17 @@ class MainWindow:
|
||||
# 状态文本
|
||||
self.status_text = tk.StringVar()
|
||||
self.status_text.set("就绪")
|
||||
status_label = ttk.Label(self.status_bar, textvariable=self.status_text, anchor=tk.W)
|
||||
status_label = ttk.Label(
|
||||
self.status_bar, textvariable=self.status_text, anchor=tk.W
|
||||
)
|
||||
status_label.pack(side=tk.LEFT, padx=5)
|
||||
|
||||
# 配置状态指示
|
||||
self.config_status = tk.StringVar()
|
||||
self.config_status.set("配置已加载")
|
||||
config_label = ttk.Label(self.status_bar, textvariable=self.config_status, anchor=tk.E)
|
||||
config_label = ttk.Label(
|
||||
self.status_bar, textvariable=self.config_status, anchor=tk.E
|
||||
)
|
||||
config_label.pack(side=tk.RIGHT, padx=5)
|
||||
|
||||
def _center_window(self):
|
||||
@@ -113,6 +117,7 @@ class MainWindow:
|
||||
def show_about(self):
|
||||
"""显示关于对话框"""
|
||||
from tkinter import messagebox
|
||||
|
||||
messagebox.showinfo(
|
||||
"关于 ERP 自动化工具",
|
||||
"ERP 自动化工具 v1.0\n\n"
|
||||
@@ -121,5 +126,5 @@ class MainWindow:
|
||||
"• 物料校验 - 校验物料状态并匹配待删除物料\n"
|
||||
"• 数据查询 - 查询生产订单号等信息\n"
|
||||
"• 设置管理 - 管理系统配置\n\n"
|
||||
"基于 Playwright 和 Python 开发"
|
||||
"基于 Playwright 和 Python 开发",
|
||||
)
|
||||
|
||||
@@ -76,7 +76,7 @@ class MaterialValidationTab(ttk.Frame):
|
||||
text="使用现有 Excel 文件",
|
||||
variable=self.source_mode,
|
||||
value="existing",
|
||||
command=self._on_source_mode_change
|
||||
command=self._on_source_mode_change,
|
||||
).grid(row=0, column=0, sticky="w", padx=5)
|
||||
|
||||
ttk.Radiobutton(
|
||||
@@ -84,7 +84,7 @@ class MaterialValidationTab(ttk.Frame):
|
||||
text="完整工作流 (提取 + 校验)",
|
||||
variable=self.source_mode,
|
||||
value="full",
|
||||
command=self._on_source_mode_change
|
||||
command=self._on_source_mode_change,
|
||||
).grid(row=0, column=1, sticky="w", padx=5)
|
||||
|
||||
# 文件选择
|
||||
@@ -100,7 +100,7 @@ class MaterialValidationTab(ttk.Frame):
|
||||
label_text="现有 Excel 文件:",
|
||||
file_type="file",
|
||||
file_types=[("Excel 文件", "*.xlsx"), ("所有文件", "*.*")],
|
||||
initial_dir=self.config.get('paths.data_dir', 'data/')
|
||||
initial_dir=self.config.get("paths.data_dir", "data/"),
|
||||
)
|
||||
self.existing_excel_selector.pack(fill=tk.X)
|
||||
|
||||
@@ -113,7 +113,7 @@ class MaterialValidationTab(ttk.Frame):
|
||||
label_text="ProductionID 文件:",
|
||||
file_type="file",
|
||||
file_types=[("文本文件", "*.txt"), ("所有文件", "*.*")],
|
||||
initial_dir="D:/python/playwrite/"
|
||||
initial_dir="D:/python/playwrite/",
|
||||
)
|
||||
self.production_id_selector.pack(fill=tk.X)
|
||||
|
||||
@@ -126,14 +126,14 @@ class MaterialValidationTab(ttk.Frame):
|
||||
label_text="输出文件:",
|
||||
file_type="file",
|
||||
file_types=[("Excel 文件", "*.xlsx"), ("所有文件", "*.*")],
|
||||
initial_dir=self.config.get('paths.data_dir', 'data/')
|
||||
initial_dir=self.config.get("paths.data_dir", "data/"),
|
||||
)
|
||||
self.output_file_selector.pack(fill=tk.X)
|
||||
|
||||
# 设置默认输出
|
||||
default_output = os.path.join(
|
||||
self.config.get('paths.data_dir', 'data/'),
|
||||
self.config.get('paths.validation_output', '物料状态校验结果.xlsx')
|
||||
self.config.get("paths.data_dir", "data/"),
|
||||
self.config.get("paths.validation_output", "物料状态校验结果.xlsx"),
|
||||
)
|
||||
self.output_file_selector.set(default_output)
|
||||
|
||||
@@ -141,10 +141,17 @@ class MaterialValidationTab(ttk.Frame):
|
||||
button_frame = ttk.Frame(parent)
|
||||
button_frame.pack(fill=tk.X, pady=10)
|
||||
|
||||
self.start_button = ttk.Button(button_frame, text="开始校验", command=self.start_validation)
|
||||
self.start_button = ttk.Button(
|
||||
button_frame, text="开始校验", command=self.start_validation
|
||||
)
|
||||
self.start_button.pack(side=tk.LEFT, padx=5)
|
||||
|
||||
self.export_button = ttk.Button(button_frame, text="导出结果", command=self.export_results, state=tk.DISABLED)
|
||||
self.export_button = ttk.Button(
|
||||
button_frame,
|
||||
text="导出结果",
|
||||
command=self.export_results,
|
||||
state=tk.DISABLED,
|
||||
)
|
||||
self.export_button.pack(side=tk.LEFT, padx=5)
|
||||
|
||||
def _create_result_table(self, parent):
|
||||
@@ -166,9 +173,13 @@ class MaterialValidationTab(ttk.Frame):
|
||||
|
||||
# 添加滚动条
|
||||
scrollbar_y = ttk.Scrollbar(parent, orient=tk.VERTICAL, command=self.tree.yview)
|
||||
scrollbar_x = ttk.Scrollbar(parent, orient=tk.HORIZONTAL, command=self.tree.xview)
|
||||
scrollbar_x = ttk.Scrollbar(
|
||||
parent, orient=tk.HORIZONTAL, command=self.tree.xview
|
||||
)
|
||||
|
||||
self.tree.configure(yscrollcommand=scrollbar_y.set, xscrollcommand=scrollbar_x.set)
|
||||
self.tree.configure(
|
||||
yscrollcommand=scrollbar_y.set, xscrollcommand=scrollbar_x.set
|
||||
)
|
||||
|
||||
# 布局
|
||||
self.tree.grid(row=0, column=0, sticky="nsew")
|
||||
@@ -239,11 +250,13 @@ class MaterialValidationTab(ttk.Frame):
|
||||
validation_thread = threading.Thread(
|
||||
target=self._validation_worker,
|
||||
args=(input_file, production_id_file, output_file),
|
||||
daemon=True
|
||||
daemon=True,
|
||||
)
|
||||
validation_thread.start()
|
||||
|
||||
def _validation_worker(self, input_file: str, production_id_file: str, output_file: str):
|
||||
def _validation_worker(
|
||||
self, input_file: str, production_id_file: str, output_file: str
|
||||
):
|
||||
"""校验工作线程"""
|
||||
try:
|
||||
# 导入校验器
|
||||
@@ -251,10 +264,10 @@ class MaterialValidationTab(ttk.Frame):
|
||||
|
||||
# 创建校验器实例(需要 ERP 凭据,因为可能需要登录系统)
|
||||
validator = MaterialStatusValidator(
|
||||
username=self.config.get('erp.username'),
|
||||
password=self.config.get('erp.password'),
|
||||
headless=self.config.get('erp.headless', True),
|
||||
verbose=True
|
||||
username=self.config.get("erp.username"),
|
||||
password=self.config.get("erp.password"),
|
||||
headless=self.config.get("erp.headless", True),
|
||||
verbose=True,
|
||||
)
|
||||
|
||||
# 捕获 stdout 输出
|
||||
@@ -264,20 +277,19 @@ class MaterialValidationTab(ttk.Frame):
|
||||
with redirect_stdout(captured_output):
|
||||
if self.source_mode.get() == "existing":
|
||||
result = validator.validate_from_existing_excel(
|
||||
excel_file=input_file,
|
||||
output_file=output_file
|
||||
excel_file=input_file, output_file=output_file
|
||||
)
|
||||
else:
|
||||
result = validator.validate(
|
||||
production_id_file=production_id_file,
|
||||
merged_excel_file=None, # 将在内部生成
|
||||
output_file=output_file
|
||||
output_file=output_file,
|
||||
)
|
||||
|
||||
# 获取捕获的输出并显示到日志
|
||||
output_text = captured_output.getvalue()
|
||||
if output_text:
|
||||
for line in output_text.split('\n'):
|
||||
for line in output_text.split("\n"):
|
||||
if line.strip():
|
||||
self._update_log(line, "INFO")
|
||||
|
||||
@@ -307,12 +319,16 @@ class MaterialValidationTab(ttk.Frame):
|
||||
# 在主线程中更新表格
|
||||
def update_table():
|
||||
for _, row in df.iterrows():
|
||||
self.tree.insert("", tk.END, values=(
|
||||
row.get('材料名称', ''),
|
||||
row.get('匹配的MaterialName', ''),
|
||||
row.get('负责人', ''),
|
||||
row.get('匹配状态', '')
|
||||
))
|
||||
self.tree.insert(
|
||||
"",
|
||||
tk.END,
|
||||
values=(
|
||||
row.get("材料名称", ""),
|
||||
row.get("匹配的MaterialName", ""),
|
||||
row.get("负责人", ""),
|
||||
row.get("匹配状态", ""),
|
||||
),
|
||||
)
|
||||
|
||||
if len(df) > 0:
|
||||
self.export_button.config(state=tk.NORMAL)
|
||||
@@ -325,6 +341,7 @@ class MaterialValidationTab(ttk.Frame):
|
||||
|
||||
def _update_log(self, message: str, level: str = "INFO"):
|
||||
"""线程安全的日志更新"""
|
||||
|
||||
def update():
|
||||
if level == "INFO":
|
||||
self.log_text.info(message)
|
||||
@@ -345,7 +362,7 @@ class MaterialValidationTab(ttk.Frame):
|
||||
output_file = filedialog.asksaveasfilename(
|
||||
title="保存结果",
|
||||
defaultextension=".xlsx",
|
||||
filetypes=[("Excel 文件", "*.xlsx"), ("所有文件", "*.*")]
|
||||
filetypes=[("Excel 文件", "*.xlsx"), ("所有文件", "*.*")],
|
||||
)
|
||||
|
||||
if not output_file:
|
||||
@@ -355,7 +372,7 @@ class MaterialValidationTab(ttk.Frame):
|
||||
# 收集表格数据
|
||||
data = []
|
||||
for item in self.tree.get_children():
|
||||
values = self.tree.item(item)['values']
|
||||
values = self.tree.item(item)["values"]
|
||||
data.append(values)
|
||||
|
||||
if not data:
|
||||
@@ -363,7 +380,9 @@ class MaterialValidationTab(ttk.Frame):
|
||||
return
|
||||
|
||||
# 创建 DataFrame 并保存
|
||||
df = pd.DataFrame(data, columns=["材料名称", "匹配的MaterialName", "负责人", "匹配状态"])
|
||||
df = pd.DataFrame(
|
||||
data, columns=["材料名称", "匹配的MaterialName", "负责人", "匹配状态"]
|
||||
)
|
||||
df.to_excel(output_file, index=False)
|
||||
|
||||
messagebox.showinfo("成功", f"结果已导出到:{output_file}")
|
||||
|
||||
@@ -17,7 +17,10 @@ class ProgressInfo:
|
||||
|
||||
用于在后台任务和 GUI 之间传递进度信息。
|
||||
"""
|
||||
stage: str # 阶段标识: 'login', 'query', 'download', 'logout', 'convert', 'complete'
|
||||
|
||||
stage: (
|
||||
str # 阶段标识: 'login', 'query', 'download', 'logout', 'convert', 'complete'
|
||||
)
|
||||
current: int # 当前进度值
|
||||
total: int # 总量
|
||||
message: str # 显示给用户的消息
|
||||
@@ -43,12 +46,12 @@ class ProgressCalculator:
|
||||
|
||||
# 各阶段在总进度中的占比
|
||||
STAGE_WEIGHTS = {
|
||||
'login': 5, # 登录: 0-5%
|
||||
'query': 5, # 查询: 5-10%
|
||||
'download': 65, # 下载: 10-75%
|
||||
'logout': 5, # 注销: 75-80%
|
||||
'convert': 15, # 转换: 80-95%
|
||||
'complete': 5, # 完成: 95-100%
|
||||
"login": 5, # 登录: 0-5%
|
||||
"query": 5, # 查询: 5-10%
|
||||
"download": 65, # 下载: 10-75%
|
||||
"logout": 5, # 注销: 75-80%
|
||||
"convert": 15, # 转换: 80-95%
|
||||
"complete": 5, # 完成: 95-100%
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
@@ -76,7 +79,7 @@ class ProgressCalculator:
|
||||
"""
|
||||
stage = progress.stage
|
||||
|
||||
if stage == 'complete':
|
||||
if stage == "complete":
|
||||
return 100
|
||||
|
||||
if stage not in self._stage_offsets:
|
||||
|
||||
@@ -36,8 +36,7 @@ class SettingsTab(ttk.Frame):
|
||||
scrollable_frame = ttk.Frame(canvas)
|
||||
|
||||
scrollable_frame.bind(
|
||||
"<Configure>",
|
||||
lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
|
||||
"<Configure>", lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
|
||||
)
|
||||
|
||||
canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
|
||||
@@ -62,10 +61,18 @@ class SettingsTab(ttk.Frame):
|
||||
button_frame = ttk.Frame(scrollable_frame)
|
||||
button_frame.grid(row=5, column=0, columnspan=2, pady=20, sticky="ew")
|
||||
|
||||
ttk.Button(button_frame, text="测试 ERP 连接", command=self.test_erp_connection).pack(side="left", padx=5)
|
||||
ttk.Button(button_frame, text="测试数据库连接", command=self.test_db_connection).pack(side="left", padx=5)
|
||||
ttk.Button(button_frame, text="保存设置", command=self.save_settings).pack(side="left", padx=5)
|
||||
ttk.Button(button_frame, text="恢复默认", command=self.reset_defaults).pack(side="left", padx=5)
|
||||
ttk.Button(
|
||||
button_frame, text="测试 ERP 连接", command=self.test_erp_connection
|
||||
).pack(side="left", padx=5)
|
||||
ttk.Button(
|
||||
button_frame, text="测试数据库连接", command=self.test_db_connection
|
||||
).pack(side="left", padx=5)
|
||||
ttk.Button(button_frame, text="保存设置", command=self.save_settings).pack(
|
||||
side="left", padx=5
|
||||
)
|
||||
ttk.Button(button_frame, text="恢复默认", command=self.reset_defaults).pack(
|
||||
side="left", padx=5
|
||||
)
|
||||
|
||||
# 布局
|
||||
canvas.grid(row=0, column=0, sticky="nsew")
|
||||
@@ -82,12 +89,16 @@ class SettingsTab(ttk.Frame):
|
||||
# URL
|
||||
ttk.Label(group, text="ERP URL:").grid(row=0, column=0, sticky="w", pady=5)
|
||||
self.erp_url_var = tk.StringVar()
|
||||
ttk.Entry(group, textvariable=self.erp_url_var, width=50).grid(row=0, column=1, pady=5, sticky="ew")
|
||||
ttk.Entry(group, textvariable=self.erp_url_var, width=50).grid(
|
||||
row=0, column=1, pady=5, sticky="ew"
|
||||
)
|
||||
|
||||
# 用户名
|
||||
ttk.Label(group, text="用户名:").grid(row=1, column=0, sticky="w", pady=5)
|
||||
self.erp_username_var = tk.StringVar()
|
||||
ttk.Entry(group, textvariable=self.erp_username_var, width=50).grid(row=1, column=1, pady=5, sticky="ew")
|
||||
ttk.Entry(group, textvariable=self.erp_username_var, width=50).grid(
|
||||
row=1, column=1, pady=5, sticky="ew"
|
||||
)
|
||||
|
||||
# 密码
|
||||
ttk.Label(group, text="密码:").grid(row=2, column=0, sticky="w", pady=5)
|
||||
@@ -105,17 +116,23 @@ class SettingsTab(ttk.Frame):
|
||||
# 服务器
|
||||
ttk.Label(group, text="服务器:").grid(row=0, column=0, sticky="w", pady=5)
|
||||
self.db_server_var = tk.StringVar()
|
||||
ttk.Entry(group, textvariable=self.db_server_var, width=50).grid(row=0, column=1, pady=5, sticky="ew")
|
||||
ttk.Entry(group, textvariable=self.db_server_var, width=50).grid(
|
||||
row=0, column=1, pady=5, sticky="ew"
|
||||
)
|
||||
|
||||
# 数据库名
|
||||
ttk.Label(group, text="数据库:").grid(row=1, column=0, sticky="w", pady=5)
|
||||
self.db_name_var = tk.StringVar()
|
||||
ttk.Entry(group, textvariable=self.db_name_var, width=50).grid(row=1, column=1, pady=5, sticky="ew")
|
||||
ttk.Entry(group, textvariable=self.db_name_var, width=50).grid(
|
||||
row=1, column=1, pady=5, sticky="ew"
|
||||
)
|
||||
|
||||
# 用户名
|
||||
ttk.Label(group, text="用户名:").grid(row=2, column=0, sticky="w", pady=5)
|
||||
self.db_username_var = tk.StringVar()
|
||||
ttk.Entry(group, textvariable=self.db_username_var, width=50).grid(row=2, column=1, pady=5, sticky="ew")
|
||||
ttk.Entry(group, textvariable=self.db_username_var, width=50).grid(
|
||||
row=2, column=1, pady=5, sticky="ew"
|
||||
)
|
||||
|
||||
# 密码
|
||||
ttk.Label(group, text="密码:").grid(row=3, column=0, sticky="w", pady=5)
|
||||
@@ -131,13 +148,19 @@ class SettingsTab(ttk.Frame):
|
||||
group.grid(row=2, column=0, pady=10, padx=10, sticky="ew")
|
||||
|
||||
self.browser_headless_var = tk.BooleanVar()
|
||||
ttk.Checkbutton(group, text="无头模式 (不显示浏览器)", variable=self.browser_headless_var).grid(row=0, column=0, sticky="w", pady=5)
|
||||
ttk.Checkbutton(
|
||||
group, text="无头模式 (不显示浏览器)", variable=self.browser_headless_var
|
||||
).grid(row=0, column=0, sticky="w", pady=5)
|
||||
|
||||
self.browser_ignore_https_var = tk.BooleanVar()
|
||||
ttk.Checkbutton(group, text="忽略 HTTPS 错误", variable=self.browser_ignore_https_var).grid(row=1, column=0, sticky="w", pady=5)
|
||||
ttk.Checkbutton(
|
||||
group, text="忽略 HTTPS 错误", variable=self.browser_ignore_https_var
|
||||
).grid(row=1, column=0, sticky="w", pady=5)
|
||||
|
||||
self.browser_auto_close_var = tk.BooleanVar()
|
||||
ttk.Checkbutton(group, text="操作完成后自动关闭浏览器", variable=self.browser_auto_close_var).grid(row=2, column=0, sticky="w", pady=5)
|
||||
ttk.Checkbutton(
|
||||
group, text="操作完成后自动关闭浏览器", variable=self.browser_auto_close_var
|
||||
).grid(row=2, column=0, sticky="w", pady=5)
|
||||
|
||||
def _create_paths_group(self, parent):
|
||||
"""创建路径配置组"""
|
||||
@@ -152,14 +175,16 @@ class SettingsTab(ttk.Frame):
|
||||
group,
|
||||
label_text="",
|
||||
file_type="directory",
|
||||
initial_dir="D:/python/playwrite/data/"
|
||||
initial_dir="D:/python/playwrite/data/",
|
||||
)
|
||||
self.data_dir_selector.grid(row=1, column=0, columnspan=2, sticky="ew", pady=5)
|
||||
|
||||
# 默认输出文件
|
||||
ttk.Label(group, text="默认输出文件:").grid(row=2, column=0, sticky="w", pady=5)
|
||||
self.default_output_var = tk.StringVar()
|
||||
ttk.Entry(group, textvariable=self.default_output_var, width=40).grid(row=3, column=0, columnspan=2, sticky="ew", pady=5)
|
||||
ttk.Entry(group, textvariable=self.default_output_var, width=40).grid(
|
||||
row=3, column=0, columnspan=2, sticky="ew", pady=5
|
||||
)
|
||||
|
||||
group.columnconfigure(0, weight=1)
|
||||
|
||||
@@ -171,75 +196,85 @@ class SettingsTab(ttk.Frame):
|
||||
# 批次大小
|
||||
ttk.Label(group, text="批次大小:").grid(row=0, column=0, sticky="w", pady=5)
|
||||
self.batch_size_var = tk.IntVar(value=100)
|
||||
ttk.Spinbox(group, from_=10, to=500, textvariable=self.batch_size_var, width=10).grid(row=0, column=1, sticky="w", pady=5)
|
||||
ttk.Spinbox(
|
||||
group, from_=10, to=500, textvariable=self.batch_size_var, width=10
|
||||
).grid(row=0, column=1, sticky="w", pady=5)
|
||||
|
||||
# 详细日志
|
||||
self.verbose_var = tk.BooleanVar()
|
||||
ttk.Checkbutton(group, text="启用详细日志", variable=self.verbose_var).grid(row=1, column=0, columnspan=2, sticky="w", pady=5)
|
||||
ttk.Checkbutton(group, text="启用详细日志", variable=self.verbose_var).grid(
|
||||
row=1, column=0, columnspan=2, sticky="w", pady=5
|
||||
)
|
||||
|
||||
# 自动转换
|
||||
self.auto_convert_var = tk.BooleanVar()
|
||||
ttk.Checkbutton(group, text="自动转换 Excel 格式", variable=self.auto_convert_var).grid(row=2, column=0, columnspan=2, sticky="w", pady=5)
|
||||
ttk.Checkbutton(
|
||||
group, text="自动转换 Excel 格式", variable=self.auto_convert_var
|
||||
).grid(row=2, column=0, columnspan=2, sticky="w", pady=5)
|
||||
|
||||
# 合并批次
|
||||
self.merge_batches_var = tk.BooleanVar()
|
||||
ttk.Checkbutton(group, text="自动合并批次数据", variable=self.merge_batches_var).grid(row=3, column=0, columnspan=2, sticky="w", pady=5)
|
||||
ttk.Checkbutton(
|
||||
group, text="自动合并批次数据", variable=self.merge_batches_var
|
||||
).grid(row=3, column=0, columnspan=2, sticky="w", pady=5)
|
||||
|
||||
def load_settings(self):
|
||||
"""从配置加载设置到界面"""
|
||||
# ERP 设置
|
||||
self.erp_url_var.set(self.config.get('erp.url', ''))
|
||||
self.erp_username_var.set(self.config.get('erp.username', ''))
|
||||
self.erp_password_var.set(self.config.get('erp.password', ''))
|
||||
self.erp_url_var.set(self.config.get("erp.url", ""))
|
||||
self.erp_username_var.set(self.config.get("erp.username", ""))
|
||||
self.erp_password_var.set(self.config.get("erp.password", ""))
|
||||
|
||||
# 数据库设置
|
||||
self.db_server_var.set(self.config.get('database.server', ''))
|
||||
self.db_name_var.set(self.config.get('database.database', ''))
|
||||
self.db_username_var.set(self.config.get('database.username', ''))
|
||||
self.db_password_var.set(self.config.get('database.password', ''))
|
||||
self.db_server_var.set(self.config.get("database.server", ""))
|
||||
self.db_name_var.set(self.config.get("database.database", ""))
|
||||
self.db_username_var.set(self.config.get("database.username", ""))
|
||||
self.db_password_var.set(self.config.get("database.password", ""))
|
||||
|
||||
# 浏览器设置(已合并到 ERP 配置中)
|
||||
self.browser_headless_var.set(self.config.get('erp.headless', True))
|
||||
self.browser_ignore_https_var.set(self.config.get('erp.ignore_https_errors', True))
|
||||
self.browser_auto_close_var.set(self.config.get('erp.auto_close_browser', True))
|
||||
self.browser_headless_var.set(self.config.get("erp.headless", True))
|
||||
self.browser_ignore_https_var.set(
|
||||
self.config.get("erp.ignore_https_errors", True)
|
||||
)
|
||||
self.browser_auto_close_var.set(self.config.get("erp.auto_close_browser", True))
|
||||
|
||||
# 路径设置
|
||||
self.data_dir_selector.set(self.config.get('paths.data_dir', ''))
|
||||
self.default_output_var.set(self.config.get('paths.default_output', ''))
|
||||
self.data_dir_selector.set(self.config.get("paths.data_dir", ""))
|
||||
self.default_output_var.set(self.config.get("paths.default_output", ""))
|
||||
|
||||
# 处理设置
|
||||
self.batch_size_var.set(self.config.get('extraction.batch_size', 100))
|
||||
self.verbose_var.set(self.config.get('extraction.verbose', True))
|
||||
self.auto_convert_var.set(self.config.get('extraction.auto_convert', True))
|
||||
self.merge_batches_var.set(self.config.get('extraction.merge_batches', True))
|
||||
self.batch_size_var.set(self.config.get("extraction.batch_size", 100))
|
||||
self.verbose_var.set(self.config.get("extraction.verbose", True))
|
||||
self.auto_convert_var.set(self.config.get("extraction.auto_convert", True))
|
||||
self.merge_batches_var.set(self.config.get("extraction.merge_batches", True))
|
||||
|
||||
def save_settings(self):
|
||||
"""保存界面设置到配置"""
|
||||
# ERP 设置
|
||||
self.config.set('erp.url', self.erp_url_var.get())
|
||||
self.config.set('erp.username', self.erp_username_var.get())
|
||||
self.config.set('erp.password', self.erp_password_var.get())
|
||||
self.config.set("erp.url", self.erp_url_var.get())
|
||||
self.config.set("erp.username", self.erp_username_var.get())
|
||||
self.config.set("erp.password", self.erp_password_var.get())
|
||||
|
||||
# 数据库设置
|
||||
self.config.set('database.server', self.db_server_var.get())
|
||||
self.config.set('database.database', self.db_name_var.get())
|
||||
self.config.set('database.username', self.db_username_var.get())
|
||||
self.config.set('database.password', self.db_password_var.get())
|
||||
self.config.set("database.server", self.db_server_var.get())
|
||||
self.config.set("database.database", self.db_name_var.get())
|
||||
self.config.set("database.username", self.db_username_var.get())
|
||||
self.config.set("database.password", self.db_password_var.get())
|
||||
|
||||
# 浏览器设置(已合并到 ERP 配置中)
|
||||
self.config.set('erp.headless', self.browser_headless_var.get())
|
||||
self.config.set('erp.ignore_https_errors', self.browser_ignore_https_var.get())
|
||||
self.config.set('erp.auto_close_browser', self.browser_auto_close_var.get())
|
||||
self.config.set("erp.headless", self.browser_headless_var.get())
|
||||
self.config.set("erp.ignore_https_errors", self.browser_ignore_https_var.get())
|
||||
self.config.set("erp.auto_close_browser", self.browser_auto_close_var.get())
|
||||
|
||||
# 路径设置
|
||||
self.config.set('paths.data_dir', self.data_dir_selector.get())
|
||||
self.config.set('paths.default_output', self.default_output_var.get())
|
||||
self.config.set("paths.data_dir", self.data_dir_selector.get())
|
||||
self.config.set("paths.default_output", self.default_output_var.get())
|
||||
|
||||
# 处理设置
|
||||
self.config.set('extraction.batch_size', self.batch_size_var.get())
|
||||
self.config.set('extraction.verbose', self.verbose_var.get())
|
||||
self.config.set('extraction.auto_convert', self.auto_convert_var.get())
|
||||
self.config.set('extraction.merge_batches', self.merge_batches_var.get())
|
||||
self.config.set("extraction.batch_size", self.batch_size_var.get())
|
||||
self.config.set("extraction.verbose", self.verbose_var.get())
|
||||
self.config.set("extraction.auto_convert", self.auto_convert_var.get())
|
||||
self.config.set("extraction.merge_batches", self.merge_batches_var.get())
|
||||
|
||||
# 保存到文件
|
||||
if self.config.save():
|
||||
|
||||
@@ -24,7 +24,7 @@ class RealtimeOutput:
|
||||
"""写入文本"""
|
||||
if text:
|
||||
# 将文本按行分割,逐行回调
|
||||
lines = text.split('\n')
|
||||
lines = text.split("\n")
|
||||
for line in lines:
|
||||
if line: # 忽略空行(由 split 产生)
|
||||
self.callback(line)
|
||||
|
||||
Reference in New Issue
Block a user