最近在为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> const QEvent::Type CustomEvent_Login = (QEvent::Type)5001;//建议用5000以上唯一的标识
public: private: PostEvent::PostEvent(QWidget *parent) void PostEvent::customEvent(QEvent *e) 呵呵,就这样吧 本人一开始犯了一个很白痴的错误,即把customEvent函数当作用户可以自定义的函数,殊不知是父类中的虚函数,所以一直捕获不到消息。好了,以上只是一个简单的关于postEvent的一个应用,如果想了解更多的消息机制请阅读其他关于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() } } 先处理Qt事件队列中的事件, 直至为空. 再处理系统消息队列中的消息,直至为空, 在处理系统消息的时候会产生新的Qt事件, 需要对其再次进行处理. 调用QApplication::sendEvent的时候, 消息会立即被处理,是同步的. 实际上QApplication::sendEvent()是通过调用QApplication::notify(), 直接进入了事件的派发和处理环节。 根据对Qt事件机制的分析, 我们可以得到5种级别的事件过滤,处理办法. 以功能从弱到强, 排列如下: 1 重载特定事件处理函数.最常见的事件处理办法就是重载象mousePressEvent(), keyPressEvent(), paintEvent()这样的特定事件处理函数. 以按键事件为例, 一个典型的处理函数如下: void imageView::keyPressEvent(QKeyEvent* event)
2重载event()函数.
通过重载event()函数,我们可以在事件被特定的事件处理函数处理之前(象keyPressEvent())处理它. 比如, 当我们想改变tab键的默认动作时,一般要重载这个函数. 在处理一些不常见的事件(比如:LayoutDirectionChange)时,evnet()也很有用,因为这些函数没有相应的特定事件处理函数. 当我们重载event()函数时, 需要调用父类的event()函数来处理我们不需要处理或是不清楚如何处理的事件. 下面这个例子演示了如何重载event()函数, 改变Tab键的默认动作: (默认的是键盘焦点移动到下一个控件上. ) bool CodeEditor::event(QEvent * 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()函数只有一个. --------------------------------------------------------------------------------
|
|