Ejemplo n.º 1
0
 def sample4d():
     "Sample drawing, xvalue/yvalue axes, y connected to top of x."
     drawing = Drawing(400, 200)
     data = [(10, 20, 30, 42)]
     yAxis = YValueAxis()
     yAxis.setPosition(50, 50, 125)
     yAxis.configure(data)
     xAxis = XValueAxis()
     xAxis._length = 300
     xAxis.joinAxis = yAxis
     xAxis.joinAxisMode = 'top'
     xAxis.configure(data)
     drawing.add(xAxis)
     drawing.add(yAxis)
     return drawing
Ejemplo n.º 2
0
    def __init__(self):
        PlotArea.__init__(self)
        self.reversePlotOrder = 0

        self.xValueAxis = XValueAxis()
        self.yValueAxis = YValueAxis()

        # this defines two series of 3 points.  Just an example.
        self.data = [((1, 1), (2, 2), (2.5, 1), (3, 3), (4, 5)), ((1, 2), (2, 3), (2.5, 2), (3, 4), (4, 6))]

        self.lines = TypedPropertyCollection(LinePlotProperties)
        self.lines.strokeWidth = 1
        self.lines[0].strokeColor = colors.red
        self.lines[1].strokeColor = colors.blue

        self.lineLabels = TypedPropertyCollection(Label)
        self.lineLabelFormat = None
        self.lineLabelArray = None

        # this says whether the origin is inside or outside
        # the bar - +10 means put the origin ten points
        # above the tip of the bar if value > 0, or ten
        # points inside if bar value < 0.  This is different
        # to label dx/dy which are not dependent on the
        # sign of the data.
        self.lineLabelNudge = 10
        # if you have multiple series, by default they butt
        # together.

        # New line chart attributes.
        self.joinedLines = 1  # Connect items with straight lines.

        # private attributes
        self._inFill = None
Ejemplo n.º 3
0
    def __init__(self):
        LineChart.__init__(self)

        # Allow for a bounding rectangle.
        self.strokeColor = None
        self.fillColor = None

        # Named so we have less recoding for the horizontal one :-)
        self.categoryAxis = XCategoryAxis()
        self.valueAxis = YValueAxis()

        # This defines two series of 3 points.  Just an example.
        self.data = [(100,110,120,130),
                     (70, 80, 80, 90)]
        self.categoryNames = ('North','South','East','West')

        self.lines = TypedPropertyCollection(LineChartProperties)
        self.lines.strokeWidth = 1
        self.lines[0].strokeColor = colors.red
        self.lines[1].strokeColor = colors.green
        self.lines[2].strokeColor = colors.blue

        # control spacing. if useAbsolute = 1 then
        # the next parameters are in points; otherwise
        # they are 'proportions' and are normalized to
        # fit the available space.
        self.useAbsolute = 0   #- not done yet
        self.groupSpacing = 1 #5

        self.lineLabels = TypedPropertyCollection(Label)
        self.lineLabelFormat = None
        self.lineLabelArray = None

        # This says whether the origin is above or below
        # the data point. +10 means put the origin ten points
        # above the data point if value > 0, or ten
        # points below if data value < 0.  This is different
        # to label dx/dy which are not dependent on the
        # sign of the data.
        self.lineLabelNudge = 10
        # If you have multiple series, by default they butt
        # together.

        # New line chart attributes.
        self.joinedLines = 1 # Connect items with straight lines.
        self.inFill = 0
        self.reversePlotOrder = 0
Ejemplo n.º 4
0
    def makeTickLabels(self):
        g = YValueAxis.makeTickLabels(self)

        desc_text = make_desc_test(self, "Y")
        if desc_text is not None:
            g.add(desc_text)

        return g
Ejemplo n.º 5
0
 def sample6b():
     "Sample drawing, xcat/yvalue axes, x connected at bottom of y."
     drawing = Drawing(400, 200)
     data = [(10, 20, 30, 42)]
     yAxis = YValueAxis()
     yAxis.setPosition(50, 50, 125)
     yAxis.configure(data)
     xAxis = XCategoryAxis()
     xAxis._length = 300
     xAxis.configure(data)
     xAxis.joinAxis = yAxis
     xAxis.joinAxisMode = 'bottom'
     xAxis.categoryNames = ['Beer', 'Wine', 'Meat', 'Cannelloni']
     xAxis.labels.boxAnchor = 'n'
     drawing.add(xAxis)
     drawing.add(yAxis)
     return drawing
Ejemplo n.º 6
0
    def __init__(self, data=None, legend_data=None):
        Drawing.__init__(self, width=530, height=160)

        self._add(self,
                  MyChartFrame(x=0, y=0, width=530, height=160,
                               title='Volume'),
                  name='frame')

        # chart
        self._add(self, LinePlot(), name='chart')
        self.chart.y = 20
        self.chart.x = 34
        self.chart.width = 423
        self.chart.height = 110

        # line styles
        self.chart.lines.symbol = makeMarker('Circle', size=2)

        colors = map(toColor, get_n_random_colors(len(legend_data)))
        for (i, _) in enumerate(legend_data):
            self.chart.lines[i].strokeColor = colors[i]

        # x axis
        self.chart.xValueAxis = NormalDateXValueAxis()
        self.chart.xValueAxis.xLabelFormat = '{dd} {MMM}'
        self.chart.xValueAxis.loLLen = 8
        self.chart.xValueAxis.hiLLen = 5
        self.chart.xValueAxis.labels.fontName = 'Lato'
        self.chart.xValueAxis.labels.fontSize = 9

        # y axis
        self.chart.yValueAxis = YValueAxis()
        self.chart.yValueAxis.visibleGrid = 1
        self.chart.yValueAxis.visibleAxis = 0
        self.chart.yValueAxis.strokeWidth = 0.25
        self.chart.yValueAxis.labels.rightPadding = 5
        self.chart.yValueAxis.labels.fontName = 'Lato'
        self.chart.yValueAxis.labels.fontSize = 9
        self.chart.yValueAxis.rangeRound = 'both'
        self.chart.yValueAxis.tickLeft = 7.5
        self.chart.yValueAxis.minimumTickSpacing = 0.5
        self.chart.yValueAxis.maximumTicks = 8
        self.chart.yValueAxis.avoidBoundFrac = 0.1

        # legend
        self._add(self, LineLegend(), name='legend')
        self.legend.alignment = 'right'
        self.legend.y = 110
        self.legend.x = 462
        self.legend.dxTextSpace = 5
        self.legend.columnMaximum = 4
        self.legend.fontName = 'Lato'
        self.legend.fontSize = 9

        self.legend.colorNamePairs = zip(colors, legend_data)

        # Data...
        self.chart.data = data
Ejemplo n.º 7
0
 def sample1():
     "Sample drawing containing two unconnected axes."
     from reportlab.graphics.shapes import _baseGFontNameB
     drawing = Drawing(400, 200)
     data = [(10, 20, 30, 42)]
     xAxis = XCategoryAxis()
     xAxis.setPosition(75, 75, 300)
     xAxis.configure(data)
     xAxis.categoryNames = ['Beer','Wine','Meat','Cannelloni']
     xAxis.labels.boxAnchor = 'n'
     xAxis.labels[3].dy = -15
     xAxis.labels[3].angle = 30
     xAxis.labels[3].fontName = _baseGFontNameB
     yAxis = YValueAxis()
     yAxis.setPosition(50, 50, 125)
     yAxis.configure(data)
     drawing.add(xAxis)
     drawing.add(yAxis)
     return drawing
Ejemplo n.º 8
0
 def sample5b():
     "Sample drawing, xvalue/yvalue axes, y connected at value 35 of x."
     drawing = Drawing(400, 200)
     data = [(10, 20, 30, 42)]
     xAxis = XValueAxis()
     xAxis.setPosition(50, 50, 300)
     xAxis.configure(data)
     yAxis = YValueAxis()
     yAxis.setPosition(50, 50, 125)
     yAxis.joinAxis = xAxis
     yAxis.joinAxisMode = 'value'
     yAxis.joinAxisPos = 35
     yAxis.configure(data)
     drawing.add(xAxis)
     drawing.add(yAxis)
     return drawing
Ejemplo n.º 9
0
    def __init__(self):
        PlotArea.__init__(self)
        self.reversePlotOrder = 0

        self.xValueAxis = XValueAxis()
        self.yValueAxis = YValueAxis()

        # this defines two series of 3 points.  Just an example.
        self.data = [
            ((1,1), (2,2), (2.5,1), (3,3), (4,5)),
            ((1,2), (2,3), (2.5,2), (3,4), (4,6))
            ]

        self.lines = TypedPropertyCollection(LinePlotProperties)
        self.lines.strokeWidth = 1
        self.lines[0].strokeColor = colors.red
        self.lines[1].strokeColor = colors.blue

        self.lineLabels = TypedPropertyCollection(Label)
        self.lineLabelFormat = None
        self.lineLabelArray = None

        # this says whether the origin is inside or outside
        # the bar - +10 means put the origin ten points
        # above the tip of the bar if value > 0, or ten
        # points inside if bar value < 0.  This is different
        # to label dx/dy which are not dependent on the
        # sign of the data.
        self.lineLabelNudge = 10
        # if you have multiple series, by default they butt
        # together.

        # New line chart attributes.
        self.joinedLines = 1 # Connect items with straight lines.

        #private attributes
        self._inFill = None
        self.annotations = []
        self.behindAxes = 0
        self.gridFirst = 0
Ejemplo n.º 10
0
 def sample5b():
     "Sample drawing, xvalue/yvalue axes, y connected at value 35 of x."
     drawing = Drawing(400, 200)
     data = [(10, 20, 30, 42)]
     xAxis = XValueAxis()
     xAxis.setPosition(50, 50, 300)
     xAxis.configure(data)
     yAxis = YValueAxis()
     yAxis.setPosition(50, 50, 125)
     yAxis.joinAxis = xAxis
     yAxis.joinAxisMode = 'value'
     yAxis.joinAxisPos = 35
     yAxis.configure(data)
     drawing.add(xAxis)
     drawing.add(yAxis)
     return drawing
Ejemplo n.º 11
0
    def __init__(self):
        LineChart.__init__(self)

        # Allow for a bounding rectangle.
        self.strokeColor = None
        self.fillColor = None

        # Named so we have less recoding for the horizontal one :-)
        self.categoryAxis = XCategoryAxis()
        self.valueAxis = YValueAxis()

        # This defines two series of 3 points.  Just an example.
        self.data = [(100,110,120,130),
                     (70, 80, 80, 90)]
        self.categoryNames = ('North','South','East','West')

        self.lines = TypedPropertyCollection(LineChartProperties)
        self.lines.strokeWidth = 1
        self.lines[0].strokeColor = colors.red
        self.lines[1].strokeColor = colors.green
        self.lines[2].strokeColor = colors.blue

        # control spacing. if useAbsolute = 1 then
        # the next parameters are in points; otherwise
        # they are 'proportions' and are normalized to
        # fit the available space.
        self.useAbsolute = 0   #- not done yet
        self.groupSpacing = 1 #5

        self.lineLabels = TypedPropertyCollection(Label)
        self.lineLabelFormat = None
        self.lineLabelArray = None

        # This says whether the origin is above or below
        # the data point. +10 means put the origin ten points
        # above the data point if value > 0, or ten
        # points below if data value < 0.  This is different
        # to label dx/dy which are not dependent on the
        # sign of the data.
        self.lineLabelNudge = 10
        # If you have multiple series, by default they butt
        # together.

        # New line chart attributes.
        self.joinedLines = 1 # Connect items with straight lines.
        self.inFill = 0
        self.reversePlotOrder = 0
Ejemplo n.º 12
0
 def sample4c1():
     "xvalue/yvalue axes, without drawing axis lines/ticks."
     drawing = Drawing(400, 200)
     data = [(10, 20, 30, 42)]
     yAxis = YValueAxis()
     yAxis.setPosition(50, 50, 125)
     yAxis.configure(data)
     yAxis.visibleAxis = 0
     yAxis.visibleTicks = 0
     xAxis = XValueAxis()
     xAxis._length = 300
     xAxis.joinAxis = yAxis
     xAxis.joinAxisMode = 'bottom'
     xAxis.configure(data)
     xAxis.visibleAxis = 0
     xAxis.visibleTicks = 0
     drawing.add(xAxis)
     drawing.add(yAxis)
     return drawing
Ejemplo n.º 13
0
    def __init__(self, data=None):
        Drawing.__init__(self, width=458, height=130)

        # chart
        self._add(self, LinePlot(), name='chart')
        self.chart.y = 20
        self.chart.x = 60
        self.chart.width = 320
        self.chart.height = 100

        # line styles
        self.chart.lines.symbol = makeMarker('Circle', size=2)

        colors = map(toColor, get_n_random_colors(len(data)))
        for (i, _) in enumerate(data):
            self.chart.lines[i].strokeColor = colors[i]

        # x axis
        self.chart.xValueAxis = NormalDateXValueAxis()
        self.chart.xValueAxis.xLabelFormat = '{dd} {MMM}'
        self.chart.xValueAxis.loLLen = 8
        self.chart.xValueAxis.hiLLen = 5
        self.chart.xValueAxis.labels.fontName = 'Lato'
        self.chart.xValueAxis.labels.fontSize = 9

        # y axis
        self.chart.yValueAxis = YValueAxis()
        self.chart.yValueAxis.visibleGrid = 1
        self.chart.yValueAxis.visibleAxis = 0
        self.chart.yValueAxis.strokeWidth = 0.25
        self.chart.yValueAxis.labels.rightPadding = 5
        self.chart.yValueAxis.labels.fontName = 'Lato'
        self.chart.yValueAxis.labels.fontSize = 9
        self.chart.yValueAxis.rangeRound = 'both'
        self.chart.yValueAxis.tickLeft = 7.5
        self.chart.yValueAxis.minimumTickSpacing = 0.5
        self.chart.yValueAxis.maximumTicks = 8
        self.chart.yValueAxis.avoidBoundFrac = 0.1

        # Data...
        self.chart.data = data
Ejemplo n.º 14
0
 def sample4d():
     "Sample drawing, xvalue/yvalue axes, y connected to top of x."
     drawing = Drawing(400, 200)
     data = [(10, 20, 30, 42)]
     yAxis = YValueAxis()
     yAxis.setPosition(50, 50, 125)
     yAxis.configure(data)
     xAxis = XValueAxis()
     xAxis._length = 300
     xAxis.joinAxis = yAxis
     xAxis.joinAxisMode = 'top'
     xAxis.configure(data)
     drawing.add(xAxis)
     drawing.add(yAxis)
     return drawing
Ejemplo n.º 15
0
 def sample4c1():
     "xvalue/yvalue axes, without drawing axis lines/ticks."
     drawing = Drawing(400, 200)
     data = [(10, 20, 30, 42)]
     yAxis = YValueAxis()
     yAxis.setPosition(50, 50, 125)
     yAxis.configure(data)
     yAxis.visibleAxis = 0
     yAxis.visibleTicks = 0
     xAxis = XValueAxis()
     xAxis._length = 300
     xAxis.joinAxis = yAxis
     xAxis.joinAxisMode = 'bottom'
     xAxis.configure(data)
     xAxis.visibleAxis = 0
     xAxis.visibleTicks = 0
     drawing.add(xAxis)
     drawing.add(yAxis)
     return drawing
Ejemplo n.º 16
0
 def sample6b():
     "Sample drawing, xcat/yvalue axes, x connected at bottom of y."
     drawing = Drawing(400, 200)
     data = [(10, 20, 30, 42)]
     yAxis = YValueAxis()
     yAxis.setPosition(50, 50, 125)
     yAxis.configure(data)
     xAxis = XCategoryAxis()
     xAxis._length = 300
     xAxis.configure(data)
     xAxis.joinAxis = yAxis
     xAxis.joinAxisMode = 'bottom'
     xAxis.categoryNames = ['Beer', 'Wine', 'Meat', 'Cannelloni']
     xAxis.labels.boxAnchor = 'n'
     drawing.add(xAxis)
     drawing.add(yAxis)
     return drawing
Ejemplo n.º 17
0
 def sample1():
     "Sample drawing containing two unconnected axes."
     from reportlab.graphics.shapes import _baseGFontNameB
     drawing = Drawing(400, 200)
     data = [(10, 20, 30, 42)]
     xAxis = XCategoryAxis()
     xAxis.setPosition(75, 75, 300)
     xAxis.configure(data)
     xAxis.categoryNames = ['Beer', 'Wine', 'Meat', 'Cannelloni']
     xAxis.labels.boxAnchor = 'n'
     xAxis.labels[3].dy = -15
     xAxis.labels[3].angle = 30
     xAxis.labels[3].fontName = _baseGFontNameB
     yAxis = YValueAxis()
     yAxis.setPosition(50, 50, 125)
     yAxis.configure(data)
     drawing.add(xAxis)
     drawing.add(yAxis)
     return drawing
Ejemplo n.º 18
0
def axes_demo():
    drawing = shapes.Drawing(width=500, height=300)

    data = [(5, 10, 15, 20), (10, 17, 25, 31)]

    x_axis = XCategoryAxis()
    x_axis.setPosition(100, 100, 350)
    x_axis.configure(data, barWidth=None)
    x_axis.categoryNames = ['Python', 'Ruby', 'C++', 'Haskell', 'Java']
    x_axis.labels.boxAnchor = 'n'
    x_axis.labels[0].angle = 45
    x_axis.labels[0].fontName = 'Times-Bold'
    x_axis.labels[1].fontName = 'Courier'
    x_axis.labels[1].fontSize = 16
    drawing.add(x_axis)

    y_axis = YValueAxis()
    y_axis.setPosition(75, 75, 150)
    y_axis.configure(data)
    drawing.add(y_axis)

    renderPDF.drawToFile(drawing, 'axes_demo.pdf')
Ejemplo n.º 19
0
    def generateGraphs(self, x, y):
        (x1, y1, Width, Height) = self._getGraphRegion(x, y)
        #Draw Axes
        yVAxis = YValueAxis()
        #If we got numeric X vals, we use a ValueAxis, otherwise CategoryAxis.
        if len(self.x_vals) > 0:
            if isinstance(self.x_vals[0], str):
                xVAxis = XCategoryAxis()
            else:
                xVAxis = XValueAxis()
        else:
            return -1
        
        (y_min, y_max, y_step) = self.getValueAxisScale(yVAxis, [self.y_vals])

        if y_min == -1 and y_min == -1 and y_step == -1:
            return -1
        
        (self.valueMin, self.valueMax, self.valueStep) = (y_min, y_max, y_step)
        
        (SizeXaxis, SizeYaxis) = self.getSizes()
        #lot of ugliness in here to get the split chart working :|
        if self.betweenSplitsF == True:
            if self.numSplits == 0:
                self.tmpYVal = SizeYaxis
            else:
                SizeYaxis = self.tmpYVal
        if self.numSplits > 0:
            xVAxis.visibleLabels = False
            xVAxis.visibleTicks = False
        X_start_pos = x1 - x + SizeYaxis
        X_width = Width - SizeYaxis - 10
        if self.betweenSplitsF == True:
            if self.numSplits > 0:
                Y_start_pos = y1 - y + SizeXaxis + (self.numSplits *
                        self.height) - (self.numSplits * 15)
            else:
                Y_start_pos = y1 - y + SizeXaxis + (self.numSplits *
                        self.height)
            Y_height = Height - SizeXaxis + 40
        else:
            Y_start_pos = y1 - y + SizeXaxis + (self.numSplits *
                    self.height)
            Y_height = Height - SizeXaxis
        xVAxis.setPosition(X_start_pos, Y_start_pos, X_width)
        if isinstance(xVAxis, XValueAxis):
            dataList = []
            dataTuple =  reduce(lambda a,b: a + (b,), self.x_vals, ())
            dataList.append(dataTuple)
            xVAxis.configure(dataList)
            xVAxis.labelTextFormat = '%0.' + '%d' % self.xAxisDigits + 'f'
        else:
            dataList = []
            zerodata = [0 for val in self.x_vals]
            dataTuple =  reduce(lambda a,b: a + (b,), zerodata, ())
            dataList.append(dataTuple)
            xVAxis.configure(dataList)
            xVAxis.categoryNames = self.x_vals
            #Ugly hack for setting the labels.dy. Empirical: If there are more than 5 
            #chars, dy = -35 fits fine (assuming the angle would be > 75,when we have 
            #so long val)
            maxLen = 0
            for val in self.x_vals:
                valLen = len(val)
                if valLen > maxLen:
                    maxLen = valLen
            if maxLen > 5:
                xVAxis.labels.dy = -35
                           
        xVAxis.labels.fontName = 'Helvetica'
        xVAxis.labels.fontSize = 7
        xVAxis.labels.angle = self.xValsDisplayAngle
        drawLegendF = False
        if len(self.legendList) > 0:
            drawLegendF = True
        drawLabelF = False
        lblCounts = len(self.x_vals) * len(self.y_vals)
        if lblCounts <= 30 and self.displayDataLbls == True:
            drawLabelF = True
        if len(self.x_vals) == 1:
            drawLabelF = True            
        #hack to increase chart height to include legend and labels
        total_height = 0
        legendHeight = 0
        if drawLegendF == True:
            #Kludge: We enchroach the space needed for legends by bringing down the 
            #y value by 28 (in drawOn()), we use that space here
            legendHeight = Y_height + 28
        graph_height = Y_height
        yVAxis.setPosition(X_start_pos, Y_start_pos, Y_height)
        yVAxis.valueMin = y_min
        yVAxis.valueMax = y_max    
        yVAxis.valueStep = y_step
        yVAxis.labels.fontName = 'Helvetica'
        yVAxis.labels.fontSize = 7
        yVAxis.labelTextFormat = '%0.' + '%d' % self.yAxisDigits + 'f'
        yVAxis.configure(self.y_vals)
        #will later sync the yVAxis as the ValueAxis of the graph
        self.drawing.add(xVAxis)
        if self.numSplits == 0:
            tmp = self.height
            self.height = self.origHeight
            self._drawLabels(self.title, self.x_label, self.y_label)
            self.height = tmp
        for graphtype in self.graphList:
            #Draw only one set of Legends/Labels for any dataset,
            #even if there are multiple charts drawn for the same dataset
            if self.labelsDrawnF == True and self.numSplits == 0:
                drawLabelF = False
            if self.legendsDrawnF == True and self.numSplits == 0:
                drawLegendF = False
            if graphtype == 'Bar':
                GraphObj = VerticalBarChart()
                GraphObj.valueAxis = yVAxis
                self.drawGraph(GraphObj, X_start_pos, Y_start_pos, X_width,
                        graph_height, drawLabelF, drawLegendF, legendHeight)
            if graphtype == 'Line':
                GraphObj = HorizontalLineChart()
                GraphObj.valueAxis = yVAxis
                self.drawGraph(GraphObj, X_start_pos, Y_start_pos, X_width,
                        graph_height, drawLabelF, drawLegendF, legendHeight)
Ejemplo n.º 20
0
class HorizontalLineChart(LineChart):
    """Line chart with multiple lines.

    A line chart is assumed to have one category and one value axis.
    Despite its generic name this particular line chart class has
    a vertical value axis and a horizontal category one. It may
    evolve into individual horizontal and vertical variants (like
    with the existing bar charts).

    Available attributes are:

        x: x-position of lower-left chart origin
        y: y-position of lower-left chart origin
        width: chart width
        height: chart height

        useAbsolute: disables auto-scaling of chart elements (?)
        lineLabelNudge: distance of data labels to data points
        lineLabels: labels associated with data values
        lineLabelFormat: format string or callback function
        groupSpacing: space between categories

        joinedLines: enables drawing of lines

        strokeColor: color of chart lines (?)
        fillColor: color for chart background (?)
        lines: style list, used cyclically for data series

        valueAxis: value axis object
        categoryAxis: category axis object
        categoryNames: category names

        data: chart data, a list of data series of equal length
    """

    _attrMap = AttrMap(BASE=LineChart,
        useAbsolute = AttrMapValue(isNumber, desc='Flag to use absolute spacing values.',advancedUsage=1),
        lineLabelNudge = AttrMapValue(isNumber, desc='Distance between a data point and its label.',advancedUsage=1),
        lineLabels = AttrMapValue(None, desc='Handle to the list of data point labels.'),
        lineLabelFormat = AttrMapValue(None, desc='Formatting string or function used for data point labels.'),
        lineLabelArray = AttrMapValue(None, desc='explicit array of line label values, must match size of data if present.'),
        groupSpacing = AttrMapValue(isNumber, desc='? - Likely to disappear.'),
        joinedLines = AttrMapValue(isNumber, desc='Display data points joined with lines if true.'),
        lines = AttrMapValue(None, desc='Handle of the lines.'),
        valueAxis = AttrMapValue(None, desc='Handle of the value axis.'),
        categoryAxis = AttrMapValue(None, desc='Handle of the category axis.'),
        categoryNames = AttrMapValue(isListOfStringsOrNone, desc='List of category names.'),
        data = AttrMapValue(None, desc='Data to be plotted, list of (lists of) numbers.'),
        inFill = AttrMapValue(isBoolean, desc='Whether infilling should be done.',advancedUsage=1),
        reversePlotOrder = AttrMapValue(isBoolean, desc='If true reverse plot order.',advancedUsage=1),
        annotations = AttrMapValue(None, desc='list of callables, will be called with self, xscale, yscale.',advancedUsage=1),
        )

    def __init__(self):
        LineChart.__init__(self)

        # Allow for a bounding rectangle.
        self.strokeColor = None
        self.fillColor = None

        # Named so we have less recoding for the horizontal one :-)
        self.categoryAxis = XCategoryAxis()
        self.valueAxis = YValueAxis()

        # This defines two series of 3 points.  Just an example.
        self.data = [(100,110,120,130),
                     (70, 80, 80, 90)]
        self.categoryNames = ('North','South','East','West')

        self.lines = TypedPropertyCollection(LineChartProperties)
        self.lines.strokeWidth = 1
        self.lines[0].strokeColor = colors.red
        self.lines[1].strokeColor = colors.green
        self.lines[2].strokeColor = colors.blue

        # control spacing. if useAbsolute = 1 then
        # the next parameters are in points; otherwise
        # they are 'proportions' and are normalized to
        # fit the available space.
        self.useAbsolute = 0   #- not done yet
        self.groupSpacing = 1 #5

        self.lineLabels = TypedPropertyCollection(Label)
        self.lineLabelFormat = None
        self.lineLabelArray = None

        # This says whether the origin is above or below
        # the data point. +10 means put the origin ten points
        # above the data point if value > 0, or ten
        # points below if data value < 0.  This is different
        # to label dx/dy which are not dependent on the
        # sign of the data.
        self.lineLabelNudge = 10
        # If you have multiple series, by default they butt
        # together.

        # New line chart attributes.
        self.joinedLines = 1 # Connect items with straight lines.
        self.inFill = 0
        self.reversePlotOrder = 0


    def demo(self):
        """Shows basic use of a line chart."""

        drawing = Drawing(200, 100)

        data = [
                (13, 5, 20, 22, 37, 45, 19, 4),
                (14, 10, 21, 28, 38, 46, 25, 5)
                ]

        lc = HorizontalLineChart()

        lc.x = 20
        lc.y = 10
        lc.height = 85
        lc.width = 170
        lc.data = data
        lc.lines.symbol = makeMarker('Circle')

        drawing.add(lc)

        return drawing


    def calcPositions(self):
        """Works out where they go.

        Sets an attribute _positions which is a list of
        lists of (x, y) matching the data.
        """

        self._seriesCount = len(self.data)
        self._rowLength = max(list(map(len,self.data)))

        if self.useAbsolute:
            # Dimensions are absolute.
            normFactor = 1.0
        else:
            # Dimensions are normalized to fit.
            normWidth = self.groupSpacing
            availWidth = self.categoryAxis.scale(0)[1]
            normFactor = availWidth / normWidth

        self._positions = []
        for rowNo in range(len(self.data)):
            lineRow = []
            for colNo in range(len(self.data[rowNo])):
                datum = self.data[rowNo][colNo]
                if datum is not None:
                    (groupX, groupWidth) = self.categoryAxis.scale(colNo)
                    x = groupX + (0.5 * self.groupSpacing * normFactor)
                    y = self.valueAxis.scale(0)
                    height = self.valueAxis.scale(datum) - y
                    lineRow.append((x, y+height))
            self._positions.append(lineRow)


    def _innerDrawLabel(self, rowNo, colNo, x, y):
        "Draw a label for a given item in the list."

        labelFmt = self.lineLabelFormat
        labelValue = self.data[rowNo][colNo]

        if labelFmt is None:
            labelText = None
        elif type(labelFmt) is str:
            if labelFmt == 'values':
                try:
                    labelText = self.lineLabelArray[rowNo][colNo]
                except:
                    labelText = None
            else:
                labelText = labelFmt % labelValue
        elif hasattr(labelFmt,'__call__'):
            labelText = labelFmt(labelValue)
        else:
            raise ValueError("Unknown formatter type %s, expected string or function"%labelFmt)

        if labelText:
            label = self.lineLabels[(rowNo, colNo)]
            if not label.visible: return
            # Make sure labels are some distance off the data point.
            if y > 0:
                label.setOrigin(x, y + self.lineLabelNudge)
            else:
                label.setOrigin(x, y - self.lineLabelNudge)
            label.setText(labelText)
        else:
            label = None
        return label

    def drawLabel(self, G, rowNo, colNo, x, y):
        '''Draw a label for a given item in the list.
        G must have an add method'''
        G.add(self._innerDrawLabel(rowNo,colNo,x,y))

    def makeLines(self):
        g = Group()

        labelFmt = self.lineLabelFormat
        P = list(range(len(self._positions)))
        if self.reversePlotOrder: P.reverse()
        inFill = self.inFill
        if inFill:
            inFillY = self.categoryAxis._y
            inFillX0 = self.valueAxis._x
            inFillX1 = inFillX0 + self.categoryAxis._length
            inFillG = getattr(self,'_inFillG',g)

        # Iterate over data rows.
        for rowNo in P:
            row = self._positions[rowNo]
            styleCount = len(self.lines)
            styleIdx = rowNo % styleCount
            rowStyle = self.lines[styleIdx]
            rowColor = rowStyle.strokeColor
            dash = getattr(rowStyle, 'strokeDashArray', None)

            if hasattr(self.lines[styleIdx], 'strokeWidth'):
                strokeWidth = self.lines[styleIdx].strokeWidth
            elif hasattr(self.lines, 'strokeWidth'):
                strokeWidth = self.lines.strokeWidth
            else:
                strokeWidth = None

            # Iterate over data columns.
            if self.joinedLines:
                points = []
                for colNo in range(len(row)):
                    points += row[colNo]
                if inFill:
                    points = points + [inFillX1,inFillY,inFillX0,inFillY]
                    inFillG.add(Polygon(points,fillColor=rowColor,strokeColor=rowColor,strokeWidth=0.1))
                else:
                    line = PolyLine(points,strokeColor=rowColor,strokeLineCap=0,strokeLineJoin=1)
                    if strokeWidth:
                        line.strokeWidth = strokeWidth
                    if dash:
                        line.strokeDashArray = dash
                    g.add(line)

            if hasattr(self.lines[styleIdx], 'symbol'):
                uSymbol = self.lines[styleIdx].symbol
            elif hasattr(self.lines, 'symbol'):
                uSymbol = self.lines.symbol
            else:
                uSymbol = None

            if uSymbol:
                for colNo in range(len(row)):
                    x1, y1 = row[colNo]
                    symbol = uSymbol2Symbol(uSymbol,x1,y1,rowStyle.strokeColor)
                    if symbol: g.add(symbol)

            # Draw item labels.
            for colNo in range(len(row)):
                x1, y1 = row[colNo]
                self.drawLabel(g, rowNo, colNo, x1, y1)

        return g

    def draw(self):
        "Draws itself."

        vA, cA = self.valueAxis, self.categoryAxis
        vA.setPosition(self.x, self.y, self.height)
        if vA: vA.joinAxis = cA
        if cA: cA.joinAxis = vA
        vA.configure(self.data)

        # If zero is in chart, put x axis there, otherwise
        # use bottom.
        xAxisCrossesAt = vA.scale(0)
        if ((xAxisCrossesAt > self.y + self.height) or (xAxisCrossesAt < self.y)):
            y = self.y
        else:
            y = xAxisCrossesAt

        cA.setPosition(self.x, y, self.width)
        cA.configure(self.data)

        self.calcPositions()

        g = Group()
        g.add(self.makeBackground())
        if self.inFill:
            self._inFillG = Group()
            g.add(self._inFillG)

        g.add(cA)
        g.add(vA)
        cAdgl = getattr(cA,'drawGridLast',False)
        vAdgl = getattr(vA,'drawGridLast',False)
        if not cAdgl: cA.makeGrid(g,parent=self,dim=vA.getGridDims)
        if not vAdgl: vA.makeGrid(g,parent=self,dim=cA.getGridDims)
        g.add(self.makeLines())
        if cAdgl: cA.makeGrid(g,parent=self,dim=vA.getGridDims)
        if vAdgl: vA.makeGrid(g,parent=self,dim=cA.getGridDims)
        for a in getattr(self,'annotations',()): g.add(a(self,cA.scale,vA.scale))
        return g
Ejemplo n.º 21
0
 def __init__(self):
     LinePlot.__init__(self)
     self.xValueAxis = NormalDateXValueAxis()
     self.yValueAxis = YValueAxis()
     self.data = _monthlyIndexData
Ejemplo n.º 22
0
    def __init__(self, desc=None):
        YValueAxis.__init__(self)

        self.desc = None
        if isString(desc) is True:
            self.desc = desc
Ejemplo n.º 23
0
class LinePlot(PlotArea):
    """Line plot with multiple lines.

    Both x- and y-axis are value axis (so there are no seperate
    X and Y versions of this class).
    """

    _attrMap = AttrMap(
        BASE=PlotArea,
        reversePlotOrder=AttrMapValue(isBoolean, desc="If true reverse plot order."),
        lineLabelNudge=AttrMapValue(isNumber, desc="Distance between a data point and its label."),
        lineLabels=AttrMapValue(None, desc="Handle to the list of data point labels."),
        lineLabelFormat=AttrMapValue(None, desc="Formatting string or function used for data point labels."),
        lineLabelArray=AttrMapValue(
            None, desc="explicit array of line label values, must match size of data if present."
        ),
        joinedLines=AttrMapValue(isNumber, desc="Display data points joined with lines if true."),
        strokeColor=AttrMapValue(isColorOrNone, desc="Color used for background border of plot area."),
        fillColor=AttrMapValue(isColorOrNone, desc="Color used for background interior of plot area."),
        lines=AttrMapValue(None, desc="Handle of the lines."),
        xValueAxis=AttrMapValue(None, desc="Handle of the x axis."),
        yValueAxis=AttrMapValue(None, desc="Handle of the y axis."),
        data=AttrMapValue(None, desc="Data to be plotted, list of (lists of) x/y tuples."),
        annotations=AttrMapValue(None, desc="list of callables, will be called with self, xscale, yscale."),
    )

    def __init__(self):
        PlotArea.__init__(self)
        self.reversePlotOrder = 0

        self.xValueAxis = XValueAxis()
        self.yValueAxis = YValueAxis()

        # this defines two series of 3 points.  Just an example.
        self.data = [((1, 1), (2, 2), (2.5, 1), (3, 3), (4, 5)), ((1, 2), (2, 3), (2.5, 2), (3, 4), (4, 6))]

        self.lines = TypedPropertyCollection(LinePlotProperties)
        self.lines.strokeWidth = 1
        self.lines[0].strokeColor = colors.red
        self.lines[1].strokeColor = colors.blue

        self.lineLabels = TypedPropertyCollection(Label)
        self.lineLabelFormat = None
        self.lineLabelArray = None

        # this says whether the origin is inside or outside
        # the bar - +10 means put the origin ten points
        # above the tip of the bar if value > 0, or ten
        # points inside if bar value < 0.  This is different
        # to label dx/dy which are not dependent on the
        # sign of the data.
        self.lineLabelNudge = 10
        # if you have multiple series, by default they butt
        # together.

        # New line chart attributes.
        self.joinedLines = 1  # Connect items with straight lines.

        # private attributes
        self._inFill = None

    def demo(self):
        """Shows basic use of a line chart."""

        drawing = Drawing(400, 200)

        data = [((1, 1), (2, 2), (2.5, 1), (3, 3), (4, 5)), ((1, 2), (2, 3), (2.5, 2), (3.5, 5), (4, 6))]

        lp = LinePlot()

        lp.x = 50
        lp.y = 50
        lp.height = 125
        lp.width = 300
        lp.data = data
        lp.joinedLines = 1
        lp.lineLabelFormat = "%2.0f"
        lp.strokeColor = colors.black

        lp.lines[0].strokeColor = colors.red
        lp.lines[0].symbol = makeMarker("FilledCircle")
        lp.lines[1].strokeColor = colors.blue
        lp.lines[1].symbol = makeMarker("FilledDiamond")

        lp.xValueAxis.valueMin = 0
        lp.xValueAxis.valueMax = 5
        lp.xValueAxis.valueStep = 1

        lp.yValueAxis.valueMin = 0
        lp.yValueAxis.valueMax = 7
        lp.yValueAxis.valueStep = 1

        drawing.add(lp)

        return drawing

    def calcPositions(self):
        """Works out where they go.

        Sets an attribute _positions which is a list of
        lists of (x, y) matching the data.
        """

        self._seriesCount = len(self.data)
        self._rowLength = max(map(len, self.data))

        self._positions = []
        for rowNo in range(len(self.data)):
            line = []
            for colNo in range(len(self.data[rowNo])):
                datum = self.data[rowNo][colNo]  # x,y value
                if type(datum[0]) == type(""):
                    x = self.xValueAxis.scale(mktime(mkTimeTuple(datum[0])))
                else:
                    x = self.xValueAxis.scale(datum[0])
                y = self.yValueAxis.scale(datum[1])
                line.append((x, y))
            self._positions.append(line)

    def _innerDrawLabel(self, rowNo, colNo, x, y):
        "Draw a label for a given item in the list."

        labelFmt = self.lineLabelFormat
        labelValue = self.data[rowNo][colNo][1]  ###

        if labelFmt is None:
            labelText = None
        elif type(labelFmt) is StringType:
            if labelFmt == "values":
                labelText = self.lineLabelArray[rowNo][colNo]
            else:
                labelText = labelFmt % labelValue
        elif type(labelFmt) is FunctionType:
            labelText = labelFmt(labelValue)
        elif isinstance(labelFmt, Formatter):
            labelText = labelFmt(labelValue)
        else:
            msg = "Unknown formatter type %s, expected string or function"
            raise Exception, msg % labelFmt

        if labelText:
            label = self.lineLabels[(rowNo, colNo)]
            # hack to make sure labels are outside the bar
            if y > 0:
                label.setOrigin(x, y + self.lineLabelNudge)
            else:
                label.setOrigin(x, y - self.lineLabelNudge)
            label.setText(labelText)
        else:
            label = None
        return label

    def drawLabel(self, G, rowNo, colNo, x, y):
        """Draw a label for a given item in the list.
        G must have an add method"""
        G.add(self._innerDrawLabel(rowNo, colNo, x, y))

    def makeLines(self):
        g = Group()
        bubblePlot = getattr(self, "_bubblePlot", None)
        if bubblePlot:
            yA = self.yValueAxis
            xA = self.xValueAxis
            bubbleR = min(yA._bubbleRadius, xA._bubbleRadius)
            bubbleMax = xA._bubbleMax

        labelFmt = self.lineLabelFormat

        P = range(len(self._positions))
        if self.reversePlotOrder:
            P.reverse()
        inFill = getattr(self, "_inFill", None)
        if inFill:
            inFillY = self.xValueAxis._y
            inFillX0 = self.yValueAxis._x
            inFillX1 = inFillX0 + self.xValueAxis._length
            inFillG = getattr(self, "_inFillG", g)
        # Iterate over data rows.
        styleCount = len(self.lines)
        for rowNo in P:
            row = self._positions[rowNo]
            rowStyle = self.lines[rowNo % styleCount]
            rowColor = rowStyle.strokeColor
            dash = getattr(rowStyle, "strokeDashArray", None)

            if hasattr(rowStyle, "strokeWidth"):
                width = rowStyle.strokeWidth
            elif hasattr(self.lines, "strokeWidth"):
                width = self.lines.strokeWidth
            else:
                width = None

            # Iterate over data columns.
            if self.joinedLines:
                points = []
                for xy in row:
                    points = points + [xy[0], xy[1]]
                if inFill:
                    points = points + [inFillX1, inFillY, inFillX0, inFillY]
                    filler = getattr(rowStyle, "filler", None)
                    if filler:
                        filler.fill(self, inFillG, rowNo, rowColor, points)
                    else:
                        inFillG.add(Polygon(points, fillColor=rowColor, strokeColor=rowColor, strokeWidth=0.1))
                else:
                    line = PolyLine(points, strokeColor=rowColor, strokeLineCap=0, strokeLineJoin=1)
                    if width:
                        line.strokeWidth = width
                    if dash:
                        line.strokeDashArray = dash
                    g.add(line)

            if hasattr(rowStyle, "symbol"):
                uSymbol = rowStyle.symbol
            elif hasattr(self.lines, "symbol"):
                uSymbol = self.lines.symbol
            else:
                uSymbol = None

            if uSymbol:
                j = -1
                if bubblePlot:
                    drow = self.data[rowNo]
                for xy in row:
                    j += 1
                    symbol = uSymbol2Symbol(uSymbol, xy[0], xy[1], rowColor)
                    if symbol:
                        if bubblePlot:
                            symbol.size = bubbleR * (drow[j][2] / bubbleMax) ** 0.5
                        g.add(symbol)

            # Draw data labels.
            for colNo in range(len(row)):
                x1, y1 = row[colNo]
                self.drawLabel(g, rowNo, colNo, x1, y1)

            shader = getattr(rowStyle, "shader", None)
            if shader:
                shader.shade(self, g, rowNo, rowColor, row)

        return g

    def makeSwatchSample(self, rowNo, x, y, width, height):
        styleCount = len(self.lines)
        styleIdx = rowNo % styleCount
        rowColor = self.lines[styleIdx].strokeColor

        if self.joinedLines:
            dash = getattr(self.lines[styleIdx], "strokeDashArray", getattr(self.lines, "strokeDashArray", None))
            strokeWidth = getattr(
                self.lines[styleIdx], "strokeWidth", getattr(self.lines[styleIdx], "strokeWidth", None)
            )
            L = Line(x, y, x + width, y + height, strokeColor=rowColor, strokeLineCap=0)
            if strokeWidth:
                L.strokeWidth = strokeWidth
            if dash:
                L.strokeDashArray = dash
        else:
            L = None

        if hasattr(self.lines[styleIdx], "symbol"):
            S = self.lines[styleIdx].symbol
        elif hasattr(self.lines, "symbol"):
            S = self.lines.symbol
        else:
            S = None

        if S:
            S = uSymbol2Symbol(S, x + width / 2.0, y + height / 2.0, rowColor)
        if S and L:
            g = Group()
            g.add(S)
            g.add(L)
            return g
        return S or L

    def draw(self):
        yA = self.yValueAxis
        xA = self.xValueAxis
        if getattr(self, "_bubblePlot", None):
            yA._bubblePlot = xA._bubblePlot = 1
        yA.setPosition(self.x, self.y, self.height)
        if yA:
            yA.joinAxis = xA
        if xA:
            xA.joinAxis = yA
        yA.configure(self.data)

        # if zero is in chart, put x axis there, otherwise use bottom.
        xAxisCrossesAt = yA.scale(0)
        if (xAxisCrossesAt > self.y + self.height) or (xAxisCrossesAt < self.y):
            y = self.y
        else:
            y = xAxisCrossesAt

        xA.setPosition(self.x, y, self.width)
        xA.configure(self.data)
        self.calcPositions()
        g = Group()
        g.add(self.makeBackground())
        if self._inFill:
            self._inFillG = Group()
            g.add(self._inFillG)
        g.add(xA)
        g.add(yA)
        yA.gridStart = xA._x
        yA.gridEnd = xA._x + xA._length
        xA.gridStart = yA._y
        xA.gridEnd = yA._y + yA._length
        xA.makeGrid(g, parent=self)
        yA.makeGrid(g, parent=self)
        g.add(self.makeLines())
        for a in getattr(self, "annotations", ()):
            g.add(a(self, xA.scale, yA.scale))
        return g
Ejemplo n.º 24
0
def getPlot():
    drawing = Drawing(400, 200)
    #temps = [((0.5,7), (1.5,1), (2.5,2), (3.5,1), (4.5,3), (5.5,5), (6.5, 10), (7.5,6))]
    temps = [getTemps()]
    bc = LinePlot()
    bc.x = 50
    bc.y = 50
    bc.height = 125
    bc.width = 300
    bc.data = temps
    #labels
    yilabel = Label()
    yilabel.setText("Temperatura (°C)")
    yilabel.angle = 90
    yilabel.setOrigin(20,120)
    xlabel = Label()
    xlabel.setText("Días")
    xlabel.setOrigin(200,20)

    labelT = Label()
    labelT.setText("Temperatura")
    labelT.setOrigin(210,185)

    labelH = Label()
    labelH.setText("Humedad")
    labelH.setOrigin(285,185)


    bc.xValueAxis.valueMin = 0
    bc.xValueAxis.valueMax = 20
    bc.xValueAxis.valueSteps = [x for x in range(1,bc.xValueAxis.valueMax)]
    #bc.xValueAxis.labelTextFormat = '%2.1f'
    bc.yValueAxis.valueMin = 0
    bc.yValueAxis.valueMax = 60
    bc.yValueAxis.valueSteps = [0, 10, 20, 30, 40, 50, 60]
    drawing.add(bc)
    drawing.add(yilabel)
    drawing.add(xlabel)
    drawing.add(Line(170,185,185,185, strokeColor=colors.red))
    drawing.add(Line(250,185,265,185, strokeColor=colors.blue))
    drawing.add(labelT)
    drawing.add(labelH)

    #humedad=[[(0.5, 4), (1.5, 3), (2.5, 4), (3.5, 6), (4.5, 4), (5.5, 2), (6.5, 5), (7.5, 6)]]
    humedad = [getHumedad()]
    lp = LinePlot()
    lp.x = bc.x
    lp.y = bc.y
    lp.height = bc.height
    lp.width = bc.width
    lp.data = humedad

    ydlabel = Label()
    ydlabel.setText("Humedad (%)")
    ydlabel.angle = -90
    ydlabel.setOrigin(lp.x+lp.width+30,lp.y+70)

    lp.joinedLines = 1
    lp.lines[0].symbol = makeMarker('Circle')
    lp.lines[0].strokeColor=colors.blue
    lp.lineLabelFormat = '%2.0f'
    lp.xValueAxis.valueMin = 0
    lp.xValueAxis.valueMax = bc.xValueAxis.valueMax
    lp.yValueAxis.valueMin = 0
    lp.yValueAxis.valueMax = 100
    lp.xValueAxis.visible=False
    lp.yValueAxis.visible=False #Hide 2nd plot its Yaxis
    drawing.add(lp)
    drawing.add(ydlabel)

    y2Axis = YValueAxis()#Replicate 2nd plot Yaxis in the right
    y2Axis.setProperties(lp.yValueAxis.getProperties())
    y2Axis.setPosition(lp.x+lp.width,lp.y,lp.height)
    y2Axis.tickRight=5
    y2Axis.tickLeft=0
    y2Axis.labels.dx = 20
    y2Axis.configure(humedad)
    y2Axis.visible=True
    drawing.add(y2Axis)

    return drawing
Ejemplo n.º 25
0
from reportlab.graphics.charts.axes import XCategoryAxis,YValueAxis

drawing = Drawing(400, 200)

data = [(10, 20, 30, 40), (15, 22, 37, 42)]

xAxis = XCategoryAxis()
xAxis.setPosition(75, 75, 300)
xAxis.configure(data)
xAxis.categoryNames = ['Beer', 'Wine', 'Meat', 'Cannelloni']
xAxis.labels.boxAnchor = 'n'
xAxis.labels[3].dy = -15
xAxis.labels[3].angle = 30
xAxis.labels[3].fontName = 'Times-Bold'

yAxis = YValueAxis()
yAxis.setPosition(50, 50, 125)
yAxis.configure(data)

drawing.add(xAxis)
drawing.add(yAxis)

draw(drawing, 'Two isolated axes')


disc("Here is the code that created them: ")

eg("""
from reportlab.graphics import shapes
from reportlab.graphics.charts.axes import XCategoryAxis,YValueAxis
Ejemplo n.º 26
0
def run(self):
    def weight_sort(a, b):
        return cmp(a.getWeight(), b.getWeight())
        
    drawing = Drawing(600, 300)
    lc = LinePlot()
     
    # Determine axis dimensions and create data set
    maxval = 0
    minval = 0    
    dimension_one_values = []
    dimension_two_values = []
    dimension_one_answeroptions_as_objects = []
    dimension_two_answeroptions_as_objects = []
    counter = 0
    for question in self.getQuestions():        
        weights = [int(weight) for weight in question.getAnswerOptionsWeights()]
        answeroptions = list(question.getAnswerOptions())

        # This is used by the legend. Sort on weight.
        if counter == 0:
            dimension_one_answeroptions_as_objects = question.getAnswerOptionsAsObjects()
            dimension_one_answeroptions_as_objects.sort(weight_sort)
        else:
            dimension_two_answeroptions_as_objects = question.getAnswerOptionsAsObjects()
            dimension_two_answeroptions_as_objects.sort(weight_sort)

        # Minmax
        lmin = min(weights)
        lmax = max(weights)
        if lmin < minval:
            minval = lmin
        if lmax > maxval:
            maxval = lmax
        
        # Data
        for user, answer in question.answers.items():
            value = answer.get('value', None)            
            weight = None
            if value is not None:                
                # Lookup the integer weight of this answer
                if value in answeroptions:
                    index = answeroptions.index(value)
                    weight = weights[index]
            # Always add to the list. ReportLab deals with None.    
            if counter == 0:
                dimension_one_values.append(weight)
            else:
                dimension_two_values.append(weight)
                
        counter += 1

    # Set minmax
    absmax = max(abs(minval), abs(maxval)) * 1.1    
    lc.xValueAxis.valueMin = -absmax
    lc.xValueAxis.valueMax = absmax    
    lc.yValueAxis.valueMin = -absmax
    lc.yValueAxis.valueMax = absmax
       
    # Zip to create data
    data = [zip(dimension_one_values, dimension_two_values)]
    if not len(data[0]):
        return
    
    lc.x = 0
    lc.y = 0
    # Misc setup
    lc.height = 300
    lc.width = 300
    lc.data = data
    lc.joinedLines = 0
    lc.fillColor = None
    lc.lines[0].strokeColor = colors.red
    lc.lines[0].symbol = makeMarker('FilledCircle')

    # Add a grid
    grid = DoubleGrid()
    lc.background = grid
    setupGrid(lc)    
    lc.background = None
    # Finetune the grid
    grid.grid0.strokeWidth = 0.2
    grid.grid1.strokeWidth = 0.2
    # Add to drawing else it overwrites the center Y axis
    drawing.add(grid)
   
    # Add a Y axis to pass through the origin
    yaxis = YValueAxis()
    yaxis.setPosition(lc.width/2, 0, lc.height)
    yaxis.configure([(0,-absmax),(0,absmax)])
    yaxis.strokeColor = colors.blue
    drawing.add(yaxis)

    # Color X-Axis
    lc.xValueAxis.strokeColor = colors.green

    drawing.add(lc)

    # Legend for Dimension One
    drawing.add(String(lc.width+20, lc.height-12, 'Dimension One (X-Axis):', 
        fontName='Helvetica', fontSize=12, fillColor=colors.green))
    legend = Legend()
    legend.alignment = 'right'
    legend.x = lc.width + 20
    legend.y = lc.height - 20
    legend.fontName = 'Helvetica'
    legend.fontSize = 12
    legend.columnMaximum = 7
    items = []
    for ob in dimension_one_answeroptions_as_objects:
        items.append( ( StringWidget(ob.getWeight()), ob() ) )
    legend.colorNamePairs = items
    drawing.add(legend, 'legend1')

    # Legend for Dimension Two
    drawing.add(String(lc.width+20, lc.height/2-12, 'Dimension Two (Y-Axis):', 
        fontName='Helvetica', fontSize=12, fillColor=colors.blue))
    legend = Legend()
    legend.alignment = 'right'
    legend.x = lc.width + 20
    legend.y = lc.height/2 - 20
    legend.fontName = 'Helvetica'
    legend.fontSize = 12
    legend.columnMaximum = 7
    items = []
    for ob in dimension_two_answeroptions_as_objects:
        items.append( ( StringWidget(ob.getWeight()), ob() ) )
    legend.colorNamePairs = items
    drawing.add(legend, 'legend2')

    # Write out
    data = drawing.asString('png')
    request = self.REQUEST
    response = request.RESPONSE
    response.setHeader('Content-Type', 'image/png')
    response.setHeader('Content-Disposition','inline; filename=%s.png' % self.getId())
    response.setHeader('Content-Length', len(data))           
    response.setHeader('Cache-Control', 's-maxage=0')
    
    return data
Ejemplo n.º 27
0
class LinePlot(AbstractLineChart):
    """Line plot with multiple lines.

    Both x- and y-axis are value axis (so there are no seperate
    X and Y versions of this class).
    """
    _attrMap = AttrMap(BASE=PlotArea,
        reversePlotOrder = AttrMapValue(isBoolean, desc='If true reverse plot order.',advancedUsage=1),
        lineLabelNudge = AttrMapValue(isNumber, desc='Distance between a data point and its label.',advancedUsage=1),
        lineLabels = AttrMapValue(None, desc='Handle to the list of data point labels.'),
        lineLabelFormat = AttrMapValue(None, desc='Formatting string or function used for data point labels.'),
        lineLabelArray = AttrMapValue(None, desc='explicit array of line label values, must match size of data if present.'),
        joinedLines = AttrMapValue(isNumber, desc='Display data points joined with lines if true.'),
        strokeColor = AttrMapValue(isColorOrNone, desc='Color used for background border of plot area.'),
        fillColor = AttrMapValue(isColorOrNone, desc='Color used for background interior of plot area.'),
        lines = AttrMapValue(None, desc='Handle of the lines.'),
        xValueAxis = AttrMapValue(None, desc='Handle of the x axis.'),
        yValueAxis = AttrMapValue(None, desc='Handle of the y axis.'),
        data = AttrMapValue(None, desc='Data to be plotted, list of (lists of) x/y tuples.'),
        annotations = AttrMapValue(None, desc='list of callables, will be called with self, xscale, yscale.',advancedUsage=1),
        behindAxes = AttrMapValue(isBoolean, desc='If true use separate line group.',advancedUsage=1),
        gridFirst = AttrMapValue(isBoolean, desc='If true use draw grids before axes.',advancedUsage=1),
        )

    def __init__(self):
        PlotArea.__init__(self)
        self.reversePlotOrder = 0

        self.xValueAxis = XValueAxis()
        self.yValueAxis = YValueAxis()

        # this defines two series of 3 points.  Just an example.
        self.data = [
            ((1,1), (2,2), (2.5,1), (3,3), (4,5)),
            ((1,2), (2,3), (2.5,2), (3,4), (4,6))
            ]

        self.lines = TypedPropertyCollection(LinePlotProperties)
        self.lines.strokeWidth = 1
        self.lines[0].strokeColor = colors.red
        self.lines[1].strokeColor = colors.blue

        self.lineLabels = TypedPropertyCollection(Label)
        self.lineLabelFormat = None
        self.lineLabelArray = None

        # this says whether the origin is inside or outside
        # the bar - +10 means put the origin ten points
        # above the tip of the bar if value > 0, or ten
        # points inside if bar value < 0.  This is different
        # to label dx/dy which are not dependent on the
        # sign of the data.
        self.lineLabelNudge = 10
        # if you have multiple series, by default they butt
        # together.

        # New line chart attributes.
        self.joinedLines = 1 # Connect items with straight lines.

        #private attributes
        self._inFill = None
        self.annotations = []
        self.behindAxes = 0
        self.gridFirst = 0

    def demo(self):
        """Shows basic use of a line chart."""

        drawing = Drawing(400, 200)

        data = [
            ((1,1), (2,2), (2.5,1), (3,3), (4,5)),
            ((1,2), (2,3), (2.5,2), (3.5,5), (4,6))
            ]

        lp = LinePlot()

        lp.x = 50
        lp.y = 50
        lp.height = 125
        lp.width = 300
        lp.data = data
        lp.joinedLines = 1
        lp.lineLabelFormat = '%2.0f'
        lp.strokeColor = colors.black

        lp.lines[0].strokeColor = colors.red
        lp.lines[0].symbol = makeMarker('FilledCircle')
        lp.lines[1].strokeColor = colors.blue
        lp.lines[1].symbol = makeMarker('FilledDiamond')

        lp.xValueAxis.valueMin = 0
        lp.xValueAxis.valueMax = 5
        lp.xValueAxis.valueStep = 1

        lp.yValueAxis.valueMin = 0
        lp.yValueAxis.valueMax = 7
        lp.yValueAxis.valueStep = 1

        drawing.add(lp)

        return drawing


    def calcPositions(self):
        """Works out where they go.

        Sets an attribute _positions which is a list of
        lists of (x, y) matching the data.
        """

        self._seriesCount = len(self.data)
        self._rowLength = max(map(len,self.data))

        self._positions = []
        for rowNo in range(len(self.data)):
            line = []
            for colNo in range(len(self.data[rowNo])):
                datum = self.data[rowNo][colNo] # x,y value
                if type(datum[0]) == type(''):
                    x = self.xValueAxis.scale(mktime(mkTimeTuple(datum[0])))
                else:
                    x = self.xValueAxis.scale(datum[0])
                y = self.yValueAxis.scale(datum[1])
                line.append((x, y))
            self._positions.append(line)

    def _innerDrawLabel(self, rowNo, colNo, x, y):
        "Draw a label for a given item in the list."

        labelFmt = self.lineLabelFormat
        labelValue = self.data[rowNo][colNo][1] ###

        if labelFmt is None:
            labelText = None
        elif type(labelFmt) is StringType:
            if labelFmt == 'values':
                labelText = self.lineLabelArray[rowNo][colNo]
            else:
                labelText = labelFmt % labelValue
        elif hasattr(labelFmt,'__call__'):
            labelText = labelFmt(labelValue)
        else:
            raise ValueError("Unknown formatter type %s, expected string or function"%labelFmt)

        if labelText:
            label = self.lineLabels[(rowNo, colNo)]
            if not label.visible: return
            #hack to make sure labels are outside the bar
            if y > 0:
                label.setOrigin(x, y + self.lineLabelNudge)
            else:
                label.setOrigin(x, y - self.lineLabelNudge)
            label.setText(labelText)
        else:
            label = None
        return label

    def drawLabel(self, G, rowNo, colNo, x, y):
        '''Draw a label for a given item in the list.
        G must have an add method'''
        G.add(self._innerDrawLabel(rowNo,colNo,x,y))

    def makeLines(self):
        g = Group()
        bubblePlot = getattr(self,'_bubblePlot',None)
        if bubblePlot:
            yA = self.yValueAxis
            xA = self.xValueAxis
            bubbleR = min(yA._bubbleRadius,xA._bubbleRadius)
            bubbleMax = xA._bubbleMax

        labelFmt = self.lineLabelFormat

        P = range(len(self._positions))
        if self.reversePlotOrder: P.reverse()
        inFill = getattr(self,'_inFill',None)
        styleCount = len(self.lines)
        if inFill or [rowNo for rowNo in P if getattr(self.lines[rowNo%styleCount],'inFill',False)]:
            inFillY = self.xValueAxis._y
            inFillX0 = self.yValueAxis._x
            inFillX1 = inFillX0 + self.xValueAxis._length
            inFillG = getattr(self,'_inFillG',g)
        lG = getattr(self,'_lineG',g)
        # Iterate over data rows.
        for rowNo in P:
            row = self._positions[rowNo]
            rowStyle = self.lines[rowNo % styleCount]
            rowColor = getattr(rowStyle,'strokeColor',None)
            dash = getattr(rowStyle, 'strokeDashArray', None)

            if hasattr(rowStyle, 'strokeWidth'):
                width = rowStyle.strokeWidth
            elif hasattr(self.lines, 'strokeWidth'):
                width = self.lines.strokeWidth
            else:
                width = None

            # Iterate over data columns.
            if self.joinedLines:
                points = []
                for xy in row:
                    points = points + [xy[0], xy[1]]
                if inFill or getattr(rowStyle,'inFill',False):
                    fpoints = [inFillX0,inFillY] + points + [inFillX1,inFillY]
                    filler = getattr(rowStyle, 'filler', None)
                    if filler:
                        filler.fill(self,inFillG,rowNo,rowColor,fpoints)
                    else:
                        inFillG.add(Polygon(fpoints,fillColor=rowColor,strokeColor=rowColor,strokeWidth=width or 0.1))
                if inFill in (None,0,2):
                    line = PolyLine(points,strokeColor=rowColor,strokeLineCap=0,strokeLineJoin=1)
                    if width:
                        line.strokeWidth = width
                    if dash:
                        line.strokeDashArray = dash
                    lG.add(line)

            if hasattr(rowStyle, 'symbol'):
                uSymbol = rowStyle.symbol
            elif hasattr(self.lines, 'symbol'):
                uSymbol = self.lines.symbol
            else:
                uSymbol = None

            if uSymbol:
                j = -1
                if bubblePlot: drow = self.data[rowNo]
                for xy in row:
                    j += 1
                    symbol = uSymbol2Symbol(uSymbol,xy[0],xy[1],rowColor)
                    if symbol:
                        if bubblePlot:
                            symbol.size = bubbleR*(drow[j][2]/bubbleMax)**0.5
                        g.add(symbol)

            # Draw data labels.
            for colNo in range(len(row)):
                x1, y1 = row[colNo]
                self.drawLabel(g, rowNo, colNo, x1, y1)

            shader = getattr(rowStyle, 'shader', None)
            if shader: shader.shade(self,g,rowNo,rowColor,row)

        return g

    def draw(self):
        yA = self.yValueAxis
        xA = self.xValueAxis
        if getattr(self,'_bubblePlot',None):
            yA._bubblePlot = xA._bubblePlot = 1
        yA.setPosition(self.x, self.y, self.height)
        if yA: yA.joinAxis = xA
        if xA: xA.joinAxis = yA
        yA.configure(self.data)

        # if zero is in chart, put x axis there, otherwise use bottom.
        xAxisCrossesAt = yA.scale(0)
        if ((xAxisCrossesAt > self.y + self.height) or (xAxisCrossesAt < self.y)):
            y = self.y
        else:
            y = xAxisCrossesAt

        xA.setPosition(self.x, y, self.width)
        xA.configure(self.data)
        self.calcPositions()
        g = Group()
        g.add(self.makeBackground())
        if self._inFill or self.behindAxes:
            xA._joinToAxis()
            if self._inFill:
                self._inFillG = Group()
                g.add(self._inFillG)
            if self.behindAxes:
                self._lineG = Group()
                g.add(self._lineG)
        if self.gridFirst:
            xA.makeGrid(g,parent=self,dim=yA.getGridDims)
            yA.makeGrid(g,parent=self,dim=xA.getGridDims)
        g.add(xA)
        g.add(yA)
        if not self.gridFirst:
            xAdgl = getattr(xA,'drawGridLast',False)
            yAdgl = getattr(yA,'drawGridLast',False)
            if not xAdgl: xA.makeGrid(g,parent=self,dim=yA.getGridDims)
            if not yAdgl: yA.makeGrid(g,parent=self,dim=xA.getGridDims)
        annotations = getattr(self,'annotations',[])
        for a in annotations:
            if getattr(a,'beforeLines',None):
                g.add(a(self,xA.scale,yA.scale))
        g.add(self.makeLines())
        if not self.gridFirst:
            if xAdgl: xA.makeGrid(g,parent=self,dim=yA.getGridDims)
            if yAdgl: yA.makeGrid(g,parent=self,dim=xA.getGridDims)
        for a in annotations:
            if not getattr(a,'beforeLines',None):
                g.add(a(self,xA.scale,yA.scale))
        return g

    def addCrossHair(self,name,xv,yv,strokeColor=colors.black,strokeWidth=1,beforeLines=True):
        from reportlab.graphics.shapes import Group, Line
        annotations = [a for a in getattr(self,'annotations',[]) if getattr(a,'name',None)!=name]
        def annotation(self,xScale,yScale):
            x = xScale(xv)
            y = yScale(yv)
            g = Group()
            xA = xScale.im_self #the x axis
            g.add(Line(xA._x,y,xA._x+xA._length,y,strokeColor=strokeColor,strokeWidth=strokeWidth))
            yA = yScale.im_self #the y axis
            g.add(Line(x,yA._y,x,yA._y+yA._length,strokeColor=strokeColor,strokeWidth=strokeWidth))
            return g
        annotation.beforeLines = beforeLines
        annotations.append(annotation)
        self.annotations = annotations
Ejemplo n.º 28
0
class LinePlot(AbstractLineChart):
    """Line plot with multiple lines.

    Both x- and y-axis are value axis (so there are no seperate
    X and Y versions of this class).
    """
    _attrMap = AttrMap(
        BASE=PlotArea,
        reversePlotOrder=AttrMapValue(isBoolean,
                                      desc='If true reverse plot order.',
                                      advancedUsage=1),
        lineLabelNudge=AttrMapValue(
            isNumber,
            desc='Distance between a data point and its label.',
            advancedUsage=1),
        lineLabels=AttrMapValue(
            None, desc='Handle to the list of data point labels.'),
        lineLabelFormat=AttrMapValue(
            None,
            desc='Formatting string or function used for data point labels.'),
        lineLabelArray=AttrMapValue(
            None,
            desc=
            'explicit array of line label values, must match size of data if present.'
        ),
        joinedLines=AttrMapValue(
            isNumber, desc='Display data points joined with lines if true.'),
        strokeColor=AttrMapValue(
            isColorOrNone,
            desc='Color used for background border of plot area.'),
        fillColor=AttrMapValue(
            isColorOrNone,
            desc='Color used for background interior of plot area.'),
        lines=AttrMapValue(None, desc='Handle of the lines.'),
        xValueAxis=AttrMapValue(None, desc='Handle of the x axis.'),
        yValueAxis=AttrMapValue(None, desc='Handle of the y axis.'),
        data=AttrMapValue(
            None, desc='Data to be plotted, list of (lists of) x/y tuples.'),
        annotations=AttrMapValue(
            None,
            desc='list of callables, will be called with self, xscale, yscale.',
            advancedUsage=1),
        behindAxes=AttrMapValue(isBoolean,
                                desc='If true use separate line group.',
                                advancedUsage=1),
        gridFirst=AttrMapValue(isBoolean,
                               desc='If true use draw grids before axes.',
                               advancedUsage=1),
    )

    def __init__(self):
        PlotArea.__init__(self)
        self.reversePlotOrder = 0

        self.xValueAxis = XValueAxis()
        self.yValueAxis = YValueAxis()

        # this defines two series of 3 points.  Just an example.
        self.data = [((1, 1), (2, 2), (2.5, 1), (3, 3), (4, 5)),
                     ((1, 2), (2, 3), (2.5, 2), (3, 4), (4, 6))]

        self.lines = TypedPropertyCollection(LinePlotProperties)
        self.lines.strokeWidth = 1
        self.lines[0].strokeColor = colors.red
        self.lines[1].strokeColor = colors.blue

        self.lineLabels = TypedPropertyCollection(Label)
        self.lineLabelFormat = None
        self.lineLabelArray = None

        # this says whether the origin is inside or outside
        # the bar - +10 means put the origin ten points
        # above the tip of the bar if value > 0, or ten
        # points inside if bar value < 0.  This is different
        # to label dx/dy which are not dependent on the
        # sign of the data.
        self.lineLabelNudge = 10
        # if you have multiple series, by default they butt
        # together.

        # New line chart attributes.
        self.joinedLines = 1  # Connect items with straight lines.

        #private attributes
        self._inFill = None
        self.annotations = []
        self.behindAxes = 0
        self.gridFirst = 0

    def demo(self):
        """Shows basic use of a line chart."""

        drawing = Drawing(400, 200)

        data = [((1, 1), (2, 2), (2.5, 1), (3, 3), (4, 5)),
                ((1, 2), (2, 3), (2.5, 2), (3.5, 5), (4, 6))]

        lp = LinePlot()

        lp.x = 50
        lp.y = 50
        lp.height = 125
        lp.width = 300
        lp.data = data
        lp.joinedLines = 1
        lp.lineLabelFormat = '%2.0f'
        lp.strokeColor = colors.black

        lp.lines[0].strokeColor = colors.red
        lp.lines[0].symbol = makeMarker('FilledCircle')
        lp.lines[1].strokeColor = colors.blue
        lp.lines[1].symbol = makeMarker('FilledDiamond')

        lp.xValueAxis.valueMin = 0
        lp.xValueAxis.valueMax = 5
        lp.xValueAxis.valueStep = 1

        lp.yValueAxis.valueMin = 0
        lp.yValueAxis.valueMax = 7
        lp.yValueAxis.valueStep = 1

        drawing.add(lp)

        return drawing

    def calcPositions(self):
        """Works out where they go.

        Sets an attribute _positions which is a list of
        lists of (x, y) matching the data.
        """

        self._seriesCount = len(self.data)
        self._rowLength = max(map(len, self.data))

        self._positions = []
        for rowNo in range(len(self.data)):
            line = []
            for colNo in range(len(self.data[rowNo])):
                datum = self.data[rowNo][colNo]  # x,y value
                if type(datum[0]) == type(''):
                    x = self.xValueAxis.scale(mktime(mkTimeTuple(datum[0])))
                else:
                    x = self.xValueAxis.scale(datum[0])
                y = self.yValueAxis.scale(datum[1])
                line.append((x, y))
            self._positions.append(line)

    def _innerDrawLabel(self, rowNo, colNo, x, y):
        "Draw a label for a given item in the list."

        labelFmt = self.lineLabelFormat
        labelValue = self.data[rowNo][colNo][1]  ###

        if labelFmt is None:
            labelText = None
        elif type(labelFmt) is StringType:
            if labelFmt == 'values':
                labelText = self.lineLabelArray[rowNo][colNo]
            else:
                labelText = labelFmt % labelValue
        elif hasattr(labelFmt, '__call__'):
            labelText = labelFmt(labelValue)
        else:
            raise ValueError(
                "Unknown formatter type %s, expected string or function" %
                labelFmt)

        if labelText:
            label = self.lineLabels[(rowNo, colNo)]
            if not label.visible: return
            #hack to make sure labels are outside the bar
            if y > 0:
                label.setOrigin(x, y + self.lineLabelNudge)
            else:
                label.setOrigin(x, y - self.lineLabelNudge)
            label.setText(labelText)
        else:
            label = None
        return label

    def drawLabel(self, G, rowNo, colNo, x, y):
        '''Draw a label for a given item in the list.
        G must have an add method'''
        G.add(self._innerDrawLabel(rowNo, colNo, x, y))

    def makeLines(self):
        g = Group()
        bubblePlot = getattr(self, '_bubblePlot', None)
        if bubblePlot:
            yA = self.yValueAxis
            xA = self.xValueAxis
            bubbleR = min(yA._bubbleRadius, xA._bubbleRadius)
            bubbleMax = xA._bubbleMax

        labelFmt = self.lineLabelFormat

        P = range(len(self._positions))
        if self.reversePlotOrder: P.reverse()
        inFill = getattr(self, '_inFill', None)
        styleCount = len(self.lines)
        if inFill or [
                rowNo for rowNo in P
                if getattr(self.lines[rowNo % styleCount], 'inFill', False)
        ]:
            inFillY = self.xValueAxis._y
            inFillX0 = self.yValueAxis._x
            inFillX1 = inFillX0 + self.xValueAxis._length
            inFillG = getattr(self, '_inFillG', g)
        lG = getattr(self, '_lineG', g)
        # Iterate over data rows.
        for rowNo in P:
            row = self._positions[rowNo]
            rowStyle = self.lines[rowNo % styleCount]
            rowColor = getattr(rowStyle, 'strokeColor', None)
            dash = getattr(rowStyle, 'strokeDashArray', None)

            if hasattr(rowStyle, 'strokeWidth'):
                width = rowStyle.strokeWidth
            elif hasattr(self.lines, 'strokeWidth'):
                width = self.lines.strokeWidth
            else:
                width = None

            # Iterate over data columns.
            if self.joinedLines:
                points = []
                for xy in row:
                    points += [xy[0], xy[1]]
                if inFill or getattr(rowStyle, 'inFill', False):
                    fpoints = [inFillX0, inFillY
                               ] + points + [inFillX1, inFillY]
                    filler = getattr(rowStyle, 'filler', None)
                    if filler:
                        filler.fill(self, inFillG, rowNo, rowColor, fpoints)
                    else:
                        inFillG.add(
                            Polygon(fpoints,
                                    fillColor=rowColor,
                                    strokeColor=rowColor,
                                    strokeWidth=width or 0.1))
                if inFill in (None, 0, 2):
                    line = PolyLine(points,
                                    strokeColor=rowColor,
                                    strokeLineCap=0,
                                    strokeLineJoin=1)
                    if width:
                        line.strokeWidth = width
                    if dash:
                        line.strokeDashArray = dash
                    lG.add(line)

            if hasattr(rowStyle, 'symbol'):
                uSymbol = rowStyle.symbol
            elif hasattr(self.lines, 'symbol'):
                uSymbol = self.lines.symbol
            else:
                uSymbol = None

            if uSymbol:
                j = -1
                if bubblePlot: drow = self.data[rowNo]
                for xy in row:
                    j += 1
                    symbol = uSymbol2Symbol(uSymbol, xy[0], xy[1], rowColor)
                    if symbol:
                        if bubblePlot:
                            symbol.size = bubbleR * (drow[j][2] /
                                                     bubbleMax)**0.5
                        g.add(symbol)

            # Draw data labels.
            for colNo in range(len(row)):
                x1, y1 = row[colNo]
                self.drawLabel(g, rowNo, colNo, x1, y1)

            shader = getattr(rowStyle, 'shader', None)
            if shader: shader.shade(self, g, rowNo, rowColor, row)

        return g

    def draw(self):
        yA = self.yValueAxis
        xA = self.xValueAxis
        if getattr(self, '_bubblePlot', None):
            yA._bubblePlot = xA._bubblePlot = 1
        yA.setPosition(self.x, self.y, self.height)
        if yA: yA.joinAxis = xA
        if xA: xA.joinAxis = yA
        yA.configure(self.data)

        # if zero is in chart, put x axis there, otherwise use bottom.
        xAxisCrossesAt = yA.scale(0)
        if ((xAxisCrossesAt > self.y + self.height)
                or (xAxisCrossesAt < self.y)):
            y = self.y
        else:
            y = xAxisCrossesAt

        xA.setPosition(self.x, y, self.width)
        xA.configure(self.data)
        self.calcPositions()
        g = Group()
        g.add(self.makeBackground())
        if self._inFill or self.behindAxes:
            xA._joinToAxis()
            if self._inFill:
                self._inFillG = Group()
                g.add(self._inFillG)
            if self.behindAxes:
                self._lineG = Group()
                g.add(self._lineG)
        if self.gridFirst:
            xA.makeGrid(g, parent=self, dim=yA.getGridDims)
            yA.makeGrid(g, parent=self, dim=xA.getGridDims)
        g.add(xA.draw())
        g.add(yA.draw())
        xAex = xA.visibleAxis and (xA._y, ) or ()
        yAex = yA.visibleAxis and (yA._x, ) or ()
        if not self.gridFirst:
            xAdgl = getattr(xA, 'drawGridLast', False)
            yAdgl = getattr(yA, 'drawGridLast', False)
            if not xAdgl:
                xA.makeGrid(g, parent=self, dim=yA.getGridDims, exclude=yAex)
            if not yAdgl:
                yA.makeGrid(g, parent=self, dim=xA.getGridDims, exclude=xAex)
        annotations = getattr(self, 'annotations', [])
        for a in annotations:
            if getattr(a, 'beforeLines', None):
                g.add(a(self, xA.scale, yA.scale))
        g.add(self.makeLines())
        if not self.gridFirst:
            if xAdgl:
                xA.makeGrid(g, parent=self, dim=yA.getGridDims, exclude=yAex)
            if yAdgl:
                yA.makeGrid(g, parent=self, dim=xA.getGridDims, exclude=xAex)
        for a in annotations:
            if not getattr(a, 'beforeLines', None):
                g.add(a(self, xA.scale, yA.scale))
        return g

    def addCrossHair(self,
                     name,
                     xv,
                     yv,
                     strokeColor=colors.black,
                     strokeWidth=1,
                     beforeLines=True):
        from reportlab.graphics.shapes import Group, Line
        annotations = [
            a for a in getattr(self, 'annotations', [])
            if getattr(a, 'name', None) != name
        ]

        def annotation(self, xScale, yScale):
            x = xScale(xv)
            y = yScale(yv)
            g = Group()
            xA = xScale.im_self  #the x axis
            g.add(
                Line(xA._x,
                     y,
                     xA._x + xA._length,
                     y,
                     strokeColor=strokeColor,
                     strokeWidth=strokeWidth))
            yA = yScale.im_self  #the y axis
            g.add(
                Line(x,
                     yA._y,
                     x,
                     yA._y + yA._length,
                     strokeColor=strokeColor,
                     strokeWidth=strokeWidth))
            return g

        annotation.beforeLines = beforeLines
        annotations.append(annotation)
        self.annotations = annotations
Ejemplo n.º 29
0
class HorizontalLineChart(LineChart):
    """Line chart with multiple lines.

    A line chart is assumed to have one category and one value axis.
    Despite its generic name this particular line chart class has
    a vertical value axis and a horizontal category one. It may
    evolve into individual horizontal and vertical variants (like
    with the existing bar charts).

    Available attributes are:

        x: x-position of lower-left chart origin
        y: y-position of lower-left chart origin
        width: chart width
        height: chart height

        useAbsolute: disables auto-scaling of chart elements (?)
        lineLabelNudge: distance of data labels to data points
        lineLabels: labels associated with data values
        lineLabelFormat: format string or callback function
        groupSpacing: space between categories

        joinedLines: enables drawing of lines

        strokeColor: color of chart lines (?)
        fillColor: color for chart background (?)
        lines: style list, used cyclically for data series

        valueAxis: value axis object
        categoryAxis: category axis object
        categoryNames: category names

        data: chart data, a list of data series of equal length
    """

    _attrMap = AttrMap(BASE=LineChart,
        useAbsolute = AttrMapValue(isNumber, desc='Flag to use absolute spacing values.',advancedUsage=1),
        lineLabelNudge = AttrMapValue(isNumber, desc='Distance between a data point and its label.',advancedUsage=1),
        lineLabels = AttrMapValue(None, desc='Handle to the list of data point labels.'),
        lineLabelFormat = AttrMapValue(None, desc='Formatting string or function used for data point labels.'),
        lineLabelArray = AttrMapValue(None, desc='explicit array of line label values, must match size of data if present.'),
        groupSpacing = AttrMapValue(isNumber, desc='? - Likely to disappear.'),
        joinedLines = AttrMapValue(isNumber, desc='Display data points joined with lines if true.'),
        lines = AttrMapValue(None, desc='Handle of the lines.'),
        valueAxis = AttrMapValue(None, desc='Handle of the value axis.'),
        categoryAxis = AttrMapValue(None, desc='Handle of the category axis.'),
        categoryNames = AttrMapValue(isListOfStringsOrNone, desc='List of category names.'),
        data = AttrMapValue(None, desc='Data to be plotted, list of (lists of) numbers.'),
        inFill = AttrMapValue(isBoolean, desc='Whether infilling should be done.',advancedUsage=1),
        reversePlotOrder = AttrMapValue(isBoolean, desc='If true reverse plot order.',advancedUsage=1),
        annotations = AttrMapValue(None, desc='list of callables, will be called with self, xscale, yscale.',advancedUsage=1),
        )

    def __init__(self):
        LineChart.__init__(self)

        # Allow for a bounding rectangle.
        self.strokeColor = None
        self.fillColor = None

        # Named so we have less recoding for the horizontal one :-)
        self.categoryAxis = XCategoryAxis()
        self.valueAxis = YValueAxis()

        # This defines two series of 3 points.  Just an example.
        self.data = [(100,110,120,130),
                     (70, 80, 80, 90)]
        self.categoryNames = ('North','South','East','West')

        self.lines = TypedPropertyCollection(LineChartProperties)
        self.lines.strokeWidth = 1
        self.lines[0].strokeColor = colors.red
        self.lines[1].strokeColor = colors.green
        self.lines[2].strokeColor = colors.blue

        # control spacing. if useAbsolute = 1 then
        # the next parameters are in points; otherwise
        # they are 'proportions' and are normalized to
        # fit the available space.
        self.useAbsolute = 0   #- not done yet
        self.groupSpacing = 1 #5

        self.lineLabels = TypedPropertyCollection(Label)
        self.lineLabelFormat = None
        self.lineLabelArray = None

        # This says whether the origin is above or below
        # the data point. +10 means put the origin ten points
        # above the data point if value > 0, or ten
        # points below if data value < 0.  This is different
        # to label dx/dy which are not dependent on the
        # sign of the data.
        self.lineLabelNudge = 10
        # If you have multiple series, by default they butt
        # together.

        # New line chart attributes.
        self.joinedLines = 1 # Connect items with straight lines.
        self.inFill = 0
        self.reversePlotOrder = 0


    def demo(self):
        """Shows basic use of a line chart."""

        drawing = Drawing(200, 100)

        data = [
                (13, 5, 20, 22, 37, 45, 19, 4),
                (14, 10, 21, 28, 38, 46, 25, 5)
                ]

        lc = HorizontalLineChart()

        lc.x = 20
        lc.y = 10
        lc.height = 85
        lc.width = 170
        lc.data = data
        lc.lines.symbol = makeMarker('Circle')

        drawing.add(lc)

        return drawing


    def calcPositions(self):
        """Works out where they go.

        Sets an attribute _positions which is a list of
        lists of (x, y) matching the data.
        """

        self._seriesCount = len(self.data)
        self._rowLength = max(list(map(len,self.data)))

        if self.useAbsolute:
            # Dimensions are absolute.
            normFactor = 1.0
        else:
            # Dimensions are normalized to fit.
            normWidth = self.groupSpacing
            availWidth = self.categoryAxis.scale(0)[1]
            normFactor = availWidth / normWidth
        self._normFactor = normFactor
        self._yzero = yzero = self.valueAxis.scale(0)
        self._hngs = hngs = 0.5 * self.groupSpacing * normFactor

        self._positions = []
        for rowNo in range(len(self.data)):
            lineRow = []
            for colNo in range(len(self.data[rowNo])):
                datum = self.data[rowNo][colNo]
                if datum is not None:
                    (groupX, groupWidth) = self.categoryAxis.scale(colNo)
                    x = groupX + hngs
                    y = yzero
                    height = self.valueAxis.scale(datum) - y
                    lineRow.append((x, y+height))
            self._positions.append(lineRow)


    def _innerDrawLabel(self, rowNo, colNo, x, y):
        "Draw a label for a given item in the list."

        labelFmt = self.lineLabelFormat
        labelValue = self.data[rowNo][colNo]

        if labelFmt is None:
            labelText = None
        elif type(labelFmt) is str:
            if labelFmt == 'values':
                try:
                    labelText = self.lineLabelArray[rowNo][colNo]
                except:
                    labelText = None
            else:
                labelText = labelFmt % labelValue
        elif hasattr(labelFmt,'__call__'):
            labelText = labelFmt(labelValue)
        else:
            raise ValueError("Unknown formatter type %s, expected string or function"%labelFmt)

        if labelText:
            label = self.lineLabels[(rowNo, colNo)]
            if not label.visible: return
            # Make sure labels are some distance off the data point.
            if y > 0:
                label.setOrigin(x, y + self.lineLabelNudge)
            else:
                label.setOrigin(x, y - self.lineLabelNudge)
            label.setText(labelText)
        else:
            label = None
        return label

    def drawLabel(self, G, rowNo, colNo, x, y):
        '''Draw a label for a given item in the list.
        G must have an add method'''
        G.add(self._innerDrawLabel(rowNo,colNo,x,y))

    def makeLines(self):
        g = Group()

        labelFmt = self.lineLabelFormat
        P = list(range(len(self._positions)))
        if self.reversePlotOrder: P.reverse()
        inFill = self.inFill
        if inFill:
            inFillY = self.categoryAxis._y
            inFillX0 = self.valueAxis._x
            inFillX1 = inFillX0 + self.categoryAxis._length
            inFillG = getattr(self,'_inFillG',g)
        yzero = self._yzero

        # Iterate over data rows.
        for rowNo in P:
            row = self._positions[rowNo]
            styleCount = len(self.lines)
            styleIdx = rowNo % styleCount
            rowStyle = self.lines[styleIdx]
            rowColor = rowStyle.strokeColor
            dash = getattr(rowStyle, 'strokeDashArray', None)
            lineStyle = getattr(rowStyle,'lineStyle',None)

            if hasattr(rowStyle, 'strokeWidth'):
                strokeWidth = rowStyle.strokeWidth
            elif hasattr(self.lines, 'strokeWidth'):
                strokeWidth = self.lines.strokeWidth
            else:
                strokeWidth = None

            # Iterate over data columns.
            if lineStyle=='bar':
                barWidth = getattr(rowStyle,'barWidth',Percentage(50))
                fillColor = getattr(rowStyle,'fillColor',rowColor)
                if isinstance(barWidth,Percentage):
                    hbw = self._hngs*barWidth*0.01
                else:
                    hbw = barWidth*0.5
                for colNo in range(len(row)):
                    x,y = row[colNo]
                    g.add(Rect(x-hbw,min(y,yzero),2*hbw,abs(y-yzero),strokeWidth=strokeWidth,strokeColor=rowColor,fillColor=fillColor))
            elif self.joinedLines or lineStyle=='joinedLine':
                points = []
                for colNo in range(len(row)):
                    points += row[colNo]
                if inFill:
                    points = points + [inFillX1,inFillY,inFillX0,inFillY]
                    inFillG.add(Polygon(points,fillColor=rowColor,strokeColor=rowColor,strokeWidth=0.1))
                else:
                    line = PolyLine(points,strokeColor=rowColor,strokeLineCap=0,strokeLineJoin=1)
                    if strokeWidth:
                        line.strokeWidth = strokeWidth
                    if dash:
                        line.strokeDashArray = dash
                    g.add(line)

            if hasattr(rowStyle, 'symbol'):
                uSymbol = rowStyle.symbol
            elif hasattr(self.lines, 'symbol'):
                uSymbol = self.lines.symbol
            else:
                uSymbol = None

            if uSymbol:
                for colNo in range(len(row)):
                    x1, y1 = row[colNo]
                    symbol = uSymbol2Symbol(uSymbol,x1,y1,rowStyle.strokeColor)
                    if symbol: g.add(symbol)

            # Draw item labels.
            for colNo in range(len(row)):
                x1, y1 = row[colNo]
                self.drawLabel(g, rowNo, colNo, x1, y1)

        return g

    def draw(self):
        "Draws itself."

        vA, cA = self.valueAxis, self.categoryAxis
        vA.setPosition(self.x, self.y, self.height)
        if vA: vA.joinAxis = cA
        if cA: cA.joinAxis = vA
        vA.configure(self.data)

        # If zero is in chart, put x axis there, otherwise
        # use bottom.
        xAxisCrossesAt = vA.scale(0)
        if ((xAxisCrossesAt > self.y + self.height) or (xAxisCrossesAt < self.y)):
            y = self.y
        else:
            y = xAxisCrossesAt

        cA.setPosition(self.x, y, self.width)
        cA.configure(self.data)

        self.calcPositions()

        g = Group()
        g.add(self.makeBackground())
        if self.inFill:
            self._inFillG = Group()
            g.add(self._inFillG)

        g.add(cA)
        g.add(vA)
        cAdgl = getattr(cA,'drawGridLast',False)
        vAdgl = getattr(vA,'drawGridLast',False)
        if not cAdgl: cA.makeGrid(g,parent=self,dim=vA.getGridDims)
        if not vAdgl: vA.makeGrid(g,parent=self,dim=cA.getGridDims)
        g.add(self.makeLines())
        if cAdgl: cA.makeGrid(g,parent=self,dim=vA.getGridDims)
        if vAdgl: vA.makeGrid(g,parent=self,dim=cA.getGridDims)
        for a in getattr(self,'annotations',()): g.add(a(self,cA.scale,vA.scale))
        return g
Ejemplo n.º 30
0
def generatePdfReport(request):
    # Create the HttpResponse object with the appropriate PDF headers.
    response = HttpResponse(content_type='application/pdf')
    response['Content-Disposition'] = 'attachment; filename="somefilename.pdf"'

    # Create the PDF object, using the response object as its "file."
    p = canvas.Canvas(response)
    
    # Draw things on the PDF. Here's where the PDF generation happens.
    # See the ReportLab documentation for the full list of functionality.
    Title="Test Report"
    PAGE_HEIGHT=defaultPageSize[1]  
    PAGE_WIDTH=defaultPageSize[0]  
    p.drawCentredString(PAGE_WIDTH/2.0, PAGE_HEIGHT-108, Title)
    p.drawString(100,900,"Test Configuration")
    configuration=request.session["test_configuration"]
    data=[['Operation','On/Off','Percentage','Keys','Size']]
    if 'readState' in configuration:
        readData=[['Read',configuration['readState'],configuration['readPercentage'],configuration['readKeys'],configuration['readSize']]]
    else:
        readData=[['Read','0','0','0','0']]
    data=data+readData
    if 'writeState' in configuration:
        writeData=[['Write',configuration['writeState'],configuration['writePercentage'],configuration['writeKeys'],configuration['writeSize']]]
    else:
        writeData=[['Write','0','0','0','0']]
    data=data+writeData
    if 'updateState' in configuration:
        updateData=[['Update',configuration['updateState'],configuration['updatePercentage'],configuration['updateKeys'],configuration['updateSize']]]
    else:
        updateData=[['Update','0','0','0','0']]
    data=data+updateData
    table = Table(data, colWidths=100, rowHeights=20)
    table.hAlign="CENTER"
    table.setStyle(TableStyle())
    table.wrapOn(p, 300, 800)
    table.drawOn(p,80,550)
    
    p.drawString(80,700,"Test Configuration")
    p.drawString(80,500,"Test Time : ")
    p.drawString(200,500,str(t1.getTime()) + " miliseconds " )
    p.drawString(80,400,"Test Chart")
    p.saveState()
    
    drawing = Drawing(10, 10)
    data = [(10, 20, 30, 40), (15, 22, 37, 42)]
    xAxis = XCategoryAxis()
    xAxis.setPosition(75, 75, 300)
    xAxis.configure(data)
    xAxis.categoryNames = ['Beer', 'Wine', 'Meat', 'Cannelloni']
    xAxis.labels.boxAnchor = 'n'
    xAxis.labels[3].dy = -15
    xAxis.labels[3].angle = 30
    xAxis.labels[3].fontName = 'Times-Bold'
    yAxis = YValueAxis()
    yAxis.setPosition(50, 50, 125)
    yAxis.configure(data)
    drawing.add(xAxis)
    drawing.add(yAxis)
    
    # Close the PDF object cleanly, and we're done.
    p.showPage()
    p.save()
    return response