您的位置:首页 > 编程语言 > Qt开发

Qt Drag and Drop Puzzle Example 看看看~

2016-03-24 21:22 489 查看
这个例子展示使用拖放的API来完成一个拼图的解密游戏。



如图,将左边的拼图块拖放到右边,并完成恢复原图的样子即完成了游戏。

例子中用到了一个QSizePolicy的类,它是用来描述横向和纵向大小策略的布局属性的。

setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));

它会影响窗体的布局引擎,每个窗体在放置前都会返回一个QSizePolicy的东西,我们可以使用QWidget::sizePolicy来改变这个策略。QSizePolicy包含两个独立的QSizePolicy::Policy值和两个stretch因子,分别描述横向和纵向的策略。

可用horizontalPolicy(), verticalPolicy(), horizontalStretch(), verticalStretch()函数就可以返回;

可用setHorizontalPolicy(), setVerticalPolicy(), setHorizontalStretch(), setVerticalStretch()进行设置。

例子的原理是,通过将一张图缩放为正方形后,将此图划分为5*5的大小的拼图,在右方的部件(PuzzleWidget)中,为部件也同样划分为5*5的方块,保存其大小和拼图大小相同并保持不变。MainWindow将图设置好,并为拼图设置编号,并存入懂啊左边部件(PiecesList)中。当我们将左边部件往右变拖放时,每放置一块就保存放置的位置,并检查是否完成了拼图,放置前检查是否此位置可放(当前位置没有被保存)。最后如果检查完成了拼图就释放信号,让MainWindow接收并弹出提示对话框。

这个例子是个不错的例子。通过本例子不仅可以学到如何自定义拖放内容,处理拖放等,还可以学到如何自定义窗体部件。

[cpp] view
plain copy

int main(int argc, char *argv[])

{

Q_INIT_RESOURCE(puzzle);

QApplication app(argc, argv);

MainWindow window;

window.openImage(":/images/example.jpg"); // 打开资源图片

#ifdef Q_OS_SYMBIAN

window.showMaximized();

#else

window.show();

#endif

return app.exec();

}

[cpp] view
plain copy

// PiecesList继承自QListWidget,支持拖放

class PiecesList : public QListWidget

{

Q_OBJECT

public:

PiecesList(int pieceSize, QWidget *parent = 0);

void addPiece(QPixmap pixmap, QPoint location); // 加入拼图块

protected:

void dragEnterEvent(QDragEnterEvent *event);

void dragMoveEvent(QDragMoveEvent *event);

void dropEvent(QDropEvent *event);

void startDrag(Qt::DropActions supportedActions);

int m_PieceSize; // 保存拼图块的大小

};

[cpp] view
plain copy

PiecesList::PiecesList(int pieceSize, QWidget *parent)

: QListWidget(parent), m_PieceSize(pieceSize)

{

setDragEnabled(true); // 设置视图中的项可拖动

// 设置视图模型,默认对于ListMode是不可拖放的,IconMode可拖放

setViewMode(QListView::IconMode);

setIconSize(QSize(m_PieceSize, m_PieceSize));

setSpacing(10); // 设置空白,一个项周围空10像素

setAcceptDrops(true); // 设置可接收放置操作

setDropIndicatorShown(true); // 放置指示器显示

}

// 拖动进入

void PiecesList::dragEnterEvent(QDragEnterEvent *event)

{

if (event->mimeData()->hasFormat("image/x-puzzle-piece")) // 格式允许

event->accept();

else

event->ignore();

}

// 拖动移动

void PiecesList::dragMoveEvent(QDragMoveEvent *event)

{

if (event->mimeData()->hasFormat("image/x-puzzle-piece")) { // 是目标格式

event->setDropAction(Qt::MoveAction); // 设置为移动动作

event->accept(); // 接收

} else

event->ignore();

}

// 放置

void PiecesList::dropEvent(QDropEvent *event)

{

if (event->mimeData()->hasFormat("image/x-puzzle-piece")) {

QByteArray pieceData = event->mimeData()->data("image/x-puzzle-piece");

QDataStream dataStream(&pieceData, QIODevice::ReadOnly);

QPixmap pixmap;

QPoint location;

dataStream >> pixmap >> location; // 获取MIME文件信息中的数据

addPiece(pixmap, location); // 将数据(拼图)加入到PiecesList

event->setDropAction(Qt::MoveAction);

event->accept();

} else

event->ignore();

}

// 添加一块拼图项

void PiecesList::addPiece(QPixmap pixmap, QPoint location)

{

QListWidgetItem *pieceItem = new QListWidgetItem(this);

pieceItem->setIcon(QIcon(pixmap)); // 项图标

pieceItem->setData(Qt::UserRole, QVariant(pixmap)); // 项数据

pieceItem->setData(Qt::UserRole+1, location);

pieceItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable // 设置标记

| Qt::ItemIsDragEnabled);

}

// 开始拖动

void PiecesList::startDrag(Qt::DropActions /*supportedActions*/)

{

QListWidgetItem *item = currentItem(); // 获取选中的项

QByteArray itemData;

QDataStream dataStream(&itemData, QIODevice::WriteOnly); // 将信息写入itemData

QPixmap pixmap = qvariant_cast<QPixmap>(item->data(Qt::UserRole)); // 提取项的Qt::UserRole数据

QPoint location = item->data(Qt::UserRole+1).toPoint(); // 获取原缩放后图排列中的位置

dataStream << pixmap << location; // 存入数据流

QMimeData *mimeData = new QMimeData;

mimeData->setData("image/x-puzzle-piece", itemData); // 设置MIME文件数据信息

QDrag *drag = new QDrag(this); // 定义拖动

drag->setMimeData(mimeData); // 设置MIME文件

drag->setHotSpot(QPoint(pixmap.width()/2, pixmap.height()/2)); // 设置热点

drag->setPixmap(pixmap); // 设置拖动时的图片

if (drag->exec(Qt::MoveAction) == Qt::MoveAction) // 如果返回移动消息就删除本项

delete takeItem(row(item));

}

[cpp] view
plain copy

class PuzzleWidget : public QWidget

{

Q_OBJECT

public:

PuzzleWidget(int imageSize, QWidget *parent = 0); // 初始化固定的大小构造器

void clear(); // 清空

int pieceSize() const; // 拼图块大小

int imageSize() const; // 图大小

signals:

void puzzleCompleted(); // 完成拼图

protected: // 提供拖放的重新实现事件处理器

void dragEnterEvent(QDragEnterEvent *event);

void dragLeaveEvent(QDragLeaveEvent *event);

void dragMoveEvent(QDragMoveEvent *event);

void dropEvent(QDropEvent *event);

void mousePressEvent(QMouseEvent *event);

void paintEvent(QPaintEvent *event); // 绘制事件

private:

int findPiece(const QRect &pieceRect) const;

const QRect targetSquare(const QPoint &position) const;

QList<QPixmap> piecePixmaps; // 存储拼图块

QList<QRect> pieceRects; // 拼图块矩形表,保存放置进来的对应位置的拼图

QList<QPoint> pieceLocations; // 拼图位置

QRect highlightedRect; // 高亮矩形

int inPlace; //

int m_ImageSize; // 窗体大小数据

};

[cpp] view
plain copy

PuzzleWidget::PuzzleWidget(int imageSize, QWidget *parent)

: QWidget(parent), m_ImageSize(imageSize)

{

setAcceptDrops(true); // 设置可接收放置操作

setMinimumSize(m_ImageSize, m_ImageSize); // 设置窗体最小最大,使之大小固定不变

setMaximumSize(m_ImageSize, m_ImageSize);

}

// 清空

void PuzzleWidget::clear()

{

pieceLocations.clear(); // 将存储的那些数据都清空掉

piecePixmaps.clear();

pieceRects.clear();

highlightedRect = QRect();

inPlace = 0;

update(); // 更新

}

// 拖动进入

void PuzzleWidget::dragEnterEvent(QDragEnterEvent *event)

{

if (event->mimeData()->hasFormat("image/x-puzzle-piece"))

event->accept();

else

event->ignore();

}

// 拖动离开

void PuzzleWidget::dragLeaveEvent(QDragLeaveEvent *event)

{

QRect updateRect = highlightedRect;

highlightedRect = QRect();

update(updateRect);

event->accept();

}

// 拖动移动

void PuzzleWidget::dragMoveEvent(QDragMoveEvent *event)

{

QRect updateRect = highlightedRect.unite(targetSquare(event->pos()));

if (event->mimeData()->hasFormat("image/x-puzzle-piece")

&& findPiece(targetSquare(event->pos())) == -1) {

highlightedRect = targetSquare(event->pos());

event->setDropAction(Qt::MoveAction);

event->accept();

} else {

highlightedRect = QRect();

event->ignore();

}

update(updateRect);

}

// 放置

void PuzzleWidget::dropEvent(QDropEvent *event)

{

if (event->mimeData()->hasFormat("image/x-puzzle-piece") // MIME文件格式

&& findPiece(targetSquare(event->pos())) == -1) { // 没有放置过拼图

QByteArray pieceData = event->mimeData()->data("image/x-puzzle-piece"); // 获取MIME文件信息

QDataStream dataStream(&pieceData, QIODevice::ReadOnly); // 读取信息

QRect square = targetSquare(event->pos()); // 获取放置位置的矩形块

QPixmap pixmap;

QPoint location;

dataStream >> pixmap >> location; // 数据赋值到pixmap,location

pieceLocations.append(location); // 将location存入pieceLocation

piecePixmaps.append(pixmap); // 将拼图存入到piecePixmap

pieceRects.append(square); // 将矩形块加入到pieceRects,表示该位置矩形已占用

highlightedRect = QRect();

update(square);

event->setDropAction(Qt::MoveAction); // 设置为移动动作

event->accept();

if (location == QPoint(square.x()/pieceSize(), square.y()/pieceSize())) {

inPlace++;

if (inPlace == 25)

emit puzzleCompleted(); // 当拼完并排序正确,激发此信号

}

} else {

highlightedRect = QRect();

event->ignore();

}

}

// 查找对应位置

int PuzzleWidget::findPiece(const QRect &pieceRect) const

{

for (int i = 0; i < pieceRects.size(); ++i) {

if (pieceRect == pieceRects[i]) {

return i;

}

}

return -1;

}

// 鼠标按下

void PuzzleWidget::mousePressEvent(QMouseEvent *event)

{

QRect square = targetSquare(event->pos()); // 根据按下的位置,返回对应位置的矩形

int found = findPiece(square);

if (found == -1) // 对应位置没有放置过拼图

return;

QPoint location = pieceLocations[found]; //

QPixmap pixmap = piecePixmaps[found];

pieceLocations.removeAt(found);

piecePixmaps.removeAt(found);

pieceRects.removeAt(found);

if (location == QPoint(square.x()/pieceSize(), square.y()/pieceSize()))

inPlace--;

update(square);

QByteArray itemData;

QDataStream dataStream(&itemData, QIODevice::WriteOnly);

dataStream << pixmap << location;

QMimeData *mimeData = new QMimeData;

mimeData->setData("image/x-puzzle-piece", itemData);

QDrag *drag = new QDrag(this);

drag->setMimeData(mimeData);

drag->setHotSpot(event->pos() - square.topLeft());

drag->setPixmap(pixmap);

if (!(drag->exec(Qt::MoveAction) == Qt::MoveAction)) {

pieceLocations.insert(found, location);

piecePixmaps.insert(found, pixmap);

pieceRects.insert(found, square);

update(targetSquare(event->pos()));

if (location == QPoint(square.x()/pieceSize(), square.y()/pieceSize()))

inPlace++;

}

}

// 绘制事件

void PuzzleWidget::paintEvent(QPaintEvent *event)

{

QPainter painter;

painter.begin(this);

painter.fillRect(event->rect(), Qt::white); // 用白色填充本窗体的区域

if (highlightedRect.isValid()) { // 如果有高亮矩形块要显示

painter.setBrush(QColor("#ffcccc")); // 设置画刷为粉色

painter.setPen(Qt::NoPen); // 设置画笔:Qt::NoPen

painter.drawRect(highlightedRect.adjusted(0, 0, -1, -1)); // 将方块画出

}

for (int i = 0; i < pieceRects.size(); ++i) { // 绘制已经放置好了的拼图块

painter.drawPixmap(pieceRects[i], piecePixmaps[i]);

}

painter.end();

}

// 返回对应位置的矩形

const QRect PuzzleWidget::targetSquare(const QPoint &position) const

{

return QRect(position.x()/pieceSize() * pieceSize(), position.y()/pieceSize() * pieceSize(), pieceSize(), pieceSize());

}

// 返回拼图块大小(原图片被分为了5行5列)

int PuzzleWidget::pieceSize() const

{

return m_ImageSize / 5;

}

// 返回原图大小

int PuzzleWidget::imageSize() const

{

return m_ImageSize;

}

[cpp] view
plain copy

class MainWindow : public QMainWindow

{

Q_OBJECT

public:

MainWindow(QWidget *parent = 0);

public slots:

void openImage(const QString &path = QString()); // 载入图片

void setupPuzzle(); // 建立拼图槽

private slots:

void setCompleted(); // 拼图摆放完成提示槽

private:

void setupMenus(); // 设置菜单

void setupWidgets(); // 建立两个窗体部件

QPixmap puzzleImage; // 图片

PiecesList *piecesList; // 存放拼图的ListWidget

PuzzleWidget *puzzleWidget; // 摆放拼图的Widget

};

[cpp] view
plain copy

MainWindow::MainWindow(QWidget *parent)

: QMainWindow(parent)

{

setupMenus(); // 建立菜单

setupWidgets(); // 建立窗体部件

setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); // 大小策略

setWindowTitle(tr("Puzzle")); // 标题

}

void MainWindow::openImage(const QString &path) // 打开一个图片

{

QString fileName = path;

if (fileName.isNull())

fileName = QFileDialog::getOpenFileName(this,

tr("Open Image"), "", "Image Files (*.png *.jpg *.bmp)");

if (!fileName.isEmpty()) { // 载入图片

QPixmap newImage;

if (!newImage.load(fileName)) { // 未成功载入

QMessageBox::warning(this, tr("Open Image"),

tr("The image file could not be loaded."),

QMessageBox::Cancel);

return;

}

puzzleImage = newImage; // 将成功载入的图复制给puzleImage

setupPuzzle(); // 建立游戏

}

}

// 设置完成消息,并重新开始建立游戏

void MainWindow::setCompleted()

{

QMessageBox::information(this, tr("Puzzle Completed"),

tr("Congratulations! You have completed the puzzle!\n"

"Click OK to start again."),

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();

for (int y = 0; y < 5; ++y) { // 初始化拼图列表,将缩放后的图划分为5*5的拼图,QPoint指示几行几列编号

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));

}

}

qsrand(QCursor::pos().x() ^ QCursor::pos().y());

for (int i = 0; i < piecesList->count(); ++i) { // 将每个拼图块作为piecesList的项

if (int(2.0*qrand()/(RAND_MAX+1.0)) == 1) {

QListWidgetItem *item = piecesList->takeItem(i); // 移除并返回

piecesList->insertItem(0, item); // 插入

}

}

puzzleWidget->clear();

}

// 创建菜单

void MainWindow::setupMenus()

{

QMenu *fileMenu = menuBar()->addMenu(tr("&File")); // file菜单栏

QAction *openAction = fileMenu->addAction(tr("&Open...")); // open菜单项

openAction->setShortcuts(QKeySequence::Open);

QAction *exitAction = fileMenu->addAction(tr("E&xit")); // exit菜单项

exitAction->setShortcuts(QKeySequence::Quit);

QMenu *gameMenu = menuBar()->addMenu(tr("&Game")); // Game菜单栏

QAction *restartAction = gameMenu->addAction(tr("&Restart")); // restart菜单项

// 连接信号槽

connect(openAction, SIGNAL(triggered()), this, SLOT(openImage()));

connect(exitAction, SIGNAL(triggered()), qApp, SLOT(quit()));

connect(restartAction, SIGNAL(triggered()), this, SLOT(setupPuzzle()));

}

// 创建窗体部件并布局

void MainWindow::setupWidgets()

{

QFrame *frame = new QFrame; // 创建一个QFrame来包含两个拼图部件

QHBoxLayout *frameLayout = new QHBoxLayout(frame);

#if defined(Q_OS_SYMBIAN) || defined(Q_WS_SIMULATOR)

puzzleWidget = new PuzzleWidget(260);

#else

puzzleWidget = new PuzzleWidget(400); // 放置拼图的部件大小为固定的

#endif

piecesList = new PiecesList(puzzleWidget->pieceSize(), this); // 返回puzzleWidget拼图片大小

connect(puzzleWidget, SIGNAL(puzzleCompleted()), // puzzleWidget的完成后执行setCompleted

this, SLOT(setCompleted()), Qt::QueuedConnection);

frameLayout->addWidget(piecesList); // 添加部件

frameLayout->addWidget(puzzleWidget);

setCentralWidget(frame); // 设置中心部件

}

FROM: http://blog.csdn.net/xuguangsoft/article/details/8543092
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: