I have an image like this:
I have a custom widget that reimplements paintEvent()
.
On top of the image I'd like to draw an overlay with one or more rectangles spared out. With rounded corners and proper anti-aliasing.
I'm guessing this might be possible with QPainter::CompositionMode. But how? Maybe draw the overlay into a QImage? Invert it?
In paint.net I drew a rectangle into a second layer, then selected the inside with the magic wand, and deleted it. I don't know how to do that with QPainter.
Working with composition modes is possible, but it requires complete understanding of their behavior (frankly, I was never able to wrap my head around them).
Also, when working with pixmaps, it's often necessary a further step, which is to draw one of the layers on a temporary "buffer" (a further QPixmap or QImage).
As long as the "masking" is simple, it doesn't involve alpha blending of multiple images, and the image is opaque (no transparency at all), we could just follow a simpler path, which is painting over and eventually "clear" the required areas by clipping the contents.
The procedure is actually easy:
setClipPath()
on the QPainter (with the Antialiasing
render hint set);I cannot write in C++, but the following simple example in Python should be clear enough:
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class ClipTest(QWidget):
def __init__(self):
super().__init__()
# This is the original image used in the question
self.pixmap = QPixmap('baseimage.png')
self.setFixedSize(self.pixmap.size())
def paintEvent(self, event):
qp = QPainter(self)
# draw the source image
qp.drawPixmap(0, 0, self.pixmap)
# draw the darker "shade" on the whole widget
qp.fillRect(self.rect(), QColor(32, 32, 32, 127))
path = QPainterPath()
# add a couple of rounded rects, which are also colliding
path.addRoundedRect(250, 110, 140, 140, 20, 20)
path.addRoundedRect(380, 110, 140, 140, 20, 20)
# to fill all enclosed regions, including those of colliding
# sub paths that may be otherwise be unpainted
path.setFillRule(Qt.WindingFill)
# clip the painting on the path above
qp.setClipPath(path)
# for proper antialiased clipping
qp.setRenderHint(qp.Antialiasing)
# redraw the pixmap again
qp.drawPixmap(0, 0, self.pixmap)
app = QApplication([])
win = Test()
win.show()
app.exec()
Which gives the following result: