背景需求
把“圆卡”发给托小班组长看
【办公类-109-02】20250729托小班新生挂牌(增加拼音声调)(正面:班级、拼音、姓名、学号;反面:学校园区)合并三种样式(单面,双面名字+园区,双面名字)-CSDN博客文章浏览阅读992次,点赞31次,收藏12次。【办公类-109-02】20250729托小班新生挂牌(增加拼音声调)(正面:班级、拼音、姓名、学号;反面:学校园区)合并三种样式(单面,双面名字+园区,双面名字)https://blog.csdn.net/reasonsummer/article/details/149740119?spm=1011.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, 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 "" 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 = path+r"\零时文件" if not os.path.exists(temp_dir): os.makedirs(temp_dir) # 模板文件路径 template_path = path+r"\竖版.docx" # 字体设置 font_name = "微软雅黑" line_spacing_multiplier = 1 # 1.2倍行距 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) # 获取第一个表格(假设模板中只有一个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(f"XXX幼 {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, FileFormat=17) # 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 = r"C:\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}秒)")时长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, 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 "" 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 = path+r"\零时文件" if not os.path.exists(temp_dir): os.makedirs(temp_dir) # 模板文件路径 template_path = path+r"\横版.docx" # 字体设置 font_name = "微软雅黑" line_spacing_multiplier = 1 # 1.2倍行距 for class_name, students in class_data.items(): # 按9人一组分组(因为3行×3双列=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) # 获取第一个表格(假设模板中是一个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(f"XXX幼\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, FileFormat=17) # 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 = r"C:\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}秒)")