组合 QGraphicsItem 未检测到对子项的点击

Composed QGraphicsItem not detecting clicks on sub-items

我正在尝试对 QGraphicsRectItem 做一个简单的扩展,它允许我调整矩形的大小并用鼠标移动它。我在要启用拖动的角上用椭圆拱形手柄建模,我将其实现为 QGraphicsEllipseItems:

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);
};

我希望能够检测到矩形和手柄项上的点击(这是必要的,因为如果矩形太小,手柄是唯一容易点击的区域),所以我想我会扩展QGraphicsRectItem::shape() 添加到返回的 QPainterPath 子项的路径(调整坐标使它们相对于父项):

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;
}

然而,我得到的是现在完全忽略手柄区域内的点击,只处理矩形中心区域的点击。

当项目具有非平凡的形状时,扩展项目的可点击区域的正确方法是什么?

更新:我尝试在句柄上设置 ItemIsSelectable 标志,现在,如果我单击它,我会看到它被选中。但是,我仍然没有在父级中得到任何 mousePressEvent 。我做错了什么?

编辑:

这是构造函数实现:

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);
}

您的 QGraphicsEllipseItem 位于基本项目之上,因此您永远不会收到鼠标事件。

您需要做的是使用 sceneEventFilter,但是由于 QGraphicsEllipseItem 是主项的子项,它们永远不会移动,所以它们不应该有父项,但您应该将它们直接添加到场景中。

完整功能在以下代码中实现:

*.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);
}