使用 QScrollArea 的视口修复背景渐变
Fix background-gradient with viewport for QScrollArea
请看下面的代码:
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()
目前渐变设置为background-brush随滚动条滚动。
我知道我可以将背景图像设置为 fixed with the viewport,但我该如何修正渐变?
我能想到的唯一办法就是将渐变放在一个单独的标签中,然后用QScrollArea覆盖它,但我希望有一个更简单的解决方案。
更准确地说:
我希望无论滚动条的位置如何,QScrollArea 的背景总是看起来像这样(旋转以保存 space):
也就是说,整个渐变应该始终可见,而不仅仅是它的一部分。
这不能使用调色板来完成,因为渐变背景绘制在“滚动”小部件上,而您想要使其适应视口的可见部分。另外,请注意,在小部件上设置调色板会自动将其设置为其子项(由于 属性 继承),因此除非您确定其结果,否则通常应避免这样做。
解决方案是覆盖滚动区域的 paintEvent 并将该渐变用于视口。为确保此功能正常工作,为滚动区域设置的小部件必须设置透明背景。
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;
<b>self.</b>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('<b>.QWidget</b> {background: transparent;}')
def paintEvent(self, event):
qp = QPainter(self.viewport())
qp.fillRect(self.viewport().rect(), self.color_gradient)
请看下面的代码:
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()
目前渐变设置为background-brush随滚动条滚动。
我知道我可以将背景图像设置为 fixed with the viewport,但我该如何修正渐变?
我能想到的唯一办法就是将渐变放在一个单独的标签中,然后用QScrollArea覆盖它,但我希望有一个更简单的解决方案。
更准确地说:
我希望无论滚动条的位置如何,QScrollArea 的背景总是看起来像这样(旋转以保存 space):
也就是说,整个渐变应该始终可见,而不仅仅是它的一部分。
这不能使用调色板来完成,因为渐变背景绘制在“滚动”小部件上,而您想要使其适应视口的可见部分。另外,请注意,在小部件上设置调色板会自动将其设置为其子项(由于 属性 继承),因此除非您确定其结果,否则通常应避免这样做。
解决方案是覆盖滚动区域的 paintEvent 并将该渐变用于视口。为确保此功能正常工作,为滚动区域设置的小部件必须设置透明背景。
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;
<b>self.</b>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('<b>.QWidget</b> {background: transparent;}')
def paintEvent(self, event):
qp = QPainter(self.viewport())
qp.fillRect(self.viewport().rect(), self.color_gradient)