如果不了解Qt drag-drop 的建议先看一下 Qt 实现拖放内容 drag – drop 【简单明了】 否则看起来会一头雾水 看一下官方的介绍: 译文:这个例子是一个简单的拼图游戏的实现,它使用了Qt的模型/视图框架提供的对拖放的内置支持。拖放拼图的例子展示了许多相同的特性,但是采用了另一种方法,即在应用程序级别使用Qt的拖放API来处理拖放操作。 这个拼图的demo 还是能学到东西的 项目叫做 puzzle 大家可以去官方demo 里找一下 玩一玩 项目的结构如下 main 初始化了资源 图片就是下面 类也不复杂 private: 他是自定义了 模型 等会看 整个的结构是这样的 把这个几个函数的实现都挨个看吧 用了 水平布局 把 左边的 listView 和 右边的 puzzleWidget 合起来 listview 设置了可以拖拽 然后给 listview 设置自定义的 model 打开图片 把.h 里面声明的 puzzleImage 赋值 新读进来的图片 这两句的 意思是 (puzzleImage.width() – size) / 2 用笔在纸上画一下就明白了 然后把缩放好的图片 给到 model model 的实现 我们下面看 不了解 自定义 model 的要去看一下 否则会看不懂 前面的 几个函数 就是重载 基类的函数 只有这几个是自己实现的 beginRemoveRows(QModelIndex(), 0, 24); 只有我们重载了 基类的 removeRows 函数 上面的就必须要写 把传进来的图片 分成了 5行5列 的 25个 格子 每个格子的像素是 m_PieceSize 然后把这些 图片格子(拼图块) 以随机的方式 插入到list里面 获取模型的数据 根据枚举的不同 返回的类型不同 userRole 是我们自定义的 这里可能有人看不懂了 我给你们弄个 gif 仔细看 逻辑是这样的 当拖走了一个拼图块 左边部分 那么 剩余的图块会进行一个排序 从拖走的位置 开始补齐 这里要明白 必须要看我上面发的链接 这个就是包装拖拽数据的类的密码头 包装我们的数据 我们的拼图为啥能从 左边的 widget 移动到 一个 widget 是因为 drag 和 drop 的实现 把 pixmap 和 位置 以数据流的形式写到了 QbyteArray 然后给到 QMimeData 这个地方就用了 我们说的自定义的用户的枚举 来获取不同的信息 又学到了一点 这个函数 其实是 拖到这里放下的函数 因为我们可以把拼图从右边拖回到左边 先判断 hasFormat mimeTypes 对不对 把数据插入 然后 把每个拼图 向后移动一个位置 把它塞进去 ok 这边的整个类就结束了 对这块不了解的 可能看不太懂 继续看 右边的拼图类 这边 还是重载了基类的方法 拖拽进入事件 一个个看吧 设置 接收拖拽放下事件 这个必须要写 不写接收不到拖拽放下事件 设置窗口的固定大小 就是加载的图片处理完后的的大小 还是判断 mimeType 这里是 拖拽移动时 绘制后面的 高亮矩形块 下面的 放下事件 和 点击事件 写到这里 基本的也都说完了 反正就是 你要看懂这个拼图项目 首先要搞懂 自定义model 看一下 MVD 模型 这些在我的其他我文章都有写 可以看一下 然后在来看这个 就比较清晰了
前言:
mainwindow 主界面
piecesmodel 拼图块模型
puzzlewidget 拼图窗口main :
实例化了主窗体
加载了一个图片
mainWindow.h
class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = 0); public slots: void openImage(); void loadImage(const QString &path); void setupPuzzle(); private slots: void setCompleted(); private: void setupMenus(); void setupWidgets(); QPixmap puzzleImage; QListView *piecesList; PuzzleWidget *puzzleWidget; PiecesModel *model; };
一个 存放 拼图照片的 pixmap
一个 存放左边拼图块的 listview
右边的拼图窗口
拼图的块模型
#include "mainwindow.h" #include "piecesmodel.h" #include "puzzlewidget.h" #include <QDebug> #include <QtWidgets> #include <stdlib.h> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { setupMenus(); setupWidgets(); model = new PiecesModel(puzzleWidget->pieceSize(), this); piecesList->setModel(model); setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); setWindowTitle(tr("Puzzle")); } void MainWindow::openImage() { const QString fileName = QFileDialog::getOpenFileName(this, tr("Open Image"), QString(), tr("Image Files (*.png *.jpg *.bmp)")); if (!fileName.isEmpty()) loadImage(fileName); } 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::Cancel); return; } puzzleImage = newImage; 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->imageSize(), puzzleWidget->imageSize(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); qsrand(QCursor::pos().x() ^ QCursor::pos().y()); model->addPieces(puzzleImage); puzzleWidget->clear(); } void MainWindow::setupMenus() { QMenu *fileMenu = menuBar()->addMenu(tr("&File")); QAction *openAction = fileMenu->addAction(tr("&Open...")); openAction->setShortcuts(QKeySequence::Open); QAction *exitAction = fileMenu->addAction(tr("E&xit")); exitAction->setShortcuts(QKeySequence::Quit); QMenu *gameMenu = menuBar()->addMenu(tr("&Game")); QAction *restartAction = gameMenu->addAction(tr("&Restart")); connect(openAction, &QAction::triggered, this, &MainWindow::openImage); connect(exitAction, &QAction::triggered, qApp, &QCoreApplication::quit); connect(restartAction, &QAction::triggered, this, &MainWindow::setupPuzzle); } void MainWindow::setupWidgets() { QFrame *frame = new QFrame; QHBoxLayout *frameLayout = new QHBoxLayout(frame); puzzleWidget = new PuzzleWidget(400); piecesList = new QListView; piecesList->setDragEnabled(true); piecesList->setViewMode(QListView::IconMode); piecesList->setIconSize(QSize(puzzleWidget->pieceSize() - 20, puzzleWidget->pieceSize() - 20)); piecesList->setGridSize(QSize(puzzleWidget->pieceSize(), puzzleWidget->pieceSize())); piecesList->setSpacing(10); piecesList->setMovement(QListView::Snap); piecesList->setAcceptDrops(true); piecesList->setDropIndicatorShown(true); PiecesModel *model = new PiecesModel(puzzleWidget->pieceSize(), this); piecesList->setModel(model); connect(puzzleWidget, &PuzzleWidget::puzzleCompleted, this, &MainWindow::setCompleted, Qt::QueuedConnection); frameLayout->addWidget(piecesList); frameLayout->addWidget(puzzleWidget); setCentralWidget(frame); }
构造:
setupMenus()
顶部的菜单栏 没啥说的setupWidgets()
设置了 view mode 是icon
设置了 icon 的大小 puzzleWidget->pieceSize() 是多少 等会去看这个类
设置 网格的大小
设置间距
设置 item 移动时 吸附到指定的网格上;
设置 item 在拖动和删除项时是否显示拖放指示器。openImage()
loadImage()
setupPuzzle()
把图片 缩放为 为 size 的正方形
size 是 取最小的一方 比如 800*600 的图片 那么就是取 600
取 600 也不是从 0到600
而是去取 中间的 600
为什么?
( 800 -600 /2) =100
从 100 的位置 开始取 取600 【100,700】PiecesModel.h
#include <QAbstractListModel> #include <QList> #include <QPixmap> #include <QPoint> #include <QStringList> QT_BEGIN_NAMESPACE class QMimeData; QT_END_NAMESPACE class PiecesModel : public QAbstractListModel { Q_OBJECT public: explicit PiecesModel(int pieceSize, QObject *parent = 0); QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; bool removeRows(int row, int count, const QModelIndex &parent) override; bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override; QMimeData *mimeData(const QModelIndexList &indexes) const override; QStringList mimeTypes() const override; int rowCount(const QModelIndex &parent) const override; Qt::DropActions supportedDropActions() const override; void addPiece(const QPixmap &pixmap, const QPoint &location); void addPieces(const QPixmap& pixmap); private: QList<QPoint> locations; QList<QPixmap> pixmaps; int m_PieceSize; };
addPieces(const QPixmap& pixmap)
刚才 我们处理好的图片 就是传递给了这个函数
endRemoveRows();
for (int y = 0; y < 5; ++y) { for (int x = 0; x < 5; ++x) { QPixmap pieceImage = pixmap.copy(x*m_PieceSize, y*m_PieceSize, m_PieceSize, m_PieceSize); addPiece(pieceImage, QPoint(x, y)); } }
他是多少 等会 puzzleWidget 里会说addPiece(const QPixmap &pixmap, const QPoint &location)
有的是在头部 有的是在尾部插入 打乱了顺序data(const QModelIndex &index, int role) const
有 icon 有 pixmap 有 位置
removeRows(int row, int count, const QModelIndex &parent)
而不是 空着那个位置mimeTypes() const
mimeData(const QModelIndexList &indexes) const
其实就是把 左边的 数据 发送到 右边的窗口 在把他画出来
这里数据的封装 必须用 QMimeData
这块不明白的去看文章头部的链接 看完就懂了
dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
然后 把 包装的数据 (拼图)解包
我觉得我说的够详细了PuzzleWidget.h
#include <QList> #include <QPixmap> #include <QPoint> #include <QWidget> QT_BEGIN_NAMESPACE class QDragEnterEvent; class QDropEvent; class QMouseEvent; QT_END_NAMESPACE class PuzzleWidget : public QWidget { Q_OBJECT public: explicit PuzzleWidget(int imageSize, QWidget *parent = 0); void clear(); int pieceSize() const; int imageSize() const; signals: void puzzleCompleted(); protected: 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; 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; };
拖拽离开事件
拖拽移动事件
放下事件
鼠标按压事件
绘图事件构造
dragEnterEvent(QDragEnterEvent *event)
比如 快递是我们要的东西 才能签收dragMoveEvent(QDragMoveEvent *event)
painter 绘制背部高亮矩形框和拼图
都是 把数据包装 删除拼图 各种或者 把数据拆开 添加进容器 绘制出来
和上面 model 的实现类似void PuzzleWidget::dropEvent(QDropEvent *event) { if (event->mimeData()->hasFormat("image/x-puzzle-piece") && findPiece(targetSquare(event->pos())) == -1) { QByteArray pieceData = event->mimeData()->data("image/x-puzzle-piece"); QDataStream stream(&pieceData, QIODevice::ReadOnly); QRect square = targetSquare(event->pos()); QPixmap pixmap; QPoint location; stream >> pixmap >> location; pieceLocations.append(location); piecePixmaps.append(pixmap); pieceRects.append(square); 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->start(Qt::MoveAction) == 0) { 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++; } }
太长了 我也不想写了 就到这吧
了解 drop 和 drag 的机制
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算