在 QGraphicsItem 绘制方法中围绕所有 objects/shapes 绘制边框
Draw a border around all objects/shapes in a QGraphicsItem paint method
我想在 QGraphicsItem
Paint 方法中围绕所有 objects/shapes 绘制边框。 (绿色和红色圆圈是单独项目的一部分,因此在这种情况下它们不算数)
我目前正在绘图 RoundedRects
,但我正在寻找一种可扩展的解决方案,该解决方案还可以支持更复杂的对象(星星、香蕉等);对象相互重叠的情况。
我有一个项目,选中后会更改两个 RoundedRects
的边框颜色。
我想要一个解决方案,其中只有轮廓边框会改变颜色,而不是内部。
我认为可行的方法是使用 QGraphicsDropShadowEffect
或创建 QtCore.Qt.MaskOutColor
并以某种方式控制线宽。我之前通过放大重复的蒙版形状来完成此操作,但结果并不理想。所以我真的很想听听其他人的解决方案!
这是我的基础QGraphicsItem
。
class Node(QtWidgets.QGraphicsItem):
def __init__(self, scene, parent=None):
super(Node, self).__init__(parent)
scene.addItem(self)
# Variables
self.main_background_colour = QtGui.QColor(31, 176, 224)
self.title_background_colour = QtGui.QColor("#fffeb3")
self.name_background_colour = QtGui.QColor("#b8b64b")
self.brush = QtGui.QBrush(self.main_background_colour)
self.pen = QtGui.QPen(self.title_text_colour, 2)
self.main_rect = QtCore.QRectF(0, 0, 400, 200)
self.title_rect = QtCore.QRectF(self.main_rect.x() + (self.main_rect.width() * 0.05), self.main_rect.y() - 10, (self.main_rect.width() * 0.9), (self.main_rect.height() * 0.2))
self.name_rect = QtCore.QRectF(self.main_rect.x() + (self.main_rect.width() * 0.02), self.title_rect.bottom() - 10, (self.main_rect.width() * 0.96), (self.main_rect.height() * 0.3))
self.name_font_rect = QtCore.QRectF(self.name_rect.x() + (self.name_rect.width() * 0.05), self.name_rect.y() + 10, self.name_rect.width() * 0.9, self.name_rect.height() * 0.65)
# Flags
self.setFlag(self.ItemIsMovable, True)
self.setFlag(self.ItemSendsGeometryChanges, True)
self.setFlag(self.ItemIsSelectable, True)
self.setFlag(self.ItemIsFocusable, True)
self.setCacheMode(QtWidgets.QGraphicsItem.DeviceCoordinateCache)
def boundingRect(self):
return QtCore.QRectF(self.main_rect.x(), self.title_rect.y(), self.main_rect.width(), self.main_rect.height() + abs(self.main_rect.y() + self.title_rect.y()))
def paint(self, painter, option, widget=None):
# Border
if self.isSelected():
border_colour = QtGui.QColor(241, 175, 0)
else:
border_colour = self.main_background_colour.lighter()
self.pen.setColor(border_colour)
self.pen.setWidth(2)
painter.setPen(self.pen)
# Background
self.brush.setColor(self.main_background_colour)
painter.setBrush(self.brush)
painter.drawRoundedRect(self.main_rect, 4, 4)
# Name
self.brush.setColor(self.name_background_colour)
painter.setBrush(self.brush)
self.pen.setColor(QtGui.QColor("black"))
painter.setPen(self.pen)
painter.drawRoundedRect(self.name_rect, 4, 4)
# Tile
self.brush.setColor(self.title_background_colour)
painter.setBrush(self.brush)
self.pen.setColor(border_colour)
painter.setPen(self.pen)
painter.drawRoundedRect(self.title_rect, 4, 4)
更新
我已尝试使用 答案中所示的绘画方法,但它会单独在所有矩形周围绘制边框,而不是整体外部形状。
我的基本代码的其余部分是相同的,但我更改了绘画以匹配 回复。
def paint(self, painter, option, widget=None):
path = QtGui.QPainterPath()
path.setFillRule(QtCore.Qt.WindingFill)
for rect in (self.main_rect, self.name_rect, self.title_rect):
path.addRoundedRect(rect, 4, 4)
if self.isSelected():
border_colour = QtGui.QColor(241, 175, 0)
else:
border_colour = self.main_background_colour.lighter()
painter.setPen(QtGui.QPen(border_colour, 2))
painter.drawPath(path)
澄清一下,这是我要达到的边界:
最简单(但还不是最佳)的解决方案是基于所有矩形创建一个 QPainterPath,并绘制连接项目包含的所有形状的“最终”边框:
def paint(self, painter, option, widget=None):
# draw all items here, using default values
# ...
path = QtGui.QPainterPath()
path.setFillRule(Qt.WindingFill)
for rect in (self.main_rect, self.name_rect, self.title_rect):
path.addRoundedRect(rect, 4, 4)
if self.isSelected():
border_colour = QtGui.QColor(241, 175, 0)
else:
border_colour = self.main_background_colour.lighter()
painter.setPen(QtGui.QPen(border_colour, 2))
painter.drawPath(path.simplified())
不过请注意,绘制函数 非常 经常被调用。一定程度的缓存和现有的实现总是首选,特别是考虑到 python 是一个 巨大的 瓶颈:你应该总是尝试利用 C++ 实现,可能与现有的基础QGraphicsItem 形状。
例如,您可以使用 QGraphicsPathItem 并为每个项目设置预定义的 QGraphicsPath,而不是总是从 python 绘制三个圆角矩形。
因为你已经设置了 ItemSendsGeometryChanges
标志,你可以覆盖 itemChange()
以在需要时更新该路径,这样你就不需要为每个 paint()
打电话。
如果这些项目用于显示文本,则使用 QGraphicsPathItem 并将 QGraphicsSimpleTextItem 设置为它的子项。
或者,考虑将 QPicture 用作将用作“middle-cache”对象的 class/instance 变量:虽然它可能会在实现中增加一定程度的复杂性,它肯定会提高性能,尤其是在显示多个项目时。
我想在 QGraphicsItem
Paint 方法中围绕所有 objects/shapes 绘制边框。 (绿色和红色圆圈是单独项目的一部分,因此在这种情况下它们不算数)
我目前正在绘图 RoundedRects
,但我正在寻找一种可扩展的解决方案,该解决方案还可以支持更复杂的对象(星星、香蕉等);对象相互重叠的情况。
我有一个项目,选中后会更改两个 RoundedRects
的边框颜色。
我想要一个解决方案,其中只有轮廓边框会改变颜色,而不是内部。
我认为可行的方法是使用 QGraphicsDropShadowEffect
或创建 QtCore.Qt.MaskOutColor
并以某种方式控制线宽。我之前通过放大重复的蒙版形状来完成此操作,但结果并不理想。所以我真的很想听听其他人的解决方案!
这是我的基础QGraphicsItem
。
class Node(QtWidgets.QGraphicsItem):
def __init__(self, scene, parent=None):
super(Node, self).__init__(parent)
scene.addItem(self)
# Variables
self.main_background_colour = QtGui.QColor(31, 176, 224)
self.title_background_colour = QtGui.QColor("#fffeb3")
self.name_background_colour = QtGui.QColor("#b8b64b")
self.brush = QtGui.QBrush(self.main_background_colour)
self.pen = QtGui.QPen(self.title_text_colour, 2)
self.main_rect = QtCore.QRectF(0, 0, 400, 200)
self.title_rect = QtCore.QRectF(self.main_rect.x() + (self.main_rect.width() * 0.05), self.main_rect.y() - 10, (self.main_rect.width() * 0.9), (self.main_rect.height() * 0.2))
self.name_rect = QtCore.QRectF(self.main_rect.x() + (self.main_rect.width() * 0.02), self.title_rect.bottom() - 10, (self.main_rect.width() * 0.96), (self.main_rect.height() * 0.3))
self.name_font_rect = QtCore.QRectF(self.name_rect.x() + (self.name_rect.width() * 0.05), self.name_rect.y() + 10, self.name_rect.width() * 0.9, self.name_rect.height() * 0.65)
# Flags
self.setFlag(self.ItemIsMovable, True)
self.setFlag(self.ItemSendsGeometryChanges, True)
self.setFlag(self.ItemIsSelectable, True)
self.setFlag(self.ItemIsFocusable, True)
self.setCacheMode(QtWidgets.QGraphicsItem.DeviceCoordinateCache)
def boundingRect(self):
return QtCore.QRectF(self.main_rect.x(), self.title_rect.y(), self.main_rect.width(), self.main_rect.height() + abs(self.main_rect.y() + self.title_rect.y()))
def paint(self, painter, option, widget=None):
# Border
if self.isSelected():
border_colour = QtGui.QColor(241, 175, 0)
else:
border_colour = self.main_background_colour.lighter()
self.pen.setColor(border_colour)
self.pen.setWidth(2)
painter.setPen(self.pen)
# Background
self.brush.setColor(self.main_background_colour)
painter.setBrush(self.brush)
painter.drawRoundedRect(self.main_rect, 4, 4)
# Name
self.brush.setColor(self.name_background_colour)
painter.setBrush(self.brush)
self.pen.setColor(QtGui.QColor("black"))
painter.setPen(self.pen)
painter.drawRoundedRect(self.name_rect, 4, 4)
# Tile
self.brush.setColor(self.title_background_colour)
painter.setBrush(self.brush)
self.pen.setColor(border_colour)
painter.setPen(self.pen)
painter.drawRoundedRect(self.title_rect, 4, 4)
更新
我已尝试使用
我的基本代码的其余部分是相同的,但我更改了绘画以匹配
def paint(self, painter, option, widget=None):
path = QtGui.QPainterPath()
path.setFillRule(QtCore.Qt.WindingFill)
for rect in (self.main_rect, self.name_rect, self.title_rect):
path.addRoundedRect(rect, 4, 4)
if self.isSelected():
border_colour = QtGui.QColor(241, 175, 0)
else:
border_colour = self.main_background_colour.lighter()
painter.setPen(QtGui.QPen(border_colour, 2))
painter.drawPath(path)
澄清一下,这是我要达到的边界:
最简单(但还不是最佳)的解决方案是基于所有矩形创建一个 QPainterPath,并绘制连接项目包含的所有形状的“最终”边框:
def paint(self, painter, option, widget=None):
# draw all items here, using default values
# ...
path = QtGui.QPainterPath()
path.setFillRule(Qt.WindingFill)
for rect in (self.main_rect, self.name_rect, self.title_rect):
path.addRoundedRect(rect, 4, 4)
if self.isSelected():
border_colour = QtGui.QColor(241, 175, 0)
else:
border_colour = self.main_background_colour.lighter()
painter.setPen(QtGui.QPen(border_colour, 2))
painter.drawPath(path.simplified())
不过请注意,绘制函数 非常 经常被调用。一定程度的缓存和现有的实现总是首选,特别是考虑到 python 是一个 巨大的 瓶颈:你应该总是尝试利用 C++ 实现,可能与现有的基础QGraphicsItem 形状。
例如,您可以使用 QGraphicsPathItem 并为每个项目设置预定义的 QGraphicsPath,而不是总是从 python 绘制三个圆角矩形。
因为你已经设置了 ItemSendsGeometryChanges
标志,你可以覆盖 itemChange()
以在需要时更新该路径,这样你就不需要为每个 paint()
打电话。
如果这些项目用于显示文本,则使用 QGraphicsPathItem 并将 QGraphicsSimpleTextItem 设置为它的子项。
或者,考虑将 QPicture 用作将用作“middle-cache”对象的 class/instance 变量:虽然它可能会在实现中增加一定程度的复杂性,它肯定会提高性能,尤其是在显示多个项目时。