qt中的拖拽及其使用技巧

来源:互联网 发布:实用五金工具手册软件 编辑:程序博客网 时间:2024/06/05 15:59

关于qt中的拖放操作,首先可以看这篇官方文档:http://doc.qt.io/qt-5.5/dnd.html

一、QDrag

首先是创建QDrag,可以在mousePressEvent或者mouseMoveEvent中创建。

[cpp] view plain copy
  1. void DragDropWidget::mousePressEvent(QMouseEvent *event)  
  2. {  
  3.     if (event->button() == Qt::LeftButton)  
  4.     {  
  5.         QDrag *drag = new QDrag(this);  
  6.         QMimeData *data = new QMimeData;  
  7.         drag->setMimeData(data);  
  8.         drag->exec(Qt::MoveAction);  
  9.     }  
  10. }  
或者
[cpp] view plain copy
  1. void DragDropWidget::mousePressEvent(QMouseEvent *event)  
  2. {  
  3.     if (event->buttons() & Qt::LeftButton)  
  4.     {  
  5.         startPos = event->pos();  
  6.     }  
  7. }  
  8.   
  9. void DragDropWidget::mouseMoveEvent(QMouseEvent *event)  
  10. {  
  11.     if (event->buttons() & Qt::LeftButton)  
  12.     {  
  13.         if ((event->pos() - startPos).manhattanLength() < QApplication::startDragDistance())  
  14.         {  
  15.             QDrag *drag = new QDrag(this);  
  16.             QMimeData *data = new QMimeData;  
  17.             drag->setMimeData(data);  
  18.             drag->exec(Qt::LinkAction);  
  19.         }  
  20.     }  
  21. }  
QDrag在exec前,一定要设置QMimeData,否则会弹出警告“QDrag: No mimedata set before starting the drag”。并且不会开始拖拽操作。

QMimeData在拖拽中非常有用,可以用来保存拖拽操作附带的信息,比如字符串、文件或者图片,同时也可以用来验证其所保存的信息格式,并以此来判断是否可接收。未来再补充QMimeData的用法。

另外要注意,在windows下,QDrag::exec()是个同步操作,要在exec()返回后,才会继续执行下面的代码。

二、drag相关事件

首先,当需要一个控件接收drag和drop,就要先调用控件的方法:setAcceptDrops(true)。

qt中一共有三个drag相关事件,dragEnterEvent、dragMoveEvent、dragLeaveEvent。这三个事件触发条件类似鼠标移入,鼠标移动,鼠标移出。当鼠标拖拽进入控件触发dragEnterEvent,在控件内拖拽移动触发dragMoveEvent,鼠标拖拽离开控件触发dragLeaveEvent。

dragEnterEvent事件有一个参数,参数类型是QDragEnterEvent,继承自QDragMoveEvent。

dragMoveEvent事件有一个参数,参数类型是QDragMoveEvent。

dragLeaveEvent事件有一个参数,参数类型是QDragLeaveEvent,继承自QEvent,无特别用法。

当鼠标拖拽进入控件时,会触发dragEnterEvent,如果不做处理,后续将不会接收到dragMoveEvent事件和dragLeaveEvent事件。在dragEnterEvent事件中,如果调用了QDragMoveEvent::accept()函数,后续将可以收到dragMoveEvent事件和dragLeaveEvent事件。而如果调用QDragMoveEvent::ignore()函数,效果相当于不处理,不会接收后续事件。

[cpp] view plain copy
  1. void DragDropWidget::dragEnterEvent(QDragEnterEvent *event)  
  2. {  
  3.     if (event->source() == this)  
  4.         event->ignore();  
  5.     else  
  6.         event->accept();  
  7. }  
发起drag的控件自身也是可以接受到来自自身的drag的相关事件的,这个要注意,处理不好容易出bug。

三、dropEvent

当drag为accept状态,然后释放鼠标,就会产生dropEvent。我们可以在这个事件里处理本次拖拽附带的Mime信息。


四、使用技巧

在我的一个项目中,需要通过拖拽操作连接两个控件,然后在两个控件之间画连接线。这里有两种控件,画布控件CanvasWidget,和节点控件NodeWidget。NodeWidget在CanvasWidget上,连接线也是在CanvasWidget上绘制。我们可以在NodeWidget的dropEvent事件里给CanvasWidget发送连接信号,告诉CanvasWidget两个NodeWidget已经相连了。

[cpp] view plain copy
  1. void NodeWidget::dropEvent(QDropEvent *event)  
  2. {  
  3.     auto src = event->source();  
  4.     event->setDropAction(Qt::LinkAction);  
  5.     event->accept();  
  6.     emit linked(src, this);  
  7. }  

然后我又想在拖拽的过程中就看到一条动态的连接线,这时候就要用到CanvasWidget的drag相关事件。在CanvasWidget的dragMoveEvent事件中这样处理:

[cpp] view plain copy
  1. void CanvasWidget::dragMoveEvent(QDragMoveEvent *event)  
  2. {  
  3.     m_lineDraging.end = event->pos();  
  4.     update();  
  5. }  

m_lineDraging就是用来绘制的动态连接线。设置完连接线的端点,然后再update()一下,就可以显示了。当然这只是一个简单方案,还没有验证过在绘制量较大时,在move事件中update会不会很卡。

下面是效果图


Qt Drag and Drop思维导图



参考资料:

  1. http://doc.qt.io/qt-5.5/dnd.html
  2. http://blog.csdn.net/pcsuite/article/details/6147191
原创粉丝点击