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,内容请搜索靠谱客的其他文章。
发表评论 取消回复