16

我在完全掌握 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()));
}

但我不确定我应该如何做到这一点并将其连接起来。我是否需要在每个级别都有信号并通过树发出?

4

2 回答 2

22

A Bit of Signal-Slot Theory

The signal-slot connections are oblivious to parent-child relationships between QObjects, and any such relationship doesn't matter. You're free to connect objects to their children, to their siblings, to their parents, or even to QObjects that are in a separate hierarchy, or to lone QObjects that have neither parents nor children. It doesn't matter.

A signal-slot connection connects a signal on a particular instance of QObject to slot on another instance of QObject. To use the connect method, you need the pointers to the instance of sender QObject and the instance of receiver QObject. You then use the static QObject::connect(sender, SIGNAL(...), receiver, SLOT(...)). Those connections have nothing to do with any hierarchy there is between the sender and receiver.

You can also connect a signal to a signal, to forward it -- for example from a private UI element to a signal that's part of the API of the class. You cannot connect a slot to a slot, because it'd incur a bit of runtime overhead for a rarely-used case. The overhead would be an extra bool member in QObjectPrivate, plus a failed if (bool) test. If you want to forward slots to slots, there are at least two ways to do it:

  1. Emit a signal in the source slot and connect that signal to the destination slot.

  2. Obtain a list of all signals connected to the source slot, iterate on it and connect them to to the target slot. There's no easy way to maintain such connections when further signals are connected or disconnected from the source slot. Unfortunately, QObject only has a connectNotify(const char*) protected function, but not a signal -- so you can't hook up to it unless you would modify src/corelib/kernel/qobject[.cpp,_p.h,.h] to emit such a signal. If you truly need it, just modify the Qt source, you have access it for a reason, after all. Hacking the vtable without modifying Qt is possible, but discouraged for obvious reasons.

The Answer

Below is a self contained example that shows how to do what you want. Turns out I have answers to quite a few questions from my various experiments I've done in Qt in the past. I'm a packrat when it comes to test code. It's all SSCCE to boot :)

// 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();
}
于 2012-05-31T21:49:48.350 回答
0
Connect(stackedwidget->currentactivewidget,SIGNAL(OnAction()),this,SLOT(PrivateSlot()));

PrivateSlot()是一个私下声明的槽。所以在这个函数中,你可以添加你的代码来改变stackedwidget对应currentactivewidget产生的动作的页面。

同样,如果您真的想将信号向上传递到层次结构,publicsignal()请在私有插槽函数的末尾发出 a 。

Connect(this,SIGNAL(publicsignal()),Parentwidgetofstackedwidget(here mainwindow),SLOT(mainwindow_slot()));
于 2012-05-28T11:23:07.017 回答