QObject 提供一个十分有用的 api,T findChild(QString, Qt::FindChildOptions)
,这个函数接收一个模版参数,返回模版参数的类型(若是子对象能够造型成 T ),也就是说返回值已经作了 cast
造型处理,这样就能够直接用特定的子类指针接收,使用起来很是方便。能够在对象的子类中寻找特定名称(objectName)的对象,而 Qt::FindChildOptions
参数用于设置搜索范围,Qt::FindDirectChildrenOnly
能够设置搜索范围仅限于当前对象的 children,而且不迭代 children 搜索,默认选项 Qt::FindChildrenRecursively
将迭代搜索全部子对象。html
如下内容摘自官方示例:api
// 返回最匹配的对象指针,该对象指针能够造型成 QPushButton 指针,而且该对象的 objectName 为 button1 QPushButton *button = parentWidget->findChild<QPushButton *>("button1"); // 返回最匹配的对象指针,该对象指针能够造型成 QListWidget 指针 QPushButton *list = parentWidget->findChild<QListWidget *>(); // 返回最匹配的对象指针,仅在 parentWidget 对象的直接子对象中查找, // 该对象指针能够造型成 QPushButton 指针,而且该对象的 objectName 为 button1 QPushButton *button = parentWidget->findChild<QPushButton *>("button1", Qt::FindDirectChildrenOnly); // 返回最匹配的对象指针,仅在 parentWidget 对象的直接子对象中查找, // 该对象指针能够造型成 QPushButton 指针 QListWidget *list = parentWidget->findChild<QListWidget *>(QString(), Qt::FindDirectChildrenOnly);
须要注意的是最后一种用法,这里想要在 parentWidget 中找到可造型成 QListWidget 的直接子对象,可是不须要指定对象名,第一个参数须要用 QString()
而不能用空字符串 ""
,不然便没有符合要求的子类 list,此时 list 的值为 0x0,结果一直找不到子对象。架构
如下代码为 Qt5.7 版本源代码:函数
// QObject.h template<typename T> inline T findChild(const QString &aName = QString(), Qt::FindChildOptions options = Qt::FindChildrenRecursively) const { typedef typename QtPrivate::remove_cv<typename QtPrivate::remove_pointer<T>::type>::type ObjType; return static_cast<T>(qt_qFindChild_helper(this, aName, ObjType::staticMetaObject, options)); } // QObject.cpp QObject *qt_qFindChild_helper(const QObject *parent, const QString &name, const QMetaObject &mo, Qt::FindChildOptions options) { if (!parent) return 0; const QObjectList &children = parent->children(); QObject *obj; int i; for (i = 0; i < children.size(); ++i) { obj = children.at(i); if (mo.cast(obj) && (name.isNull() || obj->objectName() == name)) return obj; } if (options & Qt::FindChildrenRecursively) { for (i = 0; i < children.size(); ++i) { obj = qt_qFindChild_helper(children.at(i), name, mo, options); if (obj) return obj; } } return 0; }
须要注意,类模版中成员函数的定义和声明都要放在 .h
文件中,防止编译器报错,参考上面的 findChild
模版成员函数,这个函数用到模版,声明和定义都放在 QObject.h 中。学习
该函数的实现是经过 qt_qFindChild_helper 助手函数实现的。ui
在 QObject.cpp 源代码中找到的两个函数定义:this
void qt_qFindChildren_helper(const QObject *parent, const QRegularExpression &re,const QMetaObject &mo, QList<void*> *list, Qt::FindChildOptions options);
QObject *qt_qFindChild_helper(const QObject *parent, const QString &name, const QMetaObject &mo, Qt::FindChildOptions options);
在源代码实现中,判断可否返回对象指针的判断条件是 mo.cast(obj) && (name.isNull() || obj->objectName() == name)
若是传入的名称参数是空字符串 ""
那么 name.isNull()
返回的是 false, obj->objectName() == name
返回的是 false,整个判断返回 false,也就没法找到符合要求的子对象了。spa
关于 Qt 的 Model/View 架构,在 官方文档 中已经给出了详细的介绍。指针
实际项目中使用到的模型是 Table 模型,在子类化 QAbstractTableModel 时,重写其中一些函数用于显示数据。code
好比重写 QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
用于显示标题数据。标题数据有纵向和横向两种选择,Qt::Orientation
用于设置方向。若是不指定方向,对全部 role 值为 Qt::DisplayRole
的数据进行绘制,将会看到横向的第一个标题头彷佛老是不显示或者显示的是错误的数据。缘由在于 TableModel 还会显示纵向的标题头,致使两个方向的标题头重叠。 role 值有 Qt 提供的一些值如 Qt::DisplayRole
,Qt::BackgroundRole
,前者用于显示文本或图片,或者能够用来设置背景。
最开始接触 Qt 的 Model/View 架构时感受它有些复杂,除了 Model 外,在重写函数时常常看到对 ModelIndex 的使用,ModelIndex 就是索引,包含一些数据,可是 QModelIndex 隐藏了它的构造函数(它的构造函数是用 private 修饰的),没法经过构造函数直接构造一个 QModelIndex 对象(须要经过 createIndex 方法构造):
如下为 Qt5.7 的部分源代码
// qabstractitemmodel.h friend class QAbstractItemModel; ... private: inline QModelIndex(int arow, int acolumn, void *ptr, const QAbstractItemModel *amodel) Q_DECL_NOTHROW : r(arow), c(acolumn), i(reinterpret_cast<quintptr>(ptr)), m(amodel) {}
编写本身的 model 类时感受无从下手,好在官方的示例不少,介绍的也很详细。而在重写 TableModel 时,其实也没有怎么使用成员函数中 QModelIndex 参数,就是直接跳过这个参数,对外提供数据。 模型比较简单,目前仅在刷新数据时用到 QModelIndex 。