文档章节

Qt 学习之路 2(52):使用拖放

SuShine
 SuShine
发布于 2016/05/12 23:10
字数 2213
阅读 87
收藏 4
点赞 2
评论 0

拖放(Drag and Drop),通常会简称为 DnD,是现代软件开发中必不可少的一项技术。它提供了一种能够在应用程序内部甚至是应用程序之间进行信息交换的机制。操作系统与应用程序之间进行的剪贴板内容的交换,也可以被认为是拖放的一部分。

 

拖放其实是由两部分组成的:拖动和释放。拖动是将被拖放对象进行移动,释放是将被拖放对象放下。前者是一个按下鼠标按键并移动的过程,后者是一个松开鼠标按键的过程;通常这两个操作之间的鼠标按键是被一直按下的。当然,这只是一种普遍的情况,其它情况还是要看应用程序的具体实现。对于 Qt 而言,一个组件既可以作为被拖动对象进行拖动,也可以作为释放掉的目的地对象,或者二者都是。

在下面的例子中(来自 C++ GUI Programming with Qt4, 2nd Edition),我们将创建一个程序,将操作系统中的文本文件拖进来,然后在窗口中读取内容。

 

1

2

3

4

5

6

7

8

9

10

11

12

13

class MainWindow : public QMainWindow

{

    Q_OBJECT

public:

    MainWindow(QWidget *parent = 0);

    ~MainWindow();

protected:

    void dragEnterEvent(QDragEnterEvent *event);

    void dropEvent(QDropEvent *event);

private:

    bool readFile(const QString &fileName);

    QTextEdit *textEdit;

};

注意到我们需要重写dragEnterEvent()dropEvent()两个函数。顾名思义,前者是拖放进入的事件,后者是释放鼠标的事件。

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

MainWindow::MainWindow(QWidget *parent)

    : QMainWindow(parent)

{

    textEdit = new QTextEdit;

    setCentralWidget(textEdit);

 

    textEdit->setAcceptDrops(false);

    setAcceptDrops(true);

 

    setWindowTitle(tr("Text Editor"));

}

 

MainWindow::~MainWindow()

{

}

在构造函数中,我们创建了QTextEdit的对象。默认情况下,QTextEdit可以接受从其它应用程序拖放过来的文本类型的数据。如果用户把一个文件拖到这面,默认会把文件名插入到光标位置。但是我们希望让MainWindow读取文件内容,而不是仅仅插入文件名,所以我们在MainWindow中加入了拖放操作。首先要把QTextEditsetAcceptDrops()函数置为 false,并且把MainWindowsetAcceptDrops()置为 true,这样我们就能够让MainWindow截获拖放事件,而不是交给QTextEdit处理。

 

1

2

3

4

5

6

void MainWindow::dragEnterEvent(QDragEnterEvent *event)

{

    if (event->mimeData()->hasFormat("text/uri-list")) {

        event->acceptProposedAction();

    }

}

当用户将对象拖动到组件上面时,系统会回调dragEnterEvent()函数。如果我们在事件处理代码中调用acceptProposeAction()函数,就可以向用户暗示,你可以将拖动的对象放在这个组件上。默认情况下,组件是不会接受拖放的。如果我们调用了这个函数,那么 Qt 会自动以光标样式的变化来提示用户是否可以将对象放在组件上。在这里,我们希望告诉用户,窗口可以接受拖放,但是我们仅接受某一种类型的文件,而不是全部文件。我们首先检查拖放文件的 MIME 类型信息。MIME 类型由 Internet Assigned Numbers Authority (IANA) 定义,Qt 的拖放事件使用 MIME 类型来判断拖放对象的类型。关于 MIME 类型的详细信息,请参考 http://www.iana.org/assignments/media-types/。MIME 类型为 text/uri-list 通常用来描述一个 URI 列表。这些 URI 可以是文件名,可以是 URL 或者其它的资源描述符。如果发现用户拖放的是一个 text/uri-list 数据(即文件名),我们便接受这个动作。

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

void MainWindow::dropEvent(QDropEvent *event)

{

    QList<QUrl> urls = event->mimeData()->urls();

    if (urls.isEmpty()) {

        return;

    }

 

    QString fileName = urls.first().toLocalFile();

    if (fileName.isEmpty()) {

        return;

    }

 

    if (readFile(fileName)) {

        setWindowTitle(tr("%1 - %2").arg(fileName, tr("Drag File")));

    }

}

 

bool MainWindow::readFile(const QString &fileName)

{

    bool r = false;

    QFile file(fileName);

    QString content;

    if(file.open(QIODevice::ReadOnly)) {

        content = file.readAll();

        r = true;

    }

    textEdit->setText(content);

    return r;

}

当用户将对象释放到组件上面时,系统回调dropEvent()函数。我们使用QMimeData::urls()来获得QUrl的一个列表。通常,这种拖动应该只有一个文件,但是也不排除多个文件一起拖动。因此我们需要检查这个列表是否为空,如果不为空,则取出第一个,否则立即返回。最后我们调用readFile()函数读取文件内容。这个函数的内容很简单,我们前面也讲解过有关文件的操作,这里不再赘述。现在可以运行下看看效果了。

接下来的例子也是来自 C++ GUI Programming with Qt4, 2nd Edition。在这个例子中,我们将创建左右两个并列的列表,可以实现二者之间数据的相互拖动。

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

class ProjectListWidget : public QListWidget

{

    Q_OBJECT

public:

    ProjectListWidget(QWidget *parent = 0);

protected:

    void mousePressEvent(QMouseEvent *event);

    void mouseMoveEvent(QMouseEvent *event);

    void dragEnterEvent(QDragEnterEvent *event);

    void dragMoveEvent(QDragMoveEvent *event);

    void dropEvent(QDropEvent *event);

private:

    void performDrag();

    QPoint startPos;

};

ProjectListWidget是我们的列表的实现。这个类继承自QListWidget。在最终的程序中,将会是两个ProjectListWidget的并列。

 

1

2

3

4

5

ProjectListWidget::ProjectListWidget(QWidget *parent)

    : QListWidget(parent)

{

    setAcceptDrops(true);

}

构造函数我们设置了setAcceptDrops(),使ProjectListWidget能够支持拖动操作。

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

void ProjectListWidget::mousePressEvent(QMouseEvent *event)

{

    if (event->button() == Qt::LeftButton)

        startPos = event->pos();

    QListWidget::mousePressEvent(event);

}

 

void ProjectListWidget::mouseMoveEvent(QMouseEvent *event)

{

    if (event->buttons() & Qt::LeftButton) {

        int distance = (event->pos() - startPos).manhattanLength();

        if (distance >= QApplication::startDragDistance())

            performDrag();

    }

    QListWidget::mouseMoveEvent(event);

}

 

void ProjectListWidget::performDrag()

{

    QListWidgetItem *item = currentItem();

    if (item) {

        QMimeData *mimeData = new QMimeData;

        mimeData->setText(item->text());

 

        QDrag *drag = new QDrag(this);

        drag->setMimeData(mimeData);

        drag->setPixmap(QPixmap(":/images/person.png"));

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

            delete item;

    }

}

mousePressEvent()函数中,我们检测鼠标左键点击,如果是的话就记录下当前位置。需要注意的是,这个函数最后需要调用系统自带的处理函数,以便实现通常的那种操作。这在一些重写事件的函数中都是需要注意的,前面我们已经反复强调过这一点。

mouseMoveEvent()函数判断了,如果鼠标在移动的时候一直按住左键(也就是 if 里面的内容),那么就计算一个manhattanLength()值。从字面上翻译,这是个“曼哈顿长度”。首先来看看event.pos() - startPos是什么。在mousePressEvent()函数中,我们将鼠标按下的坐标记录为 startPos,而event.pos()则是鼠标当前的坐标:一个点减去另外一个点,这就是一个位移向量。所谓曼哈顿距离就是两点之间的距离(按照勾股定理进行计算而来),也就是这个向量的长度。然后继续判断,如果大于QApplication::startDragDistance(),我们才进行释放的操作。当然,最后还是要调用系统默认的鼠标拖动函数。这一判断的意义在于,防止用户因为手的抖动等因素造成的鼠标拖动。用户必须将鼠标拖动一段距离之后,我们才认为他是希望进行拖动操作,而这一距离就是QApplication::startDragDistance()提供的,这个值通常是 4px。

performDrag()开始处理拖放的过程。这里,我们要创建一个QDrag对象,将 this 作为 parent。QDrag使用QMimeData存储数据。例如我们使用QMimeData::setText()函数将一个字符串存储为 text/plain 类型的数据。QMimeData提供了很多函数,用于存储诸如 URL、颜色等类型的数据。使用QDrag::setPixmap()则可以设置拖动发生时鼠标的样式。QDrag::exec()会阻塞拖动的操作,直到用户完成操作或者取消操作。它接受不同类型的动作作为参数,返回值是真正执行的动作。这些动作的类型一般为Qt::CopyActionQt::MoveActionQt::LinkAction。返回值会有这几种动作,同时还会有一个Qt::IgnoreAction用于表示用户取消了拖放。这些动作取决于拖放源对象允许的类型,目的对象接受的类型以及拖放时按下的键盘按键。在exec()调用之后,Qt 会在拖放对象不需要的时候释放掉。

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

void ProjectListWidget::dragMoveEvent(QDragMoveEvent *event)

{

    ProjectListWidget *source =

            qobject_cast(event->source());

    if (source && source != this) {

        event->setDropAction(Qt::MoveAction);

        event->accept();

    }

}

 

void ProjectListWidget::dropEvent(QDropEvent *event)

{

    ProjectListWidget *source =

            qobject_cast(event->source());

    if (source && source != this) {

        addItem(event->mimeData()->text());

        event->setDropAction(Qt::MoveAction);

        event->accept();

    }

}

dragMoveEvent()dropEvent()相似。首先判断事件的来源(source),由于我们是两个ProjectListWidget之间相互拖动,所以来源应该是ProjectListWidget类型的(当然,这个 source 不能是自己,所以我们还得判断source != this)。dragMoveEvent()中我们检查的是被拖动的对象;dropEvent()中我们检查的是释放的对象:这二者是不同的。

本文转载自:http://www.devbean.net/2013/05/qt-study-road-2-dnd/

共有 人打赏支持
SuShine
粉丝 118
博文 436
码字总数 88684
作品 0
青岛
后端工程师
QT学习探索之路

近期由于工作需要在学习QT做界面开发,再次将我最近一个月的学习心得记录,同时欢迎各位大牛给我提出学习建议。 1:我的学习方法: 第一阶段: 主要是参考 C++ GUI Qt4 编程这本书 主要学习Q...

wb999999abc ⋅ 2017/05/20 ⋅ 0

Python GUI教程(十二):使用拖放控件

文章首发个人博客:http://zmister.com/archives/186.html 微信公众号:州的先生 同步更新 Pytho爬虫、数据分析、机器学习、渗透测试、Web开发:http://zmister.com/ 在之前的文章中,构建了...

州的先生 ⋅ 2017/11/27 ⋅ 0

Qt 国际化及 Qt Linguist 的使用

前几天,偶然间看到光哥的一篇代码,里面有这样一句话: action->setText(tr("开始")); 并且在main函数中还有这种一段代码: int main(int argc, char *argv[]){ } devbean大神和dbzhang800大...

鉴客 ⋅ 2014/03/06 ⋅ 0

跟我一起学QT7:拖放文件

源代码下载地址 1. 简单文件拖放 https://github.com/leichaojian/qt/tree/master/drop1 2. 复杂文件拖放 https://github.com/leichaojian/qt/tree/master/drop 3. 通过按钮来完成列表数据的...

fzyz_sb ⋅ 2014/11/17 ⋅ 1

收藏的博客 -- Qt/C++学习

Qt下载官网 http://download.qt.io/ Qt知名社区 http://www.qtcn.org/bbs/i.php http://www.qter.org/ Qt知名博客 https://www.devbean.net/ --- Qt学习之路,Qt Creator源码学习 http://blo......

libaineu2004 ⋅ 2017/08/18 ⋅ 0

关于Qt5里的两个问题

第一个问题是 Qt学习之路47 里的booleanmodel.cpp文件 void BooleanModel::setRootNode(Node *node){ } 基类是QAbstractItemModel 有人说Qt5没有reset()函数,不知道有什么代替的。 第二个问...

李新浩 ⋅ 2014/03/06 ⋅ 0

鼠标拖放技术二

接着上次的说,上次说到了拖放技术,今天依然是一个例子,同样是来自《C++ GUI Programming with Qt 4, 2nd Edition》的。 这次的 demo 还算是比较实用:实现的是两个 list 之间的数据互拖。...

晨曦之光 ⋅ 2012/04/13 ⋅ 0

拖放技术一

拖放 Drag and Drop,有时又被称为 DnD,是现代软件开发中必不可少的一项技术。它提供了一种能够在应用程序内部甚至是应用程序之间进行信息交换的机制,并且,操作系统与应用程序之间进行剪贴...

晨曦之光 ⋅ 2012/04/13 ⋅ 0

Qt使用教程之创建一个基于Qt部件的应用程序(二)

<Qt Enterprise最新版下载> 在创建一个基于Qt部件的应用程序(一)中,我们为大家介绍了如何创建文本搜索项目。在本文中,我们将继续介绍另一部分重要内容——如何填充缺失部分。 填充缺失部...

Miss_Hello_World ⋅ 2015/10/29 ⋅ 0

【Qt笔记】使用拖放

拖放(Drag and Drop),通常会简称为 DnD,是现代软件开发中必不可少的一项技术。它提供了一种能够在应用程序内部甚至是应用程序之间进行信息交换的机制。操作系统与应用程序之间进行的剪贴...

大道无名 ⋅ 2016/08/02 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

从方法论到零售客户实践 解码阿里巴巴数据中台——2018上海云栖大会

摘要: 一、数据中台之道 6月8日,上海云栖大会进入了第二天的议程,数据中台专场论坛座无虚席,数据中台总架构师邓中华女士向在场的观众介绍了数据中台的衍生发展之道。 基于OneID、OneData...

阿里云云栖社区 ⋅ 22分钟前 ⋅ 0

Ubuntu部署django问题汇总

使用Anaconda3的Python3.6的pip安装UWSGI报错 原因是gcc版本不兼容,安装4.7并修改gccsudo apt-get install gcc-4.7sudo mv /usr/bin/gcc /usr/bin/gcc.baksudo ln -s /usr/bin/gcc-4.......

wuyaSama ⋅ 25分钟前 ⋅ 0

从方法论到零售客户实践 解码阿里巴巴数据中台——2018上海云栖大会

摘要: 一、数据中台之道 6月8日,上海云栖大会进入了第二天的议程,数据中台专场论坛座无虚席,数据中台总架构师邓中华女士向在场的观众介绍了数据中台的衍生发展之道。 基于OneID、OneData...

猫耳m ⋅ 25分钟前 ⋅ 0

Docker减肥小记

如果经常使用 docker,你会发现 docker 占用的资源膨胀很快,其中最明显也最容易被察 如何快速的清理 docker 占用的系统资源,具体点说就是删除那些无用的镜像、容器、网络和数据卷… 1、查看...

寰宇01 ⋅ 36分钟前 ⋅ 0

微信小程序中如何使用WebSocket实现长连接(含完整源码)

本文由腾讯云技术团队原创,感谢作者的分享。 1、前言 微信小程序提供了一套在微信上运行小程序的解决方案,有比较完整的框架、组件以及 API,在这个平台上面的想象空间很大。腾讯云研究了一...

JackJiang- ⋅ 44分钟前 ⋅ 0

定制库到Maven本地资源库

1.如果只有定制库的JAR文件 下载链接如下:pdf.jar 2.使用命令转换成Maven本地资源 mvn install:install-file -Dfile=/Users/manager/Downloads/clj-pdf-2.2.33.jar -DgroupId=clj-pdf -Dar......

年少爱追梦 ⋅ 48分钟前 ⋅ 0

高仿springmvc之xuchen-mvc

package org.mvc.framework.servlet; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.......

徐志 ⋅ 50分钟前 ⋅ 0

关于自定义URLStreamHandler的一次踩坑

关于自定义URLStreamHandler的一次踩坑 20180625 lambo init 说明 一般自定义实现url的协议解析.方案为实现URLStreamHandler.实现其 openConnection 就可以了, 如果我们执行 new URL("xx://...

林小宝 ⋅ 51分钟前 ⋅ 0

【SM2证书】利用BC的X509v3CertificateBuilder组装X509国密证书

演示证书文件 链接: https://pan.baidu.com/s/1ijHNnMQJj7jzW-jXEVd6Gg 密码: vfva 所需jar包 <!-- https://mvnrepository.com/artifact/org.bouncycastle/bcpkix-jdk15on --> <dependenc......

小帅帅丶 ⋅ 52分钟前 ⋅ 0

用Calendar 实现 计算 一段时间的毫秒值

Calendar c=Calendar.getInstance();c.add(Calendar.MONTH, -1);int lastMonthMaxDay=c.getActualMaximum(Calendar.DAY_OF_MONTH);c.set(c.get(Calendar.YEAR), c.get(Calendar.MONTH)......

岸芷汀兰 ⋅ 56分钟前 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部