QObject类详述

QObject详述

QObject类从Qt类继承,是所有Qt对象的基类,Qt类含有全局需要的各种枚举类型、类型的定义。通常情况下,用户不需要关心这个类,因为它是QObject及很少几个类的基类。例如:它定义了枚举类型ButtonState说明了鼠标按钮的状态,它定义了枚举类型WidgetState说明了窗口部件状态等。

一、对象树

QObject类是所有Qt对象的基类,是Qt对象模型的核心。 通过QObject对象可组织成对象树,QObject类提供了对对象树进行访问需要的各种成员函数。当你创建一个对象时,这个对象的父对象自动调用函数insertChild()将这个对象插入到父对象的孩子对象链表中,并调用函数children()可得到孩子对象链表。调用函数objectTrees()可得到对象树根的所有对象的链表。调用函数queryList可查询得到符合查询条件的对象的链表。

对象链表是通过QObjectList 类实现的,QObject对象树是一个静态的QObjectList类对象object_treesobject_trees链表中存有所有的对象指针,通过object_trees链表可查询到所有的对象。QObject对象树的层次图如图11。从图中可见,QObject对象是分层管理的,顶层链表链接的是无父对象的对象,一般是顶层窗口,第二层链表是无父对象的对象的孩子链表,依此类推。object_trees对象树与文件系统的文件组织方式类似。这种树型分层结构加快了对象的查找速度。

二、事件处理过程

Qt里,一个事件是继承自QEvent的对象。事件通过调用QObject::event()被发送到继承自 QObject 的对象。事件发送表明一个事件已经产生,用 QEvent表达这个事件,且QObject 需要做出回应。多数事件针对 QWidget和他的子类的,此外还有些不和图形相关的重要事件,比如,套接字激活,它是QSocketNotifier使用的事件。

一些事件来自窗口系统,如QMouseEvent,一些来自其他地方,如QTimerEvent,还有一些来自应用程序。Qt一视同仁,你可以象Qt自己的事件循环所作的方式一样地发送事件。

大多数事件类型有特定的类,常用的有QResizeEventQPaintEventQMouseEventQKeyEventQCloseEvent。有很多别的事件类。每个类派生自QEvent且添加事件特定的函数;例如,QResizeEvent。在QResizeEvent中,就被加入了QResizeEvent::size()QResizeEvent::oldSize()

有些类支持多种事件类型。QMouseEvent支持鼠标移动、按压、粘滞按压、拖拽、点击、右按压等。

因为程序需要以多变和复杂的方式作出返回,因此,Qt的事件分发机制就是灵活的。一个事件被发送的通常方法是通过调用一个虚拟函数。例如:QPaintEvent通过调用QWidget::paintEvent()被分发。

1) 创建用户事件

创建一个自定义类型的事件,你需要定义一个事件号,其值必须大于QEvent::User。为了传递有关你的自定义事件的特性。可能自定义的事件需要从QCustomEvent类继承。

示例1:用户事件类的编写

下面列出了一个用户事件类TEST_Event的示例,编写用户事件类的方法是先定义一个事件号,再实现用户事件类,应用程序将把用户事件类与Qt的事件类一样进行处理。

用户事件类TEST_Event的头文件test_event.h列出如下:

#include <qevent.h>

#include <qstring.h>

#define REFRESHUI_EVENT QEvent::User+2

class TEST_Event: public QCustomEvent

{

public:

    TEST_Event();

};

用户事件类TEST_Event的实现文件test_event.cpp列出如下:

#include <test_event.h>

TEST_Event::TEST_Event():QCustomEvent(REFRESHUI_EVENT )

{

}

2)事件发送

许多应用程序都要创建和发送他们自己的事件。这需要创建一个相应的事件类的对象,然后调用QApplication::sendEvent()或者QApplication::postEvent()发送事件。对用户事件来说,还需要用户有事件对应的操作函数。对于Qt事件来说,Qt中已实现了事件对应的操作函数。

示例2:使用用户事件类

test_engine.cpp文件中,函数refreshUI发送事件到主窗口,要求刷新主窗口界面。在main_window.cpp文件中,主窗口类MainWindow_UI重载了customEvent函数,实现了事件需求的操作:刷新主窗口界面。

test_engine.cpp文件中函数refreshUI实现代码列出如下:

void Engine_View::refreshUI()

{

// pmainWindowGui是应用程序主窗口类MainWindow_UI的实例

    qApp->postEvent( pmainWindowGui, new TEST_Event() );//发送事件

qApp->wakeUpGuiThread();//唤醒Gui线程

}

main_window.cpp文件中函数customEvent的实现代码列出如下:

void MainWindow_UI::customEvent( QCustomEvent * e)

{

    if ( e->type() == REFRESHUI_EVENT  )

    {

        进行主窗口刷新操作

    }

}

在应用程序的基类QApplication中有事件处理函数的实现,其中,事件的发送函数说明如下:

bool QApplication::sendEvent ( QObject * receiver, QEvent * event ) [static]

sendEvent() 立即发送事件给接收对象receiver,当sendEvent()返回时,(事件过滤器和)对象已经处理过事件了。对于很多事件类,可以通过调用isAccepted()函数知道该事件能否被处理者所接受或者拒绝。

void QApplication::postEvent ( QObject * receiver, QEvent * event ) [static]

postEvent()投寄事件到一个队列,以便能延迟分发。在下次Qt的主事件循环运行时,它分发全部事件,还可进行一些优化。例如,若有数个resize事件,它们就被压缩成一个。对于paint事件同样如此:QWidget::update()调用了   postEvent()postEvent()投寄事件可以避免屏幕因多次重画闪烁,同时还加快了运行速度。

postEvent()在对象初始化期间常常被使用,因为在对象完成初始化后,投送的消息会被很快派发。

bool QApplication::notify ( QObject * receiver, QEvent * e ) [virtual]

发送事件到接收对象者receiver,返回值是从receiver的事件处理函数中返回的值。对于某一类型的事件(如:鼠标和键事件)来说,如果接收者对事件不感兴趣(如:返回FALSE),事件将被传播到receiver的父类,如果父类不感兴趣,就一直向上级传递,直到顶层的object类。

3)事件的处理

5种不同的处理事件的方法,列出如下:

1)重载函数QApplication::notify(),这可提供有效的事件控制,但仅能在派生于QApplication的类中重实现这个函数。

2)在qApp(是QApplication的全局实例)中实现事件过滤,这样的一个事件过滤器能为所有的widget处理所有的事件,而且,可以有超过一个全局应用程序的事件过滤器。如:鼠标事件的全局事件过滤器设置了鼠标跟踪使能,则鼠标移动事件对于所有widget有效。

3)重载QObject::event()(在QWidget类中),在任何widget特定的事件过滤器之前,QObject::event()能看到所有事件。QObject::event()函数声明列出如下:

bool QObject::event ( QEvent * e ) [virtual]

这个虚函数接收给一个对象的事件,如果事件被识别并被处理,将返回TRUE。这个函数能被用来重实现一个对象的行为。

4)在对象上安装事件过滤器。

5)重载Qt基类事件处理函数

当用户发现Qt基类的事件处理函数不能满足用户的需要时,可以在用户类中重载这些函数,对于特定的Qt事件,可以重载特定的事件函数,如:重载paintEvent(), mousePressEvent()等等函数。如果想对多个Qt事件处理函数进行修改,可以重载QObject::event()来实现。

示例1:重载QObject::event()

下面是重载QObject::event()函数的例子,它进行特定的tab键处理,还处理用户事件。

bool MyClass:event( QEvent * e )

{

   if ( e->type() == QEvent::KeyPress )

   {

       QKeyEvent * ke = (QKeyEvent*) e;

       if ( ke->key() == Key_Tab )

       {

          // 这里是特定的tab处理

          k->accept();

          return TRUE;

       }

    }

    else if ( e->type() >= QEvent::User )

    {

          QCustomEvent * c = (QCustomEvent*) e;

          // 这里是自定义事件处理

          return TRUE;

      }

      QWidget::event( e );

}

三、事件运行机制

当应用程序的main函数中调用qApp->exec()时,应用程序进入Qt的主事件循环,Qt的主事件循环从事件队列中取出本窗口及系统事件,把它们翻译成QEvents,并使用函数QApplication::notify发送翻译的事件给相应的对象QObjects。同时,还处理控制台tty的信号、QWSserver服务器的事件,将来自socket的消息转化成事件进行分发。这些事件的处理工作在函数QEventLoop::processEvents(flags)中完成。

通常事件来自于窗口系统(用spontaneous()函数检查时返回TRUE),也可能来自使用Application::sendEvent()QApplication::postEvent()手动发送的事件(用spontaneous()函数检查时返回FALSE)。

QObjects通过调用它们的QObject::event()接收事件,这个函数也可被子类重载来处理事件,最典型的重载是QWidget::event()

四、事件过滤器

一个事件过滤器是一个能接收所有发送到这个对象上的事件的对象。这个过滤器能停止或转发到这个对象上的事件。事件过滤器通过eventFilter()函数来接收事件,如果这个事件应该被过滤(如:停止事件等),eventFilter()函数返回TRUE, 否则,返回FALSE。如果多个事件过滤器被安装在一个对象上,最后安装的过滤器将被激活。

QObject类还提供了事件过滤器的安装,QObject类与事件过滤相关的几个成员函数说明如下:

bool QObject::event ( QEvent * e ) [virtual] 接收到一个对象的事件,如果事件被识别并处理时,返回TRUE。这个函数能被重载来定制一个对象的行为。

bool QObject::eventFilter ( QObject * watched, QEvent * e ) [virtual] 如果一个对象上已安装了事件过滤器,eventFilter函数将被用来过滤事件。在这个函数的重载中,如果你想过滤事件e(如:让它停止不再被处理),就返回TRUE,否则返回FALSE

void QObject::installEventFilter ( const QObject * filterObj ) 在对象filterObj上安装事件过滤器。

下面是一个事件过滤器的使用样例,MyMainWindow类在本对象上安装了事件过滤器,事件过滤处理函数eventFilter被重载用来在textEdit对象上处理KeyPress事件。没处理的事件被传递到基类的eventFilter()函数中,因为基类也可能因为内部事件处理的原因已重载了eventFilter()函数。如果你在这个函数中删除了接收者对象,确信返回TRUE,否则,Qt将向前传递事件到删除的对象中,程序将崩溃。

class MyMainWindow : public QMainWindow

    {

    public:

        MyMainWindow( QWidget *parent = 0, const char *name = 0 );

    protected:

        bool eventFilter( QObject *obj, QEvent *ev );

    private:

        QTextEdit *textEdit;

    };

    MyMainWindow::MyMainWindow( QWidget *parent, const char *name )

        : QMainWindow( parent, name )

    {

        textEdit = new QTextEdit( this );

        setCentralWidget( textEdit );

        textEdit->installEventFilter( this );//在本对象上安装事件过滤器

    }

    bool MyMainWindow::eventFilter( QObject *obj, QEvent *ev )

    {

        if ( obj == textEdit ) {//过滤的对象

            if ( e->type() == QEvent::KeyPress ) {//过滤的事件

                qDebug( "Ate key press %d", k->key() );

                return TRUE;//已对事件处理,必须返回TRUE,这样,系统不会对这个事件做第二次处理了

            } else {

                return FALSE;

            }

        } else {

            // 传递事件到父类

            return QMainWindow::eventFilter( obj, ev );

        }

    }

五、定时器

使用定时器有2种方法,一种是使用QTimer类,另一种是使用QObject类的定时器。定时器的精确度依赖于操作系统和硬件,大多数平台支持20ms的精确度。

1QObject类的定时器

Qobject是所有Qt对象的基类,它提供了一个基本的定时器。通过QObject::startTimer(),你可以把一个以毫秒为单位的时间间隔作为参数来开始定时器。这个函数返回一个唯一的整数的定时器的标识符。这个定时器现在就会在每一个时间间隔"触发",直到你明确地使用这个定时器的标识符来调用QObject::killTimer()结束。

应用程序在main函数中通过调用QApplication::exec()来开始进行事件循环。当定时器触发时,应用程序会发送一个QTimerEvent,在事件循环中,处理器按照事件队列顺序来处理定时器事件,当处理器正忙于其它事件处理时,定时器就不能马上触发。

QObject类还提供定时器的功能,与定时器相关的成员函数有startTimer()timerEvent()killTimer()killTimers()QObject基类中的startTimertimerEvent

原型及说明如下:

int QObject::startTimer ( int interval )

开始一个定时器并返回定时器ID,如果不能开始一个定时器将返回0。定时器开始后,每隔 interval毫秒间隔将触发一次超时事件,直到killTimer()killTimers()被调用来删除定时器。如果interval0,那么定时器事件每次发生时没有窗口系统事件处理。

void QObject::timerEvent ( QTimerEvent * ) [virtual protected]

虚拟函数timerEvent被重载来实现用户的超时事件处理函数。如果有多个定时器在运行,QTimerEvent::timerId()被用来查找是哪个定时器被激活。

当定时器事件发生时,虚函数timerEvent()随着QTimerEvent事件参数类一起被调用。重载这个函数可以获得定时器事件。

定时器的用法样例如下:

class MyObject : public QObject

    {

        Q_OBJECT

    public:

        MyObject( QObject *parent = 0, const char *name = 0 );

    protected:

        void timerEvent( QTimerEvent * );

    };

    MyObject::MyObject( QObject *parent, const char *name )

        : QObject( parent, name )

    {

        startTimer( 50 );    // 50ms定时器

        startTimer( 1000 );  // 1s定时器

        startTimer( 60000 ); // 1分钟定时器

    }

void MyObject::timerEvent( QTimerEvent *e )//重载timerEvent函数

{

    qDebug( "timer event, id %d", e->timerId() );

}

void MyObject::stopTimer()

{

    killTimer( showDateTimer );

    showDateTimer = -1;

}

2)定时器类QTimer

定时器类QTimer提供当定时器触发的时候发射一个信号的定时器,QTimer提供只触发一次的超时事件。通常的使用方法如下:

QTimer * testtimer = new QTimer( this );//创建定时器

connect(testtimer, SIGNAL(timeout()),

this, SLOT(updateCaption()) );//将定时器超时信号与槽(功能函数)连接起来

testtimer ->start( 1000 );  //开始运行定时器,定时时间间隔为1000ms

testtimer定时器被作为这个窗口部件的子类,这样当这个窗口部件被删除时,定时器也会被删除。

QTimer还提供了一个简单的只有一次定时的函数singleShot。例如:一个定时器在100ms后触发处理函数animateTimeout,并且只触发一次。代码如下:

QTimer::singleShot( 100, this, SLOT(animateTimeout();

猜你喜欢

转载自blog.csdn.net/fascinateu/article/details/80787990