【问题标题】:Composed QGraphicsItem not detecting clicks on sub-items组合的 QGraphicsItem 未检测到对子项目的点击
【发布时间】:2019-01-04 07:54:01
【问题描述】:

我正在尝试对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);
};

我希望能够检测矩形和手柄项目上的点击(这是必要的,因为如果矩形变得太小,手柄是唯一容易点击的区域),所以我想我会扩展 @987654325 @ 将子项的路径添加到返回的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);
}

【问题讨论】:

  • 您是如何将 m_anchorBottomLeft 和 m_anchorTopRight 添加到场景中的?
  • 好问题.. 我不确定。它们是根 Rect 项目的子项目,我将其添加到场景中。我假设孩子是与父母一起添加的,但我不确定情况是否如此
  • 当我们需要解决方案时,我们选择了github.com/cesarbs/sizegripitem
  • @ypnos 谢谢,我一定会看看的! (我希望我昨天找到它..)但是,我仍然想知道我的代码中发生了什么,如果只是为了了解当我再次需要它时如何做到这一点:)

标签: c++ qt qt5 qgraphicsscene qgraphicsitem


【解决方案1】:

您的 QGraphicsEllipseItem 位于基础项目之上,因此鼠标事件永远不会出现在您身上。

你需要做的是使用一个场景事件过滤器,但由于 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);
}

【讨论】:

  • 谢谢,我试用了代码,它似乎可以工作(我仍然需要用我原来的一些东西来调整它,但这绝对是我需要的)。
猜你喜欢
  • 2015-09-10
  • 1970-01-01
  • 1970-01-01
  • 2013-08-16
  • 1970-01-01
  • 2022-10-22
  • 1970-01-01
  • 1970-01-01
  • 2013-07-17
相关资源
最近更新 更多