Issue
I have QGraphicsView
with QGraphicsScene
and I need to draw unclosed paths (which may contain lines, or Bezier curves) in limited area of scene
defined by rect
.
There is simple way to clip closed path by using QPainterPath.intersected(path)
function, but if path
is unclosed, then intersected
closes it (adds line from end to start point).
Here is the simplified code that illustrates my problem:
#!/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_())
It draws generated path:
- Without clipping.
- With clipping (path closes instead of just clip) - this is unacceptable for me.
- And finally, results that I want to get (but it's reached with workaround).
Workaround: close path
by drawing it in reversed
order. But my path
may contain many lines/beziers, so it's ineffective, and anti aliasing looks bad with doubled lines.
So question is how to clip unclosed path or line in QGraphicsScene without changing logic of path generation function?
UPDATE
Now I'm using following function:
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)
UPDATE 2
Better method as @Hello W suggested:
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
Now it looks nicer, because anti aliasing works right.
Solution
How about subclassing from QGraphicsPathItem
and then reimplementing the paint method of it and calling painter.setClipRect()
Answered By - Hello W
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.