QDialog with rounded corners have black corners instead of being translucent

淺唱寂寞╮ 提交于 2020-01-25 07:04:45

问题


I need to create a QDialog with rounded translucent corners. The problem is when doing so, the corners are translucent, but are somehow filled in by the Window's alpha property making it black (which is my understanding of the cause of the problem)

Clearly visible is the rounded edges, but with a black 'background' of sorts. To confirm, this Dialog is a modal dialog launched by a QThread from QMainWindow. It is not the parent window.

The CSS for the QDialog (root) component is:

QDialog {
    background-color: rgba(250, 250, 250, 255);
    border-radius: 30px;
    color: #3C3C3C;
}

with a Component Layout as shown below

I added a drop shadow effect on the QDialog too. This is the constructor code for the QDialog:

UpdateDialog::UpdateDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::UpdateDialog)
{
    ui->setupUi(this);

    setWindowFlags(Qt::FramelessWindowHint | Qt::Dialog);
    QGraphicsDropShadowEffect* effect = new QGraphicsDropShadowEffect();
    effect->setBlurRadius(5);
    this->setGraphicsEffect(effect);
}

Notably, the parent QMainWindow has the following attributes set in its constructor

ui->setupUi(this);
statusBar()->hide();
setWindowFlags(Qt::FramelessWindowHint);
setAttribute(Qt::WA_TranslucentBackground, true);

// Set rounded corners style
setStyleSheet("QMainWindow {\n  background-color:rgba(240,240,240,255);\n   border-radius: 30px;\n}\n\nQDialog {\n    border-radius: 30px;\n}");

// Add shadow to main window
QGraphicsDropShadowEffect* effect = new QGraphicsDropShadowEffect(ui->mainWindow);
effect->setBlurRadius(5);
effect->setOffset(4, 4);
this->setGraphicsEffect(effect);

How do I make this QDialog to have rounded translucent corners?


回答1:


UPDATE: Here is a much better version. The pixelated corners were bugging me, among other things. This one looks smooth as silk and can be styled with either CSS or C++. It does require subclassing the QWidget to be rounded (unlike the first version), but it's worth it. Again I'm using a QMessageBox as the base widget here for simplicity (no layouts/etc), but it would work with any QWidget (may need a Qt::Dialog window flag added).

The message box implementation:

#include <QtWidgets>

class RoundedMessageBox : public QMessageBox
{
        Q_OBJECT
    public:
        explicit RoundedMessageBox(QWidget *parent = nullptr) :
          QMessageBox(parent)
        {
            // The FramelessWindowHint flag and WA_TranslucentBackground attribute are vital.
            setWindowFlags(windowFlags() | Qt::FramelessWindowHint | Qt::WindowSystemMenuHint);
            setAttribute(Qt::WA_TranslucentBackground);
        }

        qreal radius = 0.0;        // desired radius in absolute pixels
        qreal borderWidth = -1.0;  // -1 : use style hint frame width;  0 : no border;  > 0 : use this width.

    protected:
        void paintEvent(QPaintEvent *) override
        {
            if (!(windowFlags() & Qt::FramelessWindowHint) && !testAttribute(Qt::WA_TranslucentBackground))
                return;  // nothing to do

            QPainter p(this);
            p.setRenderHint(QPainter::Antialiasing);

            // Have style sheet?
            if (testAttribute(Qt::WA_StyleSheetTarget)) {
                // Let QStylesheetStyle have its way with us.
                QStyleOption opt;
                opt.initFrom(this);
                style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
                p.end();
                return;
            }

            // Paint thyself.
            QRectF rect(QPointF(0, 0), size());
            // Check for a border size.
            qreal penWidth = borderWidth;
            if (penWidth < 0.0) {
                QStyleOption opt;
                opt.initFrom(this);
                penWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth, &opt, this);
            }
            // Got pen?
            if (penWidth > 0.0) {
                p.setPen(QPen(palette().brush(foregroundRole()), penWidth));
                // Ensure border fits inside the available space.
                const qreal dlta = penWidth * 0.5;
                rect.adjust(dlta, dlta, -dlta, -dlta);
            }
            else {
                // QPainter comes with a default 1px pen when initialized on a QWidget.
                p.setPen(Qt::NoPen);
            }
            // Set the brush from palette role.
            p.setBrush(palette().brush(backgroundRole()));
            // Got radius?  Otherwise draw a quicker rect.
            if (radius > 0.0)
                p.drawRoundedRect(rect, radius, radius, Qt::AbsoluteSize);
            else
                p.drawRect(rect);

            // C'est finí
            p.end();
        }
};

Example usage showing both CSS and C++ styling options:

int main(int argc, char *argv[])
{
    //QApplication::setStyle("Fusion");
    QApplication app(argc, argv);

    // Dialog setup
    RoundedMessageBox *msgBox = new RoundedMessageBox();
    msgBox->setAttribute(Qt::WA_DeleteOnClose);
    msgBox->setMinimumSize(300, 300);
    msgBox->setWindowTitle("Frameless window test");

    msgBox->setText("<h3>Frameless rounded message box.</h3>");
    msgBox->setInformativeText("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean fermentum erat rhoncus, "
                               "scelerisque eros ac, hendrerit metus. Nunc ac lorem id tortor porttitor mollis. Nunc "
                               "tristique orci vel risus convallis, non hendrerit sapien condimentum. Phasellus lorem tortor, "
                               "mollis luctus efficitur id, consequat eget nulla. Nam ac magna quis elit tristique hendrerit id "
                               "at erat. Integer id tortor elementum, dictum urna sed, tincidunt metus. Proin ultrices tempus "
                               "lacinia. Integer sit amet fringilla nunc.");

    if (1) {
        // Use QSS style
        app.setStyleSheet(QStringLiteral(
            "QDialog { "
                "border-radius: 12px; "
                "border: 3.5px solid; "
                "border-color: qlineargradient(x1: 1, y1: 1, x2: 0, y2: 0, stop: 0 #ffeb7f, stop: 1 #d09d1e); "
                "background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 1, stop: 0 #ffeb7f, stop: 1 #d09d1e); "
                "color: #003200; "
            "}"
        ));
    }
    else {
        // Use "native" styling
        msgBox->radius = 12.0;
        msgBox->borderWidth = 3.5;

        QLinearGradient bgGrad(0, 0, 1, 1);
        bgGrad.setCoordinateMode(QGradient::ObjectMode);
        bgGrad.setColorAt(0.0, QColor("gold").lighter());
        bgGrad.setColorAt(1.0, QColor("goldenrod").darker(105));
        QLinearGradient fgGrad(bgGrad);
        fgGrad.setStart(bgGrad.finalStop());
        fgGrad.setFinalStop(bgGrad.start());

        QPalette pal;
        pal.setBrush(QPalette::Window, QBrush(bgGrad));
        pal.setBrush(QPalette::Mid, QBrush(fgGrad));
        pal.setBrush(QPalette::WindowText, QColor("darkgreen").darker());
        msgBox->setPalette(pal);

        msgBox->setForegroundRole(QPalette::Mid);     // default is WindowText
        msgBox->setBackgroundRole(QPalette::Window);  // this is actually the default already
    }

    // Drop shadow doesn't work.
    //  QGraphicsDropShadowEffect* effect = new QGraphicsDropShadowEffect();
    //  effect->setBlurRadius(2);
    //  msgBox->setGraphicsEffect(effect);

    msgBox->show();
    return app.exec();
}


ORIGINAL: using a generic QMessageBox and setting a mask on it.

Turned out I could use a simple frameless dialog for an "about" message box in a new app... so here you go. This is the simplest method I can think of w/out re-implementing the widget painting process altogether (like in the Qt clock example). But there's clearly limits to this implementation, and I haven't tried it on a Mac yet.

Also, the drop shadow came in very handy... although you can't actually see it, it does a great job smoothing out the corners. Great idea, even if that wasn't the original intention. :)

#include <QtWidgets>

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

    // Dialog setup. Actually use a QMessageBox for a shorter example.
    QMessageBox *msgBox = new QMessageBox();
    msgBox->setAttribute(Qt::WA_DeleteOnClose);
    msgBox->setMinimumSize(300, 300);
    msgBox->setWindowTitle("Frameless window test");  // might still be visible eg. in a taskbar
    msgBox->setText("<h3>Frameless rounded message box.</h3>");
    msgBox->setInformativeText("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean fermentum erat rhoncus, "
                               "scelerisque eros ac, hendrerit metus. Nunc ac lorem id tortor porttitor mollis. Nunc "
                               "tristique orci vel risus convallis, non hendrerit sapien condimentum. Phasellus lorem tortor, "
                               "mollis luctus efficitur id, consequat eget nulla. Nam ac magna quis elit tristique hendrerit id "
                               "at erat. Integer id tortor elementum, dictum urna sed, tincidunt metus. Proin ultrices tempus "
                               "lacinia. Integer sit amet fringilla nunc.");

    // Here come the styling bits... First need the frameless window flag hint
    msgBox->setWindowFlags(msgBox->windowFlags() | Qt::FramelessWindowHint | Qt::WindowSystemMenuHint);
    // The desired border radius;
    const int radius = 12;
    // Style the box with CSS. Set the border radius here. 
    // The border style helps blend the corners, but could be omitted.
    // The background is optional... could add other styling here too.
    msgBox->setStyleSheet(QString(
        "QDialog { "
            "border-radius: %1px; "
            "border: 2px solid palette(shadow); "
            "background-color: palette(base); "
        "}"
    ).arg(radius));

    // The effect will not be actually visible outside the rounded window,
    // but it does help get rid of the pixelated rounded corners.
    QGraphicsDropShadowEffect* effect = new QGraphicsDropShadowEffect();
    // The color should match the border color set in CSS.
    effect->setColor(QApplication::palette().color(QPalette::Shadow));
    effect->setBlurRadius(5);
    msgBox->setGraphicsEffect(effect);

    // Need to show the box before we can get its proper dimensions.
    msgBox->show();

    // Here we draw the mask to cover the "cut off" corners, otherwise they show through.
    // The mask is sized based on the current window geometry. If the window were resizable (somehow)
    // then the mask would need to be set in resizeEvent().
    const QRect rect(QPoint(0,0), msgBox->geometry().size());
    QBitmap b(rect.size());
    b.fill(QColor(Qt::color0));
    QPainter painter(&b);
    painter.setRenderHint(QPainter::Antialiasing);
    painter.setBrush(Qt::color1);
    // this radius should match the CSS radius
    painter.drawRoundedRect(rect, radius, radius, Qt::AbsoluteSize);
    painter.end();
    msgBox->setMask(b);

    return app.exec();
}



来源:https://stackoverflow.com/questions/58145272/qdialog-with-rounded-corners-have-black-corners-instead-of-being-translucent

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!