123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165 |
- from reportlab.lib.colors import blue, _PCMYK_black
- from reportlab.graphics.charts.textlabels import Label
- from reportlab.graphics.shapes import Circle, Drawing, Group, Line, Rect, String
- from reportlab.graphics.widgetbase import Widget
- from reportlab.lib.attrmap import *
- from reportlab.lib.validators import *
- from reportlab.lib.units import cm
- from reportlab.pdfbase.pdfmetrics import getFont
- from reportlab.graphics.charts.lineplots import _maxWidth
- class DotBox(Widget):
- """Returns a dotbox widget."""
- #Doesn't use TypedPropertyCollection for labels - this can be a later improvement
- _attrMap = AttrMap(
- xlabels = AttrMapValue(isNoneOrListOfNoneOrStrings,
- desc="List of text labels for boxes on left hand side"),
- ylabels = AttrMapValue(isNoneOrListOfNoneOrStrings,
- desc="Text label for second box on left hand side"),
- labelFontName = AttrMapValue(isString,
- desc="Name of font used for the labels"),
- labelFontSize = AttrMapValue(isNumber,
- desc="Size of font used for the labels"),
- labelOffset = AttrMapValue(isNumber,
- desc="Space between label text and grid edge"),
- strokeWidth = AttrMapValue(isNumber,
- desc='Width of the grid and dot outline'),
- gridDivWidth = AttrMapValue(isNumber,
- desc="Width of each 'box'"),
- gridColor = AttrMapValue(isColor,
- desc='Colour for the box and gridding'),
- dotDiameter = AttrMapValue(isNumber,
- desc="Diameter of the circle used for the 'dot'"),
- dotColor = AttrMapValue(isColor,
- desc='Colour of the circle on the box'),
- dotXPosition = AttrMapValue(isNumber,
- desc='X Position of the circle'),
- dotYPosition = AttrMapValue(isNumber,
- desc='X Position of the circle'),
- x = AttrMapValue(isNumber,
- desc='X Position of dotbox'),
- y = AttrMapValue(isNumber,
- desc='Y Position of dotbox'),
- )
- def __init__(self):
- self.xlabels=["Value", "Blend", "Growth"]
- self.ylabels=["Small", "Medium", "Large"]
- self.labelFontName = "Helvetica"
- self.labelFontSize = 6
- self.labelOffset = 5
- self.strokeWidth = 0.5
- self.gridDivWidth=0.5*cm
- self.gridColor=colors.Color(25/255.0,77/255.0,135/255.0)
- self.dotDiameter=0.4*cm
- self.dotColor=colors.Color(232/255.0,224/255.0,119/255.0)
- self.dotXPosition = 1
- self.dotYPosition = 1
- self.x = 30
- self.y = 5
- def _getDrawingDimensions(self):
- leftPadding=rightPadding=topPadding=bottomPadding=5
- #find width of grid
- tx=len(self.xlabels)*self.gridDivWidth
- #add padding (and offset)
- tx=tx+leftPadding+rightPadding+self.labelOffset
- #add in maximum width of text
- tx=tx+_maxWidth(self.xlabels, self.labelFontName, self.labelFontSize)
- #find height of grid
- ty=len(self.ylabels)*self.gridDivWidth
- #add padding (and offset)
- ty=ty+topPadding+bottomPadding+self.labelOffset
- #add in maximum width of text
- ty=ty+_maxWidth(self.ylabels, self.labelFontName, self.labelFontSize)
- #print (tx, ty)
- return (tx,ty)
- def demo(self,drawing=None):
- if not drawing:
- tx,ty=self._getDrawingDimensions()
- drawing = Drawing(tx,ty)
- drawing.add(self.draw())
- return drawing
- def draw(self):
- g = Group()
- #box
- g.add(Rect(self.x,self.y,len(self.xlabels)*self.gridDivWidth,len(self.ylabels)*self.gridDivWidth,
- strokeColor=self.gridColor,
- strokeWidth=self.strokeWidth,
- fillColor=None))
- #internal gridding
- for f in range (1,len(self.ylabels)):
- #horizontal
- g.add(Line(strokeColor=self.gridColor,
- strokeWidth=self.strokeWidth,
- x1 = self.x,
- y1 = self.y+f*self.gridDivWidth,
- x2 = self.x+len(self.xlabels)*self.gridDivWidth,
- y2 = self.y+f*self.gridDivWidth))
- for f in range (1,len(self.xlabels)):
- #vertical
- g.add(Line(strokeColor=self.gridColor,
- strokeWidth=self.strokeWidth,
- x1 = self.x+f*self.gridDivWidth,
- y1 = self.y,
- x2 = self.x+f*self.gridDivWidth,
- y2 = self.y+len(self.ylabels)*self.gridDivWidth))
- # draw the 'dot'
- g.add(Circle(strokeColor=self.gridColor,
- strokeWidth=self.strokeWidth,
- fillColor=self.dotColor,
- cx = self.x+(self.dotXPosition*self.gridDivWidth),
- cy = self.y+(self.dotYPosition*self.gridDivWidth),
- r = self.dotDiameter/2.0))
- #used for centering y-labels (below)
- ascent=getFont(self.labelFontName).face.ascent
- if ascent==0:
- ascent=0.718 # default (from helvetica)
- ascent=ascent*self.labelFontSize # normalize
- #do y-labels
- if self.ylabels != None:
- for f in range (len(self.ylabels)-1,-1,-1):
- if self.ylabels[f]!= None:
- g.add(String(strokeColor=self.gridColor,
- text = self.ylabels[f],
- fontName = self.labelFontName,
- fontSize = self.labelFontSize,
- fillColor=_PCMYK_black,
- x = self.x-self.labelOffset,
- y = self.y+(f*self.gridDivWidth+(self.gridDivWidth-ascent)/2.0),
- textAnchor = 'end'))
- #do x-labels
- if self.xlabels != None:
- for f in range (0,len(self.xlabels)):
- if self.xlabels[f]!= None:
- l=Label()
- l.x=self.x+(f*self.gridDivWidth)+(self.gridDivWidth+ascent)/2.0
- l.y=self.y+(len(self.ylabels)*self.gridDivWidth)+self.labelOffset
- l.angle=90
- l.textAnchor='start'
- l.fontName = self.labelFontName
- l.fontSize = self.labelFontSize
- l.fillColor = _PCMYK_black
- l.setText(self.xlabels[f])
- l.boxAnchor = 'sw'
- l.draw()
- g.add(l)
- return g
- if __name__ == "__main__":
- d = DotBox()
- d.demo().save(fnRoot="dotbox")
|