让我们首先来看一个例子:
//CustomButton.h #include <QWidget> #include <QPushButton> #include <QDebug>
class CustomButton : public QPushButton { Q_OBJECT
public: CustomButton(QWidget* parent = nullptr); ~CustomButton()=default;
protected: inline void onButtonClicked() { qDebug()<<"You click this!"; } };
CustomButton::CustomButton(QWidget* parent) :QPushButton(parent) { connect(this, &CustomButton::clicked, this, &CustomButton::onButtonClicked); }
//mian.cpp #include <QApplication>
int main(int argc, char *argv[]) { QApplication a(argc, argv); CustomButton customButton; customButton.setText(QString("This is a CustomButton")); customButton.show();
return a.exec(); }
很显然这个例子在我们点击该按钮的时候会在控制台显示:"You click this".
根据上面的例子我们通过继承QPushButton重写了一个protected的事件处理函数:
#include <QWidget> #include <QPushButton> #include <QDebug>
class CustomButton : public QPushButton { Q_OBJECT
public: CustomButton(QWidget* parent = nullptr); ~CustomButton()=default;
private: inline void onButtonClicked() { qDebug()<<"You click this!"; }
protected: virtual void mousePressEvent(QMouseEvent* e)override; };
#include <QMouseEvent>
CustomButton::CustomButton(QWidget* parent) :QPushButton(parent) { connect(this, &CustomButton::clicked, this, &CustomButton::onButtonClicked); }
void CustomButton::mousePressEvent(QMouseEvent* e) { if(e->button() == Qt::LeftButton){ qDebug()<<"you clicked left-key"; //emit clicked(); //注意这里.
}else{ QPushButton::mousePressEvent(e); } }
运行发现竟然显示:"you clicked left-key"!!!!!!!!!!!!!!!
那么为什么呢???我们来仔细看一下代码与上个例子不同之处在于我们重写了mousePressEvent()这个函数,通过代码页肯定能
看出来这个函数在QPushButton中是protected的virtual函数,而我们现在重写了该函数发现无论如果clicked()信号都无法连接到
onButtonClicked,但是没重写的时候却好好的!由此说明在QPushButton中该函数肯定发出了clicked()信号.
在上面的例子中我们在else的部分通过调用QPushButton::mousePressEvent(e),来处理该事件,却并没有发出clicked()信号.
通过调用父类的同名函数可以把QT5的事件传递看成链状的,如果当前类没有处理(accept)该信号就传递给父类由父类处理.这样使得我们不用自己去调用ignore函数,如果调用ignore,该事件就一定会被传递给父组件,可能造成我们不能遇见的后果.而父类中的该同名函数可能对该事件有所处理,可能会拦截掉该信号(比如:QPushButton),也可能会接着忽略该事件(比如QWidget).
QT5的事件对象有2个函数ignore()和accept(),前者告诉当前类不想处理该事件,后者告诉当前类想处理该事件.具体来说:如果一个组件的事件处理函数中事件对象调用了accept()函数,这个事件就不会继续传递个父组件了!如果调用了ignore()函数,那么就会从父组件中寻找其他的接收者!
QT5中的事件处理函数都是protected的,也就是说重写的函数必定存在着其父类中的响应的函数,然而我们并没有写该响应函数,因此调用父类中的同名函数来使当前类忽略该信号是可行的!也就是说如果我们想要当前组件忽略掉当前事件最好调用其父类中的同名函数而不是调用ignore.
如果我们在当前类的事件处理函数中直接调用事件的ignore函数,QT就会让该信号寻找其他的接收者.这样不就存在潜在危险了么!
为了避免自己去调用ingore()和accept(),QT5做了特殊的设计:事件对象一般默认都是accept的的,但是在QWidget中事件对象却是ignore的,由于QWidget是所有组件的基类,因此如果我们的当前类线接受事件就不需要调用其基类的默认实现了!如果我们的当前类想忽略该事件那就直接调用基类的同名函数就好了.
让我们接着来看例子:
#include <QMainWindow> #include <QPushButton> #include <QVBoxLayout> #include <QDebug> #include <memory>
class CustomButton : public QPushButton { Q_OBJECT
public: CustomButton(QWidget* parent=nullptr):QPushButton(parent){} virtual ~CustomButton()=default;
protected: virtual void mousePressEvent(QMouseEvent* ev)override { qDebug()<<"CustomButton!"; } };
class CustomButtonEx : public CustomButton { Q_OBJECT
public: CustomButtonEx(QWidget* parent=nullptr):CustomButton(parent){} ~CustomButtonEx()=default;
protected: virtual void mousePressEvent(QMouseEvent* ev) override { qDebug()<<"CustomButtonEx!"; } };
class CustomWidget : public QWidget { Q_OBJECT
public: CustomWidget(QWidget* parent=nullptr):QWidget(parent){} ~CustomWidget()=default;
protected: virtual void mousePressEvent(QMouseEvent* ev)override { qDebug()<<"CustomWidget!"; } };
class MainWindow : public QMainWindow { Q_OBJECT
public: MainWindow(QWidget* parent = nullptr); ~MainWindow()=default;
protected: virtual void mousePressEvent(QMouseEvent* ev)override { qDebug()<<"MainWindow!"; }
private: std::shared_ptr<CustomWidget> customWidget; std::shared_ptr<CustomButtonEx> customButtonEx; std::shared_ptr<CustomButton> customButton; std::shared_ptr<QVBoxLayout> vBoxLayout; };
#include "mainwindow.h"
MainWindow::MainWindow(QWidget* parent) :QMainWindow(parent) { this->customWidget = std::shared_ptr<CustomWidget>(new CustomWidget(this)); this->customButton = std::shared_ptr<CustomButton>(new CustomButton(customWidget.get())); customButton->setText(tr("CustomButton")); this->customButtonEx = std::shared_ptr<CustomButtonEx>(new CustomButtonEx(customWidget.get())); customButtonEx->setText(tr("CustomButtonEx"));
this->vBoxLayout = std::shared_ptr<QVBoxLayout>(new QVBoxLayout(customWidget.get())); vBoxLayout->addWidget(customButton.get()); vBoxLayout->addWidget(customButtonEx.get());
this->setCentralWidget(customWidget.get()); }
#include "mainwindow.h" #include <QApplication>
int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; w.show();
return a.exec(); }
注意上面的例子中我们对于mousePressEvent(QMouseEvent* ev)的实现我们并没有对ev进行任何操作.但是在我们点击相应的组件的时候比如CustomWidget, CustomButton,CustomButtonEx的时候这些组件仍然能够精准的接受到信号.由此可以看出除了QWdiget之外的所有事件处理函数中的事件对象默认都是accept的。
那么我们来改一下CustomButtonEx中的mousePressEvent函数吧!
virtual void mousePressEvent(QMouseEvent* ev) override { ev->ignore(); qDebug()<<"CustomButtonEx!"; }
输出结果是:
CustomButtonEx!
CustomWidget!
是的你没看错传递给了其父组件!由此可以看出来ignore只是表明当前组件不想响应该事件,而不是说该事件就被扼杀了!
我们可以看出来由于CustomButtonEx不想响应因此该事件被传递给了父组件,由此可以看出事件的传递是在父-子组件之间的,
而不是父-子继承之间的.
我们接着修改CustomWidget中的mousePressEvent的实现(上面的修改也不要变啊):
virtual void mousePressEvent(QMouseEvent* ev)override { qDebug()<<"CustomWidget!"; this->QWidget::mousePressEvent(ev); }
输出结果是:
CustomButtonEx!
CustomWidget!
MainWindow!
我们在程序中点击了CustomButtonEx这个按钮,这个按钮中的mousePressEvent选择忽略,于是事件被传递给了CustomWidget,而在CustomWidget中我们调用它的基类中的mousePressEvent接受该事件,又由于CustomWidget的基类是
QWidget,而QWidget的mousePressEvent函数中的事件对象默认设置为ignore所以该事件又被传递给了MainWindow.