c++qtqwidgettranslucency

Qt Translucent background causes child widget to be 'imprinted' in parent


I have a parent container (MyCartParentWidget) with translucent background, inside which I have to draw a child widget (MyCart) with an image background (this image is in portrait, this image is in landscape), also drawn with translucent background, and both being QLabels. There is a button clicking on which, the child widget toggles its dimensions (resetCartStyle), i.e it goes from portrait to landscape mode and vice versa. Problem is, when it toggles, the original imprint stays back, i.e, this is the original pic where it is in 'portrait' mode:

enter image description here

Then when I switch to 'landscape' mode, it does shift, but the original 'portrait' mode stays back:

enter image description here

This is my code:

main.cpp:

#include <QApplication>
#include "MyCartParentWidget.hpp"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MyCartParentWidget p;
    p.move(370,10);
    p.show();
    return a.exec();
}

MyCart.cpp:

 #include "MyCart.hpp"
 #include <QPainter>

MyCart::MyCart(QWidget *parent): QLabel(parent)
{
    setAttribute(Qt::WA_TranslucentBackground);
    fPixMap.load("/Users/attitude/Desktop/RnSghvV.png");
    setStyleSheet("background-color: rgba(0,0,0,255);");
    setFixedSize(325,400);

}

void MyCart::paintEvent(QPaintEvent *)
{
    QPainter p(this);
    p.setRenderHint(QPainter::SmoothPixmapTransform);
    p.drawPixmap(0,0,width(),height(),fPixMap);
}

void MyCart::resetCartStyle(QString url, int w, int h)
{
    setFixedSize(w,h);
    fPixMap.load(url);
    this->update();
}

MyCart.hpp:

#pragma once

#include <QLabel>
#include <QPaintEvent>
#include <QPixmap>


class MyCart: public QLabel
{
public:
    MyCart(QWidget*);
    virtual void paintEvent(QPaintEvent *);
    QPixmap fPixMap;
    void resetCartStyle(QString, int w, int h);
};

MyCartParentWidget.cpp:

#include "MyCartParentWidget.hpp"
#include <QPushButton>

MyCartParentWidget::MyCartParentWidget()
{

    setFixedSize(800,700);
    setWindowFlags(Qt::FramelessWindowHint);
    setAttribute(Qt::WA_TranslucentBackground);
    setStyleSheet("background-color: none;");

    fLayout = new QHBoxLayout();
    setLayout(fLayout);
    fLayout->setContentsMargins(0,0,0,0);
    fLayout->setSpacing(0);
    fLayout->setMargin(0);
    fLayout->setAlignment(Qt::AlignLeft | Qt:: AlignTop);
    i = 0;

    fCart = new MyCart(this);
    fLayout->addWidget(fCart);

    QPushButton* p = new QPushButton(this);
    p->setText("Toggle");
    p->move(0,650);
    connect(p,SIGNAL(clicked(bool)),this,SLOT(clickedSlot()));
}

void MyCartParentWidget::clickedSlot()
{
    if (i == 0)
    {
        //enter landscape mode
        i = 1;
        fCart->resetCartStyle("/Users/attitude/Desktop/foo.png",400,325);
    }
    else
    {
        //enter portrait mode
        i = 0;
        fCart->resetCartStyle("/Users/attitude/Desktop/RnSghvV.png",325,400);
    }
}

MyCartParentWidget.hpp:

#pragma once

#include <QLabel>
#include <QHBoxLayout>
#include "MyCart.hpp"


class MyCartParentWidget: public QLabel
{
    Q_OBJECT
public:
    MyCartParentWidget();

    QHBoxLayout* fLayout;
    MyCart *fCart;
    int i;

private slots:
    void clickedSlot();
};

This problem does not happen when I set the background of the parent widget to something like green and comment out the setAttribute(Qt::WA_TranslucentBackground); part, this happens only with setAttribute(Qt::WA_TranslucentBackground); part.

How do I fix this?

Platform - OS X Yosemite, Qt 5.3.1, 32 bit.

Ilya's solution below works fine on Windows, but the problem persists on Mac.


Solution

  • Instead of painting/updating manually, just call the setPixmap method, the QLabel should manage itself. The code is working fine, except on Mac OS X:

    MyCart.cpp:

    #include "MyCart.hpp"
    
    MyCart::MyCart(QWidget *parent): QLabel(parent)
    {
       resetCartStyle("C:/dev/cart/portrait.png");
    }
    
    void MyCart::resetCartStyle(QString url)
    {
       fPixMap.load(url);
       setPixmap(fPixMap);
    }
    

    MyCart.hpp:

    #pragma once
    
    #include <QLabel>
    #include <QPaintEvent>
    #include <QPixmap>
    
    
    class MyCart: public QLabel
    {
    public:
        MyCart(QWidget*);
        QPixmap fPixMap;
        void resetCartStyle(QString);
    };
    

    MyCartParentWidget.cpp:

    #include "MyCartParentWidget.hpp"
    #include <QPushButton>
    
    MyCartParentWidget::MyCartParentWidget()
    {
    
        setFixedSize(800,700);
        setWindowFlags(Qt::FramelessWindowHint);
        setAttribute(Qt::WA_TranslucentBackground);
    
        fLayout = new QHBoxLayout();
        setLayout(fLayout);
        fLayout->setContentsMargins(0,0,0,0);
        fLayout->setSpacing(0);
        fLayout->setMargin(0);
        fLayout->setAlignment(Qt::AlignLeft | Qt:: AlignTop);
        i = 0;
    
        fCart = new MyCart(this);
        fLayout->addWidget(fCart);
    
        QPushButton* p = new QPushButton(this);
        p->setText("Toggle");
        p->move(0,650);
        connect(p,SIGNAL(clicked(bool)),this,SLOT(clickedSlot()));
    }
    
    void MyCartParentWidget::clickedSlot()
    {
        if (i == 0)
        {
            //enter landscape mode
            i = 1;
            fCart->resetCartStyle("C:/dev/cart/landscape.png");
        }
        else
        {
            //enter portrait mode
            i = 0;
            fCart->resetCartStyle("C:/dev/cart/portrait.png");
        }
    }
    

    So what about Mac OS ? The result is a bit better with Qt 5.5.1 than with 5.3.1, here's a screenshot (Mac OS 10.11):

    enter image description here

    So, there's a ghost of the image remaining. To get to a fully correct display, the simplest and most effective trick is to hide/show the parent widget before/after toggling:

    void MyCartParentWidget::clickedSlot()
    {
        hide();
        if (i == 0)
        {
            //enter landscape mode
            i = 1;
            fCart->resetCartStyle("C:/dev/cart/landscape.png");
        }
        else
        {
            //enter portrait mode
            i = 0;
            fCart->resetCartStyle("C:/dev/cart/portrait.png");
        }
        show();
    }
    

    For completeness, below are two other tricks found while searching for a fix, that have fixed the code of the MCV exemple but not the production application.

    First trick:

    MyCart.cpp

    MyCart::MyCart(QWidget *parent): QLabel(parent)
    {
       // do nothing in the constructor
    }
    

    MyCartParentWidget.cpp

    MyCartParentWidget::MyCartParentWidget()
    {
        ...previous code    
    
        // add this line...
        QTimer::singleShot( 0, this, SLOT(onclicked() ); // ...to show the widget
    }
    

    This code still doesn't work with Qt 5.3.1, the OP's version. For this Qt version, another fix is necessary (lifted from this bug report). NB that fix doesn't suppress the ghost image for Qt 5.5.1.

    void MyCartParentWidget::paintEvent(QPaintEvent *)
    {
        QPainter p( this );    
        p.setCompositionMode( QPainter::CompositionMode_Clear );
        p.fillRect( this->rect(), Qt::transparent );
    }
    

    So for a working code (for the exemple) on Mac OS with both Qt versions (5.3.1 and 5.5.1), you have to use both tricks.