Search code examples
c++qtqt5qgraphicsitemqgraphicspathitem

QGraphicsPathItem between two movable QGraphicsRectItem


I'm trying to follow this example and eyllanesc's answer here Weird behaviour when dragging QGraphicsItem to draw a line ending with an arrow between two draggable/movable QGraphicsRectItems

Here is my code:

The customized QGraphicsPathItemclass (inherits from QObject also because I use signals):

wireArrow.h


public slots:
    void changePosStart(QPointF newpos);
    void changePosEnd(QPointF newpos);

private:
    component *myStartItem;
    component *myEndItem;
    QPolygonF arrowHead;
    QColor myColor = Qt::black;

WireArrow.cpp

WireArrow::WireArrow(component *startItem, component *endItem,
                                  QGraphicsItem *parent )
            : QGraphicsPathItem(parent), myStartItem(startItem), myEndItem(endItem)

{

    connect(startItem,SIGNAL(componentPosChanged(QPointF)),this, SLOT(changePosStart(QPointF))   );
);
    connect(endItem,SIGNAL(componentPosChanged(QPointF)),this,SLOT(changePosEnd(QPointF)) );;

    this->setPos(myStartItem->pos());
    setFlag(ItemIsSelectable);
    setPen(QPen(myColor, 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));

//    QPainterPath path;
//    path.quadTo(100,0,100,100);

    QPainterPath rectPath;
        rectPath.moveTo(0.0, 0.0);
        rectPath.lineTo(myStartItem->pos().x()-myEndItem->pos().x() , myStartItem->pos().y()-myEndItem->pos().y());



        this->setPath(rectPath);
}

void WireArrow::changePosStart(QPointF newpos)
{//@to-do: find how to update pos setpos + this->update maybe? }

void WireArrow::changePosEnd(QPointF newpos)
{//@to-do: find how to update the end pos}

The customized qgraphicsitem class (also inherits from QObject to emit signal on position update):

component::component(/*some irrelevant params*/QGraphicsItem*parent  ):
QGraphicsRectItem(parent), //...init other params
    {
        setRect(-40, -40, 80, 80);
        setFlag(ItemIsMovable);
        setFlag(ItemIsSelectable);
        setFlag(QGraphicsItem::ItemSendsScenePositionChanges);

    }

QVariant component::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value)
{

    if (change == ItemPositionChange && scene()) {
        qDebug() << "cmp:: event change on scene";
        QPointF newPos = value.toPointF();
        emit componentPosChanged(newPos);
    }
    return QGraphicsItem::itemChange(change, value);

}


main

 // create two components out and in
WireArrow * wa = new WireArrow(out,in);
scene->addItem(wa);

I can create the rectangles (components) and move them just fine (thanks to the answer here , my problem is I can draw lines orignation from out but :

  1. I can't draw from one component to another correctly.
  2. I need to make them move automatically when dragging rectangles. In other examples, I saw they were treating the lines as an attribute of the item, which is something I can't do here since the wire is for two items and not one so I replaced that with signal/slot connection but I still can't figure out how to update the position...

An image explaining what I'm trying to do

enter image description here

(squares are really made with qt, but the line is made in paint, the squares are movable ).

So in short: Just trying to make a line between two points(the position of the two squares) and the squares are movable so the line should adapt if one of the the 2 squares is moved.


Solution

  • What is required is very similar to what I implement in this answer so I will limit the code translated from to :

    component.h

    #ifndef COMPONENT_H
    #define COMPONENT_H
    
    #include <QGraphicsRectItem>
    
    class Arrow;
    
    class Component : public QGraphicsRectItem
    {
    public:
        Component(QGraphicsItem *parent = nullptr);
        void addArrow(Arrow *arrow);
    protected:
        QVariant itemChange(GraphicsItemChange change, const QVariant &value);
    private:
        QVector<Arrow *> mArrows;
    };
    
    #endif // COMPONENT_H
    

    component.cpp

    #include "arrow.h"
    #include "component.h"
    
    Component::Component(QGraphicsItem *parent):
        QGraphicsRectItem(parent)
    {
        setRect(-40, -40, 80, 80);
        setFlags(QGraphicsItem::ItemIsMovable |
                 QGraphicsItem::ItemIsSelectable |
                 QGraphicsItem::ItemSendsGeometryChanges);
    }
    
    void Component::addArrow(Arrow *arrow)
    {
        mArrows << arrow;
    }
    
    QVariant Component::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value)
    {
        if(change == QGraphicsItem::ItemPositionHasChanged){
            for(Arrow * arrow: qAsConst(mArrows)){
                arrow->adjust();
            }
        }
        return QGraphicsRectItem::itemChange(change, value);
    }
    

    arrow.h

    #ifndef ARROW_H
    #define ARROW_H
    
    #include <QGraphicsPathItem>
    
    class Component;
    
    class Arrow : public QGraphicsPathItem
    {
    public:
        Arrow(Component *startItem, Component *endItem, QGraphicsItem *parent = nullptr);
        void adjust();
    private:
        Component *mStartItem;
        Component *mEndItem;
    };
    
    #endif // ARROW_H
    

    arrow.cpp

    #include "arrow.h"
    #include "component.h"
    
    #include <QPen>
    
    Arrow::Arrow(Component *startItem, Component *endItem, QGraphicsItem *parent):
        QGraphicsPathItem(parent), mStartItem(startItem), mEndItem(endItem)
    {
        mStartItem->addArrow(this);
        mEndItem->addArrow(this);
        setPen(QPen(QColor("red"), 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
        adjust();
    }
    
    void Arrow::adjust()
    {
        prepareGeometryChange();
        QPainterPath path;
        path.moveTo(mStartItem->pos());
        path.lineTo(mEndItem->pos());
        setPath(path);
    }
    
    Component *comp1 = new Component;
    Component *comp2 = new Component;
    
    comp1->setPos(50, 50);
    comp2->setPos(400, 400);
    
    Arrow *arrow = new Arrow(comp1, comp2);
    scene->addItem(comp1);
    scene->addItem(comp2);
    scene->addItem(arrow);