Qt 学习之路 2(19):事件的接受与忽略(当重写事件回调函数时,时刻注意是否需要通过调用父类的同名函数来确保原有实现仍能进行!有好几个例子。为什么要这么做?而不是自己去手动调用这两个函数呢?因为我们无法确认父类中的这个处理函数有没有额外的操作)
版本:
- 2012-09-29
- 2013-04-23 更新有关
accept()
和ignore()
函数的相关内容。 - 2013-12-02 增加有关
accept()
和ignore()
函数的示例。
上一章我们介绍了有关事件的相关内容。我们曾经提到,事件可以依情况接受和忽略。现在,我们就来了解下有关事件的更多的知识。
首先来看一段代码:
这是一段简单的代码,经过我们前面一段时间的学习,我们已经能够知道这段代码的运行结果:点击按钮,会在控制台打印出“You clicked this!”字符串。这是我们前面介绍过的内容。下面,我们向CustomButton
类添加一个事件函数:
我们重写了CustomButton
的mousePressEvent()
函数,也就是鼠标按下。在这个函数中,我们判断如果鼠标按下的是左键,则打印出来“left”字符串,否则,调用父类的同名函数。编译运行这段代码,当我们点击按钮时,“You clicked this!”字符串不再出现,只有一个“left”。也就是说,我们把父类的实现覆盖掉了。由此可以看出,父类QPushButton
的mousePressEvent()
函数中肯定发出了clicked()
信号,否则的话,我们的槽函数怎么会不执行了呢?这暗示我们一个非常重要的细节:当重写事件回调函数时,时刻注意是否需要通过调用父类的同名函数来确保原有实现仍能进行!比如我们的CustomButton
类,如果像我们这么覆盖函数,clicked()
信号永远不会发生,你连接到这个信号的槽函数也就永远不会被执行。这个错误非常隐蔽,很可能会浪费你很多时间才能找到。因为这个错误不会有任何提示。这一定程度上说,我们的组件“忽略”了父类的事件,但这更多的是一种违心之举,一种错误。
通过调用父类的同名函数,我们可以把 Qt 的事件传递看成链状:如果子类没有处理这个事件,就会继续向其父类传递。Qt 的事件对象有两个函数:accept()
和ignore()
。正如它们的名字一样,前者用来告诉 Qt,这个类的事件处理函数想要处理这个事件;后者则告诉 Qt,这个类的事件处理函数不想要处理这个事件。在事件处理函数中,可以使用isAccepted()
来查询这个事件是不是已经被接收了。具体来说:如果一个事件处理函数调用了一个事件对象的accept()
函数,这个事件就不会被继续传播给其父组件;如果它调用了事件的ignore()
函数,Qt 会从其父组件中寻找另外的接受者。
事实上,我们很少会使用accept()
和ignore()
函数,而是像上面的示例一样,如果希望忽略事件(所谓忽略,是指自己不想要这个事件),只要调用父类的响应函数即可。记得我们曾经说过,Qt 中的事件都是 protected 的,因此,重写的函数必定存在着其父类中的响应函数,所以,这个方法是可行的。为什么要这么做,而不是自己去手动调用这两个函数呢?因为我们无法确认父类中的这个处理函数有没有额外的操作。如果我们在子类中直接忽略事件,Qt 会去寻找其他的接收者,该子类的父类的操作会被忽略(因为没有调用父类的同名函数),这可能会有潜在的危险。为了避免自己去调用accept()
和ignore()
函数,而是尽量调用父类实现,Qt 做了特殊的设计:事件对象默认是 accept 的,而作为所有组件的父类QWidget
的默认实现则是调用ignore()
。这么一来,如果你自己实现事件处理函数,不调用QWidget
的默认实现,你就等于是接受了事件;如果你要忽略事件,只需调用QWidget
的默认实现。这一点我们前面已经说明。下面可以从代码级别来理解这一点,我们可以查看一下QWidget
的mousePressEvent()
函数的实现:
这段代码在 Qt4 和 Qt5 中基本一致(区别在于activePopupWidget()
一行,Qt4 的版本是qApp->activePopupWidget()
)。注意函数的第一个语句:event->ignore()
,如果子类都没有重写这个函数,Qt 会默认忽略这个事件,继续寻找下一个事件接收者。如果我们在子类的mousePressEvent()
函数中直接调用了accept()
或者ignore()
,而没有调用父类的同名函数,QWidget::mousePressEvent()
函数中关于Popup
判断的那段代码就不会被执行,因此可能会出现默认其妙的怪异现象。
针对accept()
和ignore()
,我们再来看一个例子:
这段代码在一个MainWindow
中添加了一个CustomWidget
,里面有两个按钮对象:CustomButton
和CustomButtonEx
。每一个类都重写了mousePressEvent()
函数。运行程序点击 CustomButtonEx,结果是
这是因为我们重写了鼠标按下的事件,但是并没有调用父类函数或者显式设置accept()
或ignore()
。下面我们在CustomButtonEx
的mousePressEvent()
第一行增加一句event->accept()
,重新运行,发现结果不变。正如我们前面所说,QEvent
默认是accept
的,调用这个函数并没有什么区别。然后我们将CustomButtonEx
的event->accept()
改成event->ignore()
。这次运行结果是
ignore()
说明我们想让事件继续传播,于是CustomButtonEx
的父组件CustomWidget
也收到了这个事件,所以输出了自己的结果。同理,CustomWidget
又没有调用父类函数或者显式设置accept()
或ignore()
,所以事件传播就此打住。这里值得注意的是,CustomButtonEx
的事件传播给了父组件CustomWidget
,而不是它的父类CustomButton
。事件的传播是在组件层次上面的,而不是依靠类继承机制。
接下来我们继续测试,在CustomWidget
的mousePressEvent()
中增加QWidget::mousePressEvent(event)
。这次的输出是
如果你把QWidget::mousePressEvent(event)
改成event->ignore()
,结果也是一样的。这正如我们前面说的,QWidget
的默认是调用event->ignore()
。
在一个特殊的情形下,我们必须使用accept()
和ignore()
函数,那就是窗口关闭的事件。对于窗口关闭QCloseEvent
事件,调用accept()
意味着 Qt 会停止事件的传播,窗口关闭;调用ignore()
则意味着事件继续传播,即阻止窗口关闭。回到我们前面写的简单的文本编辑器。我们在构造函数中添加如下代码:
setWindowTitle()
函数可以使用 [] 这种语法来表明,在窗口内容发生改变时(通过setWindowModified(true)
函数通知),Qt 会自动在标题上面的 [] 位置替换成 * 号。我们使用 Lambda 表达式连接QTextEdit::textChanged()
信号,将windowModified
设置为 true。然后我们需要重写closeEvent()
函数。在这个函数中,我们首先判断是不是有过修改,如果有,则弹出询问框,问一下是否要退出。如果用户点击了“Yes”,则接受关闭事件,这个事件所在的操作就是关闭窗口。因此,一旦接受事件,窗口就会被关闭;否则窗口继续保留。当然,如果窗口内容没有被修改,则直接接受事件,关闭窗口。
https://www.devbean.net/2012/09/qt-study-road-2-events-accept-reject/
Qt 学习之路 2(19):事件的接受与忽略(当重写事件回调函数时,时刻注意是否需要通过调用父类的同名函数来确保原有实现仍能进行!有好几个例子。为什么要这么做?而不是自己去手动调用这两个函数呢?因为我们无法确认父类中的这个处理函数有没有额外的操作)的更多相关文章
- Qt 学习之路 2(19):事件的接受与忽略
Home / Qt 学习之路 2 / Qt 学习之路 2(19):事件的接受与忽略 Qt 学习之路 2(19):事件的接受与忽略 豆子 2012年9月29日 Qt 学习之路 2 140条评论 ...
- Qt 学习之路 2(68):访问网络(4)
Home / Qt 学习之路 2 / Qt 学习之路 2(68):访问网络(4) Qt 学习之路 2(68):访问网络(4) 豆子 2013年11月7日 Qt 学习之路 2 19条评论 前面几章我们了 ...
- Qt 学习之路 2(73):Qt 线程相关类
Home / Qt 学习之路 2 / Qt 学习之路 2(73):Qt 线程相关类 Qt 学习之路 2(73):Qt 线程相关类 豆子 2013年11月26日 Qt 学习之路 2 7条评论 希 ...
- Qt 学习之路 2(63):使用 QJson 处理 JSON
Home / Qt 学习之路 2 / Qt 学习之路 2(63):使用 QJson 处理 JSON Qt 学习之路 2(63):使用 QJson 处理 JSON 豆子 2013年9月9日 Qt ...
- 《Qt 学习之路 2》目录
<Qt 学习之路 2>目录 <Qt 学习之路 2>目录 豆子 2012年8月23日 Qt 学习之路 2 177条评论 <Qt 学习之路 2>目录 序 Qt ...
- Qt 学习之路 2(72):线程和事件循环
Qt 学习之路 2(72):线程和事件循环 <理解不清晰,不透彻> -- 有需求的话还需要进行专题学习 豆子 2013年11月24日 Qt 学习之路 2 34条评论 前面一章我 ...
- Qt 学习之路 2(23):自定义事件
Qt 学习之路 2(23):自定义事件 豆子 2012年10月23日 Qt 学习之路 2 21条评论 尽管 Qt 已经提供了很多事件,但对于更加千变万化的需求来说,有限的事件都是不够的.例如, ...
- Qt 学习之路 2(22):事件总结
Qt 学习之路 2(22):事件总结 豆子 2012年10月16日 Qt 学习之路 2 47条评论 Qt 的事件是整个 Qt 框架的核心机制之一,也比较复杂.说它复杂,更多是因为它涉及到的函数众多,而 ...
- Qt 学习之路 2(18):事件
Home / Qt 学习之路 2 / Qt 学习之路 2(18):事件 Qt 学习之路 2(18):事件 豆子 2012年9月27日 Qt 学习之路 2 60条评论 事件(event)是由系统 ...
随机推荐
- 【HDU 1402】A * B Problem Plus(FFT)
Problem Description Calculate A * B. Input Each line will contain two integers A and B. Process to e ...
- zoj 1240
IBM Minus One Time Limit: 2 Seconds Memory Limit: 65536 KB You may have heard of the book '2001 ...
- 可以学习相关框架【转:https://testerhome.com/topics/6283】
https://testerhome.com/topics/6283 单元测试方面(Java): Junit:本来想用我熟悉的testng,但是开发的同学说测试springmvc只能用Junit.所以 ...
- 多边形之战(bzoj 2927)
Description 多边形之战是一个双人游戏.游戏在一个有n个顶点的凸多边形上进行,这个凸多边形的n-3条对角线将多边形分成n-2个三角形,这n-3条对角线在多边形的顶点相交.三角形中的一个被染成 ...
- 【BZOJ1225】求正整数(数论)
题意:对于任意输入的正整数n,请编程求出具有n个不同因子的最小正整数m. n<=50000 思路:记得以前好像看的是maigo的题解 n即为将m分解为质数幂次的乘积后的次数+1之积 经检验只需要 ...
- BMP格式,转载
BMP文件格式,又称为Bitmap(位图)或是DIB(Device-Independent Device,设备无关位图),是Windows系统中广泛使用的图像文件格式.由于它可以不作任何变换地保存图像 ...
- make only output error/warning message( 编译时,只输出错误信息和警告信息)
make > /dev/null 这样,正常的信息被重定向输出到/dev/null,错误和警告信息会输出到标准错误设备(standard error,相对于标准输入/输出设备来说).
- Python闭包函数
闭包 闭包:python中的闭包从表现形式上定义(解释)为: 如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure). 先看一个函数: ...
- mongodb按照日期分组统计
目录 1.使用时间格式化方法 2.进行时间补偿(默认当前时区是东八区,即8x3600x1000=28800000) mongodb的默认时间是格林尼治时间,如果是要按照日期进行分组需要注意!!!. 解 ...
- Hive安装中遇到过的坑
实现说明每一个用户的环境都有细微的不一致,所以这里只是个人经过这些坑的处理,但是不意味着所有处理都是这样的操作,仅作为参考. 第一个坑 数据库安装,数据库最好装在Linux上,一直出了很多错,这里有一 ...