拖拽(QT)

一、拖放(Drag and Drop)的概念

拖放提供了一种简单的可视化机制,用户可以使用该机制在应用程序之间和内部传输信息。

拖放在功能上类似于粘贴板机制。

二、拖放类

这些类处理拖放和必要的mime类型编码和解码。

QDrag:

支持基于MIME的拖放数据传输

QDragEnterEvent:

当拖放操作进入窗体时发送给它的事件

QDragLeaveEvent:

当拖放操作离开窗体时发送给它的事件

QDragMoveEvent:

在进行拖放操作时发送的事件

QDropEvent:

当一个拖放操作完成时的事件

三、配置(Configuration)

QStyleHints

对象提供了一些与拖放操作相关的属性。

QStyleHints::startDragTime()

描述在开始拖动之前,用户必须在对象上按住鼠标按钮的时间量(毫米)

QStyleHints::startDragDistance()

指示在将移动解释为拖动之前,按住鼠标按钮时用户必须移动鼠标 的距离。

QStyleHints::tartDragVelocity()

指示用户移动鼠标开始拖动的速度(以像素/秒为单位)值为0表示 没有此限制。

如果在控件中提供拖放支持,则这些配置将提供与基础窗口系统兼容的合理默认值供用户使用。

四、拖(Dragging)操作

要开始拖动,需要创建一个QDrag对象,并调用其exec()函数。 在大多数应用程序中,只有在按下鼠标按钮并将光标移动一定距离后才开始拖放操作。 但是,启用窗体拖动的最简单方法是重新实现窗体的mousePressEvent()并开始拖放操作:

void MainWindow::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton
尽管用户可能需要一些时间来完成拖动操作,但就应用程序而言,exec()函数是一个阻塞函数,它使
用多个值之一返回。这些说明了操作是如何结束的。
注意:exec()函数不会阻塞主事件循环。
对于需要区分鼠标单击和拖动的窗体,重新实现窗体的mousePressEvent()函数以记录拖动的开始位
置:
之后,在mouseMoveEvent()中,我们可以确定是否应该开始拖动,并构造一个拖动对象来处理该操
作:
&& iconLabel->geometry().contains(event->pos())) {
QDrag *drag = new QDrag(this);
QMimeData *mimeData = new QMimeData;
mimeData->setText(commentEdit->toPlainText());
drag->setMimeData(mimeData);
drag->setPixmap(iconPixmap);
Qt::DropAction dropAction = drag->exec();
...
}
}

尽管用户可能需要一些时间来完成拖动操作,但就应用程序而言,exec()函数是一个阻塞函数,它使 用多个值之一返回。这些说明了操作是如何结束的。

注意:exec()函数不会阻塞主事件循环。

对于需要区分鼠标单击和拖动的窗体,重新实现窗体的mousePressEvent()函数以记录拖动的开始位 置:

void DragWidget::mousePressEvent(QMouseEvent *event){
    if(event->button()Qt::LeftButton)
        dragStartPosition = event->pos();
}

之后,在mouseMoveEvent()中,我们可以确定是否应该开始拖动,并制造一个拖动对象来处理该操作:

void DragWidget::mouseMoveEvent(QMouseEvent *event)
{
if (!(event->buttons() & Qt::LeftButton))
return;
if ((event->pos() - dragStartPosition).manhattanLength() <
QApplication::startDragDistance())
return;
QDrag *drag = new QDrag(this);
QMimeData *mimeData = new QMimeData;
mimeData->setData(mimeType, data);
drag->setMimeData(mimeData);
Qt::DropAction dropAction = drag->exec(Qt::CopyAction | Qt::MoveAction);
...
}

五、放(Dropping)操作

若要接收放在窗体上的内容,需要在窗体初始化时调用setAcceptDrops(true),然后重新实现 dragEnterEvent()和dropEvent()事件处理程序函数。

例如,以下代码在QWidget子类的构造函数中启用了drop事件,从而可以有效地实现drop事件处理程 序:

Window::Window(QWidget *parent)
: QWidget(parent)
{
...
setAcceptDrops(true);
}

dragEnterEvent() 函数通常用于通知Qt窗体接受的数据类型。如果要在DragMoveEvent()和 DropEvent()的重新实现中接收QDragMoveEvent或QDropEvent,则必须重新实现此函数。

以下代码显示如何重新实现dragEnterEvent(),以告知拖放系统我们只能处理纯文本:

void Window::dragEnterEvent(QDragEnterEvent *event)
{
if (event->mimeData()->hasFormat("text/plain"))
event->acceptProposedAction();
}

dropEvent()用于解压拖放(Dropping)的数据,并以相应的方式进行处理。

在以下代码中,事件中提供的文本将传递给QTextBrowser,QComboBox将填充用于描述数据的MIME 类型列表:

void Window::dropEvent(QDropEvent *event)
{
textBrowser->setPlainText(event->mimeData()->text());
mimeTypeCombo->clear();
mimeTypeCombo->addItems(event->mimeData()->formats());
event->acceptProposedAction();
}

在这种情况下,我们接受拖放的操作,而不检查它是什么。在实际应用程序中,可能需要从dropEvent ()函数返回,而不接受建议的操作(ProposedAction),或者在操作不相关的情况下处理数据。

例如,如果不支持应用程序中指向外部源的链接,我们可以选择忽略Qt::LinkAction操作。

1、覆盖Proposed Actions

我们也可以覆盖Proposed Actions,并对数据执行其他操作。为此,我们将在调用accept()之前使用 Qt::DropAction中的首选操作调用事件对象的setDropAction()。这将确保使用替换操作,而不是 Proposed Actions。

对于更复杂的应用程序,重新实现dragMoveEvent() 和dragLeaveEvent()将使窗口的某些部分对拖放事 件敏感,并能够更好地控制应用程序中的拖放。

枚举类型说明

枚举类型:QLayout::DropActions

枚举值:

名称 说明
Qt::CopyAction 0x1 复制数据到目标
Qt::MoveAction 0x2 从源移动数据到目标
Qt::LinkAction 0x4 从源创建一个链接到目标
Qt::ActionMask 0xff -
Qt::lgnoreAction 0x0 忽略这个动作,什么都不做

2、子类化复杂窗体

某些标准的Qt窗体提供了自己的拖放支持。在子类化这些窗口时,除了dragEnterEvent()和dropEvent()之外,可能还需要重新实现dragMoveEvent(),以防止基类提供默认的拖放处理,并处理我们感兴趣的任何特殊情况。

六、拖拽动作

(Drag and Drop Actions)

在最简单的情况下,拖放操作的目标接收被拖动数据的副本,源决定是否删除原始数据。这由Qt::CopyAction操作描述。

目标也可以选择处理其他操作,特别是Qt::MoveAction和Qt::LinkAction操作。

如果源调用QDrag::exec(),并返回Qt::MoveAction,则如果源选择删除任何原始数据,它将负责删 除这些数据。不应删除由源窗体创建的QMimeData和QDrag对象-它们将被Qt销毁。

目标负责获取拖放操作中发送的数据的所有权;这通常是通过保留对数据的引用来完成的。

如果目标理解Qt::LinkAction操作,它应该存储自己对原始信息的引用;源不需要对数据执行任何进一步 的处理拖放操作。最常见的用法是在同一个窗口中执行移动。

拖动操作的另一个主要用途是使用引用类型(如text/uri list),其中拖动的数据实际上是对文件或对象 的引用。

七、添加新的拖放类型

(Adding New Drag and Drop Types)

拖放不限于文本和图像。任何类型的信息都可以通过拖放操作进行传输。要在应用程序之间拖动信息, 应用程序必须能够相互指示它们可以接受的数据格式以及它们可以生成的数据格式。这是使用MIME类 型实现的源构造的QDrag对象包含一个MIME类型列表,用于表示数据(从最合适到最不合适的顺序排 列),drop目标使用其中一个来访问数据。对于常见的数据类型,便利函数处理透明使用的MIME类 型,但是对于自定义数据类型,必须显式地声明它们。

为了实现一个不被QDrag便利函数所覆盖的信息类型的拖放操作,第一个也是最重要的步骤是寻找合适 的现有格式:因特网分配数字授权(IANA)在信息科学研究所(ISI)提供MIME媒体类型的分层列表。 使用标准MIME类型可以最大化与其他软件现在和将来的应用程序的互操作性。

要支持其他媒体类型,只需使用setData()函数在QMimeData对象中设置数据,提供完整的MIME类 型和包含适当格式数据的QByteArray。

以下代码从标签中获取一个pixmap,并将其存储为QMimeData对象中的可移植网络图形(PNG)文 件:

QByteArray output;
QBuffer outputBuffer(&output);
outputBuffer.open(QIODevice::WriteOnly);
imageLabel->pixmap()->toImage().save(&outputBuffer, "PNG");
mimeData->setData("image/png", output);

当然,在这种情况下,我们可以简单地使用setImageData()来提供多种格式的图像数据:

mimeData->setImageData(QVariant(*imageLabel->pixmap()));

QByteArray方法在这种情况下仍然有用,因为它提供了对存储在QMimeData对象中的数据量的更大控 制。

请注意,项视图(item views)中使用的自定义数据类型必须声明为元对象,并且必须实现它们的流运算 符。

八、Drop操作

(Drop Actions)

在剪贴板模型中,用户可以剪切或复制源信息,然后粘贴它。类似地,在拖放模型中,用户可以拖动信 息的副本,也可以将信息本身拖动到新位置(移动它)。对于程序员来说,拖放模型还有一个额外的复 杂性:在操作完成之前,程序不知道用户是否想要剪切或复制信息。在应用程序之间拖动信息时,这通 常没有区别,但在应用程序中,检查使用了哪个拖放操作是很重要的。

我们可以重新实现窗体的mouseMoveEvent(),并使用可能的拖放操作组合启动拖放操作。例如,我们 可能希望确保拖动始终移动窗体中的对象:

void DragWidget::mouseMoveEvent(QMouseEvent *event)
{
if (!(event->buttons() & Qt::LeftButton))
return;
if ((event->pos() - dragStartPosition).manhattanLength()
< QApplication::startDragDistance())
return;
QDrag *drag = new QDrag(this);
QMimeData *mimeData = new QMimeData;
mimeData->setData(mimeType, data);
drag->setMimeData(mimeData);
Qt::DropAction dropAction = drag->exec(Qt::CopyAction | Qt::MoveAction);
...
}

如果信息被放到另一个应用程序中,exec()函数返回的操作可能默认为CopyAction,但是,如果它被 放到同一个应用程序中的另一个窗体中,我们可能会获得不同的Drop操作。

可以在窗体的dragMoveEvent()函数中筛选建议的drop操作。但是,可以接受dragEnterEvent()中的所 有建议操作,并让用户决定以后要接受哪些操作:

void DragWidget::dragEnterEvent(QDragEnterEvent *event)
{
event->acceptProposedAction();
}

当控件中发送drop时,将调用dropEvent()处理程序函数,我们可以依次处理每个可能的操作。首先,我们在同一个小部件中处理拖放操作:

void DragWidget::dropEvent(QDropEvent *event)
{
if (event->source() == this && event->possibleActions() & Qt::MoveAction)
return;
//在这种情况下,我们拒绝处理移动操作我们接受的每种类型的drop操作都会进行相应的检查和处理:
if (event->proposedAction() == Qt::MoveAction) {
event->acceptProposedAction();
// Process the data from the event.
} else if (event->proposedAction() == Qt::CopyAction) {
event->acceptProposedAction();
// Process the data from the event.
} else {
// Ignore the drop.
return;
}
...
}

注意,我们在上面的代码中检查了单独的drop操作。如上所述,在忽视建议的操作一节中,有时需要覆盖建议的drop操作,并从可能的drop操作的选择中选择不同的操作。为此,需要检查事件possibleActions()提供的值中是否存在每个操作,使用setDropAction()设置drop操作,然后调用accept()。

九、drop矩形

(Drop Rectangles)

窗体的dragMoveEvent()可用于限制窗体的某些部分的drop,方法是仅在光标位于这些区域内时接受建议的删除操作。例如,当光标位于子窗体(dropFrame)上时,以下代码接受任何建议的drop操作:

void Window::dragMoveEvent(QDragMoveEvent *event)
{
if (event->mimeData()->hasFormat("text/plain")
&& event->answerRect().intersects(dropFrame->geometry()))
event->acceptProposedAction();
}

如果在拖放操作期间需要提供视觉反馈、滚动窗口或任何适当的操作,也可以使用dragMoveEvent()。

九、drop矩形

(Drop Rectangles)

窗体的dragMoveEvent()可用于限制窗体的某些部分的drop,方法是仅在光标位于这些区域内时接受建议的删除操作。例如,当光标位于子窗体(dropFrame)上时,以下代码接受任何建议的drop操作:

void Window::dragMoveEvent(QDragMoveEvent *event)
{
if (event->mimeData()->hasFormat("text/plain")
&& event->answerRect().intersects(dropFrame->geometry()))
event->acceptProposedAction();
}

如果在拖放操作期间需要提供视觉反馈、滚动窗口或任何适当的操作,也可以使用dragMoveEvent()。

你可能感兴趣的:(qt,c++,开发语言)