Embedding ROOT Canvas in Qt

Dear ROOTers (most probably Betrand?),

I am right now converting all our Qt-ROOT applications to the nicer and cleaner way of just embedding the TCanvas in a QWidget.
I had a lot of successes with the example from [url]Root and Qt5 thanks a lot for this!

However, I find some problems left:

  • Not working on OS X with qt-mac (i.e. Cocoa). Is there a solution? From gVirtualX, I just get back a window-ID of “0” which is not a valid drawable.
  • OpenGL not working, i.e. “OpenGL.CanvasPreferGL: 1” fails.
  • No transparency support (not so crucial).

A solution especially for the Mac-issue would be nice since I would not like to cut off our Mac userbase :wink:.

Cheers and thanks,
Oliver

Have you tired with the X11 version of ROOT on mac ?

Hi Olivier,

I don’t have access to a Mac, so I cannot test right now…

You could try to load the TGLManager plugin:

TApplication rootapp("Simple Qt ROOT Application", &argc, argv); if (!gGLManager) { TString x = "win32"; if (gVirtualX->InheritsFrom("TGX11")) x = "x11"; else if (gVirtualX->InheritsFrom("TGCocoa")) x = "osx"; TPluginHandler *ph = gROOT->GetPluginManager()->FindHandler("TGLManager", x); if (ph && ph->LoadPlugin() != -1) ph->ExecPlugin(0); } gStyle->SetCanvasPreferGL(true);
Cheers, Bertrand.

I tried just now, it does not solve the problem, since Qt is running with Cocoa backend and embedding via X11 does not work there, as expected. I now get the following errors, very similar to what I had before:

Asking virtualx now at wid: -1, qtwid: 140232247154768
Building canvas now at wid: 0, qtwid: 140232247154768
Trying canvas with wid = 0, qtwinid = 140232247154768
Error in <RootX11ErrorHandler>: BadWindow (invalid Window parameter) (XID: 1564940368, XREQ: 2)
Error in <RootX11ErrorHandler>: BadWindow (invalid Window parameter) (XID: 1564940368, XREQ: 61)
Error in <RootX11ErrorHandler>: BadDrawable (invalid Pixmap or Window parameter) (XID: 1564940368, XREQ: 14)
Error in <RootX11ErrorHandler>: BadWindow (invalid Window parameter) (XID: 1564940368, XREQ: 40)

The first output lines are debug output from my program: After asking gVirtualX providing the wid “140232247154768” as gotten from “(ULong_t)winId()”, gVirtualX provides me with a return value of “0” with which I construct the TCanvas.
The gVirtualX which is now then not a TGCocoa but X11 based since I use root with X11 then complains.

I’ll try TVirtualGL in a minute.

P.S. Looking at the TGCocoa implementation of TVirtualX::AddWindow():

Int_t TGCocoa::AddWindow(ULong_t /*qwid*/, UInt_t /*w*/, UInt_t /*h*/) { //Should register a window created by Qt as a ROOT window, //but since Qt-ROOT does not work on Mac and will never work, //especially with version 4.8 - this implementation will always //be empty. return 0; }
So I’m afraid there is no solution for the time being…

Cheers, Bertrand.

Dear Bertrand,

I tried your code snippet inside a QWidget (i.e. using the QRootCanvas example which you provided on the forums here), followed by:

wid = gGLManager->InitGLWindow((ULong_t)winId());
fCanvas = new TCanvas("Root Canvas", width(), height(), wid);

That’s on Linux.
There I get:

Building canvas now at wid: 1, qtwid: 153092319
Warning in <TCanvas::ResizePad>: Root Canvas width changed from 0 to 10

and the QWidget stays blank.

I am unsure whether I need to change the interfaces and implementation to a QGLWidget, do you have any experience with such a solution?
If not, I may try to give it a go and try to implement a QGLWidget for embedding a ROOT GL canvas into Qt.

My guess / expectation is that using GL, it might be possible to solve the problems on OS X, since embedding a GL-Widget should also work fine inside Cocoa. If this works in some way, it could be a nice extension to
root.cern.ch/how/how-embed-tcan … plications which then actually is non-OS X only for now.

Since we still have a few people who use ROOT on their macs, it would be nice to offer a native solution to them (and although Qt-ROOT seemed to work, I think we both agree that it is quite a “hack” and not a real solution).

Thanks for your reply and cheers,
Oliver

Dear Oliver,

No, in fact I have not much experience with Qt in general. The TCanvas embedding mechanism has been developed for a generic use in any foreign application/framework, as soon as one can get a window ID (from the OS/Window Manager). But maybe the TGCocoa author could give more details…

Cheers, Bertrand.

Every ROOT’s widget has a corresponding QuartzView object (it’s a Cocoa’s view essentially), QuartzView can only be a child of another QuartzView and have children views of the same QuartzView type. At the top level such hierarchy has QuartzWindow object. No other views/entities are supported.
Integer numbers gVirtualX returns/accepts as a "drawable id’ - are some opaque indices that have no meaning outside ROOT (the same is true about window id that XQuartz returns - it has no sense for Cocoa/ROOT Cocoa).
Technically, it’s possible to make ROOT’s GUI work with Qt-5 (cross-embedding Qt/ROOT widgets in Qt/ROOT applications), but this would require quite serious changes + this makes ROOT dependent on the Qt’s implementation, that can change at any moment (for example, Qt’s changed a lot between v.4 and 5).

Dear Timur,

thanks a lot for your reply!

[quote=“tpochep”]
Technically, it’s possible to make ROOT’s GUI work with Qt-5 (cross-embedding Qt/ROOT widgets in Qt/ROOT applications), but this would require quite serious changes + this makes ROOT dependent on the Qt’s implementation[/quote]
I understand, this would be a bad idea and not really maintainable in the long-term.

I have made some progresses using QGLWidget and embedding via gVirtualX into that, though. Dince my experience with Qt and Gl is still close to zero, progress is slow. However, it seems to me that QGLWidget is also supported inside cocoa-based Qt, so in case I get this working in a good way, this could be a cross-platform solution for embedding ROOT’s Gl-canvases into Gl-capable frameworks such as Qt on all operating systems…

If you call gGLManager->InitGLWindow on OS X (ROOT Cocoa), it calls gVirtualX->CreateOpenGLWindow(…) and this will create QuartzWindow + ROOTOpenGLView or ROOTOpenGLView + embed it into existing QuartzWindow and return some index, that has no meaning for Qt.

Dear Timur,

so in other words, it is impossible to embed a TCanvas drawn in GL inside the OpenGL-context in an QGLWidget in a normal Qt Cocoa app?
If that’s the case, I can stop trying to make it work on Linux (before giving it to somebody for testing on OS X)…

[quote=“olifre”]
so in other words, it is impossible to embed a TCanvas drawn in GL inside the OpenGL-context in an QGLWidget in a normal Qt Cocoa app?
If that’s the case, I can stop trying to make it work on Linux (before giving it to somebody for testing on OS X)…[/quote]

On Linux (with X11) Drawable/Window/Pixmap - are opaque ‘handles’ that X11 understands so different GUI toolkits can interchange them, they still have the same meaning. With ROOT Cocoa/OS X - it’s not like this anymore. As I said, WindowID in ROOT - is just some ‘index’, Qt’s QWidget::winId() will be, probably, NSView * and this NSView * is not valid for TGCocoa.

[quote=“olifre”]Dear Timur,

so in other words, it is impossible to embed a TCanvas drawn in GL inside the OpenGL-context in an QGLWidget in a normal Qt Cocoa app?
If that’s the case, I can stop trying to make it work on Linux (before giving it to somebody for testing on OS X)…[/quote]

I think I have good news. Every year we have some kind of internal “hackathon” in our company. As this year I’m short on ideas, I’ll probably make this happen - make it possible to embed Qt’s widgets in ROOT and ROOT’s widgets (mainly canvases) in a Qt application. Obviously, I mean OS X as platform and the latest Qt 5.

Dear Timur,

this really is good news, thanks a lot for taking up this project, also many thanks from my affected colleagues (they right now have to use a virtual machine on MacOS to use our Qt+ROOT apps)!
Of course, embedding a TCanvas in Qt is the most common application for embedding, in all our 5 applications which were Qt-ROOT based before, this is exactly what they are doing - the “other way around”, Qt widgets in ROOT GUI, we never needed up to now.

In case it’s possible to implement this embedding in a common way to the existing mechanisms on Linux / Windows that would of course be best (or using a common abstraction in between), then experiments can implement their applications in a common way for all platforms.

Cheers and thanks a lot, I am really looking forward to this!
Oliver

Dear Timur,

do you have any news on this - is an implementation coming up?

Cheers and thanks again!
Oliver

Dear Timur, I have the same problem Oliver mentions in this thread, namely porting a Qt-based application containing a ROOT TCanvas embedded inside a Qt widget from a Linux (where it works just fine) to Mac (Sierra, where it crashes).

In a class inheriting from QWidget I have:

int wid = gVirtualX->AddWindow((ULong_t)winId(), width(), height());

wid is always returned zero (on a Mac) and therefore

fCanvas = new TCanvas(title.c_str(), width(), height(), wid);

spits out a message

**Fatal error: requested non-existing drawable 0**
**This drawable not found among allocated/deleted drawables**

Did you succeed in your program to develop the code in a hackaton as you write in this thread on Nov’15?

My code has been developed in over five years by me and a bunch of students which non longer work with me and it would be practically impossible for me to reengineer the code without ROOT and Qt.

Here’s the full snippet (QRootCanvas inherits from a QWidget):

QRootCanvas::QRootCanvas(QWidget *parent, string title) : QWidget(parent, 0), fCanvas(0)
{

   setAttribute     (Qt::WA_PaintOnScreen,    true);
   setAttribute     (Qt::WA_OpaquePaintEvent, true);
   setAttribute     (Qt::WA_NativeWindow,     true);
   setMinimumSize   (50, 50);
   setUpdatesEnabled(kFALSE);
   setMouseTracking (kTRUE );


   int wid = gVirtualX->AddWindow((ULong_t)winId(), width(), height());

   this->setGeometry(
                     0,
                     0,
                     parent->width(),
                     parent->height()
                    );


   fCanvas = new TCanvas(title.c_str(), width(), height(), wid);

   TQObject::Connect("TGPopMenu", "PoppedDown()", "TCanvas", fCanvas, "Update()") ;
}

Thanks for any help you might provide on this!
Dario

Unfortunately, I’ve underestimated the complexity/amount of work and did not finish it, only had a proof of concept somewhat working. Also, it was very version-specific (based on the knowledge of Qt’s internals), for example it will not work today, Cocoa plugin has changed a lot since then.