QT核心编程之鼠标拖放是本节介绍的内容。QT核心编程我们要分几个部分来介绍,想参考更多内容,请看末尾的编辑推荐进行详细阅读,先来看本篇内容。
拖放提供了一种用户在应用程序之间或之内传递信息的一种简单可视机制。在术语中,这被称为"直接操作模型"。拖放在功能上类似剪贴板的剪切和粘贴机制。拖放机制包括拖动、放下、剪贴板、拖放操作、添加新的拖放类型、高级拖放以及和其它应用程序之间的操作几个方面。下面从这几个方面分别进行说明:
(1)拖动
开始一个拖动,比如是在鼠标移动事件,创建一个适合你的媒体的QDragObject的子类的对象,例如:对于文本使用QTextDrag,对于图片使用QImageDrag。然后调用drag()方法。例如,从一个窗口部件中开始拖动一些文本:
- void MyWidget::startDrag() {
- QDragObject *d = new QTextDrag( myHighlightedText(), this );
- d->dragCopy(); //拷贝选中文本 // 不要删除d
- }
注意在拖动之后,QDragObject没有被删除。在拖放明显完成后,这个QDragObject需要被保存。因为它还可能需要与其它进程通信。最后 Qt会删除这个对象。如果拥有拖动对象的窗口部件在删除拖动对象之前被删除,那么任何没有完成的放下操作将会被取消,并且拖动对象会被删除。因为这个原因,你应该小心对待对象引用。
(2)放下
为了能在一个窗口部件中接收被放下的媒体,这个窗口部件调用setAcceptDrops(TRUE)(如:在它的构造函数中),并且重载事件处理方法dragEnterEvent()和dropEvent()。对于更复杂的应用程序,重载dragMoveEvent()和 dragLeaveEvent()也是必需的。
例如,当拖动后放下文本或图片时,窗口部件接受并处理放下操作的代码如下:
- MyWidget::MyWidget(...) : QWidget(...) {
- ... setAcceptDrops(TRUE); //接收被放下的媒体
- }//当一个拖动正在进行并且鼠标进入这个窗口部件,这个事件处理函数被调用
- void MyWidget::dragEnterEvent(QDragEnterEvent* event) {
- event->accept( QTextDrag::canDecode(event) || QImageDrag::canDecode(event) );
- }//当拖动在这个窗口部件上被放下,这个事件处理器被调用
- void MyWidget::dropEvent(QDropEvent* event) {
- QImage image;
- QString text;
- if ( QImageDrag::decode(event, image) ) {//解码图片
- insertImageAt(image, event->pos()); //在窗口部件中插入图片
- } else if ( QTextDrag::decode(event, text) ) {
- insertTextAt(text, event->pos());
- }
- }
(3)剪贴板
QDragObject、QDragEnterEvent、QDragMoveEvent和QDropEvent类都是 QMimeSource(提供类型信息的类)的子类。如果你在QDragObject中基于你的数据进行传递,你不仅可使用拖放,而且还可以使用传统的剪切和粘贴。QClipboard有两个函数:
- setData(QMimeSource*)
- QMimeSource* data()const
使用这些函数,你可以把你的拖放初始信息放到剪贴板中:
- void MyWidget::copy(){
- QApplication::clipboard()->setData( new QTextDrag(myHighlightedText()) );
- }
- void MyWidget::paste(){
- QString text;
- if ( QTextDrag::decode(QApplication::clipboard()->data(), text) )
- insertText( text );
- }
你甚至能使用QDragObject的子类作为文件I/O部分。例如,如果你的程序有一个QDragObject的子类把CAD设计编码成DXF格式,你可以象下面这样存储和装载这个格式的文件:
- void MyWidget::save(){
- QFile out(current_file_name);
- out.open(IO_WriteOnly);
- MyCadDrag tmp(current_design); // MyCadDrag是QDragObject的子类
- out.writeBlock( tmp->encodedData( "image/x-dxf" ) );
- } void MyWidget::load(){
- QFile in(current_file_name);
- in.open(IO_ReadOnly);
- if ( !MyCadDrag::decode(in.readAll(), current_design) ) {
- QMessageBox::warning( this, "Format error", tr("The file \"%1\" is not in any supported format") .arg(current_file_name) );
- }
- }
(4)拖放操作
在一些简单的情况下,拖放的目标接收一个被拖动的数据的拷贝,并且由源来决定是否删除初始的拖动对象。这是QDropEvent中的"Copy"操作。目标也可以选择理解其它操作,特别是"Move"和"Link"操作。如果目标理解了"Move"操作,目标负责拷贝和删除操作,源不会尝试删除数据。如果目标理解为"Link"操作,它存储它自己的引用到初始信息中,并且源不会删除初始信息。最通用的拖放操作是在同一个窗口部件中执行一个"Move"操作。
拖动操作的另一个主要用途是当使用一个引用类型,比如text/uri-list,实际上被拖动的数据是文件或对象的引用。
(5)添加新的拖放类型
拖放不仅仅局限于文本和图片,任何信息都可以被拖放。为了在应用程序之间拖放信息,两个应用程序必须指明彼此都能接受和产生的数据格式。这个可以通过使用MIME类型来获得。拖动的源提供一个它能产生的MIME类型列表(按从最合适的到最少合适的顺序排列),并且放下的目标选择一种它能接受的类型。例如,QTextDrag提供了"text/plain"MIME类型(普通的没有格式的文本),还有"text/utf16"和"text /utf8"的Unicode格式的类型。QImageDrag提供了"image/*"类型,*是QImageIO支持的任何一种图片格式,并且 QUriDrag子类提供了"text/uri-list"的支持,它是传输一个文件名列表(或URL)的标准格式。
为了实现一些还没有可用QDragObject子类的信息类型的拖放,首先和最重要的步骤是查找合适的存在格式:IANA(Internet Assigned Numbers Authority)在ISI(Information Sciences Institute)提供了一个MIME媒体类型的分级列表。使用标准的MIME类型将会使你的应用程序现在及未来能更好地与其它软件互相操作。
为了支持另外的媒体类型,从QDragObject或QStoredDrag派生类。当你需要提供多种媒体类型的支持时,从QDragObject派生类。当一个类型足够时,就从更简单的QStoredDrag派生类。
QDragObject的子类将会重载const char* format(int i) const和QByteArray encodedData(const char* mimetype) const成员,并且提供一套方法编码媒体数据,提供静态成员canDecode()和decode()解码输入的数据,QImageDrag的成员函数 bool canDecode(QMimeSource*) const和QByteArray decode(QMimeSource*) const在子类中需要类似的重载。
QStoredDrag的子类提供了提供一套方法编码媒体数据,静态成员canDecode()和decode()对进入的数据进行解码。
(6)高级拖放
在剪贴板模式中,用户可以剪切或复制资源信息,然后粘贴它。相似地,在拖放模式中,用户可以拖动信息的拷贝或者拖动信息本身到一个新的位置(移动它)。拖放模式对于程序员来说都是更多的复杂性:程序直到放下(粘贴)完成才会知道用户是想剪切还是复制。在应用程序之间拖动,这个没有什么区别,但是在一个应用程序之内进行拖动,应用程序必须小心不要将拷贝粘贴到同一个地方。例如,在同上窗口部件中拖动文本,拖动的开始点和放下事件处理函数应象下面这样重载:
- void MyEditor::startDrag(){
- QDragObject *d = new QTextDrag(myHighlightedText(), this);
- if ( d->drag() && d->target() != this ) cutMyHighlightedText(); //剪切选中的文本
- }
- void MyEditor::dropEvent(QDropEvent* event){
- QString text;
- if ( QTextDrag::decode(event, text) ) {
- if ( event->source() == this && event->action() == QDropEvent::Move ) { // 在同一个窗口部件时,不能使用粘贴拷贝,而应是移到到这个位置
- event->acceptAction();
- moveMyHighlightedTextTo(event->pos());
- }
- else {
- pasteTextAt(text, event->pos()); //粘贴拷贝
- }
- }
- }
一些窗口部件在数据被拖动到它们上面时需要指定"是"或"否"接收。例如,一个CAD程序也许只接收在视图中的文本对象上放下的文本。在这种情况下,dragMoveEvent()被使用并且给定接受或者忽略拖动的区域。代码列出如下:
- void MyWidget::dragMoveEvent(QDragMoveEvent* event){
- if ( QTextDrag::canDecode(event) ) {
- MyCadItem* item = findMyItemAt(event->pos());
- if ( item )
- event->accept();
- }
- }
(7)和其它应用程序之间的操作
在X11上,拖动使用公有的XDND协议,而Qt在Windows上使用OLE标准,Qt在Mac上使用Carbon拖动管理器。在X11 上,XDND使用MIME,所以不需要转换。Qt的应用编程接口与平台无关。在Windows上,识别MIME的应用程序可以通过使用MIME类型的剪贴板格式名字进行通信。一些Windows应用程序已经对它们的剪贴板格式使用MIME命名规范了。在内部,Qt有能力在专有的剪贴板格式和MIME类型之间转换。在X11上,Qt也支持使用Motif拖放协议的拖动。
小结:QT核心编程之鼠标拖放的内容介绍完了,希望本文对你有所帮助,如果需要更多的内容,请参考编辑推荐。
【编辑推荐】