33

I want to draw a rectangle with rounded corners (border radius same for all 4 corners) with a specific color filling the entire rectangle, and a separate border color (say border is 1 px wide).

From my observation, Qt provides three methods - fillRect and drawRect and drawRoundedRect. I have tried them, they don't work like I want to. There is no method like fillRoundedRect. Which means that I can draw a rounded rectangle but it won't be filled with the color I want.

How do I do it? And also, I read that due to some aliasing problems, the corners are often rendered as unequal. How do I set it as equal for all four? Will painter.setRenderHint(QPainter::Antialiasing) suffice? Or do I have to do anything else?

SexyBeast
  • 7,913
  • 28
  • 108
  • 196

2 Answers2

77

You can create a QPainterPath, add the rounded rect to it, and then fill and stroke it:

QPainter p(this);
p.setRenderHint(QPainter::Antialiasing);
QPainterPath path;
path.addRoundedRect(QRectF(10, 10, 100, 50), 10, 10);
QPen pen(Qt::black, 10);
p.setPen(pen);
p.fillPath(path, Qt::red);
p.drawPath(path);

Note that even with antialiasing, 1 px border will probably never really look good, especially on a low DPI desktop monitor, on a high DPI mobile device it will be almost invisible.

enter image description here

If you create the rectangle as QRectF(9.5, 9.5, 100, 50) it will look better with 1 px antialiased border, because it will "snap" on the right pixel:

enter image description here

dtech
  • 47,916
  • 17
  • 112
  • 190
  • Works like a charm! However, I need a thin border, say of 1 px width. When I change the width, the border looks kind of rough and jagged at the corners. Even with the anti-aliasing. Any solution for that? – SexyBeast Mar 22 '15 at 16:28
  • @hkBattousai - if you paint on a widget, then in its paint event, if you paint on another paint device it doesn't matter. – dtech Jan 05 '17 at 15:13
2

The answer above (from @dtech) works great, but can sometimes end up with an uneven border around the roundedRect. Using QPainter.strokePath() instead of QPainter.drawPath() can fix this issue.

Here is a python implementation of QPushButton, with paintEvent reimplemented:

# I use PySide6, but whatever library should work.
from PySide6.QtWidgets import QPushButton
from PySide6.QtGui import QPainter, QPainterPath, QBrush, QPen
from PySide6.QtCore import Qt, QRectF

class RoundedButton(QPushButton):
    def __init__(self, text, bordersize, outlineColor, fillColor):
        super(RoundedButton, self).__init__()
        self.bordersize = bordersize
        self.outlineColor = outlineColor
        self.fillColor = fillColor
        self.setText(text)

    def paintEvent(self, event):
        # Create the painter
        painter = QPainter(self)
        painter.setRenderHint(QPainter.Antialiasing)
        # Create the path
        path = QPainterPath()
        # Set painter colors to given values.
        pen = QPen(self.outlineColor, self.bordersize)
        painter.setPen(pen)
        brush = QBrush(self.fillColor)
        painter.setBrush(brush)

        rect = QRectF(event.rect())
        # Slighly shrink dimensions to account for bordersize.
        rect.adjust(self.bordersize/2, self.bordersize/2, -self.bordersize/2, -self.bordersize/2)

        # Add the rect to path.
        path.addRoundedRect(rect, 10, 10)
        painter.setClipPath(path)

        # Fill shape, draw the border and center the text.
        painter.fillPath(path, painter.brush())
        painter.strokePath(path, painter.pen())
        painter.drawText(rect, Qt.AlignCenter, self.text())
Umbral Reaper
  • 325
  • 4
  • 9
  • 1
    This question is specifically tagged as a C++ question. Please avoid adding answers in other languages, as it adds noise to the answers and can cause confusion for future readers. – Hoppeduppeanut Feb 26 '21 at 04:34
  • 6
    @UmbralReaper The case of Qt is special since there are bindings in many languages but with a lot of activity in python, so an example helps a lot. This is not a question about C++ itself but about Qt technology. – eyllanesc Mar 03 '21 at 05:33
  • @eyllanesc I'll keep that in mind when I answer other Qt questions. – Umbral Reaper Mar 03 '21 at 05:46
  • @UmbralReaper change `rect = QRectF( ...` to `rect.adjust(bordersize/2, bordersize/2, -bordersize, -bordersize)` – eyllanesc Mar 03 '21 at 05:49