"Horizontal bar chart with complex labelling" #Autogenerated by ReportLab guiedit do not edit from reportlab.graphics.shapes import Group, Line, Drawing, _DrawingEditorMixin from reportlab.graphics.charts.barcharts import HorizontalBarChart from reportlab.graphics.charts.legends import Legend from reportlab.lib.colors import PCMYKColor, black, white, rgb2cmyk, pink, toColor from reportlab.graphics.charts.textlabels import Label, XLabel class RegionSectorChart(_DrawingEditorMixin,Drawing): def __init__(self,width=400,height=200,*args,**kw): Drawing.__init__(self,width,height,*args,**kw) self._add(self,HorizontalBarChart(),name='chart',validate=None,desc=None) #directly placed annotations self._add(self,Group(),name='A',validate=None,desc=None) self.chart.data = [(100, 110, 120, 130)] self.width = 660 self.height = 295 textWidth = 120 numberWidth = 30 footnoteHeight = 40 legendHeight = 20 self.chart.width = 340 self.chart.y = footnoteHeight self.chart.x = textWidth + numberWidth self.chart.height = self.height - legendHeight - footnoteHeight self.chart.categoryAxis.visibleLabels = 0 self.chart.categoryAxis.visible = 1 self.chart.categoryAxis.visibleTicks = 0 self.chart.categoryAxis.strokeColor = PCMYKColor(0,0,0,30) self.chart.categoryAxis.strokeWidth = 1.5 self.chart.categoryAxis.labelAxisMode='low' self.chart.valueAxis.visible = 0 self.chart.valueAxis.forceZero = 1 self.chart.bars.strokeColor = None unknownColor = toColor(rgb2cmyk(*(pink.rgb()))) self.__dict__.update(dict(lightBlue=PCMYKColor(15,3,2,0),darkBlue=PCMYKColor(100,69,0,60))) self._colorMap = lambda n: getattr(self,n,unknownColor) #this is sample data in intermediate form self._category_values = [19.2, 16.5, 15.8, 14.6, 12.5, 10.6, 8.3, 7.9, 7.2, 6.4, 5.7] self._category_names = ('Utilities', 'Consumer Staples', 'Health Care', 'Communication Services', 'Real Estate', 'Information Technology', 'Materials', 'Consumer Discretionary', 'Financials', 'Industrials', 'Energy') self._category_group_names = ('Cyclical', 'Cyclical', 'Cyclical', 'Cyclical', 'Cyclical', 'Cyclical', 'Defensive', 'Cyclical', 'Defensive', 'Defensive', 'Defensive') self._category_group_colors = ['darkBlue', 'darkBlue', 'darkBlue', 'darkBlue', 'darkBlue', 'darkBlue', 'lightBlue', 'darkBlue', 'lightBlue', 'lightBlue', 'lightBlue'] self._legend_data = ('Cyclical', 'darkBlue', 'Defensive', 'lightBlue') self._section_labels = ('Top 3 Industry Groups', 'Bottom 3 Industry Groups') self._contrib_values = (29.6, 27.4, 22.7) self._contrib_names = ('Transportation', 'Consumer Durables', 'Semiconductors') self._contrib_colors = ('darkBlue', 'darkBlue', 'darkBlue') self._contrib_owners = ('Industrials', 'Consumer Discretionary', 'Information Technology') self._contrib_owner_positions = (2, 4, 6) self._detract_values = (4.1, 4.0, 1.2) self._detract_names = ('Software', 'Pharmaceuticals', 'Household') self._detract_colors = ('darkBlue', 'lightBlue', 'lightBlue') self._detract_owners = ('Information Technology', 'Health Care', 'Consumer Staples') self._detract_owner_positions = (6, 9, 10) self._footnote = ('Past performance is no guarantee of future results.',) self._sort = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] #debug = 1 #self._dbgprt = lambda *args,**kwds:(print(*args,**kwds) if debug else None) #self._dbg = lambda n:(self._dbgprt('%s = %r' % (n,getattr(self,n,None))) if debug else None) self._row_zero_vars = '''_legend_data _section_labels _contrib_values _contrib_names _contrib_colors _contrib_owners _contrib_owner_positions _detract_values _detract_names _detract_colors _detract_owners _detract_owner_positions _footnote'''.split() self._fix_nulls = lambda V: tuple(((None if v=='NULL' else v) for v in V)) if isinstance(V,(tuple,list)) else V self._fix_row_zero_vars = lambda v: self._fix_nulls(v[0]) if isinstance(v,list) else v def getContents(self): #this is the additional group list A = self.A.contents aA = A.append del A[:] #delete any existing content for n in self._row_zero_vars: setattr(self,n,self._fix_row_zero_vars(getattr(self,n,None))) for n in '_category_values _category_names _category_group_names _category_group_colors'.split(): setattr(self,n,tuple(reversed(getattr(self,n)))) self._category_group_names = self._fix_nulls(getattr(self,'_category_group_names',None)) #for n in '_category_values _category_names _category_group_names _category_group_colors _legend_data _section_labels _contrib_values _contrib_names _contrib_colors _contrib_owners _contrib_owner_positions _detract_values _detract_names _detract_colors _detract_owners _detract_owner_positions _footnote _sort'.split(): self._dbg(n) chart = self.chart chart.data = [self._category_values] for i,c in enumerate(self._category_group_colors): chart.bars[0,i].fillColor = self._colorMap(c) legendData = self._legend_data if None in legendData: legendData = None if legendData: leg = Legend() if legendData: legAttrs = dict(x=2,y=self.height-2,boxAnchor='nw',colorNamePairs=[(self._colorMap(legendData[i+1]),legendData[i]) for i in (0,2)],dx=6,dy=6,strokeWidth=0,strokeColor=None,fontName='Helvetica',fontSize=10,alignment='right',dxTextSpace=6,columnMaximum=1,variColumn=1) if legendData: leg.__dict__.update(legAttrs) if legendData: aA(leg) chart._computeBarPositions() BY = chart._barPositions[0] BX0 = min((min(v[0],v[0]+v[2]) for v in BY)) #lowest x BX = [max(v[0],v[0]+v[2]) for v in BY] #max in case width negative BY = [v[1]+0.5*v[3] for v in BY] #self._dbgprt('BX0=%r\nBX=%r\nBY=%r' % (BX0,BX,BY)) for i,v in enumerate(self._category_values): aA(Label(_text='%.1f' % v, x=BX0-2, y=BY[i], boxAnchor='e', textAnchor='end', fontName='Helvetica-Bold', fontSize=10, fillColor=black)) for i,t in enumerate(self._category_names): aA(Label(_text=t, x=2, y=BY[i], boxAnchor='w', textAnchor='start', fontName='Helvetica', fontSize=10, fillColor=black)) x0 = chart.x+chart.width + 12 x1 = self.width - 2 w = 130 - 12 pos = lambda i: len(self._category_values)-i #draw the contributions top down CY = [chart.y+chart.height + 2 - 28*i for i in range(4)] aA(XLabel(x=x0,y=CY[0],maxWidth=w+8, boxAnchor='nw',fontName='Helvetica-Bold', fontSize=10,_text=self._section_labels[0])) for i in range(3): aA(Label(x=x0,y=CY[i+1], boxAnchor='nw', fontName='ZapfDingbats', fontSize=10,_text='\u25b2', fillColor=self._colorMap(self._contrib_colors[i]))) for i in range(3): aA(XLabel(x=x0+10,y=CY[i+1],maxWidth=w, boxAnchor='nw',fontName='Helvetica', fontSize=10,_text=self._contrib_names[i])) for i in range(3): aA(Label(x=x1,y=CY[i+1], boxAnchor='ne', textAnchor='end', fontName='Helvetica-Bold', fontSize=10,_text='%.1f' % self._contrib_values[i])) for i in range(3): aA(Line(x1=x0-1,y1=CY[i+1]-6,x2=BX[pos(self._contrib_owner_positions[i])],y2=BY[pos(self._contrib_owner_positions[i])], strokeWidth=1.5,strokeColor=PCMYKColor(0,0,0,30))) #draw detractions topdown DY = list(reversed([chart.y + 20 - 2 + 28*i for i in range(4)])) aA(XLabel(x=x0,y=DY[0],maxWidth=w+8, boxAnchor='nw',fontName='Helvetica-Bold', fontSize=10,_text=self._section_labels[1])) for i in range(3): aA(Label(x=x0,y=DY[i+1], boxAnchor='nw', fontName='ZapfDingbats', fontSize=10,_text='\u25bc', fillColor=self._colorMap(self._detract_colors[i]))) for i in range(3): aA(XLabel(x=x0+10,y=DY[i+1],maxWidth=w, boxAnchor='nw',fontName='Helvetica', fontSize=10,_text=self._detract_names[i])) for i in range(3): aA(Label(x=x1,y=DY[i+1], boxAnchor='ne', textAnchor='end', fontName='Helvetica-Bold', fontSize=10,_text='%.1f' % self._detract_values[i])) for i in range(3): aA(Line(x1=x0-1,y1=DY[i+1]-6,x2=BX[pos(self._detract_owner_positions[i])],y2=BY[pos(self._detract_owner_positions[i])], strokeWidth=1.5,strokeColor=PCMYKColor(0,0,0,30))) #mid horizontal line ymid = chart.y + 0.5*chart.height aA(Line(x1=x0+2,y1=ymid,x2=self.width-2,y2=ymid, strokeWidth=1.5,strokeColor=PCMYKColor(0,0,0,30))) aA(XLabel(x=2,y=chart.y - 10,maxWidth=self.width-2,boxAnchor='nw',fontName='Helvetica', fontSize=10,_text=self._footnote[0])) return Drawing.getContents(self) if __name__=="__main__": #NORUNTESTS RegionSectorChart().go()