Search code examples
pythonreportlab

python reportlab categoryAxis label placement


I've written a simple function that generates a simple bar chart from a single data series as shown below. The input data can be positive or negative, which when negative results in the category axis labels being covered by the bar (charts 2 and 3 in the code below).

I have searched the reportlab graphics reference, and on page 202 a bar chart with labels placed "low" is shown. However the command used to do this: bc.categoryAxis.labels.dy = -60, appears to be arbitrarily chosen. How would one know the negative offset if the input data is not known? Is there a way to identify the x axis location on the drawing and then determine the label offset from that location, or any other work-around when the input data is unknown?

Thanks for any guidance.

from reportlab.graphics.shapes import Drawing
from reportlab.graphics.charts.barcharts import VerticalBarChart
from reportlab.graphics.charts.textlabels import Label
from reportlab.lib import colors
from reportlab.platypus import SimpleDocTemplate
from reportlab.lib.units import inch
from reportlab.lib.pagesizes import letter

def vbar_one_series(data, xlabels, chart_title, xaxis_text):
    drawing = Drawing(180, 180)

    bc = VerticalBarChart()
    bc.x = 25
    bc.y = 25
    bc.height = 125
    bc.width = 150
    bc.data = [data]
    bc.valueAxis.valueMin = int(min(min(bc.data[0]), 0))
    bc.valueAxis.valueMax = int(max(bc.data[0]) + 2)
    bc.valueAxis.labels.fontName = 'Helvetica'
    bc.valueAxis.labels.fontSize = 7
    bc.valueAxis.labelTextFormat = '%0.1f'
    bc.bars[0].fillColor = colors.HexColor('#000035')
    bc.categoryAxis.labels.boxAnchor = 'c'
    bc.categoryAxis.labels.fontName = 'Helvetica'
    bc.categoryAxis.labels.fontSize = 7
    bc.categoryAxis.tickDown = 0
    bc.categoryAxis.labels.dx = 0
    bc.categoryAxis.labels.dy = -7
    bc.categoryAxis.labels.angle = 0

    bc.categoryAxis.categoryNames = xlabels

    bc.barLabels.nudge = 10
    bc.barLabelFormat = '%0.1f'
    bc.barLabels.dx = 0
    bc.barLabels.dy = 0
    bc.barLabels.fontName = 'Helvetica'
    bc.barLabels.fontSize = 7

    xlabel = Label()
    xlabel.setText(xaxis_text)
    xlabel.fontSize = 7
    xlabel.fontName = 'Helvetica'
    xlabel.dx = 90
    xlabel.dy = 5

    title = Label()
    title.setText(chart_title)
    title.fontSize = 7
    title.fontName = 'Helvetica'
    title.dx = 90
    title.dy = 175

    drawing.add(bc)
    drawing.add(xlabel)
    drawing.add(title)

    return drawing

if __name__ == '__main__':

    bar1 = vbar_one_series([1.0, 2.3, 1.8], ['A', 'B', 'C'],
                                 'Title 1', '')

    bar2 = vbar_one_series([-25, 50, 10], ['A', 'B', 'C'],
                           'Title 2', '')

    bar3 = vbar_one_series([0, -1.1, -0.4], ['A', 'B', 'C'],
                           'Title 3', '')

    doc = SimpleDocTemplate('H:/Desktop/report.pdf', pagesize=letter,
                            topMargin=0.5 * inch, bottomMargin=0.5 * inch)
    Catalog = []
    Catalog.append(bar1)
    Catalog.append(bar2)
    Catalog.append(bar3)
    doc.build(Catalog)

report.pdf


Solution

  • After some additional research, I found that this problem can be solved using the .categoryAxis.labelAxisMode = 'low' property.