QT 5:イベントの受け入れと無視.

7888 ワード

まず例を見てみましょう
 //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」と表示されるのは明らかです.
 
上記の例によれば、QPShButtonを継承することによって、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()という関数を書き換えたので、コードページを通じてきっと
この関数はQPShButtonでprotectedのvirtual関数であることが分かったが,clicked()信号が
onButtonClickedですが、書き換えていないときはちゃんと!これにより、QPShButtonにおいて、この関数はclicked()信号を発するに違いない.
上記の例では、elseの部分でQPShButton::mousePressEvent(e)を呼び出すことによって、このイベントを処理したが、clicked()信号は発せなかった.
親クラスの同名関数を呼び出すことでQT 5のイベント伝達をチェーン状と見なすことができ、現在のクラスが処理(accept)していない場合、この信号が親クラスに伝達する親クラスが処理する.これにより、ignore関数を自分で呼び出す必要がなくなり、ignoreを呼び出すと、このイベントは必ず親コンポーネントに渡され、私たちが遭遇できない結果をもたらす可能性があります.一方、親クラスの同名関数は、イベントを処理する、信号(例えば、QPShButton)をブロックするか、QWidgetなどのイベントを無視するかのいずれかである.
 
QT 5のイベントオブジェクトには2つの関数ignore()とaccept()があり、前者は現在のクラスにそのイベントを処理したくないことを伝え、後者は現在のクラスにそのイベントを処理したいことを伝える.具体的には、コンポーネントのイベント処理関数でイベントオブジェクトがaccept()関数を呼び出すと、このイベントは親コンポーネントを渡し続けません.ignore()関数が呼び出されると、親コンポーネントから他の受信者が見つかります.
QT 5のイベント処理関数はすべてprotectedであり、つまり書き換えた関数には必ずその親の応答の関数が存在するが、私たちはその応答関数を書いていないので、親の同名の関数を呼び出して現在のクラスに信号を無視させることは可能である.すなわち、ignoreを呼び出すのではなく、現在のコンポーネントが現在のイベントを無視するには、親クラスの同じ名前の関数を呼び出すことが望ましい.
現在のクラスのイベント処理関数でイベントのignore関数を直接呼び出すと、QTはその信号に他の受信者を探す.これで潜在的な危険があるのではないでしょうか.
自分でingore()とaccept()を呼び出すことを避けるために、QT 5は特殊な設計をしました:イベントオブジェクトは一般的に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に渡される.