30

スロットが呼び出されないという問題を抱えている人を繰り返し見かけます。よくある理由をまとめてみました。だから多分私は人々を助け、多くの冗長な質問を避けることができます.

シグナル/スロット接続が機能しない理由は何ですか? このような問題をどのように回避できますか?

4

3 に答える 3

50

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

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

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

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

それ以外の

connect(that, SIGNAL(mySignal), this, SLOT(mySlot));

書きます

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

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

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

シグナルとスロットの必要なオーバーロードを使用しているかどうか、使用したオーバーロードが実際に存在するかどうかを慎重に確認してください。

4) 信号とスロットに互換性がある必要があります。

これは特に、パラメーターが同じ型 (参照は許容される) であり、同じ順序である必要があることを意味します。

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

5) connect メソッドの戻り値を常にチェックします(プログラマーは戻り値を無視してはなりません):

それ以外の

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

Qt5.14 から、オーバーロードされたシグナルは非推奨になりました。上記の悪ふざけを取り除くために、非推奨の Qt 機能を無効にします。

また、その構文で const/non-const シグナル/スロットを混在させないでください (通常、シグナルとスロットは non-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 に問い合わせてください。

于 2014-10-17T09:37:48.737 に答える