Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

QGraphicsItemGroup and childs resize



  • Hello everybody. I made an algorithm for resizing a picture(inherited from QGraphicsItem) using vector mathematics (I added points to the corners and using the mouse, the picture is resized while maintaining the aspect ratio https://forum.qt.io/topic/124924/qgraphicsitem-resize-with-keeping-aspect-ratio).

    Next, I created a group(inherited from QGraphicsItemGroup) add border dots(inherited from QGraphicsRectItem) and added pictures to the group (via addToGroup).

    Is it possible to generalize this algorithm to a group? So that all pictures in the group are resized with border dot position.

    this what I want: https://gph.is/g/EJxpeVQ
    and this is what I got: https://gph.is/g/aQnpq5x
    can't resize childs... here the code and gif example:
    borderdot.h :

    #ifndef BORDERDOT_H
    #define BORDERDOT_H
    
    #include <QObject>
    #include <QGraphicsRectItem>
    
    class QGraphicsSceneHoverEventPrivate;
    class QGraphicsSceneMouseEvent;
    
    class DotSignal : public QObject, public QGraphicsRectItem
    {
        Q_OBJECT
        Q_PROPERTY(QPointF previousPosition READ previousPosition WRITE setPreviousPosition NOTIFY previousPositionChanged)
    
    public:
        explicit DotSignal(QGraphicsItem *parentItem = 0, QObject *parent = 0);
        explicit DotSignal(QPointF pos, QGraphicsItem *parentItem = 0, QObject *parent = 0);
        ~DotSignal();
    
        enum Flags {
            Movable = 0x01
        };
    
        enum { Type = UserType + 1 };
    
        int type() const override
        {
            return Type;
        }
        QPointF previousPosition() const;
        void setPreviousPosition(const QPointF previousPosition);
    
        void setDotFlags(unsigned int flags);
    
    signals:
        void previousPositionChanged();
        void signalMouseRelease();
        void signalMove(QGraphicsItem *signalOwner, qreal dx, qreal dy);
    
    protected:
        void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
        void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
        void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
        void hoverEnterEvent(QGraphicsSceneHoverEvent *event) override;
        void hoverLeaveEvent(QGraphicsSceneHoverEvent *event) override;
    
    public slots:
    
    private:
        unsigned int m_flags;
        QPointF m_previousPosition;
    };
    
    #endif // BORDERDOT_H
    

    borderdot.cpp:

    #include "borderdot.h"
    
    
    #include <QBrush>
    #include <QColor>
    #include <QGraphicsSceneHoverEvent>
    #include <QGraphicsSceneMouseEvent>
    
    DotSignal::DotSignal(QGraphicsItem *parentItem, QObject *parent) :
        QObject(parent)
    {
        setZValue(999999999);
    //    setFlags(ItemIsMovable);
        setParentItem(parentItem);
        setAcceptHoverEvents(true);
        setBrush(QBrush(Qt::black));
        setRect(-4,-4,8,8);
        setDotFlags(0);
    }
    
    DotSignal::DotSignal(QPointF pos, QGraphicsItem *parentItem, QObject *parent) :
        QObject(parent)
    {
        setZValue(999999999);
    //    setFlags(ItemIsMovable);
        setParentItem(parentItem);
        setAcceptHoverEvents(true);
        setBrush(QBrush(Qt::black));
        setRect(-4,-4,8,8);
        setPos(pos);
        setPreviousPosition(pos);
        setDotFlags(0);
    }
    
    DotSignal::~DotSignal()
    {
    
    }
    
    QPointF DotSignal::previousPosition() const
    {
        return m_previousPosition;
    }
    
    void DotSignal::setPreviousPosition(const QPointF previousPosition)
    {
        if (m_previousPosition == previousPosition)
            return;
    
        m_previousPosition = previousPosition;
        emit previousPositionChanged();
    }
    
    void DotSignal::setDotFlags(unsigned int flags)
    {
        m_flags = flags;
    }
    
    void DotSignal::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
    {
        if(m_flags & Movable) {
            qDebug()<<"DotSignal::mouseMoveEvent";
            auto dx = event->scenePos().x() - m_previousPosition.x();
            auto dy = event->scenePos().y() - m_previousPosition.y();
            moveBy(dx,dy);
            setPreviousPosition(event->scenePos());
            emit signalMove(this, dx, dy);
        } else {
            qDebug()<<"else DotSignal::mouseMoveEvent";
            QGraphicsItem::mouseMoveEvent(event);
        }
    }
    
    void DotSignal::mousePressEvent(QGraphicsSceneMouseEvent *event)
    {
        if(m_flags & Movable){
            setPreviousPosition(event->scenePos());
        } else {
            QGraphicsItem::mousePressEvent(event);
        }
    }
    
    void DotSignal::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
    {
        emit signalMouseRelease();
        QGraphicsItem::mouseReleaseEvent(event);
    }
    
    void DotSignal::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
    {
        qDebug()<<"DotSignal::hoverEnterEvent";
        Q_UNUSED(event)
        setBrush(QBrush(Qt::red));
    }
    
    void DotSignal::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
    {
        qDebug()<<"DotSignal::hoverLeaveEvent";
        Q_UNUSED(event)
        setBrush(QBrush(Qt::black));
    }
    

    itemgroup.h:

    #ifndef ITEMGROUP_H
    #define ITEMGROUP_H
    
    #include <QObject>
    #include <QGraphicsItemGroup>
    
    
    class DotSignal;
    class QGraphicsSceneMouseEvent;
    
    class ItemGroup : public QObject, public QGraphicsItemGroup
    {
        Q_OBJECT
        Q_INTERFACES(QGraphicsItem)
        Q_PROPERTY(QPointF previousPosition READ previousPosition WRITE setPreviousPosition NOTIFY previousPositionChanged)
    public:
        enum EItemsType {
            eBorderDot = QGraphicsItem::UserType + 1,
        };
        ItemGroup(uint64_t& zc, QGraphicsItemGroup *parent = nullptr);
        ~ItemGroup();
        enum ActionStates {
            ResizeState = 0x01,
            RotationState = 0x02
        };
    
        enum CornerFlags {
            Top = 0x01,
            Bottom = 0x02,
            Left = 0x04,
            Right = 0x08,
            TopLeft = Top|Left,
            TopRight = Top|Right,
            BottomLeft = Bottom|Left,
            BottomRight = Bottom|Right
        };
    
        enum CornerGrabbers {
            GrabberTop = 0,
            GrabberBottom,
            GrabberLeft,
            GrabberRight,
            GrabberTopLeft,
            GrabberTopRight,
            GrabberBottomLeft,
            GrabberBottomRight
        };
    
    public:
        void addItem(QGraphicsItem* item);
        void printChilds();
        QPointF previousPosition() const;
        void setPreviousPosition(const QPointF previousPosition);
    
    
    signals:
        void rectChanged(ItemGroup *rect);
        void previousPositionChanged();
        void clicked(ItemGroup *rect);
        void signalMove(QGraphicsItemGroup *item, qreal dx, qreal dy);
    
    protected:
        void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
        void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
        void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
        void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override;
        void hoverEnterEvent(QGraphicsSceneHoverEvent *event) override;
        void hoverLeaveEvent(QGraphicsSceneHoverEvent *event) override;
        void hoverMoveEvent(QGraphicsSceneHoverEvent *event) override;
        QVariant itemChange(GraphicsItemChange change, const QVariant &value) override;
    public:
        void clearItemGroup();
        bool isContain(const QGraphicsItem* item) const;
        bool isEmpty() const;
        void incZ();
    protected:
        QRectF boundingRect() const override;
        void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
    
    
    
    private:
        QPointF shiftMouseCoords_;
        uint64_t& zCounter_;
        QRectF m_tmpRect;
    
    private:
        unsigned int m_cornerFlags;
        unsigned int m_actionFlags;
        QPointF m_previousPosition;
        bool m_leftMouseButtonPressed;
        DotSignal *cornerGrabber[8];
    
        void resizeLeft( const QPointF &pt);
        void resizeRight( const QPointF &pt);
        void resizeBottom(const QPointF &pt);
        void resizeTop(const QPointF &pt);
    
        void rotateItem(const QPointF &pt);
        void setPositionGrabbers();
        void setVisibilityGrabbers();
        void hideGrabbers();
    };
    
    #endif // ITEMGROUP_H
    
    

    itemgroup.cpp:

    #include <QPainter>
    #include <QDebug>
    #include <QCursor>
    #include <QGraphicsScene>
    #include <QGraphicsSceneMouseEvent>
    #include <QGraphicsRectItem>
    #include <math.h>
    #include "borderdot.h"
    
    static const double Pi = 3.14159265358979323846264338327950288419717;
    static double TwoPi = 2.0 * Pi;
    
    
    ItemGroup::~ItemGroup()
    {
        for(int i = 0; i < 8; i++){
            delete cornerGrabber[i];
        }
    }
    
    QPointF ItemGroup::previousPosition() const
    {
        return m_previousPosition;
    }
    
    
    void ItemGroup::setPreviousPosition(const QPointF previousPosition)
    {
        if (m_previousPosition == previousPosition)
            return;
    
        m_previousPosition = previousPosition;
        emit previousPositionChanged();
    }
    
    
    ItemGroup::ItemGroup(uint64_t& zc, QGraphicsItemGroup *parent) :
        QGraphicsItemGroup(parent),
        zCounter_(zc),
        m_cornerFlags(0),
        m_actionFlags(ResizeState)
    {
        setAcceptHoverEvents(true);
        setFlags(ItemIsSelectable|ItemSendsGeometryChanges);
        for(int i = 0; i < 8; i++){
            cornerGrabber[i] = new DotSignal(this);
        }
        setPositionGrabbers();
    }
    
    void ItemGroup::addItem(QGraphicsItem* item)
    {
        addToGroup(item);
        auto childs = childItems();
        auto tmp = childs.first()->sceneBoundingRect();
        for (auto& it : childs) {
            if (it->type() == eBorderDot) continue;
            tmp = tmp.united(it->sceneBoundingRect());
        }
        m_tmpRect = tmp;
    }
    
    void ItemGroup::printChilds()
    {
        auto childs = childItems();
        for (auto& it : childs) {
            LOG_DEBUG(logger, "CHILDREN: ", it);
        }
    }
    
    QRectF ItemGroup::boundingRect() const
    {
        return m_tmpRect;
    }
    
    void ItemGroup::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
    {
        QPointF pt = event->pos();
        if(m_actionFlags == ResizeState){
            switch (m_cornerFlags) {
            case Top:
                resizeTop(pt);
                break;
            case Bottom:
                resizeBottom(pt);
                break;
            case Left:
                resizeLeft(pt);
                break;
            case Right:
                resizeRight(pt);
                break;
            case TopLeft:
                resizeTop(pt);
                resizeLeft(pt);
                break;
            case TopRight:
                resizeTop(pt);
                resizeRight(pt);
                break;
            case BottomLeft:
                resizeBottom(pt);
                resizeLeft(pt);
                break;
            case BottomRight:
                resizeBottom(pt);
                resizeRight(pt);
                break;
            default:
                if (m_leftMouseButtonPressed) {
                    setCursor(Qt::ClosedHandCursor);
                    auto dx = event->scenePos().x() - m_previousPosition.x();
                    auto dy = event->scenePos().y() - m_previousPosition.y();
                    moveBy(dx,dy);
                    setPreviousPosition(event->scenePos());
                    emit signalMove(this, dx, dy);
                }
                break;
            }
        } else {
                if (m_leftMouseButtonPressed) {
                    setCursor(Qt::ClosedHandCursor);
                    auto dx = event->scenePos().x() - m_previousPosition.x();
                    auto dy = event->scenePos().y() - m_previousPosition.y();
                    moveBy(dx,dy);
                    setPreviousPosition(event->scenePos());
                    emit signalMove(this, dx, dy);
                }
        }
        QGraphicsItemGroup::mouseMoveEvent(event);
    }
    
    void ItemGroup::mousePressEvent(QGraphicsSceneMouseEvent *event)
    {
        setZValue(++zCounter_);
        shiftMouseCoords_ = (this->pos() - mapToScene(event->pos()))/scale();
        if (event->button() & Qt::LeftButton) {
            m_leftMouseButtonPressed = true;
            setPreviousPosition(event->scenePos());
            emit clicked(this);
        }
        QGraphicsItemGroup::mousePressEvent(event);
        LOG_DEBUG(logger, "EventPos: (", event->pos().x(),";",event->pos().y(), "), Pos: (", pos().x(),";",pos().y(),")");
    }
    
    void ItemGroup::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
    {
        if (event->button() & Qt::LeftButton) {
            m_leftMouseButtonPressed = false;
        }
        QGraphicsItemGroup::mouseReleaseEvent(event);
    }
    
    void ItemGroup::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
    {
        qDebug()<<"ItemGroup::hoverEnterEvent";
        setPositionGrabbers();
        setVisibilityGrabbers();
        QGraphicsItem::hoverEnterEvent(event);
    }
    
    void ItemGroup::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
    {
        qDebug()<<"ItemGroup::hoverLeaveEvent";
        m_cornerFlags = 0;
        hideGrabbers();
        setCursor(Qt::CrossCursor);
        QGraphicsItem::hoverLeaveEvent( event );
    }
    
    void ItemGroup::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
    {
        QPointF pt = event->pos();              // The current position of the mouse
        qreal drx = pt.x() - boundingRect().right();    // Distance between the mouse and the right
        qreal dlx = pt.x() - boundingRect().left();     // Distance between the mouse and the left
    
        qreal dby = pt.y() - boundingRect().top();      // Distance between the mouse and the top
        qreal dty = pt.y() - boundingRect().bottom();   // Distance between the mouse and the bottom
    
    
        m_cornerFlags = 0;
        if( dby < 10 && dby > -10 ) m_cornerFlags |= Top;       // Top side
        if( dty < 10 && dty > -10 ) m_cornerFlags |= Bottom;    // Bottom side
        if( drx < 10 && drx > -10 ) m_cornerFlags |= Right;     // Right side
        if( dlx < 10 && dlx > -10 ) m_cornerFlags |= Left;      // Left side
    
        switch (m_cornerFlags) {
        case TopLeft:
        case TopRight:
        case BottomLeft:
        case BottomRight: {
            setCursor(Qt::BusyCursor);
            break;
        }
        default:
            setCursor(Qt::CrossCursor);
            break;
        }
    }
    
    
    QVariant ItemGroup::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value)
    {
        switch (change) {
        case QGraphicsItemGroup::ItemSelectedChange:
            m_actionFlags = ResizeState;
            break;
        default:
            break;
        }
        return QGraphicsItemGroup::itemChange(change, value);
    }
    
    
    void ItemGroup::resizeRight(const QPointF &pt)
    {
        QRectF tmpRect = boundingRect();
        if( pt.x() < tmpRect.left() )
            return;
        qreal widthOffset =  ( pt.x() - tmpRect.left() );
        if( widthOffset < 10 ) /// limit
            return;
        if( widthOffset < 10)
            tmpRect.setWidth( -widthOffset );
        else
            tmpRect.setWidth( widthOffset );
        prepareGeometryChange();
        m_tmpRect = tmpRect;
        update();
        setPositionGrabbers();
    }
    
    void ItemGroup::resizeTop(const QPointF &pt)
    {
        QRectF tmpRect = boundingRect();
        if( pt.y() > tmpRect.bottom() )
            return;
        qreal heightOffset =  ( pt.y() - tmpRect.bottom() );
        if( heightOffset > -11 ) /// limit
            return;
        if( heightOffset < 0)
            tmpRect.setHeight( -heightOffset );
        else
            tmpRect.setHeight( heightOffset );
        tmpRect.translate( 0 , boundingRect().height() - tmpRect.height() );
        prepareGeometryChange();
        m_tmpRect = tmpRect;
        update();
        setPositionGrabbers();
    }
    
    void ItemGroup::setPositionGrabbers()
    {
        QRectF tmpRect = boundingRect();
        cornerGrabber[GrabberTop]->setPos(tmpRect.left() + tmpRect.width()/2, tmpRect.top());
        cornerGrabber[GrabberBottom]->setPos(tmpRect.left() + tmpRect.width()/2, tmpRect.bottom());
        cornerGrabber[GrabberLeft]->setPos(tmpRect.left(), tmpRect.top() + tmpRect.height()/2);
        cornerGrabber[GrabberRight]->setPos(tmpRect.right(), tmpRect.top() + tmpRect.height()/2);
        cornerGrabber[GrabberTopLeft]->setPos(tmpRect.topLeft().x(), tmpRect.topLeft().y());
        cornerGrabber[GrabberTopRight]->setPos(tmpRect.topRight().x(), tmpRect.topRight().y());
        cornerGrabber[GrabberBottomLeft]->setPos(tmpRect.bottomLeft().x(), tmpRect.bottomLeft().y());
        cornerGrabber[GrabberBottomRight]->setPos(tmpRect.bottomRight().x(), tmpRect.bottomRight().y());
    }
    
    void ItemGroup::setVisibilityGrabbers()
    {
        cornerGrabber[GrabberTopLeft]->setVisible(true);
        cornerGrabber[GrabberTopRight]->setVisible(true);
        cornerGrabber[GrabberBottomLeft]->setVisible(true);
        cornerGrabber[GrabberBottomRight]->setVisible(true);
        cornerGrabber[GrabberTop]->setVisible(true);
        cornerGrabber[GrabberBottom]->setVisible(true);
        cornerGrabber[GrabberLeft]->setVisible(true);
        cornerGrabber[GrabberRight]->setVisible(true);
    }
    
    void ItemGroup::hideGrabbers()
    {
        for(int i = 0; i < 8; i++){
            cornerGrabber[i]->setVisible(false);
        }
    }
    
    

Log in to reply