from concurrent.futures import ThreadPoolExecutor import os import re import sys import time from bs4 import BeautifulSoup import threading import tkinter as tk from tkinter import messagebox def get_app_dir(): if getattr(sys, 'frozen', False): return os.path.dirname(os.path.abspath(sys.executable)) return os.path.dirname(os.path.abspath(__file__)) # 定义常量 i = 1 j = 0 scale_count = 0 # 489300 n = 700 m2cm = 100 Segments = int((n*m2cm//2)/5000) + 1 APP_DIR = get_app_dir() SOURCE_HTML_PATH = os.path.join(APP_DIR, 'code_ims') OUTPUT_FOLDER = os.path.join(APP_DIR, 'QRTape') # 修改二维码的SVG值 def data_scaling(svg_content): replacements = { "40": "7.092198581560284", "80": "14.18439716312057", "120": "21.27659574468085", "160": "28.36879432624113", "200": "35.46099290780142", "240": "42.55319148936170", "280": "49.64539007092199", "320": "56.73758865248227", "400": "56.73758865248227", "420": "56.73758865248227" } # 替换函数 def replace_text(text): for old, new in replacements.items(): text = re.sub(r'\b' + re.escape(old) + r'\b', new, text) return text return replace_text(svg_content) # 将svg与二维码的起点对齐 subtract_value = 7.092198581560284 # 定义正则表达式模式,用于匹配 x 和 y 属性 pattern = re.compile(r'(x|y)="\d+(\.\d+)?"') # 定义一个替换函数,用于减去指定值 def subtract_coords(match): attr = match.group(1) value = float(match.group(0).split('=')[1].strip('"')) new_value = value - subtract_value return f'{attr}="{new_value:.4f}"' # 获取二维码的 SVG 数据 def get_qr_code_svg(html_file): with open(html_file, 'r', encoding='utf-8') as file: html_content = file.read() soup = BeautifulSoup(html_content, 'html.parser') for text_tag in soup.find_all('text'): text_tag.decompose() svgs = soup.find_all('svg') all_svg_content = "" for svg in svgs: all_svg_content += str(svg) + "\n" return all_svg_content def save_svg_to_html(svg_content, output_path, segment, length): """将 SVG 内容保存到 HTML 文件中""" # if segment == Segments: tick_mark_html = "" tick_interval = 189.1252955082742 # 每隔189像素一个刻度线 total_width = n*(n-1)*75.65011820330969 # outer-container 的宽度 outer_container_width = length*56.73758865248227 + (length - 1)*18.91252955082742 + 18.91252955082742 for i in range(0, int(total_width // tick_interval) + 1): scale_line_left = i * 189.1252955082742 if scale_line_left <= outer_container_width: tick_position = i * tick_interval # 刻度线 tick_mark_html += f'
\n' tick_label = int(int(require_length_start * m2cm) + 5 * i + 10000 * (segment - 1)) # 刻度数 tick_mark_html += f'
{tick_label}
\n' html_content = f"""
{svg_content}
{tick_mark_html}
""" with open(output_path, 'w', encoding='utf-8') as file: file.write(html_content) print(f"html 文件已保存至" + output_path) def process_segment(segment_num, result_dict, output_path): """处理单个段的二维码生成""" all_scaled_svg_content = "" count = 0 segment_size = len(result_dict[f"result_{segment_num}_1"]) for k in range(1, 3): # 处理两排 for j in range(segment_size): html_file = os.path.join(SOURCE_HTML_PATH, f'im{result_dict[f"result_{segment_num}_{k}"][j]}.html') svg_contents = get_qr_code_svg(html_file) scaled_svg_content = data_scaling(svg_contents) updated_svg_content = pattern.sub(subtract_coords, scaled_svg_content) all_scaled_svg_content += updated_svg_content count += 1 save_svg_to_html(all_scaled_svg_content, output_path, segment_num, len(result_dict[f"result_{segment_num}_{k}"])) print(f"码带段{segment_num}处理完成") def create_html_with_qrcodes(output_path, segments = Segments): try: # result_all代表700个码生成的码带的所有ID:1,2,1,3,...,699,700; result_all = [] # result_all_1代表result_all的第一排码ID result_all_1 = [] # result_all_2代表result_all的第二排码ID result_all_2 = [] result_dict = {} for i in range(1, segments + 1): # result_dict表示一个 包含多个列表的字典。例如: # "result_1_1": [1, 2, 3], # 第一段,第一排码 # "result_1_2": [7, 8, 9], # 第一段,第二排码 # "result_2_1": [4, 5], # 第二段,第一排码 # "result_2_2": [10, 11] # 第二段,第二排码 result_dict[f"result_{i}_1"] = [] result_dict[f"result_{i}_2"] = [] global scale_count for i in range(1, n+1): for j in range(i+1, n+1): result_all.append(i) result_all.append(j) for i in range(0, len(result_all)): if(i < n*(n-1) // 2): result_all_1.append(result_all[i]) else: result_all_2.append(result_all[i]) for i in range(0, segments): for j in range(int(require_length_start * m2cm // 2), 5000 + int(require_length_start * m2cm // 2)): j = j + 5000 * i if(j >= len(result_all_1) or j >= require_length_end * m2cm // 2): break result_dict[f"result_{i+1}_1"].append(result_all_1[j]) result_dict[f"result_{i+1}_2"].append(result_all_2[j]) # for i in range(1, segments + 1): # print(f"第{i}段第1排:" + f" length:" + str(len(result_dict[f"result_{i}_1"]))) # print(f"第{i}段第2排:" + f" length:" + str(len(result_dict[f"result_{i}_2"]))) start_time = time.perf_counter() print("开始处理,请稍等...") with ThreadPoolExecutor(max_workers=min(segments, os.cpu_count() * 2)) as executor: futures = [] for i in range(1, segments + 1): if len(result_dict[f"result_{i}_1"]) == 0 : continue future = executor.submit( process_segment, i, result_dict, output_path ) futures.append(future) # 等待所有任务完成 for future in futures: future.result() print("所有码带段处理完成") end_time = time.perf_counter() elapsed_time = (end_time - start_time) * 1000 print(f"段落处理执行耗时: {elapsed_time:.4f} 毫秒") except Exception as e: # 捕获生成码带时的错误 print(f"生成码带时发生错误: {str(e)}") def run_gui(): def on_generate(): try: global require_length_start, require_length_end # 获取用户输入的码带长度 require_length_start = float(entry_start.get()) require_length_end = float(entry_end.get()) if len(str(require_length_start).split('.')[1]) > 2 or len(str(require_length_end).split('.')[1]) > 2: raise ValueError("最多保留小数点后两位,请重新输入!") if require_length_start < 0: raise ValueError("码带起点必须大于或等于零") if require_length_end < 0: raise ValueError("码带终点必须大于零") if require_length_end >= 4800: raise ValueError("码带终点必须小于或等于4800") if require_length_end < require_length_start: raise ValueError("码带终点必须大于起点") output_html_path = os.path.normpath(OUTPUT_FOLDER) if not os.path.exists(output_html_path): os.makedirs(output_html_path) file_name = f"{require_length_start}_{require_length_end}.html" OUTPUT_HTML_PATH = os.path.normpath(os.path.join(output_html_path, file_name)) if not os.path.exists(output_html_path): messagebox.showerror("错误", f"输出文件夹路径不存在: {output_html_path}") return if not os.path.exists(SOURCE_HTML_PATH): messagebox.showerror("错误", f"请将 code_ims 文件夹放在程序同一目录下:\n{SOURCE_HTML_PATH}") return def show_open_folder_button(): last_output_dir["path"] = output_html_path if not btn_open.winfo_ismapped(): btn_open.pack(pady=5) def show_loading_window(): loading_window = tk.Toplevel(root) loading_window.title("生成码带") loading_window.geometry("300x100") label = tk.Label(loading_window, text="正在生成码带中,请稍等......", font=("Arial", 14)) label.pack(pady=30) loading_window.update_idletasks() create_html_with_qrcodes(OUTPUT_HTML_PATH) loading_window.destroy() root.after(0, show_open_folder_button) messagebox.showinfo("成功", "码带生成成功,文件已保存至程序目录下QRTape文件夹") threading.Thread(target=show_loading_window, daemon=True).start() except ValueError as e: # 如果输入的长度无效,弹出错误提示框 messagebox.showerror("错误", f"输入的码带长度无效: {str(e)}") except Exception as e: # 其他异常处理 messagebox.showerror("错误", f"发生错误: {str(e)}") last_output_dir = {"path": None} def on_open_folder(): folder_path = last_output_dir["path"] if not folder_path or not os.path.exists(folder_path): messagebox.showerror("错误", "还没有生成码带,或文件夹不存在") return os.startfile(folder_path) # 创建主窗口 root = tk.Tk() root.title("新码带生成器") root.geometry("300x300") label_start = tk.Label(root, text="请输入码带起点(m):", font=("Arial", 14)) label_start.pack(padx=10, pady=5) # 输入框:输入码带起点 entry_start = tk.Entry(root, width=10, font=("Arial", 14)) entry_start.pack(padx=10, pady=5) # 标签:提示输入码带终点 label_end = tk.Label(root, text="请输入码带终点(m):", font=("Arial", 14)) label_end.pack(padx=10, pady=5) # 输入框:输入码带终点 entry_end = tk.Entry(root, width=10, font=("Arial", 14)) entry_end.pack(padx=10, pady=5) # 按钮:点击生成码带 btn_generate = tk.Button(root, text="Click to Generate", command=on_generate, font=("Arial", 15)) btn_generate.pack(pady=20) btn_open = tk.Button(root, text="打开生成文件夹", command=on_open_folder, font=("Arial", 14)) # 运行GUI root.mainloop() if __name__ == '__main__': run_gui()