TCanvas size not persisted when using Clone

The size of a TCanvas is not persisted in the new object after calling Clone().

I’m using pyROOT with batch mode set to True and ROOT version 6.06/00. I have attached some demonstration code which shows that the canvasses saved before and after cloning have different widths and heights.
preserveCanvas.py (633 Bytes)

In my code I make plots and Draw them onto TCanvases, constructed with:

c = TCanvas('c','c',600,640) #these look like portrait orientation

I append each TCanvas to a list. The canvases have the same names. I found that the code seg faults if I try to print any of the canvases. I found that copying or cloning the canvases before adding them to the list solves the problem. I can then print the canvases to a PDF and all the correct plots are printed. However, the size of the canvases are not persisted but return to the default size, which looks like a square or landsacpe orientation. I also noticed that the GetWindowWidth and GetWindowHeight functions return different values before and after cloning:

print c.GetWindowHeight() #returns 600    
cnew = c.Clone()
clist.append( cnew )
print cnew.GetWindowHeight() #returns 0   

So it appears that the “canvas window” isn’t cloned, and this has something to do with the size of the canvas?

How can I persist the canvas size? I tried setting the width and height after cloning:

cnew.SetCanvasSize( c.GetWindowWidth(), c.GetWindowHeight() )

but this doesn’t change anything. And if I replace the above snippet with:

cnew.SetWindowSize( c.GetWindowWidth(), c.GetWindowHeight() )

the code segfaults because cnew doesn’t have a window.

I’ll check with root master.

Is there any news from this?

Not yet, I was fixing other bugs

I am not sure what your script is supposed to show. I did:

$ python preserveCanvas.py 
Info in <TCanvas::Print>: pdf file ./temp_before_0.pdf has been created
TCanvas::Constructor:0: RuntimeWarning: Deleting canvas with same name: c
Info in <TCanvas::Print>: pdf file ./temp_before_1.pdf has been created
TCanvas::Constructor:0: RuntimeWarning: Deleting canvas with same name: c
Info in <TCanvas::Print>: pdf file ./temp_before_2.pdf has been created
Info in <TCanvas::Print>: pdf file ./temp_after_0.pdf has been created
Info in <TCanvas::Print>: pdf file ./temp_after_1.pdf has been created

it gives me 5 white pdf files having the same size.

When I run this script the canvases are not all the same size. Here is a similar script which makes the problem clearer:
preserveCanvas.py (856 Bytes)

Here is a screenshot of what the produced PDFs look like on my desktop:

The corresponding numbered PDFs should have the same size, because they are clones of each other. But as you can see, they do not.

Hence, I think that the canvas sizes are not preserved when cloning.

I see now … do you get the same with using this list ? …
i am not familiar with python and would like to translate it in C++ to see if this issue is due to python,.

I see the same result when running this code: preserveCanvas.C (913 Bytes)

Compiled on lxplus with:

g++ preserveCanvas.C -o preserveCanvas.exe `root-config --cflags --glibs`

Thanks for your input. I have tried to convert your program into a .C macro and ran it in interactive mode with ROOT 6. But I get a crash. I then tried to simplified it (with a array of canvas) and it still crashes. I am investigating.

Is this a bug or something wrong with my implementation?

I was doing something else meanwhile … I will come back to this.

The crash is on the Cocoa version on Mac only. With X11 version it does not crash. So I can execute the following macro.

void preserveCanvas2()
{
   TCanvas *c = new TCanvas("c","c",600,100);
   c->Print("temp_before_0.pdf");
   cout << ">>> "<< c->GetWindowWidth() << ", " << c->GetWindowHeight() << endl;

   auto cnew0 = (TCanvas*) c->Clone();

   c = new TCanvas("c","c",100,600);
   c->Print("./temp_before_1.pdf");
   cout << ">>> "<< c->GetWindowWidth() << ", " << c->GetWindowHeight() << endl;

   TCanvas *cnew1 = (TCanvas*) c->Clone();

   cnew0->Print("temp_after_0.pdf");

   cout << ">>> "<< cnew0->GetWindowWidth() << ", " << cnew0->GetWindowHeight() << endl;
   cnew1->Print("./temp_after_1.pdf");
   cout << ">>> "<< cnew1->GetWindowWidth() << ", " << cnew1->GetWindowHeight() << endl;
}
```
I see that temp_after_0.pdf is oriented as portrait instead of landscape.

Ok, so the Clone function is working fine.

Can you look at my original email again? The issue is that when I put these canvases into a list, then overwrite the variable, say “cnew”, that refers to the pointer, the pointer in the list also gets changed so that the canvases in the list are all the same.

My issue is how to add canvases into a list in a generic way, so that I can do addCanvas( mycanvas ), and then later on I can do canvasList[index].Print(’…’) . My solution to this in the second piece of code I posted was to Clone the canvas so that it was a different object, then add it to the list. But this doesn’t work.

No … as i said " temp_after_0.pdf is oriented as portrait instead of landscape."

I think we can reduce the problem to:

{
   TCanvas *c = new TCanvas("c","c",600,100);
   cout << ">>> c "<< c->GetWindowWidth() << ", " << c->GetWindowHeight() << endl;
   c->Update(); c->Print("temp_before_0.pdf");

   auto cnew0 = (TCanvas*) c->Clone();

   c = new TCanvas("c","c",100,600); // this line makes temp_after_0.pdf portrait

   cout << ">>> cnew0 "<< cnew0->GetWindowWidth() << ", " << cnew0->GetWindowHeight() << endl;
   cnew0->Update(); cnew0->Print("temp_after_0.pdf");
}
```

if I do the following then the 2 pdf are landscape as they should be:

{
   TCanvas *c = new TCanvas("c","c",600,100);
   cout << ">>> c "<< c->GetWindowWidth() << ", " << c->GetWindowHeight() << endl;
   c->Print("temp_before_0.pdf");

   auto cnew0 = (TCanvas*) c->Clone();
   cnew0->SetName("cnew0");
   cnew0->Draw(); cnew0->Update();

   delete c;
   c = new TCanvas("c","c",100,600); // this line makes temp_after_0.pdf portrait

   cout << ">>> cnew0 "<< cnew0->GetWindowWidth() << ", " << cnew0->GetWindowHeight() << endl;
   cnew0->Print("temp_after_0.pdf");
}

If I run the code you posted the output is

Info in <TCanvas::Print>: pdf file ./temp_before_0.pdf has been created
600, 100
600, 100
Info in <TCanvas::Print>: pdf file ./temp_after_0.pdf has been created

BUT the canvases are not the same size. The window sizes are the same, but the second canvas, “temp_after_0.pdf” has a smaller y-value than “temp_before_0.pdf”. See the attached screenshot.

I also output the GetWh() functions:

  cout << c->GetWindowWidth() << ", " << c->GetWindowHeight() << endl;
  cout << c->GetWw() << ", " << c->GetWh() << endl;
...
  cout << cnew->GetWindowWidth() << ", " << cnew->GetWindowHeight() << endl;
  cout << cnew->GetWw() << ", " << cnew->GetWh() << endl;

which gives:

Info in <TCanvas::Print>: pdf file ./temp_before_0.pdf has been created
600, 100
596, 72
600, 100
592, 44
Info in <TCanvas::Print>: pdf file ./temp_after_0.pdf has been created

I implemented your fix in my python framework. It seems that the aspect ratios of the canvases are (almost) preserved when using your solution. This is completely adequet for my purposes, thanks!

Although it’s still quite strange that the canvas changes size.

I think the explanation is given in https://root.cern/doc/master/classTCanvas.html
at the end of the Header doc, the sentence starting with:

At creation time, no matter if in interactive or batch mode, …

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