说明:博客,只展示练习后的代码,具体内容,请参考“Python基础教程”书籍。
遇到的坑:我用的Python解释器是3.6版本,但是ReportLab最新的,只有3.5版本的。我只好,更换了一下Python。更换Python解释器版本便捷方法:在PyCharm开发工具中,使用Conda Environment进行添加,上图:
以上,是练习中遇到的坑。
初次实现,代码:我的代码中,有print函数,是为了加强理解用的。
from reportlab.lib import colors
from reportlab.graphics.shapes import *
from reportlab.graphics import renderPDF
data = [
# Year Month Predicted High Low
(2007, 8, 113.2, 114.2, 112.2),
(2007, 9, 112.8, 115.8, 109.8),
(2007, 10, 111.0, 116.0, 106.0),
(2007, 11, 109.8, 116.8, 102.8),
(2007, 12, 107.3, 115.3, 99.3),
(2008, 1, 105.2, 114.2, 96.2),
(2008, 2, 104.1, 114.1, 94.1),
(2008, 3, 99.9, 110.9, 88.9),
(2008, 4, 94.8, 106.8, 82.8),
(2008, 5, 91.2, 104.2, 78.2),
]
# times = [200 * ((row[0] + row[1] / 12.0) - 2007) - 110 for row in data]
# print(times)
drawing = Drawing(200, 150)
pred = [row[2] - 40 for row in data]
# print(pred)
high = [row[3] - 40 for row in data]
low = [row[4] - 40 for row in data]
times = [200 * ((row[0] + row[1] / 12.0) - 2007) - 110 for row in data]
drawing.add(PolyLine(list(zip(times, pred)), strokeColor=colors.blue))
drawing.add(PolyLine(list(zip(times, high)), strokeColor=colors.red))
drawing.add(PolyLine(list(zip(times, low)), strokeColor=colors.green))
drawing.add(String(65, 115, 'Sunspots', fontSize=18, fillColor=colors.red))
renderPDF.drawToFile(drawing, 'report1.pdf', 'Sunspots')
运行之后,会在如下目录生成report1.pdf文件:打开文件,如下:
源码解析,加强理解:
class Drawing(Group, Flowable):
"""Outermost container; the thing a renderer works on.
This has no properties except a height, width and list
of contents."""
_saveModes=(
'pdf','ps','eps','gif','png','jpg','jpeg','pct',
'pict','tiff','tif','py','bmp','svg','tiffp','tiffl','tiff1',
)
_xtraAttrMap = AttrMap(
width = AttrMapValue(isNumber,desc="Drawing width in points."),
height = AttrMapValue(isNumber,desc="Drawing height in points."),
canv = AttrMapValue(None),
background = AttrMapValue(isValidChildOrNone,desc="Background widget for the drawing e.g. Rect(0,0,width,height)"),
hAlign = AttrMapValue(OneOf("LEFT", "RIGHT", "CENTER", "CENTRE"), desc="Horizontal alignment within parent document"),
vAlign = AttrMapValue(OneOf("TOP", "BOTTOM", "CENTER", "CENTRE"), desc="Vertical alignment within parent document"),
#AR temporary hack to track back up.
#fontName = AttrMapValue(isStringOrNone),
renderScale = AttrMapValue(isNumber,desc="Global scaling for rendering"),
)
_attrMap = AttrMap(BASE=Group,
formats = AttrMapValue(SequenceOf(
OneOf('pdf','gif','png','tif','jpg','tiff','pct','pict',
'bmp','tiffp','tiffl','tiff1','eps','svg','ps','py'),
lo=1,emptyOK=0), desc='One or more plot modes'),
)
_attrMap.update(_xtraAttrMap)
def __init__(self, width=400, height=200, *nodes, **keywords):
self.background = None
Group.__init__(self,*nodes,**keywords)
self.width = width
self.height = height
self.hAlign = 'LEFT'
self.vAlign = 'BOTTOM'
self.renderScale = 1.0
def _renderPy(self):
I = {'reportlab.graphics.shapes': ['_DrawingEditorMixin','Drawing','Group']}
G = _renderGroupPy(self._explode(),'self',I)
n = 'ExplodedDrawing_' + self.__class__.__name__
s = '#Autogenerated by ReportLab guiedit do not edit\n'
for m, o in I.items():
s = s + 'from %s import %s\n' % (m,str(o)[1:-1].replace("'",""))
s = s + '\nclass %s(_DrawingEditorMixin,Drawing):\n' % n
s = s + '\tdef __init__(self,width=%s,height=%s,*args,**kw):\n' % (self.width,self.height)
s = s + '\t\tDrawing.__init__(self,width,height,*args,**kw)\n'
s = s + G
s = s + '\n\nif __name__=="__main__": #NORUNTESTS\n\t%s().save(formats=[\'pdf\'],outDir=\'.\',fnRoot=None)\n' % n
return s
def draw(self,showBoundary=_unset_):
"""This is used by the Platypus framework to let the document
draw itself in a story. It is specific to PDF and should not
be used directly."""
from reportlab.graphics import renderPDF
renderPDF.draw(self, self.canv, 0, 0, showBoundary=showBoundary)
def wrap(self, availWidth, availHeight):
width = self.width
height = self.height
renderScale = self.renderScale
if renderScale!=1.0:
width *= renderScale
height *= renderScale
return width, height
def expandUserNodes(self):
"""Return a new drawing which only contains primitive shapes."""
obj = Group.expandUserNodes(self)
obj.width = self.width
obj.height = self.height
return obj
def copy(self):
"""Returns a copy"""
return self._copy(self.__class__(self.width, self.height))
def asGroup(self,*args,**kw):
return self._copy(Group(*args,**kw))
def save(self, formats=None, verbose=None, fnRoot=None, outDir=None, title='', **kw):
"""Saves copies of self in desired location and formats.
Multiple formats can be supported in one call
the extra keywords can be of the form
_renderPM_dpi=96 (which passes dpi=96 to renderPM)
"""
genFmt = kw.pop('seqNumber','')
if isinstance(genFmt,int):
genFmt = '%4d: ' % genFmt
else:
genFmt = ''
genFmt += 'generating %s file %s'
from reportlab import rl_config
ext = ''
if not fnRoot:
fnRoot = getattr(self,'fileNamePattern',(self.__class__.__name__+'%03d'))
chartId = getattr(self,'chartId',0)
if hasattr(chartId,'__call__'):
chartId = chartId(self)
if hasattr(fnRoot,'__call__'):
fnRoot = fnRoot(chartId)
else:
try:
fnRoot = fnRoot % chartId
except TypeError as err:
#the exact error message changed from 2.2 to 2.3 so we need to
#check a substring
if str(err).find('not all arguments converted') < 0: raise
if os.path.isabs(fnRoot):
outDir, fnRoot = os.path.split(fnRoot)
else:
outDir = outDir or getattr(self,'outDir','.')
outDir = outDir.rstrip().rstrip(os.sep)
if not outDir: outDir = '.'
if not os.path.isabs(outDir): outDir = os.path.join(getattr(self,'_override_CWD',os.path.dirname(sys.argv[0])),outDir)
if not os.path.isdir(outDir): os.makedirs(outDir)
fnroot = os.path.normpath(os.path.join(outDir,fnRoot))
plotMode = os.path.splitext(fnroot)
if plotMode[1][1:].lower() in self._saveModes:
fnroot = plotMode[0]
plotMode = [x.lower() for x in (formats or getattr(self,'formats',['pdf']))]
verbose = (verbose is not None and (verbose,) or (getattr(self,'verbose',verbose),))[0]
_saved = logger.warnOnce.enabled, logger.infoOnce.enabled
logger.warnOnce.enabled = logger.infoOnce.enabled = verbose
if 'pdf' in plotMode:
from reportlab.graphics import renderPDF
filename = fnroot+'.pdf'
if verbose: print(genFmt % ('PDF',filename))
renderPDF.drawToFile(self, filename, title, showBoundary=getattr(self,'showBorder',rl_config.showBoundary),**_extraKW(self,'_renderPDF_',**kw))
ext = ext + '/.pdf'
if sys.platform=='mac':
import macfs, macostools
macfs.FSSpec(filename).SetCreatorType("CARO", "PDF ")
macostools.touched(filename)
for bmFmt in ('gif','png','tif','jpg','tiff','pct','pict', 'bmp','tiffp','tiffl','tiff1'):
if bmFmt in plotMode:
from reportlab.graphics import renderPM
filename = '%s.%s' % (fnroot,bmFmt)
if verbose: print(genFmt % (bmFmt,filename))
dtc = getattr(self,'_drawTimeCollector',None)
if dtc:
dtcfmts = getattr(dtc,'formats',[bmFmt])
if bmFmt in dtcfmts and not getattr(dtc,'disabled',0):
dtc.clear()
else:
dtc = None
renderPM.drawToFile(self, filename,fmt=bmFmt,showBoundary=getattr(self,'showBorder',rl_config.showBoundary),**_extraKW(self,'_renderPM_',**kw))
ext = ext + '/.' + bmFmt
if dtc: dtc.save(filename)
if 'eps' in plotMode:
try:
from rlextra.graphics import renderPS_SEP as renderPS
except ImportError:
from reportlab.graphics import renderPS
filename = fnroot+'.eps'
if verbose: print(genFmt % ('EPS',filename))
renderPS.drawToFile(self,
filename,
title = fnroot,
dept = getattr(self,'EPS_info',['Testing'])[0],
company = getattr(self,'EPS_info',['','ReportLab'])[1],
preview = getattr(self,'preview',rl_config.eps_preview),
showBoundary=getattr(self,'showBorder',rl_config.showBoundary),
ttf_embed=getattr(self,'ttf_embed',rl_config.eps_ttf_embed),
**_extraKW(self,'_renderPS_',**kw))
ext = ext + '/.eps'
if 'svg' in plotMode:
from reportlab.graphics import renderSVG
filename = fnroot+'.svg'
if verbose: print(genFmt % ('SVG',filename))
renderSVG.drawToFile(self,
filename,
showBoundary=getattr(self,'showBorder',rl_config.showBoundary),**_extraKW(self,'_renderSVG_',**kw))
ext = ext + '/.svg'
if 'ps' in plotMode:
from reportlab.graphics import renderPS
filename = fnroot+'.ps'
if verbose: print(genFmt % ('EPS',filename))
renderPS.drawToFile(self, filename, showBoundary=getattr(self,'showBorder',rl_config.showBoundary),**_extraKW(self,'_renderPS_',**kw))
ext = ext + '/.ps'
if 'py' in plotMode:
filename = fnroot+'.py'
if verbose: print(genFmt % ('py',filename))
with open(filename,'wb') as f:
f.write(asBytes(self._renderPy().replace('\n',os.linesep)))
ext = ext + '/.py'
logger.warnOnce.enabled, logger.infoOnce.enabled = _saved
if hasattr(self,'saveLogger'):
self.saveLogger(fnroot,ext)
return ext and fnroot+ext[1:] or ''
def asString(self, format, verbose=None, preview=0, **kw):
"""Converts to an 8 bit string in given format."""
assert format in ('pdf','ps','eps','gif','png','jpg','jpeg','bmp','ppm','tiff','tif','py','pict','pct','tiffp','tiffl','tiff1'), 'Unknown file format "%s"' % format
from reportlab import rl_config
#verbose = verbose is not None and (verbose,) or (getattr(self,'verbose',verbose),)[0]
if format == 'pdf':
from reportlab.graphics import renderPDF
return renderPDF.drawToString(self)
elif format in ('gif','png','tif','tiff','jpg','pct','pict','bmp','ppm','tiffp','tiffl','tiff1'):
from reportlab.graphics import renderPM
return renderPM.drawToString(self, fmt=format,showBoundary=getattr(self,'showBorder',
rl_config.showBoundary),**_extraKW(self,'_renderPM_',**kw))
elif format == 'eps':
try:
from rlextra.graphics import renderPS_SEP as renderPS
except ImportError:
from reportlab.graphics import renderPS
return renderPS.drawToString(self,
preview = preview,
showBoundary=getattr(self,'showBorder',rl_config.showBoundary))
elif format == 'ps':
from reportlab.graphics import renderPS
return renderPS.drawToString(self, showBoundary=getattr(self,'showBorder',rl_config.showBoundary))
elif format == 'py':
return self._renderPy()
def resized(self,kind='fit',lpad=0,rpad=0,bpad=0,tpad=0):
'''return a base class drawing which ensures all the contents fits'''
C = self.getContents()
oW = self.width
oH = self.height
drawing = Drawing(oW,oH,*C)
xL,yL,xH,yH = drawing.getBounds()
if kind=='fit' or (kind=='expand' and (xL<lpad or xH>oW-rpad or yL<bpad or yH>oH-tpad)):
drawing.width = xH-xL+lpad+rpad
drawing.height = yH-yL+tpad+bpad
drawing.transform = (1,0,0,1,lpad-xL,bpad-yL)
elif kind=='fitx' or (kind=='expandx' and (xL<lpad or xH>oW-rpad)):
drawing.width = xH-xL+lpad+rpad
drawing.transform = (1,0,0,1,lpad-xL,0)
elif kind=='fity' or (kind=='expandy' and (yL<bpad or yH>oH-tpad)):
drawing.height = yH-yL+tpad+bpad
drawing.transform = (1,0,0,1,0,bpad-yL)
return drawing
其它的源代码,自己去分析的,如有需要,可以留言。
至此,本项目初次实现完成。