Qt シグナル スロットのリスト

Qt シグナル スロットのリスト

シグナルとスロットはオブジェクト間の通信に使用されます。シグナルとスロットのメカニズムは Qt の中核機能であり、おそらく他のフレームワークが提供する機能と最も異なるものです。シグナルとスロットは、Qt のメタオブジェクト システムによって可能になります。

GUI プログラミングでは、1 つのウィジェットを変更すると、通常、別のウィジェットに通知が必要になります。より一般的には、あらゆるタイプのオブジェクトが相互に通信できるようにする必要があります。たとえば、ユーザーが「閉じる」ボタンをクリックした場合、ウィンドウの Close() 関数を呼び出すことができます。

他のツールキットはコールバックを使用してこの通信を実現します。コールバック関数は関数へのポインタであるため、ハンドラにイベントを通知させたい場合は、別の関数 (コールバック関数) へのポインタをハンドラに渡す必要があります。次に、ハンドラー関数は、必要に応じてコールバックを呼び出します。このアプローチを使用して成功したフレームワークは存在しますが、コールバックは直感的ではない可能性があり、コールバック パラメーターの型の正確性を確保する際に問題が発生する可能性があります。

Qt にはコールバック手法に代わる手段があり、シグナルとスロットを使用します。特定のイベントが発生したときに信号を送信します。Qt のウィジェットには多くの事前定義されたシグナルがありますが、いつでもウィジェットをサブクラス化して独自のシグナルを追加できます。スロットは、特定の信号に応答して呼び出される関数です。Qt のウィジェットには多数の事前定義されたスロットがありますが、対象のシグナルを処理するためにウィジェットをサブクラス化し、独自のスロットを追加するのが一般的です。

ここに画像の説明を挿入

信号とスロットのメカニズムはタイプセーフです。信号の署名は受信スロットの署名と一致する必要があります。(実際には、スロットのシグネチャは、追加の引数を無視できるため、受信するシグナルよりも短い場合があります。) シグネチャには互換性があるため、コンパイラは、関数ポインター ベースの構文を使用するときに型の不一致を検出するのに役立ちます。文字列ベースの SIGNAL および SLOT 構文は、実行時に型の不一致を検出します。シグナルとスロットは疎結合です。シグナルを発行するクラスは、どのスロットがシグナルを受信するかを知りませんし、気にしません。**Qt のシグナルとスロットのメカニズムにより、シグナルをスロットに接続すると、スロットはシグナルの引数を使用して正しいタイミングで呼び出されます。** シグナルとスロットは、任意のタイプの任意の数の引数を受け入れることができます。これらは完全にタイプセーフです。

QObject またはそのサブクラス (QWidget など) から継承するすべてのクラスには、シグナルとスロットを含めることができます。シグナルは、他のオブジェクトが関心を持つ可能性のある方法でオブジェクトの状態が変化するときに発行されます。オブジェクトが通信するために行うのはこれだけです。送信した信号を何かが受信したかどうかは知りませんし、気にも留めません。これは真の情報のカプセル化であり、オブジェクトをコンポーネントベースのソフトウェアエンジニアリングに使用できるようにします。

スロットはシグナルの受信に使用できますが、通常のメンバー関数でもあります。オブジェクトがその信号を受信するものがあるかどうかを知らないのと同じように、スロットはそれに信号が接続されているかどうかを知りません。これにより、真に自己完結型のコンポーネントを Qt で作成できるようになります。

1 つのスロットに好きなだけ信号を接続でき、また、同じ数のスロットに好きなだけ信号を接続できます。ある信号を別の信号に直接接続することも可能です。(最初の信号が発信されるとすぐに、すぐに 2 番目の信号が発信されます。)

信号とスロットは、強力なコンポーネント プログラミング メカニズムを形成します。

信号

オブジェクトの内部状態が、オブジェクトのクライアントまたは所有者にとって興味深い可能性のある方法で変更された場合、シグナルが発行されます。シグナルはパブリック アクセス関数であり、どこからでも発行できますが、シグナルとそのサブクラスを定義するクラスからのみシグナルを発行することをお勧めします。

シグナルが発行されると、通常は、通常の関数呼び出しと同様に、それに接続されているスロットがすぐに実行されます。これが発生すると、シグナルとスロットのメカニズムは GUI イベント ループから完全に独立します。すべてのスロットが返されると、emit ステートメントに続くコードが実行されます。キュー接続を使用する場合は状況が少し異なります。この場合、emit キーワードに続くコードはすぐに続行され、スロットは後で実行されます。

複数のスロットが信号に接続されている場合、それらのスロットは信号の発行時に接続された順序で次々に実行されます。

シグナルは moc によって自動的に生成されるため、 に含めることはできません。CPP ファイル。戻り値の型を持つことはできません (つまり、void を使用します)。

パラメーターに関する注意: 私たちの経験から、シグナルとスロットは特別なタイプを使用しない方が再利用可能であることがわかっています。QScrollBar::valueChanged() が特別なタイプ、たとえば仮想の QScrollBar::Range を使用する場合、QScrollBar 用に特別に設計されたスロットにのみ接続できます。異なる入力ウィジェットを一緒に接続することはできません。

スロット

スロットは、そのスロットに接続された信号が発信されるときに呼び出されます。スロットは通常の C++ 関数であり、通常どおり呼び出すことができます。スロットの唯一の特性は、信号をスロットに接続できることです。

スロットは通常のメンバー関数であるため、直接呼び出す場合は通常の C++ ルールに従います。ただし、スロットとして、アクセス レベルに関係なく、信号スロット接続を通じて任意のコンポーネントから呼び出すことができます。これは、任意のクラスのインスタンスから発行されたシグナルにより、無関係なクラスのインスタンスでプライベート スロットが呼び出される可能性があることを意味します。

スロットを仮想として定義することもできますが、これは実際に非常に便利であることがわかりました。

シグナルとスロットは柔軟性が高いため、コールバックよりもわずかに遅くなりますが、実際のアプリケーションでは、この点での違いは重要ではありません。一般に、あるスロットに接続された信号を送信することは、非仮想関数呼び出しをレシーバーに直接呼び出すよりも約 10 倍遅くなります。これは、接続オブジェクトを見つけ、すべての接続を安全に反復処理し (つまり、後続の受信者が送信中に破棄されているかどうかを確認し)、一般的な方法でパラメータをマーシャリングするために必要なオーバーヘッドです。10 回の非仮想関数呼び出しは多そうに聞こえるかもしれませんが、新規操作や削除操作よりもオーバーヘッドははるかに少なくなります。文字列、ベクトル、またはリストの操作を実行したら、バックグラウンドを新規作成するか削除する必要がありますが、信号とスロットのオーバーヘッドは関数呼び出し全体のコストのほんの一部にすぎません。スロットでシステムコールを実行する場合でも、10 個以上の関数を間接的に呼び出す場合でも、これは同じです。シグナルとスロットのメカニズムのシンプルさと柔軟性にはオーバーヘッドを払うだけの価値があり、ユーザーはそれに気づくことさえありません。

シグナルまたはスロットと呼ばれる変数を定義する他のライブラリは、Qt ベースのアプリケーションでコンパイルするとコンパイラの警告やエラーを引き起こす可能性があることに注意してください。これを修正するには、# undef プリプロセッサ シンボルを使用します。

Qtでのconnectの使い方

古いバージョンの書き込み方法はより複雑であり、信号とスロットを形式的なパラメータを含めて明確に指定する必要があります。

class MyButton : public QWidget
{
    Q_OBJECT
public:
    explicit MyButton(QWidget *parent = nullptr);

signals:
    void sigClicked();
    void sigClicked(bool check);
};

古いバージョンは一目で書けますが、さらに面倒です

connect(m_pBtn,SIGNAL(sigClicked()),this,SLOT(onClicked()));
connect(m_pBtn,SIGNAL(sigClicked(bool)),this,SLOT(onClicked(bool)));

Qt5.0以降の書き方

connect(m_pBtn,&MyButton::sigClicked,this,&Widget::onClicked);

信号が過負荷になると、エラーが報告されます。

エラー: 'connect' の呼び出しに一致するメンバー関数がありません connect(m_pBtn,&MyButton::sigClicked,this,&Widget::onClicked);

シグナル関数の指定バージョンを指定するには、以下の記述方法を使用しますスロット関数がオーバーロードされている場合は、古い方法を使用することも可能

connect(m_pBtn, static_cast<void (MyButton::*)(bool)>(&MyButton::sigClicked), this, &Widget::onClicked);

信号関数のオーバーロードを最適化する

しかし、オーバーロードされたスロット機能をリンクすることはできません。

connect(m_pBtn, QOverload<bool>::of(&MyButton::sigClicked),this,&Widget::onClicked);

Lambda関数の書き方

スロット関数の内容が単純な場合は、別途スロットリンクを定義する必要がなく、Lambda関数を直接使用するのが便利です。

connect(m_pBtn, QOverload<bool>::of(&MyButton::sigClicked),

               [=](bool check){

                /* do something.. */

                });

connect(m_pBtn, static_cast<void (MyButton::*)(bool)>(&MyButton::sigClicked), this, [=](bool check){

                 //do something

                 });

ラムダ関数

[capture](parameters) mutable ->return-type{statement}

1.[キャプチャ]: キャプチャリスト。キャプチャリストは常に Lambda 関数の先頭に表示されます。実際、[] はラムダ引用符です。コンパイラは、エリシタに基づいて、次のコードが Lambda 関数であるかどうかを判断します。キャプチャリストは、Lambda 関数で使用するためにコンテキスト内の変数をキャプチャできます。

2. (パラメータ): パラメータのリスト。通常の関数のパラメータリストと一致します。パラメータの受け渡しが必要ない場合は、括弧「()」と一緒に省略できます。

3. mutable: 変更可能な修飾子。デフォルトでは、Lambda 関数は常に const 関数であり、mutable はその const 性をキャンセルできます。この修飾子を使用する場合、パラメータ リストは (パラメータが空の場合でも) 省略できません。

4.->return-type: 戻り値の型。トレース戻り値の型フォームを使用して、関数の戻り値の型を宣言します。戻り値が必要ない場合は、記号「->」と合わせて省略することもできます。さらに、戻り値の型が明らかな場合は、この部分を省略して、コンパイラに戻り値の型を推定させることもできます。

5. {ステートメント}: 関数本体。内容は通常の関数と同じですが、パラメータ以外にもキャプチャした変数をすべて使用することができます。

通常の関数との最大の違いは、Lambda 関数はパラメータに加えて、キャプチャ リストを通じて一部のコンテキストのデータにアクセスできることです。具体的には、キャプチャ リストには、コンテキスト内のどのデータが Lambda で使用できるか、およびそのデータの使用方法 (値または参照による) が記述されます。構文上、「[]」内に含まれるものはキャプチャリストであり、キャプチャリストはカンマで区切られた複数のキャプチャ項目で構成されます。キャプチャ リストは次の形式で提供されます。

1. [var] は、値転送メソッドが変数 var をキャプチャすることを示します。
2. [=] は、値転送メソッドが親スコープ内のすべての変数 (これを含む) をキャプチャすることを示します。
3. [&var] は、参照転送メソッドが変数 var をキャプチャすることを示します。変数 var;
4. [&] は、参照転送メソッドが親スコープ内のすべての変数 (this を含む) をキャプチャすることを意味します;
5. [this] は、値転送メソッドが現在の this ポインタをキャプチャすることを意味します。

上記では親スコープ、つまり Lambda 関数を含むステートメント ブロックについて説明しましたが、平たく言えば、Lambda を含む "{}" コード ブロックです。上記のキャプチャ リストは、次のように組み合わせることもできます。

1.[=,&a,&b] は変数 a と b を参照によってキャプチャし、他のすべての変数を値によってキャプチャすることを意味します; 2.[&,a,this] は変数 a と this を値、つまりパスバイによってキャプチャすることを意味し
ます-reference メソッドは、他のすべての変数をキャプチャします。

ただし、キャプチャ リストでは変数を繰り返し渡すことができないことに注意してください。次の例は、コンパイル時エラーを引き起こす典型的な重複です。例えば:

3. ここの [=, a] は値転送によってすべての変数をキャプチャしていますが、a が繰り返しキャプチャされるとエラーが報告されます; 4.
ここの [&,&this] & は参照転送によってすべての変数をキャプチャしてから、これをキャプチャしますそれの繰り返しでもあります。

ラムダ関数の例

【キャプチャー】説明

前のセクションのプロジェクトに基づいて、ボタン mybutton_1 を追加し、ボタンを押して、ボタンのテキストを変更します。上記のラムダ式の基本構成コードによれば、次のように記述できます(間違った書き方)。

QPushButton *mybutton_1=new QPushButton(this);
mybutton_1->setText("关注<程序媛讲QT>");
mybutton_1->move(150,100);   //移动按键
connect(mybutton_1,&QPushButton::pressed,
        []()
        {
             mybutton_1->setText("Lambda表达式");
         }
);

このとき、コンパイル時に「エラー: 'mybutton_1' はキャプチャされません」というエラー メッセージが表示されます。これは、mybutton_1 がスコープ内にないことを意味します。このとき、[capture]を使用して外部変数を渡す必要があります。この時点では、mybutton_1 を渡すだけでよく、コードは通常どおりコンパイルして実行できます。connect() 関数のコードは次のように変更されます。

connect(mybutton_1,&QPushButton::pressed,
        [mybutton_1]()
        {
              mybutton_1->setText("Lambda表达式");
        }
);

この時点で、コードはコンパイルして通常どおり実行できます。そうすると外部に複数の変数がある場合、いちいち[]に書き込むのは面倒なので、このときは「=」を使えばよいのです。例えば:

QPushButton *mybutton_1=new QPushButton(this);
mybutton_1->setText("关注<程序媛讲QT>");
mybutton_1->move(150,100);   //移动按键
int a=2,b=7;
connect(mybutton_1,&QPushButton::pressed,
        [=]()
         {
               mybutton_1->setText("Lambda表达式");
               qDebug()<<a+b;
          }
);

qDebug() は C 言語の printf に似た出力であり、使用する場合は #include “QDebug” を指定する必要があります。
  コンパイルして実行し、ボタンを押すと、ボタンのテキストが変更され、テキスト「9」が出力されます。「=」に外部変数a、b、mybutton_1の値が渡されていることがわかります。
  "= は、すべての外部ローカル変数とクラスのすべてのメンバーを値によって渡すことができることがわかります。同様に、「this」はクラス内のすべてのメンバーが値によって渡されることを意味し、「&」はすべての外部ローカル変数が参照されることを意味します。

要約:

  • [capture]: Lambda 式では、この部分が存在する必要があり、省略できません。
  • =: すべての外部ローカル変数とクラスのすべてのメンバーを値によって Lambda 式に渡します。デフォルトは読み取り専用で、変数の値は変更できません。
  • &: すべてのローカル変数 (ラムダ式が配置されているクラス内のこれを含む)、参照の転送メソッド。
  • this: Lambda が配置されているクラスのメンバー変数を関数本体で使用できます。
  • 「=」を推奨します。

ミュータブルの説明

上で述べたように、「=」は値によって渡されます。デフォルトでは読み取り専用です。外部変数を変更したい場合は、現時点では mutable を使用できます。コードは次のとおりです。

connect(mybutton_1,&QPushButton::pressed,
        [=]() mutable
        {
              b=3;
              mybutton_1->setText("Lambda表达式");
              qDebug()<<a+b;
         }
);
  • 要約:
  • ラムダ式では、mutable を省略できます。変数を値で渡す場合、mutable 修飾子を追加すると Lambda 式内の変数の値を変更できますが、値自体は変更されません。

(パラメータ)の説明

ラムダ式にパラメータがない場合、この部分は無視できます。信号にパラメータがある場合は、() を使用して関数パラメータをオーバーロードできます。例えば:

connect(mybutton_1,&QPushButton::clicked,
             [=](bool Check)
             {
                  qDebug()<<Check;
             }
         );

QPushButton のシグナル void QAbstractButton::clicked(bool selected = false) にはパラメーターがあり、パラメーターは () を通じて Lambda 式に渡すことができます。

要約:

utton::clicked,
[=](bool Check)
{ qDebug()<<Check; } );



 QPushButton的信号void QAbstractButton::clicked(bool checked = false)是有参数的,通过()可将参数传入Lambda表达式中。

**总结:**

- **在Lambda表达式中无参数时,(parameters)部分是可以省略的。**

おすすめ

転載: blog.csdn.net/weixin_43925768/article/details/131124411