Reportlab生成PDF文件

用自己定义的水平直方图类SpecialHorizontalBarChart在一个画板上放两个直方图,这两个直方图共用一个图例。类SpecialHorizontalBarChart最理想是给两组数据(每组两个数据柱),否则需要调整刻度标签位置。

代码及注释:

"Dual Bar charts on one canvas."
from reportlab.lib.colors import PCMYKColor, black, red
from reportlab.graphics.charts.legends import Legend
from reportlab.graphics.shapes import Drawing, _DrawingEditorMixin
from reportlab.lib.formatters import DecimalFormatter
from reportlab.graphics.charts.textlabels import Label
from reportlab.graphics.charts.barcharts import HorizontalBarChart

class SpecialHorizontalBarChart(HorizontalBarChart):    # 注意它的父类
    def __init__(drawing, data, categoryName):  # 注意第一个参数是drawing画板
        _fontName = 'Helvetica'
        _fontSize = 5
        HorizontalBarChart.__init__(drawing)    # 父类的初始化函数直接拿来用
        drawing.x                       = 45
        drawing.y                       = 24
        drawing.width                   = 52
        drawing.height                  = 154
        drawing.fillColor               = None
        drawing.reversePlotOrder = 1
        # 垂直直方图,无论是数据还是类别轴标签都是从y的坐标原点开始,从下至上往正向铺排
        # 代表数据的数据柱从下到上专程从上向下

        # 设置数据柱
        drawing.groupSpacing            = 4
        drawing.barWidth                = 5
        drawing.barSpacing              = 0
        drawing.bars.strokeWidth        = 0
        drawing.bars.strokeColor        = None
        drawing.barLabels.fontName      = _fontName
        drawing.barLabels.fontSize      = _fontSize
        drawing.barLabelFormat          = DecimalFormatter(2, suffix='%')
        # 数据柱标签两位小数,%后缀
        drawing.barLabels.boxAnchor     ='w'
        drawing.barLabels.boxFillColor  = None
        drawing.barLabels.boxStrokeColor= None
        drawing.barLabels.dx            = 5
        drawing.barLabels.dy            = 0
        # 柱标签相对于数据柱的位置
        drawing.barLabels.boxTarget     = 'hi'
        # 'hi',数据柱右;'lo',数据柱左;'anti',坐标轴左右,正左负右
        # 'normal',数据柱旁,正左负右

        # value axis(啥都不可见)
        drawing.valueAxis.visibleAxis          = False
        drawing.valueAxis.visibleGrid           = False
        drawing.valueAxis.visibleTicks          = False
        drawing.valueAxis.visibleLabels          = False

        # category axis
        drawing.categoryAxis.labels.fontName = _fontName
        drawing.categoryAxis.labels.fontSize = _fontSize+0.5
        drawing.categoryAxis.visibleGrid         = False
        drawing.categoryAxis.visibleTicks        = False
        drawing.categoryAxis.strokeWidth         = 0.25
        drawing.categoryAxis.labelAxisMode       ='low'
        # 'low',坐标轴标签在坐标轴左;'high',刻度标签在整个图表的顶部
        # 'axis',刻度标签在坐标轴旁;
        # 'axispmv',刻度标签在坐标轴旁,在上在下就看数据柱的主要位置,反着来
        drawing.categoryAxis.labels.textAnchor   ='end'
        # 'end'右靠齐,'start'左靠齐,'middle'居中
        drawing.categoryAxis.labels.fillColor    = black
        drawing.categoryAxis.labels.angle        = 0
        drawing.categoryAxis.labels.boxAnchor    = 'e'
        drawing.categoryAxis.labels.leading      = 5        # 行间距
        drawing.categoryAxis.labels.dx           = -5       # x轴方向微调坐标
        drawing.categoryAxis.labels.dy           = -5       # y轴方向微调坐标
        drawing.categoryAxis.reverseDirection    = 1
        # 垂直直方图,无论是数据还是类别轴标签都是从y的坐标原点开始,从下至上往正向铺排
        # reverseDirection是的类别标签从上向下铺排,最后的标签在y的坐标原点
        drawing.categoryAxis.joinAxisMode   =   'left'
        # 指定类别轴的位置:'bottom',置于图表底部;'top',置于图表顶部
        # 取值'points'和'value'时,跟joinAxisPos值配合使用
        # 取值'points',属性joinAxisPos取值100,距离画板底部100个points的位置
        # 取值'value',属性joinAxisPos= 30,y轴刻度30的位置
        drawing.data = data
        drawing.categoryAxis.categoryNames       = categoryName
class BarChart_Dual(_DrawingEditorMixin,Drawing):
    def __init__(self,width=264,height=190,*args,**kw):
        Drawing.__init__(self,width,height,*args,**kw)

        colorM = PCMYKColor(100,60,0,50,alpha=85)
        colorR = PCMYKColor(66,13,0,22,alpha=85)
        colorN = PCMYKColor(100,0,90,50,alpha=85)
        self._fontName = 'Helvetica'
        self._fontSize = 5

        # 第一个直方图的数据
        data = [[10.9, 10.84, 15.99, 18.75, 14.75, 9.1300000000000008, 9.2100000000000009, 5.21, 2.3500000000000001, 2.8799999999999999, 0.0],
                        [8.9900000000000002, 11.210000000000001, 11.859999999999999, 17.0, 12.59, 11.4, 12.279999999999999, 4.7199999999999998, 4.4699999999999998, 5.4900000000000002, 0.0]]
        # 第一个直方图的类别标签
        nameList = 'Consumer\nStaples', 'Consumer\nDiscretionary', 'Energy', 'Financials', 'Health Care', 'Industrials', 'Information\nTechnology', 'Materials', 'Telecomm.\nServices', 'Utilities', 'Other'
        # 用自己的定义的类SpecialHorizontalBarChart生成水平直方图
        self._add(self,SpecialHorizontalBarChart(data, nameList),name='chart1',validate=None,desc=None)
        # 颜色序列,定义直方图数据柱的颜色
        colorList1 = colorM, colorR
        for i, _ in enumerate(self.chart1.data):
            self.chart1.bars[i].fillColor = colorList1[i]

        # 第二个直方图
        data = [
            [0.85999999999999999, 5.2599999999999998, 2.1899999999999999, 0.94999999999999996, 8.4600000000000009, 11.220000000000001, 3.25, 3.2799999999999998, 8.7400000000000002, 14.4, 41.380000000000003],
            [0.050000000000000003, 3.0299999999999998, 0.26000000000000001, 0.01, 1.3799999999999999, 3.2599999999999998, 0.01, 0.0, 0.0, 92.0, 0.0],
        ]
        nameList = 'Asset Backed\nSecurities', 'US\nTreasury/Agency', 'Commercial\nMortgage Backed', 'High Yield\nCorporate', 'Investment Grade\nCorporate', 'Mortgage Backed', 'Govt Emerging\nMarkets', 'Non US Currency', 'TIPS', 'Cash', 'Other'
        self._add(self,SpecialHorizontalBarChart(data, nameList),name='chart2',validate=None,desc=None)
        # 调整了第二个直方图的位置
        self.chart2.x = 182
        colorList2 = colorM, colorN
        for i, _ in enumerate(self.chart2.data):
            self.chart2.bars[i].fillColor = colorList2[i]

        # 两个直方图共用的图例
        self._add(self,Legend(),name='legend',validate=None,desc=None)
        self.legend.fontName         = self._fontName
        self.legend.fontSize         = self._fontSize+0.5
        self.legend.boxAnchor        = 'nw'
        self.legend.x                = 3
        self.legend.y                = 20
        self.legend.deltax           = 0
        self.legend.deltay           = 5
        self.legend.autoXPadding     = 0
        self.legend.columnMaximum    = 3
        self.legend.alignment        = 'right'
        self.legend.strokeWidth      = 0
        self.legend.strokeColor      = None
        self.legend.dx               = 4
        self.legend.dy               = 4
        self.legend.variColumn       = True
        self.legend.dxTextSpace      = 4
        self.legend.colorNamePairs   = [(colorR, 'Microsoft'),
                                        (colorM, 'Russel 2000 Index'),
                                        (colorN, 'NASDAQ Composite Index')]

        # 第一个直方图的标题
        self._add(self,Label(),name='label1',validate=None,desc=None)
        self.label1.boxAnchor      = 'nw'
        self.label1.x              = 0
        self.label1.y              = self.height
        self.label1.fontName       = self._fontName
        self.label1.fontSize       = 8
        self.label1._text          = 'Equity Composition'
        # 第二个直方图的标题
        self._add(self,Label(),name='label2',validate=None,desc=None)
        self.label2.boxAnchor      = 'nw'
        self.label2.x              = self.chart2.x-self.chart1.x
        self.label2.y              = self.height
        self.label2.fontName       = self._fontName
        self.label2.fontSize       = 8
        self.label2.textAnchor     ='middle'
        self.label2._text          = 'Fixed Income Composition'

if __name__=="__main__":
    BarChart_Dual().save(formats=['pdf'],outDir='.',fnRoot=None)