Qt拖动工具栏控件到图页中均匀展示

dragcontroller.h

复制代码
#ifndef DRAGCONTROLLER_H
#define DRAGCONTROLLER_H

#include <QObject>
#include <QPointer>
#include <QWidget>

class DragController  : public QObject
{
    Q_OBJECT
public:

    explicit DragController(QObject *parent = nullptr);

    void setSourceWidget(QWidget *w);

    // QML 里直接调用:DragController.startDrag({type:"rect"})
    Q_INVOKABLE void startDrag(const QVariantMap &payload);

private:
    QPointer<QWidget> source_;
};

#endif // DRAGCONTROLLER_H

dragcontroller.cpp

复制代码
#include "dragcontroller.h"
#include <QDrag>
#include <QMimeData>
#include <QJsonDocument>
#include <QJsonObject>
#include <QVariantMap>


DragController::DragController(QObject *parent)
{

}
void DragController::setSourceWidget(QWidget *w) {
    source_ = w;
}

void DragController::startDrag(const QVariantMap &payload) {
    if (!source_) return;

    // 把 payload 转成 JSON bytes,放进自定义 mime
    QJsonObject obj = QJsonObject::fromVariantMap(payload);
    QByteArray bytes = QJsonDocument(obj).toJson(QJsonDocument::Compact);

    auto *mime = new QMimeData;
    mime->setData("application/x-palette-item", bytes);

    auto *drag = new QDrag(source_);
    drag->setMimeData(mime);

    // 可选:给个拖拽图标(不设置也行)
    // drag->setPixmap(QPixmap(":/icons/rect.png"));

    drag->exec(Qt::CopyAction);
}

dropscene.h

复制代码
#ifndef DROPSCENE_H
#define DROPSCENE_H

#include <QGraphicsScene>
#include <QGraphicsSceneDragDropEvent>
#include <QtQuickWidgets/QQuickWidget>
#include <QMimeData>
#include <QJsonDocument>
#include <QJsonObject>
#include <QGraphicsRectItem>
#include <QGraphicsEllipseItem>
#include <QGraphicsTextItem>
#include <QPen>
#include <QBrush>
#include <QGraphicsItem>

#include <QVector>

class PlaceholderItem;
class SnapRectItem;


class DropScene : public QGraphicsScene
{
    Q_OBJECT
public:
    explicit DropScene(QObject* parent = nullptr);
    PlaceholderItem* placeholderAt(const QPointF& scenePos) const;
    void snapItemToPlaceholder(SnapRectItem* item, PlaceholderItem* ph);

protected:
    void dragEnterEvent(QGraphicsSceneDragDropEvent* event) override;

    void dragMoveEvent(QGraphicsSceneDragDropEvent* event) override ;

    void dropEvent(QGraphicsSceneDragDropEvent* event) override;
private:
    QVector<PlaceholderItem*> placeholders_;
};

#endif // DROPSCENE_H

dropscene.cpp

复制代码
#include "dropscene.h"
#include "placeholderitem.h"
#include "snaprectitem.h"

#include <QGraphicsSceneDragDropEvent>
#include <QMimeData>
#include <QJsonDocument>
#include <QJsonObject>
#include <QPen>
#include <QBrush>


DropScene::DropScene(QObject *parent)
    : QGraphicsScene(parent)
{
    const qreal w = 160, h = 500;
    const qreal gap = 20;
    const qreal startX = 80;
    const qreal y = 80;

    for (int i = 0; i < 5; ++i) {
        QRectF
            r(startX + i*(w+gap), y, w, h);
        auto* ph = new PlaceholderItem(r);
        addItem(ph);
        placeholders_.push_back(ph);
    }

    // 让 sceneRect 自动包住占位框(加点边距)
    setSceneRect(itemsBoundingRect().adjusted(-50, -50, 50, 50));

    qDebug() << "items count(after):" << items().size()
             << "sceneRect:" << sceneRect();
}

PlaceholderItem* DropScene::placeholderAt(const QPointF& scenePos) const {
    for (auto* ph : placeholders_) {
        if (ph->rect().contains(scenePos))
            return ph;
    }
    return nullptr;
}

void DropScene::snapItemToPlaceholder(SnapRectItem* item, PlaceholderItem* ph) {
    if (!item || !ph || ph->occupied) return;

    // 让矩形填满占位框
    QRectF r = ph->rect();
    item->setRect(0, 0, r.width(), r.height());
    item->setPos(r.topLeft());

    ph->occupied = true;
}

void DropScene::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
{
    if (event->mimeData()->hasFormat("application/x-palette-item")) {
        event->acceptProposedAction();
        return;
    }
    QGraphicsScene::dragEnterEvent(event);
}

void DropScene::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
{
    if (event->mimeData()->hasFormat("application/x-palette-item")) {
        event->acceptProposedAction();
        return;
    }
    QGraphicsScene::dragMoveEvent(event);
}

void DropScene::dropEvent(QGraphicsSceneDragDropEvent *event)
{
    if (!event->mimeData()->hasFormat("application/x-palette-item")) {
        QGraphicsScene::dropEvent(event);
        return;
    }

    const QByteArray bytes = event->mimeData()->data("application/x-palette-item");
    const QJsonDocument doc = QJsonDocument::fromJson(bytes);
    const QJsonObject obj = doc.object();

    const QString type = obj.value("type").toString();
    const QPointF pos = event->scenePos();

    if (type == "rect") {
        // 1) 看看是不是落在某个占位框里
        auto* ph = placeholderAt(pos);

        // 2) 创建可吸附矩形
        auto* item = new SnapRectItem(this);
        item->setBrush(QBrush(Qt::lightGray));
        item->setPen(QPen(Qt::black));
        item->setFlag(QGraphicsItem::ItemIsMovable, true);
        item->setFlag(QGraphicsItem::ItemIsSelectable, true);
        addItem(item);

        if (ph && !ph->occupied) {
            snapItemToPlaceholder(item, ph); // 自动填充
        } else {
            // 不在框里:自由放置一个默认大小
            item->setRect(0, 0, 120, 60);
            item->setPos(pos);
        }
    }

    // ellipse/text 你可继续保持原逻辑...
    event->acceptProposedAction();
}

mainwindow.h

复制代码
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QTabWidget>
#include <QGraphicsView>
#include <QDockWidget>
#include <QQuickWidget>
#include <QQmlContext>
#include <QQuickView>
#include <QWidget>
#include "dropscene.h"
#include "dragcontroller.h"

QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget* parent = nullptr);
    void addNewTab(const QString& name);
    QTabWidget* tabs_ = nullptr;
private:
    Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H

mainwindow.cpp

复制代码
#include "mainwindow.h"
#include "ui_mainwindow.h"




MainWindow::MainWindow(QWidget *parent)
{
    setWindowTitle("Widgets + QML Palette Drag&Drop");
    resize(1200, 800);

    tabs_ = new QTabWidget(this);
    setCentralWidget(tabs_);
    addNewTab("Scene 1");
    addNewTab("Scene 2");

    auto *dock = new QDockWidget("Tools", this);
    addDockWidget(Qt::LeftDockWidgetArea, dock);

    auto *quickView = new QQuickView;
    quickView->setResizeMode(QQuickView::SizeRootObjectToView);

    // 1) 创建 controller
    auto *dragCtrl = new DragController(this);

    // 2) 暴露给 QML:DragController.startDrag(...)
    quickView->rootContext()->setContextProperty("DragController", dragCtrl);

    quickView->setSource(QUrl::fromLocalFile("D:/work/tmp/Qt/TestSceneItem/image/controls.qml"));

    auto *container = QWidget::createWindowContainer(quickView, dock);
    dock->setWidget(container);

    // 3) source widget 用 container(QDrag 需要 QWidget 作为源)
    dragCtrl->setSourceWidget(container);
}

void MainWindow::addNewTab(const QString &name)
{
    auto* scene = new DropScene(this);
    auto* view  = new QGraphicsView(scene);

    view
        ->setAcceptDrops(true);
    view
        ->viewport()->setAcceptDrops(true);
    view
        ->setDragMode(QGraphicsView::RubberBandDrag);

    // 关键:强制视口对准占位框区域
    view
        ->centerOn(0, 0);  // 或者 centerOn(80, 80);

    // 也可以更精确:保证 (80,80,...) 这块一定可见
    view
        ->ensureVisible(QRectF(60, 60, 5*160 + 4*20 + 40, 90 + 40));

    tabs_
        ->addTab(view, name);
}

placeholderitem.h

复制代码
#ifndef PLACEHOLDERITEM_H
#define PLACEHOLDERITEM_H

#include <QGraphicsRectItem>
#include <QPen>

class PlaceholderItem : public QGraphicsRectItem
{
public:
    explicit PlaceholderItem(const QRectF& r);

signals:

public:
    bool occupied = false;
};

#endif // PLACEHOLDERITEM_H

placeholderitem.cpp

复制代码
#include "placeholderitem.h"



PlaceholderItem::PlaceholderItem(const QRectF &r)
    : QGraphicsRectItem(r)
{

    setPen(QPen(Qt::darkGray, 2, Qt::DashLine));
    setBrush(Qt::NoBrush);
    setZValue(-10);          // 放底层
    setFlag(QGraphicsItem::ItemIsSelectable, false);
    setAcceptedMouseButtons(Qt::NoButton);
}

snaprectItem.h

复制代码
#ifndef SNAPRECTITEM_H
#define SNAPRECTITEM_H
#include <QGraphicsRectItem>
class DropScene;

class SnapRectItem : public QGraphicsRectItem {
public:
    explicit SnapRectItem(DropScene* sceneOwner, QGraphicsItem* parent=nullptr);

protected:
    void mouseReleaseEvent(QGraphicsSceneMouseEvent* event) override;

private:
    DropScene* owner_ = nullptr;
};

#endif // SNAPRECTITEM_H

snaprectItem.cpp

复制代码
#include "snaprectItem.h"
#include "dropscene.h"
#include "placeholderitem.h"
#include <QGraphicsSceneMouseEvent>

SnapRectItem::SnapRectItem(DropScene* sceneOwner, QGraphicsItem* parent)
    : QGraphicsRectItem(parent), owner_(sceneOwner) {}

void SnapRectItem::mouseReleaseEvent(QGraphicsSceneMouseEvent* event) {
    QGraphicsRectItem::mouseReleaseEvent(event);

    if (!owner_) return;

    // 用图元中心点判断落在哪个框里
    QPointF center= sceneBoundingRect().center();
    auto* ph = owner_->placeholderAt(center);

    if (ph && !ph->occupied) {
        owner_->snapItemToPlaceholder(this, ph);
    }
}

main.cpp

复制代码
#include "mainwindow.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

运行截图:

相关推荐
Trouvaille ~1 天前
【Linux】UDP Socket编程实战(二):网络字典与回调设计
linux·运维·服务器·网络·c++·udp·操作系统
3GPP仿真实验室1 天前
【Matlab源码】6G候选波形:OFDM-IM 索引调制仿真平台
开发语言·matlab
明洞日记1 天前
【图解软考八股034】深入解析 UML:识别标准建模图示
c++·软件工程·软考·uml·面向对象·架构设计
Coder_Boy_1 天前
基于SpringAI的在线考试系统-企业级教育考试系统核心架构(完善版)
开发语言·人工智能·spring boot·python·架构·领域驱动
前端玖耀里1 天前
Linux C/C++ 中系统调用与库函数调用的区别
linux·c语言·c++
艾莉丝努力练剑1 天前
【Linux:文件】基础IO:文件操作的系统调用和库函数各个接口汇总及代码演示
linux·运维·服务器·c++·人工智能·centos·io
2301_765703141 天前
C++中的代理模式变体
开发语言·c++·算法
咚为1 天前
Rust tokio:Task ≠ Thread:Tokio 调度模型中的“假并发”与真实代价
开发语言·后端·rust
灰子学技术1 天前
性能分析工具比较pprof、perf、valgrind、asan
java·开发语言
Minilinux20181 天前
Google ProtoBuf 简介
开发语言·google·protobuf·protobuf介绍