博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

Qt4开发总结

Posted on 2010-07-14 09:45  nkzc  阅读(1037)  评论(0)    收藏  举报

对Qt的认识和开发还只是刚刚起步,但作一个阶段学习的总结还是很必要的。

这阶段主要参考的资料有:

C++ GUI Programming with Qt 4, Second Edition,这里讲了Qt程序的写法,以及各个类的应用。

Inside Qt Series:深入介绍了Qt的实现机制

http://blog.csdn.net/MicroSky2813/archive/2010/02/03/5283563.aspx

http://www.qtcn.org/bbs/read.php?tid=16378&fpage=5

 

1.Qt的事件处理机制

事件起源:

基于事件如何被产生与分发,可以把事件分为三类:
* Spontaneous 事件,由窗口系统产生,它们被放到系统队列中,通过事件循环逐个处理。
* Posted 事件,由Qt或是应用程序产生,它们被Qt组成队列,再通过事件循环处理。
* Sent  事件,由Qt或是应用程序产生,但它们被直接发送到目标对象。
当我们在main()函数的末尾调用QApplication::exec()时,程序进入了Qt的事件循环,大概来讲,事件循环如下面所示:
while (!exit_was_called)
{
  while(!posted_event_queue_is_empty)
       {
         process_next_posted_event();
       }
  while(!spontaneous_event_queue_is_empty)
      {
         process_next_spontaneous_event();
      }
  while(!posted_event_queue_is_empty)
      {
        process_next_posted_event();
      }
}
首先,事件循环处理所有的posted事件,直到队列空。然后再处理所有的spontaneous事件,最后它处理所有的因为处理spontaneous事件而产生的posted事件。send 事件并不在事件循环内处理,它们都直接被发送到了目标对象。现在看一下实践中的paint 事件是如何工作的。当一个widget第一次可见,或是被遮挡后再次变为可见,窗口系统产生一个(spontaneous) paint事件,要求程序重画widget,事件循环最终从事件队列中捡选这个事件并把它分发到那个需要重画的widget。

并不是所有的paint事件都是由窗口系统产生的。当你调用QWidget::update()去强行重画widget,这个widget会post 一个paint 事件给自己。这个paint事件被放入队列,最终被事件循环分发之。
假如你很不耐烦,等不及事件循环去重画一个widget, 理论上,你应该直接调用paintEvent()强制进行立即的重画。但实际上这不总是可行的,因为paintEvent()函数是protected的(很可能访问不了)。它也绕开了任何存在的事件过滤器。因为这些原因,Qt提供了一个机制,直接sending事件而不是posting。 QWidget::repaint()就使用了这个机制来强制进行立即重画。
posting 相对于sending的一个优势是,它给了Qt一个压缩(compress)事件的机会。假如你在一个widget上连续地调用update() 十次,因update()而产生的这十个事件,将会自动地被合并为一个单独的事件,但是QPaintEvents事件附带的区域信息也合并了。可压缩的事件类型包括:paint,move,resize,layout hint,language change。
最后要注意,你可以在任何时候调用QApplication::sendPostedEvent(),强制Qt产生一个对象的posted事件。

Qt的signal slot 机制与事件驱动机制的区别在于:

1)signal slot是同步的,而事件可以同步(sendign)也可以异步(posting);

2)当一个signal被emit出来的时候,链接到这个signal的slot会立刻被调用,就好像是一个函数调用一样。当有多个slot链接到一个signal的时候,这些slot会一个接着一个的、以随机的顺序被执行。

3)As a rule, signals are useful when using a widget, whereas events are useful when implementing a widget.即信号是一种较高级的机制,事件是一种较低级的机制。

4)都可用于跨线程环境。

Qt的Event常见的使用情况有情况:

1)继承类,重写类的特定Event处理函数:如重写QTextEdit的focusInEvent和focusOutEvent

2)重写QObject::event(),能在事件到达类的特定Event处理函数前处理它。在需要改变Tab键的惯用法时这样做。也可以处理那些没有特定事件处理函数的比较少见的事件类型(例如,QEvent::HoverEnter)。我们重写event()时,必须要调用基类的event(),由基类处理我们不需要处理的那些情况。

3)给单独的QObject对象安装事件过滤器:对象用installEventFilter()注册后,所有目标对象的事件都首先到达监视对象的eventFilter()函数。如果一个对象有多个事件过滤器,过滤器按顺序激活,先到达最近安装的监视对象,最后到达最先安装的监视对象。

4)给QApplication对象安装事件过滤器,如果qApp(唯一的QApplication对象)安装了事件过滤器,程序中所有对象的每个事件在被送到任何其它事件过滤器之前都要送到eventFilter()函数中。这个方法在调试的时候非常有用,在处理禁止使能状态的控件的鼠标事件时这个方法也很常用。

5)继承QApplication,重写notify()。Qt调用QApplication::nofity()来发送事件。重写这个函数是在其他事件过滤器接收事件前得到所有事件的唯一方法。通常事件过滤器是最有用的,因为在同一时间,可以有任意数量的事件过滤器,但是notify()函数只有一个。

 

2.Qt的元对象系统

Meta Object System 的设计基于以下几个基础设施:

    * QObject 类
      作为每一个需要利用元对象系统的类的基类
    * Q_OBJECT 宏,
      定义在每一个类的私有数据段,用来启用元对象功能,比如,动态属性,信号和槽
    * 元对象编译器moc (the Meta Object Complier),
      moc 分析C++源文件,如果它发现在一个头文件(header file)中包含Q_OBJECT 宏定义,然后动态的生成另外一个C++源文件,这个新的源文件包含 Q_OBJECT 的实现代码,这个新的 C++ 源文件也会被编译、链接到这个类的二进制代码中去,因为它也是这个类的完整的一部分。通常,这个新的C++ 源文件会在以前的C++ 源文件名前面加上 moc_ 作为新文件的文件名。

Q_OBJECT宏

Meta Object 的功能实现,这个宏立下了汗马功劳。首先,让我们来看看这个宏是如何定义的:

 

#define Q_OBJECT \
public: \
     Q_OBJECT_CHECK \
     static const QMetaObject staticMetaObject; \
     virtual const QMetaObject *metaObject() const; \
     virtual void *qt_metacast(const char *); \
     QT_TR_FUNCTIONS \
     virtual int qt_metacall(QMetaObject::Call, int, void **); \
private:

 

 

这里,我们先忽略Q_OBJECT_CHECK 和QT_TR_FUNCTIONS 这两个宏。

我们看到,首先定义了一个静态类型的类变量staticMetaObject,然后有一个获取这个对象指针的方法metaObject()。这里最重要的就是类变量staticMetaObject 的定义。这说明所有的 QObject 的对象都会共享这一个staticMetaObject 类变量,靠它来完成所有信号和槽的功能,所以我们就有必要来仔细的看看它是怎么回事了。

 

struct Q_CORE_EXPORT QMetaObject
{
    // ……
    struct { // private data
        const QMetaObject *superdata;//指向与之对应的QObject类的父类,或者是祖先类的QMetaObject对象。
        const char *stringdata;//包含了Class Name, Signal Name, Slot Name, Property Name的字符串
        const uint *data;//有一部分指出了stringdata中不同字符串的索引值
        const void *extradata;
    } d;
};