1

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.

1 Answers1

2

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);
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • I'm going to give it a try right now, but just to know, is there anything wrong with using slot/singal connection to call adjust instead of having arrows as an attribute of the component? – aNormalPerson Jan 10 '21 at 00:53
  • @aNormalPerson No, there is nothing wrong, but as indicated in my answer I have only translated my code – eyllanesc Jan 10 '21 at 00:56
  • I kind of tried similar code, I thought I was doing something wrong because I was getting segfault, but your code give a segfault too... The segfault appeared whenever `rectPath.lineTo(myEndItem->pos());` was called. so when you posted an answer close to what I had I figured the issue might be my testing code not the drawing itself... Thanks it helped! – aNormalPerson Jan 10 '21 at 01:40
  • your code works, however one small issue remains... Is there a way to prevent aliasing (some anti-aliasing filter)? I checked the doc for graphicsitem, gpen and qgraphicspath but they mention nothing about aliasing. Also Since I enabled dragging for components , I'm getting artefacts on screen ... any idea how to solve this? [video example](https://streamable.com/43ih5p) – aNormalPerson Jan 10 '21 at 03:05
  • 1
    @aNormalPerson The first thing you point out is out of the question but the solution is simple: `your_qgraphicsview->setRenderHint(QPainter::Antialiasing);`, for the second point I cannot reproduce it and it is probably caused by another part of your code (which is not my interest since my solution only focuses on functionality) but to analyze my test you can check my source code: https://github.com/eyllanesc/stackoverflow/tree/master/questions/65646117 – eyllanesc Jan 10 '21 at 03:11
  • Thanks for the aliasing solution, it works!! As for the other issue, I ran the code on a different PC, and it didn't have the weird glitches... Sounds like some specific compatibility issue with my laptop so it's off-topic here. Thanks again! – aNormalPerson Jan 10 '21 at 03:49