呼叫函数直接与发射信号(QT-信号和插槽)

Call function directly vs emiting Signal (Qt - Signals and Slots)

本文关键字:信号 QT- 插槽 发射 函数 呼叫      更新时间:2023-10-16

此时,我正处于何时发射信号与直接调用其他类中的方法的困境(同一线程)。例如,在我执行的教程中,我将仪器类的通知信号(型号)连接到" this" aka aka the View Manager的连接插槽,请参阅setupViewManager :: wirebuttons(),代码中的第三行。(我正在使用MVVM设计模式)。在这里,信号和插槽很有意义,因为仪器类(模型)不应了解视图管理器的任何知识。(即,将视图管理器的引用传递给模型是不否,因为它会破坏MVVM的设计模式。)辉煌。

我遇到的问题是,在教程中,ViewManager的连接插槽发出了其他信号,然后我必须手动连接到另一个视图类的插槽,即setuptab(ref void setupviewManager :: innected andnected andnected andnected and Connected and void andnected and voidsetupViewManager :: wiredisplayupdate()在代码中)。

我的问题是,为什么不将连接插槽中的所有发射替换为直接调用setuptab的方法呢?对我来说,感觉像是过于复杂的代码。

额外付出的额外发射信号的优势是什么优势,不得不将所有内容连接起来,只是从我有参考的另一个类中调用公共功能(信号)?这不是多线程应用程序(我知道信号和插槽是安全的)。

请启发我。

谢谢。

setupViewManager.cpp:

#include "setupviewmanager.h"
#include "View/setuptab.h"
#include "Model/instrument.h"
#include "Model/settings.h"
#include "utils.h"
namespace Ps
{
    SetupViewManager::SetupViewManager(QObject *parent,
                                       SetupTab &tab,
                                       Instrument &inst,
                                       Settings &config) :
        QObject(parent),
        m_setupTab(tab),
        m_instrument(inst)
    {
        WireSettings(config);
        config.ParseJsonData();
        WireHostAndPort();
        WireMessages();
        WireButtons();
        WireDisplayUpdate();
        m_setupTab.SetHostName(config.getHostName());
        m_setupTab.SetPort(config.getPortNumber());
        m_setupTab.SetCommands(config.getCommandsAsModel());
        auto long_wait = config.getLongWaitMs();
        auto short_wait = config.getShortWaitMs();
        m_instrument.SetlongWaitMs(long_wait);
        m_instrument.SetShortWaitMs(short_wait);
        emit NotifyStatusUpdated(tr("Long wait Ms: %1").arg(long_wait));
        emit NotifyStatusUpdated(tr("Short Wait Ms: %1").arg(short_wait));
        onDisconnected();
    }
    SetupViewManager::~SetupViewManager()
    {
        Utils::DestructorMsg(this);
    }
    void SetupViewManager::WireSettings(Settings &config)
    {
        connect(&config, &Settings::NotifyStatusMessage, &m_setupTab, &SetupTab::onStatusUpdated);
    }
    void SetupViewManager::WireHostAndPort()
    {
        connect(&m_setupTab, &SetupTab::NotifyHostNameChanged, &m_instrument, &Instrument::onHostNameChanged);
        connect(&m_setupTab, &SetupTab::NotifyPortChanged, &m_instrument, &Instrument::onPortChanged);
    }
    void SetupViewManager::WireMessages()
    {
        connect(&m_instrument, &Instrument::NotifyErrorDetected, &m_setupTab, &SetupTab::onStatusUpdated);
        connect(&m_instrument, &Instrument::NotifyStatusUpdated, &m_setupTab, &SetupTab::onStatusUpdated);
        connect(this, &SetupViewManager::NotifyStatusUpdated, &m_setupTab, &SetupTab::onStatusUpdated);
    }
    void SetupViewManager::WireButtons()
    {
        connect(&m_setupTab, &SetupTab::NotifyConnectClicked,&m_instrument, &Instrument::Connect);
        connect(&m_instrument, &Instrument::NotifyConnected, &m_setupTab, &SetupTab::onConnected);
        connect(&m_instrument, &Instrument::NotifyConnected, this, &SetupViewManager::onConnected);
        connect(&m_setupTab, &SetupTab::NotifyDisconnectClicked,&m_instrument, &Instrument::Disconnect);
        connect(&m_instrument, &Instrument::NotifyDisconnected, &m_setupTab,&SetupTab::onDisconnected);
        connect(&m_instrument, &Instrument::NotifyDisconnected, this, &SetupViewManager::onDisconnected);
        connect(&m_setupTab, &SetupTab::NotifySendClicked,&m_instrument, &Instrument::onSendRequest);
        connect(&m_instrument, &Instrument::NotifyDataSent,&m_setupTab, &SetupTab::onDataSent);
        connect(&m_setupTab, &SetupTab::NotifyReceiveClicked,&m_instrument, &Instrument::onReceiveRequest);
        connect(&m_instrument, &Instrument::NotifyDataReceived,&m_setupTab, &SetupTab::onDataReceived);
    }
    void SetupViewManager::WireDisplayUpdate()
    {
       connect (this, &SetupViewManager::NotifyConnectEnabled, &m_setupTab, &SetupTab::onConnectEnabled);
       connect (this, &SetupViewManager::NotifyDisconnectEnabled, &m_setupTab, &SetupTab::onDisconnectEnabled);
       connect (this, &SetupViewManager::NotifyDirectCommandsEnabled, &m_setupTab, &SetupTab::onDirectCommandsEnabled);
       connect (this, &SetupViewManager::NotifyControlTabEnabled, &m_setupTab, &SetupTab::onControlTabEnabled);
    }
    void SetupViewManager::onConnected()
    {
        emit NotifyConnectEnabled(false); // HERE. Why not just call method directly with m_setupTab.onConnectEnabled(false); etc...?
        emit NotifyDisconnectEnabled(true);
        emit NotifyDirectCommandsEnabled(true);
        emit NotifyControlTabEnabled(true);
    }
    void SetupViewManager::onDisconnected()
    {
        emit NotifyConnectEnabled(true);
        emit NotifyDisconnectEnabled(false);
        emit NotifyDirectCommandsEnabled(false);
        emit NotifyControlTabEnabled(false);
    }
}

信号插槽机制的优势:

  • 当您的班级没有有关其客户的信息时易于使用;
  • 可用于线程安全调用;
  • 您不得手动记住所有对象以通知它们;
  • 连接两个对象的唯一规则是它们俩都必须是qobject子类。

缺点:

  • 调用较慢(每个连接对象的每个信号扫描列表);
  • 可能是复杂的意大利面条;您不知道,谁以及何时会呼叫任何插槽或发出信号。

您应该考虑自己的案子。如果在设置UpviewManager之外没有信号"侦听器",请尝试直接呼叫。如果其他人可以连接到此信号,那么您的选择正在散发它们。

也可能还有其他使用信号的原因。但是,没有理由只是使用它们来调用功能。至少在一个线程中。

信号和插槽用于解除类,以便他们不需要明确知道谁使用其功能以及如何使用。在许多情况下,解耦是您软件设计的理想特征。当然,这本身并不是目的,当它可以帮助您推理代码的正确性并更容易维护时,它很有用。解耦有助于理解/推理代码,因为它会导致较小的代码单位,您可以孤立地进行分析。查看它的另一种方法是关注点:让一个单位代码做一件事情,例如将一个类集中在功能的一个方面上。

当您有一对课程并希望决定是否夫妇时,请考虑是否可以与其他班级一起使用。A可以耦合到B,但是耦合该对的接口可以由C而不是B使用?如果是这样,则必须使用一些解耦模式,而信号插槽模式就是其中之一。

例如,让我们比较这两个接口如何影响与用户代码的耦合。目标很简单:将调试输出添加到对象的驱动器中:

class QObject {
  ...
  Q_SIGNAL void destroyed(QObject * obj = Q_NULLPTR);
};
class QObjectB {
  ...
  virtual void on_destroyed();
};
int main() {
  QObject a;
  struct ObjectB : QObjectB {
    void on_destroyed() override { qDebug() << "~QObjectB"; }
  } b;
  QObject::connect(&a, &QObject::on_destroyed, []{ qDebug() << "~QObject"; });
}

信号插槽接口允许您轻松地将功能添加到现有对象中,而无需子类。这是观察者模式的特别灵活的实现。这将您的代码从对象的代码中分离出来。

第二个实现,使用模板方法看起来像模式,迫使一个更紧密的耦合:要在ObjectB的破坏上行动,您必须具有一个派生类的实例,在该类别中,您可以实现所需的功能。