【办公类-109-03】20250730 所有班级的圆形挂牌被子号牌直径9,4CM(突出数字学号信息)正面:班级、拼音、姓名、学号;反面:学校园区)合并三种样式(单面,双面名字+园区,双面名字)

发布时间:2026/7/21 19:45:22
【办公类-109-03】20250730 所有班级的圆形挂牌被子号牌直径9,4CM(突出数字学号信息)正面:班级、拼音、姓名、学号;反面:学校园区)合并三种样式(单面,双面名字+园区,双面名字) 背景需求:前期制作新生(托小班)的胸前姓名挂牌,便于教师认识幼儿人脸、姓名、学号【办公类-109-01】20250728托小班新生挂牌(学号姓名)-CSDN博客文章浏览阅读519次,点赞9次,收藏12次。【办公类-109-01】20250728托小班新生挂牌(学号姓名)https://blog.csdn.net/reasonsummer/article/details/149722959?spm=1011.2415.3001.5331【办公类-109-02】20250729托小班新生挂牌(增加拼音声调)(正面:班级、拼音、姓名、学号;反面:学校园区)-CSDN博客文章浏览阅读737次,点赞20次,收藏7次。【办公类-109-02】20250729托小班新生挂牌(增加拼音声调)(正面:班级、拼音、姓名、学号;反面:学校园区)https://blog.csdn.net/reasonsummer/article/details/149740119?spm=1011.2415.3001.5331一共制作了三种样式:1、单页单面打印(打印后,正面信息+反面空白)2、横向双面打印(打印后,正面信息,反面园区)3、横向双面打印(打印后,正面信息,反面也是信息)根据这个代码,再做一套全校20个班级的被子上捆扎的挂牌。——每个学期9月就要给孩子的被子袋子扎上不同颜色底纹的圆形挂牌,便于老师收集和分发被子。在双周周五分发被子时,各班被子集中放在一个野餐垫上,需要按学号排队摆放,便于排队幼儿一对一领取,因此实用性考虑,需要突出“数字学号”,班级、姓名都是辅助信息具体照片,开学拍摄代码展示步骤:因为是辨认被子用的,每个班级的打印纸底纹色不同,所以只要突出学号就可以了。1、单页单面打印(打印后,正面信息+反面空白)''' 1.制作20250901全校所有班级被子上的圆形挂牌 2.添加姓名拼音显示功能(拼音在姓名上方,带声调) 3.单面打印(正面有文字,反面空白) 豆包,deepseek,阿夏 20250730 ''' # ========== 读取Excel数据 ========== from PIL import Image, ImageDraw, ImageFont import os import pandas as pd import glob from docx import Document from docx.shared import Cm from docx.enum.text import WD_PARAGRAPH_ALIGNMENT import win32com.client from PyPDF2 import PdfMerger import shutil from pypinyin import pinyin, Style # ========== 读取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" # 单姓"万"读Wàn } # 复姓处理字典 COMPOUND_SURNAMES = { "欧阳": "Ōu yáng", "上官": "Shàng guān", "皇甫": "Huáng fǔ", "尉迟": "Yù chí", "万俟": "Mò qí", # 复姓"万俟"读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" } # 重要说明: # 1. 单姓"万"标准读音为Wàn(如万千、万茜) # 2. 复姓"万俟"读音为Mò qí(源自南北朝鲜卑族) # 3. 两个姓氏虽然都含"万"字,但读音和来源完全不同 # 4. 复姓读音保持中间空格的传统拼音格式 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}" # 这里使用正确的变量名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: # 读取当前工作表(A-E列),无表头 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(): print(f"跳过 {sheet_name} 工作表第 {row_num} 行:无效学号") continue # 处理姓名(严格检查空值) name = str(row[3]).strip() if pd.notna(row[3]) else "" if not name: print(f"跳过 {sheet_name} 工作表第 {row_num} 行:姓名为空") 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) # 调试输出 print(f"处理: {name} - {name_pinyin}") # 添加调试信息 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: print(f"处理 {sheet_name} 工作表第 {row_num} 行时出错:{str(e)}") continue if data_list: class_data[sheet_name] = data_list print(f"成功读取 [{sheet_name}]:{len(data_list)} 条有效记录") return class_data except Exception as e: print(f"读取Excel文件失败:{type(e).__name__} - {str(e)}") return {} # ========== 正面图片生成函数 ========== def generate_image(data, output_dir): """根据单条数据生成正面图片(图片学号显示为1号,文件名包含01)""" # id_display = data['id_display'] # 图片上显示的学号(如1号) # id_file = data['id_file'] # 文件名中使用的学号(如01) # class_name = data['class_name'] # name = data['name'] id_display = data['id_display'] # 图片上显示的学号(如1号) id_file = data['id_file'] # 文件名中使用的学号(如01) class_name = data['class_name'] name = data['name'] name_pinyin = data['name_pinyin'] # 确保输出目录存在 os.makedirs(output_dir, exist_ok=True) # 图片参数 image_size = (830, 830) circle_diameter = 780 border_width = 30 # 小圆形参数 small_circle_diameter = 50 small_circle_border = 5 small_circle_offset_y = -320 # # 基础字体大小 # base_name_font_size = 200 # class_font_size = 130 # id_font_size = 100 # pinyin_font_size = 30 # 拼音字体大小(比之前稍大) # 基础字体大小 base_name_font_size = 100 class_font_size = 130 id_font_size = 200 pinyin_font_size = 30 # 拼音字体大小(比之前稍大) # 根据姓名长度调整字号(4个字时减小20磅) # name_length = len(name) # name_font_size = base_name_font_size - 40 if name_length = 4 else base_name_font_size name_font_size = base_name_font_size # 位置偏移 class_offset_y = -220 name_offset_y = 230 id_offset_y = -40 pinyin_offset_y = 70 # 拼音在姓名上方 # 其他样式 underline_thickness = 10 outer_circle_diameter = 830 outer_line_width = 5 zeng = 80 # 加载字体(强制微软雅黑) try: name_font = ImageFont.truetype("msyh.ttc", name_font_size) class_font = ImageFont.truetype("msyh.ttc", class_font_size) id_font = ImageFont.truetype("msyh.ttc", id_font_size) pinyin_font = ImageFont.truetype("msyh.ttc", pinyin_font_size) except Exception as e: print(f"字体加载失败:{e},使用默认字体") name_font = ImageFont.load_default() class_font = ImageFont.load_default() id_font = ImageFont.load_default() pinyin_font = ImageFont.load_default() # 创建新图片 img = Image.new('RGB', image_size, 'white') draw = ImageDraw.Draw(img) # 设置颜色 line_color = text_color = outer_color = 'black' # 其他常用灰色: # (105, 105, 105) —— 暗灰色(Dim Gray) # (169, 169, 169) —— 深灰色(Dark Gray) # (220, 220, 220) —— 极浅灰色(Gainsboro) gray_color='DarkGray' # 绘制圆形边框 circle_x = (image_size[0] - circle_diameter) // 2 circle_y = (image_size[1] - circle_diameter) // 2 draw.ellipse([circle_x, circle_y, circle_x + circle_diameter, circle_y + circle_diameter], outline=line_color, width=border_width) # 绘制内部圆形 inner_diameter = circle_diameter - 2 * border_width inner_x = circle_x + border_width inner_y = circle_y + border_width draw.ellipse([inner_x, inner_y, inner_x + inner_diameter, inner_y + inner_diameter], fill='white', outline='white') # 绘制外部实线圆 outer_radius = outer_circle_diameter // 2 outer_center = (image_size[0] // 2, image_size[1] // 2) outer_x1 = outer_center[0] - outer_radius outer_y1 = outer_center[1] - outer_radius outer_x2 = outer_center[0] + outer_radius outer_y2 = outer_center[1] + outer_radius draw.ellipse([outer_x1, outer_y1, outer_x2, outer_y2], outline=outer_color, width=outer_line_width) # 绘制小圆形 small_center_x = image_size[0] // 2 small_center_y = image_size[1] // 2 + small_circle_offset_y small_radius = small_circle_diameter // 2 draw.ellipse([ small_center_x - small_radius, small_center_y - small_radius, small_center_x + small_radius, small_center_y + small_radius ], fill='white', outline='black', width=small_circle_border) # 绘制班级文字 class_bbox = draw.textbbox((0, 0), class_name, font=class_font) class_x = (image_size[0] - (class_bbox[2] - class_bbox[0])) // 2 class_y = (image_size[1] - (class_bbox[3] - class_bbox[1])) // 2 + class_offset_y for offset in [(0, 0), (1, 0), (-1, 0), (0, 1), (0, -1)]: draw.text((class_x + offset[0], class_y + offset[1]), class_name, font=class_font, fill=gray_color) # 绘制姓名 name_bbox = draw.textbbox((0, 0), name, font=name_font) name_x = (image_size[0] - (name_bbox[2] - name_bbox[0])) // 2 name_y = (image_size[1] - (name_bbox[3] - name_bbox[1])) // 2 + name_offset_y for offset in [(0, 0), (1, 0), (-1, 0), (0, 1), (0, -1)]: draw.text((name_x + offset[0], name_y + offset[1]), name, font=name_font, fill=gray_color) # 绘制拼音(在姓名上方) pinyin_bbox = draw.textbbox((0, 0), name_pinyin, font=pinyin_font) pinyin_x = (image_size[0] - (pinyin_bbox[2] - pinyin_bbox[0])) // 2 pinyin_y = name_y - (name_bbox[3] - name_bbox[1]) + pinyin_offset_y for offset in [(0, 0), (1, 0), (-1, 0), (0, 1), (0, -1)]: draw.text((pinyin_x + offset[0], pinyin_y + offset[1]), name_pinyin, font=pinyin_font, fill=gray_color) # 绘制学号及下划线(使用图片显示用学号) id_bbox = draw.textbbox((0, 0), id_display, font=id_font) id_x = (image_size[0] - (id_bbox[2] - id_bbox[0])) // 2 id_y = (image_size[1] - (id_bbox[3] - id_bbox[1])) // 2 + id_offset_y for offset in [(0, 0), (1, 0), (-1, 0), (0, 1), (0, -1)]: draw.text((id_x + offset[0], id_y + offset[1]), id_display, font=id_font, fill=text_color) # 绘制学号下划线 - 修改这里 # 下划线应该在学号文本的正下方 # 绘制学号下划线 underline_y = id_y + (id_bbox[3] - id_bbox[1]) + zeng draw.line([(id_x, underline_y), (id_x + (id_bbox[2] - id_bbox[0]), underline_y)], fill=line_color, width=underline_thickness) # id_bbox = draw.textbbox((0, 0), id_display, font=id_font) # id_x = (image_size[0] - (id_bbox[2] - id_bbox[0])) // 2 # id_y = (image_size[1] - (id_bbox[3] - id_bbox[1])) // 2 + id_offset_y # for offset in [(0, 0), (1, 0), (-1, 0), (0, 1), (0, -1)]: # draw.text((id_x + offset[0], id_y + offset[1]), # id_display, font=id_font, fill=gray_color) # # 绘制学号下划线 # underline_y = id_y + (id_bbox[3] - id_bbox[1]) + zeng # draw.line([(id_x, underline_y), (id_x + (id_bbox[2] - id_bbox[0]), underline_y)], # fill=text_color, width=underline_thickness) # 保存图片(文件名使用格式化后的学号,如01) filename = f"{class_name}_{id_file}_{name}.png" output_path = os.path.join(output_dir, filename) img.save(output_path) print(f"已生成:{output_path}") # ========== 背面图片生成函数 ========== def generate_back_image(class_name, campus, output_dir): """生成班级背面图片""" # 图片参数 image_size = (830, 830) circle_diameter = 780 border_width = 30 # 小圆形参数 small_circle_diameter = 50 small_circle_border = 5 small_circle_offset_y = -320 # 外部实线圆参数 outer_circle_diameter = 830 outer_line_width = 5 outer_color = 'black' # 字体参数 school_font_size = 150 campus_font_size = 120 line_spacing = 80 # 行间距 # 强制使用微软雅黑 try: school_font = ImageFont.truetype("msyh.ttc", school_font_size) campus_font = ImageFont.truetype("msyh.ttc", campus_font_size) except: school_font = ImageFont.load_default() campus_font = ImageFont.load_default() # 创建图片 img = Image.new('RGB', image_size, 'white') draw = ImageDraw.Draw(img) text_color = 'black' # 绘制圆形边框 circle_x = (image_size[0] - circle_diameter) // 2 circle_y = (image_size[1] - circle_diameter) // 2 draw.ellipse([circle_x, circle_y, circle_x + circle_diameter, circle_y + circle_diameter], outline=text_color, width=border_width) # 绘制内部圆形 inner_diameter = circle_diameter - 2 * border_width draw.ellipse([circle_x + border_width, circle_y + border_width, circle_x + border_width + inner_diameter, circle_y + border_width + inner_diameter], fill='white', outline='white') # 绘制小圆形 small_center_x = image_size[0] // 2 small_center_y = image_size[1] // 2 + small_circle_offset_y small_radius = small_circle_diameter // 2 draw.ellipse([ small_center_x - small_radius, small_center_y - small_radius, small_center_x + small_radius, small_center_y + small_radius ], fill='white', outline='black', width=small_circle_border) # 计算文字位置 school_name = "XXX幼" campus_name = f"{campus}" # 学校名称位置 school_bbox = draw.textbbox((0, 0), school_name, font=school_font) school_x = (image_size[0] - (school_bbox[2] - school_bbox[0])) // 2 school_y = (image_size[1] - (school_bbox[3] - school_bbox[1]) - line_spacing) // 2 - 60 # 分园名称位置 campus_bbox = draw.textbbox((0, 0), campus_name, font=campus_font) campus_x = (image_size[0] - (campus_bbox[2] - campus_bbox[0])) // 2 campus_y = school_y + (school_bbox[3] - school_bbox[1]) + line_spacing # 绘制文字(带加粗效果) for offset in [(0, 0), (1, 0), (-1, 0), (0, 1), (0, -1)]: draw.text((school_x + offset[0], school_y + offset[1]), school_name, font=school_font, fill=text_color) draw.text((campus_x + offset[0], campus_y + offset[1]), campus_name, font=campus_font, fill=text_color) # 绘制外部实线圆 outer_radius = outer_circle_diameter // 2 outer_center = (image_size[0] // 2, image_size[1] // 2) outer_x1 = outer_center[0] - outer_radius outer_y1 = outer_center[1] - outer_radius outer_x2 = outer_center[0] + outer_radius outer_y2 = outer_center[1] + outer_radius draw.ellipse([outer_x1, outer_y1, outer_x2, outer_y2], outline=outer_color, width=outer_line_width) # 保存图片 filename = f"{class_name}背面图.png" output_path = os.path.join(output_dir, filename) img.save(output_path) print(f"已生成背面图:{output_path}") # ========== PDF生成函数 ========== def process_images_to_pdf(path, class_name): # 创建临时文件夹 temp_dir = os.path.join(path, "临时文件夹") os.makedirs(temp_dir, exist_ok=True) # 获取所有图片(使用班级名称动态生成图片文件夹路径) image_folder = os.path.join(path, f"{class_name}黑色文字") image_files = glob.glob(os.path.join(image_folder, "*.jpg")) + \ glob.glob(os.path.join(image_folder, "*.png")) # 排除背面图 back_image = f"{class_name}背面图.png" image_files = [f for f in image_files if back_image not in os.path.basename(f)] # 计算总页数 total_pages = (len(image_files) + 5) // 6 # 向上取整 # 每6张图片一组处理 for i in range(0, len(image_files), 6): group = image_files[i:i+6] # 打开现有的Word模板 doc = Document(os.path.join(path, "挂牌.docx")) # 获取文档中的第一个表格 table = doc.tables[0] # 清除表格中已有的内容 for row in table.rows: for cell in row.cells: for paragraph in cell.paragraphs: while paragraph.runs: del paragraph.runs[0] # 插入图片到表格 for idx, img_path in enumerate(group): row = idx // 2 col = idx % 2 if row 3 and col 2: # 确保不超过表格范围 cell = table.cell(row, col) paragraph = cell.paragraphs[0] paragraph.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER # 添加图片并设置大小 run = paragraph.add_run() run.add_picture(img_path, width=Cm(9.4), height=Cm(9.4)) # 保存Word文档 docx_path = os.path.join(temp_dir, f"{(i//6 + 1):03}.docx") doc.save(docx_path) print(f"已生成: {docx_path}") # 将所有Word转为PDF word_files = glob.glob(os.path.join(temp_dir, "*.docx")) pdf_files = [] # 初始化Word应用 word = win32com.client.Dispatch("Word.Application") word.Visible = False try: for docx_file in word_files: pdf_file = os.path.join(temp_dir, os.path.basename(docx_file).replace(".docx", ".pdf")) doc = word.Documents.Open(os.path.abspath(docx_file)) doc.SaveAs(os.path.abspath(pdf_file), FileFormat=17) # 17是PDF格式 doc.Close() pdf_files.append(pdf_file) print(f"已转换: {pdf_file}") except Exception as e: print(f"转换过程出错: {str(e)}") finally: word.Quit() del word # 合并所有PDF merger = PdfMerger() # 合并所有PDF all = path + r'\04只有正面单面打印的被子拼音挂牌' os.makedirs(all, exist_ok=True) # 在文件名中添加页数信息 output_pdf = os.path.join(all, f"{class_name}被子挂牌合并(只有正面,单面打印,共{total_pages}页).pdf") for pdf in sorted(pdf_files): merger.append(pdf) merger.write(output_pdf) merger.close() print(f"已合并PDF到: {output_pdf}") # 清理临时文件夹 if os.path.exists(temp_dir): shutil.rmtree(temp_dir) print(f"已清理临时文件:{temp_dir}") # ========== 主程序 ========== if __name__ == "__main__": # 配置参数 base_dir = path = r'C:\Users\jg2yXRZ\OneDrive\桌面\20250729全校被子挂牌' excel_file = os.path.join(base_dir, "班级信息表.xlsx") # 读取所有班级数据 print("开始读取Excel数据...") class_data_dict = read_excel_data(excel_file) if not class_data_dict: print("无有效数据,程序退出") exit() # 处理每个班级 for sheet_name, data_list in class_data_dict.items(): # 获取班级信息(使用第一个学生的班级信息) class_name = data_list[0]['class_name'] campus = data_list[0]['campus'] print(f"\n开始处理班级: {class_name} ({sheet_name})") # 创建班级输出目录 output_dir = os.path.join(base_dir, f"{class_name}黑色文字") os.makedirs(output_dir, exist_ok=True) # 生成正面学生名牌 print("开始生成正面图片...") for data in data_list: generate_image(data, output_dir) # 生成背面班级图 print("开始生成背面图片...") generate_back_image(class_name, campus, output_dir) # 生成PDF文件 print("开始生成PDF文件...") process_images_to_pdf(base_dir, class_name) print(f"班级 {class_name} 处理完成!") # # 删除圆形图片文件夹 shutil.rmtree(output_dir) print(f"\n所有班级处理完成!结果已保存到:{base_dir}")2、横向双面打印(打印后,正面信息,反面园区)''' 1.制作20250901全校所有班级被子上的圆形挂牌 2.添加姓名拼音显示功能 3.横向双面双面打印(正面学号拼音,反面学校园区) 豆包,deepseek,阿夏 20250730 ''' # ========== 读取Excel数据 ========== from PIL import Image, ImageDraw, ImageFont import os import pandas as pd import glob from docx import Document from docx.shared import Cm from docx.enum.text import WD_PARAGRAPH_ALIGNMENT import win32com.client from PyPDF2 import PdfMerger import shutil from pypinyin import pinyin, Style # ========== 读取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" # 单姓"万"读Wàn } # 复姓处理字典 COMPOUND_SURNAMES = { "欧阳": "Ōu yáng", "上官": "Shàng guān", "皇甫": "Huáng fǔ", "尉迟": "Yù chí", "万俟": "Mò qí", # 复姓"万俟"读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" } # 重要说明: # 1. 单姓"万"标准读音为Wàn(如万千、万茜) # 2. 复姓"万俟"读音为Mò qí(源自南北朝鲜卑族) # 3. 两个姓氏虽然都含"万"字,但读音和来源完全不同 # 4. 复姓读音保持中间空格的传统拼音格式