有没有办法按类名找到 QObject 孩子?
Is there a way to find QObject child by class name?
如果我们知道孩子的类型和名称(如果指定),就有可能找到一个孩子,如下所示:
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_if
metaObject()->className()
?
>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")
,显然,您可以对此进行扩展以包含objectName
,如果您想将指针作为具体类型获取,可以做一些qobject_cast
......如果你这样做了,你无论如何都应该使用现有的findChild()
函数......仅当您事先不知道类型时,将类型指定为字符串才有意义,并且说...在运行时确定。
如果我们想指定一些要查找的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";
});
即使不包含其标头,也可以"作弊"并利用 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);
命名空间和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
的残缺名称 - 这适用于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
。
- 更改"child's parent's style sheet"时如何将孩子的样式表重置为 Qt 默认样式表?
- 删除 QSharedPointer 指向的 QObject
- 为什么 isVisible 在 QWidget 孩子中不起作用
- QObject::连接无法将信号连接到*this*对象的插槽
- 调试符号中缺少 QObject 类信息(编辑但存在其他 Qt 类)
- OOP使用不同的结构与孩子一起构建建议
- QML/C++/QObject Struct Copy
- QObject::d eleteLater在我的Qt测试中没有像预期的那样调用
- 具有 QObject 继承的单例 - Qt
- QObject::连接不起作用 - 使用函数语法找不到信号
- 是否可以在单独的线程中将 QObject 设置为 QML 上下文属性?
- 智能指针作为 QObject::d eleteLater() 的替代品
- Shiboken2 qobject.h:46:10:致命:找不到'QtCore/qobjectdefs.h'文件
- 在 dll 中为 qLibrary 编译 qobject 时出错
- 将复制构造函数设置为默认值在继承自 QObject 时不起作用
- 我应该使用多个角色还是一个角色,将实际属性推迟到将数据包装/公开为其属性的QObject
- 在另一个线程上发出 QObject 信号的正确方法?
- c ++如何将模板应用于模板类的孩子/朋友?
- 有没有办法按类名找到 QObject 孩子?
- 我怎样才能得到一个QObject的孩子?