Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
TWebCanvas.cxx
Go to the documentation of this file.
1// Author: Sergey Linev, GSI 7/12/2016
2
3/*************************************************************************
4 * Copyright (C) 1995-2023, Rene Brun and Fons Rademakers. *
5 * All rights reserved. *
6 * *
7 * For the licensing terms see $ROOTSYS/LICENSE. *
8 * For the list of contributors see $ROOTSYS/README/CREDITS. *
9 *************************************************************************/
10
11#include "TWebCanvas.h"
12
13#include "TWebSnapshot.h"
14#include "TWebPadPainter.h"
15#include "TWebMenuItem.h"
17#include "THttpServer.h"
18
19#include "TSystem.h"
20#include "TStyle.h"
21#include "TCanvas.h"
22#include "TButton.h"
23#include "TSlider.h"
24#include "TFrame.h"
25#include "TPaveText.h"
26#include "TPaveStats.h"
27#include "TText.h"
28#include "TROOT.h"
29#include "TClass.h"
30#include "TColor.h"
31#include "TObjArray.h"
32#include "TArrayI.h"
33#include "TList.h"
34#include "TF1.h"
35#include "TF2.h"
36#include "TF3.h"
37#include "TH1.h"
38#include "TH2.h"
39#include "THStack.h"
40#include "TMultiGraph.h"
41#include "TEnv.h"
42#include "TError.h"
43#include "TGraph.h"
44#include "TGraphPolar.h"
45#include "TGraphPolargram.h"
46#include "TGraph2D.h"
47#include "TGaxis.h"
48#include "TScatter.h"
49#include "TCutG.h"
50#include "TBufferJSON.h"
51#include "TBase64.h"
52#include "TAtt3D.h"
53#include "TView.h"
54#include "TExec.h"
55#include "TVirtualX.h"
56#include "TVirtualPS.h"
57#include "TMath.h"
58#include "TTimer.h"
59#include "TThread.h"
60
61#include <cstdio>
62#include <cstring>
63#include <fstream>
64#include <iostream>
65#include <memory>
66#include <sstream>
67#include <vector>
68
69
70class TWebCanvasTimer : public TTimer {
75public:
77
78 Bool_t IsSlow() const { return fSlow; }
80 {
81 fSlow = slow;
82 fSlowCnt = 0;
83 SetTime(slow ? 50 : 10);
84 }
85
86 /// used to send control messages to clients
87 void Timeout() override
88 {
90 return;
94 if (res) {
95 fSlowCnt = 0;
96 } else if (++fSlowCnt > 100 && !IsSlow()) {
98 }
99 }
100};
101
102
103/** \class TWebCanvas
104\ingroup webgui6
105\ingroup webwidgets
106
107Basic TCanvasImp ABI implementation for Web-based Graphics
108Provides painting of main ROOT classes in web browsers using [JSROOT](https://root.cern/js/)
109
110Following settings parameters can be useful for TWebCanvas:
111
112 WebGui.FullCanvas: 1 read-only mode (0), full-functional canvas (1) (default - 1)
113 WebGui.StyleDelivery: 1 provide gStyle object to JSROOT client (default - 1)
114 WebGui.PaletteDelivery: 1 provide color palette to JSROOT client (default - 1)
115 WebGui.TF1UseSave: 1 used saved values for function drawing: 0 - off, 1 - if client fail to evaluate function, 2 - always (default - 1)
116
117TWebCanvas is used by default in interactive ROOT session. To use web-based canvas in batch mode for image
118generation, one should explicitly specify `--web` option when starting ROOT:
119
120 [shell] root -b --web tutorials/hsimple.root -e 'hpxpy->Draw("colz"); c1->SaveAs("image.png");'
121
122If for any reasons TWebCanvas does not provide required functionality, one always can disable it.
123Either by specifying `root --web=off` when starting ROOT or by setting `Canvas.Name: TRootCanvas` in rootrc file.
124
125*/
126
127using namespace std::string_literals;
128
129static const std::string sid_pad_histogram = "__pad_histogram__";
130
131
140
141static std::vector<WebFont_t> gWebFonts;
142
143std::string TWebCanvas::gCustomScripts = {};
144std::vector<std::string> TWebCanvas::gCustomClasses = {};
145
148std::vector<std::string> TWebCanvas::gBatchFiles;
149std::vector<std::string> TWebCanvas::gBatchJsons;
150std::vector<int> TWebCanvas::gBatchWidths;
151std::vector<int> TWebCanvas::gBatchHeights;
152
153//////////////////////////////////////////////////////////////////////////////////////////////////
154/// Configure batch image mode for web graphics.
155/// Allows to process many images with single headless browser invocation and increase performance of image production.
156/// When many canvases are stored as image in difference places, they first collected in batch and then processed when at least `n`
157/// images are prepared. Only then headless browser invoked and create all these images at once.
158/// This allows to significantly increase performance of image production in web mode
159
166
167//////////////////////////////////////////////////////////////////////////////////////////////////
168/// Flush batch images
169
171{
172 bool res = true;
173
174 if (gBatchJsons.size() > 0)
176
177 gBatchFiles.clear();
178 gBatchJsons.clear();
179 gBatchWidths.clear();
180 gBatchHeights.clear();
181
182 return res;
183}
184
185////////////////////////////////////////////////////////////////////////////////
186/// Constructor
187
189 : TCanvasImp(c, name, x, y, width, height)
190{
191 // Workaround for multi-threaded environment
192 // Ensure main thread id picked when canvas implementation is created -
193 // otherwise it may be assigned in other thread and screw-up gPad access.
194 // Workaround may not work if main thread id was wrongly initialized before
195 // This resolves issue https://github.com/root-project/root/issues/15498
197
198 fTimer = new TWebCanvasTimer(*this);
199
201 fStyleDelivery = gEnv->GetValue("WebGui.StyleDelivery", 1);
202 fPaletteDelivery = gEnv->GetValue("WebGui.PaletteDelivery", 1);
203 fPrimitivesMerge = gEnv->GetValue("WebGui.PrimitivesMerge", 100);
204 fTF1UseSave = gEnv->GetValue("WebGui.TF1UseSave", (Int_t) 1);
206
207 fWebConn.emplace_back(0); // add special connection which only used to perform updates
208
209 fTimer->TurnOn();
210
211 // fAsyncMode = kTRUE;
212}
213
214
215////////////////////////////////////////////////////////////////////////////////
216/// Destructor
217
219{
220 if(fWindow)
221 fWindow->Reset();
222
223 delete fTimer;
224}
225
226//////////////////////////////////////////////////////////////////////////////////////////////////
227/// Add font to static list of fonts supported by the canvas
228/// Name specifies name of the font, second is font file with .ttf or .woff2 extension
229/// Only True Type Fonts (ttf) are supported by PDF
230/// Returns font index which can be used in
231/// auto font_indx = TWebCanvas::AddFont("test", "test.ttf", 2);
232/// gStyle->SetStatFont(font_indx);
233
234Font_t TWebCanvas::AddFont(const char *name, const char *fontfile, Int_t precision)
235{
236 Font_t maxindx = 22;
237 for (auto &entry : gWebFonts) {
238 if (entry.fName == name)
239 return precision > 0 ? entry.fIndx*10 + precision : entry.fIndx;
240 if (entry.fIndx > maxindx)
241 maxindx = entry.fIndx;
242 }
243
244 TString fullname = fontfile, fmt = "ttf";
245 auto pos = fullname.Last('.');
246 if (pos != kNPOS) {
247 fmt = fullname(pos+1, fullname.Length() - pos);
248 fmt.ToLower();
249 if ((fmt != "ttf") && (fmt != "woff2")) {
250 ::Error("TWebCanvas::AddFont", "Unsupported font file extension %s", fmt.Data());
251 return (Font_t) -1;
252 }
253 }
254
255 gSystem->ExpandPathName(fullname);
256
257 if (gSystem->AccessPathName(fullname.Data(), kReadPermission)) {
258 ::Error("TWebCanvas::AddFont", "Not possible to read font file %s", fullname.Data());
259 return (Font_t) -1;
260 }
261
262 std::ifstream is(fullname.Data(), std::ios::in | std::ios::binary);
263 std::string res;
264 if (is) {
265 is.seekg(0, std::ios::end);
266 res.resize(is.tellg());
267 is.seekg(0, std::ios::beg);
268 is.read((char *)res.data(), res.length());
269 if (!is)
270 res.clear();
271 }
272
273 if (res.empty()) {
274 ::Error("TWebCanvas::AddFont", "Fail to read font file %s", fullname.Data());
275 return (Font_t) -1;
276 }
277
278 TString base64 = TBase64::Encode(res.c_str(), res.length());
279
280 maxindx++;
281
282 gWebFonts.emplace_back(maxindx, name, fmt, base64);
283
284 return precision > 0 ? maxindx*10 + precision : maxindx;
285}
286
287////////////////////////////////////////////////////////////////////////////////
288/// Initialize window for the web canvas
289/// At this place canvas is not yet register to the list of canvases - one cannot call RWebWindow::Show()
290
292{
293 return 111222333; // should not be used at all
294}
295
296////////////////////////////////////////////////////////////////////////////////
297/// Creates web-based pad painter
298
303
304////////////////////////////////////////////////////////////////////////////////
305/// Returns kTRUE when object is fully supported on JSROOT side
306/// In ROOT7 Paint function will just return appropriate flag that object can be displayed on JSROOT side
307
309{
310 if (!obj)
311 return kTRUE;
312
313 static const struct {
314 const char *name{nullptr};
315 bool with_derived{false};
316 bool reduse_by_many{false};
317 } supported_classes[] = {{"ROOT::Experimental::RTreeMapPainter"},
318 {"TH1", true},
319 {"TF1", true},
320 {"TGraph", true},
321 {"TScatter"},
322 {"TFrame"},
323 {"THStack"},
324 {"TMultiGraph"},
325 {"TGraphPolargram", true},
326 {"TPave", true},
327 {"TGaxis"},
328 {"TEfficiency"},
329 {"TPave", true},
330 {"TButton", true},
331 {"TSlider", true},
332 {"TArrow"},
333 {"TBox", false, true}, // can be handled via TWebPainter, disable for large number of primitives (like in greyscale.C)
334 {"TWbox"}, // some extra calls which cannot be handled via TWebPainter
335 {"TLine", false, true}, // can be handler via TWebPainter, disable for large number of primitives (like in greyscale.C)
336 {"TEllipse", true, true}, // can be handled via TWebPainter, disable for large number of primitives (like in greyscale.C)
337 {"TPie"},
338 {"TText"},
339 {"TLatex"},
340 {"TLink"},
341 {"TAnnotation"},
342 {"TMathText"},
343 {"TMarker"},
344 {"TPolyMarker"},
345 {"TPolyLine", true, true}, // can be handled via TWebPainter, simplify colors handling
346 {"TPolyMarker3D"},
347 {"TPolyLine3D"},
348 {"TGraphTime"},
349 {"TGraph2D"},
350 {"TGraph2DErrors"},
351 {"TGraphTime"},
352 {"TASImage"},
353 {"TRatioPlot"},
354 {"TSpline"},
355 {"TSpline3"},
356 {"TSpline5"},
357 {"TGeoManager"},
358 {"TGeoVolume"},
359 {}};
360
361 // fast check of class name
362 for (int i = 0; supported_classes[i].name != nullptr; ++i)
364 return kTRUE;
365
366 // now check inheritance only for configured classes
367 for (int i = 0; supported_classes[i].name != nullptr; ++i)
369 if (obj->InheritsFrom(supported_classes[i].name))
370 return kTRUE;
371
372 return IsCustomClass(obj->IsA());
373}
374
375//////////////////////////////////////////////////////////////////////////////////////////////////
376/// Configures custom script for canvas.
377/// If started with "modules:" prefix, module(s) will be imported with `loadModules` function of JSROOT.
378/// If custom path was configured in RWebWindowsManager::AddServerLocation, it can be used in module paths.
379/// If started with "load:" prefix, code will be loaded with `loadScript` function of JSROOT (old, deprecated way)
380/// Script also can be a plain JavaScript code which imports JSROOT and provides draw function for custom classes
381/// See tutorials/visualisation/webgui/custom/custom.mjs demonstrating such example
382
383void TWebCanvas::SetCustomScripts(const std::string &src)
384{
386}
387
388//////////////////////////////////////////////////////////////////////////////////////////////////
389/// Returns configured custom script
390
392{
393 return gCustomScripts;
394}
395
396//////////////////////////////////////////////////////////////////////////////////////////////////
397/// For batch mode special handling of scripts are required
398/// Headless browser not able to load modules from the file system
399/// Therefore custom web-canvas modules and scripts has to be loaded in advance and processed
400
402{
403 if (!batch || gCustomScripts.empty() || (gCustomScripts.find("modules:") != 0))
404 return gCustomScripts;
405
407
408 std::string content;
409
410 std::string modules_names = gCustomScripts.substr(8);
411
412 std::map<std::string, bool> mapped_funcs;
413
414 while (!modules_names.empty()) {
415 std::string modname;
416 auto p = modules_names.find(";");
417 if (p == std::string::npos) {
419 modules_names.clear();
420 } else {
421 modname = modules_names.substr(0, p);
422 modules_names = modules_names.substr(p+1);
423 }
424
425 p = modname.find("/");
426 if ((p == std::string::npos) || modname.empty())
427 continue;
428
429 std::string pathname = modname.substr(0, p+1);
430 std::string filename = modname.substr(p+1);
431
432 auto fpath = loc[pathname];
433
434 if (fpath.empty())
435 continue;
436
438 if (cont.empty())
439 continue;
440
441 // check that special mark is in the script
442 auto pmark = cont.find("$$jsroot_batch_conform$$");
443 if (pmark == std::string::npos)
444 continue;
445
446 // process line like this
447 // import { ObjectPainter, addMoveHandler, addDrawFunc, ensureTCanvas } from 'jsroot';
448
449 static const std::string str1 = "import {";
450 static const std::string str2 = "} from 'jsroot';";
451
452 auto p1 = cont.find(str1);
453 auto p2 = cont.find(str2, p1);
454 if ((p1 == std::string::npos) || (p2 == std::string::npos) || (p2 > pmark))
455 continue;
456
457 TString globs;
458
459 TString funcs = cont.substr(p1 + 8, p2 - p1 - 8).c_str();
460 auto arr = funcs.Tokenize(",");
461
462 TIter next(arr);
463 while (auto obj = next()) {
464 TString name = obj->GetName();
465 name = name.Strip(TString::kBoth);
466 if (!mapped_funcs[name.Data()]) {
467 globs.Append(TString::Format("globalThis.%s = JSROOT.%s;\n", name.Data(), name.Data()));
468 mapped_funcs[name.Data()] = true;
469 }
470 }
471 delete arr;
472
473 cont.erase(p1, p2 + str2.length() - p1);
474
475 cont.insert(p1, globs.Data());
476
477 content.append(cont);
478 }
479
480 return content;
481}
482
483
484//////////////////////////////////////////////////////////////////////////////////////////////////
485/// Assign custom class
486
487void TWebCanvas::AddCustomClass(const std::string &clname, bool with_derived)
488{
489 if (with_derived)
490 gCustomClasses.emplace_back("+"s + clname);
491 else
492 gCustomClasses.emplace_back(clname);
493}
494
495//////////////////////////////////////////////////////////////////////////////////////////////////
496/// Checks if class belongs to custom
497
499{
500 for (auto &name : gCustomClasses) {
501 if (name[0] == '+') {
502 if (cl->InheritsFrom(name.substr(1).c_str()))
503 return true;
504 } else if (name.compare(cl->GetName()) == 0) {
505 return true;
506 }
507 }
508 return false;
509}
510
511//////////////////////////////////////////////////////////////////////////////////////////////////
512/// Creates representation of the object for painting in web browser
513
515{
516 if (IsJSSupportedClass(obj, masterps != nullptr)) {
517 master.NewPrimitive(obj, opt).SetSnapshot(TWebSnapshot::kObject, obj);
518 return;
519 }
520
521 // painter is not necessary for batch canvas, but keep configuring it for a while
522 auto *painter = dynamic_cast<TWebPadPainter *>(Canvas()->GetCanvasPainter());
523
524 TView *view = nullptr;
525
527
528 gPad = pad;
529
530 if (obj->InheritsFrom(TAtt3D::Class()) && !pad->GetView()) {
531 pad->GetViewer3D("pad");
532 view = TView::CreateView(1, 0, 0); // Cartesian view by default
533 pad->SetView(view);
534
535 // Set view to perform first auto-range (scaling) pass
536 view->SetAutoRange(kTRUE);
537 }
538
540
541 auto painting = masterps;
542 if (!masterps) {
544 painting->SetClassName(obj->ClassName());
545 painting->SetObjectName(obj->GetName());
546 }
547 gVirtualPS = nullptr;
548 if (painter)
549 painter->SetPainting(painting);
550
551 // calling Paint function for the object
552 obj->Paint(opt);
553
554 if (view) {
555 view->SetAutoRange(kFALSE);
556 // call 3D paint once again to make real drawing
557 obj->Paint(opt);
558 pad->SetView(nullptr);
559 }
560
561 if (painter)
562 painter->SetPainting(nullptr);
563
565
566 fPadsStatus[pad]._has_specials = true;
567
568 // if there are master PS, do not create separate entries
569 if (!masterps) {
570 if (!painting->IsEmpty())
571 master.NewPrimitive(obj, opt).SetSnapshot(TWebSnapshot::kSVG, painting, kTRUE);
572 else
573 delete painting;
574 }
575}
576
577//////////////////////////////////////////////////////////////////////////////////////////////////
578/// Calculate hash function for all colors and palette
579
581{
582 UInt_t hash = 0;
583
584 TObjArray *colors = (TObjArray *)gROOT->GetListOfColors();
585
586 if (colors) {
587 for (Int_t n = 0; n <= colors->GetLast(); ++n)
588 if (colors->At(n))
589 hash += TString::Hash(colors->At(n), TColor::Class()->Size());
590 }
591
593
594 hash += TString::Hash(pal.GetArray(), pal.GetSize() * sizeof(Int_t));
595
596 return hash;
597}
598
599
600//////////////////////////////////////////////////////////////////////////////////////////////////
601/// Add special canvas objects with list of colors and color palette
602
604{
605 TObjArray *colors = (TObjArray *)gROOT->GetListOfColors();
606
607 if (!colors)
608 return;
609
610 //Int_t cnt = 0;
611 //for (Int_t n = 0; n <= colors->GetLast(); ++n)
612 // if (colors->At(n))
613 // cnt++;
614 //if (cnt <= 598)
615 // return; // normally there are 598 colors defined
616
618
619 auto listofcols = new TWebPainting;
620 for (Int_t n = 0; n <= colors->GetLast(); ++n)
621 listofcols->AddColor(n, (TColor *)colors->At(n));
622
623 // store palette in the buffer
624 auto *tgt = listofcols->Reserve(pal.GetSize());
625 for (Int_t i = 0; i < pal.GetSize(); i++)
626 tgt[i] = pal[i];
627 listofcols->FixSize();
628
629 master.NewSpecials().SetSnapshot(TWebSnapshot::kColors, listofcols, kTRUE);
630}
631
632//////////////////////////////////////////////////////////////////////////////////////////////////
633/// Add special canvas objects with custom fonts
634
636{
637 for (auto &entry : gWebFonts) {
638 TString code = TString::Format("%d:%s:%s:%s", entry.fIndx, entry.fName.Data(), entry.fFormat.Data(), entry.fData.Data());
639 auto custom_font = new TWebPainting;
640 custom_font->AddOper(code.Data());
641 master.NewSpecials().SetSnapshot(TWebSnapshot::kFont, custom_font, kTRUE);
642 }
643}
644
645//////////////////////////////////////////////////////////////////////////////////////////////////
646/// Create snapshot for pad and all primitives
647/// Callback function is used to create JSON in the middle of data processing -
648/// when all misc objects removed from canvas list of primitives or histogram list of functions
649/// After that objects are moved back to their places
650
652{
653 auto &pad_status = fPadsStatus[pad];
654
655 // send primitives if version 0 or actual pad version grater than already send version
656 bool process_primitives = (version == 0) || (pad_status.fVersion > version);
657
658 if (paddata.IsSetObjectIds()) {
659 paddata.SetActive(pad == gPad);
660 paddata.SetObjectIDAsPtr(pad);
661 }
662 paddata.SetSnapshot(TWebSnapshot::kSubPad, pad); // add ref to the pad
663 paddata.SetWithoutPrimitives(!process_primitives);
664 paddata.SetHasExecs(pad->GetListOfExecs()); // if pad execs are there provide more events from client
665
666 // check style changes every time when creating canvas snapshot
667 if (resfunc && (GetStyleDelivery() > 0)) {
668
670 auto hash = TString::Hash(gStyle, TStyle::Class()->Size());
671 if ((hash != fStyleHash) || (fStyleVersion == 0)) {
674 }
675 }
676
678 paddata.NewPrimitive().SetSnapshot(TWebSnapshot::kStyle, gStyle);
679 }
680
681 // for the first time add custom fonts to the canvas snapshot
682 if (resfunc && (version == 0))
684
685 fAllPads.emplace_back(pad);
686
687 TList *primitives = pad->GetListOfPrimitives();
688
689 auto masterps = std::make_unique<TWebPainting>();
690 bool usemaster = primitives ? (primitives->GetSize() > fPrimitivesMerge) : false;
691
692 TIter iter(primitives);
693 TObject *obj = nullptr;
694 TFrame *frame = nullptr;
695 TPaveText *title = nullptr;
696 TGraphPolar *first_polar = nullptr;
697 TGraphPolargram *polargram = nullptr;
699 bool need_frame = false, has_histo = false, need_palette = false;
700 std::string need_title;
701
702 auto checkNeedPalette = [](TH1* hist, const TString &opt) {
703 auto check = [&opt](const TString &arg) {
704 return opt.Contains(arg + "Z") || opt.Contains(arg + "HZ");
705 };
706
707 return ((hist->GetDimension() == 2) && (check("COL") || check("LEGO") || check("LEGO4") || check("SURF2"))) ||
708 ((hist->GetDimension() == 3) && (check("BOX2") || check("BOX3")));
709 };
710
711 while (process_primitives && ((obj = iter()) != nullptr)) {
712 TString opt = iter.GetOption();
713 opt.ToUpper();
714
715 if (obj->InheritsFrom(THStack::Class())) {
716 // workaround for THStack, create extra components before sending to client
717 if (!opt.Contains("PADS") && !opt.Contains("SAME")) {
719
720 auto hs = static_cast<THStack *>(obj);
721
722 if (!opt.Contains("NOSTACK") && !opt.Contains("CANDLE") && !opt.Contains("VIOLIN") && !IsReadOnly() && !fUsedObjs[hs]) {
724 fUsedObjs[hs] = true;
725 }
726
727 if (strlen(obj->GetTitle()) > 0)
728 need_title = obj->GetTitle();
730 hs->BuildPrimitives(iter.GetOption(), do_rebuild_stack);
731 has_histo = true;
732 need_frame = true;
733 }
734 } else if (obj->InheritsFrom(TMultiGraph::Class())) {
735 // workaround for TMultiGraph
736 auto mg = static_cast<TMultiGraph *>(obj);
737 // assign auto-colors if any
738 mg->BuildPrimitives(iter.GetOption());
739 if (opt.Contains("A")) {
741 mg->GetHistogram(); // force creation of histogram without any drawings
742 has_histo = true;
743 if (strlen(obj->GetTitle()) > 0)
744 need_title = obj->GetTitle();
745 need_frame = true;
746 }
747 } else if (obj->InheritsFrom(TFrame::Class())) {
748 if (!frame)
749 frame = static_cast<TFrame *>(obj);
750 } else if (obj->InheritsFrom(TH1::Class())) {
751 need_frame = true;
752 has_histo = true;
753 if (!obj->TestBit(TH1::kNoTitle) && !opt.Contains("SAME") && !opt.Contains("AXIS") && !opt.Contains("AXIG") && (strlen(obj->GetTitle()) > 0))
754 need_title = obj->GetTitle();
755 if (checkNeedPalette(static_cast<TH1*>(obj), opt))
756 need_palette = true;
757 } else if (obj->InheritsFrom(TGraphPolar::Class())) {
758 auto polar = static_cast<TGraphPolar *> (obj);
759 if (!first_polar) {
761 need_title = first_polar->GetTitle();
762 polargram = first_polar->GetPolargram();
763 if (!polargram) {
764 polargram = first_polar->CreatePolargram(opt);
765 polargram_drawopt = opt.Contains("N") ? "N" : "";
766 if (opt.Contains("O")) polargram_drawopt.Append("O");
767 }
768 }
769 polar->SetPolargram(polargram);
770 } else if (obj->InheritsFrom(TGraph::Class())) {
771 if (opt.Contains("A")) {
772 need_frame = true;
773 if (!has_histo && (strlen(obj->GetTitle()) > 0) && !obj->TestBit(TH1::kNoTitle))
774 need_title = obj->GetTitle();
775 }
776 } else if (obj->InheritsFrom(TGraph2D::Class())) {
777 if (!has_histo && (strlen(obj->GetTitle()) > 0))
778 need_title = obj->GetTitle();
779 } else if (obj->InheritsFrom(TScatter::Class())) {
780 need_frame = need_palette = true;
781 if (strlen(obj->GetTitle()) > 0)
782 need_title = obj->GetTitle();
783 } else if (obj->InheritsFrom(TF1::Class())) {
784 if (!opt.Contains("SAME")) {
786 if (!has_histo && (strlen(obj->GetTitle()) > 0))
787 need_title = obj->GetTitle();
788 }
789 } else if (obj->InheritsFrom(TPaveText::Class())) {
790 if (strcmp(obj->GetName(), "title") == 0)
791 title = static_cast<TPaveText *>(obj);
792 } else if (obj->InheritsFrom(TButton::Class())) {
793 auto btn = (TButton *) obj;
794 auto text = dynamic_cast<TText *> (btn->GetListOfPrimitives()->First());
795 if (text) {
796 text->SetTitle(btn->GetTitle());
797 text->SetTextSize(btn->GetTextSize());
798 text->SetTextFont(btn->GetTextFont());
799 text->SetTextAlign(btn->GetTextAlign());
800 text->SetTextColor(btn->GetTextColor());
801 text->SetTextAngle(btn->GetTextAngle());
802 }
803 }
804 }
805
806 if (need_frame && !frame && primitives && CanCreateObject("TFrame")) {
807 if (!IsReadOnly() && need_palette && (pad->GetRightMargin() < 0.12) && (pad->GetRightMargin() == gStyle->GetPadRightMargin()))
808 pad->SetRightMargin(0.12);
809
810 frame = pad->GetFrame();
811 if(frame)
812 primitives->AddFirst(frame, "");
813 }
814
815 if (!need_title.empty() && gStyle->GetOptTitle()) {
816 if (title) {
817 auto line0 = title->GetLine(0);
818 if (line0 && !IsReadOnly()) line0->SetTitle(need_title.c_str());
819 } else if (primitives && CanCreateObject("TPaveText")) {
820 title = new TPaveText(0, 0, 0, 0, "blNDC");
823 title->SetName("title");
826 title->SetTextFont(gStyle->GetTitleFont(""));
827 if (gStyle->GetTitleFont("") % 10 > 2)
829 title->AddText(need_title.c_str());
830 title->SetBit(kCanDelete);
831 primitives->Add(title, title->GetOption());
832 }
833 }
834
835 if (polargram && (polargram_drawopt != "-"))
836 primitives->Add(polargram, polargram_drawopt);
837
838 auto flush_master = [&]() {
839 if (!usemaster || masterps->IsEmpty()) return;
840
841 paddata.NewPrimitive(pad).SetSnapshot(TWebSnapshot::kSVG, masterps.release(), kTRUE);
842 masterps = std::make_unique<TWebPainting>(); // create for next operations
843 };
844
845 auto check_cutg_in_options = [&](const TString &opt) {
846 auto p1 = opt.Index("["), p2 = opt.Index("]");
847 if ((p1 != kNPOS) && (p2 != kNPOS) && p2 > p1 + 1) {
848 TString cutname = opt(p1 + 1, p2 - p1 - 1);
849 TObject *cutg = primitives->FindObject(cutname.Data());
850 if (!cutg || (cutg->IsA() != TCutG::Class())) {
851 cutg = gROOT->GetListOfSpecials()->FindObject(cutname.Data());
852 if (cutg && cutg->IsA() == TCutG::Class())
853 paddata.NewPrimitive(cutg, "__ignore_drawing__").SetSnapshot(TWebSnapshot::kObject, cutg);
854 }
855 }
856 };
857
858 auto check_save_tf1 = [&](TObject *fobj, bool ignore_nodraw = false) {
859 if (!paddata.IsBatchMode() && (fTF1UseSave <= 0))
860 return;
861 if (!ignore_nodraw && fobj->TestBit(TF1::kNotDraw))
862 return;
863
864 auto f1 = static_cast<TF1 *>(fobj);
865 // check if TF1 can be used
866 if (!f1->IsValid())
867 return;
868
869 // in default case save buffer used as is
870 if ((fTF1UseSave == 1) && f1->HasSave())
871 return;
872
873 auto f3 = dynamic_cast<TF3 *>(f1);
874 auto f2 = dynamic_cast<TF2 *>(f1);
875 if (f3)
876 f3->Save(f3->GetXmin(), f3->GetXmax(), f3->GetYmin(), f3->GetYmax(), f3->GetZmin(), f3->GetZmax());
877 else if (f2)
878 f2->Save(f2->GetXmin(), f2->GetXmax(), f2->GetYmin(), f2->GetYmax(), 0, 0);
879 else
880 f1->Save(f1->GetXmin(), f1->GetXmax(), 0, 0, 0, 0);
881 };
882
883 auto create_stats = [&]() {
884 TPaveStats *stats = nullptr;
885 if (CanCreateObject("TPaveStats")) {
886 stats = new TPaveStats(
889 gStyle->GetStatX(),
890 gStyle->GetStatY(), "brNDC");
891
892 // do not set optfit and optstat, they calling pad->Update,
893 // values correctly set already in TPaveStats constructor
894 // stats->SetOptFit(gStyle->GetOptFit());
895 // stats->SetOptStat(gStyle->GetOptStat());
899 stats->SetTextFont(gStyle->GetStatFont());
900 if (gStyle->GetStatFont()%10 > 2)
904 stats->SetName("stats");
905
907 stats->SetTextAlign(12);
908 stats->SetBit(kCanDelete);
909 stats->SetBit(kMustCleanup);
910 }
911
912 return stats;
913 };
914
915 auto check_graph_funcs = [&](TGraph *gr, TList *funcs = nullptr) {
916 if (!funcs && gr)
918 if (!funcs)
919 return;
920
922 TPaveStats *stats = nullptr;
923 bool has_tf1 = false;
924
925 while (auto fobj = fiter()) {
926 if (fobj->InheritsFrom(TPaveStats::Class()))
927 stats = dynamic_cast<TPaveStats *> (fobj);
928 else if (fobj->InheritsFrom(TF1::Class())) {
930 has_tf1 = true;
931 }
932 }
933
934 if (!stats && has_tf1 && gr && !gr->TestBit(TGraph::kNoStats) && (gStyle->GetOptFit() > 0)) {
935 stats = create_stats();
936 if (stats) {
937 stats->SetOptStat(0);
938 stats->SetOptFit(gStyle->GetOptFit());
939 stats->SetParent(funcs);
940 funcs->Add(stats);
941 }
942 }
943 };
944
945 iter.Reset();
946
947 bool first_obj = true;
948
950 pad_status._has_specials = false;
951
952 while ((obj = iter()) != nullptr) {
953 if (obj->IsA() == TPad::Class()) {
954 flush_master();
955 CreatePadSnapshot(paddata.NewSubPad(), (TPad *)obj, version, nullptr);
956 } else if (!process_primitives) {
957 continue;
958 } else if (obj->InheritsFrom(TH1::Class())) {
959 flush_master();
960
961 TH1 *hist = static_cast<TH1 *>(obj);
962 hist->BufferEmpty();
963
964 TPaveStats *stats = nullptr;
965 TObject *palette = nullptr;
966
968 while (auto fobj = fiter()) {
969 if (fobj->InheritsFrom(TPaveStats::Class()))
970 stats = dynamic_cast<TPaveStats *> (fobj);
971 else if (fobj->InheritsFrom("TPaletteAxis"))
972 palette = fobj;
973 else if (fobj->InheritsFrom(TF1::Class()))
975 }
976
977 TString hopt = iter.GetOption();
978 TString o = hopt;
979 o.ToUpper();
980
981 if (!stats && (first_obj || o.Contains("SAMES")) && (gStyle->GetOptStat() > 0)) {
982 stats = create_stats();
983 if (stats) {
984 stats->SetParent(hist);
985 hist->GetListOfFunctions()->Add(stats);
986 }
987 }
988
989 if (!palette && CanCreateObject("TPaletteAxis") && checkNeedPalette(hist, o)) {
990 std::stringstream exec;
991 exec << "new TPaletteAxis(0,0,0,0, (TH1*)" << std::hex << std::showbase << (size_t)hist << ");";
992 palette = (TObject *)gROOT->ProcessLine(exec.str().c_str());
993 if (palette)
995 }
996
997 paddata.NewPrimitive(obj, hopt.Data()).SetSnapshot(TWebSnapshot::kObject, obj);
998
999 if (hist->GetDimension() == 2)
1001
1002 first_obj = false;
1003 } else if (obj->InheritsFrom(TGraphPolar::Class())) {
1004 flush_master();
1005
1006 auto polar = static_cast<TGraphPolar *>(obj);
1007
1009
1010 paddata.NewPrimitive(obj, iter.GetOption()).SetSnapshot(TWebSnapshot::kObject, obj);
1011
1012 first_obj = false;
1013 } else if (obj->InheritsFrom(TGraphPolargram::Class())) {
1014 // do nothing, object must be streamed with graphpolar
1015 } else if (obj->InheritsFrom(TGraph::Class())) {
1016 flush_master();
1017
1018 TGraph *gr = static_cast<TGraph *>(obj);
1019
1021
1022 TString gropt = iter.GetOption();
1023
1024 // ensure histogram exists on server to draw it properly on clients side
1025 if (!IsReadOnly() && (first_obj || gropt.Index("A", 0, TString::kIgnoreCase) != kNPOS ||
1026 (gropt.Index("X+", 0, TString::kIgnoreCase) != kNPOS) || (gropt.Index("Y+", 0, TString::kIgnoreCase) != kNPOS)))
1027 gr->GetHistogram();
1028
1029 paddata.NewPrimitive(obj, gropt.Data()).SetSnapshot(TWebSnapshot::kObject, obj);
1030
1031 first_obj = false;
1032 } else if (obj->InheritsFrom(TGraph2D::Class())) {
1033 flush_master();
1034
1035 TGraph2D *gr2d = static_cast<TGraph2D *>(obj);
1036
1037 check_graph_funcs(nullptr, gr2d->GetListOfFunctions());
1038
1039 // ensure correct range of histogram
1040 if (!IsReadOnly() && first_obj) {
1041 TString gropt = iter.GetOption();
1042 gropt.ToUpper();
1043 Bool_t zscale = gropt.Contains("TRI1") || gropt.Contains("TRI2") || gropt.Contains("COL");
1044 Bool_t cont5_draw = gropt.Contains("CONT5");
1045 Bool_t real_draw = gropt.Contains("TRI") || gropt.Contains("LINE") || gropt.Contains("ERR") || gropt.Contains("P") || cont5_draw;
1046
1047 TString hopt = !real_draw ? iter.GetOption() : (cont5_draw ? "" : (zscale ? "lego2z" : "lego2"));
1048 if (title) hopt.Append(";;use_pad_title");
1049
1050 // if gr2d not draw - let create histogram with correspondent content
1051 auto hist = gr2d->GetHistogram(real_draw ? "empty" : "");
1052
1053 paddata.NewPrimitive(gr2d, hopt.Data(), "#hist").SetSnapshot(TWebSnapshot::kObject, hist);
1054 }
1055
1056 paddata.NewPrimitive(obj, iter.GetOption()).SetSnapshot(TWebSnapshot::kObject, obj);
1057 first_obj = false;
1058 } else if (obj->InheritsFrom(TMultiGraph::Class())) {
1059 flush_master();
1060
1061 TMultiGraph *mgr = static_cast<TMultiGraph *>(obj);
1062 TIter fiter(mgr->GetListOfFunctions());
1063 while (auto fobj = fiter()) {
1064 if (fobj->InheritsFrom(TF1::Class()))
1066 }
1067
1068 TIter giter(mgr->GetListOfGraphs());
1069 while (auto gobj = giter())
1070 check_graph_funcs(static_cast<TGraph *>(gobj));
1071
1072 paddata.NewPrimitive(obj, iter.GetOption()).SetSnapshot(TWebSnapshot::kObject, obj);
1073
1074 first_obj = false;
1075 } else if (obj->InheritsFrom(THStack::Class())) {
1076 flush_master();
1077
1078 THStack *hs = static_cast<THStack *>(obj);
1079
1080 TString hopt = iter.GetOption();
1081 hopt.ToLower();
1082 if (!hopt.Contains("nostack") && !hopt.Contains("candle") && !hopt.Contains("violin") && !hopt.Contains("pads")) {
1083 auto arr = hs->GetStack();
1084 arr->SetName(hs->GetName()); // mark list for JS
1085 paddata.NewPrimitive(arr, "__ignore_drawing__").SetSnapshot(TWebSnapshot::kObject, arr);
1086 }
1087
1088 paddata.NewPrimitive(obj, iter.GetOption()).SetSnapshot(TWebSnapshot::kObject, obj);
1089
1090 first_obj = hs->GetNhists() > 0; // real drawing only if there are histograms
1091 } else if (obj->InheritsFrom(TScatter::Class())) {
1092 flush_master();
1093
1094 TScatter *scatter = static_cast<TScatter *>(obj);
1095
1096 TObject *palette = nullptr;
1097
1098 TIter fiter(scatter->GetGraph()->GetListOfFunctions());
1099 while (auto fobj = fiter()) {
1100 if (fobj->InheritsFrom("TPaletteAxis"))
1101 palette = fobj;
1102 }
1103
1104 // ensure histogram exists on server to draw it properly on clients side
1105 if (!IsReadOnly() && first_obj)
1106 scatter->GetHistogram();
1107
1108 if (!palette && CanCreateObject("TPaletteAxis")) {
1109 std::stringstream exec;
1110 exec << "new TPaletteAxis(0,0,0,0,0,0);";
1111 palette = (TObject *)gROOT->ProcessLine(exec.str().c_str());
1112 if (palette)
1113 scatter->GetGraph()->GetListOfFunctions()->AddFirst(palette);
1114 }
1115
1116 paddata.NewPrimitive(obj, iter.GetOption()).SetSnapshot(TWebSnapshot::kObject, obj);
1117
1118 first_obj = false;
1119 } else if (obj->InheritsFrom(TF1::Class())) {
1120 flush_master();
1121 auto f1 = static_cast<TF1 *> (obj);
1122
1123 TString f1opt = iter.GetOption();
1124
1125 check_save_tf1(obj, true);
1126 if (fTF1UseSave > 1)
1127 f1opt.Append(";force_saved");
1128 else if (fTF1UseSave == 1)
1129 f1opt.Append(";prefer_saved");
1130
1131 if (first_obj) {
1132 auto hist = f1->GetHistogram();
1133 paddata.NewPrimitive(hist, "__ignore_drawing__").SetSnapshot(TWebSnapshot::kObject, hist);
1134 f1opt.Append(";webcanv_hist");
1135 }
1136
1137 if (f1->IsA() == TF2::Class())
1139
1140 paddata.NewPrimitive(f1, f1opt.Data()).SetSnapshot(TWebSnapshot::kObject, f1);
1141
1142 first_obj = false;
1143
1144 } else if (obj->InheritsFrom(TGaxis::Class())) {
1145 flush_master();
1146 auto gaxis = static_cast<TGaxis *> (obj);
1147 auto func = gaxis->GetFunction();
1148 if (func)
1149 paddata.NewPrimitive(func, "__ignore_drawing__").SetSnapshot(TWebSnapshot::kObject, func);
1150
1151 paddata.NewPrimitive(obj, iter.GetOption()).SetSnapshot(TWebSnapshot::kObject, obj);
1152 } else if (obj->InheritsFrom(TFrame::Class())) {
1153 flush_master();
1154 if (frame && (obj == frame)) {
1155 paddata.NewPrimitive(obj, iter.GetOption()).SetSnapshot(TWebSnapshot::kObject, obj);
1156 frame = nullptr; // add frame only once
1157 }
1158 } else if (IsJSSupportedClass(obj, usemaster)) {
1159 flush_master();
1160 paddata.NewPrimitive(obj, iter.GetOption()).SetSnapshot(TWebSnapshot::kObject, obj);
1161 } else {
1162 CreateObjectSnapshot(paddata, pad, obj, iter.GetOption(), usemaster ? masterps.get() : nullptr);
1163 }
1164 }
1165
1166 flush_master();
1167
1168 bool provide_colors = false;
1169
1170 if ((GetPaletteDelivery() > 2) || ((GetPaletteDelivery() == 2) && resfunc)) {
1171 // provide colors: either for each subpad (> 2) or only for canvas (== 2)
1173 } else if ((GetPaletteDelivery() == 1) && resfunc) {
1174 // check that colors really changing, using hash
1175
1177 auto hash = CalculateColorsHash();
1178 if ((hash != fColorsHash) || (fColorsVersion == 0)) {
1179 fColorsHash = hash;
1181 }
1182 }
1183
1185 }
1186
1187 // add colors after painting is performed - new colors may be generated only during painting
1188 if (provide_colors)
1190
1191 if (!resfunc)
1192 return;
1193
1194 // now hide all primitives to perform I/O
1195 std::vector<TList *> all_primitives(fAllPads.size());
1196 for (unsigned n = 0; n < fAllPads.size(); ++n) {
1197 all_primitives[n] = fAllPads[n]->fPrimitives;
1198 fAllPads[n]->fPrimitives = nullptr;
1199 }
1200
1201 // execute function to prevent storing of colors with custom TCanvas streamer
1203
1204 // invoke callback for streaming
1205 resfunc(&paddata);
1206
1207 // and restore back primitives - delete any temporary if necessary
1208 for (unsigned n = 0; n < fAllPads.size(); ++n) {
1209 if (fAllPads[n]->fPrimitives)
1210 delete fAllPads[n]->fPrimitives;
1211 fAllPads[n]->fPrimitives = all_primitives[n];
1212 }
1213 fAllPads.clear();
1214 fUsedObjs.clear();
1215}
1216
1217//////////////////////////////////////////////////////////////////////////////////////////////////
1218/// Add control message for specified connection
1219/// Same control message can be overwritten many time before it really sends to the client
1220/// If connid == 0, message will be add to all connections
1221/// After ctrl message is add to the output, short timer is activated and message send afterwards
1222
1223void TWebCanvas::AddCtrlMsg(unsigned connid, const std::string &key, const std::string &value)
1224{
1226
1227 for (auto &conn : fWebConn) {
1228 if (conn.match(connid)) {
1229 conn.fCtrl[key] = value;
1230 new_ctrl = kTRUE;
1231 }
1232 }
1233
1234 if (new_ctrl && fTimer->IsSlow())
1236}
1237
1238
1239//////////////////////////////////////////////////////////////////////////////////////////////////
1240/// Add message to send queue for specified connection
1241/// If connid == 0, message will be add to all connections
1242
1243void TWebCanvas::AddSendQueue(unsigned connid, const std::string &msg)
1244{
1245 for (auto &conn : fWebConn) {
1246 if (conn.match(connid))
1247 conn.fSend.emplace(msg);
1248 }
1249}
1250
1251
1252//////////////////////////////////////////////////////////////////////////////////////////////////
1253/// Check if any data should be send to client
1254/// If connid != 0, only selected connection will be checked
1255
1257{
1258 if (!Canvas())
1259 return kFALSE;
1260
1261 bool isMoreData = false, isAnySend = false;
1262
1263 for (auto &conn : fWebConn) {
1264
1265 bool isConnData = !conn.fCtrl.empty() || !conn.fSend.empty() ||
1266 ((conn.fCheckedVersion < fCanvVersion) && (conn.fSendVersion == conn.fDrawVersion));
1267
1268 while ((conn.is_batch() && !connid) || (conn.match(connid) && fWindow && fWindow->CanSend(conn.fConnId, true))) {
1269 // check if any control messages still there to keep timer running
1270
1271 std::string buf;
1272
1273 if (!conn.fCtrl.empty()) {
1275 conn.fCtrl.clear();
1276 } else if (!conn.fSend.empty()) {
1277 std::swap(buf, conn.fSend.front());
1278 conn.fSend.pop();
1279 } else if ((conn.fCheckedVersion < fCanvVersion) && (conn.fSendVersion == conn.fDrawVersion)) {
1280
1281 buf = "SNAP6:"s + std::to_string(fCanvVersion) + ":"s;
1282
1283 TCanvasWebSnapshot holder(IsReadOnly(), true, false); // readonly, set ids, batchmode
1284
1285 holder.SetFixedSize(fFixedSize); // set fixed size flag
1286
1287 // scripts send only when canvas drawn for the first time
1288 if (!conn.fSendVersion)
1289 holder.SetScripts(ProcessCustomScripts(false));
1290
1291 holder.SetHighlightConnect(Canvas()->HasConnection("Highlighted(TVirtualPad*,TObject*,Int_t,Int_t)"));
1292
1293 CreatePadSnapshot(holder, Canvas(), conn.fSendVersion, [&buf, &conn, this](TPadWebSnapshot *snap) {
1294 if (conn.is_batch()) {
1295 // for batch connection only calling of CreatePadSnapshot is important
1296 buf.clear();
1297 return;
1298 }
1299
1301 auto hash = json.Hash();
1302 if (conn.fLastSendHash && (conn.fLastSendHash == hash) && conn.fSendVersion) {
1303 // prevent looping when same data send many times
1304 buf.clear();
1305 } else {
1306 buf.append(json.Data());
1307 conn.fLastSendHash = hash;
1308 }
1309 });
1310
1311 conn.fCheckedVersion = fCanvVersion;
1312
1313 conn.fSendVersion = fCanvVersion;
1314
1315 if (buf.empty())
1316 conn.fDrawVersion = fCanvVersion;
1317 } else {
1318 isConnData = false;
1319 break;
1320 }
1321
1322 if (!buf.empty() && !conn.is_batch()) {
1323 fWindow->Send(conn.fConnId, buf);
1324 isAnySend = true;
1325 }
1326 }
1327
1328 if (isConnData)
1329 isMoreData = true;
1330 }
1331
1332 if (fTimer->IsSlow() && isMoreData)
1333 fTimer->SetSlow(kFALSE);
1334
1335 return isAnySend;
1336}
1337
1338//////////////////////////////////////////////////////////////////////////////////////////
1339/// Close web canvas - not implemented
1340
1342{
1343}
1344
1345//////////////////////////////////////////////////////////////////////////////////////////
1346/// Create web window for the canvas
1347
1349{
1350 if (fWindow)
1351 return;
1352
1354
1355 fWindow->SetConnLimit(0); // configure connections limit
1356
1357 fWindow->SetDefaultPage("file:rootui5sys/canv/canvas6.html");
1358
1359 fWindow->SetCallBacks(
1360 // connection
1361 [this](unsigned connid) {
1362 if (fWindow->GetConnectionId(0) == connid)
1363 fWebConn.emplace(fWebConn.begin() + 1, connid);
1364 else
1365 fWebConn.emplace_back(connid);
1366 CheckDataToSend(connid);
1367 },
1368 // data
1369 [this](unsigned connid, const std::string &arg) {
1370 ProcessData(connid, arg);
1372 },
1373 // disconnect
1374 [this](unsigned connid) {
1375 unsigned indx = 0;
1376 for (auto &c : fWebConn) {
1377 if (c.fConnId == connid) {
1378 fWebConn.erase(fWebConn.begin() + indx);
1379 break;
1380 }
1381 indx++;
1382 }
1383 });
1384}
1385
1386//////////////////////////////////////////////////////////////////////////////////////////
1387/// Show canvas in specified place.
1388/// If parameter args not specified, default ROOT web display will be used
1389
1391{
1393
1396
1397 auto w = Canvas()->GetWindowWidth(), h = Canvas()->GetWindowHeight();
1398 if ((w > 0) && (w < 50000) && (h > 0) && (h < 30000))
1399 fWindow->SetGeometry(w, h);
1400
1402}
1403
1404//////////////////////////////////////////////////////////////////////////////////////////
1405/// Show canvas in browser window
1406
1408{
1409 if (gROOT->IsWebDisplayBatch())
1410 return;
1411
1412 if (fWindow && !fWindow->HasConnection(0))
1413 fLastDrawVersion = 0;
1414
1416 args.SetWidgetKind("TCanvas");
1417 args.SetSize(Canvas()->GetWindowWidth(), Canvas()->GetWindowHeight());
1418 args.SetPos(Canvas()->GetWindowTopX(), Canvas()->GetWindowTopY());
1419
1420 ShowWebWindow(args);
1421}
1422
1423//////////////////////////////////////////////////////////////////////////////////////////
1424/// Function used to send command to browser to toggle menu, toolbar, editors, ...
1425
1426void TWebCanvas::ShowCmd(const std::string &arg, Bool_t show)
1427{
1428 AddCtrlMsg(0, arg, show ? "1"s : "0"s);
1429}
1430
1431//////////////////////////////////////////////////////////////////////////////////////////
1432/// Activate object in editor in web browser
1433
1435{
1436 if (!pad || !obj) return;
1437
1438 UInt_t hash = TString::Hash(&obj, sizeof(obj));
1439
1440 AddCtrlMsg(0, "edit"s, std::to_string(hash));
1441}
1442
1443//////////////////////////////////////////////////////////////////////////////////////////
1444/// Returns kTRUE if web canvas has graphical editor
1445
1447{
1448 return (fClientBits & TCanvas::kShowEditor) != 0;
1449}
1450
1451//////////////////////////////////////////////////////////////////////////////////////////
1452/// Returns kTRUE if web canvas has menu bar
1453
1455{
1456 return (fClientBits & TCanvas::kMenuBar) != 0;
1457}
1458
1459//////////////////////////////////////////////////////////////////////////////////////////
1460/// Returns kTRUE if web canvas has status bar
1461
1466
1467//////////////////////////////////////////////////////////////////////////////////////////
1468/// Returns kTRUE if tooltips are activated in web canvas
1469
1471{
1472 return (fClientBits & TCanvas::kShowToolTips) != 0;
1473}
1474
1475//////////////////////////////////////////////////////////////////////////////////////////
1476/// Set window position of web canvas
1477
1479{
1480 AddCtrlMsg(0, "x"s, std::to_string(x));
1481 AddCtrlMsg(0, "y"s, std::to_string(y));
1482}
1483
1484//////////////////////////////////////////////////////////////////////////////////////////
1485/// Set window size of web canvas
1486
1488{
1489 AddCtrlMsg(0, "w"s, std::to_string(w));
1490 AddCtrlMsg(0, "h"s, std::to_string(h));
1491}
1492
1493//////////////////////////////////////////////////////////////////////////////////////////
1494/// Set window title of web canvas
1495
1497{
1498 AddCtrlMsg(0, "title"s, newTitle);
1499}
1500
1501//////////////////////////////////////////////////////////////////////////////////////////
1502/// Set canvas size of web canvas
1503
1505{
1506 fFixedSize = kTRUE;
1507 AddCtrlMsg(0, "cw"s, std::to_string(cw));
1508 AddCtrlMsg(0, "ch"s, std::to_string(ch));
1509 if ((cw > 0) && (ch > 0)) {
1510 Canvas()->fCw = cw;
1511 Canvas()->fCh = ch;
1512 } else {
1513 // temporary value, will be reported back from client
1514 Canvas()->fCw = Canvas()->fWindowWidth;
1516 }
1517}
1518
1519//////////////////////////////////////////////////////////////////////////////////////////
1520/// Iconify browser window
1521
1523{
1524 AddCtrlMsg(0, "winstate"s, "iconify"s);
1525}
1526
1527//////////////////////////////////////////////////////////////////////////////////////////
1528/// Raise browser window
1529
1531{
1532 AddCtrlMsg(0, "winstate"s, "raise"s);
1533}
1534
1535//////////////////////////////////////////////////////////////////////////////////////////
1536/// Assign clients bits
1537
1546
1547//////////////////////////////////////////////////////////////////////////////////////////////////
1548/// Decode all pad options, which includes ranges plus objects options
1549
1551{
1552 if (IsReadOnly() || msg.empty())
1553 return kFALSE;
1554
1555 auto arr = TBufferJSON::FromJSON<std::vector<TWebPadOptions>>(msg);
1556
1557 if (!arr)
1558 return kFALSE;
1559
1561
1562 TPad *pad_with_execs = nullptr;
1563 TExec *hist_exec = nullptr;
1564
1565 for (unsigned n = 0; n < arr->size(); ++n) {
1566 auto &r = arr->at(n);
1567
1568 TPad *pad = dynamic_cast<TPad *>(FindPrimitive(r.snapid));
1569
1570 if (!pad)
1571 continue;
1572
1573 if (pad == Canvas()) {
1574 AssignStatusBits(r.bits);
1575 Canvas()->fCw = r.cw;
1576 Canvas()->fCh = r.ch;
1577 if (r.w.size() == 4)
1579 }
1580
1581 // only if get OPTIONS message from client allow to change gPad
1582 if (r.active && (pad != gPad) && process_execs)
1583 gPad = pad;
1584
1585 if ((pad->GetTickx() != r.tickx) || (pad->GetTicky() != r.ticky))
1586 pad->SetTicks(r.tickx, r.ticky);
1587 if ((pad->GetGridx() != (r.gridx > 0)) || (pad->GetGridy() != (r.gridy > 0)))
1588 pad->SetGrid(r.gridx, r.gridy);
1589 pad->fLogx = r.logx;
1590 pad->fLogy = r.logy;
1591 pad->fLogz = r.logz;
1592
1593 pad->SetLeftMargin(r.mleft);
1594 pad->SetRightMargin(r.mright);
1595 pad->SetTopMargin(r.mtop);
1596 pad->SetBottomMargin(r.mbottom);
1597
1598 if (r.ranges) {
1599 // avoid call of original methods, set members directly
1600 // pad->Range(r.px1, r.py1, r.px2, r.py2);
1601 // pad->RangeAxis(r.ux1, r.uy1, r.ux2, r.uy2);
1602
1603 pad->fX1 = r.px1;
1604 pad->fX2 = r.px2;
1605 pad->fY1 = r.py1;
1606 pad->fY2 = r.py2;
1607
1608 pad->fUxmin = r.ux1;
1609 pad->fUxmax = r.ux2;
1610 pad->fUymin = r.uy1;
1611 pad->fUymax = r.uy2;
1612 }
1613
1614 // pad->SetPad(r.mleft, r.mbottom, 1-r.mright, 1-r.mtop);
1615
1616 pad->fAbsXlowNDC = r.xlow;
1617 pad->fAbsYlowNDC = r.ylow;
1618 pad->fAbsWNDC = r.xup - r.xlow;
1619 pad->fAbsHNDC = r.yup - r.ylow;
1620
1621 if (pad == Canvas()) {
1622 pad->fXlowNDC = r.xlow;
1623 pad->fYlowNDC = r.ylow;
1624 pad->fXUpNDC = r.xup;
1625 pad->fYUpNDC = r.yup;
1626 pad->fWNDC = r.xup - r.xlow;
1627 pad->fHNDC = r.yup - r.ylow;
1628 } else {
1629 auto mother = pad->GetMother();
1630 if (mother->GetAbsWNDC() > 0. && mother->GetAbsHNDC() > 0.) {
1631 pad->fXlowNDC = (r.xlow - mother->GetAbsXlowNDC()) / mother->GetAbsWNDC();
1632 pad->fYlowNDC = (r.ylow - mother->GetAbsYlowNDC()) / mother->GetAbsHNDC();
1633 pad->fXUpNDC = (r.xup - mother->GetAbsXlowNDC()) / mother->GetAbsWNDC();
1634 pad->fYUpNDC = (r.yup - mother->GetAbsYlowNDC()) / mother->GetAbsHNDC();
1635 pad->fWNDC = (r.xup - r.xlow) / mother->GetAbsWNDC();
1636 pad->fHNDC = (r.yup - r.ylow) / mother->GetAbsHNDC();
1637 }
1638 }
1639
1640 if (r.phi || r.theta) {
1641 pad->fPhi = r.phi;
1642 pad->fTheta = r.theta;
1643 }
1644
1645 // copy of code from TPad::ResizePad()
1646
1647 Double_t pxlow = r.xlow * r.cw;
1648 Double_t pylow = (1-r.ylow) * r.ch;
1649 Double_t pxrange = (r.xup - r.xlow) * r.cw;
1650 Double_t pyrange = -1*(r.yup - r.ylow) * r.ch;
1651
1652 Double_t rounding = 0.00005;
1653 Double_t xrange = r.px2 - r.px1;
1654 Double_t yrange = r.py2 - r.py1;
1655
1656 if ((xrange != 0.) && (pxrange != 0)) {
1657 // Linear X axis
1658 pad->fXtoAbsPixelk = rounding + pxlow - pxrange*r.px1/xrange; //origin at left
1659 pad->fXtoPixelk = rounding + -pxrange*r.px1/xrange;
1660 pad->fXtoPixel = pxrange/xrange;
1661 pad->fAbsPixeltoXk = r.px1 - pxlow*xrange/pxrange;
1662 pad->fPixeltoXk = r.px1;
1663 pad->fPixeltoX = xrange/pxrange;
1664 }
1665
1666 if ((yrange != 0.) && (pyrange != 0.)) {
1667 // Linear Y axis
1668 pad->fYtoAbsPixelk = rounding + pylow - pyrange*r.py1/yrange; //origin at top
1669 pad->fYtoPixelk = rounding + -pyrange - pyrange*r.py1/yrange;
1670 pad->fYtoPixel = pyrange/yrange;
1671 pad->fAbsPixeltoYk = r.py1 - pylow*yrange/pyrange;
1672 pad->fPixeltoYk = r.py1;
1673 pad->fPixeltoY = yrange/pyrange;
1674 }
1675
1676 pad->SetFixedAspectRatio(kFALSE);
1677
1678 TObjLink *objlnk = nullptr;
1679
1680 TH1 *hist = static_cast<TH1 *>(FindPrimitive(sid_pad_histogram, 1, pad, &objlnk));
1681
1682 if (hist) {
1683
1684 TObject *hist_holder = objlnk ? objlnk->GetObject() : nullptr;
1685 if (hist_holder == hist)
1686 hist_holder = nullptr;
1687
1688 Bool_t no_entries = hist->GetEntries();
1690
1691 Double_t hmin = 0., hmax = 0.;
1692
1693 auto setAxisRange = [](TAxis *ax, Double_t r1, Double_t r2) {
1694 if (r1 != r2)
1695 ax->SetRangeUser(r1, r2);
1696 else if ((ax->GetFirst() == ax->GetLast()) || ((ax->GetFirst() > 0) && (ax->GetLast() <= ax->GetNbins())))
1697 // only if no underflow/overflow bins selected - let reset
1698 ax->SetRange(0, 0);
1699 };
1700
1701 setAxisRange(hist->GetXaxis(), r.zx1, r.zx2);
1702
1703 if (hist->GetDimension() == 1) {
1704 hmin = r.zy1;
1705 hmax = r.zy2;
1706 if ((hmin == hmax) && !no_entries && !is_stack) {
1707 // if there are no zooming on Y and histogram has no entries, hmin/hmax should be set to full range
1708 hmin = pad->fLogy ? TMath::Power(pad->fLogy < 2 ? 10 : pad->fLogy, r.uy1) : r.uy1;
1709 hmax = pad->fLogy ? TMath::Power(pad->fLogy < 2 ? 10 : pad->fLogy, r.uy2) : r.uy2;
1710 }
1711 } else {
1712 setAxisRange(hist->GetYaxis(), r.zy1, r.zy2);
1713 }
1714
1715 if (hist->GetDimension() == 2) {
1716 hmin = r.zz1;
1717 hmax = r.zz2;
1718 if ((hmin == hmax) && !no_entries) {
1719 // z scale is not transformed
1720 hmin = r.uz1;
1721 hmax = r.uz2;
1722 }
1723 } else if (hist->GetDimension() == 3) {
1724 setAxisRange(hist->GetZaxis(), r.zz1, r.zz2);
1725 }
1726
1727 if (hmin == hmax)
1728 hmin = hmax = -1111;
1729
1730 if (is_stack) {
1731 hist->SetMinimum(hmin);
1732 hist->SetMaximum(hmax);
1733 hist->SetBit(TH1::kIsZoomed, hmin != hmax);
1734 } else if (!hist_holder || (hist_holder->IsA() == TScatter::Class())) {
1735 hist->SetMinimum(hmin);
1736 hist->SetMaximum(hmax);
1737 } else {
1738 auto SetMember = [hist_holder](const char *name, Double_t value) {
1739 auto offset = hist_holder->IsA()->GetDataMemberOffset(name);
1740 if (offset > 0)
1741 *((Double_t *)((char*) hist_holder + offset)) = value;
1742 else
1743 ::Error("SetMember", "Cannot find %s data member in %s", name, hist_holder->ClassName());
1744 };
1745
1746 // directly set min/max in classes like THStack, TGraph, TMultiGraph
1747 SetMember("fMinimum", hmin);
1748 SetMember("fMaximum", hmax);
1749 }
1750
1751 TIter next(hist->GetListOfFunctions());
1752 while (auto fobj = next())
1753 if (!hist_exec && fobj->InheritsFrom(TExec::Class())) {
1754 hist_exec = (TExec *) fobj;
1756 }
1757 }
1758
1759 std::map<std::string, int> idmap;
1760
1761 for (auto &item : r.primitives) {
1762 auto iter = idmap.find(item.snapid);
1763 int idcnt = 1;
1764 if (iter == idmap.end())
1765 idmap[item.snapid] = 1;
1766 else
1767 idcnt = ++iter->second;
1768
1770 }
1771
1772 // without special objects no need for explicit update of the pad
1773 if (fPadsStatus[pad]._has_specials) {
1774 pad->Modified(kTRUE);
1776 }
1777
1778 if (process_execs && (gPad == pad))
1780 }
1781
1783
1784 if (fUpdatedSignal) fUpdatedSignal(); // invoke signal
1785
1786 return need_update;
1787}
1788
1789//////////////////////////////////////////////////////////////////////////////////////////////////
1790/// Process TExec objects in the pad
1791
1793{
1794 auto execs = pad ? pad->GetListOfExecs() : nullptr;
1795
1796 if ((!execs || !execs->GetSize()) && !extra)
1797 return;
1798
1799 auto saveps = gVirtualPS;
1800 gVirtualPS = nullptr;
1801
1802 auto savex = gVirtualX;
1803 TVirtualX x;
1804 gVirtualX = &x;
1805
1806 TIter next(execs);
1807 while (auto obj = next()) {
1808 auto exec = dynamic_cast<TExec *>(obj);
1809 if (exec)
1810 exec->Exec();
1811 }
1812
1813 if (extra)
1814 extra->Exec();
1815
1817 gVirtualX = savex;
1818}
1819
1820//////////////////////////////////////////////////////////////////////////////////////////
1821/// Execute one or several methods for selected object
1822/// String can be separated by ";;" to let execute several methods at once
1824{
1825 std::string buf = lines;
1826
1827 Int_t indx = 0;
1828
1829 while (obj && !buf.empty()) {
1830 std::string sub = buf;
1831 auto pos = buf.find(";;");
1832 if (pos == std::string::npos) {
1833 sub = buf;
1834 buf.clear();
1835 } else {
1836 sub = buf.substr(0,pos);
1837 buf = buf.substr(pos+2);
1838 }
1839 if (sub.empty()) continue;
1840
1841 std::stringstream exec;
1842 exec << "((" << obj->ClassName() << " *) " << std::hex << std::showbase << (size_t)obj << ")->" << sub << ";";
1844 Info("ProcessLinesForObject", "Obj %s Execute %s", obj->GetName(), exec.str().c_str());
1845 gROOT->ProcessLine(exec.str().c_str());
1846 indx++;
1847 }
1848}
1849
1850//////////////////////////////////////////////////////////////////////////////////////////
1851/// Handle data from web browser
1852/// Returns kFALSE if message was not processed
1853
1854Bool_t TWebCanvas::ProcessData(unsigned connid, const std::string &arg)
1855{
1856 if (arg.empty())
1857 return kTRUE;
1858
1859 // try to identify connection for given WS request
1860 unsigned indx = 0; // first connection is batch and excluded
1861 while(++indx < fWebConn.size()) {
1862 if (fWebConn[indx].fConnId == connid)
1863 break;
1864 }
1865 if (indx >= fWebConn.size())
1866 return kTRUE;
1867
1868 Bool_t is_main_connection = indx == 1; // first connection allow to make changes
1869
1870 struct FlagGuard {
1871 Bool_t &flag;
1872 FlagGuard(Bool_t &_flag) : flag(_flag) { flag = true; }
1873 ~FlagGuard() { flag = false; }
1874 };
1875
1877
1878 const char *cdata = arg.c_str();
1879
1880 if (arg == "KEEPALIVE") {
1881 // do nothing
1882
1883 } else if (arg == "QUIT") {
1884
1885 // use window manager to correctly terminate http server
1886 fWindow->TerminateROOT();
1887
1888 } else if (arg.compare(0, 7, "READY6:") == 0) {
1889
1890 // this is reply on drawing of ROOT6 snapshot
1891 // it confirms when drawing of specific canvas version is completed
1892
1893 cdata += 7;
1894
1895 const char *separ = strchr(cdata, ':');
1896 if (!separ) {
1897 fWebConn[indx].fDrawVersion = std::stoll(cdata);
1898 } else {
1899 fWebConn[indx].fDrawVersion = std::stoll(std::string(cdata, separ - cdata));
1901 if (DecodePadOptions(separ+1, false))
1903 }
1904
1905 if (indx == 1)
1906 fLastDrawVersion = fWebConn[indx].fDrawVersion;
1907
1908 } else if (arg == "RELOAD") {
1909
1910 // trigger reload of canvas data
1911 fWebConn[indx].reset();
1912
1913 } else if (arg.compare(0, 5, "SAVE:") == 0) {
1914
1915 // save image produced by the client side - like png or svg
1916 const char *img = cdata + 5;
1917
1918 const char *separ = strchr(img, ':');
1919 if (separ) {
1921 img = separ + 1;
1922
1923 std::ofstream ofs(filename.Data());
1924
1925 int filelen = -1;
1926
1927 if ((filename.Index(".svg") != kNPOS) || (filename.Index(".html") != kNPOS)) {
1928 // ofs << "<?xml version=\"1.0\" standalone=\"no\"?>";
1929 ofs << img;
1930 filelen = strlen(img);
1931 } else {
1933 ofs.write(binary.Data(), binary.Length());
1934 filelen = binary.Length();
1935 }
1936 ofs.close();
1937
1938 Info("ProcessData", "File %s size %d has been created", filename.Data(), filelen);
1939 }
1940
1941 } else if (arg.compare(0, 8, "PRODUCE:") == 0) {
1942
1943 // create ROOT, PDF, ... files using native ROOT functionality
1944 Canvas()->Print(arg.c_str() + 8);
1945
1946 } else if (arg.compare(0, 8, "GETMENU:") == 0) {
1947
1948 TObject *obj = FindPrimitive(arg.substr(8));
1949 if (!obj)
1950 obj = Canvas();
1951
1952 TWebMenuItems items(arg.c_str() + 8);
1953 items.PopulateObjectMenu(obj, obj->IsA());
1954 std::string buf = "MENU:";
1955 buf.append(TBufferJSON::ToJSON(&items, 103).Data());
1956
1957 AddSendQueue(connid, buf);
1958
1959 } else if (arg.compare(0, 11, "STATUSBITS:") == 0) {
1960
1961 if (is_main_connection) {
1962 AssignStatusBits(std::stoul(arg.substr(11)));
1963 if (fUpdatedSignal) fUpdatedSignal(); // invoke signal
1964 }
1965
1966 } else if (arg.compare(0, 10, "HIGHLIGHT:") == 0) {
1967
1968 if (is_main_connection) {
1969 auto arr = TBufferJSON::FromJSON<std::vector<std::string>>(arg.substr(10));
1970 if (!arr || (arr->size() != 4)) {
1971 Error("ProcessData", "Wrong arguments count %d in highlight message", (int)(arr ? arr->size() : -1));
1972 } else {
1973 auto pad = dynamic_cast<TVirtualPad *>(FindPrimitive(arr->at(0)));
1974 auto obj = FindPrimitive(arr->at(1));
1975 int argx = std::stoi(arr->at(2));
1976 int argy = std::stoi(arr->at(3));
1977 if (pad && obj) {
1978 Canvas()->Highlighted(pad, obj, argx, argy);
1980 }
1981 }
1982 }
1983
1984 } else if (ROOT::RWebWindow::IsFileDialogMessage(arg)) {
1985
1987
1988 } else if (IsReadOnly() || !is_main_connection) {
1989
1990 ///////////////////////////////////////////////////////////////////////////////////////
1991 // all following messages are not allowed in readonly mode or for secondary connections
1992
1993 return kFALSE;
1994
1995 } else if (arg.compare(0, 9, "OPTIONS6:") == 0) {
1996
1997 if (DecodePadOptions(arg.substr(9), true))
1999
2000 } else if (arg.compare(0, 9, "FITPANEL:") == 0) {
2001
2002 std::string chid = arg.substr(9);
2003
2004 TH1 *hist = nullptr;
2005 TIter iter(Canvas()->GetListOfPrimitives());
2006 while (auto obj = iter()) {
2007 hist = dynamic_cast<TH1 *>(obj);
2008 if (hist) break;
2009 }
2010
2012 if (chid == "standalone")
2013 showcmd = "panel->Show()";
2014 else
2015 showcmd = TString::Format("auto wptr = (std::shared_ptr<ROOT::RWebWindow>*)0x%zx;"
2016 "panel->Show({*wptr, %u, %s})",
2017 (size_t) &fWindow, connid, chid.c_str());
2018
2019 auto cmd = TString::Format("auto panel = std::make_shared<ROOT::Experimental::RFitPanel>(\"FitPanel\");"
2020 "panel->AssignCanvas(\"%s\");"
2021 "panel->AssignHistogram((TH1 *)0x%zx);"
2022 "%s;panel->ClearOnClose(panel);",
2023 Canvas()->GetName(), (size_t) hist, showcmd.Data());
2024 gROOT->ProcessLine(cmd.Data());
2025 } else if (arg == "START_BROWSER"s) {
2026
2027 gROOT->ProcessLine("new TBrowser;");
2028
2029 } else if (arg.compare(0, 6, "EVENT:") == 0) {
2030 auto arr = TBufferJSON::FromJSON<std::vector<std::string>>(arg.substr(6));
2031 if (!arr || (arr->size() != 5)) {
2032 Error("ProcessData", "Wrong arguments count %d in event message", (int)(arr ? arr->size() : -1));
2033 } else {
2034 auto pad = dynamic_cast<TPad *>(FindPrimitive(arr->at(0)));
2035 std::string kind = arr->at(1);
2036 int event = -1;
2037 if (kind == "move"s) event = kMouseMotion;
2038 int argx = std::stoi(arr->at(2));
2039 int argy = std::stoi(arr->at(3));
2040 auto selobj = FindPrimitive(arr->at(4));
2041
2042 if ((event >= 0) && pad && (pad == gPad)) {
2043 Canvas()->fEvent = event;
2044 Canvas()->fEventX = argx;
2045 Canvas()->fEventY = argy;
2046
2047 Canvas()->fSelected = selobj;
2048
2050 }
2051 }
2052
2053 } else if (arg.compare(0, 8, "PRIMIT6:") == 0) {
2054
2055 auto opt = TBufferJSON::FromJSON<TWebObjectOptions>(arg.c_str() + 8);
2056
2057 if (opt) {
2058 TPad *modpad = ProcessObjectOptions(*opt, nullptr);
2059
2060 // indicate that pad was modified
2061 if (modpad)
2062 modpad->Modified();
2063 }
2064
2065 } else if (arg.compare(0, 11, "PADCLICKED:") == 0) {
2066
2067 auto click = TBufferJSON::FromJSON<TWebPadClick>(arg.c_str() + 11);
2068
2069 if (click) {
2070
2071 TPad *pad = dynamic_cast<TPad *>(FindPrimitive(click->padid));
2072
2073 if (pad && pad->InheritsFrom(TButton::Class())) {
2074 auto btn = (TButton *) pad;
2075 const char *mthd = btn->GetMethod();
2076 if (mthd && *mthd) {
2077 auto cpad = gROOT->GetSelectedPad();
2078 if (cpad)
2079 cpad->cd();
2080 gROOT->ProcessLine(mthd);
2081 }
2082 return kTRUE;
2083 }
2084
2085 if (pad && (pad != gPad)) {
2086 gPad = pad;
2090 }
2091
2092 if (!click->objid.empty()) {
2093 auto selobj = FindPrimitive(click->objid);
2095 Canvas()->fSelected = selobj;
2096 if (pad && selobj && fObjSelectSignal)
2098 }
2099
2100 if ((click->x >= 0) && (click->y >= 0)) {
2102 Canvas()->fEventX = click->x;
2103 Canvas()->fEventY = click->y;
2104 if (click->dbl && fPadDblClickedSignal)
2106 else if (!click->dbl && fPadClickedSignal)
2108 }
2109
2111 }
2112
2113 } else if (arg.compare(0, 8, "OBJEXEC:") == 0) {
2114
2115 auto buf = arg.substr(8);
2116 auto pos = buf.find(":");
2117
2118 if ((pos > 0) && (pos != std::string::npos)) {
2119 auto sid = buf.substr(0, pos);
2120 buf.erase(0, pos + 1);
2121
2122 TObjLink *lnk = nullptr;
2123 TPad *objpad = nullptr;
2124
2125 TObject *obj = FindPrimitive(sid, 1, nullptr, &lnk, &objpad);
2126
2127 if (obj && !buf.empty()) {
2128
2130
2131 if (objpad)
2132 objpad->Modified();
2133 else
2134 Canvas()->Modified();
2135
2137 }
2138 }
2139
2140 } else if (arg.compare(0, 12, "EXECANDSEND:") == 0) {
2141
2142 // execute method and send data, used by drawing projections
2143
2144 std::string buf = arg.substr(12);
2145 std::string reply;
2146 TObject *obj = nullptr;
2147
2148 auto pos = buf.find(":");
2149
2150 if (pos > 0) {
2151 // only first client can execute commands
2152 reply = buf.substr(0, pos);
2153 buf.erase(0, pos + 1);
2154 pos = buf.find(":");
2155 if (pos > 0) {
2156 auto sid = buf.substr(0, pos);
2157 buf.erase(0, pos + 1);
2158 obj = FindPrimitive(sid);
2159 }
2160 }
2161
2162 if (obj && !buf.empty() && !reply.empty()) {
2163 std::stringstream exec;
2164 exec << "((" << obj->ClassName() << " *) " << std::hex << std::showbase << (size_t)obj
2165 << ")->" << buf << ";";
2166 if (gDebug > 0)
2167 Info("ProcessData", "Obj %s Exec %s", obj->GetName(), exec.str().c_str());
2168
2169 auto res = gROOT->ProcessLine(exec.str().c_str());
2170 TObject *resobj = (TObject *)(res);
2171 if (resobj) {
2172 std::string send = reply;
2173 send.append(":");
2174 send.append(TBufferJSON::ToJSON(resobj, 23).Data());
2175 AddSendQueue(connid, send);
2176 if (reply[0] == 'D')
2177 delete resobj; // delete object if first symbol in reply is D
2178 }
2179 }
2180
2181 } else if (arg.compare(0, 6, "CLEAR:") == 0) {
2182 std::string snapid = arg.substr(6);
2183
2184 TPad *pad = dynamic_cast<TPad *>(FindPrimitive(snapid));
2185
2186 if (pad) {
2187 pad->Clear();
2188 pad->Modified();
2190 } else {
2191 Error("ProcessData", "Not found pad with id %s to clear\n", snapid.c_str());
2192 }
2193 } else if (arg.compare(0, 7, "DIVIDE:") == 0) {
2194 auto arr = TBufferJSON::FromJSON<std::vector<std::string>>(arg.substr(7));
2195 if (arr && arr->size() == 2) {
2196 TPad *pad = dynamic_cast<TPad *>(FindPrimitive(arr->at(0)));
2197 int nn = 0, n1 = 0, n2 = 0;
2198
2199 std::string divide = arr->at(1);
2200 auto p = divide.find('x');
2201 if (p == std::string::npos)
2202 p = divide.find('X');
2203
2204 if (p != std::string::npos) {
2205 n1 = std::stoi(divide.substr(0,p));
2206 n2 = std::stoi(divide.substr(p+1));
2207 } else {
2208 nn = std::stoi(divide);
2209 }
2210
2211 if (pad && ((nn > 1) || (n1*n2 > 1))) {
2212 pad->Clear();
2213 pad->Modified();
2214 if (nn > 1)
2215 pad->DivideSquare(nn);
2216 else
2217 pad->Divide(n1, n2);
2218 pad->cd(1);
2220 }
2221 }
2222
2223 } else if (arg.compare(0, 8, "DRAWOPT:") == 0) {
2224
2225 auto arr = TBufferJSON::FromJSON<std::vector<std::string>>(arg.substr(8));
2226 if (arr && arr->size() == 2) {
2227 TObjLink *objlnk = nullptr;
2228 FindPrimitive(arr->at(0), 1, nullptr, &objlnk);
2229 if (objlnk)
2230 objlnk->SetOption(arr->at(1).c_str());
2231 }
2232
2233 } else if (arg.compare(0, 8, "RESIZED:") == 0) {
2234
2235 auto arr = TBufferJSON::FromJSON<std::vector<int>>(arg.substr(8));
2236 if (arr && arr->size() == 7) {
2237 // set members directly to avoid redrawing of the client again
2238 Canvas()->fCw = arr->at(4);
2239 Canvas()->fCh = arr->at(5);
2240 fFixedSize = arr->at(6) > 0;
2241 arr->resize(4);
2243 }
2244
2245 } else if (arg.compare(0, 7, "POPOBJ:") == 0) {
2246
2247 auto arr = TBufferJSON::FromJSON<std::vector<std::string>>(arg.substr(7));
2248 if (arr && arr->size() == 2) {
2249 TPad *pad = dynamic_cast<TPad *>(FindPrimitive(arr->at(0)));
2250 TObject *obj = FindPrimitive(arr->at(1), 0, pad);
2251 if (pad && obj && (obj != pad->GetListOfPrimitives()->Last())) {
2252 TIter next(pad->GetListOfPrimitives());
2253 while (auto o = next())
2254 if (obj == o) {
2255 TString opt = next.GetOption();
2256 pad->Remove(obj, kFALSE);
2257 pad->Add(obj, opt.Data());
2258 break;
2259 }
2260 }
2261 }
2262
2263 } else if (arg.compare(0, 8, "SHOWURL:") == 0) {
2264
2266 args.SetUrl(arg.substr(8));
2267 args.SetStandalone(false);
2268
2270
2271 } else if (arg == "INTERRUPT"s) {
2272
2273 gROOT->SetInterrupt();
2274
2275 } else {
2276
2277 // unknown message, probably should be processed by other implementation
2278 return kFALSE;
2279
2280 }
2281
2282 return kTRUE;
2283}
2284
2285//////////////////////////////////////////////////////////////////////////////////////////
2286/// Returns true if any pad in the canvas were modified
2287/// Reset modified flags, increment canvas version (if inc_version is true)
2288
2290{
2291 if (fPadsStatus.find(pad) == fPadsStatus.end())
2292 fPadsStatus[pad] = PadStatus{0, true, true};
2293
2294 auto &entry = fPadsStatus[pad];
2295 entry._detected = true;
2296 if (pad->IsModified()) {
2297 pad->Modified(kFALSE);
2298 entry._modified = true;
2299 }
2300
2301 TIter iter(pad->GetListOfPrimitives());
2302 while (auto obj = iter()) {
2303 if (obj->IsA() == TPad::Class())
2304 CheckPadModified(static_cast<TPad *>(obj));
2305 }
2306}
2307
2308//////////////////////////////////////////////////////////////////////////////////////////
2309/// Check if any pad on the canvas was modified
2310/// If yes, increment version of correspondent pad
2311/// Returns true when canvas really modified
2312
2314{
2315 // clear temporary flags
2316 for (auto &entry : fPadsStatus) {
2317 entry.second._detected = false;
2318 entry.second._modified = force_modified;
2319 }
2320
2321 // scan sub-pads
2323
2324 // remove no-longer existing pads
2325 bool is_any_modified = false;
2326 for(auto iter = fPadsStatus.begin(); iter != fPadsStatus.end(); ) {
2327 if (iter->second._modified)
2328 is_any_modified = true;
2329 if (!iter->second._detected)
2330 fPadsStatus.erase(iter++);
2331 else
2332 iter++;
2333 }
2334
2335 // if any pad modified, increment canvas version and set version of modified pads
2336 if (is_any_modified) {
2337 fCanvVersion++;
2338 for(auto &entry : fPadsStatus)
2339 if (entry.second._modified)
2340 entry.second.fVersion = fCanvVersion;
2341 }
2342
2343 return is_any_modified;
2344}
2345
2346//////////////////////////////////////////////////////////////////////////////////////////
2347/// Set window geometry as array with coordinates and dimensions
2348
2349void TWebCanvas::SetWindowGeometry(const std::vector<int> &arr)
2350{
2352 Canvas()->fWindowTopX = arr[0];
2353 Canvas()->fWindowTopY = arr[1];
2354 Canvas()->fWindowWidth = arr[2];
2355 Canvas()->fWindowHeight = arr[3];
2356 if (fWindow) {
2357 // position is unreliable and cannot be used
2358 // fWindow->SetPosition(arr[0], arr[1]);
2359 fWindow->SetGeometry(arr[2], arr[3]);
2360 }
2361}
2362
2363//////////////////////////////////////////////////////////////////////////////////////////
2364/// Returns window geometry including borders and menus
2365
2367{
2368 if (fWindowGeometry.size() == 4) {
2369 x = fWindowGeometry[0];
2370 y = fWindowGeometry[1];
2371 w = fWindowGeometry[2];
2372 h = fWindowGeometry[3];
2373 } else {
2374 x = Canvas()->fWindowTopX;
2375 y = Canvas()->fWindowTopY;
2376 w = Canvas()->fWindowWidth;
2377 h = Canvas()->fWindowHeight;
2378 }
2379 return 0;
2380}
2381
2382
2383//////////////////////////////////////////////////////////////////////////////////////////
2384/// if canvas or any subpad was modified,
2385/// scan all primitives in the TCanvas and subpads and convert them into
2386/// the structure which will be delivered to JSROOT client
2387
2389{
2391
2393
2394 if (!fProcessingData && !IsAsyncMode() && !async)
2396 else if (fWindow)
2397 fWindow->Sync();
2398
2399 return kTRUE;
2400}
2401
2402//////////////////////////////////////////////////////////////////////////////////////////
2403/// Increment canvas version and force sending data to client - do not wait for reply
2404
2406{
2407 CheckCanvasModified(true);
2408
2409 if (!fWindow) {
2410 TCanvasWebSnapshot holder(IsReadOnly(), false, true); // readonly, set ids, batchmode
2411
2412 holder.SetScripts(ProcessCustomScripts(true));
2413
2414 CreatePadSnapshot(holder, Canvas(), 0, nullptr);
2415 } else {
2417 }
2418}
2419
2420//////////////////////////////////////////////////////////////////////////////////////////
2421/// Wait when specified version of canvas was painted and confirmed by browser
2422
2424{
2425 if (!fWindow)
2426 return kTRUE;
2427
2428 // simple polling loop until specified version delivered to the clients
2429 // first 500 loops done without sleep, then with 1ms sleep and last 500 with 100 ms sleep
2430
2431 long cnt = 0, cnt_limit = GetLongerPolling() ? 5500 : 1500;
2432
2433 if (gDebug > 2)
2434 Info("WaitWhenCanvasPainted", "version %ld", (long)ver);
2435
2436 while (cnt++ < cnt_limit) {
2437
2438 // handle send operations, check connection timeouts
2439 fWindow->Sync();
2440
2441 if (!fWindow->HasConnection(0, false)) {
2442 if (gDebug > 2)
2443 Info("WaitWhenCanvasPainted", "no connections - abort");
2444 return kFALSE; // wait ~1 min if no new connection established
2445 }
2446
2447 if ((fWebConn.size() > 1) && (fWebConn[1].fDrawVersion >= ver)) {
2448 if (gDebug > 2)
2449 Info("WaitWhenCanvasPainted", "ver %ld got painted", (long)ver);
2450 return kTRUE;
2451 }
2452
2453 if (!fWindow->HasConnection(0) && (fLastDrawVersion > 0)) {
2454 if (gDebug > 2)
2455 Info("WaitWhenCanvasPainted", "ver %ld got painted before client disconnected", (long)fLastDrawVersion);
2456 return kTRUE;
2457 }
2458
2460 if (cnt > 500)
2461 gSystem->Sleep((cnt < cnt_limit - 500) ? 1 : 100); // increase sleep interval when do very often
2462 }
2463
2464 if (gDebug > 2)
2465 Info("WaitWhenCanvasPainted", "timeout");
2466
2467 return kFALSE;
2468}
2469
2470//////////////////////////////////////////////////////////////////////////////////////////
2471/// Create JSON painting output for given pad
2472/// Produce JSON can be used for offline drawing with JSROOT
2473
2475{
2476 TString res;
2477 if (!pad)
2478 return res;
2479
2480 TCanvas *c = dynamic_cast<TCanvas *>(pad);
2481 if (c) {
2483 } else {
2484 auto imp = std::make_unique<TWebCanvas>(pad->GetCanvas(), pad->GetName(), 0, 0, pad->GetWw(), pad->GetWh(), kTRUE);
2485
2486 TPadWebSnapshot holder(true, false, batchmode); // readonly, no ids, batchmode
2487
2488 imp->CreatePadSnapshot(holder, pad, 0, [&res, json_compression](TPadWebSnapshot *snap) {
2490 });
2491 }
2492
2493 return res;
2494}
2495
2496//////////////////////////////////////////////////////////////////////////////////////////
2497/// Create JSON painting output for given canvas
2498/// Produce JSON can be used for offline drawing with JSROOT
2499
2501{
2502 TString res;
2503
2504 if (!c)
2505 return res;
2506
2507 {
2508 auto imp = std::make_unique<TWebCanvas>(c, c->GetName(), 0, 0, c->GetWw(), c->GetWh(), kTRUE);
2509
2510 TCanvasWebSnapshot holder(true, false, batchmode); // readonly, no ids, batchmode
2511
2513
2514 imp->CreatePadSnapshot(holder, c, 0, [&res, json_compression](TPadWebSnapshot *snap) {
2516 });
2517 }
2518
2519 return res;
2520}
2521
2522//////////////////////////////////////////////////////////////////////////////////////////
2523/// Create JSON painting output for given canvas and store into the file
2524/// See TBufferJSON::ExportToFile() method for more details about option
2525/// If option string starts with symbol 'b', JSON for batch mode will be generated (default)
2526/// If option string starts with symbol 'i', JSON for interactive mode will be generated
2527
2529{
2530 Int_t res = 0;
2532 if (option) {
2533 if (*option == 'b') {
2534 batchmode = kTRUE;
2535 ++option;
2536 } else if (*option == 'i') {
2537 batchmode = kFALSE;
2538 ++option;
2539 }
2540 }
2541
2542 if (!c)
2543 return res;
2544
2545 {
2546 auto imp = std::make_unique<TWebCanvas>(c, c->GetName(), 0, 0, c->GetWw(), c->GetWh(), kTRUE);
2547
2548 TCanvasWebSnapshot holder(true, false, batchmode); // readonly, no ids, batchmode
2549
2551
2552 imp->CreatePadSnapshot(holder, c, 0, [&res, filename, option](TPadWebSnapshot *snap) {
2554 });
2555 }
2556
2557 return res;
2558}
2559
2560//////////////////////////////////////////////////////////////////////////////////////////
2561/// Create image using batch (headless) capability of Chrome or Firefox browsers
2562/// Supported png, jpeg, svg, pdf formats
2563
2565{
2566 if (!pad)
2567 return false;
2568
2570 if (!json.Length())
2571 return false;
2572
2573 TString fname = fileName;
2574 const char *endings[4] = {"(", "[", "]", ")"};
2575 const char *suffix = nullptr;
2576 for (int n = 0; (n < 4) && !suffix; ++n) {
2577 if (fname.EndsWith(endings[n])) {
2578 fname.Resize(fname.Length() - 1);
2579 suffix = endings[n];
2580 }
2581 }
2582
2584
2586 if (fmt.empty())
2587 return false;
2588
2589 if (suffix) {
2590 if (fmt != "pdf")
2591 return false;
2592 switch (*suffix) {
2593 case '(': gBatchMultiPdf = fname.Data(); flush_batch = kFALSE; break;
2594 case '[': gBatchMultiPdf = fname.Data(); append_batch = kFALSE; flush_batch = kFALSE; break;
2595 case ']': gBatchMultiPdf.clear(); append_batch = kFALSE; break;
2596 case ')': gBatchMultiPdf.clear(); fname.Append("+"); break;
2597 }
2598 } else if (fmt == "pdf") {
2599 if (!gBatchMultiPdf.empty()) {
2600 if (gBatchMultiPdf.compare(fileName) == 0) {
2603 suffix = "+"; // to let append to the batch
2604 if ((gBatchFiles.size() > 0) && (gBatchFiles.back().compare(0, fname.Length(), fname.Data()) == 0))
2605 fname.Append("+"); // .pdf+ means appending image to previous
2606 } else {
2607 ::Error("TWebCanvas::ProduceImage", "Cannot change PDF name when multi-page PDF active");
2608 return false;
2609 }
2610 }
2611 } else if (!gBatchMultiPdf.empty()) {
2612 ::Error("TWebCanvas::ProduceImage", "Cannot produce other images when multi-page PDF active");
2613 return false;
2614 }
2615
2616 if (!width && !height) {
2617 if ((pad->GetCanvas() == pad) || (pad->IsA() == TCanvas::Class())) {
2618 width = pad->GetWw();
2619 height = pad->GetWh();
2620 } else {
2621 width = (Int_t) (pad->GetAbsWNDC() * pad->GetCanvas()->GetWw());
2622 height = (Int_t) (pad->GetAbsHNDC() * pad->GetCanvas()->GetWh());
2623 }
2624 }
2625
2626 if (!suffix && (!gBatchImageMode || (fmt == "s.pdf") || (fmt == "json") || (fmt == "html") || (fmt == "s.png")))
2628
2629 if (append_batch) {
2630 gBatchFiles.emplace_back(fname.Data());
2631 gBatchJsons.emplace_back(json);
2632 gBatchWidths.emplace_back(width);
2633 gBatchHeights.emplace_back(height);
2634 }
2635
2636 if (!flush_batch || (gBatchJsons.size() < gBatchImageMode))
2637 return true;
2638
2639 return FlushBatchImages();
2640}
2641
2642//////////////////////////////////////////////////////////////////////////////////////////
2643/// Create images for several pads using batch (headless) capability of Chrome or Firefox browsers
2644/// Supported png, jpeg, svg, pdf, webp formats
2645/// One can include %d qualifier which will be replaced by image index using printf functionality.
2646/// If for pdf format %d qualifier not specified, all images will be stored in single PDF file.
2647/// For all other formats %d qualifier will be add before extension automatically
2648
2649bool TWebCanvas::ProduceImages(std::vector<TPad *> pads, const char *filename, Int_t width, Int_t height)
2650{
2651 if (pads.empty())
2652 return false;
2653
2654 std::vector<std::string> jsons;
2655 std::vector<Int_t> widths, heights;
2656
2657 for (unsigned n = 0; n < pads.size(); ++n) {
2658 auto pad = pads[n];
2659
2661 if (!json.Length())
2662 continue;
2663
2664 Int_t w = width, h = height;
2665
2666 if (!w && !h) {
2667 if ((pad->GetCanvas() == pad) || (pad->IsA() == TCanvas::Class())) {
2668 w = pad->GetWw();
2669 h = pad->GetWh();
2670 } else {
2671 w = (Int_t) (pad->GetAbsWNDC() * pad->GetCanvas()->GetWw());
2672 h = (Int_t) (pad->GetAbsHNDC() * pad->GetCanvas()->GetWh());
2673 }
2674 }
2675
2676 jsons.emplace_back(json.Data());
2677 widths.emplace_back(w);
2678 heights.emplace_back(h);
2679 }
2680
2682
2683 if (!gBatchImageMode || (fmt == "json") || (fmt == "s.png") || (fmt == "s.pdf"))
2685
2687
2689 gBatchJsons.insert(gBatchJsons.end(), jsons.begin(), jsons.end());
2692 if (gBatchJsons.size() < gBatchImageMode)
2693 return true;
2694
2695 return FlushBatchImages();
2696}
2697
2698
2699//////////////////////////////////////////////////////////////////////////////////////////
2700/// Process data for single primitive
2701/// Returns object pad if object was modified
2702
2704{
2705 TObjLink *lnk = nullptr;
2706 TPad *objpad = nullptr;
2707 TObject *obj = FindPrimitive(item.snapid, idcnt, pad, &lnk, &objpad);
2708
2709 if (item.fcust.compare("exec") == 0) {
2710 auto pos = item.opt.find("(");
2711 if (obj && (pos != std::string::npos) && obj->IsA()->GetMethodAllAny(item.opt.substr(0,pos).c_str())) {
2712 std::stringstream exec;
2713 exec << "((" << obj->ClassName() << " *) " << std::hex << std::showbase
2714 << (size_t)obj << ")->" << item.opt << ";";
2715 if (gDebug > 0)
2716 Info("ProcessObjectOptions", "Obj %s Execute %s", obj->GetName(), exec.str().c_str());
2717 gROOT->ProcessLine(exec.str().c_str());
2718 } else {
2719 Error("ProcessObjectOptions", "Fail to execute %s for object %p %s", item.opt.c_str(), obj, obj ? obj->ClassName() : "---");
2720 objpad = nullptr;
2721 }
2722 return objpad;
2723 }
2724
2725 bool modified = false;
2726
2727 if (obj && lnk) {
2728 auto pos = item.opt.find(";;use_"); // special coding of extra options
2729 if (pos != std::string::npos) item.opt.resize(pos);
2730
2731 if (gDebug > 0)
2732 Info("ProcessObjectOptions", "Set draw option %s for object %s %s", item.opt.c_str(),
2733 obj->ClassName(), obj->GetName());
2734
2735 lnk->SetOption(item.opt.c_str());
2736
2737 modified = true;
2738 }
2739
2740 if (item.fcust.compare(0,10,"auto_exec:") == 0) {
2741 ProcessLinesForObject(obj, item.fcust.substr(10));
2742 } else if (item.fcust.compare("frame") == 0) {
2743 if (obj && obj->InheritsFrom(TFrame::Class())) {
2744 TFrame *frame = static_cast<TFrame *>(obj);
2745 if (item.fopt.size() >= 4) {
2746 frame->SetX1(item.fopt[0]);
2747 frame->SetY1(item.fopt[1]);
2748 frame->SetX2(item.fopt[2]);
2749 frame->SetY2(item.fopt[3]);
2750 modified = true;
2751 }
2752 }
2753 } else if (item.fcust.compare(0,4,"pave") == 0) {
2754 if (obj && obj->InheritsFrom(TPave::Class())) {
2755 TPave *pave = static_cast<TPave *>(obj);
2756 if ((item.fopt.size() >= 4) && objpad) {
2758
2759 // first time need to overcome init problem
2760 pave->ConvertNDCtoPad();
2761
2762 pave->SetX1NDC(item.fopt[0]);
2763 pave->SetY1NDC(item.fopt[1]);
2764 pave->SetX2NDC(item.fopt[2]);
2765 pave->SetY2NDC(item.fopt[3]);
2766 modified = true;
2767
2768 pave->ConvertNDCtoPad();
2769 }
2770 if ((item.fcust.length() > 4) && pave->InheritsFrom(TPaveStats::Class())) {
2771 // add text lines for statsbox
2772 auto stats = static_cast<TPaveStats *>(pave);
2773 stats->Clear();
2774 size_t pos_start = 6, pos_end;
2775 while ((pos_end = item.fcust.find(";;", pos_start)) != std::string::npos) {
2776 stats->AddText(item.fcust.substr(pos_start, pos_end - pos_start).c_str());
2777 pos_start = pos_end + 2;
2778 }
2779 stats->AddText(item.fcust.substr(pos_start).c_str());
2780 }
2781 }
2782 } else if (item.fcust.compare(0,9,"func_fail") == 0) {
2783 if (fTF1UseSave <= 0) {
2784 fTF1UseSave = 1;
2785 modified = true;
2786 }
2787 }
2788
2789 return modified ? objpad : nullptr;
2790}
2791
2792//////////////////////////////////////////////////////////////////////////////////////////////////
2793/// Search of object with given id in list of primitives
2794/// One could specify pad where search could be start
2795/// Also if object is in list of primitives, one could ask for entry link for such object,
2796/// This can allow to change draw option
2797
2799{
2800 if (sid.empty() || (sid == "0"s))
2801 return nullptr;
2802
2803 if (!pad)
2804 pad = Canvas();
2805
2806 std::string subelement;
2807 long unsigned id = 0;
2808 bool search_hist = (sid == sid_pad_histogram);
2809 if (!search_hist) {
2810 auto separ = sid.find("#");
2811
2812 if (separ == std::string::npos) {
2813 id = std::stoul(sid);
2814 } else {
2815 subelement = sid.substr(separ + 1);
2816 id = std::stoul(sid.substr(0, separ));
2817 }
2818 if (TString::Hash(&pad, sizeof(pad)) == id)
2819 return pad;
2820 }
2821
2822 for (auto lnk = pad->GetListOfPrimitives()->FirstLink(); lnk != nullptr; lnk = lnk->Next()) {
2823 TObject *obj = lnk->GetObject();
2824 if (!obj) continue;
2825
2826 if (!search_hist && (TString::Hash(&obj, sizeof(obj)) != id)) {
2827 if (obj->IsA() == TPad::Class()) {
2828 obj = FindPrimitive(sid, idcnt, (TPad *)obj, objlnk, objpad);
2829 if (objpad && !*objpad)
2830 *objpad = pad;
2831 if (obj)
2832 return obj;
2833 }
2834 continue;
2835 }
2836
2837 // one may require to access n-th object
2838 if (!search_hist && --idcnt > 0)
2839 continue;
2840
2841 if (objpad)
2842 *objpad = pad;
2843
2844 if (objlnk)
2845 *objlnk = lnk;
2846
2847 if (search_hist)
2848 subelement = "hist";
2849
2850 auto getHistogram = [](TObject *container) -> TH1* {
2851 auto offset = container->IsA()->GetDataMemberOffset("fHistogram");
2852 if (offset > 0)
2853 return *((TH1 **)((char *)container + offset));
2854 ::Error("getHistogram", "Cannot access fHistogram data member in %s", container->ClassName());
2855 return nullptr;
2856 };
2857
2858 while(!subelement.empty() && obj) {
2859 // do not return link if sub-selement is searched - except for histogram
2860 if (!search_hist && objlnk)
2861 *objlnk = nullptr;
2862
2863 std::string kind = subelement;
2864 auto separ = kind.find("#");
2865 if (separ == std::string::npos) {
2866 subelement.clear();
2867 } else {
2868 kind.resize(separ);
2869 subelement = subelement.substr(separ + 1);
2870 }
2871
2872 TH1 *h1 = obj->InheritsFrom(TH1::Class()) ? static_cast<TH1 *>(obj) : nullptr;
2873 TGraph *gr = obj->InheritsFrom(TGraph::Class()) ? static_cast<TGraph *>(obj) : nullptr;
2874 TGraph2D *gr2d = obj->InheritsFrom(TGraph2D::Class()) ? static_cast<TGraph2D *>(obj) : nullptr;
2875 TScatter *scatter = obj->InheritsFrom(TScatter::Class()) ? static_cast<TScatter *>(obj) : nullptr;
2876 TMultiGraph *mg = obj->InheritsFrom(TMultiGraph::Class()) ? static_cast<TMultiGraph *>(obj) : nullptr;
2877 THStack *hs = obj->InheritsFrom(THStack::Class()) ? static_cast<THStack *>(obj) : nullptr;
2878 TF1 *f1 = obj->InheritsFrom(TF1::Class()) ? static_cast<TF1 *>(obj) : nullptr;
2879
2880 if (kind.compare("hist") == 0) {
2881 if (h1)
2882 obj = h1;
2883 else if (gr)
2884 obj = getHistogram(gr);
2885 else if (mg)
2886 obj = getHistogram(mg);
2887 else if (hs && (hs->GetNhists() > 0))
2888 obj = getHistogram(hs);
2889 else if (scatter)
2890 obj = getHistogram(scatter);
2891 else if (f1)
2892 obj = getHistogram(f1);
2893 else if (gr2d)
2894 obj = getHistogram(gr2d);
2895 else
2896 obj = nullptr;
2897 } else if (kind.compare("x") == 0) {
2898 obj = h1 ? h1->GetXaxis() : nullptr;
2899 } else if (kind.compare("y") == 0) {
2900 obj = h1 ? h1->GetYaxis() : nullptr;
2901 } else if (kind.compare("z") == 0) {
2902 obj = h1 ? h1->GetZaxis() : nullptr;
2903 } else if ((kind.compare(0,5,"func_") == 0) || (kind.compare(0,5,"indx_") == 0)) {
2904 auto funcname = kind.substr(5);
2905 TList *col = nullptr;
2906 if (h1)
2908 else if (gr)
2910 else if (mg)
2911 col = mg->GetListOfFunctions();
2912 else if (scatter->GetGraph())
2913 col = scatter->GetGraph()->GetListOfFunctions();
2914 if (!col)
2915 obj = nullptr;
2916 else if (kind.compare(0,5,"func_") == 0)
2917 obj = col->FindObject(funcname.c_str());
2918 else
2919 obj = col->At(std::stoi(funcname));
2920 } else if (kind.compare("polargram") == 0) {
2921 auto polar = dynamic_cast<TGraphPolar *>(obj);
2922 obj = polar ? polar->GetPolargram() : nullptr;
2923 } else if (kind.compare(0,7,"graphs_") == 0) {
2924 TList *graphs = mg ? mg->GetListOfGraphs() : nullptr;
2925 obj = graphs ? graphs->At(std::stoi(kind.substr(7))) : nullptr;
2926 } else if (kind.compare(0,6,"hists_") == 0) {
2927 TList *hists = hs ? hs->GetHists() : nullptr;
2928 obj = hists ? hists->At(std::stoi(kind.substr(6))) : nullptr;
2929 } else if (kind.compare(0,6,"stack_") == 0) {
2930 auto stack = hs ? hs->GetStack() : nullptr;
2931 obj = stack ? stack->At(std::stoi(kind.substr(6))) : nullptr;
2932 } else if (kind.compare(0,7,"member_") == 0) {
2933 auto member = kind.substr(7);
2934 auto offset = obj->IsA() ? obj->IsA()->GetDataMemberOffset(member.c_str()) : 0;
2935 obj = (offset > 0) ? *((TObject **)((char *) obj + offset)) : nullptr;
2936 } else {
2937 obj = nullptr;
2938 }
2939 }
2940
2941 if (!search_hist || obj)
2942 return obj;
2943 }
2944
2945 return nullptr;
2946}
2947
2948//////////////////////////////////////////////////////////////////////////////////////////////////
2949/// Static method to create TWebCanvas instance
2950/// Used by plugin manager
2951
2953{
2954 Bool_t readonly = gEnv->GetValue("WebGui.FullCanvas", (Int_t) 1) == 0;
2955
2956 auto imp = new TWebCanvas(c, name, x, y, width, height, readonly);
2957
2958 c->fWindowTopX = x;
2959 c->fWindowTopY = y;
2960 c->fWindowWidth = width;
2961 c->fWindowHeight = height;
2962 if (!gROOT->IsBatch() && (height > 25))
2963 height -= 25;
2964 c->fCw = width;
2965 c->fCh = height;
2966
2967 return imp;
2968}
2969
2970//////////////////////////////////////////////////////////////////////////////////////////////////
2971/// Create TCanvas and assign TWebCanvas implementation to it
2972/// Canvas is not displayed automatically, therefore canv->Show() method must be called
2973/// Or canvas can be embed in other widgets.
2974
2976{
2977 auto canvas = new TCanvas(kFALSE);
2978 canvas->SetName(name);
2979 canvas->SetTitle(title);
2980 canvas->ResetBit(TCanvas::kShowEditor);
2981 canvas->ResetBit(TCanvas::kShowToolBar);
2982 canvas->SetBit(TCanvas::kMenuBar, kTRUE);
2983 canvas->SetCanvas(canvas);
2984 canvas->SetBatch(kTRUE); // mark canvas as batch
2985 canvas->SetEditable(kTRUE); // ensure fPrimitives are created
2986
2987 // copy gStyle attributes
2988 canvas->SetFillColor(gStyle->GetCanvasColor());
2989 canvas->SetFillStyle(1001);
2990 canvas->SetGrid(gStyle->GetPadGridX(),gStyle->GetPadGridY());
2991 canvas->SetTicks(gStyle->GetPadTickX(),gStyle->GetPadTickY());
2992 canvas->SetLogx(gStyle->GetOptLogx());
2993 canvas->SetLogy(gStyle->GetOptLogy());
2994 canvas->SetLogz(gStyle->GetOptLogz());
2995 canvas->SetBottomMargin(gStyle->GetPadBottomMargin());
2996 canvas->SetTopMargin(gStyle->GetPadTopMargin());
2997 canvas->SetLeftMargin(gStyle->GetPadLeftMargin());
2998 canvas->SetRightMargin(gStyle->GetPadRightMargin());
2999 canvas->SetBorderSize(gStyle->GetCanvasBorderSize());
3000 canvas->SetBorderMode(gStyle->GetCanvasBorderMode());
3001
3002 auto imp = static_cast<TWebCanvas *> (NewCanvas(canvas, name, 0, 0, width, height));
3003
3004 canvas->SetCanvasImp(imp);
3005
3006 canvas->cd();
3007
3008 {
3010 auto l1 = gROOT->GetListOfCleanups();
3011 if (!l1->FindObject(canvas))
3012 l1->Add(canvas);
3013 auto l2 = gROOT->GetListOfCanvases();
3014 if (!l2->FindObject(canvas))
3015 l2->Add(canvas);
3016 }
3017
3018 // ensure creation of web window
3019 imp->CreateWebWindow();
3020
3021 return canvas;
3022}
3023
@ kMouseMotion
Definition Buttons.h:23
@ kButton1Double
Definition Buttons.h:24
@ kButton1Up
Definition Buttons.h:19
nlohmann::json json
#define c(i)
Definition RSha256.hxx:101
#define h(i)
Definition RSha256.hxx:106
cudaEvent_t event
int Int_t
Signed integer 4 bytes (int)
Definition RtypesCore.h:60
unsigned int UInt_t
Unsigned integer 4 bytes (unsigned int)
Definition RtypesCore.h:61
short Font_t
Font number (short)
Definition RtypesCore.h:96
constexpr Bool_t kFALSE
Definition RtypesCore.h:109
constexpr Ssiz_t kNPOS
The equivalent of std::string::npos for the ROOT class TString.
Definition RtypesCore.h:132
long long Long64_t
Portable signed long integer 8 bytes.
Definition RtypesCore.h:84
constexpr Bool_t kTRUE
Definition RtypesCore.h:108
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
R__EXTERN TEnv * gEnv
Definition TEnv.h:126
void Info(const char *location, const char *msgfmt,...)
Use this function for informational messages.
Definition TError.cxx:241
void Error(const char *location, const char *msgfmt,...)
Use this function in case an error occurred.
Definition TError.cxx:208
winID h TVirtualViewer3D TVirtualGLPainter p
Option_t Option_t option
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void data
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t hmin
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char filename
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t hmax
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h offset
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t r
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void funcs
Option_t Option_t width
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t src
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t height
Option_t Option_t TPoint TPoint const char text
char name[80]
Definition TGX11.cxx:142
@ kCanDelete
Definition TObject.h:375
@ kMustCleanup
Definition TObject.h:376
Int_t gDebug
Global variable setting the debug level. Set to 0 to disable, increase it in steps of 1 to increase t...
Definition TROOT.cxx:792
R__EXTERN TVirtualMutex * gROOTMutex
Definition TROOT.h:63
#define gROOT
Definition TROOT.h:417
R__EXTERN TStyle * gStyle
Definition TStyle.h:442
@ kReadPermission
Definition TSystem.h:55
R__EXTERN TSystem * gSystem
Definition TSystem.h:582
#define R__LOCKGUARD(mutex)
R__EXTERN TVirtualPS * gVirtualPS
Definition TVirtualPS.h:93
#define gPad
#define gVirtualX
Definition TVirtualX.h:379
static std::vector< WebFont_t > gWebFonts
static const std::string sid_pad_histogram
const_iterator begin() const
const_iterator end() const
Holds different arguments for starting browser with RWebDisplayHandle::Display() method.
EBrowserKind GetBrowserKind() const
returns configured browser kind, see EBrowserKind for supported values
void SetStandalone(bool on=true)
Set standalone mode for running browser, default on When disabled, normal browser window (or just tab...
RWebDisplayArgs & SetWidgetKind(const std::string &kind)
set widget kind
RWebDisplayArgs & SetSize(int w, int h)
set preferable web window width and height
RWebDisplayArgs & SetUrl(const std::string &url)
set window url
RWebDisplayArgs & SetPos(int x=-1, int y=-1)
set preferable web window x and y position, negative is default
@ kCEF
Chromium Embedded Framework - local display with CEF libs.
@ kQt6
Qt6 QWebEngine libraries - Chromium code packed in qt6.
static bool ProduceImages(const std::string &fname, const std::vector< std::string > &jsons, const std::vector< int > &widths, const std::vector< int > &heights, const char *batch_file=nullptr)
Produce image file(s) using JSON data as source Invokes JSROOT drawing functionality in headless brow...
static std::vector< std::string > ProduceImagesNames(const std::string &fname, unsigned nfiles=1)
Produce vector of file names for specified file pattern Depending from supported file formats.
static std::string GetImageFormat(const std::string &fname)
Detect image format There is special handling of ".screenshot.pdf" and ".screenshot....
static bool ProduceImage(const std::string &fname, const std::string &json, int width=800, int height=600, const char *batch_file=nullptr)
Produce image file using JSON data as source Invokes JSROOT drawing functionality in headless browser...
static std::unique_ptr< RWebDisplayHandle > Display(const RWebDisplayArgs &args)
Create web display.
static std::shared_ptr< RWebWindow > Create()
Create new RWebWindow Using default RWebWindowsManager.
static unsigned ShowWindow(std::shared_ptr< RWebWindow > window, const RWebDisplayArgs &args="")
Static method to show web window Has to be used instead of RWebWindow::Show() when window potentially...
static bool EmbedFileDialog(const std::shared_ptr< RWebWindow > &window, unsigned connid, const std::string &args)
Create dialog instance to use as embedded dialog inside provided widget Loads libROOTBrowserv7 and tr...
static bool IsFileDialogMessage(const std::string &msg)
Check if this could be the message send by client to start new file dialog If returns true,...
static std::map< std::string, std::string > GetServerLocations()
Returns server locations as <std::string, std::string> Key is location name (with slash at the end) a...
Array of integers (32 bits per element).
Definition TArrayI.h:27
static TClass * Class()
virtual void SetFillColor(Color_t fcolor)
Set the fill area color.
Definition TAttFill.h:40
virtual void SetFillStyle(Style_t fstyle)
Set the fill area style.
Definition TAttFill.h:42
virtual void SetTextAlign(Short_t align=11)
Set the text alignment.
Definition TAttText.h:48
virtual void SetTextColor(Color_t tcolor=1)
Set the text color.
Definition TAttText.h:50
virtual void SetTextFont(Font_t tfont=62)
Set the text font.
Definition TAttText.h:52
virtual void SetTextSize(Float_t tsize=1)
Set the text size.
Definition TAttText.h:53
Class to manage histogram axis.
Definition TAxis.h:32
static TString Decode(const char *data)
Decode a base64 string date into a generic TString.
Definition TBase64.cxx:130
static TString Encode(const char *data)
Transform data into a null terminated base64 string.
Definition TBase64.cxx:106
virtual void SetY2(Double_t y2)
Definition TBox.h:65
virtual void SetX1(Double_t x1)
Definition TBox.h:62
virtual void SetX2(Double_t x2)
Definition TBox.h:63
virtual void SetY1(Double_t y1)
Definition TBox.h:64
static Int_t ExportToFile(const char *filename, const TObject *obj, const char *option=nullptr)
Convert object into JSON and store in text file Returns size of the produce file Used in TObject::Sav...
static TString ToJSON(const T *obj, Int_t compact=0, const char *member_name=nullptr)
Definition TBufferJSON.h:77
@ kNoSpaces
no new lines plus remove all spaces around "," and ":" symbols
Definition TBufferJSON.h:39
@ kMapAsObject
store std::map, std::unordered_map as JSON object
Definition TBufferJSON.h:41
@ kSameSuppression
zero suppression plus compress many similar values together
Definition TBufferJSON.h:45
A TButton object is a user interface object.
Definition TButton.h:18
static TClass * Class()
ABC describing GUI independent main window (with menubar, scrollbars and a drawing area).
Definition TCanvasImp.h:31
TCanvas * Canvas() const
Definition TCanvasImp.h:59
friend class TCanvas
Definition TCanvasImp.h:32
The Canvas class.
Definition TCanvas.h:23
UInt_t fCw
Width of the canvas along X (pixels)
Definition TCanvas.h:44
UInt_t GetWindowHeight() const
Definition TCanvas.h:166
void SetClickSelectedPad(TPad *pad)
Definition TCanvas.h:217
Int_t fWindowTopX
Top X position of window (in pixels)
Definition TCanvas.h:40
Int_t fEventX
! Last X mouse position in canvas
Definition TCanvas.h:47
TVirtualPadPainter * GetCanvasPainter()
Access and (probably) creation of pad painter.
Definition TCanvas.cxx:2632
UInt_t fWindowWidth
Width of window (including borders, etc.)
Definition TCanvas.h:42
Int_t fEventY
! Last Y mouse position in canvas
Definition TCanvas.h:48
UInt_t fWindowHeight
Height of window (including menubar, borders, etc.)
Definition TCanvas.h:43
TObject * fSelected
! Currently selected object
Definition TCanvas.h:50
UInt_t fCh
Height of the canvas along Y (pixels)
Definition TCanvas.h:45
UInt_t GetWindowWidth() const
Definition TCanvas.h:165
Int_t fWindowTopY
Top Y position of window (in pixels)
Definition TCanvas.h:41
void SetClickSelected(TObject *obj)
Definition TCanvas.h:215
virtual void Highlighted(TVirtualPad *pad, TObject *obj, Int_t x, Int_t y)
Emit Highlighted() signal.
Definition TCanvas.cxx:1618
static TClass * Class()
Int_t fEvent
! Type of current or last handled event
Definition TCanvas.h:46
@ kShowToolTips
Definition TCanvas.h:101
@ kShowToolBar
Definition TCanvas.h:96
@ kShowEventStatus
Definition TCanvas.h:93
@ kMenuBar
Definition TCanvas.h:95
@ kShowEditor
Definition TCanvas.h:97
TClass instances represent classes, structs and namespaces in the ROOT type system.
Definition TClass.h:84
Bool_t InheritsFrom(const char *cl) const override
Return kTRUE if this class inherits from a class with name "classname".
Definition TClass.cxx:4995
virtual Int_t GetSize() const
Return the capacity of the collection, i.e.
The color creation and management class.
Definition TColor.h:22
static const TArrayI & GetPalette()
Static function returning the current active palette.
Definition TColor.cxx:1534
static TClass * Class()
static Bool_t DefinedColors(Int_t set_always_on=0)
Static method returning kTRUE if some new colors have been defined after initialisation or since the ...
Definition TColor.cxx:1555
static TClass * Class()
virtual Int_t GetValue(const char *name, Int_t dflt) const
Returns the integer value for a resource.
Definition TEnv.cxx:511
TExec is a utility class that can be used to execute a C++ command when some event happens in a pad.
Definition TExec.h:26
virtual void Exec(const char *command="")
Execute the command referenced by this object.
Definition TExec.cxx:142
static TClass * Class()
1-Dim function class
Definition TF1.h:182
virtual Double_t GetXmax() const
Definition TF1.h:525
virtual TH1 * GetHistogram() const
Return a pointer to the histogram used to visualise the function Note that this histogram is managed ...
Definition TF1.cxx:1635
static TClass * Class()
@ kNotDraw
Definition TF1.h:297
virtual Bool_t IsValid() const
Return kTRUE if the function is valid.
Definition TF1.cxx:2932
virtual void Save(Double_t xmin, Double_t xmax, Double_t ymin, Double_t ymax, Double_t zmin, Double_t zmax)
Save values of function in array fSave.
Definition TF1.cxx:3212
TClass * IsA() const override
Definition TF1.h:694
virtual Double_t GetXmin() const
Definition TF1.h:521
Bool_t HasSave() const
Return true if function has data in fSave buffer.
Definition TF1.h:403
A 2-Dim function with parameters.
Definition TF2.h:29
void Save(Double_t xmin, Double_t xmax, Double_t ymin, Double_t ymax, Double_t zmin, Double_t zmax) override
Save values of function in array fSave.
Definition TF2.cxx:861
static TClass * Class()
TF3 defines a 3D Function with Parameters.
Definition TF3.h:28
Define a Frame.
Definition TFrame.h:19
static TClass * Class()
The axis painter class.
Definition TGaxis.h:26
static TClass * Class()
Graphics object made of three arrays X, Y and Z with the same number of points each.
Definition TGraph2D.h:41
static TClass * Class()
To draw a polar graph.
Definition TGraphPolar.h:23
static TClass * Class()
To draw polar axis.
static TClass * Class()
A TGraph is an object made of two arrays X and Y with npoints each.
Definition TGraph.h:41
static TClass * Class()
@ kNoStats
Don't draw stats box.
Definition TGraph.h:74
TList * GetListOfFunctions() const
Definition TGraph.h:125
virtual TH1F * GetHistogram() const
Returns a pointer to the histogram used to draw the axis Takes into account the two following cases.
Definition TGraph.cxx:1457
TH1 is the base class of all histogram classes in ROOT.
Definition TH1.h:109
TAxis * GetZaxis()
Definition TH1.h:573
static TClass * Class()
virtual Int_t GetDimension() const
Definition TH1.h:527
@ kNoTitle
Don't draw the histogram title.
Definition TH1.h:408
@ kIsZoomed
Bit set when zooming on Y axis.
Definition TH1.h:407
TAxis * GetXaxis()
Definition TH1.h:571
virtual void SetMaximum(Double_t maximum=-1111)
Definition TH1.h:652
TAxis * GetYaxis()
Definition TH1.h:572
virtual void SetMinimum(Double_t minimum=-1111)
Definition TH1.h:653
virtual Double_t GetEntries() const
Return the current number of entries.
Definition TH1.cxx:4574
TList * GetListOfFunctions() const
Definition TH1.h:488
virtual Int_t BufferEmpty(Int_t action=0)
Fill histogram with all entries in the buffer.
Definition TH1.cxx:1445
The Histogram stack class.
Definition THStack.h:40
static TClass * Class()
static char * ReadFileContent(const char *filename, Int_t &len)
Reads content of file from the disk.
Option_t * GetOption() const
void Reset()
A doubly linked list.
Definition TList.h:38
TObject * FindObject(const char *name) const override
Find an object in this list using its name.
Definition TList.cxx:708
void Add(TObject *obj) override
Definition TList.h:81
TObject * At(Int_t idx) const override
Returns the object at position idx. Returns 0 if idx is out of range.
Definition TList.cxx:487
void AddFirst(TObject *obj) override
Add object at the beginning of the list.
Definition TList.cxx:97
A TMultiGraph is a collection of TGraph (or derived) objects.
Definition TMultiGraph.h:34
TList * GetListOfGraphs() const
Definition TMultiGraph.h:70
void BuildPrimitives(Option_t *chopt="")
Definition TMultiGraph.h:54
static TClass * Class()
TList * GetListOfFunctions()
Return pointer to list of functions.
const char * GetName() const override
Returns name of object.
Definition TNamed.h:49
An array of TObjects.
Definition TObjArray.h:31
Mother of all ROOT objects.
Definition TObject.h:42
virtual const char * GetName() const
Returns name of object.
Definition TObject.cxx:461
R__ALWAYS_INLINE Bool_t TestBit(UInt_t f) const
Definition TObject.h:204
virtual const char * ClassName() const
Returns name of class to which the object belongs.
Definition TObject.cxx:226
void SetBit(UInt_t f, Bool_t set)
Set or unset the user status bits as specified in f.
Definition TObject.cxx:886
virtual Bool_t InheritsFrom(const char *classname) const
Returns kTRUE if object inherits from class "classname".
Definition TObject.cxx:548
virtual const char * GetTitle() const
Returns title of object.
Definition TObject.cxx:506
virtual TClass * IsA() const
Definition TObject.h:248
virtual void Paint(Option_t *option="")
This method must be overridden if a class wants to paint itself.
Definition TObject.cxx:630
The most important graphics class in the ROOT system.
Definition TPad.h:28
static TClass * Class()
void Modified(Bool_t flag=true) override
Mark pad modified Will be repainted when TCanvas::Update() will be called next time.
Definition TPad.cxx:7356
void Print(const char *filename="") const override
This method is equivalent to SaveAs("filename"). See TPad::SaveAs for details.
Definition TPad.cxx:4803
The histogram statistics painter class.
Definition TPaveStats.h:18
virtual void SetStatFormat(const char *format="6.4g")
Change (i.e. set) the format for printing statistics.
void SetOptStat(Int_t stat=1)
Set the stat option.
virtual void SetFitFormat(const char *format="5.4g")
Change (i.e. set) the format for printing fit parameters in statistics box.
void SetParent(TObject *obj) override
Definition TPaveStats.h:53
void SetOptFit(Int_t fit=1)
Set the fit option.
static TClass * Class()
A Pave (see TPave) with text, lines or/and boxes inside.
Definition TPaveText.h:21
virtual TText * AddText(Double_t x1, Double_t y1, const char *label)
Add a new Text line to this pavetext at given coordinates.
static TClass * Class()
void Clear(Option_t *option="") override
Clear all lines in this pavetext.
virtual TText * GetLine(Int_t number) const
Get Pointer to line number in this pavetext.
A TBox with a bordersize and a shadow option.
Definition TPave.h:19
virtual void SetName(const char *name="")
Definition TPave.h:81
virtual void SetBorderSize(Int_t bordersize=4)
Sets the border size of the TPave box and shadow.
Definition TPave.h:79
static TClass * Class()
Option_t * GetOption() const override
Definition TPave.h:59
A TScatter is able to draw four variables scatter plot on a single plot.
Definition TScatter.h:32
static TClass * Class()
Basic string class.
Definition TString.h:137
Ssiz_t Length() const
Definition TString.h:426
const char * Data() const
Definition TString.h:385
@ kBoth
Definition TString.h:283
@ kIgnoreCase
Definition TString.h:284
Ssiz_t Last(char c) const
Find last occurrence of a character c.
Definition TString.cxx:939
void ToUpper()
Change string to upper case.
Definition TString.cxx:1203
UInt_t Hash(ECaseCompare cmp=kExact) const
Return hash value.
Definition TString.cxx:685
TString & Append(const char *cs)
Definition TString.h:582
static TString Format(const char *fmt,...)
Static method which formats a string using a printf style format descriptor and return a TString.
Definition TString.cxx:2460
Bool_t Contains(const char *pat, ECaseCompare cmp=kExact) const
Definition TString.h:642
Int_t GetOptLogy() const
Definition TStyle.h:250
Int_t GetOptStat() const
Definition TStyle.h:247
Color_t GetStatTextColor() const
Definition TStyle.h:260
Int_t GetOptTitle() const
Definition TStyle.h:248
Int_t GetPadTickX() const
Definition TStyle.h:219
Float_t GetStatFontSize() const
Definition TStyle.h:263
Float_t GetStatX() const
Definition TStyle.h:266
Float_t GetPadRightMargin() const
Definition TStyle.h:216
Style_t GetTitleFont(Option_t *axis="X") const
Return title font.
Definition TStyle.cxx:1217
Float_t GetStatY() const
Definition TStyle.h:267
Color_t GetTitleFillColor() const
Definition TStyle.h:273
Style_t GetTitleStyle() const
Definition TStyle.h:275
Bool_t GetPadGridY() const
Definition TStyle.h:218
Color_t GetStatColor() const
Definition TStyle.h:259
Float_t GetPadLeftMargin() const
Definition TStyle.h:215
Bool_t GetPadGridX() const
Definition TStyle.h:217
Float_t GetStatH() const
Definition TStyle.h:269
static TClass * Class()
Int_t GetPadTickY() const
Definition TStyle.h:220
Width_t GetTitleBorderSize() const
Definition TStyle.h:277
Color_t GetCanvasColor() const
Definition TStyle.h:190
Float_t GetPadBottomMargin() const
Definition TStyle.h:213
Width_t GetStatBorderSize() const
Definition TStyle.h:261
Color_t GetTitleTextColor() const
Definition TStyle.h:274
Int_t GetOptLogx() const
Definition TStyle.h:249
Style_t GetStatStyle() const
Definition TStyle.h:264
Float_t GetStatW() const
Definition TStyle.h:268
const char * GetFitFormat() const
Definition TStyle.h:201
Int_t GetCanvasBorderMode() const
Definition TStyle.h:192
const char * GetStatFormat() const
Definition TStyle.h:265
Width_t GetCanvasBorderSize() const
Definition TStyle.h:191
Int_t GetOptFit() const
Definition TStyle.h:246
Style_t GetStatFont() const
Definition TStyle.h:262
Int_t GetOptLogz() const
Definition TStyle.h:251
Float_t GetTitleFontSize() const
Definition TStyle.h:276
Float_t GetPadTopMargin() const
Definition TStyle.h:214
virtual Bool_t ExpandPathName(TString &path)
Expand a pathname getting rid of special shell characters like ~.
Definition TSystem.cxx:1289
virtual Bool_t AccessPathName(const char *path, EAccessMode mode=kFileExists)
Returns FALSE if one can access a file using the specified access mode.
Definition TSystem.cxx:1311
virtual void Sleep(UInt_t milliSec)
Sleep milliSec milli seconds.
Definition TSystem.cxx:439
virtual Bool_t ProcessEvents()
Process pending events (GUI, timers, sockets).
Definition TSystem.cxx:418
Base class for several text objects.
Definition TText.h:22
static Long_t SelfId()
Static method returning the id for the current thread.
Definition TThread.cxx:554
Handles synchronous and a-synchronous timer events.
Definition TTimer.h:51
virtual void TurnOn()
Add the timer to the system timer list.
Definition TTimer.cxx:246
void SetTime(Long_t milliSec)
Definition TTimer.h:91
See TView3D.
Definition TView.h:25
static TView * CreateView(Int_t system=1, const Double_t *rmin=nullptr, const Double_t *rmax=nullptr)
Create a concrete default 3-d view via the plug-in manager.
Definition TView.cxx:26
virtual void SetAutoRange(Bool_t autorange=kTRUE)=0
TVirtualPS is an abstract interface to Postscript, PDF, SVG.
Definition TVirtualPS.h:32
To make it possible to use GL for 2D graphic in a TPad/TCanvas.
small helper class to store/restore gPad context in TPad methods
Definition TVirtualPad.h:61
TVirtualPad is an abstract base class for the Pad and Canvas classes.
Definition TVirtualPad.h:51
Semi-Abstract base class defining a generic interface to the underlying, low level,...
Definition TVirtualX.h:47
void SetSlow(Bool_t slow=kTRUE)
TWebCanvasTimer(TWebCanvas &canv)
Bool_t IsSlow() const
void Timeout() override
used to send control messages to clients
TWebCanvas & fCanv
Basic TCanvasImp ABI implementation for Web-based Graphics Provides painting of main ROOT classes in ...
Definition TWebCanvas.h:35
TVirtualPadPainter * CreatePadPainter() override
Creates web-based pad painter.
void ForceUpdate() override
Increment canvas version and force sending data to client - do not wait for reply.
static TCanvas * CreateWebCanvas(const char *name, const char *title, UInt_t width=1200, UInt_t height=800)
Create TCanvas and assign TWebCanvas implementation to it Canvas is not displayed automatically,...
static void AddCustomClass(const std::string &clname, bool with_derived=false)
Assign custom class.
static TString CreatePadJSON(TPad *pad, Int_t json_compression=0, Bool_t batchmode=kFALSE)
Create JSON painting output for given pad Produce JSON can be used for offline drawing with JSROOT.
void SetCanvasSize(UInt_t w, UInt_t h) override
Set canvas size of web canvas.
UInt_t fColorsHash
! last hash of colors/palette
Definition TWebCanvas.h:107
Int_t fTF1UseSave
! use save buffer for TF1/TF2, 0:off, 1:prefer, 2:force
Definition TWebCanvas.h:108
void ShowCmd(const std::string &arg, Bool_t show)
Function used to send command to browser to toggle menu, toolbar, editors, ...
Long64_t fColorsVersion
! current colors/palette version, checked every time when new snapshot created
Definition TWebCanvas.h:106
virtual Bool_t IsReadOnly() const
Definition TWebCanvas.h:197
std::shared_ptr< ROOT::RWebWindow > fWindow
Definition TWebCanvas.h:88
virtual Bool_t IsJSSupportedClass(TObject *obj, Bool_t many_primitives=kFALSE)
Returns kTRUE when object is fully supported on JSROOT side In ROOT7 Paint function will just return ...
void AddCtrlMsg(unsigned connid, const std::string &key, const std::string &value)
Add control message for specified connection Same control message can be overwritten many time before...
static void SetCustomScripts(const std::string &src)
Configures custom script for canvas.
ObjectSelectSignal_t fObjSelectSignal
! signal emitted when new object selected in the pad
Definition TWebCanvas.h:116
PadClickedSignal_t fPadClickedSignal
! signal emitted when simple mouse click performed on the pad
Definition TWebCanvas.h:114
void SetLongerPolling(Bool_t on)
Definition TWebCanvas.h:253
UInt_t fStyleHash
! last hash of gStyle
Definition TWebCanvas.h:105
virtual Bool_t CanCreateObject(const std::string &)
Definition TWebCanvas.h:171
void ShowWebWindow(const ROOT::RWebDisplayArgs &user_args="")
Show canvas in specified place.
Int_t fPrimitivesMerge
! number of PS primitives, which will be merged together
Definition TWebCanvas.h:98
void Show() override
Show canvas in browser window.
Bool_t WaitWhenCanvasPainted(Long64_t ver)
Wait when specified version of canvas was painted and confirmed by browser.
static UInt_t gBatchImageMode
! configured batch size
Definition TWebCanvas.h:123
static std::string gCustomScripts
! custom JavaScript code or URL on JavaScript files to load before start drawing
Definition TWebCanvas.h:143
Bool_t IsAsyncMode() const
Definition TWebCanvas.h:257
Long64_t fLastDrawVersion
! last draw version
Definition TWebCanvas.h:92
UInt_t CalculateColorsHash()
Calculate hash function for all colors and palette.
void SetWindowGeometry(const std::vector< int > &arr)
Set window geometry as array with coordinates and dimensions.
Bool_t HasStatusBar() const override
Returns kTRUE if web canvas has status bar.
static std::vector< std::string > gCustomClasses
! list of custom classes, which can be delivered as is to client
Definition TWebCanvas.h:144
void CreateWebWindow()
Create web window for the canvas.
void Close() override
Close web canvas - not implemented.
static bool ProduceImages(std::vector< TPad * > pads, const char *filename, Int_t width=0, Int_t height=0)
Create images for several pads using batch (headless) capability of Chrome or Firefox browsers Suppor...
Bool_t HasMenuBar() const override
Returns kTRUE if web canvas has menu bar.
Int_t InitWindow() override
Initialize window for the web canvas At this place canvas is not yet register to the list of canvases...
void CheckPadModified(TPad *pad)
Returns true if any pad in the canvas were modified Reset modified flags, increment canvas version (i...
void RaiseWindow() override
Raise browser window.
static bool ProduceImage(TPad *pad, const char *filename, Int_t width=0, Int_t height=0)
Create image using batch (headless) capability of Chrome or Firefox browsers Supported png,...
void ActivateInEditor(TPad *pad, TObject *obj)
Activate object in editor in web browser.
std::vector< WebConn > fWebConn
! connections
Definition TWebCanvas.h:83
PadSignal_t fActivePadChangedSignal
! signal emitted when active pad changed in the canvas
Definition TWebCanvas.h:113
Bool_t GetLongerPolling() const
Definition TWebCanvas.h:254
UInt_t fClientBits
! latest status bits from client like editor visible or not
Definition TWebCanvas.h:93
std::function< void(TPadWebSnapshot *)> PadPaintingReady_t
Function called when pad painting produced.
Definition TWebCanvas.h:55
Int_t fPaletteDelivery
! colors palette delivery 0:never, 1:once, 2:always, 3:per subpad
Definition TWebCanvas.h:97
Bool_t fProcessingData
! flag used to prevent blocking methods when process data is invoked
Definition TWebCanvas.h:102
Bool_t HasToolTips() const override
Returns kTRUE if tooltips are activated in web canvas.
std::vector< TPad * > fAllPads
! list of all pads recognized during streaming
Definition TWebCanvas.h:94
friend class TWebCanvasTimer
Definition TWebCanvas.h:37
TWebCanvasTimer * fTimer
! timer to submit control messages
Definition TWebCanvas.h:84
static std::vector< std::string > gBatchJsons
! converted jsons batch job
Definition TWebCanvas.h:126
Long64_t fCanvVersion
! actual canvas version, changed with every new Modified() call
Definition TWebCanvas.h:91
std::vector< int > fWindowGeometry
! last received window geometry
Definition TWebCanvas.h:109
TPad * ProcessObjectOptions(TWebObjectOptions &item, TPad *pad, int idcnt=1)
Process data for single primitive Returns object pad if object was modified.
std::map< TObject *, bool > fUsedObjs
! map of used objects during streaming
Definition TWebCanvas.h:95
void AddColorsPalette(TPadWebSnapshot &master)
Add special canvas objects with list of colors and color palette.
Long64_t fStyleVersion
! current gStyle object version, checked every time when new snapshot created
Definition TWebCanvas.h:104
static std::string gBatchMultiPdf
! name of current multi-page pdf file
Definition TWebCanvas.h:124
static void BatchImageMode(UInt_t n=100)
Configure batch image mode for web graphics.
std::vector< std::unique_ptr< ROOT::RWebDisplayHandle > > fHelpHandles
! array of handles for help widgets
Definition TWebCanvas.h:118
void AddSendQueue(unsigned connid, const std::string &msg)
Add message to send queue for specified connection If connid == 0, message will be add to all connect...
void SetWindowPosition(Int_t x, Int_t y) override
Set window position of web canvas.
UpdatedSignal_t fUpdatedSignal
! signal emitted when canvas updated or state is changed
Definition TWebCanvas.h:112
Int_t fJsonComp
! compression factor for messages send to the client
Definition TWebCanvas.h:99
static std::vector< int > gBatchWidths
! batch job widths
Definition TWebCanvas.h:127
~TWebCanvas() override
Destructor.
static std::string ProcessCustomScripts(bool batch)
For batch mode special handling of scripts are required Headless browser not able to load modules fro...
Bool_t fReadOnly
!< configured display
Definition TWebCanvas.h:90
std::map< TPad *, PadStatus > fPadsStatus
! map of pads in canvas and their status flags
Definition TWebCanvas.h:86
static Font_t AddFont(const char *name, const char *ttffile, Int_t precision=2)
Add font to static list of fonts supported by the canvas Name specifies name of the font,...
void AddCustomFonts(TPadWebSnapshot &master)
Add special canvas objects with custom fonts.
Bool_t CheckDataToSend(unsigned connid=0)
Check if any data should be send to client If connid != 0, only selected connection will be checked.
Bool_t PerformUpdate(Bool_t async) override
if canvas or any subpad was modified, scan all primitives in the TCanvas and subpads and convert them...
void AssignStatusBits(UInt_t bits)
Assign clients bits.
virtual Bool_t ProcessData(unsigned connid, const std::string &arg)
Handle data from web browser Returns kFALSE if message was not processed.
void ProcessLinesForObject(TObject *obj, const std::string &lines)
Execute one or several methods for selected object String can be separated by ";;" to let execute sev...
TWebCanvas(TCanvas *c, const char *name, Int_t x, Int_t y, UInt_t width, UInt_t height, Bool_t readonly=kTRUE)
Constructor.
static std::vector< int > gBatchHeights
! batch job heights
Definition TWebCanvas.h:128
static Int_t StoreCanvasJSON(TCanvas *c, const char *filename, const char *option="")
Create JSON painting output for given canvas and store into the file See TBufferJSON::ExportToFile() ...
static const std::string & GetCustomScripts()
Returns configured custom script.
void Iconify() override
Iconify browser window.
void SetWindowTitle(const char *newTitle) override
Set window title of web canvas.
UInt_t GetWindowGeometry(Int_t &x, Int_t &y, UInt_t &w, UInt_t &h) override
Returns window geometry including borders and menus.
static std::vector< std::string > gBatchFiles
! file names for batch job
Definition TWebCanvas.h:125
static TCanvasImp * NewCanvas(TCanvas *c, const char *name, Int_t x, Int_t y, UInt_t width, UInt_t height)
Static method to create TWebCanvas instance Used by plugin manager.
static TString CreateCanvasJSON(TCanvas *c, Int_t json_compression=0, Bool_t batchmode=kFALSE)
Create JSON painting output for given canvas Produce JSON can be used for offline drawing with JSROOT...
Bool_t HasEditor() const override
Returns kTRUE if web canvas has graphical editor.
Int_t fStyleDelivery
! gStyle delivery to clients: 0:never, 1:once, 2:always
Definition TWebCanvas.h:96
PadClickedSignal_t fPadDblClickedSignal
! signal emitted when simple mouse click performed on the pad
Definition TWebCanvas.h:115
void ProcessExecs(TPad *pad, TExec *extra=nullptr)
Process TExec objects in the pad.
static bool FlushBatchImages()
Flush batch images.
void CreatePadSnapshot(TPadWebSnapshot &paddata, TPad *pad, Long64_t version, PadPaintingReady_t func)
Create snapshot for pad and all primitives Callback function is used to create JSON in the middle of ...
void CreateObjectSnapshot(TPadWebSnapshot &master, TPad *pad, TObject *obj, const char *opt, TWebPainting *masterps=nullptr)
Creates representation of the object for painting in web browser.
Bool_t CheckCanvasModified(bool force_modified=false)
Check if any pad on the canvas was modified If yes, increment version of correspondent pad Returns tr...
virtual Bool_t DecodePadOptions(const std::string &, bool process_execs=false)
Decode all pad options, which includes ranges plus objects options.
Int_t GetPaletteDelivery() const
Definition TWebCanvas.h:248
void SetWindowSize(UInt_t w, UInt_t h) override
Set window size of web canvas.
static bool IsCustomClass(const TClass *cl)
Checks if class belongs to custom.
TObject * FindPrimitive(const std::string &id, int idcnt=1, TPad *pad=nullptr, TObjLink **objlnk=nullptr, TPad **objpad=nullptr)
Search of object with given id in list of primitives One could specify pad where search could be star...
Bool_t fFixedSize
! is canvas size fixed
Definition TWebCanvas.h:110
Int_t GetStyleDelivery() const
Definition TWebCanvas.h:245
Class used to transport drawing options from the client.
Implement TVirtualPadPainter which abstracts painting operations.
Object used to store paint operations and deliver them to JSROOT.
@ kStyle
gStyle object
@ kObject
object itself
@ kSVG
list of SVG primitives
@ kSubPad
subpad
@ kFont
custom web font
@ kColors
list of ROOT colors + palette
Double_t y[n]
Definition legend1.C:17
Double_t x[n]
Definition legend1.C:17
const Int_t n
Definition legend1.C:16
TGraphErrors * gr
Definition legend1.C:25
TH1F * h1
Definition legend1.C:5
TF1 * f1
Definition legend1.C:11
LongDouble_t Power(LongDouble_t x, LongDouble_t y)
Returns x raised to the power y.
Definition TMath.h:734
TString fName
TString fFormat
WebFont_t()=default
TString fData
WebFont_t(Int_t indx, const TString &name, const TString &fmt, const TString &data)