Python+Stata:批量制作个性化结业证书

发布时间:2020-09-06 阅读 2873

Stata 连享会   主页 || 视频 || 推文

温馨提示: 定期 清理浏览器缓存,可以获得最佳浏览体验。

课程详情 https://gitee.com/arlionn/Course   |   lianxh.cn

课程主页 https://gitee.com/arlionn/Course

作者:王俊(中山大学岭南学院)

Email: wangj676@mail2.sysu.edu.cn


目录


1. 需求

事情缘起:半个月前,接到老师的一项任务,能否批量生成连享会的结业证书。

结业证书的模版是这样的。简单观察能够发现共需要填写 7 个空格,分别是姓名、单位、月份1、日期1、月份2、日期2以及课时数。手工去一个个从 Excel 中找到信息,并对图片进行加工实在是耗时耗力,还容易出错,因此有没有程序化的办法呢?

此外,部分学员并不要求将学校信息打印在证书上,因此,还有另外一个模版(该模版所填信息较少,因此下文主要围绕第一个模版来展开生成证书的过程),如图所示:

2. 初步想法

计算机最适合做程序化的事情了。我的想法如下:

  • 首先,导入 Excel
  • 写好循环
  • 在每张图片上加上学员的个人信息

如此一来,工作基本就完成了。

可是问题又来了,第1-2步很简单,而最关键的一步是:如何在图片上加上我们想要的信息呢?果然,万能的网络让我找到了解决办法——python批量添加姓名生成奖状批量处理图片教师学生奖状。利用 Python 中的 PIL 模块即可实现读写图片的目的。

核心代码如下:

# 以下代码仅示范写入学员名称
from PIL import Image,ImageDraw,ImageFont
# 读取图片
old_img = Image.open(r"lxh.png")
draw = ImageDraw.Draw(old_img)
left = 250  #left 代表字符离左边距的距离
height = 1700 # height 代表字符的高度
# 设置图片文字,字体类型,以及字体大小
newfont=ImageFont.truetype('Songti.ttc',60)  # Songti.ttc 代表字体,60 代表字号
draw.text((left, height),"学员名",font=newfont,fill="black") 
# old_img.show()    #运行显示
old_img.save('编号+学员名.png')

这样一来,所有的问题都已经解决了。我们仅需写好函数,设置好相关参数即可。

主要参数如下:

  • 字体类型(固定)
  • 字体大小(可变)
  • 字符的左边距(可变)
  • 字符的高度(可变)
  • 字符颜色(固定)

值得注意的是,字体类型、颜色在整个过程中都是一致的,我们设定为宋体黑色。但字体左边距是随着字符的位置和字符个数而转变,字体大小也会因为字符过多需要设定为小一号字体。因此,字体类型和颜色都已经固定下来,而字体大小、左边距、高度都是可变参数。当确定了以上所有元素后,我们需要的只是创建一个函数,并把这些可变参数填进去即可。

此外,因此我们简单看一下Excel文件的前几个观察值。除了上述提到的元素外,每个学员的姓名 (n)、学校 (s)、起始学习日期 (m1和d1)、结束学习日期 (m2和d2)和课时数 (t)都是有差异的。

序号 姓名 课程 单位 学时数 月份1 月份2 结束日1 结束日2
1 学生1 初级 24 7 7 28 30
2 学生2 初级┋高级 单位1 48 7 8 28 3
3 学生3 高级 24 8 8 1 3
4 学生4 论文 单位2 24 8 8 5 7
5 学生5 高级 单位3 24 8 8 1 3

因此,可变参数一共包含:字体大小、左边距、高度、姓名、学校、起始月、起始日、结束月、结束日以及课时数。

3. 编写函数

为此,我们编写了如下函数。

特别需要注意的是,部分学员要求提供带有学校信息的证书,而部分学员没有此类需求,那么需要两个不同的函数来做这件事情。但逻辑都是一样的,因此不再赘述。

# 要求录入学校信息的证书
def zs_school(size, left, height, n, c, m1, d1, m2, d2, t):
    newfont = ImageFont.truetype(font="Songti.ttc", size=size)
    draw.text((600,height),n, font = newfont, fill= "black")
    draw.text((left,1700),c, font = newfont, fill= "black")
    draw.text((1750,1700),m1, font = newfont, fill= "black")
    draw.text((1900,1700),d1, font = newfont, fill= "black")
    draw.text((300,1850),m2, font = newfont, fill= "black")
    draw.text((550,1850),d2, font = newfont, fill= "black")
    draw.text((520,2150),t, font = newfont, fill= "black")
    # old_img.show()    #运行显示
    old_img.save(str(int(l))+n+".png")

# 不要求录入学校信息的证书
def zs(n, m1, d1, m2, d2, t):
    # 设置图片文字,字体类型,以及字体大小,颜色
    newfont=ImageFont.truetype('Songti.ttc',100)
    draw.text((600,1380),n, font = newfont, fill= "black")
    draw.text((700,1700),m1, font = newfont, fill= "black")
    draw.text((850,1700),d1, font = newfont, fill= "black")
    draw.text((1170,1700),m2, font = newfont, fill= "black")
    draw.text((1380,1700),d2, font = newfont, fill= "black")
    draw.text((1400,2000),t, font = newfont, fill= "black")
    # old_img.show()    #运行显示
    old_img.save(str(int(l))+n+".png")

上述最关键的第三步已经完成,那么剩下的就只有循环遍历第二步了。

4. 循环生成全部证书

具体做法是:

  • 导入 Excel 文件
  • 将学员信息保存到列表中
  • 写循环语句,并在其中需要嵌入刚刚编写的函数

代码如下:

# 导入 Excel 文件
data = xlrd.open_workbook('table1.xlsx')
table = data.sheet_by_index(0)
# 给各列表变量赋值
list =  table.col_values(0)[0:]
name = table.col_values(1)[0:]
school = table.col_values(4)[0:]
time = table.col_values(10)[0:]
month1 = table.col_values(11)[0:]
month2 = table.col_values(12)[0:]
day1 = table.col_values(13)[0:]
day2 = table.col_values(14)[0:]
# 更改目录
os.chdir("/Users/wangj676/Desktop/pics")
# 循环生成新图片
for i in range(0,113):
    l = list[i]
    n = name[i]
    s = school[i]
    m1 = str(int(month1[i]))
    m2 = str(int(month2[i]))
    d1 = str(int(day1[i]))
    d2 = str(int(day2[i]))
    t = str(int(time[i]))
    c=str(s)+" "+str(n) # 学校+“空格”+姓名
    # 判断是否要求录入学校信息:是
    if school[i]!="":
        old_img = Image.open("/Users/wangj676/Desktop/lxh.png")
        draw = ImageDraw.Draw(old_img)
        # 判断字符长度,以便设置字体大小和左边距
        if len(c)>=10:
            # 设置位置
            length=len(c)
            left=600-22*length
            height = 1400
            size = 80
            # 调用 zs_school() 函数
            zs_school(size, left, height, n, c, m1, d1, m2, d2, t)
        else:
            # 设置位置
            length=len(c)
            left=600-18*length
            height = 1380
            size = 100
            # 调用 zs_school() 函数
            zs_school(size, left, height, n, c, m1, d1, m2, d2, t)

    # 判断是否要求录入学校信息:否
    if school[i]=="":
        old_img = Image.open("/Users/wangj676/Desktop/lxh0.png")
        draw = ImageDraw.Draw(old_img)
        # 调用 zs() 函数
        zs(n, c, m1, d1, m2, d2, t)

运行以上代码,就可以为每位学员生成完美的结业证书啦!如图所示:

5. 在 Stata 中运行以上代码

5.1 关联 python

首先,需要从官网或者Anaconda下载pythonStata支持 Python 2.7 及以上的版本,第一次调用时,Stata 会自动搜索系统中通过官网、Anaconda安装的 Python 。

如果不确定 Stata 是否已经和 python 关联起来,可以输入 python search 来搜索系统中所有可用的版本,也可以输入 python query 来查看当前配置版本和系统信息。

# Mac 版本下的信息如下,Windows 也可以这样查看相关信息
. python search
----------------------------------------------------------------------------
 Python environments found:  
 /usr/bin/python
 /usr/local/bin/python3
-------------------------------
. python query
----------------------------------------------------------------------------
    Python Settings
      set python_exec      /usr/local/bin/python3
      set python_userpath  
    Python system information
      initialized          yes
      version              3.8.5
      architecture         64-bit
      library path         /usr/local/opt/python@3.8/Frameworks/Python.framework/Versions/3.8/lib/python3.8/config-3.8-darwin/libpython3.8.dylib

5.2 交互式运行 python 代码

在 dofile 中输入 pythonpython: 进入 python 环境,而后通过 end退出即可。

如下所示:

python
运行所需的代码
end

5.3 脚本式运行 python 代码

将 py 代码单独保存为一份 .py 的文件,而后可以通过 python script codes.py 来调用即可。

Stata16 调用 python 的操作可详细查看 在Stata16中如何快速配置Python?如何在Stata16中调用Python 以及 Stata与Python等效操作与调用

当然,本文并没有涉及到 Stata16python的交互,所以直接执行 python 代码即可,但Statapython的交互是一种趋势,可以在日后的工作学习中经常使用以增加对于此类用法的熟悉程度。

最后附上生成证书的全部 python 代码:

import os
from PIL import Image,ImageDraw,ImageFont
import xlrd

# 要求录入学校信息的证书
def zs_school(size, left, height, n, c, m1, d1, m2, d2, t):
    newfont = ImageFont.truetype(font="Songti.ttc", size=size)
    draw.text((600,height),n, font = newfont, fill= "black")
    draw.text((left,1700),c, font = newfont, fill= "black")
    draw.text((1750,1700),m1, font = newfont, fill= "black")
    draw.text((1900,1700),d1, font = newfont, fill= "black")
    draw.text((300,1850),m2, font = newfont, fill= "black")
    draw.text((550,1850),d2, font = newfont, fill= "black")
    draw.text((520,2150),t, font = newfont, fill= "black")
    # old_img.show()    #运行显示
    old_img.save(str(int(l))+n+".png")

# 不要求录入学校信息的证书
def zs(n, m1, d1, m2, d2, t):
    # 设置图片文字,字体类型,以及字体大小,颜色
    newfont=ImageFont.truetype('Songti.ttc',100)
    draw.text((600,1380),n, font = newfont, fill= "black")
    draw.text((700,1700),m1, font = newfont, fill= "black")
    draw.text((850,1700),d1, font = newfont, fill= "black")
    draw.text((1170,1700),m2, font = newfont, fill= "black")
    draw.text((1380,1700),d2, font = newfont, fill= "black")
    draw.text((1400,2000),t, font = newfont, fill= "black")
    # old_img.show()    #运行显示
    old_img.save(str(int(l))+n+".png")

# 导入 Excel 文件
data = xlrd.open_workbook('/Users/wangj676/Desktop/table1.xlsx')
table = data.sheet_by_index(0)
# 给各列表变量赋值
list =  table.col_values(0)[0:]
name = table.col_values(1)[0:]
school = table.col_values(4)[0:]
time = table.col_values(10)[0:]
month1 = table.col_values(11)[0:]
month2 = table.col_values(12)[0:]
day1 = table.col_values(13)[0:]
day2 = table.col_values(14)[0:]

os.chdir("/Users/wangj676/Desktop/pics")
# 循环生成新图片
for i in range(0,113):
    l = list[i]
    n = name[i]
    s = school[i]
    m1 = str(int(month1[i]))
    m2 = str(int(month2[i]))
    d1 = str(int(day1[i]))
    d2 = str(int(day2[i]))
    t = str(int(time[i]))
    c=str(s)+" "+str(n) # 学校+“空格”+姓名
    # 判断是否要求录入学校信息:是
    if school[i]!="":
        old_img = Image.open("/Users/wangj676/Desktop/lxh.png")
        draw = ImageDraw.Draw(old_img)
        # 判断字符长度,以便设置字体大小和左边距
        if len(c)>=10:
            # 设置位置
            length=len(c)
            left=600-22*length
            height = 1400
            size = 80
            # 调用 zs_school() 函数
            zs_school(size, left, height, n, c, m1, d1, m2, d2, t)
        else:
            # 设置位置
            length=len(c)
            left=600-18*length
            height = 1380
            size = 100
            # 调用 zs_school() 函数
            zs_school(size, left, height, n, c, m1, d1, m2, d2, t)

    # 判断是否要求录入学校信息:否
    if school[i]=="":
        old_img = Image.open("/Users/wangj676/Desktop/lxh0.png")
        draw = ImageDraw.Draw(old_img)
        # 调用 zs() 函数
        zs(n, c, m1, d1, m2, d2, t)

6. 参考资料

相关课程

连享会-直播课 上线了!
http://lianxh.duanshu.com

免费公开课:


课程一览

支持回看,所有课程可以随时购买观看。

专题 嘉宾 直播/回看视频
最新专题 DSGE, 因果推断, 空间计量等
Stata数据清洗 游万海 直播, 2 小时,已上线
研究设计 连玉君 我的特斯拉-实证研究设计-幻灯片-
面板模型 连玉君 动态面板模型-幻灯片-
面板模型 连玉君 直击面板数据模型 [免费公开课,2小时]

Note: 部分课程的资料,PPT 等可以前往 连享会-直播课 主页查看,下载。


关于我们

  • Stata连享会 由中山大学连玉君老师团队创办,定期分享实证分析经验。直播间 有很多视频课程,可以随时观看。
  • 连享会-主页知乎专栏,300+ 推文,实证分析不再抓狂。
  • 公众号推文分类: 计量专题 | 分类推文 | 资源工具。推文分成 内生性 | 空间计量 | 时序面板 | 结果输出 | 交乘调节 五类,主流方法介绍一目了然:DID, RDD, IV, GMM, FE, Probit 等。
  • 公众号关键词搜索/回复 功能已经上线。大家可以在公众号左下角点击键盘图标,输入简要关键词,以便快速呈现历史推文,获取工具软件和数据下载。常见关键词:课程, 直播, 视频, 客服, 模型设定, 研究设计, stata, plus, 绘图, 编程, 面板, 论文重现, 可视化, RDD, DID, PSM, 合成控制法

连享会主页  lianxh.cn
连享会主页 lianxh.cn

连享会小程序:扫一扫,看推文,看视频……

扫码加入连享会微信群,提问交流更方便

✏ 连享会学习群-常见问题解答汇总:
https://gitee.com/arlionn/WD