Qt 拖放入门
基本概念
拖-放技术是基本的传输协议,它把一个数据对象从源拖动到目标处,其过程如下:终端用户在源应用程序的窗口中选择一个数据对象,用鼠标点中此对象,然后按住鼠标把对象拖动到目标应用程序的窗口中,再松开鼠标按钮。
所以拖-放,实质是指用鼠标拖动的方法,在不同程序的窗口之间、同一个程序的不同窗口之间或不同控件之间,进行移动、复制和粘贴等操作的技术。
被拖动的数据对象按指定的数据格式提供数据,拖放操作结束时,接收拖放的窗口按指定的数据格式提取有关数据,并根据提取的数据生成相应的对象。
Windows系统拖放到Qt程序
Windows的一个显著特色就在于支持鼠标拖放,使用拖放可以简化许多烦琐的操作。Qt也支持拖放操作,从windows系统拖动数据对象到Qt的应用程序,是很方便的。
QTextEdit 类已经实现了关于拖放操作数据对象的函数,而且默认支持拖放操作,方便用来直接演示:
-
virtual bool canInsertFromMimeData(const QMimeData *source) const
-
virtual QMimeData * createMimeDataFromSelection() const
-
virtual void insertFromMimeData(const QMimeData *source)
-
virtual void dragEnterEvent(QDragEnterEvent *e) override
-
virtual void dragLeaveEvent(QDragLeaveEvent *e) override
-
virtual void dragMoveEvent(QDragMoveEvent *e) override
-
virtual void dropEvent(QDropEvent *e) override
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QTextEdit t; // QTextEdit默认支持拖放功能,用来演示
t.resize(300,200);
t.setWindowFlags(Qt::WindowStaysOnTopHint); // 为方便操作,窗口前置
t.show();
return a.exec();
}
从上面的示例可以看出 Qt 和 Windows 操作系统同时对拖放技术的支持,如果拖放的数据对象格式是文本类型的,QTextEdit会直接复制其文本,如果是其他类型的,QTextEdit会自动将其URL添加到文本框中。
拖放的对象:MIME
Qt 和 windows 操作系统之所以可以使用拖放传输数据,是因为拖放的数据对象都是基于MIME的标准。
MIME (Multipurpose Internet Mail Extensions) 用来表示文档、文件或字节流的性质和格式,现已成为互联网通讯的标准。
语法
type/subtype
MIME的语法组成结构非常简单;由类型与子类型两个字符串中间用
'/'
分隔而组成。不允许空格存在。
常用的数据类型:
类型 | 描述 | 典型示例 |
---|---|---|
text | 表明文件是普通文本,理论上是人类可读 | text/plain , text/html , text/css, text/javascript |
image | 表明是某种图像。不包括视频,但是动态图(比如动态gif)也使用image类型 | image/gif , image/png , image/jpeg , image/bmp , image/webp , image/x-icon , image/vnd.microsoft.icon |
audio | 表明是某种音频文件 | audio/midi , audio/mpeg, audio/webm, audio/ogg, audio/wav |
video | 表明是某种视频文件 | video/webm , video/ogg |
application | 表明是某种二进制数据 | application/octet-stream , application/pkcs12 , application/vnd.mspowerpoint , application/xhtml+xml , application/xml , application/pdf |
对于text文件类型若没有特定的subtype,就使用 text/plain
。
类似的,二进制文件没有特定或已知的 subtype,即使用 application/octet-stream
。
获取MIME的数据类型
Qt中表示MIME数据的类是QMimeData类,在拖放的过程中,在QDropEvent、QDragMoveEvent 以及QDragEnterEvent中作为参数传输。三者继承关系如下:
TextEdit
TextEdit 子类化 QTextEdit,重写dropEvent事件函数,从而获得QMimeData 类。
定义
class TextEdit : public QTextEdit
{
Q_OBJECT
public:
explicit TextEdit(QWidget *parent = nullptr);
void dropEvent(QDropEvent *e) override;
};
基础实现
#include "TextEdit.h"
TextEdit::TextEdit(QWidget *parent)
:QTextEdit(parent)
{
}
void TextEdit::dropEvent(QDropEvent *e)
{
QTextEdit::dropEvent(e);
const QMimeData *mime = e->mimeData();
foreach(const QString str,mime->formats())
append(str);
}
但是在Windows系统上,QMimeData::formats() 将使用x-qt-windows-mime子类型返回MIME数据中可用的自定义格式,以指示它们表示非标准格式的数据。 格式将采用以下形式:
application/x-qt-windows-mime;value="<custom type>"
这种格式不是标准格式,万幸Qt还有QMimeDatabase 和 QMimeType类,这两个类中的函数就可以分辨出对象数据的MIME类型的标准格式:
- QMimeType QMimeDatabase::mimeTypeForData(QIODevice *device) const
- QMimeType QMimeDatabase::mimeTypeForFile(const QString &fileName, QMimeDatabase::MatchMode mode = MatchDefault)
- QString QMimeType::name() const
改进实现
void TextEdit::dropEvent(QDropEvent *e)
{
QTextEdit::dropEvent(e);
const QMimeData *mime = e->mimeData();
QString filePath = mime->urls().at(0).toLocalFile();
QFile file(filePath);
QMimeDatabase mdd;
QMimeType mType;
if(file.size()==0) // 大小为0的文件,是无法使用数据内容分辨MIME格式的
mType = mdd.mimeTypeForFile(file,QMimeDatabase::MatchExtension); // 使用文件的名称分辨MIME格式
else{
mType = mdd.mimeTypeForFile(file,QMimeDatabase::MatchContent); ; // 使用数据内容来分辨MIME格式
}
append(mType.name() + "\n\n");
}
这种读取数据内容的方式,可以获取正确的文件类型,而不用管文件后缀名是否存在,或者后缀名是否正确。
总结
拖放技术是基于MIME标准的数据传输协议,理解了MIME数据对象的内容和格式,将有助于对拖放操作的理解。