Как обрезать незамкнутый путь в Qt QGraphicsScene - PullRequest
3 голосов
/ 06 января 2012

У меня 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_())

Рисует сгенерированный путь:

  1. Без отсечения.
  2. С отсечением (путь закрывается, а не просто клип) - для меня это неприемлемо.
  3. И, наконец, результаты, которые я хочу получить (но это достигается с помощью обходного пути).

Обходной путь: закройте 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

Теперь это выглядит лучше, потому что сглаживание работает правильно.

1 Ответ

1 голос
/ 08 мая 2012

Как насчет подклассов из QGraphicsPathItem, а затем переопределить метод рисования и вызвать painter.setClipRect()

...