Qt drag & drop
Example: draggable Icons
用户将一个widget中的东西,这个例子中是QLable控件拖动至另一个widget,这个例子甚至支持程序2个实例之间的拖拽操作。Widget之间和程序实例之间拖拽操作结果是复制一个lable并将其放到鼠标release的位置上。
传输的信息不仅包含QLable本身包含的图片信息,还有鼠标点击位置信息,这样用户就能精确放置Icon位置了。不过这需要MIME类型格式。
MIME格式:多用途互联网邮件扩展(MIME,Multipurpose Internet Mail Extensions)是一个互联网标准,它扩展了电子邮件标准,使其能够支持非ASCII字符、二进制格式附件等多种格式的邮件消息。MIME是通过标准化电子邮件报文的头部的附加域(fields)而实现的;这些头部的附加域,描述新的报文类型的内容和组织形式。
用于放置icon的widget:
class DragWidget : public QFrame
{
public:
DragWidget(QWidget *parent=0);
protected:
void dragEnterEvent(QDragEnterEvent *event);
void dragMoveEvent(QDragMoveEvent *event);
void dropEvent(QDropEvent *event);
voidmousePressEvent(QMouseEvent *event);
};
构造函数:
DragWidget::DragWidget(QWidget *parent)
: QFrame(parent)
{
setMinimumSize(200, 200);
setFrameStyle(QFrame::Sunken | QFrame::StyledPanel);
setAcceptDrops(true);
QLabel *boatIcon = new QLabel(this);
boatIcon->setPixmap(QPixmap(":/images/boat.png"));
boatIcon->move(20, 20);
boatIcon->show();
boatIcon->setAttribute(Qt::WA_DeleteOnClose);
QLabel *carIcon = new QLabel(this);
carIcon->setPixmap(QPixmap(":/images/car.png"));
carIcon->move(120, 20);
carIcon->show();
carIcon->setAttribute(Qt::WA_DeleteOnClose);
QLabel *houseIcon = new QLabel(this);
houseIcon->setPixmap(QPixmap(":/images/house.png"));
houseIcon->move(20, 120);
houseIcon->show();
houseIcon->setAttribute(Qt::WA_DeleteOnClose);
}
houseIcon->setAttribute(Qt::WA_DeleteOnClose);
//这句话表示Qt将在QLable控件接受closeEvent的时候删除之。
//[疑问]那不写这句话会怎么样呢?
从起始位置实现拖操作,必须有一个鼠标点击事件函数:
voidDragWidget::mousePressEvent(QMouseEvent *event)
{
QLabel *child = static_cast<QLabel*>(childAt(event->pos()));
if (!child)
return;
QPixmap pixmap = *child->pixmap();
QByteArray itemData;
QDataStream dataStream(&itemData, QIODevice::WriteOnly);
dataStream << pixmap << QPoint(event->pos() -child->pos());
这里用了QByteArray打包了pixmap和鼠标移动造成的偏移向量信息。用dataStream写入到QByteArray非常方便。
使用QMimeData保存这些信息,据说是为了interoperability(两台不同电脑共同工作交通的能力):
QMimeData *mimeData = new QMimeData;
mimeData->setData("application/x-dnditemdata", itemData);
QMineData有个setData(),可以直接将一个QByteArray参数传入。第一个参数一个字符串用于指定这个MIME的format,方便其他widget或者程序识别拖拽信息。
拖拽操作本身由一个QDrag完成:
QDrag *drag = new QDrag(this);
drag->setMimeData(mimeData);//将数据传给drag
drag->setPixmap(pixmap);//拖拽过程中显示的图片
drag->setHotSpot(event->pos() - child->pos());
//Hot Spot是一个精确定位鼠标位置的东东,event->pos()指示鼠标点击的位置。
//Setsthe position of the hot spot relative to the top-left corner of the pixmap usedto the point specified by hotspot.
voidDragWidget::dragEnterEvent(QDragEnterEvent *event)
{
if(event->mimeData()->hasFormat("application/x-dnditemdata")) {
if (event->source() == this) {
event->setDropAction(Qt::MoveAction);
event->accept();
} else {
event->acceptProposedAction();
}
} else {
event->ignore();
}
}
其中的acceptProposedAction()。一个drop event包含了一个proposed action,这个action到底是什么可以从proposedAction()获得,无论这个widget是否接受这个event。如果widget能够处理这个event,那么应该调用一下acceptProposedAction()函数。因为proposed action是一个Qt::DropAction枚举的混合,选用这些枚举中的其中一个作为默认的action或者让用于选择(用户可以在放下物件的时候选择操作,复制,移动什么的)非常有用。
Constant | Value | Description |
---|---|---|
Qt::CopyAction | 0x1 | Copy the data to the target. |
Qt::MoveAction | 0x2 | Move the data from the source to the target. |
Qt::LinkAction | 0x4 | Create a link from the source to the target. |
Qt::ActionMask | 0xff | |
Qt::IgnoreAction | 0x0 | Ignore the action (do nothing with the data). |
Qt::TargetMoveAction | 0x8002 | On Windows, this value is used when the ownership of the D&D data should be taken over by the target application, i.e., the source application should not delete the data. On X11 this value is used to do a move. TargetMoveAction is not used on the Mac. |
voidDragWidget::dragMoveEvent(QDragMoveEvent *event)
{
if(event->mimeData()->hasFormat("application/x-dnditemdata")) {
if (event->source() == this) {
event->setDropAction(Qt::MoveAction);
event->accept();
} else {
event->acceptProposedAction();
}
} else {
event->ignore();
}
}
void DragWidget::dropEvent(QDropEvent *event)//一个对信息的解锁过程
{
if(event->mimeData()->hasFormat("application/x-dnditemdata")) {
QByteArray itemData = event->mimeData()->data("application/x-dnditemdata");
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);
if (event->source() == this) {
event->setDropAction(Qt::MoveAction);
event->accept();
} else {
event->acceptProposedAction();
}
}else {
event->ignore();
}
}