Qt拖动控件移动
中国好公民st 人气:10做惯了静态图,今天来搞一搞动态图吧,首先来个最基础的功能:如果让一个控件拖动起来。
展示效果:
按照以往简单的做法,使用mouseMoveEvent、mousePressEvent、mouseReleaseEvent也是可以实现的。这是最基础的移动做法。
今天,不使用那种简单的做法,采用Qt一种特有的拖动方法来实现!
使用QDropEvent实现拖拽事件。
实现控件拖拽的流程,如下:
1:创建一个控件,这里使用QLabel控件。
2:选中需要拖拽的控件
3:重写拖拽事件。
根据上述操作流程,来实现一个控件的拖拽吧!
1.设置窗口拖拽属性
在Qt中,默认是不响应拖拽消息的,这跟mouseMoveEvent消息默认不响应是一样的,必须明确调用,告诉窗口,需要响应此消息。
this->setAcceptDrops(true);
2.创建初始控件
创建一个初始控件,用于初始拖动使用。
QLabel *labIcon = new QLabel(this); labIcon->setText(""); labIcon->setPixmap(QPixmap(":/QDragSingleLabel/image/boat.png")); labIcon->move(10, 10); labIcon->show(); labIcon->setAttribute(Qt::WA_DeleteOnClose);
偷懒起见,对QLabel控件设置了窗口关闭销毁的功能,很是方便。
3.选中控件进行拖动
鼠标在控件上按下,开始做拖动操作;当鼠标抬起时,不进行拖动操作。
3.1响应mousePressEvent事件
需要知道鼠标是否点击到控件上
这里需要特殊注意的是:QLabel是一个静态控件,正常情况下是不会响应鼠标选中效果的。
此时,需要响应QWidget鼠标按下的事件,将鼠标点击的点转换成是否选中QLabel控件,侧面实现数据点击控件效果。
QLabel *child = static_cast<QLabel*>(childAt(event->pos())); if(!child) { //不是QLabel控件,不进行处理 return; }
QWidget::childAt(const QPoint& p)const;
说明:返回窗口小部件自身坐标系统中p点处的可见子窗口小部件。
查询到有效QLabel指针后,创建一个可存储在剪贴板中的信息,通过拖放机制进行传输的。这里采用:QMimeData
类实现。
优势该类可以确保信息在应用程序之间安全传输,并且可以在相同的应用程序内复制。
创建该类并将QLabel中的数据传入到类中,用于做拖拽使用。
QMimeData *mimeData = new QMimeData; mimeData->setData(qsEnum, itemData);
设置数据。
qsEnum:类型:QString。
在这里可以设置任意字符串,只要保证在拖拽消息时用的一个字符串就可以。为了方便统一,将该字符串做了统一设置。
const QString qsEnum = "zhongGuoHaoGongMin";//自定义数据类型
itemData:类型:QByteArray。
对QMimeData传入的数据,这里存放了QLabel的图片以及显示位置。
QPixmap pixmap = *child->pixmap(); QByteArray itemData; QDataStream dataStream(&itemData, QIODevice::WriteOnly); dataStream << pixmap << QPoint(event->pos() - child->pos());
上述内容准备就绪后,创建拖拽类,用于数据拖拽。
QDrag *drag = new QDrag(this); drag->setMimeData(mimeData); drag->setPixmap(pixmap); drag->setHotSpot(event->pos() - child->pos());
将数据传递给拖动对象,设置将在操作期间与光标一起显示的像素图,并定义一个热点的位置,该热点将像素图的位置置于光标之下。
绘制拖动的位置,这里采用了QPainter绘制机制
QPixmap tempPixmap = pixmap; QPainter painter; painter.begin(&tempPixmap); painter.fillRect(pixmap.rect(), QColor(127, 127, 127, 127)); painter.end(); child->setPixmap(tempPixmap);
开始拖动操作,调用QDrag::exec()
;
3.2判断控件拖动
目前只有一个控件可以拖动,那么,当创建多个拖动控件时,该如何判断要拖动哪个呢?
这时候,在创建QMimeData传入的自定义数据类型就起到作用了。
当数据类型是qsEnum时,进行判断,如果不是,不进行判断。
if (event->mimeData()->hasFormat(qsEnum)) { //进行判断 } else { event->ingnore(); //忽略判断 }
事件:dragEnterEvent、dragMoveEvent、dropEvent都需要这样判断。
3.3事件处理
当前是匹配的自定义数据类型时,并且是该资源是,接受拖动进入事件,并设置当前为拖动事件。
if (event->source() == this) { event->setDropAction(Qt::MoveAction); event->accept(); }
否则设置执行操作并接收该事件
else { event->acceptProposedAction(); }
3.4结束拖动
结束拖动,响应事件:virtual void dropEvent(QDropEvent *event)override;
除了处理操作3中的事件处理,还需要当鼠标结束操作时,需要在新的位置上重新创建QLabel控件。并将鼠标按下时创建的QMimeData数据获取出来,显示到新创建的QLabel控件上。
QByteArray itemData = event->mimeData()->data(qsEnum); QDataStream dataStream(&itemData, QIODevice::ReadOnly); QPixmap pixmap; QPoint offset; dataStream >> pixmap >> offset; QLabel *newIcon = new QLabel(this); newIcon->setPixmap(pixmap); newIcon->move(event->pos() - offset); newIcon->show(); newIcon->setAttribute(Qt::WA_DeleteOnClose);
dropEvent消息是什么时候被触发呢?
当鼠标左键弹起时,说明结束了控件拖动事件,需要调用dropEvent并重新创建控件,显示新位置。
Qt::DropAction n = drag->exec(Qt::CopyAction | Qt::MoveAction, Qt::CopyAction); if (n == Qt::MoveAction) { //结束操作 child->close(); } else { //继续拖动控件,实时显示新位置 child->show(); child->setPixmap(pixmap); }
加载全部内容