分享

[Qt] postevent emit

 own360 2013-11-17
分类: QtWebKit 2011-08-27 00:02 2461人阅读 评论(4) 收藏 举报

目录(?)[+]

      最近在为bestv做一个播放的plugin,这个plugin主要是在Qt中完成play等功能并能在JavaScript里面进行调用,其中里面就遇到了信息、事件等问题。开始时,我选择通过connect关联singal和slots,再利用emit来触发信息,从而达到执行和回调信息给JavaScript的作用(之所以选择这种,其中一个主要的因素,就是设计plugin类的哥们定义了event处理方式,就是emit方式——从而,我也觉得此种方式可以完成Qt Object 和JSObject的回调等交互与信号传输)。不过,发现此类方式的singal触发时序很难控制并且bestv方在对singal接受部分,并未有完善的方案,最后,只好放弃emit、选择postevent。下面转载、整理、分享了一些来源于网络的Qt-event相关的文档。

------------------------------------------------------------------------------------

Qt中抛消息有:信号和槽、postEvent、sentEvent等机制,sentEvent只支持同步的。postEvent可以实现异步的,其机制是将消息发送到消息队列中,消息队列又会把这些消息都抛出(当然要实现该功能我们也可以用信号和槽机制,将connect函数的最后一个参数设置为Qt::QueuedConnection即可)。

言归正传,上段我们说到消息队列把消息抛出来,我们该如何去捕获该消息呢?我们只要实现父类中的event()或者customEvent()函数即可,在里面实现我们自己的处理,在此建议采用customEvent()。

以下是一个简单的实例:

#include <QWidget>
#include <QEvent>

const QEvent::Type CustomEvent_Login = (QEvent::Type)5001;//建议用5000以上唯一的标识


class PostEvent : public QWidget
{
Q_OBJECT

public:
PostEvent(QWidget *parent = 0);
~PostEvent();

private:
void customEvent(QEvent *e);   //该函数是父类QWidget的虚函数

};

PostEvent::PostEvent(QWidget *parent)
{
QApplication::postEvent(this, new QEvent(CustomEvent_Login));   //该函数实现将自定义的消息发送到队列,且new QEvent(CustomEvent_Login))只能动态分配,原因请看Qt的帮助文档中的postEvent函数说明。
}

void PostEvent::customEvent(QEvent *e)
{
if (e->type() == CustomEvent_Login)   //捕获消息
{
   QMessageBox msgBox;
   msgBox.setText("The document has been modified.");
   msgBox.exec();
}
}

呵呵,就这样吧 本人一开始犯了一个很白痴的错误,即把customEvent函数当作用户可以自定义的函数,殊不知是父类中的虚函数,所以一直捕获不到消息。好了,以上只是一个简单的关于postEvent的一个应用,如果想了解更多的消息机制请阅读其他关于event的文章。
/*
其实对于event的处事方式,最著名的就是两种,一是事件冒泡法(event bubbing),即:事件开始时,由event送至各事件接收方,IE浏览器的研发团队就是采用此方法;另一是事件捕获(event capturing),即事件接收方主动去捕获event。
*/

-------------------------------------------------------------------------------------

qt event 是Qt中最重要的概念之一,是因为Qt程序是典型的事件驱动型, 程序的每个动作都是由幕后某个事件所触发. Qt事件的类型很多, 常见的qt的事件如下:

§          键盘事件: 按键按下和松开.

§          鼠标事件: 鼠标移动,鼠标按键的按下和松开.

§          拖放事件: 用鼠标进行拖放.

§          滚轮事件: 鼠标滚轮滚动.

§          绘屏事件: 重绘屏幕的某些部分.

§          定时事件: 定时器到时.

§          焦点事件: 键盘焦点移动.

§          进入和离开事件: 鼠标移入widget之内,或是移出.

§          移动事件: widget的位置改变.

§          大小改变事件: widget的大小改变.

§          显示和隐藏事件: widget显示和隐藏.

§          窗口事件: 窗口是否为当前窗口.

      Qt的事件和Qt中的signal不一样, 后者通常用来"使用"widget, 而前者用来"实现" widget。比如一个按钮我们使用这个按钮的时候, 我们只关心他clicked()signal,至于这个按钮如何接收处理鼠标事件再发射这个信号我们是不用关心的但是如果我们要重载一个按钮的时候,我们就要面对event比如我们可以改变它的行为在鼠标按键按下的时候(mouse press event) 就触发clicked() signal而不是通常在释放的( mouse release event)时候。如下图给出event处理过程的示意图。

    由图可知:
  一、postevent是将event提交到event队列中,交给Qt去处理,至于如何处理,并不关心也不参与;
  二、event一般只有两个来源,1)是系统产生,通常是指window system把从系统得到的消息比如鼠标按键,键盘按键等放入系统的消息队列中,Qt事件循环的时候读取这些事件转化为QEvent,再依次处理。2)Qt应用程序程序自身产生的程序产生事件有两种方式一种是调用QApplication::postEvent(). 例如QWidget::update()函数,当需要重新绘制屏幕时,程序调用update()函数,new出来一个paintEvent,调用QApplication::postEvent(),将其放入Qt的消息队列中等待依次被处理另一种方式是调用sendEvent()函数这时候事件不会放入队列而是直接被派发和处理, QWidget::repaint()函数用的就是这种方式
  三、event处理的方式,即event的提交与处理的调度也有两种:一是同步的,一是异步。

Qt的事件循环是异步的,当调用QApplication::exec()时,就进入了事件循环. 该循环可以简化的描述为如下的代码:

while (!app_exit_loop ) {

       while( !postedEvents ) {             processPostedEvents()       }
       while( !qwsEvnts ){            qwsProcessEvents();   }
       while( !postedEvents ) {             processPostedEvents()       }

}

先处理Qt事件队列中的事件, 直至为空. 再处理系统消息队列中的消息,直至为空, 在处理系统消息的时候会产生新的Qt事件, 需要对其再次进行处理.

调用QApplication::sendEvent的时候, 消息会立即被处理,是同步的. 实际上QApplication::sendEvent()是通过调用QApplication::notify(), 直接进入了事件的派发和处理环节。
---------------------------------------------------------------------------------
事件过滤器:
    事件过滤器是Qt中一个独特的事件处理机制,功能强大而且使用起来灵活方便.通过它,可以让一个对象侦听拦截另外一个对象的事件.事件过滤器是这样实现的:在所有Qt对象的基类: QObject中有一个类型为QObjectList的成员变量,名字为eventFilters,当某个QObjec(qobjA)给另一个QObject (qobjB)安装了事件过滤器之后, qobjB会把qobjA的指针保存在eventFilters.qobjB处理事件之前,会先去检查eventFilters列表,如果非空,就先调用列表中对象的eventFilter()函数.一个对象可以给多个对象安装过滤器.同样,一个对象能同时被安装多个过滤器,在事件到达之后,这些过滤器以安装次序的反序被调用.事件过滤器函数( eventFilter() )返回值是bool,如果返回true,则表示该事件已经被处理完毕, Qt将直接返回,进行下一事件的处理;如果返回false,事件将接着被送往剩下的事件过滤器或是目标对象进行处理.
---------------------------------------------------------------------------------

根据对Qt事件机制的分析, 我们可以得到5种级别的事件过滤,处理办法. 以功能从弱到强, 排列如下:

1 重载特定事件处理函数.

最常见的事件处理办法就是重载象mousePressEvent(), keyPressEvent(), paintEvent()这样的特定事件处理函数. 以按键事件为例, 一个典型的处理函数如下:

void imageView::keyPressEvent(QKeyEvent* event)
{
switch (event->key()) {
case Key_Plus:
zoomIn();
break;
case Key_Minus:
zoomOut();
break;
case Key_Left:
// …
default:
QWidget::keyPressEvent(event);
}
}  

2重载event()函数.

通过重载event()函数,我们可以在事件被特定的事件处理函数处理之前(象keyPressEvent())处理它. 比如, 当我们想改变tab键的默认动作时,一般要重载这个函数. 在处理一些不常见的事件(比如:LayoutDirectionChange)时,evnet()也很有用,因为这些函数没有相应的特定事件处理函数. 当我们重载event()函数时, 需要调用父类的event()函数来处理我们不需要处理或是不清楚如何处理的事件.

下面这个例子演示了如何重载event()函数, 改变Tab键的默认动作: (默认的是键盘焦点移动到下一个控件上. )

bool CodeEditor::event(QEvent * event)
{
if (event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = (QKeyEvent *) event;
if (keyEvent->key() == Key_Tab) {
insertAtCurrentPosition('/t');
return true;
}
}
return QWidget::event(event);
}

3 在Qt对象上安装事件过滤器.

安装事件过滤器有两个步骤:(假设要用A来监视过滤B的事件)

首先调用B的installEventFilter( const QOject *obj ), 以A的指针作为参数. 这样所有发往B的事件都将先由A的eventFilter()处理.

 然后, A要重载QObject::eventFilter()函数, 在eventFilter() 中书写对事件进行处理的代码.

用这种方法改写上面的例子:(假设我们将CodeEditor 放在MainWidget中)

MainWidget::MainWidget()

{

       // …

CodeEditor * ce = new CodeEditor( this, “codeeditor”);

ce->installEventFilter( this );

// …

}

bool MainWidget::eventFilter( QOject * target , QEvent* event )

{

       if( target== ce ){

              if(event->type() == QEvent::KeyPress ) {

                     QKeyEvent*ke = (QKeyEvent *) event;

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

ce->insertAtCurrentPosition('/t');

return true;

                     }

              }

       }

       returnfalse;

}

4 给QAppliction对象安装事件过滤器.

一旦我们给qApp(每个程序中唯一的QApplication对象)装上过滤器,那么所有的事件在发往任何其他的过滤器时,都要先经过当前这个eventFilter().在debug的时候,这个办法就非常有用, 也常常被用来处理失效了的widget的鼠标事件,通常这些事件会被QApplication::notify()丢掉. ( 在QApplication::notify() 中, 是先调用qApp的过滤器, 再对事件进行分析, 以决定是否合并或丢弃)

5 继承QApplication类,并重载notify()函数.

Qt是用QApplication::notify()函数来分发事件的.想要在任何事件过滤器查看任何事件之前先得到这些事件,重载这个函数是唯一的办法.通常来说事件过滤器更好用一些, 因为不需要去继承QApplication类. 而且可以给QApplication对象安装任意个数的事件过滤器,相比之下, notify()函数只有一个.

--------------------------------------------------------------------------------

更多 0

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多