准备reportlab表格视频的材料时发现了一个挺实用的程序(http://bit.ly/2mOO3lQ),改吧改吧可用作应用实例。这个程序把指定文件夹为根的目录树上的超过1M的文件都找出来,然后把跟这些文件相关的信息制成表格输出到PDF文件中,生成的PDF文件大概长这样:
生成表格最关键的一步就是取数据,在第三方模块pathlib的帮助下遍历目录树取超过1M的文件,获取文件信息。如果q指向给定的文件夹,遍历以这个文件夹为根的目录树用
for p in q.glob('**/*.*'):
‘**/*.*’不可以替换成‘*/*.*’,不然只遍历q文件夹下面一级的文件和文件夹,不会进入到子目录、子目录的子目录……。
数据取出来之后还要整理,整理进列表,列表的元素还是列表,对应表格的行数据。数据整理完毕,生成表格对象,定义表格格式。添加进PDF文件页面时还需要判断一下页面放不放得下表格,如果放不下页面要放倒(横放)。除了放表格,生成的PDF文件里还会放一个标题。定义一个类DataToPdf来做这些工作。
from operator import itemgetter
from reportlab.lib import colors
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.platypus import Paragraph, SimpleDocTemplate, Spacer, \
Table, TableStyle
# 注册中文字体
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.pdfbase import pdfmetrics
pdfmetrics.registerFont(TTFont('lively', '/Library/Fonts/Chinese/ChaoZiSheZengYuBoShouShuJian-2.ttf'))
pdfmetrics.registerFont(TTFont('apple', '/Library/Fonts/Chinese/XiaoHuYao-2.ttf'))
class DataToPdf():
"""
输出文件信息到到PDF文件的表格中去
"""
def __init__(self, fields, data, sort_by=None, title=None):
"""
参数解释:
fields - 元组,元组元素也是元组,存放表头和读取数据的键值
data - 列表,列表元素是存放文件信息的字典,字典键值也在fields里
sort_by - 元组(排列的关键字,顺序),指定依据什么排列和排列顺序:ACS升序或DESC降序
title - 文件的标题
"""
self.fields = fields
self.data = data
self.title = title
self.sort_by = sort_by
def export(self, filename, data_align='LEFT', table_halign='CENTRE'):
"""
数据输出到PDF文件中
参数解释:
filename - PDF的文件名
data_align - 表格里数据的对齐方式(比如'LEFT', 'CENTER', 'RIGHT')
table_halign - 整个表格在页面里的对齐方式(比如'LEFT', 'CENTER', 'RIGHT')
"""
# 标题格式
styles = getSampleStyleSheet()
styleH = styles['Heading1'] # 标题模版上略作修改
styleH.fontName = 'lively' # 字体改成中文字体
styleH.alignment = 1 # 居中对齐,默认是靠左(左0中1右2)
story = [] # 列表存放flowable
if self.title: # 生成对象时如果给了标题参数……
story.append(Paragraph(self.title, styleH)) # 标题是标题样式的段落,添加进story
story.append(Spacer(1, 20)) # 标题和表格间放段空白,也是flowable的一种,填进story
if self.sort_by:
# 生成对象时如果没给排序参数,按遍历时找到文件的先后填入表格;如果给了排序参数……
reverse_order = False # 默认是正序排列
if (str(self.sort_by[1]).upper() == 'DESC'): # 除非特别指定是逆序(DESC)
reverse_order = True
self.data = sorted(self.data,
key=itemgetter(self.sort_by[0]),
# self.sort_by的值('size', 'DESC')
# self.sort_by[0]取'size'
reverse=reverse_order)
# self.data是一个列表,每个元素是字典类型,排序的关键字有参数key=决定
# key=itemgetter('size')决定按字典键值'size'排列,reverse=True逆序排
converted_data = self.__convert_data()
# 将表格要用到的数据整理到一个列表,列表元素还是列表,存放表格每一行的数据
t = Table(converted_data, hAlign=table_halign)
# 生成表格对象,hAlign='CENTRE'使得表格的页面对齐方式是居中
t.setStyle(TableStyle([ # 自定义表格样式
('INNERGRID', (0, 0), (-1, -1), 0.50, colors.black), # 内线
('BOX', (0, 0), (-1, -1), 0.25, colors.black), # 外框
('FONT', (0, 0), (-1, 0), 'lively'), # 表头字体
('ALIGN', (0, 0), (-1, 0), 'CENTER'), # 表头行单元格内的对齐方式
('BACKGROUND', (0, 0), (-1, 0), colors.peachpuff), # 表头行背景
('FACE', (0, 1), (-1, -1), 'apple'), # 数据区字体
('ALIGN',(0, 1),(0,-1), data_align), # 数据区单元格内对齐方式
('ROWBACKGROUNDS', (0, 1), (-1, -1), (colors.aliceblue, colors.lightgoldenrodyellow, colors.lavenderblush)).
# 数据区背景
]))
story.append(t) # 表格也是一个flowable,添加进story
doc = SimpleDocTemplate(filename) # 生成pdf文档对象
self.pageSetup(doc, t) # 根据表格宽度决定页面要不要横放
doc.build(story) # 生成pdf文件
def __convert_data(self):
"""
为了生成PDF上的表格,把表头元组和存放文件信息的字典列表整合成元素是列表的列表
"""
# 表头列表
keys, names = zip(*self.fields)
# 生成数据的字典键值元组和表头元组
names = list(names) # 表头元组->表头列表
new_data = [names] # new_data接收整合好的表格数据
# 整合数据
for d in self.data:
new_data.append([d[k] for k in keys])
# for k in keys循环变量k依次取字典键值,d[k]取键值对应的值。外面的[]把取出来的值放进列表,对应表格里的一行
return new_data
def pageSetup(self, doc, t):
"""
对比页面可用宽度和表格需要的宽度,表格太宽时把页面横过来
参数解释:
doc - 接收PDF文档对象
t - 接收表格对象
"""
from reportlab.lib.pagesizes import A3, A4, landscape
pageSize = A3 # 页面尺寸
doc.pagesize = pageSize # 指定页面尺寸,doc没有这个属性,然而这种做法目前行之有效
margin = 72 # 缺省的上下左右留白是72points(1 inch)
pW, pH = pageSize # 页面宽高
tW, tH = t.wrap(pH, pH) # 表格宽高
aW = pW - margin * 2 # 除去页面留白,表格真正可用的宽度
if tW > aW: # 表格太宽页面只好横放
doc.pagesize = landscape(pageSize) # 页面横放
print("横过来后的页面宽度:", pH - margin * 2)
print("纵向页面可用空间:", aW, ",表格占用空间:", tW)
import time, datetime
from pathlib import Path
if __name__ == '__main__':
data = [] # 存放文件信息的列表,每个元素是字典类型
q = Path('/Users/PythonABC/reportlab')
for p in q.glob('**/*.*'): # 遍历目录树
if p.is_file(): # 判断是不是文件
filename = p.name # 文件名
filepath = p.absolute() # 绝对路径
filesize = p.stat().st_size / 1024 # 获得文件尺寸,/1024是为了单位换算成KB
if filesize > 1024: # 大于1024K即大于1M
mtime = p.stat().st_mtime # 文件修改时间,得到的是时间戳
mtime = datetime.datetime.strptime(time.ctime(mtime),
'%a %b %d %H:%M:%S %Y')
# 转化成指定格式的日期时间字符串
data.append({'filename': filename,
'filepath': filepath,
'size': filesize,
'mtime': mtime})
# 用字典类型保存文件信息,而后添加进列表
fields = (
('filename', '文件名'),
('filepath', '文件路径'),
('size', '文件大小(KB)'),
('mtime', '修改时间'),
)
# 表头内容和个数固定用元组,表头的每个条目跟保存文件信息的字典的关键字组成元组,放进一个大元组里
doc = DataToPdf(fields, data, sort_by=('size', 'DESC'),
title='超过1MB的文件') # DataToPdf是自己定义的类,生成类的对象doc
doc.export('listFile>1M.pdf') # 调用对象的方法函数制作表格生成PDF文件