Please have a look at the following code:
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
class MyScrollArea(QScrollArea):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
color_gradient = QLinearGradient(1, 0, 1, 1)
color_gradient.setSpread(QGradient.PadSpread)
color_gradient.setCoordinateMode(QGradient.ObjectMode)
color_gradient.setColorAt(0, QColor('#8000D3'))
color_gradient.setColorAt(0.5, QColor('#CB5CFF'))
color_gradient.setColorAt(1, QColor('#8000D3'))
palette = self.palette()
palette.setBrush(QPalette.Window, QBrush(color_gradient))
self.setPalette(palette)
# Set widget and layout
self.scroll_widget = QWidget()
self.layout = QVBoxLayout()
self.layout.setContentsMargins(0, 0, 0, 0)
self.layout.setSpacing(30)
self.scroll_widget.setLayout(self.layout)
self.setWidget(self.scroll_widget)
self.setWidgetResizable(True)
# Add Labels
for _ in range(40):
label = QLabel("test")
self.layout.addWidget(label)
if __name__ == '__main__':
app = QApplication([])
dialog = MyScrollArea()
dialog.show()
app.exec()
Currently, the gradient set as the background-brush scrolls with the scrollbar.
I know that I can set the background-image to be fixed with the viewport, but how can I fix the gradient instead?
The only option I can think of is to put the gradient in a separate label and overlay it with the QScrollArea, but I hope there is a simpler solution.
To be more precise:
I want that regardless of the position of the scrollbar, the background of the QScrollArea always looks like this (rotated to save space):
That is the whole gradient should always be visible, and not just parts of it.
This cannot be done using the palette, because the gradient background is painted on the "scrolling" widget, while you want to adapt it to the visible part of the viewport. Also, be aware that setting a palette on a widget automatically sets it to its children (due to property inheritance), so you should generally avoid that unless you are sure about its result.
The solution is to override the paintEvent of the scroll area and use that gradient for the viewport. In order to ensure that this works properly, the widget set for the scroll area must have a transparent background set.
class MyScrollArea(QScrollArea):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# note that I've made the gradient an attribute of the instance, so it can
# be used in the paint event without recreating it everytime;
self.color_gradient = QLinearGradient(1, 0, 1, 1)
self.color_gradient.setSpread(QGradient.PadSpread)
self.color_gradient.setCoordinateMode(QGradient.StretchToDeviceMode)
self.color_gradient.setColorAt(0, QColor('#8000D3'))
self.color_gradient.setColorAt(0.5, QColor('#CB5CFF'))
self.color_gradient.setColorAt(1, QColor('#8000D3'))
# ...
# set the transparent background **only** for the container widget; note
# the period before QWidget;
self.scroll_widget.setStyleSheet('.QWidget {background: transparent;}')
def paintEvent(self, event):
qp = QPainter(self.viewport())
qp.fillRect(self.viewport().rect(), self.color_gradient)