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

24

我将一个槽与一个信号连接起来。但现在我想暂时断开它们之间的连接。

这是我的类声明的一部分:

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

但它仍然无法工作。那么有人能帮我如何做吗?


1
你看过 disconnect 的文档吗?它和 connect 接受相同的参数。 - Retired Ninja
我阅读了disconnect的文档,并发现static bool disconnect(const QMetaObject::Connection &);。那么我错在哪里了? - ctxrr
您使用 QObject::disconnect() 的示例是正确的,除了示例通过指针传递参数而引用被期望 - 因此它甚至不应该编译!disconnect(myConnect) 是正确的。 - user2365669
2个回答

26

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
}

更新:

我发现从Qt 5.3开始,API中官方添加了一个非常类似的类。它的功能与上述类似,但具有稍微更大的功能集。我建议您改用官方的QSignalBlocker类,以使您的代码库保持最新的API更改。

然而,使用方法仍然完全相同。


17

断开/重新连接语法

有许多方法可以调用断开连接,具体取决于您想要断开连接的内容。请参阅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()));
}

您可以通过复制“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一段时间的操作;重构函数或为其提供自己的线程可能更有意义。)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接