信号和插槽 Qt 5.8.0

Qt 5.8 Qt参考文档

内容
介绍
信号和插槽
信号
老虎机
一个小例子
一个真实的例子
信号和插槽与默认参数
高级信号和插槽使用
使用Qt与第三方信号和插槽
信号和插槽
信号和槽用于对象之间的通信。信号和插槽机制是Qt的核心功能,可能是与其他框架提供的功能最不同的部分。 Qt的元对象系统使信号和插槽成为可能。
介绍
在GUI编程中,当我们更改一个小部件时,我们经常需要通知另一个小部件。更一般地说,我们希望任何类型的对象能够彼此通信。例如,如果用户单击“关闭”按钮,我们可能希望调用窗口的close()函数。
其他工具包使用回调实现这种通信。回调是指向函数的指针,因此如果您希望处理函数通知您某些事件,则将指针传递给处理函数的另一个函数(回调)。然后,处理函数在适当时调用回调。虽然确实存在使用此方法的成功框架,但回调可能不直观,并且可能在确保回调参数的类型正确性方面存在问题。
信号和插槽
在Qt中,我们有一种替代回调技术:我们使用信号和插槽。发生特定事件时会发出信号。 Qt的小部件有许多预定义信号,但我们总是可以将小部件子类化为向它们添加我们自己的信号。时隙是响应于特定信号而被调用的函数。 Qt的小部件有许多预定义的插槽,但通常的做法是将小部件子类化并添加自己的插槽,以便您可以处理您感兴趣的信号。

信号和插槽机制是类型安全的:信号的签名必须与接收插槽的签名匹配。 (事实上​​,一个槽可能比它收到的信号具有更短的签名,因为它可以忽略额外的参数。)由于签名是兼容的,编译器可以帮助我们在使用基于函数指针的语法时检测类型不匹配。基于字符串的SIGNAL和SLOT语法将在运行时检测类型不匹配。信号和插槽松散耦合:发出信号的类既不知道也不关心哪个插槽接收信号。 Qt的信号和插槽机制确保如果您将信号连接到插槽,将在适当的时间使用信号的参数调用插槽。信号和槽可以采用任何类型的任意数量的参数。它们完全是类型安全的。
从QObject或其子类之一(例如,QWidget)继承的所有类都可以包含信号和槽。当对象以可能对其他对象感兴趣的方式改变其状态时,由对象发出信号。这是沟通的所有对象。它不知道或关心是否有任何东西正在接收它发出的信号。这是真正的信息封装,并确保该对象可以用作软件组件。
插槽可用于接收信号,但它们也是正常的成员功能。就像一个物体不知道是否有任何东西接收到它的信号一样,一个槽不知道它是否有任何信号连接到它。这确保了可以使用Qt创建真正独立的组件。
您可以将任意数量的信号连接到单个插槽,并且可以根据需要将信号连接到任意数量的插槽。甚至可以将信号直接连接到另一个信号。 (每当发射第一个信号时,这将立即发出第二个信号。)
信号和插槽共同构成了一个强大的组件编程机制。
信号
当对象的内部状态以某种可能对对象的客户端或所有者感兴趣的方式发生更改时,对象会发出信号。信号是公共访问函数,可以从任何地方发出,但我们建议只从定义信号及其子类的类中发出它们。
发出信号时,通常会立即执行与其连接的插槽,就像正常的函数调用一样。发生这种情况时,信号和插槽机制完全独立于任何GUI事件循环。所有插槽返回后,将在emit语句后执行代码。使用排队连接时情况略有不同;在这种情况下,emit关键字后面的代码将立即继续,并且插槽将在稍后执行。
如果多个插槽连接到一个信号,则在发出信号时,插槽将按照它们已连接的顺序依次执行。
信号由moc自动生成,不得在.cpp文件中实现。他们永远不会有返回类型(即使用void)。

关于参数的说明:我们的经验表明,如果信号和插槽不使用特殊类型,则它们可以更加重复使用。如果QScrollBar :: valueChanged()使用特殊类型,例如假设的QScrollBar :: Range,则它只能连接到专门为QScrollBar设计的插槽。将不同的输入小部件连接在一起是不可能的。

当发射连接到它的信号时,调用一个槽。插槽是普通的C ++函数,可以正常调用;它们唯一的特点是信号可以连接到它们。
由于插槽是普通的成员函数,因此它们在直接调用时遵循正常的C ++规则。但是,作为插槽,它们可以通过信号插槽连接由任何组件调用,而不管其访问级别如何。这意味着从任意类的实例发出的信号可以导致在不相关的类的实例中调用私有槽。
您还可以将插槽定义为虚拟插槽,我们发现这些插槽在实践中非常有用。
与回调相比,信号和插槽稍微慢一些,因为它们提供了更大的灵活性,尽管实际应用的差异是微不足道的。通常,发射连接到某些插槽的信号比使用非虚函数调用直接调用接收器慢大约十倍。这是定位连接对象,安全地遍历所有连接(即检查后续接收器在发射期间没有被破坏)以及以通用方式编组任何参数所需的开销。虽然十个非虚函数调用可能听起来很多,但它比任何新的或删除操作的开销要小得多,例如。一旦执行场景后面的字符串,向量或列表操作需要新的或删除,信号和插槽开销仅负责完整函数调用成本的一小部分。无论何时在插槽中进行系统调用,情况都是如此;或间接调用十多个函数。信号和插槽机制的简单性和灵活性非常值得开销,您的用户甚至都不会注意到。
请注意,定义称为信号或槽的变量的其他库在与基于Qt的应用程序一起编译时可能会导致编译器警告和错误。要解决此问题,请#undef违规的预处理程序符号。
一个小例子
最小的C ++类声明可能是:

 class Counter
  {
  public:
      Counter() { m_value = 0; }

      int value() const { return m_value; }
      void setValue(int value);

  private:
      int m_value;
  };

一个基于QObject的小类可能会读取:

 

 #include 

  class Counter : public QObject
  {
      Q_OBJECT

  public:
      Counter() { m_value = 0; }

      int value() const { return m_value; }

  public slots:
      void setValue(int value);

  signals:
      void valueChanged(int newValue);

  private:
      int m_value;
  };

基于QObject的版本具有相同的内部状态,并提供访问状态的公共方法,但此外它还支持使用信号和插槽进行组件编程。这个类可以通过发出一个信号valueChanged()告诉外面世界它的状态发生了变化,并且它有一个其他对象可以发送信号的插槽。
包含信号或槽的所有类必须在其声明的顶部提及Q_OBJECT。它们还必须(直接或间接)从QObject派生。
插槽由应用程序员实现。这是Counter :: setValue()槽的可能实现:

 void Counter::setValue(int value)
  {
      if (value != m_value) {
          m_value = value;
          emit valueChanged(value);
      }
  }

发射线从对象发出信号valueChanged(),新值作为参数。
在下面的代码片段中,我们创建了两个Counter对象,并使用QObject :: connect()将第一个对象的valueChanged()信号连接到第二个对象的setValue()槽:

Counter a, b;
      QObject::connect(&a, &Counter::valueChanged,
                       &b, &Counter::setValue);

      a.setValue(12);     // a.value() == 12, b.value() == 12
      b.setValue(48);     // a.value() == 12, b.value() == 48

调用a.setValue(12)使得发出valueChanged(12)信号,b将在其setValue()槽中接收,即调用b.setValue(12)。然后b发出相同的valueChanged()信号,但由于没有插槽连接到b的valueChanged()信号,因此忽略该信号。
请注意,setValue()函数仅在值!= m_value时设置值并发出信号。这可以防止在循环连接的情况下无限循环(例如,如果b.valueChanged()连接到a.setValue())。
默认情况下,对于您所做的每个连接,都会发出一个信号;发出两个信号用于重复连接。您可以通过一次disconnect()调用来中断所有这些连接。如果传递Qt :: UniqueConnection类型,则只有在不是重复的情况下才会建立连接。如果已经有重复(完全相同的信号到相同对象上的完全相同的插槽),连接将失败并且connect将返回false
此示例说明对象可以一起工作,而无需了解彼此的任何信息。要启用它,只需要将对象连接在一起,这可以通过一些简单的QObject :: connect()函数调用或uic的自动连接功能来实现。

 

常见错误:信号槽未响应常见问题原因

1。没有定义Q_OBJECT

2.函数没有声明为槽函数(及没有在slot宏下)

3.connect绑定失败(可能原因是信号中带有形参,可通过返回值判断)

你可能感兴趣的:(Qt,Qt,信号与槽)