Qt 4.x:如何实现拖放到桌面或文件夹中?
Posted
技术标签:
【中文标题】Qt 4.x:如何实现拖放到桌面或文件夹中?【英文标题】:Qt 4.x: how to implement drag-and-drop onto the desktop or into a folder? 【发布时间】:2011-02-13 00:48:13 【问题描述】:我使用 Qt 4.x 编写了一个用 C++ 编写的小文件传输应用程序......它登录到服务器,向用户显示服务器上可用文件的列表,并让用户上传或下载文件.
这一切都很好;您甚至可以从桌面(或打开的文件夹)拖入文件,当您将文件图标拖放到服务器文件列表视图中时,拖放的文件会上传到服务器。
现在我也请求执行相反的操作...我的用户希望能够将文件从服务器文件列表视图中拖到桌面上,或者拖到打开的文件夹窗口中,并将该文件下载到该位置。
这似乎是一个合理的要求,但我不知道如何实现它。当图标被拖放到桌面或打开的文件夹窗口时,Qt 应用程序是否有办法找出与“放置事件”发生的位置相对应的目录?理想情况下,这将是一个基于 Qt 的平台中立机制,但如果不存在,那么适用于 MacOS/X 和 Windows(XP 或更高版本)的平台特定机制就足够了。
有什么想法吗?
【问题讨论】:
我很久以前就尝试使用 Qt 4.2 执行此操作,但没有找到正确执行此操作的方法。但也许有人可以提出解决方案? 看我的回复,你不能轻易跨平台。例如,windows 需要 shell 扩展,除非您将文件下载到临时目录,然后让 windows 将其复制到正确的位置。 @Jeremy Friesner 你能在不冻结你的 gui 的情况下解决这个问题吗?我需要实现一些非常相似的东西.. @atamanroman 不,我还没有找到满意的解决方案:( @JeremyFriesner hay 我发布了一个类似的问题,我想我可能在 FileZilla 的源代码中找到了一种可能的解决方案。以防你仍然感兴趣***.com/questions/38804272/… 【参考方案1】:看QMimeData
和它的文档,它有一个虚函数
virtual QVariant retrieveData ( const QString & mimetype, QVariant::Type type ) const
这意味着你拖到外面你相应地实现了这个功能
class DeferredMimeData : public QMimeData
DeferredMimeData(QString downloadFilename) : m_filename(downloadFilename)
virtual QVariant retrieveData (const QString & mimetype, QVariant::Type type) const
if (mimetype matches expected && type matches expected)
perform download with m_filename
delayed encoding 示例显示了这一原则。
您可能还必须覆盖 hasFormat
和 formats
以提供适当的类型,application/octet-stream
可能是最适合您的类型,您可能需要阅读 Windows 的具体处理方式使用 mime 类型进行拖放。
我不知道您将如何提供保存文件的文件名,但您可能必须进入 Windows 方面。查看QWindowsMime
的来源可能也会有所帮助。可能有一个多步骤的过程,您将收到对文件名的text/uri-list
数据的请求,然后是对数据的application/octet-stream
的请求。
希望对你有帮助
【讨论】:
这看起来几乎可以工作,除了我怀疑它会冻结我的应用程序的 GUI,直到retrieveData() 返回。鉴于下载可能需要很长时间(几分钟或几小时),这将是次优的。 :// 我想说这并不排除在单独的线程中进行下载或让 GUI 通过 QCoreApplication::processEvents() 处理事件。我不得不承认虽然我试图破解延迟将文件存放在桌面上的编码示例并没有太大的成功 单独的线程将无济于事,因为retrieveData() 仍需要阻塞,因为它以返回值返回数据,因此在下载完成之前无法返回。从retrieveData() 中调用processEvents() 可能会奏效,但我不相信它......我过去曾被这种技术所困扰。 对于其他研究此问题的人,2014 年延迟编码示例现在位于:qt-project.org/doc/qt-4.8/draganddrop-delayedencoding.html。其中的关键技巧是:使用自定义 MimeData 类,该类在调用它的 retrieveData 方法时发出一个信号。然后你就可以知道drop结束的地方了。 即使在retrieveData 方法中您也无法知道用户放置项目的路径时,发出信号有什么帮助?给出的唯一参数是type
和mimeType
doc.qt.io/qt-5/qmimedata.html#retrieveData【参考方案2】:
我认为你的做法是错误的。
你不在乎掉落到哪里,你只知道它被丢弃了。在 DropEvent 中,将文件下载到临时位置,然后将 mime 数据设置为下载的内容。当然,这最终可能会在硬盘驱动器上进行第二次复制,它从一开始就是跨平台的。之后您可以通过特定于平台的调用对其进行优化。
查看Dropsite example,了解其他来源的 mime 数据如何工作...
编辑:
如果您不编写 shell 扩展(至少对于 windows),看起来双重复制方法是标准。 Filezilla 和 7zip 都这样做,它们返回一个带有临时位置的“text/uri-list”mime 类型。这样,资源管理器将数据从临时位置复制到真实位置。您的应用程序也可以这样做,创建一个没有数据的临时文件(或只是名称)。使用delayed encoding example,在drop 上创建数据。整个操作看起来是非常特定于平台的。在 Linux(运行 KDE)上,我可以根据 mime 类型进行拖放。看来windows不够灵活。
【讨论】:
请在投票前阅读。 dropsite 示例向您展示了如何使用来自任何来源的 mime 类型。您可以启动您的程序,然后进入该程序以查看列出了哪些 mime 类型。至于第一部分,您让操作系统处理丢弃。您向它提供 mime 类型和数据,它会将其放置在您放置它的位置。就像将项目从一个浏览器窗口拖到另一个浏览器窗口一样。请阅读拖放文档以及bit.ly/bDooQe 我读过,但我认为答案不适用于手头的问题。特别是,操作系统不知道如何下载文件,所以它无法处理丢弃。我不能将文件的数据直接包含在拖动对象中,因为在拖动时该文件仅存在于服务器上。我无法在拖动之前或期间下载数据,因为文件可能很大(例如几 GB)。 如果您查看:doc.trolltech.com/4.6/draganddrop-delayedencoding.html 以及将文件从资源管理器拖放到 dropsite 示例中,您可能能够找到创建文件所需的 mime 类型的神奇组合。您可能必须触发 mouserlease 事件而不是 dropevent,因为就像您说的那样,您没有得到 drop。 我认为问题仍然存在,我不能只是在 drop 上“创建数据”,因为数据不存在于本地。我必须下载数据,这个过程可能需要很长时间(即分钟/小时)......太长了,无法简单地在下载完成之前使用 retrieveData() 块。 是的,这是棘手的部分,我不确定您是否能够启动线程并使用doc.qt.nokia.com/4.6/qfuturesynchronizer.html 等待下载,同时仍保持 GUI 响应。【参考方案3】:您只需实现the drag part。您不需要知道丢弃发生在哪里,因为接收它的应用程序会处理它并决定将您的文件存储在哪里。
您创建了一个QMimeData,不确定是哪种类型,但从我看到的here 和here,可能是“application/octet-stream”,您的文件作为QByteArray 或“text/uri-list”中的数据" 带有您文件的网址。
您创建一个 QDrag,并使用它的 setMimeData() 方法和 exec()(不确定选择哪个 QT::DropAction)。
这是一个例子(免责声明:我是用颜色而不是文件做的):
void DraggedWidget::mousePressEvent(QMouseEvent *event)
if (event->button() == Qt::LeftButton)
start_pos = event->pos();
void DraggedWidget::mouseMoveEvent(QMouseEvent *event)
if (event->buttons() & Qt::LeftButton)
int distance = (event->pos() - start_pos).manhattanLength();
if (distance >= QApplication::startDragDistance())
/* Drag */
QMimeData *mime_data = new QMimeData;
mime_data->setData( ... );
QDrag *drag = new QDrag(this);
drag->setMimeData(mime_data);
drag->exec(Qt::CopyAction);
抱歉,内容不完整,希望对大家有所帮助。
【讨论】:
问题是文件中的数据要等到下载完成后才能使用,而且数千兆字节的下载可能需要一个小时或更长时间。所以我需要知道下降发生在哪里,以便我的应用程序可以启动一个下载线程将数据写入该位置。 AFAICT,依赖于拖动时本地已经存在的数据的系统对我不起作用。包含文件的 URI 可能有效,但前提是文件可以通过操作系统知道如何下载的协议(例如 HTTP?)访问。当前,服务器使用专有协议。嗯....以上是关于Qt 4.x:如何实现拖放到桌面或文件夹中?的主要内容,如果未能解决你的问题,请参考以下文章