Draw canvases produced in batch mode

I need to produce many canvases in batch in my application. What I do is something like this:

gROOT->SetBatch(kTRUE);
std::vector<TCanvas*> canvases;
for(int i = 0; int < nCanvases; i++){
	TCanvas *c = new TCanvas;
	TH1F *h1 = new TH1F(...);
	TH1F *h2 = new TH1F(...);
	// Fill h1 and h2 and then:
	h1->Draw();
	h2->Draw("SAME");
	canvases.push_back(c);
}
gROOT->SetBatch(kFALSE);

At the end I have a vector of TCanvas. At this point the user may ask the GUI to show a certain canvas, so I need to draw it. The problem is that it seems that if a Canvas is produced when Root is in batch mode then it is not displayed. Calling TCanvas::SetBatch(kFALSE) before drawing results in a segfault. I attach a small example to illustrate the issue (compile with: g++ -g root-config --cflags --libs batch.cpp).
Thanks for the help.
batch.cpp (520 Bytes)

I reported the same here (I can switch from interactive to batch mode, but not the other way): SetBatch causes different graphics output for pngs

Thanks Coyote. That’s a pretty old post, must I assume that this is a known issue that will never be fixed?

Yes the following macro does not produce any graphics.
Intuitively it should… I guess

void can()
{
   gROOT->SetBatch(kTRUE);
   TCanvas *c = new TCanvas;
   TH1F *h1 = new TH1F("h1", "h1", 10, 0,10);
   h1->Draw();
   gROOT->SetBatch(kFALSE);
   c->Draw();
}

Olivier, I think the reason your macro does not produce anything is because a canvas created in batch mode is itself put in batch mode, which I assume makes the TCanvas::Draw call actually draw nothing. The canvas can in principle be brought back in interactive mode by TCanvas::SetBatch(kFALSE), but in this case a segfault is generated when calling Draw, as shown by the example I attached to my first post.

I tried:

void can()
{
   gROOT->SetBatch(kTRUE);
   TCanvas *c = new TCanvas;
   TH1F *h1 = new TH1F("h1", "h1", 10, 0,10);
   h1->Draw();
   gROOT->SetBatch(kFALSE);
   c->SetBatch(kTRUE);
   c->Draw();
}

I do not get any graphics with this… but no crash either (ROOT6 on Mac).

[quote=“couet”]I tried:

void can()
{
   gROOT->SetBatch(kTRUE);
   TCanvas *c = new TCanvas;
   TH1F *h1 = new TH1F("h1", "h1", 10, 0,10);
   h1->Draw();
   gROOT->SetBatch(kFALSE);
   c->SetBatch(kTRUE);
   c->Draw();
}

I do not get any graphics with this… but no crash either (ROOT6 on Mac).[/quote]
That’s because your canvas is still in batch mode when you draw it. You should call c->SetBatch(kFALSE) before Draw (i.e. change the argument of the call to SetBatch). This gives a crash for me with Root 5.34.30.

I do:

void can()
{
   gROOT->SetBatch(kTRUE);
   TCanvas *c = new TCanvas;
   TH1F *h1 = new TH1F("h1", "h1", 10, 0,10);
   h1->Draw();
   gROOT->SetBatch(kFALSE);
   c->SetBatch(kFALSE);
   c->Draw();
}

No crash with root 6

OK, at least the crash is no longer a problem with Root 6, but the canvas is not drawn anyway, if I understand correctly. So my original problem is not solved by Root 6, and it seems to be a long-standing issue even if not so popular, maybe.

Yes that’s right. I will look at that.

Actually may be the comment in TCanvas::SetBatch explains the fact it stays blocked in batch mode. It says:

/// Toggle batch mode. However, if the canvas is created without a window
/// then batch mode always stays set.

Ok, so I have to find another way… I found that if I clone the canvas when Root is not in batch mode then the clone canvas is correctly drawn:

gROOT->SetBatch(kTRUE);
TCanvas *c = new TCanvas;
TH1F *h1 = new TH1F("h1", "h1", 10, 0,10);
h1->Draw();
gROOT->SetBatch(kFALSE);
c->DrawClone();

The clone process makes the clone canvas drawable. This makes me think that the streamer does not suffer from the problem which seems to plague the constructor call after the interactive mode has been restored for gROOT in your previous post. Since it is possible to obtain a drawable clone canvas starting from a non-drawable one I’d say that there should be a way to make the non-drawable canvas itself drawable, perhaps doing the same initialization steps (which are unknown to me) that the streamer does when it creates the clone canvas.

Sadly, drawing a clone of the canvas is not optimal for me since it seems that there are memory leaks related to the clone process which make my program crash in the long run.

[quote=“couet”]Actually may be the comment in TCanvas::SetBatch explains the fact it stays blocked in batch mode. It says:

/// Toggle batch mode. However, if the canvas is created without a window /// then batch mode always stays set. [/quote]
I understand, but it does not explain why if you create a new canvas after putting back gROOT in interactive mode then you get those warnings you reported in the first version of your previous post. Correct?

Correct. I need to look where these warnings comes from.

I notice that if you do:

root [0]    gROOT->SetBatch(kTRUE);
root [1] TCanvas *c1 = new TCanvas;
root [2]    gROOT->SetBatch(kFALSE);
root [3] TCanvas *c2 = new TCanvas;
Warning in <TCanvas::ResizePad>: c1_n2 width changed from 0 to 10
Warning in <TCanvas::ResizePad>: c1_n2 height changed from 0 to 10

Then you get the warning and no canvas c2. But if you start with a canvas not in batch then all is fine :

root [0] TCanvas *c1 = new TCanvas;
root [1]    gROOT->SetBatch(kTRUE);
root [2] TCanvas *c2 = new TCanvas;
root [3]    gROOT->SetBatch(kFALSE);
root [4] TCanvas *c3 = new TCanvas;

Hi Olivier, I’d guess then that the first canvas initializes some global variables according to the current status of gROOT (batch or interactive). Then these global variables are not changed when the status of gROOT changes, event if their value depend on this status. This sound like a buggy behaviour, but knowing it maybe I can workaround it.
Thanks again for your help.

Hi Nicola, Yes you are right. I am using this thread to keep track of my investigations as it is hard for me to work continuously on this bug …

OK, then I’ll keep watching the thread :slight_smile:

In the following case (1st canvas in batch mode) gVirtualX points to TVirtualX:

root [0]  gROOT->SetBatch(kTRUE);
root [1] TCanvas *c1 = new TCanvas;
root [2] gROOT->SetBatch(kFALSE);
root [3] TCanvas *c2 = new TCanvas;
Warning in <TCanvas::ResizePad>: c1_n2 width changed from 0 to 10
Warning in <TCanvas::ResizePad>: c1_n2 height changed from 0 to 10

And the method TVirtualX::GetGeometry(…) returns 0 for window width and heigh…

Whereas is the following case (first canvas in interactive mode) gVirtualX points to TGX11:

root [0] TCanvas *c1 = new TCanvas;
root [1] gROOT->SetBatch(kTRUE);
root [2] TCanvas *c2 = new TCanvas;
root [3] gROOT->SetBatch(kFALSE);
root [4] TCanvas *c3 = new TCanvas;

And the method TGX11::GetGeometry(…) returns the correct window size.

GetGeometry() is called from TCanvas::Build().

I think I found the fix. But that needs to be checked closely.