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