Qt--拖放操作

x33g5p2x  于2022-03-18 转载在 其他  
字(6.8k)|赞(0)|评价(0)|浏览(267)

Qt–拖放操作

1 使用拖放打开文件

将桌面上的.txt文件拖入程序打开。
新建Widget项目,项目名称为mydragdrop,类名和积累保持MainWindow和QMainWindow不变。建立完项目后,往界面上拖入一个Text Edit部件。然后在mainwindow.h文件中添加函数声明:

  1. protected:
  2. void dragEnterEvent(QDragEnterEvent *event); // 拖动进入事件
  3. void dropEvent(QDropEvent *event); // 放下事件

然后再mainwindow.cpp文件中添加头文件

  1. #include <QDragEnterEvent>
  2. #include <QUrl>
  3. #include <QFile>
  4. #include <QTextStream>
  5. #include <QMimeData>

最后对二个事件处理函数定义

  1. void MainWindow::dragEnterEvent(QDragEnterEvent *event) // 拖动进入事件
  2. {
  3. if(event->mimeData()->hasUrls()) // 数据中是否包含URL
  4. event->acceptProposedAction(); // 如果是则接收动作
  5. else event->ignore(); // 否则忽略该事件
  6. }
  7. void MainWindow::dropEvent(QDropEvent *event) // 放下事件
  8. {
  9. const QMimeData *mimeData = event->mimeData(); // 获取MIME数据
  10. if(mimeData->hasUrls()){ // 如果数据中包含URL
  11. QList<QUrl> urlList = mimeData->urls(); // 获取URL列表
  12. // 将其中第一个URL表示为本地文件路径
  13. QString fileName = urlList.at(0).toLocalFile();
  14. if(!fileName.isEmpty()){ // 如果文件路径不为空
  15. QFile file(fileName); // 建立QFile对象并且以只读方式打开该文件
  16. if(!file.open(QIODevice::ReadOnly)) return;
  17. QTextStream in(&file); // 建立文本流对象
  18. ui->textEdit->setText(in.readAll()); // 将文件中所有内容读入编辑器
  19. }
  20. }
  21. }

当鼠标拖拽一个数据进入主窗口时,就会触发dragEnterEvent()事件处理函数,从而获取其中的MIME 数据;然后查看它是否包含URL路径,因为拖入的文本文件实际上就是拖入了它的路径,这就是event→mimeData()→hasUrls()实现的功能。

如果有这样的数据,就接收它,否则忽略该事件。QMimeData类中提供了几个函数来处理常见的MIME数据,如表所列。

当松开鼠标左键,将数据放入主窗口时就会触发dropEvent()事件处理函数,这里获取了MIME数据中的URL列表。

因为拖入的只有一个文件,所以获取了列表中的第一个条目,并使用toLocalFile()函数将它转换为本地文件路径。

然后使用QFileQTextStream将文件中的数据读入编辑器中。

最后,进入 mainwindow.cpp文件,在构造函数中添加一行代码:

  1. setAcceptDrops(true);

这样主窗口就可以接收放下事件了。这时先运行程序,然后从桌面上将一个文本文件拖入程序主窗口界面(不是里面的Text Edit部件中),可以看到文本编辑器中显示了文本文件中的内容。

Qt中QList<T>的用法

这里用到了Qt中QList<T>的用法
QList以列表形态存储并管理值,并能进行基于快速索引的访问,也可以进行快速的数据删除操作。

QList使用运算符将项目添加到列表

  1. QList<QString> list;
  2. list<<"one"<<"two"<<“three”;
  3. //list:["one","two","three"]

QList通过比较运算符来获得返回值

  1. if(list[0]=="boy")
  2. list[0]="bossa";

QList实现指针数组后,通过快速运算获取存储到QList的值。并且使用函数at()可以很容易的获得存储到列表的位置。

  1. for( int i=0; i< list.size(); ++i)
  2. {
  3. if(list.at(i)=="J")
  4. cout <<"J at position " << i <<endl;
  5. }

2 自定义拖放操作

下面再来看一个在窗口中拖动图片的例子,实现的功能就是在窗口中有一个图片,可以随意拖动它。这里需要使用到自定义的MIME类型。

新建Qt Widgets应用,项目名称改为 imagedrag-drop,类名和基类保持Main Window 和QMainWindow不变。完成后,在 mainwindow.h文件中对几个事件处理函数进行声明:

  1. protected:
  2. void mousePressEvent(QMouseEvent *event); // 鼠标按下事件
  3. void dragEnterEvent(QDragEnterEvent *event); // 拖动进入事件
  4. void dragMoveEvent(QDragMoveEvent *event); // 拖动事件
  5. void dropEvent(QDropEvent *event); // 放下事件

然后再mainwindow.cpp中添加头文件:

  1. #include <QLabel>
  2. #include <QMouseEvent>
  3. #include <QDragEnterEvent>
  4. #include <QDragMoveEvent>
  5. #include <QDropEvent>
  6. #include <QPainter>
  7. #include <QMimeData>
  8. #include <QDrag>

在构造函数中添加如下代码:

  1. setAcceptDrops(true); // 设置窗口部件可以接收拖入
  2. QLabel *label = new QLabel(this); // 创建标签
  3. QPixmap pix("../imagedragdrop/logo.png");
  4. label->setPixmap(pix); // 添加图片
  5. label->resize(pix.size()); // 设置标签大小为图片的大小
  6. label->move(100,100);
  7. label->setAttribute(Qt::WA_DeleteOnClose); // 当窗口关闭时销毁图片

这里必须先设置部件使其可以接收拖放操作,窗口部件默认是不可以接收拖放操作的。
然后创建了一个标签﹐并且为其添加了一张图片,这里将图片放到了项目源码目录下。

下面是添加那几个事件处理函数的定义:

  1. void MainWindow::mousePressEvent(QMouseEvent *event) //鼠标按下事件
  2. {
  3. // 第一步:获取图片
  4. // 将鼠标指针所在位置的部件强制转换为QLabel类型
  5. QLabel *child = static_cast<QLabel*>(childAt(event->pos()));
  6. if(!child->inherits("QLabel")) return; // 如果部件不是QLabel则直接返回
  7. QPixmap pixmap = *child->pixmap(); // 获取QLabel中的图片
  8. // 第二步:自定义MIME类型
  9. QByteArray itemData; // 创建字节数组
  10. QDataStream dataStream(&itemData, QIODevice::WriteOnly); // 创建数据流
  11. // 将图片信息,位置信息输入到字节数组中
  12. dataStream << pixmap << QPoint(event->pos() - child->pos());
  13. // 第三步:将数据放入QMimeData中
  14. QMimeData *mimeData = new QMimeData; // 创建QMimeData用来存放要移动的数据
  15. // 将字节数组放入QMimeData中,这里的MIME类型是我们自己定义的
  16. mimeData->setData("myimage/png", itemData);
  17. // 第四步:将QMimeData数据放入QDrag中
  18. QDrag *drag = new QDrag(this); // 创建QDrag,它用来移动数据
  19. drag->setMimeData(mimeData);
  20. drag->setPixmap(pixmap);//在移动过程中显示图片,若不设置则默认显示一个小矩形
  21. drag->setHotSpot(event->pos() - child->pos()); // 拖动时鼠标指针的位置不变
  22. // 第五步:给原图片添加阴影
  23. QPixmap tempPixmap = pixmap; // 使原图片添加阴影
  24. QPainter painter; // 创建QPainter,用来绘制QPixmap
  25. painter.begin(&tempPixmap);
  26. // 在图片的外接矩形中添加一层透明的淡黑色形成阴影效果
  27. painter.fillRect(pixmap.rect(), QColor(127, 127, 127, 127));
  28. painter.end();
  29. child->setPixmap(tempPixmap); // 在移动图片过程中,让原图片添加一层黑色阴影
  30. // 第六步:执行拖放操作
  31. if (drag->exec(Qt::CopyAction | Qt::MoveAction, Qt::CopyAction)
  32. == Qt::MoveAction) // 设置拖放可以是移动和复制操作,默认是复制操作
  33. child->close(); // 如果是移动操作,那么拖放完成后关闭原标签
  34. else {
  35. child->show(); // 如果是复制操作,那么拖放完成后显示标签
  36. child->setPixmap(pixmap); // 显示原图片,不再使用阴影
  37. }
  38. }

鼠标按下时会触发鼠标按下事件,进而执行其处理函数,这里进行了一系列操作,就像程序的注释所描述的那样,大体上可以分为6步。

第1步:先获取鼠标指针所在处的部件的指针,将它强制转换为QLabel类型的指针,然后使用inherits()函数判断它是否是QLabel类型,如果不是则直接返回,不再进行下面的操作。

第2步:因为不仅要在拖动的数据中包含图片数据,还要包含它的位置信息﹐所以需要使用自定义的MIME类型。这里使用了QByteArray字节数组来存放图片数据和位置数据。然后使用QDataStream类将数据写入数组中,。其中,位置信息是当前鼠标指针的坐标减去图片左上角的坐标而得到的差值。

第3步:创建了QMimeData类对象指针,使用了自定义的MIME类型“myimage/png”,将字节数组放入QMimeData中。

第4步:为了移动数据,必须创建QDrag类对象,然后为其添加QMimeData数据。这里为了在移动过程中一直显示图片,需要使用setPixmap()函数为其设置图片。然后使用setHotSpot()函数指定了鼠标在图片上单击的位置,这里是相对于图片左上角的位置,如果不设定这个,那么在拖动图片过程中,指针会位于图片的左上角。

第5步:在移动图片过程中我们希望原来的图片有所改变来表明它正在被操作,所以为其添加了一层阴影。

第6步:执行拖动操作,这需要使用QDrag类的exec()函数,它不会影响主事件循环,所以这时界面不会被冻结。这个函数可以设定所支持的放下动作和默认的放下动作,比如这里设置了支持复制动作Qt : :CopyAction和移动动作Qt :: MoveAction,并设置默认的动作是复制。这就是说我们拖动图片,可以是移动它,也可以是进行复制,而默认的是复制操作,比如使用acceptProposedAction()函数时就是使用默认的操作。当图片被放下后,exec()函数就会返回操作类型,这个返回值由下面要讲到的dropE-vent()函数中的设置决定。这里判断到底进行了什么操作,如果是移动操作,那么就删除原来的图片;如果是复制操作,就恢复原来的图片。

  1. void MainWindow::dragEnterEvent(QDragEnterEvent *event) // 拖动进入事件
  2. {
  3. // 如果有我们定义的MIME类型数据,则进行移动操作
  4. if (event->mimeData()->hasFormat("myimage/png")) {
  5. event->setDropAction(Qt::MoveAction);
  6. event->accept();
  7. } else {
  8. event->ignore();
  9. }
  10. }
  11. void MainWindow::dragMoveEvent(QDragMoveEvent *event) // 拖动事件
  12. {
  13. if (event->mimeData()->hasFormat("myimage/png")) {
  14. event->setDropAction(Qt::MoveAction);
  15. event->accept();
  16. } else {
  17. event->ignore();
  18. }
  19. }

在这二个事件处理函数中,先判断拖动的数据中是否有自定义的MIME类型的数据,如果有,则执行移动动作 Qt::MoveAction

  1. void MainWindow::dropEvent(QDropEvent *event) // 放下事件
  2. {
  3. if (event->mimeData()->hasFormat("myimage/png")) {
  4. QByteArray itemData = event->mimeData()->data("myimage/png");
  5. QDataStream dataStream(&itemData, QIODevice::ReadOnly);
  6. QPixmap pixmap;
  7. QPoint offset;
  8. // 使用数据流将字节数组中的数据读入到QPixmap和QPoint变量中
  9. dataStream >> pixmap >> offset;
  10. // 新建标签,为其添加图片,并根据图片大小设置标签的大小
  11. QLabel *newLabel = new QLabel(this);
  12. newLabel->setPixmap(pixmap);
  13. newLabel->resize(pixmap.size());
  14. // 让图片移动到放下的位置,不设置的话,图片会默认显示在(0,0)点即窗口左上角
  15. newLabel->move(event->pos() - offset);
  16. newLabel->show();
  17. newLabel->setAttribute(Qt::WA_DeleteOnClose);
  18. event->setDropAction(Qt::MoveAction);
  19. event->accept();
  20. } else {
  21. event->ignore();
  22. }
  23. }

在放下事件中,使用字节数组获取了拖放的数据﹐然后将其中的图片数据和位置数据读取到两个变量中,并使用它们来设置新建的标签。

上面的例子是对图片进行移动,如果要像对图片进行复制,则只需将dragEnterEvent,dropEvent,dragMoveEvent这3个函数中的event->setDropAction()函数中的参数改为Qt::CopyAction即可

对于拖放操作的其他应用,比如根据移动的距离来判断是否开始一个拖放操作,还有剪贴板QClioboard类,都可以通过在Drag and Drop关键字查看

相关文章