Qt4学习笔记 (4)

This is a repost from my previous Qt learning series, based on Qt 4.3.

    今天来说一些琐碎的细节问题:

1. Item-View 类:

    所谓Item-View类就是类似excel的表格啊, windows资源管理器左边的目录树结构啊之类的控件.
    Qt的Item-View架构有点类似Struts的MVC架构, model表示的是数据, view表示的是数据显示. 没有control了, 一个控件还需要什么control么? 不过Qt还是在中间加了一个东西叫做delegate, 代理. delegate的作用就是: 控制当数据model要修改的时候, UI的view应该怎么显示, 是现实一个combobox呢? 还是一个lineeditor这样. 于是整个Qt的架构被称作model/view架构.
    然后举一个例子来说明一些类的关系. Qt对于比如一个list列表控件会提供这样几个类: QListWidget, QListItem, QListView, QXxxModel(比如QStringListModel). 这几个类的关系是: a) QListWidget = QListView + QXxxModel, QListWidget是Qt提供的一个方便类, 可以拿来直接用, 而如果要自定义一些行为的话, 那么要分别继承QXxxModelQListView类来达到目的(这个做法让我想到了java/eclipse里的jface的用法). b) QListWidget里的每一项叫做QListItem.
    最后还有一个index和role的概念. QXxxModel里的每一项其实都有一个index, 叫做QModelIndex, 实际上就是一个抽象. 注意它是针对一个model的, 跟QXxxItem没有直接的关系. 这个QModelIndex的作用就是, 可以跟据这个index来拿model中的数据. 这里又有一个role角色的概念–这里其实就类似于一个attribute的名字. 比如你要自己实现一个model, 那么你必须实现如下函数:

    这里role是一个枚举, 可以参看Qt的帮助文档.

2. Container 类:

    这个东西其实没什么好多说的, 作用跟C++ STL的容器类一样, Qt也提供比如QVector, QLinkedList, QMap之类的容器类.
    不过用Qt的容器类有这样一些好处:
a) 它们支持implicitly shared的feature. 好高级的名字是吧..那如果告诉你其实就是copy-on-write, 那么应该能理解了吧(而COW本身就是一个设计模式: flyweight pattern). 我们用STL容器类的时候, 当要把它作为参数传递的时候, 一般都会传引用. 而如果用Qt类的话则不用. Qt会自己帮我们搞定一切, 不会因为copy参数而降低performance. 说到实现的话, 稍微喵了一眼源码, 其实就是一个引用计数(reference count) 的问题, 呵呵.
b) Qt的容器类能更好的跟其它Qt类来进行merge和互操作.
c) 这里还有一个function object, 或者说functor的东西, 其实这是STL里的概念了. 所谓的functor就是一个class重写了operator()操作符, 一边这个类可以当函数来使用(比如qSort()的第三个参数). 它的好处是, 既然是一个类, 便可以绑定额外的数据.
d) 顺便提一句, QString这个类是支持unicode的.

3. Thread 类:

    Qt的thread类的用法跟linux下pthread类用法很像. 我就不列举用法了. 只说一些需要注意的地方, 多线程总是很麻烦的皑皑.
a) 在同一线程中, singal/slot的调用是同步的, 但是在不同线程中就是异步的. 调用线程只是简单的发送一个event, 然后接受线程在自己的event loop中来调用slot.
b) thread-safe的概念(简称ts吧).. ts函数: 不同线程调用这个函数, 而这个函数会操作全局的共享变量, 如果不会出现同步问题, 那么这个函数就是ts的. ts类: 其中所有的函数都是ts的, 那么这个类也是ts的.
c) reentrant, 可重入的概念. 这个东西很高级, 应该说, 一个函数如果是reentrant的话, 那么它就是ts的; 如果它不是ts的话, 那么它就一定不是reentrant的. reentrant的概念跟多线程无关, 它要求即使在同一线程内, 调用同一函数能正常工作.
    举个例子, 比如strtok()函数一定用过, 每次迭代返回下一个token的时候, 参数都一样, 但是返回不一样. 于是strtok()函数肯定是在内部维护了一个static的buffer以便操作. 由此来说, 包含static或者global变量操作的函数一定不是reentrant的, 我们要避免这种情况.
    还有一个问题, 什么情况下ts的函数不是reentrant的呢? 再举例子, 有两个线程分别用t1和t2表示. 它们同时调用一个ts的函数, 这个函数用mutex来互斥共享数据. 假设t1先进入那个mutex, 于是t2进不去了. 这个时候把t1喀嚓掉(不管你用pthread_cancel()还是别的什么). 然后, 问题就来了..t2这个线程永远卡死了=v=…
d) QObject类是reentrant的. 但是有一些注意事项:
i) 如果要创建一个新的QObject, 那么创建必须在它的parent object(指的是包含关系, 而不是继承)的创建线程中进行这个操作.
ii) 在某个线程中创建QObject必须在那个线程中释放. 如果要释放其它线程中的QObject, 那么可以进行deffered delete: 调用QObject::deleteLater()函数.
iii) Timer类, Network类的使用必须在单个线程中, 跨线程调用是不行的.
iv) 由于底层类库的限制, Qt的widget类都不是reentrant的, 而结果就是a线程不能直接调用b线程中某个widget的成员函数. 有2个妥协的方法: 1) 用singal/slot. 2) 用QMetaObject::invokeMethod() (其实就是singal/slot机制的底层实现函数).
    额额, 多线程就是麻烦呀.. 写了那么多…

4. Network 类:

    这个没什么好说的其实.
    我们知道网络一般都延时, Qt提供了2套函数: 同步和异步, 分别对应block和non-block的情况, 就这些.

5. XML 类:

    也没啥好多说的.
    Qt提供了3套XML的API(版本4.3为止; 4.4似乎又有新的API):
a) 是最容易理解的, 写什么就是什么, 读什么就是什么, 基于文本操作. 相关的类有QXmlStreamReader, QXmlStreamWriter.
b) 是SAX API, 基于event driven. 没啥好说的, 标准API啊…
c) 不用说也知道了, DOM API. 又是标准…..

6. 其它:

    以上介绍了一些Qt常用类. Qt实际上还包含database的支持(mysql, db2, oracle… 还整个包含sqlite), DnD (Drag& Drop), 2D graphics, I/O(这里有一个设计模式, Serializer Pattern, 呵呵) 等一些类, 由于比较简单, 就不说了. 最新的Qt 4.4还包括Webkit, Phonon等新的API. 4.5会加入ODF, XSLT等支持, 4.6则是USB, bluetooth等… 这个东西已经不是简单的GUI tookit了, 会越来越强大…..

Leave a Reply

Your email address will not be published.