qtqpainterpath

how can I achieve the implementation effect like revit by use QPainterPath in Qt?


I want to implement the effect that one path through another, the two path break off and they still two independence path.

When I use the method according to my code. ( I draw two path using QPainterPath object and put them into QGraphicsPathItem like first picture. red one is pRect1 and blue one is pRect2. Then I use simplified() function to union two path, the result is green one).

What should I do to achieve my purpose? should I use the method in QPainterPath or I must write my own algorithm? Which algorithm should I consider?

Could you please give me some suggestions what should I do to achieve the implementation effect like revit ( Like the second picture, one path through another, they still independence path).

Thank you very much.

    QGraphicsView* viewer = new QGraphicsView(this);
    this->setCentralWidget(viewer);
    viewer->show();
    QGraphicsScene* scene = new QGraphicsScene(viewer);
    viewer->setScene(scene);
    QGraphicsPathItem* item1 = new QGraphicsPathItem;
    QGraphicsPathItem* item2 = new QGraphicsPathItem;
    QGraphicsPathItem* item3 = new QGraphicsPathItem;

    scene->setSceneRect(QRect(0,0, viewer->width(), viewer->height()));

    const int kb = 80;
    const int offset = kb/10;
    QRectF br(0, 0, kb*2, offset);

    QPainterPath pRect1;
    pRect1.moveTo(br.left(), br.top());
    pRect1.lineTo(br.right(), br.top());
    pRect1.lineTo(br.right(), br.bottom());
    pRect1.lineTo(br.left(), br.bottom());
    pRect1.lineTo(br.left(), br.top());

    QPainterPath pRect2;
    pRect2.moveTo(br.left() + br.width()/2.0 - offset, br.top() - br.width());
    pRect2.lineTo(br.left() + br.width()/2.0 + offset, br.top() - br.width());
    pRect2.lineTo(br.left() + br.width()/2.0 + offset, br.bottom() + br.width());
    pRect2.lineTo(br.left() + br.width()/2.0 - offset, br.bottom() + br.width());
    pRect2.lineTo(br.left() + br.width()/2.0 - offset, br.top() - br.width());

    QPainterPath shape = pRect1.united(pRect2);
    shape.simplified();

    item1->setPen(QPen(QColor(255,0,0)));
    item1->setPos(0,50);
    item1->setPath(pRect1);

    item2->setPen(QPen(QColor(0,0,255)));
    item2->setPos(0,50);
    item2->setPath(pRect2);


    item3->setPen(QPen(QColor(0,255,0)));
    item3->setPos(250,50);
    item3->setPath(shape);

    scene->addItem(item1);
    scene->addItem(item2);
    scene->addItem(item3);

enter image description here enter image description here


Solution

  • This is not possible with standard graphics items, but you can do this by overriding the paint() in a QGraphicsPathItem subclass, and subtract the shape of the colliding items from the clip path.

    The following is a simple implementation in PyQt/PySide, but porting in C++ should be trivial. It clips overlapping parts of each colliding path as long as they are of the same item type.

    Screenshot of the example code

    class CollidingItem(QGraphicsPathItem):
        def __init__(self):
            super().__init__()
            path = QPainterPath()
            path.addRect(QRectF(-200, -25, 400, 50))
            self.setPath(path)
    
        def paint(self, qp, opt, widget=None):
            shape = self.shape()
            for item in self.collidingItems():
                if isinstance(item, CollidingItem):
                    shape -= self.mapFromItem(item, item.path())
            qp.save()
            qp.setClipPath(shape)
            super().paint(qp, opt, widget)
            qp.restore()
    
    app = QApplication(sys.argv)
    scene = QGraphicsScene()
    
    item1 = CollidingItem()
    item1.setPen(QPen(Qt.red, 2))
    item1.setBrush(QColor('aqua'))
    
    item2 = CollidingItem()
    item2.setPen(QPen(Qt.blue, 2))
    item2.setRotation(45)
    
    scene.addItem(item1)
    scene.addItem(item2)
    
    window = QGraphicsView(scene)
    window.setRenderHint(QPainter.Antialiasing)
    window.show()
    
    app.exec()