Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
rootqt5.cpp
Go to the documentation of this file.
1// Author: Sergey Linev <S.Linev@gsi.de>
2// Date: 2017-06-29
3// Warning: This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback is welcome!
4
5/*************************************************************************
6 * Copyright (C) 1995-2023, Rene Brun and Fons Rademakers. *
7 * All rights reserved. *
8 * *
9 * For the licensing terms see $ROOTSYS/LICENSE. *
10 * For the list of contributors see $ROOTSYS/README/CREDITS. *
11 *************************************************************************/
12
13#include <QApplication>
14#include <QWebEngineView>
15#include <qtwebengineglobal.h>
16#include <QWebEngineDownloadItem>
17
18#include <QThread>
19#include <QWebEngineSettings>
20#include <QWebEngineProfile>
21#include <QtGlobal>
22#include <QWebEngineUrlScheme>
23
24#include "TROOT.h"
25#include "TApplication.h"
26#include "TTimer.h"
27#include "TEnv.h"
28#include "TThread.h"
29#include "THttpServer.h"
30#include "TSystem.h"
31#include "TDirectory.h"
32
33#include "rootwebview.h"
34#include "rootwebpage.h"
36
37#include <memory>
38
41#include <ROOT/RLogger.hxx>
42
43QWebEngineUrlScheme gRootScheme("rootscheme");
44QApplication *gOwnApplication = nullptr;
46bool gProcEvents = false, gDoingShutdown = false;
47
49{
51 delete gOwnApplication;
52 gOwnApplication = nullptr;
53 }
54}
55
56class DummyObject : public TObject {
57public:
58 ~DummyObject() override
59 {
60 gDoingShutdown = true;
62 }
63
64};
65
66/** \class TQt5Timer
67\ingroup qt5webdisplay
68*/
69
70class TQt5Timer : public TTimer {
71public:
72 TQt5Timer(Long_t milliSec, Bool_t mode) : TTimer(milliSec, mode) {}
73
74 /// timeout handler
75 /// used to process all qt5 events in main ROOT thread
76 void Timeout() override
77 {
78 gProcEvents = true;
79 QApplication::sendPostedEvents();
80 QApplication::processEvents();
81 gProcEvents = false;
82
83 }
84};
85
86namespace ROOT {
87
88/** \class RQt5WebDisplayHandle
89\ingroup qt5webdisplay
90*/
91
93protected:
94
95 RootWebView *fView{nullptr}; ///< pointer on widget, need to release when handle is destroyed
96
97 class Qt5Creator : public Creator {
98 int qargc{1}; ///< arg counter
99 char *qargv[2]; ///< arg values
100 std::unique_ptr<TQt5Timer> fTimer; ///< timer to process ROOT events
101 std::unique_ptr<RootUrlSchemeHandler> fHandler; ///< specialized handler
102 public:
103
104 Qt5Creator() = default;
105
106 ~Qt5Creator() override
107 {
108 /** Code executed during exit and sometime crashes.
109 * Disable it, while not clear if defaultProfile can be still used - seems to be not */
110 // if (fHandler)
111 // QWebEngineProfile::defaultProfile()->removeUrlSchemeHandler(fHandler.get());
112
113 // do not try to destroy objects during exit
114 fHandler.release();
115 fTimer.release();
116 }
117
118 std::unique_ptr<RWebDisplayHandle> Display(const RWebDisplayArgs &args) override
119 {
120 if (!gOwnApplication && !QApplication::instance()) {
121
122 if (!gApplication) {
123 R__LOG_ERROR(QtWebDisplayLog()) << "Not found gApplication to create QApplication";
124 return nullptr;
125 }
126
127 // initialize web engine only before creating QApplication
128 QtWebEngine::initialize();
129
130 qargv[0] = gApplication->Argv(0);
131 qargv[1] = nullptr;
132
133 gOwnApplication = new QApplication(qargc, qargv);
134
135 // this is workaround to detect ROOT shutdown
136 TDirectory::TContext ctxt; // preserve gDirectory
137 auto dir = new TDirectory("dummy_qt5web_dir", "cleanup instance for qt5web");
138 dir->GetList()->Add(new DummyObject());
139 gROOT->GetListOfClosedObjects()->Add(dir);
140 }
141
142 // create timer to process Qt events from inside ROOT process events
143 // very much improve performance, even when Qt event loop runs by QApplication normally
144 if (!fTimer && !args.IsHeadless()) {
145 Int_t interval = gEnv->GetValue("WebGui.Qt5Timer", 1);
146 if (interval > 0) {
147 fTimer = std::make_unique<TQt5Timer>(interval, kTRUE);
148 fTimer->TurnOn();
149 }
150 }
151
152 QString fullurl = QString(args.GetFullUrl().c_str());
153
154 // if no server provided - normal HTTP will be allowed to use
155 if (args.GetHttpServer()) {
156 if (!fHandler) {
157 fHandler = std::make_unique<RootUrlSchemeHandler>();
158 QWebEngineProfile::defaultProfile()->installUrlSchemeHandler("rootscheme", fHandler.get());
159 QWebEngineProfile::defaultProfile()->connect(QWebEngineProfile::defaultProfile(), &QWebEngineProfile::downloadRequested,
160 [](QWebEngineDownloadItem *item) { item->accept(); });
161 }
162
163 fullurl = fHandler->MakeFullUrl(args.GetHttpServer(), fullurl);
164 }
165
166 QWidget *qparent = static_cast<QWidget *>(args.GetDriverData());
167
168 auto handle = std::make_unique<RQt5WebDisplayHandle>(fullurl.toLatin1().constData());
169
170 RootWebView *view = new RootWebView(qparent, args.GetWidth(), args.GetHeight(), args.GetX(), args.GetY());
171
172 if (!args.IsHeadless()) {
173 if (!qparent) handle->fView = view;
174 view->load(QUrl(fullurl));
175 view->show();
176 } else {
177
178 int tmout_sec = 30, expired = tmout_sec * 100;
179 bool load_finished = false, did_try = false, get_content = false, is_error = false;
180 std::string content, pdffile;
181
182 if (!args.GetExtraArgs().empty() && (args.GetExtraArgs().find("--print-to-pdf=")==0))
183 pdffile = args.GetExtraArgs().substr(15);
184
185 QObject::connect(view, &RootWebView::loadFinished, [&load_finished, &is_error](bool is_ok) {
186 load_finished = true; is_error = !is_ok;
187 });
188
189 #if QT_VERSION >= 0x050900
190 if (!pdffile.empty())
191 QObject::connect(view->page(), &RootWebPage::pdfPrintingFinished, [&expired, &is_error](const QString &, bool is_ok) {
192 expired = 0; is_error = !is_ok;
193 });
194 #endif
195
196 const std::string &page_content = args.GetPageContent();
197 if (page_content.empty())
198 view->load(QUrl(fullurl));
199 else
200 view->setHtml(QString::fromUtf8(page_content.data(), page_content.size()), QUrl("file:///batch_page.html"));
201
202 // loop here until content is configured
203 while ((--expired > 0) && !get_content && !is_error) {
204
205 if (gSystem->ProcessEvents()) break; // interrupted, has to return
206
207 gProcEvents = true;
208 QApplication::sendPostedEvents();
209 QApplication::processEvents();
210 gProcEvents = false;
211
212 if (load_finished && !did_try) {
213 did_try = true;
214
215 if (pdffile.empty()) {
216 view->page()->toHtml([&get_content, &content](const QString& res) {
217 get_content = true;
218 content = res.toLatin1().constData();
219 });
220 } else {
221 view->page()->printToPdf(QString::fromUtf8(pdffile.data(), pdffile.size()));
222 #if QT_VERSION < 0x050900
223 expired = 5; // no signal will be produced, just wait short time and break loop
224 #endif
225 }
226 }
227
228 gSystem->Sleep(10); // only 10 ms sleep
229 }
230
231 if(get_content)
232 handle->SetContent(content);
233
234 // delete view and process events
235 delete view;
236
237 for (expired=0;expired<100;++expired) {
238 gProcEvents = true;
239 QApplication::sendPostedEvents();
240 QApplication::processEvents();
241 gProcEvents = false;
242 }
243
244 }
245
246 return handle;
247 }
248
249 };
250
251public:
252 RQt5WebDisplayHandle(const std::string &url) : RWebDisplayHandle(url) { gQt5HandleCounts++; }
253
255 {
256 // now view can be safely destroyed
257 if (fView) {
258 delete fView;
259 fView = nullptr;
260 }
261
263
265 }
266
267 bool Resize(int width, int height) override
268 {
269 if (!fView)
270 return false;
271 fView->resize(QSize(width, height));
272 return true;
273 }
274
275 static void AddCreator()
276 {
277 auto &entry = FindCreator("qt5");
278 if (!entry)
279 GetMap().emplace("qt5", std::make_unique<Qt5Creator>());
280 }
281
282};
283
287
288 gRootScheme.setSyntax(QWebEngineUrlScheme::Syntax::HostAndPort);
289 gRootScheme.setDefaultPort(2345);
290 gRootScheme.setFlags(QWebEngineUrlScheme::SecureScheme);
291 QWebEngineUrlScheme::registerScheme(gRootScheme);
292
293 }
295
296} // namespace ROOT
#define R__LOG_ERROR(...)
Definition RLogger.hxx:362
long Long_t
Definition RtypesCore.h:54
constexpr Bool_t kTRUE
Definition RtypesCore.h:93
R__EXTERN TApplication * gApplication
R__EXTERN TEnv * gEnv
Definition TEnv.h:170
Option_t Option_t TPoint TPoint const char mode
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 height
#define gROOT
Definition TROOT.h:406
R__EXTERN TSystem * gSystem
Definition TSystem.h:561
~DummyObject() override
Definition rootqt5.cpp:58
std::unique_ptr< RWebDisplayHandle > Display(const RWebDisplayArgs &args) override
Definition rootqt5.cpp:118
std::unique_ptr< RootUrlSchemeHandler > fHandler
specialized handler
Definition rootqt5.cpp:101
std::unique_ptr< TQt5Timer > fTimer
timer to process ROOT events
Definition rootqt5.cpp:100
RootWebView * fView
pointer on widget, need to release when handle is destroyed
Definition rootqt5.cpp:95
bool Resize(int width, int height) override
resize web window - if possible
Definition rootqt5.cpp:267
RQt5WebDisplayHandle(const std::string &url)
Definition rootqt5.cpp:252
~RQt5WebDisplayHandle() override
Definition rootqt5.cpp:254
Holds different arguments for starting browser with RWebDisplayHandle::Display() method.
THttpServer * GetHttpServer() const
returns http server instance, used for window display
int GetWidth() const
returns preferable web window width
int GetY() const
set preferable web window y position
std::string GetFullUrl() const
returns window url with append options
int GetHeight() const
returns preferable web window height
bool IsHeadless() const
returns headless mode
void * GetDriverData() const
[internal] returns web-driver data, used to start window
const std::string & GetExtraArgs() const
get extra command line arguments for starting web browser command
int GetX() const
set preferable web window x position
const std::string & GetPageContent() const
returns window url
Handle of created web-based display Depending from type of web display, holds handle of started brows...
static std::map< std::string, std::unique_ptr< Creator > > & GetMap()
Static holder of registered creators of web displays.
static std::unique_ptr< Creator > & FindCreator(const std::string &name, const std::string &libname="")
Search for specific browser creator If not found, try to add one.
char ** Argv() const
TDirectory::TContext keeps track and restore the current directory.
Definition TDirectory.h:89
Describe directory structure in memory.
Definition TDirectory.h:45
virtual Int_t GetValue(const char *name, Int_t dflt) const
Returns the integer value for a resource.
Definition TEnv.cxx:491
Mother of all ROOT objects.
Definition TObject.h:41
TQt5Timer(Long_t milliSec, Bool_t mode)
Definition rootqt5.cpp:72
void Timeout() override
timeout handler used to process all qt5 events in main ROOT thread
Definition rootqt5.cpp:76
virtual void Sleep(UInt_t milliSec)
Sleep milliSec milli seconds.
Definition TSystem.cxx:437
virtual Bool_t ProcessEvents()
Process pending events (GUI, timers, sockets).
Definition TSystem.cxx:416
Handles synchronous and a-synchronous timer events.
Definition TTimer.h:51
tbb::task_arena is an alias of tbb::interface7::task_arena, which doesn't allow to forward declare tb...
struct ROOT::RQt5CreatorReg newRQt5CreatorReg
void TestQt5Cleanup()
Definition rootqt5.cpp:48
QApplication * gOwnApplication
Definition rootqt5.cpp:44
bool gProcEvents
Definition rootqt5.cpp:46
int gQt5HandleCounts
Definition rootqt5.cpp:45
bool gDoingShutdown
Definition rootqt5.cpp:46
QWebEngineUrlScheme gRootScheme("rootscheme")
ROOT::Experimental::RLogChannel & QtWebDisplayLog()