我多次看到具有槽没有被称为问题的人。 我想收集一些最常见的原因。 所以,也许我可以帮助人们避免大量的冗余问题。
有什么理由不工作的信号/槽连接? 如何避免这样的问题?
我多次看到具有槽没有被称为问题的人。 我想收集一些最常见的原因。 所以,也许我可以帮助人们避免大量的冗余问题。
有什么理由不工作的信号/槽连接? 如何避免这样的问题?
有一些规则,使有信号,更容易槽寿命并覆盖有缺陷连接的最常见的原因。 如果我忘了什么事,请告诉我。
1)检查调试控制台输出:
当发生执行错误,调试输出可以告诉你的原因。
2)使用信号和时隙的完整签名:
代替
connect(that, SIGNAL(mySignal), this, SLOT(mySlot));
写
connect(that, SIGNAL(mySignal(int)), this, SLOT(mySlot(int)));
并检查您的拼写和大小写。
3)利用现有的重载:
仔细检查,如果你正在使用的信号和槽的期望过载,如果存在,您实际使用的过载。
4)你的信号和时隙必须是兼容的:
这尤其意味着参数必须是相同的类型(参考文献都耐受)的和具有相同的顺序。
编译时的语法也需要相同数量的参数。 旧的运行时语法允许信号以较少的参数连接到插槽。
5)经常检查连接方法 (程序员不应该忽略返回值) 的返回值 :
代替
connect(that, SIGNAL(mySignal(int)), this, SLOT(mySlot(int)));
始终使用类似
bool success = connect(that, SIGNAL(mySignal(int)), this, SLOT(mySlot(int)));
Q_ASSERT(success);
或者,如果你想抛出一个异常,实行全员错误处理。 您也可以使用这样的宏:
#ifndef QT_NO_DEBUG
#define CHECK_TRUE(instruction) Q_ASSERT(instruction)
#else
#define CHECK_TRUE(instruction) (instruction)
#endif
CHECK_TRUE(connect(that, SIGNAL(mySignal(int)), this, SLOT(mySlot(int))));
6)你需要排队连接的事件循环:
即当过您连接信号/由不同的线程(所谓的排队连接)拥有你需要调用两个对象的插槽exec();
在插槽的线程!
该事件循环还需要实际服务。 每当插槽的线程是停留在某种繁忙的循环,排队的连接没有执行!
7)您需要排队连接注册自定义类型:
因此,在排队的连接,使用自定义的类型时,你必须注册他们为了这个目的。
首先使用下面的宏声明的类型:
Q_DECLARE_METATYPE(MyType)
然后使用以下调用之一:
qRegisterMetaType<MyTypedefType>("MyTypedefType"); // For typedef defined types
qRegisterMetaType<MyType>(); // For other types
8)宁要旧的运行时检查语法新的编译时的语法:
代替
connect(that, SIGNAL(mySignal(int)), this, SLOT(mySlot(int)));
使用此语法
connect(that, &ThatObject::mySignal, this, &ThisObject::mySlot));
这在编译时期间检查信号和槽,甚至不需要目的地址为一个实际的槽。
如果你的信号过载使用的语法如下:
connect(that, static_cast<void (ThatObject::*)(int)> &ThatObject::mySignal), this, &ThisObject::mySlot); // <Qt5.7
connect(that, qOverload<int>::of(&ThatObject::mySignal), this, &ThisObject::mySlot); // >=Qt5.7 & C++11
connect(that, qOverload<int>(&ThatObject::mySignal), this, &ThisObject::mySlot); // >=Qt5.7 & C++14
也不会混合用于该语法常量/非const信号/插槽(通常信号和槽将非const)。
9)你的类需要一个Q_OBJECT宏:
在您使用的“信号”和“槽”规范类,你需要添加一个Q_OBJECT宏是这样的:
class SomeClass
{
Q_OBJECT
signals:
void MySignal(int x);
};
class SomeMoreClass
{
Q_OBJECT
public slots:
void MySlot(int x);
};
此宏增加必要的元信息的类。
10)你的对象必须是活:
只要任一发送者或对象的接收器对象被销毁,Qt的自动丢弃的连接。
如果信号没有发出:是否发送对象还存在吗? 如果该插槽不叫:是否接收对象还存在吗?
要检查寿命两个对象使用调试器断点或在构造/解构一些qDebug()输出。
11)它仍然不能正常工作:
做你的连接非常快速和肮脏的检查使用一些虚拟参数由你自己发出的信号,看看它被称为:
connect(that, SIGNAL(mySignal(int)), this, SLOT(mySlot(int)));
emit that->mySignal(0); // Ugly, don't forget to remove it immediately
最后当然是可能的信号根本不排放。 如果你按照上面的规则,大概什么是错在你的程序的逻辑。 阅读文档。 使用调试器。 如果现在有其他的方式,请咨询计算器。
在我的实践,我所遇到的对象接收信号错误地覆盖eventFilter情况。 一些新手程序员忘记在最后返回true。 因而不允许MetaCall事件传递给接收对象。 在这种情况下,信号不是在接收对象处理。
你(几乎)不必担心这个问题了。 总是使用QMetaMethod /指针的构件原型connect
,因为它会在编译时,如果信号和槽是不兼容的失败。
connect(sourceObject, &SourceClass::signal, destObject, &DestClass::slot);
这个原型只会在运行时失败,如果sourceObject
或destObject
为空(这是可以预料的)。 但争论的不兼容性将在编译过程中显示出来
只有极少数情况下需要旧SIGNAL
/ SLOT
基于文本的语法,所以这应该是你的最后一招。
如果满足以下条件的签名是兼容的:
signalA(int, std::string)
=> signalC(int, std::string)
signalA(int, std::string)
=> slotB(int, std::string)
signalA(int, std::string)
=> slotB(int)
signalA(int, std::string)
=> slotB()
signalA(int, const char*)
=> slotB(int, QString)
QString(const char*)
signalA(int, std::string)
=> slotB(std::string)
int
不隐含转换为std::string
signalA(int, std::string)
=> slotB(std::string, int)
signalA(int, std::string)
=> slotB(int, std::string, int)