У меня QGraphicsView
с QGraphicsScene
, и мне нужно нарисовать незамкнутые пути (которые могут содержать линии или кривые Безье) в ограниченной области scene
, определяемой 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
, нарисовав его в reversed
порядке. Но мой path
может содержать много строк / безеров, поэтому он неэффективен, и сглаживание выглядит плохо с удвоенными линиями.
Итак, вопрос в том, как обрезать незамкнутый путь или линию в QGraphicsScene без изменения логики функции генерации пути?
UPDATE
Теперь я использую следующую функцию:
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
Теперь это выглядит лучше, потому что сглаживание работает правильно.