【办公类-109-06】20250916圆形挂牌卡片15CM手工纸+动物头像+拼音表+word单面编辑

【办公类-109-06】20250916圆形挂牌卡片15CM手工纸+动物头像+拼音表+word单面编辑 背景需求 因为园所只有五种浅色A4纸(粉红、黄、浅绿、浅蓝、白色),分园都是一共4个班级,正好一人一种颜色,但是总部中班、大班都是6个,所以一个班级没法用到不同颜色纸,因此校级家长讲座接待家长签到时,我看到组长班就是自己设计打印了15CM手工纸的被子挂牌和挂牌名字卡(用了网络图片模版框自己打印的)小班组长还在纸上插入了小动物图片。这样好处是,1、直接打印15CM手工纸,不用裁剪,不用剪圆,直接塑封。2、有小动物更符合低年龄幼儿的兴趣。设计思考:我完全可以做15CM小动物的圆牌卡(换一个自定义尺寸,插入小动物图片)一、图像获取通义万相:万相2.1专业 萌系卡通纯白色背景,卡通简笔画,无颜色,一只羚羊的头像,正面头像,开口大笑,简单笔画,卡通,加粗黑白轮廓线,无色,幼儿插图,线条画,黑白漫画线条艺术:,线描,粗轮廓,清晰的线条,矢量线。简单,大,通义万相下载动物头像二、图像处理用程序把彩色图片转黑白蓝图,再切边由于动物头像轮廓线没有封闭,所以很多图像没有实现内部填充白色(还是透明的)手动挑选,适合的动物头像为了节省墨水,黑色多的图案删除全选,看看透明部分,删除脸部是透明的图片,我需要的是脸部是白色的图片因为学额最大是35人,圆牌是一页6张*6=最多36人,方卡片是一页9张*4=最多36人。所以我留了36种动物头像他们的长宽不同。三、模版制作在PPT里修改背景图片的样式,把打孔圆形上移,做一个2CM的动物图片位子灰线框原来代码的参数(9CM大小,一页6个圆圈)在此基础上,增大文字大小https://blog.csdn.net/reasonsummer/article/details/151996771?spm=1011.2415.3001.5331https://blog.csdn.net/reasonsummer/article/details/151996771?spm=1011.2415.3001.5331手动修改字体大小、字间距四、代码修改无动物图的15CM手工纸打印效果(无拼音表)# -*- coding:utf-8 -*- ''' 制作被子圆牌(word合并)多个班级-凑满整夜,如托班20人,凑满6*4页、中班30人,凑满5页,不存在的名字都是"张三"占位用,以后有插班生就可以自己修改名字了 预先再word里面页面背景-纹理,插入A4大小的图片背景(有6个园), 一个人两个牌子(被子圆牌、接送牌) 15CM手工纸大小(单面没有动物) 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 = r'c:\Users\jg2yXRZ\OneDrive\桌面\20250915方形被子牌15CM手工纸' excel_file = os.path.join(base_dir, "班级信息表.xlsx") # Excel数据路径 template_word = os.path.join(base_dir, "15厘米手工纸模版(无动物).docx") # 现有3行2列表格模板 output_dir = os.path.join(base_dir, "零时文件夹") # 结果保存目录 editable_dir = os.path.join(base_dir, "20251005手工纸15CM无动物圆形挂牌(word编辑)" ) # 新建的可编辑文件夹 os.makedirs(output_dir, exist_ok=True) # 创建结果目录 os.makedirs(editable_dir, exist_ok=True) # 创建可编辑文件夹 # 每个名字制作的份数(一个名字两张卡,连在一起的 COPIES_PER_NAME = 1 # 定义各年级的固定人数(现在每个名字做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, style=Style.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, style=Style.TONE)]) return f"{surn_pinyin} {given_pinyin}" # 普通姓名拼音 return ' '.join([p[0] for p in pinyin(name, style=Style.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_name=sheet_name, usecols=range(5), header=None, 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": "春花幼儿园(" + (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(f"Excel读取失败:{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 "春花幼儿园" 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"{i+1}号", "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}份)") # 按1个学生一组分割只有一个孩子 student_groups = [final_student_list[i:i+1] for i in range(0, len(final_student_list), 1)] generated_files = [] # 存储生成的文件路径 for group_idx, group in enumerate(student_groups, 1): # 复制模板文件(避免修改原模板) temp_template = os.path.join(output_path, f"temp_{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_bold=False, is_gray=True, underline=False): """设置段落的字体、大小、对齐(居中)和颜色""" 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_size=26, is_bold=True, is_gray=True) para.paragraph_format.line_spacing = Pt(100) elif line_idx == 1: # 班级:30pt set_paragraph_style(para, line, font_size=36, is_bold=True, is_gray=True) para.paragraph_format.line_spacing = Pt(40) elif line_idx == 2: # 学号:50pt set_paragraph_style(para, line, font_size=72, is_bold=True, is_gray=False, underline=True) para.paragraph_format.line_spacing = Pt(90) elif line_idx == 3: # 拼音行:10pt set_paragraph_style(para, line, font_size=14, is_bold=True, is_gray=True) para.paragraph_format.line_spacing = Pt(25) elif line_idx == 4: # 姓名行:30pt,加粗 set_paragraph_style(para, line, font_size=40, is_bold=True, is_gray=True) para.paragraph_format.line_spacing = Pt(50) 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(rows=len(table.rows), cols=len(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_ok=True) 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:写入Word(3行2列表格,6个学生一组) generated_files = write_student_to_word( student_list=student_list, template_path=template_word, output_path=class_output_dirs[sheet_name], class_name=class_name ) # 步骤4:为每个班级合并所有生成的docx文件 if generated_files: # 使用第一个文件作为模板 template_file = generated_files[0] # 合并后的文件保存到可编辑文件夹 merged_filename = os.path.join(editable_dir, f"20250923_{class_name}_15CM圆形牌(无动物)_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)}")我在模版里把边框取消无动物图的15CM手工纸打印效果(有拼音表)# -*- coding:utf-8 -*- ''' 制作被子圆牌(word合并)多个班级-凑满整夜,如托班20人,凑满6*4页、中班30人,凑满5页,不存在的名字都是"张三"占位用,以后有插班生就可以自己修改名字了 预先再word里面页面背景-纹理,插入A4大小的图片背景(有6个园), 一个人两个牌子(被子圆牌、接送牌) 15CM手工纸大小(单面没有动物) 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 import win32com.client as