网站首页 > 博客文章 正文
上一章我们介绍了有关事件的相关内容。我们曾经提到,事件可以依情况接受和忽略。现在,我们就来了解下有关事件的更多的知识。
首先来看一段代码:
//!!! 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();
}
这是一段简单的代码,经过我们前面一段时间的学习,我们已经能够知道这段代码的运行结果:点击按钮,会在控制台打印出 “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);
}
}
...
我们重写了 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() 函数的实现:
【领QT开发教程学习资料,点击→「链接」←莬费领取,先码住不迷路~】
//!!! 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();
}
}
}
这段代码在 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";
}
};
这段代码在一个 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事件与信号槽)
- 下一篇: Qt如何模拟鼠标点击?(qt 模拟鼠标点击)
猜你喜欢
- 2024-09-29 Qt的6个简单小案例(qt官方例子)
- 2024-09-29 Qt入门阶段之事件(qt入门阶段之事件是什么)
- 2024-09-29 Qt编写安防视频监控系统9-自动隐藏光标
- 2024-09-29 Qt为什么站稳c++GUI的top1(qt quuid)
- 2024-09-29 Qt5 事件(event)机制详解(qt事件是通过什么实现的)
- 2024-09-29 Qt 保持GUI响应的几种方法(qt保存快捷键)
- 2024-09-29 Qt如何模拟鼠标点击?(qt 模拟鼠标点击)
- 2024-09-29 「VTK」「DICOM」修改后的VTK sample code
- 2024-09-29 Qt事件(qt事件与信号槽)
- 2024-09-29 Qt事件总结(不良事件分析总结报告)
你 发表评论:
欢迎- 最近发表
-
- 给3D Slicer添加Python第三方插件库
- Python自动化——pytest常用插件详解
- Pycharm下安装MicroPython Tools插件(ESP32开发板)
- IntelliJ IDEA 2025.1.3 发布(idea 2020)
- IDEA+Continue插件+DeepSeek:开发者效率飙升的「三体组合」!
- Cursor:提升Python开发效率的必备IDE及插件安装指南
- 日本旅行时想借厕所、买香烟怎么办?便利商店里能解决大问题!
- 11天!日本史上最长黄金周来了!旅游万金句总结!
- 北川景子&DAIGO缘定1.11 召开记者会宣布结婚
- PIKO‘PPAP’ 洗脑歌登上美国告示牌
- 标签列表
-
- ifneq (61)
- messagesource (56)
- aspose.pdf破解版 (56)
- promise.race (63)
- 2019cad序列号和密钥激活码 (62)
- window.performance (66)
- qt删除文件夹 (72)
- mysqlcaching_sha2_password (64)
- ubuntu升级gcc (58)
- nacos启动失败 (64)
- ssh-add (70)
- jwt漏洞 (58)
- macos14下载 (58)
- yarnnode (62)
- abstractqueuedsynchronizer (64)
- source~/.bashrc没有那个文件或目录 (65)
- springboot整合activiti工作流 (70)
- jmeter插件下载 (61)
- 抓包分析 (60)
- idea创建mavenweb项目 (65)
- vue回到顶部 (57)
- qcombobox样式表 (68)
- vue数组concat (56)
- tomcatundertow (58)
- pastemac (61)
本文暂时没有评论,来添加一个吧(●'◡'●)