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

在QGraphicsScene中通过鼠标位置获取QGraphicsItem并实现图形项连线

实现QGraphicsItem间的中键拖拽连线功能

看起来你已经搭好了连线的基础框架,但还需要完善鼠标事件的处理流程,才能让“中键点选第一个块→拖拽到第二个块→松键连线”的完整交互跑起来。我来帮你梳理具体的实现步骤和代码细节:

一、核心逻辑拆解

要实现这个交互,我们需要处理三个关键阶段:

  • 中键按下时:记录起始块,并创建临时连线给用户视觉反馈
  • 鼠标拖拽时:实时更新临时连线的终点位置
  • 鼠标松开时:检测是否命中目标块,创建正式连线并清理临时资源

二、代码实现细节

1. 给GraphicsBlock添加鼠标事件处理

首先在GraphicsBlock类中添加成员变量和重写鼠标事件:

#include <QGraphicsItem>
#include <QGraphicsSceneMouseEvent>
#include <QList>

class GraphicsConnect; // 前置声明

class GraphicsBlock : public QGraphicsItem {
public:
    // 你的现有成员...
    void connectBlocks(GraphicsBlock *block);

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

private:
    GraphicsBlock* m_startBlock = nullptr; // 记录起始连线块
    GraphicsConnect* m_tempConnection = nullptr; // 拖拽时的临时连线
    QList<GraphicsConnect*> m_connections; // 保存当前块的所有连线(可选,用于后续更新)
};

2. 实现鼠标事件逻辑

#include "GraphicsConnect.h"

void GraphicsBlock::mousePressEvent(QGraphicsSceneMouseEvent* event) {
    if (event->button() == Qt::MiddleButton) {
        // 标记当前块为起始点
        m_startBlock = this;
        // 创建临时连线并添加到场景
        m_tempConnection = new GraphicsConnect();
        QLineF initialLine(this->pos(), event->scenePos());
        m_tempConnection->line->setLine(initialLine);
        scene()->addItem(m_tempConnection);
    }
    // 别忘了调用父类事件处理,保证其他鼠标行为正常
    QGraphicsItem::mousePressEvent(event);
}

void GraphicsBlock::mouseMoveEvent(QGraphicsSceneMouseEvent* event) {
    if (m_startBlock && m_tempConnection) {
        // 实时更新临时连线的终点为当前鼠标位置
        QLineF updatedLine(m_startBlock->pos(), event->scenePos());
        m_tempConnection->line->setLine(updatedLine);
    }
    QGraphicsItem::mouseMoveEvent(event);
}

void GraphicsBlock::mouseReleaseEvent(QGraphicsSceneMouseEvent* event) {
    if (m_startBlock && m_tempConnection) {
        // 检测鼠标松开位置是否存在目标块
        QList<QGraphicsItem*> hitItems = scene()->items(event->scenePos());
        for (QGraphicsItem* item : hitItems) {
            GraphicsBlock* targetBlock = qgraphicsitem_cast<GraphicsBlock*>(item);
            // 确保目标块存在且不是起始块本身
            if (targetBlock && targetBlock != m_startBlock) {
                m_startBlock->connectBlocks(targetBlock);
                break;
            }
        }
        // 清理临时连线资源
        scene()->removeItem(m_tempConnection);
        delete m_tempConnection;
        m_tempConnection = nullptr;
        m_startBlock = nullptr;
    }
    QGraphicsItem::mouseReleaseEvent(event);
}

3. 完善connectBlocks函数

你的原有函数缺少将连线添加到场景的步骤,补充后如下:

void GraphicsBlock::connectBlocks(GraphicsBlock *block) {
    GraphicsConnect *connection = new GraphicsConnect();
    // 注意:如果你的块的pos()是左上角而非中心,建议改用boundingRect().center() + pos()
    QLineF line(this->pos(), block->pos());
    connection->line->setLine(line);
    // 将正式连线添加到场景才能显示
    scene()->addItem(connection);
    // 可选:保存连线引用,后续块移动时可更新连线位置
    m_connections.append(connection);
    block->m_connections.append(connection);
}

4. 优化GraphicsConnect类

确保连线的视觉效果正常,比如设置画笔样式:

#include <QGraphicsLineItem>

class GraphicsConnect : public QGraphicsItem {
public:
    QGraphicsLineItem* line;
    GraphicsConnect() {
        line = new QGraphicsLineItem(this);
        // 设置连线的画笔,让它更明显
        line->setPen(QPen(Qt::darkBlue, 2, Qt::SolidLine));
    }
    // 重写boundingRect和paint,保证QGraphicsItem的特性正常工作
    QRectF boundingRect() const override {
        return line->boundingRect();
    }
    void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override {
        // 无需额外绘制,子项line会自动渲染
    }
};

三、可选进阶优化

  • 如果需要块移动时连线自动跟随,可重写GraphicsBlock的itemChange函数,监听ItemPositionHasChanged事件,遍历m_connections更新每条连线的起点和终点。
  • 可以给临时连线设置不同的样式(比如虚线),区分临时和正式连线。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 10:21:42