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”,则接受关闭事件,这个事件所在的操作就是关闭窗口。因此,一旦接受事件,窗口就会被关闭;否则窗口继续保留。当然,如果窗口内容没有被修改,则直接接受事件,关闭窗口。
QT事件的接受与忽略相关推荐
- Qt 事件机制,底层实现原理
[1]事件 事件是可以被控件识别的操作.如按下确定按钮.选择某个单选按钮或复选框. 每种控件有自己可识别的事件,如窗体的加载.单击.双击等事件,编辑框(文本框)的文本改变事件等等. 事件就是用户对窗口 ...
- Qt事件体系概述(The Event System)
本文译自https://doc.qt.io/qt-5/eventsandfilters.html,是意译. 目录 事件的发送(How Events are delivered) 事件类型(Event ...
- Qt 事件机制源码分析 QApplication exec 源码分析 多图超级详细
前言: 不熟悉qt 源码结构的 可以先看这一篇 点我点我点我 写qt 的都知道 以下代码, 这段代码究竟的运行机制是怎么样的 咱们一步一步的看 QApplication a(argc, argv);Q ...
- 【转】QT事件传递与事件过滤器
[概览] 1.重载特定事件函数. 比如: mousePressEvent(),keyPressEvent(), paintEvent() . 2.重新实现QObject:: ...
- 210322阶段三QT事件循环及opencv图像处理
目录 一.学习的知识点 1.QT事件循环机制 1.1QT是事件驱动的.QT将系统产生的信号(软件中断)转换成QT事件,并且将事件封装成类,所有的事件类都是QEvent派生的,事件的产生和处理就是QT程 ...
- QT事件事件之一:Qt中的事件处理与传递
QT事件事件之一:Qt中的事件处理与传递 前言 一.简介 二.QT中的事件 三.事件的实现的方法 前言 在QT中,事件是我们很常用的东西,以下是我用事件时总结和做法 一.简介 在QT中,事件作为一个对 ...
- Qt事件耦合器实现(类似C#的Prism中的事件耦合器)
该耦合器提供了一种事件机制,可以实现应用程序中松散耦合组件之间的通信.这种机制基于事件聚合器服务,允许发布者和订阅者通过事件进行通信,并且彼此之间仍然没有直接引用.简单来说就是提供了一个多播发布/订阅 ...
- Qt事件重写实现按住左键拖动移动控件,bool eventFilter(QObject *watched, QEvent *event)
Qt事件重写实现按住左键拖动移动控件 1. 遇到的问题:我们需要在一个界面中实现控件(如按钮,标签等)的拖动,怎么办呢? 2. 很简单:只需要在需要实现的控件类中重写`eventFilter`即可 3 ...
- QT入门第七天 网络编程TCP/IP/UDP+Http和JSON解析+qt事件软键盘【CSDN最详细】
网络编程+Http和JSON解析+qt事件软键盘 第一章 QT中的网络编程 [1]涉及到的类 [2]tcp协议的流程 [2.1]服务器的流程 socket-->bind-->listen- ...
最新文章
- RSA加密解密在jsencrypt+c#的实现-博客园加密登录
- shell中的命令替换和变量替换
- 前端校招准备--HTTP相关面试题总结
- VS2013中提示:没有可放置在工具箱上的组件
- input change获取改变之前的值和改变之后的值_使用Vue3.0新特性造轮子 WidgetUI3.0 (Input输入框组件)
- 《智能家居》培训第六天------2019-01-10
- SpringCloud学习笔记(6)----Spring Cloud Netflix之负载均衡-Ribbon的使用
- 每天一点Swift(五)控制器的生命周期和SizeClass
- 给爱设计的你,一个理由爱上(和使用)渐变素材的理由
- layUI提交多个相同name的值
- Bookshelf 2 POJ - 3628(01背包||DFS)
- 【优化算法】天牛须搜索优化粒子群算法【含Matlab源码 1256期】
- 学计算机的学后感,计算机学习心得体会(通用10篇)
- lammps教程:EAM势函数设置详解
- 如何用计算机算tan2,arctan计算器(万能计算器在线使用)
- Coverity软件下载安装使用试用
- 小白看看!本人自学Python编程经验分享。
- python修改文件的某一行_简单文件操作python 修改文件指定行的方法
- markdown生成chm电子书或在线文档
- YOLOV3 config理解
热门文章
- Dapper的基本使用
- 简单解释什么是 依赖注入 和 控制反转
- AngualrJS之服务器端通信
- Create Volume 操作(Part I) - 每天5分钟玩转 OpenStack(50)
- 【RAC】How to Proceed from Failed 11gR2 CRS Installation
- 一个跳楼博士生的遗书:这个世界是一沟绝望的死水
- 2018双一流排名 计算机,2018中国双一流高校名单
- android自定义金额输入键盘_Android 自定义控件 - 仿支付宝数字键盘
- mysql正在运行安全文件怎么办_MySQL服务器运行的安全文件化选项,所以它不能执行该语句什么情? 爱问知识人...
- python xgboost安装_win7 64 python2 xgboost安装