QT事件的接受与忽略

转载:http://blog.csdn.net/gusgao/article/details/48862391

首先来看一段代码:

//!!! Qt5
// ---------- custombutton.h ---------- //
class CustomButton : public QPushButton
{Q_OBJECT
public:CustomButton(QWidget *parent = 0);
private:void onButtonCliecked();
};// ---------- custombutton.cpp ---------- //
CustomButton::CustomButton(QWidget *parent) :QPushButton(parent)
{connect(this, &CustomButton::clicked,this, &CustomButton::onButtonCliecked);
}void CustomButton::onButtonCliecked()
{qDebug() << "You clicked this!";
}// ---------- 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
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

这是一段简单的代码,经过我们前面一段时间的学习,我们已经能够知道这段代码的运行结果:点击按钮,会在控制台打印出“You clicked this!”字符串。这是我们前面介绍过的内容。下面,我们向CustomButton类添加一个事件函数:

// CustomButton
...
protected:void mousePressEvent(QMouseEvent *event);
...// ---------- custombutton.cpp ---------- //
...
void CustomButton::mousePressEvent(QMouseEvent *event)
{if (event->button() == Qt::LeftButton) {qDebug() << "left";} else {QPushButton::mousePressEvent(event);}
}
...
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

我们重写了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()函数的实现:

//!!! Qt5
void QWidget::mousePressEvent(QMouseEvent *event)
{event->ignore();if ((windowType() == Qt::Popup)) {event->accept();QWidget* w;while ((w = QApplication::activePopupWidget()) && w != this){w->close();if (QApplication::activePopupWidget() == w)w->hide(); // hide at least}if (!rect().contains(event->pos())){close();}}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

这段代码在 Qt4 和 Qt5 中基本一致(区别在于activePopupWidget()一行,Qt4 的版本是qApp->activePopupWidget())。注意函数的第一个语句:event->ignore(),如果子类都没有重写这个函数,Qt 会默认忽略这个事件,继续寻找下一个事件接收者。如果我们在子类的mousePressEvent()函数中直接调用了accept()或者ignore(),而没有调用父类的同名函数,QWidget::mousePressEvent()函数中关于Popup判断的那段代码就不会被执行,因此可能会出现默认其妙的怪异现象。

针对accept()和ignore(),我们再来看一个例子:

class CustomButton : public QPushButton
{Q_OBJECT
public:CustomButton::CustomButton(QWidget *parent): QPushButton(parent){}
protected:void mousePressEvent(QMouseEvent *event){qDebug() << "CustomButton";}
};class CustomButtonEx : public CustomButton
{Q_OBJECT
public:CustomButtonEx::CustomButtonEx(QWidget *parent): CustomButton(parent){}
protected:void mousePressEvent(QMouseEvent *event){qDebug() << "CustomButtonEx";}
};class CustomWidget : public QWidget
{Q_OBJECT
public:CustomWidget::CustomWidget(QWidget *parent): QWidget(parent){}
protected:void mousePressEvent(QMouseEvent *event){qDebug() << "CustomWidget";}
};class MainWindow : public QMainWindow
{Q_OBJECT
public:MainWindow::MainWindow(QWidget *parent = 0): QMainWindow(parent){CustomWidget *widget = new CustomWidget(this);CustomButton *cbex = new CustomButton(widget);cbex->setText(tr("CustomButton"));CustomButtonEx *cb = new CustomButtonEx(widget);cb->setText(tr("CustomButtonEx"));QVBoxLayout *widgetLayout = new QVBoxLayout(widget);widgetLayout->addWidget(cbex);widgetLayout->addWidget(cb);this->setCentralWidget(widget);}
protected:void mousePressEvent(QMouseEvent *event){qDebug() << "MainWindow";}
};
  • 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
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68

这段代码在一个MainWindow中添加了一个CustomWidget,里面有两个按钮对象:CustomButton和CustomButtonEx。每一个类都重写了mousePressEvent()函数。运行程序点击 CustomButtonEx,结果是`CustomButtonEx

这是因为我们重写了鼠标按下的事件,但是并没有调用父类函数或者显式设置accept()或ignore()。下面我们在CustomButtonEx的mousePressEvent()第一行增加一句event->accept(),重新运行,发现结果不变。正如我们前面所说,QEvent默认是accept的,调用这个函数并没有什么区别。然后我们将CustomButtonEx的event->accept()改成event->ignore()。这次运行结果是CustomButtonEx
CustomWidget` 
ignore()说明我们想让事件继续传播,于是CustomButtonEx的父组件CustomWidget也收到了这个事件,所以输出了自己的结果。同理,CustomWidget又没有调用父类函数或者显式设置accept()或ignore(),所以事件传播就此打住。这里值得注意的是,CustomButtonEx的事件传播给了父组件CustomWidget,而不是它的父类CustomButton。事件的传播是在组件层次上面的,而不是依靠类继承机制。

接下来我们继续测试,在CustomWidget的mousePressEvent()中增加QWidget::mousePressEvent(event)。这次的输出是CustomButtonEx 
CustomWidget 
MainWindow

如果你把QWidget::mousePressEvent(event)改成event->ignore(),结果也是一样的。这正如我们前面说的,QWidget的默认是调用event->ignore()。

在一个特殊的情形下,我们必须使用accept()和ignore()函数,那就是窗口关闭的事件。对于窗口关闭QCloseEvent事件,调用accept()意味着 Qt 会停止事件的传播,窗口关闭;调用ignore()则意味着事件继续传播,即阻止窗口关闭。回到我们前面写的简单的文本编辑器。我们在构造函数中添加如下代码:

“` 
//!!! Qt5 
… 
textEdit = new QTextEdit(this); 
setCentralWidget(textEdit); 
connect(textEdit, &QTextEdit::textChanged, = { 
this->setWindowModified(true); 
});

setWindowTitle(“TextPad [*]”); 

void MainWindow::closeEvent(QCloseEvent *event) 

if (isWindowModified()) { 
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(); 

} else { 
event->accept(); 


“`setWindowTitle()函数可以使用 [] 这种语法来表明,在窗口内容发生改变时(通过setWindowModified(true)函数通知),Qt 会自动在标题上面的 [] 位置替换成 * 号。我们使用 Lambda 表达式连接QTextEdit::textChanged()信号,将windowModified设置为 true。然后我们需要重写closeEvent()函数。在这个函数中,我们首先判断是不是有过修改,如果有,则弹出询问框,问一下是否要退出。如果用户点击了“Yes”,则接受关闭事件,这个事件所在的操作就是关闭窗口。因此,一旦接受事件,窗口就会被关闭;否则窗口继续保留。当然,如果窗口内容没有被修改,则直接接受事件,关闭窗口。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/463773.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

导师问我打开句柄fd和没有打开的差异在哪里?

大家好昨晚看到一个同学在群里提问&#xff0c;想简单回答这个问题&#xff0c;我的答案可能不是最全面的&#xff0c;文章最后的两篇技术文大家可以看看&#xff0c;大家也可以说下自己的看法。fd的发明我觉得是计算机的一个壮举&#xff0c;因为对于应用程序来说&#xff0c;…

.net数据绑定控件中的数据导出到Excel

在做审计局内部系统时&#xff0c;数据需要导出到Excel&#xff0c;在网上找了些代码&#xff0c;成功的完成了此功能 在html代码第一行中添加EnableEventValidation"false" <% Page Language"C#" AutoEventWireup"true" EnableEventValidat…

汇编调用c函数为什么要设置栈

一.栈的整体作用 (1)保存现场/上下文 (2)传递参数:汇编代码调用c函数时&#xff0c;需传递参数 (3)保存临时变量:包括函数的非静态局部变量以及编译器自动生成的其他临时变量。 二.为什么汇编代码调用c函数需要设置栈 之前看了很多关于uboot的分析&#xff0c;其中就有说要为C语…

C++之Boost准标准库配置

下载安装 进入官网下载地址&#xff1a;https://www.boost.org/users/download/ 本教程直接下载官方已编译库&#xff0c;不涉及源代码手动编译 点击官方编号好的链接&#xff0c;然后进入一个下载地址&#xff1a;https://sourceforge.net/projects/boost/files/boost-binarie…

给.net初学者的一些建议(共勉之)[转载]

.不要以为.net就需要把代码全部写到.cs页面,不屑于使用vs提供的方便的控件操作. .不要以为.net就是把各种控件往页面上一拖,然后通过vs方便的设置一下就ok. .不要以为.net就需要在一开始从编译原理到web服务各种各样的书都看上一遍 .不要以为.net就是直接打开vs建立页面然后闷头…

大小端模式 续

union w { int a; char x[2]; }e; e.x[0]10; e.x[1]1; cout<<e.a<<endl; 那么运算结果在little-endian模式中的结果为266,那么原因是什么呢 我个人的理解 小端模式下&#xff1a; printf("0x%x", e.a); 结果为0x10a 大端模式下&#xff1a; printf("…

利用HTML中的XML数据岛记录浏览

html文件&#xff1a;shop.html <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns"http://www.w3.org/1999/xhtml"> <head> <me…

1.5

C Concurrency in Action Anthony Williams Chapter 1. Hello, world of concurrency in C! 1.1 What is concurrency? 1.1.1 Concurrency in computer systems 1.1.2 Approaches to concurrency 1.2 Why use concurrency? 1.2.1 Using concurrency for separation of concer…

测试一下你对IP地址的掌握水平(网管面试时会用到)

以下内容摘自《网管员面试宝典》一书。测试一下你对IP地址的理解能力&#xff0c;大家先不看题后的解答&#xff0c;看自己能做出多少题。网管面试时会用到的。面试题1&#xff1a;以下说法正确的是&#xff08; &#xff09;。A. C类地址就是局域网用的IP地址 B. A类地址的网…

中秋的秋

又是一年中秋中秋是比较特别的节日&#xff0c;因为每一年的中秋&#xff0c;我和小云总是能遇到各种事情而分开「当然今年不会」。去年的时候&#xff0c;我们因为要赶项目&#xff0c;所以中秋申请了加班&#xff0c;要申请加班的那天我还是挺不情愿的&#xff0c;然后旁边的…

OpenSSL原理与实现

1. 概念 1.1. SSL&#xff08;Secure Sockets Layer安全层套接字&#xff09;/TLS&#xff08;Transport Layer Security传输层套接字&#xff09;。 最常见的应用是在网站安全方面&#xff0c;用于http数据传输的加密。 1.2. 安全的目标&#xff1a;保密性&#xff08;confide…

shell的debug模式

如何调试shell脚本&#xff1f; 在指定shell运行版本时加上 -x #!/bin/bash -x ➜ demo git:(master) ✗ cat debug.sh #!/bin/bash -xecho "hi" date sleep 1 echo "hi" date调试结果&#xff1a; ➜ demo git:(master) ✗ ./debug.sh echo hi hidat…

C++ 重载(overload)、重写(overrride)、重定义(redefine)总结

昨晚打开论坛&#xff0c;看到有朋友问了一个关于虚函数的问题&#xff0c;因为头太疼了&#xff0c;所以今天中午起床再看。 问题传送门&#xff1a;http://www.cppleyuan.com/viewthread.php?tid7923 C的一些特性好久没使用了&#xff0c;导致有些生疏了&#xff0c;所以查了…

工作和异地,都是生活的考验

12年毕业的我&#xff0c;应该没有人比我更懂异地恋了。12年毕业拿了一份上海的ARM底层开发offer&#xff0c;薪资不算高&#xff0c;不过我们那一年竟没有一个拿到比上一届师兄薪资好的offer&#xff0c;我那时心里郁郁发闷&#xff0c;女朋友那时候考公务员&#xff0c;我们没…

strdup与strcpy具体的区别

我分别在XP的环境和linux环境下编译运行C代码&#xff0c;我发现一个不解的现象&#xff1a; 现象描述&#xff1a; 两个字符串 char* from&#xff0c; char* to;要把from的内容复制给to。 我在XP下&#xff0c;用strcpy&#xff08;to&#xff0c; from&#xff09;出现memor…

go语言基础之工程管理和工作区介绍

1、工程管理 在实际的开发工作中&#xff0c;直接调用编译器进行编译和链接的场景是少而又少&#xff0c;因为在工程中不会简单到只有一个源代码文件&#xff0c;且源文件之间会有相互的依赖关系。如果这样一个文件一个文件逐步编译&#xff0c;那不亚于一场灾难。 Go语言的设计…

用临时表的GridView分页

本例子采用sql2000下的Nowthwind数据库中的[Order Details]表 下面是存储过程脚本 Code1ALTER PROC OrderDetailsPaging 2(PageIndex int,--页码 3 PageSize int,--页尺寸 4 RowsCount int output)--总行数 5AS 6BEGIN 7set nocount on 8declare PageLowerBound int 9declar…

HTML与CSS(图解6):超链接

动态的超链接&#xff1a; <html> <head> <title>动态超链接</title> <style> <!-- body{background:url(bg9.gif); /* 页面背景图片 */margin:0px; padding:0px;cursor:pointer; /*意思就是鼠标指针变成 手 的形状&#xff0c;和放到链…

pointcut 切面表达式 切入点表达式

下面给出一些常见切入点表达式的例子。 任意公共方法的执行&#xff1a; execution(public * *(..)) 任何一个以“set”开始的方法的执行&#xff1a; execution(* set*(..)) AccountService 接口的任意方法的执行&#xff1a; execution(* com.xyz.service.AccountService.*…

keil分散加载文件浅析

什么是分散加载文件分散加载文件&#xff08;scatter file&#xff09;是一个文本文件&#xff0c;它的作用是可以用于描述 ARM 链接器生成映像文件所需要的信息。如果不使用 scatter file 文件来指定&#xff0c;那么 ARM 链接器会按照默认的方式来生成映像文件&#xff0c;但…