Reportlab生成PDF文件

这个图表在x轴下的表格是伸长的刻度线做出来的,用chart.categoryAxis.tickDown=36控制刻度线向下伸出的长度,表格内容用String()添加。

代码和注释:

"Multi-line x-axis labels"
from reportlab.graphics.charts.barcharts import VerticalBarChart
from reportlab.graphics.shapes import Drawing, _DrawingEditorMixin, String, Line
from reportlab.lib.colors import PCMYKColor, black

class BarChart_LongTickTableAnnotation(_DrawingEditorMixin,Drawing):
    def __init__(self,width=400,height=200,*args,**kw):
        Drawing.__init__(self,width,height,*args,**kw)
        self._add(self,VerticalBarChart(),name='chart',validate=None,desc=None)

        # 公共变量
        fontName = 'Helvetica'          # 字体
        strokeWidth = 0.5               # 线宽
        strokeDashArray = (0.3, 1)      # 线样式
        lineCap = 1                     # 线头样式
        gridStickOut = 4                # 格子线的伸出量
        tickOut     = 36                # 刻度线伸出量

        self.chart.barSpacing = 3       # 同组数据柱间距
        self.chart.barWidth = 9         # 柱宽
        self.chart.groupSpacing = 15    # 组与组间距
        self.chart.width = 319          # 图表宽
        self.chart.x = 24               # 图表左下角坐标
        self.chart.y = 54
        self.chart.bars.strokeColor = None  # 柱周线颜色
        self.chart.bars.strokeWidth = 0     # 柱周线宽度

        colorsList = [PCMYKColor(100,60,0,50,alpha=100), PCMYKColor(100,0,90,50,alpha=100)]
        for i, color in enumerate(colorsList):
            self.chart.bars[i].fillColor = colorsList[i]    # 数据柱颜色

        # 图表数据
        self.chart.data = [[19.260000000000002, 32.359999999999999, 1.78, -4.3499999999999996, 2.1000000000000001, 5.1200000000000001, 11.0],
                           [18.239999999999998, 14.85, -10.619999999999999, -7.8700000000000001, 0.90000000000000002, 2.5899999999999999, 10.869999999999999]]

        # categoryAxis.joinAxisMode属性指定类别轴(x轴)的位置
        self.chart.categoryAxis.joinAxisMode='bottom'       # 置于图表底部
        # self.chart.categoryAxis.joinAxisMode='top'          # 置于图表顶部
        # joinAxisMode取值'points'和'value'时,跟joinAxisPos值配合使用
        # self.chart.categoryAxis.joinAxisMode='points'
        # self.chart.categoryAxis.joinAxisPos= 100            # 距离画板底部100个points的位置
        # self.chart.categoryAxis.joinAxisMode='value'
        # self.chart.categoryAxis.joinAxisPos= 30             # y轴刻度30的位置

        # 设置刻度标签
        self.chart.categoryAxis.labelAxisMode='low'         # 置于图表底部
        self.chart.categoryAxis.labels.angle = 0            # 旋转0度
        self.chart.categoryAxis.labels.boxAnchor = 'n'      # 锚点在n
        self.chart.categoryAxis.labels.dy = -4              # 向下微调4
        self.chart.categoryAxis.labels.fillColor = black    # 黑色
        self.chart.categoryAxis.labels.fontName = fontName  # 字体
        self.chart.categoryAxis.labels.fontSize = 6         # 字体大小
        self.chart.categoryAxis.labels.textAnchor='middle'  # 文字居中

        # 属性tickShift取值布尔型,刻度线的位置,为1刻度居于类别中,为0居于类别侧
        self.chart.categoryAxis.tickShift = 0

        # 目前为0,居于类别侧且向下伸出很长,起到了表格的效果
        self.chart.categoryAxis.visibleTicks = 1            # 刻度线可见
        self.chart.categoryAxis.tickDown    = tickOut       # 向下伸出量
        self.chart.categoryAxis.strokeDashArray      = strokeDashArray  # 刻度线样式
        self.chart.categoryAxis.strokeWidth         = strokeWidth   # 刻度线宽度
        self.chart.categoryAxis.visibleGrid         = 0     # 格子线不可见
        self.chart.categoryAxis.categoryNames = ['Quarterly', 'YTD', '1 Year', '3 Year', '5 Year', '10 Year', 'Since Inception\n(11/05/1984)']
        # 类别名称

        # 设置数值轴刻度标签
        self.chart.valueAxis.labels.fontName = fontName     # 字体
        self.chart.valueAxis.labels.fontSize = 6            # 字体大小
        self.chart.valueAxis.labels.rightPadding   = 3      # 右留白

        self.chart.valueAxis.drawGridLast=0
        # 这个属性若为真,则最后才画格子线
        self.chart.valueAxis.rangeRound='both'
        # 可取值:'none','both','ceiling','floor',规定刻度的最高最低怎么取
        # ceiling取最近的那个最大的,floor是取最近的那个最小的,both兼顾
        # self.chart.valueAxis.valueStep = 5                # 刻度的步长
        self.chart.valueAxis.minimumTickSpacing    = 0.5    # 刻度的最小间距
        self.chart.valueAxis.maximumTicks          = 8      # 最多8个刻度线
        self.chart.valueAxis.forceZero             = 1      # 强迫刻度有0
        self.chart.valueAxis.visibleGrid = 1                # 格子线可见
        self.chart.valueAxis.gridStrokeDashArray = strokeDashArray  # 格子线样式
        self.chart.valueAxis.gridStrokeWidth       = strokeWidth    # 格子线宽度
        self.chart.valueAxis.gridStart              = self.chart.x-gridStickOut
        # 格子线起点
        self.chart.valueAxis.gridEnd                = self.chart.x+self.chart.width+gridStickOut
        # 格子线终点
        self.chart.valueAxis.gridStrokeLineCap      = lineCap
        # 格子线的虚线线头样式,可取0、1、2,自己试了放大虚线可见效果
        self.chart.valueAxis.strokeDashArray       = strokeDashArray    # 轴线样式
        self.chart.valueAxis.strokeWidth           = strokeWidth        # 周线宽度
        self.chart.valueAxis.strokeColor            = None              # 颜色
        self.chart.valueAxis.visibleTicks           = False             # 刻度线不可见

        # 属性annotations的使用参见帖子28 图表的annotation属性         
        self.chart.annotations=[lambda c,cA,vA: Line(c.x,vA(0),c.x+c.width,vA(0),strokeColor=black,strokeWidth=strokeWidth)]
        # y轴刻度0起始到图表宽度,画一条跟x轴平行的直线。vA(0)是y轴0刻度的坐标
        self.chart.annotations.extend([lambda c,cA,vA: Line(c.x,c.y-tickOut,c.x,self.chart.y+c.height+gridStickOut,strokeColor=black,strokeWidth=strokeWidth)])
        # 左边这条跟y轴重合的直线,起点y坐标是刻度线伸出的最底端,终点y坐标是图表的最顶
        # extend()是列表的方法函数,添加另一个列表的内容进来
        self.chart.annotations.extend([lambda c,cA,vA: Line(c.x+c.width,c.y-tickOut,c.x+c.width,self.chart.y+c.height+gridStickOut,strokeColor=black,strokeWidth=0.5)])
        # 图表最右边垂直线,起点y坐标是刻度线伸出的最底端,终点y坐标是图表的最顶

        # 画板和图表的宽高
        self.width                 = 400
        self.height                = 200
        self.chart.width           = 350
        self.chart.height          = 125

        # 写表格内的内容
        y = self.chart.y - 26           # 第一行表格内容的y坐标
        colWidth = self.chart.width * 1.0 / len(self.chart.data[0])
        # chart.data有两组数据,chart.data[0]是其中的一组数据
        # len(self.chart.data[0])可得每个数据组的长度
        # colWidth把chart的宽度均分
        for h, series in enumerate(self.chart.data):
        # h获得索引,series获得每个数据组
            for (i, value) in enumerate(series):
                if value is None:
                    text = 'N/A'
                else:
                    text = '%0.2f' % value      # 实数保留两位小数
                x = self.chart.x + (i + 0.5) * colWidth
                # 计算表格内容的x坐标,单元格的中部
                s = String(x, y, text, textAnchor='middle')
                # 字符串,锚点在middle,所以字符串中心的坐标是(x,y)
                s.fontSize = 6      # 字体大小
                s.fillColor =  self.chart.bars[h].fillColor
                # 字体颜色跟数据柱的填充颜色相同
                self.add(s)         # 往画板上添加字符串
            y = y - 8               # 写下一行
if __name__=="__main__":
    BarChart_LongTickTableAnnotation().save(formats=['pdf'],outDir='.',fnRoot=None)