26 用刻度线做表格的直方图(BarChart)
这个图表在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)