淘先锋技术网

首页 1 2 3 4 5 6 7

这个例子展示使用拖放的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
  1. int main(int argc, char *argv[])  
  2. {  
  3.     Q_INIT_RESOURCE(puzzle);  
  4.   
  5.     QApplication app(argc, argv);  
  6.     MainWindow window;  
  7.     window.openImage(":/images/example.jpg");  // 打开资源图片  
  8. #ifdef Q_OS_SYMBIAN  
  9.     window.showMaximized();  
  10. #else  
  11.     window.show();  
  12. #endif  
  13.     return app.exec();  
  14. }  
[cpp]  view plain  copy
  1. // PiecesList继承自QListWidget,支持拖放  
  2. class PiecesList : public QListWidget  
  3. {  
  4.     Q_OBJECT  
  5.   
  6. public:  
  7.     PiecesList(int pieceSize, QWidget *parent = 0);  
  8.     void addPiece(QPixmap pixmap, QPoint location);  // 加入拼图块  
  9.   
  10. protected:  
  11.     void dragEnterEvent(QDragEnterEvent *event);  
  12.     void dragMoveEvent(QDragMoveEvent *event);  
  13.     void dropEvent(QDropEvent *event);  
  14.     void startDrag(Qt::DropActions supportedActions);  
  15.   
  16.     int m_PieceSize;   // 保存拼图块的大小  
  17. };  
[cpp]  view plain  copy
  1. PiecesList::PiecesList(int pieceSize, QWidget *parent)  
  2.     : QListWidget(parent), m_PieceSize(pieceSize)  
  3. {  
  4.     setDragEnabled(true);    // 设置视图中的项可拖动  
  5.     // 设置视图模型,默认对于ListMode是不可拖放的,IconMode可拖放  
  6.     setViewMode(QListView::IconMode);  
  7.     setIconSize(QSize(m_PieceSize, m_PieceSize));  
  8.     setSpacing(10);                // 设置空白,一个项周围空10像素  
  9.     setAcceptDrops(true);          // 设置可接收放置操作  
  10.     setDropIndicatorShown(true);   // 放置指示器显示  
  11. }  
  12.   
  13. // 拖动进入  
  14. void PiecesList::dragEnterEvent(QDragEnterEvent *event)  
  15. {  
  16.    if (event->mimeData()->hasFormat("image/x-puzzle-piece")) // 格式允许  
  17.         event->accept();  
  18.     else  
  19.         event->ignore();  
  20. }  
  21.   
  22. // 拖动移动  
  23. void PiecesList::dragMoveEvent(QDragMoveEvent *event)  
  24. {  
  25.     if (event->mimeData()->hasFormat("image/x-puzzle-piece")) { // 是目标格式  
  26.         event->setDropAction(Qt::MoveAction);       // 设置为移动动作  
  27.         event->accept();                            // 接收  
  28.     } else  
  29.         event->ignore();  
  30. }  
  31.   
  32. // 放置  
  33. void PiecesList::dropEvent(QDropEvent *event)  
  34. {  
  35.     if (event->mimeData()->hasFormat("image/x-puzzle-piece")) {  
  36.         QByteArray pieceData = event->mimeData()->data("image/x-puzzle-piece");  
  37.         QDataStream dataStream(&pieceData, QIODevice::ReadOnly);  
  38.         QPixmap pixmap;  
  39.         QPoint location;  
  40.         dataStream >> pixmap >> location;  // 获取MIME文件信息中的数据  
  41.   
  42.         addPiece(pixmap, location);        // 将数据(拼图)加入到PiecesList  
  43.   
  44.         event->setDropAction(Qt::MoveAction);  
  45.         event->accept();  
  46.     } else  
  47.         event->ignore();  
  48. }  
  49.   
  50. // 添加一块拼图项  
  51. void PiecesList::addPiece(QPixmap pixmap, QPoint location)  
  52. {  
  53.     QListWidgetItem *pieceItem = new QListWidgetItem(this);  
  54.     pieceItem->setIcon(QIcon(pixmap));          // 项图标  
  55.     pieceItem->setData(Qt::UserRole, QVariant(pixmap));  // 项数据  
  56.     pieceItem->setData(Qt::UserRole+1, location);  
  57.     pieceItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable  // 设置标记  
  58.                         | Qt::ItemIsDragEnabled);  
  59. }  
  60.   
  61. // 开始拖动  
  62. void PiecesList::startDrag(Qt::DropActions )  
  63. {  
  64.     QListWidgetItem *item = currentItem();      // 获取选中的项  
  65.   
  66.     QByteArray itemData;  
  67.     QDataStream dataStream(&itemData, QIODevice::WriteOnly);  // 将信息写入itemData  
  68.     QPixmap pixmap = qvariant_cast<QPixmap>(item->data(Qt::UserRole));  // 提取项的Qt::UserRole数据  
  69.     QPoint location = item->data(Qt::UserRole+1).toPoint();   // 获取原缩放后图排列中的位置  
  70.   
  71.     dataStream << pixmap << location;       // 存入数据流  
  72.   
  73.     QMimeData *mimeData = new QMimeData;  
  74.     mimeData->setData("image/x-puzzle-piece", itemData);  // 设置MIME文件数据信息  
  75.   
  76.     QDrag *drag = new QDrag(this);     // 定义拖动  
  77.     drag->setMimeData(mimeData);       // 设置MIME文件  
  78.     drag->setHotSpot(QPoint(pixmap.width()/2, pixmap.height()/2));  // 设置热点  
  79.     drag->setPixmap(pixmap);           // 设置拖动时的图片  
  80.   
  81.     if (drag->exec(Qt::MoveAction) == Qt::MoveAction)  // 如果返回移动消息就删除本项  
  82.         delete takeItem(row(item));  
  83. }  
[cpp]  view plain  copy
  1. class PuzzleWidget : public QWidget  
  2. {  
  3.     Q_OBJECT  
  4.   
  5. public:  
  6.     PuzzleWidget(int imageSize, QWidget *parent = 0); // 初始化固定的大小构造器  
  7.     void clear();               // 清空  
  8.   
  9.     int pieceSize() const;      // 拼图块大小  
  10.     int imageSize() const;      // 图大小  
  11.   
  12. signals:  
  13.     void puzzleCompleted();     // 完成拼图  
  14.   
  15. protected// 提供拖放的重新实现事件处理器  
  16.     void dragEnterEvent(QDragEnterEvent *event);  
  17.     void dragLeaveEvent(QDragLeaveEvent *event);  
  18.     void dragMoveEvent(QDragMoveEvent *event);  
  19.     void dropEvent(QDropEvent *event);  
  20.     void mousePressEvent(QMouseEvent *event);  
  21.     void paintEvent(QPaintEvent *event);  // 绘制事件  
  22.   
  23. private:  
  24.     int findPiece(const QRect &pieceRect) const;  
  25.     const QRect targetSquare(const QPoint &position) const;  
  26.   
  27.     QList<QPixmap> piecePixmaps;    // 存储拼图块  
  28.     QList<QRect> pieceRects;        // 拼图块矩形表,保存放置进来的对应位置的拼图  
  29.     QList<QPoint> pieceLocations;   // 拼图位置  
  30.     QRect highlightedRect;          // 高亮矩形  
  31.     int inPlace;                    //  
  32.   
  33.     int m_ImageSize;                // 窗体大小数据  
  34. };  
[cpp]  view plain  copy
  1. PuzzleWidget::PuzzleWidget(int imageSize, QWidget *parent)  
  2.     : QWidget(parent), m_ImageSize(imageSize)  
  3. {  
  4.     setAcceptDrops(true);       // 设置可接收放置操作  
  5.     setMinimumSize(m_ImageSize, m_ImageSize);  // 设置窗体最小最大,使之大小固定不变  
  6.     setMaximumSize(m_ImageSize, m_ImageSize);  
  7. }  
  8.   
  9. // 清空  
  10. void PuzzleWidget::clear()  
  11. {  
  12.     pieceLocations.clear();    // 将存储的那些数据都清空掉  
  13.     piecePixmaps.clear();  
  14.     pieceRects.clear();  
  15.     highlightedRect = QRect();  
  16.     inPlace = 0;  
  17.     update();    // 更新  
  18. }  
  19.   
  20. // 拖动进入  
  21. void PuzzleWidget::dragEnterEvent(QDragEnterEvent *event)  
  22. {  
  23.     if (event->mimeData()->hasFormat("image/x-puzzle-piece"))  
  24.         event->accept();  
  25.     else  
  26.         event->ignore();  
  27. }  
  28.   
  29. // 拖动离开  
  30. void PuzzleWidget::dragLeaveEvent(QDragLeaveEvent *event)  
  31. {  
  32.     QRect updateRect = highlightedRect;  
  33.     highlightedRect = QRect();  
  34.     update(updateRect);  
  35.     event->accept();  
  36. }  
  37.   
  38. // 拖动移动  
  39. void PuzzleWidget::dragMoveEvent(QDragMoveEvent *event)  
  40. {  
  41.     QRect updateRect = highlightedRect.unite(targetSquare(event->pos()));  
  42.   
  43.     if (event->mimeData()->hasFormat("image/x-puzzle-piece")  
  44.         && findPiece(targetSquare(event->pos())) == -1) {  
  45.   
  46.         highlightedRect = targetSquare(event->pos());  
  47.         event->setDropAction(Qt::MoveAction);  
  48.         event->accept();  
  49.     } else {  
  50.         highlightedRect = QRect();  
  51.         event->ignore();  
  52.     }  
  53.   
  54.     update(updateRect);  
  55. }  
  56.   
  57. // 放置  
  58. void PuzzleWidget::dropEvent(QDropEvent *event)  
  59. {  
  60.     if (event->mimeData()->hasFormat("image/x-puzzle-piece"// MIME文件格式  
  61.         && findPiece(targetSquare(event->pos())) == -1) {    // 没有放置过拼图  
  62.   
  63.         QByteArray pieceData = event->mimeData()->data("image/x-puzzle-piece");  // 获取MIME文件信息  
  64.         QDataStream dataStream(&pieceData, QIODevice::ReadOnly); // 读取信息  
  65.         QRect square = targetSquare(event->pos());  // 获取放置位置的矩形块  
  66.         QPixmap pixmap;  
  67.         QPoint location;  
  68.         dataStream >> pixmap >> location;    // 数据赋值到pixmap,location  
  69.   
  70.         pieceLocations.append(location);     // 将location存入pieceLocation  
  71.         piecePixmaps.append(pixmap);         // 将拼图存入到piecePixmap  
  72.         pieceRects.append(square);           // 将矩形块加入到pieceRects,表示该位置矩形已占用  
  73.   
  74.         highlightedRect = QRect();  
  75.         update(square);  
  76.   
  77.         event->setDropAction(Qt::MoveAction);   // 设置为移动动作  
  78.         event->accept();  
  79.   
  80.         if (location == QPoint(square.x()/pieceSize(), square.y()/pieceSize())) {  
  81.             inPlace++;  
  82.             if (inPlace == 25)  
  83.                 emit puzzleCompleted();         // 当拼完并排序正确,激发此信号  
  84.         }  
  85.     } else {  
  86.         highlightedRect = QRect();  
  87.         event->ignore();  
  88.     }  
  89. }  
  90.   
  91. // 查找对应位置  
  92. int PuzzleWidget::findPiece(const QRect &pieceRect) const  
  93. {  
  94.     for (int i = 0; i < pieceRects.size(); ++i) {  
  95.         if (pieceRect == pieceRects[i]) {  
  96.             return i;  
  97.         }  
  98.     }  
  99.     return -1;  
  100. }  
  101.   
  102. // 鼠标按下  
  103. void PuzzleWidget::mousePressEvent(QMouseEvent *event)  
  104. {  
  105.     QRect square = targetSquare(event->pos());   // 根据按下的位置,返回对应位置的矩形  
  106.     int found = findPiece(square);  
  107.   
  108.     if (found == -1)  // 对应位置没有放置过拼图  
  109.         return;  
  110.   
  111.     QPoint location = pieceLocations[found];  //  
  112.     QPixmap pixmap = piecePixmaps[found];  
  113.     pieceLocations.removeAt(found);  
  114.     piecePixmaps.removeAt(found);  
  115.     pieceRects.removeAt(found);  
  116.   
  117.     if (location == QPoint(square.x()/pieceSize(), square.y()/pieceSize()))  
  118.         inPlace--;  
  119.   
  120.     update(square);  
  121.   
  122.     QByteArray itemData;  
  123.     QDataStream dataStream(&itemData, QIODevice::WriteOnly);  
  124.   
  125.     dataStream << pixmap << location;  
  126.   
  127.     QMimeData *mimeData = new QMimeData;  
  128.     mimeData->setData("image/x-puzzle-piece", itemData);  
  129.   
  130.     QDrag *drag = new QDrag(this);  
  131.     drag->setMimeData(mimeData);  
  132.     drag->setHotSpot(event->pos() - square.topLeft());  
  133.     drag->setPixmap(pixmap);  
  134.   
  135.     if (!(drag->exec(Qt::MoveAction) == Qt::MoveAction)) {  
  136.         pieceLocations.insert(found, location);  
  137.         piecePixmaps.insert(found, pixmap);  
  138.         pieceRects.insert(found, square);  
  139.         update(targetSquare(event->pos()));  
  140.   
  141.         if (location == QPoint(square.x()/pieceSize(), square.y()/pieceSize()))  
  142.             inPlace++;  
  143.     }  
  144. }  
  145.   
  146. // 绘制事件  
  147. void PuzzleWidget::paintEvent(QPaintEvent *event)  
  148. {  
  149.     QPainter painter;  
  150.     painter.begin(this);  
  151.     painter.fillRect(event->rect(), Qt::white); // 用白色填充本窗体的区域  
  152.   
  153.     if (highlightedRect.isValid()) {    // 如果有高亮矩形块要显示  
  154.         painter.setBrush(QColor("#ffcccc"));  // 设置画刷为粉色  
  155.         painter.setPen(Qt::NoPen);            // 设置画笔:Qt::NoPen  
  156.         painter.drawRect(highlightedRect.adjusted(0, 0, -1, -1)); // 将方块画出  
  157.     }  
  158.   
  159.     for (int i = 0; i < pieceRects.size(); ++i) {  // 绘制已经放置好了的拼图块  
  160.         painter.drawPixmap(pieceRects[i], piecePixmaps[i]);  
  161.     }  
  162.     painter.end();  
  163. }  
  164.   
  165. // 返回对应位置的矩形  
  166. const QRect PuzzleWidget::targetSquare(const QPoint &position) const  
  167. {  
  168.     return QRect(position.x()/pieceSize() * pieceSize(), position.y()/pieceSize() * pieceSize(), pieceSize(), pieceSize());  
  169. }  
  170.   
  171. // 返回拼图块大小(原图片被分为了5行5列)  
  172. int PuzzleWidget::pieceSize() const  
  173. {  
  174.     return m_ImageSize / 5;  
  175. }  
  176.   
  177. // 返回原图大小  
  178. int PuzzleWidget::imageSize() const  
  179. {  
  180.     return m_ImageSize;  
  181. }  
[cpp]  view plain  copy
  1. class MainWindow : public QMainWindow  
  2. {  
  3.     Q_OBJECT  
  4.   
  5. public:  
  6.     MainWindow(QWidget *parent = 0);  
  7.   
  8. public slots:  
  9.     void openImage(const QString &path = QString()); // 载入图片  
  10.     void setupPuzzle();             // 建立拼图槽  
  11.   
  12. private slots:  
  13.     void setCompleted();            // 拼图摆放完成提示槽  
  14.   
  15. private:  
  16.     void setupMenus();              // 设置菜单  
  17.     void setupWidgets();            // 建立两个窗体部件  
  18.   
  19.     QPixmap puzzleImage;            // 图片  
  20.     PiecesList *piecesList;         // 存放拼图的ListWidget  
  21.     PuzzleWidget *puzzleWidget;     // 摆放拼图的Widget  
  22. };  
[cpp]  view plain  copy
  1. MainWindow::MainWindow(QWidget *parent)  
  2.     : QMainWindow(parent)  
  3. {  
  4.     setupMenus();       // 建立菜单  
  5.     setupWidgets();     // 建立窗体部件  
  6.   
  7.     setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); // 大小策略  
  8.     setWindowTitle(tr("Puzzle"));       // 标题  
  9. }  
  10.   
  11. void MainWindow::openImage(const QString &path)  // 打开一个图片  
  12. {  
  13.     QString fileName = path;  
  14.   
  15.     if (fileName.isNull())  
  16.         fileName = QFileDialog::getOpenFileName(this,  
  17.             tr("Open Image"), """Image Files (*.png *.jpg *.bmp)");  
  18.   
  19.     if (!fileName.isEmpty()) {  // 载入图片  
  20.         QPixmap newImage;  
  21.         if (!newImage.load(fileName)) {  // 未成功载入  
  22.             QMessageBox::warning(this, tr("Open Image"),  
  23.                                   tr("The image file could not be loaded."),  
  24.                                   QMessageBox::Cancel);  
  25.             return;  
  26.         }  
  27.         puzzleImage = newImage;  // 将成功载入的图复制给puzleImage  
  28.         setupPuzzle();           // 建立游戏  
  29.     }  
  30. }  
  31.   
  32. // 设置完成消息,并重新开始建立游戏  
  33. void MainWindow::setCompleted()  
  34. {  
  35.     QMessageBox::information(this, tr("Puzzle Completed"),  
  36.         tr("Congratulations! You have completed the puzzle!\n"  
  37.            "Click OK to start again."),  
  38.         QMessageBox::Ok);  
  39.   
  40.     setupPuzzle();  
  41. }  
  42.   
  43. // 建立游戏  
  44. void MainWindow::setupPuzzle()  
  45. {  
  46.     int size = qMin(puzzleImage.width(), puzzleImage.height());  // 获取长宽中的较小者  
  47.     puzzleImage = puzzleImage.copy((puzzleImage.width() - size)/2,  
  48.         (puzzleImage.height() - size)/2, size, size).scaled(puzzleWidget->width(),  
  49.             puzzleWidget->height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);  
  50.   
  51.     piecesList->clear();  
  52.   
  53.     for (int y = 0; y < 5; ++y) {   // 初始化拼图列表,将缩放后的图划分为5*5的拼图,QPoint指示几行几列编号  
  54.         for (int x = 0; x < 5; ++x) {  
  55.             int pieceSize = puzzleWidget->pieceSize();  
  56.             QPixmap pieceImage = puzzleImage.copy(x * pieceSize, y * pieceSize, pieceSize, pieceSize);  
  57.             piecesList->addPiece(pieceImage, QPoint(x, y));  
  58.         }  
  59.     }  
  60.   
  61.     qsrand(QCursor::pos().x() ^ QCursor::pos().y());  
  62.   
  63.     for (int i = 0; i < piecesList->count(); ++i) {  // 将每个拼图块作为piecesList的项  
  64.         if (int(2.0*qrand()/(RAND_MAX+1.0)) == 1) {  
  65.             QListWidgetItem *item = piecesList->takeItem(i);  // 移除并返回  
  66.             piecesList->insertItem(0, item);         // 插入  
  67.         }  
  68.     }  
  69.   
  70.     puzzleWidget->clear();  
  71. }  
  72.   
  73. // 创建菜单  
  74. void MainWindow::setupMenus()  
  75. {  
  76.     QMenu *fileMenu = menuBar()->addMenu(tr("&File")); // file菜单栏  
  77.   
  78.     QAction *openAction = fileMenu->addAction(tr("&Open...")); // open菜单项  
  79.     openAction->setShortcuts(QKeySequence::Open);  
  80.   
  81.     QAction *exitAction = fileMenu->addAction(tr("E&xit"));   // exit菜单项  
  82.     exitAction->setShortcuts(QKeySequence::Quit);  
  83.   
  84.     QMenu *gameMenu = menuBar()->addMenu(tr("&Game")); // Game菜单栏  
  85.   
  86.     QAction *restartAction = gameMenu->addAction(tr("&Restart"));  // restart菜单项  
  87.   
  88.     // 连接信号槽  
  89.     connect(openAction, SIGNAL(triggered()), this, SLOT(openImage()));  
  90.     connect(exitAction, SIGNAL(triggered()), qApp, SLOT(quit()));  
  91.     connect(restartAction, SIGNAL(triggered()), this, SLOT(setupPuzzle()));  
  92. }  
  93.   
  94. // 创建窗体部件并布局  
  95. void MainWindow::setupWidgets()  
  96. {  
  97.     QFrame *frame = new QFrame;  // 创建一个QFrame来包含两个拼图部件  
  98.     QHBoxLayout *frameLayout = new QHBoxLayout(frame);  
  99. #if defined(Q_OS_SYMBIAN) || defined(Q_WS_SIMULATOR)  
  100.     puzzleWidget = new PuzzleWidget(260);  
  101. #else  
  102.     puzzleWidget = new PuzzleWidget(400);  // 放置拼图的部件大小为固定的  
  103. #endif  
  104.   
  105.     piecesList = new PiecesList(puzzleWidget->pieceSize(), this); // 返回puzzleWidget拼图片大小  
  106.   
  107.   
  108.     connect(puzzleWidget, SIGNAL(puzzleCompleted()),        // puzzleWidget的完成后执行setCompleted  
  109.             this, SLOT(setCompleted()), Qt::QueuedConnection);  
  110.   
  111.     frameLayout->addWidget(piecesList);     // 添加部件  
  112.     frameLayout->addWidget(puzzleWidget);  
  113.     setCentralWidget(frame);                // 设置中心部件  
  114. }  



FROM: http://blog.csdn.net/xuguangsoft/article/details/8543092