【办公类-109-05】20250923插班生圆形挂牌卡片改良01:一人2个圆牌(接送卡被子卡床卡入园卡_word编辑单面)

📅 2026/7/21 18:42:43
【办公类-109-05】20250923插班生圆形挂牌卡片改良01:一人2个圆牌(接送卡被子卡床卡入园卡_word编辑单面)
背景需求10天前15号学生转学了空出一个学额20250923招生老师通知插班生来了我打印了圆牌和方牌发现要打印三张但只要其中一小块。塑封塑封裁剪全套资料打印我发现插班生单独打印圆牌和卡牌有点浪费纸一个一张只要其中一小块。因为圆牌需要两个被子牌接送牌我暂时先将圆牌做一人两块。这样插班生只要打印1张纸共3个人每人两个圆牌# -*- coding:utf-8 -*- 制作被子圆牌word合并多个班级-凑满整夜如托班20人凑满6*4页、中班30人凑满5页不存在的名字都是张三占位用以后有插班生就可以自己修改名字了 预先再word里面页面背景-纹理插入A4大小的图片背景有6个园 一个人两个牌子被子圆牌、接送牌 deepseek豆包 阿夏 20250923 修改每个名字制作2个相同的挂牌一页3个名字*2 import os import pandas as pd from docx import Document from docx.shared import Cm from docx.enum.text import WD_PARAGRAPH_ALIGNMENT from docx.shared import Pt # 用于设置字体大小 from docx.oxml.ns import qn # 用于设置中文字体 from pypinyin import pinyin, Style from docx.enum.table import WD_CELL_VERTICAL_ALIGNMENT from docx.shared import RGBColor # 用于设置字体颜色 import shutil import time import re # 1. 核心配置仅修改此部分路径 base_dir rc:\Users\jg2yXRZ\OneDrive\桌面\20250923文本框可编辑全校被子挂牌 excel_file os.path.join(base_dir, 班级信息表.xlsx) # Excel数据路径 template_word os.path.join(base_dir, 挂牌.docx) # 现有3行2列表格模板 output_dir os.path.join(base_dir, 零时文件夹) # 结果保存目录 editable_dir os.path.join(base_dir, 20250923圆形挂牌word编辑) ) # 新建的可编辑文件夹 os.makedirs(output_dir, exist_okTrue) # 创建结果目录 os.makedirs(editable_dir, exist_okTrue) # 创建可编辑文件夹 # 每个名字制作的份数一个名字两张卡连在一起的 COPIES_PER_NAME 2 # 定义各年级的固定人数现在每个名字做2个所以实际人数减半 GRADE_SIZES { 托: 24*COPIES_PER_NAME, # 托班20人凑满4张 小: 30*COPIES_PER_NAME, # 小班25人5张 中: 30*COPIES_PER_NAME, # 中班30人5张 大: 35*COPIES_PER_NAME # 大班35人6张 } # 2. Excel数据读取与拼音生成保留核心逻辑 def read_excel_and_generate_pinyin(file_path): 读取Excel数据生成带声调的姓名拼音返回{班级名: 学生数据列表} # 特殊姓氏读音校正确保拼音准确性 SPECIAL_SURNAMES { 乐: Yuè, 单: Shàn, 解: Xiè, 查: Zhā, 盖: Gě, 仇: Qiú, 种: Chóng, 朴: Piáo, 翟: Zhái, 区: Ōu, 繁: Pó, 覃: Qín, 召: Shào, 华: Huà, 纪: Jǐ, 曾: Zēng, 缪: Miào, 员: Yùn, 车: Chē, 过: Guō, 尉: Yù, 万: Wàn } COMPOUND_SURNAMES { 欧阳: Ōu yáng, 上官: Shàng guān, 皇甫: Huáng fǔ, 尉迟: Yù chí, 万俟: Mò qí, 长孙: Zhǎng sūn, 司徒: Sī tú, 司空: Sī kōng, 司徒: Sī tú, 诸葛: Zhū gě, 东方: Dōng fāng, 独孤: Dú gū, 慕容: Mù róng, 宇文: Yǔ wén } def correct_name_pinyin(name): 生成带声调的姓名拼音姓氏校正 if not name or not isinstance(name, str): return name name.strip() # 优先处理复姓 for compound_surn in COMPOUND_SURNAMES: if name.startswith(compound_surn): surn_pinyin COMPOUND_SURNAMES[compound_surn] given_name name[len(compound_surn):] given_pinyin .join([p[0] for p in pinyin(given_name, styleStyle.TONE)]) return f{surn_pinyin} {given_pinyin} # 处理单姓多音字 first_char name[0] if first_char in SPECIAL_SURNAMES: surn_pinyin SPECIAL_SURNAMES[first_char] given_name name[1:] given_pinyin .join([p[0] for p in pinyin(given_name, styleStyle.TONE)]) return f{surn_pinyin} {given_pinyin} # 普通姓名拼音 return .join([p[0] for p in pinyin(name, styleStyle.TONE)]) try: # 读取所有工作表 excel pd.ExcelFile(file_path) sheet_names excel.sheet_names class_data {} for sheet_name in sheet_names: # 跳过非班级工作表如果有的话 if not any(keyword in sheet_name for keyword in [班, class, Class]): continue df pd.read_excel(file_path, sheet_namesheet_name, usecolsrange(5), headerNone, dtype{0: str}) df df.iloc[1:] # 跳过第1行标题 student_list [] for idx, row in df.iterrows(): # 过滤无效数据学号为空、姓名为空 raw_id str(row[0]).strip() if pd.notna(row[0]) else name str(row[3]).strip() if pd.notna(row[3]) else if not raw_id.isdigit() or not name: # 如果没有学号没有名字 name张三 elif not name: # 如果有学号没有名字 name张三 # 提取核心信息 student_info { campus: XXX幼- (str(row[1]).strip() if pd.notna(row[1]) else ), # 园区 class_name: str(row[2]).strip() if pd.notna(row[2]) else , # 班级 student_id: f{int(raw_id)}号, # 学号如1号 name: name, # 姓名 name_pinyin: correct_name_pinyin(name) # 带声调拼音 } student_list.append(student_info) if student_list: print(f读取[{sheet_name}]{len(student_list)}个有效学生) class_data[sheet_name] student_list else: print(f工作表[{sheet_name}]无有效学生数据) return class_data except Exception as e: print(fExcel读取失败{str(e)}) return {} def get_grade_size(class_name): 根据班级名称获取对应的固定人数 # 提取班级名称中的年级信息 if 托 in class_name: return GRADE_SIZES[托] elif 小 in class_name: return GRADE_SIZES[小] elif 中 in class_name: return GRADE_SIZES[中] elif 大 in class_name: return GRADE_SIZES[大] else: # 默认返回小班人数 return GRADE_SIZES[小] def expand_student_list(student_list, target_size, class_name): 扩展学生列表到目标大小用空学生填充 # 因为每个名字要做2份所以实际需要的名字数量是目标大小的一半 name_count_needed target_size // COPIES_PER_NAME if len(student_list) name_count_needed: return student_list[:name_count_needed] # 如果实际学生多于目标截取前name_count_needed个 # 扩展学生列表 expanded_list student_list.copy() # 获取园区和班级名称从第一个有效学生获取 campus student_list[0][campus] if student_list else XXX幼 class_name_full student_list[0][class_name] if student_list else class_name # 添加空学生 for i in range(len(student_list), name_count_needed): empty_student { campus: campus, class_name: class_name_full, student_id: f{i1}号, name: 张三, # 空姓名 name_pinyin: Zhang San # 空拼音 } expanded_list.append(empty_student) return expanded_list def duplicate_students_for_copies(student_list): 为每个学生复制COPIES_PER_NAME份 duplicated_list [] for student in student_list: for i in range(COPIES_PER_NAME): duplicated_list.append(student.copy()) return duplicated_list # 3. 写入Word表格3行2列单个单元格含5种信息回车 def write_student_to_word(student_list, template_path, output_path, class_name): 将学生数据写入Word模板的3行2列表格 单个单元格内容园区\n班级\n学号\n拼音\n姓名按此顺序换行 字体微软雅黑拼音大小10pt姓名大小20pt其他信息14pt 颜色班级、拼音、姓名为灰色学号为黑色 # 根据班级类型确定目标人数 target_size get_grade_size(class_name) print(f班级 {class_name} 目标人数: {target_size}, 实际人数: {len(student_list)}) # 扩展学生列表到目标大小注意现在扩展的是名字数量不是总挂牌数 expanded_student_list expand_student_list(student_list, target_size, class_name) # 为每个名字复制COPIES_PER_NAME份 final_student_list duplicate_students_for_copies(expanded_student_list) print(f复制后总挂牌数: {len(final_student_list)} (每个名字{COPIES_PER_NAME}份)) # 按6个学生一组分割3行2列表格可容纳6个学生 student_groups [final_student_list[i:i6] for i in range(0, len(final_student_list), 6)] generated_files [] # 存储生成的文件路径 for group_idx, group in enumerate(student_groups, 1): # 复制模板文件避免修改原模板 temp_template os.path.join(output_path, ftemp_{class_name}_{group_idx}.docx) shutil.copy2(template_path, temp_template) # 打开复制后的模板操作表格 doc Document(temp_template) # 获取模板中的第一个表格需确保挂牌2.docx的第一个表格是3行2列) if not doc.tables: print(f模板{template_path}中未找到表格跳过此组) os.remove(temp_template) continue table doc.tables[0] # 清除表格原有内容 for row in table.rows: for cell in row.cells: for para in cell.paragraphs: p para._element p.getparent().remove(p) cell._element.clear() # 完全清空单元格 # 定义字体样式 def set_paragraph_style(para, text, font_size, is_boldFalse, is_grayTrue, underlineFalse): 设置段落的字体、大小、对齐居中和颜色 if not text: # 跳过空文本 return run para.add_run(text) # 设置中文字体为微软雅黑 run.font.name 微软雅黑 run.element.rPr.rFonts.set(qn(w:eastAsia), 微软雅黑) run.font.size Pt(font_size) run.font.bold is_bold run.font.underline underline # 设置下划线 # 设置颜色灰色或黑色 if is_gray: run.font.color.rgb RGBColor(169, 169, 169) # DarkGray else: run.font.color.rgb RGBColor(0, 0, 0) # 黑色 para.alignment WD_PARAGRAPH_ALIGNMENT.CENTER # 文字居中 # 逐个单元格写入学生信息3行2列共6个单元格 cell_index 0 # 单元格索引0-5对应3行2列的6个单元格 for row_idx, row in enumerate(table.rows): for cell_idx, cell in enumerate(row.cells): if cell_index len(group): break # 学生不足6个时空单元格跳过 # 添加这行代码设置单元格垂直居中 cell.vertical_alignment WD_CELL_VERTICAL_ALIGNMENT.CENTER # 当前学生的5种信息按园区→班级→学号→拼音→姓名顺序) student group[cell_index] info_lines [ student[campus], # 第1行园区 student[class_name], # 第2行班级 student[student_id], # 第3行学号 student[name_pinyin], # 第4行拼音10pt student[name] # 第5行姓名20pt加粗 ] # 向单元格写入内容每行单独设置字体大小和颜色 for line_idx, line in enumerate(info_lines): if line: # 只写入非空内容 para cell.add_paragraph() if line_idx 0: # 园区16pt set_paragraph_style(para, line, font_size16, is_boldTrue, is_grayTrue) para.paragraph_format.line_spacing Pt(30) elif line_idx 1: # 班级30pt set_paragraph_style(para, line, font_size30, is_boldTrue, is_grayTrue) para.paragraph_format.line_spacing Pt(40) elif line_idx 2: # 学号50pt set_paragraph_style(para, line, font_size50, is_boldTrue, is_grayFalse, underlineTrue) para.paragraph_format.line_spacing Pt(65) elif line_idx 3: # 拼音行10pt set_paragraph_style(para, line, font_size10, is_boldTrue, is_grayTrue) para.paragraph_format.line_spacing Pt(25) elif line_idx 4: # 姓名行30pt加粗 set_paragraph_style(para, line, font_size30, is_boldTrue, is_grayTrue) para.paragraph_format.line_spacing Pt(35) cell_index 1 # 保存当前组的Word文件 final_word_path os.path.join(output_path, f{class_name}_挂牌_{group_idx:02d}.docx) doc.save(final_word_path) generated_files.append(final_word_path) # 添加到生成文件列表 os.remove(temp_template) # 删除临时模板 print(f生成第{group_idx}组Word{os.path.basename(final_word_path)}) return generated_files def merge_docx_files_with_win32(template_file, files_list, output_filename): 使用win32com合并Word文档去掉换页符 if not files_list: print(没有文件可合并) return word None doc None try: # 启动Word应用程序 import win32com.client as win32 word win32.Dispatch(Word.Application) word.Visible False # 不显示Word界面 word.DisplayAlerts False # 不显示警告 # 复制模板文件到输出文件 shutil.copy2(template_file, output_filename) # 打开输出文件 doc word.Documents.Open(output_filename) # 将光标移动到文档末尾 selection word.Selection selection.EndKey(6) # wdStory 6, 移动到文档末尾 # 逐个插入其他文件的内容跳过第一个文件因为已经是模板 for i, file_path in enumerate(files_list): if i 0: # 跳过模板文件本身 continue print(f正在插入文件: {os.path.basename(file_path)}) # 插入文件内容 selection.InsertFile(file_path) # 移动到文档末尾准备插入下一个文件 selection.EndKey(6) time.sleep(1) # 添加延时确保Word有足够时间处理 # 保存合并后的文档 doc.Save() print(f已合并所有文档到{output_filename}) except Exception as e: print(f合并文档时出错{e}) # 尝试使用备选方法 try: print(尝试使用备选合并方法...) merge_docx_files_simple(template_file, files_list, output_filename) except Exception as e2: print(f备选方法也失败{e2}) finally: # 确保正确关闭文档和Word应用程序 try: if doc: doc.Close(False) time.sleep(0.5) except: pass try: if word: word.Quit() time.sleep(0.5) except: pass def merge_docx_files_simple(template_file, files_list, output_filename): 简单的合并方法复制模板然后逐个追加内容 # 复制模板文件 shutil.copy2(template_file, output_filename) # 打开模板文件 main_doc Document(output_filename) # 逐个添加其他文件的内容 for i, file_path in enumerate(files_list): if i 0: # 跳过模板文件本身 continue # 添加分页符 if i 1: # 从第二个文件开始添加分页符 main_doc.add_page_break() # 读取要添加的文件 sub_doc Document(file_path) # 复制表格内容 for table in sub_doc.tables: # 创建新表格 new_table main_doc.add_table(rowslen(table.rows), colslen(table.columns)) # 复制内容和格式 for row_idx, row in enumerate(table.rows): for col_idx, cell in enumerate(row.cells): new_cell new_table.cell(row_idx, col_idx) # 清空新单元格 for paragraph in new_cell.paragraphs: p paragraph._element p.getparent().remove(p) new_cell._element.clear() # 复制内容 for paragraph in cell.paragraphs: new_para new_cell.add_paragraph() for run in paragraph.runs: new_run new_para.add_run(run.text) # 复制字体样式 new_run.font.name run.font.name new_run.font.size run.font.size new_run.font.bold run.font.bold new_run.font.underline run.font.underline if hasattr(run.font.color, rgb): new_run.font.color.rgb run.font.color.rgb # 保存合并后的文档 main_doc.save(output_filename) print(f使用简单方法合并完成{output_filename}) def cleanup_directory(directory_path): 清理目录 try: if os.path.exists(directory_path): shutil.rmtree(directory_path) print(f已删除目录: {directory_path}) except Exception as e: print(f删除目录时出错: {e}) # 4. 主程序处理所有班级 if __name__ __main__: # 步骤1读取Excel数据含拼音生成 print( 开始读取Excel数据 ) class_data read_excel_and_generate_pinyin(excel_file) if not class_data: print(无有效学生数据程序退出) exit() # 为每个班级创建单独的输出目录 class_output_dirs {} for class_name in class_data.keys(): class_dir os.path.join(output_dir, class_name) os.makedirs(class_dir, exist_okTrue) class_output_dirs[class_name] class_dir # 步骤2遍历每个班级写入Word表格 all_merged_files [] for sheet_name, student_list in class_data.items(): # 获取班级名称取第一个学生的班级信息 class_name student_list[0][class_name] if student_list else sheet_name print(f\n 处理班级{class_name} ) # 步骤3写入Word3行2列表格6个学生一组 generated_files write_student_to_word( student_liststudent_list, template_pathtemplate_word, output_pathclass_output_dirs[sheet_name], class_nameclass_name ) # 步骤4为每个班级合并所有生成的docx文件 if generated_files: # 使用第一个文件作为模板 template_file generated_files[0] # 合并后的文件保存到可编辑文件夹 merged_filename os.path.join(editable_dir, f20250923_{class_name}_圆形牌_word编辑接送被子床卡一人两块圆牌.docx) print(f开始合并{class_name}的文档...) try: merge_docx_files_with_win32(template_file, generated_files, merged_filename) all_merged_files.append(merged_filename) print(f{class_name}合并完成) except Exception as e: print(f{class_name}的win32合并失败尝试简单方法: {e}) try: merge_docx_files_simple(template_file, generated_files, merged_filename) all_merged_files.append(merged_filename) print(f{class_name}使用简单方法合并完成) except Exception as e2: print(f{class_name}的所有合并方法都失败: {e2}) # 处理完一个班级后稍作延时释放资源 time.sleep(2) # 步骤5清理临时文件 print(\n 清理临时文件 ) cleanup_directory(output_dir) print(f\n 所有处理完成 ) print(f合并后的文件已保存到{editable_dir}) print(f生成的合并文件) for file in all_merged_files: print(f - {os.path.basename(file)})后续我还要做一个插班生专用备份一页包括两个圆牌、两个竖版长方、两个竖版长方卡便于教师只修改这个打印在一页上