Полупрозрачный фон Qt заставляет дочерний виджет «отпечатываться» в родительском

У меня есть родительский контейнер (MyCartParentWidget) с полупрозрачным фоном, внутри которого я должен нарисовать дочерний виджет (MyCart) с фоновым изображением (это изображение в книжной ориентации, это изображение в альбомной ориентации), также нарисовано с полупрозрачным фоном, и оба являются QLabels. Есть кнопка, нажав на которую, дочерний виджет переключает свои размеры (resetCartStyle), т.е. переходит из портретного режима в ландшафтный и наоборот. Проблема в том, что когда он переключается, исходный отпечаток остается, то есть это исходное изображение, где оно находится в «портретном» режиме:

введите здесь описание изображения

Затем, когда я переключаюсь в «пейзажный» режим, он смещается, но исходный «портретный» режим остается:

введите здесь описание изображения

Это мой код:

основной.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();
}

Моя корзина.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();
}

Моя корзина.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();
};

Эта проблема не возникает, когда я устанавливаю фон родительского виджета на что-то вроде green и закомментирую часть setAttribute(Qt::WA_TranslucentBackground);, это происходит только с частью setAttribute(Qt::WA_TranslucentBackground);.

Как я могу это исправить?

Платформа - OS X Yosemite, Qt 5.3.1, 32 бит.

Приведенное ниже решение Ильи отлично работает в Windows, но проблема остается на Mac.


person SexyBeast    schedule 17.01.2016    source источник


Ответы (1)


Вместо рисования/обновления вручную просто вызовите метод setPixmap, QLabel должен управлять собой. Код работает нормально, за исключением Mac OS X:

Моя корзина.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);
}

Моя корзина.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");
    }
}

Так что насчет Mac OS? Результат с Qt 5.5.1 немного лучше, чем с 5.3.1, вот скриншот (Mac OS 10.11):

введите здесь описание изображения

Итак, остался призрак изображения. Чтобы получить полностью правильное отображение, самый простой и эффективный способ — скрыть/показать родительский виджет до/после переключения:

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();
}

Для полноты ниже приведены два других приема, найденных при поиске исправления, которые исправили код примера MCV, но не рабочего приложения.

Первый трюк:

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
}

Этот код по-прежнему не работает с Qt 5.3.1, версией OP. Для этой версии Qt необходимо другое исправление (взято из этого отчета об ошибке). Обратите внимание, что это исправление не подавляет фантомное изображение для Qt 5.5.1.

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

Таким образом, для рабочего кода (для примера) в Mac OS с обеими версиями Qt (5.3.1 и 5.5.1) вы должны использовать оба приема.

person Ilya    schedule 17.01.2016
comment
Например QPainter::fillRect - person Ilya; 18.01.2016
comment
Да, fillRect чем? Пожалуйста, предоставьте минимальный рабочий пример. Я предполагаю, что вы говорите о переопределении paintEvent из MyCartParentWidget? - person SexyBeast; 18.01.2016
comment
Что-то вроде fillRect( rect(), QColor(0, 0, 0, 0) ); - person Ilya; 18.01.2016
comment
Неа. Не работает. Я добавил метод paintEvent в MyCartParentWidget, где все, что я делаю, это QPainter p(this); p.fillRect( rect(), QColor(0, 0, 0, 0) );. А в методе resetCartStyle после вызова this->update(); я вызвал this->parentWidget()->update();. Тот же результат. - person SexyBeast; 18.01.2016
comment
Это не для родительского события рисования, а для события рисования корзины перед рисованием растрового изображения. - person Ilya; 18.01.2016
comment
Да, я тоже положил его туда, для проверки. Смотрите отредактированный вопрос. Тот же результат... Можете ли вы привести минимальный рабочий пример? - person SexyBeast; 18.01.2016
comment
Давайте продолжим обсуждение в чате. - person Ilya; 18.01.2016
comment
@AttitudeMonger результат с Q 5.5.1 в Mac OS 10.11 забавный, он почти правильный... - person Ilya; 27.01.2016
comment
Меня немного смущают правки. Можете ли вы сделать одно последнее крутое редактирование своего ответа и предоставить окончательный полный код? - person SexyBeast; 27.01.2016
comment
Я добавил полный код для двух измененных файлов, но я, вероятно, попытаюсь найти более чистый трюк для окончательной версии. Я также был бы рад, если бы гуру Qt мог объяснить, что происходит за сценой (ошибка Qt?). - person Ilya; 27.01.2016
comment
То есть таймер нужно вызывать только один раз, в конструкторе родительского виджета? - person SexyBeast; 27.01.2016
comment
Да все, просто отложенный звонок. - person Ilya; 27.01.2016
comment
Вздох. Может в Qt 5.5 работает, в 5.3 результат тот же.. :( - person SexyBeast; 27.01.2016
comment
Данг. Я посмотрю отчеты об ошибках Qt, посмотрю, есть ли что-то связанное между 5.3.1 и 5.5.1. - person Ilya; 27.01.2016
comment
Вот этот: bugreports.qt.io/browse/QTBUG-38760. Очень похоже на вашу проблему, нет ссылки на исправление, но есть обходной путь. - person Ilya; 27.01.2016
comment
Хммм. Этот paintEvent должен быть установлен в родительском виджете или в корзине? Родитель, да? - person SexyBeast; 27.01.2016
comment
Имеет смысл установить его в родительском, да. - person Ilya; 27.01.2016
comment
Работает как шарм! Это чертовски здорово! Я погуглил, но отчет об ошибке почему-то не появился, иначе все было бы так просто.. :) В любом случае, вы более чем заслужили награду! - person SexyBeast; 27.01.2016
comment
Что ж, это была интересная погоня. Я обновлю ответ со всей информацией. Спасибо за щедрость BTW. - person Ilya; 27.01.2016
comment
Да, пожалуйста... SO более удобен для Google, чем сайт отчетов об ошибках Qt.. :( - person SexyBeast; 27.01.2016
comment
Вздох. Призрачное изображение вызывает проблемы... Давайте продолжим в чате. - person SexyBeast; 27.01.2016
comment
@AttitudeMonger содержит ли (производственный) виджет корзины что-либо, кроме изображения (метки или границы)? - person Ilya; 28.01.2016
comment
Нет, он не содержит ничего другого. Родительский виджет, за которым следует дочерний виджет (корзина), внутри которого сгенерированное OpenGL изображение находится внутри QFrame. - person SexyBeast; 28.01.2016
comment
Итак, в производственном коде тележка - это QFrame, а не QLabel? - person Ilya; 29.01.2016
comment
Нет-нет, корзина по-прежнему QLabel. Внутри него, в области окна просмотра, находится QFrame (того же размера, что и окно просмотра), внутри которого находится сгенерированное изображение. Это изображение является QWidget. - person SexyBeast; 29.01.2016
comment
Мне удалось найти способ заставить его работать, если я скрою и покажу родительский виджет после изменения, теперь он правильно перерисовывается, удаляя призрачное изображение. - person SexyBeast; 29.01.2016
comment
Большой! Я помню, как в какой-то момент тестировал скрытие/показ, но, вероятно, это было в корзине, а не в родительском элементе. - person Ilya; 29.01.2016
comment
@AttitudeMonger Я обновил ответ вашим окончательным (надеюсь) решением. Чего все еще не хватает, так это объяснения (или реального исправления) поведения Mac OS от кого-то, кто может проанализировать эту часть кода Qt. - person Ilya; 30.01.2016
comment
Это никогда не оставит меня в покое. У меня есть другая ситуация в другом месте, где скрывать и показывать неприемлемо, так что это причиняет мне боль. Я подробно описал это здесь: bugreports.qt.io/browse/QTBUG-50728 . Удачи! - person SexyBeast; 30.01.2016
comment
Поможет ли мне воспроизвести проблему, используя код отчета об ошибке с 5.5? Я считаю, что именно поэтому отчет об ошибке находится в статусе «Нужна дополнительная информация». - person Ilya; 30.01.2016
comment
Да конечно, не стесняйтесь! Если вы скажете, что это одно и то же, я могу изменить информацию о версии, чтобы привлечь больше внимания, или вы можете присоединиться с комментарием, в котором говорится то же самое. - person SexyBeast; 30.01.2016
comment
Святый дым, я вернулся домой, даже я могу попробовать с 5.5! Но ты сначала сделай это и дай мне знать, тогда я сделаю.. - person SexyBeast; 30.01.2016
comment
Давайте продолжим обсуждение в чате. - person Ilya; 30.01.2016