Search code examples
pythonpyside2

QPaintEvent event rect for scrollable widget


I have a QPaintEvent override for a custom widget that has a fixed size set. This fixed size can change per instance but in this simple example, ive set it. however the PaintEvent doesn't take it into account so when the users scrolls to the right the rectangle shouldn't paint rounded corners since the widget extends past the visible viewport. How do i fix this?

Full widget painted correctly...

enter image description here

When i resize dialog and scroll right, you'll see rounded corners appear on the left side... when it should NOT.

enter image description here

They should look like this...

enter image description here

Code

import os
import sys
from PySide2 import  QtGui, QtWidgets, QtCore, QtSvg


class Card(QtWidgets.QWidget):

    def __init__(self, parent=None):
        super(Card, self).__init__(parent=parent)

        self.label = QtWidgets.QLabel('Help This Paint Event Is Broken')
        self.label.setFixedHeight(40)
        self.label.setFixedWidth(300)

        self.mainLayout = QtWidgets.QVBoxLayout(self)
        self.mainLayout.addWidget(self.label)

    # overrides
    def paintEvent(self, event):
        painter = QtGui.QPainter() 
        painter.begin(self) 
        painter.setOpacity(1.0)
        painter.setRenderHints(QtGui.QPainter.Antialiasing)
        painter.setPen(QtGui.QColor(0, 0, 0, 128))
        painter.setPen(QtCore.Qt.NoPen)
        painter.setBrush(QtGui.QColor('#F44336'))
        painter.drawRoundedRect(event.rect(), 12, 12)
        painter.setRenderHint(QtGui.QPainter.Antialiasing) 
        painter.end() 


class ListViewExample(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(ListViewExample, self).__init__(parent)
        self.resize(200,200)

        self.listView = QtWidgets.QListWidget()
        self.listView.setSpacing(10)
        self.listView.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
        self.listView.verticalScrollBar().setSingleStep(10)

        # layout
        self.mainLayout = QtWidgets.QVBoxLayout()
        self.mainLayout.setContentsMargins(0,0,0,0)
        self.mainLayout.addWidget(self.listView)
        self.setLayout(self.mainLayout)

        for x in range(50):
            wgt = Card()
            self.appendItem(wgt)

    def appendItem(self, widget):
        lwi = QtWidgets.QListWidgetItem()
        lwi.setSizeHint(widget.sizeHint())
        self.listView.addItem(lwi)
        self.listView.setItemWidget(lwi, widget) 


################################################################################
# Widgets
################################################################################
def unitTest_CardDelegate():
    app = QtWidgets.QApplication(sys.argv)
    window = ListViewExample()
    window.show()
    app.exec_()


if __name__ == '__main__':
    pass
    unitTest_CardDelegate()

Solution

  • QPaintEvent::rect() returns the visible rectangle, not the rectangle of the widget itself, so you observe this behavior. The solution is:

    painter.drawRoundedRect(self.rect(), 12, 12)