如何在Qt中暂时断开信号与插槽的连接?

ctx*_*xrr 19 qt signals-slots

我用一个信号连接一个插槽.但现在我想暂时断开它们.

这是我的班级声明的一部分:

class frmMain : public QWidget
{
    ...
private:
    QTimer *myReadTimer;
    ...
private slots:
    void on_btnDownload_clicked();
    ...
};
Run Code Online (Sandbox Code Playgroud)

在构造函数中frmMain,我连接myReadTimer一个插槽,以便ReadMyCom每5秒调用一次:

myReadTimer=new QTimer(this);
myReadTimer->setInterval(5000);
connect(myReadTimer,SIGNAL(timeout()),this,SLOT(ReadMyCom()));
Run Code Online (Sandbox Code Playgroud)

但是,在插槽中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
}
Run Code Online (Sandbox Code Playgroud)

我在Stackoverflow中搜索并获得了一些答案,比如调用QObject析构函数.但我不知道如何使用它.

我也试过用disconnect,比如:

QMetaObject::Connection myConnect;
myConnect=connect(myReadTimer,SIGNAL(timeout()),this,SLOT(ReadMyCom()));
...
disconnect(& myConnect);
Run Code Online (Sandbox Code Playgroud)

但它仍然无效.那么任何人都可以帮我怎么做?

Rob*_*ieE 21

有一个非常好的功能,QObject它不时派上用场:QObject::blockSignals()

这是一个非常简单的"即发即弃"课程,可以满足您的需求.我不相信它的设计,我很久以前就在互联网上找到了它.但要小心,它会阻止所有对象的所有信号.如果这不是您想要的,您可以修改类以满足您的需要.

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;
};
Run Code Online (Sandbox Code Playgroud)

在您的情况下,使用变得微不足道

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
}
Run Code Online (Sandbox Code Playgroud)

更新:

我从Qt 5.3中看到,一个非常类似的类被正式添加到API中.它的功能与上面的相似,功能稍大一些.我建议您使用官方的QSignalBlocker类,以使您的代码库与任何API更改保持同步.

但是,用法仍然完全相同.


Ale*_*x P 11

断开/重新连接语法

有多种方法可以调用断开连接,具体取决于您要断开连接的内容.有关它们如何工作的说明,请参阅QObject文档页面.

这是一个使用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()));
}
Run Code Online (Sandbox Code Playgroud)

或者,您可以通过复制"connect"语法来指定要断开的确切信号槽对,如下所示:

disconnect(myReadTimer,SIGNAL(timeout()),this,SLOT(ReadMyCom()));
Run Code Online (Sandbox Code Playgroud)

停止计时器

由于您正在使用计时器,这可能更简单:

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();
}
Run Code Online (Sandbox Code Playgroud)

与您原来的方法不同:

  • 由于您正在停止并重新启动计时器,因此下次启动时interval您的插槽功能将完成.

你需要做任何特别的事情吗?

在单线程Qt应用程序中,如果您已经在处理信号,则另一个信号将不会"跳到该代码的中间".相反,它会在当前时隙返回后立即排队等待处理.

因此,您可能根本不需要停止或断开计时器.

与您原来的方法不同:

  • 如果on_btnDownload_clicked需要一段时间才能执行,您可能会ReadMyComon_btnDownload_clicked完成后排队多个事件.(注意,此时你的操作基本上会"锁定"你的GUI一段时间;重构函数或给它自己的线程更有意义.)