using namespace ROOT; // RDataFrame lives in here ROOT::EnableImplicitMT(); const auto poolSize = ROOT::GetThreadPoolSize(); const auto nSlots = 0 == poolSize ? 1 : poolSize; const auto nEvents = nSlots * 10000ull; RDataFrame d(nEvents); TRandom r; auto heavyWork = [&r]() { for (volatile int i = 0; i < 1000000; ++i) ; return r.Gaus(); }; auto df = d.Define("x", heavyWork); auto h = df.Histo1D({"browserHisto", "", 100, -2., 2.}, "x"); auto dfDirectory = new TMemFile("RDFResults", "RECREATE"); auto browser = new TBrowser("b", dfDirectory); auto browserPad = gPad; h.OnPartialResult(h.kOnce, [dfDirectory](TH1D &h_) { dfDirectory->Add(&h_); }); h.OnPartialResult(50, [&browserPad](TH1D &hist) { if (!browserPad) return; // in case root -b was invoked browserPad->cd(); hist.Draw(); browserPad->Update(); // This call tells ROOT to process all pending GUI events // It allows users to use the TBrowser as usual while the event-loop is running gSystem->ProcessEvents(); }); std::string progressBar; std::mutex barMutex; // Only one thread at a time can lock a mutex. Let's use this to avoid concurrent printing. const auto everyN = nSlots == 8 ? 1000 : 100ull * nSlots; const auto barWidth = nEvents / everyN; h.OnPartialResultSlot(everyN, [&barWidth, &progressBar, &barMutex](unsigned int /*slot*/, TH1D & /*partialHist*/) { std::lock_guard l(barMutex); // lock_guard locks the mutex at construction, releases it at destruction progressBar.push_back('#'); // re-print the line with the progress bar std::cout << "\r[" << std::left << std::setw(barWidth) << progressBar << ']' << std::flush; }); std::cout << "Analysis running..." << std::endl; h->Draw(); // the final, complete result will be drawn after the event-loop has completed. std::cout << "\nDone!" << std::endl; dfDirectory->Clear(); auto clone = static_cast(h->Clone()); clone->SetDirectory(nullptr); dfDirectory->Add(clone); if (!browserPad) return; // in case root -b was invoked browserPad->cd(); clone->Draw(); browserPad->Update(); gROOT->GetListOfCanvases()->Draw()