回答:
Qtのドキュメントは、おそらく最高のそれを説明します:
Qtでは、イベントは抽象
QEvent
クラスから派生したオブジェクトであり、アプリケーション内で、またはアプリケーションが知る必要のある外部アクティビティの結果として発生したことを表します。イベントはQObject
サブクラスの任意のインスタンスで受信および処理できますが、特にウィジェットに関連しています。このドキュメントでは、一般的なアプリケーションでイベントが配信および処理される方法について説明します。
したがって、イベントとシグナル/スロットは、同じことを実行する2つの並列メカニズムです。一般に、イベントは外部エンティティ(キーボードやマウスホイールなど)によって生成され、のイベントループを通じて配信されQApplication
ます。一般に、コードを設定しない限り、イベントは生成されません。QObject::installEventFilter()
適切な関数をオーバーライドすることで、それらをフィルタリングしたり、サブクラス化されたオブジェクトのイベントを処理したりできます。
シグナルとスロットは生成と受信がはるかに簡単で、2つのQObject
サブクラスを接続できます。それらはメタクラスを通じて処理されます(詳細はmoc_classname.cppファイルを参照してください)。ただし、生成するクラス間通信のほとんどは、信号とスロットを使用します。シグナルはすぐに配信されるか、キューを介して遅延されます(スレッドを使用している場合)。
信号を生成できます。
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)
見る?このコードは、新しい抽象化を提供します。ボタンのように機能するウィジェットですが、いくつかの追加機能があります。この機能を非常に便利に追加しました:
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()
相互に交換可能という意味ではなく、関連しているということだけです。
したがって、シグナルとイベントには異なる目的があります(ただし、どちらも発生したことの通知を「サブスクライブ」できるという点で関連しています)。
私はこれまでの答えが好きではありません。–質問のこの部分に集中しましょう。
イベントは信号/スロットの抽象化ですか?
短い答え:いいえ。長い答えは「より良い」質問を提起します:シグナルとイベントはどのように関連していますか?
アイドル状態のメインループ(Qtなど)は、通常、オペレーティングシステムのselect()呼び出しで「スタック」しています。この呼び出しにより、アプリケーションは「スリープ」状態になります。一方、アプリケーションは一連のソケットやファイル、または要求するものをカーネルに渡します。これらに何か変更があった場合は、select()呼び出しを返します。–そして、カーネルは、世界のマスターとして、いつそれが起こるかを知っています。
そのselect()呼び出しの結果は次のようになります:X11に接続するソケットの新しいデータ、リッスンするUDPポートへのパケットの着信など。–これはQtシグナルでもQtイベントでもありません。 Qtのメインループは、新しいデータを一方に変換するか、もう一方に変換するか、無視するかを決定します。
QtはkeyPressEvent()のようなメソッド(またはいくつか)を呼び出し、Qtイベントに効果的に変換できます。または、Qtはシグナルを発行します。これは、事実上、そのシグナルに登録されているすべての関数を検索し、それらを順番に呼び出します。
これらの2つの概念の1つの違いがここに表示されます。スロットは、その信号に登録されている他のスロットが呼び出されるかどうかについて投票権がありません。–イベントはよりチェーンに似ており、イベントハンドラーはそのチェーンを中断するかどうかを決定します。この点では、信号は星または木のように見えます。
イベントはトリガーされるか、完全にシグナルに変換されます(シグナルを発生させるだけで、「super()」を呼び出さないでください)。シグナルをイベントに変換できます(イベントハンドラーを呼び出します)。
ケースに依存するものを抽象化するもの:clicked()シグナルはマウスイベントを抽象化します(ボタンはあまり動き回ることなく上下に移動します)。キーボードイベントは、下位レベルからの抽象化です(果やéのようなものは、私のシステムではいくつかのキーストロークです)。
たぶんfocusInEvent()は反対の例です:clicked()シグナルを使用する(したがって抽象化する)ことができますが、実際に使用するかどうかはわかりません。
イベントは、イベントループによってディスパッチされます。各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ドキュメントですべての接続タイプを確認してください。
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
イベント処理について詳しく説明している記事があります。http://www.packtpub.com/article/events-and-signals
ここでは、イベントと信号の違いについて説明します。
イベントとシグナルは、同じことを実行するために使用される2つの並列メカニズムです。一般的な違いとして、シグナルはウィジェットを使用するときに役立ちますが、イベントはウィジェットを実装するときに役立ちます。たとえば、QPushButtonのようなウィジェットを使用している場合、シグナルが発生する原因となった低レベルのマウスプレスまたはキープレスイベントよりも、そのclicked()シグナルに関心があります。ただし、QPushButtonクラスを実装する場合は、マウスイベントとキーイベントのコードの実装により関心があります。また、通常はイベントを処理しますが、信号放出によって通知されます。
受け入れられた回答は同じフレーズのいくつかを使用するため、これはそれについて話す一般的な方法のようです。
注意してください。KubaOberからのこの回答について、以下の役立つコメントを参照してください。
event
。信号とスロットは、具体的にはメソッドですが、接続メカニズムは、接続されているものとしてリストされている1つ以上のスロットを信号が呼び出すことができるデータ構造です。信号/スロットをイベントの「サブセット」または「バリアント」として説明することはナンセンスであり、その逆も同様であることをご理解いただければ幸いです。これらは実際には、いくつかのウィジェットのコンテキストで同様の目的で使用されることになる、異なるものです。それでおしまい。一般化すればするほど、私見になる助けが少なくなります。
TL; DR:信号とスロットは間接的なメソッド呼び出しです。イベントはデータ構造です。だから彼らは全く異なる動物です。
それらが一緒になるのは、スロットの呼び出しがスレッドの境界を越えて行われるときだけです。スロット呼び出しの引数はデータ構造にまとめられ、受信スレッドのイベントキューにイベントとして送信されます。受信スレッドでは、QObject::event
メソッドは引数をアンパックし、呼び出しを実行し、ブロッキング接続の場合は結果を返す可能性があります。
忘却に一般化したいのであれば、イベントはターゲットオブジェクトのevent
メソッドを呼び出す方法と考えることができます。これは流行後の間接的なメソッド呼び出しですが、たとえそれが真のステートメントであったとしても、それを考えるのに役立つ方法だとは思いません。
イベント(一般的なユーザー/ネットワークの相互作用の意味)は、Qtでシグナル/スロットを使用して処理されますが、シグナル/スロットは他の多くのことを行うことができます。
QEventとそのサブクラスは基本的に、フレームワークがコードと通信するための標準化された小さなデータパッケージです。何らかの方法でマウスに注意を向けたい場合は、QMouseEvent APIを確認するだけでよく、ライブラリの設計者は、マウスの隅でマウスが何をしたかを理解する必要があるたびにホイールを作り直す必要はありません。 Qt API。
何らかのイベント(これも一般的なケースです)を待機している場合、スロットはほぼ確実にQEventサブクラスを引数として受け入れます。
そうは言っても、信号とスロットはQEventsなしで確実に使用できますが、信号をアクティブ化するための元の推進力は、多くの場合、何らかのユーザー操作または他の非同期アクティビティーであることがわかります。ただし、場合によっては、コードが特定の信号を発射することが適切な場合に到達することがあります。たとえば、長いプロセス中にプログレスバーに接続されたシグナルを発生させることは、その時点までのQEventを含みません。
Leow Wee Khengの「イベント処理」を読んでいるときにこの質問を見つけました。それはまた言います:
標準の関数呼び出しやシグナルとスロットではなくイベントを使用する主な理由は、関数を呼び出すか呼び出して、イベントを同期と非同期の両方で使用できるためです(sendEvent()とpostEvents()のどちらを呼び出すかによって異なります)。スロットは常に同期しています。イベントのもう1つの利点は、イベントをフィルタリングできることです。
私の意見では、イベントは完全に冗長であり、破棄される可能性があります。Qtが既にセットアップされていることを除いて、シグナルをイベントまたはシグナルによるイベントに置き換えることができなかった理由はありません。キューに入れられたシグナルはイベントによってラップされ、イベントはシグナルによってラップされる可能性があります。次に例を示します。
connect(this, &MyItem::mouseMove, [this](QMouseEvent*){});
mouseMoveEvent()
にあるQWidget
(ただし、現在ではQQuickItem
ない)便利な関数を置き換え、mouseMove
シーンマネージャがアイテムに対して発行する信号を処理します。信号が外部エンティティによってアイテムに代わって発信されるという事実は重要ではなく、Qtコンポーネントの世界では非常に頻繁に発生しますが、許可されていないと思われます(Qtコンポーネントはこのルールを回避することがよくあります)。しかし、Qtは多くの異なる設計上の決定の集まりであり、古いコードを壊すことを恐れて(とにかく十分頻繁に発生します)かなり石を投げかけています。
QObject
必要に応じて親子階層を伝播するという利点があります。信号/スロット接続は、特定の条件が満たされたときに、直接的または間接的に関数を呼び出すための約束にすぎません。シグナルとスロットに関連付けられた処理階層はありません。
accepted
参照パラメーターを信号とそれを処理するスロットに追加することも、accepted
フィールドを持つ参照パラメーターとして構造体を作成することもできます。しかし、Qtは設計の選択を行い、今やそれらは堅実です。