我是靠谱客的博主 单身日记本,这篇文章主要介绍qtEvent, 事件传递、事件过滤器、update()、绘图事件、鼠标事件、鼠标穿透杂谈QT的事件/信号处理,现在分享给大家,希望可以做个参考。

catalog

  • 杂谈
    • 构造函数内的connect
    • 内存的父类、对象树的父类
  • QT的事件/信号处理
    • eventFilter
    • event
    • event和eventFilter应用
    • 鼠标/绘图事件、鼠标穿透

杂谈

构造函数内的connect

MyBtn::MyBtn(){ 
	connect( this, &QPushButton::clicked, this, [&](){ 111 } );
}

MyBtn* obj;
connect( obj, &QPushButton::clicked, this, [&](){ 222 } );

这是可以的。
点击按钮: 先调用111, 后调用222

111函数,与对象无关,所有对象执行的功能 都是完全一样的
222函数,可以与对象相关,不同的对象 执行不同的功能

内存的父类、对象树的父类

这两个概念,一定要分清楚!!!
从ui上看: my_widget的上面,有一个my_btn

对于这个my_btn来说:
1, 他的“内存的父类”,也就是 class层次上的父类
、、 class my_btn : QPushButton (即,my_btn的内存父类是: QPushButton)
、、他是“固定死的“,一旦你的my_btn类确定了,内存父类也就确定了
、、内存父类,是一个类名,不是对象

2, 他的“对象树的父类”,也就是 ui层次上的父类
、、my_btn.setParent( my_widget ); (即,my_btn的对象树父类是: my_widget)
、、他是“随时可以变的“,这个my_btn放到哪个ui上面,他的 ”对象树父类“,就是哪个
、、对象树父类,不是类名,是一个对象

QT的事件/信号处理

在这里插入图片描述
即,我们点击的这个事件e
1,先在btn2对象里,做一大堆的事情
2,然后在btn1对象里,做一大堆事情
3,最后在my_widget对象里,做一大堆事情
从btn2 -> btn1 -> my_widget,这就是一个“对象树”呀~

我们看上面,有两个核心的函数:

eventFilter

bool f = Fa.eventFilter( btn2, e );

你可能会担心,如果Fa里面,没有这个函数怎么办?QWidget里确实没有这个函数
但在QObject里:

virtual bool eventFilter(QObject *watched, QEvent *event){ ... }

即:即使Fa里没有这个函数,他会调用: QObject的这个函数!!

一般这个函数的写法是:

bool My_widget::eventFilter(QObject *watched, QEvent *event){
   if( watched == btn2 ){ ' 对特定的某个部件,处理 '
       if( event->type() == QEvent::MouseButtonPress ){
           // 处理
           return true; // true为拦截: 即提前终止!! 他不会进入event函数里
       }
       return false; // 其他事件,放行 (即可以正常进入event)
   }
   QWidget::eventFilter(watched, event);
   // 其实这个不写也行,因为前面if都判断了,也不会到达这里
   // 即便到达这里,父类也不认识这个watched,因为btn2是当前类里的
   return false;
}

这个函数的作用是:
比如你想对一个部件的某个事件 进行单独的处理(因为,事件的种类 要比 信号 多 更丰富)

  • 比如对于QPushButton btn; 你只能再自定义一个类 My_btn,然后重写My_btn::xxxEvent()函数,进行处理。
  • 但是,如果不想再写一个类 重写函数,有个更方便的做法
    直接将这个btn安装一个事件过滤器, 这样,连event函数 都不会进入

event

上图中, 可以说最最重要的函数,就是这个
当(没有安装 事件过滤器)或(事件过滤器 返回false不拦截),就会调用: btn2.event( e ) 函数

我们上图,只考虑了他的返回值 (表示:是否穿透 即,是否传递给:对象树的父类

我们这里,更多的介绍下 这个函数里面 做了什么

btn2.event( e )

思考:

  • 如果btn2是自定义类,且没有event函数呢???
  • 如果btn2是QPushButton呢?

看一下event的源码,就明白了:

bool QObject::event(QEvent *e){ ... }
bool QWidget::event(QEvent *e){ ... }
bool QPushButton::event(QEvent *e){ ... }

也就是说: 如果没有event函数,那他就调用: 父类的event函数 这里的父类,肯定是内存父类,不是对象树

重点: 任何一个部件的event函数,调用频率是非常高的!!! 意味着,这个函数非常重要!
千万要谨慎使用,因为,这个部件的一切(展示到屏幕,你对他的操作)都会调用event函数!!!
比如,你每次最小化/展示出 窗口,他都会调用很多次 绘图event
其中有: QPaintEvent(对base_widget) QPaintEvent(若干个子部件)

比如,你的My_btn::event(QEvent* e){ return true/false; }
函数里 什么都没有做,后果是: 你的按钮.show()后 什么都没有!!!
因为这个按钮,要展示到屏幕上,会有绘图事件。
具体他是怎么画,以及怎么发的信号Signal 等等,这些 都在: QPushButton::event(QEvent* e){ … }
系统把这么复杂的操作,都做好了(我们直接调用即可)

即: 我们此时“重写”了event函数 所谓重写: 父类QObject/QWidget/QPushButton都已经“实现”了这个函数,我们子类重新实现
我们重写的目的,可不是真的重写,那工作量就太大了…
而是新加一些功能,或捕获一些操作 做特殊处理

event返回值
对象树: my_widget -> btn1 -> btn2
我们不重写event,一切按照qt默认的来:
我们点击btn2
1, 进入btn2.event(),这函数内部执行了:btn2.MousePressEvent(发送信号)
2, btn2.event()函数的返回值,默认是: true 表示:不会穿透
即,你对btn2点击,btn1和my_widget是捕获不到这个事件的
如果btn2.event()返回是false,则会调用btn1.event(),同理,btn1.event()的返回值 决定是否会调用:my_widget.event()

默认情况下:

bool My_btn::event(QEvent* e){
	return QPushButton::event(e);
}

此时关注return QPushButton::event(e);这句话,不同的e 他的返回值是不同的!!
即,有些事件 系统默认是false 有穿透,有些默认是true
比如,如果e是QEvent::MousePressEvent,即鼠标点击事件,返回值是true;(不穿透)

当然没必要去记忆他,因为,既然我们自己捕获了,就自定义呗~
所以,他的返回值我们不要,我们可以写成:

if(e.type() == QEvent::MousePressEvent){
	QPushButton::event(e);
	return true; ' 这样,btn1/my_widget,也可以接受到 鼠标事件 '
}

此时,重点关注,QPushButton::event(e); 他里面做了什么??
他调用了: btn2.mousePressEvent(e)函数!!!

mousePressEvent函数源码:

void QWidget::mousePressEvent(QMouseEvent *e){ ... }
void QAbstractButton::mousePressEvent(QMouseEvent *e){ ... }

同样,即使你My_btn没有实现这个函数,他会调用父类的
既然你从event里,捕获了这个事件,自然是想调用你的mousePressEvent自定义函数

我们知道,此时点击btn2了,但是,经过event()后,我们发现 系统没做实质工作,只是把他交给了mousePressEvent函数
说明: 这个函数,是实际干事情的!!!(比如,点击按钮后: 发信号,按钮变化等等)

默认情况:

void My_btn::mousePressEvent(QMouseEvent* e){
	QPushButton::mousePressEvent(e);
}

当然,QPushButton::mousePressEvent(e);这个是实际做事情的!!!
比如: 发送了btn2.clicked信号,包括点击后按钮颜色变化等等…

在这里插入图片描述

我们自己重写event和xxEvent()函数,一定要遵守规则,不要做不该做的事情
event()函数: 不要做事情,他这是一个“转达”的作用,即交个具体函数来工作
即,你写了event() 肯定需要再重写一个xxEvent()函数,他是具体做事情的,event只是一个分发

bool Btn::event(QEvent *e){ 
    if(e->type() == QEvent::MouseButtonPress){
        QPushButton::event(e); // 这是重点!! 他会自动调用:this.mousePressEvent()函数
        // 或者在这里,直接指定到: 你的mousePressEvent函数执行
        return false; // 穿透,即对象树的父对象fa,也会调用fa.event()函数
    }
    QPushButton::event(e);  
    return false; // 也可以直接:return QPushButton::event(e),由系统决定,哪些事件是否要穿透
} ' 如果不指定“穿透性问题”,其实event也不用写,他默认就是这样 '

void Btn::mousePressEvent(QMouseEvent *e){
    DE<<e->x()<<", "<<e->y();
    QPushButton::mousePressEvent(e); // 重点,他的里面: 发送clicked信号,等等...
}

My_widget同时和Btn一样,重写了这2个函数

则对象树: my_widget -> btn1 -> btn2
点击btn2(以上面的代码来说):

  • 执行btn2.event(),执行btn2.mousePressEvent(),发送btn2.clicked信号
  • 执行btn1.event(),执行btn1.mousePressEvent(),发送btn1.clicked信号
  • 执行my_widget.event(),执行my_widget.mousePressEvent()
  • 最后: btn2的槽函数 -> btn1的槽函数 信号发出,不是立刻给槽函数,而是等到函数调用完毕后,才调用的btn2的槽函数

event和eventFilter应用

my_widget{
	QPushButton btn1;
	My_btn btn2;
}

对于btn1,你可以将它安装eventFilter,然后重写: my_widget::eventFilter(…)
这样,与btn1这个对象 相关的事件,就被你捕获了,你可以做一些额外处理,也可以决定 是否要交给btn1.event()
但对于btn1,你无法修改他的event/xxEvent函数,因为是系统的东西

对于btn2,你可以重写My_btn::event() + xxEvent(),这样: 如果到时候,btn2没有被eventFilter过滤掉,就会进入你自己的处理里面

鼠标/绘图事件、鼠标穿透

需求: 我们想,左击哪里 哪里就会出现一个“红点”(之前的红点消失)
而鼠标点击,并不会产生“paintEvent绘图事件”,所以,我们需要 手动调用绘图事件
手动让整个widget重新绘图,可以调用update()、repaint()函数 qt建议使用update()

void My_widget::mouseReleaseEvent(QMouseEvent *e){
    if(e->button() == Qt::LeftButton){
        ui_x = e->globalX(), ui_y = e->globalY();
        update();
    }
    QWidget::mouseReleaseEvent(e); // 千万不要忘了这里
}
void My_widget::paintEvent(QPaintEvent *e){
    QPainter p(this);
    p.setPen(QPen(Qt::red, 5));
    p.drawPoint(ui_x, ui_y);
    p.end();

    QWidget::paintEvent(e); // 千万不要忘了这里
}

以上代码,有一个问题: 当我们点击按钮区域时,是不会进入我们当前event事件里面的!!!
你去设置My_widget::event,让他返回false(默认返回true),是没有用的!!!
、、因为,这个点击,先交给了按钮对象,你的my_widget根本就接收不到!
因为,按钮默认是 不穿透的,即按钮的event返回true
而这个按钮是系统的,我们无法重写按钮的事件。
需求是: 让鼠标穿透下来, 我们的widget 也可以接收到 这个鼠标事件。
设置 按钮的“鼠标穿透”: btn->setAttribute(Qt::WA_TransparentForMouseEvents);
相对于,让按钮的event事件,返回了false
这样, 我们widget的event也可以接收到,点击按钮的鼠标事件。
但因为,你的paintEvent绘图是在widget上的, 所以,红点画在了widget上,被按钮挡住了。 不过ui_x和ui_y是记录了的

最后

以上就是单身日记本最近收集整理的关于qtEvent, 事件传递、事件过滤器、update()、绘图事件、鼠标事件、鼠标穿透杂谈QT的事件/信号处理的全部内容,更多相关qtEvent,内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(116)

评论列表共有 0 条评论

立即
投稿
返回
顶部