TLegend font size alignment problems in TCanvas.Divide with TMultigraphs

Try a simple example like what couet wrote; does that look fine?

1 Like

@couet example works:

import ROOT
c1 = ROOT.TCanvas("c1","Divide Example",800,400)

g = ROOT.TGraph()
g.AddPoint(1.,1.)
g.AddPoint(2.,2.)

c1.Divide(4,1,0,0)

for i in range(4):
    c1.cd(i+1)
    g.Draw("AP")

l = ROOT.TLegend()
l.AddEntry(g,"Legend")
l.SetTextSize(0.04)
l.SetTextAlign(12)
l.Draw()
c1.Update()

But not with TMultiGraph:

import ROOT
c1 = ROOT.TCanvas("c1","Divide Example",800,400)

g1 = ROOT.TGraph()
g1.AddPoint(1.,1.)
g1.AddPoint(2.,2.)

mg = ROOT.TMultiGraph()

g2 = ROOT.TGraph()
g2.AddPoint(1.,1.)
g2.AddPoint(2.,2.)

mg.Add(g1)
mg.Add(g2)

c1.Divide(4,1,0,0)

for i in range(4):
    c1.cd(i+1)
    mg.Draw("AL")

l = ROOT.TLegend()
l.AddEntry(g1,"Legend1")
l.AddEntry(g2,"Legend2")
l.SetTextSize(0.04)
l.SetTextAlign(12)
l.Draw()
c1.Update()


(right side is missing, if I move the pad in the visualization screen, it appears)

It works for me with a multigraph:

{
   c1 = new TCanvas("c1","Divide Example",800,400);
   auto g1 = new TGraph();
   g1->AddPoint(1.,1.);
   g1->AddPoint(2.,2.);

   auto g2 = new TGraph();
   g2->AddPoint(1.,1.);
   g2->AddPoint(2.,2.);

   auto mg =new TMultiGraph();

   mg->Add(g1);
   mg->Add(g2);

   c1->Divide(4,1,0,0);
   for (int i = 1; i<=4; i++) {
      c1->cd(i);
      mg->Draw("AL");
   }
   auto l = new TLegend(0.008,0.35,0.9,0.6);
   l->AddEntry(g1,"Legend 1");
   l->AddEntry(g2,"Legend 2");
   l->Draw();
}

The C++ code runs well, but with Python (on WSL, ROOT 6.24/06) I get the same as acgc99, the legend entries’ text is not properly centered vertically, so it looks like a problem with pyroot or some library used by pyroot (or perhaps WSL, but I haven’t tried on a “pure” linux).

Update: already tried on Linux (Ubuntu 20.04, no WSL) and I get the same issue with PyROOT.

So it seems a Python related problem. I do not really understand how python can interfere in this text alignment. May be @etejedor has an idea.

Hi,
No, there shouldn’t be any difference between Python and C++. Can you share here the code in C++ and Python that gives different results? If I translate @couet 's last code snippet to Python I get exactly the same result as in C++.

That’s more logical. The python code giving the unaligned text must be different.

@etejedor note this code was given earlier in the thread. May be you can compare with your own translation.

Hi,
The code that the user gave for Multigraph and the one you wrote are not exactly the same, hence the differences.

This is the exact translation of @couet 's code to Python, which gives the same result as in C++:

import ROOT
c1 = ROOT.TCanvas("c1","Divide Example",800,400)

g1 = ROOT.TGraph()
g1.AddPoint(1.,1.)
g1.AddPoint(2.,2.)

g2 = ROOT.TGraph()
g2.AddPoint(1.,1.)
g2.AddPoint(2.,2.)

mg = ROOT.TMultiGraph()

mg.Add(g1)
mg.Add(g2)

c1.Divide(4,1,0,0)

for i in range(4):
    c1.cd(i+1)
    mg.Draw("AL")

l = ROOT.TLegend(0.008,0.35,0.9,0.6)
l.AddEntry(g1,"Legend1")
l.AddEntry(g2,"Legend2")
l.Draw()

I found the difference. In my code I create the TLegend with:

   auto l = new TLegend(0.008,0.35,0.9,0.6);

that’s the trick. In your case you let the default parameters They are too small because you ask for a bigger size with:

   l->SetTextSize(0.04);

If you let the default parameters for the legend box and remove the size setting it works also.

If you enlarge the text size it is now fixed, the legend drawing cannot optimise the text size. You should also give more room for the legend drawing by enlarging the legend box.

Okey, that solves the problem. But now there is a aesthetic question. Going back to my original code.

Default legend:

legend = ROOT.TLegend()


It is unreadable. If I apply what I learned here:

legend = ROOT.TLegend(0.1, 0.5, 0.4, 0.9)
legend.SetTextSize(0.05)
legend.SetTextAlign(12)

It is well aligned, but there is too much white space between entrees. I cannot reduce the space because it gets unaligned (legend = ROOT.TLegend(0.1, 0.6, 0.4, 0.9)).

You have more space in the first pad on the left. Put the legend there using c1.cd(1) before drawing the legend. Then make it less high and wider. for example:

{
   c1 = new TCanvas("c1","Divide Example",800,400);
   c1->SetLeftMargin(0.3);
   c1->SetBottomMargin(0.1);
   c1->Divide(4,1,0,0);

   auto g1 = new TGraph(); g1->SetLineColor(2);
   g1->AddPoint(1.,1.);
   g1->AddPoint(1.8,1.2);
   g1->AddPoint(2.,2.);

   auto g2 = new TGraph(); g2->SetLineColor(3);
   g2->AddPoint(3.,1.);
   g2->AddPoint(4.,2.);

   auto g3 = new TGraph(); g3->SetLineColor(4);
   g3->AddPoint(5.,1.);
   g3->AddPoint(6.,2.);

   auto g4 = new TGraph(); g4->SetLineColor(6);
   g4->AddPoint(7.,1.);
   g4->AddPoint(8.,2.);

   c1->cd(1); g1->Draw("AL*");
   c1->cd(2); g2->Draw("AL*");
   c1->cd(3); g3->Draw("AL*");
   c1->cd(4); g4->Draw("AL*");

   auto l = new TLegend(0.45,0.6,0.88,0.9);
   //l->SetTextSize(0.06); // Can be use to have a bigger text
   l->AddEntry(g1,"Legend 1");
   l->AddEntry(g2,"Legend 2");
   l->AddEntry(g3,"Legend 3");
   l->AddEntry(g4,"Legend 4");
   c1->cd(1);
   l->Draw();

   c1->cd(1)->Update();
   c1->cd(4)->Update();

   g1->GetYaxis()->SetTitle("\\sigma/\\sigma_{SM}");
   g4->GetXaxis()->SetTitle("\\mathscr{O}");
   g4->GetXaxis()->SetTitleOffset(0.4);

   g4->GetXaxis()->SetTitleSize(0.08);
   g1->GetYaxis()->SetTitleSize(0.08);
   c1->cd(1)->Modified();
   c1->cd(4)->Modified();
}

It seems that the problem comes when I try to specify legend box size and legend text size at the same time. Look at this cases:

Case 1)

legend = ROOT.TLegend(0,0,0.3,0.3)
legend.SetTextSize(0.05)

It is not well aligned.

Case 2)

legend = ROOT.TLegend(0,0,0.3,0.4)
legend.SetTextSize(0.05)

It is well aligned but too much vertical space needed.

Case 3)

legend = ROOT.TLegend()
legend.SetTextSize(0.05)

It looks fine but I cannot change possition.

As you can see Case 3) requires much less vertical space than Case 2), and if you reduce the vertical space, you get Case 1).

May be send me your macro. That will be easier than proposing solutions in my reproducer.

In the example I sent you I can do that without problems:

{
   c1 = new TCanvas("c1","Divide Example",800,400);
   c1->SetLeftMargin(0.3);
   c1->SetBottomMargin(0.1);
   c1->Divide(4,1,0,0);

   auto g1 = new TGraph(); g1->SetLineColor(2);
   g1->AddPoint(1.,1.);
   g1->AddPoint(1.8,1.2);
   g1->AddPoint(2.,2.);

   auto g2 = new TGraph(); g2->SetLineColor(3);
   g2->AddPoint(3.,1.);
   g2->AddPoint(4.,2.);

   auto g3 = new TGraph(); g3->SetLineColor(4);
   g3->AddPoint(5.,1.);
   g3->AddPoint(6.,2.);

   auto g4 = new TGraph(); g4->SetLineColor(6);
   g4->AddPoint(7.,1.);
   g4->AddPoint(8.,2.);

   c1->cd(1); g1->Draw("AL*");
   c1->cd(2); g2->Draw("AL*");
   c1->cd(3); g3->Draw("AL*");
   c1->cd(4); g4->Draw("AL*");

   auto l = new TLegend(0.45,0.6,0.88,0.9);
   l->SetTextSize(0.06); // bigger text
   l->AddEntry(g1,"Legend 1");
   l->AddEntry(g2,"Legend 2");
   l->AddEntry(g3,"Legend 3");
   l->AddEntry(g4,"Legend 4");
   c1->cd(1);
   l->Draw();

   c1->cd(1)->Update();
   c1->cd(4)->Update();

   g1->GetYaxis()->SetTitle("\\sigma/\\sigma_{SM}");
   g4->GetXaxis()->SetTitle("\\mathscr{O}");
   g4->GetXaxis()->SetTitleOffset(0.4);

   g4->GetXaxis()->SetTitleSize(0.08);
   g1->GetYaxis()->SetTitleSize(0.08);
   c1->cd(1)->Modified();
   c1->cd(4)->Modified();
}

So I tried to reduce the code as much as possible and problem is still there. Note that I used same parameters for the legend:

import ROOT


def test():

    c1 = ROOT.TCanvas()
    c1.Divide(4, 1, 0, 0)

    graphs = {}
    frames = {}

    for z in range(4):

        c1.cd(z+1)
        
        frames[z] = ROOT.gPad.DrawFrame(0., 0., 1., 1)
        
        graphs[z] = ROOT.TGraph()
        graphs[z].AddPoint(1,1)
        graphs[z].Draw("AP")

        c1.Update()

    c1.cd(1)
    legend = ROOT.TLegend(0.45,0.6,
                          0.88,0.8)
    legend.SetTextSize(0.06)
 
    legend.AddEntry(graphs[0], "legend")
    legend.AddEntry(graphs[1], "legend")
    legend.AddEntry(graphs[2], "legend")
        
    legend.Draw()
    c1.Update()
    c1.Print("aa.pdf")


test()

It seems like the vertical centering is not working as expected when there is a large empty space between entries. You can try using a larger font with the same legend box size, or a smaller box with the same font size, so that the text fills more of the vertical space; in both cases it looks better centered.

import ROOT

def test():
    c1 = ROOT.TCanvas()
    c1.Divide(4, 1, 0, 0)
    graphs = {}
    frames = {}
    for z in range(4):
        c1.cd(z+1)
        frames[z] = ROOT.gPad.DrawFrame(0., 0., 1., 1)
        graphs[z] = ROOT.TGraph()
        graphs[z].AddPoint(1,1)
        graphs[z].Draw("AP")
        c1.Update()
    c1.cd(1)
    legend1 = ROOT.TLegend(0.45,0.6,
                          0.88,0.8)
    legend1.SetTextSize(0.085)
    legend1.AddEntry(graphs[0], "legend")
    legend1.AddEntry(graphs[1], "legend")
    legend1.AddEntry(graphs[2], "legend")        
    legend1.Draw()

    c1.cd(2)
    legend2 = ROOT.TLegend(0.45,0.7,
                          0.88,0.8)
    legend2.SetTextSize(0.06)
    legend2.AddEntry(graphs[0], "legend")
    legend2.AddEntry(graphs[1], "legend")
    legend2.AddEntry(graphs[2], "legend")        
    legend2.Draw()

    c1.Update()
    c1.Print("ab.png")

test()

Now it has been solved, thanks to all, especially @dastudillo .

For the real project, I ended using

    legend = ROOT.TLegend(0.45,y0,0.88,0.8)
    legend.SetTextSize(0.06)

where y0=0.62 for three entries and y0=0.6 for four entries. Probably there is still some misalignment, but too small to be testing values (you need to focus to notice).

Should this behaviour be reported as a bug?

I do not think so because it really depends on the legend geometry. When the text size is not automatic it may occur that a correct alignment is not possible depending on the box size.

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