呼叫函数直接与发射信号(QT-信号和插槽)
Call function directly vs emiting Signal (Qt - Signals and Slots)
此时,我正处于何时发射信号与直接调用其他类中的方法的困境(同一线程)。例如,在我执行的教程中,我将仪器类的通知信号(型号)连接到" 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
的破坏上行动,您必须具有一个派生类的实例,在该类别中,您可以实现所需的功能。
- Qt VTK交互风格的信号到小部件
- 如何将点击的信号和插槽添加到qt中的自定义按钮中
- 如何在qt中将信号和插槽与另一个对象连接 --解决了
- Qt将信号与另一个类方法连接
- 通过插槽和信号在不同线程中的两个qt对象之间进行通信
- 控制带有信号/插槽的Qt QML滑动视图
- Qt 信号/插槽问题
- Qt信号和插槽如果从QRunnable或其他线程调用,则不起作用
- Qt:如何通知对象已建立涉及它的信号槽连接
- Qt/C++ 系统单元启动/停止时的信号
- C++Qt信号和插槽
- Qt moveToThread,带有参数的信号/插槽
- 如何在Qt测试框架中对信号进行基准测试?
- Qt - Q_PROPERTY的通知信号未发出有关成员更改的通知信号
- 当再次触发信号时,从Qt插槽执行的功能被第二次调用时会发生什么?
- 使用std::move将std::unique_ptr作为qt信号参数传递
- 使用std::bind将Qt信号连接到插槽
- Qt:向死/停止线程发送信号
- 呼叫函数直接与发射信号(QT-信号和插槽)
- 没有这样的插槽/信号(Qt)