如何在Qt QGraphicsScene中裁剪未闭合路径
我有一个QGraphicsView
,里面包含了QGraphicsScene
,我需要在一个由rect
定义的有限区域内绘制不封闭的路径(这些路径可能包含直线或贝塞尔曲线)。
对于封闭的路径,有一个简单的方法可以使用QPainterPath.intersected(path)
函数来裁剪,但如果path
是不封闭的,intersected
会把它封闭(也就是从路径的末尾画一条线到起点)。
下面是一个简化的代码,展示了我的问题:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import functools
import sys
from PySide.QtCore import *
from PySide.QtGui import *
def path_through_points(points):
path = QPainterPath()
path.moveTo(*points[0])
for x, y in points[1:]:
path.lineTo(x, y)
return path
def path_through_points_workaround(points):
return path_through_points(points + list(reversed(points)))
if __name__ == "__main__":
app = QApplication(sys.argv)
scene = QGraphicsScene()
view = QGraphicsView(scene)
rect = QRectF(0, 0, 300, 300)
clip = QPainterPath()
clip.addRect(rect)
points = [(50, 50), (100, 100), (500, 300)]
def test_draw(path):
scene.clear()
scene.addRect(rect)
scene.addPath(path)
unclosed_path = path_through_points(points)
closed_path = path_through_points_workaround(points)
QTimer.singleShot(0, functools.partial(test_draw, unclosed_path))
QTimer.singleShot(2000, functools.partial(test_draw, unclosed_path.intersected(clip)))
QTimer.singleShot(4000, functools.partial(test_draw, closed_path.intersected(clip)))
view.resize(640, 480)
view.show()
sys.exit(app.exec_())
它绘制了生成的路径:
- 没有裁剪。
- 进行了裁剪(路径被封闭,而不是单纯裁剪)——这对我来说是不可接受的。
- 最后,我想要得到的结果(但这是通过变通方法实现的)。
变通方法:通过反向绘制来封闭path
。但是我的path
可能包含很多直线和贝塞尔曲线,所以这样做效率不高,而且双重线条的抗锯齿效果看起来很糟糕。
所以我的问题是,如何在不改变路径生成逻辑的情况下,在QGraphicsScene
中裁剪不封闭的路径或线条?
更新
现在我使用以下函数:
def clipped_path(path, min_x, min_y, max_x, max_y):
""" Returns clipped path, supports unclosed paths of any kind
(lines, beziers)
NOTE: Resulting path can loose antialiasing
"""
path.connectPath(path.toReversed())
clip = QPainterPath()
clip.addRect(QRectF(min_x, min_y, max_x, max_y))
return path.intersected(clip)
更新 2
更好的方法是@Hello W建议的:
class ClippedItemMixin(object):
def __init__(self, min_x, min_y, max_x, max_y):
self._clip_path = QtGui.QPainterPath()
self._clip_path.addRect(QtCore.QRectF(min_x, min_y, max_x, max_y))
super(ClippedItemMixin, self).__init__()
def paint(self, painter, *args, **kwargs):
painter.setClipPath(self._clip_path)
super(ClippedItemMixin, self).paint(painter, *args, **kwargs)
class ClippedPathItem(ClippedItemMixin, QtGui.QGraphicsPathItem):
pass
现在看起来更好,因为抗锯齿效果正常工作。
1 个回答
1
你可以试试从 QGraphicsPathItem
这个类继承一个新类,然后重新实现它的绘制方法。在这个方法里,你可以调用 painter.setClipRect()
来设置绘图区域。