Qtイベントとシグナル/スロット


97

Qtの世界では、イベントと信号/スロットの違いは何ですか?

一方が他方を置き換えますか?イベントは信号/スロットの抽象化ですか?

回答:


30

Qtのドキュメントは、おそらく最高のそれを説明します:

Qtでは、イベントは抽象QEventクラスから派生したオブジェクトであり、アプリケーション内で、またはアプリケーションが知る必要のある外部アクティビティの結果として発生したことを表します。イベントはQObjectサブクラスの任意のインスタンスで受信および処理できますが、特にウィジェットに関連しています。このドキュメントでは、一般的なアプリケーションでイベントが配信および処理される方法について説明します。

したがって、イベントとシグナル/スロットは、同じことを実行する2つの並列メカニズムです。一般に、イベントは外部エンティティ(キーボードやマウスホイールなど)によって生成され、のイベントループを通じて配信されQApplicationます。一般に、コードを設定しない限り、イベントは生成されません。QObject::installEventFilter()適切な関数をオーバーライドすることで、それらをフィルタリングしたり、サブクラス化されたオブジェクトのイベントを処理したりできます。

シグナルとスロットは生成と受信がはるかに簡単で、2つのQObjectサブクラスを接続できます。それらはメタクラスを通じて処理されます(詳細はmoc_classname.cppファイルを参照してください)。ただし、生成するクラス間通信のほとんどは、信号とスロットを使用します。シグナルはすぐに配信されるか、キューを介して遅延されます(スレッドを使用している場合)。

信号を生成できます。


遅延キューはキューイベントループと同じですか、それとも2つのキューが存在しますか?1つは遅延信号用で、もう1つはイベント用ですか?
Guillaume07 2011

29
@Raphaelがこの回答を受け入れたことを恥じます。–引用された段落には、「シグナル」または「スロット」という単語さえ含まれていません。
Robert Siemer

1
@neuronet:段落は「[それ]はそれを最もよく説明します」で引用されていますが、そうではありません。どういたしまして。少しでも。
Robert Siemer 2016年

@Robertそれで、その小さなイントロ行が「最初に、ドキュメントがイベントを説明する方法を検討しましょう。次に、それらが信号にどのように関連しているかを検討する前に」に変更された場合、答えは大丈夫でしょうか?もしそうなら、それはマイナーポイントであり、私は答えを編集してそれを改善することができます。[これは本物の質問です。残りの部分がはるかに優れているとは思えないので編集してください...しかし、引用された部分が信号について言及していないからといって、残りの部分が実際に良い場合は表面的な心配のようです。想定しない。]
エリック2016年

4
@neuronet、引用を完全に削除すると、私の目の答えが改善されます。–回答全体を削除すると、このQA全体が改善されます。
Robert Siemer 2016年

152

Qtでは、シグナルとイベントはどちらもObserverパターンの実装です。それらは異なる長所と短所を持っているため、さまざまな状況で使用されます。

まず最初に、「Qtイベント」の意味を正確に定義しましょう。Qtクラスの仮想関数です。イベントを処理する場合は、自分の基本クラスに再実装することが期待されています。これは、テンプレートメソッドパターンに関連しています

ハンドル」という単語の使い方に注意してください。実際、シグナルとイベントの目的の基本的な違いは次のとおりです。

  • イベントを「処理」します
  • 信号放出通知」します

違いは、イベントを「処理」するときに、クラスの外で役立つ動作で「応答」する責任を引き受けることです。たとえば、番号が付いたボタンがあるアプリを考えてみます。アプリでは、ユーザーがボタンにフォーカスを合わせて、「上」および「下」のキーボードキーを押して番号を変更する必要があります。それ以外の場合、ボタンは通常のように機能するはずですQPushButton(クリックできるなど)。Qt QPushButtonでは、これを再実装する独自の小さな再利用可能な「コンポーネント」(のサブクラス)を作成することによって行われますQWidget::keyPressEvent。疑似コード:

class NumericButton extends QPushButton
    private void addToNumber(int value):
        // ...

    reimplement base.keyPressEvent(QKeyEvent event):
        if(event.key == up)
            this.addToNumber(1)
        else if(event.key == down)
            this.addToNumber(-1)
        else
            base.keyPressEvent(event)

見る?このコードは、新しい抽象化を提供します。ボタンのように機能するウィジェットですが、いくつかの追加機能があります。この機能を非常に便利に追加しました:

  • 仮想を再実装したため、実装は自動的にクラスにカプセル化されました。Qtの設計者がkeyPressEvent信号を作成した場合、信号を継承するQPushButtonか、外部から信号に接続するかを決定する必要があります。しかし、Qt ではカスタム動作のウィジェットを作成するときに常に継承することが期待されているので、それは愚かです(正当な理由-再利用性/モジュール性)。つまりkeyPressEvent、イベントを作成することで、keyPressEvent機能の基本的なビルディングブロックにすぎないという意図を伝えます。シグナルである場合、意図されていない場合は、ユーザーに直接触れるもののように見えます。
  • 関数の基本クラス実装が利用可能であるため、特別なケース(上方向キーと下方向キー)を処理し、残りを基本クラスに任せることで、責任の連鎖パターンを簡単に実装できます。これがkeyPressEvent信号である場合、これはほぼ不可能であることがわかります。

Qtのデザインはよく考えられています。それらは、正しいことを簡単に、間違ったことを(keyPressEventをイベントにすることで)実行しにくくすることで、私たちを成功へと導きました。

一方、最も簡単な使用法を考えるQPushButton- ちょうどそれをインスタンス化し、それをクリックしたときに通知だばかり

button = new QPushButton(this)
connect(button, SIGNAL(clicked()), SLOT(sayHello())

これは明らかにクラスのユーザーが行うことを意味します:

  • QPushButtonボタンをクリックして通知するたびにサブクラス化する必要がある場合は、正当な理由もなく多くのサブクラスが必要になりますmessageboxクリックすると常に「Hello world」を表示するウィジェットは、1つの場合にのみ役立つため、完全に再利用することはできません。繰り返しますが、外部に接続することにより、正しいことを行うしかありません。
  • 複数のスロットをclicked()接続したり、複数の信号を接続したりする場合がありsayHello()ます。シグナルがあれば大騒ぎはありません。サブクラス化を使用すると、適切な設計を決定するまで、いくつかのクラス図についてじっくり考える必要があります。

QPushButton放出する場所の1つがclicked()そのmousePressEvent()実装にあることに注意してください。これはclicked()mousePressEvent()相互に交換可能という意味ではなく、関連しているということだけです。

したがって、シグナルとイベントには異なる目的があります(ただし、どちらも発生したことの通知を「サブスクライブ」できるという点で関連しています)。


私はアイデアを得ました。イベントはクラスごとであり、クラスのすべてのインスタンスは同じように反応し、それらすべてが同じQClassName :: eventを呼び出します。ただし、信号はオブジェクトごとであり、各オブジェクトは独自の信号スロット接続を持つことができます。
爆鱼芋条德里克

39

私はこれまでの答えが好きではありません。–質問のこの部分に集中しましょう。

イベントは信号/スロットの抽象化ですか?

短い答え:いいえ。長い答えは「より良い」質問を提起します:シグナルとイベントはどのように関連していますか?

アイドル状態のメインループ(Qtなど)は、通常、オペレーティングシステムのselect()呼び出しで「スタック」しています。この呼び出しにより、アプリケーションは「スリープ」状態になります。一方、アプリケーションは一連のソケットやファイル、または要求するものをカーネルに渡します。これらに何か変更があった場合は、select()呼び出しを返します。–そして、カーネルは、世界のマスターとして、いつそれが起こるかを知っています。

そのselect()呼び出しの結果は次のようになります:X11に接続するソケットの新しいデータ、リッスンするUDPポートへのパケットの着信など。–これはQtシグナルでもQtイベントでもありません。 Qtのメインループは、新しいデータを一方に変換するか、もう一方に変換するか、無視するかを決定します。

QtはkeyPressEvent()のようなメソッド(またはいくつか)を呼び出し、Qtイベントに効果的に変換できます。または、Qtはシグナルを発行します。これは、事実上、そのシグナルに登録されているすべての関数を検索し、それらを順番に呼び出します。

これらの2つの概念の1つの違いがここに表示されます。スロットは、その信号に登録されている他のスロットが呼び出されるかどうかについて投票権がありません。–イベントはよりチェーンに似ており、イベントハンドラーはそのチェーンを中断するかどうかを決定します。この点では、信号は星または木のように見えます。

イベントはトリガーされるか、完全にシグナルに変換されます(シグナルを発生させるだけで、「super()」を呼び出さないでください)。シグナルをイベントに変換できます(イベントハンドラーを呼び出します)。

ケースに依存するものを抽象化するもの:clicked()シグナルはマウスイベントを抽象化します(ボタンはあまり動き回ることなく上下に移動します)。キーボードイベントは、下位レベルからの抽象化です(果やéのようなものは、私のシステムではいくつかのキーストロークです)。

たぶんfocusInEvent()は反対の例です:clicked()シグナルを使用する(したがって抽象化する)ことができますが、実際に使用するかどうかはわかりません。


4
私はこの部分を見つけます:「スロットは、そのシグナルに登録された他のスロットが呼び出されるかどうかに投票しません」シグナルとイベントの違いを非常に啓発します。ただし、関連する質問があります。メッセージとは何か、メッセージはシグナルとイベントにどのように関連していますか。(それらが関連している場合)。メッセージはシグナルとイベントの抽象化ですか?それらはのQObjectの間のこのような相互作用を記述するために使用することができる(または他のオブジェクトを?)
user1284631

@axeoth:Qtでは、「メッセージ」のようなものはありません。まあ、メッセージボックスがありますが、それはそれについてです。
モニカ

@KubaOber:ありがとう。「イベント」という意味でした。
user1284631 2013

3
@axeoth:それであなたの質問はナンセンスです。「イベントとは何か、シグナルとイベントに関連するイベントはどのようなものか」と書かれています。
モニカを

@KubaOber:1年後のコンテキストを覚えていません。とにかく、私はOPとほぼ同じことを尋ねていたようです:違いは何ですか?イベントと、イベントとシグナルとスロットの関係はどうですか?その当時の実際の状況は、IIRCで、Qt信号/スロット駆動型クラスをある種の非Qtクラスにラップする方法を探していたため、後者の内部から最初のコマンドを実行する方法を探していました。イベントをそれらに送信することを検討していたと思います。これは、Qtヘッダーのみを含める必要があり、クラスに信号/スロットの実装を強制しないことを意味したためです。
user1284631 2013

16

イベントは、イベントループによってディスパッチされます。各GUIプログラムには、Qt、Win32、またはその他のGUIライブラリを使用して、WindowsまたはLinuxで記述したイベントループが必要です。同様に、各スレッドには独自のイベントループがあります。Qtでは "GUIイベントループ"(すべてのQtアプリケーションのメインループ)は非表示になっていますが、次の呼び出しを開始します。

QApplication a(argc, argv);
return a.exec();

OSおよびその他のアプリケーションがプログラムに送信するメッセージは、イベントとして送出されます。

シグナルとスロットはQtメカニズムです。moc(メタオブジェクトコンパイラ)を使用したコンパイルの過程で、それらはコールバック関数に変更されます。

イベントには、それをディスパッチするレシーバーが1つ必要です。他の誰もそのイベントを受け取るべきではありません。

発信された信号に接続されたすべてのスロットが実行されます。

Qtのドキュメントを読むことができるので、Signalsをイベントと見なすべきではありません。

シグナルが発信されると、それに接続されているスロットは、通常の関数呼び出しと同様に、通常すぐに実行されます。これが発生した場合、信号とスロットのメカニズムは、GUIイベントループから完全に独立しています。

イベントを送信するときは、イベントループが先に来たすべてのイベントをディスパッチするまで、しばらく待つ必要があります。このため、イベントまたはシグナルを送信した後のコードの実行は異なります。イベント送信後のコードはすぐに実行されます。信号とスロットのメカニズムでは、接続タイプによって異なります。通常は、すべてのスロットの後に実行されます。Qt :: QueuedConnectionを使用すると、イベントと同様にすぐに実行されます。Qtドキュメントですべての接続タイプを確認してください。


この文は、Qt信号スロットとイベントの違いの簡潔な理解を私に与えてくれます:When you send an event, it must wait for time when event loop dispatch all events that came earlier. Because of this, execution of the cod after sending event or signal is different
swdev

7

イベント処理について詳しく説明している記事があります。http//www.packtpub.com/article/events-and-signals

ここでは、イベントと信号の違いについて説明します。

イベントとシグナルは、同じことを実行するために使用される2つの並列メカニズムです。一般的な違いとして、シグナルはウィジェットを使用するときに役立ちますが、イベントはウィジェットを実装するときに役立ちます。たとえば、QPushButtonのようなウィジェットを使用している場合、シグナルが発生する原因となった低レベルのマウスプレスまたはキープレスイベントよりも、そのclicked()シグナルに関心があります。ただし、QPushButtonクラスを実装する場合は、マウスイベントとキーイベントのコードの実装により関心があります。また、通常はイベントを処理しますが、信号放出によって通知されます。

受け入れられた回答は同じフレーズのいくつかを使用するため、これはそれについて話す一般的な方法のようです。


注意してください。KubaOberからのこの回答について、以下の役立つコメントを参照してください。


同じことを達成するために使用される2つのメカニズムがどのようになるかはわかりません。誰にも役に立たない無駄な一般化だと思います。
モニカ

@KubaOberいいえ、それは下位レベルの詳細を回避するのに役立ちます。同じことをマシンコードで書くことができるので、Pythonは役に立たないと思いますか?:)
エリック2016年

2
引用の最初の文は役に立たないほど一般化していると言っています。技術的には正しくありませんが、必要に応じてイベントパッシングを使用して信号スロットメカニズムの機能を実行でき、その逆も可能です。それは非常に面倒です。だから、いや、シグナル・スロットがありませんイベントと同じ、彼らはしていない、同じことを達成する、との両方が存在であるという事実の重要なのQtの成功のために。引用されているボタンの例は、信号スロットシステムの一般的な機能を完全に無視しています。
モニカを2016

1
「イベントとシグナルは、一部のUIウィジェット/コントロールの狭い範囲内で同じことを実行するために使用される2つの並列メカニズムです。」太字の部分は非常に欠けているため、引用文は役に立ちません。それでも、どれだけの「同じこと」が実際に達成されているのか疑問に思われるかもしれません。シグナルを使用すると、ウィジェットに(またはウィジェットから派生した)イベントフィルターをインストールしなくても、クリックイベントに反応できます。シグナルなしでそれを行うことははるかに面倒であり、クライアントコードをコントロールにしっかりと結合します。それは悪いデザインです。
モニカ

1
イベントはユビキタスなコンセプトです。Qtの特定の実装では、それらをに渡されるデータ構造として具体的に設定しますevent。信号とスロットは、具体的にはメソッドですが、接続メカニズムは、接続されているものとしてリストされている1つ以上のスロットを信号が呼び出すことができるデータ構造です。信号/スロットをイベントの「サブセット」または「バリアント」として説明することはナンセンスであり、その逆も同様であることをご理解いただければ幸いです。これらは実際には、いくつかのウィジェットのコンテキストで同様の目的で使用されることになる、異なるものです。それでおしまい。一般化すればするほど、私見になる助けが少なくなります。
モニカ

5

TL; DR:信号とスロットは間接的なメソッド呼び出しです。イベントはデータ構造です。だから彼らは全く異なる動物です。

それらが一緒になるのは、スロットの呼び出しがスレッドの境界を越えて行われるときだけです。スロット呼び出しの引数はデータ構造にまとめられ、受信スレッドのイベントキューにイベントとして送信されます。受信スレッドでは、QObject::eventメソッドは引数をアンパックし、呼び出しを実行し、ブロッキング接続の場合は結果を返す可能性があります。

忘却に一般化したいのであれば、イベントはターゲットオブジェクトのeventメソッドを呼び出す方法と考えることができます。これは流行後の間接的なメソッド呼び出しですが、たとえそれが真のステートメントであったとしても、それを考えるのに役立つ方法だとは思いません。


2

イベント(一般的なユーザー/ネットワークの相互作用の意味)は、Qtでシグナル/スロットを使用して処理されますが、シグナル/スロットは他の多くのことを行うことができます。

QEventとそのサブクラスは基本的に、フレームワークがコードと通信するための標準化された小さなデータパッケージです。何らかの方法でマウスに注意を向けたい場合は、QMouseEvent APIを確認するだけでよく、ライブラリの設計者は、マウスの隅でマウスが何をしたかを理解する必要があるたびにホイールを作り直す必要はありません。 Qt API。

何らかのイベント(これも一般的なケースです)を待機している場合、スロットはほぼ確実にQEventサブクラスを引数として受け入れます。

そうは言っても、信号とスロットはQEventsなしで確実に使用できますが、信号をアクティブ化するための元の推進力は、多くの場合、何らかのユーザー操作または他の非同期アクティビティーであることがわかります。ただし、場合によっては、コードが特定の信号を発射することが適切な場合に到達することがあります。たとえば、長いプロセス中にプログレスバーに接続されたシグナルを発生させることは、その時点までのQEventを含みません。


2
「イベントは通常、Qtでシグナル/スロットを使用して処理されます」-実際にはありません... QEventオブジェクトは常にオーバーロードされた仮想を介して渡されます!たとえば、QWidgetには「keyDown」という信号はありません。代わりに、keyDownは仮想関数です。
Stefan Monov 2010

ステファンに同意します。実際にはかなり混乱しているQtの一部です
Harald Scheirich

1
@Stefan:keyDownをオーバーライドするQtアプリはほとんどありません(代わりに、QAbstractButton :: clickedやQLineEdit :: editingFinishedなどのシグナルを使用するのが一般的です)。私は確かに以前にキーボード入力を妨害しましたが、それはイベントを処理する通常の方法ではありません。
jkerian

編集後(「イベント」の意味を明確にした後)、投稿は正しくなりました。ただし、そのような言葉の再利用は控えます。信号は「イベントの一種」と呼ばれず、十分に濁っています。
Stefan Monov 2010

2

Leow Wee Khengの「イベント処理」を読んでいるときにこの質問を見つけました。それはまた言います:

ここに画像の説明を入力してください

ジャスミンブランシェットさんのコメント

標準の関数呼び出しやシグナルとスロットではなくイベントを使用する主な理由は、関数を呼び出すか呼び出して、イベントを同期と非同期の両方で使用できるためです(sendEvent()とpostEvents()のどちらを呼び出すかによって異なります)。スロットは常に同期しています。イベントのもう1つの利点は、イベントをフィルタリングできることです。


1

もう1つの実用的な考慮事項:シグナルの送信または受信には継承QObjectが必要ですが、継承のオブジェクトではイベントをポストまたは送信できます(QCoreApplication.sendEvent()またはを呼び出すためpostEvent())これは通常は問題ではありませんが、シグナルを使用するにはQObject、奇妙なことにPyQt が最初のスーパークラスである必要があります。シグナルを送信できるようにするためだけに、継承の順序を変更したくない場合があります。)


-1

私の意見では、イベントは完全に冗長であり、破棄される可能性があります。Qtが既にセットアップされていることを除いて、シグナルをイベントまたはシグナルによるイベントに置き換えることができなかった理由はありません。キューに入れられたシグナルはイベントによってラップされ、イベントはシグナルによってラップされる可能性があります。次に例を示します。

connect(this, &MyItem::mouseMove, [this](QMouseEvent*){});

mouseMoveEvent()にあるQWidget(ただし、現在ではQQuickItemない)便利な関数を置き換え、mouseMoveシーンマネージャがアイテムに対して発行する信号を処理します。信号が外部エンティティによってアイテムに代わって発信されるという事実は重要ではなく、Qtコンポーネントの世界では非常に頻繁に発生しますが、許可されていないと思われます(Qtコンポーネントはこのルールを回避することがよくあります)。しかし、Qtは多くの異なる設計上の決定の集まりであり、古いコードを壊すことを恐れて(とにかく十分頻繁に発生します)かなり石を投げかけています。


イベントには、QObject必要に応じて親子階層を伝播するという利点があります。信号/スロット接続は、特定の条件が満たされたときに、直接的または間接的に関数を呼び出すための約束にすぎません。シグナルとスロットに関連付けられた処理階層はありません。
匿名

シグナルを使用して同様の伝播ルールを実装できます。ルールはありません。特定の信号を別のオブジェクト(子)に再発行できないこと。accepted参照パラメーターを信号とそれを処理するスロットに追加することも、acceptedフィールドを持つ参照パラメーターとして構造体を作成することもできます。しかし、Qtは設計の選択を行い、今やそれらは堅実です。
user1095108 '11

1
あなたが提案した方法でそれを行うなら、あなたは基本的にイベントを再実装しているだけです。
Fabio A.

@FabioA。それがOPの質問のポイントです。イベントとシグナルは、相互に置き換えることができます。
user1095108

イベントを再実装する場合は、イベントを置き換えません。さらに:シグナルはイベントのに実装さます。あなたはおそらくあなたのものではないスレッドで、他のいくつかのイベントループに、将来いつかはどこかで与えられた関数を実行しなければならないことを伝えるために、一種の「イベントシステム」が必要です。
Fabio A.
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.