Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
webgui: let specify callback in Canvas::Update() call
Invoked when current canvas version delivered to all clients.
  • Loading branch information
linev committed Jul 27, 2017
commit 9dbcdbb49ea1baeeeb0c8e7c6eff43040d7f9b36
2 changes: 1 addition & 1 deletion graf2d/gpad/v7/inc/ROOT/TCanvas.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ public:
bool IsModified() const;

/// update drawing
void Update(bool async = false);
void Update(bool async = false, CanvasCallback_t callback = nullptr);

/// Save canvas in image file
void SaveAs(const std::string &filename, bool async = false, CanvasCallback_t callback = nullptr);
Expand Down
2 changes: 1 addition & 1 deletion graf2d/gpad/v7/inc/ROOT/TVirtualCanvasPainter.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public:
virtual void AddDisplayItem(TDisplayItem *item) = 0;

/// indicate that canvas changed, provides current version of the canvas
virtual void CanvasUpdated(uint64_t, bool) = 0;
virtual void CanvasUpdated(uint64_t, bool, CanvasCallback_t) = 0;

/// return true if canvas modified since last painting
virtual bool IsCanvasModified(uint64_t) const = 0;
Expand Down
6 changes: 3 additions & 3 deletions graf2d/gpad/v7/src/TCanvas.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,10 @@ bool ROOT::Experimental::TCanvas::IsModified() const
return fPainter ? fPainter->IsCanvasModified(fModified) : fModified;
}

void ROOT::Experimental::TCanvas::Update(bool async)
void ROOT::Experimental::TCanvas::Update(bool async, CanvasCallback_t callback)
{
if (fPainter)
fPainter->CanvasUpdated(fModified, async);
fPainter->CanvasUpdated(fModified, async, callback);

// SnapshotList_t lst;
// for (auto&& drw: fPrimitives) {
Expand Down Expand Up @@ -97,7 +97,7 @@ void ROOT::Experimental::TCanvas::Show(const std::string &where)
fPainter = Internal::TVirtualCanvasPainter::Create(*this, batch_mode);
if (fPainter) {
fPainter->NewDisplay(where);
fPainter->CanvasUpdated(fModified, true); // trigger async display
fPainter->CanvasUpdated(fModified, true, nullptr); // trigger async display
}
}

Expand Down
79 changes: 59 additions & 20 deletions gui/canvaspainter/v7/src/TCanvasPainter.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -173,18 +173,26 @@ class TCanvasPainter : public THttpWSHandler,
};

struct WebCommand {
std::string fId; ///<! command identifier
std::string fName; ///<! command name
std::string fArg; ///<! command arg
bool fRunning; ///<! true when command submitted
std::string fId; ///<! command identifier
std::string fName; ///<! command name
std::string fArg; ///<! command arg
bool fRunning; ///<! true when command submitted
ROOT::Experimental::CanvasCallback_t fCallback; ///<! callback function associated with command
WebCommand() : fId(), fName(), fArg(), fRunning(false), fCallback() {}
};

struct WebUpdate {
uint64_t fVersion; ///<! canvas version
ROOT::Experimental::CanvasCallback_t fCallback; ///<! callback function associated with command
WebUpdate() : fVersion(0), fCallback() {}
};

typedef std::list<WebConn> WebConnList;

typedef std::list<WebCommand> WebCommandsList;

typedef std::list<WebUpdate> WebUpdatesList;

typedef std::vector<ROOT::Experimental::Detail::TMenuItem> MenuItemsVector;

/// The canvas we are painting. It might go out of existence while painting.
Expand All @@ -201,6 +209,7 @@ class TCanvasPainter : public THttpWSHandler,
uint64_t fSnapshotVersion; ///!< version of snapshot
std::string fSnapshot; ///!< last produced snapshot
uint64_t fSnapshotDelivered; ///!< minimal version delivered to all connections
WebUpdatesList fUpdatesLst; ///!< list of callbacks for canvas update

static std::string fAddr; ///<! real http address (when assigned)
static THttpServer *gServer; ///<! server
Expand Down Expand Up @@ -234,7 +243,7 @@ class TCanvasPainter : public THttpWSHandler,
/// The painter observes it; it needs to know should the TCanvas be deleted.
TCanvasPainter(const std::string &name, const ROOT::Experimental::TCanvas &canv, bool batch_mode)
: THttpWSHandler(name.c_str(), "title"), fCanvas(canv), fBatchMode(batch_mode), fWebConn(), fDisplayList(),
fCmds(), fCmdsCnt(0), fWaitingCmdId(), fSnapshotVersion(0), fSnapshot(), fSnapshotDelivered(0)
fCmds(), fCmdsCnt(0), fWaitingCmdId(), fSnapshotVersion(0), fSnapshot(), fSnapshotDelivered(0), fUpdatesLst()
{
CreateHttpServer();
gServer->Register("/web7gui", this);
Expand All @@ -244,12 +253,13 @@ class TCanvasPainter : public THttpWSHandler,

virtual void AddDisplayItem(ROOT::Experimental::TDisplayItem *item) final;

virtual void CanvasUpdated(uint64_t, bool) override;
virtual void CanvasUpdated(uint64_t, bool, ROOT::Experimental::CanvasCallback_t) override;

virtual bool IsCanvasModified(uint64_t) const override;

/// perform special action when drawing is ready
virtual void DoWhenReady(const std::string &cmd, const std::string &arg, bool async, ROOT::Experimental::CanvasCallback_t callback) final;
virtual void DoWhenReady(const std::string &cmd, const std::string &arg, bool async,
ROOT::Experimental::CanvasCallback_t callback) final;

// open new display for the canvas
virtual void NewDisplay(const std::string &where) override;
Expand Down Expand Up @@ -380,8 +390,8 @@ void TCanvasPainter::NewDisplay(const std::string &where)

TString exec;

if (!is_native && !ic_cef && !is_qt5 && (where!="browser")) {
if (where.find("$url")!=std::string::npos) {
if (!is_native && !ic_cef && !is_qt5 && (where != "browser")) {
if (where.find("$url") != std::string::npos) {
exec = where.c_str();
exec.ReplaceAll("$url", addr);
} else {
Expand All @@ -397,13 +407,26 @@ void TCanvasPainter::NewDisplay(const std::string &where)
gSystem->Exec(exec);
}

void TCanvasPainter::CanvasUpdated(uint64_t ver, bool async)
void TCanvasPainter::CanvasUpdated(uint64_t ver, bool async, ROOT::Experimental::CanvasCallback_t callback)
{
if (ver && fSnapshotDelivered && (ver <= fSnapshotDelivered)) {
// if given canvas version was already delivered to clients, can return immediately
if (callback) callback(true);
return;
}

fSnapshotVersion = ver;
fSnapshot = CreateSnapshot(fCanvas);

CheckDataToSend();

if (callback) {
WebUpdate item;
item.fVersion = ver;
item.fCallback = callback;
fUpdatesLst.push_back(item);
}

if (!async)
WaitWhenCanvasPainted(ver);
}
Expand Down Expand Up @@ -431,7 +454,8 @@ bool TCanvasPainter::WaitWhenCanvasPainted(uint64_t ver)
return false;
}

void TCanvasPainter::DoWhenReady(const std::string &name, const std::string &arg, bool async, ROOT::Experimental::CanvasCallback_t callback)
void TCanvasPainter::DoWhenReady(const std::string &name, const std::string &arg, bool async,
ROOT::Experimental::CanvasCallback_t callback)
{
if (!async && !fWaitingCmdId.empty()) {
Error("DoWhenReady", "Fail to submit sync command when previous is still awaited - use async");
Expand All @@ -446,11 +470,13 @@ void TCanvasPainter::DoWhenReady(const std::string &name, const std::string &arg
cmd.fCallback = callback;
fCmds.push_back(cmd);

if (!async) fWaitingCmdId = cmd.fId;
if (!async)
fWaitingCmdId = cmd.fId;

CheckDataToSend();

if (async) return;
if (async)
return;

uint64_t cnt = 0;
bool had_connection = false;
Expand All @@ -467,7 +493,6 @@ void TCanvasPainter::DoWhenReady(const std::string &name, const std::string &arg
gSystem->ProcessEvents();
gSystem->Sleep((++cnt < 500) ? 1 : 100); // increase sleep interval when do very often
}

}

/////////////////////////////////////////////////////////////////////////////////////////////
Expand All @@ -476,12 +501,15 @@ void TCanvasPainter::DoWhenReady(const std::string &name, const std::string &arg

void TCanvasPainter::PopFrontCommand(bool result)
{
if (fCmds.size() == 0) return;
if (fCmds.size() == 0)
return;

// simple condition, which will be checked in waiting loop
if (!fWaitingCmdId.empty() && (fWaitingCmdId == fCmds.front().fId)) fWaitingCmdId.clear();
if (!fWaitingCmdId.empty() && (fWaitingCmdId == fCmds.front().fId))
fWaitingCmdId.clear();

if (fCmds.front().fCallback) fCmds.front().fCallback(result);
if (fCmds.front().fCallback)
fCmds.front().fCallback(result);

fCmds.pop_front();
}
Expand Down Expand Up @@ -619,15 +647,16 @@ Bool_t TCanvasPainter::ProcessWS(THttpCallArg *arg)
const char *sid = cdata + 6;
const char *separ = strchr(sid, ':');
std::string id;
if (separ) id.append(sid, separ - sid);
if (separ)
id.append(sid, separ - sid);
if (fCmds.size() == 0) {
Error("ProcessWS", "Get REPLY without command");
} else if (!fCmds.front().fRunning) {
Error("ProcessWS", "Front command is not running when get reply");
} else if (fCmds.front().fId != id) {
Error("ProcessWS", "Mismatch with front command and ID in REPLY");
} else {
bool res = FrontCommandReplied(separ+1);
bool res = FrontCommandReplied(separ + 1);
PopFrontCommand(res);
}
conn->fReady = kTRUE;
Expand Down Expand Up @@ -666,7 +695,7 @@ void TCanvasPainter::CheckDataToSend()

TString buf;

if (conn.fDrawReady && (fCmds.size()>0) && !fCmds.front().fRunning) {
if (conn.fDrawReady && (fCmds.size() > 0) && !fCmds.front().fRunning) {
WebCommand &cmd = fCmds.front();
cmd.fRunning = true;
buf = "CMD:";
Expand Down Expand Up @@ -710,6 +739,16 @@ void TCanvasPainter::CheckDataToSend()

if (fSnapshotDelivered != min_delivered) {
fSnapshotDelivered = min_delivered;

auto iter = fUpdatesLst.begin();
while (iter != fUpdatesLst.end()) {
auto curr = iter; iter++;
if (curr->fVersion <= fSnapshotDelivered) {
curr->fCallback(true);
fUpdatesLst.erase(curr);
}
}

// one could call-back canvas methods here
}
}
Expand Down
6 changes: 5 additions & 1 deletion tutorials/v7/draw_v6.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,11 @@ void draw_v6()
// canvas->Show("/usr/bin/opera"); // one could specify program name which should show canvas
// canvas->Show("firefox"); // it could be firefox, opera, chromium; canvas can be shown several times

canvas->Update(); // synchronous, wait until painting is finished
// synchronous, wait until painting is finished
canvas->Update(false, [](bool res) { std::cout << "First Update done = " << (res ? "true" : "false") << std::endl; });

// call again, should return immediately
canvas->Update(false, [](bool res) { std::cout << "Second Update done = " << (res ? "true" : "false") << std::endl; });

// request to create PNG file in asynchronous mode and specify lambda function as callback
// when request processed by the client, callback called with result value
Expand Down