You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让无父子关系的重叠QGraphicsItem均接收hover事件?

问题

我使用两个自定义图形项:继承自QGraphicsItem的ItemImage,以及继承自QGraphicsPixmapItem的ItemDrawing。两者均在主窗口构造函数中创建并通过scene->addItem()添加到场景:

MyWindow::MyWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MyWindowClass())
    , _scene(new QGraphicsScene(this))
    , _item_drawing(new ItemDrawing())
    , _item_image(new ItemImage())
{
    ...
    _scene->addItem(_item_drawing);
    _scene->addItem(_item_image);
    ui->view->setScene(_scene);
}

两个项均在构造函数中调用setAcceptHoverEvents(true),并重写了hoverEnterEvent和hoverMoveEvent;同时我还重写了GraphicsView的mouseMoveEvent并调用了QGraphicsView::mouseMoveEvent:

/* ItemImage */
ItemImage::ItemImage(QGraphicsItem* parent)
    : QGraphicsItem(parent)
{
    setAcceptHoverEvents(true);
    setFlags(ItemIsSelectable | ItemIsMovable);
}

void ItemImage::hoverMoveEvent(QGraphicsSceneHoverEvent* event)
{
    ...
    QGraphicsItem::hoverMoveEvent(event);
}


/* ItemDrawing */
ItemDrawing::ItemDrawing(QGraphicsItem* parent)
    : QGraphicsPixmapItem(parent)
    , _temp_measure()
{
    setZValue(1);
    setTransformationMode(Qt::SmoothTransformation);
    setShapeMode(QGraphicsPixmapItem::BoundingRectShape);
    setAcceptHoverEvents(true);
}

void ItemDrawing::hoverMoveEvent(QGraphicsSceneHoverEvent* event)
{
    ...
    QGraphicsItem::hoverMoveEvent(event);
}


/* GraphicsView which inherits QGraphicsView */
void GraphicsView::mouseMoveEvent(QMouseEvent* e)
{
    ...
    QGraphicsView::mouseMoveEvent(e);
}

目前的问题是:鼠标位于重叠坐标时,只有z序最高的项(即设置了setZValue(1)的ItemDrawing)能接收hover事件;当ItemImage移出ItemDrawing区域时,ItemImage可正常接收hover事件;若将一个项设为另一个的子项,两者均可接收hover事件。

想知道:除了设置父子关系外,是否还有其他方法让重叠的无父子关系的两个项都接收hover事件?

我查看Qt的QGraphicsScenePrivate::dispatchHoverEvent实现后,发现它似乎不会将事件传递给鼠标下方的所有项,因此推测可能无法实现,但不确定是否遗漏了某些方法。


解决方案

1. 自定义场景手动分发hover事件

重写QGraphicsScene的hover事件处理函数,在事件触发时,通过items()方法获取鼠标位置下的所有堆叠项,再手动调用每个项的hover事件接口。

示例代码:

void MyGraphicsScene::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
{
    // 获取鼠标位置下所有符合条件的项,按z序从高到低排列
    QList<QGraphicsItem*> itemsUnderMouse = items(
        event->scenePos(), 
        Qt::IntersectsItemShape, 
        Qt::DescendingOrder
    );
    
    for (QGraphicsItem* item : itemsUnderMouse) {
        if (item->acceptHoverEvents()) {
            // 手动触发项的hoverMove事件
            item->hoverMoveEvent(event);
        }
    }
    
    // 保留父类默认事件逻辑
    QGraphicsScene::hoverMoveEvent(event);
}

void MyGraphicsScene::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{
    QList<QGraphicsItem*> itemsUnderMouse = items(
        event->scenePos(), 
        Qt::IntersectsItemShape, 
        Qt::DescendingOrder
    );
    
    for (QGraphicsItem* item : itemsUnderMouse) {
        if (item->acceptHoverEvents()) {
            item->hoverEnterEvent(event);
        }
    }
    
    QGraphicsScene::hoverEnterEvent(event);
}

注意:如果需要避免原事件被修改影响后续项,可以克隆事件对象后再分发。

2. 上层项主动转发hover事件

在z序较高的ItemDrawing的hover事件处理函数中,主动查找鼠标位置下的其他目标项,并调用其hover事件方法。

示例代码:

void ItemDrawing::hoverMoveEvent(QGraphicsSceneHoverEvent* event)
{
    // 先处理自身的hover逻辑
    ...
    
    // 获取鼠标位置下的所有项,排除自身后查找目标项
    QList<QGraphicsItem*> itemsUnderMouse = scene()->items(event->scenePos());
    for (QGraphicsItem* item : itemsUnderMouse) {
        if (item != this && item->type() == ItemImage::Type && item->acceptHoverEvents()) {
            static_cast<ItemImage*>(item)->hoverMoveEvent(event);
            break; // 仅转发给特定类型项,找到后即可退出循环
        }
    }
    
    QGraphicsItem::hoverMoveEvent(event);
}

这种方法适合仅需要转发给特定类型项的场景,代码侵入性小,灵活性高。

3. 使用事件过滤器拦截分发

在主窗口或场景中安装事件过滤器,拦截hover事件后分发给所有符合条件的项,无需修改项的内部代码。

示例代码:

bool MyWindow::eventFilter(QObject *watched, QEvent *event)
{
    if (watched == _scene && 
        (event->type() == QEvent::GraphicsSceneHoverMove || event->type() == QEvent::GraphicsSceneHoverEnter)) 
    {
        QGraphicsSceneHoverEvent* hoverEvent = static_cast<QGraphicsSceneHoverEvent*>(event);
        QList<QGraphicsItem*> itemsUnderMouse = _scene->items(hoverEvent->scenePos());
        
        for (QGraphicsItem* item : itemsUnderMouse) {
            if (item->acceptHoverEvents()) {
                if (event->type() == QEvent::GraphicsSceneHoverMove) {
                    item->hoverMoveEvent(hoverEvent);
                } else {
                    item->hoverEnterEvent(hoverEvent);
                }
            }
        }
    }
    return QMainWindow::eventFilter(watched, event);
}

在主窗口构造函数中安装过滤器:

_scene->installEventFilter(this);

内容的提问来源于stack exchange,提问作者Dean Lee

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 06:29:52