Qt - визуализировать QFrame с изображением в качестве фона без использования WA_TranslucentBackground

Мне нужно установить QFrame с фоновым изображением: https://imgur.com/RnSghvV. Вот что я пробовал:

setAutoFillBackground(true);
setWindowFlags(Qt::FramelessWindowHint);
setStyleSheet("background: transparent;");
QPalette p = this->palette();
p.setBrush(QPalette::Base, QPixmap("ipad.png"));
this->setPalette(p);

Вот что я получаю:

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

Как видите, по краям есть надоедливая черная рамка, которую я хочу убрать, и просматривать только изображение. Как я могу это сделать?

PS, его можно заставить работать, используя свойство Qt:: WA_TranslucentBackground, как показано здесь. Однако в моем случае QFrame будет содержать другие подвиджеты, некоторые из которых представляют собой QImages, отображаемые через OpenGL, и установка Qt:: WA_TranslucentBackground делает эти изображения невидимыми в Windows. Поэтому я ищу решение, которое не использует это свойство.

Редактировать:

На основе решения, предложенного Евгением, я попробовал это (я использовал 325 на 400 в качестве размеров для виджета, потому что это размеры изображения):

#include <QApplication>
#include <QLabel>
#include <QBitmap>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QLabel l;
    l.setWindowFlags(Qt::FramelessWindowHint);
    QPixmap p(":/img/ipad.png");
    l.setPixmap(p);
    l.setScaledContents(true);
    l.resize(300, 500); //just to test my idea
    l.setMask(p.scaled(l.width(),l.height(),Qt::IgnoreAspectRatio,
    Qt::SmoothTransformation).mask());
    l.show();
    return a.exec();
}

При этом выглядит так:

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

Справа и снизу по-прежнему отображается серый фон. Если я добавлю setStyleSheet("background: transparent"), серый фон станет черным.


person SexyBeast    schedule 19.12.2015    source источник
comment
Возможно setStyleSheet (цвет фона: прозрачный;); это исправит.   -  person Alexander V    schedule 19.12.2015
comment
background работает так же, как background-color. Пробовал оба, не работает.   -  person SexyBeast    schedule 19.12.2015
comment
Может setStyleSheet("border-image: ipad.png"); не трогая палитру виджета будет работать?   -  person dvvrd    schedule 19.12.2015
comment
Я предполагаю, что причина в том, что изображение является фоновым, и тогда не применяется прозрачная маска. Изображение должно быть на переднем плане.   -  person Alexander V    schedule 19.12.2015
comment
@AlexanderVX, можешь показать код для этого? Применение маски с ролью переднего плана?   -  person SexyBeast    schedule 19.12.2015
comment
Вы пытались использовать QWidget::setMask?   -  person Evgeny    schedule 05.01.2016
comment
Можете ли вы привести рабочий пример? Я пробовал, у меня не работает. Но вроде должно.   -  person SexyBeast    schedule 06.01.2016
comment
@AttitudeMonger вот пример из документации Qt doc.qt.io/ qt-5/qtwidgets-widgets- shapeclock-example.html   -  person Evgeny    schedule 06.01.2016


Ответы (2)


Хорошо, наконец-то я получил полностью рабочий пример с использованием setMask. Основная проблема в том, что ваше изображение имеет лишнее пространство на границе. Это проблема для OS X. В Windows он отлично работает с исходным изображением, но для Mac я его вырезал. Вы можете получить его здесь: http://smages.com/images/pic2016010ata.png

Тогда я мог бы получить рабочий пример на Mac с QFrame.

В файле .h:

#include <QFrame>
#include <QPixmap>

class TestFrame : public QFrame
{
    Q_OBJECT
public:
    TestFrame(QWidget* p = 0);

protected:
    void resizeEvent(QResizeEvent* e) override;

private:
    QPixmap _pix;
};

В файле .cpp:

#include <QBitmap>

TestFrame::TestFrame(QWidget *p) :
    QFrame(p, Qt::FramelessWindowHint | Qt::WindowSystemMenuHint),
    _pix(":/img/i2.png")
{
    QPalette pal = palette();
    pal.setBrush(QPalette::Background, QBrush(_pix));
    setPalette(pal);
    resize(_pix.width(), _pix.height());
}

void TestFrame::resizeEvent(QResizeEvent *e)
{
    QFrame::resizeEvent(e);
    setMask(_pix.scaled(size()).mask());
}

Вот скриншот на OS X

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

Второе решение — использовать QLabel вместо QFrame следующим образом:

#include <QApplication>
#include <QLabel>
#include <QBitmap>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QLabel l;
    l.setWindowFlags(Qt::FramelessWindowHint);
    QPixmap p(":/img/ipad.png");
    l.setPixmap(p);
    l.setScaledContents(true);
    l.resize(300, 500); //just to test my idea
    l.setMask(p.scaled(l.width(),l.height(),Qt::IgnoreAspectRatio,
    Qt::SmoothTransformation).mask());
    l.show();
    return a.exec();
}
person Evgeny    schedule 06.01.2016
comment
Спасибо. Я проверю это. Можете ли вы предоставить скриншот вашего приложения в соответствии с этим кодом? - person SexyBeast; 07.01.2016
comment
Я думаю, проблема в том, что размер этикетки и размер изображения отличаются. Поэтому я обновил свой ответ с поддержкой масштабированного изображения. Вам не нужно масштабировать свою метку, но вы должны установить маску с масштабированным пиксельным изображением. Попробуй это. - person Evgeny; 07.01.2016
comment
Странно. У меня все еще та же проблема. С какой версией Qt вы тестируете? У меня 5.3.1, 32-битная, на OS X, Yosemite. Соотношение сторон может искажать изображение, но это не должно влиять на установленный фон, как вы сказали. - person SexyBeast; 07.01.2016
comment
Кстати, ваше решение не совсем точное, фон хорошо виден по нижнему и правому краям, не такой ровный, как при использовании WA_TranslucentBackground. Это гладкость, которую мне нужно достичь: stackoverflow.com/a/31010821/1469954 - person SexyBeast; 07.01.2016
comment
Это в Windows с Qt 5.5.1 - person Evgeny; 07.01.2016
comment
Я ошибся, предыдущее решение отлично работает в Windows (вы также можете восстановить это решение для дальнейшего использования зрителями), а на Mac, хотя это будет работать, я могу использовать WA_TranslucentBackground, т.е. аутентичный способ сделать это Работа. Большое спасибо! - person SexyBeast; 09.01.2016
comment
Пока мы этим занимаемся, не могли бы вы рассказать мне, как нарисовать полностью прозрачный QFrame/QWidget в Windows без WA_TranslucentBackground, то есть без изображения или чего-либо в качестве фона, просто прозрачный? Я попытался установить пустой pixmap и добавить setStyleSheet("background: transparent"), он все еще отображается с серовато-белым/черным прямоугольником фона. Я могу отредактировать вопрос, включив в него это (хотя изначально об этом не упоминалось). - person SexyBeast; 09.01.2016
comment
В полупрозрачном окне хоть виджет и есть, но его видно сквозь и фон можно трогать, т.е. как будто виджета вообще нет. Мне не нужен этот вариант использования, я просто хочу, чтобы он был прозрачным. - person SexyBeast; 09.01.2016
comment
@AttitudeMonger Я восстановил второе решение в своем ответе. Также, если вам нравится этот ответ, отметьте его как действительный. - person Evgeny; 10.01.2016
comment
Сделал это. Можете ли вы сказать мне, есть ли способ сделать полностью прозрачное окно (без фонового изображения или чего-то еще) в Windows? - person SexyBeast; 10.01.2016
comment
Афаик, это невозможно. Это можно сделать в OS X. - person Evgeny; 10.01.2016
comment
С setMask вы сказали, что будут видны только те части виджета, соответствующая часть которых видна на изображении. По этой логике, почему весь виджет не невидим, когда изображения нет вообще? - person SexyBeast; 10.01.2016
comment
Вы уверены, что это работает для всех изображений? Я попробовал это изображение в Windows: imgur.com/rf5d4cj, я вижу белый закругленный контур на закругленных углах .. - person SexyBeast; 16.01.2016
comment
Я думаю, что должен. Но также я думаю, что лучший способ подготовить маску отдельно от изображения. Сохраните pixmac.mask() на диск, откройте графическим редактором и внесите исправления. Затем используйте это изображение в качестве маски вместо pixmap.mask(). - person Evgeny; 17.01.2016
comment
Как внести исправления? Само изображение идёт сюда с белым фоном по углам.. - person SexyBeast; 17.01.2016

Изображение переднего плана в главном окне (в нашем случае с битами прозрачности) можно применить следующим образом:

#include "mainwindow.h"
#include <QLabel>
#include <QPixmap>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent, Qt::FramelessWindowHint)
{
    setAttribute(Qt::WA_TranslucentBackground, true);
    setStyleSheet("background-color: rgba(255, 255, 255, 255);"); // preventing child widgets from not being drawn

    QLabel* pImageLabel = new QLabel;
    QPixmap pixmap(":/RnSghvV.png");
    pImageLabel->setPixmap(pixmap);

    setCentralWidget(pImageLabel);
}

А QLabel является производным от QFrame, поэтому его можно применять в том же заполнителе. Имейте в виду, что имея главное окно программы, вам также нужно применить setAttribute(Qt::WA_TranslucentBackground, true),

person Alexander V    schedule 19.12.2015
comment
Эм, нет. Я сказал, что виджет должен быть QFrame, поэтому нельзя установить такое растровое изображение. - person SexyBeast; 19.12.2015
comment
Также можно сделать углы рамки круглыми. - person Alexander V; 19.12.2015
comment
Только что понял: QLabel - это QFrame. Вы можете создать QLabel и присвоить его адрес QFrame*. - person Alexander V; 19.12.2015
comment
Ох, блин. Я никогда этого не знал. Я всегда думал, что QLabel наследуется от QWidget. Пытаюсь.. - person SexyBeast; 19.12.2015
comment
Странный. Я сделал свой виджет наследником QLabel вместо QFrame, и теперь код setPixmap(QPixmap("ipad.png")); setWindowFlags(Qt::FramelessWindowHint);. Удалены настройки таблиц стилей и все остальное, о чем идет речь. Теперь я просто получаю прямоугольный виджет с серым фоном! - person SexyBeast; 19.12.2015
comment
Мне любопытно, и я попробую через некоторое время. - person Alexander V; 19.12.2015
comment
Хорошо, это была ошибка форматирования. Изображение идет сейчас, но оно выглядит так же, как указано в вопросе - stackoverflow.com/questions/30965022/. А установка background-color: none; делает края черными, как указано в этом вопросе. В любом случае цветные края остаются. :( - person SexyBeast; 19.12.2015
comment
Проблема решена путем применения setAttribute(Qt::WA_TranslucentBackground, true); Теперь, я думаю, вам интересно, как сделать окно подвижным и т. д., но это отдельный вопрос. - person Alexander V; 19.12.2015
comment
Эм, не совсем. В вопросе четко указано, что использование Qt::WA_TranslucentBackground заставляет его выглядеть так, как ожидалось, но я не могу использовать его по причинам, которые я указал в вопросе, поэтому мне нужен другой способ сделать то же самое... - person SexyBeast; 19.12.2015
comment
На самом деле я столкнулся с аналогичной проблемой с WA_TranslucentBackground, и содержимое некоторых дочерних виджетов было просто черным. Проблема решена с помощью: setStyleSheet (цвет фона: rgba (255, 255, 255, 255);); Я обновил свой ответ. Просто немного не уверен, достаточно ли применить это к глобальной области видимости, но так и должно быть. - person Alexander V; 20.12.2015
comment
QWidget::paintEvent() - person Alexander V; 04.01.2016
comment
Ммм, да, paintEvent я уже пробовал в сочетании с WA_TranslucentBackground. Это то, из-за чего проблема начинается с. Как вы предлагаете заставить его работать с paintEvent без использования WA_TranslucentBackground? - person SexyBeast; 04.01.2016
comment
Хорошо, здесь опубликовано решение моей проблемы: stackoverflow.com/questions/30965022/. Вы хотите сказать, что я должен удалить настройку свойства WA_TranslucentBackground и вызвать setBackgroundMode с помощью Qt::NoBackground? - person SexyBeast; 04.01.2016
comment
Это то, что я имел в виду, и на ваш вопрос уже был дан ответ? Почему тогда вы спросили это снова? - person Alexander V; 04.01.2016
comment
Нет, точно не ответили. Сначала я спросил, как нарисовать изображение iPad в виде отдельного виджета, и узнал, что это можно сделать с помощью WA_TranslucentBackground. Затем, используя его, я обнаружил, что проблема OpenGL с WA_TranslucentBackground известна, поэтому мне нужно другое решение. Я задал вопрос в комментарии там, решение не было получено, поэтому, поскольку это известная проблема, я подумал, что это требует другого вопроса. - person SexyBeast; 05.01.2016
comment
Ага, понятно. Например, с окном QML, которое использует OpenGL для рисования, мы используем виджет-контейнер для его встраивания и избегаем проблем пересечения с остальными виджетами. Возможно, вы могли бы найти решение своей проблемы аналогичным образом. - person Alexander V; 05.01.2016
comment
Хорошо. Сейчас я пробую решение Qt::NoBackground. Вернусь с результатами.. - person SexyBeast; 05.01.2016
comment
Это не работает, как ожидалось. Я имею в виду, что вспомогательный виджет отображается правильно, но изображение iPad не является прозрачным, белый фон появляется по бокам, как показано на изображении, приведенном в вопросе. К вашему сведению, я не нашел перечисления Qt::NoBackground..поэтому я использую Qt::TransparentMode... - person SexyBeast; 05.01.2016