Questions about TGMatrixLayout and how uniquely identifying each of an unknown number of buttons

Hi
I’m working with a project and have encountered a bit of a roadblock. This is not as much a technical problem as most of the threads here, but I hope that is ok.

One part of the project is an object keeping track of a list of properties, something that can be represented in a GUI as a filled grid of buttons. The length of the list/dimensions of the grid is determined at runtime. Each button can trigger a function targeting the equivalent point in the list.

There are 2 problems linked to this I’m struggling with. I have barely any experience with ROOT, so the answer may be trivial.

1 )
Because I have a list of buttons to be displayed in a grid, the natural choice seems to be using TGMatrixLayout. I do however don’t understand how that class is supposed to be ‘added’ to a button group (my interpretation of what it is supposed to do). It seems to be closely linked to, but not interchangeable with, TGLayoutHints. Neither is it an independent ‘frame bundle’ capable of containing buttons. None of the GUI tutorial examples had anything on it, and I found nothing searching. So how is it used?

An alternative I can see to make a m*n button grid is to make a TGVerticalFrame hosting m TGHButtonGroups, each containing n TGTextButtons. The downside of this is a more complicated structure and build (like loops in loops) and having several groups of buttons seems to cause a slight fragmentation of identifiers, something bringing us to:

2 )
The ROOTUserGuide mentions that each button in a TGButtonGroup has a unique ID (I think you could also do it without a group, but the description seems to point to it being more difficult). My question is then how do you translate/transfer a unique ID of a button being activated to a function getting the coordinate (the equivalent spot on the list)? If there was a known number of buttons you could make just as many buttons, but that is not an alternative here.
From experiments the Connect(myButton, "Clicked()","TH1",hist, "Draw(="MyVar")");, syntax (the “Draw(=“MyVar”)” part) meant to use MyVar as an input in Draw, don’t seem to work. What other ways do you have to do something like this?

Thanks in advance.

Hi,
Here is an example using different layouts:


const int p[] = {
   0,1,0,1,
   0,1,1,2,
   1,2,0,1,
   1,2,1,2
};

TGRadioButton *rb[4];

void DoRadio(const char *msg = 0)
{
   // Handle radio buttons.

   TGButton *btn = (TGButton *) gTQSender;
   Int_t id = btn->WidgetId();

   if (id >= 21 && id <= 24) {
      for (int i = 0; i < 4; i++)
         if (rb[i]->WidgetId() != id)
            rb[i]->SetState(kButtonUp);
   }
   if (msg)
      printf("%s\n", msg);
}

void test_matrix()
{
   Int_t t;
   TGMainFrame *mf = new TGMainFrame(gClient->GetRoot(), 300, 170);
   TGGroupFrame *gf = new TGGroupFrame(mf, "Operation mode");
   mf->AddFrame(gf, new TGLayoutHints(kLHintsCenterX | kLHintsCenterY));
   gf->SetLayoutManager(new TGMatrixLayout(gf, 0, 2, 10, 10));
   rb[0] = new TGRadioButton(gf, "&Normal", 21);
   rb[1] = new TGRadioButton(gf, "&Hold", 22);
   rb[2] = new TGRadioButton(gf, "&Vent", 23);
   rb[3] = new TGRadioButton(gf, "&Tare", 24);

   for (int i = 0; i < 4; ++i) {
      t = i*4;
      rb[i]->Resize(rb[i]->GetDefaultSize());
      gf->AddFrame(rb[i]);
      rb[i]->Connect("Clicked()", 0, 0, "DoRadio(=\"from matrix layout\")");
   }
   rb[0]->SetState(kButtonDown);
   mf->MapSubwindows();
   mf->Layout();
   mf->MapWindow();
   mf->Resize(300, 170);
}

void test_table()
{
   Int_t t;
   TGMainFrame *mf = new TGMainFrame(gClient->GetRoot(), 300, 170);
   TGGroupFrame *gf = new TGGroupFrame(mf, "Operation mode");
   mf->AddFrame(gf, new TGLayoutHints(kLHintsCenterX | kLHintsCenterY));
   gf->SetLayoutManager(new TGTableLayout(gf, 2, 2));
   rb[0] = new TGRadioButton(gf, "&Normal", 21);
   rb[1] = new TGRadioButton(gf, "&Hold", 22);
   rb[2] = new TGRadioButton(gf, "&Vent", 23);
   rb[3] = new TGRadioButton(gf, "&Tare", 24);

   for (int i = 0; i < 4; ++i) {
      t = i*4;
      rb[i]->Resize(rb[i]->GetDefaultSize());
      gf->AddFrame(rb[i], new TGTableLayoutHints(p[t],p[t+1],p[t+2],p[t+3],
                    kLHintsLeft | kLHintsCenterY, 3, 3, 3, 3));
      rb[i]->Connect("Clicked()", 0, 0, "DoRadio(=\"from table layout\")");
   }
   rb[0]->SetState(kButtonDown);
   mf->MapSubwindows();
   mf->Layout();
   mf->MapWindow();
   mf->Resize(300, 170);
}

void test_normal()
{
   TGMainFrame *mf = new TGMainFrame(gClient->GetRoot(), 300, 170);
   TGGroupFrame *gf = new TGGroupFrame(mf, "Operation mode", kHorizontalFrame);
   TGVerticalFrame *vf1 = new TGVerticalFrame(gf, 100, 100);
   TGVerticalFrame *vf2 = new TGVerticalFrame(gf, 100, 100);
   rb[0] = new TGRadioButton(vf1, "&Normal", 21);
   rb[1] = new TGRadioButton(vf1, "&Hold", 22);
   rb[2] = new TGRadioButton(vf2, "&Vent", 23);
   rb[3] = new TGRadioButton(vf2, "&Tare", 24);
   TGLayoutHints *lh = new TGLayoutHints(kLHintsTop | kLHintsLeft, 2, 2, 4, 0);
   vf1->AddFrame(rb[0], lh);
   vf1->AddFrame(rb[1], lh);
   vf2->AddFrame(rb[2], lh);
   vf2->AddFrame(rb[3], lh);
   
   for (int i = 0; i < 4; ++i) {
      rb[i]->Connect("Clicked()", 0, 0, "DoRadio(=\"from normal layout\")");
   }
   rb[0]->SetState(kButtonDown);
   gf->AddFrame(vf1, new TGLayoutHints(kLHintsExpandX | kLHintsExpandY, 0, 0, 2, 0));
   gf->AddFrame(vf2, new TGLayoutHints(kLHintsExpandX | kLHintsExpandY, 0, 0, 2, 0));
   mf->AddFrame(gf, new TGLayoutHints(kLHintsCenterX | kLHintsCenterY));
   mf->MapSubwindows();
   mf->Layout();
   mf->MapWindow();
   mf->Resize(300, 170);
}

void test_layouts(const char *what = "table")
{
   if (strstr(what, "table"))
      test_table();
   else if (strstr(what, "matrix"))
      test_matrix();
   else
      test_normal();
}

Thank you! That works perfectly.

Is there any specific reason you start the button id at 21 (from 0 also seems to work)?

No, you can use any integer value.