开始搞之前我们先来看看成果:
写这个玩意是因为咱工作的日期和大部分人是不一样的,我又懒得下载一个app只为了看值班情况,有了这个程序我就可以快乐的给图片扔给他们来告诉他们啥时候可以找我了。
程序分析
这个程序比较简单,首先用户输入要生成的月份,年份,哪一天上班,上多久,休息几天。之后根据用户输入的月份和年份,使用一个二维数组来保存每个月的状态,最后,每天使用一个长度为三的数组保存,
[日期,是否休假,农历]
然后调用pillow画个图就搞定了。
开始编程
先创建一个文件,编辑好主方法和引用。
1 2 3 4 5 6 7 |
from PIL import Image,ImageDraw,ImageFont from datetime import date, datetime from zhdate import ZhDate if __name__ == "__main__": year = 2022 month = 8 |
下一步,根据之前的分析,我们来制作生成每个月数组的类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
def get_month_daysnum(year,month): # 首先把已知的月份加进去。 if month in [1,3,5,7,8,10,12]: return 31 elif month in [4,6,9,11]: return 30 # 计算闰年 elif month == 2: if (year % 4 == 0 and year % 100 != 0)or year % 400 == 0: return 29 else: return 28 else: return -1 def get_date_nl_chinese(datetime): date = ZhDate.from_datetime(datetime) nl = date.chinese() nl = nl.split("年") nl = nl[1].split("月") mm = nl[0] nl = nl[1].split(" ")[0] if nl == "初一": return str(mm+"月")[0:2] else: return nl[0:2] def make_paiban_list(year,month,start_day,ls_day,rest_day): # 获取总天数 count_day = get_month_daysnum(year,month) # 获取该月第一天周几 start_week = date(year, month, 1).weekday() # 因为是使用周日作为第一天的,所以要把周日修改成0. date_week_list_tmp = [1,2,3,4,5,6,0] flag = date_week_list_tmp[start_week] month_lists = [] tmp_list = [] isWork = False flag_day = -1 tmp_ls = -1 # 开始for循环,将日期之前的部分填写为负数 for i in range(0-flag,count_day): # 当每周生成完毕后填充到月份数组 if len(tmp_list) > 6: month_lists.append(tmp_list) tmp_list = [] # 小于零的日期填空,其他日期写日期 if i < 0: tmp_list.append(["",False,""]) else: if i+1 == start_day or flag_day == 0: isWork = True flag_day = rest_day tmp_ls = ls_day - 1 elif tmp_ls > 0: tmp_ls = tmp_ls - 1 isWork = True else: flag_day = flag_day - 1 isWork = False # 获取农历日期 tmp_list.append([str(i+1),isWork,get_date_nl_chinese(datetime(year,month,i+1))]) if len(tmp_list) != 0: # 对最后一周补零 for i in range(len(tmp_list),7): tmp_list.append(["",False,""]) month_lists.append(tmp_list) return month_lists |
这里使用了两个方法,get_month_daysnum用来获取一个特定月份的日期,另一个方法生成月份的数组,为了给每月1号之前的日期补零,函数使用0-flag的方法,并将小于负数的值填为空。
测试方法:
1 2 3 4 5 6 7 8 |
if __name__ == "__main__": year = 2022 month = 8 # 这里代表统计2022年8月的表格,1号为第一个工作日,上一天休息两天。 # 类似的,15号为第一个工作日,上四天休息八天可以写为: # make_paiban_list(year,month,15,4,8) list = make_paiban_list(year,month,1,1,2) print(list) |
此处输出了:
[[[‘ ‘, False, ”], [‘1’, True, ‘初四’], [‘2’, False, ‘初五’], [‘3’, False, ‘初六’], [‘4’, True, ‘初七’], [‘5’, False, ‘初八’], [‘6’, False, ‘初九’]], [[‘7’, True, ‘初十’], [‘8’, False, ‘十一’], [‘9’,False, ‘十二’], [’10’, True, ‘十三’], [’11’, False, ‘十四’], [’12’, False, ‘十五’], [’13’, True, ‘十六’]], [[’14’, False, ‘十七’], [’15’, False, ‘十八’], [’16’, True, ‘十九’], [’17’, False, ‘二十’], [’18’, False, ‘二十’], [’19’, True, ‘二十’], [’20’, False, ‘二十’]], [[’21’, False, ‘二十’], [’22’, True, ‘二十’], [’23’, False, ‘二十’], [’24’, False, ‘二十’], [’25’, True, ‘二十’], [’26’, False, ‘二十’], [’27’, False, ‘八月’]], [[’28’, True, ‘初二’], [’29’, False, ‘初三’], [’30’, False, ‘初四’], [’31’, True, ‘初五’], [‘ ‘, False, ”], [‘ ‘, False, ”], [‘ ‘, False, ”]]]
可以看到,8月1日为周一,初四,8月以外的日期都被空数据填充,该函数工作正常。接下来设计画图的类,这里使用了霞鹜文楷(点击跳转github),很不错的字体。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
def make_paiban_pic_n(year,month,month_list,filename): # 设置字体,这里选择了霞鹜文楷 font_month = ImageFont.truetype("LXGWWenKaiMonoTC-Regular.ttf", 50) font_nl = ImageFont.truetype("LXGWWenKaiMonoTC-Regular.ttf", 20) font_zb = ImageFont.truetype("LXGWWenKaiMonoTC-Regular.ttf", 30) # 新建一个背景文件,把 Draw 对象初始化 background = Image.new(mode = 'RGBA',size = (500,500),color = 'white') draw = ImageDraw.Draw(background) # 通过 textSize 函数获取文字所占面积 zbw,zbh = draw.textsize("今日值班", font=font_zb, spacing=6) nlw,nlh = draw.textsize("值\n班", font=font_nl, spacing=6) mtw,mth = draw.textsize("30", font=font_month, spacing=6) color_blue = (71,124,246) color_black = (26,26,26) color_grey = (130,130,130) color_green = (140,192,97) color_red = (224,102,75) color_white = (255,255,255) height_jg = 45 width_jg = 30 date_size_w = zbw + 16; date_size_h = nlh + 15 + zbh + 16 img_w = date_size_w * 7 + width_jg * 8 img_h = mth + zbh + date_size_h * len(month_list) + (len(month_list)+3) * height_jg img_size = (img_w,img_h) background = background.resize(img_size,Image.ANTIALIAS) draw = ImageDraw.Draw(background) # 描绘标题 draw.text((width_jg,height_jg), str(year)+"-"+str(month)+"月工作情况", fill=(0,0,0), font=font_month) #描绘周几标题 week_list = ["周日","周一","周二","周三","周四","周五","周六"] for i in range(0,7): tmp_size_width,tmp_size_heigth = draw.textsize(week_list[i], font=font_zb, spacing=6) bq = ((date_size_w - tmp_size_width)/2) if tmp_size_width < date_size_w else 0; draw.text((width_jg * ( i + 1 )+ date_size_w * i + bq+10,height_jg * 2 + mth), week_list[i], fill=(0,0,0), font=font_nl) #描绘日期 for w in range(0,len(month_list)): week = month_list[w] for i in range(0,len(week)): day = week[i] day_status = "值班" if day[1] else "休息" if day[2]!="": day_m = str(day[0]) day_nl = str(day[2][0:1]+"\n"+day[2][-1:]) zbw,zbh = draw.textsize(day_status, font=font_zb, spacing=6) nlw,nlh = draw.textsize(day_nl, font=font_nl, spacing=6) mtw,mth = draw.textsize(day_m, font=font_month, spacing=6) #确定x,y x,y = (width_jg * ( i + 1 )+ date_size_w * i,height_jg * ( w + 3 ) + mth + date_size_h * w ) dxpd,dypd = (date_size_w - mtw)/2,(date_size_h - zbh - 16-mth)/2-15 nlx,nly = (date_size_w - dxpd+mtw-nlw)/2+x+dxpd,(date_size_h-zbh-16-nlh)/2+y-13 rect_x,rect_y,rect_w,rect_h=x,y+date_size_h-zbh-16,date_size_w,zbh+16 zbx,zby = x + (date_size_w-zbw)/2,rect_y+(rect_h-zbh)/2 if day[1]: month_fill_color = color_black rect_fill_color = color_blue elif i == 0 or i == 6: month_fill_color = color_red rect_fill_color = color_red else: month_fill_color = color_black rect_fill_color = color_green draw.text((nlx,nly),day_nl,fill=color_grey,font=font_nl) draw.text((x + dxpd,y+dypd), day_m, fill=month_fill_color, font=font_month) drawRoundRec(background,draw,rect_fill_color,rect_x,rect_y,rect_w,rect_h,15) draw.text((zbx,zby),day_status,fill=color_white,font=font_zb) background.save(filename) |
首先使用ImageFont.truetype方法初始化了字体。之后设置了白色背景的background作为画布,ImageDraw.Draw作为画笔。为了计算画布的总体尺寸,使用三个 textSize作为基础坐标来进行运算。并定义了间隔 height_jg/width_jg和出现的几种颜色。
在这里,date_size_w/h代表了每个日期的长和宽,这里不计算中间间隔,在img_w/h的部分计算了各个间隔,并将这两个值作为画布的大小对background进行resize操作。
之后依次描绘了标题和每周的周几。日期部分根据每天的日期不同、值班状态不同,重新计算了textSize,并根据值调整了每个文字描绘的位置。这一部分的算式比较复杂,但是参考标题图应该不难理解。
最后,根据状态描绘并保存到filename中,程序就完成了。
修改主程序:
1 2 3 4 5 6 7 8 9 |
if __name__ == "__main__": year = 2022 month = 8 # 这里代表统计2022年8月的表格,1号为第一个工作日,上一天休息两天。 # 类似的,15号为第一个工作日,上四天休息八天可以写为: # make_paiban_list(year,month,15,4,8) list = make_paiban_listLS(year,month,1,1,2) print(list) make_paiban_pic_n(year,month,list,str(year)+"-"+str(month)+".png") |
执行,就可以在你的文件夹里查看到这个图片了。
具体的内容已经上传到了我的Github,可以直接下载代码进行查看。