我正在尝试在 QGraphicsScene 内将圆(QGraphicsEllipseItem)着色为 25% 蓝色、25% 透明、25% 红色、25% 透明。我正在努力这样做。我尝试做这样的事情:
class CircleItem(QGraphicsEllipseItem):
def __init__(self, x=0, y=0, radius=0):
super(CircleItem, self).__init__(x - radius, y - radius, radius * 2, radius * 2)
self.setPen(QPen(Qt.red, 2))
gradient = QRadialGradient(self.boundingRect().center(), radius)
gradient.setColorAt(0, Qt.blue)
gradient.setColorAt(0.25, Qt.transparent)
gradient.setColorAt(0.5, Qt.red)
gradient.setColorAt(0.75, Qt.transparent)
self.setBrush(QBrush(gradient))
但它并没有真正填充任何东西,有没有办法按照我想要的方式画圆圈?就像图片一样
您使用了错误的渐变类型。
QRadialGradient 使用不同的 radius 值传播渐变,如 QGradient 文档中所示:
你需要的是一个QConicalGradient:
然后,即使使用正确的渐变类型,它也不会按照您想要的给定值显示圆的“部分”:您使用的是 gradient,因此它的颜色在其 stops 之间逐渐变化。
angle
值),如下所示:
gradient = QConicalGradient(self.boundingRect().center(), 0)
gradient.setColorAt(0, Qt.blue)
gradient.setColorAt(0.25, Qt.transparent)
gradient.setColorAt(0.5, Qt.red)
gradient.setColorAt(0.75, Qt.transparent)
结果仍然不合适,这是因为渐变使用颜色插值,这是渐变的主要目的:
一个可能的解决方案是在颜色变化旁边使用窄值,例如:
gradient = QConicalGradient(self.boundingRect().center(), 0)
gradient.setColorAt(0, Qt.blue)
gradient.setColorAt(0.2499, Qt.blue)
gradient.setColorAt(0.25, Qt.transparent)
gradient.setColorAt(0.4999, Qt.transparent)
gradient.setColorAt(0.5, Qt.red)
gradient.setColorAt(0.7499, Qt.red)
gradient.setColorAt(0.75, Qt.transparent)
这要好得多,因为几乎没有插值:
仍然不够。即使停靠点之间的差异很小,在更高的分辨率下也可能会出现问题:
注意顶部的小工件。如果渐变具有不同的角度并进行以下更改,这一点会更加明显:
gradient = QConicalGradient(self.boundingRect().center(), 15)
注意
15
值,该值会将渐变逆时针旋转 15°。
即使使用抗锯齿功能,渐变仍然存在锯齿:
请注意,设置项目的旋转不会解决问题。
只有通过自定义绘画才能获得更好的视觉效果,这涉及到 QGraphicsEllipseItem 子类,该子类在内部设置“切片”并自行覆盖绘画。
这种方法可能看起来有点复杂,但它也是正确显示所必需的,并且允许进一步的可扩展性(例如多种颜色或每种颜色的不同范围)。
class SmoothCircleItem(QGraphicsEllipseItem):
_colors = None
_colorAngle = 0
_colorRanges = None
def __init__(self, x=0, y=0, radius=1, colors=None, angle=0):
super().__init__(x - radius, y - radius, radius * 2, radius * 2)
self.setPen(QPen(Qt.red, 2))
if colors is None:
colors = (Qt.blue, Qt.transparent, Qt.red, Qt.transparent)
self.setColors(colors, angle)
def setColors(self, colors, angle=None):
# acceptable values for "colors" are lists or tuples of:
# - QColor, Qt.GlobalColor (or their int values)
# - list/tuples of [color, position]
if isinstance(colors[0], (QColor, Qt.GlobalColor, int)):
# accept an arbitrary list of colors that splits the ellipse
# assigning identical parts to each color
_colors = []
ratio = 1 / len(colors)
pos = 0
for c in colors:
if not isinstance(c, QColor):
c = QColor(c)
_colors.append((c, pos))
pos += ratio
colors = _colors
else:
# accept iterables in the form of [color, pos], with positions
# in real ranges between 0.0 and 1.0
colors = sorted(
[(c, p % 1.) for c, p in colors],
key=lambda cd: cd[1]
)
# create an internal list of colors in the following form:
# [color, start, extent]
first = colors[0]
last = [first[0], first[1] % 1.]
self._colors = [last]
for color, pos in colors[1:]:
last.append((pos - last[-1]))
colorData = [color, pos]
self._colors.append(colorData)
last = colorData
if len(colors) > 1:
last.append(1. + first[1] - last[-1])
else:
last.append(1.)
if self._colorAngle == angle:
self._update()
else:
self.setColorAngle(angle)
def setColorAngle(self, angle):
angle = angle % 360
if self._colorAngle != angle:
self._colorAngle = angle
self._update()
def _update(self):
if self._colorAngle:
# adjust the start values based on the angle
realAngle = self._colorAngle / 360
values = [(c, (s + realAngle) % 1, e) for c, s, e in self._colors]
else:
values = self._colors
# update angles using QPainter angles, which are in sixteenths
self._colorRanges = [
(c, int(s * 5760), int(e * 5760)) for c, s, e in values
]
self.update()
def paint(self, qp, opt, widget=None):
rect = self.rect()
qp.save()
qp.setPen(Qt.NoPen)
for color, start, extent in self._colorRanges:
qp.setBrush(color)
qp.drawPie(rect, start, extent)
qp.restore()
super().paint(qp, opt, widget)
从下面的比较中,视觉差异非常明显(第一个是基于 QGradient 的项目,第二个是上面具有相似值的
SmoothCircleItem
,而第三个显示了具有不同颜色数据的可能扩展: