Belle II Software development
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
56using namespace Belle2;
57
58DisplayUI::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
87void 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
113void 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
124void 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
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
182void 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
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 {
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
216void 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
297void 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
312void 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
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) {
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 {
401 }
402
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
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
703void 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
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{
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
831void 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
SplitGLView * getViewPane()
return right-side pane with viewers.
Definition: DisplayUI.h:133
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
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.
A single parameter of the module.
Definition: ModuleParam.h:34
Responsible for arranging the GL viewers and providing related functionality.
Definition: SplitGLView.h:30
TEveProjectionManager * getRhoZMgr() const
return Rho-Z projection manager.
Definition: SplitGLView.h:60
TGLEmbeddedViewer * getActiveGLViewer()
return TGLEmbeddedViewer that is active right now.
Definition: SplitGLView.cc:297
InfoWidget * getInfoWidget() const
text-based info viewer.
Definition: SplitGLView.h:64
TEveProjectionManager * getRPhiMgr() const
return R-Phi projection manager.
Definition: SplitGLView.h:58
Accessor to arrays stored in the data store.
Definition: StoreArray.h:113
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