



在日常工作和学习中,我们总会遇到批量制作奖状的场景:老师要给班级优秀学生、进步之星打印荣誉证书,公司行政要为优秀员工、团队标兵定制表彰奖状,培训机构要为结业学员发放专属结业证书……但传统的奖状制作方式,要么是手动一张张填写,耗时耗力还容易出错;要么是借助复杂的设计软件,需要专业技能,新手根本无从下手;更别提批量生成、自由排版、一键导出打印这些刚需功能,普通工具完全无法满足。
难道就没有一款简单易用、颜值拉满、功能齐全的奖状制作工具吗?今天就给大家带来一款纯Python开发的可视化奖状生成器,无需复杂操作,无需设计基础,打开就能用!这款工具采用PyQt5打造精致不透明的高级界面,支持模板导入、文字自由拖拽、Excel批量导入姓名、单张/批量导出、打印预览+直接打印全功能,界面美观大方、操作逻辑简单清晰,无论是电脑小白还是编程新手,都能轻松上手,几分钟搞定上百张奖状的制作,彻底解放双手,告别繁琐的手动操作!
工具核心亮点拉满:✅精致高级GUI界面,不透明无冗余,视觉超舒适;✅文字自由拖拽定位,字号一键调整,排版随心所欲;✅支持图片模板导入,缩放适配不失真;✅Excel批量导入姓名,一键生成所有奖状;✅单张/批量高清导出,打印预览+直接打印全覆盖;✅纯Python编写,代码可定制,功能可拓展。接下来就带大家深度拆解这款宝藏工具的核心代码,从界面搭建到核心功能,手把手带你掌握开发逻辑!
我们首先要实现奖状的核心交互功能——文字自由拖拽。奖状上的姓名、奖励名称、时间、单位都需要精准定位在模板上,普通标签无法拖拽,因此我们自定义DraggableLabel类,继承自QLabel,实现无背景、无边框、可拖拽、可调整字体的文字标签,这是整个工具排版的核心基础。
# 可拖拽文字标签(已去掉边框、背景,完全干净)classDraggableLabel(QLabel):def__init__(self, text, parent=None): super().__init__(text, parent)# 关键:去掉边框 + 去掉背景,只显示文字 self.setStyleSheet("background:transparent; border:none; padding:2px;") self.drag_flag = False# 拖拽标记 self.drag_pos = QPoint(0,0) # 拖拽初始位置 self.font_size = 36# 默认字号 self.font_bold = False# 默认不加粗 self.text_color = QColor(0,0,0) # 默认黑色# 鼠标按下事件:记录拖拽起始位置defmousePressEvent(self, event):if event.button() == Qt.LeftButton: self.drag_flag = True self.drag_pos = event.globalPos() - self.frameGeometry().topLeft() self.setCursor(Qt.OpenHandCursor) # 切换抓手图标 super().mousePressEvent(event)# 鼠标移动事件:实现标签跟随移动defmouseMoveEvent(self, event):if self.drag_flag and event.buttons() == Qt.LeftButton: self.move(event.globalPos() - self.drag_pos) super().mouseMoveEvent(event)# 鼠标松开事件:结束拖拽defmouseReleaseEvent(self, event): self.drag_flag = False self.setCursor(Qt.ArrowCursor) # 恢复默认光标 super().mouseReleaseEvent(event)# 更新字体样式:字号、颜色、加粗统一设置defupdate_font(self): font = QFont("宋体", self.font_size)if self.font_bold: font.setBold(True) self.setFont(font) self.setStyleSheet(f"color:{self.text_color.name()};background:transparent;border:none;padding:2px;")mousePressEvent记录鼠标按下的初始位置,mouseMoveEvent实时更新标签位置实现拖拽,mouseReleaseEvent结束拖拽状态,三个事件配合完成流畅的拖拽交互;update_font方法统一管理字体样式,支持字号调整、颜色设置、加粗切换,后续可以直接调用修改文字样式。工具的核心交互都在主窗口完成,我们使用PyQt5的QMainWindow搭建左右分栏布局:左侧为功能控制面板,右侧为奖状预览画布,界面固定宽度、配色柔和、控件间距合理,实现美观大方、不透明的高级视觉效果,同时集成所有核心功能按钮。
# 奖状生成器主窗口classCertificateGenerator(QMainWindow):def__init__(self): super().__init__() self.setWindowTitle("奖状生成器|批量导入下载版") self.resize(1200,800) # 固定窗口大小 self.template_path = None# 模板路径 self.template_pix = None# 模板图片 self.scale_factor = 1.0# 缩放比例 self.name_list = [] # 存储导入的姓名列表 self.init_ui() # 初始化界面 self.init_drag_labels() # 初始化拖拽标签# 初始化UI界面:左右分栏布局definit_ui(self): central = QWidget() self.setCentralWidget(central) main_h = QHBoxLayout(central)# 左侧功能面板:固定宽度,浅灰色背景 left_widget = QWidget() left_widget.setFixedWidth(320) left_widget.setStyleSheet("background-color:#f8f9fa;border-right:1px solid #ddd;") left_v = QVBoxLayout(left_widget) left_v.setAlignment(Qt.AlignTop) left_v.setSpacing(8) left_v.setContentsMargins(15,15,15,15)# 模板选择按钮 self.btn_template = QPushButton("📂 选择奖状模板") self.btn_template.setStyleSheet("height:35px;font-size:14px;") self.btn_template.clicked.connect(self.select_template) self.label_template_name = QLabel("未选择模板") self.label_template_name.setAlignment(Qt.AlignCenter) left_v.addWidget(self.btn_template) left_v.addWidget(self.label_template_name) left_v.addSpacing(10)# Excel批量导入模块 left_v.addWidget(QLabel("=== 批量导入 ===")) self.btn_import_xls = QPushButton("📄 导入XLS姓名列表") self.btn_import_xls.setStyleSheet("height:35px;font-size:14px;") self.btn_import_xls.clicked.connect(self.import_xls_names) self.label_import_count = QLabel("已导入:0人") self.label_import_count.setAlignment(Qt.AlignCenter) left_v.addWidget(self.btn_import_xls) left_v.addWidget(self.label_import_count) left_v.addSpacing(10)# 批量下载按钮 self.btn_batch_save = QPushButton("📥 批量下载所有奖状") self.btn_batch_save.setStyleSheet("height:40px;font-size:14px;background-color:#28a745;color:white;") self.btn_batch_save.clicked.connect(self.batch_save_certificates) left_v.addWidget(self.btn_batch_save) left_v.addSpacing(10)# 画布缩放模块 zoom_h = QHBoxLayout() self.btn_zoom_in = QPushButton("🔍 放大 +") self.btn_zoom_out = QPushButton("🔍 缩小 -") self.btn_reset = QPushButton("重置缩放") self.btn_zoom_in.clicked.connect(lambda:self.zoom(1.1)) self.btn_zoom_out.clicked.connect(lambda:self.zoom(0.9)) self.btn_reset.clicked.connect(self.zoom_reset) zoom_h.addWidget(self.btn_zoom_in) zoom_h.addWidget(self.btn_zoom_out) zoom_h.addWidget(self.btn_reset) left_v.addLayout(zoom_h) left_v.addSpacing(10)# 文字内容输入模块 left_v.addWidget(QLabel("=== 文字内容设置 ===")) self.edit_name = QLineEdit("张三") self.edit_reward = QLineEdit("优秀学生标兵") self.edit_time = QLineEdit("2026年03月31日") self.edit_school = QLineEdit("青岛市第一实验小学")for edit in [self.edit_name,self.edit_reward,self.edit_time,self.edit_school]: edit.setStyleSheet("height:30px;font-size:14px;") left_v.addWidget(QLabel("姓名:")) left_v.addWidget(self.edit_name) left_v.addWidget(QLabel("奖励名称:")) left_v.addWidget(self.edit_reward) left_v.addWidget(QLabel("时间:")) left_v.addWidget(self.edit_time) left_v.addWidget(QLabel("单位:")) left_v.addWidget(self.edit_school) left_v.addSpacing(10)# 字号调整模块 left_v.addWidget(QLabel("=== 全局字号 ===")) self.spin_font = QSpinBox() self.spin_font.setRange(12,120) self.spin_font.setValue(36) self.spin_font.setStyleSheet("height:30px;font-size:14px;") self.spin_font.valueChanged.connect(self.set_all_font_size) left_v.addWidget(self.spin_font) left_v.addSpacing(20)# 操作按钮:刷新、保存、打印 self.btn_refresh = QPushButton("🔄 刷新文字") self.btn_save = QPushButton("💾 单张下载成品") self.btn_preview = QPushButton("🖨️ 打印预览") self.btn_print = QPushButton("直接打印")for btn in [self.btn_refresh,self.btn_save,self.btn_preview,self.btn_print]: btn.setStyleSheet("height:38px;font-size:14px;") self.btn_refresh.clicked.connect(self.refresh_text) self.btn_save.clicked.connect(self.save_visible_canvas) self.btn_preview.clicked.connect(self.print_preview_canvas) self.btn_print.clicked.connect(self.print_canvas) left_v.addWidget(self.btn_refresh) left_v.addWidget(self.btn_save) left_v.addWidget(self.btn_preview) left_v.addWidget(self.btn_print)# 右侧画布:滚动区域+奖状预览 right_widget = QWidget() right_v = QVBoxLayout(right_widget) self.scroll_area = QScrollArea() self.scroll_area.setWidgetResizable(True) self.canvas_container = QWidget() self.canvas_layout = QStackedLayout(self.canvas_container) self.bg_label = QLabel() self.bg_label.setAlignment(Qt.AlignCenter) self.bg_label.setStyleSheet("background:#eee; border:none;") self.canvas_layout.addWidget(self.bg_label) self.scroll_area.setWidget(self.canvas_container) right_v.addWidget(self.scroll_area) main_h.addWidget(left_widget) main_h.addWidget(right_widget)# 初始化拖拽标签:创建4个核心文字标签并预设位置definit_drag_labels(self): self.drag_name = DraggableLabel("张三") self.drag_reward = DraggableLabel("优秀学生标兵") self.drag_time = DraggableLabel("2026年03月31日") self.drag_school = DraggableLabel("青岛市第一实验小学")for lab in [self.drag_name,self.drag_reward,self.drag_time,self.drag_school]: lab.font_size = 36 lab.update_font()# 将标签绑定到画布上 self.drag_name.setParent(self.bg_label) self.drag_reward.setParent(self.bg_label) self.drag_time.setParent(self.bg_label) self.drag_school.setParent(self.bg_label)# 预设初始位置 self.drag_name.move(200,180) self.drag_reward.move(200,320) self.drag_time.move(400,550) self.drag_school.move(200,620)init_drag_labels创建姓名、奖励、时间、单位四个拖拽标签,绑定到画布上并预设位置,用户可直接拖拽调整。这是工具的灵魂模块,实现Excel姓名批量导入、奖状模板缩放、单张/批量高清导出、打印预览+直接打印功能,解决传统奖状制作的所有痛点,代码逻辑严谨,支持异常处理,运行稳定无卡顿。
# ========== 导入Excel姓名列表 ==========defimport_xls_names(self): path, _ = QFileDialog.getOpenFileName(self, "选择Excel文件", "", "Excel文件(*.xls)")ifnot path:returntry: workbook = xlrd.open_workbook(path) sheet = workbook.sheet_by_index(0) self.name_list = []# 读取第一列所有非空姓名for row in range(sheet.nrows): name = str(sheet.cell_value(row, 0)).strip()if name: self.name_list.append(name) self.label_import_count.setText(f"已导入:{len(self.name_list)}人") QMessageBox.information(self, "导入成功", f"成功导入 {len(self.name_list)} 个姓名!")except Exception as e: QMessageBox.critical(self, "导入失败", f"请使用.xls格式文件\n错误信息:{str(e)}")# 生成唯一文件名:姓名+时间+随机数defgenerate_filename(self, name): time_str = datetime.now().strftime("%Y%m%d%H%M%S") rand_str = str(random.randint(10000, 99999))returnf"{name}_{time_str}_{rand_str}.png"# ========== 批量下载所有奖状 ==========defbatch_save_certificates(self):ifnot self.template_pix: QMessageBox.warning(self, "警告", "请先选择奖状模板!")return# 选择保存目录 save_dir = QFileDialog.getExistingDirectory(self, "选择保存文件夹")ifnot save_dir:return# 获取待生成姓名列表 target_names = self.name_list if self.name_list else [self.edit_name.text()] success_count = 0 total = len(target_names)# 循环生成奖状for name in target_names:try: self.drag_name.setText(name) QApplication.processEvents() # 刷新界面 pix = self.get_canvas_pixmap()# 保存图片 filename = self.generate_filename(name) save_path = os.path.join(save_dir, filename) pix.save(save_path, "PNG", 100) success_count += 1except Exception as e: print(f"生成失败 {name}:{e}") QMessageBox.information(self, "批量完成", f"共处理:{total} 张\n成功生成:{success_count} 张")# 选择奖状模板并缩放defselect_template(self): path,_ = QFileDialog.getOpenFileName(self,"选择奖状模板","","图片(*.jpg *.png *.jpeg *.bmp)")ifnot path: return self.template_path = path self.label_template_name.setText(os.path.basename(path)) self.template_pix = QPixmap(path) self.apply_zoom()# 画布缩放功能defzoom(self, rate): self.scale_factor *= rate self.scale_factor = max(0.3, min(3.0, self.scale_factor)) self.apply_zoom()defzoom_reset(self): self.scale_factor = 1.0 self.apply_zoom()defapply_zoom(self):ifnot self.template_pix: return w = int(self.template_pix.width() * self.scale_factor) h = int(self.template_pix.height() * self.scale_factor) scaled_pix = self.template_pix.scaled(w,h,Qt.KeepAspectRatio,Qt.SmoothTransformation) self.bg_label.setPixmap(scaled_pix) self.bg_label.resize(scaled_pix.size())# 刷新文字内容defrefresh_text(self): self.drag_name.setText(self.edit_name.text()) self.drag_reward.setText(self.edit_reward.text()) self.drag_time.setText(self.edit_time.text()) self.drag_school.setText(self.edit_school.text())# 统一调整字号defset_all_font_size(self, size):for lab in [self.drag_name,self.drag_reward,self.drag_time,self.drag_school]: lab.font_size = size lab.update_font()# 核心:截取画布高清图片defget_canvas_pixmap(self):ifnot self.bg_label.pixmap() or self.bg_label.pixmap().isNull():returnNone pix = QPixmap(self.bg_label.size()) self.bg_label.render(pix)return pix# 单张导出奖状defsave_visible_canvas(self): pix = self.get_canvas_pixmap()ifnot pix: QMessageBox.warning(self,"警告","请先选择模板!")return path, _ = QFileDialog.getSaveFileName(self,"保存成品",self.generate_filename(self.edit_name.text()),"PNG图片(*.png)")if path: pix.save(path, "PNG", 100) QMessageBox.information(self,"成功","已保存无框干净成品!")# 打印预览defprint_preview_canvas(self): pix = self.get_canvas_pixmap()ifnot pix: QMessageBox.warning(self,"警告","请先选择模板!")return p = QPrinter(QPrinter.HighResolution) dlg = QPrintPreviewDialog(p) dlg.paintRequested.connect(lambda pr: self.draw_pix(pr, pix)) dlg.exec()# 直接打印defprint_canvas(self): pix = self.get_canvas_pixmap()ifnot pix: QMessageBox.warning(self,"警告","请先选择模板!")return p = QPrinter(QPrinter.HighResolution) dlg = QPrintDialog(p)if dlg.exec_(): self.draw_pix(p, pix)# 打印绘制图片defdraw_pix(self, printer, pix): pt = QPainter(printer) rect = pt.viewport() size = pix.size() size.scale(rect.size(), Qt.KeepAspectRatio) pt.setViewport(rect.x(), rect.y(), size.width(), size.height()) pt.setWindow(pix.rect()) pt.drawPixmap(0, 0, pix)# 主程序运行if __name__ == "__main__": app = QApplication(sys.argv) app.setStyle(QStyleFactory.create("Fusion")) win = CertificateGenerator() win.show() sys.exit(app.exec_())xlrd库读取.xls格式文件,提取第一列姓名,支持空值过滤,异常处理提示格式错误;get_canvas_pixmap实现画布无框高清截图,支持单张导出、批量导出、打印预览、直接打印,满足所有使用场景。pip install PyQt5 xlrd==1.2.0;这款Python奖状生成器,用极简的代码实现了超全的功能,界面精致高级、操作简单易用,完美解决了批量制作奖状的痛点。无论是日常使用还是学习PyQt5开发,都是不可多得的优质案例,赶紧收藏试用,让效率直接拉满!
通过网盘分享的文件:奖状批量生成器.exe
链接: https://pan.baidu.com/s/1-1RraGHWUwCYVZKKhVu-uw?pwd=4444 提取码: 4444