src/reportlab/graphics/widgets/table.py
changeset 3228 60250a3b480d
child 3617 ae5744e97c42
equal deleted inserted replaced
3227:750fe33ccd5e 3228:60250a3b480d
       
     1 #!/usr/bin/env python
       
     2 #Copyright ReportLab Europe Ltd. 2000-2004
       
     3 #see license.txt for license details
       
     4 #history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/graphics/widgets/grids.py
       
     5 __version__=''' $Id$ '''
       
     6 
       
     7 from reportlab.graphics.widgetbase import Widget
       
     8 from reportlab.graphics.charts.textlabels import Label
       
     9 from reportlab.graphics import shapes
       
    10 from reportlab.lib import colors
       
    11 from reportlab.lib.validators import *
       
    12 from reportlab.lib.attrmap import *
       
    13 
       
    14 from reportlab.graphics.shapes import Drawing
       
    15 
       
    16 class TableWidget(Widget):
       
    17     """A two dimensions table of labels
       
    18     """
       
    19 
       
    20     _attrMap = AttrMap(
       
    21         x = AttrMapValue(isNumber, desc="x position of left edge of table"),
       
    22         y = AttrMapValue(isNumber, desc="y position of bottom edge of table"),
       
    23         width = AttrMapValue(isNumber, desc="table width"),
       
    24         height = AttrMapValue(isNumber, desc="table height"),
       
    25         borderStrokeColor = AttrMapValue(isColorOrNone, desc="table border color"),
       
    26         fillColor = AttrMapValue(isColorOrNone, desc="table fill color"),
       
    27         borderStrokeWidth = AttrMapValue(isNumber, desc="border line width"),
       
    28         horizontalDividerStrokeColor = AttrMapValue(isColorOrNone, desc="table inner horizontal lines color"),
       
    29         verticalDividerStrokeColor = AttrMapValue(isColorOrNone, desc="table inner vertical lines color"),
       
    30         horizontalDividerStrokeWidth = AttrMapValue(isNumber, desc="table inner horizontal lines width"),
       
    31         verticalDividerStrokeWidth = AttrMapValue(isNumber, desc="table inner vertical lines width"),
       
    32         dividerDashArray = AttrMapValue(isListOfNumbersOrNone, desc='Dash array for dividerLines.'),
       
    33         data = AttrMapValue(None, desc="a list of list of strings to be displayed in the cells"),
       
    34         boxAnchor = AttrMapValue(isBoxAnchor, desc="location of the table anchoring point"),
       
    35         fontName = AttrMapValue(isString, desc="text font in the table"),
       
    36         fontSize = AttrMapValue(isNumber, desc="font size of the table"),
       
    37         fontColor = AttrMapValue(isColorOrNone, desc="font color"),
       
    38         alignment = AttrMapValue(OneOf("left", "right"), desc="Alignment of text within cells"),
       
    39         textAnchor = AttrMapValue(OneOf('start','middle','end','numeric'), desc="Alignment of text within cells"),
       
    40     )
       
    41 
       
    42     def __init__(self, x=10, y=10, **kw):
       
    43 
       
    44         self.x = x
       
    45         self.y = y
       
    46         self.width = 200
       
    47         self.height = 100
       
    48         self.borderStrokeColor = colors.black
       
    49         self.fillColor = None
       
    50         self.borderStrokeWidth = 0.5
       
    51         self.horizontalDividerStrokeColor = colors.black
       
    52         self.verticalDividerStrokeColor = colors.black
       
    53         self.horizontalDividerStrokeWidth = 0.5
       
    54         self.verticalDividerStrokeWidth = 0.25
       
    55         self.dividerDashArray = None
       
    56         self.data = [['North','South','East','West'],[100,110,120,130],['A','B','C','D']] # list of rows each row is a list of columns
       
    57         self.boxAnchor = 'nw'
       
    58         #self.fontName = None
       
    59         self.fontSize = 8
       
    60         self.fontColor = colors.black
       
    61         self.alignment = 'right'
       
    62         self.textAnchor = 'start'
       
    63 
       
    64 
       
    65         for k, v in kw.items():
       
    66             if k in self.__class__._attrMap.keys():
       
    67                 setattr(self, k, v)
       
    68                 print 'setting %s = %s'%(k, v)
       
    69             else:
       
    70                 raise ValueError('invalid argument supplied for class %s'%self.__class__)
       
    71 
       
    72     def demo(self):
       
    73         """ returns a sample of this widget with data
       
    74         """
       
    75         d = Drawing(400, 200)
       
    76         t = TableWidget()
       
    77         d.add(t, name='table')
       
    78         d.table.dividerDashArray = (1, 3, 2)
       
    79         d.table.verticalDividerStrokeColor = None
       
    80         d.table.borderStrokeWidth = 0
       
    81         d.table.borderStrokeColor = colors.red
       
    82         return d
       
    83 
       
    84     def draw(self):
       
    85         """ returns a group of shapes
       
    86         """
       
    87         g = shapes.Group()
       
    88 
       
    89         #overall border and fill
       
    90         if self.borderStrokeColor or self.fillColor: # adds border and filling color
       
    91             rect = shapes.Rect(self.x, self.y, self.width, self.height)
       
    92             rect.fillColor = self.fillColor
       
    93             rect.strokeColor = self.borderStrokeColor
       
    94             rect.strokeWidth = self.borderStrokeWidth
       
    95             g.add(rect)
       
    96 
       
    97         #special case - for an empty table we want to avoid divide-by-zero
       
    98         data = self.preProcessData(self.data)
       
    99         rows = len(self.data)
       
   100         cols = len(self.data[0])
       
   101         #print "(rows,cols)=(%s, %s)"%(rows,cols)
       
   102         row_step = self.height / float(rows)
       
   103         col_step = self.width / float(cols)
       
   104         #print "(row_step,col_step)=(%s, %s)"%(row_step,col_step)
       
   105         # draw the grid
       
   106         if self.horizontalDividerStrokeColor:
       
   107             for i in xrange(rows): # make horizontal lines
       
   108                 x1 = self.x
       
   109                 x2 = self.x + self.width
       
   110                 y = self.y + row_step*i
       
   111                 #print 'line (%s, %s), (%s, %s)'%(x1, y, x2, y)
       
   112                 line = shapes.Line(x1, y, x2, y)
       
   113                 line.strokeDashArray = self.dividerDashArray
       
   114                 line.strokeWidth = self.horizontalDividerStrokeWidth
       
   115                 line.strokeColor = self.horizontalDividerStrokeColor
       
   116                 g.add(line)
       
   117         if self.verticalDividerStrokeColor:
       
   118             for i in xrange(cols): # make vertical lines
       
   119                 x = self.x+col_step*i
       
   120                 y1 = self.y
       
   121                 y2 = self.y + self.height
       
   122                 #print 'line (%s, %s), (%s, %s)'%(x, y1, x, y2)
       
   123                 line = shapes.Line(x, y1, x, y2)
       
   124                 line.strokeDashArray = self.dividerDashArray
       
   125                 line.strokeWidth = self.verticalDividerStrokeWidth
       
   126                 line.strokeColor = self.verticalDividerStrokeColor
       
   127                 g.add(line)
       
   128 
       
   129         # since we plot data from down up, we reverse the list
       
   130         self.data.reverse()
       
   131         for (j, row) in enumerate(self.data):
       
   132             y = self.y + j*row_step + 0.5*row_step - 0.5 * self.fontSize
       
   133             for (i, datum) in enumerate(row):
       
   134                 if datum:
       
   135                     x = self.x + i*col_step + 0.5*col_step
       
   136                     s = shapes.String(x, y, str(datum), textAnchor=self.textAnchor)
       
   137                     s.fontName = self.fontName
       
   138                     s.fontSize = self.fontSize
       
   139                     s.fillColor = self.fontColor
       
   140                     g.add(s)
       
   141         return g
       
   142 
       
   143     def preProcessData(self, data):
       
   144         """preprocess and return a new array with at least one row
       
   145         and column (use a None) if needed, and all rows the same
       
   146         length (adding Nones if needed)
       
   147 
       
   148         """
       
   149         if not data:
       
   150             return [[None]]
       
   151         #make all rows have similar number of cells, append None when needed
       
   152         max_row = max( [len(x) for x in data] )
       
   153         for rowNo, row in enumerate(data):
       
   154             if len(row) < max_row:
       
   155                 row.extend([None]*(max_row-len(row)))
       
   156         return data
       
   157 
       
   158 #test
       
   159 if __name__ == '__main__':
       
   160     d = TableWidget().demo()
       
   161     import os
       
   162     d.save(formats=['pdf'],outDir=os.getcwd(),fnRoot=None)