Dear Python experts,
This is mostly a Python question, but strongly connected to ROOT’s gStyle.
Consider the little piece of code:
from ROOT import gStyle
from ROOT import gROOT
from ROOT import TStyle
jhStyle = TStyle("jhStyle","jhStyle")
print "SetStyle: before gStyle.Getname() = '%s'" % gStyle.GetName()
print "SetStyle: after gStyle.Getname() = '%s'" % gStyle.GetName()
print "DoSomething: gStyle.Getname() = '%s'" % gStyle.GetName()
if __name__ == "__main__":
print "main: before gStyle.Getname() = '%s'" % gStyle.GetName()
print "main: after gStyle.Getname() = '%s'" % gStyle.GetName()
This works fine, no question about that. If I understand correctly, my new style has to be global because the call to SetStyle does not really copy anything, it just sets a pointer to my chosen style, right? This means that the new style actually has to survive the return from the call so it has to be global.
But if I comment out the line ‘global jhStyle’ I get the following output:
main: before gStyle.Getname() = 'Default'
SetStyle: before gStyle.Getname() = 'Default'
SetStyle: after gStyle.Getname() = 'jhStyle'
main: after gStyle.Getname() = 'Default'
DoSomething: gStyle.Getname() = 'Default'
The question is: how does it go back to ‘Default’? Is the style pointer reset explicitly somewhere? Or is this some scoping issue I don’t understand?
the communication is indeed ROOT internal, and python is not aware of it: in the TStyle constructor, it adds itself to the ListOfStyles as maintained by gROOT. After that, SetStyle can find your new style by name in that list.
Then, without the “global” keyword, your TStyle’s destructor gets called when the python object goes out of scope at the end of the function (that is, its reference count goes to zero, and it gets collected). In TStyle::~TStyle, if gStyle == this, it reverts to the last known TStyle object (i.e. the default in this case).
Instead of using a global, one can also relinquish ownership explicitly by doing:
ROOT.SetOwnership( jhStyle, False )and take it back by using ‘True’ as the second argument, if/as needed. If the object is not owned by python, the corresponding C++ object will not be deleted after all python references to it go away.
for explaining this to me. One more (curious) question then: if I release ownership, do the normal C(++?) rules hold? I mean, will everything that started life as some sort of local variable be cleaned up for me? Can you explain to me what exactly happens in Python in the line ‘jhStyle = TStyle("…","…")?’ I guess neither the object nor the reference to it can be on the stack, if you can keep them after the function returns, right?
all C++ objects created from python are new-ed. Typically, one should only give up ownership if one knows that the object will be deleted on the C++ side. In the particular case of TStyle, I thought it would be deleted by gROOT, but on closer inspection, I see:
// fStyles->Delete(); SafeDelete(fStyles);in the TROOT destructor. That is, cleaning up of the styles is commented out, and indeed does not happen.
So, in this case, using “global” is better (and perhaps better understandable as well, given that setting a new style is a global state change). Note that ROOT has a communication mechanism, such that if ROOT were to decide to destroy your TStyle, python would be notified and the jhStyle reference on the python side redirected to a None-like object.