你们还记得上一篇文章0.来学点Python吧!从一个斗图小工具开始中最后提到的几个问题么,咱们此次就来解决一下其中难度最大的一个:文本居中!python
上次以后,我偷偷把代码优化了,如今的main方法长这样:git
# -*- coding:utf-8 -*- #__author__ = 'akers' from io import BytesIO from PIL import Image, ImageDraw, ImageFont import sys,platform,operators.image,operators.clipboard def main(argv): # 建立表情图 image = operators.image.draw_emo('./resources/background/pander/default.png', './resources/face/jgz/laugth.png', argv[1]) output = BytesIO() image.save(output, format="BMP") operators.clipboard.add_bmp(output) image.save('output/facing.png') # 调试用 # plt.figure("生成表情包") # plt.imshow(target) # plt.show() if __name__ == "__main__": main(sys.argv)
对的,我把那些老长老长的代码藏到别的包里了,下面是一点题外话,顺便介绍一下Python的包与模块github
以咱们重构后的项目为例,一个项目的结构大体以下segmentfault
python中的包就是一个包含了__init__.py文件的文件夹,包下可有子包函数
python的模块就是一个py文件,其中可包括类、函数、变量等等内容工具
python中使用import
关键字引入模块,import的使用分如下几种优化
导入指定的模块,调用时需使用模块全名,例如spa
import operators.image operators.image.draw_emo
导入指定模块,调用时直接使用模块名调试
from operators import image image.draw_emo()
导入模块中的函数或成员、类、变量等code
from operators.image import draw_emo draw_emo()
Python的包跟模块的内容还有不少,更详细的就先不赘述了,而后让咱们继续主题,咳咳
首先,咱们来实现文本的居中,咱们先来回顾一下咱们是如何插入文本的:
#底图上的10,200位置写入文字 draw.text((10, 200), argv[1],fill='black', font=font)
text函数的第一个参数须要个元组,而元组中的0、1两个元素分别表明的x和y的坐标(以像素为单位),那实现居中就很简单了,咱们只须要根据插入文本的具体宽度计算出一个合适的x坐标就能够了。
因而请看大屏幕,啊呸,请看下图,
从图中咱们能够看到,文本的插入坐标能够用这样的公式进行计算:(底图宽-文本宽) / 2,那问题又来了,文本宽咱们怎么获得呢?
在PIL中,咱们能够用draw.textsize获取文本所占的大小,返回是个元组:(宽,高),像这样:
txtSize = draw.textsize(text, imageFont)
因此咱们修改一下draw_text方法:对x坐标进行计算
def draw_text_v1(text, image, off_set=200): """强化版绘制文字v1,让文字在x轴上居中 Args: text: 显示在图片上的文本 image: 当前正使用的Image off_set: 纵向偏移量 Returns: Image Raises: """ # 加入文字 # ImageDraw为PIL的绘图模块 _DEFAULT_FONT_SIZE = 30 draw = ImageDraw.Draw(image) imageFont = ImageFont.truetype('./resources/msyh.ttc', _DEFAULT_FONT_SIZE) _MAX_TXT_HEIGH = 32 txtSize = draw.textsize(text, imageFont) pos_x = (CONST_IMG_WIDTH - txtSize[0]) / 2 if CONST_IMG_WIDTH > txtSize[0] else 0 print("当前X坐标", pos_x) # 默认显示位置 pos = (pos_x, off_set) draw.text(pos, text, fill='black', font=imageFont) del draw return image
相信眼尖的强迫症患者可能会发现,这里面混着一个很奇怪的写法:
(CONST_IMG_WIDTH - txtSize[0]) / 2 if CONST_IMG_WIDTH > txtSize[0] else 0
这个if跟else是否是有点调皮,其实这个在python 里相似于c语系里的 ? :三目运算符:若是if后的条件成立,则取if前的表达式进行返回,不然取else后的表达式,上面的代码实际等同于:
CONST_IMG_WIDTH > txtSize[0] ? (CONST_IMG_WIDTH - txtSize[0]) / 2 : 0
这个细节又暴露了Python的哲学:我已经有了if else了,还要? :干吗,关键字的数量也要最简;而个语法应该也是从C语系转过来的小伙伴以为最别扭的语法之一了吧....
好废话很少说,来让咱们看看效果,很中对不对:
正所谓要精益求精,既然都作了居中,索性把对齐作个参数好了,跟居中同样的原理,我把左对齐跟右对齐也作了,只须要对draw_text_v1作一点点润色:
def draw_text_v2(text, image, off_set=(0, 200), allign='center'): """强化版绘制文字v2,左中右,想放哪里放哪里 Args: text: 显示在图片上的文本 image: 当前正使用的Image off_set: 偏移量,用于保留最小编剧,(x, y)以像素未单位 allign: 排版,left左对齐,center居中,right右对齐 Returns: Image Raises: """ # 加入文字 # ImageDraw为PIL的绘图模块 _DEFAULT_FONT_SIZE = 30 draw = ImageDraw.Draw(image) imageFont = ImageFont.truetype('./resources/msyh.ttc', _DEFAULT_FONT_SIZE) _MAX_TXT_HEIGH = 32 txtSize = draw.textsize(text, imageFont) imageFont = ImageFont.truetype('./resources/msyh.ttc', 30) # 计算x坐标 pos_x = { # 居中对齐 'center': lambda max_width, txt_len, off: (max_width / 2 - txt_len / 2 if max_width > txt_len else 0) + off, # 左对齐 'left': lambda max_width, txt_len, off: (off if max_width > txt_len else 0), # 右对齐 'right': lambda max_width, txt_len, off: (max_width - txt_len if max_width > txt_len else 0) }[allign if allign in ('center', 'left', 'right') else 'center'](CONST_IMG_WIDTH - 2*off_set[0], txtSize[0], off_set[0]) # 默认显示位置 pos = (pos_x, off_set[1]) # 底图上的10,200位置写入文字 draw.text(pos, text, fill='black', font=imageFont) del draw return image
效果好像还不错:
估计又有眼尖的小伙伴发现有个代码不对了,怎么pos_x的计算实现是如此的奇葩,我都看不懂了!
pos_x = { # 居中对齐 'center': lambda max_width, txt_len, off: (max_width / 2 - txt_len / 2 if max_width > txt_len else 0) + off, # 左对齐 'left': lambda max_width, txt_len, off: (off if max_width > txt_len else 0), # 右对齐 'right': lambda max_width, txt_len, off: (max_width - txt_len if max_width > txt_len else 0) }[allign if allign in ('center', 'left', 'right') else 'center'](CONST_IMG_WIDTH - 2*off_set[0], txtSize[0], off_set[0])
其实这里嘛,是Python实现switch功能的一个奇技淫巧...对的,你猜的没错,Python是没有switch结构的,由于:“咱们已经有了完美的if...else...要switch干吗”,因此若是按照官方建议,这段代码应该是这样的:
max_width = CONST_IMG_WIDTH - 2*off_set[0] txt_len = txtSize[0] if allign == 'center': pos_x = (max_width / 2 - txt_len / 2 if max_width > txt_len else 0) + off_set[0] else if allign == 'left': pos_x = off_set[0] if CONST_IMG_WIDTH - 2*off_set[0] > txt_len else 0 else if allign == 'right': pos_x = max_width - txt_len if max_width > txt_len else 0 else: pos_x = ((max_width / 2 - txt_len / 2 if max_width > txt_len else 0) + off_set[0]
而我在实现上是采用了字典结构加上lamada表达式进行实现的,这里再多嘴一句,python的lamada表达式只支持单行只支持单行!由于官方以为若是一个lamada表达式须要多行才能解决问题,那你仍是去定义个函数吧!
啊?你说上面的代码仍是没搞懂?嗯...好吧,那我把代码好好讲讲。
首先,代码是使用了一些简写的,咱们把它拆出来:
def center(max_width, txt_len, off): return (max_width / 2 - txt_len / 2 if max_width > txt_len else 0) + off def left(max_width, txt_len, off): return off if max_width > txt_len else 0 def right(max_width, txt_len, off): return max_width - txt_len if max_width > txt_len else 0 dict_temp = { # 居中对齐 'center': center, # 左对齐 'left': left, # 右对齐 'right': right } pos_x_func = dict_temp[allign if allign in ('center', 'left', 'right') else 'center'] pos_x = pos_x_func(CONST_IMG_WIDTH - 2*off_set[0], txtSize[0], off_set[0])
得益于python动态语言的特性,咱们能够把全部的中间变量都去掉,就造成了样例中的那种代码结构了,这个属于加大阅读难度的(但代码看上去简洁了啊!)你们搞不懂的也无所谓,我就主要简单介绍一下lamada表达式了。
从上面分解的代码能够看出,lamada实际上也是函数,有入参有返回值,但就是没了个名字,因此他其实是一个匿名函数,其构成是这样的:
lamada 参数1 [, 参数n] : 表达式
记住了,Python不支持多行lamada!
感受此次文章基础语法扯的有点多了,以后的文章对基础语法的介绍会进一步减小的!
本次的代码样例一样在个人GitHub上能够找到
好的,那么接下来
额...好吧,下次咱们就来解决上面这个问题!