Qt利用QDrag实现拖拽拼图功能详解

 更新时间:2022年07月25日 08:14:02   作者:wendy_ya  
QDrag类为MIME-based拖拽数据转换提供支持。本文为大家主要介绍如何利用QDrag类实现拖拽拼图功能。左边是打散的图,拖动到右边进行复现,此外程序还支持手动拖入原图片,感兴趣的可以了解一下

一、项目介绍

本文介绍利用QDrag类实现拖拽拼图功能。左边是打散的图,拖动到右边进行复现,此外程序还支持手动拖入原图片。

二、项目基本配置

新建一个Qt案例,项目名称为“puzzle”,基类选择“QMainWindow”,取消选中创建UI界面复选框,完成项目创建。

三、UI界面设置

UI界面如下:

无UI界面

四、主程序实现

4.1 main.cpp

源文件main.cpp中需要预先调用loadImage函数加载图像并显示界面,代码如下:

    QApplication a(argc, argv);
    MainWindow w;
    w.loadImage(QStringLiteral(":/example.jpg"));
    w.show();
    return a.exec();

4.1 mainwindow.h头文件

头文件中声明相应的对象和槽函数:

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
    void loadImage(const QString &path);

public slots:
    void openImage();
    void setupPuzzle();

private slots:
    void setCompleted();

private:
    void setupMenus();
    void setupWidgets();

    QPixmap puzzleImage;
    PiecesList *piecesList;
    PuzzleWidget *puzzleWidget;

4.2 mainwindow.cpp源文件

源文件中对函数进行定义,首先在构造函数中运行setupMenus()函数和setupWidgets()函数并设置大小尺寸和标题:

    setupMenus();
    setupWidgets();

    setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));//设置大小策略
    setWindowTitle(tr("拖拽拼图"));

定义打开图像函数:

//打开图像(重新选择图像分割)
void MainWindow::openImage()
{
    const QString directory =
        QStandardPaths::standardLocations(QStandardPaths::PicturesLocation).value(0, QDir::homePath());
    QFileDialog dialog(this, tr("Open Image"), directory);//创建打开文件对话框
    dialog.setFileMode(QFileDialog::ExistingFile);//设置返回存在的文件名
    QStringList mimeTypeFilters;
    for (const QByteArray &mimeTypeName : QImageReader::supportedMimeTypes())
        mimeTypeFilters.append(mimeTypeName);
    mimeTypeFilters.sort(); //排序
    dialog.setMimeTypeFilters(mimeTypeFilters);
    dialog.selectMimeTypeFilter("image/jpeg");
    if (dialog.exec() == QDialog::Accepted)
        loadImage(dialog.selectedFiles().constFirst());
}

定义加载图像函数:

// 加载图片
void MainWindow::loadImage(const QString &fileName)
{
    QPixmap newImage;
    if (!newImage.load(fileName)) {
        QMessageBox::warning(this, tr("Open Image"),
                             tr("The image file could not be loaded."),
                             QMessageBox::Close);
        return;
    }
    puzzleImage = newImage;
    setupPuzzle();
}

拼图完成后弹出完成对话框:

//拼图完成后弹出对话框
void MainWindow::setCompleted()
{
    QMessageBox::information(this, tr("拼图完成"),
                             tr("恭喜!您已经成功拼图 \n"
                                "点击OK重新开始"),
                             QMessageBox::Ok);

    setupPuzzle();
}

建立拼图函数:

void MainWindow::setupPuzzle()
{
    int size = qMin(puzzleImage.width(), puzzleImage.height());//获取图像宽度和高度的最小值
    puzzleImage = puzzleImage.copy((puzzleImage.width() - size) / 2,
        (puzzleImage.height() - size) / 2, size, size).scaled(puzzleWidget->width(),
            puzzleWidget->height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);//缩放

    piecesList->clear();//清空List
    //切分成5*5=25张拼图
    for (int y = 0; y < 5; ++y) {
        for (int x = 0; x < 5; ++x) {
            int pieceSize = puzzleWidget->pieceSize();
            QPixmap pieceImage = puzzleImage.copy(x * pieceSize, y * pieceSize, pieceSize, pieceSize);
            piecesList->addPiece(pieceImage, QPoint(x, y));
        }
    }

    for (int i = 0; i < piecesList->count(); ++i) {
        if (QRandomGenerator::global()->bounded(2) == 1) {
            QListWidgetItem *item = piecesList->takeItem(i);
            piecesList->insertItem(0, item);
        }
    }

    puzzleWidget->clear();
}

设置菜单栏:

//设置菜单栏
void MainWindow::setupMenus()
{
    QMenu *fileMenu = menuBar()->addMenu(tr("&文件"));

    QAction *openAction = fileMenu->addAction(tr("&打开..."), this, &MainWindow::openImage);
    openAction->setShortcuts(QKeySequence::Open);   //快捷键
    QAction *exitAction = fileMenu->addAction(tr("&退出"), qApp, &QCoreApplication::quit);
    exitAction->setShortcuts(QKeySequence::Quit);   //快捷键

    QMenu *gameMenu = menuBar()->addMenu(tr("&游戏"));
    gameMenu->addAction(tr("&重启"), this, &MainWindow::setupPuzzle);
}

设置界面布局:

//设置界面布局
void MainWindow::setupWidgets()
{
    QFrame *frame = new QFrame;
    QHBoxLayout *frameLayout = new QHBoxLayout(frame);//水平布局
    puzzleWidget = new PuzzleWidget(400);//新建PuzzleWidget对象,设置图像尺寸为400

    piecesList = new PiecesList(puzzleWidget->pieceSize(), this);


    connect(puzzleWidget, &PuzzleWidget::puzzleCompleted,
            this, &MainWindow::setCompleted, Qt::QueuedConnection);

    frameLayout->addWidget(piecesList);
    frameLayout->addWidget(puzzleWidget);
    setCentralWidget(frame);//中心部件
}

4.3 PiecesList类

新建PiecesList类,并继承自QListWidget:

4.4 PuzzleWidget类

新建PuzzleWidget类,继承自QWidget:

它实现了以下几个方法。

    void dragEnterEvent(QDragEnterEvent *event) override;
    void dragLeaveEvent(QDragLeaveEvent *event) override;
    void dragMoveEvent(QDragMoveEvent *event) override;
    void dropEvent(QDropEvent *event) override;
    void mousePressEvent(QMouseEvent *event) override;
    void paintEvent(QPaintEvent *event) override;

Drag执行的流程是:

Drag是从drag->exec()开始的,此时将开启进入一个新的事件循环,然后在拖动的过程中会在下面三个事件中交替:

其中DragEnter是有拖动进入该Widget时触发的,对应的DragLeave则是拖动离开时触发的,而DragMove就是鼠标拖动的时候触发的。

最后当鼠标释放的时候将触发dragEvent,此时将决定拖拽的结果。

回头看一下Drag的触发,和大多数系统一样,一个Drag可能是从控件外触发的,即将外部的数据拖入,也可以是从控件内部触发,即手动生成一个QDrag对象。

拖动的机制:

其实拖动就是将一处的数据移动或者复制到另外一处,在QT中拖动所承载的数据使用QMimeData表示的,它可以用来表示许多Mime Type的集合。一个Mime Type即有format和data两部分组成,format即指示了如何解析对应的data。

五、效果演示

完整效果如下:

初始界面:

拼图完成后界面:

到此这篇关于Qt利用QDrag实现拖拽拼图功能详解的文章就介绍到这了,更多相关Qt QDrag拖拽拼图内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C++实现LeetCode(113.二叉树路径之和之二)

    C++实现LeetCode(113.二叉树路径之和之二)

    这篇文章主要介绍了C++实现LeetCode(113.二叉树路径之和之二),本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-07-07
  • Opencv图像处理之轮廓外背景颜色改变

    Opencv图像处理之轮廓外背景颜色改变

    这篇文章主要为大家详细介绍了Opencv图像处理之轮廓外背景颜色改变,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-05-05
  • C++模板 index_sequence使用示例详解

    C++模板 index_sequence使用示例详解

    这篇文章主要为大家介绍了C++模板 index_sequence使用示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12
  • 浅谈C++如何求等差素数列

    浅谈C++如何求等差素数列

    这篇文章主要介绍了浅谈C++如何求等差素数列,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-07-07
  • C语言编程const遇上指针分析

    C语言编程const遇上指针分析

    本篇文章是C语言编程篇,主要为大家介绍C语言编程中当Const遇上指针的分析讲解,有需要的朋友可以借鉴参考下,希望可以有所帮助
    2021-09-09
  • C++ virtual destructor虚拟析构函数

    C++ virtual destructor虚拟析构函数

    C++中基类采用virtual虚析构函数是为了防止内存泄漏。具体地说,如果派生类中申请了内存空间,并在其析构函数中对这些内存空间进行释放,今天通过本文给大家介绍C++ virtual destructor虚拟析构函数的相关知识,感兴趣的朋友一起看看吧
    2021-05-05
  • C语言断言函数assert()的学习笔记

    C语言断言函数assert()的学习笔记

    在C语言库函数中提供了一个辅助调试程序的小型库,它是由assert()宏组成,本文就详细的介绍了一下如何使用,感兴趣的可以了解一下
    2021-11-11
  • C语言指针超详细讲解上篇

    C语言指针超详细讲解上篇

    指针提供了对地址操作的一种方法,因此,使用指针可使得 C 语言能够更高效地实现对计算机底层硬件的操作。另外,通过指针可以更便捷地操作数组。在一定意义上可以说,指针是 C 语言的精髓
    2022-04-04
  • C++ 中使用lambda代替 unique_ptr 的Deleter的方法

    C++ 中使用lambda代替 unique_ptr 的Deleter的方法

    这篇文章主要介绍了C++ 中使用lambda代替 unique_ptr 的Deleter的方法,需要的朋友可以参考下
    2017-04-04
  • C语言如何用顺序栈实现回文序列判断

    C语言如何用顺序栈实现回文序列判断

    这篇文章主要为大家介绍了C语言如何用顺序栈来实现回文序列的判断,文中含有详细的代码示例及分析,有需要的朋友可以借鉴参考下,希望能够有所帮助
    2021-10-10

最新评论