简体   繁体   中英

Composed QGraphicsItem not detecting clicks on sub-items

I am trying to make a simple extension of QGraphicsRectItem that allows me to resize the rectangle and move it around with the mouse. I model the handles with elliptical arches on the corners I want to enable for dragging, which I implement as QGraphicsEllipseItem s:

class QGraphicsBoxWithHandlesItem : public QObject, public QGraphicsRectItem
{
    Q_OBJECT

    typedef enum {
        None,
        BottomLeft,
        TopRight
    } ActiveAnchor;

private:
    QGraphicsEllipseItem m_anchorBottomLeft;
    QGraphicsEllipseItem m_anchorTopRight;
    float m_anchorRadius;
    ActiveAnchor m_activeAnchor;

public:
    QGraphicsBoxWithHandlesItem(QRectF r, float handlesRadius = 20.0, QGraphicsItem *parent = nullptr);

    void setAnchorRadius(float radius);
    float getAnchorRadius();

    QPainterPath shape() const;

protected:
    void mousePressEvent(QGraphicsSceneMouseEvent * event);
    void mouseMoveEvent(QGraphicsSceneMouseEvent * event);
    void mouseReleaseEvent(QGraphicsSceneMouseEvent * event);
};

I want to be able to detect clicks both on the rectangle and on the handle items (this is necessary because if the rectangle gets too small the handles are the only easily clickable area), so I thought I'd extend QGraphicsRectItem::shape() to add to the returned QPainterPath the paths of the sub-items (adjusting coordinates to make them relative to the parent item):

QPainterPath QGraphicsBoxWithHandlesItem::shape() const
{
    auto curShape = QGraphicsRectItem::shape();

    curShape.addPath( mapFromItem(&m_anchorBottomLeft, m_anchorBottomLeft.shape()) );
    curShape.addPath( mapFromItem(&m_anchorTopRight, m_anchorTopRight.shape()) );
    return curShape;
}

What I get, however, is that now clicks inside the handles areas are completely ignored and only clicks in the central area of the rectangle are handled.

What is the correct way to extend the clickable area of an item when it has a non-trivial shape?

Update : I tried to set the ItemIsSelectable flag on a handle and now, if I click on it, I see that it gets selected. I still don't get any mousePressEvent in the parent, however. What am I doing wrong?

Edit:

This is the constructor implementation:

QGraphicsBoxWithHandlesItem::QGraphicsBoxWithHandlesItem( QRectF r, float handlesRadius, QGraphicsItem * parent) :
    QGraphicsRectItem(parent),
    m_anchorRadius(handlesRadius),
    m_activeAnchor(None)
{
    setFlag(QGraphicsItem::ItemIsMovable);
    setFlag(QGraphicsItem::ItemIsSelectable);

    setRect(r);

    m_anchorBottomLeft.setRect(-m_anchorRadius, -m_anchorRadius, m_anchorRadius*2, m_anchorRadius*2);
    m_anchorBottomLeft.setPos(rect().bottomLeft());
    m_anchorBottomLeft.setSpanAngle(90 * 16); // angle is in 16ths of degree
    m_anchorBottomLeft.setParentItem(this);

    m_anchorTopRight.setRect(-m_anchorRadius, -m_anchorRadius, m_anchorRadius*2, m_anchorRadius*2);
    m_anchorTopRight.setPos(rect().topRight());
    m_anchorTopRight.setStartAngle(180 * 16); // angle is in 16ths of degree
    m_anchorTopRight.setSpanAngle(90 * 16); // angle is in 16ths of degree
    m_anchorTopRight.setParentItem(this);
}

Your QGraphicsEllipseItem are on top of the base item so the mouse events will never come to you.

What you have to do is use a sceneEventFilter, but since the QGraphicsEllipseItem are children of the main item they will never move, so they should not have a parent but you should add them directly to the scene.

The full functionality is implemented in the following code:

*.h

#ifndef QGRAPHICSBOXWITHHANDLESITEM_H
#define QGRAPHICSBOXWITHHANDLESITEM_H

#include <QGraphicsRectItem>
#include <QObject>

class QGraphicsBoxWithHandlesItem : public QObject, public QGraphicsRectItem
{
    Q_OBJECT
    enum ActiveAnchor{
        None,
        BottomLeft,
        TopRight
    };

public:
    QGraphicsBoxWithHandlesItem(QRectF r, float handlesRadius = 20.0, QGraphicsItem *parent = nullptr);
protected:
    QVariant itemChange(GraphicsItemChange change, const QVariant &value);
    bool sceneEventFilter(QGraphicsItem *watched, QEvent *event);
private:
    QGraphicsEllipseItem m_anchorBottomLeft;
    QGraphicsEllipseItem m_anchorTopRight;
    float m_anchorRadius;
    ActiveAnchor m_activeAnchor;
};

#endif // QGRAPHICSBOXWITHHANDLESITEM_H

*.cpp

#include "qgraphicsboxwithhandlesitem.h"

#include <QEvent>
#include <QGraphicsScene>
#include <QDebug>

QGraphicsBoxWithHandlesItem::QGraphicsBoxWithHandlesItem( QRectF r, float handlesRadius, QGraphicsItem * parent) :
    QGraphicsRectItem(parent),
    m_anchorRadius(handlesRadius),
    m_activeAnchor(None)
{
    setFlag(QGraphicsItem::ItemIsMovable);
    setFlag(QGraphicsItem::ItemIsSelectable);
    setFlag(QGraphicsItem::ItemSendsGeometryChanges);

    setRect(r);

    m_anchorBottomLeft.setRect(-m_anchorRadius, -m_anchorRadius, m_anchorRadius*2, m_anchorRadius*2);
    m_anchorBottomLeft.setPos(rect().bottomLeft());
    m_anchorBottomLeft.setSpanAngle(90 * 16); // angle is in 16ths of degree
    //m_anchorBottomLeft.setParentItem(this);
    m_anchorBottomLeft.setFlag(QGraphicsItem::ItemIsMovable);
    m_anchorBottomLeft.setFlag(QGraphicsItem::ItemIsSelectable);

    m_anchorTopRight.setRect(-m_anchorRadius, -m_anchorRadius, m_anchorRadius*2, m_anchorRadius*2);
    m_anchorTopRight.setPos(rect().topRight());
    m_anchorTopRight.setStartAngle(180 * 16); // angle is in 16ths of degree
    m_anchorTopRight.setSpanAngle(90 * 16); // angle is in 16ths of degree
    //m_anchorTopRight.setParentItem(this);
    m_anchorTopRight.setFlag(QGraphicsItem::ItemIsMovable);
    m_anchorTopRight.setFlag(QGraphicsItem::ItemIsSelectable);
}

QVariant QGraphicsBoxWithHandlesItem::itemChange(GraphicsItemChange change, const QVariant & value){
    if(change == QGraphicsItem::ItemSceneHasChanged){
        if(scene()){
            scene()->addItem(&m_anchorBottomLeft);
            scene()->addItem(&m_anchorTopRight);
            m_anchorBottomLeft.installSceneEventFilter(this);
            m_anchorTopRight.installSceneEventFilter(this);
        }
    }
    else if (change == QGraphicsItem::ItemPositionHasChanged) {
        m_anchorBottomLeft.setPos(mapToScene(rect().bottomLeft()));
        m_anchorTopRight.setPos(mapToScene(rect().topRight()));
    }
    return QGraphicsRectItem::itemChange(change, value);
}

bool QGraphicsBoxWithHandlesItem::sceneEventFilter(QGraphicsItem *watched, QEvent *event)
{
    if(watched == &m_anchorTopRight){
        switch (event->type()) {
        case QEvent::GraphicsSceneMousePress:{
            //mousePressEvent
            qDebug()<<"mousePressEvent m_anchorTopRight";
            break;
        }
        case QEvent::GraphicsSceneMouseMove:{
            // mouseMoveEvent
            QRectF r = rect();
            auto p = m_anchorTopRight.mapToScene(m_anchorTopRight.rect().center());
            r.setTopRight(mapFromScene(p));
            setRect(r);
            qDebug()<<"mouseMoveEvent m_anchorTopRight";
            break;
        }
        case QEvent::GraphicsSceneMouseRelease :{
            //mouseReleaseEvent
            qDebug()<<"mouseReleaseEvent m_anchorTopRight";
            break;
        }
        }
    }

    if(watched == &m_anchorBottomLeft){
        switch (event->type()) {
        case QEvent::GraphicsSceneMousePress:{
            //mousePressEvent
            qDebug()<<"mousePressEvent m_anchorBottomLeft";
            break;
        }
        case QEvent::GraphicsSceneMouseMove:{
            // mouseMoveEvent
            QRectF r = rect();
            auto p = m_anchorBottomLeft.mapToScene(m_anchorBottomLeft.rect().center());
            r.setBottomLeft(mapFromScene(p));
            setRect(r);
            qDebug()<<"mouseMoveEvent m_anchorBottomLeft";
            break;
        }
        case QEvent::GraphicsSceneMouseRelease :{
            //mouseReleaseEvent
            qDebug()<<"mouseReleaseEvent m_anchorBottomLeft";
            break;
        }
        }
    }
    return  QGraphicsRectItem::sceneEventFilter(watched, event);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM