OOP pyROOT histogram plotted from file disappears on click

I have a lot of root files and I want to plot the same plots from each of them, so I’ve made a class to try to help me to do that efficiently. So far I have some code which works really well and does exactly what I want it to do except that once the plots are drawn as soon as I click on the canvas the histogram disappears, leaving just the title and an empty pad as shown below.
Can someone help me fix this? Thanks!

import ROOT, os
from ROOT import TCanvas, gPad, kBlue, kRed, TFile

def histo_plotter(histo):
    ROOT.SetOwnership(histo,False)
    histo.Draw("same")
    return

class myclass:
    def __init__(self,run_no):
        self.run_no = run_no

    @staticmethod
    def canvas_creator(canv_name):
        my_canvas = TCanvas(canv_name,canv_name,900,700)
        return my_canvas

    @staticmethod
    def histo_maker(names,colour):
        h1=ROOT.TH1F(names[0],names[0],64,0,4)
        h1.FillRandom('gaus')
        h2=ROOT.TH1F(names[1],names[1],64,0,4)
        h2.FillRandom('gaus')

        h1.SetLineColor(colour)
        h2.SetLineColor(colour)

        write_file = TFile("~/Downloads/file.root","RECREATE")
        h1.Write()
        h2.Write()
        write_file.Close()
        
        histo_list = [h1,h2]
        return histo_list
    
def main():
    instances = [myclass(x) for x in range(2)]
    colours   = [kBlue, kRed]
    names     = [["h1", "h2"], ["h3","h4"]]
       
    histos = [instance.histo_maker(names[i_inst], colours[i_inst]) for i_inst, instance in enumerate(instances)]
    canvas_x_signals = [instance.canvas_creator(f"canv_{no}") for no, instance in enumerate(instances)]

    file_path=os.path.join("/Users/bethlong/Downloads/", "file.root")
    root_file = ROOT.TFile(file_path,"read")
    root_file.ls()
    test_histo = root_file.Get("h3")

    test_canv = TCanvas("test_canv","test_canv",900,700)
    ROOT.SetOwnership(test_canv, False)
    test_histo.Draw()
    gPad.Update()
    
if __name__ == '__main__':
    main()

Hi,

From the code it seems you are writing the histograms to a file, which is then closed. The ownership of the histograms is of the file: this is why the canvas is empty.
Can you refactor perhaps your code to avoid this?

Best,
D

Hi, thanks for the answer. Actually, I observed the problem first in a program which just reads the histograms and plots them, it doesn’t create them. In fact if you comment the line

histos = [instance.histo_maker(names[i_inst], colours[i_inst]) for i_inst, instance in enumerate(instances)]

then the problem persists. I just included that to make the whole example self-contained, instead of assuming that someone would create their own file to test with!
Also, when the canvas is created the histogram is shown, it only disappears when you click on it, and the fact that the title remains makes me think that maybe it isn’t a problem of the ownership of the histogram?

Any help would be very welcome!

Bumping this, is anyone else able to reproduce the problem?

Hi, I could really use a solution to this problem!

Hi,

Have you tried to avoid closing the files to which the histograms are attached as written before? Maybe it could be helpful if you reduce your example to a minimal form that shows the problem?

Best,
D

Hi Danilo,

sorry for being slow to reply and sorry if I wasn’t clear before, as I say, I only included the creation of the histo file to make the minimum working example completely self contained. If you already have some histos to plot and you plot them using the rest of the code then I hope you should be able to reproduce the problem:

import ROOT, os
from ROOT import TCanvas, gPad, TFile

def histo_plotter(histo):
    ROOT.SetOwnership(histo,False)
    histo.Draw("same")
    return

class myclass:
    def __init__(self,run_no):
        self.run_no = run_no
        self.file_path=os.path.join("/Users/bethlong/Downloads/", "file.root")
        self.root_file = ROOT.TFile(self.file_path,"read")

    @staticmethod
    def canvas_creator(canv_name):
        my_canvas = TCanvas(canv_name,canv_name,900,700)
        return my_canvas

    def histo_getter(self):
        h1 = self.root_file.Get("h3")
        h2 = self.root_file.Get("h4")
        return h1,h2
    
def main():
    instances = [myclass(x) for x in range(2)]       
    canvas_x_signals = [instance.canvas_creator(f"canv_{no}") for no, instance in enumerate(instances)]

    file_path=os.path.join("/Users/bethlong/Downloads/", "file.root")
    root_file = ROOT.TFile(file_path,"read")
    test_histo = root_file.Get("h3")

    test_canv = TCanvas("test_canv","test_canv",900,700)
    ROOT.SetOwnership(test_canv, False)
    test_histo.Draw()
    gPad.Update()
    
if __name__ == '__main__':
    main()

Update: I’ve reproduced the problem on an M1 Mac, still python3.12, still macOS Sonoma

Am I really the only person to use pyroot for OOP? Is it just not recommended? It’s a real shame that I can’t interact with the canvases since as soon as I click on one the plot disappears

Hi,

As said before, the histogram is going out of scope, as everything else, given that the main function returns. To keep it drawn, this should be avoided. One way to achieve this could be to boot the interpreter with python -i and test_histo.DrawClone().

Cheers,
D

Hi, thanks for the reply. I already ran the code with python -i, I tried DrawClone() and the problem persisted but DrawCopy() seems to work! Thanks for the inspiration.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.