背景需求:
电脑坏了,需要重做《长方形挂牌》的模版、代码库
根据最新一份长方形挂牌制作
【办公类-109-09】20260228挂牌长方形挂牌卡片(只有word编辑单面,添加有效日期)(85*54mm横板和54*85mm竖版,一页9张)https://mp.csdn.net/mp_blog/creation/editor/158264646
二、代码库制作
把CSDN的代码复制进去
修改参数
把成果文件夹路径制作
把包含所有幼儿信息的xlsx放入,添加序号(便于排序)
还需要做一个“横版”模版
![]()
改名
还需要做一个“竖版”模版
改名
三、模版制作
参考这个文档里面的Word模版制作流程
【办公类-109-04】20250807托小班新生的卡套长方形挂牌制作Python3.11.5 32(85*54mm横板和54*85mm竖版,一页9张)https://mp.csdn.net/mp_blog/creation/editor/150009366
(一)竖版:3*3,页边距:2和2.4
为了让表格能够在画布中间
最后一行,变成段间距0.7
为了防止生成的内容下移到下一页,最关键的设置
‘
’
(二)横板:3*3 页边距:2和2.35,部分中线隐藏
为了防止生成的内容下移到下一页,最关键的设置
模版都做完了。
四、运行代码
''' 1.制作20250901托小班长方形卡片,横版85*54mm和竖版54*85mm,一页9张单面 2.添加姓名拼音显示功能(拼音在姓名上方,带声调) 3.python 3.7.8 3. 横版布局:单列显示园区和班级,双列显示拼音、姓名和学号 4. 竖版布局:单列显示所有信息 5. 添加照片占位符:2.5cm宽*3.5cm高,黑色框线,中间写"贴照片" 6、生成全部班级或生成指定班级(中1-中6班) 7、添加日期2026.9-2027.6 豆包,deepseek,阿夏 20260901 ''' # ========== 记录程序开始时间 ========== import time start_time = time.time() # ========== 创建照片占位符图片 ========== import os from copy import deepcopy from PIL import Image, ImageDraw, ImageFont import shutil import pandas as pd from pypinyin import pinyin, Style from docx import Document from docx.shared import Cm, Pt from docx.enum.text import WD_PARAGRAPH_ALIGNMENT from docx.oxml.ns import qn from docx.oxml import OxmlElement import glob from docx.oxml.shared import qn as oxml_qn from docx.oxml import parse_xml from docx.enum.text import WD_BREAK from docx.oxml.shared import qn from docx.oxml import parse_xml date='2026.9—2027.6' dates='202609' school='XXX幼' def create_photo_placeholder(image_path): """创建照片占位符图片""" try: # 确保目录存在 os.makedirs(os.path.dirname(image_path), exist_ok=True) # 设置图片尺寸 (2.5cm x 3.5cm) width, height = int(2.5/2.54*300), int(3.5/2.54*300) # 转换为300dpi的像素 # 创建白色背景图片 img = Image.new('RGB', (width, height), 'white') draw = ImageDraw.Draw(img) # 绘制黑色边框 draw.rectangle([(10, 10), (width-10, height-10)], outline='black', width=2) # 添加文字 try: # 尝试使用微软雅黑字体 font = ImageFont.truetype("msyh.ttc", 20) except: # 如果找不到字体,使用默认字体 font = ImageFont.load_default() text = "贴照片" text_bbox = draw.textbbox((0, 0), text, font=font) text_width = text_bbox[2] - text_bbox[0] text_height = text_bbox[3] - text_bbox[1] text_x = (width - text_width) // 2 text_y = (height - text_height) // 2 draw.text((text_x, text_y), text, fill='black', font=font) # 保存图片 img.save(image_path) print(f"已创建照片占位符: {image_path}") return True except Exception as e: print(f"创建照片占位符失败: {str(e)}") return False # ========== 读取Excel数据 ========== def read_excel_data(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ī mǎ", "诸葛": "Zhū gě", "东方": "Dōng fāng", "独孤": "Dú gū", "慕容": "Mù róng", "宇文": "Yǔ wén" } def correct_surname_pinyin(name): """校正姓氏拼音,处理多音字问题""" if not name or not isinstance(name, str) or name.strip() == "": return "" name = name.strip() # 先检查复姓 for surname in COMPOUND_SURNAMES: if name.startswith(surname): surname_pinyin = COMPOUND_SURNAMES[surname] given_name = name[2:] given_pinyin = ' '.join([p[0] for p in pinyin(given_name, style=Style.TONE)]) return f"{surname_pinyin} {given_pinyin}" # 检查单姓 first_char = name[0] if first_char in SPECIAL_SURNAMES: surname_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"{surname_pinyin} {given_pinyin}" return ' '.join([p[0] for p in pinyin(name, style=Style.TONE)]) try: xls = pd.ExcelFile(file_path) class_data = {} for sheet_name in xls.sheet_names: df = pd.read_excel( file_path, sheet_name=sheet_name, usecols=range(5), header=None, dtype={0: str} ) df = df.iloc[1:] data_list = [] for index, row in df.iterrows(): row_num = index + 2 raw_id = str(row[0]).strip() if pd.notna(row[0]) else "" # 如果没有学号但有名字,或者有学号但没有名字,都创建记录 name = str(row[3]).strip() if pd.notna(row[3]) else "" try: # 处理学号 if raw_id.isdigit(): id_num = int(raw_id) formatted_id = f"{id_num:02d}" id_display = f"{id_num}号" else: # 如果没有学号,使用行号作为学号 id_num = index + 1 formatted_id = f"{id_num:02d}" id_display = f"{id_num}号" campus = str(row[1]).strip() if pd.notna(row[1]) else "" class_name = str(row[2]).strip() if pd.notna(row[2]) else "" gender = str(row[4]).strip() if pd.notna(row[4]) else "" # 如果没有名字,使用"张三" if not name: name = "张三" name_pinyin = correct_surname_pinyin(name) data_list.append({ 'id_display': id_display, 'id_file': formatted_id, 'campus': campus, 'name': name, 'name_pinyin': name_pinyin, 'class_name': class_name, 'gender': gender }) except Exception as e: print(f"处理第{row_num}行数据时出错: {str(e)}") continue if data_list: class_data[sheet_name] = data_list return class_data except Exception as e: print(f"读取Excel文件失败:{str(e)}") return {} def expand_students_to_target_count(students, class_name): """将学生列表扩展到目标人数(托小班27人,中大班36人)""" # 确定目标人数 if "托" in class_name : target_count = 27 elif "小" in class_name: target_count = 27 elif "中" in class_name: target_count = 36 else: target_count = 36 # 如果实际人数已经达到或超过目标人数,直接返回实际人数 if len(students) >= target_count: return students[:target_count] # 扩展学生列表 expanded_students = students.copy() # 获取第一个学生的园区和班级信息(用于填充空白学生) if students: base_campus = students[0]['campus'] base_class = students[0]['class_name'] else: base_campus = "" base_class = class_name # 添加空白学生 for i in range(len(students), target_count): empty_student = { 'id_display': f"{i+1}号", 'id_file': f"{i+1:02d}", 'campus': base_campus, 'name': "张三", 'name_pinyin': "Zhāng Sān", 'class_name': base_class, 'gender': "" } expanded_students.append(empty_student) return expanded_students def create_word_documents(class_data, orientation, temp_dir, photo_path): """创建Word文档(横版或竖版)""" if orientation == "横版": return create_horizontal_documents(class_data, temp_dir, photo_path) else: return create_vertical_documents(class_data, temp_dir, photo_path) def create_horizontal_documents(class_data, temp_dir, photo_path): """创建横版Word文档""" # 模板文件路径 template_path = os.path.join(path, "00 长方形挂牌_横版.docx") # 字体设置 font_name = "微软雅黑" line_spacing_multiplier = 1 for class_name, students in class_data.items(): # 按9人一组分组 groups = [students[i:i+9] for i in range(0, len(students), 9)] for group_num, group in enumerate(groups, 1): # 复制模板文件 file_name = f"{class_name}_横版_{group_num:02d}.docx" file_path = os.path.join(temp_dir, file_name) shutil.copy(template_path, file_path) # 打开文档 doc = Document(file_path) if len(doc.tables) == 0: print(f"警告: {file_path} 中没有找到表格") continue table = doc.tables[0] # 填充数据 for i, student in enumerate(group): row = i // 3 col_pair = i % 3 double_col = 1 + (col_pair * 2) single_col = col_pair * 2 # 单列单元格(园区和班级) single_cell = table.cell(row, single_col) single_cell._element.clear_content() # 先插入照片 p_photo = single_cell.add_paragraph() p_photo.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER try: run = p_photo.add_run() run.add_picture(photo_path, width=Cm(2.5), height=Cm(3.5)) except Exception as e: print(f"插入照片失败: {str(e)}") # 如果插入照片失败,添加文字提示 run = p_photo.add_run("[照片占位符]") run.font.name = font_name run._element.rPr.rFonts.set(qn('w:eastAsia'), font_name) run.font.size = Pt(10) # 然后添加园区和班级信息 # 然后添加园区和班级信息 p_info = single_cell.add_paragraph() p_info.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER p_pr = p_info._element.get_or_add_pPr() spacing = OxmlElement('w:spacing') spacing.set(qn('w:lineRule'), 'auto') spacing.set(qn('w:line'), str(int(240 * line_spacing_multiplier))) spacing.set(qn('w:before'), '0') spacing.set(qn('w:after'), '0') p_pr.append(spacing) # 学校园区 run = p_info.add_run(f"{school} {student['campus']}\n") run.font.name = font_name run._element.rPr.rFonts.set(qn('w:eastAsia'), font_name) run.font.size = Pt(15) run.font.bold = False # 日期 run = p_info.add_run(f"有效期:{date}") run.font.name = font_name run._element.rPr.rFonts.set(qn('w:eastAsia'), font_name) run.font.size = Pt(10) # 小5 run.font.bold = False # 双列单元格(拼音、姓名和学号) double_cell = table.cell(row, double_col) double_cell._element.clear_content() p_double = double_cell.add_paragraph() p_double.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER p_pr = p_double._element.get_or_add_pPr() spacing = OxmlElement('w:spacing') spacing.set(qn('w:lineRule'), 'auto') spacing.set(qn('w:line'), str(int(240 * line_spacing_multiplier))) spacing.set(qn('w:before'), '0') spacing.set(qn('w:after'), '0') p_pr.append(spacing) run = p_double.add_run(f"{student['class_name']}\n") run.font.name = font_name run._element.rPr.rFonts.set(qn('w:eastAsia'), font_name) run.font.size = Pt(20) run.font.bold = True # 如果是多音字,在拼音后面添加序号标识 pinyin_text = student['name_pinyin'] # if student['pinyin_total'] > 1: # # pinyin_text += f" ({student['pinyin_index']}/{student['pinyin_total']})" # pinyin_text += f" ({student['pinyin_index']})" run = p_double.add_run(f"{pinyin_text}\n") run.font.name = font_name run._element.rPr.rFonts.set(qn('w:eastAsia'), font_name) run.font.size = Pt(12) run.font.bold = False run = p_double.add_run(f"{student['name']}\n") run.font.name = font_name run._element.rPr.rFonts.set(qn('w:eastAsia'), font_name) run.font.size = Pt(25) run.font.bold = True run = p_double.add_run(f"{student['id_display']}") run.font.name = font_name run._element.rPr.rFonts.set(qn('w:eastAsia'), font_name) run.font.size = Pt(20) run.font.bold = True doc.save(file_path) print(f"已创建横版: {file_path}") return temp_dir def create_vertical_documents(class_data, temp_dir, photo_path): """创建竖版Word文档""" # 模板文件路径 template_path = os.path.join(path, "00 长方形挂牌_竖版.docx") # 字体设置 font_name = "微软雅黑" line_spacing_multiplier = 1 for class_name, students in class_data.items(): # 按9人一组分组 groups = [students[i:i+9] for i in range(0, len(students), 9)] for group_num, group in enumerate(groups, 1): # 复制模板文件 file_name = f"{class_name}_竖版_{group_num:02d}.docx" file_path = os.path.join(temp_dir, file_name) shutil.copy(template_path, file_path) # 打开文档 doc = Document(file_path) if len(doc.tables) == 0: print(f"警告: {file_path} 中没有找到表格") continue table = doc.tables[0] # 填充数据 for i, student in enumerate(group): row = i // 3 col = i % 3 cell = table.cell(row, col) cell._element.clear_content() # 添加学校园区信息 p_campus = cell.add_paragraph() p_campus.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER # 设置行距 p_pr = p_campus._element.get_or_add_pPr() spacing = OxmlElement('w:spacing') spacing.set(qn('w:lineRule'), 'auto') spacing.set(qn('w:line'), str(int(240 * line_spacing_multiplier))) spacing.set(qn('w:before'), '0') spacing.set(qn('w:after'), '0') p_pr.append(spacing) # 学校园区 run = p_campus.add_run(f"{school} {student['campus']}") run.font.name = font_name run._element.rPr.rFonts.set(qn('w:eastAsia'), font_name) run.font.size = Pt(15) run.font.bold = False # 插入照片 p_photo = cell.add_paragraph() p_photo.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER try: run = p_photo.add_run() run.add_picture(photo_path, width=Cm(2.5), height=Cm(3.5)) except Exception as e: print(f"插入照片失败: {str(e)}") # 如果插入照片失败,添加文字提示 run = p_photo.add_run("[照片占位符]") run.font.name = font_name run._element.rPr.rFonts.set(qn('w:eastAsia'), font_name) run.font.size = Pt(10) # 添加其他信息 p_info = cell.add_paragraph() p_info.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER # 设置行距 p_pr = p_info._element.get_or_add_pPr() spacing = OxmlElement('w:spacing') spacing.set(qn('w:lineRule'), 'auto') spacing.set(qn('w:line'), str(int(240 * line_spacing_multiplier))) spacing.set(qn('w:before'), '0') spacing.set(qn('w:after'), '0') p_pr.append(spacing) # 班级和学号一行 run = p_info.add_run(f"{student['class_name']}_") run.font.name = font_name run._element.rPr.rFonts.set(qn('w:eastAsia'), font_name) run.font.size = Pt(20) run.font.bold = True run = p_info.add_run(f"{student['id_display']}\n") run.font.name = font_name run._element.rPr.rFonts.set(qn('w:eastAsia'), font_name) run.font.size = Pt(20) run.font.bold = True # 拼音 run = p_info.add_run(f"{student['name_pinyin']}\n") run.font.name = font_name run._element.rPr.rFonts.set(qn('w:eastAsia'), font_name) run.font.size = Pt(12) run.font.bold = False # 姓名 run = p_info.add_run(f"{student['name']}\n") run.font.name = font_name run._element.rPr.rFonts.set(qn('w:eastAsia'), font_name) run.font.size = Pt(25) run.font.bold = True # 日期 run = p_info.add_run(f"有效期:{date}") run.font.name = font_name run._element.rPr.rFonts.set(qn('w:eastAsia'), font_name) run.font.size = Pt(10) run.font.bold = False doc.save(file_path) print(f"已创建竖版: {file_path}") return temp_dir def merge_word_documents(temp_dir, output_dir, orientation): """将同一班级的Word文档合并为一个Word文件 - 使用复制粘贴方法""" # 获取所有Word文件并按班级分组 word_files = glob.glob(os.path.join(temp_dir, f"*_{orientation}_*.docx")) class_files = {} for word_file in word_files: filename = os.path.basename(word_file) class_name = filename.split("_")[0] if class_name not in class_files: class_files[class_name] = [] class_files[class_name].append(word_file) # 确保输出目录存在 os.makedirs(output_dir, exist_ok=True) for class_name, files in class_files.items(): files.sort() if not files: continue # 使用第一个文档作为基础 base_doc = Document(files[0]) # 添加剩余文档的内容 for i in range(1, len(files)): source_doc = Document(files[i]) # # 添加分页符 # base_doc.add_page_break() # 复制所有段落和表格 for element in source_doc.element.body: base_doc.element.body.append(deepcopy(element)) # 保存合并后的文档 output_file = os.path.join(output_dir, f"03_{dates}_{orientation}_长方形挂牌_word可编辑_{class_name}.docx") base_doc.save(output_file) print(f"已合并{orientation}版Word: {output_file}") return output_dir def cleanup(temp_dir): """清理临时文件""" try: shutil.rmtree(temp_dir) print(f"已清理临时文件夹: {temp_dir}") except Exception as e: print(f"清理临时文件夹失败: {str(e)}") # 主程序 if __name__ == "__main__": # 指定Excel文件路径 path = r"D:\Python最终内容\20260901长方形挂牌" excel_file = os.path.join(path, "01 20260805班级信息表.xlsx") # # 指定要生成的班级列表(中1班到中6班) # target_classes = ["中1班", "中2班", "中3班", "中4班", "中5班", "中6班"] # 获取所有工作表名称(排除第一个) try: xls = pd.ExcelFile(excel_file) all_sheets = xls.sheet_names # 排除第一个工作表(通常是封面或说明)本次就是从第一张表开始 if len(all_sheets) > 1: target_classes = all_sheets[0:] # 【1:】从第二个工作表开始取所有 print(f"将处理以下工作表: {target_classes}") else: print("错误: Excel文件中没有足够的工作表") exit(1) except Exception as e: print(f"读取Excel文件失败: {str(e)}") exit(1) # 创建照片占位符 photo_path = os.path.join(path, "photo_placeholder.png") create_photo_placeholder(photo_path) # 创建临时目录 temp_dir = os.path.join(path, "零时文件夹") os.makedirs(temp_dir, exist_ok=True) # 处理横版和竖版 orientations = ["横版", "竖版"] for orientation in orientations: if orientation == "横版": output_dir = os.path.join(path, f"{dates}_托小中大班新生_横版_卡片_可编辑") else: output_dir = os.path.join(path, f"{dates}_托小中大班新生_竖版_卡片_可编辑") # 确保输出目录存在 os.makedirs(output_dir, exist_ok=True) print(f"当前处理方向: {orientation}, 输出目录: {output_dir}") # 调用读取函数 all_class_data = read_excel_data(excel_file) if all_class_data: # 如果指定了target_classes,则只处理这些班级;否则处理所有班级 if 'target_classes' in locals() and target_classes: class_data = {} for class_name in target_classes: if class_name in all_class_data: class_data[class_name] = all_class_data[class_name] print(f"找到班级: {class_name}, 共{len(all_class_data[class_name])}人") else: print(f"警告: 班级 {class_name} 在Excel文件中未找到") else: # 处理所有班级 class_data = all_class_data print(f"将处理所有{len(class_data)}个班级") if class_data: # 扩展学生到目标人数 expanded_class_data = {} for class_name, students in class_data.items(): expanded_students = expand_students_to_target_count(students, class_name) expanded_class_data[class_name] = expanded_students # 创建Word文档(使用扩展后的数据) temp_dir = create_word_documents(expanded_class_data, orientation, temp_dir, photo_path) # 合并同一班级的Word文档 output_dir = merge_word_documents(temp_dir, output_dir, orientation) # 打印汇总信息 print("\n处理完成,汇总信息:") for class_name, students in class_data.items(): # 显示原始人数和扩展后人数 expanded_students = expand_students_to_target_count(students, class_name) group_count = (len(expanded_students) // 9 + (1 if len(expanded_students) % 9 != 0 else 0)) print(f"{class_name}: 原始{len(students)}人 -> 扩展到{len(expanded_students)}人,分成{group_count}个文档") else: print("没有找到任何班级的数据") else: print("未能读取到任何班级数据,请检查文件路径和内容格式。") # 清理临时文件 cleanup(temp_dir) # 计算并显示运行时间 end_time = time.time() total_seconds = end_time - start_time minutes = int(total_seconds // 60) seconds = int(total_seconds % 60) print(f"\n程序运行时间: {minutes}分{seconds}秒 (共{total_seconds:.2f}秒)")