Home | News | Documentation | Download

Histogram disappears as soon as TApplication Run method is called

Dear rooters,
I am working on the analysis of some data and I was trying to use a c++ object oriented approach but I encountered a problem while displaying histogram using TApplication.

Basically I have a class:

class amulet
{
public:
    //constructors
    amulet();
    amulet(RNode up, RNode dwn);
    amulet(RDataFrame up, RDataFrame dwn);
    ~amulet();

    //methods
void Perform_Fit(double* fitLims, string function, vector<string> names, bool fast = false, string fitOpts="RSE", string rootFileOpt="UPDATE");

private:
    RNode _dfUp;
    RNode _dfDwn;
};

where the method Perform_Fit create a canvas c1, divides it and draw fitted histograms:

void amulet::Perform_Lifetime_Fit(double* fitLims, string function, vector<string> names, bool fast, string fitOpts, string rootFileOpt)
{
	//some code here...

   TFile* outFile = new TFile(rootOut.c_str(), rootFileOpt.c_str()); //open root file to store data
   TCanvas* c1 = NULL;
   TLegend* legend = NULL;
   if(!fast)
   {  
      c1 = (combinedDecayName == "NoComb") ? new TCanvas(outDecayCanvasName.c_str(),outDecayCanvasName.c_str(),200,10,2*700,500) : new TCanvas(outDecayCanvasName.c_str(),outDecayCanvasName.c_str(),200,10,2*700,2*500);
	   (combinedDecayName == "NoComb") ? c1->Divide(2,1) : c1->Divide(2,2);
   }
	auto fitFunc = new TF1(("fitFunc_"+outDecayCanvasName).c_str(), function.c_str(), lowerLimit, upperLimit);

	if(!fast) c1->cd(1);
	
	fitFunc->SetParameters(histUpDecay->GetMean(), histUpDecay->GetBinContent(histUpDecay->GetBin(lowerLimit)) , histUpDecay->GetBinContent(histUpDecay->GetBin(upperLimit))); //automize parameter initialization
	fitFunc->SetParLimits(0,1e-8,5e-5);
    histUpDecay->Fit(fitFunc, "REM");

   if(!fast)
   {
      histUpDecay->SetTitle((UpDecayName+"; #Deltat [s]; N events").c_str());
      histUpDecay->Draw("E");
      
      auto legend = new TLegend(0.42,0.335,0.8452,0.506);
      legend->SetLineColor(kWhite);
      legend->AddEntry(fitFunc,fitFunc->GetExpFormula(),"l");
      legend->AddEntry(UpDecayName.c_str(),UpDecayName.c_str(),"l"); //legend->AddEntry(&(*h1),"data","l"); //&(*h1) to unwrap h1 from RResultPtr
      legend->Draw();
      
      gPad->Update();
      TPaveStats *stUpDecay = (TPaveStats*)histUpDecay->FindObject("stats");
      stUpDecay->SetX1NDC(0.517192), stUpDecay->SetX2NDC(0.869628); //move and resize stat box
      stUpDecay->SetY1NDC(0.567511), stUpDecay->SetY2NDC(0.883966);
      
      c1->Modified();
      c1->Update();

    //some other code to perform other fits and sum/subtract histograms and draw them in the other subcanvases
   }
   if(!fast)
   {
      c1->Write();
      c1->SaveAs((outDecayCanvasDir+".pdf").c_str(), ".pdf");
   }
   outFile->Close();
}

Then in my main cpp file I create my object with previous dataframes and I call the Perform_Fit method:

int main()
{
//some code here
	gStyle->SetOptTitle(true);
	gStyle->SetOptFit(1111);
	gStyle->SetOptStat(110);
	(setRootBatchMode) ? gROOT->SetBatch(true) : gROOT->SetBatch(false);
	TRint *myApp = (setRootBatchMode) ? NULL : new TRint("ROOT example", &argc, argv);
	
	auto amu = new amulet(df_histUp, df_histDwn);

	amu->Perform_Fit(fitLimits, myFormula, names);
	if (!setRootBatchMode)
		myApp->Run();
    return 0;
}

During the execution a window with the TCanvas opens up and seems to show everything correctly: both the histograms and the fitted functions. The problem is that when the execution ends and the myApp->Run() line is reached the TApplication remains open and I can interact with it but for some reason the datapoints an the fitted function disappears. The TLegend and the statistical box remains.
I have a similar problem also when I try to add two histograms.
I would appreciate any help, thanks in advance.


ROOT Version: 6.22/07
Platform: Ubuntu 20.10
Compiler: GCC 10.2.0


Hi,
it’s probably a lifetime issue (histUpDecay or fitFunc being deleted before the end of the application).

One reason they might be deleted is that outFile thinks it owns them and deletes them when you call outFile->Close(). There are a few things you can try: comment out outFile->Close(), just to verify that’s the issue, and switch from histUpDecay->Draw("E"); to histUpDecay->DrawClone("E");. Another option is to call histUpDecay->SetDirectory(nullptr); to detach histUpDecay from outFile (if that’s the issue).

Cheers,
Enrico

Hi,
I have tried to comment out outFile->Close() and the problem persisted. I have also tried to call histUpDecay->SetDirectory(nullptr); and unfortunately it didn’t worked. Also DrawClone gave me some problems but I have tried with histUpDecay->DrawCopy("E"); and it worked so I guess there were some variables going out of scope or being deleted as you said.
However I have encountered another problem while implementing a second method into my class so probably I am not understanding how the ownership works in ROOT.
I have implemented another method in my class with the following structure:

//in source file
amulet::myNewMethod(/*parameters*/)
{
     //some code here
     auto myPlot = new TGraph();
     Perform_Fit( fitLims, function, names, true ); //notice that by passing true inside the Perform_Fit method a canvas with a null pointer is istantiated
     TCanvas* c0 = new TCanvas("newname", "newname", /*dimensions*/);
     c0->cd();
     //fill plot
     myPlot ->Draw("AP");
}

and then in my main() function I did:

int main()
{
    //some code here
    auto myObject = new amulet(my_df1, my_df2);
    myObject->Perform_Fit( fitLims, function, names, false ); //notice that by passing false a non-trivial canvas is created and opened in the live session
    //and then
    myObject->myNewMethod(/*params*/);
}

and what happened was that at first call in main() of myObject->Perform_Fit( fitLims, function, names, false ); all the plot opened correctly in the live session but then as soon as I called myObject->myNewMethod(/*params*/); the last histogram drawn by myObject->Perform_Fit( fitLims, function, names, false ); disappeared from the subpad.
I have also tried to call SetDirectory(nullptr); and nothing happened.
The strangest thing is that I have solved by creating a third canvas to hold the new plot and then I have deleted it before creating a new one to draw the plot. In this way everything opened up correctly.

 //in myNewMethod method
TCanvas* c1 = new TCanvas();
 c1->SetBatch(true);
 c1->cd();
auto myPlot = new TGraph();
Perform_Fit( fitLims, function, names, true );
//fill plot
delete c1;
 TCanvas* c0 = new TCanvas("newname", "newname", /*dimensions*/);
 c0->cd();
 myPlot ->Draw("AP");

From what I understand myPlot should be owned by c1 so I am confused: how is it possible that it survives to be drawn in c0?
Unfortunately I cannot provide a working script because I decided to change the whole structure of both my class and my main file.
Anyway thanks a lot bc I didn’t know much about ownership in ROOT and now everything seems more clear.

Ok, unfortunately without a reproducer it’s really hard for me to tell what’s going on exactly. A complete write-up about ROOT object ownership can be found in the old users guide, although that might be too much info :smiley:

Great, it seems very useful! Thanks a lot