GSI Object Oriented Online Offline (Go4)  GO4-6.2.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
TWebCanvasFull.cxx
Go to the documentation of this file.
1 #include "TWebCanvasFull.h"
2 
3 #include "TWebMenuItem.h"
4 #include "TCanvas.h"
5 #include "TROOT.h"
6 #include "TPave.h"
7 #include "TH1.h"
8 #include "TGraph.h"
9 #include "TClass.h"
10 #include "TFrame.h"
11 #include "TBufferJSON.h"
12 #include "TError.h"
13 
14 #include <sstream>
15 
18 
19 TWebCanvasFull::TWebCanvasFull(TCanvas *c, const char *name, Int_t x, Int_t y, UInt_t width, UInt_t height) :
20  TWebCanvas(c,name,x,y,width,height)
21 {
22  printf("CREATING FULL CANVAS\n");
23 }
24 
30 
31 TObject *TWebCanvasFull::FindPrimitive(const std::string &sid, TPad *pad, TObjLink **padlnk, TPad **objpad)
32 {
33 
34  if (!pad)
35  pad = Canvas();
36 
37  std::string kind;
38  auto separ = sid.find("#");
39  long unsigned id = 0;
40  bool search_hist = false;
41 
42  if (sid == "histogram") {
43  search_hist = true;
44  } else if (separ == std::string::npos) {
45  id = std::stoul(sid);
46  } else {
47  kind = sid.substr(separ + 1);
48  id = std::stoul(sid.substr(0, separ));
49  }
50 
51  if (!search_hist && TString::Hash(&pad, sizeof(pad)) == id)
52  return pad;
53 
54  TObjLink *lnk = pad->GetListOfPrimitives()->FirstLink();
55  while (lnk) {
56  TObject *obj = lnk->GetObject();
57  if (!obj) {
58  lnk = lnk->Next();
59  continue;
60  }
61  TH1 *h1 = obj->InheritsFrom(TH1::Class()) ? static_cast<TH1 *>(obj) : nullptr;
62  TGraph *gr = obj->InheritsFrom(TGraph::Class()) ? static_cast<TGraph *>(obj) : nullptr;
63 
64  if (search_hist) {
65  if (h1) return h1;
66  if (gr) {
67  auto offset = TGraph::Class()->GetDataMemberOffset("fHistogram");
68  if (offset > 0) {
69  return *((TH1 **)((char*) gr + offset));
70  } else {
71  printf("ERROR: Cannot access fHistogram data member in TGraph\n");
72  return nullptr;
73  }
74  }
75  lnk = lnk->Next();
76  continue;
77  }
78 
79  if (TString::Hash(&obj, sizeof(obj)) == id) {
80  if (objpad)
81  *objpad = pad;
82 
83  if (gr && (kind.find("hist")==0)) {
84  // access to graph histogram
85  obj = h1 = gr->GetHistogram();
86  kind.erase(0,4);
87  if (!kind.empty() && (kind[0]=='#')) kind.erase(0,1);
88  padlnk = nullptr;
89  }
90 
91  if (h1 && (kind == "x"))
92  return h1->GetXaxis();
93  if (h1 && (kind == "y"))
94  return h1->GetYaxis();
95  if (h1 && (kind == "z"))
96  return h1->GetZaxis();
97 
98  if (!kind.empty() && (kind.compare(0,7,"member_") == 0)) {
99  auto member = kind.substr(7);
100  auto offset = obj->IsA()->GetDataMemberOffset(member.c_str());
101  if (offset > 0) {
102  TObject **mobj = (TObject **)((char*) obj + offset);
103  return *mobj;
104  }
105  return nullptr;
106  }
107 
108  if (padlnk)
109  *padlnk = lnk;
110  return obj;
111  }
112  if (h1 || gr) {
113  TIter fiter(h1 ? h1->GetListOfFunctions() : gr->GetListOfFunctions());
114  TObject *fobj = nullptr;
115  while ((fobj = fiter()) != nullptr)
116  if (TString::Hash(&fobj, sizeof(fobj)) == id) {
117  if (objpad)
118  *objpad = pad;
119  return fobj;
120  }
121  } else if (obj->InheritsFrom(TPad::Class())) {
122  obj = FindPrimitive(sid, (TPad *)obj, padlnk, objpad);
123  if (objpad && !*objpad)
124  *objpad = pad;
125  if (obj)
126  return obj;
127  }
128  lnk = lnk->Next();
129  }
130 
131  return nullptr;
132 }
133 
136 
137 Bool_t TWebCanvasFull::ProcessData(unsigned connid, const std::string &arg)
138 {
139  if (TWebCanvas::ProcessData(connid, arg))
140  return kTRUE;
141 
142  if (arg.compare(0, 8, "GETMENU:") == 0) {
143 
144  TObject *obj = FindPrimitive(arg.substr(8));
145  if (!obj)
146  obj = Canvas();
147 
148  TWebMenuItems items(arg.c_str() + 8);
149  items.PopulateObjectMenu(obj, obj->IsA());
150  std::string buf = "MENU:";
151  buf.append(TBufferJSON::ToJSON(&items, 103).Data());
152 
153  AddToSendQueue(connid, buf);
154 
155  } else if (arg.compare(0, 8, "PRIMIT6:") == 0) {
156 
157  if (IsFirstConn(connid) && !IsReadOnly()) { // only first connection can modify object
158 
159  auto opt = TBufferJSON::FromJSON<TWebObjectOptions>(arg.c_str() + 8);
160 
161  if (opt) {
162  TPad *modpad = ProcessObjectOptions(*opt, nullptr);
163 
164  // indicate that pad was modified
165  if (modpad)
166  modpad->Modified();
167  }
168  }
169 
170  } else if (arg.compare(0, 11, "PADCLICKED:") == 0) {
171 
172  auto click = TBufferJSON::FromJSON<TWebPadClick>(arg.c_str() + 11);
173 
174  if (click && IsFirstConn(connid) && !IsReadOnly()) {
175 
176  TPad *pad = dynamic_cast<TPad *>(FindPrimitive(click->padid));
177  if (pad && (pad != gPad)) {
178  Info("ProcessData", "Activate pad %s", pad->GetName());
179  gPad = pad;
180  Canvas()->SetClickSelectedPad(pad);
183  }
184 
185  if (!click->objid.empty()) {
186  TObject *selobj = FindPrimitive(click->objid);
187  Canvas()->SetClickSelected(selobj);
188  if (pad && selobj && fObjSelectSignal)
189  fObjSelectSignal(pad, selobj);
190  }
191 
192  if ((click->x >= 0) && (click->y >= 0)) {
193  if (click->dbl && fPadDblClickedSignal)
194  fPadDblClickedSignal(pad, click->x, click->y);
195  else if (fPadClickedSignal)
196  fPadClickedSignal(pad, click->x, click->y);
197  }
198  }
199 
200  } else if (arg.compare(0, 8, "OBJEXEC:") == 0) {
201 
202  auto buf = arg.substr(8);
203  auto pos = buf.find(":");
204 
205  if ((pos > 0) && IsFirstConn(connid) && !IsReadOnly()) { // only first client can execute commands
206  auto sid = buf.substr(0, pos);
207  buf.erase(0, pos + 1);
208 
209  TObject *obj = FindPrimitive(sid);
210  if (obj && !buf.empty()) {
211 
212  while (!buf.empty()) {
213  std::string sub = buf;
214  auto pos = buf.find(";;");
215  if (pos == std::string::npos) {
216  sub = buf;
217  buf.clear();
218  } else {
219  sub = buf.substr(0,pos);
220  buf = buf.substr(pos+2);
221  }
222  if (sub.empty()) continue;
223 
224  std::stringstream exec;
225  exec << "((" << obj->ClassName() << " *) " << std::hex << std::showbase << (size_t)obj << ")->" << sub << ";";
226  Info("ProcessData", "Obj %s Execute %s", obj->GetName(), exec.str().c_str());
227  gROOT->ProcessLine(exec.str().c_str());
228  }
229 
230  CheckPadModified(Canvas());
231  }
232  }
233 
234  } else if (arg.compare(0, 12, "EXECANDSEND:") == 0) {
235 
236  // execute method and send data, used by drawing projections
237 
238  std::string buf = arg.substr(12);
239  std::string reply;
240  TObject *obj = nullptr;
241 
242  auto pos = buf.find(":");
243 
244  if ((pos > 0) && IsFirstConn(connid) && !IsReadOnly()) {
245  // only first client can execute commands
246  reply = buf.substr(0, pos);
247  buf.erase(0, pos + 1);
248  pos = buf.find(":");
249  if (pos > 0) {
250  auto sid = buf.substr(0, pos);
251  buf.erase(0, pos + 1);
252  obj = FindPrimitive(sid);
253  }
254  }
255 
256  if (obj && !buf.empty() && !reply.empty()) {
257  std::stringstream exec;
258  exec << "((" << obj->ClassName() << " *) " << std::hex << std::showbase << (size_t)obj
259  << ")->" << buf << ";";
260  if (gDebug > 1)
261  Info("ProcessData", "Obj %s Exec %s", obj->GetName(), exec.str().c_str());
262 
263  Long_t res = gROOT->ProcessLine(exec.str().c_str());
264  TObject *resobj = (TObject *)res;
265  if (resobj) {
266  std::string send = reply;
267  send.append(":");
268  send.append(TBufferJSON::ToJSON(resobj, 23).Data());
269  AddToSendQueue(connid, send);
270  if (reply[0] == 'D')
271  delete resobj; // delete object if first symbol in reply is D
272  }
273  }
274 
275  }
276 
277  return kTRUE;
278 }
279 
282 
283 Bool_t TWebCanvasFull::DecodePadOptions(const std::string &msg)
284 {
285  if (msg.empty())
286  return kFALSE;
287 
288  auto arr = TBufferJSON::FromJSON<std::vector<TWebPadOptions>>(msg);
289 
290  if (!arr)
291  return kFALSE;
292 
293  for (unsigned n = 0; n < arr->size(); ++n) {
294  auto &r = arr->at(n);
295  TPad *pad = dynamic_cast<TPad *>(FindPrimitive(r.snapid));
296 
297  if (!pad)
298  continue;
299 
300  if (pad == Canvas()) AssignStatusBits(r.bits);
301 
302  if (r.active && (pad != gPad)) gPad = pad;
303 
304  pad->SetTicks(r.tickx, r.ticky);
305  pad->SetGrid(r.gridx, r.gridy);
306  if (r.logx != pad->GetLogx())
307  pad->SetLogx(r.logx);
308  if (r.logy != pad->GetLogy())
309  pad->SetLogy(r.logy);
310  if (r.logz != pad->GetLogz())
311  pad->SetLogz(r.logz);
312 
313  pad->SetLeftMargin(r.mleft);
314  pad->SetRightMargin(r.mright);
315  pad->SetTopMargin(r.mtop);
316  pad->SetBottomMargin(r.mbottom);
317 
318  if (r.ranges) {
319 
320  Double_t ux1_, ux2_, uy1_, uy2_, px1_, px2_, py1_, py2_;
321 
322  pad->GetRange(px1_, py1_, px2_, py2_);
323  pad->GetRangeAxis(ux1_, uy1_, ux2_, uy2_);
324 
325  bool same_range = (r.ux1 == ux1_) && (r.ux2 == ux2_) && (r.uy1 == uy1_) && (r.uy2 == uy2_) &&
326  (r.px1 == px1_) && (r.px2 == px2_) && (r.py1 == py1_) && (r.py2 == py2_);
327 
328  if (!same_range) {
329  pad->RangeAxis(r.ux1, r.uy1, r.ux2, r.uy2);
330 
331  pad->Range(r.px1, r.py1, r.px2, r.py2);
332 
333  if (gDebug > 1)
334  Info("DecodeAllRanges", "Change ranges for pad %s", pad->GetName());
335  }
336  }
337 
338  pad->SetPad(r.mleft, r.mbottom, 1-r.mright, 1-r.mtop);
339 
340  TH1 *hist = static_cast<TH1 *>(FindPrimitive("histogram", pad));
341 
342  if (hist) {
343  double hmin = 0, hmax = 0;
344 
345  if (r.zx1 == r.zx2)
346  hist->GetXaxis()->SetRange(0,0);
347  else
348  hist->GetXaxis()->SetRangeUser(r.zx1, r.zx2);
349 
350  if (hist->GetDimension() == 1) {
351  hmin = r.zy1;
352  hmax = r.zy2;
353  } else if (r.zy1 == r.zy2) {
354  hist->GetYaxis()->SetRange(0,0);
355  } else {
356  hist->GetYaxis()->SetRangeUser(r.zy1, r.zy2);
357  }
358 
359  if (hist->GetDimension() == 2) {
360  hmin = r.zz1;
361  hmax = r.zz2;
362  } else if (hist->GetDimension() == 3) {
363  if (r.zz1 == r.zz2) {
364  hist->GetZaxis()->SetRange(0,0);
365  } else {
366  hist->GetZaxis()->SetRangeUser(r.zz1, r.zz2);
367  }
368  }
369 
370  if (hmin == hmax) { hist->SetMinimum(); hist->SetMaximum(); }
371  else { hist->SetMinimum(hmin); hist->SetMaximum(hmax); }
372 
373  }
374 
375  for (auto &item : r.primitives)
376  ProcessObjectOptions(item, pad);
377 
378  // without special objects no need for explicit update of the pad
379  if (fHasSpecials)
380  pad->Modified(kTRUE);
381  }
382 
383  if (fUpdatedSignal) fUpdatedSignal(); // invoke signal
384 
385  return kTRUE;
386 }
387 
391 
393 {
394  TObjLink *lnk = nullptr;
395  TPad *objpad = nullptr;
396  TObject *obj = FindPrimitive(item.snapid, pad, &lnk, &objpad);
397 
398  if (item.fcust.compare("exec") == 0) {
399  auto pos = item.opt.find("(");
400  if (obj && (pos != std::string::npos) && obj->IsA()->GetMethodAllAny(item.opt.substr(0,pos).c_str())) {
401  std::stringstream exec;
402  exec << "((" << obj->ClassName() << " *) " << std::hex << std::showbase
403  << (size_t)obj << ")->" << item.opt << ";";
404  Info("ProcessObjectOptions", "Obj %s Execute %s", obj->GetName(), exec.str().c_str());
405  gROOT->ProcessLine(exec.str().c_str());
406  } else {
407  Error("ProcessObjectOptions", "Fail to execute %s for object %p %s", item.opt.c_str(), obj, obj ? obj->ClassName() : "---");
408  objpad = nullptr;
409  }
410  return objpad;
411  }
412 
413  bool modified = false;
414 
415  if (obj && lnk) {
416  auto pos = item.opt.find(";;use_"); // special coding of extra options
417  if (pos != std::string::npos) item.opt.resize(pos);
418 
419  if (gDebug > 1)
420  Info("ProcessObjectOptions", "Set draw option %s for object %s %s", item.opt.c_str(),
421  obj->ClassName(), obj->GetName());
422 
423  lnk->SetOption(item.opt.c_str());
424 
425  modified = true;
426  }
427 
428  if (item.fcust.compare("frame") == 0) {
429  if (obj && obj->InheritsFrom(TFrame::Class())) {
430  TFrame *frame = static_cast<TFrame *>(obj);
431  if (item.fopt.size() >= 4) {
432  frame->SetX1(item.fopt[0]);
433  frame->SetY1(item.fopt[1]);
434  frame->SetX2(item.fopt[2]);
435  frame->SetY2(item.fopt[3]);
436  modified = true;
437  }
438  }
439  } else if (item.fcust.compare("pave") == 0) {
440  if (obj && obj->InheritsFrom(TPave::Class())) {
441  TPave *pave = static_cast<TPave *>(obj);
442  if ((item.fopt.size() >= 4) && objpad) {
443  auto save = gPad;
444  gPad = objpad;
445 
446  // first time need to overcome init problem
447  pave->ConvertNDCtoPad();
448 
449  pave->SetX1NDC(item.fopt[0]);
450  pave->SetY1NDC(item.fopt[1]);
451  pave->SetX2NDC(item.fopt[2]);
452  pave->SetY2NDC(item.fopt[3]);
453  modified = true;
454 
455  pave->ConvertNDCtoPad();
456  gPad = save;
457  }
458  }
459  }
460 
461  return modified ? objpad : nullptr;
462 }
463 
Bool_t IsReadOnly() const override
TPad * ProcessObjectOptions(TWebObjectOptions &item, TPad *pad)
ObjectSelectSignal_t fObjSelectSignal
! signal emitted when new object selected in the pad
TWebCanvasFull(TCanvas *c, const char *name, Int_t x, Int_t y, UInt_t width, UInt_t height)
constructor
PadClickedSignal_t fPadClickedSignal
! signal emitted when simple mouse click performed on the pad
void PopulateObjectMenu(void *obj, TClass *cl)
Bool_t DecodePadOptions(const std::string &) override
Decode all pad options, which includes ranges plus objects options.
std::string fcust
custom string
std::string snapid
id of the object
std::string opt
drawing options
std::vector< double > fopt
custom float array
Bool_t fCanCreateObjects kTRUE
! indicates if canvas allowed to create extra objects for interactive painting
PadClickedSignal_t fPadDblClickedSignal
! signal emitted when simple mouse click performed on the pad
PadSignal_t fActivePadChangedSignal
! signal emitted when active pad changed in the canvas
TObject * FindPrimitive(const std::string &id, TPad *pad=nullptr, TObjLink **padlnk=nullptr, TPad **objpad=nullptr)
Class used to transport drawing options from the client.
Bool_t ProcessData(unsigned connid, const std::string &arg) override
Process reply from client, which is not processed by basic TWebCanvas.
string msg
Definition: go4init.py:11