当前位置:   article > 正文

Qt 之 Event_qt event

qt event

事件

事件开始

exec()函数。
在启动该函数之后,程序进入事件循环来监听应用程序的事件

事件对象QEvent

  • 当某个事件发生时,Qt将创建一个事件对象。
  • Qt中所有事件都继承与QEvent

事件的转发

在事件对象创建完毕后,Qt将这个事件对象传递给QObject的event( )函数。

  • event( )函数并不直接处理事件, 而是按照事件对象的类型分派给特定的事件处理函数(event handler)。
  • 在自定义类中重写该函数
//虚函数, 事件分发
bool event(QEvent *e);
  • 1
  • 2

QWidget自定义事件处理函数

keyPressEvent();
keyReleaseEvent();
mouseDoubleClickEvent();
mouseReleaseEvent();
  • 1
  • 2
  • 3
  • 4

上述这些函数时 protected virtual 的,可以在子类中重新实现这些函数。

鼠标追踪

QWidget有一个MouseTracking的属性,默认情况下,该属性是false(关闭的)。 也就是说,在默认情况下, mouseMoveEvent 事件,只有当鼠标点击一下后,才会发出。
可以通过设置该属性为true来开机move追踪事件。

selfWidget->setMouseTracking(true);
  • 1

事件的接收和过滤

  • custombutton.h
class CustomButton : public QPushButton
{
    Q_OBJECT
public:
    CustomButton(QWidget *parent = 0);

protected:
   void mousePressEvent(QMouseEvent *event);	//重写mousePressEvent事件处理函数

private:
    void onButtonCliecked();
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • custombutton.cpp

CustomButton::CustomButton(QWidget *parent) :
    QPushButton(parent)
{
    connect(this, &CustomButton::clicked,
            this, &CustomButton::onButtonCliecked);
}

//重写mousePressEvent 处理函数
//当重写事件回调函数时,时刻注意是否需要通过调用父类的同名函数来确保原有实现仍能进行!
//下述重写后,上面的connect中的"clicked"信号,永远不会发生,这个信号连接的槽函数也就永远不会被执行。
void mousePressEvent(QMouseEvent *event)
{
	if(event->button() == Qt::LeftButton)
	{
		qDebug()<<"Left";
	}
	else
	{
		QPushButton::mousePressEvent(event);
	}
}


void CustomButton::onButtonCliecked()
{
    qDebug() << "You clicked this!";
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • main.cpp
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    CustomButton btn;
    btn.setText("This is a Button!");
    btn.show();

    return a.exec();
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 通过调用父类的同名函数,可以把Qt的事件传递看成链状:如果子类没有处理这个事件, 就会继续向其父类传递。

accept()和ignore()

accept()

如果一个事件处理函数调用了一个事件对象的 accept( ) 函数,这个事件就不会被继续传播给其父组件

ignore()

如果调用了事件的ignore( )函数,Qt会从其父组件中寻找另外的接受者。

isAccepted()

查询事件是否已经被接收了。

事实上,我们很少会使用accept()和ignore()函数,而是像上面的示例一样,如果希望忽略事件(所谓忽略,是指自己不想要这个事件),只要调用父类的响应函数即可。为了避免自己去调用accept()和ignore()函数,而是尽量调用父类实现,Qt 做了特殊的设计:事件对象默认是 accept 的,而作为所有组件的父类QWidget的默认实现则是调用ignore()。这么一来,如果你自己实现事件处理函数,不调用QWidget的默认实现,你就等于是接受了事件;如果你要忽略事件,只需调用QWidget的默认实现。

QCloseEvent关闭窗口事件

在一个特殊的情形下,我们必须使用accept()和ignore()函数,那就是窗口关闭的事件。对于窗口关闭QCloseEvent事件,调用accept()意味着 Qt 会停止事件的传播,窗口关闭;调用ignore()则意味着事件继续传播,即阻止窗口关闭。

void MainWindow::closeEvent(QCloseEvent *event)
{
   bool exit = QMessageBox::question(this,
                     tr("Quit"),
                     tr("Are you sure to quit this application?"),
                     QMessageBox::Yes | QMessageBox::No,
                     QMessageBox::No) == QMessageBox::Yes);
	if (exit) 
	{
	        event->accept();
	} 
	else 
	{
	         event->ignore();
	}
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

event()

  • event事件处理事件分发在这里插入图片描述

event事件的枚举

Event Class的 Event类型

  • exmaple
    在这里插入图片描述

Demo:检测Tab键

bool CustomWidget::event(QEvent *e)  //QEvnet 对象作为参数,即需要转发的事件对象
{
    if (e->type() == QEvent::KeyPress) {
        QKeyEvent *keyEvent = static_cast<QKeyEvent *>(e);  //强制类型转换,将QEvent转成QKeyEvent
        //另一种转换
        //QKeyEvent *keyEvent = (QKeyEvent *)e;
        if (keyEvent->key() == Qt::Key_Tab) {
            qDebug() << "You press tab.";
            return true;
        }
    }
    return QWidget::event(e);
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

1.如果传入的事件已被识别并且处理,则需要返回 true,否则返回 false。如果返回值是 true,那么 Qt 会认为这个事件已经处理完毕,不会再将这个事件发送给其它对象,而是会继续处理事件队列中的下一事件。

2.在event()函数中,调用事件对象的accept()和ignore()函数是没有作用的,不会影响到事件的传播。

重写event( )陷阱

在重写event()处理器时, 需要格外小心是否需要转发,否则就会出现只响应自定义的事件的情况

bool CustomTextEdit::event(QEvent *e)
{
   if (e->type() == QEvent::KeyPress) 
   {
        QKeyEvent *keyEvent = static_cast<QKeyEvent *>(e);
       if (keyEvent->key() == Qt::Key_Tab) 
       {
            qDebug() << "You press tab.";
            return true;						//处理完Tab 按键消息,就直接返回,表示已经
            								    //对Tab键按下事件进行了处理
       }
    }
    
    return false; 						//对于其他事件,由于直接返回false, Qt不转发,其父类	
                                        //收不到其他事件,导致这个自定义的Widget只能处理Tab事件
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

event( )控制器源码

//!!! Qt5
bool QObject::event(QEvent *e)
{
    switch (e->type()) {
    case QEvent::Timer:
        timerEvent((QTimerEvent*)e);
        break;

    case QEvent::ChildAdded:
    case QEvent::ChildPolished:
    case QEvent::ChildRemoved:
        childEvent((QChildEvent*)e);
        break;
    // ...
    default:
        if (e->type() >= QEvent::User) {
            customEvent(e);
            break;
        }
        return false;
    }
    return true;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

事件过滤器

事件过滤场景

  • 对象需要查看,拦截发送到另外对象的事件
  • 对话框拦截按钮事件
  • 修改回车键的默认处理

在这里插入图片描述

eventFilter 函数

  • 原型
virtual bool QObject::eventFilter ( QObject * watched, QEvent * event );
  • 1
Created with Raphaël 2.3.0 事件来了 eventFilter( ) true/false? 转发 不转发 yes no

调用机制

事件过滤器的调用时间是目标对象(也就是参数里面的watched对象)接收到事件对象之前。
也就是说,如果你在事件过滤器中停止了某个事件,那么,watched对象以及以后所有的事件过滤器根本不会知道这么一个事件。

 class MainWindow : public QMainWindow
 {
 public:
     MainWindow();

 protected:
     bool eventFilter(QObject *obj, QEvent *ev) override;

 private:
     QTextEdit *textEdit;
 };

 MainWindow::MainWindow()
 {
     textEdit = new QTextEdit;
     setCentralWidget(textEdit);

     textEdit->installEventFilter(this);			//安装过滤器
 }

 bool MainWindow::eventFilter(QObject *obj, QEvent *event)
 {
     /*
      * In your reimplementation of this function, if you want to filter the event out,
      * i.e. stop it being handled further, return true; otherwise return false.
      */
     if (obj == textEdit) {									//过滤特定Widget上的事件,判断obj对象
         if (event->type() == QEvent::KeyPress) {
             QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
             qDebug() << "Ate key press" << keyEvent->key();
             return true;							//stop it being handled further
         } else {
             return false;							//return false, let it being handled further
         }
     } else {
         // pass the event on to the parent class
         return QMainWindow::eventFilter(obj, event);
     }
 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

安装和移除事件过滤器

void QObject::installEventFilter ( QObject * filterObj )
  • 1

这个函数接受一个QObject *类型的参数。记得刚刚我们说的,eventFilter()函数是QObject的一个成员函数,因此,任意QObject都可以作为事件过滤器(问题在于,如果你没有重写eventFilter()函数,这个事件过滤器是没有任何作用的,因为默认什么都不会过滤)。已经存在的过滤器则可以通过QObject::removeEventFilter()函数移除。

我们可以向一个对象上面安装多个事件处理器,只要调用多次installEventFilter()函数。如果一个对象存在多个事件过滤器,那么,最后一个安装的会第一个执行,也就是后进先执行的顺序。

不应该

事件过滤器和被安装过滤器的组件必须在同一线程,否则,过滤器将不起作用。另外,如果在安装过滤器之后,这两个组件到了不同的线程,那么,只有等到二者重新回到同一线程的时候过滤器才会有效。

应该

事件过滤器和被安装过滤器的组件必须在同一线程,否则,过滤器将不起作用。另外,如果在安装过滤器之后,这两个组件到了不同的线程,那么,只有等到二者重新回到同一线程的时候过滤器才会有效。

event无法处理的情况

  • 不允许处理某个事件

event()函数是一个 protected 的函数,这意味着我们要想重写event(),必须继承一个已有的类。
试想,我的程序根本不想要鼠标事件,程序中所有组件都不允许处理鼠标事件,是不是我得继承所有组件,一一重写其event()函数?

  • 库中的组件无法继承

?protected 函数带来的另外一个问题是,如果我基于第三方库进行开发,而对方没有提供源代码,只有一个链接库,其它都是封装好的。我怎么去继承这种库中的组件呢?

  • 不想看到某种事件

event()函数的确有一定的控制,不过有时候我的需求更严格一些:希望那些组件根本看不到这种事件。event()函数虽然可以拦截,但其实也是接收到了QMouseEvent对象。

事件过滤器的有点

  • 首先,事件过滤器不是 protected 的,因此我们可以向任何QObject子类安装事件过滤器;
  • 其次,事件过滤器在目标对象接收到事件之前进行处理,如果我们将事件过滤掉,目标对象根本不会见到这个事件。

总结

  • 5个层次
层次操作说明
1重写paintEvent()、mousePressEvent()等事件处理函数简单
2重写event( )所有对象事件的入口,QObject和QWidget 中的实现,默认是把所有事件传递给特定的事件处理函数
3在特定的Widget或对象上安装事件过滤器过滤器仅过滤该对象接收到的事件
4在 QCoreApplication::instance()上面安装事件过滤器全局过滤器有一个问题:只能用在主线程
5重写QCoreApplication::notify()函数这是最强大的,和全局事件过滤器一样提供完全控制,并且不受线程的限制。但是全局范围内只能有一个被使用(因为QCoreApplication是单例的)。
声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号