def drawtext(self, *texts, **kwargs): """ Draw TLaTeX text in the corner. drawtext(str text) drawtext(str text, str text, ...) drawtext(list texts) Text position can be controlled in several ways drawlegend(text,position=position) where position is a string which can contain the horizontal position, e.g. 'left', 'center', 'right', 'L', 'C', or 'R' The position string can also contain the vertical position as e.g. 'top', 'middle', 'bottom', 'T', 'M', or 'B' Instead of the strings, the exact coordinates can be controlled with the keywords x and y: drawtext(x=0.2,y=0.8) These floats are normalized to the axis frame, ignoring the canvas margins: x=0 is the left, x=1 is the right, y=0 is the bottom and y=1 is the top side. Values less than 0, or larger than 1, will put the text outside the frame. """ verbosity = LOG.getverbosity(self, kwargs) position = kwargs.get('pos', 'topleft') position = kwargs.get('position', position) #.lower() tsize = kwargs.get('tsize', _lsize) # text size theight = kwargs.get('theight', None) or 1 bold = kwargs.get('bold', False) # bold text dolatex = kwargs.get('latex', True) # automatically format strings as LaTeX xuser = kwargs.get('x', None) # horizontal position yuser = kwargs.get('y', None) # vertical position align_user = kwargs.get('align', None) # text line panel = kwargs.get('panel', 1) # panel (top=1, bottom=2) texts = unwraplistargs(texts) if not any(t != "" for t in texts): return None # CHECK LOG.insist(self.canvas, "Canvas does not exist!") self.canvas.cd(panel) scale = 485. / min(gPad.GetWh() * gPad.GetHNDC(), gPad.GetWw() * gPad.GetWNDC()) tsize *= scale # text size # POSITION font = 62 if bold else 42 align = 13 position = position.replace('left', 'L').replace( 'center', 'C').replace('right', 'R').replace( #.lower() 'top', 'T').replace('middle', 'M').replace('bottom', 'B') if 'R' in position: x, align = 0.95, 30 # right if 'C' in position: x, align = 0.50, 20 # center else: x, align = 0.05, 10 # left if 'B' in position: y = 0.05 align += 1 # bottom if 'M' in position: y = 0.50 align += 2 # middle else: y = 0.95 align += 3 # top #x1 = float(re.findall(r"x=(\d\.\d+)",position)[0]) #y2 = float(re.findall(r"y=(\d\.\d+)",position)[0]); if xuser != None: x = xuser if yuser != None: y = yuser if align_user != None: align = align_user L, R = gPad.GetLeftMargin(), gPad.GetRightMargin() T, B = gPad.GetTopMargin(), gPad.GetBottomMargin() x = L + (1 - L - R) * x # convert frame to canvas coordinates y = B + (1 - T - B) * y # convert frame to canvas coordinates # LATEX latex = TLatex() latex.SetTextSize(tsize) latex.SetTextAlign(align) latex.SetTextFont(font) #latex.SetTextColor(kRed) latex.SetNDC(True) for i, line in enumerate(texts): if dolatex: line = maketitle(line) yline = y - i * theight * 1.2 * tsize latex.DrawLatex(x, yline, line) LOG.verb( "Plot.drawcornertext: i=%d, x=%.2f, y=%.2f, text=%r" % (i, x, yline, line), verbosity, 2) self.texts.append(latex) return latex
def setaxes(self, *args, **kwargs): """Make axis.""" verbosity = LOG.getverbosity(self, kwargs) hists = [] binning = [] for arg in args[:]: if hasattr(arg, 'GetXaxis'): hists.append(arg) elif isinstance(arg, Ratio): hists.append(arg.frame) elif isnumber(arg): binning.append(arg) if not hists: LOG.warning( "Plot.setaxes: No objects (TH1, TGraph, ...) given in args %s to set axis..." % (args)) return 0, 0, 100, 100 frame = hists[0] if len(binning) >= 2: xmin, xmax = binning[:2] else: xmin, xmax = frame.GetXaxis().GetXmin(), frame.GetXaxis().GetXmax() nbins = frame.GetXaxis().GetNbins() binwidth = float(xmax - xmin) / nbins xmin = kwargs.get('xmin', xmin) xmax = kwargs.get('xmax', xmax) ymin = kwargs.get('ymin', None) ymax = kwargs.get('ymax', None) ratiorange = kwargs.get('rrange', None) binlabels = kwargs.get('binlabels', None) intbins = kwargs.get('intbins', True) # allow integer binning logx = kwargs.get('logx', False) logy = kwargs.get('logy', False) ymargin = kwargs.get('ymargin', None) or ( 1.3 if logy else 1.2) # margin between hist maximum and plot's top logyrange = kwargs.get( 'logyrange', None) or 3 # log(y) range from hist maximum to ymin negativey = kwargs.get('negativey', True) # allow negative y values xtitle = kwargs.get('xtitle', frame.GetTitle()) ytitle = kwargs.get('ytitle', None) latex = kwargs.get('latex', True) # automatically format strings as LaTeX grid = kwargs.get('grid', False) ycenter = kwargs.get('center', False) nxdivisions = kwargs.get('nxdiv', 510) nydivisions = kwargs.get('nydiv', 510) main = kwargs.get('main', False) # main panel of ratio plot scale = 600. / min(gPad.GetWh() * gPad.GetHNDC(), gPad.GetWw() * gPad.GetWNDC()) xtitlesize = kwargs.get('xtitlesize', _tsize) * scale ytitlesize = kwargs.get('ytitlesize', _tsize) * scale xlabelsize = kwargs.get('xlabelsize', _lsize) * scale ylabelsize = kwargs.get('ylabelsize', _lsize) * scale ytitleoffset = kwargs.get('ytitleoffset', 1.0) * 1.26 / scale xtitleoffset = kwargs.get('xtitleoffset', 1.0) * 1.00 xlabeloffset = kwargs.get('xlabeloffset', 0.007) if main: xtitlesize = 0.0 xlabelsize = 0.0 if latex: xtitle = makelatex(xtitle) LOG.verb("Plot.setaxes: Binning (%s,%.1f,%.1f)" % (nbins, xmin, xmax), verbosity, 2) if ratiorange: ymin, ymax = 1 - ratiorange, 1 + ratiorange if intbins and nbins < 15 and int(xmin) == xmin and int( xmax) == xmax and binwidth == 1: LOG.verb( "Plot.setaxes: Setting integer binning for (%r,%s,%d,%d)!" % (xtitle, nbins, xmin, xmax), verbosity, 1) binlabels = [str(i) for i in range(int(xmin), int(xmax) + 1)] xlabelsize *= 1.6 xlabeloffset *= 0.88 * scale if logy: ylabelsize *= 1.08 if binlabels: nxdivisions = 15 # GET HIST MAX hmaxs = [] hmins = [0] if isinstance(frame, THStack): hmaxs.append(frame.GetMaximum()) #frame = frame.GetStack().Last() for hist in hists: hmaxs.append( getTGraphYRange(hist)[1] if isinstance(hist, TGraph) else hist.GetMaximum()) else: for hist in hists: ymin1, ymax1 = getTGraphYRange(hist) if isinstance( hist, TGraph) else hist.GetMinimum(), hist.GetMaximum() if negativey: hmins.append(ymin1) hmaxs.append(ymax1) if ymin == None: ymin = min(hmins) * (1.1 if ymin > 0 else 0.9) hmax = max(hmaxs) hmin = min(hmins) # SET AXES RANGES if ymin == None: ymin = 0 if logy: if not ymin or ymin <= 0: # avoid zero or negative ymin for log plots ymin = 10**(magnitude(hmax) - logyrange ) #max(0.1,10**(magnitude(ymax)-3)) LOG.verb( "Plot.setaxes: logy=%s, hmax=%6.6g, magnitude(hmax)=%s, logyrange=%s, ymin=%.6g" % (logy, hmax, magnitude(hmax), logyrange, ymin), verbosity, 2) if ymax == None: if hmax > ymin > 0: span = abs(log10(hmax / ymin)) * ymargin ymax = ymin * (10**span) LOG.verb( "Plot.setaxes: log10(hmax/ymin)=%6.6g, span=%6.6g, ymax=%.6g" % (log10(hmax / ymin), span, ymax), verbosity, 2) else: ymax = hmax * ymargin gPad.Update() gPad.SetLogy() elif ymax == None: ymax = hmax * ymargin if logx: if not xmin: xmin = 0.1 xmax *= 0.9999999999999 gPad.Update() gPad.SetLogx() if grid: gPad.SetGrid() frame.GetXaxis().SetRangeUser(xmin, xmax) frame.SetMinimum(ymin) frame.SetMaximum(ymax) if ytitle == None: #ytitle = "Events" if "multiplicity" in xtitle.lower(): ytitle = "Events" elif hmax < 1.: ytitle = "A.U." else: binwidth = frame.GetXaxis().GetBinWidth(0) binwidstr = ("%.3f" % binwidth).rstrip('0').rstrip('.') units = re.findall(r' \[(.+)\]', xtitle) #+ re.findall(r' (.+)',xtitle) if frame.GetXaxis().IsVariableBinSize(): if units: ytitle = "Events / " + units[-1] else: ytitle = "Events / bin size" elif units: if binwidth != 1: ytitle = "Events / %s %s" % (binwidstr, units[-1]) else: ytitle = "Events / " + units[-1] elif binwidth != 1: ytitle = "Events / " + binwidstr else: ytitle = "Events" LOG.verb( "Plot.setaxes: ytitle=%r, units=%s, binwidth=%s, binwidstr=%r" % (ytitle, units, binwidth, binwidstr), verbosity, 2) # alphanumerical bin labels if binlabels: if len(binlabels) < nbins: LOG.warning("Plot.setaxes: len(binlabels)=%d < %d=nbins" % (len(binlabels), nbins)) for i, binlabel in zip(range(1, nbins + 1), binlabels): frame.GetXaxis().SetBinLabel(i, binlabel) #frame.GetXaxis().LabelsOption('h') # X axis frame.GetXaxis().SetTitleSize(xtitlesize) frame.GetXaxis().SetTitleOffset(xtitleoffset) frame.GetXaxis().SetLabelSize(xlabelsize) frame.GetXaxis().SetLabelOffset(xlabeloffset) frame.GetXaxis().SetNdivisions(nxdivisions) frame.GetXaxis().SetTitle(xtitle) # Y axis if ymax >= 1e4: ylabelsize *= 0.95 if ycenter: frame.GetYaxis().CenterTitle(True) frame.GetYaxis().SetTitleSize(ytitlesize) frame.GetYaxis().SetTitleOffset(ytitleoffset) frame.GetYaxis().SetLabelSize(ylabelsize) frame.GetYaxis().SetNdivisions(nydivisions) frame.GetYaxis().SetTitle(ytitle) if verbosity >= 1: print ">>> Plot.setaxes: xtitle=%r, [hmin,hmax] = [%.6g,%.6g], [xmin,xmax] = [%.6g,%.6g], [ymin,ymax] = [%.6g,%.6g]" % ( xtitle, hmin, hmax, xmin, xmax, ymin, ymax) elif verbosity >= 2: print ">>> Plot.setaxes: frame=%s" % (frame) print ">>> Plot.setaxes: hists=%s" % (hists) print ">>> Plot.setaxes: [hmin,hmax] = [%.6g,%.6g], [xmin,xmax] = [%.6g,%.6g], [ymin,ymax] = [%.6g,%.6g]" % ( hmin, hmax, xmin, xmax, ymin, ymax) print ">>> Plot.setaxes: xtitlesize=%4.4g, xlabelsize=%4.4g, xtitleoffset=%4.4g, xtitle=%r" % ( xtitlesize, xlabelsize, xtitleoffset, xtitle) print ">>> Plot.setaxes: ytitlesize=%4.4g, ylabelsize=%4.4g, ytitleoffset=%4.4g, ytitle=%r" % ( ytitlesize, ylabelsize, ytitleoffset, ytitle) print ">>> Plot.setaxes: scale=%4.4g, nxdivisions=%s, nydivisions=%s, ymargin=%.3f, logyrange=%.3f" % ( scale, nxdivisions, nydivisions, ymargin, logyrange) if main: #if any(a!=None and a!=b for a, b in [(self.xmin,xmin),(self.xmax,xmax)]): # LOG.warning("Plot.setaxes: x axis range changed: [xmin,xmax] = [%6.6g,%6.6g] -> [%6.6g,%6.6g]"%( # self.xmin,self.xmax,xmin,xmax)) #if any(a!=None and a!=b for a, b in [(self.ymin,ymin),(self.ymax,ymax)]): # LOG.warning("Plot.setaxes: y axis range changed: [ymin,ymax] = [%6.6g,%6.6g] -> [%6.6g,%6.6g]"%( # self.ymin,self.ymax,ymin,ymax)) self.xmin, self.xmax = xmin, xmax self.ymin, self.ymax = ymin, ymax return xmin, xmax, ymin, ymax
def drawlegend(self, position=None, **kwargs): """Create and draw legend. Legend position can be controlled in several ways drawlegend(position) drawlegend(position=position) where position is a string which can contain the horizontal position, e.g. 'left', 'center', 'right', 'L', 'C', 'R', 'x=0.3', ... where 'x' is the position (between 0 an 1) of the left side in the frame. The position string can also contain the vertical position as e.g. 'top', 'middle', 'bottom', 'T', 'M', 'B', 'y=0.3', ... where 'y' is the position (between 0 an 1) of the top side in the frame. Instead of the strings, the exact legend coordinates can be controlled with the keywords x1, x2, y1 and y2, or, x1, y1, width and height: drawlegend(x1=0.2,width=0.4) drawlegend(x1=0.2,width=0.4,y1=0.9,height=0.4) drawlegend(x1=0.2,x2=0.6,y1=0.9,y2=0.4) These floats are normalized to the axis frame, ignoring the canvas margins: x=0 is the left, x=1 is the right, y=0 is the bottom and y=1 is the top side. Values less than 0, or larger than 1, will put the legend outside the frame. """ #if not ratio: # tsize *= 0.80 # signaltsize *= 0.80 verbosity = LOG.getverbosity(self, kwargs) hists = self.hists errstyle = 'lep' if gStyle.GetErrorX() else 'ep' entries = kwargs.get('entries', []) bands = kwargs.get('band', [self.errband]) # error bands bands = ensurelist(bands, nonzero=True) bandentries = kwargs.get('bandentries', []) title = kwargs.get('header', None) title = kwargs.get('title', title) # legend header/title latex = kwargs.get('latex', True) # automatically format strings as LaTeX style = kwargs.get('style', None) style0 = kwargs.get('style0', None) # style of first histogram errstyle = kwargs.get('errstyle', errstyle) # style for an error point styles = kwargs.get('styles', []) position = kwargs.get('pos', position) # legend position position = kwargs.get('position', position) or self.position option = kwargs.get('option', '') border = kwargs.get('border', False) transparent = kwargs.get('transparent', True) x1_user = kwargs.get('x1', None) # legend left side x2_user = kwargs.get('x2', None) # legend right side y1_user = kwargs.get('y1', None) # legend top side y2_user = kwargs.get('y2', None) # legend bottom side width = kwargs.get('width', -1) # legend width height = kwargs.get('height', -1) # legend height tsize = kwargs.get('tsize', _lsize) # text size twidth = kwargs.get('twidth', None) or 1 # scalefactor for legend width theight = kwargs.get('theight', None) or 1 # scalefactor for legend height texts = kwargs.get('text', []) # extra text below legend ncols = kwargs.get('ncols', self.ncols) or 1 # number of legend columns colsep = kwargs.get('colsep', 0.06) # seperation between legend columns bold = kwargs.get('bold', True) # bold legend header panel = kwargs.get('panel', 1) # panel (top=1, bottom=2) texts = ensurelist(texts, nonzero=True) entries = ensurelist(entries, nonzero=False) bandentries = ensurelist(bandentries, nonzero=True) headerfont = 62 if bold else 42 # CHECK LOG.insist(self.canvas, "Canvas does not exist!") self.canvas.cd(panel) scale = 485. / min(gPad.GetWh() * gPad.GetHNDC(), gPad.GetWw() * gPad.GetWNDC()) tsize *= scale # text size # ENTRIES #if len(bandentries)==len(bands) and len(entries)>len(hists): # for band, bandtitle in zip(band,bandentries): # entries.insert(hists.index(band),bandtitle) while len(entries) < len(hists): entries.append(hists[len(entries)].GetTitle()) while len(bandentries) < len(bands): bandentries.append(bands[len(bandentries)].GetTitle()) hists = hists + bands entries = entries + bandentries if latex: title = maketitle(title) entries = [maketitle(e) for e in entries] texts = [maketitle(t) for t in texts] maxlen = estimatelen([title] + entries + texts) # STYLES if style0: styles[0] = style0 while len(styles) < len(hists): hist = hists[len(styles)] if hist in bands: styles.append('f') elif style != None: styles.append(style) elif hasattr(hist, 'GetFillStyle') and hist.GetFillStyle() > 0: styles.append('f') elif 'E0' in hist.GetOption() or 'E1' in hist.GetOption(): styles.append(errstyle) else: styles.append('lp') # NUMBER of LINES nlines = sum([1 + e.count('\n') for e in entries]) #else: nlines += 0.80 if texts: nlines += sum([1 + t.count('\n') for t in texts]) if ncols > 1: nlines /= float(ncols) if title: nlines += 1 + title.count('\n') # DIMENSIONS if width < 0: width = twidth * max(0.22, min(0.60, 0.036 + 0.016 * maxlen)) if height < 0: height = theight * 1.34 * tsize * nlines if ncols > 1: width *= ncols / (1 - colsep) x2 = 0.90 x1 = x2 - width y1 = 0.92 y2 = y1 - height # POSITION if position == None: position = "" position = position.replace('left', 'L').replace( 'center', 'C').replace('right', 'R').replace( #.lower() 'top', 'T').replace('middle', 'M').replace('bottom', 'B') if not any(c in position for c in 'TMBy'): # set default vertical position += 'T' if not any(c in position for c in 'LCRx'): # set default horizontal position += 'RR' if ncols > 1 else 'R' # if title else 'L' if 'C' in position: if 'R' in position: center = 0.57 elif 'L' in position: center = 0.43 else: center = 0.50 x1 = center - width / 2 x2 = center + width / 2 elif 'LL' in position: x1 = 0.03 x2 = x1 + width elif 'L' in position: x1 = 0.08 x2 = x1 + width elif 'RR' in position: x2 = 0.97 x1 = x2 - width elif 'R' in position: x2 = 0.92 x1 = x2 - width elif 'x=' in position: x1 = float(re.findall(r"x=(\d\.\d+)", position)[0]) x2 = x1 + width if 'M' in position: if 'T' in position: middle = 0.57 elif 'B' in position: middle = 0.43 else: middle = 0.50 y1 = middle - height / 2 y2 = middle + height / 2 elif 'TT' in position: y2 = 0.97 y1 = y2 - height elif 'T' in position: y2 = 0.92 y1 = y2 - height elif 'BB' in position: y1 = 0.03 y2 = y1 + height elif 'B' in position: y1 = 0.08 y2 = y1 + height elif 'y=' in position: y2 = float(re.findall(r"y=(\d\.\d+)", position)[0]) y1 = y2 - height if x1_user != None: x1 = x1_user x2 = x1 + width if x2_user == None else x2_user if y1_user != None: y1 = y1_user y2 = y1 - height if y2_user == None else y2_user L, R = gPad.GetLeftMargin(), gPad.GetRightMargin() T, B = gPad.GetTopMargin(), gPad.GetBottomMargin() X1, X2 = L + (1 - L - R) * x1, L + ( 1 - L - R) * x2 # convert frame to canvas coordinates Y1, Y2 = B + (1 - T - B) * y1, B + ( 1 - T - B) * y2 # convert frame to canvas coordinates legend = TLegend(X1, Y1, X2, Y2) LOG.verb( "Plot.drawlegend: position=%r, height=%.3f, width=%.3f, (x1,y1,x2,y2)=(%.2f,%.2f,%.2f,%.2f), (X1,Y1,X2,Y2)=(%.2f,%.2f,%.2f,%.2f)" % (position, height, width, x1, y1, x2, y2, X1, Y1, X2, Y2), verbosity, 1) # MARGIN if ncols >= 2: margin = 0.090 / width else: margin = 0.044 / width legend.SetMargin(margin) # STYLE if transparent: legend.SetFillStyle(0) # 0 = transparent else: legend.SetFillColor(0) legend.SetBorderSize(border) legend.SetTextSize(tsize) legend.SetTextFont(headerfont) # bold for title if ncols > 1: legend.SetNColumns(ncols) legend.SetColumnSeparation(colsep) # HEADER if title: legend.SetHeader(title) legend.SetTextFont(42) # no bold for entries # ENTRIES if hists: for hist1, entry1, style1 in columnize(zip(hists, entries, styles), ncols): for entry in entry1.split('\n'): legend.AddEntry(hist1, entry, style1) hist1, style1 = 0, '' for line in texts: legend.AddEntry(0, line, '') if verbosity >= 2: print ">>> Plot.drawlegend: title=%r, texts=%s, latex=%s" % ( title, texts, latex) print ">>> Plot.drawlegend: hists=%s" % (hists) print ">>> Plot.drawlegend: entries=%s" % (entries) print ">>> Plot.drawlegend: styles=%s" % (styles) print ">>> Plot.drawlegend: nlines=%s, len(hists)=%s, len(texts)=%s, ncols=%s, margin=%s" % ( nlines, len(hists), len(texts), ncols, margin) legend.Draw(option) self.legends.append(legend) return legend
h0.GetYaxis().SetTitleOffset(1.23) h0.GetXaxis().SetRangeUser(minx, maxx) c0 = TCanvas("c0", "ratio 0") c0.Clear() rp0 = TRatioPlot(h0, h0) rp0.Draw() # set split fraction by hand sf = 0.5 fInsetWidth = 0.0025 pm = fInsetWidth width = gPad.GetWNDC() height = gPad.GetHNDC() f = height / width rp0.GetUpperPad().SetPad(pm * f, sf, 1. - pm * f, 1. - pm) rp0.GetLowerPad().SetPad(pm * f, pm, 1. - pm * f, sf) rp0.GetUpperPad().cd() gPad.SetLogy() h0.Draw("e same") h1.Draw("e same") h2.Draw("e same") legend = TLegend(0.60, 0.60, 0.88, 0.85) legend.SetFillColor(0) #legend.SetFillStyle(0)