(19):事件的接受与忽略
  TEZNKK3IfmPf 2023年11月15日 97 0


上一章我们介绍了有关事件的相关内容。我们曾经提到,事件可以依情况接受和忽略。现在,我们就来了解下有关事件的更多的知识。

 

首先来看一段代码:

//!!! 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 的事件对象有两个函数:​​accept()​​​和​​ignore()​​​。正如它们的名字一样,前者用来告诉 Qt,这个类的事件处理函数想要处理这个事件;后者则告诉 Qt,这个类的事件处理函数不想要处理这个事件 。在事件处理函数中,可以使用​​isAccepted()​​​来查询这个事件是不是已经被接收了。具体来说:如果一个事件处理函数调用了一个事件对象的​​accept()​​函数,这个事件就不会被继续传播给其父组件;如果它调用了事件的​​ignore()​​函数,Qt 会从其父组件中寻找另外的接受者。

 

事实上,我们很少会使用​​accept()​​​和​​ignore()​​函数,而是像上面的示例一样,如果希望忽略事件,只要调用父类的响应函数即可。

 

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

class CustomButton : public QPushButton
{
Q_OBJECT
public:
CustomButton(QWidget *parent) : QPushButton(parent)
{
}
protected:
void mousePressEvent(QMouseEvent *event)
{
qDebug() << "CustomButton";
}
};

class CustomButtonEx : public CustomButton
{
Q_OBJECT
public:
CustomButtonEx(QWidget *parent) : CustomButton(parent)
{
}
protected:
void mousePressEvent(QMouseEvent *event)
{
qDebug() << "CustomButtonEx";
}
};

class CustomWidget : public QWidget
{
Q_OBJECT
public:
CustomWidget(QWidget *parent) : QWidget(parent)
{
}
protected:
void mousePressEvent(QMouseEvent *event)
{
qDebug() << "CustomWidget";
}
};

class MainWindow : public QMainWindow
{
Q_OBJECT
public:
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​​。事件的传播是在组件层次上面的,而不是依靠类继承机制。

 

在一个特殊的情形下,我们必须使用​​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”,则接受关闭事件,这个事件所在的操作就是关闭窗口。因此,一旦接受事件,窗口就会被关闭;否则窗口继续保留。当然,如果窗口内容没有被修改,则直接接受事件,关闭窗口。

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月15日 0

暂无评论

推荐阅读
  TEZNKK3IfmPf   19天前   43   0   0 java
  TEZNKK3IfmPf   2024年05月31日   54   0   0 java
TEZNKK3IfmPf