Skip to content
Snippets Groups Projects
Commit 8af6eb98 authored by Axel Naumann's avatar Axel Naumann
Browse files

Introducing TCanvasPainter for web canvas. From Sergey Linev!

parent 7a2f8513
No related branches found
No related tags found
No related merge requests found
......@@ -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;
};
......
......@@ -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()
......@@ -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
......@@ -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.
......
......@@ -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);
......
......@@ -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>();
......
......@@ -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)
......@@ -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);
// }
//}
......@@ -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
......@@ -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>;
......
......@@ -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 {}
};
......
......@@ -44,4 +44,6 @@ void draw() {
// Create a canvas to be displayed.
auto canvas = Experimental::TCanvas::Create("Canvas Title");
canvas->Draw(pHist);
canvas->Show();
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment