• Qt实战案例(53)——利用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();
    
    • 1
    • 2
    • 3
    • 4
    • 5

    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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    4.2 mainwindow.cpp源文件

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

        setupMenus();
        setupWidgets();
    
        setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));//设置大小策略
        setWindowTitle(tr("拖拽拼图"));
    
    • 1
    • 2
    • 3
    • 4
    • 5

    定义打开图像函数:

    //打开图像(重新选择图像分割)
    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());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    定义加载图像函数:

    // 加载图片
    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();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

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

    //拼图完成后弹出对话框
    void MainWindow::setCompleted()
    {
        QMessageBox::information(this, tr("拼图完成"),
                                 tr("恭喜!您已经成功拼图 \n"
                                    "点击OK重新开始"),
                                 QMessageBox::Ok);
    
        setupPuzzle();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    建立拼图函数:

    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();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    设置菜单栏:

    //设置菜单栏
    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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    设置界面布局:

    //设置界面布局
    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);//中心部件
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    Drag执行的流程是:
    Drag是从drag->exec()开始的,此时将开启进入一个新的事件循环,然后在拖动的过程中会在下面三个事件中交替:
    在这里插入图片描述
    其中DragEnter是有拖动进入该Widget时触发的,对应的DragLeave则是拖动离开时触发的,而DragMove就是鼠标拖动的时候触发的。

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

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

    拖动的机制:
    其实拖动就是将一处的数据移动或者复制到另外一处,在QT中拖动所承载的数据使用QMimeData表示的,它可以用来表示许多Mime Type的集合。一个Mime Type即有format和data两部分组成,format即指示了如何解析对应的data。更详细的定义可以参考:http://en.wikipedia.org/wiki/MIME

    五、效果演示

    完整效果如下:
    初始界面:
    在这里插入图片描述
    拼图完成后界面:
    在这里插入图片描述

    如果没有看懂的话,完整代码可以参考:https://download.csdn.net/download/didi_ya/86249377


    ok,以上便是本文的全部内容了,如果对你有所帮助,记得点个赞哟~

  • 相关阅读:
    专精特新中小企业申报条件
    WPF编程宝典:使用C# 2012和.NET 4.5 第4版---1.4 WPF体系结构
    uni-app之android原生插件开发
    常用函数utils
    Linux python2升级到python3
    【漏洞复现】Apache_HTTP_2.4.49_路径穿越漏洞(CVE-2021-41773)
    Clickhouse架构与设计
    云原生|kubernetes|etcd数据库增删改查
    acwing算法基础之数据结构--并查集算法
    牛客刷题<二>异步复位的串联T触发器
  • 原文地址:https://blog.csdn.net/didi_ya/article/details/125959399