4

如果我们知道它的类型和名称(如果指定),就有可能找到一个孩子,如下所示:

QPushButton *button = parentWidget->findChild<QPushButton *>("button1");

然而,每个QObject都有metaObject()返回的函数属性 QMetaObject*。其又QMetaObject具有功能className()QObject是否可以像这样通过类名轻松找到:

QWidget *widget = (QWidget*)parentWidget->findByClassName("QPushButton", "button1");

或者是让所有QWidget孩子通过的唯一方法

QList<QWidget *> widgets = parentWidget->findChildren<QWidget *>("widgetname");

然后用std::find_ifby过滤列表metaObject()->className()

4

3 回答 3

7

findChild()已经允许您指定要搜索的内容的类型。

第二个参数实际上是objectName字符串属性。

如果您询问是否可以将类类型指定为字符串,则似乎没有这样的选项。

您可以轻松地制作这样的功能,只需迭代对象树并查询每个对象的元对象以获取类名并与您的字符串进行比较。

QObject * findByClassName(const QObject * const o, const char *name) {
  QObject * res = nullptr;
  foreach (QObject * c, o->children()) {
    if (res) break;
    if (QLatin1String(c->metaObject()->className()) == name) res = c;
    else res = findByClassName(c, name);
  }
  return res;
}

然后很简单,很明显,如果你想将指针作为具体类型findByClassName(parentWidget, "QPushButton"),你可以扩展它以包含objectNameand 做一些事情......如果你这样做了,你应该已经使用现有函数......指定仅当您事先不知道类型时,作为字符串的类型才有意义,并且据说...在运行时确定。qobject_castfindChild()

于 2018-03-11T02:03:47.533 回答
3

如果我们想指定某个CustomWidget类来查找并且不希望或无法添加它的标题定义,我的解决方案,因此只想通过以下方式搜索className()

QList<QWidget *> widgets = parentWidget->findChildren<QWidget *>();
        // or QApplication::allWidgets();

QList<QWidget *>::iterator it = std::find_if(widgets.begin(), widgets.end(),
    [](QWidget *widget) -> bool {
        return QLatin1String(widget->metaObject()->className()) ==
               "CustomWidget";
    });
于 2018-03-11T13:16:30.290 回答
0

您可以“作弊”并利用 ABI 的属性来访问该类型,即使不包括其标头。我们真正关心的是声明一个变量,该变量将为CustomWidget::staticMetaObject.

一个班级和QObject::findChildren

根据语言标准,这将是 UB,但所有主流 ABI 都支持这种 hack。

class NoneSpecial { // A base used not to violate ODR
  NoneSpecial() = delete;
  NoneSpecial(const NoneSpecial &) = delete;
  NoneSpecial(NoneSpecial &&) = delete;
  void operator=(const NoneSpecial &) = delete;
  void operator=(NoneSpecial &&) = delete;
  ~NoneSpecial() = delete;
};
class CustomWidget final : NoneSpecial { // Must not inherit any other base!
public:
  static const QMetaObject staticMetaObject;
};

template <typename T> QList<QWidget*> getWidgetChildren(QWidget *parent,
  Qt::FindChildOptions options = Qt::FindChildrenRecursively)
{
  auto const widgets = parent->findChildren<T*>();
  return reinterpret_cast<const QList<QWidget*>&>(widgets);
}

auto widgets = getWidgetChildren<CustomWidget>(parentWidget);

命名空间和findChildren实现细节

这也适用于所有理智的编译器,即使按照标准它是 UB。

namespace CustomWidget {
  extern const QMetaObject staticMetaObject;
}

QList<QWidget*> getWidgetChildren(QWidget *parent, const QMetaObject & mo,
  Qt::FindChildOptions options = Qt::FindChildrenRecursively)
{
  QList<QWidget*> widgets;
  qt_qFindChildren_helper(parent, {}, mo,
                          reinterpret_cast<QList<void*>*>(&widgets),
                          options);
  return widgets;
}

auto widgets = getWidgetChildren(parentWidget, CustomWidget::staticMetaObject);

直接使用重命名的名称

我们可以直接引用staticMetaObject's 的重命名 - 这适用于 GCC、ICC 和 Clang。不幸的是,由于损坏的名称不是有效的标识符,因此无法在 MSVC 上实现它。该Length参数在编译时被断言为正确的,但不幸的是,没有预处理器技巧来获取它的长度并避免传递它的需要。

#ifdef __GNUG__
// Works on gcc, clang and icc
#define DECLARE_STATIC_METAOBJECT(Class, Length) \
inline const QMetaObject & Class##_staticMetaObject() { \
  static_assert(sizeof(#Class) == (Length+1)); \
  extern const QMetaObject _ZN##Length##Class##16staticMetaObjectE; \
  return _ZN##Length##Class##16staticMetaObjectE; \
}

DECLARE_STATIC_METAOBJECT(CustomWidget, 16)
#endif

auto widgets = getWidgetChildren(parentWidget, CustomWidget_staticMetaObject());

导出已知名称的符号

此解决方案是上述命名空间方法的变体,但不涉及任何 UB。它确实需要添加

// metaexport.h
#define DEFINE_META_EXPORT(Class) \
  const QMetaObject & Class#_staticMetaObject() { return Class::staticMetaObject; }
#define DECLARE_META_EXPORT(Class) const QMetaObject & Class#_staticMetaObject();

// customwidget.cpp
#include "customwidget.h"
#include "metaexport.h"
DEFINE_META_EXPORT(CustomWidget)
...

// myclass.cpp
// doesn't include "customwidget.h"
#include "metaexport.h"
DECLARE_META_EXPORT(CustomWidget)

然后qt_qFindChildren_helper在上面的第二个(命名空间)解决方案中使用。

于 2018-03-16T22:26:44.567 回答