Drawing histograms in TObjArray of canvases

Hi All,

I am currently generating a TObjArray of multipad canvases that I would like to iterate through to fill and draw histograms, which I will later write the TObjArray to a ROOT file. I have been successful in generating the TObjArray of canvases, but am having trouble filling and drawing the histograms.

Here is the script I am currently using:

   gROOT->SetBatch(kTRUE);

   char cname[10], ctitle[10], hname[10], htitle[10];
   TObjArray Clist(0);
   Clist.SetOwner(kTRUE);
   TCanvas* c;
   TH1F* h;

   for(int a = 0; a <= 1600; a++)
   {
      sprintf(cname, "c%d", a);
      c = new TCanvas(cname, "multipads", 900, 700);
      c->Divide(4,5);
      for(int f = 0; f <= 19; f++)
      {
         c->cd(f);
         sprintf(hname, "h%d at c%d", f, a);
         sprintf(htitle, "hist num: %d, c%d", f, a);
         h = new TH1F(hname, htitle, 100, 0, 400);
      }
      Clist.Add(c);
   }

   for(int i = 0; i < N; i++)
   {

      ...

      Clist.At(element);           // element, an integer  is determined based off of the value of N
      c->cd(0);
      h->Fill(tot_hit_count);    // tot_hit_count, an integer is calculated in the ellipses above

      for(int b = 1; b <= 19; b++)
      {
         c->cd(b);
         h->Fill(hc[b]);  // hc[b], an integer, the array is filled in the ellipses

      }
  }

   for(int m = 0; m <= 1600; m++)
   {
      Clist.At(m);
      for(int n = 0; n <= 19; n++)
      {
         c->cd(n);
         h->Draw();
      }
   }

   Clist.Write();
   Clist.Delete();

Upon viewing my root file in TBrowser, I am able to view the canvases, but they are empty.

All help is greatly appreciated. Thank you.

-Frank

ROOT Version: 6.22/06
Platform: MacOS Big Sur
Compiler: gcc


Can you provide a small running macro reproducing the problem ? It is hard to tell what’s wrong with the pieces of code you sent.

@couet, I have uploaded the full macro I am using, and a ROOT file which is intended for use to generate the histograms in the canvases. Thank you so much for taking the time to look at this.

recon_tree.C (4.1 KB)

I must admit I am a bit confused with the logic of your program.
First you do:

// Initializing canvases and histograms for TObjArray

   for(int a = 0; a <= 1600; a++)
   {
      sprintf(cname, "c%d", a);
      c = new TCanvas(cname, "multipads", 900, 700);
      c->Divide(4,5);
      for(int f = 0; f <= 19; f++)
      {
         c->cd(f);
         sprintf(hname, "h%d at c%d", f, a);
         sprintf(htitle, "hist num: %d, c%d", f, a);
         h = new TH1F(hname, htitle, 100, 0, 400);
      }
      Clist.Add(c);
   }

This creates 1600 canvases each of them Divided in 20 pads. For each canvas you do a loop over the 20 pads but this loop does basically nothing on the pad. You just keep creating again and again the same histogram h… 20x1600 times … And finally you add this empty newly created canvas in a list. …

then you fill h

And you have the final loop:

 for(int m = 0; m <= 1600; m++)
   {
      Clist.At(m);
      for(int n = 0; n <= 19; n++)
      {
         c->cd(n);
         h->Draw();
      }
   }

Which does nothing because ‘c’ never change …

@couet Thank you so much for your quick feedback. In the first code block you reference, in the second for loop, I had thought that maybe the reason I was not able to fill the histograms within the canvases was due to them not being initialized in some way. Previously I had just generated the canvases and added to Clist.

And for the second block, I thought the call:

Clist.At(m);

was referencing the canvas at “m” in the TObjArray, and then within the nested for loop that:

c->cd(n); // references pad n within canvas m
h->Draw(); // would draw the histogram, initialized in the nested for loop above, in the selected pad

I hope this at least clears up what I was thinking.

It seems to me that in the first loop, you create many histograms, but in the later loops, you always “Fill” and “Draw” only one of them (just the last created one, which the “h” pointer is referencing).

You seem to believe that "c->cd(...); h->Fill(...);" and “c->cd(...); h->Draw();” will automatically update the “h” pointer, which is not the case.

Note: All these histograms are owned by “gROOT”, not by the canvases / pads, so doing “c->cd(...);” before "h = new TH1F(...);" is meaningless.

In the First loop you really do nothing except creating empty divided canvases.
The histogram h you create 1600x20 times is not used. At least you should Draw it in the current pad. It will be stored, empty, in that pad.

At returns the object at position m in Clist . As you do not use the return value, the line
Clist.At(m); does nothing.

@couet and @Wile_E_Coyote thank you so much for your help. Given the macro I have uploaded, can you suggest the appropriate way to handle the task I am attempting? To re-state it: I would like to be able to fill the histograms within the canvases, by iterating over them, or addressing them in some way based on the “element” variable I am using?

Thank you again both so much for your help and time.

recon_tree.C (4.5 KB)

@Wile_E_Coyote Thank you so much. Also @couet for all of your time and help regarding this macro.

@Wile_E_Coyote I notice that the histograms generated appear empty. I assume you ran the macro you provided with the data I posted. Did you also observe empty histograms?

They seem not empty for me. See:

@couet Yes, I did not search enough of the canvases. Thank you for verifying this for me.

@couet and @Wile_E_Coyote, hopefully this is my last question. Is there an efficient way to scan through “recon_tree.root” and access the histograms within the canvases within the file? Specifically I would like to access the bin height of a given bin within a given histogram. I would like to use the generated histograms as probability density functions.

The normal way will be to do Get to retrieve the histograms in the file.

TH1D* h = (TH1D*)f->Get("h");

But you stored canvases … not histograms:

% rootls -l recon_tree.root
TCanvas  May 20 09:15 2021 c0     "multipads 0"
TCanvas  May 20 09:15 2021 c1     "multipads 1"
TCanvas  May 20 09:15 2021 c10    "multipads 10"
TCanvas  May 20 09:15 2021 c100   "multipads 100"
TCanvas  May 20 09:16 2021 c1000  "multipads 1000"
TCanvas  May 20 09:16 2021 c1001  "multipads 1001"
TCanvas  May 20 09:16 2021 c1002  "multipads 1002"
TCanvas  May 20 09:16 2021 c1003  "multipads 1003"
TCanvas  May 20 09:16 2021 c1004  "multipads 1004"
[...]

That makes the retrieval much more tricky. Can you consider storing histograms instead of canvases ? canvases are graphics objects. Storing them to then working in the histograms in is not a good idea.

@couet Thanks again. This is doable, as the canvas use is for presentation purposes only. I have implemented the Get() method, referencing a histogram in written to a ROOT file. As a test, I chose one histogram, which when viewing the ROOT file in a TBrowser, was not empty. I then used:

treefile->GetObject("h19_at_c30", h[30][19]);
h[30][19]->Draw("HIST");

but obtained an empty histogram on output.

Thanks again for all of your help.

What is the content of your new .root file ?

@couet Here is a link to the ROOT file:

Yes you have histograms in the file now:

 % rootls -l recon_tree2.root
TH1F   May 21 00:23 2021 h0_at_c0     "PMT num: 0, element 0"
TH1F   May 21 00:23 2021 h0_at_c1     "PMT num: 0, element 1"
TH1F   May 21 00:23 2021 h0_at_c10    "PMT num: 0, element 10"
TH1F   May 21 00:23 2021 h0_at_c100   "PMT num: 0, element 100"
TH1F   May 21 00:23 2021 h0_at_c101   "PMT num: 0, element 101"
TH1F   May 21 00:23 2021 h0_at_c102   "PMT num: 0, element 102"
TH1F   May 21 00:23 2021 h0_at_c103   "PMT num: 0, element 103"
TH1F   May 21 00:23 2021 h0_at_c104   "PMT num: 0, element 104"
TH1F   May 21 00:23 2021 h0_at_c105   "PMT num: 0, element 105"
....

Simply use Get to retrieve them:

root [0] auto f = new TFile("recon_tree.root") 
(TFile *) @0x7ffee5c36840
root [1] auto h = (TH1F*)f->Get("h9_at_c99");
root [2] h->Draw()
Info in <TCanvas::MakeDefCanvas>:  created default TCanvas with name c1
root [3] 

@couet, Thank you so much. I am still perplexed as to why this is not working as well:

void recon_macro()
{
	// Opening and referencing recon tree

	TFile *treefile = TFile::Open("recon_tree.root");

   	TH1F *h[401][20];
   	for(int a = 0; a <= 400; a++)
   	{
   		for(int f = 0; f <= 19; f++) 
   		{
       		h[a][f] = new TH1F(TString::Format("h%d_at_c%d", f, a),
			TString::Format("PMT ref num: %d, element %d", f, a), 100, 0, 400);
        }
   	}

   	h[30][19] = (TH1F*)treefile->Get("h9_at_c99");

   	h[30][19]->Draw("HIST");
    
}

unnamed.pdf (13.8 KB)