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

QGraphicsItem子类:如何在item层级检测自身是否处于移动状态

Qt多选中移动Item的状态检测与箭头适配方案

一、解决Item层级的移动状态检测问题

你之前用QApplication::mouseButtons()的方案无效,是因为多选移动过程中左键始终处于按下状态,无法区分主动拖拽项和连带移动项。换用以下思路:

1. 给Item添加状态标记

在BaseItem类中新增成员变量:

private:
    bool m_isDragging = false;       // 是否被鼠标直接拖拽
    bool m_isMoving = false;         // 是否处于移动状态(主动/连带/父项带动)
    QList<ArrowItem*> m_connectedArrows; // 关联的箭头列表

2. 重写鼠标事件标记拖拽状态

通过鼠标事件标记当前项是否是主动拖拽的发起者:

void BaseItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    if (event->button() == Qt::LeftButton) {
        m_isDragging = true;
        m_isMoving = true;
    }
    QGraphicsItem::mousePressEvent(event);
}

void BaseItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    if (event->button() == Qt::LeftButton) {
        m_isDragging = false;
        m_isMoving = false;
        notifyArrowsUpdate();
    }
    QGraphicsItem::mouseReleaseEvent(event);
}

3. 在itemChange中精准判断移动状态

结合拖拽标记、选中状态、父项状态来确定当前项是否在移动:

QVariant BaseItem::itemChange(GraphicsItemChange change, const QVariant &value)
{
    if (change == ItemScenePositionHasChanged) {
        // 检查父项是否在移动(处理子项被父项带动的情况)
        bool parentIsMoving = false;
        QGraphicsItem* parent = parentItem();
        while (parent) {
            BaseItem* baseParent = qobject_cast<BaseItem*>(parent);
            if (baseParent && baseParent->isMoving()) {
                parentIsMoving = true;
                break;
            }
            parent = parent->parentItem();
        }

        // 判断当前项是否处于移动状态:主动拖拽/选中连带移动/父项带动
        m_isMoving = m_isDragging || parentIsMoving;
        if (!m_isMoving && isSelected() && scene()->mouseGrabberItem()) {
            m_isMoving = true;
        }

        // 移动状态变化时通知关联箭头
        if (m_isMoving) {
            notifyArrowsUpdate();
        }
    }
    return QGraphicsItem::itemChange(change, value);
}

// 新增公共方法供外部访问状态
bool BaseItem::isMoving() const
{
    return m_isMoving;
}

// 通知所有关联箭头更新的辅助方法
void BaseItem::notifyArrowsUpdate()
{
    for (ArrowItem* arrow : m_connectedArrows) {
        if (arrow) arrow->updateShape();
    }
}

二、ArrowItem的自适应逻辑实现

针对箭头自动调整/随动的需求,在ArrowItem中添加如下逻辑:

1. 保存关联Item与自定义形状标记

private:
    BaseItem* m_item1 = nullptr;
    BaseItem* m_item2 = nullptr;
    bool m_customShapeEnabled = false; // 是否开启自定义形状

2. 核心更新逻辑

在updateShape中区分两种场景:

void ArrowItem::updateShape()
{
    if (!m_item1 || !m_item2) return;

    // 自定义形状时,检查两个关联Item是否同步移动
    if (m_customShapeEnabled) {
        bool item1Moving = m_item1->isMoving();
        bool item2Moving = m_item2->isMoving();
        
        if (item1Moving && item2Moving) {
            // 两者同步移动,仅随动,不调整箭头形状
            adjustPositionOnly();
            return;
        }
    }

    // 非自定义形状/不同步移动时,自动适配形状
    autoAdjustShape();
}

// 仅随动调整位置的辅助方法
void ArrowItem::adjustPositionOnly()
{
    QLineF line(m_item1->scenePos(), m_item2->scenePos());
    setLine(mapFromScene(line.p1()), mapFromScene(line.p2()));
}

// 自动调整形状的核心逻辑
void ArrowItem::autoAdjustShape()
{
    // 实现箭头自动适配两点位置的逻辑,比如调整路径、箭头角度等
    QLineF line(m_item1->scenePos(), m_item2->scenePos());
    setLine(mapFromScene(line.p1()), mapFromScene(line.p2()));
    // 额外的箭头形状调整(如箭头大小、曲线适配)...
}

三、补充说明

  • 确保BaseItem维护好与ArrowItem的关联关系(比如在箭头创建/销毁时添加/移除到m_connectedArrows列表)。
  • 若Item有复杂的父子层级,可在父项的notifyArrowsUpdate中递归通知所有子项,确保子项移动时箭头也能收到通知。

内容的提问来源于stack exchange,提问作者Brett Li

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 03:48:35