web-dev-qa-db-ja.com

信号/スロット接続が機能しない

スロットが呼び出されないという問題がある人々を繰り返し見かけます。最も一般的な理由のいくつかを収集したいと思います。だから私は人々を助け、多くの冗長な質問を避けることができます。

信号/スロット接続が機能しない理由は何ですか?このような問題を回避するにはどうすればよいですか?

21
Silicomancer

信号とスロットの使用を容易にし、接続不良の最も一般的な理由をカバーするいくつかのルールがあります。何か忘れた場合は教えてください。

1)デバッグコンソールの出力を確認します:

実行エラーが発生すると、デバッグ出力に理由が示されます。

2)シグナルとスロットの完全な署名を使用:

の代わりに

_connect(that, SIGNAL(mySignal), this, SLOT(mySlot));
_

書く

_connect(that, SIGNAL(mySignal(int)), this, SLOT(mySlot(int)));
_

スペルと大文字を確認してください。

)既存のオーバーロードを使用:

信号とスロットの目的のオーバーロードを使用しているかどうか、および使用したオーバーロードが実際に存在するかどうかを慎重に確認してください。

4)信号とスロットは互換性がなければなりません:

これは特に、パラメーターが同じタイプ(参照が許容される)であり、同じ順序でなければならないことを意味します。

コンパイル時の構文でも同じ数のパラメーターが必要です。古いランタイム構文では、より少ないパラメーターでスロットに信号を接続できます。

5)常に接続メソッドの戻り値を確認する(プログラマはnever戻り値を無視する必要があります):

の代わりに

_connect(that, SIGNAL(mySignal(int)), this, SLOT(mySlot(int)));
_

常に次のようなものを使用します

_bool success = connect(that, SIGNAL(mySignal(int)), this, SLOT(mySlot(int)));
Q_ASSERT(success);
_

または、例外をスローしたり、完全なエラー処理を実装したい場合。次のようなマクロを使用することもできます。

_#ifndef QT_NO_DEBUG
#define CHECK_TRUE(instruction) Q_ASSERT(instruction)
#else
#define CHECK_TRUE(instruction) (instruction)
#endif 

CHECK_TRUE(connect(that, SIGNAL(mySignal(int)), this, SLOT(mySlot(int))));
_

6)キューに入れられた接続にイベントループが必要です:

つまり異なるスレッド(いわゆるキュー接続)が所有する2つのオブジェクトのシグナル/スロットを接続するときは、スロットのスレッドでexec();を呼び出す必要があります。

イベントループも実際に提供される必要があります。スロットのスレッドがなんらかのビジーループでスタックしているときはいつでも、キューに入れられた接続は実行されません!

7)キュー接続にはカスタムタイプを登録する必要があります:

したがって、キューに入れられた接続でカスタムタイプを使用する場合は、この目的のためにそれらを登録する必要があります。

最初に、次のマクロを使用して型を宣言します。

_Q_DECLARE_METATYPE(MyType)
_

次に、以下のいずれかの呼び出しを使用します。

_qRegisterMetaType<MyTypedefType>("MyTypedefType"); // For typedef defined types
qRegisterMetaType<MyType>(); // For other types
_

8)古いランタイムチェックの構文よりも新しいコンパイル時の構文を優先する:

の代わりに

_connect(that, SIGNAL(mySignal(int)), this, SLOT(mySlot(int)));
_

この構文を使用

_connect(that, &ThatObject::mySignal, this, &ThisObject::mySlot));
_

コンパイル時にシグナルとスロットをチェックし、実際のスロットである宛先を必要としません。

信号が過負荷の場合は、次の構文を使用します。

_connect(that, static_cast<void (ThatObject::*)(int)> &ThatObject::mySignal), this, &ThisObject::mySlot); // <Qt5.7
connect(that, qOverload<int>::of(&ThatObject::mySignal), this, &ThisObject::mySlot); // >=Qt5.7 & C++11
connect(that, qOverload<int>(&ThatObject::mySignal), this, &ThisObject::mySlot); // >=Qt5.7 & C++14
_

また、その構文ではconst/non-constシグナル/スロットを混在させないでください(通常、シグナルとスロットは非constになります)。

9)クラスにはQ_OBJECTマクロが必要です:

「シグナル」と「スロット」の仕様を使用しているクラスでは、次のようなQ_OBJECTマクロを追加する必要があります。

_class SomeClass
{
   Q_OBJECT

signals:
   void MySignal(int x);
};

class SomeMoreClass
{
   Q_OBJECT

public slots:
   void MySlot(int x);
};
_

このマクロは、クラスに必要なメタ情報を追加します。

10)オブジェクトは生きている必要があります:

送信者オブジェクトまたは受信者オブジェクトのいずれかが破棄されるとすぐに、Qtは接続を自動的に破棄します。

シグナルが発生しない場合:送信者オブジェクトはまだ存在しますか?スロットが呼び出されない場合:レシーバーオブジェクトはまだ存在しますか?

両方のオブジェクトの存続期間を確認するには、コンストラクター/デストラクターでデバッガーのブレークポイントまたはqDebug()出力を使用します。

11)それでも動作しません:

接続の非常に迅速でダーティなチェックを行うには、いくつかのダミー引数を使用して自分で信号を発信し、それが呼び出されるかどうかを確認します。

_connect(that, SIGNAL(mySignal(int)), this, SLOT(mySlot(int)));
emit that->mySignal(0); // Ugly, don't forget to remove it immediately
_

最後に、もちろん、信号が単に発信されない可能性もあります。上記のルールに従っている場合は、プログラムのロジックに問題がある可能性があります。ドキュメントをお読みください。デバッガーを使用します。そして、他の方法がある場合は、stackoverflowで質問してください。

37
Silicomancer

私の実践では、シグナルを受信するオブジェクトでeventFilterを誤ってオーバーライドするケースに遭遇しました。一部の初心者プログラマーは、関数の最後で「false」を返すのを忘れています。したがって、MetaCallイベントが受信オブジェクトに渡されることを許可しません。この場合、信号は受信オブジェクトで処理されません。

3
Sergey

短い答え

あなたは(ほとんど)もう心配する必要はありません。 connect のメンバープロトタイプには常にQMetaMethod/Pointerを使用してください。信号とスロットに互換性がない場合、コンパイル時に失敗するためです。

_connect(sourceObject, &SourceClass::signal, destObject, &DestClass::slot);
_

このプロトタイプは、sourceObjectまたはdestObjectがnullの場合(予想される)、実行時にのみ失敗します。ただし、コンパイル時に引数の非互換性が表示されます

まれな状況でのみ古いSIGNAL/SLOTリテラルベースの構文が必要になるため、これが最後の手段になります。

互換性

次の条件を満たす場合、署名は互換性があります。

  • 信号をスロットまたは信号に接続している
  • 宛先信号/スロットには、同じ数またはless引数がソース信号と同じです
  • ソース信号の引数は、使用される場合、宛先信号/スロットで対応する引数(順番に一致)に暗黙的に変換できます。
  • [〜#〜] ok [〜#〜]-signalA(int, std::string) => signalC(int, std::string)
    • 信号に接続していることに注意してください
  • [〜#〜] ok [〜#〜]-signalA(int, std::string) => slotB(int, std::string)
  • [〜#〜] ok [〜#〜]-signalA(int, std::string) => slotB(int)
    • 無視された文字列パラメータ
  • [〜#〜] ok [〜#〜]-signalA(int, std::string) => slotB()
    • すべてのパラメーターが無視されました
  • [〜#〜] ok [〜#〜]-signalA(int, const char*) => slotB(int, QString)
    • 暗黙的にQString(const char*)で変換されます
  • 失敗-signalA(int, std::string) => slotB(std::string)
    • intは暗黙的に_std::string_に変換できません
  • 失敗-signalA(int, std::string) => slotB(std::string, int)
    • 間違った順序
  • 失敗-signalA(int, std::string) => slotB(int, std::string, int)
    • 右側の引数が多すぎます
0
Adrien Leravat