3. 图元拖拽与放置

图元拖拽与放置 学习卡片

任务目标

在Qt图形视图框架中,实现图元(QGraphicsItem)的鼠标拖拽移动与放置。用户点击图元后按住拖动,图元跟随鼠标移动,松开后停留在新位置。通过本案例掌握QGraphicsItem的鼠标事件处理、坐标转换以及QGraphicsScene的拖拽模式设置。

环境依赖

基于Qt Widgets模块,需要Qt5或Qt6开发环境,核心依赖为QtWidgets与QtGui。在项目文件(.pro)中添加:

QT += widgets

核心实现

自定义图元类DraggableItem,重写mousePressEvent、mouseMoveEvent与mouseReleaseEvent。关键点:按下时记录鼠标与图元原点的偏移量,移动时通过setPos更新位置,释放时不做额外处理即完成放置。场景需设置为QGraphicsScene::ItemIsMovable模式以启用拖拽,但本案例通过事件处理实现更精细的控制。

#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsRectItem>
#include <QMouseEvent>
#include <QPointF>

class DraggableItem : public QGraphicsRectItem {
public:
    DraggableItem(qreal x, qreal y, qreal w, qreal h)
        : QGraphicsRectItem(x, y, w, h) {
        setFlag(QGraphicsItem::ItemIsMovable, false); // 禁用内置移动,使用自定义事件
        setFlag(QGraphicsItem::ItemIsSelectable, true);
        setBrush(QBrush(QColor(100, 180, 255)));
        setPen(QPen(Qt::black, 2));
    }

protected:
    void mousePressEvent(QGraphicsSceneMouseEvent *event) override {
        // 记录鼠标按下位置与图元位置之间的偏移
        m_offset = event->scenePos() - pos();
        event->accept();
    }

    void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override {
        // 根据鼠标场景坐标减去偏移量,更新图元位置
        setPos(event->scenePos() - m_offset);
        event->accept();
    }

    void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override {
        // 放置完成,无需额外处理
        event->accept();
    }

private:
    QPointF m_offset;
};

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);

    QGraphicsScene scene;
    scene.setSceneRect(0, 0, 400, 300);

    // 创建两个可拖拽图元
    DraggableItem *item1 = new DraggableItem(0, 0, 80, 60);
    item1->setPos(50, 50);
    scene.addItem(item1);

    DraggableItem *item2 = new DraggableItem(0, 0, 100, 80);
    item2->setPos(200, 150);
    scene.addItem(item2);

    QGraphicsView view(&scene);
    view.setRenderHint(QPainter::Antialiasing);
    view.resize(420, 320);
    view.show();

    return app.exec();
}

运行与验证

编译运行:

qmake
make
./drag_demo

验证步骤:

  1. 程序启动后,场景中显示两个蓝色矩形图元。
  2. 鼠标左键点击任一图元并按住拖动,图元应跟随鼠标移动。
  3. 松开鼠标,图元停留在释放位置,不会弹回原处。
  4. 重复拖拽多个图元,图元之间可重叠,位置互不影响。

通过条件:图元在拖拽过程中无闪烁、无跳变,松开后位置精确对应鼠标释放点;两个图元均可独立拖拽,且拖拽时图元始终位于鼠标下方(以按下点为准)。

关键说明

本实现通过event->scenePos()获取鼠标在场景坐标系中的位置,减去按下时记录的偏移量得到图元应处的位置。若需限制图元在场景边界内,可在mouseMoveEvent中添加边界判断。若使用setFlag(ItemIsMovable)配合场景的ItemIsMovable模式,Qt会自动处理拖拽,但自定义事件方式便于后续扩展吸附、对齐等工业组态场景中的精确放置需求。