How can I implement a style sheet rounded border in a paintEvent?
-
Hi all,
I'm working on a custom
QPushButton
. Currently, I set a style sheet:setStyleSheet(QStringLiteral("QPushButton { border: 1px solid palette(highlight); " "border-radius: 4px; }"));
which makes the button appear e.g. like so:
Now, I want to do this exactly in a custom
paintEvent
. I tried the following:painter.setPen(QPen(palette().color(QPalette::Highlight), 1)); painter.drawRoundedRect(rect().adjusted(0, 0, -1, -1), 4, 4);
which gives me
The corners are not rounded in the same way, and they are not smooth like in the style sheet version. So I tried to enable anti-aliasing:
painter.setRenderHint(QPainter::Antialiasing, true);
which then causes the button to be rendered like this:
still very different from the style sheet version.
So: How can I get the exact same result like when setting a style sheet?
Thanks for all help in advance!
-
It actually can be achieved way easier. Shamelessly stolen from Breeze's
drawFocusFrame
method and a lot simplified:void PopupButton::paintEvent(QPaintEvent *) { static const int margin = 1; static const int radius = 5; QPainterPath painterPath; painterPath.setFillRule(Qt::OddEvenFill); painterPath.addRoundedRect(rect().adjusted(margin, margin, -margin, -margin), radius, radius); painterPath.addRoundedRect(rect(), radius + margin, radius + margin); QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.fillPath(painterPath, palette().color(QPalette::Highlight)); }
Result:
Of course lacks the text, hover indicator and all that – but the border looks fine.
-
Hi,
The most simple would be to read the style sheet's QStyle implementation in Qt's source code.
-
@l3u_ It is in
QRenderRule::drawBorder
of Src/qtbase/src/widgets/styles/qstylesheetstyle.cpp andqDrawBorder
of Src/qtbase/src/gui/painting/qcssutil.cpp.
From theqDrawBorder
source you can see that the edges and corners are drawn separately.
An easier way is to call this function directly, but this need to add the private header module to your project.
If you are using qmake, just addQT += gui-private
to pro file. If using cmake you can google :).
Then in the code#include <private/qcssutil_p.h> void Form::paintEvent(QPaintEvent *) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing, true); const QVector<QCss::BorderStyle> styles(4, QCss::BorderStyle_Solid); const QVector<int> borders(4, 1); const QVector<QBrush> colors(4, palette().color(QPalette::Highlight)); const QVector<QSize> radii(4, {4, 4}); qDrawBorder(&painter, rect().adjusted(0, 0, -1, -1), styles.data(), borders.data(), colors.data(), radii.data()); }
-
Simply look in the code of this function. It's open source...
-
@l3u_ Then you already know where the source code is. You can check by yourself.
P.S. I won't worry much about the private header.
First, the APIs in this header file haven't been changed from Qt5.0 till current Qt6.9.
Second, even the public headers have also been changed a lot, so what if the private header changes . -
It actually can be achieved way easier. Shamelessly stolen from Breeze's
drawFocusFrame
method and a lot simplified:void PopupButton::paintEvent(QPaintEvent *) { static const int margin = 1; static const int radius = 5; QPainterPath painterPath; painterPath.setFillRule(Qt::OddEvenFill); painterPath.addRoundedRect(rect().adjusted(margin, margin, -margin, -margin), radius, radius); painterPath.addRoundedRect(rect(), radius + margin, radius + margin); QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.fillPath(painterPath, palette().color(QPalette::Highlight)); }
Result:
Of course lacks the text, hover indicator and all that – but the border looks fine.
-
L l3u_ has marked this topic as solved