《qt核心编程.pdf》由会员分享,可在线阅读,更多相关《qt核心编程.pdf(29页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、11Qt 模板库Qt 模板库(QT Template Library 简称 QTL)是一套提供对象容器的模板。如果你的编译器没有适当的STL(标准模板库)可用,QTL将被代替使用。QTL 提供了对象的链表、对象的矢量(动态数组)、从一个类型到另一个类型的映射(或称为字典)和相关的迭代器和算法。一个容器是包含和管理其它对象的一个对 象,并且提供迭代器对被包含的对 象进行访问。Qt 模板类说明如表 2。表 2Qt 模板类说明模板类名称说明QMap提供基于值的一个字典的模板类QMapConstIteratorQMap的常量迭代器QMapIteratorQMap的迭代器QPair提供基于值的一对元素的
2、模板类QValueList提供基于值的一个双向链表的模板类QValueListConstIteratorQValueList的常量迭代器QValueListIteratorQValueList的迭代器QValueStack提供基于值的一个堆栈模板类QValueVector提供基于值的一个动态数组模板类QTL 类的命名约定与其他 Qt 类一致(比如,count()、isEmpty())。它们还提供额外的函数来兼容STL 算法,比如size()和empty()。可以像使用STL 的函数 map一样来使用它们。与STL 相比,QTL仅仅包含了STL 容器应用程序接口的最重要的特性,没有平台差异,通常
3、要慢一些并且经常扩展为更少的对象代码。如果你不想拷贝存储对象,你最好使用QPtrCollection 及派生类。它们就是被设计用来处理各种类指针的。QObject 没有拷贝 构造函数,因此 QObject 不能作为一个值使用。但可以存储指向 QObject 的指针到QValueList。当然,直接使用 QPtrList 更好。QPtrList 像所有其它的基于 QPtrCollection 的容器一样,提供了比速度优化了、基于值的容器更多健全的检查。如果你有一些使用值的对象,并且在你的目标平台没有可用的 STL,Qt 模板库就可以替代它。使用值的对象至少需要一个拷贝构造函数、一个赋值操作符和一
4、个默认构造函数(如:一个没有任何参数的构造函数)。注意一个快速的拷贝构造函数对于容器的高性能是关键的,因为许多拷贝操作将会发生。如果你想排序你的数据,你必须在你的数据类中实现operator()。Qt 模板库是为高性能而设计,迭代器是非常快的。为了实现这样的性能,Qt 模板库比基于QPtrCollection 的 集合类做更少的错误 检查。一个 QTL 容器,例如:QTL容器没有跟踪任何相关的迭代器。这样在诸如删除条目时没有执行有效性检查,但它提供了很好的执行性能。11.1 迭代器(Iterators)Qt 模板库打交道的是值对象,而不是指针对象。迭代器是最好的遍历容器方法。遍历一个容器可使用
5、像下面的循环:typedef QValueList List;List l;for(List:Iterator it=l.begin();it!=l.end();+it)printf(Number is%in,*it);begin()返回第一个元素的迭代器,end()返回的是最后一个元素之后的一个迭代器。end()标明的是一个无效 的位置,它永远不能被解除引用。它 只是任何一次迭代的终止条件,迭代可以从 begin()或 end()开始。同样的概念也适用于其它容器类,例如,用于QMap 和 QValueVector 的迭代方法如下:typedef QMap Map;Map map;for(Ma
6、p:iterator it=map.begin();it!=map.end();+it)printf(Key=%s Data=%sn,it.key().ascii(),it.data().ascii();typedef QValueVector Vector;Vector vec;for(Vector:iterator it=vec.begin();it!=vec.end();+it)printf(Data=%dn,*it);11.2 算法Qt 模板库定义了大量操作容器的算法。这些算法用模板库函数实现,还提供了有迭代器的容器的通用代码。例如:qHeapSort()和 qBubbleSort()
7、提供了著名的堆排序和冒泡排序算法。你可以象下面这样使用它们:typedef QValueList List;List l;l 42 100 1234 12 8;qHeapSort(l);List l2;l2 42 100 1234 12 8;List:Iterator b=l2.find(100);List:Iterator e=l2.find(8);qHeapSort(b,e);double arr=3.2,5.6,8.9;qHeapSort(arr,arr+3);第一个例子对整个列表排序。第二个例子对两个迭代器之间的所有元素排序,即 100、1234和 12。第三个例子表明迭代器是作为指针
8、使用的。一些常用的模板函数说明如下:(1)函数 qSwap()用来交换两个变量的值,例如:QString second(Einstein);QString name(Albert);qSwap(second,name);(2)函数 qCount()用于统计容器中一个值出现的次数。例如:QValueList l;l.push_back(1);/放入 1到 l 链表中 l.push_back(1);l.push_back(1);l.push_back(2);int c=0;qCount(l.begin(),l.end(),1,c);/统计 1 的个数 c,c=3(3)函数 qFind()用于查找容
9、器中一个值的第一次出现位置。例如:QValueList l;l.push_back(1);l.push_back(1);l.push_back(1);l.push_back(2);/查找 2所在的位置 QValueListIterator it=qFind(l.begin(),l.end(),2);(4)函数 qFill()用于将一个值拷贝填充到一个范围。例如:QValueVector v(3);qFill(v.begin(),v.end(),99);/将 99 填充整个 v 数组,v包含 99,99,99(5)函数 qEqual()用来比较两个范围的元素是否相等,两个范围的元素个数不一定相等
10、。只要第一个范围的元素与第二个范围的对应元素都相等时,就认为这两个范围相等。例如:QValueVector v1(3);v10=1;v12=2;v13=3;QValueVector v2(5);v10=1;v12=2;v13=3;v14=4;v15=5;bool b=qEqual(v1.begin(),v2.end(),v2.begin();/b=TRUE(6)函数 qCopy()用于拷贝一个范围的元素到输出迭代器,例如:QValueList l;l.push_back(100);l.push_back(200);l.push_back(300);QTextOStream str(stdout
11、);/拷贝 l中所有元素到输出迭代器 QTextOStreamIterator qCopy(l.begin(),l.end(),QTextOStreamIterator(str);(7)函数 qCopyBackward()用于拷贝一个容器或者它的一部分到一个输出迭代器,拷贝的次序是从后面开始,例如:QValueVector vec(3);vec.push_back(100);vec.push_back(200);vec.push_back(300);QValueVector another;/“another”包含的是按倒序排列的(300、200、100)。qCopyBackward(vec.
12、begin(),vec.end(),another.begin();如果你写了新的算法,请考虑把它们写成模板函数,这样就可以使它们能够用在尽可能多的容器上了。在上一个例子中,你可以很容易地使用qCopy()打印出一个标准 C+数组,方法列出如下:int arr=100,200,300;QTextOStream str(stdout);qCopy(arr,arr+3,QTextOStreamIterator(str);11.3 数据流串行化所有提到的容器(如:QValueList、QStringList、QValueStack 和 QMap等)都可被相应的流操作符串行化。下面是一个例子。QDat
13、aStream str(.);QValueList l;/在这里填充这个列表 str l;容器还能象下面这样被再一次读入:QValueList l;str l;12集合类一个集合类是装有多个条目的容器,每个条目是某种数据结构,集合类能执行对容器中的条目的插入、删除及查找等操作。Qt 有几个基于值和基于指针的集合类。基于指针的集合类使用指向条目的指针来工作,而基于值的集合类存储着它们条目的拷贝。基于值的集合类类似于 STL 容器类,能和 STL算法和容器一起使用。基于值的集合类说明如表4 所示:表4基于值的集合类表类名称说明QValueList基于值的链表QValueVector基于值的矢量结构
14、QValueStack基于值的栈结构QMap基于值的字典结构基于指针的集合类说明如表5 所示:表 5基于指针的集合类表类名称说明QCache和QIntCacheLRU(least recently used)缓存结构。QDict、QIntDict和QPtrDict字典结构。QPtrList双向链接的链表结构。QPtrQueueFIFO 先进先出(first in,first out)队列结构。QPtrStackLIFO后进先出(last in,first out)栈结构。QPtrVector矢量结构。QMemArray 是一个例外,它既不是基于指针也不是基于值,而是基于内存的结构。用于在有简单
15、数据结构的数组中使用QMemArray 效率最高,QMemArray 在拷贝和数组元素比较时使用位逻辑运算符操作。这些类中有一些具有迭代器,迭代器是遍历集合类中的条目的类。在 Qt 模板库里,基于值的集合和算法集成在一起。下面讨论基于指针的容器。12.1 基于指针的容器的结构基于指针的容器有4 个内部基类(QGCache,QGDict,QGList 和 QGVector)操作 void 类型指针。通过增加/删除条目指针,一个由这4 个类组成的薄模板层实现了实际的集合。允许Qt 的模板类的策略使得在空间上很经济(实现这些模板类仅增加了对基类的内联调用),而且还不影响执行效率。示例:QPtrLis
16、t 使用下面的例子说明了如何存储Employee 条目到一个链表,并将它们以相反的次序打印出来。#include#include#include class Employeepublic:Employee(const char*name,int salary)n=name;s=salary;const char*name()const return n;int salary()const return s;private:QString n;int s;int main()QPtrList list;/指向 Employee 的指针链表。list.setAutoDelete(TRUE);/当链
17、表条目被移动时,删除条目。list.append(new Employee(Bill,50000);/链表追加新的对象。list.append(new Employee(Steve,80000);list.append(new Employee(Ron,60000);QPtrListIterator it(list);/遍历Employee 链表。for(it.toLast();it.current();-it)/从尾向头遍历。Employee*emp=it.current();printf(%s earns%dn,emp-name(),emp-salary();return 0;程序运行结果
18、如下:Ron earns 60000Steve earns 80000Bill earns 5000012.2 管理集合条目所有基于指针的集合继承了QPtrCollection 基类。这个类仅知道集合中的条目个数和删除策略。当集合中的条目被移去时,缺省时它们不被删除。QPtrCollection:setAutoDelete()定义了删除策略。在上述QPtrList 使用示例子,我们激活了自动删除功能来进行链表删除。当插入一个条目到一个集合时,仅指针被拷贝,而不是拷贝条目本身。这称为浅拷贝。当插入一个条目时,拷贝所有条目的数组到集合中也是可能的,这称为深拷贝。所有的集合类函数在插入条目时调用虚拟
19、函数 QPtrCollection:newItem()。如果你想进行深拷贝,你需要重载它。当从一个链表中移去一个条目时,调用虚拟函数 QPtrCollection:deleteItem()。如果自动删除功能被激活,在所有集合类中的缺省实现函数被调用来删除条目。基于指针的集合类,如:QPtrList,定义了指向对象的指针集合。我们在这里只讨论 QPtrList 类,其它的基于指针的集合类和所有集合类迭代器都有同样的使用方法。模板实例化方法如下:QPtrList list;在这个例子中,条目的类或类型是Employee,它必须在链表定义之前被定义。例如:class Employee .;QPtrL
20、ist list;12.3 迭代器(Iterators)QPtrListIterator 能在链表被修改的同时非常安全的遍历链表。在同一个集合上,多个迭代器能独立地工作。QPtrList 有一个指向所有迭代器的内部链表,这些迭代器当前操作链表。当一个链表条目被移去时,链表更新所有的指向这个条目的迭代器。QDict 和 QCache集合没有遍历函数。为了遍历集合,你必须使用 QDictIterator 或 QCacheIterator。Qt 预定义的集合类有字符串链表:QStrList,QStrIList(在 qstrlist.h 中)和 QStringList(在 qstringlist.h
21、中)。在绝大多数情况下你将选择QStringList,它是一个共享的 QString Unicode 字符串的值链表。QPtrStrList 和 QPtrStrIList 仅存储字符指针,而不是字符串本身。基于指针的集合类和相关的迭代器类说明如表 4。表 4基于指针的集合类和相关的迭代器类列表QAsciiCache基于char*keys 的缓存的模板类。QAsciiCacheIteratorQAsciiCache集合的迭代器。QAsciiDict基于char*keys 的字典的模板类。QAsciiDictIteratorQAsciiDict集合的迭代器。QBitArrayBit位数组。QBit
22、Val与 QBitArray 一起用的内部类QBuffer在QByteArray上操作的I/O设备。QByteArray字节数组QCache基于QString keys的模板类。QCacheIteratorQCache集合的迭代器。QCStringC语言中0结尾的字符数组(char*)。QDict基于QString keys 的字典的模板类。QDictIteratorQDict collections的迭代器。QIntCache基于long keys 的缓存的模板类。QIntCacheIteratorQIntCache集合的迭代器。QIntDict基于long keys 的字典的模板类。QIn
23、tDictIteratorQIntDict集合的迭代器。QObjectListQObjects的QPtrList链表。QObjectListItQObjectLists的迭代器。QPtrCollection大多数基于指针的Qt集合的基类。QPtrDict基于void*keys的字典的模板类。QPtrDictIteratorQPtrDict的迭代器。QPtrList双向链表的模板类。QPtrListIteratorQPtrList集合的迭代器。QPtrQueue一个队列的模板类。QStrIList对于大小写敏感的char*双向链接的链表。QStrListchar*双向链接的链表。13 Qt 线程
24、Qt 对线程提供了支持,它引入了一些基本与平台无关的线程类、线程安全传递事件的方式和全局 Qt 库互斥量允许你从不同的线程调用Qt 的方法。Qt 中与线程应用相关的类如表 6 所示。表6Qt 中与线程相关的类QMutex线程之间的互斥锁QSemaphore整数信号量QThread与平台无关的线程QWaitCondition线程之间允许等待/唤醒的条件使用线程需要Qt 提供相应的线程库的支持,因此,在编译安装 Qt 时,需要加上线程支持选项。当在Windows 操作系统上编译Qt 时,线程支持是在一些编译器上的一个选项。在 Windows 操作系统上编译应用程序时,通过在qconfig.h 文件
25、中增加一个选项来解决来解决这个问题。在Mac OS X 和 Unix 上编译Qt 时,你应在运行 configure 脚本时添加-thread选项。在Unix 平台上,多线程 程序必须用特殊的线程支持库连接,多线 程程序必须连接线程支持库libqt-mt,而不是标准的 Qt 库。编译应用程序时,你应该使用宏定义QT_THREAD_SUPPORT 来编译(如:编译 时使用-DQT_THREAD_SUPPORT)。13.1 线程类 QThread 在 Qt 中提供了QThread 线程类,它提供了创建一个新线程的方法。线程通过重载 QThread:run()函数开始执行的,这一点与Java 中的线
26、程类相似。示例1:一个简单的线程下面的例子实现了一个简单的继承自QThread 的用户线程类,并运行 2个线程,线程 b 在线程 a运行完后运行。代码列出如下:class MyThread:public QThread public:virtual void run();void MyThread:run()/运行线程 for(int count=0;count lock()和 qApp-unlock())是在访问 Qt 的 GUI 界面资源时用到的互斥量。在 Qt 中没有使用互斥量而调用一个函数通常会导致不可预知的行为。从另外一个线程中调用 Qt 的一个 GUI 相关函数需要使用Qt 库互斥
27、量。在这种情况下,所有 访问图形或窗口系统资源的函数都与 GUI 相关。如果对象仅被一个线程访问,使用容器类,字符串或者输入/输出类不需要任何互斥量。13.2 线程安全的事件传递在Qt 中,一个线程总是一个事件线程,线程从窗口系统中拉出事件并且把它们分发给窗口部件。静态方法QThread:postEvent 从线程中 邮递事件,而不是从事件线程。事件线程被唤醒并且事件象一个正常窗口系统 的事件一样在事件线程中被分发。例如,你可以从不同的线程强制一个窗口部件进行重 绘,方法如下:QWidget*mywidget;QThread:postEvent(mywidget,new QPaintEvent
28、(QRect(0,0,100,100);上述代码将异步地使mywidget 在它区域中重绘一块 100*100 的正方形区域。另外,还需要一些机制使得处于等待状态的线程在给定条件下被唤醒。QWaitCondition 类就提供了这种功能。线程等待的条件QWaitCondition 满足,QWaitCondition 表明发生了什么事情,它阻塞直到这件事情发生。当发 生给定的事情时,QWaitCondition 将唤醒等待该事情的所有线程或者唤醒任意一个被选中的线程。(这和POSIX 线程条件变量具有相同功能,是 Unix 上的一种实现。)示例2:QWaitCondition 类应用下面这个例子
29、的功能是:当你按下按钮,这个程序就会唤醒 worker 线程,这个线程在按钮上显示工作状态:等待(Waiting)还是正在 工作(Working)。当按钮被按下时,worker 线程正在工作,那么对线程不产生影响。当run 函数再次循环到 mycond.wait()时,线程 阻塞并等待。当按钮再被按下时,触发 slotClicked()函数运行,唤醒等待的线程。#include#include /全局条件变量QWaitCondition mycond;/Worker 类实现class Worker:public QPushButton,public QThread Q_OBJECT publi
30、c:Worker(QWidget*parent=0,const char*name=0):QPushButton(parent,name)setText(Start Working);/将 QPushButton 继承来的信号与槽 slotClicked()连接起来。connect(this,SIGNAL(clicked(),SLOT(slotClicked();/调用从 QThread 继承来的 start()方法开始线程的执行 QThread:start();public slots:void slotClicked()/唤醒等待这个条件变量的一个线程 mycond.wakeOne();p
31、rotected:void run()/重载run函数 while(TRUE)/锁定应用程序互斥锁,并且设置窗口标题来表明我们正在等待开始工作 qApp-lock();setCaption(Waiting);qApp-unlock();/等待直到我们被告知可以继续 mycond.wait();/如果到了这里,表示我们已经被另一个线程唤醒 qApp-lock();setCaption(Working!);/设置标题,表示正在工作 qApp-unlock();int main(int argc,char*argv)QApplication app(argc,argv);/创建一个worker Wo
32、rker firstworker(0,worker);app.setMainWidget(&worker);/将worker 设置为应用程序的主窗口。worker.show();return app.exec();当进行线程编程时,需要注意的一些事项:(1)在持有 Qt 库互斥量时不要做任何阻塞操作。这将冻结事件循环。(2)确认你锁定一个递归 QMutex的次数等于解锁的次数,不能多也不能少。(3)在调用除了 Qt 容器和工具类外的任何东西之前锁定 Qt 应用程序互斥量。(4)谨防隐含的共享类,如果你需要在线程之间指定它们,你应该用detach()分离它们。(5)小心没有被设计成线程安全的 Q
33、t 类,例如,QPtrList 的 API 接口不是线程安全的,并且如果不同的线程需要遍历一个QPtrList,它们应该在调用 QPtrList:first()之前锁住,在到达终点后解锁。(6)确信仅在 GUI线程中创建继承自 QWidget、QTimer 和QSocketNotifier 的对象。在一些平台上,创建在线程中而不是GUI 线程的对象永远不会接收到底层窗口系统的事件。(7)和上面很相似,只在 GUI线程中使用 QNetwork 类。因为所有的 QNetwork 类都是异步的,没必要把QSocket 用在多线程中。(8)永远不要尝试在不是 GUI线程的线程中调用 processEv
34、ents()函数。这也包括QDialog:exec()、QPopupMenu:exec()、QApplication:processEvents()和其它一些函数。(9)在你的应用程序中,不要把普通的 Qt 库和支持线程的 Qt 库混合使用。这意味着如果你的程序使用了支持线程的Qt 库,你就不能连接普通 的 Qt 库、动态的载入普通 Qt 库或者动态地连接其它依赖普通 Qt 库的库或者插件。在一些系统上,这样做会导致Qt 库中使用的静态数据崩溃。14鼠标拖放拖放提供了一种用户在应用程序之间或之内传递信息的一种简单可视机制。在术语中,这被称为直接操作模型。拖放在功能上类似剪贴板的剪切和粘贴机 制
35、。拖放机制包括拖动、放下、剪贴板、拖放操作、添加新的拖放类型、高级拖放以及和其它应用程序之间的操作几个方面。下面从这几个方面分别进行说明:(1)拖动开始一个拖动,比如是在鼠标移动事件,创建一个适合你的媒体的 QDragObject 的子类的对象,例如:对于文本使用QTextDrag,对于图片使用QImageDrag。然后调用 drag()方法。例如,从一个窗口部件中开始拖动一些文本:void MyWidget:startDrag()QDragObject*d=new QTextDrag(myHighlightedText(),this);d-dragCopy();/拷贝选中文本 /不要删除d。
36、注意在拖动之后,QDragObject 没有被删除。在拖放明显完成后,这个QDragObject 需要被保存。因为它还 可能需要与其它进程通信。最后 Qt 会删除这个对象。如果拥有拖动对象的窗口部件在删除拖动对象之前被 删除,那么任何没有完成的放下操作将会被取消,并且拖动对象会被删除。因为这个原 因,你应该小心对待对象引用。(2)放下为了能在一个窗口部件中接收被放下的媒体,这个窗口部件调用 setAcceptDrops(TRUE)(如:在它的构造 函数中),并且重载 事件处理方法 dragEnterEvent()和 dropEvent()。对于更复杂的应用程序,重载dragMoveEvent(
37、)和 dragLeaveEvent()也是必需的。例如,当拖动后放下文本或图片时,窗口部件接受并处理放下操作的代码如下:MyWidget:MyWidget(.):QWidget(.).setAcceptDrops(TRUE);/接收被放下的媒体。/当一个拖动正在进行并且鼠标进入这个窗口部件,这个事件处理函数被调用。void MyWidget:dragEnterEvent(QDragEnterEvent*event)event-accept(QTextDrag:canDecode(event)|QImageDrag:canDecode(event);/当拖动在这个窗口部件上被放下,这个事件处理器
38、被调用。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(提供
39、类型信息的类)的子类。如果你在QDragObject 中基于你的数据进行传递,你不仅可使用拖放,而且还可以使用传统 的剪 切和粘贴。QClipboard 有两个函数:setData(QMimeSource*)QMimeSource*data()const使用这些函数,你可以把你的拖放初始信息放到剪贴板中:void MyWidget:copy()QApplication:clipboard()-setData(new QTextDrag(myHighlightedText();void MyWidget:paste()QString text;if(QTextDrag:decode(QAppli
40、cation: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-enc
41、odedData(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)拖放操作在一些简单的情况下,拖放的目标接收一个被拖动的数据的拷贝,并且由源来决定是否删除初始的拖动对
42、象。这是QDropEvent 中 的Copy操作。目标也可以选择理解其它操作,特别是Move和Link操作。如果目标理解了Move 操作,目标负责拷贝和删除操作,源 不会尝试删除数据。如果目标理解为Link操作,它存储 它自己的引用到初始信息中,并且源不会删除初始信息。最通用的拖放操作是在同一个窗口部件中执 行一个Move操作。拖动操作的另一个主要用途是当使用一个引用类型,比如 text/uri-list,实际上被拖动的数据是文件或对象的引用。(5)添加新的拖放类型拖放不仅仅局限于文本和图片,任何信息都可以被拖放。为了在应用程序之间拖放信息,两个应用程序必须 指明彼此都能接受和产生的数据格式。
43、这 个可以通过使用MIME 类型来获得。拖动的源提供一个它能产生的MIME 类型列表(按从最合适的到最少合适的顺序排列),并且放下的目标选择一种它能接受 的类型。例如,QTextDrag 提供了text/plainMIME 类型(普通的没有格式的文本),还有text/utf16和text/utf8的Unicode 格式的类型。QImageDrag 提供了image/*类型,*是 QImageIO支持的任何一种图片格式,并且 QUriDrag 子类提供了text/uri-list的支持,它是传输一个文件名列表(或 URL)的标准格式。为了实现一些还没有可用QDragObject 子类的信息类型的
44、拖放,首先和最重要的步骤是查找合适的存在格 式:IANA(Internet Assigned Numbers Authority)在 ISI(Information Sciences Institute)提供了一个MIME 媒体类型的分级列表。使用标准的MIME 类型将会使你的应用程序现在及未来能更好地与其它软件互相操作。为了支持另外的媒体类型,从QDragObject 或 QStoredDrag 派生类。当你需要提供多种媒体类型的支持时,从QDragObject 派生类。当一个类型足够时,就从更简单的 QStoredDrag 派生类。QDragObject 的子类将会重载 const cha
45、r*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)高级拖
46、放在剪贴板模式中,用户可以剪切或复制资源信息,然后粘贴它。相似地,在拖放模式中,用户可以拖动信息 的拷贝或者拖动信息本身到一个新的位置(移动它)。拖放模式对于程序员来说都是更多的复杂性:程序直 到放下(粘贴)完成才会知道用户是想剪切还是复制。在应用程序之间拖动,这个没有什么区别,但是在一个应用程序之内进行拖动,应用程序必须小心不要将拷贝粘贴到同一个地方。例如,在同上窗口部件中拖动 文本,拖动的开始点和放下事件处理函数应象下 面这样重载:void MyEditor:startDrag()QDragObject*d=new QTextDrag(myHighlightedText(),this);i
47、f(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 pasteTex
48、tAt(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 上,拖
49、动使用公有的 XDND 协议,而 Qt 在Windows 上使用 OLE 标准,Qt 在 Mac上使用 Carbon 拖动管理器。在X11 上,XDND 使用MIME,所以不需要转换。Qt 的应用编程接口与平台无关。在 Windows上,识别MIME 的应用程序可以通过使用 MIME 类型的剪贴 板格式名字进行通信。一些 Windows 应用程序已经对它们的剪贴板格式使用MIME 命名规范了。在内部,Qt 有能力在专有的剪贴板格式和 MIME 类型之 间转换。在X11 上,Qt 也支持使用Motif 拖放协议的拖动。15 键盘焦点Qt 的窗口部件在图形用户界面中按用户的习惯的方式来处理键盘焦点
50、。基本出发点是用户的击键能定向到屏 幕上窗口中的任何一个,和在窗口中任何一个部 件中。当用户按下一个键,他们期望键盘焦点能够到达正确 的位置,并且软件必须尽量满足这种希望。系统必须确定击键定位在哪一个应用程序、应用程序中的哪一 个窗口和窗口中的哪一个窗口部件。15.1 焦点移动的方式把焦点定位特殊的窗口部件的习惯方式有:(1)用户按下 Tab键(或者Shift 键+Tab 键)(或者有时是Enter 键)。(2)用户点击一个窗口部件。(3)用户按下一个键盘快捷键。(4)用户使用鼠标滚轮。(5)用户移动焦点到一个窗口,并且应用程序必须决定窗口中的哪个窗口部件应该得到焦点。这些移动机制的每个都是不