TLegend font size alignment problems in TCanvas.Divide with TMultigraphs

Hi,
I’m doing a TCanvas divided in four pads with TMultigraph. I only want the legend to be at one of them, I get:

from

legend = ROOT.TLegend(0.1, 0.7, 0.4, 0.9)

Since font size is too small, I do:

legend.SetTextSize(0.04)

To get:

As you can see the legend coloured lines and labels are not aligned even if I increase the size of the label in the X axis.

ROOT Version: 6.24/06 (I suppose PyROOT uses the same)
Platform: Windows Subsystem for Linux, Debian
Compiler: IDLE

I do not see this problem:

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

   c1->Divide(4,1,0,0);
   for (int i = 1; i<=4; i++) {
      c1->cd(i);
      g->Draw();
   }
   auto l = new TLegend();
   l->AddEntry(g,"Legend");
   l->Draw();
}

Maybe you are changing the alignment somewhere in the macro or ROOT configuration. You can try

legend.SetTextAlign(12)

https://root.cern/doc/master/classTAttText.html

legend.SetTextAlign(12) and nothing changes. I share the relevant code with you if that helps:

def parametrizations_together(mll_cuts, fitParameters, keys):

    ROOT.gStyle.SetTitleFontSize(0.1)
    ROOT.gStyle.SetTitleXSize(0.04)
    ROOT.gStyle.SetTitleYSize(0.04)
    c1 = ROOT.TCanvas()
    c1.Divide(4, 1, 0, 0)

    graphs = {}
    mgGraphs = {}

    minCoef = -5
    maxCoef = 5
    N = 100
    coefs = [minCoef + i*2*maxCoef/N for i in range(N)]
    coefs.append(maxCoef)

    colours = [ROOT.kRed, ROOT.kBlue, ROOT.kGreen, ROOT.kYellow]
    coloursDict = {}
    for i in range(len(keys)):
        coloursDict[keys[i]] = colours[i]

    for z in range(4):

        c1.cd(z+1)
        ROOT.gPad.DrawFrame(0., 0., 1., 1)

        mgGraphs[z] = ROOT.TMultiGraph()

        for key in keys:
            if key == "SM": continue

            currKey = key + str(z)
            graphs[currKey] = ROOT.TGraph()
            graphs[currKey].SetLineColor(coloursDict[key])

            p0 = fitParameters[key][z][0]
            p1 = fitParameters[key][z][1]
            p2 = fitParameters[key][z][2]

            for i in range(N+1):
                coef = coefs[i]
                f = p0 + p1*coef + p2*coef**2
                graphs[currKey].AddPoint(coef, f)

            mgGraphs[z].Add(graphs[currKey], "L")
            mgGraphs[z].SetTitle(mll_cuts[z])
            mgGraphs[z].GetXaxis().SetTitle("\\mathscr{O}")
            mgGraphs[z].GetYaxis().SetTitle(r"\sigma/\sigma_{SM}")

        # add (0, 1) as reference point (SM)
        graphs["SM" + str(z)] = ROOT.TGraph()
        graphs["SM" + str(z)].AddPoint(0, 1)
        graphs["SM" + str(z)].SetMarkerStyle(20)
        graphs["SM" + str(z)].SetMarkerSize(1)
        mgGraphs[z].Add(graphs["SM" + str(z)], "P")

        if z == 3:

            legend = ROOT.TLegend(0.1, 0.7, 0.4, 0.9)
            legend.SetTextSize(0.04)
            legend.SetTextAlign(12)

            for key in keys:

                legend.AddEntry(graphs[key + str(z)], key, "l")

        mgGraphs[z].GetXaxis().SetRangeUser(minCoef, maxCoef)
        mgGraphs[z].SetMinimum(0.6)
        mgGraphs[z].SetMaximum(2)
        mgGraphs[z].Draw("A")
        
        if z == 3:
            legend.Draw()

    c1.Update()

it is fine too with these additional setting:

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

   c1->Divide(4,1,0,0);
   for (int i = 1; i<=4; i++) {
      c1->cd(i);
      g->Draw();
   }
   auto l = new TLegend();
   l->AddEntry(g,"Legend");
   l->SetTextSize(0.04);
   l->SetTextAlign(12);
   l->Draw();
}

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();
}