class BarcodeUSPS_4State(_BarcodeWidget): codeName = "USPS_4State" _attrMap = AttrMap( BASE=_BarcodeWidget, widthSize=AttrMapValue( isNumber, '''(float, default 1): the bar width size adjustment between 0 and 1.''' ), heightSize=AttrMapValue( isNumber, '''(float, default 1): the bar height size adjustment between 0 and 1.''' ), fontName=AttrMapValue(isString, desc='human readable font'), fontSize=AttrMapValue(isNumber, desc='human readable font size'), tracking=AttrMapValue(isString, desc='tracking data'), routing=AttrMapValue(isString, desc='routing data'), humanReadable=AttrMapValue(isBoolean, desc='if human readable'), ) def __init__(self, **kw): from reportlab.graphics.barcode.usps4s import USPS_4State kw.setdefault('routing', '01234567891') _BarcodeWidget.__init__(self, USPS_4State, '01234567094987654321', **kw) def annotate(self, x, y, text, fontName, fontSize, anchor='middle'): _BarcodeWidget.annotate(self, x, y, text, fontName, fontSize, anchor='start')
class AddedPlot(LinePlot, object): _attrMap = AttrMap(BASE=LinePlot, g=AttrMapValue(None, desc='Groups.')) def __init__(self): super(AddedPlot, self).__init__() self.g = Group() def draw(self): g = super(AddedPlot, self).draw() for i in self.g.contents: if isinstance(i, Rect): x = self.xValueAxis.scale(i.x) width = self.xValueAxis.scale( i.width) - self.xValueAxis.scale(0) y = self.yValueAxis.scale(i.y) height = self.yValueAxis.scale( i.height) - self.yValueAxis.scale(0) g.add( Rect(x, y, width, height, strokeColor=i.strokeColor, strokeWidth=i.strokeWidth, fillColor=i.fillColor)) else: g.add(i) return g
class BarcodeFIM(_BarcodeWidget): """ FIM was developed as part of the POSTNET barcoding system. FIM (Face Identification Marking) is used by the cancelling machines to sort mail according to whether or not they have bar code and their postage requirements. There are four types of FIM called FIM A, FIM B, FIM C, and FIM D. The four FIM types have the following meanings: FIM A- Postage required pre-barcoded FIM B - Postage pre-paid, no bar code exists FIM C- Postage prepaid prebarcoded FIM D- Postage required, no bar code exists """ codeName = "FIM" _attrMap = AttrMap(BASE=_BarcodeWidget, barWidth = AttrMapValue(isNumber,'''(float, default 1/32in): the bar width.'''), spaceWidth = AttrMapValue(isNumber,'''(float or None, default 1/16in): width of intercharacter gap. None means "use barWidth".'''), barHeight = AttrMapValue(isNumber,'''(float, default 5/8in): The bar height.'''), quiet = AttrMapValue(isBoolean,'''(bool, default 0): Whether to include quiet zones in the symbol.'''), lquiet = AttrMapValue(isNumber,'''(float, default: 15/32in): Quiet zone size to left of code, if quiet is true.'''), rquiet = AttrMapValue(isNumber,'''(float, default 1/4in): Quiet zone size to right left of code, if quiet is true.'''), fontName = AttrMapValue(isString, desc='human readable font'), fontSize = AttrMapValue(isNumber, desc='human readable font size'), humanReadable = AttrMapValue(isBoolean, desc='if human readable'), ) def __init__(self,**kw): from reportlab.graphics.barcode.usps import FIM _BarcodeWidget.__init__(self,FIM,"A",**kw)
class BarcodeI2of5(_BarcodeWidget): """Interleaved 2 of 5 is used in distribution and warehouse industries. It encodes an even-numbered sequence of numeric digits. There is an optional module 10 check digit; if including this, the total length must be odd so that it becomes even after including the check digit. Otherwise the length must be even. Since the check digit is optional, our library does not check it. """ _tests = [ '12', '1234', '123456', '12345678', '1234567890' ] codeName = "I2of5" _attrMap = AttrMap(BASE=_BarcodeWidget, barWidth = AttrMapValue(isNumber,'''(float, default .0075): X-Dimension, or width of the smallest element Minumum is .0075 inch (7.5 mils).'''), ratio = AttrMapValue(isNumber,'''(float, default 2.2): The ratio of wide elements to narrow elements. Must be between 2.0 and 3.0 (or 2.2 and 3.0 if the barWidth is greater than 20 mils (.02 inch))'''), gap = AttrMapValue(isNumberOrNone,'''(float or None, default None): width of intercharacter gap. None means "use barWidth".'''), barHeight = AttrMapValue(isNumber,'''(float, see default below): Height of the symbol. Default is the height of the two bearer bars (if they exist) plus the greater of .25 inch or .15 times the symbol's length.'''), checksum = AttrMapValue(isBoolean,'''(bool, default 1): Whether to compute and include the check digit'''), bearers = AttrMapValue(isNumber,'''(float, in units of barWidth. default 3.0): Height of bearer bars (horizontal bars along the top and bottom of the barcode). Default is 3 x-dimensions. Set to zero for no bearer bars. (Bearer bars help detect misscans, so it is suggested to leave them on).'''), quiet = AttrMapValue(isBoolean,'''(bool, default 1): Whether to include quiet zones in the symbol.'''), lquiet = AttrMapValue(isNumber,'''(float, see default below): Quiet zone size to left of code, if quiet is true. Default is the greater of .25 inch, or .15 times the symbol's length.'''), rquiet = AttrMapValue(isNumber,'''(float, defaults as above): Quiet zone size to right left of code, if quiet is true.'''), fontName = AttrMapValue(isString, desc='human readable font'), fontSize = AttrMapValue(isNumber, desc='human readable font size'), humanReadable = AttrMapValue(isBoolean, desc='if human readable'), stop = AttrMapValue(isBoolean, desc='if we use start/stop symbols (default 1)'), ) _bcTransMap = {} def __init__(self,**kw): from reportlab.graphics.barcode.common import I2of5 _BarcodeWidget.__init__(self,I2of5,1234,**kw)
class BarcodeStandard93(BarcodeCode128): """This is a compressed form of Code 39""" codeName = "Standard93" _attrMap = AttrMap(BASE=BarcodeCode128, stop = AttrMapValue(isBoolean, desc='if we use start/stop symbols (default 1)'), ) def __init__(self,**kw): from reportlab.graphics.barcode.code93 import Standard93 _BarcodeWidget.__init__(self,Standard93,"CODE 93",**kw)
class DataMatrixWidget(Widget, _DMTXCheck): codeName = "DataMatrix" _attrMap = AttrMap( BASE=Widget, value=AttrMapValue(isString, desc='Datamatrix data'), x=AttrMapValue(isNumber, desc='x-coord'), y=AttrMapValue(isNumber, desc='y-coord'), color=AttrMapValue(isColor, desc='foreground color'), bgColor=AttrMapValue(isColorOrNone, desc='background color'), encoding=AttrMapValue(isString, desc='encoding'), size=AttrMapValue(isString, desc='size'), cellSize=AttrMapValue(isString, desc='cellSize'), anchor=AttrMapValue(isBoxAnchor, desc='anchor pooint for x,y'), ) _defaults = dict( x=('0', _numConv), y=('0', _numConv), color=('black', toColor), bgColor=(None, lambda _: toColor(_) if _ is not None else _), encoding=('Ascii', None), size=('SquareAuto', None), cellSize=('5x5', None), anchor=('sw', None), ) def __init__(self, value='Hello Cruel World!', **kwds): self.pylibdmtx_check() self.value = value for k, (d, c) in self._defaults.items(): v = kwds.pop(k, d) if c: v = c(v) setattr(self, k, v) def rect(self, x, y, w, h, fill=1, stroke=0): self._gadd( Rect(x, y, w, h, strokeColor=None, fillColor=self._fillColor)) def saveState(self, *args, **kwds): pass restoreState = setStrokeColor = saveState def setFillColor(self, c): self._fillColor = c def draw(self): m = DataMatrix(value=self.value, **{k: getattr(self, k) for k in self._defaults}) m.canv = self m.y += m.height g = Group() self._gadd = g.add m.draw() return g
class BarcodeCode128(BarcodeI2of5): """Code 128 encodes any number of characters in the ASCII character set. """ _tests = [ 'ReportLab Rocks!' ] codeName = "Code128" _attrMap = AttrMap(BASE=BarcodeI2of5,UNWANTED=('bearers','checksum','ratio','checksum','stop')) def __init__(self,**kw): from reportlab.graphics.barcode.code128 import Code128 _BarcodeWidget.__init__(self,Code128,"AB-12345678",**kw)
class ShadedPolygon(Widget, LineShape): _attrMap = AttrMap( BASE=LineShape, angle=AttrMapValue(isNumber, desc="Shading angle"), fillColorStart=AttrMapValue(isColorOrNone), fillColorEnd=AttrMapValue(isColorOrNone), numShades=AttrMapValue(isNumber, desc='The number of interpolating colors.'), cylinderMode=AttrMapValue(isBoolean, desc='True if shading reverses in middle.'), points=AttrMapValue(isListOfNumbers), ) def __init__(self, **kw): self.angle = 90 self.fillColorStart = colors.red self.fillColorEnd = colors.green self.cylinderMode = 0 self.numShades = 50 self.points = [-1, -1, 2, 2, 3, -1] LineShape.__init__(self, kw) def draw(self): P = self.points P = map(lambda i, P=P: (P[i], P[i + 1]), xrange(0, len(P), 2)) path = definePath([('moveTo', ) + P[0]] + map(lambda x: ('lineTo', ) + x, P[1:]) + ['closePath'], fillColor=None, strokeColor=None) path.isClipPath = 1 g = Group() g.add(path) angle = self.angle orientation = 'vertical' if angle == 180: angle = 0 elif angle in (90, 270): orientation = 'horizontal' angle = 0 rect = ShadedRect(strokeWidth=0, strokeColor=None, orientation=orientation) for k in 'fillColorStart', 'fillColorEnd', 'numShades', 'cylinderMode': setattr(rect, k, getattr(self, k)) g.add(rotatedEnclosingRect(P, angle, rect)) g.add(EmptyClipPath) path = path.copy() path.isClipPath = 0 path.strokeColor = self.strokeColor path.strokeWidth = self.strokeWidth g.add(path) return g
class BarcodePOSTNET(_BarcodeWidget): codeName = "POSTNET" _attrMap = AttrMap(BASE=_BarcodeWidget, barWidth = AttrMapValue(isNumber,'''(float, default 0.018*in): the bar width.'''), spaceWidth = AttrMapValue(isNumber,'''(float or None, default 0.0275in): width of intercharacter gap.'''), shortHeight = AttrMapValue(isNumber,'''(float, default 0.05in): The short bar height.'''), barHeight = AttrMapValue(isNumber,'''(float, default 0.125in): The full bar height.'''), fontName = AttrMapValue(isString, desc='human readable font'), fontSize = AttrMapValue(isNumber, desc='human readable font size'), humanReadable = AttrMapValue(isBoolean, desc='if human readable'), ) def __init__(self,**kw): from reportlab.graphics.barcode.usps import POSTNET _BarcodeWidget.__init__(self,POSTNET,"78247-1043",**kw)
class BarcodeCode11(BarcodeI2of5): """Used mostly for labelling telecommunications equipment. It encodes numeric digits. """ codeName = "Code11" _attrMap = AttrMap(BASE=BarcodeI2of5, checksum = AttrMapValue(isInt,'''(integer, default 2): Whether to compute and include the check digit(s). (0 none, 1 1-digit, 2 2-digit, -1 auto, default -1): How many checksum digits to include. -1 ("auto") means 1 if the number of digits is 10 or less, else 2.'''), ) def __init__(self,**kw): from reportlab.graphics.barcode.common import Code11 _BarcodeWidget.__init__(self,Code11,"01234545634563",**kw)
class _BarcodeWidget(PlotArea): _attrMap = AttrMap(BASE=PlotArea, barStrokeColor = AttrMapValue(isColorOrNone, desc='Color of bar borders.'), barFillColor = AttrMapValue(isColorOrNone, desc='Color of bar interior areas.'), barStrokeWidth = AttrMapValue(isNumber, desc='Width of bar borders.'), value = AttrMapValue(EitherOr((isString,isNumber)), desc='Value.'), textColor = AttrMapValue(isColorOrNone, desc='Color of human readable text.'), valid = AttrMapValue(isBoolean), validated = AttrMapValue(isString,desc="validated form of input"), encoded = AttrMapValue(None,desc="encoded form of input"), decomposed = AttrMapValue(isString,desc="decomposed form of input"), canv = AttrMapValue(None,desc="temporarily used for internal methods"), gap = AttrMapValue(isNumberOrNone, desc='Width of inter character gaps.'), ) textColor = barFillColor = black barStrokeColor = None barStrokeWidth = 0 _BCC = None def __init__(self,BCC=None,_value='',**kw): self._BCC = BCC class Combiner(self.__class__,BCC): __name__ = self.__class__.__name__ self.__class__ = Combiner PlotArea.__init__(self) del self.width, self.height self.x = self.y = 0 kw.setdefault('value',_value) BCC.__init__(self,**kw) def rect(self,x,y,w,h,**kw): self._Gadd(Rect(self.x+x,self.y+y,w,h, strokeColor=self.barStrokeColor,strokeWidth=self.barStrokeWidth, fillColor=self.barFillColor)) def draw(self): if not self._BCC: raise NotImplementedError("Abstract class %s cannot be drawn" % self.__class__.__name__) self.canv = self G = Group() self._Gadd = G.add self._Gadd(Rect(self.x,self.y,self.width,self.height,fillColor=None,strokeColor=None,strokeWidth=0.0001)) self._BCC.draw(self) del self.canv, self._Gadd return G def annotate(self,x,y,text,fontName,fontSize,anchor='middle'): self._Gadd(String(self.x+x,self.y+y,text,fontName=fontName,fontSize=fontSize, textAnchor=anchor,fillColor=self.textColor))
class YValueAxisWithDesc(YValueAxis): _attrMap = AttrMap(BASE=YValueAxis, desc=AttrMapValue( None, desc="The description of the Y axis.")) def __init__(self, desc=None): YValueAxis.__init__(self) self.desc = None if isString(desc) is True: self.desc = desc 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
class XCategoryAxisWithDesc(XCategoryAxis): _attrMap = AttrMap(BASE=XCategoryAxis, desc=AttrMapValue( None, desc="The description of the X axis.")) def __init__(self, desc=None): XCategoryAxis.__init__(self) self.desc = None if isString(desc) is True: self.desc = desc self.labels.fontName = DefaultFontName def makeTickLabels(self): g = XCategoryAxis.makeTickLabels(self) desc_text = make_desc_test(self, "X") if desc_text is not None: g.add(desc_text) return g
class Marker(Widget): '''A polymorphic class of markers''' _attrMap = AttrMap(BASE=Widget, kind = AttrMapValue( OneOf(None, 'Square', 'Diamond', 'Circle', 'Cross', 'Triangle', 'StarSix', 'Pentagon', 'Hexagon', 'Heptagon', 'Octagon', 'StarFive', 'FilledSquare', 'FilledCircle', 'FilledDiamond', 'FilledCross', 'FilledTriangle','FilledStarSix', 'FilledPentagon', 'FilledHexagon', 'FilledHeptagon', 'FilledOctagon', 'FilledStarFive', 'Smiley','ArrowHead', 'FilledArrowHead'), desc='marker type name'), size = AttrMapValue(isNumber,desc='marker size'), x = AttrMapValue(isNumber,desc='marker x coordinate'), y = AttrMapValue(isNumber,desc='marker y coordinate'), dx = AttrMapValue(isNumber,desc='marker x coordinate adjustment'), dy = AttrMapValue(isNumber,desc='marker y coordinate adjustment'), angle = AttrMapValue(isNumber,desc='marker rotation'), fillColor = AttrMapValue(isColorOrNone, desc='marker fill colour'), strokeColor = AttrMapValue(isColorOrNone, desc='marker stroke colour'), strokeWidth = AttrMapValue(isNumber, desc='marker stroke width'), arrowBarbDx = AttrMapValue(isNumber, desc='arrow only the delta x for the barbs'), arrowHeight = AttrMapValue(isNumber, desc='arrow only height'), ) def __init__(self,*args,**kw): self.setProperties(kw) self._setKeywords( kind = None, strokeColor = black, strokeWidth = 0.1, fillColor = None, size = 5, x = 0, y = 0, dx = 0, dy = 0, angle = 0, arrowBarbDx = -1.25, arrowHeight = 1.875, ) def clone(self): return new.instance(self.__class__,self.__dict__.copy()) def _Smiley(self): x, y = self.x+self.dx, self.y+self.dy d = self.size/2.0 s = SmileyFace() s.fillColor = self.fillColor s.strokeWidth = self.strokeWidth s.strokeColor = self.strokeColor s.x = x-d s.y = y-d s.size = d*2 return s def _Square(self): x, y = self.x+self.dx, self.y+self.dy d = self.size/2.0 s = Rect(x-d,y-d,2*d,2*d,fillColor=self.fillColor,strokeColor=self.strokeColor,strokeWidth=self.strokeWidth) return s def _Diamond(self): d = self.size/2.0 return self._doPolygon((-d,0,0,d,d,0,0,-d)) def _Circle(self): x, y = self.x+self.dx, self.y+self.dy s = Circle(x,y,self.size/2.0,fillColor=self.fillColor,strokeColor=self.strokeColor,strokeWidth=self.strokeWidth) return s def _Cross(self): x, y = self.x+self.dx, self.y+self.dy s = float(self.size) h, s = s/2, s/6 return self._doPolygon((-s,-h,-s,-s,-h,-s,-h,s,-s,s,-s,h,s,h,s,s,h,s,h,-s,s,-s,s,-h)) def _Triangle(self): x, y = self.x+self.dx, self.y+self.dy r = float(self.size)/2 c = 30*_toradians s = sin(30*_toradians)*r c = cos(c)*r return self._doPolygon((0,r,-c,-s,c,-s)) def _StarSix(self): r = float(self.size)/2 c = 30*_toradians s = sin(c)*r c = cos(c)*r z = s/2 g = c/2 return self._doPolygon((0,r,-z,s,-c,s,-s,0,-c,-s,-z,-s,0,-r,z,-s,c,-s,s,0,c,s,z,s)) def _StarFive(self): R = float(self.size)/2 r = R*sin(18*_toradians)/cos(36*_toradians) P = [] angle = 90 for i in xrange(5): for radius in R, r: theta = angle*_toradians P.append(radius*cos(theta)) P.append(radius*sin(theta)) angle = angle + 36 return self._doPolygon(P) def _Pentagon(self): return self._doNgon(5) def _Hexagon(self): return self._doNgon(6) def _Heptagon(self): return self._doNgon(7) def _Octagon(self): return self._doNgon(8) def _ArrowHead(self): s = self.size h = self.arrowHeight b = self.arrowBarbDx return self._doPolygon((0,0,b,-h,s,0,b,h)) def _doPolygon(self,P): x, y = self.x+self.dx, self.y+self.dy if x or y: P = map(lambda i,P=P,A=[x,y]: P[i] + A[i&1], range(len(P))) return Polygon(P, strokeWidth =self.strokeWidth, strokeColor=self.strokeColor, fillColor=self.fillColor) def _doFill(self): old = self.fillColor if old is None: self.fillColor = self.strokeColor r = (self.kind and getattr(self,'_'+self.kind[6:]) or Group)() self.fillColor = old return r def _doNgon(self,n): P = [] size = float(self.size)/2 for i in xrange(n): r = (2.*i/n+0.5)*pi P.append(size*cos(r)) P.append(size*sin(r)) return self._doPolygon(P) _FilledCircle = _doFill _FilledSquare = _doFill _FilledDiamond = _doFill _FilledCross = _doFill _FilledTriangle = _doFill _FilledStarSix = _doFill _FilledPentagon = _doFill _FilledHexagon = _doFill _FilledHeptagon = _doFill _FilledOctagon = _doFill _FilledStarFive = _doFill _FilledArrowHead = _doFill def draw(self): if self.kind: m = getattr(self,'_'+self.kind) if self.angle: _x, _dx, _y, _dy = self.x, self.dx, self.y, self.dy self.x, self.dx, self.y, self.dy = 0,0,0,0 try: m = m() finally: self.x, self.dx, self.y, self.dy = _x, _dx, _y, _dy if not isinstance(m,Group): _m, m = m, Group() m.add(_m) if self.angle: m.rotate(self.angle) x, y = _x+_dx, _y+_dy if x or y: m.shift(x,y) else: m = m() else: m = Group() return m
class PH(PropHolder): _attrMap = AttrMap(a=AttrMapValue(isNumber), b=AttrMapValue(isNumber))
AttrMap( BASE=_BarcodeWidget, barWidth=AttrMapValue( isNumber, '''(float, default .0075): X-Dimension, or width of the smallest element Minumum is .0075 inch (7.5 mils).'''), ratio=AttrMapValue( isNumber, '''(float, default 2.2): The ratio of wide elements to narrow elements. Must be between 2.0 and 3.0 (or 2.2 and 3.0 if the barWidth is greater than 20 mils (.02 inch))'''), gap=AttrMapValue( isNumberOrNone, '''(float or None, default None): width of intercharacter gap. None means "use barWidth".'''), barHeight=AttrMapValue( isNumber, '''(float, see default below): Height of the symbol. Default is the height of the two bearer bars (if they exist) plus the greater of .25 inch or .15 times the symbol's length.'''), checksum=AttrMapValue( isBoolean, '''(bool, default 1): Whether to compute and include the check digit'''), bearers=AttrMapValue( isNumber, '''(float, in units of barWidth. default 3.0): Height of bearer bars (horizontal bars along the top and bottom of the barcode). Default is 3 x-dimensions. Set to zero for no bearer bars. (Bearer bars help detect misscans, so it is suggested to leave them on).'''), quiet=AttrMapValue( isBoolean, '''(bool, default 1): Whether to include quiet zones in the symbol.'''), lquiet=AttrMapValue( isNumber, '''(float, see default below): Quiet zone size to left of code, if quiet is true. Default is the greater of .25 inch, or .15 times the symbol's length.'''), rquiet=AttrMapValue( isNumber, '''(float, defaults as above): Quiet zone size to right left of code, if quiet is true.'''), fontName=AttrMapValue(isString, desc='human readable font'), fontSize=AttrMapValue(isNumber, desc='human readable font size'), humanReadable=AttrMapValue(isBoolean, desc='if human readable'), stop=AttrMapValue(isBoolean, desc='if we use start/stop symbols (default 1)'), ),
class ReportLabBarChart(BarChart): _flipXY = 0 _attrMap = AttrMap( BASE=BarChart, drawLegend=AttrMapValue(isBoolean, desc='If true draw legend.', advancedUsage=1), legendPositionType=AttrMapValue(OneOf("null", "top-left", "top-mid", "top-right", "bottom-left", "bottom-mid", "bottom-right"), desc="The position of LinLegend."), legendAdjustX=AttrMapValue(isNumber, desc='xxx.'), legendAdjustY=AttrMapValue(isNumber, desc='xxx.'), legendCategoryNames=AttrMapValue( isListOfStringsOrNone, desc='List of legend category names.'), titleMain=AttrMapValue(isString, desc='main title text.'), titleMainFontName=AttrMapValue(isString, desc='main title font name.'), titleMainFontSize=AttrMapValue(isNumberInRange(0, 100), desc='main title font size.'), titleMainFontColor=AttrMapValue(isColor, desc='main title font color.'), legendFontSize=AttrMapValue(isNumberInRange(0, 100), desc='legend text font size.'), x_labels_height=AttrMapValue(isNumberInRange(0, 100), desc='the max height in x-labels.'), y_labels_height=AttrMapValue(isNumberInRange(0, 50), desc='the max height in y-labels.')) def __init__(self, x, y, width, height, cat_names, data, step_count=4, style="parallel", label_format=None, label_sum=False, legend_names=None, legend_position="top-right", legend_adjust_x=0, legend_adjust_y=0, main_title="", main_title_font_name=None, main_title_font_size=None, main_title_font_color=None, x_desc=None, y_desc=None, cat_label_angle=30, cat_label_all=False): BarChart.__init__(self) if self._flipXY: self.categoryAxis = YCategoryAxisWithDesc(desc=y_desc) self.valueAxis = XValueAxisWithDesc(desc=x_desc) else: self.categoryAxis = XCategoryAxisWithDesc(desc=x_desc) self.valueAxis = YValueAxisWithDesc(desc=y_desc) if style not in ["stacked", "parallel"]: style = "parallel" self.categoryAxis.style = style self.valueAxis.visibleGrid = 1 self.valueAxis.gridStrokeColor = colors.Color(0.5, 0.5, 0.5, 0.5) self.valueAxis.gridStrokeWidth = 1 self.x = x self.y = y self.height = height self.width = width self.data = data self.strokeColor = colors.black self.categoryAxis.labels.boxAnchor = 'ne' # self.categoryAxis.labels.dx = 0 # self.categoryAxis.labels.dy = 0 self.categoryAxis.labels.angle = cat_label_angle # self.categoryAxis.labels.boxFillColor = colors.Color(1, 0, 0, 1) if cat_label_all is False: cat_names_num = len(cat_names) show_cat_num = 4 if cat_names_num > show_cat_num: gap_num = int(cat_names_num / show_cat_num) for i in range(cat_names_num): if i % gap_num != 0: cat_names[i] = "" self.categoryAxis.categoryNames = cat_names self._lable_sum = [] if label_format is not None: self.barLabelFormat = label_format if len(data) > 1 and style == "stacked": if label_sum: self._cal_col_sum() self.barLabels.boxTarget = "hi" self.barLabels.nudge = 15 else: self.barLabels.boxTarget = "mid" else: self.barLabels.boxTarget = "hi" self.barLabels.nudge = 15 min_value, max_value, step = self.get_limit_value(step_count) self.valueAxis.valueMin = min_value self.valueAxis.valueMax = max_value self.valueAxis.valueStep = step self.drawLegend = False self.legendCategoryNames = None if legend_names is not None and isListOfStrings(legend_names) is True: self.drawLegend = True self.legendCategoryNames = legend_names self.legendPositionType = legend_position self.legendAdjustX = legend_adjust_x self.legendAdjustY = legend_adjust_y self.legendFontSize = 7 self.titleMain = main_title self.titleMainFontName = DefaultFontName self.titleMainFontSize = STATE_DEFAULTS['fontSize'] self.titleMainFontColor = colors.black if main_title_font_name is not None: self.titleMainFontName = main_title_font_name if main_title_font_size is not None: self.titleMainFontSize = main_title_font_size if main_title_font_color is not None: self.titleMainFontColor = main_title_font_color self.x_labels_height = 0 self.y_labels_height = 0 def _cal_col_sum(self): for i in range(len(self.data[0])): self._lable_sum.append(0) for d in self.data: idx = 0 for i in d: self._lable_sum[idx] += i idx += 1 def get_limit_value(self, step_count): min_value = 0xffffffff max_value = 0 - min_value _data = [] if self.categoryAxis.style == "stacked": flag = True for d in self.data: idx = 0 for i in d: if flag: _data.append(i) else: _data[idx] += i idx += 1 flag = False for d in _data: if d > max_value: max_value = d for d in self.data: for i in d: if i < min_value: min_value = i else: _data = self.data[:] for d in _data: for i in d: if i > max_value: max_value = i if i < min_value: min_value = i max_value += int(max_value / 10) max_value = int(max_value / 5) * 5 min_value -= int(min_value / 10) min_value = int(min_value / 5) * 5 step = int((max_value - min_value) / step_count) step = int(step / 5 + 1) * 5 max_value = min_value + (step * step_count) return min_value, max_value, step def set_bar_color(self): if self.legendCategoryNames is None: self.legendCategoryNames = [] legend_num = len(self.legendCategoryNames) data_num = len(self.data) for i in range(data_num): bar = self.bars[i] bar.strokeColor = ALL_COLORS[i] bar.fillColor = ALL_COLORS[i] if i >= legend_num: self.legendCategoryNames.append("unknown") legend_num = len(self.legendCategoryNames) temp_category_names = self.legendCategoryNames[:] if legend_num >= 1: self.legendCategoryNames = [] color_name_pairs = [(0, name) for name in temp_category_names] legend_width = ChartsLegend.calc_legend_width( color_name_pairs, 10, 10, DefaultFontName, self.legendFontSize) per_legend_width = int(legend_width / legend_num) legend_num_per_row = int(self.width / per_legend_width) index = 0 row_names = [] for name in temp_category_names: row_names.append(name) index += 1 if index == legend_num_per_row: index = 0 self.legendCategoryNames.append(row_names) row_names = [] if len(row_names) > 0: self.legendCategoryNames.append(row_names) else: self.legendCategoryNames = temp_category_names def _draw_legend(self, g): legend_count = 0 for i in range(len(self.legendCategoryNames)): legend = ChartsLegend() legend.positionType = self.legendPositionType if self.legendPositionType != "null": if self.legendPositionType in [ "bottom-left", "bottom-mid", "bottom-right" ]: legend.backgroundRect = \ Rect(self.x, self.y + legend.bottom_gap - self.x_labels_height - 15 - ((i + 1) * legend.fontSize), self.width, self.height) else: legend.backgroundRect = Rect( self.x, self.y + (i * legend.fontSize * 1.2), self.width, self.height) legend.adjustX = self.legendAdjustX legend.adjustY = self.legendAdjustY legend.fontSize = self.legendFontSize legend.colorNamePairs = [] for j in range(len(self.legendCategoryNames[i])): legend.colorNamePairs.append((ALL_COLORS[legend_count + j], self.legendCategoryNames[i][j])) legend_count += len(self.legendCategoryNames[i]) g.add(legend) def _get_label_sum_text(self, row_no, col_no): """ return formatted label text :param row_no: :param col_no: :return: """ len_row = len(self.data) if row_no != len_row - 1: return None text = self._lable_sum[col_no] label_fmt = self.barLabelFormat if isinstance(label_fmt, (list, tuple)): label_fmt = label_fmt[row_no] if isinstance(label_fmt, (list, tuple)): label_fmt = label_fmt[col_no] if label_fmt is None: label_text = None elif label_fmt == 'values': label_text = text elif isStr(label_fmt): label_text = label_fmt % text elif hasattr(label_fmt, '__call__'): label_text = label_fmt(text) else: msg = "Unknown formatter type %s, expected string or function" % label_fmt raise Exception(msg) return label_text def _addBarLabel(self, g, row_no, col_no, x, y, width, height): if self._lable_sum: text = self._get_label_sum_text(row_no, col_no) else: text = self._getLabelText(row_no, col_no) if text: self._addLabel(text, self.barLabels[(row_no, col_no)], g, row_no, col_no, x, y, width, height) def draw(self): self.set_bar_color() if self.drawLegend is True: if self.legendPositionType in [ "bottom-left", "bottom-mid", "bottom-right" ]: row_count = len(self.legendCategoryNames) + 1 self.height -= row_count * self.legendFontSize self.y += row_count * self.legendFontSize g = BarChart.draw(self) if self.drawLegend is True: self._draw_legend(g) if self.titleMain != "": title = String(0, 0, self.titleMain) title.fontSize = self.titleMainFontSize title.fontName = self.titleMainFontName title.fillColor = self.titleMainFontColor title.textAnchor = 'start' # title.x = self.x - 20 # title.y = self.y + self.height + 20 title.x = 0 title.y = self.y + self.height + 20 g.add(title) return g
class QrCodeWidget(Widget): codeName = "QR" _attrMap = AttrMap( BASE=Widget, value=AttrMapValue(isUnicodeOrQRList, desc='QRCode data'), x=AttrMapValue(isNumber, desc='x-coord'), y=AttrMapValue(isNumber, desc='y-coord'), barFillColor=AttrMapValue(isColor, desc='bar color'), barWidth=AttrMapValue( isNumber, desc='Width of bars.'), # maybe should be named just width? barHeight=AttrMapValue( isNumber, desc='Height of bars.'), # maybe should be named just height? barBorder=AttrMapValue( isNumber, desc='Width of QR border.'), # maybe should be named qrBorder? barLevel=AttrMapValue( isLevel, desc='QR Code level.'), # maybe should be named qrLevel qrVersion=AttrMapValue(isNumberOrNone, desc='QR Code version. None for auto'), # Below are ignored, they make no sense barStrokeWidth=AttrMapValue(isNumber, desc='Width of bar borders.'), barStrokeColor=AttrMapValue(isColor, desc='Color of bar borders.'), ) x = 0 y = 0 barFillColor = colors.black barStrokeColor = None barStrokeWidth = 0 barHeight = 32 * mm barWidth = 32 * mm barBorder = 4 barLevel = 'L' qrVersion = None value = None def __init__(self, value='Hello World', **kw): self.value = isUnicodeOrQRList.normalize(value) for k, v in kw.items(): setattr(self, k, v) ec_level = getattr(qrencoder.QRErrorCorrectLevel, self.barLevel) self.__dict__['qr'] = qrencoder.QRCode(self.qrVersion, ec_level) if isUnicode(self.value): self.addData(self.value) elif self.value: for v in self.value: self.addData(v) def addData(self, value): self.qr.addData(value) def draw(self): self.qr.make() g = Group() color = self.barFillColor border = self.barBorder width = self.barWidth height = self.barHeight x = self.x y = self.y g.add(SRect(x, y, width, height, fillColor=None)) moduleCount = self.qr.getModuleCount() minwh = float(min(width, height)) boxsize = minwh / (moduleCount + border * 2.0) offsetX = x + (width - minwh) / 2.0 offsetY = y + (minwh - height) / 2.0 for r, row in enumerate(self.qr.modules): row = map(bool, row) c = 0 for t, tt in itertools.groupby(row): isDark = t count = len(list(tt)) if isDark: x = (c + border) * boxsize y = (r + border + 1) * boxsize s = SRect(offsetX + x, offsetY + height - y, count * boxsize, boxsize, fillColor=color) g.add(s) c += count return g
class PlotArea(Widget): "Abstract base class representing a chart's plot area, pretty unusable by itself." _attrMap = AttrMap( x = AttrMapValue(isNumber, desc='X position of the lower-left corner of the chart.'), y = AttrMapValue(isNumber, desc='Y position of the lower-left corner of the chart.'), width = AttrMapValue(isNumber, desc='Width of the chart.'), height = AttrMapValue(isNumber, desc='Height of the chart.'), strokeColor = AttrMapValue(isColorOrNone, desc='Color of the plot area border.'), strokeWidth = AttrMapValue(isNumber, desc='Width plot area border.'), fillColor = AttrMapValue(isColorOrNone, desc='Color of the plot area interior.'), background = AttrMapValue(isNoneOrShape, desc='Handle to background object e.g. Rect(0,0,width,height).'), debug = AttrMapValue(isNumber, desc='Used only for debugging.'), ) def __init__(self): self.x = 20 self.y = 10 self.height = 85 self.width = 180 self.strokeColor = None self.strokeWidth = 1 self.fillColor = None self.background = None self.debug = 0 def makeBackground(self): if self.background is not None: BG = self.background if isinstance(BG,Group): g = BG for bg in g.contents: bg.x = self.x bg.y = self.y bg.width = self.width bg.height = self.height else: g = Group() if type(BG) not in (type(()),type([])): BG=(BG,) for bg in BG: bg.x = self.x bg.y = self.y bg.width = self.width bg.height = self.height g.add(bg) return g else: strokeColor,strokeWidth,fillColor=self.strokeColor, self.strokeWidth, self.fillColor if (strokeWidth and strokeColor) or fillColor: g = Group() _3d_dy = getattr(self,'_3d_dy',None) x = self.x y = self.y h = self.height w = self.width if _3d_dy is not None: _3d_dx = self._3d_dx if fillColor and not strokeColor: from reportlab.lib.colors import Blacker c = Blacker(fillColor, getattr(self,'_3d_blacken',0.7)) else: c = strokeColor if not strokeWidth: strokeWidth = 0.5 if fillColor or strokeColor or c: bg = Polygon([x,y,x,y+h,x+_3d_dx,y+h+_3d_dy,x+w+_3d_dx,y+h+_3d_dy,x+w+_3d_dx,y+_3d_dy,x+w,y], strokeColor=strokeColor or c or grey, strokeWidth=strokeWidth, fillColor=fillColor) g.add(bg) g.add(Line(x,y,x+_3d_dx,y+_3d_dy, strokeWidth=0.5, strokeColor=c)) g.add(Line(x+_3d_dx,y+_3d_dy, x+_3d_dx,y+h+_3d_dy,strokeWidth=0.5, strokeColor=c)) fc = Blacker(c, getattr(self,'_3d_blacken',0.8)) g.add(Polygon([x,y,x+_3d_dx,y+_3d_dy,x+w+_3d_dx,y+_3d_dy,x+w,y], strokeColor=strokeColor or c or grey, strokeWidth=strokeWidth, fillColor=fc)) bg = Line(x+_3d_dx,y+_3d_dy, x+w+_3d_dx,y+_3d_dy,strokeWidth=0.5, strokeColor=c) else: bg = None else: bg = Rect(x, y, w, h, strokeColor=strokeColor, strokeWidth=strokeWidth, fillColor=fillColor) if bg: g.add(bg) return g else: return None
class LegendedHorizontalLineChart(HorizontalLineChart): """A subclass of Legend for drawing legends with lines as the swatches rather than rectangles. Useful for lineCharts and linePlots. Should be similar in all other ways the the standard Legend class. """ _attrMap = AttrMap( BASE=HorizontalLineChart, drawLegend=AttrMapValue(isBoolean, desc='If true draw legend.', advancedUsage=1), legendPositionType=AttrMapValue( OneOf( "null", "top-left", "top-mid", "top-right", "bottom-left", "bottom-mid", "bottom-right" ), desc="The position of LinLegend."), legendAdjustX=AttrMapValue(isNumber, desc='xxx.'), legendAdjustY=AttrMapValue(isNumber, desc='xxx.'), legendCategoryNames=AttrMapValue(isListOfStringsOrNone, desc='List of legend category names.'), titleMain=AttrMapValue(isString, desc='main title text.'), titleMainFontName=AttrMapValue(isString, desc='main title font name.'), titleMainFontSize=AttrMapValue(isNumberInRange(0, 100), desc='main title font size.'), titleMainFontColor=AttrMapValue(isColor, desc='main title font color.'), legendFontSize=AttrMapValue(isNumberInRange(0, 100), desc='legend text font size.'), labels_height=AttrMapValue(isNumberInRange(0, 100), desc='the max height of x-labels.') ) def __init__(self): HorizontalLineChart.__init__(self) self.drawLegend = False self.legendPositionType = "null" self.legendCategoryNames = None self.legendAdjustX = 0 self.legendAdjustY = 0 self.titleMain = "" self.titleMainFontColor = colors.gray self.titleMainFontName = DefaultFontName self.titleMainFontSize = STATE_DEFAULTS['fontSize'] self.legendFontSize = 7 self.labels_height = 0 def set_line_color(self): if self.legendCategoryNames is None: self.legendCategoryNames = [] legend_num = len(self.legendCategoryNames) data_num = len(self.data) for i in range(data_num): line = self.lines[i] line.strokeColor = ALL_COLORS[i] if i >= legend_num: self.legendCategoryNames.append("unknown") legend_num = len(self.legendCategoryNames) temp_category_names = self.legendCategoryNames[:] if legend_num >= 1: self.legendCategoryNames = [] color_name_pairs = [(0, name) for name in temp_category_names] legend_width = ChartsLegend.calc_legend_width( color_name_pairs, 10, 10, DefaultFontName, self.legendFontSize) per_legend_width = int(legend_width / legend_num) legend_num_per_row = int(self.width / per_legend_width) index = 0 row_names = [] for name in temp_category_names: row_names.append(name) index += 1 if index == legend_num_per_row: index = 0 self.legendCategoryNames.append(row_names) row_names = [] if len(row_names) > 0: self.legendCategoryNames.append(row_names) else: self.legendCategoryNames = temp_category_names def _calc_labels_size(self): max_width = 0 index = 0 for label_text in self.categoryAxis.categoryNames: tmp_width = get_string_width(label_text, self.categoryAxis.labels.fontName, self.categoryAxis.labels.fontSize) if tmp_width > max_width: max_width = tmp_width if self.categoryAxis.labels[index].angle % 90 == 0: self.categoryAxis.labels[index].dx = \ int(tmp_width * math.cos(self.categoryAxis.labels[index].angle / 180 * math.pi) / 2) - \ int(self.categoryAxis.labels.fontSize * math.sin(self.categoryAxis.labels[index].angle / 180 * math.pi) / 2) index += 1 self.labels_height = \ int(max_width * math.sin(self.categoryAxis.labels.angle / 180 * math.pi)) + \ int(self.categoryAxis.labels.fontSize * math.cos(self.categoryAxis.labels.angle / 180 * math.pi)) return self.labels_height def _adjust_positon(self): self.x = 30 if self.labels_height > 20: self.y = self.labels_height + 10 else: self.y = 30 self.width -= self.x + 30 self.height -= self.y + self.titleMainFontSize + 20 def draw(self): self._calc_labels_size() self._adjust_positon() self.set_line_color() if self.drawLegend is True: if self.legendPositionType in ["bottom-left", "bottom-mid", "bottom-right"]: row_count = len(self.legendCategoryNames) + 1 self.height -= row_count * self.legendFontSize self.y += row_count * self.legendFontSize g = HorizontalLineChart.draw(self) if self.drawLegend: legend_count = 0 for i in range(len(self.legendCategoryNames)): legend = ChartsLegend() legend.positionType = self.legendPositionType if self.legendPositionType != "null": if self.legendPositionType in ["bottom-left", "bottom-mid", "bottom-right"]: legend.backgroundRect = \ Rect(self.x, self.y + legend.bottom_gap - self.labels_height - 15 - ((i+1) * legend.fontSize), self.width, self.height) else: legend.backgroundRect = Rect(self.x, self.y - (i * legend.fontSize * 1.2), self.width, self.height) legend.adjustX = self.legendAdjustX legend.adjustY = self.legendAdjustY legend.fontSize = self.legendFontSize legend.colorNamePairs = [] for j in range(len(self.legendCategoryNames[i])): legend.colorNamePairs.append((ALL_COLORS[legend_count + j], self.legendCategoryNames[i][j])) legend_count += len(self.legendCategoryNames[i]) g.add(legend) if self.titleMain != "": title = String(0, 0, self.titleMain) title.fontSize = self.titleMainFontSize title.fontName = self.titleMainFontName title.fillColor = self.titleMainFontColor title.textAnchor = 'start' # title.x = self.x - 20 title.x = 0 title.y = self.y + self.height + 20 g.add(title) return g
class Grid(Widget): """This makes a rectangular grid of equidistant stripes. The grid contains an outer border rectangle, and stripes inside which can be drawn with lines and/or as solid tiles. The drawing order is: outer rectangle, then lines and tiles. The stripes' width is indicated as 'delta'. The sequence of stripes can have an offset named 'delta0'. Both values need to be positive! """ _attrMap = AttrMap( x=AttrMapValue(isNumber, desc="The grid's lower-left x position."), y=AttrMapValue(isNumber, desc="The grid's lower-left y position."), width=AttrMapValue(isNumber, desc="The grid's width."), height=AttrMapValue(isNumber, desc="The grid's height."), orientation=AttrMapValue( OneOf(('vertical', 'horizontal')), desc='Determines if stripes are vertical or horizontal.'), useLines=AttrMapValue( OneOf((0, 1)), desc='Determines if stripes are drawn with lines.'), useRects=AttrMapValue( OneOf((0, 1)), desc='Determines if stripes are drawn with solid rectangles.'), delta=AttrMapValue(isNumber, desc='Determines the width/height of the stripes.'), delta0=AttrMapValue( isNumber, desc='Determines the stripes initial width/height offset.'), deltaSteps=AttrMapValue(isListOfNumbers, desc='List of deltas to be used cyclically.'), stripeColors=AttrMapValue( isListOfColors, desc='Colors applied cyclically in the right or upper direction.'), fillColor=AttrMapValue(isColorOrNone, desc='Background color for entire rectangle.'), strokeColor=AttrMapValue(isColorOrNone, desc='Color used for lines.'), strokeWidth=AttrMapValue(isNumber, desc='Width used for lines.'), rectStrokeColor=AttrMapValue(isColorOrNone, desc='Color for outer rect stroke.'), rectStrokeWidth=AttrMapValue(isNumberOrNone, desc='Width for outer rect stroke.'), ) def __init__(self): self.x = 0 self.y = 0 self.width = 100 self.height = 100 self.orientation = 'vertical' self.useLines = 0 self.useRects = 1 self.delta = 20 self.delta0 = 0 self.deltaSteps = [] self.fillColor = colors.white self.stripeColors = [colors.red, colors.green, colors.blue] self.strokeColor = colors.black self.strokeWidth = 2 def demo(self): D = Drawing(100, 100) g = Grid() D.add(g) return D def makeOuterRect(self): strokeColor = getattr(self, 'rectStrokeColor', self.strokeColor) strokeWidth = getattr(self, 'rectStrokeWidth', self.strokeWidth) if self.fillColor or (strokeColor and strokeWidth): rect = Rect(self.x, self.y, self.width, self.height) rect.fillColor = self.fillColor rect.strokeColor = strokeColor rect.strokeWidth = strokeWidth return rect else: return None def makeLinePosList(self, start, isX=0): "Returns a list of positions where to place lines." w, h = self.width, self.height if isX: length = w else: length = h if self.deltaSteps: r = [start + self.delta0] i = 0 while 1: if r[-1] > start + length: del r[-1] break r.append(r[-1] + self.deltaSteps[i % len(self.deltaSteps)]) i = i + 1 else: r = frange(start + self.delta0, start + length, self.delta) r.append(start + length) if self.delta0 != 0: r.insert(0, start) #print 'Grid.makeLinePosList() -> %s' % r return r def makeInnerLines(self): # inner grid lines group = Group() w, h = self.width, self.height if self.useLines == 1: if self.orientation == 'vertical': r = self.makeLinePosList(self.x, isX=1) for x in r: line = Line(x, self.y, x, self.y + h) line.strokeColor = self.strokeColor line.strokeWidth = self.strokeWidth group.add(line) elif self.orientation == 'horizontal': r = self.makeLinePosList(self.y, isX=0) for y in r: line = Line(self.x, y, self.x + w, y) line.strokeColor = self.strokeColor line.strokeWidth = self.strokeWidth group.add(line) return group def makeInnerTiles(self): # inner grid lines group = Group() w, h = self.width, self.height # inner grid stripes (solid rectangles) if self.useRects == 1: cols = self.stripeColors if self.orientation == 'vertical': r = self.makeLinePosList(self.x, isX=1) elif self.orientation == 'horizontal': r = self.makeLinePosList(self.y, isX=0) dist = makeDistancesList(r) i = 0 for j in range(len(dist)): if self.orientation == 'vertical': x = r[j] stripe = Rect(x, self.y, dist[j], h) elif self.orientation == 'horizontal': y = r[j] stripe = Rect(self.x, y, w, dist[j]) stripe.fillColor = cols[i % len(cols)] stripe.strokeColor = None group.add(stripe) i = i + 1 return group def draw(self): # general widget bits group = Group() group.add(self.makeOuterRect()) group.add(self.makeInnerTiles()) group.add(self.makeInnerLines(), name='_gridLines') return group
class ShadedRect(Widget): """This makes a rectangle with shaded colors between two colors. Colors are interpolated linearly between 'fillColorStart' and 'fillColorEnd', both of which appear at the margins. If 'numShades' is set to one, though, only 'fillColorStart' is used. """ _attrMap = AttrMap( x=AttrMapValue(isNumber, desc="The grid's lower-left x position."), y=AttrMapValue(isNumber, desc="The grid's lower-left y position."), width=AttrMapValue(isNumber, desc="The grid's width."), height=AttrMapValue(isNumber, desc="The grid's height."), orientation=AttrMapValue( OneOf(('vertical', 'horizontal')), desc='Determines if stripes are vertical or horizontal.'), numShades=AttrMapValue(isNumber, desc='The number of interpolating colors.'), fillColorStart=AttrMapValue(isColorOrNone, desc='Start value of the color shade.'), fillColorEnd=AttrMapValue(isColorOrNone, desc='End value of the color shade.'), strokeColor=AttrMapValue(isColorOrNone, desc='Color used for border line.'), strokeWidth=AttrMapValue(isNumber, desc='Width used for lines.'), cylinderMode=AttrMapValue(isBoolean, desc='True if shading reverses in middle.'), ) def __init__(self, **kw): self.x = 0 self.y = 0 self.width = 100 self.height = 100 self.orientation = 'vertical' self.numShades = 20 self.fillColorStart = colors.pink self.fillColorEnd = colors.black self.strokeColor = colors.black self.strokeWidth = 2 self.cylinderMode = 0 self.setProperties(kw) def demo(self): D = Drawing(100, 100) g = ShadedRect() D.add(g) return D def _flipRectCorners(self): "Flip rectangle's corners if width or height is negative." x, y, width, height, fillColorStart, fillColorEnd = self.x, self.y, self.width, self.height, self.fillColorStart, self.fillColorEnd if width < 0 and height > 0: x = x + width width = -width if self.orientation == 'vertical': fillColorStart, fillColorEnd = fillColorEnd, fillColorStart elif height < 0 and width > 0: y = y + height height = -height if self.orientation == 'horizontal': fillColorStart, fillColorEnd = fillColorEnd, fillColorStart elif height < 0 and height < 0: x = x + width width = -width y = y + height height = -height return x, y, width, height, fillColorStart, fillColorEnd def draw(self): # general widget bits group = Group() x, y, w, h, c0, c1 = self._flipRectCorners() numShades = self.numShades if self.cylinderMode: if not numShades % 2: numShades = numShades + 1 halfNumShades = (numShades - 1) / 2 + 1 num = float(numShades) # must make it float! vertical = self.orientation == 'vertical' if vertical: if numShades == 1: V = [x] else: V = frange(x, x + w, w / num) else: if numShades == 1: V = [y] else: V = frange(y, y + h, h / num) for v in V: stripe = vertical and Rect(v, y, w / num, h) or Rect( x, v, w, h / num) if self.cylinderMode: if V.index(v) >= halfNumShades: col = colors.linearlyInterpolatedColor( c1, c0, V[halfNumShades], V[-1], v) else: col = colors.linearlyInterpolatedColor( c0, c1, V[0], V[halfNumShades], v) else: col = colors.linearlyInterpolatedColor(c0, c1, V[0], V[-1], v) stripe.fillColor = col stripe.strokeColor = col stripe.strokeWidth = 1 group.add(stripe) if self.strokeColor and self.strokeWidth >= 0: rect = Rect(x, y, w, h) rect.strokeColor = self.strokeColor rect.strokeWidth = self.strokeWidth rect.fillColor = None group.add(rect) return group
class DoubleGrid(Widget): """This combines two ordinary Grid objects orthogonal to each other. """ _attrMap = AttrMap( x=AttrMapValue(isNumber, desc="The grid's lower-left x position."), y=AttrMapValue(isNumber, desc="The grid's lower-left y position."), width=AttrMapValue(isNumber, desc="The grid's width."), height=AttrMapValue(isNumber, desc="The grid's height."), grid0=AttrMapValue(None, desc="The first grid component."), grid1=AttrMapValue(None, desc="The second grid component."), ) def __init__(self): self.x = 0 self.y = 0 self.width = 100 self.height = 100 g0 = Grid() g0.x = self.x g0.y = self.y g0.width = self.width g0.height = self.height g0.orientation = 'vertical' g0.useLines = 1 g0.useRects = 0 g0.delta = 20 g0.delta0 = 0 g0.deltaSteps = [] g0.fillColor = colors.white g0.stripeColors = [colors.red, colors.green, colors.blue] g0.strokeColor = colors.black g0.strokeWidth = 1 g1 = Grid() g1.x = self.x g1.y = self.y g1.width = self.width g1.height = self.height g1.orientation = 'horizontal' g1.useLines = 1 g1.useRects = 0 g1.delta = 20 g1.delta0 = 0 g1.deltaSteps = [] g1.fillColor = colors.white g1.stripeColors = [colors.red, colors.green, colors.blue] g1.strokeColor = colors.black g1.strokeWidth = 1 self.grid0 = g0 self.grid1 = g1 ## # This gives an AttributeError: ## # DoubleGrid instance has no attribute 'grid0' ## def __setattr__(self, name, value): ## if name in ('x', 'y', 'width', 'height'): ## setattr(self.grid0, name, value) ## setattr(self.grid1, name, value) def demo(self): D = Drawing(100, 100) g = DoubleGrid() D.add(g) return D def draw(self): group = Group() g0, g1 = self.grid0, self.grid1 # Order groups to make sure both v and h lines # are visible (works only when there is only # one kind of stripes, v or h). G = g0.useRects == 1 and g1.useRects == 0 and (g0, g1) or (g1, g0) for g in G: group.add(g.makeOuterRect()) for g in G: group.add(g.makeInnerTiles()) group.add(g.makeInnerLines(), name='_gridLines') return group
class ChartsLegend(LineLegend): """A subclass of Legend for drawing legends with lines as the swatches rather than rectangles. Useful for lineCharts and linePlots. Should be similar in all other ways the the standard Legend class. """ _attrMap = AttrMap( BASE=LineLegend, positionType=AttrMapValue(OneOf("null", "top-left", "top-mid", "top-right", "bottom-left", "bottom-mid", "bottom-right", "right"), desc="The position of LinLegend."), backgroundRect=AttrMapValue(None, desc="The position of LinLegend."), adjustX=AttrMapValue(isNumber, desc='xxx.'), adjustY=AttrMapValue(isNumber, desc='xxx.'), bottom_gap=AttrMapValue(isNumber, desc='xxx.')) def __init__(self): LineLegend.__init__(self) self.positionType = "null" self.backgroundRect = None self.adjustX = 0 self.adjustY = 0 self.fontName = DefaultFontName self.deltax = 10 self.deltay = 0 self.boxAnchor = 'w' self.columnMaximum = 1 self.yGap = 0 self.fontSize = 7 self.alignment = 'right' self.dxTextSpace = 5 self.bottom_gap = 40 @staticmethod def calc_legend_width(color_name_pairs, dx, deltax, font_name, font_size, sub_cols=None): pairs_num = len(color_name_pairs) max_text_width = 0 x_width = 0 for x in color_name_pairs: if type(x[1]) is tuple: for str_i in x[1]: tmp_width = stringWidth(str(str_i), font_name, font_size) if sub_cols is not None and tmp_width < sub_cols[ 0].minWidth: tmp_width = sub_cols[0].minWidth x_width += tmp_width else: str_x = x[1] x_width = stringWidth(str_x, font_name, font_size) if x_width > max_text_width: max_text_width = x_width total_text_width = (pairs_num - 1) * max_text_width + x_width legend_width = total_text_width + (dx * pairs_num) + (deltax * pairs_num) return legend_width def draw(self): legend_width = self.calc_legend_width(self.colorNamePairs, self.dx, self.deltax, self.fontName, self.fontSize, self.subCols) if self.positionType != "null" and self.backgroundRect is not None: if self.positionType == "top-left": self.x = self.backgroundRect.x self.y = self.backgroundRect.y + self.backgroundRect.height elif self.positionType == "top-mid": self.x = self.backgroundRect.x + int( self.backgroundRect.width / 2) - int(legend_width / 2) self.y = self.backgroundRect.y + self.backgroundRect.height elif self.positionType == "top-right": self.x = self.backgroundRect.x + self.backgroundRect.width - legend_width self.y = self.backgroundRect.y + self.backgroundRect.height elif self.positionType == "bottom-left": self.x = self.backgroundRect.x self.y = self.backgroundRect.y - self.bottom_gap elif self.positionType == "bottom-mid": self.x = self.backgroundRect.x + int( self.backgroundRect.width / 2) - int(legend_width / 2) self.y = self.backgroundRect.y - self.bottom_gap elif self.positionType == "bottom-right": self.x = self.backgroundRect.x + self.backgroundRect.width - legend_width self.y = self.backgroundRect.y - self.bottom_gap elif self.positionType == "right": self.x = self.backgroundRect.x + self.backgroundRect.width + 10 self.y = self.backgroundRect.y + self.backgroundRect.height self.x += self.adjustX self.y += self.adjustY return LineLegend.draw(self)