ATLAS Offline Software
python/ROOTUtils.py
Go to the documentation of this file.
1 # Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
2 
3 """
4 Miscellaneous utilities for PyROOT.
5 """
6 __author__ = 'Juerg Beringer'
7 __version__ = '$Id: ROOTUtils.py 759047 2016-07-01 00:45:13Z beringer $'
8 
9 
10 import ROOT
11 
12 protectedObjectList = []
13 
14 def protect(obj):
15  """Utility function to prevent ROOT objects from being garbage-collected,
16  (thus disappearing from plots) when they go out of scope."""
17  protectedObjectList.append(obj)
18  return obj
19 
20 
21 # Define a set of useful canvas sizes for different purposes
22 myCanvasSizes = { 'default': (700, 500),
23  'default2d': (545, 500),
24  'slide1': (900,560),
25  'slide2': (450,560),
26  'slide2text': (400,500),
27  'fig1': (700, 500),
28  'fig2': (1400, 500),
29  'page': (750,1000),
30  'landscape': (1600,1000),
31  'wide': (1000,500),
32  'extrawide': (1500,500),
33  'square': (700,700)
34  }
35 
36 
37 class MyCanvas(ROOT.TCanvas):
38  """Class MyCanvas is a ROOT TCanvas that allows choosing one of
39  several typically used canvas sizes. It can automatically save
40  the canvas in different formats upon destruction provided the
41  user makes sure that all histograms still exist."""
42 
43  # Class defaults
44  saveAsList = [ '.gif' ]
45  autoSaveOnExit = False
46  autoName = '%s.autosave%s'
47 
48  def __init__(self,name='MyCanvas', size='default', xdivs=1, ydivs=1, saveAsList=None, autoSaveOnExit=None):
49  super(MyCanvas,self).__init__(name)
50  self.name = name
51  if saveAsList is not None:
52  self.saveAsList = saveAsList
53  else:
54  self.saveAsList = MyCanvas.saveAsList
55  if autoSaveOnExit is not None:
56  self.autoSaveOnExit = autoSaveOnExit
57  else:
58  self.autoSaveOnExit = MyCanvas.autoSaveOnExit
59  self.autoName = MyCanvas.autoName
60  self.Divide(xdivs,ydivs)
61 
62  def __del__(self):
63  if self.autoSaveOnExit:
64  self.save()
65 
66  def save(self):
67  for o in self.saveAsList:
68  if o[0]=='.':
69  self.SaveAs(self.autoName % (self.name,o))
70  else:
71  self.SaveAs(o)
72 
73 
75  """PlotLibrary is a base class that can be used to manage a set of
76  plots. Each individual plot should be created as a method of a
77  derived class. Other methods of the derived class that do not
78  generate plots should either have names starting with
79  underscore, or should be declared when calling __init__ of the
80  base class."""
81 
82  def __init__(self, name = 'MyPlots', otherMethods=[]):
83  """Constructor. otherMethods specifies a list of methods of the derived
84  class that are not creating plots and should not be called by plot()."""
85  self.name = name
86  self.otherMethods = []
87  self.otherMethods.extend(otherMethods)
88  self.rootObjects = []
89 
90  # Variables used by genPlot
91  self.whatList = []
92  self.singleCanvasSize = 'default'
93  self.allCanvasSize = 'page'
94  self.allCanvasDivs = (3,4)
95  self.saveAsList = []
96  self.gPadSaveAsList = []
97 
98  def protect(self,obj):
99  """Protect ROOT object from garbage collection."""
100  self.rootObjects.append(obj)
101  return obj
102 
103  def plot(self, plotName='',*args):
104  """Make one or all (if plotName=='') plots."""
105  if plotName:
106  self.__class__.__dict__[plotName](self,*args)
107  else:
108  for i in self.__class__.__dict__:
109  if i[0]=='_': continue # skip private methods
110  if i in self.otherMethods: continue
111  self.__class__.__dict__[i](self,*args)
112 
113  def genPlot(self,what='ALL',code='plot',labels=[],*args):
114  """Make plots using a general code. genPlot makes either a single plot defined
115  by argument what, or all plots (if what=''). The plots made in the latter
116  case are specified by whatList. A canvas that is subdivided if necessary
117  is created before the plotting code is called."""
118  if what=='' or what.upper()=='ALL':
119  c = self.protect( MyCanvas('%s-%s-%s' % (self.name,what,code),
120  self.allCanvasSize,self.allCanvasDivs[0],self.allCanvasDivs[1]) )
121  iCanvas = 0
122  for w in self.whatList:
123  iCanvas += 1
124  c.cd(iCanvas)
125  try:
126  self.__class__.__dict__[code](self,w,*args)
127  except Exception:
128  self.__class__.__bases__[0].__dict__[code](self,w,*args)
129  ROOT.gPad.Update()
130  # For some strange reason, this works only for .eps, but not e.g. for gif files...???
131  for o in self.gPadSaveAsList:
132  if o[0]=='.':
133  ROOT.gPad.SaveAs('%s-%s-%s%s' % (self.name,code,w,o))
134  else:
135  ROOT.gPad.SaveAs(o)
136 
137  # If we want to put some labels on an empty pad, add them now.
138  if labels!=[] and self.allCanvasDivs[0]*self.allCanvasDivs[1] > len(self.whatList):
139  iCanvas+=1
140  c.cd(iCanvas)
141  xtext=0.0
142  ytext=0.8
143  for label in labels:
144  drawText(xtext,ytext,0.06,label)
145  ytext=ytext-0.1
146  elif labels!=[]:
147  print ("ERROR: can't add labels unless we have an empty pad to use. Ignoring labels.")
148 
149  for o in self.saveAsList:
150  if o[0]=='.':
151  c.SaveAs('%s-%s-%s%s' % (self.name,code,what,o))
152  else:
153  c.SaveAs(o)
154 
155  else:
156  c = self.protect( MyCanvas(what,self.singleCanvasSize) )
157  try:
158  self.__class__.__dict__[code](self,what,*args)
159  except Exception:
160  self.__class__.__bases__[0].__dict__[code](self,what,*args)
161  ROOT.gPad.Update()
162  for o in self.saveAsList:
163  if o[0]=='.':
164  c.SaveAs('%s-%s-%s%s' % (self.name,code,what,o))
165  else:
166  c.SaveAs(o)
167 
169  """StyleFactory is a helper class for assigning marker styles, line styles,
170  and colors in ROOT plots."""
171 
172  markerSequence = [ 8, 21, 22, 23, 29, 4, 25, 26, 27, 28, 30, 2, 3, 6]
173  lineSequence = [ 1, 2, 3, 4]
174  colorSequence = [ 1, 2, 4, 6, 8]
175 
176  def __init__(self,incMarker=True,incLine=True,incColor=True):
177  self.idMarker = -1
178  self.idLine = -1
179  self.idColor = -1
180  self.incMarker = 1 if incMarker else 0
181  self.incLine = 1 if incLine else 0
182  self.incColor = 1 if incColor else 0
183 
184  def nextStyle(self):
185  """Get next style cycling through markers, lines and colors as specified by
186  the flags in the constructor. Returns a triplet of (marker,line,color) styles."""
187  self.idMarker += self.incMarker
188  self.idLine += self.incLine
189  self.idColor += self.incColor
190  return (StyleFactory.markerSequence[self.idMarker % len(StyleFactory.markerSequence)],
191  StyleFactory.lineSequence[self.idLine % len(StyleFactory.lineSequence)],
192  StyleFactory.colorSequence[self.idColor % len(StyleFactory.colorSequence)])
193 
194  def nextMarkerStyle(self):
195  """Get next marker style."""
196  self.idMarker +=1
197  return StyleFactory.markerSequence[self.idMarker % len(StyleFactory.markerSequence)]
198 
199  def nextLineStyle(self):
200  """Get next line style."""
201  self.idLine +=1
202  return StyleFactory.lineSequence[self.idLine % len(StyleFactory.lineSequence)]
203 
204  def nextColorStyle(self):
205  """Get next color."""
206  self.idColor +=1
207  return StyleFactory.colorSequence[self.idColor % len(StyleFactory.colorSequence)]
208 
209 
210 def drawAxisFrame(xmin,xmax,ymin,ymax,title='',xTitleOffset=None,yTitleOffset=None,doPlot=True,protectFrame=True):
211  frame = ROOT.TH2F('axisFrame',title,100,xmin,xmax,100,ymin,ymax)
212  frame.SetStats(False)
213  if xTitleOffset is not None:
214  frame.GetXaxis().SetTitleOffset(xTitleOffset)
215  if yTitleOffset is not None:
216  frame.GetYaxis().SetTitleOffset(yTitleOffset)
217  if doPlot:
218  frame.Draw()
219  if protectFrame:
220  protect(frame)
221  return frame
222 
223 
224 def drawHorizontalBand(xmin,xmax,y,ywidth,color=33,protectBand=True,centralLine=False):
225  """Draw a horizontal band of width +-ywidth. Nice colors are 19 and 33."""
226  b = ROOT.TH1F('band','band',1,xmin,xmax)
227  b.SetBinContent(1,y)
228  b.SetBinError(1,ywidth)
229  b.SetFillColor(color)
230  b.SetMarkerColor(color)
231  b.SetMarkerSize(0.01)
232  b.Draw('SAMEe2')
233  if centralLine:
234  b.Draw('SAMEL')
235  if protectBand:
236  protect(b)
237  return b
238 
239 
240 def drawText(x=0.74,y=0.87,dy=0.06,text='',font=62,color=1,align=11,linesep=';'):
241  """Draw a variable number of lines of text."""
242  t = ROOT.TLatex()
243  t.SetNDC()
244  t.SetTextFont(font)
245  t.SetTextColor(color)
246  t.SetTextAlign(align)
247  t.SetTextSize(dy/1.2)
248  for line in text.split(linesep):
249  t.DrawLatex(x,y,line)
250  y = y-dy
251  # Seems TLatex text doesn't need to be protected - why not?
252  return t
253 
254 
255 #def drawLegend(x=0.18,y=0.9,s=0.07,legendList=[],fillColor=0,lineColor=0):
256 def drawLegend(x1,y1,x2,y2,legendList=[],fillColor=0,lineColor=0,textSize=None,protectLegend=True):
257  """Draw a legend with one or more entries. legendList is a list of lists,
258  where each inner list defines the object, text and option of a legend entry.
259  NOTE: Must store returned TLegend, otherwise legend will disappear!"""
260  nlines = len(legendList)
261  if not nlines:
262  print ("ERROR: drawLegend called w/o any legend entries")
263  #maxchar = 0
264  #for e in legendList:
265  # maxchar = max(len(e[1]),maxchar)
266  #l = ROOT.TLegend(x,y-nlines*s,x+(2+maxchar)*s/3.7,y)
267  l = ROOT.TLegend(x1,y1,x2,y2)
268  l.SetFillColor(fillColor)
269  l.SetLineColor(lineColor)
270  l.SetShadowColor(lineColor)
271  if textSize:
272  l.SetTextSize(textSize)
273  for e in legendList:
274  l.AddEntry(e[0],e[1],e[2])
275  l.Draw()
276  if protectLegend:
277  protect(l)
278  return l
279 
280 
281 def moveStats(h,dx,dy,xw=0,yw=0,label=''):
282  if not h:
283  return
284  ROOT.gPad.Update()
285  st = h.GetListOfFunctions().FindObject('stats')
286  if not st:
287  print ('ERROR: No stats found - cannot move it')
288  return
289  st.SetTextColor(h.GetLineColor())
290  st.SetX1NDC(st.GetX1NDC()+dx)
291  st.SetY1NDC(st.GetY1NDC()+dy)
292  st.SetX2NDC(st.GetX2NDC()+xw if xw!=0 else st.GetX2NDC()+dx)
293  st.SetY2NDC(st.GetY2NDC()+yw if yw!=0 else st.GetY2NDC()+dy)
294  if not label:
295  h.SetName(label)
296  ROOT.gPad.Modified()
297  ROOT.gPad.Update
298 
299 
300 def atlasLabel(x,y,isPreliminary=False,color=1,offset=0.115,isForApproval=False,energy=8,customstring="",size=0.05):
301  if x is None or y is None:
302  print ("Must set (x,y) position using --atlasx and --atlasy to add labels. No ATLAS labels created.")
303  return
304  offset = offset/0.05*size
305  l = ROOT.TLatex()
306  l.SetNDC()
307  l.SetTextFont(72)
308  l.SetTextSize(size)
309  l.SetTextColor(color)
310  l.DrawLatex(x,y,"ATLAS")
311 
312  p = ROOT.TLatex()
313  p.SetNDC()
314  p.SetTextFont(42)
315  p.SetTextSize(size)
316  p.SetTextColor(color)
317 
318  if customstring != "":
319  p.DrawLatex(x+offset,y,customstring)
320  else:
321  if (isPreliminary):
322  p.DrawLatex(x+offset,y,"Preliminary")
323  if (isForApproval):
324  #p.DrawLatex(x+offset,y,"Internal for approval")
325  p.DrawLatex(x+offset,y,"Internal")
326 
327  if energy:
328  if float(energy)<15:
329  p.DrawLatex(x, y-(0.07/0.05*size), "#sqrt{s} = %s TeV" % energy)
330  else:
331  p.DrawLatex(x, y-(0.07/0.05*size), "#sqrt{s} = %s GeV" % energy)
332 
333 
334 
335 def atlasStyle(protectStyle=True):
336  s = ROOT.TStyle('ATLAS','ATLAS Style V2.02')
337 
338  # use plain black on white colors
339  icol=0 # WHITE
340  s.SetFrameBorderMode(icol)
341  s.SetFrameFillColor(icol)
342  s.SetCanvasBorderMode(icol)
343  s.SetCanvasColor(icol)
344  s.SetPadBorderMode(icol)
345  s.SetPadColor(icol)
346  s.SetStatColor(icol)
347  #s.SetFillColor(icol) # don't use: white fill color floa *all* objects
348 
349  # set the paper & margin sizes
350  s.SetPaperSize(20,26)
351  s.SetPadTopMargin(0.05)
352  s.SetPadRightMargin(0.05)
353  s.SetPadBottomMargin(0.16)
354  s.SetPadLeftMargin(0.16)
355 
356  # set title offsets (for axis label)
357  s.SetTitleXOffset(1.4)
358  s.SetTitleYOffset(1.4)
359 
360  # use large fonts
361  #Int_t font=72 # Helvetica italics
362  font = 42 # Helvetica
363  tsize = 0.05
364  s.SetTextFont(font)
365 
366  s.SetTextSize(tsize)
367  s.SetLabelFont(font,"x")
368  s.SetTitleFont(font,"x")
369  s.SetLabelFont(font,"y")
370  s.SetTitleFont(font,"y")
371  s.SetLabelFont(font,"z")
372  s.SetTitleFont(font,"z")
373 
374  s.SetLabelSize(tsize,"x")
375  s.SetTitleSize(tsize,"x")
376  s.SetLabelSize(tsize,"y")
377  s.SetTitleSize(tsize,"y")
378  s.SetLabelSize(tsize,"z")
379  s.SetTitleSize(tsize,"z")
380 
381  # use bold lines and markers
382  s.SetMarkerStyle(20)
383  s.SetMarkerSize(1.2)
384  s.SetHistLineWidth(2)
385  s.SetLineStyleString(2,"[12 12]") # postscript dashes
386 
387  # get rid of X error bars and y error bar caps
388  #s.SetErrorX(0.001)
389 
390  # get rid of error bar caps
391  s.SetEndErrorSize(0.)
392 
393  # do not display any of the standard histogram decorations
394  s.SetOptTitle(0)
395  #s.SetOptStat(1111)
396  s.SetOptStat(0)
397  #s.SetOptFit(1111)
398  s.SetOptFit(0)
399 
400  # put tick marks on top and RHS of plots
401  s.SetPadTickX(1)
402  s.SetPadTickY(1)
403 
404  if protectStyle:
405  protect(s)
406 
407  return s
408 
409 
410 def setStyle(style = None):
411  if not style:
412  style = atlasStyle()
413  print ('\nApplying style %s (%s) ...\n' % (style.GetName(),style.GetTitle()))
414  ROOT.gROOT.SetStyle(style.GetName())
415  ROOT.gROOT.ForceStyle()
python.ROOTUtils.StyleFactory.incColor
incColor
Definition: python/ROOTUtils.py:182
python.ROOTUtils.setStyle
def setStyle(style=None)
Definition: python/ROOTUtils.py:410
python.ROOTUtils.MyCanvas.name
name
Definition: python/ROOTUtils.py:50
python.ROOTUtils.StyleFactory.idLine
idLine
Definition: python/ROOTUtils.py:178
python.ROOTUtils.drawText
def drawText(x=0.74, y=0.87, dy=0.06, text='', font=62, color=1, align=11, linesep=';')
Definition: python/ROOTUtils.py:240
python.ROOTUtils.protect
def protect(obj)
Definition: python/ROOTUtils.py:14
python.ROOTUtils.PlotLibrary
Definition: python/ROOTUtils.py:74
python.ROOTUtils.StyleFactory.incLine
incLine
Definition: python/ROOTUtils.py:181
python.ROOTUtils.MyCanvas.autoName
string autoName
Definition: python/ROOTUtils.py:46
python.ROOTUtils.MyCanvas.saveAsList
list saveAsList
Definition: python/ROOTUtils.py:44
python.ROOTUtils.StyleFactory.nextMarkerStyle
def nextMarkerStyle(self)
Definition: python/ROOTUtils.py:194
dumpHVPathFromNtuple.append
bool append
Definition: dumpHVPathFromNtuple.py:91
python.ROOTUtils.PlotLibrary.__init__
def __init__(self, name='MyPlots', otherMethods=[])
Definition: python/ROOTUtils.py:82
python.ROOTUtils.PlotLibrary.singleCanvasSize
singleCanvasSize
Definition: python/ROOTUtils.py:92
python.ROOTUtils.PlotLibrary.allCanvasSize
allCanvasSize
Definition: python/ROOTUtils.py:93
python.ROOTUtils.StyleFactory.idColor
idColor
Definition: python/ROOTUtils.py:179
python.ROOTUtils.StyleFactory.__init__
def __init__(self, incMarker=True, incLine=True, incColor=True)
Definition: python/ROOTUtils.py:176
python.ROOTUtils.drawLegend
def drawLegend(x1, y1, x2, y2, legendList=[], fillColor=0, lineColor=0, textSize=None, protectLegend=True)
Definition: python/ROOTUtils.py:256
python.ROOTUtils.StyleFactory.idMarker
idMarker
Definition: python/ROOTUtils.py:177
python.ROOTUtils.atlasStyle
def atlasStyle(protectStyle=True)
Definition: python/ROOTUtils.py:335
python.ROOTUtils.PlotLibrary.name
name
Definition: python/ROOTUtils.py:85
python.ROOTUtils.StyleFactory.nextColorStyle
def nextColorStyle(self)
Definition: python/ROOTUtils.py:204
python.ROOTUtils.StyleFactory
Definition: python/ROOTUtils.py:168
python.ROOTUtils.PlotLibrary.protect
def protect(self, obj)
Definition: python/ROOTUtils.py:98
python.ROOTUtils.PlotLibrary.saveAsList
saveAsList
Definition: python/ROOTUtils.py:95
python.ROOTUtils.atlasLabel
def atlasLabel(x, y, isPreliminary=False, color=1, offset=0.115, isForApproval=False, energy=8, customstring="", size=0.05)
Definition: python/ROOTUtils.py:300
python.ROOTUtils.PlotLibrary.otherMethods
otherMethods
Definition: python/ROOTUtils.py:86
python.ROOTUtils.StyleFactory.incMarker
incMarker
Definition: python/ROOTUtils.py:180
python.ROOTUtils.drawAxisFrame
def drawAxisFrame(xmin, xmax, ymin, ymax, title='', xTitleOffset=None, yTitleOffset=None, doPlot=True, protectFrame=True)
Definition: python/ROOTUtils.py:210
python.ROOTUtils.MyCanvas.__del__
def __del__(self)
Definition: python/ROOTUtils.py:62
ActsTrk::detail::MakeDerivedVariant::extend
constexpr std::variant< Args..., T > extend(const std::variant< Args... > &, const T &)
Definition: MakeDerivedVariant.h:17
python.ROOTUtils.PlotLibrary.whatList
whatList
Definition: python/ROOTUtils.py:91
python.ROOTUtils.PlotLibrary.genPlot
def genPlot(self, what='ALL', code='plot', labels=[], *args)
Definition: python/ROOTUtils.py:113
python.ROOTUtils.PlotLibrary.gPadSaveAsList
gPadSaveAsList
Definition: python/ROOTUtils.py:96
python.ROOTUtils.MyCanvas.__init__
def __init__(self, name='MyCanvas', size='default', xdivs=1, ydivs=1, saveAsList=None, autoSaveOnExit=None)
Definition: python/ROOTUtils.py:48
python.ROOTUtils.PlotLibrary.plot
def plot(self, plotName='', *args)
Definition: python/ROOTUtils.py:103
python.ROOTUtils.MyCanvas.autoSaveOnExit
bool autoSaveOnExit
Definition: python/ROOTUtils.py:45
python.ROOTUtils.PlotLibrary.allCanvasDivs
allCanvasDivs
Definition: python/ROOTUtils.py:94
python.ROOTUtils.StyleFactory.nextStyle
def nextStyle(self)
Definition: python/ROOTUtils.py:184
python.ROOTUtils.MyCanvas
Definition: python/ROOTUtils.py:37
python.ROOTUtils.MyCanvas.save
def save(self)
Definition: python/ROOTUtils.py:66
python.ROOTUtils.StyleFactory.nextLineStyle
def nextLineStyle(self)
Definition: python/ROOTUtils.py:199
readCCLHist.float
float
Definition: readCCLHist.py:83
python.ROOTUtils.moveStats
def moveStats(h, dx, dy, xw=0, yw=0, label='')
Definition: python/ROOTUtils.py:281
python.ROOTUtils.drawHorizontalBand
def drawHorizontalBand(xmin, xmax, y, ywidth, color=33, protectBand=True, centralLine=False)
Definition: python/ROOTUtils.py:224
python.ROOTUtils.PlotLibrary.rootObjects
rootObjects
Definition: python/ROOTUtils.py:88