首頁 > 軟體

Qt利用QDrag實現拖拽拼圖功能詳解

2022-07-25 10:01:40

一、專案介紹

本文介紹利用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拖拽拼圖內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com