C++ 有没有办法通过类名找到QObject子对象?

C++ 有没有办法通过类名找到QObject子对象?,c++,qt,qobject,qmetaobject,C++,Qt,Qobject,Qmetaobject,如果我们知道子对象的类型和名称(如果指定),则可以找到该子对象,如下所示: QPushButton *button = parentWidget->findChild<QPushButton *>("button1"); QWidget *widget = (QWidget*)parentWidget->findByClassName("QPushButton", "button1"); 或者是通过 QList<QWidget *> widgets = p

如果我们知道子对象的类型和名称(如果指定),则可以找到该子对象,如下所示:

QPushButton *button = parentWidget->findChild<QPushButton *>("button1");
QWidget *widget = (QWidget*)parentWidget->findByClassName("QPushButton", "button1");
或者是通过

QList<QWidget *> widgets = parentWidget->findChildren<QWidget *>("widgetname");
QList widgets=parentWidget->findChildren(“widgetname”);
然后使用
std::find_筛选列表,如果
metaObject()->className()

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

第二个参数实际上是
objectName
string属性

如果您询问是否可以将类类型指定为string,那么似乎没有这样的选项

您可以轻松地创建这样的函数,只需迭代对象树,查询每个对象的元对象的类名,并与字符串进行比较

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”)
,显然,您可以在此基础上进行扩展,以包括
objectName
,并执行一些
qobject\u cast
操作,如果您想将指针作为具体类型获取的话。。。如果您这样做了,您只需使用现有的
findChild()
函数即可。。。仅当您事先不知道类型时,将类型指定为字符串才有意义,例如。。。在运行时确定。

如果要指定要查找的某个
CustomWidget
类,并且不希望或无法添加其标题定义,因此只希望通过
className()
进行搜索,则我的解决方案是:

QList widgets=parentWidget->findChildren();
//或QApplication::allWidgets();
QList::iterator it=std::find_if(widgets.begin(),widgets.end(),
[](QWidget*widget)->bool{
返回QLatin1String(widget->metaObject()->className())==
“CustomWidget”;
});
您可以“欺骗”并利用ABI的属性来访问该类型,即使不包括其标头。我们真正关心的是声明一个变量,该变量将为
CustomWidget::staticMetaObject
生成正确的损坏名称

类和
QObject::findChildren
按照语言标准,这将是UB,但所有主流ABI都支持这种攻击

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);
直接使用损坏的名称 我们可以直接参考
staticMetaObject
的损坏名称-这适用于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\u qFindChildren\u helper
就像上面第二个(名称空间)解决方案一样。

如果我想将任何小部件强制转换为简单的QWidget,将类型指定为字符串是有意义的。findChild()不允许我按名称搜索。在另一个答案中查看我的解决方案。我想可能会有更简单的。您的类也足够好。@AlekseyKontsevich这是错误的,
findChild()
可以使用具体类类型或其任何基类类型。因此,如果您想要的话,您可以从按钮中获得一个小部件。这是完全正确的,你是对的。但是如果我想指定某个要查找的
CustomWidget
类,并且不想或没有能力添加它的头定义,那么我只想通过
className()
进行搜索。在这种情况下,上述解决方案是可行的。是的,您的解决方案可能会运行得更快,因为它只搜索/循环一次-接受它。As
children()
仅返回直接子级:文档中没有任何内容。请使用
QLatin1String(
)或甚至
qstrcmp(foo,bar)代替
QString::fromLatin1(
)= 0=/COD>:后两种是QT的可执行的LATIN 1字符串比较方法,避免转换为“代码> qString ”。应该是跨平台的。@ AelkSykKnSoSvVICE,除非您的平台是真正晦涩的。第一个解决方案适用于所有C++ C++编译器、ICC和MSVC。mpilers和ICC。我不是想说服你使用它,只是指出它确实是跨平台的。它有点脆弱,因为它假设来自某一类UBs的某个行为,但很难想出一个编译策略来打破它。尽管这个空间可能是第一个使用它的地方关于这一点:许多人积极分享他们的见解。
#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());
// 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)