web-dev-qa-db-ja.com

Qtで一時的にスロットを使用して信号を切断する方法は?

スロットを信号に接続します。しかし、今は一時的にそれらを切断したいと思います。

クラス宣言の一部を次に示します。

class frmMain : public QWidget
{
    ...
private:
    QTimer *myReadTimer;
    ...
private slots:
    void on_btnDownload_clicked();
    ...
};

frmMainのコンストラクターで、myReadTimerが5秒ごとに呼び出されるように、ReadMyComをスロットに接続します。

myReadTimer=new QTimer(this);
myReadTimer->setInterval(5000);
connect(myReadTimer,SIGNAL(timeout()),this,SLOT(ReadMyCom()));

しかし、スロットon_btnDownload_clickedで。 myReadTimeron_btnDownload_clickedのスコープ内で信号を発信しないようにします。したがって、on_btnDownload_clickedの先頭でそれらを切断し、最後に再接続します。このような:

void frmMain::on_btnDownload_clicked()
{
    //some method to disconnect the slot & singal

    ...//the code that I want myReadTimer to leave me alone

    //some method to reconnect the slot & singal
}

Stackoverflowで検索し、QObjectデストラクタを呼び出すような回答を得ました。しかし、私はそれを使用する方法がわかりません。

また、次のようにdisconnectを使用しようとしました:

QMetaObject::Connection myConnect;
myConnect=connect(myReadTimer,SIGNAL(timeout()),this,SLOT(ReadMyCom()));
...
disconnect(& myConnect);

しかし、まだ機能しません。だから誰かがこれを行う方法を手伝ってくれますか?

17
ctxrr

QObjectには非常に便利な関数があります。これは時々便利です:QObject::blockSignals()

これは、必要なことを実行する非常に単純なfire-and-forgetクラスです。私はそれがデザインであることを信用していません。私はずっと前にインターネットでそれを見つけました。ただし、すべてのオブジェクトへのすべての信号をブロックすることに注意してください。これが望んでいない場合は、ニーズに合わせてクラスを変更できます。

class SignalBlocker{
public:
    SignalBlocker(QObject *o): object(o), alreadyBlocked(object->signalsBlocked()){
        if (!alreadyBlocked){
            object->blockSignals(true);
        }
    }
    ~SignalBlocker() {
        if (!alreadyBlocked){
            object->blockSignals(false);
        }
    }

private:
    QObject *object;
    bool alreadyBlocked;
};

あなたの場合、使い方は簡単になります

void frmMain::on_btnDownload_clicked()
{
    SignalBlocker timerSignalBlocker(myReadTimer);

    ...//the code that I want myReadTimer to leave me alone

    // signals automatically unblocked when the function exits
}

UPDATE:

Qt 5.3から、APIに非常によく似たクラスが公式に追加されたことがわかります。わずかに大きな機能セットを使用して、上記と同様のジョブを実行します。 APIの変更に合わせてコードベースを最新の状態に保つために、代わりに公式の QSignalBlocker クラスを使用することをお勧めします。

ただし、使用方法はまったく同じです。

19
RobbieE

切断/再接続の構文

切断するものに応じて、切断を呼び出す方法は多数あります。 QObject documentation page を参照して、それらがどのように機能するかについて説明してください。

0を使用して「すべてのスロットを切断する」ことを意味する例を次に示します。

void frmMain::on_btnDownload_clicked()
{
    // disconnect everything connected to myReadTimer's timeout
    disconnect(myReadTimer, SIGNAL(timeout()), 0, 0);    

    ...//the code that I want myReadTimer to leave me alone

    // restore the connection
    connect(myReadTimer,SIGNAL(timeout()),this,SLOT(ReadMyCom()));
}

または、次のように 'connect'構文をコピーして、切断する正確な信号とスロットのペアを指定できます。

disconnect(myReadTimer,SIGNAL(timeout()),this,SLOT(ReadMyCom()));

タイマーを停止する

タイマーを使用しているので、これはもっと簡単かもしれません:

void frmMain::on_btnDownload_clicked()
{
    // stop the timer (so you won't get any timeout signals)
    myReadTimer->stop();    

    ...//the code that I want myReadTimer to leave me alone

    // restart the timer (using whatever interval was set previously)
    myReadTimer->start();
}

元のアプローチとの違い:

  • タイマーを停止して再起動しているため、スロット関数が終了した後、次に起動するのはintervalになります。

何か特別なことをする必要がありますか?

シングルスレッドQtアプリケーションでは、既にシグナルを処理している場合、別のシグナルはそのコードの「途中でジャンプ」しません。代わりに、現在のスロットが戻った直後に処理するイベントとしてキューに入れられます。

したがって、タイマーを停止または切断する必要はまったくありません。

元のアプローチとの違い:

  • on_btnDownload_clickedの実行に時間がかかる場合、on_btnDownload_clickedの完了後に複数のReadMyComイベントがキューに入れられる可能性があります。 (この時点では、基本的にしばらくの間GUIを基本的に「ロックアップ」する操作があることに注意してください。関数をリファクタリングするか、独自のスレッドを与える方が理にかなっています。)
10
Alex P