PDA

View Full Version : Render OpenGL based widget inside a QFrame with translucent background on Windows



Cupidvogel
1st January 2016, 02:52
I am making a desktop carousel app. There I need to show image widgets, which might contain other sub-widgets as well. For that I am using a QFrame with the required image as background. Here is the image I am trying to use: image (http://imgur.com/RnSghvV). What I want is that only the image shows up, no background image or anything shows up as well, so to the user it looks like just the image. Qt provides a flag called Qt::WA_TranslucentBackground to do that. With it, this code works fine:



#ifndef main_h
#define main_h

#include <QFrame>
#include <QPixmap>

class MyFrame : public QFrame
{
public:
MyFrame(QWidget * parent);

virtual void paintEvent(QPaintEvent * e);

private:
QPixmap _pixmap;
};

#endif




#include <QApplication>
#include <QPainter>
#include "main.h"

MyFrame :: MyFrame(QWidget * parent) : QFrame(parent, Qt::Window|Qt::FramelessWindowHint)
{
setAttribute(Qt::WA_TranslucentBackground);

_pixmap.load("/Users/jaf/iPad_Vector.png");
resize(_pixmap.size());
}

void MyFrame :: paintEvent(QPaintEvent * /*e*/)
{
QPainter p(this);
p.drawPixmap(0,0,width(),height(), _pixmap);
}

int main(int argc, char ** argv)
{
QApplication app(argc, argv);

MyFrame f(NULL);
f.show();

return app.exec();
}



However, the problem is that the iPad frame needs to show other elements within it (in the viewport area), and those elements are drawn and rendered through OpenGL, and OpenGL rendered elements show up as completely blank when placed in an element with the property Qt::WA_TranslucentBackground (I don't know whether it is a Qt or OpenGL bug or expected behavior) . As a result, the viewport shows up just like it is in the original iPad image, because the new widget that is supposed to sit there on top of it shows blank.

This happens only in Windows. Works fine in Mac. Is there any way to fix this? (Calling native C++ APIs on Windows is acceptable. C# as well, provided they can be called through Qt, like we can call native Cocoa code on Mac from Qt)

Or can the iPad image be used as background in any other way?

d_stranz
1st January 2016, 20:56
What if, instead of directly painting into the frame, you give it a child derived from QOpenGLWidget and implement your image painting as an OpenGL texture mapped onto the frame rect?

Cupidvogel
1st January 2016, 21:04
That could have been a solution, but the rendering of the child widget is not in our hands, so it will take time to make that work. Meanwhile, I have another solution in mind, to draw the child widget separately as another window. But there are problems with that:

I have two windows say A and B, A being the iPad image widget (QFrame) and B being the child widget rendered through OpenGL. I need B to always stay on top of A. The ideal way would have been to make B a child widget within A, and it was working fine, but due to the above reasons, and we have to move out B out of A and make it a standalone window. So now we have to ensure that B is a separate window and always appears on top of A. This we can do through raise() call on B and always call it when we detect a click on A to ensure that even though A has been clicked, B is still on top of it.

Problem is, Mac has a feature called Mission Control, where you can now see both windows separately, and there if you click A, the app will show up maximized with A on top of B (and since A is bigger than B size wise, it covers up B completely and there is no way to go back without launching Mission Control again and choosing B! And we cannot detect a click on A there, plus more importantly, we don't want to show B as a separate widget). So I though about making B a tool widget, so that only A shows up in Mission Control, and clicking it does not alter the behavior of a tool widget B, which continues to remain on top.

I first tried this:



QFrame* b = new QFrame();
b->setGeometry(400,40,40,100);
b->setStyleSheet("background: green;");
b->setWindowFlags(Qt::FramelessWindowHint | Qt::Tool);
b->show();


This works, B does not show up as a separate window in Mission Control and stays on top of A. Until you open a normal Finder window and see B disappearing. From this link (https://bugreports.qt.io/browse/QTBUG-10744), it looks like a known feature, and the suggested workaround is to set the Qt::WA_MacAlwaysShowToolWindow flag to true, which I tried next:



QFrame* b = new QFrame();
b->setGeometry(400,40,40,100);
b->setStyleSheet("background: green;");
b->setWindowFlags(Qt::FramelessWindowHint | Qt::Tool);
b->setAttribute(Qt::WA_MacAlwaysShowToolWindow,true);
b->show();


Same effect, the tool widget always hides when the app loses focus. So I decided to try TooTip, and came up with this:



QFrame* b = new QFrame();
b->setGeometry(400,40,40,100);
b->setStyleSheet("background: green;");
b->setWindowFlags(Qt::FramelessWindowHint | Qt::ToolTip);
b->show();


Now this works, does not hide the tool widget when app loses focus. But the problem is, it never hides at all! Even if you click other applications and they come into view, the widget B appears on top of them as well! I added this line to see if it works, it didn't. Same result:



b->setAttribute(Qt::WA_MacAlwaysShowToolWindow,false) ;


So I next tried



b->setWindowFlags(Qt::FramelessWindowHint | Qt:: WindowStaysOnTopHint);


This also behaves in the same way as ToolTip, B always stays on top of all other applications, there doesn't seem to be a way to hide it.

If I can do this, this will be a good replacement solution in place of drawing B inside A, thus we can draw A smoothly and put B on top of it without it being affected.

d_stranz
1st January 2016, 22:05
Wish I could be of help, but I know nothing of Macs.

Cupidvogel
1st January 2016, 22:18
Well, let's hope somebody who does can help.. :)

Added after 11 minutes:

Is there any other possible way to render the iPad image as background of the parent QFrame without using WA_TranslucentBackground property?

Cupidvogel
2nd January 2016, 00:52
Hmm. Looks like this is a known issue. Qt, at least as of 5.3.1 never implemented the WA_MacAlwaysShowToolWindow feature at all, so setting it to true will have no effect. This native Objective C code can be compiled along with my Qt code, passing in pointer to my B widget to this method (before it we have to add the Qt::Tool property):



void preventHidingOnFocusOut(QWidget* aWidget)
{
[[reinterpret_cast<NSView *>(aWidget->winId()) window] setHidesOnDeactivate:NO];
}


However, this brings back the same issue as using Qt::ToolTip - it stays on top of other applications as well. Source - https://bugreports.qt.io/browse/QTBUG-29816