Ответ 1
Немного теории сигнальных слотов
Соединения сигнальных слотов не обращают внимания на отношения родитель-потомок между QObjects, и любые такие отношения не имеют значения. Вы можете подключать объекты к своим детям, их братьям и сестрам, их родителям или даже к QObjects, которые находятся в отдельной иерархии, или к одиноким QObjects, у которых нет ни родителей, ни детей. Это не имеет значения.
Соединение сигнального слота соединяет сигнал на конкретном экземпляре объекта QObject в слот в другом экземпляре объекта QObject. Чтобы использовать метод connect, вам нужны указатели на экземпляр QObject отправителя и экземпляр QObject получателя. Затем вы используете статический QObject::connect(sender, SIGNAL(...), receiver, SLOT(...))
. Эти соединения не имеют ничего общего с какой-либо иерархией между отправителем и получателем.
Вы также можете подключить сигнал к сигналу, чтобы переслать его - например, из частного элемента пользовательского интерфейса в сигнал той части API класса. Вы не можете подключить слот к слоту, потому что он может повлечь за собой чрезмерные издержки во время выполнения для редко используемого случая. Накладные расходы будут дополнительным членом bool
в QObjectPrivate, плюс неудачный тест if (bool)
. Если вы хотите пересылать слоты в слоты, есть как минимум два способа сделать это:
-
Выпустите сигнал в исходном слоте и подключите этот сигнал к разъему назначения.
-
Получите список всех сигналов, подключенных к исходному слоту, идите на него и подключите их к целевому слоту. Там нет простого способа поддерживать такие соединения, когда дополнительные сигналы подключены или отсоединены от исходного разъема. К сожалению, QObject имеет только функцию
connectNotify(const char*)
, но не сигнал, поэтому вы не можете подключиться к ней, если вы не изменитеsrc/corelib/kernel/qobject[.cpp,_p.h,.h]
излучать такой сигнал. Если вам это действительно нужно, просто измените источник Qt, вы получите доступ к нему по какой-то причине. Взлом vtable без изменения Qt возможен, но он обескуражен по понятным причинам.
Ответ
Ниже приведен пример, который показывает, как делать то, что вы хотите. Оказывается, у меня есть ответы на несколько вопросов из моих различных экспериментов, которые я делал в 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();
}