Qt将系统产生的消息转化为Qt事件,Qt事件被封装为对象,所有的Qt事件均继承抽象类QEvent
,用于描述程序内部或外部发生的动作,任意的QObject
对象都具备处理Qt事件的能力。
操作系统将获取的事件,如鼠标、键盘按键等事件,放入系统消息队列中,Qt事件循环的时候读取消息队列中的事件,转化为QEvent
,再依次处理
此种产生有两种方式
当Widget
需要重新绘制屏幕时,程序将调用update()
函数,new
出来一个paintEvent
,调用 QApplication::postEvent()
,将其放入Qt的消息队列中,等待依次被处理
事件不会放入队列,而是直接被派发和处理,QWidget::repaint()
函数用的就是这种方式
调度方式有两种,同步与异步
Qt的事件循环是异步的,当调用QApplication::exec()
时,就进入了事件循环,先处理Qt事件队列中的事件, 直至为空,再处理系统消息队列中的消息,直至为空,处理系统消息的时候会产生新的Qt事件,需要对其再次进行处理
下面来看看Qt事件循环
while (!app_exit_loop)
{
while (!postedEvents)
{
// 由Qt应用程序产生,将放入Qt事件队列中
processPostedEvents();
}
while (!qwsEvnts)
{
// 由窗口系统产生,将被放入系统队列中去
qwsProcessEvents();
}
while (!postedEvents)
{
// 处理系统消息产生的新的Qt事件
processPostedEvents();
}
}
调用QApplication::sendEvent()
的时候,消息会立即被处理,是同步的;实际上QApplication::sendEvent()
是通过调用QApplication::notify()
,直接进入了事件的派发和处理环节
事件的分发主要用event()
函数,在事件对象创建完毕后,Qt就将这个事件传递给QObject
的event()
函数,它并不直接处理事件,而是将事件按照不同类型分发给不同的事件处理器event handler
event()
函数接受一个QEvent
对象,这个对象是需要转发的对象,转发时需要调用type()
函数进行类型判断,函数的返回值是QEvent::Type
的枚举;处理完事件后,可以直接return true
,这时QApplication
的notify()
函数会认为这个事件已处理完毕,则会继续从处理事件队列中取出下一事件进行处理;若返回值为false
,QApplication
会尝试寻找这个事件的下一个处理函数
event()函数的定义
bool QWidget::event(QEvent *event)
{
switch (e->type() )
{
case QEvent::KeyPress:
keyPressEvent((QKeyEvent *)event);
if (!((QKeyEvent *)event)->isAccepted())
return false;
break;
case QEvent::KeyRelease:
keyReleaseEvent((QKeyEvent *)event);
if (!((QKeyEvent *)event)->isAccepted())
return false;
break;
// more...
}
return true;
}
当需要重写event()
函数时,只写关于我们需要处理事件即可,其他事件调用父类的event()
函数继续转发通过return parent::event(event)实现
,否则这个组件就只能处理我们定义的事件了
如下的重写event()
函数
bool MyWidget::event(QEvent* event)
{
if (event->type() == QEvent::KeyPress)
{
QKeyEvent *keyEvent = static_cast(event);
if (keyEvent->key() == Qt::Key_Tab)
{
// 处理 Tab 鍵
return true;
}
}
return QWidget::event(event);//其他事件转发给父类处理
}
事件过滤器是Qt中一个独特的事件处理机制,它让一个对象监听拦截另外一个对象的事件,可在一个对象处理事件前调用,可判断是否调用event()
函数,主要用于过滤事件,例如停止对某事件的响应
一个对象可以给多个对象安装过滤器,一个对象能同时被安装多个过滤器,在事件到达之后,事件过滤器以安装次序的反序被调用,类似于栈操作
事件过滤器函数eventFilter()
返回值是bool
型, 如果返回true
,则表示事件已经被处理完毕,Qt将直接返回,进行下一事件的处理;如果返回false
,事件将接着被送往剩下的事件过滤器或是目标对象进行处理
事件过滤器可以对其他组件接收到的事件进行监控,任意的QObject
对象都可以作为事件过滤器使用,事件过滤器对象需要重写eventFilter
函数
组件通过installEventFilter
函数安装事件过滤器,事件过滤器在组件之前接收到事件,能够决定是否将事件转发到组件对象
QObject
中有一个类型为QObjectList
的成员变量,名字为eventFilters
;eventFilters
中eventFilters
列表eventFilter()
函数true
则不会再次执行此事件,否则继续执行此事件函数声明
virtual bool QObject::eventFilter(QObject * watched, QEvent * event)
watched对象安装了事件过滤器,这个函数会调用并进行事件过滤,然后才进行组件事件处理,重写这个函数时,若想过滤掉某事件,例停止某事件的响应,需要返回true
,对于该组件其他需要处理的事件则返回false
,而对于其他组件的事件,并不能保证它有没有安装过滤器,则需要转发给父类
例子如下
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
if (obj == textEdit)
{
if (event->type() == QEvent::KeyPress)
{
return true;
}
else
{
return false;
}
}
else
{
// pass the event on to the parent class
return QMainWindow::eventFilter(obj, event);
}
}
上面的例子中为MainWindow
建立了一个事件过滤器,为了过滤某个组件上的事件,首先需要判断这个对象是哪个组件,然后判断这个事件的类型
函数声明如下:
void QObject::installEventFilter(QObject * filterObj)
若有textField.installEventFilter(obj)
,则如果有事件发送到textField
组件是,会先调用obj->eventFilter()
函数,然后才会调用textField.event()
事件过滤器和被安装的组件必须在同一线程
对于某些类别的事件,如果在整个事件的派发过程结束后还没有被处理,那么这个事件将会向上转发给它的父widget,直到最顶层窗口
QApplication::notify()
,QObject::eventFilter()
,QObject::event()
通过返回bool值来表示是否已处理QEvent::ignore()
或QEvent::accept()
对事件进行标识,只用于event()
函数和特定事件处理函数之间的沟通,而且只有用在某些类别事件上是有意义的看,这些事件就是上面提到的那些会被转发的事件,包括鼠标,滚轮,按键等事件QT提供了五种不同级别的事件处理和过滤
最常见的事件处理办法就是重写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);
}
}
event()
函数重写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);
}
Qt
对象安装事件过滤器安装事件过滤器有两个步骤(假设要用A来监视过滤B的事件)
eventFilter()
处理QObject::eventFilter()
函数, 在eventFilter()
中写对事件进行处理的代码用这种方法改写上面的例子(假设我们将CodeEditor
放在MainWidget
中)
MainWidget::MainWidget()
{
CodeEditor* ce = new CodeEditor(this, “code editor”);
ce->installEventFilter(this);
}
bool MainWidget::eventFilter(QOject* target, QEvent* event)
{
if(target == ce)
{
if(event->type() == QEvent::KeyPress)
{
QKeyEvent* key = (QKeyEvent*)event;
if(key->key() == Key_Tab)
{
ce->insertAtCurrentPosition('\t');
return true;
}
}
}
return false;
}
QAppliction
对象安装事件过滤器如果给QApplication对象装上过滤器,那么所有的事件在发往任何其他的过滤器时,都要先经过当前eventFilter()
在QApplication::notify()
中, 是先调用qApp
的过滤器,再对事件进行分析,以决定是否合并或丢弃
QApplication
类,并重载notify()
函数Qt
是用QApplication::notify()
函数来分发事件的,要在任何事件过滤器查看任何事件之前先得到这些事件,重写notify()
函数是唯一的办法;
通常来说事件过滤器更好用一些,因为不需要去继承QApplication
类,而且可以给QApplication
对象安装任意个数的事件过滤器
Qt
事件产生后会被立即发送到QWidget
对象QWwidget
中的event(QEvent*)
进行事件处理event(QEvent*)
根据事件类型调用不同的事件处理函数Qt
预定义的信号close
、key
、keyboard
事件等QEvent
并重写对象的event()
函数QEvent
中的主要成员函数
void ignore();// 接收者忽略当前事件,事件可能传递给父组件
void accept();// 接收者期望处理当前事件
bool isAccepted();// 判断当前事件是否被处理
继承QEvent
类,还需要提供一个QEvent::Type
类型的参数,作为自定义事件的类型值,这是Qt
事件类型的枚举,在Qt
中,系统保留了0-999
的值,故自定义事件的值要大于999,即自定义事件的值要在QEvent::User
与QEvent::MaxUser
之间,即1000-65535
,这能保证自定义事件不会覆盖系统事件
要想保证自定义事件之间不会被覆盖,则需要通过registerEventType()
函数注册自定义事件
函数声明如下
static int QEvent::registerEventType(int hint = -1);
若函数接受一个int
值,若hint
合法,即不会发生任何覆盖,则会返回hint
,若不合法,系统会自动分配一个合法值并返回
eventFilter
函数installEventFilter(receiver, QEvent* event)
函数,在目标对象上安装过滤器sendEvent()
发送方式sendEvent()
立即同步处理要发送的event
,对于多数的event
类,有一个成员函数isAccepted()
可用来判别event
事件是已被接受处理或被拒绝处理;使用这个函数必须栈上创建对象
函数声明
static bool QCoreApplication::sendEvent(QObjecy receiver, QEvent event)
发送过程
构造事件对象
QEvent event(QEvent::MouseButtonPress);
发送事件给指定对象
QApplication::sendEvent(receive, &event);
postEvent()
发送方式postEvent()
将event
提交到一个事件队列中等待调度,在下一次Qt
的主event loop
运行的时候,主event loop
就会以某种优化的方式调度所有提交到队列中的event
;例如, 如果有几个 resize event
,他们就会被压缩成一个事件
postEvent()
也被用于对象的初始化过程,因为提交过的event
通常在相应对象初始化完毕后极短的时间内就会被调度;使用这个函数必须在堆上创建对象
函数声明
static bool QCoreApplication::postEvent(QObject receiver, QEvent event)
发送过程
QApplication::postEvent(object, new MyEvent(QEvent::registerEventType(2048)) );
这个对象不需要手动delete
,Qt
会自动delete
信号通常用来”使用”widget
,而事件用来”实现”widget
事件由具体对象进行处理,信号由具体对象产生
所有信号的实现都是通过事件来实现的,例如,一个button
的clicked()
信号,如何处理鼠标事件再发射这个信号是由事件完成的
事件可以过滤,事件更底层,事件是基础,信号是扩展