首页 Qt 学习之路 2 Qt 学习之路 2(19):事件的接受与忽略

Qt 学习之路 2(19):事件的接受与忽略

169 13.7K

版本:

  1. 2012-09-29
  2. 2013-04-23 更新有关accept()ignore()函数的相关内容。
  3. 2013-12-02 增加有关accept()ignore()函数的示例。

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

首先来看一段代码:

//!!! 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);
    }
}
...

我们重写了CustomButtonmousePressEvent()函数,也就是鼠标按下。在这个函数中,我们判断如果鼠标按下的是左键,则打印出来“left”字符串,否则,调用父类的同名函数。编译运行这段代码,当我们点击按钮时,“You clicked this!”字符串不再出现,只有一个“left”。也就是说,我们把父类的实现覆盖掉了。由此可以看出,父类QPushButtonmousePressEvent()函数中肯定发出了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的默认实现。这一点我们前面已经说明。下面可以从代码级别来理解这一点,我们可以查看一下QWidgetmousePressEvent()函数的实现:

//!!! 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(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,里面有两个按钮对象:CustomButtonCustomButtonEx。每一个类都重写了mousePressEvent()函数。运行程序点击 CustomButtonEx,结果是

CustomButtonEx

这是因为我们重写了鼠标按下的事件,但是并没有调用父类函数或者显式设置accept()ignore()。下面我们在CustomButtonExmousePressEvent()第一行增加一句event->accept(),重新运行,发现结果不变。正如我们前面所说,QEvent默认是accept的,调用这个函数并没有什么区别。然后我们将CustomButtonExevent->accept()改成event->ignore()。这次运行结果是

CustomButtonEx
CustomWidget

ignore()说明我们想让事件继续传播,于是CustomButtonEx的父组件CustomWidget也收到了这个事件,所以输出了自己的结果。同理,CustomWidget又没有调用父类函数或者显式设置accept()ignore(),所以事件传播就此打住。这里值得注意的是,CustomButtonEx的事件传播给了父组件CustomWidget,而不是它的父类CustomButton事件的传播是在组件层次上面的,而不是依靠类继承机制。

接下来我们继续测试,在CustomWidgetmousePressEvent()中增加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”,则接受关闭事件,这个事件所在的操作就是关闭窗口。因此,一旦接受事件,窗口就会被关闭;否则窗口继续保留。当然,如果窗口内容没有被修改,则直接接受事件,关闭窗口。

169 评论

Anonymous 2012年10月2日 - 15:58

我一直不清楚一个问题虽然和文章没什么关系,那就是规避调用kdelib的话,qt应用跑在gnome和xfce等gtk+驱动的桌面上效果会不会很成问题,或至少能不能运行。如果可以运行,编译的时候有什么额外要注意或设置的吗?

回复
DevBean 2012年10月2日 - 19:17

你是说不调用 kdelib 的 API?如果仅使用 Qt API 的话,只要版本正确,gnome、xfce 等环境下都是可以正常运行的,不存在效果的问题。如果仅只用 Qt API,只要重新编译一下,如果运行环境相同,甚至可以不需要编译。

回复
Anonymous 2012年10月2日 - 20:56

多谢回答。

回复
li 2016年5月1日 - 14:21

您好,为什么我会出现这种问题
main.obj:-1: error: LNK2019: 无法解析的外部符号 "public: __thiscall CustomButton::CustomButton(class QWidget *)" (??0CustomButton@@QAE@PAVQWidget@@@Z),该符号在函数 _main 中被引用

回复
li 2016年5月1日 - 14:26

执行qmake后就可以了,谢谢哈

回复
李振宇 2022年1月12日 - 15:17

你好,请教一下,使用event->ignore()时出现:cannot initialize object parameter of type QEvent with an expression of type QMouseEvent,这个应该怎样解决?

回复
tony 2012年10月6日 - 17:49

请解释一下:
connect(textEdit, &QTextEdit::textChanged, [=]() {
this->setWindowModified(true);
});中的[=]()是什么意思?

回复
DevBean 2012年10月6日 - 20:20

这个是 C++11 新增的 Lambda 表达式的语法,详细请参考 http://www.devbean.info/2012/05/cpp11-lambda/

回复
xuefu 2013年3月14日 - 23:07

应该是.net吧。。。.info进不去(404!)...

回复
豆子 2013年3月15日 - 09:26

这是哪里的?因为改过一次域名,所以可能有疏漏。

回复
渡世白玉 2013年4月16日 - 13:29

山东联通的nds没有这个网站的解析,我都是用毛豆或谷歌的dns才能正常进入、、

longchisihai 2012年12月30日 - 22:52

onButtonCliecked()声明的时候应为private slots?我这边用的Qt4 如果不这样写 不会输出 “You clicked this!”字符串。

回复
DevBean 2012年12月31日 - 10:03

是这样的,Qt 4 要声明 slots(不过 public 也是可以的),因为这段代码前面已经注明是 Qt5 的,所以没有指出(Qt5 是不需要声明的)。见谅见谅

回复
longchisihai 2012年12月31日 - 13:43

我这边使用的Qt4。对于槽onButtonClicked,只能接收鼠标左键发出的信号还是左键右键的信号都能接收?如果信号都能接收,
不添加事件函数mousePressEvent时,为什么只有单击左键才能显示You clicked this!字符串,右键不显示此字符串?
如果只能接收鼠标左键,当添加事件函数之后 添加else if (event->button() == Qt::RightButton)
{
qDebug() << "right";
}
单击鼠标右键也能输出字符串“right”
求解~~

回复
DevBean 2012年12月31日 - 14:19

信号只有左键单击时才会触发。这一点可以用代码测试得出,也可以查看 Qt 源代码。你疑问的原因是,把信号同 mousePressEvent() 混为一谈了。信号是左键单击时发出,事件是左右键都会监听到。查看源代码即可得知,Qt 在监听到左键单击时才会发出 clicked() 信号,右键单击时会进入事件处理,因此会输出 right。

回复
xuefu 2013年3月15日 - 20:02

赞一个。。。正好想问这个问题。。。结果一看下面的提问,豆子已经说得这么详细了。。。给力。。。

回复
cwq 2013年1月25日 - 16:07

请问一下,我想实现QSlider滑块移动到鼠标点击的位置,重写了mousePressEvent可以实现,但是滑块却不能拖动了,不知道问题出在哪里,没有头绪额 😕

回复
豆子 2013年1月25日 - 16:40

重写过之后有没有调用父类的实现呢?并且,你说的这个内容,原始的实现应该就满足的吧

回复
JiaPan 2013年1月30日 - 20:20

connect(textEdit, &QTextEdit::textChanged, [=]() {
this->setWindowModified(true);
这一段在WIN下不报错,在Linux下报错为什么呢?GCC、G++已升级之4.7版本。

回复
豆子 2013年1月31日 - 15:43

这个我没有使用 GCC 测试,是什么错误呢?

回复
JiaPan 2013年1月31日 - 20:08

一个警告,一个错误分别是:
/home/jiapan/Source/qt/action/mainwindow.cpp:32: 警告:lambda expressions only available with -std=c++11 or -std=gnu++11 [enabled by default]

/home/jiapan/Source/qt/action/mainwindow.cpp:32: 错误:no matching function for call to 'MainWindow::connect(QTextEdit*&, void (QTextEdit::*)(), MainWindow::MainWindow(QWidget*)::)'

回复
great 2013年2月1日 - 16:09

pro文件内加上一句
CONFIG+=c++11

回复
豆子 2013年2月5日 - 15:05

应该是 great 提出的解决方案吧,试试看看

回复
春云者 2017年5月6日 - 08:21

gcc4.7不支持c++11标准。从gcc4.8才开始部分支持,4.9才开始全部支持。所以使用lambda表达式还是先升级一下编译器版本吧!

回复
qingxp9 2013年4月22日 - 21:25

有点不明白
accept() 和 ignore()。前者用来告诉 Qt ,这个类的事件处理函数想要这个事件;后者则告诉 Qt ,这个类的事件处理函数不想要这个事件。无论是 accept() 还是 ignore(),该类不想要的事件都会被继续发送
给其父组件。------不是只有ignore才不想要吗,那有accept什么事?
还有就是:“如果我们在子类中直接忽略事件,Qt 将不会再去寻找其他的接收者,那么父类的操作也就不能进行” 刚不是说会继续发给其父组建吗?(因为是不想要的事件)

回复
豆子 2013年4月23日 - 10:08

文章已经更新过,希望这次可以明白一些 ;-P

回复
qingxp9 2013年4月23日 - 22:35

"如果它调用了事件的 ignore() 函数,Qt 会从其父组件中寻找另外的接受者。

如果我们在子类中直接忽略事件,Qt 将不会再去寻找其他的接收者,那么父类的操作也就不能进行,这可能会有潜在的危险。"

前者的ignore()与后者的"忽略"不是同一个东西吗?如果是同样的东西,那为什么后者不会寻找其他接受者,不是应该按前者说的从其父组件中寻找其他接受者吗? 你看看我哪弄混淆了。

回复
豆子 2013年4月24日 - 09:11

这段写的的确有问题。现在已经更正过来,主要的意思说,自己不要去调用 accept() 和 ignore() 函数,而是调用父类的实现。

回复
qingxp9 2013年4月26日 - 20:14

这次完全了解清楚了 本来大概明白的,被那几句弄得有点晕,谢了

豆子 2013年4月27日 - 09:00

非常抱歉哦~

hysteria 2013年12月2日 - 18:25

我觉得前者的ignore和后者的“忽略”不是同一个东西。
引用豆子的一句话”通过调用父类的同名函数,我们可以把 Qt 的事件传递看成链状:如果子类没有处理这个事件,就会继续向其父类传递。”
想象成链表的话,ignore是让这个链表继续。“忽略”是因为没调用父类的同名函数,所以链表断掉了。这是我个人理解。。

回复
Alien You 2017年4月30日 - 22:43

今天我大概琢磨了一下,accept()应该是如果先对子类判断是否有同名函数的实现,如果有则进行并且中断事件的传播,若子类没有实现,则调用父类同名函数,以此类推。ignore()的话应该是说,当前忽略该事件,并把事件传播给父组件,由父组件传播给其他的接收者(继承父组件的部件),我大概的理解是这样,不知道有没有偏差

Jakes 2013年4月27日 - 23:51

[*] 这种语法? 为什么会在窗口更改时自动换为*呢?能否具体讲一下这里?

回复
豆子 2013年4月28日 - 09:07

[*] 是 Qt 提供的一种占位符,不是新的语法。底层上应该是 Qt 查询字符串进行替换,没有什么特别之处。

回复
guiji16 2013年5月30日 - 15:35

注意函数的第一个语句:event->ignore();,如果子类都没有重写这个函数,Qt 会默认忽略这个事件,不会继续传播。 event->accept()??
对于 QCloseEvent 事件,调用 accept() 意味着 Qt 会停止事件的传播,调用 ignore() 则意味着事件继续传播。

回复
Rice 2013年9月23日 - 14:50

關於"guiji16"要問的問題
應該是指下面兩段話似乎矛盾
我也存在相同疑問
還請豆子大大說明
謝謝

第四段:
"具体来说:如果一个事件处理函数调用了一个事件对象的 accept() 函数,这个事件就不会被继续传播给其父组件;如果它调用了事件的 ignore() 函数,Qt 会从其父组件中寻找另外的接受者。"
(accept()事件就不会被继续传播)

第六段:
"注意函数的第一个语句:event->ignore();,如果子类都没有重写这个函数,Qt 会默认忽略这个事件,不会继续传播。"
(ignore()忽略这个事件,不会继续传播。)

回复
豆子 2013年9月24日 - 19:16

这里的描述应该是,accept() 会停止事件继续传播;ignore() 会使 Qt 寻找下一个事件接收者,也就是继续传播。所以原文第六段应该是有错误的。

回复
enockipp 2013年9月15日 - 21:22

第二遍看这部分,看明白了什么意思,其实博主后面的解释就是想说“到底是想调用父组件还是父类相应的事件处理函数吧”

回复
hysteria 2013年12月1日 - 16:26

如果我们在子类的mousePressEvent()函数中直接调用了accept()或者ignore(),而没有调用父类的同名函数,QWidget::mousePressEvent()函数中关于Popup判断的那段代码就不会被执行,因此可能会出现默认其妙的怪异现象。

为什么关于Popup判断的那段代码就不会被执行,程序执行到QWidget::mousePressEvent()的ignore的语句会跳出这个函数吗?如果是这样,那是不是说ignore是即时生效的,就是说,当调用ignore的时候,程序会跳出这个函数不执行下面的代码以及下次不再光顾这个类了?如果不是即时生效,那为什么下面的语句不会被执行?子类调用accept而没有调用父类的同名函数,这样就连QWidget::mousePressEvent()函数都不会执行把?如果,子类调用ignore而没有调用父类的同名函数(ignore不是即时生效的情况下)因为子类告诉程序,下次我不需要你的到来了,这样也会把下面的代码执行完,然而,由于子类调用了ignore,QT继而去寻找父类的接受者,这样QWidget::mousePressEvent()也会被执行。。还是说,等到下次事件到来的时候,QT才去寻找父类的接收者?我理解能力较差,望各位帮助。。

回复
hysteria 2013年12月1日 - 16:36

还有两句话不能理解
1、事实上,我们很少会使用accept()和ignore()函数,而是像上面的示例一样,如果希望忽略事件(所谓忽略,是指自己不想要这个事件)
2、如果我们在子类中直接忽略事件,Qt 会去寻找其他的接收者,该子类的父类的操作会被忽略(因为没有调用父类的同名函数),这可能会有潜在的危险。

第一句话的“自己”指的是类还是程序?如果指的是类,那么我们是不是可以不重写那个重名函数吗?这样就忽略这个事件了?
第二句话的直接忽略事件,指的是不重写父类的重名函数(也就是子类没有这个事件的响应函数),还是重写了父类的重名函数而函数里面没有调用父类的同名函数?如果是跟我说的一样,不重写父类的重名函数,导致父类的操作(没有调用父类的同名函数)忽略而存在隐患,这样的意思是不是说,我们要把父类的每个响应函数都重写一遍?

回复
豆子 2013年12月2日 - 09:21

1. “自己”指的是子类。确切的说,如果你不关心某一个事件,只要不重写就好了。
2. “忽略事件”指的是重写父类的同名函数并且调用了 ignore() 函数。由于父类的同名函数一般会调用 ignore() 函数,因此大致相当于调用父类同名函数。

回复
豆子 2013年12月2日 - 10:41

事件处理是一个逐层调用的过程:先调用子类 Button 的,然后一层一层向上层父类调用,最后直到最顶层父类 QObject 或者 QWidget(鼠标等 GUI 事件只有 QWidget 及其子类才能处理)。所谓忽略,只是阻断了这个事件的传播过程,并不影响当前函数的执行。

回复
付强 2013年12月3日 - 20:32

豆子,
为什么以下代码:
void CustomButton::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
QPushButton::mousePressEvent(event);
} else {
qDebug() <button() == Qt::LeftButton) {
qDebug() << "left";
} else {
QPushButton::mousePressEvent(event);
}
}
点击左键有输出:left
但是点击右键无输出;怎么理解?

回复
付强 2013年12月3日 - 20:44

前面表述错了,是这样的:
void CustomButton::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
QPushButton::mousePressEvent(event);
} else {
qDebug() <button() == Qt::LeftButton) {
qDebug() << "left";
} else {
QPushButton::mousePressEvent(event);
}
}
左击输出:left! 右击无反应;
这个 理解不了;为什么这个eventQPushButton::mousePressEvent没有处理?

回复
付强 2013年12月3日 - 20:51

还是显示不全,代码贴在这里了:
http://paste.ubuntu.com/6514314/

回复
豆子 2013年12月3日 - 21:20

左键点击之后,进入mousePressEvent()函数,然后输出 left。当点击右键时,调用父类实现,默认父类实现就是忽略这个事件,所以没有任何反应。这就是Qt正常的事件处理。

回复
付强 2013年12月3日 - 20:46

奇怪,我在留言的时候写的 和 网页上显示的不一样额。丢失了好多字;

回复
付强 2013年12月3日 - 21:16

我看懂了,楼主;还是上面的评论里面的,就是事件监听的是按键,而信号clicked的发出是左键。 继续学习了,打扰豆子了。。 不过发现一个额外的问题,是不是回复有字数限制?超出文字的中间没了。像我上面那几条评论样。

回复
xiaoziwen 2013年12月18日 - 16:43

有个问题

ignore不是忽略这个事件了嘛
忽略了为啥后面的qDebug还是可以输出
这样怎么叫忽略嘛 仅仅是把事件又发出去给父组件了嘛 后面的代码还是要执行嘛

求解释 谢谢

回复
豆子 2013年12月18日 - 17:04

所谓“忽略”,是指自己不想要这个事件,要求 Qt 把事件继续传播给父组件。忽略并不是终止函数执行,其影响的是事件的传播路径。这是 Qt 的命名方式,你也可以把它称为stopEvent Propagation()之类,只不过 Qt 就是用 ignore 这个词。

回复
sgnannan 2014年1月8日 - 22:57

豆子,我初学的,我把你上面的代码copy到qt上,结果运行时出现好多无法解析的外部符号,不知道为什么?烦请解答下

回复
豆子 2014年1月9日 - 16:47

无法解析外部符号,可能是因为添加了 Q_OBJECT 宏之后没有重新运行 qmake,也可能是其它函数没有相应实现。先重新构建一下试试,如果不行,要看具体是缺少哪些函数。

回复
2014年2月18日 - 15:45

这暗示我们一个非常重要的细节:当重写事件回调函数时,时刻注意是否需要通过调用父类的同名函数来确保原有实现仍能进行!比如我们的CustomButton了,如果像我们这么覆盖函数,clicked()信号永远不会发生,你连接到这个信号的槽函数也就永远不会被执行。

这段,好像有问题。这是因为CustomButtion::mousePressEvent 重写了父类的mousePressEvent,导致无法执行emit clicked的方法。跟event的accept和ignore无关吧?

回复
豆子 2014年2月18日 - 17:16

是的,这是由于函数覆盖产生的问题。所以在后面的“忽略”一词是添加了引号的。

回复
2014年2月19日 - 09:46

对于QCloseEvent事件,调用accept()意味着 Qt 会停止事件的传播,调用ignore()则意味着事件继续传播。
.......
因此,一旦接受事件,窗口就会被关闭;否则窗口继续保留。

这里是什么意思呢
上面的例子里MainWindow应该没有父组件,就是说无所谓accept和ignore需不需要传播了。那么我的理解应该是,事件都无法传播了,这里的含义应该只是,是否接受关闭而已。
请指教

回复
豆子 2014年2月19日 - 10:11

个人认为这里可以这样理解。另外值得说明一下,QMainWindow 也可能有父组件,比如将其放在 MDI 的子窗口里面(尽管看上去不应该这么做,但是的确可以这么做,好处是可以在子窗口直接利用 QMainWindow 固定好的布局,比如工具条、菜单栏之类)。

回复
大灰狼嘎嘎 2014年3月2日 - 18:15

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

豆哥,你是不是弄混了? ignore是一旦忽略,就传递给父组件,而不是父类吧。。

QtHelp中QEvent::ignore()说明:
event receiver does not want the event. Unwanted events might be propagated to the parent widget.

回复
大灰狼嘎嘎 2014年3月2日 - 21:28

看错了。。忽略我吧。。

回复
Const_Lin 2014年3月7日 - 15:08

豆子你好。
“由此可以看出,父类QPushButton的mousePressEvent()函数中肯定发出了clicked()信号,否则的话,我们的槽函数怎么会不执行了呢?”
这段话看不是很懂。

回复
豆子 2014年3月10日 - 10:17

我们重写了 mousePressEvent() 函数,此时 clicked 信号对应的 slot 没有被执行,这意味着我们的重写导致了这也问题。之所以槽函数没有被执行,是因为信号没有发出,这样分析下来,也就是说,我们的重写导致了信号没有被发出,反过来可以推断出,clicked 信号的发出是在 mousePressEvent 函数中。

回复
Const_Lin 2014年3月7日 - 23:02

豆子你好
“对于QCloseEvent事件,调用accept()意味着 Qt 会停止事件的传播,调用ignore()则意味着事件继续传播。这与前面所说的正好相反”
这句话让我很是困惑,感觉对于对于QCloseEvent事件,accept()和ignore()感觉和前面所说的一样。

回复
豆子 2014年3月10日 - 10:48

这个可能是个人理解问题,先不要纠结,理解就好了吧 ;-P

回复
橘子 2014年4月1日 - 15:36

“由此可以看出,父类QPushButton的mousePressEvent()函数中肯定发出了clicked()信号,否则的话,我们的槽函数怎么会不执行了呢?”
文中这句,是否该改成“肯定没发出clicked()信号”,因为后面也提到了“比如我们的CustomButton了,如果像我们这么覆盖函数,clicked()信号永远不会发生”

回复
豆子 2014年4月2日 - 22:44

这里意思是 QPushButton 的 mousePressEvent 发出了 clicked 信号。由于我们覆盖了父类实现,没有手动发出 clicked 信号,结果这个信号就不见了,由此推断是原来父类的 mousePressEvent 应该发出了 clicked 信号。

回复
ksn13 2014年5月8日 - 10:20

豆哥,我QT4,为什么注册了clicked信号,和mousePressEvent()事件以后。点击鼠标的时候只相应mousePressEvent()函数。而“You clicked this!” 不打印了?
这是什么原理,如何才能都有打印?谢谢

回复
豆子 2014年5月8日 - 14:48

你重写了 mousePressEvent() 函数之后,如果没有调用父类的实现,clicked 信号没有发出,所以对应的槽函数就没有执行了。

回复
darkhandz 2014年5月10日 - 19:50

咋示例里 onButtonCliecked 要加多个"e"? 直接用 onButtonClicked 不好吗?

回复
darkhandz 2014年5月11日 - 12:08

我又来当孔乙己了……
CustomButton *cbex = new CustomButton(widget);
cbex->setText(tr("CustomButton"));
CustomButtonEx *cb = new CustomButtonEx(widget);
cb->setText(tr("CustomButtonEx"));
感觉上面两个指针的名字互换一下比较符合类名呀……

回复
pubemail 2014年5月31日 - 20:05

我觉得对于QCloseEvent的响应和析构函数有关,因为毕竟close这个事件是要销毁回收对象的。
我建立了三个独立的对象
MainWindow w;
w.show();
MainWindow ww;
ww.show();
CustomTextEdit editor;
editor.show();

在一个自定义texteditor里这样做
bool CustomTextEdit::event(QEvent *e){
e->ignore();
//e->accept();
return true;
}

如果ignore()那么texteditor窗口永远无法正常关闭,而其它窗口可以一个一个点叉关闭。如果accept()那么所有窗口都可以正常关闭。所以可能的情况是,如果ignore()那么析构函数就不执行,也不再执行父类的析构函数;如果accept()那么正常执行析构。(返回值true和false都是这样的情况)

还有奇怪的是,在CustomTextEdit::event里ignore(),按cmd+q(OS X 10.9),其它窗口时而可以关闭,时而不能关闭(texteditor不能关闭),貌似和这个窗口是否激活过有关。(初学者,理解不深)

回复
pubemail 2014年5月31日 - 20:11

话说是怎么获得我的头像的,我也没有在wordpress注册过

回复
豆子 2014年6月3日 - 09:26

这个头像是直接从 gravatar 获取的

回复
rainc 2014年6月14日 - 23:29

关于这个ignore()函数,既然代表忽略了这个事件,为什么还是可以输出
CustomButtonEx
CustomWidget
MainWindow
也就是既然忽略了这个事件,怎么还会调用这个事件处理函数呢?

回复
豆子 2014年6月18日 - 11:23

ignore()函数意味着自己不想处理这个事件,是指自己忽略事件,而让事件继续传播,由其它组件处理。所以才会输出下面的信息。

回复
shandy 2016年10月17日 - 17:25

rainc 问的应该是为什么已经忽略了,还会执行当前事件处理函数的下面的代码,这也引出了我想问的一个问题,既然不想处理这个时间,为什么还要重写父类的时间处理函数,这样不是多此一举吗?

回复
Habor 2014年7月17日 - 11:52

楼主你好,我是小菜鸟一枚,我对您的第二段代码有些不解,在文中您说到“父类QPushButton的mousePressEvent()函数中肯定发出了clicked()信号”,从而clicked()信号就会触发onButtonCliecked槽。在代码是else语句中不是可以执行QPushButton的mousePressEvent()吗?那就可以触发onButtonCliecked槽呀?为什么不可以呢?

回复
Geek 2014年9月16日 - 16:08

豆子你好,我是初学者,谢谢你的教材。
下面这句话我没看懂:“也就是说,我们把父类的实现覆盖掉了。由此可以看出,父类QPushButton的mousePressEvent()函数中肯定发出了clicked()信号,否则的话,我们的槽函数怎么会不执行了呢?”
但是我想,这段代码之所以是这么个执行结果,是不是因为它的执行逻辑是这样的:
当你没有加入mousePressEvent()时,鼠标点击事件被槽函数传递给onButtonClicked()。此时,并非没有mousePressEvent(),只不过是从父类中直接继承而来,直接继承而来的成员,不用重新写一遍,所以,代码中似乎没有,但实际上却存在。点击鼠标之后,QT首先调用mousePressEvent(),mousePressEvent()里面没有写明如何处理鼠标点击事件,于是鼠标点击事件由mousePressEvent()这个event函数把点击事件发给槽函数,槽函数其实扮演着告诉event函数某个事件应该发到哪里的角色。而onButtonClicked()就是event handler。当你重写mousePressEvent()之后,你在mousePressEvent()函数里面加入如何响应鼠标点击事件的代码,于是,当你点击左键,它执行 qDebug() << "left";然后就结束了,既然代码没有写要去找connect()并发送信号,mousePressEvent()不会自动去找connect(),它执行完 qDebug() << "left";就完事了。另一分支,它会调用QPushButton::mousePressEvent(event);,父类的mousePressButton()也不会事先写明去找子类的connect(),于是,鼠标点击被忽略了。

回复
豆子 2014年9月18日 - 09:55

大致是这样一个过程。这是 C++ 的执行过程:由于 mousePressEvent() 是虚函数,所以如果子类提供了自己的实现,系统将直接调用子类的版本,不会去调用父类的版本。当我们在子类 CustomButton 重写了 mousePressEvent() 函数时,系统会使用这个版本。该版本中,鼠标左键点击时没有调用父类版本,槽函数也没有被调用。由此推断出,父类版本中发出了 clicked() 信号。

回复
jmgs 2014年12月30日 - 12:38

QT中的事件机制,就相当于Windows中的消息机制吧

回复
双人鱼 2015年1月28日 - 22:04

今天是第一天来到这里。对博主真正的敬佩啊!!!!

回复
xiaomxxx 2015年3月9日 - 11:57

//Qt4头文件
#ifndef CUSTOMBUTTON_H
#define CUSTOMBUTTON_H

#include
#include
#include
#include

class CustomButton : public QPushButton
{
Q_OBJECT

public:
CustomButton(QWidget *parent = 0);

#if 0
protected:
void mousePressEvent(QMouseEvent *event);
#endif

private slots:
void onButtonClicked();
};
#endif // CUSTOMBUTTON

//源文件
#include "custombutton.h"
#include

CustomButton::CustomButton(QWidget *parent) :
QPushButton(parent)
{
connect(this, SIGNAL(clicked()),
this, SLOT(onButtonClicked()));
}

#if 0
void CustomButton::mousePressEvent(QMouseEvent *event)
{
if(event->button() == Qt::LeftButton)
{
qDebug() << "left";
}
else
{
qDebug() << "not left";
QPushButton::mousePressEvent(event);
}
}
#endif

void CustomButton::onButtonClicked()
{
qDebug() << "you clicked this!";
}
把头文件中的宏定义Q_OBJECT去掉就能编译通过了,但是如果去掉了这个宏定义好像就不能使用信号槽了,请问我应该怎么改,谢谢豆哥。

回复
豆子 2015年3月9日 - 14:23

不知道具体是什么错误?

回复
伶仃听雨客 2015年4月13日 - 22:20

由此可以看出,父类QPushButton的mousePressEvent()函数中肯定发出了clicked()信号,否则的话,我们的槽函数怎么会不执行了呢?

烦请博主注意下,这句话有什么因果关系吗?父类的那个clicked()不是被子类CustomerPushButton重写了吗?他发出了clicked和我们的槽函数不执行 有什么逻辑上的递进关系吗?没看懂。

回复
豆子 2015年4月21日 - 12:48

由于子类重写了这个函数,导致槽未被调用;反过来说,子类不重写,槽可以被调用,因此得出推论:父类发出了 clicked() 信号

回复
Lucky 2015年4月27日 - 15:55

你好博主,我理解的意思是这样的,烦请您看看对吗,
void CustomButton::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
qDebug() << "left";
} else {
QPushButton::mousePressEvent(event);
}
}
就是说子类CustomButton中重新实现了父类QPushbutton的mousePressEvent函数,然后如果左键点击按钮,不会像重新实现之前那样发出clicked()信号,从而调用槽函数,输出“You clicked this!”,取而代之,if语句判断条件成立,就会执行“left”的输出,然而如果点击非鼠标左键(右键和中键),就会执行else语句,然后调用父类的mousePressEvent函数,然而此时我们点击的是非左键,因此我认为父类的mousePressEvent函数不会emit clicked()信号,因此点右键和中键都无输出,因为如果发出clicked()信号,那我们connect对应的onButtonCliecked槽函数应该会响应并输出“You clicked this!”对吧。
不知道我又没有表述清楚,如果哪里有说错还请您指出。

回复
豆子 2015年6月3日 - 09:59

应该就是这个意思

回复
laoreja 2015年4月14日 - 16:07

嗯。。在mac下标题栏里没有显示*?

回复
laoreja 2015年4月14日 - 16:10

嗯,在简单的那个custombutton程序中,我调用QPushButton::mousePressEvent(event); 可以实现输出两行,但是改为event->ignore(); 就没有效果,这是为什么呢?

回复
xie 2015年5月13日 - 09:37

豆子
我的是在Qt4下编译,为什么会有这样的错误"custombutton.cpp: In constructor ‘CustomButton::CustomButton(QWidget*)’:
custombutton.cpp:4:74: error: no matching function for call to ‘CustomButton::connect(CustomButton* const, void (QAbstractButton::*)(bool), CustomButton* const, void (CustomButton::*)())’
connect(this,&CustomButton::clicked,this,&CustomButton::onButtonCliecked);
"帮忙指正以下.
谢谢

回复
豆子 2015年6月3日 - 11:23

这是 Qt 5 的新语法,Qt 4 并不支持。

回复
Ycoronene 2015年9月2日 - 11:03

请问豆子老师,在这个地方:
在 CustomWidget的 mousePressEvent()中增加QWidget::mousePressEvent(event)。这次的输出是
CustomButtonEx
CustomWidget
MainWindow
//这里MainWindow不应该是是QWeight的子组件吗?为什么会这样输出呢?

回复
豆子 2015年9月2日 - 13:24

由于这个函数是虚函数,所以在用这样的语法进行调用时,会动态绑定到子类的实现,因而也就是 MainWindow 自己的实现。

回复
shandy 2016年10月17日 - 17:58

这里的子类是不是子组件,我好纠结呀,这一小节我已经看了一天了

回复
markzzz 2015年9月10日 - 14:30

豆子老师 我还是不明白“当我们点击按钮时,“You clicked this!”字符串不再出现,只有一个“left”。也就是说,我们把父类的实现覆盖掉了。” 所以是我们重写的mousePressEvent()把原有的mousePressEvent()覆盖了,所以槽函数才没有被执行? 但怎么又说“父类QPushButton的mousePressEvent()函数中肯定发出了clicked()信号”呢? 我们写的是if else, 点击左键的话,是没办法执行父类mousepressevent啊

回复
豆子 2015年9月10日 - 21:48

正是由于这个结果的出现,可以推断出信号没有发出。为什么信号没有发出呢?与普通的调用相比,因为我们没有调用父类的函数,信号才没有发出,因此说,这个信号应该是父类的函数中发出的。

回复
markzzz 2015年9月11日 - 03:31

懂了 多谢豆子老师

回复
王先先 2015年9月23日 - 16:06

事件的传播是在组件层次上面的,而不是依靠类继承机制。
============================================

豆子大神 ,我想问下 如果 事件的对象不是 在组件上 产生的,这样的话 ,他的传播是如何完成的?

回复
Crazydoudou 2015年10月29日 - 21:50

void CustomButton::mousePressEvent(QMouseEvent *event)
{
QPushButton::mousePressEvent(event);
if (event->button() == Qt::LeftButton)
qDebug() << "left";
}
豆子,如果这样写,是否左键点击,就会同时响应 槽和事件 了呢?
还有个问题想请教,QT具备了信号与槽的模式,为什么还要设计事件响应的机制?换句话讲,什么情况下多使用信号槽, 什么情况下会使用事件?

回复
豆子 2015年11月4日 - 14:06

按照你的写法,应该就是正确的做法。
至于为什么既有信号槽,又有事件机制,在 https://www.devbean.net/2012/09/qt-study-road-2-events/ 可以找到一些答案。

回复
Kane 2020年8月4日 - 16:18

试了下这种写法,为何是先打印left,后打印You clicked this!呢

回复
Kane 2020年8月4日 - 16:34

尝试断点调试这块,每次程序都崩溃。。。

回复
孙江涛 2015年11月12日 - 17:17

你好,在CustomWidget的mousePressEvent()中增加QWidget::mousePressEvent(event)。我的输出依然是CustomButtonEx和CustomWidget,并不会出现CustomWidget的父亲。
实验环境:win10+vs2012+Qt5.4
谢谢

回复
hlx1996 2015年11月25日 - 19:15

看了上面的评论还是不知道为什么:
在CustomWidget的mousePressEvent()中增加QWidget::mousePressEvent(event)或event->ignore()后结果又增加一行MainWindow。
传给QWidget了,或者直接ignore()掉,不应该直接结束了吗?为什么会多一行MainWindow。
请指教,谢谢~

回复
豆子 2015年11月26日 - 11:27

增加 QWidget::mousePressEvent() 的调用,QWidget 会将事件转发给子类,交给子类处理;调用 ignore(),说明自己不愿意处理,交给父类。因此,这两个语句都会调用 MainWindow 的相应函数。

回复
shandy 2016年10月17日 - 15:42

自己不愿意处理,不是交给父组件吗,怎么又变成父类了,上面的输出结果CustomWidget不就是这样的吗

回复
shandy 2016年10月17日 - 17:53

你这里说的子类,父类应该是子组件,父组件吧?QWidget::mousePressEvent() 是虚函数,QWidget 会将事件转发给子组件,调用他的子组件的事件处理函数, ;调用 ignore(),说明自己不愿意处理,交给父组件,CustomWidget的父组件 MainWindow处理

回复
豆子 2016年10月17日 - 22:20

这里说的不是很清楚,“交给父类”指的是调用父类的实现,应该理解为一种代码复用,即调用父类的实现,这是取决于继承关系,而不是事件的传播

回复
善良超哥哥 2015年12月3日 - 16:54

custombutton.h:3: error: C1083: 无法打开包括文件:“qpushbutton”: No such file or directory

回复
善良超哥哥 2015年12月3日 - 17:51

我用vs创建的qt工程,qDebug() << "You clicked this!"没效果,没有控制台。怎么弄

回复
善良超哥哥 2015年12月3日 - 17:57

不好意思,这个基本的问题本该自己搞定。我已经解决了

回复
yeyiliang 2015年12月10日 - 17:29

。也就是说,我们把父类的实现覆盖掉了。由此可以看出,父类QPushButton的mousePressEvent()函数中肯定发出了clicked()信号,否则的话,我们的槽函数怎么会不执行了呢?这暗示我们一个非常重要的细节:当重写事件回调函数时,时刻注意是否需要通过调用父类的同名函数来确保原有实现仍能进行!///////////////////////////////没有发出clicked()信号吧。

回复
2015年12月11日 - 20:48

关于 ignore() 我有个问题想请教一下,根据你在最后一个例子中使用 ignore 使父组件成功调用了他的 mousePressEvent 函数,也就是说 这样调用 ignore 函数 会让 父类的 mousePressEvent 函数自动调用,所以最后一个例子没有显式调用父组件的mousePressEvent函数但是还是自动调用了,但是我在第一个例子里面,如果不显示调用 QPushButton的mousePressEvent函数的话,是不会显示 “You clicked this"的,加上 ignore 也没有用,这是怎么一回事呢?

回复
2015年12月11日 - 22:10

哦,我想明白了,在使用 ignore 后,事件确实转发给父组件了,并且父组件的 mousePressEvent 确实也执行了,但是 发出 clicked 信号却不是父组件所做的工作,直接调用 QPushButton::mousePressEvent 函数这样仍然是在处理 CustomButton 的内部事件,环境仍然是在 CustomButton 里面,但是如果单纯的使用 ignore的话,那么虽然会发生事件转发导致父组件的 mousePressEvent 被调用,但是处理环境确实换到了 父组件里面,并不会发出 clicked 信号,所以必须 显示调用 QPushButton::mousePressEvent 才能发出 clicked 信号

回复
lipanlin 2015年12月13日 - 23:05

对于QCloseEvent事件,调用accept()意味着 Qt 会停止事件的传播,调用ignore()则意味着事件继续传播。
因此,一旦接受事件,窗口就会被关闭;否则窗口继续保留。
这里不太明白,accept()之后并没有做任何处理,也没有调用父类的closeEvent(),为什么就能关闭窗口呢?

回复
12 2016年1月16日 - 22:43

比如我们的CustomButton了
这里应该是
比如我们的CustomButton类

回复
豆子 2016年1月17日 - 12:05

多谢指出

回复
SHIHUA 2016年4月3日 - 00:30

豆子老师你好, 为什么我在CustomButtonEx中增加了CustomButton::mousePressEvent(event),点击CustomButton还是会出现
CustomButtonEx和CustomButton?

回复
SHIHUA 2016年4月3日 - 00:31

不是只在组件层面的传播的呀?

回复
豆子 2016年4月6日 - 21:32

CustomButton::mousePressEvent(event) 这一句是调用父类的同名函数,这与事件传播不同,只是调用了父类的实现

回复
小新 2016年5月16日 - 02:18

请问setWindowTitle("TextPad [*]"); 这句应该写在哪呀?

回复
普法居士 2016年5月19日 - 15:36

加在connect一句的下面

回复
普法居士 2016年5月19日 - 15:43

感觉这一节的内容有点像Android中的事件处理。
比如Android的touch事件,Activity中的view,重写onTouch事件,调用super.onTouch就相当于这里的customButton的mousePressEvent方法调用父类的mousePressEvent。
如果在onTouch中return true,就相当入这里的accept,事件到此结束
reture false就相当于ignore,会把处理事件传给上层控件或者Activity。

回复
司徒 2016年8月3日 - 08:58

豆子,问一个比较粗浅的问题,这里的“父组件”和“父类”有什么联系和区别?谢谢。

回复
豆子 2016年8月3日 - 09:50

父组件是这个组件所在的组件,比如一个 QDialog 里面添加了一个 QLabel,那么,QDialog 就是 QLabel 的父组件。父类是从类设计的角度来说的,比如 QLabel 继承自 QWidget,那么,QWidget 就是 QLabel 的父类。

回复
小新 2016年8月3日 - 10:15

一般组件的构造函数里都会有个一个 parent指针, 指的其实就是这个组件的父组件吧

回复
豆子 2016年8月3日 - 14:05

一般是的,不过这个 parent 的主要作用还在于在 parent 析构时,它的 children 都会自动析构,也就是一种半自动化的内存管理。所以,也有些组件会把 parent 指针设置为 MainWindow,这不是直接父组件,但就内存管理而言也是合理的。

回复
瞬间 2016年9月6日 - 17:39

dialog = new CustomDialog(this);
CustomButtonEx *cbex = new CustomButtonEx(dialog);
dialog->setAttribute(Qt::WA_NoMousePropagation);

CustomButtonEx、CustomDialog都有重写mousePressEvent(),其中,CustomButtonEx::mousePressEvent()中使用ignore();
打印结果:
CustomButtonEx.
CustomDialog.

设置Qt::WA_NoMousePropagation后,理论上mousePressEvent不再传递给父组件dialog,应该只打印CustomButtonEx.
是我哪里设置不对吗?

回复
豆子 2016年9月8日 - 10:02

如果你想要只打印 CustomButtonEx,鼠标事件应该在 CustomButtonEx 被拦截:在 CustomButtonEx 中就应该设置停止传播。因此,应该是 cbex->setAttribute(Qt::WA_NoMousePropagation);,而不是在 dialog 一层(dialog 已经是最顶层的父组件了,你在 dialog 进行拦截,只是不让事件传播到 dialog 的父组件)。

回复
bin 2016年9月7日 - 15:28

大神,你觉得“父类QPushButton的mousePressEvent()函数中肯定发出了clicked()信号”这句话是不是应该改成“如果没有重写父类QPushButton的mousePressEvent()函数,当点击鼠标左键时,父类QPushButton的mousePressEvent()函数中就会发出clicked()信号”。前面一种说法感觉挺难理解的,好像是在说槽函数没执行是因为发出了clicked()信号

回复
豆子 2016年9月8日 - 09:06

感谢指出!

回复
William 2016年10月21日 - 15:41

感谢豆子,文笔很好,自己有多年MFC基础,读起来轻松,同时学到了知识。

回复
firephoenix 2016年12月6日 - 11:09

豆子哥,谢谢分享

回复
firephoenix 2016年12月6日 - 11:15

豆子哥,想请教个问题,最近在弄个产品用到了按键,自定义了一些类,并重写了按键事件,发现父窗口可以接收到重写的按键事件但是子窗口却接收不到,想问一下是qt本来就是这样还是哪里有问题造成的,我用的是qt5

回复
Zed 2016年12月8日 - 10:42

豆子哥,我这里很奇怪的是突然出现了编译错误。。
D:\Nut\Qt\Q-19\mainwindow.cpp:15: error: extra qualification 'CustomButton::' on member 'CustomButton' [-fpermissive]
CustomButton::CustomButton(QWidget *parent)
^
其实一开始我对于这个形态就不太理解。
CustomButton::CustomButton(QWidget *parent) : QPushButton(parent)
这句话的意思是CustomButton的构造函数,以QWidget parent作为父类吗?
然后继承于QPushButton(parent)?

回复
豆子 2016年12月10日 - 16:20

这个你应该看看 C++ 的基础。CustomButton::CustomButton(QWidget *parent) : QPushButton(parent) 含义是 CustomButton 的构造函数,QWidget *作为其参数,在该构造函数执行之前需要调用 QPushButton(parent) 构造函数,而 QPushButton 才是其父类。

回复
Ollog 2017年2月14日 - 15:00

豆子大佬,最后那个记事本的例子中忽略了closeEvent,那么事件传播到父组件也没事吗?父组件如果不是widget类应该默认是accept的啊。我感觉我没理清楚事件的传播机制

回复
暴雪 2017年4月6日 - 17:54

没管三七二十一,直接打出来,先看看效果。运行之后, 错误好多。豆子, 构造函数在类内实现,加作用域解析运算符作甚。看到错误, 直接认为是事件函数有错。
有时间的话, 改过来吧。

回复
豆子 2017年4月8日 - 23:15

已经改过来了,谢谢指出

回复
Alien You 2017年4月30日 - 16:26

“Qt 会从其父组件中寻找另外的接受者。”这句话不应该是,Qt会把消息传递给其父组件吗?

回复
豆子 2017年5月6日 - 10:20

是的,应该理解成按照链式传递给父组件

回复
小浩 2017年5月5日 - 10:11

出现问题·:D:\document\CustomButton\main.cpp:9: error: 'class CustomButton' has no member named 'setText'
btn.setText("This is a Button!");
^
原主函数main.cpp代码:
#include "custombutton.h"
#include
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
CustomButton btn;
btn.setText("This is a Button!"); <这里有setText问题是怎么回事啊》
btn.show();

return a.exec();
}

回复
angon 2017年7月24日 - 23:46

根据这句话
“CustomWidget又没有调用父类函数或者显式设置accept()或ignore(),所以事件传播就此打住”
可以理解为:
调用了 accept(), 或 没有调用父类函数而且没有显式设置accept()或ignore()
这样的话传播才停止吗

回复
ben 2017年8月8日 - 16:28

楼主,倒数第2个例子你编译过吗?
我编译时会出现错误:undefined reference to `vtable for MainWin dow'

查了网上的资料,把3个类中的Q_OBJECT都注释掉,就没问题了,原因是:qmake 不会处理.cpp文件里的Q_OBJECT

回复
ben 2017年8月8日 - 16:50

楼主,看了半天对这句话还是有点不太理解:ignore()告诉Qt,这个类的事件处理函数不想要处理这个事件。
1. 既然事件处理函数不想要处理这个事件,那直接不重写不就好了?何必重写了再调用ignore(),这不是多些一举吗?
2. 如果事件处理函数中调用了ignore(),但是函数中之后的还是继续对这个事件进行处理,那这种处理会生效吗?

回复
yhw 2018年8月10日 - 12:15

由此可以看出,父类QPushButton的mousePressEvent()函数中肯定发出了clicked()信号,否则的话,我们的槽函数怎么会不执行了呢?
这句话的逻辑怎么感觉不对。。。 不应该是没有发出cliked()信号吗???

回复
豆子 2018年8月11日 - 20:29

原本的意思是想说,父类中应该发出了 clicked() 信号,如果发出了信号,槽函数就会执行,但这里槽函数没有执行,所以是没有发出 clicked() 信号。

回复
无忧无虑的兔兔 2019年5月12日 - 16:50

大佬,为什么keypressevent不能这样传递.

回复
无忧无虑的兔兔 2019年5月12日 - 17:03

我懂了,感谢!

回复
zz 2019年7月26日 - 16:17

否则,调用父类的同名函数。编译运行这段代码,当我们点击按钮时,“You clicked this!”字符串不再出现,只有一个“left”。也就是说,我们把父类的实现覆盖掉了。由此可以看出,父类QPushButton的mousePressEvent()函数中肯定发出了clicked()信号,否则的话,我们的槽函数怎么会不执行了呢?这暗示我们一个非常重要的细节:当重写事件回调函数时,时刻注意是否需要通过调用父类的同名函数来确保原有实现仍能进行!

这段话我这样理解的,如果点击的不是leftbutton 就会调用父类QPushbutton::mousePreesEvent(e);,既然调用了父类的同名函数,父类就应该可以发出clicked的信号呀,这个没理解,麻烦豆子老师解释一下谢谢

回复
zz 2019年7月26日 - 16:18

我理解的代码结果是 不是左键就会触发槽函数应该,请大佬帮我解释下谢谢

回复
ct 2019年8月13日 - 22:40

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();
}
}
这个函数应该覆盖父类的closeEvent(QCloseEvent *event)了吧,为什么还能关闭窗口?难道event自身就能关闭窗口了吗?
(都2019年了还来麻烦您,真不好意思)

回复
豆子 2019年8月16日 - 21:37

应该是这么理解吧,就是 QCloseEvent 这个事件被接受的话就会退出了

回复
燎原 2019年8月15日 - 13:49

void MainWindow::closeEvent()QCloseEvent *event
这个函数不应该覆盖了父类的closeEvent(QCloseEvent *event)了吗,为什么程序还是能关闭窗口呢?

回复
Owen 2019年10月23日 - 17:30

豆子老师您好,我根据我自己的理解对accept函数和ignore函数有一些解释,就是当一个“事件(event)”发生的时候,这个事件会开始传播,传播路径是顺着当前组件的层次结构开始传播的,是从下往上(子控件往父控件)的传播方式,然后当相应的事件函数被调用的时候,该事件函数默认是accept的,也就是说这个组件会“接收”这个事件,事件的传播就停止了,所以设置accept()也是一样的。然后如果是设置ignore()的话,表明该组件虽然意识到事件的发生,但是会“无视”掉这个事件,让事件继续往层级结构上方传播。而且无论是accept还是ignore,该组件相关的事件函数肯定会被调用,也就是说这两个函数影响的就只是“事件的传播”而已,并不会影响到事件函数 的执行。
我这样理解是正确的吗?

回复
塔奇克马 2020年7月3日 - 14:10

总结起来就是QEvent的accept()和ignore()只是作用在Qt的对象树中,accept()表明我会做处理,不要再去传播给我的父组件,ignore()表明我不会做处理,请传播给父组件,一般情况下不需要显示调用。日常用的顶多就是定制EventHandler,也即重写父类的EventHandler函数(会覆盖父类的,不会再去调用父类的)。

回复
Kane 2020年8月5日 - 16:41

如果一个事件处理函数调用了一个事件对象的accept()函数,这个事件就不会被继续传播给其父组件;如果它调用了事件的ignore()函数,Qt 会从其父组件中寻找另外的接受者。
QWidget::mousePressEvent(QMouseEvent *event)
{
event->ignore();
***
}
那么Qt 还会从QWidget的父组件中寻找另外的接受者吗?这里的逻辑是怎样的

回复
小学生Kane 2020年8月5日 - 16:52

可否说说QWidget::mousePressEvent()函数中关于Popup判断的那段代码是干嘛的

回复
豆子 2020年8月10日 - 15:07

如果没有理解错误的话,感觉应该是判断如果这个 widget 是一个 popup 类型的组件,那么鼠标点击就会是其关闭

回复
小学生Kane 2020年8月17日 - 15:30

void CustomButton::mousePressEvent(QMouseEvent *event){
QPushButton::mousePressEvent((event));
if(event->button() == Qt::LeftButton)
qDebug() << "left";
}
如果这样改写mousePressEvent,结果先打印left,后打印You clicked this!
百思不得其解啊,QPushButton::mousePressEvent((event));不是先调用了吗。

回复
JiangHui 2021年2月26日 - 17:53

你好,请问一下这个工程是怎么创建出来的呀,貌似QT5.14.2如果创建工程,基于的基类只有三个,分别是QMainWindow/QDialog/QWidget,但是没有基于继承QPushButton类的选项,但是如果创建gui工程的话,自己手动写继承与QPushButton就无法重写mousePressEvent()函数

回复
JiangHui 2021年2月26日 - 18:01

我尝试使用QPushButton作用于下的mousePressButton(),但是补全显示这个函数是私有的

回复
豆子 2021年2月27日 - 09:26

Qt Creator 不能直接把 QPushButton 当做基类,需要手动修改。mousePressEvent() 应该是 protected 的,不是 private 的

回复
xingchen 2021年4月1日 - 13:08

我点击了no,执行了ignore(),但窗口还是关闭了,这咋回事呢

回复
xingchen 2021年4月1日 - 13:14

少写了个else哈哈

回复
Luo 2021年11月4日 - 09:55

1)我们重写了CustomButton的mousePressEvent()函数,也就是鼠标按下。
这里应该是重写了QAbstractButton的mousePressEvent()函数!
2)父类QPushButton的mousePressEvent()函数中肯定发出了clicked()信号,否则的话,我们的槽函数怎么会不执行了呢?
这里应该是肯定没有发出clicked()信号!

回复
Luo 2021年11月4日 - 10:01

第1条修正一下,反正这个虚函数是一路继承下来的,所以这里应该说重写QPushButton的虚函数

回复
Luo 2021年11月4日 - 15:57

请问大佬,这个占位符[*]这种知识是哪里查到的呢?

回复
豆子 2021年11月5日 - 10:03

直接在 QWidget::setWindowTitle() 的文档里面就有说明的

回复
Luo 2021年11月4日 - 16:18

有个疑问,最开始的例子,custombutton它没有挂父组件,只有父类,所以没有父组件的时候是传给父类执行事件么?

回复
豆子 2021年11月5日 - 10:06

应该说,父组件是 Qt 的概念,父类是 C++ 语言级别的概念,二者不是一致的。父类的执行是通过显式调用父类函数实现的,你有这句代码才会执行,与是否有父组件没有关系

回复

回复 付强 取消回复

关于我

devbean

devbean

豆子,生于山东,定居南京。毕业于山东大学软件工程专业。软件工程师,主要关注于 Qt、Angular 等界面技术。

主题 Salodad 由 PenciDesign 提供 | 静态文件存储由又拍云存储提供 | 苏ICP备13027999号-2