tests/test_graphics_charts.py
changeset 2963 c414c0ab69e7
child 2966 c9df63ccabdf
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test_graphics_charts.py	Wed Sep 03 16:05:15 2008 +0000
@@ -0,0 +1,423 @@
+#Copyright ReportLab Europe Ltd. 2000-2004
+#see license.txt for license details
+#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/test/test_graphics_charts.py
+"""
+Tests for chart class.
+"""
+
+import os, sys, copy
+from os.path import join, basename, splitext
+
+from reportlab.test import unittest
+from reportlab.test.utils import makeSuiteForClasses, outputfile, printLocation
+from reportlab.lib import colors
+from reportlab.lib.units import cm
+from reportlab.lib.pagesizes import A4
+from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
+from reportlab.lib.validators import Auto
+from reportlab.pdfgen.canvas import Canvas
+from reportlab.graphics.shapes import *
+from reportlab.graphics.charts.textlabels import Label
+from reportlab.platypus.flowables import Spacer, PageBreak
+from reportlab.platypus.paragraph import Paragraph
+from reportlab.platypus.xpreformatted import XPreformatted
+from reportlab.platypus.frames import Frame
+from reportlab.platypus.doctemplate \
+     import PageTemplate, BaseDocTemplate
+
+from reportlab.graphics.charts.barcharts import VerticalBarChart
+from reportlab.graphics.charts.linecharts import HorizontalLineChart
+from reportlab.graphics.charts.lineplots import LinePlot
+from reportlab.graphics.charts.piecharts import Pie
+from reportlab.graphics.charts.legends import Legend
+from reportlab.graphics.charts.spider import SpiderChart
+from reportlab.graphics.widgets.markers import makeMarker
+
+
+
+def myMainPageFrame(canvas, doc):
+    "The page frame used for all PDF documents."
+
+    canvas.saveState()
+
+    #canvas.rect(2.5*cm, 2.5*cm, 15*cm, 25*cm)
+    canvas.setFont('Times-Roman', 12)
+    pageNumber = canvas.getPageNumber()
+    canvas.drawString(10*cm, cm, str(pageNumber))
+
+    canvas.restoreState()
+
+
+class MyDocTemplate(BaseDocTemplate):
+    "The document template used for all PDF documents."
+
+    _invalidInitArgs = ('pageTemplates',)
+
+    def __init__(self, filename, **kw):
+        frame1 = Frame(2.5*cm, 2.5*cm, 15*cm, 25*cm, id='F1')
+        self.allowSplitting = 0
+        apply(BaseDocTemplate.__init__, (self, filename), kw)
+        template = PageTemplate('normal', [frame1], myMainPageFrame)
+        self.addPageTemplates(template)
+
+
+def sample1bar(data=[(13, 5, 20, 22, 37, 45, 19, 4)]):
+    drawing = Drawing(400, 200)
+
+    bc = VerticalBarChart()
+    bc.x = 50
+    bc.y = 50
+    bc.height = 125
+    bc.width = 300
+    bc.data = data
+
+    bc.strokeColor = colors.black
+
+    bc.valueAxis.valueMin = 0
+    bc.valueAxis.valueMax = 60
+    bc.valueAxis.valueStep = 15
+
+    bc.categoryAxis.labels.boxAnchor = 'ne'
+    bc.categoryAxis.labels.dx = 8
+    bc.categoryAxis.labels.dy = -2
+    bc.categoryAxis.labels.angle = 30
+
+    catNames = string.split('Jan Feb Mar Apr May Jun Jul Aug', ' ')
+    catNames = map(lambda n:n+'-99', catNames)
+    bc.categoryAxis.categoryNames = catNames
+    drawing.add(bc)
+
+    return drawing
+
+
+def sample2bar(data=[(13, 5, 20, 22, 37, 45, 19, 4),
+                  (14, 6, 21, 23, 38, 46, 20, 5)]):
+    return sample1bar(data)
+
+
+def sample1line(data=[(13, 5, 20, 22, 37, 45, 19, 4)]):
+    drawing = Drawing(400, 200)
+
+    bc = HorizontalLineChart()
+    bc.x = 50
+    bc.y = 50
+    bc.height = 125
+    bc.width = 300
+    bc.data = data
+
+    bc.strokeColor = colors.black
+
+    bc.valueAxis.valueMin = 0
+    bc.valueAxis.valueMax = 60
+    bc.valueAxis.valueStep = 15
+
+    bc.categoryAxis.labels.boxAnchor = 'ne'
+    bc.categoryAxis.labels.dx = 8
+    bc.categoryAxis.labels.dy = -2
+    bc.categoryAxis.labels.angle = 30
+
+    catNames = string.split('Jan Feb Mar Apr May Jun Jul Aug', ' ')
+    catNames = map(lambda n:n+'-99', catNames)
+    bc.categoryAxis.categoryNames = catNames
+    drawing.add(bc)
+
+    return drawing
+
+
+def sample2line(data=[(13, 5, 20, 22, 37, 45, 19, 4),
+                  (14, 6, 21, 23, 38, 46, 20, 5)]):
+    return sample1line(data)
+
+
+def sample3(drawing=None):
+    "Add sample swatches to a diagram."
+
+    d = drawing or Drawing(400, 200)
+
+    swatches = Legend()
+    swatches.alignment = 'right'
+    swatches.x = 80
+    swatches.y = 160
+    swatches.deltax = 60
+    swatches.dxTextSpace = 10
+    swatches.columnMaximum = 4
+    items = [(colors.red, 'before'), (colors.green, 'after')]
+    swatches.colorNamePairs = items
+
+    d.add(swatches, 'legend')
+
+    return d
+
+
+def sample4pie():
+    width = 300
+    height = 150
+    d = Drawing(width, height)
+    pc = Pie()
+    pc.x = 150
+    pc.y = 50
+    pc.data = [1, 50, 100, 100, 100, 100, 100, 100, 100, 50]
+    pc.labels = ['0','a','b','c','d','e','f','g','h','i']
+    pc.slices.strokeWidth=0.5
+    pc.slices[3].popout = 20
+    pc.slices[3].strokeWidth = 2
+    pc.slices[3].strokeDashArray = [2,2]
+    pc.slices[3].labelRadius = 1.75
+    pc.slices[3].fontColor = colors.red
+    d.add(pc)
+    legend = Legend()
+    legend.x = width-5
+    legend.y = height-5
+    legend.dx = 20
+    legend.dy = 5
+    legend.deltax = 0
+    legend.boxAnchor = 'nw'
+    legend.colorNamePairs=Auto(chart=pc)
+    d.add(legend)
+    return d
+
+def autoLegender(i,chart,styleObj,sym='symbol'):
+    if sym:
+        setattr(styleObj[0],sym, makeMarker('Diamond',size=6))
+        setattr(styleObj[1],sym,makeMarker('Square'))
+    width = 300
+    height = 150
+    legend = Legend()
+    legend.x = width-5
+    legend.y = 5
+    legend.dx = 20
+    legend.dy = 5
+    legend.deltay = 0
+    legend.boxAnchor = 'se'
+    if i=='col auto':
+        legend.colorNamePairs[0]=(Auto(chart=chart),'auto chart=self.chart')
+        legend.colorNamePairs[1]=(Auto(obj=chart,index=1),'auto  chart=self.chart index=1')
+    elif i=='full auto':
+        legend.colorNamePairs=Auto(chart=chart)
+    elif i=='swatch set':
+        legend.swatchMarker=makeMarker('Circle')
+        legend.swatchMarker.size = 10
+    elif i=='swatch auto':
+        legend.swatchMarker=Auto(chart=chart)
+    d = Drawing(width,height)
+    d.background = Rect(0,0,width,height,strokeWidth=1,strokeColor=colors.red,fillColor=None)
+    m = makeMarker('Cross')
+    m.x = width-5
+    m.y = 5
+    m.fillColor = colors.red
+    m.strokeColor = colors.yellow
+    d.add(chart)
+    d.add(legend)
+    d.add(m)
+    return d
+
+def lpleg(i=None):
+    chart = LinePlot()
+    return autoLegender(i,chart,chart.lines)
+
+def hlcleg(i=None):
+    chart = HorizontalLineChart()
+    return autoLegender(i,chart,chart.lines)
+
+def bcleg(i=None):
+    chart = VerticalBarChart()
+    return autoLegender(i,chart,chart.bars,None)
+
+def pcleg(i=None):
+    chart = Pie()
+    return autoLegender(i,chart,chart.slices,None)
+
+def scleg(i=None):
+    chart = SpiderChart()
+    return autoLegender(i,chart,chart.strands,None)
+
+def plpleg(i=None):
+    from reportlab.lib.colors import pink, red, green
+    pie = Pie()
+    pie.x = 0
+    pie.y = 0
+    pie.pointerLabelMode='LeftAndRight'
+    pie.slices.label_boxStrokeColor      = red
+    pie.simpleLabels = 0
+    pie.sameRadii = 1
+    pie.data = [1, 0.1, 1.7, 4.2,0,0]
+    pie.labels = ['abcdef', 'b', 'c', 'd','e','fedcba']
+    pie.strokeWidth=1
+    pie.strokeColor=green
+    pie.slices.label_pointer_piePad      = 6
+    pie.width = 160
+    pie.direction = 'clockwise'
+    pie.pointerLabelMode  = 'LeftRight'
+    return autoLegender(i,pie,pie.slices,None)
+
+def notFail(d):
+    try:
+        return d.getContents()
+    except:
+        import traceback
+        traceback.print_exc()
+        return None
+
+STORY = []
+styleSheet = getSampleStyleSheet()
+bt = styleSheet['BodyText']
+h1 = styleSheet['Heading1']
+h2 = styleSheet['Heading2']
+h3 = styleSheet['Heading3']
+FINISHED = 0
+
+
+class ChartTestCase(unittest.TestCase):
+    "Test chart classes."
+
+    def setUp(self):
+        "Hook method for setting up the test fixture before exercising it."
+
+        global STORY
+        self.story = STORY
+
+        if self.story == []:
+            self.story.append(Paragraph('Tests for chart classes', h1))
+
+    def tearDown(self):
+        "Hook method for deconstructing the test fixture after testing it."
+
+        if FINISHED:
+            path=outputfile('test_graphics_charts.pdf')
+            doc = MyDocTemplate(path)
+            doc.build(self.story)
+
+    def test0(self):
+        "Test bar charts."
+
+        story = self.story
+        story.append(Paragraph('Single data row', h2))
+
+        story.append(Spacer(0, 0.5*cm))
+        drawing = sample1bar()
+        story.append(drawing)
+        story.append(Spacer(0, 1*cm))
+
+
+    def test1(self):
+        "Test bar charts."
+
+        story = self.story
+        story.append(Paragraph('Double data row', h2))
+
+        story.append(Spacer(0, 0.5*cm))
+        drawing = sample2bar()
+        story.append(drawing)
+        story.append(Spacer(0, 1*cm))
+
+
+    def test2(self):
+        "Test bar charts."
+
+        story = self.story
+        story.append(Paragraph('Double data row with legend', h2))
+
+        story.append(Spacer(0, 0.5*cm))
+        drawing = sample2bar()
+        drawing = sample3(drawing)
+        story.append(drawing)
+        story.append(Spacer(0, 1*cm))
+
+
+    def test3(self):
+        "Test line charts."
+
+        story = self.story
+        story.append(Paragraph('Single data row', h2))
+
+        story.append(Spacer(0, 0.5*cm))
+        drawing = sample1line()
+        story.append(drawing)
+        story.append(Spacer(0, 1*cm))
+
+
+    def test4(self):
+        "Test line charts."
+
+        story = self.story
+        story.append(Paragraph('Single data row', h2))
+
+        story.append(Spacer(0, 0.5*cm))
+        drawing = sample2line()
+        story.append(drawing)
+        story.append(Spacer(0, 1*cm))
+
+    def test4b(self):
+        story = self.story
+        for code in (lpleg, hlcleg, bcleg, pcleg, scleg, plpleg):
+            code_name = code.func_code.co_name
+            for i in ('standard', 'col auto', 'full auto', 'swatch set', 'swatch auto'):
+                d = code(i)
+                assert notFail(d),'getContents failed for %s %s' % (code_name,i)
+                story.append(Paragraph('%s %s' % (i,code_name), h2))
+                story.append(Spacer(0, 0.5*cm))
+                story.append(code(i))
+                story.append(Spacer(0, 1*cm))
+
+    def test5(self):
+        "Test pie charts."
+
+        story = self.story
+        story.append(Paragraph('Pie', h2))
+
+        story.append(Spacer(0, 0.5*cm))
+        drawing = sample4pie()
+        story.append(drawing)
+        story.append(Spacer(0, 1*cm))
+
+    def test6(self):
+        from reportlab.graphics.charts.piecharts import Pie, _makeSideArcDefs, intervalIntersection
+        L = []
+
+        def intSum(arcs,A):
+            s = 0
+            for a in A:
+                for arc in arcs:
+                    i = intervalIntersection(arc[1:],a)
+                    if i: s += i[1] - i[0]
+            return s
+
+        def subtest(sa,d):
+            pc = Pie()
+            pc.direction=d
+            pc.startAngle=sa
+            arcs = _makeSideArcDefs(sa,d)
+            A = [x[1] for x in pc.makeAngles()]
+            arcsum = sum([a[2]-a[1] for a in arcs])
+            isum = intSum(arcs,A)
+            mi = max([a[2]-a[1] for a in arcs])
+            ni = min([a[2]-a[1] for a in arcs])
+            l = []
+            s = (arcsum-360)
+            if s>1e-8: l.append('Arc length=%s != 360' % s)
+            s = abs(isum-360)
+            if s>1e-8: l.append('interval intersection length=%s != 360' % s)
+            if mi>360: l.append('max interval intersection length=%s >360' % mi)
+            if ni<0: l.append('min interval intersection length=%s <0' % ni)
+            if l:
+                l.append('sa: %s d: %s' % (sa,d))
+                l.append('sidearcs: %s' % str(arcs))
+                l.append('Angles: %s' % A)
+                raise ValueError('piecharts._makeSideArcDefs failure\n%s' % '\n'.join(l))
+
+        for d in ('anticlockwise','clockwise'):
+            for sa in (225, 180, 135, 90, 45, 0, -45, -90):
+                subtest(sa,d)
+
+        # This triggers the document build operation (hackish).
+        global FINISHED
+        FINISHED = 1
+
+def makeSuite():
+    return makeSuiteForClasses(ChartTestCase)
+
+
+#noruntests
+if __name__ == "__main__":
+    unittest.TextTestRunner().run(makeSuite())
+    printLocation()