ROOT の TCanvas を綺麗に並べる

大画面液晶を使用中に ROOT でたくさん TCanvas を作った際に、重ならないように自動で綺麗に並べたいとき。

ちょっと整理していないコードだけど、とりあえずこれで動く。

void TileCanvases() {
  auto canvases = gROOT->GetListOfCanvases();

  int x0 = 0;
  int y0 = 1000000000;

  int margin1 = 0;
  int margin2 = 0;

  // First get the menu bar position
  for(int i = 0; i < canvases->GetEntries(); i++) {
    auto can = (TCanvas*)canvases->At(i);

    if (i == 0) {
      margin2 = (can->GetWindowWidth() - can->GetWw())/2;
      margin1 = can->GetWindowHeight() - can->GetWh() - margin2;
    }

    if (y0 > can->GetWindowTopY()) {
      y0 = can->GetWindowTopY();
    }
  }

  int w_display = gClient->GetDisplayWidth();
  int h_display = gClient->GetDisplayHeight();
  int x1 = x0;
  int y1 = y0;
  int ymax = 0;
  for(int i = 0; i < canvases->GetEntries(); i++) {
    auto can = (TCanvas*)canvases->At(i);
    int w_can = can->GetWindowWidth();
    int h_can = can->GetWindowHeight();
    if (x1 + w_can > w_display) {
      x1 = x0; // reset the xy position to the leftmost
      y1 = ymax + margin1;
    }

    if (y1 + h_can > h_display) {
      x1 = x0 + y0 ; // reset the x position to top left
      y1 = y0 * 2;
    }

    can->SetWindowPosition(x1 - margin2, y1 - margin1);
    x1 += w_can;
    if (ymax < y1 + h_can) {
      ymax = y1 + h_can;
    }
  }
}

例えば次のようにすると、何をしているか分かるはず。

for(int i = 0; i < 10; i++) {
  int w = gRandom->Uniform(400, 600);
  int h = gRandom->Uniform(400, 600);
  auto can = new TCanvas(Form("can%d", i), Form("can%d", i), w, h);
}

TileCanvases();