Legend not drawn from function in PyRoot

Hi,

I am having trouble drawing legend, legend does not get drawn if I put the Draw statement in a function, though it does draw if the Draw statement is outside.

A simple example is

from ROfrom ROOT import *
file=TFile("SingleTopNtuple/SingleTop.ntuple.TDR_012.root")
tree=file.Get("UserData")

histo=TH1F("histo","", 100, -5,5)
tree.Draw("elec_eta>>histo", "")
legend=TLegend(0.6,0.6,0.8,0.8)
legend.AddEntry(histo, histo.GetName(), "l")
legend.Draw()

This does what I expect it to do. Draw histogram and legend. Now the following

from ROOT import *
file=TFile("SingleTopNtuple/SingleTop.ntuple.TDR_012.root")
tree=file.Get("UserData")

def fun():
  histo=TH1F("histo","", 100, -5,5)
  tree.Draw("elec_eta>>histo", "")
  legend=TLegend(0.6,0.6,0.8,0.8)
  legend.AddEntry(histo, histo.GetName(), "l")
  legend.Draw()

fun()

does not draw the legend, only the histogram.
A workaround is

from ROOT import *
file=TFile("SingleTopNtuple/SingleTop.ntuple.TDR_012.root")
tree=file.Get("UserData")

def fun():
  histo=TH1F("histo","", 100, -5,5)
  tree.Draw("elec_eta>>histo", "")
  legend=TLegend(0.6,0.6,0.8,0.8)
  legend.AddEntry(histo, histo.GetName(), "l")
  return legend

leg=fun()
leg.Draw()

this works the same as the first example.

Why does the second not work?

Thank you for your help.

Cheers
Akira

Akira,

it’s a scoping issue. Your TLegend object is created in python, therefore memory-managed by the python interpreter and thus destroyed at the end of your function. Because of the memory manager in ROOT, that destruction is propagated and hence it is completely removed.

I do not believe that in the old version of PyROOT that you’re using there is any other option than what you did (or keep it in a global list called something obvious such as ‘keep’). In newer versions of PyROOT, however, you can do:from ROOT import SetOwnership SetOwnership( legend, 0 ) # 0 = release (not keep), 1 = keep

Maybe I should just make a special case for TObject::Draw(), given what happens behind closed doors.

HTH,
Wim

Hi Wim

Thank you for your help.
I understand the situation but this does not happen to histograms (different ownership?) I see them drawn either way.

Cheers
Akira

Akira,

by default, PyROOT uses some heuristics to determine ownership rules (unless the memory policy is explicitly set to “strict,” which unfortunately is a global feature, so either all code is strict or all use heuristics).

One of the rules thus applied, is that once an object is passed by non-const pointer, ownership is relinquished. This works very well in ROOT, since when ownership of an object is not going to be taken over, the interfaces tend to use (const) references. It is, however, not generally a good idea (Gaudi/Athena comes to mind, where most interfaces are passed around by non-const pointer, regardless of ownership, which is taken care of with ref-counting), and hence the strict mode was invented.

In your case, it is this call:legend.AddEntry(histo, histo.GetName(), "l")which passes ‘histo’ by non-const pointer, causing PyROOT to give up ownership. Indeed, if you dig a bit, the pointer to the histo is kept as a datamember of a TLegendEntry, and so the heuristic rule that prevents python from deleting the C++ side of ‘histo’ is correctly applied here.

HTH,
Wim

Hi Wim

Thank you for a detailed explanation. I understand as far as you told me but looks like there’s much more to learn for me. Thanks your help, have a nice weekend.

Cheers
Akira