QT Embedded二三事之QObject的元对象
一、元对象
元对象被称做是meta object.在运行时刻(runtime),能够提供对象的运行时信息。
MFC的实现方式是宏定义:如
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二三事之QObject的元对象的更多相关文章
- Qt 在相同的线程中可以在信号中传递未注册的元对象,在非相同线程中则不能传递未测试的对象,为什么呢?
有兄台知道可以在留言告诉我,万分感谢!!! 需求:需要在多线程中传递未注册的非元对象数据,时间紧急,无法及时更改该传递的数据为元对象,非继承 QObject 这里采用指针方式传递,同时把传递的局部变量 ...
- Qt对象模型之二:对象树与元对象系统
一.对象树的概念 Qt中使用对象树(object tree)来组织和管理所有的QObject类及其子类的对象.当创建一个QObject时,如果使用了其他的对象作为其父对象(parent),那么这个 Q ...
- 深入了解Qt(二)之元对象系统(Meta-Object System)
深入了解Qt主要内容来源于Inside Qt系列,本文做了部分删改,以便于理解.在此向原作者表示感谢! 在Qt Meta Object System-元对象系统这篇文章中,从底层实现的源码剖析了元对象 ...
- Qt信号槽机制的实现(面试的感悟,猜测每一个类保存的一个信号和槽的二维表,实际使用函数指针 元对象 还有类型安全的检查设定等等)
因为面试时问了我这道题,导致我想去了解信号槽到底是如何实现的,于是贴着顺序看了下源码,大致了解了整个框架.网上关于信号槽的文章也很多,但是大部分都是将如何应用的,这里我就写一下我所理解的如何实现吧, ...
- Qt——元对象和属性机制
http://www.cnblogs.com/hellovenus/p/5582521.html 一.元对象 元对象(meta object)意思是描述另一个对象结构的对象,比如获得一个对象有多少成员 ...
- Qt根据类名创建对象(元对象反射)
在java语言中,可以使用getObject(String)函数,从类名直接构建新的对象. 而在C++中是没有这种机制的,Qt虽然提供了元对象机制,但只可以获取对象的类名,不能反向构建. 这个问题我在 ...
- Qt元对象系统简介
在Qt中提供了c++的扩展,提供了一种元对象系统的机制,(meta-object-system)的机制.其中包含了信号与槽的内部机制,能够访问到QObject子类的元对象信息的功能. Q_OBJECT ...
- 让QT/Embedded支持国际化
让QT/Embedded支持国际化 环境配置: Qt/Embedded ,在主机和目标板上存放路径都为:/root/qt-embedded-free- Qt/X11 3.3 (主要用到其中的lupda ...
- Qt 元对象系统(Meta-Object System)
(转自:http://blog.csdn.net/aladdina/article/details/5496891) Qt的元对象系统基于如下三件事情: 类:QObject,为所有需要利用原对象系统的 ...
随机推荐
- JavaScript Basics_Fundamentals Part 1_Variables
JavaScript Variables JavaScript 变量(Variables)是用于存储数据值的容器. 创建一个 JavaScript 变量,可以使用关键字 let. Example le ...
- ES6 map与filter
ES6 map与filter 1.map let arr1 = [1,2,3]; let arr2 = arr1.map((value,key,arr) => { console.log(val ...
- RobHess的SIFT代码解析之kd树
平台:win10 x64 +VS 2015专业版 +opencv-2.4.11 + gtk_-bundle_2.24.10_win32 主要参考:1.代码:RobHess的SIFT源码:SIFT+KD ...
- docker-compose 编排文件小疑点
在学习docker-compose的时候,查看了下st2中的docker-compose.yml文件,有个地方没搞明白 env_file 制定的文件路径,一开始以为是在对应的容器中的conf目录中,但 ...
- selenium检测webdriver封爬虫的解决方法
有不少朋友在开发爬虫的过程中喜欢使用Selenium + Chromedriver,以为这样就能做到不被网站的反爬虫机制发现. 先不说淘宝这种基于用户行为的反爬虫策略,仅仅是一个普通的小网站,使用一行 ...
- IDEA 运行报错:WARN: Establishing SSL connection
使用JDBC连接数据库时出现报错, 报错内容:Wed Sep 26 14:30:31 CST 2018 WARN: Establishing SSL connection without server ...
- 开启aix SFTP日志 是否和链接SFTP有关呢
1.修改SSH配置 vi /etc/ssh/sshd_config 在sftp配置处添加-l INFO -f AUTH Subsystem sftp /usr/lib64/ssh/sftp-serve ...
- Physical Education Lessons CodeForces - 915E (动态开点线段树)
Physical Education Lessons CodeForces - 915E This year Alex has finished school, and now he is a fir ...
- Java队列与栈转换中String.Valueof()使用
1. 由 基本数据型态转换成 String String 类别中已经提供了将基本数据型态转换成 String 的 static 方法 也就是 String.valueOf() 这个参数多载的方法 有下 ...
- 微信 使用wScratchPad 组件时 出错
说是由于android版本bug 可访问 https://github.com/websanova/wScratchPad/issues/15 解决方法: $("#wScratchPad c ...