diff --git a/gui/browsable/CMakeLists.txt b/gui/browsable/CMakeLists.txt index 8e49b1f508bd5..3f1cdeed9a7dc 100644 --- a/gui/browsable/CMakeLists.txt +++ b/gui/browsable/CMakeLists.txt @@ -123,6 +123,7 @@ if(root7) ROOTNTuple ROOTNTupleBrowse Gpad + ROOTTreeMap ) ROOT_LINKER_LIBRARY(ROOTNTupleDraw7Provider @@ -132,6 +133,7 @@ if(root7) ROOTNTuple ROOTNTupleBrowse ROOTGpadv7 + ROOTTreeMap ) endif() diff --git a/gui/browsable/src/RNTupleBrowseProvider.cxx b/gui/browsable/src/RNTupleBrowseProvider.cxx index 82b67cd34eecd..f89fe2e29bb58 100644 --- a/gui/browsable/src/RNTupleBrowseProvider.cxx +++ b/gui/browsable/src/RNTupleBrowseProvider.cxx @@ -1,5 +1,5 @@ /************************************************************************* - * Copyright (C) 1995-2021, Rene Brun and Fons Rademakers. * + * Copyright (C) 1995-2025, Rene Brun and Fons Rademakers. * * All rights reserved. * * * * For the licensing terms see $ROOTSYS/LICENSE. * @@ -18,22 +18,198 @@ #include "TClass.h" #include "RFieldHolder.hxx" +#include "RVisualizationHolder.hxx" +namespace ROOT::Experimental { +class TObjectDrawable; +} using namespace std::string_literals; - using namespace ROOT::Browsable; +// ============================================================================================== +/** \class RTreeMapElement +\ingroup rbrowser +\brief Browsing element representing TreeMap visualization for RNTuple +\author Patryk Pilichowski +\date 2025 +\warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback is +welcome! +*/ + +class RTreeMapElement : public RElement { +protected: + std::shared_ptr fNtplReader; + std::string fFileName; + +public: + RTreeMapElement(std::shared_ptr ntplReader, const std::string &fileName = "") + : RElement(), fNtplReader(ntplReader), fFileName(fileName) + { + } + + ~RTreeMapElement() override = default; + + /** Name of TreeMap visualization */ + std::string GetName() const override { return "TreeMap"; } + + /** Title of TreeMap visualization */ + std::string GetTitle() const override { return "TreeMap visualization of RNTuple structure and disk usage"; } + + /** No children for TreeMap visualization */ + std::unique_ptr GetChildsIter() override { return nullptr; } + + /** Return class of RNTuple for consistency */ + const TClass *GetClass() const { return TClass::GetClass(); } + + /** Return holder with visualization data */ + std::unique_ptr GetObject() override + { + return std::make_unique(fNtplReader, fFileName, fNtplReader->GetDescriptor().GetName()); + } + + /** Default action is to draw the treemap */ + EActionKind GetDefaultAction() const override { return kActDraw6; /*temporary, replace with kActDraw7; */ } + + /** Check if visualization is capable of being drawn */ + bool IsCapable(EActionKind kind) const override + { + return kind == kActDraw6; + } + + /** Create item with TreeMap icon */ + std::unique_ptr CreateItem() const override + { + auto item = std::make_unique(GetName(), 0, "sap-icon://Chart-Tree-Map"); + item->SetTitle(GetTitle()); + return item; + } + + /** Not expandable by default */ + bool IsFolder() const override { return false; } + + /** Set filename for treemap creation */ + void SetFileName(const std::string &fileName) { fFileName = fileName; } +}; // ============================================================================================== +/** \class RVisualizationElement +\ingroup rbrowser +\brief Browsing element representing visualization folder for RNTuple +\author Patryk Pilichowski +\date 2025 +\warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback is +welcome! +*/ + +class RVisualizationElement : public RElement { +protected: + std::shared_ptr fNtplReader; + std::string fFileName; + +public: + RVisualizationElement(std::shared_ptr ntplReader, const std::string &fileName = "") + : RElement(), fNtplReader(ntplReader), fFileName(fileName) + { + } + + ~RVisualizationElement() override = default; + + /** Name of visualization folder */ + std::string GetName() const override { return "Visualization"; } + + /** Title of visualization folder */ + std::string GetTitle() const override { return "Visualization tools and options for RNTuple data"; } + + /** Create iterator for visualization children */ + std::unique_ptr GetChildsIter() override; + + /** Return class of RNTuple for consistency */ + const TClass *GetClass() const { return TClass::GetClass(); } + + /** No direct object for folder */ + std::unique_ptr GetObject() override { return nullptr; } + + /** Default action is none for folder */ + EActionKind GetDefaultAction() const override { return kActNone; } + + /** Create item with visualization folder icon */ + std::unique_ptr CreateItem() const override + { + auto item = std::make_unique(GetName(), 1, "sap-icon://show"); + item->SetTitle(GetTitle()); + return item; + } + + /** This is a folder */ + bool IsFolder() const override { return true; } -/** \class RFieldElement + /** Set filename for child elements */ + void SetFileName(const std::string &fileName) { fFileName = fileName; } +}; + +// ============================================================================================== +/** \class RVisualizationIterator \ingroup rbrowser -\brief Browsing element representing of RField -\author Sergey Linev -\date 2021-03-08 -\warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback is welcome! +\brief Iterator over visualization options +\author Patryk Pilichowski +\date 2025 +\warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback is +welcome! */ +class RVisualizationIterator : public RLevelIter { + std::shared_ptr fNtplReader; + std::string fFileName; + int fCounter{-1}; + int fTotalItems{1}; + +public: + RVisualizationIterator(std::shared_ptr ntplReader, const std::string &fileName = "") + : fNtplReader(ntplReader), fFileName(fileName) + { + } + + ~RVisualizationIterator() override = default; + + bool Next() override { return ++fCounter < fTotalItems; } + + std::string GetItemName() const override + { + if (fCounter == 0) { + return "TreeMap"; + } + return ""; + } + + bool CanItemHaveChilds() const override { return false; } + + /** Create item for the browser */ + std::unique_ptr CreateItem() override + { + if (fCounter == 0) { + auto item = std::make_unique("TreeMap", 0, "sap-icon://Chart-Tree-Map"); + item->SetTitle("TreeMap visualization of RNTuple structure and disk usage"); + return item; + } + return nullptr; + } + + std::shared_ptr GetElement() override + { + if (fCounter == 0) { + return std::make_shared(fNtplReader, fFileName); + } + return nullptr; + } +}; + +std::unique_ptr RVisualizationElement::GetChildsIter() +{ + return std::make_unique(fNtplReader, fFileName); +} + +// ============================================================================================== + class RFieldElement : public RElement { protected: std::shared_ptr fNtplReader; @@ -76,7 +252,8 @@ class RFieldElement : public RElement { EActionKind GetDefaultAction() const override { auto range = fNtplReader->GetDescriptor().GetFieldIterable(fFieldId); - if (range.begin() != range.end()) return kActNone; + if (range.begin() != range.end()) + return kActNone; return kActDraw7; } @@ -87,7 +264,6 @@ class RFieldElement : public RElement { return false; } - }; // ============================================================================================== @@ -97,15 +273,17 @@ class RFieldElement : public RElement { \brief Browsing element representing of RNTuple \author Sergey Linev \date 2021-03-08 -\warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback is welcome! +\warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback is +welcome! */ class RNTupleElement : public RElement { protected: std::shared_ptr fNtplReader; + std::string fFileName; public: - RNTupleElement(const std::string &ntplName, const std::string &filename) + RNTupleElement(const std::string &ntplName, const std::string &filename) : fFileName(filename) { fNtplReader = ROOT::RNTupleReader::Open(ntplName, filename); } @@ -121,6 +299,9 @@ class RNTupleElement : public RElement { /** Title of NTuple */ std::string GetTitle() const override { return "RNTuple title"s; } + /** Get the filename */ + const std::string &GetFileName() const { return fFileName; } + /** Create iterator for childs elements if any */ std::unique_ptr GetChildsIter() override; @@ -132,86 +313,111 @@ class RNTupleElement : public RElement { item->SetTitle(GetTitle()); return item; } - - //EActionKind GetDefaultAction() const override; - - //bool IsCapable(EActionKind) const override; }; - // ============================================================================================== -/** \class RFieldsIterator +/** \class RNTupleIterator \ingroup rbrowser -\brief Iterator over RNTuple fields +\brief Iterator over RNTuple fields & visualization entry \author Sergey Linev \date 2021-03-08 -\warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback is welcome! +\warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback is +welcome! */ - -class RFieldsIterator : public RLevelIter { - +class RNTupleIterator : public RLevelIter { std::shared_ptr fNtplReader; std::vector fProvidedFieldIds; std::vector fActualFieldIds; std::string fParentName; + std::string fFileName; int fCounter{-1}; + bool fHasVisualization{false}; + int fTotalItems{0}; public: - RFieldsIterator(std::shared_ptr ntplReader, std::vector &&ids, - const std::string &parent_name = "") - : fNtplReader(ntplReader), fProvidedFieldIds(ids), fParentName(parent_name) + RNTupleIterator(std::shared_ptr ntplReader, std::vector &&ids, + const std::string &parent_name = "", bool includeVisualization = false, + const std::string &fileName = "") + : fNtplReader(ntplReader), + fProvidedFieldIds(ids), + fParentName(parent_name), + fFileName(fileName), + fHasVisualization(includeVisualization) { const auto &desc = fNtplReader->GetDescriptor(); fActualFieldIds.reserve(fProvidedFieldIds.size()); for (auto fid : fProvidedFieldIds) { fActualFieldIds.emplace_back(ROOT::Internal::GetNextBrowsableField(fid, desc)); } + + fTotalItems = fProvidedFieldIds.size(); + if (fHasVisualization) { + fTotalItems++; + } } - ~RFieldsIterator() override = default; + ~RNTupleIterator() override = default; - bool Next() override { return ++fCounter < (int)fProvidedFieldIds.size(); } + bool Next() override { return ++fCounter < fTotalItems; } std::string GetItemName() const override { - return fNtplReader->GetDescriptor().GetFieldDescriptor(fProvidedFieldIds[fCounter]).GetFieldName(); + if (fHasVisualization && fCounter == 0) { + return "Visualization"; + } + int fieldIndex = fHasVisualization ? fCounter - 1 : fCounter; + return fNtplReader->GetDescriptor().GetFieldDescriptor(fProvidedFieldIds[fieldIndex]).GetFieldName(); } bool CanItemHaveChilds() const override { - auto subrange = fNtplReader->GetDescriptor().GetFieldIterable(fActualFieldIds[fCounter]); + if (fHasVisualization && fCounter == 0) { + return true; + } + int fieldIndex = fHasVisualization ? fCounter - 1 : fCounter; + auto subrange = fNtplReader->GetDescriptor().GetFieldIterable(fActualFieldIds[fieldIndex]); return subrange.begin() != subrange.end(); } /** Create element for the browser */ std::unique_ptr CreateItem() override { + if (fHasVisualization && fCounter == 0) { + auto item = std::make_unique("Visualization", 1, "sap-icon://show"); + item->SetTitle("Visualization tools and options for RNTuple data"); + return item; + } + + int fieldIndex = fHasVisualization ? fCounter - 1 : fCounter; int nchilds = 0; - for (auto &sub : fNtplReader->GetDescriptor().GetFieldIterable(fActualFieldIds[fCounter])) { + for (auto &sub : fNtplReader->GetDescriptor().GetFieldIterable(fActualFieldIds[fieldIndex])) { (void)sub; nchilds++; } - const auto &field = fNtplReader->GetDescriptor().GetFieldDescriptor(fProvidedFieldIds[fCounter]); + const auto &field = fNtplReader->GetDescriptor().GetFieldDescriptor(fProvidedFieldIds[fieldIndex]); auto item = std::make_unique(field.GetFieldName(), nchilds, nchilds > 0 ? "sap-icon://split" : "sap-icon://e-care"); item->SetTitle("RField name "s + field.GetFieldName() + " type "s + field.GetTypeName()); - return item; } std::shared_ptr GetElement() override { - const auto name = fNtplReader->GetDescriptor().GetFieldDescriptor(fProvidedFieldIds[fCounter]).GetFieldName(); - return std::make_shared(fNtplReader, fParentName, fActualFieldIds[fCounter], name); + if (fHasVisualization && fCounter == 0) { + return std::make_shared(fNtplReader, fFileName); + } + + int fieldIndex = fHasVisualization ? fCounter - 1 : fCounter; + const auto name = fNtplReader->GetDescriptor().GetFieldDescriptor(fProvidedFieldIds[fieldIndex]).GetFieldName(); + return std::make_shared(fNtplReader, fParentName, fActualFieldIds[fieldIndex], name); } }; - std::unique_ptr RFieldElement::GetChildsIter() { std::vector ids; @@ -229,7 +435,7 @@ std::unique_ptr RFieldElement::GetChildsIter() prefix.append(fld.GetFieldName()); prefix.append("."); - return std::make_unique(fNtplReader, std::move(ids), prefix); + return std::make_unique(fNtplReader, std::move(ids), prefix); } std::unique_ptr RNTupleElement::GetChildsIter() @@ -239,10 +445,11 @@ std::unique_ptr RNTupleElement::GetChildsIter() for (auto &f : fNtplReader->GetDescriptor().GetTopLevelFields()) ids.emplace_back(f.GetId()); - if (ids.size() == 0) return nullptr; - return std::make_unique(fNtplReader, std::move(ids)); -} + if (ids.size() == 0) + return nullptr; + return std::make_unique(fNtplReader, std::move(ids), "", true, fFileName); +} // ============================================================================================== @@ -251,13 +458,13 @@ std::unique_ptr RNTupleElement::GetChildsIter() \brief Provider for browsing RNTuple classes \author Sergey Linev \date 2021-03-08 -\warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback is welcome! +\warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback is +welcome! */ class RNTupleBrowseProvider : public RProvider { public: - RNTupleBrowseProvider() { RegisterNTupleFunc([](const std::string &tuple_name, const std::string &filename) -> std::shared_ptr { diff --git a/gui/browsable/src/RNTupleDraw6Provider.cxx b/gui/browsable/src/RNTupleDraw6Provider.cxx index b353fdb67b9e5..5ff52ffca87c4 100644 --- a/gui/browsable/src/RNTupleDraw6Provider.cxx +++ b/gui/browsable/src/RNTupleDraw6Provider.cxx @@ -1,5 +1,5 @@ /************************************************************************* - * Copyright (C) 1995-2021, Rene Brun and Fons Rademakers. * + * Copyright (C) 1995-2025, Rene Brun and Fons Rademakers. * * All rights reserved. * * * * For the licensing terms see $ROOTSYS/LICENSE. * @@ -10,7 +10,7 @@ #include "TClass.h" #include "RFieldProvider.hxx" - +#include "RVisualizationProvider.hxx" // ============================================================================================== @@ -19,26 +19,40 @@ \brief Provider for RNTuple drawing on TCanvas \author Sergey Linev \date 2021-03-09 -\warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback is welcome! +\warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback is +welcome! */ -class RNTupleDraw6Provider : public RFieldProvider { +class RNTupleDraw6Provider : public RProvider { +private: + RFieldProvider fieldProvider; + RVisualizationProvider visualizationProvider; public: - RNTupleDraw6Provider() { RegisterDraw6(TClass::GetClass(), [this](TVirtualPad *pad, std::unique_ptr &obj, const std::string &opt) -> bool { - auto h1 = DrawField(dynamic_cast(obj.get())); - if (!h1) - return false; - - pad->Add(h1, opt.c_str()); - - return true; + auto visHolder = dynamic_cast(obj.get()); + if (visHolder) { + auto treeMap = visualizationProvider.CreateTreeMap(visHolder); + if (!treeMap) + return false; + + treeMap->Paint(opt.c_str()); + return true; + } + + auto fieldHolder = dynamic_cast(obj.get()); + if (fieldHolder) { + auto h1 = fieldProvider.DrawField(fieldHolder); + if (!h1) + return false; + + pad->Add(h1, opt.c_str()); + return true; + } + return false; }); } - } newRNTupleDraw6Provider; - diff --git a/gui/browsable/src/RNTupleDraw7Provider.cxx b/gui/browsable/src/RNTupleDraw7Provider.cxx index 9e4428a51020f..e4e599c37b562 100644 --- a/gui/browsable/src/RNTupleDraw7Provider.cxx +++ b/gui/browsable/src/RNTupleDraw7Provider.cxx @@ -1,5 +1,5 @@ /************************************************************************* - * Copyright (C) 1995-2021, Rene Brun and Fons Rademakers. * + * Copyright (C) 1995-2025, Rene Brun and Fons Rademakers. * * All rights reserved. * * * * For the licensing terms see $ROOTSYS/LICENSE. * @@ -12,6 +12,7 @@ #include #include "RFieldProvider.hxx" +#include "RVisualizationProvider.hxx" using namespace ROOT::Browsable; @@ -22,30 +23,48 @@ using namespace ROOT::Browsable; \brief Provider for RNTuple drawing on RCanvas \author Sergey Linev \date 2021-03-09 -\warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback is welcome! +\warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback is +welcome! */ -class RNTupleDraw7Provider : public RFieldProvider { +class RNTupleDraw7Provider : public RProvider { +private: + RFieldProvider fieldProvider; + RVisualizationProvider visualizationProvider; public: - RNTupleDraw7Provider() { RegisterDraw7(TClass::GetClass(), [this](std::shared_ptr &subpad, std::unique_ptr &obj, const std::string &opt) -> bool { - auto h1 = DrawField(dynamic_cast(obj.get())); - if (!h1) - return false; + auto visHolder = dynamic_cast(obj.get()); + if (visHolder) { + auto treeMap = visualizationProvider.CreateTreeMap(visHolder); + if (!treeMap) + return false; + + std::shared_ptr shared; + shared.reset(treeMap.release()); + + subpad->Draw(shared, opt); + return true; + } + + auto fieldHolder = dynamic_cast(obj.get()); + if (fieldHolder) { + auto h1 = fieldProvider.DrawField(fieldHolder); + if (!h1) + return false; - std::shared_ptr shared; - shared.reset(h1); + std::shared_ptr shared; + shared.reset(h1); - subpad->Draw(shared, opt); - subpad->GetCanvas()->Update(true); - return true; + subpad->Draw(shared, opt); + return true; + } + + return false; }); } - } newRNTupleDraw7Provider; - diff --git a/gui/browsable/src/RVisualizationHolder.hxx b/gui/browsable/src/RVisualizationHolder.hxx new file mode 100644 index 0000000000000..27a560cc4cb24 --- /dev/null +++ b/gui/browsable/src/RVisualizationHolder.hxx @@ -0,0 +1,47 @@ +/************************************************************************* + * Copyright (C) 1995-2025, Rene Brun and Fons Rademakers. * + * All rights reserved. * + * * + * For the licensing terms see $ROOTSYS/LICENSE. * + * For the list of contributors see $ROOTSYS/README/CREDITS. * + *************************************************************************/ + +#ifndef ROOT_RVISUALIZATIONHOLDER_HXX +#define ROOT_RVISUALIZATIONHOLDER_HXX + +#include +#include + +/** \class RVisualizationHolder +\ingroup rbrowser +\brief Holder for RNTuple visualization data +\author Patryk Pilichowski +\date 2025 +\warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback is +welcome! +*/ + +class RVisualizationHolder : public ROOT::Browsable::RHolder { +protected: + std::shared_ptr fNtplReader; + std::string fFileName; + std::string fTupleName; + +public: + RVisualizationHolder(std::shared_ptr ntplReader, const std::string &fileName, + const std::string &tupleName) + : fNtplReader(ntplReader), fFileName(fileName), fTupleName(tupleName) + { + } + + const TClass *GetClass() const override { return TClass::GetClass(); } + + /** Returns direct (temporary) object pointer */ + const void *GetObject() const override { return nullptr; } + + std::shared_ptr GetNTupleReader() const { return fNtplReader; } + const std::string &GetFileName() const { return fFileName; } + const std::string &GetTupleName() const { return fTupleName; } +}; + +#endif // ROOT_RVISUALIZATIONHOLDER_HXX diff --git a/gui/browsable/src/RVisualizationProvider.hxx b/gui/browsable/src/RVisualizationProvider.hxx new file mode 100644 index 0000000000000..2a61abbeaba2b --- /dev/null +++ b/gui/browsable/src/RVisualizationProvider.hxx @@ -0,0 +1,38 @@ +/************************************************************************* + * Copyright (C) 1995-2025, Rene Brun and Fons Rademakers. * + * All rights reserved. * + * * + * For the licensing terms see $ROOTSYS/LICENSE. * + * For the list of contributors see $ROOTSYS/README/CREDITS. * + *************************************************************************/ + +#ifndef ROOT_RVISUALIZATIONPROVIDER_HXX +#define ROOT_RVISUALIZATIONPROVIDER_HXX + +#include + +#include "RVisualizationHolder.hxx" + +#include + +/** \class RVisualizationProvider +\ingroup rbrowser +\brief Provider for RNTuple TreeMap visualization on TCanvas +\author Patryk Pilichowski +\date 2025 +\warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback is +welcome! +*/ +class RVisualizationProvider : public RProvider { +public: + /** Create TreeMap visualization for RNTuple */ + std::unique_ptr CreateTreeMap(RVisualizationHolder *holder) const + { + if (!holder) + return nullptr; + + return ROOT::Experimental::CreateTreeMapFromRNTuple(holder->GetFileName(), holder->GetTupleName()); + } +}; + +#endif // ROOT_RVISUALIZATIONPROVIDER_HXX