Qt利用QDrag實(shí)現(xiàn)拖拽拼圖功能詳解
一、項(xiàng)目介紹
本文介紹利用QDrag類實(shí)現(xiàn)拖拽拼圖功能。左邊是打散的圖,拖動(dòng)到右邊進(jìn)行復(fù)現(xiàn),此外程序還支持手動(dòng)拖入原圖片。
二、項(xiàng)目基本配置
新建一個(gè)Qt案例,項(xiàng)目名稱為“puzzle”,基類選擇“QMainWindow”,取消選中創(chuàng)建UI界面復(fù)選框,完成項(xiàng)目創(chuàng)建。
三、UI界面設(shè)置
UI界面如下:
無UI界面
四、主程序?qū)崿F(xiàn)
4.1 main.cpp
源文件main.cpp中需要預(yù)先調(diào)用loadImage函數(shù)加載圖像并顯示界面,代碼如下:
QApplication a(argc, argv); MainWindow w; w.loadImage(QStringLiteral(":/example.jpg")); w.show(); return a.exec();
4.1 mainwindow.h頭文件
頭文件中聲明相應(yīng)的對(duì)象和槽函數(shù):
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源文件
源文件中對(duì)函數(shù)進(jìn)行定義,首先在構(gòu)造函數(shù)中運(yùn)行setupMenus()函數(shù)和setupWidgets()函數(shù)并設(shè)置大小尺寸和標(biāo)題:
setupMenus(); setupWidgets(); setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));//設(shè)置大小策略 setWindowTitle(tr("拖拽拼圖"));
定義打開圖像函數(shù):
//打開圖像(重新選擇圖像分割) void MainWindow::openImage() { const QString directory = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation).value(0, QDir::homePath()); QFileDialog dialog(this, tr("Open Image"), directory);//創(chuàng)建打開文件對(duì)話框 dialog.setFileMode(QFileDialog::ExistingFile);//設(shè)置返回存在的文件名 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()); }
定義加載圖像函數(shù):
// 加載圖片 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(); }
拼圖完成后彈出完成對(duì)話框:
//拼圖完成后彈出對(duì)話框 void MainWindow::setCompleted() { QMessageBox::information(this, tr("拼圖完成"), tr("恭喜!您已經(jīng)成功拼圖 \n" "點(diǎn)擊OK重新開始"), QMessageBox::Ok); setupPuzzle(); }
建立拼圖函數(shù):
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(); }
設(shè)置菜單欄:
//設(shè)置菜單欄 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); }
設(shè)置界面布局:
//設(shè)置界面布局 void MainWindow::setupWidgets() { QFrame *frame = new QFrame; QHBoxLayout *frameLayout = new QHBoxLayout(frame);//水平布局 puzzleWidget = new PuzzleWidget(400);//新建PuzzleWidget對(duì)象,設(shè)置圖像尺寸為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:
它實(shí)現(xiàn)了以下幾個(gè)方法。
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執(zhí)行的流程是:
Drag是從drag->exec()開始的,此時(shí)將開啟進(jìn)入一個(gè)新的事件循環(huán),然后在拖動(dòng)的過程中會(huì)在下面三個(gè)事件中交替:
其中DragEnter是有拖動(dòng)進(jìn)入該Widget時(shí)觸發(fā)的,對(duì)應(yīng)的DragLeave則是拖動(dòng)離開時(shí)觸發(fā)的,而DragMove就是鼠標(biāo)拖動(dòng)的時(shí)候觸發(fā)的。
最后當(dāng)鼠標(biāo)釋放的時(shí)候?qū)⒂|發(fā)dragEvent,此時(shí)將決定拖拽的結(jié)果。
回頭看一下Drag的觸發(fā),和大多數(shù)系統(tǒng)一樣,一個(gè)Drag可能是從控件外觸發(fā)的,即將外部的數(shù)據(jù)拖入,也可以是從控件內(nèi)部觸發(fā),即手動(dòng)生成一個(gè)QDrag對(duì)象。
拖動(dòng)的機(jī)制:
其實(shí)拖動(dòng)就是將一處的數(shù)據(jù)移動(dòng)或者復(fù)制到另外一處,在QT中拖動(dòng)所承載的數(shù)據(jù)使用QMimeData表示的,它可以用來表示許多Mime Type的集合。一個(gè)Mime Type即有format和data兩部分組成,format即指示了如何解析對(duì)應(yīng)的data。
五、效果演示
完整效果如下:
初始界面:
拼圖完成后界面:
到此這篇關(guān)于Qt利用QDrag實(shí)現(xiàn)拖拽拼圖功能詳解的文章就介紹到這了,更多相關(guān)Qt QDrag拖拽拼圖內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++實(shí)現(xiàn)LeetCode(113.二叉樹路徑之和之二)
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(113.二叉樹路徑之和之二),本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07C++ virtual destructor虛擬析構(gòu)函數(shù)
C++中基類采用virtual虛析構(gòu)函數(shù)是為了防止內(nèi)存泄漏。具體地說,如果派生類中申請(qǐng)了內(nèi)存空間,并在其析構(gòu)函數(shù)中對(duì)這些內(nèi)存空間進(jìn)行釋放,今天通過本文給大家介紹C++ virtual destructor虛擬析構(gòu)函數(shù)的相關(guān)知識(shí),感興趣的朋友一起看看吧2021-05-05C語言斷言函數(shù)assert()的學(xué)習(xí)筆記
在C語言庫函數(shù)中提供了一個(gè)輔助調(diào)試程序的小型庫,它是由assert()宏組成,本文就詳細(xì)的介紹了一下如何使用,感興趣的可以了解一下2021-11-11C++ 中使用lambda代替 unique_ptr 的Deleter的方法
這篇文章主要介紹了C++ 中使用lambda代替 unique_ptr 的Deleter的方法,需要的朋友可以參考下2017-04-04