Search code examples
qtdraggableqgraphicsitem

Weird behaviour when dragging QGraphicsItem


I'm trying to create an arrow item in Qt, so I'm basing myself on this example: http://doc.qt.io/qt-5/qtwidgets-graphicsview-diagramscene-arrow-cpp.html

The only difference being that I want to connect an arrow to one item and not two. And it is all working except when I try to drag my item, it leaves a blurry trail behind as if it was painting the whole drag movement and if I alt tab it disapears.

Here's before I drag: enter image description here

And here's after: enter image description here

Only the arrow head is behaving like that, and I believe may be something wrong with its paint event:

void myArrow::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget){

qreal arrowSize = 20;
double angle = std::atan2(-line().dy(), line().dx());

QPointF arrowP1 = line().p1() + QPointF(sin(angle + M_PI / 3) * arrowSize,
                                cos(angle + M_PI / 3) * arrowSize);
QPointF arrowP2 = line().p1() + QPointF(sin(angle + M_PI - M_PI / 3) * arrowSize,
                                cos(angle + M_PI - M_PI / 3) * arrowSize);

arrowHead.clear();
arrowHead << line().p1() << arrowP1 << arrowP2;

painter->drawLine(line());
painter->drawPolygon(arrowHead);
}

If I take out the arrow head by removing this line painter->drawPolygon(arrowHead) the line itself works fine.

Here is the whole source project: https://github.com/Matheusih/0000000123

Any hints, please?


Solution

  • The problem is that the boundingRect of MyArrow only takes the QLineF as a base and not the arrow, that generates that undesired effect.

    My solution proposes to use a QGraphicsPathItem instead of a QGraphicsLineItem, besides I do not understand your logic implemented in moveLineToCenter, I see it as unnecessary, I have modified it to update its position:

    myarrow.h

    #ifndef MYARROW_H
    #define MYARROW_H
    
    class CustomRect;
    
    #include <QGraphicsPathItem>
    
    class MyArrow : public QGraphicsPathItem
    {
    public:
        MyArrow(CustomRect *rect);
        void updatePosition(QPointF p1, QPointF p2);
    private:
        CustomRect *myrect;
    };
    
    #endif // MYARROW_H
    

    myarrow.cpp

    #include "customrect.h"
    #include "myarrow.h"
    
    #include <QPainter>
    
    #include <cmath>
    
    MyArrow::MyArrow(CustomRect *rect){
        myrect = rect;
        QPointF p = rect->boundingRect().center();
        updatePosition(p - QPointF(0, 50), p - QPointF(0, 250));
        setFlag(QGraphicsLineItem::ItemIsSelectable);
    }
    
    void MyArrow::updatePosition(QPointF p1, QPointF p2)
    {
    
        const qreal arrowSize = 20;
    
        QPainterPath path;
    
        path.moveTo(p1);
        path.lineTo(p2);
    
        QPointF diff = p2-p1;
        double angle = std::atan2(-diff.y(), diff.x());
    
        QPointF arrowP1 = p1 + QPointF(sin(angle + M_PI / 3) * arrowSize,
                                        cos(angle + M_PI / 3) * arrowSize);
        QPointF arrowP2 = p1 + QPointF(sin(angle + M_PI - M_PI / 3) * arrowSize,
                                        cos(angle + M_PI - M_PI / 3) * arrowSize);
        QPolygonF arrowHead;
        arrowHead << p1 << arrowP1 << arrowP2 << p1;
        path.moveTo(p1);
        path.addPolygon(arrowHead);
    
        setPath(path);
    }
    

    customrect.h

    #ifndef CUSTOMRECT_H
    #define CUSTOMRECT_H
    
    #include <QGraphicsPixmapItem>
    
    class MyArrow;
    
    class CustomRect : public QGraphicsRectItem
    {
    public:
        CustomRect (const QRectF& rect);
        void addLine(MyArrow *line);
        QVariant itemChange(GraphicsItemChange change, const QVariant &value);
    
        void moveLineToCenter(QPointF newPos);
    
    private:
        QList<MyArrow *> arrows;
    };
    #endif // CUSTOMRECT_H
    

    customrect.cpp

    #include "customrect.h"
    #include "myarrow.h"
    
    CustomRect::CustomRect(const QRectF &rect) : QGraphicsRectItem(rect){
        setFlag(QGraphicsItem::ItemIsMovable);
        setFlag(QGraphicsItem::ItemSendsScenePositionChanges);
    }
    
    void CustomRect::addLine(MyArrow *line) {
        arrows << line;
    }
    
    QVariant CustomRect::itemChange(GraphicsItemChange change, const QVariant &value)
    {
        if (change == ItemPositionChange && scene()) {
            QPointF newPos = value.toPointF();
            moveLineToCenter(newPos);
        }
        return QGraphicsItem::itemChange(change, value);
    }
    
    void CustomRect::moveLineToCenter(QPointF newPos) {
    
        for(MyArrow *arrow: arrows) {
            arrow->setPos(newPos);
        }
    }
    

    The complete solution can be found in the following link