QOpenGLWidget and transparency - qt

I have small problem with QOpenGLWidget and its background color.
When I want to create semi-transparent rect on my custom QOpenGLWidget using QPainter there are 2 different results:
If MyCustomWidget have parent - on every update rect's color multiplies (and after few repaints it is opaque, like previous painting result not cleaned)
If MyCustomWidget doesn't have parent - color doesn't repaints each time
Here is code example for QPainter:
class Widget : public QOpenGLWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = 0)
: QOpenGLWidget(parent)
{
resize(800, 600);
Test *test = new Test(this);
}
~Widget(){}
protected:
void paintEvent(QPaintEvent *) {}
protected:
void initializeGL() {
if(paintEngine()->type() != QPaintEngine::OpenGL &&
paintEngine()->type() != QPaintEngine::OpenGL2)
qDebug() << "ERROR. Type is: " << paintEngine()->type();
}
void resizeGL(int, int) {}
void paintGL() {
QPainter p;
p.begin(this);
{
p.fillRect(rect(), Qt::white);
}
p.end();
}
private:
class Test : public QOpenGLWidget
{
public:
Test(QWidget *parent = 0) : QOpenGLWidget(parent) {
resize(100, 100);
}
protected:
void paintEvent(QPaintEvent *) {
QPainter p(this);
p.fillRect(rect(), QColor(125, 125, 125, 255/10));
}
};
};
Also by default it has black background (I don't know how to fix it. setAttribute(Qt::WA_TranslucentBackground) doesn't helps).
Also, when I'm trying to clear color using glClear it ignores alpha (both on QOpenGLWidget with parent and not). Here is Test class from previous code, but now it is using opengl to clear color:
class Test : public QOpenGLWidget
{
public:
Test(QWidget *parent = 0) : QOpenGLWidget(parent) {
resize(100, 100);
}
void initializeGL() {
QOpenGLFunctions *f = context()->functions();
f->glClearColor(0.0f, 1.0f, 0.0f, 0.1f);
}
void paintGL() {
QOpenGLFunctions *f = context()->functions();
f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
};
How can I fix this problems?
I'm using Qt 5.5.0, Windows 10, MinGW 4.9.2

Xeed is correct when saying the QOpenGLWidget is painted first.
I'm not an expert but I think I found the solution. You need to set a widget attribute to always make the widget stacked on top (think of the widgets as layers on the window). Here is a link to where I got the following information:
P.S. As mentioned in the QQuickWidget post, there is a limitation regarding semi-transparency when using QQuickWidget or QOpenGLWidget as child widgets. For applications that absolutely need this, Qt 5.4 offers a workaround: the newly introduced Qt::WA_AlwaysStackOnTop widget attribute. This, at the expense of breaking the stacking order for other types of layouts, makes it possible to have a semi-transparent QQuickWidget or QOpenGLWidget with other widgets visible underneath. Of course, if the intention is only to make other applications on the desktop visible underneath, then the Qt::WA_TranslucentBackground attribute is sufficient
Solution in Python:
set attribute of OpenGL widget
setAttribute(Qt.WA_AlwaysStackOnTop)
Now the OpenGL widget is considered 'on top' in the window. Use 'glClearColor' function and specify the alpha channel to be zero (0.0).
glClearColor(0.0, 0.0, 0.0, 0.0)
I'm not sure how to write that in other languages but this worked for me. The OpenGL widget no longer has the default black background. It is transparent! Hope this helps.

As far as I know the QOpenGLWidget is always drawn first. Therefore you cannot show any widgets layered below. I'm currently looking into the same issue. I'll report back, when I find any solution.

I've had similar issue with QOpenGLWidget not repainting correctly in transparent areas and decided to switch to QOpenGLWindow wrapped inside QWidget::createWindowContainer()

Related

Qt Button Group over OpenGLWidget

I am designing a user interface with Qt Creator. I have a openGLwidget which covers all the form and I want to place some buttons aligned to bottom and some kind of notification frames over the widget. Is there any default way to do that?
When I try to design that on creator, I could not get rid of layout limits and don't allow me to place a widget over openglwidget.
When reading the question first, I was reading this as “How to make a QOpenGLWidget with Head-Up Display with buttons?”.
How to make a QOpenGLWidget with Head-Up Display (painted with QPainter), I once answered in SO: Paint a rect on qglwidget at specifit times. However, there was only painting – no interactive widgets.
Hence, I prepared a new sample testQGLWidgetHUDButtons.cc:
#include <QtWidgets>
class OpenGLWidget: public QOpenGLWidget, public QOpenGLFunctions {
private:
struct ClearColor {
float r, g, b;
ClearColor(): r(0.6f), g(0.8f), b(1.0f) { }
} _clearColor;
public:
OpenGLWidget(QWidget *pQParent = nullptr):
QOpenGLWidget(pQParent),
QOpenGLFunctions()
{ }
virtual ~OpenGLWidget() = default;
OpenGLWidget(const OpenGLWidget&) = delete;
OpenGLWidget& operator=(const OpenGLWidget&) = delete;
void setClearColor(float r, float g, float b);
protected:
virtual void initializeGL() override;
virtual void paintGL() override;
};
void OpenGLWidget::setClearColor(float r, float g, float b)
{
_clearColor.r = r; _clearColor.g = g; _clearColor.b = b;
update(); // force update of widget
}
void OpenGLWidget::initializeGL()
{
initializeOpenGLFunctions();
}
void OpenGLWidget::paintGL()
{
glClearColor(
_clearColor.r, _clearColor.g, _clearColor.b, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
// setup GUI
OpenGLWidget qWin;
QGridLayout qGrid;
QPushButton qBtn1(QString::fromUtf8("Black BG"));
qGrid.addWidget(&qBtn1, 1, 1);
QPushButton qBtn2(QString::fromUtf8("White BG"));
qGrid.addWidget(&qBtn2, 1, 2);
QPushButton qBtn3(QString::fromUtf8("Blue BG"));
qGrid.addWidget(&qBtn3, 1, 3);
qGrid.setRowStretch(0, 1);
qGrid.setColumnStretch(0, 1);
qWin.setLayout(&qGrid);
qWin.show();
// install signal handlers
QObject::connect(&qBtn1, &QPushButton::clicked,
[&qWin](bool) { qWin.setClearColor(0.0f, 0.0f, 0.0f); });
QObject::connect(&qBtn2, &QPushButton::clicked,
[&qWin](bool) { qWin.setClearColor(1.0f, 1.0f, 1.0f); });
QObject::connect(&qBtn3, &QPushButton::clicked,
[&qWin](bool) { qWin.setClearColor(0.6f, 0.8f, 1.0f); });
// runtime loop
return app.exec();
}
and the project file testQGLWidgetHUDButtons.pro:
SOURCES = testQGLWidgetHUDButtons.cc
QT += widgets opengl
Compiled and tested in VS2013 on Windows 10:
It's actually quite easy – QOpenGLWidget is derived from QWidget. QWidget provides the method QWidget::setLayout(). To add child widgets, a QLayout should be used which manages layout of children in parent widget (and takes part in size negotiation when parent widget is layouted).
To keep it simple, I used a QGridLayout where buttons are placed in cells (1, 1), (1, 2), and (1, 3). Row 0 and column 0 are left empty but enabled for stetching. This results effectively in attaching the buttons in lower right corner.
When I was about to publish this answer, I took a second look onto the question and realized the Qt Button Group in title. I would've ignore it (multiple buttons are a button group?) but there is also the qbuttongroup which made me stumbling.
A QButtonGroup:
The QButtonGroup class provides a container to organize groups of button widgets.
QButtonGroup provides an abstract container into which button widgets can be placed. It does not provide a visual representation of this container (see QGroupBox for a container widget), but instead manages the states of each of the buttons in the group.
(Emphasize mine.)
So, a button group can be used e.g. to keep radio buttons in sync. It is derived from QObject and hence, neither a QWidget nor a QLayout. Thus, it's not suitable to add visible child widgets to the QOpenGLWidget.
Though I've no experiences with QCreator, I assume it should work there as well as it does in my sample:
Assign a layout to the QOpenGLWidget (e.g. QGridLayout as I did in my sample).
Add QPushButtons to this layout.
Btw. IMHO this is no specific problem about the QOpenGLWidget – it should've happend with any other QWidget as well.

How to animate the outline of a QGraphicsItem in real time?

I am designing a timer with Qt. With QGraphicsEllipseItem, I drew a circle and now I need to animate the QPen around this circle (change color) every second. I found QGraphicsPathItem, but I need some examples on how to move forward. Can anyone show me an example?
You have two problems:
QGraphicsEllipseItem is not a QObject so QPropertyAnimation can't be used directly on this item
QGraphicsItemAnimation doesn't cover property you want to animate.
What you can do?
IMO best approach is to provide some custom QObject on which you could do this animation. You can inherit QObject or use fake QGraphicsObject (which is a QObject).
class ShapeItemPenAnimator : public QGraphicsObject {
Q_OBJECT
private:
QAbstractGraphicsShapeItem *mParent;
QPropertyAnimation *mAnimation;
public:
QPROPERTY(QColor penColor
READ penColor
WRITE setPenColor)
explicit ShapeItemPenAnimator(QAbstractGraphicsShapeItem * parent)
: QGraphicsObject(parent)
, mParent(parent) {
setFlags(QGraphicsItem::ItemHasNoContents);
mAnimation = new QPropertyAnimation(this, "penColor", this);
}
QColor penColor() const {
return mParent->pen().color();
}
public slots:
void setPenColor(const QColor &color) {
QPen pen(mParent->pen());
pen.setColor(color);
mParent->setPen(pen);
}
public:
void paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget = 0) {
}
QRectF boundingRect() const {
return QRectF();
}
QPropertyAnimation *animation() const {
return mAnimation;
}
}
Now you just attach this object to your QGraphicsEllipseItem and set animation you need.
// yourEllipse
ShapeItemPenAnimator *animator = new ShapeItemPenAnimator(yourEllipse);
animator->animation()->setEndValue(....);
animator->animation()->setStartValue(....);
animator->animation()->setDuration(....);
animator->animation()->setEasingCurve(....);
There are several classes helping with animations of QGraphicsItem in Qt. I suggest looking into QGraphicsItemAnimation and QPropertyAnimation. You can use the second one to animate the color of an item. Here is an example of using QPropertyAnimation:
How to make Qt widgets fade in or fade out?

Sliding OSX Dock type "sidebar" rather embedded QDockWidget type?

I want to make a sliding "sidebar" similar to the functionality of the OSX "Dock" (e.g. mouse passes edge of screen and Dock slides out). I've been playing around with QDockWidget but since that is embedded in the window layout, it causes everything to shift when it becomes visible.
Can someone suggest a way to implement this?
Doesn't need to float (as a separate window/tool bar)
Should scale to window height (e.g. window can be fullscreen or default size)
Doesn't need to slide (animate) if that is complicated.
I'm new to Qt and so don't want to over-think this. Is this just a matter of a custom widget or should I be looking at a borderless window? The custom widget approach seems right but I don't know how to specify that it overlay other window content and also scale if the window scales.
QDockWidget has nothing to do with what you want - behaviorally. Just because it's called a Dock widget doesn't mean it's the same "Dock" concept as in OS X. It merely means that it docks somewhere. QDockWidget's documentation quite explicitly explains what is meant by the docking behavior.
The code below implements the behavior you seem to want. Whether it's good design or not is arguable. The reason the code is "convoluted" seems to hint that nobody is expected to come up with such a UI design. What's wrong with actually clicking a button somewhere to display the slider window?
The code works under both Qt 4.8 and 5.1.
Note: This begs to be implemented in Qt Quick 2. That's what it was designed for :) Of course Qt 4.6+ improved the behavior of the QWidget-moving animations, and Qt 5 does further tweaks, but really this code smells bad and there's a good reason it does: QWidget API, while powerful, ultimately encapsulates a set of APIs that date to 1984 when the original Macintosh was released. There's only so much you can do when you have to composite results from a bunch of stacked painters. In Qt Quick, the rendering is done by the GPU. The animation amounts to passing a couple of new floats to the GPU to update a single transformation matrix. That's it.
#include <QApplication>
#include <QWidget>
#include <QGridLayout>
#include <QLabel>
#include <QPainter>
#include <QGradient>
#include <QMouseEvent>
#include <QPropertyAnimation>
class Slider : public QWidget {
void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE {
QPainter p(this);
QLinearGradient g(QPointF(0,0), QPointF(rect().bottomRight()));
g.setColorAt(0, Qt::blue);
g.setColorAt(1, Qt::gray);
p.setBackground(g);
p.eraseRect(rect());
p.setPen(Qt::yellow);
p.setFont(QFont("Helvetica", 48));
p.drawText(rect(), "Click Me To Hide");
}
void mousePressEvent(QMouseEvent *) Q_DECL_OVERRIDE {
hide();
}
public:
explicit Slider(QWidget *parent = 0) : QWidget(parent) {
setAttribute(Qt::WA_OpaquePaintEvent);
}
};
class Window : public QWidget {
QGridLayout m_layout;
Slider m_slider;
QLabel m_label;
QPropertyAnimation m_animation;
public:
explicit Window(QWidget *parent = 0, Qt::WindowFlags f = 0) :
QWidget(parent, f),
m_layout(this),
m_slider(this),
m_animation(&m_slider, "pos")
{
setMouseTracking(true);
m_layout.addWidget(&m_label);
m_slider.hide();
m_slider.setMouseTracking(false);
m_animation.setStartValue(QPoint(-width(), 0));
m_animation.setEndValue(QPoint(0, 0));
m_animation.setDuration(500);
m_animation.setEasingCurve(QEasingCurve::InCubic);
}
void leaveEvent(QEvent *) {
if (window() && QCursor::pos().x() <= window()->geometry().topLeft().x()) {
showSlider();
}
}
void childEvent(QChildEvent * ev) {
if (ev->added() && ev->child()->isWidgetType()) {
ev->child()->installEventFilter(this);
static_cast<QWidget*>(ev->child())->setMouseTracking(true);
}
}
bool event(QEvent * ev) {
eventFilter(this, ev);
return QWidget::event(ev);
}
bool eventFilter(QObject *, QEvent * ev) {
if (ev->type() == QEvent::MouseMove) {
auto pos = QCursor::pos();
if (window() && window()->isFullScreen()) {
if (pos.x() <= window()->geometry().topLeft().x()) {
showSlider();
}
}
m_label.setText(QString("%1, %2").arg(pos.x()).arg(pos.y()));
}
return false;
}
void resizeEvent(QResizeEvent *) {
m_slider.resize(size());
m_animation.setStartValue(QPoint(-width(), 0));
}
Q_SLOT void showSlider() {
if (m_slider.isVisible() || (window() && qApp->activeWindow() != window())) return;
m_slider.raise();
m_slider.show();
m_animation.start();
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Window w;
w.show();
return a.exec();
}

Qt disable move window

I have a QMainWindow with this flag :
this->setWindowFlags(Qt::SubWindow);
How do to forbid the window moving, and this, keeping this window style ?
I don't think there is a cross-os Qt way to achieve this when using the standard window controls.
You can try stuff like:
class Widget : public QWidget {
Q_OBJECT
public:
Widget()
: fixed_pos_(QPoint(100, 100)) {
setWindowFlags(Qt::SubWindow);
}
void SetFixedPos(const QPoint& pos) {
fixed_pos_ = pos;
}
protected:
void moveEvent(QMoveEvent* ev) {
if (ev->pos() != fixed_pos_)
move(fixed_pos_);
}
private:
QPoint fixed_pos_;
};
These have a few issues like flicker, does not update until Mouse-release and so on that's also different per OS.
Most efficient way is to just make your Window a Qt::FramelessWindowHint and render a titlebar yourself. That way you can pretty much do what you want when it comes to handling events on that titlebar.

Problem with drawing focus frame in Qt

I'm trying to create custom widget inheriting QFrame. All works fine, but I'm unable to draw the focus rectangle around my widget. Below is the sample code I use for drawing:
frame.h
class Frame : public QFrame {
Q_OBJECT
public:
Frame(QWidget *parent = 0);
~Frame();
protected:
void paintEvent(QPaintEvent *event);
private:
Ui::Frame *ui;
};
frame.cpp
Frame::Frame(QWidget *parent) :
QFrame(parent),
ui(new Ui::Frame)
{
ui->setupUi(this);
setFocusPolicy(Qt::ClickFocus);
}
Frame::~Frame()
{
delete ui;
}
void Frame::paintEvent(QPaintEvent *event)
{
QFrame::paintEvent(event);
if (hasFocus()) {
QStylePainter painter(this);
QStyleOptionFocusRect option;
option.initFrom(this);
option.backgroundColor = palette().dark().color();
painter.drawPrimitive(QStyle::PE_FrameFocusRect, option);
}
}
What I mean by 'unable to draw focus frame' is that when you click a standard widget that accepts focus (let's say QLineEdit), it has a blue rectangle drawn around it. When I click my widget there is no such rectangle drawn. Are there any more things I should do besides setting focusPolicy on my widget?
It might have something to do with the style your app is using. When I try your code with the "gtk" and "cleanlooks" style, no focus rectangle is drawn. With "plastique" and "windows" it is. Since I'm on Linux, I cannot test "windowsxp" and "macintosh". Try running with the -style option and see what happens.
try also
setFocusPolicy(Qt::StrongFocus);
setAttribute( Qt::WA_MacShowFocusRect);

Resources