c++qtqt6qgraphicspixmapitem

How to join two QGraphicsPixmapItem with a QLine?


I have two QGraphicsPixmapItem added to my QGraphicsScene.

Now I want to add a QLine connecting both of this QGraphicsPixmapItem and should be able to move around with QLine stretching like graph.

I have looked into this Elastic Nodes Example, but it's too complicated for me.

based on this: How to use itemChange from QGraphicsItem in Qt, I was able to solve my problem except I am struggling to replace QGraphicsEllipseItem with QGraphicsPixmapItem because of rect argument, which is needed in another function for calculating position.


#include <QApplication>

#include <QGraphicsScene>

#include <QGraphicsView>

#include <QGraphicsEllipseItem>

#include <QGraphicsLineItem>

class CustomElipse : public QGraphicsEllipseItem

{

public:

    CustomElipse (const QRectF& rect) : QGraphicsEllipseItem(rect) {

        setFlag(QGraphicsItem::ItemIsMovable);

        setFlag(QGraphicsItem::ItemSendsScenePositionChanges);

    }

    void addLine(QGraphicsLineItem *line, bool isPoint1) {

        this->line = line;

        isP1 = isPoint1;

    }

    QVariant itemChange(GraphicsItemChange change, const QVariant &value)

    {

        if (change == ItemPositionChange && scene()) {

            // value is the new position.

            QPointF newPos = value.toPointF();

            moveLineToCenter(newPos);

        }

        return QGraphicsItem::itemChange(change, value);

    }

    void moveLineToCenter(QPointF newPos) {

        // Converts the elipse position (top-left)

        // to its center position

        int xOffset = rect().x() + rect().width()/2;

        int yOffset = rect().y() + rect().height()/2;

        QPointF newCenterPos = QPointF(newPos.x() + xOffset, newPos.y() + yOffset);

        // Move the required point of the line to the center of the elipse

        QPointF p1 = isP1 ? newCenterPos : line->line().p1();

        QPointF p2 = isP1 ? line->line().p2() : newCenterPos;

        line->setLine(QLineF(p1, p2));

    }

private:

    QGraphicsLineItem *line;

    bool isP1;

};

int main(int argc, char *argv[])

{

    QApplication a(argc, argv);

    QGraphicsScene scene;

    CustomElipse *elipse1 = new CustomElipse(QRectF(30, 30, 15, 25));

    scene.addItem(elipse1);

    CustomElipse *elipse2 = new CustomElipse(QRectF(70, 70, 25, 15));

    scene.addItem(elipse2);

    QGraphicsLineItem *line = scene.addLine(QLineF(40, 40, 80, 80));

    elipse1->addLine(line, true);

    elipse2->addLine(line, false);

    QGraphicsView view(&scene);

    view.show();

    return a.exec();

}

Solution

  • From the Qt Documentation of QGraphicsPixmapItem:

    The QGraphicsPixmapItem class provides a pixmap item that you can add to a QGraphicsScene.

    Here's an example with comments on the changes I made:

    #include <QApplication>
    #include <QGraphicsScene>
    #include <QGraphicsView>
    #include <QGraphicsPixmapItem>
    #include <QGraphicsLineItem>
    #include <QPixmap>
    
    class CustomElipse : public QGraphicsPixmapItem
    {
    public:
        //Use a QPixmap instead of QRect
        CustomElipse (const QPixmap& pixmap) : QGraphicsPixmapItem(pixmap) 
        {
            setFlag(QGraphicsItem::ItemIsMovable);
            setFlag(QGraphicsItem::ItemSendsScenePositionChanges);
        }
    
        void addLine(QGraphicsLineItem *line, bool isPoint1) 
        {
            this->line = line;
            isP1 = isPoint1;
        }
    
        QVariant itemChange(GraphicsItemChange change, const QVariant &value)
        {
            if (change == ItemPositionChange && scene()) 
            {
                // value is the new position.
                QPointF newPos = value.toPointF();
                moveLineToCenter(newPos);
            }
            return QGraphicsItem::itemChange(change, value);
        }
        
        void moveLineToCenter(QPointF newPos) 
        {
            // Converts the elipse position (top-left)
            // to its center position
            //use pixmap's rect to calculate the offset
            int xOffset = pixmap().rect().x() + pixmap().width()/2;
            int yOffset = pixmap().rect().y() + pixmap().height()/2;
    
            QPointF newCenterPos = QPointF(newPos.x() + xOffset, newPos.y() + yOffset);
    
            // Move the required point of the line to the center of the elipse
            QPointF p1 = isP1 ? newCenterPos : line->line().p1();
            QPointF p2 = isP1 ? line->line().p2() : newCenterPos;
    
            line->setLine(QLineF(p1, p2));
        }
    
    private:
        QGraphicsLineItem *line;
        bool isP1;
    };
    
    int main(int argc,char*argv[])
    {
        QApplication a(argc, argv);
    
        QGraphicsScene scene;
    
        //I used an image of a green circle to test it
        CustomElipse *elipse1 = new CustomElipse(QPixmap(":green.png"));
        scene.addItem(elipse1);
    
        CustomElipse *elipse2 = new CustomElipse(QPixmap(":green.png"));
        scene.addItem(elipse2);
    
        QGraphicsLineItem *line = scene.addLine(QLineF(40, 40, 80, 80));
        elipse1->addLine(line, true);
        elipse2->addLine(line, false);
    
        QGraphicsView view(&scene);
        view.show();
    
        return a.exec();
    }
    

    Here's the result:

    Result