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-07
C++ 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-05
C語言斷言函數(shù)assert()的學(xué)習(xí)筆記
在C語言庫函數(shù)中提供了一個(gè)輔助調(diào)試程序的小型庫,它是由assert()宏組成,本文就詳細(xì)的介紹了一下如何使用,感興趣的可以了解一下2021-11-11
C++ 中使用lambda代替 unique_ptr 的Deleter的方法
這篇文章主要介紹了C++ 中使用lambda代替 unique_ptr 的Deleter的方法,需要的朋友可以參考下2017-04-04

