C++ 直接调用函数与发出信号(Qt-信号和插槽)
在这一点上,我处于两难境地:何时发出信号,何时直接调用另一个类中的方法(同一线程)。例如,在我正在进行的教程中,我将仪器类(型号)的NotifyConnected信号连接到“this”(即视图管理器)的onConnected插槽,请参阅代码中的第三行SetupViewManager::WireButtons()。(我使用的是MVVM设计模式)。在这里,信号和插槽是有意义的,因为Instruments类(模型)不应该知道有关视图管理器的任何信息。(也就是说,将视图管理器的引用传递给模型是不允许的,因为这将打破MVVM设计模式。)太棒了 我遇到的问题是,在本教程的下一步中,ViewManager的onConnected插槽会发出其他信号,然后我必须继续手动连接到另一个视图类的插槽,即SetupTab(代码中的参考void SetupViewManager::onConnected和void SetupViewManager::WireDisplayUpdate()) 我的问题是,为什么不直接调用SetupTab的方法来替换onConnected插槽中的所有emit呢?我觉得代码太复杂了 为了发出信号而付出额外的努力,并且为了简单地调用另一个类中的公共函数(信号)(我有一个参考)而必须连接所有东西,这有什么好处?它不是多线程应用程序(我知道信号和插槽是线程安全的) 请开导我 谢谢 setupviewmanager.cpp:C++ 直接调用函数与发出信号(Qt-信号和插槽),c++,qt,signals-slots,C++,Qt,Signals Slots,在这一点上,我处于两难境地:何时发出信号,何时直接调用另一个类中的方法(同一线程)。例如,在我正在进行的教程中,我将仪器类(型号)的NotifyConnected信号连接到“this”(即视图管理器)的onConnected插槽,请参阅代码中的第三行SetupViewManager::WireButtons()。(我使用的是MVVM设计模式)。在这里,信号和插槽是有意义的,因为Instruments类(模型)不应该知道有关视图管理器的任何信息。(也就是说,将视图管理器的引用传递给模型是不允许的,
#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子类
- 慢速呼叫(每个信号发射扫描所有连接对象的列表)李>
- 可能是复杂的意大利面代码;你们不知道,谁和什么时候会呼叫任何插槽,或者谁会收到信号
使用信号可能还有其他原因。但是没有理由仅仅为了调用函数而使用它们。至少在一个线程中。信号和插槽用于解耦类,这样它们就不需要明确知道谁使用了它们的功能以及如何使用它们。在许多情况下,解耦是软件设计的理想特性。当然,它本身并不是目的,当它帮助您分析代码的正确性并使其更易于维护时,它是有用的。解耦有助于理解/推理代码,因为它导致更小的代码单元,您可以单独分析。另一种方式是关注点分离:让一个代码单元做一件事,例如,将一个类集中在功能的一个方面 当您有一对类并希望决定是否将它们耦合时,请考虑它们是否可以与其他类一起使用
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"; });
}
类QObject{
...
Q_信号无效被破坏(QObject*obj=Q_NULLPTR);
};
类QoObjectB{
...
_上的虚拟空已销毁();
};
int main(){
对象a;
结构对象B:QObjectB{
在_destromed()重写{qDebug()时无效你能改变你的文本格式,让眼睛更容易阅读吗?另外,一段代码可能会进一步澄清这个问题。你是否将视图设置为viewmanager
?你不应该在那里建立这些连接吗?当我们对这些类一无所知时,很难判断。按要求完成。请参考代码。谢谢!在这种情况下,这几乎没有什么区别。您可以从外部连接到这些信号,但如果您不打算这样做,可能最好直接调用m_setupTab
的方法。因此,您的意思是,在SetupViewManager::onConnected()中直接调用setupTab中的方法是正确的使用m_setupTab.functionName(参数),而不是发出信号,然后连接信号/插槽。对吗?回答很好。干杯:)