通过层次结构传递信号

Fir*_*gon 14 qt

我在完全掌握Qt中如何使用信号和插槽方面遇到了一些困难.我相信这是非常基本的,但我今天没有得到它.

我有一组小部件有点像这样:

MainWindow - > StackedWidget - > ChildForms

现在的想法是,子窗口小部件上有一些操作会导致堆叠窗口小部件显示不同的页面.

因此,如果我理解正确,我认为连接信号和插槽的方法是使用connect()知道对象的范围,但我设法工作的方法不是这样做的.在我的子表单中,我使用parentWidget()来访问StackedWidget的插槽但我真的不是很满意,因为它给了孩子关于它不应该具有的父节点的信息:

void TaskSelectionForm::setButtonMappings()
{
    // Set up a mapping between the buttons and the pages
    QSignalMapper *mapper = new QSignalMapper(this);
    connect(mapper, SIGNAL(mapped(int)), parentWidget(), SLOT(setCurrentIndex(int)));

    mapper->setMapping(ui->utilitiesButton, 2); // Value of the index
    connect(ui->utilitiesButton, SIGNAL(clicked()), mapper, SLOT(map()));
}
Run Code Online (Sandbox Code Playgroud)

但我不确定我应该怎么做并将其连接起来.我是否需要在每个级别都有信号并通过树发射?

Rei*_*ica 18

一点信号槽理论

信号槽连接忽略了QObject之间的父子关系,任何这样的关系都无关紧要.您可以自由地将对象连接到他们的孩子,他们的兄弟姐妹,他们的父母,甚至连接到单独的层次结构中的QObject,或者连接既没有父母也没有孩子的QObject.没关系.

信号槽连接将QObject 的特定实例上的信号连接到另一个QObject 实例上的槽.要使用connect方法,需要指向发送方QObject实例和接收方QObject实例的指针.然后使用静态QObject::connect(sender, SIGNAL(...), receiver, SLOT(...)).这些连接与发送方和接收方之间的任何层次结构无关.

您还可以将信号连接到信号,将其转发 - 例如从私有UI元素转换为类的API的一部分信号.您无法将插槽连接到插槽,因为它会为很少使用的情况带来一些运行时开销.开销将是boolQObjectPrivate中的额外成员,加上失败的if (bool)测试.如果要将插槽转发到插槽,至少有两种方法可以实现:

  1. 在源插槽中发出信号并将该信号连接到目标插槽.

  2. 获取连接到源插槽的所有信号的列表,迭代它并将它们连接到目标插槽.当进一步的信号与源插槽连接或断开时,没有简单的方法来维持这种连接.不幸的是,QObject只有一个connectNotify(const char*)受保护的功能,但不是一个信号 - 所以你不能连接它,除非你修改src/corelib/kernel/qobject[.cpp,_p.h,.h]发出这样的信号.如果你确实需要它,只需修改Qt源,毕竟你有理由访问它.在不修改Qt的情况下黑客攻击vtable是可能的,但由于显而易见的原因而气馁.

答案

下面是一个自包含的示例,说明如何执行您想要的操作.事实证明,我在过去的Qt中所做的各种实验中得到了很多问题的答案.在测试代​​码方面,我是一个包装工具.这是所有SSCCE启动:)

// https://github.com/KubaO/stackoverflown/tree/master/questions/signal-slot-hierarchy-10783656
#include <QtGui>
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
#include <QtWidgets>
#endif

class Window : public QWidget
{
   QSignalMapper m_mapper;
   QStackedLayout m_stack{this};
   QWidget      m_page1,                  m_page2;
   QHBoxLayout  m_layout1{&m_page1},      m_layout2{&m_page2};
   QLabel       m_label1{"Page 1"},       m_label2{"Page 2"};
   QPushButton  m_button1{"Show Page 2"}, m_button2{"Show Page 1"};
public:
   Window(QWidget * parent = {}) : QWidget(parent) {
      // the mapper tells the stack which page to switch to
      connect(&m_mapper, SIGNAL(mapped(int)), &m_stack, SLOT(setCurrentIndex(int)));

      // Page 1
      m_layout1.addWidget(&m_label1);
      m_layout1.addWidget(&m_button1);
      // tell the mapper to map signals coming from this button to integer 1 (index of page 2)
      m_mapper.setMapping(&m_button1, 1);
      // when the button is clicked, the mapper will do its mapping and emit the mapped() signal
      connect(&m_button1, SIGNAL(clicked()), &m_mapper, SLOT(map()));
      m_stack.addWidget(&m_page1);

      // Page 2
      m_layout2.addWidget(&m_label2);
      m_layout2.addWidget(&m_button2);
      // tell the mapper to map signals coming from this button to integer 0 (index of page 1)
      m_mapper.setMapping(&m_button2, 0);
      connect(&m_button2, SIGNAL(clicked()), &m_mapper, SLOT(map()));
      m_stack.addWidget(&m_page2);
   }
};

int main(int argc, char *argv[])
{
   QApplication a(argc, argv);
   Window w;
   w.show();
   return a.exec();
}
Run Code Online (Sandbox Code Playgroud)