【办公类-109-04】20250807托小班新生的卡套长方形挂牌制作Python3.11.5 32(85*54mm横板和54*85mm竖版,一页9张)

📅 2026/7/21 17:17:50
【办公类-109-04】20250807托小班新生的卡套长方形挂牌制作Python3.11.5 32(85*54mm横板和54*85mm竖版,一页9张)
背景需求把“圆卡”发给托小班组长看【办公类-109-02】20250729托小班新生挂牌增加拼音声调正面班级、拼音、姓名、学号反面学校园区合并三种样式单面双面名字园区双面名字-CSDN博客文章浏览阅读992次点赞31次收藏12次。【办公类-109-02】20250729托小班新生挂牌增加拼音声调正面班级、拼音、姓名、学号反面学校园区合并三种样式单面双面名字园区双面名字https://blog.csdn.net/reasonsummer/article/details/149740119?spm1011.2415.3001.5331组长说不用“圆卡片”了直接把卡片做成与卡套相同的“长方形卡片”可能有横板和竖版尺寸都是85*54mm这个名字就不需要做成图片了就直接文字插入。素材准备为了让表格能够在画布中间竖版3*3页边距2和2.4横板3*3 页边距2和2.35部分中线隐藏EXCLE表格竖版卡片姓名班级学号 1.制作20250901托小班长方形卡片竖版54*85mm.一页9张单面因为插在卡套里另外一面看不见 2.添加姓名拼音显示功能拼音在姓名上方带声调 豆包,deepseek,阿夏 20250807 import time from PIL import Image, ImageDraw, ImageFont import os import pandas as pd import glob from docx import Document from docx.shared import Cm, Pt, Inches from docx.enum.text import WD_PARAGRAPH_ALIGNMENT import win32com.client from PyPDF2 import PdfMerger import shutil from pypinyin import pinyin, Style from docx.oxml.ns import qn from docx.oxml import OxmlElement # 用于设置段落间距 from docx.enum.text import WD_PARAGRAPH_ALIGNMENT # 记录程序开始时间 start_time time.time() # 读取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, styleStyle.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, styleStyle.TONE)]) return f{surname_pinyin} {given_pinyin} return .join([p[0] for p in pinyin(name, styleStyle.TONE)]) try: xls pd.ExcelFile(file_path) class_data {} for sheet_name in xls.sheet_names: df pd.read_excel( file_path, sheet_namesheet_name, usecolsrange(5), headerNone, 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 if not raw_id.isdigit(): continue name str(row[3]).strip() if pd.notna(row[3]) else if not name: continue try: id_num int(raw_id) formatted_id f{id_num:02d} 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 name_pinyin correct_surname_pinyin(name) data_list.append({ id_display: f{id_num}号, id_file: formatted_id, campus: campus, name: name, name_pinyin: name_pinyin, class_name: class_name, gender: gender }) except Exception as 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 create_word_documents(class_data): 使用现有模板创建Word文档每页3x3表格每9人一组包含所有信息 # 创建临时文件夹 temp_dir pathr\零时文件 if not os.path.exists(temp_dir): os.makedirs(temp_dir) # 模板文件路径 template_path pathr\竖版.docx # 字体设置 font_name 微软雅黑 line_spacing_multiplier 1 # 1.2倍行距 for class_name, students in class_data.items(): # 按9人一组分组 groups [students[i:i9] 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) # 获取第一个表格假设模板中只有一个3x3表格 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 cell.add_paragraph() p.alignment WD_PARAGRAPH_ALIGNMENT.CENTER # 设置1.2倍行距 p_pr p._element.get_or_add_pPr() # 设置行距类型为多倍行距 spacing OxmlElement(w:spacing) spacing.set(qn(w:lineRule), auto) # 自动行距规则 # 计算1.2倍行距的值Word中以240为基数1.2倍即288 # 计算公式: 240 * 1.2 288 spacing.set(qn(w:line), str(int(240 * line_spacing_multiplier))) # 设置段前和段后间距为0 spacing.set(qn(w:before), 0) spacing.set(qn(w:after), 0) p_pr.append(spacing) # 园区 (20号字) run p.add_run(fXXX幼 {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 # 班级 (20号字) run p.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(30) run.font.bold True # 拼音 (15号字) run p.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(15) run.font.bold False # 姓名 (20号字) run p.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(30) run.font.bold True # 学号 (20号字) run p.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(30) run.font.bold True # 保存文档 doc.save(file_path) print(f已创建: {file_path}) return temp_dir def convert_word_to_pdf(temp_dir): 将Word文档转换为PDF word win32com.client.Dispatch(Word.Application) word.Visible False # 获取所有Word文档 word_files glob.glob(os.path.join(temp_dir, *.docx)) for word_file in word_files: try: doc word.Documents.Open(word_file) # PDF保存在同一个临时文件夹中 pdf_file os.path.join(temp_dir, os.path.basename(word_file).replace(.docx, .pdf)) doc.SaveAs(pdf_file, FileFormat17) # 17是PDF格式 doc.Close() print(f已转换为PDF: {pdf_file}) except Exception as e: print(f转换失败: {word_file}, 错误: {str(e)}) word.Quit() return temp_dir # 返回同一个临时文件夹路径 def merge_pdfs(temp_dir, output_dir): 合并PDF文件 # 创建输出文件夹 if not os.path.exists(output_dir): os.makedirs(output_dir) merger PdfMerger() # 获取所有PDF文件并按班级分组 pdf_files glob.glob(os.path.join(temp_dir, *.pdf)) class_files {} for pdf_file in pdf_files: # 从文件名中提取班级名 filename os.path.basename(pdf_file) class_name filename.split(_)[0] if class_name not in class_files: class_files[class_name] [] class_files[class_name].append(pdf_file) # 按班级合并PDF for class_name, files in class_files.items(): # 按文件名排序 files.sort() # 合并PDF output_file os.path.join(output_dir, f{class_name}_新生_竖版卡片(班级姓名学号).pdf) for pdf in files: merger.append(pdf) merger.write(output_file) merger.close() print(f已合并PDF: {output_file}) # 重新初始化merger merger PdfMerger() merger.close() 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 rC:\Users\jg2yXRZ\OneDrive\桌面\20250805幼儿长方形挂牌 wordpathr\班级信息表.xlsx # 输出文件夹路径 pdf_output_dir pathr\20250830_托小班新生_竖版_卡片 # 调用读取函数 class_data read_excel_data(word) if class_data: # 创建Word文档 temp_dir create_word_documents(class_data) # 转换为PDF保存在同一个临时文件夹中 temp_dir convert_word_to_pdf(temp_dir) # 合并PDF从临时文件夹读取保存到输出文件夹 merge_pdfs(temp_dir, pdf_output_dir) # 清理临时文件 cleanup(temp_dir) # 打印汇总信息 print(\n处理完成汇总信息:) for class_name, students in class_data.items(): group_count (len(students) // 9 (1 if len(students) % 9 ! 0 else 0)) print(f{class_name}: {len(students)}名学生分成{group_count}个文档) else: print(未能读取到任何班级数据请检查文件路径和内容格式。) # 计算并显示运行时间 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}秒))时长48秒文件位置都是竖版横板卡片姓名班级学号 1.制作20250901托小班长方形卡片横版85*54mm.一页9张单面因为插在卡套里另外一面看不见 2.添加姓名拼音显示功能拼音在姓名上方带声调 豆包,deepseek,阿夏 20250807 3. 修改布局单列显示园区和班级双列显示拼音、姓名和学号 import time from PIL import Image, ImageDraw, ImageFont import os import pandas as pd import glob from docx import Document from docx.shared import Cm, Pt, Inches from docx.enum.text import WD_PARAGRAPH_ALIGNMENT import win32com.client from PyPDF2 import PdfMerger import shutil from pypinyin import pinyin, Style from docx.oxml.ns import qn from docx.oxml import OxmlElement # 用于设置段落间距 from docx.enum.text import WD_PARAGRAPH_ALIGNMENT # 记录程序开始时间 start_time time.time() # 读取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, styleStyle.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, styleStyle.TONE)]) return f{surname_pinyin} {given_pinyin} return .join([p[0] for p in pinyin(name, styleStyle.TONE)]) try: xls pd.ExcelFile(file_path) class_data {} for sheet_name in xls.sheet_names: df pd.read_excel( file_path, sheet_namesheet_name, usecolsrange(5), headerNone, 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 if not raw_id.isdigit(): continue name str(row[3]).strip() if pd.notna(row[3]) else if not name: continue try: id_num int(raw_id) formatted_id f{id_num:02d} 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 name_pinyin correct_surname_pinyin(name) data_list.append({ id_display: f{id_num}号, id_file: formatted_id, campus: campus, name: name, name_pinyin: name_pinyin, class_name: class_name, gender: gender }) except Exception as 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 create_word_documents(class_data): 使用现有模板创建Word文档每页3行6列表格单列显示园区班级双列显示拼音姓名学号 # 创建临时文件夹 temp_dir pathr\零时文件 if not os.path.exists(temp_dir): os.makedirs(temp_dir) # 模板文件路径 template_path pathr\横版.docx # 字体设置 font_name 微软雅黑 line_spacing_multiplier 1 # 1.2倍行距 for class_name, students in class_data.items(): # 按9人一组分组因为3行×3双列9人 groups [students[i:i9] 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) # 获取第一个表格假设模板中是一个3行6列的表格 if len(doc.tables) 0: print(f警告: {file_path} 中没有找到表格) continue table doc.tables[0] # 填充数据 for i, student in enumerate(group): # 计算行和列的位置 # 每行有2个双列单元格和2个单列单元格 row i // 3 # 每3个学生占一行 col_pair i % 3 # 每行的3个双列位置 # 双列单元格的位置 (0,1), (0,3), (0,5), (1,1), (1,3), (1,5), (2,1), (2,3), (2,5) double_col 1 (col_pair * 2) # 单列单元格的位置 (0,0), (0,2), (0,4), (1,0), (1,2), (1,4), (2,0), (2,2), (2,4) single_col col_pair * 2 # 先处理单列单元格园区和班级 single_cell table.cell(row, single_col) single_cell._element.clear_content() p_single single_cell.add_paragraph() p_single.alignment WD_PARAGRAPH_ALIGNMENT.CENTER # 设置行距 p_pr p_single._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) # 园区 (15号字) run p_single.add_run(fXXX幼\n{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 # 班级 (30号字) run p_single.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(30) run.font.bold True # 处理双列单元格拼音、姓名和学号 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) # 拼音 (15号字) run p_double.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 # 姓名 (30号字) 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(26) run.font.bold True # 学号 (30号字) 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(30) run.font.bold True # 保存文档 doc.save(file_path) print(f已创建: {file_path}) return temp_dir def convert_word_to_pdf(temp_dir): 将Word文档转换为PDF word win32com.client.Dispatch(Word.Application) word.Visible False # 获取所有Word文档 word_files glob.glob(os.path.join(temp_dir, *.docx)) for word_file in word_files: try: doc word.Documents.Open(word_file) # PDF保存在同一个临时文件夹中 pdf_file os.path.join(temp_dir, os.path.basename(word_file).replace(.docx, .pdf)) doc.SaveAs(pdf_file, FileFormat17) # 17是PDF格式 doc.Close() print(f已转换为PDF: {pdf_file}) except Exception as e: print(f转换失败: {word_file}, 错误: {str(e)}) word.Quit() return temp_dir # 返回同一个临时文件夹路径 def merge_pdfs(temp_dir, output_dir): 合并PDF文件 # 创建输出文件夹 if not os.path.exists(output_dir): os.makedirs(output_dir) merger PdfMerger() # 获取所有PDF文件并按班级分组 pdf_files glob.glob(os.path.join(temp_dir, *.pdf)) class_files {} for pdf_file in pdf_files: # 从文件名中提取班级名 filename os.path.basename(pdf_file) class_name filename.split(_)[0] if class_name not in class_files: class_files[class_name] [] class_files[class_name].append(pdf_file) # 按班级合并PDF for class_name, files in class_files.items(): # 按文件名排序 files.sort() # 合并PDF output_file os.path.join(output_dir, f{class_name}_新生_横版卡片(班级姓名学号).pdf) for pdf in files: merger.append(pdf) merger.write(output_file) merger.close() print(f已合并PDF: {output_file}) # 重新初始化merger merger PdfMerger() merger.close() 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 rC:\Users\jg2yXRZ\OneDrive\桌面\20250805幼儿长方形挂牌 word path r\班级信息表.xlsx # 输出文件夹路径 pdf_output_dir path r\20250830_托小班新生_横版_卡片 # 调用读取函数 class_data read_excel_data(word) if class_data: # 创建Word文档 temp_dir create_word_documents(class_data) # 转换为PDF保存在同一个临时文件夹中 temp_dir convert_word_to_pdf(temp_dir) # 合并PDF从临时文件夹读取保存到输出文件夹 merge_pdfs(temp_dir, pdf_output_dir) # 清理临时文件 cleanup(temp_dir) # 打印汇总信息 print(\n处理完成汇总信息:) for class_name, students in class_data.items(): group_count (len(students) // 9 (1 if len(students) % 9 ! 0 else 0)) print(f{class_name}: {len(students)}名学生分成{group_count}个文档) else: print(未能读取到任何班级数据请检查文件路径和内容格式。) # 计算并显示运行时间 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}秒))如果四个名字的拼音很长就会到下一页横板有点麻烦就是名字长度和拼音长度会导致表格下移下次真名名单时看看有没有四个字如果时3个字就不会表格下移四个姓名很靠近边缘为了保障万一还是把姓名和拼音字号变小这次用了45秒开学后打印不同颜色的彩色纸看看是否能放入卡套