diff --git a/core/base/v7/inc/ROOT/TDrawable.hxx b/core/base/v7/inc/ROOT/TDrawable.hxx index 9e64f532748e4132a49649f5222cf9719d3582de..7a3a69192edd72a1278c526b2dc0cba8cfbdb390 100644 --- a/core/base/v7/inc/ROOT/TDrawable.hxx +++ b/core/base/v7/inc/ROOT/TDrawable.hxx @@ -19,6 +19,9 @@ namespace ROOT { namespace Experimental { + +class TCanvas; + namespace Internal { class TVirtualCanvasPainter; @@ -31,7 +34,6 @@ class TDrawable { public: virtual ~TDrawable(); - /// Paint the object virtual void Paint(TVirtualCanvasPainter& onCanv) = 0; }; diff --git a/graf2d/gpad/CMakeLists.txt b/graf2d/gpad/CMakeLists.txt index 3b830c0a05991a05feedfd8d158bbc142eeecc68..84a14c5ff0bbc068754ab15a5dfe6aba5a2eed0c 100644 --- a/graf2d/gpad/CMakeLists.txt +++ b/graf2d/gpad/CMakeLists.txt @@ -3,11 +3,12 @@ # @author Pere Mato, CERN ############################################################################ -ROOT_GENERATE_DICTIONARY(G__Gpad *.h MODULE Gpad LINKDEF LinkDef.h OPTIONS "-writeEmptyRootPCM") - if(root7) set(root7src v7/src/) + ROOT_GLOB_HEADERS(Gpad_v7_dict_headers ${CMAKE_CURRENT_SOURCE_DIR}/v7/inc/ROOT/T*.hxx) endif() +ROOT_GENERATE_DICTIONARY(G__Gpad *.h ${Gpad_v7_dict_headers} MODULE Gpad LINKDEF LinkDef.h OPTIONS "-writeEmptyRootPCM") + ROOT_LINKER_LIBRARY(Gpad *.cxx ${root7src} G__Gpad.cxx DEPENDENCIES Graf Hist) ROOT_INSTALL_HEADERS() diff --git a/graf2d/gpad/inc/LinkDef.h b/graf2d/gpad/inc/LinkDef.h index eb6d25849948d38977d1a0dfe7ca74915d839e8d..fc13cde13c36bd34238b618247f264e3e258c679 100644 --- a/graf2d/gpad/inc/LinkDef.h +++ b/graf2d/gpad/inc/LinkDef.h @@ -33,4 +33,12 @@ #pragma link C++ class TPadPainter; #pragma link C++ class TRatioPlot+; +#ifdef ROOT7_TDisplayItem +#pragma link C++ class ROOT::Experimental::TDisplayItem+; +#pragma link C++ class std::vector<ROOT::Experimental::TDisplayItem*>+; +#pragma link C++ class ROOT::Experimental::TPadDisplayItem+; +#pragma link C++ class ROOT::Experimental::TUniqueDisplayItem<TPad>+; +#pragma link C++ class ROOT::Experimental::TOrdinaryDisplayItem<TH1>+; +#endif + #endif diff --git a/graf2d/gpad/v7/inc/ROOT/TCanvas.hxx b/graf2d/gpad/v7/inc/ROOT/TCanvas.hxx index cf9cd07404c63590130c2ec70e1636ae222723b4..70aaad9dfc2356ea6909ecc6fea7cd75ce29eec2 100644 --- a/graf2d/gpad/v7/inc/ROOT/TCanvas.hxx +++ b/graf2d/gpad/v7/inc/ROOT/TCanvas.hxx @@ -26,6 +26,10 @@ namespace ROOT { namespace Experimental { +namespace Internal { +class TCanvasSharedPtrMaker; +} + /** \class ROOT::Experimental::TCanvas Graphic container for `TDrawable`-s. Access is through TCanvasPtr. @@ -35,13 +39,16 @@ class TCanvas { public: using Primitives_t = std::vector<std::unique_ptr<Internal::TDrawable>>; -private: + private: /// Content of the pad. Primitives_t fPrimitives; /// Title of the canvas. std::string fTitle; + /// If canvas modified. + bool fModified; + /// The painter of this canvas, bootstrapping the graphics connection. /// Unmapped canvases (those that never had `Draw()` invoked) might not have /// a painter. @@ -110,12 +117,17 @@ public: /// Remove an object from the list of primitives. //TODO: void Wipe(); + void Modified() { fModified = true; } + /// Actually display the canvas. void Show() { fPainter = Internal::TVirtualCanvasPainter::Create(*this); } - /// Get the canvas's title. + /// update drawing + void Update(); + + /// Get the canvas's title. const std::string& GetTitle() const { return fTitle; } /// Set the canvas's title. diff --git a/graf2d/gpad/v7/inc/ROOT/TVirtualCanvasPainter.hxx b/graf2d/gpad/v7/inc/ROOT/TVirtualCanvasPainter.hxx index d1329b6f15f936abbf362f2ae71aae444e2691d7..48d03f95bf3bc3b0fcf754b38c77e57552435d2d 100644 --- a/graf2d/gpad/v7/inc/ROOT/TVirtualCanvasPainter.hxx +++ b/graf2d/gpad/v7/inc/ROOT/TVirtualCanvasPainter.hxx @@ -17,6 +17,8 @@ #include <memory> +#include "ROOT/TDisplayItem.hxx" + namespace ROOT { namespace Experimental { class TCanvas; @@ -42,6 +44,8 @@ protected: public: /// Default destructor. virtual ~TVirtualCanvasPainter(); + + virtual void AddDisplayItem(TDisplayItem *item) = 0; /// Loads the plugin that implements this class. static std::unique_ptr<TVirtualCanvasPainter> Create(const TCanvas& canv); diff --git a/graf2d/gpad/v7/src/TCanvas.cxx b/graf2d/gpad/v7/src/TCanvas.cxx index 8158af9e96b2532e34ca4038a5e526d8580eaf50..4a371eb0ae080881e9ff93377a538528426d1c9a 100644 --- a/graf2d/gpad/v7/src/TCanvas.cxx +++ b/graf2d/gpad/v7/src/TCanvas.cxx @@ -15,7 +15,13 @@ #include "ROOT/TCanvas.hxx" +#include "ROOT/TDrawable.hxx" +#include "ROOT/TLogger.hxx" + #include <memory> +#include <stdio.h> +#include <string.h> + namespace { static @@ -25,12 +31,29 @@ std::vector<std::shared_ptr<ROOT::Experimental::TCanvas>>& GetHeldCanvases() { } } - const std::vector<std::shared_ptr<ROOT::Experimental::TCanvas>> & ROOT::Experimental::TCanvas::GetCanvases() { return GetHeldCanvases(); } + +//void ROOT::Experimental::TCanvas::Paint() { +// for (auto&& drw: fPrimitives) { +// drw->Paint(*this); +// } +// } + + +void ROOT::Experimental::TCanvas::Update() { + // SnapshotList_t lst; + // for (auto&& drw: fPrimitives) { + // TSnapshot *snap = drw->CreateSnapshot(*this); + // lst.push_back(std::unique_ptr<TSnapshot>(snap)); + // } +} + + + std::shared_ptr<ROOT::Experimental::TCanvas> ROOT::Experimental::TCanvas::Create(const std::string& title) { auto pCanvas = std::make_shared<TCanvas>(); diff --git a/gui/canvaspainter/CMakeLists.txt b/gui/canvaspainter/CMakeLists.txt index cdece3d85c1e112b3da733592151ffd61dfb1798..fee6f5d4f0c2e34e7049039cf97387530a570f79 100644 --- a/gui/canvaspainter/CMakeLists.txt +++ b/gui/canvaspainter/CMakeLists.txt @@ -4,4 +4,4 @@ set(libname ROOTCanvasPainter) -ROOT_LINKER_LIBRARY(${libname} *.cxx v7/src/ DEPENDENCIES Gpad) +ROOT_LINKER_LIBRARY(${libname} *.cxx v7/src/ DEPENDENCIES Gpad RHTTP) diff --git a/gui/canvaspainter/v7/src/TCanvasPainter.cxx b/gui/canvaspainter/v7/src/TCanvasPainter.cxx index b082df213c3681d35c40f45faab2b9c0e022bb06..f6ebc6e457ae1c1d99d97c066192d95bd9247c2d 100644 --- a/gui/canvaspainter/v7/src/TCanvasPainter.cxx +++ b/gui/canvaspainter/v7/src/TCanvasPainter.cxx @@ -16,10 +16,21 @@ #include "ROOT/TVirtualCanvasPainter.hxx" #include "ROOT/TCanvas.hxx" #include <ROOT/TLogger.hxx> +#include <ROOT/TDisplayItem.hxx> #include <memory> #include <string> #include <vector> +#include <list> + +#include "THttpEngine.h" +#include "THttpServer.h" +#include "TSystem.h" +#include "TList.h" +#include "TRandom.h" +#include "TPad.h" +#include "TROOT.h" +#include "TBufferJSON.h" namespace { @@ -27,23 +38,68 @@ namespace { Handles TCanvas communication with THttpServer. */ -class TCanvasPainter: public ROOT::Experimental::Internal::TVirtualCanvasPainter /*, THttpSocketListener*/ { +class TCanvasPainter: public THttpWSHandler, + public ROOT::Experimental::Internal::TVirtualCanvasPainter /*, THttpSocketListener*/ { private: + + struct WebConn { + THttpWSEngine *fHandle; ///<! websocket handle + Bool_t fReady; + UInt_t fGetMenu; ///<! object id for menu request + Bool_t fModified; + WebConn() : fHandle(0), fReady(kFALSE), fGetMenu(0), fModified(kFALSE) {} + }; + + typedef std::list<WebConn> WebConnList; + /// The canvas we are painting. It might go out of existence while painting. const ROOT::Experimental::TCanvas& fCanvas; + + WebConnList fWebConn; ///<! connections list + ROOT::Experimental::TPadDisplayItem fDisplayList; ///!< full list of items to display + + static std::string fAddr; + static THttpServer *gServer; + + + /// Disable copy construction. TCanvasPainter(const TCanvasPainter&) = delete; + /// Disable assignment. TCanvasPainter& operator=(const TCanvasPainter&) = delete; + + void CreateHttpServer(); + void PopupBrowser(); + void CheckModifiedFlag(); + std::string CreateSnapshot(const ROOT::Experimental::TCanvas& can); + + /// Send the canvas primitives to the THttpServer. - void SendCanvas(); + // void SendCanvas(); + + virtual Bool_t ProcessWS(THttpCallArg *arg); + public: /// Create a TVirtualCanvasPainter for the given canvas. /// The painter observes it; it needs to know should the TCanvas be deleted. - TCanvasPainter(const ROOT::Experimental::TCanvas& canv): fCanvas(canv) {} + TCanvasPainter(const std::string &name, const ROOT::Experimental::TCanvas& canv): + THttpWSHandler(name.c_str(), "title"), + fCanvas(canv), + fWebConn() + { + CreateHttpServer(); + gServer->Register("/web7gui", this); + PopupBrowser(); + } + + + virtual void AddDisplayItem(ROOT::Experimental::TDisplayItem *item) final; + + // void ReactToSocketNews(...) override { SendCanvas(); } @@ -56,7 +112,7 @@ public: /// Create a new TCanvasPainter to paint the given TCanvas. std::unique_ptr<TVirtualCanvasPainter> Create(const ROOT::Experimental::TCanvas& canv) const override { - return std::make_unique<TCanvasPainter>(canv); + return std::make_unique<TCanvasPainter>("name", canv); } ~GeneratorImpl() = default; @@ -76,6 +132,8 @@ public: }; }; +std::string TCanvasPainter::fAddr = ""; +THttpServer *TCanvasPainter::gServer = 0; /** \class TCanvasPainterReg @@ -95,9 +153,191 @@ struct TCanvasPainterReg { } // unnamed namespace + void TCanvasPainter::CreateHttpServer() + { + if (gServer) return; -void TCanvasPainter::SendCanvas() { - for (auto &&drawable: fCanvas.GetPrimitives()) { - drawable->Paint(*this); - } + // gServer = new THttpServer("http:8080?loopback&websocket_timeout=10000"); + const char *port = gSystem->Getenv("WEBGUI_PORT"); + TString buf; + if (!port) { + gRandom->SetSeed(0); + buf.Form("%d", (int) (8800 + 1000* gRandom->Rndm(1))); + port = buf.Data(); // "8181"; + } + fAddr = TString::Format("http://localhost:%s", port).Data(); + gServer = new THttpServer(TString::Format("http:%s?websocket_timeout=10000", port).Data()); + } + + + void TCanvasPainter::PopupBrowser() + { + TString addr, exec; + + addr.Form("%s/web7gui/%s/draw.htm?webcanvas", fAddr.c_str(), GetName()); + + if (gSystem->InheritsFrom("TMacOSXSystem")) + exec.Form("open %s", addr.Data()); + else + exec.Form("xdg-open %s &", addr.Data()); + printf("Exec %s\n", exec.Data()); + + gSystem->Exec(exec); + } + + Bool_t TCanvasPainter::ProcessWS(THttpCallArg *arg) + { + if (!arg) return kTRUE; + + // try to identify connection for given WS request + WebConn* conn = 0; + WebConnList::iterator iter = fWebConn.begin(); + while (iter != fWebConn.end()) { + if (iter->fHandle && (iter->fHandle->GetId() == arg->GetWSId()) && arg->GetWSId()) { + conn = &(*iter); break; + } + ++iter; + } + + if (strcmp(arg->GetMethod(),"WS_CONNECT")==0) { + + // accept all requests, in future one could limit number of connections + // arg->Set404(); // refuse connection + return kTRUE; + } + + if (strcmp(arg->GetMethod(),"WS_READY")==0) { + THttpWSEngine* wshandle = dynamic_cast<THttpWSEngine*> (arg->TakeWSHandle()); + + if (conn != 0) Error("ProcessWSRequest","WSHandle with given websocket id exists!!!"); + + WebConn newconn; + newconn.fHandle = wshandle; + newconn.fModified = kTRUE; + + fWebConn.push_back(newconn); + printf("socket is ready\n"); + + return kTRUE; + } + + if (strcmp(arg->GetMethod(),"WS_CLOSE")==0) { + // connection is closed, one can remove handle + + if (conn && conn->fHandle) { + conn->fHandle->ClearHandle(); + delete conn->fHandle; + conn->fHandle = 0; + } + + if (conn) fWebConn.erase(iter); + return kTRUE; + } + + if (strcmp(arg->GetMethod(),"WS_DATA")!=0) { + Error("ProcessWSRequest","WSHandle DATA request expected!"); + return kFALSE; + } + + + if (!conn) { + Error("ProcessWSRequest","Get websocket data without valid connection - ignore!!!"); + return kFALSE; + } + + if (conn->fHandle->PreviewData(arg)) return kTRUE; + + const char* cdata = (arg->GetPostDataLength()<=0) ? "" : (const char*) arg->GetPostData(); + + if (strncmp(cdata,"READY",5)==0) { + conn->fReady = kTRUE; + CheckModifiedFlag(); + } else + if (strncmp(cdata, "RREADY:", 7)==0) { + conn->fReady = kTRUE; + CheckModifiedFlag(); + } + + return kTRUE; + } + +void TCanvasPainter::CheckModifiedFlag() +{ + + for (WebConnList::iterator citer = fWebConn.begin(); citer != fWebConn.end(); ++citer) { + WebConn& conn = *citer; + + if (!conn.fReady || !conn.fHandle) continue; + + TString buf; + + if (conn.fGetMenu) { + + conn.fGetMenu = 0; + } else + if (conn.fModified) { + // buf = "JSON"; + // buf += TBufferJSON::ConvertToJSON(Canvas(), 3); + + buf = "SNAP"; + buf += CreateSnapshot(fCanvas); + conn.fModified = kFALSE; + } + + if (buf.Length() > 0) { + // sending of data can be moved into separate thread - not to block user code + conn.fReady = kFALSE; + conn.fHandle->SendCharStar(buf.Data()); + } + } +} + +void TCanvasPainter::AddDisplayItem(ROOT::Experimental::TDisplayItem *item) +{ + fDisplayList.Add(item); } + +std::string TCanvasPainter::CreateSnapshot(const ROOT::Experimental::TCanvas& can) +{ + + fDisplayList.Clear(); + + fDisplayList.SetObjectIDAsPtr((void*)&can); + + TPad *dummy = new TPad(); // just to keep information we need for different ranges now + + auto *snap = new ROOT::Experimental::TUniqueDisplayItem<TPad>(dummy); + snap->SetObjectIDAsPtr((void*)&can); + fDisplayList.Add(snap); + + for (auto &&drawable: can.GetPrimitives()) { + + drawable->Paint(*this); + + fDisplayList.Last()->SetObjectIDAsPtr(&(*drawable)); + + // ROOT::Experimental::TDisplayItem *sub = drawable->CreateSnapshot(can); + // if (!sub) continue; + // sub->SetObjectIDAsPtr(&(*drawable)); + // lst.Add(sub); + } + + TString res = TBufferJSON::ConvertToJSON(&fDisplayList, gROOT->GetClass("ROOT::Experimental::TPadDisplayItem")); + + + fDisplayList.Clear(); + + // printf("JSON %s\n", res.Data()); + + return std::string(res.Data()); +} + + + + + +//void TCanvasPainter::SendCanvas() { +// for (auto &&drawable: fCanvas.GetPrimitives()) { +// drawable->Paint(*this); +// } +//} diff --git a/hist/hist/inc/LinkDef.h b/hist/hist/inc/LinkDef.h index ba2a3b6ef0207c59d07cb979bb6ca502f3f77a07..e3c8779d63f61ea48518127ee72437c81f49f3b5 100644 --- a/hist/hist/inc/LinkDef.h +++ b/hist/hist/inc/LinkDef.h @@ -354,6 +354,7 @@ #pragma link C++ class ROOT::Experimental::TAxisEquidistant+; #pragma link C++ class ROOT::Experimental::TAxisIrregular+; #pragma link C++ class ROOT::Experimental::TAxisBase+; +#pragma link C++ class ROOT::Experimental::TOrdinarySnapshot<TH1>+; #endif #endif diff --git a/hist/hist/v7/inc/ROOT/THistDrawable.hxx b/hist/hist/v7/inc/ROOT/THistDrawable.hxx index aa93f9b442cb43219ee97f2a0c2abb866de442dc..fa59ba9727493e00860bf4438ef78489c4499bc2 100644 --- a/hist/hist/v7/inc/ROOT/THistDrawable.hxx +++ b/hist/hist/v7/inc/ROOT/THistDrawable.hxx @@ -67,13 +67,14 @@ protected: std::unique_ptr<TH1> fOldHist; public: - TH1* GetOldHist() const { return fOldHist.get(); } + TH1* GetOldHist() const { return fOldHist.get(); } THistDrawableBase(); THistDrawableBase(THistDrawableBase&&); virtual ~THistDrawableBase(); THistDrawableBase& operator=(THistDrawableBase&&); + }; template <int DIMENSIONS> @@ -102,6 +103,7 @@ public: if (UpdateOldHist()) THistPainterBase<DIMENSIONS>::GetPainter()->Paint(*this, fOpts, canv); } + }; extern template class THistDrawable<1>; diff --git a/hist/histpainter/v7/src/THistPainter.cxx b/hist/histpainter/v7/src/THistPainter.cxx index 0c63617e131cb3852edb731befc6c4a7fb4c704c..623304deca3317f31736ca7d0306cd4aece134b7 100644 --- a/hist/histpainter/v7/src/THistPainter.cxx +++ b/hist/histpainter/v7/src/THistPainter.cxx @@ -15,6 +15,8 @@ //#include "ROOT/THistPainter.hxx" see ROOT/THistDrawable.h #include "ROOT/THistDrawable.hxx" +#include "ROOT/TVirtualCanvasPainter.hxx" +#include "ROOT/TDisplayItem.hxx" #include "TH1.h" #include <iostream> @@ -37,11 +39,17 @@ public: class THistPainter2D: public THistPainterBase<2> { public: void Paint(TDrawable& drw, THistDrawOptions<2> /*opts*/, - TVirtualCanvasPainter& /* canv */) final { + TVirtualCanvasPainter& canv) final { std::cout << "Painting histogram @" << &drw << '\n'; assert(dynamic_cast<THistDrawable<2>*>(&drw) && "Wrong drawable type"); THistDrawable<2>& hd = static_cast<THistDrawable<2>&>(drw); - hd.GetOldHist()->Paint("BOX"); + + ROOT::Experimental::TDisplayItem *res = new TOrdinaryDisplayItem<TH1>(hd.GetOldHist()); + res->SetOption("col"); + + canv.AddDisplayItem(res); + + //hd.GetOldHist()->Paint("BOX"); } virtual ~THistPainter2D() final {} }; diff --git a/tutorials/v7/draw.cxx b/tutorials/v7/draw.cxx index 1d31354a98c8eb743e6bc72e8e89b614b3ac527d..f1c124c6d6bad763a056bb7b37a22a36c263aca5 100644 --- a/tutorials/v7/draw.cxx +++ b/tutorials/v7/draw.cxx @@ -44,4 +44,6 @@ void draw() { // Create a canvas to be displayed. auto canvas = Experimental::TCanvas::Create("Canvas Title"); canvas->Draw(pHist); + + canvas->Show(); }