在前面我们也曾经简单提到,Qt 程序需要在main()函数创建一个QApplication对象,然后调用它的exec()函数。这个函数就是开始 Qt 的事件循环。在执行exec()函数之后,程序将进入事件循环来监听应用程序的事件。当事件发生时,Qt 将创建一个事件对象。Qt 中所有事件类都继承于QEvent。在事件对象创建完毕后,Qt 将这个事件对象传递给QObject的event()函数。event()函数并不直接处理事件,而是按照事件对象的类型分派给特定的事件处理函数。
在所有组件的父类QWidget中,定义了很多事件处理的回调函数,如:keyReleaseEvent()、keyPressEvent()、mouseDoubleClickEvent()、mouseMoveEvent()、mousePressEvent()、mouseReleaseEvent() 等。这些函数都是 protected virtual 的,也就是说,我们可以在子类中重新实现这些函数。例如:
class EventLabel : public QLabel
{
protected:
void mousePressEvent(QMouseEvent *event);//重写QLabel的鼠标按下处理函数
};
void EventLabel::mousePressEvent(QMouseEvent *event)
{
this->setText(QString("Press:(%1, %2)
").arg(QString::number(event->x()),
QString::number(event->y())));
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
EventLabel *label = new EventLabel;
label->setWindowTitle("MouseEvent Demo");
label->resize(300, 200);
label->show();
return a.exec();
}
在Qt中使用定时器有两种方法,一种是使用QObiect类的定时器;一种是使用QTimer类。
1、QObject类的定时器
int QObject::startTimer(int interval);
/* 开始一个定时器并返回定时器ID,函数调用失败,将返回0。定时器开始后,每隔interval毫秒间隔将触发一次事件 */
void QObject::killTimer(int id);
/* 删除一个指定的id的定时器 */
virtual void QObject::timerEvent(QTimerEvent *event);
/* 虚函数timerEvent()被重载来实现用户的超时事件处理函数 ,当存在多个定时器时,可通过event的成员函数timerId()进行判断*/
2、QTimer类的定时器
//1.产生一个定时器
QTimer *time_clock=new QTimer(parent);
//2.连接这个定时器的信号和槽,利用定时器的timeout(),即定时时间到就会发送timeout()信号,从而触发slottimedone()槽去完成某项事情
connect(time_clock,SIGNAL(timeout()),this,SLOT(slottimedone()));
//3.开始定时器,并设定定时周期,定时器定时有两种:
start(int time)和setSingleShot(true)
//其中start(int time)是表示每隔time秒就会重启定时器,重复触发
//而setSingleShot(true)则是仅仅启动定时器一次
//4、停止运行定时器
if ( time_clock->isActive() )
time_clock->stop();
https://www.devbean.net/2012/09/qt-study-road-2-events-accept-reject/
event()函数主要用于事件的分发。所以,如果你希望在事件分发之前做一些操作,就可以重写这个event()函数了。例如:
bool CustomWidget::event(QEvent *e)
{
//如果事件类型时键盘按下的话,则进行特殊处理
if (e->type() == QEvent::KeyPress)
{
//需要进行强制类型转换
QKeyEvent *keyEvent = static_cast(e);
if (keyEvent->key() == Qt::Key_Tab)
{
qDebug() << "You press tab.";
//表示该事件已经被处理,不需要往下传送了
return true;
}
}
//其他事件进行默认处理
return QWidget::event(e);
}
如果你不想重写一大堆事件处理器,就可以重写这个event()函数,通过QEvent::type()判断不同的事件。
QObject有一个eventFilter()函数,用于建立事件过滤器。函数原型如下:
//安装过滤器
void QObject::installEventFilter ( QObject * filterObj );
virtual bool QObject::eventFilter ( QObject * watched, QEvent * event );
所谓事件过滤器,可以理解成一种过滤代码。事件过滤器会检查接收到的事件。如果这个事件是我们感兴趣的类型,就进行我们自己的处理;如果不是,就继续转发。这个函数返回一个 bool 类型,如果你想将参数 event 过滤出来,比如,不想让它继续转发,就返回 true,否则返回 false。事件过滤器的调用时间是目标对象(也就是参数里面的watched对象)接收到事件对象之前。也就是说,如果你在事件过滤器中停止了某个事件,那么,watched对象以及以后所有的事件过滤器根本不会知道这么一个事件。已经存在的过滤器则可以通过QObject::removeEventFilter()函数移除。事件过滤器的强大之处在于,我们可以为整个应用程序添加一个事件过滤器。
示例代码:
MyWidget::MyWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::MyWidget)
{
ui->setupUi(this);
//安装事件过滤器
ui->textEdit->installEventFilter(this);
}
bool MyWidget::eventFilter(QObject *obj, QEvent *event)
{
//判断该对象是不是自己感兴趣的控件
if (obj == ui->textEdit)
{
//判断该事件类型是不是自己感兴趣的事件
if (event->type() == QEvent::KeyPress)
{
//强制类型转换
QKeyEvent *keyEvent = static_cast(event);
qDebug() << "Ate key press" << keyEvent->key();
return true;
}
return false;
}
else
{
//其他对象进行默认处理
return MyWidget::eventFilter(obj, event);
}
}
注意:
事件过滤器和被安装过滤器的组件必须在同一线程,否则,过滤器将不起作用。另外,如果在安装过滤器之后,这两个组件到了不同的线程,那么,只有等到二者重新回到同一线程的时候过滤器才会有效。
现在我们可以总结一下 Qt 的事件处理,实际上是有五个层次: