Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to disconnect a signal with a slot temporarily in Qt?

I connect a slot with a signal. But now I want to disconnect them temporarily.

Here is part of my class declaration:

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

In the constructor of frmMain, I connect myReadTimer with a slot so that ReadMyCom will be called every 5 seconds:

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

But, in slot on_btnDownload_clicked. I don't want myReadTimer to emit any signal in on_btnDownload_clicked's scope. So I want to disconnect them at the beginning of on_btnDownload_clicked and reconnect them in the end. Like this:

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
}

I searched in Stackoverflow and got some answer like call the QObject destructor. But I don't know how to use it.

I also tried to use disconnect, like:

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

But it still not work. So could any one help me how to do this?

like image 233
ctxrr Avatar asked Feb 15 '15 09:02

ctxrr


People also ask

How do you turn off signals in Qt?

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

How do signals and slots work Qt?

In Qt, we have an alternative to the callback technique: We use signals and slots. A signal is emitted when a particular event occurs. Qt's widgets have many predefined signals, but we can always subclass widgets to add our own signals to them. A slot is a function that is called in response to a particular signal.


2 Answers

There is a very nice function in QObject that comes in handy every now and again: QObject::blockSignals()

Here's a very simple fire-and-forget class that will do what you want. I take no credit for it's design, I found it on the internet somewhere a long time ago. Be careful though, it will block all signals to all objects. If this is not what you want, you can modify the class to suit your needs.

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;
};

Usage, in your case, becomes trivial

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:

I see that from Qt 5.3, a very similar class has been offically added to the API. It does a similar job as the one above with a slightly bigger feature-set. I suggest you use the official QSignalBlocker class instead in order to keep your codebase up-to-date with any API changes.

Usage, however, remains exactly the same.

like image 122
RobbieE Avatar answered Oct 16 '22 17:10

RobbieE


Disconnect/reconnect syntax

There are many ways to call disconnect, depending on exactly what you want disconnected. See the QObject documentation page for an explanation of how they work.

Here's an example using 0 to mean "disconnect all slots."

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()));
}

Or you can specify the exact signal-slot pair to disconnect by copying your 'connect' syntax, like this:

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

Stopping the timer

Since you're working with a timer, this may be simpler:

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();
}

Differences from your original approach:

  • Since you're stopping and restarting the timer, the next time it fires will be interval after your slot function finishes.

Do you need to do anything special at all?

In a single-threaded Qt application, if you're already handling a signal, another signal won't "jump in the middle" of that code. Instead it'll be queued up as an even to handle immediately after the current slot returns.

So perhaps you don't need to stop or disconnect your timer at all.

Differences from your original approach:

  • If on_btnDownload_clicked takes a while to execute, you might have multiple ReadMyCom events queued up after on_btnDownload_clicked completes. (Note that at this point you'd have an operation that basically "locks up" your GUI for a while anyway; it may make more sense to refactor the function or give it its own thread.)
like image 33
Alex P Avatar answered Oct 16 '22 19:10

Alex P