`
qimo601
  • 浏览: 3446149 次
  • 性别: Icon_minigender_1
  • 来自: 苏州
社区版块
存档分类
最新评论

QT Embedded二三事之QObject的元对象

    博客分类:
  • Qt
阅读更多

 

一、元对象 

       元对象被称做是meta object.在运行时刻(runtime),能够提供对象的运行时信息。

       在C++语言发展的早期,C++语言本身没有定义对象的运行时信息,如输出类的名称,父类的名称,判断对象的继承关系等等。虽然新的C++语言增加了RTTI,但还是不够的。这些信息在面向对象编程和调试程序过程中是非常有用的。因此不同的类库采取了不同的方式来扩展语言。
         MFC的实现方式是宏定义:如
          DECLARE_DYNAMIC,DECLARE_DYNCREATE,DECLARE_SERIAL。这种实现方式没有扩展语言本身,因此可以不加处理的兼容所有的C++编译器。
       QT的实现方式是宏定义加moc编译,定义宏Q_OBJECT,并对语言本身做了部分扩展,因此需要用QT的moc编译器特殊处理一下,产生一个moc_XXX.cpp的文件,然后就可以使用通用C++编译器编译了。
 Q_OBJECT的定义如下:
#define Q_OBJECT \
public: \
  virtual QMetaObject *metaObject() const { \
  return staticMetaObject(); \
  } \
  virtual const char *className() const; \
  virtual void* qt_cast( const char* ); \
  virtual bool qt_invoke( int, QUObject* ); \
  virtual bool qt_emit( int, QUObject* ); \
  QT_PROP_FUNCTIONS \
  static QMetaObject* staticMetaObject(); \
  QObject* qObject() { return (QObject*)this; } \
  QT_TR_FUNCTIONS \
private: \
  static QMetaObject *metaObj;
 
 
       从定义上看,QT的元对象信息主是通过QMetaObject对象来管理的,每一个类都会增加一个static QMetaObject *metaobj。QMetaObject中包含三部分信息:
 (1)className,superclassname
        这是用来判断对象的继承关系的,是实现QObject::isA(const char          *classname)和QObject::inherits(const char *classname)的基础。
 (2)用来实现Q_PROPERTY的property信息。
 (3)用来实现signal/slot的信息

二、 signal/slot 

       signal/slot机制是QT最具特色的特性。signal/slot巧妙的简单的实现了面向对象编程中经常使用的观察者模式(observer,或称为消息预定模式)。同时也封装了callback机制,一定程度上保证了callback函数的类型安全。

      从实现上来看,signal/slot需要QMetaObject和moc编译器的支持。signal和slot实际上是两种类函数,分别需要在类函数声明前面加signals和slots两个宏。
 以QButton的一个signals和slots为例说明实现过程:
class Q_EXPORT QButton : public QWidget
{
  Q_OBJECT
 ......... 
signals:
  void pressed();
 ................
public slots:
  void animateClick();
};
 

 (1)signal/slot机制需要增加Q_OBJECT声明,signal/slot是元对象信息,需要QMetaObject的支持。

 (2)signals、slots、emit都是宏,从定义上看,都是空值。对C++编译器编译没有任何影响。实际上,这三个宏都是给QT的moc编译器使用的。moc编译器会对标记为signals和slots的函数编号,方便的实现(编号,函数名字,函数地址)三者之间的转换。同时实现了标记为signals的函数。因此pressed()函数的是由moc编译器实现的,不需要用户实现,具体内容在moc_qbutton.cpp中。

 (3)connect/disconnect
 这两个函数的定义如下:
        QObject::connect( const QObject *sender, const char *signal, const QObject *receiver, const char *member )
        QObject::disconnect( const QObject *sender, const char *signal, const QObject *receiver, const char *member )

        QMetaObject中会为每一个signal建立一个QConnectList列表,记录着连接到这个signal的所有receiver和其slot函数。 connect就是在QConnectList中增加一项,让sender的QMetaObject记住。disconnect的作用相反。

 (4)emit pressed()

        emit是一个宏,但是一个空值,因此emit pressed()==pressed()。pressed()是由moc编译器实现的。 QButton::pressed()就是把通过QConnectList把所连接的receiver和其member函数找出来,然后按先后顺序调用receiver->member(),由于是一个list,所以是先连接的slot先执行。这与Observer模式中的实现是一致的。

         从上面的分析来看,signal/slot是同步直接调用。实现有些复杂,效率上比callback函数也慢一些,但是对于面向对象编程来说,非常符合对象之间低耦合的思想,减少了对象之间的依赖。使用起来也灵活。函数接口也可以任意组合。的确是QT的特色。

         使用过程中,也需要注意如下几点:
        (1)QT的signal和unix操作系统的signal是不同的,unix的signal是进程之间的一种通讯方式,QT的signal是对象之间的一种消息传递方式,不是一个层面的概念。
        (2)slot函数的返回值是没有意义的,signal函数是由moc编译器实现的,从具体实现来看,不关心slot函数的返回值。
        (3)根据目前的实现,slot函数的被执行是有顺序的,但是这要依赖于目前的实现,以后或许会变
        (4)signal/slot和(public,protected,private)没有冲突,private的函数也可以是signal/slot
        (4)slot函数可以是虚函数吗?
        (5)signal函数可以是虚函数吗?

三、 event机制
        在面象对象编程中,对象是核心。对象之间的需要通讯,对象A需要给对象B发消息。A对象向怎么才能向B对象发消息message? 一般需要把message定义成B对象的一个函数,然后由A对象调用b->message.这种实现不灵活,每个message都要定义函数。
       在UI系统中,不同的widget会经常发各种消息,要用上面的方式肯定是不行的。通常UI系统都会把这些消息和消息参数规范化。QT把消息类型和消息参数都封装到QEvent及其子类中,同时定义了QObject::event(QEvent *e)函数来处理各种QEvent,并且在此基础上实现了消息钩子和消息拦截等功能。这就是QT的event机制,下面详细介绍一下具体实现。

  1.QEvent
         QEvent封装了消息类型和消息参数,还有一些属性。下面是其实现代码:

class Q_EXPORT QEvent: public Qt // event base class
{
public:
  enum Type {

 None = 0, // invalid event
 Timer = 1, // timer event
 MouseButtonPress = 2, // mouse button pressed
 MouseButtonRelease = 3, // mouse button released
 MouseButtonDblClick = 4, // mouse button double click
 MouseMove = 5, // mouse move
 KeyPress = 6, // key pressed
 KeyRelease = 7, // key released
 FocusIn = 8, // keyboard focus received
 FocusOut = 9, // keyboard focus lost
 Enter = 10, // mouse enters widget
 Leave = 11, // mouse leaves widget
 ..................
 WindowStateChange = 96, // window state has changed
 IconDrag = 97, // proxy icon dragged
 User = 1000, // first user event id
 MaxUser = 65535 // last user event id
  };
  QEvent( Type type ) : t(type), posted(FALSE), spont(FALSE) {}
  virtual ~QEvent();
  Type type() const { return t; }
  bool spontaneous() const { return spont; }
protected:
  Type t;
private:
  uint posted : 1;
  uint spont : 1;
  friend class QApplication;
  friend class QAccelManager;
  friend class QBaseApplication;
  friend class QETWidget;
};
 
 QEvent首先将各种消息编号,如    
       KeyPress,KeyRelease,FocusIn,FocusOut,Show,并用变量t标识类型,同时还有两个属性:posted用来标识QEvent是由sendEvent发出还是由postEvent发出的;spont的含义不明,从代码上看,只有通过sendSpontaneousEvent发出的QEvent的spont才是true;
 
 2.QObject::event(QEvent *e)
        QObject::event是QT的消息处理函数。下面分析其实现:
bool QObject::event( QEvent *e )
{
#if defined(QT_CHECK_NULL)
  if ( e == 0 )
 qWarning( "QObject::event: Null events are not permitted" );
#endif
 //这里的消息钩子和消息拦截
  if ( eventFilters ) { // try filters
 if ( activate_filters(e) ) // stopped by a filter
  return TRUE;
  }

 //下面就是分发消息了
  switch ( e->type() ) {
  case QEvent::Timer:
 timerEvent( (QTimerEvent*)e );
 return TRUE;

  case QEvent::ChildInserted:
  case QEvent::ChildRemoved:
 childEvent( (QChildEvent*)e );
 return TRUE;

  case QEvent::DeferredDelete:
 delete this;
 return TRUE;

  default:
 if ( e->type() >= QEvent::User ) {
  customEvent( (QCustomEvent*) e );
  return TRUE;
 }
 break;
  }
  return FALSE;
}
 
       从实现上看,QObject::event函数相当于所有消息的一个分发函数,根据e->type的类型再分别调用,其相应的函数,如timerEvent,keyPressEvent等。

  3.消息钩子和消息拦截
        UI系统中,经常需要知道是否某一个widget收到某一类型的消息,例如没有focus的widget想接收当前keypress或keyrelease事件,这就需要在当前有foucs的widget上面安装一个钩子hook,并且在适当的时候截断这个消息,使得即使有focus的widget也不能收到事件。这就是消息钩子和消息拦截功能。在QT中,这是通过eventFilter来实现的。
 首先,需要调用QObject::installEventFilter安装eventFilter,即focuswidget->installEventFilter(nonfocuswidget)。
       下面,我们看看当有QEvent到来时发生了什么。根据QObject::event的实现,QObject::event会首先检查是否有eventFilter,如果有就会执行activate_filters,下面是activate_filters的实现。

bool QObject::activate_filters( QEvent *e )
{
  if ( !eventFilters ) // no event filter
 return FALSE;
  QObjectListIt it( *eventFilters );
  register QObject *obj = it.current();
  while ( obj ) { // send to all filters
 ++it; // until one returns TRUE
 if ( obj->eventFilter(this,e) ) {
  return TRUE;
 }
 obj = it.current();
  }
  return FALSE; // don't do anything with it
}
   
 
        activate_filters会依次执行每个安装的QObject::eventFilter,这是一个虚函数。这样用户可以改eventFilter,加上自已的逻辑实现,这样nonfocuswidget就知道目前发生了一个keypress事件。
       从代码中,还可以看出,如果有一个eventFilter函数返回值是TRUE,将会停止执行eventFilter函数,并返加TRUE到QObject::event,然后QObject::event也不会继续执行,这样就使实现了消息拦截功能。

  4.QEvent与Win32的消息对比
       Win32中,也对所有的消息进行了封装,也有消息处理函数,不过Win32的实现与QT的实现差别还是很大的:
       (1)Win32提供的是C接口,所以没有把消息封装成类,这个即使是MFC也没有。Win32中,消息类型是一个UINT,消息参数固定成两个参数(WPARAM wParam, LPARAM lParam)。
      (2)Win32的消息处理函数WindowProc也是一个C接口的函数,它的原型如下:
       LRESULT CALLBACK WindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);
       其中uMsg是消息类型,wParam和lParam是两个消息参数。从这点来看,Win32的实现简洁一些。
       (3)Win32中的消息是围绕window定义,window是处理消息的主体。在QT中,消息处理定义到了QObject这一层,而不是在QWidget上,因此所有的       QObject都可以处理消息。QT中的消息也不仅局限于窗口消息,如ChildInserted和ChildRemoved就是处理两个QObject之间父子关系的。
      (4)Win32中的消息钩子功能要比eventFilter强大,不公能拦截本进程内窗口的消息,还能拦截整个系统内所有窗口的消息。


 5.event和signal/slot的对比
        event和signal/slot都是QT提供的两种对象间通讯的方式,下面是一些不同点:
       (1)signal/slot的需要元对象模型的支持,需要声明Q_OBJECT。event与元对象模型无关。
       (2)emit signal是一种同步直接调用,对于event来说,sendEvent是同步直接调用,postEvent是异步调用。
       (3)用户可以自定义signal/slot的类型,event的类型都是提前定义好的,从这一点上看,signal/slot更为灵活。
       (4)signal/slot缺少消息钩子和消息拦截功能

 

       QApplication代表着QT Embedded应用程序的运行时对象,类似于MFC的CWinApp。应用程序可以通过一个全局指针变量qApp表访问当前应用程序的QApplication。 对于QT应用来说,第一步就是要创建一个QApplication对象。         在此过程中,做了如下事情:
 (1)初始化qApp
 (2)对于QWS Server,创建QWSServer全局指针qwsServer
 (3)创建QDisplay全局指针qt_fbdpy
 (4)创建QScreen全局指针qt_screen

1.初始化qApp

2.创建QWSServer全局指针qwsServer

3.创建QDisplay全局指针qt_fbdpy

4.创建QScreen全局指针qt_screen

分享到:
评论

相关推荐

    qt embedded 软键盘输入的实现

    Qt Embedded是一个轻量级、可定制的图形用户界面框架,特别适合于嵌入式设备。本文将详细介绍如何在Qt Embedded环境中实现软键盘输入功能,以供字母、数字及各种符号的输入。 首先,我们要了解Qt中的Input Method ...

    Qt Embedded中如何实现自己的keyboard.doc

    ### Qt Embedded中自定义键盘驱动实现详解 #### 一、背景与目的 在Qt Embedded开发过程中,对于嵌入式设备而言,实现一个自定义的键盘驱动是非常常见的需求。特别是当设备需要特定的输入方式或者标准的键盘解决方案...

    QT理论试题及答案.pdf

    8. QT 的元对象系统可以对信号和槽使用模板,可以在多个平台的应用程序中完全应用模板。 知识点:QT 的元对象系统、模板使用、跨平台应用 9. moc 是 QT 的元对象编译器,可以产生能被任何标准 C++ 编译器访问的...

    qt4中文手册,Qt入门开发资料

    对象模型提供了诸如属性系统、事件处理、动态属性和元对象系统等功能,使得Qt在编程时具有高度的灵活性和可扩展性。 1. **信号与槽机制**:这是Qt中最独特的设计,允许对象之间进行通信而无需相互了解对方。当一个...

    Qt笔试面试资料

    3. moc(Meta Object Compiler):用于处理Qt的元对象系统,支持信号与槽机制,这是Qt事件驱动编程的关键。 二、Qt对象模型 1. 信号与槽:Qt中对象间通信的方式,信号表示一个状态或事件的发生,槽是响应这些事件的...

    Qt中文帮助文档

    9. **嵌入式开发**:针对Qt在嵌入式设备上的应用,如Qt for Embedded Linux和Qt Quick for Devices。 10. **Qt Quick**:讲解基于QML的快速UI开发,包括元素、动画和状态机的使用。 11. **Qt Widgets**:详细介绍...

    Qt4图形设计与嵌入式开发(全套书籍)

    8. **Qtopia与QtEmbedded**:这部分内容详细阐述Qtopia和QtEmbedded,这两个是专为嵌入式系统设计的Qt版本,它们的特性、配置和应用实例。 9. **设备驱动与硬件交互**:在嵌入式开发中,直接与硬件交互是常见的任务...

    windows下进行嵌入式arm qt 编程

    诺基亚曾经是 Qt 的主要维护者之一,但现在已经转交给了 The Qt Company。可以访问 Qt 官方网站下载最新版的 Qt Creator 安装包。假设下载了 `qt-sdk-win-opensource-2010.02.1.exe` 文件,在 Windows 下完成安装...

    QT学习资料

    12. **QT的嵌入式开发**: QT Embedded(现在称为QT for Devices)允许在嵌入式设备上运行QT应用,如嵌入式Linux系统或物联网设备。 13. **QT Quick (QML)**: QML是一种声明式的UI设计语言,结合JavaScript,可以...

    qt 经典 学习书籍,速成

    - **QObjects和继承**:Qt的基础类是QObject,它提供了信号和槽、属性系统以及事件处理等功能,大部分Qt类都继承自QObject。 2. **Qt开发环境** - **Qt Creator**:官方提供的集成开发环境(IDE),集成了编辑器...

    qt Qt编程指南

    此外,还有QObject类,它是所有Qt对象的基类,提供了属性系统和事件处理等基本功能。 Qt的图形用户界面(GUI)组件是另一个重要的部分,包括各种窗口小部件(Widgets),如按钮、文本框、菜单等。这些小部件通过...

    Qt.zip_QT

    本文将围绕"Qt.zip_QT"的主题,深入探讨Qt框架中的关键概念、核心功能和实际应用,特别关注“qtopia精讲”部分,这通常指的是早期的Qt for Embedded Linux,现在已更名为Qt for Devices。 **一、消息与槽机制** 在...

    最新版的qt开发书籍

    1. **Qt基础知识**:Qt 的核心概念,如信号与槽机制,这是Qt事件处理的核心,使得对象间通信变得简单高效。还有QObjects、QObject派生类以及如何创建自定义的控件。 2. **Qt界面设计**:Qt Designer 工具的使用,...

    QT理论试题及答案 (2).pdf

    QT还拥有元对象系统,允许在运行时进行类型查询和动态属性操作,这极大地增强了代码的灵活性和可扩展性。元对象系统是基于QObject类构建的,它使得在多个平台的应用程序中可以完全应用模板。 QT的元对象编译器(moc...

    Qt简介Qt简介Qt简介Qt简介

    Qt的核心特性之一是它的对象模型,这是通过QObject类实现的。QObject类提供了事件处理、信号与槽机制、属性系统以及类型系统,这些特性使得Qt成为一种面向对象的编程框架。信号与槽机制是Qt的独有特性,允许对象之间...

    QT理论试题及答案 (2).docx

    QT的元对象系统(Meta-Object System)是其独特之处,它扩展了C++,增加了运行时类型信息、动态属性以及信号和槽的动态连接能力。元对象编译器(moc,Meta-Object Compiler)是一个工具,它处理包含Q_OBJECT宏的类,...

    Qt及Linux操作系统窗口设计-目录.pdf

    本章介绍了Qt的对象模型,包括信号与槽机制,元对象系统以及元对象编译器(moc)的工作原理。QObject类是所有Qt对象的基类,涵盖对象树、事件处理、定时器、连接函数connect等。Qt的国际化支持使得软件能适应多种...

    Qt入门教程实例代码

    1. **QObject和继承体系**:Qt库基于QObject类,所有图形和非图形组件都继承自它,提供了信号和槽、属性系统等特性。 2. **信号与槽**:信号表示状态变化,槽是响应这些变化的函数,两者通过连接实现异步通信。 3. *...

    Qt4学习笔记-培训班

    - **QObject** 是所有 **Qt** 对象的基类。 - **QWidget** 类继承自 **QObject**,类似于 **MFC** 中的 **CWnd** 类,用于创建和管理窗口组件。 - 任何继承自 **QWidget** 的类都可以被用来创建可视化的窗口或...

Global site tag (gtag.js) - Google Analytics