当对话框中的任何小部件发出信号时,是否可以调用插槽?

Is it possible to call a slot when any of the widgets in a dialog box emits a signal?

我正在尝试为应用程序创建配置菜单框,并使用 QDialog 框来显示用户可以更改的选项。此框包含 QComboBoxes 和 QLineEdits,但数量很多(7 个组合框和 12 个行编辑)。底部有一个名为 "Apply Changes" 的 QPushButton,仅当框中的任何 属性 发生更改时才应启用。

我是否必须 link 来自每个带有插槽的小部件的每个信号以单独启用按钮,或者是否有 QDialog 框本身在其组成小部件发生变化时发出的信号?

现在我有这个:

connect(Combo1,SIGNAL(activated(QString)),this,SLOT(fnEnable(QString)));
connect(Combo2,SIGNAL(activated(QString)),this,SLOT(fnEnable(QString)))

后面还有 17 行这些连接。

void MyClass::fnEnable(QString)
{
ApplyButton->setEnabled(true); //It is initialised as false
}

我想知道是否有更短的方法来做到这一点,也许(就像我之前提到的)QDialog 发出的信号(我在 documentation 中找不到)

我知道这不会加快程序速度,因为只调用了所需的连接,但它会进一步尝试使更雄心勃勃的对话框变得更容易。

实际上并没有这样的信号,但是一种方法是创建一个QComboBox的列表,然后用for建立连接,例如:

QList <*QCombobox> l; 

l<<combobox1<< combobox2<< ....; 

for (auto combo: l) {
    connect(combo, &QComboBox::activated, this, &MyClass::fnEnable);
}

QLineEdit 也是如此。

您可以迭代小部件框的初始化列表并利用 C++11 为您完成所有无聊的工作:

MyClass::MyClass(QWidget * parent) : QWidget(parent) {
  auto const comboBoxes = {Combo1, Combo2, ... };
  for (auto combo : comboBoxes)
    connect(combo, &QComboBox::activates, this, &MyClass::fnEnable);
}

您还可以自动查找所有组合框:

MyClass::MyClass(QWidget * parent) : QWidget(parent) {
  ui.setupUi(this); // or other setup code
  for (auto combo : findChildren<QComboBox*>(this))
    connect(combo, &QComboBox::activated, this, &MyClass::fnEnable);
}

或者您可以自动附加到用户 属性 的更改信号。这将适用于拥有用户 属性 的所有控件。用户 属性 是包含该控件显示的主要数据的控件的 属性。

void for_layout_widgets(QLayout * layout, const std::function<void(QWidget*)> & fun, 
                        const std::function<bool(QWidget*)> & pred = +[](QWidget*){ return true; })
{
  if (!layout) return;
  for (int i = 0; i < layout->count(); ++i) {
    auto item = layout->itemAt(i);
    for_layout_widgets(item->layout(), fun, pred);
    auto widget = item->widget();
    if (widget && pred(widget)) fun(widget);
  }
}

class MyClass : public QWidget {
  Q_OBJECT
  Q_SLOT void MyClass::fnEnable(); // must take no arguments
  ...
};

MyClass::MyClass(QWidget * parent) : QWidget(parent) {
  // setup code here
  auto slot = metaObject()->method(metaObject()->indexOfMethod("fnEnable()"));
  Q_ASSERT(slot.isValid());
  for_layout_widgets(layout(), [=](QWidget * widget){
    auto mo = widget->metaObject();
    auto user = mo->userProperty();
    if (!user.isValid()) return;
    auto notify = user.notifySignal();
    if (!notify.isValid()) return;
    connect(widget, notify, this, slot);
  });
}   

您还可以按值将组合框保存在一个数组中。这最大限度地减少了间接引用的成本,并导致代码占用尽可能少的内存并表现良好:

class MyClass : public QWidget {
  Q_OBJECT
  QVBoxLayout m_layout{this};
  std::array<QComboBox, 14> m_comboBoxes;
  ...
};

MyClass(QWidget * parent) : QWidget(parent) {
  for (auto & combo : m_comboBoxes) {
    m_layout.addWidget(&combo);
    connect(&combo, &QComboBox::activates, this, &MyClass::fnEnable);
  }
}