Belle II Software  release-08-01-10
DisplayUI.cc
1 /**************************************************************************
2  * basf2 (Belle II Analysis Software Framework) *
3  * Author: The Belle II Collaboration *
4  * *
5  * See git log for contributors and copyright holders. *
6  * This file is licensed under LGPL-3.0, see LICENSE.md. *
7  **************************************************************************/
8 
9 //needs to be first
10 #include <framework/core/ModuleParam.h>
11 
12 #include <framework/dataobjects/DisplayData.h>
13 #include <display/DisplayUI.h>
14 #include <display/VisualRepMap.h>
15 #include <display/BrowsableWrapper.h>
16 #include <display/SplitGLView.h>
17 #include <display/InfoWidget.h>
18 
19 #include <framework/core/InputController.h>
20 #include <framework/datastore/StoreObjPtr.h>
21 #include <framework/datastore/StoreArray.h>
22 #include <framework/dataobjects/EventMetaData.h>
23 #include <framework/logging/Logger.h>
24 #include <framework/pcore/AsyncWrapper.h>
25 
26 #include <TApplication.h>
27 #include <TCanvas.h>
28 #include <TEveBrowser.h>
29 #include <TEveManager.h>
30 #include <TEveEventManager.h>
31 #include <TEveSelection.h>
32 #include <TEveScene.h>
33 #include <TEveViewer.h>
34 #include <TGButton.h>
35 #include <TGFileBrowser.h>
36 #include <TGFrame.h>
37 #include <TGLabel.h>
38 #include <TGNumberEntry.h>
39 #include <TGMsgBox.h>
40 #include <TGFileDialog.h>
41 #include <TGInputDialog.h>
42 #include <TGTextEntry.h>
43 #include <TGLViewer.h>
44 #include <TGLWidget.h>
45 #include <TObject.h>
46 #include <TROOT.h>
47 #include <TSystem.h>
48 #include <TVirtualX.h>
49 
50 #include <sys/prctl.h>
51 #include <signal.h>
52 
53 #include <cassert>
54 
55 
56 using namespace Belle2;
57 
58 DisplayUI::DisplayUI(bool automatic, bool advance):
59  m_automatic(automatic), m_advance(advance)
60 {
61  //ensure GUI thread goes away when parent dies. (root UI loves deadlocks)
62  prctl(PR_SET_PDEATHSIG, SIGHUP);
63 
64  if (!gEve) {
65  B2INFO("Creating TEve window.");
66  TEveManager::Create(!m_automatic, "I"); //show window in interactive mode, hide file browser
67  }
68 
69  setTitle(); //set default window title
70  TEveBrowser* browser = gEve->GetBrowser();
71  browser->HideBottomTab();
72  browser->StartEmbedding(TRootBrowser::kRight);
73  m_viewPane = new SplitGLView();
74  browser->StopEmbedding();
75 
76  //Without this, our own menu bar entries are not drawn (might appear later)
77  browser->Resize(TGDimension(1200, 1000));
78 
79  m_eventData = new TEveElementList();
80 }
81 
83 {
84  delete m_timer;
85 }
86 
87 void DisplayUI::addParameter(const std::string& label, ModuleParam<bool>& param, int level)
88 {
89  Parameter p;
90  p.m_label = label;
91  p.m_param = &param;
92  p.m_level = level;
93  m_paramList.push_back(p);
94 }
95 
97 {
98  // periodically called by auto-advance timer, but we don't want to freeze UI if no events are there
99  if (!m_nextButton->IsEnabled())
100  return;
102 }
103 
105 {
106  if (!m_prevButton->IsEnabled())
107  return;
109  return;
111 }
112 
113 void DisplayUI::setTitle(const std::string& fileName)
114 {
115  assert(gEve);
116  std::string title("Belle II Event Display");
117  if (!fileName.empty())
118  title += " - " + fileName;
119 
120  TEveBrowser* browser = gEve->GetBrowser();
121  browser->SetWindowName(title.c_str());
122 }
123 
124 void DisplayUI::allowFlaggingEvents(const std::string& description)
125 {
126  std::string label = "Flag this Event";
127  if (!description.empty())
128  label += " for " + description;
129 
130  m_flagEvent = new TGCheckButton(nullptr, label.c_str());
131 }
132 
135 {
136  return m_flagEvent && (m_flagEvent->GetState() == kButtonDown);
137 }
138 
140 {
143 
145  }
146 
147  //change UI state?
148  const long numEntries = InputController::numEntries();
150  m_nextButton->SetEnabled((m_currentEntry + 1 < numEntries) or !InputController::canControlInput());
151  if (m_currentEntry != m_eventNumberWidget->GetIntNumber())
152  m_eventNumberWidget->SetIntNumber(m_currentEntry);
153  if (m_timer and InputController::canControlInput() and !m_nextButton->IsEnabled()) {
154  //reached last file entry in play mode, stop
155  togglePlayPause();
156  }
157  if (m_flagEvent)
158  m_flagEvent->SetState(kButtonUp);
159 
160  StoreObjPtr<EventMetaData> eventMetaData;
161  m_eventLabel->SetTextColor(gROOT->GetColor(kBlack));
162  if (!eventMetaData) {
163  m_eventLabel->SetText("No EventMetaData object available.");
164  } else {
165  time_t secondsSinceEpoch = eventMetaData->getTime() / 1e9;
166  //double subSecondPart = double(eventMetaData->getTime()) / 1e9 - secondsSinceEpoch;
167  char date[30] = "<Invalid time>";
168  if (secondsSinceEpoch == 0)
169  strcpy(date, "");
170  else if (auto gmt = gmtime(&secondsSinceEpoch))
171  strftime(date, 30, "<%Y-%m-%d %H:%M:%S UTC>", gmt);
172  m_eventLabel->SetText(TString::Format("Event: \t\t%u\nRun: \t\t%d\nExperiment: \t%d\n\n%s",
173  eventMetaData->getEvent(),
174  eventMetaData->getRun(), eventMetaData->getExperiment(),
175  date));
176  }
177  m_eventLabel->Resize();
178 
179  gVirtualX->SetCursor(gEve->GetBrowser()->GetId(), gVirtualX->CreateCursor(kPointer));
180 }
181 
182 void DisplayUI::goToEvent(Long_t id)
183 {
184  if (id < 0)
185  id = 0;
186  const long numEntries = InputController::numEntries();
187  if (numEntries > 0 and id >= numEntries)
188  id = numEntries - 1;
189 
190  if (m_currentEntry == id) return;
191 
192  if (!InputController::canControlInput() && m_currentEntry != id - 1) {
193  B2ERROR("Cannot switch to event " << id << ", only works in conjunction with RootInput.");
194  }
195 
196  if (numEntries > 0 && InputController::canControlInput()) {
197  B2DEBUG(100, "Switching to event " << id);
199  } else {
200  m_currentEntry++;
201  }
202  gVirtualX->SetCursor(gEve->GetBrowser()->GetId(), gVirtualX->CreateCursor(kWatch));
203  m_nextButton->SetEnabled(false);
204  m_prevButton->SetEnabled(false);
205 
206  if (m_timer)
207  m_timer->Stop(); //apparently timer only deactivates after processing event, so do it manually
208  //process remaining events to ensure redraw (only needed if called from Timeout())
209  gSystem->ProcessEvents();
210 
211  B2DEBUG(100, "exiting event loop now.");
212  //exit event loop to allow basf2 to go to next event
213  gSystem->ExitLoop();
214 }
215 
216 void DisplayUI::goToEvent(Long_t event, Long_t run, Long_t experiment)
217 {
218  const long numEntries = InputController::numEntries();
219  if (numEntries > 0 && InputController::canControlInput()) {
220  B2DEBUG(100, "Switching to event " << event << " in run " << run << ", experiment " << experiment);
221  gVirtualX->SetCursor(gEve->GetBrowser()->GetId(), gVirtualX->CreateCursor(kWatch));
222  InputController::setNextEntry(experiment, run, event);
223  }
224  B2DEBUG(100, "exiting event loop now.");
225  gSystem->ExitLoop();
226 }
227 
229 {
230  goToEvent(m_eventNumberWidget->GetIntNumber());
231 }
232 
234 {
235  if (m_timer) {
236  m_timer->Stop();
237  m_timer->Start((int)(1000.0 * m_autoAdvanceDelay->GetNumber()));
238  }
239 }
240 
242 {
243  const TString icondir(Form("%s/icons/", gSystem->Getenv("ROOTSYS")));
244  if (m_timer) {
245  //pause
246  delete m_timer;
247  m_timer = nullptr;
248  m_playPauseButton->SetPicture(gClient->GetPicture(icondir + "ed_execute.png"));
249  } else {
250  //play
251  m_timer = new TTimer();
252  const int pollIntervalMs = (int)(1000.0 * m_autoAdvanceDelay->GetNumber());
253  m_timer->Connect("Timeout()", "Belle2::DisplayUI", this, "next()");
254  m_timer->Start(pollIntervalMs);
255  m_playPauseButton->SetPicture(gClient->GetPicture(icondir + "ed_interrupt.png"));
256  }
257 }
258 
260 {
261  StoreObjPtr<EventMetaData> eventMetaData;
262  if (!eventMetaData)
263  return; //this should not actually happen.
264 
265  char returnString[256]; //magic length specified by TGInputDialog. Note that it still overwrites the stack if you paste something long enough.
266  new TGInputDialog(gEve->GetBrowser()->GetClient()->GetDefaultRoot(), gEve->GetBrowser(),
267  "Jump to event '#evt/#run/#exp':",
268  TString::Format("%u/%d/%d", eventMetaData->getEvent(), eventMetaData->getRun(), eventMetaData->getExperiment()),
269  returnString);
270  if (returnString[0] == '\0')
271  return; //cancelled
272 
273  unsigned int event, run, exp;
274  returnString[255] = '\0'; //I don't trust root to terminate the string correctly
275  if (sscanf(returnString, "%u/%u/%u", &event, &run, &exp) != 3) {
276  B2WARNING("Wrong format!");
277  return;
278  }
279  goToEvent(event, run, exp);
280 }
281 
283 {
284  if (!gEve)
285  return;
286  if (m_cumulative) {
287  gEve->AddEvent(new TEveEventManager());
288  } else {
289  if (gEve->GetCurrentEvent())
290  gEve->GetCurrentEvent()->DestroyElements();
291 
292  //make sure we delete projected events with the rest of the event scene
293  m_eventData->DestroyElements();
294  }
295 }
296 
297 void DisplayUI::selectionHandler(TEveElement* eveObj)
298 {
299  if (VisualRepMap::getInstance()->isCurrentlySelecting())
300  return;
301 
302  //B2INFO("in selection handler: " << eveObj->GetElementName());
303 
304  const TObject* representedObject = VisualRepMap::getInstance()->getDataStoreObject(eveObj);
305  if (representedObject)
306  m_viewPane->getInfoWidget()->show(representedObject);
307 
310 }
311 
312 void DisplayUI::handleEvent(Event_t* event)
313 {
314  if (event->fType == kGKeyPress) {
315  //B2DEBUG(100, "event type " << event->fType << ", code: " << event->fCode << ", state: " << event->fState);
316  switch (event->fCode) {
317  case 117: //Page Down
318  next();
319  break;
320  case 112: //Page Up
321  prev();
322  break;
323  case 65: //Space bar
324  togglePlayPause();
325  break;
326  case 47: // s
328  break;
329  case 53: //Ctrl + q
330  if (event->fState & kKeyControlMask)
331  closeAndExit();
332  break;
333  }
334  }
335 }
336 
338 {
339  if (!m_guiInitialized) {
340  makeGui();
341  if (AsyncWrapper::isAsync()) {
342  //continually check for new events and enable/disable '->' button accordingly
343  TTimer* t = new TTimer();
344  const int pollIntervalMs = 300;
345  t->Connect("Timeout()", "Belle2::DisplayUI", this, "pollNewEvents()");
346  t->Start(pollIntervalMs);
347  }
348 
349  if (m_advance) {
350  togglePlayPause();
351  }
352 
353  //import the geometry in the projection managers (only needs to be done once)
354  TEveScene* gs = gEve->GetGlobalScene();
355  TEveProjectionManager* rphiManager = getViewPane()->getRPhiMgr();
356  if (rphiManager) {
357  rphiManager->ImportElements(gs);
358  }
359  TEveProjectionManager* rhozManager = getViewPane()->getRhoZMgr();
360  if (rhozManager) {
361  rhozManager->ImportElements(gs);
362  }
363 
364  //We want to do special things when objects are selected
365  gEve->GetSelection()->Connect("SelectionAdded(TEveElement*)", "Belle2::DisplayUI", this, "selectionHandler(TEveElement*)");
366  gEve->GetSelection()->Connect("SelectionRepeated(TEveElement*)", "Belle2::DisplayUI", this, "selectionHandler(TEveElement*)");
367  }
368 
369  updateUI(); //update button state
370 
372 
373  m_eventData->AddElement(getViewPane()->getRPhiMgr()->ImportElements(gEve->GetEventScene()));
374  m_eventData->AddElement(getViewPane()->getRhoZMgr()->ImportElements(gEve->GetEventScene()));
375 
376  for (std::string name : m_hideObjects) {
377  TGListTreeItem* eventItem = gEve->GetListTree()->FindItemByPathname("Event");
378  TGListTreeItem* item = gEve->GetListTree()->FindChildByName(eventItem, name.c_str());
379  if (item) {
380  B2INFO("hiding object '" << name << "'.");
381  TEveElement* eveItem = static_cast<TEveElement*>(item->GetUserData());
382  eveItem->SetRnrSelfChildren(false, false);
383  } else {
384  B2ERROR("hideObjects: '" << name << "' not found.");
385  }
386  }
387 
388  m_reshowCurrentEvent = false;
389  if (!m_automatic) {
390  gEve->Redraw3D(false); //do not reset camera when redrawing
391 
392  //restart auto-advance timer after loading event (interval already set)
393  if (m_timer)
394  m_timer->Start(-1);
395 
396  //make display interactive
397  gApplication->Run(true); //return from Run()
398  //interactive part done, event data removed from scene
399  } else {
400  automaticEvent();
401  }
402 
403  return m_reshowCurrentEvent;
404 }
405 
407 {
408  m_guiInitialized = true;
409  TEveBrowser* browser = gEve->GetBrowser();
410  const int margin = 3;
411 
412  browser->Connect("CloseWindow()", "Belle2::DisplayUI", this, "closeAndExit()");
413 
414  //add handler for keyboard events, needs to be done for browser TGFrame as well as frames of all TGLViewers
415  browser->Connect("ProcessedEvent(Event_t*)", "Belle2::DisplayUI", this, "handleEvent(Event_t*)");
416  TEveViewerList* viewers = gEve->GetViewers();
417  TEveElement::List_ci end_it = viewers->EndChildren();
418  for (TEveElement::List_i it = viewers->BeginChildren(); it != end_it; ++it) {
419  TEveViewer* v = static_cast<TEveViewer*>(*it);
420  TGLViewer* glv = v->GetGLViewer();
421  glv->GetGLWidget()->Connect("ProcessedEvent(Event_t*)", "Belle2::DisplayUI", this, "handleEvent(Event_t*)");
422  }
423 
424  //----------Event Control
425  browser->StartEmbedding(TRootBrowser::kLeft);
426 
427  TGMainFrame* frmMain = new TGMainFrame(gClient->GetRoot(), 240, 600);
428  frmMain->SetWindowName("Event control main frame");
429  frmMain->SetCleanup(kDeepCleanup);
430 
431  const TString icondir(Form("%s/icons/", gSystem->Getenv("ROOTSYS")));
432 
433  TGGroupFrame* event_frame = new TGGroupFrame(frmMain);
434  event_frame->SetTitle("Event");
435  {
436  TGHorizontalFrame* hf = new TGHorizontalFrame(event_frame);
437  {
438  m_prevButton = new TGPictureButton(hf, gClient->GetPicture(icondir + "GoBack.gif"));
439  m_prevButton->SetToolTipText("Go to previous event (Page Up)");
440  hf->AddFrame(m_prevButton, new TGLayoutHints(kLHintsLeft | kLHintsCenterY, margin, margin, margin, margin));
441  m_prevButton->Connect("Clicked()", "Belle2::DisplayUI", this, "prev()");
442 
443  const long numEntries = InputController::numEntries();
444  m_eventNumberWidget = new TGNumberEntry(hf, 0, 4, 999, TGNumberFormat::kNESInteger,
445  TGNumberFormat::kNEANonNegative,
446  TGNumberFormat::kNELLimitMinMax,
447  0, numEntries - 1);
449  hf->AddFrame(m_eventNumberWidget, new TGLayoutHints(kLHintsLeft | kLHintsCenterY, margin, margin, margin, margin));
450  //note: parameter of ValueSet signal is _not_ the number just set.
451  m_eventNumberWidget->Connect("ValueSet(Long_t)", "Belle2::DisplayUI", this, "goToEventWidget()");
452  m_eventNumberWidget->GetNumberEntry()->Connect("ReturnPressed()", "Belle2::DisplayUI", this, "goToEventWidget()");
453 
454 
455  if (numEntries > 0) {
456  TGLabel* maxEvents = new TGLabel(hf, TString::Format("/%ld", numEntries - 1));
457  hf->AddFrame(maxEvents, new TGLayoutHints(kLHintsLeft | kLHintsCenterY, margin, margin, margin, margin));
458  }
459 
460  m_nextButton = new TGPictureButton(hf, gClient->GetPicture(icondir + "GoForward.gif"));
461  m_nextButton->SetToolTipText("Go to next event (Page Down)");
462  hf->AddFrame(m_nextButton, new TGLayoutHints(kLHintsLeft | kLHintsCenterY, margin, margin, margin, margin));
463  m_nextButton->Connect("Clicked()", "Belle2::DisplayUI", this, "next()");
464  }
465  event_frame->AddFrame(hf, new TGLayoutHints(kLHintsCenterX | kLHintsCenterY, 0, 0, 0, 0));
466 
467  hf = new TGHorizontalFrame(event_frame);
468  {
469  //const bool file = InputController::canControlInput();
470  const bool async = AsyncWrapper::isAsync();
471 
472  TGLabel* delayLabel = new TGLabel(hf, "Delay (s):");
473  hf->AddFrame(delayLabel, new TGLayoutHints(kLHintsLeft | kLHintsCenterY, margin, margin, margin, margin));
474 
475  const double valueSeconds = async ? 0.5 : 3.5;
476  m_autoAdvanceDelay = new TGNumberEntry(hf, valueSeconds, 3, 999, TGNumberFormat::kNESRealOne,
477  TGNumberFormat::kNEAPositive,
478  TGNumberFormat::kNELLimitMin,
479  0.1); //minimum
480  //m_autoAdvanceDelay->SetState(file or async);
481  hf->AddFrame(m_autoAdvanceDelay, new TGLayoutHints(kLHintsLeft | kLHintsCenterY, margin, margin, margin, margin));
482  //note: parameter of ValueSet signal is _not_ the number just set.
483  m_autoAdvanceDelay->Connect("ValueSet(Long_t)", "Belle2::DisplayUI", this, "autoAdvanceDelayChanged()");
484  m_autoAdvanceDelay->GetNumberEntry()->Connect("ReturnPressed()", "Belle2::DisplayUI", this, "autoAdvanceDelayChanged()");
485 
486  m_playPauseButton = new TGPictureButton(hf, gClient->GetPicture(icondir + "ed_execute.png"));
487  //m_playPauseButton->SetEnabled(file or async);
488  m_playPauseButton->SetToolTipText("Advance automatically to next event after the given delay.");
489  hf->AddFrame(m_playPauseButton, new TGLayoutHints(kLHintsCenterX | kLHintsCenterY, margin, margin, margin, margin));
490  m_playPauseButton->Connect("Clicked()", "Belle2::DisplayUI", this, "togglePlayPause()");
491  }
492  event_frame->AddFrame(hf, new TGLayoutHints(kLHintsCenterX | kLHintsCenterY, 0, 0, 0, 0));
493 
494  TGButton* jumpToEventButton = new TGTextButton(event_frame, "Jump to event/run/exp...");
495  jumpToEventButton->SetEnabled(InputController::canControlInput());
496  jumpToEventButton->SetToolTipText("Find a given entry identified by an event / run / experiment triplet in the current file");
497  event_frame->AddFrame(jumpToEventButton, new TGLayoutHints(kLHintsCenterX | kLHintsCenterY, margin, margin, margin, margin));
498  jumpToEventButton->Connect("Clicked()", "Belle2::DisplayUI", this, "showJumpToEventDialog()");
499 
500  m_eventLabel = new TGLabel(event_frame);
501  event_frame->AddFrame(m_eventLabel, new TGLayoutHints(kLHintsCenterX | kLHintsCenterY, margin, margin, margin, margin));
502 
503  if (m_flagEvent) {
504  TString descr = m_flagEvent->GetString();
505  delete m_flagEvent;
506  m_flagEvent = new TGCheckButton(event_frame, descr);
507  m_flagEvent->SetToolTipText("Set return value to true for this event");
508  m_flagEvent->SetState(kButtonUp);
509  event_frame->AddFrame(m_flagEvent, new TGLayoutHints(kLHintsCenterX | kLHintsCenterY, margin, margin, margin, margin));
510  }
511  }
512  frmMain->AddFrame(event_frame, new TGLayoutHints(kLHintsExpandX, 0, 0, 0, 0));
513 
514  TGGroupFrame* param_frame = new TGGroupFrame(frmMain);
515  param_frame->SetTitle("Options");
516  {
517  const int nParams = m_paramList.size();
518  for (int i = 0; i < nParams; i++) {
519  TGCheckButton* b = new TGCheckButton(param_frame, m_paramList[i].m_label.c_str(), i);
520  b->SetToolTipText(m_paramList[i].m_param->getDescription().c_str());
521  b->SetState(m_paramList[i].m_param->getValue() ? kButtonDown : kButtonUp);
522  b->Connect("Clicked()", "Belle2::DisplayUI", this, TString::Format("handleParameterChange(=%d)", i));
523  int indentation = 15 * m_paramList[i].m_level;
524  param_frame->AddFrame(b, new TGLayoutHints(kLHintsExpandX | kLHintsCenterY, indentation, margin, margin, margin));
525  }
526 
527  }
528  frmMain->AddFrame(param_frame, new TGLayoutHints(kLHintsExpandX, 0, 0, 0, 0));
529 
530  TGGroupFrame* viewer_frame = new TGGroupFrame(frmMain);
531  viewer_frame->SetTitle("Current Viewer");
532  {
533  TGHorizontalFrame* hf = new TGHorizontalFrame(viewer_frame);
534  TGButton* b = 0;
535  {
536  b = new TGTextButton(hf, "Save As...");
537  b->SetToolTipText("Save a bitmap graphic for the current viewer");
538  hf->AddFrame(b, new TGLayoutHints(kLHintsCenterX | kLHintsCenterY, margin + 1, margin, margin, margin));
539  b->Connect("Clicked()", "Belle2::DisplayUI", this, "savePicture()");
540 
541  b = new TGTextButton(hf, "Save As (High-Res)... ");
542  b->SetToolTipText("Save a bitmap graphic for the current viewer with user-specified size");
543  hf->AddFrame(b, new TGLayoutHints(kLHintsCenterX | kLHintsCenterY, margin, margin, margin, margin));
544  b->Connect("Clicked()", "Belle2::DisplayUI", this, "saveHiResPicture()");
545 
546  }
547  viewer_frame->AddFrame(hf, new TGLayoutHints(kLHintsCenterX | kLHintsCenterY, 0, 0, 0, 0));
548 
549  hf = new TGHorizontalFrame(viewer_frame);
550  {
551  b = new TGTextButton(hf, "Dock/Undock Viewer");
552  b->SetToolTipText("Move current viewer into it's own window, or back to its original position");
553  hf->AddFrame(b, new TGLayoutHints(kLHintsCenterX | kLHintsCenterY, margin, margin, margin, margin));
554  b->Connect("Clicked()", "Belle2::DisplayUI", this, "toggleUndock()");
555  }
556  viewer_frame->AddFrame(hf, new TGLayoutHints(kLHintsCenterX | kLHintsCenterY, 0, 0, 0, 0));
557  }
558  frmMain->AddFrame(viewer_frame, new TGLayoutHints(kLHintsExpandX, 0, 0, 0, 0));
559 
560 
561  TGGroupFrame* visOptionsFrame = new TGGroupFrame(frmMain);
562  visOptionsFrame->SetTitle("Visualisation Options");
563  {
564  TGHorizontalFrame* hf = new TGHorizontalFrame(visOptionsFrame);
565  {
566  TGButton* b = new TGTextButton(hf, "Dark/light colors");
567  b->SetToolTipText("Toggle background color");
568  hf->AddFrame(b, new TGLayoutHints(kLHintsCenterX | kLHintsCenterY, margin, margin, margin, margin));
569  b->Connect("Clicked()", "Belle2::DisplayUI", this, "toggleColorScheme()");
570  }
571  visOptionsFrame->AddFrame(hf, new TGLayoutHints(kLHintsCenterX | kLHintsCenterY, 0, 0, 0, 0));
572 
573  {
574  TGCheckButton* c = new TGCheckButton(visOptionsFrame, "Cumulative mode (experimental)");
575  c->SetToolTipText("Do not erase previous event, i.e. show data from multiple events. This is quite unstable and will crash sooner or later.");
576  c->SetState(m_cumulative ? kButtonDown : kButtonUp);
577  c->Connect("Clicked()", "Belle2::DisplayUI", this, "toggleCumulative()");
578  visOptionsFrame->AddFrame(c, new TGLayoutHints(kLHintsExpandX | kLHintsCenterY, 0, margin, margin, margin));
579  }
580  }
581  frmMain->AddFrame(visOptionsFrame, new TGLayoutHints(kLHintsExpandX, 0, 0, 0, 0));
582 
583  TGGroupFrame* automatisation_frame = new TGGroupFrame(frmMain);
584  automatisation_frame->SetTitle("Automatic Saving (experimental)");
585  {
586  TGHorizontalFrame* hf = new TGHorizontalFrame(automatisation_frame);
587  {
588  TGLabel* prefixLabel = new TGLabel(hf, "Prefix:");
589  hf->AddFrame(prefixLabel, new TGLayoutHints(kLHintsLeft | kLHintsCenterY, margin, margin, margin, margin));
590 
591  m_autoFileNamePrefix = new TGTextEntry(hf, "display_");
592  hf->AddFrame(m_autoFileNamePrefix, new TGLayoutHints(kLHintsLeft | kLHintsCenterY, margin, margin, margin, margin));
593  }
594  automatisation_frame->AddFrame(hf, new TGLayoutHints(kLHintsCenterX | kLHintsCenterY, 0, 0, 0, 0));
595 
596  hf = new TGHorizontalFrame(automatisation_frame);
597  {
598  TGLabel* widthLabel = new TGLabel(hf, "Width (px):");
599  hf->AddFrame(widthLabel, new TGLayoutHints(kLHintsLeft | kLHintsCenterY, margin, margin, margin, margin));
600 
601  m_autoPictureWidth = new TGNumberEntry(hf, 800, 5, 998, TGNumberFormat::kNESInteger,
602  TGNumberFormat::kNEANonNegative,
603  TGNumberFormat::kNELLimitMinMax,
604  100, 6000);
605  hf->AddFrame(m_autoPictureWidth, new TGLayoutHints(kLHintsLeft | kLHintsCenterY, margin, margin, margin, margin));
606 
607  TGButton* b = new TGTextButton(hf, "Save PNGs");
608  b->SetToolTipText("Save bitmap graphics for all further events. Cannot be aborted. (EXPERIMENTAL)");
609  hf->AddFrame(b, new TGLayoutHints(kLHintsLeft | kLHintsCenterY, margin, margin, margin, margin));
610  b->Connect("Clicked()", "Belle2::DisplayUI", this, "startAutomaticRun()");
611  }
612  automatisation_frame->AddFrame(hf, new TGLayoutHints(kLHintsCenterX | kLHintsCenterY, 0, 0, 0, 0));
613 
614  }
615  frmMain->AddFrame(automatisation_frame, new TGLayoutHints(kLHintsExpandX, 0, 0, 0, 0));
616 
617  //this will be shown at the very bottom
618  TGGroupFrame* exit_frame = new TGGroupFrame(frmMain);
619  exit_frame->SetTitle("Closing");
620  {
621  TGHorizontalFrame* hf = new TGHorizontalFrame(exit_frame);
622  {
623  TGButton* b = new TGTextButton(hf, " Exit ");
624  b->SetToolTipText("Close the display and stop basf2 after this event.");
625  hf->AddFrame(b, new TGLayoutHints(kLHintsCenterX | kLHintsCenterY, margin, margin, margin, margin));
626  b->Connect("Clicked()", "Belle2::DisplayUI", this, "closeAndExit()");
627 
628  }
629  exit_frame->AddFrame(hf, new TGLayoutHints(kLHintsCenterX | kLHintsCenterY, 0, 0, 0, 0));
630 
631  }
632  frmMain->AddFrame(exit_frame, new TGLayoutHints(kLHintsExpandX | kLHintsBottom, 0, 0, 0, 0));
633 
634  //magic to prevent the frame being empty.
635  frmMain->MapSubwindows();
636  frmMain->Resize();
637  frmMain->MapWindow();
638  browser->StopEmbedding("Event Control");
639 }
640 
642 {
643  if (id >= (int)m_paramList.size()) {
644  B2ERROR("widget ID too large!");
645  return;
646  }
647  //toggle value
648  m_paramList[id].m_param->setValue(!m_paramList[id].m_param->getValue());
649 
650  //reprocess current event
651  m_reshowCurrentEvent = true;
652  gSystem->ExitLoop();
653 }
654 
656 {
657  TEveViewerList* viewers = gEve->GetViewers();
658  TEveElement::List_ci end_it = viewers->EndChildren();
659  for (TEveElement::List_i it = viewers->BeginChildren(); it != end_it; ++it) {
660  TEveViewer* v = static_cast<TEveViewer*>(*it);
661  TGLViewer* glv = v->GetGLViewer();
662 
663  bool dark = glv->ColorSet().Background().GetColorIndex() != kWhite;
664  glv->RefLightColorSet().Background().SetColor(kWhite);
665  glv->RefDarkColorSet().Background().SetColor(kBlack);
666  if (dark)
667  glv->UseLightColorSet();
668  else
669  glv->UseDarkColorSet();
670  glv->DoDraw();
671  }
672 }
673 
675 {
676  TGLViewer* activeGLviewer = getViewPane()->getActiveGLViewer();
677  TEveViewerList* viewers = gEve->GetViewers();
678  TEveElement::List_ci end_it = viewers->EndChildren();
679  TEveViewer* activeViewer = nullptr;
680  for (TEveElement::List_i it = viewers->BeginChildren(); it != end_it; ++it) {
681  TEveViewer* v = static_cast<TEveViewer*>(*it);
682  if (v->GetGLViewer() == activeGLviewer) {
683  activeViewer = v;
684  break;
685  }
686  }
687 
688  if (!activeViewer) {
689  B2ERROR("No active viewer. Please select one by clicking on it.");
690  return;
691  }
692 
693  TEveCompositeFrameInMainFrame* packFrame = dynamic_cast<TEveCompositeFrameInMainFrame*>(activeViewer->GetEveFrame());
694  if (!packFrame) {
695  //we're docked
696  activeViewer->UndockWindow();
697  } else {
698  //we're undocked
699  packFrame->MainFrameClosed();
700  }
701 }
702 
703 void DisplayUI::savePicture(bool highres)
704 {
705  const char* filetypes[] = {
706  "PNG (bitmap)", "*.png",
707  "PDF (experimental!)", "*.pdf",
708  "All files", "*",
709  0, 0
710  };
711  TGFileInfo fi;
712  fi.fFileTypes = filetypes;
713 
714  //deleting the pointer crashes, so I'm assuming this is magically cleaned up at some point
715  new TGFileDialog(gEve->GetBrowser()->GetClient()->GetDefaultRoot(), gEve->GetBrowser(), kFDSave, &fi);
716  if (!fi.fFilename)
717  return; //cancelled
718  TGLViewer* v = getViewPane()->getActiveGLViewer();
719  bool success = false;
720  if (!highres) {
721  success = v->SavePicture(fi.fFilename);
722  } else {
723  char returnString[256];
724  new TGInputDialog(gEve->GetBrowser()->GetClient()->GetDefaultRoot(), gEve->GetBrowser(),
725  "Bitmap width (pixels) [Note: Values larger than ~5000 may cause crashes.]",
726  "4000", //default
727  returnString);
728  if (returnString[0] == '\0')
729  return; //cancelled
730  const TString t(returnString);
731  if (!t.IsDigit()) {
732  B2ERROR("Given width is not a number!");
733  return;
734  }
735  const int width = t.Atoi();
736  B2INFO("Saving bitmap (width: " << width << "px)..."); //may take a while
737  success = v->SavePictureWidth(fi.fFilename, width, false); // don't scale pixel objects
738  }
739 
740  if (success) {
741  B2INFO("Saved image in: " << fi.fFilename);
742  } else {
743  new TGMsgBox(gEve->GetBrowser()->GetClient()->GetDefaultRoot(), gEve->GetBrowser(), "Saving image failed",
744  TString::Format("Couldn't save to '%s'! Please verify you used an appropriate image file extension in the file name. Check console output for further information.",
745  fi.fFilename));
746  }
747 
748  //file dialog leaves empty box, redraw
749  gEve->FullRedraw3D(false); //do not reset camera when redrawing
750 }
751 
753 {
754  B2INFO("Starting automatic run.");
755  m_automatic = true;
756 
757  //save current event, too
758  automaticEvent();
759 
760  gSystem->ExitLoop();
761 }
762 
764 {
765  static int i = 0;
766  B2INFO("Saving event " << i);
767 
768  //force immediate redraw
769  gEve->FullRedraw3D();
770 
771  TEveViewerList* viewers = gEve->GetViewers();
772  TEveElement::List_ci end_it = viewers->EndChildren();
773  for (TEveElement::List_i it = viewers->BeginChildren(); it != end_it; ++it) {
774  TEveViewer* v = static_cast<TEveViewer*>(*it);
775  TGLViewer* glv = v->GetGLViewer();
776 
777  TString projectionName(v->GetName());
778  projectionName.ReplaceAll(" viewer", "");
779  projectionName.ReplaceAll("/", "");
780  const int width = m_autoPictureWidth->GetIntNumber();
781  const bool scalePixelObjects = false;
782  TString name = TString::Format("%s_%s_%d.png", m_autoFileNamePrefix->GetText(), projectionName.Data(), i);
783  glv->SavePictureWidth(name, width, scalePixelObjects);
784  }
785 
786  i++;
787 }
788 
790 {
791  if (!AsyncWrapper::isAsync())
792  return;
793  if (!gEve)
794  return;
795 
796  int numEvents = AsyncWrapper::numAvailableEvents();
797  bool state = m_nextButton->IsEnabled();
798  //only call SetEnabled() if state changes (interrupts UI interactions otherwise)
799  if (state != (numEvents > 0))
800  m_nextButton->SetEnabled(numEvents > 0);
801 }
802 
804 {
805  //stop event processing after current event
806  StoreObjPtr<EventMetaData> eventMetaData;
807  eventMetaData->setEndOfData();
808 
809  gSystem->ExitLoop();
810  gROOT->SetInterrupt();
811  m_cumulative = false;
812 
813  if (!gEve)
814  return;
815 
816  // avoid emittting signals at end
817  gEve->GetBrowser()->Disconnect();
818 
819  gEve->GetSelection()->Disconnect();
820  gEve->GetHighlight()->Disconnect();
821 
822  gEve->GetBrowser()->UnmapWindow();
823  gEve->GetBrowser()->SendCloseMessage();
824 
825  // This is a bit ugly to do, but it allows us to terminate the process faster and without segfaults
826  // TODO: it's possible to handle this better
827  B2INFO("The display and the basf2 process will now be gracefully terminated.");
828  exit(0);
829 }
830 
831 void DisplayUI::showUserData(const DisplayData& displayData)
832 {
833  static std::map<std::string, BrowsableWrapper*> wrapperMap;
834  for (auto& entry : wrapperMap) {
835  entry.second = NULL;
836  }
837 
838  if (!displayData.m_histograms.empty()) {
839  static TGFileBrowser* fileBrowser = NULL;
840  if (!fileBrowser) {
841  gEve->GetBrowser()->StartEmbedding(0);
842  fileBrowser = gEve->GetBrowser()->MakeFileBrowser();
843  gEve->GetBrowser()->StopEmbedding("Histograms");
844 
845  //create new tab with canvas
846  gEve->GetBrowser()->StartEmbedding(TRootBrowser::kRight);
847  TEveWindowSlot* slot = TEveWindow::CreateWindowMainFrame();
848  gEve->GetBrowser()->StopEmbedding();
849  slot->StartEmbedding();
850  new TCanvas;
851  slot->StopEmbedding("Canvas");
852  }
853 
854  //invert pad -> name map
855  const std::map<TVirtualPad*, std::string>& padMap = BrowsableWrapper::getPads();
856  std::map<std::string, TVirtualPad*> nameMap;
857  for (const auto& entry : padMap) {
858  nameMap[entry.second] = entry.first;
859  }
860 
861  for (unsigned int i = 0; i < displayData.m_histograms.size(); i++) {
862  std::string name(displayData.m_histograms.at(i)->GetName());
863  if (!wrapperMap[name])
864  wrapperMap[name] = new BrowsableWrapper(displayData.m_histograms.at(i));
865  else
866  wrapperMap[name]->setWrapped(displayData.m_histograms.at(i));
867  BrowsableWrapper* wrapper = wrapperMap[name];
868 
869  if (nameMap.find(name) != nameMap.end()) {
870  TVirtualPad* oldGpad = gPad;
871  //redraw
872  nameMap[name]->cd();
873  wrapper->Browse(fileBrowser->Browser());
874 
875  //restore state
876  oldGpad->cd();
877  }
878  fileBrowser->Add(wrapper);
879 
880  }
881  }
882 
883  for (const auto& pair : displayData.m_selectedObjects) {
884  //convert the name, index pair back into pointer
885  StoreArray<TObject> array(pair.first);
886  const TObject* obj = array[pair.second];
887  if (obj) {
888  VisualRepMap::getInstance()->select(array[pair.second]);
889  } else {
890  B2WARNING("Cannot select object " << pair.first << "[" << pair.second << "], not found. Is the array available?");
891  }
892  }
893 }
static bool isAsync()
returns true if the current process is on the receiving (async) side of an AsyncWrapper.
Definition: AsyncWrapper.h:57
static int numAvailableEvents()
Retun number of events available in the RingBuffer.
Definition: AsyncWrapper.cc:41
A wrapper for browsable objects to enable automatic redrawing.
static const std::map< TVirtualPad *, std::string > & getPads()
Get list of pads (static).
Add custom information to the display.
Definition: DisplayData.h:55
std::vector< TH1 * > m_histograms
Histograms to be shown in Eve.
Definition: DisplayData.h:111
std::vector< std::pair< std::string, unsigned int > > m_selectedObjects
List of selected objects (array name, index).
Definition: DisplayData.h:113
void clearEvent()
remove all event data in current event.
Definition: DisplayUI.cc:282
void next()
Go to next event.
Definition: DisplayUI.cc:96
TGLabel * m_eventLabel
show event/run/exp number for current event.
Definition: DisplayUI.h:216
void handleEvent(Event_t *event)
Handles keyboard shortcuts.
Definition: DisplayUI.cc:312
long m_currentEntry
Current entry id.
Definition: DisplayUI.h:177
std::vector< Parameter > m_paramList
List of run time configurable module parameters.
Definition: DisplayUI.h:195
void startAutomaticRun()
switch to automatic mode, where visualisations are saved for each event, with no interactive control.
Definition: DisplayUI.cc:752
void autoAdvanceDelayChanged()
m_autoAdvanceDelay was changed, update m_timer if enabled.
Definition: DisplayUI.cc:233
~DisplayUI()
Destructor.
Definition: DisplayUI.cc:82
void togglePlayPause()
Handle Play/Pause button clicks.
Definition: DisplayUI.cc:241
TGButton * m_prevButton
Button to switch to previous event.
Definition: DisplayUI.h:198
void closeAndExit()
Close window and exit immediately.
Definition: DisplayUI.cc:803
bool m_automatic
If true, disable interactive control and call automaticEvent() instead.
Definition: DisplayUI.h:186
bool getReturnValue() const
Return value for current event, only makes sense if allowFlaggingEvents(true) was called.
Definition: DisplayUI.cc:134
void setTitle(const std::string &fileName="")
Set title of Eve window.
Definition: DisplayUI.cc:113
DisplayUI(bool automatic=false, bool advance=false)
Constructor.
Definition: DisplayUI.cc:58
TEveElementList * m_eventData
List of event data, including projections.
Definition: DisplayUI.h:225
void automaticEvent()
The actual per-event functionality for automatic saving.
Definition: DisplayUI.cc:763
void selectionHandler(TEveElement *eveObj)
Handle special actions when objects are selected.
Definition: DisplayUI.cc:297
void makeGui()
Build the buttons for event navigation.
Definition: DisplayUI.cc:406
void pollNewEvents()
Check if new events are available, and go to next event.
Definition: DisplayUI.cc:789
TGButton * m_nextButton
Button to switch to next event.
Definition: DisplayUI.h:201
TGPictureButton * m_playPauseButton
Play / Pause button.
Definition: DisplayUI.h:210
TTimer * m_timer
Polling/auto-advance timer.
Definition: DisplayUI.h:231
void handleParameterChange(int id)
Called when one of the module parameters is changed via UI.
Definition: DisplayUI.cc:641
bool m_guiInitialized
Was GUI already built?
Definition: DisplayUI.h:180
SplitGLView * getViewPane()
return right-side pane with viewers.
Definition: DisplayUI.h:133
void toggleUndock()
dock/undock active viewer.
Definition: DisplayUI.cc:674
void updateUI()
Update UI after a new event was loaded, as well as m_currentEntry.
Definition: DisplayUI.cc:139
TGNumberEntry * m_autoAdvanceDelay
Delay for automatic advance, in seconds.
Definition: DisplayUI.h:207
void goToEvent(Long_t id)
Go to event with index id.
Definition: DisplayUI.cc:182
TGNumberEntry * m_eventNumberWidget
Event switcher with numeric entry.
Definition: DisplayUI.h:204
void addParameter(const std::string &label, ModuleParam< bool > &param, int level)
Generate UI elements so the given module parameter can be changed at run time.
Definition: DisplayUI.cc:87
void savePicture(bool highres=false)
Save the current view to a user-defined filename.
Definition: DisplayUI.cc:703
TGCheckButton * m_flagEvent
Show control for flagging events (to set module return value).
Definition: DisplayUI.h:213
void toggleColorScheme()
Toggle between light and dark color scheme for viewers.
Definition: DisplayUI.cc:655
bool startDisplay()
Start interactive display for current event.
Definition: DisplayUI.cc:337
void showUserData(const DisplayData &displayData)
Add user-defined data (histograms, etc.).
Definition: DisplayUI.cc:831
void prev()
Go to previous event.
Definition: DisplayUI.cc:104
TGNumberEntry * m_autoPictureWidth
width of saved PNGs.
Definition: DisplayUI.h:222
void goToEventWidget()
go to the event given by m_eventNumberWidget.
Definition: DisplayUI.cc:228
bool m_reshowCurrentEvent
Show current event again after startDisplay() returns?
Definition: DisplayUI.h:183
bool m_advance
If true, start advancing through the events on startup.
Definition: DisplayUI.h:189
TGTextEntry * m_autoFileNamePrefix
File name prefix (prefix + event number + "_" + projection + ".png").
Definition: DisplayUI.h:219
void showJumpToEventDialog()
Show a dialog to to enter exp, run, event numbers.
Definition: DisplayUI.cc:259
void saveHiResPicture()
alias for savePicture(true).
Definition: DisplayUI.h:130
SplitGLView * m_viewPane
pointer to right-side pane with viewers.
Definition: DisplayUI.h:228
std::vector< std::string > m_hideObjects
objects which are to be hidden (can be manually re-enabled in tree view).
Definition: DisplayUI.h:234
void allowFlaggingEvents(const std::string &description="")
Show control for flagging events (to set module return value).
Definition: DisplayUI.cc:124
bool m_cumulative
If true, DisplayModule shouldn't clear previous data (i.e.
Definition: DisplayUI.h:192
void show(const char *uri="main:", bool clearSelection=true)
Navigate to given URI.
Definition: InfoWidget.cc:93
void update()
reset for new event (try to show same object if it exists).
Definition: InfoWidget.cc:55
static std::string getCurrentFileName(bool independentPath=false)
Return name of current file in loaded chain (or empty string if none loaded).
static void setNextEntry(long entry, bool independentPath=false)
Set the file entry to be loaded the next time event() is called.
static long getCurrentEntry(bool independentPath=false)
returns the entry number currently loaded.
static long numEntries(bool independentPath=false)
Returns total number of entries in the event tree.
static bool canControlInput()
Is there an input module to be controlled.
Responsible for arranging the GL viewers and providing related functionality.
Definition: SplitGLView.h:30
InfoWidget * getInfoWidget() const
text-based info viewer.
Definition: SplitGLView.h:64
TEveProjectionManager * getRPhiMgr() const
return R-Phi projection manager.
Definition: SplitGLView.h:58
TGLEmbeddedViewer * getActiveGLViewer()
return TGLEmbeddedViewer that is active right now.
Definition: SplitGLView.cc:297
TEveProjectionManager * getRhoZMgr() const
return Rho-Z projection manager.
Definition: SplitGLView.h:60
Type-safe access to single objects in the data store.
Definition: StoreObjPtr.h:96
const TObject * getDataStoreObject(TEveElement *elem) const
Get object represented by given visual representation.
Definition: VisualRepMap.cc:56
static VisualRepMap * getInstance()
get instance pointer.
Definition: VisualRepMap.cc:36
void selectRelated(TEveElement *eveObj) const
Select related objects.
void selectOnly(const TEveElement *eveObj) const
Deselect all other objects.
void select(const TObject *object) const
Select the representation of the given object.
Definition: VisualRepMap.cc:87
Abstract base class for different kinds of events.
Wraps a module parameter that can be toggled from the UI.
Definition: DisplayUI.h:164